calendarui/views/dayview/src/calendayinfo.cpp
changeset 45 b6db4fd4947b
child 70 a5ed90760192
equal deleted inserted replaced
23:fd30d51f876b 45:b6db4fd4947b
       
     1 /*
       
     2 * Copyright (c) 2007-2010 Nokia Corporation and/or its subsidiary(-ies).
       
     3 * All rights reserved.
       
     4 * This component and the accompanying materials are made available
       
     5 * under the terms of "Eclipse Public License v1.0"
       
     6 * which accompanies this distribution, and is available
       
     7 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     8 *
       
     9 * Initial Contributors:
       
    10 * Nokia Corporation - initial contribution.
       
    11 *
       
    12 * Contributors:
       
    13 *
       
    14 * Description:  Storage class for day and week views.
       
    15 *
       
    16 */
       
    17 
       
    18 
       
    19 //  INCLUDES
       
    20 #include "calendayinfo.h"
       
    21 #include "calenagendautils.h"
       
    22 #include "calenconstants.h"
       
    23 #include "calendateutils.h"
       
    24 
       
    25 namespace
       
    26 {
       
    27 const int KUntimedDefaultSlot(16);
       
    28 }
       
    29 
       
    30 /*!
       
    31  \brief CalenDayInfo::CalenDayInfo
       
    32 */
       
    33 CalenDayInfo::CalenDayInfo(TSlotsInHour aSlotsInHour) :
       
    34     iFirstUntimedSlot(KUntimedDefaultSlot), iSlotsInHour(aSlotsInHour)
       
    35 {
       
    36     iSelectedSlot = KFSCalStartingHour * iSlotsInHour;
       
    37     iSelectedRegion = KErrNotFound;
       
    38     iSelectedColumn = 0;
       
    39     iSelectedAlldayEvent = KErrNotFound;
       
    40     iEarliestEndSlot = KErrNotFound;
       
    41     iLastStartSlot = KErrNotFound;
       
    42 }
       
    43 
       
    44 /*!
       
    45  \brief Destructor
       
    46 */
       
    47 CalenDayInfo::~CalenDayInfo()
       
    48 {
       
    49     for (int i = 0; i < iRegionList.count(); i++) {
       
    50         iRegionList[i].Close();
       
    51     }
       
    52     iRegionList.clear();
       
    53     iUntimedEvents.clear();//Close();
       
    54     iTodoEvents.clear();//Close();
       
    55     iAlldayEvents.clear();//Close();
       
    56 }
       
    57 
       
    58 /*!
       
    59  \brief CalenDayInfo::Reset
       
    60 */
       
    61 void CalenDayInfo::Reset()
       
    62 {
       
    63     for (int i = 0; i < iRegionList.count(); i++) {
       
    64         iRegionList[i].Close();
       
    65     }
       
    66     iRegionList.clear();//Reset();
       
    67     iUntimedEvents.clear();//Reset();
       
    68     iTodoEvents.clear();//Reset();
       
    69     iAlldayEvents.clear();//Reset();
       
    70 
       
    71     iUntimedSlotCount = 0;
       
    72     iFirstUntimedSlot = KFSCalStartingHour * iSlotsInHour;
       
    73 
       
    74     iSelectedSlot = KFSCalStartingHour * iSlotsInHour;
       
    75     iSelectedRegion = KErrNotFound;
       
    76     iSelectedColumn = 0;
       
    77     iSelectedAlldayEvent = KErrNotFound;
       
    78     iEarliestEndSlot = KErrNotFound;
       
    79     iLastStartSlot = KErrNotFound;
       
    80 }
       
    81 
       
    82 /*!
       
    83  \brief CalenDayInfo::InsertTimedEvent
       
    84 */
       
    85 void CalenDayInfo::InsertTimedEvent(const SCalenApptInfo& aItemInfo)
       
    86 {
       
    87 
       
    88     int startIndex = SlotIndexForStartTime(aItemInfo.iStartTime);
       
    89     int endIndex = SlotIndexForEndTime(aItemInfo.iEndTime);
       
    90     if (endIndex == startIndex) {
       
    91         endIndex++;
       
    92     }
       
    93     if (iRegionList.count() > 0) {
       
    94         // the timed events must be added in order
       
    95         ASSERT( startIndex >= iLastStartSlot );
       
    96     }
       
    97 
       
    98     CalenTimedEventInfo info;
       
    99     info.iStartSlot = startIndex;
       
   100     info.iEndSlot = endIndex;
       
   101     info.iId = aItemInfo.iId;
       
   102     info.iStatus = aItemInfo.iStatus;
       
   103     //    info.iReplicationStatus = aItemInfo.iReplicationStatus;
       
   104     iLastStartSlot = startIndex;
       
   105     if (iEarliestEndSlot == KErrNotFound || endIndex < iEarliestEndSlot) {
       
   106         iEarliestEndSlot = endIndex;
       
   107     }
       
   108 
       
   109     bool added(false);
       
   110     if (iRegionList.count() > 0) {
       
   111         // Since events are added in order, the event either overlaps the
       
   112         // last region or doesn't overlap any region at all
       
   113         CalenTimeRegion& region = iRegionList[iRegionList.count() - 1];
       
   114         if (region.Overlaps(info)) {
       
   115             region.AddEvent(info);
       
   116             added = true;
       
   117         }
       
   118     }
       
   119 
       
   120     if (!added) {
       
   121         // If it didn't overlap the last region, add a new region for this event.
       
   122         iRegionList.append(CalenTimeRegion());
       
   123         CalenTimeRegion& region = iRegionList[iRegionList.count() - 1];
       
   124         region.AddEvent(info);
       
   125 
       
   126         // Do rounding of the region ends
       
   127         if (iRegionList.count() >= 2) {
       
   128             // Check if the previous region end time can be rounded down
       
   129             CalenTimeRegion& prevRegion = iRegionList[iRegionList.count() - 2];
       
   130             int end = RoundHourDown(prevRegion.iEndSlot);
       
   131             if (end <= region.iStartSlot) {
       
   132                 prevRegion.iEndSlot = end;
       
   133             }
       
   134             // Check if the start time of the new region can be rounded up
       
   135             int start = RoundHourUp(region.iStartSlot);
       
   136             if (start >= prevRegion.iEndSlot) {
       
   137                 region.iStartSlot = start;
       
   138             }
       
   139         }
       
   140         else {
       
   141             // No previous region, round the start time up
       
   142             region.iStartSlot = RoundHourUp(region.iStartSlot);
       
   143         }
       
   144     }
       
   145 }
       
   146 
       
   147 /*!
       
   148  \brief CalenDayInfo::InsertAlldayEvent
       
   149 */
       
   150 void CalenDayInfo::InsertAlldayEvent(const SCalenApptInfo& aItemInfo)
       
   151 {
       
   152     CalenTimedEventInfo info;
       
   153     info.iStartSlot = 0;
       
   154     info.iEndSlot = iSlotsInHour * KCalenHoursInDay + iUntimedSlotCount;
       
   155     info.iId = aItemInfo.iId;
       
   156     info.iStatus = aItemInfo.iStatus;
       
   157     //    info.iReplicationStatus = aItemInfo.iReplicationStatus;
       
   158 
       
   159     iAlldayEvents.append(info);
       
   160 }
       
   161 
       
   162 /*!
       
   163  \brief CalenDayInfo::IsAllDayEvent
       
   164 */
       
   165 bool CalenDayInfo::IsAlldayEvent(QDateTime aStart, QDateTime aEnd)
       
   166 {
       
   167     bool isAllDay(false);
       
   168 
       
   169     if (!CalenDateUtils::onSameDay(aStart, aEnd)) {
       
   170         QDateTime startMidnight = CalenDateUtils::beginningOfDay(aStart);
       
   171         QDateTime endMidnight = CalenDateUtils::beginningOfDay(aEnd);
       
   172 
       
   173         if ((aStart == startMidnight) && (aEnd == endMidnight)) {
       
   174             isAllDay = true;
       
   175         }
       
   176     }
       
   177     return isAllDay;
       
   178 }
       
   179 
       
   180 /*!
       
   181  \brief CalenDayInfo::IsAllDayEvent
       
   182  Allday event is an event starting and ending at midnight,
       
   183  with a duration of n*24h.
       
   184 */
       
   185 /*
       
   186 bool CalenDayInfo::IsAlldayEvent(const CCalInstance& aInstance)
       
   187 {
       
   188     bool isAllDay(false);
       
   189 
       
   190     // If getting the start or end time fails, return false.
       
   191     QDateTime start;
       
   192     QDateTime end;
       
   193     TRAPD( error,
       
   194         {
       
   195             start = aInstance.StartTimeL().TimeLocalL();
       
   196             end = aInstance.EndTimeL().TimeLocalL();
       
   197         });
       
   198 
       
   199     if (error == KErrNone && !CalenDateUtils::onSameDay(start, end)) {
       
   200         QDateTime startMidnight = CalenDateUtils::beginningOfDay(start);
       
   201         QDateTime endMidnight = CalenDateUtils::beginningOfDay(end);
       
   202 
       
   203         if ((start == startMidnight) && (end == endMidnight)) {
       
   204             isAllDay = true;
       
   205         }
       
   206     }
       
   207 
       
   208     if (error != KErrNone) {
       
   209         CEikonEnv::Static()->HandleError(error);//codescanner::eikonenvstatic
       
   210     }
       
   211 
       
   212     return isAllDay;
       
   213 }
       
   214 */
       
   215 
       
   216 /*!
       
   217  \brief CalenDayInfo::AlldayCount
       
   218 */
       
   219 int CalenDayInfo::AlldayCount()
       
   220 {
       
   221     return iAlldayEvents.count();
       
   222 }
       
   223 
       
   224 /*!
       
   225  \brief CalenDayInfo::TodoCount
       
   226 */
       
   227 int CalenDayInfo::TodoCount()
       
   228 {
       
   229     return iTodoEvents.count();
       
   230 }
       
   231 
       
   232 /*!
       
   233  \brief CalenDayInfo::GetLocation
       
   234 */
       
   235 void CalenDayInfo::GetLocation(
       
   236     const SCalenApptInfo& aItemInfo,
       
   237     int& aStartSlot,
       
   238     int& aEndSlot,
       
   239     int& aColumnIndex,
       
   240     int& aColumns)
       
   241 {
       
   242     int startIndex = SlotIndexForStartTime(aItemInfo.iStartTime);
       
   243     int endIndex = SlotIndexForEndTime(aItemInfo.iEndTime);
       
   244     if (endIndex == startIndex) {
       
   245         endIndex++;
       
   246     }
       
   247 
       
   248     aStartSlot = startIndex;
       
   249     aEndSlot = endIndex;
       
   250 
       
   251     CalenSlotInterval interval;
       
   252     interval.iStartSlot = startIndex;
       
   253     interval.iEndSlot = endIndex;
       
   254 
       
   255     bool found(false);
       
   256     // Scan through all allday events and see if it matches any of them
       
   257     for (int i = 0; i < iAlldayEvents.count() && !found; i++) {
       
   258         if (aItemInfo.iId == iAlldayEvents[i].iId) {
       
   259             aColumns = iAlldayEvents.count();
       
   260             aColumnIndex = i;
       
   261             found = true;
       
   262         }
       
   263     }
       
   264 
       
   265     // Scan through all regions and see if the event overlaps any region.
       
   266     // If it overlaps a region, the event must be in that region.
       
   267     for (int i = 0; i < iRegionList.count() && !found; i++) {
       
   268         CalenTimeRegion& region = iRegionList[i];
       
   269         if (region.Overlaps(interval)) {
       
   270             // Scan through all columns and look for the event
       
   271             aColumns = region.iColumns.count();
       
   272             for (int j = 0; j < aColumns && !found; j++) {
       
   273                 if (region.iColumns[j].ContainsEvent(aItemInfo.iId)) {
       
   274                     aColumnIndex = j;
       
   275                     found = true;
       
   276                 }
       
   277             }
       
   278             // the event should be in this region if it overlaps the region
       
   279             ASSERT( found );
       
   280         }
       
   281     }
       
   282     // the event should have been found somewhere
       
   283     ASSERT( found );
       
   284 }
       
   285 
       
   286 /*!
       
   287  \brief CalenDayInfo::GetSelectedSlot
       
   288 */
       
   289 void CalenDayInfo::GetSelectedSlot(int& aSlot, int& aRegion, int& aColumnIndex, int& aColumns)
       
   290 {
       
   291 
       
   292     aSlot = iSelectedSlot;
       
   293     aColumnIndex = iSelectedColumn;
       
   294     if (iSelectedRegion >= 0) {
       
   295         aColumns = iRegionList[iSelectedRegion].iColumns.count();
       
   296         aRegion = iSelectedRegion;
       
   297     }
       
   298     else {
       
   299         aColumns = 0;
       
   300         aRegion = KErrNotFound;
       
   301     }
       
   302 }
       
   303 
       
   304 /*!
       
   305  \brief CalenDayInfo::FindRegion
       
   306 */
       
   307 int CalenDayInfo::FindRegion(const CalenSlotInterval& aInterval, int aDirection)
       
   308 {
       
   309     // Setup the start and end region indices for iterating
       
   310     int start, end;
       
   311     if (aDirection > 0) {
       
   312         start = 0;
       
   313         end = iRegionList.count();
       
   314     }
       
   315     else {
       
   316         start = iRegionList.count() - 1;
       
   317         end = -1;
       
   318     }
       
   319 
       
   320     int region = KErrNotFound;
       
   321 
       
   322     // Iterate over the regions, in the order given, looking for a region
       
   323     // overlapping this interval
       
   324     for (int i = start; i != end && region < 0; i += aDirection) {
       
   325         if (iRegionList[i].Overlaps(aInterval)) {
       
   326             region = i;
       
   327         }
       
   328     }
       
   329     return region;
       
   330 }
       
   331 
       
   332 /*!
       
   333  \brief Find an event within the given interval of the current column,
       
   334  searching in the given direction.
       
   335 */
       
   336 int CalenDayInfo::FindEvent(const CalenSlotInterval& aInterval, int aDirection)
       
   337 {
       
   338     ASSERT( iSelectedRegion >= 0 && iSelectedRegion < iRegionList.count() );
       
   339 
       
   340     CalenTimeRegion& region = iRegionList[iSelectedRegion];
       
   341     int index = KErrNotFound;
       
   342 
       
   343     if (iSelectedColumn < region.iColumns.count()) {
       
   344         CalenTimeColumn& column = region.iColumns[iSelectedColumn];
       
   345         int start, end;
       
   346         if (aDirection > 0) {
       
   347             start = 0;
       
   348             end = column.iEventArray.count();
       
   349         }
       
   350         else {
       
   351             start = column.iEventArray.count() - 1;
       
   352             end = -1;
       
   353         }
       
   354 
       
   355         // Iterate over the events in the chosen column in the order given,
       
   356         // looking for an event overlapping this interval
       
   357         for (int i = start; i != end && index < 0; i += aDirection) {
       
   358             if (column.iEventArray[i].Overlaps(aInterval)) {
       
   359                 index = i;
       
   360             }
       
   361         }
       
   362     }
       
   363     return index;
       
   364 }
       
   365 
       
   366 /*!
       
   367  \brief CalenDayInfo::IsEventSelected
       
   368 */
       
   369 bool CalenDayInfo::IsEventSelected() const
       
   370 {
       
   371     bool selected(false);
       
   372     if (iSelectedAlldayEvent >= 0) {
       
   373         selected = true;
       
   374     }
       
   375     else
       
   376         if (iSelectedRegion >= 0 && iSelectedColumn < iRegionList[iSelectedRegion].iColumns.count()
       
   377             && iSelectedColumnEventIndex >= 0) {
       
   378             selected = true;
       
   379         }
       
   380         else
       
   381             if (IsExtraSlot(iSelectedSlot) && iSelectedSlot - iFirstUntimedSlot
       
   382                 >= iEmptyUntimedSlots) {
       
   383                 selected = true;
       
   384             }
       
   385             else {
       
   386                 // if no event is selected, the selection slot must be a hour starting slot or an extra slot
       
   387                 ASSERT( IsHourStartSlot( iSelectedSlot ) || IsExtraSlot( iSelectedSlot ) );
       
   388                 selected = false;
       
   389             }
       
   390     return selected;
       
   391 }
       
   392 
       
   393 /*!
       
   394  \brief CalenDayInfo::IsMultipleEventsSelected
       
   395 */
       
   396 bool CalenDayInfo::IsMultipleEventsSelected() const
       
   397 {
       
   398     bool isMultiple(false);
       
   399     if (iSelectedAlldayEvent >= 0) {
       
   400         // If an allday event is selected, disregard the value of
       
   401         // iSelectedSlot
       
   402         isMultiple = false;
       
   403     }
       
   404     else
       
   405         if (IsExtraSlot(iSelectedSlot)) {
       
   406             int index = iSelectedSlot - iFirstUntimedSlot - iEmptyUntimedSlots;
       
   407             isMultiple = (index == 0) && iTodoEvents.count() > 1;
       
   408         }
       
   409     return isMultiple;
       
   410 }
       
   411 
       
   412 /*!
       
   413  \brief CalenDayInfo::IsAlldayEventSelected
       
   414 */
       
   415 bool CalenDayInfo::IsAlldayEventSelected() const
       
   416 {
       
   417     bool selected(false);
       
   418     if (iSelectedAlldayEvent >= 0) {
       
   419         ASSERT( iSelectedAlldayEvent < iAlldayEvents.count() );
       
   420         selected = true;
       
   421     }
       
   422     return selected;
       
   423 }
       
   424 
       
   425 /*!
       
   426  \brief CalenDayInfo::UntimedEvent
       
   427 */
       
   428 TCalenInstanceId CalenDayInfo::UntimedEvent(int aIndex)
       
   429 {
       
   430     TCalenInstanceId id;
       
   431     if (iTodoEvents.count() > 0 && aIndex == 0) {
       
   432         id = iTodoEvents[0];
       
   433     }
       
   434     else {
       
   435         // If we have one or more todo items, the first slot is used for them
       
   436         // (but not more than one slot)
       
   437         if (iTodoEvents.count() > 0) {
       
   438             aIndex--;
       
   439         }
       
   440 
       
   441         if (aIndex >= 0 && aIndex < iUntimedEvents.count()) {
       
   442             id = iUntimedEvents[aIndex];
       
   443         }
       
   444         else {
       
   445             // should not happen
       
   446             User::Invariant();
       
   447         }
       
   448     }
       
   449 
       
   450     return id;
       
   451 }
       
   452 
       
   453 /*!
       
   454  \brief CalenDayInfo::AlldayEvent
       
   455 */
       
   456 const CalenTimedEventInfo& CalenDayInfo::AlldayEvent(int aIndex)
       
   457 {
       
   458     ASSERT( aIndex >= 0 && aIndex < iAlldayEvents.count() );
       
   459     return iAlldayEvents[aIndex];
       
   460 }
       
   461 
       
   462 /*!
       
   463  \brief CalenDayInfo::SelectedEvent
       
   464 */
       
   465 TCalenInstanceId CalenDayInfo::SelectedEvent()
       
   466 {
       
   467     TCalenInstanceId id;
       
   468     if (iSelectedAlldayEvent >= 0) {
       
   469         ASSERT( iSelectedAlldayEvent < iAlldayEvents.count() );
       
   470         id = iAlldayEvents[iSelectedAlldayEvent].iId;
       
   471     }
       
   472     else
       
   473         if (iSelectedRegion >= 0) {
       
   474             ASSERT( iSelectedRegion < iRegionList.count() );
       
   475 
       
   476             CalenTimeRegion& region = iRegionList[iSelectedRegion];
       
   477             if (iSelectedColumn < region.iColumns.count()) {
       
   478                 CalenTimeColumn& column = region.iColumns[iSelectedColumn];
       
   479                 if (iSelectedColumnEventIndex >= 0 && iSelectedColumnEventIndex
       
   480                     < column.iEventArray.count()) {
       
   481                     CalenTimedEventInfo& info = column.iEventArray[iSelectedColumnEventIndex];
       
   482                     id = info.iId;
       
   483                 }
       
   484                 else {
       
   485                     // should not happen, no event selected in the column
       
   486                     User::Invariant();
       
   487                 }
       
   488             }
       
   489             else {
       
   490                 // should not happen, the empty column was selected
       
   491                 User::Invariant();
       
   492             }
       
   493         }
       
   494         else
       
   495             if (IsExtraSlot(iSelectedSlot)) {
       
   496                 int index = iSelectedSlot - iFirstUntimedSlot - iEmptyUntimedSlots;
       
   497                 id = UntimedEvent(index);
       
   498             }
       
   499             else {
       
   500                 // should not happen
       
   501                 User::Invariant();
       
   502             }
       
   503 
       
   504     return id;
       
   505 }
       
   506 
       
   507 /*!
       
   508  \brief CalenDayInfo::SelectEvent
       
   509  Return KErrNotFound if not found, otherwise KErrNone
       
   510 */
       
   511 int CalenDayInfo::SelectEvent(const TCalenInstanceId& aId)
       
   512 {
       
   513     // Id can be of allday event, untimed event or other events
       
   514 
       
   515     // Look for this event id in all event data structures and set
       
   516     // the state accordingly if it was found
       
   517 
       
   518     for (int i(0); i < iAlldayEvents.count(); i++) {
       
   519         TCalenInstanceId id = iAlldayEvents[i].iId;
       
   520         if (id == aId) {
       
   521             iSelectedAlldayEvent = i;
       
   522             iSelectedRegion = KErrNotFound;
       
   523             return KErrNone;
       
   524         }
       
   525     }
       
   526 
       
   527     for (int i(0); i < iTodoEvents.count(); i++) {
       
   528         TCalenInstanceId id = iTodoEvents[i];
       
   529         if (id == aId) {
       
   530             iSelectedAlldayEvent = KErrNotFound;
       
   531             iSelectedRegion = KErrNotFound;
       
   532             iSelectedSlot = iFirstUntimedSlot + iEmptyUntimedSlots;
       
   533             return KErrNone;
       
   534         }
       
   535     }
       
   536 
       
   537     for (int i(0); i < iUntimedEvents.count(); i++) {
       
   538         TCalenInstanceId id = iUntimedEvents[i];
       
   539         if (id == aId) {
       
   540             iSelectedAlldayEvent = KErrNotFound;
       
   541             iSelectedRegion = KErrNotFound;
       
   542             iSelectedSlot = iFirstUntimedSlot + iEmptyUntimedSlots + i;
       
   543             if (iTodoEvents.count() > 0) {
       
   544                 iSelectedSlot++;
       
   545             }
       
   546             return KErrNone;
       
   547         }
       
   548     }
       
   549 
       
   550     for (int i(0); i < iRegionList.count(); i++) {
       
   551         CalenTimeRegion& region = iRegionList[i];
       
   552         for (int col(0); col < region.iColumns.count(); col++) {
       
   553             for (int ind(0); ind < region.iColumns[col].iEventArray.count(); ind++) {
       
   554                 if (region.iColumns[col].iEventArray[ind].iId == aId) {
       
   555                     iSelectedAlldayEvent = KErrNotFound;
       
   556                     iSelectedRegion = i;
       
   557                     iSelectedColumn = col;
       
   558                     iSelectedColumnEventIndex = ind;
       
   559                     SetSelectionInEvent(EMoveDirectionDown);
       
   560                     return KErrNone;
       
   561                 }
       
   562             }
       
   563         }
       
   564     }
       
   565 
       
   566     return KErrNotFound;
       
   567 }
       
   568 
       
   569 /*!
       
   570  \brief CalenDayInfo::EnterRegionFromBelow
       
   571 */
       
   572 void CalenDayInfo::EnterRegionFromBelow()
       
   573 {
       
   574     ASSERT( iSelectedRegion >= 0 && iSelectedRegion < iRegionList.count() );
       
   575     iSelectedColumn = KErrNotFound;
       
   576     CalenTimeRegion& region = iRegionList[iSelectedRegion];
       
   577     int latestEnd = region.iStartSlot;
       
   578     // Look for the column with the latest end
       
   579     for (int i = 0; i < region.iColumns.count(); i++) {
       
   580         CalenTimeColumn& column = region.iColumns[i];
       
   581         ASSERT( column.iEventArray.count()> 0 );
       
   582         if (column.iEndSlot > latestEnd) {
       
   583             // found a column with an event touching the end of the region
       
   584             latestEnd = column.iEndSlot;
       
   585             iSelectedColumn = i;
       
   586             iSelectedColumnEventIndex = column.iEventArray.count() - 1;
       
   587             SetSelectionInEvent(EMoveDirectionUp);
       
   588         }
       
   589     }
       
   590     ASSERT( iSelectedColumn >= 0 );
       
   591 }
       
   592 
       
   593 /*!
       
   594  \brief CalenDayInfo::EnterRegionFromAbove
       
   595 */
       
   596 void CalenDayInfo::EnterRegionFromAbove()
       
   597 {
       
   598     ASSERT( iSelectedRegion >= 0 && iSelectedRegion < iRegionList.count() );
       
   599     iSelectedColumn = KErrNotFound;
       
   600     CalenTimeRegion& region = iRegionList[iSelectedRegion];
       
   601     int earliestStart = region.iEndSlot;
       
   602     // Look for the column with the earliest start
       
   603     for (int i = 0; i < region.iColumns.count(); i++) {
       
   604         CalenTimeColumn& column = region.iColumns[i];
       
   605         ASSERT( column.iEventArray.count()> 0 );
       
   606         if (column.iStartSlot < earliestStart) {
       
   607             // found a column with an event
       
   608             earliestStart = column.iStartSlot;
       
   609             iSelectedColumn = i;
       
   610             iSelectedColumnEventIndex = 0;
       
   611             SetSelectionInEvent(EMoveDirectionDown);
       
   612         }
       
   613     }
       
   614     ASSERT( iSelectedColumn >= 0 );
       
   615 }
       
   616 
       
   617 /*!
       
   618  \brief CalenDayInfo::NextFocusArea
       
   619  Determines how large area to scan for new events/regions when moving in
       
   620  the given direction.  If no events are found in this area, focus an
       
   621  empty slot instead
       
   622 */
       
   623 CalenSlotInterval CalenDayInfo::NextFocusArea(const CalenSlotInterval& aInterval, int aDirection)
       
   624 {
       
   625     // NOTE: the comments are written with the visual layout in mind.
       
   626     // upwards == up in the display, towards earlier times
       
   627     // downwards == down in the display, towards later times
       
   628     CalenSlotInterval target;
       
   629     target.iEndSlot = 0;
       
   630     target.iStartSlot = 0;
       
   631 
       
   632     if (IsExtraSlot(aInterval.iStartSlot)) {
       
   633         // Special case logic for moving within the extra slots.
       
   634 
       
   635         // The index within the extra slot area
       
   636         int extraIndex = aInterval.iStartSlot - iFirstUntimedSlot;
       
   637         // Generally, move just one slot at a time in this area
       
   638         int newIndex = extraIndex + aDirection;
       
   639 
       
   640         if (newIndex < iEmptyUntimedSlots) {
       
   641             // Moved upwards past the first slot, return
       
   642             // the whole last hour before the untimed slot area
       
   643             target.iEndSlot = iFirstUntimedSlot;
       
   644             target.iStartSlot = target.iEndSlot - iSlotsInHour;
       
   645         }
       
   646         else
       
   647             if (newIndex >= iUntimedSlotCount) {
       
   648                 // Moved downwards past the last untimed slot, return
       
   649                 // the whole first hour after the untimed slot area
       
   650                 target.iStartSlot = iFirstUntimedSlot + iUntimedSlotCount;
       
   651                 target.iEndSlot = target.iStartSlot + iSlotsInHour;
       
   652             }
       
   653             else {
       
   654                 // Moved normally within the untimed slot area
       
   655                 target.iStartSlot = aInterval.iStartSlot + aDirection;
       
   656                 target.iEndSlot = target.iStartSlot + 1;
       
   657             }
       
   658         return target;
       
   659     }
       
   660 
       
   661     // Helper interval for the whole untimed area
       
   662     CalenSlotInterval untimedInterval;
       
   663     untimedInterval.iStartSlot = iFirstUntimedSlot;
       
   664     untimedInterval.iEndSlot = untimedInterval.iStartSlot + iUntimedSlotCount;
       
   665 
       
   666     if (aDirection < 0) {
       
   667         // Moving upwards
       
   668         // Create a target interval of one hour upwards
       
   669         target.iEndSlot = aInterval.iStartSlot;
       
   670         target.iStartSlot = aInterval.iStartSlot - iSlotsInHour;
       
   671 
       
   672         if (iUntimedSlotCount > 0 && untimedInterval.Overlaps(target)) {
       
   673             // The target interval overlaps the untimed area
       
   674             if (iUntimedSlotCount > iEmptyUntimedSlots) {
       
   675                 // Make the interval start at the last untimed slot
       
   676                 target.iStartSlot = untimedInterval.iEndSlot - 1;
       
   677             }
       
   678             else {
       
   679                 // Untimed area containing no actual events.
       
   680                 // Include one whole hour before the untimed area.
       
   681                 target.iStartSlot = iFirstUntimedSlot - iSlotsInHour;
       
   682             }
       
   683             return target;
       
   684         }
       
   685 
       
   686         // Round the start slot upwards to an even hour
       
   687         target.iStartSlot = RoundHourUp(target.iStartSlot);
       
   688     }
       
   689     else {
       
   690         // Moving downwards
       
   691         // Create a target interval of one hour downwards
       
   692         target.iStartSlot = aInterval.iEndSlot;
       
   693         target.iEndSlot = aInterval.iEndSlot + iSlotsInHour;
       
   694 
       
   695         if (iUntimedSlotCount > 0 && untimedInterval.Overlaps(target)) {
       
   696             // The target interval overlaps the untimed area
       
   697 
       
   698             // Assumption: There can't be any regions before the untimed area
       
   699             if (iUntimedSlotCount > iEmptyUntimedSlots) {
       
   700                 // Make the interval end at the first untimed slot containing
       
   701                 // an event
       
   702                 target.iStartSlot = untimedInterval.iStartSlot + iEmptyUntimedSlots;
       
   703                 target.iEndSlot = target.iStartSlot + 1;
       
   704             }
       
   705             else {
       
   706                 // Untimed area containing no actual events.
       
   707                 // Include one whole hour after the untimed area.
       
   708                 target.iStartSlot = untimedInterval.iStartSlot + iUntimedSlotCount;
       
   709                 target.iEndSlot = target.iStartSlot + iSlotsInHour;
       
   710             }
       
   711             return target;
       
   712         }
       
   713 
       
   714         target.iEndSlot = RoundHourDown(target.iEndSlot);
       
   715     }
       
   716 
       
   717     return target;
       
   718 }
       
   719 
       
   720 /*!
       
   721  \brief CalenDayInfo::NextEmptyFocusSlot
       
   722  If no events are found in an empty area scanned, focus this slot instead
       
   723 */
       
   724 int CalenDayInfo::NextEmptyFocusSlot(const CalenSlotInterval& aInterval, int aDirection)
       
   725 {
       
   726     // NOTE: the comments are written with the visual layout in mind.
       
   727     // upwards == up in the display, towards earlier times
       
   728     // downwards == down in the display, towards later times
       
   729 
       
   730     int target(0);
       
   731 
       
   732     // If the next focus area logic says we should go to an untimed slot,
       
   733     // skip the rest of this method
       
   734     CalenSlotInterval testInterval = NextFocusArea(aInterval, aDirection);
       
   735     if (IsExtraSlot(testInterval.iStartSlot)) {
       
   736         target = testInterval.iStartSlot;
       
   737     }
       
   738     else {
       
   739         if (aDirection < 0) {
       
   740             // Moving upwards, target slot is the start of the area returned by
       
   741             // NextFocusArea. This always is an even hour.
       
   742             target = testInterval.iStartSlot;
       
   743             ASSERT( IsHourStartSlot( target ) );
       
   744         }
       
   745         else {
       
   746             target = testInterval.iStartSlot;
       
   747             if (!IsHourStartSlot(target)) {
       
   748                 // If the interval doesn't start on an even hour, round downwards.
       
   749                 target = RoundHourDown(target);
       
   750             }
       
   751         }
       
   752     }
       
   753 
       
   754     return target;
       
   755 }
       
   756 
       
   757 /*!
       
   758  \brief CalenDayInfo::EmptySelectionInterval
       
   759  Create an interval representation of the current focus area
       
   760 */
       
   761 CalenSlotInterval CalenDayInfo::EmptySelectionInterval()
       
   762 {
       
   763     CalenSlotInterval target;
       
   764     target.iEndSlot = 0;
       
   765     target.iStartSlot = 0;
       
   766 
       
   767     if (IsExtraSlot(iSelectedSlot)) {
       
   768         // Return an interval consisting of one single slot
       
   769         target.iStartSlot = iSelectedSlot;
       
   770         target.iEndSlot = target.iStartSlot + 1;
       
   771     }
       
   772     else {
       
   773         // Round the start slot to an even hour if it isn't.
       
   774         // NOTE: This actually updates the selection state!
       
   775         // Only call this method if no event actually is selected.
       
   776         if (!IsHourStartSlot(iSelectedSlot)) {
       
   777             iSelectedSlot = RoundHourUp(iSelectedSlot);
       
   778         }
       
   779         target.iStartSlot = iSelectedSlot;
       
   780         target.iEndSlot = iSelectedSlot + iSlotsInHour;
       
   781     }
       
   782 
       
   783     return target;
       
   784 }
       
   785 
       
   786 /*!
       
   787  \brief CalenDayInfo::SelectedInterval
       
   788 */
       
   789 CalenSlotInterval CalenDayInfo::SelectedInterval()
       
   790 {
       
   791     CalenSlotInterval selection;
       
   792     selection.iStartSlot = selection.iEndSlot = 0;
       
   793 
       
   794     if (iSelectedAlldayEvent >= 0) {
       
   795         selection.iStartSlot = 0;
       
   796         selection.iEndSlot = iSlotsInHour * KCalenHoursInDay + iUntimedSlotCount;
       
   797     }
       
   798     else
       
   799         if (iSelectedRegion >= 0) {
       
   800 
       
   801             CalenTimeRegion& region = iRegionList[iSelectedRegion];
       
   802             if (iSelectedColumn < region.iColumns.count()) {
       
   803                 CalenTimeColumn& column = region.iColumns[iSelectedColumn];
       
   804                 if (iSelectedColumnEventIndex >= 0 && iSelectedColumnEventIndex
       
   805                     < column.iEventArray.count()) {
       
   806                     CalenTimedEventInfo& info = column.iEventArray[iSelectedColumnEventIndex];
       
   807                     // gets only the CalenSlotInterval part from the CalenTimedEventInfo struct
       
   808                     selection = info;
       
   809                 }
       
   810             }
       
   811         }
       
   812 
       
   813     if (selection.IsEmpty()) {
       
   814         // No selection was set in the cases above, no event is selected, thus
       
   815         // use the empty area selection interval instead.
       
   816         selection = EmptySelectionInterval();
       
   817     }
       
   818     return selection;
       
   819 }
       
   820 
       
   821 /*!
       
   822  \brief CalenDayInfo::SetSelectionInRegion
       
   823  Sets selection within a region
       
   824 */
       
   825 bool CalenDayInfo::SetSelectionInRegion(int aRegion, int aColumn, int aSlot)
       
   826 {
       
   827     bool selected(false);
       
   828     StoreOrigSelection();
       
   829     //validate given values
       
   830     if (aRegion >= 0 && aRegion < iRegionList.count()) {//ASSERT( iSelectedRegion >= 0 && iSelectedRegion < iRegionList.count() );
       
   831         CalenSlotInterval interval;
       
   832         //TODO: round aSlot hour to full hour down????
       
   833         interval.iStartSlot = aSlot;
       
   834         interval.iEndSlot = interval.iStartSlot + 1;
       
   835 
       
   836         CalenTimeRegion& region = iRegionList[aRegion];
       
   837         if (region.Overlaps(interval)) {
       
   838             if (aColumn >= 0 && aColumn < region.iColumns.count()) {
       
   839                 bool eventFound(false);
       
   840                 CalenTimeColumn& column = region.iColumns[aColumn];
       
   841                 if (column.Overlaps(interval)) {
       
   842                     ASSERT( column.iEventArray.count()> 0 );
       
   843                     for (int i = 0; i < column.iEventArray.count(); i++) {
       
   844                         CalenTimedEventInfo& event = column.iEventArray[i];
       
   845                         if (event.Overlaps(interval)) {
       
   846                             iSelectedColumnEventIndex = i;
       
   847 
       
   848                             iSelectedAlldayEvent = KErrNotFound;
       
   849                             iSelectedRegion = aRegion;
       
   850                             iSelectedColumn = aColumn;
       
   851                             iSelectedSlot = aSlot;
       
   852                             selected = true;
       
   853                             eventFound = true;
       
   854                             break;
       
   855                         }
       
   856 
       
   857                     }
       
   858                 }
       
   859 
       
   860                 if (!eventFound) {
       
   861                     iSelectedAlldayEvent = KErrNotFound;
       
   862                     iSelectedRegion = aRegion;
       
   863                     iSelectedColumn = aColumn;
       
   864                     iSelectedSlot = aSlot;
       
   865                     iSelectedColumnEventIndex = KErrNotFound;
       
   866                     selected = true;
       
   867                 }
       
   868             }
       
   869             else
       
   870                 if (aColumn == iRegionList[aRegion].iColumns.count()) {//empty selection on right selected
       
   871                     iSelectedAlldayEvent = KErrNotFound;
       
   872                     iSelectedRegion = aRegion;
       
   873                     iSelectedColumn = aColumn;
       
   874                     iSelectedSlot = aSlot;
       
   875                     iSelectedColumnEventIndex = KErrNotFound;
       
   876                     selected = true;
       
   877                 }
       
   878         }
       
   879     }
       
   880 
       
   881     return selected;
       
   882 }
       
   883 
       
   884 /*!
       
   885  \brief CalenDayInfo::MoveOutFromRegion
       
   886 */
       
   887 void CalenDayInfo::MoveOutFromRegion(int aDirection)
       
   888 {
       
   889     CalenTimeRegion& region = iRegionList[iSelectedRegion];
       
   890     CalenSlotInterval targetInterval = NextFocusArea(region, aDirection);
       
   891     int next = iSelectedRegion + aDirection;
       
   892     // Check what the next region would be and if we should move into that
       
   893     // or into an empty area
       
   894     if (next >= 0 && next < iRegionList.count()) {
       
   895         if (iRegionList[next].Overlaps(targetInterval)) {
       
   896             iSelectedRegion = next;
       
   897             if (aDirection < 0) {
       
   898                 EnterRegionFromBelow();
       
   899             }
       
   900             else {
       
   901                 EnterRegionFromAbove();
       
   902             }
       
   903         }
       
   904         else {
       
   905             iSelectedRegion = KErrNotFound;
       
   906             iSelectedSlot = NextEmptyFocusSlot(region, aDirection);
       
   907         }
       
   908     }
       
   909     else {
       
   910         iSelectedRegion = KErrNotFound;
       
   911         iSelectedSlot = NextEmptyFocusSlot(region, aDirection);
       
   912     }
       
   913 }
       
   914 
       
   915 /*!
       
   916  \brief CalenDayInfo::MoveInColumn
       
   917 */
       
   918 void CalenDayInfo::MoveInColumn(int aDirection)
       
   919 {
       
   920     CalenTimeRegion& region = iRegionList[iSelectedRegion];
       
   921     if (iSelectedColumn < region.iColumns.count()) {
       
   922         CalenTimeColumn& column = region.iColumns[iSelectedColumn];
       
   923         if (iSelectedColumnEventIndex >= 0) {
       
   924             // We currently have an event in a column selected
       
   925             CalenSlotInterval curInterval = column.iEventArray[iSelectedColumnEventIndex];
       
   926             CalenSlotInterval targetInterval = NextFocusArea(curInterval, aDirection);
       
   927 
       
   928             int next = iSelectedColumnEventIndex + aDirection;
       
   929             // Check if the target interval overlaps the next event in this column
       
   930             if (next >= 0 && next < column.iEventArray.count()) {
       
   931                 if (column.iEventArray[next].Overlaps(targetInterval)) {
       
   932                     iSelectedColumnEventIndex = next;
       
   933                     SetSelectionInEvent(aDirection);
       
   934                 }
       
   935                 else {
       
   936                     iSelectedColumnEventIndex = KErrNotFound;
       
   937                     iSelectedSlot = NextEmptyFocusSlot(curInterval, aDirection);
       
   938                 }
       
   939             }
       
   940             else {
       
   941                 // There's no next event in this column, focus an empty area instead.
       
   942                 iSelectedColumnEventIndex = KErrNotFound;
       
   943                 iSelectedSlot = NextEmptyFocusSlot(curInterval, aDirection);
       
   944             }
       
   945         }
       
   946         else {
       
   947             // No event selected in the current column, find the target interval
       
   948             // and check if there's any event there
       
   949             CalenSlotInterval curInterval = EmptySelectionInterval();
       
   950             CalenSlotInterval targetInterval = NextFocusArea(curInterval, aDirection);
       
   951             iSelectedColumnEventIndex = FindEvent(targetInterval, aDirection);
       
   952             if (iSelectedColumnEventIndex >= 0) {
       
   953                 SetSelectionInEvent(aDirection);
       
   954             }
       
   955             else {
       
   956                 iSelectedSlot = NextEmptyFocusSlot(curInterval, aDirection);
       
   957             }
       
   958         }
       
   959 
       
   960         // If the new selection lies outside of the region, move out from the region.
       
   961         CalenSlotInterval selection = SelectedInterval();
       
   962         if (selection.iStartSlot < region.iStartSlot || selection.iEndSlot > region.iEndSlot) {
       
   963             MoveOutFromRegion(aDirection);
       
   964         }
       
   965 
       
   966     }
       
   967     else {
       
   968         // The empty column was selected, just move up/down in that column
       
   969         iSelectedSlot = NextEmptyFocusSlot(EmptySelectionInterval(), aDirection);
       
   970 
       
   971         // When moving in the empty column, move out from the region only if
       
   972         // the selection moved completely out from the region.
       
   973         CalenSlotInterval selection = SelectedInterval();
       
   974         if (!selection.Overlaps(region)) {
       
   975             // The target selection is completely outside of the current region
       
   976             int next = iSelectedRegion + aDirection;
       
   977             if (next >= 0 && next < iRegionList.count() && iRegionList[next].Overlaps(selection)) {
       
   978                 // The target selection overlaps the next region, move into that,
       
   979                 // stay within the rightmost empty column
       
   980                 iSelectedRegion = next;
       
   981                 iSelectedColumn = iRegionList[next].iColumns.count();
       
   982                 iSelectedColumnEventIndex = KErrNotFound;
       
   983             }
       
   984             else {
       
   985                 // Move out from the current region
       
   986                 iSelectedRegion = KErrNotFound;
       
   987                 iSelectedColumn = KErrNotFound;
       
   988                 iSelectedColumnEventIndex = KErrNotFound;
       
   989                 // iSelectedSlot was update above, keep the same value
       
   990             }
       
   991         }
       
   992 
       
   993     }
       
   994 
       
   995 }
       
   996 
       
   997 /*!
       
   998  \brief CalenDayInfo::MoveInAlldayEvent
       
   999 */
       
  1000 bool CalenDayInfo::MoveInAlldayEvent(TScrollDirection aDirection)
       
  1001 {
       
  1002     ASSERT( iSelectedAlldayEvent >= 0 );
       
  1003     bool moved(true);
       
  1004     switch (aDirection) {
       
  1005         case EScrollUp: {
       
  1006             // don't do anything
       
  1007         }
       
  1008             break;
       
  1009         case EScrollDown: {
       
  1010             // don't do anything
       
  1011         }
       
  1012             break;
       
  1013         case EScrollLeft: {
       
  1014             if (iSelectedAlldayEvent > 0) {
       
  1015                 // there are more allday events to the left, select next
       
  1016                 iSelectedAlldayEvent--;
       
  1017             }
       
  1018             else {
       
  1019                 // move to previous day
       
  1020                 moved = false;
       
  1021             }
       
  1022         }
       
  1023             break;
       
  1024         case EScrollRight: {
       
  1025             if (iSelectedAlldayEvent < iAlldayEvents.count() - 1) {
       
  1026                 // there are more allday events to the right, select next
       
  1027                 iSelectedAlldayEvent++;
       
  1028             }
       
  1029             else {
       
  1030                 iSelectedAlldayEvent = KErrNotFound;
       
  1031                 // find a region to select
       
  1032                 if (iSelectedRegion >= 0) {
       
  1033                     // keep using the old selection state
       
  1034                 }
       
  1035                 else {
       
  1036                     // find a new suitable selection
       
  1037 
       
  1038                     // if iSelectedSlot was moved to the empty untimed slot area,
       
  1039                     // move it down to a real slot
       
  1040 
       
  1041                     if (iSelectedSlot >= iFirstUntimedSlot && iSelectedSlot < iFirstUntimedSlot
       
  1042                         + iEmptyUntimedSlots) {
       
  1043                         iSelectedSlot = iFirstUntimedSlot + iEmptyUntimedSlots;
       
  1044                     }
       
  1045 
       
  1046                     CalenSlotInterval interval = EmptySelectionInterval();
       
  1047 
       
  1048                     iSelectedRegion = FindRegion(interval, EMoveDirectionDown);
       
  1049                     int firstRegion = iSelectedRegion;
       
  1050                     bool found(false);
       
  1051                     // Iterate over all regions overlapping this interval, see
       
  1052                     // if any of them has events in this column
       
  1053                     while (iSelectedRegion >= 0 && iSelectedRegion < iRegionList.count()
       
  1054                         && iRegionList[iSelectedRegion].Overlaps(interval) && !found) {
       
  1055 
       
  1056                         ASSERT( iSelectedRegion < iRegionList.count() );
       
  1057                         CalenTimeRegion& region = iRegionList[iSelectedRegion];
       
  1058 
       
  1059                         ASSERT( region.iColumns.count()> 0 );
       
  1060                         iSelectedColumn = 0;
       
  1061                         CalenTimeColumn& column = region.iColumns[iSelectedColumn];
       
  1062 
       
  1063                         iSelectedColumnEventIndex = FindEvent(interval, EMoveDirectionDown);
       
  1064                         if (iSelectedColumnEventIndex >= 0) {
       
  1065                             ASSERT( iSelectedColumnEventIndex < column.iEventArray.count() );
       
  1066                             // Keep using the old selected slot also here, just
       
  1067                             // update it to make sure it's within the actual event
       
  1068                             UpdateSelectionInEvent();
       
  1069                             found = true;
       
  1070                         }
       
  1071                         else {
       
  1072                             // Check the next region
       
  1073                             iSelectedRegion++;
       
  1074                         }
       
  1075                     }
       
  1076 
       
  1077                     if (!found) {
       
  1078                         iSelectedRegion = firstRegion;
       
  1079                         iSelectedColumn = 0;
       
  1080                         iSelectedSlot = interval.iStartSlot;
       
  1081                     }
       
  1082 
       
  1083                 }
       
  1084             }
       
  1085         }
       
  1086             break;
       
  1087         default:
       
  1088             // Do nothing
       
  1089             break;
       
  1090     }
       
  1091 
       
  1092     return moved;
       
  1093 }
       
  1094 
       
  1095 /*!
       
  1096  \brief CalenDayInfo::MoveBetweenColumns
       
  1097 */
       
  1098 void CalenDayInfo::MoveBetweenColumns(TScrollDirection aDirection)
       
  1099 {
       
  1100     // Moving to another column
       
  1101     CalenSlotInterval selection = SelectedInterval();
       
  1102     if (IsEventSelected()) {
       
  1103         // If moving from an event, just use the selected
       
  1104         // slot pos within the event, not the whole event
       
  1105         // interval.
       
  1106         selection.iStartSlot = iSelectedSlot;
       
  1107         selection.iEndSlot = selection.iStartSlot + iSlotsInHour;
       
  1108         // Crop the selection to be within the event
       
  1109         selection.Intersect(SelectedInterval());
       
  1110     }
       
  1111 
       
  1112     switch (aDirection) {
       
  1113         case EScrollLeft: {
       
  1114             iSelectedColumn--;
       
  1115             break;
       
  1116         }
       
  1117         case EScrollRight: {
       
  1118             iSelectedColumn++;
       
  1119             break;
       
  1120         }
       
  1121         default:
       
  1122             // Do nothing
       
  1123             break;
       
  1124     }
       
  1125 
       
  1126     // First try finding events exactly in the selection area
       
  1127     iSelectedColumnEventIndex = FindEvent(selection, EMoveDirectionDown);
       
  1128     if (iSelectedColumnEventIndex >= 0) {
       
  1129         // An event was found in the given interval
       
  1130         // Keep using the old selection position, just update it to make
       
  1131         // sure it's within the event
       
  1132         UpdateSelectionInEvent();
       
  1133     }
       
  1134     else {
       
  1135         // No event found in the interval
       
  1136 
       
  1137         // Expand the interval to even hour endings
       
  1138         selection.iStartSlot = RoundHourUp(selection.iStartSlot);
       
  1139         selection.iEndSlot = RoundHourDown(selection.iEndSlot);
       
  1140         CalenTimeRegion& region = iRegionList[iSelectedRegion];
       
  1141         // Crop the selection to the current region
       
  1142         selection.Intersect(region);
       
  1143 
       
  1144         // Try again with the possibly larger interval
       
  1145         iSelectedColumnEventIndex = FindEvent(selection, EMoveDirectionDown);
       
  1146         if (iSelectedColumnEventIndex >= 0) {
       
  1147             // An event was found in the given interval
       
  1148             // Keep using the old selection position, just update it to make
       
  1149             // sure it's within the event
       
  1150             UpdateSelectionInEvent();
       
  1151         }
       
  1152         else {
       
  1153             // Still no events found. Select an empty interval if possible.
       
  1154 
       
  1155             if (!IsHourStartSlot(selection.iStartSlot)) {
       
  1156                 // The starting slot isn't a hour start slot. Rounding directly
       
  1157                 // would move the interval outside of the region. See if we can
       
  1158                 // round the starting slot down instead and still be within
       
  1159                 // the region.
       
  1160                 int start = RoundHourDown(selection.iStartSlot);
       
  1161                 if (start + iSlotsInHour <= region.iEndSlot) {
       
  1162                     // Ok, an empty selection fits within the region.
       
  1163                     iSelectedSlot = start;
       
  1164                 }
       
  1165             }
       
  1166             else
       
  1167                 if (!IsHourStartSlot(selection.iEndSlot)) {
       
  1168                     // The ending slot isn't a hour start slot. Rounding directly
       
  1169                     // could move the interval outside of the region. See if we can
       
  1170                     // round the upwards instead and still be within
       
  1171                     // the region.
       
  1172                     int start = RoundHourUp(selection.iEndSlot - iSlotsInHour);
       
  1173                     if (start >= region.iStartSlot) {
       
  1174                         // Ok, an empty selection fits within the region.
       
  1175                         iSelectedSlot = start;
       
  1176                     }
       
  1177                 }
       
  1178 
       
  1179             // Make sure the selected interval really is a valid
       
  1180             // empty interval selection (this forces rounding if needed)
       
  1181             selection = EmptySelectionInterval();
       
  1182 
       
  1183             // Check the final target interval once more.
       
  1184             iSelectedColumnEventIndex = FindEvent(selection, EMoveDirectionDown);
       
  1185             if (iSelectedColumnEventIndex >= 0) {
       
  1186                 // An event was found in the given interval
       
  1187                 UpdateSelectionInEvent();
       
  1188             }
       
  1189             else {
       
  1190                 // No even found, use the empty selection
       
  1191 
       
  1192                 // If not within the region, move out
       
  1193                 if (selection.iStartSlot < region.iStartSlot) {
       
  1194                     MoveOutFromRegion(EMoveDirectionUp);
       
  1195                 }
       
  1196                 else
       
  1197                     if (selection.iEndSlot > region.iEndSlot) {
       
  1198                         MoveOutFromRegion(EMoveDirectionDown);
       
  1199                     }
       
  1200             }
       
  1201         }
       
  1202 
       
  1203     }
       
  1204 
       
  1205 }
       
  1206 
       
  1207 /*!
       
  1208  \brief CalenDayInfo::MoveInRegion
       
  1209 */
       
  1210 bool CalenDayInfo::MoveInRegion(TScrollDirection aDirection)
       
  1211 {
       
  1212     ASSERT( iSelectedAlldayEvent < 0 && iSelectedRegion >= 0 );
       
  1213     bool moved(true);
       
  1214     switch (aDirection) {
       
  1215         case EScrollUp: {
       
  1216             MoveInColumn(EMoveDirectionUp);
       
  1217         }
       
  1218             break;
       
  1219         case EScrollDown: {
       
  1220             MoveInColumn(EMoveDirectionDown);
       
  1221         }
       
  1222             break;
       
  1223 
       
  1224         case EScrollLeft: {
       
  1225             ASSERT( iSelectedRegion < iRegionList.count() );
       
  1226             CalenTimeRegion& region = iRegionList[iSelectedRegion];
       
  1227             ASSERT( iSelectedColumn >= 0 && iSelectedColumn <= region.iColumns.count() );
       
  1228             if (iSelectedColumn == 0) {
       
  1229                 if (iAlldayEvents.count() > 0) {
       
  1230                     // remember the last selection state
       
  1231                     iSelectedAlldayEvent = iAlldayEvents.count() - 1;
       
  1232                 }
       
  1233                 else {
       
  1234                     // move to previous day
       
  1235                     moved = false;
       
  1236                 }
       
  1237             }
       
  1238             else {
       
  1239                 MoveBetweenColumns(aDirection);
       
  1240             }
       
  1241         }
       
  1242             break;
       
  1243 
       
  1244         case EScrollRight: {
       
  1245             ASSERT( iSelectedRegion < iRegionList.count() );
       
  1246             CalenTimeRegion& region = iRegionList[iSelectedRegion];
       
  1247             if (iSelectedColumn < region.iColumns.count() - 1) {
       
  1248                 MoveBetweenColumns(aDirection);
       
  1249             }
       
  1250             else
       
  1251                 if (iSelectedColumn == region.iColumns.count() - 1) {
       
  1252                     // In the last column, moving to the empty column
       
  1253                     iSelectedColumn++;
       
  1254                     iSelectedColumnEventIndex = KErrNotFound;
       
  1255 
       
  1256                     // Round the selection to an empty interval
       
  1257                     EmptySelectionInterval();
       
  1258                 }
       
  1259                 else {
       
  1260                     // in the last, empty column
       
  1261                     // move to next day
       
  1262                     moved = false;
       
  1263                 }
       
  1264         }
       
  1265             break;
       
  1266 
       
  1267         default:
       
  1268             // Do nothing
       
  1269             break;
       
  1270     }
       
  1271 
       
  1272     return moved;
       
  1273 }
       
  1274 
       
  1275 /*!
       
  1276  \brief CalenDayInfo::MoveInEmptyArea
       
  1277 */
       
  1278 bool CalenDayInfo::MoveInEmptyArea(TScrollDirection aDirection)
       
  1279 {
       
  1280     ASSERT( iSelectedAlldayEvent < 0 && iSelectedRegion < 0 );
       
  1281     bool moved(true);
       
  1282     switch (aDirection) {
       
  1283         case EScrollUp: {
       
  1284             CalenSlotInterval curInterval = EmptySelectionInterval();
       
  1285             CalenSlotInterval targetInterval = NextFocusArea(curInterval, EMoveDirectionUp);
       
  1286             iSelectedRegion = FindRegion(targetInterval, EMoveDirectionUp);
       
  1287             if (iSelectedRegion >= 0) {
       
  1288                 EnterRegionFromBelow();
       
  1289             }
       
  1290             else {
       
  1291                 iSelectedSlot = NextEmptyFocusSlot(curInterval, EMoveDirectionUp);
       
  1292             }
       
  1293 
       
  1294         }
       
  1295             break;
       
  1296         case EScrollDown: {
       
  1297             CalenSlotInterval curInterval = EmptySelectionInterval();
       
  1298             CalenSlotInterval targetInterval = NextFocusArea(curInterval, EMoveDirectionDown);
       
  1299             iSelectedRegion = FindRegion(targetInterval, EMoveDirectionDown);
       
  1300             if (iSelectedRegion >= 0) {
       
  1301                 EnterRegionFromAbove();
       
  1302             }
       
  1303             else {
       
  1304                 iSelectedSlot = NextEmptyFocusSlot(curInterval, EMoveDirectionDown);
       
  1305             }
       
  1306         }
       
  1307             break;
       
  1308         case EScrollLeft: {
       
  1309             if (iAlldayEvents.count() > 0) {
       
  1310                 iSelectedAlldayEvent = iAlldayEvents.count() - 1;
       
  1311             }
       
  1312             else {
       
  1313                 // move to previous day
       
  1314                 moved = false;
       
  1315             }
       
  1316         }
       
  1317             break;
       
  1318         case EScrollRight: {
       
  1319             // move to next day
       
  1320             moved = false;
       
  1321         }
       
  1322             break;
       
  1323         default:
       
  1324             // Do nothing
       
  1325             break;
       
  1326     }
       
  1327 
       
  1328     return moved;
       
  1329 }
       
  1330 
       
  1331 /*!
       
  1332  \brief CalenDayInfo::MoveSelection
       
  1333 */
       
  1334 bool CalenDayInfo::MoveSelection(TScrollDirection aDirection)
       
  1335 {
       
  1336     bool moved(true);
       
  1337     StoreOrigSelection();
       
  1338     if (iSelectedAlldayEvent >= 0) {
       
  1339         // focused on an allday event
       
  1340         moved = MoveInAlldayEvent(aDirection);
       
  1341     }
       
  1342     else
       
  1343         if (iSelectedRegion >= 0) {
       
  1344             // focused on some event region
       
  1345             moved = MoveInRegion(aDirection);
       
  1346         }
       
  1347         else {
       
  1348             // focused on an empty, plain area
       
  1349             moved = MoveInEmptyArea(aDirection);
       
  1350         }
       
  1351     if (!ValidateSelection()) {
       
  1352         moved = false;
       
  1353     }
       
  1354     return moved;
       
  1355 }
       
  1356 
       
  1357 /*!
       
  1358  \brief CalenDayInfo::MoveSelectionInEvent
       
  1359 */
       
  1360 void CalenDayInfo::MoveSelectionInEvent(TScrollDirection aDirection)
       
  1361 {
       
  1362     ASSERT( IsEventSelected() );
       
  1363 
       
  1364     switch (aDirection) {
       
  1365         case EScrollUp: {
       
  1366             iSelectedSlot -= KFSCalSlotsInHour;
       
  1367         }
       
  1368             break;
       
  1369         case EScrollDown: {
       
  1370             iSelectedSlot += KFSCalSlotsInHour;
       
  1371         }
       
  1372             break;
       
  1373 
       
  1374         default:
       
  1375             // Do nothing
       
  1376             break;
       
  1377     }
       
  1378 
       
  1379     UpdateSelectionInEvent();
       
  1380 
       
  1381     if (iSelectedAlldayEvent >= 0) {
       
  1382         // if an allday event is selected, invalidate
       
  1383         // the old region/event selection
       
  1384         iSelectedRegion = KErrNotFound;
       
  1385     }
       
  1386 }
       
  1387 
       
  1388 /*!
       
  1389  \brief CalenDayInfo::UpdateSelectionInEvent
       
  1390 */
       
  1391 void CalenDayInfo::UpdateSelectionInEvent()
       
  1392 {
       
  1393     ASSERT( IsEventSelected() );
       
  1394     CalenSlotInterval event = SelectedInterval();
       
  1395 
       
  1396     if (iSelectedSlot < event.iStartSlot) {
       
  1397         iSelectedSlot = event.iStartSlot;
       
  1398     }
       
  1399     if (iSelectedSlot >= event.iEndSlot) {
       
  1400         iSelectedSlot = event.iEndSlot - 1;
       
  1401     }
       
  1402 
       
  1403 }
       
  1404 
       
  1405 /*!
       
  1406  \brief CalenDayInfo::SetSelectionInEvent
       
  1407 */
       
  1408 void CalenDayInfo::SetSelectionInEvent(int aDirection)
       
  1409 {
       
  1410     CalenSlotInterval event = SelectedInterval();
       
  1411     if (aDirection > 0) {
       
  1412         iSelectedSlot = event.iStartSlot;
       
  1413     }
       
  1414     else {
       
  1415         iSelectedSlot = event.iEndSlot - 1;
       
  1416     }
       
  1417 }
       
  1418 
       
  1419 /*!
       
  1420  \brief CalenDayInfo::StoreOrigSelection
       
  1421 */
       
  1422 void CalenDayInfo::StoreOrigSelection()
       
  1423 {
       
  1424     iOrigSelectedAlldayEvent = iSelectedAlldayEvent;
       
  1425     iOrigSelectedRegion = iSelectedRegion;
       
  1426     iOrigSelectedColumn = iSelectedColumn;
       
  1427     iOrigSelectedSlot = iSelectedSlot;
       
  1428     iOrigSelectedColumnEventIndex = iSelectedColumnEventIndex;
       
  1429 }
       
  1430 
       
  1431 /*!
       
  1432  \brief CalenDayInfo::ValidateSelection
       
  1433 */
       
  1434 bool CalenDayInfo::ValidateSelection()
       
  1435 {
       
  1436     bool validate(true);
       
  1437     if (iSelectedSlot < 0 || iSelectedSlot >= iSlotsInHour * KCalenHoursInDay + iUntimedSlotCount) {
       
  1438         // tried to move outside of the day
       
  1439         // revert to the original coords
       
  1440         iSelectedAlldayEvent = iOrigSelectedAlldayEvent;
       
  1441         iSelectedRegion = iOrigSelectedRegion;
       
  1442         iSelectedColumn = iOrigSelectedColumn;
       
  1443         iSelectedSlot = iOrigSelectedSlot;
       
  1444         iSelectedColumnEventIndex = iOrigSelectedColumnEventIndex;
       
  1445         validate = false;
       
  1446     }
       
  1447     return validate;
       
  1448 }
       
  1449 
       
  1450 /*!
       
  1451  \brief CalenDayInfo::SelectSlot
       
  1452 */
       
  1453 void CalenDayInfo::SelectSlot(int aSlot)
       
  1454 {
       
  1455     // Check for special case: empty untimed slot is selected
       
  1456     if (aSlot >= iFirstUntimedSlot && aSlot < iFirstUntimedSlot + iEmptyUntimedSlots) {
       
  1457         aSlot = iFirstUntimedSlot + iEmptyUntimedSlots;
       
  1458     }
       
  1459 
       
  1460     iSelectedAlldayEvent = KErrNotFound;
       
  1461     iSelectedSlot = aSlot;
       
  1462     CalenSlotInterval interval = EmptySelectionInterval();
       
  1463     iSelectedRegion = FindRegion(interval, EMoveDirectionDown);
       
  1464     if (iSelectedRegion >= 0) {
       
  1465         CalenTimeRegion& region = iRegionList[iSelectedRegion];
       
  1466         int regCount(region.iColumns.count());
       
  1467         for (int i = 0; i < regCount; i++) {
       
  1468             CalenTimeColumn& column = region.iColumns[i];
       
  1469             int colCount(column.iEventArray.count());
       
  1470             for (int j = 0; j < colCount; j++) {
       
  1471                 if (column.iEventArray[j].Overlaps(interval)) {
       
  1472                     iSelectedColumn = i;
       
  1473                     iSelectedColumnEventIndex = j;
       
  1474                     iSelectedSlot = aSlot;
       
  1475                     UpdateSelectionInEvent();
       
  1476                     return;
       
  1477                 }
       
  1478             }
       
  1479         }
       
  1480         // Regions can have empty areas where there aren't any events,
       
  1481         // due to rounding of the ends of the region to whole hours.
       
  1482         // A region cannot, however, have a empty complete whole hour.
       
  1483         // Therefore, this cannot occur.
       
  1484         User::Invariant();
       
  1485     }
       
  1486 }
       
  1487 
       
  1488 /*!
       
  1489  \brief CalenDayInfo::RegionList
       
  1490 */
       
  1491 const QList<CalenTimeRegion>& CalenDayInfo::RegionList() const
       
  1492 {
       
  1493     return iRegionList;
       
  1494 }
       
  1495 
       
  1496 /*!
       
  1497  \brief CalenDayInfo::GetEventIntervals
       
  1498 */
       
  1499 void CalenDayInfo::GetEventIntervals(QList<CalenEventInterval>& aArray) const
       
  1500 {
       
  1501     int regCount(iRegionList.count());
       
  1502     for (int i = 0; i < regCount; i++) {
       
  1503         const CalenTimeRegion& region = iRegionList[i];
       
  1504         for (int j = 0; j < region.iIntervals.count(); j++) {
       
  1505             aArray.append(region.iIntervals[j]);
       
  1506         }
       
  1507     }
       
  1508 }
       
  1509 
       
  1510 /*!
       
  1511  \brief CalenDayInfo::InsertUntimedEventL
       
  1512 */
       
  1513 /*
       
  1514 void CalenDayInfo::InsertUntimedEventL(const CCalInstance& aInstance)
       
  1515 {
       
  1516     if (CalenAgendaUtils::IsTimedEntryL(aInstance.Entry().EntryTypeL())) {
       
  1517         // timed entry added as untimed event
       
  1518         User::Invariant();
       
  1519     }
       
  1520 
       
  1521     TCalenInstanceId id = TCalenInstanceId::CreateL(aInstance);
       
  1522     InsertUntimedEvent(aInstance.Entry().EntryTypeL(), id);
       
  1523 }
       
  1524 */
       
  1525 
       
  1526 /*!
       
  1527  \brief CalenDayInfo::InsertUntimedEvent
       
  1528 */
       
  1529 void CalenDayInfo::InsertUntimedEvent(AgendaEntry::Type aType, const TCalenInstanceId& aId)
       
  1530 {
       
  1531     if (aType == AgendaEntry::TypeTodo) {
       
  1532         iTodoEvents.append(aId);
       
  1533     }
       
  1534     else {
       
  1535         iUntimedEvents.append(aId);
       
  1536     }
       
  1537 }
       
  1538 
       
  1539 /*!
       
  1540  \brief CalenDayInfo::SuggestedUntimedSlotPos
       
  1541 */
       
  1542 int CalenDayInfo::SuggestedUntimedSlotPos()
       
  1543 {
       
  1544     int slot = KFSCalStartingHour * iSlotsInHour;
       
  1545     if (iRegionList.count() > 0) {
       
  1546         CalenTimeRegion& region = iRegionList[0];
       
  1547         if (region.iStartSlot < slot) {
       
  1548             slot = RoundHourUp(region.iStartSlot);
       
  1549         }
       
  1550     }
       
  1551     return slot;
       
  1552 }
       
  1553 
       
  1554 /*!
       
  1555  \brief CalenDayInfo::NeededUntimedSlotCount
       
  1556 */
       
  1557 int CalenDayInfo::NeededUntimedSlotCount()
       
  1558 {
       
  1559     int count = iUntimedEvents.count();
       
  1560     if (iTodoEvents.count() > 0) {
       
  1561         count++;
       
  1562     }
       
  1563     return count;
       
  1564 }
       
  1565 
       
  1566 /*!
       
  1567  \brief CalenDayInfo::NeededUntimedSlotCount
       
  1568 */
       
  1569 int CalenDayInfo::UpdateUntimedPos(int aSlot, int aUntimedCount)
       
  1570 {
       
  1571     int newValue = NeededUntimedSlotCount();
       
  1572 
       
  1573     // If this method is called many times, first undo the previous modifications
       
  1574     int regCount(iRegionList.count());
       
  1575     for (int i = 0; i < regCount; i++) {
       
  1576         iRegionList[i].AddOffset(-iUntimedSlotCount, iFirstUntimedSlot);
       
  1577     }
       
  1578     if (iSelectedSlot >= iFirstUntimedSlot) {
       
  1579         iSelectedSlot -= iUntimedSlotCount;
       
  1580     }
       
  1581 
       
  1582     // Reset the untimed slot count
       
  1583     iUntimedSlotCount = 0;
       
  1584 
       
  1585     // Get the default values
       
  1586     iFirstUntimedSlot = SuggestedUntimedSlotPos();
       
  1587     iUntimedSlotCount = newValue;
       
  1588 
       
  1589     // If parameters were given, use them instead of the defaults
       
  1590     if (aSlot >= 0) {
       
  1591         ASSERT( aSlot <= iFirstUntimedSlot );
       
  1592         ASSERT( iUntimedSlotCount <= aUntimedCount );
       
  1593 
       
  1594         iFirstUntimedSlot = aSlot;
       
  1595         iUntimedSlotCount = aUntimedCount;
       
  1596     }
       
  1597 
       
  1598     iEmptyUntimedSlots = iUntimedSlotCount - NeededUntimedSlotCount();
       
  1599 
       
  1600     // Add the new offset to all regions and update the selected slot
       
  1601     regCount = iRegionList.count();
       
  1602     for (int i = 0; i < regCount; i++) {
       
  1603         iRegionList[i].AddOffset(iUntimedSlotCount, iFirstUntimedSlot);
       
  1604     }
       
  1605     if (iSelectedSlot >= iFirstUntimedSlot) {
       
  1606         iSelectedSlot += iUntimedSlotCount;
       
  1607     }
       
  1608     int eventCount(iAlldayEvents.count());
       
  1609     for (int i = 0; i < eventCount; i++) {
       
  1610         iAlldayEvents[i].iEndSlot = iSlotsInHour * KCalenHoursInDay + iUntimedSlotCount;
       
  1611     }
       
  1612 
       
  1613     // Do rounding of the end of the last region. This should be done when no
       
  1614     // more events will be added, but there's no real harm if new events are
       
  1615     // added after this.
       
  1616     if (iRegionList.count() > 0) {
       
  1617         // Round the end of the last region down
       
  1618         CalenTimeRegion& lastRegion = iRegionList[iRegionList.count() - 1];
       
  1619         lastRegion.iEndSlot = RoundHourDown(lastRegion.iEndSlot);
       
  1620     }
       
  1621 
       
  1622     return iFirstUntimedSlot;
       
  1623 }
       
  1624 
       
  1625 /*!
       
  1626  \brief Returns the index of the first occupied slot
       
  1627 */
       
  1628 int CalenDayInfo::FirstOccupiedSlot()
       
  1629 {
       
  1630 
       
  1631     int firstNonemptySlot(KErrNotFound);
       
  1632 
       
  1633     if (iUntimedSlotCount > 0) {
       
  1634         firstNonemptySlot = iFirstUntimedSlot;
       
  1635     }
       
  1636     else {
       
  1637         if (iRegionList.count() > 0) {
       
  1638             CalenTimeRegion& region = iRegionList[0];
       
  1639             firstNonemptySlot = region.iStartSlot;
       
  1640         }
       
  1641     }
       
  1642 
       
  1643     return firstNonemptySlot;
       
  1644 }
       
  1645 
       
  1646 /*!
       
  1647  \brief Returns the index of the last occupied slot
       
  1648 */
       
  1649 int CalenDayInfo::LastOccupiedSlot()
       
  1650 {
       
  1651     int lastNonemptySlot(KErrNotFound);
       
  1652     if (iRegionList.count() > 0) {
       
  1653         int lastIndex = iRegionList.count() - 1;
       
  1654         CalenTimeRegion& region = iRegionList[lastIndex];
       
  1655         lastNonemptySlot = region.iEndSlot;
       
  1656     }
       
  1657     else {
       
  1658         if (iUntimedSlotCount > 0) {
       
  1659             lastNonemptySlot = iFirstUntimedSlot + iUntimedSlotCount - 1;
       
  1660         }
       
  1661     }
       
  1662     return lastNonemptySlot;
       
  1663 }
       
  1664 
       
  1665 /*!
       
  1666  \brief Returns the index of the earliest end slot
       
  1667 */
       
  1668 int CalenDayInfo::EarliestEndSlot()
       
  1669 {
       
  1670     int earliestEndSlot(KErrNotFound);
       
  1671     int untimedEventCount = iUntimedSlotCount - iEmptyUntimedSlots;
       
  1672     if (untimedEventCount > 0) {
       
  1673         earliestEndSlot = iFirstUntimedSlot + iEmptyUntimedSlots;
       
  1674         // add 1, since end slot is actually the one that is right after..
       
  1675         earliestEndSlot++;
       
  1676     }
       
  1677     else {
       
  1678         if (iRegionList.count() > 0) {
       
  1679             earliestEndSlot = iEarliestEndSlot + iEmptyUntimedSlots;
       
  1680         }
       
  1681     }
       
  1682     return earliestEndSlot;
       
  1683 }
       
  1684 
       
  1685 /*!
       
  1686  \brief Returns the index of the last start slot
       
  1687 */
       
  1688 int CalenDayInfo::LastStartSlot()
       
  1689 {
       
  1690     int lastStartSlot(KErrNotFound);
       
  1691 
       
  1692     if (iRegionList.count() > 0) {
       
  1693         lastStartSlot = iLastStartSlot + iUntimedSlotCount;
       
  1694     }
       
  1695     else {
       
  1696         if (iUntimedSlotCount - iEmptyUntimedSlots > 0) {
       
  1697             lastStartSlot = iFirstUntimedSlot + iUntimedSlotCount - 1;
       
  1698         }
       
  1699     }
       
  1700     return lastStartSlot;
       
  1701 }
       
  1702 
       
  1703 /*!
       
  1704  \brief Returns the index of a slot where this event should start drawing,
       
  1705  based on the start time
       
  1706 */
       
  1707 int CalenDayInfo::SlotIndexForStartTime(QDateTime aStartTime)
       
  1708 {
       
  1709     // For example, 1/2 hour accuracy (iSlotsInHour == 2):
       
  1710     // Start drawing with the previus half hour: 9:15 -> 9:00, 9:59-> 9:30.
       
  1711 
       
  1712 
       
  1713     //    TDateTime dt = aStartTime.DateTime();
       
  1714     //    int minutes = dt.Minute();
       
  1715     //    int hours = dt.Hour();
       
  1716     int minutes = aStartTime.time().minute();//dt.Minute();
       
  1717     int hours = aStartTime.time().hour();//dt.Hour();
       
  1718 
       
  1719     // calculate index based on the hour.
       
  1720     int slotIndex = hours * iSlotsInHour;
       
  1721 
       
  1722     switch (iSlotsInHour) {
       
  1723         case EOne: // do nothing
       
  1724             break;
       
  1725         case ETwo: {
       
  1726             if (minutes >= 30) {
       
  1727                 slotIndex++;
       
  1728             }
       
  1729         }
       
  1730             break;
       
  1731         case EThree: {
       
  1732             if (minutes >= 20) {
       
  1733                 slotIndex++;
       
  1734             }
       
  1735             if (minutes >= 40) {
       
  1736                 slotIndex++;
       
  1737             }
       
  1738         }
       
  1739             break;
       
  1740         case EFour: // followthrough
       
  1741         default: {
       
  1742             if (minutes >= 15) {
       
  1743                 slotIndex++;
       
  1744             }
       
  1745             if (minutes >= 30) {
       
  1746                 slotIndex++;
       
  1747             }
       
  1748             if (minutes >= 45) {
       
  1749                 slotIndex++;
       
  1750             }
       
  1751 
       
  1752         }
       
  1753     }
       
  1754 
       
  1755     if (slotIndex >= iFirstUntimedSlot) {
       
  1756         slotIndex += iUntimedSlotCount;
       
  1757     }
       
  1758     return slotIndex;
       
  1759 }
       
  1760 
       
  1761 /*!
       
  1762  \brief Returns the index of a slot where this event should end drawing,
       
  1763  based on the end time
       
  1764 */
       
  1765 int CalenDayInfo::SlotIndexForEndTime(QDateTime aEndTime)
       
  1766 {
       
  1767     // For example, 1/2 hour accuracy (iSlotsInHour == 2):
       
  1768     // End drawing with the next half hour: 9:10 -> 9:30, 9:59-> 10:00.
       
  1769 
       
  1770     //    TDateTime dt = aEndTime.DateTime();
       
  1771     //    int minutes = dt.Minute();
       
  1772     //    int hours = dt.Hour();
       
  1773     int minutes = aEndTime.time().minute();
       
  1774     int hours = aEndTime.time().hour();
       
  1775 
       
  1776     // calculate index based on the hour.
       
  1777     int slotIndex = hours * iSlotsInHour;
       
  1778 
       
  1779     switch (iSlotsInHour) {
       
  1780         case EOne: {
       
  1781             if (minutes > 0) {
       
  1782                 slotIndex++;
       
  1783             }
       
  1784         }
       
  1785 
       
  1786             break;
       
  1787         case ETwo: {
       
  1788             if (minutes > 0) {
       
  1789                 slotIndex++;
       
  1790             }
       
  1791             if (minutes > 30) {
       
  1792                 slotIndex++;
       
  1793             }
       
  1794         }
       
  1795             break;
       
  1796         case EThree: {
       
  1797             if (minutes > 0) {
       
  1798                 slotIndex++;
       
  1799             }
       
  1800             if (minutes > 20) {
       
  1801                 slotIndex++;
       
  1802             }
       
  1803             if (minutes > 40) {
       
  1804                 slotIndex++;
       
  1805             }
       
  1806         }
       
  1807             break;
       
  1808         case EFour: // followthrough
       
  1809         default: {
       
  1810             if (minutes > 0) {
       
  1811                 slotIndex++;
       
  1812             }
       
  1813 
       
  1814             if (minutes > 15) {
       
  1815                 slotIndex++;
       
  1816             }
       
  1817             if (minutes > 30) {
       
  1818                 slotIndex++;
       
  1819             }
       
  1820             if (minutes > 45) {
       
  1821                 slotIndex++;
       
  1822             }
       
  1823 
       
  1824         }
       
  1825     }
       
  1826 
       
  1827     if (slotIndex >= iFirstUntimedSlot) {
       
  1828         slotIndex += iUntimedSlotCount;
       
  1829     }
       
  1830 
       
  1831     return slotIndex;
       
  1832 
       
  1833 }
       
  1834 
       
  1835 /*!
       
  1836  \brief CalenDayInfo::IsHourStartSlot
       
  1837 */
       
  1838 bool CalenDayInfo::IsHourStartSlot(const int& aSlotIndex) const
       
  1839 {
       
  1840     bool isHourStartSlot(false);
       
  1841     if (IsExtraSlot(aSlotIndex)) {
       
  1842         isHourStartSlot = false;
       
  1843     }
       
  1844     else {
       
  1845         int hourIndex(aSlotIndex);
       
  1846         if (aSlotIndex >= iFirstUntimedSlot + iUntimedSlotCount) {
       
  1847             hourIndex -= iUntimedSlotCount;
       
  1848         }
       
  1849         int hour = hourIndex / iSlotsInHour;
       
  1850         int startIndOfHour = hour * iSlotsInHour;
       
  1851         if (hourIndex - startIndOfHour > 0) {
       
  1852             isHourStartSlot = false;
       
  1853         }
       
  1854         else {
       
  1855             isHourStartSlot = true;
       
  1856         }
       
  1857     }
       
  1858     return isHourStartSlot;
       
  1859 
       
  1860 }
       
  1861 
       
  1862 /*!
       
  1863  \brief CalenDayInfo::IsExtraSlot
       
  1864 */
       
  1865 bool CalenDayInfo::IsExtraSlot(const int& aSlotIndex) const
       
  1866 {
       
  1867     return (aSlotIndex >= iFirstUntimedSlot) && (aSlotIndex < (iFirstUntimedSlot
       
  1868         + iUntimedSlotCount));
       
  1869 }
       
  1870 
       
  1871 /*!
       
  1872  \brief CalenDayInfo::HourFromSlotIndex
       
  1873  Returns the corresponding hour, or KErrNone if index is not hour slot index
       
  1874 */
       
  1875 int CalenDayInfo::HourFromSlotIndex(const int& aSlotInd) const
       
  1876 {
       
  1877 
       
  1878     int hour(KErrNotFound);
       
  1879 
       
  1880     if (!IsExtraSlot(aSlotInd)) {
       
  1881 
       
  1882         if (aSlotInd < 0) {
       
  1883             // round downwards, to the previous starting hour
       
  1884             // e.g. iSlotsInHour = 2, aSlotInd = -1, hour = -1,
       
  1885             // which by SlotIndexFromHour corresponds to slot -2
       
  1886             hour = (aSlotInd - iSlotsInHour + 1) / iSlotsInHour;
       
  1887         }
       
  1888         else
       
  1889             if (aSlotInd < iFirstUntimedSlot) {
       
  1890                 hour = aSlotInd / iSlotsInHour;
       
  1891             }
       
  1892             else
       
  1893                 if (aSlotInd >= iFirstUntimedSlot + iUntimedSlotCount) {
       
  1894                     hour = (aSlotInd - iUntimedSlotCount) / iSlotsInHour;
       
  1895                 }
       
  1896     }
       
  1897     return hour;
       
  1898 }
       
  1899 
       
  1900 /*!
       
  1901  \brief CalenDayInfo::SlotIndexFromHour
       
  1902  Returns the corresponding hour, or KErrNone if index is not hour slot index
       
  1903 */
       
  1904 int CalenDayInfo::SlotIndexFromHour(int aHour)
       
  1905 {
       
  1906 
       
  1907     int slotIndex(KErrNotFound);
       
  1908 
       
  1909     if (aHour >= iFirstUntimedSlot / iSlotsInHour) {
       
  1910         slotIndex = aHour * iSlotsInHour + iUntimedSlotCount;
       
  1911     }
       
  1912     else {
       
  1913         slotIndex = aHour * iSlotsInHour;
       
  1914     }
       
  1915 
       
  1916     return slotIndex;
       
  1917 }
       
  1918 
       
  1919 /*!
       
  1920  \brief CalenDayInfo::RoundHourUp
       
  1921  Rounds the slot number up (towards earlier hours) to an even hour
       
  1922 */
       
  1923 int CalenDayInfo::RoundHourUp(int aSlot)
       
  1924 {
       
  1925     if (!IsExtraSlot(aSlot)) {
       
  1926         aSlot = SlotIndexFromHour(HourFromSlotIndex(aSlot));
       
  1927     }
       
  1928     return aSlot;
       
  1929 }
       
  1930 
       
  1931 /*!
       
  1932  \brief CalenDayInfo::RoundHourDown
       
  1933  Rounds the slot number down (towards later hours) to an even hour
       
  1934 */
       
  1935 int CalenDayInfo::RoundHourDown(int aSlot)
       
  1936 {
       
  1937     if (!IsExtraSlot(aSlot) && !IsHourStartSlot(aSlot)) {
       
  1938         aSlot = SlotIndexFromHour(HourFromSlotIndex(aSlot + iSlotsInHour));
       
  1939     }
       
  1940     return aSlot;
       
  1941 }
       
  1942 
       
  1943 /*!
       
  1944  \brief CalenSlotInterval::Overlaps
       
  1945 */
       
  1946 bool CalenSlotInterval::Overlaps(const CalenSlotInterval& aInterval) const
       
  1947 {
       
  1948     return aInterval.iStartSlot < iEndSlot && aInterval.iEndSlot > iStartSlot;
       
  1949 }
       
  1950 
       
  1951 /*!
       
  1952  \brief CalenSlotInterval::AddOffset
       
  1953 */
       
  1954 void CalenSlotInterval::AddOffset(int aOffset, int aPos)
       
  1955 {
       
  1956     if (iStartSlot >= aPos) {
       
  1957         iStartSlot += aOffset;
       
  1958     }
       
  1959     if (iEndSlot >= aPos) {
       
  1960         iEndSlot += aOffset;
       
  1961     }
       
  1962 }
       
  1963 
       
  1964 /*!
       
  1965  \brief CalenSlotInterval::Union
       
  1966 */
       
  1967 void CalenSlotInterval::Union(const CalenSlotInterval& aInterval)
       
  1968 {
       
  1969     if (!aInterval.IsEmpty()) {
       
  1970         iStartSlot = Min(iStartSlot, aInterval.iStartSlot);
       
  1971         iEndSlot = Max(iEndSlot, aInterval.iEndSlot);
       
  1972     }
       
  1973 }
       
  1974 
       
  1975 /*!
       
  1976  \brief CalenSlotInterval::Adjacent
       
  1977 */
       
  1978 bool CalenSlotInterval::Adjacent(const CalenSlotInterval& aInterval) const
       
  1979 {
       
  1980     bool adjacent(false);
       
  1981     if (Overlaps(aInterval)) {
       
  1982         adjacent = true;
       
  1983     }
       
  1984     else {
       
  1985         adjacent = iStartSlot == aInterval.iEndSlot || iEndSlot == aInterval.iStartSlot;
       
  1986     }
       
  1987     return adjacent;
       
  1988 }
       
  1989 
       
  1990 /*!
       
  1991  \brief CalenSlotInterval::IsEmpty
       
  1992 */
       
  1993 bool CalenSlotInterval::IsEmpty() const
       
  1994 {
       
  1995     return iStartSlot >= iEndSlot;
       
  1996 }
       
  1997 
       
  1998 /*!
       
  1999  \brief CalenSlotInterval::Intersect
       
  2000 */
       
  2001 void CalenSlotInterval::Intersect(const CalenSlotInterval& aInterval)
       
  2002 {
       
  2003     if (aInterval.IsEmpty()) {
       
  2004         iEndSlot = iStartSlot;
       
  2005     }
       
  2006     else {
       
  2007         iStartSlot = Max(iStartSlot, aInterval.iStartSlot);
       
  2008         iEndSlot = Min(iEndSlot, aInterval.iEndSlot);
       
  2009     }
       
  2010 }
       
  2011 
       
  2012 /*!
       
  2013  \brief CalenSlotInterval::Subtract
       
  2014 */
       
  2015 void CalenSlotInterval::Subtract(const CalenSlotInterval& aInterval, CalenSlotInterval& aSecondPart)
       
  2016 {
       
  2017     aSecondPart.iStartSlot = aSecondPart.iEndSlot = 0;
       
  2018     if (!aInterval.IsEmpty() && Overlaps(aInterval)) {
       
  2019         if (aInterval.iStartSlot <= iStartSlot) {
       
  2020             iStartSlot = aInterval.iEndSlot;
       
  2021         }
       
  2022         else
       
  2023             if (aInterval.iEndSlot >= iEndSlot) {
       
  2024                 iEndSlot = aInterval.iStartSlot;
       
  2025             }
       
  2026             else {
       
  2027                 aSecondPart.iStartSlot = aInterval.iEndSlot;
       
  2028                 aSecondPart.iEndSlot = iEndSlot;
       
  2029                 iEndSlot = aInterval.iStartSlot;
       
  2030             }
       
  2031     }
       
  2032 }
       
  2033 
       
  2034 /*!
       
  2035  \brief CalenSlotInterval::operator>
       
  2036 */
       
  2037 bool CalenSlotInterval::operator>(const CalenSlotInterval& aInterval) const
       
  2038 {
       
  2039     return iStartSlot > aInterval.iStartSlot;
       
  2040 }
       
  2041 
       
  2042 /*!
       
  2043  \brief CalenTimeColumn::Close
       
  2044 */
       
  2045 void CalenTimeColumn::Close()
       
  2046 {
       
  2047     iEventArray.clear();
       
  2048 }
       
  2049 
       
  2050 /*!
       
  2051  \brief CalenTimeColumn::AddEvent
       
  2052 */
       
  2053 void CalenTimeColumn::AddEvent(const CalenTimedEventInfo& aEvent)
       
  2054 {
       
  2055     ASSERT( aEvent.iStartSlot < aEvent.iEndSlot );
       
  2056     ASSERT( CanFitEvent( aEvent ) );
       
  2057 
       
  2058     if (iEventArray.count() == 0) {
       
  2059         iStartSlot = aEvent.iStartSlot;
       
  2060     }
       
  2061     iEndSlot = aEvent.iEndSlot;
       
  2062     iEventArray.append(aEvent);
       
  2063 }
       
  2064 
       
  2065 /*!
       
  2066  \brief CalenTimeColumn::CanFitEvent
       
  2067 */
       
  2068 bool CalenTimeColumn::CanFitEvent(const CalenTimedEventInfo& aEvent)
       
  2069 {
       
  2070     return (aEvent.iStartSlot >= iEndSlot) || (iEventArray.count() == 0);
       
  2071 }
       
  2072 
       
  2073 /*!
       
  2074  \brief CalenTimeColumn::ContainsEvent
       
  2075 */
       
  2076 bool CalenTimeColumn::ContainsEvent(const TCalenInstanceId& aId)
       
  2077 {
       
  2078     bool contains(false);
       
  2079     int eventCount(iEventArray.count());
       
  2080     for (int i = 0; i < eventCount && !contains; i++) {
       
  2081         if (iEventArray[i].iId == aId) {
       
  2082             contains = true;
       
  2083         }
       
  2084     }
       
  2085     return contains;
       
  2086 }
       
  2087 
       
  2088 /*!
       
  2089  \brief CalenTimeColumn::AddOffset
       
  2090 */
       
  2091 void CalenTimeColumn::AddOffset(int aOffset, int aPos)
       
  2092 {
       
  2093     if (aOffset != 0) {
       
  2094         CalenSlotInterval::AddOffset(aOffset, aPos);
       
  2095         int eventCount(iEventArray.count());
       
  2096         for (int i = 0; i < eventCount; i++) {
       
  2097             iEventArray[i].AddOffset(aOffset, aPos);
       
  2098         }
       
  2099     }
       
  2100 }
       
  2101 
       
  2102 /*!
       
  2103  \brief CalenTimeRegion::Close
       
  2104 */
       
  2105 void CalenTimeRegion::Close()
       
  2106 {
       
  2107     int colCount(iColumns.count());
       
  2108     for (int i = 0; i < colCount; i++) {
       
  2109         iColumns[i].Close();
       
  2110     }
       
  2111     iColumns.clear();
       
  2112     iIntervals.clear();
       
  2113 }
       
  2114 
       
  2115 /*!
       
  2116  \brief CalenTimeRegion::Overlaps
       
  2117 */
       
  2118 bool CalenTimeRegion::Overlaps(const CalenSlotInterval& aInterval) const
       
  2119 {
       
  2120     // the base class implementation would be ok, but we might want the assertion here
       
  2121     ASSERT( iColumns.count()> 0 );
       
  2122     return CalenSlotInterval::Overlaps(aInterval);
       
  2123 }
       
  2124 
       
  2125 /*!
       
  2126  \brief CalenTimeRegion::AddOffset
       
  2127 */
       
  2128 void CalenTimeRegion::AddOffset(int aOffset, int aPos)
       
  2129 {
       
  2130     if (aOffset != 0) {
       
  2131         CalenSlotInterval::AddOffset(aOffset, aPos);
       
  2132         int colCount(iColumns.count());
       
  2133         for (int i = 0; i < colCount; i++) {
       
  2134             iColumns[i].AddOffset(aOffset, aPos);
       
  2135         }
       
  2136         int intervalCount(iIntervals.count());
       
  2137         for (int i = 0; i < intervalCount; i++) {
       
  2138             iIntervals[i].AddOffset(aOffset, aPos);
       
  2139         }
       
  2140     }
       
  2141 
       
  2142 }
       
  2143 
       
  2144 /*!
       
  2145  \brief CalenTimeRegion::AddEvent
       
  2146 */
       
  2147 void CalenTimeRegion::AddEvent(const CalenTimedEventInfo& aEvent)
       
  2148 {
       
  2149     if (iColumns.count() == 0) {
       
  2150         // This is the first event added to this region
       
  2151         iStartSlot = aEvent.iStartSlot;
       
  2152         iEndSlot = aEvent.iEndSlot;
       
  2153     }
       
  2154     else {
       
  2155         // Check that the events actually are added in the correct order
       
  2156         ASSERT( aEvent.iStartSlot >= iStartSlot );
       
  2157         ASSERT( aEvent.iStartSlot < iEndSlot );
       
  2158 
       
  2159         if (aEvent.iEndSlot > iEndSlot) {
       
  2160             iEndSlot = aEvent.iEndSlot;
       
  2161         }
       
  2162     }
       
  2163 
       
  2164     AddInterval(aEvent);
       
  2165 
       
  2166     // If the event fits in one of the existing columns, add it to that one
       
  2167     bool added(false);
       
  2168     int colCount(iColumns.count());
       
  2169     for (int i = 0; i < colCount && !added; i++) {
       
  2170         if (iColumns[i].CanFitEvent(aEvent)) {
       
  2171             iColumns[i].AddEvent(aEvent);
       
  2172             added = true;
       
  2173         }
       
  2174     }
       
  2175 
       
  2176     if (!added) {
       
  2177         // otherwise create a new column for it
       
  2178         iColumns.append(CalenTimeColumn());
       
  2179         iColumns[iColumns.count() - 1].AddEvent(aEvent);
       
  2180     }
       
  2181 }
       
  2182 
       
  2183 /*!
       
  2184  \brief CalenTimeRegion::AddInterval
       
  2185 */
       
  2186 void CalenTimeRegion::AddInterval(const CalenTimedEventInfo& aEvent)
       
  2187 {
       
  2188     /*
       
  2189      * Here are a few examples of different possible cases for this method.
       
  2190      * The first picture of each example shows the initial situation in the
       
  2191      * array, and the new event to be added. The transformations performed
       
  2192      * are shown with a few intermediate steps.
       
  2193      *
       
  2194      * nO = newOverlap, nE = newEvent
       
  2195      *
       
  2196      * Example one:
       
  2197      *
       
  2198      *               ------------------
       
  2199      *               | newEvent       |
       
  2200      * --------------------------------
       
  2201      * ... | lastOverlap | tail |
       
  2202      * --------------------------
       
  2203      *
       
  2204      *                   --------------
       
  2205      *                   | newEvent   |
       
  2206      * --------------------------------
       
  2207      * ... | lastOverlap | tail |
       
  2208      * --------------------------
       
  2209      *
       
  2210      *                   --------------
       
  2211      *                   | nO   | nE  |
       
  2212      * --------------------------------
       
  2213      * ... | lastOverlap | tail |
       
  2214      * --------------------------
       
  2215      *
       
  2216      * Result:
       
  2217      * --------------------------------
       
  2218      * ... | lastOverlap        | nE  |
       
  2219      * --------------------------------
       
  2220      *
       
  2221      *
       
  2222      * Example two:
       
  2223      *                          ------------------
       
  2224      *                          | newEvent       |
       
  2225      * -------------------------------------------
       
  2226      * ... | lastOverlap | tail      |
       
  2227      * -------------------------------
       
  2228      *
       
  2229      *                          ------------------
       
  2230      *                          | nO | newEvent  |
       
  2231      * -------------------------------------------
       
  2232      * ... | lastOverlap | tail      |
       
  2233      * -------------------------------
       
  2234      *
       
  2235      * Result:
       
  2236      * -------------------------------------------
       
  2237      * ... | lastOverlap | tail | nO | newEvent  |
       
  2238      * -------------------------------------------
       
  2239      *
       
  2240      *
       
  2241      * Example three:
       
  2242      *                          --------------
       
  2243      *                          | newEvent   |
       
  2244      * -----------------------------------------------------
       
  2245      * ... | lastOverlap | tail                            |
       
  2246      * -----------------------------------------------------
       
  2247      *
       
  2248      *                          --------------
       
  2249      *                          | newOverlap |
       
  2250      * -----------------------------------------------------
       
  2251      * ... | lastOverlap | tail                            |
       
  2252      * -----------------------------------------------------
       
  2253      *
       
  2254      *                          --------------
       
  2255      *                          | newOverlap |
       
  2256      * -----------------------------------------------------
       
  2257      * ... | lastOverlap | tail |            | tailPart2   |
       
  2258      * -----------------------------------------------------
       
  2259      *
       
  2260      * Result:
       
  2261      * -----------------------------------------------------
       
  2262      * ... | lastOverlap | tail | newOverlap | tailPart2   |
       
  2263      * -----------------------------------------------------
       
  2264      */
       
  2265 
       
  2266     // Fill out the new event
       
  2267     CalenEventInterval newEvent;
       
  2268     newEvent.iStartSlot = aEvent.iStartSlot;
       
  2269     newEvent.iEndSlot = aEvent.iEndSlot;
       
  2270     newEvent.iStatus = aEvent.iStatus;
       
  2271     //    newEvent.iReplicationStatus = aEvent.iReplicationStatus;
       
  2272     newEvent.iOverlap = false;
       
  2273 
       
  2274     // a pointer to the last interval which is an overlap interval
       
  2275     CalenEventInterval* lastOverlap = NULL;
       
  2276     // If nonempty, this is the last interval after the last overlap
       
  2277     CalenEventInterval tail;
       
  2278     tail.iStartSlot = tail.iEndSlot = 0;
       
  2279 
       
  2280     // Find lastOverlap and tail.
       
  2281     if (iIntervals.count() >= 1) {
       
  2282         // lastInterval is a pointer to the last interval in the array
       
  2283         CalenEventInterval* lastInterval = &iIntervals[iIntervals.count() - 1];
       
  2284 
       
  2285         // If this is an overlap interval, we haven't got any tail.
       
  2286         if (lastInterval->iOverlap) {
       
  2287             lastOverlap = lastInterval;
       
  2288         }
       
  2289         else {
       
  2290             tail = *lastInterval;
       
  2291         }
       
  2292 
       
  2293         // If there's at least two intervals, and the last one wasn't an overlap,
       
  2294         // the second last one must be an overlap.
       
  2295         if (iIntervals.count() >= 2 && !lastOverlap) {
       
  2296             lastOverlap = &iIntervals[iIntervals.count() - 2];
       
  2297             ASSERT( lastOverlap->iOverlap );
       
  2298         }
       
  2299     }
       
  2300 
       
  2301     // If we got a tail, remove it from the array (it will be readded
       
  2302     // at the end if needed)
       
  2303     if (!tail.IsEmpty()) {
       
  2304         iIntervals.removeAt(iIntervals.count() - 1);
       
  2305     }
       
  2306 
       
  2307     CalenEventInterval empty;
       
  2308     if (lastOverlap) {
       
  2309         // Remove the part which already is marked as an overlap
       
  2310         // from the new event. The new event can't start before the
       
  2311         // last overlap starts since events are added in order, therefore
       
  2312         // the second subtraction result interval will remain empty.
       
  2313         newEvent.Subtract(*lastOverlap, empty);
       
  2314         ASSERT( empty.IsEmpty() );
       
  2315     }
       
  2316 
       
  2317     // Create a new interval, representing the overlap between the old tail
       
  2318     // and the new event
       
  2319     CalenEventInterval newOverlap = newEvent;
       
  2320     newOverlap.iOverlap = true;
       
  2321     newOverlap.Intersect(tail);
       
  2322 
       
  2323     CalenEventInterval tailPart2 = tail; // initialize iOverlap and iStatus from tail
       
  2324     // Remove the new overlap from the old tail, possibly creating two separate intervals.
       
  2325     tail.Subtract(newOverlap, tailPart2);
       
  2326 
       
  2327     // If the subtraction only yielded one single interval, but it's
       
  2328     // after newOverlap, make tailPart2 contain that and make tail empty.
       
  2329     if (tail > newOverlap) {
       
  2330         tailPart2 = tail;
       
  2331         tail.iEndSlot = tail.iStartSlot;
       
  2332     }
       
  2333 
       
  2334     // Remove the new overlap from the new event. Since we already removed the old
       
  2335     // overlap, this subtraction can't produce two intervals either.
       
  2336     newEvent.Subtract(newOverlap, empty);
       
  2337     ASSERT( empty.IsEmpty() );
       
  2338 
       
  2339     // If the new overlap is adjacent to the old one, expand the old one
       
  2340     // and set the new overlap to be empty.
       
  2341     if (lastOverlap && newOverlap.Adjacent(*lastOverlap)) {
       
  2342         lastOverlap->Union(newOverlap);
       
  2343         newOverlap.iEndSlot = newOverlap.iStartSlot;
       
  2344     }
       
  2345 
       
  2346     // Add all the new intervals, if they're non-empty.
       
  2347     if (!tail.IsEmpty()) {
       
  2348         iIntervals.append(tail);
       
  2349     }
       
  2350     if (!newOverlap.IsEmpty()) {
       
  2351         iIntervals.append(newOverlap);
       
  2352     }
       
  2353     if (!tailPart2.IsEmpty()) {
       
  2354         iIntervals.append(tailPart2);
       
  2355     }
       
  2356     if (!newEvent.IsEmpty()) {
       
  2357         iIntervals.append(newEvent);
       
  2358     }
       
  2359 
       
  2360 }
       
  2361 
       
  2362 // End of File