[seahorse/wip/nielsdg/seahorse-listview: 2/3] Use a GtkListBox instead of a GtkTreeView
- From: Niels De Graef <nielsdg src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [seahorse/wip/nielsdg/seahorse-listview: 2/3] Use a GtkListBox instead of a GtkTreeView
- Date: Wed, 11 Dec 2019 07:06:20 +0000 (UTC)
commit 0b69465aec7c39004cc28fde456e2359617480c5
Author: Niels De Graef <nielsdegraef gmail com>
Date: Sun Sep 8 12:03:36 2019 +0200
Use a GtkListBox instead of a GtkTreeView
common/catalog.vala | 1 -
common/key-manager-store.vala | 496 ---------------------
common/meson.build | 1 -
gkr/gkr-backend.vala | 30 +-
libseahorse/seahorse.css | 18 +
src/key-manager-item-row.vala | 1 +
src/key-manager.vala | 15 +-
src/sidebar.vala | 977 +++++++++++++-----------------------------
8 files changed, 327 insertions(+), 1212 deletions(-)
---
diff --git a/common/catalog.vala b/common/catalog.vala
index f8f9a911..2cff02e5 100644
--- a/common/catalog.vala
+++ b/common/catalog.vala
@@ -30,7 +30,6 @@ public abstract class Catalog : Gtk.ApplicationWindow {
private GLib.Settings _settings;
public abstract GLib.List<weak Backend> get_backends();
- public abstract Place? get_focused_place();
public abstract GLib.List<GLib.Object> get_selected_objects();
private const ActionEntry[] ACTION_ENTRIES = {
diff --git a/common/meson.build b/common/meson.build
index 71b80cc3..9a2b622c 100644
--- a/common/meson.build
+++ b/common/meson.build
@@ -10,7 +10,6 @@ common_sources = [
'exportable.vala',
'exporter.vala',
'icons.vala',
- 'key-manager-store.vala',
'interaction.vala',
'item-list.vala',
'lockable.vala',
diff --git a/gkr/gkr-backend.vala b/gkr/gkr-backend.vala
index 44bf065e..ad70bf6c 100644
--- a/gkr/gkr-backend.vala
+++ b/gkr/gkr-backend.vala
@@ -115,18 +115,20 @@ public class Backend: GLib.Object , Gcr.Collection, Seahorse.Backend {
if (this._aliases.lookup("session") == object_path)
continue;
- seen.add(object_path);
- if (this._keyrings.lookup(object_path) == null) {
- this._keyrings.insert(object_path, (Keyring)keyring);
+ var uri = "secret-service://%s".printf(object_path);
+ seen.add(uri);
+ if (this._keyrings.lookup(uri) == null) {
+ this._keyrings.insert(uri, (Keyring)keyring);
emit_added(keyring);
}
}
/* Remove any that we didn't find */
var iter = GLib.HashTableIter<string, Keyring>(this._keyrings);
- while (iter.next(out object_path, null)) {
- if (!seen.contains(object_path)) {
- var keyring = this._keyrings.lookup(object_path);
+ string uri;
+ while (iter.next(out uri, null)) {
+ if (!seen.contains(uri)) {
+ var keyring = this._keyrings.lookup(uri);
iter.remove();
emit_removed(keyring);
}
@@ -146,13 +148,13 @@ public class Backend: GLib.Object , Gcr.Collection, Seahorse.Backend {
return get_keyrings();
}
- public bool contains(GLib.Object object) {
- if (object is Keyring) {
- var keyring = (Keyring)object;
- return this._keyrings.lookup(keyring.uri) == keyring;
- }
- return false;
- }
+ public bool contains(GLib.Object object) {
+ var keyring = object as Gkr.Keyring;
+ if (keyring == null)
+ return false;
+
+ return this._keyrings.lookup(keyring.uri) == keyring;
+ }
public Place? lookup_place(string uri) {
return this._keyrings.lookup(uri);
@@ -169,7 +171,7 @@ public class Backend: GLib.Object , Gcr.Collection, Seahorse.Backend {
return Backend._instance;
}
- public GLib.List<weak Keyring> get_keyrings() {
+ public GLib.List<unowned Keyring> get_keyrings() {
return this._keyrings.get_values();
}
diff --git a/libseahorse/seahorse.css b/libseahorse/seahorse.css
index b77eaaad..e0dfcbc7 100644
--- a/libseahorse/seahorse.css
+++ b/libseahorse/seahorse.css
@@ -5,6 +5,24 @@
border-radius: 0;
}
+.seahorse-sidebar-item-header {
+ margin-top: 6px;
+ font-weight: bold;
+}
+
+.seahorse-sidebar-item {
+ margin: 0 3px 0 12px;
+}
+
+.seahorse-sidebar-item > label {
+ margin: 3px 0 3px 0;
+}
+
+.seahorse-sidebar-item > button {
+ padding: 0;
+ margin: 0;
+}
+
.seahorse-item-listbox {
border: 1px solid @borders;
min-width: 400px;
diff --git a/src/key-manager-item-row.vala b/src/key-manager-item-row.vala
index df51825b..0fdf09ac 100644
--- a/src/key-manager-item-row.vala
+++ b/src/key-manager-item-row.vala
@@ -55,6 +55,7 @@ public class Seahorse.KeyManagerItemRow : Gtk.ListBoxRow {
object.get("description", out description);
var description_label = new Gtk.Label(description);
description_label.xalign = 1.0f;
+ description_label.valign = Gtk.Align.START;
description_label.get_style_context().add_class("seahorse-item-listbox-row-description");
grid.attach(description_label, 2, 0);
diff --git a/src/key-manager.vala b/src/key-manager.vala
index 5fb1c937..4097d953 100644
--- a/src/key-manager.vala
+++ b/src/key-manager.vala
@@ -183,6 +183,7 @@ public class Seahorse.KeyManager : Catalog {
private void check_empty_state() {
bool empty = (this.item_list.get_n_items() == 0);
+ debug("Checking empty state: %s", empty.to_string());
this.show_search_button.sensitive = !empty;
if (!empty) {
@@ -193,7 +194,7 @@ public class Seahorse.KeyManager : Catalog {
// We have an empty page, that might still have 2 reasons:
// - we really have no items in our collections
// - we're dealing with a locked keyring
- Place? place = get_focused_place();
+ Place? place = this.sidebar.get_focused_place();
if (place != null && place is Lockable && ((Lockable) place).unlockable) {
this.content_stack.visible_child_name = "locked_keyring_page";
return;
@@ -384,17 +385,13 @@ public class Seahorse.KeyManager : Catalog {
}
}
- public override Place? get_focused_place() {
- return this.sidebar.get_focused_place();
- }
-
private Gcr.Collection setup_sidebar() {
this.sidebar = new Sidebar();
sidebar.hexpand = true;
/* Make sure we update the empty state on any change */
- this.sidebar.get_selection().changed.connect((sel) => check_empty_state());
- this.sidebar.current_collection_changed.connect (() => check_empty_state ());
+ this.sidebar.selected_rows_changed.connect((sidebar) => { check_empty_state(); });
+ this.sidebar.current_collection_changed.connect((sidebar) => { check_empty_state (); });
this.sidebar_panes.position = this.settings.get_int("sidebar-width");
this.sidebar_panes.realize.connect(() => { this.sidebar_panes.position =
this.settings.get_int("sidebar-width"); });
@@ -411,7 +408,7 @@ public class Seahorse.KeyManager : Catalog {
this.settings.bind("keyrings-selected", this.sidebar, "selected-uris", SettingsBindFlags.DEFAULT);
- return this.sidebar.collection;
+ return this.sidebar.objects;
}
public override List<weak Backend> get_backends() {
@@ -426,7 +423,7 @@ public class Seahorse.KeyManager : Catalog {
[GtkCallback]
private void on_locked_keyring_unlock_button_clicked(Gtk.Button unlock_button) {
- Lockable? place = get_focused_place() as Lockable;
+ Lockable? place = this.sidebar.get_focused_place() as Lockable;
return_if_fail(place != null && place.unlockable);
unlock_button.sensitive = false;
diff --git a/src/sidebar.vala b/src/sidebar.vala
index f7a0b2f9..86561d99 100644
--- a/src/sidebar.vala
+++ b/src/sidebar.vala
@@ -18,39 +18,18 @@
* <http://www.gnu.org/licenses/>.
*/
-public class Seahorse.Sidebar : Gtk.TreeView {
+public class Seahorse.Sidebar : Gtk.ListBox {
- private const int ACTION_BUTTON_XPAD = 6;
-
- private Gtk.ListStore store = new Gtk.ListStore.newv(Column.types());
+ private GLib.ListStore store = new GLib.ListStore(typeof(Seahorse.Place));
private List<Backend> backends = new List<Backend>();
- private Gcr.UnionCollection objects = new Gcr.UnionCollection();
-
- // The selection
- private HashTable<Gcr.Collection, Gcr.Collection> selection
- = new HashTable<Gcr.Collection, Gcr.Collection>(direct_hash, direct_equal);
- private bool updating;
// A set of chosen uris, used with settings
private GenericSet<string?> chosen = new GenericSet<string?>(str_hash, str_equal);
- // Action icons
- private Gdk.Pixbuf? pixbuf_lock;
- private Gdk.Pixbuf? pixbuf_unlock;
- private Gdk.Pixbuf? pixbuf_lock_l;
- private Gdk.Pixbuf? pixbuf_unlock_l;
- private Gtk.TreePath? action_highlight_path;
- private Gtk.CellRendererPixbuf action_cell_renderer;
- private int action_button_size;
-
- private uint update_places_sig;
-
/**
* Collection of objects sidebar represents
*/
- public Gcr.Collection collection {
- get { return this.objects; }
- }
+ public Gcr.UnionCollection objects { get; private set; default = new Gcr.UnionCollection(); }
/**
* The URIs selected by the user
@@ -68,7 +47,7 @@ public class Seahorse.Sidebar : Gtk.TreeView {
set {
if (this._combined != value) {
this._combined = value;
- update_objects_in_collection(false);
+ update_objects();
}
}
}
@@ -80,98 +59,12 @@ public class Seahorse.Sidebar : Gtk.TreeView {
*/
public signal void current_collection_changed();
- private enum RowType {
- BACKEND,
- PLACE,
- }
-
- private enum Column {
- ROW_TYPE,
- ICON,
- LABEL,
- TOOLTIP,
- CATEGORY,
- COLLECTION,
- URI,
- N_COLUMNS;
-
- public static Type[] types() {
- return {
- typeof(uint),
- typeof(Icon),
- typeof(string),
- typeof(string),
- typeof(string),
- typeof(Gcr.Collection),
- typeof(string)
- };
- }
- }
-
public Sidebar() {
- /* get_style_context().set_junction_sides(Gtk.JunctionSides.RIGHT | Gtk.JunctionSides.LEFT); */
-
- // tree view
- Gtk.TreeViewColumn col = new Gtk.TreeViewColumn();
-
- // initial padding
- Gtk.CellRenderer cell = new Gtk.CellRendererText();
- col.pack_start(cell, false);
- cell.xpad = 6;
-
- // headings
- Gtk.CellRendererText headings_cell = new Gtk.CellRendererText();
- col.pack_start(headings_cell, false);
- col.set_attributes(headings_cell, "text", Column.LABEL, null);
- headings_cell.weight = Pango.Weight.BOLD;
- headings_cell.weight_set = true;
- headings_cell.ypad = 6;
- headings_cell.xpad = 0;
- col.set_cell_data_func(headings_cell, on_cell_renderer_heading_visible);
-
- // icon padding
- cell = new Gtk.CellRendererText();
- col.pack_start(cell, false);
- col.set_cell_data_func(cell, on_padding_cell_renderer);
-
- // icon renderer
- cell = new Gtk.CellRendererPixbuf();
- col.pack_start(cell, false);
- col.set_attributes(cell, "gicon", Column.ICON, null);
- col.set_cell_data_func(cell, on_cell_renderer_heading_not_visible);
-
- // normal text renderer
- Gtk.CellRendererText text_cell = new Gtk.CellRendererText();
- col.pack_start(text_cell, true);
- text_cell.editable = false;
- col.set_attributes(text_cell, "text", Column.LABEL, null);
- col.set_cell_data_func(text_cell, on_cell_renderer_heading_not_visible);
- text_cell.ellipsize = Pango.EllipsizeMode.END;
- text_cell.ellipsize_set = true;
-
- // lock/unlock icon renderer
- this.action_cell_renderer = new Gtk.CellRendererPixbuf();
- this.action_cell_renderer.mode = Gtk.CellRendererMode.ACTIVATABLE;
- this.action_cell_renderer.stock_size = Gtk.IconSize.MENU;
- this.action_cell_renderer.xpad = ACTION_BUTTON_XPAD;
- this.action_cell_renderer.xalign = 1.0f;
- col.pack_start(this.action_cell_renderer, false);
- col.set_cell_data_func(this.action_cell_renderer, on_cell_renderer_action_icon);
- col.set_max_width(24);
- append_column(col);
-
- set_headers_visible(false);
- set_tooltip_column(Column.TOOLTIP);
- set_model(this.store);
- this.popup_menu.connect(on_popup_menu);
- this.button_press_event.connect(on_button_press_event);
- this.motion_notify_event.connect(on_motion_notify_event);
- this.button_release_event.connect(on_button_release_event);
-
- Gtk.TreeSelection selection = get_selection();
- selection.set_mode(Gtk.SelectionMode.MULTIPLE);
- selection.set_select_function(on_tree_selection_validate);
- selection.changed.connect(() => update_objects_for_selection(selection));
+ GLib.Object(selection_mode: Gtk.SelectionMode.BROWSE);
+
+ bind_model(this.store, place_widget_create_cb);
+ set_header_func(place_header_cb);
+ this.selected_rows_changed.connect(update_objects);
load_backends();
}
@@ -185,11 +78,6 @@ public class Seahorse.Sidebar : Gtk.TreeView {
foreach (weak GLib.Object obj in backend.get_objects())
on_place_removed (backend, (Place) obj);
}
-
- invalidate_sidebar_pixbufs();
-
- if (this.update_places_sig != 0)
- Source.remove(this.update_places_sig);
}
private void load_backends() {
@@ -210,20 +98,78 @@ public class Seahorse.Sidebar : Gtk.TreeView {
});
}
- private void on_place_added(Gcr.Collection? places, GLib.Object obj) {
- ((Place) obj).notify.connect(() => update_places_later());
- update_places_later();
+ private void on_place_added(Gcr.Collection? places, GLib.Object place) {
+ place.notify.connect(() => update_places());
+ update_places();
}
- private void on_place_removed(Gcr.Collection? places, GLib.Object obj) {
- SignalHandler.disconnect_by_func((void*) obj, (void*) update_places_later, this);
- update_places_later();
+ private void on_place_removed(Gcr.Collection? places, GLib.Object place) {
+ SignalHandler.disconnect_by_func((void*) place, (void*) update_places, this);
+ update_places();
}
private void on_backend_changed(GLib.Object obj, ParamSpec spec) {
- update_places_later();
+ update_places();
+ }
+
+ private Gtk.Widget place_widget_create_cb(GLib.Object object) {
+ var item = new SidebarItem(object as Seahorse.Place);
+ item.place_changed.connect(on_sidebar_item_changed);
+ return item;
+ }
+
+ private void on_sidebar_item_changed(SidebarItem item) {
+ /* current_collection_changed(); */
}
+ private void place_header_cb(Gtk.ListBoxRow row, Gtk.ListBoxRow? before) {
+ Seahorse.Place place = ((SidebarItem) row).place;
+ string scheme = Uri.parse_scheme(place.uri);
+
+ // We don't need a title iff
+ // * there is no previous row
+ // * the previous row is from another backend
+ if (before != null) {
+ Seahorse.Place before_place = ((SidebarItem) before).place;
+ if (Uri.parse_scheme(before_place.uri) == scheme)
+ return;
+ }
+
+ // Find the backend that has the given scheme
+ foreach (var b in this.backends) {
+ if (place in b) {
+ var label = new Gtk.Label(b.label);
+ label.tooltip_text = b.description;
+ label.get_style_context().add_class("seahorse-sidebar-item-header");
+ label.xalign = 0f;
+ label.margin_start = 6;
+ label.margin_top = 6;
+ label.show();
+ row.set_header(label);
+ return;
+ }
+ }
+
+ warning("Couldn't find backend for place %s", place.label);
+ }
+
+ private int compare_places(GLib.Object obj_a, GLib.Object obj_b) {
+ Seahorse.Place a = (Seahorse.Place) obj_a;
+ Seahorse.Place b = (Seahorse.Place) obj_b;
+
+ // First of all, order the backends (SSH vs GPG)
+ // Since there is no easy way to map a place to its original backend,
+ // we can use the URI scheme
+ var a_scheme = GLib.Uri.parse_scheme(a.uri);
+ var b_scheme = GLib.Uri.parse_scheme(b.uri);
+ if (a_scheme != b_scheme)
+ return order_from_scheme(b_scheme) - order_from_scheme(a_scheme);
+
+ // In the same backend, order alphabetically
+ return a.label.casefold().collate(b.label.casefold());
+ }
+
+ // Keep in sync with order_from_scheme()
private static int order_from_backend (Backend backend) {
switch (backend.name) {
case "gkr":
@@ -239,433 +185,274 @@ public class Seahorse.Sidebar : Gtk.TreeView {
}
}
- private void ensure_sidebar_pixbufs() {
- if (this.pixbuf_lock != null && this.pixbuf_lock_l != null
- && this.pixbuf_unlock_l != null && this.pixbuf_unlock != null)
- return;
-
- Gtk.IconTheme icon_theme = Gtk.IconTheme.get_default();
- Gtk.StyleContext style = get_style_context();
-
- int height;
- if (!Gtk.icon_size_lookup(Gtk.IconSize.MENU, out this.action_button_size, out height))
- this.action_button_size = 16;
-
- // Lock icon
- Icon icon = new ThemedIcon.with_default_fallbacks("changes-prevent-symbolic");
- Gtk.IconInfo? icon_info = icon_theme.lookup_by_gicon(icon, this.action_button_size,
Gtk.IconLookupFlags.FORCE_SYMBOLIC);
- if (icon_info == null)
- return;
- try {
- if (this.pixbuf_lock == null)
- this.pixbuf_lock = icon_info.load_symbolic_for_context(style, null);
- if (this.pixbuf_lock_l == null)
- this.pixbuf_lock_l = create_spotlight_pixbuf(this.pixbuf_lock);
- } catch (Error e) {
- debug("Error while looking up lock icon: %s", e.message);
- }
-
- // Unlock icon
- icon = new ThemedIcon.with_default_fallbacks("changes-allow-symbolic");
- icon_info = icon_theme.lookup_by_gicon(icon, this.action_button_size,
Gtk.IconLookupFlags.FORCE_SYMBOLIC);
- if (icon_info == null)
- return;
- try {
- if (this.pixbuf_unlock == null)
- this.pixbuf_unlock = icon_info.load_symbolic_for_context(style, null);
- if (this.pixbuf_unlock_l == null)
- this.pixbuf_unlock_l = create_spotlight_pixbuf(this.pixbuf_unlock);
- } catch (Error e) {
- debug("Error while looking up unlock icon: %s", e.message);
+ // Keep in sync with order_from_backend()
+ private static int order_from_scheme (string scheme) {
+ switch (scheme) {
+ case "secret-service":
+ return 0;
+ case "gnupg":
+ return 1;
+ case "pkcs11":
+ return 2;
+ case "openssh":
+ return 3;
+ default:
+ return 10;
}
}
- private Gdk.Pixbuf? create_spotlight_pixbuf (Gdk.Pixbuf? src) {
- Gdk.Pixbuf? dest = new Gdk.Pixbuf(src.colorspace, src.has_alpha, src.bits_per_sample,
- src.width, src.height);
-
- bool has_alpha = src.has_alpha;
- int width = src.width;
- int height = src.height;
- int dst_row_stride = dest.rowstride;
- int src_row_stride = src.rowstride;
- uint8* target_pixels = dest.pixels;
- uint8* original_pixels = src.pixels;
-
- for (int i = 0; i < height; i++) {
- uint8* pixdest = target_pixels + i * dst_row_stride;
- uint8* pixsrc = original_pixels + i * src_row_stride;
- for (int j = 0; j < width; j++) {
- *pixdest++ = lighten_component (*pixsrc++);
- *pixdest++ = lighten_component (*pixsrc++);
- *pixdest++ = lighten_component (*pixsrc++);
- if (has_alpha) {
- *pixdest++ = *pixsrc++;
+ private void update_objects() {
+ debug("Updating objects (combined: %s)", this.combined.to_string());
+
+ // Combined overrides and shows all objects
+ if (this.combined) {
+ foreach (Backend backend in this.backends) {
+ foreach (var obj in backend.get_objects()) {
+ var place = (Place) obj;
+ if (!this.objects.have(place))
+ this.objects.add(place);
}
}
- }
- return dest;
- }
-
- private uint8 lighten_component(uint8 cur_value) {
- int new_value = cur_value + 24 + (cur_value >> 3);
- return (new_value > 255)? (uint8)255 : (uint8)new_value;
- }
-
- private void invalidate_sidebar_pixbufs() {
- this.pixbuf_lock = null;
- this.pixbuf_unlock = null;
- this.pixbuf_lock_l = null;
- this.pixbuf_unlock_l = null;
- }
-
- private void next_or_append_row(Gtk.ListStore? store, ref Gtk.TreeIter iter, string? category,
- Gcr.Collection? collection) {
- // We try to keep the same row in order to preserve checked state
- // and selections. So if the next row matches the values we want to
- // set on it, then just keep that row.
- //
- // This is complicated by the fact that the first row being inserted
- // doesn't have a valid iter, and we don't have a standard way to
- // detect that an iter isn't valid.
-
- // A marker that tells us the iter is not yet valid
- if (iter.stamp == int.from_pointer(&iter) && iter.user_data3 == (&iter) &&
- iter.user_data2 == (&iter) && iter.user_data == (&iter)) {
- if (!store.get_iter_first(out iter))
- store.append(out iter);
return;
}
- if (!store.iter_next(ref iter)) {
- store.append(out iter);
+ // Only selected ones should be in this.objects
+ var selected = get_focused_place();
+ if (selected == null)
return;
- }
- for (;;) {
- string? row_category;
- Gcr.Collection? row_collection;
- store.get(iter, Column.CATEGORY, out row_category,
- Column.COLLECTION, out row_collection);
-
- if (row_category == category && row_collection == collection)
- return;
-
- if (!store.remove(ref iter)) {
- store.append(out iter);
- return;
- }
+ foreach (var place in this.objects.elements()) {
+ if (selected != place)
+ this.objects.remove(place);
}
+ if (!this.objects.have(selected))
+ this.objects.add(selected);
}
- private void update_objects_in_collection(bool update_chosen) {
- if (this.updating) // Updating collection is blocked
- return;
+ private void update_places() {
+ debug("Updating sidebar places");
- bool changed = false;
- foreach (Backend backend in this.backends) {
- foreach (weak GLib.Object obj in backend.get_objects()) {
- Place place = (Place) obj;
- bool include = this.selection.lookup(place) != null;
-
- if (update_chosen) {
- string? uri = place.uri;
- bool have = (uri in this.chosen);
- if (include && !have) {
- this.chosen.add(uri);
- changed = true;
- } else if (!include && have) {
- this.chosen.remove(uri);
- changed = true;
- }
- }
+ foreach (Backend backend in this.backends)
+ update_backend(backend);
- // Combined overrides and shows all objects
- if (this.combined)
- include = true;
+ this.store.sort(compare_places);
- bool have = this.objects.have(place);
- if (include && !have)
- this.objects.add(place);
- else if (!include && have)
- this.objects.remove(place);
- }
- }
+ // Update selection
+ /* update_objects(); */
}
- private void update_objects_for_selection(Gtk.TreeSelection selection) {
- if (this.updating)
+ private void update_backend(Backend? backend) {
+ if (backend.get_objects() == null) // Ignore categories that have nothing
return;
- HashTable<Gcr.Collection, Gcr.Collection> selected = new HashTable<Gcr.Collection,
Gcr.Collection>(direct_hash, direct_equal);
- selection.selected_foreach((model, path, iter) => {
- Gcr.Collection? collection = null;
- model.get(iter, Column.COLLECTION, out collection, -1);
- if (collection != null)
- selected.insert(collection, collection);
- });
+ foreach (weak GLib.Object obj in backend.get_objects()) {
+ unowned Place? place = obj as Place;
+ if (place == null)
+ continue;
- this.selection = selected;
+ bool already_in = false;
+ for (int i = 0; i < this.store.get_n_items(); i++) {
+ if (this.store.get_object(i) == place) {
+ already_in = true;
+ break;
+ }
+ }
- if (!this.combined)
- update_objects_in_collection(true);
+ if (!already_in)
+ this.store.append(place);
+ }
}
- private void update_objects_for_chosen(GenericSet<string?> chosen) {
- this.updating = true;
-
- Gtk.TreeSelection selection = get_selection();
-
- // Update the display
- Gtk.TreeIter iter;
- if (this.store.get_iter_first(out iter)) {
- do {
- Gcr.Collection? collection = null;
- string? uri = null;
- this.store.get(iter, Column.COLLECTION, out collection,
- Column.URI, out uri, -1);
+ public override bool popup_menu() {
+ if (base.popup_menu())
+ return true;
- if (collection != null && uri != null) {
- if (uri in chosen)
- selection.select_iter(iter);
- else
- selection.unselect_iter(iter);
- }
- } while (this.store.iter_next(ref iter));
- }
+ var row = get_selected_row() as SidebarItem;
+ if (row == null)
+ return false;
- this.updating = false;
- update_objects_for_selection(selection);
+ row.show_popup_menu();
+ return true;
}
- private void update_places() {
- Gtk.TreeIter iter = Gtk.TreeIter();
- iter.stamp = int.from_pointer(&iter); // A marker that tells us the iter is not yet valid
- iter.user_data3 = iter.user_data2 = iter.user_data = &iter;
+ public override bool button_press_event(Gdk.EventButton event) {
+ if (base.button_press_event(event))
+ return true;
- foreach (Backend backend in this.backends)
- update_backend(backend, ref iter);
+ if (event.button != 3 || event.type != Gdk.EventType.BUTTON_PRESS)
+ return false;
- // Update selection
- update_objects_for_chosen(this.chosen);
+ var row = get_row_at_y((int) event.y) as SidebarItem;
+ if (row != null)
+ row.show_popup_menu();
- if (this.combined)
- update_objects_in_collection(false);
+ return true;
}
- private void update_backend(Backend? backend, ref Gtk.TreeIter iter) {
- if (backend.get_objects() == null) // Ignore categories that have nothing
- return;
-
- next_or_append_row(this.store, ref iter, backend.name, backend);
- this.store.set(iter, Column.ROW_TYPE, RowType.BACKEND,
- Column.CATEGORY, backend.name,
- Column.LABEL, backend.label,
- Column.TOOLTIP, backend.description,
- Column.COLLECTION, backend);
-
- foreach (weak GLib.Object obj in backend.get_objects()) {
- Place place = obj as Place;
- if (place == null)
- continue;
+ public string[] chosen_uris_to_array() {
+ string[] results = {};
+ foreach (string? uri in this.chosen)
+ results += uri;
- next_or_append_row(this.store, ref iter, backend.name, place);
- this.store.set(iter, Column.ROW_TYPE, RowType.PLACE,
- Column.CATEGORY, backend.name,
- Column.LABEL, place.label,
- Column.TOOLTIP, place.description,
- Column.ICON, place.icon,
- Column.COLLECTION, place,
- Column.URI, place.uri);
- }
- }
+ results += null;
- private void update_places_later() {
- if (this.update_places_sig == 0) {
- this.update_places_sig = Idle.add(() => {
- this.update_places_sig = 0;
- update_places();
- return false; // don't call again
- });
- }
+ return results;
}
- private Lockable? lookup_lockable_for_iter(Gtk.TreeModel? model, Gtk.TreeIter? iter) {
- Gcr.Collection? collection = null;
- model.get(iter, Column.COLLECTION, out collection, -1);
+ public void replace_chosen_uris(string[] uris) {
+ // For quick lookups
+ GenericSet<string?> chosen = new GenericSet<string?>(str_hash, str_equal);
+ foreach (string uri in uris)
+ chosen.add(uri);
- return collection as Lockable;
+ /* update_objects(); */
+ this.chosen = chosen;
}
- private void on_cell_renderer_action_icon(Gtk.CellLayout layout, Gtk.CellRenderer? cell,
- Gtk.TreeModel? model, Gtk.TreeIter? iter) {
- bool can_lock = false;
- bool can_unlock = false;
+ public List<weak Gcr.Collection>? get_selected_places() {
+ List<weak Gcr.Collection>? places = null;
- Lockable? lockable = lookup_lockable_for_iter(model, iter);
- if (lockable != null) {
- can_lock = lockable.lockable;
- can_unlock = lockable.unlockable;
+ foreach (var row in get_selected_rows()) {
+ var item = row as SidebarItem;
+ if (item != null)
+ places.append(item.place);
}
- if (can_lock || can_unlock) {
- ensure_sidebar_pixbufs();
-
- bool highlight = false;
- if (this.action_highlight_path != null) {
- Gtk.TreePath? path = model.get_path(iter);
- highlight = path.compare(this.action_highlight_path) == 0;
- }
+ return places;
+ }
- Gdk.Pixbuf? pixbuf;
- if (can_lock)
- pixbuf = highlight ? this.pixbuf_unlock : this.pixbuf_unlock_l;
- else
- pixbuf = highlight ? this.pixbuf_lock : this.pixbuf_lock_l;
+ public Place? get_focused_place() {
+ var row = get_selected_row() as SidebarItem;
+ debug("Getting focused place: %p", row);
+ return (row != null)? row.place : null;
+ }
- this.action_cell_renderer.visible = true;
- this.action_cell_renderer.pixbuf = pixbuf;
- } else {
- this.action_cell_renderer.visible = false;
- this.action_cell_renderer.pixbuf = null;
+ public void set_focused_place(string uri_prefix) {
+ foreach (Backend backend in this.backends) {
+ foreach (weak GLib.Object obj in backend.get_objects()) {
+ Place place = obj as Place;
+ if (place == null)
+ continue;
+ else if (place.uri.has_prefix(uri_prefix)) {
+ var chosen = new GenericSet<string?>(str_hash, str_equal);
+ chosen.add(place.uri);
+ //XXX
+ /* update_objects(); */
+ return;
+ }
+ }
}
}
- private void on_cell_renderer_heading_visible(Gtk.CellLayout layout, Gtk.CellRenderer? cell,
- Gtk.TreeModel? model, Gtk.TreeIter? iter) {
- RowType type;
- model.get(iter, Column.ROW_TYPE, out type, -1);
- cell.visible = (type == RowType.BACKEND);
+ public List<weak Backend>? get_backends() {
+ return this.backends.copy();
}
+}
- private void on_padding_cell_renderer(Gtk.CellLayout layout, Gtk.CellRenderer? cell,
- Gtk.TreeModel? model, Gtk.TreeIter? iter) {
- RowType type;
- model.get(iter, Column.ROW_TYPE, out type, -1);
+internal class Seahorse.SidebarItem : Gtk.ListBoxRow {
- if (type == RowType.BACKEND) {
- cell.visible = false;
- cell.xpad = 0;
- cell.ypad = 0;
- } else {
- cell.visible = true;
- cell.xpad = 3;
- cell.ypad = 3;
- }
- }
+ private Gtk.Button? lock_button = null;
- private void on_cell_renderer_heading_not_visible(Gtk.CellLayout layout, Gtk.CellRenderer? cell,
- Gtk.TreeModel? model, Gtk.TreeIter? iter) {
- RowType type;
- model.get(iter, Column.ROW_TYPE, out type, -1);
- cell.visible = (type != RowType.BACKEND);
- }
+ public weak Seahorse.Place place { get; construct set; }
- private bool on_tree_selection_validate(Gtk.TreeSelection selection, Gtk.TreeModel? model,
- Gtk.TreePath? path, bool path_currently_selected) {
- Gtk.TreeIter iter;
- model.get_iter(out iter, path);
+ public signal void place_changed();
- RowType row_type;
- model.get(iter, Column.ROW_TYPE, out row_type, -1);
- if (row_type == RowType.BACKEND)
- return false;
+ construct {
+ var grid = new Gtk.Grid();
+ grid.get_style_context().add_class("seahorse-sidebar-item");
+ grid.valign = Gtk.Align.CENTER;
+ grid.row_spacing = 6;
+ grid.column_spacing = 6;
+ add(grid);
- return true;
- }
+ var icon = new Gtk.Image.from_gicon(place.icon, Gtk.IconSize.BUTTON);
+ grid.attach(icon, 0, 0);
- private void place_lock(Lockable lockable, Gtk.Window? window) {
- Cancellable cancellable = new Cancellable();
- TlsInteraction interaction = new Interaction(window);
+ var label = new Gtk.Label(place.label);
+ label.hexpand = true;
+ label.ellipsize = Pango.EllipsizeMode.END;
+ label.xalign = 0f;
+ grid.attach(label, 1, 0);
- lockable.lock.begin(interaction, cancellable, (obj, res) => {
- try {
- lockable.lock.end(res);
- current_collection_changed();
- } catch (Error e) {
- Util.show_error(window, _("Couldn’t lock"), e.message);
- }
- });
- }
+ var lockable = place as Lockable;
+ if (lockable != null && (lockable.lockable || lockable.unlockable)) {
+ this.lock_button = new Gtk.Button.from_icon_name(get_lock_icon_name(lockable),
+ Gtk.IconSize.BUTTON);
+ this.lock_button.get_style_context().add_class("flat");
+ this.lock_button.clicked.connect((b) => {
+ if (lockable.unlockable)
+ place_unlock(lockable, (Gtk.Window) get_toplevel());
+ else if (lockable.lockable)
+ place_lock(lockable, (Gtk.Window) get_toplevel());
+
+ update_lock_icon(lockable);
+ });
+ grid.attach(this.lock_button, 2, 0);
+ }
- private void on_place_lock(Gtk.MenuItem item, Lockable lockable) {
- place_lock(lockable, (Gtk.Window) item.get_toplevel());
+ show_all();
}
- private void place_unlock(Lockable lockable, Gtk.Window? window) {
- Cancellable cancellable = new Cancellable();
- TlsInteraction interaction = new Interaction(window);
+ private static unowned string? get_lock_icon_name(Lockable lockable) {
+ if (lockable.unlockable)
+ return "changes-prevent-symbolic";
- lockable.unlock.begin(interaction, cancellable, (obj, res) => {
- try {
- lockable.unlock.end(res);
- current_collection_changed();
- } catch (Error e) {
- Util.show_error(window, _("Couldn’t unlock"), e.message);
- }
- });
+ if (lockable.lockable)
+ return "changes-allow-symbolic";
+
+ return null;
}
- private void on_place_unlock(Gtk.MenuItem item, Lockable lockable) {
- place_unlock(lockable, (Gtk.Window) item.get_toplevel());
+ private void update_lock_icon(Lockable lockable) {
+ ((Gtk.Image) this.lock_button.get_image()).icon_name = get_lock_icon_name(lockable);
}
- private void on_place_delete(Gtk.MenuItem item, Deletable deletable) {
- Deleter deleter = deletable.create_deleter();
- if (deleter.prompt((Gtk.Window) item.get_toplevel())) {
- deleter.delete.begin(null, (obj, res) => {
- try {
- deleter.delete.end(res);
- } catch (Error e) {
- Util.show_error(parent, _("Couldn’t delete"), e.message);
- }
- });
- }
+ public SidebarItem(Seahorse.Place place) {
+ GLib.Object(place: place);
}
- private void popup_menu_for_place(Place place) {
- // Start from the menu model provided by the place (if any)
- var menu = (place.menu_model != null)? new Gtk.Menu.from_model(place.menu_model)
- : new Gtk.Menu();
+ public void show_popup_menu() {
+ // Start from the menu model provided by the this.place (if any)
+ var menu = (this.place.menu_model != null)? new Gtk.Menu.from_model(this.place.menu_model)
+ : new Gtk.Menu();
// Make sure the actions from the collection
- if (place.actions != null)
- menu.insert_action_group(place.action_prefix, place.actions);
+ if (this.place.actions != null)
+ menu.insert_action_group(this.place.action_prefix, this.place.actions);
// Lock and unlock items
- if (place is Lockable) {
+ if (this.place is Lockable) {
Gtk.MenuItem item = new Gtk.MenuItem.with_mnemonic(_("_Lock"));
- item.activate.connect(() => on_place_lock(item, (Lockable) place));
- place.bind_property("lockable", item, "visible", BindingFlags.SYNC_CREATE);
+ item.activate.connect(() => on_place_lock(item, (Lockable) this.place));
+ this.place.bind_property("lockable", item, "visible", BindingFlags.SYNC_CREATE);
menu.append(item);
item = new Gtk.MenuItem.with_mnemonic(_("_Unlock"));
- item.activate.connect(() => on_place_unlock(item, (Lockable) place));
- place.bind_property("unlockable", item, "visible", BindingFlags.SYNC_CREATE);
+ item.activate.connect(() => on_place_unlock(item, (Lockable) this.place));
+ this.place.bind_property("unlockable", item, "visible", BindingFlags.SYNC_CREATE);
menu.append(item);
}
// Delete item
- if (place is Deletable) {
+ if (this.place is Deletable) {
Gtk.MenuItem item = new Gtk.MenuItem.with_mnemonic(_("_Delete"));
- item.activate.connect(() => on_place_delete(item, (Deletable) place));
- place.bind_property("deletable", item, "sensitive", BindingFlags.SYNC_CREATE);
+ item.activate.connect(() => on_place_delete(item, (Deletable) this.place));
+ this.place.bind_property("deletable", item, "sensitive", BindingFlags.SYNC_CREATE);
menu.append(item);
item.show();
}
// Properties item
- if (place is Viewable) {
+ if (this.place is Viewable) {
Gtk.MenuItem item = new Gtk.MenuItem.with_mnemonic(_("_Properties"));
- item.activate.connect(() => Viewable.view(place, (Gtk.Window) item.get_toplevel()));
+ item.activate.connect(() => Viewable.view(this.place, (Gtk.Window) item.get_toplevel()));
menu.append(item);
item.show();
}
bool visible = false;
menu.foreach((widget) => {
- if (widget.visible)
- visible = true;
+ visible |= widget.visible;
});
if (visible) {
@@ -677,246 +464,54 @@ public class Seahorse.Sidebar : Gtk.TreeView {
}
}
- private bool on_popup_menu(Gtk.Widget? widget) {
- Gtk.TreePath? path;
- get_cursor(out path, null);
- if (path == null)
- return false;
-
- Gtk.TreeIter iter;
- if (!this.store.get_iter(out iter, path))
- return false;
-
- Gcr.Collection? collection;
- this.store.get(iter, Column.COLLECTION, out collection, -1);
-
- if (collection is Place) {
- popup_menu_for_place((Place) collection);
- return true;
- }
-
- return false;
- }
-
- private void update_action_buttons_take_path(Gtk.TreePath? path) {
- if (path == this.action_highlight_path)
- return;
-
- if (path != null && this.action_highlight_path != null &&
- this.action_highlight_path.compare(path) == 0) {
- return;
- }
-
- Gtk.TreePath? old_path = this.action_highlight_path;
- this.action_highlight_path = path;
-
- Gtk.TreeIter? iter = null;
- if (this.action_highlight_path != null
- && this.store.get_iter(out iter, this.action_highlight_path))
- this.store.row_changed(this.action_highlight_path, iter);
-
- if (old_path != null && this.store.get_iter(out iter, old_path))
- this.store.row_changed(old_path, iter);
- }
-
- private bool over_action_button(int x, int y, out Gtk.TreePath? path) {
-
- path = null;
- Gtk.TreeViewColumn column;
- if (get_path_at_pos(x, y, out path, out column, null, null)) {
- Gtk.TreeIter iter;
- this.store.get_iter(out iter, path);
-
- int hseparator;
- style_get("horizontal-separator", out hseparator, null);
-
- // Reload cell attributes for this particular row
- column.cell_set_cell_data(this.store, iter, false, false);
- int width, x_offset;
- column.cell_get_position(this.action_cell_renderer, out x_offset, out width);
-
- // This is kinda weird, but we have to do it to workaround gtk+ expanding
- // the eject cell renderer (even thought we told it not to) and we then
- // had to set it right-aligned
- x_offset += width - hseparator - ACTION_BUTTON_XPAD - this.action_button_size;
-
- if (x - x_offset >= 0 && x - x_offset <= this.action_button_size)
- return true;
- }
-
- if (path != null)
- path = null;
-
- return false;
- }
-
- private bool on_motion_notify_event(Gtk.Widget? widget, Gdk.EventMotion event) {
- Gtk.TreePath? path = null;
- if (over_action_button((int) event.x, (int) event.y, out path)) {
- update_action_buttons_take_path(path);
- return true;
- }
-
- update_action_buttons_take_path(null);
- return false;
- }
-
- private bool on_button_press_event (Gtk.Widget? widget, Gdk.EventButton event) {
- if (event.button != 3 || event.type != Gdk.EventType.BUTTON_PRESS)
- return false;
-
- Gtk.TreePath? path;
- if (!get_path_at_pos((int) event.x, (int) event.y, out path, null, null, null))
- return false;
-
- set_cursor(path, null, false);
- Gtk.TreeIter iter;
- if (!this.store.get_iter(out iter, path))
- return false;
-
- Gcr.Collection? collection;
- this.store.get(iter, Column.COLLECTION, out collection, -1);
-
- if (collection is Place)
- popup_menu_for_place((Place) collection);
-
- return true;
- }
-
- private bool on_button_release_event (Gtk.Widget? widget, Gdk.EventButton event) {
- if (event.type != Gdk.EventType.BUTTON_RELEASE)
- return true;
-
- Gtk.TreePath? path;
- if (!over_action_button((int) event.x, (int) event.y, out path))
- return false;
-
- Gtk.TreeIter iter;
- if (!this.store.get_iter(out iter, path))
- return false;
-
- Gtk.Window? window = (Gtk.Window) widget.get_toplevel();
-
- Lockable? lockable = lookup_lockable_for_iter(this.store, iter);
- if (lockable != null) {
- if (lockable.lockable)
- place_lock(lockable, window);
- else if (lockable.unlockable)
- place_unlock(lockable, window);
- }
-
- return true;
- }
-
- public string[] chosen_uris_to_array() {
- string[] results = {};
- foreach (string? uri in this.chosen)
- results += uri;
-
- results += null;
+ private void place_lock(Lockable lockable, Gtk.Window? window) {
+ Cancellable cancellable = new Cancellable();
+ TlsInteraction interaction = new Interaction(window);
- return results;
+ lockable.lock.begin(interaction, cancellable, (obj, res) => {
+ try {
+ lockable.lock.end(res);
+ update_lock_icon (lockable);
+ place_changed();
+ } catch (Error e) {
+ Util.show_error(window, _("Couldn’t lock"), e.message);
+ }
+ });
}
- public void replace_chosen_uris(string[] uris) {
- // For quick lookups
- GenericSet<string?> chosen = new GenericSet<string?>(str_hash, str_equal);
- foreach (string uri in uris)
- chosen.add(uri);
-
- update_objects_for_chosen(chosen);
- this.chosen = chosen;
+ private void on_place_lock(Gtk.Widget widget, Lockable lockable) {
+ place_lock(lockable, (Gtk.Window) widget.get_toplevel());
}
- public List<weak Gcr.Collection>? get_selected_places() {
- List<weak Gcr.Collection> places = this.objects.elements();
-
- Gtk.TreePath? path = null;
- get_cursor(out path, null);
- if (path != null) {
-
- Gtk.TreeIter iter;
- if (!this.store.get_iter(out iter, path))
- return null;
-
- Gcr.Collection? collection;
- RowType row_type;
- this.store.get(iter, Column.ROW_TYPE, out row_type,
- Column.COLLECTION, out collection, -1);
+ private void place_unlock(Lockable lockable, Gtk.Window? window) {
+ Cancellable cancellable = new Cancellable();
+ TlsInteraction interaction = new Interaction(window);
- if (collection != null) {
- if (row_type == RowType.PLACE) {
- places.remove(collection);
- places.prepend(collection);
- }
+ lockable.unlock.begin(interaction, cancellable, (obj, res) => {
+ try {
+ lockable.unlock.end(res);
+ update_lock_icon (lockable);
+ place_changed();
+ } catch (Error e) {
+ Util.show_error(window, _("Couldn’t unlock"), e.message);
}
- }
-
- return places;
- }
-
- public Place? get_focused_place() {
- Gtk.TreeIter iter;
-
- Gtk.TreePath? path = null;
- get_cursor(out path, null);
- if (path != null) {
- if (!this.store.get_iter(out iter, path))
- return null;
-
- Gcr.Collection? collection;
- RowType row_type;
- this.store.get(iter, Column.ROW_TYPE, out row_type,
- Column.COLLECTION, out collection, -1);
-
- if (row_type == RowType.PLACE)
- return (Place) collection;
- }
-
- return null;
+ });
}
- public void set_focused_place(string uri_prefix) {
- foreach (Backend backend in this.backends) {
- foreach (weak GLib.Object obj in backend.get_objects()) {
- Place place = obj as Place;
- if (place == null)
- continue;
- else if (place.uri.has_prefix(uri_prefix)) {
- var chosen = new GenericSet<string?>(str_hash, str_equal);
- chosen.add(place.uri);
- this.update_objects_for_chosen(chosen);
- return;
- }
- }
- }
+ private void on_place_unlock(Gtk.MenuItem widget, Lockable lockable) {
+ place_unlock(lockable, (Gtk.Window) widget.get_toplevel());
}
- public List<weak Backend>? get_backends() {
- Gtk.TreeIter iter;
-
- List<weak Backend> backends = this.backends.copy();
- backends.reverse();
-
- Gtk.TreePath? path = null;
- get_cursor(out path, null);
- if (path != null) {
- if (!this.store.get_iter(out iter, path))
- return null;
-
- Gcr.Collection? collection;
- RowType row_type;
- this.store.get(iter, Column.ROW_TYPE, out row_type,
- Column.COLLECTION, out collection, -1);
-
- if (collection != null) {
- if (row_type == RowType.BACKEND) {
- backends.remove((Backend) collection);
- backends.prepend((Backend) collection);
+ private void on_place_delete(Gtk.MenuItem item, Deletable deletable) {
+ Deleter deleter = deletable.create_deleter();
+ if (deleter.prompt((Gtk.Window) item.get_toplevel())) {
+ deleter.delete.begin(null, (obj, res) => {
+ try {
+ deleter.delete.end(res);
+ } catch (Error e) {
+ Util.show_error(parent, _("Couldn’t delete"), e.message);
}
- }
+ });
}
-
- return backends;
}
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]