///////////////////////////////////////////////////////////////////////// // Copyright (c) 2011-2017 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 class represents a hotkey, used to open pies. It supports any /// combination of modifier keys with keyboard and mouse buttons. ///////////////////////////////////////////////////////////////////////// public class Trigger : GLib.Object { ///////////////////////////////////////////////////////////////////// /// Returns a human-readable version of this Trigger. ///////////////////////////////////////////////////////////////////// public string label { get; private set; default=""; } ///////////////////////////////////////////////////////////////////// /// Returns a human-readable version of this Trigger. Small /// identifiers for turbo mode and delayed mode are added. ///////////////////////////////////////////////////////////////////// public string label_with_specials { get; private set; default=""; } ///////////////////////////////////////////////////////////////////// /// The Trigger string. Like [delayed]button3 ///////////////////////////////////////////////////////////////////// public string name { get; private set; default=""; } ///////////////////////////////////////////////////////////////////// /// The key code of the hotkey or the button number of the mouse. ///////////////////////////////////////////////////////////////////// public int key_code { get; private set; default=0; } ///////////////////////////////////////////////////////////////////// /// The keysym of the hotkey or the button number of the mouse. ///////////////////////////////////////////////////////////////////// public uint key_sym { get; private set; default=0; } ///////////////////////////////////////////////////////////////////// /// Modifier keys pressed for this hotkey. ///////////////////////////////////////////////////////////////////// public Gdk.ModifierType modifiers { get; private set; default=0; } ///////////////////////////////////////////////////////////////////// /// True if this hotkey involves the mouse. ///////////////////////////////////////////////////////////////////// public bool with_mouse { get; private set; default=false; } ///////////////////////////////////////////////////////////////////// /// True if the pie closes when the trigger hotkey is released. ///////////////////////////////////////////////////////////////////// public bool turbo { get; private set; default=false; } ///////////////////////////////////////////////////////////////////// /// True if the trigger should wait a short delay before being /// triggered. ///////////////////////////////////////////////////////////////////// public bool delayed { get; private set; default=false; } ///////////////////////////////////////////////////////////////////// /// True if the pie opens in the middle of the screen. ///////////////////////////////////////////////////////////////////// public bool centered { get; private set; default=false; } ///////////////////////////////////////////////////////////////////// /// True if the mouse pointer is warped to the pie's center. ///////////////////////////////////////////////////////////////////// public bool warp { get; private set; default=false; } ///////////////////////////////////////////////////////////////////// /// Returns the current selected "radio-button" shape: 0= automatic /// 5= full pie; 1,3,7,8= quarters; 2,4,6,8=halves /// 1 | 4 | 7 /// 2 | 5 | 8 /// 3 | 6 | 9 ///////////////////////////////////////////////////////////////////// public int shape { get; private set; default=5; } ///////////////////////////////////////////////////////////////////// /// C'tor, creates a new, "unbound" Trigger. ///////////////////////////////////////////////////////////////////// public Trigger() { this.set_unbound(); } ///////////////////////////////////////////////////////////////////// /// C'tor, creates a new Trigger from a given Trigger string. This is /// in this format: "[option(s)]button" where /// "" is something like "" or "", "button" /// something like "s", "F4" or "button0" and "[option]" is either /// "[turbo]", "[centered]", "[warp]", "["delayed"]" or "["shape#"]" ///////////////////////////////////////////////////////////////////// public Trigger.from_string(string trigger) { this.parse_string(trigger); } ///////////////////////////////////////////////////////////////////// /// C'tor, creates a new Trigger from the key values. ///////////////////////////////////////////////////////////////////// public Trigger.from_values(uint key_sym, Gdk.ModifierType modifiers, bool with_mouse, bool turbo, bool delayed, bool centered, bool warp, int shape ) { string trigger = (turbo ? "[turbo]" : "") + (delayed ? "[delayed]" : "") + (centered ? "[centered]" : "") + (warp ? "[warp]" : "") + (shape!=5 ? "[shape%d]".printf(shape) : ""); if (with_mouse) { trigger += Gtk.accelerator_name(0, modifiers) + "button%u".printf(key_sym); } else { trigger += Gtk.accelerator_name(key_sym, modifiers); } this.parse_string(trigger); } ///////////////////////////////////////////////////////////////////// /// Parses a Trigger string. This is /// in this format: "[option(s)]button" where /// "" is something like "" or "", "button" /// something like "s", "F4" or "button0" and "[option]" is either /// "[turbo]", "[centered]", "[warp]", "["delayed"]" or "["shape#"]" ///////////////////////////////////////////////////////////////////// public void parse_string(string trigger) { if (this.is_valid(trigger)) { // copy string string check_string = trigger; this.name = check_string; this.turbo = check_string.contains("[turbo]"); this.delayed = check_string.contains("[delayed]"); this.centered = check_string.contains("[centered]"); this.warp = check_string.contains("[warp]"); this.shape= parse_shape( check_string ); // remove optional arguments check_string = remove_optional(check_string); int button = this.get_mouse_button(check_string); if (button > 0) { check_string = check_string.substring(0, check_string.index_of("button")); this.with_mouse = true; this.key_code = button; this.key_sym = button; Gtk.accelerator_parse(check_string, null, out this._modifiers); this.label = Gtk.accelerator_get_label(0, this.modifiers); if (this.label != "") { label += "+"; } string button_text = _("Button %i").printf(this.key_code); if (this.key_code == 1) button_text = _("LeftButton"); else if (this.key_code == 3) button_text = _("RightButton"); else if (this.key_code == 2) button_text = _("MiddleButton"); this.label += button_text; } else { //empty triggers are ok now, they carry open options as well if (check_string == "") { this.label = _("Not bound"); this.key_code = 0; this.key_sym = 0; this.modifiers = 0; } else { this.with_mouse = false; var display = new X.Display(); uint keysym = 0; Gtk.accelerator_parse(check_string, out keysym, out this._modifiers); this.key_code = display.keysym_to_keycode(keysym); this.key_sym = keysym; this.label = Gtk.accelerator_get_label(keysym, this.modifiers); } } this.label_with_specials = GLib.Markup.escape_text(this.label); string msg= ""; if (this.turbo) { msg= _("Turbo"); } if (this.delayed) { if (msg == "") msg= _("Delayed"); else msg += " | " + _("Delayed"); } if (this.centered) { if (msg == "") msg= _("Centered"); else msg += " | " + _("Centered"); } if (this.warp) { if (msg == "") msg= _("Warp"); else msg += " | " + _("Warp"); } if (this.shape == 0) { if (msg == "") msg= _("Auto-shaped"); else msg += " | " + _("Auto-shaped"); } else if (this.shape == 1 || this.shape ==3 || this.shape == 7 || this.shape == 9) { if (msg == "") msg= _("Quarter pie"); else msg += " | " + _("Quarter pie"); } else if (this.shape == 2 || this.shape == 4 || this.shape == 6 || this.shape == 8) { if (msg == "") msg= _("Half pie"); else msg += " | " + _("Half pie"); } if (msg != "") this.label_with_specials += (" [ " + msg + " ]"); } else { this.set_unbound(); } } ///////////////////////////////////////////////////////////////////// /// Extract shape number from trigger string /// "[0]".."[9]" 0:auto 5:full pie (default) /// 1,3,7,9=quarters 2,4,6,8= halves ///////////////////////////////////////////////////////////////////// private int parse_shape(string trigger) { int rs; for( rs= 0; rs < 10; rs++ ) if (trigger.contains("[shape%d]".printf(rs) )) return rs; return 5; //default= full pie } ///////////////////////////////////////////////////////////////////// /// Resets all member variables to their defaults. ///////////////////////////////////////////////////////////////////// private void set_unbound() { this.label = _("Not bound"); this.label_with_specials = _("Not bound"); this.name = ""; this.key_code = 0; this.key_sym = 0; this.modifiers = 0; this.turbo = false; this.delayed = false; this.centered = false; this.warp = false; this.shape = 5; //full pie this.with_mouse = false; } ///////////////////////////////////////////////////////////////////// /// Remove optional arguments from the given string /// "[turbo]", "[delayed]", "[warp]" "[centered]" and "[shape#]" ///////////////////////////////////////////////////////////////////// public static string remove_optional(string trigger) { string trg= trigger; trg = trg.replace("[turbo]", ""); trg = trg.replace("[delayed]", ""); trg = trg.replace("[centered]", ""); trg = trg.replace("[warp]", ""); for (int rs= 0; rs < 10; rs++) trg = trg.replace("[shape%d]".printf(rs), ""); return trg; } ///////////////////////////////////////////////////////////////////// /// Returns true, if the trigger string is in a valid format. ///////////////////////////////////////////////////////////////////// private bool is_valid(string trigger) { // remove optional arguments string check_string = remove_optional(trigger); if (this.get_mouse_button(check_string) > 0) { // it seems to be a valid mouse-trigger so replace button part, // with something accepted by gtk, and check it with gtk int button_index = check_string.index_of("button"); check_string = check_string.slice(0, button_index) + "a"; } //empty triggers are ok now, they carry open options as well if (check_string == "") return true; // now it shouls be a normal gtk accelerator uint keysym = 0; Gdk.ModifierType modifiers = 0; Gtk.accelerator_parse(check_string, out keysym, out modifiers); if (keysym == 0) return false; return true; } ///////////////////////////////////////////////////////////////////// /// Returns the mouse button number of the given trigger string. /// Returns -1 if it is not a mouse trigger. ///////////////////////////////////////////////////////////////////// private int get_mouse_button(string trigger) { if (trigger.contains("button")) { // it seems to be a mouse-trigger so check the button part. int button_index = trigger.index_of("button"); int number = int.parse(trigger.slice(button_index + 6, trigger.length)); if (number > 0) return number; } return -1; } } }