calendarwidgetplugin/src/contentlayouthandler.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Tue, 06 Jul 2010 11:55:49 +0300
changeset 0 db1bf15cefff
child 1 f8e7eccf5f96
permissions -rw-r--r--
Revision: 201027

/*
* Copyright (c) 2009 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: Homescreen calendar widget's content layout handler.
*
*/

// System includes
#include <HbColorScheme>
#include <QDateTime>
#include <HbLabel>
#include <HbDocumentLoader>
#include <HbCore>
#include <HbFrameDrawer>
#include <HbFrameItem>
#include <QTimer>
#ifndef TEST
#include <agendautil.h>
#else
#include "mockagendautil.h"
#endif
#include <agendaentry.h>
#include <xqsettingsmanager.h>
#include <xqsettingskey.h>
#include <calnotification.h>
#include <QGraphicsLayout>
#include <QPointF>
#include <xqaiwrequest.h>
#include <xqappmgr.h>
#include <QCoreApplication>

// User includes
#include "contentlayouthandler.h"
#include "calendarwidgetdebug.h"
#include "datetimeobserver.h"

// Local constants
namespace {
    const HbFrameDrawer::FrameType REMINDER_FRAME_TYPE = HbFrameDrawer::OnePiece;
    const char *CONTENT_CONTAINER = "contentContainer";
    const char *UPPER_LABEL_SHORT = "upperLabelShort";
    const char *UPPER_LABEL_LONG = "upperLabelLong";
    const char *MIDDLE_LABEL = "middleLabel";
    const char *LOWER_LABEL = "lowerLabel";
    const char *REMINDER_ICON_LABEL = "reminderLabel";
    const char *REMINDER_IMAGE_NAME = "qtg_small_reminder";
    const char *HIGHLIGHT_IMAGE_NAME = "qtg_fr_hsitems_pressed";
    //same color applies to upper and middle labels
    const char *UPPER_LABEL_COLOR = "qtc_hs_list_item_title_normal";
    const char *LOWER_LABEL_COLOR = "qtc_hs_list_item_content_normal";
    const char *HIGHLIGHT_LABEL_COLOR = "qtc_hs_list_item_pressed";
    // time constants
    const int KHalfHour = 30;
    const int KSecondInMillisec = 1000;
    const int KSecondsInMinute = 60;
    const int KToday = 1;
    const int KComingDays = 0;
    const int KDaysInWeek = 7;
    const int KHourMinuteSeparatorIndex = 1;
    const int KPassedEventTimeoutSecs = 1800; // Half hour
    
    //from DateIconLayoutHandler
    const HbFrameDrawer::FrameType DATE_ICON_BACKGROUND_FRAME_TYPE = HbFrameDrawer::OnePiece;
    const char *ICON_CONTAINER = "iconContainer";
    const char *DATE_ICON_LABEL = "iconLabel";
    const char *DATE_ICON_DAYNUMBER = "dayNumber";
    const char *DATE_ICON_MONTHLABEL = "month";
    const char *DATE_ICON_HIGHLIGHTLABEL = "iconHighlight";
    const char *NUMBER_LABEL_COLOR = "qtc_hs_cal";
    const char *MONTH_LABEL_COLOR = "qtc_hs_list_item_title_normal";
    //const char *HIGHLIGHT_LABEL_COLOR = "qtc_hs_list_item_pressed";
    const char *DATE_BACKGROUND = "qtg_large_calendar_dynamic";
    //const char *HIGHLIGHT_IMAGE_NAME = "qtg_fr_hsitems_pressed";
    
    //From MainLayoutHandler
    const char WIDGET[] = "CalendarWidget";
    const char *BACKGROUND_LABEL = "widgetBackground";
    const char *SEPARATOR_LABEL = "separatorLabel";
    const char *SEPARATOR_IMAGE_NAME = "qtg_graf_divider_v_thin";
    const char *BACKGROUND_IMAGE_NAME = "qtg_fr_hswidget_normal";
    const HbFrameDrawer::FrameType BACKGROUND_FRAME_TYPE = HbFrameDrawer::NinePieces;
    const HbFrameDrawer::FrameType SEPARATOR_FRAME_TYPE = HbFrameDrawer::OnePiece;
}

// ======== MEMBER FUNCTIONS ========

/*
 * ContentLayoutHandler::ContentLayoutHandler()
 */
ContentLayoutHandler::ContentLayoutHandler()
        : mContainer(0),
          mReminderLabel(0),
          mUpperLabelShort(0),
          mUpperLabelLong(0),
          mMiddleLabel(0),
          mLowerLabel(0),
          mIconContainer(0),
          mIconLabel(0),
          mNumberLabel(0),
          mMonthLabel(0),
          mHighlightLabel(0),
          mDateIconFrameDrawer(0),
          mReminderDrawer(0),
          mEventTimer(0),
          mAgendaUtil(0),
          mCalendarKeyManager(0),
          mDateHighlightActive(0),
          mContentHighlightActive(0)
{
    LOGS("ContentLayoutHandler::ContentLayoutHandler");

    mCalendarKeyManager = new XQSettingsManager();
    unsigned long int key = (unsigned long int)ECalPubSubEventNotification;
    XQSettingsKey calendarKey(XQSettingsKey::TargetPublishAndSubscribe,
        KCalPubSubCategory.iUid, key);
    mCalendarKeyManager->startMonitoring(calendarKey);
    connect(mCalendarKeyManager, SIGNAL(valueChanged(XQSettingsKey, QVariant)),
        this, SLOT(calendarChanged(XQSettingsKey, QVariant)));
}

