|
1 /* |
|
2 * Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). |
|
3 * All rights reserved. |
|
4 * This component and the accompanying materials are made available |
|
5 * under the terms of "Eclipse Public License v1.0" |
|
6 * which accompanies this distribution, and is available |
|
7 * at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
8 * |
|
9 * Initial Contributors: |
|
10 * Nokia Corporation - initial contribution. |
|
11 * |
|
12 * Contributors: |
|
13 * |
|
14 * Description: Day view control of calendar |
|
15 * |
|
16 */ |
|
17 |
|
18 //System includes |
|
19 #include <QTime> |
|
20 #include <QGraphicsLinearLayout> |
|
21 #include <QGesture> |
|
22 |
|
23 #ifdef _DEBUG |
|
24 #include <QPainter> |
|
25 #endif |
|
26 |
|
27 #include <hbabstractitemview.h> |
|
28 #include <hbtextitem.h> |
|
29 #include <hbmodeliterator.h> |
|
30 #include <hbinstance.h> |
|
31 |
|
32 //User includes |
|
33 #include "calendaycontainer.h" |
|
34 #include "calendayutils.h" |
|
35 #include "calendayeventspane.h" |
|
36 #include "calendayitem.h" |
|
37 #include "calendaymodel.h" |
|
38 #include "calendayinfo.h" |
|
39 #include "calendayview.h" |
|
40 |
|
41 // ----------------------------------------------------------------------------- |
|
42 // CalenDayContainer() |
|
43 // Constructor |
|
44 // ----------------------------------------------------------------------------- |
|
45 // |
|
46 CalenDayContainer::CalenDayContainer(QGraphicsItem *parent) : |
|
47 HbAbstractItemContainer(parent), mGeometryUpdated(false), mInfo(0) |
|
48 { |
|
49 getTimedEventLayoutValues(mLayoutValues); |
|
50 |
|
51 // Get the height of element |
|
52 qreal paneHeight = CalenDayUtils::instance()->hourElementHeight(); |
|
53 |
|
54 QGraphicsLinearLayout* timeLinesLayout = new QGraphicsLinearLayout( |
|
55 Qt::Vertical, this); |
|
56 for (int i = 0; i < 24; i++) { |
|
57 CalenDayEventsPane* element = new CalenDayEventsPane(this); |
|
58 element->setPreferredHeight(paneHeight); |
|
59 element->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); |
|
60 // Draw top line at midnight |
|
61 if (i == 0) { |
|
62 element->drawTopLine(true); |
|
63 } |
|
64 timeLinesLayout->addItem(element); |
|
65 } |
|
66 timeLinesLayout->setContentsMargins(0.0, 0.0, 0.0, 0.0); |
|
67 timeLinesLayout->setSpacing(0.0); |
|
68 |
|
69 setLayout(timeLinesLayout); |
|
70 } |
|
71 |
|
72 // ----------------------------------------------------------------------------- |
|
73 // ~CalenDayContainer() |
|
74 // Destructor |
|
75 // ----------------------------------------------------------------------------- |
|
76 // |
|
77 CalenDayContainer::~CalenDayContainer() |
|
78 { |
|
79 } |
|
80 |
|
81 // ----------------------------------------------------------------------------- |
|
82 // itemAdded() |
|
83 // |
|
84 // ----------------------------------------------------------------------------- |
|
85 // |
|
86 void CalenDayContainer::itemAdded( int index, HbAbstractViewItem *item, |
|
87 bool animate ) |
|
88 { |
|
89 Q_UNUSED( index ) |
|
90 Q_UNUSED( item ) |
|
91 Q_UNUSED( animate ) |
|
92 } |
|
93 |
|
94 // ----------------------------------------------------------------------------- |
|
95 // reset() |
|
96 // |
|
97 // ----------------------------------------------------------------------------- |
|
98 // |
|
99 void CalenDayContainer::reset() |
|
100 { |
|
101 // remove absorbers if exist |
|
102 if (mAbsorbers.count()) |
|
103 { |
|
104 qDeleteAll(mAbsorbers); |
|
105 mAbsorbers.clear(); |
|
106 } |
|
107 |
|
108 // shrink event area when all-day events available after reset |
|
109 getTimedEventLayoutValues(mLayoutValues); |
|
110 |
|
111 // position need to be maintained while changing model |
|
112 QPointF position(pos()); |
|
113 HbAbstractItemContainer::reset(); |
|
114 setPos( position ); |
|
115 } |
|
116 |
|
117 // ----------------------------------------------------------------------------- |
|
118 // itemRemoved() |
|
119 // |
|
120 // ----------------------------------------------------------------------------- |
|
121 // |
|
122 void CalenDayContainer::itemRemoved( HbAbstractViewItem *item, bool animate ) |
|
123 { |
|
124 Q_UNUSED( item ) |
|
125 Q_UNUSED( animate ) |
|
126 } |
|
127 |
|
128 // ----------------------------------------------------------------------------- |
|
129 // viewResized() |
|
130 // |
|
131 // ----------------------------------------------------------------------------- |
|
132 // |
|
133 void CalenDayContainer::viewResized (const QSizeF &size) |
|
134 { |
|
135 resize(size); |
|
136 if (!mGeometryUpdated) { |
|
137 mGeometryUpdated = true; |
|
138 updateGeometry(); |
|
139 } |
|
140 } |
|
141 |
|
142 // ----------------------------------------------------------------------------- |
|
143 // createDefaultPrototype() |
|
144 // |
|
145 // ----------------------------------------------------------------------------- |
|
146 // |
|
147 HbAbstractViewItem * CalenDayContainer::createDefaultPrototype() const |
|
148 { |
|
149 CalenDayItem *calendarViewItem = new CalenDayItem; |
|
150 return calendarViewItem; |
|
151 } |
|
152 |
|
153 // ----------------------------------------------------------------------------- |
|
154 // setItemModelIndex() |
|
155 // |
|
156 // ----------------------------------------------------------------------------- |
|
157 // |
|
158 void CalenDayContainer::setItemModelIndex(HbAbstractViewItem *item, |
|
159 const QModelIndex &index) |
|
160 { |
|
161 QVariant variant = index.data( CalenDayEntry ); |
|
162 AgendaEntry entry = variant.value<AgendaEntry>(); |
|
163 |
|
164 if (entry.isTimedEntry()) { |
|
165 updateTimedEventGeometry( item, index ); |
|
166 item->setParentItem(this); |
|
167 } |
|
168 else if( entry.type() == AgendaEntry::TypeEvent ){ |
|
169 updateAllDayEventGeometry( item, index ); |
|
170 item->setParentItem(this); |
|
171 } |
|
172 else { |
|
173 item->setVisible(false); |
|
174 } |
|
175 |
|
176 // last item |
|
177 if (index.row() == index.model()->rowCount() - 1) { |
|
178 createTouchEventAbsorbers(); |
|
179 } |
|
180 |
|
181 HbAbstractItemContainer::setItemModelIndex(item, index); |
|
182 } |
|
183 |
|
184 // ----------------------------------------------------------------------------- |
|
185 // updateTimedEventGeometry() |
|
186 // Updates geometry of a timed event. |
|
187 // ----------------------------------------------------------------------------- |
|
188 // |
|
189 void CalenDayContainer::updateTimedEventGeometry(HbAbstractViewItem *item, |
|
190 const QModelIndex &index) |
|
191 { |
|
192 //safety check |
|
193 if ( !mInfo ) { |
|
194 return; |
|
195 } |
|
196 |
|
197 QVariant variant = index.data( CalenDayEntry ); |
|
198 AgendaEntry entry = variant.value<AgendaEntry>(); |
|
199 |
|
200 //1. get 'virtual' event position from DayInfo |
|
201 //TODO: k.g.: Day Info should store model index instead of keeping redundant data |
|
202 SCalenApptInfo apptInfo; |
|
203 apptInfo.iIndex = index; |
|
204 |
|
205 QDateTime start; |
|
206 QDateTime end; |
|
207 QDateTime currentDate; |
|
208 currentDate = static_cast<const CalenDayModel*>(index.model())->modelDate(); |
|
209 CalenDayUtils::instance()->getEventValidStartEndTime( start, end, entry, |
|
210 currentDate ); |
|
211 apptInfo.iStartTime = start; |
|
212 apptInfo.iEndTime = end; |
|
213 |
|
214 TCalenInstanceId id = TCalenInstanceId::nullInstanceId(); |
|
215 id.mEntryLocalUid = index.row(); //index.row() - temporary ID |
|
216 id.mInstanceTime = apptInfo.iStartTime; |
|
217 apptInfo.iId = id; |
|
218 apptInfo.iAllDay = 0; |
|
219 apptInfo.iColor = 0xffff; |
|
220 |
|
221 int startSlot, endSlot, columnIdx, columns; |
|
222 mInfo->GetLocation( apptInfo, startSlot, endSlot, columnIdx, columns ); |
|
223 |
|
224 |
|
225 //2. set timed event's geometry |
|
226 qreal eventStartX(mLayoutValues.eventAreaX ); |
|
227 qreal eventStartY(0.0); |
|
228 qreal eventWidth(mLayoutValues.eventAreaWidth); |
|
229 qreal eventHeight(0.0); |
|
230 |
|
231 //event's startY/height |
|
232 eventStartY = startSlot * mLayoutValues.slotHeight; |
|
233 eventHeight = (endSlot - startSlot) * mLayoutValues.slotHeight; |
|
234 |
|
235 //event's startX/width |
|
236 eventWidth /= columns; |
|
237 |
|
238 //In case when eventWidth will be smaller then 3.0un we need to |
|
239 //make spacings between events smaller. |
|
240 //Check whether it's possible to shring them so the bubbles |
|
241 //width can stay at 3.0un (time stripe + frame margins). |
|
242 qreal minWidth = 3.0 * mLayoutValues.unitInPixels; |
|
243 if(eventWidth - mLayoutValues.eventMargin < minWidth){ |
|
244 |
|
245 //Calculate new margin value |
|
246 //from totalMarginSpace we need to subtract |
|
247 //mLayoutValues.eventMargin because first margin is always 1.5un |
|
248 qreal totalMarginSpace = mLayoutValues.eventAreaWidth - minWidth * columns - mLayoutValues.eventMargin; |
|
249 qreal newMarginValue = totalMarginSpace / (columns - 1); |
|
250 |
|
251 //check if we managed to pack all the events into space we have |
|
252 if(newMarginValue > 0){ |
|
253 |
|
254 eventWidth = minWidth; |
|
255 } |
|
256 else{ |
|
257 //there's not enough space |
|
258 //new minWidth it's 1.5un (time stripe only) |
|
259 minWidth = 1.5 * mLayoutValues.unitInPixels; |
|
260 totalMarginSpace = mLayoutValues.eventAreaWidth - minWidth * columns - mLayoutValues.eventMargin; |
|
261 newMarginValue = totalMarginSpace / (columns - 1); |
|
262 eventWidth = minWidth; |
|
263 } |
|
264 |
|
265 //First column margin should be always 1.5un (mLayoutValues.eventMargin) |
|
266 eventStartX += columnIdx * (eventWidth + newMarginValue) + mLayoutValues.eventMargin; |
|
267 } |
|
268 else{ |
|
269 //add margins between the event |
|
270 eventStartX += columnIdx * eventWidth + mLayoutValues.eventMargin; |
|
271 eventWidth -= mLayoutValues.eventMargin; |
|
272 } |
|
273 |
|
274 QRectF eventGeometry( eventStartX, eventStartY, eventWidth, eventHeight ); |
|
275 item->setGeometry(eventGeometry);} |
|
276 |
|
277 |
|
278 // ----------------------------------------------------------------------------- |
|
279 // updateAllDayEventGeometry() |
|
280 // Updates geometry of a timed event. |
|
281 // ----------------------------------------------------------------------------- |
|
282 // |
|
283 void CalenDayContainer::updateAllDayEventGeometry(HbAbstractViewItem *item, |
|
284 const QModelIndex &index) |
|
285 { |
|
286 //safety check |
|
287 if ( !mInfo ) { |
|
288 return; |
|
289 } |
|
290 |
|
291 QVariant variant = index.data( CalenDayEntry ); |
|
292 AgendaEntry entry = variant.value<AgendaEntry>(); |
|
293 |
|
294 //1. get 'virtual' event position from DayInfo |
|
295 //TODO: k.g.: Day Info should store model index instead of keeping redundant data |
|
296 SCalenApptInfo apptInfo; |
|
297 apptInfo.iIndex = index; |
|
298 |
|
299 |
|
300 QDateTime start; |
|
301 QDateTime end; |
|
302 QDateTime currentDate; |
|
303 currentDate = static_cast<const CalenDayModel*>(index.model())->modelDate(); |
|
304 CalenDayUtils::instance()->getEventValidStartEndTime( start, end, entry, |
|
305 currentDate ); |
|
306 apptInfo.iStartTime = start; |
|
307 apptInfo.iEndTime = end; |
|
308 |
|
309 TCalenInstanceId id = TCalenInstanceId::nullInstanceId(); |
|
310 id.mEntryLocalUid = index.row(); //index.row() - temporary ID |
|
311 id.mInstanceTime = apptInfo.iStartTime; |
|
312 apptInfo.iId = id; |
|
313 apptInfo.iAllDay = true; |
|
314 apptInfo.iColor = 0xffff; |
|
315 |
|
316 int startSlot, endSlot, columnIdx, columns; |
|
317 mInfo->GetLocation( apptInfo, startSlot, endSlot, columnIdx, columns ); |
|
318 |
|
319 //2. set timed event's geometry |
|
320 qreal eventStartX(0.0); |
|
321 qreal eventStartY(0.0); |
|
322 qreal eventWidth(mLayoutValues.eventAreaX); |
|
323 qreal eventHeight = (endSlot - startSlot) * mLayoutValues.slotHeight; |
|
324 |
|
325 |
|
326 //event's startX/width |
|
327 if ( columns > 1 ) { |
|
328 eventWidth /= columns; |
|
329 eventStartX += columnIdx * eventWidth + mLayoutValues.eventMargin; |
|
330 //add margins between the event |
|
331 eventWidth -= mLayoutValues.eventMargin; |
|
332 } else { |
|
333 eventStartX += mLayoutValues.eventMargin; |
|
334 eventWidth -= mLayoutValues.eventMargin; |
|
335 } |
|
336 |
|
337 QRectF eventGeometry( eventStartX, eventStartY, eventWidth, eventHeight ); |
|
338 item->setGeometry(eventGeometry); |
|
339 |
|
340 } |
|
341 |
|
342 |
|
343 // ----------------------------------------------------------------------------- |
|
344 // movingBackwards() |
|
345 // |
|
346 // ----------------------------------------------------------------------------- |
|
347 // |
|
348 void CalenDayContainer::getTimedEventLayoutValues(LayoutValues& layoutValues) |
|
349 { |
|
350 // get the width of content area |
|
351 qreal contentWidth = CalenDayUtils::instance()->contentWidth(); |
|
352 //1.time column width -> eventAreaX[out] |
|
353 HbStyle style; |
|
354 HbDeviceProfile deviceProfile; |
|
355 layoutValues.unitInPixels = deviceProfile.unitValue(); |
|
356 |
|
357 if ( mInfo && mInfo->AlldayCount()) |
|
358 { // 9.5 -> all-day area width |
|
359 layoutValues.eventAreaX = 9.5 * layoutValues.unitInPixels; |
|
360 } |
|
361 else |
|
362 { |
|
363 layoutValues.eventAreaX = 0; |
|
364 } |
|
365 |
|
366 //2. event area width -> eventAreaWidth[out] |
|
367 qreal emptyRightColumnWidth(0.0); |
|
368 emptyRightColumnWidth = 6.0 * layoutValues.unitInPixels; //pix (according to UI spec) |
|
369 layoutValues.eventAreaWidth = contentWidth - emptyRightColumnWidth - layoutValues.eventAreaX ; |
|
370 //3. margins between the overlapping events -> eventMargin[out] |
|
371 layoutValues.eventMargin = 1.5 * layoutValues.unitInPixels; |
|
372 //4. half-hour slot'h height -> slotHeight[out] |
|
373 //curent slot height corresponds to half an hour |
|
374 layoutValues.slotHeight = |
|
375 CalenDayUtils::instance()->hourElementHeight() / 2; |
|
376 |
|
377 // 8.2 un (min. touchable event) from layout guide |
|
378 layoutValues.maxColumns = layoutValues.eventAreaWidth / (8.2 * layoutValues.unitInPixels); |
|
379 } |
|
380 |
|
381 // ----------------------------------------------------------------------------- |
|
382 // setDayInfo() |
|
383 // Sets day's info structer to the container. |
|
384 // ----------------------------------------------------------------------------- |
|
385 // |
|
386 void CalenDayContainer::setDayInfo( CalenDayInfo* dayInfo ) |
|
387 { |
|
388 mInfo = dayInfo; |
|
389 } |
|
390 |
|
391 // ----------------------------------------------------------------------------- |
|
392 // orientationChanged() |
|
393 // Slot handles layout switch. |
|
394 // ----------------------------------------------------------------------------- |
|
395 // |
|
396 void CalenDayContainer::orientationChanged(Qt::Orientation orientation) |
|
397 { |
|
398 getTimedEventLayoutValues(mLayoutValues); |
|
399 |
|
400 Q_UNUSED( orientation ) |
|
401 QList<HbAbstractViewItem *> items = this->items(); |
|
402 int count(items.count()); |
|
403 for (int i = 0; i < count; i++) { |
|
404 QModelIndex modelIndex = items[i]->modelIndex(); |
|
405 if (modelIndex.isValid()) { |
|
406 QVariant variant = modelIndex.data(CalenDayEntry); |
|
407 AgendaEntry entry = variant.value<AgendaEntry> (); |
|
408 if (entry.isTimedEntry()) { |
|
409 updateTimedEventGeometry(items[i], modelIndex); |
|
410 } |
|
411 } |
|
412 } |
|
413 |
|
414 createTouchEventAbsorbers(); |
|
415 } |
|
416 |
|
417 // ----------------------------------------------------------------------------- |
|
418 // createTouchEventAbsorbers() |
|
419 // Creates absorbers which prevent touching to small items |
|
420 // ----------------------------------------------------------------------------- |
|
421 // |
|
422 void CalenDayContainer::createTouchEventAbsorbers() |
|
423 { |
|
424 // remove absorbers if exist |
|
425 if (mAbsorbers.count()) |
|
426 { |
|
427 qDeleteAll(mAbsorbers); |
|
428 mAbsorbers.clear(); |
|
429 } |
|
430 |
|
431 const QList<CalenTimeRegion>& regionList = mInfo->RegionList(); |
|
432 |
|
433 for(int i=0; i < regionList.count(); i++) |
|
434 { |
|
435 if(regionList[i].iColumns.count() > mLayoutValues.maxColumns ) |
|
436 { |
|
437 TouchEventAbsorber* absorber = |
|
438 crateAbsorberBetweenSlots(regionList[i].iStartSlot, regionList[i].iEndSlot); |
|
439 |
|
440 mAbsorbers.append(absorber); |
|
441 } |
|
442 } |
|
443 |
|
444 } |
|
445 |
|
446 // ----------------------------------------------------------------------------- |
|
447 // crateAbsorberBetweenSlots() |
|
448 // Creates single absorber in given location |
|
449 // ----------------------------------------------------------------------------- |
|
450 // |
|
451 TouchEventAbsorber *CalenDayContainer::crateAbsorberBetweenSlots |
|
452 (int startSlot, int endSlot) |
|
453 { |
|
454 TouchEventAbsorber *absorber = new TouchEventAbsorber(this); |
|
455 absorber->setZValue(1000); |
|
456 absorber->setVisible(true); |
|
457 |
|
458 absorber->setGeometry( mLayoutValues.eventAreaX, // x |
|
459 startSlot * mLayoutValues.slotHeight, // y |
|
460 mLayoutValues.eventAreaWidth, // w |
|
461 (endSlot-startSlot) * mLayoutValues.slotHeight ); // h |
|
462 |
|
463 return absorber; |
|
464 } |
|
465 |
|
466 |
|
467 // ----------------------------------------------------------------------------- |
|
468 // TouchEventAbsorber::gestureEvent() |
|
469 // Handles tap event on overlapping area (currently it leads to Agenda View - |
|
470 // as described in UI spec) |
|
471 // ----------------------------------------------------------------------------- |
|
472 // |
|
473 void TouchEventAbsorber::gestureEvent(QGestureEvent *event) |
|
474 { |
|
475 QTapGesture *tapGesture = qobject_cast<QTapGesture*> (event->gesture( |
|
476 Qt::TapGesture)); |
|
477 |
|
478 if (tapGesture && tapGesture->state() == Qt::GestureFinished) |
|
479 { |
|
480 CalenDayView* dayView = static_cast<CalenDayView*> |
|
481 (CalenDayUtils::instance()->mainWindow()->currentView()); |
|
482 |
|
483 dayView->changeView(ECalenAgendaView); |
|
484 } |
|
485 } |
|
486 |
|
487 // ----------------------------------------------------------------------------- |
|
488 // TouchEventAbsorber() |
|
489 // default ctor |
|
490 // ----------------------------------------------------------------------------- |
|
491 // |
|
492 TouchEventAbsorber::TouchEventAbsorber(QGraphicsItem *parent) : HbWidget(parent) |
|
493 { |
|
494 #ifdef _DEBUG |
|
495 setFlag(QGraphicsItem::ItemHasNoContents, false); |
|
496 #endif |
|
497 grabGesture(Qt::TapGesture); |
|
498 } |
|
499 |
|
500 // ----------------------------------------------------------------------------- |
|
501 // TouchEventAbsorber() |
|
502 // default dtor |
|
503 // ----------------------------------------------------------------------------- |
|
504 // |
|
505 TouchEventAbsorber::~TouchEventAbsorber() |
|
506 { |
|
507 |
|
508 } |
|
509 |
|
510 // ----------------------------------------------------------------------------- |
|
511 // TouchEventAbsorber::paint() |
|
512 // used for debugging purposes to see absorbers areas |
|
513 // ----------------------------------------------------------------------------- |
|
514 // |
|
515 #ifdef _DEBUG |
|
516 void TouchEventAbsorber::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, |
|
517 QWidget *widget) |
|
518 { |
|
519 Q_UNUSED(option) |
|
520 Q_UNUSED(widget) |
|
521 |
|
522 painter->save(); |
|
523 QPen pen; |
|
524 pen.setWidth(2); |
|
525 pen.setColor(Qt::red); |
|
526 painter->setPen(pen); |
|
527 painter->drawRect(boundingRect()); |
|
528 painter->restore(); |
|
529 } |
|
530 #endif |
|
531 // End of File |