[california/wip/725786-edit-recurring] Initialize controls with RRULE parameters



commit ca50b1bf75568a715d72478afe3addc4364a9706
Author: Jim Nelson <jim yorba org>
Date:   Thu Jul 10 18:07:33 2014 -0700

    Initialize controls with RRULE parameters
    
    This introduces a notion of a master Instance that can be fetched
    from the CalendarSource

 src/backing/backing-calendar-source.vala         |   17 +++
 src/backing/backing-error.vala                   |    6 +-
 src/backing/eds/backing-eds-calendar-source.vala |   16 +++
 src/component/component-date-time.vala           |   50 ++++---
 src/component/component-event.vala               |   16 +--
 src/component/component-instance.vala            |    3 +-
 src/component/component-recurrence-rule.vala     |   21 +++-
 src/host/host-create-update-recurring.vala       |  155 ++++++++++++++++++++--
 src/host/host-main-window.vala                   |    5 +-
 src/host/host-quick-create-event.vala            |   16 +-
 src/host/host-show-event.vala                    |    4 +-
 src/rc/create-update-recurring.ui                |    9 +-
 12 files changed, 257 insertions(+), 61 deletions(-)
---
diff --git a/src/backing/backing-calendar-source.vala b/src/backing/backing-calendar-source.vala
index 4e6589b..895f7cf 100644
--- a/src/backing/backing-calendar-source.vala
+++ b/src/backing/backing-calendar-source.vala
@@ -95,6 +95,23 @@ public abstract class CalendarSource : Source {
         AffectedInstances affected, Cancellable? cancellable = null) throws Error;
     
     /**
+     * Fetches the master component of an { link Component.Instance}.
+     *
+     * If an Instance is recurring, { link CalendarSourceSubscription} produces individual instances
+     * of those recurrences (which therefore lack the rules and information that produce them).
+     * This call allows for the original (or master) component to be retrieved.
+     *
+     * If the instance is not recurring, the returned Instance will be identical (or near-identical)
+     * to the one produced by CalendarSourceSubscription.
+     *
+     * @see Component.Instance.is_recurring_instance
+     * @throws An Error if the { link Component.UID} is unknown to the { link CalendarSource},
+     * as well as for the sundry I/O errors and such.
+     */
+    public abstract async Component.Instance fetch_master_component_async(Component.UID uid,
+        Cancellable? cancellable = null) throws Error;
+    
+    /**
      * Imports a { link Component.iCalendar} into the { link CalendarSource}.
      */
     public abstract async void import_icalendar_async(Component.iCalendar ical, Cancellable? cancellable = 
null)
diff --git a/src/backing/backing-error.vala b/src/backing/backing-error.vala
index 0857010..3f30d3a 100644
--- a/src/backing/backing-error.vala
+++ b/src/backing/backing-error.vala
@@ -22,7 +22,11 @@ public errordomain BackingError {
     /**
      * The method or object is unavailable due to a state change (not open or removed).
      */
-    UNAVAILABLE
+    UNAVAILABLE,
+    /**
+     * The object or identifier is not recognized.
+     */
+    UNKNOWN
 }
 
 }
diff --git a/src/backing/eds/backing-eds-calendar-source.vala 
b/src/backing/eds/backing-eds-calendar-source.vala
index 440327e..eb1e2a9 100644
--- a/src/backing/eds/backing-eds-calendar-source.vala
+++ b/src/backing/eds/backing-eds-calendar-source.vala
@@ -226,6 +226,22 @@ internal class EdsCalendarSource : CalendarSource {
         yield client.modify_object(ical_component, E.CalObjModType.THIS, cancellable);
     }
     
