summaryrefslogtreecommitdiff
path: root/src/utilities
diff options
context:
space:
mode:
Diffstat (limited to 'src/utilities')
-rw-r--r--src/utilities/animatedValue.vala190
-rw-r--r--src/utilities/bindingManager.vala196
-rw-r--r--src/utilities/color.vala305
-rw-r--r--src/utilities/config.vala202
-rw-r--r--src/utilities/icon.vala102
-rw-r--r--src/utilities/image.vala163
-rw-r--r--src/utilities/key.vala139
-rw-r--r--src/utilities/logger.vala194
-rw-r--r--src/utilities/paths.vala211
-rw-r--r--src/utilities/renderedText.vala110
-rw-r--r--src/utilities/themedIcon.vala161
11 files changed, 1973 insertions, 0 deletions
diff --git a/src/utilities/animatedValue.vala b/src/utilities/animatedValue.vala
new file mode 100644
index 0000000..32ab889
--- /dev/null
+++ b/src/utilities/animatedValue.vala
@@ -0,0 +1,190 @@
+/*
+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 class which interpolates smoothly between to given values.
+/// Duration and interpolation mode can be specified.
+/////////////////////////////////////////////////////////////////////////
+
+public class AnimatedValue : GLib.Object {
+
+ /////////////////////////////////////////////////////////////////////
+ /// The direction of the interpolation.
+ /////////////////////////////////////////////////////////////////////
+
+ public enum Direction { IN, OUT, IN_OUT, OUT_IN }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Type of the interpolation, linear or cubic.
+ /////////////////////////////////////////////////////////////////////
+
+ private enum Type { LINEAR, CUBIC }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Current value, interpolated.
+ /////////////////////////////////////////////////////////////////////
+
+ public double val { get; private set; }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Starting value of the interpolation.
+ /////////////////////////////////////////////////////////////////////
+
+ public double start { get; private set; default=0.0; }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Final value of the interpolation.
+ /////////////////////////////////////////////////////////////////////
+
+ public double end { get; private set; default=0.0; }
+
+ /////////////////////////////////////////////////////////////////////
+ /// The current state. In range 0 .. 1
+ /////////////////////////////////////////////////////////////////////
+
+ private double state = 0.0;
+
+ /////////////////////////////////////////////////////////////////////
+ /// Duration of the interpolation. Should be in the same unit as
+ /// taken for the update() method.
+ /////////////////////////////////////////////////////////////////////
+
+ private double duration = 0.0;
+
+ /////////////////////////////////////////////////////////////////////
+ /// The amount of over-shooting of the cubicly interpolated value.
+ /////////////////////////////////////////////////////////////////////
+
+ private double multiplier = 0.0;
+
+ /////////////////////////////////////////////////////////////////////
+ /// Type of the interpolation, linear or cubic.
+ /////////////////////////////////////////////////////////////////////
+
+ private Type type = Type.LINEAR;
+
+ /////////////////////////////////////////////////////////////////////
+ /// The direction of the interpolation.
+ /////////////////////////////////////////////////////////////////////
+
+ private Direction direction = Direction.IN;
+
+ /////////////////////////////////////////////////////////////////////
+ /// Creates a new linearly interpolated value.
+ /////////////////////////////////////////////////////////////////////
+
+ public AnimatedValue.linear(double start, double end, double duration) {
+ this.val = start;
+ this.start = start;
+ this.end = end;
+ this.duration = duration;
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Creates a new cubicly interpolated value.
+ /////////////////////////////////////////////////////////////////////
+
+ public AnimatedValue.cubic(Direction direction, double start, double end, double duration, double multiplier = 0) {
+ this.val = start;
+ this.start = start;
+ this.end = end;
+ this.duration = duration;
+ this.direction = direction;
+ this.type = Type.CUBIC;
+ this.multiplier = multiplier;
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Resets the final value of the interpolation to a new value. The
+ /// current state is taken for the beginning from now.
+ /////////////////////////////////////////////////////////////////////
+
+ public void reset_target(double end, double duration) {
+ this.start = this.val;
+ this.end = end;
+ this.duration = duration;
+ this.state = 0.0;
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Updates the interpolated value according to it's type.
+ /////////////////////////////////////////////////////////////////////
+
+ public void update(double time) {
+ this.state += time/this.duration;
+
+ if (state < 1) {
+
+ switch (this.type) {
+ case Type.LINEAR:
+ this.val = update_linear();
+ break;
+ case Type.CUBIC:
+ switch (this.direction) {
+ case Direction.IN:
+ this.val = update_ease_in();
+ return;
+ case Direction.OUT:
+ this.val = update_ease_out();
+ return;
+ case Direction.IN_OUT:
+ this.val = update_ease_in_out();
+ return;
+ case Direction.OUT_IN:
+ this.val = update_ease_out_in();
+ return;
+ }
+ break;
+ }
+ } else if (this.val != this.end) {
+ this.val = this.end;
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// The following equations are based on Robert Penner's easing
+ /// equations. See (http://www.robertpenner.com/easing/) and their
+ /// adaption by Zeh Fernando, Nate Chatellier and Arthur Debert for
+ /// the Tweener class. See (http://code.google.com/p/tweener/).
+ /////////////////////////////////////////////////////////////////////
+
+ private double update_linear(double t = this.state, double s = this.start, double e = this.end) {
+ return (s + t*(e - s));
+ }
+
+ private double update_ease_in(double t = this.state, double s = this.start, double e = this.end) {
+ return (s + (t*t*((multiplier+1)*t-multiplier))*(e - s));
+ }
+
+ private double update_ease_out(double t = this.state, double s = this.start, double e = this.end) {
+ return (s + ((t-1) * (t-1) * ((multiplier+1)*(t-1)+multiplier) + 1) * (e - s));
+ }
+
+ private double update_ease_in_out(double t = this.state, double s = this.start, double e = this.end) {
+ if (this.state < 0.5) return update_ease_in(t*2, s, e - (e-s)*0.5);
+ else return update_ease_out(t*2-1, s + (e-s)*0.5, e);
+ }
+
+ private double update_ease_out_in(double t = this.state, double s = this.start, double e = this.end) {
+ if (this.state < 0.5) return update_ease_out(t*2, s, e - (e-s)*0.5);
+ else return update_ease_in(t*2-1, s + (e-s)*0.5, e);
+ }
+}
+
+}
diff --git a/src/utilities/bindingManager.vala b/src/utilities/bindingManager.vala
new file mode 100644
index 0000000..8795124
--- /dev/null
+++ b/src/utilities/bindingManager.vala
@@ -0,0 +1,196 @@
+/*
+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 {
+
+/////////////////////////////////////////////////////////////////////////
+/// Globally binds key stroke to given ID's. When one of the bound
+/// strokes is invoked, a signal with the according ID is emitted.
+/////////////////////////////////////////////////////////////////////////
+
+public class BindingManager : GLib.Object {
+
+ /////////////////////////////////////////////////////////////////////
+ /// Called when a stored binding is invoked. The according ID is
+ /// passed as argument.
+ /////////////////////////////////////////////////////////////////////
+
+ public signal void on_press(string id);
+
+ /////////////////////////////////////////////////////////////////////
+ /// A list storing bindings, which are invoked even if Gnome-Pie
+ /// doesn't have the current focus
+ /////////////////////////////////////////////////////////////////////
+
+ private Gee.List<Keybinding> bindings = new Gee.ArrayList<Keybinding>();
+
+ /////////////////////////////////////////////////////////////////////
+ /// Ignored modifier masks, used to grab all keys even if these locks
+ /// are active.
+ /////////////////////////////////////////////////////////////////////
+
+ private static uint[] lock_modifiers = {
+ 0,
+ Gdk.ModifierType.MOD2_MASK,
+ Gdk.ModifierType.LOCK_MASK,
+ Gdk.ModifierType.MOD5_MASK,
+ Gdk.ModifierType.MOD2_MASK|Gdk.ModifierType.LOCK_MASK,
+ Gdk.ModifierType.MOD2_MASK|Gdk.ModifierType.MOD5_MASK,
+ Gdk.ModifierType.LOCK_MASK|Gdk.ModifierType.MOD5_MASK,
+ Gdk.ModifierType.MOD2_MASK|Gdk.ModifierType.LOCK_MASK|Gdk.ModifierType.MOD5_MASK
+ };
+
+ /////////////////////////////////////////////////////////////////////
+ /// Helper class to store keybinding
+ /////////////////////////////////////////////////////////////////////
+
+ private class Keybinding {
+
+ public Keybinding(string accelerator, int keycode, Gdk.ModifierType modifiers, string id) {
+ this.accelerator = accelerator;
+ this.keycode = keycode;
+ this.modifiers = modifiers;
+ this.id = id;
+ }
+
+ public string accelerator { get; set; }
+ public int keycode { get; set; }
+ public Gdk.ModifierType modifiers { get; set; }
+ public string id { get; set; }
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// C'tor adds the event filter to the root window.
+ /////////////////////////////////////////////////////////////////////
+
+ public BindingManager() {
+ // init filter to retrieve X.Events
+ Gdk.Window rootwin = Gdk.get_default_root_window();
+ if(rootwin != null) {
+ rootwin.add_filter(event_filter);
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Binds the ID to the given accelerator.
+ /////////////////////////////////////////////////////////////////////
+
+ public void bind(string accelerator, string id) {
+ uint keysym;
+ Gdk.ModifierType modifiers;
+ Gtk.accelerator_parse(accelerator, out keysym, out modifiers);
+
+ if (keysym == 0) {
+ warning("Invalid keystroke: " + accelerator);
+ return;
+ }
+
+ Gdk.Window rootwin = Gdk.get_default_root_window();
+ X.Display display = Gdk.x11_drawable_get_xdisplay(rootwin);
+ X.ID xid = Gdk.x11_drawable_get_xid(rootwin);
+ int keycode = display.keysym_to_keycode(keysym);
+
+ if(keycode != 0) {
+ Gdk.error_trap_push();
+
+ foreach(uint lock_modifier in lock_modifiers) {
+ display.grab_key(keycode, modifiers|lock_modifier, xid, false, X.GrabMode.Async, X.GrabMode.Async);
+ }
+
+ Gdk.flush();
+
+ Keybinding binding = new Keybinding(accelerator, keycode, modifiers, id);
+ bindings.add(binding);
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Unbinds the accelerator of the given ID.
+ /////////////////////////////////////////////////////////////////////
+
+ public void unbind(string id) {
+ Gdk.Window rootwin = Gdk.get_default_root_window();
+ X.Display display = Gdk.x11_drawable_get_xdisplay(rootwin);
+ X.ID xid = Gdk.x11_drawable_get_xid(rootwin);
+ Gee.List<Keybinding> remove_bindings = new Gee.ArrayList<Keybinding>();
+ foreach(var binding in bindings) {
+ if(id == binding.id) {
+ foreach(uint lock_modifier in lock_modifiers) {
+ display.ungrab_key(binding.keycode, binding.modifiers, xid);
+ }
+ remove_bindings.add(binding);
+ }
+ }
+
+ bindings.remove_all(remove_bindings);
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Returns a human readable accelerator for the given ID.
+ /////////////////////////////////////////////////////////////////////
+
+ public string get_accelerator_label_of(string id) {
+ string accelerator = this.get_accelerator_of(id);
+
+ if (accelerator == "")
+ return _("Not bound");
+
+ uint key = 0;
+ Gdk.ModifierType mods;
+ Gtk.accelerator_parse(accelerator, out key, out mods);
+ return Gtk.accelerator_get_label(key, mods);
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Returns the accelerator to which the given ID is bound.
+ /////////////////////////////////////////////////////////////////////
+
+ public string get_accelerator_of(string id) {
+ foreach (var binding in bindings) {
+ if (binding.id == id) {
+ return binding.accelerator;
+ }
+ }
+
+ return "";
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Event filter method needed to fetch X.Events
+ /////////////////////////////////////////////////////////////////////
+
+ private Gdk.FilterReturn event_filter(Gdk.XEvent gdk_xevent, Gdk.Event gdk_event) {
+ Gdk.FilterReturn filter_return = Gdk.FilterReturn.CONTINUE;
+
+ void* pointer = &gdk_xevent;
+ X.Event* xevent = (X.Event*) pointer;
+
+ if(xevent->type == X.EventType.KeyPress) {
+ foreach(var binding in bindings) {
+ // remove NumLock, CapsLock and ScrollLock from key state
+ uint event_mods = xevent.xkey.state & ~ (lock_modifiers[7]);
+ if(xevent->xkey.keycode == binding.keycode && event_mods == binding.modifiers) {
+ on_press(binding.id);
+ }
+ }
+ }
+
+ return filter_return;
+ }
+}
+
+}
diff --git a/src/utilities/color.vala b/src/utilities/color.vala
new file mode 100644
index 0000000..836411e
--- /dev/null
+++ b/src/utilities/color.vala
@@ -0,0 +1,305 @@
+/*
+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 Color class with full rgb/hsv support
+/// and some useful utility methods.
+/////////////////////////////////////////////////////////////////////////
+
+public class Color: GLib.Object {
+
+ /////////////////////////////////////////////////////////////////////
+ /// Private members, storing the actual color information.
+ /// In range 0 .. 1
+ /////////////////////////////////////////////////////////////////////
+
+ private float _r;
+ private float _g;
+ private float _b;
+ private float _a;
+
+ /////////////////////////////////////////////////////////////////////
+ /// Creates a white Color.
+ /////////////////////////////////////////////////////////////////////
+
+ public Color() {
+ Color.from_rgb(1.0f, 1.0f, 1.0f);
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Creates a solid color with the given RGB values.
+ /////////////////////////////////////////////////////////////////////
+
+ public Color.from_rgb(float red, float green, float blue) {
+ Color.from_rgba(red, green, blue, 1.0f);
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Creates a translucient color with the given RGBA values.
+ /////////////////////////////////////////////////////////////////////
+
+ public Color.from_rgba(float red, float green, float blue, float alpha) {
+ r = red;
+ g = green;
+ b = blue;
+ a = alpha;
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Creates a color from the given Gdk.Color
+ /////////////////////////////////////////////////////////////////////
+
+ public Color.from_gdk(Gdk.Color color) {
+ Color.from_rgb(
+ (float)color.red/65535.0f,
+ (float)color.green/65535.0f,
+ (float)color.blue/65535.0f
+ );
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Creates a color, parsed from a string, such as #22EE33
+ /////////////////////////////////////////////////////////////////////
+
+ public Color.from_string(string hex_string) {
+ Gdk.Color color;
+ Gdk.Color.parse(hex_string, out color);
+ Color.from_gdk(color);
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Gets the main color from an Image. Code from Unity.
+ /////////////////////////////////////////////////////////////////////
+
+ public Color.from_icon(Image icon) {
+ unowned uchar[] data = icon.surface.get_data();
+
+ uint width = icon.surface.get_width();
+ uint height = icon.surface.get_height();
+ uint row_bytes = icon.surface.get_stride();
+
+ double total = 0.0;
+ double rtotal = 0.0;
+ double gtotal = 0.0;
+ double btotal = 0.0;
+
+ for (uint i = 0; i < width; ++i) {
+ for (uint j = 0; j < height; ++j) {
+ uint pixel = j * row_bytes + i * 4;
+ double b = data[pixel + 0]/255.0;
+ double g = data[pixel + 1]/255.0;
+ double r = data[pixel + 2]/255.0;
+ double a = data[pixel + 3]/255.0;
+
+ double saturation = (fmax (r, fmax (g, b)) - fmin (r, fmin (g, b)));
+ double relevance = 0.1 + 0.9 * a * saturation;
+
+ rtotal += (r * relevance);
+ gtotal += (g * relevance);
+ btotal += (b * relevance);
+
+ total += relevance;
+ }
+ }
+
+ Color.from_rgb((float)(rtotal/total), (float)(gtotal/total), (float)(btotal/total));
+
+ if (s > 0.15f) s = 0.65f;
+
+ v = 1.0f;
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// The reddish part of the color.
+ /////////////////////////////////////////////////////////////////////
+
+ public float r {
+ get {
+ return _r;
+ }
+ set {
+ if (value > 1.0f) _r = 1.0f;
+ else if (value < 0.0f) _r = 0.0f;
+ else _r = value;
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// The greenish part of the color.
+ /////////////////////////////////////////////////////////////////////
+
+ public float g {
+ get {
+ return _g;
+ }
+ set {
+ if (value > 1.0f) _g = 1.0f;
+ else if (value < 0.0f) _g = 0.0f;
+ else _g = value;
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// The blueish part of the color.
+ /////////////////////////////////////////////////////////////////////
+
+ public float b {
+ get {
+ return _b;
+ }
+ set {
+ if (value > 1.0f) _b = 1.0f;
+ else if (value < 0.0f) _b = 0.0f;
+ else _b = value;
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// The transparency of the color.
+ /////////////////////////////////////////////////////////////////////
+
+ public float a {
+ get {
+ return _a;
+ }
+ set {
+ if (value > 1.0f) _a = 1.0f;
+ else if (value < 0.0f) _a = 0.0f;
+ else _a = value;
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// The hue of the color.
+ /////////////////////////////////////////////////////////////////////
+
+ public float h {
+ get {
+ if (s > 0.0f) {
+ float maxi = fmaxf(fmaxf(r, g), b);
+ float mini = fminf(fminf(r, g), b);
+
+ if (maxi == r)
+ return fmodf(60.0f*((g-b)/(maxi-mini))+360.0f, 360.0f);
+ else if (maxi == g)
+ return fmodf(60.0f*(2.0f + (b-r)/(maxi-mini))+360.0f, 360.0f);
+ else
+ return fmodf(60.0f*(4.0f + (r-g)/(maxi-mini))+360.0f, 360.0f);
+ }
+ else return 0.0f;
+ }
+ set {
+ setHSV(value, s, v);
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// The saturation of the color.
+ /////////////////////////////////////////////////////////////////////
+
+ public float s {
+ get {
+ if (v == 0.0f) return 0.0f;
+ else return ((v-fminf(fminf(r, g), b)) / v);
+ }
+ set {
+ if (value > 1.0f) setHSV(h, 1.0f, v);
+ else if (value < 0.0f) setHSV(h, 0.0f, v);
+ else setHSV(h, value, v);
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// The value of the color.
+ /////////////////////////////////////////////////////////////////////
+
+ public float v {
+ get {
+ return fmaxf(fmaxf(r, g), b);
+ }
+ set {
+ if (value > 1) setHSV(h, s, 1.0f);
+ else if (value < 0) setHSV(h, s, 0.0f);
+ else setHSV(h, s, value);
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Inverts the color.
+ /////////////////////////////////////////////////////////////////////
+
+ public void invert() {
+ h += 180.0f;
+ v = 1.0f - v;
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Private member, used to apply color changes.
+ /////////////////////////////////////////////////////////////////////
+
+ private void setHSV(float hue, float saturation, float val) {
+ if(saturation == 0) {
+ r = val;
+ g = val;
+ b = val;
+ return;
+ }
+ hue = fmodf(hue, 360);
+ hue /= 60;
+ int i = (int) floorf(hue);
+ float f = hue - i;
+
+ switch(i) {
+ case 0:
+ r = val;
+ g = val * (1.0f - saturation * (1.0f - f));
+ b = val * (1.0f - saturation);
+ break;
+ case 1:
+ r = val * (1.0f - saturation * f);
+ g = val;
+ b = val * (1.0f - saturation);
+ break;
+ case 2:
+ r = val * (1.0f - saturation);
+ g = val;
+ b = val * (1.0f - saturation * (1.0f - f));
+ break;
+ case 3:
+ r = val * (1.0f - saturation);
+ g = val * (1.0f - saturation * f);
+ b = val;
+ break;
+ case 4:
+ r = val * (1.0f - saturation * (1.0f - f));
+ g = val * (1.0f - saturation);
+ b = val;
+ break;
+ default:
+ r = val;
+ g = val * (1.0f - saturation);
+ b = val * (1.0f - saturation * f);
+ break;
+ }
+ }
+}
+
+}
diff --git a/src/utilities/config.vala b/src/utilities/config.vala
new file mode 100644
index 0000000..c5dedd5
--- /dev/null
+++ b/src/utilities/config.vala
@@ -0,0 +1,202 @@
+/*
+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 singleton class for storing global settings. These settings can
+/// be loaded from and saved to an XML file.
+/////////////////////////////////////////////////////////////////////////
+
+public class Config : GLib.Object {
+
+ /////////////////////////////////////////////////////////////////////
+ /// The singleton instance of this class.
+ /////////////////////////////////////////////////////////////////////
+
+ private static Config _instance = null;
+
+ /////////////////////////////////////////////////////////////////////
+ /// Returns the singleton instance.
+ /////////////////////////////////////////////////////////////////////
+
+ public static Config global {
+ get {
+ if (_instance == null) {
+ _instance = new Config();
+ _instance.load();
+ }
+ return _instance;
+ }
+ private set {
+ _instance = value;
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// All settings variables.
+ /////////////////////////////////////////////////////////////////////
+
+ public Theme theme { get; set; }
+ public double refresh_rate { get; set; default = 60.0; }
+ public double global_scale { get; set; default = 1.0; }
+ public bool show_indicator { get; set; default = true; }
+ public bool open_at_mouse { get; set; default = true; }
+ public bool turbo_mode { get; set; default = false; }
+ public bool auto_start { get; set; default = false; }
+ public Gee.ArrayList<Theme?> themes { get; private set; }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Saves all above variables to a file.
+ /////////////////////////////////////////////////////////////////////
+
+ public void save() {
+ var writer = new Xml.TextWriter.filename(Paths.settings);
+ writer.start_document("1.0");
+ writer.start_element("settings");
+ writer.write_attribute("theme", theme.name);
+ writer.write_attribute("refresh_rate", refresh_rate.to_string());
+ writer.write_attribute("global_scale", global_scale.to_string());
+ writer.write_attribute("show_indicator", show_indicator ? "true" : "false");
+ writer.write_attribute("open_at_mouse", open_at_mouse ? "true" : "false");
+ writer.write_attribute("turbo_mode", turbo_mode ? "true" : "false");
+ writer.end_element();
+ writer.end_document();
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Loads all settings variables from a file.
+ /////////////////////////////////////////////////////////////////////
+
+ private void load() {
+
+ // check for auto_start filename
+ this.auto_start = FileUtils.test(Paths.autostart, FileTest.EXISTS);
+
+ // parse the settings file
+ Xml.Parser.init();
+ Xml.Doc* settingsXML = Xml.Parser.parse_file(Paths.settings);
+ bool error_occrured = false;
+ string theme_name = "";
+
+ if (settingsXML != null) {
+
+ Xml.Node* root = settingsXML->get_root_element();
+ if (root != null) {
+
+ for (Xml.Attr* attribute = root->properties; attribute != null; attribute = attribute->next) {
+ string attr_name = attribute->name.down();
+ string attr_content = attribute->children->content;
+
+ switch (attr_name) {
+ case "theme":
+ theme_name = attr_content;
+ break;
+ case "refresh_rate":
+ refresh_rate = double.parse(attr_content);
+ break;
+ case "global_scale":
+ global_scale = double.parse(attr_content);
+ global_scale.clamp(0.5, 2.0);
+ break;
+ case "show_indicator":
+ show_indicator = bool.parse(attr_content);
+ break;
+ case "open_at_mouse":
+ open_at_mouse = bool.parse(attr_content);
+ break;
+ case "turbo_mode":
+ turbo_mode = bool.parse(attr_content);
+ break;
+ default:
+ warning("Invalid setting \"" + attr_name + "\" in gnome-pie.conf!");
+ break;
+ }
+ }
+
+ Xml.Parser.cleanup();
+
+ } else {
+ warning("Error loading settings: gnome-pie.conf is empty! Using defaults...");
+ error_occrured = true;
+ }
+
+ delete settingsXML;
+
+ } else {
+ warning("Error loading settings: gnome-pie.conf not found! Using defaults...");
+ error_occrured = true;
+ }
+
+ load_themes(theme_name);
+
+ if (error_occrured) save();
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Registers all themes in the user's and in the global
+ /// theme directory.
+ /////////////////////////////////////////////////////////////////////
+
+ public void load_themes(string current) {
+ themes = new Gee.ArrayList<Theme?>();
+ try {
+ string name;
+
+ // load global themes
+ var d = Dir.open(Paths.global_themes);
+ while ((name = d.read_name()) != null) {
+ var theme = new Theme(Paths.global_themes + "/" + name);
+ if (theme != null)
+ themes.add(theme);
+ }
+
+ // load local themes
+ d = Dir.open(Paths.local_themes);
+ while ((name = d.read_name()) != null) {
+ var theme = new Theme(Paths.local_themes + "/" + name);
+ if (theme != null)
+ themes.add(theme);
+ }
+
+ } catch (Error e) {
+ warning (e.message);
+ }
+
+ if (themes.size > 0) {
+ if (current == "") {
+ current = "Unity";
+ warning("No theme specified! Using default...");
+ }
+ foreach (var t in themes) {
+ if (t.name == current) {
+ theme = t;
+ theme.load_images();
+ break;
+ }
+ }
+ if (theme == null) {
+ theme = themes[0];
+ warning("Theme \"" + current + "\" not found! Using fallback...");
+ }
+ }
+ else error("No theme found!");
+ }
+
+}
+
+}
diff --git a/src/utilities/icon.vala b/src/utilities/icon.vala
new file mode 100644
index 0000000..1c8a9f4
--- /dev/null
+++ b/src/utilities/icon.vala
@@ -0,0 +1,102 @@
+/*
+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 class representing a square-shaped icon, loaded from the users
+/// icon theme.
+/////////////////////////////////////////////////////////////////////////
+
+public class Icon : Image {
+
+ /////////////////////////////////////////////////////////////////////
+ /// A cache which stores loaded icon. It is cleared when the icon
+ /// theme of the user changes. The key is in form <filename>@<size>.
+ /////////////////////////////////////////////////////////////////////
+
+ private static Gee.HashMap<string, Cairo.ImageSurface?> cache { private get; private set; }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Initializes the cache.
+ /////////////////////////////////////////////////////////////////////
+
+ public static void init() {
+ clear_cache();
+
+ Gtk.IconTheme.get_default().changed.connect(() => {
+ clear_cache();
+ });
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Clears the cache.
+ /////////////////////////////////////////////////////////////////////
+
+ public static void clear_cache() {
+ cache = new Gee.HashMap<string, Cairo.ImageSurface?>();
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Loads an icon from the current icon theme of the user.
+ /////////////////////////////////////////////////////////////////////
+
+ public Icon(string icon_name, int size) {
+ var cached = this.cache.get("%s@%u".printf(icon_name, size));
+
+ if (cached == null) {
+ this.load_file_at_size(this.get_icon_file(icon_name, size), size, size);
+ this.cache.set("%s@%u".printf(icon_name, size), this.surface);
+ } else {
+ this.surface = cached;
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Returns the size of the icon in pixels. Greetings to Liskov.
+ /////////////////////////////////////////////////////////////////////
+
+ public int size() {
+ return base.width();
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Returns the filename for a given system icon.
+ /////////////////////////////////////////////////////////////////////
+
+ public static string get_icon_file(string icon_name, int size) {
+ string result = "";
+
+ var icon_theme = Gtk.IconTheme.get_default();
+ var file = icon_theme.lookup_icon(icon_name, size, 0);
+ if (file != null) result = file.get_filename();
+
+ if (result == "") {
+ warning("Icon \"" + icon_name + "\" not found! Using default icon...");
+ icon_name = "application-default-icon";
+ file = icon_theme.lookup_icon(icon_name, size, 0);
+ if (file != null) result = file.get_filename();
+ }
+
+ if (result == "")
+ warning("Icon \"" + icon_name + "\" not found! Will be ugly...");
+
+ return result;
+ }
+}
+
+}
diff --git a/src/utilities/image.vala b/src/utilities/image.vala
new file mode 100644
index 0000000..836e4e2
--- /dev/null
+++ b/src/utilities/image.vala
@@ -0,0 +1,163 @@
+/*
+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 class which loads image files. It can load image files in various
+/// formats, including jpeg, png and svg.
+/////////////////////////////////////////////////////////////////////////
+
+public class Image : GLib.Object {
+
+ /////////////////////////////////////////////////////////////////////
+ /// The internally used surface.
+ /////////////////////////////////////////////////////////////////////
+
+ public Cairo.ImageSurface surface { public get; protected set; default=null; }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Creates an empty Image.
+ /////////////////////////////////////////////////////////////////////
+
+ public Image.empty(int width, int height, Color? color = null) {
+ this.surface = new Cairo.ImageSurface(Cairo.Format.ARGB32, width, height);
+
+ if (color != null) {
+ var ctx = this.context();
+ ctx.set_source_rgb(color.r, color.g, color.b);
+ ctx.paint();
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Creates an image from the the given filename.
+ /////////////////////////////////////////////////////////////////////
+
+ public Image.from_file(string filename) {
+ this.load_file(filename);
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Creates an image from the the given filename at a given size.
+ /////////////////////////////////////////////////////////////////////
+
+ public Image.from_file_at_size(string filename, int width, int height) {
+ this.load_file_at_size(filename, width, height);
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Creates an image from the the given Gdk.Pixbuf.
+ /////////////////////////////////////////////////////////////////////
+
+ public Image.from_pixbuf(Gdk.Pixbuf pixbuf) {
+ this.load_pixbuf(pixbuf);
+ }
+
+ public Image.capture_screen(int posx, int posy, int width, int height) {
+ Gdk.Window root = Gdk.get_default_root_window();
+ Gdk.Pixbuf pixbuf = Gdk.pixbuf_get_from_drawable(null, root, null, posx, posy, 0, 0, width, height);
+
+ this.load_pixbuf(pixbuf);
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Loads an image from the the given filename.
+ /////////////////////////////////////////////////////////////////////
+
+ public void load_file(string filename) {
+ try {
+ var pixbuf = new Gdk.Pixbuf.from_file(filename);
+
+ if (pixbuf != null) {
+ this.load_pixbuf(pixbuf);
+ } else {
+ warning("Failed to load " + filename + "!");
+ }
+ } catch (GLib.Error e) {
+ message("Error loading image file: %s", e.message);
+ this.surface = new Cairo.ImageSurface(Cairo.Format.ARGB32, 1, 1);
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Loads an image from the the given filename at a given size.
+ /////////////////////////////////////////////////////////////////////
+
+ public void load_file_at_size(string filename, int width, int height) {
+ try {
+ var pixbuf = new Gdk.Pixbuf.from_file_at_size(filename, width, height);
+
+ if (pixbuf != null) {
+ this.load_pixbuf(pixbuf);
+ } else {
+ warning("Failed to load " + filename + "!");
+ }
+ } catch (GLib.Error e) {
+ message("Error loading image file: %s", e.message);
+ this.surface = new Cairo.ImageSurface(Cairo.Format.ARGB32, width, height);
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Loads an image from the the given Gdk.Pixbuf.
+ /////////////////////////////////////////////////////////////////////
+
+ public void load_pixbuf(Gdk.Pixbuf pixbuf) {
+ this.surface = new Cairo.ImageSurface(Cairo.Format.ARGB32, pixbuf.width, pixbuf.height);
+
+ var ctx = this.context();
+ Gdk.cairo_set_source_pixbuf(ctx, pixbuf, 1.0, 1.0);
+ ctx.paint();
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Paints the image onto the given Cairo.Context
+ /////////////////////////////////////////////////////////////////////
+
+ public void paint_on(Cairo.Context ctx, double alpha = 1.0) {
+ ctx.set_source_surface(this.surface, -0.5*this.width()-1, -0.5*this.height()-1);
+ if (alpha >= 1.0) ctx.paint();
+ else ctx.paint_with_alpha(alpha);
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Returns a Cairo.Context for the Image.
+ /////////////////////////////////////////////////////////////////////
+
+ public Cairo.Context context() {
+ return new Cairo.Context(this.surface);;
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Returns the width of the image in pixels.
+ /////////////////////////////////////////////////////////////////////
+
+ public int width() {
+ return this.surface.get_width();
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Returns the height of the image in pixels.
+ /////////////////////////////////////////////////////////////////////
+
+ public int height() {
+ return this.surface.get_height();
+ }
+}
+
+}
diff --git a/src/utilities/key.vala b/src/utilities/key.vala
new file mode 100644
index 0000000..6700b16
--- /dev/null
+++ b/src/utilities/key.vala
@@ -0,0 +1,139 @@
+/*
+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 class which represents a key stroke. It can be used to "press"
+/// the associated keys.
+/////////////////////////////////////////////////////////////////////////
+
+public class Key : GLib.Object {
+
+ /////////////////////////////////////////////////////////////////////
+ /// Some static members, which are often used by this class.
+ /////////////////////////////////////////////////////////////////////
+
+ private static X.Display display;
+
+ private static int shift_code;
+ private static int ctrl_code;
+ private static int alt_code;
+ private static int super_code;
+
+ /////////////////////////////////////////////////////////////////////
+ /// A human readable form of the Key's accelerator.
+ /////////////////////////////////////////////////////////////////////
+
+ public string label { get; private set; }
+
+ /////////////////////////////////////////////////////////////////////
+ /// The accelerator of the Key.
+ /////////////////////////////////////////////////////////////////////
+
+ public string accelerator { get; private set; }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Keycode and modifiers of this stroke.
+ /////////////////////////////////////////////////////////////////////
+
+ private int key_code;
+ private Gdk.ModifierType modifiers;
+
+ /////////////////////////////////////////////////////////////////////
+ /// C'tor, initializes all members.
+ /////////////////////////////////////////////////////////////////////
+
+ public Key(string stroke) {
+ this.accelerator = stroke;
+
+ uint keysym;
+ Gtk.accelerator_parse(stroke, out keysym, out this.modifiers);
+ this.key_code = display.keysym_to_keycode(keysym);
+ this.label = Gtk.accelerator_get_label(keysym, this.modifiers);
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Initializes static members.
+ /////////////////////////////////////////////////////////////////////
+
+ static construct {
+ display = new X.Display();
+
+ shift_code = display.keysym_to_keycode(Gdk.keyval_from_name("Shift_L"));
+ ctrl_code = display.keysym_to_keycode(Gdk.keyval_from_name("Control_L"));
+ alt_code = display.keysym_to_keycode(Gdk.keyval_from_name("Alt_L"));
+ super_code = display.keysym_to_keycode(Gdk.keyval_from_name("Super_L"));
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Simulates the pressing of the Key .
+ /////////////////////////////////////////////////////////////////////
+
+ public void press() {
+ // store currently pressed modifier keys
+ Gdk.ModifierType current_modifiers = get_modifiers();
+
+ // release them and press the desired ones
+ press_modifiers(current_modifiers, false);
+ press_modifiers(this.modifiers, true);
+
+ // send events to X
+ display.flush();
+
+ // press and release the actual key
+ X.Test.fake_key_event(this.display, this.key_code, true, 0);
+ X.Test.fake_key_event(this.display, this.key_code, false, 0);
+
+ // release the pressed modifiers and re-press the keys hold down by the user
+ press_modifiers(this.modifiers, false);
+ press_modifiers(current_modifiers, true);
+
+ // send events to X
+ display.flush();
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Helper method returning currently hold down modifier keys.
+ /////////////////////////////////////////////////////////////////////
+
+ private Gdk.ModifierType get_modifiers() {
+ Gdk.ModifierType modifiers;
+ Gdk.Display.get_default().get_pointer(null, null, null, out modifiers);
+ return modifiers;
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Helper method which 'presses' the desired modifier keys.
+ /////////////////////////////////////////////////////////////////////
+
+ private void press_modifiers(Gdk.ModifierType modifiers, bool down) {
+ if ((modifiers & Gdk.ModifierType.CONTROL_MASK) > 0)
+ X.Test.fake_key_event(this.display, this.ctrl_code, down, 0);
+
+ if ((modifiers & Gdk.ModifierType.SHIFT_MASK) > 0)
+ X.Test.fake_key_event(this.display, this.shift_code, down, 0);
+
+ if ((modifiers & Gdk.ModifierType.MOD1_MASK) > 0)
+ X.Test.fake_key_event(this.display, this.alt_code, down, 0);
+
+ if ((modifiers & Gdk.ModifierType.SUPER_MASK) > 0)
+ X.Test.fake_key_event(this.display, this.super_code, down, 0);
+ }
+}
+
+}
diff --git a/src/utilities/logger.vala b/src/utilities/logger.vala
new file mode 100644
index 0000000..3108ba3
--- /dev/null
+++ b/src/utilities/logger.vala
@@ -0,0 +1,194 @@
+/*
+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 beautifies the messages of the default logger.
+/// Some of this code is inspired by plank's written by Robert Dyer.
+/// Thanks a lot for this project!
+/////////////////////////////////////////////////////////////////////////
+
+public class Logger {
+
+ /////////////////////////////////////////////////////////////////////
+ /// If these are set to false, the according messages are not shown
+ /////////////////////////////////////////////////////////////////////
+
+ public static bool display_info { get; set; default = true; }
+ public static bool display_debug { get; set; default = true; }
+ public static bool display_warning { get; set; default = true; }
+ public static bool display_error { get; set; default = true; }
+
+ /////////////////////////////////////////////////////////////////////
+ /// If true, a time stamp is shown in each message.
+ /////////////////////////////////////////////////////////////////////
+
+ public static bool display_time { get; set; default = true; }
+
+ /////////////////////////////////////////////////////////////////////
+ /// If true, the origin of the message is shown. In form file:line
+ /////////////////////////////////////////////////////////////////////
+
+ public static bool display_file { get; set; default = false; }
+
+ /////////////////////////////////////////////////////////////////////
+ /// A regex, used to format the standard message.
+ /////////////////////////////////////////////////////////////////////
+
+ private static Regex regex = null;
+
+ /////////////////////////////////////////////////////////////////////
+ /// Possible terminal colors.
+ /////////////////////////////////////////////////////////////////////
+
+ private enum Color {
+ BLACK,
+ RED,
+ GREEN,
+ YELLOW,
+ BLUE,
+ PURPLE,
+ TURQUOISE,
+ WHITE
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Creates the regex and binds the handler.
+ /////////////////////////////////////////////////////////////////////
+
+ public static void init() {
+ try {
+ regex = new Regex("""(.*)\.vala(:\d+): (.*)""");
+ } catch {}
+
+ GLib.Log.set_default_handler(log_func);
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Displays an Info message.
+ /////////////////////////////////////////////////////////////////////
+
+ private static void info(string message) {
+ if (display_info) {
+ stdout.printf(set_color(Color.GREEN, false) + "[" + get_time() + "MESSAGE]" + message);
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Displays a Debug message.
+ /////////////////////////////////////////////////////////////////////
+
+ private static void debug(string message) {
+ if (display_debug) {
+ stdout.printf(set_color(Color.BLUE, false) + "[" + get_time() + " DEBUG ]" + message);
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Displays a Warning message.
+ /////////////////////////////////////////////////////////////////////
+
+ private static void warning(string message) {
+ if (display_warning) {
+ stdout.printf(set_color(Color.YELLOW, false) + "[" + get_time() + "WARNING]" + message);
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Displays a Error message.
+ /////////////////////////////////////////////////////////////////////
+
+ private static void error(string message) {
+ if (display_error) {
+ stdout.printf(set_color(Color.RED, false) + "[" + get_time() + " ERROR ]" + message);
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Helper method which resets the terminal color.
+ /////////////////////////////////////////////////////////////////////
+
+ private static string reset_color() {
+ return "\x001b[0m";
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Helper method which sets the terminal color.
+ /////////////////////////////////////////////////////////////////////
+
+ private static string set_color(Color color, bool bold) {
+ if (bold) return "\x001b[1;%dm".printf((int)color + 30);
+ else return "\x001b[0;%dm".printf((int)color + 30);
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Returns the current time in hh:mm:ss:mmmmmm
+ /////////////////////////////////////////////////////////////////////
+
+ private static string get_time() {
+ if (display_time) {
+ var now = new DateTime.now_local ();
+ return "%.2d:%.2d:%.2d:%.6d ".printf (now.get_hour (), now.get_minute (), now.get_second (), now.get_microsecond ());
+ } else {
+ return "";
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Helper method to format the message.
+ /////////////////////////////////////////////////////////////////////
+
+ private static string create_message(string message) {
+ if (display_file && regex != null && regex.match(message)) {
+ var parts = regex.split(message);
+ return " [%s%s]%s %s\n".printf(parts[1], parts[2], reset_color(), parts[3]);
+ } else if (regex != null && regex.match(message)) {
+ var parts = regex.split(message);
+ return "%s %s\n".printf(reset_color(), parts[3]);
+ } else {
+ return reset_color() + " " + message + "\n";
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// The handler function.
+ /////////////////////////////////////////////////////////////////////
+
+ private static void log_func(string? d, LogLevelFlags flags, string message) {
+
+ switch (flags) {
+ case LogLevelFlags.LEVEL_ERROR:
+ case LogLevelFlags.LEVEL_CRITICAL:
+ error(create_message(message));
+ break;
+ case LogLevelFlags.LEVEL_INFO:
+ case LogLevelFlags.LEVEL_MESSAGE:
+ info(create_message(message));
+ break;
+ case LogLevelFlags.LEVEL_DEBUG:
+ debug(create_message(message));
+ break;
+ case LogLevelFlags.LEVEL_WARNING:
+ default:
+ warning(create_message(message));
+ break;
+ }
+ }
+}
+
+}
diff --git a/src/utilities/paths.vala b/src/utilities/paths.vala
new file mode 100644
index 0000000..1c42176
--- /dev/null
+++ b/src/utilities/paths.vala
@@ -0,0 +1,211 @@
+/*
+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 relevant paths used by Gnome-Pie.
+/// These depend upon the location from which the program was launched.
+/////////////////////////////////////////////////////////////////////////
+
+public class Paths : GLib.Object {
+
+ /////////////////////////////////////////////////////////////////////
+ /// The file settings file,
+ /// usually ~/.config/gnome-pie/gnome-pie.conf.
+ /////////////////////////////////////////////////////////////////////
+
+ public static string settings { get; private set; default=""; }
+
+ /////////////////////////////////////////////////////////////////////
+ /// The file pie configuration file
+ /// usually ~/.config/gnome-pie/pies.conf.
+ /////////////////////////////////////////////////////////////////////
+
+ public static string pie_config { get; private set; default=""; }
+
+ /////////////////////////////////////////////////////////////////////
+ /// The directory containing themes installed by the user
+ /// usually ~/.config/gnome-pie/themes.
+ /////////////////////////////////////////////////////////////////////
+
+ public static string local_themes { get; private set; default=""; }
+
+ /////////////////////////////////////////////////////////////////////
+ /// The directory containing pre-installed themes
+ /// usually /usr/share/gnome-pie/themes.
+ /////////////////////////////////////////////////////////////////////
+
+ public static string global_themes { get; private set; default=""; }
+
+ /////////////////////////////////////////////////////////////////////
+ /// The directory containing locale files
+ /// usually /usr/share/locale.
+ /////////////////////////////////////////////////////////////////////
+
+ public static string locales { get; private set; default=""; }
+
+ /////////////////////////////////////////////////////////////////////
+ /// The autostart file of gnome-pie_config
+ /// usually ~/.config/autostart/gnome-pie.desktop.
+ /////////////////////////////////////////////////////////////////////
+
+ public static string autostart { get; private set; default=""; }
+
+ /////////////////////////////////////////////////////////////////////
+ /// The path where all pie-launchers are stored
+ /// usually ~/.config/gnome-pie/launchers.
+ /////////////////////////////////////////////////////////////////////
+
+ public static string launchers { get; private set; default=""; }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Initializes all values above.
+ /////////////////////////////////////////////////////////////////////
+
+ public static void init() {
+
+ // append resources to icon search path to icon theme, if neccasary
+ try {
+ var icon_dir = GLib.File.new_for_path(GLib.Path.get_dirname(
+ GLib.FileUtils.read_link("/proc/self/exe"))).get_child("resources");
+
+ if (icon_dir.query_exists()) {
+ string path = icon_dir.get_path();
+ Gtk.IconTheme.get_default().append_search_path(path);
+ }
+
+ Gtk.IconTheme.get_default().append_search_path("/usr/share/pixmaps/");
+
+ } catch (GLib.FileError e) {
+ warning("Failed to get path of executable!");
+ }
+
+ // get global paths
+ var default_dir = GLib.File.new_for_path("/usr/share/gnome-pie/");
+ if(!default_dir.query_exists()) {
+ default_dir = GLib.File.new_for_path("/usr/local/share/gnome-pie/");
+
+ if(!default_dir.query_exists()) {
+ try {
+ default_dir = GLib.File.new_for_path(GLib.Path.get_dirname(
+ GLib.FileUtils.read_link("/proc/self/exe"))).get_child("resources");
+ } catch (GLib.FileError e) {
+ warning("Failed to get path of executable!");
+ }
+ }
+ }
+
+ global_themes = default_dir.get_path() + "/themes";
+
+ // get locales path
+ var locale_dir = GLib.File.new_for_path("/usr/share/locale/de/LC_MESSAGES/gnomepie.mo");
+ if(locale_dir.query_exists()) {
+ locale_dir = GLib.File.new_for_path("/usr/share/locale");
+ } else {
+ locale_dir = GLib.File.new_for_path("/usr/local/share/locale/de/LC_MESSAGES/gnomepie.mo");
+ if(locale_dir.query_exists()) {
+ locale_dir = GLib.File.new_for_path("/usr/local/share/locale");
+ } else {
+
+ try {
+ locale_dir = GLib.File.new_for_path(GLib.Path.get_dirname(
+ GLib.FileUtils.read_link("/proc/self/exe"))).get_child(
+ "resources/locale/de/LC_MESSAGES/gnomepie.mo");
+ } catch (GLib.FileError e) {
+ warning("Failed to get path of executable!");
+ }
+
+ if(locale_dir.query_exists()) {
+ try {
+ locale_dir = GLib.File.new_for_path(GLib.Path.get_dirname(
+ GLib.FileUtils.read_link("/proc/self/exe"))).get_child("resources/locale");
+ } catch (GLib.FileError e) {
+ warning("Failed to get path of executable!");
+ }
+ }
+ }
+ }
+
+ locales = locale_dir.get_path();
+
+ // get local paths
+ var config_dir = GLib.File.new_for_path(
+ GLib.Environment.get_user_config_dir()).get_child("gnome-pie");
+
+ // create config_dir if neccasary
+ if(!config_dir.query_exists()) {
+ try {
+ config_dir.make_directory();
+ } catch (GLib.Error e) {
+ error(e.message);
+ }
+ }
+
+ // create local themes directory if neccasary
+ var themes_dir = config_dir.get_child("themes");
+ if(!themes_dir.query_exists()) {
+ try {
+ themes_dir.make_directory();
+ } catch (GLib.Error e) {
+ error(e.message);
+ }
+ }
+
+ local_themes = themes_dir.get_path();
+
+ // create launchers directory if neccasary
+ var launchers_dir = config_dir.get_child("launchers");
+ if(!launchers_dir.query_exists()) {
+ try {
+ launchers_dir.make_directory();
+ } catch (GLib.Error e) {
+ error(e.message);
+ }
+ }
+
+ launchers = launchers_dir.get_path();
+
+ // check for config file
+ var config_file = config_dir.get_child("pies.conf");
+
+ pie_config = config_file.get_path();
+ settings = config_dir.get_path() + "/gnome-pie.conf";
+
+ // autostart file name
+ autostart = GLib.Path.build_filename(GLib.Environment.get_user_config_dir(),
+ "autostart", "gnome-pie.desktop", null);
+
+ // print results
+ if (!GLib.File.new_for_path(pie_config).query_exists())
+ warning("Failed to find pie configuration file \"pies.conf\"! (This should only happen when Gnome-Pie is started for the first time...)");
+
+ if (!GLib.File.new_for_path(settings).query_exists())
+ warning("Failed to find settings file \"gnome-pie.conf\"!");
+
+ if (!GLib.File.new_for_path(local_themes).query_exists())
+ warning("Failed to find local themes directory!");
+
+ if (!GLib.File.new_for_path(launchers).query_exists())
+ warning("Failed to find launchers directory!");
+
+ if (!GLib.File.new_for_path(global_themes).query_exists())
+ warning("Failed to find global themes directory!");
+ }
+}
+
+}
diff --git a/src/utilities/renderedText.vala b/src/utilities/renderedText.vala
new file mode 100644
index 0000000..924742a
--- /dev/null
+++ b/src/utilities/renderedText.vala
@@ -0,0 +1,110 @@
+/*
+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 class representing string, rendered on an Image.
+/////////////////////////////////////////////////////////////////////////
+
+public class RenderedText : Image {
+
+ /////////////////////////////////////////////////////////////////////
+ /// A cache which stores images. It is cleared when the theme of
+ /// Gnome-Pie changes.
+ /// The key is in form <string>@<width>x<height>:<font>.
+ /////////////////////////////////////////////////////////////////////
+
+ private static Gee.HashMap<string, Cairo.ImageSurface?> cache { private get; private set; }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Initializes the cache.
+ /////////////////////////////////////////////////////////////////////
+
+ public static void init() {
+ clear_cache();
+
+ Config.global.notify["theme"].connect(() => {
+ clear_cache();
+ });
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Clears the cache.
+ /////////////////////////////////////////////////////////////////////
+
+ static void clear_cache() {
+ cache = new Gee.HashMap<string, Cairo.ImageSurface?>();
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// C'tor, creates a new image representation of a string.
+ /////////////////////////////////////////////////////////////////////
+
+ public RenderedText(string text, int width, int height, string font) {
+ var cached = this.cache.get("%s@%ux%u:%s".printf(text, width, height, font));
+
+ if (cached == null) {
+ this.render_text(text, width, height, font);
+ this.cache.set("%s@%ux%u:%s".printf(text, width, height, font), this.surface);
+ } else {
+ this.surface = cached;
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Creates a new transparent image, with text written onto.
+ /////////////////////////////////////////////////////////////////////
+
+ public void render_text(string text, int width, int height, string font) {
+ this.surface = new Cairo.ImageSurface(Cairo.Format.ARGB32, width, height);
+
+ var ctx = this.context();
+
+ // set the color as specified in the current theme
+ Color color = Config.global.theme.caption_color;
+ ctx.set_source_rgb(color.r, color.g, color.g);
+
+ var layout = Pango.cairo_create_layout(ctx);
+ layout.set_width(Pango.units_from_double(width));
+
+ var font_description = Pango.FontDescription.from_string(font);
+ font_description.set_size((int)(font_description.get_size() * Config.global.global_scale));
+
+ layout.set_font_description(font_description);
+ layout.set_text(text, -1);
+
+ // add newlines at the end of each line, in order to allow ellipsizing
+ string broken_string = "";
+ foreach (var line in layout.get_lines()) {
+ broken_string = broken_string.concat(text.substring(line.start_index, line.length), "\n");
+ }
+ layout.set_text(broken_string, broken_string.length-1);
+
+ layout.set_ellipsize(Pango.EllipsizeMode.END);
+ layout.set_alignment(Pango.Alignment.CENTER);
+
+ Pango.Rectangle extents;
+ layout.get_pixel_extents(null, out extents);
+ ctx.move_to(0, (int)(0.5*(height - extents.height)));
+
+ Pango.cairo_update_layout(ctx, layout);
+ Pango.cairo_show_layout(ctx, layout);
+ }
+}
+
+}
diff --git a/src/utilities/themedIcon.vala b/src/utilities/themedIcon.vala
new file mode 100644
index 0000000..29ae380
--- /dev/null
+++ b/src/utilities/themedIcon.vala
@@ -0,0 +1,161 @@
+/*
+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 class representing a square-shaped icon, themed according to the
+/// current theme of Gnome-Pie.
+/////////////////////////////////////////////////////////////////////////
+
+public class ThemedIcon : Image {
+
+ /////////////////////////////////////////////////////////////////////
+ /// A cache which stores loaded icon. The key is the icon name. When
+ /// the users icon theme or the theme of Gnome-Pie changes, these
+ /// cahces are cleared.
+ /////////////////////////////////////////////////////////////////////
+
+ private static Gee.HashMap<string, Cairo.ImageSurface?> active_cache { private get; private set; }
+ private static Gee.HashMap<string, Cairo.ImageSurface?> inactive_cache { private get; private set; }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Initializes the caches.
+ /////////////////////////////////////////////////////////////////////
+
+ public static void init() {
+ clear_cache();
+
+ Config.global.notify["theme"].connect(() => {
+ clear_cache();
+ });
+
+ Gtk.IconTheme.get_default().changed.connect(() => {
+ clear_cache();
+ });
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Clears the cache.
+ /////////////////////////////////////////////////////////////////////
+
+ public static void clear_cache() {
+ active_cache = new Gee.HashMap<string, Cairo.ImageSurface?>();
+ inactive_cache = new Gee.HashMap<string, Cairo.ImageSurface?>();
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Paint a slice icon according to the current theme.
+ /////////////////////////////////////////////////////////////////////
+
+ public ThemedIcon(string icon_name, bool active) {
+ // check cache
+ var current_cache = active ? active_cache : inactive_cache;
+ var cached = current_cache.get(icon_name);
+
+ if (cached != null) {
+ this.surface = cached;
+ return;
+ }
+
+ // get layers for the desired slice type
+ var layers = active ? Config.global.theme.active_slice_layers : Config.global.theme.inactive_slice_layers;
+
+ // get max size
+ int size = 0;
+ foreach (var layer in layers) {
+ if (layer.image.width() > size) size = layer.image.width();
+ }
+
+ this.surface = new Cairo.ImageSurface(Cairo.Format.ARGB32, size, size);
+
+ // get size of icon layer
+ int icon_size = size;
+ foreach (var layer in layers) {
+ if (layer.is_icon) icon_size = layer.image.width();
+ }
+
+ Image icon;
+ if (icon_name.contains("/"))
+ icon = new Image.from_file_at_size(icon_name, icon_size, icon_size);
+ else
+ icon = new Icon(icon_name, icon_size);
+
+ var color = new Color.from_icon(icon);
+ var ctx = this.context();
+
+ ctx.translate(size/2, size/2);
+ ctx.set_operator(Cairo.Operator.OVER);
+
+ // now render all layers on top of each other
+ foreach (var layer in layers) {
+
+ if (layer.colorize) {
+ ctx.push_group();
+ }
+
+ if (layer.is_icon) {
+
+ ctx.push_group();
+
+ layer.image.paint_on(ctx);
+
+ ctx.set_operator(Cairo.Operator.IN);
+
+ if (layer.image.width() != icon_size) {
+ if (icon_name.contains("/"))
+ icon = new Image.from_file_at_size(icon_name, layer.image.width(), layer.image.width());
+ else
+ icon = new Icon(icon_name,layer.image.width());
+ }
+
+ icon.paint_on(ctx);
+
+ ctx.pop_group_to_source();
+ ctx.paint();
+ ctx.set_operator(Cairo.Operator.OVER);
+
+ } else {
+ layer.image.paint_on(ctx);
+ }
+
+ // colorize the whole layer if neccasary
+ if (layer.colorize) {
+ ctx.set_operator(Cairo.Operator.ATOP);
+ ctx.set_source_rgb(color.r, color.g, color.b);
+ ctx.paint();
+
+ ctx.set_operator(Cairo.Operator.OVER);
+ ctx.pop_group_to_source();
+ ctx.paint();
+ }
+ }
+
+ // store the surface in cache
+ current_cache.set(icon_name, this.surface);
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Returns the size of the icon in pixels. Greetings to Liskov.
+ /////////////////////////////////////////////////////////////////////
+
+ public int size() {
+ return base.width();
+ }
+}
+
+}