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