From c43dfb815a4951b8248f4f0e98babe4f80204f03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Frings-F=C3=BCrst?= Date: Fri, 3 Apr 2015 13:14:53 +0200 Subject: Imported Upstream version 0.22.0 --- src/sidebar/Branch.vala | 64 ++++------ src/sidebar/Entry.vala | 21 ++-- src/sidebar/Sidebar.vala | 2 +- src/sidebar/Tree.vala | 295 +++++++++++++++++++++++++++-------------------- src/sidebar/common.vala | 59 +++++++--- 5 files changed, 245 insertions(+), 196 deletions(-) (limited to 'src/sidebar') diff --git a/src/sidebar/Branch.vala b/src/sidebar/Branch.vala index 23badda..a6c3ee8 100644 --- a/src/sidebar/Branch.vala +++ b/src/sidebar/Branch.vala @@ -1,4 +1,4 @@ -/* Copyright 2011-2014 Yorba Foundation +/* Copyright 2011-2015 Yorba Foundation * * This software is licensed under the GNU Lesser General Public License * (version 2.1 or later). See the COPYING file in this distribution. @@ -39,23 +39,22 @@ public class Sidebar.Branch : Object { public Sidebar.Entry entry; public weak Node? parent; - public CompareDataFunc comparator; + public CompareFunc comparator; public Gee.SortedSet? children = null; - public Node(Sidebar.Entry entry, Node? parent, - owned CompareDataFunc comparator) { + public Node(Sidebar.Entry entry, Node? parent, CompareFunc comparator) { this.entry = entry; this.parent = parent; - this.comparator = (owned) comparator; + this.comparator = comparator; } - private static int comparator_wrapper(Node? a, Node? b) { - if (a == b) + private static int comparator_wrapper(Node anode, Node bnode) { + if (anode == bnode) return 0; - assert(a.parent == b.parent); + assert(anode.parent == bnode.parent); - return a.parent.comparator(a.entry, b.entry); + return anode.parent.comparator(anode.entry, bnode.entry); } public bool has_children() { @@ -172,16 +171,16 @@ public class Sidebar.Branch : Object { cb(this); } - public void change_comparator(owned CompareDataFunc comparator, bool recursive, + public void change_comparator(CompareFunc comparator, bool recursive, ChildrenReorderedCallback cb) { - this.comparator = (owned) comparator; + this.comparator = comparator; // reorder children, but need to do manual recursion to set comparator reorder_children(false, cb); if (recursive) { foreach (Node child in children) - child.change_comparator((owned) comparator, true, cb); + child.change_comparator(comparator, true, cb); } } } @@ -189,7 +188,7 @@ public class Sidebar.Branch : Object { private Node root; private Options options; private bool shown = true; - private CompareDataFunc default_comparator; + private CompareFunc default_comparator; private Gee.HashMap map = new Gee.HashMap(); public signal void entry_added(Sidebar.Entry entry); @@ -204,19 +203,11 @@ public class Sidebar.Branch : Object { public signal void show_branch(bool show); - public Branch(Sidebar.Entry root, Options options, - owned CompareDataFunc default_comparator, - owned CompareDataFunc? root_comparator = null) { - this.default_comparator = (owned) default_comparator; - - CompareDataFunc? broken_ternary_workaround; - - if (root_comparator != null) - broken_ternary_workaround = (owned) root_comparator; - else - broken_ternary_workaround = (owned) default_comparator; - - this.root = new Node(root, null, (owned) broken_ternary_workaround); + public Branch(Sidebar.Entry root, Options options, CompareFunc default_comparator, + CompareFunc? root_comparator = null) { + this.default_comparator = default_comparator; + this.root = new Node(root, null, + (root_comparator != null) ? root_comparator : default_comparator); this.options = options; map.set(root, this.root); @@ -254,7 +245,7 @@ public class Sidebar.Branch : Object { } public void graft(Sidebar.Entry parent, Sidebar.Entry entry, - owned CompareDataFunc? comparator = null) { + CompareFunc? comparator = null) { assert(map.has_key(parent)); assert(!map.has_key(entry)); @@ -262,15 +253,8 @@ public class Sidebar.Branch : Object { set_show_branch(true); Node parent_node = map.get(parent); - - CompareDataFunc? broken_ternary_workaround; - - if (comparator != null) - broken_ternary_workaround = (owned) comparator; - else - broken_ternary_workaround = (owned) default_comparator; - - Node entry_node = new Node(entry, parent_node, (owned) broken_ternary_workaround); + Node entry_node = new Node(entry, parent_node, + (comparator != null) ? comparator : default_comparator); parent_node.add_child(entry_node); map.set(entry, entry_node); @@ -347,16 +331,16 @@ public class Sidebar.Branch : Object { entry_node.reorder_children(recursive, children_reordered_callback); } - public void change_all_comparators(owned CompareDataFunc? comparator) { - root.change_comparator((owned) comparator, true, children_reordered_callback); + public void change_all_comparators(CompareFunc? comparator) { + root.change_comparator(comparator, true, children_reordered_callback); } public void change_comparator(Sidebar.Entry entry, bool recursive, - owned CompareDataFunc? comparator) { + CompareFunc? comparator) { Node? entry_node = map.get(entry); assert(entry_node != null); - entry_node.change_comparator((owned) comparator, recursive, children_reordered_callback); + entry_node.change_comparator(comparator, recursive, children_reordered_callback); } public int get_child_count(Sidebar.Entry parent) { diff --git a/src/sidebar/Entry.vala b/src/sidebar/Entry.vala index 4162f21..5a84f74 100644 --- a/src/sidebar/Entry.vala +++ b/src/sidebar/Entry.vala @@ -1,4 +1,4 @@ -/* Copyright 2011-2014 Yorba Foundation +/* Copyright 2011-2015 Yorba Foundation * * This software is licensed under the GNU Lesser General Public License * (version 2.1 or later). See the COPYING file in this distribution. @@ -7,13 +7,13 @@ public interface Sidebar.Entry : Object { public signal void sidebar_tooltip_changed(string? tooltip); - public signal void sidebar_icon_changed(Icon? icon); + public signal void sidebar_icon_changed(string? icon); public abstract string get_sidebar_name(); public abstract string? get_sidebar_tooltip(); - public abstract Icon? get_sidebar_icon(); + public abstract string? get_sidebar_icon(); public abstract string to_string(); @@ -25,12 +25,6 @@ public interface Sidebar.Entry : Object { } public interface Sidebar.ExpandableEntry : Sidebar.Entry { - public signal void sidebar_open_closed_icons_changed(Icon? open, Icon? closed); - - public abstract Icon? get_sidebar_open_icon(); - - public abstract Icon? get_sidebar_closed_icon(); - public abstract bool expand_on_select(); } @@ -53,6 +47,15 @@ public interface Sidebar.RenameableEntry : Sidebar.Entry { public signal void sidebar_name_changed(string name); public abstract void rename(string new_name); + + // Return true to allow the user to rename the sidebar entry in the UI. + public abstract bool is_user_renameable(); +} + +public interface Sidebar.EmphasizableEntry : Sidebar.Entry { + public signal void is_emphasized_changed(bool emphasized); + + public abstract bool is_emphasized(); } public interface Sidebar.DestroyableEntry : Sidebar.Entry { diff --git a/src/sidebar/Sidebar.vala b/src/sidebar/Sidebar.vala index 8f6904b..0b4c0da 100644 --- a/src/sidebar/Sidebar.vala +++ b/src/sidebar/Sidebar.vala @@ -1,4 +1,4 @@ -/* Copyright 2011-2014 Yorba Foundation +/* Copyright 2011-2015 Yorba Foundation * * This software is licensed under the GNU Lesser General Public License * (version 2.1 or later). See the COPYING file in this distribution. diff --git a/src/sidebar/Tree.vala b/src/sidebar/Tree.vala index 37da7e0..a020b18 100644 --- a/src/sidebar/Tree.vala +++ b/src/sidebar/Tree.vala @@ -1,4 +1,4 @@ -/* Copyright 2011-2014 Yorba Foundation +/* Copyright 2011-2015 Yorba Foundation * * This software is licensed under the GNU Lesser General Public License * (version 2.1 or later). See the COPYING file in this distribution. @@ -37,8 +37,7 @@ public class Sidebar.Tree : Gtk.TreeView { private class RootWrapper : EntryWrapper { public int root_position; - public RootWrapper(Gtk.TreeModel model, Sidebar.Entry entry, Gtk.TreePath path, int root_position) - requires (root_position >= 0) { + public RootWrapper(Gtk.TreeModel model, Sidebar.Entry entry, Gtk.TreePath path, int root_position) { base (model, entry, path); this.root_position = root_position; @@ -49,9 +48,7 @@ public class Sidebar.Tree : Gtk.TreeView { NAME, TOOLTIP, WRAPPER, - PIXBUF, - CLOSED_PIXBUF, - OPEN_PIXBUF, + ICON, N_COLUMNS } @@ -59,17 +56,13 @@ public class Sidebar.Tree : Gtk.TreeView { typeof (string), // NAME typeof (string?), // TOOLTIP typeof (EntryWrapper), // WRAPPER - typeof (Gdk.Pixbuf?), // PIXBUF - typeof (Gdk.Pixbuf?), // CLOSED_PIXBUF - typeof (Gdk.Pixbuf?) // OPEN_PIXBUF + typeof (string?) // ICON ); private Gtk.UIManager ui = new Gtk.UIManager(); - private Gtk.IconTheme icon_theme; private Gtk.CellRendererText text_renderer; private unowned ExternalDropHandler drop_handler; private Gtk.Entry? text_entry = null; - private Gee.HashMap icon_cache = new Gee.HashMap(); private Gee.HashMap entry_map = new Gee.HashMap(); private Gee.HashMap branches = new Gee.HashMap(); @@ -77,8 +70,11 @@ public class Sidebar.Tree : Gtk.TreeView { private bool mask_entry_selected_signal = false; private weak EntryWrapper? selected_wrapper = null; private Gtk.Menu? default_context_menu = null; + private bool expander_called_manually = false; + private int expander_special_count = 0; private bool is_internal_drag_in_progress = false; private Sidebar.Entry? internal_drag_source_entry = null; + private Gtk.TreeRowReference? old_path_ref = null; public signal void entry_selected(Sidebar.SelectableEntry selectable); @@ -97,15 +93,17 @@ public class Sidebar.Tree : Gtk.TreeView { public Tree(Gtk.TargetEntry[] target_entries, Gdk.DragAction actions, ExternalDropHandler drop_handler) { set_model(store); + get_style_context().add_class("sidebar"); Gtk.TreeViewColumn text_column = new Gtk.TreeViewColumn(); - text_column.set_sizing(Gtk.TreeViewColumnSizing.FIXED); + text_column.set_expand(true); Gtk.CellRendererPixbuf icon_renderer = new Gtk.CellRendererPixbuf(); + icon_renderer.follow_state = true; text_column.pack_start(icon_renderer, false); - text_column.add_attribute(icon_renderer, "pixbuf", Columns.PIXBUF); - text_column.add_attribute(icon_renderer, "pixbuf_expander_closed", Columns.CLOSED_PIXBUF); - text_column.add_attribute(icon_renderer, "pixbuf_expander_open", Columns.OPEN_PIXBUF); + text_column.add_attribute(icon_renderer, "icon_name", Columns.ICON); + text_column.set_cell_data_func(icon_renderer, icon_renderer_function); text_renderer = new Gtk.CellRendererText(); + text_renderer.ellipsize = Pango.EllipsizeMode.END; text_renderer.editing_canceled.connect(on_editing_canceled); text_renderer.editing_started.connect(on_editing_started); text_column.pack_start(text_renderer, true); @@ -131,6 +129,9 @@ public class Sidebar.Tree : Gtk.TreeView { selection.set_mode(Gtk.SelectionMode.BROWSE); selection.set_select_function(on_selection); + test_expand_row.connect(on_toggle_row); + test_collapse_row.connect(on_toggle_row); + // It Would Be Nice if the target entries and actions were gleaned by querying each // Sidebar.Entry as it was added, but that's a tad too complicated for our needs // currently @@ -145,9 +146,6 @@ public class Sidebar.Tree : Gtk.TreeView { popup_menu.connect(on_context_menu_keypress); - icon_theme = Resources.get_icon_theme_engine(); - icon_theme.changed.connect(on_theme_change); - setup_default_context_menu(); drag_begin.connect(on_drag_begin); @@ -158,7 +156,14 @@ public class Sidebar.Tree : Gtk.TreeView { ~Tree() { text_renderer.editing_canceled.disconnect(on_editing_canceled); text_renderer.editing_started.disconnect(on_editing_started); - icon_theme.changed.disconnect(on_theme_change); + } + + public void icon_renderer_function(Gtk.CellLayout layout, Gtk.CellRenderer renderer, Gtk.TreeModel model, Gtk.TreeIter iter) { + EntryWrapper? wrapper = get_wrapper_at_iter(iter); + if (wrapper == null) { + return; + } + renderer.visible = !(wrapper.entry is Sidebar.Header); } private void on_drag_begin(Gdk.DragContext ctx) { @@ -270,41 +275,72 @@ public class Sidebar.Tree : Gtk.TreeView { public bool is_selected(Sidebar.Entry entry) { EntryWrapper? wrapper = get_wrapper(entry); - return (wrapper != null) ? get_selection().path_is_selected(wrapper.get_path()) : false; + // Even though get_selection() does not report its return type as nullable, it can be null + // if the window has been destroyed. + Gtk.TreeSelection selection = get_selection(); + if (selection == null) + return false; + + return (wrapper != null) ? selection.path_is_selected(wrapper.get_path()) : false; } public bool is_any_selected() { return get_selection().count_selected_rows() != 0; } - + private Gtk.TreePath? get_selected_path() { Gtk.TreeModel model; - GLib.List rows = get_selection().get_selected_rows(out model); + Gtk.TreeSelection? selection = get_selection(); + if (selection == null){ + return null; + } + GLib.List rows = selection.get_selected_rows(out model); assert(rows.length() == 0 || rows.length() == 1); - + return rows.length() != 0 ? rows.nth_data(0) : null; } + + private string get_name_for_entry(Sidebar.Entry entry) { + string name = guarded_markup_escape_text(entry.get_sidebar_name()); + + Sidebar.EmphasizableEntry? emphasizable_entry = entry as Sidebar.EmphasizableEntry; + if (emphasizable_entry != null && emphasizable_entry.is_emphasized()) + name = "%s".printf(name); + + return name; + } + + public virtual bool accept_cursor_changed() { + return true; + } public override void cursor_changed() { Gtk.TreePath? path = get_selected_path(); if (path == null) { if (base.cursor_changed != null) base.cursor_changed(); - return; } EntryWrapper? wrapper = get_wrapper_at_path(path); - - selected_wrapper = wrapper; - - if (editing_disabled == 0 && wrapper != null) - text_renderer.editable = wrapper.entry is Sidebar.RenameableEntry; - if (wrapper != null && !mask_entry_selected_signal) { - Sidebar.SelectableEntry? selectable = wrapper.entry as Sidebar.SelectableEntry; - if (selectable != null) - entry_selected(selectable); + if (selected_wrapper != wrapper) { + EntryWrapper old_wrapper = selected_wrapper; + selected_wrapper = wrapper; + + if (editing_disabled == 0 && wrapper != null && wrapper.entry is Sidebar.RenameableEntry) + text_renderer.editable = ((Sidebar.RenameableEntry) wrapper.entry).is_user_renameable(); + + if (wrapper != null && !mask_entry_selected_signal) { + Sidebar.SelectableEntry? selectable = wrapper.entry as Sidebar.SelectableEntry; + if (selectable != null) { + if (accept_cursor_changed()) { + entry_selected(selectable); + } else { + place_cursor(old_wrapper.entry, true); + } + } + } } if (base.cursor_changed != null) @@ -320,11 +356,14 @@ public class Sidebar.Tree : Gtk.TreeView { Gtk.TreePath? path = get_selected_path(); if (path != null && editing_disabled > 0 && --editing_disabled == 0) { EntryWrapper? wrapper = get_wrapper_at_path(path); - text_renderer.editable = (wrapper != null && (wrapper.entry is Sidebar.RenameableEntry)); + if (wrapper != null && (wrapper.entry is Sidebar.RenameableEntry)) + text_renderer.editable = ((Sidebar.RenameableEntry) wrapper.entry). + is_user_renameable(); } } public void toggle_branch_expansion(Gtk.TreePath path, bool expand_all) { + expander_called_manually = true; if (is_row_expanded(path)) collapse_row(path); else @@ -332,6 +371,7 @@ public class Sidebar.Tree : Gtk.TreeView { } public bool expand_to_entry(Sidebar.Entry entry) { + expander_called_manually = true; EntryWrapper? wrapper = get_wrapper(entry); if (wrapper == null) return false; @@ -342,6 +382,7 @@ public class Sidebar.Tree : Gtk.TreeView { } public void expand_to_first_child(Sidebar.Entry entry) { + expander_called_manually = true; EntryWrapper? wrapper = get_wrapper(entry); if (wrapper == null) return; @@ -445,7 +486,7 @@ public class Sidebar.Tree : Gtk.TreeView { assert(!entry_map.has_key(entry)); entry_map.set(entry, wrapper); - store.set(assoc_iter, Columns.NAME, guarded_markup_escape_text(entry.get_sidebar_name())); + store.set(assoc_iter, Columns.NAME, get_name_for_entry(entry)); store.set(assoc_iter, Columns.TOOLTIP, guarded_markup_escape_text(entry.get_sidebar_tooltip())); store.set(assoc_iter, Columns.WRAPPER, wrapper); load_entry_icons(assoc_iter); @@ -458,15 +499,15 @@ public class Sidebar.Tree : Gtk.TreeView { pageable.page_created.connect(on_sidebar_page_created); pageable.destroying_page.connect(on_sidebar_destroying_page); } + + Sidebar.EmphasizableEntry? emphasizable = entry as Sidebar.EmphasizableEntry; + if (emphasizable != null) + emphasizable.is_emphasized_changed.connect(on_is_emphasized_changed); Sidebar.RenameableEntry? renameable = entry as Sidebar.RenameableEntry; if (renameable != null) renameable.sidebar_name_changed.connect(on_sidebar_name_changed); - - Sidebar.ExpandableEntry? expandable = entry as Sidebar.ExpandableEntry; - if (expandable != null) - expandable.sidebar_open_closed_icons_changed.connect(on_sidebar_open_closed_icons_changed); - + entry.grafted(this); } @@ -479,7 +520,7 @@ public class Sidebar.Tree : Gtk.TreeView { EntryWrapper new_wrapper = new EntryWrapper(store, entry, store.get_path(new_iter)); entry_map.set(entry, new_wrapper); - store.set(new_iter, Columns.NAME, guarded_markup_escape_text(entry.get_sidebar_name())); + store.set(new_iter, Columns.NAME, get_name_for_entry(entry)); store.set(new_iter, Columns.TOOLTIP, guarded_markup_escape_text(entry.get_sidebar_tooltip())); store.set(new_iter, Columns.WRAPPER, new_wrapper); load_entry_icons(new_iter); @@ -571,9 +612,9 @@ public class Sidebar.Tree : Gtk.TreeView { if (renameable != null) renameable.sidebar_name_changed.disconnect(on_sidebar_name_changed); - Sidebar.ExpandableEntry? expandable = entry as Sidebar.ExpandableEntry; - if (expandable != null) - expandable.sidebar_open_closed_icons_changed.disconnect(on_sidebar_open_closed_icons_changed); + Sidebar.EmphasizableEntry? emphasizable = entry as Sidebar.EmphasizableEntry; + if (emphasizable != null) + emphasizable.is_emphasized_changed.disconnect(on_is_emphasized_changed); bool removed = entry_map.unset(entry); assert(removed); @@ -703,88 +744,42 @@ public class Sidebar.Tree : Gtk.TreeView { store.set(wrapper.get_iter(), Columns.TOOLTIP, guarded_markup_escape_text(tooltip)); } - private void on_sidebar_icon_changed(Sidebar.Entry entry, Icon? icon) { + private void on_sidebar_icon_changed(Sidebar.Entry entry, string? icon) { EntryWrapper? wrapper = get_wrapper(entry); assert(wrapper != null); - store.set(wrapper.get_iter(), Columns.PIXBUF, fetch_icon_pixbuf(icon)); + store.set(wrapper.get_iter(), Columns.ICON, icon); } - - private void on_sidebar_page_created(Sidebar.PageRepresentative entry, Page page) { - page_created(entry, page); + + private void rename_entry(Sidebar.Entry entry) { + EntryWrapper? wrapper = get_wrapper(entry); + assert(wrapper != null); + + store.set(wrapper.get_iter(), Columns.NAME, get_name_for_entry(entry)); } - private void on_sidebar_destroying_page(Sidebar.PageRepresentative entry, Page page) { - destroying_page(entry, page); + private void on_sidebar_name_changed(Sidebar.Entry entry, string name) { + rename_entry(entry); } - private void on_sidebar_open_closed_icons_changed(Sidebar.ExpandableEntry entry, Icon? open, - Icon? closed) { - EntryWrapper? wrapper = get_wrapper(entry); - assert(wrapper != null); - - store.set(wrapper.get_iter(), Columns.OPEN_PIXBUF, fetch_icon_pixbuf(open)); - store.set(wrapper.get_iter(), Columns.CLOSED_PIXBUF, fetch_icon_pixbuf(closed)); + private void on_sidebar_page_created(Sidebar.PageRepresentative entry, Page page) { + page_created(entry, page); } - private void on_sidebar_name_changed(Sidebar.RenameableEntry entry, string name) { - EntryWrapper? wrapper = get_wrapper(entry); - assert(wrapper != null); - - store.set(wrapper.get_iter(), Columns.NAME, guarded_markup_escape_text(name)); + private void on_is_emphasized_changed(Sidebar.EmphasizableEntry entry, bool is_emphasized) { + rename_entry(entry); } - private Gdk.Pixbuf? fetch_icon_pixbuf(GLib.Icon? gicon) { - if (gicon == null) - return null; - - try { - Gdk.Pixbuf? icon = icon_cache.get(gicon.to_string()); - if (icon != null) - return icon; - - Gtk.IconInfo? info = icon_theme.lookup_by_gicon(gicon, ICON_SIZE, 0); - if (info == null) - return null; - - icon = info.load_icon(); - if (icon == null) - return null; - - icon_cache.set(gicon.to_string(), icon); - - return icon; - } catch (Error err) { - warning("Unable to load icon %s: %s", gicon.to_string(), err.message); - - return null; - } + private void on_sidebar_destroying_page(Sidebar.PageRepresentative entry, Page page) { + destroying_page(entry, page); } private void load_entry_icons(Gtk.TreeIter iter) { EntryWrapper? wrapper = get_wrapper_at_iter(iter); if (wrapper == null) return; - - Icon? icon = wrapper.entry.get_sidebar_icon(); - Icon? open = null; - Icon? closed = null; - - Sidebar.ExpandableEntry? expandable = wrapper.entry as Sidebar.ExpandableEntry; - if (expandable != null) { - open = expandable.get_sidebar_open_icon(); - closed = expandable.get_sidebar_closed_icon(); - } - - if (open == null) - open = icon; - - if (closed == null) - closed = icon; - - store.set(iter, Columns.PIXBUF, fetch_icon_pixbuf(icon)); - store.set(iter, Columns.OPEN_PIXBUF, fetch_icon_pixbuf(open)); - store.set(iter, Columns.CLOSED_PIXBUF, fetch_icon_pixbuf(closed)); + string? icon = wrapper.entry.get_sidebar_icon(); + store.set(iter, Columns.ICON, icon); } private void load_branch_icons(Gtk.TreeIter iter) { @@ -798,15 +793,6 @@ public class Sidebar.Tree : Gtk.TreeView { } } - private void on_theme_change() { - Gtk.TreeIter iter; - if (store.get_iter_first(out iter)) { - do { - load_branch_icons(iter); - } while (store.iter_next(ref iter)); - } - } - private bool on_selection(Gtk.TreeSelection selection, Gtk.TreeModel model, Gtk.TreePath path, bool path_currently_selected) { // only allow selection if a page is selectable @@ -879,6 +865,42 @@ public class Sidebar.Tree : Gtk.TreeView { return true; } + public bool on_toggle_row(Gtk.TreeIter iter, Gtk.TreePath path) { + // Determine whether to allow the row to toggle + EntryWrapper? wrapper = get_wrapper_at_iter(iter); + if (wrapper == null) { + return false; // don't affect things + } + + // Most of the time, only allow manual toggles + bool should_allow_toggle = expander_called_manually; + + // Cancel out the manual flag + expander_called_manually = false; + + // If we are an expanded parent entry with content + if (is_row_expanded(path) && store.iter_has_child(iter) && wrapper.entry is Sidebar.SelectableEntry) { + // We are taking a special action + expander_special_count++; + if (expander_special_count == 1) { + // Workaround that prevents arrows from double-toggling + return true; + } else { + // Toggle only if non-manual, as opposed to the usual behavior + should_allow_toggle = !should_allow_toggle; + } + } else { + // Reset the special behavior count + expander_special_count = 0; + } + + if (should_allow_toggle) { + return false; + } + // Prevent branch expansion toggle + return true; + } + public override bool button_press_event(Gdk.EventButton event) { Gtk.TreePath? path = get_path_from_event(event); @@ -893,19 +915,29 @@ public class Sidebar.Tree : Gtk.TreeView { popup_context_menu(path, event); else popup_default_context_menu(event); - } else if (event.button == 1 && event.type == Gdk.EventType.2BUTTON_PRESS) { - // double left click - if (path != null) { + } else if (event.button == 1 && event.type == Gdk.EventType.BUTTON_PRESS) { + if (path == null) { + old_path_ref = null; + return base.button_press_event(event); + } + + EntryWrapper? wrapper = get_wrapper_at_path(path); + + if (wrapper == null) { + old_path_ref = null; + return base.button_press_event(event); + } + + // Enable single click to toggle tree entries (bug 4985) + if (wrapper.entry is Sidebar.ExpandableEntry + || wrapper.entry is Sidebar.InternalDropTargetEntry) { + // all labels are InternalDropTargetEntries toggle_branch_expansion(path, false); - - if (can_rename_path(path)) - return false; } - } else if (event.button == 1 && event.type == Gdk.EventType.BUTTON_PRESS) { + // Is this a click on an already-highlighted tree item? - Gtk.TreePath? cursor_path = null; - get_cursor(out cursor_path, null); - if ((cursor_path != null) && (cursor_path.compare(path) == 0)) { + if ((old_path_ref != null) && (old_path_ref.get_path() != null) + && (old_path_ref.get_path().compare(path) == 0)) { // yes, don't allow single-click editing, but // pass the event on for dragging. text_renderer.editable = false; @@ -914,9 +946,13 @@ public class Sidebar.Tree : Gtk.TreeView { // Got click on different tree item, make sure it is editable // if it needs to be. - if (path != null && get_wrapper_at_path(path).entry is Sidebar.RenameableEntry) { + if (wrapper.entry is Sidebar.RenameableEntry && + ((Sidebar.RenameableEntry) wrapper.entry).is_user_renameable()) { text_renderer.editable = true; } + + // Remember what tree item is highlighted for next time. + old_path_ref = new Gtk.TreeRowReference(store, path); } return base.button_press_event(event); @@ -1106,6 +1142,9 @@ public class Sidebar.Tree : Gtk.TreeView { if (renameable == null) return false; + if (wrapper.entry is Sidebar.Header) + return false; + get_selection().select_path(path); return true; diff --git a/src/sidebar/common.vala b/src/sidebar/common.vala index 36adfff..0f1bc05 100644 --- a/src/sidebar/common.vala +++ b/src/sidebar/common.vala @@ -1,39 +1,42 @@ -/* Copyright 2011-2014 Yorba Foundation +/* Copyright 2011-2015 Yorba Foundation * * This software is licensed under the GNU Lesser General Public License * (version 2.1 or later). See the COPYING file in this distribution. */ // A simple grouping Entry that is only expandable -public class Sidebar.Grouping : Object, Sidebar.Entry, Sidebar.ExpandableEntry { +public class Sidebar.Grouping : Object, Sidebar.Entry, Sidebar.ExpandableEntry, + Sidebar.RenameableEntry { + private string name; - private Icon? open_icon; - private Icon? closed_icon; + private string? tooltip; + private string? icon; - public Grouping(string name, Icon? open_icon, Icon? closed_icon = null) { + public Grouping(string name, string? icon, string? tooltip = null) { this.name = name; - this.open_icon = open_icon; - this.closed_icon = closed_icon ?? open_icon; + this.icon = icon; + this.tooltip = tooltip; } - public string get_sidebar_name() { - return name; + public void rename(string name) { + this.name = name; + sidebar_name_changed(name); } - public string? get_sidebar_tooltip() { - return name; + public bool is_user_renameable() { + return false; } - public Icon? get_sidebar_icon() { - return null; + public string get_sidebar_name() { + return name; } - public Icon? get_sidebar_open_icon() { - return open_icon; + public string? get_sidebar_tooltip() { + return tooltip; } - public Icon? get_sidebar_closed_icon() { - return closed_icon; + public string? get_sidebar_icon() { + return icon; } public string to_string() { @@ -61,7 +64,7 @@ public abstract class Sidebar.SimplePageEntry : Object, Sidebar.Entry, Sidebar.S return get_sidebar_name(); } - public abstract Icon? get_sidebar_icon(); + public abstract string? get_sidebar_icon(); public virtual string to_string() { return get_sidebar_name(); @@ -107,6 +110,26 @@ public class Sidebar.RootOnlyBranch : Sidebar.Branch { } } +/** + * A header is an entry that is visually distinguished from its children. Bug 6397 recommends + * headers to appear bolded and without any icons. To prevent the icons from rendering, we set the + * icons to null in the base class @see Sidebar.Grouping. But we also go a step further by + * using a custom cell_data_function (@see Sidebar.Tree::icon_renderer_function) which ensures that + * header icons won't be rendered. This approach avoids the blank icon spacing issues. + */ +public class Sidebar.Header : Sidebar.Grouping, Sidebar.EmphasizableEntry { + private bool emphasized; + + public Header(string name, bool emphasized = true) { + base(name, null); + this.emphasized = emphasized; + } + + public bool is_emphasized() { + return emphasized; + } +} + public interface Sidebar.Contextable : Object { // Return null if the context menu should not be invoked for this event public abstract Gtk.Menu? get_sidebar_context_menu(Gdk.EventButton? event); -- cgit v1.2.3