+    public override async Component.Instance fetch_master_component_async(Component.UID uid,
+        Cancellable? cancellable = null) throws Error {
+        // get the master instance for this UID
+        iCal.icalcomponent ical_component;
+        yield client.get_object(uid.value, null, cancellable, out ical_component);
+        
+        // convert into an Instance and return
+        Component.Instance? instance = Component.Instance.convert(this, ical_component);
+        if (instance == null) {
+            throw new BackingError.UNKNOWN("UID %s is unknown to calendar %s", uid.to_string(),
+                to_string());
+        }
+        
+        return instance;
+    }
+    
     public override async void import_icalendar_async(Component.iCalendar ical, Cancellable? cancellable = 
null)
         throws Error {
         check_open();
diff --git a/src/component/component-date-time.vala b/src/component/component-date-time.vala
index d52bd00..aba1c9f 100644
--- a/src/component/component-date-time.vala
+++ b/src/component/component-date-time.vala
@@ -126,39 +126,45 @@ public class DateTime : BaseObject, Gee.Hashable<DateTime>, Gee.Comparable<DateT
     
     /**
      * Creates a new { link DateTime} for a component's RRULE UNTIL property.
+     *
+     * Strict will attempt to adhere to the MUSTs and SHALLs present in the iCal specification
+     * regarding RRULE's UNTIL property. See [[https://tools.ietf.org/html/rfc5545#section-3.3.10]]
      */
-    public DateTime.rrule_until(iCal.icalrecurrencetype rrule, DateTime dtstart) throws ComponentError {
+    public DateTime.rrule_until(iCal.icalrecurrencetype rrule, DateTime dtstart, bool strict)
+        throws ComponentError {
         if (iCal.icaltime_is_null_time(rrule.until) != 0)
             throw new ComponentError.INVALID("DATE-TIME for RRULE UNTIL is null time");
         
-        if (iCal.icaltime_is_valid_time(rrule.until) != 0)
+        if (iCal.icaltime_is_valid_time(rrule.until) == 0)
             throw new ComponentError.INVALID("DATE-TIME for RRULE UNTIL is invalid");
         
         bool until_is_date = (iCal.icaltime_is_date(rrule.until) != 0);
         bool until_is_utc = (iCal.icaltime_is_utc(rrule.until) != 0);
         
-        // "The value of the UNTIL rule part MUST have the same value type as the 'DTSTART'
-        // property."
-        if (dtstart.is_date != until_is_date)
-            throw new ComponentError.INVALID("RRULE UNTIL and DTSTART must be of same type 
(DATE/DATE-TIME)");
-        
-        // "If the 'DTSTART' property is specified as a date with local time, then the UNTIL rule
-        // part MUST also be specified as a date with local time."
-        if (dtstart.is_utc != until_is_utc)
-            throw new ComponentError.INVALID("RRULE UNTIL and DTSTART must be of same time type 
(UTC/local)");
-        
-        // "if the 'DTSTART' property is specified as a date with UTC time or a date with local time
-        // and a time zone reference, then the UNTIL rule part MUST be specified as a date with
-        // UTC time."
-        if (dtstart.is_date || (!dtstart.is_utc && dtstart.zone != null)) {
-            if (!until_is_utc)
-                throw new ComponentError.INVALID("RRULE UNTIL must be UTC for DTSTART DATE or w/ time zone");
+        if (strict) {
+            // "The value of the UNTIL rule part MUST have the same value type as the 'DTSTART'
+            // property."
+            if (dtstart.is_date != until_is_date)
+                throw new ComponentError.INVALID("RRULE UNTIL and DTSTART must be of same type 
(DATE/DATE-TIME)");
+            
+            // "If the 'DTSTART' property is specified as a date with local time, then the UNTIL rule
+            // part MUST also be specified as a date with local time."
+            if (dtstart.is_utc != until_is_utc)
+                throw new ComponentError.INVALID("RRULE UNTIL and DTSTART must be of same time type 
(UTC/local)");
+            
+            // "if the 'DTSTART' property is specified as a date with UTC time or a date with local time
+            // and a time zone reference, then the UNTIL rule part MUST be specified as a date with
+            // UTC time."
+            if (dtstart.is_date || (!dtstart.is_utc && dtstart.zone != null)) {
+                if (!until_is_utc)
+                    throw new ComponentError.INVALID("RRULE UNTIL must be UTC for DTSTART DATE or w/ time 
zone");
+            }
+            
+            // "If specified as a DATE-TIME value, then it MUST be specified in a UTC time format."
+            if (!until_is_date && !until_is_utc)
+                throw new ComponentError.INVALID("RRULE DATE-TIME UNTIL must be UTC");
         }
         
-        // "If specified as a DATE-TIME value, then it MUST be specified in a UTC time format."
-        if (!until_is_date && !until_is_utc)
-            throw new ComponentError.INVALID("RRULE DATE-TIME UNTIL must be UTC");
-        
         kind = iCal.icalproperty_kind.RRULE_PROPERTY;
         dt = rrule.until;
         zone = (!until_is_date || until_is_utc) ? Calendar.OlsonZone.utc : null;
diff --git a/src/component/component-event.vala b/src/component/component-event.vala
index 9a14b3d..ebae855 100644
--- a/src/component/component-event.vala
+++ b/src/component/component-event.vala
@@ -163,7 +163,7 @@ public class Event : Instance, Gee.Comparable<Event> {
         }
         
         try {
-            make_recurring(new RecurrenceRule.from_ical(ical_component));
+            make_recurring(new RecurrenceRule.from_ical(ical_component, false));
         } catch (ComponentError comperr) {
             // ignored; generally means no RRULE in component
         }
@@ -438,12 +438,10 @@ public class Event : Instance, Gee.Comparable<Event> {
         if (compare != 0)
             return compare;
         
-        // if recurring, go by sequence number, as the UID and RID are the same for all instances
-        if (is_recurring) {
-            compare = sequence - other.sequence;
-            if (compare != 0)
-                return compare;
-        }
+        // use sequence number if available
+        compare = sequence - other.sequence;
+        if (compare != 0)
+            return compare;
         
         // stabilize with UIDs
         return uid.compare_to(other.uid);
@@ -457,10 +455,10 @@ public class Event : Instance, Gee.Comparable<Event> {
         if (this == other_event)
             return true;
         
-        if (is_recurring != other_event.is_recurring)
+        if (is_recurring_instance != other_event.is_recurring_instance)
             return false;
         
-        if (is_recurring && !rid.equal_to(other_event.rid))
+        if (is_recurring_instance && !rid.equal_to(other_event.rid))
             return false;
         
         if (sequence != other_event.sequence)
diff --git a/src/component/component-instance.vala b/src/component/component-instance.vala
index 56c86ab..a515ade 100644
--- a/src/component/component-instance.vala
+++ b/src/component/component-instance.vala
@@ -71,8 +71,9 @@ public abstract class Instance : BaseObject, Gee.Hashable<Instance> {
      * Returns true if the { link Recurrable} is in fact a recurring instance.
      *
      * @see rid
+     * @see Backing.CalendarSource.fetch_master_component_async
      */
-    public bool is_recurring { get { return rid != null; } }
+    public bool is_recurring_instance { get { return rid != null; } }
     
     /**
      * The SEQUENCE of a VEVENT, VTODO, or VJOURNAL.
diff --git a/src/component/component-recurrence-rule.vala b/src/component/component-recurrence-rule.vala
index 289a4b3..ab3d624 100644
--- a/src/component/component-recurrence-rule.vala
+++ b/src/component/component-recurrence-rule.vala
@@ -137,7 +137,7 @@ public class RecurrenceRule : BaseObject {
         this.freq = freq;
     }
     
-    internal RecurrenceRule.from_ical(iCal.icalcomponent ical_component) throws Error {
+    internal RecurrenceRule.from_ical(iCal.icalcomponent ical_component, bool strict) throws Error {
         // need DTSTART for timezone purposes
         DateTime dtstart = new DateTime(ical_component, iCal.icalproperty_kind.DTSTART_PROPERTY);
         
@@ -155,7 +155,7 @@ public class RecurrenceRule : BaseObject {
         if (rrule.count > 0) {
             set_recurrence_count(rrule.count);
         } else {
-            Component.DateTime date_time = new DateTime.rrule_until(rrule, dtstart);
+            Component.DateTime date_time = new DateTime.rrule_until(rrule, dtstart, strict);
             if (date_time.is_date)
                 set_recurrence_end_date(date_time.to_date());
             else
@@ -253,6 +253,23 @@ public class RecurrenceRule : BaseObject {
     }
     
     /**
+     * Returns the UNTIL property as a { link Calendar.Date}.
+     *
+     * If { link until_exact_time} is set, only the Date portion is returned.
+     *
+     * @returns null if neither { link until_date} or until_exact_time is set.
+     */
+    public Calendar.Date? get_recurrence_end_date() {
+        if (until_date != null)
+            return until_date;
+        
+        if (until_exact_time != null)
+            return new Calendar.Date.from_exact_time(until_exact_time);
+        
+        return null;
+    }
+    
+    /**
      * Sets the { link count} property.
      *
      * Also clears { link until_date} and { link until_exact_time}.
diff --git a/src/host/host-create-update-recurring.vala b/src/host/host-create-update-recurring.vala
index 20b2a6a..7c4ba87 100644
--- a/src/host/host-create-update-recurring.vala
+++ b/src/host/host-create-update-recurring.vala
@@ -13,8 +13,8 @@ public class CreateUpdateRecurring : Gtk.Grid, Toolkit.Card {
     private const string PROP_START_DATE = "start-date";
     private const string PROP_END_DATE = "end-date";
     
-    // DO NOT CHANGE UNLESS YOU KNOW WHAT YOU'RE DOING.  These values are mirrored in the Glade
-    // file's repeats_combobox model.
+    // DO NOT CHANGE VALUES UNLESS YOU KNOW WHAT YOU'RE DOING.  These values are mirrored in the
+    // Glade file's repeats_combobox model.
     private enum Repeats {
         DAILY = 0,
         WEEKLY = 1,
@@ -56,6 +56,27 @@ public class CreateUpdateRecurring : Gtk.Grid, Toolkit.Card {
     private Gtk.Box on_days_box;
     
     [GtkChild]
+    private Gtk.CheckButton sunday_checkbutton;
+    
+    [GtkChild]
+    private Gtk.CheckButton monday_checkbutton;
+    
+    [GtkChild]
+    private Gtk.CheckButton tuesday_checkbutton;
+    
+    [GtkChild]
+    private Gtk.CheckButton wednesday_checkbutton;
+    
+    [GtkChild]
+    private Gtk.CheckButton thursday_checkbutton;
+    
+    [GtkChild]
+    private Gtk.CheckButton friday_checkbutton;
+    
+    [GtkChild]
+    private Gtk.CheckButton saturday_checkbutton;
+    
+    [GtkChild]
     private Gtk.Button start_date_button;
     
     [GtkChild]
@@ -80,6 +101,9 @@ public class CreateUpdateRecurring : Gtk.Grid, Toolkit.Card {
     private Gtk.Button ok_button;
     
     private new Component.Event? event = null;
+    private Component.Event? master = null;
+    private Gee.HashMap<Calendar.DayOfWeek, Gtk.CheckButton> on_day_checkbuttons = new Gee.HashMap<
+        Calendar.DayOfWeek, Gtk.CheckButton>();
     private bool blocking_insert_text_numbers_only_signal = false;
     
     public CreateUpdateRecurring() {
@@ -104,6 +128,15 @@ public class CreateUpdateRecurring : Gtk.Grid, Toolkit.Card {
             transform_date_to_string);
         bind_property(PROP_END_DATE, end_date_button, "label", BindingFlags.SYNC_CREATE,
             transform_date_to_string);
+        
+        // map on-day checkboxes to days of week
+        on_day_checkbuttons[Calendar.DayOfWeek.SUN] = sunday_checkbutton;
+        on_day_checkbuttons[Calendar.DayOfWeek.MON] = monday_checkbutton;
+        on_day_checkbuttons[Calendar.DayOfWeek.TUE] = tuesday_checkbutton;
+        on_day_checkbuttons[Calendar.DayOfWeek.WED] = wednesday_checkbutton;
+        on_day_checkbuttons[Calendar.DayOfWeek.THU] = thursday_checkbutton;
+        on_day_checkbuttons[Calendar.DayOfWeek.FRI] = friday_checkbutton;
+        on_day_checkbuttons[Calendar.DayOfWeek.SAT] = saturday_checkbutton;
     }
     
     private bool transform_repeats_active_to_on_days_visible(Binding binding, Value source_value,
@@ -126,25 +159,124 @@ public class CreateUpdateRecurring : Gtk.Grid, Toolkit.Card {
         
         // *must* have an Event by this point, whether from before or due to this jump
         assert(event != null);
+        
+        // need to load the master component in order to update the RRULE (which isn't stored in
+        // the generated instances)
+        load_master_async.begin();
+    }
+    
+    private async void load_master_async() {
+        try {
+            Component.Instance master_instance = yield event.calendar_source.fetch_master_component_async(
+                event.uid, null);
+            master = master_instance as Component.Event;
+        } catch (Error err) {
+            debug("Unable to load master from %s: %s", event.calendar_source.to_string(),
+                err.message);
+            
+            master = null;
+        }
+        
+        if (master == null) {
+            jump_back();
+            
+            return;
+        }
+        
         update_controls();
     }
     
     private void update_controls() {
-        make_recurring_checkbutton.active = (event.rrule != null);
+        assert(master != null);
         
-        // set to defaults if not a recurring event
-        if (event.rrule == null) {
+        make_recurring_checkbutton.active = (master.rrule != null);
+        
+        // some defaults that may not be set even if an RRULE is present
+        
+        // "Ends ... After" entry
+        after_entry.text = "1";
+        
+        // "Starts" and "Ends...On" entries
+        Calendar.DateSpan event_span = master.get_event_date_span(Calendar.Timezone.local);
+        start_date = event_span.start_date;
+        end_date = event_span.end_date;
+        
+        // Clear all "On days" checkboxes for sanity's sake
+        foreach (Gtk.CheckButton checkbutton in on_day_checkbuttons.values)
+            checkbutton.active = false;
+        
+        // set remaining defaults if not a recurring event
+        if (master.rrule == null) {
             repeats_combobox.active = Repeats.DAILY;
             every_entry.text = "1";
             never_radiobutton.active = true;
-            after_entry.text = "1";
-            
-            Calendar.DateSpan event_span = event.get_event_date_span(Calendar.Timezone.local);
-            start_date = event_span.start_date;
-            end_date = event_span.end_date;
             
             return;
         }
+        
+        // "Repeats" combobox
+        switch (master.rrule.freq) {
+            case iCal.icalrecurrencetype_frequency.DAILY_RECURRENCE:
+                repeats_combobox.active = Repeats.DAILY;
+            break;
+            
+            case iCal.icalrecurrencetype_frequency.WEEKLY_RECURRENCE:
+                repeats_combobox.active = Repeats.WEEKLY;
+            break;
+            
+            // TODO: Don't support MONTHLY RRULEs with multiple ByRules or ByRules we can't
+            // represent ... basically, non-simple repeating rules
+            case iCal.icalrecurrencetype_frequency.MONTHLY_RECURRENCE:
+                bool by_day = master.rrule.get_by_rule(Component.RecurrenceRule.ByRule.DAY).size > 0;
+                bool by_monthday = master.rrule.get_by_rule(Component.RecurrenceRule.ByRule.MONTH_DAY).size 
0;
+                
+                if (by_day && !by_monthday)
+                    repeats_combobox.active = Repeats.DAY_OF_THE_WEEK;
+                else if (!by_day && by_monthday)
+                    repeats_combobox.active = Repeats.DAY_OF_THE_MONTH;
+                else
+                    assert_not_reached();
+            break;
+            
+            case iCal.icalrecurrencetype_frequency.YEARLY_RECURRENCE:
+                repeats_combobox.active = Repeats.YEARLY;
+            break;
+            
+            // TODO: Don't support sub-day RRULEs
+            default:
+                assert_not_reached();
+        }
+        
+        // "Every" entry
+        every_entry.text = master.rrule.interval.to_string();
+        
+        // "On days" week day checkboxes are only visible if a WEEKLY event
+        if (master.rrule.is_weekly) {
+            Gee.Map<Calendar.DayOfWeek?, int> by_days =
+                
Component.RecurrenceRule.decode_days(master.rrule.get_by_rule(Component.RecurrenceRule.ByRule.DAY));
+            
+            // the presence of a "null" day means every or all days
+            if (by_days.has_key(null)) {
+                foreach (Gtk.CheckButton checkbutton in on_day_checkbuttons.values)
+                    checkbutton.active = true;
+            } else {
+                foreach (Calendar.DayOfWeek dow in by_days.keys)
+                    on_day_checkbuttons[dow].active = true;
+            }
+        }
+        
+        // "Ends" choices
+        if (!master.rrule.has_duration) {
+            never_radiobutton.active = true;
+        } else if (master.rrule.count > 0) {
+            after_radiobutton.active = true;
+            after_entry.text = master.rrule.count.to_string();
+        } else {
+            assert(master.rrule.until_date != null || master.rrule.until_exact_time != null);
+            
+            ends_on_radiobutton.active = true;
+            end_date = master.rrule.get_recurrence_end_date();
+        }
     }
     
     [GtkCallback]
@@ -228,7 +360,8 @@ public class CreateUpdateRecurring : Gtk.Grid, Toolkit.Card {
             .to_string(ch => ch.to_string());
         
         // insert new text into place, ensure this handler doesn't attempt to process this
-        // modified text
+        // modified text ... would use SignalHandler.block_by_func() and unblock_by_func(), but
+        // the bindings are ungood
         if (!String.is_empty(numbers_only)) {
             blocking_insert_text_numbers_only_signal = true;
             editable.insert_text(numbers_only, numbers_only.length, ref position);
diff --git a/src/host/host-main-window.vala b/src/host/host-main-window.vala
index 72327bb..e2ebb2f 100644
--- a/src/host/host-main-window.vala
+++ b/src/host/host-main-window.vala
@@ -424,7 +424,7 @@ public class MainWindow : Gtk.ApplicationWindow {
     }
     
     private void quick_create_event(Component.Event? initial, Gtk.Widget relative_to, Gdk.Point? 
for_location) {
-        QuickCreateEvent quick_create = new QuickCreateEvent(initial);
+        QuickCreateEvent quick_create = new QuickCreateEvent();
         
         CreateUpdateEvent create_update = new CreateUpdateEvent();
         create_update.is_update = false;
@@ -437,6 +437,9 @@ public class MainWindow : Gtk.ApplicationWindow {
             .to_array_list()
         );
         
+        // initialize the Deck with the initial event (if any)
+        deck.go_home(initial);
+        
         show_deck(relative_to, for_location, deck);
     }
     
diff --git a/src/host/host-quick-create-event.vala b/src/host/host-quick-create-event.vala
index e4ca931..869e7f2 100644
--- a/src/host/host-quick-create-event.vala
+++ b/src/host/host-quick-create-event.vala
@@ -40,15 +40,18 @@ public class QuickCreateEvent : Gtk.Grid, Toolkit.Card {
     
     private Toolkit.ComboBoxTextModel<Backing.CalendarSource> model;
     
-    public QuickCreateEvent(Component.Event? initial) {
-        event = initial;
+    public QuickCreateEvent() {
+    }
+    
+    public void jumped_to(Toolkit.Card? from, Toolkit.Card.Jump reason, Value? message) {
+        event = message as Component.Event;
         
         // if initial date/times supplied, reveal to the user and change the example
         string eg;
-        if (initial != null && (initial.date_span != null || initial.exact_time_span != null)) {
+        if (event != null && (event.date_span != null || event.exact_time_span != null)) {
             when_box.visible = true;
-            when_text_label.label = initial.get_event_time_pretty_string(Calendar.Timezone.local);
-            if (initial.date_span != null)
+            when_text_label.label = event.get_event_time_pretty_string(Calendar.Timezone.local);
+            if (event.date_span != null)
                 eg = _("Example: Dinner at Tadich Grill 7:30pm");
             else
                 eg = _("Example: Dinner at Tadich Grill");
@@ -76,9 +79,6 @@ public class QuickCreateEvent : Gtk.Grid, Toolkit.Card {
         calendar_combo_box.active = 0;
     }
     
-    public void jumped_to(Toolkit.Card? from, Toolkit.Card.Jump reason, Value? message) {
-    }
-    
     [GtkCallback]
     private void on_details_entry_icon_release(Gtk.Entry entry, Gtk.EntryIconPosition icon,
         Gdk.Event event) {
diff --git a/src/host/host-show-event.vala b/src/host/host-show-event.vala
index af4da0e..c647be4 100644
--- a/src/host/host-show-event.vala
+++ b/src/host/host-show-event.vala
@@ -90,7 +90,7 @@ public class ShowEvent : Gtk.Grid, Toolkit.Card {
         // https://bugzilla.gnome.org/show_bug.cgi?id=725786
         bool read_only = event.calendar_source != null && event.calendar_source.read_only;
         
-        bool updatable = !event.is_recurring && !read_only;
+        bool updatable = !event.is_recurring_instance && !read_only;
         update_button.visible = updatable;
         update_button.no_show_all = updatable;
         
@@ -142,7 +142,7 @@ public class ShowEvent : Gtk.Grid, Toolkit.Card {
         //
         // TODO: Gtk.Stack would be a better widget for this animation, but it's unavailable in
         // Glade as of GTK+ 3.12.
-        if (event.is_recurring) {
+        if (event.is_recurring_instance) {
             button_box_revealer.reveal_child = false;
             remove_recurring_revealer.reveal_child = true;
             
diff --git a/src/rc/create-update-recurring.ui b/src/rc/create-update-recurring.ui
index eda59b6..2a6456f 100644
--- a/src/rc/create-update-recurring.ui
+++ b/src/rc/create-update-recurring.ui
@@ -16,6 +16,7 @@
         <property name="visible">True</property>
         <property name="can_focus">True</property>
         <property name="receives_default">False</property>
+        <property name="halign">start</property>
         <property name="valign">start</property>
         <property name="use_underline">True</property>
         <property name="xalign">0</property>
@@ -33,8 +34,8 @@
         <property name="visible">True</property>
         <property name="can_focus">False</property>
         <property name="margin_left">16</property>
-        <property name="row_spacing">6</property>
-        <property name="column_spacing">6</property>
+        <property name="row_spacing">8</property>
+        <property name="column_spacing">8</property>
         <child>
           <object class="GtkLabel" id="label1">
             <property name="visible">True</property>
@@ -343,7 +344,7 @@
                 <property name="spacing">4</property>
                 <child>
                   <object class="GtkRadioButton" id="after_radiobutton">
-                    <property name="label" translatable="yes">Aft_er</property>
+                    <property name="label" translatable="yes" comments="As in, &quot;After n 
events&quot;">Aft_er</property>
                     <property name="visible">True</property>
                     <property name="can_focus">True</property>
                     <property name="receives_default">False</property>
@@ -380,7 +381,7 @@
                   <object class="GtkLabel" id="after_label">
                     <property name="visible">True</property>
                     <property name="can_focus">False</property>
-                    <property name="label" translatable="yes">events</property>
+                    <property name="label">(none)</property>
                   </object>
                   <packing>
                     <property name="expand">False</property>


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]