From 98f3ef2689de06e8ab8b46a91acfa7dd2056a3a6 Mon Sep 17 00:00:00 2001
From: Alessandro Ghedini <al3xbio@gmail.com>
Date: Mon, 5 Mar 2012 12:19:59 +0100
Subject: Imported Upstream version 0.5.1

---
 src/actionGroups/devicesGroup.vala |  12 +--
 src/actionGroups/menuGroup.vala    |  15 +---
 src/actions/actionRegistry.vala    |  27 ++----
 src/actions/pieAction.vala         |  10 ++-
 src/deamon.vala                    |  21 ++++-
 src/gui/aboutWindow.vala           |   5 +-
 src/gui/newSliceWindow.vala        |   1 +
 src/gui/piePreview.vala            |   3 +
 src/gui/settingsWindow.vala        |  38 ++++++++-
 src/gui/themeList.vala             |  12 +++
 src/gui/triggerSelectButton.vala   |   4 +-
 src/images/icon.vala               |  18 ++++
 src/images/renderedText.vala       |  81 +++++++++++-------
 src/images/themedIcon.vala         | 129 +++++++++++-----------------
 src/pies/load.vala                 |   2 +
 src/pies/pieManager.vala           |  21 ++++-
 src/pies/save.vala                 |  15 ++++
 src/renderers/pieRenderer.vala     |  42 +++++++---
 src/renderers/pieWindow.vala       |  23 +++++
 src/renderers/sliceRenderer.vala   |  32 +++++--
 src/themes/sliceLayer.vala         |  50 +++++++++--
 src/themes/theme.vala              |  61 +++++++++++++-
 src/utilities/config.vala          |   5 ++
 src/utilities/logger.vala          | 168 ++++++++++++++++++++++++++++++-------
 src/utilities/paths.vala           |  44 +++++++++-
 25 files changed, 609 insertions(+), 230 deletions(-)

(limited to 'src')

diff --git a/src/actionGroups/devicesGroup.vala b/src/actionGroups/devicesGroup.vala
index dee6a6e..d3892fe 100644
--- a/src/actionGroups/devicesGroup.vala
+++ b/src/actionGroups/devicesGroup.vala
@@ -87,17 +87,9 @@ public class DevicesGroup : ActionGroup {
         // add all other devices
         foreach(var mount in this.monitor.get_mounts()) {
             // get icon
-            var icon_names = mount.get_icon().to_string().split(" ");
+            var icon = mount.get_icon();
             
-            string icon = "";
-            foreach (var icon_name in icon_names) {
-                if (Gtk.IconTheme.get_default().has_icon(icon_name)) {
-                    icon = icon_name;
-                    break;
-                }
-            }
-            
-            this.add_action(new UriAction(mount.get_name(), icon, mount.get_root().get_uri()));
+            this.add_action(new UriAction(mount.get_name(), Icon.get_icon_name(icon), mount.get_root().get_uri()));
         }
     }
     
