diff -Nru indicator-datetime-13.10.0+14.04.20140311.1/debian/changelog indicator-datetime-13.10.0+14.04.20140314.1/debian/changelog --- indicator-datetime-13.10.0+14.04.20140311.1/debian/changelog 2014-03-17 15:20:21.000000000 +0000 +++ indicator-datetime-13.10.0+14.04.20140314.1/debian/changelog 2014-03-17 15:20:21.000000000 +0000 @@ -1,3 +1,14 @@ +indicator-datetime (13.10.0+14.04.20140314.1-0ubuntu1) trusty; urgency=low + + [ Charles Kerr ] + * When the user clicks on a date in the calendar, update the "Upcoming + Events" section to show events starting at that date. (LP: #1290169) + * Don't use EDS if we're in the greeter. (LP: #1256130) + * Don't show the "Add Event..." button if the calendar app can't be + found. (LP: #1250632) + + -- Ubuntu daily release Fri, 14 Mar 2014 17:37:32 +0000 + indicator-datetime (13.10.0+14.04.20140311.1-0ubuntu1) trusty; urgency=low [ Lars Uebernickel ] diff -Nru indicator-datetime-13.10.0+14.04.20140311.1/include/datetime/actions.h indicator-datetime-13.10.0+14.04.20140314.1/include/datetime/actions.h --- indicator-datetime-13.10.0+14.04.20140311.1/include/datetime/actions.h 2014-03-11 18:16:26.000000000 +0000 +++ indicator-datetime-13.10.0+14.04.20140314.1/include/datetime/actions.h 2014-03-14 17:37:20.000000000 +0000 @@ -45,6 +45,7 @@ virtual void open_desktop_settings() =0; virtual void open_phone_settings() =0; virtual void open_phone_clock_app() =0; + virtual bool can_open_planner() const = 0; virtual void open_planner() =0; virtual void open_planner_at(const DateTime&) =0; virtual void open_appointment(const std::string& uid) =0; diff -Nru indicator-datetime-13.10.0+14.04.20140311.1/include/datetime/actions-live.h indicator-datetime-13.10.0+14.04.20140314.1/include/datetime/actions-live.h --- indicator-datetime-13.10.0+14.04.20140311.1/include/datetime/actions-live.h 2014-03-11 18:16:26.000000000 +0000 +++ indicator-datetime-13.10.0+14.04.20140314.1/include/datetime/actions-live.h 2014-03-14 17:37:20.000000000 +0000 @@ -42,6 +42,7 @@ void open_desktop_settings(); void open_phone_settings(); void open_phone_clock_app(); + bool can_open_planner() const; void open_planner(); void open_planner_at(const DateTime&); void open_appointment(const std::string& uid); diff -Nru indicator-datetime-13.10.0+14.04.20140311.1/include/datetime/clock-watcher.h indicator-datetime-13.10.0+14.04.20140314.1/include/datetime/clock-watcher.h --- indicator-datetime-13.10.0+14.04.20140311.1/include/datetime/clock-watcher.h 2014-03-11 18:16:26.000000000 +0000 +++ indicator-datetime-13.10.0+14.04.20140314.1/include/datetime/clock-watcher.h 2014-03-14 17:36:59.000000000 +0000 @@ -20,8 +20,9 @@ #ifndef INDICATOR_DATETIME_CLOCK_WATCHER_H #define INDICATOR_DATETIME_CLOCK_WATCHER_H -#include #include +#include +#include #include @@ -53,14 +54,16 @@ class ClockWatcherImpl: public ClockWatcher { public: - ClockWatcherImpl(const std::shared_ptr& state); + ClockWatcherImpl(const std::shared_ptr& clock, + const std::shared_ptr& upcoming_planner); ~ClockWatcherImpl() =default; core::Signal& alarm_reached(); private: void pulse(); std::set m_triggered; - std::shared_ptr m_state; + const std::shared_ptr m_clock; + const std::shared_ptr m_upcoming_planner; core::Signal m_alarm_reached; }; diff -Nru indicator-datetime-13.10.0+14.04.20140311.1/include/datetime/date-time.h indicator-datetime-13.10.0+14.04.20140314.1/include/datetime/date-time.h --- indicator-datetime-13.10.0+14.04.20140311.1/include/datetime/date-time.h 2014-03-11 18:16:26.000000000 +0000 +++ indicator-datetime-13.10.0+14.04.20140314.1/include/datetime/date-time.h 2014-03-14 17:36:59.000000000 +0000 @@ -36,6 +36,8 @@ { public: static DateTime NowLocal(); + static DateTime Local(int years, int months, int days, int hours, int minutes, int seconds); + explicit DateTime(time_t t); explicit DateTime(GDateTime* in=nullptr); DateTime& operator=(GDateTime* in); @@ -48,7 +50,10 @@ GDateTime* operator()() const {return get();} std::string format(const std::string& fmt) const; + void ymd(int& year, int& month, int& day) const; int day_of_month() const; + int hour() const; + int minute() const; double seconds() const; int64_t to_unix() const; diff -Nru indicator-datetime-13.10.0+14.04.20140311.1/include/datetime/engine-eds.h indicator-datetime-13.10.0+14.04.20140314.1/include/datetime/engine-eds.h --- indicator-datetime-13.10.0+14.04.20140311.1/include/datetime/engine-eds.h 1970-01-01 00:00:00.000000000 +0000 +++ indicator-datetime-13.10.0+14.04.20140314.1/include/datetime/engine-eds.h 2014-03-14 17:37:09.000000000 +0000 @@ -0,0 +1,75 @@ +/* + * Copyright 2014 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Charles Kerr + */ + +#ifndef INDICATOR_DATETIME_ENGINE_EDS__H +#define INDICATOR_DATETIME_ENGINE_EDS__H + +#include + +#include +#include +#include + +#include +#include + +namespace unity { +namespace indicator { +namespace datetime { + +/**** +***** +****/ + +/** + * Class wrapper around EDS so multiple #EdsPlanners can share resources + * + * @see EdsPlanner + */ +class EdsEngine: public Engine +{ +public: + EdsEngine(); + ~EdsEngine(); + + void get_appointments(const DateTime& begin, + const DateTime& end, + const Timezone& default_timezone, + std::function&)> appointment_func); + + core::Signal<>& changed(); + +private: + class Impl; + std::unique_ptr p; + + // we've got a unique_ptr here, disable copying... + EdsEngine(const EdsEngine&) =delete; + EdsEngine& operator=(const EdsEngine&) =delete; +}; + +/*** +**** +***/ + +} // namespace datetime +} // namespace indicator +} // namespace unity + +#endif // INDICATOR_DATETIME_ENGINE_EDS__H diff -Nru indicator-datetime-13.10.0+14.04.20140311.1/include/datetime/engine.h indicator-datetime-13.10.0+14.04.20140314.1/include/datetime/engine.h --- indicator-datetime-13.10.0+14.04.20140311.1/include/datetime/engine.h 1970-01-01 00:00:00.000000000 +0000 +++ indicator-datetime-13.10.0+14.04.20140314.1/include/datetime/engine.h 2014-03-14 17:37:09.000000000 +0000 @@ -0,0 +1,68 @@ +/* + * Copyright 2014 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Charles Kerr + */ + +#ifndef INDICATOR_DATETIME_ENGINE__H +#define INDICATOR_DATETIME_ENGINE__H + +#include +#include +#include + +#include +#include + +namespace unity { +namespace indicator { +namespace datetime { + +/**** +***** +****/ + +/** + * Class wrapper around the backend that generates appointments + * + * @see EdsEngine + * @see EdsPlanner + */ +class Engine +{ +public: + virtual ~Engine() =default; + + virtual void get_appointments(const DateTime& begin, + const DateTime& end, + const Timezone& default_timezone, + std::function&)> appointment_func) =0; + + virtual core::Signal<>& changed() =0; + +protected: + Engine() =default; +}; + +/*** +**** +***/ + +} // namespace datetime +} // namespace indicator +} // namespace unity + +#endif // INDICATOR_DATETIME_ENGINE__H diff -Nru indicator-datetime-13.10.0+14.04.20140311.1/include/datetime/engine-mock.h indicator-datetime-13.10.0+14.04.20140314.1/include/datetime/engine-mock.h --- indicator-datetime-13.10.0+14.04.20140311.1/include/datetime/engine-mock.h 1970-01-01 00:00:00.000000000 +0000 +++ indicator-datetime-13.10.0+14.04.20140314.1/include/datetime/engine-mock.h 2014-03-14 17:37:09.000000000 +0000 @@ -0,0 +1,68 @@ +/* + * Copyright 2014 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Charles Kerr + */ + +#ifndef INDICATOR_DATETIME_ENGINE_MOCK__H +#define INDICATOR_DATETIME_ENGINE_MOCK__H + +#include + +namespace unity { +namespace indicator { +namespace datetime { + +/**** +***** +****/ + +/** + * A no-op #Engine + * + * @see Engine + */ +class MockEngine: public Engine +{ +public: + MockEngine() =default; + ~MockEngine() =default; + + void get_appointments(const DateTime& /*begin*/, + const DateTime& /*end*/, + const Timezone& /*default_timezone*/, + std::function&)> appointment_func) { + appointment_func(m_appointments); + } + + core::Signal<>& changed() { + return m_changed; + } + +private: + core::Signal<> m_changed; + std::vector m_appointments; +}; + +/*** +**** +***/ + +} // namespace datetime +} // namespace indicator +} // namespace unity + +#endif // INDICATOR_DATETIME_ENGINE_NOOP__H diff -Nru indicator-datetime-13.10.0+14.04.20140311.1/include/datetime/planner-eds.h indicator-datetime-13.10.0+14.04.20140314.1/include/datetime/planner-eds.h --- indicator-datetime-13.10.0+14.04.20140311.1/include/datetime/planner-eds.h 2014-03-11 18:16:26.000000000 +0000 +++ indicator-datetime-13.10.0+14.04.20140314.1/include/datetime/planner-eds.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,50 +0,0 @@ -/* - * Copyright 2013 Canonical Ltd. - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 3, as published - * by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranties of - * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - * - * Authors: - * Charles Kerr - */ - -#ifndef INDICATOR_DATETIME_PLANNER_EDS_H -#define INDICATOR_DATETIME_PLANNER_EDS_H - -#include -#include - -#include // shared_ptr, unique_ptr - -namespace unity { -namespace indicator { -namespace datetime { - -/** - * \brief Planner which uses EDS as its backend - */ -class PlannerEds: public Planner -{ -public: - PlannerEds(const std::shared_ptr& clock); - virtual ~PlannerEds(); - -private: - class Impl; - std::unique_ptr p; -}; - -} // namespace datetime -} // namespace indicator -} // namespace unity - -#endif // INDICATOR_DATETIME_PLANNER_EDS_H diff -Nru indicator-datetime-13.10.0+14.04.20140311.1/include/datetime/planner.h indicator-datetime-13.10.0+14.04.20140314.1/include/datetime/planner.h --- indicator-datetime-13.10.0+14.04.20140311.1/include/datetime/planner.h 2014-03-11 18:16:26.000000000 +0000 +++ indicator-datetime-13.10.0+14.04.20140314.1/include/datetime/planner.h 2014-03-14 17:36:59.000000000 +0000 @@ -32,41 +32,16 @@ namespace datetime { /** - * \brief Simple appointment book - * - * @see EdsPlanner - * @see State + * \brief Simple collection of appointments */ class Planner { public: virtual ~Planner() =default; - - /** - * \brief Timestamp used to determine the appointments in the `upcoming' and `this_month' properties. - * Setting this value will cause the planner to re-query its backend and - * update the `upcoming' and `this_month' properties. - */ - core::Property time; - - /** - * \brief The next few appointments that follow the time specified in the time property. - */ - core::Property> upcoming; - - /** - * \brief The appointments that occur in the same month as the time property - */ - core::Property> this_month; + virtual core::Property>& appointments() =0; protected: Planner() =default; - -private: - - // disable copying - Planner(const Planner&) =delete; - Planner& operator=(const Planner&) =delete; }; } // namespace datetime diff -Nru indicator-datetime-13.10.0+14.04.20140311.1/include/datetime/planner-month.h indicator-datetime-13.10.0+14.04.20140314.1/include/datetime/planner-month.h --- indicator-datetime-13.10.0+14.04.20140311.1/include/datetime/planner-month.h 1970-01-01 00:00:00.000000000 +0000 +++ indicator-datetime-13.10.0+14.04.20140314.1/include/datetime/planner-month.h 2014-03-14 17:36:59.000000000 +0000 @@ -0,0 +1,56 @@ +/* + * Copyright 2014 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Charles Kerr + */ + +#ifndef INDICATOR_DATETIME_PLANNER_MONTH_H +#define INDICATOR_DATETIME_PLANNER_MONTH_H + +#include + +#include +#include + +#include // std::shared_ptr + +namespace unity { +namespace indicator { +namespace datetime { + +/** + * \brief A #Planner that contains appointments for a specified calendar month + */ +class MonthPlanner: public Planner +{ +public: + MonthPlanner(const std::shared_ptr& range_planner, + const DateTime& month_in); + ~MonthPlanner() =default; + + core::Property>& appointments(); + core::Property& month(); + +private: + std::shared_ptr m_range_planner; + core::Property m_month; +}; + +} // namespace datetime +} // namespace indicator +} // namespace unity + +#endif // INDICATOR_DATETIME_PLANNER_MONTH_H diff -Nru indicator-datetime-13.10.0+14.04.20140311.1/include/datetime/planner-range.h indicator-datetime-13.10.0+14.04.20140314.1/include/datetime/planner-range.h --- indicator-datetime-13.10.0+14.04.20140311.1/include/datetime/planner-range.h 1970-01-01 00:00:00.000000000 +0000 +++ indicator-datetime-13.10.0+14.04.20140314.1/include/datetime/planner-range.h 2014-03-14 17:37:09.000000000 +0000 @@ -0,0 +1,84 @@ +/* + * Copyright 2014 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Charles Kerr + */ + +#ifndef INDICATOR_DATETIME_PLANNER_RANGE_H +#define INDICATOR_DATETIME_PLANNER_RANGE_H + +#include + +#include +#include + +namespace unity { +namespace indicator { +namespace datetime { + +/** + * \brief A #Planner that contains appointments in a specified date range + * + * @see Planner + */ +class RangePlanner: public Planner +{ +public: + virtual ~RangePlanner() =default; + virtual core::Property>& range() =0; + +protected: + RangePlanner() =default; +}; + +/** + * \brief A #RangePlanner that uses an #Engine to generate appointments + * + * @see Planner + */ +class SimpleRangePlanner: public RangePlanner +{ +public: + SimpleRangePlanner(const std::shared_ptr& engine, + const std::shared_ptr& timezone); + virtual ~SimpleRangePlanner(); + + core::Property>& appointments(); + core::Property>& range(); + +private: + // rebuild scaffolding + void rebuild_soon(); + virtual void rebuild_now(); + static gboolean rebuild_now_static(gpointer); + guint m_rebuild_tag = 0; + + std::shared_ptr m_engine; + std::shared_ptr m_timezone; + core::Property> m_range; + core::Property> m_appointments; + + // we've got a GSignal tag here, so disable copying + SimpleRangePlanner(const RangePlanner&) =delete; + SimpleRangePlanner& operator=(const RangePlanner&) =delete; +}; + + +} // namespace datetime +} // namespace indicator +} // namespace unity + +#endif // INDICATOR_DATETIME_PLANNER_RANGE_H diff -Nru indicator-datetime-13.10.0+14.04.20140311.1/include/datetime/planner-upcoming.h indicator-datetime-13.10.0+14.04.20140314.1/include/datetime/planner-upcoming.h --- indicator-datetime-13.10.0+14.04.20140311.1/include/datetime/planner-upcoming.h 1970-01-01 00:00:00.000000000 +0000 +++ indicator-datetime-13.10.0+14.04.20140314.1/include/datetime/planner-upcoming.h 2014-03-14 17:36:59.000000000 +0000 @@ -0,0 +1,56 @@ +/* + * Copyright 2014 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Charles Kerr + */ + +#ifndef INDICATOR_DATETIME_PLANNER_UPCOMING_H +#define INDICATOR_DATETIME_PLANNER_UPCOMING_H + +#include + +#include +#include + +#include // std::shared_ptr + +namespace unity { +namespace indicator { +namespace datetime { + +/** + * \brief A collection of upcoming appointments starting from the specified date + */ +class UpcomingPlanner: public Planner +{ +public: + UpcomingPlanner(const std::shared_ptr& range_planner, + const DateTime& date); + ~UpcomingPlanner() =default; + + core::Property>& appointments(); + core::Property& date(); + +private: + std::shared_ptr m_range_planner; + core::Property m_date; +}; + +} // namespace datetime +} // namespace indicator +} // namespace unity + +#endif // INDICATOR_DATETIME_PLANNER_UPCOMING_H diff -Nru indicator-datetime-13.10.0+14.04.20140311.1/include/datetime/state.h indicator-datetime-13.10.0+14.04.20140314.1/include/datetime/state.h --- indicator-datetime-13.10.0+14.04.20140311.1/include/datetime/state.h 2014-03-11 18:16:26.000000000 +0000 +++ indicator-datetime-13.10.0+14.04.20140314.1/include/datetime/state.h 2014-03-14 17:36:59.000000000 +0000 @@ -22,7 +22,8 @@ #include #include -#include +#include +#include #include #include @@ -60,9 +61,14 @@ section of the #Menu */ std::shared_ptr locations; - /** \brief The appointments to be displayed in the Calendar and - Appointments sections of the #Menu */ - std::shared_ptr planner; + /** \brief Appointments in the month that's being displayed + in the calendar section of the #Menu */ + std::shared_ptr calendar_month; + + /** \brief The next appointments that follow highlighed date + highlighted in the calendar section of the #Menu + (default date = today) */ + std::shared_ptr calendar_upcoming; /** \brief Configuration options that modify the view */ std::shared_ptr settings; diff -Nru indicator-datetime-13.10.0+14.04.20140311.1/src/actions.cpp indicator-datetime-13.10.0+14.04.20140314.1/src/actions.cpp --- indicator-datetime-13.10.0+14.04.20140311.1/src/actions.cpp 2014-03-11 18:16:26.000000000 +0000 +++ indicator-datetime-13.10.0+14.04.20140314.1/src/actions.cpp 2014-03-14 17:36:59.000000000 +0000 @@ -65,7 +65,7 @@ g_return_if_fail(uid && *uid); // find url of the upcoming appointment with this uid - for (const auto& appt : self->state()->planner->upcoming.get()) + for (const auto& appt : self->state()->calendar_upcoming->appointments().get()) { if (appt.uid == uid) { @@ -146,7 +146,7 @@ GVariant* create_calendar_state(const std::shared_ptr& state) { gboolean days[32] = { 0 }; - for (const auto& appt : state->planner->this_month.get()) + for (const auto& appt : state->calendar_month->appointments().get()) days[appt.begin.day_of_month()] = true; GVariantBuilder day_builder; @@ -163,7 +163,7 @@ g_variant_builder_add(&dict_builder, "{sv}", key, v); key = "calendar-day"; - v = g_variant_new_int64(state->planner->time.get().to_unix()); + v = g_variant_new_int64(state->calendar_month->month().get().to_unix()); g_variant_builder_add(&dict_builder, "{sv}", key, v); key = "show-week-numbers"; @@ -219,10 +219,10 @@ /// Keep our GActionGroup's action's states in sync with m_state /// - m_state->planner->time.changed().connect([this](const DateTime&){ + m_state->calendar_month->month().changed().connect([this](const DateTime&){ update_calendar_state(); }); - m_state->planner->this_month.changed().connect([this](const std::vector&){ + m_state->calendar_month->appointments().changed().connect([this](const std::vector&){ update_calendar_state(); }); m_state->settings->show_week_numbers.changed().connect([this](bool){ @@ -246,7 +246,8 @@ void Actions::set_calendar_date(const DateTime& date) { - m_state->planner->time.set(date); + m_state->calendar_month->month().set(date); + m_state->calendar_upcoming->date().set(date); } GActionGroup* Actions::action_group() diff -Nru indicator-datetime-13.10.0+14.04.20140311.1/src/actions-live.cpp indicator-datetime-13.10.0+14.04.20140314.1/src/actions-live.cpp --- indicator-datetime-13.10.0+14.04.20140311.1/src/actions-live.cpp 2014-03-11 18:16:26.000000000 +0000 +++ indicator-datetime-13.10.0+14.04.20140314.1/src/actions-live.cpp 2014-03-14 17:37:20.000000000 +0000 @@ -74,6 +74,30 @@ g_free (path); } +bool LiveActions::can_open_planner() const +{ + static bool inited = false; + static bool have_calendar = false; + + if (G_UNLIKELY(!inited)) + { + inited = true; + + auto all = g_app_info_get_all_for_type ("text/calendar"); + for(auto l=all; !have_calendar && l!=nullptr; l=l->next) + { + auto app_info = static_cast(l->data); + + if (!g_strcmp0("evolution.desktop", g_app_info_get_id(app_info))) + have_calendar = true; + } + + g_list_free_full(all, (GDestroyNotify)g_object_unref); + } + + return have_calendar; +} + void LiveActions::open_planner() { execute_command("evolution -c calendar"); @@ -91,13 +115,15 @@ void LiveActions::open_planner_at(const DateTime& dt) { - auto cmd = dt.format("evolution \"calendar:///?startdate=%Y%m%d\""); + const auto day_begins = dt.add_full(0, 0, 0, -dt.hour(), -dt.minute(), -dt.seconds()); + const auto gmt = day_begins.to_timezone("UTC"); + auto cmd = gmt.format("evolution \"calendar:///?startdate=%Y%m%dT%H%M%SZ\""); execute_command(cmd.c_str()); } void LiveActions::open_appointment(const std::string& uid) { - for(const auto& appt : state()->planner->upcoming.get()) + for(const auto& appt : state()->calendar_upcoming->appointments().get()) { if(appt.uid != uid) continue; diff -Nru indicator-datetime-13.10.0+14.04.20140311.1/src/clock-watcher.cpp indicator-datetime-13.10.0+14.04.20140314.1/src/clock-watcher.cpp --- indicator-datetime-13.10.0+14.04.20140311.1/src/clock-watcher.cpp 2014-03-11 18:16:26.000000000 +0000 +++ indicator-datetime-13.10.0+14.04.20140314.1/src/clock-watcher.cpp 2014-03-14 17:36:59.000000000 +0000 @@ -27,17 +27,27 @@ **** ***/ -ClockWatcherImpl::ClockWatcherImpl(const std::shared_ptr& state): - m_state(state) +ClockWatcherImpl::ClockWatcherImpl(const std::shared_ptr& clock, + const std::shared_ptr& upcoming_planner): + m_clock(clock), + m_upcoming_planner(upcoming_planner) { - m_state->planner->upcoming.changed().connect([this](const std::vector&){ - g_debug("ClockWatcher pulse because upcoming appointments changed"); + m_clock->date_changed.connect([this](){ + const auto now = m_clock->localtime(); + g_debug("ClockWatcher %p refretching appointments due to date change: %s", this, now.format("%F %T").c_str()); + m_upcoming_planner->date().set(now); + }); + + m_clock->minute_changed.connect([this](){ + g_debug("ClockWatcher %p calling pulse() due to clock minute_changed", this); pulse(); }); - m_state->clock->minute_changed.connect([this](){ - g_debug("ClockWatcher pulse because clock minute_changed"); + + m_upcoming_planner->appointments().changed().connect([this](const std::vector&){ + g_debug("ClockWatcher %p calling pulse() due to appointments changed", this); pulse(); }); + pulse(); } @@ -48,9 +58,9 @@ void ClockWatcherImpl::pulse() { - const auto now = m_state->clock->localtime(); + const auto now = m_clock->localtime(); - for(const auto& appointment : m_state->planner->upcoming.get()) + for(const auto& appointment : m_upcoming_planner->appointments().get()) { if (m_triggered.count(appointment.uid)) continue; diff -Nru indicator-datetime-13.10.0+14.04.20140311.1/src/CMakeLists.txt indicator-datetime-13.10.0+14.04.20140314.1/src/CMakeLists.txt --- indicator-datetime-13.10.0+14.04.20140311.1/src/CMakeLists.txt 2014-03-11 18:16:26.000000000 +0000 +++ indicator-datetime-13.10.0+14.04.20140314.1/src/CMakeLists.txt 2014-03-14 17:37:09.000000000 +0000 @@ -15,13 +15,16 @@ clock-live.cpp clock-watcher.cpp date-time.cpp + engine-eds.cpp exporter.cpp formatter.cpp formatter-desktop.cpp locations.cpp locations-settings.cpp menu.cpp - planner-eds.cpp + planner-month.cpp + planner-range.cpp + planner-upcoming.cpp settings-live.cpp snap.cpp timezone-file.cpp diff -Nru indicator-datetime-13.10.0+14.04.20140311.1/src/date-time.cpp indicator-datetime-13.10.0+14.04.20140314.1/src/date-time.cpp --- indicator-datetime-13.10.0+14.04.20140311.1/src/date-time.cpp 2014-03-11 18:16:26.000000000 +0000 +++ indicator-datetime-13.10.0+14.04.20140314.1/src/date-time.cpp 2014-03-14 17:36:59.000000000 +0000 @@ -46,14 +46,22 @@ DateTime::DateTime(time_t t) { - GDateTime * gdt = g_date_time_new_from_unix_local(t); + auto gdt = g_date_time_new_from_unix_local(t); reset(gdt); g_date_time_unref(gdt); } DateTime DateTime::NowLocal() { - GDateTime * gdt = g_date_time_new_now_local(); + auto gdt = g_date_time_new_now_local(); + DateTime dt(gdt); + g_date_time_unref(gdt); + return dt; +} + +DateTime DateTime::Local(int year, int month, int day, int hour, int minute, int seconds) +{ + auto gdt = g_date_time_new_local (year, month, day, hour, minute, seconds); DateTime dt(gdt); g_date_time_unref(gdt); return dt; @@ -97,11 +105,26 @@ return ret; } +void DateTime::ymd(int& year, int& month, int& day) const +{ + g_date_time_get_ymd(get(), &year, &month, &day); +} + int DateTime::day_of_month() const { return g_date_time_get_day_of_month(get()); } +int DateTime::hour() const +{ + return g_date_time_get_hour(get()); +} + +int DateTime::minute() const +{ + return g_date_time_get_minute(get()); +} + double DateTime::seconds() const { return g_date_time_get_seconds(get()); diff -Nru indicator-datetime-13.10.0+14.04.20140311.1/src/engine-eds.cpp indicator-datetime-13.10.0+14.04.20140314.1/src/engine-eds.cpp --- indicator-datetime-13.10.0+14.04.20140311.1/src/engine-eds.cpp 1970-01-01 00:00:00.000000000 +0000 +++ indicator-datetime-13.10.0+14.04.20140314.1/src/engine-eds.cpp 2014-03-14 17:36:59.000000000 +0000 @@ -0,0 +1,531 @@ +/* + * Copyright 2014 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Charles Kerr + */ + +#include + +#include +#include +#include +#include + +#include // std::sort() +#include +#include + +namespace unity { +namespace indicator { +namespace datetime { + +/**** +***** +****/ + +class EdsEngine::Impl +{ +public: + + Impl(EdsEngine& owner): + m_owner(owner), + m_cancellable(g_cancellable_new()) + { + e_source_registry_new(m_cancellable, on_source_registry_ready, this); + } + + ~Impl() + { + g_cancellable_cancel(m_cancellable); + g_clear_object(&m_cancellable); + + while(!m_sources.empty()) + remove_source(*m_sources.begin()); + + if (m_rebuild_tag) + g_source_remove(m_rebuild_tag); + + if (m_source_registry) + g_signal_handlers_disconnect_by_data(m_source_registry, this); + g_clear_object(&m_source_registry); + } + + core::Signal<>& changed() + { + return m_changed; + } + + void get_appointments(const DateTime& begin, + const DateTime& end, + const Timezone& timezone, + std::function&)> func) + { + const auto begin_timet = begin.to_unix(); + const auto end_timet = end.to_unix(); + + const auto b_str = begin.format("%F %T"); + const auto e_str = end.format("%F %T"); + g_debug("getting all appointments from [%s ... %s]", b_str.c_str(), e_str.c_str()); + + /** + *** init the default timezone + **/ + + icaltimezone * default_timezone = nullptr; + const auto tz = timezone.timezone.get().c_str(); + if (tz && *tz) + { + default_timezone = icaltimezone_get_builtin_timezone(tz); + + if (default_timezone == nullptr) // maybe str is a tzid? + default_timezone = icaltimezone_get_builtin_timezone_from_tzid(tz); + + g_debug("default_timezone is %p", (void*)default_timezone); + } + + /** + *** walk through the sources to build the appointment list + **/ + + auto task_deleter = [](Task* task){ + // give the caller the (sorted) finished product + auto& a = task->appointments; + std::sort(a.begin(), a.end(), [](const Appointment& a, const Appointment& b){return a.begin < b.begin;}); + task->func(a); + // we're done; delete the task + g_debug("time to delete task %p", (void*)task); + delete task; + }; + + std::shared_ptr main_task(new Task(this, func), task_deleter); + + for (auto& kv : m_clients) + { + auto& client = kv.second; + if (default_timezone != nullptr) + e_cal_client_set_default_timezone(client, default_timezone); + + // start a new subtask to enumerate all the components in this client. + auto& source = kv.first; + auto extension = e_source_get_extension(source, E_SOURCE_EXTENSION_CALENDAR); + const auto color = e_source_selectable_get_color(E_SOURCE_SELECTABLE(extension)); + g_debug("calling e_cal_client_generate_instances for %p", (void*)client); + e_cal_client_generate_instances(client, + begin_timet, + end_timet, + m_cancellable, + my_get_appointments_foreach, + new AppointmentSubtask (main_task, client, color), + [](gpointer g){delete static_cast(g);}); + } + } + +private: + + void set_dirty_now() + { + m_changed(); + } + + static gboolean set_dirty_now_static (gpointer gself) + { + auto self = static_cast(gself); + self->m_rebuild_tag = 0; + self->set_dirty_now(); + return G_SOURCE_REMOVE; + } + + void set_dirty_soon() + { + static const int ARBITRARY_BATCH_MSEC = 200; + + if (m_rebuild_tag == 0) + m_rebuild_tag = g_timeout_add(ARBITRARY_BATCH_MSEC, set_dirty_now_static, this); + } + + static void on_source_registry_ready(GObject* /*source*/, GAsyncResult* res, gpointer gself) + { + GError * error = nullptr; + auto r = e_source_registry_new_finish(res, &error); + if (error != nullptr) + { + if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning("indicator-datetime cannot show EDS appointments: %s", error->message); + + g_error_free(error); + } + else + { + g_signal_connect(r, "source-added", G_CALLBACK(on_source_added), gself); + g_signal_connect(r, "source-removed", G_CALLBACK(on_source_removed), gself); + g_signal_connect(r, "source-changed", G_CALLBACK(on_source_changed), gself); + g_signal_connect(r, "source-disabled", G_CALLBACK(on_source_disabled), gself); + g_signal_connect(r, "source-enabled", G_CALLBACK(on_source_enabled), gself); + + auto self = static_cast(gself); + self->m_source_registry = r; + self->add_sources_by_extension(E_SOURCE_EXTENSION_CALENDAR); + self->add_sources_by_extension(E_SOURCE_EXTENSION_TASK_LIST); + } + } + + void add_sources_by_extension(const char* extension) + { + auto& r = m_source_registry; + auto sources = e_source_registry_list_sources(r, extension); + for (auto l=sources; l!=nullptr; l=l->next) + on_source_added(r, E_SOURCE(l->data), this); + g_list_free_full(sources, g_object_unref); + } + + static void on_source_added(ESourceRegistry* registry, ESource* source, gpointer gself) + { + auto self = static_cast(gself); + + self->m_sources.insert(E_SOURCE(g_object_ref(source))); + + if (e_source_get_enabled(source)) + on_source_enabled(registry, source, gself); + } + + static void on_source_enabled(ESourceRegistry* /*registry*/, ESource* source, gpointer gself) + { + auto self = static_cast(gself); + ECalClientSourceType source_type; + bool client_wanted = false; + + if (e_source_has_extension(source, E_SOURCE_EXTENSION_CALENDAR)) + { + source_type = E_CAL_CLIENT_SOURCE_TYPE_EVENTS; + client_wanted = true; + } + else if (e_source_has_extension(source, E_SOURCE_EXTENSION_TASK_LIST)) + { + source_type = E_CAL_CLIENT_SOURCE_TYPE_TASKS; + client_wanted = true; + } + + const auto source_uid = e_source_get_uid(source); + if (client_wanted) + { + g_debug("%s connecting a client to source %s", G_STRFUNC, source_uid); + e_cal_client_connect(source, + source_type, + self->m_cancellable, + on_client_connected, + gself); + } + else + { + g_debug("%s not using source %s -- no tasks/calendar", G_STRFUNC, source_uid); + } + } + + static void on_client_connected(GObject* /*source*/, GAsyncResult * res, gpointer gself) + { + GError * error = nullptr; + EClient * client = e_cal_client_connect_finish(res, &error); + if (error) + { + if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning("indicator-datetime cannot connect to EDS source: %s", error->message); + + g_error_free(error); + } + else + { + // add the client to our collection + auto self = static_cast(gself); + g_debug("got a client for %s", e_cal_client_get_local_attachment_store(E_CAL_CLIENT(client))); + self->m_clients[e_client_get_source(client)] = E_CAL_CLIENT(client); + + // now create a view for it so that we can listen for changes + e_cal_client_get_view (E_CAL_CLIENT(client), + "#t", // match all + self->m_cancellable, + on_client_view_ready, + self); + + g_debug("client connected; calling set_dirty_soon()"); + self->set_dirty_soon(); + } + } + + static void on_client_view_ready (GObject* client, GAsyncResult* res, gpointer gself) + { + GError* error = nullptr; + ECalClientView* view = nullptr; + + if (e_cal_client_get_view_finish (E_CAL_CLIENT(client), res, &view, &error)) + { + // add the view to our collection + e_cal_client_view_start(view, &error); + g_debug("got a view for %s", e_cal_client_get_local_attachment_store(E_CAL_CLIENT(client))); + auto self = static_cast(gself); + self->m_views[e_client_get_source(E_CLIENT(client))] = view; + + g_signal_connect(view, "objects-added", G_CALLBACK(on_view_objects_added), self); + g_signal_connect(view, "objects-modified", G_CALLBACK(on_view_objects_modified), self); + g_signal_connect(view, "objects-removed", G_CALLBACK(on_view_objects_removed), self); + g_debug("view connected; calling set_dirty_soon()"); + self->set_dirty_soon(); + } + else if(error != nullptr) + { + if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning("indicator-datetime cannot get View to EDS client: %s", error->message); + + g_error_free(error); + } + } + + static void on_view_objects_added(ECalClientView* /*view*/, gpointer /*objects*/, gpointer gself) + { + g_debug("%s", G_STRFUNC); + static_cast(gself)->set_dirty_soon(); + } + static void on_view_objects_modified(ECalClientView* /*view*/, gpointer /*objects*/, gpointer gself) + { + g_debug("%s", G_STRFUNC); + static_cast(gself)->set_dirty_soon(); + } + static void on_view_objects_removed(ECalClientView* /*view*/, gpointer /*objects*/, gpointer gself) + { + g_debug("%s", G_STRFUNC); + static_cast(gself)->set_dirty_soon(); + } + + static void on_source_disabled(ESourceRegistry* /*registry*/, ESource* source, gpointer gself) + { + static_cast(gself)->disable_source(source); + } + void disable_source(ESource* source) + { + // if an ECalClientView is associated with this source, remove it + auto vit = m_views.find(source); + if (vit != m_views.end()) + { + auto& view = vit->second; + e_cal_client_view_stop(view, nullptr); + const auto n_disconnected = g_signal_handlers_disconnect_by_data(view, this); + g_warn_if_fail(n_disconnected == 3); + g_object_unref(view); + m_views.erase(vit); + set_dirty_soon(); + } + + // if an ECalClient is associated with this source, remove it + auto cit = m_clients.find(source); + if (cit != m_clients.end()) + { + auto& client = cit->second; + g_object_unref(client); + m_clients.erase(cit); + set_dirty_soon(); + } + } + + static void on_source_removed(ESourceRegistry* /*registry*/, ESource* source, gpointer gself) + { + static_cast(gself)->remove_source(source); + } + void remove_source(ESource* source) + { + disable_source(source); + + auto sit = m_sources.find(source); + if (sit != m_sources.end()) + { + g_object_unref(*sit); + m_sources.erase(sit); + set_dirty_soon(); + } + } + + static void on_source_changed(ESourceRegistry* /*registry*/, ESource* /*source*/, gpointer gself) + { + g_debug("source changed; calling set_dirty_soon()"); + static_cast(gself)->set_dirty_soon(); + } + +private: + + typedef std::function&)> appointment_func; + + struct Task + { + Impl* p; + appointment_func func; + std::vector appointments; + Task(Impl* p_in, const appointment_func& func_in): p(p_in), func(func_in) {} + }; + + struct AppointmentSubtask + { + std::shared_ptr task; + ECalClient* client; + std::string color; + AppointmentSubtask(const std::shared_ptr& task_in, ECalClient* client_in, const char* color_in): + task(task_in), client(client_in) + { + if (color_in) + color = color_in; + } + }; + + struct UrlSubtask + { + std::shared_ptr task; + Appointment appointment; + UrlSubtask(const std::shared_ptr& task_in, const Appointment& appointment_in): + task(task_in), appointment(appointment_in) {} + }; + + static gboolean + my_get_appointments_foreach(ECalComponent* component, + time_t begin, + time_t end, + gpointer gsubtask) + { + const auto vtype = e_cal_component_get_vtype(component); + auto subtask = static_cast(gsubtask); + + if ((vtype == E_CAL_COMPONENT_EVENT) || (vtype == E_CAL_COMPONENT_TODO)) + { + const gchar* uid = nullptr; + e_cal_component_get_uid(component, &uid); + + auto status = ICAL_STATUS_NONE; + e_cal_component_get_status(component, &status); + + if ((uid != nullptr) && + (status != ICAL_STATUS_COMPLETED) && + (status != ICAL_STATUS_CANCELLED)) + { + Appointment appointment; + + /* Determine whether this is a recurring event. + NB: icalrecurrencetype supports complex recurrence patterns; + however, since design only allows daily recurrence, + that's all we support here. */ + GSList * recur_list; + e_cal_component_get_rrule_list(component, &recur_list); + for (auto l=recur_list; l!=nullptr; l=l->next) + { + const auto recur = static_cast(l->data); + appointment.is_daily |= ((recur->freq == ICAL_DAILY_RECURRENCE) + && (recur->interval == 1)); + } + e_cal_component_free_recur_list(recur_list); + + ECalComponentText text; + text.value = nullptr; + e_cal_component_get_summary(component, &text); + if (text.value) + appointment.summary = text.value; + + appointment.begin = DateTime(begin); + appointment.end = DateTime(end); + appointment.color = subtask->color; + appointment.is_event = vtype == E_CAL_COMPONENT_EVENT; + appointment.uid = uid; + + GList * alarm_uids = e_cal_component_get_alarm_uids(component); + appointment.has_alarms = alarm_uids != nullptr; + cal_obj_uid_list_free(alarm_uids); + + e_cal_client_get_attachment_uris(subtask->client, + uid, + nullptr, + subtask->task->p->m_cancellable, + on_appointment_uris_ready, + new UrlSubtask(subtask->task, appointment)); + } + } + + return G_SOURCE_CONTINUE; + } + + static void on_appointment_uris_ready(GObject* client, GAsyncResult* res, gpointer gsubtask) + { + auto subtask = static_cast(gsubtask); + + GSList * uris = nullptr; + GError * error = nullptr; + e_cal_client_get_attachment_uris_finish(E_CAL_CLIENT(client), res, &uris, &error); + if (error != nullptr) + { + if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED) && + !g_error_matches(error, E_CLIENT_ERROR, E_CLIENT_ERROR_NOT_SUPPORTED)) + { + g_warning("Error getting appointment uris: %s", error->message); + } + + g_error_free(error); + } + else if (uris != nullptr) + { + subtask->appointment.url = (const char*) uris->data; // copy the first URL + g_debug("found url '%s' for appointment '%s'", subtask->appointment.url.c_str(), subtask->appointment.summary.c_str()); + e_client_util_free_string_slist(uris); + } + + g_debug("adding appointment '%s' '%s'", subtask->appointment.summary.c_str(), subtask->appointment.url.c_str()); + subtask->task->appointments.push_back(subtask->appointment); + delete subtask; + } + + EdsEngine& m_owner; + core::Signal<> m_changed; + std::set m_sources; + std::map m_clients; + std::map m_views; + GCancellable* m_cancellable = nullptr; + ESourceRegistry* m_source_registry = nullptr; + guint m_rebuild_tag = 0; +}; + +/*** +**** +***/ + +EdsEngine::EdsEngine(): + p(new Impl(*this)) +{ +} + +EdsEngine::~EdsEngine() =default; + +core::Signal<>& EdsEngine::changed() +{ + return p->changed(); +} + +void EdsEngine::get_appointments(const DateTime& begin, + const DateTime& end, + const Timezone& tz, + std::function&)> func) +{ + p->get_appointments(begin, end, tz, func); +} + +/*** +**** +***/ + +} // namespace datetime +} // namespace indicator +} // namespace unity diff -Nru indicator-datetime-13.10.0+14.04.20140311.1/src/main.cpp indicator-datetime-13.10.0+14.04.20140314.1/src/main.cpp --- indicator-datetime-13.10.0+14.04.20140311.1/src/main.cpp 2014-03-11 18:16:26.000000000 +0000 +++ indicator-datetime-13.10.0+14.04.20140314.1/src/main.cpp 2014-03-14 17:37:09.000000000 +0000 @@ -20,13 +20,16 @@ #include #include #include +#include +#include #include #include #include -#include +#include #include #include #include +#include #include #include // bindtextdomain() @@ -51,21 +54,33 @@ bindtextdomain(GETTEXT_PACKAGE, GNOMELOCALEDIR); textdomain(GETTEXT_PACKAGE); + // we don't show appointments in the greeter, + // so no need to connect to EDS there... + std::shared_ptr engine; + if (!g_strcmp0("lightdm", g_get_user_name())) + engine.reset(new MockEngine); + else + engine.reset(new EdsEngine); + // build the state, actions, and menufactory std::shared_ptr state(new State); std::shared_ptr live_settings(new LiveSettings); std::shared_ptr live_timezones(new LiveTimezones(live_settings, TIMEZONE_FILE)); std::shared_ptr live_clock(new LiveClock(live_timezones)); + std::shared_ptr file_timezone(new FileTimezone(TIMEZONE_FILE)); + const auto now = live_clock->localtime(); state->settings = live_settings; state->clock = live_clock; state->locations.reset(new SettingsLocations(live_settings, live_timezones)); - state->planner.reset(new PlannerEds(live_clock)); - state->planner->time = live_clock->localtime(); + auto calendar_month = new MonthPlanner(std::shared_ptr(new SimpleRangePlanner(engine, file_timezone)), now); + state->calendar_month.reset(calendar_month); + state->calendar_upcoming.reset(new UpcomingPlanner(std::shared_ptr(new SimpleRangePlanner(engine, file_timezone)), now)); std::shared_ptr actions(new LiveActions(state)); MenuFactory factory(actions, state); // snap decisions - ClockWatcherImpl clock_watcher(state); + std::shared_ptr upcoming_planner(new UpcomingPlanner(std::shared_ptr(new SimpleRangePlanner(engine, file_timezone)), now)); + ClockWatcherImpl clock_watcher(live_clock, upcoming_planner); Snap snap; clock_watcher.alarm_reached().connect([&snap](const Appointment& appt){ auto snap_show = [](const Appointment& a){ diff -Nru indicator-datetime-13.10.0+14.04.20140311.1/src/menu.cpp indicator-datetime-13.10.0+14.04.20140314.1/src/menu.cpp --- indicator-datetime-13.10.0+14.04.20140311.1/src/menu.cpp 2014-03-11 18:16:26.000000000 +0000 +++ indicator-datetime-13.10.0+14.04.20140314.1/src/menu.cpp 2014-03-14 17:37:20.000000000 +0000 @@ -104,7 +104,7 @@ m_state->settings->show_events.changed().connect([this](bool){ update_section(Appointments); // showing events got toggled }); - m_state->planner->upcoming.changed().connect([this](const std::vector&){ + m_state->calendar_upcoming->appointments().changed().connect([this](const std::vector&){ update_upcoming(); // our m_upcoming is planner->upcoming() filtered by time }); m_state->clock->date_changed.connect([this](){ @@ -138,12 +138,18 @@ void update_upcoming() { + // show upcoming appointments that occur after "calendar_next_minute", + // where that is the wallclock time on the specified calendar day + const auto calendar_day = m_state->calendar_month->month().get(); const auto now = m_state->clock->localtime(); - const auto next_minute = now.add_full(0,0,0,0,1,-now.seconds()); + int y, m, d; + calendar_day.ymd(y, m, d); + const auto calendar_now = DateTime::Local(y, m, d, now.hour(), now.minute(), now.seconds()); + const auto calendar_next_minute = calendar_now.add_full(0, 0, 0, 0, 1, -now.seconds()); std::vector upcoming; - for(const auto& a : m_state->planner->upcoming.get()) - if (next_minute <= a.begin) + for(const auto& a : m_state->calendar_upcoming->appointments().get()) + if (calendar_next_minute <= a.begin) upcoming.push_back(a); if (m_upcoming != upcoming) @@ -302,7 +308,7 @@ g_menu_item_set_action_and_target_value (menu_item, "indicator.activate-appointment", g_variant_new_string (appt.uid.c_str())); - else + else if (m_actions->can_open_planner()) g_menu_item_set_action_and_target_value (menu_item, "indicator.activate-planner", g_variant_new_int64 (unix_time)); @@ -319,13 +325,16 @@ { add_appointments (menu, profile); - // add the 'Add Event…' menuitem - auto menu_item = g_menu_item_new(_("Add Event…"), nullptr); - const gchar* action_name = "indicator.activate-planner"; - auto v = g_variant_new_int64(0); - g_menu_item_set_action_and_target_value(menu_item, action_name, v); - g_menu_append_item(menu, menu_item); - g_object_unref(menu_item); + if (m_actions->can_open_planner()) + { + // add the 'Add Event…' menuitem + auto menu_item = g_menu_item_new(_("Add Event…"), nullptr); + const gchar* action_name = "indicator.activate-planner"; + auto v = g_variant_new_int64(0); + g_menu_item_set_action_and_target_value(menu_item, action_name, v); + g_menu_append_item(menu, menu_item); + g_object_unref(menu_item); + } } else if (profile==Phone) { diff -Nru indicator-datetime-13.10.0+14.04.20140311.1/src/planner-eds.cpp indicator-datetime-13.10.0+14.04.20140314.1/src/planner-eds.cpp --- indicator-datetime-13.10.0+14.04.20140311.1/src/planner-eds.cpp 2014-03-11 18:16:26.000000000 +0000 +++ indicator-datetime-13.10.0+14.04.20140314.1/src/planner-eds.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,564 +0,0 @@ -/* - * Copyright 2013 Canonical Ltd. - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 3, as published - * by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranties of - * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - * - * Authors: - * Charles Kerr - */ - -#include - -#include - -#include -#include -#include -#include - -#include // std::sort() -#include -#include - -namespace unity { -namespace indicator { -namespace datetime { - -/**** -***** -****/ - -class PlannerEds::Impl -{ -public: - - Impl(PlannerEds& owner, const std::shared_ptr& clock): - m_owner(owner), - m_clock(clock), - m_cancellable(g_cancellable_new()) - { - e_source_registry_new(m_cancellable, on_source_registry_ready, this); - - m_clock->minute_changed.connect([this](){ - g_debug("rebuilding upcoming because the clock's minute_changed"); - rebuild_soon(UPCOMING); - }); - - m_owner.time.changed().connect([this](const DateTime& dt) { - g_debug("planner's datetime property changed to %s; calling rebuild_soon()", dt.format("%F %T").c_str()); - rebuild_soon(MONTH); - }); - - rebuild_soon(ALL); - } - - ~Impl() - { - g_cancellable_cancel(m_cancellable); - g_clear_object(&m_cancellable); - - while(!m_sources.empty()) - remove_source(*m_sources.begin()); - - if (m_rebuild_tag) - g_source_remove(m_rebuild_tag); - - if (m_source_registry) - g_signal_handlers_disconnect_by_data(m_source_registry, this); - g_clear_object(&m_source_registry); - } - -private: - - static void on_source_registry_ready(GObject* /*source*/, GAsyncResult* res, gpointer gself) - { - GError * error = nullptr; - auto r = e_source_registry_new_finish(res, &error); - if (error != nullptr) - { - if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) - g_warning("indicator-datetime cannot show EDS appointments: %s", error->message); - - g_error_free(error); - } - else - { - g_signal_connect(r, "source-added", G_CALLBACK(on_source_added), gself); - g_signal_connect(r, "source-removed", G_CALLBACK(on_source_removed), gself); - g_signal_connect(r, "source-changed", G_CALLBACK(on_source_changed), gself); - g_signal_connect(r, "source-disabled", G_CALLBACK(on_source_disabled), gself); - g_signal_connect(r, "source-enabled", G_CALLBACK(on_source_enabled), gself); - - auto self = static_cast(gself); - self->m_source_registry = r; - self->add_sources_by_extension(E_SOURCE_EXTENSION_CALENDAR); - self->add_sources_by_extension(E_SOURCE_EXTENSION_TASK_LIST); - } - } - - void add_sources_by_extension(const char* extension) - { - auto& r = m_source_registry; - auto sources = e_source_registry_list_sources(r, extension); - for (auto l=sources; l!=nullptr; l=l->next) - on_source_added(r, E_SOURCE(l->data), this); - g_list_free_full(sources, g_object_unref); - } - - static void on_source_added(ESourceRegistry* registry, ESource* source, gpointer gself) - { - auto self = static_cast(gself); - - self->m_sources.insert(E_SOURCE(g_object_ref(source))); - - if (e_source_get_enabled(source)) - on_source_enabled(registry, source, gself); - } - - static void on_source_enabled(ESourceRegistry* /*registry*/, ESource* source, gpointer gself) - { - auto self = static_cast(gself); - ECalClientSourceType source_type; - bool client_wanted = false; - - if (e_source_has_extension(source, E_SOURCE_EXTENSION_CALENDAR)) - { - source_type = E_CAL_CLIENT_SOURCE_TYPE_EVENTS; - client_wanted = true; - } - else if (e_source_has_extension(source, E_SOURCE_EXTENSION_TASK_LIST)) - { - source_type = E_CAL_CLIENT_SOURCE_TYPE_TASKS; - client_wanted = true; - } - - const auto source_uid = e_source_get_uid(source); - if (client_wanted) - { - g_debug("%s connecting a client to source %s", G_STRFUNC, source_uid); - e_cal_client_connect(source, - source_type, - self->m_cancellable, - on_client_connected, - gself); - } - else - { - g_debug("%s not using source %s -- no tasks/calendar", G_STRFUNC, source_uid); - } - } - - static void on_client_connected(GObject* /*source*/, GAsyncResult * res, gpointer gself) - { - GError * error = nullptr; - EClient * client = e_cal_client_connect_finish(res, &error); - if (error) - { - if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) - g_warning("indicator-datetime cannot connect to EDS source: %s", error->message); - - g_error_free(error); - } - else - { - // add the client to our collection - auto self = static_cast(gself); - g_debug("got a client for %s", e_cal_client_get_local_attachment_store(E_CAL_CLIENT(client))); - self->m_clients[e_client_get_source(client)] = E_CAL_CLIENT(client); - - // now create a view for it so that we can listen for changes - e_cal_client_get_view (E_CAL_CLIENT(client), - "#t", // match all - self->m_cancellable, - on_client_view_ready, - self); - - g_debug("client connected; calling rebuild_soon()"); - self->rebuild_soon(ALL); - } - } - - static void on_client_view_ready (GObject* client, GAsyncResult* res, gpointer gself) - { - GError* error = nullptr; - ECalClientView* view = nullptr; - - if (e_cal_client_get_view_finish (E_CAL_CLIENT(client), res, &view, &error)) - { - // add the view to our collection - e_cal_client_view_start(view, &error); - g_debug("got a view for %s", e_cal_client_get_local_attachment_store(E_CAL_CLIENT(client))); - auto self = static_cast(gself); - self->m_views[e_client_get_source(E_CLIENT(client))] = view; - - g_signal_connect(view, "objects-added", G_CALLBACK(on_view_objects_added), self); - g_signal_connect(view, "objects-modified", G_CALLBACK(on_view_objects_modified), self); - g_signal_connect(view, "objects-removed", G_CALLBACK(on_view_objects_removed), self); - g_debug("view connected; calling rebuild_soon()"); - self->rebuild_soon(ALL); - } - else if(error != nullptr) - { - if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) - g_warning("indicator-datetime cannot get View to EDS client: %s", error->message); - - g_error_free(error); - } - } - - static void on_view_objects_added(ECalClientView* /*view*/, gpointer /*objects*/, gpointer gself) - { - g_debug("%s", G_STRFUNC); - static_cast(gself)->rebuild_soon(ALL); - } - static void on_view_objects_modified(ECalClientView* /*view*/, gpointer /*objects*/, gpointer gself) - { - g_debug("%s", G_STRFUNC); - static_cast(gself)->rebuild_soon(ALL); - } - static void on_view_objects_removed(ECalClientView* /*view*/, gpointer /*objects*/, gpointer gself) - { - g_debug("%s", G_STRFUNC); - static_cast(gself)->rebuild_soon(ALL); - } - - static void on_source_disabled(ESourceRegistry* /*registry*/, ESource* source, gpointer gself) - { - static_cast(gself)->disable_source(source); - } - void disable_source(ESource* source) - { - // if an ECalClientView is associated with this source, remove it - auto vit = m_views.find(source); - if (vit != m_views.end()) - { - auto& view = vit->second; - e_cal_client_view_stop(view, nullptr); - const auto n_disconnected = g_signal_handlers_disconnect_by_data(view, this); - g_warn_if_fail(n_disconnected == 3); - g_object_unref(view); - m_views.erase(vit); - rebuild_soon(ALL); - } - - // if an ECalClient is associated with this source, remove it - auto cit = m_clients.find(source); - if (cit != m_clients.end()) - { - auto& client = cit->second; - g_object_unref(client); - m_clients.erase(cit); - rebuild_soon(ALL); - } - } - - static void on_source_removed(ESourceRegistry* /*registry*/, ESource* source, gpointer gself) - { - static_cast(gself)->remove_source(source); - } - void remove_source(ESource* source) - { - disable_source(source); - - auto sit = m_sources.find(source); - if (sit != m_sources.end()) - { - g_object_unref(*sit); - m_sources.erase(sit); - rebuild_soon(ALL); - } - } - - static void on_source_changed(ESourceRegistry* /*registry*/, ESource* /*source*/, gpointer gself) - { - g_debug("source changed; calling rebuild_soon()"); - static_cast(gself)->rebuild_soon(ALL); - } - -private: - - typedef std::function&)> appointment_func; - - struct Task - { - Impl* p; - appointment_func func; - std::vector appointments; - Task(Impl* p_in, const appointment_func& func_in): p(p_in), func(func_in) {} - }; - - struct AppointmentSubtask - { - std::shared_ptr task; - ECalClient* client; - std::string color; - AppointmentSubtask(const std::shared_ptr& task_in, ECalClient* client_in, const char* color_in): - task(task_in), client(client_in) - { - if (color_in) - color = color_in; - } - }; - - void rebuild_soon(int rebuild_flags) - { - static const guint ARBITRARY_INTERVAL_SECS = 2; - - m_rebuild_flags |= rebuild_flags; - - if (m_rebuild_tag == 0) - m_rebuild_tag = g_timeout_add_seconds(ARBITRARY_INTERVAL_SECS, rebuild_now_static, this); - } - - static gboolean rebuild_now_static(gpointer gself) - { - auto self = static_cast(gself); - const auto flags = self->m_rebuild_flags; - self->m_rebuild_tag = 0; - self->m_rebuild_flags = 0; - self->rebuild_now(flags); - return G_SOURCE_REMOVE; - } - - void rebuild_now(int rebuild_flags) - { - if (rebuild_flags & UPCOMING) - rebuild_upcoming(); - - if (rebuild_flags & MONTH) - rebuild_month(); - } - - void rebuild_month() - { - const auto ref = m_owner.time.get().get(); - auto month_begin = g_date_time_add_full(ref, - 0, // subtract no years - 0, // subtract no months - -(g_date_time_get_day_of_month(ref)-1), - -g_date_time_get_hour(ref), - -g_date_time_get_minute(ref), - -g_date_time_get_seconds(ref)); - auto month_end = g_date_time_add_full(month_begin, 0, 1, 0, 0, 0, -0.1); - - get_appointments(month_begin, month_end, [this](const std::vector& appointments) { - g_debug("got %d appointments in this calendar month", (int)appointments.size()); - m_owner.this_month.set(appointments); - }); - - g_date_time_unref(month_end); - g_date_time_unref(month_begin); - } - - void rebuild_upcoming() - { - const auto ref = m_clock->localtime(); - const auto begin = g_date_time_add_minutes(ref.get(),-10); - const auto end = g_date_time_add_months(begin,1); - - get_appointments(begin, end, [this](const std::vector& appointments) { - g_debug("got %d upcoming appointments", (int)appointments.size()); - m_owner.upcoming.set(appointments); - }); - - g_date_time_unref(end); - g_date_time_unref(begin); - } - - void get_appointments(GDateTime* begin_dt, GDateTime* end_dt, appointment_func func) - { - const auto begin = g_date_time_to_unix(begin_dt); - const auto end = g_date_time_to_unix(end_dt); - - auto begin_str = g_date_time_format(begin_dt, "%F %T"); - auto end_str = g_date_time_format(end_dt, "%F %T"); - g_debug("getting all appointments from [%s ... %s]", begin_str, end_str); - g_free(begin_str); - g_free(end_str); - - /** - *** init the default timezone - **/ - - icaltimezone * default_timezone = nullptr; - - const auto tz = g_date_time_get_timezone_abbreviation(m_owner.time.get().get()); - g_debug("%s tz is %s", G_STRLOC, tz); - if (tz && *tz) - { - default_timezone = icaltimezone_get_builtin_timezone(tz); - - if (default_timezone == nullptr) // maybe str is a tzid? - default_timezone = icaltimezone_get_builtin_timezone_from_tzid(tz); - - g_debug("default_timezone is %p", (void*)default_timezone); - } - - /** - *** walk through the sources to build the appointment list - **/ - - auto task_deleter = [](Task* task){ - // give the caller the (sorted) finished product - auto& a = task->appointments; - std::sort(a.begin(), a.end(), [](const Appointment& a, const Appointment& b){return a.begin < b.begin;}); - task->func(a); - // we're done; delete the task - g_debug("time to delete task %p", (void*)task); - delete task; - }; - - std::shared_ptr main_task(new Task(this, func), task_deleter); - - for (auto& kv : m_clients) - { - auto& client = kv.second; - if (default_timezone != nullptr) - e_cal_client_set_default_timezone(client, default_timezone); - - // start a new subtask to enumerate all the components in this client. - auto& source = kv.first; - auto extension = e_source_get_extension(source, E_SOURCE_EXTENSION_CALENDAR); - const auto color = e_source_selectable_get_color(E_SOURCE_SELECTABLE(extension)); - g_debug("calling e_cal_client_generate_instances for %p", (void*)client); - e_cal_client_generate_instances(client, - begin, - end, - m_cancellable, - my_get_appointments_foreach, - new AppointmentSubtask (main_task, client, color), - [](gpointer g){delete static_cast(g);}); - } - } - - struct UrlSubtask - { - std::shared_ptr task; - Appointment appointment; - UrlSubtask(const std::shared_ptr& task_in, const Appointment& appointment_in): - task(task_in), appointment(appointment_in) {} - }; - - static gboolean - my_get_appointments_foreach(ECalComponent* component, - time_t begin, - time_t end, - gpointer gsubtask) - { - const auto vtype = e_cal_component_get_vtype(component); - auto subtask = static_cast(gsubtask); - - if ((vtype == E_CAL_COMPONENT_EVENT) || (vtype == E_CAL_COMPONENT_TODO)) - { - const gchar* uid = nullptr; - e_cal_component_get_uid(component, &uid); - - auto status = ICAL_STATUS_NONE; - e_cal_component_get_status(component, &status); - - if ((uid != nullptr) && - (status != ICAL_STATUS_COMPLETED) && - (status != ICAL_STATUS_CANCELLED)) - { - Appointment appointment; - - /* Determine whether this is a recurring event. - NB: icalrecurrencetype supports complex recurrence patterns; - however, since design only allows daily recurrence, - that's all we support here. */ - GSList * recur_list; - e_cal_component_get_rrule_list(component, &recur_list); - for (auto l=recur_list; l!=nullptr; l=l->next) - { - const auto recur = static_cast(l->data); - appointment.is_daily |= ((recur->freq == ICAL_DAILY_RECURRENCE) - && (recur->interval == 1)); - } - e_cal_component_free_recur_list(recur_list); - - ECalComponentText text; - text.value = nullptr; - e_cal_component_get_summary(component, &text); - if (text.value) - appointment.summary = text.value; - - appointment.begin = DateTime(begin); - appointment.end = DateTime(end); - appointment.color = subtask->color; - appointment.is_event = vtype == E_CAL_COMPONENT_EVENT; - appointment.uid = uid; - - GList * alarm_uids = e_cal_component_get_alarm_uids(component); - appointment.has_alarms = alarm_uids != nullptr; - cal_obj_uid_list_free(alarm_uids); - - e_cal_client_get_attachment_uris(subtask->client, - uid, - nullptr, - subtask->task->p->m_cancellable, - on_appointment_uris_ready, - new UrlSubtask(subtask->task, appointment)); - } - } - - return G_SOURCE_CONTINUE; - } - - static void on_appointment_uris_ready(GObject* client, GAsyncResult* res, gpointer gsubtask) - { - auto subtask = static_cast(gsubtask); - - GSList * uris = nullptr; - GError * error = nullptr; - e_cal_client_get_attachment_uris_finish(E_CAL_CLIENT(client), res, &uris, &error); - if (error != nullptr) - { - if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED) && - !g_error_matches(error, E_CLIENT_ERROR, E_CLIENT_ERROR_NOT_SUPPORTED)) - { - g_warning("Error getting appointment uris: %s", error->message); - } - - g_error_free(error); - } - else if (uris != nullptr) - { - subtask->appointment.url = (const char*) uris->data; // copy the first URL - g_debug("found url '%s' for appointment '%s'", subtask->appointment.url.c_str(), subtask->appointment.summary.c_str()); - e_client_util_free_string_slist(uris); - } - - g_debug("adding appointment '%s' '%s'", subtask->appointment.summary.c_str(), subtask->appointment.url.c_str()); - subtask->task->appointments.push_back(subtask->appointment); - delete subtask; - } - - PlannerEds& m_owner; - std::shared_ptr m_clock; - std::set m_sources; - std::map m_clients; - std::map m_views; - GCancellable* m_cancellable = nullptr; - ESourceRegistry* m_source_registry = nullptr; - guint m_rebuild_tag = 0; - guint m_rebuild_flags = 0; - enum { UPCOMING=(1<<0), MONTH=(1<<1), ALL=UPCOMING|MONTH }; -}; - -PlannerEds::PlannerEds(const std::shared_ptr& clock): p(new Impl(*this, clock)) {} - -PlannerEds::~PlannerEds() =default; - -} // namespace datetime -} // namespace indicator -} // namespace unity diff -Nru indicator-datetime-13.10.0+14.04.20140311.1/src/planner-month.cpp indicator-datetime-13.10.0+14.04.20140314.1/src/planner-month.cpp --- indicator-datetime-13.10.0+14.04.20140311.1/src/planner-month.cpp 1970-01-01 00:00:00.000000000 +0000 +++ indicator-datetime-13.10.0+14.04.20140314.1/src/planner-month.cpp 2014-03-14 17:36:59.000000000 +0000 @@ -0,0 +1,66 @@ +/* + * Copyright 2014 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Charles Kerr + */ + +#include + +namespace unity { +namespace indicator { +namespace datetime { + +/*** +**** +***/ + +MonthPlanner::MonthPlanner(const std::shared_ptr& range_planner, + const DateTime& month_in): + m_range_planner(range_planner) +{ + month().changed().connect([this](const DateTime& m){ + auto month_begin = m.add_full(0, // no years + 0, // no months + -(m.day_of_month()-1), + -m.hour(), + -m.minute(), + -m.seconds()); + auto month_end = month_begin.add_full(0, 1, 0, 0, 0, -0.1); + g_debug("PlannerMonth %p setting calendar month range: [%s..%s]", this, month_begin.format("%F %T").c_str(), month_end.format("%F %T").c_str()); + m_range_planner->range().set(std::pair(month_begin,month_end)); + }); + + month().set(month_in); +} + +core::Property& MonthPlanner::month() +{ + return m_month; +} + +core::Property>& MonthPlanner::appointments() +{ + return m_range_planner->appointments(); +} + + +/*** +**** +***/ + +} // namespace datetime +} // namespace indicator +} // namespace unity diff -Nru indicator-datetime-13.10.0+14.04.20140311.1/src/planner-range.cpp indicator-datetime-13.10.0+14.04.20140314.1/src/planner-range.cpp --- indicator-datetime-13.10.0+14.04.20140311.1/src/planner-range.cpp 1970-01-01 00:00:00.000000000 +0000 +++ indicator-datetime-13.10.0+14.04.20140314.1/src/planner-range.cpp 2014-03-14 17:37:09.000000000 +0000 @@ -0,0 +1,105 @@ +/* + * Copyright 2014 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Charles Kerr + */ + +#include + +namespace unity { +namespace indicator { +namespace datetime { + +/*** +**** +***/ + +SimpleRangePlanner::SimpleRangePlanner(const std::shared_ptr& engine, + const std::shared_ptr& timezone): + m_engine(engine), + m_timezone(timezone), + m_range(std::pair(DateTime::NowLocal(), DateTime::NowLocal())) +{ + engine->changed().connect([this](){ + g_debug("RangePlanner %p rebuilding soon because Engine %p emitted 'changed' signal%p", this, m_engine.get()); + rebuild_soon(); + }); + + range().changed().connect([this](const std::pair&){ + g_debug("rebuilding because the date range changed"); + rebuild_soon(); + }); +} + +SimpleRangePlanner::~SimpleRangePlanner() +{ + if (m_rebuild_tag) + g_source_remove(m_rebuild_tag); +} + +/*** +**** +***/ + +void SimpleRangePlanner::rebuild_now() +{ + const auto& r = range().get(); + + auto on_appointments_fetched = [this](const std::vector& a){ + g_debug("RangePlanner %p got %zu appointments", this, a.size()); + appointments().set(a); + }; + + m_engine->get_appointments(r.first, r.second, *m_timezone.get(), on_appointments_fetched); +} + +void SimpleRangePlanner::rebuild_soon() +{ + static const int ARBITRARY_BATCH_MSEC = 200; + + if (m_rebuild_tag == 0) + m_rebuild_tag = g_timeout_add(ARBITRARY_BATCH_MSEC, rebuild_now_static, this); +} + +gboolean SimpleRangePlanner::rebuild_now_static(gpointer gself) +{ + auto self = static_cast(gself); + self->m_rebuild_tag = 0; + self->rebuild_now(); + return G_SOURCE_REMOVE; +} + +/*** +**** +***/ + +core::Property>& SimpleRangePlanner::appointments() +{ + return m_appointments; +} + +core::Property>& SimpleRangePlanner::range() +{ + return m_range; +} + +/*** +**** +***/ + +} // namespace datetime +} // namespace indicator +} // namespace unity diff -Nru indicator-datetime-13.10.0+14.04.20140311.1/src/planner-upcoming.cpp indicator-datetime-13.10.0+14.04.20140314.1/src/planner-upcoming.cpp --- indicator-datetime-13.10.0+14.04.20140311.1/src/planner-upcoming.cpp 1970-01-01 00:00:00.000000000 +0000 +++ indicator-datetime-13.10.0+14.04.20140314.1/src/planner-upcoming.cpp 2014-03-14 17:36:59.000000000 +0000 @@ -0,0 +1,61 @@ +/* + * Copyright 2014 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Charles Kerr + */ + +#include + +namespace unity { +namespace indicator { +namespace datetime { + +/*** +**** +***/ + +UpcomingPlanner::UpcomingPlanner(const std::shared_ptr& range_planner, + const DateTime& date_in): + m_range_planner(range_planner) +{ + date().changed().connect([this](const DateTime& dt){ + // set the range to the upcoming month + const auto b = dt.add_full(0, 0, -1, 0, 0, 0); + const auto e = dt.add_full(0, 1, 0, 0, 0, 0); + g_debug("%p setting date range to [%s..%s]", this, b.format("%F %T").c_str(), e.format("%F %T").c_str()); + m_range_planner->range().set(std::pair(b,e)); + }); + + date().set(date_in); +} + +core::Property& UpcomingPlanner::date() +{ + return m_date; +} + +core::Property>& UpcomingPlanner::appointments() +{ + return m_range_planner->appointments(); +} + +/*** +**** +***/ + +} // namespace datetime +} // namespace indicator +} // namespace unity diff -Nru indicator-datetime-13.10.0+14.04.20140311.1/src/utils.c indicator-datetime-13.10.0+14.04.20140314.1/src/utils.c --- indicator-datetime-13.10.0+14.04.20140311.1/src/utils.c 2014-03-11 18:16:26.000000000 +0000 +++ indicator-datetime-13.10.0+14.04.20140314.1/src/utils.c 2014-03-14 17:36:59.000000000 +0000 @@ -159,6 +159,10 @@ gint now_year, now_month, now_day; gint time_year, time_month, time_day; + // did it already happen? + if (g_date_time_difference(time, now) < -G_USEC_PER_SEC) + return DATE_PROXIMITY_FAR; + // does it happen today? g_date_time_get_ymd(now, &now_year, &now_month, &now_day); g_date_time_get_ymd(time, &time_year, &time_month, &time_day); diff -Nru indicator-datetime-13.10.0+14.04.20140311.1/tests/actions-mock.h indicator-datetime-13.10.0+14.04.20140314.1/tests/actions-mock.h --- indicator-datetime-13.10.0+14.04.20140311.1/tests/actions-mock.h 2014-03-11 18:16:26.000000000 +0000 +++ indicator-datetime-13.10.0+14.04.20140314.1/tests/actions-mock.h 2014-03-14 17:37:20.000000000 +0000 @@ -49,6 +49,8 @@ void open_phone_clock_app() { m_history.push_back(OpenPhoneClockApp); } + bool can_open_planner() const { return m_can_open_planner; } + void open_planner() { m_history.push_back(OpenPlanner); } void open_planner_at(const DateTime& date_time_) { @@ -67,7 +69,10 @@ m_url = url_; } + void set_can_open_planner(bool b) { m_can_open_planner = b; } + private: + bool m_can_open_planner = true; std::string m_url; std::string m_zone; std::string m_name; diff -Nru indicator-datetime-13.10.0+14.04.20140311.1/tests/planner-mock.h indicator-datetime-13.10.0+14.04.20140314.1/tests/planner-mock.h --- indicator-datetime-13.10.0+14.04.20140311.1/tests/planner-mock.h 2014-03-11 18:16:26.000000000 +0000 +++ indicator-datetime-13.10.0+14.04.20140314.1/tests/planner-mock.h 2014-03-14 17:37:09.000000000 +0000 @@ -20,22 +20,34 @@ #ifndef INDICATOR_DATETIME_PLANNER_MOCK_H #define INDICATOR_DATETIME_PLANNER_MOCK_H -#include +#include namespace unity { namespace indicator { namespace datetime { /** - * \brief Planner which does nothing on its own. - * It requires its client must set its appointments property. + * \brief #RangePlanner which does nothing on its own. + * Its controller must set its appointments property. */ -class MockPlanner: public Planner +class MockRangePlanner: public RangePlanner { public: - MockPlanner() =default; - virtual ~MockPlanner() =default; + MockRangePlanner(): + m_range(std::pair(DateTime::NowLocal(), DateTime::NowLocal())) + { + } + + ~MockRangePlanner() =default; + + core::Property>& appointments() { return m_appointments; } + core::Property>& range() { return m_range; } + +private: + core::Property> m_appointments; + core::Property> m_range; }; + } // namespace datetime } // namespace indicator diff -Nru indicator-datetime-13.10.0+14.04.20140311.1/tests/state-mock.h indicator-datetime-13.10.0+14.04.20140314.1/tests/state-mock.h --- indicator-datetime-13.10.0+14.04.20140311.1/tests/state-mock.h 2014-03-11 18:16:26.000000000 +0000 +++ indicator-datetime-13.10.0+14.04.20140314.1/tests/state-mock.h 2014-03-14 17:36:59.000000000 +0000 @@ -28,15 +28,21 @@ { public: std::shared_ptr mock_clock; + std::shared_ptr mock_range_planner; MockState() { const DateTime now = DateTime::NowLocal(); mock_clock.reset(new MockClock(now)); - settings.reset(new Settings); clock = std::dynamic_pointer_cast(mock_clock); - planner.reset(new MockPlanner); - planner->time = now; + + settings.reset(new Settings); + + mock_range_planner.reset(new MockRangePlanner); + auto range_planner = std::dynamic_pointer_cast(mock_range_planner); + calendar_month.reset(new MonthPlanner(range_planner, now)); + calendar_upcoming.reset(new UpcomingPlanner(range_planner, now)); + locations.reset(new Locations); } }; diff -Nru indicator-datetime-13.10.0+14.04.20140311.1/tests/test-actions.cpp indicator-datetime-13.10.0+14.04.20140314.1/tests/test-actions.cpp --- indicator-datetime-13.10.0+14.04.20140311.1/tests/test-actions.cpp 2014-03-11 18:16:26.000000000 +0000 +++ indicator-datetime-13.10.0+14.04.20140314.1/tests/test-actions.cpp 2014-03-14 17:36:59.000000000 +0000 @@ -150,10 +150,10 @@ // confirm that Planner.time gets changed to that date when we // activate the 'calendar' action with that date's time_t as the arg - EXPECT_NE (now, m_state->planner->time.get()); + EXPECT_NE (now, m_state->calendar_month->month().get()); auto v = g_variant_new_int64(now.to_unix()); g_action_group_activate_action (action_group, action_name, v); - EXPECT_EQ (now, m_state->planner->time.get()); + EXPECT_EQ (now, m_state->calendar_month->month().get()); } TEST_F(ActionsFixture, ActivatingTheCalendarResetsItsDate) @@ -171,11 +171,12 @@ const auto now = m_state->clock->localtime(); auto next_week = g_date_time_add_weeks(now.get(), 1); const auto next_week_unix = g_date_time_to_unix(next_week); + g_date_time_unref(next_week); g_action_group_activate_action (action_group, "calendar", g_variant_new_int64(next_week_unix)); // confirm the planner and calendar action state moved a week into the future // but that m_state->clock is unchanged - EXPECT_EQ(next_week_unix, m_state->planner->time.get().to_unix()); + EXPECT_EQ(next_week_unix, m_state->calendar_month->month().get().to_unix()); EXPECT_EQ(now, m_state->clock->localtime()); auto calendar_state = g_action_group_get_action_state(action_group, "calendar"); EXPECT_TRUE(calendar_state != nullptr); @@ -196,7 +197,7 @@ g_action_group_change_action_state(action_group, "calendar-active", g_variant_new_boolean(true)); // confirm the planner and calendar action state were reset back to m_state->clock's time - EXPECT_EQ(now.to_unix(), m_state->planner->time.get().to_unix()); + EXPECT_EQ(now.to_unix(), m_state->calendar_month->month().get().to_unix()); EXPECT_EQ(now, m_state->clock->localtime()); calendar_state = g_action_group_get_action_state(action_group, "calendar"); EXPECT_TRUE(calendar_state != nullptr); @@ -215,7 +216,8 @@ Appointment appt; appt.uid = "some arbitrary uid"; appt.url = "http://www.canonical.com/"; - m_state->planner->upcoming.set(std::vector({appt})); + appt.begin = m_state->clock->localtime(); + m_state->calendar_upcoming->appointments().set(std::vector({appt})); const auto action_name = "activate-appointment"; auto action_group = m_actions->action_group(); diff -Nru indicator-datetime-13.10.0+14.04.20140311.1/tests/test-clock-watcher.cpp indicator-datetime-13.10.0+14.04.20140314.1/tests/test-clock-watcher.cpp --- indicator-datetime-13.10.0+14.04.20140311.1/tests/test-clock-watcher.cpp 2014-03-11 18:16:26.000000000 +0000 +++ indicator-datetime-13.10.0+14.04.20140314.1/tests/test-clock-watcher.cpp 2014-03-14 17:36:59.000000000 +0000 @@ -35,12 +35,16 @@ std::vector m_triggered; std::unique_ptr m_watcher; + std::shared_ptr m_range_planner; + std::shared_ptr m_upcoming; void SetUp() { super::SetUp(); - m_watcher.reset(new ClockWatcherImpl(m_state)); + m_range_planner.reset(new MockRangePlanner); + m_upcoming.reset(new UpcomingPlanner(m_range_planner, m_state->clock->localtime())); + m_watcher.reset(new ClockWatcherImpl(m_state->clock, m_upcoming)); m_watcher->alarm_reached().connect([this](const Appointment& appt){ m_triggered.push_back(appt.uid); }); @@ -52,6 +56,8 @@ { m_triggered.clear(); m_watcher.reset(); + m_upcoming.reset(); + m_range_planner.reset(); super::TearDown(); } @@ -108,7 +114,7 @@ // One of these matches our state's localtime, so that should get triggered. std::vector a = build_some_appointments(); a[0].begin = m_state->clock->localtime(); - m_state->planner->upcoming.set(a); + m_range_planner->appointments().set(a); // Confirm that it got fired EXPECT_EQ(1, m_triggered.size()); @@ -121,10 +127,10 @@ // Add some appointments to the planner. // Neither of these match the state's localtime, so nothing should be triggered. std::vector a = build_some_appointments(); - m_state->planner->upcoming.set(a); + m_range_planner->appointments().set(a); EXPECT_TRUE(m_triggered.empty()); - // Set the state's clock to a time that matches one of the appointments. + // Set the state's clock to a time that matches one of the appointments(). // That appointment should get triggered. m_mock_state->mock_clock->set_localtime(a[1].begin); EXPECT_EQ(1, m_triggered.size()); @@ -137,7 +143,7 @@ const auto now = m_state->clock->localtime(); std::vector a = build_some_appointments(); a[0].begin = a[1].begin = now; - m_state->planner->upcoming.set(a); + m_range_planner->appointments().set(a); EXPECT_EQ(2, m_triggered.size()); EXPECT_EQ(a[0].uid, m_triggered[0]); @@ -153,14 +159,14 @@ std::vector a; a.push_back(appointments[0]); a[0].begin = now; - m_state->planner->upcoming.set(a); + m_range_planner->appointments().set(a); EXPECT_EQ(1, m_triggered.size()); EXPECT_EQ(a[0].uid, m_triggered[0]); // Now change the appointment vector by adding one to it. // Confirm that the ClockWatcher doesn't re-trigger a[0] a.push_back(appointments[1]); - m_state->planner->upcoming.set(a); + m_range_planner->appointments().set(a); EXPECT_EQ(1, m_triggered.size()); EXPECT_EQ(a[0].uid, m_triggered[0]); } diff -Nru indicator-datetime-13.10.0+14.04.20140311.1/tests/test-live-actions.cpp indicator-datetime-13.10.0+14.04.20140314.1/tests/test-live-actions.cpp --- indicator-datetime-13.10.0+14.04.20140311.1/tests/test-live-actions.cpp 2014-03-11 18:16:26.000000000 +0000 +++ indicator-datetime-13.10.0+14.04.20140314.1/tests/test-live-actions.cpp 2014-03-14 17:36:59.000000000 +0000 @@ -284,7 +284,9 @@ { const auto now = DateTime::NowLocal(); m_actions->open_planner_at(now); - const std::string expected = now.format("evolution \"calendar:///?startdate=%Y%m%d\""); + const auto today_begins = now.add_full(0, 0, 0, -now.hour(), -now.minute(), -now.seconds()); + const auto gmt = today_begins.to_timezone("UTC"); + const auto expected = gmt.format("evolution \"calendar:///?startdate=%Y%m%dT%H%M%SZ\""); EXPECT_EQ(expected, m_live_actions->last_cmd); } @@ -295,7 +297,8 @@ const DateTime now (tmp); g_date_time_unref (tmp); m_mock_state->mock_clock->set_localtime (now); - m_state->planner->time.set(now); + m_state->calendar_month->month().set(now); + //m_state->planner->time.set(now); /// /// Test the default calendar state. @@ -315,7 +318,7 @@ // calendar-day should be in sync with m_state->calendar_day v = g_variant_lookup_value (calendar_state, "calendar-day", G_VARIANT_TYPE_INT64); EXPECT_TRUE (v != nullptr); - EXPECT_EQ (m_state->planner->time.get().to_unix(), g_variant_get_int64(v)); + EXPECT_EQ (m_state->calendar_month->month().get().to_unix(), g_variant_get_int64(v)); g_clear_pointer (&v, g_variant_unref); // show-week-numbers should be false because MockSettings defaults everything to 0 @@ -356,7 +359,7 @@ a2.begin = next_begin; a2.end = next_end; - m_state->planner->this_month.set(std::vector({a1, a2})); + m_state->calendar_month->appointments().set(std::vector({a1, a2})); /// /// Now test the calendar state again. diff -Nru indicator-datetime-13.10.0+14.04.20140311.1/tests/test-menus.cpp indicator-datetime-13.10.0+14.04.20140314.1/tests/test-menus.cpp --- indicator-datetime-13.10.0+14.04.20140311.1/tests/test-menus.cpp 2014-03-11 18:16:26.000000000 +0000 +++ indicator-datetime-13.10.0+14.04.20140314.1/tests/test-menus.cpp 2014-03-14 17:37:20.000000000 +0000 @@ -252,15 +252,17 @@ void InspectAppointmentMenuItems(GMenuModel* section, int first_appt_index, - const std::vector& appointments) + const std::vector& appointments, + bool can_open_planner) { // try adding a few appointments and see if the menu updates itself - m_state->planner->upcoming.set(appointments); + m_state->calendar_upcoming->appointments().set(appointments); wait_msec(); // wait a moment for the menu to update //auto submenu = g_menu_model_get_item_link(menu_model, 0, G_MENU_LINK_SUBMENU); //auto section = g_menu_model_get_item_link(submenu, Menu::Appointments, G_MENU_LINK_SECTION); - EXPECT_EQ(appointments.size()+1, g_menu_model_get_n_items(section)); + const int n_add_event_buttons = can_open_planner ? 1 : 0; + EXPECT_EQ(n_add_event_buttons + appointments.size(), g_menu_model_get_n_items(section)); for (int i=0, n=appointments.size(); i appointments; m_state->settings->show_events.set(true); - m_state->planner->upcoming.set(appointments); + m_state->calendar_upcoming->appointments().set(appointments); wait_msec(); section = g_menu_model_get_item_link(submenu, Menu::Appointments, G_MENU_LINK_SECTION); - EXPECT_EQ(1, g_menu_model_get_n_items(section)); - gchar* action = nullptr; - EXPECT_TRUE(g_menu_model_get_item_attribute(section, 0, G_MENU_ATTRIBUTE_ACTION, "s", &action)); - const char* expected_action = "activate-planner"; - EXPECT_EQ(std::string("indicator.")+expected_action, action); - EXPECT_TRUE(g_action_group_has_action(m_actions->action_group(), expected_action)); - g_free(action); + EXPECT_EQ(n_add_event_buttons, g_menu_model_get_n_items(section)); + if (can_open_planner) + { + // when "show_events" is true, + // there should be an "add event" button even if there aren't any appointments + gchar* action = nullptr; + EXPECT_TRUE(g_menu_model_get_item_attribute(section, 0, G_MENU_ATTRIBUTE_ACTION, "s", &action)); + const char* expected_action = "activate-planner"; + EXPECT_EQ(std::string("indicator.")+expected_action, action); + EXPECT_TRUE(g_action_group_has_action(m_actions->action_group(), expected_action)); + g_free(action); + } g_clear_object(§ion); // try adding a few appointments and see if the menu updates itself appointments = build_some_appointments(); - m_state->planner->upcoming.set(appointments); + m_state->calendar_upcoming->appointments().set(appointments); wait_msec(); // wait a moment for the menu to update section = g_menu_model_get_item_link(submenu, Menu::Appointments, G_MENU_LINK_SECTION); - EXPECT_EQ(3, g_menu_model_get_n_items(section)); - InspectAppointmentMenuItems(section, 0, appointments); + EXPECT_EQ(n_add_event_buttons + 2, g_menu_model_get_n_items(section)); + InspectAppointmentMenuItems(section, 0, appointments, can_open_planner); g_clear_object(§ion); // cleanup g_clear_object(&submenu); } - void InspectPhoneAppointments(GMenuModel* menu_model) + void InspectPhoneAppointments(GMenuModel* menu_model, bool can_open_planner) { auto submenu = g_menu_model_get_item_link(menu_model, 0, G_MENU_LINK_SUBMENU); // clear all the appointments std::vector appointments; - m_state->planner->upcoming.set(appointments); + m_state->calendar_upcoming->appointments().set(appointments); wait_msec(); // wait a moment for the menu to update // check that there's a "clock app" menuitem even when there are no appointments @@ -332,11 +339,11 @@ // add some appointments and test them appointments = build_some_appointments(); - m_state->planner->upcoming.set(appointments); + m_state->calendar_upcoming->appointments().set(appointments); wait_msec(); // wait a moment for the menu to update section = g_menu_model_get_item_link(submenu, Menu::Appointments, G_MENU_LINK_SECTION); EXPECT_EQ(3, g_menu_model_get_n_items(section)); - InspectAppointmentMenuItems(section, 1, appointments); + InspectAppointmentMenuItems(section, 1, appointments, can_open_planner); g_clear_object(§ion); // cleanup @@ -347,10 +354,12 @@ void InspectAppointments(GMenuModel* menu_model, Menu::Profile profile) { + const auto can_open_planner = m_actions->can_open_planner(); + switch (profile) { case Menu::Desktop: - InspectDesktopAppointments(menu_model); + InspectDesktopAppointments(menu_model, can_open_planner); break; case Menu::DesktopGreeter: @@ -358,7 +367,7 @@ break; case Menu::Phone: - InspectPhoneAppointments(menu_model); + InspectPhoneAppointments(menu_model, can_open_planner); break; case Menu::PhoneGreeter: @@ -507,6 +516,13 @@ { for(auto& menu : m_menus) InspectAppointments(menu->menu_model(), menu->profile()); + + // toggle can_open_planner() and test the desktop again + // to confirm that the "Add Event…" menuitem appears iff + // there's a calendar available user-agent + m_mock_actions->set_can_open_planner (!m_actions->can_open_planner()); + std::shared_ptr menu = m_menu_factory->buildMenu(Menu::Desktop); + InspectAppointments(menu->menu_model(), menu->profile()); } TEST_F(MenuFixture, Locations) diff -Nru indicator-datetime-13.10.0+14.04.20140311.1/tests/test-planner.cpp indicator-datetime-13.10.0+14.04.20140314.1/tests/test-planner.cpp --- indicator-datetime-13.10.0+14.04.20140311.1/tests/test-planner.cpp 2014-03-11 18:16:26.000000000 +0000 +++ indicator-datetime-13.10.0+14.04.20140314.1/tests/test-planner.cpp 2014-03-14 17:37:09.000000000 +0000 @@ -18,12 +18,13 @@ */ #include "glib-fixture.h" +#include "timezone-mock.h" #include #include #include #include -#include +#include #include #include @@ -36,26 +37,6 @@ typedef GlibFixture PlannerFixture; -TEST_F(PlannerFixture, EDS) -{ - auto tmp = g_date_time_new_now_local(); - const auto now = DateTime(tmp); - g_date_time_unref(tmp); - - std::shared_ptr clock(new MockClock(now)); - PlannerEds planner(clock); - wait_msec(100); - - planner.time.set(now); - wait_msec(2500); - - std::vector this_month = planner.this_month.get(); - std::cerr << this_month.size() << " appointments this month" << std::endl; - for(const auto& a : this_month) - std::cerr << a.summary << std::endl; -} - - TEST_F(PlannerFixture, HelloWorld) { auto halloween = g_date_time_new_local(2020, 10, 31, 18, 30, 59); diff -Nru indicator-datetime-13.10.0+14.04.20140311.1/tests/timezone-mock.h indicator-datetime-13.10.0+14.04.20140314.1/tests/timezone-mock.h --- indicator-datetime-13.10.0+14.04.20140311.1/tests/timezone-mock.h 1970-01-01 00:00:00.000000000 +0000 +++ indicator-datetime-13.10.0+14.04.20140314.1/tests/timezone-mock.h 2014-03-14 17:36:59.000000000 +0000 @@ -0,0 +1,40 @@ +/* + * Copyright 2014 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Charles Kerr + */ + +#ifndef INDICATOR_DATETIME_TIMEZONE_MOCK_H +#define INDICATOR_DATETIME_TIMEZONE_MOCK_H + +#include + +namespace unity { +namespace indicator { +namespace datetime { + +class MockTimezone: public Timezone +{ +public: + MockTimezone() =default; + ~MockTimezone() =default; +}; + +} // namespace datetime +} // namespace indicator +} // namespace unity + +#endif // INDICATOR_DATETIME_TIMEZONE_MOCK_H