summaryrefslogtreecommitdiff
path: root/src/plugins/ManifestWidget.vala
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/ManifestWidget.vala')
-rw-r--r--src/plugins/ManifestWidget.vala282
1 files changed, 282 insertions, 0 deletions
diff --git a/src/plugins/ManifestWidget.vala b/src/plugins/ManifestWidget.vala
new file mode 100644
index 0000000..54f2e56
--- /dev/null
+++ b/src/plugins/ManifestWidget.vala
@@ -0,0 +1,282 @@
+/* Copyright 2011-2014 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.
+ */
+
+namespace Plugins {
+
+public class ManifestWidgetMediator {
+ public Gtk.Widget widget {
+ get {
+ return builder.get_object("plugin-manifest") as Gtk.Widget;
+ }
+ }
+
+ private Gtk.Button about_button {
+ get {
+ return builder.get_object("about-plugin-button") as Gtk.Button;
+ }
+ }
+
+ private Gtk.ScrolledWindow list_bin {
+ get {
+ return builder.get_object("plugin-list-scrolled-window") as Gtk.ScrolledWindow;
+ }
+ }
+
+ private Gtk.Builder builder = AppWindow.create_builder();
+ private ManifestListView list = new ManifestListView();
+
+ public ManifestWidgetMediator() {
+ list_bin.add_with_viewport(list);
+
+ about_button.clicked.connect(on_about);
+ list.get_selection().changed.connect(on_selection_changed);
+
+ set_about_button_sensitivity();
+ }
+
+ ~ManifestWidgetMediator() {
+ about_button.clicked.disconnect(on_about);
+ list.get_selection().changed.disconnect(on_selection_changed);
+ }
+
+ private void on_about() {
+ string[] ids = list.get_selected_ids();
+ if (ids.length == 0)
+ return;
+
+ string id = ids[0];
+
+ Spit.PluggableInfo info = Spit.PluggableInfo();
+ if (!get_pluggable_info(id, ref info)) {
+ warning("Unable to retrieve information for plugin %s", id);
+
+ return;
+ }
+
+ // prepare authors names (which are comma-delimited by the plugin) for the about box
+ // (which wants an array of names)
+ string[]? authors = null;
+ if (info.authors != null) {
+ string[] split = info.authors.split(",");
+ for (int ctr = 0; ctr < split.length; ctr++) {
+ string stripped = split[ctr].strip();
+ if (!is_string_empty(stripped)) {
+ if (authors == null)
+ authors = new string[0];
+
+ authors += stripped;
+ }
+ }
+ }
+
+ Gtk.AboutDialog about_dialog = new Gtk.AboutDialog();
+ about_dialog.authors = authors;
+ about_dialog.comments = info.brief_description;
+ about_dialog.copyright = info.copyright;
+ about_dialog.license = info.license;
+ about_dialog.wrap_license = info.is_license_wordwrapped;
+ about_dialog.logo = (info.icons != null && info.icons.length > 0) ? info.icons[0] :
+ Resources.get_icon(Resources.ICON_GENERIC_PLUGIN);
+ about_dialog.program_name = get_pluggable_name(id);
+ about_dialog.translator_credits = info.translators;
+ about_dialog.version = info.version;
+ about_dialog.website = info.website_url;
+ about_dialog.website_label = info.website_name;
+
+ about_dialog.run();
+
+ about_dialog.destroy();
+ }
+
+ private void on_selection_changed() {
+ set_about_button_sensitivity();
+ }
+
+ private void set_about_button_sensitivity() {
+ // have to get the array and then get its length rather than do so in one call due to a
+ // bug in Vala 0.10:
+ // list.get_selected_ids().length -> uninitialized value
+ // this appears to be fixed in Vala 0.11
+ string[] ids = list.get_selected_ids();
+ about_button.sensitive = (ids.length == 1);
+ }
+}
+
+private class ManifestListView : Gtk.TreeView {
+ private const int ICON_SIZE = 24;
+ private const int ICON_X_PADDING = 6;
+ private const int ICON_Y_PADDING = 2;
+
+ private enum Column {
+ ENABLED,
+ CAN_ENABLE,
+ ICON,
+ NAME,
+ ID,
+ N_COLUMNS
+ }
+
+ private Gtk.TreeStore store = new Gtk.TreeStore(Column.N_COLUMNS,
+ typeof(bool), // ENABLED
+ typeof(bool), // CAN_ENABLE
+ typeof(Gdk.Pixbuf), // ICON
+ typeof(string), // NAME
+ typeof(string) // ID
+ );
+
+ public ManifestListView() {
+ set_model(store);
+
+ Gtk.CellRendererToggle checkbox_renderer = new Gtk.CellRendererToggle();
+ checkbox_renderer.radio = false;
+ checkbox_renderer.activatable = true;
+
+ Gtk.CellRendererPixbuf icon_renderer = new Gtk.CellRendererPixbuf();
+ icon_renderer.stock_size = Gtk.IconSize.MENU;
+ icon_renderer.xpad = ICON_X_PADDING;
+ icon_renderer.ypad = ICON_Y_PADDING;
+
+ Gtk.CellRendererText text_renderer = new Gtk.CellRendererText();
+
+ Gtk.TreeViewColumn column = new Gtk.TreeViewColumn();
+ column.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE);
+ column.pack_start(checkbox_renderer, false);
+ column.pack_start(icon_renderer, false);
+ column.pack_end(text_renderer, true);
+
+ column.add_attribute(checkbox_renderer, "active", Column.ENABLED);
+ column.add_attribute(checkbox_renderer, "visible", Column.CAN_ENABLE);
+ column.add_attribute(icon_renderer, "pixbuf", Column.ICON);
+ column.add_attribute(text_renderer, "text", Column.NAME);
+
+ append_column(column);
+
+ set_headers_visible(false);
+ set_enable_search(false);
+ set_rules_hint(true);
+ set_show_expanders(true);
+ set_reorderable(false);
+ set_enable_tree_lines(false);
+ set_grid_lines(Gtk.TreeViewGridLines.NONE);
+ get_selection().set_mode(Gtk.SelectionMode.BROWSE);
+
+ Gtk.IconTheme icon_theme = Resources.get_icon_theme_engine();
+
+ // create a list of plugins (sorted by name) that are separated by extension points (sorted
+ // by name)
+ foreach (ExtensionPoint extension_point in get_extension_points(compare_extension_point_names)) {
+ Gtk.TreeIter category_iter;
+ store.append(out category_iter, null);
+
+ Gdk.Pixbuf? icon = null;
+ if (extension_point.icon_name != null) {
+ Gtk.IconInfo? icon_info = icon_theme.lookup_by_gicon(
+ new ThemedIcon(extension_point.icon_name), ICON_SIZE, 0);
+ if (icon_info != null) {
+ try {
+ icon = icon_info.load_icon();
+ } catch (Error err) {
+ warning("Unable to load icon %s: %s", extension_point.icon_name, err.message);
+ }
+ }
+ }
+
+ store.set(category_iter, Column.NAME, extension_point.name, Column.CAN_ENABLE, false,
+ Column.ICON, icon);
+
+ Gee.Collection<Spit.Pluggable> pluggables = get_pluggables_for_type(
+ extension_point.pluggable_type, compare_pluggable_names, true);
+ foreach (Spit.Pluggable pluggable in pluggables) {
+ bool enabled;
+ if (!get_pluggable_enabled(pluggable.get_id(), out enabled))
+ continue;
+
+ Spit.PluggableInfo info = Spit.PluggableInfo();
+ pluggable.get_info(ref info);
+
+ icon = (info.icons != null && info.icons.length > 0)
+ ? info.icons[0]
+ : Resources.get_icon(Resources.ICON_GENERIC_PLUGIN, ICON_SIZE);
+
+ Gtk.TreeIter plugin_iter;
+ store.append(out plugin_iter, category_iter);
+
+ store.set(plugin_iter, Column.ENABLED, enabled, Column.NAME, pluggable.get_pluggable_name(),
+ Column.ID, pluggable.get_id(), Column.CAN_ENABLE, true, Column.ICON, icon);
+ }
+ }
+
+ expand_all();
+ }
+
+ public string[] get_selected_ids() {
+ string[] ids = new string[0];
+
+ List<Gtk.TreePath> selected = get_selection().get_selected_rows(null);
+ foreach (Gtk.TreePath path in selected) {
+ Gtk.TreeIter iter;
+ string? id = get_id_at_path(path, out iter);
+ if (id != null)
+ ids += id;
+ }
+
+ return ids;
+ }
+
+ private string? get_id_at_path(Gtk.TreePath path, out Gtk.TreeIter iter) {
+ if (!store.get_iter(out iter, path))
+ return null;
+
+ unowned string id;
+ store.get(iter, Column.ID, out id);
+
+ return id;
+ }
+
+ // Because we want each row to left-align and not for each column to line up in a grid
+ // (otherwise the checkboxes -- hidden or not -- would cause the rest of the row to line up
+ // along the icon's left edge), we put all the renderers into a single column. However, the
+ // checkbox renderer then triggers its "toggle" signal any time the row is single-clicked,
+ // whether or not the actual checkbox hit-tests.
+ //
+ // The only way found to work around this is to capture the button-down event and do our own
+ // hit-testing.
+ public override bool button_press_event(Gdk.EventButton event) {
+ Gtk.TreePath path;
+ Gtk.TreeViewColumn col;
+ int cellx;
+ int celly;
+ if (!get_path_at_pos((int) event.x, (int) event.y, out path, out col, out cellx,
+ out celly))
+ return base.button_press_event(event);
+
+ // Perform custom hit testing as described above. The first cell in the column is offset
+ // from the left edge by whatever size the group description icon is allocated (including
+ // padding).
+ if (cellx < (ICON_SIZE + ICON_X_PADDING) || cellx > (2 * (ICON_X_PADDING + ICON_SIZE)))
+ return base.button_press_event(event);
+
+ Gtk.TreeIter iter;
+ string? id = get_id_at_path(path, out iter);
+ if (id == null)
+ return base.button_press_event(event);
+
+ bool enabled;
+ if (!get_pluggable_enabled(id, out enabled))
+ return base.button_press_event(event);
+
+ // toggle and set
+ enabled = !enabled;
+ set_pluggable_enabled(id, enabled);
+
+ store.set(iter, Column.ENABLED, enabled);
+
+ return true;
+ }
+}
+
+}
+