kplato

kptganttview.cpp
1/* This file is part of the KDE project
2 Copyright (C) 2002 - 2005 Dag Andersen <danders@get2net.dk>
3 Copyright (C) 2006 Raphael Langerhorst <raphael.langerhorst@kdemail.net>
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License as published by the Free Software Foundation;
8 version 2 of the License.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
14
15 You should have received a copy of the GNU Library General Public License
16 along with this library; see the file COPYING.LIB. If not, write to
17 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19*/
20
21#include "kptganttview.h"
22
23#include "kptappointment.h"
24#include "kptpart.h"
25#include "kptview.h"
26#include "kptcanvasitem.h"
27#include "kptnode.h"
28#include "kptpart.h"
29#include "kptproject.h"
30#include "kpttask.h"
31#include "kptresource.h"
32#include "kptdatetime.h"
33#include "kpttaskappointmentsview.h"
34#include "kptrelation.h"
35#include "kptcontext.h"
36#include "kptschedule.h"
37
38#include "KDGanttView.h"
39#include "KDGanttViewItem.h"
40#include "KDGanttViewTaskItem.h"
41#include "KDGanttViewSummaryItem.h"
42#include "KDGanttViewEventItem.h"
43
44#include <kdebug.h>
45
46#include <tqsplitter.h>
47#include <tqvbox.h>
48#include <tqlayout.h>
49#include <tqlistview.h>
50#include <tqheader.h>
51#include <tqpopupmenu.h>
52#include <tqtabwidget.h>
53#include <tqptrlist.h>
54#include <tqlineedit.h>
55#include <tqwidget.h>
56#include <tqlabel.h>
57#include <tqspinbox.h>
58#include <tqstringlist.h>
59#include <tqvaluelist.h>
60#include <tqpainter.h>
61#include <tqpaintdevicemetrics.h>
62
63#include <tdelocale.h>
64#include <tdeglobal.h>
65#include <kprinter.h>
66#include <tdemessagebox.h>
67
68namespace KPlato
69{
70
71class MyKDGanttView : public KDGanttView {
72public:
73 MyKDGanttView(TQWidget *parent, const char *name)
74 : KDGanttView(parent, name) {
75 }
76 virtual TQSize sizeHint() const {
77 return minimumSizeHint(); //HACK: koshell splitter minimumSize problem
78 }
79};
80
81GanttView::GanttView(TQWidget *parent, bool readWrite, const char* name)
82 : TQSplitter(parent, name),
83 m_readWrite(readWrite),
84 m_currentItem(0),
85 m_taskView(0),
86 m_firstTime(true),
87 m_project(0)
88{
89 kdDebug() << " ---------------- KPlato: Creating GanttView ----------------" << endl;
90 setOrientation(TQt::Vertical);
91
92 m_gantt = new MyKDGanttView(this, "Gantt view");
93
94 m_showExpected = true;
95 m_showOptimistic = false;
96 m_showPessimistic = false;
97 m_showResources = false; // FIXME
98 m_showTaskName = false; // FIXME
99 m_showTaskLinks = false; // FIXME
100 m_showProgress = false; //FIXME
101 m_showPositiveFloat = false; //FIXME
102 m_showCriticalTasks = false; //FIXME
103 m_showCriticalPath = false; //FIXME
104 m_showNoInformation = false; //FIXME
105 m_showAppointments = false;
106
107 m_gantt->setHeaderVisible(true);
108 m_gantt->addColumn(i18n("Work Breakdown Structure", "WBS"));
109 // HACK: need changes to kdgantt
110 KDGanttViewTaskItem *item = new KDGanttViewTaskItem(m_gantt);
111 TQListView *lv = item->listView();
112 lv->header()->moveSection(1, 0);
113
114 m_gantt->setScale(KDGanttView::Day);
115 m_gantt->setShowLegendButton(false);
116 m_gantt->setShowHeaderPopupMenu();
117 m_taskView = new TaskAppointmentsView(this);
118 // hide TaskAppointmentsView
119 TQValueList<int> list = sizes();
120 list[0] += list[1];
121 list[1] = 0;
122 setSizes(list);
123 m_taskView->hide();
124
125 setReadWriteMode(readWrite);
126
127 connect(m_gantt, TQ_SIGNAL(lvContextMenuRequested ( KDGanttViewItem *, const TQPoint &, int )),
128 this, TQ_SLOT (popupMenuRequested(KDGanttViewItem *, const TQPoint &, int)));
129
130 connect(m_gantt, TQ_SIGNAL(lvCurrentChanged(KDGanttViewItem*)), this, TQ_SLOT (currentItemChanged(KDGanttViewItem*)));
131
132 // HACK: kdgantt emits 2 signals for each *double* click, so we go direct to listview
133 connect(lv, TQ_SIGNAL(doubleClicked(TQListViewItem*, const TQPoint&, int)), this, TQ_SLOT (slotItemDoubleClicked(TQListViewItem*)));
134
135 m_taskLinks.setAutoDelete(true);
136
137 if (m_gantt->firstChild()) {
138 m_gantt->firstChild()->listView()->setCurrentItem(m_gantt->firstChild());
139 m_gantt->firstChild()->listView()->setFocus();
140 }
141}
142
143void GanttView::setZoom(double zoom)
144{
145 kdDebug() << "setting gantt zoom: " << zoom << endl;
146 m_gantt->setZoomFactor(zoom,true);
147 m_taskView->zoom( zoom );
148}
149
150void GanttView::clear()
151{
152 m_gantt->clear();
153 m_taskView->clear();
154}
155
156void GanttView::draw(Project &project)
157{
158 m_project = &project;
159 //kdDebug()<<k_funcinfo<<endl;
160 Schedule::Type type = Schedule::Expected;
161 if (m_showOptimistic) {
162 type = Schedule::Optimistic;
163 } else if (m_showPessimistic) {
164 type = Schedule::Pessimistic;
165 }
166 Schedule *sch = project.findSchedule(type);
167 if (sch) {
168 project.setCurrentSchedule(sch->id());
169 }
170 //kdDebug()<<k_funcinfo<<"Schedule: "<<(sch?sch->typeToString():"None")<<endl;
171 m_gantt->setUpdateEnabled(false);
172
173 clear();
174 drawChildren(NULL, project);
175 drawRelations();
176
177 if (m_firstTime) {
178 m_gantt->centerTimelineAfterShow(project.startTime().addDays(-1));
179 m_firstTime = false;
180 }
181 m_gantt->setUpdateEnabled(true);
182 currentItemChanged(m_currentItem);
183}
184
185void GanttView::drawChanges(Project &project)
186{
187 m_project = &project; //FIXME Only draw changes on same project
188 //kdDebug()<<k_funcinfo<<endl;
189 Schedule::Type type = Schedule::Expected;
190 if (m_showOptimistic) {
191 type = Schedule::Optimistic;
192 } else if (m_showPessimistic) {
193 type = Schedule::Pessimistic;
194 }
195 Schedule *sch = project.findSchedule(type);
196 if (sch) {
197 project.setCurrentSchedule(sch->id());
198 }
199 //kdDebug()<<k_funcinfo<<"Schedule: "<<(sch?sch->typeToString():"None")<<endl;
200 m_gantt->setUpdateEnabled(false);
201 resetDrawn(m_gantt->firstChild());
202 updateChildren(&project); // don't draw project
203 removeNotDrawn(m_gantt->firstChild());
204
205 m_taskLinks.clear();
206 drawRelations();
207
208 m_gantt->setUpdateEnabled(true);
209 if (m_currentItem == 0 && m_gantt->firstChild()) {
210 m_gantt->firstChild()->listView()->setCurrentItem(m_gantt->firstChild());
211 currentItemChanged(m_gantt->firstChild()); //hmmm
212 }
213 currentItemChanged(m_currentItem);
214}
215
216void GanttView::drawOnPainter(TQPainter* painter, const TQRect rect)
217{
218 // Assume clipping is allready set
219
220 // Fill out the rect by adding ticks to right side of the timeline
221 TQSize s = m_gantt->drawContents(0, false, true);
222 while (s.width() < rect.width()) {
223 m_gantt->addTicksRight();
224 m_gantt->setTimelineToEnd();
225 s = m_gantt->drawContents(0, false, true);
226 }
227 kdDebug()<<k_funcinfo<<rect<<" : "<<s<<endl;
228 painter->save();
229
230// TQValueList<int> sizes = m_taskView->sizes();
231// if (sizes.count() >= 2)
232// {
233// int first = sizes[0];
234// int second = sizes[1];
235// sizes.pop_front();
236// sizes.pop_front();
237// sizes.prepend(first+second);
238// sizes.prepend(0);
239// m_taskView->setSizes(sizes);
240// }
241// else
242// kdWarning() << "Apparently the task view splitter contains less than 2 parts!" << endl;
243
244// bool showlistview = m_gantt->showListView();
245// int listviewwidth = m_gantt->listViewWidth();
246// m_gantt->setShowListView(false);
247// m_gantt->setListViewWidth(0);
248
249// m_gantt->setGanttMaximumWidth(rect.x());
250 m_gantt->drawContents(painter,false,true);
251// m_gantt->setShowListView(showlistview);
252// m_gantt->setListViewWidth(listviewwidth);
253
254// m_taskView->drawContents(painter); //TODO doesn't seem to do very much
255 painter->restore();
256}
257
258KDGanttViewItem *GanttView::findItem(Node *node)
259{
260 return findItem(node, m_gantt->firstChild());
261}
262
263KDGanttViewItem *GanttView::findItem(Node *node, KDGanttViewItem *item)
264{
265 for (; item; item = item->nextSibling()) {
266 if (node == getNode(item)) {
267 return item;
268 }
269 KDGanttViewItem *i = findItem(node, item->firstChild());
270 if (i)
271 return i;
272 }
273 return 0;
274}
275
276Node *GanttView::getNode(KDGanttViewItem *item) const {
277 if (item) {
278 if (item->type() == KDGanttViewItem::Event){
279 return static_cast<GanttViewEventItem *>(item)->getTask();
280 } else if (item->type() == KDGanttViewItem::Task) {
281 return static_cast<GanttViewTaskItem *>(item)->getTask();
282 } else if (item->type() == KDGanttViewItem::Summary) {
283 return static_cast<GanttViewSummaryItem *>(item)->getNode();
284 }
285 }
286 return 0;
287}
288
289bool GanttView::isDrawn(KDGanttViewItem *item) {
290 if (item) {
291 if (item->type() == KDGanttViewItem::Event){
292 return static_cast<GanttViewEventItem *>(item)->isDrawn();
293 } else if (item->type() == KDGanttViewItem::Task) {
294 return static_cast<GanttViewTaskItem *>(item)->isDrawn();
295 } else if (item->type() == KDGanttViewItem::Summary) {
296 return static_cast<GanttViewSummaryItem *>(item)->isDrawn();
297 } else {
298 kdWarning()<<k_funcinfo<<"Unknown item type: "<<item->type()<<endl;
299 }
300 }
301 return false;
302}
303
304void GanttView::setDrawn(KDGanttViewItem *item, bool state) {
305 if (item) {
306 if (item->type() == KDGanttViewItem::Event){
307 static_cast<GanttViewEventItem *>(item)->setDrawn(state);
308 } else if (item->type() == KDGanttViewItem::Task) {
309 static_cast<GanttViewTaskItem *>(item)->setDrawn(state);
310 } else if (item->type() == KDGanttViewItem::Summary) {
311 static_cast<GanttViewSummaryItem *>(item)->setDrawn(state);
312 } else {
313 kdWarning()<<k_funcinfo<<"Unknown item type: "<<item->type()<<endl;
314 }
315 }
316 return;
317}
318
319void GanttView::resetDrawn(KDGanttViewItem *_item)
320{
321 KDGanttViewItem *nextItem, *item=_item;
322 for (; item; item = nextItem) {
323 nextItem = item->nextSibling();
324 setDrawn(item, false);
325 resetDrawn(item->firstChild()); // then my children
326 }
327}
328
329void GanttView::removeNotDrawn(KDGanttViewItem *_item)
330{
331 KDGanttViewItem *nextItem, *item=_item;
332 for (; item; item = nextItem) {
333 nextItem = item->nextSibling();
334 if (!isDrawn(item)) {
335 if (item == m_currentItem)
336 m_currentItem = 0;
337 deleteItem(item);
338 } else {
339 removeNotDrawn(item->firstChild()); // then my children
340 }
341 }
342}
343
344void GanttView::deleteItem(KDGanttViewItem *item)
345{
346 //kdDebug()<<k_funcinfo<<item->listViewText()<<endl;
347 if (item->parent())
348 item->parent()->takeItem(item);
349 else
350 item->listView()->takeItem(item);
351 delete item;
352}
353
354KDGanttViewItem *GanttView::correctType(KDGanttViewItem *item, Node *node)
355{
356 //kdDebug()<<k_funcinfo<<item->listViewText()<<": "<<item->type()<<" node: "<<node->type()<<endl;
357 switch (node->type()) {
358 case Node::Type_Project:
359 return item;
360 break;
361 case Node::Type_Summarytask:
362 case Node::Type_Subproject:
363 if (item->type() == KDGanttViewItem::Summary)
364 return item;
365 break;
366 case Node::Type_Task:
367 if (item->type() == KDGanttViewItem::Task)
368 return item;
369 break;
370 case Node::Type_Milestone:
371 if (item->type() == KDGanttViewItem::Event)
372 return item;
373 break;
374 default:
375 return item;
376 break;
377 }
378 KDGanttViewItem *newItem = addNode(item->parent(), node, item);
379 newItem->setOpen(item->isOpen());
380 deleteItem(item);
381 return newItem;
382}
383
384void GanttView::correctPosition(KDGanttViewItem *item, Node *node)
385{
386 KDGanttViewItem *after = findItem(node->siblingBefore());
387 if (after) {
388 item->moveItem(after);
389 }
390}
391
392KDGanttViewItem *GanttView::correctParent(KDGanttViewItem *item, Node *node)
393{
394 KDGanttViewItem *p = findItem(node->getParent());
395 if (p == item->parent()) {
396 return item;
397 }
398 KDGanttViewItem *newItem = addNode(p, node);
399 newItem->setOpen(item->isOpen());
400 deleteItem(item);
401 return newItem;
402}
403
404void GanttView::updateChildren(Node *parentNode)
405{
406 //kdDebug()<<k_funcinfo<<endl;
407 TQPtrListIterator<Node> nit(parentNode->childNodeIterator());
408 for (; nit.current(); ++nit )
409 {
410 updateNode(nit.current());
411 }
412}
413
414void GanttView::updateNode(Node *node)
415{
416 //kdDebug()<<k_funcinfo<<node->name()<<endl;
417 KDGanttViewItem *item = findItem(node);
418 if (!item) {
419 item = addNode(findItem(node->getParent()), node, findItem(node->siblingBefore()));
420 if (item && node->type() == Node::Type_Summarytask)
421 updateChildren(node);
422 return;
423 }
424 item = correctType(item, node);
425 item = correctParent(item, node);
426 correctPosition(item, node);
427
428 modifyNode(node);
429
430 if (node->type() == Node::Type_Summarytask)
431 updateChildren(node);
432}
433
434void GanttView::modifyChildren(Node *node)
435{
436 //kdDebug()<<k_funcinfo<<endl;
437 TQPtrListIterator<Node> nit(node->childNodeIterator());
438 for ( nit.toLast(); nit.current(); --nit ) {
439 modifyNode(nit.current());
440 modifyChildren(nit.current());
441 }
442}
443
444void GanttView::modifyNode(Node *node)
445{
446 //kdDebug()<<k_funcinfo<<endl;
447 KDGanttViewItem *item = findItem(node);
448 if (!item) {
449 kdDebug()<<k_funcinfo<<" Item not found"<<endl;
450 return;
451 }
452 if (node->type() == Node::Type_Project) {
453 return modifyProject(item, node);
454 }
455 if (node->type() == Node::Type_Subproject) {
456 return modifyProject(item, node);
457 }
458 if (node->type() == Node::Type_Summarytask) {
459 return modifySummaryTask(item, static_cast<Task *>(node));
460 }
461 if (node->type() == Node::Type_Task) {
462 return modifyTask(item, static_cast<Task *>(node));
463 }
464 if (node->type() == Node::Type_Milestone) {
465 return modifyMilestone(item, static_cast<Task *>(node));
466 }
467 return;
468}
469
470void GanttView::modifyProject(KDGanttViewItem *item, Node *node)
471{
472 //kdDebug()<<k_funcinfo<<endl;
473 item->setListViewText(node->name());
474 item->setListViewText(1, node->wbs());
475 item->setStartTime(node->startTime());
476 item->setEndTime(node->endTime());
477 //item->setOpen(true);
478 setDrawn(item, true);
479
480}
481
482void GanttView::modifySummaryTask(KDGanttViewItem *item, Task *task)
483{
484 //kdDebug()<<k_funcinfo<<endl;
485 TDELocale *locale = TDEGlobal::locale();
486 //kdDebug()<<k_funcinfo<<task->name()<<": "<<task->currentSchedule()<<", "<<task->notScheduled()<<", "<<(m_project ? m_project->notScheduled() : false)<<endl;
487 if (task->currentSchedule() == 0) {
488 item->setShowNoInformation(m_showNoInformation);
489 item->setStartTime(task->projectNode()->startTime());
490 item->setEndTime(item->startTime().addDays(1));
491 } else {
492 bool noinf = m_showNoInformation && (task->notScheduled() || (m_project ? m_project->notScheduled() : false /*hmmm, no project?*/));
493 item->setShowNoInformation(noinf);
494 item->setStartTime(task->startTime());
495 item->setEndTime(task->endTime());
496 }
497 item->setListViewText(task->name());
498 item->setListViewText(1, task->wbs());
499 //item->setOpen(true);
500 if (m_showTaskName) {
501 item->setText(task->name());
502 } else {
503 item->setText(TQString());
504 }
505 TQString w = i18n("Name: %1").arg(task->name());
506 if (!task->notScheduled()) {
507 w += "\n" + i18n("Start: %1").arg(locale->formatDateTime(task->startTime()));
508 w += "\n" + i18n("End: %1").arg(locale->formatDateTime(task->endTime()));
509 }
510 bool ok = true;
511 if (task->notScheduled()) {
512 w += "\n" + i18n("Not scheduled");
513 ok = false;
514 } else {
515 if (!m_showNoInformation && m_project && m_project->notScheduled()) {
516 ok = false;
517 }
518 }
519 if (ok) {
520 TQColor c(cyan);
521 item->setColors(c,c,c);
522 } else {
523 TQColor c(yellow);
524 item->setColors(c,c,c);
525 }
526 item->setTooltipText(w);
527 setDrawn(item, true);
528}
529
530void GanttView::modifyTask(KDGanttViewItem *item, Task *task)
531{
532 //kdDebug()<<k_funcinfo<<endl;
533 TDELocale *locale = TDEGlobal::locale();
534 //kdDebug()<<k_funcinfo<<task->name()<<": "<<task->currentSchedule()<<", "<<task->notScheduled()<<", "<<(m_project ? m_project->notScheduled() : false)<<endl;
535 item->setListViewText(task->name());
536 item->setListViewText(1, task->wbs());
537 if (task->currentSchedule() == 0) {
538 item->setShowNoInformation(m_showNoInformation);
539 item->setStartTime(task->projectNode()->startTime());
540 item->setEndTime(item->startTime().addDays(1));
541 } else {
542 bool noinf = m_showNoInformation && (task->notScheduled() || (m_project ? m_project->notScheduled() : false /*hmmm, no project?*/));
543 item->setShowNoInformation(noinf);
544 item->setStartTime(task->startTime());
545 item->setEndTime(task->endTime());
546 }
547 //item->setOpen(true);
548 TQString text;
549 if (m_showTaskName) {
550 text = task->name();
551 }
552 if (m_showResources && !task->notScheduled()) {
553 TQPtrList<Appointment> lst = task->appointments();
554 if (lst.count() > 0) {
555 if (!text.isEmpty())
556 text += ' ';
557 text += '(';
558 TQPtrListIterator<Appointment> it = lst;
559 for (bool first=true; it.current(); ++it) {
560 if (!first)
561 text += ", ";
562 text += it.current()->resource()->resource()->name();
563 first = false;
564 }
565 text += ')';
566 }
567 }
568 item->setText(text);
569 if (m_showProgress) {
570 item->setProgress(task->progress().percentFinished);
571 } else {
572 item->setProgress(0);
573 }
574 if (m_showPositiveFloat) {
575 TQDateTime t = task->endTime() + task->positiveFloat();
576 if (t.isValid() && t > task->endTime()) {
577 item->setFloatEndTime(t);
578 } else {
579 item->setFloatEndTime(TQDateTime());
580 }
581 } else {
582 item->setFloatStartTime(TQDateTime());
583 item->setFloatEndTime(TQDateTime());
584 }
585 TQString w = i18n("Name: %1").arg(task->name());
586 if (!task->notScheduled()) {
587 w += "\n"; w += i18n("Start: %1").arg(locale->formatDateTime(task->startTime()));
588 w += "\n"; w += i18n("End: %1").arg(locale->formatDateTime(task->endTime()));
589 if (m_showProgress) {
590 w += "\n"; w += i18n("Completion: %1%").arg(task->progress().percentFinished);
591 }
592 if (task->positiveFloat() > Duration::zeroDuration) {
593 w += "\n" + i18n("Float: %1").arg(task->positiveFloat().toString(Duration::Format_i18nDayTime));
594 }
595 if (task->inCriticalPath()) {
596 w += "\n" + i18n("Critical path");
597 } else if (task->isCritical()) {
598 w += "\n" + i18n("Critical");
599 }
600 }
601 TQString sts;
602 bool ok = true;
603 if (task->notScheduled()) {
604 sts += "\n" + i18n("Not scheduled");
605 ok = false;
606 } else {
607 if (task->resourceError()) {
608 sts += "\n" + i18n("No resource assigned");
609 ok = false;
610 }
611 if (task->resourceNotAvailable()) {
612 sts += "\n" + i18n("Resource not available");
613 ok = false;
614 }
615 if (task->schedulingError()) {
616 sts += "\n" + i18n("Scheduling conflict");
617 ok = false;
618 }
619 if (task->effortMetError()) {
620 sts += "\n" + i18n("Requested effort could not be met");
621 ok = false;
622 }
623 if (task->resourceOverbooked()) {
624 ok = false;
625 TQStringList rl = task->overbookedResources();
626 sts += "\n" + i18n("arg: list of resources", "Resource overbooked: %1").arg(rl.join(","));
627
628 }
629 if (!m_showNoInformation && m_project && m_project->notScheduled()) {
630 ok = false;
631 }
632 }
633 if (ok) {
634 TQColor c(green);
635 item->setColors(c,c,c);
636 } else {
637 w += sts;
638 TQColor c(yellow);
639 item->setColors(c,c,c);
640 }
641 item->setHighlight(false);
642 if (m_showCriticalTasks) {
643 item->setHighlight(task->isCritical());
644 } else if (m_showCriticalPath) {
645 item->setHighlight(task->inCriticalPath());
646 }
647
648 item->setTooltipText(w);
649 setDrawn(item, true);
650}
651
652void GanttView::modifyMilestone(KDGanttViewItem *item, Task *task)
653{
654 //kdDebug()<<k_funcinfo<<endl;
655 TDELocale *locale = TDEGlobal::locale();
656 //kdDebug()<<k_funcinfo<<task->name()<<": "<<task->currentSchedule()<<", "<<task->notScheduled()<<", "<<(m_project ? m_project->notScheduled() : false)<<endl;
657 if (task->currentSchedule() == 0) {
658 item->setShowNoInformation(m_showNoInformation);
659 item->setStartTime(task->projectNode()->startTime());
660 } else {
661 bool noinf = m_showNoInformation && (task->notScheduled() || (m_project ? m_project->notScheduled() : false /*hmmm, no project?*/));
662 item->setShowNoInformation(noinf);
663 item->setStartTime(task->startTime());
664 }
665 item->setListViewText(task->name());
666 item->setListViewText(1, task->wbs());
667 //item->setOpen(true);
668 if (m_showTaskName) {
669 item->setText(task->name());
670 } else {
671 item->setText(TQString());
672 }
673 if (m_showPositiveFloat) {
674 DateTime t = task->startTime() + task->positiveFloat();
675 //kdDebug()<<k_funcinfo<<task->name()<<" float: "<<t.toString()<<endl;
676 if (t.isValid() && t > task->startTime()) {
677 item->setFloatEndTime(t);
678 } else {
679 item->setFloatEndTime(TQDateTime());
680 }
681 } else {
682 item->setFloatStartTime(TQDateTime());
683 item->setFloatEndTime(TQDateTime());
684 }
685 //TODO: Show progress
686
687 TQString w = i18n("Name: %1").arg(task->name());
688 if (!task->notScheduled()) {
689 w += "\n" + i18n("Time: %1").arg(locale->formatDateTime(task->startTime()));
690
691 if (task->positiveFloat() > Duration::zeroDuration) {
692 w += "\n" + i18n("Float: %1").arg(task->positiveFloat().toString(Duration::Format_i18nDayTime));
693 }
694 if (task->inCriticalPath()) {
695 w += "\n" + i18n("Critical path");
696 } else if (task->isCritical()) {
697 w += "\n" + i18n("Critical");
698 }
699 }
700 bool ok = true;
701 if (task->notScheduled()) {
702 w += "\n" + i18n("Not scheduled");
703 ok = false;
704 } else {
705 if (task->schedulingError()) {
706 w += "\n" + i18n("Scheduling conflict");
707 ok = false;
708 }
709 if (!m_showNoInformation && m_project && m_project->notScheduled()) {
710 ok = false;
711 }
712 }
713 if (ok) {
714 TQColor c(blue);
715 item->setColors(c,c,c);
716 } else {
717 TQColor c(yellow);
718 item->setColors(c,c,c);
719 }
720 item->setHighlight(false);
721 if (m_showCriticalTasks) {
722 item->setHighlight(task->isCritical());
723 } else if (m_showCriticalPath) {
724 item->setHighlight(task->inCriticalPath());
725 }
726
727 item->setTooltipText(w);
728 setDrawn(item, true);
729}
730
731KDGanttViewItem *GanttView::addNode( KDGanttViewItem *parentItem, Node *node, KDGanttViewItem *after)
732{
733 //kdDebug()<<k_funcinfo<<endl;
734 if (node->type() == Node::Type_Project) {
735 return addProject(parentItem, node, after);
736 }
737 if (node->type() == Node::Type_Subproject) {
738 return addSubProject(parentItem, node, after);
739 }
740 if (node->type() == Node::Type_Summarytask) {
741 return addSummaryTask(parentItem, static_cast<Task *>(node), after);
742 }
743 if (node->type() == Node::Type_Task) {
744 return addTask(parentItem, static_cast<Task *>(node), after);
745 }
746 if (node->type() == Node::Type_Milestone) {
747 return addMilestone(parentItem, static_cast<Task *>(node), after);
748 }
749 return 0;
750}
751
752KDGanttViewItem *GanttView::addProject(KDGanttViewItem *parentItem, Node *node, KDGanttViewItem *after)
753{
754 //kdDebug()<<k_funcinfo<<endl;
755 GanttViewSummaryItem *item;
756 if ( parentItem) {
757 item = new GanttViewSummaryItem(parentItem, node);
758 } else {
759 // we are on the top level
760 item = new GanttViewSummaryItem(m_gantt, node);
761 }
762 if (after)
763 item->moveItem(after);
764 modifyProject(item, node);
765 return item;
766}
767
768KDGanttViewItem *GanttView::addSubProject(KDGanttViewItem *parentItem, Node *node, KDGanttViewItem *after)
769{
770 //kdDebug()<<k_funcinfo<<endl;
771 return addProject(parentItem, node, after);
772}
773
774KDGanttViewItem *GanttView::addSummaryTask(KDGanttViewItem *parentItem, Task *task, KDGanttViewItem *after)
775{
776 //kdDebug()<<k_funcinfo<<endl;
777 // display summary item
778 GanttViewSummaryItem *item;
779 if ( parentItem) {
780 item = new GanttViewSummaryItem(parentItem, task);
781 } else {
782 // we are on the top level
783 item = new GanttViewSummaryItem(m_gantt, task);
784 }
785 if (after)
786 item->moveItem(after);
787 modifySummaryTask(item, task);
788 return item;
789}
790
791KDGanttViewItem *GanttView::addTask(KDGanttViewItem *parentItem, Task *task, KDGanttViewItem *after)
792{
793 //kdDebug()<<k_funcinfo<<endl;
794 // display task item
795 GanttViewTaskItem *item;
796 if ( parentItem ) {
797 item = new GanttViewTaskItem(parentItem, task);
798 }
799 else {
800 // we are on the top level
801 item = new GanttViewTaskItem(m_gantt, task);
802 }
803 if (after)
804 item->moveItem(after);
805 modifyTask(item, task);
806 return item;
807}
808
809KDGanttViewItem *GanttView::addMilestone(KDGanttViewItem *parentItem, Task *task, KDGanttViewItem *after)
810{
811 //kdDebug()<<k_funcinfo<<endl;
812 GanttViewEventItem *item;
813 if ( parentItem ) {
814 item = new GanttViewEventItem(parentItem, task);
815 } else {
816 // we are on the top level
817 item = new GanttViewEventItem(m_gantt, task);
818 }
819 if (after)
820 item->moveItem(after);
821 modifyMilestone(item, task);
822 return item;
823}
824
825void GanttView::drawChildren(KDGanttViewItem *parentItem, Node &parentNode)
826{
827 //kdDebug()<<k_funcinfo<<endl;
828 TQPtrListIterator<Node> nit(parentNode.childNodeIterator());
829 for ( nit.toLast(); nit.current(); --nit )
830 {
831 Node *n = nit.current();
832 if (n->type() == Node::Type_Project)
833 drawProject(parentItem, n);
834 else if (n->type() == Node::Type_Subproject)
835 drawSubProject(parentItem, n);
836 else if (n->type() == Node::Type_Summarytask) {
837 Task *t = dynamic_cast<Task *>(n);
838 drawSummaryTask(parentItem, t);
839 } else if (n->type() == Node::Type_Task) {
840 Task *t = dynamic_cast<Task *>(n);
841 drawTask(parentItem, t);
842 } else if (n->type() == Node::Type_Milestone) {
843 Task *t = dynamic_cast<Task *>(n);
844 drawMilestone(parentItem, t);
845 }
846 else
847 kdDebug()<<k_funcinfo<<"Node type "<<n->type()<<" not implemented yet"<<endl;
848
849 }
850}
851
852
853void GanttView::drawProject(KDGanttViewItem *parentItem, Node *node)
854{
855 //kdDebug()<<k_funcinfo<<endl;
856 GanttViewSummaryItem *item = dynamic_cast<GanttViewSummaryItem*>(addProject(parentItem, node));
857 drawChildren(item, *node);
858}
859
860void GanttView::drawSubProject(KDGanttViewItem *parentItem, Node *node)
861{
862 //kdDebug()<<k_funcinfo<<endl;
863 GanttViewSummaryItem *item = dynamic_cast<GanttViewSummaryItem*>(addSubProject(parentItem, node));
864 drawChildren(item, *node);
865}
866
867void GanttView::drawSummaryTask(KDGanttViewItem *parentItem, Task *task)
868{
869 //kdDebug()<<k_funcinfo<<endl;
870 GanttViewSummaryItem *item = dynamic_cast<GanttViewSummaryItem*>(addSummaryTask(parentItem, task));
871 drawChildren(item, *task);
872}
873
874void GanttView::drawTask(KDGanttViewItem *parentItem, Task *task)
875{
876 //kdDebug()<<k_funcinfo<<endl;
877 addTask(parentItem, task);
878}
879
880void GanttView::drawMilestone(KDGanttViewItem *parentItem, Task *task)
881{
882 //kdDebug()<<k_funcinfo<<endl;
883 addMilestone(parentItem, task);
884}
885
886void GanttView::addTaskLink(KDGanttViewTaskLink *link) {
887 //kdDebug()<<k_funcinfo<<endl;
888 m_taskLinks.append(link);
889}
890
891void GanttView::drawRelations()
892{
893 if (!m_showTaskLinks)
894 return;
895 KDGanttViewItem *item = m_gantt->firstChild();
896 //kdDebug()<<k_funcinfo<<"First: "<<(item ? item->listViewText() : "nil")<<endl;
897 for (; item; item = item->nextSibling())
898 {
899 drawRelations(item);
900 drawChildRelations(item->firstChild());
901 }
902}
903
904void GanttView::drawChildRelations(KDGanttViewItem *item)
905{
906 //kdDebug()<<k_funcinfo<<"item: "<<(item ? item->listViewText() : "nil")<<endl;
907 for (; item; item = item->nextSibling())
908 {
909 drawRelations(item);
910 drawChildRelations(item->firstChild());
911 }
912}
913
914void GanttView::drawRelations(KDGanttViewItem *item)
915{
916 //kdDebug()<<k_funcinfo<<endl;
917 if (!item) return;
918
919 GanttViewSummaryItem *summaryItem = dynamic_cast<GanttViewSummaryItem *>(item);
920 if (summaryItem)
921 {
922 //kdDebug()<<k_funcinfo<<"Summary item: "<<summaryItem->listViewText()<<endl;
923 summaryItem->insertRelations(this);
924 return;
925 }
926 GanttViewTaskItem *taskItem = dynamic_cast<GanttViewTaskItem *>(item);
927 if (taskItem)
928 {
929 //kdDebug()<<k_funcinfo<<"Task item: "<<taskItem->listViewText()<<endl;
930 taskItem->insertRelations(this);
931 return;
932 }
933 GanttViewEventItem *milestoneItem = dynamic_cast<GanttViewEventItem *>(item);
934 if (milestoneItem)
935 {
936 //kdDebug()<<k_funcinfo<<"Milestone item: "<<milestoneItem->listViewText()<<endl;
937 milestoneItem->insertRelations(this);
938 return;
939 }
940 kdDebug()<<k_funcinfo<<"Unknown item type: "<<item->listViewText()<<endl;
941}
942
943void GanttView::currentItemChanged(KDGanttViewItem* item)
944{
945 //kdDebug()<<k_funcinfo<<(item ? item->listViewText() : "null")<<endl;
946 m_taskView->clear();
947 m_gantt->setSelected(m_currentItem, false);
948 m_currentItem = item;
949 if (item) {
950 m_gantt->setSelected(item, true);
951 if (m_showAppointments) {
952 m_taskView->show();
953 GanttViewTaskItem *taskItem = dynamic_cast<GanttViewTaskItem *>(item);
954 if (taskItem) {
955 m_taskView->draw(taskItem->getTask());
956 } else {
957 GanttViewEventItem *msItem = dynamic_cast<GanttViewEventItem *>(item);
958 if (msItem)
959 m_taskView->draw(msItem->getTask());
960 }
961 } else {
962 m_taskView->hide();
963 }
964 }
965 emit enableActions(true);
966}
967
968Node *GanttView::currentNode() const
969{
970 return getNode(m_currentItem);
971}
972
973void GanttView::popupMenuRequested(KDGanttViewItem * item, const TQPoint & pos, int)
974{
975 //kdDebug()<<k_funcinfo<<(item?item->listViewText(0):"0")<<endl;
976 if (item == 0) {
977 kdDebug()<<"No item selected"<<endl;
978 return;
979 }
980 Node *n = getNode(item);
981 if (n == 0) {
982 kdDebug()<<"No node selected"<<endl;
983 return;
984 }
985 Task *t = dynamic_cast<Task*>(n);
986 if (t && (t->type() == Node::Type_Task || t->type() == Node::Type_Milestone)) {
987 emit requestPopupMenu("task_popup",pos);
988// TQPopupMenu *menu = m_mainview->popupMenu("task_popup");
989// if (menu)
990// {
991// /*int id =*/ menu->exec(pos);
992 //kdDebug()<<k_funcinfo<<"id="<<id<<endl;
993// }
994 return;
995 }
996 if (t && t->type() == Node::Type_Summarytask) {
997 emit requestPopupMenu("summarytask_popup",pos);
998// TQPopupMenu *menu = m_mainview->popupMenu("summarytask_popup");
999// if (menu)
1000// {
1001// /*int id =*/ menu->exec(pos);
1002 //kdDebug()<<k_funcinfo<<"id="<<id<<endl;
1003// }
1004 return;
1005 }
1006 //TODO: Other nodetypes
1007}
1008
1009void GanttView::slotItemDoubleClicked(TQListViewItem* item) {
1010 //kdDebug()<<k_funcinfo<<endl;
1011 if (item == 0 || item->childCount() > 0) {
1012 // FIXME: How else to avoid interference wirh expanding/collapsing summary items?
1013 return;
1014 }
1015 emit itemDoubleClicked();
1016}
1017
1018//TODO: 1) make it koffice compliant,
1019// 2) allow printing on multiple pages
1020void GanttView::print(KPrinter &prt) {
1021 //kdDebug()<<k_funcinfo<<endl;
1022
1023 KDGanttViewItem *selItem = m_gantt->selectedItem();
1024 if (selItem)
1025 selItem->setSelected(false);
1026
1027 //Comment from KWord
1028 // We don't get valid metrics from the printer - and we want a better resolution
1029 // anyway (it's the PS driver that takes care of the printer resolution).
1030 //But KSpread uses fixed 300 dpis, so we can use it.
1031
1032 TQPaintDeviceMetrics metrics( &prt );
1033 uint top, left, bottom, right;
1034 prt.margins(&top, &left, &bottom, &right);
1035 //kdDebug()<<m.width()<<"x"<<m.height()<<" : "<<top<<", "<<left<<", "<<bottom<<", "<<right<<" : "<<size()<<endl;
1036
1037 // get the size of the desired output for scaling.
1038 // here we want to print: ListView and TimeLine (default)
1039 // for this purpose, we call drawContents() with a 0 pointer as painter
1040 TQSize size = m_gantt->drawContents(0);
1041
1042 TQPainter p;
1043 p.begin( &prt );
1044 p.setViewport(left, top, metrics.width()-left-right, metrics.height()-top-bottom);
1045 p.setClipRect(left, top, metrics.width()-left-right, metrics.height()-top-bottom);
1046
1047 // Make a simple header
1048 p.drawRect(0,0,metrics.width(),metrics.height());
1049 TQString text;
1050 int hei = 0;
1051 text = TDEGlobal::locale()->formatDateTime(TQDateTime::currentDateTime());
1052 TQRect r = p.boundingRect(metrics.width()-1,0,0,0, TQt::AlignRight, text );
1053 p.drawText( r, TQt::AlignRight, text );
1054 hei = r.height();
1055 //kdDebug()<<"Date r="<<r.left()<<","<<r.top()<<" "<<r.width()<<"x"<<r.height()<<endl;
1056 if (m_project)
1057 {
1058 TQRect re = p.boundingRect(1,0,0,0, TQt::AlignLeft, text );
1059 re.setWidth(metrics.width()-r.width()-5); // don't print on top of date
1060 p.drawText( re, TQt::AlignLeft, m_project->name() );
1061 hei = r.height();
1062 //kdDebug()<<"Project r="<<re.left()<<","<<re.top()<<" "<<re.width()<<"x"<<re.height()<<": "<<endl;
1063 hei = TQMAX(hei, re.height());
1064 }
1065
1066 hei++;
1067 p.drawLine(0,hei,metrics.width(),hei);
1068 hei += 3;
1069 // compute the scale
1070 float dx = (float) (metrics.width()-2) / (float)size.width();
1071 float dy = (float)(metrics.height()-hei) / (float)size.height();
1072 float scale;
1073 // scale to fit the width or height of the paper
1074 if ( dx < dy )
1075 scale = dx;
1076 else
1077 scale = dy;
1078 // set the scale
1079 p.translate(1,hei);
1080 p.scale( scale, scale );
1081 m_gantt->drawContents(&p);
1082 // the drawContents() has the side effect, that the painter translation is
1083 // after drawContents() set to the bottom of the painted stuff
1084 // for instance a
1085 // p.drawText(0, 0, "printend");
1086 // would be painted directly below the paintout of drawContents()
1087
1088 p.end();
1089 if (selItem)
1090 selItem->setSelected(true);
1091}
1092
1093void GanttView::slotItemRenamed(KDGanttViewItem* item, int col, const TQString& str) {
1094 //kdDebug()<<k_funcinfo<<(item ? item->listViewText(col) : "null")<<": "<<str<<endl;
1095 if (col == 0) {
1096 emit itemRenamed(getNode(item), str);
1097 }
1098}
1099
1100 void GanttView::slotGvItemClicked(KDGanttViewItem *) {
1101}
1102
1103// testing
1104bool GanttView::exportGantt(TQIODevice* device) {
1105 kdDebug()<<k_funcinfo<<endl;
1106 return m_gantt->saveProject(device);
1107}
1108
1109void GanttView::slotLinkItems(KDGanttViewItem* from, KDGanttViewItem* to, int linkType) {
1110 //kdDebug()<<k_funcinfo<<(from?from->listViewText():"null")<<" to "<<(to?to->listViewText():"null")<<" linkType="<<linkType<<endl;
1111 Node *par = getNode(from);
1112 Node *child = getNode(to);
1113 if (!par || !child || !(par->legalToLink(child))) {
1114 KMessageBox::sorry(this, i18n("Cannot link these nodes"));
1115 return;
1116 }
1117 Relation *rel = child->findRelation(par);
1118 if (rel)
1119 emit modifyRelation(rel, linkTypeToRelation(linkType));
1120 else
1121 emit addRelation(par, child, linkTypeToRelation(linkType));
1122
1123 return;
1124}
1125
1126int GanttView::linkTypeToRelation(int linkType) {
1127 switch (linkType) {
1128 case KDGanttViewTaskLink::FinishStart:
1129 return Relation::FinishStart;
1130 break;
1131 case KDGanttViewTaskLink::StartStart:
1132 return Relation::StartStart;
1133 break;
1134 case KDGanttViewTaskLink::FinishFinish:
1135 return Relation::FinishFinish;
1136 break;
1137 case KDGanttViewTaskLink::StartFinish:
1138 default:
1139 return -1;
1140 break;
1141 }
1142}
1143
1144void GanttView::slotModifyLink(KDGanttViewTaskLink* link) {
1145 //kdDebug()<<k_funcinfo<<link<<endl;
1146 // we support only one from/to item in each link
1147 Node *par = getNode(link->from().first());
1148 Relation *rel = par->findRelation(getNode(link->to().first()));
1149 if (rel)
1150 emit modifyRelation(rel);
1151}
1152
1153bool GanttView::setContext(Context::Ganttview &context, Project& /*project*/) {
1154 //kdDebug()<<k_funcinfo<<endl;
1155
1156 TQValueList<int> list = sizes();
1157 list[0] = context.ganttviewsize;
1158 list[1] = context.taskviewsize;
1159 setSizes(list);
1160
1161 //TODO this does not work yet!
1162// currentItemChanged(findItem(project.findNode(context.currentNode)));
1163
1164 m_showResources = context.showResources ;
1165 m_showTaskName = context.showTaskName;
1166 m_showTaskLinks = context.showTaskLinks;
1167 m_showProgress = context.showProgress;
1168 m_showPositiveFloat = context.showPositiveFloat;
1169 m_showCriticalTasks = context.showCriticalTasks;
1170 m_showCriticalPath = context.showCriticalPath;
1171 m_showNoInformation = context.showNoInformation;
1172
1173 //TODO this does not work yet!
1174// getContextClosedNodes(context, m_gantt->firstChild());
1175// for (TQStringList::ConstIterator it = context.closedNodes.begin(); it != context.closedNodes.end(); ++it) {
1176// KDGanttViewItem *item = findItem(project.findNode(*it));
1177// if (item) {
1178// item->setOpen(false);
1179// }
1180// }
1181 return true;
1182}
1183
1184void GanttView::getContext(Context::Ganttview &context) const {
1185 //kdDebug()<<k_funcinfo<<endl;
1186 context.ganttviewsize = sizes()[0];
1187 context.taskviewsize = sizes()[1];
1188 //kdDebug()<<k_funcinfo<<"sizes="<<sizes()[0]<<","<<sizes()[1]<<endl;
1189 if (currentNode()) {
1190 context.currentNode = currentNode()->id();
1191 }
1192 context.showResources = m_showResources;
1193 context.showTaskName = m_showTaskName;
1194 context.showTaskLinks = m_showTaskLinks;
1195 context.showProgress = m_showProgress;
1196 context.showPositiveFloat = m_showPositiveFloat;
1197 context.showCriticalTasks = m_showCriticalTasks;
1198 context.showCriticalPath = m_showCriticalPath;
1199 context.showNoInformation = m_showNoInformation;
1200 getContextClosedNodes(context, m_gantt->firstChild());
1201}
1202
1203void GanttView::getContextClosedNodes(Context::Ganttview &context, KDGanttViewItem *item) const {
1204 if (item == 0)
1205 return;
1206 for (KDGanttViewItem *i = item; i; i = i->nextSibling()) {
1207 if (!i->isOpen()) {
1208 context.closedNodes.append(getNode(i)->id());
1209 //kdDebug()<<k_funcinfo<<"add closed "<<i->listViewText()<<endl;
1210 }
1211 getContextClosedNodes(context, i->firstChild());
1212 }
1213}
1214
1215void GanttView::setReadWriteMode(bool on) {
1216 m_readWrite = on;
1217 disconnect(m_gantt, TQ_SIGNAL(linkItems(KDGanttViewItem*, KDGanttViewItem*, int)), this, TQ_SLOT(slotLinkItems(KDGanttViewItem*, KDGanttViewItem*, int)));
1218 disconnect(m_gantt, TQ_SIGNAL(taskLinkDoubleClicked(KDGanttViewTaskLink*)), this, TQ_SLOT(slotModifyLink(KDGanttViewTaskLink*)));
1219 m_gantt->setLinkItemsEnabled(on);
1220
1221 if (on) {
1222 connect(m_gantt, TQ_SIGNAL(linkItems(KDGanttViewItem*, KDGanttViewItem*, int)), TQ_SLOT(slotLinkItems(KDGanttViewItem*, KDGanttViewItem*, int)));
1223
1224 connect(m_gantt, TQ_SIGNAL(taskLinkDoubleClicked(KDGanttViewTaskLink*)), TQ_SLOT(slotModifyLink(KDGanttViewTaskLink*)));
1225 }
1226 setRenameEnabled(m_gantt->firstChild(), on);
1227}
1228
1229void GanttView::setRenameEnabled(TQListViewItem *item, bool on) {
1230 if (item == 0)
1231 return;
1232 for (TQListViewItem *i = item; i; i = i->nextSibling()) {
1233 i->setRenameEnabled(0, on);
1234 setRenameEnabled(i->firstChild(), on);
1235 }
1236}
1237
1238} //KPlato namespace
1239
1240#include "kptganttview.moc"