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