[dconf-editor] Add setting to sort folders first or last



commit c726a145aeee241356da4e06c7ed4fe76a732c70
Author: Davi da Silva Böger <dsboger gmail com>
Date:   Sun Nov 12 03:31:52 2017 -0200

    Add setting to sort folders first or last
    
    Add a 'sort-folders' setting whose value determines if the folders
    should come first, last or mixed (default) in the sorting order. Some of
    the sorting mechanics was refactored in the process to better accomodate
    the new setting. In particular, sorting logic was mostly concentrated in
    a new SortingOptions class.
    
    Fix https://bugzilla.gnome.org/show_bug.cgi?id=790116

 editor/ca.desrt.dconf-editor.gschema.xml |   11 ++-
 editor/dconf-model.vala                  |  160 ++++++++++++++++++++++++++----
 editor/registry-view.vala                |   15 ++-
 3 files changed, 159 insertions(+), 27 deletions(-)
---
diff --git a/editor/ca.desrt.dconf-editor.gschema.xml b/editor/ca.desrt.dconf-editor.gschema.xml
index 3fd9509..1088f1e 100644
--- a/editor/ca.desrt.dconf-editor.gschema.xml
+++ b/editor/ca.desrt.dconf-editor.gschema.xml
@@ -11,6 +11,11 @@
     <value value="3" nick="always-confirm-explicit"/>
     <value value="4" nick="always-delay"/>
   </enum>
+  <enum id="ca.desrt.dconf-editor.MergeType">
+    <value value="0" nick="mixed"/>
+    <value value="1" nick="first"/>
+    <value value="2" nick="last"/>
+  </enum>
   <schema id="ca.desrt.dconf-editor.Settings" path="/ca/desrt/dconf-editor/">
     <child schema="ca.desrt.dconf-editor.Demo" name="demo"/>
     <key name="window-width" type="i">
@@ -73,7 +78,11 @@
       <summary>A flag to sort keys list case-sensitively</summary>
       <description>GSettings doesn’t allow keys to use upper-case in their names, but installation paths of 
schemas can. If “true”, the keys list is sorted case-sensitively, with in usual order upper-case folders 
first.</description>
     </key>
-    <!-- TODO sort-folder-first/last/mixed ; maybe also sort-reversed, but should that one be saved between 
sessions if the last path is not? -->
+    <key name="sort-folders" enum="ca.desrt.dconf-editor.MergeType">
+      <default>'mixed'</default>
+      <summary>A flag to sort folders before, after or mixed with keys</summary>
+      <description>If “mixed”, folders are sorted together with keys; if “first”, all folders are sorted 
before keys; if “last”, all folders are sorted after keys.</description>
+    </key>
     <key type="b" name="mouse-use-extra-buttons">
       <default>true</default>
       <summary>Use “Back” and “Forward” mouse button events</summary>
diff --git a/editor/dconf-model.vala b/editor/dconf-model.vala
index f6dc0b9..a9680a4 100644
--- a/editor/dconf-model.vala
+++ b/editor/dconf-model.vala
@@ -57,9 +57,6 @@ public class Directory : SettingObject
 
     private DConf.Client client;
 
