calendarui/views/dayview/src/calendaycontainer.cpp
branchRCL_3
changeset 65 12af337248b1
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/calendarui/views/dayview/src/calendaycontainer.cpp	Tue Aug 31 15:13:43 2010 +0300
@@ -0,0 +1,610 @@
+/*
+ * Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ * All rights reserved.
+ * This component and the accompanying materials are made available
+ * under the terms of "Eclipse Public License v1.0"
+ * which accompanies this distribution, and is available
+ * at the URL "http://www.eclipse.org/legal/epl-v10.html".
+ *
+ * Initial Contributors:
+ * Nokia Corporation - initial contribution.
+ *
+ * Contributors:
+ *
+ * Description:  Day view container - parent widget for events (CalenDayItem)
+ * and hours area widgets (CalenDayEventsPane)
+ * Responsible for positioning and resizing events widgets.
+ */
+
+//System includes
+#include <QTime>
+#include <QGraphicsLinearLayout>
+#include <QGesture>
+
+#ifdef _DEBUG
+	#include <QPainter>
+#endif
+
+#include <HbAbstractItemView>
+#include <HbTextItem>
+#include <HbModelIterator>
+#include <HbInstance>
+
+//User includes
+#include "calendaycontainer.h"
+#include "calendayutils.h"
+#include "calendayeventspane.h"
+#include "calendayitem.h"
+#include "calendaymodel.h"
+#include "calendayinfo.h"
+#include "calendayview.h"
+#include "calenagendautils.h"
+#include "calendaycommonheaders.h"
+
+/*!
+ \class CalenDayContainer
+ 
+ \brief CalenDayContainer Item container class associated with model.
+ */
+
+/*!
+ \brief Constructor
+ 
+ Sets container initial geometry, creates hours area widgets.
+ \param parent Parent object
+ */
+CalenDayContainer::CalenDayContainer(QGraphicsItem *parent) :
+    HbAbstractItemContainer(parent), mGeometryUpdated(false), mInfo(0)
+{
+    getTimedEventLayoutValues(mLayoutValues);
+
+    QGraphicsLinearLayout* timeLinesLayout = new QGraphicsLinearLayout(
+        Qt::Vertical, this);
+    for (int i = 0; i < KCalenHoursInDay; i++) {
+        CalenDayEventsPane* element = new CalenDayEventsPane(this);
+        // Draw top line at midnight
+        if (i == 0) {
+            element->setDrawTopLine(true);
+        }
+        timeLinesLayout->addItem(element);
+    }
+    timeLinesLayout->setContentsMargins(0.0, 0.0, 0.0, 0.0);
+    timeLinesLayout->setSpacing(0.0);
+
+    setLayout(timeLinesLayout);
+}
+
+/*!
+ \brief Destructor
+ */
+CalenDayContainer::~CalenDayContainer()
+{
+    // Remove absorbers if exist
+    if (mAbsorbers.count()) {
+        qDeleteAll(mAbsorbers);
+        mAbsorbers.clear();
+    }
+}
+
+/*
+ \reimp
+ */
+void CalenDayContainer::itemAdded(
+    int index,
+    HbAbstractViewItem *item,
+    bool animate)
+{
+    Q_UNUSED( index )
+    Q_UNUSED( item )
+    Q_UNUSED( animate )
+}
+
+
+/*
+ \brief Resets the state of container.
+ 
+ Removes absorbers, gets layout values and maintains current position.
+ */
+void CalenDayContainer::reset()
+{
+    // Remove absorbers if exist
+    if (mAbsorbers.count()) {
+        qDeleteAll(mAbsorbers);
+        mAbsorbers.clear();
+    }
+
+    // Shrink event area when all-day events available after reset
+    getTimedEventLayoutValues(mLayoutValues);
+
+    // Position need to be maintained while changing model
+    QPointF position(pos());
+    HbAbstractItemContainer::reset();
+    setPos(position);
+}
+
+/*
+ \reimp
+ */
+void CalenDayContainer::itemRemoved(HbAbstractViewItem *item, bool animate)
+{
+    Q_UNUSED( item )
+    Q_UNUSED( animate )
+}
+
+/*
+ \reimp
+ */
+void CalenDayContainer::viewResized(const QSizeF &size)
+{
+    resize(size);
+    if (!mGeometryUpdated) {
+        mGeometryUpdated = true;
+        updateGeometry();
+    }
+}
+
+/*
+ \reimp
+ */
+HbAbstractViewItem * CalenDayContainer::createDefaultPrototype() const
+{
+    CalenDayItem *calendarViewItem = new CalenDayItem(this);
+    return calendarViewItem;
+}
+
+/*
+ \reimp
+ */
+void CalenDayContainer::setItemModelIndex(
+    HbAbstractViewItem *item,
+    const QModelIndex &index)
+{
+    QVariant variant = index.data(CalenDayEntry);
+    AgendaEntry entry = variant.value<AgendaEntry> ();
+
+    // Check for All Day Events and Timed entries
+    if (CalenAgendaUtils::isAlldayEvent(entry)) {
+        updateAllDayEventGeometry(item, index);
+        item->setParentItem(this);
+    }
+    else
+        if (entry.isTimedEntry()) {
+            updateTimedEventGeometry(item, index);
+            item->setParentItem(this);
+        }
+        else {
+            item->setVisible(false);
+        }
+
+    // Create touch event absorbers after last item
+    if (index.row() == index.model()->rowCount() - 1) {
+        createTouchEventAbsorbers();
+    }
+
+    HbAbstractItemContainer::setItemModelIndex(item, index);
+}
+
+
+/*!
+ \brief Set size and position of singe timed event widget (bubble)
+ \a item bubble widget
+ \a index pointing item data in model
+ */
+void CalenDayContainer::updateTimedEventGeometry(
+    HbAbstractViewItem *item,
+    const QModelIndex &index)
+{
+    // Safety check
+    if (!mInfo) {
+        return;
+    }
+
+    QVariant variant = index.data(CalenDayEntry);
+    AgendaEntry entry = variant.value<AgendaEntry> ();
+
+    // 1. Get 'virtual' event position from DayInfo
+    SCalenApptInfo apptInfo;
+    apptInfo.iIndex = index;
+
+    QDateTime start;
+    QDateTime end;
+    QDateTime currentDate;
+    currentDate
+        = static_cast<const CalenDayModel*> (index.model())->modelDate();
+    CalenDayUtils::instance()->getEventValidStartEndTime(start, end, entry,
+        currentDate);
+    apptInfo.iStartTime = start;
+    apptInfo.iEndTime = end;
+
+    TCalenInstanceId id = TCalenInstanceId::nullInstanceId();
+    id.mEntryLocalUid = index.row(); //index.row() - temporary ID
+    id.mInstanceTime = apptInfo.iStartTime;
+    apptInfo.iId = id;
+    apptInfo.iAllDay = 0;
+    apptInfo.iColor = 0xffff;
+
+    int startSlot, endSlot, columnIdx, columns;
+    mInfo->GetLocation(apptInfo, startSlot, endSlot, columnIdx, columns);
+
+    // 2. Set timed event's geometry
+    qreal eventStartX(mLayoutValues.eventAreaX);
+    qreal eventStartY(0.0);
+    qreal eventWidth(mLayoutValues.eventAreaWidth);
+    qreal eventHeight(0.0);
+
+    // Event's startY/height
+    eventStartY = startSlot * mLayoutValues.slotHeight;
+    eventHeight = (endSlot - startSlot) * mLayoutValues.slotHeight;
+
+    // Event's startX/width
+    eventWidth /= columns;
+    
+    // In case when eventWidth will be smaller then KCalenMinEventWidth [un]
+    // spacings between events should be smaller.
+    // Check whether it's possible to shrink them so the bubbles width 
+    // can stay at KCalenMinEventWidth [un] (time stripe + frame margins).
+    qreal minWidth = KCalenMinEventWidth * mLayoutValues.unitInPixels;
+    if (eventWidth - mLayoutValues.eventMargin < minWidth) {
+
+        // Calculate new margin value
+        // from totalMarginSpace we need to subtract mLayoutValues.eventMargin 
+        // because first margin is always KCalenSpaceBeetwenEvents
+        qreal totalMarginSpace = mLayoutValues.eventAreaWidth - minWidth
+            * columns - mLayoutValues.eventMargin;
+        qreal newMarginValue = totalMarginSpace / (columns - 1);
+
+        // Check if we managed to pack all the events into space we have
+        if (newMarginValue > 0) {
+            eventWidth = minWidth;
+        }
+        else {
+            // There's not enough space
+            // New minWidth is KCalenMinTimeStripWidth [un] (time stripe only)
+            minWidth = KCalenMinTimeStripWidth * mLayoutValues.unitInPixels;
+            totalMarginSpace = mLayoutValues.eventAreaWidth - minWidth * columns 
+				- mLayoutValues.eventMargin;
+            newMarginValue = totalMarginSpace / (columns - 1);
+            eventWidth = minWidth;
+        }
+        
+        // First column margin should be always KCalenSpaceBeetwenEvents 
+        // (mLayoutValues.eventMargin)
+        eventStartX += columnIdx * (eventWidth + newMarginValue) + mLayoutValues.eventMargin;
+    }
+    else {
+        // Add margins between the event
+        eventStartX += columnIdx * eventWidth + mLayoutValues.eventMargin;
+        eventWidth -= mLayoutValues.eventMargin;
+    }
+
+    QRectF eventGeometry(eventStartX, eventStartY, eventWidth, eventHeight);
+    
+    // Workaround to prevent size hint caching inside effectiveSizeHint
+    item->setMinimumSize(0, 0);
+    item->setMaximumSize(eventWidth, eventHeight);
+    item->setGeometry(eventGeometry);
+}
+
+
+/*!
+ \brief Set size and position of singe all-day event widget (bubble)
+ \a item bubble widget
+ \a index pointing item data in model
+ */
+void CalenDayContainer::updateAllDayEventGeometry(
+    HbAbstractViewItem *item,
+    const QModelIndex &index)
+{
+    // Safety check
+    if (!mInfo) {
+        return;
+    }
+
+    QVariant variant = index.data(CalenDayEntry);
+    AgendaEntry entry = variant.value<AgendaEntry> ();
+
+    // 1. Get 'virtual' event position from DayInfo 
+    SCalenApptInfo apptInfo;
+    apptInfo.iIndex = index;
+
+    QDateTime start;
+    QDateTime end;
+    QDateTime currentDate;
+    currentDate
+        = static_cast<const CalenDayModel*> (index.model())->modelDate();
+    CalenDayUtils::instance()->getEventValidStartEndTime(start, end, entry,
+        currentDate);
+    apptInfo.iStartTime = start;
+    apptInfo.iEndTime = end;
+
+    TCalenInstanceId id = TCalenInstanceId::nullInstanceId();
+    id.mEntryLocalUid = index.row(); //index.row() - temporary ID
+    id.mInstanceTime = apptInfo.iStartTime;
+    apptInfo.iId = id;
+    apptInfo.iAllDay = true;
+    apptInfo.iColor = 0xffff;
+
+    int startSlot, endSlot, columnIdx, columns;
+    mInfo->GetLocation(apptInfo, startSlot, endSlot, columnIdx, columns);
+
+    // 2. Set timed event's geometry
+    qreal eventStartX(0.0);
+    qreal eventStartY(0.0);
+    qreal eventWidth(mLayoutValues.eventAreaX);
+    qreal eventHeight = (endSlot - startSlot) * mLayoutValues.slotHeight;
+
+    // Event's startX/width
+    if (columns > 1) {
+        eventWidth /= columns;
+        eventStartX += columnIdx * eventWidth + mLayoutValues.eventMargin;
+        // Add margins between the event
+        eventWidth -= mLayoutValues.eventMargin;
+    }
+    else {
+        eventStartX += mLayoutValues.eventMargin;
+        eventWidth -= mLayoutValues.eventMargin;
+    }
+
+    QRectF eventGeometry(eventStartX, eventStartY, eventWidth, eventHeight);
+	
+    // Workaround to prevent size hint caching inside effectiveSizeHint
+    item->setMinimumSize(0, 0);
+    item->setMaximumSize(eventWidth, eventHeight);
+    item->setGeometry(eventGeometry);
+}
+
+
+/*!
+ \brief Gets event layout values
+ \a layoutValues structure to be filled with layout data
+ */
+void CalenDayContainer::getTimedEventLayoutValues(LayoutValues& layoutValues)
+{
+    // Get the width of entire content area
+    qreal contentWidth = CalenDayUtils::instance()->contentWidth();
+
+    HbStyle style;
+    HbDeviceProfile deviceProfile;
+    layoutValues.unitInPixels = deviceProfile.unitValue();
+
+    // Empty right column's width
+    qreal emptyRightColumnWidth = KCalenEmptyRightColumnWidth
+        * layoutValues.unitInPixels;
+
+    // Margins between the overlapping events -> eventMargin[out]
+    layoutValues.eventMargin = KCalenSpaceBeetwenEvents
+        * layoutValues.unitInPixels;
+
+    // Start position (x) for drawing events -> eventAreaX[out]
+    if (mInfo && mInfo->AlldayCount()) {
+        layoutValues.eventAreaX = KCalenAllDayEventArea * (contentWidth
+            - emptyRightColumnWidth);
+    }
+    else {
+        layoutValues.eventAreaX = 0;
+    }
+
+    // Event area width (excluding All Day Events area)-> eventAreaWidth[out]
+    layoutValues.eventAreaWidth = contentWidth - emptyRightColumnWidth
+        - layoutValues.eventAreaX;
+
+    // Half-hour slot's height -> slotHeight[out]
+    layoutValues.slotHeight = CalenDayUtils::instance()->hourElementHeight()
+        / KCalenSlotsInHour;
+
+    // Check if touch absorbers should be created over some overlapping regions
+    layoutValues.maxColumns = layoutValues.eventAreaWidth
+        / ((KCalenMinTouchableEventWidth + KCalenSpaceBeetwenEvents)
+            * layoutValues.unitInPixels);
+}
+
+
+/*!
+ \brief Sets day's info structure to the container.
+ \a dayInfo day's info data
+
+ \sa CalenDayInfo, CalenDayContainer::dayInfo
+ */
+void CalenDayContainer::setDayInfo(CalenDayInfo* dayInfo)
+{
+    mInfo = dayInfo;
+}
+
+/*!
+ \brief It return pointer to info structure of container.
+ 
+ \sa CalenDayInfo, CalenDayContainer::setDayInfo
+ */
+CalenDayInfo* CalenDayContainer::dayInfo()
+{
+    return mInfo;
+}
+
+/*!
+ \brief Sets date to the container. 
+ Changes according to model which is connected to given view.
+ 
+ \a date Date of container
+ */
+void CalenDayContainer::setDate(const QDate &date)
+{
+    mDate = date;
+}
+
+// -----------------------------------------------------------------------------
+// date()
+// Returns date of the container.
+// -----------------------------------------------------------------------------
+//
+/*!
+ \brief Returns date of the container.
+ 
+ \sa date Date of container
+ */
+const QDate &CalenDayContainer::date() const
+{
+    return mDate;
+}
+
+/*!
+ \brief Slot handles layout switch.
+ \a orientation current device orientation
+ */
+void CalenDayContainer::orientationChanged(Qt::Orientation orientation)
+{
+    getTimedEventLayoutValues(mLayoutValues);
+
+    Q_UNUSED( orientation )
+    QList<HbAbstractViewItem *> items = this->items();
+    int count(items.count());
+    for (int i = 0; i < count; i++) {
+        QModelIndex modelIndex = items[i]->modelIndex();
+        if (modelIndex.isValid()) {
+            QVariant variant = modelIndex.data(CalenDayEntry);
+            AgendaEntry entry = variant.value<AgendaEntry> ();
+            if (entry.isTimedEntry() && !CalenAgendaUtils::isAlldayEvent(entry)) {
+                updateTimedEventGeometry(items[i], modelIndex);
+            }
+            else
+                if (CalenAgendaUtils::isAlldayEvent(entry)) {
+                    updateAllDayEventGeometry(items[i], modelIndex);
+                }
+        }
+    }
+
+    createTouchEventAbsorbers();
+}
+
+
+/*!
+ \brief Creates absorbers which prevent touching to small items
+ According to UI spec items smaller than 8.2 un are untouchable
+ */
+void CalenDayContainer::createTouchEventAbsorbers()
+{
+    // remove absorbers if exist
+    if (mAbsorbers.count()) {
+        qDeleteAll(mAbsorbers);
+        mAbsorbers.clear();
+    }
+
+    // Create absorber for all-day events
+    Qt::Orientation orientation = CalenDayUtils::instance()->orientation();
+    int allDayCount = mInfo->AlldayCount();
+
+    if ((orientation == Qt::Vertical 
+        && allDayCount > KCalenTouchableAllDayEventsCountPortrait) 
+        || (orientation == Qt::Horizontal
+        && allDayCount > KCalenTouchableAllDayEventsCountLandscape)) {
+        TouchEventAbsorber* absorber = crateAbsorberBetweenSlots(0, 0, true);
+        mAbsorbers.append(absorber);
+    }
+
+    // Create absorbers for timed events
+    const QList<CalenTimeRegion>& regionList = mInfo->RegionList();
+    for (int i = 0; i < regionList.count(); i++) {
+        if (regionList[i].iColumns.count() > mLayoutValues.maxColumns) {
+            TouchEventAbsorber* absorber = crateAbsorberBetweenSlots(
+                regionList[i].iStartSlot, regionList[i].iEndSlot, false);
+
+            mAbsorbers.append(absorber);
+        }
+    }
+}
+
+
+/*!
+ \brief Creates single absorber in given location
+ \a startSlot absorber area starts from there
+ \a endSlot absobrber area ends here
+ \a forAllDayEvents if true absorber in all-day events area is created
+ */
+TouchEventAbsorber *CalenDayContainer::crateAbsorberBetweenSlots(
+    int startSlot,
+    int endSlot,
+    bool forAllDayEvents)
+{
+    TouchEventAbsorber *absorber = new TouchEventAbsorber(this);
+    absorber->setZValue(1000);
+    absorber->setVisible(true);
+    if (!forAllDayEvents) {
+        absorber->setGeometry(mLayoutValues.eventAreaX, // x
+            startSlot * mLayoutValues.slotHeight, // y
+            mLayoutValues.eventAreaWidth, // w
+            (endSlot - startSlot) * mLayoutValues.slotHeight); // h
+    }
+    else {
+        absorber->setGeometry(0, 0, mLayoutValues.eventAreaX,
+            KCalenHoursInDay * KCalenSlotsInHour * mLayoutValues.slotHeight);
+    }
+
+    return absorber;
+}
+
+
+/*!
+ \brief Handles tap event on overlapping area
+ Currently it leads to Agenda View -  as described in UI spec
+ \a event qt gesture event
+ */
+void TouchEventAbsorber::gestureEvent(QGestureEvent *event)
+{
+    QTapGesture *tapGesture = qobject_cast<QTapGesture*> (event->gesture(
+        Qt::TapGesture));
+    
+    if (tapGesture && tapGesture->state() == Qt::GestureFinished) {
+    	CalenDayView* dayView = static_cast<CalenDayView*>
+                (CalenDayUtils::instance()->mainWindow()->currentView());
+    	
+    	dayView->changeView(ECalenAgendaView);
+    }
+}
+
+/*!
+ \brief Constructor
+ */
+TouchEventAbsorber::TouchEventAbsorber(QGraphicsItem *parent) :
+    HbWidget(parent)
+{
+#ifdef _DEBUG
+    setFlag(QGraphicsItem::ItemHasNoContents, false);
+#endif	
+    grabGesture(Qt::TapGesture);
+}
+
+
+/*!
+ \brief Destructor
+ 
+ Sets container initial geometry, creates hours area widgets.
+ */
+TouchEventAbsorber::~TouchEventAbsorber()
+{
+
+}
+
+
+/*!
+ \brief Used for debugging purposes to see absorbers areas
+ Not active in release builds!
+ 
+ */
+#ifdef _DEBUG
+void TouchEventAbsorber::paint(
+    QPainter *painter,
+    const QStyleOptionGraphicsItem *option,
+    QWidget *widget)
+{
+    Q_UNUSED(option)
+    Q_UNUSED(widget)
+
+    painter->save();
+    QPen pen;
+    pen.setWidth(2);
+    pen.setColor(Qt::red);
+    painter->setPen(pen);
+    painter->drawRect(boundingRect());
+    painter->restore();
+}
+#endif
+// End of File