kplato

kptresourceview.cc

00001 /* This file is part of the KDE project
00002    Copyright (C) 2003 - 2004 Dag Andersen kplato@kde.org>
00003 
00004    This library is free software; you can redistribute it and/or
00005    modify it under the terms of the GNU Library General Public
00006    License as published by the Free Software Foundation;
00007    version 2 of the License.
00008 
00009    This library is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY; without even the implied warranty of
00011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012    Library General Public License for more details.
00013 
00014    You should have received a copy of the GNU Library General Public License
00015    along with this library; see the file COPYING.LIB.  If not, write to
00016    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00017  * Boston, MA 02110-1301, USA.
00018 */
00019 
00020 #include "kptresourceview.h"
00021 
00022 #include "kptcalendar.h"
00023 #include "kptduration.h"
00024 #include "kptresourceappointmentsview.h"
00025 #include "kptview.h"
00026 #include "kptnode.h"
00027 #include "kptproject.h"
00028 #include "kpttask.h"
00029 #include "kptresource.h"
00030 #include "kptdatetime.h"
00031 #include "kptcontext.h"
00032 
00033 #include <qheader.h>
00034 #include <qpopupmenu.h>
00035 #include <qpainter.h>
00036 #include <qpaintdevicemetrics.h>
00037 #include <qstyle.h>
00038 
00039 #include <klistview.h>
00040 #include <klocale.h>
00041 #include <kglobal.h>
00042 #include <kprinter.h>
00043 
00044 #include <kdebug.h>
00045 
00046 namespace KPlato
00047 {
00048 
00049 class ResListView : public KListView {
00050 public:
00051     ResListView(QWidget * parent = 0, const char* name=0)
00052     : KListView(parent, name)
00053     {}
00054 
00055     int headerHeight() const {
00056         return header()->count() > 0 ? header()->sectionRect(0).height() : 0;
00057     }
00058     virtual void paintToPrinter(QPainter *p, int x, int y, int w, int h) {
00059         p->save();
00060         QColor bgc(193, 223, 255);
00061         QBrush bg(bgc);
00062         p->setBackgroundMode(Qt::OpaqueMode);
00063         p->setBackgroundColor(bgc);
00064         QHeader *head = header();
00065         int offset = 0;
00066         QRect sr;
00067         // Header shall always be at top/left on page
00068         for (int s = 0; s < head->count(); ++s) {
00069             sr = head->sectionRect(s);
00070             if (offset > sr.x())
00071                 offset = sr.x();
00072         }
00073         for (int s = 0; s < head->count(); ++s) {
00074             sr = head->sectionRect(s);
00075             if (offset != 0) {
00076                 sr = QRect(sr.x()-offset, sr.y(), sr.width(), sr.height());
00077             }
00078             //kdDebug()<<s<<": "<<head->label(s)<<" "<<sr<<endl;
00079             if (sr.x()+sr.width() <= x || sr.x() >= x+w) {
00080                 //kdDebug()<<s<<": "<<h->label(s)<<" "<<sr<<": continue"<<endl;
00081                 continue;
00082             }
00083             QRect tr = sr;
00084             if (sr.x() < x) {
00085                 tr.setX(x);
00086                 //kdDebug()<<s<<": "<<head->label(s)<<" "<<tr<<endl;
00087             }
00088             p->eraseRect(tr);
00089             p->drawText(tr, columnAlignment(s)|Qt::AlignVCenter, head->label(s), -1);
00090         }
00091         p->restore();
00092         p->save();
00093         p->translate(0, headerHeight());
00094         drawAllContents(p, x, y, w, h);
00095         p->restore();
00096     }
00097     int calculateY(int ymin, int ymax) const {
00098         QPtrList<ResListView::DrawableItem> drawables;
00099         drawables.setAutoDelete(true);
00100         QListViewItem *child = firstChild();
00101         int level = 0;
00102         int ypos = 0;
00103         for (; child; child = child->nextSibling()) {
00104             ypos = buildDrawables(drawables, level, ypos, child, ymin, ymax);
00105         }
00106         int y = 0;
00107         DrawableItem *item = drawables.getLast();
00108         if (item) {
00109             y = item->y + item->i->height();
00110         }
00111         //kdDebug()<<k_funcinfo<<y<<" ("<<ymin<<", "<<ymax<<")"<<endl;
00112         return y;
00113     }
00114     class DrawableItem {
00115     public:
00116         DrawableItem(int level, int ypos, QListViewItem *item ) { y = ypos; l = level; i = item; };
00117         int y;
00118         int l;
00119         QListViewItem * i;
00120     };
00121 protected:
00122     int buildDrawables(QPtrList<ResListView::DrawableItem> &lst, int level, int ypos, QListViewItem *item, int ymin, int ymax) const {
00123         int y = ypos;
00124         int ih = item->height();
00125         if (y < ymin && y+ih > ymin) {
00126             y = ymin; // include partial item at top
00127         }
00128         if (y >= ymin && y+ih < ymax) { // exclude partial item at bottom
00129             ResListView::DrawableItem *dr = new ResListView::DrawableItem(level, y, item);
00130             lst.append(dr);
00131             //kdDebug()<<k_funcinfo<<level<<", "<<y<<" : "<<item->text(0)<<endl;
00132         }
00133         y += ih;
00134         if (item->isOpen()) {
00135             QListViewItem *child = item->firstChild();
00136             for (; child; child = child->nextSibling()) {
00137                 y = buildDrawables(lst, level+1, y, child, ymin, ymax);
00138             }
00139         }
00140         return y;
00141     }
00142     // This is a copy of QListView::drawContentsOffset(), with a few changes
00143     // because drawContentsOffset() only draws *visible* items,
00144     // we want to draw *all* items.
00145     // FIXME: Haven't got paintBraches() to work, atm live without it.
00146     virtual void drawAllContents(QPainter * p, int cx, int cy, int cw, int ch) {
00147         if ( columns() == 0 ) {
00148             paintEmptyArea( p, QRect( cx, cy, cw, ch ) );
00149             return;
00150         }
00151         //kdDebug()<<k_funcinfo<<QRect(cx, cy, cw, ch)<<endl;
00152         QPtrList<ResListView::DrawableItem> drawables;
00153         drawables.setAutoDelete(true);
00154         QListViewItem *child = firstChild();
00155         int level = 0;
00156         int ypos = 0;
00157         for (; child; child = child->nextSibling()) {
00158             ypos = buildDrawables(drawables, level, ypos, child, cy, cy+ch);
00159         }
00160         
00161         p->setFont( font() );
00162     
00163         QPtrListIterator<ResListView::DrawableItem> it(drawables);
00164     
00165         QRect r;
00166         int fx = -1, x, fc = 0, lc = 0;
00167         int tx = -1;
00168         ResListView::DrawableItem * current;
00169     
00170         while ( (current = it.current()) != 0 ) {
00171             ++it;
00172             int ih = current->i->height();
00173             int ith = current->i->totalHeight();
00174             int c;
00175             int cs;
00176     
00177             // need to paint current?
00178             if ( ih > 0 && current->y < cy+ch && current->y+ih > cy ) {
00179                 //kdDebug()<<k_funcinfo<<"Paint: "<<current->i->text(0)<<" y="<<current->y<<endl;
00180                 if ( fx < 0 ) {
00181                     // find first interesting column, once
00182                     x = 0;
00183                     c = 0;
00184                     cs = header()->cellSize( 0 );
00185                     while ( x + cs <= cx && c < header()->count() ) {
00186                         x += cs;
00187                         c++;
00188                         if ( c < header()->count() )
00189                             cs = header()->cellSize( c );
00190                     }
00191                     fx = x;
00192                     fc = c;
00193                     while( x < cx + cw && c < header()->count() ) {
00194                         x += cs;
00195                         c++;
00196                         if ( c < header()->count() )
00197                             cs = header()->cellSize( c );
00198                     }
00199                     lc = c;
00200                 }
00201     
00202                 x = fx;
00203                 c = fc;
00204                 // draw to last interesting column
00205     
00206                 const QColorGroup &cg = ( palette().inactive() );
00207     
00208                 while ( c < lc && !drawables.isEmpty() ) {
00209                     int i = header()->mapToLogical( c );
00210                     cs = header()->cellSize( c );
00211                     r.setRect( x, current->y-cy, cs, ih );
00212                     if ( i == 0 )
00213                         r.setLeft( r.left() + current->l * treeStepSize() );
00214     
00215                     p->save();
00216                     // No need to paint if the cell isn't technically visible
00217                     if ( !( r.width() == 0 || r.height() == 0 ) ) {
00218                         p->translate( r.left(), r.top() );
00219                         int ac = header()->mapToLogical( c );
00220                         // map to Left currently. This should change once we
00221                         // can really reverse the listview.
00222                         int align = columnAlignment( ac );
00223                         if ( align == AlignAuto ) align = AlignLeft;
00224                         bool sel = current->i->isSelected();
00225                         if (sel)
00226                             current->i->setSelected(false);
00227                         current->i->paintCell( p, cg, ac, r.width(), align );
00228                         if (sel)
00229                             current->i->setSelected(sel);
00230                     }
00231                     p->restore();
00232                     x += cs;
00233                     c++;
00234                 }
00235     
00236             }
00237     
00238             const int cell = header()->mapToActual( 0 );
00239     
00240             if ( tx < 0 )
00241                 tx = header()->cellPos( cell );
00242     
00243             // do any children of current need to be painted?
00244 /* FIXME: painting branches doesn't work for some reason...
00245               if ( ih != ith && 
00246                  rootIsDecorated() &&
00247                  current->y + ith > cy &&
00248                  current->y + ih < cy + ch &&
00249                  tx + current->l * treeStepSize() < cx + cw &&
00250                  tx + (current->l+1) * treeStepSize() > cx ) {
00251                 // compute the clip rectangle the safe way
00252     
00253                 int rtop = current->y + ih;
00254                 int rbottom = current->y + ith;
00255                 int rleft = tx + current->l*treeStepSize();
00256                 int rright = rleft + treeStepSize();
00257     
00258                 int crtop = QMAX( rtop, cy );
00259                 int crbottom = QMIN( rbottom, cy+ch );
00260                 int crleft = QMAX( rleft, cx );
00261                 int crright = QMIN( rright, cx+cw );
00262     
00263                 r.setRect( crleft, crtop,
00264                         crright-crleft, crbottom-crtop );
00265     
00266                 if ( r.isValid() ) {
00267                     p->save();
00268                     p->translate( rleft, crtop );
00269                     //kdDebug()<<k_funcinfo<<"paintBranches: "<<current->i->text(0)<<endl;
00270 
00271                      current->i->paintBranches( p, colorGroup(), treeStepSize(),
00272                                              rtop - crtop, r.height() );
00273                     p->restore();
00274                 }
00275             }*/
00276         }
00277     }
00278 
00279 };
00280 
00281 class ResourceItemPrivate : public KListViewItem {
00282 public:
00283     ResourceItemPrivate(Resource *r, QListViewItem *parent)
00284         : KListViewItem(parent, r->name()),
00285         resource(r) {}
00286 
00287     Resource *resource;
00288 
00289     virtual void paintCell(QPainter *p, const QColorGroup &cg, int column, int width, int align) {
00290         QColorGroup g = cg;
00291         if (m_columns[column] == 1) {
00292             g.setColor(QColorGroup::Text, QColor(red));
00293             g.setColor(QColorGroup::HighlightedText, QColor(red));
00294         }
00295 
00296         KListViewItem::paintCell(p, g, column, width, align);
00297     }
00298     void setColumnState(int c, int state=1) {
00299         m_columns[c] = state;
00300     }
00301 private:
00302     QMap<int, int> m_columns;
00303 };
00304 
00305 class NodeItemPrivate : public KListViewItem {
00306 public:
00307     NodeItemPrivate(Task *n, QListView *parent)
00308     : KListViewItem(parent, n->name()),
00309       node(n) {
00310         init();
00311     }
00312 
00313     NodeItemPrivate(QString name, QListView *parent)
00314     : KListViewItem(parent, name),
00315       node(0) {
00316         init();
00317     }
00318 
00319     void setPriority(int col, int prio) {
00320         if (prioColors.contains(prio)) {
00321             columnPrio.insert(col, prio);
00322         } else {
00323             columnPrio.remove(col);
00324         }
00325     }
00326     int priority(int col) {
00327         if (columnPrio.contains(col)) {
00328             return columnPrio[col];
00329         }
00330         return 0;
00331     }
00332         
00333     virtual void paintCell ( QPainter * p, const QColorGroup & cg, int column, int width, int align ) {
00334         //kdDebug()<<k_funcinfo<<"c="<<column<<" prio="<<(columnPrio.contains(column)?columnPrio[column]:0)<<endl;
00335         QColorGroup g = cg;
00336         if (columnPrio.contains(column)) {
00337             g.setColor(QColorGroup::Base, prioColors[columnPrio[column]]);
00338         }
00339         KListViewItem::paintCell(p, g, column, width, align);
00340     }
00341     
00342     Task *node;
00343 private:
00344     void init() {
00345         prioColors.insert(1, QColor(gray));
00346         prioColors.insert(2, QColor(green));
00347         prioColors.insert(3, QColor(yellow));
00348         prioColors.insert(4, QColor(red));
00349     }
00350     QMap<int, QColor> prioColors;
00351     QMap<int, int> columnPrio;
00352 };
00353 
00354 class AppointmentItem : public KListViewItem {
00355 public:
00356     AppointmentItem(Appointment *a, QDate &d, QListViewItem *parent)
00357         : KListViewItem(parent),
00358         appointment(a),
00359         date(d) {}
00360 
00361     Appointment *appointment;
00362     QDate date;
00363 };
00364 
00365 QSize ResourceView::sizeHint() const {
00366     return minimumSizeHint(); // HACK: koshell splitter minimumSize problem
00367 }
00368 
00369 ResourceView::ResourceView(View *view, QWidget *parent)
00370     : QSplitter(parent, "Resource view"),
00371     m_mainview(view),
00372     m_selectedItem(0),
00373     m_currentNode(0)
00374 {
00375     setOrientation(QSplitter::Vertical);
00376 
00377     resList = new ResListView(this, "Resource list");
00378     resList->setItemMargin(2);
00379 #if KDE_IS_VERSION(3,3,9)
00380     resList->setShadeSortColumn(false);
00381 #endif
00382     resList->setRootIsDecorated(true);
00383     resList->addColumn(i18n("Name"));
00384     resList->setColumnAlignment(1, AlignHCenter);
00385     resList->addColumn(i18n("Type"));
00386     resList->setColumnAlignment(2, AlignHCenter);
00387     resList->addColumn(i18n("Initials"));
00388     resList->setColumnAlignment(3, AlignLeft);
00389     resList->addColumn(i18n("Email"));
00390     resList->setColumnAlignment(4, AlignHCenter);
00391     resList->addColumn(i18n("Calendar Name"));
00392     resList->setColumnAlignment(5, AlignRight);
00393     resList->addColumn(i18n("Available From"));
00394     resList->setColumnAlignment(6, AlignRight);
00395     resList->addColumn(i18n("Available Until"));
00396     resList->setColumnAlignment(7, AlignRight);
00397     resList->addColumn(i18n("%"));
00398     resList->setColumnAlignment(8, AlignRight);
00399     resList->addColumn(i18n("Normal Rate"));
00400     resList->setColumnAlignment(9, AlignRight);
00401     resList->addColumn(i18n("Overtime Rate"));
00402 
00403     m_showAppointments = false;
00404     m_appview = new ResourceAppointmentsView(view, this);
00405     m_appview->hide();
00406     draw(view->getProject());
00407 
00408     //connect(resList, SIGNAL(selectionChanged(QListViewItem*)), SLOT(resSelectionChanged(QListViewItem*)));
00409     connect(resList, SIGNAL(selectionChanged()), SLOT(resSelectionChanged()));
00410     connect(resList, SIGNAL( contextMenuRequested(QListViewItem*, const QPoint&, int)), SLOT(popupMenuRequested(QListViewItem*, const QPoint&, int)));
00411     //NOTE: using doubleClicked, not executed() to be consistent with ganttview
00412     connect(resList, SIGNAL(doubleClicked(QListViewItem*, const QPoint&, int)), SLOT(slotItemDoubleClicked(QListViewItem*)));
00413 
00414 }
00415 
00416 void ResourceView::zoom(double /*zoom*/)
00417 {
00418 }
00419 
00420 Resource *ResourceView::currentResource() {
00421     if (m_selectedItem)
00422         return m_selectedItem->resource;
00423     return 0;
00424 }
00425 
00426 void ResourceView::draw(Project &project)
00427 {
00428     //kdDebug()<<k_funcinfo<<endl;
00429     resList->clear();
00430     m_appview->clear();
00431     m_selectedItem = 0;
00432 
00433     QPtrListIterator<ResourceGroup> it(project.resourceGroups());
00434     for (; it.current(); ++it) {
00435         KListViewItem *item = new KListViewItem(resList, it.current()->name());
00436         item->setOpen(true);
00437         drawResources(project, item, it.current());
00438     }
00439     if (m_selectedItem) {
00440         resList->setSelected(m_selectedItem, true);
00441     } else {
00442         resSelectionChanged(m_selectedItem);
00443     }
00444 }
00445 
00446 
00447 void ResourceView::drawResources(const Project &proj, QListViewItem *parent, ResourceGroup *group)
00448 {
00449     //kdDebug()<<k_funcinfo<<"group: "<<group->name()<<" ("<<group<<")"<<endl;
00450     QPtrListIterator<Resource> it(group->resources());
00451     for (; it.current(); ++it) {
00452         Resource *r = it.current();
00453         ResourceItemPrivate *item = new ResourceItemPrivate(r, parent);
00454         // set column colors
00455         item->setColumnState(0, 0);
00456         item->setColumnState(4, 0);
00457         item->setColumnState(5, 0);
00458         item->setColumnState(6, 0);
00459         item->setColumnState(7, 0);
00460         if (r->calendar() == 0) {
00461             item->setColumnState(0, 1);
00462             item->setColumnState(4, 1);
00463         }
00464         if (proj.constraint() == Node::MustFinishOn) {
00465             if (proj.mustFinishOn() <= r->availableFrom()) {
00466                 item->setColumnState(0, 1);
00467                 item->setColumnState(5, 1);
00468             }
00469         } else {
00470             if (proj.mustStartOn() >= r->availableUntil()) {
00471                 item->setColumnState(0, 1);
00472                 item->setColumnState(6, 1);
00473             }
00474         }
00475         if (r->units() == 0) {
00476             item->setColumnState(0, 1);
00477             item->setColumnState(7, 1);
00478         }
00479         // and the texts
00480         item->setText(0, r->name()); // refresh
00481         switch (r->type()) {
00482             case Resource::Type_Work:
00483                 item->setText(1, i18n("Work"));
00484                 break;
00485             case Resource::Type_Material:
00486                 item->setText(1, i18n("Material"));
00487                 break;
00488             default:
00489                 item->setText(1, i18n("Undefined"));
00490                 break;
00491         }
00492         item->setText(2, r->initials());
00493         item->setText(3, r->email());
00494         item->setText(4, r->calendar() ? r->calendar()->name() : i18n("None"));
00495         item->setText(5, KGlobal::locale()->formatDateTime(r->availableFrom()));
00496         item->setText(6, KGlobal::locale()->formatDateTime(r->availableUntil()));
00497         item->setText(7, QString().setNum(r->units()));
00498         item->setText(8, KGlobal::locale()->formatMoney(r->normalRate()));
00499         item->setText(9, KGlobal::locale()->formatMoney(r->overtimeRate()));
00500         if (!m_selectedItem) {
00501             m_selectedItem = item;
00502         }
00503     }
00504 }
00505 
00506 
00507 void ResourceView::resSelectionChanged() {
00508     //kdDebug()<<k_funcinfo<<endl;
00509     resSelectionChanged(resList->selectedItem());
00510 }
00511 
00512 void ResourceView::resSelectionChanged(QListViewItem *item) {
00513     //kdDebug()<<k_funcinfo<<item<<endl;
00514     ResourceItemPrivate *ritem = dynamic_cast<ResourceItemPrivate *>(item);
00515     if (ritem) {
00516         m_selectedItem = ritem;
00517         if (m_showAppointments) {
00518             m_appview->show();
00519             m_appview->draw(ritem->resource, m_mainview->getProject().startTime().date(), m_mainview->getProject().endTime().date());
00520         } else {
00521             m_appview->hide();
00522         }
00523         return;
00524     }
00525     m_selectedItem = 0;
00526     m_appview->clear();
00527 }
00528 
00529 
00530 void ResourceView::slotItemDoubleClicked(QListViewItem*) {
00531     emit itemDoubleClicked();
00532 }
00533 
00534 void ResourceView::popupMenuRequested(QListViewItem* item, const QPoint & pos, int)
00535 {
00536     ResourceItemPrivate *ritem = dynamic_cast<ResourceItemPrivate *>(item);
00537     if (ritem) {
00538         if (ritem != m_selectedItem)
00539             resSelectionChanged(ritem);
00540         QPopupMenu *menu = m_mainview->popupMenu("resource_popup");
00541         if (menu)
00542         {
00543             menu->exec(pos);
00544             //kdDebug()<<k_funcinfo<<"id="<<id<<endl;
00545         }
00546         else
00547             kdDebug()<<k_funcinfo<<"No menu!"<<endl;
00548     }
00549 }
00550 
00551 QValueList<int> ResourceView::listOffsets(int pageHeight) const {
00552     QValueList<int> lst;
00553     int hh = resList->headerHeight();
00554     int ph = pageHeight-hh;
00555     int lh = resList->contentsHeight() - hh; // list height ex header.
00556     int ly = 0;
00557     kdDebug()<<k_funcinfo<<ly<<", "<<lh<<endl;
00558     while (ly < lh) {
00559         lst << ly;
00560         ly = resList->calculateY(ly+1, ly+ph); // offset into the list, ex header
00561         //kdDebug()<<k_funcinfo<<ly<<", "<<lh<<endl;
00562     }
00563     return lst;
00564 }
00565 
00566 void ResourceView::print(KPrinter &printer) {
00567     //kdDebug()<<k_funcinfo<<endl;
00568     QPaintDeviceMetrics m = QPaintDeviceMetrics ( &printer );
00569     uint top, left, bottom, right;
00570     printer.margins(&top, &left, &bottom, &right);
00571     //kdDebug()<<m.width()<<"x"<<m.height()<<" : "<<top<<", "<<left<<", "<<bottom<<", "<<right<<" : "<<size()<<endl;
00572     QPainter p;
00573     p.begin(&printer);
00574     p.setViewport(left, top, m.width()-left-right, m.height()-top-bottom);
00575     p.setClipRect(left, top, m.width()-left-right, m.height()-top-bottom);
00576     QRect preg = p.clipRegion(QPainter::CoordPainter).boundingRect();
00577     //kdDebug()<<"p="<<preg<<endl;
00578     //p.drawRect(preg.x(), preg.y(), preg.width()-1, preg.height()-1);
00579     int ch = resList->contentsHeight();
00580     int cw = resList->contentsWidth();
00581     double scale = (double)preg.width()/(double)(cw);
00582     //kdDebug()<<"scale="<<scale<<endl;
00583     if (scale < 1.0) {
00584         p.scale(scale, scale);
00585     }
00586     int ph = preg.height()-resList->headerHeight();
00587     QValueList<int> lst = listOffsets(preg.height());
00588     for (int i=0; i < lst.count(); ++i) {
00589         //kdDebug()<<"Page "<<i+1<<": "<<"scale="<<scale<<" "<<lst[i]<<" : "<<cw<<"x"<<ch<<endl;
00590         if (i > 0) {
00591             printer.newPage();
00592         }
00593         resList->paintToPrinter(&p, 0, lst[i], cw, ph);
00594     }
00595     
00596     p.end();
00597 }
00598 
00599 bool ResourceView::setContext(Context::Resourceview &/*context*/) {
00600     //kdDebug()<<k_funcinfo<<endl;
00601     return true;
00602 }
00603 
00604 void ResourceView::getContext(Context::Resourceview &/*context*/) const {
00605     //kdDebug()<<k_funcinfo<<endl;
00606 }
00607 
00608 
00609 }  //KPlato namespace
00610 
00611 #include "kptresourceview.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys