[gnome-boxes/wip/feborges/flowbox] icon-view: Port to GtkFlowBox



commit 42a8db53925a670b195f7f7818a4886a35e61e69
Author: Felipe Borges <felipeborges gnome org>
Date:   Fri Mar 24 13:06:36 2017 +0100

    icon-view: Port to GtkFlowBox
    
    This is NOT the final version of the port.
    
    I am glad about the similarities between the ListBox and the
    FlowBox API, to the point that I now realize that it would
    be more elegant to implement a generic View and leave the
    specific bits to the Boxes.IconView and Boxes.ListView
    classes only.
    
    Other than that, the Collection class could be a GListModel,
    which would apply the sorting and filtering independently
    from the views.
    
    All in all, with this patch you should be able to use all the
    IconView features as they were before with libgd's MainView.
    
    This is part of an initiative to move away from libgd in cases
    where we have a compatible widget in Gtk+.
    
    Stay tuned, in this same bat-repository, same bat-branch.

 data/gnome-boxes.gresource.xml |    1 +
 data/ui/icon-view-child.ui     |   95 ++++++++
 src/Makefile.am                |    1 +
 src/icon-view-child.vala       |   91 ++++++++
 src/icon-view.vala             |  487 +++++++++++++++-------------------------
 5 files changed, 368 insertions(+), 307 deletions(-)
---
diff --git a/data/gnome-boxes.gresource.xml b/data/gnome-boxes.gresource.xml
index d4c71bc..25cf0fa 100644
--- a/data/gnome-boxes.gresource.xml
+++ b/data/gnome-boxes.gresource.xml
@@ -13,6 +13,7 @@
     <file preprocess="xml-stripblanks">ui/display-toolbar.ui</file>
     <file preprocess="xml-stripblanks">ui/empty-boxes.ui</file>
     <file preprocess="xml-stripblanks">ui/editable-entry.ui</file>
+    <file preprocess="xml-stripblanks">ui/icon-view-child.ui</file>
     <file preprocess="xml-stripblanks">ui/resource-graph.ui</file>
     <file preprocess="xml-stripblanks">ui/kbd-shortcuts-window.ui</file>
     <file preprocess="xml-stripblanks">ui/list-view.ui</file>
diff --git a/data/ui/icon-view-child.ui b/data/ui/icon-view-child.ui
new file mode 100644
index 0000000..9328ccb
--- /dev/null
+++ b/data/ui/icon-view-child.ui
@@ -0,0 +1,95 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <!-- interface-requires gtk+ 3.9 -->
+  <template class="BoxesIconViewChild" parent="GtkBox">
+    <property name="visible">True</property>
+    <property name="orientation">vertical</property>
+
+    <child>
+      <object class="GtkOverlay" id="overlay">
+        <property name="visible">True</property>
+
+        <child type="overlay">
+          <object class="GtkBox">
+            <property name="visible">True</property>
+            <property name="valign">end</property>
+            <property name="margin">10</property>
+            <property name="margin-start">20</property>
+            <property name="margin-end">20</property>
+
+            <child>
+              <object class="GtkImage" id="favorite">
+                <property name="visible">True</property>
+                <property name="width-request">16</property>
+                <property name="height-request">16</property>
+              </object>
+            </child>
+
+            <child>
+              <object class="GtkCheckButton" id="selection_button">
+                <property name="visible">False</property>
+                <property name="can-focus">False</property>
+              </object>
+              <packing>
+                <property name="pack-type">end</property>
+              </packing>
+            </child>
+
+          </object>
+        </child>
+
+        <child>
+          <object class="GtkStack" id="stack">
+            <property name="visible">True</property>
+            <child>
+              <object class="GtkImage" id="thumbnail">
+                <property name="visible">True</property>
+              </object>
+
+              <packing>
+                <property name="name">thumbnail</property>
+              </packing>
+            </child>
+
+            <child>
+              <object class="GtkSpinner" id="spinner">
+                <property name="visible">True</property>
+                <property name="height-request">8</property>
+                <property name="width-request">8</property>
+                <property name="halign">0.5</property>
+                <property name="valign">0.5</property>
+                <style>
+                  <class name="slow-spinner"/>
+                </style>
+              </object>
+
+              <packing>
+                <property name="name">spinner</property>
+              </packing>
+            </child>
+          </object>
+        </child>
+
+      </object>
+    </child>
+
+    <child>
+      <object class="GtkLabel" id="machine_name">
+        <property name="visible">True</property>
+        <property name="width-request">192</property>
+        <property name="max-width-chars">128</property>
+        <property name="justify">left</property>
+        <property name="halign">start</property>
+        <property name="hexpand">True</property>
+        <property name="valign">center</property>
+        <property name="ellipsize">end</property>
+        <property name="margin-top">10</property>
+      </object>
+      <packing>
+        <property name="expand">False</property>
+        <property name="fill">True</property>
+      </packing>
+    </child>
+
+  </template>
+</interface>
diff --git a/src/Makefile.am b/src/Makefile.am
index 5323d52..c67e685 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -113,6 +113,7 @@ gnome_boxes_SOURCES =                               \
        i-properties-provider.vala              \
        i-collection-view.vala                  \
        icon-view.vala                          \
+       icon-view-child.vala                    \
        installer-media.vala                    \
        installed-media.vala                    \
        keys-input-popover.vala                 \
diff --git a/src/icon-view-child.vala b/src/icon-view-child.vala
new file mode 100644
index 0000000..473a2b1
--- /dev/null
+++ b/src/icon-view-child.vala
@@ -0,0 +1,91 @@
+// This file is part of GNOME Boxes. License: LGPLv2+
+
+using Gtk;
+
+[GtkTemplate (ui = "/org/gnome/Boxes/ui/icon-view-child.ui")]
+private class Boxes.IconViewChild : Gtk.Box {
+    public bool _selection_mode;
+    public bool selection_mode {
+        get { return _selection_mode; }
+        set {
+            _selection_mode = value;
+
+            selection_button.visible = _selection_mode;
+
+            if (!_selection_mode)
+                selected = false;
+        }
+        default = false ;
+    }
+
+    public bool _selected;
+    public bool selected {
+        get { return _selected; }
+        set { _selected = selection_mode && value; }
+        default = false;
+    }
+
+    public CollectionItem item { get; private set; }
+    private Machine machine {
+        get { return item as Machine; }
+    }
+
+    [GtkChild]
+    private Gtk.CheckButton selection_button;
+    [GtkChild]
+    private Gtk.Stack stack;
+    [GtkChild]
+    private Gtk.Image thumbnail;
+    [GtkChild]
+    private Gtk.Spinner spinner;
+    [GtkChild]
+    private Gtk.Image favorite;
+    [GtkChild]
+    private Gtk.Label machine_name;
+
+    private Boxes.MachineThumbnailer thumbnailer;
+
+    private Binding selected_binding;
+
+
+    public IconViewChild (CollectionItem item) {
+        this.item = item;
+
+        thumbnailer = machine.thumbnailer;
+
+        thumbnailer.favorite_emblem_enabled = false;
+        thumbnailer.notify["thumbnail"].connect (() => {
+            thumbnail.set_from_pixbuf (thumbnailer.thumbnail);
+        });
+        thumbnail.set_from_pixbuf (thumbnailer.thumbnail);
+
+        selected_binding = bind_property ("selected", selection_button, "active", 
BindingFlags.BIDIRECTIONAL);
+
+        machine.config.notify["categories"].connect (update_favorite);
+        machine.notify["under-construction"].connect (update_thumbnail);
+
+        update_thumbnail ();
+        update_favorite ();
+
+        machine.bind_property ("name", machine_name, "label", BindingFlags.SYNC_CREATE);
+    }
+
+    private void update_thumbnail () {
+        if (machine.under_construction) {
+            stack.visible_child = spinner;
+            spinner.start ();
+            spinner.visible = true;
+        }
+        else {
+            stack.visible_child = thumbnail;
+            spinner.stop ();
+        }
+    }
+
+    private void update_favorite () {
+        if ("favorite" in machine.config.categories)
+            favorite.set_from_icon_name ("starred-symbolic", Gtk.IconSize.MENU);
+        else
+            favorite.clear ();
+    }
+}
\ No newline at end of file
diff --git a/src/icon-view.vala b/src/icon-view.vala
index 4fb98d9..c60b32c 100644
--- a/src/icon-view.vala
+++ b/src/icon-view.vala
@@ -6,11 +6,18 @@ public enum Boxes.SelectionCriteria {
     RUNNING
 }
 
-private class Boxes.IconView: Gd.MainView, Boxes.ICollectionView, Boxes.UI {
+private class Boxes.IconView: Gtk.ScrolledWindow, Boxes.ICollectionView, Boxes.UI {
     public UIState previous_ui_state { get; protected set; }
     public UIState ui_state { get; protected set; }
 
+    public CollectionFilter filter { get; protected set; }
+
+    private Gtk.FlowBox flowbox;
+
+    private GLib.List<CollectionItem> hidden_items;
+
     private AppWindow window;
+    private Boxes.ActionsPopover context_popover;
 
     private Category _category;
     public Category category {
@@ -21,101 +28,33 @@ private class Boxes.IconView: Gd.MainView, Boxes.ICollectionView, Boxes.UI {
         }
     }
 
-    private enum ModelColumns {
-        SCREENSHOT = Gd.MainColumns.ICON,
-        TITLE = Gd.MainColumns.PRIMARY_TEXT,
-        INFO = Gd.MainColumns.SECONDARY_TEXT,
-        SELECTED = Gd.MainColumns.SELECTED,
-        PULSE = Gd.MainColumns.PULSE,
-        ITEM = Gd.MainColumns.LAST,
-
-        LAST
-    }
-
-    private Gtk.ListStore store;
-    private Gtk.TreeModelFilter model_filter;
-    private Boxes.ActionsPopover context_popover;
-    private GLib.List<CollectionItem> hidden_items;
-
-    public CollectionFilter filter { get; protected set; }
-
-    private const Gtk.CornerType[] right_corners = { Gtk.CornerType.TOP_RIGHT, Gtk.CornerType.BOTTOM_RIGHT };
-    private const Gtk.CornerType[] bottom_corners = { Gtk.CornerType.BOTTOM_LEFT, 
Gtk.CornerType.BOTTOM_RIGHT };
-
-    public const Gdk.RGBA FRAME_BORDER_COLOR = { 0x3b / 255.0, 0x3c / 255.0, 0x38 / 255.0, 1.0 };
-    public const Gdk.RGBA FRAME_BACKGROUND_COLOR = { 0x2d / 255.0, 0x2d / 255.0, 0x2d / 255.0, 1.0 };
-    public const double FRAME_RADIUS = 2.0;
-
-    public const Gdk.RGBA CENTERED_EMBLEM_COLOR = { 0xbe / 255.0, 0xbe / 255.0, 0xbe / 255.0, 1.0 };
-    public const Gdk.RGBA SMALL_EMBLEM_COLOR = { 1.0, 1.0, 1.0, 1.0 };
-    public const int BIG_EMBLEM_SIZE = 32;
-    public const int SMALL_EMBLEM_SIZE = 16;
-    public const int EMBLEM_PADDING = 8;
-
     construct {
         category = new Category (_("New and Recent"), Category.Kind.NEW);
         hidden_items = new GLib.List<CollectionItem> ();
+
+        setup_flowbox ();
+
         filter = new CollectionFilter ();
-        setup_view ();
-        notify["ui-state"].connect (ui_state_changed);
         filter.notify["text"].connect (() => {
-            model_filter.refilter ();
+            flowbox.invalidate_filter ();
         });
         filter.filter_func_changed.connect (() => {
-            model_filter.refilter ();
+            flowbox.invalidate_filter ();
         });
+
+        notify["ui-state"].connect (ui_state_changed);
     }
 
     public void setup_ui (AppWindow window) {
         this.window = window;
 
         window.notify["selection-mode"].connect (() => {
-            set_selection_mode (window.selection_mode);
-            if (!window.selection_mode)
-                unselect_all (); // Reset selection on exiting selection mode
+            flowbox.selection_mode = window.selection_mode ? Gtk.SelectionMode.MULTIPLE :
+                                                             Gtk.SelectionMode.NONE;
+            update_selection_mode ();
         });
 
-        var icon_view = get_generic_view () as Gtk.IconView;
-        icon_view.button_release_event.connect (on_button_press_event);
-        icon_view.key_press_event.connect (on_key_press_event);
         context_popover = new Boxes.ActionsPopover (window);
-        context_popover.relative_to = icon_view;
-    }
-
-    private void ui_state_changed () {
-        if (ui_state == UIState.COLLECTION)
-            unselect_all ();
-    }
-
-    private void update_screenshot (Gtk.TreeIter iter) {
-        CollectionItem item;
-
-        store.get (iter, ModelColumns.ITEM, out item);
-        Machine machine = item as Machine;
-        return_if_fail (machine != null);
-        var pixbuf = machine.thumbnailer.thumbnail;
-
-        store.set (iter, ModelColumns.SCREENSHOT, pixbuf);
-        queue_draw ();
-    }
-
-    private Gtk.TreeIter append (string title,
-                                 string? info,
-                                 CollectionItem item) {
-        Gtk.TreeIter iter;
-
-        store.append (out iter);
-        store.set (iter, Gd.MainColumns.ID, "%p".printf (item));
-        store.set (iter, ModelColumns.TITLE, title);
-        if (info != null)
-            store.set (iter, ModelColumns.INFO, info);
-        store.set (iter, ModelColumns.SELECTED, false);
-        store.set (iter, ModelColumns.ITEM, item);
-        update_screenshot (iter);
-
-        item.set_data<Gtk.TreeIter?> ("iter", iter);
-
-        return iter;
     }
 
     public void add_item (CollectionItem item) {
@@ -147,303 +86,237 @@ private class Boxes.IconView: Gd.MainView, Boxes.ICollectionView, Boxes.UI {
             return;
         }
 
-        var info = machine.status?? machine.info;
-        var iter = append (machine.name, info,  item);
-        var thumbnail_id = machine.thumbnailer.notify["thumbnail"].connect (() => {
-            // apparently iter is stable after insertion/removal/sort
-            update_screenshot (iter);
-        });
-        item.set_data<ulong> ("thumbnail_id", thumbnail_id);
+        add_child (item);
+    }
 
-        var categories_id = machine.config.notify["categories"].connect (() => {
-            // apparently iter is stable after insertion/removal/sort
-            update_screenshot (iter);
-        });
-        item.set_data<ulong> ("categories_id", categories_id);
+    private void add_child (CollectionItem item) {
+        var child = new Gtk.FlowBoxChild ();
+        child.halign = Gtk.Align.START;
+        var box = new IconViewChild (item);
 
-        var name_id = item.notify["name"].connect (() => {
-            // apparently iter is stable after insertion/removal/sort
-            store.set (iter, ModelColumns.TITLE, item.name);
-            queue_draw ();
-        });
-        item.set_data<ulong> ("name_id", name_id);
-
-        var info_id = machine.notify["info"].connect (on_machine_info_changed);
-        item.set_data<ulong> ("info_id", info_id);
-        var status_id = machine.notify["status"].connect (on_machine_info_changed);
-        item.set_data<ulong> ("status_id", status_id);
-
-        setup_activity (iter, machine);
-        var under_construct_id = machine.notify["under-construction"].connect (() => {
-            // apparently iter is stable after insertion/removal/sort
-            setup_activity (iter, machine);
-            queue_draw ();
+        box.notify["selected"].connect (() => {
+            propagate_view_child_selection (child);
         });
-        item.set_data<ulong> ("under_construct_id", under_construct_id);
-        var machine_state_id = machine.notify["state"].connect (() => {
-            // apparently iter is stable after insertion/removal/sort
-            setup_activity (iter, machine);
-            queue_draw ();
-        });
-        item.set_data<ulong> ("machine_state_id", machine_state_id);
 
-        item.set_state (window.ui_state);
-    }
+        box.visible = true;
+        child.visible = true;
 
-    public List<CollectionItem> get_selected_items () {
-        var selected = new List<CollectionItem> ();
+        child.add (box);
+        flowbox.add (child);
+    }
 
-        store.foreach ((store, path, iter) => {
-            CollectionItem item;
-            bool is_selected;
+    public void remove_item (CollectionItem item) {
+        hidden_items.remove (item);
+        remove_child (item);
+    }
 
-            store.get (iter,
-                       ModelColumns.SELECTED, out is_selected,
-                       ModelColumns.ITEM, out item);
-            if (is_selected && item != null)
-                selected.append (item);
+    private void remove_child (CollectionItem item) {
+        foreach_child ((box_child) => {
+            var view_child = box_child.get_child () as IconViewChild;
+            if (view_child == null)
+                return;
 
-            return false;
+            if (view_child.item == item) {
+                flowbox.remove (view_child);
+            }
         });
-
-        return (owned) selected;
     }
 
-    public void remove_item (CollectionItem item) {
-        hidden_items.remove (item);
+    public void select_by_criteria (SelectionCriteria criteria) {
+        window.selection_mode = true;
 
-        var iter = item.get_data<Gtk.TreeIter?> ("iter");
-        if (iter == null) {
-            debug ("item not in view or already removed");
-            return;
-        }
+        switch (criteria) {
+        default:
+        case SelectionCriteria.ALL:
+            foreach_child ((box_child) => { select_child (box_child); });
+
+            break;
+        case SelectionCriteria.NONE:
+            foreach_child ((box_child) => { unselect_child (box_child); });
+
+            break;
+        case SelectionCriteria.RUNNING:
+            foreach_child ((box_child) => {
+                var item = get_item_for_child (box_child);
+                if (item != null && item is Machine && (item as Machine).is_running)
+                    select_child (box_child);
+                else
+                    unselect_child (box_child);
+            });
 
-#if VALA_0_36
-        store.remove (ref iter);
-#else
-        store.remove (iter);
-#endif
-        item.set_data<Gtk.TreeIter?> ("iter", null);
-
-        var name_id = item.get_data<ulong> ("name_id");
-        item.disconnect (name_id);
-        var status_id = item.get_data<ulong> ("status_id");
-        item.disconnect (status_id);
-        var info_id = item.get_data<ulong> ("info_id");
-        item.disconnect (info_id);
-        var under_construct_id = item.get_data<ulong> ("under_construct_id");
-        item.disconnect (under_construct_id);
-        var machine_state_id = item.get_data<ulong> ("machine_state_id");
-        if (machine_state_id != 0)
-            item.disconnect (machine_state_id);
-
-        if (item as Machine != null) {
-            var machine = item as Machine;
-            var categories_id = item.get_data<ulong> ("categories_id");
-            machine.config.disconnect (categories_id);
-
-            var thumbnail_id = item.get_data<ulong> ("thumbnail_id");
-            machine.thumbnailer.disconnect (thumbnail_id);
+            break;
         }
+
+        App.app.notify_property ("selected-items");
     }
 
-    private CollectionItem get_item_for_iter (Gtk.TreeIter iter) {
-        GLib.Value value;
+    public List<CollectionItem> get_selected_items () {
+        var selected = new List<CollectionItem> ();
 
-        store.get_value (iter, ModelColumns.ITEM, out value);
+        foreach (var box_child in flowbox.get_selected_children ()) {
+            var item = get_item_for_child (box_child);
+            selected.append (item);
+        }
 
-        return (CollectionItem) value;
+        return (owned) selected;
     }
 
+    public void activate_first_item () {
+        Gtk.FlowBoxChild first_child = null;
+        foreach_child ((box_child) => {
+            if (first_child == null)
+                first_child = box_child;
+        });
 
-    private CollectionItem get_item_for_path (Gtk.TreePath path) {
-        Gtk.TreeIter filter_iter, iter;
-
-        model_filter.get_iter (out filter_iter, path);
-        model_filter.convert_iter_to_child_iter (out iter, filter_iter);
-
-        return get_item_for_iter (iter);
+        if (first_child == null)
+            flowbox.child_activated (first_child);
     }
 
-    private bool model_visible (Gtk.TreeModel model, Gtk.TreeIter iter) {
-        var item = get_item_for_iter (iter);
-        if (item  == null)
-            return false;
+    private void setup_flowbox () {
+        flowbox = new Gtk.FlowBox ();
 
-        return filter.filter (item);
-    }
+        flowbox.selection_mode = Gtk.SelectionMode.NONE;
+        flowbox.valign = Gtk.Align.START;
+        flowbox.border_width = 12;
+        flowbox.column_spacing = 20;
+        flowbox.row_spacing = 6;
+        add (flowbox);
+        flowbox.show ();
 
-    public void activate_first_item () {
-        if (model_filter.iter_n_children (null) == 1) {
-            Gtk.TreePath path = new Gtk.TreePath.from_string ("0");
-            (get_generic_view () as Gtk.IconView).item_activated (path);
-        }
-    }
+        flowbox.button_release_event.connect (on_button_press_event);
+        flowbox.key_press_event.connect (on_key_press_event);
 
-    private void setup_view () {
-        store = new Gtk.ListStore (ModelColumns.LAST,
-                                   typeof (string),
-                                   typeof (string),
-                                   typeof (string),
-                                   typeof (string),
-                                   typeof (Gdk.Pixbuf),
-                                   typeof (long),
-                                   typeof (bool),
-                                   typeof (uint),
-                                   typeof (CollectionItem));
-        store.set_default_sort_func ((store, a, b) => {
-            CollectionItem item_a, item_b;
-
-            store.get (a, ModelColumns.ITEM, out item_a);
-            store.get (b, ModelColumns.ITEM, out item_b);
-
-            if (item_a == null || item_b == null) // FIXME?!
-                return 0;
-
-            return item_a.compare (item_b);
-        });
-        store.set_sort_column_id (Gtk.SortColumn.DEFAULT, Gtk.SortType.ASCENDING);
-        store.row_deleted.connect (() => {
-            App.app.notify_property ("selected-items");
-        });
-        store.row_inserted.connect (() => {
-            App.app.notify_property ("selected-items");
-        });
+        flowbox.set_filter_func (model_filter);
+        flowbox.set_sort_func (model_sort);
 
-        model_filter = new Gtk.TreeModelFilter (store, null);
-        model_filter.set_visible_func (model_visible);
+        flowbox.child_activated.connect ((child) => {
+            if (window.selection_mode)
+                return;
 
-        set_view_type (Gd.MainViewType.ICON);
-        set_model (model_filter);
-        item_activated.connect ((view, id, path) => {
-            var item = get_item_for_path (path);
+            var item = get_item_for_child (child);
             if (item is LibvirtMachine && (item as LibvirtMachine).importing)
                 return;
+
             window.select_item (item);
         });
-        view_selection_changed.connect (() => {
-            queue_draw ();
-            App.app.notify_property ("selected-items");
-        });
 
-        show_all ();
+        update_selection_mode ();
     }
 
-    public void select_by_criteria (SelectionCriteria criteria) {
-        window.selection_mode = true;
+    private CollectionItem? get_item_for_child (Gtk.FlowBoxChild child) {
+        var view = child.get_child () as IconViewChild;
+        if (view == null)
+            return null;
 
-        model_filter.foreach ( (filter_model, filter_path, filter_iter) => {
-            Gtk.TreeIter iter;
-            model_filter.convert_iter_to_child_iter (out iter, filter_iter);
-            bool selected;
-            switch (criteria) {
-            default:
-            case SelectionCriteria.ALL:
-                selected = true;
-                break;
-            case SelectionCriteria.NONE:
-                selected = false;
-                break;
-            case SelectionCriteria.RUNNING:
-                CollectionItem item;
-                store.get (iter, ModelColumns.ITEM, out item);
-                selected = item != null && item is Machine &&
-                    (item as Machine).is_running;
-                break;
-            }
-            store.set (iter, ModelColumns.SELECTED, selected);
-            return false;
-        });
-        queue_draw ();
-        App.app.notify_property ("selected-items");
+        return view.item;
     }
 
-    private void setup_activity (Gtk.TreeIter iter, Machine machine) {
-        var activity_timeout = machine.get_data<uint> ("activity_timeout");
-        if (activity_timeout > 0) {
-            Source.remove (activity_timeout);
-            machine.set_data<uint> ("activity_timeout", 0);
-        }
+    private void foreach_child (Func<Gtk.FlowBoxChild> func) {
+        flowbox.forall ((child) => {
+            var view_child = child as Gtk.FlowBoxChild;
+            if (view_child == null)
+                return;
 
-        if (!machine.under_construction) {
-            store.set (iter, ModelColumns.PULSE, 0);
-            var machine_state_id = machine.get_data<ulong> ("machine_state_id");
-            if (machine_state_id != 0) {
-                machine.disconnect (machine_state_id);
-                machine.set_data<ulong> ("machine_state_id", 0);
-            }
+            func (view_child);
+        });
+    }
 
-            return;
-        }
+    private int model_sort (Gtk.FlowBoxChild child1, Gtk.FlowBoxChild child2) {
+        var item1 = get_item_for_child (child1);
+        var item2 = get_item_for_child (child2);
 
-        var pulse = 1;
-        store.set (iter, ModelColumns.PULSE, pulse++);
+        if (item1 == null || item2 == null)
+            return 0;
 
-        if (machine.state == Machine.MachineState.SAVED)
-            return;
+        return item1.compare (item2);
+    }
 
-        activity_timeout = Timeout.add (150, () => {
-            var machine_iter = machine.get_data<Gtk.TreeIter?> ("iter");
-            if (machine_iter == null)
-                return false; // Item removed
+    private bool model_filter (Gtk.FlowBoxChild child) {
+        if (child  == null)
+            return false;
 
-            store.set (machine_iter, ModelColumns.PULSE, pulse++);
-            queue_draw ();
+        var item = get_item_for_child (child);
+        if (item  == null)
+            return false;
 
-            return true;
-        });
-        machine.set_data<uint> ("activity_timeout", activity_timeout);
+        return filter.filter (item as CollectionItem);
+    }
+
+    private void ui_state_changed () {
+        if (ui_state == UIState.COLLECTION)
+            flowbox.unselect_all ();
     }
 
     private bool on_button_press_event (Gdk.EventButton event) {
         if (event.type != Gdk.EventType.BUTTON_RELEASE || event.button != 3)
             return false;
 
-        var generic_view = get_generic_view () as Gd.MainViewGeneric;
-        var path = generic_view.get_path_at_pos ((int) event.x, (int) event.y);
-        if (path == null)
-            return false;
+        var child = flowbox.get_child_at_pos ((int) event.x, (int) event.y);
 
-        return launch_context_popover_for_path (path);
+        return launch_context_popover_for_child (child);
     }
 
     private bool on_key_press_event (Gdk.EventKey event) {
         if (event.keyval != Gdk.Key.Menu)
             return false;
 
-        var icon_view = get_generic_view () as Gtk.IconView;
-        Gtk.TreePath path;
-        Gtk.CellRenderer cell;
-        if (!icon_view.get_cursor (out path, out cell))
+        var child = flowbox.get_selected_children ().nth_data (0);
+        if (child == null)
             return false;
 
-        return launch_context_popover_for_path (path);
+        return launch_context_popover_for_child (child);
     }
 
-    private bool launch_context_popover_for_path (Gtk.TreePath path) {
-        var item = get_item_for_path (path);
+    private bool launch_context_popover_for_child (Gtk.FlowBoxChild child) {
+        var item = get_item_for_child (child);
         if (item == null)
             return false;
 
-        var icon_view = get_generic_view () as Gtk.IconView;
-        Gdk.Rectangle rect;
-        icon_view.get_cell_rect (path, null, out rect);
-
-        context_popover.update_for_item (item);
-        rect.height /= 2; // Show in the middle
-        context_popover.pointing_to = rect;
+        context_popover.update_for_item (get_item_for_child (child));
+        context_popover.set_relative_to (child); // FIXME: maybe more to the centre
         context_popover.show ();
 
         return true;
     }
 
-    private void on_machine_info_changed (GLib.Object object, GLib.ParamSpec spec) {
-        var machine = object as Machine;
-        var iter = machine.get_data<Gtk.TreeIter?> ("iter");
-        return_if_fail (iter != null);
+    private void update_selection_mode () {
+        foreach_child ((child) => {
+            var view_child = child.get_child () as Boxes.IconViewChild;
+
+            if (view_child.selection_mode != window.selection_mode)
+                view_child.selection_mode = window.selection_mode;
+
+            unselect_child (child);
+        });
+    }
+
+    private void propagate_view_child_selection (Gtk.FlowBoxChild child) {
+        var view_child = child.get_child () as IconViewChild;
 
-        var info = machine.status?? machine.info;
-        store.set (iter, ModelColumns.INFO, info);
-        queue_draw ();
+        if (view_child.selected)
+            select_child (child);
+        else
+            unselect_child (child);
     }
-}
+
+    private void select_child (Gtk.FlowBoxChild child) {
+        var view_child = child.get_child () as IconViewChild;
+
+        flowbox.select_child (child);
+        if (!view_child.selected)
+            view_child.selected = true;
+
+        App.app.notify_property ("selected-items");
+    }
+
+
+    private void unselect_child (Gtk.FlowBoxChild child) {
+        var view_child = child.get_child () as IconViewChild;
+
+        flowbox.unselect_child (child);
+        if (view_child.selected)
+            view_child.selected = false;
+
+        App.app.notify_property ("selected-items");
+    }
+}
\ No newline at end of file


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