calendarui/views/dayview/src/calendayinfo.cpp
changeset 45 b6db4fd4947b
child 70 a5ed90760192
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/calendarui/views/dayview/src/calendayinfo.cpp	Mon Jun 28 15:22:02 2010 +0530
@@ -0,0 +1,2362 @@
+/*
+* Copyright (c) 2007-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:  Storage class for day and week views.
+*
+*/
+
+
+//  INCLUDES
+#include "calendayinfo.h"
+#include "calenagendautils.h"
+#include "calenconstants.h"
+#include "calendateutils.h"
+
+namespace
+{
+const int KUntimedDefaultSlot(16);
+}
+
+/*!
+ \brief CalenDayInfo::CalenDayInfo
+*/
+CalenDayInfo::CalenDayInfo(TSlotsInHour aSlotsInHour) :
+    iFirstUntimedSlot(KUntimedDefaultSlot), iSlotsInHour(aSlotsInHour)
+{
+    iSelectedSlot = KFSCalStartingHour * iSlotsInHour;
+    iSelectedRegion = KErrNotFound;
+    iSelectedColumn = 0;
+    iSelectedAlldayEvent = KErrNotFound;
+    iEarliestEndSlot = KErrNotFound;
+    iLastStartSlot = KErrNotFound;
+}
+
+/*!
+ \brief Destructor
+*/
+CalenDayInfo::~CalenDayInfo()
+{
+    for (int i = 0; i < iRegionList.count(); i++) {
+        iRegionList[i].Close();
+    }
+    iRegionList.clear();
+    iUntimedEvents.clear();//Close();
+    iTodoEvents.clear();//Close();
+    iAlldayEvents.clear();//Close();
+}
+
+/*!
+ \brief CalenDayInfo::Reset
+*/
+void CalenDayInfo::Reset()
+{
+    for (int i = 0; i < iRegionList.count(); i++) {
+        iRegionList[i].Close();
+    }
+    iRegionList.clear();//Reset();
+    iUntimedEvents.clear();//Reset();
+    iTodoEvents.clear();//Reset();
+    iAlldayEvents.clear();//Reset();
+
+    iUntimedSlotCount = 0;
+    iFirstUntimedSlot = KFSCalStartingHour * iSlotsInHour;
+
+    iSelectedSlot = KFSCalStartingHour * iSlotsInHour;
+    iSelectedRegion = KErrNotFound;
+    iSelectedColumn = 0;
+    iSelectedAlldayEvent = KErrNotFound;
+    iEarliestEndSlot = KErrNotFound;
+    iLastStartSlot = KErrNotFound;
+}
+
+/*!
+ \brief CalenDayInfo::InsertTimedEvent
+*/
+void CalenDayInfo::InsertTimedEvent(const SCalenApptInfo& aItemInfo)
+{
+
+    int startIndex = SlotIndexForStartTime(aItemInfo.iStartTime);
+    int endIndex = SlotIndexForEndTime(aItemInfo.iEndTime);
+    if (endIndex == startIndex) {
+        endIndex++;
+    }
+    if (iRegionList.count() > 0) {
+        // the timed events must be added in order
+        ASSERT( startIndex >= iLastStartSlot );
+    }
+
+    CalenTimedEventInfo info;
+    info.iStartSlot = startIndex;
+    info.iEndSlot = endIndex;
+    info.iId = aItemInfo.iId;
+    info.iStatus = aItemInfo.iStatus;
+    //    info.iReplicationStatus = aItemInfo.iReplicationStatus;
+    iLastStartSlot = startIndex;
+    if (iEarliestEndSlot == KErrNotFound || endIndex < iEarliestEndSlot) {
+        iEarliestEndSlot = endIndex;
+    }
+
+    bool added(false);
+    if (iRegionList.count() > 0) {
+        // Since events are added in order, the event either overlaps the
+        // last region or doesn't overlap any region at all
+        CalenTimeRegion& region = iRegionList[iRegionList.count() - 1];
+        if (region.Overlaps(info)) {
+            region.AddEvent(info);
+            added = true;
+        }
+    }
+
+    if (!added) {
+        // If it didn't overlap the last region, add a new region for this event.
+        iRegionList.append(CalenTimeRegion());
+        CalenTimeRegion& region = iRegionList[iRegionList.count() - 1];
+        region.AddEvent(info);
+
+        // Do rounding of the region ends
+        if (iRegionList.count() >= 2) {
+            // Check if the previous region end time can be rounded down
+            CalenTimeRegion& prevRegion = iRegionList[iRegionList.count() - 2];
+            int end = RoundHourDown(prevRegion.iEndSlot);
+            if (end <= region.iStartSlot) {
+                prevRegion.iEndSlot = end;
+            }
+            // Check if the start time of the new region can be rounded up
+            int start = RoundHourUp(region.iStartSlot);
+            if (start >= prevRegion.iEndSlot) {
+                region.iStartSlot = start;
+            }
+        }
+        else {
+            // No previous region, round the start time up
+            region.iStartSlot = RoundHourUp(region.iStartSlot);
+        }
+    }
+}
+
+/*!
+ \brief CalenDayInfo::InsertAlldayEvent
+*/
+void CalenDayInfo::InsertAlldayEvent(const SCalenApptInfo& aItemInfo)
+{
+    CalenTimedEventInfo info;
+    info.iStartSlot = 0;
+    info.iEndSlot = iSlotsInHour * KCalenHoursInDay + iUntimedSlotCount;
+    info.iId = aItemInfo.iId;
+    info.iStatus = aItemInfo.iStatus;
+    //    info.iReplicationStatus = aItemInfo.iReplicationStatus;
+
+    iAlldayEvents.append(info);
+}
+
+/*!
+ \brief CalenDayInfo::IsAllDayEvent
+*/
+bool CalenDayInfo::IsAlldayEvent(QDateTime aStart, QDateTime aEnd)
+{
+    bool isAllDay(false);
+
+    if (!CalenDateUtils::onSameDay(aStart, aEnd)) {
+        QDateTime startMidnight = CalenDateUtils::beginningOfDay(aStart);
+        QDateTime endMidnight = CalenDateUtils::beginningOfDay(aEnd);
+
+        if ((aStart == startMidnight) && (aEnd == endMidnight)) {
+            isAllDay = true;
+        }
+    }
+    return isAllDay;
+}
+
+/*!
+ \brief CalenDayInfo::IsAllDayEvent
+ Allday event is an event starting and ending at midnight,
+ with a duration of n*24h.
+*/
+/*
+bool CalenDayInfo::IsAlldayEvent(const CCalInstance& aInstance)
+{
+    bool isAllDay(false);
+
+    // If getting the start or end time fails, return false.
+    QDateTime start;
+    QDateTime end;
+    TRAPD( error,
+        {
+            start = aInstance.StartTimeL().TimeLocalL();
+            end = aInstance.EndTimeL().TimeLocalL();
+        });
+
+    if (error == KErrNone && !CalenDateUtils::onSameDay(start, end)) {
+        QDateTime startMidnight = CalenDateUtils::beginningOfDay(start);
+        QDateTime endMidnight = CalenDateUtils::beginningOfDay(end);
+
+        if ((start == startMidnight) && (end == endMidnight)) {
+            isAllDay = true;
+        }
+    }
+
+    if (error != KErrNone) {
+        CEikonEnv::Static()->HandleError(error);//codescanner::eikonenvstatic
+    }
+
+    return isAllDay;
+}
+*/
+
+/*!
+ \brief CalenDayInfo::AlldayCount
+*/
+int CalenDayInfo::AlldayCount()
+{
+    return iAlldayEvents.count();
+}
+
+/*!
+ \brief CalenDayInfo::TodoCount
+*/
+int CalenDayInfo::TodoCount()
+{
+    return iTodoEvents.count();
+}
+
+/*!
+ \brief CalenDayInfo::GetLocation
+*/
+void CalenDayInfo::GetLocation(
+    const SCalenApptInfo& aItemInfo,
+    int& aStartSlot,
+    int& aEndSlot,
+    int& aColumnIndex,
+    int& aColumns)
+{
+    int startIndex = SlotIndexForStartTime(aItemInfo.iStartTime);
+    int endIndex = SlotIndexForEndTime(aItemInfo.iEndTime);
+    if (endIndex == startIndex) {
+        endIndex++;
+    }
+
+    aStartSlot = startIndex;
+    aEndSlot = endIndex;
+
+    CalenSlotInterval interval;
+    interval.iStartSlot = startIndex;
+    interval.iEndSlot = endIndex;
+
+    bool found(false);
+    // Scan through all allday events and see if it matches any of them
+    for (int i = 0; i < iAlldayEvents.count() && !found; i++) {
+        if (aItemInfo.iId == iAlldayEvents[i].iId) {
+            aColumns = iAlldayEvents.count();
+            aColumnIndex = i;
+            found = true;
+        }
+    }
+
+    // Scan through all regions and see if the event overlaps any region.
+    // If it overlaps a region, the event must be in that region.
+    for (int i = 0; i < iRegionList.count() && !found; i++) {
+        CalenTimeRegion& region = iRegionList[i];
+        if (region.Overlaps(interval)) {
+            // Scan through all columns and look for the event
+            aColumns = region.iColumns.count();
+            for (int j = 0; j < aColumns && !found; j++) {
+                if (region.iColumns[j].ContainsEvent(aItemInfo.iId)) {
+                    aColumnIndex = j;
+                    found = true;
+                }
+            }
+            // the event should be in this region if it overlaps the region
+            ASSERT( found );
+        }
+    }
+    // the event should have been found somewhere
+    ASSERT( found );
+}
+
+/*!
+ \brief CalenDayInfo::GetSelectedSlot
+*/
+void CalenDayInfo::GetSelectedSlot(int& aSlot, int& aRegion, int& aColumnIndex, int& aColumns)
+{
+
+    aSlot = iSelectedSlot;
+    aColumnIndex = iSelectedColumn;
+    if (iSelectedRegion >= 0) {
+        aColumns = iRegionList[iSelectedRegion].iColumns.count();
+        aRegion = iSelectedRegion;
+    }
+    else {
+        aColumns = 0;
+        aRegion = KErrNotFound;
+    }
+}
+
+/*!
+ \brief CalenDayInfo::FindRegion
+*/
+int CalenDayInfo::FindRegion(const CalenSlotInterval& aInterval, int aDirection)
+{
+    // Setup the start and end region indices for iterating
+    int start, end;
+    if (aDirection > 0) {
+        start = 0;
+        end = iRegionList.count();
+    }
+    else {
+        start = iRegionList.count() - 1;
+        end = -1;
+    }
+
+    int region = KErrNotFound;
+
+    // Iterate over the regions, in the order given, looking for a region
+    // overlapping this interval
+    for (int i = start; i != end && region < 0; i += aDirection) {
+        if (iRegionList[i].Overlaps(aInterval)) {
+            region = i;
+        }
+    }
+    return region;
+}
+
+/*!
+ \brief Find an event within the given interval of the current column,
+ searching in the given direction.
+*/
+int CalenDayInfo::FindEvent(const CalenSlotInterval& aInterval, int aDirection)
+{
+    ASSERT( iSelectedRegion >= 0 && iSelectedRegion < iRegionList.count() );
+
+    CalenTimeRegion& region = iRegionList[iSelectedRegion];
+    int index = KErrNotFound;
+
+    if (iSelectedColumn < region.iColumns.count()) {
+        CalenTimeColumn& column = region.iColumns[iSelectedColumn];
+        int start, end;
+        if (aDirection > 0) {
+            start = 0;
+            end = column.iEventArray.count();
+        }
+        else {
+            start = column.iEventArray.count() - 1;
+            end = -1;
+        }
+
+        // Iterate over the events in the chosen column in the order given,
+        // looking for an event overlapping this interval
+        for (int i = start; i != end && index < 0; i += aDirection) {
+            if (column.iEventArray[i].Overlaps(aInterval)) {
+                index = i;
+            }
+        }
+    }
+    return index;
+}
+
+/*!
+ \brief CalenDayInfo::IsEventSelected
+*/
+bool CalenDayInfo::IsEventSelected() const
+{
+    bool selected(false);
+    if (iSelectedAlldayEvent >= 0) {
+        selected = true;
+    }
+    else
+        if (iSelectedRegion >= 0 && iSelectedColumn < iRegionList[iSelectedRegion].iColumns.count()
+            && iSelectedColumnEventIndex >= 0) {
+            selected = true;
+        }
+        else
+            if (IsExtraSlot(iSelectedSlot) && iSelectedSlot - iFirstUntimedSlot
+                >= iEmptyUntimedSlots) {
+                selected = true;
+            }
+            else {
+                // if no event is selected, the selection slot must be a hour starting slot or an extra slot
+                ASSERT( IsHourStartSlot( iSelectedSlot ) || IsExtraSlot( iSelectedSlot ) );
+                selected = false;
+            }
+    return selected;
+}
+
+/*!
+ \brief CalenDayInfo::IsMultipleEventsSelected
+*/
+bool CalenDayInfo::IsMultipleEventsSelected() const
+{
+    bool isMultiple(false);
+    if (iSelectedAlldayEvent >= 0) {
+        // If an allday event is selected, disregard the value of
+        // iSelectedSlot
+        isMultiple = false;
+    }
+    else
+        if (IsExtraSlot(iSelectedSlot)) {
+            int index = iSelectedSlot - iFirstUntimedSlot - iEmptyUntimedSlots;
+            isMultiple = (index == 0) && iTodoEvents.count() > 1;
+        }
+    return isMultiple;
+}
+
+/*!
+ \brief CalenDayInfo::IsAlldayEventSelected
+*/
+bool CalenDayInfo::IsAlldayEventSelected() const
+{
+    bool selected(false);
+    if (iSelectedAlldayEvent >= 0) {
+        ASSERT( iSelectedAlldayEvent < iAlldayEvents.count() );
+        selected = true;
+    }
+    return selected;
+}
+
+/*!
+ \brief CalenDayInfo::UntimedEvent
+*/
+TCalenInstanceId CalenDayInfo::UntimedEvent(int aIndex)
+{
+    TCalenInstanceId id;
+    if (iTodoEvents.count() > 0 && aIndex == 0) {
+        id = iTodoEvents[0];
+    }
+    else {
+        // If we have one or more todo items, the first slot is used for them
+        // (but not more than one slot)
+        if (iTodoEvents.count() > 0) {
+            aIndex--;
+        }
+
+        if (aIndex >= 0 && aIndex < iUntimedEvents.count()) {
+            id = iUntimedEvents[aIndex];
+        }
+        else {
+            // should not happen
+            User::Invariant();
+        }
+    }
+
+    return id;
+}
+
+/*!
+ \brief CalenDayInfo::AlldayEvent
+*/
+const CalenTimedEventInfo& CalenDayInfo::AlldayEvent(int aIndex)
+{
+    ASSERT( aIndex >= 0 && aIndex < iAlldayEvents.count() );
+    return iAlldayEvents[aIndex];
+}
+
+/*!
+ \brief CalenDayInfo::SelectedEvent
+*/
+TCalenInstanceId CalenDayInfo::SelectedEvent()
+{
+    TCalenInstanceId id;
+    if (iSelectedAlldayEvent >= 0) {
+        ASSERT( iSelectedAlldayEvent < iAlldayEvents.count() );
+        id = iAlldayEvents[iSelectedAlldayEvent].iId;
+    }
+    else
+        if (iSelectedRegion >= 0) {
+            ASSERT( iSelectedRegion < iRegionList.count() );
+
+            CalenTimeRegion& region = iRegionList[iSelectedRegion];
+            if (iSelectedColumn < region.iColumns.count()) {
+                CalenTimeColumn& column = region.iColumns[iSelectedColumn];
+                if (iSelectedColumnEventIndex >= 0 && iSelectedColumnEventIndex
+                    < column.iEventArray.count()) {
+                    CalenTimedEventInfo& info = column.iEventArray[iSelectedColumnEventIndex];
+                    id = info.iId;
+                }
+                else {
+                    // should not happen, no event selected in the column
+                    User::Invariant();
+                }
+            }
+            else {
+                // should not happen, the empty column was selected
+                User::Invariant();
+            }
+        }
+        else
+            if (IsExtraSlot(iSelectedSlot)) {
+                int index = iSelectedSlot - iFirstUntimedSlot - iEmptyUntimedSlots;
+                id = UntimedEvent(index);
+            }
+            else {
+                // should not happen
+                User::Invariant();
+            }
+
+    return id;
+}
+
+/*!
+ \brief CalenDayInfo::SelectEvent
+ Return KErrNotFound if not found, otherwise KErrNone
+*/
+int CalenDayInfo::SelectEvent(const TCalenInstanceId& aId)
+{
+    // Id can be of allday event, untimed event or other events
+
+    // Look for this event id in all event data structures and set
+    // the state accordingly if it was found
+
+    for (int i(0); i < iAlldayEvents.count(); i++) {
+        TCalenInstanceId id = iAlldayEvents[i].iId;
+        if (id == aId) {
+            iSelectedAlldayEvent = i;
+            iSelectedRegion = KErrNotFound;
+            return KErrNone;
+        }
+    }
+
+    for (int i(0); i < iTodoEvents.count(); i++) {
+        TCalenInstanceId id = iTodoEvents[i];
+        if (id == aId) {
+            iSelectedAlldayEvent = KErrNotFound;
+            iSelectedRegion = KErrNotFound;
+            iSelectedSlot = iFirstUntimedSlot + iEmptyUntimedSlots;
+            return KErrNone;
+        }
+    }
+
+    for (int i(0); i < iUntimedEvents.count(); i++) {
+        TCalenInstanceId id = iUntimedEvents[i];
+        if (id == aId) {
+            iSelectedAlldayEvent = KErrNotFound;
+            iSelectedRegion = KErrNotFound;
+            iSelectedSlot = iFirstUntimedSlot + iEmptyUntimedSlots + i;
+            if (iTodoEvents.count() > 0) {
+                iSelectedSlot++;
+            }
+            return KErrNone;
+        }
+    }
+
+    for (int i(0); i < iRegionList.count(); i++) {
+        CalenTimeRegion& region = iRegionList[i];
+        for (int col(0); col < region.iColumns.count(); col++) {
+            for (int ind(0); ind < region.iColumns[col].iEventArray.count(); ind++) {
+                if (region.iColumns[col].iEventArray[ind].iId == aId) {
+                    iSelectedAlldayEvent = KErrNotFound;
+                    iSelectedRegion = i;
+                    iSelectedColumn = col;
+                    iSelectedColumnEventIndex = ind;
+                    SetSelectionInEvent(EMoveDirectionDown);
+                    return KErrNone;
+                }
+            }
+        }
+    }
+
+    return KErrNotFound;
+}
+
+/*!
+ \brief CalenDayInfo::EnterRegionFromBelow
+*/
+void CalenDayInfo::EnterRegionFromBelow()
+{
+    ASSERT( iSelectedRegion >= 0 && iSelectedRegion < iRegionList.count() );
+    iSelectedColumn = KErrNotFound;
+    CalenTimeRegion& region = iRegionList[iSelectedRegion];
+    int latestEnd = region.iStartSlot;
+    // Look for the column with the latest end
+    for (int i = 0; i < region.iColumns.count(); i++) {
+        CalenTimeColumn& column = region.iColumns[i];
+        ASSERT( column.iEventArray.count()> 0 );
+        if (column.iEndSlot > latestEnd) {
+            // found a column with an event touching the end of the region
+            latestEnd = column.iEndSlot;
+            iSelectedColumn = i;
+            iSelectedColumnEventIndex = column.iEventArray.count() - 1;
+            SetSelectionInEvent(EMoveDirectionUp);
+        }
+    }
+    ASSERT( iSelectedColumn >= 0 );
+}
+
+/*!
+ \brief CalenDayInfo::EnterRegionFromAbove
+*/
+void CalenDayInfo::EnterRegionFromAbove()
+{
+    ASSERT( iSelectedRegion >= 0 && iSelectedRegion < iRegionList.count() );
+    iSelectedColumn = KErrNotFound;
+    CalenTimeRegion& region = iRegionList[iSelectedRegion];
+    int earliestStart = region.iEndSlot;
+    // Look for the column with the earliest start
+    for (int i = 0; i < region.iColumns.count(); i++) {
+        CalenTimeColumn& column = region.iColumns[i];
+        ASSERT( column.iEventArray.count()> 0 );
+        if (column.iStartSlot < earliestStart) {
+            // found a column with an event
+            earliestStart = column.iStartSlot;
+            iSelectedColumn = i;
+            iSelectedColumnEventIndex = 0;
+            SetSelectionInEvent(EMoveDirectionDown);
+        }
+    }
+    ASSERT( iSelectedColumn >= 0 );
+}
+
+/*!
+ \brief CalenDayInfo::NextFocusArea
+ Determines how large area to scan for new events/regions when moving in
+ the given direction.  If no events are found in this area, focus an
+ empty slot instead
+*/
+CalenSlotInterval CalenDayInfo::NextFocusArea(const CalenSlotInterval& aInterval, int aDirection)
+{
+    // NOTE: the comments are written with the visual layout in mind.
+    // upwards == up in the display, towards earlier times
+    // downwards == down in the display, towards later times
+    CalenSlotInterval target;
+    target.iEndSlot = 0;
+    target.iStartSlot = 0;
+
+    if (IsExtraSlot(aInterval.iStartSlot)) {
+        // Special case logic for moving within the extra slots.
+
+        // The index within the extra slot area
+        int extraIndex = aInterval.iStartSlot - iFirstUntimedSlot;
+        // Generally, move just one slot at a time in this area
+        int newIndex = extraIndex + aDirection;
+
+        if (newIndex < iEmptyUntimedSlots) {
+            // Moved upwards past the first slot, return
+            // the whole last hour before the untimed slot area
+            target.iEndSlot = iFirstUntimedSlot;
+            target.iStartSlot = target.iEndSlot - iSlotsInHour;
+        }
+        else
+            if (newIndex >= iUntimedSlotCount) {
+                // Moved downwards past the last untimed slot, return
+                // the whole first hour after the untimed slot area
+                target.iStartSlot = iFirstUntimedSlot + iUntimedSlotCount;
+                target.iEndSlot = target.iStartSlot + iSlotsInHour;
+            }
+            else {
+                // Moved normally within the untimed slot area
+                target.iStartSlot = aInterval.iStartSlot + aDirection;
+                target.iEndSlot = target.iStartSlot + 1;
+            }
+        return target;
+    }
+
+    // Helper interval for the whole untimed area
+    CalenSlotInterval untimedInterval;
+    untimedInterval.iStartSlot = iFirstUntimedSlot;
+    untimedInterval.iEndSlot = untimedInterval.iStartSlot + iUntimedSlotCount;
+
+    if (aDirection < 0) {
+        // Moving upwards
+        // Create a target interval of one hour upwards
+        target.iEndSlot = aInterval.iStartSlot;
+        target.iStartSlot = aInterval.iStartSlot - iSlotsInHour;
+
+        if (iUntimedSlotCount > 0 && untimedInterval.Overlaps(target)) {
+            // The target interval overlaps the untimed area
+            if (iUntimedSlotCount > iEmptyUntimedSlots) {
+                // Make the interval start at the last untimed slot
+                target.iStartSlot = untimedInterval.iEndSlot - 1;
+            }
+            else {
+                // Untimed area containing no actual events.
+                // Include one whole hour before the untimed area.
+                target.iStartSlot = iFirstUntimedSlot - iSlotsInHour;
+            }
+            return target;
+        }
+
+        // Round the start slot upwards to an even hour
+        target.iStartSlot = RoundHourUp(target.iStartSlot);
+    }
+    else {
+        // Moving downwards
+        // Create a target interval of one hour downwards
+        target.iStartSlot = aInterval.iEndSlot;
+        target.iEndSlot = aInterval.iEndSlot + iSlotsInHour;
+
+        if (iUntimedSlotCount > 0 && untimedInterval.Overlaps(target)) {
+            // The target interval overlaps the untimed area
+
+            // Assumption: There can't be any regions before the untimed area
+            if (iUntimedSlotCount > iEmptyUntimedSlots) {
+                // Make the interval end at the first untimed slot containing
+                // an event
+                target.iStartSlot = untimedInterval.iStartSlot + iEmptyUntimedSlots;
+                target.iEndSlot = target.iStartSlot + 1;
+            }
+            else {
+                // Untimed area containing no actual events.
+                // Include one whole hour after the untimed area.
+                target.iStartSlot = untimedInterval.iStartSlot + iUntimedSlotCount;
+                target.iEndSlot = target.iStartSlot + iSlotsInHour;
+            }
+            return target;
+        }
+
+        target.iEndSlot = RoundHourDown(target.iEndSlot);
+    }
+
+    return target;
+}
+
+/*!
+ \brief CalenDayInfo::NextEmptyFocusSlot
+ If no events are found in an empty area scanned, focus this slot instead
+*/
+int CalenDayInfo::NextEmptyFocusSlot(const CalenSlotInterval& aInterval, int aDirection)
+{
+    // NOTE: the comments are written with the visual layout in mind.
+    // upwards == up in the display, towards earlier times
+    // downwards == down in the display, towards later times
+
+    int target(0);
+
+    // If the next focus area logic says we should go to an untimed slot,
+    // skip the rest of this method
+    CalenSlotInterval testInterval = NextFocusArea(aInterval, aDirection);
+    if (IsExtraSlot(testInterval.iStartSlot)) {
+        target = testInterval.iStartSlot;
+    }
+    else {
+        if (aDirection < 0) {
+            // Moving upwards, target slot is the start of the area returned by
+            // NextFocusArea. This always is an even hour.
+            target = testInterval.iStartSlot;
+            ASSERT( IsHourStartSlot( target ) );
+        }
+        else {
+            target = testInterval.iStartSlot;
+            if (!IsHourStartSlot(target)) {
+                // If the interval doesn't start on an even hour, round downwards.
+                target = RoundHourDown(target);
+            }
+        }
+    }
+
+    return target;
+}
+
+/*!
+ \brief CalenDayInfo::EmptySelectionInterval
+ Create an interval representation of the current focus area
+*/
+CalenSlotInterval CalenDayInfo::EmptySelectionInterval()
+{
+    CalenSlotInterval target;
+    target.iEndSlot = 0;
+    target.iStartSlot = 0;
+
+    if (IsExtraSlot(iSelectedSlot)) {
+        // Return an interval consisting of one single slot
+        target.iStartSlot = iSelectedSlot;
+        target.iEndSlot = target.iStartSlot + 1;
+    }
+    else {
+        // Round the start slot to an even hour if it isn't.
+        // NOTE: This actually updates the selection state!
+        // Only call this method if no event actually is selected.
+        if (!IsHourStartSlot(iSelectedSlot)) {
+            iSelectedSlot = RoundHourUp(iSelectedSlot);
+        }
+        target.iStartSlot = iSelectedSlot;
+        target.iEndSlot = iSelectedSlot + iSlotsInHour;
+    }
+
+    return target;
+}
+
+/*!
+ \brief CalenDayInfo::SelectedInterval
+*/
+CalenSlotInterval CalenDayInfo::SelectedInterval()
+{
+    CalenSlotInterval selection;
+    selection.iStartSlot = selection.iEndSlot = 0;
+
+    if (iSelectedAlldayEvent >= 0) {
+        selection.iStartSlot = 0;
+        selection.iEndSlot = iSlotsInHour * KCalenHoursInDay + iUntimedSlotCount;
+    }
+    else
+        if (iSelectedRegion >= 0) {
+
+            CalenTimeRegion& region = iRegionList[iSelectedRegion];
+            if (iSelectedColumn < region.iColumns.count()) {
+                CalenTimeColumn& column = region.iColumns[iSelectedColumn];
+                if (iSelectedColumnEventIndex >= 0 && iSelectedColumnEventIndex
+                    < column.iEventArray.count()) {
+                    CalenTimedEventInfo& info = column.iEventArray[iSelectedColumnEventIndex];
+                    // gets only the CalenSlotInterval part from the CalenTimedEventInfo struct
+                    selection = info;
+                }
+            }
+        }
+
+    if (selection.IsEmpty()) {
+        // No selection was set in the cases above, no event is selected, thus
+        // use the empty area selection interval instead.
+        selection = EmptySelectionInterval();
+    }
+    return selection;
+}
+
+/*!
+ \brief CalenDayInfo::SetSelectionInRegion
+ Sets selection within a region
+*/
+bool CalenDayInfo::SetSelectionInRegion(int aRegion, int aColumn, int aSlot)
+{
+    bool selected(false);
+    StoreOrigSelection();
+    //validate given values
+    if (aRegion >= 0 && aRegion < iRegionList.count()) {//ASSERT( iSelectedRegion >= 0 && iSelectedRegion < iRegionList.count() );
+        CalenSlotInterval interval;
+        //TODO: round aSlot hour to full hour down????
+        interval.iStartSlot = aSlot;
+        interval.iEndSlot = interval.iStartSlot + 1;
+
+        CalenTimeRegion& region = iRegionList[aRegion];
+        if (region.Overlaps(interval)) {
+            if (aColumn >= 0 && aColumn < region.iColumns.count()) {
+                bool eventFound(false);
+                CalenTimeColumn& column = region.iColumns[aColumn];
+                if (column.Overlaps(interval)) {
+                    ASSERT( column.iEventArray.count()> 0 );
+                    for (int i = 0; i < column.iEventArray.count(); i++) {
+                        CalenTimedEventInfo& event = column.iEventArray[i];
+                        if (event.Overlaps(interval)) {
+                            iSelectedColumnEventIndex = i;
+
+                            iSelectedAlldayEvent = KErrNotFound;
+                            iSelectedRegion = aRegion;
+                            iSelectedColumn = aColumn;
+                            iSelectedSlot = aSlot;
+                            selected = true;
+                            eventFound = true;
+                            break;
+                        }
+
+                    }
+                }
+
+                if (!eventFound) {
+                    iSelectedAlldayEvent = KErrNotFound;
+                    iSelectedRegion = aRegion;
+                    iSelectedColumn = aColumn;
+                    iSelectedSlot = aSlot;
+                    iSelectedColumnEventIndex = KErrNotFound;
+                    selected = true;
+                }
+            }
+            else
+                if (aColumn == iRegionList[aRegion].iColumns.count()) {//empty selection on right selected
+                    iSelectedAlldayEvent = KErrNotFound;
+                    iSelectedRegion = aRegion;
+                    iSelectedColumn = aColumn;
+                    iSelectedSlot = aSlot;
+                    iSelectedColumnEventIndex = KErrNotFound;
+                    selected = true;
+                }
+        }
+    }
+
+    return selected;
+}
+
+/*!
+ \brief CalenDayInfo::MoveOutFromRegion
+*/
+void CalenDayInfo::MoveOutFromRegion(int aDirection)
+{
+    CalenTimeRegion& region = iRegionList[iSelectedRegion];
+    CalenSlotInterval targetInterval = NextFocusArea(region, aDirection);
+    int next = iSelectedRegion + aDirection;
+    // Check what the next region would be and if we should move into that
+    // or into an empty area
+    if (next >= 0 && next < iRegionList.count()) {
+        if (iRegionList[next].Overlaps(targetInterval)) {
+            iSelectedRegion = next;
+            if (aDirection < 0) {
+                EnterRegionFromBelow();
+            }
+            else {
+                EnterRegionFromAbove();
+            }
+        }
+        else {
+            iSelectedRegion = KErrNotFound;
+            iSelectedSlot = NextEmptyFocusSlot(region, aDirection);
+        }
+    }
+    else {
+        iSelectedRegion = KErrNotFound;
+        iSelectedSlot = NextEmptyFocusSlot(region, aDirection);
+    }
+}
+
+/*!
+ \brief CalenDayInfo::MoveInColumn
+*/
+void CalenDayInfo::MoveInColumn(int aDirection)
+{
+    CalenTimeRegion& region = iRegionList[iSelectedRegion];
+    if (iSelectedColumn < region.iColumns.count()) {
+        CalenTimeColumn& column = region.iColumns[iSelectedColumn];
+        if (iSelectedColumnEventIndex >= 0) {
+            // We currently have an event in a column selected
+            CalenSlotInterval curInterval = column.iEventArray[iSelectedColumnEventIndex];
+            CalenSlotInterval targetInterval = NextFocusArea(curInterval, aDirection);
+
+            int next = iSelectedColumnEventIndex + aDirection;
+            // Check if the target interval overlaps the next event in this column
+            if (next >= 0 && next < column.iEventArray.count()) {
+                if (column.iEventArray[next].Overlaps(targetInterval)) {
+                    iSelectedColumnEventIndex = next;
+                    SetSelectionInEvent(aDirection);
+                }
+                else {
+                    iSelectedColumnEventIndex = KErrNotFound;
+                    iSelectedSlot = NextEmptyFocusSlot(curInterval, aDirection);
+                }
+            }
+            else {
+                // There's no next event in this column, focus an empty area instead.
+                iSelectedColumnEventIndex = KErrNotFound;
+                iSelectedSlot = NextEmptyFocusSlot(curInterval, aDirection);
+            }
+        }
+        else {
+            // No event selected in the current column, find the target interval
+            // and check if there's any event there
+            CalenSlotInterval curInterval = EmptySelectionInterval();
+            CalenSlotInterval targetInterval = NextFocusArea(curInterval, aDirection);
+            iSelectedColumnEventIndex = FindEvent(targetInterval, aDirection);
+            if (iSelectedColumnEventIndex >= 0) {
+                SetSelectionInEvent(aDirection);
+            }
+            else {
+                iSelectedSlot = NextEmptyFocusSlot(curInterval, aDirection);
+            }
+        }
+
+        // If the new selection lies outside of the region, move out from the region.
+        CalenSlotInterval selection = SelectedInterval();
+        if (selection.iStartSlot < region.iStartSlot || selection.iEndSlot > region.iEndSlot) {
+            MoveOutFromRegion(aDirection);
+        }
+
+    }
+    else {
+        // The empty column was selected, just move up/down in that column
+        iSelectedSlot = NextEmptyFocusSlot(EmptySelectionInterval(), aDirection);
+
+        // When moving in the empty column, move out from the region only if
+        // the selection moved completely out from the region.
+        CalenSlotInterval selection = SelectedInterval();
+        if (!selection.Overlaps(region)) {
+            // The target selection is completely outside of the current region
+            int next = iSelectedRegion + aDirection;
+            if (next >= 0 && next < iRegionList.count() && iRegionList[next].Overlaps(selection)) {
+                // The target selection overlaps the next region, move into that,
+                // stay within the rightmost empty column
+                iSelectedRegion = next;
+                iSelectedColumn = iRegionList[next].iColumns.count();
+                iSelectedColumnEventIndex = KErrNotFound;
+            }
+            else {
+                // Move out from the current region
+                iSelectedRegion = KErrNotFound;
+                iSelectedColumn = KErrNotFound;
+                iSelectedColumnEventIndex = KErrNotFound;
+                // iSelectedSlot was update above, keep the same value
+            }
+        }
+
+    }
+
+}
+
+/*!
+ \brief CalenDayInfo::MoveInAlldayEvent
+*/
+bool CalenDayInfo::MoveInAlldayEvent(TScrollDirection aDirection)
+{
+    ASSERT( iSelectedAlldayEvent >= 0 );
+    bool moved(true);
+    switch (aDirection) {
+        case EScrollUp: {
+            // don't do anything
+        }
+            break;
+        case EScrollDown: {
+            // don't do anything
+        }
+            break;
+        case EScrollLeft: {
+            if (iSelectedAlldayEvent > 0) {
+                // there are more allday events to the left, select next
+                iSelectedAlldayEvent--;
+            }
+            else {
+                // move to previous day
+                moved = false;
+            }
+        }
+            break;
+        case EScrollRight: {
+            if (iSelectedAlldayEvent < iAlldayEvents.count() - 1) {
+                // there are more allday events to the right, select next
+                iSelectedAlldayEvent++;
+            }
+            else {
+                iSelectedAlldayEvent = KErrNotFound;
+                // find a region to select
+                if (iSelectedRegion >= 0) {
+                    // keep using the old selection state
+                }
+                else {
+                    // find a new suitable selection
+
+                    // if iSelectedSlot was moved to the empty untimed slot area,
+                    // move it down to a real slot
+
+                    if (iSelectedSlot >= iFirstUntimedSlot && iSelectedSlot < iFirstUntimedSlot
+                        + iEmptyUntimedSlots) {
+                        iSelectedSlot = iFirstUntimedSlot + iEmptyUntimedSlots;
+                    }
+
+                    CalenSlotInterval interval = EmptySelectionInterval();
+
+                    iSelectedRegion = FindRegion(interval, EMoveDirectionDown);
+                    int firstRegion = iSelectedRegion;
+                    bool found(false);
+                    // Iterate over all regions overlapping this interval, see
+                    // if any of them has events in this column
+                    while (iSelectedRegion >= 0 && iSelectedRegion < iRegionList.count()
+                        && iRegionList[iSelectedRegion].Overlaps(interval) && !found) {
+
+                        ASSERT( iSelectedRegion < iRegionList.count() );
+                        CalenTimeRegion& region = iRegionList[iSelectedRegion];
+
+                        ASSERT( region.iColumns.count()> 0 );
+                        iSelectedColumn = 0;
+                        CalenTimeColumn& column = region.iColumns[iSelectedColumn];
+
+                        iSelectedColumnEventIndex = FindEvent(interval, EMoveDirectionDown);
+                        if (iSelectedColumnEventIndex >= 0) {
+                            ASSERT( iSelectedColumnEventIndex < column.iEventArray.count() );
+                            // Keep using the old selected slot also here, just
+                            // update it to make sure it's within the actual event
+                            UpdateSelectionInEvent();
+                            found = true;
+                        }
+                        else {
+                            // Check the next region
+                            iSelectedRegion++;
+                        }
+                    }
+
+                    if (!found) {
+                        iSelectedRegion = firstRegion;
+                        iSelectedColumn = 0;
+                        iSelectedSlot = interval.iStartSlot;
+                    }
+
+                }
+            }
+        }
+            break;
+        default:
+            // Do nothing
+            break;
+    }
+
+    return moved;
+}
+
+/*!
+ \brief CalenDayInfo::MoveBetweenColumns
+*/
+void CalenDayInfo::MoveBetweenColumns(TScrollDirection aDirection)
+{
+    // Moving to another column
+    CalenSlotInterval selection = SelectedInterval();
+    if (IsEventSelected()) {
+        // If moving from an event, just use the selected
+        // slot pos within the event, not the whole event
+        // interval.
+        selection.iStartSlot = iSelectedSlot;
+        selection.iEndSlot = selection.iStartSlot + iSlotsInHour;
+        // Crop the selection to be within the event
+        selection.Intersect(SelectedInterval());
+    }
+
+    switch (aDirection) {
+        case EScrollLeft: {
+            iSelectedColumn--;
+            break;
+        }
+        case EScrollRight: {
+            iSelectedColumn++;
+            break;
+        }
+        default:
+            // Do nothing
+            break;
+    }
+
+    // First try finding events exactly in the selection area
+    iSelectedColumnEventIndex = FindEvent(selection, EMoveDirectionDown);
+    if (iSelectedColumnEventIndex >= 0) {
+        // An event was found in the given interval
+        // Keep using the old selection position, just update it to make
+        // sure it's within the event
+        UpdateSelectionInEvent();
+    }
+    else {
+        // No event found in the interval
+
+        // Expand the interval to even hour endings
+        selection.iStartSlot = RoundHourUp(selection.iStartSlot);
+        selection.iEndSlot = RoundHourDown(selection.iEndSlot);
+        CalenTimeRegion& region = iRegionList[iSelectedRegion];
+        // Crop the selection to the current region
+        selection.Intersect(region);
+
+        // Try again with the possibly larger interval
+        iSelectedColumnEventIndex = FindEvent(selection, EMoveDirectionDown);
+        if (iSelectedColumnEventIndex >= 0) {
+            // An event was found in the given interval
+            // Keep using the old selection position, just update it to make
+            // sure it's within the event
+            UpdateSelectionInEvent();
+        }
+        else {
+            // Still no events found. Select an empty interval if possible.
+
+            if (!IsHourStartSlot(selection.iStartSlot)) {
+                // The starting slot isn't a hour start slot. Rounding directly
+                // would move the interval outside of the region. See if we can
+                // round the starting slot down instead and still be within
+                // the region.
+                int start = RoundHourDown(selection.iStartSlot);
+                if (start + iSlotsInHour <= region.iEndSlot) {
+                    // Ok, an empty selection fits within the region.
+                    iSelectedSlot = start;
+                }
+            }
+            else
+                if (!IsHourStartSlot(selection.iEndSlot)) {
+                    // The ending slot isn't a hour start slot. Rounding directly
+                    // could move the interval outside of the region. See if we can
+                    // round the upwards instead and still be within
+                    // the region.
+                    int start = RoundHourUp(selection.iEndSlot - iSlotsInHour);
+                    if (start >= region.iStartSlot) {
+                        // Ok, an empty selection fits within the region.
+                        iSelectedSlot = start;
+                    }
+                }
+
+            // Make sure the selected interval really is a valid
+            // empty interval selection (this forces rounding if needed)
+            selection = EmptySelectionInterval();
+
+            // Check the final target interval once more.
+            iSelectedColumnEventIndex = FindEvent(selection, EMoveDirectionDown);
+            if (iSelectedColumnEventIndex >= 0) {
+                // An event was found in the given interval
+                UpdateSelectionInEvent();
+            }
+            else {
+                // No even found, use the empty selection
+
+                // If not within the region, move out
+                if (selection.iStartSlot < region.iStartSlot) {
+                    MoveOutFromRegion(EMoveDirectionUp);
+                }
+                else
+                    if (selection.iEndSlot > region.iEndSlot) {
+                        MoveOutFromRegion(EMoveDirectionDown);
+                    }
+            }
+        }
+
+    }
+
+}
+
+/*!
+ \brief CalenDayInfo::MoveInRegion
+*/
+bool CalenDayInfo::MoveInRegion(TScrollDirection aDirection)
+{
+    ASSERT( iSelectedAlldayEvent < 0 && iSelectedRegion >= 0 );
+    bool moved(true);
+    switch (aDirection) {
+        case EScrollUp: {
+            MoveInColumn(EMoveDirectionUp);
+        }
+            break;
+        case EScrollDown: {
+            MoveInColumn(EMoveDirectionDown);
+        }
+            break;
+
+        case EScrollLeft: {
+            ASSERT( iSelectedRegion < iRegionList.count() );
+            CalenTimeRegion& region = iRegionList[iSelectedRegion];
+            ASSERT( iSelectedColumn >= 0 && iSelectedColumn <= region.iColumns.count() );
+            if (iSelectedColumn == 0) {
+                if (iAlldayEvents.count() > 0) {
+                    // remember the last selection state
+                    iSelectedAlldayEvent = iAlldayEvents.count() - 1;
+                }
+                else {
+                    // move to previous day
+                    moved = false;
+                }
+            }
+            else {
+                MoveBetweenColumns(aDirection);
+            }
+        }
+            break;
+
+        case EScrollRight: {
+            ASSERT( iSelectedRegion < iRegionList.count() );
+            CalenTimeRegion& region = iRegionList[iSelectedRegion];
+            if (iSelectedColumn < region.iColumns.count() - 1) {
+                MoveBetweenColumns(aDirection);
+            }
+            else
+                if (iSelectedColumn == region.iColumns.count() - 1) {
+                    // In the last column, moving to the empty column
+                    iSelectedColumn++;
+                    iSelectedColumnEventIndex = KErrNotFound;
+
+                    // Round the selection to an empty interval
+                    EmptySelectionInterval();
+                }
+                else {
+                    // in the last, empty column
+                    // move to next day
+                    moved = false;
+                }
+        }
+            break;
+
+        default:
+            // Do nothing
+            break;
+    }
+
+    return moved;
+}
+
+/*!
+ \brief CalenDayInfo::MoveInEmptyArea
+*/
+bool CalenDayInfo::MoveInEmptyArea(TScrollDirection aDirection)
+{
+    ASSERT( iSelectedAlldayEvent < 0 && iSelectedRegion < 0 );
+    bool moved(true);
+    switch (aDirection) {
+        case EScrollUp: {
+            CalenSlotInterval curInterval = EmptySelectionInterval();
+            CalenSlotInterval targetInterval = NextFocusArea(curInterval, EMoveDirectionUp);
+            iSelectedRegion = FindRegion(targetInterval, EMoveDirectionUp);
+            if (iSelectedRegion >= 0) {
+                EnterRegionFromBelow();
+            }
+            else {
+                iSelectedSlot = NextEmptyFocusSlot(curInterval, EMoveDirectionUp);
+            }
+
+        }
+            break;
+        case EScrollDown: {
+            CalenSlotInterval curInterval = EmptySelectionInterval();
+            CalenSlotInterval targetInterval = NextFocusArea(curInterval, EMoveDirectionDown);
+            iSelectedRegion = FindRegion(targetInterval, EMoveDirectionDown);
+            if (iSelectedRegion >= 0) {
+                EnterRegionFromAbove();
+            }
+            else {
+                iSelectedSlot = NextEmptyFocusSlot(curInterval, EMoveDirectionDown);
+            }
+        }
+            break;
+        case EScrollLeft: {
+            if (iAlldayEvents.count() > 0) {
+                iSelectedAlldayEvent = iAlldayEvents.count() - 1;
+            }
+            else {
+                // move to previous day
+                moved = false;
+            }
+        }
+            break;
+        case EScrollRight: {
+            // move to next day
+            moved = false;
+        }
+            break;
+        default:
+            // Do nothing
+            break;
+    }
+
+    return moved;
+}
+
+/*!
+ \brief CalenDayInfo::MoveSelection
+*/
+bool CalenDayInfo::MoveSelection(TScrollDirection aDirection)
+{
+    bool moved(true);
+    StoreOrigSelection();
+    if (iSelectedAlldayEvent >= 0) {
+        // focused on an allday event
+        moved = MoveInAlldayEvent(aDirection);
+    }
+    else
+        if (iSelectedRegion >= 0) {
+            // focused on some event region
+            moved = MoveInRegion(aDirection);
+        }
+        else {
+            // focused on an empty, plain area
+            moved = MoveInEmptyArea(aDirection);
+        }
+    if (!ValidateSelection()) {
+        moved = false;
+    }
+    return moved;
+}
+
+/*!
+ \brief CalenDayInfo::MoveSelectionInEvent
+*/
+void CalenDayInfo::MoveSelectionInEvent(TScrollDirection aDirection)
+{
+    ASSERT( IsEventSelected() );
+
+    switch (aDirection) {
+        case EScrollUp: {
+            iSelectedSlot -= KFSCalSlotsInHour;
+        }
+            break;
+        case EScrollDown: {
+            iSelectedSlot += KFSCalSlotsInHour;
+        }
+            break;
+
+        default:
+            // Do nothing
+            break;
+    }
+
+    UpdateSelectionInEvent();
+
+    if (iSelectedAlldayEvent >= 0) {
+        // if an allday event is selected, invalidate
+        // the old region/event selection
+        iSelectedRegion = KErrNotFound;
+    }
+}
+
+/*!
+ \brief CalenDayInfo::UpdateSelectionInEvent
+*/
+void CalenDayInfo::UpdateSelectionInEvent()
+{
+    ASSERT( IsEventSelected() );
+    CalenSlotInterval event = SelectedInterval();
+
+    if (iSelectedSlot < event.iStartSlot) {
+        iSelectedSlot = event.iStartSlot;
+    }
+    if (iSelectedSlot >= event.iEndSlot) {
+        iSelectedSlot = event.iEndSlot - 1;
+    }
+
+}
+
+/*!
+ \brief CalenDayInfo::SetSelectionInEvent
+*/
+void CalenDayInfo::SetSelectionInEvent(int aDirection)
+{
+    CalenSlotInterval event = SelectedInterval();
+    if (aDirection > 0) {
+        iSelectedSlot = event.iStartSlot;
+    }
+    else {
+        iSelectedSlot = event.iEndSlot - 1;
+    }
+}
+
+/*!
+ \brief CalenDayInfo::StoreOrigSelection
+*/
+void CalenDayInfo::StoreOrigSelection()
+{
+    iOrigSelectedAlldayEvent = iSelectedAlldayEvent;
+    iOrigSelectedRegion = iSelectedRegion;
+    iOrigSelectedColumn = iSelectedColumn;
+    iOrigSelectedSlot = iSelectedSlot;
+    iOrigSelectedColumnEventIndex = iSelectedColumnEventIndex;
+}
+
+/*!
+ \brief CalenDayInfo::ValidateSelection
+*/
+bool CalenDayInfo::ValidateSelection()
+{
+    bool validate(true);
+    if (iSelectedSlot < 0 || iSelectedSlot >= iSlotsInHour * KCalenHoursInDay + iUntimedSlotCount) {
+        // tried to move outside of the day
+        // revert to the original coords
+        iSelectedAlldayEvent = iOrigSelectedAlldayEvent;
+        iSelectedRegion = iOrigSelectedRegion;
+        iSelectedColumn = iOrigSelectedColumn;
+        iSelectedSlot = iOrigSelectedSlot;
+        iSelectedColumnEventIndex = iOrigSelectedColumnEventIndex;
+        validate = false;
+    }
+    return validate;
+}
+
+/*!
+ \brief CalenDayInfo::SelectSlot
+*/
+void CalenDayInfo::SelectSlot(int aSlot)
+{
+    // Check for special case: empty untimed slot is selected
+    if (aSlot >= iFirstUntimedSlot && aSlot < iFirstUntimedSlot + iEmptyUntimedSlots) {
+        aSlot = iFirstUntimedSlot + iEmptyUntimedSlots;
+    }
+
+    iSelectedAlldayEvent = KErrNotFound;
+    iSelectedSlot = aSlot;
+    CalenSlotInterval interval = EmptySelectionInterval();
+    iSelectedRegion = FindRegion(interval, EMoveDirectionDown);
+    if (iSelectedRegion >= 0) {
+        CalenTimeRegion& region = iRegionList[iSelectedRegion];
+        int regCount(region.iColumns.count());
+        for (int i = 0; i < regCount; i++) {
+            CalenTimeColumn& column = region.iColumns[i];
+            int colCount(column.iEventArray.count());
+            for (int j = 0; j < colCount; j++) {
+                if (column.iEventArray[j].Overlaps(interval)) {
+                    iSelectedColumn = i;
+                    iSelectedColumnEventIndex = j;
+                    iSelectedSlot = aSlot;
+                    UpdateSelectionInEvent();
+                    return;
+                }
+            }
+        }
+        // Regions can have empty areas where there aren't any events,
+        // due to rounding of the ends of the region to whole hours.
+        // A region cannot, however, have a empty complete whole hour.
+        // Therefore, this cannot occur.
+        User::Invariant();
+    }
+}
+
+/*!
+ \brief CalenDayInfo::RegionList
+*/
+const QList<CalenTimeRegion>& CalenDayInfo::RegionList() const
+{
+    return iRegionList;
+}
+
+/*!
+ \brief CalenDayInfo::GetEventIntervals
+*/
+void CalenDayInfo::GetEventIntervals(QList<CalenEventInterval>& aArray) const
+{
+    int regCount(iRegionList.count());
+    for (int i = 0; i < regCount; i++) {
+        const CalenTimeRegion& region = iRegionList[i];
+        for (int j = 0; j < region.iIntervals.count(); j++) {
+            aArray.append(region.iIntervals[j]);
+        }
+    }
+}
+
+/*!
+ \brief CalenDayInfo::InsertUntimedEventL
+*/
+/*
+void CalenDayInfo::InsertUntimedEventL(const CCalInstance& aInstance)
+{
+    if (CalenAgendaUtils::IsTimedEntryL(aInstance.Entry().EntryTypeL())) {
+        // timed entry added as untimed event
+        User::Invariant();
+    }
+
+    TCalenInstanceId id = TCalenInstanceId::CreateL(aInstance);
+    InsertUntimedEvent(aInstance.Entry().EntryTypeL(), id);
+}
+*/
+
+/*!
+ \brief CalenDayInfo::InsertUntimedEvent
+*/
+void CalenDayInfo::InsertUntimedEvent(AgendaEntry::Type aType, const TCalenInstanceId& aId)
+{
+    if (aType == AgendaEntry::TypeTodo) {
+        iTodoEvents.append(aId);
+    }
+    else {
+        iUntimedEvents.append(aId);
+    }
+}
+
+/*!
+ \brief CalenDayInfo::SuggestedUntimedSlotPos
+*/
+int CalenDayInfo::SuggestedUntimedSlotPos()
+{
+    int slot = KFSCalStartingHour * iSlotsInHour;
+    if (iRegionList.count() > 0) {
+        CalenTimeRegion& region = iRegionList[0];
+        if (region.iStartSlot < slot) {
+            slot = RoundHourUp(region.iStartSlot);
+        }
+    }
+    return slot;
+}
+
+/*!
+ \brief CalenDayInfo::NeededUntimedSlotCount
+*/
+int CalenDayInfo::NeededUntimedSlotCount()
+{
+    int count = iUntimedEvents.count();
+    if (iTodoEvents.count() > 0) {
+        count++;
+    }
+    return count;
+}
+
+/*!
+ \brief CalenDayInfo::NeededUntimedSlotCount
+*/
+int CalenDayInfo::UpdateUntimedPos(int aSlot, int aUntimedCount)
+{
+    int newValue = NeededUntimedSlotCount();
+
+    // If this method is called many times, first undo the previous modifications
+    int regCount(iRegionList.count());
+    for (int i = 0; i < regCount; i++) {
+        iRegionList[i].AddOffset(-iUntimedSlotCount, iFirstUntimedSlot);
+    }
+    if (iSelectedSlot >= iFirstUntimedSlot) {
+        iSelectedSlot -= iUntimedSlotCount;
+    }
+
+    // Reset the untimed slot count
+    iUntimedSlotCount = 0;
+
+    // Get the default values
+    iFirstUntimedSlot = SuggestedUntimedSlotPos();
+    iUntimedSlotCount = newValue;
+
+    // If parameters were given, use them instead of the defaults
+    if (aSlot >= 0) {
+        ASSERT( aSlot <= iFirstUntimedSlot );
+        ASSERT( iUntimedSlotCount <= aUntimedCount );
+
+        iFirstUntimedSlot = aSlot;
+        iUntimedSlotCount = aUntimedCount;
+    }
+
+    iEmptyUntimedSlots = iUntimedSlotCount - NeededUntimedSlotCount();
+
+    // Add the new offset to all regions and update the selected slot
+    regCount = iRegionList.count();
+    for (int i = 0; i < regCount; i++) {
+        iRegionList[i].AddOffset(iUntimedSlotCount, iFirstUntimedSlot);
+    }
+    if (iSelectedSlot >= iFirstUntimedSlot) {
+        iSelectedSlot += iUntimedSlotCount;
+    }
+    int eventCount(iAlldayEvents.count());
+    for (int i = 0; i < eventCount; i++) {
+        iAlldayEvents[i].iEndSlot = iSlotsInHour * KCalenHoursInDay + iUntimedSlotCount;
+    }
+
+    // Do rounding of the end of the last region. This should be done when no
+    // more events will be added, but there's no real harm if new events are
+    // added after this.
+    if (iRegionList.count() > 0) {
+        // Round the end of the last region down
+        CalenTimeRegion& lastRegion = iRegionList[iRegionList.count() - 1];
+        lastRegion.iEndSlot = RoundHourDown(lastRegion.iEndSlot);
+    }
+
+    return iFirstUntimedSlot;
+}
+
+/*!
+ \brief Returns the index of the first occupied slot
+*/
+int CalenDayInfo::FirstOccupiedSlot()
+{
+
+    int firstNonemptySlot(KErrNotFound);
+
+    if (iUntimedSlotCount > 0) {
+        firstNonemptySlot = iFirstUntimedSlot;
+    }
+    else {
+        if (iRegionList.count() > 0) {
+            CalenTimeRegion& region = iRegionList[0];
+            firstNonemptySlot = region.iStartSlot;
+        }
+    }
+
+    return firstNonemptySlot;
+}
+
+/*!
+ \brief Returns the index of the last occupied slot
+*/
+int CalenDayInfo::LastOccupiedSlot()
+{
+    int lastNonemptySlot(KErrNotFound);
+    if (iRegionList.count() > 0) {
+        int lastIndex = iRegionList.count() - 1;
+        CalenTimeRegion& region = iRegionList[lastIndex];
+        lastNonemptySlot = region.iEndSlot;
+    }
+    else {
+        if (iUntimedSlotCount > 0) {
+            lastNonemptySlot = iFirstUntimedSlot + iUntimedSlotCount - 1;
+        }
+    }
+    return lastNonemptySlot;
+}
+
+/*!
+ \brief Returns the index of the earliest end slot
+*/
+int CalenDayInfo::EarliestEndSlot()
+{
+    int earliestEndSlot(KErrNotFound);
+    int untimedEventCount = iUntimedSlotCount - iEmptyUntimedSlots;
+    if (untimedEventCount > 0) {
+        earliestEndSlot = iFirstUntimedSlot + iEmptyUntimedSlots;
+        // add 1, since end slot is actually the one that is right after..
+        earliestEndSlot++;
+    }
+    else {
+        if (iRegionList.count() > 0) {
+            earliestEndSlot = iEarliestEndSlot + iEmptyUntimedSlots;
+        }
+    }
+    return earliestEndSlot;
+}
+
+/*!
+ \brief Returns the index of the last start slot
+*/
+int CalenDayInfo::LastStartSlot()
+{
+    int lastStartSlot(KErrNotFound);
+
+    if (iRegionList.count() > 0) {
+        lastStartSlot = iLastStartSlot + iUntimedSlotCount;
+    }
+    else {
+        if (iUntimedSlotCount - iEmptyUntimedSlots > 0) {
+            lastStartSlot = iFirstUntimedSlot + iUntimedSlotCount - 1;
+        }
+    }
+    return lastStartSlot;
+}
+
+/*!
+ \brief Returns the index of a slot where this event should start drawing,
+ based on the start time
+*/
+int CalenDayInfo::SlotIndexForStartTime(QDateTime aStartTime)
+{
+    // For example, 1/2 hour accuracy (iSlotsInHour == 2):
+    // Start drawing with the previus half hour: 9:15 -> 9:00, 9:59-> 9:30.
+
+
+    //    TDateTime dt = aStartTime.DateTime();
+    //    int minutes = dt.Minute();
+    //    int hours = dt.Hour();
+    int minutes = aStartTime.time().minute();//dt.Minute();
+    int hours = aStartTime.time().hour();//dt.Hour();
+
+    // calculate index based on the hour.
+    int slotIndex = hours * iSlotsInHour;
+
+    switch (iSlotsInHour) {
+        case EOne: // do nothing
+            break;
+        case ETwo: {
+            if (minutes >= 30) {
+                slotIndex++;
+            }
+        }
+            break;
+        case EThree: {
+            if (minutes >= 20) {
+                slotIndex++;
+            }
+            if (minutes >= 40) {
+                slotIndex++;
+            }
+        }
+            break;
+        case EFour: // followthrough
+        default: {
+            if (minutes >= 15) {
+                slotIndex++;
+            }
+            if (minutes >= 30) {
+                slotIndex++;
+            }
+            if (minutes >= 45) {
+                slotIndex++;
+            }
+
+        }
+    }
+
+    if (slotIndex >= iFirstUntimedSlot) {
+        slotIndex += iUntimedSlotCount;
+    }
+    return slotIndex;
+}
+
+/*!
+ \brief Returns the index of a slot where this event should end drawing,
+ based on the end time
+*/
+int CalenDayInfo::SlotIndexForEndTime(QDateTime aEndTime)
+{
+    // For example, 1/2 hour accuracy (iSlotsInHour == 2):
+    // End drawing with the next half hour: 9:10 -> 9:30, 9:59-> 10:00.
+
+    //    TDateTime dt = aEndTime.DateTime();
+    //    int minutes = dt.Minute();
+    //    int hours = dt.Hour();
+    int minutes = aEndTime.time().minute();
+    int hours = aEndTime.time().hour();
+
+    // calculate index based on the hour.
+    int slotIndex = hours * iSlotsInHour;
+
+    switch (iSlotsInHour) {
+        case EOne: {
+            if (minutes > 0) {
+                slotIndex++;
+            }
+        }
+
+            break;
+        case ETwo: {
+            if (minutes > 0) {
+                slotIndex++;
+            }
+            if (minutes > 30) {
+                slotIndex++;
+            }
+        }
+            break;
+        case EThree: {
+            if (minutes > 0) {
+                slotIndex++;
+            }
+            if (minutes > 20) {
+                slotIndex++;
+            }
+            if (minutes > 40) {
+                slotIndex++;
+            }
+        }
+            break;
+        case EFour: // followthrough
+        default: {
+            if (minutes > 0) {
+                slotIndex++;
+            }
+
+            if (minutes > 15) {
+                slotIndex++;
+            }
+            if (minutes > 30) {
+                slotIndex++;
+            }
+            if (minutes > 45) {
+                slotIndex++;
+            }
+
+        }
+    }
+
+    if (slotIndex >= iFirstUntimedSlot) {
+        slotIndex += iUntimedSlotCount;
+    }
+
+    return slotIndex;
+
+}
+
+/*!
+ \brief CalenDayInfo::IsHourStartSlot
+*/
+bool CalenDayInfo::IsHourStartSlot(const int& aSlotIndex) const
+{
+    bool isHourStartSlot(false);
+    if (IsExtraSlot(aSlotIndex)) {
+        isHourStartSlot = false;
+    }
+    else {
+        int hourIndex(aSlotIndex);
+        if (aSlotIndex >= iFirstUntimedSlot + iUntimedSlotCount) {
+            hourIndex -= iUntimedSlotCount;
+        }
+        int hour = hourIndex / iSlotsInHour;
+        int startIndOfHour = hour * iSlotsInHour;
+        if (hourIndex - startIndOfHour > 0) {
+            isHourStartSlot = false;
+        }
+        else {
+            isHourStartSlot = true;
+        }
+    }
+    return isHourStartSlot;
+
+}
+
+/*!
+ \brief CalenDayInfo::IsExtraSlot
+*/
+bool CalenDayInfo::IsExtraSlot(const int& aSlotIndex) const
+{
+    return (aSlotIndex >= iFirstUntimedSlot) && (aSlotIndex < (iFirstUntimedSlot
+        + iUntimedSlotCount));
+}
+
+/*!
+ \brief CalenDayInfo::HourFromSlotIndex
+ Returns the corresponding hour, or KErrNone if index is not hour slot index
+*/
+int CalenDayInfo::HourFromSlotIndex(const int& aSlotInd) const
+{
+
+    int hour(KErrNotFound);
+
+    if (!IsExtraSlot(aSlotInd)) {
+
+        if (aSlotInd < 0) {
+            // round downwards, to the previous starting hour
+            // e.g. iSlotsInHour = 2, aSlotInd = -1, hour = -1,
+            // which by SlotIndexFromHour corresponds to slot -2
+            hour = (aSlotInd - iSlotsInHour + 1) / iSlotsInHour;
+        }
+        else
+            if (aSlotInd < iFirstUntimedSlot) {
+                hour = aSlotInd / iSlotsInHour;
+            }
+            else
+                if (aSlotInd >= iFirstUntimedSlot + iUntimedSlotCount) {
+                    hour = (aSlotInd - iUntimedSlotCount) / iSlotsInHour;
+                }
+    }
+    return hour;
+}
+
+/*!
+ \brief CalenDayInfo::SlotIndexFromHour
+ Returns the corresponding hour, or KErrNone if index is not hour slot index
+*/
+int CalenDayInfo::SlotIndexFromHour(int aHour)
+{
+
+    int slotIndex(KErrNotFound);
+
+    if (aHour >= iFirstUntimedSlot / iSlotsInHour) {
+        slotIndex = aHour * iSlotsInHour + iUntimedSlotCount;
+    }
+    else {
+        slotIndex = aHour * iSlotsInHour;
+    }
+
+    return slotIndex;
+}
+
+/*!
+ \brief CalenDayInfo::RoundHourUp
+ Rounds the slot number up (towards earlier hours) to an even hour
+*/
+int CalenDayInfo::RoundHourUp(int aSlot)
+{
+    if (!IsExtraSlot(aSlot)) {
+        aSlot = SlotIndexFromHour(HourFromSlotIndex(aSlot));
+    }
+    return aSlot;
+}
+
+/*!
+ \brief CalenDayInfo::RoundHourDown
+ Rounds the slot number down (towards later hours) to an even hour
+*/
+int CalenDayInfo::RoundHourDown(int aSlot)
+{
+    if (!IsExtraSlot(aSlot) && !IsHourStartSlot(aSlot)) {
+        aSlot = SlotIndexFromHour(HourFromSlotIndex(aSlot + iSlotsInHour));
+    }
+    return aSlot;
+}
+
+/*!
+ \brief CalenSlotInterval::Overlaps
+*/
+bool CalenSlotInterval::Overlaps(const CalenSlotInterval& aInterval) const
+{
+    return aInterval.iStartSlot < iEndSlot && aInterval.iEndSlot > iStartSlot;
+}
+
+/*!
+ \brief CalenSlotInterval::AddOffset
+*/
+void CalenSlotInterval::AddOffset(int aOffset, int aPos)
+{
+    if (iStartSlot >= aPos) {
+        iStartSlot += aOffset;
+    }
+    if (iEndSlot >= aPos) {
+        iEndSlot += aOffset;
+    }
+}
+
+/*!
+ \brief CalenSlotInterval::Union
+*/
+void CalenSlotInterval::Union(const CalenSlotInterval& aInterval)
+{
+    if (!aInterval.IsEmpty()) {
+        iStartSlot = Min(iStartSlot, aInterval.iStartSlot);
+        iEndSlot = Max(iEndSlot, aInterval.iEndSlot);
+    }
+}
+
+/*!
+ \brief CalenSlotInterval::Adjacent
+*/
+bool CalenSlotInterval::Adjacent(const CalenSlotInterval& aInterval) const
+{
+    bool adjacent(false);
+    if (Overlaps(aInterval)) {
+        adjacent = true;
+    }
+    else {
+        adjacent = iStartSlot == aInterval.iEndSlot || iEndSlot == aInterval.iStartSlot;
+    }
+    return adjacent;
+}
+
+/*!
+ \brief CalenSlotInterval::IsEmpty
+*/
+bool CalenSlotInterval::IsEmpty() const
+{
+    return iStartSlot >= iEndSlot;
+}
+
+/*!
+ \brief CalenSlotInterval::Intersect
+*/
+void CalenSlotInterval::Intersect(const CalenSlotInterval& aInterval)
+{
+    if (aInterval.IsEmpty()) {
+        iEndSlot = iStartSlot;
+    }
+    else {
+        iStartSlot = Max(iStartSlot, aInterval.iStartSlot);
+        iEndSlot = Min(iEndSlot, aInterval.iEndSlot);
+    }
+}
+
+/*!
+ \brief CalenSlotInterval::Subtract
+*/
+void CalenSlotInterval::Subtract(const CalenSlotInterval& aInterval, CalenSlotInterval& aSecondPart)
+{
+    aSecondPart.iStartSlot = aSecondPart.iEndSlot = 0;
+    if (!aInterval.IsEmpty() && Overlaps(aInterval)) {
+        if (aInterval.iStartSlot <= iStartSlot) {
+            iStartSlot = aInterval.iEndSlot;
+        }
+        else
+            if (aInterval.iEndSlot >= iEndSlot) {
+                iEndSlot = aInterval.iStartSlot;
+            }
+            else {
+                aSecondPart.iStartSlot = aInterval.iEndSlot;
+                aSecondPart.iEndSlot = iEndSlot;
+                iEndSlot = aInterval.iStartSlot;
+            }
+    }
+}
+
+/*!
+ \brief CalenSlotInterval::operator>
+*/
+bool CalenSlotInterval::operator>(const CalenSlotInterval& aInterval) const
+{
+    return iStartSlot > aInterval.iStartSlot;
+}
+
+/*!
+ \brief CalenTimeColumn::Close
+*/
+void CalenTimeColumn::Close()
+{
+    iEventArray.clear();
+}
+
+/*!
+ \brief CalenTimeColumn::AddEvent
+*/
+void CalenTimeColumn::AddEvent(const CalenTimedEventInfo& aEvent)
+{
+    ASSERT( aEvent.iStartSlot < aEvent.iEndSlot );
+    ASSERT( CanFitEvent( aEvent ) );
+
+    if (iEventArray.count() == 0) {
+        iStartSlot = aEvent.iStartSlot;
+    }
+    iEndSlot = aEvent.iEndSlot;
+    iEventArray.append(aEvent);
+}
+
+/*!
+ \brief CalenTimeColumn::CanFitEvent
+*/
+bool CalenTimeColumn::CanFitEvent(const CalenTimedEventInfo& aEvent)
+{
+    return (aEvent.iStartSlot >= iEndSlot) || (iEventArray.count() == 0);
+}
+
+/*!
+ \brief CalenTimeColumn::ContainsEvent
+*/
+bool CalenTimeColumn::ContainsEvent(const TCalenInstanceId& aId)
+{
+    bool contains(false);
+    int eventCount(iEventArray.count());
+    for (int i = 0; i < eventCount && !contains; i++) {
+        if (iEventArray[i].iId == aId) {
+            contains = true;
+        }
+    }
+    return contains;
+}
+
+/*!
+ \brief CalenTimeColumn::AddOffset
+*/
+void CalenTimeColumn::AddOffset(int aOffset, int aPos)
+{
+    if (aOffset != 0) {
+        CalenSlotInterval::AddOffset(aOffset, aPos);
+        int eventCount(iEventArray.count());
+        for (int i = 0; i < eventCount; i++) {
+            iEventArray[i].AddOffset(aOffset, aPos);
+        }
+    }
+}
+
+/*!
+ \brief CalenTimeRegion::Close
+*/
+void CalenTimeRegion::Close()
+{
+    int colCount(iColumns.count());
+    for (int i = 0; i < colCount; i++) {
+        iColumns[i].Close();
+    }
+    iColumns.clear();
+    iIntervals.clear();
+}
+
+/*!
+ \brief CalenTimeRegion::Overlaps
+*/
+bool CalenTimeRegion::Overlaps(const CalenSlotInterval& aInterval) const
+{
+    // the base class implementation would be ok, but we might want the assertion here
+    ASSERT( iColumns.count()> 0 );
+    return CalenSlotInterval::Overlaps(aInterval);
+}
+
+/*!
+ \brief CalenTimeRegion::AddOffset
+*/
+void CalenTimeRegion::AddOffset(int aOffset, int aPos)
+{
+    if (aOffset != 0) {
+        CalenSlotInterval::AddOffset(aOffset, aPos);
+        int colCount(iColumns.count());
+        for (int i = 0; i < colCount; i++) {
+            iColumns[i].AddOffset(aOffset, aPos);
+        }
+        int intervalCount(iIntervals.count());
+        for (int i = 0; i < intervalCount; i++) {
+            iIntervals[i].AddOffset(aOffset, aPos);
+        }
+    }
+
+}
+
+/*!
+ \brief CalenTimeRegion::AddEvent
+*/
+void CalenTimeRegion::AddEvent(const CalenTimedEventInfo& aEvent)
+{
+    if (iColumns.count() == 0) {
+        // This is the first event added to this region
+        iStartSlot = aEvent.iStartSlot;
+        iEndSlot = aEvent.iEndSlot;
+    }
+    else {
+        // Check that the events actually are added in the correct order
+        ASSERT( aEvent.iStartSlot >= iStartSlot );
+        ASSERT( aEvent.iStartSlot < iEndSlot );
+
+        if (aEvent.iEndSlot > iEndSlot) {
+            iEndSlot = aEvent.iEndSlot;
+        }
+    }
+
+    AddInterval(aEvent);
+
+    // If the event fits in one of the existing columns, add it to that one
+    bool added(false);
+    int colCount(iColumns.count());
+    for (int i = 0; i < colCount && !added; i++) {
+        if (iColumns[i].CanFitEvent(aEvent)) {
+            iColumns[i].AddEvent(aEvent);
+            added = true;
+        }
+    }
+
+    if (!added) {
+        // otherwise create a new column for it
+        iColumns.append(CalenTimeColumn());
+        iColumns[iColumns.count() - 1].AddEvent(aEvent);
+    }
+}
+
+/*!
+ \brief CalenTimeRegion::AddInterval
+*/
+void CalenTimeRegion::AddInterval(const CalenTimedEventInfo& aEvent)
+{
+    /*
+     * Here are a few examples of different possible cases for this method.
+     * The first picture of each example shows the initial situation in the
+     * array, and the new event to be added. The transformations performed
+     * are shown with a few intermediate steps.
+     *
+     * nO = newOverlap, nE = newEvent
+     *
+     * Example one:
+     *
+     *               ------------------
+     *               | newEvent       |
+     * --------------------------------
+     * ... | lastOverlap | tail |
+     * --------------------------
+     *
+     *                   --------------
+     *                   | newEvent   |
+     * --------------------------------
+     * ... | lastOverlap | tail |
+     * --------------------------
+     *
+     *                   --------------
+     *                   | nO   | nE  |
+     * --------------------------------
+     * ... | lastOverlap | tail |
+     * --------------------------
+     *
+     * Result:
+     * --------------------------------
+     * ... | lastOverlap        | nE  |
+     * --------------------------------
+     *
+     *
+     * Example two:
+     *                          ------------------
+     *                          | newEvent       |
+     * -------------------------------------------
+     * ... | lastOverlap | tail      |
+     * -------------------------------
+     *
+     *                          ------------------
+     *                          | nO | newEvent  |
+     * -------------------------------------------
+     * ... | lastOverlap | tail      |
+     * -------------------------------
+     *
+     * Result:
+     * -------------------------------------------
+     * ... | lastOverlap | tail | nO | newEvent  |
+     * -------------------------------------------
+     *
+     *
+     * Example three:
+     *                          --------------
+     *                          | newEvent   |
+     * -----------------------------------------------------
+     * ... | lastOverlap | tail                            |
+     * -----------------------------------------------------
+     *
+     *                          --------------
+     *                          | newOverlap |
+     * -----------------------------------------------------
+     * ... | lastOverlap | tail                            |
+     * -----------------------------------------------------
+     *
+     *                          --------------
+     *                          | newOverlap |
+     * -----------------------------------------------------
+     * ... | lastOverlap | tail |            | tailPart2   |
+     * -----------------------------------------------------
+     *
+     * Result:
+     * -----------------------------------------------------
+     * ... | lastOverlap | tail | newOverlap | tailPart2   |
+     * -----------------------------------------------------
+     */
+
+    // Fill out the new event
+    CalenEventInterval newEvent;
+    newEvent.iStartSlot = aEvent.iStartSlot;
+    newEvent.iEndSlot = aEvent.iEndSlot;
+    newEvent.iStatus = aEvent.iStatus;
+    //    newEvent.iReplicationStatus = aEvent.iReplicationStatus;
+    newEvent.iOverlap = false;
+
+    // a pointer to the last interval which is an overlap interval
+    CalenEventInterval* lastOverlap = NULL;
+    // If nonempty, this is the last interval after the last overlap
+    CalenEventInterval tail;
+    tail.iStartSlot = tail.iEndSlot = 0;
+
+    // Find lastOverlap and tail.
+    if (iIntervals.count() >= 1) {
+        // lastInterval is a pointer to the last interval in the array
+        CalenEventInterval* lastInterval = &iIntervals[iIntervals.count() - 1];
+
+        // If this is an overlap interval, we haven't got any tail.
+        if (lastInterval->iOverlap) {
+            lastOverlap = lastInterval;
+        }
+        else {
+            tail = *lastInterval;
+        }
+
+        // If there's at least two intervals, and the last one wasn't an overlap,
+        // the second last one must be an overlap.
+        if (iIntervals.count() >= 2 && !lastOverlap) {
+            lastOverlap = &iIntervals[iIntervals.count() - 2];
+            ASSERT( lastOverlap->iOverlap );
+        }
+    }
+
+    // If we got a tail, remove it from the array (it will be readded
+    // at the end if needed)
+    if (!tail.IsEmpty()) {
+        iIntervals.removeAt(iIntervals.count() - 1);
+    }
+
+    CalenEventInterval empty;
+    if (lastOverlap) {
+        // Remove the part which already is marked as an overlap
+        // from the new event. The new event can't start before the
+        // last overlap starts since events are added in order, therefore
+        // the second subtraction result interval will remain empty.
+        newEvent.Subtract(*lastOverlap, empty);
+        ASSERT( empty.IsEmpty() );
+    }
+
+    // Create a new interval, representing the overlap between the old tail
+    // and the new event
+    CalenEventInterval newOverlap = newEvent;
+    newOverlap.iOverlap = true;
+    newOverlap.Intersect(tail);
+
+    CalenEventInterval tailPart2 = tail; // initialize iOverlap and iStatus from tail
+    // Remove the new overlap from the old tail, possibly creating two separate intervals.
+    tail.Subtract(newOverlap, tailPart2);
+
+    // If the subtraction only yielded one single interval, but it's
+    // after newOverlap, make tailPart2 contain that and make tail empty.
+    if (tail > newOverlap) {
+        tailPart2 = tail;
+        tail.iEndSlot = tail.iStartSlot;
+    }
+
+    // Remove the new overlap from the new event. Since we already removed the old
+    // overlap, this subtraction can't produce two intervals either.
+    newEvent.Subtract(newOverlap, empty);
+    ASSERT( empty.IsEmpty() );
+
+    // If the new overlap is adjacent to the old one, expand the old one
+    // and set the new overlap to be empty.
+    if (lastOverlap && newOverlap.Adjacent(*lastOverlap)) {
+        lastOverlap->Union(newOverlap);
+        newOverlap.iEndSlot = newOverlap.iStartSlot;
+    }
+
+    // Add all the new intervals, if they're non-empty.
+    if (!tail.IsEmpty()) {
+        iIntervals.append(tail);
+    }
+    if (!newOverlap.IsEmpty()) {
+        iIntervals.append(newOverlap);
+    }
+    if (!tailPart2.IsEmpty()) {
+        iIntervals.append(tailPart2);
+    }
+    if (!newEvent.IsEmpty()) {
+        iIntervals.append(newEvent);
+    }
+
+}
+
+// End of File