summaryrefslogtreecommitdiff
path: root/src/gui
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui')
-rw-r--r--src/gui/about.vala34
-rw-r--r--src/gui/cellRendererTrigger.vala84
-rw-r--r--src/gui/iconSelectWindow.vala200
-rw-r--r--src/gui/pieList.vala40
-rw-r--r--src/gui/preferences.vala29
-rw-r--r--src/gui/triggerSelectWindow.vala257
6 files changed, 574 insertions, 70 deletions
diff --git a/src/gui/about.vala b/src/gui/about.vala
index 1ace9cb..ce4256e 100644
--- a/src/gui/about.vala
+++ b/src/gui/about.vala
@@ -24,18 +24,44 @@ namespace GnomePie {
public class GnomePieAboutDialog: Gtk.AboutDialog {
public GnomePieAboutDialog () {
- string[] devs = {"Simon Schneegans <code@simonschneegans.de>",
- "Francesco Piccinno"};
- string[] artists = {"Simon Schneegans <code@simonschneegans.de>"};
+ string[] devs = {
+ "Simon Schneegans <code@simonschneegans.de>",
+ "Francesco Piccinno <stack.box@gmail.com>"
+ };
+ string[] artists = {
+ "Simon Schneegans <code@simonschneegans.de>"
+ };
+ string[] translators = {
+ "DE\t\t Simon Schneegans <code@simonschneegans.de>",
+ "IT\t\t Riccardo Traverso <gr3yfox.fw@gmail.com>",
+ "PT-BR\t Magnun Leno <magnun@codecommunity.org>",
+ "EN\t\t Simon Schneegans <code@simonschneegans.de>",
+ "KO\t\t Kim Boram <Boramism@gmail.com>"
+ };
+
+ // sort translators
+ GLib.List<string> translator_list = new GLib.List<string>();
+ foreach (var translator in translators)
+ translator_list.append(translator);
+
+ translator_list.sort((a, b) => {
+ return a.ascii_casecmp(b);
+ });
+
+ string translator_string = "";
+ foreach (var translator in translator_list)
+ translator_string += translator + "\n";
+
GLib.Object (
artists : artists,
authors : devs,
+ translator_credits : translator_string,
copyright : "Copyright (C) 2011 Simon Schneegans <code@simonschneegans.de>",
program_name: "Gnome-Pie",
logo_icon_name: "gnome-pie",
website: "http://www.simonschneegans.de/?page_id=12",
website_label: "www.gnome-pie.simonschneegans.de",
- version: "0.2"
+ version: "0.3.1"
);
}
}
diff --git a/src/gui/cellRendererTrigger.vala b/src/gui/cellRendererTrigger.vala
new file mode 100644
index 0000000..a825c32
--- /dev/null
+++ b/src/gui/cellRendererTrigger.vala
@@ -0,0 +1,84 @@
+/*
+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 CellRenderer which opens a TriggerSelectWindow.
+/////////////////////////////////////////////////////////////////////////
+
+public class CellRendererTrigger : Gtk.CellRendererText {
+
+ /////////////////////////////////////////////////////////////////////
+ /// This signal is emitted when the user selects another trigger.
+ /////////////////////////////////////////////////////////////////////
+
+ public signal void on_select(string path, Trigger trigger);
+
+ /////////////////////////////////////////////////////////////////////
+ /// The trigger which can be set with this window.
+ /////////////////////////////////////////////////////////////////////
+
+ public string trigger { get; set; }
+
+ /////////////////////////////////////////////////////////////////////
+ /// The IconSelectWindow which is shown on click.
+ /////////////////////////////////////////////////////////////////////
+
+ private TriggerSelectWindow select_window = null;
+
+ /////////////////////////////////////////////////////////////////////
+ /// A helper variable, needed to emit the current path.
+ /////////////////////////////////////////////////////////////////////
+
+ private string current_path = "";
+
+ /////////////////////////////////////////////////////////////////////
+ /// C'tor, creates a new CellRendererIcon.
+ /////////////////////////////////////////////////////////////////////
+
+ public CellRendererTrigger() {
+ this.select_window = new TriggerSelectWindow();
+
+ this.select_window.on_select.connect((trigger) => {
+ this.trigger = trigger.name;
+ this.on_select(current_path, trigger);
+ });
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Open the TriggerSelectWindow on click.
+ /////////////////////////////////////////////////////////////////////
+
+ public override unowned Gtk.CellEditable start_editing(
+ Gdk.Event event, Gtk.Widget widget, string path, Gdk.Rectangle bg_area,
+ Gdk.Rectangle cell_area, Gtk.CellRendererState flags) {
+
+ this.current_path = path;
+
+ this.select_window.set_transient_for((Gtk.Window)widget.get_toplevel());
+ this.select_window.set_modal(true);
+ this.select_window.set_trigger(new Trigger.from_string(this.trigger));
+
+ this.select_window.show();
+
+ return base.start_editing(event, widget, path, bg_area, cell_area, flags);
+ }
+}
+
+}
+
diff --git a/src/gui/iconSelectWindow.vala b/src/gui/iconSelectWindow.vala
index 2274ec5..01a4a40 100644
--- a/src/gui/iconSelectWindow.vala
+++ b/src/gui/iconSelectWindow.vala
@@ -19,45 +19,17 @@ 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.
+/// theme. Custom icons/images can be selested as well. 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;}
+ /////////////////////////////////////////////////////////////////////
+ /// The currently selected icon. If set, this icon gets focused.
+ /////////////////////////////////////////////////////////////////////
- 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";
+ private string _active_icon = "application-default-icon";
public string active_icon {
get {
@@ -85,7 +57,104 @@ public class IconSelectWindow : Gtk.Dialog {
}
}
+ /////////////////////////////////////////////////////////////////////
+ /// This signal gets emitted when the user selects a new icon.
+ /////////////////////////////////////////////////////////////////////
+
public signal void on_select(string icon_name);
+
+ /////////////////////////////////////////////////////////////////////
+ /// The ListStore storing all theme-icons.
+ /////////////////////////////////////////////////////////////////////
+
+ private static Gtk.ListStore icon_list = null;
+
+ /////////////////////////////////////////////////////////////////////
+ /// True, if the icon theme is currently reloaded.
+ /////////////////////////////////////////////////////////////////////
+
+ private static bool loading = false;
+
+ /////////////////////////////////////////////////////////////////////
+ /// If set to true, the icon list will be reloaded next time the
+ /// window opens.
+ /////////////////////////////////////////////////////////////////////
+
+ private static bool need_reload = true;
+
+ /////////////////////////////////////////////////////////////////////
+ /// Icons of these contexts won't appear in the list.
+ /////////////////////////////////////////////////////////////////////
+
+ private const string disabled_contexts = "Animations, FileSystems";
+
+ /////////////////////////////////////////////////////////////////////
+ /// The list of icons, filtered according to the chosen type and
+ /// filter string.
+ /////////////////////////////////////////////////////////////////////
+
+ private Gtk.TreeModelFilter icon_list_filtered = null;
+
+ /////////////////////////////////////////////////////////////////////
+ /// The Gtk widget displaying the icons.
+ /////////////////////////////////////////////////////////////////////
+
+ private Gtk.IconView icon_view = null;
+
+ /////////////////////////////////////////////////////////////////////
+ /// This spinner is displayed when the icons are loaded.
+ /////////////////////////////////////////////////////////////////////
+
+ private Gtk.Spinner spinner = null;
+
+ /////////////////////////////////////////////////////////////////////
+ /// A Gtk widget used for custom icon/image selection.
+ /////////////////////////////////////////////////////////////////////
+
+ private Gtk.FileChooserWidget file_chooser = null;
+
+ /////////////////////////////////////////////////////////////////////
+ /// The notebook containing the different icon choice possibilities:
+ /// from the theme or custom.
+ /////////////////////////////////////////////////////////////////////
+
+ private Gtk.Notebook tabs = null;
+
+ /////////////////////////////////////////////////////////////////////
+ /// A little structure containing data for one icon in the icon_view.
+ /////////////////////////////////////////////////////////////////////
+
+ private class ListEntry {
+ public string name;
+ public IconContext context;
+ public Gdk.Pixbuf pixbuf;
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// This queue is used for icon loading. A loading thread pushes
+ /// icons into it --- the main thread updates the icon_view
+ /// accordingly.
+ /////////////////////////////////////////////////////////////////////
+
+ private GLib.AsyncQueue<ListEntry?> load_queue;
+
+ /////////////////////////////////////////////////////////////////////
+ /// Possible icon types.
+ /////////////////////////////////////////////////////////////////////
+
+ private enum IconContext {
+ ALL,
+ APPS,
+ ACTIONS,
+ PLACES,
+ FILES,
+ EMOTES,
+ OTHER
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// C'tor, creates a new IconSelectWindow.
+ /////////////////////////////////////////////////////////////////////
public IconSelectWindow() {
this.title = _("Choose an Icon");
@@ -94,15 +163,22 @@ public class IconSelectWindow : Gtk.Dialog {
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 = new Gtk.ListStore(3, typeof(string), // icon name
+ typeof(IconContext), // icon type
+ typeof(Gdk.Pixbuf)); // the icon itself
+
+ // disable sorting until all icons are loaded
+ // else loading becomes horribly slow
this.icon_list.set_default_sort_func(() => {return 0;});
+ // reload if icon theme changes
Gtk.IconTheme.get_default().changed.connect(() => {
if (this.visible) load_icons();
else need_reload = true;
});
}
+ // make the icon_view filterable
this.icon_list_filtered = new Gtk.TreeModelFilter(this.icon_list, null);
var container = new Gtk.VBox(false, 12);
@@ -111,9 +187,11 @@ public class IconSelectWindow : Gtk.Dialog {
// tab container
this.tabs = new Gtk.Notebook();
+ // icon theme tab
var theme_tab = new Gtk.VBox(false, 12);
theme_tab.set_border_width(12);
+ // type chooser combo-box
var context_combo = new Gtk.ComboBox.text();
context_combo.append_text(_("All icons"));
context_combo.append_text(_("Applications"));
@@ -130,13 +208,16 @@ public class IconSelectWindow : Gtk.Dialog {
});
theme_tab.pack_start(context_combo, false, false);
-
+
+ // string filter entry
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);
+ // only display items which have the selected type
+ // and whose name contains the text entered in the entry
this.icon_list_filtered.set_visible_func((model, iter) => {
string name = "";
IconContext context = IconContext.ALL;
@@ -150,33 +231,39 @@ public class IconSelectWindow : Gtk.Dialog {
name.down().contains(filter.text.down());
});
+ // clear when the users clicks on the "clear" icon
filter.icon_release.connect((pos, event) => {
if (pos == Gtk.EntryIconPosition.SECONDARY)
filter.text = "";
});
+ // refilter on input
filter.notify["text"].connect(() => {
this.icon_list_filtered.refilter();
});
+ // container for the icon_view
var scroll = new Gtk.ScrolledWindow (null, null);
scroll.set_policy (Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
scroll.set_shadow_type (Gtk.ShadowType.IN);
+ // displays the filtered icons
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;
+ // set _active_icon if selection changes
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_list_filtered.get(iter, 0, out this._active_icon);
}
});
+ // hide this window when the user activates an icon
this.icon_view.item_activated.connect((path) => {
Gtk.TreeIter iter;
this.icon_list_filtered.get_iter(out iter, path);
@@ -191,20 +278,27 @@ public class IconSelectWindow : Gtk.Dialog {
tabs.append_page(theme_tab, new Gtk.Label(_("Icon Theme")));
+ // tab containing the possibility to choose a custom icon
var custom_tab = new Gtk.VBox(false, 6);
custom_tab.border_width = 12;
+ // file chooser widget
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);
+ // set _active_icon if the user selected a file
file_chooser.selection_changed.connect(() => {
- if (file_chooser.get_filename() != null && GLib.FileUtils.test(file_chooser.get_filename(), GLib.FileTest.IS_REGULAR))
+ if (file_chooser.get_filename() != null &&
+ GLib.FileUtils.test(file_chooser.get_filename(),
+ GLib.FileTest.IS_REGULAR))
+
this._active_icon = file_chooser.get_filename();
});
+ // hide this window when the user activates a file
file_chooser.file_activated.connect(() => {
this._active_icon = file_chooser.get_filename();
this.on_select(this._active_icon);
@@ -218,7 +312,9 @@ public class IconSelectWindow : Gtk.Dialog {
container.pack_start(tabs, true, true);
- // button box
+ // button box --- this dialog has a custom button box at the bottom because it
+ // should have a spinner there. Sadly that's impossible with the "normal"
+ // action_area of Gtk.Dialog's
var bottom_box = new Gtk.HBox(false, 0);
var bbox = new Gtk.HButtonBox();
@@ -255,8 +351,16 @@ public class IconSelectWindow : Gtk.Dialog {
this.set_focus(this.icon_view);
}
+ /////////////////////////////////////////////////////////////////////
+ /// Hide the "normal" action_area when this window is shown. Reload
+ /// all icons if necessary.
+ /////////////////////////////////////////////////////////////////////
+
public override void show() {
base.show();
+
+ // hide the "normal" action_area --- this Dialog has a custom set of
+ // buttons containg the spinner
this.action_area.hide();
if (this.need_reload) {
@@ -265,23 +369,32 @@ public class IconSelectWindow : Gtk.Dialog {
}
}
+ /////////////////////////////////////////////////////////////////////
+ /// (Re)load all icons.
+ /////////////////////////////////////////////////////////////////////
+
private void load_icons() {
+ // only if it's not loading currently
if (!this.loading) {
this.loading = true;
this.icon_list.clear();
+ // display the spinner
if (spinner != null)
this.spinner.visible = true;
+ // disable sorting of the icon_view - else it's horribly slow
this.icon_list.set_sort_column_id(-1, Gtk.SortType.ASCENDING);
try {
+ // start loading in another thread
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!");
}
+ // insert loaded icons every 200 ms
Timeout.add(200, () => {
while (this.load_queue.length() > 0) {
var new_entry = this.load_queue.pop();
@@ -292,6 +405,7 @@ public class IconSelectWindow : Gtk.Dialog {
2, new_entry.pixbuf);
}
+ // enable sorting of the icon_view if loading finished
if (!this.loading) this.icon_list.set_sort_column_id(0, Gtk.SortType.ASCENDING);
return loading;
@@ -299,6 +413,11 @@ public class IconSelectWindow : Gtk.Dialog {
}
}
+ /////////////////////////////////////////////////////////////////////
+ /// Loads all icons of an icon theme and pushes them into the
+ /// load_queue.
+ /////////////////////////////////////////////////////////////////////
+
private void* load_thread() {
var icon_theme = Gtk.IconTheme.get_default();
@@ -321,6 +440,7 @@ public class IconSelectWindow : Gtk.Dialog {
}
try {
+ // create a new entry for the queue
var new_entry = new ListEntry();
new_entry.name = icon;
new_entry.context = icon_context;
@@ -337,8 +457,10 @@ public class IconSelectWindow : Gtk.Dialog {
}
}
+ // finished loading
this.loading = false;
+ // hide the spinner
if (spinner != null)
spinner.visible = this.loading;
diff --git a/src/gui/pieList.vala b/src/gui/pieList.vala
index df6135a..46970d5 100644
--- a/src/gui/pieList.vala
+++ b/src/gui/pieList.vala
@@ -34,7 +34,7 @@ class PieList : Gtk.TreeView {
private enum DataPos {IS_QUICKACTION, ICON, NAME, TYPE_ID, ACTION_TYPE,
ICON_PIXBUF, FONT_WEIGHT, ICON_NAME_EDITABLE, QUICKACTION_VISIBLE, QUICKACTION_ACTIVATABLE,
TYPE_VISIBLE, GROUP_VISIBLE, APP_VISIBLE, KEY_VISIBLE, PIE_VISIBLE,
- URI_VISIBLE, DISPLAY_COMMAND_GROUP, DISPLAY_COMMAND_APP,
+ URI_VISIBLE, TRIGGER_VISIBLE, DISPLAY_COMMAND_GROUP, DISPLAY_COMMAND_APP,
DISPLAY_COMMAND_KEY, DISPLAY_COMMAND_PIE, DISPLAY_COMMAND_URI,
REAL_COMMAND_GROUP, REAL_COMMAND_PIE, REAL_COMMAND_KEY}
@@ -91,7 +91,7 @@ class PieList : Gtk.TreeView {
ActionPos.ICON_NAME_EDITABLE, false);
// main data model
- this.data = new Gtk.TreeStore(24, typeof(bool), // is quickaction
+ this.data = new Gtk.TreeStore(25, typeof(bool), // is quickaction
typeof(string), // icon
typeof(string), // name
typeof(string), // slice: type label, pie: "ID: %id"
@@ -110,6 +110,7 @@ class PieList : Gtk.TreeView {
typeof(bool), // key renderer visible
typeof(bool), // pie renderer visible
typeof(bool), // uri renderer visible
+ typeof(bool), // trigger renderer visible
typeof(string), // display command group
typeof(string), // display command app
@@ -198,6 +199,28 @@ class PieList : Gtk.TreeView {
var command_column = new Gtk.TreeViewColumn();
command_column.title = _("Command");
command_column.resizable = true;
+ command_column.expand = true;
+
+ // trigger
+ var command_renderer_trigger = new CellRendererTrigger();
+ command_renderer_trigger.editable = true;
+ command_renderer_trigger.ellipsize = Pango.EllipsizeMode.END;
+
+ command_renderer_trigger.on_select.connect((path, trigger) => {
+ Gtk.TreeIter data_iter;
+ this.data.get_iter_from_string(out data_iter, path);
+
+ this.data.set(data_iter, DataPos.DISPLAY_COMMAND_KEY, trigger.label_with_specials);
+ this.data.set(data_iter, DataPos.REAL_COMMAND_KEY, trigger.name);
+
+ this.update_pie(data_iter);
+ });
+
+ command_column.pack_end(command_renderer_trigger, true);
+ command_column.add_attribute(command_renderer_trigger, "weight", DataPos.FONT_WEIGHT);
+ command_column.add_attribute(command_renderer_trigger, "markup", DataPos.DISPLAY_COMMAND_KEY);
+ command_column.add_attribute(command_renderer_trigger, "visible", DataPos.TRIGGER_VISIBLE);
+ command_column.add_attribute(command_renderer_trigger, "trigger", DataPos.REAL_COMMAND_KEY);
// slice group
var command_renderer_group = new Gtk.CellRendererCombo();
@@ -342,6 +365,7 @@ class PieList : Gtk.TreeView {
var type_column = new Gtk.TreeViewColumn();
type_column.title = _("Pie-ID / Action type");
type_column.resizable = true;
+ type_column.expand = false;
var type_render = new Gtk.CellRendererCombo();
type_render.editable = true;
@@ -592,7 +616,7 @@ class PieList : Gtk.TreeView {
// adds a new, empty pie to the list
private void add_empty_pie() {
- var new_one = PieManager.create_persistent_pie(_("New Pie"), "application-default-icon", "");
+ var new_one = PieManager.create_persistent_pie(_("New Pie"), "application-default-icon", null);
Gtk.TreeIter last;
this.pies.append(out last); this.pies.set(last, 0, new_one.name, 1, new_one.id);
@@ -612,9 +636,10 @@ class PieList : Gtk.TreeView {
DataPos.TYPE_VISIBLE, false,
DataPos.GROUP_VISIBLE, false,
DataPos.APP_VISIBLE, false,
- DataPos.KEY_VISIBLE, true,
+ DataPos.KEY_VISIBLE, false,
DataPos.PIE_VISIBLE, false,
DataPos.URI_VISIBLE, false,
+ DataPos.TRIGGER_VISIBLE, true,
DataPos.DISPLAY_COMMAND_GROUP, "",
DataPos.DISPLAY_COMMAND_APP, "",
DataPos.DISPLAY_COMMAND_KEY, PieManager.get_accelerator_label_of(new_one.id),
@@ -672,6 +697,7 @@ class PieList : Gtk.TreeView {
DataPos.QUICKACTION_ACTIVATABLE, true,
DataPos.TYPE_VISIBLE, true,
DataPos.GROUP_VISIBLE, false,
+ DataPos.TRIGGER_VISIBLE, false,
DataPos.APP_VISIBLE, action is AppAction,
DataPos.KEY_VISIBLE, action is KeyAction,
DataPos.PIE_VISIBLE, action is PieAction,
@@ -791,9 +817,10 @@ class PieList : Gtk.TreeView {
DataPos.TYPE_VISIBLE, false,
DataPos.GROUP_VISIBLE, false,
DataPos.APP_VISIBLE, false,
- DataPos.KEY_VISIBLE, true,
+ DataPos.KEY_VISIBLE, false,
DataPos.PIE_VISIBLE, false,
DataPos.URI_VISIBLE, false,
+ DataPos.TRIGGER_VISIBLE, true,
DataPos.DISPLAY_COMMAND_GROUP, "",
DataPos.DISPLAY_COMMAND_APP, "",
DataPos.DISPLAY_COMMAND_KEY, PieManager.get_accelerator_label_of(pie.id),
@@ -834,6 +861,7 @@ class PieList : Gtk.TreeView {
DataPos.KEY_VISIBLE, false,
DataPos.PIE_VISIBLE, false,
DataPos.URI_VISIBLE, false,
+ DataPos.TRIGGER_VISIBLE, false,
DataPos.DISPLAY_COMMAND_GROUP, GroupRegistry.names[group.get_type()],
DataPos.DISPLAY_COMMAND_APP, "",
DataPos.DISPLAY_COMMAND_KEY, _("Not bound"),
@@ -888,7 +916,7 @@ class PieList : Gtk.TreeView {
});
// create new pie
- var new_pie = PieManager.create_persistent_pie(name, icon, hotkey, id);
+ var new_pie = PieManager.create_persistent_pie(name, icon, new Trigger.from_string(hotkey), id);
// add actions accordingly
if (this.data.iter_has_child(pie)) {
diff --git a/src/gui/preferences.vala b/src/gui/preferences.vala
index f43fd4a..9444fac 100644
--- a/src/gui/preferences.vala
+++ b/src/gui/preferences.vala
@@ -82,13 +82,6 @@ public class Preferences : Gtk.Window {
open_at_mouse.toggled.connect(open_at_mouse_toggled);
behavior_vbox.pack_start(open_at_mouse, false);
- // Click to activate
- var click_to_activate = new Gtk.CheckButton.with_label (_("Turbo mode"));
- click_to_activate.tooltip_text = _("If checked, the pie closes when its keystroke is released. The currently hovered slice gets executed. This allows very fast selection but disables keyboard navigation.");
- click_to_activate.active = Config.global.turbo_mode;
- click_to_activate.toggled.connect(turbo_mode_toggled);
- behavior_vbox.pack_start(click_to_activate, false);
-
// Slider
var slider_hbox = new Gtk.HBox (false, 6);
behavior_vbox.pack_start(slider_hbox);
@@ -187,6 +180,7 @@ public class Preferences : Gtk.Window {
_("You may drag'n'drop URLs and bookmarks from your internet browser to the list above."),
_("Bugs can be reported at %s!").printf("<a href='https://github.com/Simmesimme/Gnome-Pie'>Github</a>"),
_("It's possible to drag'n'drop files and folders from your file browser to the list above."),
+ _("It's recommended to keep your Pies small (at most 6-8 Slices). Else they will become hard to navigate."),
_("In order to create a launcher for a Pie, drag the Pie from the list to your desktop!")
});
this.show.connect(info_label.start_slide_show);
@@ -251,18 +245,21 @@ public class Preferences : Gtk.Window {
// close button
var bbox = new Gtk.HButtonBox ();
bbox.set_layout (Gtk.ButtonBoxStyle.END);
- var close_button = new Gtk.Button.from_stock (Gtk.Stock.CLOSE);
+ var close_button = new Gtk.Button.from_stock(Gtk.Stock.CLOSE);
close_button.clicked.connect (() => {
hide();
- // save settings on close
- Config.global.save();
- Pies.save();
});
bbox.pack_start (close_button);
main_vbox.pack_start(bbox, false);
main_vbox.show_all();
+
+ this.hide.connect(() => {
+ // save settings on close
+ Config.global.save();
+ Pies.save();
+ });
}
/////////////////////////////////////////////////////////////////////
@@ -323,16 +320,6 @@ public class Preferences : Gtk.Window {
var check = check_box as Gtk.CheckButton;
Config.global.open_at_mouse = check.active;
}
-
- /////////////////////////////////////////////////////////////////////
- /// Toggles whether the user has to click with the mouse in order to
- /// activate a slice.
- /////////////////////////////////////////////////////////////////////
-
- private void turbo_mode_toggled(Gtk.ToggleButton check_box) {
- var check = check_box as Gtk.CheckButton;
- Config.global.turbo_mode = check.active;
- }
}
}
diff --git a/src/gui/triggerSelectWindow.vala b/src/gui/triggerSelectWindow.vala
new file mode 100644
index 0000000..e003a84
--- /dev/null
+++ b/src/gui/triggerSelectWindow.vala
@@ -0,0 +1,257 @@
+/*
+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 {
+
+/////////////////////////////////////////////////////////////////////////
+/// This window allows the selection of a hotkey. It is returned in form
+/// of a Trigger. Therefore it can be either a keyboard driven hotkey or
+/// a mouse based hotkey.
+/////////////////////////////////////////////////////////////////////////
+
+public class TriggerSelectWindow : Gtk.Dialog {
+
+ /////////////////////////////////////////////////////////////////////
+ /// This signal is emitted when the user selects a new hot key.
+ /////////////////////////////////////////////////////////////////////
+
+ public signal void on_select(Trigger trigger);
+
+ /////////////////////////////////////////////////////////////////////
+ /// Some private members which are needed by other methods.
+ /////////////////////////////////////////////////////////////////////
+
+ private Gtk.CheckButton turbo;
+ private Gtk.CheckButton delayed;
+ private Gtk.Label preview;
+
+ /////////////////////////////////////////////////////////////////////
+ /// The currently configured trigger.
+ /////////////////////////////////////////////////////////////////////
+
+ private Trigger trigger = null;
+
+ /////////////////////////////////////////////////////////////////////
+ /// The trigger which was active when this window was opened. It is
+ /// stored in order to check whether anything has changed when the
+ /// user clicks on OK.
+ /////////////////////////////////////////////////////////////////////
+
+ private Trigger original_trigger = null;
+
+ /////////////////////////////////////////////////////////////////////
+ /// These modifiers are ignored.
+ /////////////////////////////////////////////////////////////////////
+
+ private Gdk.ModifierType lock_modifiers = Gdk.ModifierType.MOD2_MASK
+ |Gdk.ModifierType.LOCK_MASK
+ |Gdk.ModifierType.MOD5_MASK;
+
+ /////////////////////////////////////////////////////////////////////
+ /// C'tor, constructs a new TriggerSelectWindow.
+ /////////////////////////////////////////////////////////////////////
+
+ public TriggerSelectWindow() {
+ this.title = _("Define an open-command");
+ this.resizable = false;
+ this.delete_event.connect(hide_on_delete);
+ this.key_press_event.connect(on_key_press);
+ this.button_press_event.connect(on_button_press);
+
+ this.show.connect_after(() => {
+ FocusGrabber.grab(this);
+ });
+
+ this.hide.connect(() => {
+ FocusGrabber.ungrab(this);
+ });
+
+ var container = new Gtk.VBox(false, 6);
+ container.set_border_width(6);
+
+ // click area
+ var click_frame = new Gtk.Frame(_("Click here if you want to bind a mouse button!"));
+
+ var click_box = new Gtk.EventBox();
+ click_box.height_request = 100;
+ click_box.button_press_event.connect(on_area_clicked);
+
+ this.preview = new Gtk.Label(null);
+
+ click_box.add(this.preview);
+
+ click_frame.add(click_box);
+
+ container.pack_start(click_frame, false);
+
+ // turbo checkbox
+ this.turbo = new Gtk.CheckButton.with_label (_("Turbo mode"));
+ this.turbo.tooltip_text = _("If checked, the Pie will close when you " +
+ "release the chosen hot key.");
+ this.turbo.active = false;
+ this.turbo.toggled.connect(() => {
+ if (this.trigger != null)
+ this.update_trigger(new Trigger.from_values(
+ this.trigger.key_sym, this.trigger.modifiers,
+ this.trigger.with_mouse, this.turbo.active,
+ this.delayed.active));
+ });
+
+ container.pack_start(turbo, false);
+
+ // delayed checkbox
+ this.delayed = new Gtk.CheckButton.with_label (_("Long press for activation"));
+ this.delayed.tooltip_text = _("If checked, the Pie will only open if you " +
+ "press this hot key a bit longer.");
+ this.delayed.active = false;
+ this.delayed.toggled.connect(() => {
+ if (this.trigger != null)
+ this.update_trigger(new Trigger.from_values(
+ this.trigger.key_sym, this.trigger.modifiers,
+ this.trigger.with_mouse, this.turbo.active,
+ this.delayed.active));
+ });
+
+ container.pack_start(delayed, false);
+
+ container.show_all();
+
+ this.vbox.pack_start(container, true, true);
+
+ this.add_button(Gtk.Stock.CANCEL, 1);
+ this.add_button(Gtk.Stock.OK, 0);
+
+ // select a new trigger on OK, hide on CANCEL
+ this.response.connect((id) => {
+ if (id == 1)
+ this.hide();
+ else if (id == 0) {
+ var assigned_id = PieManager.get_assigned_id(this.trigger);
+
+ if (this.trigger == this.original_trigger) {
+ // nothing did change
+ this.hide();
+ } else if (this.trigger.key_code == this.original_trigger.key_code
+ && this.trigger.modifiers == this.original_trigger.modifiers
+ && this.trigger.with_mouse == this.original_trigger.with_mouse) {
+ // only turbo and/or delayed mode changed, no need to check for double assignment
+ this.on_select(this.trigger);
+ this.hide();
+ } else if (assigned_id != "") {
+ // it's already assigned
+ var error = _("This hotkey is already assigned to the pie \"%s\"! \n\nPlease select " +
+ "another one or cancel your selection.").printf(PieManager.get_name_of(assigned_id));
+ var dialog = new Gtk.MessageDialog((Gtk.Window)this.get_toplevel(),
+ Gtk.DialogFlags.MODAL,
+ Gtk.MessageType.ERROR,
+ Gtk.ButtonsType.CANCEL,
+ error);
+ dialog.run();
+ dialog.destroy();
+ } else {
+ // a unused hot key has been chosen, great!
+ this.on_select(this.trigger);
+ this.hide();
+ }
+ }
+ });
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Used to set the currently selected trigger on opening.
+ /////////////////////////////////////////////////////////////////////
+
+ public void set_trigger(Trigger trigger) {
+ this.turbo.active = trigger.turbo;
+ this.delayed.active = trigger.delayed;
+ this.original_trigger = trigger;
+ this.update_trigger(trigger);
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Called when the user clicks in the click area.
+ /////////////////////////////////////////////////////////////////////
+
+ private bool on_area_clicked(Gdk.EventButton event) {
+ Gdk.ModifierType state = event.state & ~ this.lock_modifiers;
+
+ var new_trigger = new Trigger.from_values((int)event.button, state, true,
+ this.turbo.active, this.delayed.active);
+ if (new_trigger.key_code == 1) {
+ var dialog = new Gtk.MessageDialog((Gtk.Window)this.get_toplevel(), Gtk.DialogFlags.MODAL,
+ Gtk.MessageType.WARNING,
+ Gtk.ButtonsType.YES_NO,
+ _("It possible to make your system unusable if " +
+ "you bind a Pie to your left mouse button. Do " +
+ "you really want to do this?"));
+
+ dialog.response.connect((response) => {
+ if (response == Gtk.ResponseType.YES) {
+ this.update_trigger(new_trigger);
+ }
+ });
+
+ dialog.run();
+ dialog.destroy();
+ } else {
+ this.update_trigger(new_trigger);
+ }
+
+ return true;
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Called when the user presses a keyboard key.
+ /////////////////////////////////////////////////////////////////////
+
+ private bool on_key_press(Gdk.EventKey event) {
+ if (Gdk.keyval_name(event.keyval) == "Escape") {
+ this.hide();
+ } else if (Gdk.keyval_name(event.keyval) == "BackSpace") {
+ this.update_trigger(new Trigger());
+ } else if (event.is_modifier == 0) {
+ Gdk.ModifierType state = event.state & ~ this.lock_modifiers;
+ this.update_trigger(new Trigger.from_values((int)event.keyval, state, false,
+ this.turbo.active, this.delayed.active));
+ }
+
+ return true;
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Called when the user presses a mouse button.
+ /////////////////////////////////////////////////////////////////////
+
+ private bool on_button_press(Gdk.EventButton event) {
+ int width = 0, height = 0;
+ this.window.get_geometry(null, null, out width, out height, null);
+ if (event.x < 0 || event.x > width || event.y < 0 || event.y > height)
+ this.hide();
+ return true;
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Helper method to update the content of the trigger preview label.
+ /////////////////////////////////////////////////////////////////////
+
+ private void update_trigger(Trigger new_trigger) {
+ this.trigger = new_trigger;
+ this.preview.set_markup("<big><b>" + this.trigger.label + "</b></big>");
+ }
+}
+
+}