/*
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 .
*/
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("" + this.trigger.label + "");
}
}
}