← Back to team overview

elementaryart team mailing list archive

[Merge] lp:~victored/granite/fixes into lp:granite

 

Victor Eduardo has proposed merging lp:~victored/granite/fixes into lp:granite.

Requested reviews:
  xapantu (xapantu)
Related bugs:
  Bug #873813 in Granite: "larger primary text in About"
  https://bugs.launchpad.net/granite/+bug/873813

For more details, see:
https://code.launchpad.net/~victored/granite/fixes/+merge/88979

Bugfixes and a couple of new functions (see revision log). Also removed deprecated GTK+ elements.
-- 
https://code.launchpad.net/~victored/granite/fixes/+merge/88979
Your team elementaryart (old) is subscribed to branch lp:granite.
=== modified file 'CMakeLists.txt'
--- CMakeLists.txt	2011-08-30 12:55:45 +0000
+++ CMakeLists.txt	2012-01-18 03:59:25 +0000
@@ -15,5 +15,4 @@
 add_custom_target(dist COMMAND ${CMAKE_MAKE_PROGRAM} package_source)
 add_subdirectory (lib)
 add_subdirectory (po)
-add_subdirectory (data)
 add_subdirectory(demo)

=== removed directory 'data'
=== removed file 'data/CMakeLists.txt'
--- data/CMakeLists.txt	2011-06-13 12:51:19 +0000
+++ data/CMakeLists.txt	1970-01-01 00:00:00 +0000
@@ -1,2 +0,0 @@
-file (GLOB _cssfiles "${CMAKE_CURRENT_SOURCE_DIR}/style/*.css")
-install (FILES ${_cssfiles}  DESTINATION ${RESOURCES_DIR}/style)

=== removed directory 'data/style'
=== removed file 'data/style/CompositedWindow.css'
--- data/style/CompositedWindow.css	2011-11-01 18:48:06 +0000
+++ data/style/CompositedWindow.css	1970-01-01 00:00:00 +0000
@@ -1,20 +0,0 @@
-/*
-* Copyright (C) 2011 Maxwell Barvian
-*
-* 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/>.
-*/
-
-.composited {
-    background-color: rgba (0, 0, 0, 0.0);
-}

=== removed file 'data/style/ModeButton.css'
--- data/style/ModeButton.css	2011-09-17 22:51:19 +0000
+++ data/style/ModeButton.css	1970-01-01 00:00:00 +0000
@@ -1,45 +0,0 @@
-/*
-* Copyright (C) 2011 Giulio Collura
-*
-* 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/>.
-*/
-
-.modebutton {
-    -GtkToolbar-button-relief: normal;
-    border-radius: 0 0 0 0;
-    border-style: solid;
-    border-width: 1 0 1 1;
-
-    -unico-outer-stroke-width: 1 0 1 0;
-    -unico-outer-stroke-radius: 0 0 0 0;
-}
-
-.modebutton:active,
-.modebutton:insensitive {
-    -unico-outer-stroke-width: 1 0 1 0;
-}
-
-.modebutton:nth-child(first) {
-    border-radius: 3 0 0 3;
-    border-width: 1 0 1 1;
-
-    -unico-outer-stroke-width: 1 0 1 1;
-}
-
-.modebutton:nth-child(last) {
-    border-radius: 0 3 3 0;
-    border-width: 1;
-
-    -unico-outer-stroke-width: 1 1 1 0;
-}

=== modified file 'demo/main.vala'
--- demo/main.vala	2012-01-11 20:08:44 +0000
+++ demo/main.vala	2012-01-18 03:59:25 +0000
@@ -1,5 +1,6 @@
 /*
- * Copyright (c) 2011-2012 Lucas Baudin <xapantu@xxxxxxxxx>, Jaap Broekhuizen <jaapz.b@xxxxxxxxx>
+ * Copyright (c) 2011-2012 Lucas Baudin <xapantu@xxxxxxxxx>,
+ *                         Jaap Broekhuizen <jaapz.b@xxxxxxxxx>
  *
  * This is a free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License as
@@ -27,17 +28,15 @@
     {
         application_id = "demo.granite.org";
         program_name = "Granite Demo";
-        app_years = "2011";
+        app_years = "2012";
 
         build_version = "1.0";
-        app_icon = "text-editor";
+        app_icon = "preferences-desktop";
         main_url = "https://launchpad.net/granite";;
-        bug_url = "https://bugs.launchpad.net/granite";;
+        bug_url = "https://bugs.launchpad.net/granite/+filebug";;
         help_url = "https://answers.launchpad.net/granite";;
         translate_url = "https://translations.launchpad.net/granite";;
-        about_authors = {"Kekun",
-                         null
-                         };
+
         about_documenters = {"Valadoc",
                              null
                              };
@@ -45,7 +44,8 @@
                          null
                          };
 
-        about_authors = {"Maxwell Barvian <mbarvian@xxxxxxxxx>",
+        about_authors = {"Kekun <kekun.plazas@xxxxxxxxxxx>",
+                         "Maxwell Barvian <mbarvian@xxxxxxxxx>",
                          "Daniel Foré <bunny@xxxxxxxxxxxx>",
                          "Avi Romanoff <aviromanoff@xxxxxxxxx>",
                          null
@@ -110,6 +110,12 @@
         tool_mode.append(new Gtk.Label("4"));
         toolbutton.add(tool_mode);
         toolbar.insert(toolbutton, -1);
+
+        // Separator
+        var toolbar_separator = new Gtk.ToolItem();
+        toolbar_separator.set_expand (true);
+        toolbar.insert(toolbar_separator, -1);
+
         toolbar.insert(create_appmenu(new Gtk.Menu()), -1);
         vbox.pack_start(toolbar, false, false);toolbar = new Gtk.Toolbar();
         toolbar.get_style_context().add_class("inline-toolbar");
@@ -130,6 +136,8 @@
         mode_button.halign = Gtk.Align.CENTER;
         mode_button.append(new Gtk.Label("Small"));
         mode_button.append(new Gtk.Label("a"));
+        mode_button.append_icon ("process-stop-symbolic", Gtk.IconSize.MENU);
+
         vbox.pack_start(mode_button);
         notebook.append_page(vbox, new Gtk.Label("ModeButton"));
 

=== modified file 'lib/GtkPatch/AboutDialog.vala'
--- lib/GtkPatch/AboutDialog.vala	2011-10-12 21:13:23 +0000
+++ lib/GtkPatch/AboutDialog.vala	2012-01-18 03:59:25 +0000
@@ -1,30 +1,26 @@
-//  
+//
 //  Copyright (C) 2011 Adrien Plazas
-// 
+//
 //  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/>.
-// 
-// 
+//
+//
 //  Authors:
 //      Adrien Plazas <kekun.plazas@xxxxxxxxxxx>
 //  Artists:
 //      Daniel Foré <daniel@xxxxxxxxxxxxxxxx>
-// 
+//
 
-/* TODO
- * GtkPatch : update_website
- * Demo : 
- */
 using Gtk;
 using Gdk;
 
