kplato

kptresourceview.cpp
1/* This file is part of the KDE project
2 Copyright (C) 2003 - 2004 Dag Andersen kplato@kde.org>
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License as published by the Free Software Foundation;
7 version 2 of the License.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18*/
19
20#include "kptresourceview.h"
21
22#include "kptcalendar.h"
23#include "kptduration.h"
24#include "kptresourceappointmentsview.h"
25#include "kptview.h"
26#include "kptnode.h"
27#include "kptproject.h"
28#include "kpttask.h"
29#include "kptresource.h"
30#include "kptdatetime.h"
31#include "kptcontext.h"
32
33#include <tqheader.h>
34#include <tqpopupmenu.h>
35#include <tqpainter.h>
36#include <tqpaintdevicemetrics.h>
37#include <tqstyle.h>
38
39#include <tdelistview.h>
40#include <tdelocale.h>
41#include <tdeglobal.h>
42#include <kprinter.h>
43
44#include <kdebug.h>
45
46namespace KPlato
47{
48
49class ResListView : public TDEListView {
50public:
51 ResListView(TQWidget * parent = 0, const char* name=0)
52 : TDEListView(parent, name)
53 {}
54
55 int headerHeight() const {
56 return header()->count() > 0 ? header()->sectionRect(0).height() : 0;
57 }
58 virtual void paintToPrinter(TQPainter *p, int x, int y, int w, int h) {
59 p->save();
60 TQColor bgc(193, 223, 255);
61 TQBrush bg(bgc);
62 p->setBackgroundMode(TQt::OpaqueMode);
63 p->setBackgroundColor(bgc);
64 TQHeader *head = header();
65 int offset = 0;
66 TQRect sr;
67 // Header shall always be at top/left on page
68 for (int s = 0; s < head->count(); ++s) {
69 sr = head->sectionRect(s);
70 if (offset > sr.x())
71 offset = sr.x();
72 }
73 for (int s = 0; s < head->count(); ++s) {
74 sr = head->sectionRect(s);
75 if (offset != 0) {
76 sr = TQRect(sr.x()-offset, sr.y(), sr.width(), sr.height());
77 }
78 //kdDebug()<<s<<": "<<head->label(s)<<" "<<sr<<endl;
79 if (sr.x()+sr.width() <= x || sr.x() >= x+w) {
80 //kdDebug()<<s<<": "<<h->label(s)<<" "<<sr<<": continue"<<endl;
81 continue;
82 }
83 TQRect tr = sr;
84 if (sr.x() < x) {
85 tr.setX(x);
86 //kdDebug()<<s<<": "<<head->label(s)<<" "<<tr<<endl;
87 }
88 p->eraseRect(tr);
89 p->drawText(tr, columnAlignment(s)|TQt::AlignVCenter, head->label(s), -1);
90 }
91 p->restore();
92 p->save();
93 p->translate(0, headerHeight());
94 drawAllContents(p, x, y, w, h);
95 p->restore();
96 }
97 int calculateY(int ymin, int ymax) const {
98 TQPtrList<ResListView::DrawableItem> drawables;
99 drawables.setAutoDelete(true);
100 TQListViewItem *child = firstChild();
101 int level = 0;
102 int ypos = 0;
103 for (; child; child = child->nextSibling()) {
104 ypos = buildDrawables(drawables, level, ypos, child, ymin, ymax);
105 }
106 int y = 0;
107 DrawableItem *item = drawables.getLast();
108 if (item) {
109 y = item->y + item->i->height();
110 }
111 //kdDebug()<<k_funcinfo<<y<<" ("<<ymin<<", "<<ymax<<")"<<endl;
112 return y;
113 }
114 class DrawableItem {
115 public:
116 DrawableItem(int level, int ypos, TQListViewItem *item ) { y = ypos; l = level; i = item; };
117 int y;
118 int l;
119 TQListViewItem * i;
120 };
121protected:
122 int buildDrawables(TQPtrList<ResListView::DrawableItem> &lst, int level, int ypos, TQListViewItem *item, int ymin, int ymax) const {
123 int y = ypos;
124 int ih = item->height();
125 if (y < ymin && y+ih > ymin) {
126 y = ymin; // include partial item at top
127 }
128 if (y >= ymin && y+ih < ymax) { // exclude partial item at bottom
129 ResListView::DrawableItem *dr = new ResListView::DrawableItem(level, y, item);
130 lst.append(dr);
131 //kdDebug()<<k_funcinfo<<level<<", "<<y<<" : "<<item->text(0)<<endl;
132 }
133 y += ih;
134 if (item->isOpen()) {
135 TQListViewItem *child = item->firstChild();
136 for (; child; child = child->nextSibling()) {
137 y = buildDrawables(lst, level+1, y, child, ymin, ymax);
138 }
139 }
140 return y;
141 }
142 // This is a copy of TQListView::drawContentsOffset(), with a few changes
143 // because drawContentsOffset() only draws *visible* items,
144 // we want to draw *all* items.
145 // FIXME: Haven't got paintBraches() to work, atm live without it.
146 virtual void drawAllContents(TQPainter * p, int cx, int cy, int cw, int ch) {
147 if ( columns() == 0 ) {
148 paintEmptyArea( p, TQRect( cx, cy, cw, ch ) );
149 return;
150 }
151 //kdDebug()<<k_funcinfo<<TQRect(cx, cy, cw, ch)<<endl;
152 TQPtrList<ResListView::DrawableItem> drawables;
153 drawables.setAutoDelete(true);
154 TQListViewItem *child = firstChild();
155 int level = 0;
156 int ypos = 0;
157 for (; child; child = child->nextSibling()) {
158 ypos = buildDrawables(drawables, level, ypos, child, cy, cy+ch);
159 }
160
161 p->setFont( font() );
162
163 TQPtrListIterator<ResListView::DrawableItem> it(drawables);
164
165 TQRect r;
166 int fx = -1, x, fc = 0, lc = 0;
167 int tx = -1;
168 ResListView::DrawableItem * current;
169
170 while ( (current = it.current()) != 0 ) {
171 ++it;
172 int ih = current->i->height();
173 int ith = current->i->totalHeight();
174 int c;
175 int cs;
176
177 // need to paint current?
178 if ( ih > 0 && current->y < cy+ch && current->y+ih > cy ) {
179 //kdDebug()<<k_funcinfo<<"Paint: "<<current->i->text(0)<<" y="<<current->y<<endl;
180 if ( fx < 0 ) {
181 // find first interesting column, once
182 x = 0;
183 c = 0;
184 cs = header()->cellSize( 0 );
185 while ( x + cs <= cx && c < header()->count() ) {
186 x += cs;
187 c++;
188 if ( c < header()->count() )
189 cs = header()->cellSize( c );
190 }
191 fx = x;
192 fc = c;
193 while( x < cx + cw && c < header()->count() ) {
194 x += cs;
195 c++;
196 if ( c < header()->count() )
197 cs = header()->cellSize( c );
198 }
199 lc = c;
200 }
201
202 x = fx;
203 c = fc;
204 // draw to last interesting column
205
206 const TQColorGroup &cg = ( palette().inactive() );
207
208 while ( c < lc && !drawables.isEmpty() ) {
209 int i = header()->mapToLogical( c );
210 cs = header()->cellSize( c );
211 r.setRect( x, current->y-cy, cs, ih );
212 if ( i == 0 )
213 r.setLeft( r.left() + current->l * treeStepSize() );
214
215 p->save();
216 // No need to paint if the cell isn't technically visible
217 if ( !( r.width() == 0 || r.height() == 0 ) ) {
218 p->translate( r.left(), r.top() );
219 int ac = header()->mapToLogical( c );
220 // map to Left currently. This should change once we
221 // can really reverse the listview.
222 int align = columnAlignment( ac );
223 if ( align == AlignAuto ) align = AlignLeft;
224 bool sel = current->i->isSelected();
225 if (sel)
226 current->i->setSelected(false);
227 current->i->paintCell( p, cg, ac, r.width(), align );
228 if (sel)
229 current->i->setSelected(sel);
230 }
231 p->restore();
232 x += cs;
233 c++;
234 }
235
236 }
237
238 const int cell = header()->mapToActual( 0 );
239
240 if ( tx < 0 )
241 tx = header()->cellPos( cell );
242
243 // do any children of current need to be painted?
244/* FIXME: painting branches doesn't work for some reason...
245 if ( ih != ith &&
246 rootIsDecorated() &&
247 current->y + ith > cy &&
248 current->y + ih < cy + ch &&
249 tx + current->l * treeStepSize() < cx + cw &&
250 tx + (current->l+1) * treeStepSize() > cx ) {
251 // compute the clip rectangle the safe way
252
253 int rtop = current->y + ih;
254 int rbottom = current->y + ith;
255 int rleft = tx + current->l*treeStepSize();
256 int rright = rleft + treeStepSize();
257
258 int crtop = TQMAX( rtop, cy );
259 int crbottom = TQMIN( rbottom, cy+ch );
260 int crleft = TQMAX( rleft, cx );
261 int crright = TQMIN( rright, cx+cw );
262
263 r.setRect( crleft, crtop,
264 crright-crleft, crbottom-crtop );
265
266 if ( r.isValid() ) {
267 p->save();
268 p->translate( rleft, crtop );
269 //kdDebug()<<k_funcinfo<<"paintBranches: "<<current->i->text(0)<<endl;
270
271 current->i->paintBranches( p, colorGroup(), treeStepSize(),
272 rtop - crtop, r.height() );
273 p->restore();
274 }
275 }*/
276 }
277 }
278
279};
280
281class ResourceItemPrivate : public TDEListViewItem {
282public:
283 ResourceItemPrivate(Resource *r, TQListViewItem *parent)
284 : TDEListViewItem(parent, r->name()),
285 resource(r) {}
286
287 Resource *resource;
288
289 virtual void paintCell(TQPainter *p, const TQColorGroup &cg, int column, int width, int align) {
290 TQColorGroup g = cg;
291 if (m_columns[column] == 1) {
292 g.setColor(TQColorGroup::Text, TQColor(red));
293 g.setColor(TQColorGroup::HighlightedText, TQColor(red));
294 }
295
296 TDEListViewItem::paintCell(p, g, column, width, align);
297 }
298 void setColumnState(int c, int state=1) {
299 m_columns[c] = state;
300 }
301private:
302 TQMap<int, int> m_columns;
303};
304
305class NodeItemPrivate : public TDEListViewItem {
306public:
307 NodeItemPrivate(Task *n, TQListView *parent)
308 : TDEListViewItem(parent, n->name()),
309 node(n) {
310 init();
311 }
312
313 NodeItemPrivate(TQString name, TQListView *parent)
314 : TDEListViewItem(parent, name),
315 node(0) {
316 init();
317 }
318
319 void setPriority(int col, int prio) {
320 if (prioColors.contains(prio)) {
321 columnPrio.insert(col, prio);
322 } else {
323 columnPrio.remove(col);
324 }
325 }
326 int priority(int col) {
327 if (columnPrio.contains(col)) {
328 return columnPrio[col];
329 }
330 return 0;
331 }
332
333 virtual void paintCell ( TQPainter * p, const TQColorGroup & cg, int column, int width, int align ) {
334 //kdDebug()<<k_funcinfo<<"c="<<column<<" prio="<<(columnPrio.contains(column)?columnPrio[column]:0)<<endl;
335 TQColorGroup g = cg;
336 if (columnPrio.contains(column)) {
337 g.setColor(TQColorGroup::Base, prioColors[columnPrio[column]]);
338 }
339 TDEListViewItem::paintCell(p, g, column, width, align);
340 }
341
342 Task *node;
343private:
344 void init() {
345 prioColors.insert(1, TQColor(gray));
346 prioColors.insert(2, TQColor(green));
347 prioColors.insert(3, TQColor(yellow));
348 prioColors.insert(4, TQColor(red));
349 }
350 TQMap<int, TQColor> prioColors;
351 TQMap<int, int> columnPrio;
352};
353
354class AppointmentItem : public TDEListViewItem {
355public:
356 AppointmentItem(Appointment *a, TQDate &d, TQListViewItem *parent)
357 : TDEListViewItem(parent),
358 appointment(a),
359 date(d) {}
360
361 Appointment *appointment;
362 TQDate date;
363};
364
365TQSize ResourceView::sizeHint() const {
366 return minimumSizeHint(); // HACK: koshell splitter minimumSize problem
367}
368
369ResourceView::ResourceView(View *view, TQWidget *parent)
370 : TQSplitter(parent, "Resource view"),
371 m_mainview(view),
372 m_selectedItem(0),
373 m_currentNode(0)
374{
375 setOrientation(TQt::Vertical);
376
377 resList = new ResListView(this, "Resource list");
378 resList->setItemMargin(2);
379#if KDE_IS_VERSION(3,3,9)
380 resList->setShadeSortColumn(false);
381#endif
382 resList->setRootIsDecorated(true);
383 resList->addColumn(i18n("Name"));
384 resList->setColumnAlignment(1, AlignHCenter);
385 resList->addColumn(i18n("Type"));
386 resList->setColumnAlignment(2, AlignHCenter);
387 resList->addColumn(i18n("Initials"));
388 resList->setColumnAlignment(3, AlignLeft);
389 resList->addColumn(i18n("Email"));
390 resList->setColumnAlignment(4, AlignHCenter);
391 resList->addColumn(i18n("Calendar Name"));
392 resList->setColumnAlignment(5, AlignRight);
393 resList->addColumn(i18n("Available From"));
394 resList->setColumnAlignment(6, AlignRight);
395 resList->addColumn(i18n("Available Until"));
396 resList->setColumnAlignment(7, AlignRight);
397 resList->addColumn(i18n("%"));
398 resList->setColumnAlignment(8, AlignRight);
399 resList->addColumn(i18n("Normal Rate"));
400 resList->setColumnAlignment(9, AlignRight);
401 resList->addColumn(i18n("Overtime Rate"));
402
403 m_showAppointments = false;
404 m_appview = new ResourceAppointmentsView(view, this);
405 m_appview->hide();
406 draw(view->getProject());
407
408 //connect(resList, TQ_SIGNAL(selectionChanged(TQListViewItem*)), TQ_SLOT(resSelectionChanged(TQListViewItem*)));
409 connect(resList, TQ_SIGNAL(selectionChanged()), TQ_SLOT(resSelectionChanged()));
410 connect(resList, TQ_SIGNAL( contextMenuRequested(TQListViewItem*, const TQPoint&, int)), TQ_SLOT(popupMenuRequested(TQListViewItem*, const TQPoint&, int)));
411 //NOTE: using doubleClicked, not executed() to be consistent with ganttview
412 connect(resList, TQ_SIGNAL(doubleClicked(TQListViewItem*, const TQPoint&, int)), TQ_SLOT(slotItemDoubleClicked(TQListViewItem*)));
413
414}
415
416void ResourceView::zoom(double /*zoom*/)
417{
418}
419
420Resource *ResourceView::currentResource() {
421 if (m_selectedItem)
422 return m_selectedItem->resource;
423 return 0;
424}
425
426void ResourceView::draw(Project &project)
427{
428 //kdDebug()<<k_funcinfo<<endl;
429 resList->clear();
430 m_appview->clear();
431 m_selectedItem = 0;
432
433 TQPtrListIterator<ResourceGroup> it(project.resourceGroups());
434 for (; it.current(); ++it) {
435 TDEListViewItem *item = new TDEListViewItem(resList, it.current()->name());
436 item->setOpen(true);
437 drawResources(project, item, it.current());
438 }
439 if (m_selectedItem) {
440 resList->setSelected(m_selectedItem, true);
441 } else {
442 resSelectionChanged(m_selectedItem);
443 }
444}
445
446
447void ResourceView::drawResources(const Project &proj, TQListViewItem *parent, ResourceGroup *group)
448{
449 //kdDebug()<<k_funcinfo<<"group: "<<group->name()<<" ("<<group<<")"<<endl;
450 TQPtrListIterator<Resource> it(group->resources());
451 for (; it.current(); ++it) {
452 Resource *r = it.current();
453 ResourceItemPrivate *item = new ResourceItemPrivate(r, parent);
454 // set column colors
455 item->setColumnState(0, 0);
456 item->setColumnState(4, 0);
457 item->setColumnState(5, 0);
458 item->setColumnState(6, 0);
459 item->setColumnState(7, 0);
460 if (r->calendar() == 0) {
461 item->setColumnState(0, 1);
462 item->setColumnState(4, 1);
463 }
464 if (proj.constraint() == Node::MustFinishOn) {
465 if (proj.mustFinishOn() <= r->availableFrom()) {
466 item->setColumnState(0, 1);
467 item->setColumnState(5, 1);
468 }
469 } else {
470 if (proj.mustStartOn() >= r->availableUntil()) {
471 item->setColumnState(0, 1);
472 item->setColumnState(6, 1);
473 }
474 }
475 if (r->units() == 0) {
476 item->setColumnState(0, 1);
477 item->setColumnState(7, 1);
478 }
479 // and the texts
480 item->setText(0, r->name()); // refresh
481 switch (r->type()) {
482 case Resource::Type_Work:
483 item->setText(1, i18n("Work"));
484 break;
485 case Resource::Type_Material:
486 item->setText(1, i18n("Material"));
487 break;
488 default:
489 item->setText(1, i18n("Undefined"));
490 break;
491 }
492 item->setText(2, r->initials());
493 item->setText(3, r->email());
494 item->setText(4, r->calendar() ? r->calendar()->name() : i18n("None"));
495 item->setText(5, TDEGlobal::locale()->formatDateTime(r->availableFrom()));
496 item->setText(6, TDEGlobal::locale()->formatDateTime(r->availableUntil()));
497 item->setText(7, TQString().setNum(r->units()));
498 item->setText(8, TDEGlobal::locale()->formatMoney(r->normalRate()));
499 item->setText(9, TDEGlobal::locale()->formatMoney(r->overtimeRate()));
500 if (!m_selectedItem) {
501 m_selectedItem = item;
502 }
503 }
504}
505
506
507void ResourceView::resSelectionChanged() {
508 //kdDebug()<<k_funcinfo<<endl;
509 resSelectionChanged(resList->selectedItem());
510}
511
512void ResourceView::resSelectionChanged(TQListViewItem *item) {
513 //kdDebug()<<k_funcinfo<<item<<endl;
514 ResourceItemPrivate *ritem = dynamic_cast<ResourceItemPrivate *>(item);
515 if (ritem) {
516 m_selectedItem = ritem;
517 if (m_showAppointments) {
518 m_appview->show();
519 m_appview->draw(ritem->resource, m_mainview->getProject().startTime().date(), m_mainview->getProject().endTime().date());
520 } else {
521 m_appview->hide();
522 }
523 return;
524 }
525 m_selectedItem = 0;
526 m_appview->clear();
527}
528
529
530void ResourceView::slotItemDoubleClicked(TQListViewItem*) {
531 emit itemDoubleClicked();
532}
533
534void ResourceView::popupMenuRequested(TQListViewItem* item, const TQPoint & pos, int)
535{
536 ResourceItemPrivate *ritem = dynamic_cast<ResourceItemPrivate *>(item);
537 if (ritem) {
538 if (ritem != m_selectedItem)
539 resSelectionChanged(ritem);
540 TQPopupMenu *menu = m_mainview->popupMenu("resource_popup");
541 if (menu)
542 {
543 menu->exec(pos);
544 //kdDebug()<<k_funcinfo<<"id="<<id<<endl;
545 }
546 else
547 kdDebug()<<k_funcinfo<<"No menu!"<<endl;
548 }
549}
550
551TQValueList<int> ResourceView::listOffsets(int pageHeight) const {
552 TQValueList<int> lst;
553 int hh = resList->headerHeight();
554 int ph = pageHeight-hh;
555 int lh = resList->contentsHeight() - hh; // list height ex header.
556 int ly = 0;
557 kdDebug()<<k_funcinfo<<ly<<", "<<lh<<endl;
558 while (ly < lh) {
559 lst << ly;
560 ly = resList->calculateY(ly+1, ly+ph); // offset into the list, ex header
561 //kdDebug()<<k_funcinfo<<ly<<", "<<lh<<endl;
562 }
563 return lst;
564}
565
566void ResourceView::print(KPrinter &printer) {
567 //kdDebug()<<k_funcinfo<<endl;
568 TQPaintDeviceMetrics m = TQPaintDeviceMetrics ( &printer );
569 uint top, left, bottom, right;
570 printer.margins(&top, &left, &bottom, &right);
571 //kdDebug()<<m.width()<<"x"<<m.height()<<" : "<<top<<", "<<left<<", "<<bottom<<", "<<right<<" : "<<size()<<endl;
572 TQPainter p;
573 p.begin(&printer);
574 p.setViewport(left, top, m.width()-left-right, m.height()-top-bottom);
575 p.setClipRect(left, top, m.width()-left-right, m.height()-top-bottom);
576 TQRect preg = p.clipRegion(TQPainter::CoordPainter).boundingRect();
577 //kdDebug()<<"p="<<preg<<endl;
578 //p.drawRect(preg.x(), preg.y(), preg.width()-1, preg.height()-1);
579 int ch = resList->contentsHeight();
580 int cw = resList->contentsWidth();
581 double scale = (double)preg.width()/(double)(cw);
582 //kdDebug()<<"scale="<<scale<<endl;
583 if (scale < 1.0) {
584 p.scale(scale, scale);
585 }
586 int ph = preg.height()-resList->headerHeight();
587 TQValueList<int> lst = listOffsets(preg.height());
588 for (int i=0; i < lst.count(); ++i) {
589 //kdDebug()<<"Page "<<i+1<<": "<<"scale="<<scale<<" "<<lst[i]<<" : "<<cw<<"x"<<ch<<endl;
590 if (i > 0) {
591 printer.newPage();
592 }
593 resList->paintToPrinter(&p, 0, lst[i], cw, ph);
594 }
595
596 p.end();
597}
598
599bool ResourceView::setContext(Context::Resourceview &/*context*/) {
600 //kdDebug()<<k_funcinfo<<endl;
601 return true;
602}
603
604void ResourceView::getContext(Context::Resourceview &/*context*/) const {
605 //kdDebug()<<k_funcinfo<<endl;
606}
607
608
609} //KPlato namespace
610
611#include "kptresourceview.moc"