diff options
Diffstat (limited to 'src/pies')
-rw-r--r-- | src/pies/defaultConfig.vala | 70 | ||||
-rw-r--r-- | src/pies/load.vala | 230 | ||||
-rw-r--r-- | src/pies/pie.vala | 96 | ||||
-rw-r--r-- | src/pies/pieManager.vala | 231 | ||||
-rw-r--r-- | src/pies/save.vala | 81 |
5 files changed, 708 insertions, 0 deletions
diff --git a/src/pies/defaultConfig.vala b/src/pies/defaultConfig.vala new file mode 100644 index 0000000..bd981b5 --- /dev/null +++ b/src/pies/defaultConfig.vala @@ -0,0 +1,70 @@ +/* +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 helper class which creates a user-specific default configuration. +///////////////////////////////////////////////////////////////////////// + +namespace Pies { + + public void create_default_config() { + + // add a pie with playback controls + var multimedia = PieManager.create_persistent_pie(_("Multimedia"), "stock_media-play", "<Control><Alt>m"); + multimedia.add_action(new KeyAction(_("Next Track"), "stock_media-next", "XF86AudioNext", true)); + multimedia.add_action(new KeyAction(_("Stop"), "stock_media-stop", "XF86AudioStop")); + multimedia.add_action(new KeyAction(_("Previous Track"), "stock_media-prev", "XF86AudioPrev")); + multimedia.add_action(new KeyAction(_("Play/Pause"), "stock_media-play", "XF86AudioPlay")); + + // add a pie with the users default applications + var apps = PieManager.create_persistent_pie(_("Applications"), "applications-accessories", "<Control><Alt>a"); + apps.add_action(ActionRegistry.default_for_mime_type("text/plain")); + apps.add_action(ActionRegistry.default_for_mime_type("audio/ogg")); + apps.add_action(ActionRegistry.default_for_mime_type("video/ogg")); + apps.add_action(ActionRegistry.default_for_mime_type("image/jpg")); + apps.add_action(ActionRegistry.default_for_uri("http")); + apps.add_action(ActionRegistry.default_for_uri("mailto")); + + // add a pie with the users bookmarks and devices + var bookmarks = PieManager.create_persistent_pie(_("Bookmarks"), "user-bookmarks", "<Control><Alt>b"); + bookmarks.add_group(new BookmarkGroup(bookmarks.id)); + bookmarks.add_group(new DevicesGroup(bookmarks.id)); + + // add a pie with session controls + var session = PieManager.create_persistent_pie(_("Session"), "gnome-session-halt", "<Control><Alt>q"); + session.add_group(new SessionGroup(session.id)); + + // add a pie with a main menu + var menu = PieManager.create_persistent_pie(_("Main Menu"), "alacarte", "<Control><Alt>space"); + menu.add_group(new MenuGroup(menu.id)); + + // add a pie with window controls + var window = PieManager.create_persistent_pie(_("Window"), "gnome-window-manager", "<Control><Alt>w"); + window.add_action(new KeyAction(_("Scale"), "top", "<Control><Alt>s")); + window.add_action(new KeyAction(_("Minimize"), "bottom", "<Alt>F9", true)); + window.add_action(new KeyAction(_("Close"), "window-close", "<Alt>F4")); + window.add_action(new KeyAction(_("Maximize"), "window_fullscreen", "<Alt>F10")); + window.add_action(new KeyAction(_("Restore"), "window_nofullscreen", "<Alt>F5")); + + // save the configuration to file + Pies.save(); + } +} + +} diff --git a/src/pies/load.vala b/src/pies/load.vala new file mode 100644 index 0000000..912ddf0 --- /dev/null +++ b/src/pies/load.vala @@ -0,0 +1,230 @@ +/* +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/>. +*/ + +using GLib.Math; + +namespace GnomePie { + +///////////////////////////////////////////////////////////////////////// +/// A helper method which loads pies according to the configuration file. +///////////////////////////////////////////////////////////////////////// + +namespace Pies { + + ///////////////////////////////////////////////////////////////////// + /// Loads all Pies from the pies.conf file. + ///////////////////////////////////////////////////////////////////// + + public void load() { + // check whether the config file exists + if (!GLib.File.new_for_path(Paths.pie_config).query_exists()) { + message("Creating new configuration file in \"" + Paths.pie_config + "\"."); + Pies.create_default_config(); + return; + } + + // load the settings file + Xml.Parser.init(); + Xml.Doc* piesXML = Xml.Parser.parse_file(Paths.pie_config); + + if (piesXML != null) { + // begin parsing at the root element + Xml.Node* root = piesXML->get_root_element(); + if (root != null) { + for (Xml.Node* node = root->children; node != null; node = node->next) { + if (node->type == Xml.ElementType.ELEMENT_NODE) { + string node_name = node->name.down(); + switch (node_name) { + case "pie": + parse_pie(node); + break; + default: + warning("Invalid child element <" + node_name + "> in <pies> element pies.conf!"); + break; + } + } + } + Xml.Parser.cleanup(); + + } else { + warning("Error loading pies: pies.conf is empty! The cake is a lie..."); + } + + delete piesXML; + + } else { + warning("Error loading pies: pies.conf not found! The cake is a lie..."); + } + } + + ///////////////////////////////////////////////////////////////////// + /// Parses a <pie> element from the pies.conf file. + ///////////////////////////////////////////////////////////////////// + + private static void parse_pie(Xml.Node* node) { + string hotkey = ""; + string name = ""; + string icon = ""; + string id = ""; + int quick_action = -1; + + // parse all attributes of this node + for (Xml.Attr* attribute = node->properties; attribute != null; attribute = attribute->next) { + string attr_name = attribute->name.down(); + string attr_content = attribute->children->content; + + switch (attr_name) { + case "hotkey": + hotkey = attr_content; + break; + case "quickaction": + quick_action = int.parse(attr_content); + break; + case "name": + name = attr_content; + break; + case "icon": + icon = attr_content; + break; + case "id": + id = attr_content; + break; + default: + warning("Invalid setting \"" + attr_name + "\" in pies.conf!"); + break; + } + } + + if (name == "") { + warning("Invalid <pie> element in pies.conf: No name specified!"); + return; + } + + // add a new Pie with the loaded properties + var pie = PieManager.create_persistent_pie(name, icon, hotkey, id); + + // and parse all child elements + for (Xml.Node* slice = node->children; slice != null; slice = slice->next) { + if (slice->type == Xml.ElementType.ELEMENT_NODE) { + string node_name = slice->name.down(); + switch (node_name) { + case "slice": + parse_slice(slice, pie); + break; + case "group": + parse_group(slice, pie); + break; + default: + warning("Invalid child element <" + node_name + "> in <pie> element in pies.conf!"); + break; + } + } + } + } + + ///////////////////////////////////////////////////////////////////// + /// Parses a <slice> element from the pies.conf file. + ///////////////////////////////////////////////////////////////////// + + private static void parse_slice(Xml.Node* slice, Pie pie) { + string name=""; + string icon=""; + string command=""; + string type=""; + bool quick_action = false; + + // parse all attributes of this node + for (Xml.Attr* attribute = slice->properties; attribute != null; attribute = attribute->next) { + string attr_name = attribute->name.down(); + string attr_content = attribute->children->content; + + switch (attr_name) { + case "name": + name = attr_content; + break; + case "icon": + icon = attr_content; + break; + case "command": + command = attr_content; + break; + case "type": + type = attr_content; + break; + case "quickaction": + quick_action = bool.parse(attr_content); + break; + default: + warning("Invalid attribute \"" + attr_name + "\" in <slice> element in pies.conf!"); + break; + } + } + + Action action = null; + + // create a new Action according to the loaded type + foreach (var action_type in ActionRegistry.types) { + if (ActionRegistry.settings_names[action_type] == type) { + + action = GLib.Object.new(action_type, "name", name, + "icon", icon, + "real_command", command, + "is_quick_action", quick_action) as Action; + break; + } + } + + if (action != null) pie.add_action(action); + } + + ///////////////////////////////////////////////////////////////////// + /// Parses a <group> element from the pies.conf file. + ///////////////////////////////////////////////////////////////////// + + private static void parse_group(Xml.Node* slice, Pie pie) { + string type=""; + + // parse all attributes of this node + for (Xml.Attr* attribute = slice->properties; attribute != null; attribute = attribute->next) { + string attr_name = attribute->name.down(); + string attr_content = attribute->children->content; + + switch (attr_name) { + case "type": + type = attr_content; + break; + default: + warning("Invalid attribute \"" + attr_name + "\" in <group> element in pies.conf!"); + break; + } + } + + ActionGroup group = null; + + // create a new ActionGroup according to the loaded type + foreach (var group_type in GroupRegistry.types) { + if (GroupRegistry.settings_names[group_type] == type) { + group = GLib.Object.new(group_type, "parent_id", pie.id) as ActionGroup; + break; + } + } + + if (group != null) pie.add_group(group); + } +} + +} diff --git a/src/pies/pie.vala b/src/pies/pie.vala new file mode 100644 index 0000000..0f87d8f --- /dev/null +++ b/src/pies/pie.vala @@ -0,0 +1,96 @@ +/* +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 class stores information on a pie. A pie consists of a name, an +/// icon name and an unique ID. Furthermore it has an arbitrary amount +/// of ActionGroups storing Actions. +///////////////////////////////////////////////////////////////////////// + +public class Pie : GLib.Object { + + ///////////////////////////////////////////////////////////////////// + /// The name of this Pie. It has not to be unique. + ///////////////////////////////////////////////////////////////////// + + public string name { get; construct; } + + ///////////////////////////////////////////////////////////////////// + /// The name of the icon to be used for this Pie. It should exist in + /// the users current icon theme, else a standard icon will be used. + ///////////////////////////////////////////////////////////////////// + + public string icon { get; construct; } + + ///////////////////////////////////////////////////////////////////// + /// The ID of this Pie. It has to be unique among all Pies. This ID + /// consists of three digits when the Pie was created by the user, + /// of four digits when it was created dynamically by another class, + /// for example by an ActionGroup. + ///////////////////////////////////////////////////////////////////// + + public string id { get; construct; } + + ///////////////////////////////////////////////////////////////////// + /// Stores all ActionGroups of this Pie. + ///////////////////////////////////////////////////////////////////// + + public Gee.ArrayList<ActionGroup?> action_groups { get; private set; } + + ///////////////////////////////////////////////////////////////////// + /// C'tor, initializes all given members. + ///////////////////////////////////////////////////////////////////// + + public Pie(string id, string name, string icon) { + GLib.Object(id: id, name: name, icon:icon); + + this.action_groups = new Gee.ArrayList<ActionGroup?>(); + } + + ///////////////////////////////////////////////////////////////////// + /// Should be called when this Pie is deleted, in order to clean up + /// stuff created by contained ActionGroups. + ///////////////////////////////////////////////////////////////////// + + public virtual void on_remove() { + foreach (var action_group in action_groups) + action_group.on_remove(); + } + + ///////////////////////////////////////////////////////////////////// + /// Adds an Action to this Pie. + ///////////////////////////////////////////////////////////////////// + + public void add_action(Action action) { + var group = new ActionGroup(this.id); + group.add_action(action); + this.add_group(group); + } + + ///////////////////////////////////////////////////////////////////// + /// Adds an ActionGroup to this Pie. + ///////////////////////////////////////////////////////////////////// + + public void add_group(ActionGroup group) { + this.action_groups.add(group); + } +} + +} + diff --git a/src/pies/pieManager.vala b/src/pies/pieManager.vala new file mode 100644 index 0000000..eb031d0 --- /dev/null +++ b/src/pies/pieManager.vala @@ -0,0 +1,231 @@ +/* +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 static class which stores all Pies. It can be used to add, delete +/// and open Pies. +///////////////////////////////////////////////////////////////////////// + +public class PieManager : GLib.Object { + + ///////////////////////////////////////////////////////////////////// + /// A map of all Pies. It contains both, dynamic and persistent Pies. + /// They are associated to their ID's. + ///////////////////////////////////////////////////////////////////// + + public static Gee.HashMap<string, Pie?> all_pies { get; private set; } + + ///////////////////////////////////////////////////////////////////// + /// Stores all global hotkeys. + ///////////////////////////////////////////////////////////////////// + + private static BindingManager bindings; + + ///////////////////////////////////////////////////////////////////// + /// True, if any pie has the current focus. If it is closing this + /// will be false already. + ///////////////////////////////////////////////////////////////////// + + private static bool a_pie_is_opened = false; + + ///////////////////////////////////////////////////////////////////// + /// Initializes all Pies. They are loaded from the pies.conf file. + ///////////////////////////////////////////////////////////////////// + + public static void init() { + all_pies = new Gee.HashMap<string, Pie?>(); + bindings = new BindingManager(); + + // load all Pies from th pies.conf file + Pies.load(); + + // open the according pie if it's hotkey is pressed + bindings.on_press.connect((id) => { + open_pie(id); + }); + } + + ///////////////////////////////////////////////////////////////////// + /// Opens the Pie with the given ID, if it exists. + ///////////////////////////////////////////////////////////////////// + + public static void open_pie(string id) { + if (!a_pie_is_opened) { + Pie? pie = all_pies[id]; + + if (pie != null) { + a_pie_is_opened = true; + + var window = new PieWindow(); + window.load_pie(pie); + window.open(); + + window.on_closing.connect(() => { + a_pie_is_opened = false; + }); + } else { + warning("Failed to open pie with ID \"" + id + "\": ID does not exist!"); + } + } + } + + ///////////////////////////////////////////////////////////////////// + /// Returns the hotkey which the Pie with the given ID is bound to. + ///////////////////////////////////////////////////////////////////// + + public static string get_accelerator_of(string id) { + return bindings.get_accelerator_of(id); + } + + ///////////////////////////////////////////////////////////////////// + /// Returns a human-readable version of the hotkey which the Pie + /// with the given ID is bound to. + ///////////////////////////////////////////////////////////////////// + + public static string get_accelerator_label_of(string id) { + return bindings.get_accelerator_label_of(id); + } + + ///////////////////////////////////////////////////////////////////// + /// Returns the name of the Pie with the given ID. + ///////////////////////////////////////////////////////////////////// + + public static string get_name_of(string id) { + Pie? pie = all_pies[id]; + if (pie == null) return ""; + else return pie.name; + } + + ///////////////////////////////////////////////////////////////////// + /// Creates a new Pie which is displayed in the configuration dialog + /// and gets saved. + ///////////////////////////////////////////////////////////////////// + + public static Pie create_persistent_pie(string name, string icon_name, string hotkey, string? desired_id = null) { + Pie pie = create_pie(name, icon_name, 100, 999, desired_id); + + if (hotkey != "") bindings.bind(hotkey, pie.id); + + create_launcher(pie.id); + + return pie; + } + + ///////////////////////////////////////////////////////////////////// + /// Creates a new Pie which is not displayed in the configuration + /// dialog and is not saved. + ///////////////////////////////////////////////////////////////////// + + public static Pie create_dynamic_pie(string name, string icon_name, string? desired_id = null) { + return create_pie(name, icon_name, 1000, 9999, desired_id); + } + + ///////////////////////////////////////////////////////////////////// + /// Adds a new Pie. Can't be accesd from outer scope. Use + /// create_persistent_pie or create_dynamic_pie instead. + ///////////////////////////////////////////////////////////////////// + + private static Pie create_pie(string name, string icon_name, int min_id, int max_id, string? desired_id = null) { + var random = new GLib.Rand(); + + string final_id; + + if (desired_id == null) + final_id = random.int_range(min_id, max_id).to_string(); + else { + final_id = desired_id; + final_id.canon("0123456789", '_'); + final_id = final_id.replace("_", ""); + + int id = int.parse(final_id); + + if (id < min_id || id > max_id) { + final_id = random.int_range(min_id, max_id).to_string(); + warning("The ID for pie \"" + name + "\" should be in range %u - %u! Using \"" + final_id + "\" instead of \"" + desired_id + "\"...", min_id, max_id); + } + } + + if (all_pies.has_key(final_id)) { + var tmp = final_id; + var id_number = int.parse(final_id) + 1; + if (id_number == max_id+1) id_number = min_id; + final_id = id_number.to_string(); + warning("Trying to add pie \"" + name + "\": ID \"" + tmp + "\" already exists! Using \"" + final_id + "\" instead..."); + return create_pie(name, icon_name, min_id, max_id, final_id); + } + + Pie pie = new Pie(final_id, name, icon_name); + all_pies.set(final_id, pie); + + return pie; + } + + ///////////////////////////////////////////////////////////////////// + /// Removes the Pie with the given ID if it exists. Additionally it + /// unbinds it's global hotkey. + ///////////////////////////////////////////////////////////////////// + + public static void remove_pie(string id) { + if (all_pies.has_key(id)) { + all_pies[id].on_remove(); + all_pies.unset(id); + bindings.unbind(id); + + if (id.length == 3) + remove_launcher(id); + } + else { + warning("Failed to remove pie with ID \"" + id + "\": ID does not exist!"); + } + } + + private static void create_launcher(string id) { + if (all_pies.has_key(id)) { + Pie? pie = all_pies[id]; + + string launcher_entry = + "#!/usr/bin/env xdg-open\n" + + "[Desktop Entry]\n" + + "Name=%s\n".printf(pie.name) + + "Exec=gnome-pie -o %s\n".printf(pie.id) + + "Encoding=UTF-8\n" + + "Type=Application\n" + + "Icon=%s\n".printf(pie.icon); + + // create the launcher file + string launcher = Paths.launchers + "/%s.desktop".printf(pie.id); + + try { + FileUtils.set_contents(launcher, launcher_entry); + FileUtils.chmod(launcher, 0755); + } catch (Error e) { + warning(e.message); + } + } + } + + private static void remove_launcher(string id) { + string launcher = Paths.launchers + "/%s.desktop".printf(id); + if (FileUtils.test(launcher, FileTest.EXISTS)) { + FileUtils.remove(launcher); + } + } +} + +} diff --git a/src/pies/save.vala b/src/pies/save.vala new file mode 100644 index 0000000..d691a95 --- /dev/null +++ b/src/pies/save.vala @@ -0,0 +1,81 @@ +/* +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/>. +*/ + +using GLib.Math; + +namespace GnomePie { + +///////////////////////////////////////////////////////////////////////// +/// A helper method which saves pies in a configuration file. +///////////////////////////////////////////////////////////////////////// + +namespace Pies { + + ///////////////////////////////////////////////////////////////////// + /// Saves all Pies of the PieManager to the pies.conf file. + ///////////////////////////////////////////////////////////////////// + + public void save() { + // initializes the XML-Writer + var writer = new Xml.TextWriter.filename(Paths.pie_config); + writer.set_indent(true); + writer.start_document("1.0"); + writer.start_element("pies"); + + // iterate through all Pies + foreach (var pie_entry in PieManager.all_pies.entries) { + var pie = pie_entry.value; + + // if it's no dynamically created Pie + if (pie.id.length == 3) { + // write all attributes of the Pie + writer.start_element("pie"); + writer.write_attribute("name", pie.name); + writer.write_attribute("id", pie.id); + writer.write_attribute("icon", pie.icon); + writer.write_attribute("hotkey", PieManager.get_accelerator_of(pie.id)); + + // and all of it's Actions + foreach (var group in pie.action_groups) { + // if it's a custom ActionGroup + if (group.get_type().depth() == 2) { + foreach (var action in group.actions) { + writer.start_element("slice"); + writer.write_attribute("type", ActionRegistry.settings_names[action.get_type()]); + if (ActionRegistry.icon_name_editables[action.get_type()]) { + writer.write_attribute("name", action.name); + writer.write_attribute("icon", action.icon); + } + writer.write_attribute("command", action.real_command); + writer.write_attribute("quickAction", action.is_quick_action ? "true" : "false"); + writer.end_element(); + } + } else { + writer.start_element("group"); + writer.write_attribute("type", GroupRegistry.settings_names[group.get_type()]); + writer.end_element(); + } + } + writer.end_element(); + } + } + writer.end_element(); + writer.end_document(); + } +} + +} |