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



commit c01d4c91fb303c9292339dc8c430bd5cf809aa9e
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             |  478 +++++++++++++++-------------------------
 src/meson.build                |    1 +
 6 files changed, 366 insertions(+), 301 deletions(-)
---
diff --git a/data/gnome-boxes.gresource.xml b/data/gnome-boxes.gresource.xml
index 12c462b..985e4f0 100644
--- a/data/gnome-boxes.gresource.xml
+++ b/data/gnome-boxes.gresource.xml
@@ -14,6 +14,7 @@
     <file preprocess="xml-stripblanks">ui/display-toolbar.ui</file>
     <file preprocess="xml-stripblanks">ui/editable-entry.ui</file>
     <file preprocess="xml-stripblanks">ui/empty-boxes.ui</file>
+    <file preprocess="xml-stripblanks">ui/icon-view-child.ui</file>
     <file preprocess="xml-stripblanks">ui/kbd-shortcuts-window.ui</file>
     <file preprocess="xml-stripblanks">ui/list-view.ui</file>
     <file preprocess="xml-stripblanks">ui/list-view-row.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 129e1a2..1f46272 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -114,6 +114,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 1e8494b..5c62875 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,299 +86,236 @@ 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);
+            });
 
-        store.remove (ref iter);
-        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.set_relative_to (child);
         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;
 
-        var info = machine.status?? machine.info;
-        store.set (iter, ModelColumns.INFO, info);
-        queue_draw ();
+            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;
+
+        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");
     }
 }
diff --git a/src/meson.build b/src/meson.build
index 97c9287..765a969 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -28,6 +28,7 @@ vala_sources = [
   'libvirt-cloned-media.vala',
   'libvirt-media.vala',
   'iso-extractor.vala',
+  'icon-view-child.vala',
   'libvirt-broker.vala',
   'libvirt-machine.vala',
   'libvirt-machine-properties.vala',


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