[california] Remember last displayed view when starting: Bug #730603



commit 056878374f3e6f387f8736c26923a93ed24b121a
Author: Jim Nelson <jim yorba org>
Date:   Thu May 29 18:21:58 2014 -0700

    Remember last displayed view when starting: Bug #730603
    
    Last displayed view (month, week, etc.) is stored in GSettings via
    bound properties.

 Makefile.am                                 |    2 +
 configure.ac                                |    2 +
 data/Makefile.am                            |   12 +++++
 data/org.yorba.california.gschema.xml       |   14 ++++++
 src/Makefile.am                             |    1 +
 src/application/california-application.vala |   50 +++++++++++++++++++++-
 src/application/california-settings.vala    |   61 +++++++++++++++++++++++++++
 src/base/base-object.vala                   |   17 +++++++
 src/calendar/calendar-system.vala           |    2 +-
 src/host/host-main-window.vala              |   25 ++++++++++-
 src/view/month/month-controller.vala        |    7 +++
 src/view/view-controllable.vala             |    7 +++
 src/view/week/week-controller.vala          |    7 +++
 13 files changed, 203 insertions(+), 4 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index d691c19..b4b1897 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -25,6 +25,8 @@ EXTRA_DIST = \
        intltool-update.in\
        $(NULL)
 
+BUILT_SOURCES =
+
 CLEANFILES =
 
 DISTCLEANFILES = \