/*
 * ContentLayoutHandler::~ContentLayoutHandler()
 */
ContentLayoutHandler::~ContentLayoutHandler()
{
    LOGS("ContentLayoutHandler::~ContentLayoutHandler");
    unsigned long int key = (unsigned long int)ECalPubSubEventNotification;
    XQSettingsKey calendarKey(XQSettingsKey::TargetPublishAndSubscribe,
        KCalPubSubCategory.iUid, key);
    mCalendarKeyManager->stopMonitoring(calendarKey);
    delete mCalendarKeyManager;
}

/*
 * ContentLayoutHandler::initializeLayout()
 */
void ContentLayoutHandler::initializeLayout(const HbDocumentLoader &layoutLoader,
    QObject *owner)
{
    this->setParent(owner);
    this->setObjectName("contentlayouthandler");
    
    //From MainLayoutHandler
    mWidget = (HbWidget*)layoutLoader.findWidget(WIDGET);
    mBgLabel = qobject_cast<HbLabel*>(layoutLoader.findWidget(BACKGROUND_LABEL));
    mSeparatorLabel = qobject_cast<HbLabel*>(layoutLoader.findWidget(QString(SEPARATOR_LABEL)));

    // Discontinue initialization of the main widget is missing
    if (!mWidget) {
        return;
    }

    addItem(mWidget);
    mWidget->setVisible(true);

    // Create main background drawer
    if (mBgLabel) {
        HbFrameDrawer* backgroundFrameDrawer = new HbFrameDrawer(BACKGROUND_IMAGE_NAME,
            BACKGROUND_FRAME_TYPE);
        mBackgroundLayoutItem = new HbFrameItem(backgroundFrameDrawer);
        mBgLabel->setBackgroundItem(mBackgroundLayoutItem);
    }
    
    // Create separator graphics drawer
    HbFrameDrawer* separatorFrameDrawer = new HbFrameDrawer(SEPARATOR_IMAGE_NAME,
        SEPARATOR_FRAME_TYPE);
    mSeparatorLayoutItem = new HbFrameItem(separatorFrameDrawer);
    mSeparatorLabel->setBackgroundItem(mSeparatorLayoutItem);

    setPreferredSize(mWidget->preferredSize());
    //END
    
    //From DateIconLayoutHandler
    mIconContainer = qobject_cast<HbWidget*>(layoutLoader.findWidget(QString(ICON_CONTAINER)));
    mIconLabel = qobject_cast<HbLabel*>(layoutLoader.findWidget(QString(DATE_ICON_LABEL)));
    mNumberLabel = qobject_cast<HbLabel*>(layoutLoader.findWidget(QString(DATE_ICON_DAYNUMBER)));
    mMonthLabel = qobject_cast<HbLabel*>(layoutLoader.findWidget(QString(DATE_ICON_MONTHLABEL)));
    mHighlightLabel = qobject_cast<HbLabel*>(layoutLoader.findWidget(QString(DATE_ICON_HIGHLIGHTLABEL)));

    // Create background drawer only if day container loading succeeded
    if (mIconLabel) {
        mDateIconFrameDrawer = new HbFrameDrawer(DATE_BACKGROUND, DATE_ICON_BACKGROUND_FRAME_TYPE);
        HbFrameItem* dateIconFrameItem = new HbFrameItem(mDateIconFrameDrawer);
        mIconLabel->setBackgroundItem(dateIconFrameItem);
    }

    setCalendarIconContent();
    setLabelsColor();

    // The icon label should never show text
    if (mIconLabel) {
        mIconLabel->clear();
    }
    if (mHighlightLabel) {
        mHighlightLabel->clear();
    }

    
    mDateObserver = new DateTimeObserver();
    connect(mDateObserver, SIGNAL(completed()), this, SIGNAL(dateChanged()));
    connect(this, SIGNAL(dateChanged()), this, SLOT(updateLayout()));
    //END
    
    // Get widget handles
    mContainer = qobject_cast<HbWidget*>(
        layoutLoader.findWidget(QString(CONTENT_CONTAINER)));
    mUpperLabelShort = qobject_cast<HbLabel*>(
        layoutLoader.findWidget(QString(UPPER_LABEL_SHORT)));
    mUpperLabelLong = qobject_cast<HbLabel*>(
        layoutLoader.findWidget(QString(UPPER_LABEL_LONG)));
    mMiddleLabel = qobject_cast<HbLabel*>(
        layoutLoader.findWidget(QString(MIDDLE_LABEL)));
    mLowerLabel = qobject_cast<HbLabel*>(
        layoutLoader.findWidget(QString(LOWER_LABEL)));
    mReminderLabel = qobject_cast<HbLabel*>(
        layoutLoader.findWidget(QString(REMINDER_ICON_LABEL)));

    // Create reminder icon drawer only if reminder label loading succeeded.
    if (mReminderLabel) {
        // Make sure there's no text in reminder icon label
        mReminderLabel->clear();
        // Create drawer for reminder icon
        mReminderDrawer = new HbFrameDrawer(REMINDER_IMAGE_NAME,
            REMINDER_FRAME_TYPE);
        //ownership transferred
        HbFrameItem* reminderFrameItem = new HbFrameItem(mReminderDrawer);
        //ownership transferred
        mReminderLabel->setBackgroundItem(reminderFrameItem);
    }

    setLabelsColor();
    hideLabels();
    createAgendaUtilsInstance();
    initializeTimer();
}

/*
 * ContentLayoutHandler::calendarChanged()
 */