diff --git a/src/actionGroups/menuGroup.vala b/src/actionGroups/menuGroup.vala
index 247376d..26a2662 100644
--- a/src/actionGroups/menuGroup.vala
+++ b/src/actionGroups/menuGroup.vala
@@ -131,18 +131,11 @@ public class MenuGroup : ActionGroup {
                     
                 if (type == GMenu.TreeItemType.DIRECTORY && !item.get_directory().get_is_nodisplay()) {
                     // create a MenuGroup for sub menus 
-                    string[] icons = item.get_directory().get_icon().to_string().split(" ");
-                    string final_icon = "application-default-icon";
                     
-                    // search for available icons
-                    foreach (var icon in icons) {
-                        if (Gtk.IconTheme.get_default().has_icon(icon)) {
-                            final_icon = icon;
-                            break;
-                        }
-                    }
-                
-                    var sub_menu = PieManager.create_dynamic_pie(item.get_directory().get_name(), final_icon);
+                    // get icon
+                    var icon = item.get_directory().get_icon();
+                    
+                    var sub_menu = PieManager.create_dynamic_pie(item.get_directory().get_name(), Icon.get_icon_name(icon));
                     var group = new MenuGroup.sub_menu(sub_menu.id);
                     group.add_action(new PieAction(parent_id, true));
                     group.load_contents(item.get_directory(), sub_menu.id);
diff --git a/src/actions/actionRegistry.vala b/src/actions/actionRegistry.vala
index 135e90c..705c06c 100644
--- a/src/actions/actionRegistry.vala
+++ b/src/actions/actionRegistry.vala
@@ -137,15 +137,8 @@ public class ActionRegistry : GLib.Object {
                         return new_for_desktop_file(file.get_parse_name());
                     
                     // search for an appropriate icon
-                    var gicon = info.get_icon();                
-                    string[] icons = gicon.to_string().split(" ");
-                    
-                    foreach (var icon in icons) {
-                        if (Gtk.IconTheme.get_default().has_icon(icon)) {
-                            final_icon = icon;
-                            break;
-                        }
-                    }
+                    var icon = info.get_icon();                
+                    final_icon = Icon.get_icon_name(icon);
                     
                 } catch (GLib.Error e) {
                     warning(e.message);
@@ -167,19 +160,11 @@ public class ActionRegistry : GLib.Object {
     /// A helper method which creates an AppAction for given AppInfo.
     /////////////////////////////////////////////////////////////////////
     
-    public static Action? new_for_app_info(GLib.AppInfo info) {        
-        string[] icons = info.get_icon().to_string().split(" ");
-        string final_icon = "application-default-icon";
-        
-        // search for available icons
-        foreach (var icon in icons) {
-            if (Gtk.IconTheme.get_default().has_icon(icon)) {
-                final_icon = icon;
-                break;
-            }
-        }
+    public static Action? new_for_app_info(GLib.AppInfo info) {   
+        // get icon
+        var icon = info.get_icon();     
         
-        return new AppAction(info.get_display_name() , final_icon, info.get_commandline());
+        return new AppAction(info.get_display_name(), Icon.get_icon_name(icon), info.get_commandline());
     }
     
     /////////////////////////////////////////////////////////////////////
diff --git a/src/actions/pieAction.vala b/src/actions/pieAction.vala
index 5b2c81d..faf7aca 100644
--- a/src/actions/pieAction.vala
+++ b/src/actions/pieAction.vala
@@ -58,13 +58,17 @@ public class PieAction : Action {
     public override string name {
         get {
             var referee = PieManager.all_pies[real_command];
-            if (referee != null)
-                return referee.name;
+            if (referee != null) {
+                owned_name = "↪" + referee.name;
+                return owned_name;
+            }
             return "";
         }
         protected set {}
     }
     
+    private string owned_name;
+    
     /////////////////////////////////////////////////////////////////////
     /// Returns the icon of the referenced Pie.
     /////////////////////////////////////////////////////////////////////
@@ -92,7 +96,7 @@ public class PieAction : Action {
     /////////////////////////////////////////////////////////////////////
 
     public override void activate() {
-        PieManager.open_pie(real_command);
+        PieManager.open_pie(real_command, true);
     } 
 }
 
diff --git a/src/deamon.vala b/src/deamon.vala
index ceecf1b..b622028 100644
--- a/src/deamon.vala
+++ b/src/deamon.vala
@@ -24,16 +24,26 @@ namespace GnomePie {
 /////////////////////////////////////////////////////////////////////////
 	
 public class Deamon : GLib.Object {
+
+    /////////////////////////////////////////////////////////////////////
+    /// The current version of Gnome-Pie
+    /////////////////////////////////////////////////////////////////////
+
+    public static string version;
     
     /////////////////////////////////////////////////////////////////////
     /// The beginning of everything.
     /////////////////////////////////////////////////////////////////////
 
     public static int main(string[] args) {
+        version = "0.5.1";
+    
         Logger.init();
         Gdk.threads_init();
         Gtk.init(ref args);
         Paths.init();
+        
+        message("Welcome to Gnome-Pie " + version + "!");
 
         // create the Deamon and run it
         var deamon = new GnomePie.Deamon();
@@ -90,6 +100,9 @@ public class Deamon : GLib.Object {
                 message("Removed file \"%s\"", Paths.pie_config);
             if (GLib.FileUtils.remove(Paths.settings) == 0)
                 message("Removed file \"%s\"", Paths.settings);
+                
+            Logger.stats("LAUNCH RESET");
+            
             return;
         }
     
@@ -107,11 +120,17 @@ public class Deamon : GLib.Object {
                 var data = new Unique.MessageData();
                 data.set_text(open_pie, open_pie.length);
                 app.send_message(Unique.Command.ACTIVATE, data);
+                
+                Logger.stats("LAUNCH PIE " + open_pie);
+                
                 return;
             } 
            
             message("Gnome-Pie is already running. Sending request to open config menu.");
             app.send_message(Unique.Command.ACTIVATE, null);
+            
+            Logger.stats("LAUNCH CONFIG");
+            
             return;
         }
         
@@ -139,7 +158,6 @@ public class Deamon : GLib.Object {
         
         PieManager.init();
         Icon.init();
-        ThemedIcon.init();
         
         // launch the indicator
         this.indicator = new Indicator();
@@ -150,6 +168,7 @@ public class Deamon : GLib.Object {
 	
 	    // finished loading... so run the prog!
 	    message("Started happily...");
+	    Logger.stats("LAUNCH " + version);
 	    
 	    // open pie if neccessary
 	    if (open_pie != null) PieManager.open_pie(open_pie);
diff --git a/src/gui/aboutWindow.vala b/src/gui/aboutWindow.vala
index 2df8c46..6c5820b 100644
--- a/src/gui/aboutWindow.vala
+++ b/src/gui/aboutWindow.vala
@@ -42,7 +42,8 @@ public class AboutWindow: Gtk.AboutDialog {
     		"Magnun Leno <magnun@codecommunity.org> (PT-BR)",
     		"Kim Boram <Boramism@gmail.com> (KO)",
             "Eduardo Anabalon <lalo1412@gmail.com> (ES)",
-            "Gregoire Bellon-Gervais <greggbg@gmail.com> (FR)",
+            "Grégoire Bellon-Gervais <greggbg@gmail.com> (FR)",
+            "Alex Maxime <cad.maxime@gmail.com> (FR)",
             "Eugene Roskin <pams@imail.ru> (RU)"
     	};
     	
@@ -68,7 +69,7 @@ public class AboutWindow: Gtk.AboutDialog {
             logo_icon_name: "gnome-pie",
             website: "http://www.simonschneegans.de/?page_id=12",
             website_label: "www.gnome-pie.simonschneegans.de",
-            version: "0.4.2"
+            version: Deamon.version
         );
     }
 }
diff --git a/src/gui/newSliceWindow.vala b/src/gui/newSliceWindow.vala
index 7bd6340..ade6432 100644
--- a/src/gui/newSliceWindow.vala
+++ b/src/gui/newSliceWindow.vala
@@ -258,6 +258,7 @@ public class NewSliceWindow : GLib.Object {
                     break;
                 case "key":
                     this.current_custom_icon = action.icon;
+                    this.current_hotkey = action.real_command;
                     this.key_select.set_trigger(new Trigger.from_string(action.real_command));
                     break;
                 case "pie":
diff --git a/src/gui/piePreview.vala b/src/gui/piePreview.vala
index 5745fcb..4963bb2 100644
--- a/src/gui/piePreview.vala
+++ b/src/gui/piePreview.vala
@@ -111,6 +111,9 @@ class PiePreview : Gtk.DrawingArea {
         this.new_slice_window.on_select.connect((new_action, as_new_slice, at_position) => {
             var pie = PieManager.all_pies[this.current_id];
             
+            debug(new_action.actions[0].name);
+            debug(new_action.actions[0].real_command);
+            
             if (new_action.has_quickaction())
                 renderer.disable_quickactions();
             
diff --git a/src/gui/settingsWindow.vala b/src/gui/settingsWindow.vala
index 1eaa0b4..0e7af20 100644
--- a/src/gui/settingsWindow.vala
+++ b/src/gui/settingsWindow.vala
@@ -32,6 +32,7 @@ public class SettingsWindow : GLib.Object {
     private ThemeList? theme_list = null;
     private Gtk.ToggleButton? indicator = null;
     private Gtk.ToggleButton? autostart = null;
+    private Gtk.ToggleButton? captions = null;
     
     /////////////////////////////////////////////////////////////////////
     /// C'tor creates, the dialog.
@@ -47,6 +48,14 @@ public class SettingsWindow : GLib.Object {
             this.window = builder.get_object("window") as Gtk.Dialog;
             
             this.theme_list = new ThemeList();
+            this.theme_list.on_select_new.connect(() => {
+                this.captions.active = Config.global.show_captions;
+                if (Config.global.theme.has_slice_captions) {
+                    this.captions.sensitive = true;
+                } else {
+                    this.captions.sensitive = false;
+                }
+            });
             
             var scroll_area = builder.get_object("theme-scrolledwindow") as Gtk.ScrolledWindow;
                 scroll_area.add(this.theme_list);
@@ -59,6 +68,9 @@ public class SettingsWindow : GLib.Object {
             this.indicator = (builder.get_object("indicator-checkbox") as Gtk.ToggleButton);
             this.indicator.toggled.connect(on_indicator_toggled);
             
+            this.captions = (builder.get_object("captions-checkbox") as Gtk.ToggleButton);
+            this.captions.toggled.connect(on_captions_toggled);
+            
             var scale_slider = (builder.get_object("scale-hscale") as Gtk.HScale);
                 scale_slider.set_range(0.5, 2.0);
                 scale_slider.set_increments(0.05, 0.25);
@@ -108,8 +120,15 @@ public class SettingsWindow : GLib.Object {
     
     public void show() {
         this.indicator.active = Config.global.show_indicator;
-        this.autostart.active = Config.global.auto_start;
-    
+        this.autostart.active = Config.global.auto_start;     
+        this.captions.active = Config.global.show_captions;
+        
+        if (Config.global.theme.has_slice_captions) {
+            this.captions.sensitive = true;
+        } else {
+            this.captions.sensitive = false;
+        }
+        
         this.window.show_all(); 
     }
     
@@ -119,6 +138,12 @@ public class SettingsWindow : GLib.Object {
     
     private void on_close_button_clicked() {
         this.window.hide();
+        
+        Logger.stats("SETTINGS " + Config.global.theme.name + 
+                     (this.indicator.active ? " INDICATOR" : "") +
+                     (this.autostart.active ? " AUTOSTART" : "") +
+                     (this.captions.active ? " CAPTIONS" : "") +
+                     " %f".printf(Config.global.global_scale));
     }
     
     /////////////////////////////////////////////////////////////////////
@@ -169,6 +194,15 @@ public class SettingsWindow : GLib.Object {
         var check = check_box as Gtk.CheckButton;
         Config.global.show_indicator = check.active;
     }
+    
+    /////////////////////////////////////////////////////////////////////
+    /// Shows or hides the captions of Slices.
+    /////////////////////////////////////////////////////////////////////
+    
+    private void on_captions_toggled(Gtk.ToggleButton check_box) {
+        var check = check_box as Gtk.CheckButton;
+        Config.global.show_captions = check.active;
+    }
 }
 
 }
diff --git a/src/gui/themeList.vala b/src/gui/themeList.vala
index 7aaecc6..1c038a9 100644
--- a/src/gui/themeList.vala
+++ b/src/gui/themeList.vala
@@ -23,6 +23,15 @@ namespace GnomePie {
 
 class ThemeList : Gtk.TreeView {
 
+    /////////////////////////////////////////////////////////////////////
+    /// This signal gets emitted, when a new theme is selected by the
+    /// user. This new theme is applied automatically, with this signal
+    /// actions may be triggered which should be executed AFTER the 
+    /// change to a new theme.
+    /////////////////////////////////////////////////////////////////////
+
+    public signal void on_select_new();
+
     /////////////////////////////////////////////////////////////////////
     /// The currently selected row.
     /////////////////////////////////////////////////////////////////////
@@ -69,6 +78,9 @@ class ThemeList : Gtk.TreeView {
                 Timeout.add(10, () => {
                     int index = int.parse(data.get_path(active).to_string());
                     Config.global.theme = Config.global.themes[index];
+                    
+                    this.on_select_new();
+    
                     Config.global.theme.load();
                     Config.global.theme.load_images();
                     return false;
diff --git a/src/gui/triggerSelectButton.vala b/src/gui/triggerSelectButton.vala
index eeb37e2..fd8505a 100644
--- a/src/gui/triggerSelectButton.vala
+++ b/src/gui/triggerSelectButton.vala
@@ -132,8 +132,8 @@ public class TriggerSelectButton : Gtk.ToggleButton {
         if (this.active) {
                 Gtk.Allocation rect;
                 this.get_allocation(out rect);
-                if (event.x < rect.x || event.x > rect.x + rect.width
-                 || event.y < rect.y || event.y > rect.y + rect.height) {
+                if (event.x < 0 || event.x > rect.width
+                 || event.y < 0 || event.y > rect.height) {
                  
                     this.cancel();
                     return true;
diff --git a/src/images/icon.vala b/src/images/icon.vala
index 81eb2d9..e942e7c 100644
--- a/src/images/icon.vala
+++ b/src/images/icon.vala
@@ -74,6 +74,24 @@ public class Icon : Image {
         return base.width();
     }
     
+    /////////////////////////////////////////////////////////////////////
+    /// Returns the icon name for a given GLib.Icon.
+    /////////////////////////////////////////////////////////////////////
+    
+    public static string get_icon_name(GLib.Icon? icon) {
+        if (icon != null) {
+            var icon_names = icon.to_string().split(" ");
+            
+            foreach (var icon_name in icon_names) {
+                if (Gtk.IconTheme.get_default().has_icon(icon_name)) {
+                    return icon_name;
+                }
+            }
+        }
+        
+        return "";
+    }
+    
     /////////////////////////////////////////////////////////////////////
     /// Returns the filename for a given system icon.
     /////////////////////////////////////////////////////////////////////
diff --git a/src/images/renderedText.vala b/src/images/renderedText.vala
index 41146d6..e99d26a 100644
--- a/src/images/renderedText.vala
+++ b/src/images/renderedText.vala
@@ -50,39 +50,60 @@ public class RenderedText : Image {
     
     public void render_text(string text, int width, int height, string font, 
                             Color color, double scale) {
-                            
+                  
         this.surface = new Cairo.ImageSurface(Cairo.Format.ARGB32, width, height);
 
-        var ctx = this.context();
-        
-        // set the 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() * 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");
+        if (text != "") {
+
+            var ctx = this.context();
+            
+            // set the 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() * 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 = "";
+            var lines = layout.get_lines().copy();
+            
+            foreach (var line in lines) {
+            
+                string next_line = text.substring(line.start_index, line.length);
+                
+                if (broken_string == "") {
+                    broken_string = next_line;
+                } else if (next_line != "") {
+                    // test whether the addition of a line would cause the height to become too large
+                    string broken_string_tmp = broken_string + "\n" + next_line;
+            
+                    layout.set_text(broken_string_tmp, -1);
+                    Pango.Rectangle extents;
+                    layout.get_pixel_extents(null, out extents);
+                    
+                    if (extents.height > height) broken_string = broken_string + next_line;
+                    else                         broken_string = broken_string_tmp;
+                }
+            }
+            
+            layout.set_text(broken_string, -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);
         }
-        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/images/themedIcon.vala b/src/images/themedIcon.vala
index 6c904a6..f816e0f 100644
--- a/src/images/themedIcon.vala
+++ b/src/images/themedIcon.vala
@@ -23,54 +23,12 @@ namespace GnomePie {
 /////////////////////////////////////////////////////////////////////////
 
 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;
-        }
+    public ThemedIcon(string caption, string icon_name, bool active) {
     
         // get layers for the desired slice type
         var layers = active ? Config.global.theme.active_slice_layers : Config.global.theme.inactive_slice_layers;
@@ -78,7 +36,8 @@ public class ThemedIcon : Image {
         // get max size
         int size = 1;
         foreach (var layer in layers) {
-            if (layer.image.width() > size) size = layer.image.width();
+            if (layer.image != null && layer.image.width() > size) 
+                size = layer.image.width();
         }
         
         this.surface = new Cairo.ImageSurface(Cairo.Format.ARGB32, size, size);
@@ -86,7 +45,8 @@ public class ThemedIcon : Image {
         // get size of icon layer
         int icon_size = size;
         foreach (var layer in layers) {
-            if (layer.is_icon) icon_size = layer.image.width();
+            if (layer.image != null && layer.layer_type == SliceLayer.Type.ICON)
+                icon_size = layer.image.width();
         }
     
         Image icon;
@@ -104,49 +64,54 @@ public class ThemedIcon : Image {
         // now render all layers on top of each other
         foreach (var layer in layers) {
         
-            if (layer.colorize) {
-                ctx.push_group();
-            }
-                    
-            if (layer.is_icon) {
+            if (layer.visibility == SliceLayer.Visibility.ANY || 
+                (Config.global.show_captions == (layer.visibility == SliceLayer.Visibility.WITH_CAPTION))) {
             
-                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());
+                if (layer.colorize) {
+                    ctx.push_group();
                 }
-                
-                icon.paint_on(ctx);
+                        
+                if (layer.layer_type == SliceLayer.Type.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.pop_group_to_source();
+                    ctx.paint();
+                    ctx.set_operator(Cairo.Operator.OVER);
+                    
+                } else if (layer.layer_type == SliceLayer.Type.CAPTION) {
+                    Image text = new RenderedText(caption, layer.width, layer.height, layer.font, layer.color, Config.global.global_scale);
+                    ctx.translate(0, layer.position);
+                    text.paint_on(ctx);
+                    ctx.translate(0, -layer.position);
+                } else if (layer.layer_type == SliceLayer.Type.FILE) {
+                    layer.image.paint_on(ctx);
+                }
                 
-                ctx.set_operator(Cairo.Operator.OVER);
-                ctx.pop_group_to_source();
-                ctx.paint();
+                // 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);
     }
     
     /////////////////////////////////////////////////////////////////////
diff --git a/src/pies/load.vala b/src/pies/load.vala
index b606cf5..4a9274d 100644
--- a/src/pies/load.vala
+++ b/src/pies/load.vala
@@ -36,6 +36,8 @@ namespace Pies {
             Pies.create_default_config();
             return;
         }
+        
+        message("Loading Pies from \"" + Paths.pie_config + "\".");
     
         // load the settings file
         Xml.Parser.init();
diff --git a/src/pies/pieManager.vala b/src/pies/pieManager.vala
index 162a61f..85d8a14 100644
--- a/src/pies/pieManager.vala
+++ b/src/pies/pieManager.vala
@@ -51,6 +51,14 @@ public class PieManager : GLib.Object {
     
     private static bool a_pie_is_active = false;
     
+    /////////////////////////////////////////////////////////////////////
+    /// Storing the position of the last Pie. Used for subpies, which are
+    /// opened at their parents location.
+    /////////////////////////////////////////////////////////////////////
+    
+    private static int last_x = 0;
+    private static int last_y = 0;
+    
     /////////////////////////////////////////////////////////////////////
     /// Initializes all Pies. They are loaded from the pies.conf file.
     /////////////////////////////////////////////////////////////////////
@@ -73,28 +81,35 @@ public class PieManager : GLib.Object {
     /// Opens the Pie with the given ID, if it exists.
     /////////////////////////////////////////////////////////////////////
     
-    public static void open_pie(string id) {
+    public static void open_pie(string id, bool at_last_position = false) {
         if (!a_pie_is_active) {
             Pie? pie = all_pies[id];
             
             if (pie != null) {
+                Logger.stats("OPEN " + id);
+                
                 a_pie_is_active = true;
                 
                 var window = new PieWindow();
                 window.load_pie(pie);
-                window.open();
+                
+                if (at_last_position) {
+                    window.open_at(last_x, last_y);
+                } else {
+                    window.open();
+                }
                 
                 opened_windows.add(window);
                 
                 window.on_closed.connect(() => {
                     opened_windows.remove(window);
                     if (opened_windows.size == 0) {
-                        ThemedIcon.clear_cache();
                         Icon.clear_cache();
                     }
                 });
                 
                 window.on_closing.connect(() => {
+                    window.get_center_pos(out last_x, out last_y);
                     a_pie_is_active = false;
                 });
                 
diff --git a/src/pies/save.vala b/src/pies/save.vala
index c940e5a..aadc7c8 100644
--- a/src/pies/save.vala
+++ b/src/pies/save.vala
@@ -30,6 +30,11 @@ namespace Pies {
     /////////////////////////////////////////////////////////////////////
     
     public void save() {
+        message("Saving Pies to \"" + Paths.pie_config + "\".");
+         
+        // log pie statistics
+        string pie_line = "PIES";
+         
         // initializes the XML-Writer
         var writer = new Xml.TextWriter.filename(Paths.pie_config);
         writer.set_indent(true);
@@ -42,6 +47,8 @@ namespace Pies {
             
             // if it's no dynamically created Pie
             if (pie.id.length == 3) {
+                int slice_count = 0;                
+
                 // write all attributes of the Pie
                 writer.start_element("pie");
                 writer.write_attribute("name", pie.name);
@@ -63,18 +70,26 @@ namespace Pies {
                             writer.write_attribute("command", action.real_command);
                             writer.write_attribute("quickAction", action.is_quickaction ? "true" : "false");
                             writer.end_element();
+                            
+                            ++ slice_count;
                         }
                     } else {
                         writer.start_element("group");
                             writer.write_attribute("type", GroupRegistry.descriptions[group.get_type().name()].id);
                         writer.end_element();
+                        
+                        slice_count += group.actions.size;
                     }
                 }
                 writer.end_element();
+                
+                pie_line += " " + pie.id + "(%d)".printf(slice_count);
             }
         }
         writer.end_element();
         writer.end_document();
+        
+        Logger.stats(pie_line);
     }
 }
 
diff --git a/src/renderers/pieRenderer.vala b/src/renderers/pieRenderer.vala
index 67a6b56..09c5f7a 100644
--- a/src/renderers/pieRenderer.vala
+++ b/src/renderers/pieRenderer.vala
@@ -58,23 +58,23 @@ public class PieRenderer : GLib.Object {
     public bool turbo_mode { get; private set; default=false; }
     
     /////////////////////////////////////////////////////////////////////
-    /// All SliceRenderers used to draw this Pie.
+    /// True if the pie is currently navigated with the keyboard. This is
+    /// set to false as soon as the mouse moves.
     /////////////////////////////////////////////////////////////////////
     
-    private Gee.ArrayList<SliceRenderer?> slices;
+    public bool key_board_control { get; set; default=false; }
     
     /////////////////////////////////////////////////////////////////////
-    /// The renderer for the center of this pie.
+    /// All SliceRenderers used to draw this Pie.
     /////////////////////////////////////////////////////////////////////
     
-    private CenterRenderer center;
+    private Gee.ArrayList<SliceRenderer?> slices;
     
     /////////////////////////////////////////////////////////////////////
-    /// True if the pie is currently navigated with the keyboard. This is
-    /// set to false as soon as the mouse moves.
+    /// The renderer for the center of this pie.
     /////////////////////////////////////////////////////////////////////
     
-    private bool key_board_control = false;
+    private CenterRenderer center;
     
     /////////////////////////////////////////////////////////////////////
     /// C'tor, initializes members.
@@ -130,9 +130,21 @@ public class PieRenderer : GLib.Object {
     /////////////////////////////////////////////////////////////////////
     
     public void activate() {
-        if (this.active_slice >= 0 && this.active_slice < this.slices.size)
+        if (this.active_slice >= 0 && this.active_slice < this.slices.size) {
             slices[active_slice].activate();
-        this.cancel();
+            
+            if (this.active_slice == this.quickaction)
+                Logger.stats("ACTIVATE QUICKACTION %d".printf(this.active_slice));
+            else
+                Logger.stats("ACTIVATE %d".printf(this.active_slice));
+        } else {
+            Logger.stats("CANCEL");
+        }
+        
+        foreach (var slice in this.slices)
+            slice.fade_out();
+            
+        center.fade_out();
     }
     
     /////////////////////////////////////////////////////////////////////
@@ -144,6 +156,8 @@ public class PieRenderer : GLib.Object {
             slice.fade_out();
             
         center.fade_out();
+        
+        Logger.stats("CANCEL");
     }
     
     /////////////////////////////////////////////////////////////////////
@@ -161,6 +175,8 @@ public class PieRenderer : GLib.Object {
            this.set_highlighted_slice(this.active_slice+1);
         else if (this.active_slice != top)
            this.set_highlighted_slice((this.active_slice-1+this.slice_count())%this.slice_count());
+           
+        this.key_board_control = true;
     }
     
     /////////////////////////////////////////////////////////////////////
@@ -178,6 +194,8 @@ public class PieRenderer : GLib.Object {
            this.set_highlighted_slice(this.active_slice-1);
         else if (this.active_slice != bottom)
            this.set_highlighted_slice((this.active_slice+1)%this.slice_count());
+           
+        this.key_board_control = true;
     }
     
     /////////////////////////////////////////////////////////////////////
@@ -195,6 +213,8 @@ public class PieRenderer : GLib.Object {
            this.set_highlighted_slice(this.active_slice-1);
         else if (this.active_slice < left)
            this.set_highlighted_slice(this.active_slice+1);
+        
+        this.key_board_control = true;
     }
     
     /////////////////////////////////////////////////////////////////////
@@ -212,6 +232,8 @@ public class PieRenderer : GLib.Object {
            this.set_highlighted_slice((this.active_slice+1)%this.slice_count());
         else if (this.active_slice < left && this.active_slice != right)
            this.set_highlighted_slice((this.active_slice-1+this.slice_count())%this.slice_count());
+           
+        this.key_board_control = true;
     }
     
     /////////////////////////////////////////////////////////////////////
@@ -292,8 +314,6 @@ public class PieRenderer : GLib.Object {
             
             foreach (var slice in this.slices)
                 slice.set_active_slice(active);
-            
-            this.key_board_control = true;
         }
     }
 }
diff --git a/src/renderers/pieWindow.vala b/src/renderers/pieWindow.vala
index 54dd691..852a739 100644
--- a/src/renderers/pieWindow.vala
+++ b/src/renderers/pieWindow.vala
@@ -192,6 +192,28 @@ public class PieWindow : Gtk.Window {
         }); 
     }
     
+    /////////////////////////////////////////////////////////////////////
+    /// Opens the window at a given location.
+    /////////////////////////////////////////////////////////////////////
+    
+    public void open_at(int at_x, int at_y) {
+        this.open();
+        this.move(at_x-this.width_request/2, at_y-this.height_request/2);
+    }
+    
+    /////////////////////////////////////////////////////////////////////
+    /// Gets the center position of the window.
+    /////////////////////////////////////////////////////////////////////
+    
+    public void get_center_pos(out int out_x, out int out_y) {
+        int x=0, y=0, width=0, height=0;
+        this.get_position(out x, out y);
+        this.get_size(out width, out height);
+        
+        out_x = x + width/2;
+        out_y = y + height/2;
+    }
+    
     /////////////////////////////////////////////////////////////////////
     /// Draw the Pie.
     /////////////////////////////////////////////////////////////////////
@@ -303,6 +325,7 @@ public class PieWindow : Gtk.Window {
                 else if (key >= 65 && key <= 90)   index = (int)key - 55;
                 
                 if (index >= 0 && index < this.renderer.slice_count()) {
+                    this.renderer.key_board_control = true;
                     this.renderer.set_highlighted_slice(index);
                 
                     if (this.renderer.active_slice == index) {
diff --git a/src/renderers/sliceRenderer.vala b/src/renderers/sliceRenderer.vala
index 4803070..743f13e 100644
--- a/src/renderers/sliceRenderer.vala
+++ b/src/renderers/sliceRenderer.vala
@@ -86,6 +86,7 @@ public class SliceRenderer : GLib.Object {
     private AnimatedValue alpha;            // for fading in/out
     private AnimatedValue fade_rotation;    // for fading in/out
     private AnimatedValue fade_scale;       // for fading in/out
+    private AnimatedValue wobble;           // for organic wobbling
 
     /////////////////////////////////////////////////////////////////////
     /// C'tor, initializes all AnimatedValues.
@@ -94,9 +95,10 @@ public class SliceRenderer : GLib.Object {
     public SliceRenderer(PieRenderer parent) {
         this.parent = parent;
        
-        this.fade =  new AnimatedValue.linear(0.0, 0.0, Config.global.theme.transition_time);
-        this.alpha = new AnimatedValue.linear(0.0, 1.0, Config.global.theme.fade_in_time);
-        this.scale = new AnimatedValue.cubic(AnimatedValue.Direction.OUT, 
+        this.fade =   new AnimatedValue.linear(0.0, 0.0, Config.global.theme.transition_time);
+        this.wobble = new AnimatedValue.linear(0.0, 0.0, Config.global.theme.transition_time);
+        this.alpha =  new AnimatedValue.linear(0.0, 1.0, Config.global.theme.fade_in_time);
+        this.scale =  new AnimatedValue.cubic(AnimatedValue.Direction.OUT, 
                                                  1.0/Config.global.theme.max_zoom, 
                                                  1.0/Config.global.theme.max_zoom, 
                                                  Config.global.theme.transition_time, 
@@ -127,8 +129,8 @@ public class SliceRenderer : GLib.Object {
                                             Config.global.theme.caption_color,
                                             Config.global.global_scale);
             
-        this.active_icon = new ThemedIcon(action.icon, true);
-        this.inactive_icon = new ThemedIcon(action.icon, false);
+        this.active_icon = new ThemedIcon(action.name, action.icon, true);
+        this.inactive_icon = new ThemedIcon(action.name, action.icon, false);
         
         this.color = new Color.from_icon(this.active_icon);
         
@@ -195,20 +197,36 @@ public class SliceRenderer : GLib.Object {
         this.fade.update(frame_time);
         this.fade_scale.update(frame_time);
         this.fade_rotation.update(frame_time);
+        this.wobble.update(frame_time);
 	    
 	    double direction = 2.0 * PI * position/parent.slice_count() + this.fade_rotation.val;
 	    double max_scale = 1.0/Config.global.theme.max_zoom;
         double diff = fabs(angle-direction);
-        
+
         if (diff > PI)
 	        diff = 2 * PI - diff;
+	        
+        active = ((parent.active_slice >= 0) && (diff < PI/parent.slice_count()));
+        
+        if (parent.active_slice >= 0) {
+            double wobble = Config.global.theme.wobble*diff/PI*(1-diff/PI);
+            if ((direction < angle && direction > angle - PI) || direction > PI+angle) {
+                this.wobble.reset_target(-wobble, Config.global.theme.transition_time*0.5);
+            } else {
+                this.wobble.reset_target(wobble, Config.global.theme.transition_time*0.5);
+            }
+        } else {
+            this.wobble.reset_target(0, Config.global.theme.transition_time*0.5);
+        }
+        
+        direction += this.wobble.val;
 
         if (diff < 2 * PI * Config.global.theme.zoom_range)
             max_scale = (Config.global.theme.max_zoom/(diff * (Config.global.theme.max_zoom - 1)
                         /(2 * PI * Config.global.theme.zoom_range) + 1))
                         /Config.global.theme.max_zoom;
 	    
-	    active = ((parent.active_slice >= 0) && (diff < PI/parent.slice_count()));
+	    
         
         max_scale = (parent.active_slice >= 0 ? max_scale : 1.0/Config.global.theme.max_zoom);
         
diff --git a/src/themes/sliceLayer.vala b/src/themes/sliceLayer.vala
index 2620912..3c650c0 100644
--- a/src/themes/sliceLayer.vala
+++ b/src/themes/sliceLayer.vala
@@ -23,31 +23,63 @@ namespace GnomePie {
 /////////////////////////////////////////////////////////////////////////
 
 public class SliceLayer : GLib.Object {
+
+    public enum Type { FILE, ICON, CAPTION }
+    public enum Visibility { ANY, WITH_CAPTION, WITHOUT_CAPTION }
+    
+    public Type layer_type { get; private set; }
+    public Visibility visibility { get; private set; }
     
     /////////////////////////////////////////////////////////////////////
     /// Information on the contained image.
     /////////////////////////////////////////////////////////////////////
     
     public Image image {get; set;}
-    public string icon_file {get; private set;}
+    
     
     /////////////////////////////////////////////////////////////////////
     /// Properties of this layer.
     /////////////////////////////////////////////////////////////////////
     
-    public bool colorize {get; private set; }
-    public bool is_icon {get; private set;}
-    public int icon_size {get; private set;}
+    public string icon_file {get; private set; default="";}
+    public bool colorize {get; private set; default=false;}
+    public int icon_size {get; private set; default=1;}
+    
+    public string font {get; private set; default="";}
+    public int width {get; private set; default=0;}
+    public int height {get; private set; default=0;}
+    public int position {get; private set; default=0;}
+    public Color color {get; private set; default=new Color();}
     
     /////////////////////////////////////////////////////////////////////
     /// C'tor, initializes all members of the layer.
     /////////////////////////////////////////////////////////////////////
     
-    public SliceLayer(string icon_file, int icon_size, bool colorize, bool is_icon) {
+    public SliceLayer.file(string icon_file, int icon_size, bool colorize, Visibility visibility) {
+        this.layer_type = Type.FILE;
         this.icon_file = icon_file;
         this.colorize = colorize;
-        this.is_icon = is_icon;
         this.icon_size = icon_size;
+        this.visibility = visibility;
+    }
+    
+    public SliceLayer.icon(string icon_file, int icon_size, bool colorize, Visibility visibility) {
+        this.layer_type = Type.ICON;
+        this.icon_file = icon_file;
+        this.colorize = colorize;
+        this.icon_size = icon_size;
+        this.visibility = visibility;
+    }
+    
+    public SliceLayer.caption(string font, int width, int height, int position, Color color, bool colorize, Visibility visibility) {
+        this.layer_type = Type.CAPTION;
+        this.font = font;
+        this.width = width;
+        this.height = height;
+        this.position = position;
+        this.color = color;
+        this.visibility = visibility;
+        this.colorize = colorize;
     }
     
     /////////////////////////////////////////////////////////////////////
@@ -55,9 +87,11 @@ public class SliceLayer : GLib.Object {
     /////////////////////////////////////////////////////////////////////
     
     public void load_image() {
-        if (this.icon_file == "" && this.is_icon == true) 
+        this.image = null;
+    
+        if (this.icon_file == "" && this.layer_type == Type.ICON) 
             this.image = new Image.empty(this.icon_size, this.icon_size, new Color.from_rgb(1, 1, 1));
-        else
+        else if (this.icon_file != "")
             this.image = new Image.from_file_at_size(this.icon_file, this.icon_size, this.icon_size);
     }
 }
diff --git a/src/themes/theme.vala b/src/themes/theme.vala
index 269a574..1956046 100644
--- a/src/themes/theme.vala
+++ b/src/themes/theme.vala
@@ -38,6 +38,7 @@ public class Theme : GLib.Object {
     public double max_zoom         {get; private set; default=1.2;}
     public double zoom_range       {get; private set; default=0.2;}
     public double transition_time  {get; private set; default=0.5;}
+    public double wobble           {get; private set; default=0.0;}
     public double fade_in_time     {get; private set; default=0.2;}
     public double fade_out_time    {get; private set; default=0.1;}
     public double fade_in_zoom     {get; private set; default=1.0;}
@@ -49,6 +50,7 @@ public class Theme : GLib.Object {
     public double active_radius    {get; private set; default=45.0;}
     public double slice_radius     {get; private set; default=32.0;}
     public double slice_gap        {get; private set; default=14.0;}
+    public bool   has_slice_captions {get; private set; default=false;}
     public bool   caption          {get; private set; default=false;}
     public string caption_font     {get; private set; default="sans 12";}
     public int    caption_width    {get; private set; default=100;}
@@ -182,6 +184,9 @@ public class Theme : GLib.Object {
                 case "transitiontime":
                     transition_time = double.parse(attr_content);
                     break;
+                case "wobble":
+                    wobble = double.parse(attr_content);
+                    break;
                 case "fadeintime":
                     fade_in_time = double.parse(attr_content);
                     break;
@@ -403,8 +408,15 @@ public class Theme : GLib.Object {
                 if (element_name == "slice_layer") {
                     string file = "";
                     double scale = 1.0;
-                    bool is_icon = false;
+                    SliceLayer.Type type = SliceLayer.Type.FILE;
+                    SliceLayer.Visibility visibility = SliceLayer.Visibility.ANY;
                     bool colorize = false;
+                    string slice_caption_font = "sans 8";
+                    int slice_caption_width = 50;
+                    int slice_caption_height = 20;
+                    int pos_x = 0;
+                    int pos_y = 0;
+                    Color slice_caption_color = new Color.from_rgb(1.0f, 1.0f, 1.0f);
                     
                     for (Xml.Attr* attribute = layer->properties; attribute != null; attribute = attribute->next) {
                         string attr_name = attribute->name.down();
@@ -419,13 +431,46 @@ public class Theme : GLib.Object {
                                 break;
                             case "type":
                                 if (attr_content == "icon")
-                                    is_icon = true;
+                                    type = SliceLayer.Type.ICON;
+                                else if (attr_content == "caption")
+                                    type = SliceLayer.Type.CAPTION;
                                 else if (attr_content != "file")
                                     warning("Invalid attribute content " + attr_content + " for attribute " + attr_name + " in <slice_layer> element!");
                                 break;
                             case "colorize":
                                 colorize = bool.parse(attr_content);
                                 break;
+                            case "font":
+                                slice_caption_font = attr_content;
+                                break;
+                            case "width":
+                                slice_caption_width = (int)(int.parse(attr_content) * Config.global.global_scale);
+                                if (slice_caption_width % 2 == 1)
+                                    --slice_caption_width;
+                                break;
+                            case "height":
+                                slice_caption_height = (int)(int.parse(attr_content) * Config.global.global_scale);
+                                if (slice_caption_height % 2 == 1)
+                                    --slice_caption_height;
+                                break;
+                            case "x":
+                                pos_x = (int)(double.parse(attr_content) * Config.global.global_scale);
+                                break;
+                            case "y":
+                                pos_y = (int)(double.parse(attr_content) * Config.global.global_scale);
+                                break;
+                            case "color":
+                                slice_caption_color = new Color.from_string(attr_content);
+                                break;
+                            case "visibility":
+                                if (attr_content == "without_caption")
+                                    visibility = SliceLayer.Visibility.WITHOUT_CAPTION;
+                                else if (attr_content == "with_caption") {
+                                    this.has_slice_captions = true;
+                                    visibility = SliceLayer.Visibility.WITH_CAPTION;
+                                } else if (attr_content != "any")
+                                    warning("Invalid attribute content " + attr_content + " for attribute " + attr_name + " in <slice_layer> element!");
+                                break;
                             default:
                                 warning("Invalid attribute \"" + attr_name + "\" in <slice_layer> element!");
                                 break;
@@ -438,9 +483,17 @@ public class Theme : GLib.Object {
                     int size = 2*(int)(slice_radius*scale*max_zoom);
 
                     if (slice->name.down() == "activeslice") {
-                        active_slice_layers.add(new SliceLayer(file, size, colorize, is_icon));
+                        if (type == SliceLayer.Type.ICON)         active_slice_layers.add(new SliceLayer.icon(file, size, colorize, visibility));
+                        else if (type == SliceLayer.Type.CAPTION) active_slice_layers.add(new SliceLayer.caption(slice_caption_font,
+                                                                             slice_caption_width, slice_caption_height,
+                                                                             pos_y, slice_caption_color, colorize, visibility));
+                        else                                      active_slice_layers.add(new SliceLayer.file(file, size, colorize, visibility));
                     } else {
-                        inactive_slice_layers.add(new SliceLayer(file, size, colorize, is_icon));
+                        if (type == SliceLayer.Type.ICON)         inactive_slice_layers.add(new SliceLayer.icon(file, size, colorize, visibility));
+                        else if (type == SliceLayer.Type.CAPTION) inactive_slice_layers.add(new SliceLayer.caption(slice_caption_font,
+                                                                             slice_caption_width, slice_caption_height,
+                                                                             pos_y, slice_caption_color, colorize, visibility));
+                        else                                      inactive_slice_layers.add(new SliceLayer.file(file, size, colorize, visibility));
                     }
 
                 } else {
diff --git a/src/utilities/config.vala b/src/utilities/config.vala
index 5790eef..cc776d5 100644
--- a/src/utilities/config.vala
+++ b/src/utilities/config.vala
@@ -55,6 +55,7 @@ public class Config : GLib.Object {
     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 show_captions { get; set; default = true; }
     public bool auto_start { get; set; default = false; }
     public Gee.ArrayList<Theme?> themes { get; private set; }
     
@@ -70,6 +71,7 @@ public class Config : GLib.Object {
                 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("show_captions", show_captions ? "true" : "false");
             writer.end_element();
         writer.end_document();
     }
@@ -112,6 +114,9 @@ public class Config : GLib.Object {
                         case "show_indicator":
                             show_indicator = bool.parse(attr_content);
                             break;
+                        case "show_captions":
+                            show_captions = bool.parse(attr_content);
+                            break;
                         default:
                             warning("Invalid setting \"" + attr_name + "\" in gnome-pie.conf!");
                             break;
diff --git a/src/utilities/logger.vala b/src/utilities/logger.vala
index 3108ba3..5334920 100644
--- a/src/utilities/logger.vala
+++ b/src/utilities/logger.vala
@@ -17,7 +17,7 @@ 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! 
@@ -29,22 +29,33 @@ 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; }
+    private static const bool display_debug = true; 
+    private static const bool display_warning = true; 
+    private static const bool display_error = true; 
+    private static const bool display_message = true; 
+    
+    /////////////////////////////////////////////////////////////////////
+    /// If these are set to false, the according messages are not logged
+    /////////////////////////////////////////////////////////////////////
+    
+    private static const bool log_debug = false; 
+    private static const bool log_warning = true; 
+    private static const bool log_error = true; 
+    private static const bool log_message = true; 
     
     /////////////////////////////////////////////////////////////////////
     /// If true, a time stamp is shown in each message.
     /////////////////////////////////////////////////////////////////////
     
-    public static bool display_time { get; set; default = true; }
+    private static const bool display_time = false; 
+    private static const bool log_time = true; 
     
     /////////////////////////////////////////////////////////////////////
     /// If true, the origin of the message is shown. In form file:line
     /////////////////////////////////////////////////////////////////////
     
-    public static bool display_file { get; set; default = false; }
+    private static const bool display_file = false; 
+    private static const bool log_file = false; 
     
     /////////////////////////////////////////////////////////////////////
     /// A regex, used to format the standard message.
@@ -52,6 +63,16 @@ public class Logger {
     
     private static Regex regex = null;
     
+    /////////////////////////////////////////////////////////////////////
+    /// Limit log and statistics size to roughly 1 MB.
+    /////////////////////////////////////////////////////////////////////
+    
+    private static const int max_log_length = 1000000;
+    private static const int max_stats_length = 1000000;
+    
+    private static int log_length;
+    private static int stats_length;
+    
     /////////////////////////////////////////////////////////////////////
     /// Possible terminal colors.
     /////////////////////////////////////////////////////////////////////
@@ -72,20 +93,84 @@ public class Logger {
     /////////////////////////////////////////////////////////////////////
     
     public static void init() {
+        log_length = -1;
+        stats_length = -1;
+    
         try {
 			regex = new Regex("""(.*)\.vala(:\d+): (.*)""");
 		} catch {}
 		
-        GLib.Log.set_default_handler(log_func);
+        GLib.Log.set_handler(null, GLib.LogLevelFlags.LEVEL_MASK, log_func);
+    }
+    
+    /////////////////////////////////////////////////////////////////////
+    /// Appends a line to the statistics file
+    /////////////////////////////////////////////////////////////////////
+    
+    public static void stats(string line) {
+        var stats = GLib.FileStream.open(Paths.stats, "a");
+            
+        if (stats != null) {
+            if (stats_length == -1) 
+                stats_length = (int)stats.tell();
+        
+            string final_line = "[" + get_time() + "] " + line + "\n";
+            stats.puts(final_line);
+            stats_length += final_line.length;
+        }
+        
+        if (stats_length > max_stats_length) {
+            string content = "";
+            
+            try {
+                GLib.FileUtils.get_contents(Paths.stats, out content);
+                int split_index = content.index_of_char('\n', stats_length - (int)(max_stats_length*0.9));                
+                GLib.FileUtils.set_contents(Paths.stats, content.substring(split_index+1));
+                
+                stats_length -= (split_index+1);
+            } catch (GLib.FileError e) {}
+        }
+    }
+    
+    /////////////////////////////////////////////////////////////////////
+    /// Appends a line to the log file
+    /////////////////////////////////////////////////////////////////////
+    
+    private static void write_log_line(string line) {
+        var log = GLib.FileStream.open(Paths.log, "a");
+            
+        if (log != null) {
+            if (log_length == -1) 
+                log_length = (int)log.tell();
+                
+            log.puts(line);
+            log_length += line.length;
+        }
+        
+        if (log_length > max_log_length) {
+            string content = "";
+            
+            try {
+                GLib.FileUtils.get_contents(Paths.log, out content);
+                int split_index = content.index_of_char('\n', log_length - (int)(max_log_length*0.9));                
+                GLib.FileUtils.set_contents(Paths.log, content.substring(split_index+1));
+                
+                log_length -= (split_index+1);
+            } catch (GLib.FileError e) {}
+        }
     }
     
     /////////////////////////////////////////////////////////////////////
-    /// Displays an Info message.
+    /// Displays a message.
     /////////////////////////////////////////////////////////////////////
     
-    private static void info(string message) {
-        if (display_info) {
-            stdout.printf(set_color(Color.GREEN, false) + "[" + get_time() + "MESSAGE]" + message);
+    private static void message(string message, string message_log) {
+        if (display_message) {
+            stdout.printf(set_color(Color.GREEN, false) + "[" + (display_time ? get_time() + " " : "") + "MESSAGE]" + message);
+        }
+        
+        if (log_message) {
+            write_log_line("[" + (log_time ? get_time() + " " : "") + "MESSAGE]" + message_log);
         }
     }
     
@@ -93,9 +178,13 @@ public class Logger {
     /// Displays a Debug message.
     /////////////////////////////////////////////////////////////////////
     
-    private static void debug(string message) {
+    private static void debug(string message, string message_log) {
         if (display_debug) {
-            stdout.printf(set_color(Color.BLUE, false) + "[" + get_time() + " DEBUG ]" + message);
+            stdout.printf(set_color(Color.BLUE, false) + "[" + (display_time ? get_time() + " " : "") + " DEBUG ]" + message);
+        }
+        
+        if (log_debug) {
+            write_log_line("[" + (log_time ? get_time() + " " : "") + " DEBUG ]" + message_log);
         }
     }
     
@@ -103,9 +192,13 @@ public class Logger {
     /// Displays a Warning message.
     /////////////////////////////////////////////////////////////////////
     
-    private static void warning(string message) {
+    private static void warning(string message, string message_log) {
         if (display_warning) {
-            stdout.printf(set_color(Color.YELLOW, false) + "[" + get_time() + "WARNING]" + message);
+            stdout.printf(set_color(Color.YELLOW, false) + "[" + (display_time ? get_time() + " " : "") + "WARNING]" + message);
+        }
+        
+        if (log_warning) {
+            write_log_line("[" + (log_time ? get_time() + " " : "") + "WARNING]" + message_log);
         }
     }
     
@@ -113,9 +206,13 @@ public class Logger {
     /// Displays a Error message.
     /////////////////////////////////////////////////////////////////////
     
-    private static void error(string message) {
+    private static void error(string message, string message_log) {
         if (display_error) {
-            stdout.printf(set_color(Color.RED, false) + "[" + get_time() + " ERROR ]" + message);
+            stdout.printf(set_color(Color.RED, false) + "[" + (display_time ? get_time() + " " : "") + " ERROR ]" + message);
+        }
+        
+        if (log_error) {
+            write_log_line("[" + (log_time ? get_time() + " " : "") + " ERROR ]" + message_log);
         }
     }
     
@@ -141,12 +238,8 @@ public class Logger {
 	/////////////////////////////////////////////////////////////////////
 	
 	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 "";
-		}
+        var now = new DateTime.now_local();
+	    return "%.4d:%.2d:%.2d:%.2d:%.2d:%.2d:%.6d".printf(now.get_year(), now.get_month(), now.get_day_of_month(), now.get_hour(), now.get_minute(), now.get_second(), now.get_microsecond());
 	}
 	
 	/////////////////////////////////////////////////////////////////////
@@ -165,27 +258,42 @@ public class Logger {
 		}
 	}
 	
+	/////////////////////////////////////////////////////////////////////
+    /// Helper method to format the message for logging.
+    /////////////////////////////////////////////////////////////////////
+	
+	private static string create_log_message(string message) {
+	    if (log_file && regex != null && regex.match(message)) {
+			var parts = regex.split(message);
+			return " [%s%s] %s\n".printf(parts[1], parts[2], parts[3]);
+		} else if (regex != null && regex.match(message)) {
+		    var parts = regex.split(message);
+			return " %s\n".printf(parts[3]);
+		} else {
+		    return " " + message + "\n";
+		}
+	}
+	
 	/////////////////////////////////////////////////////////////////////
 	/// The handler function.
 	/////////////////////////////////////////////////////////////////////
 	
-	private static void log_func(string? d, LogLevelFlags flags, string message) {
-			
+	private static void log_func(string? d, LogLevelFlags flags, string text) {
 		switch (flags) {
 		    case LogLevelFlags.LEVEL_ERROR:
 		    case LogLevelFlags.LEVEL_CRITICAL:
-			    error(create_message(message));
+			    error(create_message(text), create_log_message(text));
 			    break;
 		    case LogLevelFlags.LEVEL_INFO:
 		    case LogLevelFlags.LEVEL_MESSAGE:
-			    info(create_message(message));
+			    message(create_message(text), create_log_message(text));
 			    break;
 		    case LogLevelFlags.LEVEL_DEBUG:
-			    debug(create_message(message));
+			    debug(create_message(text), create_log_message(text));
 			    break;
 		    case LogLevelFlags.LEVEL_WARNING:
 		    default:
-			    warning(create_message(message));
+			    warning(create_message(text), create_log_message(text));
 			    break;
 		}
 	}
diff --git a/src/utilities/paths.vala b/src/utilities/paths.vala
index 589cc36..bc3e9b1 100644
--- a/src/utilities/paths.vala
+++ b/src/utilities/paths.vala
@@ -23,16 +23,30 @@ namespace GnomePie {
 /////////////////////////////////////////////////////////////////////////
 
 public class Paths : GLib.Object {
+
+    /////////////////////////////////////////////////////////////////////
+    /// The log file,
+    /// usually ~/.config/gnome-pie/gnome-pie.log.
+    /////////////////////////////////////////////////////////////////////
+    
+    public static string log { get; private set; default=""; }
+    
+    /////////////////////////////////////////////////////////////////////
+    /// The statistics file,
+    /// usually ~/.config/gnome-pie/gnome-pie.stats.
+    /////////////////////////////////////////////////////////////////////
+    
+    public static string stats { get; private set; default=""; }
     
     /////////////////////////////////////////////////////////////////////
-    /// The file settings file,
+    /// The settings file,
     /// usually ~/.config/gnome-pie/gnome-pie.conf.
     /////////////////////////////////////////////////////////////////////
     
     public static string settings { get; private set; default=""; }
     
     /////////////////////////////////////////////////////////////////////
-    /// The file pie configuration file
+    /// The pie configuration file
     /// usually ~/.config/gnome-pie/pies.conf.
     /////////////////////////////////////////////////////////////////////
     
@@ -186,6 +200,24 @@ public class Paths : GLib.Object {
         
         pie_config = config_file.get_path();
         settings = config_dir.get_path() + "/gnome-pie.conf";
+        log = config_dir.get_path() + "/gnome-pie.log";
+        stats = config_dir.get_path() + "/gnome-pie.stats";
+        
+        if (!GLib.File.new_for_path(log).query_exists()) {
+            try {
+                FileUtils.set_contents(log, "");
+            } catch (GLib.FileError e) {
+                error(e.message);
+            }
+        }
+        
+        if (!GLib.File.new_for_path(stats).query_exists()) {
+            try {
+                FileUtils.set_contents(stats, "");
+            } catch (GLib.FileError e) {
+                error(e.message);
+            }
+        }
         
         // autostart file name
         autostart = GLib.Path.build_filename(GLib.Environment.get_user_config_dir(), 
@@ -196,7 +228,13 @@ public class Paths : GLib.Object {
             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\"!");
+            warning("Failed to find settings file \"gnome-pie.conf\"! (This should only happen when Gnome-Pie is started for the first time...)");
+            
+        if (!GLib.File.new_for_path(log).query_exists())                                                  
+            warning("Failed to find log file \"gnome-pie.log\"!");
+            
+        if (!GLib.File.new_for_path(stats).query_exists())                                                  
+            warning("Failed to find statistics file \"gnome-pie.stats\"!");
             
         if (!GLib.File.new_for_path(local_themes).query_exists())                                                  
             warning("Failed to find local themes directory!");
-- 
cgit v1.2.3