[california] Set and honor default calendar: Bug #725781
- From: Jim Nelson <jnelson src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [california] Set and honor default calendar: Bug #725781
- Date: Wed, 22 Oct 2014 22:25:24 +0000 (UTC)
commit d82f0476cdb33d32dbd5f43715f31d8603971c64
Author: Jim Nelson <jim yorba org>
Date: Wed Oct 22 15:24:30 2014 -0700
Set and honor default calendar: Bug #725781
California now honors EDS' default calendar by listing it first
when creating a new event. Default is displayed in Calendar Manager
and can be changed by the user.
src/backing/backing-calendar-source.vala | 21 +++++++++
src/backing/backing-store.vala | 13 ++++++
src/backing/eds/backing-eds-store.vala | 49 ++++++++++++++++++++--
src/host/host-create-update-event.vala | 2 +-
src/host/host-quick-create-event.vala | 4 +-
src/host/host.vala | 10 ++++
src/manager/manager-calendar-list-item.vala | 28 ++++++++++++
src/rc/calendar-manager-list-item.ui | 56 +++++++++++++++++-------
src/toolkit/toolkit-combo-box-text-model.vala | 46 ++++++++++++++++++++
9 files changed, 205 insertions(+), 24 deletions(-)
---
diff --git a/src/backing/backing-calendar-source.vala b/src/backing/backing-calendar-source.vala
index aa5755d..bc27ed0 100644
--- a/src/backing/backing-calendar-source.vala
+++ b/src/backing/backing-calendar-source.vala
@@ -18,6 +18,8 @@ namespace California.Backing {
*/
public abstract class CalendarSource : Source {
+ public const string PROP_IS_DEFAULT = "is-default";
+
/**
* The affected range of a removal operation.
*
@@ -39,8 +41,27 @@ public abstract class CalendarSource : Source {
ALL
}
+ /**
+ * Indicates this { link CalendarSource} is the default calendar for its { link Backing.Store}.
+ *
+ * Thus, if/when there are more than one Backing.Store, there can be more than one default
+ * CalendarSource.
+ */
+ public bool is_default { get; private set; default = false; }
+
protected CalendarSource(Store store, string id, string title) {
base (store, id, title);
+
+ // watch store for change to default calendar
+ store.notify[Store.PROP_DEFAULT_CALENDAR].connect(on_store_default_calendar_changed);
+ }
+
+ ~CalendarSource() {
+ store.notify[Store.PROP_DEFAULT_CALENDAR].disconnect(on_store_default_calendar_changed);
+ }
+
+ private void on_store_default_calendar_changed() {
+ is_default = store.default_calendar == this;
}
/**
diff --git a/src/backing/backing-store.vala b/src/backing/backing-store.vala
index e29d3a9..bdd9194 100644
--- a/src/backing/backing-store.vala
+++ b/src/backing/backing-store.vala
@@ -14,6 +14,7 @@ namespace California.Backing {
public abstract class Store : BaseObject {
public const string PROP_IS_OPEN = "is-open";
+ public const string PROP_DEFAULT_CALENDAR = "default-calendar";
/**
* Set when the { link Store} is opened.
@@ -22,6 +23,11 @@ public abstract class Store : BaseObject {
*/
public bool is_open { get; protected set; default = false; }
+ /**
+ * Set to the default { link CalendarSource} for this { link Store}.
+ */
+ public CalendarSource? default_calendar { get; protected set; default = null; }
+
private string desc;
/**
@@ -114,6 +120,13 @@ public abstract class Store : BaseObject {
return result;
}
+ /**
+ * Set the { link CalendarSource} to the default for this { link Store}.
+ *
+ * @throws { link BackingError.MISMATCH} if CalendarSource did not originate from this store.
+ */
+ public abstract void make_default_calendar(Backing.CalendarSource calendar) throws Error;
+
public override string to_string() {
return desc;
}
diff --git a/src/backing/eds/backing-eds-store.vala b/src/backing/eds/backing-eds-store.vala
index 8c192a4..e4cdb44 100644
--- a/src/backing/eds/backing-eds-store.vala
+++ b/src/backing/eds/backing-eds-store.vala
@@ -30,6 +30,10 @@ internal class EdsStore : Store, WebCalSubscribable, CalDAVSubscribable {
registry.source_added.connect(eds_source => add_eds_source_async.begin(eds_source));
registry.source_removed.connect(eds_source => remove_eds_source(eds_source));
+ // watch for external changes of the default calendar and use handler to initialize
+ registry.notify["default-calendar"].connect(on_default_calendar_changed);
+ on_default_calendar_changed();
+
is_open = true;
}
@@ -43,12 +47,33 @@ internal class EdsStore : Store, WebCalSubscribable, CalDAVSubscribable {
is_open = false;
}
+ private void check_open() throws BackingError {
+ if (!is_open)
+ throw new BackingError.UNAVAILABLE("EDS not open");
+ }
+
+ private void on_default_calendar_changed() {
+ // EDS has a habit of issue property notifications in background threads, so ensure this
+ // property change happens in the foreground thread
+ Idle.add(() => {
+ Backing.CalendarSource? new_default_calendar = sources[registry.default_calendar]
+ as Backing.CalendarSource;
+ if (default_calendar == new_default_calendar)
+ return false;
+
+ default_calendar = new_default_calendar;
+
+ debug("Default EDS calendar: %s", (default_calendar != null) ? default_calendar.title :
"(none)");
+
+ return false;
+ });
+ }
+
/**
* @inheritDoc
*/
public override async void remove_source_async(Source source, Cancellable? cancellable) throws Error {
- if (!is_open)
- throw new BackingError.UNAVAILABLE("EDS not open");
+ check_open();
if (source.store != this) {
throw new BackingError.INVALID("Attempted to remove source %s from wrong store %s",
@@ -101,8 +126,7 @@ internal class EdsStore : Store, WebCalSubscribable, CalDAVSubscribable {
private async void subscribe_eds_async(string title, Soup.URI uri, string? username, string color,
string backend_name, Cancellable? cancellable) throws Error {
- if (!is_open)
- throw new BackingError.UNAVAILABLE("EDS not open");
+ check_open();
E.Source scratch = new E.Source(null, null);
// Surprise -- Google gets special treatment
@@ -154,6 +178,9 @@ internal class EdsStore : Store, WebCalSubscribable, CalDAVSubscribable {
yield registry.create_sources(sources, cancellable);
}
+ /**
+ * @inheritDoc
+ */
public override Gee.List<Source> get_sources() {
Gee.List<Source> list = new Gee.ArrayList<Source>();
list.add_all(sources.values.read_only_view);
@@ -201,6 +228,20 @@ internal class EdsStore : Store, WebCalSubscribable, CalDAVSubscribable {
if (calendar != null)
calendar.close_async.begin(null);
}
+
+ /**
+ * @inheritDoc
+ */
+ public override void make_default_calendar(Backing.CalendarSource calendar) throws Error {
+ check_open();
+
+ Backing.EdsCalendarSource? eds_calendar = calendar as Backing.EdsCalendarSource;
+ if (eds_calendar == null)
+ throw new BackingError.MISMATCH("Not an EDS calendar source");
+
+ if (registry.default_calendar != eds_calendar.eds_source)
+ registry.default_calendar = eds_calendar.eds_source;
+ }
}
}
diff --git a/src/host/host-create-update-event.vala b/src/host/host-create-update-event.vala
index 300968e..561d91e 100644
--- a/src/host/host-create-update-event.vala
+++ b/src/host/host-create-update-event.vala
@@ -151,7 +151,7 @@ public class CreateUpdateEvent : Gtk.Grid, Toolkit.Card {
if (event.calendar_source != null) {
calendar_model.set_item_active(event.calendar_source);
} else {
- calendar_combo.active = 0;
+ calendar_model.set_item_default_active();
is_update = false;
}
diff --git a/src/host/host-quick-create-event.vala b/src/host/host-quick-create-event.vala
index 2624cc5..f55721a 100644
--- a/src/host/host-quick-create-event.vala
+++ b/src/host/host-quick-create-event.vala
@@ -87,8 +87,8 @@ public class QuickCreateEvent : Gtk.Grid, Toolkit.Card {
example_label.label = "<small><i>%s</i></small>".printf(eg);
- // make first item active
- calendar_combo_box.active = 0;
+ // reset calendar combo to default active item
+ model.set_item_default_active();
}
[GtkCallback]
diff --git a/src/host/host.vala b/src/host/host.vala
index 7f5758c..a1fada4 100644
--- a/src/host/host.vala
+++ b/src/host/host.vala
@@ -48,6 +48,7 @@ private Toolkit.ComboBoxTextModel<Backing.CalendarSource> build_calendar_source_
// initialize with current list of calendars ... this control does not auto-update as
// calendars are added/removed/modified
+ Backing.CalendarSource? first_default = null;
foreach (Backing.CalendarSource calendar_source in
Backing.Manager.instance.get_sources_of_type<Backing.CalendarSource>()) {
if (!include_invisible && !calendar_source.visible)
@@ -57,6 +58,15 @@ private Toolkit.ComboBoxTextModel<Backing.CalendarSource> build_calendar_source_
continue;
model.add(calendar_source);
+
+ // set first calendar marked as default as the initial choice
+ if (calendar_source.is_default && first_default == null)
+ first_default = calendar_source;
+ }
+
+ if (first_default != null) {
+ model.set_item_active(first_default);
+ model.make_default_active(first_default);
}
return model;
diff --git a/src/manager/manager-calendar-list-item.vala b/src/manager/manager-calendar-list-item.vala
index ca6a2a6..f770cb4 100644
--- a/src/manager/manager-calendar-list-item.vala
+++ b/src/manager/manager-calendar-list-item.vala
@@ -36,6 +36,9 @@ internal class CalendarListItem : Gtk.Grid, Toolkit.MutableWidget {
[GtkChild]
private Gtk.ColorButton color_button;
+ [GtkChild]
+ private Gtk.Image default_icon;
+
private Toolkit.EditableLabel? editable_label = null;
public CalendarListItem(Backing.CalendarSource source) {
@@ -55,6 +58,10 @@ internal class CalendarListItem : Gtk.Grid, Toolkit.MutableWidget {
BindingFlags.SYNC_CREATE, xform_readonly_to_icon_name);
source.bind_property(Backing.Source.PROP_READONLY, readonly_icon, "tooltip-text",
BindingFlags.SYNC_CREATE, xform_readonly_to_tooltip_text);
+ source.bind_property(Backing.CalendarSource.PROP_IS_DEFAULT, default_icon, "icon-name",
+ BindingFlags.SYNC_CREATE, xform_default_to_icon_name);
+ source.bind_property(Backing.CalendarSource.PROP_IS_DEFAULT, default_icon, "tooltip-text",
+ BindingFlags.SYNC_CREATE, xform_default_to_tooltip_text);
title_eventbox.button_release_event.connect(on_title_button_release);
}
@@ -80,6 +87,18 @@ internal class CalendarListItem : Gtk.Grid, Toolkit.MutableWidget {
return true;
}
+ private bool xform_default_to_icon_name(Binding binding, Value source_value, ref Value target_value) {
+ target_value = source.is_default ? "starred-symbolic" : "";
+
+ return true;
+ }
+
+ private bool xform_default_to_tooltip_text(Binding binding, Value source_value, ref Value target_value) {
+ target_value = source.is_default ? _("Calendar is default") : _("Make this calendar default");
+
+ return true;
+ }
+
public override bool query_tooltip(int x, int y, bool keyboard_mode, Gtk.Tooltip tooltip) {
// no tooltip if text is entirely shown
if (!title_label.get_layout().is_ellipsized())
@@ -150,6 +169,15 @@ internal class CalendarListItem : Gtk.Grid, Toolkit.MutableWidget {
if (!String.is_empty(text))
source.title = text;
}
+
+ [GtkCallback]
+ private void on_default_button_clicked() {
+ try {
+ source.store.make_default_calendar(source);
+ } catch (Error err) {
+ message("Unable to set default calendar to %s: %s", source.title, err.message);
+ }
+ }
}
}
diff --git a/src/rc/calendar-manager-list-item.ui b/src/rc/calendar-manager-list-item.ui
index 420b610..d074600 100644
--- a/src/rc/calendar-manager-list-item.ui
+++ b/src/rc/calendar-manager-list-item.ui
@@ -7,6 +7,34 @@
<property name="can_focus">False</property>
<property name="column_spacing">4</property>
<child>
+ <object class="GtkEventBox" id="title_eventbox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="events">GDK_BUTTON_RELEASE_MASK | GDK_STRUCTURE_MASK</property>
+ <property name="visible_window">False</property>
+ <child>
+ <object class="GtkLabel" id="title_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="valign">center</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="label">calendar name</property>
+ <property name="ellipsize">end</property>
+ <property name="single_line_mode">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">4</property>
+ <property name="top_attach">0</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
<object class="GtkColorButton" id="color_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
@@ -17,7 +45,7 @@
<property name="title" translatable="yes">Calendar color</property>
</object>
<packing>
- <property name="left_attach">2</property>
+ <property name="left_attach">3</property>
<property name="top_attach">0</property>
<property name="width">1</property>
<property name="height">1</property>
@@ -38,7 +66,7 @@
</child>
</object>
<packing>
- <property name="left_attach">1</property>
+ <property name="left_attach">2</property>
<property name="top_attach">0</property>
<property name="width">1</property>
<property name="height">1</property>
@@ -55,35 +83,29 @@
<property name="icon_size">1</property>
</object>
<packing>
- <property name="left_attach">0</property>
+ <property name="left_attach">1</property>
<property name="top_attach">0</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
</child>
<child>
- <object class="GtkEventBox" id="title_eventbox">
+ <object class="GtkButton" id="default_button">
<property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="events">GDK_BUTTON_RELEASE_MASK | GDK_STRUCTURE_MASK</property>
- <property name="visible_window">False</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="relief">none</property>
+ <signal name="clicked" handler="on_default_button_clicked"
object="CaliforniaManagerCalendarListItem" swapped="no"/>
<child>
- <object class="GtkLabel" id="title_label">
+ <object class="GtkImage" id="default_icon">
<property name="visible">True</property>
<property name="can_focus">False</property>
- <property name="valign">center</property>
- <property name="hexpand">True</property>
- <property name="vexpand">True</property>
- <property name="xalign">0</property>
- <property name="yalign">0</property>
- <property name="label">calendar name</property>
- <property name="ellipsize">end</property>
- <property name="single_line_mode">True</property>
+ <property name="pixel_size">16</property>
</object>
</child>
</object>
<packing>
- <property name="left_attach">3</property>
+ <property name="left_attach">0</property>
<property name="top_attach">0</property>
<property name="width">1</property>
<property name="height">1</property>
diff --git a/src/toolkit/toolkit-combo-box-text-model.vala b/src/toolkit/toolkit-combo-box-text-model.vala
index dc556cb..8e5f58e 100644
--- a/src/toolkit/toolkit-combo-box-text-model.vala
+++ b/src/toolkit/toolkit-combo-box-text-model.vala
@@ -27,6 +27,13 @@ public class ComboBoxTextModel<G> : BaseObject {
public G? active { get; private set; }
/**
+ * The default active item.
+ *
+ * This can be used to restore the model to an initial state.
+ */
+ public G? default_active { get; private set; default = null; }
+
+ /**
* Set to true if the { link ModelPresentation} returns Pango markup instead of plain text.
*/
public bool is_markup { get; set; default = false; }
@@ -159,6 +166,45 @@ public class ComboBoxTextModel<G> : BaseObject {
}
/**
+ * Makes the item the { link default_active} item.
+ *
+ * The supplied item must already be a member of the model.
+ *
+ * @returns False if not present in model.
+ */
+ public bool make_default_active(G item) {
+ if (!indices.has_key(item))
+ return false;
+
+ default_active = item;
+
+ return true;
+ }
+
+ /**
+ * Clears the { link default_active} item.
+ */
+ public void clear_default_active() {
+ default_active = null;
+ }
+
+ /**
+ * Makes the { link default_active} item active in the Gtk.ComboBoxText.
+ *
+ * If default_active is null, the Gtk.ComboBoxText's zeroeth item will be set active.
+ *
+ * Returns the result of { link set_item_active} or true if default_active is null.
+ */
+ public bool set_item_default_active() {
+ if (default_active != null)
+ return set_item_active(default_active);
+
+ combo_box.active = 0;
+
+ return true;
+ }
+
+ /**
* Returns the item at the Gtk.ComboBoxText index.
*/
public G? get_item_at(int index) {
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]