void ContentLayoutHandler::calendarChanged(const XQSettingsKey& key,
    const QVariant& value)
{
    LOGS("ContentLayoutHandler::calendarChanged");
    Q_UNUSED(key);
    Q_UNUSED(value);
    updateLayout();
}

/*
 * ContentLayoutHandler::updateLayout()
 */
void ContentLayoutHandler::updateLayout()
{
    LOGS("ContentLayoutHandler::updateLayout");
    decorateContent();
    setTimerForNextUpdate();
    //From DateIconLayoutHandler
    setCalendarIconContent();
}

/*
 * ContentLayoutHandler::onThemeChange()
 */
void ContentLayoutHandler::onThemeChange()
{
    LOGS("ContentLayoutHandler::onThemeChange");
    if (mReminderDrawer) {
        mReminderDrawer->themeChanged();
    }
    
    if (mDateIconFrameDrawer) {
        mDateIconFrameDrawer->themeChanged();
    }
    
    if (mBackgroundLayoutItem) {
        mBackgroundLayoutItem->frameDrawer().themeChanged();
    }
    if (mSeparatorLayoutItem) {
        mSeparatorLayoutItem->frameDrawer().themeChanged();
    }
    
    setLabelsColor();
}

/*
 * ContentLayoutHandler::createAgendaUtilsInstance()
 */
void ContentLayoutHandler::createAgendaUtilsInstance()
{
    LOGS("ContentLayoutHandler::createAgendaUtilsInstance");
    mAgendaUtil = new AgendaUtil(this);
    connect(mAgendaUtil, SIGNAL(entryViewCreationCompleted(int)),
        this, SLOT(updateLayout()));
}

/*
 * ContentLayoutHandler::initializeTimer()
 */
void ContentLayoutHandler::initializeTimer()
{
    LOGS("ContentLayoutHandler::initializeTimer");
    mEventTimer = new QTimer();
    mEventTimer->setSingleShot(true);
    connect(mEventTimer, SIGNAL(timeout()), this, SLOT(eventTimerExpired()));
    setTimerForNextUpdate();
}

/*
 * ContentLayoutHandler::hideLabels()
 */
void ContentLayoutHandler::hideLabels()
{
    LOGS("ContentLayoutHandler::hideLabels");
    setLabelVisible(mMiddleLabel, false);
    setLabelVisible(mUpperLabelShort, false);
    setLabelVisible(mUpperLabelLong, false);
    setLabelVisible(mLowerLabel, false);
    setLabelVisible(mReminderLabel, false);
}

/*
 * ContentLayoutHandler::setLabelsColor()
 */
void ContentLayoutHandler::setLabelsColor()
{
    LOGS("ContentLayoutHandler::setLabelsColor");
    
    // --- Content layout ---
    
    // Choose text colors according to highlight state
    QString upperLabelColorAttribute;
    QString lowerLabelColorAttribute;
    if (mContentHighlightActive) {
        upperLabelColorAttribute = HIGHLIGHT_LABEL_COLOR;
        lowerLabelColorAttribute = HIGHLIGHT_LABEL_COLOR;
    }
    else {
        upperLabelColorAttribute = UPPER_LABEL_COLOR;
        lowerLabelColorAttribute = LOWER_LABEL_COLOR;
    }
    QColor upperLabelTextColor(HbColorScheme::color(upperLabelColorAttribute));
    QColor lowerLabelTextColor(HbColorScheme::color(lowerLabelColorAttribute));
    
    // Set upper and middle label text color
    if (upperLabelTextColor.isValid()) {
        if (mUpperLabelShort) {
            mUpperLabelShort->setTextColor(upperLabelTextColor);
        }
        if (mUpperLabelLong) {
            mUpperLabelLong->setTextColor(upperLabelTextColor);
        }
        if (mMiddleLabel) {
            mMiddleLabel->setTextColor(upperLabelTextColor);
        }
    }

    // Set lower label text color
    if (lowerLabelTextColor.isValid()) {
        if (mLowerLabel) {
            mLowerLabel->setTextColor(lowerLabelTextColor);
        }
    }
    
    // --- DateIconLayoutHandler ---
    QString numberLabelColorAttribute;
    QString monthLabelColorAttribute;
    if (mDateHighlightActive) {
        numberLabelColorAttribute = NUMBER_LABEL_COLOR;
        monthLabelColorAttribute = HIGHLIGHT_LABEL_COLOR;
    }
    else {
        numberLabelColorAttribute = NUMBER_LABEL_COLOR;
        monthLabelColorAttribute = MONTH_LABEL_COLOR;
    }
    QColor numberLabelTextColor(HbColorScheme::color(numberLabelColorAttribute));
    QColor monthLabelTextColor(HbColorScheme::color(monthLabelColorAttribute));
    
    // Set day number label text color
    if (numberLabelTextColor.isValid()) {
        if (mNumberLabel) {
            mNumberLabel->setTextColor(numberLabelTextColor);
        }
    }
    // Set month label text color
    if (monthLabelTextColor.isValid()) {
        if (mMonthLabel) {
            mMonthLabel->setTextColor(monthLabelTextColor);
        }
    }
}

/*
 * ContentLayoutHandler::eventTimerExpired
 */
void ContentLayoutHandler::eventTimerExpired()
{
    LOGS("ContentLayoutHandler::eventTimerExpired");
    decorateContent();
    setTimerForNextUpdate();
}

/*
 * ContentLayoutHandler::timeToNextCheck()
 */