-    private bool? last_sort = null;
-    private bool? require_sorting = null;
-
     private GLib.ListStore? _key_model = null;
     public GLib.ListStore key_model
     {
@@ -82,36 +79,23 @@ public class Directory : SettingObject
 
     private void create_folders ()
     {
-        require_sorting = false;
         children.foreach ((dir) => {
                 SettingObject _dir = (SettingObject) dir;
                 ((!) _key_model).append (_dir);
-                if (_dir.name != _dir.casefolded_name)
-                    require_sorting = true;
             });
     }
 
-    public bool need_sorting (bool case_sensitive)
-        requires (require_sorting != null)
-        requires (last_sort != null)
+    public bool need_sorting (SortingOptions sorting_options)
     {
-        return ((!) require_sorting) && (case_sensitive != (!) last_sort);
+        return !sorting_options.is_key_model_sorted (key_model);
     }
 
-    public void sort_key_model (bool case_sensitive)
+    public void sort_key_model (SortingOptions sorting_options)
     {
-        if (require_sorting != null && !need_sorting (case_sensitive))
+        if (!need_sorting (sorting_options))
             return;
 
-        _sort_key_model (key_model, case_sensitive);
-        last_sort = case_sensitive;
-    }
-    private static void _sort_key_model (GLib.ListStore model, bool case_sensitive)
-    {
-        if (case_sensitive)
-            model.sort ((a, b) => { return strcmp (((SettingObject) a).name, ((SettingObject) b).name); });
-        else
-            model.sort ((a, b) => { return ((SettingObject) a).casefolded_name.collate (((SettingObject) 
b).casefolded_name); });
+        sorting_options.sort_key_model (key_model);
     }
 
     /*\
@@ -781,3 +765,137 @@ public class SettingsModel : Object, Gtk.TreeModel
         get_directory(iter).unref();
     }
 }
+
+/*\
+* * Sorting
+\*/
+
+public enum MergeType {
+    MIXED,
+    FIRST,
+    LAST
+}
+
+public class SortingOptions : Object
+{
+    public bool case_sensitive { get; set; default = false; }
+    public MergeType sort_folders { get; set; default = MergeType.MIXED; }
+
+    private SettingComparator get_comparator ()
+    {
+        if (sort_folders == MergeType.FIRST)
+        {
+            if (case_sensitive)
+                return new FoldersFirstCaseSensitive ();
+            else
+                return new FoldersFirstCaseInsensitive ();
+        }
+        else if (sort_folders == MergeType.LAST)
+        {
+            if (case_sensitive)
+                return new FoldersLastCaseSensitive ();
+            else
+                return new FoldersLastCaseInsensitive ();
+        }
+        else // if (sort_folders == MergeType.MIXED)
+        {
+            if (case_sensitive)
+                return new FoldersMixedCaseSensitive ();
+            else
+                return new FoldersMixedCaseInsensitive ();
+        }
+    }
+
+    public void sort_key_model (GLib.ListStore model)
+    {
+        SettingComparator comparator = get_comparator ();
+
+        model.sort ((a, b) => comparator.compare ((SettingObject) a, (SettingObject) b));
+    }
+
+    public bool is_key_model_sorted (GLib.ListStore model)
+    {
+        SettingComparator comparator = get_comparator ();
+
+        uint last = model.get_n_items () - 1;
+        for (int i = 0; i < last; i++)
+        {
+            SettingObject item = (SettingObject) model.get_item (i);
+            SettingObject next = (SettingObject) model.get_item (i + 1);
+            if (comparator.compare (item, next) > 0)
+                return false;
+        }
+        return true;
+    }
+}
+
+/* Comparison functions */
+
+interface SettingComparator : Object
+{
+    public abstract int compare (SettingObject a, SettingObject b);
+}
+
+class FoldersMixedCaseInsensitive : Object, SettingComparator
+{
+    public int compare (SettingObject a, SettingObject b)
+    {
+        return a.casefolded_name.collate (b.casefolded_name);
+    }
+}
+
+class FoldersMixedCaseSensitive : Object, SettingComparator
+{
+    public int compare (SettingObject a, SettingObject b)
+    {
+        return strcmp (a.name, b.name);
+    }
+}
+
+class FoldersFirstCaseInsensitive : Object, SettingComparator
+{
+    public int compare (SettingObject a, SettingObject b)
+    {
+        if (a is Directory && !(b is Directory))
+            return -1;
+        if (!(a is Directory) && b is Directory)
+            return 1;
+        return a.casefolded_name.collate (b.casefolded_name);
+    }
+}
+
+class FoldersFirstCaseSensitive : Object, SettingComparator
+{
+    public int compare (SettingObject a, SettingObject b)
+    {
+        if (a is Directory && !(b is Directory))
+            return -1;
+        if (!(a is Directory) && b is Directory)
+            return 1;
+        return strcmp (a.name, b.name);
+    }
+}
+
+class FoldersLastCaseInsensitive : Object, SettingComparator
+{
+    public int compare (SettingObject a, SettingObject b)
+    {
+        if (a is Directory && !(b is Directory))
+            return 1;
+        if (!(a is Directory) && b is Directory)
+            return -1;
+        return a.casefolded_name.collate (b.casefolded_name);
+    }
+}
+
+class FoldersLastCaseSensitive : Object, SettingComparator
+{
+    public int compare (SettingObject a, SettingObject b)
+    {
+        if (a is Directory && !(b is Directory))
+            return 1;
+        if (!(a is Directory) && b is Directory)
+            return -1;
+        return strcmp (a.name, b.name);
+    }
+}
diff --git a/editor/registry-view.vala b/editor/registry-view.vala
index d180cde..7d5906e 100644
--- a/editor/registry-view.vala
+++ b/editor/registry-view.vala
@@ -38,6 +38,7 @@ class RegistryView : Grid, PathElement
 
     [GtkChild] private ListBox key_list_box;
     private GLib.ListStore? key_model = null;
+    private SortingOptions sorting_options;
 
     private GLib.ListStore rows_possibly_with_popover = new GLib.ListStore (typeof (ClickableListBoxRow));
 
@@ -66,6 +67,10 @@ class RegistryView : Grid, PathElement
         bind_property ("show-search-bar", search_bar, "search-mode-enabled", BindingFlags.BIDIRECTIONAL);   
// TODO in UI file?
         bind_property ("behaviour", revealer, "behaviour", 
BindingFlags.BIDIRECTIONAL|BindingFlags.SYNC_CREATE);
 
+        sorting_options = new SortingOptions ();
+        application_settings.bind ("sort-case-sensitive", sorting_options, "case-sensitive", 
GLib.SettingsBindFlags.GET);
+        application_settings.bind ("sort-folders", sorting_options, "sort-folders", 
GLib.SettingsBindFlags.GET);
+
         destroy.connect (() => {
                 revealer.disconnect (revealer_reload_handler);
                 buffer.disconnect (search_entry_buffer_deleted_text_handler);
@@ -82,8 +87,8 @@ class RegistryView : Grid, PathElement
         current_path = (restore_view && path != "" && path [0] == '/') ? path : "/";
         path_requested (current_path, null, true);
 
-        application_settings.changed ["sort-case-sensitive"].connect (() => {
-                if (get_selected_directory ().need_sorting (application_settings.get_boolean 
("sort-case-sensitive")))
+        sorting_options.notify.connect (() => {
+                if (get_selected_directory ().need_sorting (sorting_options))
                     need_reload_warning_revealer.set_reveal_child (true);
             });
     }
@@ -98,7 +103,7 @@ class RegistryView : Grid, PathElement
         need_reload_warning_revealer.set_reveal_child (false);
         multiple_schemas_warning_revealer.set_reveal_child (multiple_schemas_warning_needed);
         update_current_path (path);
-        get_selected_directory ().sort_key_model (application_settings.get_boolean ("sort-case-sensitive"));
+        get_selected_directory ().sort_key_model (sorting_options);
         stack.set_visible_child_name ("browse-view");
         if (selected != null)
         {
@@ -169,7 +174,7 @@ class RegistryView : Grid, PathElement
     {
         search_next_button.set_sensitive (true);        // TODO better, or maybe just hide search_bar 1/2
         Directory dir = get_selected_directory ();
-        dir.sort_key_model (application_settings.get_boolean ("sort-case-sensitive"));
+        dir.sort_key_model (sorting_options);
         key_model = dir.key_model;
 
         multiple_schemas_warning_needed = dir.warning_multiple_schemas;
@@ -632,7 +637,7 @@ class RegistryView : Grid, PathElement
                 on_first_directory = false;
 
             /* Select next key that matches */
-            dir.sort_key_model (application_settings.get_boolean ("sort-case-sensitive"));
+            dir.sort_key_model (sorting_options);
             GLib.ListStore key_model = dir.key_model;
             while (position < key_model.get_n_items ())
             {


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