diff --git a/configure.ac b/configure.ac
index 7a15354..8cdcbe2 100644
--- a/configure.ac
+++ b/configure.ac
@@ -47,6 +47,8 @@ PKG_CHECK_MODULES(CALIFORNIA, [
 AC_SUBST(CALIFORNIA_CFLAGS)
 AC_SUBST(CALIFORNIA_LIBS)
 
+GLIB_GSETTINGS
+
 #
 # configure switches
 #
diff --git a/data/Makefile.am b/data/Makefile.am
index 3629caa..5fa5269 100644
--- a/data/Makefile.am
+++ b/data/Makefile.am
@@ -5,13 +5,25 @@ data_desktop_DATA = $(data_desktop_in_files:.desktop.in=.desktop)
 data_appdatadir = $(datadir)/appdata
 data_appdata_DATA = data/california.appdata.xml
 
+gsettings_SCHEMAS = data/org.yorba.california.gschema.xml
+ GSETTINGS_RULES@
+
+# Local build of California's GSettings schema to allow executing from build dir
+data/gschemas.compiled: data/org.yorba.california.gschema.xml
+       @echo Building local GSettings schema...
+       @glib-compile-schemas data/
+
+BUILT_SOURCES += data/gschemas.compiled
+
 EXTRA_DIST += \
        $(data_desktop_in_files) \
        data/california.appdata.xml \
+       data/org.yorba.california.gschema.xml \
        $(NULL)
 
 CLEANFILES += \
        $(data_desktop_DATA) \
+       data/gschemas.compiled \
        $(NULL)
 
 DISTCLEANFILES += \
diff --git a/data/org.yorba.california.gschema.xml b/data/org.yorba.california.gschema.xml
new file mode 100644
index 0000000..88cedb0
--- /dev/null
+++ b/data/org.yorba.california.gschema.xml
@@ -0,0 +1,14 @@
+<schemalist>
+
+<schema id="org.yorba.california" path="/org/yorba/california/">
+    <key name="calendar-view" type="s">
+        <default>"month"</default>
+        <summary>Current calendar view</summary>
+        <description>
+            Legal values include "week" and "month".
+        </description>
+    </key>
+</schema>
+
+</schemalist>
+
diff --git a/src/Makefile.am b/src/Makefile.am
index 67612c2..5cdbef3 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -25,6 +25,7 @@ california_VALASOURCES = \
        application/california-application.vala \
        application/california-commandline.vala \
        application/california-resource.vala \
+       application/california-settings.vala \
        application/main.vala \
        \
        backing/backing.vala \
diff --git a/src/application/california-application.vala b/src/application/california-application.vala
index 996ce75..711c3e0 100644
--- a/src/application/california-application.vala
+++ b/src/application/california-application.vala
@@ -58,8 +58,50 @@ public class Application : Gtk.Application {
         { ACTION_PROCESS_FILE, on_process_file, "s" }
     };
     
+    /**
+     * The executable's location on the filesystem.
+     *
+     * This will be null until { link local_command_line} is executed.
+     */
+    public File? exec_file { get; private set; default = null; }
+    
+    /**
+     * The executable's parent directory on the filesystem.
+     *
+     * This will be null until { link local_command_line} is executed.
+     */
+    public File? exec_dir { owned get { return (exec_file != null) ? exec_file.get_parent() : null; } }
+    
+    /**
+     * The configured prefix directory as a File.
+     */
+    public File prefix_dir { owned get { return File.new_for_path(PREFIX); } }
+    
+    /**
+     * Whether or not the running executable is the installed executable (if installed at all).
+     *
+     * False if { link local_command_line} hasn't executed yet.
+     */
+    public bool is_installed {
+        get {
+            return (exec_dir != null) ? exec_dir.has_prefix(prefix_dir) : false;
+        }
+    }
+    
+    /**
+     * If not installed, returns the root of the build directory (which may not be the location
+     * of the main executable).
+     *
+     * null if { link is_installed} is true.
+     */
+    public File? build_root_dir {
+        owned get {
+            // currently the build system stores the exec in the src/ directory
+            return (!is_installed && exec_dir != null) ? exec_dir.get_parent() : null;
+        }
+    }
+    
     private Host.MainWindow? main_window = null;
-    private File? exec_file = null;
     
     private Application() {
         Object (application_id: ID);
@@ -107,6 +149,7 @@ public class Application : Gtk.Application {
         
         // unit initialization
         try {
+            Settings.init();
             Host.init();
             Manager.init();
             Activator.init();
@@ -128,6 +171,7 @@ public class Application : Gtk.Application {
         Activator.terminate();
         Manager.terminate();
         Host.terminate();
+        Settings.terminate();
         
         base.shutdown();
     }
@@ -145,7 +189,9 @@ public class Application : Gtk.Application {
         base.activate();
     }
     
-    // Presents a modal error dialog to the user
+    /*
+     * Presents a modal error dialog to the user.
+     */
     public void error_message(string msg) {
         Gtk.MessageDialog dialog = new Gtk.MessageDialog(main_window, Gtk.DialogFlags.MODAL,
             Gtk.MessageType.ERROR, Gtk.ButtonsType.OK, "%s", msg);
diff --git a/src/application/california-settings.vala b/src/application/california-settings.vala
new file mode 100644
index 0000000..226c03e
--- /dev/null
+++ b/src/application/california-settings.vala
@@ -0,0 +1,61 @@
+/* Copyright 2014 Yorba Foundation
+ *
+ * This software is licensed under the GNU Lesser General Public License
+ * (version 2.1 or later).  See the COPYING file in this distribution.
+ */
+
+namespace California {
+
+/**
+ * Various application settings for California (stored in GSettings) available as a singleton.
+ */
+
+public class Settings : BaseObject {
+    public const string PROP_CALENDAR_VIEW = "calendar-view";
+    
+    // GSettings schema identifier.
+    private const string SCHEMA_ID = "org.yorba.california";
+    
+    // schema key ids may be the same as property names, but want to keep them different in case
+    // one or the other changes
+    private const string KEY_CALENDAR_VIEW = "calendar-view";
+    
+    public static Settings instance { get; private set; }
+    
+    public string calendar_view { get; set; }
+    
+    private GLib.Settings settings;
+    
+    private Settings() {
+        // construct after env is in place
+        settings = new GLib.Settings(SCHEMA_ID);
+        
+        // bind GSettings values to properties here, which callers may access directly or bind to
+        // themselves (with a bit more type safety)
+        settings.bind(KEY_CALENDAR_VIEW, this, PROP_CALENDAR_VIEW, SettingsBindFlags.DEFAULT);
+    }
+    
+    internal static void init() throws Error {
+        // this needs to be available before initialization
+        assert(Application.instance.exec_file != null);
+        
+        // if not running installed executable, point GSettings to our copy in the build directory
+        if (!Application.instance.is_installed) {
+            File schema_dir = Application.instance.build_root_dir.get_child("data");
+            Environment.set_variable("GSETTINGS_SCHEMA_DIR", schema_dir.get_path(), true);
+        }
+        
+        instance = new Settings();
+    }
+    
+    internal static void terminate() {
+        instance = null;
+    }
+    
+    public override string to_string() {
+        return get_class().get_type().name();
+    }
+}
+
+}
+
diff --git a/src/base/base-object.vala b/src/base/base-object.vala
index 99025b4..97779dd 100644
--- a/src/base/base-object.vala
+++ b/src/base/base-object.vala
@@ -4,6 +4,10 @@
  * (version 2.1 or later).  See the COPYING file in this distribution.
  */
 
+// It's either this or double-unref Binding objects; see
+// https://bugzilla.gnome.org/show_bug.cgi?id=730967
+extern void g_binding_unbind(Binding *binding);
+
 namespace California {
 
 /**
@@ -15,6 +19,19 @@ public abstract class BaseObject : Object {
     }
     
     /**
+     * Helper for unbinding properties until g_binding_unbind() is bound.
+     *
+     * See [[https://bugzilla.gnome.org/show_bug.cgi?id=730967]]
+     */
+    public static void unbind_property(ref Binding? binding) {
+        if (binding == null)
+            return;
+        
+        g_binding_unbind(binding);
+        binding = null;
+    }
+    
+    /**
      * Returns a string representation of the object ''for debugging and logging only''.
      *
      * String conversion for other purposes (user labels, serialization, etc.) should be handled
diff --git a/src/calendar/calendar-system.vala b/src/calendar/calendar-system.vala
index 8bd745e..090d695 100644
--- a/src/calendar/calendar-system.vala
+++ b/src/calendar/calendar-system.vala
@@ -91,7 +91,7 @@ public class System : BaseObject {
      */
     public signal void timezone_changed(Timezone old_timezone, Timezone new_timezone);
     
-    private Settings system_clock_format_schema = new Settings(CLOCK_FORMAT_SCHEMA);
+    private GLib.Settings system_clock_format_schema = new GLib.Settings(CLOCK_FORMAT_SCHEMA);
     private uint date_timer_id = 0;
     
     private System() {
diff --git a/src/host/host-main-window.vala b/src/host/host-main-window.vala
index 579f169..ddb084a 100644
--- a/src/host/host-main-window.vala
+++ b/src/host/host-main-window.vala
@@ -51,6 +51,8 @@ public class MainWindow : Gtk.ApplicationWindow {
     private Gtk.Stack view_stack = new Gtk.Stack();
     private Gtk.HeaderBar headerbar = new Gtk.HeaderBar();
     private Gtk.Button today = new Gtk.Button.with_label(_("_Today"));
+    private Binding view_stack_binding;
+    private Gee.HashSet<string> view_stack_ids = new Gee.HashSet<string>();
     
     public MainWindow(Application app) {
         Object (application: app);
@@ -161,6 +163,26 @@ public class MainWindow : Gtk.ApplicationWindow {
         layout.pack_end(view_stack, true, true, 0);
         
         add(layout);
+        
+        // bind stack's visible child property to the settings for it, both ways ... because we want
+        // to initialize with the settings, use it as the source w/ SYNC_CREATE
+        view_stack_binding = Settings.instance.bind_property(Settings.PROP_CALENDAR_VIEW, view_stack,
+            "visible-child-name", BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL,
+            transform_setting_to_calendar_view);
+        
+        // to prevent storing the different children's names as the widget is destroyed (cleared,
+        // i.e. remove each one by one), unbind before that occurs
+        view_stack.destroy.connect(() => { BaseObject.unbind_property(ref view_stack_binding); });
+    }
+    
+    // only allow known stack children ids through
+    private bool transform_setting_to_calendar_view(Binding binding, Value source, ref Value target) {
+        if (!view_stack_ids.contains(source.get_string()))
+            return false;
+        
+        target = source;
+        
+        return true;
     }
     
     public override void map() {
@@ -172,7 +194,8 @@ public class MainWindow : Gtk.ApplicationWindow {
     }
     
     private void add_controller(View.Controllable controller) {
-        view_stack.add_titled(controller.get_container(), controller.title, controller.title);
+        view_stack_ids.add(controller.id);
+        view_stack.add_titled(controller.get_container(), controller.id, controller.title);
         controller.get_container().show_all();
     }
     
diff --git a/src/view/month/month-controller.vala b/src/view/month/month-controller.vala
index 60ef605..022173b 100644
--- a/src/view/month/month-controller.vala
+++ b/src/view/month/month-controller.vala
@@ -16,6 +16,8 @@ namespace California.View.Month {
 public class Controller : BaseObject, View.Controllable {
     public const string PROP_MONTH_OF_YEAR = "month-of-year";
     
+    public const string VIEW_ID = "month";
+    
     // number of Grids to keep in GtkStack and cache (in terms of months) ... this should be an
     // even number, as it is halved to determine neighboring months depths
     private const int CACHE_NEIGHBORS_COUNT = 4;
@@ -45,6 +47,11 @@ public class Controller : BaseObject, View.Controllable {
     /**
      * @inheritDoc
      */
+    public string id { get { return VIEW_ID; } }
+    
+    /**
+     * @inheritDoc
+     */
     public string title { get { return _("Month"); } }
     
     /**
diff --git a/src/view/view-controllable.vala b/src/view/view-controllable.vala
index f25b92a..d004fb0 100644
--- a/src/view/view-controllable.vala
+++ b/src/view/view-controllable.vala
@@ -21,6 +21,13 @@ public interface Controllable : Object {
     public const string PROP_FIRST_OF_WEEK = "first-of-week";
     
     /**
+     * A short string uniquely identifying this view.
+     *
+     * Since this value will be persisted, it's important it does not change without good reason.
+     */
+    public abstract string id { get; }
+    
+    /**
      * A user-visible string (short) representing this view.
      */
     public abstract string title { get; }
diff --git a/src/view/week/week-controller.vala b/src/view/week/week-controller.vala
index fbba969..ab602f2 100644
--- a/src/view/week/week-controller.vala
+++ b/src/view/week/week-controller.vala
@@ -13,6 +13,8 @@ namespace California.View.Week {
 public class Controller : BaseObject, View.Controllable {
     public const string PROP_WEEK = "week";
     
+    public const string VIEW_ID = "week";
+    
     private const int CACHE_NEIGHBORS_COUNT = 4;
     
     private class ViewContainer : Gtk.Stack, View.Container {
@@ -32,6 +34,11 @@ public class Controller : BaseObject, View.Controllable {
     /**
      * @inheritDoc
      */
+    public string id { get { return VIEW_ID; } }
+    
+    /**
+     * @inheritDoc
+     */
     public string title { get { return _("Week"); } }
     
     /**


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