int ContentLayoutHandler::timeToNextCheck()
{
    LOGS("ContentLayoutHandler::timeToNextCheck");
    QDateTime now = currentDateTime();
    int secondsToNextCheck = 0;
    int currentSeconds = 0;
    currentSeconds = now.toTime_t();
    int entryStartTimeSeconds = 0;
    int entryEndTimeSeconds = 0;
    int indexEntryToCheck = firstEntryIndex();

    QDateTime startTime = mAgendaEntryList.at(indexEntryToCheck).startTime();
    QDateTime endTime = mAgendaEntryList.at(indexEntryToCheck).endTime();
    entryStartTimeSeconds = startTime.toTime_t();
    entryEndTimeSeconds = endTime.toTime_t();

    //check if the event is bigger than half hour
    if ((entryEndTimeSeconds - entryStartTimeSeconds) < (KHalfHour * KSecondsInMinute)) {
        secondsToNextCheck = entryEndTimeSeconds - currentSeconds;
    }
    else {
        //check that it's not an all time event.
        //if it's an all day event there's no need to set the timer because there is already a date check and the widget content
        //will be updated anyway.
        if (!isAllDayEvent(mAgendaEntryList.at(indexEntryToCheck))) {
            secondsToNextCheck = entryStartTimeSeconds + KHalfHour * KSecondsInMinute
                - currentSeconds;
        }
    }

    int mSecsToNextCheck = 0;
    if (secondsToNextCheck > INT_MAX / KSecondInMillisec) {
        mSecsToNextCheck = INT_MAX;
    }
    else {
        //seconds to milliseconds
        mSecsToNextCheck = secondsToNextCheck * KSecondInMillisec;
    }

    return mSecsToNextCheck;
}

/*
 * ContentLayoutHandler::firstEntryIndex()
 */
int ContentLayoutHandler::firstEntryIndex()
{
    int indexEntryToCheck = 0;
    LOGS("ContentLayoutHandler::firstEntryIndex()");

    //let's find the first non all day event in the list
    while (isAllDayEvent(mAgendaEntryList.at(indexEntryToCheck))
        && (indexEntryToCheck < mAgendaEntryList.count() - 1)) {
        indexEntryToCheck++;
    }

    //if we found the first valid non all day event as last in the array then we have to keep that index
    if ((indexEntryToCheck == mAgendaEntryList.count() - 1)
        && isAllDayEvent(mAgendaEntryList.at(indexEntryToCheck))) {
        indexEntryToCheck = 0;
    }

    return indexEntryToCheck;
}

/*
 * ContentLayoutHandler::decorateContent()
 */
void ContentLayoutHandler::decorateContent()
{
    LOGS("ContentLayoutHandler::decorateContent");
    updateList();

    if (mAgendaEntryList.count()) {
        removeTodosFrom();
        //it removes the events that are shorter than 30 minutes and are already passed
        removePassedEvents();
    }

    //there might be only ToDos. Let's check again.
    if (mAgendaEntryList.count()) {
        int endEventIndex = 0;
        if (todayHasValidEvents()) {
            int numOverlappingEvents = checkOverlappingEvents(0, endEventIndex);
            if (!numOverlappingEvents) {
                showNextEvent();
            }
            else {
                showOverlappingEvents(KToday, numOverlappingEvents, endEventIndex);
            }
        }
        else {
            int numOverlappingEvents = checkOverlappingEvents(0, endEventIndex);
            if (!numOverlappingEvents) {
                showNextDaysEvent();
            }
            else {
                showOverlappingEvents(KComingDays, numOverlappingEvents, endEventIndex);
            }
        }
    }
    else {
        showNoEventsNextWeek();
    }
    
    QDateTime date;
    if (mAgendaEntryList.count()) {
        date = mAgendaEntryList.at(0).startTime();
    }
    else {
        date = QDateTime::currentDateTime();
    }
    
    emit currentDate(date);
}

/*
 * ContentLayoutHandler::updateList()
 */
void ContentLayoutHandler::updateList()
{
    LOGS("ContentLayoutHandler::updateList");
    
    // Calculate start of event fetching range
    QDateTime previousMidnight(currentDateTime().date(), QTime(0, 0));
    QDateTime passedEventStart(currentDateTime().addSecs(-KPassedEventTimeoutSecs));
    QDateTime rangeStart;
    if (previousMidnight < passedEventStart) {
        rangeStart = previousMidnight;
    }
    else {
        rangeStart = passedEventStart;
    }

    // Calculate end of event fetching range
    QDateTime rangeEnd(previousMidnight.addDays(KDaysInWeek+1));
    
    // Fetch events
    mAgendaEntryList = mAgendaUtil->fetchEntriesInRange(rangeStart, rangeEnd);
    
    QString agenda_count = "count: "
        + QString::number(mAgendaEntryList.count(), 10);
    LOGS(agenda_count);
    sortList();
}

/*
 * ContentLayoutHandler::removePassedEvents()
 */
void ContentLayoutHandler::removePassedEvents()
{
    LOGS("ContentLayoutHandler::removePassedEvents");
    for (int i = 0; i < mAgendaEntryList.count(); i++) {
        //add half hour
        QDateTime entryStartTime =
            mAgendaEntryList.at(i).startTime().addSecs(KHalfHour * KSecondsInMinute);
        LOGS(entryStartTime.toString());
        LOGS(currentDateTime());
        //if the entry start time plus half hour is less than current time or it is not but the entry duration is less than half hour (endtime < currenttime)
        //and the entry is not an all day event then remove it because it is passed.
        if (((entryStartTime < currentDateTime())
            || (mAgendaEntryList.at(i).endTime() < currentDateTime()))
            && !isAllDayEvent(mAgendaEntryList.at(i))) {
            mAgendaEntryList.removeAt(i);
            i--;
        }
    }
}

