some 'delegate' drawing optimizations
[toast/confclerk.git] / src / mvc / delegate.cpp
1 #include "delegate.h"
2 #include "eventmodel.h"
3 #include <track.h>
4
5 #include <QDebug>
6 #include <QPainter>
7
8 const int RADIUS = 10;
9 const int SPACER = 10;
10
11 const double scaleFactor1 = 0.4;
12 const double scaleFactor2 = 0.8;
13
14 Delegate::Delegate(QTreeView *aParent)
15     : QItemDelegate(aParent)
16     , mViewPtr(aParent)
17 {
18     mControls.clear();
19     defineControls();
20 }
21
22 Delegate::~Delegate()
23 {
24     QListIterator<ControlId> i(mControls.keys());
25     while (i.hasNext())
26     {
27         delete mControls[i.next()]->image();
28     }
29 }
30
31 void Delegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
32 {
33     if(!mViewPtr)
34         return;
35
36     painter->save();
37     QColor bkgrColor = Qt::cyan;
38
39     QPen borderPen(bkgrColor.darker());
40     //QColor bkgrColor = QColor(0,0,113);
41     //QPen borderPen(Qt::cyan);
42     if(hasParent(index))
43     {
44         int aux = option.rect.height() - mControls[FavouriteControlOn]->drawPoint().y() - mControls[FavouriteControlOn]->image()->height();
45         // font SMALL
46         QFont fontSmall = option.font;
47         fontSmall.setBold(false);
48         fontSmall.setPixelSize(aux*0.2);
49         QFontMetrics fmSmall(fontSmall);
50         // font SMALL bold
51         QFont fontSmallB = fontSmall;
52         fontSmallB.setBold(true);
53         QFontMetrics fmSmallB(fontSmallB);
54
55         // font BIG
56         QFont fontBig = option.font;
57         fontBig.setBold(false);
58         fontBig.setPixelSize(aux*0.33);
59         QFontMetrics fmBig(fontBig);
60         // font BIG bold
61         QFont fontBigB = fontBig;
62         fontBigB.setBold(true);
63         QFontMetrics fmBigB(fontBigB);
64
65         //int spacer = (fmSmall.boundingRect("999").width() < SPACER) ? SPACER : fmSmall.boundingRect("999").width();
66
67         //Time conflicts are colored differently
68         if(static_cast<Event*>(index.internalPointer())->hasTimeConflict())
69             bkgrColor = Qt::yellow;
70
71         if(isLast(index))
72         {
73             QLinearGradient lastGradient(option.rect.topLeft(), option.rect.bottomLeft());
74             lastGradient.setColorAt(0.0, Qt::white);
75             lastGradient.setColorAt(0.5, bkgrColor);
76             lastGradient.setColorAt(1.0, Qt::white);
77
78             QPainterPath endPath;
79             endPath.moveTo(option.rect.topLeft());
80             endPath.lineTo(option.rect.bottomLeft()-QPoint(0, RADIUS));
81             endPath.arcTo(option.rect.left(), option.rect.bottom()-2*RADIUS, 2*RADIUS, 2*RADIUS, 180, 90);
82             endPath.lineTo(option.rect.bottomRight()-QPoint(RADIUS, 0));
83             endPath.arcTo(option.rect.right()-2*RADIUS, option.rect.bottom()-2*RADIUS, 2*RADIUS, 2*RADIUS, 270, 90);
84             endPath.lineTo(option.rect.topRight());
85
86             painter->setBrush( bkgrColor );
87             //painter->setBrush(lastGradient);
88             painter->setPen(borderPen);
89             painter->drawPath(endPath);
90
91             painter->setFont(option.font);
92         }
93         else // middle elements
94         {
95             QLinearGradient middleGradient(option.rect.topLeft(), option.rect.bottomLeft());
96             middleGradient.setColorAt(0.0, Qt::white);
97             middleGradient.setColorAt(0.25, bkgrColor);
98             middleGradient.setColorAt(0.5, Qt::white);
99             middleGradient.setColorAt(0.75, bkgrColor);
100             middleGradient.setColorAt(1.0, Qt::white);
101
102             painter->setBrush( bkgrColor );
103             //painter->setBrush(middleGradient);
104             painter->setPen(Qt::NoPen);
105             painter->drawRect(option.rect);
106
107             painter->setPen(borderPen);
108             // vertical lines
109             painter->drawLine(option.rect.topLeft(), option.rect.bottomLeft());
110             painter->drawLine(option.rect.topRight(), option.rect.bottomRight());
111             // horizontal lines
112             painter->drawLine(option.rect.bottomLeft(), option.rect.bottomRight());
113
114             painter->setFont(option.font);
115         }
116
117         // draw Controls
118         // favourite
119         if(static_cast<Event*>(index.internalPointer())->isFavourite())
120             painter->drawImage(mControls[FavouriteControlOn]->drawPoint(option.rect),*mControls[FavouriteControlOn]->image());
121         else
122             painter->drawImage(mControls[FavouriteControlOff]->drawPoint(option.rect),*mControls[FavouriteControlOff]->image());
123 #ifdef MAEMO
124         // alarm
125         if(static_cast<Event*>(index.internalPointer())->hasAlarm())
126             painter->drawImage(mControls[AlarmControlOn]->drawPoint(option.rect),*mControls[AlarmControlOn]->image());
127         else
128             painter->drawImage(mControls[AlarmControlOff]->drawPoint(option.rect),*mControls[AlarmControlOff]->image());
129 #endif
130         // map
131         painter->drawImage(mControls[MapControl]->drawPoint(option.rect),*mControls[MapControl]->image());
132         // Time conflict
133         if(static_cast<Event*>(index.internalPointer())->hasTimeConflict())
134             painter->drawImage(mControls[WarningControl]->drawPoint(option.rect),*mControls[WarningControl]->image());
135
136         // draw texts
137         Event *event = static_cast<Event*>(index.internalPointer());
138         QPointF titlePointF(mControls[FavouriteControlOn]->drawPoint(option.rect));
139         titlePointF.setX(option.rect.x()+SPACER);
140         titlePointF.setY(titlePointF.y()+mControls[FavouriteControlOn]->image()->height());
141         QTime start = event->start().time();
142         painter->setFont(fontBig);
143         painter->drawText(titlePointF,start.toString("hh:mm") + "-" + start.addSecs(event->duration()).toString("hh:mm") + ", " + event->room());
144         // title
145         titlePointF.setY(titlePointF.y()+fmBig.height()-fmBig.descent());
146         painter->setFont(fontBigB);
147         QString title = event->title();
148         if(fmBigB.boundingRect(title).width() > (option.rect.width()-2*SPACER)) // the title won't fit the screen
149         {
150             // chop words from the end
151             while( (fmBigB.boundingRect(title + "...").width() > (option.rect.width()-2*SPACER)) && !title.isEmpty())
152             {
153                 title.chop(1);
154                 // chop characters one-by-one from the end
155                 while( (!title.at(title.length()-1).isSpace()) && !title.isEmpty())
156                 {
157                     title.chop(1);
158                 }
159             }
160             title += "...";
161         }
162         painter->drawText(titlePointF,title);
163         // persons
164         titlePointF.setY(titlePointF.y()+fmSmall.height()-fmSmall.descent());
165         painter->setFont(fontSmall);
166         painter->drawText(titlePointF,"Presenter(s): " + event->persons().join(" and "));
167         // track
168         titlePointF.setY(titlePointF.y()+fmSmall.height()-fmSmall.descent());
169         painter->drawText(titlePointF,"Track: " + Track::retrieveTrackName(event->trackId()));
170     }
171     else // doesn't have parent - time-groups' elements (top items)
172     {
173         QFont fontSmall = option.font;
174         fontSmall.setBold(true);
175         fontSmall.setPixelSize(option.rect.height()*scaleFactor1);
176         QFontMetrics fmSmall(fontSmall);
177
178         QFont fontBig = option.font;
179         fontBig.setBold(true);
180         fontBig.setPixelSize(option.rect.height()*scaleFactor2);
181         QFontMetrics fmBig(fontBig);
182
183         int spacer = (fmSmall.boundingRect("999").width() < SPACER) ? SPACER : fmSmall.boundingRect("999").width();
184
185         QLinearGradient titleGradient(option.rect.topLeft(), option.rect.topRight());
186         //titleGradient.setColorAt(0.0, Qt::white);
187         titleGradient.setColorAt(0.0, bkgrColor);
188         titleGradient.setColorAt(0.5, Qt::white);
189         titleGradient.setColorAt(1.0, bkgrColor);
190
191         QPainterPath titlePath;
192         if(isExpanded(index))
193         {
194             titlePath.moveTo(option.rect.bottomLeft());
195             titlePath.lineTo(option.rect.topLeft()+QPoint(0, RADIUS));
196             titlePath.arcTo(option.rect.left(), option.rect.top(), 2*RADIUS, 2*RADIUS, 180, -90);
197             titlePath.lineTo(option.rect.topRight()-QPoint(RADIUS, 0));
198             titlePath.arcTo(option.rect.right()-2*RADIUS, option.rect.top(), 2*RADIUS, 2*RADIUS, 90, -90);
199             titlePath.lineTo(option.rect.bottomRight());
200             titlePath.closeSubpath();
201         }
202         else
203         {
204             titlePath.lineTo(option.rect.topLeft()+QPoint(0, RADIUS));
205             titlePath.arcTo(option.rect.left(), option.rect.top(), 2*RADIUS, 2*RADIUS, 180, -90);
206             titlePath.lineTo(option.rect.topRight()-QPoint(RADIUS, 0));
207             titlePath.arcTo(option.rect.right()-2*RADIUS, option.rect.top(), 2*RADIUS, 2*RADIUS, 90, -90);
208             titlePath.lineTo(option.rect.bottomRight()-QPoint(0, RADIUS));
209             titlePath.arcTo(option.rect.right()-2*RADIUS, option.rect.bottom()-2*RADIUS, 2*RADIUS, 2*RADIUS, 0, -90);
210             titlePath.lineTo(option.rect.bottomLeft()+QPoint(RADIUS, 0));
211             titlePath.arcTo(option.rect.left(), option.rect.bottom()-2*RADIUS, 2*RADIUS, 2*RADIUS, 270, -90);      
212             titlePath.closeSubpath();
213         }
214
215         painter->setBrush(titleGradient);
216         painter->setPen(borderPen);
217         painter->drawPath(titlePath);
218
219         // draw icons 
220         painter->setFont(fontSmall);
221         QPoint drawPoint =
222             option.rect.topRight()
223             - QPoint(
224                     spacer + mControls[FavouriteControlOn]->image()->width(),
225                     - option.rect.height()/2 + mControls[FavouriteControlOn]->image()->height()/2);
226         painter->drawImage(drawPoint,*mControls[FavouriteControlOn]->image());
227         painter->drawText(drawPoint+QPoint(mControls[FavouriteControlOn]->image()->width()+2, option.rect.height()/2),
228                 QString::number(numberOfFavourities(index)));
229 #ifdef MAEMO
230         drawPoint.setX(drawPoint.x() - spacer - mControls[FavouriteControlOn]->image()->width());
231         painter->drawImage(drawPoint,*mControls[AlarmControlOn]->image());
232         painter->drawText(drawPoint+QPoint(mControls[FavouriteControlOn]->image()->width()+2, option.rect.height()/2),
233                 QString::number(numberOfAlarms(index)));
234 #endif
235         // draw texts
236         QString numEvents = QString::number(index.model()->rowCount(index)).append("/");
237         drawPoint.setX(drawPoint.x() - spacer - fmSmall.boundingRect(numEvents).width());
238         drawPoint.setY(drawPoint.y() + option.rect.height()/2);
239         painter->drawText(drawPoint,numEvents);
240
241         QPointF titlePointF = QPoint(
242                 option.rect.x()+SPACER,
243                 option.rect.y()+option.rect.height()-fmBig.descent());
244         painter->setFont(fontBig);
245
246         painter->drawText(titlePointF,qVariantValue<QString>(index.data()));
247     }
248
249     //// HIGHLIGHTING SELECTED ITEM
250     //if (option.state & QStyle::State_Selected)
251         //painter->fillRect(option.rect, option.palette.highlight());
252
253     painter->restore();
254 }
255
256 QSize Delegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
257 {
258     Q_UNUSED(option)
259
260     if (index.internalId() == 0) // time group
261     {
262         return QSize(40,40);
263     }
264     else // event
265     {
266         return QSize(100,100);
267     }
268 }
269
270 bool Delegate::hasParent( const QModelIndex &index ) const
271 {
272     if( index.parent().isValid() )
273         return true;
274     else
275         return false;
276 }
277   
278 bool Delegate::isLast( const QModelIndex &index ) const
279 {
280     if(!hasParent(index))
281         return false; // what should be returned here?
282
283     if(index.row() >= (index.model()->rowCount(index.parent())-1))
284         return true;
285     else
286         return false;
287 }
288
289 bool Delegate::isExpanded( const QModelIndex &index ) const
290 {
291     if( !mViewPtr )
292         return false;
293     else
294         return mViewPtr->isExpanded( index );
295 }
296
297 Delegate::ControlId Delegate::whichControlClicked(const QModelIndex &aIndex, const QPoint &aPoint) const
298 {
299     if(!hasParent(aIndex)) // time-group item (root item)
300         return ControlNone;
301
302     QListIterator<ControlId> i(mControls.keys());
303     while (i.hasNext())
304     {
305         ControlId id = i.next();
306         if(mControls[id]->drawRect(static_cast<QTreeView*>(parent())->visualRect(aIndex)).contains(aPoint))
307         {
308             if(id == WarningControl)
309             {
310                 if(static_cast<Event*>(aIndex.internalPointer())->hasTimeConflict())
311                     return id;
312             }
313             else
314                 return id;
315         }
316     }
317
318     return ControlNone;
319 }
320
321 void Delegate::defineControls()
322 {
323     Control *control;
324     QPoint p(0,0);
325     // FAVOURITE ICONs
326     // on
327     control = new Control(FavouriteControlOn,QString(":icons/favourite-onBig.png"));
328     p = QPoint(0,SPACER);
329     p.setX(p.x()-control->image()->width()-SPACER);
330     control->setDrawPoint(p);
331     mControls.insert(FavouriteControlOn,control);
332     // off
333     control = new Control(FavouriteControlOff,QString(":icons/favourite-offBig.png"));
334     p = QPoint(0,SPACER);
335     p.setX(p.x()-control->image()->width()-SPACER);
336     control->setDrawPoint(p);
337     mControls.insert(FavouriteControlOff,control);
338
339 #ifdef MAEMO
340     // ALARM ICONs
341     // on
342     control = new Control(AlarmControlOn,QString(":icons/alarm-onBig.png"));
343     p = mControls[FavouriteControlOn]->drawPoint();
344     p.setX(p.x()-control->image()->width()-SPACER);
345     control->setDrawPoint(p);
346     mControls.insert(AlarmControlOn,control);
347     // off
348     control = new Control(AlarmControlOff,QString(":icons/alarm-offBig.png"));
349     p = mControls[FavouriteControlOff]->drawPoint();
350     p.setX(p.x()-control->image()->width()-SPACER);
351     control->setDrawPoint(p);
352     mControls.insert(AlarmControlOff,control);
353
354     // MAP ICON
355     control = new Control(MapControl,QString(":icons/compassBig.png"));
356     p = mControls[AlarmControlOn]->drawPoint();
357     p.setX(p.x()-control->image()->width()-SPACER);
358     control->setDrawPoint(p);
359     mControls.insert(MapControl,control);
360 #else
361     // MAP ICON
362     control = new Control(MapControl,QString(":icons/compassBig.png"));
363     p = mControls[FavouriteControlOn]->drawPoint();
364     p.setX(p.x()-control->image()->width()-SPACER);
365     control->setDrawPoint(p);
366     mControls.insert(MapControl,control);
367 #endif
368
369     // WARNING ICON
370     control = new Control(WarningControl,QString(":icons/exclamation.png"));
371     p = mControls[MapControl]->drawPoint();
372     p.setX(p.x()-control->image()->width()-SPACER);
373     control->setDrawPoint(p);
374     mControls.insert(WarningControl,control);
375 }
376
377 bool Delegate::isPointFromRect(const QPoint &aPoint, const QRect &aRect) const
378 {
379     if( (aPoint.x()>=aRect.left() && aPoint.x()<=aRect.right()) && (aPoint.y()>=aRect.top() && aPoint.y()<=aRect.bottom()) )
380         return true;
381
382     return false;
383 }
384
385 int Delegate::numberOfFavourities(const QModelIndex &index) const
386 {
387     if(index.parent().isValid()) // it's event, not time-group
388         return 0;
389
390     int nrofFavs = 0;
391     for(int i=0; i<index.model()->rowCount(index); i++)
392         if(static_cast<Event*>(index.child(i,0).internalPointer())->isFavourite())
393             nrofFavs++;
394
395     return nrofFavs;
396 }
397
398 int Delegate::numberOfAlarms(const QModelIndex &index) const
399 {
400     if(index.parent().isValid()) // it's event, not time-group
401         return 0;
402
403     int nrofAlarms = 0;
404     for(int i=0; i<index.model()->rowCount(index); i++)
405         if(static_cast<Event*>(index.child(i,0).internalPointer())->hasAlarm())
406             nrofAlarms++;
407
408     return nrofAlarms;
409 }
410