@@ -98,7 +94,7 @@
                 copyright_label.set_text("");
             }
             else {
-                copyright_label.set_markup("<span size=\"small\">Copyright © " + _copyright + "</span>\n");
+                copyright_label.set_markup("<span size=\"small\">Copyright © " + _copyright.replace("&", "&amp;") + "</span>\n");
                 copyright_label.show();
             }
         }
@@ -176,7 +172,7 @@
                 translators_label.set_text("");
             }
             else {
-                translators_label.set_markup("<span size=\"small\">Translated by " + _translator_credits + "</span>\n");
+                translators_label.set_markup("<span size=\"small\">Translated by " + _translator_credits.replace("&", "&amp;") + "</span>\n");
                 translators_label.show();
             }
         }
@@ -227,20 +223,29 @@
     }
 
     // UI elements
-    Image logo_image;
-    Label name_label;
-    Label copyright_label;
-    Label comments_label;
-    Label authors_label;
-    Label artists_label;
-    Label documenters_label;
-    Label translators_label;
-    Label license_label;
-    Label website_url_label;
-    Button close_button;
-
-    string big_text_markup_start;
-    string big_text_markup_end;
+    private Image logo_image;
+    private Label name_label;
+    private Label copyright_label;
+    private Label comments_label;
+    private Label authors_label;
+    private Label artists_label;
+    private Label documenters_label;
+    private Label translators_label;
+    private Label license_label;
+    private Label website_url_label;
+    private Button close_button;
+
+    // Set the markup used for big text (program name and version)
+    private const string BIG_TEXT_MARKUP_START = "<span weight='heavy' size='17200'>";
+    private const string BIG_TEXT_MARKUP_END = "</span>";
+
+    private const string STYLESHEET = """
+        * {
+            -GtkDialog-action-area-border: 12;
+            -GtkDialog-button-spacing: 10;
+            -GtkDialog-content-area-border: 0;
+        }
+    """;
 
     /**
      * Creates a new Granite.AboutDialog
@@ -250,103 +255,119 @@
         title = "";
         has_resize_grip = false;
         resizable = false;
+        deletable = false; // Hide the window's close button when possible
         set_default_response(ResponseType.CANCEL);
 
-        // Set the markup used for big text (program name and version)
-        big_text_markup_start = "<span weight='heavy' size='x-large'>";
-        big_text_markup_end = "</span>";
+        var style_provider = new CssProvider ();
+
+        try {
+            style_provider.load_from_data (STYLESHEET, -1);
+        } catch (Error e) {
+            warning ("GraniteAboutDialog: %s. The widget will not look as intended.", e.message);
+        }
+
+        get_style_context().add_provider(style_provider, STYLE_PROVIDER_PRIORITY_APPLICATION);
 
         // Set the default containers
         Box content_area = (Box)get_content_area();
         Box action_area = (Box)get_action_area();
-        action_area.set_border_width (5);
 
-        var content_hbox = new HBox(false, 12);
+        var content_hbox = new Box(Orientation.HORIZONTAL, 12);
+        var content_right_box = new Box(Orientation.VERTICAL, 0);
         var content_scrolled = new ScrolledWindow(null, new Adjustment(0, 0, 100, 1, 10, 0));
-        var content_vbox = new VBox(false, 0);
+        var content_scrolled_vbox = new Box(Orientation.VERTICAL, 0);
+        var title_vbox = new Box(Orientation.VERTICAL, 0);
+        var logo_vbox = new Box(Orientation.VERTICAL, 0);
 
         content_scrolled.shadow_type = ShadowType.NONE;
         content_scrolled.hscrollbar_policy = PolicyType.NEVER;
         content_scrolled.vscrollbar_policy = PolicyType.AUTOMATIC;
-        content_area.pack_start(content_hbox);
+
+        content_area.pack_start(content_hbox, true, true, 0);
 
         logo_image = new Image();
+        logo_vbox.pack_start(logo_image, false, false, 0);
+        logo_vbox.pack_end(new Box(Orientation.VERTICAL, 0), true, true, 0);
 
         // Adjust sizes
-        content_hbox.margin = 12;
         content_hbox.height_request = 160;
-        content_vbox.width_request = 288;
+        content_scrolled_vbox.width_request = 288;
         logo_image.set_size_request(128, 128);
 
         name_label = new Label("");
-        name_label.xalign = 0;
+        name_label.halign = Gtk.Align.START;
         name_label.set_line_wrap(true);
         name_label.set_selectable(true);
 
         copyright_label = new Label("");
         copyright_label.set_selectable(true);
-        copyright_label.xalign = 0;
+        copyright_label.halign = Gtk.Align.START;
         copyright_label.set_line_wrap(true);
 
         comments_label = new Label("");
         comments_label.set_selectable(true);
-        comments_label.xalign = 0;
+        comments_label.halign = Gtk.Align.START;
         comments_label.set_line_wrap(true);
 
         authors_label = new Label("");
         authors_label.set_selectable(true);
-        authors_label.xalign = 0;
+        authors_label.halign = Gtk.Align.START;
         authors_label.set_line_wrap(true);
 
         artists_label = new Label("");
         artists_label.set_selectable(true);
-        artists_label.xalign = 0;
+        artists_label.halign = Gtk.Align.START;
         artists_label.set_line_wrap(true);
 
         documenters_label = new Label("");
         documenters_label.set_selectable(true);
-        documenters_label.xalign = 0;
+        documenters_label.halign = Gtk.Align.START;
         documenters_label.set_line_wrap(true);
 
         translators_label = new Label("");
         translators_label.set_selectable(true);
-        translators_label.xalign = 0;
+        translators_label.halign = Gtk.Align.START;
         translators_label.set_line_wrap(true);
 
         license_label = new Widgets.WrapLabel("");
         license_label.set_selectable(true);
 
         website_url_label = new Label("");
-        website_url_label.xalign = 0;
+        website_url_label.halign = Gtk.Align.START;
         website_url_label.set_line_wrap(true);
 
-        content_hbox.pack_start(logo_image);
-        content_hbox.pack_start(content_scrolled);
-        content_scrolled.add_with_viewport(content_vbox);
-
-        content_vbox.pack_start(name_label);
-        content_vbox.pack_start(comments_label);
-        content_vbox.pack_start(website_url_label);
-
-        content_vbox.pack_start(copyright_label);
-        content_vbox.pack_start(license_label);
-
-        content_vbox.pack_start(authors_label);
-        content_vbox.pack_start(artists_label);
-        content_vbox.pack_start(documenters_label);
-        content_vbox.pack_start(translators_label);
+        // left and right padding
+        content_hbox.pack_start(new Box(Orientation.VERTICAL, 0), false, false, 0);
+        content_hbox.pack_end(new Box(Orientation.VERTICAL, 0), false, false, 0);
+
+        content_hbox.pack_start(logo_vbox);
+        content_hbox.pack_start(content_right_box);
+
+        content_scrolled.add_with_viewport(content_scrolled_vbox);
+
+        title_vbox.pack_start(name_label, false, false, 12); //FIXME
+        
+        content_right_box.pack_start(title_vbox, false, false, 0);
+        content_right_box.pack_start(content_scrolled, true, true, 0);
+        // Extra padding between the scrolled window and the action area
+        content_right_box.pack_end(new Box(Orientation.VERTICAL, 0), false, false, 6);
+
+        content_scrolled_vbox.pack_start(comments_label);
+        content_scrolled_vbox.pack_start(website_url_label);
+
+        content_scrolled_vbox.pack_start(copyright_label);
+        content_scrolled_vbox.pack_start(license_label);
+
+        content_scrolled_vbox.pack_start(authors_label);
+        content_scrolled_vbox.pack_start(artists_label);
+        content_scrolled_vbox.pack_start(documenters_label);
+        content_scrolled_vbox.pack_start(translators_label);
 
         close_button = new Button.from_stock(Stock.CLOSE);
         close_button.clicked.connect(() => { response(ResponseType.CANCEL); });
         action_area.pack_end (close_button, false, false, 0);
 
-        content_area.show();
-        content_hbox.show();
-        content_scrolled.show();
-        content_vbox.show();
-        logo_image.show();
-
-        action_area.show_all();
+        show_all();
         close_button.grab_focus();
     }
 
@@ -424,7 +445,7 @@
             name_label.set_text(program_name);
             if (version != null && version != "")
                 name_label.set_text(name_label.get_text() + " " + version);
-            name_label.set_markup(big_text_markup_start + name_label.get_text() + big_text_markup_end + "\n");
+            name_label.set_markup(BIG_TEXT_MARKUP_START + name_label.get_text().replace("&", "&amp;") + BIG_TEXT_MARKUP_END);
             name_label.show();
         }
         else
@@ -435,7 +456,7 @@
     {
         if (website != null && website != "") {
             if (website != null && website != "") {
-                website_url_label.set_markup("<a href=\"" + website + "\">" + website_label + "</a>\n");
+                website_url_label.set_markup("<a href=\"" + website + "\">" + website_label.replace("&", "&amp;") + "</a>\n");
             }
             else
                 website_url_label.set_markup("<a href=\"" + website + "\">" + website + "</a>\n");

=== modified file 'lib/Widgets/AboutDialog.vala'
--- lib/Widgets/AboutDialog.vala	2011-10-12 12:27:13 +0000
+++ lib/Widgets/AboutDialog.vala	2012-01-18 03:59:25 +0000
@@ -1,25 +1,25 @@
-//  
+//
 //  Copyright (C) 2011 Adrien Plazas
-// 
+//
 //  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/>.
-// 
-// 
+//
+//
 //  Authors:
 //      Adrien Plazas <kekun.plazas@xxxxxxxxxxx>
 //  Artists:
 //      Daniel Foré <daniel@xxxxxxxxxxxxxxxx>
-// 
+//
 
 using Gtk;
 
@@ -62,9 +62,16 @@
         }
         string _bug = "";
 
-        Button help_button;
-        Button translate_button;
-        Button bug_button;
+        private Button help_button;
+        private Button translate_button;
+        private Button bug_button;
+
+        private const string HELP_BUTTON_STYLESHEET = """
+            /* Set Roundness */
+            .help_button {
+                border-radius: 15;
+            }
+        """;
 
         /**
          * Creates a new Granite.AboutDialog
@@ -73,13 +80,24 @@
         {
             Box action_area = (Box)get_action_area();
 
+            /* help button style */
+            var help_button_style_provider = new CssProvider();
+            try {
+                help_button_style_provider.load_from_data(HELP_BUTTON_STYLESHEET, -1);
+            }
+            catch (Error e) {
+                warning ("AboutDialog: %s. Some widgets will not look as intended", e.message);
+            }
+
             /* help button */
-            help_button = new Button.with_label(" ? ");
+            help_button = new Button.with_label("?");
             help_button.get_style_context ().add_class ("help_button");
+            help_button.get_style_context ().add_provider (help_button_style_provider,
+                                                           STYLE_PROVIDER_PRIORITY_APPLICATION);
             help_button.halign = Gtk.Align.CENTER;
-            /* FIXME test & discuss and fix this ugly hack */
             help_button.set_size_request (25, -1);
             help_button.pressed.connect(() => { activate_link(help); });
+
             action_area.pack_end (help_button, false, false, 0);
             ((Gtk.ButtonBox) action_area).set_child_secondary (help_button, true);
             ((Gtk.ButtonBox) action_area).set_child_non_homogeneous (help_button, true);

=== modified file 'lib/Widgets/ModeButton.vala'
--- lib/Widgets/ModeButton.vala	2011-11-30 18:38:01 +0000
+++ lib/Widgets/ModeButton.vala	2012-01-18 03:59:25 +0000
@@ -1,34 +1,74 @@
-//  
+//
 //  Copyright (C) 2008 Christian Hergert <chris@xxxxxxxxxxxxx>
 //  Copyright (C) 2011 Giulio Collura
-// 
+//
 //  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 Gtk;
 using Gdk;
 
 namespace Granite.Widgets {
-     
-    public class ModeButton : HBox {
-      
+
+    public class ModeButton : Gtk.Box {
+
         public signal void mode_added (int index, Gtk.Widget widget);
         public signal void mode_removed (int index, Gtk.Widget widget);
         public signal void mode_changed (Gtk.Widget widget);
-        static CssProvider style_provider;
- 
+
+        /* Style properties. Please note that style class names are for internal
+           usage only. Theme developers should use GraniteWidgetsModeButton instead.
+         */
+
+        public static CssProvider style_provider;
+        public static StyleContext widget_style;
+
+        private const int style_priority = Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION;
+
+        private const string STYLESHEET = """
+            .GraniteModeButton .button {
+                -GtkToolbar-button-relief: normal;
+                border-radius: 0 0 0 0;
+                border-style: solid;
+                border-width: 1 0 1 1;
+
+                -unico-outer-stroke-width: 1 0 1 0;
+                -unico-outer-stroke-radius: 0 0 0 0;
+            }
+
+            .GraniteModeButton .button:active,
+            .GraniteModeButton .button:insensitive {
+                -unico-outer-stroke-width: 1 0 1 0;
+            }
+
+            .GraniteModeButton .button:first-child {
+                border-radius: 3 0 0 3;
+                border-width: 1 0 1 1;
+
+                -unico-outer-stroke-width: 1 0 1 1;
+            }
+
+            .GraniteModeButton .button:last-child {
+                border-radius: 0 3 3 0;
+                border-width: 1;
+
+                -unico-outer-stroke-width: 1 1 1 0;
+            }
+        """;
+
         private int _selected = -1;
+
         public int selected {
             get {
                 return _selected;
@@ -37,86 +77,113 @@
                 set_active(value);
             }
         }
- 
+
+        public uint n_items {
+            get {
+                return get_children().length();
+            }
+        }
+
         public ModeButton () {
-        
-        
-            if(style_provider == null)
+
+            if (style_provider == null)
             {
                 style_provider = new CssProvider ();
                 try {
-                    style_provider.load_from_path (RESOURCES_DIR + "/style/ModeButton.css");
+                    style_provider.load_from_data (STYLESHEET, -1);
                 } catch (Error e) {
-                    warning ("Could not add css provider. Some widgets will not look as intended. %s", e.message);
+                    warning ("GraniteModeButton: %s. The widget will not look as intended", e.message);
                 }
             }
- 
+
+            widget_style = get_style_context ();
+            widget_style.add_class ("GraniteModeButton");
+
             homogeneous = true;
             spacing = 0;
- 
             app_paintable = true;
-            set_visual (get_screen ().get_rgba_visual());
- 
+            set_visual (get_screen().get_rgba_visual());
+
             can_focus = true;
- 
-        }
- 
+        }
+
+        public void append_pixbuf (Gdk.Pixbuf? pixbuf) {
+            if (pixbuf == null)
+                return;
+
+            var image = new Image.from_pixbuf (pixbuf);
+            append (image);
+        }
+
+        public void append_text (string? text) {
+            if (text == null)
+                return;
+
+            append (new Gtk.Label(text));
+        }
+
+        /**
+         * This is the recommended function for adding icons to the ModeButton widget.
+         * If you pass the name of a symbolic icon, it will be properly themed for
+         * every state of the widget. That is, it will match the foreground color
+         * defined by the theme for each state (active, prelight, insensitive, etc.)
+         */
+        public void append_icon (string icon_name, Gtk.IconSize size) {
+            append_mode_button_item (null, icon_name, size);
+        }
+
         public void append (Gtk.Widget w) {
- 
-            var button = new ToggleButton ();
-            button.add(w);
-            //button.width_request = 30;
-            button.can_focus = false;
-            button.get_style_context ().add_class ("modebutton");
-            button.get_style_context ().add_class ("raised");
-            button.get_style_context ().add_provider (style_provider, 600);
- 
-            button.button_press_event.connect (() => {
- 
-                int select = get_children ().index (button);
-                set_active (select);
-                return true;
- 
-            });
- 
-            add (button);
-            button.show_all ();
-            
-            mode_added((int)get_children ().length (), w);
- 
-        }
-       
-        public void set_active (int new_active) {
- 
-            if (new_active >= get_children ().length () || _selected == new_active)
+            append_mode_button_item (w, null, null);
+        }
+
+        /**
+         * This function adds the foreground style properties of the given style
+         * context to the widget's icons. This is useful when you want to make the widget
+         * adapt its symbolic icon color to that of the parent in case the GTK+
+         * theme has not set them correctly. This function only affects the behavior
+         * of icons added with append_icon().
+         */
+        public void set_icon_foreground_style (Gtk.StyleContext icon_style) {
+            foreach (weak Widget button in get_children ()) {
+                (button as ModeButtonItem).set_icon_foreground_style (icon_style);
+            }
+        }
+
+        public void set_active (int new_active_index) {
+
+            if (new_active_index >= get_children().length () || _selected == new_active_index)
                 return;
- 
+
             if (_selected >= 0)
-                ((ToggleButton) get_children ().nth_data (_selected)).set_active (false);
- 
-            _selected = new_active;
-            ((ToggleButton) get_children ().nth_data (_selected)).set_active (true);
-            mode_changed(((ToggleButton) get_children ().nth_data (_selected)).get_child());
- 
-        }
-        
-        public new void remove(int number)
+                ((ToggleButton) get_children().nth_data(_selected)).set_active (false);
+
+            _selected = new_active_index;
+            ((ToggleButton) get_children().nth_data(_selected)).set_active (true);
+
+            mode_changed(((ToggleButton) get_children().nth_data(_selected)).get_child());
+        }
+
+        public void set_item_visible(int index, bool val) {
+            get_children().nth_data(index).set_visible(val);
+        }
+
+        public new void remove(int index)
         {
-            mode_removed(number, (get_children ().nth_data (number) as Gtk.Bin).get_child ());
-            get_children ().nth_data (number).destroy();
+            mode_removed(index, (get_children().nth_data(index) as Gtk.Bin).get_child ());
+            get_children().nth_data(index).destroy();
         }
- 
+
         public void clear_children () {
- 
+
             foreach (weak Widget button in get_children ()) {
                 button.hide ();
                 if (button.get_parent () != null)
                     base.remove (button);
             }
+
             _selected = -1;
- 
         }
-        
+
         protected override bool scroll_event (EventScroll ev) {
             if(ev.direction == Gdk.ScrollDirection.DOWN) {
                 selected ++;
@@ -127,6 +194,111 @@
 
             return false;
         }
+
+        private void append_mode_button_item (Gtk.Widget? w, string? icon_name, Gtk.IconSize? size) {
+            var button = new ModeButtonItem ();
+
+            /* Modifying properties */
+            if (icon_name != null && size != null && w == null) {
+                button.set_icon (icon_name, size);
+            } else {
+                button.add(w);
+            }
+
+            button.button_press_event.connect (() => {
+                int selected = get_children().index (button);
+                set_active (selected);
+                return true;
+            });
+
+            add(button);
+            button.show_all ();
+
+            mode_added((int)get_children().length(), w);
+        }
+
+    }
+
+    private class ModeButtonItem : Gtk.ToggleButton {
+
+        /** The main purpose of this class is handling icon theming **/
+
+        private bool has_themed_icon;
+        private StyleContext? icon_style;
+
+        private string icon_name = "";
+        private Gtk.IconSize? icon_size = null;
+
+        private const int style_priority = Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION;
+
+        public ModeButtonItem () {
+            can_focus = false;
+            has_themed_icon = false;
+
+            icon_style = null;
+
+            get_style_context().add_class ("button");
+            get_style_context().add_class ("raised");
+            get_style_context().add_provider (ModeButton.style_provider, style_priority);
+
+            /* We need to track state changes in order to modify the icon */
+            state_flags_changed.connect ( () => {
+                if (has_themed_icon)
+                    load_icon ();
+            });
+        }
+
+        public void set_icon_foreground_style (StyleContext? icon_style) {
+            this.icon_style = icon_style;
+        }
+
+        public new void set_icon (string name, Gtk.IconSize size) {
+            icon_name = name;
+            icon_size = size;
+
+            has_themed_icon = true;
+
+            load_icon ();
+        }
+
+        public new void set_image (Gtk.Image? image) {
+            if (image == null)
+                return;
+
+            /* Remove previous images */
+            foreach (weak Widget _image in get_children ()) {
+                if (this.get_parent () != null && _image is Gtk.Image)
+                    _image.destroy();
+            }
+
+            /* Add new image */
+            add (image);
+
+            show_all ();
+        }
+
+        private void load_icon () {
+            set_image (new Image.from_pixbuf (render_themed_icon()));
+        }
+
+        private Gdk.Pixbuf? render_themed_icon () {
+            Gdk.Pixbuf? rv = null;
+
+            int width = 0, height = 0;
+            icon_size_lookup (icon_size, out width, out height);
+
+            try {
+                var themed_icon = new GLib.ThemedIcon.with_default_fallbacks (icon_name);
+                Gtk.IconInfo? icon_info = IconTheme.get_default().lookup_by_gicon (themed_icon as GLib.Icon, height, Gtk.IconLookupFlags.GENERIC_FALLBACK);
+                if (icon_info != null)
+                    rv = icon_info.load_symbolic_for_context (icon_style ?? ModeButton.widget_style);
+            }
+            catch (Error err) {
+                warning ("%s", err.message);
+            }
+
+            return rv;
+        }
     }
 }
 

=== modified file 'lib/Widgets/PopOver.vala'
--- lib/Widgets/PopOver.vala	2011-12-11 19:12:56 +0000
+++ lib/Widgets/PopOver.vala	2012-01-18 03:59:25 +0000
@@ -46,7 +46,7 @@
     protected Gtk.Border PADDINGS;
     double offset = 15.0;
     const int MARGIN = 12;
-    bool is_composited;
+    new bool is_composited;
     Gtk.Widget menu;
     static Gtk.CssProvider style_provider;
     Gtk.Box hbox;
@@ -60,6 +60,12 @@
         BOTTOMRIGHT
     }
 
+    protected const string POPOVER_STYLESHEET = """
+        .composited {
+            background-color: rgba (0, 0, 0, 0.0);
+        }
+    """;
+
     PopPosition pos = PopPosition.TOPRIGHT;
     protected bool arrow_up = false;
     protected double arrow_offset = 35.0;
@@ -99,12 +105,12 @@
         
         /* Are we composited? */
         is_composited = Gdk.Screen.get_default ().is_composited ();
-        
+
         if(is_composited) {
             // Set up css provider
             style_provider = new Gtk.CssProvider ();
             try {
-                style_provider.load_from_path (RESOURCES_DIR + "/style/CompositedWindow.css");
+                style_provider.load_from_data (POPOVER_STYLESHEET, -1);
             } catch (Error e) {
                 warning ("Could not add css provider. Some widgets will not look as intended. %s", e.message);
             }
@@ -390,7 +396,7 @@
         
         // Background        
         main_buffer.context.clip ();
-        Gtk.render_background (menu.get_style_context (), main_buffer.context, SHADOW_SIZE, SHADOW_SIZE, w - 2 * SHADOW_SIZE, h - 2 * SHADOW_SIZE);
+        menu.get_style_context().render_background (main_buffer.context, SHADOW_SIZE, SHADOW_SIZE, w - 2 * SHADOW_SIZE, h - 2 * SHADOW_SIZE);
         if(is_composited) {
             h -= 2* (PADDINGS.top + SHADOW_SIZE) + ARROW_HEIGHT;
             w -= 2*(PADDINGS.right + SHADOW_SIZE);

=== modified file 'lib/Widgets/StaticNotebook.vala'
--- lib/Widgets/StaticNotebook.vala	2011-09-06 16:22:53 +0000
+++ lib/Widgets/StaticNotebook.vala	2012-01-18 03:59:25 +0000
@@ -20,17 +20,17 @@
 
 namespace Granite.Widgets {
     public class StaticNotebook : Gtk.VBox
-    {   
+    {
         Gtk.Notebook notebook;
         ModeButton switcher;
-        
-        public int page { 
+
+        public int page {
             set { switcher.selected = value; notebook.page = value; }
             get { return notebook.page; }
         }
-        
-        public signal void page_changed (int index); 
-        
+
+        public signal void page_changed (int index);
+
         public StaticNotebook()
         {
             notebook = new Gtk.Notebook();
@@ -44,10 +44,10 @@
             hbox.pack_start(new Gtk.HSeparator(), true, true);
             pack_start(hbox, false, false);
             pack_start(notebook);
-            
+
             switcher.mode_changed.connect(on_mode_changed);
         }
-        
+
         public void append_page(Gtk.Widget widget, Gtk.Label label)
         {
             notebook.append_page(widget, null);
@@ -56,18 +56,26 @@
             switcher.append(label);
             if(switcher.selected == -1)
                 switcher.selected = 0;
+
+            update_switcher_visibility();
         }
-        
+
         void on_mode_changed(Gtk.Widget widget)
         {
             notebook.page = switcher.selected;
             page_changed(notebook.page);
         }
-        
+
         public void remove_page(int number)
         {
             notebook.remove_page(number);
             switcher.remove(number);
+            update_switcher_visibility();
+        }
+
+        private void update_switcher_visibility() {
+            // Don't show tabs if there's only one page
+            switcher.set_visible (notebook.get_n_pages() > 1);
         }
     }
 }

=== modified file 'lib/Widgets/ToolButtonWithMenu.vala'
--- lib/Widgets/ToolButtonWithMenu.vala	2012-01-15 21:04:11 +0000
+++ lib/Widgets/ToolButtonWithMenu.vala	2012-01-18 03:59:25 +0000
@@ -22,11 +22,11 @@
  *
  */
 
-/* 
- * ToolButtonWithMenu 
+/*
+ * ToolButtonWithMenu
  * - support long click / right click with depressed button states
  * - activate a GtkAction if any or popup a menu.
- * (used in history navigation buttons next/prev, appmenu) 
+ * (used in history navigation buttons next/prev, appmenu)
  */
 
 using Gdk;
@@ -34,46 +34,42 @@
 
 namespace Granite.Widgets {
 
-    public class ToolButtonWithMenu : ToggleToolButton 
+    public class ToolButtonWithMenu : ToggleToolButton
     {
+        public signal void right_click (Gdk.EventButton ev);
+
         public Gtk.Action? myaction;
 
+        /**
+         * CENTER: Center-align the menu relative to the button's position.
+         * LEFT: Left-align the menu relative to the button's position.
+         * RIGHT: Right-align the menu relative to the button's position.
+         * INSIDE_WINDOW: Keep the menu inside the GtkWindow. Center-align when possible.
+         */
+        public enum MenuPosition {
+            CENTER,
+            LEFT,
+            RIGHT,
+            INSIDE_WINDOW
+        }
+
         /* delegate function used to populate menu */
         public delegate Gtk.Menu MenuFetcher();
 
-        private int long_press_time = Gtk.Settings.get_default().gtk_double_click_time * 2;
+        private int long_press_time = Gtk.Settings.get_default().gtk_double_click_time / 2;
         private Button button;
         public ulong toggled_sig_id;
         private int timeout = -1;
         private uint last_click_time = -1;
 
-        private PositionType _menu_orientation;
-        public PositionType menu_orientation{
-            set{
-                var orientation = value;
-                switch(orientation){
-                    case(PositionType.TOP):
-                    case(PositionType.BOTTOM):
-                        orientation = PositionType.LEFT;
-                    break;
-                }
-                _menu_orientation = orientation;
-            }
-            get{
-                return _menu_orientation;
-            }
-        }
-
-        public signal void right_click (Gdk.EventButton ev);
-
         private bool has_fetcher = false;
         private unowned MenuFetcher _fetcher;
-        public MenuFetcher fetcher{
-            set{
+        public MenuFetcher fetcher {
+            set {
                 _fetcher = value;
                 has_fetcher = true;
             }
-            get{
+            get {
                 return _fetcher;
             }
         }
@@ -82,17 +78,20 @@
         public Gtk.Menu menu {
             get {
                     return _menu;
-                }
+            }
             set {
-                    if(has_fetcher)
-                        warning ("Don't set the menu property on a ToolMenuButton when there is allready a menu fetcher");
-                    else{
+                    if(has_fetcher) {
+                        warning ("Don't set the menu property on a ToolMenuButton when there is already a menu fetcher");
+                    }
+                    else {
                         _menu = value;
                         update_menu_properties();
-                }
+                    }
             }
         }
 
+        public MenuPosition menu_position;
+
         public ToolButtonWithMenu.from_action (Gtk.Action action)
         {
             this.from_stock(action.stock_id, IconSize.MENU, action.label, new Gtk.Menu());
@@ -121,9 +120,10 @@
             menu.deactivate.connect(popdown_menu);
         }
 
-        public ToolButtonWithMenu (Image image, string label, Gtk.Menu _menu, PositionType _menu_orientation = Gtk.PositionType.LEFT)
+        public ToolButtonWithMenu (Image image, string label, Gtk.Menu _menu,
+                                    MenuPosition menu_position = MenuPosition.CENTER)
         {
-            this.menu_orientation = _menu_orientation;
+            this.menu_position = menu_position;
 
             icon_widget = image;
             label_widget = new Label (label);
@@ -200,7 +200,7 @@
                 /* right_click */
                 right_click(ev);
                 if (myaction != null)
-                    popup_menu_and_depress_button (ev); 
+                    popup_menu_and_depress_button (ev);
             }
 
             return true;
@@ -208,7 +208,6 @@
 
         private bool on_mnemonic_activate (bool group_cycling)
         {
-            //stdout.printf ("on mnemonic activate\n");
             // ToggleButton always grabs focus away from the editor,
             // so reimplement Widget's version, which only grabs the
             // focus if we are group cycling.
@@ -223,9 +222,8 @@
 
         protected new void popup_menu(Gdk.EventButton? ev = null)
         {
-            if(has_fetcher) fetch_menu();
-
-            /* FIXME select_first works ok but some gtk_return_fail make some nasty verbose lines in the terminal, origin gtk_device_grab_add gtk+-3 */
+            if(has_fetcher)
+                fetch_menu();
 
             try {
                 menu.popup (null,
@@ -236,8 +234,8 @@
             } finally {
                 // Highlight the parent
                 if (menu.attach_widget != null)
-                    menu.attach_widget.set_state(StateType.SELECTED);
-                menu.select_first (true);
+                    menu.attach_widget.set_state_flags(StateFlags.SELECTED, true);
+                menu.select_first (false);
             }
         }
 
@@ -247,7 +245,7 @@
 
             // Unhighlight the parent
             if (menu.attach_widget != null)
-                menu.attach_widget.set_state(Gtk.StateType.NORMAL);
+                menu.attach_widget.set_state_flags(StateFlags.NORMAL, true);
         }
 
         private void fetch_menu(){
@@ -272,14 +270,55 @@
             Allocation allocation;
             menu.attach_widget.get_allocation(out allocation);
 
-            x += allocation.x;
-            x -= menu_allocation.width/2;
-            x += allocation.width/2;
-            y += allocation.y;
+            if (menu_position == MenuPosition.RIGHT) {
+                x += allocation.x;
+                x -= menu_allocation.width;
+                x += allocation.width;
+            }
+            else if (menu_position != MenuPosition.LEFT) {
+                /* Centered menu */
+                x += allocation.x;
+                x -= menu_allocation.width / 2;
+                x += allocation.width / 2;
+            }
 
             int width, height;
             menu.get_size_request(out width, out height);
 
+            if (menu_position == MenuPosition.INSIDE_WINDOW) {
+                /* Get window geometry */
+                Gtk.Widget? parent_widget = get_parent();
+                Gtk.Widget? next_parent = parent_widget.get_parent();
+
+                while (true) {
+                    if (parent_widget != null && next_parent != null) {
+                        parent_widget = parent_widget.get_parent();
+                        next_parent = parent_widget.get_parent();
+                    }
+
+                    if (parent_widget == null || next_parent == null)
+                        break;
+                }
+
+                Allocation window_allocation;
+                parent_widget.get_allocation(out window_allocation);
+
+                parent_widget.get_window().get_origin (out x, out y);
+                int parent_window_x0 = x;
+                int parent_window_xf = parent_window_x0 + window_allocation.width;
+
+                /* Now check if the menu is outside the window and un-center it
+                   if that's the case */
+
+                if (x + menu_allocation.width > parent_window_xf)
+                    x = parent_window_xf - menu_allocation.width; // Move to left
+
+                if (x < parent_window_x0)
+                    x = parent_window_x0; // Move to right
+            }
+
+            y += allocation.y;
+
             if (y + height >= menu.attach_widget.get_screen().get_height())
                 y -= height;
             else

=== modified file 'lib/Widgets/Welcome.vala'
--- lib/Widgets/Welcome.vala	2012-01-14 09:29:12 +0000
+++ lib/Widgets/Welcome.vala	2012-01-18 03:59:25 +0000
@@ -37,13 +37,11 @@
     public Welcome (string title_text, string subtitle_text) {
         string _title_text = modify_text_case (title_text, CaseConversionMode.TITLE);
         string _subtitle_text = modify_text_case (subtitle_text, CaseConversionMode.SENTENCE);
+        _title_text = _title_text.replace("&", "&amp;");
+        _subtitle_text = _subtitle_text.replace("&", "&amp;");
 
         Gtk.Box content = new Gtk.Box (Gtk.Orientation.VERTICAL, 0);
 
-        // Set theming
-        this.get_style_context().add_class ("GraniteWelcomeScreen");
-        this.get_style_context().add_class ("WelcomeScreen");
-
         // Box properties
         content.homogeneous = false;
 
@@ -83,7 +81,20 @@
         add (content);
     }
 
-    public void set_sensitivity (uint index, bool val) {
+    public void set_item_visible (uint index, bool val) {
+        if(index < children.length () && children.nth_data (index) is Gtk.Widget)
+            children.nth_data (index).set_sensitive (val);
+    }
+
+    public void remove_item (uint index) {
+        if(index < children.length () && children.nth_data (index) is Gtk.Widget) {
+            var item = children.nth_data (index);
+            item.destroy ();
+            children.remove (item);
+        }
+    }
+
+    public void set_item_sensitivity (uint index, bool val) {
         if(index < children.length () && children.nth_data (index) is Gtk.Widget)
             children.nth_data (index).set_sensitive (val);
     }
@@ -101,6 +112,8 @@
     public void append_with_image (Gtk.Image? image, string option_text, string description_text) {
         string _option_text = modify_text_case (option_text, CaseConversionMode.TITLE);
         string _description_text = modify_text_case (description_text, CaseConversionMode.SENTENCE);
+        _option_text = _option_text.replace ("&", "&amp;");
+        _description_text = _description_text.replace ("&", "&amp;");
 
         // Option label
         var label = new Gtk.Label ("<span weight='medium' size='11700'>" + _option_text + "</span>");
@@ -152,25 +165,18 @@
         } );
     }
 
+    /**
+     * This function will not modify the text if it contains one or more
+     * characters outside the English alphabet.
+     */
     private string modify_text_case (string text, CaseConversionMode mode) {
-
-        /**
-         * This function will not modify the text if any the following conditions are met:
-         * - @text ends with a dot.
-         * - @text contains at least one character outside the English alphabet.
-         */
-
-        var fixed_text = new StringBuilder ();
         unichar c;
-
-        // Disabling this feature for other languages
         for (int i = 0; text.get_next_char (ref i, out c);) {
             if (c.isgraph () && !('a' <= c.tolower () && c.tolower () <= 'z'))
                 return text;
         }
-        // Checking if @text ends with a dot.
-        if (c == '.')
-            return text;
+
+        var fixed_text = new StringBuilder ();
 
         switch (mode) {
             case CaseConversionMode.TITLE:


Follow ups