/*
 * ContentLayoutHandler::currentDateFormat()
 */
QString ContentLayoutHandler::currentDateFormat()
{
    LOGS("ContentLayoutHandler::currentDateFormat");
    QString format;
    HbExtendedLocale locale;
    switch (locale.dateStyle()) {
        case HbExtendedLocale::American:
        case HbExtendedLocale::Japanese:
            format = QString("MMM dd");
            break;
        case HbExtendedLocale::European:
            format = QString("dd MMM");
            break;
        default:
            format = QString("dd:MM");
            break;
    }        
    return format;
}

/*
 * ContentLayoutHandler::currentTimeFormat()
 */
QString ContentLayoutHandler::currentTimeFormat()
{
    LOGS("ContentLayoutHandler::currentTimeFormat");
    QString format;
    HbExtendedLocale locale;
    QChar separator = locale.timeSeparator(KHourMinuteSeparatorIndex);
    format = QString("hh") + separator + QString("mm");
    if (locale.timeStyle() == HbExtendedLocale::Time12) {
        if (locale.amPmSymbolPosition() == HbExtendedLocale::Before) {
            if (locale.amPmSpace()) {
                format.prepend(' ');
            }
            format.prepend("ap");
        }
        else {
            if (locale.amPmSpace()) {
                format.append(' ');
            }
            format.append("ap");
        }    
    }
    return format;
}

/*
 * ContentLayoutHandler::showNextDaysEvent()
 */
void ContentLayoutHandler::showNextDaysEvent()
{
    LOGS("ContentLayoutHandler::showNextDaysEvent");
    setLabelVisible(mMiddleLabel, false);
    setLabelVisible(mUpperLabelShort, true);
    setLabelVisible(mLowerLabel, true);
    QString day = mAgendaEntryList.at(0).startTime().date().toString(currentDateFormat());
    QString startTime = mAgendaEntryList.at(0).startTime().time().toString(currentTimeFormat());
    QString endTime = mAgendaEntryList.at(0).endTime().time().toString(currentTimeFormat());
    QString description = mAgendaEntryList.at(0).summary();
    if (!isAllDayEvent(mAgendaEntryList.at(0))) {
        //if not all day event show the start time
        if (mAgendaEntryList.at(0).startTime().date() == mAgendaEntryList.at(0).endTime().date()) {
            setLabelText(mUpperLabelShort, day + " " + startTime + " " + description);
            setLabelText(mLowerLabel, hbTrId("txt_calendar_widget_v_dblist_val_no_events_tod"));
        }
        else {
            //if the end date is different from the starting date show that information
            setLabelText(mUpperLabelShort, day + " " + startTime + "-"
                + mAgendaEntryList.at(0).endTime().date().toString(currentDateFormat()) + " "
                + description);
            setLabelText(mLowerLabel, hbTrId("txt_calendar_widget_v_dblist_val_no_events_tod"));
        }
    }
    else {
        // It's an all day event
        if (isOneDayAllDayEvent(mAgendaEntryList.at(0))) {
            // It's a one day all day event
            setLabelText(mUpperLabelShort, day + " " + description);
            setLabelText(mLowerLabel, hbTrId("txt_calendar_widget_v_dblist_val_no_events_tod"));
        }
        else {
            // It's a multiple day all day event
            setLabelText(mUpperLabelShort, day + " - "
                + mAgendaEntryList.at(0).endTime().date().addDays(-1).toString(currentDateFormat()) + " "
                + description);
            setLabelText(mLowerLabel, hbTrId("txt_calendar_widget_v_dblist_val_no_events_tod"));
        }
    }

    int timeoffset = mAgendaEntryList.at(0).alarm().timeOffset();
    if (mAgendaEntryList.at(0).alarm().timeOffset()
        && mAgendaEntryList.at(0).alarm().timeOffset() != -1) {
        setLabelVisible(mReminderLabel, true);
    }
    else {
        setLabelVisible(mReminderLabel, false);
    }
}

/*
 * ContentLayoutHandler::showOverlappingEvents()
 */
