[california/wip/725785-create-recurring] Progress -- can create event every day of week
- From: Jim Nelson <jnelson src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [california/wip/725785-create-recurring] Progress -- can create event every day of week
- Date: Thu, 12 Jun 2014 02:18:33 +0000 (UTC)
commit c9bfd1a6d31db21ddbff6bbe32c95f9156868ac2
Author: Jim Nelson <jim yorba org>
Date: Wed Jun 11 19:17:49 2014 -0700
Progress -- can create event every day of week
However, when created only the first appears in the calendar. Must
restart to see them all.
src/calendar/calendar-day-of-week.vala | 15 +++++
src/collection/collection-iterable.vala | 12 ++++
src/component/component-details-parser.vala | 55 +++++++++++------
src/component/component-event.vala | 40 ++++++++-----
src/component/component-recurrence-rule.vala | 83 +++++++++++++++++++-------
vapi/libical.vapi | 27 +++------
6 files changed, 158 insertions(+), 74 deletions(-)
---
diff --git a/src/calendar/calendar-day-of-week.vala b/src/calendar/calendar-day-of-week.vala
index 4c2024c..ee29481 100644
--- a/src/calendar/calendar-day-of-week.vala
+++ b/src/calendar/calendar-day-of-week.vala
@@ -52,6 +52,9 @@ public class DayOfWeek : BaseObject, Gee.Hashable<DayOfWeek> {
public static DayOfWeek SAT;
public static DayOfWeek SUN;
+ public static DayOfWeek[] weekdays;
+ public static DayOfWeek[] weekend_days;
+
public const int MIN = 1;
public const int MAX = 7;
public const int COUNT = MAX - MIN + 1;
@@ -142,11 +145,23 @@ public class DayOfWeek : BaseObject, Gee.Hashable<DayOfWeek> {
days_of_week_sunday[4] = THU;
days_of_week_sunday[5] = FRI;
days_of_week_sunday[6] = SAT;
+
+ weekdays = new DayOfWeek[5];
+ weekdays[0] = MON;
+ weekdays[1] = TUE;
+ weekdays[2] = WED;
+ weekdays[3] = THU;
+ weekdays[4] = FRI;
+
+ weekend_days = new DayOfWeek[2];
+ weekend_days[0] = SAT;
+ weekend_days[1] = SUN;
}
internal static void terminate() {
days_of_week_monday = days_of_week_sunday = null;
MON = TUE = WED = THU = FRI = SAT = SUN = null;
+ weekdays = weekend_days = null;
}
/**
diff --git a/src/collection/collection-iterable.vala b/src/collection/collection-iterable.vala
index 2276d8e..61e9b32 100644
--- a/src/collection/collection-iterable.vala
+++ b/src/collection/collection-iterable.vala
@@ -30,6 +30,18 @@ public California.Iterable<G> iterate<G>(G g, ...) {
}
/**
+ * Take a non-null array of non-null items (all of type G) and return a California.Iterable
+ * for convenience.
+ */
+public California.Iterable<G> from_array<G>(G[] ar) {
+ Gee.ArrayList<G> list = new Gee.ArrayList<G>();
+ foreach (G item in ar)
+ list.add(item);
+
+ return California.traverse<G>(list);
+}
+
+/**
* An Iterable that simply wraps an existing Iterator. You get one iteration,
* and only one iteration. Basically every method triggers one iteration and
* returns a new object.
diff --git a/src/component/component-details-parser.vala b/src/component/component-details-parser.vala
index ccea24d..85f7563 100644
--- a/src/component/component-details-parser.vala
+++ b/src/component/component-details-parser.vala
@@ -64,6 +64,7 @@ public class DetailsParser : BaseObject {
private Calendar.Date? end_date = null;
private Calendar.Duration? duration = null;
private bool adding_location = false;
+ private RecurrenceRule? rrule = null;
/**
* Parses a user-entered string of event details into an { link Event}.
@@ -161,7 +162,7 @@ public class DetailsParser : BaseObject {
// A recurring preposition suggests a regular occurrance is being described by the next
// two tokens
stack.mark();
- if (token.casefolded in RECURRING_PREPOSITIONS && parse_recurring(stack.pop(), stack.pop()))
+ if (token.casefolded in RECURRING_PREPOSITIONS && parse_recurring(stack.pop()))
continue;
stack.restore();
@@ -248,6 +249,10 @@ public class DetailsParser : BaseObject {
event.set_event_date_span(new Calendar.DateSpan(start_date, end_date));
}
+ // recurrence rule, if specified
+ if (rrule != null)
+ event.make_recurring(rrule);
+
// other event details
if (!String.is_empty(summary.str))
event.summary = summary.str;
@@ -260,6 +265,8 @@ public class DetailsParser : BaseObject {
event.description = details;
else
event.description += "\n" + details;
+
+ debug("%s", event.ical_component.as_ical_string());
}
private bool parse_time(Token? specifier, bool strict) {
@@ -344,30 +351,42 @@ public class DetailsParser : BaseObject {
return true;
}
- private bool parse_recurring(Token? amount, Token? unit) {
- // only one is required
- if (amount == null && unit == null)
+ private bool parse_recurring(Token? unit) {
+ // if a start date or recurring rule has already been specified, recurring cannot be made
+ if (unit == null || start_date != null || rrule != null)
return false;
// recurring can be specified with the amount acting as a day specifier, i.e. "every Friday"
// or a single unit, i.e. "every day" or "every weekday"
- if (unit == null) {
- Calendar.DayOfWeek? dow = Calendar.DayOfWeek.parse(amount.casefolded);
- if (dow != null) {
- return true;
- }
+ Calendar.DayOfWeek? dow = Calendar.DayOfWeek.parse(unit.casefolded);
+ if (dow != null) {
+ start_date = Calendar.System.today.upcoming(dow, true);
+ rrule = new RecurrenceRule(iCal.icalrecurrencetype_frequency.WEEKLY_RECURRENCE);
- if (amount.casefolded == DAY) {
- return true;
- }
+ return true;
+ }
+
+ if (unit.casefolded == DAY) {
+ start_date = Calendar.System.today;
+ rrule = new RecurrenceRule(iCal.icalrecurrencetype_frequency.DAILY_RECURRENCE);
- if (amount.casefolded == WEEKDAY) {
- return true;
- }
+ return true;
+ }
+
+ if (unit.casefolded == WEEKDAY) {
+ start_date = Calendar.System.today;
+ rrule = new RecurrenceRule(iCal.icalrecurrencetype_frequency.WEEKLY_RECURRENCE);
+ // TODO: Set start of week
+ Gee.Map<Calendar.DayOfWeek, int> map = new Gee.HashMap<Calendar.DayOfWeek, int>();
+ foreach (Calendar.DayOfWeek weekday in Calendar.DayOfWeek.weekdays)
+ map.set(weekday, 0);
+ rrule.set_by_rule(RecurrenceRule.ByRule.DAY, RecurrenceRule.encode_days(map));
- if (amount.casefolded == WEEKEND) {
- return true;
- }
+ return true;
+ }
+
+ if (unit.casefolded == WEEKEND) {
+ return true;
}
return false;
diff --git a/src/component/component-event.vala b/src/component/component-event.vala
index 6ef205f..f309300 100644
--- a/src/component/component-event.vala
+++ b/src/component/component-event.vala
@@ -84,6 +84,10 @@ public class Event : Instance, Gee.Comparable<Event> {
/**
* { link RecurrenceRule} (RRULE) for { link Event}.
+ *
+ * If the RecurrenceRule is itself altered, that signal is reflected to { link Instance.altered}.
+ *
+ * @see make_recurring
*/
public RecurrenceRule? rrule { get; private set; default = null; }
@@ -227,17 +231,12 @@ public class Event : Instance, Gee.Comparable<Event> {
break;
case PROP_RRULE:
- // whether rrule is added or removed (cleared), clear from ical_component
- unowned iCal.icalproperty? rrule_property = ical_component.get_first_property(
- iCal.icalproperty_kind.RRULE_PROPERTY);
- while (rrule_property != null) {
- ical_component.remove_property(rrule_property);
- rrule_property = ical_component.get_next_property(iCal.icalproperty_kind.RRULE_PROPERTY);
- }
+ // always remove existing RRULE
+ remove_all_properties(iCal.icalproperty_kind.RRULE_PROPERTY);
- // add back if necessary
+ // add new one, if added
if (rrule != null)
- rrule.to_ical(ical_component);
+ rrule.add_to_ical(ical_component);
break;
default:
@@ -348,20 +347,29 @@ public class Event : Instance, Gee.Comparable<Event> {
/**
* Add a { link RecurrenceRule} to the { link Event}.
*
- * Pass null to make non-recurring.
+ * Pass null to make Event non-recurring.
*/
public void make_recurring(RecurrenceRule? rrule) {
- if (this.rrule != null)
- this.rrule.by_rule_updated.disconnect(on_by_rule_updated);
+ if (this.rrule != null) {
+ this.rrule.notify.disconnect(on_rrule_updated);
+ this.rrule.by_rule_updated.disconnect(on_rrule_updated);
+ }
- if (rrule != null)
- rrule.by_rule_updated.connect(on_by_rule_updated);
+ if (rrule != null) {
+ rrule.notify.connect(on_rrule_updated);
+ rrule.by_rule_updated.connect(on_rrule_updated);
+ }
this.rrule = rrule;
}
- private void on_by_rule_updated() {
- // TODO: Update ical_component with new RRULE
+ private void on_rrule_updated() {
+ // remove old property, replace with new one
+ remove_all_properties(iCal.icalproperty_kind.RRULE_PROPERTY);
+ rrule.add_to_ical(ical_component);
+
+ // count this as an alteration
+ notify_altered(false);
}
/**
diff --git a/src/component/component-recurrence-rule.vala b/src/component/component-recurrence-rule.vala
index 4a579d3..52ab290 100644
--- a/src/component/component-recurrence-rule.vala
+++ b/src/component/component-recurrence-rule.vala
@@ -147,7 +147,8 @@ public class RecurrenceRule : BaseObject {
break;
}
- fill_by(rrule.by_second, iCal.BY_SECOND_SIZE, by_second);
+ fill_by(rrule.by_second, by_second);
+ /*
fill_by(rrule.by_minute, iCal.BY_MINUTE_SIZE, by_minute);
fill_by(rrule.by_hour, iCal.BY_HOUR_SIZE, by_hour);
fill_by(rrule.by_day, iCal.BY_DAY_SIZE, by_day);
@@ -156,10 +157,11 @@ public class RecurrenceRule : BaseObject {
fill_by(rrule.by_week_no, iCal.BY_WEEKNO_SIZE, by_week_num);
fill_by(rrule.by_month, iCal.BY_MONTH_SIZE, by_month);
fill_by(rrule.by_set_pos, iCal.BY_SETPOS_SIZE, by_set_pos);
+ */
}
- private void fill_by(short[] ical_by_ar, int ical_by_ar_len, Gee.SortedSet<int> by_set) {
- for (int ctr = 0; ctr < ical_by_ar_len; ctr++) {
+ private void fill_by(short[] ical_by_ar, Gee.SortedSet<int> by_set) {
+ for (int ctr = 0; ctr < ical_by_ar.length; ctr++) {
short by = ical_by_ar[ctr];
if (by == iCal.RECURRENCE_ARRAY_MAX)
break;
@@ -193,7 +195,7 @@ public class RecurrenceRule : BaseObject {
}
/**
- * Returns a read-only sorted set of BY rule settings.
+ * Returns a read-only sorted set of BY rule settings for the specified { link ByRule}.
*/
public Gee.SortedSet<int> get_by_rule(ByRule by_rule) {
switch (by_rule) {
@@ -230,8 +232,34 @@ public class RecurrenceRule : BaseObject {
}
/**
+ * Encode a Gee.Map of { link Calendar.DayOfWeek} and its position (i.e. Second Thursday of
+ * the month, last Wednesday of the year) into a value for { link set_by_rule} when using
+ * { link ByRule.DAY}.
+ *
+ * Use null for DayOfWeek and zero for position to mean "any" or "every".
+ */
+ public static Gee.Collection<int>? encode_days(Gee.Map<Calendar.DayOfWeek?, int>? day_values) {
+ if (day_values == null || day_values.size == 0)
+ return null;
+
+ Gee.Collection<int> encoded = new Gee.ArrayList<int>();
+ Gee.MapIterator<Calendar.DayOfWeek?, int> iter = day_values.map_iterator();
+ while (iter.next()) {
+ Calendar.DayOfWeek? dow = iter.get_key();
+ int dow_value = (dow != null) ? dow.ordinal(Calendar.FirstOfWeek.SUNDAY) : 0;
+ int position = iter.get_value().clamp(0, int.MAX);
+
+ encoded.add((position * 7) + dow_value);
+ }
+
+ return encoded;
+ }
+
+ /**
* Replaces the existing set of values for the BY rules with the supplied values.
*
+ * Pass null or an empty Collection to clear the by-rules values.
+ *
* @see by_rule_updated
*/
public void set_by_rule(ByRule by_rule, Gee.Collection<int>? values) {
@@ -287,13 +315,21 @@ public class RecurrenceRule : BaseObject {
/**
* Converts a { link RecurrenceRule} into an iCalendar RRULE property and adds it to the
* iCal component.
+ *
+ * This call makes no attempt to remove an existing RRULE property; that should be performed by
+ * the caller first.
*/
- internal void to_ical(iCal.icalcomponent ical_component) {
+ internal void add_to_ical(iCal.icalcomponent ical_component) {
iCal.icalrecurrencetype rrule = { 0 };
rrule.freq = freq;
- rrule.until = until.dt;
- rrule.count = count;
- rrule.interval = interval;
+
+ if (until != null)
+ rrule.until = until.dt;
+ else if (count > 0)
+ rrule.count = count;
+
+ if (interval > 0)
+ rrule.interval = interval;
if (start_of_week == null)
rrule.week_start = iCal.icalrecurrencetype_weekday.NO_WEEKDAY;
@@ -314,30 +350,33 @@ public class RecurrenceRule : BaseObject {
else
assert_not_reached();
- fill_ical_by(by_second, rrule.by_second, iCal.BY_SECOND_SIZE);
- fill_ical_by(by_minute, rrule.by_minute, iCal.BY_MINUTE_SIZE);
- fill_ical_by(by_hour, rrule.by_hour, iCal.BY_HOUR_SIZE);
- fill_ical_by(by_day, rrule.by_day, iCal.BY_DAY_SIZE);
- fill_ical_by(by_month_day, rrule.by_month_day, iCal.BY_MONTHDAY_SIZE);
- fill_ical_by(by_year_day, rrule.by_year_day, iCal.BY_YEARDAY_SIZE);
- fill_ical_by(by_week_num, rrule.by_week_no, iCal.BY_WEEKNO_SIZE);
- fill_ical_by(by_month, rrule.by_month, iCal.BY_MONTH_SIZE);
- fill_ical_by(by_set_pos, rrule.by_set_pos, iCal.BY_SETPOS_SIZE);
+ fill_ical_by(by_second, &rrule.by_second[0], rrule.by_second.length);
+ fill_ical_by(by_minute, &rrule.by_minute[0], rrule.by_minute.length);
+ fill_ical_by(by_hour, &rrule.by_hour[0], rrule.by_hour.length);
+ fill_ical_by(by_day, &rrule.by_day[0], rrule.by_day.length);
+ fill_ical_by(by_month_day, &rrule.by_month_day[0], rrule.by_month_day.length);
+ fill_ical_by(by_year_day, &rrule.by_year_day[0], rrule.by_year_day.length);
+ fill_ical_by(by_week_num, &rrule.by_week_no[0], rrule.by_week_no.length);
+ fill_ical_by(by_month, &rrule.by_month[0], rrule.by_month.length);
+ fill_ical_by(by_set_pos, &rrule.by_set_pos[0], rrule.by_set_pos.length);
iCal.icalproperty rrule_property = new iCal.icalproperty(iCal.icalproperty_kind.RRULE_PROPERTY);
rrule_property.set_rrule(rrule);
- // TODO: Remove any existing RRULE properties
-
ical_component.add_property(rrule_property);
}
- private void fill_ical_by(Gee.SortedSet<int> by_set, short[] ical_by_ar, int ical_by_ar_len) {
+ private void fill_ical_by(Gee.SortedSet<int> by_set, short *ical_by_ar, int ar_length) {
int index = 0;
- foreach (int by in by_set)
+ foreach (int by in by_set) {
ical_by_ar[index++] = (short) by;
+
+ // watch for overflow
+ if (index >= ar_length)
+ break;
+ }
- if (index < ical_by_ar_len)
+ if (index < ar_length)
ical_by_ar[index] = (short) iCal.RECURRENCE_ARRAY_MAX;
}
diff --git a/vapi/libical.vapi b/vapi/libical.vapi
index b1429e7..9d3daad 100644
--- a/vapi/libical.vapi
+++ b/vapi/libical.vapi
@@ -1716,24 +1716,15 @@ namespace iCal {
public int count;
public short interval;
public iCal.icalrecurrencetype_weekday week_start;
- [CCode (array_length = false)]
- public weak short[] by_second;
- [CCode (array_length = false)]
- public weak short[] by_minute;
- [CCode (array_length = false)]
- public weak short[] by_hour;
- [CCode (array_length = false)]
- public weak short[] by_day;
- [CCode (array_length = false)]
- public weak short[] by_month_day;
- [CCode (array_length = false)]
- public weak short[] by_year_day;
- [CCode (array_length = false)]
- public weak short[] by_week_no;
- [CCode (array_length = false)]
- public weak short[] by_month;
- [CCode (array_length = false)]
- public weak short[] by_set_pos;
+ public unowned short by_second[61];
+ public unowned short by_minute[61];
+ public unowned short by_hour[25];
+ public unowned short by_day[364];
+ public unowned short by_month_day[32];
+ public unowned short by_year_day[367];
+ public unowned short by_week_no[54];
+ public unowned short by_month[13];
+ public unowned short by_set_pos[367];
[CCode (cname = "icalrecurrencetype_as_string")]
public unowned string as_string ();
[CCode (cname = "icalrecurrencetype_as_string_r")]
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]