--- /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