void ContentLayoutHandler::showOverlappingEvents(int when, int numOverlappingEvents,
    int aEndEventIndex)
{
    LOGS("ContentLayoutHandler::showOverlappingEvents");
    if (when == KToday) {
        setLabelVisible(mMiddleLabel, false);
        setLabelVisible(mUpperLabelShort, true);
        setLabelVisible(mLowerLabel, true);
        QString startTime = mAgendaEntryList.at(0).startTime().time().toString(currentTimeFormat());
        QString endTime = calculateEndTime(aEndEventIndex);
        QString day = mAgendaEntryList.at(0).startTime().date().toString(currentDateFormat());
        if (!isAllDayEvent(mAgendaEntryList.at(0))) {
            if (mAgendaEntryList.at(0).startTime().date()
                == mAgendaEntryList.at(aEndEventIndex).endTime().date()) {
                //if not all day event show the start time and end time
                setLabelText(mUpperLabelShort, startTime + "-" + endTime);
                setLowerLabelOverlapping(numOverlappingEvents);
            }
            else {
                setLabelText(mUpperLabelShort, startTime + "-"
                    + mAgendaEntryList.at(aEndEventIndex).endTime().date().toString(
                        currentDateFormat()) + " "
                    + mAgendaEntryList.at(aEndEventIndex).endTime().time().toString(
                        currentTimeFormat()));
                setLowerLabelOverlapping(numOverlappingEvents);
            }
        }
        else {
            // It's an all day event
            if (isOneDayAllDayEvent(mAgendaEntryList.at(0))) {
                // It's a one day all day event
                setLabelText(mUpperLabelShort, day);
                setLowerLabelOverlapping(numOverlappingEvents);
            }
            else {
                // It's a multiple day all day event
                setLabelText(mUpperLabelShort, day + " - "
                    + mAgendaEntryList.at(0).endTime().date().addDays(-1).toString(currentDateFormat()));
                setLowerLabelOverlapping(numOverlappingEvents);
            }
        }
    }
    else {
        setLabelVisible(mMiddleLabel, false);
        setLabelVisible(mUpperLabelShort, true);
        setLabelVisible(mLowerLabel, true);
        QString day = mAgendaEntryList.at(0).startTime().date().toString(currentDateFormat());
        QString startTime = mAgendaEntryList.at(0).startTime().time().toString(currentTimeFormat());
        QString endTime = calculateEndTime(aEndEventIndex);
        if (!isAllDayEvent(mAgendaEntryList.at(0))) {
            if (mAgendaEntryList.at(0).startTime().date()
                == mAgendaEntryList.at(aEndEventIndex).endTime().date()) {
                //if not all day event show the start time and end time
                setLabelText(mUpperLabelShort, day + " " + startTime + "-" + endTime);
                setLowerLabelOverlapping(numOverlappingEvents);
            }
            else {
                setLabelText(mUpperLabelShort, day + " " + startTime + "-"
                    + mAgendaEntryList.at(0).endTime().date().toString(currentDateFormat()));
                setLowerLabelOverlapping(numOverlappingEvents);
            }
        }
        else {
            // It's an all day event
            if (isOneDayAllDayEvent(mAgendaEntryList.at(0))) {
                // It's a one day all day event
                setLabelText(mUpperLabelShort, day);
                setLowerLabelOverlapping(numOverlappingEvents);
            }
            else {
                // It's a multiple day all day event
                setLabelText(mUpperLabelShort, day + " - "
                    + mAgendaEntryList.at(0).endTime().date().addDays(-1).toString(currentDateFormat()));
                setLowerLabelOverlapping(numOverlappingEvents);
            }
        }
    }

    // Never show reminder icon for overlapping events
    setLabelVisible(mReminderLabel, false);
}

/*
 * ContentLayoutHandler::calculateEndTime()
 */
QString ContentLayoutHandler::calculateEndTime(int& aEndEventIndex)
{
    LOGS("ContentLayoutHandler::calculateEndTime");
    QDateTime current = mAgendaEntryList.at(0).endTime();
    int endTimeIndex = 0;
    for (int i = 0; i < aEndEventIndex + 1; i++) {
        if (mAgendaEntryList.at(i).endTime() > current) {
            current = mAgendaEntryList.at(i).endTime();
            endTimeIndex = i;
        }
    }

    aEndEventIndex = endTimeIndex;
    return current.time().toString(currentTimeFormat());
}

/*
 * ContentLayoutHandler::showNextEvent()
 */
void ContentLayoutHandler::showNextEvent()
{
    LOGS("ContentLayoutHandler::showNextEvent");
    setLabelVisible(mMiddleLabel, false);
    setLabelVisible(mUpperLabelShort, true);
    setLabelVisible(mLowerLabel, true);
    QString startTime = mAgendaEntryList.at(0).startTime().time().toString(currentTimeFormat());
    QString endTime = mAgendaEntryList.at(0).endTime().time().toString(currentTimeFormat());
    QString description = mAgendaEntryList.at(0).summary();
    if (!isAllDayEvent(mAgendaEntryList.at(0))) {
        //if not all day event show the start time and end time
        if (mAgendaEntryList.at(0).startTime().date() == mAgendaEntryList.at(0).endTime().date()) {
            setLabelText(mUpperLabelShort, startTime + "-" + endTime);
            setLabelText(mLowerLabel, description);
        }
        else {
            //if the end date is different from the starting date show that information
            setLabelText(mUpperLabelShort, startTime + "-"
                + mAgendaEntryList.at(0).endTime().date().toString(currentDateFormat()) + " "
                + endTime);
            setLabelText(mLowerLabel, description);
        }
    }
    else {
        // It's an all day event
        if (isOneDayAllDayEvent(mAgendaEntryList.at(0))) {
            // It's a one day all day event
            setLabelText(mUpperLabelShort,
                mAgendaEntryList.at(0).startTime().date().toString(currentDateFormat()));
            setLabelText(mLowerLabel, description);
        }
        else {
            // It's a multiple day all day event
            setLabelText(mUpperLabelShort,
                mAgendaEntryList.at(0).startTime().date().toString(currentDateFormat())
                + " - " + mAgendaEntryList.at(0).endTime().date().addDays(-1).toString(
                currentDateFormat()));
            setLabelText(mLowerLabel, description);
        }
    }

    //check if reminder is set
    if (mAgendaEntryList.at(0).alarm().timeOffset()
        && mAgendaEntryList.at(0).alarm().timeOffset() != -1) {
        setLabelVisible(mReminderLabel, true);
    }
    else {
        setLabelVisible(mReminderLabel, false);
    }
}

/*
 * ContentLayoutHandler::showNoEventsNextWeek()
 */
void ContentLayoutHandler::showNoEventsNextWeek()
{
    LOGS("ContentLayoutHandler::showNoEventsNextWeek");
    setLabelText(mMiddleLabel, hbTrId("txt_calendar_widget_v_dblist_val_no_events_ox"));
    setLabelVisible(mMiddleLabel, true);
    setLabelVisible(mUpperLabelShort, false);
    setLabelVisible(mLowerLabel, false);
    setLabelVisible(mReminderLabel, false);
}

