diff --git a/calendarviews/prefs.h b/calendarviews/prefs.h --- a/calendarviews/prefs.h +++ b/calendarviews/prefs.h @@ -59,6 +59,10 @@ void readConfig(); void writeConfig(); + /** Get instance of Prefs. It is made sure that there is only one + instance. */ + static Prefs *instance(); + public: void setMarcusBainsShowSeconds( bool showSeconds ); bool marcusBainsShowSeconds() const; diff --git a/calendarviews/prefs.cpp b/calendarviews/prefs.cpp --- a/calendarviews/prefs.cpp +++ b/calendarviews/prefs.cpp @@ -31,6 +31,9 @@ using namespace EventViews; +K_GLOBAL_STATIC( Prefs, globalPrefs ) +K_GLOBAL_STATIC( Prefs, globalPrefsKorg ) + QSet iconArrayToSet( const QByteArray &array ) { QSet set; @@ -552,6 +555,21 @@ } } +Prefs *Prefs::instance() +{ + static bool firstCall = true; + + if ( firstCall ) { + firstCall = false; + KSharedConfigPtr pConfig = KSharedConfig::openConfig(QLatin1String("korganizerrc")); + globalPrefsKorg->d->mBaseConfig.setSharedConfig(pConfig); + globalPrefs->d->mAppConfig = &(globalPrefsKorg->d->mBaseConfig); + globalPrefs->readConfig(); + } + + return globalPrefs; +} + void Prefs::setMarcusBainsShowSeconds( bool showSeconds ) { d->setBool( d->mBaseConfig.marcusBainsShowSecondsItem(), showSeconds ); diff --git a/incidenceeditor-ng/conflictresolver.h b/incidenceeditor-ng/conflictresolver.h --- a/incidenceeditor-ng/conflictresolver.h +++ b/incidenceeditor-ng/conflictresolver.h @@ -114,6 +114,8 @@ FreeBusyItemModel *model() const; + bool workingHoursOnly() const; + signals: /** * Emitted when the user changes the start and end dateTimes @@ -146,6 +148,10 @@ void setEarliestDateTime( const KDateTime &newDateTime ); void setLatestDateTime( const KDateTime &newDateTime ); + void setWorkingHours(QTime start, QTime end); + void setWorkingHoursOnly(bool workingTime); + void setDuration(int duration); //Duration of the event + void freebusyDataChanged(); void findAllFreeSlots(); @@ -196,6 +202,11 @@ //(bit 0 = Monday, value 1 = allowed). int mSlotResolutionSeconds; + + bool mWorkingHoursOnly; //!< Search only in working times + int mDuration; //!< Duration of event to schedule + QTime mWorkingHoursStart; + QTime mWorkingHoursEnd; }; } diff --git a/incidenceeditor-ng/conflictresolver.cpp b/incidenceeditor-ng/conflictresolver.cpp --- a/incidenceeditor-ng/conflictresolver.cpp +++ b/incidenceeditor-ng/conflictresolver.cpp @@ -36,7 +36,9 @@ mFBModel( new FreeBusyItemModel( this ) ), mParentWidget( parentWidget ), mWeekdays( 7 ), - mSlotResolutionSeconds( DEFAULT_RESOLUTION_SECONDS ) + mSlotResolutionSeconds( DEFAULT_RESOLUTION_SECONDS ), + mWorkingHoursOnly(false), + mDuration(0) { // trigger a reload in case any attendees were inserted before @@ -141,6 +143,31 @@ calculateConflicts(); } +void ConflictResolver::setWorkingHoursOnly(bool workingTime) +{ + mWorkingHoursOnly = workingTime; + calculateConflicts(); +} + +bool ConflictResolver::workingHoursOnly() const +{ + return mWorkingHoursOnly; +} + +void ConflictResolver::setWorkingHours(QTime start, QTime end) +{ + mWorkingHoursStart = start; + mWorkingHoursEnd = end; + calculateConflicts(); +} + + +void ConflictResolver::setDuration(int duration) +{ + mDuration = duration; + calculateConflicts(); +} + int ConflictResolver::tryDate( KDateTime &tryFrom, KDateTime &tryTo ) { int conflicts_count = 0; @@ -242,6 +269,14 @@ const KDateTime begin = mTimeframeConstraint.start(); const KDateTime end = mTimeframeConstraint.end(); + const int duration = mDuration / mSlotResolutionSeconds; + + bool workingHoursOnly = mWorkingHoursOnly; + + if (workingHoursOnly && (!mWorkingHoursStart.isValid() || !mWorkingHoursStart.isValid() )) { + workingHoursOnly = false; + } + // calculate the time resolution // each timeslot in the arrays represents a unit of time // specified here. @@ -255,6 +290,8 @@ // 1 week = 10080 minutes / 15 = 672 15 min timeslots // So, the array would have a length of 672 const int range = begin.secsTo( end ) / mSlotResolutionSeconds; + + if ( range <= 0 ) { kWarning() << "free slot calculation: invalid range. range( " << begin.secsTo( end ) << ") / mSlotResolutionSeconds(" << mSlotResolutionSeconds << ") = " << range; @@ -263,7 +300,8 @@ kDebug() << "from " << begin << " to " << end << "; mSlotResolutionSeconds = " << mSlotResolutionSeconds - << "; range = " << range; + << "; range = " << range + << "; workingtime =" << workingHoursOnly; // filter out attendees for which we don't have FB data // and which don't match the mandatory role contrstaint @@ -359,6 +397,8 @@ const int dayOfWeek = calSys->dayOfWeek( dateTime.date() ) - 1; // bitarray is 0 indexed if ( !mWeekdays[dayOfWeek] ) { fbArray[slot] = 1; + } else if (workingHoursOnly && !(dateTime.time() >= mWorkingHoursStart && dateTime.time() < mWorkingHoursEnd)) { + fbArray[slot] = 1; } } fbTable.append( fbArray ); @@ -375,6 +415,20 @@ } } + //if we know the event duration, skipall slotswith less time for the whole meeting + if (duration > 0) { + for (int i = 0; i < range - duration; ++i ) { + if (summed[i] == 0) { + for (int j = 1; j <= duration; j++) { + if (summed[i+j] != 0) { + summed[i] = 1; + break; + } + } + } + } + } + // Finally, iterate through the composite array locating contiguous free timeslots int free_count = 0; bool free_found = false; diff --git a/incidenceeditor-ng/incidenceattendee.cpp b/incidenceeditor-ng/incidenceattendee.cpp --- a/incidenceeditor-ng/incidenceattendee.cpp +++ b/incidenceeditor-ng/incidenceattendee.cpp @@ -31,6 +31,7 @@ #include "schedulingdialog.h" #include "attendeecomboboxdelegate.h" #include "freebusymodel/freebusyitemmodel.h" +#include #ifdef KDEPIM_MOBILE_UI #include "ui_dialogmoremobile.h" #else @@ -149,6 +150,9 @@ mConflictResolver->setEarliestDateTime( mDateTime->currentStartDateTime() ); mConflictResolver->setLatestDateTime( mDateTime->currentEndDateTime() ); + EventViews::Prefs *eventviewsPrefs = EventViews::Prefs::instance(); + mConflictResolver->setWorkingHours(eventviewsPrefs->workingHoursStart().time(), eventviewsPrefs->workingHoursEnd().time()); + connect( mUi->mSelectButton, SIGNAL(clicked(bool)), this, SLOT(slotSelectAddresses()) ); connect( mUi->mSolveButton, SIGNAL(clicked(bool)), @@ -517,6 +521,7 @@ void IncidenceEditorNG::IncidenceAttendee::slotSolveConflictPressed() { const int duration = mDateTime->startTime().secsTo( mDateTime->endTime() ); + mConflictResolver->setDuration(duration); QScopedPointer dialog( new SchedulingDialog( mDateTime->startDate(), mDateTime->startTime(), duration, mConflictResolver, diff --git a/incidenceeditor-ng/schedulingdialog.cpp b/incidenceeditor-ng/schedulingdialog.cpp --- a/incidenceeditor-ng/schedulingdialog.cpp +++ b/incidenceeditor-ng/schedulingdialog.cpp @@ -58,6 +58,8 @@ mResolver, SLOT(setLatestDate(QDate)) ); connect( mEndTime, SIGNAL(timeEdited(QTime)), mResolver, SLOT(setLatestTime(QTime)) ); + connect(mSearchOnlyWorkingTime, SIGNAL(toggled(bool)), + mResolver, SLOT(setWorkingHoursOnly(bool))); connect( mStartDate, SIGNAL(dateEdited(QDate)), this, SLOT(slotStartDateChanged(QDate)) ); @@ -87,6 +89,7 @@ mResolver->setEarliestTime( mStartTime->time() ); mResolver->setLatestDate( mEndDate->date() ); mResolver->setLatestTime( mEndTime->time() ); + mSearchOnlyWorkingTime->setChecked(mResolver->workingHoursOnly()); mMoveApptGroupBox->hide(); } diff --git a/incidenceeditor-ng/schedulingdialog.ui b/incidenceeditor-ng/schedulingdialog.ui --- a/incidenceeditor-ng/schedulingdialog.ui +++ b/incidenceeditor-ng/schedulingdialog.ui @@ -132,6 +132,20 @@ + + + + + + + + + + + Seach only in working time + + + diff --git a/incidenceeditor-ng/tests/conflictresolvertest.h b/incidenceeditor-ng/tests/conflictresolvertest.h --- a/incidenceeditor-ng/tests/conflictresolvertest.h +++ b/incidenceeditor-ng/tests/conflictresolvertest.h @@ -40,6 +40,8 @@ void init(); void cleanup(); void simpleTest(); + void durationTest(); + void workingHoursTest(); void stillPrettySimpleTest(); void akademy2010(); void testPeriodBeginsBeforeTimeframeBegins(); diff --git a/incidenceeditor-ng/tests/conflictresolvertest.cpp b/incidenceeditor-ng/tests/conflictresolvertest.cpp --- a/incidenceeditor-ng/tests/conflictresolvertest.cpp +++ b/incidenceeditor-ng/tests/conflictresolvertest.cpp @@ -105,6 +105,68 @@ } +void ConflictResolverTest::durationTest() +{ + KCalCore::Period meeting( end.addSecs( -3 * 60 * 60 ), KCalCore::Duration( 2 * 60 * 60 ) ); + addAttendee( "albert@einstein.net", + KCalCore::FreeBusy::Ptr( new KCalCore::FreeBusy( KCalCore::Period::List() + << meeting ) ) ); + + insertAttendees(); + + static const int resolution = 15 * 60; + resolver->setResolution( resolution ); + resolver->setEarliestDateTime( base ); + resolver->setLatestDateTime( end ); + resolver->setDuration( 2 * resolution ); + resolver->findAllFreeSlots(); + + QVERIFY( resolver->availableSlots().size() == 2 ); + + KCalCore::Period first = resolver->availableSlots().at( 0 ); + QCOMPARE( first.start(), base ); + QCOMPARE( first.end(), meeting.start().addSecs(-2 * resolution) ); +} + +void ConflictResolverTest::workingHoursTest() +{ + /* + * to got a valid result of these test we need workStart < workEnd, + * to schedule over the day and not over midnight. + * we get this, if set the time of "base" to something < 12am in our case 4am + */ + KDateTime base = KDateTime::currentLocalDateTime().addDays(1); + base.setTime(QTime(4,0)); + const KDateTime end = base.addSecs(10 * 60 * 60); + KCalCore::Period meeting( end.addSecs( -3 * 60 * 60 ), KCalCore::Duration( 2 * 60 * 60 ) ); + addAttendee( "albert@einstein.net", + KCalCore::FreeBusy::Ptr( new KCalCore::FreeBusy( KCalCore::Period::List() + << meeting ) ) ); + + insertAttendees(); + + static const int resolution = 15 * 60; + const KDateTime workStart = base.addSecs(2*resolution); + const KDateTime workEnd = end.addSecs(-2*resolution); + resolver->setResolution( resolution ); + resolver->setEarliestDateTime( base ); + resolver->setLatestDateTime( end ); + resolver->setWorkingHours(workStart.time(), workEnd.time()); + resolver->setWorkingHoursOnly(true); + resolver->findAllFreeSlots(); + + QCOMPARE( resolver->availableSlots().size(), 2 ); + + KCalCore::Period first = resolver->availableSlots().at( 0 ); + QCOMPARE( first.start(), workStart ); + QCOMPARE( first.end(), meeting.start() ); + + KCalCore::Period second = resolver->availableSlots().at( 1 ); + QCOMPARE( second.start(), meeting.end()); + QCOMPARE( second.end(), workEnd); + +} + void ConflictResolverTest::stillPrettySimpleTest() { KCalCore::Period meeting1( base, KCalCore::Duration( 2 * 60 * 60 ) );