diff options
author | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-01-20 01:29:50 +0000 |
---|---|---|
committer | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-01-20 01:29:50 +0000 |
commit | 8362bf63dea22bbf6736609b0f49c152f975eb63 (patch) | |
tree | 0eea3928e39e50fae91d4e68b21b1e6cbae25604 /kplato | |
download | koffice-8362bf63dea22bbf6736609b0f49c152f975eb63.tar.gz koffice-8362bf63dea22bbf6736609b0f49c152f975eb63.zip |
Added old abandoned KDE3 version of koffice
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/koffice@1077364 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kplato')
207 files changed, 43801 insertions, 0 deletions
diff --git a/kplato/KPtViewIface.cc b/kplato/KPtViewIface.cc new file mode 100644 index 00000000..e9a9e332 --- /dev/null +++ b/kplato/KPtViewIface.cc @@ -0,0 +1,88 @@ + +#include "KPtViewIface.h" +#include "kptview.h" + +#include <kapplication.h> +#include <dcopclient.h> +#include <dcopref.h> + +namespace KPlato +{ + +/************************************************ + * + * ViewIface + * + ************************************************/ + +ViewIface::ViewIface( View* t ) + : KoViewIface( t ) +{ + m_view = t; +} + +ViewIface::~ViewIface() +{ +} + +void ViewIface::slotEditResource() +{ + m_view->slotEditResource(); +} + +void ViewIface::slotEditCut() +{ + m_view->slotEditCut(); +} + +void ViewIface::slotEditCopy() +{ + m_view->slotEditCopy(); +} + +void ViewIface::slotEditPaste() +{ + m_view->slotEditPaste(); +} + +void ViewIface::slotViewGantt() +{ + m_view->slotViewGantt(); +} + +void ViewIface::slotViewPert() +{ + m_view->slotViewPert(); +} + +void ViewIface::slotViewResources() +{ + m_view->slotViewResources(); +} + +void ViewIface::slotAddTask() +{ + m_view->slotAddTask(); +} + +void ViewIface::slotAddSubTask() +{ + m_view->slotAddSubTask(); +} + +void ViewIface::slotAddMilestone() +{ + m_view->slotAddMilestone(); +} + +void ViewIface::slotProjectEdit() +{ + m_view->slotProjectEdit(); +} + +void ViewIface::slotConfigure() +{ + m_view->slotConfigure(); +} + +} //KPlato namespace diff --git a/kplato/KPtViewIface.h b/kplato/KPtViewIface.h new file mode 100644 index 00000000..bb134b74 --- /dev/null +++ b/kplato/KPtViewIface.h @@ -0,0 +1,43 @@ + +#ifndef KPT_VIEW_IFACE_H +#define KPT_VIEW_IFACE_H + +#include <KoViewIface.h> + +#include <qstring.h> +#include <qrect.h> +#include <qcolor.h> + +namespace KPlato +{ + +class View; + +class ViewIface : public KoViewIface +{ + K_DCOP +public: + ViewIface( View* ); + ~ViewIface(); + +k_dcop: + void slotEditResource(); + void slotEditCut(); + void slotEditCopy(); + void slotEditPaste(); + void slotViewGantt(); + void slotViewPert(); + void slotViewResources(); + void slotAddTask(); + void slotAddSubTask(); + void slotAddMilestone(); + void slotProjectEdit(); + void slotConfigure(); + +private: + View* m_view; +}; + +} //KPlato namespace + +#endif diff --git a/kplato/Makefile.am b/kplato/Makefile.am new file mode 100644 index 00000000..eacfc3f0 --- /dev/null +++ b/kplato/Makefile.am @@ -0,0 +1,131 @@ +SUBDIRS = templates pics reports toolbar tests + +INCLUDES = $(KOFFICE_INCLUDES) $(KOTEXT_INCLUDES) \ + -I$(top_srcdir)/kdgantt -I$(top_srcdir)/kugar/lib \ + $(all_includes) + +libkplatopart_la_SOURCES = \ + kptsummarytaskgeneralpanelbase.ui \ + kptsummarytaskdialog.cc \ + kptsummarytaskgeneralpanel.cc \ + kptschedule.cc \ + kptappointment.cc \ + kptcalendarlistpanel.ui \ + kptresourceappointmentsview.cc \ + kpttaskappointmentsview.cc \ + kptdoublelistviewbase.cc \ + kptaccountsviewconfigdialog.cc \ + kptaccountsviewconfigurepanelbase.ui \ + kptaccountsview.cc \ + kpttaskcostpanelbase.ui \ + kpttaskcostpanel.cc \ + kptmilestoneprogresspanel.cc \ + kptmilestoneprogresspanelbase.ui \ + kptmilestoneprogressdialog.cc \ + kptaccount.cc kptaccountsdialog.cc kptaccountspanel.cc kptaccountspanelbase.ui \ + kptnode.cc kptproject.cc \ + kptrelation.cc kptresource.cc kpttask.cc \ + kptduration.cc kptfactory.cc kptpart.cc kptview.cc \ + kptdurationwidget.ui relationpanel.ui\ + kptprojectdialog.cc kpttaskdialog.cc \ + kptmainprojectdialog.cc kptmainprojectpanel.cc kptmainprojectpanelbase.ui \ + kptganttview.cc kptresourcespanel.cc \ + kptpertview.cc kptpertcanvas.cc kptrelationdialog.cc \ + kptcanvasitem.cc kptprojectdialogbase.ui \ + resourcespanelbase.ui \ + kptresourceview.cc \ + kpttaskresourcespanelbase.ui kptrequestresourcespanel.cc \ + resourcedialogbase.ui kptresourcedialog.cc \ + kpttaskgeneralpanelbase.ui \ + kpttasknotespanelbase.ui \ + kptdatetime.cc \ + kptcalendar.cc kptcalendaredit.cc kptcalendarlistdialog.cc \ + kptcalendarpanel.cc kptdatetable.cc \ + kptcalendarlistdialogbase.cc kptcalendareditbase.cc \ + kptcommand.cc KPtViewIface.cc KPtViewIface.skel \ + standardworktimedialogbase.ui kptstandardworktimedialog.cc \ + kptintervaleditbase.ui kptintervaledit.cc \ + kpttaskgeneralpanel.cc kpttaskdefaultpanel.cc \ + kptconfigtaskpanelbase.ui \ + kptconfigdialog.cc \ + kpttaskprogresspanel.cc kpttaskprogresspanelbase.ui \ + kpttaskprogressdialog.cc \ + kptconfig.cc kptcontext.cc \ + kptresourcesdialog.cc \ + kptwbsdefinitiondialog.cc kptwbsdefinitionpanel.cc kptwbsdefinitionpanelbase.ui \ + kptwbsdefinition.cc +## kptconfigbehaviorpanel.cc kptconfigbehaviorpanelbase.ui \ +## kptreportview.cc + +# (this is not used) +noinst_HEADERS = \ + kptxmlloaderobject.h \ + kptsummarytaskdialog.h \ + kptsummarytaskgeneralpanel.h \ + kptschedule.h \ + kptappointment.h \ + kptresourceappointmentsview.h \ + kpttaskappointmentsview.h \ + kptdoublelistviewbase.h \ + kptaccountsviewconfigdialog.h \ + kptaccountsview.h \ + kpttaskcostpanel.h \ + kpttaskprogresspanel.h \ + kpttaskprogressdialog.h \ + kptaccount.h kptaccountsdialog.h kptaccountspanel.h \ + kptaboutdata.h kptduration.h kptfactory.h \ + kptnode.h kptpart.h kptproject.h kptrelation.h \ + kptresource.h kpttask.h kptview.h \ + kptprojectdialog.h kpttaskdialog.h \ + kptmainprojectdialog.h \ + kptganttview.h kptcanvasitem.h \ + kptpertview.h kptpertcanvas.h kptrelationdialog.h \ + kptresourcespanel.h kptresourceview.h \ + kptrequestresourcespanel.h kptresourcedialog.h\ + kptdatetime.h kptmap.h \ + kptcalendar.h kptcalendaredit.h kptcalendarlistdialog.h \ + kptcalendarpanel.h kptdatetable.h \ + kptcalendarlistdialogbase.h kptcalendareditbase.h \ + kptcommand.h \ + kptstandardworktimedialog.h kptintervaledit.h intervalitem.h \ + kpttaskgeneralpanel.h kpttaskdefaultpanel.h \ + kptconfigdialog.h \ + kpttaskprogresspanel.h \ + kptconfig.h kptcontext.h \ + kptresourcesdialog.h \ + kptwbsdefinitiondialog.h kptwbsdefinitionpanel.h \ + kptwbsdefinition.h \ + kpteffortcostmap.h +## kptconfigbehaviorpanel.h \ +## kptreportview.h + +## The part +lib_LTLIBRARIES = +kde_module_LTLIBRARIES = libkplatopart.la +libkplatopart_la_LDFLAGS = $(KDE_LDFLAGS) $(KDE_PLUGIN) +libkplatopart_la_LIBADD = $(LIB_KABC) $(LIB_KOFFICEUI) \ + $(top_builddir)/kdgantt/libkdgantt.la +## $(top_builddir)/kugar/lib/libkugarlib.la + +libkplatopart_la_METASOURCES = AUTO + +## The kdeinit loadable module and executable +kdeinit_LTLIBRARIES = kplato.la +bin_PROGRAMS = +kplato_la_SOURCES = main.cc +kplato_la_LDFLAGS = $(all_libraries) -module $(KDE_PLUGIN) +kplato_la_LIBADD = $(LIB_KOFFICECORE) + +xdg_apps_DATA = kplato.desktop + +rcdir = $(kde_datadir)/kplato +rc_DATA = kplato.rc kplato_readonly.rc + +## NOTE: extractattr needs <Label & Text on same line! +messages: rc.cpp + $(EXTRACTATTR) --attr=Label,Text reports/*.ktf >>rc.cpp + $(XGETTEXT) rc.cpp *.cc kptaboutdata.h -o $(podir)/kplato.pot + +kde_services_DATA = kplatopart.desktop + +include $(top_srcdir)/admin/Doxyfile.am diff --git a/kplato/TODO b/kplato/TODO new file mode 100644 index 00000000..c895308b --- /dev/null +++ b/kplato/TODO @@ -0,0 +1,187 @@ +Some usefull stuff: +You can search the source code for FIXME and TODO entries with: +find . -name "*.c*" -o -name "*.h" | xargs grep -E "(###|TODO|FIXME|todo)" + +Using -A and -B with grep gives you some additional context around the particular comment: +find . -name "*.c*" -o -name "*.h" | xargs grep -A 3 -B 3 -E "(###|TODO|FIXME|todo)" + + +The list is not sorted in any order, allthough more urgent things tend to +gravitate to the top. Also it is not limited to what should be fixed +before the 1.5 release! +-------------------------------------------------------------------------------- +Status Description +-------------------------------------------------------------------------------- +Taken User manual (contact: raphael.langerhorst@kdemail.net) + + Find a solution for the durationwidget. + It's now a custom widget mainly comprised by QLineEdits. + This doesn't work well. One problem is that QLineEdit gives + sizehints of approx 14 charachters minimum and we use 2-3, so + it messes up layouting. + + Resourceuseview: Scrap existing and use KChart instead. + Removed for now. Charting probably not added until after release. + + Reports: Review if/how Kugar should be used and + design a few usefull reports. + + Task details view: Improve, make it stand-alone? Convert to + a "infoview"? (as todos in KDE-PIM?) + + Cut, copy, paste. + + Start-to-finish relation. + + Improve menu- and toolbar structure. + + Icons. Would be nice to have a set with a consistent look. + + Templates + + Add tooltips and what's this. + + Better handling of resources during scheduling. + - Use Overtime + - Allow/prohibit overbooking + - Resource leveling + - Monte carlo estimation + - etc + + Calendars: + - Timezone handling + - Default calendar (See also Bug 123682) + - Localized holiday files (as in kdepim) + + Improve Progress info dialog/presentation. + Make it stand-alone, add to task context menu. + + KDGantt: + - Static/global IDs for items and tasklink groups. + - Presentation of "week-ends" when it doesn't match scale. + - Time-now line. + - Progress for event (milestone) item. + + Settings/configuration + + Implement periodic/repetitive tasks. + + Implement external events + + Implementing subproject editor-dialog + Subproject is not supported. + + Handle (re-)calculation of started/finished tasks. + + Restrict modification of started/finished tasks. + + Interface to PIM + - Issue todos and docs + - Fetch freebusy info (Prob: how to sync if we recalculate?) + + Decide on the final KPlato file format. + For now we have these options: + * leave the format the way it is + * create a new (OASIS?) specification + * use PMXML - http://www.projectoffice.com/xml/xml.asp + * or find a better suited "standard" format + http://proj.chbs.dk/specifications/ + + + Make it possible to form resource teams. + + Make it possible to request resource allocations from groups. + + Write good doxygen compatible API docs in the source code! + + Taskjuggler??? + + Change source filenames to 'classname'.cpp and d:o .h + Eg: kpttask.cc -> Task.cpp kpttask.h -> Task.h + Also remove the .ui filename prefix. +------------------------------------------------------------------ +Done Remove Networkview. + +Done Resourceview: Improve + + +Done Fix default values for "standard worktime" (and maybe a new name?). + This should maybe be part of templates. + +Done Implement embedding into other koffice apps. + +Done Fix 'embedding' into koshell. + +Done Calendars: + - Improve calendars dialogs + - Implement hierachical calendars + +Done Autogenerate task id. + +Done Implement multiple scheduling. (Expected, optimistic, pessimistic) + +Done Make a summarytask dialog. + +Done Cost Breakdown Structure: + - Define account class/list and necessary dialogs. + - Task cost specification (which shall link to accounts, resources). + - Resource cost specification. + - Implement calculation of planned cost (part of calculating/scheduling + the project). + +Done Remove KPT prefix from all KPlato classes (ask mailing list!) + +Done Requirements specification + +Done Data structuring and class creation + +Done Implementing basic view + + +Done Implementing basic task/summarytasks + + +Done Implementing Gantt view + Uses KDGanttView, which needs some improvements: + Context menu for links, possibility to present + progress in different ways (time-now, double taskbars...), + improved layout of links. + +Done Implementing task editor-dialog + +Done Implementing project editor-dialog + + + +Done Implementing milestone editor-dialog + Uses task dialog. Milestone is a task with 0 duration. + + +Done Resources (basic) + Note: Possibly redesign group/resource so that one resource + can be member of multiple groups. + + Calculations: +Done From given project start time. +Done From given project end time: + Based on task relations: +Done Finish to start. +Done Start to start. +Done Finish to finish. + Based on constraints. +Done As soon as possible. +Done As late as possible. +Done Must start on. +Done Start not earlier than. +Done Finish not later than. + +Done Based on resource requests to specific resources. +Done Allocation of resources and calculates durations accordingly. + + + Templates: +Done Plain + +Done Use KPlato namespace for all KPlato classes + + diff --git a/kplato/intervalitem.h b/kplato/intervalitem.h new file mode 100644 index 00000000..9bc8992b --- /dev/null +++ b/kplato/intervalitem.h @@ -0,0 +1,48 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Dag Andersen <danders@get2net.dk> + + 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 INTERVALITEM_H +#define INTERVALITEM_H + +#include <qlistview.h> +#include <qdatetime.h> +#include <qpair.h> + +namespace KPlato +{ + +class IntervalItem : public QListViewItem +{ +public: + IntervalItem(QListView * parent, QTime start, QTime end) + : QListViewItem(parent, QString("%1 - %2").arg(start.toString(), end.toString())), + m_start(start), + m_end(end) + {} + QPair<QTime, QTime> interval() { return QPair<QTime, QTime>(m_start, m_end); } + +private: + QTime m_start; + QTime m_end; +}; + +} //KPlato namespace + +#endif /* INTERVALITEM_H */ + diff --git a/kplato/kplato.desktop b/kplato/kplato.desktop new file mode 100644 index 00000000..12930f53 --- /dev/null +++ b/kplato/kplato.desktop @@ -0,0 +1,64 @@ +[Desktop Entry] +Name=KPlato +Name[hi]=के-प्लेटो +Name[ne]=केडीई प्लाटो +Name[sv]=Kplato +Name[zh_TW]=KDE 繪圖 +GenericName=Project Management +GenericName[bg]=Управление на проекти +GenericName[br]=Mererezh raktresioù +GenericName[ca]=Gestió de projectes +GenericName[cy]=Rheoli Cywaith +GenericName[da]=Projekthåndtering +GenericName[de]=Projektmanagement +GenericName[el]=Διαχείριση έργων +GenericName[eo]=Projektadministrado +GenericName[es]=Gestión de proyecto +GenericName[et]=Projektihaldus +GenericName[fa]=مدیریت پروژه +GenericName[fi]=Projektinhallinta +GenericName[fr]=Gestion de projets +GenericName[fy]=Projektbehear +GenericName[ga]=Bainisteoireacht Tionscadal +GenericName[gl]=Xestión de Proxectos +GenericName[he]=ניהול פרוייקטים +GenericName[hr]=Upravljanje projektima +GenericName[hu]=Projektkezelő +GenericName[is]=Verkefnisstjórn +GenericName[it]=Gestione dei progetti +GenericName[ja]=プロジェクト管理 +GenericName[km]=កម្មវិធីគ្រប់គ្រងគម្រោង +GenericName[lt]=Projektų valdymas +GenericName[lv]=Projektu pārvaldīšana +GenericName[ms]=Pengurusan Projek +GenericName[nb]=Prosjektstyring +GenericName[nds]=Projektpleeg +GenericName[ne]=परियोजना व्यवस्थापन +GenericName[nl]=Projectbeheer +GenericName[nn]=Prosjektstyring +GenericName[pl]=Zarządzanie projektami +GenericName[pt]=Gestão de Projectos +GenericName[pt_BR]=Gerenciamento de Projeto +GenericName[ru]=Проекты +GenericName[se]=Prošeaktahálddašeapmi +GenericName[sk]=Správa projektov +GenericName[sl]=Projektno vodenje +GenericName[sr]=Управљање пројектима +GenericName[sr@Latn]=Upravljanje projektima +GenericName[sv]=Projekthantering +GenericName[tg]=Лоиҳаи Роҳбар +GenericName[tr]=Proje Yönetimi +GenericName[uk]=Керування проектами +GenericName[uz]=Loyiha boshqaruvi +GenericName[uz@cyrillic]=Лойиҳа бошқаруви +GenericName[wa]=Manaedjaedje di prodjet +GenericName[zh_CN]=项目管理 +GenericName[zh_TW]=專案管理 +Exec=kplato %u +MimeType=application/x-vnd.kde.kplato +Type=Application +Icon=kplato +X-KDE-NativeMimeType=application/x-vnd.kde.kplato +DocPath=kplato/index.html +Categories=Qt;KDE;Office; +X-KDE-StartupNotify=true diff --git a/kplato/kplato.dtd b/kplato/kplato.dtd new file mode 100644 index 00000000..ef3de41b --- /dev/null +++ b/kplato/kplato.dtd @@ -0,0 +1,100 @@ +<!-- This is the Document Type Definition for The KDE Planning Tool. + Version 0.epsilon. 20020401 (no, no joke). + Written by Bo Thorsen <bo@sonofthor.dk> +--> + +<!-- A KPlato document consists of a projects, relations and + resources. TODO: It should probably also remember settings + for stuff like printing etc. + + A node in a project can be a project, task, milestone or terminalnode. + Each node is given a unique identifier. + A relation is a time restraint on the correlation between two + nodes. These are identified by their identifier. + A resource is one of the resources available and needed for the + project. These could be persons, hardware, money etc. +--> + +<!ELEMENT kplato (project)> +<!ATTLIST kplato author CDATA #IMPLIED + email CDATA #IMPLIED + editor CDATA #IMPLIED + mime CDATA "application/x-vnd.kde.kplato"> + +<!-- The project description. This has the following fields: + + name Name of the project + leader Person or group in charge of the project + description Text describing the project briefly +--> +<!ELEMENT project (project|task|endnode|startnode|relation|resource-group|appointment)*> +<!ATTLIST project id CDATA #REQUIRED + name CDATA #IMPLIED + leader CDATA #IMPLIED + description CDATA #IMPLIED + project-start CDATA #IMPLIED + project-end CDATA #IMPLIED> + +<!-- Terminalnodes +--> +<!ELEMENT startnode EMPTY> +<!ATTLIST startnode + earlieststart CDATA #IMPLIED + latestfinish CDATA #IMPLIED> + +<!ELEMENT endnode EMPTY> +<!ATTLIST endnode + earlieststart CDATA #IMPLIED + latestfinish CDATA #IMPLIED> + +<!-- Resource group +--> +<!ELEMENT resource-group (resource)*> +<!ATTLIST resource-group id CDATA #REQUIRED + type (work|material) + name CDATA #IMPLIED> + +<!-- Resource +--> +<!ELEMENT resource EMPTY +<!ATTLIST resource id CDATA #REQUIRED + name CDATA #IMPLIED> + +<!-- Tasks can also be milestones (zero duration) or summary tasks (have children) +--> +<!ELEMENT task (project|task|terminalnode|resource-request)*> +<!ATTLIST task id CDATA #REQUIRED + name CDATA #IMPLIED + leader CDATA #IMPLIED + description CDATA #IMPLIED + earlieststart CDATA #IMPLIED + latestfinish CDATA #IMPLIED + scheduling="0" + start CDATA #IMPLIED + end CDATA #IMPLIED + duration CDATA #IMPLIED> + +<!-- Resource requests are made by tasks and referes to + the number of resources needed from a resource group +--> +<!ELEMENT resource-request EMPTY> +<!ATTLIST resource-request group-id CDATA #REQUIRED + limit CDATA #IMPLIED> + + +<!-- Relations are described by the two IDs and the type of constraint + The ids are references to tasks, so all tasks must have been loaded +--> +<!ELEMENT relation EMPTY> +<!ATTLIST relation parent-id CDATA #REQUIRED + child-id CDATA #REQUIRED + type ("Finish-Start" | "Finish-Finish" | "Start-Start") + "Start-Start"> + +<!-- Appointments between resources and tasks +--> +<!ELEMENT appointment EMPTY> +<!ATTLIST appointment resource-id CDATA #REQUIRED + task-id CDATA #REQUIRED + start CDATA #IMPLIED + duration CDATA #IMPLIED> diff --git a/kplato/kplato.kdevelop b/kplato/kplato.kdevelop new file mode 100644 index 00000000..90f021f2 --- /dev/null +++ b/kplato/kplato.kdevelop @@ -0,0 +1,163 @@ +<?xml version = '1.0'?> +<kdevelop> + <general> + <author>Raphael Langerhorst</author> + <email>raphael.langerhorst@kdemail.net</email> + <version>$VERSION$</version> + <projectmanagement>KDevKDEAutoProject</projectmanagement> + <primarylanguage>C++</primarylanguage> + <keywords> + <keyword>Qt</keyword> + <keyword>KDE</keyword> + </keywords> + <projectdirectory>.</projectdirectory> + <absoluteprojectpath>false</absoluteprojectpath> + <description/> + <ignoreparts/> + </general> + <kdevfileview> + <groups> + <group pattern="*.cpp;*.cxx;*.h" name="Sources" /> + <group pattern="*.ui" name="User Interface" /> + <group pattern="*.png" name="Icons" /> + <group pattern="*.po;*.ts" name="Translations" /> + <group pattern="*" name="Others" /> + <hidenonprojectfiles>false</hidenonprojectfiles> + <hidenonlocation>false</hidenonlocation> + </groups> + <tree> + <hidepatterns>*.o,*.lo,CVS</hidepatterns> + <hidenonprojectfiles>false</hidenonprojectfiles> + </tree> + </kdevfileview> + <kdevdoctreeview> + <ignoretocs> + <toc>ada</toc> + <toc>ada_bugs_gcc</toc> + <toc>bash</toc> + <toc>bash_bugs</toc> + <toc>clanlib</toc> + <toc>fortran_bugs_gcc</toc> + <toc>gnome1</toc> + <toc>gnustep</toc> + <toc>gtk</toc> + <toc>gtk_bugs</toc> + <toc>haskell</toc> + <toc>haskell_bugs_ghc</toc> + <toc>java_bugs_gcc</toc> + <toc>java_bugs_sun</toc> + <toc>opengl</toc> + <toc>pascal_bugs_fp</toc> + <toc>php</toc> + <toc>php_bugs</toc> + <toc>perl</toc> + <toc>perl_bugs</toc> + <toc>python</toc> + <toc>python_bugs</toc> + <toc>ruby</toc> + <toc>ruby_bugs</toc> + <toc>sdl</toc> + <toc>stl</toc> + <toc>sw</toc> + <toc>w3c-dom-level2-html</toc> + <toc>w3c-svg</toc> + <toc>w3c-uaag10</toc> + <toc>wxwidgets_bugs</toc> + </ignoretocs> + <ignoreqt_xml> + <toc>qmake User Guide</toc> + </ignoreqt_xml> + </kdevdoctreeview> + <kdevdebugger> + <general> + <dbgshell>libtool</dbgshell> + <programargs/> + <gdbpath/> + <configGdbScript/> + <runShellScript/> + <runGdbScript/> + <breakonloadinglibs>true</breakonloadinglibs> + <separatetty>false</separatetty> + <floatingtoolbar>false</floatingtoolbar> + </general> + <display> + <staticmembers>false</staticmembers> + <demanglenames>true</demanglenames> + <outputradix>10</outputradix> + </display> + </kdevdebugger> + <kdevfilecreate> + <useglobaltypes> + <type ext="ui" /> + <type ext="cpp" /> + <type ext="h" /> + </useglobaltypes> + </kdevfilecreate> + <kdevautoproject> + <make> + <envvars> + <envvar value="1" name="WANT_AUTOCONF_2_5" /> + <envvar value="1" name="WANT_AUTOMAKE_1_6" /> + </envvars> + <abortonerror>true</abortonerror> + <numberofjobs>1</numberofjobs> + <dontact>false</dontact> + <makebin/> + <prio>0</prio> + </make> + <run> + <directoryradio>executable</directoryradio> + <customdirectory>/</customdirectory> + <mainprogram/> + <programargs/> + <terminal>false</terminal> + <autocompile>true</autocompile> + <envvars/> + </run> + <general> + <useconfiguration>default</useconfiguration> + </general> + <configurations> + <default> + <envvars/> + </default> + </configurations> + </kdevautoproject> + <cppsupportpart> + <filetemplates> + <interfacesuffix>.h</interfacesuffix> + <implementationsuffix>.cpp</implementationsuffix> + </filetemplates> + </cppsupportpart> + <kdevcppsupport> + <qt> + <used>true</used> + <version>3</version> + <root>/opt/qt33</root> + </qt> + <codecompletion> + <includeGlobalFunctions>true</includeGlobalFunctions> + <includeTypes>true</includeTypes> + <includeEnums>true</includeEnums> + <includeTypedefs>false</includeTypedefs> + <automaticCodeCompletion>true</automaticCodeCompletion> + <automaticArgumentsHint>true</automaticArgumentsHint> + <automaticHeaderCompletion>true</automaticHeaderCompletion> + <codeCompletionDelay>250</codeCompletionDelay> + <argumentsHintDelay>400</argumentsHintDelay> + <headerCompletionDelay>250</headerCompletionDelay> + </codecompletion> + <creategettersetter> + <prefixGet/> + <prefixSet>set</prefixSet> + <prefixVariable>m_,_</prefixVariable> + <parameterName>theValue</parameterName> + <inlineGet>true</inlineGet> + <inlineSet>true</inlineSet> + </creategettersetter> + <references> + <pcs>Qt</pcs> + <pcs>KDElibs</pcs> + </references> + </kdevcppsupport> +</kdevelop> diff --git a/kplato/kplato.rc b/kplato/kplato.rc new file mode 100644 index 00000000..b2dbe3b7 --- /dev/null +++ b/kplato/kplato.rc @@ -0,0 +1,152 @@ +<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd" > +<kpartgui name="KPlato" version="4"> + <MenuBar> + <Menu name="edit"><text>&Edit</text> + <Action name="koffice_undo"/> + <Action name="koffice_redo"/> + <Separator/> +<!-- <Action name="edit_cut"/> + <Action name="edit_copy"/> + <Action name="edit_paste"/> + <Separator/> --> + <Action name="delete_task"/> + <Action name="indent_task"/> + <Action name="unindent_task"/> + <Action name="move_task_up"/> + <Action name="move_task_down"/> + <Separator/> + <Action name="link_mode"/> + </Menu> + <Menu name="view"><text>&View</text> + <Action name="view_expected"/> + <Action name="view_optimistic"/> + <Action name="view_pessimistic"/> + <Separator/> + <Action name="view_gantt"/> + <Action name="view_gantt_showResources"/> + <Action name="view_gantt_showTaskName"/> + <Action name="view_gantt_showTaskLinks"/> + <Action name="view_gantt_showProgress"/> + <Action name="view_gantt_showFloat"/> + <Action name="view_gantt_showCriticalPath"/> + <Action name="view_gantt_showCriticalTasks"/> + <Action name="view_task_appointments"/> + <Separator/> + <!--<Action name="view_pert"/> --> + <Separator/> + <Action name="view_resources"/> + <Action name="view_resource_appointments"/> + <Separator/> + <Action name="view_accounts"/> + <Separator/> + <Action name="view_reports"/> + </Menu> + <Menu name="insert"><text>&Insert</text> + <Action name="add_task"/> + <Action name="add_sub_task"/> + <Action name="add_milestone"/> + </Menu> + <Menu name="project"><text>&Project</text> + <Action name="project_edit"/> + <Action name="project_worktime"/> + <Action name="project_calendar"/> + <Action name="project_accounts"/> + <Action name="project_resources"/> + <Separator/> + <Menu name="Calculate"><text>&Calculate</text> + <Action name="project_calculate_expected"/> + <Action name="project_calculate_optimistic"/> + <Action name="project_calculate_pessimistic"/> + </Menu> + </Menu> + <Menu name="tools"><text>&Tools</text> + <Action name="tools_define_wbs"/> + <Action name="tools_generate_wbs"/> + </Menu> + <Menu name="settings"><text>&Settings</text> + <Action name="configure" group="settings_configure"/> + </Menu> + </MenuBar> + <ToolBar name="view" position="left"><Text>View</Text> + <Action name="view_gantt"/> + <!--<Action name="view_pert"/> --> + <Action name="view_resources"/> + <Action name="view_resourceuse"/> + <Action name="view_accounts"/> + <Action name="view_reports"/> + </ToolBar> + <ToolBar name="project" position="top"><Text>Project</Text> + <Action name="koffice_undo"/> + <Action name="koffice_redo"/> + <Separator/> + <Action name="project_edit"/> + <Action name="project_calculate"> + <!-- These must be added (I think) programatically + <Action name="project_calculate_expected"/> + <Action name="project_calculate_optimistic"/> + <Action name="project_calculate_pessimistic"/> --> + </Action> + <Separator/> + <Action name="add_task"/> + <Action name="add_sub_task"/> + <Action name="add_milestone"/> + <Action name="delete_task"/> + <Separator/> + <Action name="indent_task"/> + <Action name="unindent_task"/> + <Action name="move_task_up"/> + <Action name="move_task_down"/> + </ToolBar> + <ToolBar name="report" position="top"><Text>Report</Text> + <Action name="go_firstpage"/> + <Action name="go_nextpage"/> + <Action name="go_prevpage"/> + <Action name="go_lastpage"/> + </ToolBar> + <Menu name="node_popup"> +<!-- <Action name="edit_cut"/> + <Action name="edit_copy"/> + <Action name="edit_paste"/> + <Separator/> --> + <Action name="delete_task"/> + <Action name="indent_task"/> + <Action name="unindent_task"/> + <Action name="move_task_up"/> + <Action name="move_task_down"/> + </Menu> + <Menu name="task_popup"> + <Action name="node_properties"/> + <Separator/> + <!-- <Action name="edit_cut"/> + <Action name="edit_copy"/> + <Action name="edit_paste"/> + <Separator/> --> + <Action name="delete_task"/> + <Action name="indent_task"/> + <Action name="unindent_task"/> + <Action name="move_task_up"/> + <Action name="move_task_down"/> + <Separator/> + <Action name="task_progress"/> + </Menu> + <Menu name="summarytask_popup"> + <Action name="node_properties"/> + <Separator/> + <!-- <Action name="edit_cut"/> + <Action name="edit_copy"/> + <Action name="edit_paste"/> + <Separator/> --> + <Action name="delete_task"/> + <Action name="indent_task"/> + <Action name="unindent_task"/> + <Action name="move_task_up"/> + <Action name="move_task_down"/> + </Menu> + <Menu name="resource_popup"> + <Action name="edit_resource"/> +<!-- <Separator/> + <Action name="edit_cut"/> + <Action name="edit_copy"/> + <Action name="edit_paste"/> --> + </Menu> +</kpartgui> diff --git a/kplato/kplato_readonly.rc b/kplato/kplato_readonly.rc new file mode 100644 index 00000000..463c5773 --- /dev/null +++ b/kplato/kplato_readonly.rc @@ -0,0 +1,24 @@ +<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd" ><kpartgui name="KPlato" version="4"> +<MenuBar> + <Menu name="view"><text>&View</text> + <Separator/> + <Action name="view_gantt"/> + <Separator/> + <Action name="view_gantt_showResources"/> + <Action name="view_gantt_showTaskName"/> + <Action name="view_gantt_showTaskLinks"/> + <Action name="view_gantt_showProgress"/> + <Action name="view_gantt_showFloat"/> + <Action name="view_gantt_showCriticalPath"/> + <Action name="view_gantt_showCriticalTasks"/> + <Separator/> + <Separator/> + <Action name="view_pert"/> + <Separator/> + <Action name="view_resources"/> + <Separator/> + <Action name="view_resourceuse"/> + <Separator/> + </Menu> +</MenuBar> +</kpartgui> diff --git a/kplato/kplatopart.desktop b/kplato/kplatopart.desktop new file mode 100644 index 00000000..d8c2b89a --- /dev/null +++ b/kplato/kplatopart.desktop @@ -0,0 +1,106 @@ +[Desktop Entry] +Name=KOffice Project Management Component +Name[bg]=Компонент за управление на компоненти в KOffice +Name[ca]=Component de gestió de projectes de KOffice +Name[cy]=Cydran Rheoli Cywaith KOffice +Name[da]=KOffice projekthåndteringskomponent +Name[de]=KOffice-Komponente für Projektmanagement +Name[el]=Συστατικό διαχείρισης έργων του KOffice +Name[eo]=KOffice Projektadministra Komponanto +Name[es]=Componente de gestión de proyectos de KOffice +Name[et]=KOffice'i projektihalduse komponent +Name[fa]=مؤلفۀ مدیریت پروژۀ KOffice +Name[fi]=KOfficen projektinhallintakomponentti +Name[fr]=Composant gestion de projets de KOffice +Name[fy]=KOffice projektbehear komponint +Name[gl]=Componente de Xestión de Proxectos de KOffice +Name[he]=רכיב של KOffice לניהול פרוייקטים +Name[hi]=के-ऑफ़िस परियोजना प्रबंधक अवयव +Name[hr]=KOffice komponenta upravljanja projektima +Name[hu]=KOffice projektkezelő komponens +Name[is]=Verkefnisstjórnarhluti KOffice +Name[it]=Componente per la gestione dei progetti di KOffice +Name[ja]=KOffice プロジェクト管理コンポーネント +Name[km]=សមាសភាគគ្រប់គ្រងគម្រោងសម្រាប់KOffice +Name[lt]=KOffice projektų valdymo komponentas +Name[lv]=KOffice projektu pārvaldīšanas komponente +Name[ms]=Komponen Pengurusan Projek KOffice +Name[nb]=KOffice-komponent for prosjektstyring +Name[nds]=KOffice-Komponent för Projektpleeg +Name[ne]=केडीई कार्यालय परियोजना व्यवस्थापन अवयव +Name[nl]=Koffice Projectbeheer Component +Name[nn]=KOffice-komponent for prosjektstyring +Name[pl]=Komponent zarządzania projektami dla KOffice +Name[pt]=Componente de Gestão de Projectos do KOffice +Name[pt_BR]=Componente de Gerenciamento de Projetos do KOffice +Name[ru]=Компонент управления проектами KOffice +Name[se]=KOffice prošeaktahálddašan komponeanta +Name[sk]=Koffice modul pre správu projektov +Name[sl]=Komponenta za upravljanje projektov za KOffice +Name[sr]=KOffice-ова компонента за управљање пројектима +Name[sr@Latn]=KOffice-ova komponenta za upravljanje projektima +Name[sv]=Koffice-projekthanteringskomponent +Name[tg]=Лоиҳаи роҳбари компоненти KOffice +Name[tr]=KOffice Proje Yönetimi Bileşeni +Name[uk]=Компонент керування проектами для KOffice +Name[uz]=KOffice loyiha boshqaruv komponenti +Name[uz@cyrillic]=KOffice лойиҳа бошқарув компоненти +Name[wa]=Componint di manaedjaedje di prodjet di KOffice +Name[zh_CN]=KOffice 项目管理组件部件 +Name[zh_TW]=KOffice 專案管理元件 +X-KDE-Library=libkplatopart +MimeType=application/x-vnd.kde.kplato +Type=Service +ServiceTypes=KOfficePart,KParts/ReadOnlyPart,KParts/ReadWritePart +X-KDE-NativeMimeType=application/x-vnd.kde.kplato +GenericName=Project Management +GenericName[bg]=Управление на проекти +GenericName[br]=Mererezh raktresioù +GenericName[ca]=Gestió de projectes +GenericName[cy]=Rheoli Cywaith +GenericName[da]=Projekthåndtering +GenericName[de]=Projektmanagement +GenericName[el]=Διαχείριση έργων +GenericName[eo]=Projektadministrado +GenericName[es]=Gestión de proyecto +GenericName[et]=Projektihaldus +GenericName[fa]=مدیریت پروژه +GenericName[fi]=Projektinhallinta +GenericName[fr]=Gestion de projets +GenericName[fy]=Projektbehear +GenericName[ga]=Bainisteoireacht Tionscadal +GenericName[gl]=Xestión de Proxectos +GenericName[he]=ניהול פרוייקטים +GenericName[hr]=Upravljanje projektima +GenericName[hu]=Projektkezelő +GenericName[is]=Verkefnisstjórn +GenericName[it]=Gestione dei progetti +GenericName[ja]=プロジェクト管理 +GenericName[km]=កម្មវិធីគ្រប់គ្រងគម្រោង +GenericName[lt]=Projektų valdymas +GenericName[lv]=Projektu pārvaldīšana +GenericName[ms]=Pengurusan Projek +GenericName[nb]=Prosjektstyring +GenericName[nds]=Projektpleeg +GenericName[ne]=परियोजना व्यवस्थापन +GenericName[nl]=Projectbeheer +GenericName[nn]=Prosjektstyring +GenericName[pl]=Zarządzanie projektami +GenericName[pt]=Gestão de Projectos +GenericName[pt_BR]=Gerenciamento de Projeto +GenericName[ru]=Проекты +GenericName[se]=Prošeaktahálddašeapmi +GenericName[sk]=Správa projektov +GenericName[sl]=Projektno vodenje +GenericName[sr]=Управљање пројектима +GenericName[sr@Latn]=Upravljanje projektima +GenericName[sv]=Projekthantering +GenericName[tg]=Лоиҳаи Роҳбар +GenericName[tr]=Proje Yönetimi +GenericName[uk]=Керування проектами +GenericName[uz]=Loyiha boshqaruvi +GenericName[uz@cyrillic]=Лойиҳа бошқаруви +GenericName[wa]=Manaedjaedje di prodjet +GenericName[zh_CN]=项目管理 +GenericName[zh_TW]=專案管理 +Icon=kplato diff --git a/kplato/kptaboutdata.h b/kplato/kptaboutdata.h new file mode 100644 index 00000000..d19550b3 --- /dev/null +++ b/kplato/kptaboutdata.h @@ -0,0 +1,49 @@ +/* This file is part of the KDE project + Copyright (C) 1998 - 2001 Reginald Stadlbauer <reggie@kde.org> + Copyright (C) 2004 Dag Andersen <danders@get2net.dk> + + 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 KPLATO_ABOUTDATA +#define KPLATO_ABOUTDATA + +#include <kaboutdata.h> +#include <klocale.h> + +namespace KPlato +{ + +static const char* KPLATO_DESCRIPTION=I18N_NOOP("KPlato - KDE Planning Tool"); +static const char* KPLATO_VERSION="0.6.3"; + +KAboutData * newAboutData() +{ + KAboutData * aboutData=new KAboutData( "kplato", I18N_NOOP("KPlato"), + KPLATO_VERSION, KPLATO_DESCRIPTION, KAboutData::License_GPL, + I18N_NOOP("(c) 1998-2006, The KPlato Team"), 0, + "http://www.koffice.org/kplato/" ); + aboutData->addAuthor("Thomas Zander", 0, 0 ); // please don't re-add, I don't like getting personal emails :) + aboutData->addAuthor("Bo Thorsen", 0, "bo@sonofthor.dk"); + aboutData->addAuthor("Dag Andersen", 0, "danders@get2net.dk"); + aboutData->addAuthor("Raphael Langerhorst",0,"raphael.langerhorst@kdemail.net"); + aboutData->addCredit("Nuno Pinheiro and Danny Allen", I18N_NOOP("Application icon for kplato"), "danny@dannyallen.co.uk"); + return aboutData; +} + +} //KPlato namespace + +#endif diff --git a/kplato/kptaccount.cc b/kplato/kptaccount.cc new file mode 100644 index 00000000..42dff604 --- /dev/null +++ b/kplato/kptaccount.cc @@ -0,0 +1,512 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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. +*/ + +#include <qdom.h> +#include <qstring.h> + +#include <klocale.h> + +#include <kdebug.h> + +#include "kptaccount.h" +#include "kptduration.h" +#include "kptproject.h" + +namespace KPlato +{ + +Account::Account() + : m_name(), + m_description(), + m_list(0), + m_parent(0), + m_accountList(), + m_costPlaces() { + + m_accountList.setAutoDelete(true); + m_costPlaces.setAutoDelete(true); +} + +Account::Account(QString name, QString description) + : m_name(name), + m_description(description), + m_list(0), + m_parent(0), + m_accountList(), + m_costPlaces() { + + m_accountList.setAutoDelete(true); + m_costPlaces.setAutoDelete(true); +} + +Account::~Account() { + m_accountList.clear(); + if (findAccount() == this) { + removeId(); // only remove myself (I may be just a working copy) + } + if (m_list) + m_list->accountDeleted(this); +} + + +void Account::setName(QString name) { + if (findAccount() == this) { + removeId(); + } + m_name = name; + insertId(); +} + +void Account::append(Account *account){ + Q_ASSERT(account); + m_accountList.append(account); + account->setList(m_list); + account->setParent(this); + insertId(account); +} + +void Account::insertChildren() { + AccountListIterator it = m_accountList; + for (; it.current(); ++it) { + it.current()->setList(m_list); + it.current()->setParent(this); + insertId(it.current()); + it.current()->insertChildren(); + } +} + +void Account::take(Account *account) { + if (account == 0) { + return; + } + if (account->parent() == this) { + m_accountList.take(m_accountList.findRef(account)); + } else if (account->parent()) { + account->parent()->take(account); + } else { + m_list->take(account); + } + //kdDebug()<<k_funcinfo<<account->name()<<endl; +} + +bool Account::load(QDomElement &element, const Project &project) { + m_name = element.attribute("name"); + m_description = element.attribute("description"); + QDomNodeList list = element.childNodes(); + for (unsigned int i=0; i<list.count(); ++i) { + if (list.item(i).isElement()) { + QDomElement e = list.item(i).toElement(); + if (e.tagName() == "costplace") { + Account::CostPlace *child = new Account::CostPlace(this); + if (child->load(e, project)) { + append(child); + } else { + delete child; + } + } else if (e.tagName() == "account") { + Account *child = new Account(); + if (child->load(e, project)) { + m_accountList.append(child); + } else { + // TODO: Complain about this + kdWarning()<<k_funcinfo<<"Loading failed"<<endl; + delete child; + } + } + } + } + return true; +} + +void Account::save(QDomElement &element) const { + QDomElement me = element.ownerDocument().createElement("account"); + element.appendChild(me); + me.setAttribute("name", m_name); + me.setAttribute("description", m_description); + QPtrListIterator<Account::CostPlace> cit = m_costPlaces; + for (; cit.current(); ++cit) { + cit.current()->save(me); + } + AccountListIterator it = m_accountList; + for (; it.current(); ++it) { + it.current()->save(me); + } +} + +Account::CostPlace *Account::findCostPlace(const Node &node) const { + QPtrListIterator<CostPlace> it = m_costPlaces; + for (; it.current(); ++it) { + if (&node == it.current()->node()) { + return it.current(); + } + } + return 0; +} + +Account::CostPlace *Account::findRunning(const Node &node) const { + Account::CostPlace *cp = findCostPlace(node); + return cp && cp->running() ? cp : 0; +} + +void Account::removeRunning(const Node &node) { + Account::CostPlace *cp = findRunning(node); + if (cp) { + cp->setRunning(false); + if (cp->isEmpty()) { + m_costPlaces.removeRef(cp); + } + } +} + +void Account::addRunning(Node &node) { + Account::CostPlace *cp = findCostPlace(node); + if (cp) { + cp->setRunning(true); + return; + } + append(new CostPlace(this, &node, true)); +} + +Account::CostPlace *Account::findStartup(const Node &node) const { + Account::CostPlace *cp = findCostPlace(node); + return cp && cp->startup() ? cp : 0; +} + +void Account::removeStartup(const Node &node) { + Account::CostPlace *cp = findStartup(node); + if (cp) { + cp->setStartup(false); + if (cp->isEmpty()) { + m_costPlaces.removeRef(cp); + } + } +} + +void Account::addStartup(Node &node) { + Account::CostPlace *cp = findCostPlace(node); + if (cp) { + cp->setStartup(true); + return; + } + append(new CostPlace(this, &node, false, true)); + +} + +Account::CostPlace *Account::findShutdown(const Node &node) const { + Account::CostPlace *cp = findCostPlace(node); + return cp && cp->shutdown() ? cp : 0; +} + +void Account::removeShutdown(const Node &node) { + Account::CostPlace *cp = findShutdown(node); + if (cp) { + cp->setShutdown(false); + if (cp->isEmpty()) { + m_costPlaces.removeRef(cp); + } + } +} + +void Account::addShutdown(Node &node) { + Account::CostPlace *cp = findCostPlace(node); + if (cp) { + cp->setShutdown(true); + return; + } + append(new CostPlace(this, &node, false, false, true)); +} + +Account *Account::findAccount(const QString &id) const { + if (m_list) + return m_list->findAccount(id); + return 0; +} + +bool Account::removeId(const QString &id) { + return (m_list ? m_list->removeId(id) : false); +} + +bool Account::insertId() { + return insertId(this); +} + +bool Account::insertId(const Account *account) { + return (m_list ? m_list->insertId(account) : false); +} + +//------------------------------------ +Account::CostPlace::~CostPlace() { + if (m_node) { + if (m_running) + m_node->setRunningAccount(0); + if (m_startup) + m_node->setStartupAccount(0); + if (m_shutdown) + m_node->setShutdownAccount(0); + } +} + +void Account::CostPlace::setRunning(bool on ) { + m_running = on; + if (m_node) + m_node->setRunningAccount(on ? m_account : 0); +} + +void Account::CostPlace::setStartup(bool on ) { + m_startup = on; + if (m_node) + m_node->setStartupAccount(on ? m_account : 0); +} + +void Account::CostPlace::setShutdown(bool on ) { + m_shutdown = on; + if (m_node) + m_node->setShutdownAccount(on ? m_account : 0); +} + +bool Account::CostPlace::load(QDomElement &element, const Project &project) { + //kdDebug()<<k_funcinfo<<endl; + m_nodeId = element.attribute("node-id"); + if (m_nodeId.isEmpty()) { + kdError()<<k_funcinfo<<"No node id"<<endl; + return false; + } + m_node = project.findNode(m_nodeId); + if (m_node == 0) { + kdError()<<k_funcinfo<<"Cannot not find node with id: "<<m_nodeId<<endl; + return false; + } + setRunning(element.attribute("running-cost").toInt()); + setStartup(element.attribute("startup-cost").toInt()); + setShutdown(element.attribute("shutdown-cost").toInt()); + return true; +} + +void Account::CostPlace::save(QDomElement &element) const { + //kdDebug()<<k_funcinfo<<endl; + QDomElement me = element.ownerDocument().createElement("costplace"); + element.appendChild(me); + me.setAttribute("node-id", m_nodeId); + me.setAttribute("running-cost", m_running); + me.setAttribute("startup-cost", m_startup); + me.setAttribute("shutdown-cost", m_shutdown); + +} + +//--------------------------------- +Accounts::Accounts(Project &project) + : m_project(project), + m_accountList(), + m_idDict(), + m_defaultAccount(0) { + + m_accountList.setAutoDelete(true); +} + +Accounts::~Accounts() { + m_accountList.clear(); +} + +EffortCostMap Accounts::plannedCost(const Account &account, const QDate &start, const QDate &end) { + EffortCostMap ec; + QPtrListIterator<Account::CostPlace> it = account.costPlaces(); + for (; it.current(); ++it) { + Node *n = it.current()->node(); + if (n == 0) { + continue; + } + //kdDebug()<<k_funcinfo<<"n="<<n->name()<<endl; + if (it.current()->running()) { + ec += n->plannedEffortCostPrDay(start, end); + } + if (it.current()->startup()) { + if (n->startTime().date() >= start && + n->startTime().date() <= end) + ec.add(n->startTime().date(), EffortCost(Duration::zeroDuration, n->startupCost())); + } + if (it.current()->shutdown()) { + if (n->endTime().date() >= start && + n->endTime().date() <= end) + ec.add(n->endTime().date(), EffortCost(Duration::zeroDuration, n->shutdownCost())); + } + } + if (&account == m_defaultAccount) { + QDictIterator<Node> nit = m_project.nodeDict(); + for (; nit.current(); ++nit) { + Node *n = nit.current(); + if (n->runningAccount() == 0) { + ec += n->plannedEffortCostPrDay(start, end); + } + if (n->startupAccount() == 0) { + if (n->startTime().date() >= start && + n->startTime().date() <= end) + ec.add(n->startTime().date(), EffortCost(Duration::zeroDuration, n->startupCost())); + } + if (n->shutdownAccount() == 0) { + if (n->endTime().date() >= start && + n->endTime().date() <= end) + ec.add(n->endTime().date(), EffortCost(Duration::zeroDuration, n->shutdownCost())); + } + } + } + return ec; +} + +void Accounts::append(Account *account) { + Q_ASSERT(account); + m_accountList.append(account); + account->setList(this); + account->setParent(0); // incase... + insertId(account); + //kdDebug()<<k_funcinfo<<account->name()<<endl; + account->insertChildren(); +} + +void Accounts::take(Account *account){ + if (account == 0) { + return; + } + removeId(account->name()); + if (account->parent()) { + account->parent()->take(account); + return; + } + m_accountList.take(m_accountList.findRef(account)); + //kdDebug()<<k_funcinfo<<account->name()<<endl; +} + +bool Accounts::load(QDomElement &element, const Project &project) { + QDomNodeList list = element.childNodes(); + for (unsigned int i=0; i<list.count(); ++i) { + if (list.item(i).isElement()) { + QDomElement e = list.item(i).toElement(); + if (e.tagName() == "account") { + Account *child = new Account(); + if (child->load(e, project)) { + append(child); + } else { + // TODO: Complain about this + kdWarning()<<k_funcinfo<<"Loading failed"<<endl; + delete child; + } + } + } + } + if (element.hasAttribute("default-account")) { + m_defaultAccount = findAccount(element.attribute("default-account")); + if (m_defaultAccount == 0) { + kdWarning()<<k_funcinfo<<"Could not find default account."<<endl; + } + } + return true; +} + +void Accounts::save(QDomElement &element) const { + QDomElement me = element.ownerDocument().createElement("accounts"); + element.appendChild(me); + if (m_defaultAccount) { + me.setAttribute("default-account", m_defaultAccount->name()); + } + AccountListIterator it = m_accountList; + for (; it.current(); ++it) { + it.current()->save(me); + } +} + +QStringList Accounts::costElements() const { + QDictIterator<Account> it(m_idDict); + QStringList l; + for(; it.current(); ++it) { + if (it.current()->isElement()) + l << it.currentKey(); + } + return l; +} + + +QStringList Accounts::nameList() const { + QDictIterator<Account> it(m_idDict); + QStringList l; + for(; it.current(); ++it) { + l << it.currentKey(); + } + return l; +} + +Account *Accounts::findRunningAccount(const Node &node) const { + QDictIterator<Account> it = m_idDict; + for (; it.current(); ++it) { + if (it.current()->findRunning(node)) + return it.current(); + } + return 0; +} + +Account *Accounts::findStartupAccount(const Node &node) const { + QDictIterator<Account> it = m_idDict; + for (; it.current(); ++it) { + if (it.current()->findStartup(node)) + return it.current(); + } + return 0; +} + +Account *Accounts::findShutdownAccount(const Node &node) const { + QDictIterator<Account> it = m_idDict; + for (; it.current(); ++it) { + if (it.current()->findShutdown(node)) + return it.current(); + } + return 0; +} + +Account *Accounts::findAccount(const QString &id) const { + return m_idDict.find(id); +} + +bool Accounts::insertId(const Account *account) { + Q_ASSERT(account); + Account *a = m_idDict.find(account->name()); + if (a == 0) { + //kdDebug()<<k_funcinfo<<"'"<<account->name()<<"' inserted"<<endl; + m_idDict.insert(account->name(), account); + return true; + } + if (a == account) { + kdDebug()<<k_funcinfo<<"'"<<a->name()<<"' allready exists"<<endl; + return true; + } + //TODO: Create unique id? + kdWarning()<<k_funcinfo<<"Insert failed"<<endl; + return false; +} + +bool Accounts::removeId(const QString &id) { + bool res = m_idDict.remove(id); + //kdDebug()<<k_funcinfo<<id<<": removed="<<res<<endl; + return res; +} + +#ifndef NDEBUG +void Accounts::printDebug(QString /*indent*/) { +} +void Account::printDebug(QString /*indent*/) { +} +#endif +} //namespace KPlato diff --git a/kplato/kptaccount.h b/kplato/kptaccount.h new file mode 100644 index 00000000..845f70d6 --- /dev/null +++ b/kplato/kptaccount.h @@ -0,0 +1,226 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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 KPTACCOUNT_H +#define KPTACCOUNT_H + +#include <qdatetime.h> +#include <qdict.h> +#include <qptrlist.h> +#include <qstringlist.h> + +#include "kpteffortcostmap.h" +#include "kptnode.h" + +#include <kdebug.h> + +class QDomElement; +class QString; + +namespace KPlato +{ + +class Accounts; +class Account; + + +/** + * Account holds one account. + * An account can have any number of sub-accounts. + * Account names must be unique. + */ +class Account +{ +public: + + /** + * Constructor. + */ + Account(); + + /** + * + */ + Account(QString name, QString description=QString::null); + + /** + * Destructor. + */ + ~Account(); + + + QString name() const { return m_name; } + void setName(QString name); + + QString description() const { return m_description; } + void setDescription(QString desc) { m_description = desc; } + + bool isElement() const { return m_accountList.isEmpty(); } + + Accounts *list() const { return m_list; } + void setList(Accounts *list) { m_list = list; } + Account *parent() const { return m_parent; } + void setParent(Account *parent) { m_parent = parent; } + void clear() { m_accountList.clear(); } + void append(Account *account); + void take(Account *account); + void insertChildren(); + + bool load(QDomElement &element, const Project &project); + void save(QDomElement &element) const; + + const QPtrList<Account> &accountList() const { return m_accountList; } + + Account *findAccount() const { return findAccount(m_name); } + Account *findAccount(const QString &id) const; + bool removeId() { return removeId(m_name); } + bool removeId(const QString &id); + bool insertId(); + bool insertId(const Account *account); + + class CostPlace { + public: + CostPlace() + : m_account(0), m_nodeId(), m_node(0), m_running(false), m_startup(false), m_shutdown(false) + {} + CostPlace(Account *acc) + : m_account(acc), m_nodeId(), m_node(0), m_running(false), m_startup(false), m_shutdown(false) + {} + CostPlace(Account *acc, Node *node, bool running=false, bool strtup=false, bool shutdown=false) + : m_account(acc), m_nodeId(node->id()), m_node(node) { + if (node) { + setRunning(running); + setStartup(strtup); + setShutdown(shutdown); + } + } + CostPlace(CostPlace *cp) { + m_account = cp->m_account; + m_nodeId = cp->m_nodeId; + m_node = cp->m_node; + m_running = cp->m_running; + m_startup = cp->m_startup; + m_shutdown = cp->m_shutdown; + } + ~CostPlace(); + + bool isEmpty() { return !(m_running || m_startup || m_shutdown); } + Node *node() const { return m_node; } + + bool running() const { return m_running; } + void setRunning(bool on ); + bool startup() const { return m_startup; } + void setStartup(bool on); + bool shutdown() const { return m_shutdown; } + void setShutdown(bool on); + + bool load(QDomElement &element, const Project &project); + void save(QDomElement &element) const; + + private: + Account *m_account; + QString m_nodeId; + Node *m_node; + bool m_running; + bool m_startup; + bool m_shutdown; + }; + + void append(const CostPlace *cp) { m_costPlaces.append(cp); } + const QPtrList<CostPlace> &costPlaces() const {return m_costPlaces; } + Account::CostPlace *findCostPlace(const Node &node) const; + CostPlace *findRunning(const Node &node) const; + void removeRunning(const Node &node); + void addRunning(Node &node); + CostPlace *findStartup(const Node &node) const; + void removeStartup(const Node &node); + void addStartup(Node &node); + CostPlace *findShutdown(const Node &node) const; + void removeShutdown(const Node &node); + void addShutdown(Node &node); + +private: + QString m_name; + QString m_description; + Accounts *m_list; + Account *m_parent; + QPtrList<Account> m_accountList; + QPtrList<CostPlace> m_costPlaces; + +#ifndef NDEBUG +public: + void printDebug(QString indent); +#endif +}; + +typedef QPtrList<Account> AccountList; +typedef QPtrListIterator<Account> AccountListIterator; + +/** + * Accounts administrates all accounts. + */ + +class Accounts +{ +public: + Accounts(Project &project); + ~Accounts(); + + Account *defaultAccount() const { return m_defaultAccount; } + void setDefaultAccount(Account *account) { m_defaultAccount = account; } + + EffortCostMap plannedCost(const Account &account, const QDate &start, const QDate &end); + + void clear() { m_accountList.clear(); m_idDict.clear(); } + void append(Account *account); + void take(Account *account); + + bool load(QDomElement &element, const Project &project); + void save(QDomElement &element) const; + + QStringList costElements() const; + QStringList nameList() const; + + const AccountList &accountList() const { return m_accountList; } + + Account *findRunningAccount(const Node &node) const; + Account *findStartupAccount(const Node &node) const; + Account *findShutdownAccount(const Node &node) const; + Account *findAccount(const QString &id) const; + bool insertId(const Account *account); + bool removeId(const QString &id); + + void accountDeleted(Account *account) + { if (account == m_defaultAccount) m_defaultAccount = 0; } +private: + Project &m_project; + AccountList m_accountList; + QDict<Account> m_idDict; + + Account *m_defaultAccount; + +#ifndef NDEBUG +public: + void printDebug(QString indent); +#endif +}; + +} //namespace KPlato + +#endif diff --git a/kplato/kptaccountsdialog.cc b/kplato/kptaccountsdialog.cc new file mode 100644 index 00000000..5f03b126 --- /dev/null +++ b/kplato/kptaccountsdialog.cc @@ -0,0 +1,50 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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. +*/ + +#include "kptaccountsdialog.h" + +#include "kptaccountspanel.h" + +#include <klocale.h> + +namespace KPlato +{ + +AccountsDialog::AccountsDialog(Accounts &acc, QWidget *p, const char *n) + : KDialogBase(Swallow, i18n("Edit Accounts"), Ok|Cancel, Ok, p, n, true, true) +{ + m_panel = new AccountsPanel(acc, this); + setMainWidget(m_panel); + + enableButtonOK(false); + connect(m_panel, SIGNAL(changed(bool)), SLOT(enableButtonOK(bool))); +} + +KCommand *AccountsDialog::buildCommand(Part *part) { + return m_panel->buildCommand(part); +} + +void AccountsDialog::slotOk() { + m_panel->slotOk(); + accept(); +} + +} //namespace KPlato + +#include "kptaccountsdialog.moc" diff --git a/kplato/kptaccountsdialog.h b/kplato/kptaccountsdialog.h new file mode 100644 index 00000000..eb7cd1b7 --- /dev/null +++ b/kplato/kptaccountsdialog.h @@ -0,0 +1,52 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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 KPTACCOUNTSDIALOG_H +#define KPTACCOUNTSDIALOG_H + +#include <kdialogbase.h> + +class QWidget; + +class KCommand; + +namespace KPlato +{ + +class Accounts; +class AccountsPanel; +class Part; + +class AccountsDialog : public KDialogBase { + Q_OBJECT +public: + AccountsDialog(Accounts &acc, QWidget *parent=0, const char *name=0); + + KCommand *buildCommand(Part *part); + +protected slots: + void slotOk(); + +private: + AccountsPanel *m_panel; +}; + +} //namespace KPlato + +#endif diff --git a/kplato/kptaccountspanel.cc b/kplato/kptaccountspanel.cc new file mode 100644 index 00000000..70fba38e --- /dev/null +++ b/kplato/kptaccountspanel.cc @@ -0,0 +1,452 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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. +*/ + +#include "kptaccountspanel.h" +#include "kptaccount.h" +#include "kptcommand.h" +#include "kptproject.h" + +#include <qcombobox.h> +#include <qheader.h> +#include <qlistview.h> +#include <qpushbutton.h> +#include <qstring.h> +#include <qstringlist.h> + +#include <klistview.h> +#include <klocale.h> + +#include <kdebug.h> + +namespace KPlato +{ + +class AccountItem : public KListViewItem { +public: + AccountItem(AccountsPanel &pan, QListView *parent) + : KListViewItem(parent), account(0), panel(pan) + { init(); } + AccountItem(AccountsPanel &pan, QListViewItem *parent) + : KListViewItem(parent), account(0), panel(pan) + { init(); } + AccountItem(AccountsPanel &pan, QListView *parent, QString label1, QString label2 = QString::null) + : KListViewItem(parent, label1, label2), account(0), panel(pan) + { init(); } + AccountItem(AccountsPanel &pan, QListViewItem *parent, QString label1, QString label2 = QString::null) + : KListViewItem(parent, label1, label2), account(0), panel(pan) + { init(); } + AccountItem(AccountsPanel &pan, QListView *parent, QListViewItem *after) + : KListViewItem(parent, after), account(0), panel(pan) + { init(); } + AccountItem(AccountsPanel &pan, QListViewItem *parent, QListViewItem *after) + : KListViewItem(parent, after), account(0), panel(pan) + { init(); } + + Account *account; + bool isDefault; + + QString oldText; + AccountsPanel &panel; +protected: + virtual void cancelRename(int col) { + //kdDebug()<<k_funcinfo<<endl; + if ((col == 0 && oldText.isEmpty()) || + (!panel.isUnique(this))) { + return; + } + panel.renameStopped(this); + QListViewItem::cancelRename(col); + setRenameEnabled(col, false); + } +private: + void init() { + setRenameEnabled(0, false); + setRenameEnabled(1, false); + setOpen(true); + isDefault = false; + + } +}; + +AccountsPanel::AccountsPanel(Accounts &acc, QWidget *p, const char *n) + : AccountsPanelBase(p, n), + m_accounts(acc), + m_currentIndex(0), + m_renameItem(0) +{ + + accountList->setRootIsDecorated(true); + accountList->header()->setStretchEnabled(true, 1); + accountList->setItemMargin(2); + accountList->setDefaultRenameAction(QListView::Accept); + addItems(accountList, acc); + + slotSelectionChanged(); + + connect(accountList, SIGNAL(selectionChanged()), SLOT(slotSelectionChanged())); + connect(accountList, SIGNAL(itemRenamed(QListViewItem*, int)), SLOT(slotItemRenamed(QListViewItem*, int))); + connect(accountList, SIGNAL(doubleClicked(QListViewItem*, const QPoint &, int)), SLOT(slotListDoubleClicked(QListViewItem*, const QPoint &, int))); + + connect(removeBtn, SIGNAL(clicked()), SLOT(slotRemoveBtn())); + connect(newBtn, SIGNAL(clicked()), SLOT(slotNewBtn())); + connect(subBtn, SIGNAL(clicked()), SLOT(slotSubBtn())); + + connect(accountsComboBox, SIGNAL(activated(int)), SLOT(slotActivated(int))); + + // Internal hacks, to get renaming to behave along with unique names + // Uses signals to not get in the way of QListView + connect(this, SIGNAL(renameStarted(QListViewItem*, int)), SLOT(slotRenameStarted(QListViewItem*, int))); + connect(this, SIGNAL(startRename(QListViewItem*, int)), SLOT(slotStartRename(QListViewItem*, int))); + connect(this, SIGNAL(selectionChanged()), SLOT(slotSelectionChanged())); +} + +void AccountsPanel::addItems(QListView *lv, Accounts &acc) { + //kdDebug()<<k_funcinfo<<"No of accs: "<<acc.accountList().count()<<endl; + AccountListIterator it = acc.accountList(); + for (; it.current(); ++it) { + QString n = it.current()->name(); + QString d = it.current()->description(); + AccountItem *item = new AccountItem(*this, lv, n, d); + item->account = it.current(); + item->isDefault = (it.current() == acc.defaultAccount()); + if (it.current()->isElement()) { + addElement(item); + } + addItems(item, it.current()); + } +} + +void AccountsPanel::addItems(QListViewItem *item, Account *acc) { + AccountListIterator it = acc->accountList(); + for (; it.current(); ++it) { + QString n = it.current()->name(); + QString d = it.current()->description(); + AccountItem *ai = new AccountItem(*this, item, n, d); + ai->account = it.current(); + ai->isDefault = (it.current() == acc->list()->defaultAccount()); + if (it.current()->isElement()) { + addElement(ai); + } + addItems(ai, it.current()); + } +} + +void AccountsPanel::addElement(const QListViewItem *item) { + if (item->parent()) { + removeElement(item->parent()); + } + m_elements.replace(item->text(0), item); + //kdDebug()<<k_funcinfo<<item->text(0)<<endl; + refreshDefaultAccount(); +} + +void AccountsPanel::removeElement(QListViewItem *item) { + static_cast<AccountItem*>(item)->isDefault = false; + m_elements.remove(item->text(0)); + refreshDefaultAccount(); +} + +void AccountsPanel::refreshDefaultAccount() { + accountsComboBox->clear(); + m_currentIndex = 0; + accountsComboBox->insertItem(i18n("None")); + QDictIterator<QListViewItem> it(m_elements); + for(int i=1; it.current(); ++it, ++i) { + accountsComboBox->insertItem(it.currentKey()); + if (static_cast<AccountItem*>(it.current())->isDefault) { + m_currentIndex = i; + accountsComboBox->setCurrentItem(i); + //kdDebug()<<k_funcinfo<<"Default="<<it.current()->text(0)<<endl; + } + } + //kdDebug()<<k_funcinfo<<"size="<<accountsComboBox->count()<<endl; +} +void AccountsPanel::slotActivated(int index) { + //kdDebug()<<k_funcinfo<<index<<endl; + if (m_currentIndex >= (int)m_elements.count()) { + kdError()<<k_funcinfo<<"currentIndex ("<<m_currentIndex<<") out of range ("<<m_elements.count()<<")"<<endl; + } else if (m_currentIndex > 0) { + AccountItem *i = static_cast<AccountItem*>(m_elements[accountsComboBox->text(m_currentIndex)]); + if (i) + i->isDefault = false; + } + m_currentIndex = 0; + if (index < (int)m_elements.size()) { + AccountItem *i = static_cast<AccountItem*>(m_elements[accountsComboBox->currentText()]); + if (i) { + i->isDefault = true; + m_currentIndex = index; + //kdDebug()<<k_funcinfo<<"currentIndex="<<m_currentIndex<<", "<<m_elements[accountsComboBox->currentText()]->text(0)<<endl; + } + } + slotChanged(); +} + +void AccountsPanel::slotChanged() { + emit changed(true); +} + +void AccountsPanel::slotSelectionChanged() { + //kdDebug()<<k_funcinfo<<endl; + if (m_renameItem) { + removeBtn->setEnabled(false); + newBtn->setEnabled(false); + subBtn->setEnabled(false); + accountList->setSelected(m_renameItem, true); + return; + } + if (accountList->childCount() == 0) { + removeBtn->setEnabled(false); + newBtn->setEnabled(true); + subBtn->setEnabled(false); + return; + } + QListViewItem *i = accountList->selectedItem(); + removeBtn->setEnabled((bool)i); + newBtn->setEnabled(true); + subBtn->setEnabled((bool)i); +} + +void AccountsPanel::slotItemRenamed(QListViewItem *item, int col) { + //kdDebug()<<k_funcinfo<<item->text(0)<<endl; + item->setRenameEnabled(col, false); + m_renameItem = 0; + if (col != 0) { + renameStopped(item); + slotChanged(); + return; + } + if (item->text(0).isEmpty()) { + item->setText(0, static_cast<AccountItem*>(item)->oldText); // keep the old name + } + if (item->text(0).isEmpty()) { + // Not allowed + //kdDebug()<<k_funcinfo<<"name empty"<<endl; + emit startRename(item, 0); + return; + } + if (!isUnique(item)) { + // name must be unique + emit startRename(item, 0); + return; + } + addElement(item); + removeBtn->setEnabled(accountList->selectedItem()); + newBtn->setEnabled(accountList->selectedItem()); + subBtn->setEnabled(accountList->selectedItem()); + renameStopped(item); + slotChanged(); +} + +bool AccountsPanel::isUnique(QListViewItem *item) { + QListViewItemIterator it(accountList); + for (; it.current(); ++it) { + if (it.current() != item && it.current()->text(0) == item->text(0)) { + return false; + } + } + return true; +} + +void AccountsPanel::slotRemoveBtn() { + slotRemoveItem(accountList->selectedItem()); + slotChanged(); +} + +void AccountsPanel::slotNewBtn() { + //kdDebug()<<k_funcinfo<<endl; + QListViewItem *item = accountList->selectedItem(); + if (item && item->text(0).isEmpty()) { + return; + } + QListViewItem *n; + if (item) { + if (item->parent()) { + n = new AccountItem(*this, item->parent(), item); + } else { + n = new AccountItem(*this, accountList, item); + } + } else { + n = new AccountItem(*this, accountList); + } + slotListDoubleClicked(n, QPoint(), 0); +} + +void AccountsPanel::slotSubBtn() { + //kdDebug()<<k_funcinfo<<endl; + QListViewItem *item = accountList->selectedItem(); + if (item && item->text(0).isEmpty()) { + return; + } + QListViewItem *n; + if (item) { + n = new AccountItem(*this, item); + } else { + n = new AccountItem(*this, accountList); + } + slotListDoubleClicked(n, QPoint(), 0); +} + +KCommand *AccountsPanel::buildCommand(Part *part) { + KMacroCommand *cmd = 0; + // First remove + QPtrListIterator<QListViewItem> rit = m_removedItems; + for (;rit.current(); ++rit) { + AccountItem *item = static_cast<AccountItem*>(rit.current()); + //kdDebug()<<k_funcinfo<<"Removed item"<<endl; + if (!cmd) cmd = new KMacroCommand(i18n("Modify Accounts")); + cmd->addCommand(new RemoveAccountCmd(part, part->getProject(), item->account)); + } + m_removedItems.setAutoDelete(true); + // Then add/modify + KCommand *c = save(part, part->getProject()); + if (c) { + if (!cmd) cmd = new KMacroCommand(i18n("Modify Accounts")); + cmd->addCommand(c); + } + return cmd; +} + +KCommand *AccountsPanel::save(Part *part, Project &project) { + KMacroCommand *cmd=0; + QListViewItem *myChild = accountList->firstChild(); + for (; myChild; myChild = myChild->nextSibling()) { + KCommand *c = save(part, project, myChild); + if (c) { + if (!cmd) cmd = new KMacroCommand(""); + cmd->addCommand(c); + } + } + return cmd; +} + +KCommand *AccountsPanel::save(Part *part, Project &project, QListViewItem *i) { + KMacroCommand *cmd=0; + AccountItem *item = static_cast<AccountItem*>(i); + if (item->account == 0) { + if (!item->text(0).isEmpty()) { + //kdDebug()<<k_funcinfo<<"New account: "<<item->text(0)<<endl; + if (!cmd) cmd = new KMacroCommand(""); + item->account = new Account(item->text(0), item->text(1)); + if (item->parent()) { + //kdDebug()<<k_funcinfo<<"New account: "<<item->text(0)<<endl; + cmd->addCommand(new AddAccountCmd(part, project, item->account, item->parent()->text(0))); + } else { + cmd->addCommand(new AddAccountCmd(part, project, item->account)); + } + } + } else { + if (!item->text(0).isEmpty() && (item->text(0) != item->account->name())) { + if (!cmd) cmd = new KMacroCommand(""); + //kdDebug()<<k_funcinfo<<"Renamed: "<<item->account->name()<<" to "<<item->text(0)<<endl; + cmd->addCommand(new RenameAccountCmd(part, item->account, item->text(0))); + } + if (item->text(1) != item->account->description()) { + if (!cmd) cmd = new KMacroCommand(""); + //kdDebug()<<k_funcinfo<<"New description: "<<item->account->description()<<" to "<<item->text(1)<<endl; + cmd->addCommand(new ModifyAccountDescriptionCmd(part, item->account, item->text(1))); + } + } + QListViewItem *myChild = item->firstChild(); + for (; myChild; myChild = myChild->nextSibling()) { + KCommand *c = save(part, project, myChild); + if (c) { + if (!cmd) cmd = new KMacroCommand(""); + cmd->addCommand(c); + } + } + AccountItem *ai = static_cast<AccountItem*>(m_elements[accountsComboBox->currentText()]); + Account *newDefaultAccount = 0; + if (ai) { + newDefaultAccount = ai->account; + } + if (m_oldDefaultAccount != newDefaultAccount) { + if (!cmd) cmd = new KMacroCommand(""); + cmd->addCommand(new ModifyDefaultAccountCmd(part, m_accounts, m_oldDefaultAccount, newDefaultAccount)); + } + return cmd; +} + +void AccountsPanel::slotListDoubleClicked(QListViewItem* item, const QPoint&, int col) { + //kdDebug()<<k_funcinfo<<(item?item->text(0):"")<<endl; + if (m_renameItem) + return; + slotStartRename(item, col); +} + +void AccountsPanel::slotRenameStarted(QListViewItem */*item*/, int /*col*/) { + //kdDebug()<<k_funcinfo<<(item?item->text(0):"")<<endl; + if (accountList->isRenaming()) { + removeBtn->setEnabled(false); + newBtn->setEnabled(false); + subBtn->setEnabled(false); + } +} + +void AccountsPanel::slotStartRename(QListViewItem *item, int col) { + //kdDebug()<<k_funcinfo<<(item?item->text(0):"")<<endl; + static_cast<AccountItem*>(item)->oldText = item->text(col); + item->setRenameEnabled(col, true); + item->startRename(col); + m_renameItem = item; + + emit renameStarted(item, col); +} + +void AccountsPanel::slotRemoveItem(QListViewItem *i) { + AccountItem *item = static_cast<AccountItem*>(i); + if (item == 0) + return; + //kdDebug()<<k_funcinfo<<item->text(0)<<endl; + removeElement(item); + QListViewItem *p = item->parent(); + if (p) { + p->takeItem(item); + if (item->account) { + m_removedItems.append(item); + } else { + delete item; + } + if (p->childCount() == 0) { + addElement(p); + } + } else { + accountList->takeItem(item); + if (item->account) { + m_removedItems.append(item); + } else { + delete item; + } + } +} + +// We don't get notified when rename is cancelled, this is called from the item +void AccountsPanel::renameStopped(QListViewItem */*item*/) { + //kdDebug()<<k_funcinfo<<endl; + m_renameItem = 0; + emit selectionChanged(); +} + +void AccountsPanel::slotOk() { + +} + +} //namespace KPlato + +#include "kptaccountspanel.moc" diff --git a/kplato/kptaccountspanel.h b/kplato/kptaccountspanel.h new file mode 100644 index 00000000..d067d6d3 --- /dev/null +++ b/kplato/kptaccountspanel.h @@ -0,0 +1,99 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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 KPTACCOUNTSPANEL_H +#define KPTACCOUNTSPANEL_H + +#include "kptaccountspanelbase.h" + +#include <qptrlist.h> +#include <qdict.h> + +class QListView; +class QListViewItem; +class QWidget; + +class KCommand; +class KMacroCommand; + +namespace KPlato +{ + +class AccountItem; +class Account; +class Accounts; +class Part; +class Project; + +class AccountsPanel : public AccountsPanelBase { + Q_OBJECT +public: + AccountsPanel(Accounts &acc, QWidget *parent=0, const char *name=0); + + KCommand *buildCommand(Part *part); + + bool isUnique(QListViewItem *item); + void renameStopped(QListViewItem *item); + +signals: + void changed(bool); + + // Internal + void renameStarted(QListViewItem *, int); + void startRename(QListViewItem *item, int col); + void selectionChanged(); + +public slots: + void slotOk(); + +protected slots: + void slotChanged(); + void slotSelectionChanged(); + void slotItemRenamed(QListViewItem *item, int col); + void slotRemoveBtn(); + void slotNewBtn(); + void slotSubBtn(); + void slotActivated(int); + void slotListDoubleClicked(QListViewItem* item, const QPoint&, int col); + void slotRenameStarted(QListViewItem *item, int col); + void slotStartRename(QListViewItem *item, int col); + void slotRemoveItem(QListViewItem *i); +protected: + void addItems(QListView *lv, Accounts &acc); + void addItems(QListViewItem *item, Account *acc); + void addElement(const QListViewItem *item); + void removeElement(QListViewItem *item); + void refreshDefaultAccount(); + KCommand *save(Part *part, Project &project); + KCommand *save(Part *part, Project &project, QListViewItem *item); + +private: + Accounts &m_accounts; + + QPtrList<QListViewItem> m_removedItems; + Account *m_oldDefaultAccount; + QDict<QListViewItem> m_elements; + int m_currentIndex; + QString m_renameText; + QListViewItem *m_renameItem; +}; + +} //namespace KPlato + +#endif diff --git a/kplato/kptaccountspanelbase.ui b/kplato/kptaccountspanelbase.ui new file mode 100644 index 00000000..dde73216 --- /dev/null +++ b/kplato/kptaccountspanelbase.ui @@ -0,0 +1,130 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>KPlato::AccountsPanelBase</class> +<author>Dag Andersen <danders@get2net.dk></author> +<widget class="QWidget"> + <property name="name"> + <cstring>AccountsPanelBase</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>350</width> + <height>234</height> + </rect> + </property> + <property name="minimumSize"> + <size> + <width>350</width> + <height>220</height> + </size> + </property> + <property name="baseSize"> + <size> + <width>300</width> + <height>0</height> + </size> + </property> + <property name="caption"> + <string>AccountsPanelBase</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout1</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>Default account:</string> + </property> + </widget> + <widget class="QComboBox"> + <property name="name"> + <cstring>accountsComboBox</cstring> + </property> + </widget> + </hbox> + </widget> + <widget class="KListView"> + <column> + <property name="text"> + <string>Account</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <column> + <property name="text"> + <string>Description</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <property name="name"> + <cstring>accountList</cstring> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout7</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QPushButton"> + <property name="name"> + <cstring>newBtn</cstring> + </property> + <property name="text"> + <string>&New</string> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>subBtn</cstring> + </property> + <property name="text"> + <string>New &Sub-Account</string> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>removeBtn</cstring> + </property> + <property name="text"> + <string>&Remove</string> + </property> + </widget> + </hbox> + </widget> + </vbox> +</widget> +<customwidgets> +</customwidgets> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>klistview.h</includehint> +</includehints> +</UI> diff --git a/kplato/kptaccountsview.cc b/kplato/kptaccountsview.cc new file mode 100644 index 00000000..3fc5d8dd --- /dev/null +++ b/kplato/kptaccountsview.cc @@ -0,0 +1,451 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Dag Andersen kplato@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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. +*/ + +#include "kptaccountsview.h" + +#include "kptaccountsviewconfigdialog.h" +#include "kptcontext.h" +#include "kptdatetime.h" +#include "kptproject.h" +#include "kptview.h" +#include "kpteffortcostmap.h" + +#include <qapplication.h> +#include <qcombobox.h> +#include <qdatetime.h> +#include <qdatetimeedit.h> +#include <qheader.h> +#include <qlabel.h> +#include <qlayout.h> +#include <qpainter.h> +#include <qpalette.h> +#include <qpushbutton.h> +#include <qvaluelist.h> +#include <qpopupmenu.h> +#include <qsizepolicy.h> +#include <qhbox.h> +#include <qpaintdevicemetrics.h> + +#include <kcalendarsystem.h> +#include <kglobal.h> +#include <klistview.h> +#include <klocale.h> +#include <kprinter.h> + +#include <kdebug.h> + +namespace KPlato +{ + +class Label : public QLabel +{ +public: + Label(QWidget *w) + : QLabel(w) + {} + void paintContents(QPainter *p) { + drawContents(p); + } +}; + +AccountsView::AccountItem::AccountItem(Account *a, QListView *parent, bool highlight) + : DoubleListViewBase::MasterListItem(parent, a->name(), highlight), + account(a) { + if (parent->columns() >= 3) { + setText(2, a->description()); + } + //kdDebug()<<k_funcinfo<<endl; +} +AccountsView::AccountItem::AccountItem(Account *a, QListViewItem *p, bool highlight) + : DoubleListViewBase::MasterListItem(p, a->name(), highlight), + account(a) { + if (listView() && listView()->columns() >= 3) { + setText(2, a->description()); + } + //kdDebug()<<k_funcinfo<<endl; +} + +AccountsView::AccountItem::AccountItem(QString text, Account *a, QListViewItem *parent, bool highlight) + : DoubleListViewBase::MasterListItem(parent, text, highlight), + account(a) { + //kdDebug()<<k_funcinfo<<endl; +} + +void AccountsView::AccountItem::add(int col, const QDate &date, const EffortCost &ec) { + EffortCost &cm = costMap.add(date, ec); + if (m_slaveItem) + m_slaveItem->setText(col, KGlobal::locale()->formatMoney(cm.cost(), "", 0)); +} + +AccountsView::AccountsView(Project &project, View *view, QWidget *parent) + : QWidget(parent, "Accounts view"), + m_mainview(view), + m_project(project), + m_accounts(project.accounts()) { + + m_date = QDate::currentDate(); + m_period = 0; + m_periodTexts<<i18n("Day")<<i18n("Week")<<i18n("Month"); + m_cumulative = false; + + QVBoxLayout *lay1 = new QVBoxLayout(this, 0, KDialog::spacingHint()); + + QHBoxLayout *lay2 = new QHBoxLayout(0, 0, KDialog::spacingHint()); + m_label = new Label(this); + m_label->setFrameShape(QLabel::StyledPanel); + m_label->setFrameShadow(QLabel::Sunken); + m_label->setAlignment(int(QLabel::WordBreak | QLabel::AlignVCenter)); + lay2->addWidget(m_label); + m_changeBtn = new QPushButton(i18n("Configure..."), this); + m_changeBtn->setSizePolicy(QSizePolicy((QSizePolicy::SizeType)0, (QSizePolicy::SizeType)0, 0, 0, m_changeBtn->sizePolicy().hasHeightForWidth())); + lay2->addWidget(m_changeBtn); + lay1->addLayout(lay2); + + m_dlv = new DoubleListViewBase(this, true); + m_dlv->setNameHeader(i18n("Account")); + + init(); + + lay1->addWidget(m_dlv); + + connect(this, SIGNAL(update()), SLOT(slotUpdate())); + connect(m_changeBtn, SIGNAL(clicked()), SLOT(slotConfigure())); + + QValueList<int> list = m_dlv->sizes(); + int tot = list[0] + list[1]; + list[0] = QMIN(35, tot); + list[1] = tot-list[0]; + m_dlv->setSizes(list); +} + +void AccountsView::zoom(double zoom) { + Q_UNUSED(zoom); +} + +void AccountsView::init() { + m_date = QDate::currentDate(); + m_period = 0; + initAccList(m_accounts.accountList()); +} + +void AccountsView::draw() { + //kdDebug()<<k_funcinfo<<endl; + Context::Accountsview context; + getContextClosedItems(context, m_dlv->masterListView()->firstChild()); + initAccList(m_accounts.accountList()); + setContextClosedItems(context); + slotUpdate(); +} + +void AccountsView::initAccList(const AccountList &list) { + m_dlv->clearLists(); + AccountListIterator it = list; + for (it.toLast(); it.current(); --it) { + AccountsView::AccountItem *a = new AccountsView::AccountItem(it.current(), m_dlv->masterListView()); + a->setOpen(true); + a->setExpandable(!it.current()->isElement()); + initAccSubItems(it.current(), a); + } + createPeriods(); +} + +void AccountsView::initAccSubItems(Account *acc, AccountsView::AccountItem *parent) { + if (!acc->accountList().isEmpty()) { +/* AccountsView::AccountItem *a = new AccountsView::AccountItem(i18n("Subaccounts"), acc, parent); + DoubleListViewBase::SlaveListItem *i = new DoubleListViewBase::SlaveListItem(a, parent->period); + a->period = i;*/ + + initAccList(acc->accountList(), parent); + } +// AccountsView::AccountItem *a = new AccountsView::AccountItem(i18n("Variance"), acc, parent, true); +// DoubleListViewBase::SlaveListItem *i = new DoubleListViewBase::SlaveListItem(a, parent->period, true); +// a->period = i; +// +// a = new AccountsView::AccountItem(i18n("Actual"), acc, parent); +// i = new DoubleListViewBase::SlaveListItem(a, parent->period); +// a->period = i; +// +// a = new AccountsView::AccountItem(i18n("Planned"), acc, parent); +// i = new DoubleListViewBase::SlaveListItem(a, parent->period); +// a->period = i; + +} + +void AccountsView::initAccList(const AccountList &list, AccountsView::AccountItem *parent) { + AccountListIterator it = list; + for (it.toLast(); it.current(); --it) { + AccountsView::AccountItem *a = new AccountsView::AccountItem(it.current(), parent); + a->setOpen(true); + a->setExpandable(!it.current()->isElement()); + initAccSubItems(it.current(), a); + } +} + +void AccountsView::clearPeriods() { + m_dlv->clearSlaveList(); +} + +void AccountsView::createPeriods() { + m_dlv->createSlaveItems(); +} + +void AccountsView::slotUpdate() { + //kdDebug()<<k_funcinfo<<endl; + QApplication::setOverrideCursor(Qt::waitCursor); + createPeriods(); + KLocale *locale = KGlobal::locale(); + const KCalendarSystem *cal = locale->calendar(); + + QString t; + if (m_cumulative) { + t += " <b>" + i18n("Cumulative") + "</b> "; + } + t += i18n("Cut-off date:%1").arg("<b>" + locale->formatDate(m_date, true) + "</b>"); + t += " " + i18n("Periodicity:%1").arg("<b>" + periodText(m_period) + "</b>"); + m_label->setText(t); + + // Add columns for selected period/periods + QDate start = m_project.startTime().date(); + QDate end = m_date; + //kdDebug()<<k_funcinfo<<start.toString()<<" - "<<end.toString()<<endl; + int c=0; + if (m_period == 0) { //Daily + for (QDate dt = start; dt <= end; dt = cal->addDays(dt, 1), ++c) { + QString df = locale->formatDate(dt, true); + m_dlv->addSlaveColumn(df); + } + QListViewItemIterator it(m_dlv->masterListView()); + for (;it.current(); ++it) { + AccountsView::AccountItem *item = dynamic_cast<AccountsView::AccountItem*>(it.current()); + if (!item || !item->account || !item->account->isElement()) { + continue; + } + item->costMap = m_accounts.plannedCost(*(item->account), start, end); + double cost = 0.0; + int col=0; + for (QDate d=start; d <= end; d = cal->addDays(d, 1), ++col) { + EffortCost &ec = item->costMap.effortCostOnDate(d); + cost = (m_cumulative ? cost + ec.cost() : ec.cost()); + item->setSlaveItem(col, cost); + m_cumulative ? item->setTotal(cost) : item->addToTotal(cost); + } + } + m_dlv->calculate(); + QApplication::restoreOverrideCursor(); + return; + } + if (m_period == 1) { //Weekly + //TODO make this user controlled + int weekStartDay = locale->weekStartDay(); + + QDate dt = start; + QDate pend = cal->addDays(dt, 7 + weekStartDay - 1 - cal->dayOfWeek(dt)); + for (; pend <= end; ++c) { + //kdDebug()<<k_funcinfo<<c<<": "<<dt<<"-"<<pend<<" : "<<end<<endl; + int y; + int w = cal->weekNumber(dt, &y); + QString t = i18n("<week>-<year>", "%1-%2").arg(w).arg(y); + m_dlv->addSlaveColumn(t); + dt = pend.addDays(1); + pend = cal->addDays(pend, 7); + if ((pend.year() == end.year()) && (pend.weekNumber() == end.weekNumber())) { + pend = end; + } + } + if (c == 0) { + QApplication::restoreOverrideCursor(); + return; + } + QListViewItemIterator it(m_dlv->masterListView()); + for (;it.current(); ++it) { + AccountsView::AccountItem *item = dynamic_cast<AccountsView::AccountItem*>(it.current()); + if (!item || !item->account || !item->account->isElement()) { + continue; + } + item->costMap = m_accounts.plannedCost(*(item->account), start, end); + double cost = 0.0; + QDate d = start; + QDate pend = cal->addDays(d, 7 + weekStartDay - 1 - cal->dayOfWeek(d)); + for (int col=0; pend <= end; ++col) { + double cst = item->costMap.cost(d, d.daysTo(pend)+1); + cost = (m_cumulative ? cost + cst : cst); + item->setSlaveItem(col, cost); + m_cumulative ? item->setTotal(cost) : item->addToTotal(cost); + d = pend.addDays(1); // 1. next week + pend = cal->addDays(pend, 7); + if ((pend.year() == end.year()) && (pend.weekNumber() == end.weekNumber())) { + pend = end; + } + } + } + m_dlv->calculate(); + QApplication::restoreOverrideCursor(); + return; + } + if (m_period == 2) { //Monthly + //TODO make this user controlled + QDate dt = start; + QDate pend; + cal->setYMD(pend, dt.year(), dt.month(), dt.daysInMonth()); + for (; pend <= end; ++c) { + //kdDebug()<<k_funcinfo<<c<<": "<<dt<<"-"<<pend<<" : "<<end<<endl; + QString m = cal->monthName(dt, true) + QString(" %1").arg( dt.year()); + m_dlv->addSlaveColumn(m); + + dt = pend.addDays(1); // 1. next month + pend = cal->addDays(pend, dt.daysInMonth()); + if ((pend.year() == end.year()) && (pend.month() == end.month())) { + pend = end; + } + } + if (c == 0) { + QApplication::restoreOverrideCursor(); + return; + } + QListViewItemIterator it(m_dlv->masterListView()); + for (;it.current(); ++it) { + AccountsView::AccountItem *item = dynamic_cast<AccountsView::AccountItem*>(it.current()); + if (!item || !item->account || !item->account->isElement()) { + continue; + } + item->costMap = m_accounts.plannedCost(*(item->account), start, end); + double cost = 0.0; + QDate d = start; + cal->setYMD(pend, d.year(), d.month(), d.daysInMonth()); + for (int col=0; pend <= end; ++col) { + double cst = item->costMap.cost(d, d.daysTo(pend)+1); + cost = (m_cumulative ? cost + cst : cst); + item->setSlaveItem(col, cost); + m_cumulative ? item->setTotal(cost) : item->addToTotal(cost); + d = pend.addDays(1); // 1. next month + pend = cal->addDays(pend, d.daysInMonth()); + if ((pend.year() == end.year()) && (pend.month() == end.month())) { + pend = end; + } + } + } + m_dlv->calculate(); + QApplication::restoreOverrideCursor(); + return; + } + QApplication::restoreOverrideCursor(); +} + +void AccountsView::print(KPrinter &printer) { + //kdDebug()<<k_funcinfo<<endl; + QPaintDeviceMetrics m = QPaintDeviceMetrics ( &printer ); + uint top, left, bottom, right; + printer.margins(&top, &left, &bottom, &right); + //kdDebug()<<m.width()<<"x"<<m.height()<<" : "<<top<<", "<<left<<", "<<bottom<<", "<<right<<" : "<<size()<<endl; + QPainter p; + p.begin(&printer); + p.setViewport(left, top, m.width()-left-right, m.height()-top-bottom); + p.setClipRect(left, top, m.width()-left-right, m.height()-top-bottom); + QRect preg = p.clipRegion(QPainter::CoordPainter).boundingRect(); + //kdDebug()<<"p="<<preg<<endl; + //p.drawRect(preg.x(), preg.y(), preg.width()-1, preg.height()-1); + double scale = QMIN((double)preg.width()/(double)size().width(), (double)preg.height()/(double)(size().height())); + //kdDebug()<<"scale="<<scale<<endl; + if (scale < 1.0) { + p.scale(scale, scale); + } + m_label->paintContents(&p); + p.translate(0, m_label->size().height()); + m_dlv->paintContents(&p); + p.end(); +} + +bool AccountsView::setContext(Context::Accountsview &context) { + //kdDebug()<<k_funcinfo<<"---->"<<endl; + QValueList<int> list; + list << context.accountsviewsize << context.periodviewsize; + //m_dlv->setSizes(list); //NOTE: This doesn't always work! + m_date = context.date; + if (!m_date.isValid()) + m_date = QDate::currentDate(); + m_period = context.period; + m_cumulative = context.cumulative; + setContextClosedItems(context); + //kdDebug()<<k_funcinfo<<"<----"<<endl; + return true; +} + +void AccountsView::setContextClosedItems(Context::Accountsview &context) { + for (QStringList::ConstIterator it = context.closedItems.begin(); it != context.closedItems.end(); ++it) { + if (m_accounts.findAccount(*it)) { + QListViewItemIterator lit(m_dlv->masterListView()); + for (; lit.current(); ++lit) { + if (lit.current()->text(0) == (*it)) { + m_dlv->setOpen(lit.current(), false); + break; + } + } + } + } +} + +void AccountsView::getContext(Context::Accountsview &context) const { + //kdDebug()<<k_funcinfo<<endl; + context.accountsviewsize = m_dlv->sizes()[0]; + context.periodviewsize = m_dlv->sizes()[1]; + context.date = m_date; + context.period = m_period; + context.cumulative = m_cumulative; + //kdDebug()<<k_funcinfo<<"sizes="<<sizes()[0]<<","<<sizes()[1]<<endl; + + getContextClosedItems(context, m_dlv->masterListView()->firstChild()); +} + + +void AccountsView::getContextClosedItems(Context::Accountsview &context, QListViewItem *item) const { + if (item == 0) + return; + for (QListViewItem *i = item; i; i = i->nextSibling()) { + if (!i->isOpen()) { + context.closedItems.append(i->text(0)); + //kdDebug()<<k_funcinfo<<"add closed "<<i->text(0)<<endl; + } + getContextClosedItems(context, i->firstChild()); + } +} + +void AccountsView::slotConfigure() { + //kdDebug()<<k_funcinfo<<endl; + AccountsviewConfigDialog *dia = new AccountsviewConfigDialog(m_date, m_period, m_periodTexts, m_cumulative, this); + if (dia->exec()) { + m_date = dia->date(); + m_period = dia->period(); + m_cumulative = dia->isCumulative(); + emit update(); + } + delete dia; +} + +QString AccountsView::periodText(int offset) { + QString s; + QStringList::const_iterator it = m_periodTexts.at(offset); + if (it != m_periodTexts.constEnd()) { + s = (*it); + } + return s; +} + +} //KPlato namespace + +#include "kptaccountsview.moc" diff --git a/kplato/kptaccountsview.h b/kplato/kptaccountsview.h new file mode 100644 index 00000000..cc73514a --- /dev/null +++ b/kplato/kptaccountsview.h @@ -0,0 +1,131 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Dag Andersen <kplato@kde.org> + + 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 KPTACCOUNTSVIEW_H +#define KPTACCOUNTSVIEW_H + +#include <qdatetime.h> + +#include <klistview.h> + +#include "kptaccount.h" +#include "kptcontext.h" +#include "kpteffortcostmap.h" +#include "kptdoublelistviewbase.h" + +class QComboBox; +class QDateEdit; +class QPushButton; +class QSplitter; +class QListViewItem; +class QLabel; +class QPushButton; + +class KListView; +class KListViewItem; +class KPrinter; + +namespace KPlato +{ + +class Label; +class Account; +class View; +class Project; +class Resource; +class Node; + +class ResourceGroup; +class Resource; +class ResourceItemPrivate; + +class AccountsView : public QWidget +{ + Q_OBJECT +public: + + AccountsView(Project &project, View *view, QWidget *parent); + + //~AccountsView(); + + void zoom(double zoom); + + View *mainView() { return m_mainview; } + void draw(); + void print(KPrinter &printer); + + virtual bool setContext(Context::Accountsview &context); + virtual void getContext(Context::Accountsview &context) const; + +signals: + void update(); + +public slots: + void slotConfigure(); + +protected slots: + void slotUpdate(); + +protected: + void getContextClosedItems(Context::Accountsview &context, QListViewItem *item) const; + void setContextClosedItems(Context::Accountsview &context); + +private: + class AccountItem : public DoubleListViewBase::MasterListItem { + public: + AccountItem(Account *a, QListView *parent, bool highlight=false); + AccountItem(Account *a, QListViewItem *parent, bool highlight=false); + AccountItem(QString text, Account *a, QListViewItem *parent, bool _highlight=false); + + void add(int col, const QDate &date, const EffortCost &ec); + + Account *account; + EffortCostMap costMap; + }; + + void init(); + void initAccList(const AccountList &list); + void initAccSubItems(Account *acc, AccountItem *parent); + void initAccList(const AccountList &list, AccountItem *parent); + void createPeriods(); + void clearPeriods(); + QString periodText(int offset); + +private: + View *m_mainview; + Project &m_project; + Accounts &m_accounts; + + int m_defaultFontSize; + + QDate m_date; + int m_period; + bool m_cumulative; + + DoubleListViewBase *m_dlv; + + QStringList m_periodTexts; + QPushButton *m_changeBtn; + Label *m_label; + +}; + +} //KPlato namespace + +#endif diff --git a/kplato/kptaccountsviewconfigdialog.cc b/kplato/kptaccountsviewconfigdialog.cc new file mode 100644 index 00000000..bf5e1b65 --- /dev/null +++ b/kplato/kptaccountsviewconfigdialog.cc @@ -0,0 +1,85 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Dag Andersen <danders@get2net.dk> + + 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. +*/ + +#include "kptaccountsviewconfigdialog.h" + +#include <qcheckbox.h> +#include <qcombobox.h> +#include <qdatetimeedit.h> +#include <qdatetime.h> +#include <qstring.h> + +#include <kdatewidget.h> +#include <klocale.h> + +#include <kdebug.h> + +namespace KPlato +{ + +AccountsviewConfigDialog::AccountsviewConfigDialog(const QDate &date, int period, const QStringList &periodTexts, bool cumulative, QWidget *p) + : KDialogBase(Swallow, i18n("Settings"), Ok|Cancel, Ok, p, "Accountsview Settings Dialog", true, true) +{ + m_panel = new AccountsviewConfigPanel(this); + m_panel->dateEdit->setDate(date); + m_panel->periodBox->insertStringList(periodTexts); + m_panel->periodBox->setCurrentItem(period); + m_panel->cumulative->setChecked(cumulative); + setMainWidget(m_panel); + + enableButtonOK(false); + + connect(m_panel, SIGNAL(changed(bool)), SLOT( enableButtonOK(bool))); +} + + +QDate AccountsviewConfigDialog::date() { + return m_panel->dateEdit->date(); +} + +int AccountsviewConfigDialog::period() { + return m_panel->periodBox->currentItem(); +} + +QString AccountsviewConfigDialog::periodText() { + return m_panel->periodBox->currentText(); +} + +bool AccountsviewConfigDialog::isCumulative() { + return m_panel->cumulative->isChecked(); +} + + +//---------------------------- +AccountsviewConfigPanel::AccountsviewConfigPanel(QWidget *parent) + : AccountsviewConfigurePanelBase(parent) { + + connect(dateEdit, SIGNAL(changed(QDate)), SLOT(slotChanged())); + connect(periodBox, SIGNAL(activated(int)), SLOT(slotChanged())); + connect(cumulative, SIGNAL(clicked()), SLOT(slotChanged())); +} + +void AccountsviewConfigPanel::slotChanged() { + emit changed(true); +} + + +} //KPlato namespace + +#include "kptaccountsviewconfigdialog.moc" diff --git a/kplato/kptaccountsviewconfigdialog.h b/kplato/kptaccountsviewconfigdialog.h new file mode 100644 index 00000000..696dbb87 --- /dev/null +++ b/kplato/kptaccountsviewconfigdialog.h @@ -0,0 +1,64 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Dag Andersen <danders@get2net.dk> + + 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 KPTACCOUNTSVIEWCONFIGDIALOG_H +#define KPTACCOUNTSVIEWCONFIGDIALOG_H + + +#include <kdialogbase.h> +#include "kptaccountsviewconfigurepanelbase.h" + +class QDate; +class QString; +class QWidget; + +namespace KPlato +{ + +class AccountsviewConfigPanel; + +class AccountsviewConfigDialog : public KDialogBase { + Q_OBJECT +public: + AccountsviewConfigDialog(const QDate &date, int period, const QStringList &periodTexts, bool cumulative, QWidget *parent); + + QDate date(); + int period(); + QString periodText(); + bool isCumulative(); + +private: + AccountsviewConfigPanel *m_panel; +}; + +class AccountsviewConfigPanel : public AccountsviewConfigurePanelBase { + Q_OBJECT +public: + AccountsviewConfigPanel(QWidget *parent); + +public slots: + void slotChanged(); + +signals: + void changed(bool); +}; + +} //KPlato namespace + +#endif // KPTACCOUNTSVIEWCONFIGDIALOG_H diff --git a/kplato/kptaccountsviewconfigurepanelbase.ui b/kplato/kptaccountsviewconfigurepanelbase.ui new file mode 100644 index 00000000..568adfd7 --- /dev/null +++ b/kplato/kptaccountsviewconfigurepanelbase.ui @@ -0,0 +1,68 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>KPlato::AccountsviewConfigurePanelBase</class> +<author>Dag Andersen <danders@get2net.dk></author> +<widget class="QWidget"> + <property name="name"> + <cstring>AccountsviewConfigurePanelBase</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>337</width> + <height>81</height> + </rect> + </property> + <property name="caption"> + <string>AccountsviewConfigurePanelBase</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>Cut-off date:</string> + </property> + </widget> + <widget class="QComboBox" row="1" column="1"> + <property name="name"> + <cstring>periodBox</cstring> + </property> + </widget> + <widget class="KDateWidget" row="0" column="1"> + <property name="name"> + <cstring>dateEdit</cstring> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel1_2</cstring> + </property> + <property name="text"> + <string>Periodicity:</string> + </property> + </widget> + <widget class="QCheckBox" row="2" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>cumulative</cstring> + </property> + <property name="text"> + <string>Cumulative</string> + </property> + </widget> + </grid> +</widget> +<customwidgets> +</customwidgets> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>kdatewidget.h</includehint> +</includehints> +</UI> diff --git a/kplato/kptappointment.cc b/kplato/kptappointment.cc new file mode 100644 index 00000000..897ab390 --- /dev/null +++ b/kplato/kptappointment.cc @@ -0,0 +1,734 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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. +*/ + +#include "kptappointment.h" + +#include "kptproject.h" +#include "kpttask.h" +#include "kptdatetime.h" +#include "kptcalendar.h" +#include "kpteffortcostmap.h" +#include "kptschedule.h" + +#include <kdebug.h> + +namespace KPlato +{ + +class Resource; + +AppointmentInterval::AppointmentInterval() { + m_load = 100.0; +} +AppointmentInterval::AppointmentInterval(const AppointmentInterval &interval) { + //kdDebug()<<k_funcinfo<<endl; + m_start = interval.startTime(); + m_end = interval.endTime(); + m_load = interval.load(); +} +AppointmentInterval::AppointmentInterval(const DateTime &start, const DateTime end, double load) { + //kdDebug()<<k_funcinfo<<endl; + m_start = start; + m_end = end; + m_load = load; +} +AppointmentInterval::~AppointmentInterval() { + //kdDebug()<<k_funcinfo<<endl; +} + +Duration AppointmentInterval::effort(const DateTime &start, const DateTime end) const { + if (start >= m_end || end <= m_start) { + return Duration::zeroDuration; + } + DateTime s = (start > m_start ? start : m_start); + DateTime e = (end < m_end ? end : m_end); + return (e - s) * m_load / 100; +} + +Duration AppointmentInterval::effort(const DateTime &time, bool upto) const { + if (upto) { + if (time <= m_start) { + return Duration::zeroDuration; + } + DateTime e = (time < m_end ? time : m_end); + return (e - m_start) * m_load / 100; + } + // from time till end + if (time >= m_end) { + return Duration::zeroDuration; + } + DateTime s = (time > m_start ? time : m_start); + return (m_end - s) * m_load / 100; +} + +bool AppointmentInterval::loadXML(QDomElement &element) { + //kdDebug()<<k_funcinfo<<endl; + bool ok; + QString s = element.attribute("start"); + if (s != "") + m_start = DateTime::fromString(s); + s = element.attribute("end"); + if (s != "") + m_end = DateTime::fromString(s); + m_load = element.attribute("load", "100").toDouble(&ok); + if (!ok) m_load = 100; + return m_start.isValid() && m_end.isValid(); +} + +void AppointmentInterval::saveXML(QDomElement &element) const { + QDomElement me = element.ownerDocument().createElement("interval"); + element.appendChild(me); + + me.setAttribute("start", m_start.toString(Qt::ISODate)); + me.setAttribute("end", m_end.toString(Qt::ISODate)); + me.setAttribute("load", m_load); +} + +bool AppointmentInterval::isValid() const { + return m_start.isValid() && m_end.isValid(); +} + +AppointmentInterval AppointmentInterval::firstInterval(const AppointmentInterval &interval, const DateTime &from) const { + //kdDebug()<<k_funcinfo<<interval.startTime().toString()<<" - "<<interval.endTime().toString()<<" from="<<from.toString()<<endl; + DateTime f = from; + DateTime s1 = m_start; + DateTime e1 = m_end; + DateTime s2 = interval.startTime(); + DateTime e2 = interval.endTime(); + AppointmentInterval a; + if (f.isValid() && f >= e1 && f >= e2) { + return a; + } + if (f.isValid()) { + if (s1 < f && f < e1) { + s1 = f; + } + if (s2 < f && f < e2) { + s2 = f; + } + } else { + f = s1 < s2 ? s1 : s2; + } + if (s1 < s2) { + a.setStartTime(s1); + if (e1 <= s2) { + a.setEndTime(e1); + } else { + a.setEndTime(s2); + } + a.setLoad(m_load); + } else if (s1 > s2) { + a.setStartTime(s2); + if (e2 <= s1) { + a.setEndTime(e2); + } else { + a.setEndTime(s1); + } + a.setLoad(interval.load()); + } else { + a.setStartTime(s1); + if (e1 <= e2) + a.setEndTime(e1); + else + a.setEndTime(e2); + a.setLoad(m_load + interval.load()); + } + //kdDebug()<<k_funcinfo<<a.startTime().toString()<<" - "<<a.endTime().toString()<<" load="<<a.load()<<endl; + return a; +} + +////// + +Appointment::UsedEffortItem::UsedEffortItem(QDate date, Duration effort, bool overtime) { + m_date = date; + m_effort = effort; + m_overtime = overtime; +} +QDate Appointment::UsedEffortItem::date() { + return m_date; +} +Duration Appointment::UsedEffortItem::effort() { + return m_effort; +} +bool Appointment::UsedEffortItem::isOvertime() { + return m_overtime; +} + +Appointment::UsedEffort::UsedEffort() { + setAutoDelete(true); +} + +void Appointment::UsedEffort::inSort(QDate date, Duration effort, bool overtime) { + UsedEffortItem *item = new UsedEffortItem(date, effort, overtime); + QPtrList<UsedEffortItem>::inSort(item); +} + +Duration Appointment::UsedEffort::usedEffort(bool includeOvertime) const { + Duration eff; + QPtrListIterator<UsedEffortItem> it(*this); + for (; it.current(); ++it) { + if (includeOvertime || !it.current()->isOvertime()) { + eff += it.current()->effort(); + } + } + return eff; +} + +Duration Appointment::UsedEffort::usedEffort(const QDate &date, bool includeOvertime) const { + Duration eff; + QPtrListIterator<UsedEffortItem> it(*this); + for (; it.current(); ++it) { + if ((includeOvertime || !it.current()->isOvertime()) && + it.current()->date() == date) { + eff += it.current()->effort(); + } + } + return eff; +} + +Duration Appointment::UsedEffort::usedEffortTo(const QDate &date, bool includeOvertime) const { + Duration eff; + QPtrListIterator<UsedEffortItem> it(*this); + for (; it.current(); ++it) { + if ((includeOvertime || !it.current()->isOvertime()) && + it.current()->date() <= date) { + eff += it.current()->effort(); + } + } + return eff; +} + +Duration Appointment::UsedEffort::usedOvertime() const { + UsedEffortItem *item = getFirst(); + return item==0 ? Duration::zeroDuration : usedOvertime(item->date()); +} + +Duration Appointment::UsedEffort::usedOvertime(const QDate &date) const { + Duration eff; + QPtrListIterator<UsedEffortItem> it(*this); + for (; it.current(); ++it) { + if (it.current()->isOvertime() && it.current()->date() == date) { + eff += it.current()->effort(); + } + } + return eff; +} + +Duration Appointment::UsedEffort::usedOvertimeTo(const QDate &date) const { + Duration eff; + QPtrListIterator<UsedEffortItem> it(*this); + for (; it.current(); ++it) { + if (it.current()->isOvertime() && it.current()->date() <= date) { + eff += it.current()->effort(); + } + } + return eff; +} + +bool Appointment::UsedEffort::load(QDomElement &element) { + QString s; + QDomNodeList list = element.childNodes(); + for (unsigned int i=0; i<list.count(); ++i) { + if (list.item(i).isElement()) { + QDomElement e = list.item(i).toElement(); + if (e.tagName() == "actual-effort") { + QDate date; + s = e.attribute("date"); + if (s != "") + date = QDate::fromString(s, Qt::ISODate); + Duration eff = Duration::fromString(e.attribute("effort")); + bool ot = e.attribute("overtime", "0").toInt(); + if (date.isValid()) { + inSort(date, eff, ot); + } else { + kdError()<<k_funcinfo<<"Load failed, illegal date: "<<e.attribute("date")<<endl; + } + } + } + } + return true; +} + +void Appointment::UsedEffort::save(QDomElement &element) const { + if (isEmpty()) return; + QPtrListIterator<UsedEffortItem> it = *this; + for (; it.current(); ++it) { + QDomElement me = element.ownerDocument().createElement("actual-effort"); + element.appendChild(me); + me.setAttribute("date",it.current()->date().toString(Qt::ISODate)); + me.setAttribute("effort",it.current()->effort().toString()); + me.setAttribute("overtime",it.current()->isOvertime()); + } +} + +int Appointment::UsedEffort::compareItems(QPtrCollection::Item item1, QPtrCollection::Item item2) { + QDate d1 = static_cast<UsedEffortItem*>(item1)->date(); + QDate d2 = static_cast<UsedEffortItem*>(item2)->date(); + if (d1 > d2) return 1; + if (d1 < d2) return -1; + return 0; +} + +//// +Appointment::Appointment() + : m_extraRepeats(), m_skipRepeats() { + //kdDebug()<<k_funcinfo<<"("<<this<<")"<<endl; + m_resource=0; + m_node=0; + m_repeatInterval=Duration(); + m_repeatCount=0; + + m_intervals.setAutoDelete(true); +} + +Appointment::Appointment(Schedule *resource, Schedule *node, DateTime start, DateTime end, double load) + : m_extraRepeats(), + m_skipRepeats() { + //kdDebug()<<k_funcinfo<<"("<<this<<")"<<endl; + m_node = node; + m_resource = resource; + m_repeatInterval = Duration(); + m_repeatCount = 0; + + addInterval(start, end, load); + + m_intervals.setAutoDelete(true); +} + +Appointment::Appointment(Schedule *resource, Schedule *node, DateTime start, Duration duration, double load) + : m_extraRepeats(), + m_skipRepeats() { + //kdDebug()<<k_funcinfo<<"("<<this<<")"<<endl; + m_node = node; + m_resource = resource; + m_repeatInterval = Duration(); + m_repeatCount = 0; + + addInterval(start, duration, load); + + m_intervals.setAutoDelete(true); +} + +Appointment::~Appointment() { + //kdDebug()<<k_funcinfo<<"("<<this<<")"<<endl; + detach(); +} + +void Appointment::addInterval(AppointmentInterval *a) { + //kdDebug()<<k_funcinfo<<m_resource->name()<<" to "<<m_node->name()<<endl; + m_intervals.inSort(a); +} +void Appointment::addInterval(const DateTime &start, const DateTime &end, double load) { + addInterval(new AppointmentInterval(start, end, load)); +} +void Appointment::addInterval(const DateTime &start, const Duration &duration, double load) { + DateTime e = start+duration; + addInterval(start, e, load); +} + +double Appointment::maxLoad() const { + double v = 0.0; + QPtrListIterator<AppointmentInterval> it = m_intervals; + for (; it.current(); ++it) { + if (v < it.current()->load()) + v = it.current()->load(); + } + return v; +} + +DateTime Appointment::startTime() const { + DateTime t; + QPtrListIterator<AppointmentInterval> it = m_intervals; + for (; it.current(); ++it) { + if (!t.isValid() || t > it.current()->startTime()) + t = it.current()->startTime(); + } + return t; +} + +DateTime Appointment::endTime() const { + DateTime t; + QPtrListIterator<AppointmentInterval> it = m_intervals; + for (; it.current(); ++it) { + if (!t.isValid() || t < it.current()->endTime()) + t = it.current()->endTime(); + } + return t; +} + +void Appointment::deleteAppointmentFromRepeatList(DateTime) { +} + +void Appointment::addAppointmentToRepeatList(DateTime) { +} + +bool Appointment::isBusy(const DateTime &/*start*/, const DateTime &/*end*/) { + return false; +} + +bool Appointment::loadXML(QDomElement &element, Project &project, Schedule &sch) { + //kdDebug()<<k_funcinfo<<endl; + QDictIterator<Node> it = project.nodeDict(); +/* for (; it.current(); ++it) { + kdDebug()<<" Node="<<it.current()->name()<<" id="<<it.currentKey()<<endl; + }*/ + Node *node = project.findNode(element.attribute("task-id")); + if (node == 0) { + kdError()<<k_funcinfo<<"The referenced task does not exists: "<<element.attribute("task-id")<<endl; + return false; + } + Resource *res = project.resource(element.attribute("resource-id")); + if (res == 0) { + kdError()<<k_funcinfo<<"The referenced resource does not exists: resource id="<<element.attribute("resource-id")<<endl; + return false; + } + if (!res->addAppointment(this, sch)) { + kdError()<<k_funcinfo<<"Failed to add appointment to resource: "<<res->name()<<endl; + return false; + } + if (!node->addAppointment(this, sch)) { + kdError()<<k_funcinfo<<"Failed to add appointment to node: "<<node->name()<<endl; + m_resource->takeAppointment(this); + return false; + } + //kdDebug()<<k_funcinfo<<"res="<<m_resource<<" node="<<m_node<<endl; + QDomNodeList list = element.childNodes(); + for (unsigned int i=0; i<list.count(); ++i) { + if (list.item(i).isElement()) { + QDomElement e = list.item(i).toElement(); + if (e.tagName() == "interval") { + AppointmentInterval *a = new AppointmentInterval(); + if (a->loadXML(e)) { + addInterval(a); + } else { + kdError()<<k_funcinfo<<"Could not load interval"<<endl; + delete a; + } + } + } + } + if (m_intervals.isEmpty()) { + return false; + } + m_actualEffort.load(element); + return true; +} + +void Appointment::saveXML(QDomElement &element) const { + if (m_intervals.isEmpty()) { + kdError()<<k_funcinfo<<"Incomplete appointment data: No intervals"<<endl; + } + if (m_resource == 0 || m_resource->resource() == 0) { + kdError()<<k_funcinfo<<"Incomplete appointment data: No resource"<<endl; + return; + } + if (m_node == 0 || m_node->node() == 0) { + kdError()<<k_funcinfo<<"Incomplete appointment data: No node"<<endl; + return; // shouldn't happen + } + //kdDebug()<<k_funcinfo<<endl; + QDomElement me = element.ownerDocument().createElement("appointment"); + element.appendChild(me); + + me.setAttribute("resource-id", m_resource->resource()->id()); + me.setAttribute("task-id", m_node->node()->id()); + QPtrListIterator<AppointmentInterval> it = m_intervals; + for (; it.current(); ++it) { + it.current()->saveXML(me); + } + m_actualEffort.save(me); +} + +// Returns the total actual effort for this appointment +Duration Appointment::plannedEffort() const { + Duration d; + QPtrListIterator<AppointmentInterval> it = m_intervals; + for (; it.current(); ++it) { + d += it.current()->effort(); + } + return d; +} + +// Returns the planned effort on the date +Duration Appointment::plannedEffort(const QDate &date) const { + Duration d; + DateTime s(date); + DateTime e(date.addDays(1)); + QPtrListIterator<AppointmentInterval> it = m_intervals; + for (; it.current(); ++it) { + d += it.current()->effort(s, e); + } + return d; +} + +// Returns the planned effort upto and including the date +Duration Appointment::plannedEffortTo(const QDate& date) const { + Duration d; + DateTime e(date.addDays(1)); + QPtrListIterator<AppointmentInterval> it = m_intervals; + for (; it.current(); ++it) { + d += it.current()->effort(e, true); + } + return d; +} + +// Returns a list of efforts pr day for interval start, end inclusive +// The list only includes days with any planned effort +EffortCostMap Appointment::plannedPrDay(const QDate& start, const QDate& end) const { + //kdDebug()<<k_funcinfo<<m_node->id()<<", "<<m_resource->id()<<endl; + EffortCostMap ec; + Duration eff; + DateTime dt(start); + DateTime ndt(dt.addDays(1)); + double rate = m_resource->normalRatePrHour(); + AppointmentIntervalListIterator it = m_intervals; + for (; it.current(); ++it) { + DateTime st = it.current()->startTime(); + DateTime e = it.current()->endTime(); + if (end < st.date()) + break; + if (dt.date() < st.date()) { + dt.setDate(st.date()); + } + ndt = dt.addDays(1); + while (dt.date() <= e.date()) { + eff = it.current()->effort(dt, ndt); + ec.add(dt.date(), eff, eff.toDouble(Duration::Unit_h) * rate); + if (dt.date() < e.date() ) { + // loop trough the interval (it spans dates) + dt = ndt; + ndt = ndt.addDays(1); + } else { + break; + } + } + } + return ec; +} + + +// Returns the total actual effort for this appointment +Duration Appointment::actualEffort() const { + return m_actualEffort.usedEffort(); +} + +// Returns the actual effort on the date +Duration Appointment::actualEffort(const QDate &date) const { + return m_actualEffort.usedEffort(date); +} + +// Returns the actual effort upto and including date +Duration Appointment::actualEffortTo(const QDate &date) const { + return m_actualEffort.usedEffortTo(date); +} + +double Appointment::plannedCost() { + if (m_resource && m_resource->resource()) { + return plannedEffort().toDouble(Duration::Unit_h) * m_resource->resource()->normalRate(); //FIXME overtime + } + return 0.0; +} + +//Calculates the planned cost on date +double Appointment::plannedCost(const QDate &date) { + if (m_resource && m_resource->resource()) { + return plannedEffort(date).toDouble(Duration::Unit_h) * m_resource->resource()->normalRate(); //FIXME overtime + } + return 0.0; +} + +//Calculates the planned cost upto and including date +double Appointment::plannedCostTo(const QDate &date) { + if (m_resource && m_resource->resource()) { + return plannedEffortTo(date).toDouble(Duration::Unit_h) * m_resource->resource()->normalRate(); //FIXME overtime + } + return 0.0; +} + +// Calculates the total actual cost for this appointment +double Appointment::actualCost() { + //kdDebug()<<k_funcinfo<<m_actualEffort.usedEffort(false /*ex. overtime*/).toDouble(Duration::Unit_h)<<endl; + if (m_resource && m_resource->resource()) { + return (m_actualEffort.usedEffort(false /*ex. overtime*/).toDouble(Duration::Unit_h)*m_resource->resource()->normalRate()) + (m_actualEffort.usedOvertime().toDouble(Duration::Unit_h)*m_resource->resource()->overtimeRate()); + } + return 0.0; +} + +// Calculates the actual cost on date +double Appointment::actualCost(const QDate &date) { + if (m_resource && m_resource->resource()) { + return (m_actualEffort.usedEffort(date, false /*ex. overtime*/).toDouble(Duration::Unit_h)*m_resource->resource()->normalRate()) + (m_actualEffort.usedOvertime(date).toDouble(Duration::Unit_h)*m_resource->resource()->overtimeRate()); + } + return 0.0; +} + +// Calculates the actual cost upto and including date +double Appointment::actualCostTo(const QDate &date) { + if (m_resource && m_resource->resource()) { + return (m_actualEffort.usedEffortTo(date, false /*ex. overtime*/).toDouble(Duration::Unit_h)*m_resource->resource()->normalRate()) + (m_actualEffort.usedOvertimeTo(date).toDouble(Duration::Unit_h)*m_resource->resource()->overtimeRate()); + } + return 0.0; +} + +void Appointment::addActualEffort(QDate date, Duration effort, bool overtime) { + m_actualEffort.inSort(date, effort, overtime); +} + +bool Appointment::attach() { + //kdDebug()<<k_funcinfo<<"("<<this<<")"<<endl; + if (m_resource && m_node) { + m_resource->add(this); + m_node->add(this); + return true; + } + kdWarning()<<k_funcinfo<<"Failed: "<<(m_resource ? "" : "resource=0 ") + <<(m_node ? "" : "node=0")<<endl; + return false; +} + +void Appointment::detach() { + //kdDebug()<<k_funcinfo<<"("<<this<<")"<<endl; + if (m_resource) { + m_resource->takeAppointment(this); // takes from node also + } + if (m_node) { + m_node->takeAppointment(this); // to make it robust + } +} + +// Returns the effort from start to end +Duration Appointment::effort(const DateTime &start, const DateTime &end) const { + Duration d; + QPtrListIterator<AppointmentInterval> it = m_intervals; + for (; it.current(); ++it) { + d += it.current()->effort(start, end); + } + return d; +} +// Returns the effort from start for the duration +Duration Appointment::effort(const DateTime &start, const Duration &duration) const { + Duration d; + QPtrListIterator<AppointmentInterval> it = m_intervals; + for (; it.current(); ++it) { + d += it.current()->effort(start, start+duration); + } + return d; +} +// Returns the effort upto time / from time +Duration Appointment::effortFrom(const DateTime &time) const { + Duration d; + QPtrListIterator<AppointmentInterval> it = m_intervals; + for (; it.current(); ++it) { + d += it.current()->effort(time, false); + } + return d; +} + +Appointment &Appointment::operator=(const Appointment &app) { + m_resource = app.resource(); + m_node = app.node(); + m_repeatInterval = app.repeatInterval(); + m_repeatCount = app.repeatCount(); + + m_intervals.clear(); + QPtrListIterator<AppointmentInterval> it = app.intervals(); + for (; it.current(); ++it) { + addInterval(new AppointmentInterval(*(it.current()))); + } + return *this; +} + +Appointment &Appointment::operator+=(const Appointment &app) { + *this = *this + app; + return *this; +} + +Appointment Appointment::operator+(const Appointment &app) { + Appointment a; + AppointmentIntervalList ai = app.intervals(); + AppointmentInterval i; + AppointmentInterval *i1 = m_intervals.first(); + AppointmentInterval *i2 = ai.first(); + DateTime from; + while (i1 || i2) { + if (!i1) { + if (!from.isValid() || from < i2->startTime()) + from = i2->startTime(); + a.addInterval(from, i2->endTime(), i2->load()); + //kdDebug()<<"Interval+ (i2): "<<from.toString()<<" - "<<i2->endTime().toString()<<endl; + from = i2->endTime(); + i2 = ai.next(); + continue; + } + if (!i2) { + if (!from.isValid() || from < i1->startTime()) + from = i1->startTime(); + a.addInterval(from, i1->endTime(), i1->load()); + //kdDebug()<<"Interval+ (i1): "<<from.toString()<<" - "<<i1->endTime().toString()<<endl; + from = i1->endTime(); + i1 = m_intervals.next(); + continue; + } + i = i1->firstInterval(*i2, from); + if (!i.isValid()) { + break; + } + a.addInterval(i); + from = i.endTime(); + //kdDebug()<<"Interval+ (i): "<<i.startTime().toString()<<" - "<<i.endTime().toString()<<" load="<<i.load()<<endl; + if (i1 && a.endTime() >= i1->endTime()) { + i1 = m_intervals.next(); + } + if (i2 && a.endTime() >= i2->endTime()) { + i2 = ai.next(); + } + } + return a; +} + +#ifndef NDEBUG +void Appointment::printDebug(QString indent) +{ + bool err = false; + if (m_node == 0) { + kdDebug()<<indent<<" No node schedule"<<endl; + err = true; + } else if (m_node->node() == 0) { + kdDebug()<<indent<<" No node"<<endl; + err = true; + } + if (m_resource == 0) { + kdDebug()<<indent<<" No resource schedule"<<endl; + err = true; + } else if (m_resource->resource() == 0) { + kdDebug()<<indent<<" No resource"<<endl; + err = true; + } + if (err) + return; + kdDebug()<<indent<<" + Appointment to schedule: "<<m_node->name()<<" ("<<m_node->type()<<")"<<" resource: "<<m_resource->resource()->name()<<endl; + indent += " ! "; + QPtrListIterator<AppointmentInterval> it = intervals(); + for (; it.current(); ++it) { + kdDebug()<<indent<<it.current()->startTime().toString()<<" - "<<it.current()->endTime().toString()<<" load="<<it.current()->load()<<endl; + } +} +#endif + +} //KPlato namespace diff --git a/kplato/kptappointment.h b/kplato/kptappointment.h new file mode 100644 index 00000000..820905a1 --- /dev/null +++ b/kplato/kptappointment.h @@ -0,0 +1,269 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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 KPTAPPOINTMENT_H +#define KPTAPPOINTMENT_H + +#include "kptduration.h" +#include "kptdatetime.h" + +#include <qdom.h> +#include <qintdict.h> +#include <qstring.h> +#include <qptrlist.h> + +#include <kdebug.h> + +class QTime; + +namespace KPlato +{ + +class Risk; +class Effort; +class Appointment; +class Task; +class Node; +class Project; +class Resource; +class ResourceRequest; +class ResourceGroupRequest; +class Calendar; +class ResourceRequestCollection; +class EffortCostMap; +class Schedule; + + + +class AppointmentInterval { +public: + AppointmentInterval(); + AppointmentInterval(const AppointmentInterval &AppointmentInterval); + AppointmentInterval(const DateTime &start, const DateTime end, double load=100); + ~AppointmentInterval(); + + void set(DateTime &start, DateTime &end, double load=100); + void set(DateTime &start, Duration &duration, double load=100); + + Duration effort() const { return (m_end - m_start) * m_load / 100; } + Duration effort(const DateTime &start, const DateTime end) const; + Duration effort(const DateTime &time, bool upto) const; + + bool loadXML(QDomElement &element); + void saveXML(QDomElement &element) const; + + const DateTime &startTime() const { return m_start; } + void setStartTime(const DateTime &time) { m_start = time; } + const DateTime &endTime() const { return m_end; } + void setEndTime(const DateTime &time) { m_end = time; } + double load() const { return m_load; } + void setLoad(double load) { m_load = load; } + + bool isValid() const; + AppointmentInterval firstInterval(const AppointmentInterval &interval, const DateTime &from) const; + +private: + DateTime m_start; + DateTime m_end; + double m_load; //percent +}; + + +/** + * This list is sorted after 1) startdatetime, 2) enddatetime. + * The intervals do not overlap, an interval does not start before the + * previous interval ends. + */ +class AppointmentIntervalList : public QPtrList<AppointmentInterval> { +protected: + int compareItems(QPtrCollection::Item item1, QPtrCollection::Item item2) { + AppointmentInterval *i1 = static_cast<AppointmentInterval*>(item1); + AppointmentInterval *i2 = static_cast<AppointmentInterval*>(item2); + if (i1->startTime() < i2->startTime()) { + return -1; + } + if (i1->startTime() > i2->startTime()) { + return 1; + } + if (i1->endTime() < i2->endTime()) { + return -1; + } + if (i1->endTime() > i2->endTime()) { + return 1; + } + return 0; + } +}; +typedef QPtrListIterator<AppointmentInterval> AppointmentIntervalListIterator; + +/** + * A resource (@ref Resource) can be scheduled to be used at any time, + * this is represented internally with Appointments + * There is one Appointment per resource-task pair. + * An appointment can be devided into several intervals, represented with + * a list of AppointmentInterval. + * This list is sorted after 1) startdatetime, 2) enddatetime. + * The intervals do not overlap, an interval does not start before the + * previous interval ends. + * An interval is a countinous time interval with the same load. It can span dates. + */ +class Appointment { +public: + Appointment(); + Appointment(Schedule *resource, Schedule *node, DateTime start, DateTime end, double load); + Appointment(Schedule *resource, Schedule *node, DateTime start, Duration duration, double load); + ~Appointment(); + + // get/set member values. + Schedule *node() const { return m_node; } + void setNode(Schedule *n) { m_node = n; } + + Schedule *resource() const { return m_resource; } + void setResource(Schedule *r) { m_resource = r; } + + DateTime startTime() const; + DateTime endTime() const; + double maxLoad() const; + + const Duration &repeatInterval() const {return m_repeatInterval;} + void setRepeatInterval(Duration ri) {m_repeatInterval=ri;} + + int repeatCount() const { return m_repeatCount; } + void setRepeatCount(int rc) { m_repeatCount=rc; } + + void deleteAppointmentFromRepeatList(DateTime time); + void addAppointmentToRepeatList(DateTime time); + + bool isBusy(const DateTime &start, const DateTime &end); + + /// attach appointment to resource and node + bool attach(); + /// detach appointment from resource and node + void detach(); + + void addInterval(AppointmentInterval *a); + void addInterval(AppointmentInterval &a) + { addInterval(new AppointmentInterval(a)); } + void addInterval(const DateTime &start, const DateTime &end, double load=100); + void addInterval(const DateTime &start, const Duration &duration, double load=100); + + const AppointmentIntervalList &intervals() const { return m_intervals; } + + bool loadXML(QDomElement &element, Project &project, Schedule &sch); + void saveXML(QDomElement &element) const; + + /** + * Returns the planned effort and cost for the interval start to end (inclusive). + * Only dates with any planned effort is returned. + */ + EffortCostMap plannedPrDay(const QDate& start, const QDate& end) const; + + /// Returns the planned effort from start to end + Duration effort(const DateTime &start, const DateTime &end) const; + /// Returns the planned effort from start for the duration + Duration effort(const DateTime &start, const Duration &duration) const; + /// Returns the planned effort from time onwards + Duration effortFrom(const DateTime &time) const; + + /// Returns the total planned effort for this appointment + Duration plannedEffort() const; + /// Returns the planned effort on the date + Duration plannedEffort(const QDate &date) const; + /// Returns the planned effort upto and including date + Duration plannedEffortTo(const QDate &date) const; + + /// Returns the total actual effort for this appointment + Duration actualEffort() const; + /// Returns the actual effort on the date + Duration actualEffort(const QDate &date) const; + /// Returns the actual effort on the date + Duration actualEffortTo(const QDate &date) const; + + /// Calculates the total planned cost for this appointment + double plannedCost(); + /// Calculates the planned cost on date + double plannedCost(const QDate &date); + /// Calculates the planned cost upto and including date + double plannedCostTo(const QDate &date); + + /// Calculates the total actual cost for this appointment + double actualCost(); + /// Calculates the actual cost on date + double actualCost(const QDate &date); + /// Calculates the actual cost upto and including date + double actualCostTo(const QDate &date); + + Appointment &operator=(const Appointment &app); + Appointment &operator+=(const Appointment &app); + Appointment operator+(const Appointment &app); + + void addActualEffort(QDate date, Duration effort, bool overtime=false); + +private: + Schedule *m_node; + Schedule *m_resource; + + Duration m_repeatInterval; + int m_repeatCount; + QPtrList<Duration> m_extraRepeats; + QPtrList<Duration> m_skipRepeats; + + AppointmentIntervalList m_intervals; + + class UsedEffortItem { + public: + UsedEffortItem(QDate date, Duration effort, bool overtime=false); + QDate date(); + Duration effort(); + bool isOvertime(); + private: + QDate m_date; + Duration m_effort; + bool m_overtime; + }; + class UsedEffort : QPtrList<UsedEffortItem> { + public: + UsedEffort(); + ~UsedEffort() {} + void inSort(QDate date, Duration effort, bool overtime=false); + Duration usedEffort(bool includeOvertime=true) const; + Duration usedEffort(const QDate &date, bool includeOvertime=true) const; + Duration usedEffortTo(const QDate &date, bool includeOvertime=true) const; + Duration usedOvertime() const; + Duration usedOvertime(const QDate &date) const; + Duration usedOvertimeTo(const QDate &date) const; + bool load(QDomElement &element); + void save(QDomElement &element) const; + + protected: + int compareItems(QPtrCollection::Item item1, QPtrCollection::Item item2); + }; + + UsedEffort m_actualEffort; + +#ifndef NDEBUG +public: + void printDebug(QString ident); +#endif +}; + + +} //KPlato namespace + +#endif diff --git a/kplato/kptcalendar.cc b/kplato/kptcalendar.cc new file mode 100644 index 00000000..38c2a286 --- /dev/null +++ b/kplato/kptcalendar.cc @@ -0,0 +1,1036 @@ +/* This file is part of the KDE project + Copyright (C) 2003 - 2006 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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. +*/ + +#include "kptcalendar.h" +#include "kptduration.h" +#include "kptdatetime.h" +#include "kptproject.h" + +#include <qdom.h> +#include <qptrlist.h> + +#include <klocale.h> +#include <kdebug.h> + +namespace KPlato +{ + +///// CalendarDay //// +CalendarDay::CalendarDay() + : m_date(), + m_state(0), + m_workingIntervals() { + + //kdDebug()<<k_funcinfo<<"("<<this<<")"<<endl; + m_workingIntervals.setAutoDelete(true); +} + +CalendarDay::CalendarDay(int state) + : m_date(), + m_state(state), + m_workingIntervals() { + + //kdDebug()<<k_funcinfo<<"("<<this<<")"<<endl; + m_workingIntervals.setAutoDelete(true); +} + +CalendarDay::CalendarDay(QDate date, int state) + : m_date(date), + m_state(state), + m_workingIntervals() { + + //kdDebug()<<k_funcinfo<<"("<<this<<")"<<endl; + m_workingIntervals.setAutoDelete(true); +} + +CalendarDay::CalendarDay(CalendarDay *day) + : m_workingIntervals() { + + //kdDebug()<<k_funcinfo<<"("<<this<<") from ("<<day<<")"<<endl; + m_workingIntervals.setAutoDelete(true); + copy(*day); +} + +CalendarDay::~CalendarDay() { + //kdDebug()<<k_funcinfo<<"("<<this<<")"<<endl; +} + +const CalendarDay &CalendarDay::copy(const CalendarDay &day) { + //kdDebug()<<k_funcinfo<<"("<<&day<<") date="<<day.date().toString()<<endl; + m_date = day.date(); + m_state = day.state(); + m_workingIntervals.clear(); + QPtrListIterator<QPair<QTime, QTime> > it = day.workingIntervals(); + for(; it.current(); ++it) { + m_workingIntervals.append(new QPair<QTime, QTime>(it.current()->first, it.current()->second)); + } + return *this; +} + +bool CalendarDay::load(QDomElement &element) { + //kdDebug()<<k_funcinfo<<endl; + bool ok=false; + m_state = QString(element.attribute("state", "-1")).toInt(&ok); + if (m_state < 0) + return false; + //kdDebug()<<k_funcinfo<<" state="<<m_state<<endl; + QString s = element.attribute("date"); + if (s != "") { + m_date = QDate::fromString(s, Qt::ISODate); + if (!m_date.isValid()) + m_date = QDate::fromString(s); + } + clearIntervals(); + QDomNodeList list = element.childNodes(); + for (unsigned int i=0; i<list.count(); ++i) { + if (list.item(i).isElement()) { + QDomElement e = list.item(i).toElement(); + if (e.tagName() == "interval") { + //kdDebug()<<k_funcinfo<<"Interval start="<<e.attribute("start")<<" end="<<e.attribute("end")<<endl; + QString st = e.attribute("start"); + QString en = e.attribute("end"); + if (st != "" && en != "") { + QTime start = QTime::fromString(st); + QTime end = QTime::fromString(en); + addInterval(new QPair<QTime, QTime>(start,end)); + } + } + } + } + return true; +} + +void CalendarDay::save(QDomElement &element) const { + //kdDebug()<<k_funcinfo<<m_date.toString()<<endl; + if (m_state == Map::None) + return; + if (m_date.isValid()) { + element.setAttribute("date", m_date.toString(Qt::ISODate)); + } + element.setAttribute("state", m_state); + if (m_workingIntervals.count() == 0) + return; + + QPtrListIterator<QPair<QTime, QTime> > it = m_workingIntervals; + for (; it.current(); ++it) { + QDomElement me = element.ownerDocument().createElement("interval"); + element.appendChild(me); + me.setAttribute("end", it.current()->second.toString()); + me.setAttribute("start", it.current()->first.toString()); + } +} + +void CalendarDay::addInterval(QPair<QTime, QTime> *interval) { + m_workingIntervals.append(interval); +} + +QTime CalendarDay::startOfDay() const { + QTime t; + if (!m_workingIntervals.isEmpty()) { + QPtrListIterator<QPair<QTime, QTime> > it = m_workingIntervals; + t = it.current()->first; + for (++it; it.current(); ++it) { + if (t > it.current()->first) + t = it.current()->first; + } + } + return t; +} + +QTime CalendarDay::endOfDay() const { + QTime t; + if (!m_workingIntervals.isEmpty()) { + QPtrListIterator<QPair<QTime, QTime> > it = m_workingIntervals; + t = it.current()->second; + for (++it; it.current(); ++it) { + if (t > it.current()->second) + t = it.current()->second; + } + } + return t; +} + +bool CalendarDay::operator==(const CalendarDay *day) const { + return operator==(*day); +} +bool CalendarDay::operator==(const CalendarDay &day) const { + //kdDebug()<<k_funcinfo<<endl; + if (m_date.isValid() && day.date().isValid()) { + if (m_date != day.date()) { + //kdDebug()<<k_funcinfo<<m_date.toString()<<" != "<<day.date().toString()<<endl; + return false; + } + } else if (m_date.isValid() != day.date().isValid()) { + //kdDebug()<<k_funcinfo<<"one of the dates is not valid"<<endl; + return false; + } + if (m_state != day.state()) { + //kdDebug()<<k_funcinfo<<m_state<<" != "<<day.state()<<endl; + return false; + } + if (m_workingIntervals.count() != day.workingIntervals().count()) { + //kdDebug()<<k_funcinfo<<m_workingIntervals.count()<<" != "<<day.workingIntervals().count()<<endl; + return false; + } + QPtrListIterator<QPair<QTime, QTime> > it = m_workingIntervals; + QPtrListIterator<QPair<QTime, QTime> > dit = day.workingIntervals(); + for (; it.current(); ++it) { + bool res = false; + QPair<QTime, QTime> *a = it.current(); + for (dit.toFirst(); dit.current(); ++dit) { + QPair<QTime, QTime> *b = dit.current(); + if (a->first == b->first && a->second == b->second) { + res = true; + break; + } + } + if (res == false) { + //kdDebug()<<k_funcinfo<<"interval mismatch "<<a->first.toString()<<"-"<<a->second.toString()<<endl; + return false; + } + } + return true; +} +bool CalendarDay::operator!=(const CalendarDay *day) const { + return operator!=(*day); +} +bool CalendarDay::operator!=(const CalendarDay &day) const { + return !operator==(day); +} + +Duration CalendarDay::effort(const QTime &start, const QTime &end) { + //kdDebug()<<k_funcinfo<<start.toString()<<" - "<<end.toString()<<endl; + Duration eff; + if (m_state != Map::Working) { + //kdDebug()<<k_funcinfo<<"Non working day"<<endl; + return eff; + } + QPtrListIterator<QPair<QTime, QTime> > it = m_workingIntervals; + for (; it.current(); ++it) { + //kdDebug()<<k_funcinfo<<"Interval: "<<it.current()->first.toString()<<" - "<<it.current()->second.toString()<<endl; + if (end > it.current()->first && start < it.current()->second) { + DateTime dtStart(QDate::currentDate(), start); + if (start < it.current()->first) { + dtStart.setTime(it.current()->first); + } + DateTime dtEnd(QDate::currentDate(), end); + if (end > it.current()->second) { + dtEnd.setTime(it.current()->second); + } + eff += dtEnd - dtStart; + //kdDebug()<<k_funcinfo<<dtStart.time().toString()<<" - "<<dtEnd.time().toString()<<"="<<eff.toString(Duration::Format_Day)<<endl; + } + } + //kdDebug()<<k_funcinfo<<(m_date.isValid()?m_date.toString(Qt::ISODate):"Weekday")<<": "<<start.toString()<<" - "<<end.toString()<<": total="<<eff.toString(Duration::Format_Day)<<endl; + return eff; +} + +QPair<QTime, QTime> CalendarDay::interval(const QTime &start, const QTime &end) const { + //kdDebug()<<k_funcinfo<<endl; + QTime t1, t2; + if (m_state == Map::Working) { + QPtrListIterator<QPair<QTime, QTime> > it = m_workingIntervals; + for (; it.current(); ++it) { + if (start < it.current()->second && end > it.current()->first) { + t1 = start > it.current()->first ? start : it.current()->first; + t2 = end < it.current()->second ? end : it.current()->second; + //kdDebug()<<k_funcinfo<<t1.toString()<<" to "<<t2.toString()<<endl; + return QPair<QTime, QTime>(t1, t2); + } + } + } + //kdError()<<k_funcinfo<<"No interval "<<m_date<<": "<<start<<","<<end<<endl; + return QPair<QTime, QTime>(t1, t2); +} + +bool CalendarDay::hasInterval() const { + return m_state == Map::Working && m_workingIntervals.count() > 0; +} + +bool CalendarDay::hasInterval(const QTime &start, const QTime &end) const { + //kdDebug()<<k_funcinfo<<(m_date.isValid()?m_date.toString(Qt::ISODate):"Weekday")<<" "<<start.toString()<<" - "<<end.toString()<<endl; + if (m_state != Map::Working) { + return false; + } + QPtrListIterator<QPair<QTime, QTime> > it = m_workingIntervals; + for (; it.current(); ++it) { + if (start < it.current()->second && end > it.current()->first) { + //kdDebug()<<k_funcinfo<<"true:"<<(m_date.isValid()?m_date.toString(Qt::ISODate):"Weekday")<<" "<<it.current()->first.toString()<<" - "<<it.current()->second.toString()<<endl; + return true; + } + } + return false; +} + +Duration CalendarDay::duration() const { + Duration dur; + QPtrListIterator<QPair<QTime, QTime> > it = m_workingIntervals; + for (; it.current(); ++it) { + DateTime start(QDate::currentDate(), it.current()->first); + DateTime end(QDate::currentDate(), it.current()->second); + dur += end - start; + } + return dur; +} + +///// CalendarWeekdays //// +CalendarWeekdays::CalendarWeekdays() + : m_weekdays(), + m_workHours(40) { + + //kdDebug()<<k_funcinfo<<"--->"<<endl; + for (int i=0; i < 7; ++i) { + m_weekdays.append(new CalendarDay()); + } + m_weekdays.setAutoDelete(false); + //kdDebug()<<k_funcinfo<<"<---"<<endl; +} + +CalendarWeekdays::CalendarWeekdays(CalendarWeekdays *weekdays) + : m_weekdays() { + //kdDebug()<<k_funcinfo<<"--->"<<endl; + copy(*weekdays); + //kdDebug()<<k_funcinfo<<"<---"<<endl; +} + +CalendarWeekdays::~CalendarWeekdays() { + m_weekdays.setAutoDelete(true); + //kdDebug()<<k_funcinfo<<endl; +} + +const CalendarWeekdays &CalendarWeekdays::copy(const CalendarWeekdays &weekdays) { + //kdDebug()<<k_funcinfo<<endl; + m_weekdays.setAutoDelete(true); + m_weekdays.clear(); + m_weekdays.setAutoDelete(false); + QPtrListIterator<CalendarDay> it = weekdays.weekdays(); + for (; it.current(); ++it) { + m_weekdays.append(new CalendarDay(it.current())); + } + return *this; +} + +bool CalendarWeekdays::load(QDomElement &element) { + //kdDebug()<<k_funcinfo<<endl; + bool ok; + int dayNo = QString(element.attribute("day","-1")).toInt(&ok); + if (dayNo < 0 || dayNo > 6) { + kdError()<<k_funcinfo<<"Illegal weekday: "<<dayNo<<endl; + return true; // we continue anyway + } + CalendarDay *day = m_weekdays.at(dayNo); + if (!day) + day = new CalendarDay(); + if (!day->load(element)) + day->setState(Map::None); + return true; +} + +void CalendarWeekdays::save(QDomElement &element) const { + //kdDebug()<<k_funcinfo<<endl; + QPtrListIterator<CalendarDay> it = m_weekdays; + for (int i=0; it.current(); ++it) { + QDomElement me = element.ownerDocument().createElement("weekday"); + element.appendChild(me); + me.setAttribute("day", i++); + it.current()->save(me); + } +} + +IntMap CalendarWeekdays::map() { + IntMap days; + for (unsigned int i=0; i < m_weekdays.count(); ++i) { + if (m_weekdays.at(i)->state() > 0) + days.insert(i+1, m_weekdays.at(i)->state()); //Note: day numbers 1..7 + } + return days; +} + +int CalendarWeekdays::state(const QDate &date) const { + return state(date.dayOfWeek()-1); +} + +int CalendarWeekdays::state(int weekday) const { + CalendarDay *day = const_cast<CalendarWeekdays*>(this)->m_weekdays.at(weekday); + return day ? day->state() : Map::None; +} + +void CalendarWeekdays::setState(int weekday, int state) { + CalendarDay *day = m_weekdays.at(weekday); + if (!day) + return; + day->setState(state); +} + +const QPtrList<QPair<QTime, QTime> > &CalendarWeekdays::intervals(int weekday) const { + CalendarDay *day = const_cast<CalendarWeekdays*>(this)->m_weekdays.at(weekday); + Q_ASSERT(day); + return day->workingIntervals(); +} + +void CalendarWeekdays::setIntervals(int weekday, QPtrList<QPair<QTime, QTime> >intervals) { + CalendarDay *day = m_weekdays.at(weekday); + if (day) + day->setIntervals(intervals); +} + +void CalendarWeekdays::clearIntervals(int weekday) { + CalendarDay *day = m_weekdays.at(weekday); + if (day) + day->clearIntervals(); +} + +bool CalendarWeekdays::operator==(const CalendarWeekdays *wd) const { + if (m_weekdays.count() != wd->weekdays().count()) + return false; + for (unsigned int i=0; i < m_weekdays.count(); ++i) { + // is there a better way to get around this const stuff? + CalendarDay *day1 = const_cast<CalendarWeekdays*>(this)->m_weekdays.at(i); + CalendarDay *day2 = const_cast<QPtrList<CalendarDay>&>(wd->weekdays()).at(i); + if (day1 != day2) + return false; + } + return true; +} +bool CalendarWeekdays::operator!=(const CalendarWeekdays *wd) const { + if (m_weekdays.count() != wd->weekdays().count()) + return true; + for (unsigned int i=0; i < m_weekdays.count(); ++i) { + // is there a better way to get around this const stuff? + CalendarDay *day1 = const_cast<CalendarWeekdays*>(this)->m_weekdays.at(i); + CalendarDay *day2 = const_cast<QPtrList<CalendarDay>&>(wd->weekdays()).at(i); + if (day1 != day2) + return true; + } + return false; +} + +Duration CalendarWeekdays::effort(const QDate &date, const QTime &start, const QTime &end) { + //kdDebug()<<k_funcinfo<<"Day of week="<<date.dayOfWeek()-1<<endl; + CalendarDay *day = weekday(date.dayOfWeek()-1); + if (day && day->state() == Map::Working) { + return day->effort(start, end); + } + return Duration::zeroDuration; +} + +QPair<QTime, QTime> CalendarWeekdays::interval(const QDate date, const QTime &start, const QTime &end) const { + //kdDebug()<<k_funcinfo<<endl; + CalendarDay *day = weekday(date.dayOfWeek()-1); + if (day && day->state() == Map::Working) { + if (day->hasInterval(start, end)) { + return day->interval(start, end); + } + } + return QPair<QTime, QTime>(QTime(), QTime()); +} + +bool CalendarWeekdays::hasInterval(const QDate date, const QTime &start, const QTime &end) const { + //kdDebug()<<k_funcinfo<<date.toString()<<": "<<start.toString()<<" - "<<end.toString()<<endl; + CalendarDay *day = weekday(date.dayOfWeek()-1); + return day && day->hasInterval(start, end); +} + +bool CalendarWeekdays::hasInterval() const { + //kdDebug()<<k_funcinfo<<endl; + QPtrListIterator<CalendarDay> it = m_weekdays; + for (; it.current(); ++it) { + if (it.current()->hasInterval()) + return true; + } + return false; +} + +CalendarDay *CalendarWeekdays::weekday(int day) const { + QPtrListIterator<CalendarDay> it = m_weekdays; + for (int i=0; it.current(); ++it, ++i) { + if (i == day) + return it.current(); + } + return 0; +} + +Duration CalendarWeekdays::duration() const { + Duration dur; + QPtrListIterator<CalendarDay> it = m_weekdays; + for (; it.current(); ++it) { + dur += it.current()->duration(); + } + return dur; +} + +Duration CalendarWeekdays::duration(int _weekday) const { + CalendarDay *day = weekday(_weekday); + if (day) + return day->duration(); + return Duration(); +} + +QTime CalendarWeekdays::startOfDay(int _weekday) const { + CalendarDay *day = weekday(_weekday); + if (day) + return day->startOfDay(); + return QTime(); +} + +QTime CalendarWeekdays::endOfDay(int _weekday) const { + CalendarDay *day = weekday(_weekday); + if (day) + return day->endOfDay(); + return QTime(); +} + + +///// Calendar //// + +Calendar::Calendar() + : m_parent(0), + m_project(0), + m_deleted(false) { + + init(); +} + +Calendar::Calendar(QString name, Calendar *parent) + : m_name(name), + m_parent(parent), + m_project(0), + m_deleted(false), + m_days() { + + init(); +} + +Calendar::~Calendar() { + //kdDebug()<<k_funcinfo<<"deleting "<<m_name<<endl; + removeId(); + delete m_weekdays; +} +Calendar::Calendar(Calendar *calendar) + : m_project(0), + m_days() { + m_days.setAutoDelete(true); + copy(*calendar); +} + +const Calendar &Calendar::copy(Calendar &calendar) { + m_name = calendar.name(); + m_parent = calendar.parent(); + m_deleted = calendar.isDeleted(); + m_id = calendar.id(); + + QPtrListIterator<CalendarDay> it = calendar.days(); + for (; it.current(); ++it) { + m_days.append(new CalendarDay(it.current())); + } + m_weekdays = new CalendarWeekdays(calendar.weekdays()); + return *this; +} + +void Calendar::init() { + m_days.setAutoDelete(true); + m_weekdays = new CalendarWeekdays(); +} + +void Calendar::setProject(Project *project) { + m_project = project; + generateId(); +} + +void Calendar::setDeleted(bool yes) { + if (yes) { + removeId(); + } else { + setId(m_id); + } + m_deleted = yes; +} +bool Calendar::setId(QString id) { + //kdDebug()<<k_funcinfo<<id<<endl; + if (id.isEmpty()) { + kdError()<<k_funcinfo<<"id is empty"<<endl; + m_id = id; + return false; + } + Calendar *c = findCalendar(); + if (c == this) { + kdDebug()<<k_funcinfo<<"My id found, remove it"<<endl; + removeId(); + } else if (c) { + //can happen when making a copy + kdError()<<k_funcinfo<<"My id '"<<m_id<<"' already used for different node: "<<c->name()<<endl; + } + if (findCalendar(id)) { + kdError()<<k_funcinfo<<"id '"<<id<<"' is already used for different node: "<<findCalendar(id)->name()<<endl; + m_id = QString(); // hmmm + return false; + } + m_id = id; + insertId(id); + //kdDebug()<<k_funcinfo<<m_name<<": inserted id="<<id<<endl; + return true; +} + +void Calendar::generateId() { + if (!m_id.isEmpty()) { + removeId(); + } + for (int i=0; i<32000 ; ++i) { + m_id = m_id.setNum(i); + if (!findCalendar()) { + insertId(m_id); + return; + } + } + m_id = QString(); +} + +bool Calendar::load(QDomElement &element) { + //kdDebug()<<k_funcinfo<<element.text()<<endl; + //bool ok; + setId(element.attribute("id")); + m_parentId = element.attribute("parent"); + m_name = element.attribute("name",""); + //TODO parent + + QDomNodeList list = element.childNodes(); + for (unsigned int i=0; i<list.count(); ++i) { + if (list.item(i).isElement()) { + QDomElement e = list.item(i).toElement(); + if (e.tagName() == "weekday") { + if (!m_weekdays->load(e)) + return false; + } + if (e.tagName() == "day") { + CalendarDay *day = new CalendarDay(); + if (day->load(e)) { + if (!day->date().isValid()) { + delete day; + kdError()<<k_funcinfo<<m_name<<": Failed to load calendarDay - Invalid date"<<endl; + } else { + CalendarDay *d = findDay(day->date()); + if (d) { + // already exists, keep the new + removeDay(d); + kdWarning()<<k_funcinfo<<m_name<<" Load calendarDay - Date already exists"<<endl; + } + addDay(day); + } + } else { + delete day; + kdError()<<k_funcinfo<<"Failed to load calendarDay"<<endl; + return true; //false; don't throw away the whole calendar + } + } + } + } + return true; +} + +void Calendar::save(QDomElement &element) const { + //kdDebug()<<k_funcinfo<<m_name<<endl; + if (m_deleted) + return; + + QDomElement me = element.ownerDocument().createElement("calendar"); + element.appendChild(me); + if (m_parent && !m_parent->isDeleted()) + me.setAttribute("parent", m_parent->id()); + me.setAttribute("name", m_name); + me.setAttribute("id", m_id); + m_weekdays->save(me); + QPtrListIterator<CalendarDay> it = m_days; + for (; it.current(); ++it) { + QDomElement e = me.ownerDocument().createElement("day"); + me.appendChild(e); + it.current()->save(e); + } + +} + +CalendarDay *Calendar::findDay(const QDate &date, bool skipNone) const { + //kdDebug()<<k_funcinfo<<date.toString()<<endl; + QPtrListIterator<CalendarDay> it = m_days; + for (; it.current(); ++it) { + if (it.current()->date() == date) { + if (skipNone && it.current()->state() == Map::None) { + continue; // hmmm, break? + } + return it.current(); + } + } + //kdDebug()<<k_funcinfo<<date.toString()<<" not found"<<endl; + return 0; +} + +bool Calendar::hasParent(Calendar *cal) { + //kdDebug()<<k_funcinfo<<endl; + if (!m_parent) + return false; + if (m_parent == cal) + return true; + return m_parent->hasParent(cal); +} + +Duration Calendar::effort(const QDate &date, const QTime &start, const QTime &end) const { + //kdDebug()<<k_funcinfo<<m_name<<": "<<date.toString(Qt::ISODate)<<" "<<start.toString()<<" - "<<end.toString()<<endl; + if (start == end) { + return Duration::zeroDuration; + } + QTime _start = start; + QTime _end = end; + if (start > end) { + _start = end; + _end = start; + } + // first, check my own day + CalendarDay *day = findDay(date, true); + if (day) { + if (day->state() == Map::Working) { + return day->effort(_start, _end); + } else if (day->state() == Map::NonWorking) { + return Duration::zeroDuration; + } else { + kdError()<<k_funcinfo<<"Invalid state: "<<day->state()<<endl; + return Duration::zeroDuration; + } + } + // check my own weekdays + if (m_weekdays) { + if (m_weekdays->state(date) == Map::Working) { + return m_weekdays->effort(date, _start, _end); + } + if (m_weekdays->state(date) == Map::NonWorking) { + return Duration::zeroDuration; + } + } + if (m_parent && !m_parent->isDeleted()) { + return m_parent->effort(date, start, end); + } + // Check default calendar + return project()->defaultCalendar()->effort(date, start, end); +} + +Duration Calendar::effort(const DateTime &start, const DateTime &end) const { + //kdDebug()<<k_funcinfo<<m_name<<": "<<start<<" to "<<end<<endl; + Duration eff; + if (!start.isValid() || !end.isValid() || end <= start) { + return eff; + } + QDate date = start.date(); + QTime startTime = start.time(); + QTime endTime = end.time(); + if (end.date() > date) { + endTime.setHMS(23, 59, 59, 999); + } + eff = effort(date, startTime, endTime); // first day + // Now get all the rest of the days + for (date = date.addDays(1); date <= end.date(); date = date.addDays(1)) { + if (date < end.date()) + eff += effort(date, QTime(), endTime); // whole days + else + eff += effort(date, QTime(), end.time()); // last day + //kdDebug()<<k_funcinfo<<": eff now="<<eff.toString(Duration::Format_Day)<<endl; + } + //kdDebug()<<k_funcinfo<<start.date().toString()<<"- "<<end.date().toString()<<": total="<<eff.toString(Duration::Format_Day)<<endl; + return eff; +} + + +QPair<QTime, QTime> Calendar::firstInterval(const QDate &date, const QTime &startTime, const QTime &endTime) const { + CalendarDay *day = findDay(date, true); + if (day) { + return day->interval(startTime, endTime); + } + if (m_weekdays) { + if (m_weekdays->state(date) == Map::Working) { + return m_weekdays->interval(date, startTime, endTime); + } + if (m_weekdays->state(date) == Map::NonWorking) { + return QPair<QTime, QTime>(QTime(), QTime()); + } + } + if (m_parent && !m_parent->isDeleted()) { + return m_parent->firstInterval(date, startTime, endTime); + } + return project()->defaultCalendar()->firstInterval(date, startTime, endTime); +} + +QPair<DateTime, DateTime> Calendar::firstInterval(const DateTime &start, const DateTime &end) const { + //kdDebug()<<k_funcinfo<<start.toString()<<" - "<<end.toString()<<endl; + if (!start.isValid()) { + kdWarning()<<k_funcinfo<<"Invalid start time"<<endl; + return QPair<DateTime, DateTime>(DateTime(), DateTime()); + } + if (!end.isValid()) { + kdWarning()<<k_funcinfo<<"Invalid end time"<<endl; + return QPair<DateTime, DateTime>(DateTime(), DateTime()); + } + QTime startTime; + QTime endTime; + QDate date = start.date(); + int i=0; + for (; date <= end.date(); date = date.addDays(1)) { + if (date < end.date()) + endTime = QTime(23, 59, 59, 999); + else + endTime = end.time(); + if (date > start.date()) + startTime = QTime(); + else + startTime = start.time(); + + QPair<QTime, QTime> res = firstInterval(date, startTime, endTime); + if (res.first < res.second) { + return QPair<DateTime, DateTime>(DateTime(date,res.first),DateTime(date, res.second)); + } + } + //kdError()<<k_funcinfo<<"Didn't find an interval ("<<start<<", "<<end<<")"<<endl; + return QPair<DateTime, DateTime>(DateTime(), DateTime()); +} + + +bool Calendar::hasInterval(const QDate &date, const QTime &startTime, const QTime &endTime) const { + CalendarDay *day = findDay(date, true); + if (day) { + //kdDebug()<<k_funcinfo<<m_name<<" "<<date<<": "<<startTime<<" to "<<endTime<<endl; + return day->hasInterval(startTime, endTime); + } + if (m_weekdays) { + if (m_weekdays->state(date) == Map::Working) { + return m_weekdays->hasInterval(date, startTime, endTime); + } else if (m_weekdays->state(date) == Map::NonWorking) { + return false; + } + } + if (m_parent && !m_parent->isDeleted()) { + return m_parent->hasInterval(date, startTime, endTime); + } + return project()->defaultCalendar()->hasInterval(date, startTime, endTime); +} + +bool Calendar::hasInterval(const DateTime &start, const DateTime &end) const { + //kdDebug()<<k_funcinfo<<m_name<<": "<<start<<" - "<<end<<endl; + if (!start.isValid() || !end.isValid() || end <= start) { + //kdError()<<k_funcinfo<<"Invalid input: "<<(start.isValid()?"":"(start invalid) ")<<(end.isValid()?"":"(end invalid) ")<<(start>end?"":"(start<=end)")<<endl; + //kdDebug()<<kdBacktrace(8)<<endl; + return false; + } + QTime startTime; + QTime endTime; + QDate date = start.date(); + for (; date <= end.date(); date = date.addDays(1)) { + if (date < end.date()) + endTime = QTime(23, 59, 59, 999); + else + endTime = end.time(); + if (date > start.date()) + startTime = QTime(); + else + startTime = start.time(); + + if (hasInterval(date, startTime, endTime)) + return true; + } + return false; +} + +DateTime Calendar::firstAvailableAfter(const DateTime &time, const DateTime &limit) { + //kdDebug()<<k_funcinfo<<m_name<<": check from "<<time.toString()<<" limit="<<limit.toString()<<endl; + if (!time.isValid() || !limit.isValid() || time >= limit) { + kdError()<<k_funcinfo<<"Invalid input: "<<(time.isValid()?"":"(time invalid) ")<<(limit.isValid()?"":"(limit invalid) ")<<(time>limit?"":"(time>=limit)")<<endl; + return DateTime(); + } + if (!hasInterval(time, limit)) { + return DateTime(); + } + DateTime t = firstInterval(time, limit).first; + //kdDebug()<<k_funcinfo<<m_name<<": "<<t.toString()<<endl; + return t; +} + +DateTime Calendar::firstAvailableBefore(const DateTime &time, const DateTime &limit) { + //kdDebug()<<k_funcinfo<<m_name<<": check from "<<time.toString()<<" limit="<<limit.toString()<<endl; + if (!time.isValid() || !limit.isValid() || time <= limit) { + kdError()<<k_funcinfo<<"Invalid input: "<<(time.isValid()?"":"(time invalid) ")<<(limit.isValid()?"":"(limit invalid) ")<<(time>limit?"":"(time<=limit)")<<endl; + return DateTime(); + } + DateTime lmt = time; + DateTime t = DateTime(time.date()); // start of first day + if (t == lmt) + t = t.addDays(-1); // in case time == start of day + if (t < limit) + t = limit; // always stop at limit (lower boundary) + DateTime res; + //kdDebug()<<k_funcinfo<<m_name<<": t="<<t<<", "<<lmt<<" limit="<<limit<<endl; + while (!res.isValid() && t >= limit) { + // check intervals for 1 day + DateTime r = firstInterval(t, lmt).second; + res = r; + // Find the last interval + while(r.isValid() && r < lmt) { + r = firstInterval(r, lmt).second; + if (r.isValid()) + res = r; + //kdDebug()<<k_funcinfo<<m_name<<": r="<<r<<", "<<lmt<<" res="<<res<<endl; + } + if (!res.isValid()) { + if (t == limit) { + break; + } + lmt = t; + t = t.addDays(-1); + if (t < limit) { + t = limit; + } + if (t == lmt) + break; + } + } + //kdDebug()<<k_funcinfo<<m_name<<": "<<res<<endl; + return res; +} + +Calendar *Calendar::findCalendar(const QString &id) const { + return (m_project ? m_project->findCalendar(id) : 0); +} + +bool Calendar::removeId(const QString &id) { + return (m_project ? m_project->removeCalendarId(id) : false); +} + +void Calendar::insertId(const QString &id){ + if (m_project) + m_project->insertCalendarId(id, this); +} + +///////////// +StandardWorktime::StandardWorktime() { + init(); +} + +StandardWorktime::StandardWorktime(StandardWorktime *worktime) { + if (worktime) { + m_year = worktime->durationYear(); + m_month = worktime->durationMonth(); + m_week = worktime->durationWeek(); + m_day = worktime->durationDay(); + m_calendar = new Calendar(*(worktime->calendar())); + } else { + init(); + } +} + +StandardWorktime::~StandardWorktime() { + //kdDebug()<<k_funcinfo<<"("<<this<<")"<<endl; +} + +void StandardWorktime::init() { + // Some sane default values + m_year = Duration(0, 1760, 0); + m_month = Duration(0, 176, 0); + m_week = Duration(0, 40, 0); + m_day = Duration(0, 8, 0); + m_calendar = new Calendar; + m_calendar->setName(i18n("Base")); + QPair<QTime, QTime> t = QPair<QTime, QTime>(QTime(8,0,0), QTime(16,0,0)); + for (int i=0; i < 5; ++i) { + m_calendar->weekday(i)->addInterval(t); + m_calendar->weekday(i)->setState(Map::Working); + } + m_calendar->weekday(5)->setState(Map::NonWorking); + m_calendar->weekday(6)->setState(Map::NonWorking); +} + +bool StandardWorktime::load(QDomElement &element) { + //kdDebug()<<k_funcinfo<<endl; + m_year = Duration::fromString(element.attribute("year"), Duration::Format_Hour); + m_month = Duration::fromString(element.attribute("month"), Duration::Format_Hour); + m_week = Duration::fromString(element.attribute("week"), Duration::Format_Hour); + m_day = Duration::fromString(element.attribute("day"), Duration::Format_Hour); + + QDomNodeList list = element.childNodes(); + for (unsigned int i=0; i<list.count(); ++i) { + if (list.item(i).isElement()) { + QDomElement e = list.item(i).toElement(); + if (e.tagName() == "calendar") { + delete m_calendar; + m_calendar = new Calendar; + m_calendar->load(e); + } + } + } + return true; +} + +void StandardWorktime::save(QDomElement &element) const { + //kdDebug()<<k_funcinfo<<endl; + QDomElement me = element.ownerDocument().createElement("standard-worktime"); + element.appendChild(me); + me.setAttribute("year", m_year.toString(Duration::Format_Hour)); + me.setAttribute("month", m_month.toString(Duration::Format_Hour)); + me.setAttribute("week", m_week.toString(Duration::Format_Hour)); + me.setAttribute("day", m_day.toString(Duration::Format_Hour)); + + m_calendar->save(me); +} + +#ifndef NDEBUG +void CalendarDay::printDebug(QCString indent) { + QString s[] = {"None", "Non-working", "Working"}; + kdDebug()<<indent<<" "<<m_date.toString()<<" = "<<s[m_state]<<endl; + if (m_state == Map::Working) { + indent += " "; + QPtrListIterator<QPair<QTime, QTime> > it = m_workingIntervals; + for (; it.current(); ++it) { + kdDebug()<<indent<<" Interval: "<<it.current()->first<<" to "<<it.current()->second<<endl; + } + } + +} +void CalendarWeekdays::printDebug(QCString indent) { + kdDebug()<<indent<<"Weekdays ------"<<endl; + QPtrListIterator<CalendarDay> it = m_weekdays; + for (char c='0'; it.current(); ++it) { + it.current()->printDebug(indent + " Day " + c++ + ": "); + } + +} +void Calendar::printDebug(QCString indent) { + kdDebug()<<indent<<"Calendar "<<m_id<<": '"<<m_name<<"' Deleted="<<m_deleted<<endl; + kdDebug()<<indent<<" Parent: "<<(m_parent ? m_parent->name() : "No parent")<<endl; + m_weekdays->printDebug(indent + " "); + kdDebug()<<indent<<" Days --------"<<endl; + QPtrListIterator<CalendarDay> it = m_days; + for (; it.current(); ++it) { + it.current()->printDebug(indent + " "); + } +} + +void StandardWorktime::printDebug(QCString indent) { + kdDebug()<<indent<<"StandardWorktime "<<endl; + kdDebug()<<indent<<"Year: "<<m_year.toString()<<endl; + kdDebug()<<indent<<"Month: "<<m_month.toString()<<endl; + kdDebug()<<indent<<"Week: "<<m_week.toString()<<endl; + kdDebug()<<indent<<"Day: "<<m_day.toString()<<endl; +} + +#endif + +} //KPlato namespace diff --git a/kplato/kptcalendar.h b/kplato/kptcalendar.h new file mode 100644 index 00000000..807e9a5f --- /dev/null +++ b/kplato/kptcalendar.h @@ -0,0 +1,401 @@ +/* This file is part of the KDE project + Copyright (C) 2003 - 2006 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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 KPTCALENDAR_H +#define KPTCALENDAR_H + +#include "kptmap.h" +#include "kptduration.h" + +#include <qdatetime.h> +#include <qpair.h> +#include <qptrlist.h> + +class QDomElement; +class QDateTime; +class QTime; +class QDate; +class QString; + +namespace KPlato +{ + +class DateTime; +class Project; + +class CalendarDay { + +public: + CalendarDay(); + CalendarDay(int state); + CalendarDay(QDate date, int state=0); + CalendarDay(CalendarDay *day); + ~CalendarDay(); + + bool load(QDomElement &element); + void save(QDomElement &element) const; + + const QPtrList<QPair<QTime, QTime> > &workingIntervals() const { return m_workingIntervals; } + void addInterval(QPair<QTime, QTime> *interval); + void addInterval(QPair<QTime, QTime> interval) { addInterval(new QPair<QTime, QTime>(interval)); } + void clearIntervals() { m_workingIntervals.clear(); } + void setIntervals(QPtrList<QPair<QTime, QTime> > intervals) { + m_workingIntervals.clear(); + m_workingIntervals = intervals; + } + + QTime startOfDay() const; + QTime endOfDay() const; + + QDate date() const { return m_date; } + void setDate(QDate date) { m_date = date; } + int state() const { return m_state; } + void setState(int state) { m_state = state; } + + bool operator==(const CalendarDay *day) const; + bool operator==(const CalendarDay &day) const; + bool operator!=(const CalendarDay *day) const; + bool operator!=(const CalendarDay &day) const; + + /** + * Returns the amount of 'worktime' that can be done on + * this day between the times start and end. + */ + Duration effort(const QTime &start, const QTime &end); + + /** + * Returns the actual 'work interval' for the interval start to end. + * If no 'work interval' exists, returns the interval start, end. + * Use @ref hasInterval() to check if a 'work interval' exists. + */ + QPair<QTime, QTime> interval(const QTime &start, const QTime &end) const; + + bool hasInterval() const; + + /** + * Returns true if at least a part of a 'work interval' exists + * for the interval start to end. + */ + bool hasInterval(const QTime &start, const QTime &end) const; + + Duration duration() const; + + const CalendarDay ©(const CalendarDay &day); + +private: + QDate m_date; //NOTE: inValid if used for weekdays + int m_state; + QPtrList<QPair<QTime, QTime> > m_workingIntervals; + +#ifndef NDEBUG +public: + void printDebug(QCString indent=""); +#endif +}; + +class CalendarWeekdays { + +public: + CalendarWeekdays(); + CalendarWeekdays(CalendarWeekdays *weekdays); + ~CalendarWeekdays(); + + bool load(QDomElement &element); + void save(QDomElement &element) const; + + void addWeekday(CalendarDay *day) { m_weekdays.append(day); } + const QPtrList<CalendarDay> &weekdays() const { return m_weekdays; } + /** + * Returns the pointer to CalendarDay for day or 0 if not defined. + * day is 0..6. + * @param day todo : add a comment + */ + CalendarDay *weekday(int day) const; + CalendarDay *weekday(const QDate &date) const { return weekday(date.dayOfWeek()-1); } + CalendarDay *replace(int weekday, CalendarDay *day) { + CalendarDay *d = m_weekdays.at(weekday); + m_weekdays.replace(weekday, day); + return d; + } + IntMap map(); + + void setWeekday(IntMap::iterator it, int state) { m_weekdays.at(it.key())->setState(state); } + + int state(const QDate &date) const; + int state(int weekday) const; + void setState(int weekday, int state); + + const QPtrList<QPair<QTime, QTime> > &intervals(int weekday) const; + void setIntervals(int weekday, QPtrList<QPair<QTime, QTime> >intervals); + void clearIntervals(int weekday); + + bool operator==(const CalendarWeekdays *weekdays) const; + bool operator!=(const CalendarWeekdays *weekdays) const; + + Duration effort(const QDate &date, const QTime &start, const QTime &end); + + /** + * Returns the actual 'work interval' on the weekday defined by date + * for the interval start to end. + * If no 'work interval' exists, returns the interval start, end. + * Use @ref hasInterval() to check if a 'work interval' exists. + */ + QPair<QTime, QTime> interval(const QDate date, const QTime &start, const QTime &end) const; + /** + * Returns true if at least a part of a 'work interval' exists + * on the weekday defined by date for the interval start to end. + */ + bool hasInterval(const QDate date, const QTime &start, const QTime &end) const; + bool hasInterval() const; + + Duration duration() const; + Duration duration(int weekday) const; + + /// Returns the time when the weekday starts + QTime startOfDay(int weekday) const; + /// Returns the time when the weekday ends + QTime endOfDay(int weekday) const; + + const CalendarWeekdays ©(const CalendarWeekdays &weekdays); + +private: + QPtrList<CalendarDay> m_weekdays; + double m_workHours; + +#ifndef NDEBUG +public: + void printDebug(QCString indent=""); +#endif +}; + +/** + * Calendar defines the working and nonworking days and hours. + * A day can have the three states None (Undefined), NonWorking, or Working. + * A calendar can have a parent calendar that defines the days that are + * undefined in this calendar. If a day is still undefined, it defaults + * to Nonworking. + * A Working day has one or more work intervals to define the work hours. + * + * The definition can consist of two parts: Weekdays and Day. + * Day has highest priority. + * + * A typical calendar hierarchy could include calendars on three levels: + * 1. Definition of normal weekdays and national holidays/vacation days. + * 2. Definition of the company's special workdays/-time and vacation days. + * 3. Definitions for groups of resources/individual resources. + * + */ +class Calendar { + +public: + Calendar(); + Calendar(QString name, Calendar *parent=0); + Calendar(Calendar *calendar); + ~Calendar(); + + QString name() const { return m_name; } + void setName(QString name) { m_name = name; } + + Calendar *parent() const { return m_parent; } + void setParent(Calendar *parent) { m_parent = parent; } + + Project *project() const { return m_project; } + void setProject(Project *project); + + bool isDeleted() const { return m_deleted; } + void setDeleted(bool yes); + + QString id() const { return m_id; } + bool setId(QString id); + void generateId(); + + bool load(QDomElement &element); + void save(QDomElement &element) const; + + /** + * Find the definition for the day date. + * If skipUndefined=true the day is NOT returned if it has state None (Undefined). + */ + CalendarDay *findDay(const QDate &date, bool skipUndefined=false) const; + bool addDay(CalendarDay *day) { return m_days.insert(0, day); } + bool removeDay(CalendarDay *day) { return m_days.removeRef(day); } + CalendarDay *takeDay(CalendarDay *day) { return m_days.take(m_days.find(day)); } + const QPtrList<CalendarDay> &days() const { return m_days; } + + /** + * Returns the state of definition for parents day date in it. + * Also checks the parents recursively. + */ + int parentDayState(const QDate &date) const; + + IntMap weekdaysMap() { return m_weekdays->map(); } + void setWeekday(IntMap::iterator it, int state) { m_weekdays->setWeekday(it, state); } + CalendarWeekdays *weekdays() { return m_weekdays; } + CalendarDay *weekday(int day) const { return m_weekdays->weekday(day); } + + QString parentId() const { return m_parentId; } + void setParentId(QString id) { m_parentId = id; } + + bool hasParent(Calendar *cal); + + /** + * Returns the amount of 'worktime' that can be done on + * the date date between the times start and end. + */ + Duration effort(const QDate &date, const QTime &start, const QTime &end) const; + /** + * Returns the amount of 'worktime' that can be done in the + * interval from start to end + */ + Duration effort(const DateTime &start, const DateTime &end) const; + + /** + * Returns the first 'work interval' for the interval + * starting at start and ending at end. + * If no 'work interval' exists, returns an interval with invalid DateTime. + * You can also use @ref hasInterval() to check if a 'work interval' exists. + */ + QPair<DateTime, DateTime> firstInterval(const DateTime &start, const DateTime &end) const; + + /** + * Returns the first 'work interval' on date for the interval + * starting at start and ending at end. + * If no 'work interval' exists, returns an interval with first==second. + * You can also use @ref hasInterval() to check if a 'work interval' exists. + */ + QPair<QTime, QTime> firstInterval(const QDate &date, const QTime &start, const QTime &end) const; + + /** + * Returns true if at least a part of a 'work interval' exists + * for the interval starting at start and ending at end. + */ + bool hasInterval(const DateTime &start, const DateTime &end) const; + + /** + * Returns true if at least a part of a 'work interval' exists + * for the interval on date, starting at start and ending at end. + */ + bool hasInterval(const QDate &date, const QTime &start, const QTime &end) const; + + /** + * Find the first available time after time before limit. + * Return invalid datetime if not available. + */ + DateTime firstAvailableAfter(const DateTime &time, const DateTime &limit); + /** + * Find the first available time backwards from time. Search until limit. + * Return invalid datetime if not available. + */ + DateTime firstAvailableBefore(const DateTime &time, const DateTime &limit); + + Calendar *findCalendar() const { return findCalendar(m_id); } + Calendar *findCalendar(const QString &id) const; + bool removeId() { return removeId(m_id); } + bool removeId(const QString &id); + void insertId(const QString &id); + +protected: + const Calendar ©(Calendar &calendar); + void init(); + +private: + QString m_name; + Calendar *m_parent; + Project *m_project; + bool m_deleted; + QString m_id; + QString m_parentId; + + QPtrList<CalendarDay> m_days; + CalendarWeekdays *m_weekdays; + +#ifndef NDEBUG +public: + void printDebug(QCString indent=""); +#endif +}; + +class StandardWorktime +{ +public: + StandardWorktime(); + StandardWorktime(StandardWorktime* worktime); + ~StandardWorktime(); + + /// The work time of a normal year. + Duration durationYear() const { return m_year; } + /// The work time of a normal year. + double year() const { return m_year.toDouble(Duration::Unit_h); } + /// Set the work time of a normal year. + void setYear(const Duration year) { m_year = year; } + /// Set the work time of a normal year. + void setYear(double hours) { m_year = Duration((Q_INT64)(hours*60.0*60.0)); } + + /// The work time of a normal month + Duration durationMonth() const { return m_month; } + /// The work time of a normal month + double month() const { return m_month.toDouble(Duration::Unit_h); } + /// Set the work time of a normal month + void setMonth(const Duration month) { m_month = month; } + /// Set the work time of a normal month + void setMonth(double hours) { m_month = Duration((Q_INT64)(hours*60.0*60.0)); } + + /// The work time of a normal week + Duration durationWeek() const { return m_week; } + /// The work time of a normal week + double week() const { return m_week.toDouble(Duration::Unit_h); } + /// Set the work time of a normal week + void setWeek(const Duration week) { m_week = week; } + /// Set the work time of a normal week + void setWeek(double hours) { m_week = Duration((Q_INT64)(hours*60.0*60.0)); } + + /// The work time of a normal day + Duration durationDay() const { return m_day; } + /// The work time of a normal day + double day() const { return m_day.toDouble(Duration::Unit_h); } + /// Set the work time of a normal day + void setDay(const Duration day) { m_day = day; } + /// Set the work time of a normal day + void setDay(double hours) { m_day = Duration((Q_INT64)(hours*60.0*60.0)); } + + bool load(QDomElement &element); + void save(QDomElement &element) const; + + Calendar *calendar() const { return m_calendar; } + +protected: + void init(); + +private: + Duration m_year; + Duration m_month; + Duration m_week; + Duration m_day; + + Calendar *m_calendar; + +#ifndef NDEBUG +public: + void printDebug(QCString indent=""); +#endif +}; + +} //KPlato namespace + +#endif diff --git a/kplato/kptcalendaredit.cc b/kplato/kptcalendaredit.cc new file mode 100644 index 00000000..f9d76316 --- /dev/null +++ b/kplato/kptcalendaredit.cc @@ -0,0 +1,283 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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. +*/ + +#include "kptcalendaredit.h" +#include "kptproject.h" +#include "kptcalendar.h" +#include "kptcalendarpanel.h" +#include "kptmap.h" +#include "intervalitem.h" + +#include <qbuttongroup.h> +#include <qheader.h> +#include <qpushbutton.h> +#include <qradiobutton.h> +#include <qcombobox.h> +#include <qlabel.h> +#include <qtextedit.h> +#include <qlineedit.h> +#include <qdatetimeedit.h> +#include <qdatetime.h> +#include <qtabwidget.h> +#include <qtextbrowser.h> + +#include <klocale.h> + +#include <kabc/addressee.h> +#include <kabc/addresseedialog.h> + +#include <qmap.h> + +#include <kdebug.h> + +namespace KPlato +{ + +CalendarEdit::CalendarEdit (QWidget *parent, const char */*name*/) + : CalendarEditBase(parent), + m_calendar(0) + { + + clear(); + intervalList->header()->setStretchEnabled(true, 0); + intervalList->setShowSortIndicator(true); + intervalList->setSorting(0); + + connect (calendarPanel, SIGNAL(dateChanged(QDate)), SLOT(slotDateSelected(QDate))); + connect (calendarPanel, SIGNAL(weekdaySelected(int)), SLOT(slotWeekdaySelected(int))); + connect(calendarPanel, SIGNAL(selectionCleared()), SLOT(slotSelectionCleared())); + + connect (state, SIGNAL(activated(int)), SLOT(slotStateActivated(int))); + connect (bClear, SIGNAL(clicked()), SLOT(slotClearClicked())); + connect (bAddInterval, SIGNAL(clicked()), SLOT(slotAddIntervalClicked())); + + connect (bApply, SIGNAL(clicked()), SLOT(slotApplyClicked())); +} + +void CalendarEdit::slotStateActivated(int id) { + //kdDebug()<<k_funcinfo<<"id="<<id<<endl; + if (id == 0) { // undefined + startTime->setEnabled(false); + endTime->setEnabled(false); + bClear->setEnabled(false); + bAddInterval->setEnabled(false); + intervalList->setEnabled(false); + bApply->setEnabled(true); + } else if (id == 1) { // non working + startTime->setEnabled(false); + endTime->setEnabled(false); + bClear->setEnabled(false); + bAddInterval->setEnabled(false); + intervalList->setEnabled(false); + bApply->setEnabled(true); + } else if (id == 2) { //working + startTime->setEnabled(true); + endTime->setEnabled(true); + bClear->setEnabled(true); + bAddInterval->setEnabled(true); + intervalList->setEnabled(true); + bApply->setEnabled(intervalList->firstChild()); + } +} + +void CalendarEdit::slotClearClicked() { + //kdDebug()<<k_funcinfo<<endl; + intervalList->clear(); + bApply->setEnabled(false); +} +void CalendarEdit::slotAddIntervalClicked() { + //kdDebug()<<k_funcinfo<<endl; + intervalList->insertItem(new IntervalItem(intervalList, startTime->time(), endTime->time())); + bApply->setEnabled(true); +} + +//NOTE: enum Map::State must match combobox state! +void CalendarEdit::slotApplyClicked() { + //kdDebug()<<k_funcinfo<<"("<<m_calendar<<")"<<endl; + DateMap dates = calendarPanel->selectedDates(); + for(DateMap::iterator it = dates.begin(); it != dates.end(); ++it) { + QDate date = QDate::fromString(it.key(), Qt::ISODate); + //kdDebug()<<k_funcinfo<<"Date: "<<date<<endl; + CalendarDay *calDay = m_calendar->findDay(date); + if (!calDay) { + calDay = new CalendarDay(date); + m_calendar->addDay(calDay); + } + calDay->setState(state->currentItem()); //NOTE!! + calDay->clearIntervals(); + if (calDay->state() == Map::Working) { + for (QListViewItem *item = intervalList->firstChild(); item; item = item->nextSibling()) { + //kdDebug()<<k_funcinfo<<"Adding interval: "<<static_cast<IntervalItem *>(item)->interval().first.toString()<<"-"<<static_cast<IntervalItem *>(item)->interval().second.toString()<<endl; + calDay->addInterval(static_cast<IntervalItem *>(item)->interval()); + } + } + } + + IntMap weekdays = calendarPanel->selectedWeekdays(); + for(IntMap::iterator it = weekdays.begin(); it != weekdays.end(); ++it) { + //kdDebug()<<k_funcinfo<<"weekday="<<it.key()<<endl; + CalendarDay *weekday = m_calendar->weekday(it.key()-1); + weekday->setState(state->currentItem());//NOTE!! + weekday->clearIntervals(); + if (weekday->state() == Map::Working) { + for (QListViewItem *item = intervalList->firstChild(); item; item = item->nextSibling()) { + //kdDebug()<<k_funcinfo<<"Adding interval: "<<static_cast<IntervalItem *>(item)->interval().first.toString()<<"-"<<static_cast<IntervalItem *>(item)->interval().second.toString()<<endl; + weekday->addInterval(static_cast<IntervalItem *>(item)->interval()); + } + } + } + + calendarPanel->markSelected(state->currentItem()); //NOTE!! + emit applyClicked(); + slotCheckAllFieldsFilled(); +} + +void CalendarEdit::slotCheckAllFieldsFilled() { + //kdDebug()<<k_funcinfo<<endl; + if (state->currentItem() == 0 /*undefined*/ || + state->currentItem() == 1 /*Non-working*/|| + (state->currentItem() == 2 /*Working*/ && intervalList->firstChild())) + { + emit obligatedFieldsFilled(true); + } + else if (state->currentItem() == 2 && !intervalList->firstChild()) + { + emit obligatedFieldsFilled(false); + } +} + +void CalendarEdit::setCalendar(Calendar *cal) { + m_calendar = cal; + clear(); + calendarPanel->setCalendar(cal); +} + +void CalendarEdit::clear() { + clearPanel(); + clearEditPart(); +} + +void CalendarEdit::clearPanel() { + calendarPanel->clear(); +} + +void CalendarEdit::clearEditPart() { + day->setEnabled(true); + intervalList->clear(); + intervalList->setEnabled(false); + startTime->setEnabled(false); + startTime->setTime(QTime(8, 0, 0)); //FIXME + endTime->setEnabled(false); + endTime->setTime(QTime(16, 0, 0)); //FIXME + + bAddInterval->setEnabled(false); + bClear->setEnabled(false); + bApply->setEnabled(false); + state->setEnabled(false); +} + +void CalendarEdit::slotDateSelected(QDate date) { + if (m_calendar == 0) + return; + //kdDebug()<<k_funcinfo<<"("<<date.toString()<<")"<<endl; + clearEditPart(); + state->clear(); + state->insertItem(i18n("Undefined")); + state->insertItem(i18n("Non-working")); + state->insertItem(i18n("Working")); + + CalendarDay *calDay = m_calendar->findDay(date); + state->setEnabled(true); + if (calDay) { + QPtrListIterator<QPair<QTime, QTime> > it = calDay->workingIntervals(); + for (; it.current(); ++it) { + IntervalItem *item = new IntervalItem(intervalList, it.current()->first, it.current()->second); + intervalList->insertItem(item); + } + if (calDay->state() == Map::Working) { + //kdDebug()<<k_funcinfo<<"("<<date.toString()<<") is workday"<<endl; + state->setCurrentItem(2); + slotStateActivated(2); + bApply->setEnabled(calDay->workingIntervals().count() > 0); + } else if (calDay->state() == Map::NonWorking){ + //kdDebug()<<k_funcinfo<<"("<<date.toString()<<") is holiday"<<endl; + state->setCurrentItem(1); + slotStateActivated(1); + bApply->setEnabled(true); + } else { + //kdDebug()<<k_funcinfo<<"("<<date.toString()<<")=none"<<endl; + state->setCurrentItem(0); + slotStateActivated(0); + bApply->setEnabled(true); + } + } else { + // default + state->setCurrentItem(0); + slotStateActivated(0); + bApply->setEnabled(true); + } +} + +void CalendarEdit::slotWeekdaySelected(int day_/* 1..7 */) { + if (m_calendar == 0 || day_ < 1 || day_ > 7) { + kdError()<<k_funcinfo<<"No calendar or weekday ("<<day_<<") not defined!"<<endl; + return; + } + //kdDebug()<<k_funcinfo<<"("<<day_<<")"<<endl; + clearEditPart(); + CalendarDay *calDay = m_calendar->weekday(day_-1); // 0..6 + if (!calDay) { + kdError()<<k_funcinfo<<"Weekday ("<<day_<<") not defined!"<<endl; + return; + } + state->clear(); + state->insertItem(i18n("Undefined")); + state->insertItem(i18n("Non-working")); + state->insertItem(i18n("Working")); + QPtrListIterator<QPair<QTime, QTime> > it = calDay->workingIntervals(); + for (; it.current(); ++it) { + IntervalItem *item = new IntervalItem(intervalList, it.current()->first, it.current()->second); + intervalList->insertItem(item); + } + state->setEnabled(true); + if (calDay->state() == Map::Working) { + //kdDebug()<<k_funcinfo<<"("<<day_<<")=workday"<<endl; + state->setCurrentItem(2); + slotStateActivated(2); + bApply->setEnabled(calDay->workingIntervals().count() > 0); + } else if (calDay->state() == Map::NonWorking) { + //kdDebug()<<k_funcinfo<<"("<<day_<<")=Holiday"<<endl; + state->setCurrentItem(1); + slotStateActivated(1); + bApply->setEnabled(true); + } else { + //kdDebug()<<k_funcinfo<<"("<<day_<<")=none"<<endl; + state->setCurrentItem(0); + slotStateActivated(0); + bApply->setEnabled(true); + } +} + +void CalendarEdit::slotSelectionCleared() { + clearEditPart(); +} + +} //KPlato namespace + +#include "kptcalendaredit.moc" diff --git a/kplato/kptcalendaredit.h b/kplato/kptcalendaredit.h new file mode 100644 index 00000000..9089bf0f --- /dev/null +++ b/kplato/kptcalendaredit.h @@ -0,0 +1,66 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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 KPTCALENDAREDIT_H +#define KPTCALENDAREDIT_H + +#include "kptcalendareditbase.h" + +#include <qptrlist.h> + +class QDate; + +namespace KPlato +{ + +class Calendar; + +class CalendarEdit : public CalendarEditBase { + Q_OBJECT +public: + CalendarEdit (QWidget *parent=0, const char *name=0); + + Calendar *getCalendar() { return m_calendar; } + void setCalendar(Calendar *cal); + + void clear(); + void clearPanel(); + void clearEditPart(); + +private slots: + void slotCheckAllFieldsFilled(); + void slotDateSelected(QDate date); + void slotWeekdaySelected(int day); + void slotStateActivated(int id); + void slotClearClicked(); + void slotAddIntervalClicked(); + void slotApplyClicked(); + void slotSelectionCleared(); + +signals: + void obligatedFieldsFilled(bool yes); + void applyClicked(); + +private: + Calendar *m_calendar; +}; + +} //KPlato namespace + +#endif // CALENDAREDIT_H diff --git a/kplato/kptcalendareditbase.cc b/kplato/kptcalendareditbase.cc new file mode 100644 index 00000000..ebdb75ad --- /dev/null +++ b/kplato/kptcalendareditbase.cc @@ -0,0 +1,142 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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. +*/ + +#include <klocale.h> + +#include "kptcalendareditbase.h" + +#include <qvariant.h> +#include <qpushbutton.h> +#include <qgroupbox.h> +#include <kptcalendarpanel.h> +#include <qlabel.h> +#include <qcombobox.h> +#include <qbuttongroup.h> +#include <qheader.h> +#include <qlistview.h> +#include <qdatetimeedit.h> +#include <qlayout.h> +#include <qtooltip.h> +#include <qwhatsthis.h> + +namespace KPlato +{ + +/* + * Constructs a CalendarEditBase as a child of 'parent', with the + * name 'name' and widget flags set to 'f'. + */ +CalendarEditBase::CalendarEditBase( QWidget* parent, const char* name, WFlags fl ) + : QWidget( parent, name, fl ) +{ + if ( !name ) + setName( "CalendarEditBase" ); + CalendarEditBaseLayout = new QHBoxLayout( this, 0, 0, "CalendarEditBaseLayout"); + + groupBox2 = new QGroupBox( this, "groupBox2" ); + groupBox2->setColumnLayout(0, Qt::Vertical ); + groupBox2->layout()->setSpacing( 6 ); + groupBox2->layout()->setMargin( 6 ); + groupBox2Layout = new QGridLayout( groupBox2->layout() ); + groupBox2Layout->setAlignment( Qt::AlignTop ); + + calendarPanel = new CalendarPanel( groupBox2, "calendarPanel" ); + + groupBox2Layout->addWidget( calendarPanel, 1, 0 ); + + day = new QButtonGroup( groupBox2, "day" ); + day->setColumnLayout(0, Qt::Vertical ); + day->layout()->setSpacing( 6 ); + day->layout()->setMargin( 6 ); + dayLayout = new QVBoxLayout( day->layout() ); + dayLayout->setAlignment( Qt::AlignTop ); + + layout8 = new QHBoxLayout( 0, 0, 6, "layout8"); + + state = new QComboBox( FALSE, day, "state" ); + layout8->addWidget( state ); + + bApply = new QPushButton( day, "bApply" ); + layout8->addWidget( bApply ); + dayLayout->addLayout( layout8 ); + + groupBox4 = new QGroupBox( day, "groupBox4" ); + groupBox4->setColumnLayout(0, Qt::Vertical ); + groupBox4->layout()->setSpacing( 6 ); + groupBox4->layout()->setMargin( 6 ); + groupBox4Layout = new QVBoxLayout( groupBox4->layout() ); + groupBox4Layout->setAlignment( Qt::AlignTop ); + + intervalList = new QListView( groupBox4, "intervalList" ); + intervalList->addColumn( tr2i18n( "Work Interval" ) ); + groupBox4Layout->addWidget( intervalList ); + + layout6 = new QHBoxLayout( 0, 0, 6, "layout6"); + + startTime = new QTimeEdit( groupBox4, "startTime" ); + layout6->addWidget( startTime ); + + endTime = new QTimeEdit( groupBox4, "endTime" ); + layout6->addWidget( endTime ); + groupBox4Layout->addLayout( layout6 ); + + layout5 = new QHBoxLayout( 0, 0, 6, "layout5"); + + bClear = new QPushButton( groupBox4, "bClear" ); + layout5->addWidget( bClear ); + + bAddInterval = new QPushButton( groupBox4, "bAddInterval" ); + layout5->addWidget( bAddInterval ); + groupBox4Layout->addLayout( layout5 ); + dayLayout->addWidget( groupBox4 ); + + groupBox2Layout->addWidget( day, 1, 1 ); + CalendarEditBaseLayout->addWidget( groupBox2 ); + languageChange(); + resize( QSize(540, 340).expandedTo(minimumSizeHint()) ); + clearWState( WState_Polished ); +} + +/* + * Destroys the object and frees any allocated resources + */ +CalendarEditBase::~CalendarEditBase() +{ + // no need to delete child widgets, Qt does it all for us +} + +/* + * Sets the strings of the subwidgets using the current + * language. + */ +void CalendarEditBase::languageChange() +{ + setCaption( tr2i18n( "CalendarEditBase" ) ); + groupBox2->setTitle( QString::null ); + day->setTitle( QString::null ); + bApply->setText( tr2i18n( "Apply" ) ); + groupBox4->setTitle( QString::null ); + intervalList->header()->setLabel( 0, tr2i18n( "Work Interval" ) ); + bClear->setText( tr2i18n( "Clear" ) ); + bAddInterval->setText( tr2i18n( "Add Interval" ) ); +} + +} //KPlato namespace + +#include "kptcalendareditbase.moc" diff --git a/kplato/kptcalendareditbase.h b/kplato/kptcalendareditbase.h new file mode 100644 index 00000000..284c0ecd --- /dev/null +++ b/kplato/kptcalendareditbase.h @@ -0,0 +1,80 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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 KPTCALENDAREDITBASE_H +#define KPTCALENDAREDITBASE_H + +#include <qvariant.h> +#include <qwidget.h> + +class QVBoxLayout; +class QHBoxLayout; +class QGridLayout; +class QGroupBox; +class QLabel; +class QComboBox; +class QButtonGroup; +class QPushButton; +class QListView; +class QListViewItem; +class QTimeEdit; + +namespace KPlato +{ + +class CalendarPanel; + +class CalendarEditBase : public QWidget +{ + Q_OBJECT + +public: + CalendarEditBase( QWidget* parent = 0, const char* name = 0, WFlags fl = 0 ); + ~CalendarEditBase(); + + QGroupBox* groupBox2; + CalendarPanel* calendarPanel; + QButtonGroup* day; + QComboBox* state; + QPushButton* bApply; + QGroupBox* groupBox4; + QListView* intervalList; + QTimeEdit* startTime; + QTimeEdit* endTime; + QPushButton* bClear; + QPushButton* bAddInterval; + +protected: + QHBoxLayout* CalendarEditBaseLayout; + QGridLayout* groupBox2Layout; + QHBoxLayout* layout10; + QVBoxLayout* dayLayout; + QHBoxLayout* layout8; + QVBoxLayout* groupBox4Layout; + QHBoxLayout* layout6; + QHBoxLayout* layout5; + +protected slots: + virtual void languageChange(); + +}; + +} //KPlato namespace + +#endif // CALENDAREDITBASE_H diff --git a/kplato/kptcalendarlistdialog.cc b/kplato/kptcalendarlistdialog.cc new file mode 100644 index 00000000..a319c616 --- /dev/null +++ b/kplato/kptcalendarlistdialog.cc @@ -0,0 +1,433 @@ +/* This file is part of the KDE project + Copyright (C) 2004 - 2005 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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. +*/ + +#include "kptcalendarlistdialog.h" +#include "kptproject.h" +#include "kptcalendar.h" +#include "kptcommand.h" +#include "kptpart.h" + +#include <qpushbutton.h> +#include <qcombobox.h> +#include <qheader.h> +#include <qlabel.h> +#include <qtextedit.h> +#include <qlineedit.h> +#include <qdatetimeedit.h> +#include <qdatetime.h> +#include <qtabwidget.h> +#include <qtextbrowser.h> + +#include <klocale.h> + +#include <kabc/addressee.h> +#include <kabc/addresseedialog.h> + +#include <kdebug.h> + +namespace KPlato +{ + +class CalendarListViewItem : public KListViewItem +{ +public: + CalendarListViewItem(CalendarListDialogImpl &pan, QListView *lv, Calendar *cal, Calendar *orig=0) + : KListViewItem(lv, cal->name()), panel(pan) { + + calendar = cal; + original = orig; + state = None; + base = 0; + setRenameEnabled(0, false); + } + ~CalendarListViewItem() { + delete calendar; + } + + enum State { None=0, New=1, Modified=2, Deleted=4 }; + + void setState(State s) { state |= s; } + + Calendar *baseCalendar() { + if (state & Deleted) return 0; + return original ? original : calendar; + } + bool hasBaseCalendar(CalendarListViewItem *item) { + if (!base) return false; + return base == item || base->hasBaseCalendar(item); + } + KMacroCommand *buildCommand(Part *part, Project &p) { + KMacroCommand *macro=0; + if (state & New) { + if (macro == 0) macro = new KMacroCommand(""); + //kdDebug()<<k_funcinfo<<"add: "<<calendar->name()<<" p="<<&p<<endl; + base ? calendar->setParent(base->baseCalendar()) : calendar->setParent(0); + macro->addCommand(new CalendarAddCmd(part, &p, calendar)); + calendar = 0; + } else if (state & Modified) { + //kdDebug()<<k_funcinfo<<"modified: "<<calendar->name()<<endl; + if (original->name() != calendar->name()) { + if (macro == 0) macro = new KMacroCommand(""); + macro->addCommand(new CalendarModifyNameCmd(part, original, calendar->name())); + } + Calendar *c = base ? base->baseCalendar() : 0; + if (c != original->parent()) { + if (macro == 0) macro = new KMacroCommand(""); + macro->addCommand(new CalendarModifyParentCmd(part, original, c)); + //kdDebug()<<k_funcinfo<<"Base modified: "<<c->name()<<endl; + } + + //kdDebug()<<k_funcinfo<<"Check for days deleted: "<<calendar->name()<<endl; + QPtrListIterator<CalendarDay> oit = original->days(); + for (; oit.current(); ++oit) { + if (calendar->findDay(oit.current()->date()) == 0) { + if (macro == 0) macro = new KMacroCommand(""); + macro->addCommand(new CalendarRemoveDayCmd(part, original, oit.current()->date())); + //kdDebug()<<k_funcinfo<<"Removed day"<<endl; + } + } + + //kdDebug()<<k_funcinfo<<"Check for days added or modified: "<<calendar->name()<<endl; + QPtrListIterator<CalendarDay> cit = calendar->days(); + for (; cit.current(); ++cit) { + CalendarDay *day = original->findDay(cit.current()->date()); + if (day == 0) { + if (macro == 0) macro = new KMacroCommand(""); + // added + //kdDebug()<<k_funcinfo<<"Added day"<<endl; + macro->addCommand(new CalendarAddDayCmd(part, original, new CalendarDay(cit.current()))); + } else if (*day != cit.current()) { + if (macro == 0) macro = new KMacroCommand(""); + // modified + //kdDebug()<<k_funcinfo<<"Modified day"<<endl; + macro->addCommand(new CalendarModifyDayCmd(part, original, new CalendarDay(cit.current()))); + } + } + //kdDebug()<<k_funcinfo<<"Check for weekdays modified: "<<calendar->name()<<endl; + CalendarDay *day = 0, *org = 0; + for (int i=0; i < 7; ++i) { + day = calendar->weekdays()->weekday(i); + org = original->weekdays()->weekday(i); + if (day && org) { + if (*org != *day) { + if (macro == 0) macro = new KMacroCommand(""); + //kdDebug()<<k_funcinfo<<"Weekday["<<i<<"] modified"<<endl; + macro->addCommand(new CalendarModifyWeekdayCmd(part, original, i, new CalendarDay(day))); + } + } else if (day) { + // shouldn't happen: hmmm, add day to original?? + kdError()<<k_funcinfo<<"Should always have 7 weekdays"<<endl; + } else if (org) { + // shouldn't happen: set org to default?? + kdError()<<k_funcinfo<<"Should always have 7 weekdays"<<endl; + } + } + } + return macro; + } + + Calendar *calendar; + Calendar *original; + CalendarListViewItem* base; + CalendarListDialogImpl &panel; + QString oldText; + +protected: + virtual void cancelRename(int col) { + //kdDebug()<<k_funcinfo<<endl; + if (col == 0 && oldText.isEmpty()) { + return; + } + panel.renameStopped(this); + KListViewItem::cancelRename(col); + setRenameEnabled(col, false); + } +private: + int state; +}; + +//---------------------------------------------------- +CalendarListDialog::CalendarListDialog(Project &p, QWidget *parent, const char *name) + : KDialogBase( Swallow, i18n("Calendar's Settings"), Ok|Cancel, Ok, parent, name, true, true), + project(p) +{ + //kdDebug()<<k_funcinfo<<&p<<endl; + dia = new CalendarListDialogImpl(p, this); + QPtrList<Calendar> list = p.calendars(); + QPtrListIterator<Calendar> it = list; + for (; it.current(); ++it) { + Calendar *c = new Calendar(it.current()); + //c->setProject(&p); + new CalendarListViewItem(*dia, dia->calendarList, c, it.current()); + } + dia->setBaseCalendars(); + + QListViewItem *f = dia->calendarList->firstChild(); + if (f) { + dia->calendarList->setSelected(f, true); + } + //kdDebug()<<"size="<<size().width()<<"x"<<size().height()<<" hint="<<sizeHint().width()<<"x"<<sizeHint().height()<<endl; + resize(QSize(725, 388).expandedTo(minimumSizeHint())); + + setMainWidget(dia); + enableButtonOK(false); + + connect(dia, SIGNAL(enableButtonOk(bool)), SLOT(enableButtonOK(bool))); +} + +KCommand *CalendarListDialog::buildCommand(Part *part) { + //kdDebug()<<k_funcinfo<<endl; + KMacroCommand *cmd = 0; + QListViewItemIterator cit(dia->calendarList); + for (;cit.current(); ++cit) { + CalendarListViewItem *item = dynamic_cast<CalendarListViewItem *>(cit.current()); + if (item) { + KMacroCommand *c = item->buildCommand(part, project); + if (c != 0) { + if (cmd == 0) cmd = new KMacroCommand(""); + cmd->addCommand(c); + } + } + } + QPtrListIterator<CalendarListViewItem> it = dia->deletedItems(); + for (; it.current(); ++it) { + //kdDebug()<<k_funcinfo<<"deleted: "<<it.current()->calendar->name()<<endl; + if (it.current()->original) { + if (cmd == 0) cmd = new KMacroCommand(""); + cmd->addCommand(new CalendarDeleteCmd(part, it.current()->original)); + } + } + if (cmd) { + cmd->setName(i18n("Modify Calendars")); + } + return cmd; +} + +void CalendarListDialog::slotOk() { + accept(); +} + +//-------------------------------------------------- +CalendarListDialogImpl::CalendarListDialogImpl (Project &p, QWidget *parent) + : CalendarListDialogBase(parent), + project(p), + m_renameItem(0) { + + calendarList->header()->setStretchEnabled(true, 0); + calendarList->setShowSortIndicator(true); + calendarList->setSorting(0); + calendarList->setDefaultRenameAction(QListView::Accept); + + m_deletedItems.setAutoDelete(true); + calendar->setEnabled(false); + + slotSelectionChanged(); + + connect(calendar, SIGNAL(obligatedFieldsFilled(bool)), SLOT(slotEnableButtonOk(bool))); + connect(calendar, SIGNAL(applyClicked()), SLOT(slotCalendarModified())); + + connect(bDelete, SIGNAL(clicked()), SLOT(slotDeleteClicked())); + connect(bAdd, SIGNAL(clicked()), SLOT(slotAddClicked())); + //connect(editName, SIGNAL(returnPressed()), SLOT(slotAddClicked())); + + connect(calendarList, SIGNAL(selectionChanged()), SLOT(slotSelectionChanged())); + connect(calendarList, SIGNAL(doubleClicked(QListViewItem*, const QPoint&, int)), SLOT(slotListDoubleClicked(QListViewItem*, const QPoint&, int))); + connect(calendarList, SIGNAL(itemRenamed(QListViewItem*, int)), SLOT(slotItemRenamed(QListViewItem*, int))); + + connect (baseCalendar, SIGNAL(activated(int)), SLOT(slotBaseCalendarActivated(int))); + + // Internal rename stuff + connect(this, SIGNAL(renameStarted(QListViewItem*, int)), SLOT(slotRenameStarted(QListViewItem*, int))); + connect(this, SIGNAL(startRename(QListViewItem*, int)), SLOT(slotStartRename(QListViewItem*, int))); + connect(this, SIGNAL(selectionChanged()), SLOT(slotSelectionChanged())); +} + +void CalendarListDialogImpl::setBaseCalendars() { + QListViewItemIterator it(calendarList); + for (;it.current(); ++it) { + CalendarListViewItem *item = dynamic_cast<CalendarListViewItem *>(it.current()); + if (item) { + item->base = findItem(item->calendar->parent()); + } + } +} + +void CalendarListDialogImpl::slotEnableButtonOk(bool on) { + emit enableButtonOk(on); +} + +void CalendarListDialogImpl::slotBaseCalendarActivated(int id) { + CalendarListViewItem *item = dynamic_cast<CalendarListViewItem*>(calendarList->selectedItem()); + if (item) { + item->base = baseCalendarList.at(id); + item->setState(CalendarListViewItem::Modified); + slotEnableButtonOk(true); + } else { + kdError()<<k_funcinfo<<"No CalendarListViewItem"<<endl; + } +} + +void CalendarListDialogImpl::slotSelectionChanged() { + //kdDebug()<<k_funcinfo<<endl; + QListViewItem *item = calendarList->selectedItem(); + bDelete->setEnabled((bool)item); + bAdd->setEnabled(true); + slotSelectionChanged(item); +} + +void CalendarListDialogImpl::slotSelectionChanged(QListViewItem *listItem) { + //kdDebug()<<k_funcinfo<<endl; + baseCalendarList.clear(); + baseCalendar->clear(); + baseCalendar->setEnabled(false); + CalendarListViewItem *cal = dynamic_cast<CalendarListViewItem *>(listItem); + if (cal) { + setCalendar(cal->calendar); + baseCalendar->insertItem(i18n("None")); + baseCalendarList.append(0); + int me = 0, i = 0; + QListViewItemIterator it(calendarList); + for (; it.current(); ++it) { + CalendarListViewItem *item = dynamic_cast<CalendarListViewItem*>(it.current()); + if (item && cal != item && !item->hasBaseCalendar(cal)) { + baseCalendar->insertItem(item->text(0)); + baseCalendarList.append(item); + i++; + if (item == cal->base) { + me = i; + //kdDebug()<<k_funcinfo<<"item="<<item<<": cal="<<cal->calendar->name()<<" has parent "<<cal->base->calendar->name()<<endl; + } + } + } + baseCalendar->setCurrentItem(me); + baseCalendar->setEnabled(true); + return; + } + calendar->clear(); +} +void CalendarListDialogImpl::setCalendar(Calendar *cal) { + calendar->setCalendar(cal); + calendar->setEnabled(true); +} + +void CalendarListDialogImpl::slotCalendarModified() { + CalendarListViewItem *item = dynamic_cast<CalendarListViewItem*>(calendarList->currentItem()); + if (item) { + item->setState(CalendarListViewItem::Modified); + //kdDebug()<<k_funcinfo<<"("<<item->calendar<<")"<<endl; + } + emit calendarModified(); +} + +void CalendarListDialogImpl::slotDeleteClicked() { + CalendarListViewItem *item = static_cast<CalendarListViewItem*>(calendarList->selectedItem()); + if (item) { + calendarList->takeItem(item); + item->setState(CalendarListViewItem::Deleted); + m_deletedItems.append(item); + + emit enableButtonOk(true); + } +} + +void CalendarListDialogImpl::slotAddClicked() { + Calendar *cal = new Calendar(); + cal->setProject(&project); + CalendarListViewItem *item = new CalendarListViewItem(*this, calendarList, cal); + item->setState(CalendarListViewItem::New); + + slotListDoubleClicked(item, QPoint(), 0); + +} + +QPtrList<CalendarListViewItem> &CalendarListDialogImpl::deletedItems() { + return m_deletedItems; +} + +CalendarListViewItem *CalendarListDialogImpl::findItem(Calendar *cal) { + if (!cal) + return 0; + QListViewItemIterator it(calendarList); + for (;it.current(); ++it) { + CalendarListViewItem *item = dynamic_cast<CalendarListViewItem *>(it.current()); + if (item && (cal == item->original || cal == item->calendar)) { + //kdDebug()<<k_funcinfo<<"Found: "<<cal->name()<<endl; + return item; + } + } + return 0; +} + +void CalendarListDialogImpl::slotItemRenamed(QListViewItem *itm, int col) { + //kdDebug()<<k_funcinfo<<itm->text(0)<<endl; + itm->setRenameEnabled(col, false); + m_renameItem = 0; + CalendarListViewItem *item = static_cast<CalendarListViewItem*>(itm); + if (item->text(0).isEmpty()) { + item->setText(0, item->oldText); // keep the old name + } + if (item->text(0).isEmpty()) { + // Not allowed + //kdDebug()<<k_funcinfo<<"name empty"<<endl; + emit startRename(item, 0); + return; + } + if (item->text(0) != item->oldText) { + item->setState(CalendarListViewItem::Modified); + item->calendar->setName(item->text(0)); + } + renameStopped(item); + slotEnableButtonOk(true); +} + +// We don't get notified when rename is cancelled, this is called from the item +void CalendarListDialogImpl::renameStopped(QListViewItem */*item*/) { + //kdDebug()<<k_funcinfo<<(item?item->text(0):"")<<endl; + m_renameItem = 0; + emit selectionChanged(); +} + +void CalendarListDialogImpl::slotListDoubleClicked(QListViewItem *item, const QPoint&, int col) { + //kdDebug()<<k_funcinfo<<(item?item->text(0):"")<<endl; + if (m_renameItem) + return; + slotStartRename(item, col); +} + +void CalendarListDialogImpl::slotRenameStarted(QListViewItem */*item*/, int /*col*/) { + //kdDebug()<<k_funcinfo<<(item?item->text(0):"")<<endl; + if (calendarList->isRenaming()) { + bDelete->setEnabled(false); + bAdd->setEnabled(false); + } +} + +void CalendarListDialogImpl::slotStartRename(QListViewItem *item, int col) { + //kdDebug()<<k_funcinfo<<(item?item->text(0):"")<<endl; + static_cast<CalendarListViewItem*>(item)->oldText = item->text(col); + item->setRenameEnabled(col, true); + item->startRename(col); + m_renameItem = item; + + emit renameStarted(item, col); +} + +} //KPlato namespace + +#include "kptcalendarlistdialog.moc" diff --git a/kplato/kptcalendarlistdialog.h b/kplato/kptcalendarlistdialog.h new file mode 100644 index 00000000..0f9bf01d --- /dev/null +++ b/kplato/kptcalendarlistdialog.h @@ -0,0 +1,107 @@ +/* This file is part of the KDE project + Copyright (C) 2004 - 2005 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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 KPTCALENDARLISTDIALOG_H +#define KPTCALENDARLISTDIALOG_H + +#include "kptcalendarlistdialogbase.h" +#include "kptcalendaredit.h" +#include "kptcalendar.h" + +#include <kdialogbase.h> + +#include <qstring.h> + +class KCommand; + +namespace KPlato +{ + +class CalendarListViewItem; +class Project; +class Part; + +class CalendarListDialogImpl : public CalendarListDialogBase { + Q_OBJECT +public: + CalendarListDialogImpl(Project &project, QWidget *parent); + + void setBaseCalendars(); + + Calendar *getCalendar() { return calendar->getCalendar(); } + void setCalendar(Calendar *cal); + + QPtrList<CalendarListViewItem> &deletedItems(); + + void renameStopped(QListViewItem *item); + +public slots: + void slotSelectionChanged(); + void slotSelectionChanged(QListViewItem *listItem); + +private slots: + void slotBaseCalendarActivated(int id); + void slotCalendarModified(); + void slotDeleteClicked(); + void slotAddClicked(); + void slotEnableButtonOk(bool on); + void slotItemRenamed(QListViewItem *item, int col); + void slotListDoubleClicked(QListViewItem *item, const QPoint&, int col); + void slotStartRename(QListViewItem *item, int col); + void slotRenameStarted(QListViewItem *item, int col); + +signals: + void obligatedFieldsFilled(bool yes); + void calendarModified(); + void calendarChanged(); + void enableButtonOk(bool on); + + //internal + void renameStarted(QListViewItem *, int); + void startRename(QListViewItem *item, int col); + void selectionChanged(); + +protected: + CalendarListViewItem *findItem(Calendar *cal); + +private: + Project &project; + QPtrList<CalendarListViewItem> m_deletedItems; + QPtrList<CalendarListViewItem> baseCalendarList; + QListViewItem *m_renameItem; +}; + +class CalendarListDialog : public KDialogBase { + Q_OBJECT +public: + CalendarListDialog(Project &project, QWidget *parent=0, const char *name=0); + + KCommand *buildCommand(Part *part); + +protected slots: + void slotOk(); + +private: + Project &project; + CalendarListDialogImpl *dia; +}; + +} //KPlato namespace + +#endif // CALENDARLISTDIALOG_H diff --git a/kplato/kptcalendarlistdialogbase.cc b/kplato/kptcalendarlistdialogbase.cc new file mode 100644 index 00000000..acac2637 --- /dev/null +++ b/kplato/kptcalendarlistdialogbase.cc @@ -0,0 +1,56 @@ +/* This file is part of the KDE project + Copyright (C) 2004 - 2005 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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. +*/ + +#include "kptcalendarlistdialogbase.h" +#include "kptcalendaredit.h" + +#include <qvariant.h> +#include <qframe.h> +#include <qgroupbox.h> +#include <qheader.h> +#include <qlistview.h> +#include <qlineedit.h> +#include <qpushbutton.h> +#include <qtable.h> +#include <qlayout.h> +#include <qtooltip.h> +#include <qwhatsthis.h> +#include <qlabel.h> +#include <qcombobox.h> + +#include <klocale.h> +#include <kdebug.h> + +namespace KPlato +{ + +CalendarListDialogBase::CalendarListDialogBase( QWidget* parent, const char* name, WFlags fl ) + : CalendarListPanel( parent, name, fl ) +{ + if ( !name ) + setName( "CalendarListDialogBase" ); + + QVBoxLayout *l = new QVBoxLayout(calendarBox); + calendar = new CalendarEdit(calendarBox); + l->addWidget(calendar); +} + + +} //Kplato namespace +#include "kptcalendarlistdialogbase.moc" diff --git a/kplato/kptcalendarlistdialogbase.h b/kplato/kptcalendarlistdialogbase.h new file mode 100644 index 00000000..25119f68 --- /dev/null +++ b/kplato/kptcalendarlistdialogbase.h @@ -0,0 +1,57 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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 KPTCALENDARLISTDIALOGBASE_H +#define KPTCALENDARLISTDIALOGBASE_H + +#include "kptcalendarlistpanel.h" + +#include <qvariant.h> +#include <qwidget.h> + +class QVBoxLayout; +class QHBoxLayout; +class QGridLayout; +class QListView; +class QListViewItem; +class QLineEdit; +class QPushButton; +class QComboBox; +class QLabel; + +namespace KPlato +{ + +class CalendarEdit; + +class CalendarListDialogBase : public CalendarListPanel +{ + Q_OBJECT +public: + CalendarListDialogBase( QWidget* parent = 0, const char* name = 0, WFlags fl = 0 ); + + CalendarEdit* calendar; + + +}; + +} //KPlato namespace + +#endif // CALENDARLISTDIALOGBASE_H diff --git a/kplato/kptcalendarlistpanel.ui b/kplato/kptcalendarlistpanel.ui new file mode 100644 index 00000000..94efb1f4 --- /dev/null +++ b/kplato/kptcalendarlistpanel.ui @@ -0,0 +1,129 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>KPlato::CalendarListPanel</class> +<author>Dag Andersen danders@get2net.dk</author> +<widget class="QWidget"> + <property name="name"> + <cstring>CalendarListPanel</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>413</width> + <height>203</height> + </rect> + </property> + <property name="caption"> + <string>CalendarListPanel</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QLayoutWidget" row="0" column="0"> + <property name="name"> + <cstring>layout1</cstring> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QPushButton" row="1" column="1"> + <property name="name"> + <cstring>bDelete</cstring> + </property> + <property name="text"> + <string>Remove</string> + </property> + </widget> + <widget class="QPushButton" row="1" column="0"> + <property name="name"> + <cstring>bAdd</cstring> + </property> + <property name="text"> + <string>New</string> + </property> + </widget> + <widget class="KListView" row="0" column="0" rowspan="1" colspan="2"> + <column> + <property name="text"> + <string>Calendar Name</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <property name="name"> + <cstring>calendarList</cstring> + </property> + </widget> + </grid> + </widget> + <widget class="QLayoutWidget" row="0" column="1"> + <property name="name"> + <cstring>layout3</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout2</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel2</cstring> + </property> + <property name="text"> + <string>Parent calendar:</string> + </property> + </widget> + <widget class="QComboBox"> + <property name="name"> + <cstring>baseCalendar</cstring> + </property> + </widget> + </hbox> + </widget> + <widget class="QGroupBox"> + <property name="name"> + <cstring>calendarBox</cstring> + </property> + <property name="frameShape"> + <enum>Panel</enum> + </property> + <property name="lineWidth"> + <number>0</number> + </property> + <property name="title"> + <string></string> + </property> + </widget> + </vbox> + </widget> + </grid> +</widget> +<customwidgets> +</customwidgets> +<tabstops> + <tabstop>calendarList</tabstop> + <tabstop>bAdd</tabstop> + <tabstop>bDelete</tabstop> + <tabstop>baseCalendar</tabstop> +</tabstops> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>klistview.h</includehint> +</includehints> +</UI> diff --git a/kplato/kptcalendarpanel.cc b/kplato/kptcalendarpanel.cc new file mode 100644 index 00000000..2090699c --- /dev/null +++ b/kplato/kptcalendarpanel.cc @@ -0,0 +1,603 @@ +/* This file is part of the KDE project + Copyright (C) 1997 Tim D. Gilman (tdgilman@best.org) + (C) 1998-2001 Mirko Boehm (mirko@kde.org) + (C) 2004 Dag Andersen <danders@get2net.dk> + + 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. +*/ + +/* This is based on KDatePicker. */ + +#include "kptcalendarpanel.h" +#include "kptdatetable.h" +#include "kptcalendar.h" + + +#include <kglobal.h> +#include <kapplication.h> +#include <klocale.h> +#include <kcalendarsystem.h> +#include <kiconloader.h> +#include <qframe.h> +#include <qpainter.h> +#include <qdialog.h> +#include <qstyle.h> +#include <qtoolbutton.h> +#include <qtooltip.h> +#include <qfont.h> +#include <klineedit.h> +#include <qvalidator.h> +#include <kdebug.h> +#include <knotifyclient.h> + +namespace KPlato +{ + +class CalendarPanel::CalendarPanelPrivate +{ +public: + CalendarPanelPrivate() : closeButton(0L), selectWeek(0L) {} + + QToolButton *closeButton; + QToolButton *selectWeek; +}; + + +CalendarPanel::CalendarPanel(QWidget *parent, QDate dt, const char *name, WFlags f) + : QFrame(parent,name, f) +{ + init( dt ); +} + +CalendarPanel::CalendarPanel( QWidget *parent, const char *name ) + : QFrame(parent,name) +{ + init( QDate::currentDate() ); +} + +void CalendarPanel::init( const QDate &dt ) +{ + yearForward = new QToolButton(this); + yearBackward = new QToolButton(this); + monthForward = new QToolButton(this); + monthBackward = new QToolButton(this); + selectMonth = new QToolButton(this); + selectYear = new QToolButton(this); + line = new KLineEdit(this); + val = new DateValidator(this); + table = new DateTable(this, dt, "Calendar table", 0); + fontsize = 10; + + d = new CalendarPanelPrivate(); + d->selectWeek = new QToolButton( this ); + + QToolTip::add(yearForward, i18n("Next year")); + QToolTip::add(yearBackward, i18n("Previous year")); + QToolTip::add(monthForward, i18n("Next month")); + QToolTip::add(monthBackward, i18n("Previous month")); + QToolTip::add(d->selectWeek, i18n("Select a week")); + QToolTip::add(selectMonth, i18n("Select a month")); + QToolTip::add(selectYear, i18n("Select a year")); + + // ----- + setFontSize(10); + line->setValidator(val); + line->installEventFilter( this ); + yearForward->setPixmap(BarIcon(QString::fromLatin1("2rightarrow"))); + yearBackward->setPixmap(BarIcon(QString::fromLatin1("2leftarrow"))); + monthForward->setPixmap(BarIcon(QString::fromLatin1("1rightarrow"))); + monthBackward->setPixmap(BarIcon(QString::fromLatin1("1leftarrow"))); + setDate(dt); // set button texts + connect(table, SIGNAL(dateChanged(QDate)), SLOT(dateChangedSlot(QDate))); + connect(table, SIGNAL(tableClicked()), SLOT(tableClickedSlot())); + connect(monthForward, SIGNAL(clicked()), SLOT(monthForwardClicked())); + connect(monthBackward, SIGNAL(clicked()), SLOT(monthBackwardClicked())); + connect(yearForward, SIGNAL(clicked()), SLOT(yearForwardClicked())); + connect(yearBackward, SIGNAL(clicked()), SLOT(yearBackwardClicked())); + connect(d->selectWeek, SIGNAL(clicked()), SLOT(selectWeekClicked())); + connect(selectMonth, SIGNAL(clicked()), SLOT(selectMonthClicked())); + connect(selectYear, SIGNAL(clicked()), SLOT(selectYearClicked())); + connect(line, SIGNAL(returnPressed()), SLOT(lineEnterPressed())); + + connect(table, SIGNAL(weekdaySelected(int)), SLOT(slotWeekdaySelected(int))); + connect(table, SIGNAL(weekSelected(int, int)), SLOT(slotWeekSelected(int, int))); + connect(table, SIGNAL(selectionCleared()), SLOT(slotSelectionCleared())); + + table->setFocus(); +} + +CalendarPanel::~CalendarPanel() +{ + delete d; +} + +bool +CalendarPanel::eventFilter(QObject *o, QEvent *e ) +{ + if ( e->type() == QEvent::KeyPress ) { + QKeyEvent *k = (QKeyEvent *)e; + + if ( (k->key() == Qt::Key_Prior) || + (k->key() == Qt::Key_Next) || + (k->key() == Qt::Key_Up) || + (k->key() == Qt::Key_Down) ) + { + QApplication::sendEvent( table, e ); + table->setFocus(); + return TRUE; // eat event + } + } + return QFrame::eventFilter( o, e ); +} + +void +CalendarPanel::resizeEvent(QResizeEvent*) +{ + QWidget *buttons[] = { + yearBackward, + monthBackward, + selectMonth, + selectYear, + monthForward, + yearForward, + d->closeButton + }; + const int NoOfButtons=sizeof(buttons)/sizeof(buttons[0]); + QSize sizes[NoOfButtons]; + int buttonHeight=0; + int count; + int w=0; + int x=0; + // ----- calculate button row height: + for(count=0; count<NoOfButtons; ++count) { + if ( buttons[count] ) { // closeButton may be 0L + sizes[count]=buttons[count]->sizeHint(); + buttonHeight=QMAX(buttonHeight, sizes[count].height()); + } + else + sizes[count] = QSize(0,0); // closeButton + } + + // ----- calculate size of the month button: + for(count=0; count<NoOfButtons; ++count) { + if(buttons[count]==selectMonth) { + QSize metricBound = style().sizeFromContents(QStyle::CT_ToolButton, selectMonth, maxMonthRect); + sizes[count].setWidth(QMAX(metricBound.width(), maxMonthRect.width()+2*QApplication::style().pixelMetric(QStyle::PM_ButtonMargin))); + } + } + // ----- center buttons + w=0; + for(count=0; count<NoOfButtons; ++count) + { + w +=sizes[count].width(); + } + x = (QMAX(w, width())-w)/2; + + // ----- place the buttons: + for(count=0; count<NoOfButtons; ++count) + { + w=sizes[count].width(); + if ( buttons[count] ) + buttons[count]->setGeometry(x, 0, w, buttonHeight); + x+=w; + } + // ----- place the line edit for direct input: + sizes[0]=line->sizeHint(); + int week_width=d->selectWeek->fontMetrics().width(i18n("Week XX"))+((d->closeButton != 0L) ? 50 : 20); + line->setGeometry(0, height()-sizes[0].height(), width()-week_width, sizes[0].height()); + d->selectWeek->setGeometry(width()-week_width, height()-sizes[0].height(), week_width, sizes[0].height()); + // ----- adjust the table: + table->setGeometry(0, buttonHeight, width(), + height()-buttonHeight-sizes[0].height()); +} + +void +CalendarPanel::dateChangedSlot(QDate date) +{ + //kdDebug() << "CalendarPanel::dateChangedSlot: date changed (" << date.year() << "/" << date.month() << "/" << date.day() << ")." << endl; + line->setText(KGlobal::locale()->formatDate(date, true)); + d->selectWeek->setText(i18n("Week %1").arg(weekOfYear(date))); + selectMonth->setText(KGlobal::locale()->calendar()->monthName(date.month(), false)); + selectYear->setText(date.toString("yyyy")); + emit(dateChanged(date)); +} + +void +CalendarPanel::tableClickedSlot() +{ + //kdDebug() << "CalendarPanel::tableClickedSlot: table clicked." << endl; + emit(dateSelected(table->getDate())); + emit(tableClicked()); +} + +const QDate& +CalendarPanel::getDate() const +{ + return table->getDate(); +} + +const QDate & +CalendarPanel::date() const +{ + return table->getDate(); +} + +bool +CalendarPanel::setDate(const QDate& date) +{ + if(date.isValid()) { + QString temp; + // ----- + table->setDate(date); + d->selectWeek->setText(i18n("Week %1").arg(weekOfYear(date))); + selectMonth->setText(KGlobal::locale()->calendar()->monthName(date.month(), false)); + temp.setNum(date.year()); + selectYear->setText(temp); + line->setText(KGlobal::locale()->formatDate(date, true)); + return true; + } else { + kdDebug() << "CalendarPanel::setDate: refusing to set invalid date." << endl; + return false; + } +} + +void +CalendarPanel::monthForwardClicked() +{ + setDate( table->getDate().addMonths(1) ); +} + +void +CalendarPanel::monthBackwardClicked() +{ + setDate( table->getDate().addMonths(-1) ); +} + +void +CalendarPanel::yearForwardClicked() +{ + setDate( table->getDate().addYears(1) ); +} + +void +CalendarPanel::yearBackwardClicked() +{ + setDate( table->getDate().addYears(-1) ); +} + +void +CalendarPanel::selectWeekClicked() +{ + int week; + PopupFrame* popup = new PopupFrame(this); + DateInternalWeekSelector* picker = new DateInternalWeekSelector(fontsize, popup); + // ----- + picker->resize(picker->sizeHint()); + popup->setMainWidget(picker); + connect(picker, SIGNAL(closeMe(int)), popup, SLOT(close(int))); + picker->setFocus(); + if(popup->exec(d->selectWeek->mapToGlobal(QPoint(0, d->selectWeek->height())))) + { + QDate date; + int year; + // ----- + week=picker->getWeek(); + date=table->getDate(); + year=date.year(); + // ----- find the first selectable day in this week (hacky solution :) + date.setYMD(year, 1, 1); + while (weekOfYear(date)>50) + date=date.addDays(1); + while (weekOfYear(date)<week && (week!=53 || (week==53 && + (weekOfYear(date)!=52 || weekOfYear(date.addDays(1))!=1)))) + date=date.addDays(1); + if (week==53 && weekOfYear(date)==52) + while (weekOfYear(date.addDays(-1))==52) + date=date.addDays(-1); + // ----- set this date + setDate(date); + } else { + KNotifyClient::beep(); + } + delete popup; +} + +void +CalendarPanel::selectMonthClicked() +{ + int month; + PopupFrame* popup = new PopupFrame(this); + DateInternalMonthPicker* picker = new DateInternalMonthPicker(fontsize, popup); + // ----- + picker->resize(picker->sizeHint()); + popup->setMainWidget(picker); + picker->setFocus(); + connect(picker, SIGNAL(closeMe(int)), popup, SLOT(close(int))); + if(popup->exec(selectMonth->mapToGlobal(QPoint(0, selectMonth->height())))) + { + QDate date; + int day; + // ----- + month=picker->getResult(); + date=table->getDate(); + day=date.day(); + // ----- construct a valid date in this month: + date.setYMD(date.year(), month, 1); + date.setYMD(date.year(), month, QMIN(day, date.daysInMonth())); + // ----- set this month + setDate(date); + } else { + KNotifyClient::beep(); + } + delete popup; +} + +void +CalendarPanel::selectYearClicked() +{ + int year; + PopupFrame* popup = new PopupFrame(this); + DateInternalYearSelector* picker = new DateInternalYearSelector(fontsize, popup); + // ----- + picker->resize(picker->sizeHint()); + popup->setMainWidget(picker); + connect(picker, SIGNAL(closeMe(int)), popup, SLOT(close(int))); + picker->setFocus(); + if(popup->exec(selectYear->mapToGlobal(QPoint(0, selectMonth->height())))) + { + QDate date; + int day; + // ----- + year=picker->getYear(); + date=table->getDate(); + day=date.day(); + // ----- construct a valid date in this month: + date.setYMD(year, date.month(), 1); + date.setYMD(year, date.month(), QMIN(day, date.daysInMonth())); + // ----- set this month + setDate(date); + } else { + KNotifyClient::beep(); + } + delete popup; +} + +void +CalendarPanel::setEnabled(bool enable) +{ + QWidget *widgets[]= { + yearForward, yearBackward, monthForward, monthBackward, + selectMonth, selectYear, + line, table, d->selectWeek }; + const int Size=sizeof(widgets)/sizeof(widgets[0]); + int count; + // ----- + for(count=0; count<Size; ++count) + { + widgets[count]->setEnabled(enable); + } + table->setEnabled(enable); +} + +void +CalendarPanel::lineEnterPressed() +{ + QDate temp; + // ----- + if(val->date(line->text(), temp)==QValidator::Acceptable) + { + //kdDebug() << "CalendarPanel::lineEnterPressed: valid date entered." << endl; + emit(dateEntered(temp)); + setDate(temp); + } else { + KNotifyClient::beep(); + //kdDebug() << "CalendarPanel::lineEnterPressed: invalid date entered." << endl; + } +} + +QSize +CalendarPanel::sizeHint() const +{ + QSize tableSize=table->sizeHint(); + QWidget *buttons[]={ + yearBackward, + monthBackward, + selectMonth, + selectYear, + monthForward, + yearForward, + d->closeButton + }; + const int NoOfButtons=sizeof(buttons)/sizeof(buttons[0]); + QSize sizes[NoOfButtons]; + int cx=0, cy=0, count; + // ----- store the size hints: + for(count=0; count<NoOfButtons; ++count) { + if ( buttons[count] ) + sizes[count]=buttons[count]->sizeHint(); + else + sizes[count] = QSize(0,0); + + if(buttons[count]==selectMonth) { + QSize metricBound = style().sizeFromContents(QStyle::CT_ToolButton, selectMonth, maxMonthRect); + cx+=QMAX(metricBound.width(), maxMonthRect.width()+2*QApplication::style().pixelMetric(QStyle::PM_ButtonMargin)); + } else { + cx+=sizes[count].width(); + } + cy=QMAX(sizes[count].height(), cy); + } + // ----- calculate width hint: + cx=QMAX(cx, tableSize.width()); // line edit ignored + // ----- calculate height hint: + cy+=tableSize.height()+line->sizeHint().height(); + return QSize(cx, cy); +} + +void +CalendarPanel::setFontSize(int s) +{ + QWidget *buttons[]= { + // yearBackward, + // monthBackward, + selectMonth, + selectYear, + // monthForward, + // yearForward + }; + const int NoOfButtons=sizeof(buttons)/sizeof(buttons[0]); + int count; + QFont font; + QRect r; + // ----- + fontsize=s; + for(count=0; count<NoOfButtons; ++count) + { + font=buttons[count]->font(); + font.setPointSize(s); + buttons[count]->setFont(font); + } + QFontMetrics metrics(selectMonth->fontMetrics()); + for(int i=1; i <= 12; ++i) + { // maxMonthRect is used by sizeHint() + r=metrics.boundingRect(KGlobal::locale()->calendar()->monthName(i, false)); + maxMonthRect.setWidth(QMAX(r.width(), maxMonthRect.width())); + maxMonthRect.setHeight(QMAX(r.height(), maxMonthRect.height())); + } + table->setFontSize(s); +} + +void +CalendarPanel::setCloseButton( bool enable ) +{ + if ( enable == (d->closeButton != 0L) ) + return; + + if ( enable ) { + d->closeButton = new QToolButton( this ); + QToolTip::add(d->closeButton, i18n("Close")); + d->closeButton->setPixmap( SmallIcon("remove") ); + connect( d->closeButton, SIGNAL( clicked() ), + topLevelWidget(), SLOT( close() ) ); + } + else { + delete d->closeButton; + d->closeButton = 0L; + } + + updateGeometry(); +} + +bool CalendarPanel::hasCloseButton() const +{ + return (d->closeButton != 0L); +} + +int CalendarPanel::weekOfYear(QDate date) +{ + // Calculate ISO 8601 week number (taken from glibc/Gnumeric) + int year, week, wday, jan1wday, nextjan1wday; + QDate jan1date, nextjan1date; + + year=date.year(); + wday=date.dayOfWeek(); + + jan1date=QDate(year,1,1); + jan1wday=jan1date.dayOfWeek(); + + week = (date.dayOfYear()-1 + jan1wday-1)/7 + ((jan1wday-1) == 0 ? 1 : 0); + + /* Does date belong to last week of previous year? */ + if ((week == 0) && (jan1wday > 4 /*THURSDAY*/)) { + QDate tmpdate=QDate(year-1,12,31); + return weekOfYear(tmpdate); + } + + if ((jan1wday <= 4 /*THURSDAY*/) && (jan1wday > 1 /*MONDAY*/)) + week++; + + if (week == 53) { + nextjan1date=QDate(year+1, 1, 1); + nextjan1wday = nextjan1date.dayOfWeek(); + if (nextjan1wday <= 4 /*THURSDAY*/) + week = 1; + } + + return week; +} + +void CalendarPanel::slotWeekdaySelected(int day) { + //kdDebug()<<k_funcinfo<<endl; + emit weekdaySelected(day); +} + +void CalendarPanel::slotWeekSelected(int week, int year) { + //kdDebug()<<k_funcinfo<<endl; + emit weekSelected(week, year); +} + +void CalendarPanel::setCalendar(Calendar *cal) { + //kdDebug()<<k_funcinfo<<endl; + table->clear(); + if (cal) { + table->setMarkedWeekdays(cal->weekdaysMap()); + QPtrListIterator<CalendarDay> it = cal->days(); + //kdDebug()<<k_funcinfo<<"Days="<<it.count()<<endl; + for (; it.current(); ++it) { + if (it.current()->state() != Map::None) { + table->addMarkedDate(it.current()->date(), it.current()->state()); + //kdDebug()<<k_funcinfo<<"Added day: "<<it.current()->date().toString()<<"="<<it.current()->state()<<endl; + } + } + setEnabled(true); + table->setFocus(); + } +} + +DateMap CalendarPanel::selectedDates() { + return table->selectedDates(); +} + +IntMap CalendarPanel::selectedWeekdays() { + return table->selectedWeekdays(); +} + +DateMap CalendarPanel::markedDates() { + return table->markedDates(); +} + +IntMap CalendarPanel::markedWeekdays() { + return table->markedWeekdays(); +} + +void CalendarPanel::clear() { + table->clear(); + setEnabled(false); +} + +void CalendarPanel::markSelected(int state) { + table->markSelected(state); + } + +void CalendarPanel::slotSelectionCleared() { + emit selectionCleared(); + } + +void CalendarPanel::virtual_hook( int /*id*/, void* /*data*/ ) +{ /*BASE::virtual_hook( id, data );*/ } + +} //KPlato namespace + +#include "kptcalendarpanel.moc" diff --git a/kplato/kptcalendarpanel.h b/kplato/kptcalendarpanel.h new file mode 100644 index 00000000..ce57e4af --- /dev/null +++ b/kplato/kptcalendarpanel.h @@ -0,0 +1,232 @@ +/* This file is part of the KDE project + Copyright (C) 1997 Tim D. Gilman (tdgilman@best.org) + (C) 1998-2001 Mirko Boehm (mirko@kde.org) + (C) 2004 Dag Andersen <danders@get2net.dk> + + 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. +*/ + +/* This is based on KDatePicker. */ + +#ifndef KPTCALENDARPANEL_H +#define KPTCALENDARPANEL_H + +#include "kptmap.h" + +#include <qdatetime.h> +#include <qframe.h> + +class QLineEdit; +class QToolButton; + +namespace KPlato +{ + +class DateValidator; +class DateTable; +class Calendar; + +/** + * Provides a widget for calendar input. + * + **/ +class CalendarPanel: public QFrame +{ + Q_OBJECT + Q_PROPERTY( QDate date READ date WRITE setDate) + Q_PROPERTY( bool closeButton READ hasCloseButton WRITE setCloseButton ) + +public: + /** The usual constructor. The given date will be displayed + * initially. + **/ + CalendarPanel(QWidget *parent=0, QDate=QDate::currentDate(), const char *name=0, WFlags f=0); + + /** + * Standard qt widget constructor. The initial date will be the + * current date. + */ + CalendarPanel( QWidget *parent, const char *name ); + + /** + * The destructor. + **/ + virtual ~CalendarPanel(); + + /** The size hint for date pickers. The size hint recommends the + * minimum size of the widget so that all elements may be placed + * without clipping. This sometimes looks ugly, so when using the + * size hint, try adding 28 to each of the reported numbers of + * pixels. + **/ + QSize sizeHint() const; + + QSize minimumSizeHint() const { return sizeHint(); } + /** + * Sets the date. + * + * @returns @p false and does not change anything + * if the date given is invalid. + **/ + bool setDate(const QDate&); + + /** + * Returns the selected date. + * @deprecated + **/ + const QDate& getDate() const; + + /** + * @returns the selected date. + */ + const QDate &date() const; + + /** + * Enables or disables the widget. + **/ + void setEnabled(bool); + + /** + * Sets the font size of the widgets elements. + **/ + void setFontSize(int); + /** + * Returns the font size of the widget elements. + */ + int fontSize() const + { return fontsize; } + + /** + * By calling this method with @p enable = true, CalendarPanel will show + * a little close-button in the upper button-row. Clicking the + * close-button will cause the CalendarPanel's topLevelWidget()'s close() + * method being called. This is mostly useful for toplevel datepickers + * without a window manager decoration. + * @see #hasCloseButton + */ + void setCloseButton( bool enable ); + + /** + * @returns true if a CalendarPanel shows a close-button. + * @see #setCloseButton + */ + bool hasCloseButton() const; + + void setCalendar(Calendar *cal); + + DateMap selectedDates(); + IntMap selectedWeekdays(); + WeekMap selectedWeeks(); + + DateMap markedDates(); + IntMap markedWeekdays(); + WeekMap markedWeeks(); + + void clear(); + + void markSelected(int state); + +protected: + /// to catch move keyEvents when QLineEdit has keyFocus + virtual bool eventFilter(QObject *o, QEvent *e ); + /// the resize event + virtual void resizeEvent(QResizeEvent*); + /// the year forward button + QToolButton *yearForward; + /// the year backward button + QToolButton *yearBackward; + /// the month forward button + QToolButton *monthForward; + /// the month backward button + QToolButton *monthBackward; + /// the button for selecting the month directly + QToolButton *selectMonth; + /// the button for selecting the year directly + QToolButton *selectYear; + /// the line edit to enter the date directly + QLineEdit *line; + /// the validator for the line edit: + DateValidator *val; + /// the date table + DateTable *table; + // the widest month string in pixels: + QSize maxMonthRect; + +protected slots: + void dateChangedSlot(QDate); + void tableClickedSlot(); + void monthForwardClicked(); + void monthBackwardClicked(); + void yearForwardClicked(); + void yearBackwardClicked(); + void selectWeekClicked(); + void selectMonthClicked(); + void selectYearClicked(); + void lineEnterPressed(); + + void slotWeekdaySelected(int day); + void slotWeekSelected(int week, int year); + void slotSelectionCleared(); + +signals: + /** This signal is emitted each time the selected date is changed. + * Usually, this does not mean that the date has been entered, + * since the date also changes, for example, when another month is + * selected. + * @see dateSelected + */ + void dateChanged(QDate); + /** This signal is emitted each time a day has been selected by + * clicking on the table (hitting a day in the current month). + */ + void dateSelected(QDate); + /** This signal is emitted when enter is pressed and a VALID date + * has been entered before into the line edit. Connect to both + * dateEntered() and dateSelected() to receive all events where the + * user really enters a date. + */ + void dateEntered(QDate); + /** This signal is emitted when the day has been selected by + * clicking on it in the table. + */ + void tableClicked(); + + void weekSelected(int week, int year); + void weekdaySelected(int day); + /** + * All selections have been cleared + */ + void selectionCleared(); + +private: + /// the font size for the widget + int fontsize; + + bool m_selectedDays[7]; + +protected: + virtual void virtual_hook( int id, void* data ); +private: + void init( const QDate &dt ); + class CalendarPanelPrivate; + CalendarPanelPrivate *d; + // calculate ISO 8601 week number + int weekOfYear(QDate); +}; + +} //KPlato namespace + +#endif // CALENDARPANEL_H diff --git a/kplato/kptcanvasitem.cc b/kplato/kptcanvasitem.cc new file mode 100644 index 00000000..c2aff602 --- /dev/null +++ b/kplato/kptcanvasitem.cc @@ -0,0 +1,922 @@ +/* This file is part of the KDE project + Copyright (C) 2003 - 2004 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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. +*/ + +#include "kpttask.h" +#include "kptcanvasitem.h" +#include "kptrelation.h" +#include "kptpertcanvas.h" +#include "kptganttview.h" + +#include <klocale.h> +#include <qpainter.h> +#include <qpointarray.h> +#include <qptrlist.h> +#include <qpoint.h> + +#include <kdebug.h> + +namespace KPlato +{ + +PertNodeItem::PertNodeItem( PertCanvas *view, Node &node, int row, int col ) + : QCanvasPolygon(view->canvas()), + m_node(node), + m_row(row), + m_col(col) +{ + m_x = m_y = 0; + m_wgap = view->verticalGap(); + m_hgap = view->horizontalGap(); + m_width = view->itemSize().width(); + m_height = view->itemSize().height(); + + m_name = new QCanvasText(node.name(), view->canvas()); + m_childRelations.setAutoDelete(true); +} + +PertNodeItem::~PertNodeItem() +{ + QCanvasItemList list = canvas()->allItems(); + QCanvasItemList::Iterator it = list.begin(); + for (; it != list.end(); ++it) + { + if ( *it == m_name ) + m_name->hide(); + if ( *it == m_leader ) + m_leader->hide(); + } + hide(); + } + +int PertNodeItem::rtti() const { return RTTI; } +int PertNodeItem::RTTI = 2000; + +void PertNodeItem::setVisible(bool yes) +{ + //kdDebug()<<k_funcinfo<<m_node.name()<<endl; + QCanvasPolygon::setVisible(yes); + QCanvasItemList list = canvas()->allItems(); + QCanvasItemList::Iterator it = list.begin(); + for (; it != list.end(); ++it) + { + if ( *it == m_name ) + m_name->setVisible(yes); + if ( *it == m_leader ) + m_leader->setVisible(yes); + } +} + +void PertNodeItem::move(PertCanvas *view, int row, int col) +{ + //kdDebug()<<k_funcinfo<<endl; + m_row = row; m_col = col; + view->mapNode(this); + + // Now map my children + QPtrListIterator<PertNodeRelation> it(m_childRelations); + for (; it.current(); ++it) + { + view->mapChildNode(this, it.current()->childItem, it.current()->relation->type()); + } + + // now move the item on the canvas + m_x = x(col); m_y = y(row); + m_left = QPoint(m_x, m_y + m_height/2); + m_right = QPoint(m_x + m_width, m_y + m_height/2); + QCanvasPolygon::move(m_x, m_y); + if (m_name) + m_name->move(m_x+5, m_y+2); + + setVisible(true); + //kdDebug()<<k_funcinfo<<m_node.name()<<" moved to row,col=("<<m_row<<","<<m_col<<")"<<endl; +} + +void PertNodeItem::drawShape(QPainter &p) +{ + //QPen pen(pen()); + if (isSelected()) + p.setPen(QPen(Qt::red, 2)); + QPointArray a = poly; + int size = a.size()-1; + for(int i = 0; i < size; ++i) + { + //kdDebug()<<k_funcinfo<<" draw["<<i<<"]: "<<a[i].x()<<","<<a[i].y()<<" to "<<a[i+1].x()<<","<<a[i+1].y()<<endl; + p.drawLine(a[i], a[i+1]); + } + //setPen(pen); +} + +QPoint PertNodeItem::exitPoint(Relation::Type type) const +{ + QPoint ret; + switch(type) + { + case Relation::FinishStart: + case Relation::FinishFinish: + ret = m_right + QPoint(pen().width(), 0); + break; + case Relation::StartStart: + ret = m_left + QPoint(0, 4); + break; + } + return ret; +} + +QPoint PertNodeItem::entryPoint(Relation::Type type) const +{ + QPoint ret; + switch(type) + { + case Relation::FinishStart: + ret = m_left - QPoint(pen().width(), 0); + break; + case Relation::FinishFinish: + ret = m_right - QPoint(pen().width(), 4); + break; + case Relation::StartStart: + ret = m_left - QPoint(pen().width(), 0); + break; + } + return ret; +} + +#ifndef NDEBUG +void PertNodeItem::printDebug( int /*info*/ ) +{ +} +#endif + +//////////////////// PertProjectItem ////////////////////////// + +PertProjectItem::PertProjectItem(PertCanvas *view, Node &node, int row, int col) + : PertNodeItem(view, node, row, col) +{ + //kdDebug()<<k_funcinfo<<"Node="<<node.name()<<" ("<<row<<","<<col<<")"<<endl; + + QPointArray a; + a.putPoints(0, 5, + m_x+6, m_y, m_x+m_width, m_y, m_x+m_width-6, m_y+m_height, m_x, m_y+m_height, m_x+6, m_y); + setPoints(a); + + setPen(QPen(Qt::cyan, 2)); +} + +PertProjectItem::~PertProjectItem() +{ +} + +int PertProjectItem::rtti() const { return RTTI; } +int PertProjectItem::RTTI = 2001; + +#ifndef NDEBUG +void PertProjectItem::printDebug( int /*info*/ ) +{ +} +#endif + + +//////////////////// PertTaskItem ////////////////////////// + +PertTaskItem::PertTaskItem(PertCanvas *view, Node &node, int row, int col) + : PertNodeItem(view, node, row, col) +{ + //kdDebug()<<k_funcinfo<<"Node="<<node.name()<<" ("<<row<<","<<col<<")"<<endl; + QPointArray a; + if (node.type() == Node::Type_Summarytask) + { + a.putPoints(0, 5, m_x+6, m_y, m_x+m_width, m_y, m_x+m_width-6, m_y+m_height, m_x, m_y+m_height, m_x+6, m_y); + setPen(QPen(Qt::cyan, 2)); + } + else + { + a.putPoints(0, 5, m_x, m_y, m_x+m_width, m_y, m_x+m_width, m_y+m_height, m_x, m_y+m_height, m_x, m_y); + setPen(QPen(Qt::green, 2)); + } + setPoints(a); + +} + +PertTaskItem::~PertTaskItem() +{ +} + +int PertTaskItem::rtti() const { return RTTI; } +int PertTaskItem::RTTI = 2002; + + +#ifndef NDEBUG +void PertTaskItem::printDebug( int /*info*/ ) +{ +} +#endif + + +//////////////////// PertMilestoneItem ////////////////////////// + +PertMilestoneItem::PertMilestoneItem(PertCanvas *view, Node &node, int row, int col) + : PertNodeItem(view, node, row, col) +{ + //kdDebug()<<k_funcinfo<<"Node="<<node.name()<<" ("<<row<<","<<col<<")"<<endl; + + QPointArray a; + a.putPoints(0, 7, + m_x, m_y+m_height/2, + m_x+6, m_y, + m_x+m_width-6, m_y, + m_x+m_width, m_y+m_height/2, + m_x+m_width-6, m_y+m_height, + m_x+6, m_y+m_height, + m_x, m_y+m_height/2); + + setPoints(a); + + setPen(QPen(Qt::blue, 2)); +} + +PertMilestoneItem::~PertMilestoneItem() +{ +} + +int PertMilestoneItem::rtti() const { return RTTI; } +int PertMilestoneItem::RTTI = 2003; + +#ifndef NDEBUG +void PertMilestoneItem::printDebug( int /*info*/ ) +{ +} +#endif + + +//////////////////// PertRelationItem ////////////////////////// + +PertRelationItem::PertRelationItem( PertCanvas *view, PertNodeItem *parent, PertNodeItem *child, Relation *rel) + : QCanvasPolygon(view->canvas()), + m_view(view), + m_rel(rel), + m_parentItem(parent), + m_childItem(child) +{ + //kdDebug()<<k_funcinfo<<"Parent="<<parent->node().name()<<" Child="<<child->node().name()<<endl; + draw(); + setVisible(true); +} + +PertRelationItem::~PertRelationItem() +{ + hide(); +} + +int PertRelationItem::rtti() const { return RTTI; } +int PertRelationItem::RTTI = 2020; + +void PertRelationItem::draw() +{ + //kdDebug()<<k_funcinfo<<endl; + // Some "rules": + // a) Relation::FinishStart: child column > parent column + // b) Relation::FinishFinish: child column >= parent column + // c) Relation::StartStart: child column >= parent column + // d) Child row can be >= parent row + + wgap = m_view->verticalGap(); + hgap = m_view->horizontalGap(); + + // could not use ...rect() here, don't know why + parentTop = (int)(m_parentItem->y()); + parentBottom = parentTop + (int)(m_parentItem->height()); + childTop = (int)(m_childItem->y()); + + childRow = m_childItem->row(); + childCol = m_childItem->column(); + parentRow = m_parentItem->row(); + parentCol = m_parentItem->column(); + //kdDebug()<<k_funcinfo<<"Parent="<<m_parentItem->node().name()<<" ("<<parentRow<<","<<parentCol<<") Child="<<m_childItem->node().name()<<" ("<<childRow<<","<<childCol<<")"<<endl; + + switch (type()) + { + case Relation::FinishStart: + setFinishStartPoints(); + break; + case Relation::FinishFinish: + setFinishFinishPoints(); + break; + case Relation::StartStart: + setStartStartPoints(); + break; + } + QPointArray a = poly; + left = right = a[0].x(); + top = bottom = a[0].y(); + for (uint i = 0; i < a.size(); i++) + { + left = QMIN(a[i].x(), left); + top = QMIN(a[i].y(), top); + right = QMAX(a[i].x(), right); + bottom = QMAX(a[i].y(), bottom); + } + top -= 3; + bottom += 3; + + setPen(Qt::black); + setZ(45); + +/*#ifndef NDEBUG + kdDebug()<<" PertNodeRelation from parent: "<<m_rel->parent()->name()<<" to child: "<<m_rel->child()->name()<<endl; + QPointArray pa = poly; + for (int i = 0; i < pa.size(); ++i) + kdDebug()<<" pa["<<i<<"]="<<pa[i].x()<<","<<pa[i].y()<<endl; +#endif*/ + +} + +void PertRelationItem::setFinishStartPoints() +{ + QPoint parentPoint = m_parentItem->exitPoint(Relation::FinishStart); + QPoint childPoint = m_childItem->entryPoint(Relation::FinishStart); + + QPointArray a; + a.putPoints(0, 1, parentPoint.x(), parentPoint.y()); + + if ( parentRow == childRow ) + { + if (parentCol == childCol - 1 || rowFree(parentRow, parentCol+1, childCol-1)) + { + a.putPoints(1, 1, childPoint.x(), childPoint.y()); + } + else // go around below + { + a.putPoints(1, 9, + parentPoint.x()+(wgap/2)-3, parentPoint.y(), // stop short + parentPoint.x()+(wgap/2), parentPoint.y()+3, // right/down + parentPoint.x()+(wgap/2), parentBottom+(hgap/2)-3, // stop short + parentPoint.x()+(wgap/2)+3, parentBottom+(hgap/2), // right/down + childPoint.x()-(wgap/2)-3, parentBottom+(hgap/2), // stop short + childPoint.x()-(wgap/2), parentBottom+(hgap/2)-3, // right/up + childPoint.x()-(wgap/2), childPoint.y()+3, // stop short + childPoint.x()-(wgap/2)+3, childPoint.y(), // right/up + childPoint.x(), childPoint.y()); + } + } + else if ( parentRow > childRow ) + { + if (parentCol == childCol - 1) + { + a.putPoints(1, 5, + parentPoint.x()+(wgap/2)-3, parentPoint.y(), + parentPoint.x()+(wgap/2), parentPoint.y()-3, + parentPoint.x()+wgap/2, childPoint.y()+3, + parentPoint.x()+(wgap/2)+3, childPoint.y(), + childPoint.x(), childPoint.y()); + } + else // go around above + { + a.putPoints(1, 9, + parentPoint.x()+(wgap/2)-3, parentPoint.y(), + parentPoint.x()+(wgap/2), parentPoint.y()-3, + parentPoint.x()+wgap/2, parentTop-(hgap/2)+3, + parentPoint.x()+(wgap/2)+3, parentTop-(hgap/2), + childPoint.x()-(wgap/2)-3, parentTop-hgap/2, + childPoint.x()-(wgap/2), parentTop-(hgap/2)-3, + childPoint.x()-wgap/2, childPoint.y()+3, + childPoint.x()-(wgap/2)+3, childPoint.y(), + childPoint.x(), childPoint.y()); + } + } + else if ( parentRow < childRow ) + { + if (parentCol == childCol - 1) + { + a.putPoints(1, 5, + parentPoint.x()+(wgap/2)-3, parentPoint.y(), + parentPoint.x()+(wgap/2), parentPoint.y()+3, + parentPoint.x()+wgap/2, childPoint.y()-3, + parentPoint.x()+(wgap/2)+3, childPoint.y(), + childPoint.x(), childPoint.y()); + + } + else + { + a.putPoints(1, 9, + parentPoint.x()+(wgap/2)-3, parentPoint.y(), + parentPoint.x()+(wgap/2), parentPoint.y()+3, + parentPoint.x()+wgap/2, childTop-(hgap/2)-3, + parentPoint.x()+(wgap/2)+3, childTop-(hgap/2), + childPoint.x()-(wgap/2)-3, childTop-(hgap/2), + childPoint.x()-(wgap/2), childTop-(hgap/2)+3, + childPoint.x()-wgap/2, childPoint.y()-3, + childPoint.x()-wgap/2+3, childPoint.y(), + childPoint.x(), childPoint.y()); + } + } + setPoints(a); +} + +void PertRelationItem::setFinishFinishPoints() +{ + //kdDebug()<<k_funcinfo<<endl; + QPoint parentPoint = m_parentItem->exitPoint(Relation::FinishFinish); + QPoint childPoint = m_childItem->entryPoint(Relation::FinishFinish); + + QPointArray a; + a.putPoints(0, 1, parentPoint.x(), parentPoint.y()); + + + if ( parentRow >= childRow ) + { + if (parentCol == childCol) + { + a.putPoints(1, 5, + childPoint.x()+(wgap/2)-3, parentPoint.y(), + childPoint.x()+(wgap/2), parentPoint.y()-3, + childPoint.x()+wgap/2, childPoint.y()+3, + childPoint.x()+(wgap/2)-3, childPoint.y(), + childPoint.x(), childPoint.y()); + } + else if (parentCol < childCol) + { + a.putPoints(1, 9, + parentPoint.x()+(wgap/2)-3, parentPoint.y(), // stop short + parentPoint.x()+(wgap/2), parentPoint.y()+3, // right/down + parentPoint.x()+(wgap/2), parentBottom+(hgap/2)-3, // stop short + parentPoint.x()+(wgap/2)+3, parentBottom+(hgap/2), // right/down + childPoint.x()+(wgap/2)-3, parentBottom+(hgap/2), // stop short + childPoint.x()+(wgap/2), parentBottom+(hgap/2)-3, // right/up + childPoint.x()+(wgap/2), childPoint.y()+3, // stop short + childPoint.x()+(wgap/2)-3, childPoint.y(), // left/up + childPoint.x(), childPoint.y()); + } + } + else // parentRow < choldRow + { + if (parentCol == childCol) + { + a.putPoints(1, 5, + parentPoint.x()+(wgap/2)-3, parentPoint.y(), + parentPoint.x()+(wgap/2), parentPoint.y()+3, + parentPoint.x()+wgap/2, childPoint.y()-3, + parentPoint.x()+(wgap/2)-3, childPoint.y(), + childPoint.x(), childPoint.y()); + } + else if (parentCol < childCol) + { + if (rowFree(parentRow, parentCol+1, childCol)) + a.putPoints(1, 5, + childPoint.x()+(wgap/2)-3, parentPoint.y(), + childPoint.x()+(wgap/2), parentPoint.y()+3, + childPoint.x()+(wgap/2), childPoint.y()-3, + childPoint.x()+(wgap/2)-3, childPoint.y(), + childPoint.x(), childPoint.y()); + else + a.putPoints(1, 9, + parentPoint.x()+(wgap/2)-3, parentPoint.y(), + parentPoint.x()+(wgap/2), parentPoint.y()+3, + parentPoint.x()+wgap/2, childTop-(hgap/2)-3, + parentPoint.x()+(wgap/2)+3, childTop-(hgap/2), + childPoint.x()+(wgap/2)-3, childTop-(hgap/2), + childPoint.x()+(wgap/2), childTop-(hgap/2)+3, + childPoint.x()+(wgap/2), childPoint.y()-3, + childPoint.x()+(wgap/2)-3, childPoint.y(), + childPoint.x(), childPoint.y()); + } + else + { + a.putPoints(1, 9, + parentPoint.x()+(wgap/2)-3, parentPoint.y(), + parentPoint.x()+(wgap/2), parentPoint.y()+3, + parentPoint.x()+wgap/2, childTop-(hgap/2)-3, + parentPoint.x()+(wgap/2)+3, childTop-(hgap/2), + childPoint.x()+(wgap/2)-3, childTop-(hgap/2), + childPoint.x()+(wgap/2), childTop-(hgap/2)+3, + childPoint.x()+wgap/2, childPoint.y()-3, + childPoint.x()+wgap/2-3, childPoint.y(), + childPoint.x(), childPoint.y()); + } + } + setPoints(a); +} + +void PertRelationItem::setStartStartPoints() +{ + //kdDebug()<<k_funcinfo<<endl; + QPoint parentPoint = m_parentItem->exitPoint(Relation::StartStart); + QPoint childPoint = m_childItem->entryPoint(Relation::StartStart); + + QPointArray a; + a.putPoints(0, 1, parentPoint.x(), parentPoint.y()); + + if ( parentRow > childRow ) + { + if (parentCol == childCol) // go up + { + a.putPoints(1, 4, + parentPoint.x()-(wgap/2)+3, parentPoint.y(), + parentPoint.x()-(wgap/2), parentPoint.y()-3, + parentPoint.x()-(wgap/2), childPoint.y()+3, + parentPoint.x()-(wgap/2)+3, childPoint.y()); + } + else // go above myself + { + a.putPoints(1, 8, + parentPoint.x()-(wgap/2)+3, parentPoint.y(), + parentPoint.x()-(wgap/2), parentPoint.y()-3, + parentPoint.x()-(wgap/2), parentTop-(hgap/2)+3, + parentPoint.x()-(wgap/2)+3, parentTop-(hgap/2), + childPoint.x()-(wgap/2)-3, parentTop-(hgap/2), + childPoint.x()-(wgap/2), parentTop-(hgap/2)-3, + childPoint.x()-(wgap/2), childPoint.y()+3, + childPoint.x()-(wgap/2)+3, childPoint.y()); + } + } + else // go left/down + { + a.putPoints(1, 2, + parentPoint.x()-(wgap/2)+3, parentPoint.y(), + parentPoint.x()-(wgap/2), parentPoint.y()+3); + + if (parentCol == childCol) + { + a.putPoints(3, 2, + parentPoint.x()-(wgap/2), childPoint.y()-3, + parentPoint.x()-(wgap/2)+3, childPoint.y()); + } + else // go below myself + { + if (parentRow == childRow) // go up + { + a.putPoints(3, 6, + parentPoint.x()-(wgap/2), parentBottom+hgap/2-3, + parentPoint.x()-(wgap/2)+3, parentBottom+hgap/2, + childPoint.x()-(wgap/2)-3, parentBottom+hgap/2, + childPoint.x()-(wgap/2), parentBottom+hgap/2-3, + childPoint.x()-(wgap/2), childPoint.y()+3, + childPoint.x()-(wgap/2)+3, childPoint.y()); + } + else // go down + { + a.putPoints(3, 6, + parentPoint.x()-(wgap/2), childTop-(hgap/2)-3, + parentPoint.x()-(wgap/2)+3, childTop-hgap/2, + childPoint.x()-(wgap/2)-3, childTop-hgap/2, + childPoint.x()-(wgap/2), childTop-(hgap/2)+3, + childPoint.x()-(wgap/2), childPoint.y()-3, + childPoint.x()-(wgap/2)+3, childPoint.y()); + } + } + } + a.putPoints(a.size(), 1, childPoint.x(), childPoint.y()); + setPoints(a); +} + +void PertRelationItem::drawShape(QPainter &p) +{ + //kdDebug()<<k_funcinfo<<" "<<m_rel->parent()->name()<<" to "<<m_rel->child()->name()<<endl; + // cannot use polygon's drawShape() as it doesn't use the pen + setBrush(Qt::NoBrush); + QPointArray a = poly; + int size = a.size()-1; + for(int i = 0; i < size; ++i) + { + //kdDebug()<<k_funcinfo<<" draw["<<i<<"]: "<<a[i].x()<<","<<a[i].y()<<" to "<<a[i+1].x()<<","<<a[i+1].y()<<endl; + p.drawLine(a[i], a[i+1]); + } + // Draw arrow + int pos = a.size()-1; + int xoffset = -3; + if ( pos > 1&& a[pos-1].x() > a[pos].x()) + xoffset = 3; + QPoint pnt(a[pos].x()+xoffset, a[pos].y()-3); + p.drawLine(a[pos], pnt); + pnt.setY(a[pos].y()+3); + p.drawLine(a[pos], pnt); +} + +QPointArray PertRelationItem::areaPoints () const +{ + QPointArray pa(4); + int pw = (pen().width()+1)/2; + if ( pw < 1 ) pw = 1; + if ( pen() == NoPen ) pw = 0; + pa[0] = QPoint(left-pw,top-pw); + pa[1] = pa[0] + QPoint(right-left+pw*2,0); + pa[2] = pa[1] + QPoint(0,bottom-top+pw*2); + pa[3] = pa[0] + QPoint(0,bottom-top+pw*2); +/* kdDebug()<<k_funcinfo<<" areaPoints: "<<m_rel->parent()->name()<<" to "<<m_rel->child()->name()<<endl; + kdDebug()<<" "<<pa[0].x()<<","<<pa[0].y()<<" "<<pa[1].x()<<","<<pa[1].y()<<endl; + kdDebug()<<" "<<pa[2].x()<<","<<pa[2].y()<<" "<<pa[3].x()<<","<<pa[3].y()<<endl;*/ + return pa; +} + +bool PertRelationItem::rowFree(int row, int startCol, int endCol) +{ + QCanvasItemList list = canvas()->allItems(); + QCanvasItemList::Iterator it = list.begin(); + for (; it != list.end(); ++it) + { + if ( (*it)->rtti() == PertProjectItem::RTTI || + (*it)->rtti() == PertTaskItem::RTTI || + (*it)->rtti() == PertMilestoneItem::RTTI ) + { + PertNodeItem *item = (PertNodeItem *)(*it); + if ( item->row() == row ) + { + int col = item->column(); + if (col >= startCol && col <= endCol) + { + //kdDebug()<<k_funcinfo<<"Hit on row,col="<<row<<","<<col<<endl; + return false; + } + } + } + } + return true; +} + +#ifndef NDEBUG +void PertRelationItem::printDebug( int /*info*/ ) +{ +} +#endif + +//////////////////// ItemBase ////////////////////////// +KDGanttViewTaskLink::LinkType ItemBase::kdLinkType(int relationType) { + switch (relationType) { + case Relation::FinishStart: + return KDGanttViewTaskLink::FinishStart; + break; + case Relation::FinishFinish: + return KDGanttViewTaskLink::FinishFinish; + break; + case Relation::StartStart: + return KDGanttViewTaskLink::StartStart; + break; + default: + break; + } + return KDGanttViewTaskLink::None; +} + +//////////////////// GanttViewSummaryItem ////////////////////////// + + +GanttViewSummaryItem::GanttViewSummaryItem(KDGanttView *parent, Node *node) + : KDGanttViewSummaryItem(parent, node->name()), + m_node(node), + m_view(parent) +{ + setExpandable(true); + setOpen(true); +} + +GanttViewSummaryItem::GanttViewSummaryItem(KDGanttViewItem *parent, Node *node) + : KDGanttViewSummaryItem(parent, node->name()), + m_node(node), + m_view(0) +{ + m_drawn = false; + GanttViewSummaryItem *p = dynamic_cast<GanttViewSummaryItem*>(parent); + if (p) + m_view = p->ganttView(); + setExpandable(true); + setOpen(true); +} + +void GanttViewSummaryItem::insertRelations(GanttView *view) +{ + //kdDebug()<<k_funcinfo<<endl; + + QPtrListIterator<Relation> it(m_node->dependChildNodes()); + for (; it.current(); ++it) + { + KDGanttViewItem *child = find(m_view->firstChild(), it.current()->child()); + if (child) + { + KDGanttViewTaskLink *link = new KDGanttViewTaskLink(this, child, kdLinkType(it.current()->type())); + //TODO i18n + QString t = i18n("From: %1").arg(this->listViewText(0)); + t += "\n" + i18n("To: %1").arg(child->listViewText(0)); + if (it.current()->lag() > Duration::zeroDuration) { + t += "\n" + i18n("Lag: %1").arg(it.current()->lag().toString(Duration::Format_i18nDayTime)); + } + link->setTooltipText(t); + view->addTaskLink(link); + } + } +} + +KDGanttViewItem *GanttViewSummaryItem::find(Node *node) +{ + //kdDebug()<<k_funcinfo<<endl; + if (m_node == node) + return this; + + KDGanttViewItem *item = find(firstChild(), node); + if (item) + return item; + + return find(nextSibling(), node); +} + + +KDGanttViewItem *GanttViewSummaryItem::find(KDGanttViewItem *item, Node *node) +{ + if (!item) + return 0; + + if (item->type() == Event) + { + GanttViewEventItem *i = static_cast<GanttViewEventItem *>(item); + return i->find(node); + } + else if (item->type() == Task) + { + GanttViewTaskItem *i = static_cast<GanttViewTaskItem *>(item); + return i->find(node); + } + else if (item->type() == Summary) + { + GanttViewSummaryItem *i = static_cast<GanttViewSummaryItem *>(item); + return i->find(node); + } + return 0; +} + +//////////////////// GanttViewTaskItem ////////////////////////// + + +GanttViewTaskItem::GanttViewTaskItem(KDGanttView *parent, KPlato::Task *task) + : KDGanttViewTaskItem(parent, task->name()), + m_task(task), + m_view(parent) +{ +} + +GanttViewTaskItem::GanttViewTaskItem(KDGanttViewItem *parent, KPlato::Task *task) + : KDGanttViewTaskItem(parent, task->name()), + m_task(task), + m_view() +{ + m_drawn = false; + GanttViewSummaryItem *p = dynamic_cast<GanttViewSummaryItem*>(parent); + if (p) + m_view = p->ganttView(); +} + +void GanttViewTaskItem::insertRelations(GanttView *view) +{ + //kdDebug()<<k_funcinfo<<endl; + + QPtrListIterator<Relation> it(m_task->dependChildNodes()); + for (; it.current(); ++it) + { + KDGanttViewItem *child = find(m_view->firstChild(), it.current()->child()); + if (child) + { + KDGanttViewTaskLink *link = new KDGanttViewTaskLink(this, child, kdLinkType(it.current()->type())); + //TODO i18n + QString t = i18n("From: %1").arg(this->listViewText(0)); + t += "\n" + i18n("To: %1").arg(child->listViewText(0)); + if (it.current()->lag() > Duration::zeroDuration) { + t += "\n" + i18n("Lag: %1").arg(it.current()->lag().toString(Duration::Format_i18nDayTime)); + } + link->setTooltipText(t); + view->addTaskLink(link); + } + } +} + +KDGanttViewItem *GanttViewTaskItem::find(Node *node) +{ + //kdDebug()<<k_funcinfo<<endl; + if (m_task == node) + return this; + + KDGanttViewItem *item = find(firstChild(), node); + if (item) + return item; + + return find(nextSibling(), node); +} + + +KDGanttViewItem *GanttViewTaskItem::find(KDGanttViewItem *item, Node *node) +{ + if (!item) + return 0; + + if (item->type() == Event) + { + GanttViewEventItem *i = static_cast<GanttViewEventItem *>(item); + return i->find(node); + } + else if (item->type() == Task) + { + GanttViewTaskItem *i= static_cast<GanttViewTaskItem *>(item); + return i->find(node); + } + else if (item->type() == Summary) + { + GanttViewSummaryItem *i = static_cast<GanttViewSummaryItem *>(item); + return i->find(node); + } + return 0; // avoid warning +} + +//////////////////// GanttViewEventItem ////////////////////////// + + +GanttViewEventItem::GanttViewEventItem(KDGanttView *parent, KPlato::Task *task) + : KDGanttViewEventItem(parent, task->name()), + m_task(task), + m_view(parent) +{ +} + +GanttViewEventItem::GanttViewEventItem(KDGanttViewItem *parent, KPlato::Task *task) + : KDGanttViewEventItem(parent, task->name()), + m_task(task), + m_view() +{ + m_drawn = false; + GanttViewSummaryItem *p = dynamic_cast<GanttViewSummaryItem*>(parent); + if (p) + m_view = p->ganttView(); +} + + +void GanttViewEventItem::insertRelations(GanttView *view) +{ + //kdDebug()<<k_funcinfo<<endl; + + QPtrListIterator<Relation> it(m_task->dependChildNodes()); + for (; it.current(); ++it) + { + KDGanttViewItem *child = find(m_view->firstChild(), it.current()->child()); + if (child) + { + KDGanttViewTaskLink *link = new KDGanttViewTaskLink(this, child, kdLinkType(it.current()->type())); + + QString t = i18n("From: %1").arg(this->listViewText(0)); + t += "\n" + i18n("To: %1").arg(child->listViewText(0)); + if (it.current()->lag() > Duration::zeroDuration) { + t += "\n" + i18n("Lag: %1").arg(it.current()->lag().toString(Duration::Format_i18nDayTime)); + } + link->setTooltipText(t); + view->addTaskLink(link); + } + } +} + +KDGanttViewItem *GanttViewEventItem::find(Node *node) +{ + //kdDebug()<<k_funcinfo<<endl; + if (m_task == node) + return this; + + KDGanttViewItem *item = find(firstChild(), node); + if (item) + return item; + + return find(nextSibling(), node); +} + + +KDGanttViewItem *GanttViewEventItem::find(KDGanttViewItem *item, Node *node) +{ + if (!item) + return 0; + + if (item->type() == Event) + { + GanttViewEventItem *i = static_cast<GanttViewEventItem *>(item); + return i->find(node); + } + else if (item->type() == Task) + { + GanttViewTaskItem *i = static_cast<GanttViewTaskItem *>(item); + return i->find(node); + } + else if (item->type() == Summary) + { + GanttViewSummaryItem *i = static_cast<GanttViewSummaryItem *>(item); + return i->find(node); + } + return 0; +} + +} //KPlato namespace diff --git a/kplato/kptcanvasitem.h b/kplato/kptcanvasitem.h new file mode 100644 index 00000000..fdd5891e --- /dev/null +++ b/kplato/kptcanvasitem.h @@ -0,0 +1,288 @@ +/* This file is part of the KDE project + Copyright (C) 2003 - 2004 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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 KPTCANVASITEM_H +#define KPTCANVASITEM_H + +#include "kptnode.h" +#include "KDGanttView.h" +#include "KDGanttViewItem.h" +#include "KDGanttViewSummaryItem.h" +#include "KDGanttViewTaskItem.h" +#include "KDGanttViewEventItem.h" + +#include <qcanvas.h> +#include <qrect.h> + +class QPainter; + +namespace KPlato +{ + +class Task; +class Relation; +class PertCanvas; +class PertRelationItem; +class PertNodeItem; +class GanttView; + +class PertNodeItem : public QCanvasPolygon +{ +private: + class PertNodeRelation + { + public: + PertNodeRelation(Relation *r, PertNodeItem *n) { relation = r; childItem = n; } + ~PertNodeRelation() {} + Relation *relation; + PertNodeItem * childItem; + }; + +public: + PertNodeItem( PertCanvas *view, Node &node, int row, int col ); + virtual ~PertNodeItem(); + + virtual int rtti() const; + static int RTTI; + + void setVisible(bool yes); + void move(PertCanvas *view, int row, int col); + + QPoint exitPoint(Relation::Type type) const; + QPoint entryPoint(Relation::Type type) const; + + Node &node() const { return m_node; } + + QRect rect() const { return QRect(m_left, m_right); } + void setRow(int row) { m_row = row; } + int row() const { return m_row; } + void setColumn(int col) { m_col = col; } + int column() const { return m_col; } + int x() const { return m_x; } + int x(int col) const { return m_wgap + col*(m_width+m_wgap); } + int y() const { return m_y; } + int y(int row) const { return m_hgap + row*(m_height+m_hgap); } + int width() const { return m_width; } + int height() const { return m_height; } + + void addChildRelation(Relation *relation, PertNodeItem *node) + { m_childRelations.append(new PertNodeRelation(relation, node)); } + + bool hasParent() { return m_node.numDependParentNodes(); } + bool hasChild() { return m_node.numDependChildNodes(); } + +protected: + void drawShape(QPainter & p); + + int m_wgap; + int m_hgap; + int m_width; + int m_height; + int m_x; + int m_y; + + QPtrList<PertNodeRelation> m_childRelations; + +private: + Node &m_node; + int m_row, m_col; + QPoint m_right; // Entry/exit point + QPoint m_left; // Entry/exit point + QCanvasText *m_name; + QCanvasText *m_leader; + +#ifndef NDEBUG + void printDebug( int ); +#endif + +}; + +class PertProjectItem : public PertNodeItem +{ +public: + PertProjectItem( PertCanvas *view, Node &node, int row=-1, int col=-1 ); + virtual ~PertProjectItem(); + + virtual int rtti() const; + static int RTTI; + +#ifndef NDEBUG + void printDebug( int ); +#endif + +}; + +class PertTaskItem : public PertNodeItem +{ +public: + PertTaskItem( PertCanvas *view, Node &node, int row=-1, int col=-1 ); + virtual ~PertTaskItem(); + + virtual int rtti() const; + static int RTTI; + +#ifndef NDEBUG + void printDebug( int ); +#endif + +}; + +class PertMilestoneItem : public PertNodeItem +{ +public: + PertMilestoneItem( PertCanvas *view, Node &node, int row=-1, int col=-1 ); + virtual ~PertMilestoneItem(); + + virtual int rtti() const; + static int RTTI; + + void draw(); + +#ifndef NDEBUG + void printDebug( int ); +#endif + +}; + +///////////////// PertRelationItem //////////////////// + +class PertRelationItem : public QCanvasPolygon +{ +public: + PertRelationItem(PertCanvas *view, PertNodeItem *parent, PertNodeItem *child, Relation *rel); + virtual ~PertRelationItem(); + + virtual int rtti() const; + static int RTTI; + + Relation::Type type() { return m_rel->type(); } + void draw(); + + void setFinishStartPoints(); + void setFinishFinishPoints(); + void setStartStartPoints(); + QPointArray areaPoints() const; + + bool rowFree(int row, int startCol, int endCol); + +protected: + void drawShape(QPainter &p); + +private: + PertCanvas *m_view; + Relation *m_rel; + PertNodeItem *m_parentItem; + PertNodeItem *m_childItem; + int left, top, right, bottom; + + int parentTop; + int parentBottom; + int childTop; + + int childRow; + int childCol; + int parentRow; + int parentCol; + + int wgap; + int hgap; + + +#ifndef NDEBUG + void printDebug( int ); +#endif + +}; + +class ItemBase +{ +protected: + KDGanttViewTaskLink::LinkType kdLinkType(int relationType); +}; + + +///////////////// GanttViewSummaryItem //////////////////// + +class GanttViewSummaryItem : public KDGanttViewSummaryItem, public ItemBase +{ +public: + GanttViewSummaryItem(KDGanttView *parent, Node *node); + GanttViewSummaryItem(KDGanttViewItem *parent, Node *node); + + Node *getNode() { return m_node; } + void insertRelations(GanttView *view); + KDGanttViewItem *find(Node *node); + KDGanttViewItem *find(KDGanttViewItem *item, Node *node); + KDGanttView *ganttView() const { return m_view; } + bool isDrawn() const { return m_drawn; } + void setDrawn(bool drawn) { m_drawn = drawn; } + +protected: + Node *m_node; // can be Project or Task + KDGanttView *m_view; + bool m_drawn; +}; + +///////////////// GanttViewTaskItem //////////////////// + +class GanttViewTaskItem : public KDGanttViewTaskItem, public ItemBase +{ +public: + GanttViewTaskItem(KDGanttView *parent, KPlato::Task *task); + GanttViewTaskItem(KDGanttViewItem *parent, KPlato::Task *task); + + KPlato::Task *getTask() const { return m_task; } + void insertRelations(GanttView *view); + KDGanttViewItem *find(Node *node); + KDGanttViewItem *find(KDGanttViewItem *item, Node *node); + KDGanttView *ganttView() const { return m_view; } + bool isDrawn() const { return m_drawn; } + void setDrawn(bool drawn) { m_drawn = drawn; } + +protected: + KPlato::Task *m_task; + KDGanttView *m_view; + bool m_drawn; +}; + +///////////////// GanttViewEventItem //////////////////// + +class GanttViewEventItem : public KDGanttViewEventItem, public ItemBase +{ +public: + GanttViewEventItem(KDGanttView *parent, KPlato::Task *task); + GanttViewEventItem(KDGanttViewItem *parent, KPlato::Task *task); + + KPlato::Task *getTask() { return m_task; } + void insertRelations(GanttView *view); + KDGanttViewItem *find(Node *node); + KDGanttViewItem *find(KDGanttViewItem *item, Node *node); + KDGanttView *ganttView() const { return m_view; } + bool isDrawn() const { return m_drawn; } + void setDrawn(bool drawn) { m_drawn = drawn; } + +protected: + KPlato::Task *m_task; + KDGanttView *m_view; + bool m_drawn; +}; + +} //KPlato namespace + +#endif diff --git a/kplato/kptcommand.cc b/kplato/kptcommand.cc new file mode 100644 index 00000000..043dc776 --- /dev/null +++ b/kplato/kptcommand.cc @@ -0,0 +1,1918 @@ +/* This file is part of the KDE project + Copyright (C) 2004, 2005 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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. +*/ + +#include "kptcommand.h" +#include "kptaccount.h" +#include "kptappointment.h" +#include "kptpart.h" +#include "kptproject.h" +#include "kpttask.h" +#include "kptcalendar.h" +#include "kptrelation.h" +#include "kptresource.h" + +#include <kdebug.h> +#include <klocale.h> + +#include <qintdict.h> +#include <qmap.h> + +namespace KPlato +{ + +void NamedCommand::setCommandType(int type) { + if (m_part) + m_part->setCommandType(type); +} + +void NamedCommand::setSchDeleted() { + QMap<Schedule*, bool>::Iterator it; + for (it = m_schedules.begin(); it != m_schedules.end(); ++it) { + kdDebug()<<k_funcinfo<<it.key()->id()<<": "<<it.data()<<endl; + it.key()->setDeleted(it.data()); + } +} +void NamedCommand::setSchDeleted(bool state) { + QMap<Schedule*, bool>::Iterator it; + for (it = m_schedules.begin(); it != m_schedules.end(); ++it) { + kdDebug()<<k_funcinfo<<it.key()->id()<<": "<<state<<endl; + it.key()->setDeleted(state); + } +} +void NamedCommand::setSchScheduled() { +QMap<Schedule*, bool>::Iterator it; + for (it = m_schedules.begin(); it != m_schedules.end(); ++it) { + kdDebug()<<k_funcinfo<<it.key()->id()<<": "<<it.data()<<endl; + it.key()->setScheduled(it.data()); + } +} +void NamedCommand::setSchScheduled(bool state) { + QMap<Schedule*, bool>::Iterator it; + for (it = m_schedules.begin(); it != m_schedules.end(); ++it) { + kdDebug()<<k_funcinfo<<it.key()->id()<<": "<<state<<endl; + it.key()->setScheduled(state); + } +} +void NamedCommand::addSchScheduled(Schedule *sch) { + kdDebug()<<k_funcinfo<<sch->id()<<": "<<sch->isScheduled()<<endl; + m_schedules.insert(sch, sch->isScheduled()); + QPtrListIterator<Appointment> it = sch->appointments(); + for (; it.current(); ++it) { + if (it.current()->node() == sch) { + m_schedules.insert(it.current()->resource(), it.current()->resource()->isScheduled()); + } else if (it.current()->resource() == sch) { + m_schedules.insert(it.current()->node(), it.current()->node()->isScheduled()); + } + } +} +void NamedCommand::addSchDeleted(Schedule *sch) { + kdDebug()<<k_funcinfo<<sch->id()<<": "<<sch->isDeleted()<<endl; + m_schedules.insert(sch, sch->isDeleted()); + QPtrListIterator<Appointment> it = sch->appointments(); + for (; it.current(); ++it) { + if (it.current()->node() == sch) { + m_schedules.insert(it.current()->resource(), it.current()->resource()->isDeleted()); + } else if (it.current()->resource() == sch) { + m_schedules.insert(it.current()->node(), it.current()->node()->isDeleted()); + } + } +} + +//------------------------------------------------- +CalendarAddCmd::CalendarAddCmd(Part *part, Project *project,Calendar *cal, QString name) + : NamedCommand(part, name), + m_project(project), + m_cal(cal), + m_added(false) { + cal->setDeleted(true); + //kdDebug()<<k_funcinfo<<cal->name()<<endl; +} + +void CalendarAddCmd::execute() { + if (!m_added && m_project) { + m_project->addCalendar(m_cal); + m_added = true; + } + m_cal->setDeleted(false); + + setCommandType(0); + //kdDebug()<<k_funcinfo<<m_cal->name()<<" added to: "<<m_project->name()<<endl; +} + +void CalendarAddCmd::unexecute() { + m_cal->setDeleted(true); + + setCommandType(0); + //kdDebug()<<k_funcinfo<<m_cal->name()<<endl; +} + +CalendarDeleteCmd::CalendarDeleteCmd(Part *part, Calendar *cal, QString name) + : NamedCommand(part, name), + m_cal(cal) { + + // TODO check if any resources uses this calendar + if (part) { + QIntDictIterator<Schedule> it = part->getProject().schedules(); + for (; it.current(); ++it) { + addSchScheduled(it.current()); + } + } +} + +void CalendarDeleteCmd::execute() { + m_cal->setDeleted(true); + setSchScheduled(false); + setCommandType(1); +} + +void CalendarDeleteCmd::unexecute() { + m_cal->setDeleted(false); + setSchScheduled(); + setCommandType(0); +} + +CalendarModifyNameCmd::CalendarModifyNameCmd(Part *part, Calendar *cal, QString newvalue, QString name) + : NamedCommand(part, name), + m_cal(cal) { + + m_oldvalue = cal->name(); + m_newvalue = newvalue; + //kdDebug()<<k_funcinfo<<cal->name()<<endl; +} +void CalendarModifyNameCmd::execute() { + m_cal->setName(m_newvalue); + setCommandType(0); + //kdDebug()<<k_funcinfo<<m_cal->name()<<endl; +} +void CalendarModifyNameCmd::unexecute() { + m_cal->setName(m_oldvalue); + setCommandType(0); + //kdDebug()<<k_funcinfo<<m_cal->name()<<endl; +} + +CalendarModifyParentCmd::CalendarModifyParentCmd(Part *part, Calendar *cal, Calendar *newvalue, QString name) + : NamedCommand(part, name), + m_cal(cal) { + + m_oldvalue = cal->parent(); + m_newvalue = newvalue; + //kdDebug()<<k_funcinfo<<cal->name()<<endl; + // TODO check if any resources uses this calendar + if (part) { + QIntDictIterator<Schedule> it = part->getProject().schedules(); + for (; it.current(); ++it) { + addSchScheduled(it.current()); + } + } +} +void CalendarModifyParentCmd::execute() { + m_cal->setParent(m_newvalue); + setSchScheduled(false); + setCommandType(1); +} +void CalendarModifyParentCmd::unexecute() { + m_cal->setParent(m_oldvalue); + setSchScheduled(); + setCommandType(1); +} + +CalendarAddDayCmd::CalendarAddDayCmd(Part *part, Calendar *cal, CalendarDay *newvalue, QString name) + : NamedCommand(part, name), + m_cal(cal), + m_mine(true) { + + m_newvalue = newvalue; + //kdDebug()<<k_funcinfo<<cal->name()<<endl; + // TODO check if any resources uses this calendar + if (part) { + QIntDictIterator<Schedule> it = part->getProject().schedules(); + for (; it.current(); ++it) { + addSchScheduled(it.current()); + } + } +} +CalendarAddDayCmd::~CalendarAddDayCmd() { + //kdDebug()<<k_funcinfo<<endl; + if (m_mine) + delete m_newvalue; +} +void CalendarAddDayCmd::execute() { + //kdDebug()<<k_funcinfo<<m_cal->name()<<endl; + m_cal->addDay(m_newvalue); + m_mine = false; + setSchScheduled(false); + setCommandType(1); +} +void CalendarAddDayCmd::unexecute() { + //kdDebug()<<k_funcinfo<<m_cal->name()<<endl; + m_cal->takeDay(m_newvalue); + m_mine = true; + setSchScheduled(); + setCommandType(1); +} + +CalendarRemoveDayCmd::CalendarRemoveDayCmd(Part *part, Calendar *cal, const QDate &day, QString name) + : NamedCommand(part, name), + m_cal(cal), + m_mine(false) { + + m_value = cal->findDay(day); + //kdDebug()<<k_funcinfo<<cal->name()<<endl; + // TODO check if any resources uses this calendar + if (part) { + QIntDictIterator<Schedule> it = part->getProject().schedules(); + for (; it.current(); ++it) { + addSchScheduled(it.current()); + } + } +} +void CalendarRemoveDayCmd::execute() { + //kdDebug()<<k_funcinfo<<m_cal->name()<<endl; + m_cal->takeDay(m_value); + m_mine = true; + setSchScheduled(false); + setCommandType(1); +} +void CalendarRemoveDayCmd::unexecute() { + //kdDebug()<<k_funcinfo<<m_cal->name()<<endl; + m_cal->addDay(m_value); + m_mine = false; + setSchScheduled(); + setCommandType(1); +} + +CalendarModifyDayCmd::CalendarModifyDayCmd(Part *part, Calendar *cal, CalendarDay *value, QString name) + : NamedCommand(part, name), + m_cal(cal), + m_mine(true) { + + m_newvalue = value; + m_oldvalue = cal->findDay(value->date()); + //kdDebug()<<k_funcinfo<<cal->name()<<" old:("<<m_oldvalue<<") new:("<<m_newvalue<<")"<<endl; + // TODO check if any resources uses this calendar + if (part) { + QIntDictIterator<Schedule> it = part->getProject().schedules(); + for (; it.current(); ++it) { + addSchScheduled(it.current()); + } + } +} +CalendarModifyDayCmd::~CalendarModifyDayCmd() { + //kdDebug()<<k_funcinfo<<endl; + if (m_mine) { + delete m_newvalue; + } else { + delete m_oldvalue; + } +} +void CalendarModifyDayCmd::execute() { + //kdDebug()<<k_funcinfo<<endl; + m_cal->takeDay(m_oldvalue); + m_cal->addDay(m_newvalue); + m_mine = false; + setSchScheduled(false); + setCommandType(1); +} +void CalendarModifyDayCmd::unexecute() { + //kdDebug()<<k_funcinfo<<endl; + m_cal->takeDay(m_newvalue); + m_cal->addDay(m_oldvalue); + m_mine = true; + setSchScheduled(); + setCommandType(1); +} + +CalendarModifyWeekdayCmd::CalendarModifyWeekdayCmd(Part *part, Calendar *cal, int weekday, CalendarDay *value, QString name) + : NamedCommand(part, name), + m_weekday(weekday), + m_cal(cal), + m_mine(true) { + + m_value = value; + kdDebug()<<k_funcinfo<<cal->name()<<" ("<<value<<")"<<endl; + // TODO check if any resources uses this calendar + if (part) { + QIntDictIterator<Schedule> it = part->getProject().schedules(); + for (; it.current(); ++it) { + addSchScheduled(it.current()); + } + } +} +CalendarModifyWeekdayCmd::~CalendarModifyWeekdayCmd() { + kdDebug()<<k_funcinfo<<m_weekday<<": "<<m_value<<endl; + delete m_value; + +} +void CalendarModifyWeekdayCmd::execute() { + m_value = m_cal->weekdays()->replace(m_weekday, m_value); + setSchScheduled(false); + setCommandType(1); +} +void CalendarModifyWeekdayCmd::unexecute() { + m_value = m_cal->weekdays()->replace(m_weekday, m_value); + setSchScheduled(); + setCommandType(1); +} + +NodeDeleteCmd::NodeDeleteCmd(Part *part, Node *node, QString name) + : NamedCommand(part, name), + m_node(node), + m_index(-1) { + + m_parent = node->getParent(); + if (m_parent) + m_index = m_parent->findChildNode(node); + m_mine = false; + m_appointments.setAutoDelete(true); + + m_project = static_cast<Project*>(node->projectNode()); + if (m_project) { + QIntDictIterator<Schedule> it = m_project->schedules(); + for (; it.current(); ++it) { + Schedule *s = node->findSchedule(it.current()->id()); + if (s && s->isScheduled()) { + // Only invalidate schedules this node is part of + addSchScheduled(it.current()); + } + } + } +} +NodeDeleteCmd::~NodeDeleteCmd() { + if (m_mine) + delete m_node; +} +void NodeDeleteCmd::execute() { + if (m_parent && m_project) { + //kdDebug()<<k_funcinfo<<m_node->name()<<" "<<m_index<<endl; + QPtrListIterator<Appointment> it = m_node->appointments(); + for (; it.current(); ++it) { + it.current()->detach(); + m_appointments.append(it.current()); + } + m_project->delTask(m_node); + m_mine = true; + setSchScheduled(false); + setCommandType(1); + } +} +void NodeDeleteCmd::unexecute() { + if (m_parent && m_project) { + //kdDebug()<<k_funcinfo<<m_node->name()<<" "<<m_index<<endl; + m_project->addSubTask(m_node, m_index, m_parent); + Appointment *a; + for (a = m_appointments.first(); a != 0; m_appointments.take()) { + a->attach(); + } + m_mine = false; + setSchScheduled(); + setCommandType(1); + } +} + +TaskAddCmd::TaskAddCmd(Part *part, Project *project, Node *node, Node *after, QString name) + : NamedCommand(part, name), + m_project(project), + m_node(node), + m_after(after), + m_added(false) { + + // set some reasonable defaults for normally calculated values + if (after && after->getParent() && after->getParent() != project) { + node->setStartTime(after->getParent()->startTime()); + node->setEndTime(node->startTime() + node->duration()); + } else { + if (project->constraint() == Node::MustFinishOn) { + node->setEndTime(project->endTime()); + node->setStartTime(node->endTime() - node->duration()); + } else { + node->setStartTime(project->startTime()); + node->setEndTime(node->startTime() + node->duration()); + } + } + node->setEarliestStart(node->startTime()); + node->setLatestFinish(node->endTime()); + node->setWorkStartTime(node->startTime()); + node->setWorkEndTime(node->endTime()); +} +TaskAddCmd::~TaskAddCmd() { + if (!m_added) + delete m_node; +} +void TaskAddCmd::execute() { + //kdDebug()<<k_funcinfo<<m_node->name()<<endl; + m_project->addTask(m_node, m_after); + m_added = true; + + setCommandType(1); +} +void TaskAddCmd::unexecute() { + m_project->delTask(m_node); + m_added = false; + + setCommandType(1); +} + +SubtaskAddCmd::SubtaskAddCmd(Part *part, Project *project, Node *node, Node *parent, QString name) + : NamedCommand(part, name), + m_project(project), + m_node(node), + m_parent(parent), + m_added(false) { + + // set some reasonable defaults for normally calculated values + node->setStartTime(parent->startTime()); + node->setEndTime(node->startTime() + node->duration()); + node->setEarliestStart(node->startTime()); + node->setLatestFinish(node->endTime()); + node->setWorkStartTime(node->startTime()); + node->setWorkEndTime(node->endTime()); +} +SubtaskAddCmd::~SubtaskAddCmd() { + if (!m_added) + delete m_node; +} +void SubtaskAddCmd::execute() { + m_project->addSubTask(m_node, m_parent); + m_added = true; + + setCommandType(1); +} +void SubtaskAddCmd::unexecute() { + m_project->delTask(m_node); + m_added = false; + + setCommandType(1); +} + +NodeModifyNameCmd::NodeModifyNameCmd(Part *part, Node &node, QString nodename, QString name) + : NamedCommand(part, name), + m_node(node), + newName(nodename), + oldName(node.name()) { + +} +void NodeModifyNameCmd::execute() { + m_node.setName(newName); + + setCommandType(0); +} +void NodeModifyNameCmd::unexecute() { + m_node.setName(oldName); + + setCommandType(0); +} + +NodeModifyLeaderCmd::NodeModifyLeaderCmd(Part *part, Node &node, QString leader, QString name) + : NamedCommand(part, name), + m_node(node), + newLeader(leader), + oldLeader(node.leader()) { + +} +void NodeModifyLeaderCmd::execute() { + m_node.setLeader(newLeader); + + setCommandType(0); +} +void NodeModifyLeaderCmd::unexecute() { + m_node.setLeader(oldLeader); + + setCommandType(0); +} + +NodeModifyDescriptionCmd::NodeModifyDescriptionCmd(Part *part, Node &node, QString description, QString name) + : NamedCommand(part, name), + m_node(node), + newDescription(description), + oldDescription(node.description()) { + +} +void NodeModifyDescriptionCmd::execute() { + m_node.setDescription(newDescription); + + setCommandType(0); +} +void NodeModifyDescriptionCmd::unexecute() { + m_node.setDescription(oldDescription); + + setCommandType(0); +} + +NodeModifyConstraintCmd::NodeModifyConstraintCmd(Part *part, Node &node, Node::ConstraintType c, QString name) + : NamedCommand(part, name), + m_node(node), + newConstraint(c), + oldConstraint(static_cast<Node::ConstraintType>(node.constraint())) { + + QIntDictIterator<Schedule> it = node.schedules(); + for (; it.current(); ++it) { + addSchScheduled(it.current()); + } +} +void NodeModifyConstraintCmd::execute() { + m_node.setConstraint(newConstraint); + setSchScheduled(false); + setCommandType(1); +} +void NodeModifyConstraintCmd::unexecute() { + m_node.setConstraint(oldConstraint); + setSchScheduled(); + setCommandType(1); +} + +NodeModifyConstraintStartTimeCmd::NodeModifyConstraintStartTimeCmd(Part *part, Node &node, QDateTime dt, QString name) + : NamedCommand(part, name), + m_node(node), + newTime(dt), + oldTime(node.constraintStartTime()) { + + QIntDictIterator<Schedule> it = node.schedules(); + for (; it.current(); ++it) { + addSchScheduled(it.current()); + } +} +void NodeModifyConstraintStartTimeCmd::execute() { + m_node.setConstraintStartTime(newTime); + setSchScheduled(false); + setCommandType(1); +} +void NodeModifyConstraintStartTimeCmd::unexecute() { + m_node.setConstraintStartTime(oldTime); + setSchScheduled(); + setCommandType(1); +} + +NodeModifyConstraintEndTimeCmd::NodeModifyConstraintEndTimeCmd(Part *part, Node &node, QDateTime dt, QString name) + : NamedCommand(part, name), + m_node(node), + newTime(dt), + oldTime(node.constraintEndTime()) { + + QIntDictIterator<Schedule> it = node.schedules(); + for (; it.current(); ++it) { + addSchScheduled(it.current()); + } +} +void NodeModifyConstraintEndTimeCmd::execute() { + m_node.setConstraintEndTime(newTime); + setSchScheduled(false); + setCommandType(1); +} +void NodeModifyConstraintEndTimeCmd::unexecute() { + m_node.setConstraintEndTime(oldTime); + setSchScheduled(); + setCommandType(1); +} + +NodeModifyStartTimeCmd::NodeModifyStartTimeCmd(Part *part, Node &node, QDateTime dt, QString name) + : NamedCommand(part, name), + m_node(node), + newTime(dt), + oldTime(node.startTime()) { + +} +void NodeModifyStartTimeCmd::execute() { + m_node.setStartTime(newTime); + + setCommandType(1); +} +void NodeModifyStartTimeCmd::unexecute() { + m_node.setStartTime(oldTime); + + setCommandType(1); +} + +NodeModifyEndTimeCmd::NodeModifyEndTimeCmd(Part *part, Node &node, QDateTime dt, QString name) + : NamedCommand(part, name), + m_node(node), + newTime(dt), + oldTime(node.endTime()) { + +} +void NodeModifyEndTimeCmd::execute() { + m_node.setEndTime(newTime); + + setCommandType(1); +} +void NodeModifyEndTimeCmd::unexecute() { + m_node.setEndTime(oldTime); + + setCommandType(1); +} + +NodeModifyIdCmd::NodeModifyIdCmd(Part *part, Node &node, QString id, QString name) + : NamedCommand(part, name), + m_node(node), + newId(id), + oldId(node.id()) { + +} +void NodeModifyIdCmd::execute() { + m_node.setId(newId); + + setCommandType(0); +} +void NodeModifyIdCmd::unexecute() { + m_node.setId(oldId); + + setCommandType(0); +} + +NodeIndentCmd::NodeIndentCmd(Part *part, Node &node, QString name) + : NamedCommand(part, name), + m_node(node), + m_newparent(0), + m_newindex(-1) { + +} +void NodeIndentCmd::execute() { + m_oldparent = m_node.getParent(); + m_oldindex = m_oldparent->findChildNode(&m_node); + Project *p = dynamic_cast<Project *>(m_node.projectNode()); + if (p && p->indentTask(&m_node)) { + m_newparent = m_node.getParent(); + m_newindex = m_newparent->findChildNode(&m_node); + m_node.setParent(m_newparent); + } + + setCommandType(1); +} +void NodeIndentCmd::unexecute() { + if (m_newindex != -1) { + m_newparent->delChildNode(m_newindex, false); + m_oldparent->insertChildNode(m_oldindex, &m_node); + m_node.setParent(m_oldparent); + m_newindex = -1; + } + + setCommandType(1); +} + +NodeUnindentCmd::NodeUnindentCmd(Part *part, Node &node, QString name) + : NamedCommand(part, name), + m_node(node), + m_newparent(0), + m_newindex(-1) { +} +void NodeUnindentCmd::execute() { + m_oldparent = m_node.getParent(); + m_oldindex = m_oldparent->findChildNode(&m_node); + Project *p = dynamic_cast<Project *>(m_node.projectNode()); + if (p && p->unindentTask(&m_node)) { + m_newparent = m_node.getParent(); + m_newindex = m_newparent->findChildNode(&m_node); + m_node.setParent(m_newparent); + } + + setCommandType(1); +} +void NodeUnindentCmd::unexecute() { + if (m_newindex != -1) { + m_newparent->delChildNode(m_newindex, false); + m_oldparent->insertChildNode(m_oldindex, &m_node); + m_node.setParent(m_oldparent); + m_newindex = -1; + } + + setCommandType(1); +} + +NodeMoveUpCmd::NodeMoveUpCmd(Part *part, Node &node, QString name) + : NamedCommand(part, name), + m_node(node), + m_moved(false) { + + m_project = static_cast<Project *>(m_node.projectNode()); +} +void NodeMoveUpCmd::execute() { + if (m_project) { + m_moved = m_project->moveTaskUp(&m_node); + } + + setCommandType(0); +} +void NodeMoveUpCmd::unexecute() { + if (m_project && m_moved) { + m_project->moveTaskDown(&m_node); + } + m_moved = false; + setCommandType(0); +} + +NodeMoveDownCmd::NodeMoveDownCmd(Part *part, Node &node, QString name) + : NamedCommand(part, name), + m_node(node), + m_moved(false) { + + m_project = static_cast<Project *>(m_node.projectNode()); +} +void NodeMoveDownCmd::execute() { + if (m_project) { + m_moved = m_project->moveTaskDown(&m_node); + } + setCommandType(0); +} +void NodeMoveDownCmd::unexecute() { + if (m_project && m_moved) { + m_project->moveTaskUp(&m_node); + } + m_moved = false; + setCommandType(0); +} + +AddRelationCmd::AddRelationCmd(Part *part, Relation *rel, QString name) + : NamedCommand(part, name), + m_rel(rel) { + + m_taken = true; + Node *p = rel->parent()->projectNode(); + if (p) { + QIntDictIterator<Schedule> it = p->schedules(); + for (; it.current(); ++it) { + addSchScheduled(it.current()); + } + } +} +AddRelationCmd::~AddRelationCmd() { + if (m_taken) + delete m_rel; +} +void AddRelationCmd::execute() { + //kdDebug()<<k_funcinfo<<m_rel->parent()<<" to "<<m_rel->child()<<endl; + m_taken = false; + m_rel->parent()->addDependChildNode(m_rel); + m_rel->child()->addDependParentNode(m_rel); + setSchScheduled(false); + setCommandType(1); +} +void AddRelationCmd::unexecute() { + m_taken = true; + m_rel->parent()->takeDependChildNode(m_rel); + m_rel->child()->takeDependParentNode(m_rel); + setSchScheduled(); + setCommandType(1); +} + +DeleteRelationCmd::DeleteRelationCmd(Part *part, Relation *rel, QString name) + : NamedCommand(part, name), + m_rel(rel) { + + m_taken = false; + Node *p = rel->parent()->projectNode(); + if (p) { + QIntDictIterator<Schedule> it = p->schedules(); + for (; it.current(); ++it) { + addSchScheduled(it.current()); + } + } +} +DeleteRelationCmd::~DeleteRelationCmd() { + if (m_taken) + delete m_rel; +} +void DeleteRelationCmd::execute() { + //kdDebug()<<k_funcinfo<<m_rel->parent()<<" to "<<m_rel->child()<<endl; + m_taken = true; + m_rel->parent()->takeDependChildNode(m_rel); + m_rel->child()->takeDependParentNode(m_rel); + setSchScheduled(false); + setCommandType(1); +} +void DeleteRelationCmd::unexecute() { + m_taken = false; + m_rel->parent()->addDependChildNode(m_rel); + m_rel->child()->addDependParentNode(m_rel); + setSchScheduled(); + setCommandType(1); +} + +ModifyRelationTypeCmd::ModifyRelationTypeCmd(Part *part, Relation *rel, Relation::Type type, QString name) + : NamedCommand(part, name), + m_rel(rel), + m_newtype(type) { + + m_oldtype = rel->type(); + Node *p = rel->parent()->projectNode(); + if (p) { + QIntDictIterator<Schedule> it = p->schedules(); + for (; it.current(); ++it) { + addSchScheduled(it.current()); + } + } +} +void ModifyRelationTypeCmd::execute() { + m_rel->setType(m_newtype); + setSchScheduled(false); + setCommandType(1); +} +void ModifyRelationTypeCmd::unexecute() { + m_rel->setType(m_oldtype); + setSchScheduled(); + setCommandType(1); +} + +ModifyRelationLagCmd::ModifyRelationLagCmd(Part *part, Relation *rel, Duration lag, QString name) + : NamedCommand(part, name), + m_rel(rel), + m_newlag(lag) { + + m_oldlag = rel->lag(); + Node *p = rel->parent()->projectNode(); + if (p) { + QIntDictIterator<Schedule> it = p->schedules(); + for (; it.current(); ++it) { + addSchScheduled(it.current()); + } + } +} +void ModifyRelationLagCmd::execute() { + m_rel->setLag(m_newlag); + setSchScheduled(false); + setCommandType(1); +} +void ModifyRelationLagCmd::unexecute() { + m_rel->setLag(m_oldlag); + setSchScheduled(); + setCommandType(1); +} + +AddResourceRequestCmd::AddResourceRequestCmd(Part *part, ResourceGroupRequest *group, ResourceRequest *request, QString name) + : NamedCommand(part, name), + m_group(group), + m_request(request) { + + m_mine = true; +} +AddResourceRequestCmd::~AddResourceRequestCmd() { + if (m_mine) + delete m_request; +} +void AddResourceRequestCmd::execute() { + //kdDebug()<<k_funcinfo<<"group="<<m_group<<" req="<<m_request<<endl; + m_group->addResourceRequest(m_request); + m_mine = false; + setSchScheduled(false); + setCommandType(1); +} +void AddResourceRequestCmd::unexecute() { + //kdDebug()<<k_funcinfo<<"group="<<m_group<<" req="<<m_request<<endl; + m_group->takeResourceRequest(m_request); + m_mine = true; + setSchScheduled(); + setCommandType(1); +} + +RemoveResourceRequestCmd::RemoveResourceRequestCmd(Part *part, ResourceGroupRequest *group, ResourceRequest *request, QString name) + : NamedCommand(part, name), + m_group(group), + m_request(request) { + + m_mine = false; + //kdDebug()<<k_funcinfo<<"group="<<group<<" req="<<request<<endl; + Task *t = request->task(); + if (t) { // safety, something is seriously wrong! + QIntDictIterator<Schedule> it = t->schedules(); + for (; it.current(); ++it) { + addSchScheduled(it.current()); + } + } +} +RemoveResourceRequestCmd::~RemoveResourceRequestCmd() { + if (m_mine) + delete m_request; +} +void RemoveResourceRequestCmd::execute() { + m_group->takeResourceRequest(m_request); + m_mine = true; + setSchScheduled(false); + setCommandType(1); +} +void RemoveResourceRequestCmd::unexecute() { + m_group->addResourceRequest(m_request); + m_mine = false; + setSchScheduled(); + setCommandType(1); +} + +ModifyEffortCmd::ModifyEffortCmd(Part *part, Node &node, Duration oldvalue, Duration newvalue, QString name) + : NamedCommand(part, name), + m_effort(node.effort()), + m_oldvalue(oldvalue), + m_newvalue(newvalue) { + + QIntDictIterator<Schedule> it = node.schedules(); + for (; it.current(); ++it) { + addSchScheduled(it.current()); + } +} +void ModifyEffortCmd::execute() { + m_effort->set(m_newvalue); + setSchScheduled(false); + setCommandType(1); +} +void ModifyEffortCmd::unexecute() { + m_effort->set(m_oldvalue); + setSchScheduled(); + setCommandType(1); +} + +EffortModifyOptimisticRatioCmd::EffortModifyOptimisticRatioCmd(Part *part, Node &node, int oldvalue, int newvalue, QString name) + : NamedCommand(part, name), + m_effort(node.effort()), + m_oldvalue(oldvalue), + m_newvalue(newvalue) { + + QIntDictIterator<Schedule> it = node.schedules(); + for (; it.current(); ++it) { + addSchScheduled(it.current()); + } +} +void EffortModifyOptimisticRatioCmd::execute() { + m_effort->setOptimisticRatio(m_newvalue); + setSchScheduled(false); + setCommandType(1); +} +void EffortModifyOptimisticRatioCmd::unexecute() { + m_effort->setOptimisticRatio(m_oldvalue); + setSchScheduled(); + setCommandType(1); +} + +EffortModifyPessimisticRatioCmd::EffortModifyPessimisticRatioCmd(Part *part, Node &node, int oldvalue, int newvalue, QString name) + : NamedCommand(part, name), + m_effort(node.effort()), + m_oldvalue(oldvalue), + m_newvalue(newvalue) { + + QIntDictIterator<Schedule> it = node.schedules(); + for (; it.current(); ++it) { + addSchScheduled(it.current()); + } +} +void EffortModifyPessimisticRatioCmd::execute() { + m_effort->setPessimisticRatio(m_newvalue); + setSchScheduled(false); + setCommandType(1); +} +void EffortModifyPessimisticRatioCmd::unexecute() { + m_effort->setPessimisticRatio(m_oldvalue); + setSchScheduled(); + setCommandType(1); +} + +ModifyEffortTypeCmd::ModifyEffortTypeCmd(Part *part, Node &node, int oldvalue, int newvalue, QString name) + : NamedCommand(part, name), + m_effort(node.effort()), + m_oldvalue(oldvalue), + m_newvalue(newvalue) { + + QIntDictIterator<Schedule> it = node.schedules(); + for (; it.current(); ++it) { + addSchScheduled(it.current()); + } +} +void ModifyEffortTypeCmd::execute() { + m_effort->setType(static_cast<Effort::Type>(m_newvalue)); + setSchScheduled(false); + setCommandType(1); +} +void ModifyEffortTypeCmd::unexecute() { + m_effort->setType(static_cast<Effort::Type>(m_oldvalue)); + setSchScheduled(); + setCommandType(1); +} + +EffortModifyRiskCmd::EffortModifyRiskCmd(Part *part, Node &node, int oldvalue, int newvalue, QString name) + : NamedCommand(part, name), + m_effort(node.effort()), + m_oldvalue(oldvalue), + m_newvalue(newvalue) { + + QIntDictIterator<Schedule> it = node.schedules(); + for (; it.current(); ++it) { + addSchScheduled(it.current()); + } +} +void EffortModifyRiskCmd::execute() { + m_effort->setRisktype(static_cast<Effort::Risktype>(m_newvalue)); + setSchScheduled(false); + setCommandType(1); +} +void EffortModifyRiskCmd::unexecute() { + m_effort->setRisktype(static_cast<Effort::Risktype>(m_oldvalue)); + setSchScheduled(); + setCommandType(1); +} + +AddResourceGroupRequestCmd::AddResourceGroupRequestCmd(Part *part, Task &task, ResourceGroupRequest *request, QString name) + : NamedCommand(part, name), + m_task(task), + m_request(request) { + + m_mine = true; +} +void AddResourceGroupRequestCmd::execute() { + //kdDebug()<<k_funcinfo<<"group="<<m_request<<endl; + m_task.addRequest(m_request); + m_mine = false; + + setCommandType(1); +} +void AddResourceGroupRequestCmd::unexecute() { + //kdDebug()<<k_funcinfo<<"group="<<m_request<<endl; + m_task.takeRequest(m_request); // group should now be empty of resourceRequests + m_mine = true; + + setCommandType(1); +} + +RemoveResourceGroupRequestCmd::RemoveResourceGroupRequestCmd(Part *part, ResourceGroupRequest *request, QString name) + : NamedCommand(part, name), + m_task(request->parent()->task()), + m_request(request) { + + m_mine = false; +} + +RemoveResourceGroupRequestCmd::RemoveResourceGroupRequestCmd(Part *part, Task &task, ResourceGroupRequest *request, QString name) + : NamedCommand(part, name), + m_task(task), + m_request(request) { + + m_mine = false; +} +void RemoveResourceGroupRequestCmd::execute() { + //kdDebug()<<k_funcinfo<<"group="<<m_request<<endl; + m_task.takeRequest(m_request); // group should now be empty of resourceRequests + m_mine = true; + + setCommandType(1); +} +void RemoveResourceGroupRequestCmd::unexecute() { + //kdDebug()<<k_funcinfo<<"group="<<m_request<<endl; + m_task.addRequest(m_request); + m_mine = false; + + setCommandType(1); +} + +AddResourceCmd::AddResourceCmd(Part *part, ResourceGroup *group, Resource *resource, QString name) + : NamedCommand(part, name), + m_group(group), + m_resource(resource) { + + m_mine = true; +} +AddResourceCmd::~AddResourceCmd() { + if (m_mine) { + //kdDebug()<<k_funcinfo<<"delete: "<<m_resource<<endl; + delete m_resource; + } +} +void AddResourceCmd::execute() { + m_group->addResource(m_resource, 0/*risk*/); + m_mine = false; + //kdDebug()<<k_funcinfo<<"added: "<<m_resource<<endl; + setCommandType(0); +} +void AddResourceCmd::unexecute() { + m_group->takeResource(m_resource); + //kdDebug()<<k_funcinfo<<"removed: "<<m_resource<<endl; + m_mine = true; + + setCommandType(0); +} + +RemoveResourceCmd::RemoveResourceCmd(Part *part, ResourceGroup *group, Resource *resource, QString name) + : AddResourceCmd(part, group, resource, name) { + //kdDebug()<<k_funcinfo<<resource<<endl; + m_mine = false; + m_requests = m_resource->requests(); + + QIntDictIterator<Schedule> it = resource->schedules(); + for (; it.current(); ++it) { + addSchScheduled(it.current()); + } +} +RemoveResourceCmd::~RemoveResourceCmd() { + m_appointments.setAutoDelete(true); +} +void RemoveResourceCmd::execute() { + QPtrListIterator<ResourceRequest> it = m_requests; + for (; it.current(); ++it) { + it.current()->parent()->takeResourceRequest(it.current()); + //kdDebug()<<"Remove request for"<<it.current()->resource()->name()<<endl; + } + QPtrListIterator<Appointment> ait = m_resource->appointments(); + for (; ait.current(); ++ait) { + m_appointments.append(ait.current()); + } + QPtrListIterator<Appointment> mit = m_appointments; + for (; mit.current(); ++mit) { + mit.current()->detach(); //NOTE: removes from m_resource->appointments() + //kdDebug()<<k_funcinfo<<"detached: "<<mit.current()<<endl; + } + AddResourceCmd::unexecute(); + setSchScheduled(false); +} +void RemoveResourceCmd::unexecute() { + m_appointments.first(); + while (m_appointments.current()) { + //kdDebug()<<k_funcinfo<<"attach: "<<m_appointments.current()<<endl; + m_appointments.take()->attach(); + } + QPtrListIterator<ResourceRequest> it = m_requests; + for (; it.current(); ++it) { + it.current()->parent()->addResourceRequest(it.current()); + //kdDebug()<<"Add request for "<<it.current()->resource()->name()<<endl; + } + AddResourceCmd::execute(); + setSchScheduled(); +} + +ModifyResourceNameCmd::ModifyResourceNameCmd(Part *part, Resource *resource, QString value, QString name) + : NamedCommand(part, name), + m_resource(resource), + m_newvalue(value) { + m_oldvalue = resource->name(); +} +void ModifyResourceNameCmd::execute() { + m_resource->setName(m_newvalue); + + setCommandType(0); +} +void ModifyResourceNameCmd::unexecute() { + m_resource->setName(m_oldvalue); + + setCommandType(0); +} +ModifyResourceInitialsCmd::ModifyResourceInitialsCmd(Part *part, Resource *resource, QString value, QString name) + : NamedCommand(part, name), + m_resource(resource), + m_newvalue(value) { + m_oldvalue = resource->initials(); +} +void ModifyResourceInitialsCmd::execute() { + m_resource->setInitials(m_newvalue); + + setCommandType(0); +} +void ModifyResourceInitialsCmd::unexecute() { + m_resource->setInitials(m_oldvalue); + + setCommandType(0); +} +ModifyResourceEmailCmd::ModifyResourceEmailCmd(Part *part, Resource *resource, QString value, QString name) + : NamedCommand(part, name), + m_resource(resource), + m_newvalue(value) { + m_oldvalue = resource->email(); +} +void ModifyResourceEmailCmd::execute() { + m_resource->setEmail(m_newvalue); + + setCommandType(0); +} +void ModifyResourceEmailCmd::unexecute() { + m_resource->setEmail(m_oldvalue); + + setCommandType(0); +} +ModifyResourceTypeCmd::ModifyResourceTypeCmd(Part *part, Resource *resource, int value, QString name) + : NamedCommand(part, name), + m_resource(resource), + m_newvalue(value) { + m_oldvalue = resource->type(); + + QIntDictIterator<Schedule> it = resource->schedules(); + for (; it.current(); ++it) { + addSchScheduled(it.current()); + } +} +void ModifyResourceTypeCmd::execute() { + m_resource->setType((Resource::Type)m_newvalue); + setSchScheduled(false); + setCommandType(1); +} +void ModifyResourceTypeCmd::unexecute() { + m_resource->setType((Resource::Type)m_oldvalue); + setSchScheduled(); + setCommandType(1); +} +ModifyResourceUnitsCmd::ModifyResourceUnitsCmd(Part *part, Resource *resource, int value, QString name) + : NamedCommand(part, name), + m_resource(resource), + m_newvalue(value) { + m_oldvalue = resource->units(); + + QIntDictIterator<Schedule> it = resource->schedules(); + for (; it.current(); ++it) { + addSchScheduled(it.current()); + } +} +void ModifyResourceUnitsCmd::execute() { + m_resource->setUnits(m_newvalue); + setSchScheduled(false); + setCommandType(1); +} +void ModifyResourceUnitsCmd::unexecute() { + m_resource->setUnits(m_oldvalue); + setSchScheduled(); + setCommandType(1); +} + +ModifyResourceAvailableFromCmd::ModifyResourceAvailableFromCmd(Part *part, Resource *resource, DateTime value, QString name) + : NamedCommand(part, name), + m_resource(resource), + m_newvalue(value) { + m_oldvalue = resource->availableFrom(); + + QIntDictIterator<Schedule> it = resource->schedules(); + if (!it.isEmpty() && resource->project()) { + QDateTime s; + QDateTime e; + for (; it.current(); ++it) { + Schedule *sch = resource->project()->findSchedule(it.current()->id()); + if (sch) { + s = sch->start(); + e = sch->end(); + kdDebug()<<k_funcinfo<<"old="<<m_oldvalue<<" new="<<value<<" s="<<s<<" e="<<e<<endl; + } + if (!s.isValid() || !e.isValid() || ((m_oldvalue > s || value > s) && (m_oldvalue < e || value < e))) { + addSchScheduled(it.current()); + } + } + } +} +void ModifyResourceAvailableFromCmd::execute() { + m_resource->setAvailableFrom(m_newvalue); + setSchScheduled(false); + setCommandType(1); //FIXME +} +void ModifyResourceAvailableFromCmd::unexecute() { + m_resource->setAvailableFrom(m_oldvalue); + setSchScheduled(); + setCommandType(1); //FIXME +} + +ModifyResourceAvailableUntilCmd::ModifyResourceAvailableUntilCmd(Part *part, Resource *resource, DateTime value, QString name) + : NamedCommand(part, name), + m_resource(resource), + m_newvalue(value) { + m_oldvalue = resource->availableUntil(); + + QIntDictIterator<Schedule> it = resource->schedules(); + if (!it.isEmpty()) { + QDateTime s; + QDateTime e; + for (; it.current(); ++it) { + Schedule *sch = resource->project()->findSchedule(it.current()->id()); + if (sch) { + s = sch->start(); + e = sch->end(); + kdDebug()<<k_funcinfo<<"old="<<m_oldvalue<<" new="<<value<<" s="<<s<<" e="<<e<<endl; + } + if (!s.isValid() || !e.isValid() || ((m_oldvalue > s || value > s) && (m_oldvalue < e || value < e))) { + addSchScheduled(it.current()); + } + } + } +} +void ModifyResourceAvailableUntilCmd::execute() { + m_resource->setAvailableUntil(m_newvalue); + setSchScheduled(false); + setCommandType(1); //FIXME +} +void ModifyResourceAvailableUntilCmd::unexecute() { + m_resource->setAvailableUntil(m_oldvalue); + setSchScheduled(); + setCommandType(1); //FIXME +} + +ModifyResourceNormalRateCmd::ModifyResourceNormalRateCmd(Part *part, Resource *resource, double value, QString name) + : NamedCommand(part, name), + m_resource(resource), + m_newvalue(value) { + m_oldvalue = resource->normalRate(); +} +void ModifyResourceNormalRateCmd::execute() { + m_resource->setNormalRate(m_newvalue); + + setCommandType(0); +} +void ModifyResourceNormalRateCmd::unexecute() { + m_resource->setNormalRate(m_oldvalue); + + setCommandType(0); +} +ModifyResourceOvertimeRateCmd::ModifyResourceOvertimeRateCmd(Part *part, Resource *resource, double value, QString name) + : NamedCommand(part, name), + m_resource(resource), + m_newvalue(value) { + m_oldvalue = resource->overtimeRate(); +} +void ModifyResourceOvertimeRateCmd::execute() { + m_resource->setOvertimeRate(m_newvalue); + + setCommandType(0); +} +void ModifyResourceOvertimeRateCmd::unexecute() { + m_resource->setOvertimeRate(m_oldvalue); + + setCommandType(0); +} + +ModifyResourceCalendarCmd::ModifyResourceCalendarCmd(Part *part, Resource *resource, Calendar *value, QString name) + : NamedCommand(part, name), + m_resource(resource), + m_newvalue(value) { + m_oldvalue = resource->calendar(true); + + QIntDictIterator<Schedule> it = resource->schedules(); + for (; it.current(); ++it) { + addSchScheduled(it.current()); + } +} +void ModifyResourceCalendarCmd::execute() { + m_resource->setCalendar(m_newvalue); + setSchScheduled(false); + setCommandType(1); +} +void ModifyResourceCalendarCmd::unexecute() { + m_resource->setCalendar(m_oldvalue); + setSchScheduled(); + setCommandType(1); +} + +RemoveResourceGroupCmd::RemoveResourceGroupCmd(Part *part, ResourceGroup *group, QString name) + : NamedCommand(part, name), + m_group(group) { + + m_mine = false; +} +RemoveResourceGroupCmd::~RemoveResourceGroupCmd() { + if (m_mine) + delete m_group; +} +void RemoveResourceGroupCmd::execute() { + // remove all requests to this group + int c=0; + QPtrListIterator<ResourceGroupRequest> it = m_group->requests(); + for (; it.current(); ++it) { + if (it.current()->parent()) { + it.current()->parent()->takeRequest(it.current()); + } + c = 1; + } + if (m_group->project()) + m_group->project()->takeResourceGroup(m_group); + m_mine = true; + + setCommandType(c); +} +void RemoveResourceGroupCmd::unexecute() { + // add all requests + int c=0; + QPtrListIterator<ResourceGroupRequest> it = m_group->requests(); + for (; it.current(); ++it) { + if (it.current()->parent()) { + it.current()->parent()->addRequest(it.current()); + } + c = 1; + } + if (m_group->project()) + m_group->project()->addResourceGroup(m_group); + + m_mine = false; + + setCommandType(c); +} + +AddResourceGroupCmd::AddResourceGroupCmd(Part *part, ResourceGroup *group, QString name) + : RemoveResourceGroupCmd(part, group, name) { + + m_mine = true; +} +void AddResourceGroupCmd::execute() { + RemoveResourceGroupCmd::unexecute(); +} +void AddResourceGroupCmd::unexecute() { + RemoveResourceGroupCmd::execute(); +} + +ModifyResourceGroupNameCmd::ModifyResourceGroupNameCmd(Part *part, ResourceGroup *group, QString value, QString name) + : NamedCommand(part, name), + m_group(group), + m_newvalue(value) { + m_oldvalue = group->name(); +} +void ModifyResourceGroupNameCmd::execute() { + m_group->setName(m_newvalue); + + setCommandType(0); +} +void ModifyResourceGroupNameCmd::unexecute() { + m_group->setName(m_oldvalue); + + setCommandType(0); +} + +TaskModifyProgressCmd::TaskModifyProgressCmd(Part *part, Task &task, struct Task::Progress &value, QString name) + : NamedCommand(part, name), + m_task(task), + m_newvalue(value) { + m_oldvalue = task.progress(); +} +void TaskModifyProgressCmd::execute() { + m_task.progress() = m_newvalue; + + setCommandType(0); +} +void TaskModifyProgressCmd::unexecute() { + m_task.progress() = m_oldvalue; + + setCommandType(0); +} + +ProjectModifyBaselineCmd::ProjectModifyBaselineCmd(Part *part, Project &project, bool value, QString name) + : NamedCommand(part, name), + m_project(project), + m_newvalue(value) { + m_oldvalue = project.isBaselined(); +} +void ProjectModifyBaselineCmd::execute() { + m_project.setBaselined(m_newvalue); + + setCommandType(2); +} +void ProjectModifyBaselineCmd::unexecute() { + m_project.setBaselined(m_oldvalue); + + setCommandType(2); +} + +AddAccountCmd::AddAccountCmd(Part *part, Project &project, Account *account, QString parent, QString name) + : NamedCommand(part, name), + m_project(project), + m_account(account), + m_parent(0), + m_parentName(parent) { + m_mine = true; +} + +AddAccountCmd::AddAccountCmd(Part *part, Project &project, Account *account, Account *parent, QString name) + : NamedCommand(part, name), + m_project(project), + m_account(account), + m_parent(parent) { + m_mine = true; +} + +AddAccountCmd::~AddAccountCmd() { + if (m_mine) + delete m_account; +} + +void AddAccountCmd::execute() { + if (m_parent == 0 && !m_parentName.isEmpty()) { + m_parent = m_project.accounts().findAccount(m_parentName); + } + if (m_parent) + m_parent->append(m_account); + else + m_project.accounts().append(m_account); + + setCommandType(0); + m_mine = false; +} +void AddAccountCmd::unexecute() { + if (m_parent) + m_parent->take(m_account); + else + m_project.accounts().take(m_account); + + setCommandType(0); + m_mine = true; +} + +RemoveAccountCmd::RemoveAccountCmd(Part *part, Project &project, Account *account, QString name) + : NamedCommand(part, name), + m_project(project), + m_account(account) { + m_mine = false; + m_isDefault = account == project.accounts().defaultAccount(); +} + +RemoveAccountCmd::~RemoveAccountCmd() { + if (m_mine) + delete m_account; +} + +void RemoveAccountCmd::execute() { + if (m_isDefault) { + m_project.accounts().setDefaultAccount(0); + } + if (m_account->parent()) + m_account->parent()->take(m_account); + else + m_project.accounts().take(m_account); + + setCommandType(0); + m_mine = true; +} +void RemoveAccountCmd::unexecute() { + if (m_account->parent()) + m_account->parent()->append(m_account); + else + m_project.accounts().append(m_account); + + if (m_isDefault) + m_project.accounts().setDefaultAccount(m_account); + + setCommandType(0); + m_mine = false; +} + +RenameAccountCmd::RenameAccountCmd(Part *part, Account *account, QString value, QString name) + : NamedCommand(part, name), + m_account(account) { + m_oldvalue = account->name(); + m_newvalue = value; +} + +void RenameAccountCmd::execute() { + m_account->setName(m_newvalue); + setCommandType(0); +} +void RenameAccountCmd::unexecute() { + m_account->setName(m_oldvalue); + setCommandType(0); +} + +ModifyAccountDescriptionCmd::ModifyAccountDescriptionCmd(Part *part, Account *account, QString value, QString name) + : NamedCommand(part, name), + m_account(account) { + m_oldvalue = account->description(); + m_newvalue = value; +} + +void ModifyAccountDescriptionCmd::execute() { + m_account->setDescription(m_newvalue); + setCommandType(0); +} +void ModifyAccountDescriptionCmd::unexecute() { + m_account->setDescription(m_oldvalue); + setCommandType(0); +} + + +NodeModifyStartupCostCmd::NodeModifyStartupCostCmd(Part *part, Node &node, double value, QString name) + : NamedCommand(part, name), + m_node(node) { + m_oldvalue = node.startupCost(); + m_newvalue = value; +} + +void NodeModifyStartupCostCmd::execute() { + m_node.setStartupCost(m_newvalue); + setCommandType(0); +} +void NodeModifyStartupCostCmd::unexecute() { + m_node.setStartupCost(m_oldvalue); + setCommandType(0); +} + +NodeModifyShutdownCostCmd::NodeModifyShutdownCostCmd(Part *part, Node &node, double value, QString name) + : NamedCommand(part, name), + m_node(node) { + m_oldvalue = node.startupCost(); + m_newvalue = value; +} + +void NodeModifyShutdownCostCmd::execute() { + m_node.setShutdownCost(m_newvalue); + setCommandType(0); +} +void NodeModifyShutdownCostCmd::unexecute() { + m_node.setShutdownCost(m_oldvalue); + setCommandType(0); +} + +NodeModifyRunningAccountCmd::NodeModifyRunningAccountCmd(Part *part, Node &node, Account *oldvalue, Account *newvalue, QString name) + : NamedCommand(part, name), + m_node(node) { + m_oldvalue = oldvalue; + m_newvalue = newvalue; + //kdDebug()<<k_funcinfo<<endl; +} +void NodeModifyRunningAccountCmd::execute() { + //kdDebug()<<k_funcinfo<<endl; + if (m_oldvalue) { + m_oldvalue->removeRunning(m_node); + } + if (m_newvalue) { + m_newvalue->addRunning(m_node); + } + setCommandType(0); +} +void NodeModifyRunningAccountCmd::unexecute() { + //kdDebug()<<k_funcinfo<<endl; + if (m_newvalue) { + m_newvalue->removeRunning(m_node); + } + if (m_oldvalue) { + m_oldvalue->addRunning(m_node); + } + setCommandType(0); +} + +NodeModifyStartupAccountCmd::NodeModifyStartupAccountCmd(Part *part, Node &node, Account *oldvalue, Account *newvalue, QString name) + : NamedCommand(part, name), + m_node(node) { + m_oldvalue = oldvalue; + m_newvalue = newvalue; + //kdDebug()<<k_funcinfo<<endl; +} + +void NodeModifyStartupAccountCmd::execute() { + //kdDebug()<<k_funcinfo<<endl; + if (m_oldvalue) { + m_oldvalue->removeStartup(m_node); + } + if (m_newvalue) { + m_newvalue->addStartup(m_node); + } + setCommandType(0); +} +void NodeModifyStartupAccountCmd::unexecute() { + //kdDebug()<<k_funcinfo<<endl; + if (m_newvalue) { + m_newvalue->removeStartup(m_node); + } + if (m_oldvalue) { + m_oldvalue->addStartup(m_node); + } + setCommandType(0); +} + +NodeModifyShutdownAccountCmd::NodeModifyShutdownAccountCmd(Part *part, Node &node, Account *oldvalue, Account *newvalue, QString name) + : NamedCommand(part, name), + m_node(node) { + m_oldvalue = oldvalue; + m_newvalue = newvalue; + //kdDebug()<<k_funcinfo<<endl; +} + +void NodeModifyShutdownAccountCmd::execute() { + //kdDebug()<<k_funcinfo<<endl; + if (m_oldvalue) { + m_oldvalue->removeShutdown(m_node); + } + if (m_newvalue) { + m_newvalue->addShutdown(m_node); + } + setCommandType(0); +} +void NodeModifyShutdownAccountCmd::unexecute() { + //kdDebug()<<k_funcinfo<<endl; + if (m_newvalue) { + m_newvalue->removeShutdown(m_node); + } + if (m_oldvalue) { + m_oldvalue->addShutdown(m_node); + } + setCommandType(0); +} + +ModifyDefaultAccountCmd::ModifyDefaultAccountCmd(Part *part, Accounts &acc, Account *oldvalue, Account *newvalue, QString name) + : NamedCommand(part, name), + m_accounts(acc) { + m_oldvalue = oldvalue; + m_newvalue = newvalue; + //kdDebug()<<k_funcinfo<<endl; +} + +void ModifyDefaultAccountCmd::execute() { + //kdDebug()<<k_funcinfo<<endl; + m_accounts.setDefaultAccount(m_newvalue); + setCommandType(0); +} +void ModifyDefaultAccountCmd::unexecute() { + //kdDebug()<<k_funcinfo<<endl; + m_accounts.setDefaultAccount(m_oldvalue); + setCommandType(0); +} + +ProjectModifyConstraintCmd::ProjectModifyConstraintCmd(Part *part, Project &node, Node::ConstraintType c, QString name) + : NamedCommand(part, name), + m_node(node), + newConstraint(c), + oldConstraint(static_cast<Node::ConstraintType>(node.constraint())) { + + QIntDictIterator<Schedule> it = node.schedules(); + for (; it.current(); ++it) { + addSchScheduled(it.current()); + } +} +void ProjectModifyConstraintCmd::execute() { + m_node.setConstraint(newConstraint); + setSchScheduled(false); + setCommandType(1); +} +void ProjectModifyConstraintCmd::unexecute() { + m_node.setConstraint(oldConstraint); + setSchScheduled(); + setCommandType(1); +} + +ProjectModifyStartTimeCmd::ProjectModifyStartTimeCmd(Part *part, Project &node, QDateTime dt, QString name) + : NamedCommand(part, name), + m_node(node), + newTime(dt), + oldTime(node.startTime()) { + + QIntDictIterator<Schedule> it = node.schedules(); + for (; it.current(); ++it) { + addSchScheduled(it.current()); + } +} + +void ProjectModifyStartTimeCmd::execute() { + m_node.setConstraintStartTime(newTime); + setSchScheduled(false); + setCommandType(1); +} +void ProjectModifyStartTimeCmd::unexecute() { + m_node.setConstraintStartTime(oldTime); + setSchScheduled(); + setCommandType(1); +} + +ProjectModifyEndTimeCmd::ProjectModifyEndTimeCmd(Part *part, Project &node, QDateTime dt, QString name) + : NamedCommand(part, name), + m_node(node), + newTime(dt), + oldTime(node.endTime()) { + + QIntDictIterator<Schedule> it = node.schedules(); + for (; it.current(); ++it) { + addSchScheduled(it.current()); + } +} +void ProjectModifyEndTimeCmd::execute() { + m_node.setEndTime(newTime); + m_node.setConstraintEndTime(newTime); + setSchScheduled(false); + setCommandType(1); +} +void ProjectModifyEndTimeCmd::unexecute() { + m_node.setConstraintEndTime(oldTime); + setSchScheduled(); + setCommandType(1); +} + +CalculateProjectCmd::CalculateProjectCmd(Part *part, Project &node, QString tname, int type, QString name) + : NamedCommand(part, name), + m_node(node), + m_typename(tname), + m_type(type), + newSchedule(0) { + + oldCurrent = node.currentSchedule(); + //kdDebug()<<k_funcinfo<<type<<endl; +} +void CalculateProjectCmd::execute() { + if (newSchedule == 0) { + //kdDebug()<<k_funcinfo<<" create schedule"<<endl; + newSchedule = m_node.createSchedule(m_typename, (Schedule::Type)m_type); + m_node.calculate(newSchedule); + } else { + //kdDebug()<<k_funcinfo<<" redo"<<endl; + newSchedule->setDeleted(false); + m_node.setCurrentSchedulePtr(newSchedule); + } + setCommandType(0); +} +void CalculateProjectCmd::unexecute() { + //kdDebug()<<k_funcinfo<<endl; + newSchedule->setDeleted(true); + m_node.setCurrentSchedulePtr(oldCurrent); + + setCommandType(0); +} + +RecalculateProjectCmd::RecalculateProjectCmd(Part *part, Project &node, Schedule &sch, QString name) + : NamedCommand(part, name), + m_node(node), + oldSchedule(sch), + newSchedule(0), + oldDeleted(sch.isDeleted()) { + + oldCurrent = node.currentSchedule(); + //kdDebug()<<k_funcinfo<<sch.typeToString()<<" curr="<<(oldCurrent?oldCurrent->id():-1)<<endl; +} +void RecalculateProjectCmd::execute() { + oldSchedule.setDeleted(true); + if (newSchedule == 0) { + newSchedule = m_node.createSchedule(oldSchedule.name(), oldSchedule.type()); + m_node.calculate(newSchedule); + } else { + newSchedule->setDeleted(false); + m_node.setCurrentSchedulePtr(newSchedule); + //kdDebug()<<k_funcinfo<<newSchedule->typeToString()<<" redo"<<endl; + } + setCommandType(0); +} +void RecalculateProjectCmd::unexecute() { + //kdDebug()<<k_funcinfo<<newSchedule->typeToString()<<(oldCurrent ? oldCurrent->id() : -1)<<endl; + newSchedule->setDeleted(true); + oldSchedule.setDeleted(oldDeleted); + m_node.setCurrentSchedulePtr(oldCurrent); + + setCommandType(0); +} + + +ModifyStandardWorktimeYearCmd::ModifyStandardWorktimeYearCmd(Part *part, StandardWorktime *wt, double oldvalue, double newvalue, QString name) + : NamedCommand(part, name), + swt(wt), + m_oldvalue(oldvalue), + m_newvalue(newvalue) { + +} +void ModifyStandardWorktimeYearCmd::execute() { + swt->setYear(m_newvalue); + setCommandType(0); +} +void ModifyStandardWorktimeYearCmd::unexecute() { + swt->setYear(m_oldvalue); + setCommandType(0); +} + +ModifyStandardWorktimeMonthCmd::ModifyStandardWorktimeMonthCmd(Part *part, StandardWorktime *wt, double oldvalue, double newvalue, QString name) + : NamedCommand(part, name), + swt(wt), + m_oldvalue(oldvalue), + m_newvalue(newvalue) { + +} +void ModifyStandardWorktimeMonthCmd::execute() { + swt->setMonth(m_newvalue); + setCommandType(0); +} +void ModifyStandardWorktimeMonthCmd::unexecute() { + swt->setMonth(m_oldvalue); + setCommandType(0); +} + +ModifyStandardWorktimeWeekCmd::ModifyStandardWorktimeWeekCmd(Part *part, StandardWorktime *wt, double oldvalue, double newvalue, QString name) + : NamedCommand(part, name), + swt(wt), + m_oldvalue(oldvalue), + m_newvalue(newvalue) { + +} +void ModifyStandardWorktimeWeekCmd::execute() { + swt->setWeek(m_newvalue); + setCommandType(0); +} +void ModifyStandardWorktimeWeekCmd::unexecute() { + swt->setWeek(m_oldvalue); + setCommandType(0); +} + +ModifyStandardWorktimeDayCmd::ModifyStandardWorktimeDayCmd(Part *part, StandardWorktime *wt, double oldvalue, double newvalue, QString name) + : NamedCommand(part, name), + swt(wt), + m_oldvalue(oldvalue), + m_newvalue(newvalue) { + +} + +void ModifyStandardWorktimeDayCmd::execute() { + swt->setDay(m_newvalue); + setCommandType(0); +} +void ModifyStandardWorktimeDayCmd::unexecute() { + swt->setDay(m_oldvalue); + setCommandType(0); +} + + +} //KPlato namespace diff --git a/kplato/kptcommand.h b/kplato/kptcommand.h new file mode 100644 index 00000000..a61da649 --- /dev/null +++ b/kplato/kptcommand.h @@ -0,0 +1,1052 @@ +/* This file is part of the KDE project + Copyright (C) 2004, 2005 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef KPTCOMMAND_H +#define KPTCOMMAND_H + +#include <kcommand.h> + +#include "kptappointment.h" +#include "kptnode.h" +#include "kptduration.h" +#include "kptpart.h" +#include "kpttask.h" + +class QString; + +namespace KPlato +{ + +class Account; +class Accounts; +class Project; +class Task; +class Calendar; +class CalendarDay; +class Relation; +class ResourceGroupRequest; +class ResourceRequest; +class ResourceGroup; +class Resource; +class Schedule; +class StandardWorktime; + +class NamedCommand : public KNamedCommand +{ +public: + NamedCommand(Part *part, QString name) + : KNamedCommand(name), m_part(part) + {} + + void setCommandType(int type); + +protected: + void setSchDeleted(); + void setSchDeleted(bool state); + void setSchScheduled(); + void setSchScheduled(bool state); + void addSchScheduled(Schedule *sch); + void addSchDeleted(Schedule *sch); + + QMap<Schedule*, bool> m_schedules; +private: + Part *m_part; +}; + +class CalendarAddCmd : public NamedCommand +{ +public: + CalendarAddCmd(Part *part, Project *project, Calendar *cal, QString name=0); + void execute(); + void unexecute(); + +private: + Project *m_project; + Calendar *m_cal; + bool m_added; +}; + +class CalendarDeleteCmd : public NamedCommand +{ +public: + CalendarDeleteCmd(Part *part, Calendar *cal, QString name=0); + void execute(); + void unexecute(); + +private: + Calendar *m_cal; +}; + +class CalendarModifyNameCmd : public NamedCommand +{ +public: + CalendarModifyNameCmd(Part *part, Calendar *cal, QString newvalue, QString name=0); + void execute(); + void unexecute(); + +private: + Calendar *m_cal; + QString m_newvalue; + QString m_oldvalue; +}; + +class CalendarModifyParentCmd : public NamedCommand +{ +public: + CalendarModifyParentCmd(Part *part, Calendar *cal, Calendar *newvalue, QString name=0); + void execute(); + void unexecute(); + +private: + Calendar *m_cal; + Calendar *m_newvalue; + Calendar *m_oldvalue; +}; + +class CalendarAddDayCmd : public NamedCommand +{ +public: + CalendarAddDayCmd(Part *part, Calendar *cal, CalendarDay *newvalue, QString name=0); + ~CalendarAddDayCmd(); + void execute(); + void unexecute(); + +protected: + Calendar *m_cal; + CalendarDay *m_newvalue; + bool m_mine; +}; + +class CalendarRemoveDayCmd : public NamedCommand +{ +public: + CalendarRemoveDayCmd(Part *part, Calendar *cal, const QDate &day, QString name=0); + void execute(); + void unexecute(); + +protected: + Calendar *m_cal; + CalendarDay *m_value; + bool m_mine; +}; + +class CalendarModifyDayCmd : public NamedCommand +{ +public: + CalendarModifyDayCmd(Part *part, Calendar *cal, CalendarDay *value, QString name=0); + ~CalendarModifyDayCmd(); + void execute(); + void unexecute(); + +private: + Calendar *m_cal; + CalendarDay *m_newvalue; + CalendarDay *m_oldvalue; + bool m_mine; +}; + +class CalendarModifyWeekdayCmd : public NamedCommand +{ +public: + CalendarModifyWeekdayCmd(Part *part, Calendar *cal, int weekday, CalendarDay *value, QString name=0); + ~CalendarModifyWeekdayCmd(); + void execute(); + void unexecute(); + +private: + int m_weekday; + Calendar *m_cal; + CalendarDay *m_value; + bool m_mine; +}; + + +class NodeDeleteCmd : public NamedCommand +{ +public: + NodeDeleteCmd(Part *part, Node *node, QString name=0); + ~NodeDeleteCmd(); + void execute(); + void unexecute(); + +private: + Node *m_node; + Node *m_parent; + Project *m_project; + int m_index; + bool m_mine; + QPtrList<Appointment> m_appointments; + +}; + +class TaskAddCmd : public NamedCommand +{ +public: + TaskAddCmd(Part *part, Project *project, Node *node, Node *after, QString name=0); + ~TaskAddCmd(); + void execute(); + void unexecute(); + +private: + Project *m_project; + Node *m_node; + Node *m_after; + bool m_added; +}; + +class SubtaskAddCmd : public NamedCommand +{ +public: + SubtaskAddCmd(Part *part, Project *project, Node *node, Node *parent, QString name=0); + ~SubtaskAddCmd(); + void execute(); + void unexecute(); + +private: + Project *m_project; + Node *m_node; + Node *m_parent; + bool m_added; +}; + + +class NodeModifyNameCmd : public NamedCommand +{ +public: + NodeModifyNameCmd(Part *part, Node &node, QString nodename, QString name=0); + void execute(); + void unexecute(); + +private: + Node &m_node; + QString newName; + QString oldName; +}; + +class NodeModifyLeaderCmd : public NamedCommand +{ +public: + NodeModifyLeaderCmd(Part *part, Node &node, QString leader, QString name=0); + void execute(); + void unexecute(); + +private: + Node &m_node; + QString newLeader; + QString oldLeader; +}; + +class NodeModifyDescriptionCmd : public NamedCommand +{ +public: + NodeModifyDescriptionCmd(Part *part, Node &node, QString description, QString name=0); + void execute(); + void unexecute(); + +private: + Node &m_node; + QString newDescription; + QString oldDescription; +}; + +class NodeModifyConstraintCmd : public NamedCommand +{ +public: + NodeModifyConstraintCmd(Part *part, Node &node, Node::ConstraintType c, QString name=0); + void execute(); + void unexecute(); + +private: + Node &m_node; + Node::ConstraintType newConstraint; + Node::ConstraintType oldConstraint; + +}; + +class NodeModifyConstraintStartTimeCmd : public NamedCommand +{ +public: + NodeModifyConstraintStartTimeCmd(Part *part, Node &node, QDateTime dt, QString name=0); + void execute(); + void unexecute(); + +private: + Node &m_node; + QDateTime newTime; + QDateTime oldTime; + +}; +class NodeModifyConstraintEndTimeCmd : public NamedCommand +{ +public: + NodeModifyConstraintEndTimeCmd(Part *part, Node &node, QDateTime dt, QString name=0); + void execute(); + void unexecute(); + +private: + Node &m_node; + QDateTime newTime; + QDateTime oldTime; + +}; +class NodeModifyStartTimeCmd : public NamedCommand +{ +public: + NodeModifyStartTimeCmd(Part *part, Node &node, QDateTime dt, QString name=0); + void execute(); + void unexecute(); + +private: + Node &m_node; + QDateTime newTime; + QDateTime oldTime; +}; +class NodeModifyEndTimeCmd : public NamedCommand +{ +public: + NodeModifyEndTimeCmd(Part *part, Node &node, QDateTime dt, QString name=0); + void execute(); + void unexecute(); + +private: + Node &m_node; + QDateTime newTime; + QDateTime oldTime; +}; +class NodeModifyIdCmd : public NamedCommand +{ +public: + NodeModifyIdCmd(Part *part, Node &node, QString id, QString name=0); + void execute(); + void unexecute(); + +private: + Node &m_node; + QString newId; + QString oldId; +}; + +class NodeIndentCmd : public NamedCommand +{ +public: + NodeIndentCmd(Part *part, Node &node, QString name=0); + void execute(); + void unexecute(); + +private: + Node &m_node; + Node *m_oldparent, *m_newparent; + int m_oldindex, m_newindex; +}; + +class NodeUnindentCmd : public NamedCommand +{ +public: + NodeUnindentCmd(Part *part, Node &node, QString name=0); + void execute(); + void unexecute(); + +private: + Node &m_node; + Node *m_oldparent, *m_newparent; + int m_oldindex, m_newindex; +}; + +class NodeMoveUpCmd : public NamedCommand +{ +public: + NodeMoveUpCmd(Part *part, Node &node, QString name=0); + void execute(); + void unexecute(); + +private: + Node &m_node; + Project *m_project; + bool m_moved; +}; + +class NodeMoveDownCmd : public NamedCommand +{ +public: + NodeMoveDownCmd(Part *part, Node &node, QString name=0); + void execute(); + void unexecute(); + +private: + Node &m_node; + Project *m_project; + bool m_moved; +}; + +class AddRelationCmd : public NamedCommand +{ +public: + AddRelationCmd(Part *part, Relation *rel, QString name=0); + ~AddRelationCmd(); + void execute(); + void unexecute(); + +private: + Relation *m_rel; + bool m_taken; + +}; + +class DeleteRelationCmd : public NamedCommand +{ +public: + DeleteRelationCmd(Part *part, Relation *rel, QString name=0); + ~DeleteRelationCmd(); + void execute(); + void unexecute(); + +private: + Relation *m_rel; + bool m_taken; + +}; + +class ModifyRelationTypeCmd : public NamedCommand +{ +public: + ModifyRelationTypeCmd(Part *part, Relation *rel, Relation::Type type, QString name=0); + void execute(); + void unexecute(); + +private: + Relation *m_rel; + Relation::Type m_newtype; + Relation::Type m_oldtype; + +}; + +class ModifyRelationLagCmd : public NamedCommand +{ +public: + ModifyRelationLagCmd(Part *part, Relation *rel, Duration lag, QString name=0); + void execute(); + void unexecute(); + +private: + Relation *m_rel; + Duration m_newlag; + Duration m_oldlag; + +}; + +class AddResourceRequestCmd : public NamedCommand +{ +public: + AddResourceRequestCmd(Part *part, ResourceGroupRequest *group, ResourceRequest *request, QString name=0); + ~AddResourceRequestCmd(); + void execute(); + void unexecute(); + +private: + ResourceGroupRequest *m_group; + ResourceRequest *m_request; + bool m_mine; + +}; + +class RemoveResourceRequestCmd : public NamedCommand +{ +public: + RemoveResourceRequestCmd(Part *part, ResourceGroupRequest *group, ResourceRequest *request, QString name=0); + ~RemoveResourceRequestCmd(); + void execute(); + void unexecute(); + +private: + ResourceGroupRequest *m_group; + ResourceRequest *m_request; + bool m_mine; + +}; + +class ModifyEffortCmd : public NamedCommand +{ +public: + ModifyEffortCmd(Part *part, Node &node, Duration oldvalue, Duration newvalue, QString name=0); + void execute(); + void unexecute(); + +private: + Effort *m_effort; + Duration m_oldvalue, m_newvalue; + +}; + +class EffortModifyOptimisticRatioCmd : public NamedCommand +{ +public: + EffortModifyOptimisticRatioCmd(Part *part, Node &node, int oldvalue, int newvalue, QString name=0); + void execute(); + void unexecute(); + +private: + Effort *m_effort; + int m_oldvalue, m_newvalue; + +}; + +class EffortModifyPessimisticRatioCmd : public NamedCommand +{ +public: + EffortModifyPessimisticRatioCmd(Part *part, Node &node, int oldvalue, int newvalue, QString name=0); + void execute(); + void unexecute(); + +private: + Effort *m_effort; + int m_oldvalue, m_newvalue; + +}; + +class ModifyEffortTypeCmd : public NamedCommand +{ +public: + ModifyEffortTypeCmd(Part *part, Node &node, int oldvalue, int newvalue, QString name=0); + void execute(); + void unexecute(); + +private: + Effort *m_effort; + int m_oldvalue, m_newvalue; + +}; + +class EffortModifyRiskCmd : public NamedCommand +{ +public: + EffortModifyRiskCmd(Part *part, Node &node, int oldvalue, int newvalue, QString name=0); + void execute(); + void unexecute(); + +private: + Effort *m_effort; + int m_oldvalue, m_newvalue; + +}; + +class AddResourceGroupRequestCmd : public NamedCommand +{ +public: + AddResourceGroupRequestCmd(Part *part, Task &task, ResourceGroupRequest *request, QString name=0); + void execute(); + void unexecute(); + +private: + Task &m_task; + ResourceGroupRequest *m_request; + bool m_mine; +}; + +class RemoveResourceGroupRequestCmd : public NamedCommand +{ +public: + RemoveResourceGroupRequestCmd(Part *part, ResourceGroupRequest *request, QString name=0); + RemoveResourceGroupRequestCmd(Part *part, Task &task, ResourceGroupRequest *request, QString name=0); + void execute(); + void unexecute(); + +private: + Task &m_task; + ResourceGroupRequest *m_request; + bool m_mine; +}; + +class AddResourceCmd : public NamedCommand +{ +public: + AddResourceCmd(Part *part, ResourceGroup *group, Resource *resource, QString name=0); + ~AddResourceCmd(); + void execute(); + void unexecute(); + +protected: + + ResourceGroup *m_group; + Resource *m_resource; + bool m_mine; +}; + +class RemoveResourceCmd : public AddResourceCmd +{ +public: + RemoveResourceCmd(Part *part, ResourceGroup *group, Resource *resource, QString name=0); + ~RemoveResourceCmd(); + void execute(); + void unexecute(); + +private: + QPtrList<ResourceRequest> m_requests; + QPtrList<Appointment> m_appointments; +}; + +class ModifyResourceNameCmd : public NamedCommand +{ +public: + ModifyResourceNameCmd(Part *part, Resource *resource, QString value, QString name=0); + void execute(); + void unexecute(); + +private: + + Resource *m_resource; + QString m_newvalue; + QString m_oldvalue; +}; +class ModifyResourceInitialsCmd : public NamedCommand +{ +public: + ModifyResourceInitialsCmd(Part *part, Resource *resource, QString value, QString name=0); + void execute(); + void unexecute(); + +private: + Resource *m_resource; + QString m_newvalue; + QString m_oldvalue; +}; +class ModifyResourceEmailCmd : public NamedCommand +{ +public: + ModifyResourceEmailCmd(Part *part, Resource *resource, QString value, QString name=0); + void execute(); + void unexecute(); + +private: + Resource *m_resource; + QString m_newvalue; + QString m_oldvalue; +}; +class ModifyResourceTypeCmd : public NamedCommand +{ +public: + ModifyResourceTypeCmd(Part *part, Resource *resource, int value, QString name=0); + void execute(); + void unexecute(); + +private: + Resource *m_resource; + int m_newvalue; + int m_oldvalue; +}; + +class ModifyResourceUnitsCmd : public NamedCommand +{ +public: + ModifyResourceUnitsCmd(Part *part, Resource *resource, int value, QString name=0); + void execute(); + void unexecute(); + +private: + Resource *m_resource; + int m_newvalue; + int m_oldvalue; +}; + +class ModifyResourceAvailableFromCmd : public NamedCommand +{ +public: + ModifyResourceAvailableFromCmd(Part *part, Resource *resource, DateTime value, QString name=0); + void execute(); + void unexecute(); + +private: + Resource *m_resource; + DateTime m_newvalue; + DateTime m_oldvalue; +}; +class ModifyResourceAvailableUntilCmd : public NamedCommand +{ +public: + ModifyResourceAvailableUntilCmd(Part *part, Resource *resource, DateTime value, QString name=0); + void execute(); + void unexecute(); + +private: + Resource *m_resource; + DateTime m_newvalue; + DateTime m_oldvalue; +}; + +class ModifyResourceNormalRateCmd : public NamedCommand +{ +public: + ModifyResourceNormalRateCmd(Part *part, Resource *resource, double value, QString name=0); + void execute(); + void unexecute(); + +private: + Resource *m_resource; + double m_newvalue; + double m_oldvalue; +}; +class ModifyResourceOvertimeRateCmd : public NamedCommand +{ +public: + ModifyResourceOvertimeRateCmd(Part *part, Resource *resource, double value, QString name=0); + void execute(); + void unexecute(); + +private: + Resource *m_resource; + double m_newvalue; + double m_oldvalue; +}; +class ModifyResourceCalendarCmd : public NamedCommand +{ +public: + ModifyResourceCalendarCmd(Part *part, Resource *resource, Calendar *value, QString name=0); + void execute(); + void unexecute(); + +private: + Resource *m_resource; + Calendar *m_newvalue; + Calendar *m_oldvalue; +}; + +class RemoveResourceGroupCmd : public NamedCommand +{ +public: + RemoveResourceGroupCmd(Part *part, ResourceGroup *group, QString name=0); + ~RemoveResourceGroupCmd(); + void execute(); + void unexecute(); + +protected: + + ResourceGroup *m_group; + bool m_mine; +}; + +class AddResourceGroupCmd : public RemoveResourceGroupCmd +{ +public: + AddResourceGroupCmd(Part *part, ResourceGroup *group, QString name=0); + void execute(); + void unexecute(); +}; + +class ModifyResourceGroupNameCmd : public NamedCommand +{ +public: + ModifyResourceGroupNameCmd(Part *part, ResourceGroup *group, QString value, QString name=0); + void execute(); + void unexecute(); + +private: + ResourceGroup *m_group; + QString m_newvalue; + QString m_oldvalue; +}; + +class TaskModifyProgressCmd : public NamedCommand +{ +public: + TaskModifyProgressCmd(Part *part, Task &task, struct Task::Progress &value, QString name=0); + void execute(); + void unexecute(); + +private: + Task &m_task; + struct Task::Progress m_newvalue; + struct Task::Progress m_oldvalue; +}; + +class ProjectModifyBaselineCmd : public NamedCommand +{ +public: + ProjectModifyBaselineCmd(Part *part, Project &project, bool value, QString name=0); + void execute(); + void unexecute(); + +private: + Project &m_project; + bool m_newvalue; + bool m_oldvalue; +}; + +class AddAccountCmd : public NamedCommand +{ +public: + AddAccountCmd(Part *part, Project &project, Account *account, Account *parent=0, QString name=0); + AddAccountCmd(Part *part, Project &project, Account *account, QString parent, QString name=0); + ~AddAccountCmd(); + void execute(); + void unexecute(); + +protected: + bool m_mine; + +private: + Project &m_project; + Account *m_account; + Account *m_parent; + QString m_parentName; +}; + +class RemoveAccountCmd : public NamedCommand +{ +public: + RemoveAccountCmd(Part *part, Project &project, Account *account, QString name=0); + ~RemoveAccountCmd(); + void execute(); + void unexecute(); + +private: + Project &m_project; + Account *m_account; + bool m_isDefault; + bool m_mine; + +}; + +class RenameAccountCmd : public NamedCommand +{ +public: + RenameAccountCmd(Part *part, Account *account, QString value, QString name=0); + void execute(); + void unexecute(); + +private: + Account *m_account; + QString m_oldvalue; + QString m_newvalue; +}; + +class ModifyAccountDescriptionCmd : public NamedCommand +{ +public: + ModifyAccountDescriptionCmd(Part *part, Account *account, QString value, QString name=0); + void execute(); + void unexecute(); + +private: + Account *m_account; + QString m_oldvalue; + QString m_newvalue; +}; + +class NodeModifyStartupCostCmd : public NamedCommand +{ +public: + NodeModifyStartupCostCmd(Part *part, Node &node, double value, QString name=0); + void execute(); + void unexecute(); + +private: + Node &m_node; + double m_oldvalue; + double m_newvalue; +}; + +class NodeModifyShutdownCostCmd : public NamedCommand +{ +public: + NodeModifyShutdownCostCmd(Part *part, Node &node, double value, QString name=0); + void execute(); + void unexecute(); + +private: + Node &m_node; + double m_oldvalue; + double m_newvalue; +}; + +class NodeModifyRunningAccountCmd : public NamedCommand +{ +public: + NodeModifyRunningAccountCmd(Part *part, Node &node, Account *oldvalue, Account *newvalue, QString name=0); + void execute(); + void unexecute(); + +private: + Node &m_node; + Account *m_oldvalue; + Account *m_newvalue; +}; + +class NodeModifyStartupAccountCmd : public NamedCommand +{ +public: + NodeModifyStartupAccountCmd(Part *part, Node &node, Account *oldvalue, Account *newvalue, QString name=0); + void execute(); + void unexecute(); + +private: + Node &m_node; + Account *m_oldvalue; + Account *m_newvalue; +}; + +class NodeModifyShutdownAccountCmd : public NamedCommand +{ +public: + NodeModifyShutdownAccountCmd(Part *part, Node &node, Account *oldvalue, Account *newvalue, QString name=0); + void execute(); + void unexecute(); + +private: + Node &m_node; + Account *m_oldvalue; + Account *m_newvalue; +}; + +class ModifyDefaultAccountCmd : public NamedCommand +{ +public: + ModifyDefaultAccountCmd(Part *part, Accounts &acc, Account *oldvalue, Account *newvalue, QString name=0); + void execute(); + void unexecute(); + +private: + Accounts &m_accounts; + Account *m_oldvalue; + Account *m_newvalue; +}; + +class ProjectModifyConstraintCmd : public NamedCommand +{ +public: + ProjectModifyConstraintCmd(Part *part, Project &node, Node::ConstraintType c, QString name=0); + void execute(); + void unexecute(); + +private: + Project &m_node; + Node::ConstraintType newConstraint; + Node::ConstraintType oldConstraint; + +}; + +class ProjectModifyStartTimeCmd : public NamedCommand +{ +public: + ProjectModifyStartTimeCmd(Part *part, Project &node, QDateTime dt, QString name=0); + void execute(); + void unexecute(); + +private: + Project &m_node; + QDateTime newTime; + QDateTime oldTime; + +}; + +class ProjectModifyEndTimeCmd : public NamedCommand +{ +public: + ProjectModifyEndTimeCmd(Part *part, Project &project, QDateTime dt, QString name=0); + void execute(); + void unexecute(); + +private: + Project &m_node; + QDateTime newTime; + QDateTime oldTime; + +}; + + +class CalculateProjectCmd : public NamedCommand +{ +public: + CalculateProjectCmd(Part *part, Project &project, QString tname, int type, QString name=0); + void execute(); + void unexecute(); + +private: + Project &m_node; + QString m_typename; + int m_type; + Schedule *newSchedule; + Schedule *oldCurrent; +}; + +class RecalculateProjectCmd : public NamedCommand +{ +public: + RecalculateProjectCmd(Part *part, Project &project, Schedule &sch, QString name=0); + void execute(); + void unexecute(); + +private: + Project &m_node; + Schedule &oldSchedule; + Schedule *newSchedule; + bool oldDeleted; + Schedule *oldCurrent; +}; + +class ModifyStandardWorktimeYearCmd : public NamedCommand +{ +public: + ModifyStandardWorktimeYearCmd(Part *part, StandardWorktime *wt, double oldvalue, double newvalue, QString name=0); + void execute(); + void unexecute(); +private: + StandardWorktime *swt; + double m_oldvalue; + double m_newvalue; +}; + +class ModifyStandardWorktimeMonthCmd : public NamedCommand +{ +public: + ModifyStandardWorktimeMonthCmd(Part *part, StandardWorktime *wt, double oldvalue, double newvalue, QString name=0); + void execute(); + void unexecute(); +private: + StandardWorktime *swt; + double m_oldvalue; + double m_newvalue; +}; + +class ModifyStandardWorktimeWeekCmd : public NamedCommand +{ +public: + ModifyStandardWorktimeWeekCmd(Part *part, StandardWorktime *wt, double oldvalue, double newvalue, QString name=0); + void execute(); + void unexecute(); +private: + StandardWorktime *swt; + double m_oldvalue; + double m_newvalue; +}; + +class ModifyStandardWorktimeDayCmd : public NamedCommand +{ +public: + ModifyStandardWorktimeDayCmd(Part *part, StandardWorktime *wt, double oldvalue, double newvalue, QString name=0); + void execute(); + void unexecute(); +private: + StandardWorktime *swt; + double m_oldvalue; + double m_newvalue; +}; + + +} //KPlato namespace + +#endif //COMMAND_H diff --git a/kplato/kptconfig.cc b/kplato/kptconfig.cc new file mode 100644 index 00000000..fc7945e4 --- /dev/null +++ b/kplato/kptconfig.cc @@ -0,0 +1,87 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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. +*/ + +#include "kptconfig.h" + +#include "kptfactory.h" + +#include <kconfig.h> +#include <kdebug.h> + +namespace KPlato +{ + +Config::Config() +{ + m_readWrite = true; +} + +Config::~Config() +{ +} + +void Config::load() { + //kdDebug()<<k_funcinfo<<endl; + KConfig *config = Factory::global()->config(); + +/* if( config->hasGroup("Behavior")) + { + config->setGroup("Behavior"); + m_behavior.calculationMode = config->readNumEntry("CalculationMode",m_behavior.calculationMode); + m_behavior.allowOverbooking = config->readBoolEntry("AllowOverbooking",m_behavior.allowOverbooking); + }*/ + if( config->hasGroup("Task defaults")) + { + config->setGroup("Task defaults"); + m_taskDefaults.setLeader(config->readEntry("Leader")); + m_taskDefaults.setDescription(config->readEntry("Description")); + m_taskDefaults.setConstraint((Node::ConstraintType)config->readNumEntry("ConstraintType")); + m_taskDefaults.setConstraintStartTime(config->readDateTimeEntry("ConstraintStartTime")); + m_taskDefaults.setConstraintEndTime(config->readDateTimeEntry("ConstraintEndTime")); + m_taskDefaults.effort()->setType((Effort::Type)config->readNumEntry("EffortType")); + m_taskDefaults.effort()->set(Duration((Q_INT64)config->readNumEntry("ExpectedEffort"))); + m_taskDefaults.effort()->setPessimisticRatio(config->readNumEntry("PessimisticEffort")); + m_taskDefaults.effort()->setOptimisticRatio(config->readNumEntry("OptimisticEffort")); + } +} + +void Config::save() { + //kdDebug()<<k_funcinfo<<m_readWrite<<endl; + if (!m_readWrite) + return; + + KConfig *config = Factory::global()->config(); + +// config->setGroup( "Behavior" ); +// config->writeEntry("CalculationMode",m_behavior.calculationMode); +// config->writeEntry("AllowOverbooking",m_behavior.allowOverbooking); + + config->setGroup("Task defaults"); + config->writeEntry("Leader", m_taskDefaults.leader()); + config->writeEntry("Description", m_taskDefaults.description()); + config->writeEntry("ConstraintType", m_taskDefaults.constraint()); + config->writeEntry("ConstraintStartTime", m_taskDefaults.constraintStartTime()); + config->writeEntry("ConstraintEndTime", m_taskDefaults.constraintEndTime()); + config->writeEntry("EffortType", m_taskDefaults.effort()->type()); + config->writeEntry("ExpectedEffort", m_taskDefaults.effort()->expected().seconds()); //FIXME + config->writeEntry("PessimisticEffort", m_taskDefaults.effort()->pessimisticRatio()); + config->writeEntry("OptimisticEffort", m_taskDefaults.effort()->optimisticRatio()); +} + +} //KPlato namespace diff --git a/kplato/kptconfig.h b/kplato/kptconfig.h new file mode 100644 index 00000000..7b64a1aa --- /dev/null +++ b/kplato/kptconfig.h @@ -0,0 +1,61 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef KPTCONFIG_H +#define KPTCONFIG_H + +#include "kpttask.h" + +namespace KPlato +{ + +// class Behavior { +// public: +// enum CalculationMode { Manual, OnChange }; +// Behavior() { +// calculationMode = Manual; +// allowOverbooking = true; +// } +// int calculationMode; +// bool allowOverbooking; +// }; + +class Config { +public: + + Config(); + ~Config(); + + void setReadWrite(bool readWrite) { m_readWrite = readWrite; } + void load(); + void save(); + + Task &taskDefaults() { return m_taskDefaults; } +// Behavior &behavior() { return m_behavior; } + +private: + bool m_readWrite; +// Behavior m_behavior; + Task m_taskDefaults; + +}; + +} //KPlato namespace + +#endif // CONFIG_H diff --git a/kplato/kptconfigbehaviorpanel.cc b/kplato/kptconfigbehaviorpanel.cc new file mode 100644 index 00000000..d7724237 --- /dev/null +++ b/kplato/kptconfigbehaviorpanel.cc @@ -0,0 +1,72 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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. +*/ + +#include "kptconfigbehaviorpanel.h" + +#include "kptdatetime.h" +#include "kptfactory.h" + +#include <kmessagebox.h> +#include <klineedit.h> +#include <ktextedit.h> +#include <kcombobox.h> +#include <kdatetimewidget.h> +#include <klocale.h> +#include <kcommand.h> +#include <kconfig.h> +#include <kstandarddirs.h> +#include <kdebug.h> + +#include <qlayout.h> +#include <qdatetime.h> +#include <qbuttongroup.h> +#include <qcheckbox.h> + +namespace KPlato +{ + +ConfigBehaviorPanel::ConfigBehaviorPanel(Behavior &behavior, QWidget *p, const char *n) + : ConfigBehaviorPanelBase(p, n), + m_oldvalues(behavior), + m_behavior(behavior) +{ + setStartValues(); + + allowOverbooking->setEnabled(false); // not yet used +} + +void ConfigBehaviorPanel::setStartValues() { + calculationGroup->setButton(m_oldvalues.calculationMode); + allowOverbooking->setChecked(m_oldvalues.allowOverbooking); +} + +bool ConfigBehaviorPanel::ok() { + return true; +} + +bool ConfigBehaviorPanel::apply() { + m_behavior.calculationMode = calculationGroup->selectedId(); + m_behavior.allowOverbooking = allowOverbooking->isChecked(); + return true; +} + + +} //KPlato namespace + +#include "kptconfigbehaviorpanel.moc" diff --git a/kplato/kptconfigbehaviorpanel.h b/kplato/kptconfigbehaviorpanel.h new file mode 100644 index 00000000..167810a6 --- /dev/null +++ b/kplato/kptconfigbehaviorpanel.h @@ -0,0 +1,51 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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 KPTCONFIGBEHAVIORPANEL_H +#define KPTCONFIGBEHAVIORPANEL_H + +#include "kptconfigbehaviorpanelbase.h" +#include "kptconfig.h" + +class KMacroCommand; + +namespace KPlato +{ + +class ConfigBehaviourPanelBase; +class Part; + +class ConfigBehaviorPanel : public ConfigBehaviorPanelBase { + Q_OBJECT +public: + ConfigBehaviorPanel(Behavior &behavior, QWidget *parent=0, const char *name=0); + + void setStartValues(); + bool ok(); + bool apply(); + +private: + Behavior m_oldvalues; + Behavior &m_behavior; + +}; + +} //KPlato namespace + +#endif // CONFIGBEHAVIORPANEL_H diff --git a/kplato/kptconfigbehaviorpanelbase.ui b/kplato/kptconfigbehaviorpanelbase.ui new file mode 100644 index 00000000..33eb4129 --- /dev/null +++ b/kplato/kptconfigbehaviorpanelbase.ui @@ -0,0 +1,102 @@ +<!DOCTYPE UI><UI version="3.2" stdsetdef="1"> +<class>ConfigBehaviorPanelBase</class> +<author>Dag Andersen <danders@get2net.dk></author> +<widget class="QWidget"> + <property name="name"> + <cstring>ConfigBehaviorPanelBase</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>307</width> + <height>163</height> + </rect> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QButtonGroup"> + <property name="name"> + <cstring>calculationGroup</cstring> + </property> + <property name="title"> + <string>Calculation</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QRadioButton"> + <property name="name"> + <cstring>calcManual</cstring> + </property> + <property name="text"> + <string>Manual</string> + </property> + </widget> + <widget class="QRadioButton"> + <property name="name"> + <cstring>calcImmedate</cstring> + </property> + <property name="text"> + <string>Immediate on change</string> + </property> + </widget> + </vbox> + </widget> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBox2</cstring> + </property> + <property name="title"> + <string>Constraints</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QCheckBox"> + <property name="name"> + <cstring>allowOverbooking</cstring> + </property> + <property name="text"> + <string>Allow overbooking of resources</string> + </property> + </widget> + </vbox> + </widget> + </vbox> +</widget> +<connections> + <connection> + <sender>calcManual</sender> + <signal>toggled(bool)</signal> + <receiver>ConfigBehaviorPanelBase</receiver> + <slot>slotChanged()</slot> + </connection> + <connection> + <sender>calcImmedate</sender> + <signal>toggled(bool)</signal> + <receiver>ConfigBehaviorPanelBase</receiver> + <slot>slotChanged()</slot> + </connection> + <connection> + <sender>allowOverbooking</sender> + <signal>toggled(bool)</signal> + <receiver>ConfigBehaviorPanelBase</receiver> + <slot>slotChanged()</slot> + </connection> +</connections> +<includes> + <include location="local" impldecl="in implementation">kptconfigbehaviorpanelbase.ui.h</include> +</includes> +<signals> + <signal>changed()</signal> +</signals> +<slots> + <slot>slotChanged()</slot> +</slots> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/kplato/kptconfigbehaviorpanelbase.ui.h b/kplato/kptconfigbehaviorpanelbase.ui.h new file mode 100644 index 00000000..8db0f361 --- /dev/null +++ b/kplato/kptconfigbehaviorpanelbase.ui.h @@ -0,0 +1,35 @@ +/**************************************************************************** +** ui.h extension file, included from the uic-generated form implementation. +** +** If you want to add, delete, or rename functions or slots, use +** Qt Designer to update this file, preserving your code. +** +** You should not define a constructor or destructor in this file. +** Instead, write your code in functions called init() and destroy(). +** These will automatically be called by the form's constructor and +** destructor. +*****************************************************************************/ + +/* This file is part of the KDE project + Copyright (C) 2004 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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. +*/ + +void ConfigBehaviorPanelBase::slotChanged() +{ + emit changed(); +} diff --git a/kplato/kptconfigdialog.cc b/kplato/kptconfigdialog.cc new file mode 100644 index 00000000..f6cb8ae4 --- /dev/null +++ b/kplato/kptconfigdialog.cc @@ -0,0 +1,102 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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. +*/ + +#include "kptconfigdialog.h" + +#include "kpttaskdefaultpanel.h" +//#include "kptconfigbehaviorpanel.h" + +#include "kptconfig.h" +#include "kptproject.h" +#include "kpttask.h" +#include "kptcalendar.h" + +#include <kiconloader.h> +#include <klocale.h> +#include <kcommand.h> + +#include <qvbox.h> + +#include <kdebug.h> + +namespace KPlato +{ + +// little helper stolen from kmail/kword +static inline QPixmap loadIcon( const char * name ) { + return KGlobal::instance()->iconLoader() + ->loadIcon( QString::fromLatin1(name), KIcon::NoGroup, KIcon::SizeMedium ); +} + + +ConfigDialog::ConfigDialog(Config &config, Project &project, QWidget *parent, const char *) + : KDialogBase(KDialogBase::IconList, i18n("Configure KPlato"), + KDialogBase::Ok | KDialogBase::Apply | KDialogBase::Cancel| KDialogBase::Default, + KDialogBase::Ok, parent), + m_config(config) +{ + +/* QVBox *page = addVBoxPage(i18n("Behavior"), i18n("Behavior"), loadIcon("misc")); + m_behaviorPage = new ConfigBehaviorPanel(config.behavior(), page);*/ + + QVBox *page = addVBoxPage(i18n("Task Defaults"), i18n("Task Defaults"), loadIcon("misc")); + m_taskDefaultPage = new TaskDefaultPanel(config.taskDefaults(), project.standardWorktime(), page); + + enableButtonOK(false); + enableButtonApply(false); + +// connect(m_behaviorPage, SIGNAL(changed()), SLOT(slotChanged())); + connect(m_taskDefaultPage, SIGNAL(changed()), SLOT(slotChanged())); +} + + +void ConfigDialog::slotApply() { + if (!m_taskDefaultPage->ok()) + return; +/* if (!m_behaviorPage->ok()) + return;*/ + KCommand *cmd = m_taskDefaultPage->buildCommand(0); + if (cmd) + cmd->execute(); + +// m_behaviorPage->apply(); +} + +void ConfigDialog::slotOk() { + slotApply(); + accept(); +} + +void ConfigDialog::slotDefault() { + kdDebug()<<k_funcinfo<<endl; + m_taskDefaultPage->setStartValues(m_config.taskDefaults()); +// m_behaviorPage->setStartValues(); + + enableButtonOK(false); + enableButtonApply(false); +} + +void ConfigDialog::slotChanged() { + enableButtonOK(true); + enableButtonApply(true); +} + +} //KPlato namespace + +#include "kptconfigdialog.moc" diff --git a/kplato/kptconfigdialog.h b/kplato/kptconfigdialog.h new file mode 100644 index 00000000..1b2004b5 --- /dev/null +++ b/kplato/kptconfigdialog.h @@ -0,0 +1,55 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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 KPTCONFIGDIALOG_H +#define KPTCONFIGDIALOG_H + +#include <kdialogbase.h> + +class QWidget; + +namespace KPlato +{ + +class Project; +class TaskDefaultPanel; +//class ConfigBehaviorPanel; +class Config; + +class ConfigDialog : public KDialogBase { + Q_OBJECT +public: + ConfigDialog(Config &config, Project &project, QWidget *parent=0, const char *name=0); + +protected slots: + void slotApply(); + void slotOk(); + void slotDefault(); + void slotChanged(); + +private: + Config &m_config; + TaskDefaultPanel *m_taskDefaultPage; + //ConfigBehaviorPanel *m_behaviorPage; + +}; + +} //KPlato namespace + +#endif // CONFIGDIALOG_H diff --git a/kplato/kptconfigtaskpanelbase.ui b/kplato/kptconfigtaskpanelbase.ui new file mode 100644 index 00000000..0cd3818c --- /dev/null +++ b/kplato/kptconfigtaskpanelbase.ui @@ -0,0 +1,361 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>KPlato::ConfigTaskPanelBase</class> +<author>Dag Andersen <danders@get2net.dk></author> +<widget class="QWidget"> + <property name="name"> + <cstring>ConfigTaskPanelBase</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>635</width> + <height>337</height> + </rect> + </property> + <property name="caption"> + <string></string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout7</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>leaderlabel</cstring> + </property> + <property name="text"> + <string>Responsible:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>leaderfield</cstring> + </property> + <property name="whatsThis" stdset="0"> + <string>The person responsible for this task. + +This is not limited to persons available in a resource group but can be anyone. You can even directly access your address book with the Choose button.</string> + </property> + </widget> + <widget class="KLineEdit"> + <property name="name"> + <cstring>leaderfield</cstring> + </property> + <property name="whatsThis" stdset="0"> + <string>The person responsible for this task. + +This is not limited to persons available in a resource group but can be anyone. You can even directly access your address book with the Choose button.</string> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>chooseLeader</cstring> + </property> + <property name="text"> + <string>Choose...</string> + </property> + <property name="accel"> + <string></string> + </property> + <property name="toolTip" stdset="0"> + <string>Insert a person from your address book.</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Insert a person from your address book.</string> + </property> + </widget> + </hbox> + </widget> + <widget class="QGroupBox"> + <property name="name"> + <cstring>schedulingGroup</cstring> + </property> + <property name="title"> + <string>Timing</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Scheduling Configuration. These settings affect the actual scheduling of the task. + +The estimation can be either effort based or duration based. If it is effort based, the final duration will depend on the resources assigned to the task. For duration based estimation, the assigned resources don't affect the fixed duration of the task, but only the costs.</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KComboBox" row="2" column="1"> + <item> + <property name="text"> + <string>Effort</string> + </property> + </item> + <item> + <property name="text"> + <string>Duration</string> + </property> + </item> + <property name="name"> + <cstring>estimateType</cstring> + </property> + <property name="editable"> + <bool>false</bool> + </property> + <property name="urlDropsEnabled" stdset="0"> + <bool>false</bool> + </property> + </widget> + <widget class="KComboBox" row="0" column="1"> + <item> + <property name="text"> + <string>As Soon as Possible</string> + </property> + </item> + <item> + <property name="text"> + <string>As Late as Possible</string> + </property> + </item> + <item> + <property name="text"> + <string>Must Start On</string> + </property> + </item> + <item> + <property name="text"> + <string>Must Finish On</string> + </property> + </item> + <item> + <property name="text"> + <string>Start Not Earlier Than</string> + </property> + </item> + <item> + <property name="text"> + <string>Finish Not Later Than</string> + </property> + </item> + <item> + <property name="text"> + <string>Fixed Interval</string> + </property> + </item> + <property name="name"> + <cstring>scheduleType</cstring> + </property> + </widget> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel3</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>0</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Schedule:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>scheduleType</cstring> + </property> + </widget> + <widget class="QLabel" row="2" column="0"> + <property name="name"> + <cstring>textLabel3_2</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>0</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Estimate:</string> + </property> + </widget> + <widget class="KDateWidget" row="0" column="2"> + <property name="name"> + <cstring>scheduleStartDate</cstring> + </property> + <property name="focusPolicy"> + <enum>WheelFocus</enum> + </property> + <property name="date"> + <date> + <year>2000</year> + <month>1</month> + <day>1</day> + </date> + </property> + </widget> + <widget class="QTimeEdit" row="0" column="3"> + <property name="name"> + <cstring>scheduleStartTime</cstring> + </property> + </widget> + <widget class="KDateWidget" row="1" column="2"> + <property name="name"> + <cstring>scheduleEndDate</cstring> + </property> + <property name="focusPolicy"> + <enum>WheelFocus</enum> + </property> + </widget> + <widget class="QTimeEdit" row="1" column="3"> + <property name="name"> + <cstring>scheduleEndTime</cstring> + </property> + </widget> + <widget class="KPlato::DurationWidget" row="2" column="2" rowspan="1" colspan="2"> + <property name="name"> + <cstring>estimate</cstring> + </property> + <property name="focusPolicy"> + <enum>WheelFocus</enum> + </property> + </widget> + <widget class="QLayoutWidget" row="3" column="2" rowspan="1" colspan="2"> + <property name="name"> + <cstring>layout41</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1_2</cstring> + </property> + <property name="text"> + <string>Optimistic:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>optimisticValueoptimisticValue</cstring> + </property> + </widget> + <widget class="QSpinBox"> + <property name="name"> + <cstring>optimisticValue</cstring> + </property> + <property name="suffix"> + <string>%</string> + </property> + <property name="maxValue"> + <number>0</number> + </property> + <property name="minValue"> + <number>-99</number> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel2_2</cstring> + </property> + <property name="text"> + <string>Pessimistic:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>pessimisticValue</cstring> + </property> + </widget> + <widget class="QSpinBox"> + <property name="name"> + <cstring>pessimisticValue</cstring> + </property> + <property name="suffix"> + <string>%</string> + </property> + <property name="maxValue"> + <number>999</number> + </property> + </widget> + </hbox> + </widget> + </grid> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>descriptionlabell6</cstring> + </property> + <property name="text"> + <string>Note:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>descriptionfield</cstring> + </property> + </widget> + <widget class="KTextEdit"> + <property name="name"> + <cstring>descriptionfield</cstring> + </property> + </widget> + </vbox> +</widget> +<customwidgets> + <customwidget> + <class>KPlato::DurationWidget</class> + <header location="local">kptdurationwidget.h</header> + <sizehint> + <width>0</width> + <height>20</height> + </sizehint> + <container>0</container> + <sizepolicy> + <hordata>5</hordata> + <verdata>0</verdata> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + <pixmap>image0</pixmap> + <signal>valueChanged()</signal> + <slot access="public" specifier="">slot()</slot> + <slot access="public" specifier="">setValue( const KPLato::Duration & newDuration )</slot> + <slot access="public" specifier="">slot()</slot> + <slot access="public" specifier="">handleFocus( int field )</slot> + </customwidget> +</customwidgets> +<images> + <image name="image0"> + <data format="PNG" length="1002">89504e470d0a1a0a0000000d4948445200000016000000160806000000c4b46c3b000003b149444154789cad945f4c5b551cc73fe7dc4b7b4bcba0762d45c43114323599ee6192609c51d883892ce083f1718b3ebb185f8dc91e972cf39d2d2a2f1af664b6f1e0fe3863a0718969700eb0c52142da0242a1bd6d696f7bcff101585203ceb8fd9ece39f99dcff9fe7edf939f88c562ec465f5f9fe609442c161362173c3e3eae7b7a7ac8e7f36432196cdbfe4f907c3e4f2291201e8fe338cec3737357e9e8e828aded1e229d650e1f2d51754b082110124c13a4dc5ea341eb9dc284c0558a853f3ce8cb0677ef500fde7d39d2596679e326597b8e9abb85d7a770ab16ab6983ec5a05b487a70e36f0f4e10afe408d6a558310980108478dba4a1e8233990c5d474b64ed39aa3a8fe5f3317fbf81dbd70bccfeb205947632fd74f6589c1c6ea2f70d03a58ba0c1f2c9bdc1b66de3b8256a6e11cbe7e3ee1d181b590124fe2693aeee08d223c82c3a2c24b7b874bec8f26288774f7bd054504aef0dde6e99c0eb83f9fb266323cb80a27fb0958141836044605a2ee5523393371cc646fee2da37195aa35d0c0c5b4859ac03d7e91712dcaac5adab3650a3ff9d08ef7dd8404bb48869e5d958b5b87dadc4c9a1464e9f0d0326df7ebd86bd2e310cb1bf62d384d59441f2d70a070e1c60e09489929b988681bdd9cc97170bcc4c65595f71f8e0e3301337fc24a7732467831875a47f289652b0be5e4151e6d07316c1b0c0340d8ab92023e76d66a6b2840e36d2fb7a13fee632475e6edc367ea98a90fb98b7dd6310ca0328a44761582e1bab41befabcc0ec940d28bc5e93b68e064cab84e1d9beaeb48934eac1f53b01c1b000fca496aa54b61a99fcde61662a4b4b4b23d1680be9d426173e4df3602a48ea411989a4fd590f52a8fd156b05ed9d350e3defe3cfdf4b4c7ce770ea7d3fb9f520afbe1620daeee5c26735d20b9b9cfb6811a754a439e4e5c5639a4caa1e5caf586bfc0197b78702005cb9b4cae4cd3267ce8638fe964bd72b393e39d74928d242617303a756a37f284447770dcdbffc6384a05a85de1306e9a52057c7527c7131c3c42d3f475eb2303c82d4fc3276d6811db37efeb148723082d9b08f79f97c1e5729109a9a28307cc622d2d6cdf52b2b24efe548dedb00142009862cfa879ee1a71f6cec928353511472fbf4389148b0b0e0c108081412458dfe21c9f11351e67e7358595468246d1d1e5e38a6e9e851bc39d84ab502a669331dafec0d8ec7e3e8cb06e1a881d727d1ae40180a434a8c9db129a54126ad48a7358c2b4c5352c8c374bcccdab2bb37d8719cba79fab8211f9df218e0582c261e95f8bfc04f1a1e8bc5c4dfe0a19017a725d8c60000000049454e44ae426082</data> + </image> +</images> +<tabstops> + <tabstop>leaderfield</tabstop> + <tabstop>chooseLeader</tabstop> + <tabstop>scheduleType</tabstop> + <tabstop>scheduleStartDate</tabstop> + <tabstop>scheduleStartTime</tabstop> + <tabstop>scheduleEndDate</tabstop> + <tabstop>scheduleEndTime</tabstop> + <tabstop>estimateType</tabstop> + <tabstop>estimate</tabstop> + <tabstop>optimisticValue</tabstop> + <tabstop>pessimisticValue</tabstop> + <tabstop>descriptionfield</tabstop> +</tabstops> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>klineedit.h</includehint> + <includehint>kcombobox.h</includehint> + <includehint>kcombobox.h</includehint> + <includehint>kdatewidget.h</includehint> + <includehint>kdatewidget.h</includehint> + <includehint>kptdurationwidget.h</includehint> + <includehint>ktextedit.h</includehint> +</includehints> +</UI> diff --git a/kplato/kptcontext.cc b/kplato/kptcontext.cc new file mode 100644 index 00000000..bdcd7e79 --- /dev/null +++ b/kplato/kptcontext.cc @@ -0,0 +1,163 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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. +*/ + +#include "kptcontext.h" + +#include <qdom.h> + +#include <kdebug.h> + +namespace KPlato +{ + +Context::Context() + : currentEstimateType(0), + currentSchedule(0) { +} + +Context::~Context() { +} + +bool Context::load(QDomElement &element) { + currentView = element.attribute("current-view"); + currentEstimateType = element.attribute("estimate-type").toInt(); + currentSchedule = element.attribute("current-schedule").toLong(); + actionViewExpected = element.attribute("view-expected").toInt(); + actionViewOptimistic = element.attribute("view-optimistic").toInt(); + actionViewPessimistic = element.attribute("view-pessimistic").toInt(); + + QDomNodeList list = element.childNodes(); + for (unsigned int i=0; i<list.count(); ++i) { + if (list.item(i).isElement()) { + QDomElement e = list.item(i).toElement(); + if (e.tagName() == "gantt-view") { + ganttview.ganttviewsize = e.attribute("ganttview-size").toInt(); + ganttview.taskviewsize = e.attribute("taskview-size").toInt(); + ganttview.currentNode = e.attribute("current-node"); + ganttview.showResources = e.attribute("show-resources").toInt(); + ganttview.showTaskName = e.attribute("show-taskname").toInt(); + ganttview.showTaskLinks = e.attribute("show-tasklinks").toInt(); + ganttview.showProgress = e.attribute("show-progress").toInt(); + ganttview.showPositiveFloat = e.attribute("show-positivefloat").toInt(); + ganttview.showCriticalTasks = e.attribute("show-criticaltasks").toInt(); + ganttview.showCriticalPath = e.attribute("show-criticalpath").toInt(); + ganttview.showNoInformation = e.attribute("show-noinformation").toInt(); + + QDomNodeList list = e.childNodes(); + for (unsigned int i=0; i<list.count(); ++i) { + if (list.item(i).isElement()) { + QDomElement g = list.item(i).toElement(); + if (g.tagName() == "closed-nodes") { + QDomNodeList list = g.childNodes(); + for (unsigned int i=0; i<list.count(); ++i) { + if (list.item(i).isElement()) { + QDomElement ei = list.item(i).toElement(); + if (ei.tagName() == "node") { + ganttview.closedNodes.append(ei.attribute("id")); + } + } + } + } + } + } + } else if (e.tagName() == "accounts-view") { + accountsview.accountsviewsize = e.attribute("accountsview-size").toInt(); + accountsview.periodviewsize = e.attribute("periodview-size").toInt(); + accountsview.date = QDate::fromString(e.attribute("date"), Qt::ISODate); + accountsview.period = e.attribute("period").toInt(); + accountsview.cumulative = e.attribute("cumulative").toInt(); + + QDomNodeList list = e.childNodes(); + for (unsigned int i=0; i<list.count(); ++i) { + if (list.item(i).isElement()) { + QDomElement g = list.item(i).toElement(); + if (g.tagName() == "closed-items") { + QDomNodeList list = g.childNodes(); + for (unsigned int i=0; i<list.count(); ++i) { + if (list.item(i).isElement()) { + QDomElement ei = list.item(i).toElement(); + if (ei.tagName() == "account") { + accountsview.closedItems.append(ei.attribute("name")); + } + } + } + } + } + } + } else { + kdError()<<k_funcinfo<<"Unknown tag: "<<e.tagName()<<endl; + } + } + } + + return true; +} + +void Context::save(QDomElement &element) const { + QDomElement me = element.ownerDocument().createElement("context"); + element.appendChild(me); + me.setAttribute("current-view", currentView); + me.setAttribute("estimate-type", currentEstimateType); + me.setAttribute("current-schedule", currentSchedule); + me.setAttribute("view-expected", actionViewExpected); + me.setAttribute("view-optimistic", actionViewOptimistic); + me.setAttribute("view-pessimistic", actionViewPessimistic); + // Ganttview + QDomElement g = me.ownerDocument().createElement("gantt-view"); + me.appendChild(g); + g.setAttribute("ganttview-size", ganttview.ganttviewsize); + g.setAttribute("taskview-size", ganttview.taskviewsize); + g.setAttribute("current-node", ganttview.currentNode); + g.setAttribute("show-resources", ganttview.showResources); + g.setAttribute("show-taskname", ganttview.showTaskName); + g.setAttribute("show-tasklinks", ganttview.showTaskLinks); + g.setAttribute("show-progress", ganttview.showProgress); + g.setAttribute("show-positivefloat", ganttview.showPositiveFloat); + g.setAttribute("show-criticaltasks", ganttview.showCriticalTasks); + g.setAttribute("show-criticalpath", ganttview.showCriticalPath); + g.setAttribute("show-noinformation", ganttview.showNoInformation); + if (!ganttview.closedNodes.isEmpty()) { + QDomElement e = g.ownerDocument().createElement("closed-nodes"); + g.appendChild(e); + for (QStringList::ConstIterator it = ganttview.closedNodes.begin(); it != ganttview.closedNodes.end(); ++it) { + QDomElement c = e.ownerDocument().createElement("node"); + e.appendChild(c); + c.setAttribute("id", (*it)); + } + } + // Accountsview + QDomElement a = me.ownerDocument().createElement("accounts-view"); + me.appendChild(a); + a.setAttribute("accountsview-size", accountsview.accountsviewsize); + a.setAttribute("periodview-size", accountsview.periodviewsize); + a.setAttribute("date", accountsview.date.toString(Qt::ISODate)); + a.setAttribute("period", accountsview.period); + a.setAttribute("cumulative", accountsview.cumulative); + if (!accountsview.closedItems.isEmpty()) { + QDomElement e = a.ownerDocument().createElement("closed-items"); + a.appendChild(e); + for (QStringList::ConstIterator it = accountsview.closedItems.begin(); it != accountsview.closedItems.end(); ++it) { + QDomElement c = e.ownerDocument().createElement("account"); + e.appendChild(c); + c.setAttribute("name", (*it)); + } + } +} + +} //KPlato namespace diff --git a/kplato/kptcontext.h b/kplato/kptcontext.h new file mode 100644 index 00000000..5ad7b8d7 --- /dev/null +++ b/kplato/kptcontext.h @@ -0,0 +1,87 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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 KPTCONTEXT_H +#define KPTCONTEXT_H + +#include <qdatetime.h> +#include <qstring.h> +#include <qstringlist.h> + +class QDomElement; + +namespace KPlato +{ + +class Context { +public: + Context(); + virtual ~Context(); + + virtual bool load(QDomElement &element); + virtual void save(QDomElement &element) const; + + + // View + QString currentView; + int currentEstimateType; + long currentSchedule; + bool actionViewExpected; + bool actionViewOptimistic; + bool actionViewPessimistic; + + struct Ganttview { + int ganttviewsize; + int taskviewsize; + QString currentNode; + bool showResources; + bool showTaskName; + bool showTaskLinks; + bool showProgress; + bool showPositiveFloat; + bool showCriticalTasks; + bool showCriticalPath; + bool showNoInformation; + QStringList closedNodes; + } ganttview; + + struct Pertview { + } pertview; + + struct Resourceview { + } resourceview; + + struct Accountsview { + int accountsviewsize; + int periodviewsize; + QDate date; + int period; + bool cumulative; + QStringList closedItems; + } accountsview; + + struct Reportview { + } reportview; + +}; + +} //KPlato namespace + +#endif //CONTEXT_H diff --git a/kplato/kptdatetable.cc b/kplato/kptdatetable.cc new file mode 100644 index 00000000..db49e657 --- /dev/null +++ b/kplato/kptdatetable.cc @@ -0,0 +1,1076 @@ +/* This file is part of the KDE project + Copyright (C) 1997 Tim D. Gilman (tdgilman@best.org) + (C) 1998-2001 Mirko Boehm (mirko@kde.org) + (C) 2004-2006 Dag Andersen <danders@get2net.dk> + + 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. +*/ + +#include "kptdatetable.h" +#include "kptmap.h" + +#include <kapplication.h> +#include <knotifyclient.h> +#include <kcalendarsystem.h> + +#include <qdatetime.h> +#include <qstring.h> +#include <qpen.h> +#include <qpainter.h> +#include <qdialog.h> +#include <assert.h> +#include <qlayout.h> +#include <qvaluelist.h> +#include <kglobalsettings.h> + +#include <kdebug.h> + +namespace KPlato +{ + +DateValidator::DateValidator(QWidget* parent, const char* name) + : QValidator(parent, name) +{ +} + +QValidator::State +DateValidator::validate(QString& text, int&) const +{ + QDate temp; + // ----- everything is tested in date(): + return date(text, temp); +} + +QValidator::State +DateValidator::date(const QString& text, QDate& d) const +{ + QDate tmp = KGlobal::locale()->readDate(text); + if (!tmp.isNull()) + { + d = tmp; + return Acceptable; + } else + return Valid; +} + +void +DateValidator::fixup( QString& ) const +{ + +} + + +DateTable::DateTable(QWidget *parent, QDate date_, const char* name, WFlags f) + : QGridView(parent, name, f), + m_enabled(true) +{ + //kdDebug()<<k_funcinfo<<endl; + m_dateStartCol = 1; + m_selectedDates.clear(); + m_selectedWeekdays.clear(); + + QPair<int, int> p(0,0); + m_weeks.fill(p, 7); + + setFontSize(10); + if(!date_.isValid()) { + kdError() <<k_funcinfo<<"Given date is invalid, using current date." << endl; + date_=QDate::currentDate(); + } + setFocusPolicy( QWidget::StrongFocus ); + setNumCols(7+m_dateStartCol); // 7 days a week + maybe 1 for weeknumbers + setNumRows(7); // 6 weeks max + headline + + setHScrollBarMode(AlwaysOff); + setVScrollBarMode(AlwaysOff); + viewport()->setEraseColor(KGlobalSettings::baseColor()); + setDate(date_); // this initializes firstday, numdays, numDaysPrevMonth + + colorBackgroundHoliday = QColor(0, 245, 255, QColor::Hsv); + //colorBackgroundHoliday = colorBackgroundHoliday.light(); + colorBackgroundWorkday = QColor(208, 230, 240, QColor::Hsv);; + //colorBackgroundWorkday = colorBackgroundWorkday.light(); + colorTextHoliday = black; + colorTextWorkday = black; + colorLine = black; + backgroundSelectColor = KGlobalSettings::highlightColor(); + penSelectColor=KGlobalSettings::baseColor(); + +} + +void DateTable::paintWeekday(QPainter *painter, int col) { + QRect rect; + int w=cellWidth(); + int h=cellHeight(); + + QFont font = KGlobalSettings::generalFont(); + font.setBold(true); + if (!m_enabled) + font.setItalic(true); + painter->setFont(font); + + int day = weekday(col); + + //kdDebug()<<k_funcinfo<<" col="<<col<<" day="<<day<<" name="<<daystr<<endl; + + painter->setBrush(KGlobalSettings::baseColor()); + painter->setPen(KGlobalSettings::baseColor()); + painter->drawRect(0, 0, w, h); + painter->setPen(KGlobalSettings::textColor()); + + if (m_markedWeekdays.state(day) == Map::Working) { + painter->setPen(colorBackgroundWorkday); + painter->setBrush(colorBackgroundWorkday); + painter->drawRect(0, 0, w, h); + painter->setPen(colorTextWorkday); + } else if (m_markedWeekdays.state(day) == Map::NonWorking) { + painter->setPen(colorBackgroundHoliday); + painter->setBrush(colorBackgroundHoliday); + painter->drawRect(0, 0, w, h); + painter->setPen(colorTextHoliday); + } + if (m_selectedWeekdays.contains(day)) { + painter->setPen(backgroundSelectColor); + painter->setBrush(backgroundSelectColor); + painter->drawRect(2, 2, w-4, h-4); + painter->setPen(penSelectColor); + } + painter->drawText(0, 0, w, h-1, AlignCenter, KGlobal::locale()->calendar()->weekDayName(day, true), -1, &rect); + painter->setPen(colorLine); + painter->moveTo(0, h-1); + painter->lineTo(w-1, h-1); + + if(rect.width()>maxCell.width()) maxCell.setWidth(rect.width()); + if(rect.height()>maxCell.height()) maxCell.setHeight(rect.height()); + + //kdDebug()<<k_funcinfo<<"headline: row,col=("<<row<<","<<col<<")"<<" day="<<daystr<<endl; +} + +void DateTable::paintWeekNumber(QPainter *painter, int row) { + QRect rect; + int w=cellWidth(); + int h=cellHeight(); + + QFont font=KGlobalSettings::generalFont(); + font.setBold(true); + if (!m_enabled) + font.setItalic(true); + painter->setFont(font); + + painter->setBrush(KGlobalSettings::baseColor()); + painter->setPen(KGlobalSettings::baseColor()); + painter->drawRect(0, 0, w, h); + painter->setPen(KGlobalSettings::textColor()); + + painter->drawText(0, 0, w, h-1, AlignCenter, QString("%1").arg(m_weeks[row].first), -1, &rect); + painter->setPen(colorLine); + painter->moveTo(w-1, 0); + painter->lineTo(w-1, h-1); + + if(rect.width()>maxCell.width()) maxCell.setWidth(rect.width()); + if(rect.height()>maxCell.height()) maxCell.setHeight(rect.height()); +} + +void DateTable::paintDay(QPainter *painter, int row, int col) { + //kdDebug()<<k_funcinfo<<"row,col=("<<row<<","<<col<<")"<<" num col="<<numCols()<<endl; + QRect rect; + int w=cellWidth(); + int h=cellHeight(); + + QFont font=KGlobalSettings::generalFont(); + font.setPointSize(fontsize); + if (!m_enabled) + font.setItalic(true); + painter->setFont(font); + + QDate d = getDate(position(row, col)); + + painter->setBrush(KGlobalSettings::baseColor()); + painter->setPen(KGlobalSettings::baseColor()); + painter->drawRect(0, 0, w, h); + + // First paint the dates background + if (m_markedDates.state(d) == Map::NonWorking) { + //kdDebug()<<k_funcinfo<<"Marked date: "<<d<<" row,col=("<<row<<","<<col<<")=NonWorking"<<endl; + painter->setPen(colorBackgroundHoliday); + painter->setBrush(colorBackgroundHoliday); + painter->drawRect(0, 0, w, h); + } else if (m_markedDates.state(d) == Map::Working) { + //kdDebug()<<k_funcinfo<<"Marked date: "<<d<<" row,col=("<<row<<","<<col<<")=Working"<<endl; + painter->setPen(colorBackgroundWorkday); + painter->setBrush(colorBackgroundWorkday); + painter->drawRect(0, 0, w, h); + } + if(m_selectedDates.contains(d)) { + //kdDebug()<<k_funcinfo<<"Selected: "<<d<<" row,col=("<<row<<","<<col<<")"<<endl; + painter->setPen(backgroundSelectColor); + painter->setBrush(backgroundSelectColor); + painter->drawRect(2, 2, w-4, h-4); + } + // If weeks or weekdays are selected/marked we draw lines around the date + QPen pen = painter->pen(); + if (m_markedWeekdays.state(weekday(col)) == Map::Working) { + //kdDebug()<<k_funcinfo<<"Marked weekday: row,dayCol=("<<row<<","<<dayCol<<")=Working"<<endl; + pen.setColor(colorBackgroundWorkday); + painter->setPen(pen); + painter->moveTo(0, 0); + painter->lineTo(0, h-1); + painter->moveTo(w-1, 0); + painter->lineTo(w-1, h-1); + } + // then paint square if current date + if (d == QDate::currentDate()) { + painter->setPen(colorLine); + painter->drawRect(1, 1, w-2, h-2); + } + + // and now the day number + d.month() == date.month() ? painter->setPen(KGlobalSettings::textColor()) : painter->setPen(gray); + painter->drawText(0, 0, w, h, AlignCenter, QString().setNum(d.day()), -1, &rect); + + if(rect.width()>maxCell.width()) maxCell.setWidth(rect.width()); + if(rect.height()>maxCell.height()) maxCell.setHeight(rect.height()); +} + +void DateTable::paintCell(QPainter *painter, int row, int col) { + //kdDebug()<<k_funcinfo<<"row,col=("<<row<<","<<col<<")"<<"enabled="<<m_enabled<<endl; + if (row == 0 && col == 0) { + painter->save(); + int w=cellWidth(); + int h=cellHeight(); + painter->setPen(colorLine); + painter->setBrush(KGlobalSettings::baseColor()); + painter->moveTo(w-1, 0); + painter->lineTo(w-1, h-1); + painter->lineTo(0, h-1); + painter->restore(); + return; + } + painter->save(); + if(row==0) { // we are drawing the weekdays + paintWeekday(painter, col); + } else if (col == 0) { // draw week numbers + paintWeekNumber(painter, row); + } else { // draw the day + paintDay(painter, row, col); + } + painter->restore(); +} + +//FIXME +void DateTable::keyPressEvent( QKeyEvent *e ) { + if (!m_enabled) + return; + if ( e->key() == Qt::Key_Prior ) { + setDate(date.addMonths(-1)); + return; + } + if ( e->key() == Qt::Key_Next ) { + setDate(date.addMonths(1)); + return; + } + + if ( e->key() == Qt::Key_Up ) { + if ( date.day() > 7 ) { + setDate(date.addDays(-7)); + return; + } + } + if ( e->key() == Qt::Key_Down ) { + if ( date.day() <= date.daysInMonth()-7 ) { + setDate(date.addDays(7)); + return; + } + } + if ( e->key() == Qt::Key_Left ) { + if ( date.day() > 1 ) { + setDate(date.addDays(-1)); + return; + } + } + if ( e->key() == Qt::Key_Right ) { + if ( date.day() < date.daysInMonth() ) { + setDate(date.addDays(1)); + return; + } + } + + if ( e->key() == Qt::Key_Minus ) { + setDate(date.addDays(-1)); + return; + } + if ( e->key() == Qt::Key_Plus ) { + setDate(date.addDays(1)); + return; + } + if ( e->key() == Qt::Key_N ) { + setDate(QDate::currentDate()); + return; + } + if ( e->key() == Qt::Key_Control ) { + return; + } + if ( e->key() == Qt::Key_Shift ) { + return; + } + + KNotifyClient::beep(); +} + +void DateTable::viewportResizeEvent(QResizeEvent * e) { + QGridView::viewportResizeEvent(e); + + setCellWidth(viewport()->width()/numCols()); + setCellHeight(viewport()->height()/numRows()); +} + +void DateTable::setFontSize(int size) { + int count; + QFontMetrics metrics(fontMetrics()); + QRect rect; + // ----- store rectangles: + fontsize=size; + // ----- find largest day name: + maxCell.setWidth(0); + maxCell.setHeight(0); + for(count=0; count<7; ++count) + { + rect=metrics.boundingRect(KGlobal::locale()->calendar()->weekDayName(count+1, true)); + maxCell.setWidth(QMAX(maxCell.width(), rect.width())); + maxCell.setHeight(QMAX(maxCell.height(), rect.height())); + } + // ----- compare with a real wide number and add some space: + rect=metrics.boundingRect(QString::fromLatin1("88")); + maxCell.setWidth(QMAX(maxCell.width()+2, rect.width())); + maxCell.setHeight(QMAX(maxCell.height()+4, rect.height())); +} + +//FIXME +void DateTable::wheelEvent ( QWheelEvent * e ) { + setDate(date.addMonths( -(int)(e->delta()/120)) ); + e->accept(); +} + + +void DateTable::contentsMousePressEvent(QMouseEvent *e) { + if (!m_enabled) + return; + //kdDebug()<<k_funcinfo<<endl; + if(e->type()!=QEvent::MouseButtonPress) { + return; + } + QPoint mouseCoord = e->pos(); + int row=rowAt(mouseCoord.y()); + int col=columnAt(mouseCoord.x()); + if (row == 0 && col == 0) { // user clicked on (unused) upper left square + updateSelectedCells(); + m_selectedWeekdays.clear(); + m_selectedDates.clear(); + repaintContents(false); + emit selectionCleared(); + return; + } + if (col == 0) { // user clicked on week numbers + updateSelectedCells(); + m_selectedWeekdays.clear(); + m_selectedDates.clear(); + updateSelectedCells(); + repaintContents(false); + return; + } + if (row==0 && col>0) { // the user clicked on weekdays + updateSelectedCells(); + m_selectedDates.clear(); + int day = weekday(col); + if (e->state() & ShiftButton) { + // select all days between this and the furthest away selected day, + // check first downside - then upside, clear all others + bool select = false; + for(int i=m_dateStartCol; i < col; ++i) { + //kdDebug()<<"Down["<<i<<"]: col="<<col<<" day="<<day<<" column(i)="<<column(i)<<endl; + if (m_selectedWeekdays.contains(weekday(i))) { + select = true; // we have hit a selected day; select the rest + } else if (select) { + m_selectedWeekdays.toggle(weekday(i)); // select + } + } + bool selected = select; + select = false; + for(int i=7; i > col; --i) { + //kdDebug()<<"Up["<<i<<"]: col="<<col<<" day="<<day<<" column(i)="<<column(i)<<endl; + if (m_selectedWeekdays.contains(weekday(i))) { + if (selected) m_selectedWeekdays.toggle(weekday(i)); // deselect + else select = true; + } else if (select) { + m_selectedWeekdays.toggle(weekday(i)); // select + } + } + if (!m_selectedWeekdays.contains(day)) { + m_selectedWeekdays.toggle(day); // always select + } + } else if (e->state() & ControlButton) { + // toggle select this date + m_selectedWeekdays.toggle(day); + } else { + // toggle select this, clear all others + m_selectedWeekdays.toggleClear(day); + } + updateSelectedCells(); + repaintContents(false); + if (m_enabled) { + //kdDebug()<<k_funcinfo<<"emit weekdaySelected("<<day<<")"<<endl; + emit weekdaySelected(day); // day= 1..7 + } + return; + } + + if (contentsMousePressEvent_internal(e)) { + // Date hit, + m_selectedWeekdays.clear(); + if (e->state() & ShiftButton) { + // find first&last date + QDate first; + QDate last; + DateMap::ConstIterator it; + for (it = m_selectedDates.constBegin(); it != m_selectedDates.constEnd(); ++it) { + //kdDebug()<<k_funcinfo<<it.key()<<endl; + QDate d = QDate::fromString(it.key(), Qt::ISODate); + if (!d.isValid()) + continue; + if (!first.isValid() || first > d) + first = d; + if (!last.isValid() || last < d) + last = d; + } + // select between anchor and pressed date inclusive + m_selectedDates.clear(); + if (first.isValid() && last.isValid()) { + QDate anchor = first < date ? first : last; + int i = anchor > date ? -1 : 1; + while (anchor != date) { + //kdDebug()<<k_funcinfo<<anchor.toString(Qt::ISODate)<<endl; + m_selectedDates.toggle(anchor); + anchor = anchor.addDays(i); + } + } + m_selectedDates.toggle(date); + } else if (e->state() & ControlButton) { + // toggle select this date + m_selectedDates.toggle(date); + //kdDebug()<<k_funcinfo<<"toggle date: "<<date.toString()<<" state="<<m_selectedDates.state(date)<<endl; + } else { + // Select this, clear all others + m_selectedDates.clear(); + m_selectedDates.toggleClear(date); + //kdDebug()<<k_funcinfo<<"toggleClear date: "<<date.toString()<<" state="<<m_selectedDates.state(date)<<endl; + } + } + repaintContents(false); +} + +bool DateTable::contentsMousePressEvent_internal(QMouseEvent *e) { + QPoint mouseCoord = e->pos(); + int row=rowAt(mouseCoord.y()); + int col=columnAt(mouseCoord.x()); + if(row<1 || col<0) { // the user clicked on the frame of the table + return false; + } + //kdDebug()<<k_funcinfo<<"pos["<<row<<","<<col<<"]="<<position(row,col)<<" firstday="<<firstday<<endl; + selectDate(getDate(position(row, col))); + return true; +} + +bool DateTable::selectDate(const QDate& date_) { + //kdDebug()<<k_funcinfo<<"date="<<date_.toString()<<endl; + bool changed=false; + QDate temp; + // ----- + if(!date_.isValid()) { + return false; + } + if(date!=date_) { + date=date_; + changed=true; + } + + temp.setYMD(date.year(), date.month(), 1); + firstday=column(KGlobal::locale()->calendar()->dayOfWeek(temp)); + if(firstday==1) firstday=8; // Reserve row 1 for previous month + numdays=date.daysInMonth(); + if(date.month()==1) { // set to december of previous year + temp.setYMD(date.year()-1, 12, 1); + setWeekNumbers(QDate(date.year()-1, 12, 31)); + } else { // set to previous month + temp.setYMD(date.year(), date.month()-1, 1); + QDate d(date.year(), date.month()-1,1); + setWeekNumbers(d.addDays(d.daysInMonth()-1)); + } + numDaysPrevMonth=temp.daysInMonth(); + if(changed) { + repaintContents(false); + } + if (m_enabled) + emit(dateChanged(date)); + return true; +} + +bool DateTable::setDate(const QDate& date_, bool repaint) { + //kdDebug()<<k_funcinfo<<"date="<<date_.toString()<<endl; + bool changed=false; + QDate temp; + // ----- + if(!date_.isValid()) { + //kdDebug() << "DateTable::setDate: refusing to set invalid date." << endl; + return false; + } + if(date!=date_) { + date=date_; + changed=true; + } + //m_selectedDates.clear(); + + temp.setYMD(date.year(), date.month(), 1); + firstday=column(KGlobal::locale()->calendar()->dayOfWeek(temp)); + if(firstday==1) firstday=8; + //kdDebug()<<k_funcinfo<<"date="<<temp<<"day="<<(KGlobal::locale()->calendar()->dayOfWeek(temp))<<" firstday="<<firstday<<endl; + numdays=date.daysInMonth(); + if(date.month()==1) { // set to december of previous year + temp.setYMD(date.year()-1, 12, 1); + setWeekNumbers(QDate(date.year()-1, 12, 31)); + } else { // set to previous month + temp.setYMD(date.year(), date.month()-1, 1); + QDate d(date.year(), date.month()-1,1); + setWeekNumbers(d.addDays(d.daysInMonth()-1)); + } +/* if (m_selectedWeekdays.isEmpty() && + !m_selectedDates.isEmpty() && !m_selectedDates.contains(date)) + { + //kdDebug()<<k_funcinfo<<"date inserted"<<endl; + m_selectedDates.insert(date); + }*/ + numDaysPrevMonth=temp.daysInMonth(); + if(changed && repaint) { + repaintContents(false); + } + if (m_enabled) + emit(dateChanged(date)); + return true; +} + +const QDate& DateTable::getDate() const { + return date; +} + +void DateTable::focusInEvent( QFocusEvent *e ) { + QGridView::focusInEvent( e ); +} + +void DateTable::focusOutEvent( QFocusEvent *e ) { + QGridView::focusOutEvent( e ); +} + +QSize DateTable::sizeHint() const { + if(maxCell.height()>0 && maxCell.width()>0) { + return QSize(maxCell.width()*numCols()+2*frameWidth(), + (maxCell.height()+2)*numRows()+2*frameWidth()); + } else { + //kdDebug() << "DateTable::sizeHint: obscure failure - " << endl; + return QSize(-1, -1); + } +} + +void DateTable::setWeekNumbers(QDate date) { + if (!date.isValid()) { + kdError()<<k_funcinfo<<"Invalid date"<<endl; + } + QDate d(date); + for (int i = 1; i < 7; ++i) { + m_weeks[i].first = d.weekNumber(&(m_weeks[i].second)); + //kdDebug()<<k_funcinfo<<"date="<<d.toString()<<" week=("<<m_weeks[i].first<<","<<m_weeks[i].second<<")"<<endl; + d = d.addDays(7); + } +} + +void DateTable::updateCells() { + //kdDebug()<<k_funcinfo<<endl; + for (int row=0; row < numRows(); ++row) { + for (int col=0; col < numCols(); ++col) { + updateCell(row, col); + } + } +} + +void DateTable::updateSelectedCells() { + //kdDebug()<<k_funcinfo<<endl; + QDate dt(date.year(), date.month(), 1); + dt = dt.addDays(-firstday); + for (int pos=0; pos < 42; ++pos) { + if (m_selectedDates.contains(dt.addDays(pos)) || + m_selectedWeekdays.contains(pos%7+1)) + { + updateCell(pos/7+1, pos%7+1); + //kdDebug()<<k_funcinfo<<" update cell ("<<pos/7+1<<","<<pos%7+1<<") date="<<dt.addDays(pos).toString()<<endl; + } + } +} + +void DateTable::updateMarkedCells() { + QDate dt(date.year(), date.month(), 1); + dt = dt.addDays(-firstday); + for (int pos=0; pos < 42; ++pos) { + if (m_markedDates.contains(dt.addDays(pos)) || + m_markedWeekdays.contains(pos%7+1)) + { + updateCell(pos/7+1, pos%7+1); + //kdDebug()<<k_funcinfo<<" update cell ("<<pos/7+1<<","<<pos%7+1<<") date="<<dt.addDays(pos).toString()<<endl; + } + } +} + +void DateTable::setMarkedWeekdays(const IntMap days) { + updateMarkedCells(); + m_markedWeekdays.clear(); + m_markedWeekdays = days; + updateMarkedCells(); + repaintContents(false); +} + +bool DateTable::weekdayMarked(int day) { + return m_markedWeekdays.contains(day); +} + +bool DateTable::dateMarked(QDate date) { + return m_markedDates[date.toString()]; +} + +QDate DateTable::getDate(int pos) const { + return QDate(date.year(), date.month(), 1).addDays(pos-firstday); +} + +int DateTable::weekday(int col) const { + int day = col - m_dateStartCol + KGlobal::locale()->weekStartDay(); + if (day > 7) day %= 7; + //kdDebug()<<k_funcinfo<<"col="<<col<<" day="<<day<<" StartCol="<<m_dateStartCol<<" weekStartDay="<<KGlobal::locale()->weekStartDay()<<endl; + return day; +} + +int DateTable::column(int weekday) const { + int col = weekday - KGlobal::locale()->weekStartDay(); + if (col < 0) col += 7; + //kdDebug()<<k_funcinfo<<"col="<<col<<" day="<<col<<" StartCol="<<m_dateStartCol<<" weekStartDay="<<KGlobal::locale()->weekStartDay()<<endl; + return col + m_dateStartCol; +} + +void DateTable::clear() { + clearSelection(); + m_markedDates.clear(); + m_markedWeekdays.clear(); + repaintContents(false); +} + +void DateTable::clearSelection() { + m_selectedDates.clear(); + m_selectedWeekdays.clear(); + repaintContents(false); +} + + void DateTable::setEnabled(bool yes) { + if (m_enabled == yes) + return; + m_enabled=yes; + updateCells(); +} + +void DateTable::markSelected(int state) { + if (!m_selectedDates.isEmpty()) { + DateMap::iterator it; + for(it = m_selectedDates.begin(); it != m_selectedDates.end(); ++it) { + m_markedDates.insert(it.key(), state); + //kdDebug()<<k_funcinfo<<"marked date: "<<it.key()<<"="<<state<<endl; + } + } else if (!m_selectedWeekdays.isEmpty()) { + IntMap::iterator it; + for(it = m_selectedWeekdays.begin(); it != m_selectedWeekdays.end(); ++it) { + m_markedWeekdays.insert(it.key(), state); + //kdDebug()<<k_funcinfo<<"marked weekday: "<<it.key()<<"="<<state<<endl; + } + } + updateSelectedCells(); + repaintContents(false); +} + +DateInternalWeekSelector::DateInternalWeekSelector +(int fontsize, QWidget* parent, const char* name) + : QLineEdit(parent, name), + val(new QIntValidator(this)), + result(0) +{ + QFont font; + // ----- + font=KGlobalSettings::generalFont(); + font.setPointSize(fontsize); + setFont(font); + setFrameStyle(QFrame::NoFrame); + val->setRange(1, 53); + setValidator(val); + connect(this, SIGNAL(returnPressed()), SLOT(weekEnteredSlot())); +} + +void +DateInternalWeekSelector::weekEnteredSlot() +{ + bool ok; + int week; + // ----- check if this is a valid week: + week=text().toInt(&ok); + if(!ok) + { + KNotifyClient::beep(); + return; + } + result=week; + emit(closeMe(1)); +} + +int +DateInternalWeekSelector::getWeek() const +{ + return result; +} + +void +DateInternalWeekSelector::setWeek(int week) +{ + QString temp; + // ----- + temp.setNum(week); + setText(temp); +} + +DateInternalMonthPicker::DateInternalMonthPicker +(int fontsize, QWidget* parent, const char* name) + : QGridView(parent, name), + result(0) // invalid +{ + QRect rect; + QFont font; + // ----- + activeCol = -1; + activeRow = -1; + font=KGlobalSettings::generalFont(); + font.setPointSize(fontsize); + setFont(font); + setHScrollBarMode(AlwaysOff); + setVScrollBarMode(AlwaysOff); + setFrameStyle(QFrame::NoFrame); + setNumRows(4); + setNumCols(3); + // enable to find drawing failures: + // setTableFlags(Tbl_clipCellPainting); + viewport()->setEraseColor(KGlobalSettings::baseColor()); // for consistency with the datepicker + // ----- find the preferred size + // (this is slow, possibly, but unfortunatly it is needed here): + QFontMetrics metrics(font); + for(int i=1; i <= 12; ++i) + { + rect=metrics.boundingRect(KGlobal::locale()->calendar()->monthName(i, false)); + if(max.width()<rect.width()) max.setWidth(rect.width()); + if(max.height()<rect.height()) max.setHeight(rect.height()); + } + +} + +QSize +DateInternalMonthPicker::sizeHint() const +{ + return QSize((max.width()+6)*numCols()+2*frameWidth(), + (max.height()+6)*numRows()+2*frameWidth()); +} + +int +DateInternalMonthPicker::getResult() const +{ + return result; +} + +void +DateInternalMonthPicker::setupPainter(QPainter *p) +{ + p->setPen(KGlobalSettings::textColor()); +} + +void +DateInternalMonthPicker::viewportResizeEvent(QResizeEvent*) +{ + setCellWidth(width()/3); + setCellHeight(height()/4); +} + +void +DateInternalMonthPicker::paintCell(QPainter* painter, int row, int col) +{ + int index; + QString text; + // ----- find the number of the cell: + index=3*row+col+1; + text=KGlobal::locale()->calendar()->monthName(index, false); + painter->drawText(0, 0, cellWidth(), cellHeight(), AlignCenter, text); + if ( activeCol == col && activeRow == row ) + painter->drawRect( 0, 0, cellWidth(), cellHeight() ); +} + +void +DateInternalMonthPicker::contentsMousePressEvent(QMouseEvent *e) +{ + if(!isEnabled() || e->button() != LeftButton) + { + KNotifyClient::beep(); + return; + } + // ----- + int row, col; + QPoint mouseCoord; + // ----- + mouseCoord = e->pos(); + row=rowAt(mouseCoord.y()); + col=columnAt(mouseCoord.x()); + + if(row<0 || col<0) + { // the user clicked on the frame of the table + activeCol = -1; + activeRow = -1; + } else { + activeCol = col; + activeRow = row; + updateCell( row, col /*, false */ ); + } +} + +void +DateInternalMonthPicker::contentsMouseMoveEvent(QMouseEvent *e) +{ + if (e->state() & LeftButton) + { + int row, col; + QPoint mouseCoord; + // ----- + mouseCoord = e->pos(); + row=rowAt(mouseCoord.y()); + col=columnAt(mouseCoord.x()); + int tmpRow = -1, tmpCol = -1; + if(row<0 || col<0) + { // the user clicked on the frame of the table + if ( activeCol > -1 ) + { + tmpRow = activeRow; + tmpCol = activeCol; + } + activeCol = -1; + activeRow = -1; + } else { + bool differentCell = (activeRow != row || activeCol != col); + if ( activeCol > -1 && differentCell) + { + tmpRow = activeRow; + tmpCol = activeCol; + } + if ( differentCell) + { + activeRow = row; + activeCol = col; + updateCell( row, col /*, false */ ); // mark the new active cell + } + } + if ( tmpRow > -1 ) // repaint the former active cell + updateCell( tmpRow, tmpCol /*, true */ ); + } +} + +void +DateInternalMonthPicker::contentsMouseReleaseEvent(QMouseEvent *e) +{ + if(!isEnabled()) + { + return; + } + // ----- + int row, col, pos; + QPoint mouseCoord; + // ----- + mouseCoord = e->pos(); + row=rowAt(mouseCoord.y()); + col=columnAt(mouseCoord.x()); + if(row<0 || col<0) + { // the user clicked on the frame of the table + emit(closeMe(0)); + } + pos=3*row+col+1; + result=pos; + emit(closeMe(1)); +} + + + +DateInternalYearSelector::DateInternalYearSelector +(int fontsize, QWidget* parent, const char* name) + : QLineEdit(parent, name), + val(new QIntValidator(this)), + result(0) +{ + QFont font; + // ----- + font=KGlobalSettings::generalFont(); + font.setPointSize(fontsize); + setFont(font); + setFrameStyle(QFrame::NoFrame); + // we have to respect the limits of QDate here, I fear: + val->setRange(0, 8000); + setValidator(val); + connect(this, SIGNAL(returnPressed()), SLOT(yearEnteredSlot())); +} + +void +DateInternalYearSelector::yearEnteredSlot() +{ + bool ok; + int year; + QDate date; + // ----- check if this is a valid year: + year=text().toInt(&ok); + if(!ok) + { + KNotifyClient::beep(); + return; + } + date.setYMD(year, 1, 1); + if(!date.isValid()) + { + KNotifyClient::beep(); + return; + } + result=year; + emit(closeMe(1)); +} + +int +DateInternalYearSelector::getYear() const +{ + return result; +} + +void +DateInternalYearSelector::setYear(int year) +{ + QString temp; + // ----- + temp.setNum(year); + setText(temp); +} + +PopupFrame::PopupFrame(QWidget* parent, const char* name) + : QFrame(parent, name, WType_Popup), + result(0), // rejected + main(0) +{ + setFrameStyle(QFrame::Box|QFrame::Raised); + setMidLineWidth(2); +} + +void +PopupFrame::keyPressEvent(QKeyEvent* e) +{ + if(e->key()==Key_Escape) + { + result=0; // rejected + qApp->exit_loop(); + } +} + +void +PopupFrame::close(int r) +{ + result=r; + qApp->exit_loop(); +} + +void +PopupFrame::setMainWidget(QWidget* m) +{ + main=m; + if(main!=0) + { + resize(main->width()+2*frameWidth(), main->height()+2*frameWidth()); + } +} + +void +PopupFrame::resizeEvent(QResizeEvent*) +{ + if(main!=0) + { + main->setGeometry(frameWidth(), frameWidth(), + width()-2*frameWidth(), height()-2*frameWidth()); + } +} + +void +PopupFrame::popup(const QPoint &pos) +{ + // Make sure the whole popup is visible. + QRect d = QApplication::desktop()->screenGeometry(QApplication::desktop()->screenNumber(pos)); + int x = pos.x(); + int y = pos.y(); + int w = width(); + int h = height(); + if (x+w > d.x()+d.width()) + x = d.width() - w; + if (y+h > d.y()+d.height()) + y = d.height() - h; + if (x < d.x()) + x = 0; + if (y < d.y()) + y = 0; + + // Pop the thingy up. + move(x, y); + show(); +} + +int +PopupFrame::exec(QPoint pos) +{ + popup(pos); + repaint(); + qApp->enter_loop(); + hide(); + return result; +} + +int +PopupFrame::exec(int x, int y) +{ + return exec(QPoint(x, y)); +} + +void PopupFrame::virtual_hook( int, void* ) +{ /*BASE::virtual_hook( id, data );*/ } + +void DateTable::virtual_hook( int, void* ) +{ /*BASE::virtual_hook( id, data );*/ } + +} //KPlato namespace + +#include "kptdatetable.moc" diff --git a/kplato/kptdatetable.h b/kplato/kptdatetable.h new file mode 100644 index 00000000..40576e8c --- /dev/null +++ b/kplato/kptdatetable.h @@ -0,0 +1,427 @@ +/* This file is part of the KDE project + Copyright (C) 1997 Tim D. Gilman (tdgilman@best.org) + (C) 1998-2001 Mirko Boehm (mirko@kde.org) + (C) 2004-2006 Dag Andersen <danders@get2net.dk> + + 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 KPTDATETABEL_H +#define KPTDATETABEL_H + +#include "kptmap.h" + +#include <kglobal.h> +#include <klocale.h> + +#include <qgridview.h> +#include <qmemarray.h> +#include <qdict.h> +#include <qpair.h> + +#include <qvalidator.h> +#include <qlineedit.h> +#include <qdatetime.h> + +namespace KPlato +{ + +/** Week selection widget. +* @internal +* @version $Id: kptdatetable.h 576264 2006-08-23 16:28:46Z danders $ +* @author Stephan Binner +*/ +class DateInternalWeekSelector : public QLineEdit +{ + Q_OBJECT +protected: + QIntValidator *val; + int result; +public slots: + void weekEnteredSlot(); +signals: + void closeMe(int); +public: + DateInternalWeekSelector(int fontsize, + QWidget* parent=0, + const char* name=0); + int getWeek() const; + void setWeek(int week); + +private: + class DateInternalWeekPrivate; + DateInternalWeekPrivate *d; +}; + +/** +* A table containing month names. It is used to pick a month directly. +* @internal +* @version $Id: kptdatetable.h 576264 2006-08-23 16:28:46Z danders $ +* @author Tim Gilman, Mirko Boehm +*/ +class DateInternalMonthPicker : public QGridView +{ + Q_OBJECT +protected: + /** + * Store the month that has been clicked [1..12]. + */ + int result; + /** + * the cell under mouse cursor when LBM is pressed + */ + short int activeCol; + short int activeRow; + /** + * Contains the largest rectangle needed by the month names. + */ + QRect max; +signals: + /** + * This is send from the mouse click event handler. + */ + void closeMe(int); +public: + /** + * The constructor. + */ + DateInternalMonthPicker(int fontsize, QWidget* parent, const char* name=0); + /** + * The size hint. + */ + QSize sizeHint() const; + /** + * The minimum size hint. + */ + QSize minimumSizeHint() const { return sizeHint(); } + /** + * Return the result. 0 means no selection (reject()), 1..12 are the + * months. + */ + int getResult() const; +protected: + /** + * Set up the painter. + */ + void setupPainter(QPainter *p); + /** + * The resize event. + */ + virtual void viewportResizeEvent(QResizeEvent*); + /** + * Paint a cell. This simply draws the month names in it. + */ + virtual void paintCell(QPainter* painter, int row, int col); + /** + * Catch mouse click and move events to paint a rectangle around the item. + */ + virtual void contentsMousePressEvent(QMouseEvent *e); + virtual void contentsMouseMoveEvent(QMouseEvent *e); + /** + * Emit monthSelected(int) when a cell has been released. + */ + virtual void contentsMouseReleaseEvent(QMouseEvent *e); + +private: + class DateInternalMonthPrivate; + DateInternalMonthPrivate *d; +}; + +/** Year selection widget. +* @internal +* @version $Id: kptdatetable.h 576264 2006-08-23 16:28:46Z danders $ +* @author Tim Gilman, Mirko Boehm +*/ +class DateInternalYearSelector : public QLineEdit +{ + Q_OBJECT +protected: + QIntValidator *val; + int result; +public slots: + void yearEnteredSlot(); +signals: + void closeMe(int); +public: + DateInternalYearSelector(int fontsize, + QWidget* parent=0, + const char* name=0); + int getYear() const; + void setYear(int year); + +private: + class DateInternalYearPrivate; + DateInternalYearPrivate *d; +}; + +/** + * Frame with popup menu behaviour. + * @author Tim Gilman, Mirko Boehm + * @version $Id: kptdatetable.h 576264 2006-08-23 16:28:46Z danders $ + */ +class PopupFrame : public QFrame +{ + Q_OBJECT +protected: + /** + * The result. It is returned from exec() when the popup window closes. + */ + int result; + /** + * Catch key press events. + */ + virtual void keyPressEvent(QKeyEvent* e); + /** + * The only subwidget that uses the whole dialog window. + */ + QWidget *main; +public slots: + /** + * Close the popup window. This is called from the main widget, usually. + * @p r is the result returned from exec(). + */ + void close(int r); +public: + /** + * The contructor. Creates a dialog without buttons. + */ + PopupFrame(QWidget* parent=0, const char* name=0); + /** + * Set the main widget. You cannot set the main widget from the constructor, + * since it must be a child of the frame itselfes. + * Be careful: the size is set to the main widgets size. It is up to you to + * set the main widgets correct size before setting it as the main + * widget. + */ + void setMainWidget(QWidget* m); + /** + * The resize event. Simply resizes the main widget to the whole + * widgets client size. + */ + virtual void resizeEvent(QResizeEvent*); + /** + * Open the popup window at position pos. + */ + void popup(const QPoint &pos); + /** + * Execute the popup window. + */ + int exec(QPoint p); + /** + * Dito. + */ + int exec(int x, int y); + +private: + + virtual bool close(bool alsoDelete) { return QFrame::close(alsoDelete); } +protected: + virtual void virtual_hook( int id, void* data ); +private: + class PopupFramePrivate; + PopupFramePrivate *d; +}; + +/** +* Validates user-entered dates. +*/ +class DateValidator : public QValidator +{ +public: + DateValidator(QWidget* parent=0, const char* name=0); + virtual State validate(QString&, int&) const; + virtual void fixup ( QString & input ) const; + State date(const QString&, QDate&) const; +}; + + +class DateTable : public QGridView +{ + Q_OBJECT +public: + /** + * The constructor. + */ + DateTable(QWidget *parent=0, QDate date=QDate::currentDate(), + const char* name="DateTable", WFlags f=0); + + /** + * Returns a recommended size for the widget. + * To save some time, the size of the largest used cell content is + * calculated in each paintCell() call, since all calculations have + * to be done there anyway. The size is stored in maxCell. The + * sizeHint() simply returns a multiple of maxCell. + */ + virtual QSize sizeHint() const; + /** + * Set the font size of the date table. + */ + void setFontSize(int size); + /** + * Select and display this date. + */ + bool setDate(const QDate&, bool repaint=true); + const QDate& getDate() const; + bool selectDate(const QDate& date_); + + void addMarkedDate(QDate date, int state) { m_markedDates.insert(date, state); } + bool dateMarked(QDate date); + + void addMarkedWeekday(int day, int state); + void setMarkedWeekday(int day, int state) { m_markedWeekdays.insert(day, state); } + void setMarkedWeekdays(const IntMap days); + bool weekdayMarked(int day); + + DateMap selectedDates() const { return m_selectedDates; } + IntMap selectedWeekdays() const { return m_selectedWeekdays; } + + DateMap markedDates() const { return m_markedDates; } + IntMap markedWeekdays() const { return m_markedWeekdays; } + + void clear(); + void clearSelection(); + + void setEnabled(bool yes); + bool isEnabled() const { return m_enabled; } + + void markSelected(int state); + +protected: + /** + * Paint a cell. + */ + virtual void paintCell(QPainter*, int, int); + /** + * Handle the resize events. + */ + virtual void viewportResizeEvent(QResizeEvent *); + /** + * React on mouse clicks that select a date. + */ + virtual void contentsMousePressEvent(QMouseEvent *); + virtual void wheelEvent( QWheelEvent * e ); + virtual void keyPressEvent( QKeyEvent *e ); + virtual void focusInEvent( QFocusEvent *e ); + virtual void focusOutEvent( QFocusEvent *e ); + + bool contentsMousePressEvent_internal(QMouseEvent *); + + int weekOfYear(QDate date) const; + void setWeekNumbers(QDate); + + bool weekSelected(int row); + bool weekSelected(); + bool weekdaySelected(); + bool isWeekdaySelected(int day); + bool dateSelected(QDate date); + bool dateSelected(); + void updateSelectedCells(); + void updateMarkedCells(); + void updateCells(); + + QDate getDate(int pos) const; + + /** + * pos can be 1..42 + * row starts at 1, col depends on wether weeks are presented (in col 0) + */ + int position(int row, int col) { return ((7 * (row - 1)) + col - m_dateStartCol + 1); } + + int weekday(int col) const; + int column(int weekday) const; + + void paintWeekday(QPainter *painter, int col); + void paintWeekNumber(QPainter *painter, int row); + void paintDay(QPainter *painter, int row, int col); + + /** + * The font size of the displayed text. + */ + int fontsize; + /** + * The currently selected date. + */ + QDate date; + /** + * The day of the first day in the month [1..7]. + */ + int firstday; + /** + * The number of days in the current month. + */ + int numdays; + /** + * The number of days in the previous month. + */ + int numDaysPrevMonth; + /** + * Save the size of the largest used cell content. + */ + QRect maxCell; + +signals: + /** + * The selected date changed. + */ + void dateChanged(QDate); + /** + * A date has been selected by clicking on the table. + */ + void tableClicked(); + + void weekdaySelected(int); + void weekSelected(int, int); + /** + * All selections have been cleared + */ + void selectionCleared(); + +private: + + QMemArray< QPair<int, int> > m_weeks; + + int m_currentRow; // row of selected date + + // User has selected these, results in "select coloring" the dates in datetable + + DateMap m_selectedDates; + IntMap m_selectedWeekdays; + + // These results in marking the dates, weekdays and weeks respectivly + DateMap m_markedDates; + IntMap m_markedWeekdays; + + int m_dateStartCol; + bool m_enabled; + + QColor colorBackgroundHoliday; + QColor colorBackgroundWorkday; + QColor colorTextHoliday; + QColor colorTextWorkday; + QColor colorLine; + QColor backgroundSelectColor; + QColor penSelectColor; + +protected: + virtual void virtual_hook( int id, void* data ); +private: + class DateTablePrivate; + DateTablePrivate *d; +}; + +} //KPlato namespace + +#endif // DATETABEL_H diff --git a/kplato/kptdatetime.cc b/kplato/kptdatetime.cc new file mode 100644 index 00000000..6cdfae24 --- /dev/null +++ b/kplato/kptdatetime.cc @@ -0,0 +1,84 @@ +/* This file is part of the KDE project + Copyright (C) 2003 Dag Andersen <danders@get2net.dk> + + 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. +*/ + +#include "kptdatetime.h" + +#include <kdebug.h> + +namespace KPlato +{ + +DateTime::DateTime() : QDateTime() { +} + +DateTime::DateTime(const QDateTime &dt) : QDateTime(dt.date(), dt.time()) { +} + +DateTime::DateTime(const QDate &date, const QTime &time) : QDateTime(date, time) { +} + +void DateTime::add(const Duration &duration) { + if (isValid()) + *this = addSecs(duration.seconds()); + //kdDebug()<<k_funcinfo<<"days,secs: "<<days<<","<<secs<<" gives: "<<toString()<<endl; +} + +void DateTime::subtract(const Duration &duration) { + if (isValid()) + *this = addSecs(-duration.seconds()); + //kdDebug()<<k_funcinfo<<"days,secs: "<<days<<","<<secs<<" gives: "<<toString()<<endl; +} + +Duration DateTime::duration(const DateTime &dt) const { + Duration dur; + if (isValid() && dt.isValid()) { + if (dt < *this) { + dur.addDays(dt.daysTo(*this)); + dur.addSeconds(dt.time().secsTo(time())); + } else { + dur.addDays(daysTo(dt)); + dur.addSeconds(time().secsTo(dt.time())); + } + } + return dur; +} + +DateTime DateTime::operator+(const Duration &duration) const { + DateTime d(*this); + d.add(duration); + return d; +} + +DateTime& DateTime::operator+=(const Duration &duration) { + add(duration); + return *this; +} + +DateTime DateTime::operator-(const Duration &duration) const { + DateTime d(*this); + d.subtract(duration); + return d; +} + +DateTime& DateTime::operator-=(const Duration &duration) { + subtract(duration); + return *this; +} + +} //KPlato namespace diff --git a/kplato/kptdatetime.h b/kplato/kptdatetime.h new file mode 100644 index 00000000..cb682c13 --- /dev/null +++ b/kplato/kptdatetime.h @@ -0,0 +1,76 @@ +/* This file is part of the KDE project + Copyright (C) 2003 Dag Andersen <danders@get2net.dk> + + 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 KPTDATETIME_H +#define KPTDATETIME_H + +#include <qdatetime.h> +#include "kptduration.h" + +namespace KPlato +{ + +class Duration; + +/** + * DateTime is a @ref QDateTime which knows about @ref Duration + */ +class DateTime : public QDateTime { + +public: + DateTime(); + DateTime(const QDateTime &dt); + DateTime(const QDate &date, const QTime &time); + + /** + * Adds the duration @param duration to the datetime + */ + DateTime operator+(const Duration &duration) const; + /** + * Subtracts the duration @param duration from the datetime + */ + DateTime operator-(const Duration &duration) const ; + /** + * Returns the absolute duration between the two datetimes + */ + Duration operator-(const DateTime &dt) const { return duration(dt); } + Duration operator-(const DateTime &dt) { return duration(dt); } + + DateTime &operator+=(const Duration &duration); + DateTime &operator-=(const Duration &duration); + + static DateTime fromString(const QString dts) { + QDateTime dt; + if (dts.isEmpty()) + return DateTime(); + dt = QDateTime::fromString(dts, Qt::ISODate); + if (dt.isValid()) + return DateTime(dt); + return DateTime(QDateTime::fromString(dts)); + } +private: + + Duration duration(const DateTime &dt) const; + void add(const Duration &duration); + void subtract(const Duration &duration); + +}; + +} //KPlato namespace + +#endif diff --git a/kplato/kptdoublelistviewbase.cc b/kplato/kptdoublelistviewbase.cc new file mode 100644 index 00000000..27d8e3f7 --- /dev/null +++ b/kplato/kptdoublelistviewbase.cc @@ -0,0 +1,511 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Dag Andersen kplato@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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. +*/ + +#include "kptdoublelistviewbase.h" + +#include "kptproject.h" +#include "kptview.h" + +#include <qheader.h> +#include <qlayout.h> +#include <qmap.h> +#include <qpainter.h> +#include <qpalette.h> +#include <qptrvector.h> +#include <qsplitter.h> +#include <qstring.h> +#include <qvaluelist.h> +#include <qpoint.h> + +#include <kcalendarsystem.h> +#include <kglobal.h> +#include <klocale.h> +#include <kprinter.h> +#include <qrect.h> + +#include <kdebug.h> + +namespace KPlato +{ +void ListView::paintToPrinter(QPainter * p, int cx, int cy, int cw, int ch) { + //kdDebug()<<k_funcinfo<<QRect(cx, cy, cw, ch)<<endl; + // draw header labels + p->save(); + QRegion r = p->clipRegion(QPainter::CoordPainter); + p->setClipRegion(r.intersect(QRegion(cx, 0, cw, ch)), QPainter::CoordPainter); + QColor bgc(193, 223, 255); + QBrush bg(bgc); + p->setBackgroundMode(Qt::OpaqueMode); + p->setBackgroundColor(bgc); + QHeader *h = header(); + int hei = 0; + for (int s = 0; s < h->count(); ++s) { + QRect r = h->sectionRect(s); + //kdDebug()<<s<<": "<<h->label(s)<<" "<<r<<endl; + int x, y; + viewportToContents(r.x(), r.y(), x, y); + QRect sr(x, y, r.width(), r.height()); + //kdDebug()<<s<<": "<<h->label(s)<<" "<<sr<<endl; + if (sr.x()+sr.width() <= cx || sr.x() >= cx+cw) { + //kdDebug()<<s<<": "<<h->label(s)<<" "<<sr<<": continue"<<endl; + continue; + } + QRect tr = sr; + if (sr.x() < cx) { + tr.setX(cx); + //kdDebug()<<s<<": "<<h->label(s)<<" "<<tr<<endl; + } + p->eraseRect(tr); + p->drawText(tr, columnAlignment(s)|Qt::AlignVCenter, h->label(s), -1); + hei = QMAX(tr.height(), hei); + } + r = p->clipRegion(QPainter::CoordPainter); + p->restore(); +// p->drawRect(r.boundingRect()); + p->save(); + p->translate(0, hei+2); + r = p->clipRegion(QPainter::CoordPainter); + // FIXME: Doesn't clip correctly, haven't figured out why + p->setClipRegion(r.intersect(QRegion(cx, cy, cw, ch)), QPainter::CoordPainter); + drawContentsOffset(p, 0, 0, cx, cy, cw, ch); +// p->drawRect(r.boundingRect()); + p->restore(); +} + +DoubleListViewBase::SlaveListItem::SlaveListItem(DoubleListViewBase::MasterListItem *master, QListView *parent, QListViewItem *after, bool highlight) + : KListViewItem(parent, after), + m_masterItem(master), + m_value(0.0), + m_highlight(highlight), + m_valueMap() { + + setFormat(); + setExpandable(master->isExpandable()); + setOpen(master->isOpen()); + //kdDebug()<<"DoubleListViewBase::SlaveListItem "<<master->text(0)<<" parent="<<static_cast<DoubleListViewBase::SlaveListItem*>(parent)->m_masterItem->text(0)<<endl; +} +DoubleListViewBase::SlaveListItem::SlaveListItem(DoubleListViewBase::MasterListItem *master, QListViewItem *parent, QListViewItem *after, bool highlight) + : KListViewItem(parent, after), + m_masterItem(master), + m_value(0.0), + m_highlight(highlight), + m_valueMap() { + + setFormat(); + setExpandable(master->isExpandable()); + setOpen(master->isOpen()); + //kdDebug()<<"DoubleListViewBase::SlaveListItem "<<master->text(0)<<" parent="<<static_cast<DoubleListViewBase::SlaveListItem*>(parent)->m_masterItem->text(0)<<endl; +} +DoubleListViewBase::SlaveListItem::~SlaveListItem() { + //kdDebug()<<k_funcinfo<<endl; + if (m_masterItem) + m_masterItem->slaveItemDeleted(); +} + +void DoubleListViewBase::SlaveListItem::clearColumn(int col) { + if (col >= listView()->columns()) { + return; + } + listView()->setColumnText(col, ""); + setText(col, ""); + m_valueMap[col] = 0; +} +void DoubleListViewBase::SlaveListItem::setColumn(int col, double value) { + if (col < listView()->columns()) { + //setText(col, QString("%1").arg(value, m_fieldwidth, m_fmt, m_prec)); + setText(col, KGlobal::locale()->formatNumber(value, m_prec)); + m_valueMap.replace(col, value); + //kdDebug()<<k_funcinfo<<m_masterItem->text(0)<<": column["<<col<<"]="<<value<<endl; + } +} + +void DoubleListViewBase::SlaveListItem::setLimit(int col, double limit) { + m_limitMap[col] = limit; +} + +void DoubleListViewBase::SlaveListItem::paintCell(QPainter *p, const QColorGroup &cg, int column, int width, int align) { + //kdDebug()<<k_funcinfo<<"c="<<column<<endl; + QColorGroup g = cg; + if (m_highlight) { + if (m_limitMap.contains(column)) { + if (m_valueMap[column] > m_limitMap[column]) { + g.setColor(QColorGroup::Text, QColor(red)); + } else if (m_valueMap[column] < m_limitMap[column]) { + g.setColor(QColorGroup::Text, QColor(green)); + } + } + } + KListViewItem::paintCell(p, g, column, width, align); +} + +void DoubleListViewBase::SlaveListItem::setFormat(int fieldwidth, char fmt, int prec) { + m_fieldwidth = fieldwidth; + m_fmt = fmt; + m_prec = prec; +} + +//---------------------------- +DoubleListViewBase::MasterListItem::MasterListItem(QListView *parent, bool highlight) + : KListViewItem(parent), + m_slaveItem(0), + m_value(0.0), + m_limit(0.0), + m_highlight(highlight) { + + setFormat(); + //kdDebug()<<k_funcinfo<<endl; +} + +DoubleListViewBase::MasterListItem::MasterListItem(QListView *parent, QString text, bool highlight) + : KListViewItem(parent, text), + m_slaveItem(0), + m_value(0.0), + m_limit(0.0), + m_highlight(highlight) { + + setFormat(); + //kdDebug()<<k_funcinfo<<endl; +} + +DoubleListViewBase::MasterListItem::MasterListItem(QListViewItem *parent, bool highlight) + : KListViewItem(parent), + m_slaveItem(0), + m_value(0.0), + m_limit(0.0), + m_highlight(highlight) { + + setFormat(); + //kdDebug()<<k_funcinfo<<endl; +} + +DoubleListViewBase::MasterListItem::MasterListItem(QListViewItem *parent, QString text, bool highlight) + : KListViewItem(parent, text), + m_slaveItem(0), + m_value(0.0), + m_limit(0.0), + m_highlight(highlight) { + + setFormat(); + //kdDebug()<<k_funcinfo<<endl; +} + +DoubleListViewBase::MasterListItem::~MasterListItem() { + if (m_slaveItem) + m_slaveItem->masterItemDeleted(); +} + +void DoubleListViewBase::MasterListItem::createSlaveItems(QListView *lv, QListViewItem *after) { + //kdDebug()<<k_funcinfo<<text(0)<<endl; + if (m_slaveItem) { + kdError()<<k_funcinfo<<"Slave item allready exists"<<endl; + } else { + if (parent() == 0) { + m_slaveItem = new DoubleListViewBase::SlaveListItem(this, lv, after); + } else { + m_slaveItem = new DoubleListViewBase::SlaveListItem(this, static_cast<DoubleListViewBase::MasterListItem*>(parent())->m_slaveItem, after); + } + } + DoubleListViewBase::SlaveListItem *prev = 0; + for (QListViewItem *item = firstChild(); item; item = item->nextSibling()) { + static_cast<DoubleListViewBase::MasterListItem*>(item)->createSlaveItems(lv, prev); + prev = static_cast<DoubleListViewBase::MasterListItem*>(item)->m_slaveItem; + } +} + +void DoubleListViewBase::MasterListItem::setSlaveOpen(bool on) { + if (m_slaveItem) + m_slaveItem->setOpen(on); +} + +void DoubleListViewBase::MasterListItem::slaveItemDeleted() { + setTotal(0); + m_slaveItem = 0; +} + +void DoubleListViewBase::MasterListItem::setTotal(double tot) { + m_value = tot; + //setText(1, QString("%1").arg(tot, m_fieldwidth, m_fmt, m_prec)); + setText(1, KGlobal::locale()->formatNumber(tot, m_prec)); + //kdDebug()<<k_funcinfo<<text(0)<<"="<<tot<<endl; +} + +void DoubleListViewBase::MasterListItem::addToTotal(double v) { + m_value += v; + //setText(1, QString("%1").arg(m_value, m_fieldwidth, m_fmt, m_prec)); + setText(1, KGlobal::locale()->formatNumber(m_value, m_prec)); +} + +double DoubleListViewBase::MasterListItem::calcTotal() { + double tot=0.0; + QListViewItem *item=firstChild(); + if (!item) { + tot = m_value; + } else { + for (; item; item = item->nextSibling()) { + tot += static_cast<DoubleListViewBase::MasterListItem*>(item)->calcTotal(); + } + } + setTotal(tot); + return tot; +} + +void DoubleListViewBase::MasterListItem::setSlaveItem(int col, double value) { + if (m_slaveItem) { + m_slaveItem->setColumn(col, value); + } +} + +void DoubleListViewBase::MasterListItem::clearColumn(int col) { + for (QListViewItem *item=firstChild(); item; item=item->nextSibling()) { + static_cast<DoubleListViewBase::MasterListItem*>(item)->clearColumn(col); + } + setTotal(0); + if (m_slaveItem == 0) { + kdError()<<k_funcinfo<<"No m_slaveItem"<<endl; + return; + } + m_slaveItem->clearColumn(0); +} + +void DoubleListViewBase::MasterListItem::calcSlaveItems() { + if (m_slaveItem == 0 || m_slaveItem->listView() == 0) { + kdError()<<k_funcinfo<<"No m_slaveItem or m_slaveItem->listView()"<<endl; + return; + } + int cols = m_slaveItem->listView()->columns(); + for (int i = 0; i < cols; ++i) { + calcSlaveItems(i); + } +} + +double DoubleListViewBase::MasterListItem::calcSlaveItems(int col) { + if (m_slaveItem == 0) + return 0.0; + QListViewItem *item=firstChild(); + if (!item) { + return m_slaveItem->value(col); + } + double tot=0.0; + for (; item; item = item->nextSibling()) { + tot += static_cast<DoubleListViewBase::MasterListItem*>(item)->calcSlaveItems(col); + } + //kdDebug()<<k_funcinfo<<text(0)<<" "<<col<<"="<<tot<<endl; + setSlaveItem(col, tot); + return tot; +} + +void DoubleListViewBase::MasterListItem::setSlaveLimit(int col, double limit) { + if (m_slaveItem) { + m_slaveItem->setLimit(col, limit); + } +} + +void DoubleListViewBase::MasterListItem::setSlaveHighlight(bool on) { + if (m_slaveItem) { + m_slaveItem->setHighlight(on); + } +} + +void DoubleListViewBase::MasterListItem::paintCell(QPainter *p, const QColorGroup &cg, int column, int width, int align) { + //kdDebug()<<k_funcinfo<<"c="<<column<<" value="<<m_value<<" limit="<<m_limit<<endl; + QColorGroup g = cg; + if (column == 1 && m_highlight) { + if (m_value > m_limit) { + g.setColor(QColorGroup::Text, QColor(red)); + } else if (m_value < m_limit) { + g.setColor(QColorGroup::Text, QColor(green)); + } + } + KListViewItem::paintCell(p, g, column, width, align); +} + +void DoubleListViewBase::MasterListItem::setFormat(int fieldwidth, char fmt, int prec) { + m_fieldwidth = fieldwidth; + m_fmt = fmt; + m_prec = prec; +} + +//------------------------------------- +DoubleListViewBase::DoubleListViewBase(QWidget *parent, bool description) + : QSplitter(parent), + m_fieldwidth(0), + m_fmt('f'), + m_prec(0) { + + setOrientation(QSplitter::Horizontal); + setHandleWidth(QMIN(2, handleWidth())); + + m_masterList = new ListView(this); + m_masterList->setSelectionMode(QListView::NoSelection); + m_masterList->setItemMargin(2); + m_masterList->setRootIsDecorated(true); +#if KDE_IS_VERSION(3,3,9) + m_masterList->setShadeSortColumn(false); +#endif + m_masterList->setSortColumn(-1); // Disable sort!! + m_masterList->addColumn(i18n("Name")); + m_masterList->addColumn(i18n("Total")); + m_masterList->setColumnAlignment(1, AlignRight); + if (description) { + m_masterList->addColumn(i18n("Description")); + m_masterList->header()->moveSection(2, 1); + m_masterList->header()->setStretchEnabled(true, 1); + } else { + m_masterList->header()->setStretchEnabled(true, 0); + } + m_masterList->setVScrollBarMode(QScrollView::AlwaysOff); + m_masterList->setHScrollBarMode(QScrollView::AlwaysOn); + + m_slaveList = new ListView(this); + m_slaveList->setSelectionMode(QListView::NoSelection); + m_slaveList->setItemMargin(2); + m_slaveList->setSortColumn(-1); // Disable sort!! + m_slaveList->setTreeStepSize(0); + m_slaveList->setHScrollBarMode(QScrollView::AlwaysOn); + + + connect(m_slaveList->verticalScrollBar(), SIGNAL(valueChanged(int)), + m_masterList->verticalScrollBar(), SLOT(setValue(int))); + + connect(m_masterList, SIGNAL(expanded(QListViewItem*)), SLOT(slotExpanded(QListViewItem*))); + connect(m_masterList, SIGNAL(collapsed(QListViewItem*)), SLOT(slotCollapsed(QListViewItem*))); + +} + +QSize DoubleListViewBase::sizeHint() const { + //kdDebug()<<k_funcinfo<<minimumSizeHint()<<endl; + return minimumSizeHint(); //HACK: koshell splitter problem +} + +void DoubleListViewBase::clearSlaveList() { + while (m_slaveList->columns() > 0) { + m_slaveList->removeColumn(0); // removing the last one clears the list!!! + } + m_slaveList->clear(); // to be safe +} + +void DoubleListViewBase::createSlaveItems() { + clearSlaveList(); + DoubleListViewBase::SlaveListItem *prev = 0; + for (QListViewItem *item = m_masterList->firstChild(); item; item = item->nextSibling()) { + static_cast<DoubleListViewBase::MasterListItem*>(item)->createSlaveItems(m_slaveList, prev); + prev = static_cast<DoubleListViewBase::MasterListItem*>(item)->slaveItem(); + } +} + + +void DoubleListViewBase::print(KPrinter &printer) { + kdDebug()<<k_funcinfo<<endl; + Q_UNUSED(printer); +} + +void DoubleListViewBase::setOpen(QListViewItem *item, bool open) { + //kdDebug()<<k_funcinfo<<endl; + m_masterList->setOpen(item, open); +} + +void DoubleListViewBase::slotExpanded(QListViewItem* item) { + //kdDebug()<<k_funcinfo<<endl; + if (item) { + static_cast<DoubleListViewBase::MasterListItem*>(item)->setSlaveOpen(true); + } +} + +void DoubleListViewBase::slotCollapsed(QListViewItem*item) { + //kdDebug()<<k_funcinfo<<endl; + if (item) { + static_cast<DoubleListViewBase::MasterListItem*>(item)->setSlaveOpen(false); + } +} + +void DoubleListViewBase::setDescriptionHeader(QString text) { + m_masterList->setColumnText(1, text); +} + +void DoubleListViewBase::setNameHeader(QString text) { + m_masterList->setColumnText(0, text); +} + +void DoubleListViewBase::setTotalHeader(QString text) { + m_masterList->setColumnText(2, text); +} + +void DoubleListViewBase::addSlaveColumn(QString text) { + m_slaveList->addColumn(text); + m_slaveList->setColumnAlignment(m_slaveList->columns()-1, AlignRight); +} + +void DoubleListViewBase::calculate() { + for (QListViewItem *lvi=m_masterList->firstChild(); lvi; lvi = lvi->nextSibling()) { + static_cast<DoubleListViewBase::MasterListItem *>(lvi)->calcSlaveItems(); + static_cast<DoubleListViewBase::MasterListItem *>(lvi)->calcTotal(); + } +} + +void DoubleListViewBase::clearLists() { + m_slaveList->clear(); + m_masterList->clear(); +} + +void DoubleListViewBase::setMasterFormat(int fieldwidth, char fmt, int prec) { + QListViewItemIterator it = m_masterList; + for (; it.current(); ++it) { + static_cast<DoubleListViewBase::MasterListItem*>(it.current())->setFormat(fieldwidth, fmt, prec); + } +} +void DoubleListViewBase::setSlaveFormat(int fieldwidth, char fmt, int prec) { + QListViewItemIterator it = m_slaveList; + for (; it.current(); ++it) { + static_cast<DoubleListViewBase::SlaveListItem*>(it.current())->setFormat(fieldwidth, fmt, prec); + } +} + +void DoubleListViewBase::setFormat(int fieldwidth, char fmt, int prec) { + m_fieldwidth = fieldwidth; + m_fmt = fmt; + m_prec = prec; + setMasterFormat(fieldwidth, fmt, prec); + setSlaveFormat(fieldwidth, fmt, prec); +} + +void DoubleListViewBase::paintContents(QPainter *p) { + //kdDebug()<<k_funcinfo<<endl; + QRect cm = m_masterList->contentsRect(); + QRect cs = m_slaveList->contentsRect(); + int mx, my, sx, sy; + m_masterList->contentsToViewport(cm.x(), cm.y(), mx, my); + m_slaveList->contentsToViewport(cs.x(), cs.y(), sx, sy); + if (sizes()[0] > 0) { + p->save(); + p->translate(mx, my); + m_masterList->paintToPrinter(p, -mx, -my, cm.width(), cm.height()); + p->restore(); + } + if (sizes()[1] > 0) { + p->save(); + p->translate(cm.width() + 8 + sx, sy); + m_slaveList->paintToPrinter(p, -sx, -sy, cs.width(), cs.height()); + //p->fillRect(-8, 0, 0, sy, Qt::white); + p->restore(); + } +} + +} //KPlato namespace + +#include "kptdoublelistviewbase.moc" diff --git a/kplato/kptdoublelistviewbase.h b/kplato/kptdoublelistviewbase.h new file mode 100644 index 00000000..c02f4bcb --- /dev/null +++ b/kplato/kptdoublelistviewbase.h @@ -0,0 +1,183 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Dag Andersen <kplato@kde.org> + + 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 KPTDOUBLELISTVIEWBASE_H +#define KPTDOUBLELISTVIEWBASE_H + +#include <qcolor.h> +#include <qmap.h> +#include <qptrvector.h> +#include <qsplitter.h> + +#include <klistview.h> + +class QListViewItem; + +class KListViewItem; +class KPrinter; + +namespace KPlato +{ + +class View; +class Project; + +class ListView : public KListView +{ +public: + ListView(QWidget *parent) + : KListView(parent) + {} + + virtual void paintToPrinter(QPainter * p, int cx, int cy, int cw, int ch); +}; + +/** + * The class DoubleListViewBase provides a double listview + * where the right listview (the slave) containes columns of + * double values and the left listview (the master) is the 'item' listview + * and also provides for a sum total column of the values in the slave listview. + * This makes it possible to scroll the slave listview and still see the values + * in the master listview. + */ +class DoubleListViewBase : public QSplitter +{ + Q_OBJECT +public: + + DoubleListViewBase(QWidget *parent, bool description=false); + + //~DoubleListViewBase(); + + ListView *masterListView() const { return m_masterList; } + ListView *slaveListView() const { return m_slaveList; } + void setOpen(QListViewItem *item, bool open); + + void setNameHeader(QString text); + void setTotalHeader(QString text); + void setDescriptionHeader(QString text); + void addSlaveColumn(QString text); + virtual void print(KPrinter &printer); + + virtual void calculate(); + void clearLists(); + virtual void createSlaveItems(); + void clearSlaveList(); + void setFormat(int fieldwidth=0, char fmt='f', int prec=0); + void setMasterFormat(int fieldwidth=0, char fmt='f', int prec=0); + void setSlaveFormat(int fieldwidth=0, char fmt='f', int prec=0); + virtual QSize sizeHint() const; + + class MasterListItem; + class SlaveListItem : public KListViewItem { + public: + SlaveListItem(MasterListItem *master, QListView *parent, QListViewItem *after, bool highlight=false); + SlaveListItem(MasterListItem *master, QListViewItem *parent, QListViewItem *after, bool highlight=false); + ~SlaveListItem(); + void masterItemDeleted() { m_masterItem = 0; } + + virtual void setColumn(int col, double value); + virtual void clearColumn(int col); + + double value(int col) const { return m_valueMap[col]; } + void setLimit(int col, double limit); + void setHighlight(bool on) { m_highlight = on; } + + virtual void paintCell(QPainter *p, const QColorGroup &cg, int column, int width, int align); + + void setFormat(int fieldwidth=0, char fmt='f', int prec=0); + + protected: + MasterListItem *m_masterItem; + double m_value; + bool m_highlight; + + QMap<int, double> m_valueMap; + QMap<int, double> m_limitMap; + + int m_fieldwidth; + char m_fmt; + int m_prec; + }; + + class MasterListItem : public KListViewItem { + public: + MasterListItem(QListView *parent, bool highlight=false); + MasterListItem(QListView *parent, QString text, bool highlight=false); + MasterListItem(QListViewItem *parent, bool highlight=false); + MasterListItem(QListViewItem *parent, QString text, bool highlight=false); + ~MasterListItem(); + + /// Creates slaveitems for myself and my children + void createSlaveItems(QListView *lv, QListViewItem *after=0); + void slaveItemDeleted(); + void setSlaveOpen(bool on); + SlaveListItem *slaveItem() const { return m_slaveItem; } + double value() const { return m_value; } + + void setTotal(double tot); + double calcTotal(); + void addToTotal(double v); + void setSlaveItem(int col, double value); + void setSlaveLimit(int col, double limit); + void setLimit(double limit) { m_limit = limit; } + void setHighlight(bool on) { m_highlight = on; } + void setSlaveHighlight(bool on); + void clearColumn(int col); + void calcSlaveItems(); + virtual double calcSlaveItems(int col); + + virtual void paintCell(QPainter *p, const QColorGroup &cg, int column, int width, int align); + + void setFormat(int fieldwidth=0, char fmt='f', int prec=0); + + protected: + SlaveListItem *m_slaveItem; + double m_value; + double m_limit; + bool m_highlight; + + QMap<int, double> m_valueMap; + + int m_fieldwidth; + char m_fmt; + int m_prec; + }; + +public: + virtual void paintContents(QPainter *p); + +protected slots: + void slotExpanded(QListViewItem* item); + void slotCollapsed(QListViewItem* item); + +private: + +private: + ListView *m_masterList; + ListView *m_slaveList; + + int m_fieldwidth; + char m_fmt; + int m_prec; +}; + +} //KPlato namespace + +#endif diff --git a/kplato/kptduration.cc b/kplato/kptduration.cc new file mode 100644 index 00000000..384ac754 --- /dev/null +++ b/kplato/kptduration.cc @@ -0,0 +1,257 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Thomas Zander zander@kde.org + Copyright (C) 2004, 2005 Dag Andersen <danders@get2net.dk> + + 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. +*/ + +#include "kptduration.h" +#include "kptdatetime.h" + +#include <kglobal.h> +#include <klocale.h> +#include <kdebug.h> +#include <qregexp.h> + +namespace KPlato +{ + +// Set the value of Duration::zeroDuration to zero. +const Duration Duration::zeroDuration( 0, 0, 0 ); + +Duration::Duration() { + m_ms = 0; +} + +Duration::Duration(const Duration &d) { + m_ms = d.m_ms; +} + +Duration::Duration(unsigned d, unsigned h, unsigned m, unsigned s, unsigned ms) { + m_ms = ms; + m_ms += static_cast<Q_INT64>(s) * 1000; // cast to avoid potential overflow problem + m_ms += static_cast<Q_INT64>(m) * 60 * 1000; + m_ms += static_cast<Q_INT64>(h) * 60 * 60 * 1000; + m_ms += static_cast<Q_INT64>(d) * 24 * 60 * 60 * 1000; +} + +Duration::Duration(Q_INT64 seconds) { + m_ms = seconds * 1000; +} + +Duration::~Duration() { +} + +void Duration::add(const Duration &delta) { + m_ms += delta.m_ms; +} + +void Duration::add(Q_INT64 delta) { + Q_INT64 tmp = m_ms + delta; + if (tmp < 0) { + kdDebug()<<k_funcinfo<<"Underflow"<<(long int)delta<<" from "<<this->toString()<<endl; + m_ms = 0; + return; + } + m_ms = tmp; +} + +void Duration::subtract(const Duration &delta) { + if (m_ms < delta.m_ms) { + kdDebug()<<k_funcinfo<<"Underflow"<<delta.toString()<<" from "<<this->toString()<<endl; + m_ms = 0; + return; + } + m_ms -= delta.m_ms; +} + +Duration Duration::operator*(int unit) const { + Duration dur(*this); + if (unit < 0) { + kdDebug()<<k_funcinfo<<"Underflow"<<unit<<" from "<<this->toString()<<endl; + } + else { + dur.m_ms = m_ms * unit; //FIXME + } + return dur; +} + +Duration Duration::operator/(int unit) const { + Duration dur(*this); + if (unit <= 0) { + kdDebug()<<k_funcinfo<<"Underflow"<<unit<<" from "<<this->toString()<<endl; + } + else { + dur.m_ms = m_ms / unit; //FIXME + } + return dur; +} + +Duration Duration::operator*(const double value) const { + Duration dur(*this); + dur.m_ms = QABS(m_ms * (Q_INT64)value); + return dur; +} + +double Duration::operator/(const Duration &d) const { + if (d == zeroDuration) { + kdDebug()<<k_funcinfo<<"Devide by zero: "<<this->toString()<<endl; + return 0.0; + } + return (double)(m_ms) / (double)(d.m_ms); +} + +QString Duration::toString(Format format) const { + Q_INT64 ms; + double days; + unsigned hours; + unsigned minutes; + unsigned seconds; + double f; + QString result; + + switch (format) { + case Format_Hour: + ms = m_ms; + hours = ms / (1000 * 60 * 60); + ms -= (Q_INT64)hours * (1000 * 60 * 60); + minutes = ms / (1000 * 60); + result = QString("%1h%2m").arg(hours).arg(minutes); + break; + case Format_Day: + days = m_ms / (1000 * 60 * 60 * 24.0); + result = QString("%1d").arg(QString::number(days, 'f', 4)); + break; + case Format_DayTime: + ms = m_ms; + days = m_ms / (1000 * 60 * 60 * 24); + ms -= (Q_INT64)days * (1000 * 60 * 60 * 24); + hours = ms / (1000 * 60 * 60); + ms -= (Q_INT64)hours * (1000 * 60 * 60); + minutes = ms / (1000 * 60); + ms -= minutes * (1000 * 60); + seconds = ms / (1000); + ms -= seconds * (1000); + result.sprintf("%u %02u:%02u:%02u.%u", (unsigned)days, hours, minutes, seconds, (unsigned)ms); + break; + case Format_HourFraction: + result = KGlobal::locale()->formatNumber(toDouble(Unit_h), 2); + break; + // i18n + case Format_i18nHour: + ms = m_ms; + hours = ms / (1000 * 60 * 60); + ms -= (Q_INT64)hours * (1000 * 60 * 60); + minutes = ms / (1000 * 60); + result = i18n("<hours>h:<minutes>m", "%1h:%2m").arg(hours).arg(minutes); + break; + case Format_i18nDay: + result = KGlobal::locale()->formatNumber(toDouble(Unit_d), 2); + break; + case Format_i18nDayTime: + ms = m_ms; + days = m_ms / (1000 * 60 * 60 * 24); + ms -= (Q_INT64)days * (1000 * 60 * 60 * 24); + hours = ms / (1000 * 60 * 60); + ms -= (Q_INT64)hours * (1000 * 60 * 60); + minutes = ms / (1000 * 60); + ms -= minutes * (1000 * 60); + seconds = ms / (1000); + ms -= seconds * (1000); + if (days == 0) { + result = toString(Format_i18nHour); + } else { + result = i18n("<days>d <hours>h:<minutes>m", "%1d %2h:%3m").arg(days).arg(hours).arg(minutes); + } + break; + case Format_i18nHourFraction: + result = KGlobal::locale()->formatNumber(toDouble(Unit_h), 2); + break; + default: + kdFatal()<<k_funcinfo<<"Unknown format"<<endl; + break; + } + return result; +} + +Duration::Duration Duration::fromString(const QString &s, Format format, bool *ok) { + if (ok) *ok = false; + QRegExp matcher; + Duration tmp; + switch (format) { + case Format_Hour: { + matcher.setPattern("^(\\d*)h(\\d*)m$" ); + int pos = matcher.search(s); + if (pos > -1) { + tmp.addHours(matcher.cap(1).toUInt()); + tmp.addMinutes(matcher.cap(2).toUInt()); + if (ok) *ok = true; + } + break; + } + case Format_DayTime: { + matcher.setPattern("^(\\d*) (\\d*):(\\d*):(\\d*)\\.(\\d*)$" ); + int pos = matcher.search(s); + if (pos > -1) { + tmp.addDays(matcher.cap(1).toUInt()); + tmp.addHours(matcher.cap(2).toUInt()); + tmp.addMinutes(matcher.cap(3).toUInt()); + tmp.addSeconds(matcher.cap(4).toUInt()); + tmp.addMilliseconds(matcher.cap(5).toUInt()); + if (ok) *ok = true; + } + break; + } + case Format_HourFraction: { + // should be in double format + bool res; + double f = KGlobal::locale()->readNumber(s, &res); + if (ok) *ok = res; + if (res) { + return Duration((Q_INT64)(f*3600.0)); + } + break; + } + default: + kdFatal()<<k_funcinfo<<"Unknown format"<<endl; + break; + } + return tmp; +} + +void Duration::get(unsigned *days, unsigned *hours, unsigned *minutes, unsigned *seconds, unsigned *milliseconds) const { + Q_INT64 ms; + Q_INT64 tmp; + + ms = m_ms; + tmp = ms / (1000 * 60 * 60 * 24); + *days = tmp; + ms -= tmp * (1000 * 60 * 60 * 24); + tmp = ms / (1000 * 60 * 60); + *hours = tmp; + ms -= tmp * (1000 * 60 * 60); + tmp = ms / (1000 * 60); + *minutes = tmp; + ms -= tmp * (1000 * 60); + tmp = ms / (1000); + if (seconds) + *seconds = tmp; + ms -= tmp * (1000); + if (milliseconds) + *milliseconds = ms; +} + +} //KPlato namespace diff --git a/kplato/kptduration.h b/kplato/kptduration.h new file mode 100644 index 00000000..116d0660 --- /dev/null +++ b/kplato/kptduration.h @@ -0,0 +1,146 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Thomas Zander zander@kde.org + Copyright (C) 2004 - 2006 Dag Andersen <danders@get2net.dk> + + 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 KPTDURATION_H +#define KPTDURATION_H + +#include <qglobal.h> +#include <qstring.h> + +namespace KPlato +{ + +/** + * The duration class can be used to store a timespan in a convenient format. + * The timespan can be in length in many many hours down to miliseconds. + */ +class Duration { + public: + /** + * DayTime = d hh:mm:ss.sss + * Day = d.ddd + * Hour = hh:mm + * HourFraction = h.fraction of an hour + */ + enum Format { Format_DayTime, Format_Day, Format_Hour, Format_HourFraction, Format_i18nDayTime, Format_i18nDay, Format_i18nHour, Format_i18nHourFraction }; + + Duration(); + Duration(const Duration &d); + Duration(unsigned d, unsigned h, unsigned m, unsigned s=0, unsigned ms=0); + Duration(Q_INT64 seconds); + ~Duration(); + + /** + * Adds @param delta to *this. If @param delta > *this, *this is set to zeroDuration. + */ + void addMilliseconds(Q_INT64 delta) { add(delta); } + + /** + * Adds @param delta to *this. If @param delta > *this, *this is set to zeroDuration. + */ + void addSeconds(Q_INT64 delta) { addMilliseconds(delta * 1000); } + + /** + * Adds @param delta to *this. If @param delta > *this, *this is set to zeroDuration. + */ + void addMinutes(Q_INT64 delta) { addSeconds(delta * 60); } + + /** + * Adds @param delta to *this. If @param delta > *this, *this is set to zeroDuration. + */ + void addHours(Q_INT64 delta) { addMinutes(delta * 60); } + + /** + * Adds @param delta to *this. If @param delta > *this, *this is set to zeroDuration. + */ + void addDays(Q_INT64 delta) { addHours(delta * 24); } + + //FIXME: overflow problem + Q_INT64 milliseconds() const { return m_ms; } + Q_INT64 seconds() const { return m_ms / 1000; } + Q_INT64 minutes() const { return seconds() / 60; } + unsigned hours() const { return minutes() / 60; } + unsigned days() const { return hours() / 24; } + void get(unsigned *days, unsigned *hours, unsigned *minutes, unsigned *seconds=0, unsigned *milliseconds=0) const; + + bool operator==( const Duration &d ) const { return m_ms == d.m_ms; } + bool operator==( Q_INT64 d ) const { return m_ms == d; } + bool operator!=( const Duration &d ) const { return m_ms != d.m_ms; } + bool operator!=( Q_INT64 d ) const { return m_ms != d; } + bool operator<( const Duration &d ) const { return m_ms < d.m_ms; } + bool operator<( Q_INT64 d ) const { return m_ms < d; } + bool operator<=( const Duration &d ) const { return m_ms <= d.m_ms; } + bool operator<=( Q_INT64 d ) const { return m_ms <= d; } + bool operator>( const Duration &d ) const { return m_ms > d.m_ms; } + bool operator>( Q_INT64 d ) const { return m_ms > d; } + bool operator>=( const Duration &d ) const { return m_ms >= d.m_ms; } + bool operator>=( Q_INT64 d ) const { return m_ms >= d; } + Duration &operator=(const Duration &d ) { m_ms = d.m_ms; return *this;} + Duration operator*(int unit) const; + Duration operator*(const double value) const; + Duration operator/(int unit) const; + double operator/(const Duration &d) const; + + Duration operator+(const Duration &d) const + {Duration dur(*this); dur.add(d); return dur; } + Duration &operator+=(const Duration &d) {add(d); return *this; } + + Duration operator-(const Duration &d) const + {Duration dur(*this); dur.subtract(d); return dur; } + Duration &operator-=(const Duration &d) {subtract(d); return *this; } + + QString toString(Format format = Format_DayTime) const; + static Duration fromString(const QString &s, Format format = Format_DayTime, bool *ok=0); + + //NOTE: These must match fieldnumbers in duration widget! + enum Unit { Unit_d, Unit_h, Unit_m, Unit_s, Unit_ms }; + double toDouble(Unit u=Unit_ms) const { + if (u == Unit_ms) return (double)m_ms; + else if (u == Unit_s) return (double)m_ms/1000.0; + else if (u == Unit_m) return (double)m_ms/(1000.0*60.0); + else if (u == Unit_h) return (double)m_ms/(1000.0*60.0*60.0); + else if (u == Unit_d) return (double)m_ms/(1000.0*60.0*60.0*24.0); + return (double)m_ms; + } + + /** + * This is useful for occasions where we need a zero duration. + */ + static const Duration zeroDuration; + + private: + /** + * Duration in milliseconds. Signed to allow for simple calculations which + * might go negative for intermediate results. + */ + Q_INT64 m_ms; + + void add(Q_INT64 delta); + void add(const Duration &delta); + + /** + * Subtracts @param delta from *this. If @param delta > *this, *this is set to zeroDuration. + */ + void subtract(const Duration &delta); +}; + +} //KPlato namespace + +#endif diff --git a/kplato/kptdurationwidget.cw b/kplato/kptdurationwidget.cw new file mode 100644 index 00000000..e622d273 --- /dev/null +++ b/kplato/kptdurationwidget.cw @@ -0,0 +1,25 @@ +<!DOCTYPE CW><CW> +<customwidgets> + <customwidget> + <class>DurationWidget</class> + <header location="local">kptdurationwidget.h</header> + <sizehint> + <width>0</width> + <height>20</height> + </sizehint> + <container>0</container> + <sizepolicy> + <hordata>5</hordata> + <verdata>0</verdata> + </sizepolicy> + <pixmap> + <data format="PNG" length="1002">89504e470d0a1a0a0000000d4948445200000016000000160806000000c4b46c3b000003b149444154789cad945f4c5b551cc73fe7dc4b7b4bcba0762d45c43114323599ee6192609c51d883892ce083f1718b3ebb185f8dc91e972cf39d2d2a2f1af664b6f1e0fe3863a0718969700eb0c52142da0242a1bd6d696f7bcff101585203ceb8fd9ece39f99dcff9fe7edf939f88c562ec465f5f9fe609442c161362173c3e3eae7b7a7ac8e7f36432196cdbfe4f907c3e4f2291201e8fe338cec3737357e9e8e828aded1e229d650e1f2d51754b082110124c13a4dc5ea341eb9dc284c0558a853f3ce8cb0677ef500fde7d39d2596679e326597b8e9abb85d7a770ab16ab6983ec5a05b487a70e36f0f4e10afe408d6a558310980108478dba4a1e8233990c5d474b64ed39aa3a8fe5f3317fbf81dbd70bccfeb205947632fd74f6589c1c6ea2f70d03a58ba0c1f2c9bdc1b66de3b8256a6e11cbe7e3ee1d181b590124fe2693aeee08d223c82c3a2c24b7b874bec8f26288774f7bd054504aef0dde6e99c0eb83f9fb266323cb80a27fb0958141836044605a2ee5523393371cc646fee2da37195aa35d0c0c5b4859ac03d7e91712dcaac5adab3650a3ff9d08ef7dd8404bb48869e5d958b5b87dadc4c9a1464e9f0d0326df7ebd86bd2e310cb1bf62d384d59441f2d70a070e1c60e09489929b988681bdd9cc97170bcc4c65595f71f8e0e3301337fc24a7732467831875a47f289652b0be5e4151e6d07316c1b0c0340d8ab92023e76d66a6b2840e36d2fb7a13fee632475e6edc367ea98a90fb98b7dd6310ca0328a44761582e1bab41befabcc0ec940d28bc5e93b68e064cab84e1d9beaeb48934eac1f53b01c1b000fca496aa54b61a99fcde61662a4b4b4b23d1680be9d426173e4df3602a48ea411989a4fd590f52a8fd156b05ed9d350e3defe3cfdf4b4c7ce770ea7d3fb9f520afbe1620daeee5c26735d20b9b9cfb6811a754a439e4e5c5639a4caa1e5caf586bfc0197b78702005cb9b4cae4cd3267ce8638fe964bd72b393e39d74928d242617303a756a37f284447770dcdbffc6384a05a85de1306e9a52057c7527c7131c3c42d3f475eb2303c82d4fc3276d6811db37efeb148723082d9b08f79f97c1e5729109a9a28307cc622d2d6cdf52b2b24efe548dedb00142009862cfa879ee1a71f6cec928353511472fbf4389148b0b0e0c108081412458dfe21c9f11351e67e7358595468246d1d1e5e38a6e9e851bc39d84ab502a669331dafec0d8ec7e3e8cb06e1a881d727d1ae40180a434a8c9db129a54126ad48a7358c2b4c5352c8c374bcccdab2bb37d8719cba79fab8211f9df218e0582c261e95f8bfc04f1a1e8bc5c4dfe0a19017a725d8c60000000049454e44ae426082</data> + </pixmap> + <signal>valueChanged()</signal> + <slot access="public">slot()</slot> + <slot access="public">setValue( const KPLato::Duration & newDuration )</slot> + <slot access="public">slot()</slot> + <slot access="public">handleFocus( int field )</slot> + </customwidget> +</customwidgets> +</CW> diff --git a/kplato/kptdurationwidget.ui b/kplato/kptdurationwidget.ui new file mode 100644 index 00000000..73944e3d --- /dev/null +++ b/kplato/kptdurationwidget.ui @@ -0,0 +1,420 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>KPlato::DurationWidget</class> +<widget class="QWidget"> + <property name="name"> + <cstring>DurationWidget</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>240</width> + <height>22</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="maximumSize"> + <size> + <width>32767</width> + <height>32767</height> + </size> + </property> + <property name="caption"> + <string>DurationWidget</string> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QFrame"> + <property name="name"> + <cstring>m_frame</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>0</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>32676</width> + <height>20</height> + </size> + </property> + <property name="paletteBackgroundColor"> + <color> + <red>255</red> + <green>255</green> + <blue>255</blue> + </color> + </property> + <property name="frameShape"> + <enum>LineEditPanel</enum> + </property> + <property name="frameShadow"> + <enum>Sunken</enum> + </property> + <property name="toolTip" stdset="0"> + <string>Use whole numbers or decimal fractions</string> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>1</number> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>m_hhSpace</cstring> + </property> + <property name="text"> + <string></string> + </property> + <property name="buddy" stdset="0"> + <cstring>m_ddd</cstring> + </property> + </widget> + <widget class="QLineEdit"> + <property name="name"> + <cstring>m_ddd</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>2</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>50</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>70</width> + <height>32767</height> + </size> + </property> + <property name="text"> + <string>0</string> + <comment>AAA</comment> + </property> + <property name="frame"> + <bool>false</bool> + </property> + <property name="alignment"> + <set>AlignRight</set> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>m_ddUnit</cstring> + </property> + <property name="text"> + <string>d</string> + </property> + <property name="buddy" stdset="0"> + <cstring>m_ddd</cstring> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>m_mmColon</cstring> + </property> + <property name="text"> + <string>:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>m_hh</cstring> + </property> + </widget> + <widget class="QLineEdit"> + <property name="name"> + <cstring>m_hh</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>2</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="maximumSize"> + <size> + <width>50</width> + <height>32767</height> + </size> + </property> + <property name="text"> + <string>00</string> + </property> + <property name="frame"> + <bool>false</bool> + </property> + <property name="alignment"> + <set>AlignRight</set> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>m_hhUnit</cstring> + </property> + <property name="text"> + <string>h</string> + </property> + <property name="buddy" stdset="0"> + <cstring>m_hh</cstring> + </property> + </widget> + <widget class="QLineEdit"> + <property name="name"> + <cstring>m_mm</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>2</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="maximumSize"> + <size> + <width>50</width> + <height>32767</height> + </size> + </property> + <property name="text"> + <string>00</string> + </property> + <property name="frame"> + <bool>false</bool> + </property> + <property name="alignment"> + <set>AlignRight</set> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>m_mmUnit</cstring> + </property> + <property name="text"> + <string>m</string> + </property> + <property name="buddy" stdset="0"> + <cstring>m_mm</cstring> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>m_ssColon</cstring> + </property> + <property name="text"> + <string>:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>m_ss</cstring> + </property> + </widget> + <widget class="QLineEdit"> + <property name="name"> + <cstring>m_ss</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>2</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="maximumSize"> + <size> + <width>50</width> + <height>32767</height> + </size> + </property> + <property name="text"> + <string>00</string> + </property> + <property name="frame"> + <bool>false</bool> + </property> + <property name="alignment"> + <set>AlignRight</set> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>m_ssUnit</cstring> + </property> + <property name="text"> + <string>s</string> + </property> + <property name="buddy" stdset="0"> + <cstring>m_ss</cstring> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>m_dot</cstring> + </property> + <property name="text"> + <string>.</string> + </property> + <property name="buddy" stdset="0"> + <cstring>m_ms</cstring> + </property> + </widget> + <widget class="QLineEdit"> + <property name="name"> + <cstring>m_ms</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>2</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>32</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>50</width> + <height>32767</height> + </size> + </property> + <property name="text"> + <string>000</string> + </property> + <property name="frame"> + <bool>false</bool> + </property> + <property name="alignment"> + <set>AlignRight</set> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>m_msUnit</cstring> + </property> + <property name="text"> + <string>ms</string> + </property> + <property name="buddy" stdset="0"> + <cstring>m_ms</cstring> + </property> + </widget> + </hbox> + </widget> + </hbox> +</widget> +<connections> + <connection> + <sender>m_ddd</sender> + <signal>lostFocus()</signal> + <receiver>DurationWidget</receiver> + <slot>dddLostFocus()</slot> + </connection> + <connection> + <sender>m_hh</sender> + <signal>lostFocus()</signal> + <receiver>DurationWidget</receiver> + <slot>hhLostFocus()</slot> + </connection> + <connection> + <sender>m_mm</sender> + <signal>lostFocus()</signal> + <receiver>DurationWidget</receiver> + <slot>mmLostFocus()</slot> + </connection> + <connection> + <sender>m_ss</sender> + <signal>lostFocus()</signal> + <receiver>DurationWidget</receiver> + <slot>ssLostFocus()</slot> + </connection> + <connection> + <sender>m_ms</sender> + <signal>lostFocus()</signal> + <receiver>DurationWidget</receiver> + <slot>msLostFocus()</slot> + </connection> +</connections> +<includes> + <include location="local" impldecl="in declaration">kptduration.h</include> + <include location="global" impldecl="in implementation">qvalidator.h</include> + <include location="global" impldecl="in implementation">qregexp.h</include> + <include location="local" impldecl="in implementation">kptdurationwidget.ui.h</include> +</includes> +<forwards> + <forward>class QRegExpValidator</forward> +</forwards> +<variables> + <variable access="public">enum { Days = 0x1, Hours = 0x2, Minutes = 0x4, Seconds = 0x8, Milliseconds = 0x10 } Fields;</variable> + <variable access="private">QString m_decimalPoint;</variable> + <variable access="private">QRegExpValidator *m_validator;</variable> + <variable access="private">struct FieldDescriptor *m_fields;</variable> +</variables> +<signals> + <signal>valueChanged()</signal> +</signals> +<slots> + <slot>setValue( const KPlato::Duration & newDuration )</slot> + <slot access="private">dddLostFocus()</slot> + <slot access="private">hhLostFocus()</slot> + <slot access="private">mmLostFocus()</slot> + <slot access="private">ssLostFocus()</slot> + <slot access="private">msLostFocus()</slot> + <slot>handleLostFocus( int field )</slot> +</slots> +<functions> + <function access="private">init()</function> + <function access="private" specifier="non virtual">destroy()</function> + <function access="private" specifier="non virtual" returnType="Q_INT64">setValueMilliseconds( Q_INT64 milliseconds )</function> + <function access="private" specifier="non virtual" returnType="Q_INT64">setValueSeconds( Q_INT64 seconds )</function> + <function access="private" specifier="non virtual" returnType="Q_INT64">setValueMinutes( Q_INT64 mins )</function> + <function access="private" specifier="non virtual" returnType="Q_INT64">setValueHours( Q_INT64 mins )</function> + <function access="private" specifier="non virtual" returnType="Q_INT64">setValueDays( Q_INT64 mins )</function> + <function returnType="Duration">value() const</function> + <function>setVisibleFields( int fieldMask )</function> + <function returnType="int">visibleFields()</function> + <function>setFieldLeftscale( int f, double ls )</function> + <function>setFieldRightscale( int f, double rs )</function> + <function>setFieldScale( int f, double scale )</function> + <function>setFieldUnit( int f, QString unit )</function> + <function access="private" specifier="non virtual" returnType="double">power( double m, int e )</function> + <function access="private" specifier="non virtual" returnType="double">fraction( QString number, int * exp )</function> +</functions> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/kplato/kptdurationwidget.ui.h b/kplato/kptdurationwidget.ui.h new file mode 100644 index 00000000..87b63c20 --- /dev/null +++ b/kplato/kptdurationwidget.ui.h @@ -0,0 +1,455 @@ +// +// ui.h extension file, included from the uic-generated form implementation. +// +// If you wish to add, delete or rename functions or slots use +// Qt Designer which will update this file, preserving your code. Create an +// init() function in place of a constructor, and a destroy() function in +// place of a destructor. +// + +/* This file is part of the KDE project + Copyright (C) 2004 - 2006 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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. +*/ + +#include <kdebug.h> +#include <kglobal.h> +#include <klocale.h> + +#include <cmath> + +namespace KPlato +{ + +/** + * This structure describes one of the fields shown. + */ +struct FieldDescriptor +{ + // Which field is to my left, and what conversion factor separates us? + QLineEdit *left; + double leftScale; + + // Which field am I, and how am I formatted? + QLineEdit *current; + const char *format; + + // Which field is to my right, and what conversion factor separates us? + QLineEdit *right; + double rightScale; + + // If I am hidden, who else hides with me? + QLabel *separator; + + // Used for calculating a correct duration + double fullScale; + double scale; + + // Used for displaying a unit behind each field + QLabel *unit; +}; + +#define setField(f, l, ls, c, fmt, r, rs, s, fs, sc, u) \ +do \ +{ \ + m_fields[f].left = l; \ + m_fields[f].leftScale = ls; \ + m_fields[f].current = c; \ + m_fields[f].format = fmt; \ + m_fields[f].right = r; \ + m_fields[f].rightScale = rs; \ + m_fields[f].separator = s; \ + m_fields[f].fullScale = fs; \ + m_fields[f].scale = sc; \ + m_fields[f].unit = u; \ +} while (0) + +void DurationWidget::init() +{ + // Use the user's decimal point! + m_decimalPoint = KGlobal::locale()->decimalSymbol(); + + //NOTE: + // This isn't as flexible/general as Shaheed once made it. + // It's now a long list of hacks. + // Introducing double in scales and allowing leftscale/scale/rightscale + // *NOT* to be multiples of each other increases the complexity and also + // introduces rounding errors. (eg. hour = 60 minutes, day = 7,5 hours) + // + // If you know how, please make a better solution! + // + // Any field can be entered as an integer or a floating point value. Whatever + // is entered is treated as follows: + // + // - any fractional part is moved right one field + // + // - any overflow from the integer part is carried left one field + // + // and the process repeated until the rightmost and leftmost fields are reached. + QRegExp re(QString("\\d{1,10}|\\d{1,7}\\") + m_decimalPoint + + QString("\\d{0,10}|\\d{0,7}\\") + m_decimalPoint + + QString("\\d{1,3}")); + m_validator = new QRegExpValidator(re, this); + m_ddd->setValidator(m_validator); + m_hh->setValidator(m_validator); + m_mm->setValidator(m_validator); + m_ss->setValidator(m_validator); + m_ms->setValidator(m_validator); + + m_ddUnit->hide(); + m_hhUnit->hide(); + m_mmUnit->hide(); + m_ssUnit->hide(); + m_msUnit->hide(); + + m_fields = new FieldDescriptor[5]; + setField(0, NULL, 0, m_ddd, "%u", m_hh, 24, m_hhSpace, 24, 24, m_ddUnit); + setField(1, m_ddd, 24, m_hh, "%02u", m_mm, 60, m_mmColon, 60, 60, m_hhUnit); + setField(2, m_hh, 60, m_mm, "%02u", m_ss, 60, NULL, 60, 60, m_mmUnit); + setField(3, m_mm, 60, m_ss, "%02u", m_ms, 1000, m_ssColon, 60, 60, m_ssUnit); + setField(4, m_ss, 1000, m_ms, "%03u", NULL, 0, m_dot, 1000, 1000, m_msUnit); +} + +void DurationWidget::destroy() +{ + delete m_fields; + //delete m_validator; //QWidget takes care of this +} + +Q_INT64 DurationWidget::setValueMilliseconds(Q_INT64 milliseconds) +{ + unsigned sc = (unsigned)m_fields[4].leftScale; + Q_INT64 secs = milliseconds / sc; + Q_INT64 ms = milliseconds % sc; + QString tmp; + tmp.sprintf(m_fields[4].format, ms); + m_fields[4].current->setText(tmp); + return secs; +} + +Q_INT64 DurationWidget::setValueSeconds(Q_INT64 seconds) +{ + unsigned sc = (unsigned)m_fields[3].leftScale; + Q_INT64 mins = seconds / sc; + Q_INT64 s = seconds % sc; + QString tmp; + tmp.sprintf(m_fields[3].format, s); + m_fields[3].current->setText(tmp); + return mins; +} + +Q_INT64 DurationWidget::setValueMinutes(Q_INT64 mins) +{ + unsigned sc = (unsigned)m_fields[2].leftScale; + Q_INT64 hours = mins / sc; + Q_INT64 m = mins % sc; + QString tmp; + tmp.sprintf(m_fields[2].format, m); + m_fields[2].current->setText(tmp); + return hours; +} + +// NOTE: Input is minutes and also returns minutes! +Q_INT64 DurationWidget::setValueHours(Q_INT64 mins) +{ + if (m_fields[1].current->isHidden()) + return mins; + unsigned sc = (unsigned)m_fields[1].rightScale; + Q_INT64 hours = (Q_INT64)(mins / sc); + Q_INT64 m = mins - (Q_INT64)(hours * sc); + //kdDebug()<<k_funcinfo<<"mins="<<mins<<" -> hours="<<hours<<" rem="<<m<<endl; + QString tmp; + tmp.sprintf(m_fields[1].format, hours); + m_fields[1].current->setText(tmp); + return m; +} + +// NOTE: Input is minutes and also returns minutes! +Q_INT64 DurationWidget::setValueDays(Q_INT64 mins) +{ + if (m_fields[0].current->isHidden()) + return mins; + double sc = m_fields[1].rightScale * m_fields[0].rightScale; + Q_INT64 days = (Q_INT64)(mins / sc); + Q_INT64 m = mins - (Q_INT64)(days * sc); + //kdDebug()<<k_funcinfo<<"mins="<<mins<<" -> days="<<days<<" rem="<<m<<endl; + QString tmp; + tmp.sprintf(m_fields[0].format, days); + m_fields[0].current->setText(tmp); + return m; +} + +void DurationWidget::setValue(const KPlato::Duration &newDuration) +{ + Q_INT64 value = newDuration.milliseconds(); + //kdDebug()<<k_funcinfo<<f<<": value="<<value<<endl; + value = setValueMilliseconds(value); // returns seconds + value = setValueSeconds(value); // returns minutes + // Now call days first to allow for fractional scales + value = setValueDays(value); // NOTE returns minutes + value = setValueHours(value); // NOTE returns minutes + value = setValueMinutes(value); // returns hours: Should be 0 + if (value > 0) kdError()<<k_funcinfo<<"Remainder > 0: "<<value<<endl; + + emit valueChanged(); +} + +Duration DurationWidget::value() const +{ + Duration d; + int i=0; + if (!m_fields[i].current->isHidden() && + m_fields[i].scale > 0 && + m_fields[i].scale <= m_fields[i].fullScale) + { + double v = m_fields[i].current->text().toDouble(); + v = v * m_fields[i].scale / m_fields[i].fullScale;; + d.addMilliseconds((Q_INT64)(v*(1000*60*60*24))); + } + ++i; + if (!m_fields[i].current->isHidden() && + m_fields[i].scale > 0 && + m_fields[i].scale <= m_fields[i].fullScale) + { + double v = m_fields[i].current->text().toDouble(); + v = v * m_fields[i].scale / m_fields[i].fullScale;; + d.addMilliseconds((Q_INT64)(v*(1000*60*60))); + } + ++i; + if (!m_fields[i].current->isHidden() && + m_fields[i].scale > 0 && + m_fields[i].scale <= m_fields[i].fullScale) + { + double v = m_fields[i].current->text().toDouble(); + v = v * m_fields[i].scale / m_fields[i].fullScale;; + d.addMilliseconds((Q_INT64)(v*(1000*60))); + } + ++i; + if (!m_fields[i].current->isHidden() && + m_fields[i].scale > 0 && + m_fields[i].scale <= m_fields[i].fullScale) + { + double v = m_fields[i].current->text().toDouble(); + v = v * m_fields[i].scale / m_fields[i].fullScale;; + d.addMilliseconds((Q_INT64)(v*(1000))); + } + ++i; + if (!m_fields[i].current->isHidden()) + { + Q_INT64 v = m_fields[i].current->text().toUInt(); + d.addMilliseconds(v); + } + return d; +} + +void DurationWidget::dddLostFocus() +{ + handleLostFocus(0); + emit valueChanged(); +} + +void DurationWidget::hhLostFocus( ) +{ + handleLostFocus(1); + emit valueChanged(); +} + +void DurationWidget::mmLostFocus() +{ + handleLostFocus(2); + emit valueChanged(); +} + +void DurationWidget::ssLostFocus() +{ + handleLostFocus(3); + emit valueChanged(); +} + +void DurationWidget::msLostFocus() +{ + handleLostFocus(4); + emit valueChanged(); +} + +void DurationWidget::handleLostFocus( + int field) +{ + // Get our own info, and that of our left and right neighbours. + QLineEdit *left = m_fields[field].left; + double leftScale = m_fields[field].leftScale; + const char *leftFormat = left ? m_fields[field - 1].format : NULL; + QLineEdit *current = m_fields[field].current; + const char *currentFormat = m_fields[field].format; + QLineEdit *right = m_fields[field].right; + double rightScale = m_fields[field].rightScale; + const char *rightFormat = right ? m_fields[field + 1].format : NULL; + + // avoid possible crash + if (leftScale == 0) + leftScale = 1; + + // Get the text and start processing... + QString newValue(current->text()); + double v = KGlobal::locale()->readNumber(newValue); + unsigned currentValue = 0; + QString tmp; + //kdDebug()<<k_funcinfo<<field<<": value="<<v<<" v="<<v<<endl; + if (left && v >= leftScale) + { + //kdDebug()<<k_funcinfo<<field<<": value="<<v<<" leftScale="<<leftScale<<endl; + // Carry overflow, recurse as required. + tmp.sprintf(leftFormat, (unsigned)(v / leftScale)); + left->setText(tmp); + handleLostFocus(field - 1); + + // Get remainder. + v = v - (tmp.toUInt() * leftScale); + newValue = KGlobal::locale()->formatNumber(v); + } + int point = newValue.find(m_decimalPoint); + if (point != -1) + { + //HACK doubles may be rounded(at fractions > 6 digits on my system) + int p; + double frac = fraction(newValue, &p); + if (right && frac > 0.0) + { + //kdDebug()<<k_funcinfo<<field<<": value="<<newValue<<" rightScale="<<rightScale<<" frac="<<frac<<" ("<<newValue.mid(point)<<")"<<endl; + // Propagate fraction + v = rightScale * (frac*power(10.0, -p)); + frac = fraction(KGlobal::locale()->formatNumber(v, 19), 0); + //kdDebug()<<k_funcinfo<<field<<": v="<<v<<" ("<<(unsigned)v<<") rest="<<frac<<endl; + if (frac > 0.0) + { + tmp = KGlobal::locale()->formatNumber(v, 19); + right->setText(tmp); + handleLostFocus(field + 1); + } else { + tmp.sprintf(rightFormat, (unsigned)(v)); + right->setText(tmp); + } + + } + // TODO keep fraction for last shown field + newValue = newValue.left(point); + } + currentValue = newValue.toUInt(); + tmp.sprintf(currentFormat, currentValue); + current->setText(tmp); +} + +// Set which fields are visible. +void DurationWidget::setVisibleFields( int fieldMask ) +{ + int i; + for (i = 0; i < 5; i++) + { + bool visible = ((fieldMask >> i) & 1) == 1; + + // Set the visibility of the fields, and of any associated separator. + if (visible) + { + m_fields[i].current->show(); + if (m_fields[i].separator) + { + m_fields[i].separator->show(); + } + if (m_fields[i].unit) + { + m_fields[i].unit->show(); + } + } + else + { + m_fields[i].current->hide(); + if (m_fields[i].separator) + { + m_fields[i].separator->hide(); + } + if (m_fields[i].unit) + { + m_fields[i].unit->hide(); + } + } + } +} + +// Retreive the visible fields. +int DurationWidget::visibleFields() +{ + int i; + int fieldMask = 0; + for (i = 0; i < 5; i++) + { + if (m_fields[i].current->isHidden()) + { + fieldMask |= (1 << i); + } + } + return fieldMask; +} + +void DurationWidget::setFieldLeftscale(int f, double ls) +{ + m_fields[f].leftScale = ls; +} + +void DurationWidget::setFieldRightscale(int f, double rs) +{ + m_fields[f].rightScale = rs; +} + +void DurationWidget::setFieldScale(int f, double scale) +{ + m_fields[f].scale = scale; +} + +void DurationWidget::setFieldUnit(int f, QString unit) +{ + if (m_fields[f].unit) + { + m_fields[f].unit->setText(unit); + } +} + +double DurationWidget::power(double m, int e) { + int c = e > 0 ? e : -e; + double value = 1; + for (int i=0; i < c; ++i) { + value = e > 0 ? value * m : value / m; + } + return value; +} + +double DurationWidget::fraction(QString number, int *exp) { + int point = number.find(m_decimalPoint); + if (point == -1) { + return 0.0; + } + QString v; + if (exp) { + v = number.mid(point+m_decimalPoint.length()); + *exp = v.length(); + + } else { + v = number.mid(point); + } + return KGlobal::locale()->readNumber(v); +} + +} //KPlato namespace diff --git a/kplato/kpteffortcostmap.h b/kplato/kpteffortcostmap.h new file mode 100644 index 00000000..7317d400 --- /dev/null +++ b/kplato/kpteffortcostmap.h @@ -0,0 +1,193 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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 KPTEFFORTCOST_H +#define KPTEFFORTCOST_H + +#include <qdatetime.h> +#include <qmap.h> + +#include "kptduration.h" + +#include <kdebug.h> + +namespace KPlato +{ + +class EffortCost +{ +public: + EffortCost() + : m_effort(Duration::zeroDuration), + m_cost(0) + {} + EffortCost(const Duration &effort, const double cost) + : m_effort(effort), + m_cost(cost) { + //kdDebug()<<k_funcinfo<<endl; + } + ~EffortCost() { + //kdDebug()<<k_funcinfo<<endl; + } + Duration effort() const { return m_effort; } + double cost() const { return m_cost; } + void setCost(double cost) { m_cost = cost; } + void add(const Duration &effort, const double cost) { + m_effort += effort; + m_cost += cost; + } + EffortCost &operator+=(const EffortCost &ec) { + add(ec.effort(), ec.cost()); + return *this; + } +private: + Duration m_effort; + double m_cost; +}; +typedef QMap<QDate, EffortCost> EffortCostDayMap; +class EffortCostMap +{ +public: + EffortCostMap() + : m_days() { + //kdDebug()<<k_funcinfo<<endl; + } + ~EffortCostMap() { + //kdDebug()<<k_funcinfo<<endl; + m_days.clear(); + } + + EffortCost effortCost(const QDate &date) const { + EffortCost ec; + if (!date.isValid()) { + kdError()<<k_funcinfo<<"Date not valid"<<endl; + return ec; + } + EffortCostDayMap::const_iterator it = m_days.find(date); + if (it != m_days.end()) + ec = it.data(); + return ec; + } + void insert(const QDate &date, const Duration &effort, const double cost) { + if (!date.isValid()) { + kdError()<<k_funcinfo<<"Date not valid"<<endl; + return; + } + m_days.insert(date, EffortCost(effort, cost)); + } + /** + * If data for this date allready exists add the new values to the old, + * else the new values are inserted. + */ + EffortCost &add(const QDate &date, const Duration &effort, const double cost) { + return add(date, EffortCost(effort, cost)); + } + /** + * If data for this date allready exists add the new values to the old, + * else the new value is inserted. + */ + EffortCost &add(const QDate &date, const EffortCost &ec) { + if (!date.isValid()) { + kdError()<<k_funcinfo<<"Date not valid"<<endl; + return zero(); + } + //kdDebug()<<k_funcinfo<<date.toString()<<endl; + return m_days[date] += ec; + } + + bool isEmpty() const { + return m_days.isEmpty(); + } + + EffortCostDayMap days() const { return m_days; } + + EffortCostMap &operator+=(const EffortCostMap &ec) { + //kdDebug()<<k_funcinfo<<"me="<<m_days.count()<<" ec="<<ec.days().count()<<endl; + if (ec.isEmpty()) { + return *this; + } + if (isEmpty()) { + m_days = ec.days(); + return *this; + } + EffortCostDayMap::const_iterator it; + for(it = ec.days().constBegin(); it != ec.days().constEnd(); ++it) { + add(it.key(), it.data()); + } + return *this; + } + EffortCost &effortCostOnDate(const QDate &date) { + return m_days[date]; + } + /// Return total cost for the next num days starting at date + double cost(const QDate &date, int num=7) { + double r=0.0; + for (int i=0; i < num; ++i) { + r += costOnDate(date.addDays(i)); + } + return r; + } + double costOnDate(const QDate &date) const { + if (!date.isValid()) { + kdError()<<k_funcinfo<<"Date not valid"<<endl; + return 0.0; + } + if (m_days.contains(date)) { + return m_days[date].cost(); + } + return 0.0; + } + Duration effortOnDate(const QDate &date) const { + if (!date.isValid()) { + kdError()<<k_funcinfo<<"Date not valid"<<endl; + return Duration::zeroDuration; + } + if (m_days.contains(date)) { + return m_days[date].effort(); + } + return Duration::zeroDuration; + } + double totalCost() const { + double cost = 0.0; + EffortCostDayMap::const_iterator it; + for(it = m_days.constBegin(); it != m_days.constEnd(); ++it) { + cost += it.data().cost(); + } + return cost; + } + Duration totalEffort() const { + Duration eff; + EffortCostDayMap::const_iterator it; + for(it = m_days.constBegin(); it != m_days.constEnd(); ++it) { + eff += it.data().effort(); + } + return eff; + } + +private: + EffortCost &zero() { return m_zero; } + +private: + EffortCost m_zero; + EffortCostDayMap m_days; +}; + +} //namespace KPlato + +#endif diff --git a/kplato/kptfactory.cc b/kplato/kptfactory.cc new file mode 100644 index 00000000..d71cdaaf --- /dev/null +++ b/kplato/kptfactory.cc @@ -0,0 +1,101 @@ +/* This file is part of the KDE project + Copyright (C) 1998, 1999, 2000 Torben Weis <weis@kde.org> + + 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. +*/ + +#include "kptfactory.h" +#include "kptpart.h" +#include "kptaboutdata.h" +#include <kinstance.h> +#include <kiconloader.h> +#include <klocale.h> +#include <kdebug.h> +#include <kstandarddirs.h> + +namespace KPlato +{ + +K_EXPORT_COMPONENT_FACTORY( libkplatopart, Factory ) + +KInstance* Factory::s_global = 0L; +KAboutData* Factory::s_aboutData = 0L; + +Factory::Factory( QObject* parent, const char* name ) + : KoFactory( parent, name ) +{ + global(); +} + +Factory::~Factory() +{ + delete s_aboutData; + s_aboutData = 0L; + delete s_global; + s_global = 0L; +} + +KParts::Part *Factory::createPartObject(QWidget *parentWidget, + const char *widgetName, + QObject* parent, const char* name, + const char* classname, + const QStringList &) +{ + // If classname is "KoDocument", our host is a koffice application + // otherwise, the host wants us as a simple part, so switch to readonly + // and single view. + bool bWantKoDocument = (strcmp(classname, "KoDocument") == 0); + + // parentWidget and widgetName are used by KoDocument for the + // "readonly+singleView" case. + Part *part = new Part(parentWidget, widgetName, parent, name, + !bWantKoDocument); + + if (!bWantKoDocument) + part->setReadWrite(false); + + return part; +} + +KAboutData* Factory::aboutData() +{ + if ( !s_aboutData ) + s_aboutData = newAboutData(); + return s_aboutData; +} + +KInstance* Factory::global() +{ + if ( !s_global ) + { + s_global = new KInstance( aboutData() ); + + // Add any application-specific resource directories here + s_global->dirs()->addResourceType("kplato_template", + KStandardDirs::kde_default("data") + "kplato/templates/"); + s_global->dirs()->addResourceType( "expression", KStandardDirs::kde_default("data") + "kplato/expression/"); + s_global->dirs()->addResourceType("toolbar", + KStandardDirs::kde_default("data") + "koffice/toolbar/"); + + // Tell the iconloader about share/apps/koffice/icons + s_global->iconLoader()->addAppDir("koffice"); + } + return s_global; +} + +} // KPlato namespace + +#include "kptfactory.moc" diff --git a/kplato/kptfactory.h b/kplato/kptfactory.h new file mode 100644 index 00000000..49e51ce9 --- /dev/null +++ b/kplato/kptfactory.h @@ -0,0 +1,51 @@ +/* This file is part of the KDE project + Copyright (C) 1998, 1999, 2000 Torben Weis <weis@kde.org> + + 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 KPLATO_FACTORY_H +#define KPLATO_FACTORY_H + +#include <KoFactory.h> + +class KAboutData; + +namespace KPlato +{ + +class Factory : public KoFactory +{ + Q_OBJECT +public: + Factory( QObject* parent = 0, const char* name = 0 ); + ~Factory(); + + virtual KParts::Part *createPartObject( QWidget *parentWidget = 0, const char *widgetName = 0, QObject *parent = 0, const char *name = 0, const char *classname = "KoDocument", const QStringList &args = QStringList() ); + + static KInstance* global(); + + // _Creates_ a KAboutData but doesn't keep ownership + static KAboutData* aboutData(); + +private: + static KInstance* s_global; + static KAboutData* s_aboutData; +}; + +} // KPlato namespace + +#endif diff --git a/kplato/kptganttview.cc b/kplato/kptganttview.cc new file mode 100644 index 00000000..ec960fe2 --- /dev/null +++ b/kplato/kptganttview.cc @@ -0,0 +1,1240 @@ +/* This file is part of the KDE project + Copyright (C) 2002 - 2005 Dag Andersen <danders@get2net.dk> + Copyright (C) 2006 Raphael Langerhorst <raphael.langerhorst@kdemail.net> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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. +*/ + +#include "kptganttview.h" + +#include "kptappointment.h" +#include "kptpart.h" +#include "kptview.h" +#include "kptcanvasitem.h" +#include "kptnode.h" +#include "kptpart.h" +#include "kptproject.h" +#include "kpttask.h" +#include "kptresource.h" +#include "kptdatetime.h" +#include "kpttaskappointmentsview.h" +#include "kptrelation.h" +#include "kptcontext.h" +#include "kptschedule.h" + +#include "KDGanttView.h" +#include "KDGanttViewItem.h" +#include "KDGanttViewTaskItem.h" +#include "KDGanttViewSummaryItem.h" +#include "KDGanttViewEventItem.h" + +#include <kdebug.h> + +#include <qsplitter.h> +#include <qvbox.h> +#include <qlayout.h> +#include <qlistview.h> +#include <qheader.h> +#include <qpopupmenu.h> +#include <qtabwidget.h> +#include <qptrlist.h> +#include <qlineedit.h> +#include <qwidget.h> +#include <qlabel.h> +#include <qspinbox.h> +#include <qstringlist.h> +#include <qvaluelist.h> +#include <qpainter.h> +#include <qpaintdevicemetrics.h> + +#include <klocale.h> +#include <kglobal.h> +#include <kprinter.h> +#include <kmessagebox.h> + +namespace KPlato +{ + +class MyKDGanttView : public KDGanttView { +public: + MyKDGanttView(QWidget *parent, const char *name) + : KDGanttView(parent, name) { + } + virtual QSize sizeHint() const { + return minimumSizeHint(); //HACK: koshell splitter minimumSize problem + } +}; + +GanttView::GanttView(QWidget *parent, bool readWrite, const char* name) + : QSplitter(parent, name), + m_readWrite(readWrite), + m_currentItem(0), + m_taskView(0), + m_firstTime(true), + m_project(0) +{ + kdDebug() << " ---------------- KPlato: Creating GanttView ----------------" << endl; + setOrientation(QSplitter::Vertical); + + m_gantt = new MyKDGanttView(this, "Gantt view"); + + m_showExpected = true; + m_showOptimistic = false; + m_showPessimistic = false; + m_showResources = false; // FIXME + m_showTaskName = false; // FIXME + m_showTaskLinks = false; // FIXME + m_showProgress = false; //FIXME + m_showPositiveFloat = false; //FIXME + m_showCriticalTasks = false; //FIXME + m_showCriticalPath = false; //FIXME + m_showNoInformation = false; //FIXME + m_showAppointments = false; + + m_gantt->setHeaderVisible(true); + m_gantt->addColumn(i18n("Work Breakdown Structure", "WBS")); + // HACK: need changes to kdgantt + KDGanttViewTaskItem *item = new KDGanttViewTaskItem(m_gantt); + QListView *lv = item->listView(); + lv->header()->moveSection(1, 0); + + m_gantt->setScale(KDGanttView::Day); + m_gantt->setShowLegendButton(false); + m_gantt->setShowHeaderPopupMenu(); + m_taskView = new TaskAppointmentsView(this); + // hide TaskAppointmentsView + QValueList<int> list = sizes(); + list[0] += list[1]; + list[1] = 0; + setSizes(list); + m_taskView->hide(); + + setReadWriteMode(readWrite); + + connect(m_gantt, SIGNAL(lvContextMenuRequested ( KDGanttViewItem *, const QPoint &, int )), + this, SLOT (popupMenuRequested(KDGanttViewItem *, const QPoint &, int))); + + connect(m_gantt, SIGNAL(lvCurrentChanged(KDGanttViewItem*)), this, SLOT (currentItemChanged(KDGanttViewItem*))); + + // HACK: kdgantt emits 2 signals for each *double* click, so we go direct to listview + connect(lv, SIGNAL(doubleClicked(QListViewItem*, const QPoint&, int)), this, SLOT (slotItemDoubleClicked(QListViewItem*))); + + m_taskLinks.setAutoDelete(true); + + if (m_gantt->firstChild()) { + m_gantt->firstChild()->listView()->setCurrentItem(m_gantt->firstChild()); + m_gantt->firstChild()->listView()->setFocus(); + } +} + +void GanttView::setZoom(double zoom) +{ + kdDebug() << "setting gantt zoom: " << zoom << endl; + m_gantt->setZoomFactor(zoom,true); + m_taskView->zoom( zoom ); +} + +void GanttView::clear() +{ + m_gantt->clear(); + m_taskView->clear(); +} + +void GanttView::draw(Project &project) +{ + m_project = &project; + //kdDebug()<<k_funcinfo<<endl; + Schedule::Type type = Schedule::Expected; + if (m_showOptimistic) { + type = Schedule::Optimistic; + } else if (m_showPessimistic) { + type = Schedule::Pessimistic; + } + Schedule *sch = project.findSchedule(type); + if (sch) { + project.setCurrentSchedule(sch->id()); + } + //kdDebug()<<k_funcinfo<<"Schedule: "<<(sch?sch->typeToString():"None")<<endl; + m_gantt->setUpdateEnabled(false); + + clear(); + drawChildren(NULL, project); + drawRelations(); + + if (m_firstTime) { + m_gantt->centerTimelineAfterShow(project.startTime().addDays(-1)); + m_firstTime = false; + } + m_gantt->setUpdateEnabled(true); + currentItemChanged(m_currentItem); +} + +void GanttView::drawChanges(Project &project) +{ + m_project = &project; //FIXME Only draw changes on same project + //kdDebug()<<k_funcinfo<<endl; + Schedule::Type type = Schedule::Expected; + if (m_showOptimistic) { + type = Schedule::Optimistic; + } else if (m_showPessimistic) { + type = Schedule::Pessimistic; + } + Schedule *sch = project.findSchedule(type); + if (sch) { + project.setCurrentSchedule(sch->id()); + } + //kdDebug()<<k_funcinfo<<"Schedule: "<<(sch?sch->typeToString():"None")<<endl; + m_gantt->setUpdateEnabled(false); + resetDrawn(m_gantt->firstChild()); + updateChildren(&project); // don't draw project + removeNotDrawn(m_gantt->firstChild()); + + m_taskLinks.clear(); + drawRelations(); + + m_gantt->setUpdateEnabled(true); + if (m_currentItem == 0 && m_gantt->firstChild()) { + m_gantt->firstChild()->listView()->setCurrentItem(m_gantt->firstChild()); + currentItemChanged(m_gantt->firstChild()); //hmmm + } + currentItemChanged(m_currentItem); +} + +void GanttView::drawOnPainter(QPainter* painter, const QRect rect) +{ + // Assume clipping is allready set + + // Fill out the rect by adding ticks to right side of the timeline + QSize s = m_gantt->drawContents(0, false, true); + while (s.width() < rect.width()) { + m_gantt->addTicksRight(); + m_gantt->setTimelineToEnd(); + s = m_gantt->drawContents(0, false, true); + } + kdDebug()<<k_funcinfo<<rect<<" : "<<s<<endl; + painter->save(); + +// QValueList<int> sizes = m_taskView->sizes(); +// if (sizes.count() >= 2) +// { +// int first = sizes[0]; +// int second = sizes[1]; +// sizes.pop_front(); +// sizes.pop_front(); +// sizes.prepend(first+second); +// sizes.prepend(0); +// m_taskView->setSizes(sizes); +// } +// else +// kdWarning() << "Apparently the task view splitter contains less than 2 parts!" << endl; + +// bool showlistview = m_gantt->showListView(); +// int listviewwidth = m_gantt->listViewWidth(); +// m_gantt->setShowListView(false); +// m_gantt->setListViewWidth(0); + +// m_gantt->setGanttMaximumWidth(rect.x()); + m_gantt->drawContents(painter,false,true); +// m_gantt->setShowListView(showlistview); +// m_gantt->setListViewWidth(listviewwidth); + +// m_taskView->drawContents(painter); //TODO doesn't seem to do very much + painter->restore(); +} + +KDGanttViewItem *GanttView::findItem(Node *node) +{ + return findItem(node, m_gantt->firstChild()); +} + +KDGanttViewItem *GanttView::findItem(Node *node, KDGanttViewItem *item) +{ + for (; item; item = item->nextSibling()) { + if (node == getNode(item)) { + return item; + } + KDGanttViewItem *i = findItem(node, item->firstChild()); + if (i) + return i; + } + return 0; +} + +Node *GanttView::getNode(KDGanttViewItem *item) const { + if (item) { + if (item->type() == KDGanttViewItem::Event){ + return static_cast<GanttViewEventItem *>(item)->getTask(); + } else if (item->type() == KDGanttViewItem::Task) { + return static_cast<GanttViewTaskItem *>(item)->getTask(); + } else if (item->type() == KDGanttViewItem::Summary) { + return static_cast<GanttViewSummaryItem *>(item)->getNode(); + } + } + return 0; +} + +bool GanttView::isDrawn(KDGanttViewItem *item) { + if (item) { + if (item->type() == KDGanttViewItem::Event){ + return static_cast<GanttViewEventItem *>(item)->isDrawn(); + } else if (item->type() == KDGanttViewItem::Task) { + return static_cast<GanttViewTaskItem *>(item)->isDrawn(); + } else if (item->type() == KDGanttViewItem::Summary) { + return static_cast<GanttViewSummaryItem *>(item)->isDrawn(); + } else { + kdWarning()<<k_funcinfo<<"Unknown item type: "<<item->type()<<endl; + } + } + return false; +} + +void GanttView::setDrawn(KDGanttViewItem *item, bool state) { + if (item) { + if (item->type() == KDGanttViewItem::Event){ + static_cast<GanttViewEventItem *>(item)->setDrawn(state); + } else if (item->type() == KDGanttViewItem::Task) { + static_cast<GanttViewTaskItem *>(item)->setDrawn(state); + } else if (item->type() == KDGanttViewItem::Summary) { + static_cast<GanttViewSummaryItem *>(item)->setDrawn(state); + } else { + kdWarning()<<k_funcinfo<<"Unknown item type: "<<item->type()<<endl; + } + } + return; +} + +void GanttView::resetDrawn(KDGanttViewItem *_item) +{ + KDGanttViewItem *nextItem, *item=_item; + for (; item; item = nextItem) { + nextItem = item->nextSibling(); + setDrawn(item, false); + resetDrawn(item->firstChild()); // then my children + } +} + +void GanttView::removeNotDrawn(KDGanttViewItem *_item) +{ + KDGanttViewItem *nextItem, *item=_item; + for (; item; item = nextItem) { + nextItem = item->nextSibling(); + if (!isDrawn(item)) { + if (item == m_currentItem) + m_currentItem = 0; + deleteItem(item); + } else { + removeNotDrawn(item->firstChild()); // then my children + } + } +} + +void GanttView::deleteItem(KDGanttViewItem *item) +{ + //kdDebug()<<k_funcinfo<<item->listViewText()<<endl; + if (item->parent()) + item->parent()->takeItem(item); + else + item->listView()->takeItem(item); + delete item; +} + +KDGanttViewItem *GanttView::correctType(KDGanttViewItem *item, Node *node) +{ + //kdDebug()<<k_funcinfo<<item->listViewText()<<": "<<item->type()<<" node: "<<node->type()<<endl; + switch (node->type()) { + case Node::Type_Project: + return item; + break; + case Node::Type_Summarytask: + case Node::Type_Subproject: + if (item->type() == KDGanttViewItem::Summary) + return item; + break; + case Node::Type_Task: + if (item->type() == KDGanttViewItem::Task) + return item; + break; + case Node::Type_Milestone: + if (item->type() == KDGanttViewItem::Event) + return item; + break; + default: + return item; + break; + } + KDGanttViewItem *newItem = addNode(item->parent(), node, item); + newItem->setOpen(item->isOpen()); + deleteItem(item); + return newItem; +} + +void GanttView::correctPosition(KDGanttViewItem *item, Node *node) +{ + KDGanttViewItem *after = findItem(node->siblingBefore()); + if (after) { + item->moveItem(after); + } +} + +KDGanttViewItem *GanttView::correctParent(KDGanttViewItem *item, Node *node) +{ + KDGanttViewItem *p = findItem(node->getParent()); + if (p == item->parent()) { + return item; + } + KDGanttViewItem *newItem = addNode(p, node); + newItem->setOpen(item->isOpen()); + deleteItem(item); + return newItem; +} + +void GanttView::updateChildren(Node *parentNode) +{ + //kdDebug()<<k_funcinfo<<endl; + QPtrListIterator<Node> nit(parentNode->childNodeIterator()); + for (; nit.current(); ++nit ) + { + updateNode(nit.current()); + } +} + +void GanttView::updateNode(Node *node) +{ + //kdDebug()<<k_funcinfo<<node->name()<<endl; + KDGanttViewItem *item = findItem(node); + if (!item) { + item = addNode(findItem(node->getParent()), node, findItem(node->siblingBefore())); + if (item && node->type() == Node::Type_Summarytask) + updateChildren(node); + return; + } + item = correctType(item, node); + item = correctParent(item, node); + correctPosition(item, node); + + modifyNode(node); + + if (node->type() == Node::Type_Summarytask) + updateChildren(node); +} + +void GanttView::modifyChildren(Node *node) +{ + //kdDebug()<<k_funcinfo<<endl; + QPtrListIterator<Node> nit(node->childNodeIterator()); + for ( nit.toLast(); nit.current(); --nit ) { + modifyNode(nit.current()); + modifyChildren(nit.current()); + } +} + +void GanttView::modifyNode(Node *node) +{ + //kdDebug()<<k_funcinfo<<endl; + KDGanttViewItem *item = findItem(node); + if (!item) { + kdDebug()<<k_funcinfo<<" Item not found"<<endl; + return; + } + if (node->type() == Node::Type_Project) { + return modifyProject(item, node); + } + if (node->type() == Node::Type_Subproject) { + return modifyProject(item, node); + } + if (node->type() == Node::Type_Summarytask) { + return modifySummaryTask(item, static_cast<Task *>(node)); + } + if (node->type() == Node::Type_Task) { + return modifyTask(item, static_cast<Task *>(node)); + } + if (node->type() == Node::Type_Milestone) { + return modifyMilestone(item, static_cast<Task *>(node)); + } + return; +} + +void GanttView::modifyProject(KDGanttViewItem *item, Node *node) +{ + //kdDebug()<<k_funcinfo<<endl; + item->setListViewText(node->name()); + item->setListViewText(1, node->wbs()); + item->setStartTime(node->startTime()); + item->setEndTime(node->endTime()); + //item->setOpen(true); + setDrawn(item, true); + +} + +void GanttView::modifySummaryTask(KDGanttViewItem *item, Task *task) +{ + //kdDebug()<<k_funcinfo<<endl; + KLocale *locale = KGlobal::locale(); + //kdDebug()<<k_funcinfo<<task->name()<<": "<<task->currentSchedule()<<", "<<task->notScheduled()<<", "<<(m_project ? m_project->notScheduled() : false)<<endl; + if (task->currentSchedule() == 0) { + item->setShowNoInformation(m_showNoInformation); + item->setStartTime(task->projectNode()->startTime()); + item->setEndTime(item->startTime().addDays(1)); + } else { + bool noinf = m_showNoInformation && (task->notScheduled() || (m_project ? m_project->notScheduled() : false /*hmmm, no project?*/)); + item->setShowNoInformation(noinf); + item->setStartTime(task->startTime()); + item->setEndTime(task->endTime()); + } + item->setListViewText(task->name()); + item->setListViewText(1, task->wbs()); + //item->setOpen(true); + if (m_showTaskName) { + item->setText(task->name()); + } else { + item->setText(QString()); + } + QString w = i18n("Name: %1").arg(task->name()); + if (!task->notScheduled()) { + w += "\n" + i18n("Start: %1").arg(locale->formatDateTime(task->startTime())); + w += "\n" + i18n("End: %1").arg(locale->formatDateTime(task->endTime())); + } + bool ok = true; + if (task->notScheduled()) { + w += "\n" + i18n("Not scheduled"); + ok = false; + } else { + if (!m_showNoInformation && m_project && m_project->notScheduled()) { + ok = false; + } + } + if (ok) { + QColor c(cyan); + item->setColors(c,c,c); + } else { + QColor c(yellow); + item->setColors(c,c,c); + } + item->setTooltipText(w); + setDrawn(item, true); +} + +void GanttView::modifyTask(KDGanttViewItem *item, Task *task) +{ + //kdDebug()<<k_funcinfo<<endl; + KLocale *locale = KGlobal::locale(); + //kdDebug()<<k_funcinfo<<task->name()<<": "<<task->currentSchedule()<<", "<<task->notScheduled()<<", "<<(m_project ? m_project->notScheduled() : false)<<endl; + item->setListViewText(task->name()); + item->setListViewText(1, task->wbs()); + if (task->currentSchedule() == 0) { + item->setShowNoInformation(m_showNoInformation); + item->setStartTime(task->projectNode()->startTime()); + item->setEndTime(item->startTime().addDays(1)); + } else { + bool noinf = m_showNoInformation && (task->notScheduled() || (m_project ? m_project->notScheduled() : false /*hmmm, no project?*/)); + item->setShowNoInformation(noinf); + item->setStartTime(task->startTime()); + item->setEndTime(task->endTime()); + } + //item->setOpen(true); + QString text; + if (m_showTaskName) { + text = task->name(); + } + if (m_showResources && !task->notScheduled()) { + QPtrList<Appointment> lst = task->appointments(); + if (lst.count() > 0) { + if (!text.isEmpty()) + text += ' '; + text += '('; + QPtrListIterator<Appointment> it = lst; + for (bool first=true; it.current(); ++it) { + if (!first) + text += ", "; + text += it.current()->resource()->resource()->name(); + first = false; + } + text += ')'; + } + } + item->setText(text); + if (m_showProgress) { + item->setProgress(task->progress().percentFinished); + } else { + item->setProgress(0); + } + if (m_showPositiveFloat) { + QDateTime t = task->endTime() + task->positiveFloat(); + if (t.isValid() && t > task->endTime()) { + item->setFloatEndTime(t); + } else { + item->setFloatEndTime(QDateTime()); + } + } else { + item->setFloatStartTime(QDateTime()); + item->setFloatEndTime(QDateTime()); + } + QString w = i18n("Name: %1").arg(task->name()); + if (!task->notScheduled()) { + w += "\n"; w += i18n("Start: %1").arg(locale->formatDateTime(task->startTime())); + w += "\n"; w += i18n("End: %1").arg(locale->formatDateTime(task->endTime())); + if (m_showProgress) { + w += "\n"; w += i18n("Completion: %1%").arg(task->progress().percentFinished); + } + if (task->positiveFloat() > Duration::zeroDuration) { + w += "\n" + i18n("Float: %1").arg(task->positiveFloat().toString(Duration::Format_i18nDayTime)); + } + if (task->inCriticalPath()) { + w += "\n" + i18n("Critical path"); + } else if (task->isCritical()) { + w += "\n" + i18n("Critical"); + } + } + QString sts; + bool ok = true; + if (task->notScheduled()) { + sts += "\n" + i18n("Not scheduled"); + ok = false; + } else { + if (task->resourceError()) { + sts += "\n" + i18n("No resource assigned"); + ok = false; + } + if (task->resourceNotAvailable()) { + sts += "\n" + i18n("Resource not available"); + ok = false; + } + if (task->schedulingError()) { + sts += "\n" + i18n("Scheduling conflict"); + ok = false; + } + if (task->effortMetError()) { + sts += "\n" + i18n("Requested effort could not be met"); + ok = false; + } + if (task->resourceOverbooked()) { + ok = false; + QStringList rl = task->overbookedResources(); + sts += "\n" + i18n("arg: list of resources", "Resource overbooked: %1").arg(rl.join(",")); + + } + if (!m_showNoInformation && m_project && m_project->notScheduled()) { + ok = false; + } + } + if (ok) { + QColor c(green); + item->setColors(c,c,c); + } else { + w += sts; + QColor c(yellow); + item->setColors(c,c,c); + } + item->setHighlight(false); + if (m_showCriticalTasks) { + item->setHighlight(task->isCritical()); + } else if (m_showCriticalPath) { + item->setHighlight(task->inCriticalPath()); + } + + item->setTooltipText(w); + setDrawn(item, true); +} + +void GanttView::modifyMilestone(KDGanttViewItem *item, Task *task) +{ + //kdDebug()<<k_funcinfo<<endl; + KLocale *locale = KGlobal::locale(); + //kdDebug()<<k_funcinfo<<task->name()<<": "<<task->currentSchedule()<<", "<<task->notScheduled()<<", "<<(m_project ? m_project->notScheduled() : false)<<endl; + if (task->currentSchedule() == 0) { + item->setShowNoInformation(m_showNoInformation); + item->setStartTime(task->projectNode()->startTime()); + } else { + bool noinf = m_showNoInformation && (task->notScheduled() || (m_project ? m_project->notScheduled() : false /*hmmm, no project?*/)); + item->setShowNoInformation(noinf); + item->setStartTime(task->startTime()); + } + item->setListViewText(task->name()); + item->setListViewText(1, task->wbs()); + //item->setOpen(true); + if (m_showTaskName) { + item->setText(task->name()); + } else { + item->setText(QString()); + } + if (m_showPositiveFloat) { + DateTime t = task->startTime() + task->positiveFloat(); + //kdDebug()<<k_funcinfo<<task->name()<<" float: "<<t.toString()<<endl; + if (t.isValid() && t > task->startTime()) { + item->setFloatEndTime(t); + } else { + item->setFloatEndTime(QDateTime()); + } + } else { + item->setFloatStartTime(QDateTime()); + item->setFloatEndTime(QDateTime()); + } + //TODO: Show progress + + QString w = i18n("Name: %1").arg(task->name()); + if (!task->notScheduled()) { + w += "\n" + i18n("Time: %1").arg(locale->formatDateTime(task->startTime())); + + if (task->positiveFloat() > Duration::zeroDuration) { + w += "\n" + i18n("Float: %1").arg(task->positiveFloat().toString(Duration::Format_i18nDayTime)); + } + if (task->inCriticalPath()) { + w += "\n" + i18n("Critical path"); + } else if (task->isCritical()) { + w += "\n" + i18n("Critical"); + } + } + bool ok = true; + if (task->notScheduled()) { + w += "\n" + i18n("Not scheduled"); + ok = false; + } else { + if (task->schedulingError()) { + w += "\n" + i18n("Scheduling conflict"); + ok = false; + } + if (!m_showNoInformation && m_project && m_project->notScheduled()) { + ok = false; + } + } + if (ok) { + QColor c(blue); + item->setColors(c,c,c); + } else { + QColor c(yellow); + item->setColors(c,c,c); + } + item->setHighlight(false); + if (m_showCriticalTasks) { + item->setHighlight(task->isCritical()); + } else if (m_showCriticalPath) { + item->setHighlight(task->inCriticalPath()); + } + + item->setTooltipText(w); + setDrawn(item, true); +} + +KDGanttViewItem *GanttView::addNode( KDGanttViewItem *parentItem, Node *node, KDGanttViewItem *after) +{ + //kdDebug()<<k_funcinfo<<endl; + if (node->type() == Node::Type_Project) { + return addProject(parentItem, node, after); + } + if (node->type() == Node::Type_Subproject) { + return addSubProject(parentItem, node, after); + } + if (node->type() == Node::Type_Summarytask) { + return addSummaryTask(parentItem, static_cast<Task *>(node), after); + } + if (node->type() == Node::Type_Task) { + return addTask(parentItem, static_cast<Task *>(node), after); + } + if (node->type() == Node::Type_Milestone) { + return addMilestone(parentItem, static_cast<Task *>(node), after); + } + return 0; +} + +KDGanttViewItem *GanttView::addProject(KDGanttViewItem *parentItem, Node *node, KDGanttViewItem *after) +{ + //kdDebug()<<k_funcinfo<<endl; + GanttViewSummaryItem *item; + if ( parentItem) { + item = new GanttViewSummaryItem(parentItem, node); + } else { + // we are on the top level + item = new GanttViewSummaryItem(m_gantt, node); + } + if (after) + item->moveItem(after); + modifyProject(item, node); + return item; +} + +KDGanttViewItem *GanttView::addSubProject(KDGanttViewItem *parentItem, Node *node, KDGanttViewItem *after) +{ + //kdDebug()<<k_funcinfo<<endl; + return addProject(parentItem, node, after); +} + +KDGanttViewItem *GanttView::addSummaryTask(KDGanttViewItem *parentItem, Task *task, KDGanttViewItem *after) +{ + //kdDebug()<<k_funcinfo<<endl; + // display summary item + GanttViewSummaryItem *item; + if ( parentItem) { + item = new GanttViewSummaryItem(parentItem, task); + } else { + // we are on the top level + item = new GanttViewSummaryItem(m_gantt, task); + } + if (after) + item->moveItem(after); + modifySummaryTask(item, task); + return item; +} + +KDGanttViewItem *GanttView::addTask(KDGanttViewItem *parentItem, Task *task, KDGanttViewItem *after) +{ + //kdDebug()<<k_funcinfo<<endl; + // display task item + GanttViewTaskItem *item; + if ( parentItem ) { + item = new GanttViewTaskItem(parentItem, task); + } + else { + // we are on the top level + item = new GanttViewTaskItem(m_gantt, task); + } + if (after) + item->moveItem(after); + modifyTask(item, task); + return item; +} + +KDGanttViewItem *GanttView::addMilestone(KDGanttViewItem *parentItem, Task *task, KDGanttViewItem *after) +{ + //kdDebug()<<k_funcinfo<<endl; + GanttViewEventItem *item; + if ( parentItem ) { + item = new GanttViewEventItem(parentItem, task); + } else { + // we are on the top level + item = new GanttViewEventItem(m_gantt, task); + } + if (after) + item->moveItem(after); + modifyMilestone(item, task); + return item; +} + +void GanttView::drawChildren(KDGanttViewItem *parentItem, Node &parentNode) +{ + //kdDebug()<<k_funcinfo<<endl; + QPtrListIterator<Node> nit(parentNode.childNodeIterator()); + for ( nit.toLast(); nit.current(); --nit ) + { + Node *n = nit.current(); + if (n->type() == Node::Type_Project) + drawProject(parentItem, n); + else if (n->type() == Node::Type_Subproject) + drawSubProject(parentItem, n); + else if (n->type() == Node::Type_Summarytask) { + Task *t = dynamic_cast<Task *>(n); + drawSummaryTask(parentItem, t); + } else if (n->type() == Node::Type_Task) { + Task *t = dynamic_cast<Task *>(n); + drawTask(parentItem, t); + } else if (n->type() == Node::Type_Milestone) { + Task *t = dynamic_cast<Task *>(n); + drawMilestone(parentItem, t); + } + else + kdDebug()<<k_funcinfo<<"Node type "<<n->type()<<" not implemented yet"<<endl; + + } +} + + +void GanttView::drawProject(KDGanttViewItem *parentItem, Node *node) +{ + //kdDebug()<<k_funcinfo<<endl; + GanttViewSummaryItem *item = dynamic_cast<GanttViewSummaryItem*>(addProject(parentItem, node)); + drawChildren(item, *node); +} + +void GanttView::drawSubProject(KDGanttViewItem *parentItem, Node *node) +{ + //kdDebug()<<k_funcinfo<<endl; + GanttViewSummaryItem *item = dynamic_cast<GanttViewSummaryItem*>(addSubProject(parentItem, node)); + drawChildren(item, *node); +} + +void GanttView::drawSummaryTask(KDGanttViewItem *parentItem, Task *task) +{ + //kdDebug()<<k_funcinfo<<endl; + GanttViewSummaryItem *item = dynamic_cast<GanttViewSummaryItem*>(addSummaryTask(parentItem, task)); + drawChildren(item, *task); +} + +void GanttView::drawTask(KDGanttViewItem *parentItem, Task *task) +{ + //kdDebug()<<k_funcinfo<<endl; + addTask(parentItem, task); +} + +void GanttView::drawMilestone(KDGanttViewItem *parentItem, Task *task) +{ + //kdDebug()<<k_funcinfo<<endl; + addMilestone(parentItem, task); +} + +void GanttView::addTaskLink(KDGanttViewTaskLink *link) { + //kdDebug()<<k_funcinfo<<endl; + m_taskLinks.append(link); +} + +void GanttView::drawRelations() +{ + if (!m_showTaskLinks) + return; + KDGanttViewItem *item = m_gantt->firstChild(); + //kdDebug()<<k_funcinfo<<"First: "<<(item ? item->listViewText() : "nil")<<endl; + for (; item; item = item->nextSibling()) + { + drawRelations(item); + drawChildRelations(item->firstChild()); + } +} + +void GanttView::drawChildRelations(KDGanttViewItem *item) +{ + //kdDebug()<<k_funcinfo<<"item: "<<(item ? item->listViewText() : "nil")<<endl; + for (; item; item = item->nextSibling()) + { + drawRelations(item); + drawChildRelations(item->firstChild()); + } +} + +void GanttView::drawRelations(KDGanttViewItem *item) +{ + //kdDebug()<<k_funcinfo<<endl; + if (!item) return; + + GanttViewSummaryItem *summaryItem = dynamic_cast<GanttViewSummaryItem *>(item); + if (summaryItem) + { + //kdDebug()<<k_funcinfo<<"Summary item: "<<summaryItem->listViewText()<<endl; + summaryItem->insertRelations(this); + return; + } + GanttViewTaskItem *taskItem = dynamic_cast<GanttViewTaskItem *>(item); + if (taskItem) + { + //kdDebug()<<k_funcinfo<<"Task item: "<<taskItem->listViewText()<<endl; + taskItem->insertRelations(this); + return; + } + GanttViewEventItem *milestoneItem = dynamic_cast<GanttViewEventItem *>(item); + if (milestoneItem) + { + //kdDebug()<<k_funcinfo<<"Milestone item: "<<milestoneItem->listViewText()<<endl; + milestoneItem->insertRelations(this); + return; + } + kdDebug()<<k_funcinfo<<"Unknown item type: "<<item->listViewText()<<endl; +} + +void GanttView::currentItemChanged(KDGanttViewItem* item) +{ + //kdDebug()<<k_funcinfo<<(item ? item->listViewText() : "null")<<endl; + m_taskView->clear(); + m_gantt->setSelected(m_currentItem, false); + m_currentItem = item; + if (item) { + m_gantt->setSelected(item, true); + if (m_showAppointments) { + m_taskView->show(); + GanttViewTaskItem *taskItem = dynamic_cast<GanttViewTaskItem *>(item); + if (taskItem) { + m_taskView->draw(taskItem->getTask()); + } else { + GanttViewEventItem *msItem = dynamic_cast<GanttViewEventItem *>(item); + if (msItem) + m_taskView->draw(msItem->getTask()); + } + } else { + m_taskView->hide(); + } + } + emit enableActions(true); +} + +Node *GanttView::currentNode() const +{ + return getNode(m_currentItem); +} + +void GanttView::popupMenuRequested(KDGanttViewItem * item, const QPoint & pos, int) +{ + //kdDebug()<<k_funcinfo<<(item?item->listViewText(0):"0")<<endl; + if (item == 0) { + kdDebug()<<"No item selected"<<endl; + return; + } + Node *n = getNode(item); + if (n == 0) { + kdDebug()<<"No node selected"<<endl; + return; + } + Task *t = dynamic_cast<Task*>(n); + if (t && (t->type() == Node::Type_Task || t->type() == Node::Type_Milestone)) { + emit requestPopupMenu("task_popup",pos); +// QPopupMenu *menu = m_mainview->popupMenu("task_popup"); +// if (menu) +// { +// /*int id =*/ menu->exec(pos); + //kdDebug()<<k_funcinfo<<"id="<<id<<endl; +// } + return; + } + if (t && t->type() == Node::Type_Summarytask) { + emit requestPopupMenu("summarytask_popup",pos); +// QPopupMenu *menu = m_mainview->popupMenu("summarytask_popup"); +// if (menu) +// { +// /*int id =*/ menu->exec(pos); + //kdDebug()<<k_funcinfo<<"id="<<id<<endl; +// } + return; + } + //TODO: Other nodetypes +} + +void GanttView::slotItemDoubleClicked(QListViewItem* item) { + //kdDebug()<<k_funcinfo<<endl; + if (item == 0 || item->childCount() > 0) { + // FIXME: How else to avoid interference wirh expanding/collapsing summary items? + return; + } + emit itemDoubleClicked(); +} + +//TODO: 1) make it koffice compliant, +// 2) allow printing on multiple pages +void GanttView::print(KPrinter &prt) { + //kdDebug()<<k_funcinfo<<endl; + + KDGanttViewItem *selItem = m_gantt->selectedItem(); + if (selItem) + selItem->setSelected(false); + + //Comment from KWord + // We don't get valid metrics from the printer - and we want a better resolution + // anyway (it's the PS driver that takes care of the printer resolution). + //But KSpread uses fixed 300 dpis, so we can use it. + + QPaintDeviceMetrics metrics( &prt ); + uint top, left, bottom, right; + prt.margins(&top, &left, &bottom, &right); + //kdDebug()<<m.width()<<"x"<<m.height()<<" : "<<top<<", "<<left<<", "<<bottom<<", "<<right<<" : "<<size()<<endl; + + // get the size of the desired output for scaling. + // here we want to print: ListView and TimeLine (default) + // for this purpose, we call drawContents() with a 0 pointer as painter + QSize size = m_gantt->drawContents(0); + + QPainter p; + p.begin( &prt ); + p.setViewport(left, top, metrics.width()-left-right, metrics.height()-top-bottom); + p.setClipRect(left, top, metrics.width()-left-right, metrics.height()-top-bottom); + + // Make a simple header + p.drawRect(0,0,metrics.width(),metrics.height()); + QString text; + int hei = 0; + text = KGlobal::locale()->formatDateTime(QDateTime::currentDateTime()); + QRect r = p.boundingRect(metrics.width()-1,0,0,0, Qt::AlignRight, text ); + p.drawText( r, Qt::AlignRight, text ); + hei = r.height(); + //kdDebug()<<"Date r="<<r.left()<<","<<r.top()<<" "<<r.width()<<"x"<<r.height()<<endl; + if (m_project) + { + QRect re = p.boundingRect(1,0,0,0, Qt::AlignLeft, text ); + re.setWidth(metrics.width()-r.width()-5); // don't print on top of date + p.drawText( re, Qt::AlignLeft, m_project->name() ); + hei = r.height(); + //kdDebug()<<"Project r="<<re.left()<<","<<re.top()<<" "<<re.width()<<"x"<<re.height()<<": "<<endl; + hei = QMAX(hei, re.height()); + } + + hei++; + p.drawLine(0,hei,metrics.width(),hei); + hei += 3; + // compute the scale + float dx = (float) (metrics.width()-2) / (float)size.width(); + float dy = (float)(metrics.height()-hei) / (float)size.height(); + float scale; + // scale to fit the width or height of the paper + if ( dx < dy ) + scale = dx; + else + scale = dy; + // set the scale + p.translate(1,hei); + p.scale( scale, scale ); + m_gantt->drawContents(&p); + // the drawContents() has the side effect, that the painter translation is + // after drawContents() set to the bottom of the painted stuff + // for instance a + // p.drawText(0, 0, "printend"); + // would be painted directly below the paintout of drawContents() + + p.end(); + if (selItem) + selItem->setSelected(true); +} + +void GanttView::slotItemRenamed(KDGanttViewItem* item, int col, const QString& str) { + //kdDebug()<<k_funcinfo<<(item ? item->listViewText(col) : "null")<<": "<<str<<endl; + if (col == 0) { + emit itemRenamed(getNode(item), str); + } +} + + void GanttView::slotGvItemClicked(KDGanttViewItem *) { +} + +// testing +bool GanttView::exportGantt(QIODevice* device) { + kdDebug()<<k_funcinfo<<endl; + return m_gantt->saveProject(device); +} + +void GanttView::slotLinkItems(KDGanttViewItem* from, KDGanttViewItem* to, int linkType) { + //kdDebug()<<k_funcinfo<<(from?from->listViewText():"null")<<" to "<<(to?to->listViewText():"null")<<" linkType="<<linkType<<endl; + Node *par = getNode(from); + Node *child = getNode(to); + if (!par || !child || !(par->legalToLink(child))) { + KMessageBox::sorry(this, i18n("Cannot link these nodes")); + return; + } + Relation *rel = child->findRelation(par); + if (rel) + emit modifyRelation(rel, linkTypeToRelation(linkType)); + else + emit addRelation(par, child, linkTypeToRelation(linkType)); + + return; +} + +int GanttView::linkTypeToRelation(int linkType) { + switch (linkType) { + case KDGanttViewTaskLink::FinishStart: + return Relation::FinishStart; + break; + case KDGanttViewTaskLink::StartStart: + return Relation::StartStart; + break; + case KDGanttViewTaskLink::FinishFinish: + return Relation::FinishFinish; + break; + case KDGanttViewTaskLink::StartFinish: + default: + return -1; + break; + } +} + +void GanttView::slotModifyLink(KDGanttViewTaskLink* link) { + //kdDebug()<<k_funcinfo<<link<<endl; + // we support only one from/to item in each link + Node *par = getNode(link->from().first()); + Relation *rel = par->findRelation(getNode(link->to().first())); + if (rel) + emit modifyRelation(rel); +} + +bool GanttView::setContext(Context::Ganttview &context, Project& /*project*/) { + //kdDebug()<<k_funcinfo<<endl; + + QValueList<int> list = sizes(); + list[0] = context.ganttviewsize; + list[1] = context.taskviewsize; + setSizes(list); + + //TODO this does not work yet! +// currentItemChanged(findItem(project.findNode(context.currentNode))); + + m_showResources = context.showResources ; + m_showTaskName = context.showTaskName; + m_showTaskLinks = context.showTaskLinks; + m_showProgress = context.showProgress; + m_showPositiveFloat = context.showPositiveFloat; + m_showCriticalTasks = context.showCriticalTasks; + m_showCriticalPath = context.showCriticalPath; + m_showNoInformation = context.showNoInformation; + + //TODO this does not work yet! +// getContextClosedNodes(context, m_gantt->firstChild()); +// for (QStringList::ConstIterator it = context.closedNodes.begin(); it != context.closedNodes.end(); ++it) { +// KDGanttViewItem *item = findItem(project.findNode(*it)); +// if (item) { +// item->setOpen(false); +// } +// } + return true; +} + +void GanttView::getContext(Context::Ganttview &context) const { + //kdDebug()<<k_funcinfo<<endl; + context.ganttviewsize = sizes()[0]; + context.taskviewsize = sizes()[1]; + //kdDebug()<<k_funcinfo<<"sizes="<<sizes()[0]<<","<<sizes()[1]<<endl; + if (currentNode()) { + context.currentNode = currentNode()->id(); + } + context.showResources = m_showResources; + context.showTaskName = m_showTaskName; + context.showTaskLinks = m_showTaskLinks; + context.showProgress = m_showProgress; + context.showPositiveFloat = m_showPositiveFloat; + context.showCriticalTasks = m_showCriticalTasks; + context.showCriticalPath = m_showCriticalPath; + context.showNoInformation = m_showNoInformation; + getContextClosedNodes(context, m_gantt->firstChild()); +} + +void GanttView::getContextClosedNodes(Context::Ganttview &context, KDGanttViewItem *item) const { + if (item == 0) + return; + for (KDGanttViewItem *i = item; i; i = i->nextSibling()) { + if (!i->isOpen()) { + context.closedNodes.append(getNode(i)->id()); + //kdDebug()<<k_funcinfo<<"add closed "<<i->listViewText()<<endl; + } + getContextClosedNodes(context, i->firstChild()); + } +} + +void GanttView::setReadWriteMode(bool on) { + m_readWrite = on; + disconnect(m_gantt, SIGNAL(linkItems(KDGanttViewItem*, KDGanttViewItem*, int)), this, SLOT(slotLinkItems(KDGanttViewItem*, KDGanttViewItem*, int))); + disconnect(m_gantt, SIGNAL(taskLinkDoubleClicked(KDGanttViewTaskLink*)), this, SLOT(slotModifyLink(KDGanttViewTaskLink*))); + m_gantt->setLinkItemsEnabled(on); + + if (on) { + connect(m_gantt, SIGNAL(linkItems(KDGanttViewItem*, KDGanttViewItem*, int)), SLOT(slotLinkItems(KDGanttViewItem*, KDGanttViewItem*, int))); + + connect(m_gantt, SIGNAL(taskLinkDoubleClicked(KDGanttViewTaskLink*)), SLOT(slotModifyLink(KDGanttViewTaskLink*))); + } + setRenameEnabled(m_gantt->firstChild(), on); +} + +void GanttView::setRenameEnabled(QListViewItem *item, bool on) { + if (item == 0) + return; + for (QListViewItem *i = item; i; i = i->nextSibling()) { + i->setRenameEnabled(0, on); + setRenameEnabled(i->firstChild(), on); + } +} + +} //KPlato namespace + +#include "kptganttview.moc" diff --git a/kplato/kptganttview.h b/kplato/kptganttview.h new file mode 100644 index 00000000..8a249abc --- /dev/null +++ b/kplato/kptganttview.h @@ -0,0 +1,211 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Dag Andersen <danders@get2net.dk> + Copyright (C) 2006 Raphael Langerhorst <raphael.langerhorst@kdemail.net> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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 KPTGANTTVIEW_H +#define KPTGANTTVIEW_H + +#include "kptcontext.h" + +#include <qsplitter.h> +#include <qcursor.h> + +class QLayout; +class QListViewItem; +class QPoint; +class QListView; +class QLineEdit; +class QSpinBox; + +class KDGanttViewSummaryItem; +class KDGanttViewTaskItem; +class KDGanttViewEventItem; +class KDGanttViewItem; +class KDGanttViewTaskLink; + +class KPrinter; + +namespace KPlato +{ + +class MyKDGanttView; +class TaskAppointmentsView; + +class Node; +class Task; +class Project; +class Relation; + +class GanttView : public QSplitter +{ + Q_OBJECT + + public: + + GanttView(QWidget *parent, bool readWrite=true, const char* name = 0 ); + + //~GanttView(); + + void setZoom(double zoom); + + void draw(Project &project); + void drawChanges(Project &project); + + /** + * Call draw() or drawChanges() before calling this. + */ + void drawOnPainter(QPainter* painter, const QRect rect); + + Node *currentNode() const; + + void clear(); + void print(KPrinter &prts); + + void addTaskLink(KDGanttViewTaskLink *link); + + bool exportGantt(QIODevice* device); // testing + + virtual bool setContext(Context::Ganttview &context, Project &project); + virtual void getContext(Context::Ganttview &context) const; + + void setReadWriteMode(bool on); + bool isReadWriteMode() const { return m_readWrite; } + KDGanttViewItem *currentItem() const { return m_currentItem; } + + bool showNoInformation() const { return m_showNoInformation; } + +signals: + void enableActions(bool); + void modifyRelation(Relation *rel) ; + void addRelation(Node *par, Node *child); + void modifyRelation(Relation *rel, int linkType) ; + void addRelation(Node *par, Node *child, int linkType); + void itemDoubleClicked(); + + void itemRenamed(Node*, const QString&); + + /** + * Requests a specific type of popup menu. + * Usually a KPlato::View object is connected to this signal. + */ + void requestPopupMenu(const QString& menuname, const QPoint & pos); + +public slots: + /** + * Determines the correct type of popup menu and emits requestPopupMenu() + */ + void popupMenuRequested(KDGanttViewItem * item, const QPoint & pos, int); + + void setShowExpected(bool on) { m_showExpected = on; } + void setShowOptimistic(bool on) { m_showOptimistic = on; } + void setShowPessimistic(bool on) { m_showPessimistic = on; } + void setShowResources(bool on) { m_showResources = on; } + void setShowTaskName(bool on) { m_showTaskName = on; } + void setShowTaskLinks(bool on) { m_showTaskLinks = on; } + void setShowProgress(bool on) { m_showProgress = on; } + void setShowPositiveFloat(bool on) { m_showPositiveFloat = on; } + void setShowCriticalTasks(bool on) { m_showCriticalTasks = on; } + void setShowCriticalPath(bool on) { m_showCriticalPath = on; } + void setShowNoInformation(bool on) { m_showNoInformation = on; } + void setShowAppointments(bool on) { m_showAppointments = on; } + +private slots: + void currentItemChanged(KDGanttViewItem *); + void slotItemDoubleClicked(QListViewItem*); + void slotItemRenamed(KDGanttViewItem*, int, const QString&); + + void slotLinkItems(KDGanttViewItem* from, KDGanttViewItem* to, int linkType); + + void slotGvItemClicked(KDGanttViewItem*); + + void slotModifyLink(KDGanttViewTaskLink* link); + +protected: + int linkTypeToRelation(int linkType); + void setRenameEnabled(QListViewItem *item, bool on); +private: + KDGanttViewItem *findItem(Node *node); + KDGanttViewItem *findItem(Node *node, KDGanttViewItem *item); + Node *getNode(KDGanttViewItem *item) const; + bool isDrawn(KDGanttViewItem *item); + void setDrawn(KDGanttViewItem *item, bool state); + void resetDrawn(KDGanttViewItem *_item); + void removeNotDrawn(KDGanttViewItem *_item); + void deleteItem(KDGanttViewItem *item); + KDGanttViewItem *correctType(KDGanttViewItem *item, Node *node); + void correctPosition(KDGanttViewItem *item, Node *node); + KDGanttViewItem *correctParent(KDGanttViewItem *item, Node *node); + + void updateChildren(Node *node); + void updateNode(Node *node); + + void modifyChildren(Node *node); + void modifyNode(Node *node); + void modifyProject(KDGanttViewItem *item, Node *node); + void modifySummaryTask(KDGanttViewItem *item, Task *task); + void modifyTask(KDGanttViewItem *item, Task *task); + void modifyMilestone(KDGanttViewItem *item, Task *task); + + KDGanttViewItem *addNode(KDGanttViewItem *parentItem, Node *node,KDGanttViewItem *after=0); + + KDGanttViewItem *addProject(KDGanttViewItem *parentItem, Node *node, KDGanttViewItem *after=0); + KDGanttViewItem *addSubProject(KDGanttViewItem *parentItem, Node *node, KDGanttViewItem *after=0); + KDGanttViewItem *addSummaryTask(KDGanttViewItem *parentItem, Task *task, KDGanttViewItem *after=0); + KDGanttViewItem *addTask(KDGanttViewItem *parentItem, Task *task, KDGanttViewItem *after=0); + KDGanttViewItem *addMilestone(KDGanttViewItem *parentItem, Task *task, KDGanttViewItem *after=0); + + void drawChildren(KDGanttViewItem *item, Node &node); + void drawProject(KDGanttViewItem *parentItem, Node *node); + void drawSubProject(KDGanttViewItem *parentItem, Node *node); + void drawSummaryTask(KDGanttViewItem *parentItem, Task *task); + void drawTask(KDGanttViewItem *parentItem, Task *task); + void drawMilestone(KDGanttViewItem *parentItem, Task *task); + + void drawRelations(); + void drawRelations(KDGanttViewItem *item); + void drawChildRelations(KDGanttViewItem *item); + + void getContextClosedNodes(Context::Ganttview &context, KDGanttViewItem *item) const; + +private: + bool m_readWrite; + int m_defaultFontSize; + KDGanttViewItem *m_currentItem; + MyKDGanttView *m_gantt; + TaskAppointmentsView *m_taskView; + bool m_showExpected; + bool m_showOptimistic; + bool m_showPessimistic; + bool m_showResources; + bool m_showTaskName; + bool m_showTaskLinks; + bool m_showProgress; + bool m_showPositiveFloat; + bool m_showCriticalTasks; + bool m_showCriticalPath; + bool m_showNoInformation; + bool m_showAppointments; + bool m_firstTime; + QPtrList<KDGanttViewTaskLink> m_taskLinks; + Project *m_project; +}; + +} //KPlato namespace + +#endif diff --git a/kplato/kptintervaledit.cc b/kplato/kptintervaledit.cc new file mode 100644 index 00000000..8d74b357 --- /dev/null +++ b/kplato/kptintervaledit.cc @@ -0,0 +1,101 @@ +/* This file is part of the KDE project + Copyright (C) 2004 - 2006 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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. +*/ + +#include "kptintervaledit.h" +#include "intervalitem.h" + +#include <qpushbutton.h> +#include <qcombobox.h> +#include <qheader.h> +#include <qlabel.h> +#include <qlineedit.h> +#include <qdatetimeedit.h> +#include <qdatetime.h> +#include <qlistview.h> +#include <qpair.h> +#include <qdatetime.h> + +#include <klocale.h> +#include <kdebug.h> + +namespace KPlato +{ + +IntervalEdit::IntervalEdit(QWidget *parent, const char *name) + : IntervalEditImpl(parent) +{ + //kdDebug()<<k_funcinfo<<endl; + +} + +//-------------------------------------------- +IntervalEditImpl::IntervalEditImpl(QWidget *parent) + : IntervalEditBase(parent) { + + intervalList->header()->setStretchEnabled(true); + intervalList->setSortColumn(0); + + connect(bClear, SIGNAL(clicked()), SLOT(slotClearClicked())); + connect(bAddInterval, SIGNAL(clicked()), SLOT(slotAddIntervalClicked())); + connect(intervalList, SIGNAL(selectionChanged(QListViewItem*)), SLOT(slotIntervalSelectionChanged(QListViewItem*))); + +} + +void IntervalEditImpl::slotClearClicked() { + bool c = intervalList->firstChild() != 0; + intervalList->clear(); + if (c) + emit changed(); +} + +void IntervalEditImpl::slotAddIntervalClicked() { + new IntervalItem(intervalList, startTime->time(), endTime->time()); + emit changed(); +} + +void IntervalEditImpl::slotIntervalSelectionChanged(QListViewItem *item) { + IntervalItem *ii = dynamic_cast<IntervalItem *>(item); + if (!ii) + return; + startTime->setTime(ii->interval().first); + endTime->setTime(ii->interval().second); +} + +QPtrList<QPair<QTime, QTime> > IntervalEditImpl::intervals() const { + QPtrList<QPair<QTime, QTime> > l; + QListViewItem *i = intervalList->firstChild(); + for (; i; i = i->nextSibling()) { + IntervalItem *item = dynamic_cast<IntervalItem*>(i); + if (i) + l.append(new QPair<QTime, QTime>(item->interval().first, item->interval().second)); + } + return l; +} + +void IntervalEditImpl::setIntervals(const QPtrList<QPair<QTime, QTime> > &intervals) const { + intervalList->clear(); + QPtrListIterator<QPair<QTime, QTime> > it =intervals; + for (; it.current(); ++it) { + new IntervalItem(intervalList, it.current()->first, it.current()->second); + } +} + +} //KPlato namespace + +#include "kptintervaledit.moc" diff --git a/kplato/kptintervaledit.h b/kplato/kptintervaledit.h new file mode 100644 index 00000000..981b4349 --- /dev/null +++ b/kplato/kptintervaledit.h @@ -0,0 +1,61 @@ +/* This file is part of the KDE project + Copyright (C) 2004 - 2006 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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 KPTINTERVALEDIT_H +#define KPTINTERVALEDIT_H + +#include "kptintervaleditbase.h" + +#include <kdialogbase.h> + +#include <qstring.h> +#include <qptrlist.h> +#include <qpair.h> +#include <qwidget.h> + +namespace KPlato +{ + +class IntervalEditImpl : public IntervalEditBase { + Q_OBJECT +public: + IntervalEditImpl(QWidget *parent); + + QPtrList<QPair<QTime, QTime> > intervals() const; + void setIntervals(const QPtrList<QPair<QTime, QTime> > &intervals) const; + +private slots: + void slotClearClicked(); + void slotAddIntervalClicked(); + void slotIntervalSelectionChanged(QListViewItem *item); +signals: + void changed(); + +}; + +class IntervalEdit : public IntervalEditImpl { + Q_OBJECT +public: + IntervalEdit(QWidget *parent=0, const char *name=0); + +}; + +} //KPlato namespace + +#endif // INTERVALEDIT_H diff --git a/kplato/kptintervaleditbase.ui b/kplato/kptintervaleditbase.ui new file mode 100644 index 00000000..69ad5288 --- /dev/null +++ b/kplato/kptintervaleditbase.ui @@ -0,0 +1,124 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>KPlato::IntervalEditBase</class> +<author>Dag Andersen</author> +<widget class="QWidget"> + <property name="name"> + <cstring>IntervalEditBase</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>278</width> + <height>237</height> + </rect> + </property> + <property name="caption"> + <string>CalendarEditBase</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QListView"> + <column> + <property name="text"> + <string>Work Interval</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <property name="name"> + <cstring>intervalList</cstring> + </property> + <property name="focusPolicy"> + <enum>NoFocus</enum> + </property> + <property name="showSortIndicator"> + <bool>true</bool> + </property> + <property name="resizeMode"> + <enum>AllColumns</enum> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout6</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QTimeEdit"> + <property name="name"> + <cstring>startTime</cstring> + </property> + </widget> + <widget class="QTimeEdit"> + <property name="name"> + <cstring>endTime</cstring> + </property> + </widget> + </hbox> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout3</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <spacer> + <property name="name"> + <cstring>spacer1</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>51</width> + <height>21</height> + </size> + </property> + </spacer> + <widget class="QPushButton"> + <property name="name"> + <cstring>bClear</cstring> + </property> + <property name="text"> + <string>Clear</string> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>bAddInterval</cstring> + </property> + <property name="text"> + <string>Add Interval</string> + </property> + </widget> + </hbox> + </widget> + </vbox> +</widget> +<tabstops> + <tabstop>startTime</tabstop> + <tabstop>endTime</tabstop> + <tabstop>bClear</tabstop> + <tabstop>bAddInterval</tabstop> + <tabstop>intervalList</tabstop> +</tabstops> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/kplato/kptmainprojectdialog.cc b/kplato/kptmainprojectdialog.cc new file mode 100644 index 00000000..32d0a0f1 --- /dev/null +++ b/kplato/kptmainprojectdialog.cc @@ -0,0 +1,67 @@ +/* This file is part of the KDE project + Copyright (C) 2003 - 2005 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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. +*/ + +#include <qvbox.h> + +#include <klocale.h> +#include <kcommand.h> + +#include <kdebug.h> + +#include "kptmainprojectdialog.h" +#include "kptproject.h" +#include "kptmainprojectpanel.h" + +namespace KPlato +{ + +MainProjectDialog::MainProjectDialog(Project &p, QWidget *parent, const char *name) + : KDialogBase( Swallow, i18n("Project Settings"), Ok|Cancel, Ok, parent, name, true, true), + project(p) +{ + panel = new MainProjectPanel(project, this); + + setMainWidget(panel); + enableButtonOK(false); + resize( QSize(500, 410).expandedTo(minimumSizeHint())); + connect(panel, SIGNAL(obligatedFieldsFilled(bool)), SLOT(enableButtonOK(bool))); +} + + +void MainProjectDialog::slotOk() { + if (!panel->ok()) + return; + + accept(); +} + +KCommand *MainProjectDialog::buildCommand(Part *part) { + KMacroCommand *m = 0; + QString c = i18n("Modify main project"); + KCommand *cmd = panel->buildCommand(part); + if (cmd) { + if (!m) m = new KMacroCommand(c); + m->addCommand(cmd); + } + return m; +} + +} //KPlato namespace + +#include "kptmainprojectdialog.moc" diff --git a/kplato/kptmainprojectdialog.h b/kplato/kptmainprojectdialog.h new file mode 100644 index 00000000..986b9f25 --- /dev/null +++ b/kplato/kptmainprojectdialog.h @@ -0,0 +1,52 @@ +/* This file is part of the KDE project + Copyright (C) 2003 - 2005 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License + + 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 KPTMAINPROJECTDIALOG_H +#define KPTMAINPROJECTDIALOG_H + +#include <kdialogbase.h> + +class KCommand; + +namespace KPlato +{ + +class Project; +class MainProjectPanel; +class Part; + + +class MainProjectDialog : public KDialogBase { + Q_OBJECT +public: + MainProjectDialog(Project &project, QWidget *parent=0, const char *name=0); + + KCommand *buildCommand(Part *part); + +protected slots: + void slotOk(); + +private: + Project &project; + MainProjectPanel *panel; +}; + +} //KPlato namespace + +#endif // MAINPROJECTDIALOG_H diff --git a/kplato/kptmainprojectpanel.cc b/kplato/kptmainprojectpanel.cc new file mode 100644 index 00000000..119a53f8 --- /dev/null +++ b/kplato/kptmainprojectpanel.cc @@ -0,0 +1,240 @@ +/* This file is part of the KDE project + Copyright (C) 2004, 2005 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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. +*/ + +#include "kptmainprojectpanel.h" + +#include <qcheckbox.h> +#include <qbuttongroup.h> +#include <qdatetime.h> +#include <qdatetimeedit.h> +#include <qradiobutton.h> +#include <qpushbutton.h> + +#include <qlabel.h> +#include <klineedit.h> +#include <ktextedit.h> +#include <kdatewidget.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <kcommand.h> +#include <kabc/addressee.h> +#include <kabc/addresseedialog.h> + +#include <kdebug.h> + +#include "kptproject.h" +#include "kptcommand.h" +#include "kptschedule.h" + +namespace KPlato +{ + +MainProjectPanel::MainProjectPanel(Project &p, QWidget *parent, const char *name) + : MainProjectPanelImpl(parent, name), + project(p) +{ + namefield->setText(project.name()); + idfield->setText(project.id()); + leaderfield->setText(project.leader()); + descriptionfield->setText(project.description()); + wbs->setText(project.wbs()); + + //baseline->setChecked(project.isBaselined()); FIXME: Removed for this release + + QDateTime st = project.constraintStartTime(); + QDateTime et = project.constraintEndTime(); + QString s = i18n("Scheduling"); + Schedule *sch = project.currentSchedule(); + if (sch) { + s = i18n("Scheduling (%1)").arg(sch->typeToString(true)); + } + schedulingGroup->setTitle(s); + if (project.constraint() == Node::MustStartOn) { + schedulingGroup->setButton(0); + if (sch) + et = project.endTime(); + } else if (project.constraint() == Node::MustFinishOn) { + schedulingGroup->setButton(1); + if (sch) + st = project.startTime(); + } else { + kdWarning()<<k_funcinfo<<"Illegal constraint: "<<project.constraint()<<endl; + schedulingGroup->setButton(0); + if (sch) + et = project.endTime(); + } + startDate->setDate(st.date()); + startTime->setTime(st.time()); + endDate->setDate(et.date()); + endTime->setTime(et.time()); + enableDateTime(); + //slotBaseline(); + namefield->setFocus(); +} + + +bool MainProjectPanel::ok() { + if (idfield->text() != project.id() && project.findNode(idfield->text())) { + KMessageBox::sorry(this, i18n("Project id must be unique")); + idfield->setFocus(); + return false; + } + return true; +} + +KCommand *MainProjectPanel::buildCommand(Part *part) { + KMacroCommand *m = 0; + QString c = i18n("Modify main project"); + if (project.name() != namefield->text()) { + if (!m) m = new KMacroCommand(c); + m->addCommand(new NodeModifyNameCmd(part, project, namefield->text())); + } + if (project.id() != idfield->text()) { + if (!m) m = new KMacroCommand(c); + m->addCommand(new NodeModifyIdCmd(part, project, idfield->text())); + } + if (project.leader() != leaderfield->text()) { + if (!m) m = new KMacroCommand(c); + m->addCommand(new NodeModifyLeaderCmd(part, project, leaderfield->text())); + } + if (project.description() != descriptionfield->text()) { + if (!m) m = new KMacroCommand(c); + m->addCommand(new NodeModifyDescriptionCmd(part, project, descriptionfield->text())); + } +/* FIXME: Removed for this release + if (baseline->isChecked() != project.isBaselined()) { + if (!m) m = new KMacroCommand(c); + m->addCommand(new ProjectModifyBaselineCmd(part, project, baseline->isChecked())); + } */ + if (bStartDate->state() && project.constraint() != Node::MustStartOn) { + if (!m) m = new KMacroCommand(c); + m->addCommand(new ProjectModifyConstraintCmd(part, project, Node::MustStartOn)); + } + if (bEndDate->state() && project.constraint() != Node::MustFinishOn) { + if (!m) m = new KMacroCommand(c); + m->addCommand(new ProjectModifyConstraintCmd(part, project, Node::MustFinishOn)); + } + if (bStartDate->state() && startDateTime() != project.constraintStartTime()) { + if (!m) m = new KMacroCommand(c); + m->addCommand(new ProjectModifyStartTimeCmd(part, project, startDateTime())); + } + if (bEndDate->state() && endDateTime() != project.constraintEndTime()) { + if (!m) m = new KMacroCommand(c); + m->addCommand(new ProjectModifyEndTimeCmd(part, project, endDateTime())); + } + return m; +} + +//------------------------------------------------------------------- +MainProjectPanelImpl::MainProjectPanelImpl(QWidget *parent, const char *name) + : MainProjectPanelBase(parent, name) { + + // signals and slots connections + connect( bStartDate, SIGNAL( clicked() ), this, SLOT( slotStartDateClicked() ) ); + connect( bEndDate, SIGNAL( clicked() ), this, SLOT( slotEndDateClicked() ) ); + connect( bStartDate, SIGNAL( clicked() ), this, SLOT( slotCheckAllFieldsFilled() ) ); + connect( bEndDate, SIGNAL( clicked() ), this, SLOT( slotCheckAllFieldsFilled() ) ); + connect( descriptionfield, SIGNAL( textChanged() ), this, SLOT( slotCheckAllFieldsFilled() ) ); + connect( endDate, SIGNAL( changed(QDate) ), this, SLOT( slotCheckAllFieldsFilled() ) ); + connect( endTime, SIGNAL( valueChanged(const QTime&) ), this, SLOT( slotCheckAllFieldsFilled() ) ); + connect( startDate, SIGNAL( changed(QDate) ), this, SLOT( slotCheckAllFieldsFilled() ) ); + connect( startTime, SIGNAL( valueChanged(const QTime&) ), this, SLOT( slotCheckAllFieldsFilled() ) ); + //connect( baseline, SIGNAL( toggled(bool) ), this, SLOT( slotCheckAllFieldsFilled() ) ); FIXME: Removed for this release + connect( namefield, SIGNAL( textChanged(const QString&) ), this, SLOT( slotCheckAllFieldsFilled() ) ); + connect( idfield, SIGNAL( textChanged(const QString&) ), this, SLOT( slotCheckAllFieldsFilled() ) ); + connect( leaderfield, SIGNAL( textChanged(const QString&) ), this, SLOT( slotCheckAllFieldsFilled() ) ); + //connect( baseline, SIGNAL( toggled(bool) ), this, SLOT( slotBaseline() ) ); FIXME: Removed for this release + connect( chooseLeader, SIGNAL( clicked() ), this, SLOT( slotChooseLeader() ) ); +} + +void MainProjectPanelImpl::slotCheckAllFieldsFilled() +{ + emit changed(); + emit obligatedFieldsFilled(!namefield->text().isEmpty() && !idfield->text().isEmpty() && !leaderfield->text().isEmpty()); +} + + +void MainProjectPanelImpl::slotChooseLeader() +{ + KABC::Addressee a = KABC::AddresseeDialog::getAddressee(this); + if (!a.isEmpty()) + { + leaderfield->setText(a.fullEmail()); + } +} + + +void MainProjectPanelImpl::slotStartDateClicked() +{ + enableDateTime(); +} + + +void MainProjectPanelImpl::slotEndDateClicked() +{ + enableDateTime(); +} + + + +void MainProjectPanelImpl::enableDateTime() +{ + if (schedulingGroup->selected() == bStartDate) + { + startTime->setEnabled(true); + startDate->setEnabled(true); + endTime->setEnabled(false); + endDate->setEnabled(false); + } + if (schedulingGroup->selected() == bEndDate) + { + startTime->setEnabled(false); + startDate->setEnabled(false); + endTime->setEnabled(true); + endDate->setEnabled(true); + } +} + + +QDateTime MainProjectPanelImpl::startDateTime() +{ + return QDateTime(startDate->date(), startTime->time()); +} + + +QDateTime MainProjectPanelImpl::endDateTime() +{ + return QDateTime(endDate->date(), endTime->time()); +} + + +void MainProjectPanelImpl::slotBaseline() +{ + bool b = false; + //b = baseline->isChecked(); FIXME: Removed for this release + namefield->setReadOnly(b); + idfield->setReadOnly(b); + leaderfield->setReadOnly(b); + chooseLeader->setEnabled(!b); + schedulingGroup->setEnabled(!b); +} + +} //KPlato namespace + +#include "kptmainprojectpanel.moc" diff --git a/kplato/kptmainprojectpanel.h b/kplato/kptmainprojectpanel.h new file mode 100644 index 00000000..dd7a5821 --- /dev/null +++ b/kplato/kptmainprojectpanel.h @@ -0,0 +1,74 @@ +/* This file is part of the KDE project + Copyright (C) 2004, 2005 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License + + 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 "kptmainprojectpanelbase.h" + +#include <qdatetime.h> + +class QWidget; + +class KCommand; + +namespace KPlato +{ + +class Project; +class Part; + +class MainProjectPanelImpl : public MainProjectPanelBase { + Q_OBJECT +public: + MainProjectPanelImpl(QWidget *parent=0, const char *name=0); + + virtual QDateTime startDateTime(); + virtual QDateTime endDateTime(); + +public slots: + virtual void slotCheckAllFieldsFilled(); + virtual void slotChooseLeader(); + virtual void slotStartDateClicked(); + virtual void slotEndDateClicked(); + virtual void enableDateTime(); + virtual void slotBaseline(); + +signals: + void obligatedFieldsFilled(bool); + void changed(); + +}; + +class MainProjectPanel : public MainProjectPanelImpl { + Q_OBJECT +public: + MainProjectPanel(Project &project, QWidget *parent=0, const char *name=0); + + KCommand *buildCommand(Part *part); + + bool ok(); + +private: + Project &project; +}; + +} //KPlato namespace + +#endif // MAINPROJECTPANEL_H diff --git a/kplato/kptmainprojectpanelbase.ui b/kplato/kptmainprojectpanelbase.ui new file mode 100644 index 00000000..54155a97 --- /dev/null +++ b/kplato/kptmainprojectpanelbase.ui @@ -0,0 +1,287 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>KPlato::MainProjectPanelBase</class> +<author>Dag Andersen <danders@get2net.dk></author> +<widget class="QWidget"> + <property name="name"> + <cstring>MainProjectPanelBase</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>550</width> + <height>335</height> + </rect> + </property> + <property name="caption"> + <string>ProjectPanelBase</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout14</cstring> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KLineEdit" row="2" column="1" rowspan="1" colspan="4"> + <property name="name"> + <cstring>leaderfield</cstring> + </property> + <property name="whatsThis" stdset="0"> + <string>The project leader.</string> + </property> + </widget> + <widget class="QLabel" row="2" column="0"> + <property name="name"> + <cstring>textLabel2</cstring> + </property> + <property name="text"> + <string>&Leader:</string> + </property> + <property name="alignment"> + <set>AlignVCenter</set> + </property> + <property name="buddy" stdset="0"> + <cstring>leaderfield</cstring> + </property> + <property name="whatsThis" stdset="0"> + <string>The project leader.</string> + </property> + </widget> + <widget class="KLineEdit" row="1" column="1" rowspan="1" colspan="5"> + <property name="name"> + <cstring>namefield</cstring> + </property> + <property name="whatsThis" stdset="0"> + <string>The project name.</string> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>N&ame:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>namefield</cstring> + </property> + <property name="whatsThis" stdset="0"> + <string>The project name.</string> + </property> + </widget> + <spacer row="0" column="2"> + <property name="name"> + <cstring>spacer2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>270</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QLabel" row="0" column="1"> + <property name="name"> + <cstring>wbs</cstring> + </property> + <property name="minimumSize"> + <size> + <width>20</width> + <height>0</height> + </size> + </property> + <property name="frameShape"> + <enum>NoFrame</enum> + </property> + <property name="frameShadow"> + <enum>Plain</enum> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="QLabel" row="0" column="3"> + <property name="name"> + <cstring>textLabel1_2</cstring> + </property> + <property name="text"> + <string><p align="right"></p>ID:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>idfield</cstring> + </property> + <property name="whatsThis" stdset="0"> + <string>The unique project identification</string> + </property> + </widget> + <widget class="QPushButton" row="2" column="5"> + <property name="name"> + <cstring>chooseLeader</cstring> + </property> + <property name="text"> + <string>&Choose...</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Choose a project leader from your address book.</string> + </property> + </widget> + <widget class="KLineEdit" row="0" column="4" rowspan="1" colspan="2"> + <property name="name"> + <cstring>idfield</cstring> + </property> + <property name="whatsThis" stdset="0"> + <string>The unique project identification</string> + </property> + </widget> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel1_3</cstring> + </property> + <property name="text"> + <string>WBS:</string> + </property> + </widget> + </grid> + </widget> + <widget class="QButtonGroup"> + <property name="name"> + <cstring>schedulingGroup</cstring> + </property> + <property name="lineWidth"> + <number>1</number> + </property> + <property name="title"> + <string>Scheduling</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Here you define when the project shall start or end. + +If start time is defined, the project is scheduled forward from this time. When the project has been calculated, end time shows when the project is planned to end. +If end time is defined, the project is scheduled backwards from this time. When the project has been calculated, start time shows when the project must start in order to finish in time.</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KDateWidget" row="0" column="1"> + <property name="name"> + <cstring>startDate</cstring> + </property> + <property name="toolTip" stdset="0"> + <string>Define when the project shall start.</string> + </property> + </widget> + <widget class="QTimeEdit" row="0" column="2"> + <property name="name"> + <cstring>startTime</cstring> + </property> + </widget> + <widget class="QTimeEdit" row="1" column="2"> + <property name="name"> + <cstring>endTime</cstring> + </property> + </widget> + <widget class="KDateWidget" row="1" column="1"> + <property name="name"> + <cstring>endDate</cstring> + </property> + <property name="toolTip" stdset="0"> + <string>Define when the project shall end.</string> + </property> + </widget> + <widget class="QRadioButton" row="1" column="0"> + <property name="name"> + <cstring>bEndDate</cstring> + </property> + <property name="text"> + <string>End date:</string> + </property> + <property name="buttonGroupId"> + <number>1</number> + </property> + <property name="toolTip" stdset="0"> + <string>Select this to schedule the project backward from end time.</string> + </property> + </widget> + <widget class="QRadioButton" row="0" column="0"> + <property name="name"> + <cstring>bStartDate</cstring> + </property> + <property name="text"> + <string>Start date:</string> + </property> + <property name="buttonGroupId"> + <number>0</number> + </property> + <property name="toolTip" stdset="0"> + <string>Select this to schedule the project forward from start time.</string> + </property> + </widget> + </grid> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel5</cstring> + </property> + <property name="text"> + <string>&Project notes and summary:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>descriptionfield</cstring> + </property> + <property name="whatsThis" stdset="0"> + <string>Various notes associated with the project or a project summary. + +Here you can enter any additional text you want to be stored with the project. This can for example be a short summary of the project or various notes.</string> + </property> + </widget> + <widget class="KTextEdit"> + <property name="name"> + <cstring>descriptionfield</cstring> + </property> + <property name="whatsThis" stdset="0"> + <string>Various notes associated with the project or a project summary. + +Here you can enter any additional text you want to be stored with the project. This can for example be a short summary of the project or various notes.</string> + </property> + </widget> + </vbox> +</widget> +<customwidgets> +</customwidgets> +<tabstops> + <tabstop>namefield</tabstop> + <tabstop>leaderfield</tabstop> + <tabstop>chooseLeader</tabstop> + <tabstop>bStartDate</tabstop> + <tabstop>startTime</tabstop> + <tabstop>bEndDate</tabstop> + <tabstop>endTime</tabstop> + <tabstop>descriptionfield</tabstop> + <tabstop>idfield</tabstop> +</tabstops> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>klineedit.h</includehint> + <includehint>klineedit.h</includehint> + <includehint>klineedit.h</includehint> + <includehint>kdatewidget.h</includehint> + <includehint>kdatewidget.h</includehint> + <includehint>ktextedit.h</includehint> +</includehints> +</UI> diff --git a/kplato/kptmap.h b/kplato/kptmap.h new file mode 100644 index 00000000..9edae25f --- /dev/null +++ b/kplato/kptmap.h @@ -0,0 +1,162 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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 KPTMAP_H +#define KPTMAP_H + + +#include <qmap.h> +#include <qdatetime.h> +#include <qstring.h> +#include <qpair.h> +#include <qvaluelist.h> + +#include <kdebug.h> + +namespace KPlato +{ + +namespace Map { +enum State { None=0, NonWorking=1, Working=2 }; +} // Map namespace + +typedef QMap<QString, int> DateMapType; +class DateMap : public DateMapType +{ +public: + DateMap() {} + virtual ~DateMap() {} + + virtual bool contains(QDate date) const { return DateMapType::contains(date.toString(Qt::ISODate)); } + + void insert(QString date, int state=Map::NonWorking) { + //kdDebug()<<k_funcinfo<<date<<"="<<state<<endl; + if (state == Map::None) + DateMapType::remove(date); + else + DateMapType::insert(date, state); + } + void insert(QDate date, int state=Map::NonWorking) { insert(date.toString(Qt::ISODate), state); } + + void remove(QDate date) { + //kdDebug()<<k_funcinfo<<date.toString(Qt::ISODate)<<endl; + DateMapType::remove(date.toString(Qt::ISODate)); + } + + int state(QString date) { + DateMapType::iterator it = find(date); + if (it == end()) return 0; + else return it.data(); + } + int state(QDate date) { return state(date.toString(Qt::ISODate)); } + + bool operator==(const DateMap &m) const { + return keys() == m.keys() && values() == m.values(); + } + bool operator!=(const DateMap &m) const { + return keys() != m.keys() || values() != m.values(); + } + + // boolean use + void toggle(QString date, int state=Map::NonWorking) { + //kdDebug()<<k_funcinfo<<date<<"="<<state<<endl; + if (DateMapType::contains(date)) + DateMapType::remove(date); + else + DateMapType::insert(date, state); + } + void toggle(QDate date, int state=Map::NonWorking) { return toggle(date.toString(Qt::ISODate)); } + void toggleClear(QString date, int state=Map::NonWorking) { + //kdDebug()<<k_funcinfo<<date<<"="<<state<<endl; + bool s = DateMapType::contains(date); + clear(); + if (!s) insert(date, state); + } + void toggleClear(QDate date, int state=Map::NonWorking) { toggleClear(date.toString(Qt::ISODate)); } +}; + +typedef QMap<int, int> IntMapType; +class IntMap : public IntMapType +{ +public: + IntMap() {} + virtual ~IntMap() {} + + void insert(int key, int state=Map::NonWorking) { + if (state == Map::None) + IntMapType::remove(key); + else + IntMapType::insert(key, state); } + + virtual int state(int key) { + IntMapType::iterator it = IntMapType::find(key); + if (it == IntMapType::end()) return 0; + else return it.data(); + } + + bool operator==(const IntMap &m) const { + return keys() == m.keys() && values() == m.values(); + } + bool operator!=(const IntMap &m) const { + return keys() != m.keys() || values() != m.values(); + } + + // boolean use + void toggle(int key, int state=Map::NonWorking) { IntMapType::contains(key) ? remove(key) : insert(key, state); } + void toggleClear(int key, int state=Map::NonWorking) { + bool s =contains(key); + clear(); + if (!s) insert(key, state); + } +}; + +class WeekMap : public IntMap +{ +public: + bool contains(int week, int year) { return IntMap::contains(week*10000 + year); } + bool contains(QPair<int,int> week) { return contains(week.first, week.second); } + + void insert(int week, int year, int state=Map::NonWorking) { + if (week < 1 || week > 53) { kdError()<<k_funcinfo<<"Illegal week number: "<<week<<endl; return; } + IntMap::insert(week*10000 + year, state); + } + void insert(QPair<int,int> week, int state=Map::NonWorking) { insert(week.first, week.second, state); } + + void insert(WeekMap::iterator it, int state) { insert(week(it.key()), state); } + + void remove(QPair<int,int> week) { IntMap::remove(week.first*10000 + week.second); } + + static QPair<int, int> week(int key) { return QPair<int, int>(key/10000, key%10000); } + + int state(QPair<int, int> week) { return IntMap::state(week.first*10000 + week.second); } + int state(int week, int year) { return state(QPair<int, int>(week, year)); } + + void toggle(QPair<int,int> week, int state=Map::NonWorking) { + if (week.first < 1 || week.first > 53) { kdError()<<k_funcinfo<<"Illegal week number: "<<week.first<<endl; return; } + IntMap::toggle(week.first*10000 + week.second, state); + } + void toggleClear(QPair<int,int> week, int state=Map::NonWorking) { + if (week.first < 1 || week.first > 53) { kdError()<<k_funcinfo<<"Illegal week number: "<<week.first<<endl; return; } + IntMap::toggleClear(week.first*10000 + week.second, state); + } +}; + +} //KPlato namespace + +#endif diff --git a/kplato/kptmilestoneprogressdialog.cc b/kplato/kptmilestoneprogressdialog.cc new file mode 100644 index 00000000..75f91f49 --- /dev/null +++ b/kplato/kptmilestoneprogressdialog.cc @@ -0,0 +1,71 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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. +*/ + +#include "kptmilestoneprogressdialog.h" +#include "kptmilestoneprogresspanel.h" + +#include <kcommand.h> +#include <klocale.h> + +#include <kdebug.h> + +namespace KPlato +{ + +MilestoneProgressDialog::MilestoneProgressDialog(Task &task, QWidget *p) + : KDialogBase(Swallow, i18n("Milestone Progress"), Ok|Cancel, Ok, p, "Milestone Progress Dialog", true, true) +{ + m_panel = new MilestoneProgressPanel(task, this); + + setMainWidget(m_panel); + + enableButtonOK(false); + + connect(m_panel, SIGNAL(changed()), SLOT(slotChanged())); +} + +void MilestoneProgressDialog::slotChanged() { + enableButtonOK(true); +} + +KCommand *MilestoneProgressDialog::buildCommand(Part *part) { + KMacroCommand *m = new KMacroCommand(i18n("Modify Milestone Progress")); + bool modified = false; + KCommand *cmd = m_panel->buildCommand(part); + if (cmd) { + m->addCommand(cmd); + modified = true; + } + if (!modified) { + delete m; + return 0; + } + return m; +} + +void MilestoneProgressDialog::slotOk() { + if (!m_panel->ok()) + return; + accept(); +} + + +} //KPlato namespace + +#include "kptmilestoneprogressdialog.moc" diff --git a/kplato/kptmilestoneprogressdialog.h b/kplato/kptmilestoneprogressdialog.h new file mode 100644 index 00000000..a9b78fee --- /dev/null +++ b/kplato/kptmilestoneprogressdialog.h @@ -0,0 +1,52 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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 KPTMILESTONEPROGRESSDIALOG_H +#define KPTMILESTONEPROGRESSDIALOG_H + +#include <kdialogbase.h> + +class KCommand; + +namespace KPlato +{ + +class MilestoneProgressPanel; +class Task; +class Part; + +class MilestoneProgressDialog : public KDialogBase { + Q_OBJECT +public: + MilestoneProgressDialog(Task &task, QWidget *parent=0); + + KCommand *buildCommand(Part *part); + +protected slots: + void slotChanged(); + void slotOk(); + +private: + MilestoneProgressPanel *m_panel; + +}; + +} //KPlato namespace + +#endif // MILESTONEPROGRESSDIALOG_H diff --git a/kplato/kptmilestoneprogresspanel.cc b/kplato/kptmilestoneprogresspanel.cc new file mode 100644 index 00000000..fc32fd1e --- /dev/null +++ b/kplato/kptmilestoneprogresspanel.cc @@ -0,0 +1,103 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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. +*/ + +#include "kptmilestoneprogresspanel.h" + +#include <qcheckbox.h> + +#include <kdatetimewidget.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <kcommand.h> + +#include <kdebug.h> + +#include "kpttask.h" +#include "kptcommand.h" + +namespace KPlato +{ + +MilestoneProgressPanel::MilestoneProgressPanel(Task &task, QWidget *parent, const char *name) + : MilestoneProgressPanelImpl(parent, name), + m_task(task) +{ + kdDebug()<<k_funcinfo<<endl; + m_progress = task.progress(); + finished->setChecked(m_progress.finished); + finishTime->setDateTime(m_progress.finishTime); + + enableWidgets(); + finished->setFocus(); +} + + +bool MilestoneProgressPanel::ok() { + m_progress.started = finished->isChecked(); + m_progress.finished = finished->isChecked(); + m_progress.startTime = finishTime->dateTime(); + m_progress.finishTime = finishTime->dateTime(); + m_progress.percentFinished = m_progress.finished ? 100 : 0; + return true; +} + +KCommand *MilestoneProgressPanel::buildCommand(Part *part) { + KCommand *cmd = 0; + QString c = i18n("Modify progress"); + if (m_task.progress() != m_progress) { + cmd = new TaskModifyProgressCmd(part, m_task, m_progress, c); + } + return cmd; +} + +//------------------------------------- + +MilestoneProgressPanelImpl::MilestoneProgressPanelImpl(QWidget *parent, const char *name, WFlags f) + : MilestoneProgressPanelBase(parent, name, f) { + + connect(finished, SIGNAL(toggled(bool)), SLOT(slotFinishedChanged(bool))); + connect(finished, SIGNAL(toggled(bool)), SLOT(slotChanged())); + + connect(finishTime, SIGNAL(valueChanged(const QDateTime &)), SLOT(slotChanged())); + +} + +void MilestoneProgressPanelImpl::slotChanged() { + emit changed(); +} + +void MilestoneProgressPanelImpl::slotFinishedChanged(bool state) { + if (state) { + if (!finishTime->dateTime().isValid()) { + finishTime->setDateTime(QDateTime::currentDateTime()); + } + } + enableWidgets(); +} + + +void MilestoneProgressPanelImpl::enableWidgets() { + finished->setEnabled(true); + finishTime->setEnabled(finished->isChecked()); +} + + +} //KPlato namespace + +#include "kptmilestoneprogresspanel.moc" diff --git a/kplato/kptmilestoneprogresspanel.h b/kplato/kptmilestoneprogresspanel.h new file mode 100644 index 00000000..9ad8a116 --- /dev/null +++ b/kplato/kptmilestoneprogresspanel.h @@ -0,0 +1,65 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License + + 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 KPTMILESTONEPROGRESSPANEL_H +#define KPTMILESTONEPROGRESSPANEL_H + +#include "kptmilestoneprogresspanelbase.h" +#include "kpttask.h" + +class KCommand; + +namespace KPlato +{ + +class Part; +class StandardWorktime; + +class MilestoneProgressPanelImpl : public MilestoneProgressPanelBase { + Q_OBJECT +public: + MilestoneProgressPanelImpl(QWidget *parent=0, const char *name=0, WFlags f=0); + + void enableWidgets(); + +signals: + void changed(); + +public slots: + void slotChanged(); + void slotFinishedChanged(bool state); +}; + +class MilestoneProgressPanel : public MilestoneProgressPanelImpl { + Q_OBJECT +public: + MilestoneProgressPanel(Task &task, QWidget *parent=0, const char *name=0); + + KCommand *buildCommand(Part *part); + + bool ok(); + +private: + Task &m_task; + struct Task::Progress m_progress; +}; + +} //KPlato namespace + +#endif // MILESTONEPROGRESSPANEL_H diff --git a/kplato/kptmilestoneprogresspanelbase.ui b/kplato/kptmilestoneprogresspanelbase.ui new file mode 100644 index 00000000..9455fce6 --- /dev/null +++ b/kplato/kptmilestoneprogresspanelbase.ui @@ -0,0 +1,63 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>KPlato::MilestoneProgressPanelBase</class> +<author>Dag Andersen <danders@get2net.dk></author> +<widget class="QWidget"> + <property name="name"> + <cstring>MilestoneProgressPanelBase</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>343</width> + <height>120</height> + </rect> + </property> + <property name="caption"> + <string>MilestoneProgressPanelBase</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QCheckBox"> + <property name="name"> + <cstring>finished</cstring> + </property> + <property name="text"> + <string>Finished</string> + </property> + </widget> + <widget class="KDateTimeWidget"> + <property name="name"> + <cstring>finishTime</cstring> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer8</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </vbox> +</widget> +<customwidgets> +</customwidgets> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>kdatetimewidget.h</includehint> + <includehint>kdatewidget.h</includehint> + <includehint>ktimewidget.h</includehint> +</includehints> +</UI> diff --git a/kplato/kptnode.cc b/kplato/kptnode.cc new file mode 100644 index 00000000..cab767f8 --- /dev/null +++ b/kplato/kptnode.cc @@ -0,0 +1,1011 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Thomas zander <zander@kde.org> + Copyright (C) 2004, 2005 Dag Andersen <danders@get2net.dk> + + 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. +*/ +#include "kptnode.h" + +#include "kptappointment.h" +#include "kptaccount.h" +#include "kptwbsdefinition.h" +#include "kptresource.h" +#include "kptschedule.h" + +#include <qptrlist.h> +#include <qdom.h> + +#include <klocale.h> +#include <kdebug.h> + +namespace KPlato +{ + +Node::Node(Node *parent) : m_nodes(), m_dependChildNodes(), m_dependParentNodes() { + //kdDebug()<<k_funcinfo<<"("<<this<<")"<<endl; + m_parent = parent; + init(); + m_id = QString(); // Not mapped +} + +Node::Node(Node &node, Node *parent) + : m_nodes(), + m_dependChildNodes(), + m_dependParentNodes() { + //kdDebug()<<k_funcinfo<<"("<<this<<")"<<endl; + m_parent = parent; + init(); + m_name = node.name(); + m_leader = node.leader(); + m_description = node.description(); + m_constraint = (ConstraintType) node.constraint(); + m_constraintStartTime = node.constraintStartTime(); + m_constraintEndTime = node.constraintEndTime(); + + m_dateOnlyStartDate = node.startDate(); + m_dateOnlyEndDate = node.endDate(); + + m_runningAccount = node.runningAccount(); + m_startupAccount = node.startupAccount(); + m_shutdownAccount = node.shutdownAccount(); + + m_startupCost = node.startupCost(); + m_shutdownCost = node.shutdownCost(); + + m_schedules.setAutoDelete(node.m_schedules.autoDelete()); +} + +Node::~Node() { + if (findNode() == this) { + removeId(); // only remove myself (I may be just a working copy) + } + Relation *rel = 0; + while ((rel = m_dependParentNodes.getFirst())) { + delete rel; + } + while ((rel = m_dependChildNodes.getFirst())) { + delete rel; + } + if (m_runningAccount) + m_runningAccount->removeRunning(*this); + if (m_startupAccount) + m_startupAccount->removeStartup(*this); + if (m_shutdownAccount) + m_shutdownAccount->removeShutdown(*this); +} + +void Node::init() { + m_currentSchedule = 0; + m_nodes.setAutoDelete(true); + m_name=""; + m_constraint = Node::ASAP; + m_effort = 0; + m_visitedForward = false; + m_visitedBackward = false; + + m_dateOnlyStartDate = m_dateOnlyEndDate = QDate::currentDate(); + m_dateOnlyDuration.addDays(1); + + m_runningAccount = 0; + m_startupAccount = 0; + m_shutdownAccount = 0; + m_startupCost = 0.0; + m_shutdownCost = 0.0; +} + +Node *Node::projectNode() { + if ((type() == Type_Project) || (type() == Type_Subproject)) { + return this; + } + if (m_parent) + return m_parent->projectNode(); + + kdError()<<k_funcinfo<<"Ooops, no parent and no project found"<<endl; + return 0; +} + +void Node::delChildNode( Node *node, bool remove) { + //kdDebug()<<k_funcinfo<<"find="<<m_nodes.findRef(node)<<endl; + if ( m_nodes.findRef(node) != -1 ) { + if(remove) + m_nodes.remove(); + else + m_nodes.take(); + } + node->setParent(0); +} + +void Node::delChildNode( int number, bool remove) { + Node *n = m_nodes.at(number); + //kdDebug()<<k_funcinfo<<(n?n->id():"null")<<" : "<<(n?n->name():"")<<endl; + if(remove) + m_nodes.remove(number); + else + m_nodes.take(number); + + if (n) { + n->setParent(0); + } +} + +void Node::insertChildNode( unsigned int index, Node *node) { + //kdDebug()<<k_funcinfo<<"insert id="<<node->id()<<": "<<node->name()<<endl; + m_nodes.insert(index,node); + node->setParent(this); +} + +void Node::addChildNode( Node *node, Node *after) { + //kdDebug()<<k_funcinfo<<endl; + int index = m_nodes.findRef(after); + if (index == -1) { + //kdDebug()<<k_funcinfo<<"append id="<<node->id()<<": "<<node->name()<<endl; + m_nodes.append(node); + node->setParent(this); + return; + } + //kdDebug()<<k_funcinfo<<"insert id="<<node->id()<<": "<<node->name()<<endl; + m_nodes.insert(index+1, node); + node->setParent(this); +} + +int Node::findChildNode( Node* node ) +{ + return m_nodes.findRef( node ); +} + + +const Node* Node::getChildNode(int number) const { + // Work around missing const at() method in QPtrList + const QPtrList<Node> &nodes = m_nodes; + return (const_cast<QPtrList<Node> &>(nodes)).at(number); +} + +Duration *Node::getDelay() { + /* TODO + Calculate the delay of this node. Use the calculated startTime and the setted startTime. + */ + return 0L; +} + +void Node::addDependChildNode( Node *node, Relation::Type p) { + addDependChildNode(node,p,Duration()); +} + +void Node::addDependChildNode( Node *node, Relation::Type p, Duration lag) { + Relation *relation = new Relation(this, node, p, lag); + if (node->addDependParentNode(relation)) + m_dependChildNodes.append(relation); + else + delete relation; +} + +void Node::insertDependChildNode( unsigned int index, Node *node, Relation::Type p) { + Relation *relation = new Relation(this, node, p, Duration()); + if (node->addDependParentNode(relation)) + m_dependChildNodes.insert(index, relation); + else + delete relation; +} + +bool Node::addDependChildNode( Relation *relation) { + if(m_dependChildNodes.findRef(relation) != -1) + return false; + m_dependChildNodes.append(relation); + return true; +} + +// These delDepend... methods look suspicious to me, can someone review? +void Node::delDependChildNode( Node *node, bool remove) { + if ( m_nodes.findRef(node) != -1 ) { + if(remove) + m_dependChildNodes.remove(); + else + m_dependChildNodes.take(); + } +} + +void Node::delDependChildNode( Relation *rel, bool remove) { + if ( m_dependChildNodes.findRef(rel) != -1 ) { + if(remove) + m_dependChildNodes.remove(); + else + m_dependChildNodes.take(); + } +} + +void Node::delDependChildNode( int number, bool remove) { + if(remove) + m_dependChildNodes.remove(number); + else + m_dependChildNodes.take(number); +} + +void Node::takeDependChildNode(Relation *rel) { + if (m_dependChildNodes.findRef(rel) != -1) { + m_dependChildNodes.take(); + } +} + +void Node::addDependParentNode( Node *node, Relation::Type p) { + addDependParentNode(node,p,Duration()); +} + +void Node::addDependParentNode( Node *node, Relation::Type p, Duration lag) { + Relation *relation = new Relation(node, this, p, lag); + if (node->addDependChildNode(relation)) + m_dependParentNodes.append(relation); + else + delete relation; +} + +void Node::insertDependParentNode( unsigned int index, Node *node, Relation::Type p) { + Relation *relation = new Relation(this, node, p, Duration()); + if (node->addDependChildNode(relation)) + m_dependParentNodes.insert(index,relation); + else + delete relation; +} + +bool Node::addDependParentNode( Relation *relation) { + if(m_dependParentNodes.findRef(relation) != -1) + return false; + m_dependParentNodes.append(relation); + return true; +} + +// These delDepend... methods look suspicious to me, can someone review? +void Node::delDependParentNode( Node *node, bool remove) { + if ( m_nodes.findRef(node) != -1 ) { + if(remove) + m_dependParentNodes.remove(); + else + m_dependParentNodes.take(); + } +} + +void Node::delDependParentNode( Relation *rel, bool remove) { + if ( m_dependParentNodes.findRef(rel) != -1 ) { + if(remove) + m_dependParentNodes.remove(); + else + m_dependParentNodes.take(); + } +} + +void Node::delDependParentNode( int number, bool remove) { + if(remove) + m_dependParentNodes.remove(number); + else + m_dependParentNodes.take(number); +} + +void Node::takeDependParentNode(Relation *rel) { + if (m_dependParentNodes.findRef(rel) != -1) { + rel = m_dependParentNodes.take(); + } +} + +bool Node::isParentOf(Node *node) { + if (m_nodes.findRef(node) != -1) + return true; + + QPtrListIterator<Node> nit(childNodeIterator()); + for ( ; nit.current(); ++nit ) { + if (nit.current()->isParentOf(node)) + return true; + } + return false; +} + +Relation *Node::findParentRelation(Node *node) { + for (int i=0; i<numDependParentNodes(); i++) { + Relation *rel = getDependParentNode(i); + if (rel->parent() == node) + return rel; + } + return (Relation *)0; +} + +Relation *Node::findChildRelation(Node *node) { + for (int i=0; i<numDependChildNodes(); i++) { + Relation *rel = getDependChildNode(i); + if (rel->child() == node) + return rel; + } + return (Relation *)0; +} + +Relation *Node::findRelation(Node *node) { + Relation *rel = findParentRelation(node); + if (!rel) + rel = findChildRelation(node); + return rel; +} + +bool Node::isDependChildOf(Node *node) { + //kdDebug()<<k_funcinfo<<" '"<<m_name<<"' checking against '"<<node->name()<<"'"<<endl; + for (int i=0; i<numDependParentNodes(); i++) { + Relation *rel = getDependParentNode(i); + if (rel->parent() == node) + return true; + if (rel->parent()->isDependChildOf(node)) + return true; + } + return false; +} + +Duration Node::duration(const DateTime &time, int use, bool backward) { + //kdDebug()<<k_funcinfo<<endl; + // TODO: handle risc + if (!time.isValid()) { + kdError()<<k_funcinfo<<"Time is invalid"<<endl; + return Duration::zeroDuration; + } + if (m_effort == 0) { + kdError()<<k_funcinfo<<"m_effort == 0"<<endl; + return Duration::zeroDuration; + } + if (m_currentSchedule == 0) { + return Duration::zeroDuration; + kdError()<<k_funcinfo<<"No current schedule"<<endl; + } + kdDebug()<<k_funcinfo<<m_name<<": Use="<<use<<endl; + return calcDuration(time, m_effort->effort(use), backward); +} + +void Node::makeAppointments() { + QPtrListIterator<Node> nit(m_nodes); + for ( ; nit.current(); ++nit ) { + nit.current()->makeAppointments(); + } +} + +void Node::calcResourceOverbooked() { + QPtrListIterator<Node> nit(m_nodes); + for ( ; nit.current(); ++nit ) { + nit.current()->calcResourceOverbooked(); + } +} + +QStringList Node::overbookedResources() const { + return m_currentSchedule ? m_currentSchedule->overbookedResources() : QStringList(); +} + +void Node::saveRelations(QDomElement &element) const { + QPtrListIterator<Relation> it(m_dependChildNodes); + for (; it.current(); ++it) { + it.current()->save(element); + } + QPtrListIterator<Node> nodes(m_nodes); + for ( ; nodes.current(); ++nodes ) { + nodes.current()->saveRelations(element); + } +} + +void Node::setConstraint(QString &type) { + // Do not i18n these, they are used in load() + if (type == "ASAP") + setConstraint(ASAP); + else if (type == "ALAP") + setConstraint(ALAP); + else if (type == "StartNotEarlier") + setConstraint(StartNotEarlier); + else if (type == "FinishNotLater") + setConstraint(FinishNotLater); + else if (type == "MustStartOn") + setConstraint(MustStartOn); + else if (type == "MustFinishOn") + setConstraint(MustFinishOn); + else if (type == "FixedInterval") + setConstraint(FixedInterval); + else + setConstraint(ASAP); // default +} + +QString Node::constraintToString() const { + // Do not i18n these, they are used in save() + if (m_constraint == ASAP) + return QString("ASAP"); + else if (m_constraint == ALAP) + return QString("ALAP"); + else if (m_constraint == StartNotEarlier) + return QString("StartNotEarlier"); + else if (m_constraint == FinishNotLater) + return QString("FinishNotLater"); + else if (m_constraint == MustStartOn) + return QString("MustStartOn"); + else if (m_constraint == MustFinishOn) + return QString("MustFinishOn"); + else if (m_constraint == FixedInterval) + return QString("FixedInterval"); + + return QString(); +} + +void Node::propagateEarliestStart(DateTime &time) { + if (m_currentSchedule == 0) + return; + m_currentSchedule->earliestStart = time; + //kdDebug()<<k_funcinfo<<m_name<<": "<<m_currentSchedule->earliestStart.toString()<<endl; + QPtrListIterator<Node> it = m_nodes; + for (; it.current(); ++it) { + it.current()->propagateEarliestStart(time); + } +} + +void Node::propagateLatestFinish(DateTime &time) { + if (m_currentSchedule == 0) + return; + m_currentSchedule->latestFinish = time; + //kdDebug()<<k_funcinfo<<m_name<<": "<<m_currentSchedule->latestFinish<<endl; + QPtrListIterator<Node> it = m_nodes; + for (; it.current(); ++it) { + it.current()->propagateLatestFinish(time); + } +} + +void Node::moveEarliestStart(DateTime &time) { + if (m_currentSchedule == 0) + return; + if (m_currentSchedule->earliestStart < time) + m_currentSchedule->earliestStart = time; + QPtrListIterator<Node> it = m_nodes; + for (; it.current(); ++it) { + it.current()->moveEarliestStart(time); + } +} + +void Node::moveLatestFinish(DateTime &time) { + if (m_currentSchedule == 0) + return; + if (m_currentSchedule->latestFinish > time) + m_currentSchedule->latestFinish = time; + QPtrListIterator<Node> it = m_nodes; + for (; it.current(); ++it) { + it.current()->moveLatestFinish(time); + } +} + +void Node::initiateCalculation(Schedule &sch) { + QPtrListIterator<Node> it = m_nodes; + for (; it.current(); ++it) { + it.current()->initiateCalculation(sch); + } +} + +void Node::resetVisited() { + m_visitedForward = false; + m_visitedBackward = false; + QPtrListIterator<Node> it = m_nodes; + for (; it.current(); ++it) { + it.current()->resetVisited(); + } +} + +Node *Node::siblingBefore() { + //kdDebug()<<k_funcinfo<<endl; + if (getParent()) + return getParent()->childBefore(this); + return 0; +} + +Node *Node::childBefore(Node *node) { + //kdDebug()<<k_funcinfo<<endl; + int index = m_nodes.findRef(node); + if (index > 0){ + return m_nodes.at(index-1); + } + return 0; +} + +Node *Node::siblingAfter() { + //kdDebug()<<k_funcinfo<<endl; + if (getParent()) + return getParent()->childAfter(this); + return 0; +} + +Node *Node::childAfter(Node *node) +{ + //kdDebug()<<k_funcinfo<<endl; + uint index = m_nodes.findRef(node); + if (index < m_nodes.count()-1) { + return m_nodes.at(index+1); } + return 0; +} + +bool Node::moveChildUp(Node* node) +{ + if (findChildNode(node) == -1) + return false; // not my node! + Node *sib = node->siblingBefore(); + if (!sib) + return false; + sib = sib->siblingBefore(); + delChildNode(node, false); + if (sib) { + addChildNode(node, sib); + } else { + insertChildNode(0, node); + } + return true; +} + +bool Node::moveChildDown(Node* node) +{ + if (findChildNode(node) == -1) + return false; // not my node! + Node *sib = node->siblingAfter(); + if (!sib) + return false; + delChildNode(node, false); + addChildNode(node, sib); + return true; +} + +bool Node::legalToLink(Node *node) { + Node *p = projectNode(); + if (p) + return p->legalToLink(this, node); + return false; +} + +bool Node::isEndNode() const { + return m_dependChildNodes.isEmpty(); +} +bool Node::isStartNode() const { + return m_dependParentNodes.isEmpty(); +} + +bool Node::setId(QString id) { + //kdDebug()<<k_funcinfo<<id<<endl; + if (id.isEmpty()) { + kdError()<<k_funcinfo<<"id is empty"<<endl; + m_id = id; + return false; + } + if (!m_id.isEmpty()) { + Node *n = findNode(); + if (n == this) { + //kdDebug()<<k_funcinfo<<"My id found, remove it"<<endl; + removeId(); + } else if (n) { + //Hmmm, shouldn't happen + kdError()<<k_funcinfo<<"My id '"<<m_id<<"' already used for different node: "<<n->name()<<endl; + } + } + if (findNode(id)) { + kdError()<<k_funcinfo<<"id '"<<id<<"' is already used for different node: "<<findNode(id)->name()<<endl; + m_id = QString(); // hmmm + return false; + } + m_id = id; + insertId(id); + //kdDebug()<<k_funcinfo<<m_name<<": inserted id="<<id<<endl; + return true; +} + +void Node::setStartTime(DateTime startTime) { + if (m_currentSchedule) + m_currentSchedule->startTime = startTime; + m_dateOnlyStartDate = startTime.date(); +} + +void Node::setEndTime(DateTime endTime) { + if (m_currentSchedule) + m_currentSchedule->endTime = endTime; + + m_dateOnlyEndDate = endTime.date(); + if (endTime.time().isNull() && m_dateOnlyEndDate > m_dateOnlyStartDate) + m_dateOnlyEndDate = m_dateOnlyEndDate.addDays(-1); +} + +void Node::saveAppointments(QDomElement &element, long id) const { + //kdDebug()<<k_funcinfo<<m_name<<" id="<<id<<endl; + QPtrListIterator<Node> it(m_nodes); + for (; it.current(); ++it ) { + it.current()->saveAppointments(element, id); + } +} + +QPtrList<Appointment> Node::appointments() { + QPtrList<Appointment> lst; + if (m_currentSchedule) + lst = m_currentSchedule->appointments(); + return lst; +} + +// Appointment *Node::findAppointment(Resource *resource) { +// if (m_currentSchedule) +// return m_currentSchedule->findAppointment(resource); +// return 0; +// } +bool Node::addAppointment(Appointment *appointment) { + if (m_currentSchedule) + return m_currentSchedule->add(appointment); + return false; +} + +bool Node::addAppointment(Appointment *appointment, Schedule &main) { + //kdDebug()<<k_funcinfo<<this<<endl; + Schedule *s = findSchedule(main.id()); + if (s == 0) { + s = createSchedule(&main); + } + appointment->setNode(s); + return s->add(appointment); +} + +void Node::addAppointment(ResourceSchedule *resource, DateTime &start, DateTime &end, double load) { + Schedule *node = findSchedule(resource->id()); + if (node == 0) { + node = createSchedule(resource->parent()); + } + node->addAppointment(resource, start, end, load); +} + +void Node::takeSchedule(const Schedule *schedule) { + if (schedule == 0) + return; + if (m_currentSchedule == schedule) + m_currentSchedule = 0; + m_schedules.take(schedule->id()); +} + +void Node::addSchedule(Schedule *schedule) { + if (schedule == 0) + return; + m_schedules.replace(schedule->id(), schedule); +} + +Schedule *Node::createSchedule(QString name, Schedule::Type type, long id) { + //kdDebug()<<k_funcinfo<<name<<" type="<<type<<" id="<<(int)id<<endl; + NodeSchedule *sch = new NodeSchedule(this, name, type, id); + addSchedule(sch); + return sch; +} + +Schedule *Node::createSchedule(Schedule *parent) { + //kdDebug()<<k_funcinfo<<name<<" type="<<type<<" id="<<(int)id<<endl; + NodeSchedule *sch = new NodeSchedule(parent, this); + addSchedule(sch); + return sch; +} + +Schedule *Node::findSchedule(const QString name, const Schedule::Type type) const { + QIntDictIterator<Schedule> it = m_schedules; + for (; it.current(); ++it) { + if (!it.current()->isDeleted() && + it.current()->name() == name && it.current()->type() == type) + return it.current(); + } + return 0; +} + +Schedule *Node::findSchedule(const Schedule::Type type) const { + //kdDebug()<<k_funcinfo<<m_name<<" find type="<<type<<" nr="<<m_schedules.count()<<endl; + QIntDictIterator<Schedule> it = m_schedules; + for (; it.current(); ++it) { + if (!it.current()->isDeleted() && it.current()->type() == type) { + return it.current(); + } + } + return 0; +} + +void Node::setScheduleDeleted(long id, bool on) { + Schedule *ns = findSchedule(id); + if (ns == 0) { + kdError()<<k_funcinfo<<m_name<<" Could not find schedule with id="<<id<<endl; + } else { + ns->setDeleted(on); + } +} + +void Node::setParentSchedule(Schedule *sch) { + Schedule *s = findSchedule(sch->id()); + if (s) { + s->setParent(sch); + } + QPtrListIterator<Node> it = m_nodes; + for (; it.current(); ++it) { + it.current()->setParentSchedule(sch); + } +} + +bool Node::calcCriticalPath(bool fromEnd) { + if (m_currentSchedule == 0) + return false; + //kdDebug()<<k_funcinfo<<m_name<<endl; + if (!isCritical()) { + return false; + } + if (!fromEnd && isStartNode()) { + m_currentSchedule->inCriticalPath = true; + return true; + } + if (fromEnd && isEndNode()) { + m_currentSchedule->inCriticalPath = true; + return true; + } + QPtrListIterator<Relation> pit(m_dependParentNodes); + for (; pit.current(); ++pit) { + if (pit.current()->parent()->calcCriticalPath(fromEnd)) { + m_currentSchedule->inCriticalPath = true; + } + } + return m_currentSchedule->inCriticalPath; +} + +int Node::level() { + Node *n = getParent(); + return n ? n->level() + 1 : 0; +} + +void Node::generateWBS(int count, WBSDefinition &def, QString wbs) { + m_wbs = wbs + def.code(count, level()); + //kdDebug()<<k_funcinfo<<m_name<<" wbs: "<<m_wbs<<endl; + QString w = wbs + def.wbs(count, level()); + QPtrListIterator<Node> it = m_nodes; + for (int i=0; it.current(); ++it) { + it.current()->generateWBS(++i, def, w); + } + +} + +void Node::setCurrentSchedule(long id) { + QPtrListIterator<Node> it = m_nodes; + for (; it.current(); ++it) { + it.current()->setCurrentSchedule(id); + } + //kdDebug()<<k_funcinfo<<m_name<<" id: "<<id<<"="<<m_currentSchedule<<endl; +} +////////////////////////// Effort ///////////////////////////////// + +Effort::Effort( Duration e, Duration p, Duration o) { + m_expectedEffort = e; + m_pessimisticEffort = p; + m_optimisticEffort = o; + m_type = Type_Effort; + m_risktype = Risk_None; +} + +Effort::Effort(const Effort &effort) { + set(effort.expected(), effort.pessimistic(), effort.optimistic()); + setType(effort.type()); + setRisktype(effort.risktype()); +} + +Effort::~Effort() { +} + +const Effort Effort::zeroEffort( Duration::zeroDuration, + Duration::zeroDuration, + Duration::zeroDuration ); + +void Effort::set( Duration e, Duration p, Duration o ) { + m_expectedEffort = e; + m_pessimisticEffort = (p == Duration::zeroDuration) ? e : p; + m_optimisticEffort = (o == Duration::zeroDuration) ? e : o; + //kdDebug()<<k_funcinfo<<" Expected: "<<m_expectedEffort.toString()<<endl; +} + +void Effort::set( int e, int p, int o ) { + m_expectedEffort = Duration(e); + m_pessimisticEffort = (p < 0) ? Duration(e) : Duration(p); + m_optimisticEffort = (o < 0) ? Duration(e) : Duration(o); + //kdDebug()<<k_funcinfo<<" Expected: "<<m_expectedEffort.toString()<<endl; + //kdDebug()<<k_funcinfo<<" Optimistic: "<<m_optimisticEffort.toString()<<endl; + //kdDebug()<<k_funcinfo<<" Pessimistic: "<<m_pessimisticEffort.toString()<<endl; + + //kdDebug()<<k_funcinfo<<" Expected: "<<m_expectedEffort.duration()<<" manseconds"<<endl; +} + +//TODO (?): effort is not really a duration, should maybe not use Duration for storage +void Effort::set(unsigned days, unsigned hours, unsigned minutes) { + Duration dur(days, hours, minutes); + set(dur); + //kdDebug()<<k_funcinfo<<"effort="<<dur.toString()<<endl; +} + +void Effort::expectedEffort(unsigned *days, unsigned *hours, unsigned *minutes) { + m_expectedEffort.get(days, hours, minutes); +} + +Duration Effort::variance() const { + return (m_pessimisticEffort - m_optimisticEffort)/6; +} +Duration Effort::pertExpected() const { + if (m_risktype == Risk_Low) { + return (m_optimisticEffort + m_pessimisticEffort + (m_expectedEffort*4))/6; + } else if (m_risktype == Risk_High) { + return (m_optimisticEffort + (m_pessimisticEffort*2) + (m_expectedEffort*4))/7; + } + return m_expectedEffort; // risk==none +} +Duration Effort::pertOptimistic() const { + if (m_risktype != Risk_None) { + return pertExpected() - variance(); + } + return m_optimisticEffort; +} +Duration Effort::pertPessimistic() const { + if (m_risktype != Risk_None) { + return pertExpected() + variance(); + } + return m_pessimisticEffort; +} + +Duration Effort::effort(int use) const { + if (use == Effort::Use_Expected) { + return pertExpected(); + } else if (use == Effort::Use_Optimistic) { + return pertOptimistic(); + } else if (use == Effort::Use_Pessimistic) + return pertPessimistic(); + + return m_expectedEffort; // default +} + +bool Effort::load(QDomElement &element) { + m_expectedEffort = Duration::fromString(element.attribute("expected")); + m_optimisticEffort = Duration::fromString(element.attribute("optimistic")); + m_pessimisticEffort = Duration::fromString(element.attribute("pessimistic")); + setType(element.attribute("type", "WorkBased")); + setRisktype(element.attribute("risk")); + return true; +} + +void Effort::save(QDomElement &element) const { + QDomElement me = element.ownerDocument().createElement("effort"); + element.appendChild(me); + me.setAttribute("expected", m_expectedEffort.toString()); + me.setAttribute("optimistic", m_optimisticEffort.toString()); + me.setAttribute("pessimistic", m_pessimisticEffort.toString()); + me.setAttribute("type", typeToString()); + me.setAttribute("risk", risktypeToString()); +} + +QString Effort::typeToString() const { + if (m_type == Type_Effort) + return QString("Effort"); + if (m_type == Type_FixedDuration) + return QString("Type_FixedDuration"); + + return QString(); +} + +void Effort::setType(QString type) { + if (type == "Effort") + setType(Type_Effort); + else if (type == "Type_FixedDuration") + setType(Type_FixedDuration); + else + setType(Type_Effort); // default +} + +QString Effort::risktypeToString() const { + if (m_risktype == Risk_None) + return QString("None"); + if (m_risktype == Risk_Low) + return QString("Low"); + if (m_risktype == Risk_High) + return QString("High"); + + return QString(); +} + +void Effort::setRisktype(QString type) { + if (type == "High") + setRisktype(Risk_High); + else if (type == "Low") + setRisktype(Risk_Low); + else + setRisktype(Risk_None); // default +} + +void Effort::setOptimisticRatio(int percent) +{ + int p = percent>0 ? -percent : percent; + m_optimisticEffort = m_expectedEffort*(100+p)/100; +} + +int Effort::optimisticRatio() const { + if (m_expectedEffort == Duration::zeroDuration) + return 0; + return (m_optimisticEffort.milliseconds()*100/m_expectedEffort.milliseconds())-100; +} + +void Effort::setPessimisticRatio(int percent) +{ + int p = percent<0 ? -percent : percent; + m_pessimisticEffort = m_expectedEffort*(100+p)/100; +} +int Effort::pessimisticRatio() const { + if (m_expectedEffort == Duration::zeroDuration) + return 0; + return m_pessimisticEffort.milliseconds()*100/m_expectedEffort.milliseconds()-100; +} + +// Debugging +#ifndef NDEBUG +void Node::printDebug(bool children, QCString indent) { + kdDebug()<<indent<<" Unique node identity="<<m_id<<endl; + if (m_effort) m_effort->printDebug(indent); + QString s = " Constraint: " + constraintToString(); + if (m_constraint == MustStartOn || m_constraint == StartNotEarlier || m_constraint == FixedInterval) + kdDebug()<<indent<<s<<" ("<<constraintStartTime().toString()<<")"<<endl; + if (m_constraint == MustFinishOn || m_constraint == FinishNotLater || m_constraint == FixedInterval) + kdDebug()<<indent<<s<<" ("<<constraintEndTime().toString()<<")"<<endl; + Schedule *cs = m_currentSchedule; + if (cs) { + kdDebug()<<indent<<" Current schedule: "<<"id="<<cs->id()<<" '"<<cs->name()<<"' type: "<<cs->type()<<endl; + } else { + kdDebug()<<indent<<" Current schedule: None"<<endl; + } + QIntDictIterator<Schedule> it = m_schedules; + for (; it.current(); ++it) { + it.current()->printDebug(indent+" "); + } + kdDebug()<<indent<<" Parent: "<<(m_parent ? m_parent->name() : QString("None"))<<endl; + kdDebug()<<indent<<" Level: "<<level()<<endl; + kdDebug()<<indent<<" No of predecessors: "<<m_dependParentNodes.count()<<endl; + QPtrListIterator<Relation> pit(m_dependParentNodes); + //kdDebug()<<indent<<" Dependant parents="<<pit.count()<<endl; + if (pit.count() > 0) { + for ( ; pit.current(); ++pit ) { + pit.current()->printDebug(indent); + } + } + kdDebug()<<indent<<" No of successors: "<<m_dependChildNodes.count()<<endl; + QPtrListIterator<Relation> cit(m_dependChildNodes); + //kdDebug()<<indent<<" Dependant children="<<cit.count()<<endl; + if (cit.count() > 0) { + for ( ; cit.current(); ++cit ) { + cit.current()->printDebug(indent); + } + } + + //kdDebug()<<indent<<endl; + indent += " "; + if (children) { + QPtrListIterator<Node> it(m_nodes); + for ( ; it.current(); ++it ) { + it.current()->printDebug(true,indent); + } + } + +} +#endif + + +#ifndef NDEBUG +void Effort::printDebug(QCString indent) { + kdDebug()<<indent<<" Effort:"<<endl; + indent += " "; + kdDebug()<<indent<<" Expected: "<<m_expectedEffort.toString()<<endl; + kdDebug()<<indent<<" Optimistic: "<<m_optimisticEffort.toString()<<endl; + kdDebug()<<indent<<" Pessimistic: "<<m_pessimisticEffort.toString()<<endl; + + kdDebug()<<indent<<" Risk: "<<risktypeToString()<<endl; + kdDebug()<<indent<<" Pert expected: "<<pertExpected().toString()<<endl; + kdDebug()<<indent<<" Pert optimistic: "<<pertOptimistic().toString()<<endl; + kdDebug()<<indent<<" Pert pessimistic: "<<pertPessimistic().toString()<<endl; + kdDebug()<<indent<<" Pert variance: "<<variance().toString()<<endl; +} +#endif + +} //KPlato namespace diff --git a/kplato/kptnode.h b/kplato/kptnode.h new file mode 100644 index 00000000..65623028 --- /dev/null +++ b/kplato/kptnode.h @@ -0,0 +1,642 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Thomas Zander zander@kde.org + Copyright (C) 2004, 2005 Dag Andersen <danders@get2net.dk> + + 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 KPTNODE_H +#define KPTNODE_H + +#include "kptrelation.h" +#include "kptduration.h" +#include "kptdatetime.h" +#include "kptschedule.h" + +#include <qintdict.h> +#include <qrect.h> +#include <qptrlist.h> +#include <qstring.h> +#include <qcanvas.h> + +#include <vector> + +class QDomElement; + +namespace KPlato +{ + +class Account; +class Project; +class Appointment; +class ResourceGroup; +class Resource; +class ResourceGroupRequest; +class Effort; +class WBSDefinition; +class EffortCostMap; + +/** + * This class represents any node in the project, a node can be a project or + * a subproject or any task. + * This class is basically an abstract interface to make the design more OO. + */ +class Node { + +public: + enum ConstraintType { ASAP, ALAP, MustStartOn, MustFinishOn, StartNotEarlier, FinishNotLater, FixedInterval }; + + Node(Node *parent = 0); + Node(Node &node, Node *parent = 0); + + + // Declare the class abstract + virtual ~Node() = 0; + + bool setId(QString id); + QString id() const { return m_id; } // unique identity + + enum NodeTypes { + Type_Node = 0, + Type_Project = 1, + Type_Subproject = 2, + Type_Task = 3, + Type_Milestone = 4, + Type_Periodic = 5, + Type_Summarytask = 6 + }; + + virtual int type() const = 0; + + /** + * Returns a pointer to the project node (main- or sub-project) + * Returns 0 if no project exists. + */ + virtual Node *projectNode(); + + // The load and save methods + virtual bool load(QDomElement &) { return true; } + virtual bool load(QDomElement &, Project &) { return true; } + virtual void save(QDomElement &element) const = 0; + /// Save my and my childrens relations. + virtual void saveRelations(QDomElement &element) const; + + // simple child node management + // Child nodes are things like subtasks, basically a task can exists of + // several sub-tasks. Creating a table has 4 subtasks, 1) measuring + // 2) cutting 3) building 4) painting. + Node *getParent() const { return m_parent; } + void setParent( Node* newParent ) { m_parent = newParent;} + const QPtrList<Node> &childNodeIterator() const { return m_nodes; } + int numChildren() const { return m_nodes.count(); } + virtual void addChildNode(Node *node, Node *after=0); + virtual void insertChildNode(unsigned int index, Node *node); + void delChildNode(Node *node, bool remove=true); + void delChildNode(int number, bool remove=true); + Node* getChildNode(int number) { return m_nodes.at(number); } + const Node* getChildNode(int number) const; + int findChildNode( Node* node ); + + // Time-dependent child-node-management. + // list all nodes that are dependent upon this one. + // Building a house requires the table to be finished, therefore the + // house-building is time dependent on the table-building. So a child + // of the table-building node is the house-building node. + + int numDependChildNodes() const { return m_dependChildNodes.count(); } + /// Adds relation to both this node and address node + virtual void addDependChildNode( Node *node, Relation::Type p=Relation::FinishStart); + /// Adds relation to both this node and address node + virtual void addDependChildNode( Node *node, Relation::Type p, Duration lag); + /// Adds relation only to this node + virtual bool addDependChildNode( Relation *relation); + /// Inserts relation to this node at index address index and appends relation to address node + virtual void insertDependChildNode( unsigned int index, Node *node, Relation::Type p=Relation::FinishStart); + void delDependChildNode( Node *node, bool remove=false); + void delDependChildNode( Relation *rel, bool remove=false); + void delDependChildNode( int number, bool remove=false); + Relation *getDependChildNode( int number) { + return m_dependChildNodes.at(number); + } + QPtrList<Relation> &dependChildNodes() { return m_dependChildNodes; } + + /** + * Takes the relation rel from this node only. + * Never deletes even when autoDelete = true. + */ + void takeDependChildNode(Relation *rel); + + int numDependParentNodes() const { return m_dependParentNodes.count(); } + /// Adds relation to both this node and node + virtual void addDependParentNode(Node *node, Relation::Type p=Relation::FinishStart); + /// Adds relation to both this node and node + virtual void addDependParentNode( Node *node, Relation::Type p, Duration lag); + /// Adds relation only to this node + virtual bool addDependParentNode( Relation *relation); + /// Inserts relation to this node at index and appends relation to node + virtual void insertDependParentNode( unsigned int index, Node *node, Relation::Type p=Relation::FinishStart); + void delDependParentNode( Node *node, bool remove=false); + void delDependParentNode( Relation *rel, bool remove=false); + void delDependParentNode( int number, bool remove=false); + Relation *getDependParentNode( int number) { + return m_dependParentNodes.at(number); + } + QPtrList<Relation> &dependParentNodes() { return m_dependParentNodes; } + + /** + * Takes the relation rel from this node only. + * Never deletes even when autoDelete = true. + */ + void takeDependParentNode(Relation *rel); + + bool isParentOf(Node *node); + bool isDependChildOf(Node *node); + + Relation *findParentRelation(Node *node); + Relation *findChildRelation(Node *node); + Relation *findRelation(Node *node); + + void setStartTime(DateTime startTime); + /// Return the scheduled start time + virtual DateTime startTime() const + { return m_currentSchedule ? m_currentSchedule->startTime : DateTime(); } + const QDate &startDate() const { return m_dateOnlyStartDate; } + void setEndTime(DateTime endTime); + /// Return the scheduled end time + virtual DateTime endTime() const + { return m_currentSchedule ? m_currentSchedule->endTime : DateTime(); } + const QDate &endDate() const { return m_dateOnlyEndDate; } + + void setEffort(Effort* e) { m_effort = e; } + Effort* effort() const { return m_effort; } + + /** + * Returns the (previously) calculated duration. + */ + virtual Duration *getExpectedDuration() = 0; + + /** + * Instead of using the expected duration, generate a random value using + * the Distribution of each Task. This can be used for Monte-Carlo + * estimation of Project duration. + */ + virtual Duration *getRandomDuration() = 0; + + /** + * Calculate the delay of this node. + * It is the difference between the actual startTime and scheduled startTime. + */ + Duration *getDelay(); + + /** + * getEarliestStart() returns earliest time this node can start + * given the constraints of the network. + * @see earliestStart + */ + DateTime getEarliestStart() const + { return m_currentSchedule ? m_currentSchedule->earliestStart : DateTime(); } + /** + * setEarliestStart() sets earliest time this node can start + * @see earliestStart + */ + void setEarliestStart(const DateTime &dt) + { if (m_currentSchedule) m_currentSchedule->earliestStart = dt; } + /** + * getLatestFinish() returns latest time this node can finish + * @see latestFinish + */ + DateTime getLatestFinish() const + { return m_currentSchedule ? m_currentSchedule->latestFinish : DateTime(); } + /** + * setLatestFinish() sets latest time this node can finish + * given the constraints of the network. + * @see latestFinish + */ + void setLatestFinish(const DateTime &dt) + { if (m_currentSchedule) m_currentSchedule->latestFinish = dt; } + + QString &name() { return m_name; } + QString &leader() { return m_leader; } + QString &description() { return m_description; } + const QString &name() const { return m_name; } + const QString &leader() const { return m_leader; } + const QString &description() const { return m_description; } + void setName(const QString &n) { m_name = n; } + void setLeader(const QString &l) { m_leader = l; } + void setDescription(const QString &d) { m_description = d; } + + virtual void setConstraint(Node::ConstraintType type) { m_constraint = type; } + void setConstraint(QString &type); + int constraint() const { return m_constraint; } + QString constraintToString() const; + + virtual void setConstraintStartTime(QDateTime time) { m_constraintStartTime = time; } + virtual void setConstraintEndTime(QDateTime time) { m_constraintEndTime = time; } + + virtual DateTime constraintStartTime() const { return m_constraintStartTime; } + virtual DateTime constraintEndTime() const { return m_constraintEndTime; } + virtual DateTime startNotEarlier() const { return m_constraintStartTime; } + virtual DateTime finishNotLater() const { return m_constraintEndTime; } + virtual DateTime mustStartOn() const { return m_constraintStartTime; } + virtual DateTime mustFinishOn() const { return m_constraintEndTime; } + + virtual ResourceGroupRequest *resourceRequest(ResourceGroup */*group*/) const { return 0; } + virtual void makeAppointments(); + + /// EffortType == Effort, but no resource is requested + bool resourceError() const + { return m_currentSchedule ? m_currentSchedule->resourceError : false; } + /// The assigned resource is overbooked + virtual bool resourceOverbooked() const + { return m_currentSchedule ? m_currentSchedule->resourceOverbooked : false; } + /// Return a list of overbooked resources + virtual QStringList overbookedResources() const; + /// Calculates if the assigned resource is overbooked + /// within the duration of this node + virtual void calcResourceOverbooked(); + /// The requested resource is not available + bool resourceNotAvailable() const + { return m_currentSchedule ? m_currentSchedule->resourceNotAvailable : false; } + /// The task cannot be scheduled to fullfill all the constraints + virtual bool schedulingError() const + { return m_currentSchedule ? m_currentSchedule->schedulingError : false; } + /// The node has not been scheduled + bool notScheduled() const + { return m_currentSchedule == 0 || m_currentSchedule->isDeleted() || m_currentSchedule->notScheduled; } + + virtual EffortCostMap plannedEffortCostPrDay(const QDate &start, const QDate &end) const=0; + + /// Returns the total planned effort for this task (or subtasks) + virtual Duration plannedEffort() { return Duration::zeroDuration; } + /// Returns the total planned effort for this task (or subtasks) on date + virtual Duration plannedEffort(const QDate &) { return Duration::zeroDuration; } + /// Returns the planned effort up to and including date + virtual Duration plannedEffortTo(const QDate &) { return Duration::zeroDuration; } + + /// Returns the total actual effort for this task (or subtasks) + virtual Duration actualEffort() { return Duration::zeroDuration; } + /// Returns the total actual effort for this task (or subtasks) on date + virtual Duration actualEffort(const QDate &/*date*/) { return Duration::zeroDuration; } + /// Returns the total actual effort for this task (or subtasks) up to and including date + virtual Duration actualEffortTo(const QDate &/*date*/) { return Duration::zeroDuration; } + + /** + * Planned cost is the sum total of all resources and other costs + * planned for this node. + */ + virtual double plannedCost() { return 0; } + + /// Planned cost on date + virtual double plannedCost(const QDate &/*date*/) { return 0; } + /** + * Planned cost from start of activity up to and including date + * is the sum of all resource costs and other costs planned for this node. + */ + virtual double plannedCostTo(const QDate &/*date*/) { return 0; } + /** + * Actual cost is the sum total of the reported costs actually used + * for this node. + */ + virtual double actualCost() { return 0; } + /// Actual cost on date + virtual double actualCost(const QDate &/*date*/) { return 0; } + /// Actual cost up to and including date + virtual double actualCostTo(const QDate &/*date*/) { return 0; } + + /// Effort based performance index + double effortPerformanceIndex(const QDate &/*date*/, bool */*error=0*/) { return 0.0; } + /// Cost performance index + double costPerformanceIndex(const QDate &/*date*/, bool */*error=0*/) { return 0.0; } + + virtual void initiateCalculationLists(QPtrList<Node> &startnodes, QPtrList<Node> &endnodes, QPtrList<Node> &summarytasks) = 0; + virtual DateTime calculateForward(int /*use*/) = 0; + virtual DateTime calculateBackward(int /*use*/) = 0; + virtual DateTime scheduleForward(const DateTime &, int /*use*/) = 0; + virtual DateTime scheduleBackward(const DateTime &, int /*use*/) = 0; + virtual void adjustSummarytask() = 0; + + virtual void initiateCalculation(Schedule &sch); + virtual void resetVisited(); + void propagateEarliestStart(DateTime &time); + void propagateLatestFinish(DateTime &time); + void moveEarliestStart(DateTime &time); + void moveLatestFinish(DateTime &time); + // Reimplement this + virtual Duration summarytaskDurationForward(const DateTime &/*time*/) + { return Duration::zeroDuration; } + // Reimplement this + virtual DateTime summarytaskEarliestStart() + { return DateTime(); } + // Reimplement this + virtual Duration summarytaskDurationBackward(const DateTime &/*time*/) + { return Duration::zeroDuration; } + // Reimplement this + virtual DateTime summarytaskLatestFinish() + { return DateTime(); } + // Returns the (previously) calculated duration + const Duration &duration() + { return m_currentSchedule ? m_currentSchedule->duration : Duration::zeroDuration; } + /** + * Calculates and returns the duration of the node. + * Uses the correct expected-, optimistic- or pessimistic effort + * dependent on use. + * @param time Where to start calculation. + * @param use Calculate using expected-, optimistic- or pessimistic estimate. + * @param backward If true, time specifies when the task should end. + */ + Duration duration(const DateTime &time, int use, bool backward); + // Reimplement this + virtual Duration calcDuration(const DateTime &/*time*/, const Duration &/*effort*/, bool /*backward*/) { return Duration::zeroDuration;} + + Node *siblingBefore(); + Node *childBefore(Node *node); + Node *siblingAfter(); + Node *childAfter(Node *node); + bool moveChildUp(Node *node); + bool moveChildDown(Node *node); + + /// Check if this node can be linked to node + bool legalToLink(Node *node); + /// Check if node par can be linked to node child. (Reimplement) + virtual bool legalToLink(Node *, Node *) { return false; } + /// Check if this node has any dependent child nodes + virtual bool isEndNode() const; + /// Check if this node has any dependent parent nodes + virtual bool isStartNode() const; + virtual void clearProxyRelations() {} + virtual void addParentProxyRelations(QPtrList<Relation> &) {} + virtual void addChildProxyRelations(QPtrList<Relation> &) {} + virtual void addParentProxyRelation(Node *, const Relation *) {} + virtual void addChildProxyRelation(Node *, const Relation *) {} + + /// Save appointments for schedule with id + virtual void saveAppointments(QDomElement &element, long id) const; + ///Return the list of appointments for current schedule. + QPtrList<Appointment> appointments(); + /// Return appointment this node have with resource +// Appointment *findAppointment(Resource *resource); + /// Adds appointment to this node only (not to resource) + virtual bool addAppointment(Appointment *appointment); + /// Adds appointment to this node only (not to resource) + virtual bool addAppointment(Appointment *appointment, Schedule &main); + /// Adds appointment to both this node and resource + virtual void addAppointment(ResourceSchedule *resource, DateTime &start, DateTime &end, double load=100); + + /// Find the node with my id + virtual Node *findNode() const { return findNode(m_id); } + /// Find the node with identity id + virtual Node *findNode(const QString &id) const + { return (m_parent ? m_parent->findNode(id) : 0); } + /// Remove myself from the id register + virtual bool removeId() { return removeId(m_id); } + /// Remove the registered identity id + virtual bool removeId(const QString &id) + { return (m_parent ? m_parent->removeId(id) : false); } + /// Insert myself into the id register + virtual void insertId(const QString &id) { insertId(id, this); } + /// Insert node with identity id into the register + virtual void insertId(const QString &id, const Node *node) + { if (m_parent) m_parent->insertId(id, node); } + + /** + * This is when work can start on this node in accordance with + * the calendar of allocated resources. Normally this is the same + * as @ref startTime(), but may differ if timing constraints are set. + */ + virtual DateTime workStartTime() const + { return m_currentSchedule ? m_currentSchedule->workStartTime : DateTime(); } + void setWorkStartTime(const DateTime &dt) + { if (m_currentSchedule) m_currentSchedule->workStartTime = dt; } + + /** + * This is when work can finish on this node in accordance with + * the calendar of allocated resources. Normally this is the same + * as @ref endTime(), but may differ if timing constraints are set. + */ + virtual DateTime workEndTime() const + { return m_currentSchedule ? m_currentSchedule->workEndTime : DateTime(); } + void setWorkEndTime(const DateTime &dt) + { if (m_currentSchedule) m_currentSchedule->workEndTime = dt; } + + virtual bool isCritical() const { return false; } + virtual bool inCriticalPath() const + { return m_currentSchedule ? m_currentSchedule->inCriticalPath : false; } + virtual bool calcCriticalPath(bool fromEnd); + + /// Returns the level this node is in the hierarchy. Top node is level 0. + virtual int level(); + /// Generate WBS + virtual void generateWBS(int count, WBSDefinition &def, QString wbs=QString()); + QString wbs() const { return m_wbs; } + + double startupCost() const { return m_startupCost; } + void setStartupCost(double cost) { m_startupCost = cost; } + + Account *startupAccount() const { return m_startupAccount; } + void setStartupAccount(Account *acc) { m_startupAccount = acc; } + + double shutdownCost() const { return m_shutdownCost; } + void setShutdownCost(double cost) { m_shutdownCost = cost; } + + Account *shutdownAccount() const { return m_shutdownAccount; } + void setShutdownAccount(Account *acc) { m_shutdownAccount = acc; } + + Account *runningAccount() const { return m_runningAccount; } + void setRunningAccount(Account *acc) { m_runningAccount = acc; } + + Schedule *currentSchedule() const { return m_currentSchedule; } + /// Set current schedule to schedule with identity id, for me and my children + virtual void setCurrentSchedule(long id); + // NOTE: Cannot use setCurrentSchedule() due to overload/casting problems + void setCurrentSchedulePtr(Schedule *schedule) { m_currentSchedule = schedule; } + + QIntDict<Schedule> &schedules() { return m_schedules; } + /// Find schedule matching name and type. Does not return deleted schedule. + Schedule *findSchedule(const QString name, const Schedule::Type type) const; + /// Find schedule matching type. Does not return deleted schedule. + Schedule *findSchedule(const Schedule::Type type) const; + /// Find schedule matching id. Also returns deleted schedule. + Schedule *findSchedule(long id) const { return m_schedules[id]; } + /// Take, don't delete (as in destruct). + void takeSchedule(const Schedule *schedule); + /// Add schedule to list, replace if schedule with same id allready exists. + void addSchedule(Schedule *schedule); + /// Create a new schedule. + Schedule *createSchedule(QString name, Schedule::Type type, long id); + /// Create a new schedule. + Schedule *createSchedule(Schedule *parent); + + /// Set deleted = onoff for schedule with id + void setScheduleDeleted(long id, bool onoff); + /// Set parent schedule recursivly + virtual void setParentSchedule(Schedule *sch); + + DateTime startTime() + { return m_currentSchedule ? m_currentSchedule->startTime : DateTime(); } + DateTime endTime() + { return m_currentSchedule ? m_currentSchedule->endTime : DateTime(); } + +protected: + QPtrList<Node> m_nodes; + QPtrList<Relation> m_dependChildNodes; + QPtrList<Relation> m_dependParentNodes; + Node *m_parent; + + QString m_id; // unique id + QString m_name; // Name of this node + QString m_leader; // Person or group responsible for this node + QString m_description; // Description of this node + + Effort* m_effort; + + + ConstraintType m_constraint; + + /** + * m_constraintTime is used if any of the constraints + * FixedInterval, StartNotEarlier, MustStartOn or FixedInterval is selected + */ + DateTime m_constraintStartTime; + /** + * m_constraintEndTime is used if any of the constraints + * FixedInterval, FinishNotLater, MustFinishOn or FixedInterval is selected + */ + DateTime m_constraintEndTime; + + bool m_visitedForward; + bool m_visitedBackward; + Duration m_durationForward; + Duration m_durationBackward; + + QDate m_dateOnlyStartDate; + QDate m_dateOnlyEndDate; + Duration m_dateOnlyDuration; + + QIntDict<Schedule> m_schedules; + Schedule *m_currentSchedule; + + QString m_wbs; + + double m_startupCost; + Account *m_startupAccount; + double m_shutdownCost; + Account *m_shutdownAccount; + Account *m_runningAccount; + +private: + void init(); + +#ifndef NDEBUG +public: + virtual void printDebug(bool children, QCString indent); +#endif + +}; + +//////////////////////////////// Effort //////////////////////////////// +/** + * Any @ref Node will store how much time it takes to complete the node + * (typically a @ref Task) in the traditional scheduling software the + * effort which is needed to complete the node is not simply a timespan but + * is stored as an optimistic, a pessimistic and an expected timespan. + */ +class Effort { +public: + Effort ( Duration e = Duration::zeroDuration, Duration p = Duration::zeroDuration, + Duration o = Duration::zeroDuration ); + + Effort ( double e, double p = 0, double o = 0); + + Effort (const Effort &effort); + ~Effort(); + + enum Type { Type_Effort = 0, // Changing amount of resources changes the task duration + Type_FixedDuration = 1 // Changing amount of resources will not change the tasks duration + }; + Type type() const { return m_type; } + void setType(Type type) { m_type = type; } + void setType(QString type); + QString typeToString() const; + + enum Risktype { Risk_None, Risk_Low, Risk_High }; + Risktype risktype() const { return m_risktype; } + void setRisktype(Risktype type) { m_risktype = type; } + void setRisktype(QString type); + QString risktypeToString() const; + + enum Use { Use_Expected=0, Use_Optimistic=1, Use_Pessimistic=2 }; + Duration effort(int use) const; + const Duration& optimistic() const {return m_optimisticEffort;} + const Duration& pessimistic() const {return m_pessimisticEffort;} + const Duration& expected() const {return m_expectedEffort;} + + void set( Duration e, Duration p = Duration::zeroDuration, Duration o = Duration::zeroDuration ); + void set( int e, int p = -1, int o = -1 ); + void set(unsigned days, unsigned hours, unsigned minutes); + void expectedEffort(unsigned *days, unsigned *hours, unsigned *minutes); + + bool load(QDomElement &element); + void save(QDomElement &element) const; + + /** + * Set the optimistic duration + * @param percent should be a negativ value. + */ + void setOptimisticRatio(int percent); + /** + * Return the "optimistic" duration as deviation from "expected" in percent. + * This should be a negativ value. + */ + int optimisticRatio() const; + /** + * Set the pessimistic duration + * @param percent should be a positive value. + */ + void setPessimisticRatio(int percent); + /** + * Return the "pessimistic" duration as the deviation from "expected" in percent. + * This should be a positive value. + */ + int pessimisticRatio() const; + + /** + * No effort. + */ + static const Effort zeroEffort; + + Duration variance() const; + Duration pertExpected() const; + Duration pertOptimistic() const; + Duration pertPessimistic() const; + +private: + Duration m_optimisticEffort; + Duration m_pessimisticEffort; + Duration m_expectedEffort; + + Type m_type; + Risktype m_risktype; + +#ifndef NDEBUG +public: + void printDebug(QCString indent); +#endif + +}; + +} //KPlato namespace + +#endif diff --git a/kplato/kptpart.cc b/kplato/kptpart.cc new file mode 100644 index 00000000..42055e88 --- /dev/null +++ b/kplato/kptpart.cc @@ -0,0 +1,419 @@ +/* This file is part of the KDE project + Copyright (C) 1998, 1999, 2000 Torben Weis <weis@kde.org> + Copyright (C) 2004, 2005 Dag Andersen <danders@get2net.dk> + Copyright (C) 2006 Raphael Langerhorst <raphael.langerhorst@kdemail.net> + Copyright (C) 2006 Dag Andersen <danders@get2net.dk> + + 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. +*/ + +#include "kptpart.h" +#include "kptview.h" +#include "kptfactory.h" +#include "kptproject.h" +#include "kptprojectdialog.h" +#include "kptresource.h" +#include "kptcontext.h" +#include "kptganttview.h" +#include "KDGanttViewTaskLink.h" + +#include <qpainter.h> +#include <qfileinfo.h> + +#include <kdebug.h> +#include <kconfig.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <kstandarddirs.h> +#include <kcommand.h> +#include <KoTemplateChooseDia.h> +#include <KoCommandHistory.h> +#include <KoGlobal.h> + +#define CURRENT_SYNTAX_VERSION "0.5" + +namespace KPlato +{ + +Part::Part(QWidget *parentWidget, const char *widgetName, + QObject *parent, const char *name, bool singleViewMode) + : KoDocument(parentWidget, widgetName, parent, name, singleViewMode), + m_project(0), m_projectDialog(0), m_parentWidget(parentWidget), m_view(0), + m_embeddedGanttView(new GanttView(parentWidget)), + m_embeddedContext(new Context()), m_embeddedContextInitialized(false), + m_context(0), m_xmlLoader() +{ + m_update = m_calculate = false; + m_commandHistory = new KoCommandHistory(actionCollection()); + + setInstance(Factory::global()); + setTemplateType("kplato_template"); + m_config.setReadWrite(isReadWrite()|| !isEmbedded()); + m_config.load(); + + delete m_project; + m_project = new Project(); // after config is loaded + + connect(m_commandHistory, SIGNAL(commandExecuted()), SLOT(slotCommandExecuted())); + connect(m_commandHistory, SIGNAL(documentRestored()), SLOT(slotDocumentRestored())); + + //FIXME the following is really dirty, we should make KPlato::Context a real class + // with getter and setter and signals when content changes, thus we can keep track + QTimer* timer = new QTimer(this,"context update timer"); + connect(timer,SIGNAL(timeout()),this,SLOT(slotCopyContextFromView())); + timer->start(500); +} + + +Part::~Part() { + m_config.save(); + delete m_commandHistory; // before project, in case of dependencies... + delete m_project; + delete m_projectDialog; + if (m_embeddedGanttView) + delete m_embeddedGanttView; + if (m_embeddedContext) + delete m_embeddedContext; +} + + +bool Part::initDoc(InitDocFlags flags, QWidget* parentWidget) { + bool result = true; + + if (flags==KoDocument::InitDocEmpty) + { + delete m_project; + m_project = new Project(); + setAutoSave(0); // disable + setModified(false); + return true; + } + + QString templateDoc; + KoTemplateChooseDia::ReturnType ret; + KoTemplateChooseDia::DialogType dlgtype; + if (flags != KoDocument::InitDocFileNew ) + dlgtype = KoTemplateChooseDia::Everything; + else + dlgtype = KoTemplateChooseDia::OnlyTemplates; + + ret = KoTemplateChooseDia::choose(Factory::global(), templateDoc, + dlgtype, + "kplato_template", + parentWidget); + if (ret == KoTemplateChooseDia::Template) { + resetURL(); + result = loadNativeFormat(templateDoc); + if ( !result ) + showLoadingErrorDialog(); + } else if (ret == KoTemplateChooseDia::File) { + KURL url(templateDoc); + kdDebug() << "Part::initDoc opening URL " << url.prettyURL() <<endl; + result = openURL(url); + } else if (ret == KoTemplateChooseDia::Empty) { + // Make a fresh project and let the user enter some info + delete m_project; + m_project = new Project(); + // an emty project should be empty + // m_projectDialog = new ProjectDialog(*m_project, m_view); + // m_projectDialog->exec(); + + result = true; + } else { + result = false; + } + setAutoSave(0); // disable + setModified(false); + return result; +} + + +KoView *Part::createViewInstance(QWidget *parent, const char *name) { + m_view = new View(this, parent, name); + connect(m_view,SIGNAL(destroyed()),this,SLOT(slotViewDestroyed())); + + // If there is a project dialog this should be deleted so it will + // use the m_view as parent. If the dialog will be needed again, + // it will be made at that point + if (m_projectDialog != 0) { + kdDebug() << "Deleting m_projectDialog because of new ViewInstance\n"; + delete m_projectDialog; + m_projectDialog = 0; + } + if (m_context) + m_view->setContext( *m_context ); + else if (m_embeddedContext && m_embeddedContextInitialized) + m_view->setContext( *m_embeddedContext ); + else { + // Activate menu actions. Assumes ganttview, we don't get any + // 'aboutToShow' signal. Need to redo action control. + m_view->setTaskActionsEnabled(true); + } + //m_view->setBaselineMode(getProject().isBaselined()); FIXME: Removed for this release + return m_view; +} + + +void Part::editProject() { + + QWidget* parent = m_parentWidget; + if (m_view) + parent = m_view; + + if (m_projectDialog == 0) + // Make the dialog + m_projectDialog = new ProjectDialog(*m_project, parent); + + m_projectDialog->exec(); +} + + +bool Part::loadXML(QIODevice *, const QDomDocument &document) { + QTime dt; + dt.start(); + emit sigProgress( 0 ); + + QString value; + QDomElement plan = document.documentElement(); + + // Check if this is the right app + value = plan.attribute("mime", QString::null); + if (value.isEmpty()) { + kdError() << "No mime type specified!" << endl; + setErrorMessage(i18n("Invalid document. No mimetype specified.")); + return false; + } + else if (value != "application/x-vnd.kde.kplato") { + kdError() << "Unknown mime type " << value << endl; + setErrorMessage(i18n("Invalid document. Expected mimetype application/x-vnd.kde.kplato, got %1").arg(value)); + return false; + } + QString m_syntaxVersion = plan.attribute("version", CURRENT_SYNTAX_VERSION); + if (m_syntaxVersion > CURRENT_SYNTAX_VERSION) { + int ret = KMessageBox::warningContinueCancel( + 0, i18n("This document was created with a newer version of KPlato (syntax version: %1)\n" + "Opening it in this version of KPlato will lose some information.").arg(m_syntaxVersion), + i18n("File-Format Mismatch"), i18n("Continue") ); + if (ret == KMessageBox::Cancel) + { + setErrorMessage("USER_CANCELED"); + return false; + } + } + emit sigProgress(5); + + QDomNodeList list = plan.childNodes(); + if (list.count() > 2) { + // TODO: Make a proper bitching about this + kdDebug() << "*** Error ***\n"; + kdDebug() << " Children count should be 1 but is " << list.count() + << "\n"; + return false; + } + m_xmlLoader.startLoad(); + for (unsigned int i = 0; i < list.count(); ++i) { + if (list.item(i).isElement()) { + QDomElement e = list.item(i).toElement(); + + if (e.tagName() == "context") { + delete m_context; + m_context = new Context(); + m_context->load(e); + } else if (e.tagName() == "project") { + Project *newProject = new Project(); + if (newProject->load(e)) { + // The load went fine. Throw out the old project + delete m_project; + m_project = newProject; + delete m_projectDialog; + m_projectDialog = 0; + } + else { + delete newProject; + m_xmlLoader.addMsg(XMLLoaderObject::Errors, "Loading of project failed"); + //TODO add some ui here + } + } + } + } + m_xmlLoader.stopLoad(); + emit sigProgress(100); // the rest is only processing, not loading + + kdDebug() << "Loading took " << (float)(dt.elapsed()) / 1000 << " seconds" << endl; + + // do some sanity checking on document. + emit sigProgress(-1); + + m_commandHistory->clear(); + m_commandHistory->documentSaved(); + setModified( false ); + if (m_view) + m_view->slotUpdate(false); + return true; +} + +QDomDocument Part::saveXML() { + QDomDocument document("kplato"); + + document.appendChild(document.createProcessingInstruction( + "xml", + "version=\"1.0\" encoding=\"UTF-8\"")); + + QDomElement doc = document.createElement("kplato"); + doc.setAttribute("editor", "KPlato"); + doc.setAttribute("mime", "application/x-vnd.kde.kplato"); + doc.setAttribute("version", CURRENT_SYNTAX_VERSION); + document.appendChild(doc); + + delete m_context; + m_context = 0; + if (m_view) { + m_context = new Context(); + m_view->getContext(*m_context); + } + if (m_context) { + m_context->save(doc); + } + // Save the project + m_project->save(doc); + + m_commandHistory->documentSaved(); + return document; +} + + +void Part::slotDocumentRestored() { + //kdDebug()<<k_funcinfo<<endl; + setModified(false); +} + + +void Part::paintContent(QPainter &painter, const QRect &rect, + bool /*transparent*/, + double zoomX, double /*zoomY*/) +{ + kdDebug() << "----------- KPlato: Part::paintContent ------------" << endl; + if (isEmbedded() && m_embeddedGanttView && m_project) + { + if (m_embeddedContext) + { + int ganttsize = m_embeddedContext->ganttview.ganttviewsize; + int tasksize = m_embeddedContext->ganttview.taskviewsize; + bool showtaskname = m_embeddedContext->ganttview.showTaskName; + +// m_embeddedContext->ganttview.ganttviewsize += m_embeddedContext->ganttview.taskviewsize; +// m_embeddedContext->ganttview.taskviewsize = 0; //TODO this doesn't have any effect?! (bug?) + m_embeddedContext->ganttview.showTaskName = true; //since task view is not shown(?), show name in the gantt itself + + m_embeddedGanttView->setContext( m_embeddedContext->ganttview, *m_project ); + + m_embeddedContext->ganttview.ganttviewsize = ganttsize; + m_embeddedContext->ganttview.taskviewsize = tasksize; + m_embeddedContext->ganttview.showTaskName = showtaskname; + } + else + { + kdWarning() << "Don't have any context to set!" << endl; + } + painter.setClipRect(rect, QPainter::CoordPainter); + // We don't support zoom yet, so use the painters scaling + double d_zoom = 1.0; + setZoomAndResolution(100, KoGlobal::dpiX(), KoGlobal::dpiY()); + if ( m_zoomedResolutionX != zoomX ) { + d_zoom *= ( zoomX / m_zoomedResolutionX ); + painter.scale(d_zoom, d_zoom); + } + + m_embeddedGanttView->clear(); + m_embeddedGanttView->draw(*m_project); + m_embeddedGanttView->drawOnPainter(&painter,rect); + } + // ####### handle transparency + + // Need to draw only the document rectangle described in the parameter rect. +// int left = rect.left() / 20; +// int right = rect.right() / 20 + 1; +// int top = rect.top() / 20; +// int bottom = rect.bottom() / 20 + 1; + +// for( int x = left; x < right; ++x ) +// painter.drawLine( x * 40, top * 20, 40 * 20, bottom * 20 ); +// for( int y = left; y < right; ++y ) +// painter.drawLine( left * 20, y * 20, right * 20, y * 20 ); +} + + +void Part::addCommand(KCommand * cmd, bool execute) +{ + m_commandHistory->addCommand(cmd, execute); +} + +void Part::slotCommandExecuted() { + //kdDebug()<<k_funcinfo<<endl; + setModified(true); + kdDebug() << "------- KPlato, is embedded: " << isEmbedded() << endl; + if (m_view == NULL) + return; + + if (m_calculate) + m_view->slotUpdate(false/*config().behavior().calculationMode == Behavior::OnChange*/); + else if (m_update) + m_view->slotUpdate(false); + + if (m_baseline) + m_view->setBaselineMode(getProject().isBaselined()); + + m_update = m_calculate = m_baseline = false; +} + +void Part::slotCopyContextFromView() +{ + if (m_view) + { +// kdDebug() << "Updating embedded context from view context." << endl; + this->m_view->getContext( *m_embeddedContext ); + this->m_embeddedContextInitialized = true; + } +// else +// { +// kdDebug() << "Not updating the context." << endl; +// if (m_context) +// kdDebug() << "Current View: " << m_context->currentView << endl; +// } +} + +void Part::slotViewDestroyed() +{ + m_view = NULL; +} + +void Part::setCommandType(int type) { + //kdDebug()<<k_funcinfo<<"type="<<type<<endl; + if (type == 0) + m_update = true; + else if (type == 1) + m_calculate = true; + else if (type == 2) + m_baseline = true; +} + +void Part::generateWBS() { + m_project->generateWBS(1, m_wbsDefinition); +} + +} //KPlato namespace + +#include "kptpart.moc" diff --git a/kplato/kptpart.h b/kplato/kptpart.h new file mode 100644 index 00000000..dbafa9f6 --- /dev/null +++ b/kplato/kptpart.h @@ -0,0 +1,123 @@ +/* This file is part of the KDE project + Copyright (C) 1998, 1999, 2000 Torben Weis <weis@kde.org> + Copyright (C) 2004, 2005 Dag Andersen <danders@get2net.dk> + Copyright (C) 2006 Raphael Langerhorst <raphael.langerhorst@kdemail.net> + + 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 KPLATO_PART_H +#define KPLATO_PART_H + +#include "kpttask.h" +#include "kptconfig.h" +#include "kptwbsdefinition.h" +#include "kptxmlloaderobject.h" + +#include <KoDocument.h> +#include <KoTextZoomHandler.h> + +class KoView; +class KoCommandHistory; +class KCommand; + +namespace KPlato +{ + +class View; +class Project; +class ProjectDialog; +class ResourceGroup; +class Context; +class GanttView; + +class Part : public KoDocument, public KoTextZoomHandler { + Q_OBJECT + +public: + Part(QWidget *parentWidget = 0, const char *widgetName = 0, + QObject* parent = 0, const char* name = 0, + bool singleViewMode = false); + ~Part(); + + virtual void paintContent(QPainter& painter, const QRect& rect, + bool transparent = FALSE, + double zoomX = 1.0, double zoomY = 1.0); + + virtual bool initDoc(InitDocFlags flags, QWidget* parentWidget=0); + + /** + * Edit the settings of the project + */ + void editProject(); + + Project &getProject() { return *m_project; } + const Project &getProject() const { return *m_project; } + + // The load and save functions. Look in the file kplato.dtd for info + virtual bool loadXML(QIODevice *, const QDomDocument &document); + virtual QDomDocument saveXML(); + + bool saveOasis(KoStore*, KoXmlWriter*) { return false; } + bool loadOasis(const QDomDocument &, KoOasisStyles &, const QDomDocument&, KoStore *) { return false; } + + void addCommand(KCommand * cmd, bool execute=true); + + void setCommandType(int type); + + Config &config() { return m_config; } + + void generateWBS(); + WBSDefinition &wbsDefinition() { return m_wbsDefinition; } + + const XMLLoaderObject &xmlLoader() const { return m_xmlLoader; } +protected: + virtual KoView* createViewInstance(QWidget* parent, const char* name); + +protected slots: + void slotDocumentRestored(); + void slotCommandExecuted(); + void slotCopyContextFromView(); + void slotViewDestroyed(); + +private: + Project *m_project; + ProjectDialog *m_projectDialog; + QWidget* m_parentWidget; + View *m_view; + + /** + * Used for drawing the project when embedded into another koffice app. + * @see paintContent() + */ + GanttView* m_embeddedGanttView; + Context* m_embeddedContext; + bool m_embeddedContextInitialized; + + KoCommandHistory *m_commandHistory; + bool m_update, m_calculate, m_baseline; + + Config m_config; + Context *m_context; + + WBSDefinition m_wbsDefinition; + + XMLLoaderObject m_xmlLoader; +}; + +} //KPlato namespace + +#endif diff --git a/kplato/kptpertcanvas.cc b/kplato/kptpertcanvas.cc new file mode 100644 index 00000000..a50672bc --- /dev/null +++ b/kplato/kptpertcanvas.cc @@ -0,0 +1,436 @@ +/* This file is part of the KDE project + Copyright (C) 2003 - 2004 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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. +*/ + +#include "kptpertcanvas.h" +#include "kptnode.h" +#include "kptrelation.h" +#include "kptrelationdialog.h" +#include "kptcanvasitem.h" + +#include <qbuffer.h> +#include <qtimer.h> +#include <qclipboard.h> +#include <qprogressdialog.h> +#include <qobjectlist.h> +#include <qpainter.h> +#include <qheader.h> +#include <qcursor.h> +#include <qrect.h> +#include <qsize.h> +#include <qptrlist.h> + +#include <KoStore.h> +#include <ktempfile.h> +#include <klocale.h> +#include <kdebug.h> +#include <kapplication.h> +#include <kmessagebox.h> +#include <assert.h> +#include <kmultipledrag.h> +#include <klistview.h> + +namespace KPlato +{ + +PertCanvas::PertCanvas( QWidget *parent ) + : QCanvasView( parent, "Pert canvas" /*WNorthWestGravity WStaticContents| WResizeNoErase | WRepaintNoErase */), + m_verticalGap(20), + m_horizontalGap(10), + m_itemSize(100,30) + +{ + //setHScrollBarMode(QScrollView::AlwaysOn); + m_canvas = new QCanvas( this ); + setCanvas( m_canvas ); +} + +PertCanvas::~PertCanvas() +{ +} + +void PertCanvas::draw(Project& project) +{ + //kdDebug()<<k_funcinfo<<endl; + clear(); + updateContents(); + + // First make node items + QPtrListIterator<Node> nit(project.childNodeIterator()); + for ( ; nit.current(); ++nit ) { + createChildItems(createNodeItem(nit.current())); + } + + // First all items with relations + QPtrDictIterator<PertNodeItem> it(m_nodes); + for(; it.current(); ++it) + { + if (!(it.current()->hasParent()) && it.current()->hasChild()) + { + m_rows.append(new QMemArray<bool>(1)); // New node always goes into new row, first column + it.current()->move(this, m_rows.count()-1, 0); // item also moves it's children + } + } + // now items without relations + for(it.toFirst(); it.current(); ++it) + { + if (!(it.current()->hasParent() || it.current()->hasChild())) + { + m_rows.append(new QMemArray<bool>(1)); // New node always goes into new row, first column + it.current()->move(this, m_rows.count()-1, 0); + } + } + drawRelations(); // done _after_ all nodes are drawn + QSize s = canvasSize(); + m_canvas->resize(s.width(), s.height()); + update(); +} + +PertNodeItem *PertCanvas::createNodeItem(Node *node) +{ + PertNodeItem *item = m_nodes.find(node); + if (!item) + { + if ( node->type() == Node::Type_Project) + kdDebug()<<k_funcinfo<<"Project nodes should not have relations"<<endl; + else if (node->type() == Node::Type_Subproject) + item = new PertProjectItem(this, *node); + else if (node->type()== Node::Type_Summarytask) + item = new PertTaskItem(this, *node); + else if (node->type()== Node::Type_Task) + item = new PertTaskItem(this, *node); + else if (node->type() == Node::Type_Milestone) + item = new PertMilestoneItem(this, *node); + else + kdDebug()<<k_funcinfo<<"Not implemented yet"<<endl; + + if (item) + m_nodes.insert(node, item); + } + return item; +} + +void PertCanvas::createChildItems(PertNodeItem *parentItem) +{ + //kdDebug()<<k_funcinfo<<"parentItem="<<(parentItem ? parentItem->node().name() : "nil")<<endl; + if (!parentItem) + return; + + QPtrListIterator<Relation> it(parentItem->node().dependChildNodes()); + for (; it.current(); ++it) + { + PertNodeItem *childItem = createNodeItem(it.current()->child()); + if (childItem) + parentItem->addChildRelation(it.current(), childItem); + m_relations.append(it.current()); + } + + // Now my children + QPtrListIterator<Node> nit(parentItem->node().childNodeIterator()); + for ( ; nit.current(); ++nit ) { + createChildItems(createNodeItem(nit.current())); + } +} + +void PertCanvas::drawRelations() +{ + //kdDebug()<<k_funcinfo<<endl; + QPtrListIterator<Relation> it(m_relations); + for (; it.current(); ++it) + { + PertNodeItem *parentItem = m_nodes.find(it.current()->parent()); + PertNodeItem *childItem = m_nodes.find(it.current()->child()); + if (parentItem && childItem) + { + PertRelationItem *item = new PertRelationItem(this, parentItem, childItem, it.current()); + item->show(); + } + } +} + +void PertCanvas::mapNode(PertNodeItem *item) +{ + //kdDebug()<<k_funcinfo<<endl; + if (! m_rows.at(item->row()) || (item->column() >= 0 && m_rows.at(item->row())->count() <= uint(item->column()))) + { + kdError()<<k_funcinfo<<item->node().name()<<": non existing map for: ("<<item->row()<<","<<item->column()<<")"<<endl; + return; + } + m_rows.at(item->row())->at(item->column()) = true; +} + +void PertCanvas::mapChildNode(PertNodeItem *parentItem, PertNodeItem *childItem, Relation::Type type) +{ + //kdDebug()<<k_funcinfo<<"Parent: "<<parentItem->node().name()<<" to child: "<<(childItem ? childItem->node().name() : "None")<<endl; + if (!childItem) + { // shouldn't happen... + kdError()<<k_funcinfo<<"No childItem"<<endl; + return; + } + int row = parentItem->row(); + int col = parentItem->column(); + int chRow = childItem->row(); + int chCol = childItem->column(); + bool chMapped = (chRow > -1 && chCol > -1); + //kdDebug()<<k_funcinfo<<"Parent: "<<parentItem->node().name()<<" at ("<<row<<","<<col<<"): Moving "<<childItem->node().name()<<" from: "<<chRow<<","<<chCol<<endl; + + if (type == Relation::StartStart || + type == Relation::FinishFinish) + { + // node goes into row below parent, at least same col + if (chMapped) + { + m_rows.at(chRow)->at(chCol) = false; + //kdDebug()<<k_funcinfo<<" Moving "<<childItem->node().name()<<" from: "<<chRow<<","<<chCol<<endl; + if (chRow <= row) + { + chRow = row+1; + if (chRow >= 0 && m_rows.count() <= uint(chRow)) { + m_rows.append(new QMemArray<bool>(1)); // make a new row + chRow = m_rows.count()-1; // to be safe + } + //kdDebug()<<k_funcinfo<<" Moving "<<childItem->node().name()<<" to row: "<<chRow<<endl; + } + if (chCol < col) + { + chCol = col; + if (chCol >= 0 && m_rows.at(chRow)->count() <= uint(chCol)) // col does not exist + m_rows.at(chRow)->resize(chCol+1); + + //kdDebug()<<k_funcinfo<<" Moved "<<childItem->node().name()<<" to col: "<<chCol<<endl; + } + + } + else + { + if (!(m_rows.at(row+1)) || // next row does not exists + m_rows.at(row+1)->at(col) == true) // col is not free + { + m_rows.append(new QMemArray<bool>(col+1)); // make a new row + } + else if (col >= 0 && m_rows.at(row+1)->count() <= uint(col)) // col does not exist + m_rows.at(row)->resize(col+1); + + chRow = m_rows.count() -1; + chCol = col; + } + } + else if (type == Relation::FinishStart) + { + // node goes into same row, next col if col free + if (chMapped) + { + m_rows.at(chRow)->at(chCol) = false; + if (chRow < row) + chRow = row; + if (chCol <= col) + { + chCol = col+1; + } + if (chCol >= 0 && m_rows.at(chRow)->count() <= uint(chCol)) // col does not exist + m_rows.at(chRow)->resize(chCol+1); + } + else + { + ++col; + if (col >= 0 && m_rows.at(row)->count() <= uint(col)) + m_rows.at(row)->resize(col+1); // make new column + else if (m_rows.at(row)->at(col) = true) + m_rows.append(new QMemArray<bool>(col+1)); // col not free, so make a new row + + chRow = m_rows.count() -1; + chCol = col; + } + } + else + { + kdError()<<k_funcinfo<<"Unknow relation type"<<endl; + return; + } + childItem->move(this, chRow, chCol); +} + +QSize PertCanvas::canvasSize() +{ + //kdDebug()<<k_funcinfo<<endl; + QSize s(0,0); + QCanvasItemList list = canvas()->allItems(); + QCanvasItemList::Iterator it = list.begin(); + for (; it != list.end(); ++it) + { + QRect r = (*it)->boundingRect(); + s.setWidth(QMAX(s.width(), r.right())); + s.setHeight(QMAX(s.height(), r.bottom())); + } + s.setWidth(s.width()+20); + s.setHeight(s.height()+20); + return s; +} + +void PertCanvas::clear() +{ + m_nodes.clear(); + m_relations.clear(); + m_rows.clear(); + QCanvasItemList list = canvas()->allItems(); + QCanvasItemList::Iterator it = list.begin(); + for (; it != list.end(); ++it) + { + if ( *it ) + delete *it; + } +} + +void PertCanvas::contentsMousePressEvent ( QMouseEvent * e ) +{ + //kdDebug()<<k_funcinfo<<" gl.X,gl.Y="<<e->globalX()<<","<<e->globalY()<<" x,y="<<e->x()<<","<<e->y()<<endl; + switch (e->button()) + { + case QEvent::LeftButton: + { + break; + } + case QEvent::RightButton: + { + PertNodeItem *item = selectedItem(); + if (item) + item->setSelected(false); + canvas()->update(); + + QCanvasItemList l = canvas()->collisions(e->pos()); + for (QCanvasItemList::Iterator it=l.begin(); it!=l.end(); ++it) + { + if ( (*it)->rtti() == PertProjectItem::RTTI || + (*it)->rtti() == PertTaskItem::RTTI || + (*it)->rtti() == PertMilestoneItem::RTTI ) + { + PertNodeItem *item = (PertNodeItem *)(*it); + { + item->setSelected(true); + canvas()->update(); + emit rightButtonPressed(&(item->node()), e->globalPos()); + if (item == selectedItem()) { + // item maybe deleted + item->setSelected(false); + } + canvas()->update(); + break; + } + } + } + break; + } + case QEvent::MidButton: + break; + default: + break; + } +} + +void PertCanvas::contentsMouseReleaseEvent ( QMouseEvent * e ) +{ + //kdDebug()<<k_funcinfo<<" gl.X,gl.Y="<<e->globalX()<<","<<e->globalY()<<" x,y="<<e->x()<<","<<e->y()<<endl; + switch (e->button()) + { + case QEvent::LeftButton: + { + bool hit = false; + QCanvasItemList l = canvas()->collisions(e->pos()); + for (QCanvasItemList::Iterator it=l.begin(); it!=l.end(); ++it) + { + if ( (*it)->rtti() == PertProjectItem::RTTI || + (*it)->rtti() == PertTaskItem::RTTI || + (*it)->rtti() == PertMilestoneItem::RTTI ) + { + hit = true; + PertNodeItem *item = (PertNodeItem *)(*it); + PertNodeItem *par = selectedItem(); + if ( !par) + { + //kdDebug()<<k_funcinfo<<" First node="<<item->node().name()<<endl; + item->setSelected(true); + canvas()->update(); + return; + } + par->setSelected(false); + if (&(item->node()) == &(par->node())) + { + break; + } + //kdDebug()<<k_funcinfo<<" Second node="<<item->node().name()<<endl; + // open relation dialog + if (!par->node().legalToLink(&(item->node()))) { + KMessageBox::sorry(this, i18n("Cannot link these nodes")); + } else { + Relation *rel = item->node().findRelation(&(par->node())); + if (rel) + emit modifyRelation(rel); + else + emit addRelation(&(par->node()), &(item->node())); + } + break; + } + } + if (!hit) { + PertNodeItem *i = selectedItem(); + if (i) i->setSelected(false); + } + canvas()->update(); + break; + } + case QEvent::RightButton: + { + break; + } + case QEvent::MidButton: + break; + default: + break; + } +} + +PertNodeItem *PertCanvas::selectedItem() +{ + QCanvasItemList list = canvas()->allItems(); + QCanvasItemList::Iterator it = list.begin(); + for (; it != list.end(); ++it) + { + if ( (*it)->isSelected() ) + { + if ( (*it)->rtti() == PertProjectItem::RTTI || + (*it)->rtti() == PertTaskItem::RTTI || + (*it)->rtti() == PertMilestoneItem::RTTI ) + return (PertNodeItem *)(*it); + } + } + return 0; +} + +Node *PertCanvas::selectedNode() { + return selectedItem() ? &(selectedItem()->node()) : 0; +} + +#ifndef NDEBUG +void PertCanvas::printDebug( int /*info*/ ) +{ +} +#endif + +} //KPlato namespace + +#include "kptpertcanvas.moc" diff --git a/kplato/kptpertcanvas.h b/kplato/kptpertcanvas.h new file mode 100644 index 00000000..8d2ae403 --- /dev/null +++ b/kplato/kptpertcanvas.h @@ -0,0 +1,105 @@ +/* This file is part of the KDE project + Copyright (C) 2003 - 2004 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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 KPTPERTCANVAS_H +#define KPTPERTCANVAS_H + +#include "kptnode.h" +#include "kptproject.h" + +#include <qcanvas.h> +#include <qmemarray.h> +#include <qptrdict.h> + +class QTimer; +class QPainter; +class QPoint; +class QSize; + +namespace KPlato +{ + +class PertNodeItem; + +class PertCanvas : public QCanvasView +{ + Q_OBJECT + +public: + PertCanvas( QWidget *parent ); + virtual ~PertCanvas(); + + void draw(Project& project); + void clear(); + QSize canvasSize(); + + PertNodeItem *selectedItem(); + + int verticalGap() { return m_verticalGap; } + int horizontalGap() { return m_horizontalGap; } + QSize itemSize() { return m_itemSize; } + + void setColumn(int row, int col) { m_rows.at(row)[col] = true; } + + void mapNode(PertNodeItem *item); + void mapChildNode(PertNodeItem *parentItem, PertNodeItem *childItem, Relation::Type type); + + Node *selectedNode(); + +protected: + void drawRelations(); + + void createChildItems(PertNodeItem *node); + PertNodeItem *createNodeItem(Node *node); + + void contentsMousePressEvent ( QMouseEvent * e ); + void contentsMouseReleaseEvent ( QMouseEvent * e ); + +signals: + void rightButtonPressed(Node *node, const QPoint & point); + void updateView(bool calculate); + void addRelation(Node *par, Node *child); + void modifyRelation(Relation *rel); + + +private: + QCanvas *m_canvas; + + QTimer *m_scrollTimer; + bool m_mousePressed; + bool m_printing; + + int m_verticalGap; + int m_horizontalGap; + QSize m_itemSize; + + QPtrDict<PertNodeItem> m_nodes; + QPtrList<Relation> m_relations; + + QPtrList<QMemArray<bool> > m_rows; + +#ifndef NDEBUG + void printDebug( int ); +#endif + +}; + +} //KPlato namespace + +#endif diff --git a/kplato/kptpertview.cc b/kplato/kptpertview.cc new file mode 100644 index 00000000..20932857 --- /dev/null +++ b/kplato/kptpertview.cc @@ -0,0 +1,140 @@ +/* This file is part of the KDE project + Copyright (C) 2003, 2004 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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. +*/ + +#include "kptpertview.h" + +#include "kptview.h" +#include "kptpertcanvas.h" +#include "kptpart.h" +#include "kptproject.h" +#include "kptrelation.h" +#include "kptcontext.h" + +#include <kdebug.h> + +#include <qsplitter.h> +#include <qvbox.h> +#include <qlayout.h> +#include <qlistview.h> +#include <qheader.h> +#include <qpopupmenu.h> + +#include <kprinter.h> + +namespace KPlato +{ + +PertView::PertView( View *view, QWidget *parent, QLayout *layout ) + : QWidget( parent, "Pert view" ), + m_mainview( view ), + m_node( 0 ) +{ + init(layout); +} + +PertView::~PertView() +{ +} + +void PertView::init(QLayout */*layout*/) +{ + //kdDebug()<<k_funcinfo<<endl; + QGridLayout *gl = new QGridLayout(this, 1, 1, -1, -1, "Pert QGridLayout"); + m_canvasview = new PertCanvas(this); + gl->addWidget(m_canvasview, 0, 0); + draw(); + connect(m_canvasview, SIGNAL(rightButtonPressed(Node *, const QPoint &)), this, SLOT(slotRMBPressed(Node *,const QPoint &))); + connect(m_canvasview, SIGNAL(updateView(bool)), m_mainview, SLOT(slotUpdate(bool))); + + connect(m_canvasview, SIGNAL(addRelation(Node*, Node*)), SLOT(slotAddRelation(Node*, Node*))); + connect(m_canvasview, SIGNAL(modifyRelation(Relation*)), SLOT(slotModifyRelation(Relation*))); +} + +void PertView::draw() +{ + //kdDebug()<<k_funcinfo<<endl; + m_canvasview->draw(m_mainview->getPart()->getProject()); + m_canvasview->show(); +} + +void PertView::slotRMBPressed(Node *node, const QPoint & point) +{ + //kdDebug()<<k_funcinfo<<" node: "<<node->name()<<endl; + m_node = node; + if (node && (node->type() == Node::Type_Task || node->type() == Node::Type_Milestone)) { + QPopupMenu *menu = m_mainview->popupMenu("task_popup"); + if (menu) + { + /*int id =*/ menu->exec(point); + //kdDebug()<<k_funcinfo<<"id="<<id<<endl; + } + return; + } + if (node && node->type() == Node::Type_Summarytask) { + QPopupMenu *menu = m_mainview->popupMenu("node_popup"); + if (menu) + { + /*int id =*/ menu->exec(point); + //kdDebug()<<k_funcinfo<<"id="<<id<<endl; + } + return; + } + //TODO: Other nodetypes +} + +void PertView::slotAddRelation(Node* par, Node* child) +{ + kdDebug()<<k_funcinfo<<endl; + emit addRelation(par, child); +} + +void PertView::slotModifyRelation(Relation *rel) +{ + kdDebug()<<k_funcinfo<<endl; + emit modifyRelation(rel); +} + +void PertView::print(KPrinter &printer) +{ + Q_UNUSED(printer); + kdDebug()<<k_funcinfo<<endl; + +} + +Node *PertView::currentNode() +{ + return m_canvasview->selectedNode(); +} + +bool PertView::setContext(Context::Pertview &context) +{ + Q_UNUSED(context); + //kdDebug()<<k_funcinfo<<endl; + return true; +} + +void PertView::getContext(Context::Pertview &context) const +{ + Q_UNUSED(context); + //kdDebug()<<k_funcinfo<<endl; +} + +} //KPlato namespace + +#include "kptpertview.moc" diff --git a/kplato/kptpertview.h b/kplato/kptpertview.h new file mode 100644 index 00000000..a51bb95d --- /dev/null +++ b/kplato/kptpertview.h @@ -0,0 +1,81 @@ +/* This file is part of the KDE project + Copyright (C) 2003, 2004 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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 KPTPERTVIEW_H +#define KPTPERTVIEW_H + +#include "kptcontext.h" + +#include <qsplitter.h> + +class QLayout; +class QListViewItem; + +class KPrinter; + +namespace KPlato +{ + +class View; +class PertCanvas; +class Node; +class Relation; + +class PertView : public QWidget +{ + Q_OBJECT + +public: + + PertView( View *view, QWidget *parent, QLayout *layout ); + + ~PertView(); + + void setZoom(double /*zoom*/) {} + + void draw(); + View *mainView() const { return m_mainview; } + + void print(KPrinter &printer); + + Node *currentNode(); + + virtual bool setContext(Context::Pertview &context); + virtual void getContext(Context::Pertview &context) const; + +public slots: + void slotRMBPressed(Node *node, const QPoint & point); + void slotAddRelation(Node *par, Node *child); + void slotModifyRelation(Relation *rel); + +signals: + void addRelation(Node *par, Node *child); + void modifyRelation(Relation *rel); + +private: + void init(QLayout *layout); + View *m_mainview; + PertCanvas *m_canvasview; + Node *m_node; + int m_defaultFontSize; +}; + +} //KPlato namespace + +#endif diff --git a/kplato/kptproject.cc b/kplato/kptproject.cc new file mode 100644 index 00000000..0451f34d --- /dev/null +++ b/kplato/kptproject.cc @@ -0,0 +1,1104 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Thomas zander <zander@kde.org> + Copyright (C) 2004 - 2006 Dag Andersen <danders@get2net.dk> + + 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. +*/ + +#include "kptproject.h" +#include "kptappointment.h" +#include "kpttask.h" +#include "kptprojectdialog.h" +#include "kptdatetime.h" +#include "kptpart.h" +#include "kptconfig.h" +#include "kpteffortcostmap.h" +#include "kptschedule.h" + +#include <qdom.h> +#include <qstring.h> +#include <qdatetime.h> +#include <qbrush.h> +#include <qcanvas.h> +#include <qptrlist.h> + +#include <kdatetimewidget.h> +#include <kdebug.h> + +namespace KPlato +{ + + +Project::Project(Node *parent) + : Node(parent), + m_accounts(*this), + m_baselined(false) { + //kdDebug()<<k_funcinfo<<"("<<this<<")"<<endl; + m_constraint = Node::MustStartOn; + m_standardWorktime = new StandardWorktime(); + m_schedules.setAutoDelete(true); + init(); +} + +void Project::init() { + if (m_parent == 0) { + // set sensible defaults for a project wo parent + m_constraintStartTime = QDateTime(QDate::currentDate(), QTime()); + m_constraintEndTime = m_constraintStartTime.addDays(1); + } + m_calendars.setAutoDelete(true); +} + + +Project::~Project() { + m_resourceGroups.setAutoDelete(true); + m_resourceGroups.clear(); + delete m_standardWorktime; +} + +int Project::type() const { return Node::Type_Project; } + +void Project::calculate(Schedule *schedule) { + if (schedule == 0) { + kdError()<<k_funcinfo<<"Schedule == 0, cannot calculate"<<endl; + return; + } + m_currentSchedule = schedule; + calculate(); +} + +void Project::calculate(Effort::Use estType) { + m_currentSchedule = findSchedule((Schedule::Type)estType); + if (m_currentSchedule == 0) { + m_currentSchedule = createSchedule(i18n("Standard"), (Schedule::Type)estType); + } + calculate(); +} + +void Project::calculate() { + if (m_currentSchedule == 0) { + kdError()<<k_funcinfo<<"No current schedule to calculate"<<endl; + return; + } + Effort::Use estType = (Effort::Use)m_currentSchedule->type(); + if (type() == Type_Project) { + initiateCalculation(*m_currentSchedule); + if (m_constraint == Node::MustStartOn) { + //kdDebug()<<k_funcinfo<<"Node="<<m_name<<" Start="<<m_constraintStartTime.toString()<<endl; + m_currentSchedule->startTime = m_constraintStartTime; + m_currentSchedule->earliestStart = m_constraintStartTime; + // Calculate from start time + propagateEarliestStart(m_currentSchedule->earliestStart); + m_currentSchedule->latestFinish = calculateForward(estType); + propagateLatestFinish(m_currentSchedule->latestFinish); + calculateBackward(estType); + m_currentSchedule->endTime = scheduleForward(m_currentSchedule->startTime, estType); + calcCriticalPath(false); + } else { + //kdDebug()<<k_funcinfo<<"Node="<<m_name<<" End="<<m_constraintEndTime.toString()<<endl; + m_currentSchedule->endTime = m_constraintEndTime; + m_currentSchedule->latestFinish = m_constraintEndTime; + // Calculate from end time + propagateLatestFinish(m_currentSchedule->latestFinish); + m_currentSchedule->earliestStart = calculateBackward(estType); + propagateEarliestStart(m_currentSchedule->earliestStart); + calculateForward(estType); + m_currentSchedule->startTime = scheduleBackward(m_currentSchedule->endTime, estType); + calcCriticalPath(true); + } + makeAppointments(); + calcResourceOverbooked(); + m_currentSchedule->notScheduled = false; + } else if (type() == Type_Subproject) { + kdWarning()<<k_funcinfo<<"Subprojects not implemented"<<endl; + } else { + kdError()<<k_funcinfo<<"Illegal project type: "<<type()<<endl; + } +} + +bool Project::calcCriticalPath(bool fromEnd) { + //kdDebug()<<k_funcinfo<<endl; + if (fromEnd) { + QPtrListIterator<Node> startnodes = m_startNodes; + for (; startnodes.current(); ++startnodes) { + startnodes.current()->calcCriticalPath(fromEnd); + } + } else { + QPtrListIterator<Node> endnodes = m_endNodes; + for (; endnodes.current(); ++endnodes) { + endnodes.current()->calcCriticalPath(fromEnd); + } + } + return false; +} + +DateTime Project::startTime() const { + //kdDebug()<<k_funcinfo<<(m_currentSchedule?m_currentSchedule->id():-1)<<" "<<(m_currentSchedule?m_currentSchedule->typeToString():"")<<endl; + if (m_currentSchedule) + return m_currentSchedule->startTime; + + return m_constraintStartTime; +} + +DateTime Project::endTime() const { + //kdDebug()<<k_funcinfo<<(m_currentSchedule?m_currentSchedule->id():-1)<<" "<<(m_currentSchedule?m_currentSchedule->typeToString():"")<<endl; + if (m_currentSchedule) + return m_currentSchedule->endTime; + + return m_constraintEndTime; +} + +Duration *Project::getExpectedDuration() { + //kdDebug()<<k_funcinfo<<endl; + return new Duration(getLatestFinish() - getEarliestStart()); +} + +Duration *Project::getRandomDuration() { + return 0L; +} + +DateTime Project::calculateForward(int use) { + //kdDebug()<<k_funcinfo<<m_name<<endl; + if (type() == Node::Type_Project) { + // Follow *parent* relations back and + // calculate forwards following the child relations + DateTime finish; + DateTime time; + QPtrListIterator<Node> endnodes = m_endNodes; + for (; endnodes.current(); ++endnodes) { + time = endnodes.current()->calculateForward(use); + if (!finish.isValid() || time > finish) + finish = time; + } + //kdDebug()<<k_funcinfo<<m_name<<" finish="<<finish.toString()<<endl; + return finish; + } else { + //TODO: subproject + } + return DateTime(); +} + +DateTime Project::calculateBackward(int use) { + //kdDebug()<<k_funcinfo<<m_name<<endl; + if (type() == Node::Type_Project) { + // Follow *child* relations back and + // calculate backwards following parent relation + DateTime start; + DateTime time; + QPtrListIterator<Node> startnodes = m_startNodes; + for (; startnodes.current(); ++startnodes) { + time = startnodes.current()->calculateBackward(use); + if (!start.isValid() || time < start) + start = time; + } + //kdDebug()<<k_funcinfo<<m_name<<" start="<<start.toString()<<endl; + return start; + } else { + //TODO: subproject + } + return DateTime(); +} + +DateTime Project::scheduleForward(const DateTime &earliest, int use) { + resetVisited(); + DateTime end = earliest; + DateTime time; + QPtrListIterator<Node> it(m_endNodes); + for (; it.current(); ++it) { + time = it.current()->scheduleForward(earliest, use); + if (time > end) + end = time; + } + // Fix summarytasks + adjustSummarytask(); + return end; +} + +DateTime Project::scheduleBackward(const DateTime &latest, int use) { + resetVisited(); + DateTime start = latest; + DateTime time; + QPtrListIterator<Node> it(m_startNodes); + for (; it.current(); ++it) { + time = it.current()->scheduleBackward(latest, use); + if (time < start) + start = time; + } + // Fix summarytasks + adjustSummarytask(); + return start; +} + +void Project::adjustSummarytask() { + QPtrListIterator<Node> it(m_summarytasks); + for (; it.current(); ++it) { + it.current()->adjustSummarytask(); + } +} + +void Project::initiateCalculation(Schedule &sch) { + //kdDebug()<<k_funcinfo<<m_name<<endl; + // clear all resource appointments + m_visitedForward = false; + m_visitedBackward = false; + QPtrListIterator<ResourceGroup> git(m_resourceGroups); + for ( ; git.current(); ++git ) { + git.current()->initiateCalculation(sch); + } + Node::initiateCalculation(sch); + m_startNodes.clear(); + m_endNodes.clear(); + m_summarytasks.clear(); + initiateCalculationLists(m_startNodes, m_endNodes, m_summarytasks); +} + +void Project::initiateCalculationLists(QPtrList<Node> &startnodes, QPtrList<Node> &endnodes, QPtrList<Node> &summarytasks) { + //kdDebug()<<k_funcinfo<<m_name<<endl; + if (type() == Node::Type_Project) { + QPtrListIterator<Node> it = childNodeIterator(); + for (; it.current(); ++it) { + it.current()->initiateCalculationLists(startnodes, endnodes, summarytasks); + } + } else { + //TODO: subproject + } +} + +bool Project::load(QDomElement &element) { + //kdDebug()<<k_funcinfo<<"--->"<<endl; + QString s; + bool ok = false; + QString id = element.attribute("id"); + if (!setId(id)) { + kdWarning()<<k_funcinfo<<"Id must be unique: "<<id<<endl; + } + m_name = element.attribute("name"); + m_leader = element.attribute("leader"); + m_description = element.attribute("description"); + + //m_baselined = (bool)element.attribute("baselined","0").toInt(&ok);FIXME: Removed for this release + + // 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) { + kdError()<<k_funcinfo<<"Illegal constraint: "<<constraintToString()<<endl; + setConstraint(Node::MustStartOn); + } + s = element.attribute("start-time"); + if (!s.isEmpty()) + m_constraintStartTime = DateTime::fromString(s); + s = element.attribute("end-time"); + if (!s.isEmpty()) + m_constraintEndTime = DateTime::fromString(s); + + // Load the project children + // Must do these first + QDomNodeList list = element.childNodes(); + for (unsigned int i=0; i<list.count(); ++i) { + if (list.item(i).isElement()) { + QDomElement e = list.item(i).toElement(); + if (e.tagName() == "calendar") { + // Load the calendar. + // References by resources + Calendar *child = new Calendar(); + child->setProject(this); + if (child->load(e)) { + addCalendar(child); + } else { + // TODO: Complain about this + kdError()<<k_funcinfo<<"Failed to load calendar"<<endl; + delete child; + } + } else if (e.tagName() == "standard-worktime") { + // Load standard worktime + StandardWorktime *child = new StandardWorktime(); + if (child->load(e)) { + setStandardWorktime(child); + } else { + kdError()<<k_funcinfo<<"Failed to load standard worktime"<<endl; + delete child; + } + } + } + } + for (unsigned int i=0; i<list.count(); ++i) { + if (list.item(i).isElement()) { + QDomElement e = list.item(i).toElement(); + + if (e.tagName() == "resource-group") { + // Load the resources + // References calendars + ResourceGroup *child = new ResourceGroup(this); + if (child->load(e)) { + addResourceGroup(child); + } else { + // TODO: Complain about this + delete child; + } + } + } + } + for (unsigned int i=0; i<list.count(); ++i) { + if (list.item(i).isElement()) { + QDomElement e = list.item(i).toElement(); + + if (e.tagName() == "project") { + //kdDebug()<<k_funcinfo<<"Sub project--->"<<endl; + // 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") { + //kdDebug()<<k_funcinfo<<"Task--->"<<endl; + // Load the task (and resourcerequests). + // Depends on resources already loaded + Task *child = new Task(this); + if (child->load(e, *this)) { + if (!addTask(child, this)) { + delete child; // TODO: Complain about this + } + } else { + // TODO: Complain about this + delete child; + } + } + } + } + // These go last + for (unsigned int i=0; i<list.count(); ++i) { + if (list.item(i).isElement()) { + QDomElement e = list.item(i).toElement(); + if (e.tagName() == "accounts") { + //kdDebug()<<k_funcinfo<<"Accounts--->"<<endl; + // Load accounts + // References tasks + if (!m_accounts.load(e, *this)) { + kdError()<<k_funcinfo<<"Failed to load accounts"<<endl; + } + } else if (e.tagName() == "relation") { + //kdDebug()<<k_funcinfo<<"Relation--->"<<endl; + // Load the relation + // References tasks + Relation *child = new Relation(); + if (!child->load(e, *this)) { + // TODO: Complain about this + kdError()<<k_funcinfo<<"Failed to load relation"<<endl; + delete child; + } + //kdDebug()<<k_funcinfo<<"Relation<---"<<endl; + } else if (e.tagName() == "schedules") { + //kdDebug()<<k_funcinfo<<"Project schedules & task appointments--->"<<endl; + // Prepare for multiple schedules + // References tasks and resources + QDomNodeList lst = e.childNodes(); + for (unsigned int i=0; i<lst.count(); ++i) { + if (lst.item(i).isElement()) { + QDomElement el = lst.item(i).toElement(); + if (el.tagName() == "schedule") { + MainSchedule *sch = new MainSchedule(); + if (sch->loadXML(el, *this)) { + addSchedule(sch); + sch->setNode(this); + setParentSchedule(sch); + // If it's here, it's scheduled! + sch->setScheduled(true); + } else { + kdError()<<k_funcinfo<<"Failed to load schedule"<<endl; + delete sch; + } + } + } + } + //kdDebug()<<k_funcinfo<<"Node schedules<---"<<endl; + } + } + } + //kdDebug()<<k_funcinfo<<"Calendars--->"<<endl; + // calendars references calendars in arbritary saved order + QPtrListIterator<Calendar> calit(m_calendars); + for (; calit.current(); ++calit) { + if (calit.current()->id() == calit.current()->parentId()) { + kdError()<<k_funcinfo<<"Calendar want itself as parent"<<endl; + continue; + } + calit.current()->setParent(calendar(calit.current()->parentId())); + } + //kdDebug()<<k_funcinfo<<"Project schedules--->"<<endl; + QIntDictIterator<Schedule> it = m_schedules; + if (it.current()) { + if (m_constraint == Node::MustFinishOn) + m_constraintEndTime = it.current()->endTime; + else + m_constraintStartTime = it.current()->startTime; + } + //kdDebug()<<k_funcinfo<<"Project schedules<---"<<endl; + //kdDebug()<<k_funcinfo<<"<---"<<endl; + return true; +} + +void Project::save(QDomElement &element) 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("baselined",(int)m_baselined); FIXME: Removed for this release + + me.setAttribute("scheduling",constraintToString()); + me.setAttribute("start-time", m_constraintStartTime.toString(Qt::ISODate)); + me.setAttribute("end-time", m_constraintEndTime.toString(Qt::ISODate)); + + m_accounts.save(me); + + // save calendars + QPtrListIterator<Calendar> calit(m_calendars); + for (; calit.current(); ++calit) { + calit.current()->save(me); + } + // save standard worktime + if (m_standardWorktime) + m_standardWorktime->save(me); + + // save project resources, must be after calendars + QPtrListIterator<ResourceGroup> git(m_resourceGroups); + for ( ; git.current(); ++git ) { + git.current()->save(me); + } + + // Only save parent relations + QPtrListIterator<Relation> it(m_dependParentNodes); + for ( ; it.current(); ++it ) { + it.current()->save(me); + } + + for (int i=0; i<numChildren(); i++) + // Save all children + getChildNode(i)->save(me); + + // Now we can save relations assuming no tasks have relations outside the project + QPtrListIterator<Node> nodes(m_nodes); + for ( ; nodes.current(); ++nodes ) { + nodes.current()->saveRelations(me); + } + + if (!m_schedules.isEmpty()) { + QDomElement el = me.ownerDocument().createElement("schedules"); + me.appendChild(el); + QIntDictIterator<Schedule> it = m_schedules; + for (; it.current(); ++it) { + if (!it.current()->isDeleted() && it.current()->isScheduled()) { + QDomElement schs = el.ownerDocument().createElement("schedule"); + el.appendChild(schs); + it.current()->saveXML(schs); + //kdDebug()<<k_funcinfo<<m_name<<" id="<<it.current()->id()<<(it.current()->isDeleted()?" Deleted":"")<<endl; + Node::saveAppointments(schs, it.current()->id()); + } + } + } +} + +void Project::setParentSchedule(Schedule *sch) { + QPtrListIterator<Node> it = m_nodes; + for (; it.current(); ++it) { + it.current()->setParentSchedule(sch); + } +} + +void Project::addResourceGroup(ResourceGroup * group) { + m_resourceGroups.append(group); +} + + +void Project::removeResourceGroup(ResourceGroup * group){ + m_resourceGroups.remove(group); +} + + +void Project::removeResourceGroup(int /* number */){ + // always auto remove +} + + +void Project::insertResourceGroup( unsigned int /* index */, + ResourceGroup * /* resource */) { +} + +QPtrList<ResourceGroup> &Project::resourceGroups() { + return m_resourceGroups; +} + +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 ) { + kdError()<<k_funcinfo<<"position=0, could not add task: "<<task->name()<<endl; + return false; + } + //kdDebug()<<k_funcinfo<<"Add "<<task->name()<<" after "<<position->name()<<endl; + // 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->getParent(); + if ( !parentNode ) { + kdDebug()<<k_funcinfo<<"parent node not found???"<<endl; + return false; + } + int index = parentNode->findChildNode( position ); + if ( -1 == index ) { + // ok, it does not exist + kdDebug()<<k_funcinfo<<"Task not found???"<<endl; + return false; + } + return addSubTask(task, index+1, parentNode); +} + +bool Project::addSubTask( Node* task, Node* position ) +{ + // we want to add a subtask to the node "position". It will become + // position's last child. + if ( 0 == position ) { + kdError()<<k_funcinfo<<"No parent, can not add subtask: "<<task->name()<<endl; + return false; + } + if (!registerNodeId(task)) { + kdError()<<k_funcinfo<<"Failed to register node id, can not add subtask: "<<task->name()<<endl; + return false; + } + position->addChildNode(task); + return true; +} + +bool Project::addSubTask( Node* task, int index, Node* parent ) +{ + // we want to add a subtask to the node "parent" at the given index. + if ( 0 == parent ) { + kdError()<<k_funcinfo<<"No parent, can not add subtask: "<<task->name()<<endl; + return false; + } + if (!registerNodeId(task)) { + kdError()<<k_funcinfo<<"Failed to register node id, can not add subtask: "<<task->name()<<endl; + return false; + } + parent->insertChildNode(index, task); + return true; +} + +void Project::delTask(Node *node) +{ + Node *parent = node->getParent(); + if (parent == 0) { + kdDebug()<<k_funcinfo<<"Node must have a parent!"<<endl; + return; + } + removeId(node->id()); + parent->delChildNode(node, false/*take*/); +} + +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) { + //kdDebug()<<k_funcinfo<<"The root node cannot be indented"<<endl; + return false; + } + // we have to find the parent of task to manipulate its list of children + Node* parentNode = node->getParent(); + if ( !parentNode ) { + return false; + } + if (parentNode->findChildNode(node) == -1) { + kdError()<<k_funcinfo<<"Tasknot found???"<<endl; + return false; + } + Node *sib = node->siblingBefore(); + if (!sib) { + //kdDebug()<<k_funcinfo<<"new parent node not found"<<endl; + return false; + } + if (node->findParentRelation(sib) || node->findChildRelation(sib)) { + //kdDebug()<<k_funcinfo<<"Cannot have relations to parent"<<endl; + return false; + } + return true; +} + +bool Project::indentTask( Node* node ) +{ + if (canIndentTask(node)) { + Node *newParent = node->siblingBefore(); + node->getParent()->delChildNode(node, false/*do not delete objekt*/); + newParent->addChildNode(node); + 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() ) { + //kdDebug()<<k_funcinfo<<"The root node cannot be unindented"<<endl; + 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->getParent(); + if ( !parentNode ) { + return false; + } + Node* grandParentNode = parentNode->getParent(); + if ( !grandParentNode ) { + //kdDebug()<<k_funcinfo<<"This node already is at the top level"<<endl; + return false; + } + int index = parentNode->findChildNode( node ); + if ( -1 == index ) { + kdError()<<k_funcinfo<<"Tasknot found???"<<endl; + return false; + } + return true; +} + +bool Project::unindentTask( Node* node ) +{ + if (canUnindentTask(node)) { + Node *parentNode = node->getParent(); + Node *grandParentNode = parentNode->getParent(); + parentNode->delChildNode(node, false/*do not delete objekt*/); + grandParentNode->addChildNode(node,parentNode); + 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->getParent(); + if (!parentNode) { + //kdDebug()<<k_funcinfo<<"No parent found"<<endl; + return false; + } + if (parentNode->findChildNode(node) == -1) { + kdError()<<k_funcinfo<<"Tasknot found???"<<endl; + return false; + } + if (node->siblingBefore()) { + return true; + } + return false; +} + +bool Project::moveTaskUp( Node* node ) +{ + if (canMoveTaskUp(node)) { + return node->getParent()->moveChildUp(node); + } + 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->getParent(); + if (!parentNode) { + return false; + } + if (parentNode->findChildNode(node) == -1) { + kdError()<<k_funcinfo<<"Tasknot found???"<<endl; + return false; + } + if (node->siblingAfter()) { + return true; + } + return false; +} + +bool Project::moveTaskDown( Node* node ) +{ + if (canMoveTaskDown(node)) { + return node->getParent()->moveChildDown(node); + } + return false; +} + +Task *Project::createTask(Node* parent) { + Task* node = new Task(parent); + node->setId(uniqueNodeId()); + return node; +} + +Task *Project::createTask(Task &def, Node* parent) { + Task* node = new Task(def, parent); + node->setId(uniqueNodeId()); + return node; +} + +QString Project::uniqueNodeId(int seed) { + int i = seed; + while (findNode(QString("%1").arg(i))) { + ++i; + } + return QString("%1").arg(i); +} + +bool Project::removeId(const QString &id) { + kdDebug()<<k_funcinfo<<"id="<<id<<endl; + return (m_parent ? m_parent->removeId(id) : nodeIdDict.remove(id)); +} + +void Project::insertId(const QString &id, const Node *node) { + kdDebug()<<k_funcinfo<<"id="<<id<<" "<<node->name()<<endl; + m_parent ? m_parent->insertId(id, node) : nodeIdDict.insert(id, node); +} + +bool Project::registerNodeId(Node *node) { + if (node->id().isEmpty()) { + kdError()<<k_funcinfo<<"Id is empty."<<endl; + return false; + } + Node *rn = findNode(node->id()); + if (rn == 0) { + insertId(node->id(), node); + return true; + } + if (rn != node) { + kdError()<<k_funcinfo<<"Id allready exists for different task: "<<node->id()<<endl; + return false; + } + return true; +} + + +ResourceGroup *Project::group(QString id) { + return findResourceGroup(id); +} + +Resource *Project::resource(QString id) { + return findResource(id); +} + +// TODO +EffortCostMap Project::plannedEffortCostPrDay(const QDate &/*start*/, const QDate &/*end*/) const { + //kdDebug()<<k_funcinfo<<endl; + EffortCostMap ec; + return ec; + +} + +// Returns the total planned effort for this project (or subproject) +Duration Project::plannedEffort() { + //kdDebug()<<k_funcinfo<<endl; + Duration eff; + QPtrListIterator<Node> it(childNodeIterator()); + for (; it.current(); ++it) { + eff += it.current()->plannedEffort(); + } + return eff; +} + +// Returns the total planned effort for this project (or subproject) on date +Duration Project::plannedEffort(const QDate &date) { + //kdDebug()<<k_funcinfo<<endl; + Duration eff; + QPtrListIterator<Node> it(childNodeIterator()); + for (; it.current(); ++it) { + eff += it.current()->plannedEffort(date); + } + return eff; +} + +// Returns the total planned effort for this project (or subproject) upto and including date +Duration Project::plannedEffortTo(const QDate &date) { + //kdDebug()<<k_funcinfo<<endl; + Duration eff; + QPtrListIterator<Node> it(childNodeIterator()); + for (; it.current(); ++it) { + eff += it.current()->plannedEffortTo(date); + } + return eff; +} + +// Returns the total actual effort for this project (or subproject) +Duration Project::actualEffort() { + //kdDebug()<<k_funcinfo<<endl; + Duration eff; + QPtrListIterator<Node> it(childNodeIterator()); + for (; it.current(); ++it) { + eff += it.current()->actualEffort(); + } + return eff; +} + +// Returns the total actual effort for this project (or subproject) on date +Duration Project::actualEffort(const QDate &date) { + //kdDebug()<<k_funcinfo<<endl; + Duration eff; + QPtrListIterator<Node> it(childNodeIterator()); + for (; it.current(); ++it) { + eff += it.current()->actualEffort(date); + } + return eff; +} + +// Returns the total actual effort for this project (or subproject) upto and including date +Duration Project::actualEffortTo(const QDate &date) { + //kdDebug()<<k_funcinfo<<endl; + Duration eff; + QPtrListIterator<Node> it(childNodeIterator()); + for (; it.current(); ++it) { + eff += it.current()->actualEffortTo(date); + } + return eff; +} + +double Project::plannedCost() { + //kdDebug()<<k_funcinfo<<endl; + double c = 0; + QPtrListIterator<Node> it(childNodeIterator()); + for (; it.current(); ++it) { + c += it.current()->plannedCost(); + } + return c; +} + +// Returns the total planned effort for this project (or subproject) on date +double Project::plannedCost(const QDate &date) { + //kdDebug()<<k_funcinfo<<endl; + double c = 0; + QPtrListIterator<Node> it(childNodeIterator()); + for (; it.current(); ++it) { + c += it.current()->plannedCost(date); + } + return c; +} + +// Returns the total planned effort for this project (or subproject) upto and including date +double Project::plannedCostTo(const QDate &date) { + //kdDebug()<<k_funcinfo<<endl; + double c = 0; + QPtrListIterator<Node> it(childNodeIterator()); + for (; it.current(); ++it) { + c += it.current()->plannedCostTo(date); + } + return c; +} + +double Project::actualCost() { + //kdDebug()<<k_funcinfo<<endl; + double c = 0; + QPtrListIterator<Node> it(childNodeIterator()); + for (; it.current(); ++it) { + c += it.current()->actualCost(); + } + return c; +} + +// Returns the total planned effort for this project (or subproject) on date +double Project::actualCost(const QDate &date) { + //kdDebug()<<k_funcinfo<<endl; + double c = 0; + QPtrListIterator<Node> it(childNodeIterator()); + for (; it.current(); ++it) { + c += it.current()->actualCost(date); + } + return c; +} + +// Returns the total planned effort for this project (or subproject) upto and including date +double Project::actualCostTo(const QDate &date) { + //kdDebug()<<k_funcinfo<<endl; + double c = 0; + QPtrListIterator<Node> it(childNodeIterator()); + for (; it.current(); ++it) { + c += it.current()->actualCostTo(date); + } + return c; +} + +void Project::addCalendar(Calendar *calendar) { + //kdDebug()<<k_funcinfo<<calendar->name()<<endl; + m_calendars.append(calendar); +} + +Calendar *Project::calendar(const QString id) const { + return findCalendar(id); +} + +QPtrList<Calendar> Project::calendars() { + QPtrList<Calendar> list; + QPtrListIterator<Calendar> it = m_calendars; + for (; it.current(); ++it) { + if (!it.current()->isDeleted()) { + list.append(it.current()); + } + } + return list; +} + +void Project::setStandardWorktime(StandardWorktime * worktime) { + if (m_standardWorktime != worktime) { + delete m_standardWorktime; + m_standardWorktime = worktime; + } +} + +bool Project::legalToLink(Node *par, Node *child) { + //kdDebug()<<k_funcinfo<<par.name()<<" ("<<par.numDependParentNodes()<<" parents) "<<child.name()<<" ("<<child.numDependChildNodes()<<" children)"<<endl; + + if (!child || par->isDependChildOf(child)) { + return false; + } + bool legal = true; + // see if par/child is related + if (par->isParentOf(child) || child->isParentOf(par)) { + legal = false; + } + if (legal) + legal = legalChildren(par, child); + if (legal) + legal = legalParents(par, child); + + return legal; +} + +bool Project::legalParents(Node *par, Node *child) { + bool legal = true; + //kdDebug()<<k_funcinfo<<par->name()<<" ("<<par->numDependParentNodes()<<" parents) "<<child->name()<<" ("<<child->numDependChildNodes()<<" children)"<<endl; + for (int i=0; i < par->numDependParentNodes() && legal; ++i) { + Node *pNode = par->getDependParentNode(i)->parent(); + if (child->isParentOf(pNode) || pNode->isParentOf(child)) { + //kdDebug()<<k_funcinfo<<"Found: "<<pNode->name()<<" is related to "<<child->name()<<endl; + legal = false; + } else { + legal = legalChildren(pNode, child); + } + if (legal) + legal = legalParents(pNode, child); + } + return legal; +} + +bool Project::legalChildren(Node *par, Node *child) { + bool legal = true; + //kdDebug()<<k_funcinfo<<par->name()<<" ("<<par->numDependParentNodes()<<" parents) "<<child->name()<<" ("<<child->numDependChildNodes()<<" children)"<<endl; + for (int j=0; j < child->numDependChildNodes() && legal; ++j) { + Node *cNode = child->getDependChildNode(j)->child(); + if (par->isParentOf(cNode) || cNode->isParentOf(par)) { + //kdDebug()<<k_funcinfo<<"Found: "<<par->name()<<" is related to "<<cNode->name()<<endl; + legal = false; + } else { + legal = legalChildren(par, cNode); + } + } + return legal; +} + +void Project::generateWBS(int count, WBSDefinition &def, QString wbs) { + if (type() == Type_Subproject || def.level0Enabled()) { + Node::generateWBS(count, def, wbs); + } else { + QPtrListIterator<Node> it = m_nodes; + for (int i=0; it.current(); ++it) { + it.current()->generateWBS(++i, def, m_wbs); + } + } +} + +void Project::setCurrentSchedule(long id) { + setCurrentSchedulePtr(findSchedule(id)); + Node::setCurrentSchedule(id); + QDictIterator<Resource> it = resourceIdDict; + for (; it.current(); ++it) { + it.current()->setCurrentSchedule(id); + } +} + +MainSchedule *Project::createSchedule(QString name, Schedule::Type type) { + //kdDebug()<<k_funcinfo<<"No of schedules: "<<m_schedules.count()<<endl; + long i=1; + while (m_schedules.find(i)) { + ++i; + } + MainSchedule *sch = new MainSchedule(this, name, type, i); + addSchedule(sch); + return sch; +} + +bool Project::removeCalendarId(const QString &id) { + kdDebug()<<k_funcinfo<<"id="<<id<<endl; + return calendarIdDict.remove(id); +} + +void Project::insertCalendarId(const QString &id, const Calendar *calendar) { + kdDebug()<<k_funcinfo<<"id="<<id<<": "<<calendar->name()<<endl; + calendarIdDict.insert(id, calendar); +} + +#ifndef NDEBUG +void Project::printDebug(bool children, QCString indent) { + + kdDebug()<<indent<<"+ Project node: "<<name()<<endl; + indent += "!"; + QPtrListIterator<ResourceGroup> it(resourceGroups()); + for ( ; it.current(); ++it) + it.current()->printDebug(indent); + + Node::printDebug(children, indent); +} +void Project::printCalendarDebug(QCString indent) { + kdDebug()<<indent<<"-------- Calendars debug printout --------"<<endl; + QPtrListIterator<Calendar> it = m_calendars; + for (; it.current(); ++it) { + it.current()->printDebug(indent + "--"); + kdDebug()<<endl; + } + if (m_standardWorktime) + m_standardWorktime->printDebug(); +} +#endif + +} //KPlato namespace diff --git a/kplato/kptproject.h b/kplato/kptproject.h new file mode 100644 index 00000000..93953745 --- /dev/null +++ b/kplato/kptproject.h @@ -0,0 +1,281 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Thomas Zander zander@kde.org + Copyright (C) 2004 - 2006 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef KPTPROJECT_H +#define KPTPROJECT_H + +#include "kptnode.h" + +#include "kptaccount.h" +#include "kptcalendar.h" +#include "kptdatetime.h" +#include "kptduration.h" +#include "kptresource.h" + +#include <qmap.h> +#include <qptrlist.h> +#include <qdict.h> + +#include <klistview.h> +#include <klocale.h> + +namespace KPlato +{ + +class Part; +class Schedule; +class StandardWorktime; + +//#define DEBUGPERT +/** + * Project is the main node in a project, it contains child nodes and + * possibly sub-projects. A sub-project is just another instantion of this + * node however. + */ +class Project : public Node { +public: + Project(Node *parent = 0); + ~Project(); + + /// Returns the node type. Can be Type_Project or Type_Subproject. + virtual int type() const; + + /** + * Calculate the whole project. + * + * @param schedule Schedule to use + */ + void calculate(Schedule *scedule); + /** + * Calculate the whole project. + * + * @param use Calculate using expected-, optimistic- or pessimistic estimate. + */ + void calculate(Effort::Use use); + /// Calculate current schedule + void calculate(); + + virtual bool calcCriticalPath(bool fromEnd); + + virtual DateTime startTime() const; + virtual DateTime endTime() const; + + /// Returns the duration calculated as latestFinish - earliestStart. + Duration *getExpectedDuration(); + + /** + * Instead of using the expected duration, generate a random value using + * the Distribution of each Task. This can be used for Monte-Carlo + * estimation of Project duration. + */ + Duration *getRandomDuration(); + + virtual bool load(QDomElement &element); + virtual void save(QDomElement &element) const; + + QPtrList<ResourceGroup> &resourceGroups(); + virtual void addResourceGroup(ResourceGroup *resource); + virtual void insertResourceGroup(unsigned int index, ResourceGroup *resource); + void removeResourceGroup(ResourceGroup *resource); + void removeResourceGroup(int number); + ResourceGroup *takeResourceGroup(ResourceGroup *resource) + { return m_resourceGroups.take(m_resourceGroups.findRef(resource)); } + + bool addTask( Node* task, Node* position ); + bool addSubTask( Node* task, Node* position ); + bool addSubTask( Node* task, int index, Node* parent ); + void delTask(Node *node); + bool canIndentTask(Node* node); + bool indentTask( Node* node ); + bool canUnindentTask( Node* node ); + bool unindentTask( Node* node ); + bool canMoveTaskUp( Node* node ); + bool moveTaskUp( Node* node ); + bool canMoveTaskDown( Node* node ); + bool moveTaskDown( Node* node ); + Task *createTask(Node* parent); + Task *createTask(Task &def, Node* parent); + + /// Returns the resourcegroup with identity id. + ResourceGroup *group(QString id); + /// Returns the resource with identity id. + Resource *resource(QString id); + + virtual EffortCostMap plannedEffortCostPrDay(const QDate &start, const QDate &end) const; + + /// Returns the total planned effort for this project (or subproject) + virtual Duration plannedEffort(); + /// Returns the total planned effort for this project (or subproject) on date + virtual Duration plannedEffort(const QDate &date); + /// Returns the planned effort up to and including date + virtual Duration plannedEffortTo(const QDate &date); + + /// Returns the actual effort + virtual Duration actualEffort(); + /// Returns the actual effort on date + virtual Duration actualEffort(const QDate &date); + /// Returns the actual effort up to and including date + virtual Duration actualEffortTo(const QDate &date); + /** + * Returns the total planned cost for this project + */ + virtual double plannedCost(); + /// Planned cost on date + virtual double plannedCost(const QDate &date); + /// Planned cost up to and including date + virtual double plannedCostTo(const QDate &date); + + /** + * Returns the actually reported cost for this project + */ + virtual double actualCost(); + /// Actual cost on date + virtual double actualCost(const QDate &date); + /// Actual cost up to and including date + virtual double actualCostTo(const QDate &date); + + Calendar *defaultCalendar() { return m_standardWorktime->calendar(); } + QPtrList<Calendar> calendars(); + void addCalendar(Calendar *calendar); + /// Returns the calendar with identity id. + Calendar *calendar(const QString id) const; + + /** + * Defines the length of days, weeks, months and years + * and the standard working week. + * Used for estimation and calculation of effort, + * and presentation in gantt chart. + */ + StandardWorktime *standardWorktime() { return m_standardWorktime; } + void setStandardWorktime(StandardWorktime * worktime); + + /// Check if node par can be linked to node child. + bool legalToLink(Node *par, Node *child); + + virtual const QDict<Node> &nodeDict() { return nodeIdDict; } + + /// Find the node with identity id + virtual Node *findNode(const QString &id) const + { return (m_parent ? m_parent->findNode(id) : nodeIdDict.find(id)); } + /// Remove the node with identity id from the register + virtual bool removeId(const QString &id); + /// Insert the node with identity id + virtual void insertId(const QString &id, const Node *node); + /// Register node. The nodes id must be unique and non-empty. + bool registerNodeId(Node *node); + /// Create a unique id. + QString uniqueNodeId(int seed=1); + + ResourceGroup *findResourceGroup(const QString &id) const + { return resourceGroupIdDict.find(id); } + /// Remove the resourcegroup with identity id from the register + bool removeResourceGroupId(const QString &id) + { return resourceGroupIdDict.remove(id); } + /// Insert the resourcegroup with identity id + void insertResourceGroupId(const QString &id, const ResourceGroup* group) + { resourceGroupIdDict.insert(id, group); } + + Resource *findResource(const QString &id) const + { return resourceIdDict.find(id); } + /// Remove the resource with identity id from the register + bool removeResourceId(const QString &id) + { return resourceIdDict.remove(id); } + /// Insert the resource with identity id + void insertResourceId(const QString &id, const Resource *resource) + { resourceIdDict.insert(id, resource); } + + /// Find the calendar with identity id + virtual Calendar *findCalendar(const QString &id) const + { return id.isEmpty() ? 0 : calendarIdDict.find(id); } + /// Remove the calendar with identity id from the register + virtual bool removeCalendarId(const QString &id); + /// Insert the calendar with identity id + virtual void insertCalendarId(const QString &id, const Calendar *calendar); + + /** + * Setting a project to be baselined means the project data can not be edited anymore. + * @param on the new baseline value + */ + void setBaselined(bool on) { m_baselined = on; } + /** + * @return if the project is baselined; a baselined project becomes uneditable. + */ + bool isBaselined() const { return m_baselined; } + + void generateWBS(int count, WBSDefinition &def, QString wbs=QString()); + + Accounts &accounts() { return m_accounts; } + + /// Set current schedule to schedule with identity id, for me and my children + virtual void setCurrentSchedule(long id); + /// Create new schedule with unique id. + MainSchedule *createSchedule(QString name, Schedule::Type type); + /// Set parent schedule for my children + virtual void setParentSchedule(Schedule *sch); + +protected: + Accounts m_accounts; + QPtrList<ResourceGroup> m_resourceGroups; + + QPtrList<Calendar> m_calendars; + + StandardWorktime *m_standardWorktime; + + DateTime calculateForward(int use); + DateTime calculateBackward(int use); + DateTime scheduleForward(const DateTime &earliest, int use); + DateTime scheduleBackward(const DateTime &latest, int use); + void adjustSummarytask(); + + void initiateCalculation(Schedule &sch); + void initiateCalculationLists(QPtrList<Node> &startnodes, QPtrList<Node> &endnodes, QPtrList<Node> &summarytasks); + + bool legalParents(Node *par, Node *child); + bool legalChildren(Node *par, Node *child); + +private: + void init(); + + QPtrList<Node> m_startNodes; + QPtrList<Node> m_endNodes; + QPtrList<Node> m_summarytasks; + + bool m_baselined; + + QDict<ResourceGroup> resourceGroupIdDict; + QDict<Resource> resourceIdDict; + QDict<Node> nodeIdDict; + QDict<Calendar> calendarIdDict; + +#ifndef NDEBUG +#include <qcstring.h> +public: + void printDebug(bool children, QCString indent); + void printCalendarDebug(QCString indent=""); +#ifdef DEBUGPERT + static void pert_test(); + static void printTree(Node *n, QString s); +#endif +#endif +}; + +} //KPlato namespace + +#endif diff --git a/kplato/kptprojectdialog.cc b/kplato/kptprojectdialog.cc new file mode 100644 index 00000000..6fe79067 --- /dev/null +++ b/kplato/kptprojectdialog.cc @@ -0,0 +1,145 @@ +/* This file is part of the KDE project + Copyright (C) 2002 Bo Thorsen bo@sonofthor.dk + + 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. +*/ + +#include <qpushbutton.h> +#include <qcombobox.h> +#include <qlabel.h> +#include <qtextedit.h> +#include <qlineedit.h> +#include <qdatetimeedit.h> +#include <qdatetime.h> +#include <qtabwidget.h> +#include <qtextbrowser.h> + +#include <kdatepicker.h> +#include <klocale.h> + +#include <kabc/addressee.h> +#include <kabc/addresseedialog.h> + +#include <kdebug.h> + +#include "kptprojectdialog.h" +#include "kptproject.h" +#include "kptresource.h" +#include "kptprojectdialogbase.h" +#include "kptresourcespanel.h" + +namespace KPlato +{ + +ProjectDialog::ProjectDialog(Project &p, QWidget *parent, const char *name) + : KDialogBase( Swallow, i18n("Project Settings"), Ok|Cancel, Ok, parent, name, true, true), + project(p) +{ + dia = new ProjectDialogImpl(this); + resourcesTab = new ResourcesPanel(dia, &project); + dia->daTabs->insertTab(resourcesTab, i18n("Resources"), 1); + setMainWidget(dia); + enableButtonOK(false); + + dia->namefield->setText(project.name()); + dia->leaderfield->setText(project.leader()); + + connect(dia, SIGNAL( obligatedFieldsFilled(bool) ), this, SLOT( enableButtonOK(bool) )); + connect(dia, SIGNAL( schedulingTypeChanged(int) ), this, SLOT( slotSchedulingChanged(int) )); + + slotSchedulingChanged(dia->schedulerType->currentItem()); + dia->namefield->setFocus(); + + connect(resourcesTab, SIGNAL( changed() ), dia, SLOT( slotCheckAllFieldsFilled() )); +} + + +void ProjectDialog::slotOk() { + project.setConstraint((Node::ConstraintType) dia->schedulerType->currentItem()); + //FIXME + project.setStartTime(QDateTime(dia->schedulerDate->date(), dia->schedulerTime->time())); + project.setConstraintStartTime(QDateTime(dia->schedulerDate->date(), dia->schedulerTime->time())); + + project.setName(dia->namefield->text()); + project.setLeader(dia->leaderfield->text()); + project.setDescription(dia->descriptionfield->text()); + + resourcesTab->ok(); + + accept(); +} + +void ProjectDialog::slotSchedulingChanged(int activated) { + bool needDate = activated >= 2; + dia->schedulerTime->setEnabled(needDate); + dia->schedulerDate->setEnabled(needDate); + + QString label = QString("<p><font size=\"4\" color=\"#7797BC\"><b>%1</b></font></p><p>%2</p>"); + switch(activated) { + // TODO please provide nice explenations on this. + case 0: // ASAP + label = label.arg(i18n("As Soon as Possible")); + label = label.arg(i18n("Place all events at the earliest possible moment permitted in the schedule")); + break; + case 1: // ALAP + label = label.arg(i18n("As Late as Possible")); + label = label.arg(i18n("Place all events at the last possible moment permitted in the schedule")); + break; + case 2: // Start not earlier then + label = label.arg(i18n("Start not Earlier then")); + label = label.arg(i18n("")); + break; + case 3: // Finish not later then + label = label.arg(i18n("Finish not Later then")); + label = label.arg(i18n("")); + break; + case 4: // Must start on + label = label.arg(i18n("Must Start on")); + label = label.arg(i18n("")); + break; + default: // error ... + dia->lSchedulingExplain->setText(""); + return; + } + dia->lSchedulingExplain->setText(label); +} + +ProjectDialogImpl::ProjectDialogImpl (QWidget *parent) : ProjectDialogBase(parent) { + connect (namefield, SIGNAL(textChanged( const QString& )), this, SLOT(slotCheckAllFieldsFilled()) ); + connect (leaderfield, SIGNAL(textChanged( const QString& )), this, SLOT(slotCheckAllFieldsFilled()) ); + connect (schedulerType, SIGNAL(activated( int )), this, SLOT(slotSchedulingChanged( int )) ); + connect (chooseLeader, SIGNAL(pressed()), this, SLOT(slotChooseLeader())); +} + +void ProjectDialogImpl::slotCheckAllFieldsFilled() { + emit obligatedFieldsFilled( !(namefield->text().isEmpty() || leaderfield->text().isEmpty())); +} + +void ProjectDialogImpl::slotSchedulingChanged(int activated) { + emit schedulingTypeChanged(activated); +} + +void ProjectDialogImpl::slotChooseLeader() +{ + KABC::Addressee a = KABC::AddresseeDialog::getAddressee(this); + if (!a.isEmpty()) { + leaderfield->setText(a.fullEmail()); + } +} + +} //KPlato namespace + +#include "kptprojectdialog.moc" diff --git a/kplato/kptprojectdialog.h b/kplato/kptprojectdialog.h new file mode 100644 index 00000000..4e14a529 --- /dev/null +++ b/kplato/kptprojectdialog.h @@ -0,0 +1,72 @@ +/* This file is part of the KDE project + Copyright (C) 2002 Bo Thorsen bo@sonofthor.dk + + 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 KPTPROJECTDIALOG_H +#define KPTPROJECTDIALOG_H + +#include "kptresource.h" +#include "kptprojectdialogbase.h" +//#include "kptresourcespanel.h" + +#include <kdialogbase.h> + +#include <qstring.h> + +namespace KPlato +{ + +class Project; +class ProjectDialogImpl; +class ResourcesPanel; + +class ProjectDialogImpl : public ProjectDialogBase { + Q_OBJECT +public: + ProjectDialogImpl (QWidget *parent); + +private slots: + void slotCheckAllFieldsFilled(); + void slotSchedulingChanged(int activated); + void slotChooseLeader(); + +signals: + void obligatedFieldsFilled(bool yes); + void schedulingTypeChanged(int activated); +}; + +class ProjectDialog : public KDialogBase { + Q_OBJECT +public: + ProjectDialog(Project &project, QWidget *parent=0, + const char *name=0); + + +protected slots: + void slotOk(); + void slotSchedulingChanged(int activated); + +private: + Project &project; + ProjectDialogImpl *dia; + ResourcesPanel *resourcesTab; +}; + +} //KPlato namespace + +#endif // PROJECTDIALOG_H diff --git a/kplato/kptprojectdialogbase.ui b/kplato/kptprojectdialogbase.ui new file mode 100644 index 00000000..5c602aee --- /dev/null +++ b/kplato/kptprojectdialogbase.ui @@ -0,0 +1,260 @@ +<!DOCTYPE UI><UI version="3.1" stdsetdef="1"> +<class>KPlato::ProjectDialogBase</class> +<widget class="QWidget"> + <property name="name"> + <cstring>ProjectDialogBase</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>643</width> + <height>394</height> + </rect> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QTabWidget"> + <property name="name"> + <cstring>daTabs</cstring> + </property> + <widget class="QWidget"> + <property name="name"> + <cstring>tab</cstring> + </property> + <attribute name="title"> + <string>&General</string> + </attribute> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget" row="0" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>layout5</cstring> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLineEdit" row="0" column="1" rowspan="1" colspan="2"> + <property name="name"> + <cstring>namefield</cstring> + </property> + <property name="toolTip" stdset="0"> + <string></string> + </property> + <property name="whatsThis" stdset="0"> + <string>Give a name to the project to identify it. Example can be 'rewrite explorer' or 'housing project C2'</string> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel2</cstring> + </property> + <property name="text"> + <string>Project &leader:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>leaderfield</cstring> + </property> + </widget> + <widget class="QLineEdit" row="1" column="1"> + <property name="name"> + <cstring>leaderfield</cstring> + </property> + </widget> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>Project n&ame:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>namefield</cstring> + </property> + </widget> + <widget class="QPushButton" row="1" column="2"> + <property name="name"> + <cstring>chooseLeader</cstring> + </property> + <property name="text"> + <string>&Choose...</string> + </property> + </widget> + </grid> + </widget> + <widget class="QGroupBox" row="1" column="0"> + <property name="name"> + <cstring>groupBox1</cstring> + </property> + <property name="title"> + <string>Scheduling</string> + </property> + <property name="toolTip" stdset="0"> + <string>The scheduling type will influence how variable times events are placed</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QComboBox"> + <item> + <property name="text"> + <string>ASAP</string> + </property> + </item> + <item> + <property name="text"> + <string>ALAP</string> + </property> + </item> + <item> + <property name="text"> + <string>Start Not Earlier Then</string> + </property> + </item> + <item> + <property name="text"> + <string>Finish Not Later Then</string> + </property> + </item> + <item> + <property name="text"> + <string>Must Start On</string> + </property> + </item> + <property name="name"> + <cstring>schedulerType</cstring> + </property> + </widget> + <widget class="QTextBrowser"> + <property name="name"> + <cstring>lSchedulingExplain</cstring> + </property> + </widget> + </vbox> + </widget> + <widget class="QGroupBox" row="1" column="1"> + <property name="name"> + <cstring>groupBox2</cstring> + </property> + <property name="title"> + <string>Starting Date</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KDatePicker" row="0" column="0" rowspan="1" colspan="3"> + <property name="name"> + <cstring>schedulerDate</cstring> + </property> + <property name="minimumSize"> + <size> + <width>0</width> + <height>200</height> + </size> + </property> + </widget> + <widget class="QCheckBox" row="1" column="0"> + <property name="name"> + <cstring>checkBox1</cstring> + </property> + <property name="text"> + <string>Specify time:</string> + </property> + </widget> + <widget class="QTimeEdit" row="1" column="1"> + <property name="name"> + <cstring>schedulerTime</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>4</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="time"> + <time> + <hour>12</hour> + <minute>0</minute> + <second>0</second> + </time> + </property> + </widget> + <spacer row="1" column="2"> + <property name="name"> + <cstring>spacer6</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>121</width> + <height>20</height> + </size> + </property> + </spacer> + </grid> + </widget> + </grid> + </widget> + <widget class="QWidget"> + <property name="name"> + <cstring>tab</cstring> + </property> + <attribute name="title"> + <string>&Notes</string> + </attribute> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel5</cstring> + </property> + <property name="text"> + <string>&Project notes and summary:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>descriptionfield</cstring> + </property> + </widget> + <widget class="QTextEdit"> + <property name="name"> + <cstring>descriptionfield</cstring> + </property> + </widget> + </vbox> + </widget> + </widget> + </vbox> +</widget> +<connections> + <connection> + <sender>checkBox1</sender> + <signal>toggled(bool)</signal> + <receiver>schedulerTime</receiver> + <slot>setEnabled(bool)</slot> + </connection> +</connections> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>kdatepicker.h</includehint> + <includehint>klineedit.h</includehint> +</includehints> +</UI> diff --git a/kplato/kptrelation.cc b/kplato/kptrelation.cc new file mode 100644 index 00000000..e4cf7b26 --- /dev/null +++ b/kplato/kptrelation.cc @@ -0,0 +1,142 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Thomas zander <zander@kde.org> + Copyright (C) 2004 Dag Andersen <danders@get2net.dk> + + 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. +*/ +#include "kptrelation.h" + +#include "kptnode.h" +#include "kptproject.h" +#include "kptcanvasitem.h" + +#include <qcanvas.h> +#include <qdom.h> + +#include <kdebug.h> + +namespace KPlato +{ + +Relation::Relation(Node *parent, Node *child, Type type, Duration lag) { + m_parent=parent; + m_child=child; + m_type=type; + m_lag=lag; +} + +Relation::Relation(Node *parent, Node *child, Type type) { + m_parent=parent; + m_child=child; + m_type=type; + m_lag=Duration(); +} + +Relation::Relation(Relation *rel) { + m_parent=rel->parent(); + m_child=rel->child(); + m_type=rel->type(); + m_lag=rel->lag(); +} + +Relation::~Relation() { + //kdDebug()<<k_funcinfo<<"parent: "<<(m_parent ? m_parent->name():"none")<<" child: "<<(m_child ? m_child->name():"None")<<endl; + if (m_parent) + m_parent->takeDependChildNode(this); + if (m_child) + m_child->takeDependParentNode(this); +} + +void Relation::setType(Type type) { + m_type=type; +} + + +bool Relation::load(QDomElement &element, Project &project) { + m_parent = project.findNode(element.attribute("parent-id")); + if (m_parent == 0) { + return false; + } + m_child = project.findNode(element.attribute("child-id")); + if (m_child == 0) { + return false; + } + if (m_child == m_parent) { + kdDebug()<<k_funcinfo<<"child == parent"<<endl; + return false; + } + if (!m_parent->legalToLink(m_child)) + return false; + + QString tr = element.attribute("type"); + if ( tr == "Finish-Start" ) + m_type = FinishStart; + else if ( tr == "Finish-Finish" ) + m_type = FinishFinish; + else if ( tr == "Start-Start" ) + m_type = StartStart; + else + m_type = FinishStart; + + m_lag = Duration::fromString(element.attribute("lag")); + + if (!m_parent->addDependChildNode(this)) { + kdError()<<k_funcinfo<<"Failed to add relation: Child="<<m_child->name()<<" parent="<<m_parent->name()<<endl; + return false; + } + if (!m_child->addDependParentNode(this)) { + m_parent->delDependChildNode(this, false/*do not delete*/); + kdError()<<k_funcinfo<<"Failed to add relation: Child="<<m_child->name()<<" parent="<<m_parent->name()<<endl; + return false; + } + + //kdDebug()<<k_funcinfo<<"Added relation: Child="<<m_child->name()<<" parent="<<m_parent->name()<<endl; + return true; +} + + +void Relation::save(QDomElement &element) const { + QDomElement me = element.ownerDocument().createElement("relation"); + element.appendChild(me); + + me.setAttribute("parent-id", m_parent->id()); + me.setAttribute("child-id", m_child->id()); + QString type = "Finish-Start"; + switch (m_type) { + case FinishStart: + type = "Finish-Start"; + break; + case FinishFinish: + type = "Finish-Finish"; + break; + case StartStart: + type = "Start-Start"; + break; + } + me.setAttribute("type", type); + me.setAttribute("lag", m_lag.toString()); +} + +#ifndef NDEBUG +void Relation::printDebug(QCString indent) { + indent += " "; + kdDebug()<<indent<<" Parent: "<<m_parent->name()<<endl; + kdDebug()<<indent<<" Child: "<<m_child->name()<<endl; + kdDebug()<<indent<<" Type: "<<m_type<<endl; +} +#endif + +} //KPlato namespace diff --git a/kplato/kptrelation.h b/kplato/kptrelation.h new file mode 100644 index 00000000..2ae6032d --- /dev/null +++ b/kplato/kptrelation.h @@ -0,0 +1,115 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Thomas Zander zander@kde.org + Copyright (C) 2004 Dag Andersen <danders@get2net.dk> + + 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 KPTRELATION_H +#define KPTRELATION_H + +#include "kptduration.h" + +#include <qstring.h> + +class QCanvas; +class QDomElement; + +namespace KPlato +{ + +class Node; +class Project; +class PertCanvas; + +/** + * The relation class couples a 2 nodes together which are dependent on each other. + * If for example you have a project to build a house, the node that represents the + * adding of the roof is dependent on the node that represents the building of the walls. + * The roof can't be put up if the walls are not there yet. + * We actually have a number of relationtypes so this relation can be used in different manners. + */ +class Relation { +public: + enum Type { FinishStart, FinishFinish, StartStart }; + + Relation(Node *parent, Node *child, Type type, Duration lag); + Relation(Node *parent=0, Node *child=0, Type type=FinishStart); + Relation(Relation *rel); + + /** + * When deleted the relation will remove itself from + * the parent- and child nodes lists + */ + virtual ~Relation(); + + void setType(Type ); + Type type() const { return m_type; } + + /** returns the lag. + * The lag of a relation is the time it takes between the parent starting/stopping + * and the start of the child. + */ + const Duration &lag() const { return m_lag; } + void setLag(Duration lag) { m_lag = lag; } + + /** + * @return The parent dependent node. + */ + Node *parent() const { return m_parent; } + /** + * @return The child dependent node. + */ + Node *child() const { return m_child; } + + enum Result { + SUCCESS = 0l, + HASCHILDREN = 1l, + NOTIMPL = 2l + }; + + bool load(QDomElement &element, Project &project); + void save(QDomElement &element) const; + +protected: // variables + Node *m_parent; + Node *m_child; + Type m_type; + Duration m_lag; + +private: + QString m_parentId; + +#ifndef NDEBUG +public: + void printDebug(QCString indent); +#endif + +}; + +class ProxyRelation : public Relation +{ +public: + ProxyRelation(Node *parent, Node *child, Relation::Type type, Duration lag) + : Relation(parent, child, type, lag) + {} + + ~ProxyRelation() { m_parent = 0; m_child = 0;} +}; + +} //KPlato namespace + +#endif diff --git a/kplato/kptrelationdialog.cc b/kplato/kptrelationdialog.cc new file mode 100644 index 00000000..ec936af5 --- /dev/null +++ b/kplato/kptrelationdialog.cc @@ -0,0 +1,122 @@ +/* This file is part of the KDE project + Copyright (C) 2003, 2004 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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. +*/ + +#include "kptrelationdialog.h" +#include "kptrelation.h" +#include "kptnode.h" +#include "kptpart.h" +#include "kptcommand.h" +#include "kptdurationwidget.h" +#include "relationpanel.h" + +#include <qlayout.h> +#include <qvbox.h> +#include <qlabel.h> +#include <qbuttongroup.h> +#include <qradiobutton.h> + +#include <kmessagebox.h> +#include <klocale.h> +#include <kcommand.h> +#include <kdebug.h> + +namespace KPlato +{ + +AddRelationDialog::AddRelationDialog(Relation *rel, QWidget *p, QString caption, int buttons, const char *n) + : KDialogBase(Swallow, caption, buttons, Ok, p, n, true, true) +{ + if (caption.isEmpty()) + setCaption(i18n("Add Relationship")); + m_relation = rel; + m_panel = new RelationPanel(this); + setMainWidget(m_panel); + m_panel->setActiveWindow(); + + m_panel->fromName->setText(rel->parent()->name()); + m_panel->toName->setText(rel->child()->name()); + m_panel->relationType->setButton(rel->type()); + + m_panel->lag->setVisibleFields(DurationWidget::Days|DurationWidget::Hours|DurationWidget::Minutes); + m_panel->lag->setFieldUnit(0, i18n("days", "d")); + m_panel->lag->setFieldUnit(1, i18n("hours", "h")); + m_panel->lag->setFieldUnit(2, i18n("minutes", "m")); + m_panel->lag->setValue(rel->lag()); + + m_panel->relationType->setFocus(); + enableButtonOK(true); + connect(m_panel->relationType, SIGNAL(clicked(int)), SLOT(typeClicked(int))); + connect(m_panel->lag, SIGNAL(valueChanged()), SLOT(lagChanged())); +} + +KCommand *AddRelationDialog::buildCommand(Part *part) { + return new AddRelationCmd(part, m_relation, i18n("Add Relation")); +} + +void AddRelationDialog::slotOk() { + if ( m_panel->relationType->selected() == 0 ) { + KMessageBox::sorry(this, i18n("You must select a relationship type")); + return; + } + accept(); +} + +void AddRelationDialog::lagChanged() { + enableButtonOK(true); +} + +void AddRelationDialog::typeClicked(int id) { + if (id != m_relation->type()) + enableButtonOK(true); +} + +////////////////// + +ModifyRelationDialog::ModifyRelationDialog(Relation *rel, QWidget *p, const char *n) + : AddRelationDialog(rel, p, i18n("Edit Relationship"), Ok|Cancel|User1, n) +{ + setButtonText( KDialogBase::User1, i18n("Delete") ); + m_deleted = false; + enableButtonOK(false); +} + +// Delete +void ModifyRelationDialog::slotUser1() { + m_deleted = true; + accept(); +} + +KCommand *ModifyRelationDialog::buildCommand(Part *part) { + KMacroCommand *cmd=0; + if (m_panel->relationType->selectedId() != m_relation->type()) { + if (cmd == 0) + cmd = new KMacroCommand(i18n("Modify Relation")); + cmd->addCommand(new ModifyRelationTypeCmd(part, m_relation, (Relation::Type)m_panel->relationType->selectedId())); + } + if (m_relation->lag() != m_panel->lag->value()) { + if (cmd == 0) + cmd = new KMacroCommand(i18n("Modify Relation")); + cmd->addCommand(new ModifyRelationLagCmd(part, m_relation, m_panel->lag->value())); + } + return cmd; +} + +} //KPlato namespace + +#include "kptrelationdialog.moc" diff --git a/kplato/kptrelationdialog.h b/kplato/kptrelationdialog.h new file mode 100644 index 00000000..22cebde0 --- /dev/null +++ b/kplato/kptrelationdialog.h @@ -0,0 +1,79 @@ +/* This file is part of the KDE project + Copyright (C) 2002 The koffice team <koffice@kde.org> + Copyright (C) 2003, 2004 Dag Andersen <danders@get2net.dk> + + 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 KPTRELATIONDIALOG_H +#define KPTRELATIONDIALOG_H + +#include <kdialogbase.h> + +class QButtonGroup; +class KCommand; + +namespace KPlato +{ + +class RelationPanel; +class DurationWidget; + +class Node; +class Relation; +class Part; +class ModifyRelationTypeCmd; + +class AddRelationDialog : public KDialogBase +{ + Q_OBJECT +public: + AddRelationDialog(Relation *rel, QWidget *p, QString caption=QString::null, int buttons=Ok|Cancel, const char *n=0); + + virtual KCommand *buildCommand(Part *part); + +protected slots: + void slotOk(); + void lagChanged(); + void typeClicked(int); + +protected: + RelationPanel *m_panel; + QButtonGroup *relationType; + Relation *m_relation; + DurationWidget *m_lag; +}; + + +class ModifyRelationDialog : public AddRelationDialog +{ + Q_OBJECT +public: + ModifyRelationDialog(Relation *rel, QWidget *p=0, const char *n=0); + + virtual KCommand *buildCommand(Part *part); + bool relationIsDeleted() { return m_deleted; } + +protected slots: + void slotUser1(); + +private: + bool m_deleted; +}; + +} //KPlato namespace + +#endif // RELATIONDIALOG_H diff --git a/kplato/kptreportview.cc b/kplato/kptreportview.cc new file mode 100644 index 00000000..d5e964c7 --- /dev/null +++ b/kplato/kptreportview.cc @@ -0,0 +1,732 @@ +/* This file is part of the KDE project + Copyright (C) 2003 - 2005 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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. +*/ + +#include "kptreportview.h" + +#include "kptview.h" +#include "kptpart.h" +#include "kptcontext.h" + +#include <mreportviewer.h> +#include <mpagecollection.h> + +#include <KoStore.h> + +#include <kdebug.h> +#include <kaction.h> +#include <kstdaction.h> +#include <ktoolbar.h> +#include <kstandarddirs.h> +#include <kurl.h> +#include <kmessagebox.h> +#include <kio/netaccess.h> +#include <klocale.h> +#include <kglobal.h> +#include <kdesktopfile.h> +#include <kfiledialog.h> + +#include <qfile.h> +#include <qfileinfo.h> +#include <qheader.h> +#include <qpopupmenu.h> +#include <qlayout.h> +#include <qdom.h> +#include <qstringlist.h> + +namespace KPlato +{ + +class ReportTagsPrivate { +public: + + ReportTagsPrivate() + : m_project(0), + m_task(0), + m_resourcegroup(0), + m_resource(0), + alltasksLevel("-1"), + summarytasksLevel("-1"), + tasksLevel("-1"), + milestonesLevel("-1"), + resourcegroupsLevel("-1"), + resourcesLevel("-1") + {} + + ~ReportTagsPrivate() {} + + QString getData(QString source, QString tag) const { + if (tag.contains(".")) + return getData(tag); + + return getData(source + "." + tag); + } + + QString getData(QString tag) const { + //kdDebug()<<k_funcinfo<<"tag="<<tag<<endl; + KLocale *l = KGlobal::locale(); + if (!tag.contains('.')) { + // global tags + if (tag == "currentdate") { + return l->formatDate(QDate::currentDate(), true); + } + return QString::null; + } + if (tag.section(".", 0, 0) == "project") { + if (tag.section(".", 1, 1) == "name") + return (m_project ? m_project->name() : QString::null); + if (tag.section(".", 1, 1) == "leader") + return (m_project ? m_project->leader() : QString::null); + + } else if (tag.section(".", 0, 0) == "task") { + if (tag.section(".", 1, 1) == "name") + return (m_task ? m_task->name() : QString::null); + if (tag.section(".", 1, 1) == "responsible") + return (m_task ? m_task->leader() : QString::null); + else if (tag.section(".", 1, 1) == "wbs") + return (m_task ? m_task->wbs() : QString::null); + else if (tag.section(".", 1, 1) == "start") + return (m_task ? l->formatDateTime(m_task->startTime()) : QString::null); + else if (tag.section(".", 1, 1) == "starttime") + return (m_task ? l->formatTime(m_task->startTime().time()) : QString::null); + else if (tag.section(".", 1, 1) == "startdate") + return (m_task ? l->formatDate(m_task->startTime().date(), true) : QString::null); + else if (tag.section(".", 1, 1) == "duration") { + return (m_task ? m_task->duration().toString(Duration::Format_i18nDayTime) : QString::null); + } else if (tag.section(".", 1, 1) == "plannedcost") { + return (m_task ? l->formatMoney(m_task->plannedCost()) : QString::null); + } + } else if (tag.section(".", 0, 0) == "resourcegroup") { + if (tag.section(".", 1, 1) == "name") + return (m_resourcegroup ? m_resourcegroup->name() : QString::null); + + } else if (tag.section(".", 0, 0) == "resource") { + if (tag.section(".", 1, 1) == "name") + return (m_resource ? m_resource->name() : QString::null); + if (tag.section(".", 1, 1) == "type") + return (m_resource ? m_resource->typeToString() : QString::null); + if (tag.section(".", 1, 1) == "email") + return (m_resource ? m_resource->email() : QString::null); + if (tag.section(".", 1, 1) == "availablefrom") + return (m_resource ? l->formatDate(m_resource->availableFrom().date(), true) : QString::null); + if (tag.section(".", 1, 1) == "availableuntil") + return (m_resource ? l->formatDate(m_resource->availableUntil().date(), true) : QString::null); + if (tag.section(".", 1, 1) == "units") + return (m_resource ? QString("%1%").arg(m_resource->units()) : QString::null); + if (tag.section(".", 1, 1) == "normalrate") + return (m_resource ? l->formatMoney(m_resource->normalRate()) : QString::null); + if (tag.section(".", 1, 1) == "overtimerate") + return (m_resource ? l->formatMoney(m_resource->overtimeRate()) : QString::null); + } + return QString::null; + } + + Project *m_project; + Task *m_task; + ResourceGroup *m_resourcegroup; + Resource *m_resource; + + QString alltasksLevel; + QStringList alltasksProps; + QString summarytasksLevel; + QStringList summarytasksProps; + QString tasksLevel; + QStringList tasksProps; + QString milestonesLevel; + QStringList milestonesProps; + QString resourcegroupsLevel; + QStringList resourcegroupsProps; + QString resourcesLevel; + QStringList resourcesProps; + +}; + +class KugarReportViewer : public Kugar::MReportViewer { +public: + KugarReportViewer(QWidget *parent = 0, const char *name = 0) + : MReportViewer(parent, name) + {} + + int currentPage() { + return report ? report->getCurrentIndex() : 0; + } + int pageCount() { + return report ? report->pageCount() : 0; + } +}; + +ReportView::ReportView(View *view, QWidget *parent) + : QSplitter(parent), + m_mainview(view), + m_reportTags(0) +{ + //kdDebug()<<k_funcinfo<<endl; + m_reportList = new KListView(this); +#if KDE_IS_VERSION(3,3,9) + m_reportList->setShadeSortColumn(false); +#endif + m_reportList->addColumn(i18n("Report Template")); + m_reportList->header()->setStretchEnabled(true, 0); + m_reportList->header()->setSortIndicator(0); + + m_reportview = new KugarReportViewer(this); + + initReportList(); + + connect(m_reportList, SIGNAL(clicked(QListViewItem*)), SLOT(slotReportListClicked(QListViewItem*))); + connect(m_reportList, SIGNAL(selectionChanged(QListViewItem*)), SLOT(slotReportListSelectionChanged(QListViewItem*))); + + //setCentralWidget(m_reportview); + + // Create the user interface. + //KStdAction::print(this,SLOT(slotPrint()),actionCollection()); + //KStdAction::quit(this,SLOT(slotFileQuit()),actionCollection()); + +// KStdAction::showToolbar(this,SLOT(slotViewToolBar()),actionCollection()); +// KStdAction::showStatusbar(this,SLOT(slotViewStatusBar()),actionCollection()); + +// statusBar(); + +// createGUI(); + +} + + +ReportView::~ReportView() { + //safe + delete m_reportTags; +} + +void ReportView::initReportList() { +//FIXME: We need a solution that takes care project specific reports. + //kdDebug()<<k_funcinfo<<endl; + QStringList list; + m_reportList->clear(); + KStandardDirs std; + QStringList reportDesktopFiles = std.findAllResources("data", "kplato/reports/*.desktop", true, true); + for (QStringList::iterator it = reportDesktopFiles.begin(); it != reportDesktopFiles.end(); ++it) { + KDesktopFile file((*it), true); + QString name = file.readName(); + if (!name.isNull()) { + //kdDebug()<<" file: "<<*it<<" name="<<name<<endl; + QString url = file.readURL(); + if (!url.isNull()) { + if (url.left(1) != "/" || url.left(6) != "file:/") { + QString path = (*it).left((*it).findRev('/', -1)+1); // include '/' + url = path + url; + } + m_reportList->insertItem(new ReportItem(m_reportList, name, url)); + } + } + } +} + +void ReportView::draw(const QString &report) { + //kdDebug()<<k_funcinfo<<endl; + m_reportview->clearReport(); + m_reportTags = new ReportTagsPrivate(); + getTemplateFile(report); + m_reportview->setReportTemplate(templateDoc.toString()); + setReportData(); + m_reportview->renderReport(); + m_reportview->show(); + delete m_reportTags; + m_reportTags=0L; + enableNavigationBtn(); +} + +void ReportView::setup(KPrinter &printer) { + //kdDebug()<<k_funcinfo<<endl; + m_reportview->setupPrinter(printer); +} + +void ReportView::print(KPrinter &printer) { + //kdDebug()<<k_funcinfo<<endl; + m_reportview->printReport(printer); +} + +// Generate report data based on info from the template file +void ReportView::setReportData() { + QString s = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"; + s+="<KugarData>\n"; + s += setReportDetail(); + s+="</KugarData>\n"; + //kdDebug()<<s<<endl; + m_reportview->setReportData(s); +} + +QString ReportView::setReportDetail() { + //kdDebug()<<k_funcinfo<<endl; + QString s; + if (m_reportTags->alltasksLevel != "-1") { + //kdDebug()<<k_funcinfo<<"alltasks level="<<m_reportTags->alltasksLevel<<endl; + if (m_reportTags->summarytasksLevel == "-1") { + m_reportTags->summarytasksLevel = m_reportTags->alltasksLevel; + m_reportTags->summarytasksProps = m_reportTags->alltasksProps; + } + if (m_reportTags->tasksLevel == "-1") { + m_reportTags->tasksLevel = m_reportTags->alltasksLevel; + m_reportTags->tasksProps = m_reportTags->alltasksProps; + } + if (m_reportTags->milestonesLevel == "-1") { + m_reportTags->milestonesLevel = m_reportTags->alltasksLevel; + m_reportTags->milestonesProps = m_reportTags->alltasksProps; + } + s+= setTaskChildren(&(mainView()->getProject())); + + } else if (m_reportTags->summarytasksLevel == "0") { + // make a report that has summarytasks as starting points + QPtrListIterator<Node> it(mainView()->getProject().childNodeIterator()); + for (; it.current(); ++it) { + if (it.current()->type() == Node::Type_Summarytask) { + s += setTaskDetail(it.current()); + // Now do subtasks + s+= setTaskChildren(it.current()); + } + } + + } else if (m_reportTags->tasksLevel == "0") { + // make a report that has tasks as starting points + QPtrListIterator<Node> it(mainView()->getProject().childNodeIterator()); + for (; it.current(); ++it) { + if (it.current()->type() == Node::Type_Task) { + s += setTaskDetail(it.current()); + } + if (it.current()->type() == Node::Type_Summarytask) { + s+= setTaskChildren(it.current()); + if (m_reportTags->summarytasksLevel != "-1") { + s += setTaskDetail(it.current()); + } + } + } + + } else if (m_reportTags->milestonesLevel == "0") { + + } else if (m_reportTags->resourcegroupsLevel == "0") { + // make a report that has resourcegroups as starting points + QPtrListIterator<ResourceGroup> it(mainView()->getProject().resourceGroups()); + for (; it.current(); ++it) { + s += setResourceGroupDetail(it.current()); + } + + } else if (m_reportTags->resourcesLevel == "0") { + // make a report that has resources as starting points + QPtrListIterator<ResourceGroup> it(mainView()->getProject().resourceGroups()); + for (; it.current(); ++it) { + QPtrListIterator<Resource> rit(it.current()->resources()); + for (; rit.current(); ++rit) { + s += setResourceDetail(rit.current()); + } + } + } + //kdDebug()<<k_funcinfo<<s<<endl; + return s; +} + +QString ReportView::setResourceGroupDetail(ResourceGroup *group) { + //kdDebug()<<k_funcinfo<<group->name()<<endl; + QString s; + if (m_reportTags->resourcegroupsLevel != "-1") { + m_reportTags->m_resourcegroup = group; + //kdDebug()<<k_funcinfo<<group->name()<<": level="<<m_reportTags->resourcegroupsLevel<<endl; + s = setDetail("resourcegroup", m_reportTags->resourcegroupsProps, m_reportTags->resourcegroupsLevel); + QPtrListIterator<Resource> rit(group->resources()); + for (; rit.current(); ++rit) { + s += setResourceDetail(rit.current()); + } + } + return s; +} + +QString ReportView::setResourceDetail(Resource *res) { + //kdDebug()<<k_funcinfo<<res->name()<<endl; + QString s; + if (m_reportTags->resourcesLevel != "-1") { + m_reportTags->m_resource = res; + //kdDebug()<<k_funcinfo<<res->name()<<": level="<<m_reportTags->resourcesLevel<<endl; + s = setDetail("resource", m_reportTags->resourcesProps, m_reportTags->resourcesLevel); + } + return s; +} + +QString ReportView::setTaskChildren(Node *node) { + //kdDebug()<<k_funcinfo<<endl; + QString s; + QPtrListIterator<Node> it(node->childNodeIterator()); + for (; it.current(); ++it) { + s += setTaskDetail(it.current()); + if (it.current()->type() == Node::Type_Summarytask) + s+= setTaskChildren(it.current()); + } + return s; +} + +QString ReportView::setTaskDetail(Node *node) { + //kdDebug()<<k_funcinfo<<endl; + QString s; + QStringList props; + QString level = "-1"; + if (node->type() == Node::Type_Task) { + props = m_reportTags->tasksProps; + level = m_reportTags->tasksLevel; + } else if (node->type() == Node::Type_Summarytask) { + props = m_reportTags->summarytasksProps; + level = m_reportTags->summarytasksLevel; + } else if (node->type() == Node::Type_Milestone) { + props = m_reportTags->milestonesProps; + level = m_reportTags->milestonesLevel; + } + if (level != "-1") { + m_reportTags->m_task = static_cast<Task *>(node); + s = setDetail("task", props, level); + } + return s; +} + +QString ReportView::setDetail(const QString & source, QStringList &properties, QString &level) { + QString s = "<Row"; + s += " level=\"" + level + "\""; + for (unsigned int i=0; i < properties.count(); ++i) { + //kdDebug()<<k_funcinfo<<"Property: "<<properties[i]<<endl; + s += " " + properties[i].section('=', 0, 0) + "="; // Field + QString data = m_reportTags->getData(source, properties[i].section('=', 1, 1)); + if (data.isNull()) + data = ""; + data = data.replace('<', "<"); + data = data.replace('>', ">"); + data = data.replace('"', """); + + s += "\"" + data + "\""; // Property + //kdDebug()<<k_funcinfo<<s<<endl; + } + s += "/>\n"; + return s; +} + +// Most of this is from KoDocument::loadNativeFormat +void ReportView::openTemplateFile(const QString &file) { + if (!QFileInfo(file).isFile()) { + KMessageBox::sorry( this, i18n("Cannot find report template file!"), + i18n("Generate Report")); + return; + } + QFile in; + in.setName(file); + if (!in.open(IO_ReadOnly)) { + KMessageBox::sorry( this, i18n("Cannot open report template file!"), + i18n("Generate Report")); + return; + } + // Try to find out whether it is a mime multi part file + char buf[5]; + if ( in.readBlock( buf, 4 ) < 4 ) + { + in.close(); + KMessageBox::sorry( this, i18n("Cannot read report template file!"), + i18n("Generate Report")); + return; + } + + if (strncasecmp( buf, "<?xm", 4 ) == 0) { // xml file? + in.at(0); + // fake + //m_reportview->setReportTemplate(&in); + loadTemplate(in); + in.close(); + return; + } + in.close(); + KoStore* store=KoStore::createStore(file, KoStore::Read); + if (!store) + { + KMessageBox::sorry( this, i18n("Cannot open report template file!"), + i18n("Generate Report")); + return; + } + bool b = store->open("maindoc.xml"); + if ( !b ) + { + KMessageBox::sorry( this, i18n("Cannot find the proper report template file!"), + i18n("Generate Report")); + delete store; + return; + } + loadTemplate(*(store->device())); + store->close(); +} + +void ReportView::loadTemplate(QIODevice &dev) { + QString errorMsg; + int errorLine; + int errorColumn; + if (!templateDoc.setContent( &dev , &errorMsg, &errorLine, &errorColumn)) { + QString msg = "Parsing template file failed with "; + KMessageBox::sorry( this, msg + errorMsg, i18n("Generate Report")); + return; + } + loadTemplate(templateDoc); +} + +void ReportView::loadTemplate(QDomDocument &doc) { + QDomNode tpl; + QDomNode child; + for (tpl = doc.firstChild(); !tpl.isNull(); tpl = tpl.nextSibling()) + if (tpl.nodeName() == "KugarTemplate") + break; + + if (tpl.isNull()) + return; + + m_reportTags->m_project = &(mainView()->getPart()->getProject()); + // Get all the child report elements + QDomNodeList children = tpl.childNodes(); + int childCount = children.length(); + + for(int j = 0; j < childCount; j++){ + child = children.item(j); + if(child.nodeType() == QDomNode::ElementNode) { + QDomElement e = child.toElement(); + //kdDebug()<<child.nodeName()<<endl; + // Report Header + if(child.nodeName() == "ReportHeader") { + handleHeader(child); + } else if (child.nodeName() == "PageHeader") { + handleHeader(child); + } else if(child.nodeName() == "DetailHeader") { + handleHeader(child); + } else if(child.nodeName() == "Detail") { + handleDetail(e); + } else if(child.nodeName() == "DetailFooter") { + handleHeader(child); + } else if(child.nodeName() == "PageFooter") { + handleHeader(child); + } else if(child.nodeName() == "ReportFooter") { + handleHeader(child); + } else if(child.nodeName() == "KPlato") { + handleKPlato(e); + } + } + } +} + +void ReportView::handleHeader(QDomNode &node) { + QDomNode child; + QDomNodeList children = node.childNodes(); + int childCount = children.length(); + for (int j = 0; j < childCount; j++) { + child = children.item(j); + if (child.nodeName() == "Label") { + QDomNode n = child.attributes().namedItem("Text"); + QString s = n.nodeValue(); + if (!s.isEmpty()) { + // Translate labels + s = i18n(n.nodeValue().latin1()); //NOTE: Not sure if latin1 is ok + } + QString r = s; + int i = 0, j = 0; + do { + i = j; + if ( ((i = s.find('[', i)) != -1) && ((j = s.find(']', i+1)) != -1) ) { + QString tag = s.mid(i, j-i+1); + QString data = m_reportTags->getData(tag.mid(1, tag.length()-2)); + r = r.replace(tag, data); + } + } while (i != -1 && j != -1); + n.setNodeValue(r); + //kdDebug()<<" new Text="<<n.nodeValue()<<endl; + } else if (child.nodeName() == "Field") { + QDomElement e = child.toElement(); + if (!e.isElement()) { + continue; // !!!!! + } + QString s = e.attribute("Field"); + QString data = m_reportTags->getData(s); + e.setAttribute("Text", data); + //kdDebug()<<" new Text="<<e.attribute("Text")<<endl; + } + } +} + +QStringList ReportView::getProperties(QDomElement &elem) { + QStringList props; + QDomNodeList list(elem.childNodes()); + int childCount = list.length(); + for (int j = 0; j < childCount; j++) { + QDomNode child = list.item(j); + if (child.nodeName() == "Field") { + props.append(child.attributes().namedItem("Field").nodeValue()+"="+child.attributes().namedItem("Field").nodeValue()); + } + } + return props; +} + +void ReportView::handleKPlato(QDomElement &elem) { + QDomNodeList list(elem.childNodes()); + int childCount = list.length(); + for (int j = 0; j < childCount; j++) { + QDomNode child = list.item(j); + if (child.nodeName() == "Detail") { + QDomElement e = child.toElement(); + if (!e.isElement()) { + continue; // !!!!! + } + QString source = e.attribute("SelectFrom"); + QString level = e.attribute("Level", "-1"); + //kdDebug()<<k_funcinfo<<"SelectFrom="<<source<<" Level="<<level<<endl; + if (source.isNull() || level == "-1") + continue; + + QStringList list = QStringList::split(" ", source); + QStringList::iterator it = list.begin(); + for (; it != list.end(); ++it) { + //kdDebug()<<k_funcinfo<<(*it)<<endl; + if ((*it) == "alltasks") { + m_reportTags->alltasksLevel = level; + } + if ((*it) == "summarytasks") { + m_reportTags->summarytasksLevel = level; + } + if ((*it) == "tasks") { + m_reportTags->tasksLevel = level; + } + if ((*it) == "milestones") { + m_reportTags->milestonesLevel = level; + } + if ((*it) == "resourcegroups") { + m_reportTags->resourcegroupsLevel = level; + } + if ((*it) == "resources") { + m_reportTags->resourcesLevel = level; + } + } + } + } +} + +void ReportView::handleDetail(QDomElement &elem) { + //kdDebug()<<k_funcinfo<<endl; + QString level = elem.attribute("Level", "-1"); + if (level == "-1") { + return; + } + + if (level == m_reportTags->alltasksLevel) { + m_reportTags->alltasksProps = getProperties(elem); + } + if (level == m_reportTags->summarytasksLevel) { + m_reportTags->summarytasksProps = getProperties(elem); + } + if (level == m_reportTags->tasksLevel) { + m_reportTags->tasksProps = getProperties(elem); + } + if (level == m_reportTags->milestonesLevel) { + m_reportTags->milestonesProps = getProperties(elem); + } + if (level == m_reportTags->resourcegroupsLevel) { + m_reportTags->resourcegroupsProps = getProperties(elem); + } + if (level == m_reportTags->resourcesLevel) { + m_reportTags->resourcesProps = getProperties(elem); + } +} + +void ReportView::replaceTags(QDomNode &node) { + if (node.isNull()) + return; +} + +void ReportView::getTemplateFile(const QString &tpl) { + + KURL url(tpl); + QString localtpl; + bool isTemp = false; + + if (!url.isValid()) + { + KMessageBox::sorry(this,i18n("Malformed template filename: %1").arg(url.prettyURL())); + } + else + { + if (KIO::NetAccess::download(url,localtpl,this)) + isTemp = true; + else + KMessageBox::sorry(this,i18n("Unable to download template file: %1").arg(url.prettyURL())); + } + + if (!localtpl.isNull()) + { + openTemplateFile(localtpl); + if (isTemp) + KIO::NetAccess::removeTempFile(localtpl); + } +} + +void ReportView::enableNavigationBtn() { + //kdDebug()<<k_funcinfo<<"curr="<<m_reportview->currentPage()<<" count="<<m_reportview->pageCount()<<endl; + emit setFirstPageActionEnabled(m_reportview->currentPage() > 0); + emit setNextPageActionEnabled(m_reportview->currentPage() < m_reportview->pageCount()-1); + emit setPriorPageActionEnabled(m_reportview->currentPage() > 0); + emit setLastPageActionEnabled(m_reportview->currentPage() < m_reportview->pageCount()-1); +} +void ReportView::slotFirstPage() { + m_reportview->slotFirstPage(); + enableNavigationBtn(); +} + +void ReportView::slotNextPage() { + m_reportview->slotNextPage(); + enableNavigationBtn(); +} + +void ReportView::slotPrevPage() { + m_reportview->slotPrevPage(); + enableNavigationBtn(); +} + +void ReportView::slotLastPage() { + m_reportview->slotLastPage(); + enableNavigationBtn(); +} + +bool ReportView::setContext(Context::Reportview &context) { + Q_UNUSED(context); + //kdDebug()<<k_funcinfo<<endl; + return true; +} + +void ReportView::getContext(Context::Reportview &context) const { + Q_UNUSED(context); + //kdDebug()<<k_funcinfo<<endl; +} + +void ReportView::slotReportListClicked(QListViewItem* item) { + if (item == m_reportList->selectedItem()) + slotReportListSelectionChanged(item); +} + +void ReportView::slotReportListSelectionChanged(QListViewItem* item) { + ReportItem *ri = dynamic_cast<ReportItem*>(item); + if (ri == 0) + return; + draw(ri->url); +} + + +} //KPlato namespace + +#include "kptreportview.moc" diff --git a/kplato/kptreportview.h b/kplato/kptreportview.h new file mode 100644 index 00000000..e0e8b8c2 --- /dev/null +++ b/kplato/kptreportview.h @@ -0,0 +1,139 @@ +/* This file is part of the KDE project + Copyright (C) 2003 - 2005 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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 KPTREPORTVIEW_H +#define KPTREPORTVIEW_H + +#include "kptcontext.h" +#include "kptproject.h" +#include "kpttask.h" +#include "kptresource.h" +#include "kptduration.h" +#include <qsplitter.h> +#include <qstring.h> + +#include <klistview.h> + +class KAction; +class KPrinter; + +namespace Kugar +{ + class MReportViewer; +} // Kugar namespace + +//class QString; +class QStringList; +class QDomDocument; +class QDomNode; +class QIODevice; + +namespace KPlato +{ + +class View; +class Node; +class KugarReportViewer; +class ReportTagsPrivate; + +class ReportView : public QSplitter +{ + Q_OBJECT + + public: + + ReportView(View *view, QWidget *parent); + + ~ReportView(); + + void zoom(double /*zoom*/) {} + + void draw(const QString &report); + View *mainView() const { return m_mainview; } + + void setup(KPrinter &printer); + void print(KPrinter &printer); + + void setReportData(); + + void getTemplateFile(const QString &tpl); + void openTemplateFile(const QString &file); + void loadTemplate(QIODevice &dev); + void loadTemplate(QDomDocument &doc); + void handleHeader(QDomNode &node); + void handleDetail(QDomElement &elem); + void handleKPlato(QDomElement &elem); + void replaceTags(QDomNode &node); + + QString setReportDetail(); + QString setTaskChildren(Node *node); + QString setTaskDetail(Node *node); + QStringList getProperties(QDomElement &elem); + + QString setResourceDetail(Resource *res); + QString setResourceGroupDetail(ResourceGroup *group); + + QString setDetail(const QString &source, QStringList &properties, QString &level); + + virtual bool setContext(Context::Reportview &context); + virtual void getContext(Context::Reportview &context) const; + + void enableNavigationBtn(); + +signals: + void setFirstPageActionEnabled(bool); + void setNextPageActionEnabled(bool); + void setPriorPageActionEnabled(bool); + void setLastPageActionEnabled(bool); + +public slots: + void slotFirstPage(); + void slotNextPage(); + void slotPrevPage(); + void slotLastPage(); + +protected slots: + void slotReportListClicked(QListViewItem* item); + void slotReportListSelectionChanged(QListViewItem* item); + +private: + class ReportItem : public KListViewItem { + public: + ReportItem(KListView *p, QString name, QString _url) + : KListViewItem(p, name), + url(_url) + {} + QString url; + }; + void initReportList(); + +private: + View *m_mainview; + KListView *m_reportList; + KugarReportViewer *m_reportview; + int m_defaultFontSize; + + QDomDocument templateDoc; + + ReportTagsPrivate *m_reportTags; +}; + +} //KPlato namespace + +#endif diff --git a/kplato/kptrequestresourcespanel.cc b/kplato/kptrequestresourcespanel.cc new file mode 100644 index 00000000..1996549f --- /dev/null +++ b/kplato/kptrequestresourcespanel.cc @@ -0,0 +1,266 @@ +/* This file is part of the KDE project + Copyright (C) 2003 - 2005 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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. +*/ + +#include "kptrequestresourcespanel.h" +#include "kpttask.h" +#include "kptproject.h" +#include "kptresource.h" +#include "kptcalendar.h" + +#include <kdebug.h> +#include <kmessagebox.h> +#include <klocale.h> +#include <kptcommand.h> + +#include <qlistbox.h> +#include <qpushbutton.h> +#include <qlineedit.h> +#include <qstring.h> +#include <qspinbox.h> +#include <qvalidator.h> +#include <qcombobox.h> +#include <qdatetime.h> +#include <qdatetimeedit.h> + +namespace KPlato +{ + +ResourceTableItem::ResourceTableItem(Resource *resource, ResourceRequest *request, bool check) { + m_resource = resource; + m_request = request; + m_checked = check; + m_origChecked = check; + m_checkitem = 0; + m_units = 100; + m_origUnits = 100; + //kdDebug()<<k_funcinfo<<"Added: '"<<resource->name()<<"' checked="<<m_checked<<endl; +} + +ResourceTableItem::~ResourceTableItem() { + //kdDebug()<<k_funcinfo<<m_resource->name()<<endl; +} + +void ResourceTableItem::update() { + if (m_checkitem) + m_checked = m_checkitem->isChecked(); + //kdDebug()<<k_funcinfo<<m_resource->name()<<" checked="<<m_checked<<endl; +} + +void ResourceTableItem::insert(QTable *table, int row) { + //kdDebug()<<k_funcinfo<<endl; + m_checkitem = new QCheckTableItem(table, m_resource->name()); + m_checkitem->setChecked(m_checked); + table->setItem(row, 0, m_checkitem); + + //kdDebug()<<k_funcinfo<<"Added: '"<<m_resource->name()<<"' checked="<<m_checked<<endl; +} + +GroupLVItem::GroupLVItem(QListView *parent, ResourceGroup *group, Task &task) + : QListViewItem(parent, group->name(), QString("%1").arg(group->units())), + m_group(group), + m_units(0) +{ + + m_request = task.resourceGroupRequest(group); + if (m_request) { + m_units = m_request->units(); + } + QPtrListIterator<Resource> it(group->resources()); + for (; it.current(); ++it) { + //kdDebug()<<k_funcinfo<<"resource="<<it.current()->name()<<endl; + ResourceRequest *req=0; + if (m_request) { + req = m_request->find(it.current()); + } + m_resources.append(new ResourceTableItem(it.current(), req, (bool)req)); + } + + m_resources.setAutoDelete(true); +} + +GroupLVItem::~GroupLVItem() { + //kdDebug()<<k_funcinfo<<m_group->name()<<endl; +} + +void GroupLVItem::update() { + QPtrListIterator<ResourceTableItem> it(m_resources); + for (; it.current(); ++it) { + it.current()->update(); + } +} + +void GroupLVItem::insert(QTable *table) { + + // clear the table, must be a better way! + for (int i = table->numRows(); i > 0; --i) + table->removeRow(i-1); + + if (m_group->numResources() == 0) { + table->setNumRows(1); + table->setItem(0, 0, new QCheckTableItem(table,i18n("None"))); + table->setItem(0, 1, new QComboTableItem(table,i18n("None"))); + } else { + table->setNumRows(m_group->numResources()); + QPtrListIterator<ResourceTableItem> it(m_resources); + for (int i = 0; it.current(); ++it, ++i) { + it.current()->insert(table, i); + } + } + table->adjustColumn(0); +} + +int GroupLVItem::numRequests() { + //kdDebug()<<k_funcinfo<<endl; + int value = m_units; + QPtrListIterator<ResourceTableItem> it(m_resources); + for (; it.current(); ++it) { + value += it.current()->numRequests(); + } + return value; +} + +bool GroupLVItem::isNull() const { + //kdDebug()<<k_funcinfo<<endl; + QPtrListIterator<ResourceTableItem> it(m_resources); + for (; it.current(); ++it) { + if (it.current()->isChecked()) + return false; + } + if (m_units > 0) + return false; + return true; +} + +RequestResourcesPanel::RequestResourcesPanel(QWidget *parent, Task &task, bool baseline) + : TaskResourcesPanelBase(parent), + m_task(task), + m_worktime(0), + selectedGroup(0), + m_blockChanged(false) { + + Project *p = dynamic_cast<Project*>(task.projectNode()); + if (p) { + m_worktime = p->standardWorktime(); + + QPtrListIterator<ResourceGroup> git(p->resourceGroups()); + for(int i=0; git.current(); ++git, ++i) { + ResourceGroup *grp = git.current(); + GroupLVItem *grpitem = new GroupLVItem(groupList, grp, task); + groupList->insertItem(grpitem); + //kdDebug()<<k_funcinfo<<" Added group: "<<grp->name()<<endl; + } + } + QListViewItem *item = groupList->firstChild(); + if (item) { + groupList->setSelected(item, true); + groupChanged(item); + } + + resourceTable->setReadOnly(baseline); + + connect(groupList, SIGNAL(selectionChanged(QListViewItem*)), SLOT(groupChanged(QListViewItem*))); + connect(resourceTable, SIGNAL(valueChanged(int, int)), SLOT(resourceChanged(int, int))); +// connect(numUnits, SIGNAL(valueChanged(int)), SLOT(unitsChanged(int))); + +} + +void RequestResourcesPanel::groupChanged(QListViewItem *item) { + //kdDebug()<<k_funcinfo<<endl; + GroupLVItem *grp = dynamic_cast<GroupLVItem *>(item); + if (grp == 0) + return; + + if (selectedGroup) { + selectedGroup->update(); + } + selectedGroup = grp; + +/* m_blockChanged = true; + numUnits->setMaxValue(grp->m_group->units()); + numUnits->setValue(grp->m_units); + m_blockChanged = false;*/ + grp->insert(resourceTable); +} + +void RequestResourcesPanel::resourceChanged(int /*r*/, int /*c*/) { + //kdDebug()<<k_funcinfo<<"("<<r<<","<<c<<")"<<endl; + sendChanged(); +} + +void RequestResourcesPanel::unitsChanged(int units) { + //kdDebug()<<k_funcinfo<<endl; + if (selectedGroup) { + selectedGroup->m_units = units; + sendChanged(); + } +} + +KCommand *RequestResourcesPanel::buildCommand(Part *part) { + //kdDebug()<<k_funcinfo<<endl; + KMacroCommand *cmd = 0; + if (selectedGroup) { + selectedGroup->update(); + } + QListViewItem *item = groupList->firstChild(); + for (; item; item = item->nextSibling()) { + GroupLVItem *grp = static_cast<GroupLVItem*>(item); + QPtrListIterator<ResourceTableItem> it = grp->resources(); + for (; it.current(); ++it) { + if (it.current()->isChecked() != it.current()->isOrigChecked()) { + if (!cmd) cmd = new KMacroCommand(""); + if (it.current()->isChecked()) { + if (!grp->m_request) { + grp->m_request = new ResourceGroupRequest(grp->m_group, grp->m_units); + cmd->addCommand(new AddResourceGroupRequestCmd(part, m_task, grp->m_request)); + } + cmd->addCommand(new AddResourceRequestCmd(part, grp->m_request, new ResourceRequest(it.current()->resource(), it.current()->units()))); + + continue; + } + if (grp->m_request && it.current()->request()) { + cmd->addCommand(new RemoveResourceRequestCmd(part, grp->m_request, it.current()->request())); + if (grp->isNull()) { + cmd->addCommand(new RemoveResourceGroupRequestCmd(part, m_task, grp->m_request)); + } + } else { + kdError()<<k_funcinfo<<"Remove failed"<<endl; + } + continue; + } + if (!it.current()->isChecked()) { + continue; + } + } + } + return cmd; +} + +bool RequestResourcesPanel::ok() { + if (selectedGroup) + selectedGroup->update(); + return true; +} + +void RequestResourcesPanel::sendChanged() { + if (!m_blockChanged) emit changed(); +} + +} //KPlato namespace + +#include "kptrequestresourcespanel.moc" diff --git a/kplato/kptrequestresourcespanel.h b/kplato/kptrequestresourcespanel.h new file mode 100644 index 00000000..e7d1f11c --- /dev/null +++ b/kplato/kptrequestresourcespanel.h @@ -0,0 +1,122 @@ +/* This file is part of the KDE project + Copyright (C) 2003 - 2005 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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 KPTREQUESTRESOURCESPANEL_H +#define KPTREQUESTRESOURCESPANEL_H + +#include "kpttaskresourcespanelbase.h" +#include "kptduration.h" + +#include <qlistview.h> +#include <qstring.h> +#include <qtable.h> +//class QCheckTableItem; + +class KCommand; +class KMacroCommand; + +namespace KPlato +{ + +class Account; +class Accounts; +class Task; +class ResourceGroup; +class Resource; +class ResourceGroupRequest; +class ResourceRequest; +class StandardWorktime; +class Part; +class Duration; + +class ResourceTableItem { +public: + ResourceTableItem(Resource *resource, ResourceRequest *request, bool check = false); + ~ResourceTableItem() ; + + void update(); + void insert(QTable *table, int row); + void ok(ResourceGroupRequest *group); + + bool isChecked() const { return m_checked; } + bool isOrigChecked() const { return m_origChecked; } + Resource *resource() { return m_resource; } + ResourceRequest *request() { return m_request; } + int numRequests() const { return m_checked ? 1 : 0; } + int units() const { return m_units; } + + Resource *m_resource; + int m_units, m_origUnits; + bool m_checked, m_origChecked; + QCheckTableItem *m_checkitem; + ResourceRequest *m_request; + int m_curAccountItem; + QString m_curAccountText; +}; + +class GroupLVItem : public QListViewItem { +public: + GroupLVItem(QListView *parent, ResourceGroup *group, Task &task); + ~GroupLVItem(); + + void update(); + void insert(QTable *table); + const QPtrList<ResourceTableItem> &resources() const { return m_resources; } + void ok(Task &task); + + int numRequests(); + bool isNull() const; + + ResourceGroup *m_group; + int m_units; + QPtrList<ResourceTableItem> m_resources; + ResourceGroupRequest *m_request; +}; + + +class RequestResourcesPanel : public TaskResourcesPanelBase { + Q_OBJECT +public: + RequestResourcesPanel(QWidget *parent, Task &task, bool baseline=false); + + KCommand *buildCommand(Part *part); + + bool ok(); + +private slots: + void sendChanged(); + + void groupChanged(QListViewItem *item); + void resourceChanged(int, int); + void unitsChanged(int); + +signals: + void changed(); + +private: + Task &m_task; + StandardWorktime *m_worktime; + GroupLVItem *selectedGroup; + bool m_blockChanged; + +}; + +} //KPlato namespace + +#endif diff --git a/kplato/kptresource.cc b/kplato/kptresource.cc new file mode 100644 index 00000000..2be8790c --- /dev/null +++ b/kplato/kptresource.cc @@ -0,0 +1,1260 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Thomas zander <zander@kde.org> + Copyright (C) 2004, 2005 Dag Andersen <danders@get2net.dk> + + 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. +*/ + +#include "kptresource.h" +#include "kptappointment.h" +#include "kptproject.h" +#include "kpttask.h" +#include "kptdatetime.h" +#include "kptcalendar.h" +#include "kpteffortcostmap.h" +#include "kptschedule.h" + +#include <kdebug.h> +#include <kglobal.h> +#include <klocale.h> + +namespace KPlato +{ + +ResourceGroup::ResourceGroup(Project *project) { + m_project = project; + m_type = Type_Work; + m_resources.setAutoDelete(true); + generateId(); + //kdDebug()<<k_funcinfo<<"("<<this<<")"<<endl; +} + +ResourceGroup::~ResourceGroup() { + if (findId() == this) { + removeId(); // only remove myself (I may be just a working copy) + } + //kdDebug()<<k_funcinfo<<"("<<this<<")"<<endl; +} + +bool ResourceGroup::setId(QString id) { + //kdDebug()<<k_funcinfo<<id<<endl; + if (id.isEmpty()) { + kdError()<<k_funcinfo<<"id is empty"<<endl; + m_id = id; + return false; + } + ResourceGroup *g = findId(); + if (g == this) { + //kdDebug()<<k_funcinfo<<"My id found, remove it"<<endl; + removeId(); + } else if (g) { + //Hmmm, shouldn't happen + kdError()<<k_funcinfo<<"My id '"<<m_id<<"' already used for different group: "<<g->name()<<endl; + } + if (findId(id)) { + kdError()<<k_funcinfo<<"id '"<<id<<"' is already used for different group: "<<findId(id)->name()<<endl; + m_id = QString(); // hmmm + return false; + } + m_id = id; + insertId(id); + //kdDebug()<<k_funcinfo<<m_name<<": inserted id="<<id<<endl; + return true; +} + +void ResourceGroup::generateId() { + if (!m_id.isEmpty()) { + removeId(); + } + for (int i=0; i<32000 ; ++i) { + m_id = m_id.setNum(i); + if (!findId()) { + insertId(m_id); + return; + } + } + m_id = QString(); +} + +void ResourceGroup::addResource(Resource* resource, Risk*) { + m_resources.append(resource); +} + +Resource* ResourceGroup::getResource(int) { + return 0L; +} + +Risk* ResourceGroup::getRisk(int) { + return 0L; +} + +void ResourceGroup::removeResource(Resource *resource) { + m_resources.removeRef(resource); +} + +Resource *ResourceGroup::takeResource(Resource *resource) { + return m_resources.take(m_resources.findRef(resource)); +} + +void ResourceGroup::removeResource(int) { +} + +void ResourceGroup::addRequiredResource(ResourceGroup*) { +} + +ResourceGroup* ResourceGroup::getRequiredResource(int) { + return 0L; +} + +void ResourceGroup::removeRequiredResource(int) { +} + +bool ResourceGroup::load(QDomElement &element) { + //kdDebug()<<k_funcinfo<<endl; + setId(element.attribute("id")); + m_name = element.attribute("name"); + + QDomNodeList list = element.childNodes(); + for (unsigned int i=0; i<list.count(); ++i) { + if (list.item(i).isElement()) { + QDomElement e = list.item(i).toElement(); + if (e.tagName() == "resource") { + // Load the resource + Resource *child = new Resource(m_project); + if (child->load(e)) + addResource(child, 0); + else + // TODO: Complain about this + delete child; + } + } + } + return true; +} + +void ResourceGroup::save(QDomElement &element) const { + //kdDebug()<<k_funcinfo<<endl; + + QDomElement me = element.ownerDocument().createElement("resource-group"); + element.appendChild(me); + + me.setAttribute("id", m_id); + me.setAttribute("name", m_name); + + QPtrListIterator<Resource> it(m_resources); + for ( ; it.current(); ++it ) { + it.current()->save(me); + } +} + +void ResourceGroup::initiateCalculation(Schedule &sch) { + QPtrListIterator<Resource> it(m_resources); + for (; it.current(); ++it) { + it.current()->initiateCalculation(sch); + } + clearNodes(); +} + +int ResourceGroup::units() { + int u = 0; + QPtrListIterator<Resource> it = m_resources; + for (; it.current(); ++it) { + u += it.current()->units(); + } + return u; +} + +ResourceGroup *ResourceGroup::findId(const QString &id) const { + return m_project ? m_project->findResourceGroup(id) : 0; +} + +bool ResourceGroup::removeId(const QString &id) { + return m_project ? m_project->removeResourceGroupId(id): false; +} + +void ResourceGroup::insertId(const QString &id) { + if (m_project) + m_project->insertResourceGroupId(id, this); +} + +Appointment ResourceGroup::appointmentIntervals() const { + Appointment a; + QPtrListIterator<Resource> it = m_resources; + for (; it.current(); ++it) { + a += it.current()->appointmentIntervals(); + } + return a; +} + +Resource::Resource(Project *project) : m_project(project), m_schedules(), m_workingHours() { + m_type = Type_Work; + m_units = 100; // % + + m_availableFrom = DateTime(QDate::currentDate()); + m_availableUntil = m_availableFrom.addYears(2); + + cost.normalRate = 100; + cost.overtimeRate = 200; + cost.fixed = 0; + m_calendar = 0; + m_currentSchedule = 0; + generateId(); + //kdDebug()<<k_funcinfo<<"("<<this<<")"<<endl; +} + +Resource::Resource(Resource *resource) { + //kdDebug()<<k_funcinfo<<"("<<this<<") from ("<<resource<<")"<<endl; + copy(resource); +} + +Resource::~Resource() { + //kdDebug()<<k_funcinfo<<"("<<this<<")"<<endl; + if (findId() == this) { + removeId(); // only remove myself (I may be just a working copy) + } + QPtrListIterator<ResourceRequest> it = m_requests; + for (; it.current(); ++it) { + it.current()->setResource(0); // avoid the request to mess with my list + } + m_requests.first(); + for (; m_requests.current(); m_requests.next()) { + m_requests.current()->parent()->removeResourceRequest(m_requests.current()); // deletes the request + } +} + +bool Resource::setId(QString id) { + //kdDebug()<<k_funcinfo<<id<<endl; + if (id.isEmpty()) { + kdError()<<k_funcinfo<<"id is empty"<<endl; + m_id = id; + return false; + } + Resource *r = findId(); + if (r == this) { + //kdDebug()<<k_funcinfo<<"My id found, remove it"<<endl; + removeId(); + } else if (r) { + //Hmmm, shouldn't happen + kdError()<<k_funcinfo<<"My id '"<<m_id<<"' already used for different resource: "<<r->name()<<endl; + } + if (findId(id)) { + kdError()<<k_funcinfo<<"id '"<<id<<"' is already used for different resource: "<<findId(id)->name()<<endl; + m_id = QString(); // hmmm + return false; + } + m_id = id; + insertId(id); + //kdDebug()<<k_funcinfo<<m_name<<": inserted id="<<id<<endl; + return true; +} + +void Resource::generateId() { + if (!m_id.isEmpty()) { + removeId(); + } + for (int i=0; i<32000 ; ++i) { + m_id = m_id.setNum(i); + if (!findId()) { + insertId(m_id); + //kdDebug()<<k_funcinfo<<m_name<<": inserted id="<<m_id<<endl; + return; + } + } + m_id = QString(); +} + +void Resource::setType(const QString &type) { + if (type == "Work") + m_type = Type_Work; + else if (type == "Material") + m_type = Type_Material; +} + +QString Resource::typeToString() const { + if (m_type == Type_Work) + return QString("Work"); + else if (m_type == Type_Material) + return QString("Material"); + + return QString(); +} + +void Resource::copy(Resource *resource) { + m_project = resource->project(); + //m_appointments = resource->appointments(); // Note + m_id = resource->id(); + m_name = resource->name(); + m_initials = resource->initials(); + m_email = resource->email(); + m_availableFrom = resource->availableFrom(); + m_availableUntil = resource->availableUntil(); + m_workingHours.clear(); + m_workingHours = resource->workingHours(); + + m_units = resource->units(); // available units in percent + + m_type = resource->type(); + + cost.normalRate = resource->normalRate(); + cost.overtimeRate = resource->overtimeRate(); + cost.fixed = resource->fixedCost(); + + m_calendar = resource->m_calendar; +} + +void Resource::addWorkingHour(QTime from, QTime until) { + //kdDebug()<<k_funcinfo<<endl; + m_workingHours.append(new QTime(from)); + m_workingHours.append(new QTime(until)); +} + +Calendar *Resource::calendar(bool local) const { + if (!local && project() != 0 && (m_calendar == 0 || m_calendar->isDeleted())) { + return project()->defaultCalendar(); + } + if (m_calendar && m_calendar->isDeleted()) { + return 0; + } + return m_calendar; +} + +DateTime Resource::getFirstAvailableTime(DateTime /*after*/) { + return DateTime(); +} + +DateTime Resource::getBestAvailableTime(Duration /*duration*/) { + return DateTime(); +} + +DateTime Resource::getBestAvailableTime(const DateTime /*after*/, const Duration /*duration*/) { + return DateTime(); +} + +bool Resource::load(QDomElement &element) { + //kdDebug()<<k_funcinfo<<endl; + QString s; + setId(element.attribute("id")); + m_name = element.attribute("name"); + m_initials = element.attribute("initials"); + m_email = element.attribute("email"); + setType(element.attribute("type")); + m_calendar = findCalendar(element.attribute("calendar-id")); + m_units = element.attribute("units", "100").toInt(); + s = element.attribute("available-from"); + if (s != "") + m_availableFrom = DateTime::fromString(s); + s = element.attribute("available-until"); + if (s != "") + m_availableUntil = DateTime::fromString(s); + + cost.normalRate = KGlobal::locale()->readMoney(element.attribute("normal-rate")); + cost.overtimeRate = KGlobal::locale()->readMoney(element.attribute("overtime-rate")); + return true; +} + +void Resource::save(QDomElement &element) const { + //kdDebug()<<k_funcinfo<<endl; + QDomElement me = element.ownerDocument().createElement("resource"); + element.appendChild(me); + + if (calendar(true)) + me.setAttribute("calendar-id", m_calendar->id()); + me.setAttribute("id", m_id); + me.setAttribute("name", m_name); + me.setAttribute("initials", m_initials); + me.setAttribute("email", m_email); + me.setAttribute("type", typeToString()); + me.setAttribute("units", m_units); + me.setAttribute("available-from", m_availableFrom.toString(Qt::ISODate)); + me.setAttribute("available-until", m_availableUntil.toString(Qt::ISODate)); + me.setAttribute("normal-rate", KGlobal::locale()->formatMoney(cost.normalRate)); + me.setAttribute("overtime-rate", KGlobal::locale()->formatMoney(cost.overtimeRate)); +} + +bool Resource::isAvailable(Task */*task*/) { + bool busy = false; +/* QPtrListIterator<Appointment> it(m_appointments); + for (; it.current(); ++it) { + if (it.current()->isBusy(task->startTime(), task->endTime())) { + busy = true; + break; + } + }*/ + return !busy; +} + +QPtrList<Appointment> Resource::appointments() { + QPtrList<Appointment> lst; + if (m_currentSchedule) + lst = m_currentSchedule->appointments(); + //kdDebug()<<k_funcinfo<<lst.count()<<endl; + return lst; +} + +Appointment *Resource::findAppointment(Node */*node*/) { +/* QPtrListIterator<Appointment> it = m_appointments; + for (; it.current(); ++it) { + if (it.current()->node() == node) + return it.current(); + }*/ + return 0; +} + +bool Resource::addAppointment(Appointment *appointment) { + if (m_currentSchedule) + return m_currentSchedule->add(appointment); + return false; +} + +bool Resource::addAppointment(Appointment *appointment, Schedule &main) { + Schedule *s = findSchedule(main.id()); + if (s == 0) { + s = createSchedule(&main); + } + appointment->setResource(s); + return s->add(appointment); +} + +void Resource::addAppointment(Schedule *node, DateTime &start, DateTime &end, double load) { + //kdDebug()<<k_funcinfo<<endl; + Schedule *s = findSchedule(node->id()); + if (s == 0) { + s = createSchedule(node->parent()); + } + s->addAppointment(node, start, end, load); +} + +void Resource::initiateCalculation(Schedule &sch) { + m_currentSchedule = createSchedule(&sch); +} + +void Resource::removeSchedule(Schedule *schedule) { + takeSchedule(schedule); + delete schedule; +} + +void Resource::takeSchedule(const Schedule *schedule) { + if (schedule == 0) + return; + if (m_currentSchedule == schedule) + m_currentSchedule = 0; + m_schedules.take(schedule->id()); +} + +void Resource::addSchedule(Schedule *schedule) { + if (schedule == 0) + return; + m_schedules.replace(schedule->id(), schedule); +} + +ResourceSchedule *Resource::createSchedule(QString name, int type, long id) { + ResourceSchedule *sch = new ResourceSchedule(this, name, (Schedule::Type)type, id); + addSchedule(sch); + return sch; +} + +ResourceSchedule *Resource::createSchedule(Schedule *parent) { + ResourceSchedule *sch = new ResourceSchedule(parent, this); + addSchedule(sch); + return sch; +} + +void Resource::makeAppointment(Schedule *node, const DateTime &from, const DateTime &end) { + if (!from.isValid() || !end.isValid()) { + kdWarning()<<k_funcinfo<<"Invalid time"<<endl; + return; + } + Calendar *cal = calendar(); + if (cal == 0) { + return; + } + DateTime time = from; + while (time < end) { + //kdDebug()<<k_funcinfo<<time.toString()<<" to "<<end.toString()<<endl; + if (!time.isValid() || !end.isValid()) { + kdWarning()<<k_funcinfo<<"Invalid time"<<endl; + return; + } + if (!cal->hasInterval(time, end)) { + //kdDebug()<<time.toString()<<" to "<<end.toString()<<": No (more) interval(s)"<<endl; + kdWarning()<<k_funcinfo<<m_name<<": Resource only partially available"<<endl; + //node->resourceNotAvailable = true; + return; // nothing more to do + } + QPair<DateTime, DateTime> i = cal->firstInterval(time, end); + if (!i.second.isValid()) { + kdWarning()<<k_funcinfo<<"Invalid interval: "<<time<<", "<<end<<endl; + return; + } + if (time == i.second) + return; // hmmm, didn't get a new interval, avoid loop + addAppointment(node, i.first, i.second, m_units); + //kdDebug()<<k_funcinfo<<"Add :"<<i.first.toString()<<" to "<<i.second.toString()<<endl; + if (!(node->workStartTime.isValid()) || i.first < node->workStartTime) + node->workStartTime = i.first; + if (!(node->workEndTime.isValid()) || i.second > node->workEndTime) + node->workEndTime = i.second; + time = i.second; + } + return; +} +void Resource::makeAppointment(Schedule *node) { + //kdDebug()<<k_funcinfo<<m_name<< ": "<<node->node()->name()<<": "<<node->startTime.toString()<<" dur "<<node->duration.toString()<<endl; + if (!node->startTime.isValid()) { + kdWarning()<<k_funcinfo<<m_name<<": startTime invalid"<<endl; + return; + } + if (!node->endTime.isValid()) { + kdWarning()<<k_funcinfo<<m_name<<": endTime invalid"<<endl; + return; + } + Calendar *cal = calendar(); + if (m_type == Type_Material) { + DateTime from = availableAfter(node->startTime, node->endTime); + DateTime end = availableBefore(node->endTime, node->startTime); + if (!from.isValid() || !end.isValid()) { + return; + } + if (cal == 0) { + // Allocate the whole period + addAppointment(node, from, end, m_units); + return; + } + makeAppointment(node, from, end); + } + if (!cal) { + kdWarning()<<k_funcinfo<<m_name<<": No calendar defined"<<endl; + return; + } + //TODO: units and standard non-working days + DateTime time = node->startTime; + DateTime end = node->endTime; + time = availableAfter(time, end); + if (!time.isValid()) { + kdWarning()<<k_funcinfo<<m_name<<": Resource not available (after="<<node->startTime<<", "<<end<<")"<<endl; + node->resourceNotAvailable = true; + return; + } + end = availableBefore(end, time); + if (!end.isValid()) { + kdWarning()<<k_funcinfo<<m_name<<": Resource not available (before="<<node->endTime<<", "<<time<<")"<<endl; + node->resourceNotAvailable = true; + return; + } + //kdDebug()<<k_funcinfo<<time.toString()<<" to "<<end.toString()<<endl; + makeAppointment(node, time, end); +} + +// the amount of effort we can do within the duration +Duration Resource::effort(const DateTime &start, const Duration &duration, bool backward, bool *ok) const { + //kdDebug()<<k_funcinfo<<m_name<<": "<<start.date().toString()<<" for duration "<<duration.toString(Duration::Format_Day)<<endl; + bool sts=false; + Duration e; + if (duration == 0) { + kdWarning()<<k_funcinfo<<"zero duration"<<endl; + return e; + } + Calendar *cal = calendar(); + if (cal == 0) { + kdWarning()<<k_funcinfo<<m_name<<": No calendar defined"<<endl; + return e; + } + if (backward) { + DateTime limit = start - duration; + DateTime t = availableBefore(start, limit); + if (t.isValid()) { + sts = true; + e = (cal->effort(limit, t) * m_units)/100; + } else { + //kdDebug()<<k_funcinfo<<m_name<<": Not available (start="<<start<<", "<<limit<<")"<<endl; + } + } else { + DateTime limit = start + duration; + DateTime t = availableAfter(start, limit); + if (t.isValid()) { + sts = true; + e = (cal->effort(t, limit) * m_units)/100; + } + } + //kdDebug()<<k_funcinfo<<start.toString()<<" e="<<e.toString(Duration::Format_Day)<<" ("<<m_units<<")"<<endl; + if (ok) *ok = sts; + return e; +} + +DateTime Resource::availableAfter(const DateTime &time, const DateTime limit, bool checkAppointments) const { + DateTime t; + if (m_units == 0) { + return t; + } + DateTime lmt = m_availableUntil; + if (limit.isValid() && limit < lmt) { + lmt = limit; + } + if (time >= lmt) { + return t; + } + if (type() == Type_Material) { + t = time > m_availableFrom ? time : m_availableFrom; + //kdDebug()<<k_funcinfo<<time.toString()<<"="<<t.toString()<<" "<<m_name<<endl; + return t; + } + Calendar *cal = calendar(); + if (cal == 0) { + return t; + } + t = m_availableFrom > time ? m_availableFrom : time; + t = cal->firstAvailableAfter(t, lmt); + if (checkAppointments) { + //TODO + } + //kdDebug()<<k_funcinfo<<time.toString()<<"="<<t.toString()<<" "<<m_name<<endl; + return t; +} + +DateTime Resource::availableBefore(const DateTime &time, const DateTime limit, bool checkAppointments) const { + DateTime t; + if (m_units == 0) { + return t; + } + DateTime lmt = m_availableFrom; + if (limit.isValid() && limit > lmt) { + lmt = limit; + } + if (time <= lmt) { + return t; + } + if (type() == Type_Material) { + t = time < m_availableUntil ? time : m_availableUntil; + //kdDebug()<<k_funcinfo<<time.toString()<<"="<<t.toString()<<" "<<m_name<<endl; + return t; + } + Calendar *cal = calendar(); + if (cal == 0) { + return t; + } + if (!m_availableUntil.isValid()) { + kdWarning()<<k_funcinfo<<m_name<<": availabelUntil is invalid"<<endl; + t = time; + } else { + t = m_availableUntil < time ? m_availableUntil : time; + } + //kdDebug()<<k_funcinfo<<t<<", "<<lmt<<endl; + t = cal->firstAvailableBefore(t, lmt); + if (checkAppointments) { + //TODO + } + //kdDebug()<<k_funcinfo<<m_name<<" returns: "<<time<<"="<<t<<" "<<endl; + return t; +} + +Resource *Resource::findId(const QString &id) const { + return m_project ? m_project->findResource(id) : 0; +} + +bool Resource::removeId(const QString &id) { + return m_project ? m_project->removeResourceId(id) : false; +} + +void Resource::insertId(const QString &id) { + if (m_project) + m_project->insertResourceId(id, this); +} + +Calendar *Resource::findCalendar(const QString &id) const { + return (m_project ? m_project->findCalendar(id) : 0); +} + +bool Resource::isOverbooked() const { + return isOverbooked(DateTime(), DateTime()); +} + +bool Resource::isOverbooked(const QDate &date) const { + return isOverbooked(DateTime(date), DateTime(date.addDays(1))); +} + +bool Resource::isOverbooked(const DateTime &start, const DateTime &end) const { + //kdDebug()<<k_funcinfo<<m_name<<": "<<start.toString()<<" - "<<end.toString()<<" cs=("<<m_currentSchedule<<")"<<endl; + return m_currentSchedule ? m_currentSchedule->isOverbooked(start, end) : false; +} + +Appointment Resource::appointmentIntervals() const { + Appointment a; + if (m_currentSchedule == 0) + return a; + QPtrListIterator<Appointment> it = m_currentSchedule->appointments(); + for (; it.current(); ++it) { + a += *(it.current()); + } + return a; +} + +Duration Resource::plannedEffort(const QDate &date) const { + return m_currentSchedule ? m_currentSchedule->plannedEffort(date) : Duration::zeroDuration; +} + +///////// Risk ///////// +Risk::Risk(Node *n, Resource *r, RiskType rt) { + m_node=n; + m_resource=r; + m_riskType=rt; +} + +Risk::~Risk() { +} + +ResourceRequest::ResourceRequest(Resource *resource, int units) + : m_resource(resource), + m_units(units), + m_parent(0) { + //kdDebug()<<k_funcinfo<<"("<<this<<") Request to: "<<(resource ? resource->name() : QString("None"))<<endl; +} + +ResourceRequest::~ResourceRequest() { + //kdDebug()<<k_funcinfo<<"("<<this<<") resource: "<<(m_resource ? m_resource->name() : QString("None"))<<endl; + if (m_resource) + m_resource->unregisterRequest(this); + m_resource = 0; +} + +bool ResourceRequest::load(QDomElement &element, Project &project) { + //kdDebug()<<k_funcinfo<<endl; + m_resource = project.resource(element.attribute("resource-id")); + if (m_resource == 0) { + kdWarning()<<k_funcinfo<<"The referenced resource does not exist: resource id="<<element.attribute("resource-id")<<endl; + return false; + } + m_units = element.attribute("units").toInt(); + return true; +} + +void ResourceRequest::save(QDomElement &element) const { + QDomElement me = element.ownerDocument().createElement("resource-request"); + element.appendChild(me); + me.setAttribute("resource-id", m_resource->id()); + me.setAttribute("units", m_units); +} + +int ResourceRequest::units() const { + //kdDebug()<<k_funcinfo<<m_resource->name()<<": units="<<m_units<<endl; + return m_units; +} + +int ResourceRequest::workUnits() const { + if (m_resource->type() == Resource::Type_Work) + return units(); + + //kdDebug()<<k_funcinfo<<"units=0"<<endl; + return 0; +} + +Task *ResourceRequest::task() const { + return m_parent ? m_parent->task() : 0; +} + +///////// +ResourceGroupRequest::ResourceGroupRequest(ResourceGroup *group, int units) + : m_group(group), m_units(units) { + + //kdDebug()<<k_funcinfo<<"Request to: "<<(group ? group->name() : QString("None"))<<endl; + if (group) + group->registerRequest(this); + m_resourceRequests.setAutoDelete(true); +} + +ResourceGroupRequest::~ResourceGroupRequest() { + //kdDebug()<<k_funcinfo<<"Group: "<<m_group->name()<<endl; + if (m_group) + m_group->unregisterRequest(this); + m_resourceRequests.clear(); +} + +void ResourceGroupRequest::addResourceRequest(ResourceRequest *request) { + //kdDebug()<<k_funcinfo<<"("<<request<<") to Group: "<<m_group->name()<<endl; + request->setParent(this); + m_resourceRequests.append(request); + request->registerRequest(); +} + +ResourceRequest *ResourceGroupRequest::takeResourceRequest(ResourceRequest *request) { + if (request) + request->unregisterRequest(); + return m_resourceRequests.take(m_resourceRequests.findRef(request)); +} + +ResourceRequest *ResourceGroupRequest::find(Resource *resource) const { + QPtrListIterator<ResourceRequest> it(m_resourceRequests); + for (; it.current(); ++it) + if (it.current()->resource() == resource) + return it.current(); + + return 0; +} + +bool ResourceGroupRequest::load(QDomElement &element, Project &project) { + //kdDebug()<<k_funcinfo<<endl; + m_group = project.findResourceGroup(element.attribute("group-id")); + if (m_group == 0) { + //kdDebug()<<k_funcinfo<<"The referenced resource group does not exist: group id="<<element.attribute("group-id")<<endl; + return false; + } + m_group->registerRequest(this); + + m_units = element.attribute("units").toInt(); + + QDomNodeList list = element.childNodes(); + for (unsigned int i=0; i<list.count(); ++i) { + if (list.item(i).isElement()) { + QDomElement e = list.item(i).toElement(); + if (e.tagName() == "resource-request") { + ResourceRequest *r = new ResourceRequest(); + if (r->load(e, project)) + addResourceRequest(r); + else { + kdError()<<k_funcinfo<<"Failed to load resource request"<<endl; + delete r; + } + } + } + } + return true; +} + +void ResourceGroupRequest::save(QDomElement &element) const { + if (units() == 0) + return; + QDomElement me = element.ownerDocument().createElement("resourcegroup-request"); + element.appendChild(me); + me.setAttribute("group-id", m_group->id()); + me.setAttribute("units", m_units); + QPtrListIterator<ResourceRequest> it(m_resourceRequests); + for (; it.current(); ++it) + it.current()->save(me); +} + +int ResourceGroupRequest::units() const { + int units = m_units; + QPtrListIterator<ResourceRequest> it = m_resourceRequests; + for (; it.current(); ++it) { + units += it.current()->units(); + } + //kdDebug()<<k_funcinfo<<"units="<<units<<endl; + return units; +} + +int ResourceGroupRequest::workUnits() const { + int units = 0; + if (m_group->type() == ResourceGroup::Type_Work) + units = m_units; + QPtrListIterator<ResourceRequest> it = m_resourceRequests; + for (; it.current(); ++it) { + units += it.current()->workUnits(); + } + //kdDebug()<<k_funcinfo<<"units="<<units<<endl; + return units; +} + +//TODO: handle nonspecific resources +Duration ResourceGroupRequest::effort(const DateTime &time, const Duration &duration, bool backward, bool *ok) const { + Duration e; + bool sts=false; + if (ok) *ok = sts; + QPtrListIterator<ResourceRequest> it = m_resourceRequests; + for (; it.current(); ++it) { + e += it.current()->resource()->effort(time, duration, backward, &sts); + if (sts && ok) *ok = sts; + //kdDebug()<<k_funcinfo<<(backward?"(B)":"(F)" )<<it.current()->resource()->name()<<": time="<<time<<" dur="<<duration.toString()<<"gave e="<<e.toString()<<endl; + } + //kdDebug()<<k_funcinfo<<time.toString()<<"d="<<duration.toString()<<": e="<<e.toString()<<endl; + return e; +} + +int ResourceGroupRequest::numDays(const DateTime &time, bool backward) const { + DateTime t1, t2 = time; + if (backward) { + QPtrListIterator<ResourceRequest> it = m_resourceRequests; + for (; it.current(); ++it) { + t1 = it.current()->resource()->availableFrom(); + if (!t2.isValid() || t2 > t1) + t2 = t1; + } + //kdDebug()<<k_funcinfo<<"bw "<<time.toString()<<": "<<t2.daysTo(time)<<endl; + return t2.daysTo(time); + } + QPtrListIterator<ResourceRequest> it = m_resourceRequests; + for (; it.current(); ++it) { + t1 = it.current()->resource()->availableUntil(); + if (!t2.isValid() || t2 < t1) + t2 = t1; + } + //kdDebug()<<k_funcinfo<<"fw "<<time.toString()<<": "<<time.daysTo(t2)<<endl; + return time.daysTo(t2); +} + +Duration ResourceGroupRequest::duration(const DateTime &time, const Duration &_effort, bool backward) { + //kdDebug()<<k_funcinfo<<"--->"<<(backward?"(B) ":"(F) ")<<m_group->name()<<" "<<time.toString()<<": effort: "<<_effort.toString(Duration::Format_Day)<<" ("<<_effort.milliseconds()<<")"<<endl; + Duration e; + if (_effort == Duration::zeroDuration) { + return e; + } + bool sts=true; + bool match = false; + DateTime start = time; + int inc = backward ? -1 : 1; + DateTime end = start; + Duration e1; + Duration d(1, 0, 0); // 1 day + int nDays = numDays(time, backward) + 1; + for (int i=0; !match && i <= nDays; ++i) { + // days + end = end.addDays(inc); + e1 = effort(start, d, backward, &sts); + //kdDebug()<<"["<<i<<"of"<<nDays<<"] "<<(backward?"(B)":"(F):")<<" start="<<start<<" e+e1="<<(e+e1).toString()<<" match "<<_effort.toString()<<endl; + if (e + e1 < _effort) { + e += e1; + start = end; + } else if (e + e1 == _effort) { + e += e1; + match = true; + } else { + end = start; + break; + } + } + //kdDebug()<<"duration "<<(backward?"backward ":"forward: ")<<start.toString()<<" - "<<end.toString()<<" e="<<e.toString()<<" ("<<e.milliseconds()<<") match="<<match<<" sts="<<sts<<endl; + d = Duration(0, 1, 0); // 1 hour + for (int i=0; !match && i < 24; ++i) { + // hours + end = end.addSecs(inc*60*60); + e1 = effort(start, d, backward, &sts); + if (e + e1 < _effort) { + e += e1; + start = end; + } else if (e + e1 == _effort) { + e += e1; + match = true; + } else { + end = start; + break; + } + //kdDebug()<<"duration(h)["<<i<<"]"<<(backward?"backward ":"forward:")<<" time="<<start.time().toString()<<" e="<<e.toString()<<" ("<<e.milliseconds()<<")"<<endl; + } + //kdDebug()<<"duration "<<(backward?"backward ":"forward: ")<<start.toString()<<" e="<<e.toString()<<" ("<<e.milliseconds()<<") match="<<match<<" sts="<<sts<<endl; + d = Duration(0, 0, 1); // 1 minute + for (int i=0; !match && i < 60; ++i) { + //minutes + end = end.addSecs(inc*60); + e1 = effort(start, d, backward, &sts); + if (e + e1 < _effort) { + e += e1; + start = end; + } else if (e + e1 == _effort) { + e += e1; + match = true; + } else if (e + e1 > _effort) { + end = start; + break; + } + //kdDebug()<<"duration(m) "<<(backward?"backward":"forward:")<<" time="<<start.time().toString()<<" e="<<e.toString()<<" ("<<e.milliseconds()<<")"<<endl; + } + //kdDebug()<<"duration "<<(backward?"backward":"forward:")<<" start="<<start.toString()<<" e="<<e.toString()<<" match="<<match<<" sts="<<sts<<endl; + d = Duration(0, 0, 0, 1); // 1 second + for (int i=0; !match && i < 60 && sts; ++i) { + //seconds + end = end.addSecs(inc); + e1 = effort(start, d, backward, &sts); + if (e + e1 < _effort) { + e += e1; + start = end; + } else if (e + e1 == _effort) { + e += e1; + match = true; + } else if (e + e1 > _effort) { + end = start; + break; + } + //kdDebug()<<"duration(s)["<<i<<"]"<<(backward?"backward":"forward:")<<" time="<<start.time().toString()<<" e="<<e.toString()<<" ("<<e.milliseconds()<<")"<<endl; + } + d = Duration(0, 0, 0, 0, 1); // 1 millisecond + for (int i=0; !match && i < 1000; ++i) { + //milliseconds + end.setTime(end.time().addMSecs(inc)); + e1 = effort(start, d, backward, &sts); + if (e + e1 < _effort) { + e += e1; + start = end; + } else if (e + e1 == _effort) { + e += e1; + match = true; + } else if (e + e1 > _effort) { + break; + } + //kdDebug()<<"duration(ms)["<<i<<"]"<<(backward?"backward":"forward:")<<" time="<<start.time().toString()<<" e="<<e.toString()<<" ("<<e.milliseconds()<<")"<<endl; + } + if (!match) { + kdError()<<k_funcinfo<<(task()?task()->name():"No task")<<" "<<time<<": Could not match effort."<<" Want: "<<_effort.toString(Duration::Format_Day)<<" got: "<<e.toString(Duration::Format_Day)<<" sts="<<sts<<endl; + } + DateTime t; + if (e != Duration::zeroDuration) { + t = backward ? availableAfter(end) : availableBefore(end); + } + end = t.isValid() ? t : time; + //kdDebug()<<k_funcinfo<<"<---"<<(backward?"(B) ":"(F) ")<<m_group->name()<<": "<<end.toString()<<"-"<<time.toString()<<"="<<(end - time).toString()<<" effort: "<<_effort.toString(Duration::Format_Day)<<endl; + return (end>time?end-time:time-end); +} + +DateTime ResourceGroupRequest::availableAfter(const DateTime &time) { + DateTime start; + QPtrListIterator<ResourceRequest> it = m_resourceRequests; + for (; it.current(); ++it) { + DateTime t = it.current()->resource()->availableAfter(time); + if (t.isValid() && (!start.isValid() || t < start)) + start = t; + } + if (start.isValid() && start < time) + start = time; + //kdDebug()<<k_funcinfo<<time.toString()<<"="<<start.toString()<<" "<<m_group->name()<<endl; + return start; +} + +DateTime ResourceGroupRequest::availableBefore(const DateTime &time) { + DateTime end; + QPtrListIterator<ResourceRequest> it = m_resourceRequests; + for (; it.current(); ++it) { + DateTime t = it.current()->resource()->availableBefore(time); + if (t.isValid() && (!end.isValid() || t > end)) + end = t; + } + if (!end.isValid() || end > time) + end = time; + //kdDebug()<<k_funcinfo<<time.toString()<<"="<<end.toString()<<" "<<m_group->name()<<endl; + return end; +} + +void ResourceGroupRequest::makeAppointments(Schedule *schedule) { + //kdDebug()<<k_funcinfo<<endl; + QPtrListIterator<ResourceRequest> it = m_resourceRequests; + for (; it.current(); ++it) { + it.current()->makeAppointment(schedule); + } +} + +void ResourceGroupRequest::reserve(const DateTime &start, const Duration &duration) { + m_start = start; + m_duration = duration; +} + +bool ResourceGroupRequest::isEmpty() const { + return m_resourceRequests.isEmpty() && m_units == 0; +} + +Task *ResourceGroupRequest::task() const { + return m_parent ? &(m_parent->task()) : 0; +} + +///////// +ResourceRequestCollection::ResourceRequestCollection(Task &task) + : m_task(task) { + m_requests.setAutoDelete(true); +} + +ResourceRequestCollection::~ResourceRequestCollection() { + //kdDebug()<<k_funcinfo<<"Group: "<<m_group->name()<<endl; + m_requests.clear(); +} + +ResourceGroupRequest *ResourceRequestCollection::find(ResourceGroup *group) const { + QPtrListIterator<ResourceGroupRequest> it(m_requests); + for (; it.current(); ++it) { + if (it.current()->group() == group) + return it.current(); // we assume only one request to the same group + } + return 0; +} + + +ResourceRequest *ResourceRequestCollection::find(Resource *resource) const { + ResourceRequest *req = 0; + QPtrListIterator<ResourceGroupRequest> it(m_requests); + for (; !req && it.current(); ++it) { + req = it.current()->find(resource); + } + return req; +} + +// bool ResourceRequestCollection::load(QDomElement &element, Project &project) { +// //kdDebug()<<k_funcinfo<<endl; +// return true; +// } + +void ResourceRequestCollection::save(QDomElement &element) const { + //kdDebug()<<k_funcinfo<<endl; + QPtrListIterator<ResourceGroupRequest> it(m_requests); + for ( ; it.current(); ++it ) { + it.current()->save(element); + } +} + +int ResourceRequestCollection::units() const { + //kdDebug()<<k_funcinfo<<endl; + int units = 0; + QPtrListIterator<ResourceGroupRequest> it = m_requests; + for (; it.current(); ++it) { + units += it.current()->units(); + //kdDebug()<<k_funcinfo<<" Group: "<<it.current()->group()->name()<<" now="<<units<<endl; + } + return units; +} + +int ResourceRequestCollection::workUnits() const { + //kdDebug()<<k_funcinfo<<endl; + int units = 0; + QPtrListIterator<ResourceGroupRequest> it(m_requests); + for (; it.current(); ++it) { + units += it.current()->workUnits(); + } + //kdDebug()<<k_funcinfo<<" units="<<units<<endl; + return units; +} + +// Returns the longest duration needed by any of the groups. +// The effort is distributed on "work type" resourcegroups in proportion to +// the amount of resources requested for each group. +// "Material type" of resourcegroups does not (atm) affect the duration. +Duration ResourceRequestCollection::duration(const DateTime &time, const Duration &effort, bool backward) { + //kdDebug()<<k_funcinfo<<"time="<<time.toString()<<" effort="<<effort.toString(Duration::Format_Day)<<" backward="<<backward<<endl; + if (isEmpty()) { + return effort; + } + Duration dur; + int units = workUnits(); + if (units == 0) + units = 100; //hmmmm + QPtrListIterator<ResourceGroupRequest> it(m_requests); + for (; it.current(); ++it) { + if (it.current()->isEmpty()) + continue; + if (it.current()->group()->type() == ResourceGroup::Type_Work) { + Duration d = it.current()->duration(time, (effort*it.current()->workUnits())/units, backward); + if (d > dur) + dur = d; + } else if (it.current()->group()->type() == ResourceGroup::Type_Material) { + //TODO + if (dur == Duration::zeroDuration) + dur = effort; + } + } + return dur; +} + +DateTime ResourceRequestCollection::availableAfter(const DateTime &time) { + DateTime start; + QPtrListIterator<ResourceGroupRequest> it = m_requests; + for (; it.current(); ++it) { + DateTime t = it.current()->availableAfter(time); + if (t.isValid() && (!start.isValid() || t < start)) + start = t; + } + if (start.isValid() && start < time) + start = time; + //kdDebug()<<k_funcinfo<<time.toString()<<"="<<start.toString()<<endl; + return start; +} + +DateTime ResourceRequestCollection::availableBefore(const DateTime &time) { + DateTime end; + QPtrListIterator<ResourceGroupRequest> it = m_requests; + for (; it.current(); ++it) { + DateTime t = it.current()->availableBefore(time); + if (t.isValid() && (!end.isValid() ||t > end)) + end = t; + } + if (!end.isValid() || end > time) + end = time; + return end; +} + + +void ResourceRequestCollection::makeAppointments(Schedule *schedule) { + //kdDebug()<<k_funcinfo<<endl; + QPtrListIterator<ResourceGroupRequest> it(m_requests); + for (; it.current(); ++it) { + it.current()->makeAppointments(schedule); + } +} + +void ResourceRequestCollection::reserve(const DateTime &start, const Duration &duration) { + //kdDebug()<<k_funcinfo<<endl; + QPtrListIterator<ResourceGroupRequest> it(m_requests); + for (; it.current(); ++it) { + it.current()->reserve(start, duration); + } +} + +bool ResourceRequestCollection::isEmpty() const { + QPtrListIterator<ResourceGroupRequest> it(m_requests); + for (; it.current(); ++it) { + if (!it.current()->isEmpty()) + return false; + } + return true; +} +#ifndef NDEBUG + +void ResourceGroup::printDebug(QString indent) +{ + kdDebug()<<indent<<" + Resource group: "<<m_name<<" id="<<m_id<<endl; + indent += " !"; + QPtrListIterator<Resource> it(m_resources); + for ( ; it.current(); ++it) + it.current()->printDebug(indent); +} +void Resource::printDebug(QString indent) +{ + kdDebug()<<indent<<" + Resource: "<<m_name<<" id="<<m_id/*<<" Overbooked="<<isOverbooked()*/<<endl; + QIntDictIterator<Schedule> it(m_schedules); + indent += " "; + for (; it.current(); ++it) { + it.current()->printDebug(indent); + } + indent += " !"; +} + +void ResourceGroupRequest::printDebug(QString indent) +{ + kdDebug()<<indent<<" + Request to group: "<<(m_group ? m_group->name() : "None")<<" units="<<m_units<<"%"<<endl; + indent += " !"; + QPtrListIterator<ResourceRequest> it(m_resourceRequests); + for (; it.current(); ++it) { + it.current()->printDebug(indent); + } +} + +void ResourceRequest::printDebug(QString indent) +{ + kdDebug()<<indent<<" + Request to resource: "<<(m_resource ? m_resource->name() : "None")<<" units="<<m_units<<"%"<<endl; +} + +void ResourceRequestCollection::printDebug(QString indent) +{ + kdDebug()<<indent<<" + Resource requests:"<<endl; + QPtrListIterator<ResourceGroupRequest> it = m_requests; + for (; it.current(); ++it) { + it.current()->printDebug(indent+" "); + } +} +#endif + +} //KPlato namespace diff --git a/kplato/kptresource.h b/kplato/kptresource.h new file mode 100644 index 00000000..4d160321 --- /dev/null +++ b/kplato/kptresource.h @@ -0,0 +1,579 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Thomas Zander zander@kde.org + Copyright (C) 2004, 2005 Dag Andersen <danders@get2net.dk> + + 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 KPTRESOURCE_H +#define KPTRESOURCE_H + +#include "kptduration.h" +#include "kptdatetime.h" + +#include <qdom.h> +#include <qintdict.h> +#include <qstring.h> +#include <qptrlist.h> + +#include <kdebug.h> + +class QTime; + +namespace KPlato +{ + +class Risk; +class Effort; +class Appointment; +class Task; +class Node; +class Project; +class Resource; +class ResourceRequest; +class ResourceGroupRequest; +class Calendar; +class ResourceRequestCollection; +class EffortCostMap; +class Schedule; +class ResourceSchedule; +class Schedule; + +/** + * This class represents a group of similar resources to be assigned to a task + * e.g. The list of employees, computer resources, etc + */ + +/* IDEA; lets create a resourceGroup that has the intelligence to import PIM schedules + * from the kroupware project and use the schedules to use the factory pattern to build + * Resources (probably a derived class) which returns values on getFirstAvailableTime + * and friends based on the schedules we got from the PIM projects. + * (Thomas Zander mrt-2003 by suggestion of Shaheed) + */ +class ResourceGroup { + public: + ResourceGroup(Project *project); + ~ResourceGroup(); + + enum Type { Type_Work, Type_Material }; + + QString id() const { return m_id; } + bool setId(QString id); + void generateId(); + + Project *project() { return m_project; } + + void setName(QString n) {m_name=n;} + const QString &name() const {return m_name;} + void setType(Type type) { m_type = type; } + //void setType(const QString &type); + Type type() const { return m_type; } + + /** Manage the resources in this list + * <p>At some point we will have to look at not mixing types of resources + * (e.g. you can't add a person to a list of computers + * + * <p>Risks must always be associated with a resource, so there is no option + * to manipulate risks (@ref Risk) seperately + */ + void addResource(Resource*, Risk*); + void insertResource( unsigned int index, Resource *resource ); + void removeResource( Resource *resource ); + Resource *takeResource( Resource *resource ); + void removeResource(int); + + Resource* getResource(int); + Risk* getRisk(int); + + /** Get the "num" resources which is available in the time frame + * defined by "start" and "duration". + * @param start todo + * @param duration todo + * @param num todo + */ + QPtrList<Resource> availableResources(const DateTime start, const Duration duration, int num); + /** Manage the dependent resources. This is a list of the resource + * groups that must have available resources for this resource to + * perform the work + * <p>see also @ref getRequiredResource, @ref getRequiredResource + */ + void addRequiredResource(ResourceGroup*); + /** Manage the dependent resources. This is a list of the resource + * groups that must have available resources for this resource to + * perform the work + * <p>see also @ref addRequiredResource, @ref getRequiredResource + */ + ResourceGroup* getRequiredResource(int); + /** Manage the dependent resources. This is a list of the resource + * groups that must have available resources for this resource to + * perform the work + * <p>see also @ref getRequiredResource, @ref addRequiredResource + */ + void removeRequiredResource(int); + int numResources() const { return m_resources.count(); } + QPtrList<Resource> &resources() { return m_resources; } + + bool load(QDomElement &element); + void save(QDomElement &element) const; + + void initiateCalculation(Schedule &sch); + + void addNode(const Node *node) { m_nodes.append(node); } + void clearNodes() { m_nodes.clear(); } + + Calendar *defaultCalendar() { return m_defaultCalendar; } + + int units(); + + void registerRequest(ResourceGroupRequest *request) + { m_requests.append(request); } + void unregisterRequest(ResourceGroupRequest *request) + { m_requests.removeRef(request); } + const QPtrList<ResourceGroupRequest> &requests() const + { return m_requests; } + + ResourceGroup *findId() const { return findId(m_id); } + ResourceGroup *findId(const QString &id) const; + bool removeId() { return removeId(m_id); } + bool removeId(const QString &id); + void insertId(const QString &id); + + Appointment appointmentIntervals() const; + +#ifndef NDEBUG + void printDebug(QString ident); +#endif + + private: + Project *m_project; + QString m_id; // unique id + QString m_name; + QPtrList<Resource> m_resources; + QPtrList<Risk> m_risks; + QPtrList<ResourceGroup> m_requires; + + QPtrList<Node> m_nodes; //The nodes that want resources from us + + Calendar *m_defaultCalendar; + Type m_type; + + QPtrList<ResourceGroupRequest> m_requests; + +}; + +/** + * Any resource that is used by a task. A resource can be a worker, or maybe wood. + * If the resources is a worker or a piece of equiment which can be reused but + * can only be used by one node in time, then we can use the scheduling methods of the + * resource to schedule the resource available time for the project. + * The Idea is that all nodes which need this resource point to it and the scheduling + * code (partly implemented here) schedules the actual usage. + * See also @ref ResourceGroup + */ + +class Resource { +public: + + Resource(Project *project); + Resource(Resource *resource); + virtual ~Resource(); + + QString id() const { return m_id; } + bool setId(QString id); + void generateId(); + + enum Type { Type_Work, Type_Material }; + void setType(Type type) { m_type = type; } + void setType(const QString &type); + Type type() const { return m_type; } + QString typeToString() const; + + void setName(QString n) {m_name=n;} + const QString &name() const {return m_name;} + + void setInitials(QString initials) {m_initials=initials;} + const QString &initials() const {return m_initials;} + + void setEmail(QString email) {m_email=email;} + const QString &email() const {return m_email;} + + void copy(Resource *resource); + + /// Set the time from when the resource is available to this project + void setAvailableFrom(const QDateTime &af) {m_availableFrom=af;} + /// Return the time when the resource is available to this project + const DateTime &availableFrom() const {return m_availableFrom;} + /// Set the time when the resource is no longer available to this project + void setAvailableUntil(const QDateTime au) {m_availableUntil=au;} + /// Return the time when the resource is no longer available to this project. + const DateTime &availableUntil() const {return m_availableUntil;} + + void addWorkingHour(QTime from, QTime until); + QPtrList<QTime> workingHours() { return m_workingHours; } + + DateTime getFirstAvailableTime(DateTime after = DateTime()); + DateTime getBestAvailableTime(Duration duration); + DateTime getBestAvailableTime(const DateTime after, const Duration duration); + + bool load(QDomElement &element); + void save(QDomElement &element) const; + + ///Return the list of appointments for current schedule. + QPtrList<Appointment> appointments(); + + Appointment *findAppointment(Node *node); + /// Adds appointment to current schedule + virtual bool addAppointment(Appointment *appointment); + /// Adds appointment to schedule sch + virtual bool addAppointment(Appointment *appointment, Schedule &main); + /// Adds appointment to both this resource and node + virtual void addAppointment(Schedule *node, DateTime &start, DateTime &end, double load=100); + + void initiateCalculation(Schedule &sch); + bool isAvailable(Task *task); + void makeAppointment(Schedule *schedule); + + bool isOverbooked() const; + bool isOverbooked(const QDate &date) const; + bool isOverbooked(const DateTime &start, const DateTime &end) const; + + double normalRate() const { return cost.normalRate; } + void setNormalRate(double rate) { cost.normalRate = rate; } + double overtimeRate() const { return cost.overtimeRate; } + void setOvertimeRate(double rate) { cost.overtimeRate = rate; } + double fixedCost() const { return cost.fixed; } + void setFixedCost(double value) { cost.fixed = value; } + + /** + * Return available units in percent + */ + int units() const { return m_units; } + /** + * Set available units in percent + */ + void setUnits(int units) { m_units = units; } + + Project *project() const { return m_project; } + + /** + * Get the calendar for this resource. + * If local=false, check if there is a default calendar. + */ + Calendar *calendar(bool local=false) const; + Calendar *calendar(const QString id) const; + void setCalendar(Calendar *calendar) { m_calendar = calendar; } + + /** + * Used to clean up requests when the resource is deleted. + */ + void registerRequest(const ResourceRequest *request) + { m_requests.append(request); } + void unregisterRequest(const ResourceRequest *request) + { m_requests.removeRef(request); } + const QPtrList<ResourceRequest> &requests() const + { return m_requests; } + + Duration effort(const DateTime &start, const Duration &duration, bool backward, bool *ok=0) const; + + /** + * Find the first available time after time, within limit. + * Returns invalid DateTime if not available. + */ + DateTime availableAfter(const DateTime &time, const DateTime limit=DateTime(), bool checkAppointments=false) const; + /** + * Find the first available time before time, within limit. + * Returns invalid DateTime if not available. + */ + DateTime availableBefore(const DateTime &time, const DateTime limit=DateTime(), bool checkAppointments=false) const; + + Resource *findId() const { return findId(m_id); } + Resource *findId(const QString &id) const; + bool removeId() { return removeId(m_id); } + bool removeId(const QString &id); + void insertId(const QString &id); + + Calendar *findCalendar(const QString &id) const; + + Appointment appointmentIntervals() const; + Duration plannedEffort(const QDate &date) const; + + void setCurrentSchedule(Schedule *schedule) { m_currentSchedule = schedule; } + void setCurrentSchedule(long id) { m_currentSchedule = findSchedule(id); } + Schedule *currentSchedule() const { return m_currentSchedule; } + + QIntDict<Schedule> &schedules() { return m_schedules; } + Schedule *findSchedule(long id) { return m_schedules[id]; } + /// Take, and delete. + void removeSchedule(Schedule *schedule); + /// Take, don't delete. + void takeSchedule(const Schedule *schedule); + void addSchedule(Schedule *schedule); + ResourceSchedule *createSchedule(QString name, int type, long id); + ResourceSchedule *createSchedule(Schedule *parent); + +protected: + void makeAppointment(Schedule *node, const DateTime &from, const DateTime &end); + +private: + Project *m_project; + QIntDict<Schedule> m_schedules; + QString m_id; // unique id + QString m_name; + QString m_initials; + QString m_email; + DateTime m_availableFrom; + DateTime m_availableUntil; + QPtrList<QTime> m_workingHours; + + int m_units; // avalable units in percent + + Type m_type; + + struct Cost { + double normalRate; + double overtimeRate; + double fixed; + } cost; + + Calendar *m_calendar; + QPtrList<ResourceRequest> m_requests; + + Schedule *m_currentSchedule; + +public: +#ifndef NDEBUG + void printDebug(QString ident); +#endif +}; + + +/** + * Risk is associated with a resource/task pairing to indicate the planner's confidence in the + * estimated effort. Risk can be one of none, low, or high. Some factors that may be taken into + * account for risk are the experience of the person and the reliability of equipment. + */ +class Risk { + public: + + enum RiskType { + NONE=0, + LOW=1, + HIGH=2 + }; + + Risk(Node *n, Resource *r, RiskType rt=NONE); + ~Risk(); + + RiskType riskType() { return m_riskType; } + + Node *node() { return m_node; } + Resource *resource() { return m_resource; } + + private: + Node *m_node; + Resource *m_resource; + RiskType m_riskType; +}; + +class ResourceRequest { + public: + ResourceRequest(Resource *resource=0, int units = 1); + + ~ResourceRequest(); + + ResourceGroupRequest *parent() const { return m_parent; } + void setParent(ResourceGroupRequest *parent) { m_parent = parent; } + + Resource *resource() const { return m_resource; } + void setResource(Resource* resource) { m_resource = resource; } + + bool load(QDomElement &element, Project &project); + void save(QDomElement &element) const; + + /** + * Get amount of requested resource units in percent + */ + int units() const; + + /** + * Get amount of requested work units in percent + */ + int workUnits() const; + + void registerRequest() { if (m_resource) m_resource->registerRequest(this); } + void unregisterRequest() { if (m_resource) m_resource->unregisterRequest(this); } + + void makeAppointment(Schedule *schedule) { + if (m_resource) + m_resource->makeAppointment(schedule); + } + Task *task() const; + + private: + Resource *m_resource; + int m_units; + ResourceGroupRequest *m_parent; + +#ifndef NDEBUG +public: + void printDebug(QString ident); +#endif +}; + +class ResourceGroupRequest { + public: + ResourceGroupRequest(ResourceGroup *group=0, int units=0); + ~ResourceGroupRequest(); + + void setParent(ResourceRequestCollection *parent) { m_parent = parent;} + ResourceRequestCollection *parent() const { return m_parent; } + + ResourceGroup *group() const { return m_group; } + QPtrList<ResourceRequest> &resourceRequests() { return m_resourceRequests; } + void addResourceRequest(ResourceRequest *request); + void removeResourceRequest(ResourceRequest *request) { m_resourceRequests.removeRef(request); } + ResourceRequest *takeResourceRequest(ResourceRequest *request); + ResourceRequest *find(Resource *resource) const; + + bool load(QDomElement &element, Project &project); + void save(QDomElement &element) const; + + /** + * Get total amount of resource units in percent + */ + int units() const; + + /** + * Get amount of work units in percent + */ + int workUnits() const; + + Duration effort(const DateTime &time, const Duration &duration, bool backward, bool *ok=0) const; + + int numDays(const DateTime &time, bool backward) const; + + /** + * Returns the duration needed to do the effort effort + * starting at start. + */ + Duration duration(const DateTime &start, const Duration &effort, bool backward=false); + + DateTime availableAfter(const DateTime &time); + DateTime availableBefore(const DateTime &time); + + /** + * Makes appointments for task @param task to the + * requested resources for the duration found in @ref duration(). + */ + void makeAppointments(Schedule *schedule); + + /** + * Reserves the requested resources for the specified interval + */ + void reserve(const DateTime &start, const Duration &duration); + + bool isEmpty() const; + + Task *task() const; + + private: + ResourceGroup *m_group; + int m_units; + ResourceRequestCollection *m_parent; + + QPtrList<ResourceRequest> m_resourceRequests; + DateTime m_start; + Duration m_duration; + +#ifndef NDEBUG +public: + void printDebug(QString ident); +#endif +}; + +class ResourceRequestCollection { +public: + ResourceRequestCollection(Task &task); + ~ResourceRequestCollection(); + + const QPtrList<ResourceGroupRequest> &requests() const { return m_requests; } + void addRequest(ResourceGroupRequest *request) { + m_requests.append(request); + request->setParent(this); + } + void removeRequest(ResourceGroupRequest *request) { m_requests.removeRef(request); } + void takeRequest(ResourceGroupRequest *request) { m_requests.take(m_requests.findRef(request)); } + ResourceGroupRequest *find(ResourceGroup *resource) const; + ResourceRequest *find(Resource *resource) const; + bool isEmpty() const; + + //bool load(QDomElement &element, Project &project); + void save(QDomElement &element) const; + + void clear() { m_requests.clear(); } + + /** + * Returns the total amount of resource units in percent + */ + int units() const; + + /** + * Returns the amount of work units in percent + */ + int workUnits() const; + + /** + * Returns the duration needed to do the effort @param effort + * starting at @param time. + */ + Duration duration(const DateTime &time, const Duration &effort, bool backward=false); + + DateTime availableAfter(const DateTime &time); + DateTime availableBefore(const DateTime &time); + + /** + * Makes appointments for the task @param task to the requested resources. + * Assumes that @ref duration() has been run. + */ + void makeAppointments(Schedule *schedule); + /** + * Reserves the requested resources for the specified interval + */ + void reserve(const DateTime &start, const Duration &duration); + + Task &task() const { return m_task; } + +protected: + struct Interval { + DateTime start; + DateTime end; + Duration effort; + }; + + +private: + Task &m_task; + QPtrList<ResourceGroupRequest> m_requests; + +#ifndef NDEBUG +public: + void printDebug(QString ident); +#endif +}; + +} //KPlato namespace + +#endif diff --git a/kplato/kptresourceappointmentsview.cc b/kplato/kptresourceappointmentsview.cc new file mode 100644 index 00000000..b94e9de6 --- /dev/null +++ b/kplato/kptresourceappointmentsview.cc @@ -0,0 +1,233 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Dag Andersen <danders@get2net.dk> + + 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. +*/ + +#include "kptresourceappointmentsview.h" + +#include "kptappointment.h" +#include "kptcalendar.h" +#include "kptnode.h" +#include "kptresource.h" +#include "kptview.h" + +#include <qapplication.h> +#include <kcalendarsystem.h> +#include <kglobal.h> +#include <klocale.h> + +#include <qdatetime.h> +#include <qheader.h> + +namespace KPlato +{ + +ResourceAppointmentsView::NodeItem::NodeItem(Node *t, QListView *parent, bool highlight) + : DoubleListViewBase::MasterListItem(parent, t->name(), highlight), + node(t) { + + setFormat(0, 'f', 1); + //kdDebug()<<k_funcinfo<<endl; +} +ResourceAppointmentsView::NodeItem::NodeItem(Node *t, QListViewItem *p, bool highlight) + : DoubleListViewBase::MasterListItem(p, t->name(), highlight), + node(t) { + + setFormat(0, 'f', 1); + //kdDebug()<<k_funcinfo<<endl; +} + +ResourceAppointmentsView::NodeItem::NodeItem(QString text, QListViewItem *parent, bool highlight) + : DoubleListViewBase::MasterListItem(parent, text, highlight), + node(0) { + + setFormat(0, 'f', 1); + //kdDebug()<<k_funcinfo<<endl; +} + +ResourceAppointmentsView::NodeItem::NodeItem(QString text, QListView *parent, bool highlight) + : DoubleListViewBase::MasterListItem(parent, text, highlight), + node(0) { + + setFormat(0, 'f', 1); + //kdDebug()<<k_funcinfo<<endl; +} + +//------------------------------------------- +ResourceAppointmentsView::ResourceAppointmentsView(View *view, QWidget *parent) + : DoubleListViewBase(parent), + m_mainview(view), + m_resource(0), + m_availItem(0), + m_totalItem(0) { + + setNameHeader(i18n("Task")); + + + QValueList<int> list = sizes(); + int tot = list[0] + list[1]; + list[0] = QMIN(35, tot); + list[1] = tot-list[0]; + setSizes(list); +} + +void ResourceAppointmentsView::zoom(double zoom) { + Q_UNUSED(zoom); +} + + +void ResourceAppointmentsView::draw(Resource *resource, const QDate &start, const QDate &end) { + m_resource = resource; + m_start = start; + m_end = end; + draw(); +} + +void ResourceAppointmentsView::draw() { + //kdDebug()<<k_funcinfo<<endl; + clear(); + if (!m_resource) + return; + + m_totalItem = new ResourceAppointmentsView::NodeItem(i18n("Total"), masterListView()); + m_totalItem->setExpandable(true); + m_totalItem->setOpen(true); + m_availItem = new ResourceAppointmentsView::NodeItem(i18n("Available"), masterListView()); + QPtrList<Appointment> lst = m_resource->appointments(); + //kdDebug()<<k_funcinfo<<lst.count()<<endl; + QPtrListIterator<Appointment> it(lst); + for (; it.current(); ++it) { + //kdDebug()<<k_funcinfo<<endl; + Node *n = it.current()->node()->node(); + ResourceAppointmentsView::NodeItem *item = new ResourceAppointmentsView::NodeItem(n, m_totalItem); + + item->effortMap = it.current()->plannedPrDay(m_start, m_end); + } + slotUpdate(); +} + +void ResourceAppointmentsView::slotUpdate() { + //kdDebug()<<k_funcinfo<<endl; + if (!m_resource) + return; + QApplication::setOverrideCursor(Qt::waitCursor); + createSlaveItems(); + KLocale *locale = KGlobal::locale(); + const KCalendarSystem *cal = locale->calendar(); + const Calendar *resCal = m_resource->calendar(); + const QDateTime availFrom = m_resource->availableFrom(); + const QDateTime availUntil = m_resource->availableUntil(); + // Add columns for selected period/periods + //kdDebug()<<k_funcinfo<<start.toString()<<" - "<<end.toString()<<endl; + int c=0; + for (QDate dt = m_start; dt <= m_end; dt = cal->addDays(dt, 1), ++c) { + QString df = locale->formatDate(dt, true); + addSlaveColumn(df); + } + if (m_totalItem) { + m_totalItem->setHighlight(true); + m_totalItem->setSlaveHighlight(true); + } + QListViewItemIterator it(masterListView()); + for (;it.current(); ++it) { + ResourceAppointmentsView::NodeItem *item = static_cast<ResourceAppointmentsView::NodeItem*>(it.current()); + if (!item || item->firstChild()) { + continue; + } + double eff; + double avail; + int col=0; + for (QDate d=m_start; d <= m_end; d = cal->addDays(d, 1), ++col) { + if (item == m_availItem && resCal) { + QDateTime f(d); + QDateTime u(d, QTime(23, 59, 59, 999)); + if (f >= availUntil || u <= availFrom) { + avail = 0.0; + } else { + if (availFrom > f) { + f = availFrom; + } + if (availUntil < u) { + u = availUntil; + } + avail = ((double)(resCal->effort(f.date(), f.time(), u.time())*(double)(m_resource->units())/100.0).minutes()/60.0); + } + m_availItem->setSlaveItem(col, avail); + m_availItem->addToTotal(avail); + if (m_totalItem) { + m_totalItem->setSlaveLimit(col, avail); + } + } + if (item != m_availItem) { + eff = (double)(item->effortMap.effortOnDate(d).minutes())/60.0; + item->setSlaveItem(col, eff); + item->addToTotal(eff); + } + } + } + if (m_totalItem && m_availItem) { + m_totalItem->setLimit(m_availItem->value()); + //kdDebug()<<k_funcinfo<<"avail="<<m_availItem->value()<<endl; + } + calculate(); + QApplication::restoreOverrideCursor(); +} + + +void ResourceAppointmentsView::print(KPrinter &/*printer*/) { + kdDebug()<<k_funcinfo<<endl; +} + +// bool ResourceAppointmentsView::setContext(Context::ResourceAppointmentsView &context) { +// //kdDebug()<<k_funcinfo<<endl; +// +// QValueList<int> list; +// list << context.accountsviewsize << context.periodviewsize; +// m_dlv->setSizes(list); +// m_date = context.date; +// if (!m_date.isValid()) +// m_date = QDate::currentDate(); +// m_period = context.period; +// m_cumulative = context.cumulative; +// +// return true; +// } +// +// void ResourceAppointmentsView::getContext(Context::ResourceAppointmentsView &context) const { +// //kdDebug()<<k_funcinfo<<endl; +// context.accountsviewsize = m_dlv->sizes()[0]; +// context.periodviewsize = m_dlv->sizes()[1]; +// context.date = m_date; +// context.period = m_period; +// context.cumulative = m_cumulative; +// //kdDebug()<<k_funcinfo<<"sizes="<<sizes()[0]<<","<<sizes()[1]<<endl; +// } + +void ResourceAppointmentsView::clear() { + clearLists(); + m_availItem = 0; + m_totalItem = 0; +} + +void ResourceAppointmentsView::createSlaveItems() { + DoubleListViewBase::createSlaveItems(); + setSlaveFormat(0, 'f', 1); +} + +} //KPlato namespace + +#include "kptresourceappointmentsview.moc" diff --git a/kplato/kptresourceappointmentsview.h b/kplato/kptresourceappointmentsview.h new file mode 100644 index 00000000..76bb9073 --- /dev/null +++ b/kplato/kptresourceappointmentsview.h @@ -0,0 +1,102 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Dag Andersen <danders@get2net.dk> + + 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 KPTRESOURCEAPPOINTMENTSVIEW_H +#define KPTRESOURCEAPPOINTMENTSVIEW_H + +#include "kptcontext.h" +#include "kptdoublelistviewbase.h" +#include "kpteffortcostmap.h" + +class QComboBox; +class QDateEdit; +class QPushButton; +class QSplitter; +class QListView; +class QListViewItem; +class QLabel; +class QPushButton; + +class KListView; +class KListViewItem; +class KPrinter; + +namespace KPlato +{ + +class View; +class Project; +class Resource; +class Node; + +class ResourceGroup; +class Resource; + +class ResourceAppointmentsView : public DoubleListViewBase +{ + Q_OBJECT +public: + + ResourceAppointmentsView(View *view, QWidget *parent); + + //~ResourceAppointmentsView(); + + void zoom(double zoom); + + View *mainView() const { return m_mainview; } + void draw(Resource *resource, const QDate &start, const QDate &end); + void draw(); + void print(KPrinter &printer); + void clear(); + + //virtual bool setContext(Context::ResourceAppointmentsView &context); + //virtual void getContext(Context::ResourceAppointmentsView &context) const; + + virtual void createSlaveItems(); + +protected slots: + void slotUpdate(); + +private: + class NodeItem : public DoubleListViewBase::MasterListItem { + public: + NodeItem(Node *n, QListView *parent, bool highlight=false); + NodeItem(Node *n, QListViewItem *parent, bool highlight=false); + NodeItem(QString text, QListView *parent, bool highlight=false); + NodeItem(QString text, QListViewItem *parent, bool highlight=false); + + Node *node; + EffortCostMap effortMap; + }; + +private: + View *m_mainview; + + int m_defaultFontSize; + Resource *m_resource; + QDate m_start; + QDate m_end; + NodeItem *m_availItem; + NodeItem *m_totalItem; +}; + +} //KPlato namespace + + +#endif // KPTTASKAPPOINTMENTSVIEW_H diff --git a/kplato/kptresourcedialog.cc b/kplato/kptresourcedialog.cc new file mode 100644 index 00000000..56d79482 --- /dev/null +++ b/kplato/kptresourcedialog.cc @@ -0,0 +1,241 @@ +/* This file is part of the KDE project + Copyright (C) 2003 - 2005 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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. +*/ + +#include "kptresourcedialog.h" +#include "kptcommand.h" +#include "kptpart.h" +#include "kptproject.h" +#include "kptresource.h" +#include "kptcalendar.h" + +#include <qpushbutton.h> +#include <qlabel.h> +#include <qlineedit.h> +#include <qcombobox.h> +#include <qdatetimeedit.h> +#include <qdatetime.h> +#include <qbuttongroup.h> +#include <qradiobutton.h> +#include <qspinbox.h> +#include <qptrlist.h> +#include <qstringlist.h> + +#include <kabc/addressee.h> +#include <kabc/addresseedialog.h> + +#include <kcommand.h> +#include <kdatetimewidget.h> +#include <kmessagebox.h> +#include <klocale.h> +#include <kglobal.h> +#include <kdebug.h> + +namespace KPlato +{ + +ResourceDialogImpl::ResourceDialogImpl (QWidget *parent) + : ResourceDialogBase(parent) +{ + + connect(type, SIGNAL(activated(int)), SLOT(slotChanged())); + connect(units, SIGNAL(valueChanged(int)), SLOT(slotChanged())); + connect(nameEdit, SIGNAL(textChanged(const QString&)), SLOT(slotChanged())); + connect(initialsEdit, SIGNAL(textChanged(const QString&)), SLOT(slotChanged())); + connect(emailEdit, SIGNAL(textChanged(const QString&)), SLOT(slotChanged())); + + connect(calendarList, SIGNAL(activated(int)), SLOT(slotChanged())); + + connect(rateEdit, SIGNAL(textChanged(const QString&)), SLOT(slotChanged())); + connect(overtimeEdit, SIGNAL(textChanged(const QString&)), SLOT(slotChanged())); + + connect(chooseBtn, SIGNAL(clicked()), SLOT(slotChooseResource())); + + connect(availableFrom, SIGNAL(valueChanged(const QDateTime&)), SLOT(slotChanged())); + connect(availableUntil, SIGNAL(valueChanged(const QDateTime&)), SLOT(slotChanged())); + connect(availableFrom, SIGNAL(valueChanged(const QDateTime&)), SLOT(slotAvailableFromChanged(const QDateTime&))); + connect(availableUntil, SIGNAL(valueChanged(const QDateTime&)), SLOT(slotAvailableUntilChanged(const QDateTime&))); +} + + +void ResourceDialogImpl::slotChanged() { + emit changed(); +} + +void ResourceDialogImpl::slotAvailableFromChanged(const QDateTime&) { + if (availableUntil->dateTime() < availableFrom->dateTime()) { + disconnect(availableUntil, SIGNAL(valueChanged(const QDateTime&)), this, SLOT(slotAvailableUntilChanged(const QDateTime&))); + //kdDebug()<<"From: "<<availableFrom->dateTime().toString()<<" until="<<availableUntil->dateTime().toString()<<endl; + availableUntil->setDateTime(availableFrom->dateTime()); + connect(availableUntil, SIGNAL(valueChanged(const QDateTime&)), SLOT(slotAvailableUntilChanged(const QDateTime&))); + } +} + +void ResourceDialogImpl::slotAvailableUntilChanged(const QDateTime&) { + if (availableFrom->dateTime() > availableUntil->dateTime()) { + disconnect(availableFrom, SIGNAL(valueChanged(const QDateTime&)), this, SLOT(slotAvailableFromChanged(const QDateTime&))); + //kdDebug()<<"Until: "<<availableUntil->dateTime().toString()<<" from="<<availableFrom->dateTime().toString()<<endl; + availableFrom->setDateTime(availableUntil->dateTime()); + connect(availableFrom, SIGNAL(valueChanged(const QDateTime&)), SLOT(slotAvailableFromChanged(const QDateTime&))); + } +} + +void ResourceDialogImpl::slotCalculationNeeded(const QString&) { + emit calculate(); + emit changed(); +} + +void ResourceDialogImpl::slotChooseResource() +{ + KABC::Addressee a = KABC::AddresseeDialog::getAddressee(this); + if (!a.isEmpty()) { + nameEdit->setText(a.assembledName()); + emailEdit->setText(a.preferredEmail()); + QStringList l = QStringList::split(' ', a.assembledName()); + QString in; + QStringList::Iterator it = l.begin(); + for (/*int i = 0*/; it != l.end(); ++it) { + in += (*it)[0]; + } + initialsEdit->setText(in); + } +} + +////////////////// ResourceDialog //////////////////////// + +ResourceDialog::ResourceDialog(Project &project, Resource *resource, QWidget *parent, const char *name) + : KDialogBase( Swallow, i18n("Resource Settings"), Ok|Cancel, Ok, parent, name, true, true), + m_original(resource), + m_resource(resource), + m_calculationNeeded(false) +{ + dia = new ResourceDialogImpl(this); + setMainWidget(dia); + enableButtonOK(false); + + dia->nameEdit->setText(resource->name()); + dia->initialsEdit->setText(resource->initials()); + dia->emailEdit->setText(resource->email()); + dia->type->setCurrentItem((int)resource->type()); // NOTE: must match enum + dia->units->setValue(resource->units()); + dia->availableFrom->setDateTime(resource->availableFrom()); + dia->availableUntil->setDateTime(resource->availableUntil()); + dia->rateEdit->setText(KGlobal::locale()->formatMoney(resource->normalRate())); + dia->overtimeEdit->setText(KGlobal::locale()->formatMoney(resource->overtimeRate())); + + int cal = 0; + dia->calendarList->insertItem(i18n("None")); + m_calendars.insert(0, 0); + QPtrList<Calendar> list = project.calendars(); + QPtrListIterator<Calendar> cit = list; + for(int i=1; cit.current(); ++cit, ++i) { + dia->calendarList->insertItem(cit.current()->name(), i); + m_calendars.insert(i, cit.current()); + if (cit.current() == resource->calendar()) + cal = i; + } + dia->calendarList->setCurrentItem(cal); + + connect(dia, SIGNAL(changed()), SLOT(enableButtonOk())); + connect(dia, SIGNAL(calculate()), SLOT(slotCalculationNeeded())); + connect(dia->calendarList, SIGNAL(activated(int)), SLOT(slotCalendarChanged(int))); + +} + + +void ResourceDialog::enableButtonOk() { + enableButtonOK(true); +} + +void ResourceDialog::slotCalculationNeeded() { + m_calculationNeeded = true; +} + +void ResourceDialog::slotOk() { + m_resource.setName(dia->nameEdit->text()); + m_resource.setInitials(dia->initialsEdit->text()); + m_resource.setEmail(dia->emailEdit->text()); + m_resource.setType((Resource::Type)(dia->type->currentItem())); + m_resource.setUnits(dia->units->value()); + + m_resource.setNormalRate(KGlobal::locale()->readMoney(dia->rateEdit->text())); + m_resource.setOvertimeRate(KGlobal::locale()->readMoney(dia->overtimeEdit->text())); + m_resource.setCalendar(m_calendars[dia->calendarList->currentItem()]); + m_resource.setAvailableFrom(dia->availableFrom->dateTime()); + m_resource.setAvailableUntil(dia->availableUntil->dateTime()); + accept(); +} + +void ResourceDialog::slotCalendarChanged(int /*cal*/) { + +} + +KCommand *ResourceDialog::buildCommand(Part *part) { + return buildCommand(m_original, m_resource, part); +} + +// static +KCommand *ResourceDialog::buildCommand(Resource *original, Resource &resource, Part *part) { + KMacroCommand *m=0; + QString n = i18n("Modify Resource"); + if (resource.name() != original->name()) { + if (!m) m = new KMacroCommand(n); + m->addCommand(new ModifyResourceNameCmd(part, original, resource.name())); + } + if (resource.initials() != original->initials()) { + if (!m) m = new KMacroCommand(n); + m->addCommand(new ModifyResourceInitialsCmd(part, original, resource.initials())); + } + if (resource.email() != original->email()) { + if (!m) m = new KMacroCommand(n); + m->addCommand(new ModifyResourceEmailCmd(part, original, resource.email())); + } + if (resource.type() != original->type()) { + if (!m) m = new KMacroCommand(n); + m->addCommand(new ModifyResourceTypeCmd(part, original, resource.type())); + } + if (resource.units() != original->units()) { + if (!m) m = new KMacroCommand(n); + m->addCommand(new ModifyResourceUnitsCmd(part, original, resource.units())); + } + if (resource.availableFrom() != original->availableFrom()) { + if (!m) m = new KMacroCommand(n); + m->addCommand(new ModifyResourceAvailableFromCmd(part, original, resource.availableFrom())); + } + if (resource.availableUntil() != original->availableUntil()) { + if (!m) m = new KMacroCommand(n); + m->addCommand(new ModifyResourceAvailableUntilCmd(part, original, resource.availableUntil())); + } + if (resource.normalRate() != original->normalRate()) { + if (!m) m = new KMacroCommand(n); + m->addCommand(new ModifyResourceNormalRateCmd(part, original, resource.normalRate())); + } + if (resource.overtimeRate() != original->overtimeRate()) { + if (!m) m = new KMacroCommand(n); + m->addCommand(new ModifyResourceOvertimeRateCmd(part, original, resource.overtimeRate())); + } + if (resource.calendar(true) != original->calendar(true)) { + if (!m) m = new KMacroCommand(n); + m->addCommand(new ModifyResourceCalendarCmd(part, original, resource.calendar(true))); + } + return m; +} + +} //KPlato namespace + +#include "kptresourcedialog.moc" diff --git a/kplato/kptresourcedialog.h b/kplato/kptresourcedialog.h new file mode 100644 index 00000000..bfa0da7d --- /dev/null +++ b/kplato/kptresourcedialog.h @@ -0,0 +1,92 @@ +/* This file is part of the KDE project + Copyright (C) 2003 - 2005 Dag Andersen <kplato@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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 KPTRESOURCEDIALOG_H +#define KPTRESOURCEDIALOG_H + +#include "resourcedialogbase.h" +#include "kptresource.h" + +#include <kdialogbase.h> + +#include <qmap.h> +#include <qcombobox.h> + +class KCommand; + +class QTime; +class QString; + +namespace KPlato +{ + +class Part; +class Project; +class Resource; +class Calendar; + +class ResourceDialogImpl : public ResourceDialogBase { + Q_OBJECT +public: + ResourceDialogImpl (QWidget *parent); + +public slots: + void slotChanged(); + void slotCalculationNeeded(const QString&); + void slotChooseResource(); + +signals: + void changed(); + void calculate(); + +protected slots: + void slotAvailableFromChanged(const QDateTime& dt); + void slotAvailableUntilChanged(const QDateTime& dt); +}; + +class ResourceDialog : public KDialogBase { + Q_OBJECT +public: + ResourceDialog(Project &project, Resource *resource, QWidget *parent=0, const char *name=0); + + bool calculationNeeded() { return m_calculationNeeded; } + + Calendar *calendar() { return m_calendars[dia->calendarList->currentItem()]; } + KCommand *buildCommand(Part *part = 0); + + static KCommand *buildCommand(Resource *original, Resource &resource, Part *part); + +protected slots: + void enableButtonOk(); + void slotCalculationNeeded(); + void slotOk(); + void slotCalendarChanged(int); + +private: + Resource *m_original; + Resource m_resource; + ResourceDialogImpl *dia; + bool m_calculationNeeded; + + QMap<int, Calendar*> m_calendars; +}; + +} //KPlato namespace + +#endif diff --git a/kplato/kptresourcesdialog.cc b/kplato/kptresourcesdialog.cc new file mode 100644 index 00000000..967c2f2e --- /dev/null +++ b/kplato/kptresourcesdialog.cc @@ -0,0 +1,71 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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. +*/ + +#include <qvbox.h> + +#include <klocale.h> +#include <kcommand.h> + +#include <kdebug.h> + +#include "kptresourcesdialog.h" +#include "kptproject.h" +#include "kptresourcespanel.h" +#include "kptresource.h" + +namespace KPlato +{ + +ResourcesDialog::ResourcesDialog(Project &p, QWidget *parent, const char *name) + : KDialogBase( Swallow, i18n("Resources"), Ok|Cancel, Ok, parent, name, true, true), + project(p) +{ + panel = new ResourcesPanel(this, &project); + + setMainWidget(panel); + enableButtonOK(false); + + connect(panel, SIGNAL(changed()), SLOT(slotChanged())); +} + + +void ResourcesDialog::slotChanged() { + enableButtonOK(true); +} + +void ResourcesDialog::slotOk() { + if (!panel->ok()) + return; + accept(); +} + +KCommand *ResourcesDialog::buildCommand(Part *part) { + KMacroCommand *m = 0; + QString c = i18n("Modify resources"); + KCommand *cmd = panel->buildCommand(part); + if (cmd) { + if (!m) m = new KMacroCommand(c); + m->addCommand(cmd); + } + return m; +} + +} //KPlato namespace + +#include "kptresourcesdialog.moc" diff --git a/kplato/kptresourcesdialog.h b/kplato/kptresourcesdialog.h new file mode 100644 index 00000000..65c4acd9 --- /dev/null +++ b/kplato/kptresourcesdialog.h @@ -0,0 +1,53 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License + + 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 KPTRESOURCESDIALOG_H +#define KPTRESOURCESDIALOG_H + +#include <kdialogbase.h> + +class KCommand; + +namespace KPlato +{ + +class Project; +class ResourcesPanel; +class Part; + + +class ResourcesDialog : public KDialogBase { + Q_OBJECT +public: + ResourcesDialog(Project &project, QWidget *parent=0, const char *name=0); + + KCommand *buildCommand(Part *part); + +protected slots: + void slotChanged(); + void slotOk(); + +private: + Project &project; + ResourcesPanel *panel; +}; + +} //KPlato namespace + +#endif // RESOURCESDIALOG_H diff --git a/kplato/kptresourcespanel.cc b/kplato/kptresourcespanel.cc new file mode 100644 index 00000000..71a6f919 --- /dev/null +++ b/kplato/kptresourcespanel.cc @@ -0,0 +1,561 @@ +/* This file is part of the KDE project + Copyright (C) 2003 Thomas Zander <zander@kde.org> + Copyright (C) 2004, 2005 Dag Andersen <danders@get2net.dk> + + 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. +*/ + +#include "kptresourcespanel.h" +#include "kptproject.h" +#include "kptresourcedialog.h" +#include "kptcommand.h" + +#include <kdebug.h> +#include <klistview.h> +#include <kmessagebox.h> +#include <klocale.h> +#include <kabc/addressee.h> +#include <kabc/addresseedialog.h> + +#include <qgroupbox.h> +#include <qheader.h> +#include <qlistbox.h> +#include <qlineedit.h> +#include <qlistview.h> +#include <qpushbutton.h> + +//////////////////////////// Private classes ////////////////////////////////// + +namespace KPlato +{ + +class GroupItem; +class ResourcesPanelGroupLVItem; +class ResourcesPanelResourceItem; +class Part; + +class ResourcesPanelResourceItem { +public: + enum State {None, Modified, New}; + + ResourcesPanelResourceItem(Resource *res, State state = None) + : m_originalResource(0), + m_state(state) { + if (state == New) { + m_resource = res; + } else { + m_originalResource = res; + m_resource = new Resource(res); + } + //kdDebug()<<k_funcinfo<<"("<<this<<")"<<" orgres="<<m_originalResource<<" newres="<<m_resource<<endl; + } + ~ResourcesPanelResourceItem() { + //kdDebug()<<k_funcinfo<<"("<<this<<") state="<<m_state<<endl; + delete m_resource; + } + void setState(State s) { + if (m_state == New) + return; // A new is allways new + m_state = s; + } + QString name() { return m_resource->name(); } + void setName(const QString &newName) { + m_resource->setName(newName); + setState(Modified); + } + Resource *takeResource() { + Resource *r = m_resource; + m_resource = 0; + return r; + } + KCommand *saveResource(Part *part, ResourceGroup *group); + + Resource *m_originalResource; + Resource *m_resource; // work on a local copy + State m_state; +}; +KCommand *ResourcesPanelResourceItem::saveResource(Part *part, ResourceGroup *group) { + KMacroCommand *m=0; + if (m_state == New) { + //kdDebug()<<k_funcinfo<<"Add resource: "<<m_resource->name()<<endl; + if (!m) m = new KMacroCommand("Add resource"); + m->addCommand(new AddResourceCmd(part, group, takeResource())); + } else if (m_state == Modified) { + //kdDebug()<<k_funcinfo<<"Modify resource: "<<m_originalResource->name()<<endl; + KCommand *cmd = ResourceDialog::buildCommand(m_originalResource, *m_resource, part); + if (cmd) { + if (!m) m = new KMacroCommand("Modify resource"); + m->addCommand(cmd); + } + } + return m; +} + +class ResourceLBItem : public QListBoxText { +public: + ResourceLBItem(ResourcesPanelResourceItem *item) { + m_resourceItem = item; setText(item->name()); + } + QString name() { return m_resourceItem->name(); } + void setName(const QString &newName) { + setText(newName); + m_resourceItem->setName(newName); + } + + ResourcesPanelResourceItem *m_resourceItem; +}; + + +//------------------- +class GroupItem { +public: + enum State {None=0, Modified=1, New=2}; //bitmap + + GroupItem(ResourceGroup *group, State state = None) { + m_group = group; + m_name = group->name(); + m_state = state; + m_resourceItems.setAutoDelete(true); + m_deletedItems.setAutoDelete(true); + //kdDebug()<<k_funcinfo<<"("<<this<<")"<<endl; + } + ~GroupItem() { + //kdDebug()<<k_funcinfo<<"("<<this<<")"<<endl; + if (m_state & New) { + delete m_group; + } + } + void setState(State s) { m_state |= s; } + void setName(const QString &newName) { + m_name = newName; + if (m_state & New) + m_group->setName(newName); + setState(Modified); + //kdDebug()<<k_funcinfo<<"New name: '"<<newName<<"', group name: '"<<m_group->name()<<"' state="<<m_state<<endl; + } + void addResource(ResourcesPanelResourceItem *item) { + //kdDebug()<<k_funcinfo<<" add: "<<(item?item->name():"")<<" ("<<item<<")"<<endl; + m_resourceItems.append(item); + } + void deleteResource(ResourcesPanelResourceItem *item) { + //kdDebug()<<k_funcinfo<<" Deleted: "<<item->m_name<<" ("<<item<<")"<<endl; + m_resourceItems.take(m_resourceItems.findRef(item)); + if (item->m_state == ResourcesPanelResourceItem::New) + delete item; + else + m_deletedItems.append(item); + //kdDebug()<<k_funcinfo<<"No of items now="<<m_resourceItems.count()<<", no of deleted items="<<m_deletedItems.count()<<endl; + } + ResourceGroup *takeGroup() { + //kdDebug()<<k_funcinfo<<"("<<m_group<<")"<<endl; + ResourceGroup *g = m_group; + m_group = 0; + return g; + } + void saveResources() { + ResourcesPanelResourceItem *item = m_resourceItems.first(); + while ((item = m_resourceItems.take())) { + //kdDebug()<<k_funcinfo<<item->m_resource->name()<<endl; + m_group->addResource(item->takeResource(), 0); + delete item; + } + } + ResourceGroup *m_group; + QString m_name; + QPtrList<ResourcesPanelResourceItem> m_resourceItems; + QPtrList<ResourcesPanelResourceItem> m_deletedItems; + int m_state; +}; + +class ResourcesPanelGroupLVItem : public KListViewItem { +public: + ResourcesPanelGroupLVItem(ResourcesPanel &pan, KListView *lv, GroupItem *item) + : KListViewItem(lv, item->m_name), + m_group(item), + panel(pan) { + + setRenameEnabled(0, false); + //kdDebug()<<k_funcinfo<<"("<<this<<")"<<endl; + } + ~ResourcesPanelGroupLVItem() { + //kdDebug()<<k_funcinfo<<"("<<this<<")"<<endl; + } + void setName(const QString &newName) { + setText(0, newName); + m_group->setName(newName); + } + void deleteGroup() { + delete m_group; + m_group = 0; + } + GroupItem *takeGroup() { + //kdDebug()<<k_funcinfo<<"("<<m_group<<")"<<endl; + GroupItem *g = m_group; + m_group = 0; + return g; + } + GroupItem *m_group; + ResourcesPanel &panel; + QString oldText; + +protected: + virtual void cancelRename(int col) { + //kdDebug()<<k_funcinfo<<endl; + if (col == 0 && oldText.isEmpty()){ + return; + } + panel.renameStopped(this); + KListViewItem::cancelRename(col); + setRenameEnabled(col, false); + } +}; + +//////////////////// ResourcesPanel ////////////////////// + +ResourcesPanel::ResourcesPanel(QWidget *parent, Project *p) : ResourcesPanelBase(parent) { + project = p; + m_groupItem = 0; + m_blockResourceRename = false; + m_renameItem = 0; + + bEditResource->setEnabled(false); + bRemoveResource->setEnabled(false); + resourceName->setEnabled(false); + + listOfGroups->header()->setStretchEnabled(true, 0); + listOfGroups->setSorting(0); + listOfGroups->setShowSortIndicator(true); + listOfGroups->setDefaultRenameAction (QListView::Accept); + bAdd->setEnabled(true); + + m_groupItems.setAutoDelete(true); + m_deletedGroupItems.setAutoDelete(true); + + QPtrListIterator<ResourceGroup> git(project->resourceGroups()); + for(; git.current(); ++git) { + ResourceGroup *grp = git.current(); + GroupItem *groupItem = new GroupItem(grp); + //kdDebug()<<k_funcinfo<<" Added group: "<<groupItem->m_name<<" ("<<groupItem<<")"<<endl; + QPtrListIterator<Resource> rit(grp->resources()); + for(; rit.current(); ++rit) { + Resource *res = rit.current(); + ResourcesPanelResourceItem *ritem = new ResourcesPanelResourceItem(res); + groupItem->addResource(ritem); + //kdDebug()<<k_funcinfo<<" Added resource: "<<ritem->m_name<<" ("<<ritem<<")"<<endl; + } + m_groupItems.append(groupItem); + new ResourcesPanelGroupLVItem(*this, listOfGroups, groupItem); + } + listOfGroups->setSelected(listOfGroups->firstChild(), true); + slotGroupChanged(); + + connect(bAdd, SIGNAL(clicked()), SLOT(slotAddGroup())); + connect(bRemove, SIGNAL(clicked()), SLOT(slotDeleteGroup())); + connect(listOfGroups, SIGNAL(selectionChanged()), SLOT(slotGroupChanged())); + connect(listOfGroups, SIGNAL(doubleClicked(QListViewItem*, const QPoint&, int)), SLOT(slotListDoubleClicked(QListViewItem*, const QPoint&, int))); + connect(listOfGroups, SIGNAL(itemRenamed(QListViewItem*, int)), SLOT(slotItemRenamed(QListViewItem*, int))); + + connect(bAddResource, SIGNAL( clicked() ), this, SLOT ( slotAddResource() )); + connect(bEditResource, SIGNAL( clicked() ), this, SLOT ( slotEditResource() )); + connect(bRemoveResource, SIGNAL( clicked() ), this, SLOT ( slotDeleteResource() )); + connect(listOfResources, SIGNAL(selectionChanged(QListBoxItem*)), SLOT(slotResourceChanged(QListBoxItem*))); + connect(listOfResources, SIGNAL(currentChanged(QListBoxItem*)), SLOT(slotCurrentChanged(QListBoxItem*))); + connect(resourceName, SIGNAL(textChanged(const QString&)), SLOT(slotResourceRename(const QString&))); + + + // Internal hacks, to get renaming to behave + // Uses signals to not get in the way of QListView + connect(this, SIGNAL(renameStarted(QListViewItem*, int)), SLOT(slotRenameStarted(QListViewItem*, int))); + connect(this, SIGNAL(startRename(QListViewItem*, int)), SLOT(slotStartRename(QListViewItem*, int))); + connect(this, SIGNAL(selectionChanged()), SLOT(slotGroupChanged())); +} + +void ResourcesPanel::slotAddGroup() { + //kdDebug()<<k_funcinfo<<endl; + ResourceGroup *r = new ResourceGroup(project); + GroupItem *gitem = new GroupItem(r, GroupItem::New); + m_groupItems.append(gitem); + ResourcesPanelGroupLVItem *groupItem = new ResourcesPanelGroupLVItem(*this, listOfGroups, gitem); + + slotListDoubleClicked(groupItem, QPoint(), 0); +} + +void ResourcesPanel::slotDeleteGroup() { + //kdDebug()<<k_funcinfo<<endl; + ResourcesPanelGroupLVItem *groupLVItem = dynamic_cast<ResourcesPanelGroupLVItem*> (listOfGroups->selectedItem()); + if (groupLVItem == 0) + return; + + listOfResources->clear(); + + listOfGroups->takeItem(groupLVItem); // remove from listbox + m_groupItems.take(m_groupItems.findRef(groupLVItem->m_group)); // remove GroupItem from active list + m_deletedGroupItems.append(groupLVItem->takeGroup()); // remove GroupItem from GroupLVItem and add to deleted list + + //kdDebug()<<k_funcinfo<<" No of deleted groups="<<m_deletedGroupItems.count()<<", now "<<m_groupItems.count()<<" groups left"<<endl; + + delete groupLVItem; // delete GroupLVItem (but not GroupItem) + emit changed(); +} + +void ResourcesPanel::slotAddResource() { + if (!m_groupItem) { + KMessageBox::sorry(this, i18n("Resources belong to resource groups, select the group first to add a new resource to")); + return; + } + listOfResources->setSelected(listOfResources->selectedItem(), false); + Resource *r = new Resource(project); + ResourceDialog *dia = new ResourceDialog(*project, r); + if (dia->exec()) { + KCommand *cmd = dia->buildCommand(); + if (cmd) { + cmd->execute(); // modifications -> r + delete cmd; + } + ResourcesPanelResourceItem *resourceItem = new ResourcesPanelResourceItem(r, ResourcesPanelResourceItem::New); + m_groupItem->m_group->addResource(resourceItem); + ResourceLBItem *item = new ResourceLBItem(resourceItem); + listOfResources->insertItem(item); + resourceName->clear(); + listOfResources->setSelected(item, true); + emit changed(); + //kdDebug()<<k_funcinfo<<" Added: "<<resourceItem->name()<<" to "<<m_groupItem->m_group->m_name<<endl; + } else { + delete r; + } + delete dia; +} + +void ResourcesPanel::slotEditResource() { + //kdDebug()<<k_funcinfo<<endl; + ResourceLBItem *item = dynamic_cast<ResourceLBItem*> (listOfResources->selectedItem()); + if(item == 0) return; + Resource *r = item->m_resourceItem->m_resource; + ResourceDialog *dia = new ResourceDialog(*project, r); + if (dia->exec()) { + KCommand *cmd = dia->buildCommand(); + if (cmd) { + cmd->execute(); // modifications -> r + delete cmd; + } + resourceName->setText(r->name()); + item->m_resourceItem->setState(ResourcesPanelResourceItem::Modified); + item->setName(r->name()); // refresh list + listOfResources->triggerUpdate(false); + emit changed(); + } + delete dia; +} + +void ResourcesPanel::slotDeleteResource() { + //kdDebug()<<k_funcinfo<<endl; + ResourceLBItem *item = dynamic_cast<ResourceLBItem*> (listOfResources->selectedItem()); + if(item == 0) return; + + //Can't delete resource from unselected group + if(m_groupItem == 0) return; + + m_groupItem->m_group->deleteResource(item->m_resourceItem); + listOfResources->removeItem(listOfResources->currentItem()); + + emit changed(); +} + +/* Select another resource */ +void ResourcesPanel::slotResourceChanged( QListBoxItem *item) { + if (!item) { + resourceName->setEnabled(false); + bEditResource->setEnabled(false); + bRemoveResource->setEnabled(false); + return; + } + resourceName->setText( ((ResourceLBItem *)item)->name()); + resourceName->setEnabled(true); + bEditResource->setEnabled(true); + bRemoveResource->setEnabled(true); +} + +/* Select another resource */ +void ResourcesPanel::slotCurrentChanged( QListBoxItem *item) { + if (item && !item->isSelected()) { + listOfResources->setSelected(item, true); + } +} + +void ResourcesPanel::slotResourceRename( const QString &newName) { + QListBoxItem *item = listOfResources->selectedItem(); + if(!item || m_blockResourceRename) return; + + ResourceLBItem *i = dynamic_cast<ResourceLBItem *>(item); + if (i->name() == newName) return; + + i->setName(newName); + listOfResources->triggerUpdate(false); + + emit changed(); +} + +bool ResourcesPanel::ok() { + return true; +} + +KCommand *ResourcesPanel::buildCommand(Part *part) { + KMacroCommand *m=0; + GroupItem *gitem; + + QString cmdName = "Modify resourcegroups"; + QPtrListIterator<GroupItem> dgit(m_deletedGroupItems); + for (; (gitem = dgit.current()) != 0; ++dgit) { + if (!(gitem->m_state & GroupItem::New)) { + if (!m) m = new KMacroCommand(cmdName); + //kdDebug()<<k_funcinfo<<"Remove group: '"<<gitem->m_name<<"'"<<endl; + m->addCommand(new RemoveResourceGroupCmd(part, gitem->takeGroup())); + } + } + + QPtrListIterator<GroupItem> git(m_groupItems); + for (; (gitem = git.current()) != 0; ++git) { + //kdDebug()<<k_funcinfo<<"Group: "<<gitem->m_name<<" has "<<gitem->m_resourceItems.count()<<" resources"<<" and "<<gitem->m_deletedItems.count()<<" deleted resources"<<endl; + //First remove deleted resources from group + QPtrListIterator<ResourcesPanelResourceItem> dit(gitem->m_deletedItems); + ResourcesPanelResourceItem *ditem; + for (; (ditem = dit.current()) != 0; ++dit) { + if (!m) m = new KMacroCommand(cmdName); + //kdDebug()<<k_funcinfo<<" Deleting resource: '"<<ditem->m_originalResource->name()<<"'"<<endl; + m->addCommand(new RemoveResourceCmd(part, gitem->m_group, ditem->m_originalResource)); + } + // Now add/modify group/resources + if (gitem->m_state & GroupItem::New) { + if (!m) m = new KMacroCommand(cmdName); + //kdDebug()<<k_funcinfo<<" Adding group: '"<<gitem->m_name<<"'"<<endl; + gitem->saveResources(); + m->addCommand(new AddResourceGroupCmd(part, gitem->takeGroup())); + continue; + } + ResourceGroup *rg = gitem->takeGroup(); + if (gitem->m_state & GroupItem::Modified) { + if (gitem->m_name != rg->name()) { + if (!m) m = new KMacroCommand(cmdName); + //kdDebug()<<k_funcinfo<<" Modifying group: '"<<gitem->m_name<<"'"<<endl; + m->addCommand(new ModifyResourceGroupNameCmd(part, rg, gitem->m_name)); + } + } + QPtrListIterator<ResourcesPanelResourceItem> it(gitem->m_resourceItems); + for (; it.current() != 0; ++it) { + KCommand *cmd = it.current()->saveResource(part, rg); + if (cmd) { + if (!m) m = new KMacroCommand(cmdName); + m->addCommand(cmd); + } + } + } + return m; +} + +void ResourcesPanel::slotGroupChanged() { + slotGroupChanged(listOfGroups->selectedItem()); +} + +void ResourcesPanel::slotGroupChanged(QListViewItem *itm) { + ResourcesPanelGroupLVItem *item = static_cast<ResourcesPanelGroupLVItem*>(itm); + if (!item) { + bAdd->setEnabled(true); + bRemove->setEnabled(false); + listOfResources->clear(); + resourceName->clear(); + resourceGroupBox->setEnabled(false); + return; + } + m_blockResourceRename = true; + resourceName->clear(); + resourceName->setEnabled(false); + m_blockResourceRename = false; + + m_groupItem = item; + listOfResources->clear(); + + QPtrListIterator<ResourcesPanelResourceItem> it(m_groupItem->m_group->m_resourceItems); + for ( ; it.current(); ++it ) { + listOfResources->insertItem(new ResourceLBItem(it.current())); + //kdDebug()<<k_funcinfo<<"Insert resource item: "<<it.current()->name()<<endl; + } + bAdd->setEnabled(true); + bRemove->setEnabled(true); + slotResourceChanged(0); + resourceGroupBox->setEnabled(true); +} + +void ResourcesPanel::slotListDoubleClicked(QListViewItem* item, const QPoint&, int col) { + //kdDebug()<<k_funcinfo<<(item?item->text(0):"")<<endl; + if (m_renameItem) + return; + slotStartRename(item, col); +} + +void ResourcesPanel::slotItemRenamed(QListViewItem *item, int col) { + //kdDebug()<<k_funcinfo<<item->text(0)<<endl; + item->setRenameEnabled(col, false); + m_renameItem = 0; + if (col != 0) { + renameStopped(item); + emit changed(); + return; + } + if (item->text(0).isEmpty()) { + item->setText(0, static_cast<ResourcesPanelGroupLVItem*>(item)->oldText); // keep the old name + } + if (item->text(0).isEmpty()) { + // Not allowed + //kdDebug()<<k_funcinfo<<"name empty"<<endl; + emit startRename(item, 0); + return; + } + static_cast<ResourcesPanelGroupLVItem*>(item)->setName(item->text(0)); + bRemove->setEnabled(listOfGroups->selectedItem()); + bAdd->setEnabled(listOfGroups->selectedItem()); + renameStopped(item); + emit changed(); +} + +void ResourcesPanel::slotRenameStarted(QListViewItem */*item*/, int /*col*/) { + //kdDebug()<<k_funcinfo<<(item?item->text(0):"")<<endl; + if (listOfGroups->isRenaming()) { + bRemove->setEnabled(false); + bAdd->setEnabled(false); + } +} + +void ResourcesPanel::slotStartRename(QListViewItem *item, int col) { + //kdDebug()<<k_funcinfo<<(item?item->text(0):"")<<endl; + static_cast<ResourcesPanelGroupLVItem*>(item)->oldText = item->text(col); + item->setRenameEnabled(col, true); + item->startRename(col); + m_renameItem = item; + + emit renameStarted(item, col); +} + +// We don't get notified when rename is cancelled, this is called from the item +void ResourcesPanel::renameStopped(QListViewItem *) { + //kdDebug()<<k_funcinfo<<endl; + m_renameItem = 0; + emit selectionChanged(); +} + + +} //KPlato namespace + +#include "kptresourcespanel.moc" diff --git a/kplato/kptresourcespanel.h b/kplato/kptresourcespanel.h new file mode 100644 index 00000000..a5e1a46a --- /dev/null +++ b/kplato/kptresourcespanel.h @@ -0,0 +1,90 @@ +/* This file is part of the KDE project + Copyright (C) 2003 Thomas Zander <zander@kde.org> + + 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 KPTPRESOURCESPANEL_H +#define KPTPRESOURCESPANEL_H + +#include "kptresource.h" +#include "resourcespanelbase.h" + +#include <qlistbox.h> +#include <qstring.h> + +class QListViewItem; +class KCommand; + +namespace KPlato +{ + +class Project; +class GroupItem; +class ResourcesPanelResourceItem; +class ResourcesPanelGroupLVItem; +class Part; + +class ResourcesPanel : public ResourcesPanelBase { + Q_OBJECT +public: + ResourcesPanel (QWidget *parent, Project *project); + + bool ok(); + KCommand *buildCommand(Part *part); + + void sendChanged(); + + void renameStopped(QListViewItem* item); + +protected slots: + void slotAddGroup(); + void slotDeleteGroup(); + + void slotAddResource(); + void slotEditResource(); + void slotDeleteResource(); + + void slotGroupChanged(QListViewItem *item); + void slotGroupChanged(); + void slotResourceRename(const QString &newName); + void slotResourceChanged( QListBoxItem*); + void slotCurrentChanged( QListBoxItem*); + + void slotListDoubleClicked(QListViewItem*, const QPoint&, int); + void slotItemRenamed(QListViewItem *item, int col); + void slotRenameStarted(QListViewItem *item, int col); + void slotStartRename(QListViewItem *item, int col); +signals: + void changed(); + void selectionChanged(); + void startRename(QListViewItem *item, int col); + void renameStarted(QListViewItem *item, int col); + +private: + Project *project; + ResourcesPanelGroupLVItem *m_groupItem; + + QPtrList<GroupItem> m_groupItems; + QPtrList<GroupItem> m_deletedGroupItems; + + bool m_blockResourceRename; + QListViewItem *m_renameItem; +}; + +} //KPlato namespace + +#endif // PRESOURCESPANEL_H diff --git a/kplato/kptresourceview.cc b/kplato/kptresourceview.cc new file mode 100644 index 00000000..7744392d --- /dev/null +++ b/kplato/kptresourceview.cc @@ -0,0 +1,611 @@ +/* This file is part of the KDE project + Copyright (C) 2003 - 2004 Dag Andersen kplato@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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. +*/ + +#include "kptresourceview.h" + +#include "kptcalendar.h" +#include "kptduration.h" +#include "kptresourceappointmentsview.h" +#include "kptview.h" +#include "kptnode.h" +#include "kptproject.h" +#include "kpttask.h" +#include "kptresource.h" +#include "kptdatetime.h" +#include "kptcontext.h" + +#include <qheader.h> +#include <qpopupmenu.h> +#include <qpainter.h> +#include <qpaintdevicemetrics.h> +#include <qstyle.h> + +#include <klistview.h> +#include <klocale.h> +#include <kglobal.h> +#include <kprinter.h> + +#include <kdebug.h> + +namespace KPlato +{ + +class ResListView : public KListView { +public: + ResListView(QWidget * parent = 0, const char* name=0) + : KListView(parent, name) + {} + + int headerHeight() const { + return header()->count() > 0 ? header()->sectionRect(0).height() : 0; + } + virtual void paintToPrinter(QPainter *p, int x, int y, int w, int h) { + p->save(); + QColor bgc(193, 223, 255); + QBrush bg(bgc); + p->setBackgroundMode(Qt::OpaqueMode); + p->setBackgroundColor(bgc); + QHeader *head = header(); + int offset = 0; + QRect sr; + // Header shall always be at top/left on page + for (int s = 0; s < head->count(); ++s) { + sr = head->sectionRect(s); + if (offset > sr.x()) + offset = sr.x(); + } + for (int s = 0; s < head->count(); ++s) { + sr = head->sectionRect(s); + if (offset != 0) { + sr = QRect(sr.x()-offset, sr.y(), sr.width(), sr.height()); + } + //kdDebug()<<s<<": "<<head->label(s)<<" "<<sr<<endl; + if (sr.x()+sr.width() <= x || sr.x() >= x+w) { + //kdDebug()<<s<<": "<<h->label(s)<<" "<<sr<<": continue"<<endl; + continue; + } + QRect tr = sr; + if (sr.x() < x) { + tr.setX(x); + //kdDebug()<<s<<": "<<head->label(s)<<" "<<tr<<endl; + } + p->eraseRect(tr); + p->drawText(tr, columnAlignment(s)|Qt::AlignVCenter, head->label(s), -1); + } + p->restore(); + p->save(); + p->translate(0, headerHeight()); + drawAllContents(p, x, y, w, h); + p->restore(); + } + int calculateY(int ymin, int ymax) const { + QPtrList<ResListView::DrawableItem> drawables; + drawables.setAutoDelete(true); + QListViewItem *child = firstChild(); + int level = 0; + int ypos = 0; + for (; child; child = child->nextSibling()) { + ypos = buildDrawables(drawables, level, ypos, child, ymin, ymax); + } + int y = 0; + DrawableItem *item = drawables.getLast(); + if (item) { + y = item->y + item->i->height(); + } + //kdDebug()<<k_funcinfo<<y<<" ("<<ymin<<", "<<ymax<<")"<<endl; + return y; + } + class DrawableItem { + public: + DrawableItem(int level, int ypos, QListViewItem *item ) { y = ypos; l = level; i = item; }; + int y; + int l; + QListViewItem * i; + }; +protected: + int buildDrawables(QPtrList<ResListView::DrawableItem> &lst, int level, int ypos, QListViewItem *item, int ymin, int ymax) const { + int y = ypos; + int ih = item->height(); + if (y < ymin && y+ih > ymin) { + y = ymin; // include partial item at top + } + if (y >= ymin && y+ih < ymax) { // exclude partial item at bottom + ResListView::DrawableItem *dr = new ResListView::DrawableItem(level, y, item); + lst.append(dr); + //kdDebug()<<k_funcinfo<<level<<", "<<y<<" : "<<item->text(0)<<endl; + } + y += ih; + if (item->isOpen()) { + QListViewItem *child = item->firstChild(); + for (; child; child = child->nextSibling()) { + y = buildDrawables(lst, level+1, y, child, ymin, ymax); + } + } + return y; + } + // This is a copy of QListView::drawContentsOffset(), with a few changes + // because drawContentsOffset() only draws *visible* items, + // we want to draw *all* items. + // FIXME: Haven't got paintBraches() to work, atm live without it. + virtual void drawAllContents(QPainter * p, int cx, int cy, int cw, int ch) { + if ( columns() == 0 ) { + paintEmptyArea( p, QRect( cx, cy, cw, ch ) ); + return; + } + //kdDebug()<<k_funcinfo<<QRect(cx, cy, cw, ch)<<endl; + QPtrList<ResListView::DrawableItem> drawables; + drawables.setAutoDelete(true); + QListViewItem *child = firstChild(); + int level = 0; + int ypos = 0; + for (; child; child = child->nextSibling()) { + ypos = buildDrawables(drawables, level, ypos, child, cy, cy+ch); + } + + p->setFont( font() ); + + QPtrListIterator<ResListView::DrawableItem> it(drawables); + + QRect r; + int fx = -1, x, fc = 0, lc = 0; + int tx = -1; + ResListView::DrawableItem * current; + + while ( (current = it.current()) != 0 ) { + ++it; + int ih = current->i->height(); + int ith = current->i->totalHeight(); + int c; + int cs; + + // need to paint current? + if ( ih > 0 && current->y < cy+ch && current->y+ih > cy ) { + //kdDebug()<<k_funcinfo<<"Paint: "<<current->i->text(0)<<" y="<<current->y<<endl; + if ( fx < 0 ) { + // find first interesting column, once + x = 0; + c = 0; + cs = header()->cellSize( 0 ); + while ( x + cs <= cx && c < header()->count() ) { + x += cs; + c++; + if ( c < header()->count() ) + cs = header()->cellSize( c ); + } + fx = x; + fc = c; + while( x < cx + cw && c < header()->count() ) { + x += cs; + c++; + if ( c < header()->count() ) + cs = header()->cellSize( c ); + } + lc = c; + } + + x = fx; + c = fc; + // draw to last interesting column + + const QColorGroup &cg = ( palette().inactive() ); + + while ( c < lc && !drawables.isEmpty() ) { + int i = header()->mapToLogical( c ); + cs = header()->cellSize( c ); + r.setRect( x, current->y-cy, cs, ih ); + if ( i == 0 ) + r.setLeft( r.left() + current->l * treeStepSize() ); + + p->save(); + // No need to paint if the cell isn't technically visible + if ( !( r.width() == 0 || r.height() == 0 ) ) { + p->translate( r.left(), r.top() ); + int ac = header()->mapToLogical( c ); + // map to Left currently. This should change once we + // can really reverse the listview. + int align = columnAlignment( ac ); + if ( align == AlignAuto ) align = AlignLeft; + bool sel = current->i->isSelected(); + if (sel) + current->i->setSelected(false); + current->i->paintCell( p, cg, ac, r.width(), align ); + if (sel) + current->i->setSelected(sel); + } + p->restore(); + x += cs; + c++; + } + + } + + const int cell = header()->mapToActual( 0 ); + + if ( tx < 0 ) + tx = header()->cellPos( cell ); + + // do any children of current need to be painted? +/* FIXME: painting branches doesn't work for some reason... + if ( ih != ith && + rootIsDecorated() && + current->y + ith > cy && + current->y + ih < cy + ch && + tx + current->l * treeStepSize() < cx + cw && + tx + (current->l+1) * treeStepSize() > cx ) { + // compute the clip rectangle the safe way + + int rtop = current->y + ih; + int rbottom = current->y + ith; + int rleft = tx + current->l*treeStepSize(); + int rright = rleft + treeStepSize(); + + int crtop = QMAX( rtop, cy ); + int crbottom = QMIN( rbottom, cy+ch ); + int crleft = QMAX( rleft, cx ); + int crright = QMIN( rright, cx+cw ); + + r.setRect( crleft, crtop, + crright-crleft, crbottom-crtop ); + + if ( r.isValid() ) { + p->save(); + p->translate( rleft, crtop ); + //kdDebug()<<k_funcinfo<<"paintBranches: "<<current->i->text(0)<<endl; + + current->i->paintBranches( p, colorGroup(), treeStepSize(), + rtop - crtop, r.height() ); + p->restore(); + } + }*/ + } + } + +}; + +class ResourceItemPrivate : public KListViewItem { +public: + ResourceItemPrivate(Resource *r, QListViewItem *parent) + : KListViewItem(parent, r->name()), + resource(r) {} + + Resource *resource; + + virtual void paintCell(QPainter *p, const QColorGroup &cg, int column, int width, int align) { + QColorGroup g = cg; + if (m_columns[column] == 1) { + g.setColor(QColorGroup::Text, QColor(red)); + g.setColor(QColorGroup::HighlightedText, QColor(red)); + } + + KListViewItem::paintCell(p, g, column, width, align); + } + void setColumnState(int c, int state=1) { + m_columns[c] = state; + } +private: + QMap<int, int> m_columns; +}; + +class NodeItemPrivate : public KListViewItem { +public: + NodeItemPrivate(Task *n, QListView *parent) + : KListViewItem(parent, n->name()), + node(n) { + init(); + } + + NodeItemPrivate(QString name, QListView *parent) + : KListViewItem(parent, name), + node(0) { + init(); + } + + void setPriority(int col, int prio) { + if (prioColors.contains(prio)) { + columnPrio.insert(col, prio); + } else { + columnPrio.remove(col); + } + } + int priority(int col) { + if (columnPrio.contains(col)) { + return columnPrio[col]; + } + return 0; + } + + virtual void paintCell ( QPainter * p, const QColorGroup & cg, int column, int width, int align ) { + //kdDebug()<<k_funcinfo<<"c="<<column<<" prio="<<(columnPrio.contains(column)?columnPrio[column]:0)<<endl; + QColorGroup g = cg; + if (columnPrio.contains(column)) { + g.setColor(QColorGroup::Base, prioColors[columnPrio[column]]); + } + KListViewItem::paintCell(p, g, column, width, align); + } + + Task *node; +private: + void init() { + prioColors.insert(1, QColor(gray)); + prioColors.insert(2, QColor(green)); + prioColors.insert(3, QColor(yellow)); + prioColors.insert(4, QColor(red)); + } + QMap<int, QColor> prioColors; + QMap<int, int> columnPrio; +}; + +class AppointmentItem : public KListViewItem { +public: + AppointmentItem(Appointment *a, QDate &d, QListViewItem *parent) + : KListViewItem(parent), + appointment(a), + date(d) {} + + Appointment *appointment; + QDate date; +}; + +QSize ResourceView::sizeHint() const { + return minimumSizeHint(); // HACK: koshell splitter minimumSize problem +} + +ResourceView::ResourceView(View *view, QWidget *parent) + : QSplitter(parent, "Resource view"), + m_mainview(view), + m_selectedItem(0), + m_currentNode(0) +{ + setOrientation(QSplitter::Vertical); + + resList = new ResListView(this, "Resource list"); + resList->setItemMargin(2); +#if KDE_IS_VERSION(3,3,9) + resList->setShadeSortColumn(false); +#endif + resList->setRootIsDecorated(true); + resList->addColumn(i18n("Name")); + resList->setColumnAlignment(1, AlignHCenter); + resList->addColumn(i18n("Type")); + resList->setColumnAlignment(2, AlignHCenter); + resList->addColumn(i18n("Initials")); + resList->setColumnAlignment(3, AlignLeft); + resList->addColumn(i18n("Email")); + resList->setColumnAlignment(4, AlignHCenter); + resList->addColumn(i18n("Calendar Name")); + resList->setColumnAlignment(5, AlignRight); + resList->addColumn(i18n("Available From")); + resList->setColumnAlignment(6, AlignRight); + resList->addColumn(i18n("Available Until")); + resList->setColumnAlignment(7, AlignRight); + resList->addColumn(i18n("%")); + resList->setColumnAlignment(8, AlignRight); + resList->addColumn(i18n("Normal Rate")); + resList->setColumnAlignment(9, AlignRight); + resList->addColumn(i18n("Overtime Rate")); + + m_showAppointments = false; + m_appview = new ResourceAppointmentsView(view, this); + m_appview->hide(); + draw(view->getProject()); + + //connect(resList, SIGNAL(selectionChanged(QListViewItem*)), SLOT(resSelectionChanged(QListViewItem*))); + connect(resList, SIGNAL(selectionChanged()), SLOT(resSelectionChanged())); + connect(resList, SIGNAL( contextMenuRequested(QListViewItem*, const QPoint&, int)), SLOT(popupMenuRequested(QListViewItem*, const QPoint&, int))); + //NOTE: using doubleClicked, not executed() to be consistent with ganttview + connect(resList, SIGNAL(doubleClicked(QListViewItem*, const QPoint&, int)), SLOT(slotItemDoubleClicked(QListViewItem*))); + +} + +void ResourceView::zoom(double /*zoom*/) +{ +} + +Resource *ResourceView::currentResource() { + if (m_selectedItem) + return m_selectedItem->resource; + return 0; +} + +void ResourceView::draw(Project &project) +{ + //kdDebug()<<k_funcinfo<<endl; + resList->clear(); + m_appview->clear(); + m_selectedItem = 0; + + QPtrListIterator<ResourceGroup> it(project.resourceGroups()); + for (; it.current(); ++it) { + KListViewItem *item = new KListViewItem(resList, it.current()->name()); + item->setOpen(true); + drawResources(project, item, it.current()); + } + if (m_selectedItem) { + resList->setSelected(m_selectedItem, true); + } else { + resSelectionChanged(m_selectedItem); + } +} + + +void ResourceView::drawResources(const Project &proj, QListViewItem *parent, ResourceGroup *group) +{ + //kdDebug()<<k_funcinfo<<"group: "<<group->name()<<" ("<<group<<")"<<endl; + QPtrListIterator<Resource> it(group->resources()); + for (; it.current(); ++it) { + Resource *r = it.current(); + ResourceItemPrivate *item = new ResourceItemPrivate(r, parent); + // set column colors + item->setColumnState(0, 0); + item->setColumnState(4, 0); + item->setColumnState(5, 0); + item->setColumnState(6, 0); + item->setColumnState(7, 0); + if (r->calendar() == 0) { + item->setColumnState(0, 1); + item->setColumnState(4, 1); + } + if (proj.constraint() == Node::MustFinishOn) { + if (proj.mustFinishOn() <= r->availableFrom()) { + item->setColumnState(0, 1); + item->setColumnState(5, 1); + } + } else { + if (proj.mustStartOn() >= r->availableUntil()) { + item->setColumnState(0, 1); + item->setColumnState(6, 1); + } + } + if (r->units() == 0) { + item->setColumnState(0, 1); + item->setColumnState(7, 1); + } + // and the texts + item->setText(0, r->name()); // refresh + switch (r->type()) { + case Resource::Type_Work: + item->setText(1, i18n("Work")); + break; + case Resource::Type_Material: + item->setText(1, i18n("Material")); + break; + default: + item->setText(1, i18n("Undefined")); + break; + } + item->setText(2, r->initials()); + item->setText(3, r->email()); + item->setText(4, r->calendar() ? r->calendar()->name() : i18n("None")); + item->setText(5, KGlobal::locale()->formatDateTime(r->availableFrom())); + item->setText(6, KGlobal::locale()->formatDateTime(r->availableUntil())); + item->setText(7, QString().setNum(r->units())); + item->setText(8, KGlobal::locale()->formatMoney(r->normalRate())); + item->setText(9, KGlobal::locale()->formatMoney(r->overtimeRate())); + if (!m_selectedItem) { + m_selectedItem = item; + } + } +} + + +void ResourceView::resSelectionChanged() { + //kdDebug()<<k_funcinfo<<endl; + resSelectionChanged(resList->selectedItem()); +} + +void ResourceView::resSelectionChanged(QListViewItem *item) { + //kdDebug()<<k_funcinfo<<item<<endl; + ResourceItemPrivate *ritem = dynamic_cast<ResourceItemPrivate *>(item); + if (ritem) { + m_selectedItem = ritem; + if (m_showAppointments) { + m_appview->show(); + m_appview->draw(ritem->resource, m_mainview->getProject().startTime().date(), m_mainview->getProject().endTime().date()); + } else { + m_appview->hide(); + } + return; + } + m_selectedItem = 0; + m_appview->clear(); +} + + +void ResourceView::slotItemDoubleClicked(QListViewItem*) { + emit itemDoubleClicked(); +} + +void ResourceView::popupMenuRequested(QListViewItem* item, const QPoint & pos, int) +{ + ResourceItemPrivate *ritem = dynamic_cast<ResourceItemPrivate *>(item); + if (ritem) { + if (ritem != m_selectedItem) + resSelectionChanged(ritem); + QPopupMenu *menu = m_mainview->popupMenu("resource_popup"); + if (menu) + { + menu->exec(pos); + //kdDebug()<<k_funcinfo<<"id="<<id<<endl; + } + else + kdDebug()<<k_funcinfo<<"No menu!"<<endl; + } +} + +QValueList<int> ResourceView::listOffsets(int pageHeight) const { + QValueList<int> lst; + int hh = resList->headerHeight(); + int ph = pageHeight-hh; + int lh = resList->contentsHeight() - hh; // list height ex header. + int ly = 0; + kdDebug()<<k_funcinfo<<ly<<", "<<lh<<endl; + while (ly < lh) { + lst << ly; + ly = resList->calculateY(ly+1, ly+ph); // offset into the list, ex header + //kdDebug()<<k_funcinfo<<ly<<", "<<lh<<endl; + } + return lst; +} + +void ResourceView::print(KPrinter &printer) { + //kdDebug()<<k_funcinfo<<endl; + QPaintDeviceMetrics m = QPaintDeviceMetrics ( &printer ); + uint top, left, bottom, right; + printer.margins(&top, &left, &bottom, &right); + //kdDebug()<<m.width()<<"x"<<m.height()<<" : "<<top<<", "<<left<<", "<<bottom<<", "<<right<<" : "<<size()<<endl; + QPainter p; + p.begin(&printer); + p.setViewport(left, top, m.width()-left-right, m.height()-top-bottom); + p.setClipRect(left, top, m.width()-left-right, m.height()-top-bottom); + QRect preg = p.clipRegion(QPainter::CoordPainter).boundingRect(); + //kdDebug()<<"p="<<preg<<endl; + //p.drawRect(preg.x(), preg.y(), preg.width()-1, preg.height()-1); + int ch = resList->contentsHeight(); + int cw = resList->contentsWidth(); + double scale = (double)preg.width()/(double)(cw); + //kdDebug()<<"scale="<<scale<<endl; + if (scale < 1.0) { + p.scale(scale, scale); + } + int ph = preg.height()-resList->headerHeight(); + QValueList<int> lst = listOffsets(preg.height()); + for (int i=0; i < lst.count(); ++i) { + //kdDebug()<<"Page "<<i+1<<": "<<"scale="<<scale<<" "<<lst[i]<<" : "<<cw<<"x"<<ch<<endl; + if (i > 0) { + printer.newPage(); + } + resList->paintToPrinter(&p, 0, lst[i], cw, ph); + } + + p.end(); +} + +bool ResourceView::setContext(Context::Resourceview &/*context*/) { + //kdDebug()<<k_funcinfo<<endl; + return true; +} + +void ResourceView::getContext(Context::Resourceview &/*context*/) const { + //kdDebug()<<k_funcinfo<<endl; +} + + +} //KPlato namespace + +#include "kptresourceview.moc" diff --git a/kplato/kptresourceview.h b/kplato/kptresourceview.h new file mode 100644 index 00000000..391653af --- /dev/null +++ b/kplato/kptresourceview.h @@ -0,0 +1,107 @@ +/* This file is part of the KDE project + Copyright (C) 2003 - 2004 Dag Andersen <kplato@kde.org> + + 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 KPTRESOURCEVIEW_H +#define KPTRESOURCEVIEW_H + +#include <qsplitter.h> +#include <qdatetime.h> +#include <qvaluelist.h> + +#include "kptcontext.h" + +class QPoint; +class QListViewItem; + +class KPrinter; + +namespace KPlato +{ +class ResListView; + +class View; +class Project; +class Resource; +class Node; + +class ResourceAppointmentsView; +class ResourceGroup; +class Resource; +class ResourceItemPrivate; + + + class ResourceView : public QSplitter +{ + Q_OBJECT + + public: + ResourceView(View *view, QWidget *parent); + + //~ResourceView(); + + void zoom(double zoom); + + void draw(Project &project); + View *mainView(); + + Resource *currentResource(); + + QValueList<int> listOffsets(int pageHeight) const; + void print(KPrinter &printer); + + Node *currentNode() const { return m_currentNode; } + + virtual bool setContext(Context::Resourceview &context); + virtual void getContext(Context::Resourceview &context) const; + + virtual QSize sizeHint() const; + +public slots: + void setShowAppointments(bool on) { m_showAppointments = on; } + +signals: + void itemDoubleClicked(); + +protected slots: + void resSelectionChanged(); + void resSelectionChanged(QListViewItem *item); + void slotItemDoubleClicked(QListViewItem*); + void popupMenuRequested(QListViewItem * item, const QPoint & pos, int); + +private: + void drawResources(const Project &proj, QListViewItem *parent, ResourceGroup *group); + +private: + View *m_mainview; + int m_defaultFontSize; + + ResourceItemPrivate *m_selectedItem; + ResListView *resList; + ResourceAppointmentsView *m_appview; + Node *m_currentNode; + QDate m_start; + QDate m_end; + + bool m_showAppointments; + +}; + +} //KPlato namespace + +#endif diff --git a/kplato/kptschedule.cc b/kplato/kptschedule.cc new file mode 100644 index 00000000..e3f23f07 --- /dev/null +++ b/kplato/kptschedule.cc @@ -0,0 +1,681 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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. +*/ + +#include "kptschedule.h" + +#include "kptappointment.h" +#include "kptdatetime.h" +#include "kptduration.h" +#include "kptnode.h" + +#include <qdom.h> +#include <qstring.h> +#include <qstringlist.h> + +#include <klocale.h> +#include <kdebug.h> + +namespace KPlato +{ + +Schedule::Schedule() + : m_type(Expected), + m_id(0), + m_deleted(false), + m_parent(0) { +} + +Schedule::Schedule(Schedule *parent) + : m_type(Expected), + m_id(0), + m_deleted(false), + m_appointments(), + m_parent(parent) { + + if (parent) { + m_name = parent->name(); + m_type = parent->type(); + m_id = parent->id(); + } + m_appointments.setAutoDelete(true); + //kdDebug()<<k_funcinfo<<"("<<this<<") Name: '"<<name<<"' Type="<<type<<" id="<<id<<endl; +} + +Schedule::Schedule(QString name, Type type, long id) + : m_name(name), + m_type(type), + m_id(id), + m_deleted(false), + m_appointments(), + m_parent(0) { + + //kdDebug()<<k_funcinfo<<"("<<this<<") Name: '"<<name<<"' Type="<<type<<" id="<<id<<endl; + m_appointments.setAutoDelete(true); +} + +Schedule::~Schedule() { +} + +void Schedule::setParent(Schedule *parent) { + m_parent = parent; +} + +void Schedule::setDeleted(bool on) { + //kdDebug()<<k_funcinfo<<"deleted="<<on<<endl; + m_deleted = on; +} + +bool Schedule::isDeleted() const { + return m_parent == 0 ? m_deleted : m_parent->isDeleted(); +} + +void Schedule::setType(const QString type) { + m_type = Expected; + if (type == "Expected") + m_type = Expected; + else if (type == "Optimistic") + m_type = Optimistic; + else if (type == "Pessimistic") + m_type = Pessimistic; +} + +QString Schedule::typeToString(bool translate) const { + if (translate) { + if (m_type == Expected) + return i18n("Expected"); + if (m_type == Optimistic) + return i18n("Optimistic"); + if (m_type == Pessimistic) + return i18n("Pessimistic"); + return i18n("Expected"); + } else { + if (m_type == Expected) + return "Expected"; + if (m_type == Optimistic) + return "Optimistic"; + if (m_type == Pessimistic) + return "Pessimistic"; + return "Expected"; + } +} + +void Schedule::initiateCalculation() { + resourceError = false; + resourceOverbooked = false; + schedulingError = false; + inCriticalPath = false; + workStartTime = DateTime(); + workEndTime = DateTime(); +} + +void Schedule::calcResourceOverbooked() { + resourceOverbooked = false; + QPtrListIterator<Appointment> it = m_appointments; + for (; it.current(); ++it) { + if (it.current()->resource()->isOverbooked(startTime, endTime)) { + resourceOverbooked = true; + break; + } + } +} + +QStringList Schedule::overbookedResources() const { + QStringList rl; + QPtrListIterator<Appointment> it = m_appointments; + for (; it.current(); ++it) { + if (it.current()->resource()->isOverbooked(it.current()->startTime(), it.current()->endTime())) { + rl += it.current()->resource()->resource()->name(); + } + } + return rl; +} + +bool Schedule::loadXML(const QDomElement &sch) { + m_name = sch.attribute("name"); + setType(sch.attribute("type")); + m_id = sch.attribute("id").toLong(); + return true; +} + +void Schedule::saveXML(QDomElement &element) const { + QDomElement sch = element.ownerDocument().createElement("schedule"); + element.appendChild(sch); + saveCommonXML(sch); +} + +void Schedule::saveCommonXML(QDomElement &element) const { + //kdDebug()<<k_funcinfo<<m_name<<" save schedule"<<endl; + element.setAttribute("name", m_name); + element.setAttribute("type", typeToString()); + element.setAttribute("id", m_id); +} + +void Schedule::saveAppointments(QDomElement &element) const { + //kdDebug()<<k_funcinfo<<endl; + QPtrListIterator<Appointment> it = m_appointments; + for (; it.current(); ++it) { + it.current()->saveXML(element); + } +} + +bool Schedule::add(Appointment *appointment) { + if (m_appointments.findRef(appointment) != -1) { + kdError()<<k_funcinfo<<"Appointment allready exists"<<endl; + return false; + } + m_appointments.append(appointment); + //if (resource()) kdDebug()<<k_funcinfo<<"For resource '"<<resource()->name()<<"'"<<endl; + //if (node()) kdDebug()<<k_funcinfo<<"For node '"<<node()->name()<<"'"<<endl; + return true; +} + +void Schedule::removeAppointment(Appointment *appointment) { + takeAppointment(appointment); + delete appointment; +} + +void Schedule::takeAppointment(Appointment *appointment) { + int i = m_appointments.findRef(appointment); + if (i != -1) { + m_appointments.take(i); + //kdDebug()<<k_funcinfo<<"Taken: "<<appointment<<endl; + if (appointment->node()) + appointment->node()->takeAppointment(appointment); + } else { + //kdDebug()<<k_funcinfo<<"Couldn't find appointment: "<<appointment<<endl; + } +} + +Appointment *Schedule::findAppointment(Schedule *resource, Schedule *node) { + QPtrListIterator<Appointment> it = m_appointments; + for (; it.current(); ++it) { + if (it.current()->node() == node && it.current()->resource() == resource) + return it.current(); + } + return 0; +} + +EffortCostMap Schedule::plannedEffortCostPrDay(const QDate &start, const QDate &end) const { + //kdDebug()<<k_funcinfo<<m_name<<endl; + EffortCostMap ec; + QPtrListIterator<Appointment> it(m_appointments); + for (; it.current(); ++it) { + //kdDebug()<<k_funcinfo<<m_name<<endl; + ec += it.current()->plannedPrDay(start, end); + } + return ec; +} + +Duration Schedule::plannedEffort() const { + //kdDebug()<<k_funcinfo<<endl; + Duration eff; + QPtrListIterator<Appointment> it(m_appointments); + for (; it.current(); ++it) { + eff += it.current()->plannedEffort(); + } + return eff; +} + +Duration Schedule::plannedEffort(const QDate &date) const { + //kdDebug()<<k_funcinfo<<endl; + Duration eff; + QPtrListIterator<Appointment> it(m_appointments); + for (; it.current(); ++it) { + eff += it.current()->plannedEffort(date); + } + return eff; +} + +Duration Schedule::plannedEffortTo(const QDate &date) const { + //kdDebug()<<k_funcinfo<<endl; + Duration eff; + QPtrListIterator<Appointment> it(m_appointments); + for (; it.current(); ++it) { + eff += it.current()->plannedEffortTo(date); + } + return eff; +} + +Duration Schedule::actualEffort() const { + //kdDebug()<<k_funcinfo<<endl; + Duration eff; + QPtrListIterator<Appointment> it(m_appointments); + for (; it.current(); ++it) { + eff += it.current()->actualEffort(); + } + return eff; +} + +Duration Schedule::actualEffort(const QDate &date) const { + //kdDebug()<<k_funcinfo<<endl; + Duration eff; + QPtrListIterator<Appointment> it(m_appointments); + for (; it.current(); ++it) { + eff += it.current()->actualEffort(date); + } + return eff; +} + +Duration Schedule::actualEffortTo(const QDate &date) const { + //kdDebug()<<k_funcinfo<<endl; + Duration eff; + QPtrListIterator<Appointment> it(m_appointments); + for (; it.current(); ++it) { + eff += it.current()->actualEffortTo(date); + } + return eff; +} + +double Schedule::plannedCost() const { + //kdDebug()<<k_funcinfo<<endl; + double c = 0; + QPtrListIterator<Appointment> it(m_appointments); + for (; it.current(); ++it) { + c += it.current()->plannedCost(); + } + return c; +} + +double Schedule::plannedCost(const QDate &date) const { + //kdDebug()<<k_funcinfo<<endl; + double c = 0; + QPtrListIterator<Appointment> it(m_appointments); + for (; it.current(); ++it) { + c += it.current()->plannedCost(date); + } + return c; +} + +double Schedule::plannedCostTo(const QDate &date) const { + //kdDebug()<<k_funcinfo<<endl; + double c = 0; + QPtrListIterator<Appointment> it(m_appointments); + for (; it.current(); ++it) { + c += it.current()->plannedCostTo(date); + } + return c; +} + +double Schedule::actualCost() const { + //kdDebug()<<k_funcinfo<<endl; + double c = 0; + QPtrListIterator<Appointment> it(m_appointments); + for (; it.current(); ++it) { + c += it.current()->actualCost(); + } + return c; +} + +double Schedule::actualCost(const QDate &date) const { + //kdDebug()<<k_funcinfo<<endl; + double c = 0; + QPtrListIterator<Appointment> it(m_appointments); + for (; it.current(); ++it) { + c += it.current()->actualCost(date); + } + return c; +} + +double Schedule::actualCostTo(const QDate &date) const { + //kdDebug()<<k_funcinfo<<endl; + double c = 0; + QPtrListIterator<Appointment> it(m_appointments); + for (; it.current(); ++it) { + c += it.current()->actualCostTo(date); + } + return c; +} + +//------------------------------------------------- +NodeSchedule::NodeSchedule() + : Schedule(), + m_node(0) { + //kdDebug()<<k_funcinfo<<"("<<this<<")"<<endl; + init(); +} + +NodeSchedule::NodeSchedule(Node *node, QString name, Schedule::Type type, long id) + : Schedule(name, type, id), + m_node(node) { + //kdDebug()<<k_funcinfo<<"node name: "<<node->name()<<endl; + init(); +} + +NodeSchedule::NodeSchedule(Schedule *parent, Node *node) + : Schedule(parent), + m_node(node) { + + //kdDebug()<<k_funcinfo<<"node name: "<<node->name()<<endl; + init(); +} + +NodeSchedule::~NodeSchedule() { + //kdDebug()<<k_funcinfo<<"("<<this<<")"<<endl; +} + +void NodeSchedule::init() { + resourceError = false; + resourceOverbooked = false; + resourceNotAvailable = false; + schedulingError = false; + notScheduled = true; + inCriticalPath = false; +} + +void NodeSchedule::setDeleted(bool on) { + //kdDebug()<<k_funcinfo<<"deleted="<<on<<endl; + m_deleted = on; + // set deleted also for possible resource schedules + QPtrListIterator<Appointment> it = m_appointments; + for (; it.current(); ++it) { + if (it.current()->resource()) { + it.current()->resource()->setDeleted(on); + } + } +} + +bool NodeSchedule::loadXML(const QDomElement &sch) { + //kdDebug()<<k_funcinfo<<endl; + QString s; + Schedule::loadXML(sch); + s = sch.attribute("earlieststart"); + if (s != "") + earliestStart = DateTime::fromString(s); + s = sch.attribute("latestfinish"); + if (s != "") + latestFinish = DateTime::fromString(s); + s = sch.attribute("start"); + if (s != "") + startTime = DateTime::fromString(s); + s = sch.attribute("end"); + if (s != "") + endTime = DateTime::fromString(s); + s = sch.attribute("start-work"); + if (s != "") + workStartTime = DateTime::fromString(s); + s = sch.attribute("end-work"); + if (s != "") + workEndTime = DateTime::fromString(s); + duration = Duration::fromString(sch.attribute("duration")); + + inCriticalPath = sch.attribute("in-critical-path", "0").toInt(); + resourceError = sch.attribute("resource-error", "0").toInt(); + resourceOverbooked = sch.attribute("resource-overbooked", "0").toInt(); + resourceNotAvailable = sch.attribute("resource-not-available", "0").toInt(); + schedulingError = sch.attribute("scheduling-conflict", "0").toInt(); + notScheduled = sch.attribute("not-scheduled", "1").toInt(); + + return true; +} + +void NodeSchedule::saveXML(QDomElement &element) const { + //kdDebug()<<k_funcinfo<<endl; + QDomElement sch = element.ownerDocument().createElement("schedule"); + element.appendChild(sch); + saveCommonXML(sch); + + if (earliestStart.isValid()) + sch.setAttribute("earlieststart",earliestStart.toString(Qt::ISODate)); + if (latestFinish.isValid()) + sch.setAttribute("latestfinish",latestFinish.toString(Qt::ISODate)); + if (startTime.isValid()) + sch.setAttribute("start",startTime.toString(Qt::ISODate)); + if (endTime.isValid()) + sch.setAttribute("end",endTime.toString(Qt::ISODate)); + if (workStartTime.isValid()) + sch.setAttribute("start-work", workStartTime.toString(Qt::ISODate)); + if (workEndTime.isValid()) + sch.setAttribute("end-work", workEndTime.toString(Qt::ISODate)); + + sch.setAttribute("duration",duration.toString()); + + sch.setAttribute("in-critical-path",inCriticalPath); + sch.setAttribute("resource-error",resourceError); + sch.setAttribute("resource-overbooked",resourceOverbooked); + sch.setAttribute("resource-not-available",resourceNotAvailable); + sch.setAttribute("scheduling-conflict",schedulingError); + sch.setAttribute("not-scheduled",notScheduled); +} + +void NodeSchedule::addAppointment(Schedule *resource, DateTime &start, DateTime &end, double load) { + //kdDebug()<<k_funcinfo<<endl; + Appointment *a = findAppointment(resource, this); + if (a != 0) { + //kdDebug()<<k_funcinfo<<"Add interval"<<endl; + a->addInterval(start, end, load); + return; + } + a = new Appointment(resource, this, start, end, load); + if (!add(a)) { + delete a; + } + if (!resource->add(a)) { + delete a; + } +} + +//----------------------------------------------- +ResourceSchedule::ResourceSchedule() + : Schedule(), + m_resource(0) { + //kdDebug()<<k_funcinfo<<"("<<this<<")"<<endl; +} + +ResourceSchedule::ResourceSchedule(Resource *resource, QString name, Schedule::Type type, long id) + : Schedule(name, type, id), + m_resource(resource), + m_parent(0) { + //kdDebug()<<k_funcinfo<<"resource: "<<resource->name()<<endl; +} + +ResourceSchedule::ResourceSchedule(Schedule *parent, Resource *resource) + : Schedule(parent), + m_resource(resource), + m_parent(parent) { + //kdDebug()<<k_funcinfo<<"resource: "<<resource->name()<<endl; +} + +ResourceSchedule::~ResourceSchedule() { + //kdDebug()<<k_funcinfo<<"("<<this<<")"<<endl; +} + +void ResourceSchedule::addAppointment(Schedule *node, DateTime &start, DateTime &end, double load) { + //kdDebug()<<k_funcinfo<<endl; + Appointment *a = findAppointment(this, node); + if (a != 0) { + //kdDebug()<<k_funcinfo<<"Add interval"<<endl; + a->addInterval(start, end, load); + return; + } + a = new Appointment(this, node, start, end, load); + if (!add(a)) { + delete a; + } + if (!node->add(a)) { + delete a; + } +} + +bool ResourceSchedule::isOverbooked() const { + return false; +} + +bool ResourceSchedule::isOverbooked(const DateTime &start, const DateTime &end) const { + if (m_resource == 0) + return false; + //kdDebug()<<k_funcinfo<<start.toString()<<" - "<<end.toString()<<endl; + Appointment a = appointmentIntervals(); + QPtrListIterator<AppointmentInterval> it = a.intervals(); + for (; it.current(); ++it) { + if ((!end.isValid() || it.current()->startTime() < end) && + (!start.isValid() || it.current()->endTime() > start)) + { + if (it.current()->load() > m_resource->units()) { + //kdDebug()<<k_funcinfo<<m_name<<" overbooked"<<endl; + return true; + } + } + if (it.current()->startTime() >= end) + break; + } + //kdDebug()<<k_funcinfo<<m_name<<" not overbooked"<<endl; + return false; +} + +Appointment ResourceSchedule::appointmentIntervals() const { + Appointment a; + QPtrListIterator<Appointment> it = m_appointments; + for (; it.current(); ++it) { + a += *(it.current()); + } + return a; +} + +double ResourceSchedule::normalRatePrHour() const { + return m_resource ? m_resource->normalRate() : 0.0; +} + +//-------------------------------------- +MainSchedule::MainSchedule() + : NodeSchedule() { + //kdDebug()<<k_funcinfo<<"("<<this<<")"<<endl; + init(); +} + +MainSchedule::MainSchedule(Node *node, QString name, Schedule::Type type, long id) + : NodeSchedule(node, name, type, id) { + //kdDebug()<<k_funcinfo<<"node name: "<<node->name()<<endl; + init(); +} + +MainSchedule::~MainSchedule() { + //kdDebug()<<k_funcinfo<<"("<<this<<")"<<endl; +} + +bool MainSchedule::loadXML(const QDomElement &sch, Project &project) { + kdDebug()<<k_funcinfo<<endl; + QString s; + Schedule::loadXML(sch); + + s = sch.attribute("start"); + if (s != "") + startTime = DateTime::fromString(s); + s = sch.attribute("end"); + if (s != "") + endTime = DateTime::fromString(s); + + QDomNodeList al = sch.childNodes(); + kdDebug()<<k_funcinfo<<"No of appointments: "<<al.count()<<endl; + for (unsigned int i=0; i<al.count(); ++i) { + if (al.item(i).isElement()) { + QDomElement app = al.item(i).toElement(); + if (app.tagName() == "appointment") { + // Load the appointments. + // Resources and tasks must allready loaded + Appointment *child = new Appointment(); + if (!child->loadXML(app, project, *this)) { + // TODO: Complain about this + kdError()<<k_funcinfo<<"Failed to load appointment"<<endl; + delete child; + } + } + } + } + return true; +} + +void MainSchedule::saveXML(QDomElement &element) const { + saveCommonXML(element); + + element.setAttribute("start",startTime.toString(Qt::ISODate)); + element.setAttribute("end",endTime.toString(Qt::ISODate)); +} + +#ifndef NDEBUG +void Schedule::printDebug(QString indent) { + kdDebug()<<indent<<"Schedule["<<m_id<<"] '"<<m_name<<"' type: "<<typeToString()<<" ("<<m_type<<")"<<(isDeleted()?" Deleted":"")<<endl; +} +void NodeSchedule::printDebug(QString indent) { + Schedule::printDebug(indent); + indent += "! "; + if (m_parent == 0) + kdDebug()<<indent<<"No parent schedule!"<<endl; + if (!notScheduled) { + if (node()) kdDebug()<<indent<<"Node: "<<node()->name()<<endl; + else kdDebug()<<indent<<"No parent node!"<<endl; + } + kdDebug()<<indent<<"Not scheduled="<<notScheduled<<endl; + kdDebug()<<indent<<"Start time: "<<startTime.toString()<<endl; + kdDebug()<<indent<<"End time: " <<endTime.toString()<<endl; + kdDebug()<<indent<<"Duration: "<<duration.seconds()<<QCString(" secs")<<" ("<<duration.toString()<<")"<<endl; + kdDebug()<<indent<<"Earliest start: "<<earliestStart.toString()<<endl; + kdDebug()<<indent<<"Latest finish: " <<latestFinish.toString()<<endl; + + kdDebug()<<indent<<"resourceError="<<resourceError<<endl; + kdDebug()<<indent<<"schedulingError="<<schedulingError<<endl; + kdDebug()<<indent<<"resourceNotAvailable="<<resourceNotAvailable<<endl; + kdDebug()<<indent<<"Resource overbooked="<<resourceOverbooked<<endl; + kdDebug()<<indent<<" "<<overbookedResources()<<endl; + + kdDebug()<<indent<<"inCriticalPath="<<inCriticalPath<<endl; + kdDebug()<<indent<<endl; + kdDebug()<<indent<<"workStartTime="<<workStartTime.toString()<<endl; + kdDebug()<<indent<<"workEndTime="<<workEndTime.toString()<<endl; + kdDebug()<<indent<<endl; + kdDebug()<<indent<<"Appointments: "<<m_appointments.count()<<endl; + QPtrListIterator<Appointment> it = m_appointments; + for (; it.current(); ++it) { + it.current()->printDebug(indent + " "); + } +} +void ResourceSchedule::printDebug(QString indent) { + Schedule::printDebug(indent); + indent += "! "; + if (m_parent == 0) + kdDebug()<<indent<<"No parent schedule!"<<endl; + if (resource()) kdDebug()<<indent<<"Resource: "<<resource()->name()<<endl; + else kdDebug()<<indent<<"No parent resource!"<<endl; + kdDebug()<<indent<<endl; + kdDebug()<<indent<<"Appointments: "<<m_appointments.count()<<endl; +} + +void MainSchedule::printDebug(QString indent) { + Schedule::printDebug(indent); + indent += "! "; + if (node()) kdDebug()<<indent<<"Node: "<<node()->name()<<endl; + else kdDebug()<<indent<<"No parent node!"<<endl; + + kdDebug()<<indent<<"Not scheduled="<<notScheduled<<endl; + kdDebug()<<indent<<"Start time: "<<startTime.toString()<<endl; + kdDebug()<<indent<<"End time: " <<endTime.toString()<<endl; + kdDebug()<<indent<<"Duration: "<<duration.seconds()<<QCString(" secs")<<" ("<<duration.toString()<<")"<<endl; + kdDebug()<<indent<<"Earliest start: "<<earliestStart.toString()<<endl; + kdDebug()<<indent<<"Latest finish: " <<latestFinish.toString()<<endl; + + kdDebug()<<indent<<endl; + kdDebug()<<indent<<"Appointments: "<<m_appointments.count()<<endl; + QPtrListIterator<Appointment> it = m_appointments; + for (; it.current(); ++it) { + it.current()->printDebug(indent + " "); + } +} +#endif + +} //namespace KPlato + diff --git a/kplato/kptschedule.h b/kplato/kptschedule.h new file mode 100644 index 00000000..29cb135d --- /dev/null +++ b/kplato/kptschedule.h @@ -0,0 +1,325 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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 KPTSCHEDULE_H +#define KPTSCHEDULE_H + +#include "kpteffortcostmap.h" +#include "kptresource.h" + +#include <qintdict.h> +#include <qptrlist.h> +#include <qstring.h> + +class QDomElement; +class QStringList; + +namespace KPlato +{ + +class Appointment; +class DateTime; +class Duration; +class Node; +class Task; + +/** + * The Schedule class holds data calculated during project + * calculation and scheduling, eg start- and end-times and + * appointments. + * There is one schedule per node and one per resource. + * Schedules can be of type Expected, Optimistic or Pessimistic + * refering to which estimate is used for the calculation. + * Schedule is subclassed into: + * MainSchedule Used by the main project. + * NodeSchedule Used by all other nodes (tasks). + * ResourceSchedule Used by resources. + */ +class Schedule +{ +public: + //NOTE: Must match Effort::Use atm. + enum Type { Expected=0, //Effort::Use_Expected + Optimistic=1, //Effort::Use_Optimistic + Pessimistic=2 //Effort::Use_Pessimistic + }; + + Schedule(); + Schedule(Schedule *parent); + Schedule(QString name, Type type, long id); + ~Schedule(); + + QString name() const { return m_name; } + void setName(QString name) { m_name = name; } + Type type() const { return m_type; } + void setType(Type type) { m_type = type; } + void setType(QString type); + QString typeToString(bool translate=false) const; + long id() const { return m_id; } + void setId(long id) { m_id = id; } + void setParent(Schedule *parent); + Schedule *parent() const { return m_parent; } + virtual bool isDeleted() const; + virtual void setDeleted(bool on); + + virtual Resource *resource() const { return 0; } + virtual Node *node() const { return 0; } + + virtual bool loadXML(const QDomElement &element); + virtual void saveXML(QDomElement &element) const; + void saveCommonXML(QDomElement &element) const; + void saveAppointments(QDomElement &element) const; + + /// Return the list of appointments + QPtrList<Appointment> &appointments() { return m_appointments; } + /// Adds appointment to this schedule only + virtual bool add(Appointment *appointment); + /// Adds appointment to both this resource schedule and node schedule + virtual void addAppointment(Schedule */*other*/, DateTime &/*start*/, DateTime &/*end*/, double /*load*/=100) {} + /// removes appointment and deletes it (independent of setAutoDelete) + void removeAppointment(Appointment *appointment); + /// removes appointment without deleting it (independent of setAutoDelete) + void takeAppointment(Appointment *appointment); + Appointment *findAppointment(Schedule *resource, Schedule *node); + + Appointment appointmentIntervals() const; + + virtual bool isOverbooked() const { return false; } + virtual bool isOverbooked(const DateTime &/*start*/, const DateTime &/*end*/) const { return false; } + virtual QStringList overbookedResources() const; + + virtual EffortCostMap plannedEffortCostPrDay(const QDate &start, const QDate &end) const; + + /// Returns the total planned effort for this task (or subtasks) + virtual Duration plannedEffort() const; + /// Returns the total planned effort for this task (or subtasks) on date + virtual Duration plannedEffort(const QDate &date) const; + /// Returns the planned effort up to and including date + virtual Duration plannedEffortTo(const QDate &date) const; + + /// Returns the total actual effort for this task (or subtasks) + virtual Duration actualEffort() const; + /// Returns the total actual effort for this task (or subtasks) on date + virtual Duration actualEffort(const QDate &date) const; + /// Returns the total actual effort for this task (or subtasks) up to and including date + virtual Duration actualEffortTo(const QDate &date) const; + + /** + * Planned cost is the sum total of all resources and other costs + * planned for this node. + */ + virtual double plannedCost() const; + + /// Planned cost on date + virtual double plannedCost(const QDate &date) const; + /** + * Planned cost from start of activity up to and including date + * is the sum of all resource costs and other costs planned for this node. + */ + virtual double plannedCostTo(const QDate &date) const; + /** + * Actual cost is the sum total of the reported costs actually used + * for this node. + */ + virtual double actualCost() const; + /// Actual cost on date + virtual double actualCost(const QDate &date) const; + /// Actual cost up to and including date + virtual double actualCostTo(const QDate &date) const; + + /// Effort based performance index + double effortPerformanceIndex(const QDate &/*date*/, bool */*error=0*/) { return 0.0; } + /// Cost performance index + double costPerformanceIndex(const QDate &/*date*/, bool */*error=0*/) { return 0.0; } + + virtual double normalRatePrHour() const { return 0.0; } + + void setEarliestStart(DateTime &dt) { earliestStart = dt; } + void setLatestFinish(DateTime &dt) { latestFinish = dt; } + + virtual void initiateCalculation(); + virtual void calcResourceOverbooked(); + + void setScheduled(bool on) { notScheduled = !on; } + bool isScheduled() const { return !notScheduled; } + + DateTime start() const { return startTime; } + DateTime end() const { return endTime; } + +protected: + QString m_name; + Type m_type; + long m_id; + bool m_deleted; + + QPtrList<Appointment> m_appointments; + Schedule *m_parent; + + friend class Node; + friend class Task; + friend class Project; + friend class Resource; + friend class RecalculateProjectCmd; + /** + * earliestStart is calculated by PERT/CPM. + * A task may be scheduled to start later because of constraints + * or resource availability etc. + */ + DateTime earliestStart; + /** + * latestFinish is calculated by PERT/CPM. + * A task may be scheduled to finish earlier because of constraints + * or resource availability etc. + */ + DateTime latestFinish; + /** startTime is the scheduled start time. + * It depends on constraints (i.e. ASAP/ALAP) and resource availability. + * It will always be later or equal to @ref earliestStart + */ + DateTime startTime; + /** + * m_endTime is the scheduled finish time. + * It depends on constraints (i.e. ASAP/ALAP) and resource availability. + * It will always be earlier or equal to @ref latestFinish + */ + DateTime endTime; + /** + * duration is the scheduled duration which depends on + * e.g. estimated effort, allocated resources and risk + */ + Duration duration; + + /// Set if EffortType == Effort, but no resource is requested + bool resourceError; + /// Set if the assigned resource is overbooked + bool resourceOverbooked; + /// Set if the requested resource is not available + bool resourceNotAvailable; + /// Set if the task cannot be scheduled to fullfill all the constraints + bool schedulingError; + /// Set if the node has not been scheduled + bool notScheduled; + + DateTime workStartTime; + DateTime workEndTime; + bool inCriticalPath; + +#ifndef NDEBUG +public: + virtual void printDebug(QString ident); +#endif +}; + +/** + * NodeSchedule holds scheduling information for a node (task). + * + */ +class NodeSchedule : public Schedule +{ +public: + NodeSchedule(); + NodeSchedule(Node *node, QString name, Schedule::Type type, long id); + NodeSchedule(Schedule *parent, Node *node); + ~NodeSchedule(); + + virtual bool isDeleted() const + { return m_parent == 0 ? true : m_parent->isDeleted(); } + void setDeleted(bool on); + + virtual bool loadXML(const QDomElement &element); + virtual void saveXML(QDomElement &element) const; + +// tasks------------> + virtual void addAppointment(Schedule *resource, DateTime &start, DateTime &end, double load=100); + + virtual Node *node() const { return m_node; } + virtual void setNode(Node *n) { m_node = n; } + +protected: + void init(); + +private: + Node *m_node; + +#ifndef NDEBUG +public: + virtual void printDebug(QString ident); +#endif +}; + +/** + * ResourceSchedule holds scheduling information for a resource. + * + */ +class ResourceSchedule : public Schedule +{ +public: + ResourceSchedule(); + ResourceSchedule(Resource *Resource, QString name, Schedule::Type type, long id); + ResourceSchedule(Schedule *parent, Resource *Resource); + ~ResourceSchedule(); + + virtual bool isDeleted() const + { return m_parent == 0 ? true : m_parent->isDeleted(); } + virtual void addAppointment(Schedule *node, DateTime &start, DateTime &end, double load=100); + + virtual bool isOverbooked() const; + virtual bool isOverbooked(const DateTime &start, const DateTime &end) const; + Appointment appointmentIntervals() const; + + virtual Resource *resource() const { return m_resource; } + virtual double normalRatePrHour() const; + +private: + Resource *m_resource; + Schedule *m_parent; + +#ifndef NDEBUG +public: + virtual void printDebug(QString ident); +#endif +}; + +/** + * MainSchedule holds scheduling information for the main project node. + * + */ +class MainSchedule : public NodeSchedule +{ +public: + MainSchedule(); + MainSchedule(Node *node, QString name, Schedule::Type type, long id); + ~MainSchedule(); + virtual bool isDeleted() const { return m_deleted; } + + virtual bool loadXML(const QDomElement &element, Project &project); + virtual void saveXML(QDomElement &element) const; + +private: + +#ifndef NDEBUG +public: + virtual void printDebug(QString ident); +#endif +}; + + +} //namespace KPlato + +#endif diff --git a/kplato/kptstandardworktimedialog.cc b/kplato/kptstandardworktimedialog.cc new file mode 100644 index 00000000..936c3c38 --- /dev/null +++ b/kplato/kptstandardworktimedialog.cc @@ -0,0 +1,292 @@ +/* This file is part of the KDE project + Copyright (C) 2004 - 2006 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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. +*/ + +#include "kptstandardworktimedialog.h" +#include "kptduration.h" +#include "kptproject.h" +#include "kptcalendar.h" +#include "kptcommand.h" +#include "kptintervaledit.h" +#include "kptpart.h" + +#include <qgroupbox.h> +#include <qheader.h> +#include <qpushbutton.h> +#include <qspinbox.h> +#include <qcombobox.h> +#include <qlabel.h> +#include <qlayout.h> +#include <qlineedit.h> +#include <qdatetimeedit.h> +#include <qdatetime.h> + +#include <kdebug.h> +#include <kcombobox.h> +#include <kcalendarsystem.h> +#include <kglobal.h> +#include <klocale.h> +#include <knuminput.h> +#include <klistview.h> + +namespace KPlato +{ +class WeekdayListItem : public KListViewItem +{ +public: + WeekdayListItem(Calendar *cal, int wd, KListView *parent, QString name, KListViewItem *after) + : KListViewItem(parent, after), + original(cal->weekday(wd)), + calendar(cal), + weekday(wd) + { + setText(0, name); + day = new CalendarDay(original); + if (day->state() == Map::NonWorking) { + setHours(); + } else { + setText(1, KGlobal::locale()->formatNumber(day->duration().toDouble(Duration::Unit_h))); + } + } + ~WeekdayListItem() { + delete day; + } + void setHours() { + setText(1, "-"); + day->clearIntervals(); + } + void setIntervals(QPtrList<QPair<QTime, QTime> > intervals) { + day->setIntervals(intervals); + setText(1, KGlobal::locale()->formatNumber(day->duration().toDouble(Duration::Unit_h))); + } + void setState(int st) { + day->setState(st+1); + } + + KCommand *save(Part *part) { + KCommand *cmd=0; + if (*original != *day) { + cmd = new CalendarModifyWeekdayCmd(part, calendar, weekday, day); + day = 0; + } + return cmd; + } + CalendarDay *day; + CalendarDay *original; + Calendar *calendar; + int weekday; +}; + +StandardWorktimeDialog::StandardWorktimeDialog(Project &p, QWidget *parent, const char *name) + : KDialogBase( Swallow, i18n("Standard Worktime"), Ok|Cancel, Ok, parent, name, true, true), + project(p) +{ + //kdDebug()<<k_funcinfo<<&p<<endl; + m_original = p.standardWorktime(); + dia = new StandardWorktimeDialogImpl(m_original, this); + + setMainWidget(dia); + enableButtonOK(false); + + connect(dia, SIGNAL(obligatedFieldsFilled(bool) ), SLOT(enableButtonOK(bool))); + connect(dia, SIGNAL(enableButtonOk(bool)), SLOT(enableButtonOK(bool))); +} + +KMacroCommand *StandardWorktimeDialog::buildCommand(Part *part) { + //kdDebug()<<k_funcinfo<<endl; + QString n = i18n("Modify Standard Worktime"); + KMacroCommand *cmd = 0; + if (m_original->year() != dia->inYear()) { + if (cmd == 0) cmd = new KMacroCommand(n); + cmd->addCommand(new ModifyStandardWorktimeYearCmd(part, m_original, m_original->year(), dia->inYear())); + } + if (m_original->month() != dia->inMonth()) { + if (cmd == 0) cmd = new KMacroCommand(n); + cmd->addCommand(new ModifyStandardWorktimeMonthCmd(part, m_original, m_original->month(), dia->inMonth())); + } + if (m_original->week() != dia->inWeek()) { + if (cmd == 0) cmd = new KMacroCommand(n); + cmd->addCommand(new ModifyStandardWorktimeWeekCmd(part, m_original, m_original->week(), dia->inWeek())); + } + if (m_original->day() != dia->inDay()) { + if (cmd == 0) cmd = new KMacroCommand(n); + cmd->addCommand(new ModifyStandardWorktimeDayCmd(part, m_original, m_original->day(), dia->inDay())); + } + QListViewItem *item = dia->weekdayList->firstChild(); + for (; item; item = item->nextSibling()) { + KCommand *c = static_cast<WeekdayListItem*>(item)->save(part); + if (c) { + if (cmd == 0) cmd = new KMacroCommand(n); + cmd->addCommand(c); + } + } + return cmd; + +} + +void StandardWorktimeDialog::slotOk() { + accept(); +} + + +StandardWorktimeDialogImpl::StandardWorktimeDialogImpl(StandardWorktime *std, QWidget *parent) + : StandardWorktimeDialogBase(parent), + m_std(std) { + if (!std) { + m_std = new StandardWorktime(); + } + QBoxLayout *l = new QVBoxLayout(intervalBox); + m_intervalEdit = new IntervalEdit(intervalBox); + l->addWidget(m_intervalEdit); + + m_year = m_std->year(); + m_month = m_std->month(); + m_week = m_std->week(); + m_day = m_std->day(); + + year->setValue(m_year); + month->setValue(m_month); + week->setValue(m_week); + day->setValue(m_day); + + weekdayList->setSorting(-1); + weekdayList->header()->setStretchEnabled(true); + const KCalendarSystem * cs = KGlobal::locale()->calendar(); + Calendar *cal = m_std->calendar(); + if (cal) { + WeekdayListItem *item = 0; + for (int i = 0; i < 7; ++i) { + CalendarDay *day = cal->weekday(i); + if (day == 0) { + continue; + } + item = new WeekdayListItem(cal, i, weekdayList, cs->weekDayName(i+1), item); + weekdayList->insertItem(item); + } + } + connect(year, SIGNAL(valueChanged(double)), SLOT(slotYearChanged(double))); + connect(month, SIGNAL(valueChanged(double)), SLOT(slotMonthChanged(double))); + connect(week, SIGNAL(valueChanged(double)), SLOT(slotWeekChanged(double))); + connect(day, SIGNAL(valueChanged(double)), SLOT(slotDayChanged(double))); + + connect(m_intervalEdit, SIGNAL(changed()), SLOT(slotIntervalChanged())); + connect(bApply, SIGNAL(clicked()), SLOT(slotApplyClicked())); + connect(weekdayList, SIGNAL(selectionChanged()), SLOT(slotWeekdaySelected())); + connect(state, SIGNAL(activated(int)), SLOT(slotStateChanged(int))); + + if (weekdayList->firstChild()) { + weekdayList->setSelected(weekdayList->firstChild(), true); + weekdayList->setCurrentItem(weekdayList->firstChild()); + } +} + + +void StandardWorktimeDialogImpl::slotEnableButtonApply(bool on) { + bApply->setEnabled(on); +} + +void StandardWorktimeDialogImpl::slotEnableButtonOk(bool on) { + emit enableButtonOk(on); +} + +void StandardWorktimeDialogImpl::slotCheckAllFieldsFilled() { + emit obligatedFieldsFilled(true); +} + +void StandardWorktimeDialogImpl::slotYearChanged(double value) { + //kdDebug()<<k_funcinfo<<value<<endl; + m_year = value; + if (month->value() > value) + month->setValue(value); + slotEnableButtonOk(true); +} + +void StandardWorktimeDialogImpl::slotMonthChanged(double value) { + m_month = value; + if (year->value() < value) + year->setValue(value); + if (week->value() > value) + week->setValue(value); + slotEnableButtonOk(true); +} + +void StandardWorktimeDialogImpl::slotWeekChanged(double value) { + m_week = value; + if (month->value() < value) + month->setValue(value); + if (day->value() > value) + day->setValue(value); + slotEnableButtonOk(true); +} + +void StandardWorktimeDialogImpl::slotDayChanged(double value) { + m_day = value; + if (week->value() < value) + week->setValue(value); + slotEnableButtonOk(true); +} + +void StandardWorktimeDialogImpl::slotIntervalChanged() { + //kdDebug()<<k_funcinfo<<endl; + slotEnableButtonApply(true); +} + +void StandardWorktimeDialogImpl::slotApplyClicked() { + //kdDebug()<<k_funcinfo<<"state="<<state->currentItem()<<endl; + QListViewItem *item = weekdayList->firstChild(); + for (; item; item = item->nextSibling()) { + if (item->isSelected()) { + //kdDebug()<<k_funcinfo<<item->text(0)<<" selected"<<endl; + WeekdayListItem *wd = static_cast<WeekdayListItem*>(item); + wd->setState(state->currentItem()); + if (state->currentItem() == 0) { + wd->setHours(); + } else { + wd->setIntervals(m_intervalEdit->intervals()); + } + slotEnableButtonOk(true); + } + } +} + +void StandardWorktimeDialogImpl::slotWeekdaySelected() { + //kdDebug()<<k_funcinfo<<"state="<<state->currentItem()<<endl; + + QListViewItem *item = weekdayList->firstChild(); + for (; item; item = item->nextSibling()) { + if (item->isSelected()) { + //kdDebug()<<k_funcinfo<<item->text(0)<<" selected"<<endl; + WeekdayListItem *wd = static_cast<WeekdayListItem*>(item); + state->setCurrentItem(wd->day->state()-1); + m_intervalEdit->setIntervals(wd->day->workingIntervals()); + slotStateChanged(state->currentItem()); + break; + } + } + editBox->setEnabled(item != 0); +} + +void StandardWorktimeDialogImpl::slotStateChanged(int st) { + //kdDebug()<<k_funcinfo<<"state="<<state->currentItem()<<endl; + intervalBox->setEnabled(st == 1); //Working + slotEnableButtonApply(st == 0); +} + +} //KPlato namespace + +#include "kptstandardworktimedialog.moc" diff --git a/kplato/kptstandardworktimedialog.h b/kplato/kptstandardworktimedialog.h new file mode 100644 index 00000000..34424f00 --- /dev/null +++ b/kplato/kptstandardworktimedialog.h @@ -0,0 +1,94 @@ +/* This file is part of the KDE project + Copyright (C) 2004 - 2006 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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 KPTSTANDARDWORKTIMEDIALOG_H +#define KPTSTANDARDWORKTIMEDIALOG_H + +#include "standardworktimedialogbase.h" +#include "kptcalendar.h" + +#include <kdialogbase.h> + +#include <qstring.h> + +class KMacroCommand; + +namespace KPlato +{ + +class Project; +class Part; +class IntervalEditImpl; + +class StandardWorktimeDialogImpl : public StandardWorktimeDialogBase { + Q_OBJECT +public: + StandardWorktimeDialogImpl ( StandardWorktime *std, QWidget *parent); + + StandardWorktime *standardWorktime() { return m_std; } + double inYear() const { return m_year; } + double inMonth() const { return m_month; } + double inWeek() const { return m_week; } + double inDay() const { return m_day; } + +private slots: + void slotCheckAllFieldsFilled(); + void slotEnableButtonOk(bool on); + + void slotYearChanged(double); + void slotMonthChanged(double); + void slotWeekChanged(double); + void slotDayChanged(double); + void slotIntervalChanged(); + void slotApplyClicked(); + void slotEnableButtonApply(bool); + void slotWeekdaySelected(); + void slotStateChanged(int); +signals: + void obligatedFieldsFilled(bool yes); + void enableButtonOk(bool); + +private: + StandardWorktime *m_std; + double m_year; + double m_month; + double m_week; + double m_day; + IntervalEditImpl *m_intervalEdit; +}; + +class StandardWorktimeDialog : public KDialogBase { + Q_OBJECT +public: + StandardWorktimeDialog(Project &project, QWidget *parent=0, const char *name=0); + + KMacroCommand *buildCommand(Part *part); + +protected slots: + void slotOk(); + +private: + Project &project; + StandardWorktimeDialogImpl *dia; + StandardWorktime *m_original; +}; + +} //KPlato namespace + +#endif // STANDARDWORKTIMEDIALOG_H diff --git a/kplato/kptsummarytaskdialog.cc b/kplato/kptsummarytaskdialog.cc new file mode 100644 index 00000000..25ec7227 --- /dev/null +++ b/kplato/kptsummarytaskdialog.cc @@ -0,0 +1,69 @@ +/* This file is part of the KDE project + Copyright (C) 2002 Bo Thorsen bo@sonofthor.dk + Copyright (C) 2004, 2006 Dag Andersen <danders@get2net.dk> + + 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. +*/ + +#include "kptsummarytaskdialog.h" +#include "kptsummarytaskgeneralpanel.h" + +#include <klocale.h> +#include <kcommand.h> + +#include <qvbox.h> +#include <kdebug.h> + +namespace KPlato +{ + +SummaryTaskDialog::SummaryTaskDialog(Task &task, QWidget *p) + : KDialogBase(Swallow, i18n("Summary Task Settings"), Ok|Cancel, Ok, p, "Summary Task Settings Dialog", true, true) +{ + m_generalTab = new SummaryTaskGeneralPanel(task, this); + setMainWidget(m_generalTab); + enableButtonOK(false); + + connect(m_generalTab, SIGNAL(obligatedFieldsFilled(bool)), SLOT(enableButtonOK(bool))); +} + + +KCommand *SummaryTaskDialog::buildCommand(Part *part) { + KMacroCommand *m = new KMacroCommand(i18n("Modify Summary Task")); + bool modified = false; + KCommand *cmd = m_generalTab->buildCommand(part); + if (cmd) { + m->addCommand(cmd); + modified = true; + } + if (!modified) { + delete m; + return 0; + } + return m; +} + +void SummaryTaskDialog::slotOk() { + if (!m_generalTab->ok()) + return; + + accept(); +} + + +} //KPlato namespace + +#include "kptsummarytaskdialog.moc" diff --git a/kplato/kptsummarytaskdialog.h b/kplato/kptsummarytaskdialog.h new file mode 100644 index 00000000..e307fea9 --- /dev/null +++ b/kplato/kptsummarytaskdialog.h @@ -0,0 +1,72 @@ +/* This file is part of the KDE project + Copyright (C) 2002 Bo Thorsen bo@sonofthor.dk + Copyright (C) 2004, 2006 Dag Andersen <danders@get2net.dk> + + 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 KPTSUMMARYSUMMARYTASKDIALOG_H +#define KPTSUMMARYSUMMARYTASKDIALOG_H + +#include <kdialogbase.h> + +class Duration; + +class KLineEdit; +class KCommand; +class KTextEdit; +class KComboBox; +class KDoubleNumInput; + +class QDateTimeEdit; +class QSpinBox; +class QButtonGroup; +class QListBox; +class QTable; +class QDateTime; + +namespace KPlato +{ + +class SummaryTaskGeneralPanel; +class Part; +class Task; + +/** + * The dialog that shows and allows you to alter summary tasks. + */ +class SummaryTaskDialog : public KDialogBase { + Q_OBJECT +public: + /** + * The constructor for the summary task settings dialog. + * @param task the task to edit + * @param parent parent widget + */ + SummaryTaskDialog(Task &task, QWidget *parent=0); + + KCommand *buildCommand(Part *part); + +protected slots: + void slotOk(); + +private: + SummaryTaskGeneralPanel *m_generalTab; +}; + +} //KPlato namespace + +#endif // SUMMARYTASKDIALOG_H diff --git a/kplato/kptsummarytaskgeneralpanel.cc b/kplato/kptsummarytaskgeneralpanel.cc new file mode 100644 index 00000000..3b7f80a9 --- /dev/null +++ b/kplato/kptsummarytaskgeneralpanel.cc @@ -0,0 +1,126 @@ +/* This file is part of the KDE project + Copyright (C) 2004 - 2006 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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. +*/ + +#include "kptsummarytaskgeneralpanel.h" +#include "kptsummarytaskdialog.h" +#include "kpttask.h" +#include "kptcommand.h" +#include "kptconfig.h" +#include "kptpart.h" + +#include <kmessagebox.h> +#include <klineedit.h> +#include <ktextedit.h> +#include <kcombobox.h> +#include <kdatetimewidget.h> +#include <klocale.h> +#include <kcommand.h> +#include <kabc/addressee.h> +#include <kabc/addresseedialog.h> + +#include <qpushbutton.h> +#include <qlayout.h> +#include <qlabel.h> +#include <qdatetime.h> +#include <qdatetimeedit.h> +#include <qgroupbox.h> +#include <kdebug.h> + +namespace KPlato +{ + +SummaryTaskGeneralPanel::SummaryTaskGeneralPanel(Task &task, QWidget *p, const char *n) + : SummaryTaskGeneralPanelBase(p, n), + m_task(task) +{ + setStartValues(task); + + connect(namefield, SIGNAL(textChanged(const QString&)), SLOT(slotObligatedFieldsFilled())); + connect(leaderfield, SIGNAL(textChanged(const QString&)), SLOT(slotObligatedFieldsFilled())); + connect(idfield, SIGNAL(textChanged(const QString&)), SLOT(slotObligatedFieldsFilled())); + connect(descriptionfield, SIGNAL(textChanged()), SLOT(slotObligatedFieldsFilled())); + + connect(chooseLeader, SIGNAL(clicked()), SLOT(slotChooseResponsible())); + +} + +void SummaryTaskGeneralPanel::setStartValues(Task &task) { + namefield->setText(task.name()); + leaderfield->setText(task.leader()); + descriptionfield->setText(task.description()); + idfield->setText(task.id()); + wbsfield->setText(task.wbs()); + + namefield->setFocus(); + +} + +void SummaryTaskGeneralPanel::slotObligatedFieldsFilled() { + emit obligatedFieldsFilled(!namefield->text().isEmpty() && !idfield->text().isEmpty()); +} + +KMacroCommand *SummaryTaskGeneralPanel::buildCommand(Part *part) { + KMacroCommand *cmd = new KMacroCommand(i18n("Modify Task")); + bool modified = false; + + if (!namefield->isHidden() && m_task.name() != namefield->text()) { + cmd->addCommand(new NodeModifyNameCmd(part, m_task, namefield->text())); + modified = true; + } + if (!leaderfield->isHidden() && m_task.leader() != leaderfield->text()) { + cmd->addCommand(new NodeModifyLeaderCmd(part, m_task, leaderfield->text())); + modified = true; + } + if (!descriptionfield->isHidden() && + m_task.description() != descriptionfield->text()) { + cmd->addCommand(new NodeModifyDescriptionCmd(part, m_task, descriptionfield->text())); + modified = true; + } + if (!idfield->isHidden() && idfield->text() != m_task.id()) { + cmd->addCommand(new NodeModifyIdCmd(part, m_task, idfield->text())); + modified = true; + } + if (!modified) { + delete cmd; + return 0; + } + return cmd; +} + +bool SummaryTaskGeneralPanel::ok() { + if (idfield->text() != m_task.id() && m_task.findNode(idfield->text())) { + KMessageBox::sorry(this, i18n("Task id must be unique")); + idfield->setFocus(); + return false; + } + return true; +} + +void SummaryTaskGeneralPanel::slotChooseResponsible() { + KABC::Addressee a = KABC::AddresseeDialog::getAddressee(this); + if (!a.isEmpty()) { + leaderfield->setText(a.fullEmail()); + leaderfield->setFocus(); + } +} + + +} //KPlato namespace + +#include "kptsummarytaskgeneralpanel.moc" diff --git a/kplato/kptsummarytaskgeneralpanel.h b/kplato/kptsummarytaskgeneralpanel.h new file mode 100644 index 00000000..0bcf02fa --- /dev/null +++ b/kplato/kptsummarytaskgeneralpanel.h @@ -0,0 +1,59 @@ +/* This file is part of the KDE project + Copyright (C) 2004 - 2006 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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 KPTSUMMARYTASKGENERALPANEL_H +#define KPTSUMMARYTASKGENERALPANEL_H + +#include "kptsummarytaskgeneralpanelbase.h" + +class KMacroCommand; + +namespace KPlato +{ + +class SummaryTaskGeneralPanel; +class Part; +class Task; + +class SummaryTaskGeneralPanel : public SummaryTaskGeneralPanelBase { + Q_OBJECT +public: + SummaryTaskGeneralPanel(Task &task, QWidget *parent=0, const char *name=0); + + KMacroCommand *buildCommand(Part *part); + + bool ok(); + + void setStartValues(Task &task); + +signals: + void obligatedFieldsFilled(bool); + +public slots: + void slotObligatedFieldsFilled(); + void slotChooseResponsible(); + +private: + Task &m_task; + +}; + +} //KPlato namespace + +#endif // SUMMARYTASKGENERALPANEL_H diff --git a/kplato/kptsummarytaskgeneralpanelbase.ui b/kplato/kptsummarytaskgeneralpanelbase.ui new file mode 100644 index 00000000..4405773f --- /dev/null +++ b/kplato/kptsummarytaskgeneralpanelbase.ui @@ -0,0 +1,248 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>KPlato::SummaryTaskGeneralPanelBase</class> +<author>Dag Andersen <danders@get2net.dk></author> +<widget class="QWidget"> + <property name="name"> + <cstring>SummaryTaskGeneralPanelBase</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>250</height> + </rect> + </property> + <property name="minimumSize"> + <size> + <width>400</width> + <height>0</height> + </size> + </property> + <property name="caption"> + <string>SummaryTaskGeneralPanelBase</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout11</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout9</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>wbslabel</cstring> + </property> + <property name="text"> + <string>WBS:</string> + </property> + <property name="toolTip" stdset="0"> + <string>Work Breakdown Structure</string> + </property> + <property name="whatsThis" stdset="0"> + <string>The Work Breakdown Structure introduces numbering for all tasks in the project, according to the task structure. + +The WBS code is auto-generated; simply choose Generate WBS Code from the Tools menu to generate the WBS code for the project.</string> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>namelabel</cstring> + </property> + <property name="text"> + <string>Name:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>namefield</cstring> + </property> + <property name="whatsThis" stdset="0"> + <string>The name of the Task.</string> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>leaderlabel</cstring> + </property> + <property name="text"> + <string>Responsible:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>leaderfield</cstring> + </property> + <property name="whatsThis" stdset="0"> + <string>The person responsible for this task. + +This is not limited to persons available in a resource group but can be anyone. You can even directly access your address book with the Choose button.</string> + </property> + </widget> + </vbox> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout10</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout7</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>wbsfield</cstring> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer1</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QLabel"> + <property name="name"> + <cstring>idlabel</cstring> + </property> + <property name="text"> + <string>Task id:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>idfield</cstring> + </property> + <property name="whatsThis" stdset="0"> + <string>This is the unique identifier for this task.</string> + </property> + </widget> + <widget class="KLineEdit"> + <property name="name"> + <cstring>idfield</cstring> + </property> + <property name="focusPolicy"> + <enum>WheelFocus</enum> + </property> + </widget> + </hbox> + </widget> + <widget class="KLineEdit"> + <property name="name"> + <cstring>namefield</cstring> + </property> + <property name="focusPolicy"> + <enum>WheelFocus</enum> + </property> + <property name="whatsThis" stdset="0"> + <string>The name of the Task.</string> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout6</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KLineEdit"> + <property name="name"> + <cstring>leaderfield</cstring> + </property> + <property name="focusPolicy"> + <enum>WheelFocus</enum> + </property> + <property name="whatsThis" stdset="0"> + <string>The person responsible for this task. + +This is not limited to persons available in a resource group but can be anyone. You can even directly access your address book with the Choose button.</string> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>chooseLeader</cstring> + </property> + <property name="text"> + <string>Choose...</string> + </property> + <property name="accel"> + <string></string> + </property> + <property name="toolTip" stdset="0"> + <string>Insert a person from your address book.</string> + </property> + </widget> + </hbox> + </widget> + </vbox> + </widget> + </hbox> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>descriptionlabell6</cstring> + </property> + <property name="text"> + <string>Note:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>descriptionfield</cstring> + </property> + </widget> + <widget class="KTextEdit"> + <property name="name"> + <cstring>descriptionfield</cstring> + </property> + </widget> + </vbox> +</widget> +<customwidgets> +</customwidgets> +<connections> +</connections> +<tabstops> + <tabstop>namefield</tabstop> + <tabstop>leaderfield</tabstop> + <tabstop>chooseLeader</tabstop> + <tabstop>descriptionfield</tabstop> + <tabstop>idfield</tabstop> + <tabstop>wbsfield</tabstop> +</tabstops> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>klineedit.h</includehint> + <includehint>ktextedit.h</includehint> +</includehints> +</UI> diff --git a/kplato/kpttask.cc b/kplato/kpttask.cc new file mode 100644 index 00000000..255bf948 --- /dev/null +++ b/kplato/kpttask.cc @@ -0,0 +1,1542 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Thomas zander <zander@kde.org> + Copyright (C) 2004 Dag Andersen <danders@get2net.dk> + + 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. +*/ + +#include "kpttask.h" +#include "kptproject.h" +#include "kpttaskdialog.h" +#include "kptduration.h" +#include "kptrelation.h" +#include "kptdatetime.h" +#include "kptcalendar.h" +#include "kpteffortcostmap.h" +#include "kptschedule.h" + +#include <qdom.h> +#include <qbrush.h> +#include <kdebug.h> + +namespace KPlato +{ + +Task::Task(Node *parent) : Node(parent), m_resource() { + //kdDebug()<<k_funcinfo<<"("<<this<<")"<<endl; + m_resource.setAutoDelete(true); + Duration d(1, 0, 0); + m_effort = new Effort(d); + m_effort->setOptimisticRatio(-10); + m_effort->setPessimisticRatio(20); + m_requests = 0; + + if (m_parent) + m_leader = m_parent->leader(); + + m_schedules.setAutoDelete(true); + m_parentProxyRelations.setAutoDelete(true); + m_childProxyRelations.setAutoDelete(true); +} + +Task::Task(Task &task, Node *parent) + : Node(task, parent), + m_resource() { + //kdDebug()<<k_funcinfo<<"("<<this<<")"<<endl; + m_resource.setAutoDelete(true); + + m_parentProxyRelations.setAutoDelete(true); + m_childProxyRelations.setAutoDelete(true); + m_requests = 0; + + m_effort = task.effort() ? new Effort(*(task.effort())) + : new Effort(); // Avoid crash, (shouldn't be zero) +} + + +Task::~Task() { + delete m_effort; +} + +int Task::type() const { + if ( numChildren() > 0) { + return Node::Type_Summarytask; + } + else if ( 0 == effort()->expected().seconds() ) { + return Node::Type_Milestone; + } + else { + return Node::Type_Task; + } +} + + + +Duration *Task::getExpectedDuration() { + //kdDebug()<<k_funcinfo<<endl; + // Duration should already be calculated + return m_currentSchedule ? new Duration(m_currentSchedule->duration) : new Duration(); +} + +Duration *Task::getRandomDuration() { + return 0L; +} + +ResourceGroupRequest *Task::resourceGroupRequest(ResourceGroup *group) const { + if (m_requests) + return m_requests->find(group); + return 0; +} + +void Task::clearResourceRequests() { + if (m_requests) + m_requests->clear(); +} + +void Task::addRequest(ResourceGroup *group, int numResources) { + addRequest(new ResourceGroupRequest(group, numResources)); +} + +void Task::addRequest(ResourceGroupRequest *request) { + if (!m_requests) + m_requests = new ResourceRequestCollection(*this); + m_requests->addRequest(request); +} + +void Task::takeRequest(ResourceGroupRequest *request) { + if (m_requests) { + m_requests->takeRequest(request); + if (m_requests->isEmpty()) { + delete m_requests; + m_requests = 0; + } + } +} + +int Task::units() const { + if (!m_requests) + return 0; + return m_requests->units(); +} + +int Task::workUnits() const { + if (!m_requests) + return 0; + return m_requests->workUnits(); +} + +void Task::makeAppointments() { + if (m_currentSchedule == 0) + return; + if (type() == Node::Type_Task) { + if (m_requests) { + //kdDebug()<<k_funcinfo<<m_name<<": "<<m_currentSchedule->startTime<<", "<<m_currentSchedule->endTime<<"; "<<m_currentSchedule->duration.toString()<<endl; + m_requests->makeAppointments(m_currentSchedule); + //kdDebug()<<k_funcinfo<<m_name<<": "<<m_currentSchedule->startTime<<", "<<m_currentSchedule->endTime<<"; "<<m_currentSchedule->duration.toString()<<endl; + } + } else if (type() == Node::Type_Summarytask) { + QPtrListIterator<Node> nit(m_nodes); + for ( ; nit.current(); ++nit ) { + nit.current()->makeAppointments(); + } + } else if (type() == Node::Type_Milestone) { + //kdDebug()<<k_funcinfo<<"Milestone not implemented"<<endl; + // Well, shouldn't have resources anyway... + } +} + +void Task::calcResourceOverbooked() { + if (m_currentSchedule) + m_currentSchedule->calcResourceOverbooked(); +} + +// A new constraint means start/end times and duration must be recalculated +void Task::setConstraint(Node::ConstraintType type) { + m_constraint = type; +} + + +bool Task::load(QDomElement &element, Project &project) { + // Load attributes (TODO: Handle different types of tasks, milestone, summary...) + QString s; + bool ok = false; + m_id = element.attribute("id"); + + m_name = element.attribute("name"); + m_leader = element.attribute("leader"); + m_description = element.attribute("description"); + //kdDebug()<<k_funcinfo<<m_name<<": id="<<m_id<<endl; + + // Allow for both numeric and text + QString constraint = element.attribute("scheduling","0"); + m_constraint = (Node::ConstraintType)constraint.toInt(&ok); + if (!ok) + Node::setConstraint(constraint); // hmmm, why do I need Node::? + + s = element.attribute("constraint-starttime"); + if (s != "") + m_constraintStartTime = DateTime::fromString(s); + s = element.attribute("constraint-endtime"); + if ( s != "") + m_constraintEndTime = DateTime::fromString(s); + + m_startupCost = element.attribute("startup-cost", "0.0").toDouble(); + m_shutdownCost = element.attribute("shutdown-cost", "0.0").toDouble(); + + m_wbs = element.attribute("wbs", ""); + + // Load the project children + QDomNodeList list = element.childNodes(); + for (unsigned int i=0; i<list.count(); ++i) { + if (list.item(i).isElement()) { + QDomElement e = list.item(i).toElement(); + + if (e.tagName() == "project") { + // Load the subproject + Project *child = new Project(this); + if (child->load(e)) { + if (!project.addSubTask(child, this)) { + delete child; // TODO: Complain about this + } + } else { + // TODO: Complain about this + delete child; + } + } else if (e.tagName() == "task") { + // Load the task + Task *child = new Task(this); + if (child->load(e, project)) { + if (!project.addSubTask(child, this)) { + delete child; // TODO: Complain about this + } + } else { + // TODO: Complain about this + delete child; + } + } else if (e.tagName() == "resource") { + // TODO: Load the resource (projects don't have resources yet) + } else if (e.tagName() == "effort") { + // Load the effort + m_effort->load(e); + } else if (e.tagName() == "resourcegroup-request") { + // Load the resource request + ResourceGroupRequest *r = new ResourceGroupRequest(); + if (r->load(e, project)) { + addRequest(r); + } else { + kdError()<<k_funcinfo<<"Failed to load resource request"<<endl; + delete r; + } + } else if (e.tagName() == "progress") { + m_progress.started = (bool)e.attribute("started", "0").toInt(); + m_progress.finished = (bool)e.attribute("finished", "0").toInt(); + + s = e.attribute("startTime"); + if (s != "") + m_progress.startTime = DateTime::fromString(s); + s = e.attribute("finishTime"); + if (s != "") + m_progress.finishTime = DateTime::fromString(s); + m_progress.percentFinished = e.attribute("percent-finished", "0").toInt(); + m_progress.remainingEffort = Duration::fromString(e.attribute("remaining-effort")); + m_progress.totalPerformed = Duration::fromString(e.attribute("performed-effort")); + } else if (e.tagName() == "schedules") { + QDomNodeList lst = e.childNodes(); + for (unsigned int i=0; i<lst.count(); ++i) { + if (lst.item(i).isElement()) { + QDomElement el = lst.item(i).toElement(); + if (el.tagName() == "schedule") { + NodeSchedule *sch = new NodeSchedule(); + if (sch->loadXML(el)) { + sch->setNode(this); + addSchedule(sch); + } else { + kdError()<<k_funcinfo<<"Failed to load schedule"<<endl; + delete sch; + } + } + } + } + } + } + } + //kdDebug()<<k_funcinfo<<m_name<<" loaded"<<endl; + return true; +} + + +void Task::save(QDomElement &element) const { + QDomElement me = element.ownerDocument().createElement("task"); + element.appendChild(me); + + //TODO: Handle different types of tasks, milestone, summary... + me.setAttribute("id", m_id); + me.setAttribute("name", m_name); + me.setAttribute("leader", m_leader); + me.setAttribute("description", m_description); + + me.setAttribute("scheduling",constraintToString()); + me.setAttribute("constraint-starttime",m_constraintStartTime.toString(Qt::ISODate)); + me.setAttribute("constraint-endtime",m_constraintEndTime.toString(Qt::ISODate)); + + me.setAttribute("startup-cost", m_startupCost); + me.setAttribute("shutdown-cost", m_shutdownCost); + + me.setAttribute("wbs", m_wbs); + + m_effort->save(me); + + QDomElement el = me.ownerDocument().createElement("progress"); + me.appendChild(el); + el.setAttribute("started", m_progress.started); + el.setAttribute("finished", m_progress.finished); + el.setAttribute("startTime", m_progress.startTime.toString(Qt::ISODate)); + el.setAttribute("finishTime", m_progress.finishTime.toString(Qt::ISODate)); + el.setAttribute("percent-finished", m_progress.percentFinished); + el.setAttribute("remaining-effort", m_progress.remainingEffort.toString()); + el.setAttribute("performed-effort", m_progress.totalPerformed.toString()); + + if (!m_schedules.isEmpty()) { + QDomElement schs = me.ownerDocument().createElement("schedules"); + me.appendChild(schs); + QIntDictIterator<Schedule> it = m_schedules; + for (; it.current(); ++it) { + if (!it.current()->isDeleted()) { + it.current()->saveXML(schs); + } + } + } + if (m_requests) { + m_requests->save(me); + } + for (int i=0; i<numChildren(); i++) { + getChildNode(i)->save(me); + } +} + +void Task::saveAppointments(QDomElement &element, long id) const { + //kdDebug()<<k_funcinfo<<m_name<<" id="<<id<<endl; + Schedule *sch = findSchedule(id); + if (sch) { + sch->saveAppointments(element); + } + QPtrListIterator<Node> it(m_nodes); + for (; it.current(); ++it ) { + it.current()->saveAppointments(element, id); + } +} + +EffortCostMap Task::plannedEffortCostPrDay(const QDate &start, const QDate &end) const { + //kdDebug()<<k_funcinfo<<m_name<<endl; + if (m_currentSchedule) { + return m_currentSchedule->plannedEffortCostPrDay(start, end); + } + return EffortCostMap(); +} + +// Returns the total planned effort for this task (or subtasks) +Duration Task::plannedEffort() { + //kdDebug()<<k_funcinfo<<endl; + Duration eff; + if (type() == Node::Type_Summarytask) { + QPtrListIterator<Node> it(childNodeIterator()); + for (; it.current(); ++it) { + eff += it.current()->plannedEffort(); + } + } else if (m_currentSchedule) { + eff = m_currentSchedule->plannedEffort(); + } + return eff; +} + +// Returns the total planned effort for this task (or subtasks) on date +Duration Task::plannedEffort(const QDate &date) { + //kdDebug()<<k_funcinfo<<endl; + Duration eff; + if (type() == Node::Type_Summarytask) { + QPtrListIterator<Node> it(childNodeIterator()); + for (; it.current(); ++it) { + eff += it.current()->plannedEffort(date); + } + } else if (m_currentSchedule) { + eff = m_currentSchedule->plannedEffort(date); + } + return eff; +} + +// Returns the total planned effort for this task (or subtasks) upto and including date +Duration Task::plannedEffortTo(const QDate &date) { + //kdDebug()<<k_funcinfo<<endl; + Duration eff; + if (type() == Node::Type_Summarytask) { + QPtrListIterator<Node> it(childNodeIterator()); + for (; it.current(); ++it) { + eff += it.current()->plannedEffortTo(date); + } + } else if (m_currentSchedule) { + eff = m_currentSchedule->plannedEffortTo(date); + } + return eff; +} + +// Returns the total planned effort for this task (or subtasks) +Duration Task::actualEffort() { + //kdDebug()<<k_funcinfo<<endl; + Duration eff; + if (type() == Node::Type_Summarytask) { + QPtrListIterator<Node> it(childNodeIterator()); + for (; it.current(); ++it) { + eff += it.current()->actualEffort(); + } + } else { + eff = m_progress.totalPerformed; + } + /* If we want to register pr resource... + } else if (m_currentSchedule) { + eff = m_currentSchedule->actualEffort(); + }*/ + return eff; +} + +// Returns the total planned effort for this task (or subtasks) on date +Duration Task::actualEffort(const QDate &date) { + //kdDebug()<<k_funcinfo<<endl; + Duration eff; + if (type() == Node::Type_Summarytask) { + QPtrListIterator<Node> it(childNodeIterator()); + for (; it.current(); ++it) { + eff += it.current()->actualEffort(date); + } + } else if (m_currentSchedule) { + eff = m_currentSchedule->actualEffort(date); + } + return eff; +} + +// Returns the total planned effort for this task (or subtasks) on date +Duration Task::actualEffortTo(const QDate &date) { + //kdDebug()<<k_funcinfo<<endl; + Duration eff; + if (type() == Node::Type_Summarytask) { + QPtrListIterator<Node> it(childNodeIterator()); + for (; it.current(); ++it) { + eff += it.current()->actualEffortTo(date); + } + } else if (m_currentSchedule) { + eff = m_currentSchedule->actualEffortTo(date); + } + return eff; +} + +double Task::plannedCost() { + //kdDebug()<<k_funcinfo<<endl; + double c = 0; + if (type() == Node::Type_Summarytask) { + QPtrListIterator<Node> it(childNodeIterator()); + for (; it.current(); ++it) { + c += it.current()->plannedCost(); + } + } else if (m_currentSchedule) { + c = m_currentSchedule->plannedCost(); + } + return c; +} + +double Task::plannedCost(const QDate &date) { + //kdDebug()<<k_funcinfo<<endl; + double c = 0; + if (type() == Node::Type_Summarytask) { + QPtrListIterator<Node> it(childNodeIterator()); + for (; it.current(); ++it) { + c += it.current()->plannedCost(date); + } + } else if (m_currentSchedule) { + c = m_currentSchedule->plannedCost(date); + } + return c; +} + +double Task::plannedCostTo(const QDate &date) { + //kdDebug()<<k_funcinfo<<endl; + double c = 0; + if (type() == Node::Type_Summarytask) { + QPtrListIterator<Node> it(childNodeIterator()); + for (; it.current(); ++it) { + c += it.current()->plannedCostTo(date); + } + } else if (m_currentSchedule) { + c = m_currentSchedule->plannedCostTo(date); + } + return c; +} + +double Task::actualCost() { + //kdDebug()<<k_funcinfo<<endl; + double c = 0; + if (type() == Node::Type_Summarytask) { + QPtrListIterator<Node> it(childNodeIterator()); + for (; it.current(); ++it) { + c += it.current()->actualCost(); + } + } else if (m_currentSchedule) { + c = m_currentSchedule->actualCost(); + } + return c; +} + +double Task::actualCost(const QDate &date) { + //kdDebug()<<k_funcinfo<<endl; + double c = 0; + if (type() == Node::Type_Summarytask) { + QPtrListIterator<Node> it(childNodeIterator()); + for (; it.current(); ++it) { + c += it.current()->actualCost(date); + } + } else if (m_currentSchedule) { + c = m_currentSchedule->actualCost(date); + } + return c; +} + +double Task::actualCostTo(const QDate &date) { + //kdDebug()<<k_funcinfo<<endl; + double c = 0; + if (type() == Node::Type_Summarytask) { + QPtrListIterator<Node> it(childNodeIterator()); + for (; it.current(); ++it) { + c += it.current()->actualCostTo(date); + } + } else if (m_currentSchedule) { + c = m_currentSchedule->actualCostTo(date); + } + return c; +} + +//FIXME Handle summarytasks +double Task::effortPerformanceIndex(const QDate &date, bool *error) { + double res = 0.0; + Duration ae = actualEffortTo(date); + + bool e = (ae == Duration::zeroDuration || m_progress.percentFinished == 0); + if (error) { + *error = e; + } + if (!e) { + res = (plannedEffortTo(date).toDouble() * ((double)m_progress.percentFinished/100.0) / ae.toDouble()); + } + return res; +} + +//FIXME Handle summarytasks +double Task::costPerformanceIndex(const QDate &date, bool *error) { + double res = 0.0; + Duration ac = Q_INT64(actualCostTo(date)); + + bool e = (ac == Duration::zeroDuration || m_progress.percentFinished == 0); + if (error) { + *error = e; + } + if (!e) { + res = (plannedCostTo(date) * m_progress.percentFinished)/(100 * actualCostTo(date)); + } + return res; +} + +void Task::initiateCalculation(Schedule &sch) { + //kdDebug()<<k_funcinfo<<m_name<<" schedule: "<<(sch?sch->name():"None")<<" id="<<(sch?sch->id():-1)<<endl; + m_visitedForward = false; + m_visitedBackward = false; + m_currentSchedule = createSchedule(&sch); + m_currentSchedule->initiateCalculation(); + clearProxyRelations(); + Node::initiateCalculation(sch); +} + + +void Task::initiateCalculationLists(QPtrList<Node> &startnodes, QPtrList<Node> &endnodes, QPtrList<Node> &summarytasks/*, QPtrList<Node> &milestones*/) { + //kdDebug()<<k_funcinfo<<m_name<<endl; + if (type() == Node::Type_Summarytask) { + summarytasks.append(this); + // propagate my relations to my children and dependent nodes + + QPtrListIterator<Node> nodes = m_nodes; + for (; nodes.current(); ++nodes) { + if (!dependParentNodes().isEmpty()) + nodes.current()->addParentProxyRelations(dependParentNodes()); + if (!dependChildNodes().isEmpty()) + nodes.current()->addChildProxyRelations(dependChildNodes()); + nodes.current()->initiateCalculationLists(startnodes, endnodes, summarytasks); + } + } else { + if (isEndNode()) { + endnodes.append(this); + //kdDebug()<<k_funcinfo<<"endnodes append: "<<m_name<<endl; + } + if (isStartNode()) { + startnodes.append(this); + //kdDebug()<<k_funcinfo<<"startnodes append: "<<m_name<<endl; + } + } +} + +DateTime Task::calculatePredeccessors(const QPtrList<Relation> &list, int use) { + DateTime time; + QPtrListIterator<Relation> it = list; + for (; it.current(); ++it) { + if (it.current()->parent()->type() == Type_Summarytask) { + //kdDebug()<<k_funcinfo<<"Skip summarytask: "<<it.current()->parent()->name()<<endl; + continue; // skip summarytasks + } + DateTime t = it.current()->parent()->calculateForward(use); // early finish + switch (it.current()->type()) { + case Relation::StartStart: + // I can't start earlier than my predesseccor + t = it.current()->parent()->getEarliestStart() + it.current()->lag(); + break; + case Relation::FinishFinish: + // I can't finish earlier than my predeccessor, so + // I can't start earlier than it's (earlyfinish+lag)- my duration + t += it.current()->lag(); + t -= duration(t, use, true); + break; + default: + t += it.current()->lag(); + break; + } + if (!time.isValid() || t > time) + time = t; + } + //kdDebug()<<time.toString()<<" "<<m_name<<" calculatePredeccessors() ("<<list.count()<<")"<<endl; + return time; +} +DateTime Task::calculateForward(int use) { + //kdDebug()<<k_funcinfo<<m_name<<<<endl; + if (m_currentSchedule == 0) { + return DateTime(); + } + Schedule *cs = m_currentSchedule; + if (m_visitedForward) { + //kdDebug()<<earliestStart.toString()<<" + "<<m_durationBackward.toString()<<" "<<m_name<<" calculateForward() (visited)"<<endl; + return cs->earliestStart + m_durationForward; + } + // First, calculate all predecessors + if (!dependParentNodes().isEmpty()) { + DateTime time = calculatePredeccessors(dependParentNodes(), use); + if (time.isValid() && time > cs->earliestStart) { + cs->earliestStart = time; + } + } + if (!m_parentProxyRelations.isEmpty()) { + DateTime time = calculatePredeccessors(m_parentProxyRelations, use); + if (time.isValid() && time > cs->earliestStart) { + cs->earliestStart = time; + } + } + if (type() == Node::Type_Task) { + m_durationForward = m_effort->effort(use); + switch (constraint()) { + case Node::ASAP: + case Node::ALAP: + cs->earliestStart = workStartAfter(cs->earliestStart); + m_durationForward = duration(cs->earliestStart, use, false); + //kdDebug()<<k_funcinfo<<m_name<<": "<<cs->earliestStart<<"+"<<m_durationForward.toString()<<"="<<(cs->earliestStart+m_durationForward)<<endl; + break; + case Node::MustFinishOn: + m_durationForward = duration(m_constraintEndTime, use, true); + cs->earliestStart = m_constraintEndTime - m_durationForward; + break; + case Node::FinishNotLater: + m_durationForward = duration(cs->earliestStart, use, false); + if (cs->earliestStart + m_durationForward > m_constraintEndTime) { + m_durationForward = duration(m_constraintEndTime, use, true); + cs->earliestStart = m_constraintEndTime - m_durationForward; + } + break; + case Node::MustStartOn: + cs->earliestStart = m_constraintStartTime; + m_durationForward = duration(cs->earliestStart, use, false); + break; + case Node::StartNotEarlier: + if (cs->earliestStart < m_constraintStartTime) { + cs->earliestStart = m_constraintStartTime; + } + m_durationForward = duration(cs->earliestStart, use, false); + break; + case Node::FixedInterval: { + cs->earliestStart = m_constraintStartTime; + m_durationForward = m_constraintEndTime - m_constraintStartTime; + break; + } + } + } else if (type() == Node::Type_Milestone) { + m_durationForward = Duration::zeroDuration; + switch (constraint()) { + case Node::MustFinishOn: + cs->earliestStart = m_constraintEndTime; + break; + case Node::FinishNotLater: + if (cs->earliestStart > m_constraintEndTime) { + cs->earliestStart = m_constraintEndTime; + } + break; + case Node::MustStartOn: + cs->earliestStart = m_constraintStartTime; + break; + case Node::StartNotEarlier: + if (cs->earliestStart < m_constraintStartTime) { + cs->earliestStart = m_constraintStartTime; + } + break; + case Node::FixedInterval: + cs->earliestStart = m_constraintStartTime; + break; + default: + break; + } + //kdDebug()<<k_funcinfo<<m_name<<" "<<earliestStart.toString()<<endl + } else if (type() == Node::Type_Summarytask) { + kdWarning()<<k_funcinfo<<"Summarytasks should not be calculated here: "<<m_name<<endl; + } else { // ??? + m_durationForward = Duration::zeroDuration; + } + + //kdDebug()<<"Earlyfinish: "<<cs->earliestStart<<"+"<<m_durationForward.toString()<<"="<<(cs->earliestStart+m_durationForward)<<" "<<m_name<<" calculateForward()"<<endl; + m_visitedForward = true; + return cs->earliestStart + m_durationForward; +} + +DateTime Task::calculateSuccessors(const QPtrList<Relation> &list, int use) { + DateTime time; + QPtrListIterator<Relation> it = list; + for (; it.current(); ++it) { + if (it.current()->child()->type() == Type_Summarytask) { + //kdDebug()<<k_funcinfo<<"Skip summarytask: "<<it.current()->parent()->name()<<endl; + continue; // skip summarytasks + } + DateTime t = it.current()->child()->calculateBackward(use); + switch (it.current()->type()) { + case Relation::StartStart: + // I must start before my successor, so + // I can't finish later than it's (starttime-lag) + my duration + t -= it.current()->lag(); + t += duration(t, use, false); + break; + case Relation::FinishFinish: + // My successor cannot finish before me, so + // I can't finish later than it's latest finish - lag + t = it.current()->child()->getLatestFinish() - it.current()->lag(); + break; + default: + t -= it.current()->lag(); + break; + } + if (!time.isValid() || t < time) + time = t; + } + //kdDebug()<<time.toString()<<" "<<m_name<<" calculateSuccessors() ("<<list.count()<<")"<<endl; + return time; +} +DateTime Task::calculateBackward(int use) { + //kdDebug()<<k_funcinfo<<m_name<<endl; + if (m_currentSchedule == 0) { + return DateTime(); + } + Schedule *cs = m_currentSchedule; + if (m_visitedBackward) { + //kdDebug()<<latestFinish.toString()<<" - "<<m_durationBackward.toString()<<" "<<m_name<<" calculateBackward() (visited)"<<endl; + return cs->latestFinish - m_durationBackward; + } + // First, calculate all successors + if (!dependChildNodes().isEmpty()) { + DateTime time = calculateSuccessors(dependChildNodes(), use); + if (time.isValid() && time < cs->latestFinish) { + cs->latestFinish = time; + } + } + if (!m_childProxyRelations.isEmpty()) { + DateTime time = calculateSuccessors(m_childProxyRelations, use); + if (time.isValid() && time < cs->latestFinish) { + cs->latestFinish = time; + } + } + //kdDebug()<<k_funcinfo<<m_name<<": latestFinish="<<cs->latestFinish<<endl; + if (type() == Node::Type_Task) { + m_durationBackward = m_effort->effort(use); + switch (constraint()) { + case Node::ASAP: + case Node::ALAP: + cs->latestFinish = workFinishBefore(cs->latestFinish); + m_durationBackward = duration(cs->latestFinish, use, true); + break; + case Node::MustStartOn: + m_durationBackward = duration(m_constraintStartTime, use, false); + cs->latestFinish = m_constraintStartTime + m_durationBackward; + break; + case Node::StartNotEarlier: + m_durationBackward = duration(cs->latestFinish, use, true); + if (cs->latestFinish - m_durationBackward < m_constraintStartTime) { + m_durationBackward = duration(m_constraintStartTime, use, false); + cs->latestFinish = m_constraintStartTime + m_durationBackward; + } + break; + case Node::MustFinishOn: + cs->latestFinish = m_constraintEndTime; + m_durationBackward = duration(cs->latestFinish, use, true); + break; + case Node::FinishNotLater: + if (cs->latestFinish > m_constraintEndTime) { + cs->latestFinish = m_constraintEndTime; + } + m_durationBackward = duration(cs->latestFinish, use, true); + break; + case Node::FixedInterval: { + cs->latestFinish = m_constraintEndTime; + m_durationBackward = m_constraintEndTime - m_constraintStartTime; + break; + } + } + } else if (type() == Node::Type_Milestone) { + m_durationBackward = Duration::zeroDuration; + switch (constraint()) { + case Node::MustFinishOn: + cs->latestFinish = m_constraintEndTime; + break; + case Node::FinishNotLater: + if (cs->latestFinish > m_constraintEndTime) { + cs->latestFinish = m_constraintEndTime; + } + break; + case Node::MustStartOn: + cs->latestFinish = m_constraintStartTime; + break; + case Node::StartNotEarlier: + if (cs->latestFinish < m_constraintStartTime) { + cs->latestFinish = m_constraintStartTime; + } + break; + case Node::FixedInterval: + cs->latestFinish = m_constraintEndTime; + break; + default: + break; + } + //kdDebug()<<k_funcinfo<<m_name<<" "<<cs->latestFinish<<endl; + } else if (type() == Node::Type_Summarytask) { + kdWarning()<<k_funcinfo<<"Summarytasks should not be calculated here: "<<m_name<<endl; + } else { // ??? + m_durationBackward = Duration::zeroDuration; + } + //kdDebug()<<"Latestart: "<<cs->latestFinish<<"-"<<m_durationBackward.toString()<<"="<<(cs->latestFinish-m_durationBackward).toString()<<" "<<m_name<<" calculateBackward()"<<endl; + m_visitedBackward = true; + return cs->latestFinish - m_durationBackward; +} + +DateTime Task::schedulePredeccessors(const QPtrList<Relation> &list, int use) { + DateTime time; + QPtrListIterator<Relation> it = list; + for (; it.current(); ++it) { + if (it.current()->parent()->type() == Type_Summarytask) { + //kdDebug()<<k_funcinfo<<"Skip summarytask: "<<it.current()->parent()->name()<<endl; + continue; // skip summarytasks + } + // schedule the predecessors + DateTime earliest = it.current()->parent()->getEarliestStart(); + DateTime t = it.current()->parent()->scheduleForward(earliest, use); + switch (it.current()->type()) { + case Relation::StartStart: + // I can't start before my predesseccor + t = it.current()->parent()->startTime() + it.current()->lag(); + break; + case Relation::FinishFinish: + // I can't end before my predecessor, so + // I can't start before it's endtime - my duration + t -= duration(t + it.current()->lag(), use, true); + break; + default: + t += it.current()->lag(); + break; + } + if (!time.isValid() || t > time) + time = t; + } + //kdDebug()<<time.toString()<<" "<<m_name<<" schedulePredeccessors()"<<endl; + return time; +} + +DateTime Task::scheduleForward(const DateTime &earliest, int use) { + //kdDebug()<<k_funcinfo<<m_name<<" earliest="<<earliest<<endl; + if (m_currentSchedule == 0) { + return DateTime(); + } + Schedule *cs = m_currentSchedule; + if (m_visitedForward) { + return cs->endTime; + } + cs->notScheduled = false; + cs->startTime = earliest > cs->earliestStart ? earliest : cs->earliestStart; + // First, calculate all my own predecessors + DateTime time = schedulePredeccessors(dependParentNodes(), use); + if (time.isValid() && time > cs->startTime) { + cs->startTime = time; + //kdDebug()<<k_funcinfo<<m_name<<" new startime="<<cs->startTime<<endl; + } + // Then my parents + time = schedulePredeccessors(m_parentProxyRelations, use); + if (time.isValid() && time > cs->startTime) { + cs->startTime = time; + //kdDebug()<<k_funcinfo<<m_name<<" new startime="<<cs->startTime<<endl; + } + //kdDebug()<<k_funcinfo<<m_name<<" startTime="<<cs->startTime<<endl; + if(type() == Node::Type_Task) { + cs->duration = m_effort->effort(use); + switch (m_constraint) { + case Node::ASAP: + // cs->startTime calculated above + //kdDebug()<<k_funcinfo<<m_name<<" startTime="<<cs->startTime<<endl; + cs->startTime = workStartAfter(cs->startTime); + cs->duration = duration(cs->startTime, use, false); + cs->endTime = cs->startTime + cs->duration; + //kdDebug()<<k_funcinfo<<m_name<<" startTime="<<cs->startTime<<endl; + break; + case Node::ALAP: + // cd->startTime calculated above + cs->duration = duration(cs->latestFinish, use, true); + cs->endTime = cs->latestFinish; + cs->startTime = cs->endTime - cs->duration; + //kdDebug()<<k_funcinfo<<m_name<<" endTime="<<cs->endTime<<" latest="<<cs->latestFinish<<endl; + break; + case Node::StartNotEarlier: + // cs->startTime calculated above + //kdDebug()<<"StartNotEarlier="<<m_constraintStartTime.toString()<<" "<<cd->startTime.toString()<<endl; + if (cs->startTime < m_constraintStartTime) { + cs->startTime = m_constraintStartTime; + } + cs->startTime = workStartAfter(cs->startTime); + cs->duration = duration(cs->startTime, use, false); + cs->endTime = cs->startTime + cs->duration; + if (cs->endTime > cs->latestFinish) { + cs->schedulingError = true; + } + break; + case Node::FinishNotLater: + // cs->startTime calculated above + //kdDebug()<<"FinishNotLater="<<m_constraintEndTime.toString()<<" "<<cs->startTime.toString()<<endl; + cs->duration = duration(cs->startTime, use, false); + cs->endTime = cs->startTime + cs->duration; + if (cs->endTime > m_constraintEndTime) { + cs->schedulingError = true; + cs->endTime = m_constraintEndTime; + cs->duration = duration(cs->endTime, use, true); + cs->startTime = cs->endTime - cs->duration; + } + break; + case Node::MustStartOn: + // cs->startTime calculated above + //kdDebug()<<"MustStartOn="<<m_constraintStartTime.toString()<<" "<<cs->startTime.toString()<<endl; + if (m_constraintStartTime < cs->startTime || + m_constraintStartTime > cs->latestFinish - m_durationBackward) { + cs->schedulingError = true; + } + cs->startTime = m_constraintStartTime; + cs->duration = duration(cs->startTime, use, false); + cs->endTime = cs->startTime + cs->duration; + break; + case Node::MustFinishOn: + // cs->startTime calculated above + //kdDebug()<<"MustFinishOn="<<m_constraintEndTime.toString()<<" "<<cs->startTime.toString()<<endl; + if (m_constraintEndTime > cs->latestFinish || + m_constraintEndTime < cs->earliestStart + m_durationForward) { + cs->schedulingError = true; + } + cs->endTime = m_constraintEndTime; + cs->duration = duration(cs->endTime, use, true); + cs->startTime = cs->endTime - cs->duration; + break; + case Node::FixedInterval: { + // cs->startTime calculated above + //kdDebug()<<"FixedInterval="<<m_constraintStartTime<<" "<<cs->startTime<<endl; + if (cs->startTime < cs->earliestStart) { + cs->schedulingError = true; + } + cs->startTime = m_constraintStartTime; + cs->endTime = m_constraintEndTime; + cs->duration = cs->endTime - cs->startTime; + cs->workStartTime = m_constraintStartTime; + cs->workEndTime = m_constraintEndTime; + //kdDebug()<<"FixedInterval="<<cs->startTime<<", "<<cs->endTime<<endl; + break; + } + default: + break; + } + if (m_requests) { + m_requests->reserve(cs->startTime, cs->duration); + } + } else if (type() == Node::Type_Milestone) { + switch (m_constraint) { + case Node::ASAP: { + cs->endTime = cs->startTime; + break; + } + case Node::ALAP: { + cs->startTime = cs->latestFinish; + cs->endTime = cs->latestFinish; + break; + } + case Node::MustStartOn: + case Node::FixedInterval: + //kdDebug()<<"Forw, MustStartOn: "<<m_constraintStartTime.toString()<<" "<<cs->startTime.toString()<<endl; + if (m_constraintStartTime < cs->startTime || + m_constraintStartTime > cs->latestFinish) { + cs->schedulingError = true; + } + cs->startTime = m_constraintStartTime; + cs->endTime = m_constraintStartTime; + break; + case Node::MustFinishOn: + if (m_constraintEndTime < cs->startTime || + m_constraintEndTime > cs->latestFinish) { + cs->schedulingError = true; + } + cs->startTime = m_constraintEndTime; + cs->endTime = m_constraintEndTime; + break; + case Node::StartNotEarlier: + if (cs->startTime < m_constraintStartTime) { + cs->schedulingError = true; + } + cs->endTime = cs->startTime; + break; + case Node::FinishNotLater: + if (cs->startTime > m_constraintEndTime) { + cs->schedulingError = true; + } + cs->endTime = cs->startTime; + break; + default: + break; + } + cs->duration = Duration::zeroDuration; + //kdDebug()<<k_funcinfo<<m_name<<": "<<cs->startTime<<", "<<cs->endTime<<endl; + } else if (type() == Node::Type_Summarytask) { + //shouldn't come here + cs->endTime = cs->startTime; + cs->duration = cs->endTime - cs->startTime; + kdWarning()<<k_funcinfo<<"Summarytasks should not be calculated here: "<<m_name<<endl; + } + //kdDebug()<<cs->startTime.toString()<<" : "<<cs->endTime.toString()<<" "<<m_name<<" scheduleForward()"<<endl; + m_visitedForward = true; + return cs->endTime; +} + +DateTime Task::scheduleSuccessors(const QPtrList<Relation> &list, int use) { + DateTime time; + QPtrListIterator<Relation> it = list; + for (; it.current(); ++it) { + if (it.current()->child()->type() == Type_Summarytask) { + //kdDebug()<<k_funcinfo<<"Skip summarytask: "<<it.current()->child()->name()<<endl; + continue; + } + // get the successors starttime + DateTime latest = it.current()->child()->getLatestFinish(); + DateTime t = it.current()->child()->scheduleBackward(latest, use); + switch (it.current()->type()) { + case Relation::StartStart: + // I can't start before my successor, so + // I can't finish later than it's starttime + my duration + t += duration(t - it.current()->lag(), use, false); + break; + case Relation::FinishFinish: + t = it.current()->child()->endTime() - it.current()->lag(); + break; + default: + t -= it.current()->lag(); + break; + } + if (!time.isValid() || t < time) + time = t; + } + return time; +} +DateTime Task::scheduleBackward(const DateTime &latest, int use) { + //kdDebug()<<k_funcinfo<<m_name<<": latest="<<latest<<endl; + if (m_currentSchedule == 0) { + return DateTime(); + } + Schedule *cs = m_currentSchedule; + if (m_visitedBackward) { + return cs->startTime; + } + cs->notScheduled = false; + cs->endTime = latest < cs->latestFinish ? latest : cs->latestFinish; + // First, calculate all my own successors + DateTime time = scheduleSuccessors(dependChildNodes(), use); + if (time.isValid() && time < cs->endTime) { + cs->endTime = time; + } + // Then my parents + time = scheduleSuccessors(m_childProxyRelations, use); + if (time.isValid() && time < cs->endTime) { + cs->endTime = time; + } + if (type() == Node::Type_Task) { + cs->duration = m_effort->effort(use); + switch (m_constraint) { + case Node::ASAP: { + // cs->endTime calculated above + //kdDebug()<<k_funcinfo<<m_name<<": end="<<cs->endTime<<" early="<<cs->earliestStart<<endl; + cs->endTime = workFinishBefore(cs->endTime); + cs->duration = duration(cs->earliestStart, use, false); + cs->startTime = cs->earliestStart; + DateTime e = cs->startTime + cs->duration; + if (e > cs->endTime) { + cs->schedulingError = true; + } + cs->endTime = e; + //kdDebug()<<k_funcinfo<<m_name<<": start="<<cs->startTime<<"+"<<cs->duration.toString()<<"="<<e<<" -> end="<<cs->endTime<<endl; + break; + } + case Node::ALAP: + // cs->endTime calculated above + //kdDebug()<<k_funcinfo<<m_name<<": end="<<cs->endTime<<" late="<<cs->latestFinish<<endl; + cs->endTime = workFinishBefore(cs->endTime); + cs->duration = duration(cs->endTime, use, true); + cs->startTime = cs->endTime - cs->duration; + //kdDebug()<<k_funcinfo<<m_name<<": lateStart="<<cs->startTime<<endl; + break; + case Node::StartNotEarlier: + // cs->endTime calculated above + //kdDebug()<<"StartNotEarlier="<<m_constraintStartTime.toString()<<" "<<cs->endTime.toString()<<endl; + cs->endTime = workFinishBefore(cs->endTime); + cs->duration = duration(cs->endTime, use, true); + cs->startTime = cs->endTime - cs->duration; + if (cs->startTime < m_constraintStartTime) { + cs->schedulingError = true; + cs->startTime = m_constraintStartTime; + cs->duration = duration(cs->startTime, use, false); + cs->endTime = cs->startTime + cs->duration; + } + break; + case Node::FinishNotLater: + // cs->endTime calculated above + //kdDebug()<<"FinishNotLater="<<m_constraintEndTime.toString()<<" "<<cs->endTime.toString()<<endl; + if (cs->endTime > m_constraintEndTime) { + cs->schedulingError = true; + cs->endTime = m_constraintEndTime; + } + cs->endTime = workFinishBefore(cs->endTime); + cs->duration = duration(cs->endTime, use, true); + cs->startTime = cs->endTime - cs->duration; + break; + case Node::MustStartOn: + // cs->endTime calculated above + //kdDebug()<<"MustStartOn="<<m_constraintStartTime.toString()<<" "<<cs->startTime.toString()<<endl; + if (m_constraintStartTime < cs->earliestStart || + m_constraintStartTime > cs->latestFinish - m_durationBackward) { + cs->schedulingError = true; + } + cs->startTime = m_constraintStartTime; + cs->duration = duration(cs->startTime, use, false); + cs->endTime = cs->startTime + cs->duration; + break; + case Node::MustFinishOn: + // cs->endTime calculated above + //kdDebug()<<"MustFinishOn="<<m_constraintEndTime.toString()<<" "<<cs->startTime.toString()<<endl; + if (m_constraintEndTime > cs->latestFinish || + m_constraintEndTime < cs->earliestStart + m_durationForward) { + cs->schedulingError = true; + } + cs->endTime = m_constraintEndTime; + cs->duration = duration(cs->endTime, use, true); + cs->startTime = cs->endTime - cs->duration; + break; + case Node::FixedInterval: { + // cs->endTime calculated above + //kdDebug()<<k_funcinfo<<"FixedInterval="<<m_constraintEndTime<<" "<<cs->endTime<<endl; + if (m_constraintEndTime > cs->endTime) { + cs->schedulingError = true; + //kdDebug()<<k_funcinfo<<"FixedInterval error: "<<m_constraintEndTime<<" > "<<cs->endTime<<endl; + } + cs->startTime = m_constraintStartTime; + cs->endTime = m_constraintEndTime; + cs->duration = cs->endTime - cs->startTime; + cs->workStartTime = m_constraintStartTime; + cs->workEndTime = m_constraintEndTime; + break; + } + default: + break; + } + if (m_requests) { + m_requests->reserve(cs->startTime, cs->duration); + } + } else if (type() == Node::Type_Milestone) { + switch (m_constraint) { + case Node::ASAP: + cs->startTime = cs->earliestStart; + cs->endTime = cs->earliestStart; + break; + case Node::ALAP: + cs->startTime = cs->latestFinish; + cs->endTime = cs->latestFinish; + break; + case Node::MustStartOn: + case Node::FixedInterval: + if (m_constraintStartTime < cs->earliestStart || + m_constraintStartTime > cs->endTime) { + cs->schedulingError = true; + } + cs->startTime = cs->earliestStart; + cs->endTime = cs->earliestStart; + break; + case Node::MustFinishOn: + if (m_constraintEndTime < cs->earliestStart || + m_constraintEndTime > cs->endTime) { + cs->schedulingError = true; + } + cs->startTime = cs->earliestStart; + cs->endTime = cs->earliestStart; + break; + case Node::StartNotEarlier: + if (m_constraintStartTime > cs->endTime) { + cs->schedulingError = true; + } + cs->startTime = cs->endTime; + break; + case Node::FinishNotLater: + if (m_constraintEndTime < cs->endTime) { + cs->schedulingError = true; + } + cs->startTime = cs->endTime; + break; + default: + break; + } + cs->duration = Duration::zeroDuration; + } else if (type() == Node::Type_Summarytask) { + //shouldn't come here + cs->startTime = cs->endTime; + cs->duration = cs->endTime - cs->startTime; + kdWarning()<<k_funcinfo<<"Summarytasks should not be calculated here: "<<m_name<<endl; + } + //kdDebug()<<k_funcinfo<<m_name<<": "<<cs->startTime.toString()<<" : "<<cs->endTime.toString()<<endl; + m_visitedBackward = true; + return cs->startTime; +} + +void Task::adjustSummarytask() { + if (m_currentSchedule == 0) + return; + if (type() == Type_Summarytask) { + DateTime start = m_currentSchedule->latestFinish; + DateTime end = m_currentSchedule->earliestStart; + QPtrListIterator<Node> it(m_nodes); + for (; it.current(); ++it) { + it.current()->adjustSummarytask(); + if (it.current()->startTime() < start) + start = it.current()->startTime(); + if (it.current()->endTime() > end) + end = it.current()->endTime(); + } + m_currentSchedule->startTime = start; + m_currentSchedule->endTime = end; + m_currentSchedule->duration = end - start; + m_currentSchedule->notScheduled = false; + //kdDebug()<<k_funcinfo<<cs->name<<": "<<m_currentSchedule->startTime.toString()<<" : "<<m_currentSchedule->endTime.toString()<<endl; + } +} + +Duration Task::calcDuration(const DateTime &time, const Duration &effort, bool backward) { + //kdDebug()<<"--------> calcDuration "<<(backward?"(B) ":"(F) ")<<m_name<<" time="<<time<<" effort="<<effort.toString(Duration::Format_Day)<<endl; + + // Allready checked: m_effort, m_currentSchedule and time. + Duration dur = effort; // use effort as default duration + if (m_effort->type() == Effort::Type_Effort) { + if (m_requests == 0 || m_requests->isEmpty()) { + m_currentSchedule->resourceError = true; + return effort; + } + dur = m_requests->duration(time, effort, backward); + if (dur == Duration::zeroDuration) { + kdWarning()<<k_funcinfo<<"zero duration: Resource not available"<<endl; + m_currentSchedule->resourceNotAvailable = true; + dur = effort; //??? + } + return dur; + } + if (m_effort->type() == Effort::Type_FixedDuration) { + //TODO: Different types of fixed duration + return dur; // + } + kdError()<<k_funcinfo<<"Unsupported effort type: "<<m_effort->type()<<endl; + return dur; +} + +void Task::clearProxyRelations() { + m_parentProxyRelations.clear(); + m_childProxyRelations.clear(); +} + +void Task::addParentProxyRelations(QPtrList<Relation> &list) { + //kdDebug()<<k_funcinfo<<m_name<<endl; + if (type() == Type_Summarytask) { + // propagate to my children + //kdDebug()<<k_funcinfo<<m_name<<" is summary task"<<endl; + QPtrListIterator<Node> nodes = m_nodes; + for (; nodes.current(); ++nodes) { + nodes.current()->addParentProxyRelations(list); + nodes.current()->addParentProxyRelations(dependParentNodes()); + } + } else { + // add 'this' as child relation to the relations parent + //kdDebug()<<k_funcinfo<<m_name<<" is not summary task"<<endl; + QPtrListIterator<Relation> it = list; + for (; it.current(); ++it) { + it.current()->parent()->addChildProxyRelation(this, it.current()); + // add a parent relation to myself + addParentProxyRelation(it.current()->parent(), it.current()); + } + } +} + +void Task::addChildProxyRelations(QPtrList<Relation> &list) { + //kdDebug()<<k_funcinfo<<m_name<<endl; + if (type() == Type_Summarytask) { + // propagate to my children + //kdDebug()<<k_funcinfo<<m_name<<" is summary task"<<endl; + QPtrListIterator<Node> nodes = m_nodes; + for (; nodes.current(); ++nodes) { + nodes.current()->addChildProxyRelations(list); + nodes.current()->addChildProxyRelations(dependChildNodes()); + } + } else { + // add 'this' as parent relation to the relations child + //kdDebug()<<k_funcinfo<<m_name<<" is not summary task"<<endl; + QPtrListIterator<Relation> it = list; + for (; it.current(); ++it) { + it.current()->child()->addParentProxyRelation(this, it.current()); + // add a child relation to myself + addChildProxyRelation(it.current()->child(), it.current()); + } + } +} + +void Task::addParentProxyRelation(Node *node, const Relation *rel) { + if (node->type() != Type_Summarytask) { + if (type() == Type_Summarytask) { + //kdDebug()<<"Add parent proxy from my children "<<m_name<<" to "<<node->name()<<endl; + QPtrListIterator<Node> nodes = m_nodes; + for (; nodes.current(); ++nodes) { + nodes.current()->addParentProxyRelation(node, rel); + } + } else { + //kdDebug()<<"Add parent proxy from "<<node->name()<<" to (me) "<<m_name<<endl; + m_parentProxyRelations.append(new ProxyRelation(node, this, rel->type(), rel->lag())); + } + } +} + +void Task::addChildProxyRelation(Node *node, const Relation *rel) { + if (node->type() != Type_Summarytask) { + if (type() == Type_Summarytask) { + //kdDebug()<<"Add child proxy from my children "<<m_name<<" to "<<node->name()<<endl; + QPtrListIterator<Node> nodes = m_nodes; + for (; nodes.current(); ++nodes) { + nodes.current()->addChildProxyRelation(node, rel); + } + } else { + //kdDebug()<<"Add child proxy from (me) "<<m_name<<" to "<<node->name()<<endl; + m_childProxyRelations.append(new ProxyRelation(this, node, rel->type(), rel->lag())); + } + } +} + +bool Task::isEndNode() const { + QPtrListIterator<Relation> it = m_dependChildNodes; + for (; it.current(); ++it) { + if (it.current()->type() == Relation::FinishStart) + return false; + } + QPtrListIterator<Relation> pit = m_childProxyRelations; + for (; pit.current(); ++pit) { + if (pit.current()->type() == Relation::FinishStart) + return false; + } + return true; +} +bool Task::isStartNode() const { + QPtrListIterator<Relation> it = m_dependParentNodes; + for (; it.current(); ++it) { + if (it.current()->type() == Relation::FinishStart || + it.current()->type() == Relation::StartStart) + return false; + } + QPtrListIterator<Relation> pit = m_parentProxyRelations; + for (; pit.current(); ++pit) { + if (pit.current()->type() == Relation::FinishStart || + pit.current()->type() == Relation::StartStart) + return false; + } + return true; +} + +DateTime Task::workStartTime() const { + if (m_currentSchedule == 0) + return DateTime(); + if (m_requests) + return m_currentSchedule->workStartTime; + return m_currentSchedule->startTime; +} + +DateTime Task::workEndTime() const { + if (m_currentSchedule == 0) + return DateTime(); + return m_currentSchedule->endTime; +} + +DateTime Task::workStartAfter(const DateTime &dt) { + if (m_requests) { + DateTime t = m_requests->availableAfter(dt); + return t.isValid() ? t : dt; + } + return dt; +} + +DateTime Task::workFinishBefore(const DateTime &dt) { + if (m_requests) { + return m_requests->availableBefore(dt); + } + return dt; +} + +Duration Task::positiveFloat() { + if (m_currentSchedule == 0 || + m_currentSchedule->schedulingError || + effortMetError()) { + return Duration::zeroDuration; + } + Duration f; + if (type() == Node::Type_Milestone) { + if (m_currentSchedule->startTime < m_currentSchedule->latestFinish) { + f = m_currentSchedule->latestFinish - m_currentSchedule->startTime; + } + } else if (m_effort->type() == Effort::Type_FixedDuration) { + if (m_currentSchedule->endTime.isValid()) { + if (m_currentSchedule->endTime < m_currentSchedule->latestFinish) { + f = m_currentSchedule->latestFinish - m_currentSchedule->endTime; + } + } + } else { + if (m_currentSchedule->workEndTime.isValid()) + if (m_currentSchedule->workEndTime < m_currentSchedule->latestFinish) { + f = m_currentSchedule->latestFinish - m_currentSchedule->workEndTime; + } else if (m_currentSchedule->endTime.isValid()) { + if (m_currentSchedule->endTime < m_currentSchedule->latestFinish) { + f = m_currentSchedule->latestFinish - m_currentSchedule->endTime; + } + } + } + //kdDebug()<<k_funcinfo<<f.toString()<<endl; + return f; +} + +bool Task::isCritical() { + Schedule *cs = m_currentSchedule; + if (cs == 0) { + return false; + } + return cs->earliestStart >= cs->startTime && cs->latestFinish <= cs->endTime; +} + +bool Task::calcCriticalPath(bool fromEnd) { + if (m_currentSchedule == 0) + return false; + //kdDebug()<<k_funcinfo<<m_name<<" fromEnd="<<fromEnd<<" cp="<<m_currentSchedule->inCriticalPath<<endl; + if (m_currentSchedule->inCriticalPath) { + return true; // path allready calculated + } + if (!isCritical()) { + return false; + } + if (fromEnd) { + if (isEndNode()) { + m_currentSchedule->inCriticalPath = true; + //kdDebug()<<k_funcinfo<<m_name<<" end node"<<endl; + return true; + } + QPtrListIterator<Relation> it(m_childProxyRelations); + for (; it.current(); ++it) { + if (it.current()->child()->calcCriticalPath(fromEnd)) { + m_currentSchedule->inCriticalPath = true; + } + } + QPtrListIterator<Relation> pit(m_dependChildNodes); + for (; pit.current(); ++pit) { + if (pit.current()->child()->calcCriticalPath(fromEnd)) { + m_currentSchedule->inCriticalPath = true; + } + } + } else { + if (isStartNode()) { + m_currentSchedule->inCriticalPath = true; + //kdDebug()<<k_funcinfo<<m_name<<" start node"<<endl; + return true; + } + QPtrListIterator<Relation> it(m_parentProxyRelations); + for (; it.current(); ++it) { + if (it.current()->parent()->calcCriticalPath(fromEnd)) { + m_currentSchedule->inCriticalPath = true; + } + } + QPtrListIterator<Relation> pit(m_dependParentNodes); + for (; pit.current(); ++pit) { + if (pit.current()->parent()->calcCriticalPath(fromEnd)) { + m_currentSchedule->inCriticalPath = true; + } + } + } + //kdDebug()<<k_funcinfo<<m_name<<" return cp="<<m_currentSchedule->inCriticalPath<<endl; + return m_currentSchedule->inCriticalPath; +} + +void Task::setCurrentSchedule(long id) { + setCurrentSchedulePtr(findSchedule(id)); + Node::setCurrentSchedule(id); +} + +bool Task::effortMetError() const { + if (m_currentSchedule->notScheduled) { + return false; + } + return m_currentSchedule->plannedEffort() < effort()->effort(static_cast<Effort::Use>(static_cast<int>(m_currentSchedule->type()))); +} + +#ifndef NDEBUG +void Task::printDebug(bool children, QCString indent) { + kdDebug()<<indent<<"+ Task node: "<<name()<<" type="<<type()<<endl; + indent += "! "; + kdDebug()<<indent<<"Requested resources (total): "<<units()<<"%"<<endl; + kdDebug()<<indent<<"Requested resources (work): "<<workUnits()<<"%"<<endl; + if (m_requests) + m_requests->printDebug(indent); + + Node::printDebug(children, indent); + +} + +#endif + +} //KPlato namespace diff --git a/kplato/kpttask.h b/kplato/kpttask.h new file mode 100644 index 00000000..17972ee7 --- /dev/null +++ b/kplato/kpttask.h @@ -0,0 +1,306 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Thomas Zander zander@kde.org + Copyright (C) 2004 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef KPTTASK_H +#define KPTTASK_H + +#include "kptnode.h" +#include "kptduration.h" +#include "kptresource.h" + +#include <qptrlist.h> + +namespace KPlato +{ + +class DateTime; + +/** + * A task in the scheduling software is represented by this class. A task + * can be anything from 'build house' to 'drill hole' It will always mean + * an activity. + */ +class Task : public Node { +public: + Task(Node *parent = 0); + Task(Task &task, Node *parent = 0); + ~Task(); + + /// Return task type. Can be Type_Task, Type_Summarytask ot Type_Milestone. + virtual int type() const; + + /** + * Returns the (previously) calculated duration. + * The caller must delete returned object. + */ + Duration *getExpectedDuration(); + + /** + * Instead of using the expected duration, generate a random value using + * the Distribution of each Task. This can be used for Monte-Carlo + * estimation of Project duration. + */ + Duration *getRandomDuration(); + + /** + * Return the resource request made to group + * (There should be only one) + */ + ResourceGroupRequest *resourceGroupRequest(ResourceGroup *group) const; + void clearResourceRequests(); + void addRequest(ResourceGroup *group, int numResources); + void addRequest(ResourceGroupRequest *request); + void takeRequest(ResourceGroupRequest *request); + int units() const; + int workUnits() const; + void makeAppointments(); + /** + * Calculates if the assigned resource is overbooked + * within the duration of this task + */ + void calcResourceOverbooked(); + + void setConstraint(Node::ConstraintType type); + + /// Load from document + virtual bool load(QDomElement &element, Project &project); + /// Save to document + virtual void save(QDomElement &element) const; + /// Save appointments for schedule with id + virtual void saveAppointments(QDomElement &element, long id) const; + /** + * Returns a list of planned effort and cost for this task + * for the interval start, end inclusive + */ + virtual EffortCostMap plannedEffortCostPrDay(const QDate &start, const QDate &end) const; + + /// Returns the total planned effort for this task (or subtasks) + virtual Duration plannedEffort(); + /// Returns the total planned effort for this task (or subtasks) on date + virtual Duration plannedEffort(const QDate &date); + /// Returns the planned effort up to and including date + virtual Duration plannedEffortTo(const QDate &date); + + /// Returns the total actual effort for this task (or subtasks) + virtual Duration actualEffort(); + /// Returns the total actual effort for this task (or subtasks) on date + virtual Duration actualEffort(const QDate &date); + /// Returns the actual effort up to and including date + virtual Duration actualEffortTo(const QDate &date); + + /** + * Returns the total planned cost for this task (or subtasks) + */ + virtual double plannedCost(); + /// Planned cost on date + virtual double plannedCost(const QDate &/*date*/); + /// Planned cost up to and including date + virtual double plannedCostTo(const QDate &/*date*/); + + /** + * Returns the actaually reported cost for this task (or subtasks) + */ + virtual double actualCost(); + /// Actual cost on date + virtual double actualCost(const QDate &/*date*/); + /// Actual cost up to and including date + virtual double actualCostTo(const QDate &/*date*/); + + /// Effort based performance index + double effortPerformanceIndex(const QDate &date, bool *error=0); + /// Cost performance index + double costPerformanceIndex(const QDate &date, bool *error=0); + + void initiateCalculation(Schedule &sch); + /** + * Sets up the lists used for calculation. + * This includes adding summarytasks relations to subtasks + * and lists for start- and endnodes. + */ + void initiateCalculationLists(QPtrList<Node> &startnodes, QPtrList<Node> &endnodes, QPtrList<Node> &summarytasks); + /** + * Calculates ref m_durationForward from ref earliestStart and + * returns the resulting end time, + * which will be used as the succesors ref earliestStart. + * + * @param use Calculate using expected-, optimistic- or pessimistic estimate. + */ + DateTime calculateForward(int use); + /** + * Calculates ref m_durationBackward from ref latestFinish and + * returns the resulting start time, + * which will be used as the predecessors ref latestFinish. + * + * @param use Calculate using expected-, optimistic- or pessimistic estimate. + */ + DateTime calculateBackward(int use); + /** + * Schedules the task within the limits of earliestStart and latestFinish. + * Calculates ref m_startTime, ref m_endTime and ref m_duration, + * Assumes ref calculateForward() and ref calculateBackward() has been run. + * + * @param earliest The task is not scheduled to start earlier than this + * @param use Calculate using expected-, optimistic- or pessimistic estimate. + * @return The tasks endtime which can be used for scheduling the successor. + */ + DateTime scheduleForward(const DateTime &earliest, int use); + /** + * Schedules the task within the limits of earliestStart and latestFinish. + * Calculates ref m_startTime, ref m_endTime and ref m_duration, + * Assumes ref calculateForward() and ref calculateBackward() has been run. + * + * @param latest The task is not scheduled to end later than this + * @param use Calculate using expected-, optimistic- or pessimistic estimate. + * @return The tasks starttime which can be used for scheduling the predeccessor. + */ + DateTime scheduleBackward(const DateTime &latest, int use); + + /** + * Summarytasks (with milestones) need special treatment because + * milestones are always 'glued' to their predecessors. + */ + void adjustSummarytask(); + + /** + * Return the duration calculated on bases of the requested resources + */ + Duration calcDuration(const DateTime &time, const Duration &effort, bool backward); + + // Proxy relations are relations to/from summarytasks. + // These relations are distrubuted to the relevant tasks before calculation. + void clearProxyRelations(); + void addParentProxyRelations(QPtrList<Relation> &list); + void addChildProxyRelations(QPtrList<Relation> &list); + void addParentProxyRelation(Node *node, const Relation *rel); + void addChildProxyRelation(Node *node, const Relation *rel); + + /// Check if this node has any dependent child nodes. + bool isEndNode() const; + /// Check if this node has any dependent parent nodes + bool isStartNode() const; + + /** + * Return the time when work can actually start on this task. + * This will be the time assigned resources can start work in accordance + * with their calendar, or if no resources have been assigned, + * the scheduled starttime is used. + */ + virtual DateTime workStartTime() const; + + /** + * Return the time when work can actually finish on this task. + * This will be the time assigned resources can end work in accordance + * with their calendar, or if no resources have been assigned, + * the scheduled endtime is used. + */ + virtual DateTime workEndTime() const; + + /** + * Return the duration that an activity's start can be delayed + * without affecting the project completion date. + * An activity with positive float is not on the critical path. + */ + Duration positiveFloat(); + /** + * Return the duration by which the duration of an activity or path + * has to be reduced in order to fullfill a timing constraint. + */ + Duration negativeFloat() { return Duration(); } + /** + * Return the duration by which an activity can be delayed or extended + * without affecting the start of any succeeding activity. + */ + Duration freeFloat() { return Duration(); } + /** + * Return the duration from Early Start to Late Start. + */ + Duration startFloat() { return Duration(); } + /** + * Return the duration the task has at its finish before a successor task starts. + * This is the difference between the start time of the successor and + * the finish time of this task. + */ + Duration finishFloat() { return Duration(); } + + /// A task is critical if there is no positive float + virtual bool isCritical(); + /// Calculate critical path + virtual bool calcCriticalPath(bool fromEnd); + + /// Set current schedule to schedule with identity id, for me nd my children + virtual void setCurrentSchedule(long id); + + virtual bool effortMetError() const; + + struct Progress { + Progress() { started = finished = false; percentFinished = 0; } + bool operator==(struct Progress &p) { + return started == p.started && finished == p.finished && + startTime == p.startTime && finishTime == p.finishTime && + percentFinished == p.percentFinished && + remainingEffort == p.remainingEffort && + totalPerformed == p.totalPerformed; + } + bool operator!=(struct Progress &p) { return !(*this == p); } + struct Progress &operator=(struct Progress &p) { + started = p.started; finished = p.finished; + startTime = p.startTime; finishTime = p.finishTime; + percentFinished = p.percentFinished; + remainingEffort = p.remainingEffort; + totalPerformed = p.totalPerformed; + return *this; + } + bool started, finished; + DateTime startTime, finishTime; + int percentFinished; + Duration remainingEffort; + Duration totalPerformed; + }; + struct Progress &progress() { return m_progress; } + +private: + DateTime calculateSuccessors(const QPtrList<Relation> &list, int use); + DateTime calculatePredeccessors(const QPtrList<Relation> &list, int use); + DateTime scheduleSuccessors(const QPtrList<Relation> &list, int use); + DateTime schedulePredeccessors(const QPtrList<Relation> &list, int use); + + DateTime workStartAfter(const DateTime &dt); + DateTime workFinishBefore(const DateTime &dt); + +private: + QPtrList<ResourceGroup> m_resource; + + ResourceRequestCollection *m_requests; + + QPtrList<Relation> m_parentProxyRelations; + QPtrList<Relation> m_childProxyRelations; + + struct Progress m_progress; + +#ifndef NDEBUG +public: + void printDebug(bool children, QCString indent); +#endif + +}; + +} //KPlato namespace + +#endif diff --git a/kplato/kpttaskappointmentsview.cc b/kplato/kpttaskappointmentsview.cc new file mode 100644 index 00000000..a686f108 --- /dev/null +++ b/kplato/kpttaskappointmentsview.cc @@ -0,0 +1,182 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Dag Andersen <danders@get2net.dk> + + 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. +*/ + +#include "kpttaskappointmentsview.h" + +#include "kptappointment.h" +#include "kpttask.h" + +#include <qapplication.h> +#include <kcalendarsystem.h> +#include <kglobal.h> +#include <klocale.h> + +#include <qheader.h> + +namespace KPlato +{ + +TaskAppointmentsView::ResourceItem::ResourceItem(Resource *r, QListView *parent, bool highlight) + : DoubleListViewBase::MasterListItem(parent, r->name(), highlight), + resource(r) { + + setFormat(0, 'f', 1); + //kdDebug()<<k_funcinfo<<endl; +} +TaskAppointmentsView::ResourceItem::ResourceItem(Resource *r, QListViewItem *p, bool highlight) + : DoubleListViewBase::MasterListItem(p, r->name(), highlight), + resource(r) { + + setFormat(0, 'f', 1); + //kdDebug()<<k_funcinfo<<endl; +} + +TaskAppointmentsView::ResourceItem::ResourceItem(QString text, QListViewItem *parent, bool highlight) + : DoubleListViewBase::MasterListItem(parent, text, highlight), + resource(0) { + + setFormat(0, 'f', 1); + //kdDebug()<<k_funcinfo<<endl; +} + +//------------------------------------------- +TaskAppointmentsView::TaskAppointmentsView(QWidget *parent) + : DoubleListViewBase(parent), + m_task(0) { + + setNameHeader(i18n("Resource")); + + + QValueList<int> list = sizes(); + int tot = list[0] + list[1]; + list[0] = QMIN(35, tot); + list[1] = tot-list[0]; + setSizes(list); +} + +void TaskAppointmentsView::zoom(double zoom) { + Q_UNUSED(zoom); +} + + +void TaskAppointmentsView::draw(Task *task) { + m_task = task; + draw(); +} + +void TaskAppointmentsView::draw() { + //kdDebug()<<k_funcinfo<<endl; + clearLists(); + if (!m_task) + return; + + QPtrList<Appointment> lst = m_task->appointments(); + QPtrListIterator<Appointment> it(lst); + for (; it.current(); ++it) { + Resource *r = it.current()->resource()->resource(); + TaskAppointmentsView::ResourceItem *item = new TaskAppointmentsView::ResourceItem(r, masterListView()); + + item->effortMap = it.current()->plannedPrDay(m_task->startTime().date(), m_task->endTime().date()); + } + slotUpdate(); +} + +void TaskAppointmentsView::drawContents(QPainter* painter) +{ + this->DoubleListViewBase::drawContents(painter); +} + +void TaskAppointmentsView::slotUpdate() { + //kdDebug()<<k_funcinfo<<endl; + if (!m_task) + return; + QApplication::setOverrideCursor(Qt::waitCursor); + createSlaveItems(); + KLocale *locale = KGlobal::locale(); + const KCalendarSystem *cal = locale->calendar(); + + // Add columns for selected period/periods + QDate start = m_task->startTime().date(); + QDate end = m_task->endTime().date(); + //kdDebug()<<k_funcinfo<<start.toString()<<" - "<<end.toString()<<endl; + int c=0; + for (QDate dt = start; dt <= end; dt = cal->addDays(dt, 1), ++c) { + QString df = locale->formatDate(dt, true); + addSlaveColumn(df); + } + QListViewItemIterator it(masterListView()); + for (;it.current(); ++it) { + TaskAppointmentsView::ResourceItem *item = static_cast<TaskAppointmentsView::ResourceItem*>(it.current()); + if (!item) { + continue; + } + double eff; + int col=0; + for (QDate d=start; d <= end; d = cal->addDays(d, 1), ++col) { + eff = (double)(item->effortMap.effortOnDate(d).minutes())/60.0; + item->setSlaveItem(col, eff); + item->addToTotal(eff); + } + } + calculate(); + QApplication::restoreOverrideCursor(); +} + + +void TaskAppointmentsView::print(KPrinter &/*printer*/) { + kdDebug()<<k_funcinfo<<endl; +} + +// bool TaskAppointmentsView::setContext(Context::TaskAppointmentsView &context) { +// //kdDebug()<<k_funcinfo<<endl; +// +// QValueList<int> list; +// list << context.accountsviewsize << context.periodviewsize; +// m_dlv->setSizes(list); +// m_date = context.date; +// if (!m_date.isValid()) +// m_date = QDate::currentDate(); +// m_period = context.period; +// m_cumulative = context.cumulative; +// +// return true; +// } +// +// void TaskAppointmentsView::getContext(Context::TaskAppointmentsView &context) const { +// //kdDebug()<<k_funcinfo<<endl; +// context.accountsviewsize = m_dlv->sizes()[0]; +// context.periodviewsize = m_dlv->sizes()[1]; +// context.date = m_date; +// context.period = m_period; +// context.cumulative = m_cumulative; +// //kdDebug()<<k_funcinfo<<"sizes="<<sizes()[0]<<","<<sizes()[1]<<endl; +// } + +void TaskAppointmentsView::clear() { + clearLists(); +} + +void TaskAppointmentsView::createSlaveItems() { + DoubleListViewBase::createSlaveItems(); + setSlaveFormat(0, 'f', 1); +} + +} //KPlato namespace + +#include "kpttaskappointmentsview.moc" diff --git a/kplato/kpttaskappointmentsview.h b/kplato/kpttaskappointmentsview.h new file mode 100644 index 00000000..b2c75f37 --- /dev/null +++ b/kplato/kpttaskappointmentsview.h @@ -0,0 +1,95 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Dag Andersen <danders@get2net.dk> + + 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 KPTTASKAPPOINTMENTSVIEW_H +#define KPTTASKAPPOINTMENTSVIEW_H + +#include "kptcontext.h" +#include "kptdoublelistviewbase.h" +#include "kpteffortcostmap.h" + +class QComboBox; +class QDateEdit; +class QPushButton; +class QSplitter; +class QListViewItem; +class QLabel; +class QPushButton; + +class KListView; +class KListViewItem; +class KPrinter; + +namespace KPlato +{ + +class Project; +class Resource; +class Task; + +class ResourceGroup; +class Resource; +class ResourceItemPrivate; + +class TaskAppointmentsView : public DoubleListViewBase +{ + Q_OBJECT +public: + + TaskAppointmentsView(QWidget *parent); + + //~TaskAppointmentsView(); + + void zoom(double zoom); + + void draw(); + void draw(Task *task); + virtual void drawContents(QPainter* painter); + void print(KPrinter &printer); + void clear(); + + //virtual bool setContext(Context::TaskAppointmentsView &context); + //virtual void getContext(Context::TaskAppointmentsView &context) const; + + virtual void createSlaveItems(); + +protected slots: + void slotUpdate(); + +private: + class ResourceItem : public DoubleListViewBase::MasterListItem { + public: + ResourceItem(Resource *r, QListView *parent, bool highlight=false); + ResourceItem(Resource *r, QListViewItem *parent, bool highlight=false); + ResourceItem(QString text, QListViewItem *parent, bool highlight=false); + + Resource *resource; + EffortCostMap effortMap; + }; + +private: + + int m_defaultFontSize; + Task *m_task; +}; + +} //KPlato namespace + + +#endif // KPTTASKAPPOINTMENTSVIEW_H diff --git a/kplato/kpttaskappointmentsview.ui.h b/kplato/kpttaskappointmentsview.ui.h new file mode 100644 index 00000000..5fb80e1a --- /dev/null +++ b/kplato/kpttaskappointmentsview.ui.h @@ -0,0 +1,114 @@ +/**************************************************************************** +** ui.h extension file, included from the uic-generated form implementation. +** +** If you wish to add, delete or rename functions or slots use +** Qt Designer which will update this file, preserving your code. Create an +** init() function in place of a constructor, and a destroy() function in +** place of a destructor. +*****************************************************************************/ + +/* This file is part of the KDE project + Copyright (C) 2004 - 2005 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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. +*/ + + +#include <kdebug.h> + +namespace KPlato +{ + +void TaskAppointmentsView::clear() +{ + if (m_appList) m_appList->clear(); + if (m_taskName) m_taskName->clear(); + if (m_plannedCost) m_plannedCost->clear(); + if (m_plannedCostTotal) m_plannedCostTotal->clear(); + if (m_actualCost) m_actualCost->clear(); + if (m_plannedEffort) m_plannedEffort->clear(); + if (m_plannedEffortTotal) m_plannedEffortTotal->clear(); + if (m_epi) m_epi->clear(); + if (m_cpi) m_cpi->clear(); +} + +void TaskAppointmentsView::draw(Task *task) +{ + //kdDebug()<<k_funcinfo<<endl; + m_task = task; + clear(); + if (!task) + return; + m_taskName->setText(task->name()); + + QPtrListIterator<Appointment> it(task->appointments()); + for (; it.current(); ++it) { + Resource *r = it.current()->resource(); + QListViewItem *item = new QListViewItem(m_appList, r->name()); + int i = 1; + item->setText(i++, r->typeToString()); + item->setText(i++, it.current()->startTime().date().toString(ISODate)); + item->setText(i++, it.current()->endTime().date().toString(ISODate)); + item->setText(i++, it.current()->plannedEffort().toString(Duration::Format_HourFraction)); + item->setText(i++, KGlobal::locale()->formatMoney(r->normalRate())); + item->setText(i++, KGlobal::locale()->formatMoney(r->overtimeRate())); + item->setText(i++, KGlobal::locale()->formatMoney(r->fixedCost())); + QPtrListIterator<AppointmentInterval> ait = it.current()->intervals(); + for (; ait.current(); ++ait) { + QListViewItem *sub = new QListViewItem(item, ""); + i = 1; + sub->setText(i++, ""); + sub->setText(i++, ait.current()->startTime().date().toString(ISODate)); + sub->setText(i++, ait.current()->endTime().date().toString(ISODate)); + sub->setText(i++, ait.current()->effort().toString(Duration::Format_HourFraction)); + } + + } + drawCostEffort(); +} + + +void TaskAppointmentsView::init() +{ + m_appList->setColumnAlignment(1, AlignHCenter); + m_appList->setColumnAlignment(3, AlignRight); + m_appList->setColumnAlignment(4, AlignRight); + m_appList->setColumnAlignment(5, AlignRight); + m_appList->setColumnAlignment(6, AlignRight); + + m_task = 0; + m_date->setDate(QDate::currentDate()); + +} + +void TaskAppointmentsView::drawCostEffort() +{ + if (m_task == 0) + return; + m_actualCost->setText(KGlobal::locale()->formatMoney(m_task->actualCostTo(m_date->date()))); + m_plannedCost->setText(KGlobal::locale()->formatMoney(m_task->plannedCostTo(m_date->date()))); + m_plannedCostTotal->setText(KGlobal::locale()->formatMoney(m_task->plannedCost())); + + m_actualEffort->setText(m_task->actualEffortTo(m_date->date()).toString(Duration::Format_HourFraction)); + m_plannedEffort->setText(m_task->plannedEffortTo(m_date->date()).toString(Duration::Format_HourFraction)); + m_plannedEffortTotal->setText(m_task->plannedEffort().toString(Duration::Format_HourFraction)); + + m_epi->setText(QString("%1").arg(m_task->effortPerformanceIndex(m_date->date()),3,'f',2)); + m_cpi->setText(QString("%1").arg(m_task->costPerformanceIndex(m_date->date()),3,'f',2)); + +} + +} // KPlato namespace diff --git a/kplato/kpttaskcostpanel.cc b/kplato/kpttaskcostpanel.cc new file mode 100644 index 00000000..742ae2e4 --- /dev/null +++ b/kplato/kpttaskcostpanel.cc @@ -0,0 +1,151 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library Cost Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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 Cost Public License for more details. + + You should have received a copy of the GNU Library Cost 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. +*/ + +#include "kpttaskcostpanel.h" +#include "kptaccount.h" +#include "kpttask.h" +#include "kptcommand.h" +#include "kptpart.h" + +#include <kmessagebox.h> +#include <klineedit.h> +#include <kcombobox.h> +#include <klocale.h> +#include <kcommand.h> + +#include <kdebug.h> + +namespace KPlato +{ + +TaskCostPanel::TaskCostPanel(Task &task, Accounts &accounts, QWidget *p, const char *n) + : TaskCostPanelImpl(p, n), + m_task(task), + m_accounts(accounts) { + + m_accountList << i18n("None"); + m_accountList += accounts.costElements(); + setStartValues(task); +} + +void TaskCostPanel::setStartValues(Task &task) { + runningAccount->insertStringList(m_accountList); + m_oldrunning = m_accounts.findRunningAccount(task); + if (m_oldrunning) { + setCurrentItem(runningAccount, m_oldrunning->name()); + } + + startupCost->setText(KGlobal::locale()->formatMoney(task.startupCost())); + startupAccount->insertStringList(m_accountList); + m_oldstartup = m_accounts.findStartupAccount(task); + if (m_oldstartup) { + setCurrentItem(startupAccount, m_oldstartup->name()); + } + + shutdownCost->setText(KGlobal::locale()->formatMoney(task.shutdownCost())); + shutdownAccount->insertStringList(m_accountList); + m_oldshutdown = m_accounts.findShutdownAccount(task); + if (m_oldshutdown) { + setCurrentItem(shutdownAccount, m_oldshutdown->name()); + } +} + +void TaskCostPanel::setCurrentItem(QComboBox *box, QString name) { + box->setCurrentItem(0); + for (int i = 0; i < box->count(); ++i) { + if (name == box->text(i)) { + box->setCurrentItem(i); + break; + } + } +} + +KCommand *TaskCostPanel::buildCommand(Part *part) { + KMacroCommand *cmd = new KMacroCommand(i18n("Modify Task Cost")); + bool modified = false; + + if ((m_oldrunning == 0 && runningAccount->currentItem() != 0) || + (m_oldrunning && m_oldrunning->name() != runningAccount->currentText())) { + cmd->addCommand(new NodeModifyRunningAccountCmd(part, m_task, m_oldrunning, m_accounts.findAccount(runningAccount->currentText()))); + modified = true; + } + if ((m_oldstartup == 0 && startupAccount->currentItem() != 0) || + (m_oldstartup && m_oldstartup->name() != startupAccount->currentText())) { + cmd->addCommand(new NodeModifyStartupAccountCmd(part, m_task, m_oldstartup, m_accounts.findAccount(startupAccount->currentText()))); + modified = true; + } + if ((m_oldshutdown == 0 && shutdownAccount->currentItem() != 0) || + (m_oldshutdown && m_oldshutdown->name() != shutdownAccount->currentText())) { + cmd->addCommand(new NodeModifyShutdownAccountCmd(part, m_task, m_oldshutdown, m_accounts.findAccount(shutdownAccount->currentText()))); + modified = true; + } + double money = KGlobal::locale()->readMoney(startupCost->text()); + if (money != m_task.startupCost()) { + cmd->addCommand(new NodeModifyStartupCostCmd(part, m_task, money)); + modified = true; + } + money = KGlobal::locale()->readMoney(shutdownCost->text()); + if (money != m_task.shutdownCost()) { + cmd->addCommand(new NodeModifyShutdownCostCmd(part, m_task, money)); + modified = true; + } + if (!modified) { + delete cmd; + return 0; + } + return cmd; +} + +bool TaskCostPanel::ok() { + if (runningAccount->currentItem() == 0 || + m_accounts.findAccount(runningAccount->currentText()) == 0) { + //message + return false; + } + if (startupAccount->currentItem() == 0 || + m_accounts.findAccount(startupAccount->currentText()) == 0) { + //message + return false; + } + if (shutdownAccount->currentItem() == 0 || + m_accounts.findAccount(shutdownAccount->currentText()) == 0) { + //message + return false; + } + return true; +} + + +TaskCostPanelImpl::TaskCostPanelImpl(QWidget *p, const char *n) + : TaskCostPanelBase(p, n) +{ + connect(runningAccount, SIGNAL(activated(int)), SLOT(slotChanged())); + connect(startupAccount, SIGNAL(activated(int)), SLOT(slotChanged())); + connect(shutdownAccount, SIGNAL(activated(int)), SLOT(slotChanged())); + connect(startupCost, SIGNAL(textChanged(const QString&)), SLOT(slotChanged())); + connect(shutdownCost, SIGNAL(textChanged(const QString&)), SLOT(slotChanged())); +} + +void TaskCostPanelImpl::slotChanged() { + emit changed(); +} + +} //KPlato namespace + +#include "kpttaskcostpanel.moc" diff --git a/kplato/kpttaskcostpanel.h b/kplato/kpttaskcostpanel.h new file mode 100644 index 00000000..0f4cc051 --- /dev/null +++ b/kplato/kpttaskcostpanel.h @@ -0,0 +1,73 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library Cost Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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 Cost Public License for more details. + + You should have received a copy of the GNU Library Cost 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 KPTTASKCOSTPANEL_H +#define KPTTASKCOSTPANEL_H + +#include "kpttaskcostpanelbase.h" + +class KCommand; + +namespace KPlato +{ + +class TaskCostPanel; +class Account; +class Accounts; +class Part; +class Task; + +class TaskCostPanelImpl : public TaskCostPanelBase { + Q_OBJECT +public: + TaskCostPanelImpl(QWidget *parent=0, const char *name=0); + +signals: + void changed(); + +public slots: + void slotChanged(); +}; + +class TaskCostPanel : public TaskCostPanelImpl { + Q_OBJECT +public: + TaskCostPanel(Task &task, Accounts &accounts, QWidget *parent=0, const char *name=0); + + KCommand *buildCommand(Part *part); + + bool ok(); + + void setStartValues(Task &task); + +protected: + void setCurrentItem(QComboBox *box, QString name); + +private: + Task &m_task; + Accounts &m_accounts; + QStringList m_accountList; + Account *m_oldrunning; + Account *m_oldstartup; + Account *m_oldshutdown; +}; + +} //KPlato namespace + +#endif // TASKCOSTPANEL_H diff --git a/kplato/kpttaskcostpanelbase.ui b/kplato/kpttaskcostpanelbase.ui new file mode 100644 index 00000000..5c1303aa --- /dev/null +++ b/kplato/kpttaskcostpanelbase.ui @@ -0,0 +1,231 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>KPlato::TaskCostPanelBase</class> +<author>Dag Andersen <danders@get2net.dk></author> +<widget class="QWidget"> + <property name="name"> + <cstring>TaskCostPanelBase</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>280</width> + <height>286</height> + </rect> + </property> + <property name="caption"> + <string>TaskCostPanelBase</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBox3</cstring> + </property> + <property name="title"> + <string>Running</string> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel3_2_2</cstring> + </property> + <property name="text"> + <string>Account:</string> + </property> + </widget> + <widget class="QComboBox"> + <property name="name"> + <cstring>runningAccount</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </hbox> + </widget> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBox1</cstring> + </property> + <property name="title"> + <string>Startup</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KLineEdit" row="0" column="1"> + <property name="name"> + <cstring>startupCost</cstring> + </property> + </widget> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>Cost:</string> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel3</cstring> + </property> + <property name="text"> + <string>Account:</string> + </property> + </widget> + <widget class="QComboBox" row="1" column="1"> + <property name="name"> + <cstring>startupAccount</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + <spacer row="0" column="2"> + <property name="name"> + <cstring>spacer3_2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </grid> + </widget> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBox2</cstring> + </property> + <property name="title"> + <string>Shutdown</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel3_3</cstring> + </property> + <property name="text"> + <string>Account:</string> + </property> + </widget> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel2</cstring> + </property> + <property name="text"> + <string>Cost:</string> + </property> + </widget> + <widget class="QComboBox" row="1" column="1"> + <property name="name"> + <cstring>shutdownAccount</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + <widget class="KLineEdit" row="0" column="1"> + <property name="name"> + <cstring>shutdownCost</cstring> + </property> + </widget> + <spacer row="1" column="2"> + <property name="name"> + <cstring>spacer4</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </grid> + </widget> + <spacer> + <property name="name"> + <cstring>spacer3</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </vbox> +</widget> +<customwidgets> +</customwidgets> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>klineedit.h</includehint> + <includehint>klineedit.h</includehint> +</includehints> +</UI> diff --git a/kplato/kpttaskdefaultpanel.cc b/kplato/kpttaskdefaultpanel.cc new file mode 100644 index 00000000..a76249d3 --- /dev/null +++ b/kplato/kpttaskdefaultpanel.cc @@ -0,0 +1,515 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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. +*/ + +#include "kpttaskdefaultpanel.h" +#include "kpttask.h" +#include "kptcommand.h" +#include "kptduration.h" +#include "kptdurationwidget.h" +#include "kptcalendar.h" +#include "kptdatetime.h" +#include "kptconfig.h" +#include "kptpart.h" + +#include <kmessagebox.h> +#include <klineedit.h> +#include <ktextedit.h> +#include <kcombobox.h> +#include <kdatetimewidget.h> +#include <klocale.h> +#include <kcommand.h> +#include <kabc/addressee.h> +#include <kabc/addresseedialog.h> +#include <kdatewidget.h> + +#include <qlayout.h> +#include <qlabel.h> +#include <qdatetime.h> +#include <qdatetimeedit.h> +#include <qgroupbox.h> +#include <qpushbutton.h> +#include <qspinbox.h> + +#include <kdebug.h> + +namespace KPlato +{ + +TaskDefaultPanel::TaskDefaultPanel(Task &task, StandardWorktime *workTime, QWidget *parent, const char *n) + : ConfigTaskPanelImpl(parent, n), + m_task(task), + m_dayLength(24) +{ + setStartValues(task, workTime); +} + +void TaskDefaultPanel::setStartValues(Task &task, StandardWorktime *workTime) { + m_effort = m_duration = task.effort()->expected(); + leaderfield->setText(task.leader()); + descriptionfield->setText(task.description()); + + setEstimateFields(DurationWidget::Days|DurationWidget::Hours|DurationWidget::Minutes); + if (workTime) { + //kdDebug()<<k_funcinfo<<"daylength="<<workTime->day()<<endl; + m_dayLength = workTime->day(); + if (task.effort()->type() == Effort::Type_Effort) { + setEstimateScales(m_dayLength); + } + } + setEstimateFieldUnit(0, i18n("days", "d")); + setEstimateFieldUnit(1, i18n("hours", "h")); + setEstimateFieldUnit(2, i18n("minutes", "m")); + setEstimateType(task.effort()->type()); + + setSchedulingType(task.constraint()); + if (task.constraintStartTime().isValid()) { + setStartDateTime(task.constraintStartTime()); + } else { + QDate date = QDate::currentDate(); + setStartDateTime(QDateTime(date, QTime())); + } + if (task.constraintEndTime().isValid()) { + setEndDateTime(task.constraintEndTime()); + } else { + setEndDateTime(QDateTime(startDate().addDays(1), QTime())); + } + //kdDebug()<<k_funcinfo<<"Effort: "<<task.effort()->expected().toString()<<endl; + setEstimate(task.effort()->expected()); + setOptimistic(task.effort()->optimisticRatio()); + setPessimistic(task.effort()->pessimisticRatio()); + + leaderfield->setFocus(); +} + +KMacroCommand *TaskDefaultPanel::buildCommand(Part *part) { + KMacroCommand *cmd = new KMacroCommand(i18n("Modify Default Task")); + bool modified = false; + + Duration dt = Duration(); + + if (m_task.leader() != leaderfield->text()) { + cmd->addCommand(new NodeModifyLeaderCmd(part, m_task, leaderfield->text())); + modified = true; + } + if (m_task.description() != descriptionfield->text()) { + cmd->addCommand(new NodeModifyDescriptionCmd(part, m_task, descriptionfield->text())); + modified = true; + } + Node::ConstraintType c = (Node::ConstraintType)schedulingType(); + if (c != m_task.constraint()) { + cmd->addCommand(new NodeModifyConstraintCmd(part, m_task, c)); + modified = true; + } + if (startDateTime() != m_task.constraintStartTime() && + (c == Node::FixedInterval || c == Node::StartNotEarlier || c == Node::MustStartOn)) { + cmd->addCommand(new NodeModifyConstraintStartTimeCmd(part, m_task, startDateTime())); + modified = true; + } + if (endDateTime() != m_task.constraintEndTime() && + (c == Node::FinishNotLater || c == Node::FixedInterval || c == Node::MustFinishOn)) { + cmd->addCommand(new NodeModifyConstraintEndTimeCmd(part, m_task, endDateTime())); + modified = true; + } + int et = estimationType(); + if (et != m_task.effort()->type()) { + cmd->addCommand(new ModifyEffortTypeCmd(part, m_task, m_task.effort()->type(), et)); + modified = true; + } + dt = estimationValue(); + kdDebug()<<k_funcinfo<<"Estimate: "<<dt.toString()<<endl; + bool expchanged = dt != m_task.effort()->expected(); + if ( expchanged ) { + cmd->addCommand(new ModifyEffortCmd(part, m_task, m_task.effort()->expected(), dt)); + modified = true; + } + int x = optimistic(); + if ( x != m_task.effort()->optimisticRatio() || expchanged) { + cmd->addCommand(new EffortModifyOptimisticRatioCmd(part, m_task, m_task.effort()->optimisticRatio(), x)); + modified = true; + } + x = pessimistic(); + if ( x != m_task.effort()->pessimisticRatio() || expchanged) { + cmd->addCommand(new EffortModifyPessimisticRatioCmd(part, m_task, m_task.effort()->pessimisticRatio(), x)); + modified = true; + } + if (!modified) { + delete cmd; + return 0; + } + return cmd; +} + +bool TaskDefaultPanel::ok() { + return true; +} + +void TaskDefaultPanel::estimationTypeChanged(int type) { + if (type == 0 /*Effort*/) { + Duration d = estimationValue(); + setEstimateScales(m_dayLength); + //setEstimate(d); + } else { + Duration d = estimationValue(); + setEstimateScales(24); + //setEstimate(d); + } + ConfigTaskPanelImpl::estimationTypeChanged(type); +} + +void TaskDefaultPanel::scheduleTypeChanged(int value) +{ + if (value == 6 /*Fixed interval*/) { + if (estimateType->currentItem() == 1/*duration*/){ + setEstimateScales(24); + //estimate->setEnabled(false); + //setEstimate(DateTime(endDateTime()) - DateTime(startDateTime())); + } + } else { + setEstimateScales(m_dayLength); + estimate->setEnabled(true); + } + ConfigTaskPanelImpl::scheduleTypeChanged(value); +} + + +//----------------------------- +ConfigTaskPanelImpl::ConfigTaskPanelImpl(QWidget *p, const char *n) + : ConfigTaskPanelBase(p, n) { + + connect(leaderfield, SIGNAL(textChanged(const QString &)), SLOT(checkAllFieldsFilled())); + connect(chooseLeader, SIGNAL(clicked()), SLOT(changeLeader())); + connect(estimateType, SIGNAL(activated(int)), SLOT(estimationTypeChanged(int))); + connect(scheduleType, SIGNAL(activated(int)), SLOT(scheduleTypeChanged(int))); + connect(scheduleStartDate, SIGNAL(changed(QDate)), SLOT(startDateChanged())); + connect(scheduleStartTime, SIGNAL(valueChanged(const QTime&)), SLOT(startTimeChanged(const QTime&))); + connect(scheduleEndDate, SIGNAL(changed(QDate)), SLOT(endDateChanged())); + connect(scheduleEndTime, SIGNAL(valueChanged(const QTime&)), SLOT(endTimeChanged(const QTime&))); + connect(estimate, SIGNAL(valueChanged()), SLOT(checkAllFieldsFilled())); + connect(optimisticValue, SIGNAL(valueChanged(int)), SLOT(checkAllFieldsFilled())); + connect(pessimisticValue, SIGNAL(valueChanged(int)), SLOT(checkAllFieldsFilled())); + connect(descriptionfield, SIGNAL(textChanged()), SLOT(checkAllFieldsFilled())); +} + +void ConfigTaskPanelImpl::setSchedulingType(int type) +{ + enableDateTime(type); + scheduleType->setCurrentItem(type); + emit schedulingTypeChanged(type); +} + +int ConfigTaskPanelImpl::schedulingType() const +{ + return scheduleType->currentItem(); +} + +void ConfigTaskPanelImpl::changeLeader() +{ + KABC::Addressee a = KABC::AddresseeDialog::getAddressee(this); + if (!a.isEmpty()) + { + leaderfield->setText(a.fullEmail()); + } +} + +void ConfigTaskPanelImpl::setEstimationType( int type ) +{ + estimateType->setCurrentItem(type); +} + +int ConfigTaskPanelImpl::estimationType() const +{ + return estimateType->currentItem(); +} + +void ConfigTaskPanelImpl::setOptimistic( int value ) +{ + optimisticValue->setValue(value); +} + +void ConfigTaskPanelImpl::setPessimistic( int value ) +{ + pessimisticValue->setValue(value); +} + +int ConfigTaskPanelImpl::optimistic() const +{ + return optimisticValue->value(); +} + +int ConfigTaskPanelImpl::pessimistic() +{ + return pessimisticValue->value(); +} + +void ConfigTaskPanelImpl::enableDateTime( int /*scheduleType*/ ) +{ + scheduleStartTime->setEnabled(true); + scheduleEndTime->setEnabled(true); + scheduleStartDate->setEnabled(true); + scheduleEndDate->setEnabled(true); +/* switch (scheduleType) + { + case 0: //ASAP + case 1: //ALAP + break; + case 2: //Must start on + case 4: // Start not earlier + if (useTime) { + scheduleStartTime->setEnabled(true); + scheduleEndTime->setEnabled(false); + } + scheduleStartDate->setEnabled(true); + scheduleEndDate->setEnabled(false); + break; + case 3: //Must finish on + case 5: // Finish not later + if (useTime) { + scheduleStartTime->setEnabled(false); + scheduleEndTime->setEnabled(true); + } + scheduleStartDate->setEnabled(false); + scheduleEndDate->setEnabled(true); + break; + case 6: //Fixed interval + if (useTime) { + scheduleStartTime->setEnabled(true); + scheduleEndTime->setEnabled(true); + } + scheduleStartDate->setEnabled(true); + scheduleEndDate->setEnabled(true); + break; + default: + break; + }*/ +} + + +void ConfigTaskPanelImpl::estimationTypeChanged( int /*type*/ ) +{ + checkAllFieldsFilled(); +} + + + +void ConfigTaskPanelImpl::setEstimate( const Duration & duration) +{ + estimate->setValue( duration ); +} + + +void ConfigTaskPanelImpl::setEstimateType( int type) +{ + estimateType->setCurrentItem(type); +} + + +void ConfigTaskPanelImpl::checkAllFieldsFilled() +{ + emit changed(); + emit obligatedFieldsFilled(true); +} + + +Duration ConfigTaskPanelImpl::estimationValue() +{ + return estimate->value(); +} + + +void ConfigTaskPanelImpl::setEstimateFields( int mask ) +{ + estimate->setVisibleFields(mask); +} + + +void ConfigTaskPanelImpl::setEstimateScales( double day ) +{ + estimate->setFieldScale(0, day); + estimate->setFieldRightscale(0, day); + + estimate->setFieldLeftscale(1, day); +} + + +void ConfigTaskPanelImpl::setEstimateFieldUnit( int field, QString unit ) +{ + estimate->setFieldUnit(field, unit); +} + +void ConfigTaskPanelImpl::startDateChanged() +{ + if (!scheduleStartDate->isEnabled()) { + return; + } + QDate date = startDate(); + if (startDateTime() > endDateTime()) + { + scheduleEndTime->blockSignals(true); + scheduleEndDate->blockSignals(true); + setEndDate(date); + setEndTime(startTime()); + scheduleEndTime->blockSignals(false); + scheduleEndDate->blockSignals(false); + } + if (scheduleType->currentItem() == 6 /*FixedInterval*/) + { + estimationTypeChanged(estimateType->currentItem()); + } + checkAllFieldsFilled(); +} + +void ConfigTaskPanelImpl::startTimeChanged( const QTime &time ) +{ + if (!scheduleStartTime->isEnabled()) { + return; + } + if (startDateTime() > endDateTime()) + { + scheduleEndTime->blockSignals(true); + setEndTime(time); + scheduleEndTime->blockSignals(false); + } + if (scheduleType->currentItem() == 6 /*FixedInterval*/) + { + estimationTypeChanged(estimateType->currentItem()); + } + checkAllFieldsFilled(); +} + + +void ConfigTaskPanelImpl::endDateChanged() +{ + if (!scheduleEndDate->isEnabled()) { + return; + } + QDate date = endDate(); + if (endDateTime() < startDateTime()) + { + scheduleStartTime->blockSignals(true); + scheduleStartDate->blockSignals(true); + setStartDate(date); + setStartTime(endTime()); + scheduleStartTime->blockSignals(false); + scheduleStartDate->blockSignals(false); + } + + if (scheduleType->currentItem() == 6 /*FixedInterval*/) + { + estimationTypeChanged(estimateType->currentItem()); + } + checkAllFieldsFilled(); +} + +void ConfigTaskPanelImpl::endTimeChanged( const QTime &time ) +{ + if (!scheduleEndTime->isEnabled()) { + return; + } + if (endDateTime() < startDateTime()) + { + scheduleStartTime->blockSignals(true); + setStartTime(time); + scheduleStartTime->blockSignals(false); + } + + if (scheduleType->currentItem() == 6 /*FixedInterval*/) + { + estimationTypeChanged(estimateType->currentItem()); + } + checkAllFieldsFilled(); +} + +void ConfigTaskPanelImpl::scheduleTypeChanged( int value ) +{ + estimationTypeChanged(estimateType->currentItem()); + enableDateTime(value); + checkAllFieldsFilled(); +} + + +QDateTime ConfigTaskPanelImpl::startDateTime() +{ + return QDateTime(startDate(), startTime()); +} + + +QDateTime ConfigTaskPanelImpl::endDateTime() +{ + return QDateTime(endDate(), endTime()); +} + +void ConfigTaskPanelImpl::setStartTime( const QTime &time ) +{ + scheduleStartTime->setTime(time); +} + +void ConfigTaskPanelImpl::setEndTime( const QTime &time ) +{ + scheduleEndTime->setTime(time); +} + +QTime ConfigTaskPanelImpl::startTime() const +{ + return scheduleStartTime->time(); +} + +QTime ConfigTaskPanelImpl::endTime() +{ + return scheduleEndTime->time(); +} + +QDate ConfigTaskPanelImpl::startDate() +{ + return scheduleStartDate->date(); +} + + +QDate ConfigTaskPanelImpl::endDate() +{ + return scheduleEndDate->date(); +} + +void ConfigTaskPanelImpl::setStartDateTime( const QDateTime &dt ) +{ + setStartDate(dt.date()); + setStartTime(dt.time()); +} + + +void ConfigTaskPanelImpl::setEndDateTime( const QDateTime &dt ) +{ + setEndDate(dt.date()); + setEndTime(dt.time()); +} + +void ConfigTaskPanelImpl::setStartDate( const QDate &date ) +{ + scheduleStartDate->setDate(date); +} + + +void ConfigTaskPanelImpl::setEndDate( const QDate &date ) +{ + scheduleEndDate->setDate(date); +} + + +} //KPlato namespace + +#include "kpttaskdefaultpanel.moc" diff --git a/kplato/kpttaskdefaultpanel.h b/kplato/kpttaskdefaultpanel.h new file mode 100644 index 00000000..087f1e01 --- /dev/null +++ b/kplato/kpttaskdefaultpanel.h @@ -0,0 +1,113 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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 KPTTASKDEFAULTPANEL_H +#define KPTTASKDEFAULTPANEL_H + +#include "kptconfigtaskpanelbase.h" +#include "kptduration.h" + +class KMacroCommand; + +namespace KPlato +{ + +class Part; +class Task; +class StandardWorktime; + +class ConfigTaskPanelImpl : public ConfigTaskPanelBase +{ + Q_OBJECT +public: + ConfigTaskPanelImpl(QWidget *parent, const char *name); + + virtual int schedulingType() const; + virtual int estimationType() const; + virtual int optimistic() const; + virtual int pessimistic(); + virtual Duration estimationValue(); + virtual QDateTime startDateTime(); + virtual QDateTime endDateTime(); + virtual QTime startTime() const; + virtual QTime endTime(); + virtual QDate startDate(); + virtual QDate endDate(); + +public slots: + virtual void setSchedulingType( int type ); + virtual void changeLeader(); + virtual void setEstimationType( int type ); + virtual void setOptimistic( int value ); + virtual void setPessimistic( int value ); + virtual void enableDateTime( int scheduleType ); + virtual void estimationTypeChanged( int type ); + virtual void setEstimate( const Duration & duration ); + virtual void setEstimateType( int type ); + virtual void checkAllFieldsFilled(); + virtual void setEstimateFields( int mask ); + virtual void setEstimateScales( double day ); + virtual void setEstimateFieldUnit( int field, QString unit ); + virtual void startDateChanged(); + virtual void startTimeChanged( const QTime & time ); + virtual void endDateChanged(); + virtual void endTimeChanged( const QTime & time ); + virtual void scheduleTypeChanged( int value ); + virtual void setStartTime( const QTime & time ); + virtual void setEndTime( const QTime & time ); + virtual void setStartDateTime( const QDateTime & dt ); + virtual void setEndDateTime( const QDateTime & dt ); + virtual void setStartDate( const QDate & date ); + virtual void setEndDate( const QDate & date ); + +signals: + void obligatedFieldsFilled( bool ); + void schedulingTypeChanged( int ); + void changed(); + +protected: + bool useTime; +}; + +class TaskDefaultPanel : public ConfigTaskPanelImpl { + Q_OBJECT +public: + TaskDefaultPanel(Task &task, StandardWorktime *workTime=0, QWidget *parent=0, const char *name=0); + + KMacroCommand *buildCommand(Part *part); + + bool ok(); + + void setStartValues(Task &task, StandardWorktime *workTime=0); + +public slots: + virtual void estimationTypeChanged(int type); + virtual void scheduleTypeChanged(int value); + +private: + Task &m_task; + double m_dayLength; + + Duration m_effort; + Duration m_duration; +}; + +} //KPlato namespace + +#endif // TASKDEFAULTPANEL_H diff --git a/kplato/kpttaskdialog.cc b/kplato/kpttaskdialog.cc new file mode 100644 index 00000000..36ab2022 --- /dev/null +++ b/kplato/kpttaskdialog.cc @@ -0,0 +1,96 @@ +/* This file is part of the KDE project + Copyright (C) 2002 Bo Thorsen bo@sonofthor.dk + Copyright (C) 2004 Dag Andersen <danders@get2net.dk> + + 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. +*/ + +#include "kpttaskdialog.h" +#include "kpttaskcostpanel.h" +#include "kpttaskgeneralpanel.h" +#include "kptrequestresourcespanel.h" + +#include <klocale.h> +#include <kcommand.h> + +#include <qvbox.h> +#include <kdebug.h> + +namespace KPlato +{ + +TaskDialog::TaskDialog(Task &task, Accounts &accounts, StandardWorktime *workTime, bool baseline, QWidget *p) + : KDialogBase(Tabbed, i18n("Task Settings"), Ok|Cancel, Ok, p, "Task Settings Dialog", true, true) +{ + QVBox *page; + + // Create all the tabs. + page = addVBoxPage(i18n("&General")); + m_generalTab = new TaskGeneralPanel(task, workTime, baseline, page); + + page = addVBoxPage(i18n("&Resources")); + m_resourcesTab = new RequestResourcesPanel(page, task, baseline); + + page = addVBoxPage(i18n("&Cost")); + m_costTab = new TaskCostPanel(task, accounts, page); + + // Set the state of all the child widgets. + enableButtonOK(false); + + connect(m_generalTab, SIGNAL( obligatedFieldsFilled(bool) ), this, SLOT( enableButtonOK(bool) )); + connect(m_resourcesTab, SIGNAL( changed() ), m_generalTab, SLOT( checkAllFieldsFilled() )); + connect(m_costTab, SIGNAL( changed() ), m_generalTab, SLOT( checkAllFieldsFilled() )); +} + + +KCommand *TaskDialog::buildCommand(Part *part) { + KMacroCommand *m = new KMacroCommand(i18n("Modify Task")); + bool modified = false; + KCommand *cmd = m_generalTab->buildCommand(part); + if (cmd) { + m->addCommand(cmd); + modified = true; + } + cmd = m_resourcesTab->buildCommand(part); + if (cmd) { + m->addCommand(cmd); + modified = true; + } + cmd = m_costTab->buildCommand(part); + if (cmd) { + m->addCommand(cmd); + modified = true; + } + if (!modified) { + delete m; + return 0; + } + return m; +} + +void TaskDialog::slotOk() { + if (!m_generalTab->ok()) + return; + if (!m_resourcesTab->ok()) + return; + + accept(); +} + + +} //KPlato namespace + +#include "kpttaskdialog.moc" diff --git a/kplato/kpttaskdialog.h b/kplato/kpttaskdialog.h new file mode 100644 index 00000000..26a08caa --- /dev/null +++ b/kplato/kpttaskdialog.h @@ -0,0 +1,83 @@ +/* This file is part of the KDE project + Copyright (C) 2002 Bo Thorsen bo@sonofthor.dk + Copyright (C) 2004 Dag Andersen <danders@get2net.dk> + + 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 KPTTASKDIALOG_H +#define KPTTASKDIALOG_H + +#include <kdialogbase.h> + +class Duration; + +class KLineEdit; +class KCommand; +class KTextEdit; +class KComboBox; +class KDoubleNumInput; + +class QDateTimeEdit; +class QSpinBox; +class QButtonGroup; +class QListBox; +class QTable; +class QDateTime; + +//TODO ui files are not in the KPlato namespace!! + +namespace KPlato +{ + +class Accounts; +class TaskGeneralPanel; +class RequestResourcesPanel; +class TaskCostPanel; +class Part; +class Task; +class StandardWorktime; + +/** + * The dialog that shows and allows you to alter any task. + */ +class TaskDialog : public KDialogBase { + Q_OBJECT +public: + /** + * The constructor for the task settings dialog. + * @param task the task to show + * @param accounts all defined accounts + * @param workTime defines the number of hours pr day and week + * @param baseline if true, project is baselined + * @param parent parent widget + */ + TaskDialog(Task &task, Accounts &accounts, StandardWorktime *workTime=0, bool baseline=false, QWidget *parent=0); + + KCommand *buildCommand(Part *part); + +protected slots: + void slotOk(); + +private: + TaskGeneralPanel *m_generalTab; + RequestResourcesPanel *m_resourcesTab; + TaskCostPanel *m_costTab; +}; + +} //KPlato namespace + +#endif // TASKDIALOG_H diff --git a/kplato/kpttaskgeneralpanel.cc b/kplato/kpttaskgeneralpanel.cc new file mode 100644 index 00000000..53b7e60b --- /dev/null +++ b/kplato/kpttaskgeneralpanel.cc @@ -0,0 +1,566 @@ +/* This file is part of the KDE project + Copyright (C) 2004 - 2006 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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. +*/ + +#include "kpttaskgeneralpanel.h" +#include "kpttaskdialog.h" +#include "kpttask.h" +#include "kptcommand.h" +#include "kptduration.h" +#include "kptdurationwidget.h" +#include "kptcalendar.h" +#include "kptdatetime.h" +#include "kptconfig.h" +#include "kptpart.h" + +#include <kmessagebox.h> +#include <klineedit.h> +#include <ktextedit.h> +#include <kcombobox.h> +#include <kdatetimewidget.h> +#include <klocale.h> +#include <kcommand.h> +#include <kabc/addressee.h> +#include <kabc/addresseedialog.h> +#include <kdatewidget.h> + +#include <qlayout.h> +#include <qlabel.h> +#include <qdatetime.h> +#include <qdatetimeedit.h> +#include <qgroupbox.h> +#include <qpushbutton.h> +#include <qspinbox.h> + +#include <kdebug.h> + +namespace KPlato +{ + +TaskGeneralPanel::TaskGeneralPanel(Task &task, StandardWorktime *workTime, bool /*baseline*/, QWidget *p, const char *n) + : TaskGeneralPanelImpl(p, n), + m_task(task), + m_dayLength(24) +{ + useTime = true; + setStartValues(task, workTime); +/* Why is this done? Its useless (its not actually read only, but that may be a Qt thing) and I have to + edit these to actually be able to OK the dialog. TZ-8-2005 + namefield->setReadOnly(baseline); + leaderfield->setReadOnly(baseline); + idfield->setReadOnly(baseline); + schedulingGroup->setEnabled(!baseline); +*/ +} + +void TaskGeneralPanel::setStartValues(Task &task, StandardWorktime *workTime) { + m_effort = m_duration = task.effort()->expected(); + namefield->setText(task.name()); + leaderfield->setText(task.leader()); + descriptionfield->setText(task.description()); + idfield->setText(task.id()); + wbsfield->setText(task.wbs()); + + setEstimateFields(DurationWidget::Days|DurationWidget::Hours|DurationWidget::Minutes); + if (workTime) { + //kdDebug()<<k_funcinfo<<"daylength="<<workTime->day()<<endl; + m_dayLength = workTime->day(); + if (task.effort()->type() == Effort::Type_Effort) { + setEstimateScales(m_dayLength); + } + } + setEstimateFieldUnit(0, i18n("days", "d")); + setEstimateFieldUnit(1, i18n("hours", "h")); + setEstimateFieldUnit(2, i18n("minutes", "m")); + setEstimateType(task.effort()->type()); + + setSchedulingType(task.constraint()); + if (task.constraintStartTime().isValid()) { + setStartDateTime(task.constraintStartTime()); + } else { + QDate date = QDate::currentDate(); + setStartDateTime(QDateTime(date, QTime())); + } + if (task.constraintEndTime().isValid()) { + setEndDateTime(task.constraintEndTime()); + } else { + setEndDateTime(QDateTime(startDate().addDays(1), QTime())); + } + //kdDebug()<<k_funcinfo<<"Effort: "<<task.effort()->expected().toString()<<endl; + setEstimate(task.effort()->expected()); + setOptimistic(task.effort()->optimisticRatio()); + setPessimistic(task.effort()->pessimisticRatio()); + setRisktype(task.effort()->risktype()); + namefield->setFocus(); +} + +KMacroCommand *TaskGeneralPanel::buildCommand(Part *part) { + KMacroCommand *cmd = new KMacroCommand(i18n("Modify Task")); + bool modified = false; + + Duration dt = Duration(); + + if (!namefield->isHidden() && m_task.name() != namefield->text()) { + cmd->addCommand(new NodeModifyNameCmd(part, m_task, namefield->text())); + modified = true; + } + if (!leaderfield->isHidden() && m_task.leader() != leaderfield->text()) { + cmd->addCommand(new NodeModifyLeaderCmd(part, m_task, leaderfield->text())); + modified = true; + } + if (!descriptionfield->isHidden() && + m_task.description() != descriptionfield->text()) { + cmd->addCommand(new NodeModifyDescriptionCmd(part, m_task, descriptionfield->text())); + modified = true; + } + Node::ConstraintType c = (Node::ConstraintType)schedulingType(); + if (c != m_task.constraint()) { + cmd->addCommand(new NodeModifyConstraintCmd(part, m_task, c)); + modified = true; + } + if (startDateTime() != m_task.constraintStartTime() && + (c == Node::FixedInterval || c == Node::StartNotEarlier || c == Node::MustStartOn)) { + cmd->addCommand(new NodeModifyConstraintStartTimeCmd(part, m_task, startDateTime())); + modified = true; + } + if (endDateTime() != m_task.constraintEndTime() && + (c == Node::FinishNotLater || c == Node::FixedInterval || c == Node::MustFinishOn)) { + cmd->addCommand(new NodeModifyConstraintEndTimeCmd(part, m_task, endDateTime())); + modified = true; + } + if (!idfield->isHidden() && idfield->text() != m_task.id()) { + + cmd->addCommand(new NodeModifyIdCmd(part, m_task, idfield->text())); + modified = true; + } + int et = estimationType(); + if (et != m_task.effort()->type()) { + cmd->addCommand(new ModifyEffortTypeCmd(part, m_task, m_task.effort()->type(), et)); + modified = true; + } + dt = estimationValue(); + kdDebug()<<k_funcinfo<<"Estimate: "<<dt.toString()<<endl; + bool expchanged = dt != m_task.effort()->expected(); + if ( expchanged ) { + cmd->addCommand(new ModifyEffortCmd(part, m_task, m_task.effort()->expected(), dt)); + modified = true; + } + int x = optimistic(); + if ( x != m_task.effort()->optimisticRatio() || expchanged) { + cmd->addCommand(new EffortModifyOptimisticRatioCmd(part, m_task, m_task.effort()->optimisticRatio(), x)); + modified = true; + } + x = pessimistic(); + if ( x != m_task.effort()->pessimisticRatio() || expchanged) { + cmd->addCommand(new EffortModifyPessimisticRatioCmd(part, m_task, m_task.effort()->pessimisticRatio(), x)); + modified = true; + } + if (m_task.effort()->risktype() != risktype()) { + cmd->addCommand(new EffortModifyRiskCmd(part, m_task, m_task.effort()->risktype(), risktype())); + modified = true; + } + if (!modified) { + delete cmd; + return 0; + } + return cmd; +} + +bool TaskGeneralPanel::ok() { + if (idfield->text() != m_task.id() && m_task.findNode(idfield->text())) { + KMessageBox::sorry(this, i18n("Task id must be unique")); + idfield->setFocus(); + return false; + } + return true; +} + +void TaskGeneralPanel::estimationTypeChanged(int type) { + if (type == 0 /*Effort*/) { + Duration d = estimationValue(); + setEstimateScales(m_dayLength); + //setEstimate(d); + estimate->setEnabled(true); + } else { + Duration d = estimationValue(); + setEstimateScales(24); + //setEstimate(d); + if (schedulingType() == 6) { /*Fixed interval*/ + estimate->setEnabled(false); + } else { + estimate->setEnabled(true); + } + + } + TaskGeneralPanelImpl::estimationTypeChanged(type); +} + +void TaskGeneralPanel::scheduleTypeChanged(int value) +{ + if (value == 6 /*Fixed interval*/) { + if (estimateType->currentItem() == 1/*duration*/){ + setEstimateScales(24); + estimate->setEnabled(false); + setEstimate(DateTime(endDateTime()) - DateTime(startDateTime())); + } + } else { + setEstimateScales(m_dayLength); + estimate->setEnabled(true); + } + TaskGeneralPanelImpl::scheduleTypeChanged(value); +} + +//----------------------------- +TaskGeneralPanelImpl::TaskGeneralPanelImpl(QWidget *p, const char *n) + : TaskGeneralPanelBase(p, n) { + + connect(idfield, SIGNAL(textChanged(const QString &)), SLOT(checkAllFieldsFilled())); + connect(namefield, SIGNAL(textChanged(const QString &)), SLOT(checkAllFieldsFilled())); + connect(leaderfield, SIGNAL(textChanged(const QString &)), SLOT(checkAllFieldsFilled())); + connect(chooseLeader, SIGNAL(clicked()), SLOT(changeLeader())); + connect(estimateType, SIGNAL(activated(int)), SLOT(estimationTypeChanged(int))); + connect(scheduleType, SIGNAL(activated(int)), SLOT(scheduleTypeChanged(int))); + connect(scheduleStartDate, SIGNAL(changed(QDate)), SLOT(startDateChanged())); + connect(scheduleStartTime, SIGNAL(valueChanged(const QTime&)), SLOT(startTimeChanged(const QTime&))); + connect(scheduleEndDate, SIGNAL(changed(QDate)), SLOT(endDateChanged())); + connect(scheduleEndTime, SIGNAL(valueChanged(const QTime&)), SLOT(endTimeChanged(const QTime&))); + connect(estimate, SIGNAL(valueChanged()), SLOT(checkAllFieldsFilled())); + connect(optimisticValue, SIGNAL(valueChanged(int)), SLOT(checkAllFieldsFilled())); + connect(pessimisticValue, SIGNAL(valueChanged(int)), SLOT(checkAllFieldsFilled())); + connect(descriptionfield, SIGNAL(textChanged()), SLOT(checkAllFieldsFilled())); + connect(risk, SIGNAL(activated(int)), SLOT(checkAllFieldsFilled())); +} + +void TaskGeneralPanelImpl::setSchedulingType(int type) +{ + enableDateTime(type); + scheduleType->setCurrentItem(type); + emit schedulingTypeChanged(type); +} + +int TaskGeneralPanelImpl::schedulingType() const +{ + return scheduleType->currentItem(); +} + +void TaskGeneralPanelImpl::changeLeader() +{ + KABC::Addressee a = KABC::AddresseeDialog::getAddressee(this); + if (!a.isEmpty()) + { + leaderfield->setText(a.fullEmail()); + } +} + +void TaskGeneralPanelImpl::setEstimationType( int type ) +{ + estimateType->setCurrentItem(type); +} + +int TaskGeneralPanelImpl::estimationType() const +{ + return estimateType->currentItem(); +} + +void TaskGeneralPanelImpl::setOptimistic( int value ) +{ + optimisticValue->setValue(value); +} + +void TaskGeneralPanelImpl::setPessimistic( int value ) +{ + pessimisticValue->setValue(value); +} + +int TaskGeneralPanelImpl::optimistic() const +{ + return optimisticValue->value(); +} + +int TaskGeneralPanelImpl::pessimistic() +{ + return pessimisticValue->value(); +} + +void TaskGeneralPanelImpl::enableDateTime( int scheduleType ) +{ + scheduleStartTime->setEnabled(false); + scheduleEndTime->setEnabled(false); + scheduleStartDate->setEnabled(false); + scheduleEndDate->setEnabled(false); + switch (scheduleType) + { + case 0: //ASAP + case 1: //ALAP + break; + case 2: //Must start on + case 4: // Start not earlier + if (useTime) { + scheduleStartTime->setEnabled(true); + scheduleEndTime->setEnabled(false); + } + scheduleStartDate->setEnabled(true); + scheduleEndDate->setEnabled(false); + break; + case 3: //Must finish on + case 5: // Finish not later + if (useTime) { + scheduleStartTime->setEnabled(false); + scheduleEndTime->setEnabled(true); + } + scheduleStartDate->setEnabled(false); + scheduleEndDate->setEnabled(true); + break; + case 6: //Fixed interval + if (useTime) { + scheduleStartTime->setEnabled(true); + scheduleEndTime->setEnabled(true); + } + scheduleStartDate->setEnabled(true); + scheduleEndDate->setEnabled(true); + break; + default: + break; + } +} + + +void TaskGeneralPanelImpl::estimationTypeChanged( int /*type*/ ) +{ + checkAllFieldsFilled(); +} + + + +void TaskGeneralPanelImpl::setEstimate( const Duration & duration) +{ + estimate->setValue( duration ); +} + + +void TaskGeneralPanelImpl::setEstimateType( int type) +{ + estimateType->setCurrentItem(type); +} + + +void TaskGeneralPanelImpl::checkAllFieldsFilled() +{ + emit changed(); + emit obligatedFieldsFilled(!namefield->text().isEmpty() && !idfield->text().isEmpty()); +} + + +Duration TaskGeneralPanelImpl::estimationValue() +{ + return estimate->value(); +} + + +void TaskGeneralPanelImpl::setEstimateFields( int mask ) +{ + estimate->setVisibleFields(mask); +} + + +void TaskGeneralPanelImpl::setEstimateScales( double day ) +{ + estimate->setFieldScale(0, day); + estimate->setFieldRightscale(0, day); + + estimate->setFieldLeftscale(1, day); +} + + +void TaskGeneralPanelImpl::setEstimateFieldUnit( int field, QString unit ) +{ + estimate->setFieldUnit(field, unit); +} + +void TaskGeneralPanelImpl::startDateChanged() +{ + if (!scheduleStartDate->isEnabled()) { + return; + } + QDate date = startDate(); + if (startDateTime() > endDateTime()) + { + scheduleEndTime->blockSignals(true); + scheduleEndDate->blockSignals(true); + setEndDate(date); + setEndTime(startTime()); + scheduleEndTime->blockSignals(false); + scheduleEndDate->blockSignals(false); + } + if (scheduleType->currentItem() == 6 /*FixedInterval*/) + { + estimationTypeChanged(estimateType->currentItem()); + } + checkAllFieldsFilled(); +} + +void TaskGeneralPanelImpl::startTimeChanged( const QTime &time ) +{ + if (!scheduleStartTime->isEnabled()) { + return; + } + if (startDateTime() > endDateTime()) + { + scheduleEndTime->blockSignals(true); + setEndTime(time); + scheduleEndTime->blockSignals(false); + } + if (scheduleType->currentItem() == 6 /*FixedInterval*/) + { + estimationTypeChanged(estimateType->currentItem()); + } + checkAllFieldsFilled(); +} + + +void TaskGeneralPanelImpl::endDateChanged() +{ + if (!scheduleEndDate->isEnabled()) { + return; + } + QDate date = endDate(); + if (endDateTime() < startDateTime()) + { + scheduleStartTime->blockSignals(true); + scheduleStartDate->blockSignals(true); + setStartDate(date); + setStartTime(endTime()); + scheduleStartTime->blockSignals(false); + scheduleStartDate->blockSignals(false); + } + + if (scheduleType->currentItem() == 6 /*FixedInterval*/) + { + estimationTypeChanged(estimateType->currentItem()); + } + checkAllFieldsFilled(); +} + +void TaskGeneralPanelImpl::endTimeChanged( const QTime &time ) +{ + if (!scheduleEndTime->isEnabled()) { + return; + } + if (endDateTime() < startDateTime()) + { + scheduleStartTime->blockSignals(true); + setStartTime(time); + scheduleStartTime->blockSignals(false); + } + + if (scheduleType->currentItem() == 6 /*FixedInterval*/) + { + estimationTypeChanged(estimateType->currentItem()); + } + checkAllFieldsFilled(); +} + +void TaskGeneralPanelImpl::scheduleTypeChanged( int value ) +{ + estimationTypeChanged(estimateType->currentItem()); + enableDateTime(value); + checkAllFieldsFilled(); +} + + +QDateTime TaskGeneralPanelImpl::startDateTime() +{ + return QDateTime(startDate(), startTime()); +} + + +QDateTime TaskGeneralPanelImpl::endDateTime() +{ + return QDateTime(endDate(), endTime()); +} + +void TaskGeneralPanelImpl::setStartTime( const QTime &time ) +{ + scheduleStartTime->setTime(time); +} + +void TaskGeneralPanelImpl::setEndTime( const QTime &time ) +{ + scheduleEndTime->setTime(time); +} + +QTime TaskGeneralPanelImpl::startTime() const +{ + return scheduleStartTime->time(); +} + +QTime TaskGeneralPanelImpl::endTime() +{ + return scheduleEndTime->time(); +} + +QDate TaskGeneralPanelImpl::startDate() +{ + return scheduleStartDate->date(); +} + + +QDate TaskGeneralPanelImpl::endDate() +{ + return scheduleEndDate->date(); +} + +void TaskGeneralPanelImpl::setStartDateTime( const QDateTime &dt ) +{ + setStartDate(dt.date()); + setStartTime(dt.time()); +} + + +void TaskGeneralPanelImpl::setEndDateTime( const QDateTime &dt ) +{ + setEndDate(dt.date()); + setEndTime(dt.time()); +} + +void TaskGeneralPanelImpl::setStartDate( const QDate &date ) +{ + scheduleStartDate->setDate(date); +} + + +void TaskGeneralPanelImpl::setEndDate( const QDate &date ) +{ + scheduleEndDate->setDate(date); +} + +void TaskGeneralPanelImpl::setRisktype( int r ) +{ + risk->setCurrentItem(r); +} + +int TaskGeneralPanelImpl::risktype() const +{ + return risk->currentItem(); +} + + + +} //KPlato namespace + +#include "kpttaskgeneralpanel.moc" diff --git a/kplato/kpttaskgeneralpanel.h b/kplato/kpttaskgeneralpanel.h new file mode 100644 index 00000000..fd3f539f --- /dev/null +++ b/kplato/kpttaskgeneralpanel.h @@ -0,0 +1,116 @@ +/* This file is part of the KDE project + Copyright (C) 2004 - 2006 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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 KPTTASKGENERALPANEL_H +#define KPTTASKGENERALPANEL_H + +#include "kpttaskgeneralpanelbase.h" +#include "kptduration.h" + +class KMacroCommand; + +namespace KPlato +{ + +class TaskGeneralPanel; +class RequestResourcesPanel; +class Part; +class Task; +class StandardWorktime; + +class TaskGeneralPanelImpl : public TaskGeneralPanelBase +{ + Q_OBJECT +public: + TaskGeneralPanelImpl(QWidget *parent, const char *name); + + virtual int schedulingType() const; + virtual int estimationType() const; + virtual int optimistic() const; + virtual int pessimistic(); + virtual Duration estimationValue(); + virtual QDateTime startDateTime(); + virtual QDateTime endDateTime(); + virtual QTime startTime() const; + virtual QTime endTime(); + virtual QDate startDate(); + virtual QDate endDate(); + virtual int risktype() const; +public slots: + virtual void setSchedulingType( int type ); + virtual void changeLeader(); + virtual void setEstimationType( int type ); + virtual void setOptimistic( int value ); + virtual void setPessimistic( int value ); + virtual void enableDateTime( int scheduleType ); + virtual void estimationTypeChanged( int type ); + virtual void setEstimate( const Duration & duration ); + virtual void setEstimateType( int type ); + virtual void checkAllFieldsFilled(); + virtual void setEstimateFields( int mask ); + virtual void setEstimateScales( double day ); + virtual void setEstimateFieldUnit( int field, QString unit ); + virtual void startDateChanged(); + virtual void startTimeChanged( const QTime & time ); + virtual void endDateChanged(); + virtual void endTimeChanged( const QTime & time ); + virtual void scheduleTypeChanged( int value ); + virtual void setStartTime( const QTime & time ); + virtual void setEndTime( const QTime & time ); + virtual void setStartDateTime( const QDateTime & dt ); + virtual void setEndDateTime( const QDateTime & dt ); + virtual void setStartDate( const QDate & date ); + virtual void setEndDate( const QDate & date ); + virtual void setRisktype( int r ); + +signals: + void obligatedFieldsFilled( bool ); + void schedulingTypeChanged( int ); + void changed(); + +protected: + bool useTime; +}; + +class TaskGeneralPanel : public TaskGeneralPanelImpl { + Q_OBJECT +public: + TaskGeneralPanel(Task &task, StandardWorktime *workTime=0, bool baseline=false, QWidget *parent=0, const char *name=0); + + KMacroCommand *buildCommand(Part *part); + + bool ok(); + + void setStartValues(Task &task, StandardWorktime *workTime=0); + +public slots: + virtual void estimationTypeChanged(int type); + virtual void scheduleTypeChanged(int value); + +private: + Task &m_task; + double m_dayLength; + + Duration m_effort; + Duration m_duration; +}; + +} //KPlato namespace + +#endif // TASKGENERALPANEL_H diff --git a/kplato/kpttaskgeneralpanelbase.ui b/kplato/kpttaskgeneralpanelbase.ui new file mode 100644 index 00000000..ffb7acd6 --- /dev/null +++ b/kplato/kpttaskgeneralpanelbase.ui @@ -0,0 +1,547 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>KPlato::TaskGeneralPanelBase</class> +<author>Dag Andersen <danders@get2net.dk></author> +<widget class="QWidget"> + <property name="name"> + <cstring>TaskGeneralPanelBase</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>653</width> + <height>418</height> + </rect> + </property> + <property name="caption"> + <string>TaskGeneralPanelBase</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout11</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout9</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>wbslabel</cstring> + </property> + <property name="text"> + <string>WBS:</string> + </property> + <property name="toolTip" stdset="0"> + <string>Work Breakdown Structure</string> + </property> + <property name="whatsThis" stdset="0"> + <string>The Work Breakdown Structure introduces numbering for all tasks in the project, according to the task structure. + +The WBS code is auto-generated; simply choose Generate WBS Code from the Tools menu to generate the WBS code for the project.</string> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>namelabel</cstring> + </property> + <property name="text"> + <string>Name:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>namefield</cstring> + </property> + <property name="whatsThis" stdset="0"> + <string>The name of the Task.</string> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>leaderlabel</cstring> + </property> + <property name="text"> + <string>Responsible:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>leaderfield</cstring> + </property> + <property name="whatsThis" stdset="0"> + <string>The person responsible for this task. + +This is not limited to persons available in a resource group but can be anyone. You can even directly access your address book with the Choose button.</string> + </property> + </widget> + </vbox> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout10</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout7</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>wbsfield</cstring> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer1</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QLabel"> + <property name="name"> + <cstring>idlabel</cstring> + </property> + <property name="text"> + <string>Task id:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>idfield</cstring> + </property> + <property name="whatsThis" stdset="0"> + <string>This is the unique identifier for this task.</string> + </property> + </widget> + <widget class="KLineEdit"> + <property name="name"> + <cstring>idfield</cstring> + </property> + </widget> + </hbox> + </widget> + <widget class="KLineEdit"> + <property name="name"> + <cstring>namefield</cstring> + </property> + <property name="whatsThis" stdset="0"> + <string>The name of the Task.</string> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout6</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KLineEdit"> + <property name="name"> + <cstring>leaderfield</cstring> + </property> + <property name="whatsThis" stdset="0"> + <string>The person responsible for this task. + +This is not limited to persons available in a resource group but can be anyone. You can even directly access your address book with the Choose button.</string> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>chooseLeader</cstring> + </property> + <property name="text"> + <string>Choose...</string> + </property> + <property name="accel"> + <string></string> + </property> + <property name="toolTip" stdset="0"> + <string>Insert a person from your address book.</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Insert a person from your address book.</string> + </property> + </widget> + </hbox> + </widget> + </vbox> + </widget> + </hbox> + </widget> + <widget class="QGroupBox"> + <property name="name"> + <cstring>schedulingGroup</cstring> + </property> + <property name="title"> + <string>Timing</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Scheduling Configuration. These settings affect the actual scheduling of the task. + +The estimation can be either effort based or duration based. If it is effort based, the final duration will depend on the resources assigned to the task. For duration based estimation, the assigned resources don't affect the fixed duration of the task, but only the costs.</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KComboBox" row="0" column="1"> + <item> + <property name="text"> + <string>As Soon as Possible</string> + </property> + </item> + <item> + <property name="text"> + <string>As Late as Possible</string> + </property> + </item> + <item> + <property name="text"> + <string>Must Start On</string> + </property> + </item> + <item> + <property name="text"> + <string>Must Finish On</string> + </property> + </item> + <item> + <property name="text"> + <string>Start Not Earlier Than</string> + </property> + </item> + <item> + <property name="text"> + <string>Finish Not Later Than</string> + </property> + </item> + <item> + <property name="text"> + <string>Fixed Interval</string> + </property> + </item> + <property name="name"> + <cstring>scheduleType</cstring> + </property> + </widget> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel3</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>0</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Schedule:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>scheduleType</cstring> + </property> + </widget> + <widget class="KDateWidget" row="0" column="2"> + <property name="name"> + <cstring>scheduleStartDate</cstring> + </property> + <property name="focusPolicy"> + <enum>WheelFocus</enum> + </property> + <property name="date"> + <date> + <year>2000</year> + <month>1</month> + <day>1</day> + </date> + </property> + </widget> + <widget class="QTimeEdit" row="0" column="3"> + <property name="name"> + <cstring>scheduleStartTime</cstring> + </property> + </widget> + <widget class="KDateWidget" row="1" column="2"> + <property name="name"> + <cstring>scheduleEndDate</cstring> + </property> + <property name="focusPolicy"> + <enum>WheelFocus</enum> + </property> + </widget> + <widget class="QTimeEdit" row="1" column="3"> + <property name="name"> + <cstring>scheduleEndTime</cstring> + </property> + </widget> + <widget class="KPlato::DurationWidget" row="2" column="2" rowspan="1" colspan="2"> + <property name="name"> + <cstring>estimate</cstring> + </property> + <property name="focusPolicy"> + <enum>WheelFocus</enum> + </property> + </widget> + <widget class="QLayoutWidget" row="3" column="2" rowspan="1" colspan="2"> + <property name="name"> + <cstring>layout41</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1_2</cstring> + </property> + <property name="text"> + <string>Optimistic:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>optimisticValueoptimisticValue</cstring> + </property> + </widget> + <widget class="QSpinBox"> + <property name="name"> + <cstring>optimisticValue</cstring> + </property> + <property name="suffix"> + <string>%</string> + </property> + <property name="maxValue"> + <number>0</number> + </property> + <property name="minValue"> + <number>-99</number> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel2_2</cstring> + </property> + <property name="text"> + <string>Pessimistic:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>pessimisticValue</cstring> + </property> + </widget> + <widget class="QSpinBox"> + <property name="name"> + <cstring>pessimisticValue</cstring> + </property> + <property name="suffix"> + <string>%</string> + </property> + <property name="maxValue"> + <number>999</number> + </property> + </widget> + </hbox> + </widget> + <widget class="QLabel" row="2" column="0"> + <property name="name"> + <cstring>textLabel3_2</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>0</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Estimate:</string> + </property> + </widget> + <widget class="KComboBox" row="2" column="1"> + <item> + <property name="text"> + <string>Effort</string> + </property> + </item> + <item> + <property name="text"> + <string>Duration</string> + </property> + </item> + <property name="name"> + <cstring>estimateType</cstring> + </property> + <property name="editable"> + <bool>false</bool> + </property> + <property name="urlDropsEnabled" stdset="0"> + <bool>false</bool> + </property> + </widget> + <widget class="KComboBox" row="3" column="1"> + <item> + <property name="text"> + <string>None</string> + </property> + </item> + <item> + <property name="text"> + <string>Low</string> + </property> + </item> + <item> + <property name="text"> + <string>High</string> + </property> + </item> + <property name="name"> + <cstring>risk</cstring> + </property> + <property name="editable"> + <bool>false</bool> + </property> + <property name="urlDropsEnabled" stdset="0"> + <bool>false</bool> + </property> + <property name="toolTip" stdset="0"> + <string>Risk controles the PERT distribution used when calculating the actual estimate for this task.</string> + </property> + <property name="whatsThis" stdset="0"> + <string><p>Risk controles the PERT distribution used when calculating the actual estimate for the task. +<b>None</b> means the Expected estimate is used as is. +<b>Low risk</b> means that a normal distribution is used. +<b>High risk</b> means that the estimate will be slightly pessimistic compared to Low risk.</p></string> + </property> + </widget> + <widget class="QLabel" row="3" column="0"> + <property name="name"> + <cstring>textLabel3_2_3</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>0</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Risk:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>risk</cstring> + </property> + <property name="toolTip" stdset="0"> + <string>Risk controles the PERT distribution used when calculating the actual estimate for this task.</string> + </property> + <property name="whatsThis" stdset="0"> + <string><p>Risk controles the PERT distribution used when calculating the actual estimate for the task. +<b>None</b> means the Expected estimate is used as is. +<b>Low risk</b> means that a normal distribution is used. +<b>High risk</b> means that the estimate will be slightly pessimistic compared to Low risk.</p></string> + </property> + </widget> + </grid> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>descriptionlabell6</cstring> + </property> + <property name="text"> + <string>Note:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>descriptionfield</cstring> + </property> + </widget> + <widget class="KTextEdit"> + <property name="name"> + <cstring>descriptionfield</cstring> + </property> + </widget> + </vbox> +</widget> +<customwidgets> + <customwidget> + <class>KPlato::DurationWidget</class> + <header location="local">kptdurationwidget.h</header> + <sizehint> + <width>0</width> + <height>20</height> + </sizehint> + <container>0</container> + <sizepolicy> + <hordata>5</hordata> + <verdata>0</verdata> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + <pixmap>image0</pixmap> + <signal>valueChanged()</signal> + <slot access="public" specifier="">slot()</slot> + <slot access="public" specifier="">setValue( const KPLato::Duration & newDuration )</slot> + <slot access="public" specifier="">slot()</slot> + <slot access="public" specifier="">handleFocus( int field )</slot> + </customwidget> +</customwidgets> +<images> + <image name="image0"> + <data format="PNG" length="1002">89504e470d0a1a0a0000000d4948445200000016000000160806000000c4b46c3b000003b149444154789cad945f4c5b551cc73fe7dc4b7b4bcba0762d45c43114323599ee6192609c51d883892ce083f1718b3ebb185f8dc91e972cf39d2d2a2f1af664b6f1e0fe3863a0718969700eb0c52142da0242a1bd6d696f7bcff101585203ceb8fd9ece39f99dcff9fe7edf939f88c562ec465f5f9fe609442c161362173c3e3eae7b7a7ac8e7f36432196cdbfe4f907c3e4f2291201e8fe338cec3737357e9e8e828aded1e229d650e1f2d51754b082110124c13a4dc5ea341eb9dc284c0558a853f3ce8cb0677ef500fde7d39d2596679e326597b8e9abb85d7a770ab16ab6983ec5a05b487a70e36f0f4e10afe408d6a558310980108478dba4a1e8233990c5d474b64ed39aa3a8fe5f3317fbf81dbd70bccfeb205947632fd74f6589c1c6ea2f70d03a58ba0c1f2c9bdc1b66de3b8256a6e11cbe7e3ee1d181b590124fe2693aeee08d223c82c3a2c24b7b874bec8f26288774f7bd054504aef0dde6e99c0eb83f9fb266323cb80a27fb0958141836044605a2ee5523393371cc646fee2da37195aa35d0c0c5b4859ac03d7e91712dcaac5adab3650a3ff9d08ef7dd8404bb48869e5d958b5b87dadc4c9a1464e9f0d0326df7ebd86bd2e310cb1bf62d384d59441f2d70a070e1c60e09489929b988681bdd9cc97170bcc4c65595f71f8e0e3301337fc24a7732467831875a47f289652b0be5e4151e6d07316c1b0c0340d8ab92023e76d66a6b2840e36d2fb7a13fee632475e6edc367ea98a90fb98b7dd6310ca0328a44761582e1bab41befabcc0ec940d28bc5e93b68e064cab84e1d9beaeb48934eac1f53b01c1b000fca496aa54b61a99fcde61662a4b4b4b23d1680be9d426173e4df3602a48ea411989a4fd590f52a8fd156b05ed9d350e3defe3cfdf4b4c7ce770ea7d3fb9f520afbe1620daeee5c26735d20b9b9cfb6811a754a439e4e5c5639a4caa1e5caf586bfc0197b78702005cb9b4cae4cd3267ce8638fe964bd72b393e39d74928d242617303a756a37f284447770dcdbffc6384a05a85de1306e9a52057c7527c7131c3c42d3f475eb2303c82d4fc3276d6811db37efeb148723082d9b08f79f97c1e5729109a9a28307cc622d2d6cdf52b2b24efe548dedb00142009862cfa879ee1a71f6cec928353511472fbf4389148b0b0e0c108081412458dfe21c9f11351e67e7358595468246d1d1e5e38a6e9e851bc39d84ab502a669331dafec0d8ec7e3e8cb06e1a881d727d1ae40180a434a8c9db129a54126ad48a7358c2b4c5352c8c374bcccdab2bb37d8719cba79fab8211f9df218e0582c261e95f8bfc04f1a1e8bc5c4dfe0a19017a725d8c60000000049454e44ae426082</data> + </image> +</images> +<tabstops> + <tabstop>namefield</tabstop> + <tabstop>leaderfield</tabstop> + <tabstop>chooseLeader</tabstop> + <tabstop>scheduleType</tabstop> + <tabstop>scheduleStartDate</tabstop> + <tabstop>scheduleStartTime</tabstop> + <tabstop>scheduleEndDate</tabstop> + <tabstop>scheduleEndTime</tabstop> + <tabstop>estimateType</tabstop> + <tabstop>estimate</tabstop> + <tabstop>risk</tabstop> + <tabstop>optimisticValue</tabstop> + <tabstop>pessimisticValue</tabstop> + <tabstop>descriptionfield</tabstop> + <tabstop>idfield</tabstop> + <tabstop>wbsfield</tabstop> +</tabstops> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>klineedit.h</includehint> + <includehint>kcombobox.h</includehint> + <includehint>kdatewidget.h</includehint> + <includehint>ktextedit.h</includehint> +</includehints> +</UI> diff --git a/kplato/kpttasknotespanelbase.ui b/kplato/kpttasknotespanelbase.ui new file mode 100644 index 00000000..f4f8da8d --- /dev/null +++ b/kplato/kpttasknotespanelbase.ui @@ -0,0 +1,51 @@ +<!DOCTYPE UI><UI version="3.2" stdsetdef="1"> +<class>KPlato::TaskNotesPanelBase</class> +<widget class="QWidget"> + <property name="name"> + <cstring>TaskNotesPanelBase</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>468</width> + <height>365</height> + </rect> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout1</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel5</cstring> + </property> + <property name="text"> + <string>&Project notes and summary:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>descriptionfield</cstring> + </property> + </widget> + <widget class="QTextEdit"> + <property name="name"> + <cstring>descriptionfield</cstring> + </property> + </widget> + </vbox> + </widget> + </hbox> +</widget> +<slots> + <slot>scheduling_clicked( int schedulingType )</slot> +</slots> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/kplato/kpttaskprogressdialog.cc b/kplato/kpttaskprogressdialog.cc new file mode 100644 index 00000000..33de8bf6 --- /dev/null +++ b/kplato/kpttaskprogressdialog.cc @@ -0,0 +1,71 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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. +*/ + +#include "kpttaskprogressdialog.h" +#include "kpttaskprogresspanel.h" + +#include <kcommand.h> +#include <klocale.h> + +#include <kdebug.h> + +namespace KPlato +{ + +TaskProgressDialog::TaskProgressDialog(Task &task, StandardWorktime *workTime, QWidget *p) + : KDialogBase(Swallow, i18n("Task Progress"), Ok|Cancel, Ok, p, "Task Progress Dialog", true, true) +{ + m_panel = new TaskProgressPanel(task, workTime, this); + + setMainWidget(m_panel); + + enableButtonOK(false); + + connect(m_panel, SIGNAL( changed() ), SLOT(slotChanged())); +} + +void TaskProgressDialog::slotChanged() { + enableButtonOK(true); +} + +KCommand *TaskProgressDialog::buildCommand(Part *part) { + KMacroCommand *m = new KMacroCommand(i18n("Modify Task Progress")); + bool modified = false; + KCommand *cmd = m_panel->buildCommand(part); + if (cmd) { + m->addCommand(cmd); + modified = true; + } + if (!modified) { + delete m; + return 0; + } + return m; +} + +void TaskProgressDialog::slotOk() { + if (!m_panel->ok()) + return; + accept(); +} + + +} //KPlato namespace + +#include "kpttaskprogressdialog.moc" diff --git a/kplato/kpttaskprogressdialog.h b/kplato/kpttaskprogressdialog.h new file mode 100644 index 00000000..f20e8256 --- /dev/null +++ b/kplato/kpttaskprogressdialog.h @@ -0,0 +1,53 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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 KPTTASKPROGRESSDIALOG_H +#define KPTTASKPROGRESSDIALOG_H + +#include <kdialogbase.h> + +class KCommand; + +namespace KPlato +{ + +class TaskProgressPanel; +class Task; +class Part; +class StandardWorktime; + +class TaskProgressDialog : public KDialogBase { + Q_OBJECT +public: + TaskProgressDialog(Task &task, StandardWorktime *workTime, QWidget *parent=0); + + KCommand *buildCommand(Part *part); + +protected slots: + void slotChanged(); + void slotOk(); + +private: + TaskProgressPanel *m_panel; + +}; + +} //KPlato namespace + +#endif // TASKPROGRESSDIALOG_H diff --git a/kplato/kpttaskprogresspanel.cc b/kplato/kpttaskprogresspanel.cc new file mode 100644 index 00000000..e85c6538 --- /dev/null +++ b/kplato/kpttaskprogresspanel.cc @@ -0,0 +1,190 @@ +/* This file is part of the KDE project + Copyright (C) 2004 - 2005 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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. +*/ + +#include "kpttaskprogresspanel.h" + +#include <qbuttongroup.h> +#include <qradiobutton.h> +#include <qcheckbox.h> + +#include <klineedit.h> +#include <ktextedit.h> +#include <kdatetimewidget.h> +#include <knuminput.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <kcommand.h> + +#include <kdebug.h> + +#include "kpttask.h" +#include "kptcommand.h" +#include "kptdurationwidget.h" +#include "kptcalendar.h" + +namespace KPlato +{ + +TaskProgressPanel::TaskProgressPanel(Task &task, StandardWorktime *workTime, QWidget *parent, const char *name) + : TaskProgressPanelImpl(parent, name), + m_task(task), + m_dayLength(24) +{ + kdDebug()<<k_funcinfo<<endl; + m_progress = task.progress(); + started->setChecked(m_progress.started); + finished->setChecked(m_progress.finished); + startTime->setDateTime(m_progress.startTime); + finishTime->setDateTime(m_progress.finishTime); + + percentFinished->setValue(m_progress.percentFinished); + + if (workTime) { + kdDebug()<<k_funcinfo<<"daylength="<<workTime->durationDay().hours()<<endl; + m_dayLength = workTime->durationDay().hours(); + setEstimateScales(m_dayLength); + } + remainingEffort->setValue(m_progress.remainingEffort); + remainingEffort->setVisibleFields(DurationWidget::Days | DurationWidget::Hours | DurationWidget::Minutes); + remainingEffort->setFieldUnit(0, i18n("day", "d")); + remainingEffort->setFieldUnit(1, i18n("hour", "h")); + remainingEffort->setFieldUnit(2, i18n("minute", "m")); + + m_progress.totalPerformed = task.actualEffort(); //FIXME + actualEffort->setValue(m_progress.totalPerformed); + actualEffort->setVisibleFields(DurationWidget::Days | DurationWidget::Hours | DurationWidget::Minutes); + actualEffort->setFieldUnit(0, i18n("day", "d")); + actualEffort->setFieldUnit(1, i18n("hour", "h")); + actualEffort->setFieldUnit(2, i18n("minute", "m")); + + scheduledStart->setDateTime(task.startTime()); + scheduledFinish->setDateTime(task.endTime()); + scheduledEffort->setValue(task.effort()->expected()); + scheduledEffort->setVisibleFields(DurationWidget::Days | DurationWidget::Hours | DurationWidget::Minutes); + scheduledEffort->setFieldUnit(0, i18n("day", "d")); + scheduledEffort->setFieldUnit(1, i18n("hour", "h")); + scheduledEffort->setFieldUnit(2, i18n("minute", "m")); + + enableWidgets(); + started->setFocus(); + +} + + +bool TaskProgressPanel::ok() { + m_progress.started = started->isChecked(); + m_progress.finished = finished->isChecked(); + m_progress.startTime = startTime->dateTime(); + m_progress.finishTime = finishTime->dateTime(); + m_progress.percentFinished = percentFinished->value(); + m_progress.remainingEffort = remainingEffort->value(); + m_progress.totalPerformed = actualEffort->value(); + return true; +} + +KCommand *TaskProgressPanel::buildCommand(Part *part) { + KCommand *cmd = 0; + QString c = i18n("Modify progress"); + if (m_task.progress() != m_progress) { + cmd = new TaskModifyProgressCmd(part, m_task, m_progress, c); + } + return cmd; +} + +void TaskProgressPanel::setEstimateScales( int day ) +{ + remainingEffort->setFieldScale(0, day); + remainingEffort->setFieldRightscale(0, day); + remainingEffort->setFieldLeftscale(1, day); + + actualEffort->setFieldScale(0, day); + actualEffort->setFieldRightscale(0, day); + actualEffort->setFieldLeftscale(1, day); + + scheduledEffort->setFieldScale(0, day); + scheduledEffort->setFieldRightscale(0, day); + scheduledEffort->setFieldLeftscale(1, day); +} + +//------------------------------------- + +TaskProgressPanelImpl::TaskProgressPanelImpl(QWidget *parent, const char *name, WFlags f) + : TaskProgressPanelBase(parent, name, f) { + + connect(started, SIGNAL(toggled(bool)), SLOT(slotStartedChanged(bool))); + connect(finished, SIGNAL(toggled(bool)), SLOT(slotFinishedChanged(bool))); + + connect(percentFinished, SIGNAL(valueChanged(int)), SLOT(slotPercentFinishedChanged(int))); + connect(percentFinished, SIGNAL(valueChanged(int)), SLOT(slotChanged())); + + connect(startTime, SIGNAL(valueChanged(const QDateTime &)), SLOT(slotChanged())); + connect(finishTime, SIGNAL(valueChanged(const QDateTime &)), SLOT(slotChanged())); + + connect(remainingEffort, SIGNAL(valueChanged()), SLOT(slotChanged())); + connect(actualEffort, SIGNAL(valueChanged()), SLOT(slotChanged())); + +} + +void TaskProgressPanelImpl::slotChanged() { + emit changed(); +} + +void TaskProgressPanelImpl::slotStartedChanged(bool state) { + if (state) { + startTime->setDateTime(QDateTime::currentDateTime()); + percentFinished->setValue(0); + } + enableWidgets(); +} + + +void TaskProgressPanelImpl::slotFinishedChanged(bool state) { + if (state) { + percentFinished->setValue(100); + if (!finishTime->dateTime().isValid()) { + finishTime->setDateTime(QDateTime::currentDateTime()); + } + } + enableWidgets(); +} + + +void TaskProgressPanelImpl::enableWidgets() { + started->setEnabled(!finished->isChecked()); + finished->setEnabled(started->isChecked()); + finishTime->setEnabled(started->isChecked()); + startTime->setEnabled(started->isChecked() && !finished->isChecked()); + performedGroup->setEnabled(started->isChecked() && !finished->isChecked()); + + scheduledStart->setEnabled(false); + scheduledFinish->setEnabled(false); + scheduledEffort->setEnabled(false); +} + + +void TaskProgressPanelImpl::slotPercentFinishedChanged( int value ) { + if (value == 100) { + //remainingEffort->setValue(Duration::zeroDuration); //FIXME + } +} + + +} //KPlato namespace + +#include "kpttaskprogresspanel.moc" diff --git a/kplato/kpttaskprogresspanel.h b/kplato/kpttaskprogresspanel.h new file mode 100644 index 00000000..24f4c9c2 --- /dev/null +++ b/kplato/kpttaskprogresspanel.h @@ -0,0 +1,71 @@ +/* This file is part of the KDE project + Copyright (C) 2004 - 2005 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef KPTTASKPROGRESSPANEL_H +#define KPTTASKPROGRESSPANEL_H + +#include "kpttaskprogresspanelbase.h" +#include "kpttask.h" + +class KCommand; + +namespace KPlato +{ + +class Part; +class StandardWorktime; + +class TaskProgressPanelImpl : public TaskProgressPanelBase { + Q_OBJECT +public: + TaskProgressPanelImpl(QWidget *parent=0, const char *name=0, WFlags f=0); + + void enableWidgets(); + +signals: + void changed(); + +public slots: + void slotChanged(); + void slotStartedChanged(bool state); + void slotFinishedChanged(bool state); + void slotPercentFinishedChanged(int value); +}; + +class TaskProgressPanel : public TaskProgressPanelImpl { + Q_OBJECT +public: + TaskProgressPanel(Task &task, StandardWorktime *workTime=0, QWidget *parent=0, const char *name=0); + + KCommand *buildCommand(Part *part); + + bool ok(); + +protected: + void setEstimateScales( int day ); + +private: + Task &m_task; + int m_dayLength; + struct Task::Progress m_progress; +}; + +} //KPlato namespace + +#endif // TASKPROGRESSPANEL_H diff --git a/kplato/kpttaskprogresspanelbase.ui b/kplato/kpttaskprogresspanelbase.ui new file mode 100644 index 00000000..40c03dd9 --- /dev/null +++ b/kplato/kpttaskprogresspanelbase.ui @@ -0,0 +1,431 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>KPlato::TaskProgressPanelBase</class> +<author>Dag Andersen <danders@get2net.dk></author> +<widget class="QWidget"> + <property name="name"> + <cstring>TaskProgressPanelBase</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>551</width> + <height>354</height> + </rect> + </property> + <property name="caption"> + <string>TaskProgressPanelBase</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QFrame"> + <property name="name"> + <cstring>frame5</cstring> + </property> + <property name="frameShape"> + <enum>StyledPanel</enum> + </property> + <property name="frameShadow"> + <enum>Raised</enum> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KDateTimeWidget" row="1" column="1"> + <property name="name"> + <cstring>finishTime</cstring> + </property> + <property name="focusPolicy"> + <enum>NoFocus</enum> + </property> + </widget> + <widget class="KDateTimeWidget" row="0" column="1"> + <property name="name"> + <cstring>startTime</cstring> + </property> + <property name="focusPolicy"> + <enum>NoFocus</enum> + </property> + </widget> + <widget class="QCheckBox" row="0" column="0"> + <property name="name"> + <cstring>started</cstring> + </property> + <property name="text"> + <string>Started:</string> + </property> + <property name="accel"> + <string></string> + </property> + </widget> + <widget class="QCheckBox" row="1" column="0"> + <property name="name"> + <cstring>finished</cstring> + </property> + <property name="text"> + <string>Finished:</string> + </property> + <property name="accel"> + <string></string> + </property> + </widget> + <spacer row="0" column="2" rowspan="2" colspan="1"> + <property name="name"> + <cstring>spacer17</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>61</width> + <height>41</height> + </size> + </property> + </spacer> + </grid> + </widget> + <widget class="QGroupBox"> + <property name="name"> + <cstring>performedGroup</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="title"> + <string></string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>Percent completed:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>percentFinished</cstring> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel1_2</cstring> + </property> + <property name="text"> + <string>Remaining effort:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>remainingEffort</cstring> + </property> + </widget> + <widget class="QLabel" row="2" column="0"> + <property name="name"> + <cstring>textLabel1_3</cstring> + </property> + <property name="text"> + <string>Actual effort:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>totalPerformed</cstring> + </property> + </widget> + <widget class="KPlato::DurationWidget" row="1" column="1" rowspan="1" colspan="2"> + <property name="name"> + <cstring>remainingEffort</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="focusPolicy"> + <enum>TabFocus</enum> + </property> + </widget> + <widget class="KPlato::DurationWidget" row="2" column="1" rowspan="1" colspan="2"> + <property name="name"> + <cstring>actualEffort</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="focusPolicy"> + <enum>TabFocus</enum> + </property> + </widget> + <widget class="KIntNumInput" row="0" column="1"> + <property name="name"> + <cstring>percentFinished</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="minValue"> + <number>0</number> + </property> + <property name="maxValue"> + <number>100</number> + </property> + </widget> + <spacer row="0" column="2"> + <property name="name"> + <cstring>spacer2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>200</width> + <height>20</height> + </size> + </property> + </spacer> + <spacer row="0" column="3" rowspan="3" colspan="1"> + <property name="name"> + <cstring>spacer19</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>41</width> + <height>70</height> + </size> + </property> + </spacer> + </grid> + </widget> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBox2</cstring> + </property> + <property name="title"> + <string>Scheduled</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget" row="0" column="1"> + <property name="name"> + <cstring>layout8</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KDateTimeWidget"> + <property name="name"> + <cstring>scheduledStart</cstring> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer3</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </hbox> + </widget> + <widget class="QLayoutWidget" row="1" column="1"> + <property name="name"> + <cstring>layout9</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KDateTimeWidget"> + <property name="name"> + <cstring>scheduledFinish</cstring> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer4</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </hbox> + </widget> + <widget class="QLayoutWidget" row="2" column="1"> + <property name="name"> + <cstring>layout11</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KPlato::DurationWidget"> + <property name="name"> + <cstring>scheduledEffort</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="minimumSize"> + <size> + <width>320</width> + <height>0</height> + </size> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer4_2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>59</width> + <height>20</height> + </size> + </property> + </spacer> + </hbox> + </widget> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel1_4</cstring> + </property> + <property name="text"> + <string>Start:</string> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel1_4_2</cstring> + </property> + <property name="text"> + <string>Finish:</string> + </property> + </widget> + <widget class="QLabel" row="2" column="0"> + <property name="name"> + <cstring>textLabel1_4_3</cstring> + </property> + <property name="text"> + <string>Effort:</string> + </property> + </widget> + </grid> + </widget> + <spacer> + <property name="name"> + <cstring>spacer2_2</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </vbox> +</widget> +<customwidgets> + <customwidget> + <class>KPlato::DurationWidget</class> + <header location="local">kptdurationwidget.h</header> + <sizehint> + <width>0</width> + <height>20</height> + </sizehint> + <container>0</container> + <sizepolicy> + <hordata>5</hordata> + <verdata>0</verdata> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + <pixmap>image0</pixmap> + <signal>valueChanged()</signal> + <slot access="public" specifier="">slot()</slot> + <slot access="public" specifier="">setValue( const KPLato::Duration & newDuration )</slot> + <slot access="public" specifier="">slot()</slot> + <slot access="public" specifier="">handleFocus( int field )</slot> + </customwidget> +</customwidgets> +<images> + <image name="image0"> + <data format="PNG" length="1002">89504e470d0a1a0a0000000d4948445200000016000000160806000000c4b46c3b000003b149444154789cad945f4c5b551cc73fe7dc4b7b4bcba0762d45c43114323599ee6192609c51d883892ce083f1718b3ebb185f8dc91e972cf39d2d2a2f1af664b6f1e0fe3863a0718969700eb0c52142da0242a1bd6d696f7bcff101585203ceb8fd9ece39f99dcff9fe7edf939f88c562ec465f5f9fe609442c161362173c3e3eae7b7a7ac8e7f36432196cdbfe4f907c3e4f2291201e8fe338cec3737357e9e8e828aded1e229d650e1f2d51754b082110124c13a4dc5ea341eb9dc284c0558a853f3ce8cb0677ef500fde7d39d2596679e326597b8e9abb85d7a770ab16ab6983ec5a05b487a70e36f0f4e10afe408d6a558310980108478dba4a1e8233990c5d474b64ed39aa3a8fe5f3317fbf81dbd70bccfeb205947632fd74f6589c1c6ea2f70d03a58ba0c1f2c9bdc1b66de3b8256a6e11cbe7e3ee1d181b590124fe2693aeee08d223c82c3a2c24b7b874bec8f26288774f7bd054504aef0dde6e99c0eb83f9fb266323cb80a27fb0958141836044605a2ee5523393371cc646fee2da37195aa35d0c0c5b4859ac03d7e91712dcaac5adab3650a3ff9d08ef7dd8404bb48869e5d958b5b87dadc4c9a1464e9f0d0326df7ebd86bd2e310cb1bf62d384d59441f2d70a070e1c60e09489929b988681bdd9cc97170bcc4c65595f71f8e0e3301337fc24a7732467831875a47f289652b0be5e4151e6d07316c1b0c0340d8ab92023e76d66a6b2840e36d2fb7a13fee632475e6edc367ea98a90fb98b7dd6310ca0328a44761582e1bab41befabcc0ec940d28bc5e93b68e064cab84e1d9beaeb48934eac1f53b01c1b000fca496aa54b61a99fcde61662a4b4b4b23d1680be9d426173e4df3602a48ea411989a4fd590f52a8fd156b05ed9d350e3defe3cfdf4b4c7ce770ea7d3fb9f520afbe1620daeee5c26735d20b9b9cfb6811a754a439e4e5c5639a4caa1e5caf586bfc0197b78702005cb9b4cae4cd3267ce8638fe964bd72b393e39d74928d242617303a756a37f284447770dcdbffc6384a05a85de1306e9a52057c7527c7131c3c42d3f475eb2303c82d4fc3276d6811db37efeb148723082d9b08f79f97c1e5729109a9a28307cc622d2d6cdf52b2b24efe548dedb00142009862cfa879ee1a71f6cec928353511472fbf4389148b0b0e0c108081412458dfe21c9f11351e67e7358595468246d1d1e5e38a6e9e851bc39d84ab502a669331dafec0d8ec7e3e8cb06e1a881d727d1ae40180a434a8c9db129a54126ad48a7358c2b4c5352c8c374bcccdab2bb37d8719cba79fab8211f9df218e0582c261e95f8bfc04f1a1e8bc5c4dfe0a19017a725d8c60000000049454e44ae426082</data> + </image> +</images> +<tabstops> + <tabstop>started</tabstop> + <tabstop>finished</tabstop> + <tabstop>percentFinished</tabstop> + <tabstop>remainingEffort</tabstop> + <tabstop>actualEffort</tabstop> + <tabstop>finishTime</tabstop> + <tabstop>startTime</tabstop> +</tabstops> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>kdatetimewidget.h</includehint> + <includehint>kdatewidget.h</includehint> + <includehint>ktimewidget.h</includehint> + <includehint>kdatetimewidget.h</includehint> + <includehint>kdatewidget.h</includehint> + <includehint>ktimewidget.h</includehint> + <includehint>knuminput.h</includehint> + <includehint>knuminput.h</includehint> + <includehint>kdatetimewidget.h</includehint> + <includehint>kdatewidget.h</includehint> + <includehint>ktimewidget.h</includehint> + <includehint>kdatetimewidget.h</includehint> + <includehint>kdatewidget.h</includehint> + <includehint>ktimewidget.h</includehint> +</includehints> +</UI> diff --git a/kplato/kpttaskresourcespanelbase.ui b/kplato/kpttaskresourcespanelbase.ui new file mode 100644 index 00000000..a7b308a3 --- /dev/null +++ b/kplato/kpttaskresourcespanelbase.ui @@ -0,0 +1,111 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>KPlato::TaskResourcesPanelBase</class> +<author>Dag Andersen <danders@get2net.dk></author> +<widget class="QWidget"> + <property name="name"> + <cstring>TaskResourcesPanelBase</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>445</width> + <height>169</height> + </rect> + </property> + <property name="caption"> + <string>TaskResourcesPanelBase</string> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QListView"> + <column> + <property name="text"> + <string>Group</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <column> + <property name="text"> + <string>Max. Units</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <property name="name"> + <cstring>groupList</cstring> + </property> + <property name="minimumSize"> + <size> + <width>200</width> + <height>0</height> + </size> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout14</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel2</cstring> + </property> + <property name="text"> + <string>Assign resources:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>resourceTable</cstring> + </property> + </widget> + <widget class="QTable"> + <column> + <property name="text"> + <string>Resource</string> + </property> + <property name="pixmap"> + <pixmap></pixmap> + </property> + </column> + <property name="name"> + <cstring>resourceTable</cstring> + </property> + <property name="resizePolicy"> + <enum>Default</enum> + </property> + <property name="numRows"> + <number>0</number> + </property> + <property name="numCols"> + <number>1</number> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + </vbox> + </widget> + </hbox> +</widget> +<includes> + <include location="local" impldecl="in implementation">kpttaskresourcespanelbase.ui.h</include> +</includes> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/kplato/kpttaskresourcespanelbase.ui.h b/kplato/kpttaskresourcespanelbase.ui.h new file mode 100644 index 00000000..765723cd --- /dev/null +++ b/kplato/kpttaskresourcespanelbase.ui.h @@ -0,0 +1,35 @@ +/**************************************************************************** +** ui.h extension file, included from the uic-generated form implementation. +** +** If you wish to add, delete or rename functions or slots use +** Qt Designer which will update this file, preserving your code. Create an +** init() function in place of a constructor, and a destroy() function in +** place of a destructor. +*****************************************************************************/ + + +/* This file is part of the KDE project + Copyright (C) 2004 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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. +*/ + +//TODO it seems this file can be removed + +namespace KPlato +{ + +} //KPlato namespace diff --git a/kplato/kptview.cc b/kplato/kptview.cc new file mode 100644 index 00000000..321bff42 --- /dev/null +++ b/kplato/kptview.cc @@ -0,0 +1,1519 @@ +/* This file is part of the KDE project + Copyright (C) 1998, 1999, 2000 Torben Weis <weis@kde.org> + Copyright (C) 2002 - 2005 Dag Andersen <danders@get2net.dk> + + 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. +*/ + +#include <kprinter.h> +#include <kmessagebox.h> + +#include <KoMainWindow.h> + +#include <qapplication.h> +#include <qpainter.h> +#include <qiconset.h> +#include <qlayout.h> +#include <qsplitter.h> +#include <qcanvas.h> +#include <qscrollview.h> +#include <qcolor.h> +#include <qlabel.h> +#include <qstring.h> +#include <qstringlist.h> +#include <qvbox.h> +#include <qgrid.h> +#include <qsize.h> +#include <qheader.h> +#include <qtabwidget.h> +#include <qwidgetstack.h> +#include <qtimer.h> +#include <qpopupmenu.h> +#include <qpair.h> + +#include <kiconloader.h> +#include <kaction.h> +#include <kstdaction.h> +#include <klocale.h> +#include <kdebug.h> +#include <klistview.h> +#include <kstdaccel.h> +#include <kaccelgen.h> +#include <kdeversion.h> +#include <kstatusbar.h> +#include <kxmlguifactory.h> + +#include <kstandarddirs.h> +#include <kdesktopfile.h> +#include <kcommand.h> +#include <kfiledialog.h> + +#include "kptview.h" +#include "kptaccountsview.h" +#include "kptfactory.h" +#include "kptmilestoneprogressdialog.h" +#include "kptnode.h" +#include "kptpart.h" +#include "kptproject.h" +#include "kptmainprojectdialog.h" +#include "kptprojectdialog.h" +#include "kpttask.h" +#include "kptsummarytaskdialog.h" +#include "kpttaskdialog.h" +#include "kpttaskprogressdialog.h" +#include "kptganttview.h" +#include "kptpertview.h" +//#include "kptreportview.h" +#include "kptdatetime.h" +#include "kptcommand.h" +#include "kptrelation.h" +#include "kptrelationdialog.h" +#include "kptresourceview.h" +#include "kptresourcedialog.h" +#include "kptresource.h" +#include "kptresourcesdialog.h" +#include "kptcalendarlistdialog.h" +#include "kptstandardworktimedialog.h" +#include "kptcanvasitem.h" +#include "kptconfigdialog.h" +#include "kptwbsdefinitiondialog.h" +#include "kptaccountsdialog.h" + +#include "KDGanttView.h" +#include "KDGanttViewTaskItem.h" +#include "KPtViewIface.h" + +namespace KPlato +{ + +View::View(Part* part, QWidget* parent, const char* /*name*/) + : KoView(part, parent, "Main View"), + m_ganttview(0), + m_ganttlayout(0), + m_pertview(0), + m_pertlayout(0), +// m_reportview(0), + m_baselineMode(false), + m_currentEstimateType(Effort::Use_Expected) +{ + //kdDebug()<<k_funcinfo<<endl; + getProject().setCurrentSchedule(Schedule::Expected); + + setInstance(Factory::global()); + if ( !part->isReadWrite() ) + setXMLFile("kplato_readonly.rc"); + else + setXMLFile("kplato.rc"); + m_dcop = 0L; + // build the DCOP object + dcopObject(); + + m_tab = new QWidgetStack(this); + QVBoxLayout *layout = new QVBoxLayout(this); + layout->add(m_tab); + + m_ganttview = new GanttView(m_tab, part->isReadWrite()); + m_tab->addWidget(m_ganttview); + m_updateGanttview = false; + m_ganttview->draw(getPart()->getProject()); + + m_pertview = new PertView( this, m_tab, layout ); + m_tab->addWidget(m_pertview); + + m_resourceview = new ResourceView( this, m_tab ); + m_updateResourceview = true; + m_tab->addWidget(m_resourceview); + + m_accountsview = new AccountsView( getProject(), this, m_tab ); + m_updateAccountsview = true; + m_tab->addWidget(m_accountsview); + + //m_reportview = new ReportView(this, m_tab); + //m_tab->addWidget(m_reportview); + + connect(m_tab, SIGNAL(aboutToShow(QWidget *)), this, SLOT(slotAboutToShow(QWidget *))); + + connect(m_pertview, SIGNAL(addRelation(Node*, Node*)), SLOT(slotAddRelation(Node*, Node*))); + connect(m_pertview, SIGNAL(modifyRelation(Relation*)), SLOT(slotModifyRelation(Relation*))); + + connect(m_ganttview, SIGNAL(enableActions(bool)), SLOT(setTaskActionsEnabled(bool))); + connect(m_ganttview, SIGNAL(addRelation(Node*, Node*, int)), SLOT(slotAddRelation(Node*, Node*, int))); + connect(m_ganttview, SIGNAL(modifyRelation(Relation*, int)), SLOT(slotModifyRelation(Relation*, int))); + connect(m_ganttview, SIGNAL(modifyRelation(Relation*)), SLOT(slotModifyRelation(Relation*))); + connect(m_ganttview, SIGNAL(itemDoubleClicked()), SLOT(slotOpenNode())); + connect(m_ganttview, SIGNAL(itemRenamed(Node*, const QString&)),this,SLOT(slotRenameNode(Node*, const QString&))); + connect(m_ganttview, SIGNAL(requestPopupMenu(const QString&, const QPoint &)),this,SLOT(slotPopupMenu(const QString&, const QPoint&))); + connect(m_resourceview, SIGNAL(itemDoubleClicked()), SLOT(slotEditResource())); + + // The menu items + // ------ Edit + actionCut = KStdAction::cut( this, SLOT( slotEditCut() ), actionCollection(), "edit_cut" ); + actionCopy = KStdAction::copy( this, SLOT( slotEditCopy() ), actionCollection(), "edit_copy" ); + actionPaste = KStdAction::paste( this, SLOT( slotEditPaste() ), actionCollection(), "edit_paste" ); + + actionIndentTask = new KAction(i18n("Indent Task"), "indent_task", 0, this, + SLOT(slotIndentTask()), actionCollection(), "indent_task"); + actionUnindentTask = new KAction(i18n("Unindent Task"), "unindent_task", 0, this, + SLOT(slotUnindentTask()), actionCollection(), "unindent_task"); + actionMoveTaskUp = new KAction(i18n("Move Up"), "move_task_up", 0, this, + SLOT(slotMoveTaskUp()), actionCollection(), "move_task_up"); + actionMoveTaskDown = new KAction(i18n("Move Down"), "move_task_down", 0, this, + SLOT(slotMoveTaskDown()), actionCollection(), "move_task_down"); + + // ------ View + actionViewGantt = new KAction(i18n("Gantt"), "gantt_chart", 0, this, SLOT(slotViewGantt()), actionCollection(), "view_gantt"); + + QString group = "EstimationType"; + actionViewExpected = new KRadioAction(i18n("Expected"), 0, 0, this, SLOT(slotViewExpected()), actionCollection(), "view_expected"); + actionViewExpected->setExclusiveGroup(group); + actionViewOptimistic = new KRadioAction(i18n("Optimistic"), 0, 0, this, SLOT(slotViewOptimistic()), actionCollection(), "view_optimistic"); + actionViewOptimistic->setExclusiveGroup(group); + actionViewPessimistic = new KRadioAction(i18n("Pessimistic"), 0, 0, this, SLOT(slotViewPessimistic()), actionCollection(), "view_pessimistic"); + actionViewPessimistic->setExclusiveGroup(group); + + actionViewGanttResources = new KToggleAction(i18n("Resources"), 0, 0, this, SLOT(slotViewGanttResources()), actionCollection(), "view_gantt_showResources"); + actionViewGanttTaskName = new KToggleAction(i18n("Task Name"), 0, 0, this, SLOT(slotViewGanttTaskName()), actionCollection(), "view_gantt_showTaskName"); + actionViewGanttTaskLinks = new KToggleAction(i18n("Task Links"), 0, 0, this, SLOT(slotViewGanttTaskLinks()), actionCollection(), "view_gantt_showTaskLinks"); + actionViewGanttProgress = new KToggleAction(i18n("Progress"), 0, 0, this, SLOT(slotViewGanttProgress()), actionCollection(), "view_gantt_showProgress"); + actionViewGanttFloat = new KToggleAction(i18n("Float"), 0, 0, this, SLOT(slotViewGanttFloat()), actionCollection(), "view_gantt_showFloat"); + actionViewGanttCriticalTasks = new KToggleAction(i18n("Critical Tasks"), 0, 0, this, SLOT(slotViewGanttCriticalTasks()), actionCollection(), "view_gantt_showCriticalTasks"); + actionViewGanttCriticalPath = new KToggleAction(i18n("Critical Path"), 0, 0, this, SLOT(slotViewGanttCriticalPath()), actionCollection(), "view_gantt_showCriticalPath"); + +// actionViewGanttNotScheduled = new KToggleAction(i18n("Not Scheduled"), 0, 0, this, SLOT(slotViewGanttNotScheduled()), actionCollection(), "view_gantt_showNotScheduled"); + + actionViewTaskAppointments = new KToggleAction(i18n("Show Allocations"), 0, 0, this, SLOT(slotViewTaskAppointments()), actionCollection(), "view_task_appointments"); + + actionViewPert = new KAction(i18n("Network"), "pert_chart", 0, this, SLOT(slotViewPert()), actionCollection(), "view_pert"); + + actionViewResources = new KAction(i18n("Resources"), "resources", 0, this, SLOT(slotViewResources()), actionCollection(), "view_resources"); + + actionViewResourceAppointments = new KToggleAction(i18n("Show Allocations"), 0, 0, this, SLOT(slotViewResourceAppointments()), actionCollection(), "view_resource_appointments"); + + actionViewAccounts = new KAction(i18n("Accounts"), "accounts", 0, this, SLOT(slotViewAccounts()), actionCollection(), "view_accounts"); + + //actionViewReports = new KAction(i18n("Reports"), "reports", 0, this, SLOT(slotViewReports()), actionCollection(), "view_reports"); + + // ------ Insert + actionAddTask = new KAction(i18n("Task..."), "add_task", 0, this, + SLOT(slotAddTask()), actionCollection(), "add_task"); + actionAddSubtask = new KAction(i18n("Sub-Task..."), "add_sub_task", 0, this, + SLOT(slotAddSubTask()), actionCollection(), "add_sub_task"); + actionAddMilestone = new KAction(i18n("Milestone..."), "add_milestone", 0, this, + SLOT(slotAddMilestone()), actionCollection(), "add_milestone"); + + // ------ Project + actionEditMainProject = new KAction(i18n("Edit Main Project..."), "edit", 0, this, SLOT(slotProjectEdit()), actionCollection(), "project_edit"); + actionEditStandardWorktime = new KAction(i18n("Edit Standard Worktime..."), "edit", 0, this, SLOT(slotProjectWorktime()), actionCollection(), "project_worktime"); + actionEditCalendar = new KAction(i18n("Edit Calendar..."), "edit", 0, this, SLOT(slotProjectCalendar()), actionCollection(), "project_calendar"); + actionEditAccounts = new KAction(i18n("Edit Accounts..."), "edit", 0, this, SLOT(slotProjectAccounts()), actionCollection(), "project_accounts"); + actionEditResources = new KAction(i18n("Edit Resources..."), "edit", 0, this, SLOT(slotProjectResources()), actionCollection(), "project_resources"); + + actionCalculate = new KActionMenu(i18n("Calculate"), "project_calculate", actionCollection(), "project_calculate"); + connect(actionCalculate, SIGNAL(activated()), SLOT(slotProjectCalculate())); + + actionCalculateExpected = new KAction(i18n("Expected"), 0, 0, this, SLOT(slotProjectCalculateExpected()), actionCollection(), "project_calculate_expected"); + actionCalculate->insert(actionCalculateExpected); + + actionCalculateOptimistic = new KAction(i18n("Optimistic"), 0, 0, this, SLOT(slotProjectCalculateOptimistic()), actionCollection(), "project_calculate_optimistic"); + actionCalculate->insert(actionCalculateOptimistic); + + actionCalculatePessimistic = new KAction(i18n("Pessimistic"), 0, 0, this, SLOT(slotProjectCalculatePessimistic()), actionCollection(), "project_calculate_pessimistic"); + actionCalculate->insert(actionCalculatePessimistic); + +/* // ------ Reports + actionFirstpage = KStdAction::firstPage(m_reportview,SLOT(slotPrevPage()),actionCollection(),"go_firstpage"); + connect(m_reportview, SIGNAL(setFirstPageActionEnabled(bool)), actionFirstpage, SLOT(setEnabled(bool))); + actionPriorpage = KStdAction::prior(m_reportview,SLOT(slotPrevPage()),actionCollection(),"go_prevpage"); + connect(m_reportview, SIGNAL(setPriorPageActionEnabled(bool)), actionPriorpage, SLOT(setEnabled(bool))); + actionNextpage = KStdAction::next(m_reportview,SLOT(slotNextPage()),actionCollection(), "go_nextpage"); + connect(m_reportview, SIGNAL(setNextPageActionEnabled(bool)), actionNextpage, SLOT(setEnabled(bool))); + actionLastpage = KStdAction::lastPage(m_reportview,SLOT(slotLastPage()),actionCollection(), "go_lastpage"); + connect(m_reportview, SIGNAL(setLastPageActionEnabled(bool)), actionLastpage, SLOT(setEnabled(bool))); + m_reportview->enableNavigationBtn();*/ + mainWindow()->toolBar("report")->hide(); + +// new KAction(i18n("Design..."), "report_design", 0, this, +// SLOT(slotReportDesign()), actionCollection(), "report_design"); + + + // ------ Tools + actionDefineWBS = + new KAction(i18n("Define WBS Pattern..."), "tools_define_wbs", 0, this, + SLOT(slotDefineWBS()), actionCollection(), "tools_generate_wbs"); + actionGenerateWBS = + new KAction(i18n("Generate WBS Code"), "tools_generate_wbs", 0, this, + SLOT(slotGenerateWBS()), actionCollection(), "tools_define_wbs"); + + // ------ Export (testing) + //actionExportGantt = new KAction(i18n("Export Ganttview"), "export_gantt", 0, this, + // SLOT(slotExportGantt()), actionCollection(), "export_gantt"); + + // ------ Settings + actionConfigure = new KAction(i18n("Configure KPlato..."), "configure", 0, this, + SLOT(slotConfigure()), actionCollection(), "configure"); + + // ------ Popup + actionOpenNode = new KAction(i18n("Edit..."), "edit", 0, this, + SLOT(slotOpenNode()), actionCollection(), "node_properties"); + actionTaskProgress = new KAction(i18n("Progress..."), "edit", 0, this, + SLOT(slotTaskProgress()), actionCollection(), "task_progress"); + actionDeleteTask = new KAction(i18n("Delete Task"), "editdelete", 0, this, + SLOT(slotDeleteTask()), actionCollection(), "delete_task"); + + actionEditResource = new KAction(i18n("Edit Resource..."), "edit", 0, this, + SLOT(slotEditResource()), actionCollection(), "edit_resource"); + + // ------------------- Actions with a key binding and no GUI item + // Temporary, till we get a menu entry + actNoInformation = new KAction("Toggle no information", CTRL+SHIFT+Key_T, this, SLOT(slotViewGanttNoInformation()), actionCollection(), "show_noinformation"); + +#ifndef NDEBUG + //new KAction("Print Debug", CTRL+SHIFT+Key_P, this, SLOT( slotPrintDebug()), actionCollection(), "print_debug"); + new KAction("Print Debug", CTRL+SHIFT+Key_P, this, SLOT(slotPrintSelectedDebug()), actionCollection(), "print_debug"); + new KAction("Print Calendar Debug", CTRL+SHIFT+Key_C, this, SLOT(slotPrintCalendarDebug()), actionCollection(), "print_calendar_debug"); +// new KAction("Print Test Debug", CTRL+SHIFT+Key_T, this, SLOT(slotPrintTestDebug()), actionCollection(), "print_test_debug"); + + KAction* actExportGantt = new KAction( i18n( "Export Gantt" ), CTRL+SHIFT+Key_G, + this, SLOT( slotExportGantt() ), actionCollection(), "export_gantt" ); + +#endif + // Stupid compilers ;) +#ifndef NDEBUG +/* Q_UNUSED( actPrintSelectedDebug ); + Q_UNUSED( actPrintCalendarDebug );*/ + Q_UNUSED( actExportGantt ); +#endif + + m_estlabel = new KStatusBarLabel("", 0); + addStatusBarItem(m_estlabel, 0, true); + actionViewExpected->setChecked(true); //TODO: context + setScheduleActionsEnabled(); + slotViewExpected(); + + setTaskActionsEnabled(false); +} + +View::~View() +{ + delete m_dcop; + removeStatusBarItem(m_estlabel); + delete m_estlabel; +} + +DCOPObject * View::dcopObject() +{ + if ( !m_dcop ) + m_dcop = new ViewIface( this ); + + return m_dcop; +} + + +Project& View::getProject() const +{ + return getPart()->getProject(); +} + +void View::setZoom(double zoom) { + m_ganttview->setZoom(zoom); + m_pertview->setZoom(zoom); +} + +void View::setupPrinter(KPrinter &/*printer*/) { + //kdDebug()<<k_funcinfo<<endl; +} + +void View::print(KPrinter &printer) { + //kdDebug()<<k_funcinfo<<endl; + if (printer.previewOnly()) { + //HACK: KoMainWindow shows setup on print, but not on print preview! + if (!printer.setup()) { + return; + } + } + if (m_tab->visibleWidget() == m_ganttview) + { + m_ganttview->print(printer); + } + else if (m_tab->visibleWidget() == m_pertview) + { + m_pertview->print(printer); + } + else if (m_tab->visibleWidget() == m_resourceview) + { + m_resourceview->print(printer); + } + else if (m_tab->visibleWidget() == m_accountsview) + { + m_accountsview->print(printer); + } +// else if (m_tab->visibleWidget() == m_reportview) +// { +// m_reportview->print(printer); +// } + +} + +void View::slotEditCut() { + //kdDebug()<<k_funcinfo<<endl; +} + +void View::slotEditCopy() { + //kdDebug()<<k_funcinfo<<endl; +} + +void View::slotEditPaste() { + //kdDebug()<<k_funcinfo<<endl; +} + +void View::slotViewExpected() { + //kdDebug()<<k_funcinfo<<endl; + m_currentEstimateType = Effort::Use_Expected; + getProject().setCurrentSchedulePtr(getProject().findSchedule(Schedule::Expected)); + slotUpdate(false); +} + +void View::slotViewOptimistic() { + //kdDebug()<<k_funcinfo<<endl; + m_currentEstimateType = Effort::Use_Optimistic; + getProject().setCurrentSchedulePtr(getProject().findSchedule(Schedule::Optimistic)); + slotUpdate(false); +} + +void View::slotViewPessimistic() { + //kdDebug()<<k_funcinfo<<endl; + m_currentEstimateType = Effort::Use_Pessimistic; + getProject().setCurrentSchedulePtr(getProject().findSchedule(Schedule::Pessimistic)); + slotUpdate(false); +} + +void View::slotViewGanttResources() { + //kdDebug()<<k_funcinfo<<endl; + m_ganttview->setShowResources(actionViewGanttResources->isChecked()); + if (m_tab->visibleWidget() == m_ganttview) + slotUpdate(false); +} + +void View::slotViewGanttTaskName() { + //kdDebug()<<k_funcinfo<<endl; + m_ganttview->setShowTaskName(actionViewGanttTaskName->isChecked()); + if (m_tab->visibleWidget() == m_ganttview) + slotUpdate(false); +} + +void View::slotViewGanttTaskLinks() { + //kdDebug()<<k_funcinfo<<endl; + m_ganttview->setShowTaskLinks(actionViewGanttTaskLinks->isChecked()); + if (m_tab->visibleWidget() == m_ganttview) + slotUpdate(false); +} + +void View::slotViewGanttProgress() { + //kdDebug()<<k_funcinfo<<endl; + m_ganttview->setShowProgress(actionViewGanttProgress->isChecked()); + if (m_tab->visibleWidget() == m_ganttview) + slotUpdate(false); +} + +void View::slotViewGanttFloat() { + //kdDebug()<<k_funcinfo<<endl; + m_ganttview->setShowPositiveFloat(actionViewGanttFloat->isChecked()); + if (m_tab->visibleWidget() == m_ganttview) + slotUpdate(false); +} + +void View::slotViewGanttCriticalTasks() { + //kdDebug()<<k_funcinfo<<endl; + m_ganttview->setShowCriticalTasks(actionViewGanttCriticalTasks->isChecked()); + if (m_tab->visibleWidget() == m_ganttview) + slotUpdate(false); +} + +void View::slotViewGanttCriticalPath() { + //kdDebug()<<k_funcinfo<<endl; + m_ganttview->setShowCriticalPath(actionViewGanttCriticalPath->isChecked()); + if (m_tab->visibleWidget() == m_ganttview) + slotUpdate(false); +} + +void View::slotViewGanttNoInformation() { + kdDebug()<<k_funcinfo<<m_ganttview->showNoInformation()<<endl; + m_ganttview->setShowNoInformation(!m_ganttview->showNoInformation()); //Toggle + if (m_tab->visibleWidget() == m_ganttview) + slotUpdate(false); +} + +void View::slotViewTaskAppointments() { + //kdDebug()<<k_funcinfo<<endl; + m_ganttview->setShowAppointments(actionViewTaskAppointments->isChecked()); + m_updateGanttview = true; + if (m_tab->visibleWidget() == m_ganttview) + slotUpdate(false); +} + +void View::slotViewGantt() { + //kdDebug()<<k_funcinfo<<endl; + m_tab->raiseWidget(m_ganttview); +} + +void View::slotViewPert() { + //kdDebug()<<k_funcinfo<<endl; + m_tab->raiseWidget(m_pertview); +} + +void View::slotViewResources() { + //kdDebug()<<k_funcinfo<<endl; + m_tab->raiseWidget(m_resourceview); +} + +void View::slotViewResourceAppointments() { + //kdDebug()<<k_funcinfo<<endl; + m_resourceview->setShowAppointments(actionViewResourceAppointments->isChecked()); + m_updateResourceview = true; + if (m_tab->visibleWidget() == m_resourceview) + slotUpdate(false); +} + +void View::slotViewAccounts() { + //kdDebug()<<k_funcinfo<<endl; + m_tab->raiseWidget(m_accountsview); +} + +void View::slotProjectEdit() { + MainProjectDialog *dia = new MainProjectDialog(getProject()); + if (dia->exec()) { + KCommand *cmd = dia->buildCommand(getPart()); + if (cmd) { + getPart()->addCommand(cmd); + } + } + delete dia; +} + +void View::slotProjectCalendar() { + CalendarListDialog *dia = new CalendarListDialog(getProject()); + if (dia->exec()) { + KCommand *cmd = dia->buildCommand(getPart()); + if (cmd) { + //kdDebug()<<k_funcinfo<<"Modifying calendar(s)"<<endl; + getPart()->addCommand(cmd); //also executes + } + } + delete dia; +} + +void View::slotProjectAccounts() { + AccountsDialog *dia = new AccountsDialog(getProject().accounts()); + if (dia->exec()) { + KCommand *cmd = dia->buildCommand(getPart()); + if (cmd) { + //kdDebug()<<k_funcinfo<<"Modifying account(s)"<<endl; + getPart()->addCommand(cmd); //also executes + } + } + delete dia; +} + +void View::slotProjectWorktime() { + StandardWorktimeDialog *dia = new StandardWorktimeDialog(getProject()); + if (dia->exec()) { + KCommand *cmd = dia->buildCommand(getPart()); + if (cmd) { + //kdDebug()<<k_funcinfo<<"Modifying calendar(s)"<<endl; + getPart()->addCommand(cmd); //also executes + } + } + delete dia; +} + +void View::slotProjectResources() { + ResourcesDialog *dia = new ResourcesDialog(getProject()); + if (dia->exec()) { + KCommand *cmd = dia->buildCommand(getPart()); + if (cmd) { + //kdDebug()<<k_funcinfo<<"Modifying resources"<<endl; + getPart()->addCommand(cmd); //also executes + } + } + delete dia; +} + +void View::slotProjectCalculate() { + //kdDebug()<<k_funcinfo<<endl; + slotUpdate(true); +} + +void View::slotProjectCalculateExpected() { + m_currentEstimateType = Effort::Use_Expected; + m_updateGanttview = true; + m_updateResourceview = true; + m_updateAccountsview = true; + slotUpdate(true); +} + +void View::slotProjectCalculateOptimistic() { + m_currentEstimateType = Effort::Use_Optimistic; + m_updateGanttview = true; + m_updateResourceview = true; + m_updateAccountsview = true; + slotUpdate(true); +} + +void View::slotProjectCalculatePessimistic() { + m_currentEstimateType = Effort::Use_Pessimistic; + m_updateGanttview = true; + m_updateResourceview = true; + m_updateAccountsview = true; + slotUpdate(true); +} + +void View::projectCalculate() { + if (false /*getProject().actualEffort() > 0*/) { + // NOTE: This can be removed when proper baselining etc is implemented + if (KMessageBox::warningContinueCancel(this, i18n("Progress information will be deleted if the project is recalculated."), i18n("Calculate"), i18n("Calculate")) == KMessageBox::Cancel) { + return; + } + } + QApplication::setOverrideCursor(Qt::waitCursor); + Schedule *ns = getProject().findSchedule((Schedule::Type)m_currentEstimateType); + KCommand *cmd; + if (ns) { + cmd = new RecalculateProjectCmd(getPart(), getProject(), *ns, i18n("Calculate")); + } else { + cmd = new CalculateProjectCmd(getPart(), getProject(), i18n("Standard"), (Effort::Use)m_currentEstimateType, i18n("Calculate")); + } + getPart()->addCommand(cmd); + QApplication::restoreOverrideCursor(); +} + +void View::slotViewReportDesign() { + //kdDebug()<<k_funcinfo<<endl; +} + +void View::slotViewReports() { + //kdDebug()<<k_funcinfo<<endl; + //m_tab->raiseWidget(m_reportview); +} + +void View::slotAddSubTask() { + // If we are positionend on the root project, then what we really want to + // do is to add a first project. We will silently accept the challenge + // and will not complain. + Task* node = getProject().createTask(getPart()->config().taskDefaults(), currentTask()); + TaskDialog *dia = new TaskDialog(*node, getProject().accounts(), getProject().standardWorktime(), getProject().isBaselined()); + if (dia->exec()) { + Node *currNode = currentTask(); + if (currNode) + { + KCommand *m = dia->buildCommand(getPart()); + m->execute(); // do changes to task + delete m; + SubtaskAddCmd *cmd = new SubtaskAddCmd(getPart(), &(getProject()), node, currNode, i18n("Add Subtask")); + getPart()->addCommand(cmd); // add task to project + return; + } + else + kdDebug()<<k_funcinfo<<"Cannot insert new project. Hmm, no current node!?"<<endl; + } + delete node; + delete dia; +} + + +void View::slotAddTask() { + Task* node = getProject().createTask(getPart()->config().taskDefaults(), currentTask()); + TaskDialog *dia = new TaskDialog(*node, getProject().accounts(), getProject().standardWorktime(), getProject().isBaselined()); + if (dia->exec()) { + Node* currNode = currentTask(); + if (currNode) + { + KCommand *m = dia->buildCommand(getPart()); + m->execute(); // do changes to task + delete m; + TaskAddCmd *cmd = new TaskAddCmd(getPart(), &(getProject()), node, currNode, i18n("Add Task")); + getPart()->addCommand(cmd); // add task to project + return; + } + else + kdDebug()<<k_funcinfo<<"Cannot insert new task. Hmm, no current node!?"<<endl; + } + delete node; + delete dia; +} + +void View::slotAddMilestone() { + Task* node = getProject().createTask(currentTask()); + node->effort()->set(Duration::zeroDuration); + + TaskDialog *dia = new TaskDialog(*node, getProject().accounts(), getProject().standardWorktime(), getProject().isBaselined()); + if (dia->exec()) { + Node *currNode = currentTask(); + if (currNode) + { + KCommand *m = dia->buildCommand(getPart()); + m->execute(); // do changes to task + delete m; + TaskAddCmd *cmd = new TaskAddCmd(getPart(), &(getProject()), node, currNode, i18n("Add Milestone")); + getPart()->addCommand(cmd); // add task to project + return; + } + else + kdDebug()<<k_funcinfo<<"Cannot insert new milestone. Hmm, no current node!?"<<endl; + } + delete node; + delete dia; +} + +void View::slotDefineWBS() { + //kdDebug()<<k_funcinfo<<endl; + WBSDefinitionDialog *dia = new WBSDefinitionDialog(getPart()->wbsDefinition()); + dia->exec(); + + delete dia; +} + +void View::slotGenerateWBS() { + //kdDebug()<<k_funcinfo<<endl; + getPart()->generateWBS(); + slotUpdate(false); +} + +void View::slotConfigure() { + //kdDebug()<<k_funcinfo<<endl; + ConfigDialog *dia = new ConfigDialog(getPart()->config(), getProject()); + dia->exec(); + delete dia; +} + +Node *View::currentTask() +{ + Node* task = 0; + if (m_tab->visibleWidget() == m_ganttview) { + task = m_ganttview->currentNode(); + } + else if (m_tab->visibleWidget() == m_pertview) { + task = m_pertview->currentNode(); + } + else if (m_tab->visibleWidget() == m_resourceview) { + task = m_resourceview->currentNode(); + } + if ( 0 != task ) { + return task; + } + return &(getProject()); +} + +void View::slotOpenNode() { + //kdDebug()<<k_funcinfo<<endl; + Node *node = currentTask(); + if (!node) + return; + + switch (node->type()) { + case Node::Type_Project: { + Project *project = dynamic_cast<Project *>(node); + MainProjectDialog *dia = new MainProjectDialog(*project); + if (dia->exec()){ + KCommand *m = dia->buildCommand(getPart()); + if (m) { + getPart()->addCommand(m); + } + } + delete dia; + break; + } + case Node::Type_Subproject: + //TODO + break; + case Node::Type_Task: { + Task *task = dynamic_cast<Task *>(node); + TaskDialog *dia = new TaskDialog(*task, getProject().accounts(), getProject().standardWorktime(), getProject().isBaselined()); + if (dia->exec()) { + KCommand *m = dia->buildCommand(getPart()); + if (m) { + getPart()->addCommand(m); + } + } + delete dia; + 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 = dynamic_cast<Task *>(node); + TaskDialog *dia = new TaskDialog(*task, getProject().accounts(), getProject().standardWorktime(), getProject().isBaselined()); + if (dia->exec()) { + KCommand *m = dia->buildCommand(getPart()); + if (m) { + getPart()->addCommand(m); + } + } + delete dia; + break; + } + case Node::Type_Summarytask: { + Task *task = dynamic_cast<Task *>(node); + SummaryTaskDialog *dia = new SummaryTaskDialog(*task); + if (dia->exec()) { + KCommand *m = dia->buildCommand(getPart()); + if (m) { + getPart()->addCommand(m); + } + } + delete dia; + break; + } + default: + break; // avoid warnings + } +} + +void View::slotTaskProgress() { + //kdDebug()<<k_funcinfo<<endl; + Node *node = currentTask(); + 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<Task *>(node); + TaskProgressDialog *dia = new TaskProgressDialog(*task, getProject().standardWorktime()); + if (dia->exec()) { + KCommand *m = dia->buildCommand(getPart()); + if (m) { + getPart()->addCommand(m); + } + } + delete dia; + break; + } + case Node::Type_Milestone: { + Task *task = dynamic_cast<Task *>(node); + MilestoneProgressDialog *dia = new MilestoneProgressDialog(*task); + if (dia->exec()) { + KCommand *m = dia->buildCommand(getPart()); + if (m) { + getPart()->addCommand(m); + } + } + delete dia; + break; + } + case Node::Type_Summarytask: { + // TODO + break; + } + default: + break; // avoid warnings + } +} + +void View::slotDeleteTask() +{ + //kdDebug()<<k_funcinfo<<endl; + Node *node = currentTask(); + if (node == 0 || node->getParent() == 0) { + kdDebug()<<k_funcinfo<<(node ? "Task is main project" : "No current task")<<endl; + return; + } + KMacroCommand *cmd = new KMacroCommand(i18n("Delete Task")); + cmd->addCommand(new NodeDeleteCmd(getPart(), node)); + QPtrListIterator<Relation> it = node->dependChildNodes(); + for (; it.current(); ++it) { + cmd->addCommand(new DeleteRelationCmd(getPart(), it.current())); + } + it = node->dependParentNodes(); + for (; it.current(); ++it) { + cmd->addCommand(new DeleteRelationCmd(getPart(),it.current())); + } + getPart()->addCommand(cmd); +} + +void View::slotIndentTask() +{ + //kdDebug()<<k_funcinfo<<endl; + Node *node = currentTask(); + if (node == 0 || node->getParent() == 0) { + kdDebug()<<k_funcinfo<<(node ? "Task is main project" : "No current task")<<endl; + return; + } + if (getProject().canIndentTask(node)) { + NodeIndentCmd *cmd = new NodeIndentCmd(getPart(), *node, i18n("Indent Task")); + getPart()->addCommand(cmd); + } +} + +void View::slotUnindentTask() +{ + //kdDebug()<<k_funcinfo<<endl; + Node *node = currentTask(); + if (node == 0 || node->getParent() == 0) { + kdDebug()<<k_funcinfo<<(node ? "Task is main project" : "No current task")<<endl; + return; + } + if (getProject().canUnindentTask(node)) { + NodeUnindentCmd *cmd = new NodeUnindentCmd(getPart(), *node, i18n("Unindent Task")); + getPart()->addCommand(cmd); + } +} + +void View::slotMoveTaskUp() +{ + //kdDebug()<<k_funcinfo<<endl; + + Node* task = currentTask(); + 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 + kdError()<<k_funcinfo<<"No current task"<<endl; + return; + } + + if ( Node::Type_Project == task->type() ) { + kdDebug()<<k_funcinfo<<"The root node cannot be moved up"<<endl; + return; + } + if (getProject().canMoveTaskUp(task)) { + NodeMoveUpCmd *cmd = new NodeMoveUpCmd(getPart(), *task, i18n("Move Task Up")); + getPart()->addCommand(cmd); + } +} + +void View::slotMoveTaskDown() +{ + //kdDebug()<<k_funcinfo<<endl; + + Node* task = currentTask(); + 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() ) { + kdDebug()<<k_funcinfo<<"The root node cannot be moved down"<<endl; + return; + } + if (getProject().canMoveTaskDown(task)) { + NodeMoveDownCmd *cmd = new NodeMoveDownCmd(getPart(), *task, i18n("Move Task Down")); + getPart()->addCommand(cmd); + } +} + +void View::slotAddRelation(Node *par, Node *child) { + //kdDebug()<<k_funcinfo<<endl; + Relation *rel = new Relation(par, child); + AddRelationDialog *dia = new AddRelationDialog(rel, this); + if (dia->exec()) { + KCommand *cmd = dia->buildCommand(getPart()); + if (cmd) + getPart()->addCommand(cmd); + } else { + delete rel; + } + delete dia; +} + +void View::slotAddRelation(Node *par, Node *child, int linkType) { + //kdDebug()<<k_funcinfo<<endl; + if (linkType == Relation::FinishStart || + linkType == Relation::StartStart || + linkType == Relation::FinishFinish) + { + Relation *rel = new Relation(par, child, static_cast<Relation::Type>(linkType)); + getPart()->addCommand(new AddRelationCmd(getPart(), rel, i18n("Add Relation"))); + } else { + slotAddRelation(par, child); + } +} + +void View::slotModifyRelation(Relation *rel) { + //kdDebug()<<k_funcinfo<<endl; + ModifyRelationDialog *dia = new ModifyRelationDialog(rel, this); + if (dia->exec()) { + if (dia->relationIsDeleted()) { + getPart()->addCommand(new DeleteRelationCmd(getPart(), rel, i18n("Delete Relation"))); + } else { + KCommand *cmd = dia->buildCommand(getPart()); + if (cmd) { + getPart()->addCommand(cmd); + } + } + } + delete dia; +} + +void View::slotModifyRelation(Relation *rel, int linkType) { + //kdDebug()<<k_funcinfo<<endl; + if (linkType == Relation::FinishStart || + linkType == Relation::StartStart || + linkType == Relation::FinishFinish) + { + getPart()->addCommand(new ModifyRelationTypeCmd(getPart(), rel, static_cast<Relation::Type>(linkType))); + } else { + slotModifyRelation(rel); + } +} + +// testing +void View::slotExportGantt() { + //kdDebug()<<k_funcinfo<<endl; + if (!m_ganttview) { + return; + } + QString fn = KFileDialog::getSaveFileName( QString::null, QString::null, this ); + if (!fn.isEmpty()) { + QFile f(fn); + m_ganttview->exportGantt(&f); + } +} + +void View::slotEditResource() { + //kdDebug()<<k_funcinfo<<endl; + Resource *r = m_resourceview->currentResource(); + if (!r) + return; + ResourceDialog *dia = new ResourceDialog(getProject(), r); + if (dia->exec()) { + KCommand *cmd = dia->buildCommand(getPart()); + if (cmd) + getPart()->addCommand(cmd); + } + delete dia; +} + +void View::updateReadWrite(bool /*readwrite*/) { +} + +Part *View::getPart()const { + return (Part *)koDocument(); +} + +void View::slotConnectNode() { + //kdDebug()<<k_funcinfo<<endl; +/* NodeItem *curr = m_ganttview->currentItem(); + if (curr) { + kdDebug()<<k_funcinfo<<"node="<<curr->getNode().name()<<endl; + }*/ +} + +QPopupMenu * View::popupMenu( const QString& name ) +{ + //kdDebug()<<k_funcinfo<<endl; + Q_ASSERT(factory()); + if ( factory() ) + return ((QPopupMenu*)factory()->container( name, this )); + return 0L; +} + +void View::slotChanged(QWidget *) +{ + //kdDebug()<<k_funcinfo<<endl; + slotUpdate(false); +} + +void View::slotChanged() +{ + //kdDebug()<<k_funcinfo<<endl; + slotUpdate(false); +} + +void View::slotUpdate(bool calculate) +{ + //kdDebug()<<k_funcinfo<<"calculate="<<calculate<<endl; + if (calculate) + projectCalculate(); + + m_updateGanttview = true; + m_updateResourceview = true; + m_updateAccountsview = true; + + updateView(m_tab->visibleWidget()); +} + +void View::slotAboutToShow(QWidget *widget) { + updateView(widget); +} + +void View::updateView(QWidget *widget) +{ + QApplication::setOverrideCursor(Qt::waitCursor); + setScheduleActionsEnabled(); + setTaskActionsEnabled(false); + mainWindow()->toolBar("report")->hide(); + if (widget == m_ganttview) + { + //kdDebug()<<k_funcinfo<<"draw gantt"<<endl; + m_ganttview->setShowExpected(actionViewExpected->isChecked()); + m_ganttview->setShowOptimistic(actionViewOptimistic->isChecked()); + m_ganttview->setShowPessimistic(actionViewPessimistic->isChecked()); + if (m_updateGanttview) + m_ganttview->drawChanges(getProject()); + setTaskActionsEnabled(widget, true); + m_updateGanttview = false; + } + else if (widget == m_pertview) + { + //kdDebug()<<k_funcinfo<<"draw pertview"<<endl; + m_pertview->draw(); + } + else if (widget == m_resourceview) + { + //kdDebug()<<k_funcinfo<<"draw resourceview"<<endl; + if (m_updateResourceview) + m_resourceview->draw(getPart()->getProject()); + m_updateResourceview = false; + } + else if (widget == m_accountsview) + { + //kdDebug()<<k_funcinfo<<"draw accountsview"<<endl; + if (m_updateAccountsview) + m_accountsview->draw(); + m_updateAccountsview = false; + } +/* else if (widget == m_reportview) + { + mainWindow()->toolBar("report")->show(); + m_reportview->enableNavigationBtn(); + }*/ + QApplication::restoreOverrideCursor(); +} + +void View::slotRenameNode(Node *node, const QString& name) { + //kdDebug()<<k_funcinfo<<name<<endl; + if (node) { + NodeModifyNameCmd *cmd = new NodeModifyNameCmd(getPart(), *node, name, i18n("Modify Name")); + getPart()->addCommand(cmd); + } +} + +void View::slotPopupMenu(const QString& menuname, const QPoint & pos) +{ + QPopupMenu* menu = this->popupMenu(menuname); + if (menu) + menu->exec(pos); +} + +bool View::setContext(Context &context) { + //kdDebug()<<k_funcinfo<<endl; + m_currentEstimateType = context.currentEstimateType; + getProject().setCurrentSchedule(context.currentSchedule); + actionViewExpected->setChecked(context.actionViewExpected); + actionViewOptimistic->setChecked(context.actionViewOptimistic); + actionViewPessimistic->setChecked(context.actionViewPessimistic); + + m_ganttview->setContext(context.ganttview, getProject()); + // hmmm, can't decide if these should be here or actions moved to ganttview + actionViewGanttResources->setChecked(context.ganttview.showResources); + actionViewGanttTaskName->setChecked(context.ganttview.showTaskName); + actionViewGanttTaskLinks->setChecked(context.ganttview.showTaskLinks); + actionViewGanttProgress->setChecked(context.ganttview.showProgress); + actionViewGanttFloat->setChecked(context.ganttview.showPositiveFloat); + actionViewGanttCriticalTasks->setChecked(context.ganttview.showCriticalTasks); + actionViewGanttCriticalPath->setChecked(context.ganttview.showCriticalPath); + + m_pertview->setContext(context.pertview); + m_resourceview->setContext(context.resourceview); + m_accountsview->setContext(context.accountsview); +// m_reportview->setContext(context.reportview); + + if (context.currentView == "ganttview") { + m_ganttview->setShowExpected(actionViewExpected->isChecked()); + m_ganttview->setShowOptimistic(actionViewOptimistic->isChecked()); + m_ganttview->setShowPessimistic(actionViewPessimistic->isChecked()); + slotViewGantt(); + } else if (context.currentView == "pertview") { + slotViewPert(); + } else if (context.currentView == "resourceview") { + slotViewResources(); + } else if (context.currentView == "accountsview") { + slotViewAccounts(); + } else if (context.currentView == "reportview") { + //slotViewReport(); + } else { + slotViewGantt(); + } + slotUpdate(false); + return true; +} + +void View::getContext(Context &context) const { + //kdDebug()<<k_funcinfo<<endl; + context.currentEstimateType = m_currentEstimateType; + if (getProject().currentSchedule()) + context.currentSchedule = getProject().currentSchedule()->id(); + context.actionViewExpected = actionViewExpected->isChecked(); + context.actionViewOptimistic = actionViewOptimistic->isChecked(); + context.actionViewPessimistic = actionViewPessimistic->isChecked(); + + if (m_tab->visibleWidget() == m_ganttview) { + context.currentView = "ganttview"; + } else if (m_tab->visibleWidget() == m_pertview) { + context.currentView = "pertview"; + } else if (m_tab->visibleWidget() == m_resourceview) { + context.currentView = "resourceview"; + } else if (m_tab->visibleWidget() == m_accountsview) { + context.currentView = "accountsview"; +/* } else if (m_tab->visibleWidget() == m_reportview) { + context.currentView = "reportview";*/ + } + m_ganttview->getContext(context.ganttview); + m_pertview->getContext(context.pertview); + m_resourceview->getContext(context.resourceview); + m_accountsview->getContext(context.accountsview); +// m_reportview->getContext(context.reportview); +} + +void View::setBaselineMode(bool /*on*/) { + //kdDebug()<<k_funcinfo<<endl; +/* m_baselineMode = on; + + m_ganttview->setReadWriteMode(!on); + + actionCut->setEnabled(!on); + actionCopy->setEnabled(!on); + actionPaste->setEnabled(!on); + + actionDeleteTask->setEnabled(!on); + actionIndentTask->setEnabled(!on); + actionUnindentTask->setEnabled(!on); + actionMoveTaskUp->setEnabled(!on); + actionMoveTaskDown->setEnabled(!on); + + actionAddTask->setEnabled(!on); + actionAddSubtask->setEnabled(!on); + actionAddMilestone->setEnabled(!on); + + actionEditStandardWorktime->setEnabled(!on); + actionEditCalendar->setEnabled(!on); + actionEditResources->setEnabled(!on); + actionCalculate->setEnabled(!on); + + actionEditResource->setEnabled(!on);*/ +} + +// called when widget w is about to be shown +void View::setTaskActionsEnabled(QWidget *w, bool on) { + Node *n = 0; + if (w == m_ganttview) { + n = m_ganttview->currentNode(); + }// else pert, etc when implemented + + actionAddTask->setEnabled(on); + actionAddMilestone->setEnabled(on); + // only enabled when we have a task selected + bool o = (on && n); + actionAddSubtask->setEnabled(o); + actionDeleteTask->setEnabled(o); + actionMoveTaskUp->setEnabled(o && getProject().canMoveTaskUp(n)); + actionMoveTaskDown->setEnabled(o && getProject().canMoveTaskDown(n)); + actionIndentTask->setEnabled(o && getProject().canIndentTask(n)); + actionUnindentTask->setEnabled(o && getProject().canUnindentTask(n)); +} + +void View::setTaskActionsEnabled(bool on) { + setTaskActionsEnabled(m_ganttview, on); //FIXME if more than ganttview can do this +} + +void View::setScheduleActionsEnabled() { + actionViewExpected->setEnabled(getProject().findSchedule(Schedule::Expected)); + actionViewOptimistic->setEnabled(getProject().findSchedule(Schedule::Optimistic)); + actionViewPessimistic->setEnabled(getProject().findSchedule(Schedule::Pessimistic)); + if (getProject().notScheduled()) { + m_estlabel->setText(i18n("Not scheduled")); + return; + } + Schedule *ns = getProject().currentSchedule(); + if (ns->type() == Schedule::Expected) { + actionViewExpected->setChecked(true); + m_estlabel->setText(i18n("Expected")); + } else if (ns->type() == Schedule::Optimistic) { + actionViewOptimistic->setChecked(true); + m_estlabel->setText(i18n("Optimistic")); + } else if (ns->type() == Schedule::Pessimistic) { + actionViewPessimistic->setChecked(true); + m_estlabel->setText(i18n("Pessimistic")); + } +} + + +#ifndef NDEBUG +void View::slotPrintDebug() { + kdDebug()<<"-------- Debug printout: Node list" <<endl; +/* Node *curr = m_ganttview->currentNode(); + if (curr) { + curr->printDebug(true,""); + } else*/ + getPart()->getProject().printDebug(true, ""); +} +void View::slotPrintSelectedDebug() { + Node *curr = m_ganttview->currentNode(); + if (curr) { + kdDebug()<<"-------- Debug printout: Selected node" <<endl; + curr->printDebug(true,""); + } else + slotPrintDebug(); +} +void View::slotPrintCalendarDebug() { + kdDebug()<<"-------- Debug printout: Node list" <<endl; +/* Node *curr = m_ganttview->currentNode(); + if (curr) { + curr->printDebug(true,""); + } else*/ + getPart()->getProject().printCalendarDebug(""); +} +void View::slotPrintTestDebug() { + const QStringList &lst = getPart()->xmlLoader().log(); + + for ( QStringList::ConstIterator it = lst.constBegin(); it != lst.constEnd(); ++it ) { + kdDebug()<<*it<<endl; + } +// kdDebug()<<"------------Test 1---------------------"<<endl; +// { +// DateTime d1(QDate(2006,1,2), QTime(8,0,0)); +// DateTime d2 = d1.addSecs(3600); +// Duration d = d2 - d1; +// bool b = d==Duration(0,0,0,3600); +// kdDebug()<<"1: Success="<<b<<" "<<d2.toString()<<"-"<<d1.toString()<<"="<<d.toString()<<endl; +// d = d1 - d2; +// b = d==Duration(0,0,0,3600); +// kdDebug()<<"2: Success="<<b<<" "<<d1.toString()<<"-"<<d2.toString()<<"="<<d.toString()<<endl; +// d2 = d2.addDays(-2); +// d = d1 - d2; +// b = d==Duration(2,0,0)-Duration(0,0,0,3600); +// kdDebug()<<"3: Success="<<b<<" "<<d1.toString()<<"-"<<d2.toString()<<"="<<d.toString()<<endl; +// d = d2 - d1; +// b = d==Duration(2,0,0)-Duration(0,0,0,3600); +// kdDebug()<<"4: Success="<<b<<" "<<d2.toString()<<"-"<<d1.toString()<<"="<<d.toString()<<endl; +// kdDebug()<<endl; +// b = (d2 + d)==d1; +// kdDebug()<<"5: Success="<<b<<" "<<d2<<"+"<<d.toString()<<"="<<d1<<endl; +// b = (d1 - d)==d2; +// kdDebug()<<"6: Success="<<b<<" "<<d1<<"-"<<d.toString()<<"="<<d2<<endl; +// } // end test 1 +// kdDebug()<<endl; +// kdDebug()<<"------------Test 2 Single calendar-----------------"<<endl; +// { +// Calendar *t = new Calendar("Test 2"); +// QDate wdate(2006,1,2); +// DateTime before = DateTime(wdate.addDays(-1)); +// DateTime after = DateTime(wdate.addDays(1)); +// QTime t1(8,0,0); +// QTime t2(10,0,0); +// DateTime wdt1(wdate, t1); +// DateTime wdt2(wdate, t2); +// CalendarDay *day = new CalendarDay(QDate(2006,1,2), Map::Working); +// day->addInterval(QPair<QTime, QTime>(t1, t2)); +// if (!t->addDay(day)) { +// kdDebug()<<"Failed to add day"<<endl; +// delete day; +// delete t; +// return; +// } +// kdDebug()<<"Added date="<<day->date().toString()<<" "<<day->startOfDay().toString()<<" - "<<day->endOfDay()<<endl; +// kdDebug()<<"Found date="<<day->date().toString()<<" "<<day->startOfDay().toString()<<" - "<<day->endOfDay()<<endl; +// +// CalendarDay *d = t->findDay(wdate); +// bool b = (day == d); +// kdDebug()<<"1: Success="<<b<<" Find same day"<<endl; +// +// DateTime dt = t->firstAvailableAfter(after, after.addDays(10)); +// b = !dt.isValid(); +// kdDebug()<<"2: Success="<<b<<" firstAvailableAfter("<<after<<"): ="<<dt<<endl; +// +// dt = t->firstAvailableBefore(before, before.addDays(-10)); +// b = !dt.isValid(); +// kdDebug()<<"3: Success="<<b<<" firstAvailableBefore("<<before.toString()<<"): ="<<dt<<endl; +// +// dt = t->firstAvailableAfter(before, after); +// b = dt == wdt1; +// kdDebug()<<"4: Success="<<b<<" firstAvailableAfter("<<before<<"): ="<<dt<<endl; +// +// dt = t->firstAvailableBefore(after, before); +// b = dt == wdt2; +// kdDebug()<<"5: Success="<<b<<" firstAvailableBefore("<<after<<"): ="<<dt<<endl; +// +// b = t->hasInterval(before, after); +// kdDebug()<<"6: Success="<<b<<" hasInterval("<<before<<", "<<after<<")"<<endl; +// +// b = !t->hasInterval(after, after.addDays(1)); +// kdDebug()<<"7: Success="<<b<<" !hasInterval("<<after<<", "<<after.addDays(1)<<")"<<endl; +// +// b = !t->hasInterval(before, before.addDays(-1)); +// kdDebug()<<"8: Success="<<b<<" !hasInterval("<<before<<", "<<before.addDays(-1)<<")"<<endl; +// +// Duration e1(0, 2, 0); // 2 hours +// Duration e2 = t->effort(before, after); +// b = e1==e2; +// kdDebug()<<"9: Success="<<b<<" effort"<<e1.toString()<<" = "<<e2.toString()<<endl; +// +// delete t; +// }// end test 2 +// +// kdDebug()<<endl; +// kdDebug()<<"------------Test 3 Parent calendar-----------------"<<endl; +// { +// Calendar *t = new Calendar("Test 3"); +// Calendar *p = new Calendar("Test 3 parent"); +// t->setParent(p); +// QDate wdate(2006,1,2); +// DateTime before = DateTime(wdate.addDays(-1)); +// DateTime after = DateTime(wdate.addDays(1)); +// QTime t1(8,0,0); +// QTime t2(10,0,0); +// DateTime wdt1(wdate, t1); +// DateTime wdt2(wdate, t2); +// CalendarDay *day = new CalendarDay(QDate(2006,1,2), Map::Working); +// day->addInterval(QPair<QTime, QTime>(t1, t2)); +// if (!p->addDay(day)) { +// kdDebug()<<"Failed to add day"<<endl; +// delete day; +// delete t; +// return; +// } +// kdDebug()<<"Added date="<<day->date().toString()<<" "<<day->startOfDay().toString()<<" - "<<day->endOfDay().toString()<<endl; +// kdDebug()<<"Found date="<<day->date().toString()<<" "<<day->startOfDay().toString()<<" - "<<day->endOfDay().toString()<<endl; +// +// CalendarDay *d = p->findDay(wdate); +// bool b = (day == d); +// kdDebug()<<"1: Success="<<b<<" Find same day"<<endl; +// +// DateTime dt = t->firstAvailableAfter(after, after.addDays(10)); +// b = !dt.isValid(); +// kdDebug()<<"2: Success="<<b<<" firstAvailableAfter("<<after.toString()<<"): ="<<!b<<endl; +// +// dt = t->firstAvailableBefore(before, before.addDays(-10)); +// b = !dt.isValid(); +// kdDebug()<<"3: Success="<<b<<" firstAvailableBefore("<<before.toString()<<"): ="<<!b<<endl; +// +// dt = t->firstAvailableAfter(before, after); +// b = dt == wdt1; +// kdDebug()<<"4: Success="<<b<<" firstAvailableAfter("<<before.toString()<<"): ="<<dt.toString()<<endl; +// +// dt = t->firstAvailableBefore(after, before); +// b = dt == wdt2; +// kdDebug()<<"5: Success="<<b<<" firstAvailableBefore("<<after.toString()<<"): ="<<dt.toString()<<endl; +// +// b = t->hasInterval(before, after); +// kdDebug()<<"6: Success="<<b<<" hasInterval("<<before.toString()<<", "<<after<<")"<<endl; +// +// b = !t->hasInterval(after, after.addDays(1)); +// kdDebug()<<"7: Success="<<b<<" !hasInterval("<<after.toString()<<", "<<after.addDays(1)<<")"<<endl; +// +// b = !t->hasInterval(before, before.addDays(-1)); +// kdDebug()<<"8: Success="<<b<<" !hasInterval("<<before.toString()<<", "<<before.addDays(-1)<<")"<<endl; +// Duration e1(0, 2, 0); // 2 hours +// Duration e2 = t->effort(before, after); +// b = e1==e2; +// kdDebug()<<"9: Success="<<b<<" effort "<<e1.toString()<<"=="<<e2.toString()<<endl; +// +// delete t; +// delete p; +// }// end test 3 +// kdDebug()<<endl; +// kdDebug()<<"------------Test 4 Parent calendar/weekdays-------------"<<endl; +// { +// QTime t1(8,0,0); +// QTime t2(10,0,0); +// Calendar *p = new Calendar("Test 4 parent"); +// CalendarDay *wd1 = p->weekday(0); // monday +// if (wd1 == 0) { +// kdDebug()<<"Failed to get weekday"<<endl; +// } +// wd1->setState(Map::NonWorking); +// +// CalendarDay *wd2 = p->weekday(2); // wednesday +// if (wd2 == 0) { +// kdDebug()<<"Failed to get weekday"<<endl; +// } +// wd2->addInterval(QPair<QTime, QTime>(t1, t2)); +// wd2->setState(Map::Working); +// +// Calendar *t = new Calendar("Test 4"); +// t->setParent(p); +// QDate wdate(2006,1,2); // monday jan 2 +// DateTime before = DateTime(wdate.addDays(-4)); //Thursday dec 29 +// DateTime after = DateTime(wdate.addDays(4)); // Friday jan 6 +// DateTime wdt1(wdate, t1); +// DateTime wdt2(QDate(2006, 1, 4), t2); // Wednesday +// CalendarDay *day = new CalendarDay(QDate(2006,1,2), Map::Working); +// day->addInterval(QPair<QTime, QTime>(t1, t2)); +// if (!p->addDay(day)) { +// kdDebug()<<"Failed to add day"<<endl; +// delete day; +// delete t; +// return; +// } +// kdDebug()<<"Added date="<<day->date().toString()<<" "<<day->startOfDay().toString()<<" - "<<day->endOfDay().toString()<<endl; +// kdDebug()<<"Found date="<<day->date().toString()<<" "<<day->startOfDay().toString()<<" - "<<day->endOfDay().toString()<<endl; +// +// CalendarDay *d = p->findDay(wdate); +// bool b = (day == d); +// kdDebug()<<"1: Success="<<b<<" Find same day"<<endl; +// +// DateTime dt = t->firstAvailableAfter(after, after.addDays(10)); +// b = (dt.isValid() && dt == DateTime(QDate(2006,1,11), t1)); +// kdDebug()<<"2: Success="<<b<<" firstAvailableAfter("<<after<<"): ="<<dt<<endl; +// +// dt = t->firstAvailableBefore(before, before.addDays(-10)); +// b = (dt.isValid() && dt == DateTime(QDate(2005, 12, 28), t2)); +// kdDebug()<<"3: Success="<<b<<" firstAvailableBefore("<<before.toString()<<"): ="<<dt<<endl; +// +// dt = t->firstAvailableAfter(before, after); +// b = dt == wdt1; // We find the day jan 2 +// kdDebug()<<"4: Success="<<b<<" firstAvailableAfter("<<before.toString()<<"): ="<<dt.toString()<<endl; +// +// dt = t->firstAvailableBefore(after, before); +// b = dt == wdt2; // We find the weekday (wednesday) +// kdDebug()<<"5: Success="<<b<<" firstAvailableBefore("<<after.toString()<<"): ="<<dt.toString()<<endl; +// +// b = t->hasInterval(before, after); +// kdDebug()<<"6: Success="<<b<<" hasInterval("<<before.toString()<<", "<<after<<")"<<endl; +// +// b = !t->hasInterval(after, after.addDays(1)); +// kdDebug()<<"7: Success="<<b<<" !hasInterval("<<after.toString()<<", "<<after.addDays(1)<<")"<<endl; +// +// b = !t->hasInterval(before, before.addDays(-1)); +// kdDebug()<<"8: Success="<<b<<" !hasInterval("<<before.toString()<<", "<<before.addDays(-1)<<")"<<endl; +// Duration e1(0, 4, 0); // 2 hours +// Duration e2 = t->effort(before, after); +// b = e1==e2; +// kdDebug()<<"9: Success="<<b<<" effort "<<e1.toString()<<"="<<e2.toString()<<endl; +// +// QPair<DateTime, DateTime> r = t->firstInterval(before, after); +// b = r.first == wdt1; // We find the monday jan 2 +// kdDebug()<<"10: Success="<<b<<" firstInterval("<<before<<"): ="<<r.first<<", "<<r.second<<endl; +// r = t->firstInterval(r.second, after); +// b = r.first == DateTime(QDate(2006, 1, 4),t1); // We find the wednesday jan 4 +// kdDebug()<<"11: Success="<<b<<" firstInterval("<<r.second<<"): ="<<r.first<<", "<<r.second<<endl; +// +// delete t; +// delete p; +// }// end test 4 +} +#endif + +} //KPlato namespace + +#include "kptview.moc" diff --git a/kplato/kptview.h b/kplato/kptview.h new file mode 100644 index 00000000..174a93d2 --- /dev/null +++ b/kplato/kptview.h @@ -0,0 +1,275 @@ +/* This file is part of the KDE project + Copyright (C) 1998, 1999, 2000 Torben Weis <weis@kde.org> + Copyright (C) 2002 - 2005 Dag Andersen <danders@get2net.dk> + + 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 KPLATO_VIEW +#define KPLATO_VIEW + +#include <KoView.h> +#include "kptcontext.h" + +class QListViewItem; +class QPopupMenu; +class QHBoxLayout; +class QTabWidget; +class QWidgetStack; + +class KListView; +class KPrinter; +class KAction; +class KActionMenu; +class KSelectAction; +class KToggleAction; +class KRadioAction; +class KStatusBarLabel; + +class DCOPObject; + +namespace KPlato +{ + +class AccountsView; +class GanttView; +class PertView; +class ResourceView; +//class ReportView; +class Part; +class Node; +class Project; +class Relation; +class Context; + +class View : public KoView { + Q_OBJECT + +public: + View(Part* part, QWidget* parent=0, const char* name=0); + ~View(); + /** + * Support zooming. + */ + virtual void setZoom(double zoom); + + Part *getPart()const; + + Project& getProject() const; + + virtual void setupPrinter(KPrinter &printer); + virtual void print(KPrinter &printer); + + QPopupMenu *popupMenu(const QString& name); + + void projectCalculate(); + + virtual DCOPObject* dcopObject(); + + virtual bool setContext(Context &context); + virtual void getContext(Context &context) const; + + void setTaskActionsEnabled(QWidget *w, bool on); + void setScheduleActionsEnabled(); + +public slots: + void slotUpdate(bool calculate); + void slotEditResource(); + void slotEditCut(); + void slotEditCopy(); + void slotEditPaste(); + void slotViewGantt(); + void slotViewExpected(); + void slotViewOptimistic(); + void slotViewPessimistic(); + + void slotViewGanttResources(); + void slotViewGanttTaskName(); + void slotViewGanttTaskLinks(); + void slotViewGanttProgress(); + void slotViewGanttFloat(); + void slotViewGanttCriticalTasks(); + void slotViewGanttCriticalPath(); + void slotViewGanttNoInformation(); + void slotViewTaskAppointments(); + void slotViewPert(); + void slotViewResources(); + void slotViewResourceAppointments(); + void slotViewAccounts(); + void slotAddTask(); + void slotAddSubTask(); + void slotAddMilestone(); + void slotProjectEdit(); + void slotDefineWBS(); + void slotGenerateWBS(); + void slotConfigure(); + void slotAddRelation(Node *par, Node *child); + void slotModifyRelation(Relation *rel); + void slotAddRelation(Node *par, Node *child, int linkType); + void slotModifyRelation(Relation *rel, int linkType); + + void setBaselineMode(bool on); + + void slotExportGantt(); // testing + void setTaskActionsEnabled(bool on); + + void slotRenameNode(Node *node, const QString& name); + + void slotPopupMenu(const QString& menuname, const QPoint & pos); + +protected slots: + void slotProjectCalendar(); + void slotProjectWorktime(); + void slotProjectCalculate(); + void slotProjectCalculateExpected(); + void slotProjectCalculateOptimistic(); + void slotProjectCalculatePessimistic(); + void slotProjectAccounts(); + void slotProjectResources(); + void slotViewReportDesign(); + void slotViewReports(); + + void slotOpenNode(); + void slotTaskProgress(); + void slotDeleteTask(); + void slotIndentTask(); + void slotUnindentTask(); + void slotMoveTaskUp(); + void slotMoveTaskDown(); + + void slotConnectNode(); + void slotChanged(QWidget *); + void slotChanged(); + + void slotAboutToShow(QWidget *widget); + +#ifndef NDEBUG + void slotPrintDebug(); + void slotPrintSelectedDebug(); + void slotPrintCalendarDebug(); + void slotPrintTestDebug(); +#else + static void slotPrintDebug() { }; + static void slotPrintSelectedDebug() { }; + static void slotPrintCalendarDebug() { }; + static void slotPrintTestDebug() { }; +#endif + +protected: + virtual void updateReadWrite(bool readwrite); + Node *currentTask(); + void updateView(QWidget *widget); + +private: + GanttView *m_ganttview; + QHBoxLayout *m_ganttlayout; + PertView *m_pertview; + QHBoxLayout *m_pertlayout; + QWidgetStack *m_tab; + ResourceView *m_resourceview; + AccountsView *m_accountsview; +// ReportView *m_reportview; + QPtrList<QString> m_reportTemplateFiles; + + bool m_baselineMode; + + int m_viewGrp; + int m_defaultFontSize; + int m_currentEstimateType; + + bool m_updateGanttview; + bool m_updateResourceview; + bool m_updateAccountsview; + + KStatusBarLabel *m_estlabel; + + DCOPObject* m_dcop; + + // ------ Edit + KAction *actionCut; + KAction *actionCopy; + KAction *actionPaste; + + KAction *actionIndentTask; + KAction *actionUnindentTask; + KAction *actionMoveTaskUp; + KAction *actionMoveTaskDown; + + // ------ View + KAction *actionViewGantt; + KRadioAction *actionViewExpected; + KRadioAction *actionViewOptimistic; + KRadioAction *actionViewPessimistic; + + KToggleAction *actionViewGanttResources; + KToggleAction *actionViewGanttTaskName; + KToggleAction *actionViewGanttTaskLinks; + KToggleAction *actionViewGanttProgress; + KToggleAction *actionViewGanttFloat; + KToggleAction *actionViewGanttCriticalTasks; + KToggleAction *actionViewGanttCriticalPath; + KToggleAction *actionViewGanttNotScheduled; + KToggleAction *actionViewTaskAppointments; + KAction *actionViewPert; + KAction *actionViewResources; + KToggleAction *actionViewResourceAppointments; + KAction *actionViewAccounts; + KAction *actionViewReports; + + // ------ Insert + KAction *actionAddTask; + KAction *actionAddSubtask; + KAction *actionAddMilestone; + + // ------ Project + KAction *actionEditMainProject; + KAction *actionEditStandardWorktime; + KAction *actionEditCalendar; + KAction *actionEditAccounts; + KAction *actionEditResources; + KActionMenu *actionCalculate; + KAction *actionCalculateExpected; + KAction *actionCalculateOptimistic; + KAction *actionCalculatePessimistic; + // ------ Reports + KAction *actionFirstpage; + KAction *actionPriorpage; + KAction *actionNextpage; + KAction *actionLastpage; + + // ------ Tools + KAction *actionDefineWBS; + KAction *actionGenerateWBS; + + // ------ Export (testing) + KAction *actionExportGantt; + + // ------ Settings + KAction *actionConfigure; + + // ------ Popup + KAction *actionOpenNode; + KAction *actionTaskProgress; + KAction *actionDeleteTask; + KAction *actionEditResource; + + //Test + KAction *actNoInformation; +}; + +} //Kplato namespace + +#endif diff --git a/kplato/kptwbsdefinition.cc b/kplato/kptwbsdefinition.cc new file mode 100644 index 00000000..115c8779 --- /dev/null +++ b/kplato/kptwbsdefinition.cc @@ -0,0 +1,188 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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. +*/ + +#include "kptwbsdefinition.h" + + +#include <klocale.h> +#include <kdebug.h> + +#include <qstring.h> +#include <qstringlist.h> +#include <qpair.h> + +namespace KPlato +{ + + +WBSDefinition::WBSDefinition() { + m_levelsEnabled = false; + + m_defaultDef.code = "Number"; + m_defaultDef.separator = "."; + + m_codeLists.append(qMakePair(QString("Number"), i18n("Number"))); + m_codeLists.append(qMakePair(QString("Roman, upper case"), i18n("Roman, Upper Case"))); + m_codeLists.append(qMakePair(QString("Roman, lower case"), i18n("Roman, Lower Case"))); + m_codeLists.append(qMakePair(QString("Letter, upper case"), i18n("Letter, Upper Case"))); + m_codeLists.append(qMakePair(QString("Letter, lower case"), i18n("Letter, Lower Case"))); +} + +WBSDefinition::~WBSDefinition() { +} + +void WBSDefinition::clear() { + m_defaultDef.clear(); + m_levelsDef.clear(); +} + +QString WBSDefinition::wbs(uint index, int level) { + if (isLevelsDefEnabled()) { + CodeDef def = levelsDef(level); + if (!def.isEmpty()) { + return code(def, index) + def.separator; + } + } + return code(m_defaultDef, index) + m_defaultDef.separator; +} + + +QString WBSDefinition::code(uint index, int level) { + if (isLevelsDefEnabled()) { + CodeDef def = levelsDef(level); + if (!def.isEmpty()) { + return code(def, index); + } + } + return code(m_defaultDef, index); +} + +QString WBSDefinition::separator(int level) { + if (isLevelsDefEnabled()) { + CodeDef def = levelsDef(level); + if (!def.isEmpty()) { + return def.separator; + } + } + return m_defaultDef.separator; +} + +void WBSDefinition::setLevelsDef(QMap<int, CodeDef> def) { + m_levelsDef.clear(); + m_levelsDef = def; +} + +WBSDefinition::CodeDef WBSDefinition::levelsDef(int level) const { + return m_levelsDef.contains(level) ? m_levelsDef[level] : CodeDef(); +} + +void WBSDefinition::setLevelsDef(int level, CodeDef def) { + m_levelsDef.insert(level, def); +} + +void WBSDefinition::setLevelsDef(int level, QString c, QString s) { + m_levelsDef.insert(level, CodeDef(c, s)); +} + +bool WBSDefinition::level0Enabled() { + return m_levelsEnabled && !levelsDef(0).isEmpty(); +} + +const QChar Letters[] = { '?','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z' }; + +QString WBSDefinition::code(CodeDef &def, uint index) { + if (def.code == "Number") { + return QString("%1").arg(index); + } + if (def.code == "Roman, lower case") { + return QString("%1").arg(toRoman(index)); + } + if (def.code == "Roman, upper case") { + return QString("%1").arg(toRoman(index, true)); + } + if (def.code == "Letter, lower case") { + if (index > 26) { + index = 0; + } + return QString("%1").arg(Letters[index]); + } + if (def.code == "Letter, upper case") { + if (index > 26) { + index = 0; + } + return QString("%1").arg(Letters[index].upper()); + } + return QString(); +} + +// Nicked from koparagcounter.cc +const QCString RNUnits[] = {"", "i", "ii", "iii", "iv", "v", "vi", "vii", "viii", "ix"}; +const QCString RNTens[] = {"", "x", "xx", "xxx", "xl", "l", "lx", "lxx", "lxxx", "xc"}; +const QCString RNHundreds[] = {"", "c", "cc", "ccc", "cd", "d", "dc", "dcc", "dccc", "cm"}; +const QCString RNThousands[] = {"", "m", "mm", "mmm"}; + +QString WBSDefinition::toRoman( int n, bool upper ) +{ + if ( n >= 0 ) { + QString s = QString::fromLatin1( RNThousands[ ( n / 1000 ) ] + + RNHundreds[ ( n / 100 ) % 10 ] + + RNTens[ ( n / 10 ) % 10 ] + + RNUnits[ ( n ) % 10 ] ); + return upper ? s.upper() : s; + + } else { // should never happen, but better not crash if it does + kdWarning()<< k_funcinfo << " n=" << n << endl; + return QString::number( n ); + } +} + +QStringList WBSDefinition::codeList() { + QStringList cl; + QValueList<QPair<QString, QString> >::Iterator it; + for (it = m_codeLists.begin(); it != m_codeLists.end(); ++it) { + cl.append((*it).second); + } + return cl; +} + +int WBSDefinition::defaultCodeIndex() const { + QValueList<QPair<QString, QString> >::const_iterator it; + int i = -1; + for(it = m_codeLists.begin(); it != m_codeLists.end(); ++it) { + ++i; + if (m_defaultDef.code == (*it).first) + break; + } + return i; +} + +bool WBSDefinition::setDefaultCode(uint index) { + QValueList<QPair<QString, QString> >::const_iterator it = m_codeLists.at(index); + if (it == m_codeLists.end()) { + return false; + } + m_defaultDef.code = (*it).first; + return true; +} + +void WBSDefinition::setDefaultSeparator(QString s) { + m_defaultDef.separator = s; +} + +} //namespace KPlato diff --git a/kplato/kptwbsdefinition.h b/kplato/kptwbsdefinition.h new file mode 100644 index 00000000..f27c4d7c --- /dev/null +++ b/kplato/kptwbsdefinition.h @@ -0,0 +1,95 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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 KPTWBSDEFINITION_H +#define KPTWBSDEFINITION_H + +#include <qstring.h> +#include <qmap.h> +#include <qpair.h> +#include <qvaluelist.h> + +class QStringList; + +namespace KPlato +{ + +class Part; + +class WBSDefinition { + +public: + WBSDefinition(); + ~WBSDefinition(); + + class CodeDef { + public: + CodeDef() {} + CodeDef(QString c, QString s) { code = c; separator = s; } + ~CodeDef() {} + void clear() { code = separator = QString(); } + bool isEmpty() { return code.isEmpty(); } + QString code; + QString separator; + }; + + void clear(); + + /// Return wbs string. + QString wbs(uint index, int level); + /// Return wbs code. + QString code(uint index, int level); + /// Return wbs separator. + QString separator(int level); + + CodeDef &defaultDef() { return m_defaultDef; } + void setDefaultDef(CodeDef def) { m_defaultDef = def; } + + bool isLevelsDefEnabled() const { return m_levelsEnabled; } + bool level0Enabled(); + void setLevelsDefEnabled(bool on) { m_levelsEnabled = on; } + void clearLevelsDef() { m_levelsDef.clear(); } + const QMap<int, CodeDef> &levelsDef() const { return m_levelsDef; } + void setLevelsDef(QMap<int, CodeDef> def); + CodeDef levelsDef(int level) const; + void setLevelsDef(int level, CodeDef def); + void setLevelsDef(int level, QString c, QString s); + + QStringList codeList(); + int defaultCodeIndex() const; + bool setDefaultCode(uint index); + QString defaultSeparator() const { return m_defaultDef.separator; } + void setDefaultSeparator(QString s); + +protected: + QString code(CodeDef &def, uint index); + QString toRoman(int n, bool upper = false); + +private: + CodeDef m_defaultDef; + + bool m_levelsEnabled; + QMap<int, CodeDef> m_levelsDef; + + QValueList<QPair<QString, QString> > m_codeLists; +}; + +} //namespace KPlato + +#endif //WBSDEFINITION_H diff --git a/kplato/kptwbsdefinitiondialog.cc b/kplato/kptwbsdefinitiondialog.cc new file mode 100644 index 00000000..687ddda7 --- /dev/null +++ b/kplato/kptwbsdefinitiondialog.cc @@ -0,0 +1,68 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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. +*/ + +#include "kptwbsdefinitiondialog.h" +#include "kptwbsdefinitionpanel.h" +#include "kptwbsdefinition.h" + +#include <klocale.h> +#include <kcommand.h> + +#include <kdebug.h> + +namespace KPlato +{ + +WBSDefinitionDialog::WBSDefinitionDialog(WBSDefinition &def, QWidget *p, const char *n) + : KDialogBase(Swallow, i18n("WBS Definition"), Ok|Cancel, Ok, p, n, true, true) +{ + + m_panel = new WBSDefinitionPanel(def, this); + setMainWidget(m_panel); + + enableButtonOK(false); + connect(m_panel, SIGNAL(changed(bool)), SLOT(enableButtonOK(bool))); +} + + +KMacroCommand *WBSDefinitionDialog::buildCommand(Part *part) { + KMacroCommand *m = new KMacroCommand(i18n("Modify WBS Definition")); + bool modified = false; + KCommand *cmd = m_panel->buildCommand(part); + if (cmd) { + m->addCommand(cmd); + modified = true; + } + if (!modified) { + delete m; + return 0; + } + return m; +} + +void WBSDefinitionDialog::slotOk() { + if (!m_panel->ok()) + return; + accept(); +} + + +} //KPlato namespace + +#include "kptwbsdefinitiondialog.moc" diff --git a/kplato/kptwbsdefinitiondialog.h b/kplato/kptwbsdefinitiondialog.h new file mode 100644 index 00000000..50997c48 --- /dev/null +++ b/kplato/kptwbsdefinitiondialog.h @@ -0,0 +1,50 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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 KPTWBSDEFINITIONDIALOG_H +#define KPTWBSDEFINITIONDIALOG_H + +#include <kdialogbase.h> + +class KMacroCommand; + +namespace KPlato +{ + +class WBSDefinitionPanel; +class WBSDefinition; +class Part; + +class WBSDefinitionDialog : public KDialogBase { + Q_OBJECT +public: + WBSDefinitionDialog(WBSDefinition &def, QWidget *parent=0, const char *name=0); + + KMacroCommand *buildCommand(Part *part); + +protected slots: + void slotOk(); + +private: + WBSDefinitionPanel *m_panel; +}; + +} //KPlato namespace + +#endif // WBSDEFINITIONDIALOG_H diff --git a/kplato/kptwbsdefinitionpanel.cc b/kplato/kptwbsdefinitionpanel.cc new file mode 100644 index 00000000..7b08b14f --- /dev/null +++ b/kplato/kptwbsdefinitionpanel.cc @@ -0,0 +1,173 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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. +*/ + +#include "kptwbsdefinitionpanel.h" +#include "kptwbsdefinition.h" +#include "kptcommand.h" +#include "kptpart.h" + +#include <klocale.h> +#include <kdebug.h> + +#include <qcombobox.h> +#include <qlineedit.h> +#include <qcheckbox.h> +#include <qgroupbox.h> +#include <qmemarray.h> +#include <qpushbutton.h> +#include <qstringlist.h> +#include <qspinbox.h> +#include <qtable.h> + +namespace KPlato +{ + +WBSDefinitionPanel::WBSDefinitionPanel(WBSDefinition &def, QWidget *p, const char *n) + : WBSDefinitionPanelBase(p, n), + m_def(def) +{ + removeBtn->setEnabled(false); + + QStringList codeList = def.codeList(); + defaultSeparator->setText(def.defaultSeparator()); + defaultCode->insertStringList(codeList); + defaultCode->setCurrentItem(def.defaultCodeIndex()); + defaultCode->setFocus(); + + levelsGroup->setChecked(def.isLevelsDefEnabled()); + int i = 0; + const QMap<int, WBSDefinition::CodeDef> &lev = def.levelsDef(); + levelsTable->setNumRows(lev.count()); + kdDebug()<<"Map size="<<lev.count()<<endl; + QMap<int, WBSDefinition::CodeDef>::const_iterator it; + for (it = lev.begin(); it != lev.end(); ++it) { + levelsTable->verticalHeader()->setLabel(i, QString("%1").arg(it.key())); + QComboTableItem *item = new QComboTableItem(levelsTable, codeList, true); + item->setCurrentItem(it.data().code); + levelsTable->setItem(i, 0, item); + levelsTable->setText(i, 1, it.data().separator); + i++; + } + levelsTable->setColumnStretchable(0, true); + slotLevelChanged(level->value()); + + connect(defaultCode, SIGNAL(activated(int)), SLOT(slotChanged())); + connect(defaultSeparator, SIGNAL(textChanged(const QString&)), SLOT(slotChanged())); + connect(levelsGroup, SIGNAL(toggled(bool)), SLOT(slotLevelsGroupToggled(bool))); + connect(levelsTable, SIGNAL(valueChanged(int, int)), SLOT(slotChanged())); + connect(levelsTable, SIGNAL(selectionChanged()), SLOT(slotSelectionChanged())); + connect(level, SIGNAL(valueChanged(int)), SLOT(slotLevelChanged(int))); + connect(removeBtn, SIGNAL(clicked()), SLOT(slotRemoveBtnClicked())); + connect(addBtn, SIGNAL(clicked()), SLOT(slotAddBtnClicked())); +} + +void WBSDefinitionPanel::setStartValues(Part */*part*/) { +} + +KMacroCommand *WBSDefinitionPanel::buildCommand(Part */*part*/) { + KMacroCommand *cmd = new KMacroCommand(i18n("Modify WBS Definition")); + + return cmd; +} + +bool WBSDefinitionPanel::ok() { + m_def.setDefaultCode(defaultCode->currentItem()); + m_def.setDefaultSeparator(defaultSeparator->text()); + + m_def.setLevelsDefEnabled(levelsGroup->isChecked()); + + m_def.clearLevelsDef(); + for (int i = 0; i < levelsTable->numRows(); ++i) { + m_def.setLevelsDef(levelsTable->verticalHeader()->label(i).toInt(), levelsTable->text(i, 0), levelsTable->text(i, 1)); + } + return true; +} + +void WBSDefinitionPanel::slotChanged() { + emit changed(true); +} + +void WBSDefinitionPanel::slotSelectionChanged() { + QString s; + bool rowSelected = false; + for (int i=0; i < levelsTable->numRows(); ++i) { + if (levelsTable->isRowSelected(i, true)) { + s += QString("Row[%1]=selected ").arg(i); + rowSelected = true; + } + } + removeBtn->setEnabled(rowSelected); + if (s.isEmpty()) s = "None selected"; + kdDebug()<<k_funcinfo<<s<<endl; +} + +void WBSDefinitionPanel::slotRemoveBtnClicked() { + QMemArray<int> rows; + for (int i=0; i < levelsTable->numRows(); ++i) { + if (levelsTable->isRowSelected(i)) { + rows.resize(rows.size()+1); + rows[rows.size()-1] = i; + } + } + levelsTable->removeRows(rows); + removeBtn->setEnabled(false); + slotLevelChanged(level->value()); +} + +void WBSDefinitionPanel::slotAddBtnClicked() { + kdDebug()<<k_funcinfo<<endl; + int i=levelsTable->numRows()-1; + for (; i >= 0; --i) { + kdDebug()<<k_funcinfo<<"Checking row["<<i<<"]="<<levelsTable->verticalHeader()->label(i)<<" with "<<level->value()<<endl; + if (level->value() > levelsTable->verticalHeader()->label(i).toInt()) { + break; + } + } + i++; + levelsTable->insertRows(i); + levelsTable->verticalHeader()->setLabel(i, QString("%1").arg(level->value())); + QComboTableItem *item = new QComboTableItem(levelsTable, m_def.codeList(), true); + levelsTable->setItem(i, 0, item); + levelsTable->clearSelection(); + levelsTable->selectCells(i, 0, i, 0); + levelsTable->setCurrentCell(i, 0); + addBtn->setEnabled(false); + slotChanged(); + + kdDebug()<<k_funcinfo<<"Added row="<<i<<" level="<<level->value()<<endl; +} + +void WBSDefinitionPanel::slotLevelChanged(int value) { + for (int i=0; i < levelsTable->numRows(); ++i) { + if (value == levelsTable->verticalHeader()->label(i).toInt()) { + addBtn->setEnabled(false); + return; + } + } + addBtn->setEnabled(levelsGroup->isChecked()); + slotChanged(); +} +void WBSDefinitionPanel::slotLevelsGroupToggled(bool /*on*/) { + slotLevelChanged(level->value()); +} + + +} //KPlato namespace + +#include "kptwbsdefinitionpanel.moc" diff --git a/kplato/kptwbsdefinitionpanel.h b/kplato/kptwbsdefinitionpanel.h new file mode 100644 index 00000000..abbdc91a --- /dev/null +++ b/kplato/kptwbsdefinitionpanel.h @@ -0,0 +1,61 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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 KPTWBSDEFINITIONPANEL_H +#define KPTWBSDEFINITIONPANEL_H + +#include "kptwbsdefinitionpanelbase.h" + +class KMacroCommand; + +namespace KPlato +{ + +class Part; +class WBSDefinition; + +class WBSDefinitionPanel : public WBSDefinitionPanelBase { + Q_OBJECT +public: + WBSDefinitionPanel(WBSDefinition &def, QWidget *parent=0, const char *name=0); + + KMacroCommand *buildCommand(Part *part); + + bool ok(); + + void setStartValues(Part *part); + +signals: + void changed(bool enable); + +protected slots: + void slotChanged(); + void slotSelectionChanged(); + void slotRemoveBtnClicked(); + void slotAddBtnClicked(); + void slotLevelChanged(int); + void slotLevelsGroupToggled(bool on); +private: + + WBSDefinition &m_def; +}; + +} //KPlato namespace + +#endif // WBSDEFINITIONPANEL_H diff --git a/kplato/kptwbsdefinitionpanelbase.ui b/kplato/kptwbsdefinitionpanelbase.ui new file mode 100644 index 00000000..0ab8661d --- /dev/null +++ b/kplato/kptwbsdefinitionpanelbase.ui @@ -0,0 +1,185 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>KPlato::WBSDefinitionPanelBase</class> +<author>Dag Andersen <danders@get2net.dk></author> +<widget class="QWidget"> + <property name="name"> + <cstring>WBSDefinitionPanelBase</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>440</width> + <height>252</height> + </rect> + </property> + <property name="caption"> + <string>WBSDefinitionPanelBase</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QGroupBox" row="0" column="0"> + <property name="name"> + <cstring>groupBox4</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="title"> + <string>Default</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout9</cstring> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>Code:</string> + </property> + </widget> + <widget class="QComboBox" row="1" column="0"> + <property name="name"> + <cstring>defaultCode</cstring> + </property> + </widget> + <widget class="QLabel" row="0" column="1"> + <property name="name"> + <cstring>textLabel1_3_2</cstring> + </property> + <property name="text"> + <string>Separator:</string> + </property> + </widget> + <widget class="QLineEdit" row="1" column="1"> + <property name="name"> + <cstring>defaultSeparator</cstring> + </property> + </widget> + </grid> + </widget> + </vbox> + </widget> + <widget class="QGroupBox" row="1" column="0"> + <property name="name"> + <cstring>levelsGroup</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="title"> + <string>Use Levels</string> + </property> + <property name="checkable"> + <bool>true</bool> + </property> + <property name="checked"> + <bool>false</bool> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QTable"> + <column> + <property name="text"> + <string>Code</string> + </property> + </column> + <column> + <property name="text"> + <string>Separator</string> + </property> + </column> + <property name="name"> + <cstring>levelsTable</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="numRows"> + <number>0</number> + </property> + <property name="numCols"> + <number>2</number> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout7</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QPushButton"> + <property name="name"> + <cstring>removeBtn</cstring> + </property> + <property name="text"> + <string>Remove</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer1</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>140</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout3</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QPushButton"> + <property name="name"> + <cstring>addBtn</cstring> + </property> + <property name="text"> + <string>Add Level</string> + </property> + </widget> + <widget class="QSpinBox"> + <property name="name"> + <cstring>level</cstring> + </property> + </widget> + </hbox> + </widget> + </hbox> + </widget> + </vbox> + </widget> + </grid> +</widget> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/kplato/kptxmlloaderobject.h b/kplato/kptxmlloaderobject.h new file mode 100644 index 00000000..d049d6b0 --- /dev/null +++ b/kplato/kptxmlloaderobject.h @@ -0,0 +1,100 @@ +/* This file is part of the KDE project + Copyright (C) 2006 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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 XMLLOADEROBJECT_H +#define XMLLOADEROBJECT_H + +#include "kptproject.h" + +#include <qdatetime.h> +#include <qstring.h> +#include <qstringlist.h> + +namespace KPlato +{ + +class XMLLoaderObject { +public: + enum Severity { None=0, Errors=1, Warnings=2, Diagnostics=3, Debug=4 }; + XMLLoaderObject() + : m_project(0), + m_errors(0), + m_warnings(0), + m_logLevel(Diagnostics), + m_log() { + } + ~XMLLoaderObject() {} + + void setProject(Project *proj) { m_project = proj; } + Project &project() const { return *m_project; } + + void startLoad() { + m_timer.start(); + m_starttime = QDateTime::currentDateTime(); + m_errors = m_warnings = 0; + m_log.clear(); + addMsg(QString("Loading started at %1").arg(m_starttime.toString())); + } + void stopLoad() { + m_elapsed = m_timer.elapsed(); + addMsg(QString("Loading finished at %1, took %2").arg(QDateTime::currentDateTime().toString()).arg(formatElapsed())); + } + QDateTime lastLoaded() const { return m_starttime; } + int elapsed() const { return m_elapsed; } + QString formatElapsed() { return QString("%1 seconds").arg((double)m_elapsed/1000); } + + void setLogLevel(Severity sev) { m_logLevel = sev; } + const QStringList &log() const { return m_log; } + void addMsg(int sev, QString msg) { + increment(sev); + if (m_logLevel < sev) return; + QString s; + if (sev == Errors) s = "ERROR"; + else if (sev == Warnings) s = "WARNING"; + else if (sev == Diagnostics) s = "Diagnostic"; + else if (sev == Debug) s = "Debug"; + else s = "Message"; + m_log<<QString("%1: %2").arg(s, 13).arg(msg); + } + void addMsg(QString msg) { m_log<<msg; } + void increment(int sev) { + if (sev == Errors) { incErrors(); return; } + if (sev == Warnings) { incWarnings(); return; } + } + void incErrors() { ++m_errors; } + int errors() const { return m_errors; } + bool error() const { return m_errors > 0; } + void incWarnings() { ++m_warnings; } + int warnings() const { return m_warnings; } + bool warning() const { return m_warnings > 0; } + +protected: + Project *m_project; + int m_errors; + int m_warnings; + int m_logLevel; + QStringList m_log; + QDateTime m_starttime; + QTime m_timer; + int m_elapsed; +}; + +} //namespace KPlato + +#endif diff --git a/kplato/main.cc b/kplato/main.cc new file mode 100644 index 00000000..cca5b39d --- /dev/null +++ b/kplato/main.cc @@ -0,0 +1,50 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Thomas zander <zander@kde.org> + + 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. +*/ + +#include "kptaboutdata.h" + +#include <koffice_export.h> +#include <KoApplication.h> +#include <kcmdlineargs.h> +#include <dcopclient.h> + +namespace KPlato +{ + +static const KCmdLineOptions options[]= +{ + {"+[file]", I18N_NOOP("File to open"),0}, + KCmdLineLastOption +}; + +} //KPlato namespace + + +extern "C" KPLATO_EXPORT int kdemain( int argc, char **argv ) { + KCmdLineArgs::init( argc, argv, KPlato::newAboutData()); + KCmdLineArgs::addCmdLineOptions( KPlato::options ); + + KoApplication app; + + // This is disabled for now so the crude test below will run + if (!app.start()) + return 1; + app.exec(); + return 0; +} diff --git a/kplato/pics/Makefile.am b/kplato/pics/Makefile.am new file mode 100644 index 00000000..ef1f8294 --- /dev/null +++ b/kplato/pics/Makefile.am @@ -0,0 +1 @@ +KDE_ICON = kplato diff --git a/kplato/pics/cr128-app-kplato.png b/kplato/pics/cr128-app-kplato.png Binary files differnew file mode 100644 index 00000000..ec95bf31 --- /dev/null +++ b/kplato/pics/cr128-app-kplato.png diff --git a/kplato/pics/cr16-app-kplato.png b/kplato/pics/cr16-app-kplato.png Binary files differnew file mode 100644 index 00000000..8d5ba5a7 --- /dev/null +++ b/kplato/pics/cr16-app-kplato.png diff --git a/kplato/pics/cr22-app-kplato.png b/kplato/pics/cr22-app-kplato.png Binary files differnew file mode 100644 index 00000000..7bdf0562 --- /dev/null +++ b/kplato/pics/cr22-app-kplato.png diff --git a/kplato/pics/cr32-app-kplato.png b/kplato/pics/cr32-app-kplato.png Binary files differnew file mode 100644 index 00000000..4d84b38c --- /dev/null +++ b/kplato/pics/cr32-app-kplato.png diff --git a/kplato/pics/cr48-app-kplato.png b/kplato/pics/cr48-app-kplato.png Binary files differnew file mode 100644 index 00000000..6e0a4cc0 --- /dev/null +++ b/kplato/pics/cr48-app-kplato.png diff --git a/kplato/pics/cr64-app-kplato.png b/kplato/pics/cr64-app-kplato.png Binary files differnew file mode 100644 index 00000000..a09afa12 --- /dev/null +++ b/kplato/pics/cr64-app-kplato.png diff --git a/kplato/pics/crsc-app-kplato.svgz b/kplato/pics/crsc-app-kplato.svgz Binary files differnew file mode 100644 index 00000000..cc8676a5 --- /dev/null +++ b/kplato/pics/crsc-app-kplato.svgz diff --git a/kplato/relationpanel.ui b/kplato/relationpanel.ui new file mode 100644 index 00000000..b9c886d7 --- /dev/null +++ b/kplato/relationpanel.ui @@ -0,0 +1,284 @@ +<!DOCTYPE UI><UI version="3.2" stdsetdef="1"> +<class>KPlato::RelationPanel</class> +<author>Dag Andersen <danders@get2net.dk></author> +<widget class="QWidget"> + <property name="name"> + <cstring>RelationPanel</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>306</width> + <height>232</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>0</width> + <height>0</height> + </size> + </property> + <property name="caption"> + <string>RelationPanel</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>3</number> + </property> + <property name="resizeMode"> + <enum>FreeResize</enum> + </property> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBox1</cstring> + </property> + <property name="minimumSize"> + <size> + <width>300</width> + <height>0</height> + </size> + </property> + <property name="frameShadow"> + <enum>Plain</enum> + </property> + <property name="lineWidth"> + <number>0</number> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="midLineWidth"> + <number>0</number> + </property> + <property name="title"> + <string></string> + </property> + <property name="flat"> + <bool>false</bool> + </property> + <property name="checkable"> + <bool>false</bool> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>5</number> + </property> + <property name="spacing"> + <number>2</number> + </property> + <widget class="QFrame"> + <property name="name"> + <cstring>frame3</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>7</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="frameShape"> + <enum>StyledPanel</enum> + </property> + <property name="frameShadow"> + <enum>Raised</enum> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel2</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>4</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>From:</string> + </property> + <property name="alignment"> + <set>AlignTop</set> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel3</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>4</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>To:</string> + </property> + <property name="alignment"> + <set>AlignTop</set> + </property> + </widget> + <widget class="QLabel" row="0" column="1"> + <property name="name"> + <cstring>fromName</cstring> + </property> + <property name="text"> + <string>Task 1</string> + </property> + <property name="alignment"> + <set>WordBreak|AlignTop</set> + </property> + </widget> + <widget class="QLabel" row="1" column="1"> + <property name="name"> + <cstring>toName</cstring> + </property> + <property name="text"> + <string>Task 2</string> + </property> + <property name="alignment"> + <set>WordBreak|AlignTop</set> + </property> + </widget> + </grid> + </widget> + <widget class="QButtonGroup"> + <property name="name"> + <cstring>relationType</cstring> + </property> + <property name="title"> + <string>Relationship Type</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QRadioButton"> + <property name="name"> + <cstring>radioButton1</cstring> + </property> + <property name="text"> + <string>Finish-Start</string> + </property> + </widget> + <widget class="QRadioButton"> + <property name="name"> + <cstring>radioButton1_2</cstring> + </property> + <property name="text"> + <string>Finish-Finish</string> + </property> + </widget> + <widget class="QRadioButton"> + <property name="name"> + <cstring>radioButton1_3</cstring> + </property> + <property name="text"> + <string>Start-Start</string> + </property> + </widget> + </vbox> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout9</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>Lag:</string> + </property> + <property name="alignment"> + <set>AlignBottom</set> + </property> + </widget> + <widget class="KPlato::DurationWidget"> + <property name="name"> + <cstring>lag</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>0</width> + <height>0</height> + </size> + </property> + </widget> + </vbox> + </widget> + </vbox> + </widget> + </vbox> +</widget> +<customwidgets> + <customwidget> + <class>KPlato::DurationWidget</class> + <header location="local">kptdurationwidget.h</header> + <sizehint> + <width>0</width> + <height>20</height> + </sizehint> + <container>0</container> + <sizepolicy> + <hordata>5</hordata> + <verdata>0</verdata> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + <pixmap>image0</pixmap> + <signal>valueChanged()</signal> + <slot access="public" specifier="">slot()</slot> + <slot access="public" specifier="">setValue( const KPLato::Duration & newDuration )</slot> + <slot access="public" specifier="">slot()</slot> + <slot access="public" specifier="">handleFocus( int field )</slot> + </customwidget> +</customwidgets> +<images> + <image name="image0"> + <data format="PNG" length="1002">89504e470d0a1a0a0000000d4948445200000016000000160806000000c4b46c3b000003b149444154789cad945f4c5b551cc73fe7dc4b7b4bcba0762d45c43114323599ee6192609c51d883892ce083f1718b3ebb185f8dc91e972cf39d2d2a2f1af664b6f1e0fe3863a0718969700eb0c52142da0242a1bd6d696f7bcff101585203ceb8fd9ece39f99dcff9fe7edf939f88c562ec465f5f9fe609442c161362173c3e3eae7b7a7ac8e7f36432196cdbfe4f907c3e4f2291201e8fe338cec3737357e9e8e828aded1e229d650e1f2d51754b082110124c13a4dc5ea341eb9dc284c0558a853f3ce8cb0677ef500fde7d39d2596679e326597b8e9abb85d7a770ab16ab6983ec5a05b487a70e36f0f4e10afe408d6a558310980108478dba4a1e8233990c5d474b64ed39aa3a8fe5f3317fbf81dbd70bccfeb205947632fd74f6589c1c6ea2f70d03a58ba0c1f2c9bdc1b66de3b8256a6e11cbe7e3ee1d181b590124fe2693aeee08d223c82c3a2c24b7b874bec8f26288774f7bd054504aef0dde6e99c0eb83f9fb266323cb80a27fb0958141836044605a2ee5523393371cc646fee2da37195aa35d0c0c5b4859ac03d7e91712dcaac5adab3650a3ff9d08ef7dd8404bb48869e5d958b5b87dadc4c9a1464e9f0d0326df7ebd86bd2e310cb1bf62d384d59441f2d70a070e1c60e09489929b988681bdd9cc97170bcc4c65595f71f8e0e3301337fc24a7732467831875a47f289652b0be5e4151e6d07316c1b0c0340d8ab92023e76d66a6b2840e36d2fb7a13fee632475e6edc367ea98a90fb98b7dd6310ca0328a44761582e1bab41befabcc0ec940d28bc5e93b68e064cab84e1d9beaeb48934eac1f53b01c1b000fca496aa54b61a99fcde61662a4b4b4b23d1680be9d426173e4df3602a48ea411989a4fd590f52a8fd156b05ed9d350e3defe3cfdf4b4c7ce770ea7d3fb9f520afbe1620daeee5c26735d20b9b9cfb6811a754a439e4e5c5639a4caa1e5caf586bfc0197b78702005cb9b4cae4cd3267ce8638fe964bd72b393e39d74928d242617303a756a37f284447770dcdbffc6384a05a85de1306e9a52057c7527c7131c3c42d3f475eb2303c82d4fc3276d6811db37efeb148723082d9b08f79f97c1e5729109a9a28307cc622d2d6cdf52b2b24efe548dedb00142009862cfa879ee1a71f6cec928353511472fbf4389148b0b0e0c108081412458dfe21c9f11351e67e7358595468246d1d1e5e38a6e9e851bc39d84ab502a669331dafec0d8ec7e3e8cb06e1a881d727d1ae40180a434a8c9db129a54126ad48a7358c2b4c5352c8c374bcccdab2bb37d8719cba79fab8211f9df218e0582c261e95f8bfc04f1a1e8bc5c4dfe0a19017a725d8c60000000049454e44ae426082</data> + </image> +</images> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>kptdurationwidget.h</includehint> +</includehints> +</UI> diff --git a/kplato/reports/Makefile.am b/kplato/reports/Makefile.am new file mode 100644 index 00000000..4768252a --- /dev/null +++ b/kplato/reports/Makefile.am @@ -0,0 +1,7 @@ +kplatoreportdir = $(kde_datadir)/kplato/reports +kplatoreport_DATA = resourcelist.desktop tasklist.desktop + +kplatoreportsourcedir = $(kde_datadir)/kplato/reports/.source +kplatoreportsource_DATA = resourcelist.ktf tasklist.ktf + +EXTRA_DIST = $(kplatoreport_DATA) diff --git a/kplato/reports/README b/kplato/reports/README new file mode 100644 index 00000000..ef8da3ff --- /dev/null +++ b/kplato/reports/README @@ -0,0 +1,41 @@ +This method of creating reports is temporary. +In the future kudesigner must be integrated with kplato +or maybe we'll use a totally different report generator. + +Report templates use the same format as kudesigner +and can be read/created (partially) with it. + +To map template fields to kplato data the following deviations exists: +1) A <KPlato> tag to define where to select data from. Ex: + <KPlato> + <Detail Level="0" SelectFrom="resourcegroups" /> + <Detail Level="1" SelectFrom="resources" /> + </KPlato> + will select properties from ResourceGroup into fields defined + in Detail level 0, and properties from Resource into fields defined + in Detail level 1. +2) + When you define a Field, the Field attribute (called Name in kudesigner ui) + is used to select the property from the kplato class. + Ex: + <Field Field="normalrate" /> +3) + You can also get data into fields by specifying <class>.<property>. + Ex: + <Field Field="project.name" /> + +4) + Currently one "global" property exists: currentdate. + So you can specify + <Field Field="currentdate" /> + to present a localized formatted date. + +The method I've used to create reports: +1) + Define the layout using kudesigner. +2) + Save as flat xml file. This gives you a *.kut file. +3) + Use text editor to add the <KPlato> tag. +4) + Save as a *.ktf file. diff --git a/kplato/reports/resourcelist.desktop b/kplato/reports/resourcelist.desktop new file mode 100644 index 00000000..61cb4fc7 --- /dev/null +++ b/kplato/reports/resourcelist.desktop @@ -0,0 +1,45 @@ +[Desktop Entry] +Name=List of Resources +Name[bg]=Списък с ресурси +Name[ca]=Llista de recursos +Name[da]=Liste af ressourcer +Name[de]=List von Ressourcen +Name[el]=Λίστα πόρων +Name[eo]=Listo de risurcoj +Name[es]=Lista de recursos +Name[et]=Ressursside nimekiri +Name[fa]=فهرست منابع +Name[fi]=Resurssiluettelo +Name[fr]=Liste de ressources +Name[fy]=List fan gegevensboarnen +Name[gl]=Lista de Recursos +Name[he]=רשימת מאגרים +Name[hr]=Popis resursa +Name[hu]=Erőforráslista +Name[is]=Listi yfir auðlindir +Name[it]=Elenco di risorse +Name[ja]=リソースのリスト +Name[km]=បញ្ជីធនធាន +Name[lv]=Resursu saraksts +Name[nb]=Ressursliste +Name[nds]=Ressourcenlist +Name[ne]=संसाधनहरूको सूची +Name[nl]=Lijst van gegevensbronnen +Name[pl]=Lista zasobów +Name[pt]=Acoplador de Histórico +Name[pt_BR]=Lista de Recursos +Name[ru]=Список ресурсов +Name[se]=Resursalistu +Name[sk]=Zoznam zdrojov +Name[sl]=Seznam virov +Name[sr]=Листа ресурсâ +Name[sr@Latn]=Lista resursâ +Name[sv]=Resurslista +Name[uk]=Список ресурсів +Name[uz]=Manbalar roʻyxati +Name[uz@cyrillic]=Манбалар рўйхати +Name[zh_CN]=资源列表 +Name[zh_TW]=資源清單 +Type=Link +URL= .source/resourcelist.ktf +X-KDE-Hidden=false diff --git a/kplato/reports/resourcelist.ktf b/kplato/reports/resourcelist.ktf new file mode 100644 index 00000000..925dde6e --- /dev/null +++ b/kplato/reports/resourcelist.ktf @@ -0,0 +1,252 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<!DOCTYPE KugarTemplate SYSTEM "kugartemplate.dtd"> + +<KugarTemplate PageOrientation="1" TopMargin="40" RightMargin="48" BottomMargin="40" LeftMargin="48" PageSize="0" PageWidth="947" PageHeight="669"> + <KPlato> + <Detail Level="0" SelectFrom="resourcegroups" /> + <Detail Level="1" SelectFrom="resources" /> + </KPlato> + <ReportHeader PrintFrequency="0" Height="89"> + <Label Text="List of Resources" + VAlignment="1" HAlignment="1" + BorderStyle="1" WordWrap="0" DrawLeft="false" + X="2" Y="0" ForegroundColor="0,0,0" + BorderColor="0,0,0" FontFamily="Helvetica" + DrawTop="false" FontWeight="75" Height="80" + Width="840" BackgroundColor="0,255,255" FontItalic="1" + BorderWidth="0" DrawRight="false" DrawBottom="false" + FontSize="24" /> + </ReportHeader> + + <PageHeader PrintFrequency="1" Height="70"> + <Label Text="Project name:" + VAlignment="1" HAlignment="0" + BorderStyle="1" WordWrap="0" DrawLeft="false" + X="2" Y="11" ForegroundColor="0,0,0" + BorderColor="0,0,0" FontFamily="Times New Roman" + DrawTop="false" FontWeight="50" Height="10" + Width="550" BackgroundColor="255,255,255" FontItalic="1" + BorderWidth="1" DrawRight="false" DrawBottom="false" + FontSize="10" /> + <Label Text="Project manager:" + VAlignment="1" HAlignment="0" + BorderStyle="1" WordWrap="0" DrawLeft="false" + X="552" Y="11" ForegroundColor="0,0,0" + BorderColor="0,0,0" FontFamily="Times New Roman" + DrawTop="false" FontWeight="50" Height="10" + Width="290" BackgroundColor="255,255,255" FontItalic="1" + BorderWidth="1" DrawRight="false" DrawBottom="false" + FontSize="10" /> + <Field VAlignment="0" HAlignment="0" + CommaSeparator="53" BorderStyle="1" WordWrap="1" + DrawLeft="false" Field="project.name" X="2" + Y="21" ForegroundColor="0,0,0" NegValueColor="0,0,0" + Text="" DataType="0" BorderColor="0,0,0" + DateFormat="11" FontFamily="Courier 10 Pitch" DrawTop="false" + FontWeight="63" InputMask="" Precision="2" + Height="50" Width="540" BackgroundColor="255,255,255" + Currency="53" FontItalic="0" BorderWidth="1" + DrawRight="false" DrawBottom="false" FontSize="15" /> + <Field VAlignment="0" HAlignment="0" + CommaSeparator="53" BorderStyle="1" WordWrap="1" + DrawLeft="false" Field="project.leader" X="552" + Y="21" ForegroundColor="0,0,0" NegValueColor="0,0,0" + Text="" DataType="0" BorderColor="0,0,0" + DateFormat="11" FontFamily="Courier 10 Pitch" DrawTop="false" + FontWeight="75" InputMask="" Precision="2" + Height="50" Width="290" BackgroundColor="255,255,255" + Currency="53" FontItalic="0" BorderWidth="1" + DrawRight="false" DrawBottom="false" FontSize="13" /> + <Label VAlignment="1" HAlignment="0" + BorderStyle="1" WordWrap="0" DrawLeft="false" + X="2" Y="1" ForegroundColor="0,0,0" + Text="" BorderColor="0,0,0" FontFamily="Courier 10 Pitch" + DrawTop="true" FontWeight="50" Height="10" + Width="840" BackgroundColor="255,255,255" FontItalic="0" + BorderWidth="1" DrawRight="false" DrawBottom="false" + FontSize="12" /> + </PageHeader> + + <Detail Level="0" Height="30" Repeat="false"> + <Field VAlignment="1" HAlignment="0" + CommaSeparator="53" BorderStyle="1" WordWrap="0" + DrawLeft="false" Field="name" X="152" + Y="1" ForegroundColor="0,0,0" NegValueColor="0,0,0" + Text="" DataType="0" BorderColor="0,0,0" + DateFormat="11" FontFamily="Courier 10 Pitch" DrawTop="false" + FontWeight="50" InputMask="" Precision="2" + Height="30" Width="690" BackgroundColor="0,255,0" + Currency="53" FontItalic="0" BorderWidth="1" + DrawRight="false" DrawBottom="false" FontSize="12" /> + <Label Text="Resource group:" + VAlignment="1" HAlignment="0" + BorderStyle="1" WordWrap="0" DrawLeft="false" + X="2" Y="1" ForegroundColor="0,0,0" + BorderColor="0,0,0" FontFamily="Courier 10 Pitch" + DrawTop="false" FontWeight="50" Height="30" + Width="150" BackgroundColor="0,255,0" FontItalic="0" + BorderWidth="1" DrawRight="false" DrawBottom="false" + FontSize="12" /> + </Detail> + + <DetailHeader Level="1" Height="20"> + <Label Text="Name" + VAlignment="1" HAlignment="0" + BorderStyle="1" WordWrap="0" DrawLeft="false" + X="2" Y="1" ForegroundColor="0,0,0" + BorderColor="0,0,0" FontFamily="Courier 10 Pitch" + DrawTop="false" FontWeight="50" Height="20" + Width="230" BackgroundColor="192,192,192" FontItalic="0" + BorderWidth="1" DrawRight="false" DrawBottom="false" + FontSize="12" /> + <Label Text="E-mail address" + VAlignment="1" HAlignment="0" + BorderStyle="1" WordWrap="0" DrawLeft="false" + X="232" Y="1" ForegroundColor="0,0,0" + BorderColor="0,0,0" FontFamily="Courier 10 Pitch" + DrawTop="false" FontWeight="50" Height="20" + Width="230" BackgroundColor="192,192,192" FontItalic="0" + BorderWidth="1" DrawRight="false" DrawBottom="false" + FontSize="12" /> + <Label Text="Availability" + VAlignment="1" HAlignment="1" + BorderStyle="1" WordWrap="0" DrawLeft="false" + X="462" Y="1" ForegroundColor="0,0,0" + BorderColor="0,0,0" FontFamily="Courier 10 Pitch" + DrawTop="false" FontWeight="50" Height="20" + Width="210" BackgroundColor="192,192,192" FontItalic="0" + BorderWidth="1" DrawRight="false" DrawBottom="false" + FontSize="12" /> + <Label Text="Rate" + VAlignment="1" HAlignment="1" + BorderStyle="1" WordWrap="0" DrawLeft="false" + X="672" Y="1" ForegroundColor="0,0,0" + BorderColor="0,0,0" FontFamily="Courier 10 Pitch" + DrawTop="false" FontWeight="50" Height="20" + Width="90" BackgroundColor="192,192,192" FontItalic="0" + BorderWidth="1" DrawRight="false" DrawBottom="false" + FontSize="12" /> + <Label Text="Overtime" + VAlignment="1" HAlignment="1" + BorderStyle="1" WordWrap="0" DrawLeft="false" + X="762" Y="1" ForegroundColor="0,0,0" + BorderColor="0,0,0" FontFamily="Courier 10 Pitch" + DrawTop="false" FontWeight="50" Height="20" + Width="80" BackgroundColor="192,192,192" FontItalic="0" + BorderWidth="1" DrawRight="false" DrawBottom="false" + FontSize="12" /> + </DetailHeader> + + <Detail Level="1" Height="30" Repeat="false"> + <Field VAlignment="1" HAlignment="0" + CommaSeparator="53" BorderStyle="1" WordWrap="0" + DrawLeft="false" Field="name" X="2" + Y="1" ForegroundColor="0,0,0" NegValueColor="0,0,0" + Text="" DataType="0" BorderColor="0,0,0" + DateFormat="11" FontFamily="Courier 10 Pitch" DrawTop="false" + FontWeight="50" InputMask="" Precision="2" + Height="20" Width="230" BackgroundColor="255,255,255" + Currency="53" FontItalic="0" BorderWidth="1" + DrawRight="false" DrawBottom="false" FontSize="12" /> + <Field VAlignment="1" HAlignment="0" + CommaSeparator="53" BorderStyle="1" WordWrap="0" + DrawLeft="false" Field="email" X="232" + Y="1" ForegroundColor="0,0,0" NegValueColor="0,0,0" + Text="" DataType="0" BorderColor="0,0,0" + DateFormat="11" FontFamily="Courier 10 Pitch" DrawTop="false" + FontWeight="50" InputMask="" Precision="2" + Height="20" Width="230" BackgroundColor="255,255,255" + Currency="53" FontItalic="0" BorderWidth="1" + DrawRight="false" DrawBottom="false" FontSize="12" /> + <Field VAlignment="1" HAlignment="2" + CommaSeparator="53" BorderStyle="1" WordWrap="0" + DrawLeft="false" Field="availablefrom" X="462" + Y="1" ForegroundColor="0,0,0" NegValueColor="0,0,0" + Text="" DataType="0" BorderColor="0,0,0" + DateFormat="11" FontFamily="Courier 10 Pitch" DrawTop="false" + FontWeight="50" InputMask="" Precision="2" + Height="20" Width="80" BackgroundColor="255,255,255" + Currency="53" FontItalic="0" BorderWidth="1" + DrawRight="false" DrawBottom="false" FontSize="12" /> + <Field VAlignment="1" HAlignment="2" + CommaSeparator="53" BorderStyle="1" WordWrap="0" + DrawLeft="false" Field="availableuntil" X="542" + Y="1" ForegroundColor="0,0,0" NegValueColor="0,0,0" + Text="" DataType="0" BorderColor="0,0,0" + DateFormat="11" FontFamily="Courier 10 Pitch" DrawTop="false" + FontWeight="50" InputMask="" Precision="2" + Height="20" Width="80" BackgroundColor="255,255,255" + Currency="53" FontItalic="0" BorderWidth="1" + DrawRight="false" DrawBottom="false" FontSize="12" /> + <Field VAlignment="1" HAlignment="2" + CommaSeparator="53" BorderStyle="1" WordWrap="0" + DrawLeft="false" Field="units" X="622" + Y="1" ForegroundColor="0,0,0" NegValueColor="0,0,0" + Text="" DataType="0" BorderColor="0,0,0" + DateFormat="11" FontFamily="Courier 10 Pitch" DrawTop="false" + FontWeight="50" InputMask="" Precision="2" + Height="20" Width="40" BackgroundColor="255,255,255" + Currency="53" FontItalic="0" BorderWidth="1" + DrawRight="false" DrawBottom="false" FontSize="12" /> + <Field VAlignment="1" HAlignment="2" + CommaSeparator="53" BorderStyle="1" WordWrap="0" + DrawLeft="false" Field="normalrate" X="672" + Y="1" ForegroundColor="0,0,0" NegValueColor="0,0,0" + Text="" DataType="0" BorderColor="0,0,0" + DateFormat="11" FontFamily="Courier 10 Pitch" DrawTop="false" + FontWeight="50" InputMask="" Precision="2" + Height="20" Width="90" BackgroundColor="255,255,255" + Currency="53" FontItalic="0" BorderWidth="1" + DrawRight="false" DrawBottom="false" FontSize="12" /> + <Field VAlignment="1" HAlignment="2" + CommaSeparator="53" BorderStyle="1" WordWrap="0" + DrawLeft="false" Field="overtimerate" X="762" + Y="1" ForegroundColor="0,0,0" NegValueColor="0,0,0" + Text="" DataType="0" BorderColor="0,0,0" + DateFormat="11" FontFamily="Courier 10 Pitch" DrawTop="false" + FontWeight="50" InputMask="" Precision="2" + Height="20" Width="80" BackgroundColor="255,255,255" + Currency="53" FontItalic="0" BorderWidth="1" + DrawRight="false" DrawBottom="false" FontSize="12" /> + </Detail> + + <PageFooter PrintFrequency="1" Height="30"> + <Label Text="Page:" + VAlignment="1" HAlignment="2" + BorderStyle="0" WordWrap="0" DrawLeft="false" + X="692" Y="11" ForegroundColor="0,0,0" + BorderColor="0,0,0" FontFamily="Helvetica" + DrawTop="true" FontWeight="25" Height="20" + Width="100" BackgroundColor="255,255,255" FontItalic="0" + BorderWidth="1" DrawRight="false" DrawBottom="false" + FontSize="10" /> + <Field VAlignment="1" HAlignment="0" + CommaSeparator="44" BorderStyle="1" WordWrap="0" + DrawLeft="false" Field="currentdate" X="2" + Y="11" ForegroundColor="0,0,0" NegValueColor="0,0,0" + Text="" DataType="0" BorderColor="0,0,0" + DateFormat="11" FontFamily="Courier 10 Pitch" DrawTop="false" + FontWeight="50" InputMask="" Precision="2" + Height="20" Width="390" BackgroundColor="255,255,255" + Currency="32" FontItalic="0" BorderWidth="1" + DrawRight="false" DrawBottom="false" FontSize="10" /> + <Special VAlignment="1" HAlignment="1" + BorderStyle="0" WordWrap="0" DrawLeft="false" + X="792" Y="11" ForegroundColor="0,0,0" + Text="[Sidenr.]" BorderColor="0,0,0" DateFormat="0" + FontFamily="Helvetica" DrawTop="true" FontWeight="25" + Height="20" Type="1" Width="50" + BackgroundColor="255,255,255" FontItalic="0" BorderWidth="1" + DrawRight="false" DrawBottom="false" FontSize="10" /> + <Label VAlignment="1" HAlignment="0" + BorderStyle="1" WordWrap="0" DrawLeft="false" + X="2" Y="1" ForegroundColor="0,0,0" + Text="" BorderColor="0,0,0" FontFamily="Courier 10 Pitch" + DrawTop="true" FontWeight="50" Height="10" + Width="840" BackgroundColor="255,255,255" FontItalic="0" + BorderWidth="1" DrawRight="false" DrawBottom="false" + FontSize="12" /> + </PageFooter> + +</KugarTemplate> diff --git a/kplato/reports/tasklist.desktop b/kplato/reports/tasklist.desktop new file mode 100644 index 00000000..ea64478a --- /dev/null +++ b/kplato/reports/tasklist.desktop @@ -0,0 +1,46 @@ +[Desktop Entry] +Name=List of Tasks +Name[bg]=Списък със задачи +Name[ca]=Llista de tasques +Name[da]=Opgaveliste +Name[de]=Liste von Aufgaben +Name[el]=Λίστα εργασιών +Name[eo]=Listo de taskoj +Name[es]=Lista de tareas +Name[et]=Ülesannete nimekiri +Name[fa]=فهرست تکالیف +Name[fi]=Tehtäväluettelo +Name[fr]=Liste de tâches +Name[fy]=List fan taken +Name[gl]=Lista de Tarefas +Name[he]=רשימת מטלות +Name[hr]=Popis zadataka +Name[hu]=Feladatlista +Name[is]=Listi yfir verkefni +Name[it]=Elenco di attività +Name[ja]=タスクのリスト +Name[km]=បញ្ជីភារកិច្ច +Name[lt]=Užduočių sąrašas +Name[lv]=Darbu saraksts +Name[nb]=Oppgaveliste +Name[nds]=Opgavenlist +Name[ne]=कार्यहरूको सूची +Name[nl]=Lijst van taken +Name[pl]=Lista zadań +Name[pt]=Lista de Tarefas +Name[pt_BR]=Lista de Tarefas +Name[ru]=Список задач +Name[se]=Bargolistu +Name[sk]=Zoznam úloh +Name[sl]=Seznam opravil +Name[sr]=Листа задатака +Name[sr@Latn]=Lista zadataka +Name[sv]=Aktivitetslista +Name[uk]=Список завдань +Name[uz]=Vazifalar roʻyxati +Name[uz@cyrillic]=Вазифалар рўйхати +Name[zh_CN]=任务列表 +Name[zh_TW]=工作清單 +Type=Link +URL= .source/tasklist.ktf +X-KDE-Hidden=false diff --git a/kplato/reports/tasklist.ktf b/kplato/reports/tasklist.ktf new file mode 100644 index 00000000..1d58fcc8 --- /dev/null +++ b/kplato/reports/tasklist.ktf @@ -0,0 +1,225 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<!DOCTYPE KugarTemplate SYSTEM "kugartemplate.dtd"> + +<KugarTemplate PageOrientation="1" TopMargin="40" RightMargin="48" BottomMargin="40" LeftMargin="48" PageSize="0" PageWidth="947" PageHeight="669"> + <KPlato> + <Detail Level="0" SelectFrom="alltasks" /> + </KPlato> + <ReportHeader PrintFrequency="0" Height="89"> + <Label Text="List of Tasks" + VAlignment="1" HAlignment="1" + BorderStyle="1" WordWrap="0" DrawLeft="false" + X="2" Y="0" ForegroundColor="0,0,0" + BorderColor="0,0,0" FontFamily="Helvetica" + DrawTop="false" FontWeight="75" Height="80" + Width="840" BackgroundColor="0,255,255" FontItalic="1" + BorderWidth="0" DrawRight="false" DrawBottom="false" + FontSize="24" /> + </ReportHeader> + + <PageHeader PrintFrequency="1" Height="126"> + <Label Text="Name" + VAlignment="1" HAlignment="0" Text="Name" + BorderStyle="1" WordWrap="0" DrawLeft="false" + X="72" Y="101" ForegroundColor="0,0,0" + BorderColor="0,0,0" FontFamily="Courier 10 Pitch" + DrawTop="false" FontWeight="50" Height="20" + Width="270" BackgroundColor="192,192,192" FontItalic="0" + BorderWidth="1" DrawRight="false" DrawBottom="false" + FontSize="12" /> + <Label Text="Duration" + VAlignment="1" HAlignment="2" + BorderStyle="1" WordWrap="0" DrawLeft="false" + X="642" Y="101" ForegroundColor="0,0,0" + BorderColor="0,0,0" FontFamily="Courier 10 Pitch" + DrawTop="false" FontWeight="50" Height="20" + Width="100" BackgroundColor="192,192,192" FontItalic="0" + BorderWidth="1" DrawRight="false" DrawBottom="false" + FontSize="12" /> + <Label Text="Cost" + VAlignment="1" HAlignment="1" + BorderStyle="1" WordWrap="0" DrawLeft="false" + X="742" Y="101" ForegroundColor="0,0,0" + BorderColor="0,0,0" FontFamily="Courier 10 Pitch" + DrawTop="false" FontWeight="50" Height="20" + Width="110" BackgroundColor="192,192,192" FontItalic="0" + BorderWidth="1" DrawRight="false" DrawBottom="false" + FontSize="12" /> + <Label Text="Project name:" + VAlignment="1" HAlignment="0" + BorderStyle="1" WordWrap="0" DrawLeft="false" + X="2" Y="11" ForegroundColor="0,0,0" + BorderColor="0,0,0" FontFamily="Times New Roman" + DrawTop="false" FontWeight="50" Height="20" + Width="560" BackgroundColor="255,255,255" FontItalic="1" + BorderWidth="1" DrawRight="false" DrawBottom="false" + FontSize="10" /> + <Label Text="Start" + VAlignment="1" HAlignment="1" + BorderStyle="1" WordWrap="0" DrawLeft="false" + X="532" Y="101" ForegroundColor="0,0,0" + BorderColor="0,0,0" FontFamily="Courier 10 Pitch" + DrawTop="false" FontWeight="50" Height="20" + Width="110" BackgroundColor="192,192,192" FontItalic="0" + BorderWidth="1" DrawRight="false" DrawBottom="false" + FontSize="12" /> + <Label Text="Project leader:" + VAlignment="1" HAlignment="0" + BorderStyle="1" WordWrap="0" DrawLeft="false" + X="572" Y="11" ForegroundColor="0,0,0" + BorderColor="0,0,0" FontFamily="Times New Roman" + DrawTop="false" FontWeight="50" Height="20" + Width="280" BackgroundColor="255,255,255" FontItalic="1" + BorderWidth="1" DrawRight="false" DrawBottom="false" + FontSize="10" /> + <Label Text="WBS" + VAlignment="1" HAlignment="0" + BorderStyle="1" WordWrap="0" DrawLeft="false" + X="2" Y="101" ForegroundColor="0,0,0" + BorderColor="0,0,0" FontFamily="Courier 10 Pitch" + DrawTop="false" FontWeight="50" Height="20" + Width="70" BackgroundColor="192,192,192" FontItalic="0" + BorderWidth="1" DrawRight="false" DrawBottom="false" + FontSize="12" /> + <Field VAlignment="0" HAlignment="0" + CommaSeparator="53" BorderStyle="1" WordWrap="1" + DrawLeft="false" Field="project.name" X="2" + Y="31" ForegroundColor="0,0,0" NegValueColor="0,0,0" + Text="" DataType="0" BorderColor="0,0,0" + DateFormat="11" FontFamily="Courier 10 Pitch" DrawTop="false" + FontWeight="63" InputMask="" Precision="2" + Height="60" Width="560" BackgroundColor="255,255,255" + Currency="53" FontItalic="0" BorderWidth="1" + DrawRight="false" DrawBottom="false" FontSize="15" /> + <Label VAlignment="1" HAlignment="0" + BorderStyle="1" WordWrap="0" DrawLeft="false" + X="2" Y="1" ForegroundColor="0,0,0" + Text="" BorderColor="0,0,0" FontFamily="Courier 10 Pitch" + DrawTop="true" FontWeight="50" Height="10" + Width="840" BackgroundColor="255,255,255" FontItalic="0" + BorderWidth="1" DrawRight="false" DrawBottom="false" + FontSize="12" /> + <Label Text="Responsible" + VAlignment="1" HAlignment="0" + BorderStyle="1" WordWrap="0" DrawLeft="false" + X="342" Y="101" ForegroundColor="0,0,0" + BorderColor="0,0,0" FontFamily="Courier 10 Pitch" + DrawTop="false" FontWeight="50" Height="20" + Width="190" BackgroundColor="192,192,192" FontItalic="0" + BorderWidth="1" DrawRight="false" DrawBottom="false" + FontSize="12" /> + <Field VAlignment="0" HAlignment="0" + CommaSeparator="52" BorderStyle="1" WordWrap="0" + DrawLeft="false" Field="project.leader" X="572" + Y="31" ForegroundColor="0,0,0" NegValueColor="0,0,0" + Text="" DataType="0" BorderColor="0,0,0" + DateFormat="11" FontFamily="Courier 10 Pitch" DrawTop="false" + FontWeight="63" InputMask="" Precision="2" + Height="60" Width="270" BackgroundColor="255,255,255" + Currency="51" FontItalic="0" BorderWidth="1" + DrawRight="false" DrawBottom="false" FontSize="15" /> + </PageHeader> + + <Detail Level="0" Height="25" Repeat="false"> + <Field VAlignment="1" HAlignment="0" + CommaSeparator="53" BorderStyle="1" WordWrap="0" + DrawLeft="false" Field="name" X="72" + Y="5" ForegroundColor="0,0,0" NegValueColor="255,0,0" + Text="" DataType="0" BorderColor="0,0,0" + DateFormat="0" FontFamily="Courier 10 Pitch" DrawTop="false" + FontWeight="50" InputMask="" Precision="0" + Height="20" Width="270" BackgroundColor="255,255,255" + Currency="53" FontItalic="0" BorderWidth="1" + DrawRight="false" DrawBottom="false" FontSize="12" /> + <Field VAlignment="1" HAlignment="2" + CommaSeparator="53" BorderStyle="1" WordWrap="0" + DrawLeft="false" Field="duration" X="642" + Y="5" ForegroundColor="0,0,0" NegValueColor="255,0,0" + Text="" DataType="0" BorderColor="0,0,0" + DateFormat="0" FontFamily="Courier 10 Pitch" DrawTop="false" + FontWeight="25" InputMask="" Precision="0" + Height="20" Width="100" BackgroundColor="255,255,255" + Currency="53" FontItalic="0" BorderWidth="1" + DrawRight="false" DrawBottom="false" FontSize="12" /> + <Field VAlignment="1" HAlignment="2" + CommaSeparator="53" BorderStyle="1" WordWrap="0" + DrawLeft="false" Field="plannedcost" X="742" + Y="5" ForegroundColor="0,0,0" NegValueColor="255,0,0" + Text="" DataType="0" BorderColor="0,0,0" + DateFormat="0" FontFamily="Courier 10 Pitch" DrawTop="false" + FontWeight="50" InputMask="" Precision="0" + Height="20" Width="110" BackgroundColor="255,255,255" + Currency="53" FontItalic="0" BorderWidth="1" + DrawRight="false" DrawBottom="false" FontSize="12" /> + <Field VAlignment="1" HAlignment="1" + CommaSeparator="53" BorderStyle="1" WordWrap="0" + DrawLeft="false" Field="startdate" X="532" + Y="5" ForegroundColor="0,0,0" NegValueColor="0,0,0" + Text="" DataType="0" BorderColor="0,0,0" + DateFormat="11" FontFamily="Courier 10 Pitch" DrawTop="false" + FontWeight="50" InputMask="" Precision="2" + Height="20" Width="110" BackgroundColor="255,255,255" + Currency="53" FontItalic="0" BorderWidth="1" + DrawRight="false" DrawBottom="false" FontSize="12" /> + <Field VAlignment="1" HAlignment="0" + CommaSeparator="53" BorderStyle="1" WordWrap="0" + DrawLeft="false" Field="wbs" X="2" + Y="5" ForegroundColor="0,0,0" NegValueColor="0,0,0" + Text="" DataType="0" BorderColor="0,0,0" + DateFormat="11" FontFamily="Courier 10 Pitch" DrawTop="false" + FontWeight="50" InputMask="" Precision="2" + Height="20" Width="70" BackgroundColor="255,255,255" + Currency="53" FontItalic="0" BorderWidth="1" + DrawRight="false" DrawBottom="false" FontSize="12" /> + <Field VAlignment="1" HAlignment="0" + CommaSeparator="53" BorderStyle="1" WordWrap="0" + DrawLeft="false" Field="responsible" X="342" + Y="5" ForegroundColor="0,0,0" NegValueColor="0,0,0" + Text="" DataType="0" BorderColor="0,0,0" + DateFormat="11" FontFamily="Courier 10 Pitch" DrawTop="false" + FontWeight="50" InputMask="" Precision="2" + Height="20" Width="180" BackgroundColor="255,255,255" + Currency="53" FontItalic="0" BorderWidth="1" + DrawRight="false" DrawBottom="false" FontSize="12" /> + </Detail> + + <PageFooter PrintFrequency="1" Height="31"> + <Label Text="Page:" + VAlignment="1" HAlignment="2" + BorderStyle="0" WordWrap="0" DrawLeft="false" + X="712" Y="10" ForegroundColor="0,0,0" + BorderColor="0,0,0" FontFamily="Helvetica" + DrawTop="false" FontWeight="25" Height="20" + Width="80" BackgroundColor="255,255,255" FontItalic="0" + BorderWidth="1" DrawRight="false" DrawBottom="false" + FontSize="10" /> + <Special VAlignment="1" HAlignment="1" + BorderStyle="0" WordWrap="0" DrawLeft="false" + X="802" Y="10" ForegroundColor="0,0,0" + Text="[Sidenr.]" BorderColor="0,0,0" DateFormat="0" + FontFamily="Helvetica" DrawTop="false" FontWeight="25" + Height="20" Type="1" Width="40" + BackgroundColor="255,255,255" FontItalic="0" BorderWidth="1" + DrawRight="false" DrawBottom="false" FontSize="10" /> + <Field VAlignment="1" HAlignment="0" + CommaSeparator="44" BorderStyle="1" WordWrap="0" + DrawLeft="false" Field="currentdate" X="2" + Y="10" ForegroundColor="0,0,0" NegValueColor="0,0,0" + Text="" DataType="0" BorderColor="0,0,0" + DateFormat="11" FontFamily="Courier 10 Pitch" DrawTop="false" + FontWeight="50" InputMask="" Precision="2" + Height="20" Width="290" BackgroundColor="255,255,255" + Currency="32" FontItalic="0" BorderWidth="1" + DrawRight="false" DrawBottom="false" FontSize="10" /> + <Label VAlignment="1" HAlignment="0" + BorderStyle="1" WordWrap="0" DrawLeft="false" + X="2" Y="0" ForegroundColor="0,0,0" + Text="" BorderColor="0,0,0" FontFamily="Courier 10 Pitch" + DrawTop="true" FontWeight="50" Height="10" + Width="840" BackgroundColor="255,255,255" FontItalic="0" + BorderWidth="1" DrawRight="false" DrawBottom="false" + FontSize="12" /> + </PageFooter> + +</KugarTemplate> diff --git a/kplato/resourcedialogbase.ui b/kplato/resourcedialogbase.ui new file mode 100644 index 00000000..1427744f --- /dev/null +++ b/kplato/resourcedialogbase.ui @@ -0,0 +1,397 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>KPlato::ResourceDialogBase</class> +<author>Dag Andersen <danders@get2net.dk></author> +<widget class="QWidget"> + <property name="name"> + <cstring>ResourceDialogBase</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>576</width> + <height>265</height> + </rect> + </property> + <property name="caption"> + <string>ResourceDialogBase</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QFrame"> + <property name="name"> + <cstring>frame3</cstring> + </property> + <property name="frameShape"> + <enum>StyledPanel</enum> + </property> + <property name="frameShadow"> + <enum>Raised</enum> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>Name:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>nameEdit</cstring> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel1_2</cstring> + </property> + <property name="text"> + <string>Initials:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>initialsEdit</cstring> + </property> + </widget> + <widget class="QLayoutWidget" row="1" column="1"> + <property name="name"> + <cstring>layout11</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLineEdit"> + <property name="name"> + <cstring>initialsEdit</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>0</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="maximumSize"> + <size> + <width>100</width> + <height>32767</height> + </size> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1_2_2</cstring> + </property> + <property name="text"> + <string><p align="right">Email:</p></string> + </property> + <property name="buddy" stdset="0"> + <cstring>emailEdit</cstring> + </property> + </widget> + <widget class="QLineEdit"> + <property name="name"> + <cstring>emailEdit</cstring> + </property> + <property name="text"> + <string></string> + </property> + </widget> + </hbox> + </widget> + <widget class="QLayoutWidget" row="0" column="1"> + <property name="name"> + <cstring>layout12</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLineEdit"> + <property name="name"> + <cstring>nameEdit</cstring> + </property> + <property name="focusPolicy"> + <enum>WheelFocus</enum> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>chooseBtn</cstring> + </property> + <property name="text"> + <string>Choose...</string> + </property> + <property name="toolTip" stdset="0"> + <string>Choose resource from addressbook</string> + </property> + </widget> + </hbox> + </widget> + </grid> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout7</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel7</cstring> + </property> + <property name="text"> + <string>Resource type:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>type</cstring> + </property> + </widget> + <widget class="QComboBox"> + <item> + <property name="text"> + <string>Work</string> + </property> + </item> + <item> + <property name="text"> + <string>Material</string> + </property> + </item> + <property name="name"> + <cstring>type</cstring> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel2</cstring> + </property> + <property name="text"> + <string><p align="right">Calendar:</p></string> + </property> + <property name="buddy" stdset="0"> + <cstring>calendarList</cstring> + </property> + </widget> + <widget class="QComboBox"> + <property name="name"> + <cstring>calendarList</cstring> + </property> + </widget> + </hbox> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout6</cstring> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel3</cstring> + </property> + <property name="text"> + <string>Available:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>units</cstring> + </property> + </widget> + <widget class="KDateTimeWidget" row="1" column="3"> + <property name="name"> + <cstring>availableUntil</cstring> + </property> + <property name="focusPolicy"> + <enum>WheelFocus</enum> + </property> + </widget> + <widget class="QLabel" row="0" column="2"> + <property name="name"> + <cstring>textLabel1_3</cstring> + </property> + <property name="text"> + <string><p align="right">From:</p></string> + </property> + <property name="buddy" stdset="0"> + <cstring>availableFrom</cstring> + </property> + </widget> + <widget class="QSpinBox" row="0" column="1"> + <property name="name"> + <cstring>units</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="focusPolicy"> + <enum>WheelFocus</enum> + </property> + <property name="suffix"> + <string>%</string> + </property> + <property name="maxValue"> + <number>9999</number> + </property> + </widget> + <spacer row="1" column="0"> + <property name="name"> + <cstring>spacer2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QLabel" row="1" column="2"> + <property name="name"> + <cstring>textLabel1_3_2</cstring> + </property> + <property name="text"> + <string><p align="right">Until:</p></string> + </property> + <property name="buddy" stdset="0"> + <cstring>availableUntil</cstring> + </property> + </widget> + <widget class="KDateTimeWidget" row="0" column="3"> + <property name="name"> + <cstring>availableFrom</cstring> + </property> + <property name="focusPolicy"> + <enum>WheelFocus</enum> + </property> + </widget> + </grid> + </widget> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBox3_2</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="title"> + <string>Cost</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel3_2</cstring> + </property> + <property name="text"> + <string>Hourly rate:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>rateEdit</cstring> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel4_2</cstring> + </property> + <property name="text"> + <string>Overtime rate:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>overtimeEdit</cstring> + </property> + </widget> + <widget class="QLineEdit" row="0" column="1"> + <property name="name"> + <cstring>rateEdit</cstring> + </property> + <property name="focusPolicy"> + <enum>WheelFocus</enum> + </property> + </widget> + <widget class="QLineEdit" row="1" column="1"> + <property name="name"> + <cstring>overtimeEdit</cstring> + </property> + <property name="focusPolicy"> + <enum>WheelFocus</enum> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <spacer row="1" column="2"> + <property name="name"> + <cstring>spacer10</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </grid> + </widget> + </vbox> +</widget> +<customwidgets> +</customwidgets> +<tabstops> + <tabstop>nameEdit</tabstop> + <tabstop>chooseBtn</tabstop> + <tabstop>initialsEdit</tabstop> + <tabstop>emailEdit</tabstop> + <tabstop>type</tabstop> + <tabstop>calendarList</tabstop> + <tabstop>units</tabstop> + <tabstop>availableFrom</tabstop> + <tabstop>availableUntil</tabstop> + <tabstop>rateEdit</tabstop> + <tabstop>overtimeEdit</tabstop> +</tabstops> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>kdatetimewidget.h</includehint> + <includehint>kdatewidget.h</includehint> + <includehint>ktimewidget.h</includehint> + <includehint>kdatetimewidget.h</includehint> + <includehint>kdatewidget.h</includehint> + <includehint>ktimewidget.h</includehint> +</includehints> +</UI> diff --git a/kplato/resourcespanelbase.ui b/kplato/resourcespanelbase.ui new file mode 100644 index 00000000..dd5ecb72 --- /dev/null +++ b/kplato/resourcespanelbase.ui @@ -0,0 +1,195 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>KPlato::ResourcesPanelBase</class> +<author>Dag Andersen <danders@get2net.dk></author> +<widget class="QWidget"> + <property name="name"> + <cstring>ResourcesPanelBase</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>510</width> + <height>227</height> + </rect> + </property> + <property name="minimumSize"> + <size> + <width>500</width> + <height>200</height> + </size> + </property> + <property name="caption"> + <string>ResourcesPanelBase</string> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout5</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KListView"> + <column> + <property name="text"> + <string>Resource Group</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <property name="name"> + <cstring>listOfGroups</cstring> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout1</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QPushButton"> + <property name="name"> + <cstring>bAdd</cstring> + </property> + <property name="text"> + <string>&New</string> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>bRemove</cstring> + </property> + <property name="text"> + <string>&Remove</string> + </property> + </widget> + </hbox> + </widget> + </vbox> + </widget> + <widget class="QGroupBox"> + <property name="name"> + <cstring>resourceGroupBox</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>1</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="title"> + <string>Resource</string> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout51</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLineEdit"> + <property name="name"> + <cstring>resourceName</cstring> + </property> + </widget> + <widget class="QListBox"> + <property name="name"> + <cstring>listOfResources</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>7</hsizetype> + <vsizetype>7</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </vbox> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout5</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QPushButton"> + <property name="name"> + <cstring>bAddResource</cstring> + </property> + <property name="text"> + <string>New...</string> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>bEditResource</cstring> + </property> + <property name="text"> + <string>Edit...</string> + </property> + <property name="accel"> + <string></string> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>bRemoveResource</cstring> + </property> + <property name="text"> + <string>Remove</string> + </property> + <property name="accel"> + <string></string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer9</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>220</height> + </size> + </property> + </spacer> + </vbox> + </widget> + </hbox> + </widget> + </hbox> +</widget> +<customwidgets> +</customwidgets> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>klistview.h</includehint> +</includehints> +</UI> diff --git a/kplato/standardworktimedialogbase.ui b/kplato/standardworktimedialogbase.ui new file mode 100644 index 00000000..adf39c2c --- /dev/null +++ b/kplato/standardworktimedialogbase.ui @@ -0,0 +1,290 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>KPlato::StandardWorktimeDialogBase</class> +<author>Dag Andersen </author> +<widget class="QWidget"> + <property name="name"> + <cstring>StandardWorktimeDialogBase</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>417</width> + <height>368</height> + </rect> + </property> + <property name="caption"> + <string>StandardWorktime</string> + </property> + <property name="whatsThis" stdset="0"> + <string>These values are used when you estimate the effort needed to complete a task.</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout10</cstring> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="3" column="0"> + <property name="name"> + <cstring>textLabel4</cstring> + </property> + <property name="text"> + <string>Hours per day:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>day</cstring> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel2</cstring> + </property> + <property name="text"> + <string>Hours per month:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>month</cstring> + </property> + </widget> + <widget class="QLabel" row="2" column="0"> + <property name="name"> + <cstring>textLabe3</cstring> + </property> + <property name="text"> + <string>Hours per week:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>week</cstring> + </property> + </widget> + <widget class="KDoubleSpinBox" row="1" column="1"> + <property name="name"> + <cstring>month</cstring> + </property> + <property name="focusPolicy"> + <enum>WheelFocus</enum> + </property> + <property name="maxValue"> + <number>744</number> + </property> + <property name="minValue"> + <number>1</number> + </property> + <property name="toolTip" stdset="0"> + <string>Number of working hours in a normal month.</string> + </property> + </widget> + <widget class="KDoubleSpinBox" row="3" column="1"> + <property name="name"> + <cstring>day</cstring> + </property> + <property name="focusPolicy"> + <enum>WheelFocus</enum> + </property> + <property name="maxValue"> + <number>24</number> + </property> + <property name="minValue"> + <number>1</number> + </property> + <property name="toolTip" stdset="0"> + <string>Number of working hours in a normal day.</string> + </property> + </widget> + <widget class="KDoubleSpinBox" row="0" column="1"> + <property name="name"> + <cstring>year</cstring> + </property> + <property name="focusPolicy"> + <enum>WheelFocus</enum> + </property> + <property name="maxValue"> + <number>8784</number> + </property> + <property name="minValue"> + <number>1</number> + </property> + <property name="toolTip" stdset="0"> + <string>Number of working hours in a normal year.</string> + </property> + </widget> + <widget class="KDoubleSpinBox" row="2" column="1"> + <property name="name"> + <cstring>week</cstring> + </property> + <property name="focusPolicy"> + <enum>WheelFocus</enum> + </property> + <property name="maxValue"> + <number>168</number> + </property> + <property name="minValue"> + <number>1</number> + </property> + <property name="toolTip" stdset="0"> + <string>Number of working hours in a normal week.</string> + </property> + </widget> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>Hours per year:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>year</cstring> + </property> + </widget> + </grid> + </widget> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBox1</cstring> + </property> + <property name="title"> + <string>Working Hours</string> + </property> + <property name="toolTip" stdset="0"> + <string>Define standard weekly working hours.</string> + </property> + <property name="whatsThis" stdset="0"> + <string>The working hours defined here will be used +when there is no calendar defined for a resource.</string> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KListView"> + <column> + <property name="text"> + <string>Weekday</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <column> + <property name="text"> + <string>Hours</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <property name="name"> + <cstring>weekdayList</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>7</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>160</width> + <height>210</height> + </size> + </property> + <property name="selectionMode" stdset="0"> + <enum>Extended</enum> + </property> + <property name="itemMargin"> + <number>4</number> + </property> + </widget> + <widget class="QGroupBox"> + <property name="name"> + <cstring>editBox</cstring> + </property> + <property name="title"> + <string></string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KComboBox" row="0" column="0"> + <item> + <property name="text"> + <string>Non-working</string> + </property> + </item> + <item> + <property name="text"> + <string>Working</string> + </property> + </item> + <property name="name"> + <cstring>state</cstring> + </property> + </widget> + <widget class="QPushButton" row="0" column="1"> + <property name="name"> + <cstring>bApply</cstring> + </property> + <property name="text"> + <string>Apply</string> + </property> + </widget> + <widget class="QGroupBox" row="1" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>intervalBox</cstring> + </property> + <property name="frameShape"> + <enum>GroupBoxPanel</enum> + </property> + <property name="frameShadow"> + <enum>Sunken</enum> + </property> + <property name="lineWidth"> + <number>1</number> + </property> + <property name="title"> + <string></string> + </property> + </widget> + </grid> + </widget> + </hbox> + </widget> + </vbox> +</widget> +<customwidgets> +</customwidgets> +<tabstops> + <tabstop>day</tabstop> + <tabstop>weekdayList</tabstop> + <tabstop>state</tabstop> + <tabstop>bApply</tabstop> + <tabstop>year</tabstop> + <tabstop>month</tabstop> + <tabstop>week</tabstop> +</tabstops> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>knuminput.h</includehint> + <includehint>klistview.h</includehint> + <includehint>kcombobox.h</includehint> +</includehints> +</UI> diff --git a/kplato/templates/Makefile.am b/kplato/templates/Makefile.am new file mode 100644 index 00000000..208f4017 --- /dev/null +++ b/kplato/templates/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = Simple diff --git a/kplato/templates/Simple/.directory b/kplato/templates/Simple/.directory new file mode 100644 index 00000000..a3bc1503 --- /dev/null +++ b/kplato/templates/Simple/.directory @@ -0,0 +1,60 @@ +[Desktop Entry] +Name=Simple +Name[af]=Eenvoudige +Name[az]=Bəsit +Name[bg]=Обикновен +Name[br]=Eeun +Name[bs]=Jednostavno +Name[cy]=Syml +Name[da]=Simpel +Name[de]=Einfach +Name[el]=Απλό +Name[eo]=Simpla +Name[es]=Sencillo +Name[et]=Lihtne +Name[fa]=ساده +Name[fi]=Yksinkertainen +Name[fy]=Ienfâldich +Name[ga]=Simplí +Name[gl]=Simples +Name[he]=פשוט +Name[hi]=साधारण +Name[hr]=Jednostavno +Name[hu]=Egyszerű +Name[id]=Sederhana +Name[is]=Einfalt +Name[it]=Semplice +Name[ja]=シンプル +Name[km]=សាមញ្ញ +Name[lo]=ເອກກະສານທົ່ວໄປ +Name[lv]=Vienkāršs +Name[mk]=Едноставна +Name[mt]=Sempliċi +Name[nb]=Enkel +Name[nds]=Eenfach +Name[ne]=साधारण +Name[nl]=Eenvoudig +Name[pl]=Prosty +Name[pt]=Simples +Name[pt_BR]=Simples +Name[ru]=Простой +Name[se]=Oktageardanis +Name[sk]=Jednoduchý +Name[sl]=Preporosto +Name[sr]=Једноставно +Name[sr@Latn]=Jednostavno +Name[sv]=Enkel +Name[ta]=எளிய +Name[tg]=Оддӣ +Name[th]=เอกสารทั่วไป +Name[tr]=Basit +Name[uk]=Простий +Name[uz]=Oddiy +Name[uz@cyrillic]=Оддий +Name[ven]=Zwoleluwa +Name[wa]=Simpe +Name[xh]=Lula +Name[zh_CN]=简单 +Name[zh_TW]=簡單 +Name[zu]=Okulula +X-KDE-DefaultTab=true diff --git a/kplato/templates/Simple/8HourDay-40HourWeek.desktop b/kplato/templates/Simple/8HourDay-40HourWeek.desktop new file mode 100644 index 00000000..f46c5729 --- /dev/null +++ b/kplato/templates/Simple/8HourDay-40HourWeek.desktop @@ -0,0 +1,44 @@ +[Desktop Entry] +Type=Link +URL=.source/8HourDay-40HourWeek.kplatot +Name=8 hour day, 40 hour week +Name[bg]=8 часа дневно, 40 часа седмично +Name[ca]=Dia de 8 hores, setmana de 40 hores +Name[da]=8-timers dag, 40-timers uge +Name[de]=8 Stunden / Tag, 40 Stunden / Woche +Name[el]=8ωρη ημέρα, 40ωρη εβδομάδα +Name[es]=8 horas al día, 40 horas a la semana +Name[et]=8 tundi päevas, 40 tundi nädalas +Name[fa]=۸ ساعت در روز، ۴۰ ساعت در هفته +Name[fi]=8-tuntinen päivä, 40-tuntinen viikko +Name[fr]=8 heures par jour, 40 heures par semaine +Name[fy]=8 oere de dei, 40 oeren de wike +Name[gl]=8 horas por día, con semana de 40 horas +Name[he]=שמונה שעות ביום, ארבעים שעות בשבוע +Name[hu]=Naponta 8 óra, 40 órás hét +Name[is]=8 tíma dagur, 40 tíma vika +Name[it]=Giorno di 8 ore, Settimana di 40 ore +Name[ja]=1日8時間、週40時間 +Name[km]= ៨ ម៉ោងក្នុងមួយថ្ងៃ, ៤០ ម៉ោងក្នុងមួយសប្តាហ៍ +Name[lt]=8 valandų diena, 40 valandų savaitė +Name[lv]=8 stundu diena, 40 stundu nedēļa +Name[nb]=8-timers dag, 40-timers uke +Name[nds]=8 Stünnen-Dag, 40 Stünnen-Week +Name[ne]=दिनको ८ घण्टा, हप्ताको ४० घण्टा +Name[nl]=8-urige dag, 40-urige week +Name[pl]=8 godzin na dzień, 40 na tydzień +Name[pt]=8 horas por dia, com semana de 40 horas +Name[pt_BR]=8 horas por dia, com semana de 40 horas +Name[ru]=8-часовой рабочий день, 40-часовая рабочая неделя +Name[se]=8 diimmusaš beaivi, 40 diimmusaš vahkku +Name[sk]=8 hodín denne, 40 hodín týždenne +Name[sl]=8 urni dan, 40 urni teden +Name[sr]=8-часовни дан, 40-часовна седмица +Name[sr@Latn]=8-časovni dan, 40-časovna sedmica +Name[sv]=8-timmarsdag, 40-timmarsvecka +Name[uk]=8 год. на день, 40 год. на тиждень +Name[uz]=8 soatlik ish kuni, 40 soatlik ish haftasi +Name[uz@cyrillic]=8 соатлик иш куни, 40 соатлик иш ҳафтаси +Name[zh_CN]=每天八小时,每周40小时 +Name[zh_TW]=一天八小時,一週四十小時 +Icon=template_timechart diff --git a/kplato/templates/Simple/8HourDay-40HourWeek.kplatot b/kplato/templates/Simple/8HourDay-40HourWeek.kplatot new file mode 100644 index 00000000..3759e1bf --- /dev/null +++ b/kplato/templates/Simple/8HourDay-40HourWeek.kplatot @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE kplato> +<kplato mime="application/x-vnd.kde.kplato" version="0.4" editor="KPlato" > + <project id="0" scheduling="MustStartOn" > + <standard-worktime day="8h0m" week="40h0m" month="176h0m" year="1760h0m" > + <calendar id="-1" name="Base" > + <weekday day="0" state="2" > + <interval end="16:00:00" start="08:00:00" /> + </weekday> + <weekday day="1" state="2" > + <interval end="16:00:00" start="08:00:00" /> + </weekday> + <weekday day="2" state="2" > + <interval end="16:00:00" start="08:00:00" /> + </weekday> + <weekday day="3" state="2" > + <interval end="16:00:00" start="08:00:00" /> + </weekday> + <weekday day="4" state="2" > + <interval end="16:00:00" start="08:00:00" /> + </weekday> + <weekday day="5" state="1" /> + <weekday day="6" state="1" /> + </calendar> + </standard-worktime> + </project> +</kplato> diff --git a/kplato/templates/Simple/Makefile.am b/kplato/templates/Simple/Makefile.am new file mode 100644 index 00000000..5cc4b855 --- /dev/null +++ b/kplato/templates/Simple/Makefile.am @@ -0,0 +1,9 @@ +template_DATA = .directory Plain.desktop 8HourDay-40HourWeek.desktop +templatedir = $(kde_datadir)/kplato/templates/Simple + +templatesrc_DATA = Plain.kplatot 8HourDay-40HourWeek.kplatot +templatesrcdir = $(kde_datadir)/kplato/templates/Simple/.source + +kplatoicondir = $(kde_datadir)/kplato/icons +kplatoicon_ICON = AUTO + diff --git a/kplato/templates/Simple/Plain.desktop b/kplato/templates/Simple/Plain.desktop new file mode 100644 index 00000000..a739375f --- /dev/null +++ b/kplato/templates/Simple/Plain.desktop @@ -0,0 +1,70 @@ +[Desktop Entry] +Type=Link +URL=.source/Plain.kplatot +Name=Plain +Name[af]=Eenvoudig +Name[ar]=خام +Name[az]=Düz +Name[bg]=Обикновен +Name[br]=Kompez +Name[bs]=Čisto +Name[ca]=Pla +Name[cs]=Prostý +Name[cy]=Plaen +Name[da]=Ren +Name[de]=Einfach +Name[el]=Απλό +Name[eo]=Simpla +Name[es]=Sin formato +Name[et]=Tühi +Name[eu]=Arrunta +Name[fa]=ساده +Name[fi]=Pelkistetty +Name[fo]=Vanlig(t) +Name[fr]=Brut +Name[fy]=Gewoan +Name[ga]=Simplí +Name[gl]=Plano +Name[he]=רגיל +Name[hi]=सादा +Name[hr]=Čisto +Name[hu]=Egyszerű +Name[id]=Polos +Name[is]=Einfalt +Name[it]=Normale +Name[ja]=プレーン +Name[km]=ធម្មតា +Name[lo]=ເອກະສານທຳມະດາ +Name[lt]=Paprastas +Name[lv]=Atklāts +Name[mk]=Обична +Name[ms]=Biasa +Name[nb]=Vanlig +Name[nds]=Eenfach +Name[ne]=सादा +Name[nl]=Normaal +Name[nn]=Vanleg +Name[pl]=Zwykły +Name[pt]=Plano +Name[pt_BR]=Plano +Name[ro]=Simplu +Name[ru]=Обычный +Name[se]=Dábálaš +Name[sk]=Prostý +Name[sl]=Navadno +Name[sr]=Обичан +Name[sr@Latn]=Običan +Name[sv]=Vanlig +Name[ta]=சாதாரண +Name[tg]=Якранга +Name[th]=เอกสารธรรมดา +Name[tr]=Düz +Name[uk]=Звичайний +Name[uz]=Oddiy +Name[uz@cyrillic]=Оддий +Name[ven]=Ho waho +Name[xh]=Cacileyo +Name[zh_CN]=普通 +Name[zh_TW]=普通 +Name[zu]=Okungahlobisiwe +Icon=template_timechart diff --git a/kplato/templates/Simple/Plain.kplatot b/kplato/templates/Simple/Plain.kplatot new file mode 100644 index 00000000..fe188725 --- /dev/null +++ b/kplato/templates/Simple/Plain.kplatot @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE DOC> +<DOC mime="application/x-vnd.kde.kplato" syntaxVersion="0" > +</DOC> diff --git a/kplato/templates/Simple/cr48-action-template_timechart.png b/kplato/templates/Simple/cr48-action-template_timechart.png Binary files differnew file mode 100644 index 00000000..3ea1c0af --- /dev/null +++ b/kplato/templates/Simple/cr48-action-template_timechart.png diff --git a/kplato/templates/Simple/crsc-action-template_timechart.svgz b/kplato/templates/Simple/crsc-action-template_timechart.svgz Binary files differnew file mode 100644 index 00000000..9d763a2a --- /dev/null +++ b/kplato/templates/Simple/crsc-action-template_timechart.svgz diff --git a/kplato/tests/CalendarTester.cpp b/kplato/tests/CalendarTester.cpp new file mode 100644 index 00000000..1afcce6d --- /dev/null +++ b/kplato/tests/CalendarTester.cpp @@ -0,0 +1,125 @@ +/* This file is part of the KDE project + Copyright (C) 2006 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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. +*/ +#include "CalendarTester.h" +#include <kptcalendar.h> +#include <kptdatetime.h> +#include <kptduration.h> +#include <kptmap.h> +#include <kunittest/runner.h> +#include <kunittest/module.h> +#include <qstring.h> + +using namespace KUnitTest; + +KUNITTEST_MODULE(kunittest_CalendarTester, "Calendar Tester"); +KUNITTEST_MODULE_REGISTER_TESTER(CalendarTester); + +void CalendarTester::allTests() { + testSingleDay(); + testWeekdays(); + testCalendarWithParent(); +} + +void CalendarTester::testSingleDay() { + KPlato::Calendar t("Test"); + QDate wdate(2006,1,2); + KPlato::DateTime before = KPlato::DateTime(wdate.addDays(-1)); + KPlato::DateTime after = KPlato::DateTime(wdate.addDays(1)); + QTime t1(8,0,0); + QTime t2(10,0,0); + KPlato::DateTime wdt1(wdate, t1); + KPlato::DateTime wdt2(wdate, t2); + KPlato::CalendarDay *day = new KPlato::CalendarDay(QDate(2006,1,2), KPlato::Map::Working); + day->addInterval(QPair<QTime, QTime>(t1, t2)); + VERIFY(t.addDay(day)); + COMPARE(t.findDay(wdate), day); + VERIFY((t.firstAvailableAfter(after, after.addDays(10))).isValid() == false); + VERIFY((t.firstAvailableBefore(before, before.addDays(-10))).isValid() == false); + + COMPARE(t.firstAvailableAfter(before,after).toString(), wdt1.toString()); + COMPARE(t.firstAvailableBefore(after, before).toString(), wdt2.toString()); + + VERIFY(t.hasInterval(before, after)); + VERIFY(t.hasInterval(after, before) == false); + + VERIFY(t.hasInterval(after, after.addDays(1)) == false); + VERIFY(t.hasInterval(before, before.addDays(-1)) == false); + + KPlato::Duration e(0, 2, 0); + COMPARE((t.effort(before, after)).toString(), e.toString()); + +} + +void CalendarTester::testWeekdays() { + KPlato::Calendar t("Test"); + QDate wdate(2006,1,4); // wednesday + KPlato::DateTime before = KPlato::DateTime(wdate.addDays(-2)); + KPlato::DateTime after = KPlato::DateTime(wdate.addDays(2)); + QTime t1(8,0,0); + QTime t2(10,0,0); + + KPlato::CalendarDay *wd1 = t.weekday(2); // wednesday + VERIFY(wd1 != 0); + + wd1->setState(KPlato::Map::Working); + wd1->addInterval(QPair<QTime, QTime>(t1, t2)); + + COMPARE(t.firstAvailableAfter(before, after).toString(), QDateTime(QDate(2006, 1, 4), QTime(8,0,0)).toString()); + COMPARE((t.firstAvailableBefore(after, before)).toString(), QDateTime(QDate(2006, 1, 4), QTime(10,0,0)).toString()); + + COMPARE(t.firstAvailableAfter(after, KPlato::DateTime(QDate(2006,1,14))).toString(), QDateTime(QDate(2006, 1, 11), QTime(8,0,0)).toString()); + COMPARE(t.firstAvailableBefore(before, KPlato::DateTime(QDate(2005,12,25))).toString(), QDateTime(QDate(2005, 12, 28), QTime(10,0,0)).toString()); + +} + +void CalendarTester::testCalendarWithParent() { + KPlato::Calendar t("Test 3"); + KPlato::Calendar p("Test 3 parent"); + t.setParent(&p); + QDate wdate(2006,1,2); + KPlato::DateTime before = KPlato::DateTime(wdate.addDays(-1)); + KPlato::DateTime after = KPlato::DateTime(wdate.addDays(1)); + QTime t1(8,0,0); + QTime t2(10,0,0); + KPlato::DateTime wdt1(wdate, t1); + KPlato::DateTime wdt2(wdate, t2); + + KPlato::CalendarDay *day = new KPlato::CalendarDay(QDate(2006,1,2), KPlato::Map::Working); + day->addInterval(QPair<QTime, QTime>(t1, t2)); + COMPARE(p.addDay(day), true); + COMPARE(p.findDay(wdate), day); + + // same tests as in testSingleDay() + VERIFY((t.firstAvailableAfter(after, after.addDays(10))).isValid() == false); + VERIFY((t.firstAvailableBefore(before, before.addDays(-10))).isValid() == false); + + COMPARE(t.firstAvailableAfter(before,after).toString(), wdt1.toString()); + COMPARE(t.firstAvailableBefore(after, before).toString(), wdt2.toString()); + + VERIFY(t.hasInterval(before, after)); + VERIFY(t.hasInterval(after, before) == false); + + VERIFY(t.hasInterval(after, after.addDays(1)) == false); + VERIFY(t.hasInterval(before, before.addDays(-1)) == false); + + KPlato::Duration e(0, 2, 0); + COMPARE((t.effort(before, after)).toString(), e.toString()); + +} + diff --git a/kplato/tests/CalendarTester.h b/kplato/tests/CalendarTester.h new file mode 100644 index 00000000..d9f422e3 --- /dev/null +++ b/kplato/tests/CalendarTester.h @@ -0,0 +1,30 @@ +/* This file is part of the KDE project + Copyright (C) 2006 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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. +*/ +#include <kunittest/tester.h> + +class KoXmlWriter; + +class CalendarTester : public KUnitTest::Tester { + public: + void allTests(); + private: + void testSingleDay(); + void testWeekdays(); + void testCalendarWithParent(); +}; diff --git a/kplato/tests/DateTimeTester.cpp b/kplato/tests/DateTimeTester.cpp new file mode 100644 index 00000000..d7bd2fb8 --- /dev/null +++ b/kplato/tests/DateTimeTester.cpp @@ -0,0 +1,53 @@ +/* This file is part of the KDE project + Copyright (C) 2006 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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. +*/ +#include "DateTimeTester.h" +#include <kptdatetime.h> +#include <kptduration.h> +#include <kunittest/runner.h> +#include <kunittest/module.h> + +#include <qdatetime.h> + +using namespace KUnitTest; + +KUNITTEST_MODULE(kunittest_DateTimeTester, "DateTime Tester"); +KUNITTEST_MODULE_REGISTER_TESTER(DateTimeTester); + +void DateTimeTester::allTests() { + testSubtract(); + testAdd(); +} + +//FIXME: Define a operator<< for Duration +void DateTimeTester::testSubtract() { + KPlato::DateTime dt1(QDate(2006, 1, 1), QTime(8, 0, 0)); + KPlato::DateTime dt2(QDate(2006, 1, 1), QTime(10, 0, 0)); + KPlato::Duration d(0, 2, 0); + + COMPARE((dt2-dt1).toString(), d.toString()); + COMPARE((dt1-dt2).toString(), d.toString()); // result always positive + COMPARE((dt2-d).toString(), dt1.toString()); +} + +void DateTimeTester::testAdd() { + KPlato::DateTime dt1(QDate(2006, 1, 1), QTime(8, 0, 0)); + KPlato::DateTime dt2(QDate(2006, 1, 1), QTime(10, 0, 0)); + KPlato::Duration d(0, 2, 0); + COMPARE((dt1+d).toString(), dt2.toString()); +} diff --git a/kplato/tests/DateTimeTester.h b/kplato/tests/DateTimeTester.h new file mode 100644 index 00000000..aac3e13d --- /dev/null +++ b/kplato/tests/DateTimeTester.h @@ -0,0 +1,37 @@ +/* This file is part of the KDE project + Copyright (C) 2006 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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 DATETIMETESTER_H +#define DATETIMETESTER_H + +#include <kunittest/tester.h> +#include <kptduration.h> + +class KoXmlWriter; + +class DateTimeTester : public KUnitTest::Tester { + public: + void allTests(); + private: + void testSubtract(); + void testAdd(); + +}; + +#endif diff --git a/kplato/tests/DurationTester.cpp b/kplato/tests/DurationTester.cpp new file mode 100644 index 00000000..2d84986d --- /dev/null +++ b/kplato/tests/DurationTester.cpp @@ -0,0 +1,53 @@ +/* This file is part of the KDE project + Copyright (C) 2006 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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. +*/ +#include "DurationTester.h" +#include <kptduration.h> +#include <kunittest/runner.h> +#include <kunittest/module.h> + +using namespace KUnitTest; + +KUNITTEST_MODULE(kunittest_DurationTester, "Duration Tester"); +KUNITTEST_MODULE_REGISTER_TESTER(DurationTester); + +void DurationTester::allTests() { + testArithmetic(); +} + +//FIXME: Define a operator<< for Duration + +void DurationTester::testArithmetic() { + KPlato::Duration d1(0, 2, 0); + KPlato::Duration d2(1, 0, 0); + + COMPARE((d1+d1).toString(), KPlato::Duration(0, 4, 0).toString()); + COMPARE((d1-d1).toString(), KPlato::Duration(0, 0, 0).toString()); + COMPARE((d1/2).toString(), KPlato::Duration(0, 1, 0).toString()); + + VERIFY(d1==d1); + VERIFY(d1<=d1); + VERIFY(d1>=d1); + VERIFY(!(d1!=d1)); + VERIFY(d2>d1); + VERIFY(d1<d2); + + VERIFY(d1 > 1*60*60*1000); + VERIFY(d1 < 3*60*60*1000); +} + diff --git a/kplato/tests/DurationTester.h b/kplato/tests/DurationTester.h new file mode 100644 index 00000000..bdcc4866 --- /dev/null +++ b/kplato/tests/DurationTester.h @@ -0,0 +1,28 @@ +/* This file is part of the KDE project + Copyright (C) 2006 Dag Andersen <danders@get2net.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + 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. +*/ +#include <kunittest/tester.h> + +class KoXmlWriter; + +class DurationTester : public KUnitTest::Tester { + public: + void allTests(); + private: + void testArithmetic(); +}; diff --git a/kplato/tests/Makefile.am b/kplato/tests/Makefile.am new file mode 100644 index 00000000..e9b7ec22 --- /dev/null +++ b/kplato/tests/Makefile.am @@ -0,0 +1,29 @@ +AM_CPPFLAGS = -I$(srcdir)/../ $(KOFFICE_INCLUDES) $(all_includes) + +INCLUDES = $(KOFFICE_INCLUDES) $(all_includes) + +# The check_ target makes sure we don't install the modules, +# $(KDE_CHECK_PLUGIN) assures a shared library is created. +check_LTLIBRARIES = kunittest_DateTimeTester.la \ + kunittest_DurationTester.la \ + kunittest_CalendarTester.la + +kunittest_DateTimeTester_la_SOURCES = DateTimeTester.cpp +kunittest_DateTimeTester_la_LIBADD = -lkunittest ../libkplatopart.la +kunittest_DateTimeTester_la_LDFLAGS = -module $(KDE_CHECK_PLUGIN) $(all_libraries) + +kunittest_DurationTester_la_SOURCES = DurationTester.cpp +kunittest_DurationTester_la_LIBADD = -lkunittest ../libkplatopart.la +kunittest_DurationTester_la_LDFLAGS = -module $(KDE_CHECK_PLUGIN) $(all_libraries) + +kunittest_CalendarTester_la_SOURCES = CalendarTester.cpp +kunittest_CalendarTester_la_LIBADD = -lkunittest ../libkplatopart.la +kunittest_CalendarTester_la_LDFLAGS = -module $(KDE_CHECK_PLUGIN) $(all_libraries) + +check-local: kunittest_DateTimeTester.la \ + kunittest_DurationTester.la \ + kunittest_CalendarTester.la + +check: + kunittestmodrunner + diff --git a/kplato/toolbar/Makefile.am b/kplato/toolbar/Makefile.am new file mode 100644 index 00000000..d9450121 --- /dev/null +++ b/kplato/toolbar/Makefile.am @@ -0,0 +1,2 @@ +kplatoicondir = $(kde_datadir)/kplato/icons +kplatoicon_ICON = AUTO diff --git a/kplato/toolbar/cr22-action-accounts.png b/kplato/toolbar/cr22-action-accounts.png Binary files differnew file mode 100644 index 00000000..3a4b46cf --- /dev/null +++ b/kplato/toolbar/cr22-action-accounts.png diff --git a/kplato/toolbar/cr22-action-add_milestone.png b/kplato/toolbar/cr22-action-add_milestone.png Binary files differnew file mode 100644 index 00000000..b78c374f --- /dev/null +++ b/kplato/toolbar/cr22-action-add_milestone.png diff --git a/kplato/toolbar/cr22-action-add_sub_task.png b/kplato/toolbar/cr22-action-add_sub_task.png Binary files differnew file mode 100644 index 00000000..3dc574af --- /dev/null +++ b/kplato/toolbar/cr22-action-add_sub_task.png diff --git a/kplato/toolbar/cr22-action-add_task.png b/kplato/toolbar/cr22-action-add_task.png Binary files differnew file mode 100644 index 00000000..31e3c500 --- /dev/null +++ b/kplato/toolbar/cr22-action-add_task.png diff --git a/kplato/toolbar/cr22-action-gantt_chart.png b/kplato/toolbar/cr22-action-gantt_chart.png Binary files differnew file mode 100644 index 00000000..dd11f27e --- /dev/null +++ b/kplato/toolbar/cr22-action-gantt_chart.png diff --git a/kplato/toolbar/cr22-action-indent_task.png b/kplato/toolbar/cr22-action-indent_task.png Binary files differnew file mode 100644 index 00000000..2bbcb914 --- /dev/null +++ b/kplato/toolbar/cr22-action-indent_task.png diff --git a/kplato/toolbar/cr22-action-move_task_down.png b/kplato/toolbar/cr22-action-move_task_down.png Binary files differnew file mode 100644 index 00000000..29fe9136 --- /dev/null +++ b/kplato/toolbar/cr22-action-move_task_down.png diff --git a/kplato/toolbar/cr22-action-move_task_up.png b/kplato/toolbar/cr22-action-move_task_up.png Binary files differnew file mode 100644 index 00000000..13029c29 --- /dev/null +++ b/kplato/toolbar/cr22-action-move_task_up.png diff --git a/kplato/toolbar/cr22-action-pert_chart.png b/kplato/toolbar/cr22-action-pert_chart.png Binary files differnew file mode 100644 index 00000000..15e704df --- /dev/null +++ b/kplato/toolbar/cr22-action-pert_chart.png diff --git a/kplato/toolbar/cr22-action-project_calculate.png b/kplato/toolbar/cr22-action-project_calculate.png Binary files differnew file mode 100644 index 00000000..c1e7a2ac --- /dev/null +++ b/kplato/toolbar/cr22-action-project_calculate.png diff --git a/kplato/toolbar/cr22-action-resources.png b/kplato/toolbar/cr22-action-resources.png Binary files differnew file mode 100644 index 00000000..581498ab --- /dev/null +++ b/kplato/toolbar/cr22-action-resources.png diff --git a/kplato/toolbar/cr22-action-unindent_task.png b/kplato/toolbar/cr22-action-unindent_task.png Binary files differnew file mode 100644 index 00000000..59629a5d --- /dev/null +++ b/kplato/toolbar/cr22-action-unindent_task.png |