/*
 * ContentLayoutHandler::checkOverlappingEvents()
 */
int ContentLayoutHandler::checkOverlappingEvents(ulong id, int& aEndEventIndex)
{
    LOGS("ContentLayoutHandler::checkOverlappingEvents");
    int overlappingEvents = 0;
    AgendaEntry entry = mAgendaEntryList.at(id);
    for (int i = id + 1; i < mAgendaEntryList.count(); i++) {
        if ((entry.startTime() <= mAgendaEntryList.at(i).startTime())
            && (entry.endTime() >= mAgendaEntryList.at(i).startTime())
            && entry.startTime().date() == mAgendaEntryList.at(i).startTime().date()) {
            aEndEventIndex = i;
            overlappingEvents++;
        }
    }
    if (overlappingEvents) {
        //if there are overlapping events we have to count one more
        //because the minumum is two.
        overlappingEvents++;
    }

    return overlappingEvents;
}

/*
 * ContentLayoutHandler::sortList()
 */
void ContentLayoutHandler::sortList()
{
    LOGS("ContentLayoutHandler::sortList");
    for (int i = 0; i < mAgendaEntryList.count() - 1; i++) {
        for (int j = i + 1; j < mAgendaEntryList.count(); j++) {
            if (mAgendaEntryList.at(i).startTime() > mAgendaEntryList.at(j).startTime()) {
                //TODO: Maintain an array of integer ?
                mAgendaEntryList.swap(i, j);
            }
        }
    }
}

/*
 * ContentLayoutHandler::todayHasValidEvents()
 */
bool ContentLayoutHandler::todayHasValidEvents()
{
    LOGS("ContentLayoutHandler::todayHasValidEvents");
    QDateTime currentTime = currentDateTime();
    bool hasValidEvents = false;
    for (int i = 0; i < mAgendaEntryList.count(); i++) {
        if ((mAgendaEntryList.at(i).endTime() >= currentTime)
            && (mAgendaEntryList.at(i).startTime().date() == currentTime.date())) {
            hasValidEvents = true;
        }
    }

    return hasValidEvents;
}

/*
 * ContentLayoutHandler::removeTodosFrom()
 */
void ContentLayoutHandler::removeTodosFrom()
{
    LOGS("ContentLayoutHandler::removeTodosFrom");
    for (int i = 0; i < mAgendaEntryList.count();) {
        if ((mAgendaEntryList.at(i).type() == AgendaEntry::TypeTodo)
            || (mAgendaEntryList.at(i).type() == AgendaEntry::TypeNote)
            || (mAgendaEntryList.at(i).type() == AgendaEntry::TypeUnknown)) {
            mAgendaEntryList.removeAt(i);
        }
        else {
            //nothing has been removed. Increment the counter.
            i++;
        }
    }
}

void ContentLayoutHandler::handleOk(const QVariant& var)
{
    Q_UNUSED(var);

    LOGS("ContentLayoutHandler::handleOk");
    mTestResult = 0;
    emit requestComplete();
}

void ContentLayoutHandler::handleError(int err, const QString& str)
{
    Q_UNUSED(str);

    LOGS("ContentLayoutHandler::handleError");
    mTestResult = err;
    emit requestError();
}

void ContentLayoutHandler::highlightOn(QPointF &point)
{
    // --- Content layout ---
    
    if (mContainer) {
        if (mContainer->sceneBoundingRect().contains(point)) {
            LOGS("[[[Highlight signal in Content area]]]");
            if (!mContentHighlightActive) {
                HbFrameDrawer* highlightDrawer = new HbFrameDrawer(HIGHLIGHT_IMAGE_NAME,
                    HbFrameDrawer::NinePieces);
                HbFrameItem* highlightItem = new HbFrameItem(highlightDrawer);
                mContainer->setBackgroundItem(highlightItem);
                mContentHighlightActive = true; // must be done before setLabelsColor
                setLabelsColor();
                mContainer->update();
            }
        }
    }
    
    // --- Date layout ---
    
    // TODO Don't highlight if already highlighted
   
    if (mIconContainer) {
        if (mIconContainer->sceneBoundingRect().contains(point)) {
            LOGS("[[[Highlight signal in dateicon area]]]");
            if (!mDateHighlightActive) {
                HbFrameDrawer* highlightDrawer = new HbFrameDrawer(HIGHLIGHT_IMAGE_NAME,
                    HbFrameDrawer::NinePieces);
                HbFrameItem* highlightItem = new HbFrameItem(highlightDrawer);
                if (mHighlightLabel) {
                    mHighlightLabel->setBackgroundItem(highlightItem);
                }
                mDateHighlightActive = true; // must be done before setLabelsColor
                setLabelsColor();
                if (mHighlightLabel) {
                    mHighlightLabel->update();
                }
            }
        }
    }
}

void ContentLayoutHandler::highlightOff()
{
    // --- Content layout ---
    
    if (mContentHighlightActive) {
        QGraphicsItem* background1 = mContainer->backgroundItem();
        if (background1) {
            background1->hide();
        }
        mContentHighlightActive = false; // must be done before setLabelsColor
        setLabelsColor();
        mContainer->update();
    }
    
    // --- Date layout ---
    
    if (mDateIconFrameDrawer) {
        QGraphicsItem* background2 = mHighlightLabel->backgroundItem();
        if (background2) {
            background2->hide();
        }
        mDateHighlightActive = false; // must be done before setLabelsColor
        setLabelsColor();
        mHighlightLabel->update();
    }
}

