diff options
Diffstat (limited to 'src/gui/iconSelectWindow.vala')
-rw-r--r-- | src/gui/iconSelectWindow.vala | 349 |
1 files changed, 349 insertions, 0 deletions
diff --git a/src/gui/iconSelectWindow.vala b/src/gui/iconSelectWindow.vala new file mode 100644 index 0000000..2274ec5 --- /dev/null +++ b/src/gui/iconSelectWindow.vala @@ -0,0 +1,349 @@ +/* +Copyright (c) 2011 by Simon Schneegans + +This program is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the Free +Software Foundation, either version 3 of the License, or (at your option) +any later version. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +more details. + +You should have received a copy of the GNU General Public License along with +this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +namespace GnomePie { + +///////////////////////////////////////////////////////////////////////// +/// A window which allows selection of an Icon of the user's current icon +/// theme. Loading of Icons happens in an extra thread and a spinner is +/// displayed while loading. +///////////////////////////////////////////////////////////////////////// + +public class IconSelectWindow : Gtk.Dialog { + + private static Gtk.ListStore icon_list = null; + + private static bool loading {get; set; default = false;} + private static bool need_reload {get; set; default = true;} + + private const string disabled_contexts = "Animations, FileSystems, MimeTypes"; + private Gtk.TreeModelFilter icon_list_filtered = null; + private Gtk.IconView icon_view = null; + private Gtk.Spinner spinner = null; + + private Gtk.FileChooserWidget file_chooser = null; + + private Gtk.Notebook tabs = null; + + private class ListEntry { + public string name; + public IconContext context; + public Gdk.Pixbuf pixbuf; + } + + private GLib.AsyncQueue<ListEntry?> load_queue; + + private enum IconContext { + ALL, + APPS, + ACTIONS, + PLACES, + FILES, + EMOTES, + OTHER + } + + public string _active_icon = "application-default-icon"; + + public string active_icon { + get { + return _active_icon; + } + set { + if (value.contains("/")) { + this.file_chooser.set_filename(value); + this.tabs.set_current_page(1); + } else { + this.icon_list_filtered.foreach((model, path, iter) => { + string name = ""; + model.get(iter, 0, out name); + + if (name == value) { + this.icon_view.select_path(path); + this.icon_view.scroll_to_path(path, true, 0.5f, 0.0f); + this.icon_view.set_cursor(path, null, false); + } + return (name == value); + }); + + this.tabs.set_current_page(0); + } + } + } + + public signal void on_select(string icon_name); + + public IconSelectWindow() { + this.title = _("Choose an Icon"); + this.set_size_request(520, 520); + this.delete_event.connect(hide_on_delete); + this.load_queue = new GLib.AsyncQueue<ListEntry?>(); + + if (this.icon_list == null) { + this.icon_list = new Gtk.ListStore(3, typeof(string), typeof(IconContext), typeof(Gdk.Pixbuf)); + this.icon_list.set_default_sort_func(() => {return 0;}); + + Gtk.IconTheme.get_default().changed.connect(() => { + if (this.visible) load_icons(); + else need_reload = true; + }); + } + + this.icon_list_filtered = new Gtk.TreeModelFilter(this.icon_list, null); + + var container = new Gtk.VBox(false, 12); + container.set_border_width(12); + + // tab container + this.tabs = new Gtk.Notebook(); + + var theme_tab = new Gtk.VBox(false, 12); + theme_tab.set_border_width(12); + + var context_combo = new Gtk.ComboBox.text(); + context_combo.append_text(_("All icons")); + context_combo.append_text(_("Applications")); + context_combo.append_text(_("Actions")); + context_combo.append_text(_("Places")); + context_combo.append_text(_("File types")); + context_combo.append_text(_("Emotes")); + context_combo.append_text(_("Miscellaneous")); + + context_combo.set_active(0); + + context_combo.changed.connect(() => { + this.icon_list_filtered.refilter(); + }); + + theme_tab.pack_start(context_combo, false, false); + + var filter = new Gtk.Entry(); + filter.primary_icon_stock = Gtk.Stock.FIND; + filter.primary_icon_activatable = false; + filter.secondary_icon_stock = Gtk.Stock.CLEAR; + theme_tab.pack_start(filter, false, false); + + this.icon_list_filtered.set_visible_func((model, iter) => { + string name = ""; + IconContext context = IconContext.ALL; + model.get(iter, 0, out name); + model.get(iter, 1, out context); + + if (name == null) return false; + + return (context_combo.get_active() == context || + context_combo.get_active() == IconContext.ALL) && + name.down().contains(filter.text.down()); + }); + + filter.icon_release.connect((pos, event) => { + if (pos == Gtk.EntryIconPosition.SECONDARY) + filter.text = ""; + }); + + filter.notify["text"].connect(() => { + this.icon_list_filtered.refilter(); + }); + + var scroll = new Gtk.ScrolledWindow (null, null); + scroll.set_policy (Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC); + scroll.set_shadow_type (Gtk.ShadowType.IN); + + this.icon_view = new Gtk.IconView.with_model(this.icon_list_filtered); + this.icon_view.item_width = 32; + this.icon_view.item_padding = 3; + this.icon_view.pixbuf_column = 2; + this.icon_view.tooltip_column = 0; + + this.icon_view.selection_changed.connect(() => { + foreach (var path in this.icon_view.get_selected_items()) { + Gtk.TreeIter iter; + this.icon_list_filtered.get_iter(out iter, path); + icon_list_filtered.get(iter, 0, out this._active_icon); + } + }); + + this.icon_view.item_activated.connect((path) => { + Gtk.TreeIter iter; + this.icon_list_filtered.get_iter(out iter, path); + this.icon_list_filtered.get(iter, 0, out this._active_icon); + this.on_select(this._active_icon); + this.hide(); + }); + + scroll.add(this.icon_view); + + theme_tab.pack_start(scroll, true, true); + + tabs.append_page(theme_tab, new Gtk.Label(_("Icon Theme"))); + + var custom_tab = new Gtk.VBox(false, 6); + custom_tab.border_width = 12; + + this.file_chooser = new Gtk.FileChooserWidget(Gtk.FileChooserAction.OPEN); + var file_filter = new Gtk.FileFilter(); + file_filter.add_pixbuf_formats(); + file_filter.set_name(_("All supported image formats")); + file_chooser.add_filter(file_filter); + + file_chooser.selection_changed.connect(() => { + if (file_chooser.get_filename() != null && GLib.FileUtils.test(file_chooser.get_filename(), GLib.FileTest.IS_REGULAR)) + this._active_icon = file_chooser.get_filename(); + }); + + file_chooser.file_activated.connect(() => { + this._active_icon = file_chooser.get_filename(); + this.on_select(this._active_icon); + this.hide(); + }); + + + custom_tab.pack_start(file_chooser, true, true); + + tabs.append_page(custom_tab, new Gtk.Label(_("Custom Icon"))); + + container.pack_start(tabs, true, true); + + // button box + var bottom_box = new Gtk.HBox(false, 0); + + var bbox = new Gtk.HButtonBox(); + bbox.set_spacing(6); + bbox.set_layout(Gtk.ButtonBoxStyle.END); + + var cancel_button = new Gtk.Button.from_stock(Gtk.Stock.CANCEL); + cancel_button.clicked.connect(() => { + this.hide(); + }); + bbox.pack_start(cancel_button); + + var ok_button = new Gtk.Button.from_stock(Gtk.Stock.OK); + ok_button.clicked.connect(() => { + this.on_select(this._active_icon); + this.hide(); + }); + bbox.pack_start(ok_button); + + bottom_box.pack_end(bbox, false); + + this.spinner = new Gtk.Spinner(); + this.spinner.set_size_request(16, 16); + this.spinner.start(); + + bottom_box.pack_start(this.spinner, false, false); + + container.pack_start(bottom_box, false, false); + + this.vbox.pack_start(container, true, true); + + this.vbox.show_all(); + + this.set_focus(this.icon_view); + } + + public override void show() { + base.show(); + this.action_area.hide(); + + if (this.need_reload) { + this.need_reload = false; + this.load_icons(); + } + } + + private void load_icons() { + if (!this.loading) { + this.loading = true; + this.icon_list.clear(); + + if (spinner != null) + this.spinner.visible = true; + + this.icon_list.set_sort_column_id(-1, Gtk.SortType.ASCENDING); + + try { + unowned Thread loader = Thread.create<void*>(load_thread, false); + loader.set_priority(ThreadPriority.LOW); + } catch (GLib.ThreadError e) { + error("Failed to create icon loader thread!"); + } + + Timeout.add(200, () => { + while (this.load_queue.length() > 0) { + var new_entry = this.load_queue.pop(); + Gtk.TreeIter current; + this.icon_list.append(out current); + this.icon_list.set(current, 0, new_entry.name, + 1, new_entry.context, + 2, new_entry.pixbuf); + } + + if (!this.loading) this.icon_list.set_sort_column_id(0, Gtk.SortType.ASCENDING); + + return loading; + }); + } + } + + private void* load_thread() { + var icon_theme = Gtk.IconTheme.get_default(); + + foreach (var context in icon_theme.list_contexts()) { + if (!disabled_contexts.contains(context)) { + foreach (var icon in icon_theme.list_icons(context)) { + IconContext icon_context = IconContext.OTHER; + switch(context) { + case "Apps": case "Applications": + icon_context = IconContext.APPS; break; + case "Emotes": + icon_context = IconContext.EMOTES; break; + case "Places": case "Devices": + icon_context = IconContext.PLACES; break; + case "Mimetypes": + icon_context = IconContext.FILES; break; + case "Actions": + icon_context = IconContext.ACTIONS; break; + default: break; + } + + try { + var new_entry = new ListEntry(); + new_entry.name = icon; + new_entry.context = icon_context; + new_entry.pixbuf = icon_theme.load_icon(icon, 32, 0); + + // some icons have only weird sizes... do not include them + if (new_entry.pixbuf.width == 32) + this.load_queue.push(new_entry); + + } catch (GLib.Error e) { + warning("Failed to load image " + icon); + } + } + } + } + + this.loading = false; + + if (spinner != null) + spinner.visible = this.loading; + + return null; + } +} + +} |