From 991d36f01d0b341a9c8735a09de1eb488ca3db66 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 19 May 2015 08:19:00 -0500 Subject: undo last Friday's timezone regression. --- src/engine-eds.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/engine-eds.cpp b/src/engine-eds.cpp index 9c1f7c4..e1424d7 100644 --- a/src/engine-eds.cpp +++ b/src/engine-eds.cpp @@ -571,7 +571,7 @@ private: const_cast(omit.data()), e_cal_client_resolve_tzid_cb, subtask->client, - nullptr); + subtask->default_timezone); std::map alarms; -- cgit v1.2.3 From c0a9ccc02ef49a79f64cda1bad67521de3d6feb2 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 19 May 2015 19:02:09 -0500 Subject: add tracers. --- src/engine-eds.cpp | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/engine-eds.cpp b/src/engine-eds.cpp index e1424d7..e662b3d 100644 --- a/src/engine-eds.cpp +++ b/src/engine-eds.cpp @@ -506,12 +506,24 @@ private: const DateTime begin_dt { gtz, begin }; const DateTime end_dt { gtz, end }; - g_debug ("got appointment from %s to %s, uid %s status %d", - begin_dt.format("%F %T %z").c_str(), - end_dt.format("%F %T %z").c_str(), + g_debug ("got appointment from %zu (%s) to %zu (%s), uid %s status %d", + begin, begin_dt.format("%F %T %z").c_str(), + end, end_dt.format("%F %T %z").c_str(), uid, (int)status); +e_cal_component_commit_sequence(component); +g_debug("%s e_cal_component_commit_sequence: %s", G_STRLOC, e_cal_component_get_as_string(component)); // FIXME leaks +ECalComponentDateTime eccdt; +e_cal_component_get_dtstart(component, &eccdt); +const bool is_floating_time = eccdt.tzid == nullptr; +g_debug("%s is_floating_time is %d", G_STRLOC, (int)is_floating_time); +g_debug("%s dtstart is %s", G_STRLOC, icaltime_as_ical_string(*(eccdt.value))); +g_debug("%s icaltime_as_timet(dtstart) is %zu", G_STRLOC, icaltime_as_timet(*eccdt.value)); +g_debug("%s icaltime_as_timet_with_zone(dtstart, %s) is %zu", G_STRLOC, location, icaltime_as_timet_with_zone(*eccdt.value, subtask->default_timezone)); +g_debug("%s dtstart as a DateTime is %s", G_STRLOC, DateTime(gtz, icaltime_as_timet(*eccdt.value)).format("%F %T %z").c_str()); + + // look for the in-house tags bool disabled = false; Appointment::Type type = Appointment::EVENT; @@ -542,7 +554,7 @@ private: auto icc = e_cal_component_get_icalcomponent(component); // component owns icc if (icc) { - g_debug("%s", icalcomponent_as_ical_string(icc)); // libical owns this string; no leak + g_debug("%s icalcomponent_as_ical_string: %s", G_STRLOC, icalcomponent_as_ical_string(icc)); // libical owns this string; no leak auto icalprop = icalcomponent_get_first_property(icc, ICAL_X_PROPERTY); while (icalprop) @@ -571,7 +583,7 @@ private: const_cast(omit.data()), e_cal_client_resolve_tzid_cb, subtask->client, - subtask->default_timezone); + is_floating_time ? nullptr : subtask->default_timezone); std::map alarms; @@ -582,6 +594,12 @@ private: auto ai = static_cast(l->data); auto a = e_cal_component_get_alarm(component, ai->auid); +g_debug("%s auid(%s), trigger(%zu), occur_start(%zu), occur_end(%zu)", G_STRLOC, ai->auid, ai->trigger, ai->occur_start, ai->occur_end); +g_debug("%s trigger as a DateTime('%s',%zu) is %s", G_STRLOC, location, ai->trigger, DateTime(gtz, ai->trigger).format("%F %T %z").c_str()); +auto local = DateTime::Local(ai->trigger); +g_debug("%s trigger as a DateTime::Local(%zu) is %s", G_STRLOC, ai->trigger, local.format("%F %T %z").c_str()); +g_debug("%s ..and then to_timezone('%s') is %s", G_STRLOC, location, local.to_timezone(location).format("%F %T %z").c_str()); + if (a != nullptr) { const DateTime alarm_begin{gtz, ai->trigger}; -- cgit v1.2.3 From f9ac5ed383bbd1f8f7c56dc5a09f99e08556c823 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 19 May 2015 21:06:35 -0500 Subject: work directly with ical to generate alarm instances --- src/engine-eds.cpp | 414 ++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 375 insertions(+), 39 deletions(-) (limited to 'src') diff --git a/src/engine-eds.cpp b/src/engine-eds.cpp index e662b3d..f541e44 100644 --- a/src/engine-eds.cpp +++ b/src/engine-eds.cpp @@ -79,9 +79,6 @@ public: 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()); @@ -133,15 +130,24 @@ public: client, color, default_timezone, - begin_timet, - end_timet); - e_cal_client_generate_instances(client, - begin_timet, - end_timet, - m_cancellable, - my_get_appointments_foreach, - subtask, - [](gpointer g){delete static_cast(g);}); + begin, + end); + + const auto begin_timet = begin.to_unix(); + const auto end_timet = end.to_unix(); + auto begin_str = isodate_from_time_t(begin_timet); + auto end_str = isodate_from_time_t(end_timet); + auto sexp = g_strdup_printf("(has-alarms-in-range? (make-time \"%s\") (make-time \"%s\"))", begin_str, end_str); + g_message("%s sexp is %s", G_STRLOC, sexp); + //e_cal_client_get_object_list(client, + e_cal_client_get_object_list_as_comps(client, + sexp, + m_cancellable, + on_object_list_ready, + subtask); + g_free(begin_str); + g_free(end_str); + g_free(sexp); } } @@ -419,24 +425,31 @@ private: ECalClient* client; std::string color; icaltimezone* default_timezone; - time_t begin; - time_t end; + GTimeZone * gtz; + DateTime begin; + DateTime end; AppointmentSubtask(const std::shared_ptr& task_in, ECalClient* client_in, const char* color_in, icaltimezone* default_tz, - time_t begin_, - time_t end_): + DateTime begin_, + DateTime end_): task(task_in), client(client_in), default_timezone(default_tz), + gtz(g_time_zone_new(icaltimezone_get_location(default_tz))), begin(begin_), end(end_) { if (color_in) color = color_in; } + + ~AppointmentSubtask() + { + g_clear_pointer(>z, g_time_zone_unref); + } }; static std::string get_alarm_text(ECalComponentAlarm * alarm) @@ -482,6 +495,346 @@ private: return ret; } + static void + on_object_list_ready(GObject * oclient, + GAsyncResult * res, + gpointer gsubtask) + { + GError * error = NULL; + GSList * comps = NULL; + + //if (e_cal_client_get_object_list_finish (E_CAL_CLIENT(oclient), res, &icalcomps, &error)) + if (e_cal_client_get_object_list_as_comps_finish (E_CAL_CLIENT(oclient), res, &comps, &error)) + { + auto subtask = static_cast(gsubtask); + + GSList * l; + for (l=comps; l!=nullptr; l=l->next) + add_component_to_task(subtask, static_cast(l->data)); + + e_cal_client_free_ecalcomp_slist(comps); + delete subtask; + } + else if (error != nullptr) + { + if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning("indicator-datetime cannot mark one-time alarm as disabled: %s", error->message); + + g_error_free(error); + } + } + + static DateTime + datetime_from_component_date_time(const ECalComponentDateTime & in, + GTimeZone * default_timezone) + { + DateTime out; + + g_return_val_if_fail(in.value != nullptr, out); + + auto gtz = in.tzid == nullptr ? g_time_zone_ref(default_timezone) + : g_time_zone_new(in.tzid); + out = DateTime(gtz, + in.value->year, + in.value->month, + in.value->day, + in.value->hour, + in.value->minute, + in.value->second); + g_time_zone_unref(gtz); + return out; + } + +#if 0 + static DateTime + to_datetime(struct icaltimetype& t, GTimeZone* gtz) + { + auto t_utc = icaltime_convert_to_zone(t, icaltimezone_get_utc_timezone()); + return DateTime(gtz, icaltime_as_timet(t_utc)); + } +#endif + + static struct icaltimetype + icaltimetype_from_datetime(const DateTime& dt, icaltimezone * default_timezone) + { + return icaltime_from_timet_with_zone(dt.to_unix(), false, default_timezone); + } + + static DateTime + get_trigger_time (icalcomponent* recurrence_instance, icalcomponent* valarm, GTimeZone* gtz) + { + DateTime ret; + + g_debug("%s getting trigger time for valarm %s", G_STRLOC, icalcomponent_as_ical_string(valarm)); + + auto trigger_property = icalcomponent_get_first_property(valarm, ICAL_TRIGGER_PROPERTY); + if (trigger_property == nullptr) + return ret; + + auto trigger = icalproperty_get_trigger(trigger_property); + g_debug("%s found a trigger", G_STRLOC); + g_debug("%s trigger time is %s", G_STRLOC, icaltime_as_ical_string(trigger.time)); + g_debug("%s trigger duration is %s", G_STRLOC, icaldurationtype_as_ical_string(trigger.duration)); + fflush(NULL); + + struct icaltimetype tt; + if (!icaltime_is_null_time(trigger.time)) + { + tt = trigger.time; + } + else + { + g_debug("%s got a NULL time, so it's relative to the component"); + // TRIGGER;RELATED=END:P5M 5 minutes after END + // TRIGGER;RELATED=START:-P15M 15 minutes before START + // get RELATED parameter + const char * related = icalproperty_get_parameter_as_string(trigger_property, "RELATED"); + if (related == nullptr) + related = "START"; // default value + g_debug("%s got a RELATED value of %s", G_STRLOC, related); + + struct icaltimetype reference_point; + if (!g_strcmp0(related, "START")) + reference_point = icalcomponent_get_dtstart(recurrence_instance); + else if (!g_strcmp0(related, "END")) + reference_point = icalcomponent_get_dtend(recurrence_instance); + else + reference_point = icaltime_null_time(); + + g_debug("%s reference point is %s", G_STRLOC, icaltime_as_ical_string(reference_point)); + tt = icaltime_add(reference_point, trigger.duration); + g_debug("%s reference point + offset is %s", G_STRLOC, icaltime_as_ical_string(tt)); + } + + if (icaltime_is_valid_time(tt)) + { + if (tt.zone == NULL) // floating time + ret = DateTime(gtz, tt.year, tt.month, tt.day, tt.hour, tt.minute, tt.second); + else // convert it to UTC for our time_t ctor + ret = DateTime(gtz, icaltime_as_timet(icaltime_convert_to_zone(tt, icaltimezone_get_utc_timezone()))); + } + + return ret; + } + + static void + add_component_to_task(AppointmentSubtask * subtask, + ECalComponent * component) + { + // we only want calendar events and vtodos + const auto vtype = e_cal_component_get_vtype(component); + if ((vtype != E_CAL_COMPONENT_EVENT) && (vtype != E_CAL_COMPONENT_TODO)) + return; + + // FIXME: leaks + g_message("%s --> %s", G_STRLOC, e_cal_component_get_as_string (component)); + + // get uid + const gchar* uid = nullptr; + e_cal_component_get_uid(component, &uid); + + // get status + auto status = ICAL_STATUS_NONE; + e_cal_component_get_status(component, &status); + + // get default timezone + const char * location = icaltimezone_get_location(subtask->default_timezone); + auto gtz = g_time_zone_new(location); + + // get dtstart as a DateTime + ECalComponentDateTime eccdt_tmp; + e_cal_component_get_dtstart(component, &eccdt_tmp); + const auto begin = datetime_from_component_date_time(eccdt_tmp, gtz); + e_cal_component_free_datetime(&eccdt_tmp); + + // get dtend as a DateTime + e_cal_component_get_dtend(component, &eccdt_tmp); + DateTime end = eccdt_tmp.value != nullptr ? datetime_from_component_date_time(eccdt_tmp, gtz) : begin; + e_cal_component_free_datetime(&eccdt_tmp); + + g_debug("got appointment from %s to %s, uid %s status %d", + begin.format("%F %T %z").c_str(), + end.format("%F %T %z").c_str(), + uid, + (int)status); + + auto instance_callback = [](icalcomponent *comp, struct icaltime_span *span, void *vsubtask) { + auto instance = icalcomponent_new_clone(comp); + auto subtask = static_cast(vsubtask); + icalcomponent_set_dtstart(instance, icaltime_from_timet(span->start, false)); + icalcomponent_set_dtend(instance, icaltime_from_timet(span->end, false)); + g_debug("instance %s", icalcomponent_as_ical_string(instance)); + + auto valarm = icalcomponent_get_first_component(instance, ICAL_VALARM_COMPONENT); + while (valarm != nullptr) { + g_debug("valarm %s", icalcomponent_as_ical_string(valarm)); + auto trigger_property = icalcomponent_get_first_property(valarm, ICAL_TRIGGER_PROPERTY); + if (trigger_property != nullptr) + { + auto trigger = icalproperty_get_trigger(trigger_property); + g_debug("%s found a trigger", G_STRLOC); + g_debug("%s trigger time is %s", G_STRLOC, icaltime_as_ical_string(trigger.time)); + g_debug("%s trigger duration is %s", G_STRLOC, icaldurationtype_as_ical_string(trigger.duration)); + if(!icaltime_is_null_time(trigger.time)) + g_debug("value=DATE-TIME:%s\n", icaltime_as_ical_string(trigger.time)); + else + g_debug("value=DURATION:%s\n", icaldurationtype_as_ical_string(trigger.duration)); + } + g_debug("%s %p", G_STRLOC, subtask->gtz); + auto trigger_time = get_trigger_time (instance, valarm, subtask->gtz); + g_debug("%s", G_STRLOC); + if (trigger_time.is_set()) + { + g_debug("%s whoo got trigger time! %s", G_STRLOC, trigger_time.format("%F %T %z").c_str()); + } + valarm = icalcomponent_get_next_component(instance, ICAL_VALARM_COMPONENT); + } + +/* +(process:28936): Indicator-Datetime-DEBUG: valarm BEGIN:VALARM^M +X-EVOLUTION-ALARM-UID:20150519T235024Z-22031-1000-21745-12@ghidorah^M +DESCRIPTION:Summary^M +ACTION:DISPLAY^M +TRIGGER;VALUE=DURATION;RELATED=START:-PT15M^M +END:VALARM^M +*/ + }; + + auto rbegin = icaltimetype_from_datetime(subtask->begin, subtask->default_timezone); + auto rend = icaltimetype_from_datetime(subtask->end, subtask->default_timezone); + auto icc = e_cal_component_get_icalcomponent(component); // component owns icc + g_debug("calling foreach-recurrence... [%s...%s]", icaltime_as_ical_string(rbegin), icaltime_as_ical_string(rend)); + icalcomponent_foreach_recurrence(icc, rbegin, rend, instance_callback, subtask); +#if 0 + // look for the in-house tags + bool disabled = false; + Appointment::Type type = Appointment::EVENT; + GSList * categ_list = nullptr; + e_cal_component_get_categories_list (component, &categ_list); + for (GSList * l=categ_list; l!=nullptr; l=l->next) { + auto tag = static_cast(l->data); + if (!g_strcmp0(tag, TAG_ALARM)) + type = Appointment::UBUNTU_ALARM; + if (!g_strcmp0(tag, TAG_DISABLED)) + disabled = true; + } + e_cal_component_free_categories_list(categ_list); + + if ((uid != nullptr) && + (!disabled) && + (status != ICAL_STATUS_COMPLETED) && + (status != ICAL_STATUS_CANCELLED)) + { + constexpr std::array omit = { (ECalComponentAlarmAction)-1 }; // list of action types to omit, terminated with -1 + Appointment appointment; + + ECalComponentText text {}; + e_cal_component_get_summary(component, &text); + if (text.value) + appointment.summary = text.value; + + auto icc = e_cal_component_get_icalcomponent(component); // component owns icc + if (icc) + { + g_debug("%s", icalcomponent_as_ical_string(icc)); // libical owns this string; no leak + + auto icalprop = icalcomponent_get_first_property(icc, ICAL_X_PROPERTY); + while (icalprop) + { + const char * x_name = icalproperty_get_x_name(icalprop); + if ((x_name != nullptr) && !g_ascii_strcasecmp(x_name, X_PROP_ACTIVATION_URL)) + { + const char * url = icalproperty_get_value_as_string(icalprop); + if ((url != nullptr) && appointment.activation_url.empty()) + appointment.activation_url = url; + } + + icalprop = icalcomponent_get_next_property(icc, ICAL_X_PROPERTY); + } + } + + appointment.begin = begin; + appointment.end = end; + appointment.color = subtask->color; + appointment.uid = uid; + appointment.type = type; + + auto e_alarms = e_cal_util_generate_alarms_for_comp(component, + begin.to_unix(), + end.to_unix(), + const_cast(omit.data()), + e_cal_client_resolve_tzid_cb, + subtask->client, + nullptr); + + std::map alarms; + + if (e_alarms != nullptr) + { + for (auto l=e_alarms->alarms; l!=nullptr; l=l->next) + { + auto ai = static_cast(l->data); + auto a = e_cal_component_get_alarm(component, ai->auid); + + if (a != nullptr) + { + const DateTime alarm_begin{gtz, ai->trigger}; + auto& alarm = alarms[alarm_begin]; + + if (alarm.text.empty()) + alarm.text = get_alarm_text(a); + if (alarm.audio_url.empty()) + alarm.audio_url = get_alarm_sound_url(a); + if (!alarm.time.is_set()) + alarm.time = alarm_begin; + + e_cal_component_alarm_free(a); + } + } + + e_cal_component_alarms_free(e_alarms); + } + // Hm, no alarm triggers? + // That's a bug in alarms created by some versions of ubuntu-ui-toolkit. + // If that's what's happening here, let's handle those alarms anyway + // by effectively injecting a TRIGGER;VALUE=DURATION;RELATED=START:PT0S + else if (appointment.is_ubuntu_alarm()) + { + Alarm tmp; + tmp.time = appointment.begin; + + auto auids = e_cal_component_get_alarm_uids(component); + for(auto l=auids; l!=nullptr; l=l->next) + { + const auto auid = static_cast(l->data); + auto a = e_cal_component_get_alarm(component, auid); + if (a != nullptr) + { + if (tmp.text.empty()) + tmp.text = get_alarm_text(a); + if (tmp.audio_url.empty()) + tmp.audio_url = get_alarm_sound_url(a); + e_cal_component_alarm_free(a); + } + } + cal_obj_uid_list_free(auids); + + alarms[tmp.time] = tmp; + } + + appointment.alarms.reserve(alarms.size()); + for (const auto& it : alarms) + appointment.alarms.push_back(it.second); + + subtask->task->appointments.push_back(appointment); + } + + g_time_zone_unref(gtz); +#endif + } + + +#if 0 static gboolean my_get_appointments_foreach(ECalComponent* component, time_t begin, @@ -506,24 +859,12 @@ private: const DateTime begin_dt { gtz, begin }; const DateTime end_dt { gtz, end }; - g_debug ("got appointment from %zu (%s) to %zu (%s), uid %s status %d", - begin, begin_dt.format("%F %T %z").c_str(), - end, end_dt.format("%F %T %z").c_str(), + g_debug ("got appointment from %s to %s, uid %s status %d", + begin_dt.format("%F %T %z").c_str(), + end_dt.format("%F %T %z").c_str(), uid, (int)status); -e_cal_component_commit_sequence(component); -g_debug("%s e_cal_component_commit_sequence: %s", G_STRLOC, e_cal_component_get_as_string(component)); // FIXME leaks -ECalComponentDateTime eccdt; -e_cal_component_get_dtstart(component, &eccdt); -const bool is_floating_time = eccdt.tzid == nullptr; -g_debug("%s is_floating_time is %d", G_STRLOC, (int)is_floating_time); -g_debug("%s dtstart is %s", G_STRLOC, icaltime_as_ical_string(*(eccdt.value))); -g_debug("%s icaltime_as_timet(dtstart) is %zu", G_STRLOC, icaltime_as_timet(*eccdt.value)); -g_debug("%s icaltime_as_timet_with_zone(dtstart, %s) is %zu", G_STRLOC, location, icaltime_as_timet_with_zone(*eccdt.value, subtask->default_timezone)); -g_debug("%s dtstart as a DateTime is %s", G_STRLOC, DateTime(gtz, icaltime_as_timet(*eccdt.value)).format("%F %T %z").c_str()); - - // look for the in-house tags bool disabled = false; Appointment::Type type = Appointment::EVENT; @@ -554,7 +895,7 @@ g_debug("%s dtstart as a DateTime is %s", G_STRLOC, DateTime(gtz, icaltime_as_ti auto icc = e_cal_component_get_icalcomponent(component); // component owns icc if (icc) { - g_debug("%s icalcomponent_as_ical_string: %s", G_STRLOC, icalcomponent_as_ical_string(icc)); // libical owns this string; no leak + g_debug("%s", icalcomponent_as_ical_string(icc)); // libical owns this string; no leak auto icalprop = icalcomponent_get_first_property(icc, ICAL_X_PROPERTY); while (icalprop) @@ -583,7 +924,7 @@ g_debug("%s dtstart as a DateTime is %s", G_STRLOC, DateTime(gtz, icaltime_as_ti const_cast(omit.data()), e_cal_client_resolve_tzid_cb, subtask->client, - is_floating_time ? nullptr : subtask->default_timezone); + nullptr); std::map alarms; @@ -594,12 +935,6 @@ g_debug("%s dtstart as a DateTime is %s", G_STRLOC, DateTime(gtz, icaltime_as_ti auto ai = static_cast(l->data); auto a = e_cal_component_get_alarm(component, ai->auid); -g_debug("%s auid(%s), trigger(%zu), occur_start(%zu), occur_end(%zu)", G_STRLOC, ai->auid, ai->trigger, ai->occur_start, ai->occur_end); -g_debug("%s trigger as a DateTime('%s',%zu) is %s", G_STRLOC, location, ai->trigger, DateTime(gtz, ai->trigger).format("%F %T %z").c_str()); -auto local = DateTime::Local(ai->trigger); -g_debug("%s trigger as a DateTime::Local(%zu) is %s", G_STRLOC, ai->trigger, local.format("%F %T %z").c_str()); -g_debug("%s ..and then to_timezone('%s') is %s", G_STRLOC, location, local.to_timezone(location).format("%F %T %z").c_str()); - if (a != nullptr) { const DateTime alarm_begin{gtz, ai->trigger}; @@ -658,6 +993,7 @@ g_debug("%s ..and then to_timezone('%s') is %s", G_STRLOC, location, local.to_ti return G_SOURCE_CONTINUE; } +#endif /*** **** -- cgit v1.2.3 From 14660dc8578b63b86337fa1b00eb8c4c734354cf Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 20 May 2015 06:28:25 -0500 Subject: add stub for valarm repeat --- src/engine-eds.cpp | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'src') diff --git a/src/engine-eds.cpp b/src/engine-eds.cpp index f541e44..53878b5 100644 --- a/src/engine-eds.cpp +++ b/src/engine-eds.cpp @@ -686,6 +686,13 @@ private: if (trigger_time.is_set()) { g_debug("%s whoo got trigger time! %s", G_STRLOC, trigger_time.format("%F %T %z").c_str()); + + auto duration_property = icalcomponent_get_first_property(valarm, ICAL_DURATION_PROPERTY); + auto repeat_property = icalcomponent_get_first_property(valarm, ICAL_DURATION_PROPERTY); + if ((duration_property != nullptr) && (repeat_property != nullptr)) + { + // FIXME: implement repeat + } } valarm = icalcomponent_get_next_component(instance, ICAL_VALARM_COMPONENT); } -- cgit v1.2.3 From 13ec568f7be0f3d9d3200334d331fee2ea39d9da Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 20 May 2015 06:37:06 -0500 Subject: what if we try calling generate_alarms() with the original component rather than the instance component... --- src/engine-eds.cpp | 49 +++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 45 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/engine-eds.cpp b/src/engine-eds.cpp index 53878b5..adcdb63 100644 --- a/src/engine-eds.cpp +++ b/src/engine-eds.cpp @@ -141,10 +141,10 @@ public: g_message("%s sexp is %s", G_STRLOC, sexp); //e_cal_client_get_object_list(client, e_cal_client_get_object_list_as_comps(client, - sexp, - m_cancellable, - on_object_list_ready, - subtask); + sexp, + m_cancellable, + on_object_list_ready, + subtask); g_free(begin_str); g_free(end_str); g_free(sexp); @@ -658,6 +658,45 @@ private: uid, (int)status); + constexpr std::array omit = { (ECalComponentAlarmAction)-1 }; // list of action types to omit, terminated with -1 + auto e_alarms = e_cal_util_generate_alarms_for_comp (component, + subtask->begin.to_unix(), + subtask->end.to_unix(), + const_cast(omit.data()), + e_cal_client_resolve_tzid_cb, + subtask->client, + subtask->default_timezone); + if (e_alarms != nullptr) + { + for (auto l=e_alarms->alarms; l!=nullptr; l=l->next) + { + auto ai = static_cast(l->data); + auto a = e_cal_component_get_alarm(component, ai->auid); + + if (a != nullptr) + { +g_message("%s creating alarm_begin from ai->trigger %zu", G_STRLOC, (size_t)ai->trigger); + const DateTime alarm_begin{gtz, ai->trigger}; +g_message("%s alarm_begin is %s", alarm_begin.format("%F %T %z").c_str()); +#if 0 + auto& alarm = alarms[alarm_begin]; + + if (alarm.text.empty()) + alarm.text = get_alarm_text(a); + if (alarm.audio_url.empty()) + alarm.audio_url = get_alarm_sound_url(a); + if (!alarm.time.is_set()) + alarm.time = alarm_begin; +#endif + + e_cal_component_alarm_free(a); + } + } + + e_cal_component_alarms_free(e_alarms); + } + +#if 0 auto instance_callback = [](icalcomponent *comp, struct icaltime_span *span, void *vsubtask) { auto instance = icalcomponent_new_clone(comp); auto subtask = static_cast(vsubtask); @@ -712,6 +751,8 @@ END:VALARM^M auto icc = e_cal_component_get_icalcomponent(component); // component owns icc g_debug("calling foreach-recurrence... [%s...%s]", icaltime_as_ical_string(rbegin), icaltime_as_ical_string(rend)); icalcomponent_foreach_recurrence(icc, rbegin, rend, instance_callback, subtask); +#endif + #if 0 // look for the in-house tags bool disabled = false; -- cgit v1.2.3 From 03ad335029ca9c970ac3b7fbeab3f657e4718a71 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 20 May 2015 07:16:43 -0500 Subject: yes, generating alarms from the reference component, rather than the instance component, yields the right trigger timezones. Let's get to refactoring. --- src/engine-eds.cpp | 101 +++++++++++++++++++++++++++-------------------------- 1 file changed, 52 insertions(+), 49 deletions(-) (limited to 'src') diff --git a/src/engine-eds.cpp b/src/engine-eds.cpp index adcdb63..5d423da 100644 --- a/src/engine-eds.cpp +++ b/src/engine-eds.cpp @@ -501,19 +501,38 @@ private: gpointer gsubtask) { GError * error = NULL; - GSList * comps = NULL; + GSList * comps_slist = NULL; - //if (e_cal_client_get_object_list_finish (E_CAL_CLIENT(oclient), res, &icalcomps, &error)) - if (e_cal_client_get_object_list_as_comps_finish (E_CAL_CLIENT(oclient), res, &comps, &error)) + if (e_cal_client_get_object_list_as_comps_finish (E_CAL_CLIENT(oclient), res, &comps_slist, &error)) { - auto subtask = static_cast(gsubtask); + // _generate_alarms takes a GList, so make a shallow copy of the comps GSList + GList * comps_list = nullptr; + for (auto l=comps_slist; l!=nullptr; l=l->next) + comps_list = g_list_prepend(comps_list, l->data); - GSList * l; - for (l=comps; l!=nullptr; l=l->next) - add_component_to_task(subtask, static_cast(l->data)); + constexpr std::array omit = { (ECalComponentAlarmAction)-1 }; // list of action types to omit, terminated with -1 + GSList * comp_alarms = nullptr; + auto subtask = static_cast(gsubtask); + e_cal_util_generate_alarms_for_list(comps_list, + subtask->begin.to_unix(), + subtask->end.to_unix(), + const_cast(omit.data()), + &comp_alarms, + e_cal_client_resolve_tzid_cb, + oclient, + subtask->default_timezone); + + // walk the alarms & add them + const char * location = icaltimezone_get_location(subtask->default_timezone); + auto gtz = g_time_zone_new(location); + for (auto l=comp_alarms; l!=nullptr; l=l->next) + add_alarms_to_subtask(static_cast(l->data), subtask, gtz); + g_time_zone_unref(gtz); - e_cal_client_free_ecalcomp_slist(comps); - delete subtask; + // cleanup + e_cal_free_alarms(comp_alarms); + g_list_free(comps_list); + e_cal_client_free_ecalcomp_slist(comps_slist); } else if (error != nullptr) { @@ -618,37 +637,34 @@ private: } static void - add_component_to_task(AppointmentSubtask * subtask, - ECalComponent * component) + add_alarms_to_subtask (ECalComponentAlarms * comp_alarms, + AppointmentSubtask * subtask, + GTimeZone * gtz) { // we only want calendar events and vtodos - const auto vtype = e_cal_component_get_vtype(component); + const auto vtype = e_cal_component_get_vtype(comp_alarms->comp); if ((vtype != E_CAL_COMPONENT_EVENT) && (vtype != E_CAL_COMPONENT_TODO)) return; // FIXME: leaks - g_message("%s --> %s", G_STRLOC, e_cal_component_get_as_string (component)); + g_message("%s --> %s", G_STRLOC, e_cal_component_get_as_string (comp_alarms->comp)); // get uid const gchar* uid = nullptr; - e_cal_component_get_uid(component, &uid); + e_cal_component_get_uid(comp_alarms->comp, &uid); // get status auto status = ICAL_STATUS_NONE; - e_cal_component_get_status(component, &status); - - // get default timezone - const char * location = icaltimezone_get_location(subtask->default_timezone); - auto gtz = g_time_zone_new(location); + e_cal_component_get_status(comp_alarms->comp, &status); // get dtstart as a DateTime ECalComponentDateTime eccdt_tmp; - e_cal_component_get_dtstart(component, &eccdt_tmp); + e_cal_component_get_dtstart(comp_alarms->comp, &eccdt_tmp); const auto begin = datetime_from_component_date_time(eccdt_tmp, gtz); e_cal_component_free_datetime(&eccdt_tmp); // get dtend as a DateTime - e_cal_component_get_dtend(component, &eccdt_tmp); + e_cal_component_get_dtend(comp_alarms->comp, &eccdt_tmp); DateTime end = eccdt_tmp.value != nullptr ? datetime_from_component_date_time(eccdt_tmp, gtz) : begin; e_cal_component_free_datetime(&eccdt_tmp); @@ -658,42 +674,29 @@ private: uid, (int)status); - constexpr std::array omit = { (ECalComponentAlarmAction)-1 }; // list of action types to omit, terminated with -1 - auto e_alarms = e_cal_util_generate_alarms_for_comp (component, - subtask->begin.to_unix(), - subtask->end.to_unix(), - const_cast(omit.data()), - e_cal_client_resolve_tzid_cb, - subtask->client, - subtask->default_timezone); - if (e_alarms != nullptr) + for (auto l=comp_alarms->alarms; l!=nullptr; l=l->next) { - for (auto l=e_alarms->alarms; l!=nullptr; l=l->next) - { - auto ai = static_cast(l->data); - auto a = e_cal_component_get_alarm(component, ai->auid); + auto ai = static_cast(l->data); + auto a = e_cal_component_get_alarm(comp_alarms->comp, ai->auid); - if (a != nullptr) - { + if (a != nullptr) + { g_message("%s creating alarm_begin from ai->trigger %zu", G_STRLOC, (size_t)ai->trigger); - const DateTime alarm_begin{gtz, ai->trigger}; + const DateTime alarm_begin{gtz, ai->trigger}; g_message("%s alarm_begin is %s", alarm_begin.format("%F %T %z").c_str()); #if 0 - auto& alarm = alarms[alarm_begin]; - - if (alarm.text.empty()) - alarm.text = get_alarm_text(a); - if (alarm.audio_url.empty()) - alarm.audio_url = get_alarm_sound_url(a); - if (!alarm.time.is_set()) - alarm.time = alarm_begin; + auto& alarm = alarms[alarm_begin]; + + if (alarm.text.empty()) + alarm.text = get_alarm_text(a); + if (alarm.audio_url.empty()) + alarm.audio_url = get_alarm_sound_url(a); + if (!alarm.time.is_set()) + alarm.time = alarm_begin; #endif - e_cal_component_alarm_free(a); - } + e_cal_component_alarm_free(a); } - - e_cal_component_alarms_free(e_alarms); } #if 0 -- cgit v1.2.3 From c91b1cc89bd83608e225d67983c69a9e08f93873 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 20 May 2015 08:07:15 -0500 Subject: first eds test passing --- src/engine-eds.cpp | 268 +++++++++++++++-------------------------------------- 1 file changed, 76 insertions(+), 192 deletions(-) (limited to 'src') diff --git a/src/engine-eds.cpp b/src/engine-eds.cpp index 5d423da..e49ba63 100644 --- a/src/engine-eds.cpp +++ b/src/engine-eds.cpp @@ -533,6 +533,7 @@ private: e_cal_free_alarms(comp_alarms); g_list_free(comps_list); e_cal_client_free_ecalcomp_slist(comps_slist); + delete subtask; } else if (error != nullptr) { @@ -641,50 +642,91 @@ private: AppointmentSubtask * subtask, GTimeZone * gtz) { + /** + *** Filter out components that we're not interested in + **/ + + auto& component = comp_alarms->comp; + // we only want calendar events and vtodos - const auto vtype = e_cal_component_get_vtype(comp_alarms->comp); + const auto vtype = e_cal_component_get_vtype(component); if ((vtype != E_CAL_COMPONENT_EVENT) && (vtype != E_CAL_COMPONENT_TODO)) return; - // FIXME: leaks - g_message("%s --> %s", G_STRLOC, e_cal_component_get_as_string (comp_alarms->comp)); + // we're not interested in completed or cancelled components + auto status = ICAL_STATUS_NONE; + e_cal_component_get_status(component, &status); + if ((status == ICAL_STATUS_COMPLETED) || (status == ICAL_STATUS_CANCELLED)) + return; - // get uid const gchar* uid = nullptr; - e_cal_component_get_uid(comp_alarms->comp, &uid); + e_cal_component_get_uid(component, &uid); + if (uid == nullptr) + return; - // get status - auto status = ICAL_STATUS_NONE; - e_cal_component_get_status(comp_alarms->comp, &status); + // walk through the cattegories list to look for our in-house tags + bool disabled = false; + Appointment::Type type = Appointment::EVENT; + GSList * categ_list = nullptr; + e_cal_component_get_categories_list (component, &categ_list); + for (GSList * l=categ_list; l!=nullptr; l=l->next) { + auto tag = static_cast(l->data); + if (!g_strcmp0(tag, TAG_ALARM)) + type = Appointment::UBUNTU_ALARM; + if (!g_strcmp0(tag, TAG_DISABLED)) + disabled = true; + } + e_cal_component_free_categories_list(categ_list); + if (disabled) // don't notify users about disabled alarms... + return; + + /** + *** This looks like a component that we should notify users about. + *** Let's add it. + **/ + + Appointment appointment; + appointment.uid = uid; + appointment.color = subtask->color; + appointment.type = type; + + // get appointment.summary + ECalComponentText text {}; + e_cal_component_get_summary(component, &text); + if (text.value) + appointment.summary = text.value; // get dtstart as a DateTime ECalComponentDateTime eccdt_tmp; - e_cal_component_get_dtstart(comp_alarms->comp, &eccdt_tmp); - const auto begin = datetime_from_component_date_time(eccdt_tmp, gtz); + e_cal_component_get_dtstart(component, &eccdt_tmp); + //const auto begin = datetime_from_component_date_time(eccdt_tmp, gtz); + appointment.begin = datetime_from_component_date_time(eccdt_tmp, gtz); e_cal_component_free_datetime(&eccdt_tmp); // get dtend as a DateTime - e_cal_component_get_dtend(comp_alarms->comp, &eccdt_tmp); - DateTime end = eccdt_tmp.value != nullptr ? datetime_from_component_date_time(eccdt_tmp, gtz) : begin; + e_cal_component_get_dtend(component, &eccdt_tmp); + //DateTime end = eccdt_tmp.value != nullptr ? datetime_from_component_date_time(eccdt_tmp, gtz) : begin; + appointment.end = eccdt_tmp.value != nullptr ? datetime_from_component_date_time(eccdt_tmp, gtz) : appointment.begin; e_cal_component_free_datetime(&eccdt_tmp); g_debug("got appointment from %s to %s, uid %s status %d", - begin.format("%F %T %z").c_str(), - end.format("%F %T %z").c_str(), + appointment.begin.format("%F %T %z").c_str(), + appointment.end.format("%F %T %z").c_str(), uid, (int)status); + // build our Alarm map from the comp_alarms + std::map alarms; for (auto l=comp_alarms->alarms; l!=nullptr; l=l->next) { auto ai = static_cast(l->data); - auto a = e_cal_component_get_alarm(comp_alarms->comp, ai->auid); + auto a = e_cal_component_get_alarm(component, ai->auid); if (a != nullptr) { g_message("%s creating alarm_begin from ai->trigger %zu", G_STRLOC, (size_t)ai->trigger); const DateTime alarm_begin{gtz, ai->trigger}; -g_message("%s alarm_begin is %s", alarm_begin.format("%F %T %z").c_str()); -#if 0 +g_message("%s alarm_begin is %s", G_STRLOC, alarm_begin.format("%F %T %z").c_str()); auto& alarm = alarms[alarm_begin]; if (alarm.text.empty()) @@ -693,195 +735,37 @@ g_message("%s alarm_begin is %s", alarm_begin.format("%F %T %z").c_str()); alarm.audio_url = get_alarm_sound_url(a); if (!alarm.time.is_set()) alarm.time = alarm_begin; -#endif e_cal_component_alarm_free(a); } } -#if 0 - auto instance_callback = [](icalcomponent *comp, struct icaltime_span *span, void *vsubtask) { - auto instance = icalcomponent_new_clone(comp); - auto subtask = static_cast(vsubtask); - icalcomponent_set_dtstart(instance, icaltime_from_timet(span->start, false)); - icalcomponent_set_dtend(instance, icaltime_from_timet(span->end, false)); - g_debug("instance %s", icalcomponent_as_ical_string(instance)); - - auto valarm = icalcomponent_get_first_component(instance, ICAL_VALARM_COMPONENT); - while (valarm != nullptr) { - g_debug("valarm %s", icalcomponent_as_ical_string(valarm)); - auto trigger_property = icalcomponent_get_first_property(valarm, ICAL_TRIGGER_PROPERTY); - if (trigger_property != nullptr) - { - auto trigger = icalproperty_get_trigger(trigger_property); - g_debug("%s found a trigger", G_STRLOC); - g_debug("%s trigger time is %s", G_STRLOC, icaltime_as_ical_string(trigger.time)); - g_debug("%s trigger duration is %s", G_STRLOC, icaldurationtype_as_ical_string(trigger.duration)); - if(!icaltime_is_null_time(trigger.time)) - g_debug("value=DATE-TIME:%s\n", icaltime_as_ical_string(trigger.time)); - else - g_debug("value=DURATION:%s\n", icaldurationtype_as_ical_string(trigger.duration)); - } - g_debug("%s %p", G_STRLOC, subtask->gtz); - auto trigger_time = get_trigger_time (instance, valarm, subtask->gtz); - g_debug("%s", G_STRLOC); - if (trigger_time.is_set()) - { - g_debug("%s whoo got trigger time! %s", G_STRLOC, trigger_time.format("%F %T %z").c_str()); - - auto duration_property = icalcomponent_get_first_property(valarm, ICAL_DURATION_PROPERTY); - auto repeat_property = icalcomponent_get_first_property(valarm, ICAL_DURATION_PROPERTY); - if ((duration_property != nullptr) && (repeat_property != nullptr)) - { - // FIXME: implement repeat - } - } - valarm = icalcomponent_get_next_component(instance, ICAL_VALARM_COMPONENT); - } - -/* -(process:28936): Indicator-Datetime-DEBUG: valarm BEGIN:VALARM^M -X-EVOLUTION-ALARM-UID:20150519T235024Z-22031-1000-21745-12@ghidorah^M -DESCRIPTION:Summary^M -ACTION:DISPLAY^M -TRIGGER;VALUE=DURATION;RELATED=START:-PT15M^M -END:VALARM^M -*/ - }; - - auto rbegin = icaltimetype_from_datetime(subtask->begin, subtask->default_timezone); - auto rend = icaltimetype_from_datetime(subtask->end, subtask->default_timezone); - auto icc = e_cal_component_get_icalcomponent(component); // component owns icc - g_debug("calling foreach-recurrence... [%s...%s]", icaltime_as_ical_string(rbegin), icaltime_as_ical_string(rend)); - icalcomponent_foreach_recurrence(icc, rbegin, rend, instance_callback, subtask); -#endif - -#if 0 - // look for the in-house tags - bool disabled = false; - Appointment::Type type = Appointment::EVENT; - GSList * categ_list = nullptr; - e_cal_component_get_categories_list (component, &categ_list); - for (GSList * l=categ_list; l!=nullptr; l=l->next) { - auto tag = static_cast(l->data); - if (!g_strcmp0(tag, TAG_ALARM)) - type = Appointment::UBUNTU_ALARM; - if (!g_strcmp0(tag, TAG_DISABLED)) - disabled = true; - } - e_cal_component_free_categories_list(categ_list); - - if ((uid != nullptr) && - (!disabled) && - (status != ICAL_STATUS_COMPLETED) && - (status != ICAL_STATUS_CANCELLED)) + // look for our recognized e-props + auto icc = e_cal_component_get_icalcomponent(component); // icc owned by component + if (icc != nullptr) { - constexpr std::array omit = { (ECalComponentAlarmAction)-1 }; // list of action types to omit, terminated with -1 - Appointment appointment; - - ECalComponentText text {}; - e_cal_component_get_summary(component, &text); - if (text.value) - appointment.summary = text.value; + g_debug("%s", icalcomponent_as_ical_string(icc)); // libical owns this string; no leak - auto icc = e_cal_component_get_icalcomponent(component); // component owns icc - if (icc) + auto icalprop = icalcomponent_get_first_property(icc, ICAL_X_PROPERTY); + while (icalprop) { - g_debug("%s", icalcomponent_as_ical_string(icc)); // libical owns this string; no leak - - auto icalprop = icalcomponent_get_first_property(icc, ICAL_X_PROPERTY); - while (icalprop) + const char * x_name = icalproperty_get_x_name(icalprop); + if ((x_name != nullptr) && !g_ascii_strcasecmp(x_name, X_PROP_ACTIVATION_URL)) { - const char * x_name = icalproperty_get_x_name(icalprop); - if ((x_name != nullptr) && !g_ascii_strcasecmp(x_name, X_PROP_ACTIVATION_URL)) - { - const char * url = icalproperty_get_value_as_string(icalprop); - if ((url != nullptr) && appointment.activation_url.empty()) - appointment.activation_url = url; - } - - icalprop = icalcomponent_get_next_property(icc, ICAL_X_PROPERTY); + const char * url = icalproperty_get_value_as_string(icalprop); + if ((url != nullptr) && appointment.activation_url.empty()) + appointment.activation_url = url; } - } - appointment.begin = begin; - appointment.end = end; - appointment.color = subtask->color; - appointment.uid = uid; - appointment.type = type; - - auto e_alarms = e_cal_util_generate_alarms_for_comp(component, - begin.to_unix(), - end.to_unix(), - const_cast(omit.data()), - e_cal_client_resolve_tzid_cb, - subtask->client, - nullptr); - - std::map alarms; - - if (e_alarms != nullptr) - { - for (auto l=e_alarms->alarms; l!=nullptr; l=l->next) - { - auto ai = static_cast(l->data); - auto a = e_cal_component_get_alarm(component, ai->auid); - - if (a != nullptr) - { - const DateTime alarm_begin{gtz, ai->trigger}; - auto& alarm = alarms[alarm_begin]; - - if (alarm.text.empty()) - alarm.text = get_alarm_text(a); - if (alarm.audio_url.empty()) - alarm.audio_url = get_alarm_sound_url(a); - if (!alarm.time.is_set()) - alarm.time = alarm_begin; - - e_cal_component_alarm_free(a); - } - } - - e_cal_component_alarms_free(e_alarms); - } - // Hm, no alarm triggers? - // That's a bug in alarms created by some versions of ubuntu-ui-toolkit. - // If that's what's happening here, let's handle those alarms anyway - // by effectively injecting a TRIGGER;VALUE=DURATION;RELATED=START:PT0S - else if (appointment.is_ubuntu_alarm()) - { - Alarm tmp; - tmp.time = appointment.begin; - - auto auids = e_cal_component_get_alarm_uids(component); - for(auto l=auids; l!=nullptr; l=l->next) - { - const auto auid = static_cast(l->data); - auto a = e_cal_component_get_alarm(component, auid); - if (a != nullptr) - { - if (tmp.text.empty()) - tmp.text = get_alarm_text(a); - if (tmp.audio_url.empty()) - tmp.audio_url = get_alarm_sound_url(a); - e_cal_component_alarm_free(a); - } - } - cal_obj_uid_list_free(auids); - - alarms[tmp.time] = tmp; + icalprop = icalcomponent_get_next_property(icc, ICAL_X_PROPERTY); } - - appointment.alarms.reserve(alarms.size()); - for (const auto& it : alarms) - appointment.alarms.push_back(it.second); - - subtask->task->appointments.push_back(appointment); } - g_time_zone_unref(gtz); -#endif + appointment.alarms.reserve(alarms.size()); + for (const auto& it : alarms) + appointment.alarms.push_back(it.second); + + subtask->task->appointments.push_back(appointment); } -- cgit v1.2.3 From 45122eb294b281fb939aea315fa35ddb96cf44d1 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 20 May 2015 17:04:09 -0500 Subject: fixed first test passing --- src/engine-eds.cpp | 205 ++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 156 insertions(+), 49 deletions(-) (limited to 'src') diff --git a/src/engine-eds.cpp b/src/engine-eds.cpp index e49ba63..6302f06 100644 --- a/src/engine-eds.cpp +++ b/src/engine-eds.cpp @@ -138,7 +138,7 @@ public: auto begin_str = isodate_from_time_t(begin_timet); auto end_str = isodate_from_time_t(end_timet); auto sexp = g_strdup_printf("(has-alarms-in-range? (make-time \"%s\") (make-time \"%s\"))", begin_str, end_str); - g_message("%s sexp is %s", G_STRLOC, sexp); + g_debug("%s sexp is %s", G_STRLOC, sexp); //e_cal_client_get_object_list(client, e_cal_client_get_object_list_as_comps(client, sexp, @@ -572,7 +572,6 @@ private: auto t_utc = icaltime_convert_to_zone(t, icaltimezone_get_utc_timezone()); return DateTime(gtz, icaltime_as_timet(t_utc)); } -#endif static struct icaltimetype icaltimetype_from_datetime(const DateTime& dt, icaltimezone * default_timezone) @@ -636,17 +635,102 @@ private: return ret; } +#endif + +#if 0 +struct instance_data { + time_t start; + gboolean found; +}; + +static void +check_instance (icalcomponent *, + struct icaltime_span *span, + gpointer data) +{ + auto instance = static_cast(data); +auto start = icaltime_from_timet(span->start, false); +g_debug("%s span->start %s(%zu) instance->start %zu", G_STRLOC, icaltime_as_ical_string(start), (size_t)span->start, (size_t)instance->start); + + if (span->start == instance->start) + { +g_debug("%s FOUND!", G_STRLOC); + instance->found = TRUE; + } +} + +/** + * e_cal_util_construct_instance: + * @icalcomp: A recurring #icalcomponent + * @rid: The RECURRENCE-ID to construct a component for + * + * This checks that @rid indicates a valid recurrence of @icalcomp, and + * if so, generates a copy of @comp containing a RECURRENCE-ID of @rid. + * + * Returns: the instance, or %NULL. + **/ +static icalcomponent * +foo_cal_util_construct_instance (icalcomponent *icalcomp, + struct icaltimetype rid) +{ + struct instance_data instance; + struct icaltimetype start, end; + +g_debug("%s looking for instance at %s", G_STRLOC, icaltime_as_ical_string(rid)); +auto dtstart = icalcomponent_get_dtstart(icalcomp); +g_debug("%s in icalcomp whose dtstart is %s(%zu)", G_STRLOC, icaltime_as_ical_string(dtstart), (size_t)icaltime_as_timet(dtstart)); + + g_return_val_if_fail (icalcomp != NULL, NULL); + +g_debug("%s", G_STRLOC); + /* Make sure this is really recurring */ + if (!icalcomponent_get_first_property (icalcomp, ICAL_RRULE_PROPERTY) && + !icalcomponent_get_first_property (icalcomp, ICAL_RDATE_PROPERTY)) + return NULL; + +g_debug("%s", G_STRLOC); + /* Make sure the specified instance really exists */ + start = icaltime_convert_to_zone (rid, icaltimezone_get_utc_timezone ()); + instance.start = icaltime_as_timet(start); +g_debug("%s start %s (%zu)", G_STRLOC, icaltime_as_ical_string(start), instance.start); + end = start; + icaltime_adjust (&end, 0, 0, 0, 1); +g_debug("%s end %s", G_STRLOC, icaltime_as_ical_string(end)); + icaltime_adjust (&start, -30, 0, 0, 1); + icaltime_adjust (&end, 30, 0, 0, 1); +g_debug("%s start %s", G_STRLOC, icaltime_as_ical_string(start)); +g_debug("%s end %s", G_STRLOC, icaltime_as_ical_string(end)); + + //instance.start = icaltime_convert_to_zone (rid, icaltimezone_get_utc_timezone ()); + instance.found = FALSE; + icalcomponent_foreach_recurrence (icalcomp, start, end, + check_instance, &instance); + if (!instance.found) { +g_debug("%s not found", G_STRLOC); + return NULL; + } + + /* Make the instance */ + icalcomp = icalcomponent_new_clone (icalcomp); + icalcomponent_set_recurrenceid (icalcomp, rid); + + return icalcomp; +} +#endif + + + static void add_alarms_to_subtask (ECalComponentAlarms * comp_alarms, AppointmentSubtask * subtask, GTimeZone * gtz) { + auto& component = comp_alarms->comp; + /** *** Filter out components that we're not interested in **/ - - auto& component = comp_alarms->comp; // we only want calendar events and vtodos const auto vtype = e_cal_component_get_vtype(component); @@ -659,12 +743,7 @@ private: if ((status == ICAL_STATUS_COMPLETED) || (status == ICAL_STATUS_CANCELLED)) return; - const gchar* uid = nullptr; - e_cal_component_get_uid(component, &uid); - if (uid == nullptr) - return; - - // walk through the cattegories list to look for our in-house tags + // walk through the categories list to look for our in-house tags bool disabled = false; Appointment::Type type = Appointment::EVENT; GSList * categ_list = nullptr; @@ -685,67 +764,94 @@ private: *** Let's add it. **/ - Appointment appointment; - appointment.uid = uid; - appointment.color = subtask->color; - appointment.type = type; + Appointment reference_appointment; + reference_appointment.color = subtask->color; + reference_appointment.type = type; + + // get appointment.uid + const gchar* uid = nullptr; + e_cal_component_get_uid(component, &uid); + if (uid != nullptr) + reference_appointment.uid = uid; // get appointment.summary ECalComponentText text {}; e_cal_component_get_summary(component, &text); if (text.value) - appointment.summary = text.value; + reference_appointment.summary = text.value; - // get dtstart as a DateTime - ECalComponentDateTime eccdt_tmp; + // get appointment.begin + ECalComponentDateTime eccdt_tmp {}; e_cal_component_get_dtstart(component, &eccdt_tmp); - //const auto begin = datetime_from_component_date_time(eccdt_tmp, gtz); - appointment.begin = datetime_from_component_date_time(eccdt_tmp, gtz); + reference_appointment.begin = datetime_from_component_date_time(eccdt_tmp, gtz); e_cal_component_free_datetime(&eccdt_tmp); - // get dtend as a DateTime + // get appointment.end e_cal_component_get_dtend(component, &eccdt_tmp); - //DateTime end = eccdt_tmp.value != nullptr ? datetime_from_component_date_time(eccdt_tmp, gtz) : begin; - appointment.end = eccdt_tmp.value != nullptr ? datetime_from_component_date_time(eccdt_tmp, gtz) : appointment.begin; + reference_appointment.end = eccdt_tmp.value != nullptr + ? datetime_from_component_date_time(eccdt_tmp, gtz) + : reference_appointment.begin; e_cal_component_free_datetime(&eccdt_tmp); - g_debug("got appointment from %s to %s, uid %s status %d", - appointment.begin.format("%F %T %z").c_str(), - appointment.end.format("%F %T %z").c_str(), - uid, + g_debug("got appointment from %s to %s, status %d", + reference_appointment.begin.format("%F %T %z").c_str(), + reference_appointment.end.format("%F %T %z").c_str(), (int)status); // build our Alarm map from the comp_alarms - std::map alarms; + auto icc = e_cal_component_get_icalcomponent(component); // icc owned by component + g_debug("%s icc as ical string: %s", G_STRLOC, icalcomponent_as_ical_string(icc)); + + /* outer map key is the component instance's start + end time; we build Appointment.begin and .end from that. + * inner map key is the alarm trigger, we build Alarm.time from that. + * inner map value is the Alarm. + * + * we use the inner map to fold together multiple valarms that occur at the same time; + * e.g. one valarm may have a display action and another an audio action + */ + std::map,std::map> alarms; for (auto l=comp_alarms->alarms; l!=nullptr; l=l->next) { auto ai = static_cast(l->data); auto a = e_cal_component_get_alarm(component, ai->auid); - - if (a != nullptr) - { -g_message("%s creating alarm_begin from ai->trigger %zu", G_STRLOC, (size_t)ai->trigger); - const DateTime alarm_begin{gtz, ai->trigger}; -g_message("%s alarm_begin is %s", G_STRLOC, alarm_begin.format("%F %T %z").c_str()); - auto& alarm = alarms[alarm_begin]; - - if (alarm.text.empty()) - alarm.text = get_alarm_text(a); - if (alarm.audio_url.empty()) - alarm.audio_url = get_alarm_sound_url(a); - if (!alarm.time.is_set()) - alarm.time = alarm_begin; - - e_cal_component_alarm_free(a); - } + if (a == nullptr) + continue; + + const auto instance_time = std::make_pair(DateTime{gtz, ai->occur_start}, + DateTime{gtz, ai->occur_end}); + const auto trigger_time = DateTime{gtz, ai->trigger}; + +g_message("%s occur_start %zu(%s)", G_STRLOC, (size_t)ai->occur_start, instance_time.first.format("%F %T %z").c_str()); +g_message("%s occur_end %zu(%s)", G_STRLOC, (size_t)ai->occur_end, instance_time.second.format("%F %T %z").c_str()); +g_message("%s trigger_time %zu(%s)", G_STRLOC, (size_t)ai->trigger, trigger_time.format("%F %T %z").c_str()); + auto& alarm = alarms[instance_time][trigger_time]; + + if (alarm.text.empty()) + alarm.text = get_alarm_text(a); + if (alarm.audio_url.empty()) + alarm.audio_url = get_alarm_sound_url(a); + if (!alarm.time.is_set()) + alarm.time = trigger_time; + + e_cal_component_alarm_free(a); } - // look for our recognized e-props - auto icc = e_cal_component_get_icalcomponent(component); // icc owned by component - if (icc != nullptr) + // now build the appointments + for (auto& i : alarms) { - g_debug("%s", icalcomponent_as_ical_string(icc)); // libical owns this string; no leak + Appointment appointment = reference_appointment; + appointment.begin = i.first.first; + appointment.end = i.first.second; + appointment.alarms.reserve(i.second.size()); + for (auto& j : i.second) + appointment.alarms.push_back(j.second); + subtask->task->appointments.push_back(appointment); + } + // look for our recognized e-props + //if (icc != nullptr) + //{ +#if 0 auto icalprop = icalcomponent_get_first_property(icc, ICAL_X_PROPERTY); while (icalprop) { @@ -759,13 +865,14 @@ g_message("%s alarm_begin is %s", G_STRLOC, alarm_begin.format("%F %T %z").c_str icalprop = icalcomponent_get_next_property(icc, ICAL_X_PROPERTY); } - } + //} appointment.alarms.reserve(alarms.size()); for (const auto& it : alarms) appointment.alarms.push_back(it.second); subtask->task->appointments.push_back(appointment); +#endif } -- cgit v1.2.3 From 44bd1414ea99b090657e50fd3fe48863a0d80d64 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 20 May 2015 18:56:15 -0500 Subject: remove detritus from the false starts. --- src/engine-eds.cpp | 396 +++++------------------------------------------------ 1 file changed, 34 insertions(+), 362 deletions(-) (limited to 'src') diff --git a/src/engine-eds.cpp b/src/engine-eds.cpp index 6302f06..456c80b 100644 --- a/src/engine-eds.cpp +++ b/src/engine-eds.cpp @@ -565,162 +565,6 @@ private: return out; } -#if 0 - static DateTime - to_datetime(struct icaltimetype& t, GTimeZone* gtz) - { - auto t_utc = icaltime_convert_to_zone(t, icaltimezone_get_utc_timezone()); - return DateTime(gtz, icaltime_as_timet(t_utc)); - } - - static struct icaltimetype - icaltimetype_from_datetime(const DateTime& dt, icaltimezone * default_timezone) - { - return icaltime_from_timet_with_zone(dt.to_unix(), false, default_timezone); - } - - static DateTime - get_trigger_time (icalcomponent* recurrence_instance, icalcomponent* valarm, GTimeZone* gtz) - { - DateTime ret; - - g_debug("%s getting trigger time for valarm %s", G_STRLOC, icalcomponent_as_ical_string(valarm)); - - auto trigger_property = icalcomponent_get_first_property(valarm, ICAL_TRIGGER_PROPERTY); - if (trigger_property == nullptr) - return ret; - - auto trigger = icalproperty_get_trigger(trigger_property); - g_debug("%s found a trigger", G_STRLOC); - g_debug("%s trigger time is %s", G_STRLOC, icaltime_as_ical_string(trigger.time)); - g_debug("%s trigger duration is %s", G_STRLOC, icaldurationtype_as_ical_string(trigger.duration)); - fflush(NULL); - - struct icaltimetype tt; - if (!icaltime_is_null_time(trigger.time)) - { - tt = trigger.time; - } - else - { - g_debug("%s got a NULL time, so it's relative to the component"); - // TRIGGER;RELATED=END:P5M 5 minutes after END - // TRIGGER;RELATED=START:-P15M 15 minutes before START - // get RELATED parameter - const char * related = icalproperty_get_parameter_as_string(trigger_property, "RELATED"); - if (related == nullptr) - related = "START"; // default value - g_debug("%s got a RELATED value of %s", G_STRLOC, related); - - struct icaltimetype reference_point; - if (!g_strcmp0(related, "START")) - reference_point = icalcomponent_get_dtstart(recurrence_instance); - else if (!g_strcmp0(related, "END")) - reference_point = icalcomponent_get_dtend(recurrence_instance); - else - reference_point = icaltime_null_time(); - - g_debug("%s reference point is %s", G_STRLOC, icaltime_as_ical_string(reference_point)); - tt = icaltime_add(reference_point, trigger.duration); - g_debug("%s reference point + offset is %s", G_STRLOC, icaltime_as_ical_string(tt)); - } - - if (icaltime_is_valid_time(tt)) - { - if (tt.zone == NULL) // floating time - ret = DateTime(gtz, tt.year, tt.month, tt.day, tt.hour, tt.minute, tt.second); - else // convert it to UTC for our time_t ctor - ret = DateTime(gtz, icaltime_as_timet(icaltime_convert_to_zone(tt, icaltimezone_get_utc_timezone()))); - } - - return ret; - } -#endif - -#if 0 -struct instance_data { - time_t start; - gboolean found; -}; - -static void -check_instance (icalcomponent *, - struct icaltime_span *span, - gpointer data) -{ - auto instance = static_cast(data); -auto start = icaltime_from_timet(span->start, false); -g_debug("%s span->start %s(%zu) instance->start %zu", G_STRLOC, icaltime_as_ical_string(start), (size_t)span->start, (size_t)instance->start); - - if (span->start == instance->start) - { -g_debug("%s FOUND!", G_STRLOC); - instance->found = TRUE; - } -} - -/** - * e_cal_util_construct_instance: - * @icalcomp: A recurring #icalcomponent - * @rid: The RECURRENCE-ID to construct a component for - * - * This checks that @rid indicates a valid recurrence of @icalcomp, and - * if so, generates a copy of @comp containing a RECURRENCE-ID of @rid. - * - * Returns: the instance, or %NULL. - **/ -static icalcomponent * -foo_cal_util_construct_instance (icalcomponent *icalcomp, - struct icaltimetype rid) -{ - struct instance_data instance; - struct icaltimetype start, end; - -g_debug("%s looking for instance at %s", G_STRLOC, icaltime_as_ical_string(rid)); -auto dtstart = icalcomponent_get_dtstart(icalcomp); -g_debug("%s in icalcomp whose dtstart is %s(%zu)", G_STRLOC, icaltime_as_ical_string(dtstart), (size_t)icaltime_as_timet(dtstart)); - - g_return_val_if_fail (icalcomp != NULL, NULL); - -g_debug("%s", G_STRLOC); - /* Make sure this is really recurring */ - if (!icalcomponent_get_first_property (icalcomp, ICAL_RRULE_PROPERTY) && - !icalcomponent_get_first_property (icalcomp, ICAL_RDATE_PROPERTY)) - return NULL; - -g_debug("%s", G_STRLOC); - /* Make sure the specified instance really exists */ - start = icaltime_convert_to_zone (rid, icaltimezone_get_utc_timezone ()); - instance.start = icaltime_as_timet(start); -g_debug("%s start %s (%zu)", G_STRLOC, icaltime_as_ical_string(start), instance.start); - end = start; - icaltime_adjust (&end, 0, 0, 0, 1); -g_debug("%s end %s", G_STRLOC, icaltime_as_ical_string(end)); - icaltime_adjust (&start, -30, 0, 0, 1); - icaltime_adjust (&end, 30, 0, 0, 1); -g_debug("%s start %s", G_STRLOC, icaltime_as_ical_string(start)); -g_debug("%s end %s", G_STRLOC, icaltime_as_ical_string(end)); - - //instance.start = icaltime_convert_to_zone (rid, icaltimezone_get_utc_timezone ()); - instance.found = FALSE; - icalcomponent_foreach_recurrence (icalcomp, start, end, - check_instance, &instance); - if (!instance.found) { -g_debug("%s not found", G_STRLOC); - return NULL; - } - - /* Make the instance */ - icalcomp = icalcomponent_new_clone (icalcomp); - icalcomponent_set_recurrenceid (icalcomp, rid); - - return icalcomp; -} -#endif - - - - static void add_alarms_to_subtask (ECalComponentAlarms * comp_alarms, AppointmentSubtask * subtask, @@ -760,8 +604,8 @@ g_debug("%s not found", G_STRLOC); return; /** - *** This looks like a component that we should notify users about. - *** Let's add it. + *** this looks like an appointment we can use... + *** populate reference_appointment from the component fields/props **/ Appointment reference_appointment; @@ -793,21 +637,41 @@ g_debug("%s not found", G_STRLOC); : reference_appointment.begin; e_cal_component_free_datetime(&eccdt_tmp); - g_debug("got appointment from %s to %s, status %d", + // get appointment.activation_url from x-props + auto icc = e_cal_component_get_icalcomponent(component); // icc owned by component + auto icalprop = icalcomponent_get_first_property(icc, ICAL_X_PROPERTY); + while (icalprop != nullptr) { + const char * x_name = icalproperty_get_x_name(icalprop); + if ((x_name != nullptr) && !g_ascii_strcasecmp(x_name, X_PROP_ACTIVATION_URL)) { + const char * url = icalproperty_get_value_as_string(icalprop); + if ((url != nullptr) && reference_appointment.activation_url.empty()) + reference_appointment.activation_url = url; + } + icalprop = icalcomponent_get_next_property(icc, ICAL_X_PROPERTY); + } + + g_debug("%s got appointment from %s to %s, status %d: %s", G_STRLOC, reference_appointment.begin.format("%F %T %z").c_str(), reference_appointment.end.format("%F %T %z").c_str(), - (int)status); + (int)status, + icalcomponent_as_ical_string(icc) /* string owned by ical */); - // build our Alarm map from the comp_alarms - auto icc = e_cal_component_get_icalcomponent(component); // icc owned by component - g_debug("%s icc as ical string: %s", G_STRLOC, icalcomponent_as_ical_string(icc)); - - /* outer map key is the component instance's start + end time; we build Appointment.begin and .end from that. - * inner map key is the alarm trigger, we build Alarm.time from that. - * inner map value is the Alarm. - * - * we use the inner map to fold together multiple valarms that occur at the same time; - * e.g. one valarm may have a display action and another an audio action + /** + *** Now loop through comp_alarms to get information that we need + *** to build the instance appointments and their alarms. + *** + *** Outer map key is the instance component's start + end time. + *** We build Appointment.begin and .end from that. + *** + *** inner map key is the alarm trigger, we build Alarm.time from that. + *** + *** inner map value is the Alarm. + *** + *** We map the alarms based on their trigger time so that we + *** can fold together multiple valarms that trigger for the + *** same componeng at the same time. This is commonplace; + *** e.g. one valarm will have a display action and another + *** will specify a sound to be played. */ std::map,std::map> alarms; for (auto l=comp_alarms->alarms; l!=nullptr; l=l->next) @@ -821,9 +685,6 @@ g_debug("%s not found", G_STRLOC); DateTime{gtz, ai->occur_end}); const auto trigger_time = DateTime{gtz, ai->trigger}; -g_message("%s occur_start %zu(%s)", G_STRLOC, (size_t)ai->occur_start, instance_time.first.format("%F %T %z").c_str()); -g_message("%s occur_end %zu(%s)", G_STRLOC, (size_t)ai->occur_end, instance_time.second.format("%F %T %z").c_str()); -g_message("%s trigger_time %zu(%s)", G_STRLOC, (size_t)ai->trigger, trigger_time.format("%F %T %z").c_str()); auto& alarm = alarms[instance_time][trigger_time]; if (alarm.text.empty()) @@ -836,7 +697,6 @@ g_message("%s trigger_time %zu(%s)", G_STRLOC, (size_t)ai->trigger, trigger_time e_cal_component_alarm_free(a); } - // now build the appointments for (auto& i : alarms) { Appointment appointment = reference_appointment; @@ -847,195 +707,7 @@ g_message("%s trigger_time %zu(%s)", G_STRLOC, (size_t)ai->trigger, trigger_time appointment.alarms.push_back(j.second); subtask->task->appointments.push_back(appointment); } - - // look for our recognized e-props - //if (icc != nullptr) - //{ -#if 0 - auto icalprop = icalcomponent_get_first_property(icc, ICAL_X_PROPERTY); - while (icalprop) - { - const char * x_name = icalproperty_get_x_name(icalprop); - if ((x_name != nullptr) && !g_ascii_strcasecmp(x_name, X_PROP_ACTIVATION_URL)) - { - const char * url = icalproperty_get_value_as_string(icalprop); - if ((url != nullptr) && appointment.activation_url.empty()) - appointment.activation_url = url; - } - - icalprop = icalcomponent_get_next_property(icc, ICAL_X_PROPERTY); - } - //} - - appointment.alarms.reserve(alarms.size()); - for (const auto& it : alarms) - appointment.alarms.push_back(it.second); - - subtask->task->appointments.push_back(appointment); -#endif - } - - -#if 0 - 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); - - // get the timezone we want to use for generated Appointments/Alarms - const char * location = icaltimezone_get_location(subtask->default_timezone); - auto gtz = g_time_zone_new(location); - g_debug("timezone abbreviation is %s", g_time_zone_get_abbreviation (gtz, 0)); - - const DateTime begin_dt { gtz, begin }; - const DateTime end_dt { gtz, end }; - g_debug ("got appointment from %s to %s, uid %s status %d", - begin_dt.format("%F %T %z").c_str(), - end_dt.format("%F %T %z").c_str(), - uid, - (int)status); - - // look for the in-house tags - bool disabled = false; - Appointment::Type type = Appointment::EVENT; - GSList * categ_list = nullptr; - e_cal_component_get_categories_list (component, &categ_list); - for (GSList * l=categ_list; l!=nullptr; l=l->next) { - auto tag = static_cast(l->data); - if (!g_strcmp0(tag, TAG_ALARM)) - type = Appointment::UBUNTU_ALARM; - if (!g_strcmp0(tag, TAG_DISABLED)) - disabled = true; - } - e_cal_component_free_categories_list(categ_list); - - if ((uid != nullptr) && - (!disabled) && - (status != ICAL_STATUS_COMPLETED) && - (status != ICAL_STATUS_CANCELLED)) - { - constexpr std::array omit = { (ECalComponentAlarmAction)-1 }; // list of action types to omit, terminated with -1 - Appointment appointment; - - ECalComponentText text {}; - e_cal_component_get_summary(component, &text); - if (text.value) - appointment.summary = text.value; - - auto icc = e_cal_component_get_icalcomponent(component); // component owns icc - if (icc) - { - g_debug("%s", icalcomponent_as_ical_string(icc)); // libical owns this string; no leak - - auto icalprop = icalcomponent_get_first_property(icc, ICAL_X_PROPERTY); - while (icalprop) - { - const char * x_name = icalproperty_get_x_name(icalprop); - if ((x_name != nullptr) && !g_ascii_strcasecmp(x_name, X_PROP_ACTIVATION_URL)) - { - const char * url = icalproperty_get_value_as_string(icalprop); - if ((url != nullptr) && appointment.activation_url.empty()) - appointment.activation_url = url; - } - - icalprop = icalcomponent_get_next_property(icc, ICAL_X_PROPERTY); - } - } - - appointment.begin = begin_dt; - appointment.end = end_dt; - appointment.color = subtask->color; - appointment.uid = uid; - appointment.type = type; - - auto e_alarms = e_cal_util_generate_alarms_for_comp(component, - subtask->begin, - subtask->end, - const_cast(omit.data()), - e_cal_client_resolve_tzid_cb, - subtask->client, - nullptr); - - std::map alarms; - - if (e_alarms != nullptr) - { - for (auto l=e_alarms->alarms; l!=nullptr; l=l->next) - { - auto ai = static_cast(l->data); - auto a = e_cal_component_get_alarm(component, ai->auid); - - if (a != nullptr) - { - const DateTime alarm_begin{gtz, ai->trigger}; - auto& alarm = alarms[alarm_begin]; - - if (alarm.text.empty()) - alarm.text = get_alarm_text(a); - if (alarm.audio_url.empty()) - alarm.audio_url = get_alarm_sound_url(a); - if (!alarm.time.is_set()) - alarm.time = alarm_begin; - - e_cal_component_alarm_free(a); - } - } - - e_cal_component_alarms_free(e_alarms); - } - // Hm, no alarm triggers? - // That's a bug in alarms created by some versions of ubuntu-ui-toolkit. - // If that's what's happening here, let's handle those alarms anyway - // by effectively injecting a TRIGGER;VALUE=DURATION;RELATED=START:PT0S - else if (appointment.is_ubuntu_alarm()) - { - Alarm tmp; - tmp.time = appointment.begin; - - auto auids = e_cal_component_get_alarm_uids(component); - for(auto l=auids; l!=nullptr; l=l->next) - { - const auto auid = static_cast(l->data); - auto a = e_cal_component_get_alarm(component, auid); - if (a != nullptr) - { - if (tmp.text.empty()) - tmp.text = get_alarm_text(a); - if (tmp.audio_url.empty()) - tmp.audio_url = get_alarm_sound_url(a); - e_cal_component_alarm_free(a); - } - } - cal_obj_uid_list_free(auids); - - alarms[tmp.time] = tmp; - } - - appointment.alarms.reserve(alarms.size()); - for (const auto& it : alarms) - appointment.alarms.push_back(it.second); - - subtask->task->appointments.push_back(appointment); - } - - g_time_zone_unref(gtz); - } - - return G_SOURCE_CONTINUE; } -#endif /*** **** -- cgit v1.2.3 From 70ccd1abfa7721393217336ba4048a2c51d76246 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 20 May 2015 20:22:52 -0500 Subject: copyediting --- src/engine-eds.cpp | 73 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 40 insertions(+), 33 deletions(-) (limited to 'src') diff --git a/src/engine-eds.cpp b/src/engine-eds.cpp index 456c80b..d2b2fa0 100644 --- a/src/engine-eds.cpp +++ b/src/engine-eds.cpp @@ -503,28 +503,33 @@ private: GError * error = NULL; GSList * comps_slist = NULL; - if (e_cal_client_get_object_list_as_comps_finish (E_CAL_CLIENT(oclient), res, &comps_slist, &error)) + if (e_cal_client_get_object_list_as_comps_finish(E_CAL_CLIENT(oclient), + res, + &comps_slist, + &error)) { - // _generate_alarms takes a GList, so make a shallow copy of the comps GSList + // _generate_alarms takes a GList, so make a shallow one GList * comps_list = nullptr; for (auto l=comps_slist; l!=nullptr; l=l->next) comps_list = g_list_prepend(comps_list, l->data); - constexpr std::array omit = { (ECalComponentAlarmAction)-1 }; // list of action types to omit, terminated with -1 + constexpr std::array omit = { + (ECalComponentAlarmAction)-1 + }; // list of action types to omit, terminated with -1 GSList * comp_alarms = nullptr; auto subtask = static_cast(gsubtask); - e_cal_util_generate_alarms_for_list(comps_list, - subtask->begin.to_unix(), - subtask->end.to_unix(), - const_cast(omit.data()), - &comp_alarms, - e_cal_client_resolve_tzid_cb, - oclient, - subtask->default_timezone); + e_cal_util_generate_alarms_for_list( + comps_list, + subtask->begin.to_unix(), + subtask->end.to_unix(), + const_cast(omit.data()), + &comp_alarms, + e_cal_client_resolve_tzid_cb, + oclient, + subtask->default_timezone); // walk the alarms & add them - const char * location = icaltimezone_get_location(subtask->default_timezone); - auto gtz = g_time_zone_new(location); + auto gtz = g_time_zone_new(icaltimezone_get_location(subtask->default_timezone)); for (auto l=comp_alarms; l!=nullptr; l=l->next) add_alarms_to_subtask(static_cast(l->data), subtask, gtz); g_time_zone_unref(gtz); @@ -538,7 +543,7 @@ private: else if (error != nullptr) { if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) - g_warning("indicator-datetime cannot mark one-time alarm as disabled: %s", error->message); + g_warning("can't get ecalcomponent list: %s", error->message); g_error_free(error); } @@ -578,13 +583,15 @@ private: // we only want calendar events and vtodos const auto vtype = e_cal_component_get_vtype(component); - if ((vtype != E_CAL_COMPONENT_EVENT) && (vtype != E_CAL_COMPONENT_TODO)) + if ((vtype != E_CAL_COMPONENT_EVENT) && + (vtype != E_CAL_COMPONENT_TODO)) return; // we're not interested in completed or cancelled components auto status = ICAL_STATUS_NONE; e_cal_component_get_status(component, &status); - if ((status == ICAL_STATUS_COMPLETED) || (status == ICAL_STATUS_CANCELLED)) + if ((status == ICAL_STATUS_COMPLETED) || + (status == ICAL_STATUS_CANCELLED)) return; // walk through the categories list to look for our in-house tags @@ -605,36 +612,36 @@ private: /** *** this looks like an appointment we can use... - *** populate reference_appointment from the component fields/props + *** populate baseline from the component fields/props **/ - Appointment reference_appointment; - reference_appointment.color = subtask->color; - reference_appointment.type = type; + Appointment baseline; + baseline.color = subtask->color; + baseline.type = type; // get appointment.uid const gchar* uid = nullptr; e_cal_component_get_uid(component, &uid); if (uid != nullptr) - reference_appointment.uid = uid; + baseline.uid = uid; // get appointment.summary ECalComponentText text {}; e_cal_component_get_summary(component, &text); if (text.value) - reference_appointment.summary = text.value; + baseline.summary = text.value; // get appointment.begin ECalComponentDateTime eccdt_tmp {}; e_cal_component_get_dtstart(component, &eccdt_tmp); - reference_appointment.begin = datetime_from_component_date_time(eccdt_tmp, gtz); + baseline.begin = datetime_from_component_date_time(eccdt_tmp, gtz); e_cal_component_free_datetime(&eccdt_tmp); // get appointment.end e_cal_component_get_dtend(component, &eccdt_tmp); - reference_appointment.end = eccdt_tmp.value != nullptr + baseline.end = eccdt_tmp.value != nullptr ? datetime_from_component_date_time(eccdt_tmp, gtz) - : reference_appointment.begin; + : baseline.begin; e_cal_component_free_datetime(&eccdt_tmp); // get appointment.activation_url from x-props @@ -644,15 +651,15 @@ private: const char * x_name = icalproperty_get_x_name(icalprop); if ((x_name != nullptr) && !g_ascii_strcasecmp(x_name, X_PROP_ACTIVATION_URL)) { const char * url = icalproperty_get_value_as_string(icalprop); - if ((url != nullptr) && reference_appointment.activation_url.empty()) - reference_appointment.activation_url = url; + if ((url != nullptr) && baseline.activation_url.empty()) + baseline.activation_url = url; } icalprop = icalcomponent_get_next_property(icc, ICAL_X_PROPERTY); } g_debug("%s got appointment from %s to %s, status %d: %s", G_STRLOC, - reference_appointment.begin.format("%F %T %z").c_str(), - reference_appointment.end.format("%F %T %z").c_str(), + baseline.begin.format("%F %T %z").c_str(), + baseline.end.format("%F %T %z").c_str(), (int)status, icalcomponent_as_ical_string(icc) /* string owned by ical */); @@ -681,9 +688,9 @@ private: if (a == nullptr) continue; - const auto instance_time = std::make_pair(DateTime{gtz, ai->occur_start}, - DateTime{gtz, ai->occur_end}); - const auto trigger_time = DateTime{gtz, ai->trigger}; + auto instance_time = std::make_pair(DateTime{gtz, ai->occur_start}, + DateTime{gtz, ai->occur_end}); + auto trigger_time = DateTime{gtz, ai->trigger}; auto& alarm = alarms[instance_time][trigger_time]; @@ -699,7 +706,7 @@ private: for (auto& i : alarms) { - Appointment appointment = reference_appointment; + Appointment appointment = baseline; appointment.begin = i.first.first; appointment.end = i.first.second; appointment.alarms.reserve(i.second.size()); -- cgit v1.2.3 From 9e1f2db6694791beaf0f7340de2f523c95f2551b Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 20 May 2015 20:50:53 -0500 Subject: remove a little more leftover code from the false starts --- src/engine-eds.cpp | 8 -------- 1 file changed, 8 deletions(-) (limited to 'src') diff --git a/src/engine-eds.cpp b/src/engine-eds.cpp index d2b2fa0..563d9a3 100644 --- a/src/engine-eds.cpp +++ b/src/engine-eds.cpp @@ -139,7 +139,6 @@ public: auto end_str = isodate_from_time_t(end_timet); auto sexp = g_strdup_printf("(has-alarms-in-range? (make-time \"%s\") (make-time \"%s\"))", begin_str, end_str); g_debug("%s sexp is %s", G_STRLOC, sexp); - //e_cal_client_get_object_list(client, e_cal_client_get_object_list_as_comps(client, sexp, m_cancellable, @@ -425,7 +424,6 @@ private: ECalClient* client; std::string color; icaltimezone* default_timezone; - GTimeZone * gtz; DateTime begin; DateTime end; @@ -438,18 +436,12 @@ private: task(task_in), client(client_in), default_timezone(default_tz), - gtz(g_time_zone_new(icaltimezone_get_location(default_tz))), begin(begin_), end(end_) { if (color_in) color = color_in; } - - ~AppointmentSubtask() - { - g_clear_pointer(>z, g_time_zone_unref); - } }; static std::string get_alarm_text(ECalComponentAlarm * alarm) -- cgit v1.2.3 From 105abe6c85d080f369c246e21d4d19c75ffb18c3 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Thu, 21 May 2015 05:58:00 -0500 Subject: in engine-eds.cpp's get_appointments(), clear the begin_str and end_str variables as soon as we're done with them. --- src/engine-eds.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/engine-eds.cpp b/src/engine-eds.cpp index 563d9a3..37f72e5 100644 --- a/src/engine-eds.cpp +++ b/src/engine-eds.cpp @@ -135,17 +135,19 @@ public: const auto begin_timet = begin.to_unix(); const auto end_timet = end.to_unix(); + auto begin_str = isodate_from_time_t(begin_timet); auto end_str = isodate_from_time_t(end_timet); auto sexp = g_strdup_printf("(has-alarms-in-range? (make-time \"%s\") (make-time \"%s\"))", begin_str, end_str); + g_clear_pointer(&begin_str, g_free); + g_clear_pointer(&end_str, g_free); + g_debug("%s sexp is %s", G_STRLOC, sexp); e_cal_client_get_object_list_as_comps(client, sexp, m_cancellable, on_object_list_ready, subtask); - g_free(begin_str); - g_free(end_str); g_free(sexp); } } -- cgit v1.2.3 From 505d4fae96e1d6849f119e9b31588f90073a4102 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Thu, 21 May 2015 06:01:01 -0500 Subject: in engine-eds.cpp's on_object_list_ready(), always mark the subtask as finished even if the dbus call failed. --- src/engine-eds.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/engine-eds.cpp b/src/engine-eds.cpp index 37f72e5..8350e77 100644 --- a/src/engine-eds.cpp +++ b/src/engine-eds.cpp @@ -496,6 +496,7 @@ private: { GError * error = NULL; GSList * comps_slist = NULL; + auto subtask = static_cast(gsubtask); if (e_cal_client_get_object_list_as_comps_finish(E_CAL_CLIENT(oclient), res, @@ -511,7 +512,6 @@ private: (ECalComponentAlarmAction)-1 }; // list of action types to omit, terminated with -1 GSList * comp_alarms = nullptr; - auto subtask = static_cast(gsubtask); e_cal_util_generate_alarms_for_list( comps_list, subtask->begin.to_unix(), @@ -532,7 +532,6 @@ private: e_cal_free_alarms(comp_alarms); g_list_free(comps_list); e_cal_client_free_ecalcomp_slist(comps_slist); - delete subtask; } else if (error != nullptr) { @@ -541,6 +540,8 @@ private: g_error_free(error); } + + delete subtask; } static DateTime -- cgit v1.2.3 From 58efd72df90047765721c6e931da22abdb3a956a Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Thu, 21 May 2015 07:47:24 -0500 Subject: in eds-engine, add a occur-in-time-range sexp to handle events that are interesting but don't require user notification alarms --- src/engine-eds.cpp | 248 +++++++++++++++++++++++++++++++++++------------------ 1 file changed, 163 insertions(+), 85 deletions(-) (limited to 'src') diff --git a/src/engine-eds.cpp b/src/engine-eds.cpp index 8350e77..b171fda 100644 --- a/src/engine-eds.cpp +++ b/src/engine-eds.cpp @@ -103,52 +103,51 @@ public: *** 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); + auto gtz = default_timezone != nullptr + ? g_time_zone_new(icaltimezone_get_location(default_timezone)) + : g_time_zone_new_local(); + auto main_task = std::make_shared(this, func, default_timezone, gtz, begin, end); for (auto& kv : m_clients) { auto& client = kv.second; if (default_timezone != nullptr) e_cal_client_set_default_timezone(client, default_timezone); + g_debug("calling e_cal_client_generate_instances for %p", (void*)client); - // 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); - auto subtask = new AppointmentSubtask(main_task, - client, - color, - default_timezone, - begin, - end); - - const auto begin_timet = begin.to_unix(); - const auto end_timet = end.to_unix(); - - auto begin_str = isodate_from_time_t(begin_timet); - auto end_str = isodate_from_time_t(end_timet); - auto sexp = g_strdup_printf("(has-alarms-in-range? (make-time \"%s\") (make-time \"%s\"))", begin_str, end_str); + + auto begin_str = isodate_from_time_t(begin.to_unix()); + auto end_str = isodate_from_time_t(end.to_unix()); + auto sexp_fmt = g_strdup_printf("(%%s? (make-time \"%s\") (make-time \"%s\"))", begin_str, end_str); g_clear_pointer(&begin_str, g_free); g_clear_pointer(&end_str, g_free); - g_debug("%s sexp is %s", G_STRLOC, sexp); - e_cal_client_get_object_list_as_comps(client, - sexp, - m_cancellable, - on_object_list_ready, - subtask); - g_free(sexp); + // ask EDS about alarms that occur in this window... + auto sexp = g_strdup_printf(sexp_fmt, "has-alarms-in-range"); + g_debug("%s alarm sexp is %s", G_STRLOC, sexp); + e_cal_client_get_object_list_as_comps( + client, + sexp, + m_cancellable, + on_alarm_component_list_ready, + new ClientSubtask(main_task, client, color)); + g_clear_pointer(&sexp, g_free); + + // ask EDS about events that occur in this window... + sexp = g_strdup_printf(sexp_fmt, "occur-in-time-range"); + g_debug("%s event sexp is %s", G_STRLOC, sexp); + e_cal_client_get_object_list_as_comps( + client, + sexp, + m_cancellable, + on_event_component_list_ready, + new ClientSubtask(main_task, client, color)); + g_clear_pointer(&sexp, g_free); + + g_clear_pointer(&sexp_fmt, g_free); } } @@ -416,30 +415,45 @@ private: { Impl* p; appointment_func func; + icaltimezone* default_timezone; // pointer owned by libical + GTimeZone* gtz; std::vector appointments; - Task(Impl* p_in, const appointment_func& func_in): p(p_in), func(func_in) {} + const DateTime begin; + const DateTime end; + + Task(Impl* p_in, + appointment_func func_in, + icaltimezone* tz_in, + GTimeZone* gtz_in, + const DateTime& begin_in, + const DateTime& end_in): + p{p_in}, + func{func_in}, + default_timezone{tz_in}, + gtz{gtz_in}, + begin{begin_in}, + end{end_in} {} + + ~Task() { + g_clear_pointer(>z, g_time_zone_unref); + // give the caller the sorted finished product + auto& a = appointments; + std::sort(a.begin(), a.end(), [](const Appointment& a, const Appointment& b){return a.begin < b.begin;}); + func(a); + }; }; - struct AppointmentSubtask + struct ClientSubtask { std::shared_ptr task; ECalClient* client; std::string color; - icaltimezone* default_timezone; - DateTime begin; - DateTime end; - - AppointmentSubtask(const std::shared_ptr& task_in, - ECalClient* client_in, - const char* color_in, - icaltimezone* default_tz, - DateTime begin_, - DateTime end_): + + ClientSubtask(const std::shared_ptr& task_in, + ECalClient* client_in, + const char* color_in): task(task_in), - client(client_in), - default_timezone(default_tz), - begin(begin_), - end(end_) + client(client_in) { if (color_in) color = color_in; @@ -490,13 +504,13 @@ private: } static void - on_object_list_ready(GObject * oclient, - GAsyncResult * res, - gpointer gsubtask) + on_alarm_component_list_ready(GObject * oclient, + GAsyncResult * res, + gpointer gsubtask) { GError * error = NULL; GSList * comps_slist = NULL; - auto subtask = static_cast(gsubtask); + auto subtask = static_cast(gsubtask); if (e_cal_client_get_object_list_as_comps_finish(E_CAL_CLIENT(oclient), res, @@ -514,19 +528,17 @@ private: GSList * comp_alarms = nullptr; e_cal_util_generate_alarms_for_list( comps_list, - subtask->begin.to_unix(), - subtask->end.to_unix(), + subtask->task->begin.to_unix(), + subtask->task->end.to_unix(), const_cast(omit.data()), &comp_alarms, e_cal_client_resolve_tzid_cb, oclient, - subtask->default_timezone); + subtask->task->default_timezone); // walk the alarms & add them - auto gtz = g_time_zone_new(icaltimezone_get_location(subtask->default_timezone)); for (auto l=comp_alarms; l!=nullptr; l=l->next) - add_alarms_to_subtask(static_cast(l->data), subtask, gtz); - g_time_zone_unref(gtz); + add_alarms_to_subtask(static_cast(l->data), subtask, subtask->task->gtz); // cleanup e_cal_free_alarms(comp_alarms); @@ -544,6 +556,36 @@ private: delete subtask; } + static void + on_event_component_list_ready(GObject * oclient, + GAsyncResult * res, + gpointer gsubtask) + { + GError * error = NULL; + GSList * comps_slist = NULL; + auto subtask = static_cast(gsubtask); + + if (e_cal_client_get_object_list_as_comps_finish(E_CAL_CLIENT(oclient), + res, + &comps_slist, + &error)) + { + for (auto l=comps_slist; l!=nullptr; l=l->next) + add_event_to_subtask(static_cast(l->data), subtask, subtask->task->gtz); + + e_cal_client_free_ecalcomp_slist(comps_slist); + } + else if (error != nullptr) + { + if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning("can't get ecalcomponent list: %s", error->message); + + g_error_free(error); + } + + delete subtask; + } + static DateTime datetime_from_component_date_time(const ECalComponentDateTime & in, GTimeZone * default_timezone) @@ -565,54 +607,42 @@ private: return out; } - static void - add_alarms_to_subtask (ECalComponentAlarms * comp_alarms, - AppointmentSubtask * subtask, - GTimeZone * gtz) + static bool + is_component_interesting(ECalComponent * component) { - auto& component = comp_alarms->comp; - - /** - *** Filter out components that we're not interested in - **/ - // we only want calendar events and vtodos const auto vtype = e_cal_component_get_vtype(component); if ((vtype != E_CAL_COMPONENT_EVENT) && (vtype != E_CAL_COMPONENT_TODO)) - return; + return false; // we're not interested in completed or cancelled components auto status = ICAL_STATUS_NONE; e_cal_component_get_status(component, &status); if ((status == ICAL_STATUS_COMPLETED) || (status == ICAL_STATUS_CANCELLED)) - return; + return false; - // walk through the categories list to look for our in-house tags + // we don't want disabled alarms bool disabled = false; - Appointment::Type type = Appointment::EVENT; GSList * categ_list = nullptr; e_cal_component_get_categories_list (component, &categ_list); for (GSList * l=categ_list; l!=nullptr; l=l->next) { auto tag = static_cast(l->data); - if (!g_strcmp0(tag, TAG_ALARM)) - type = Appointment::UBUNTU_ALARM; if (!g_strcmp0(tag, TAG_DISABLED)) disabled = true; } e_cal_component_free_categories_list(categ_list); - if (disabled) // don't notify users about disabled alarms... - return; + if (disabled) + return false; - /** - *** this looks like an appointment we can use... - *** populate baseline from the component fields/props - **/ + return true; + } + static Appointment + get_appointment(ECalComponent * component, GTimeZone * gtz) + { Appointment baseline; - baseline.color = subtask->color; - baseline.type = type; // get appointment.uid const gchar* uid = nullptr; @@ -652,12 +682,60 @@ private: icalprop = icalcomponent_get_next_property(icc, ICAL_X_PROPERTY); } - g_debug("%s got appointment from %s to %s, status %d: %s", G_STRLOC, + // get appointment.type + baseline.type = Appointment::EVENT; + GSList * categ_list = nullptr; + e_cal_component_get_categories_list (component, &categ_list); + for (GSList * l=categ_list; l!=nullptr; l=l->next) { + auto tag = static_cast(l->data); + if (!g_strcmp0(tag, TAG_ALARM)) + baseline.type = Appointment::UBUNTU_ALARM; + } + e_cal_component_free_categories_list(categ_list); + + g_debug("%s got appointment from %s to %s: %s", G_STRLOC, baseline.begin.format("%F %T %z").c_str(), baseline.end.format("%F %T %z").c_str(), - (int)status, icalcomponent_as_ical_string(icc) /* string owned by ical */); + return baseline; + } + + static void + add_event_to_subtask(ECalComponent * component, + ClientSubtask * subtask, + GTimeZone * gtz) + { + // events with alarms are covered by add_alarm_to_subtask(), + // so skip them here + auto auids = e_cal_component_get_alarm_uids(component); + const bool has_alarms = auids != nullptr; + cal_obj_uid_list_free(auids); + if (has_alarms) + return; + + // add it. simple, eh? + if (is_component_interesting(component)) + { + Appointment appointment = get_appointment(component, gtz); + appointment.color = subtask->color; + subtask->task->appointments.push_back(appointment); + } + } + + static void + add_alarms_to_subtask(ECalComponentAlarms * comp_alarms, + ClientSubtask * subtask, + GTimeZone * gtz) + { + auto& component = comp_alarms->comp; + + if (!is_component_interesting(component)) + return; + + Appointment baseline = get_appointment(component, gtz); + baseline.color = subtask->color; + /** *** Now loop through comp_alarms to get information that we need *** to build the instance appointments and their alarms. -- cgit v1.2.3