//TEST FUNCTIONS

/*
 * ContentLayoutHandler::testId()
 */
int ContentLayoutHandler::testId()
{
    return mTestID;
}

/*
 * ContentLayoutHandler::setTestId()
 */
void ContentLayoutHandler::setTestId(int testID)
{
    mTestID = testID;
}

/*
 * ContentLayoutHandler::testResult()
 */
int ContentLayoutHandler::testResult()
{
    return mTestResult;
}

/*
 * ContentLayoutHandler::setTestResult
 */
void ContentLayoutHandler::setTestResult(int testResult)
{
    mTestResult = testResult;
}

/*
 * ContentLayoutHandler::setTimerForNextUpdate()
 */
void ContentLayoutHandler::setTimerForNextUpdate()
{
    if (mAgendaEntryList.count()) {
        int msecsToNextEvent = timeToNextCheck();
        if (msecsToNextEvent > 0) {
            mEventTimer->start(msecsToNextEvent);
        }
    }
}

QDateTime ContentLayoutHandler::currentDateTime()
{
#ifdef TEST
    return QDateTime(QDate(2010, 1, 1), QTime(9, 0, 0));
#else
    return QDateTime::currentDateTime();
#endif
}

void ContentLayoutHandler::setLabelText(HbLabel* label, QString text)
{
    if (label) {
        label->setPlainText(text);

        if (label == mUpperLabelShort) {
            if (mUpperLabelLong) {
                mUpperLabelLong->setPlainText(text);
            }
        }
    }
}

void ContentLayoutHandler::setLabelVisible(HbLabel* label, bool visible)
{
    // State transition table
    //  R = reminder label, L = long upper label, S = short upper label
    //  rows = current state (label visibility), columns = action
    //          [hide R]  [show R]  [hide S]  [show S]
    // 0 (slr)   0 (slr)   1 (slR)   0 (slr)   2 (sLr)
    // 1 (slR)   0 (slr)   1 (slR)   1 (slR)   5 (SlR)
    // 2 (sLr)   2 (sLr)   5 (SlR)   0 (slr)   2 (sLr)
    // 3 (sLR)   2 (sLr)   5 (SlR)   1 (slR)   5 (SlR)
    // 4 (Slr)   2 (sLr)   5 (SlR)   0 (slr)   2 (sLr)
    // 5 (SlR)   2 (sLr)   5 (SlR)   1 (slR)   5 (SlR)
    // 6 (SLr)   2 (sLr)   5 (SlR)   0 (slr)   2 (sLr)
    // 7 (SLR)   2 (sLr)   5 (SlR)   1 (slR)   5 (SlR)
    const int stateTransition[] =
        {0,1,0,2,
         0,1,1,5,
         2,5,0,2,
         2,5,1,5,
         2,5,0,2, 
         2,5,1,5,
         2,5,0,2,
         2,5,1,5};
    
    // Logic for showing correct upper label
    if (label == mUpperLabelShort || label == mReminderLabel) {
        // Make sure all three labels exists
        if (mUpperLabelShort && mUpperLabelLong && mReminderLabel) {
            int currentState = 0;
            currentState += (mReminderLabel->isVisible() ? 1 : 0);
            currentState += (mUpperLabelLong->isVisible()  ? 2 : 0);
            currentState += (mUpperLabelShort->isVisible() ? 4 : 0);

            int action = -1;
            if (label == mReminderLabel) {
                if (visible) {
                    action = 1;
                }
                else {
                    action = 0;
                }
            }
            else if (label == mUpperLabelShort) {
                if (visible) {
                    action = 3;
                }
                else {
                    action = 2;
                }
            }
            
            int newState = stateTransition[currentState * 4 + action];
            
            if (newState != currentState) {
                mReminderLabel->setVisible(newState & 1);
                mUpperLabelLong->setVisible(newState & 2);
                mUpperLabelShort->setVisible(newState & 4);
            }
        }
    }
    else { // Other labels
        if (label) {
            label->setVisible(visible);
        }
    }
}

bool ContentLayoutHandler::isAllDayEvent(AgendaEntry entry)
{
    if (entry.startTime().time() == QTime(0, 0)
        && entry.endTime().time() == QTime(0, 0)
        && entry.startTime().date() < entry.endTime().date()) {
        return true;
    }
    else {
        return false;
    }
}

bool ContentLayoutHandler::isOneDayAllDayEvent(AgendaEntry entry)
{
    if (isAllDayEvent(entry)
        && entry.startTime().date().addDays(1) == entry.endTime().date()) {
        return true;
    }
    else {
        return false;
    }
}

void ContentLayoutHandler::setLowerLabelOverlapping(int numberOfEvents)
{
    QString trString(hbTrId("txt_calendar_widget_v_dblist_val_l1_events"));
    // TODO: Remove the replacing of the parameter, once localized
    //       string is corrected.
    setLabelText(mLowerLabel, trString.replace("%Ln", "%L1").arg(numberOfEvents));
}

//from DateIconLayoutHandler
/*
 * ContentLayoutHandler::setCalendarIconContent()
 */
void ContentLayoutHandler::setCalendarIconContent()
{
    LOGS("ContentLayoutHandler::setCalendarIconContent");
    if (mNumberLabel) {
        mNumberLabel->setPlainText(QString::number(QDateTime::currentDateTime().date().day(), 10));
    }
    if (mMonthLabel) {
        mMonthLabel->setPlainText(QLocale::system().monthName(
            QDateTime::currentDateTime().date().month()));
    }
}

//End of file