aboutsummaryrefslogtreecommitdiff
path: root/src/planner-eds.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/planner-eds.cpp')
-rw-r--r--src/planner-eds.cpp559
1 files changed, 29 insertions, 530 deletions
diff --git a/src/planner-eds.cpp b/src/planner-eds.cpp
index f47c2e9..f3f28ee 100644
--- a/src/planner-eds.cpp
+++ b/src/planner-eds.cpp
@@ -18,550 +18,49 @@
*/
#include <datetime/planner-eds.h>
-
-#include <datetime/appointment.h>
-
-#include <libical/ical.h>
-#include <libical/icaltime.h>
-#include <libecal/libecal.h>
-#include <libedataserver/libedataserver.h>
-
-#include <algorithm> // std::sort()
-#include <map>
-#include <set>
+#include <datetime/engine-eds.h>
namespace unity {
namespace indicator {
namespace datetime {
-/****
-*****
-****/
+/***
+****
+***/
-class PlannerEds::Impl
+EdsPlanner::EdsPlanner(const std::shared_ptr<EdsEngine>& engine,
+ const std::shared_ptr<Timezone>& timezone):
+ m_engine(engine),
+ m_timezone(timezone)
{
-public:
-
- Impl(PlannerEds& owner,
- const std::shared_ptr<Clock>& clock,
- const std::shared_ptr<Timezone>& timezone):
- m_owner(owner),
- m_clock(clock),
- m_timezone(timezone),
- 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<Impl*>(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<Impl*>(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<Impl*>(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<Impl*>(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<Impl*>(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<Impl*>(gself)->rebuild_soon(ALL);
- }
- static void on_view_objects_modified(ECalClientView* /*view*/, gpointer /*objects*/, gpointer gself)
- {
- g_debug("%s", G_STRFUNC);
- static_cast<Impl*>(gself)->rebuild_soon(ALL);
- }
- static void on_view_objects_removed(ECalClientView* /*view*/, gpointer /*objects*/, gpointer gself)
- {
- g_debug("%s", G_STRFUNC);
- static_cast<Impl*>(gself)->rebuild_soon(ALL);
- }
-
- static void on_source_disabled(ESourceRegistry* /*registry*/, ESource* source, gpointer gself)
- {
- static_cast<Impl*>(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<Impl*>(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<Impl*>(gself)->rebuild_soon(ALL);
- }
-
-private:
-
- typedef std::function<void(const std::vector<Appointment>&)> appointment_func;
-
- struct Task
- {
- Impl* p;
- appointment_func func;
- std::vector<Appointment> appointments;
- Task(Impl* p_in, const appointment_func& func_in): p(p_in), func(func_in) {}
- };
-
- struct AppointmentSubtask
- {
- std::shared_ptr<Task> task;
- ECalClient* client;
- std::string color;
- AppointmentSubtask(const std::shared_ptr<Task>& 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<Impl*>(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<Appointment>& 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_days(ref.get(),-1);
- const auto end = g_date_time_add_months(begin,1);
-
- get_appointments(begin, end, [this](const std::vector<Appointment>& 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 = m_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);
- }
+ m_engine->changed().connect([this](){
+ g_debug("EdsPlanner %p rebuilding soon because EdsEngine %p emitted 'changed' signal%p", this, m_engine.get());
+ rebuild_soon();
+ });
+}
- /**
- *** walk through the sources to build the appointment list
- **/
+EdsPlanner::~EdsPlanner() =default;
- 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<Task> 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<AppointmentSubtask*>(g);});
- }
- }
+void EdsPlanner::rebuild_now()
+{
+ const auto& r = range().get();
- struct UrlSubtask
- {
- std::shared_ptr<Task> task;
- Appointment appointment;
- UrlSubtask(const std::shared_ptr<Task>& task_in, const Appointment& appointment_in):
- task(task_in), appointment(appointment_in) {}
+ auto on_appointments_fetched = [this](const std::vector<Appointment>& a){
+ g_debug("EdsPlanner %p got %zu appointments", this, a.size());
+ m_appointments.set(a);
};
- 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<AppointmentSubtask*>(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<struct icalrecurrencetype*>(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;
+ m_engine->get_appointments(r.first, r.second, *m_timezone.get(), on_appointments_fetched);
+}
- 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<UrlSubtask*>(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<Clock> m_clock;
- std::shared_ptr<Timezone> m_timezone;
- std::set<ESource*> m_sources;
- std::map<ESource*,ECalClient*> m_clients;
- std::map<ESource*,ECalClientView*> 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>& clock,
- const std::shared_ptr<Timezone>& timezone):
- p(new Impl(*this, clock, timezone)) {}
+core::Property<std::vector<Appointment>>& EdsPlanner::appointments()
+{
+ return m_appointments;
+}
-PlannerEds::~PlannerEds() =default;
+/***
+****
+***/
} // namespace datetime
} // namespace indicator