← Back to team overview

lightdm-gtk-greeter-team team mailing list archive

[Merge] lp:~lightdm-gtk-greeter-team/lightdm-gtk-greeter/panel-items-options into lp:lightdm-gtk-greeter

 

Andrew P. has proposed merging lp:~lightdm-gtk-greeter-team/lightdm-gtk-greeter/panel-items-options into lp:lightdm-gtk-greeter.

Requested reviews:
  LightDM Gtk+ Greeter Development Team (lightdm-gtk-greeter-team)

For more details, see:
https://code.launchpad.net/~lightdm-gtk-greeter-team/lightdm-gtk-greeter/panel-items-options/+merge/239666

Moving all panel related code to GreeterMenuBar. Reviewing all indicators related code. Can cause some new bugs.

Greeter control panel items with set of greeter_menu_bar_set_item_{text|image|tooltip} functions and know nothing about actual items appearance.

Each panel item can be customized. format:
indicators = itemA: option1=value1, option2=value2; itemB: ...

Common options:

  text = any text
Add label to item. Using option without value just add label and do not override indicator text if it's present. http://imgur.com/a/Xtuhs#7

  markup = any text
Replace "text" option value and mark it as markup.
"~power: markup=value" is equivalent to "~power: text=value, markup"
http://imgur.com/a/Xtuhs#4

  image = /path/to/file or #icon-name
Logic is identical to "text"
http://imgur.com/a/Xtuhs#5

  fallback-image = /path/to/file or #icon-name
What image to use if set_item_image failed (default image for "~session" indicator).

  layout = text|image|text|image|image-text
Specify child widgets order for item.
http://imgur.com/a/Xtuhs#6

~power item options:
  hide-disbale=true|false (using without value is equivalent to "true")
Hide disabled actions instead of changing sensitivity.
http://imgur.com/a/Xtuhs#3

~clock options:
  clock-format = format
Replacement for "clock-format" key.

Another changes:
  Valid "scrolled" signal for IndicatorObject
  Clock font weight move to CSS
  Using g_date_time_format() instead of strftime()
  Some GreeterBackground fixes
  "show-indicators" is not supported now
-- 
https://code.launchpad.net/~lightdm-gtk-greeter-team/lightdm-gtk-greeter/panel-items-options/+merge/239666
Your team LightDM Gtk+ Greeter Development Team is requested to review the proposed merge of lp:~lightdm-gtk-greeter-team/lightdm-gtk-greeter/panel-items-options into lp:lightdm-gtk-greeter.
=== modified file 'configure.ac'
--- configure.ac	2014-08-10 12:48:13 +0000
+++ configure.ac	2014-10-26 18:59:04 +0000
@@ -95,6 +95,15 @@
     AC_DEFINE([START_INDICATOR_SERVICES], [], [Try to start indicator-services])
 ])
 
+AC_ARG_ENABLE([at-spi],
+    AC_HELP_STRING([--enable-at-spi], [Try to start at-spi launcher])
+    AC_HELP_STRING([--disable-at-spi], [Do not start at-spi launcher]),
+            [], [enable_at_spi=no])
+
+AS_IF([test "x$enable_at_spi" != "xno"], [
+    AC_DEFINE([START_AT_SPI], [], [Try to start at-spi launcher])
+])
+
 AC_ARG_WITH([libxklavier], AS_HELP_STRING([--with-libxklavier], [Use libxklavier to manage layouts (instead of LightDM API)]))
 
 AS_IF([test "x$with_libxklavier" = "xyes"],

=== modified file 'data/lightdm-gtk-greeter.conf'
--- data/lightdm-gtk-greeter.conf	2014-08-09 17:31:35 +0000
+++ data/lightdm-gtk-greeter.conf	2014-10-26 18:59:04 +0000
@@ -9,8 +9,7 @@
 # xft-dpi = Resolution for Xft in dots per inch (e.g. 96)
 # xft-hintstyle = What degree of hinting to use (none, slight, medium, or hintfull)
 # xft-rgba = Type of subpixel antialiasing (none, rgb, bgr, vrgb or vbgr)
-# show-indicators = semi-colon ";" separated list of allowed indicator modules. Built-in indicators include "~a11y", "~language", "~session", "~power". Unity indicators can be represented by short name (e.g. "sound", "power"), service file name, or absolute path
-# show-clock (true or false)
+# indicators = semi-colon ";" separated list of allowed indicator modules. Built-in indicators include "~a11y", "~language", "~session", "~power". Unity indicators can be represented by short name (e.g. "sound", "power"), service file name, or absolute path
 # clock-format = strftime-format string, e.g. %H:%M
 # keyboard = command to launch on-screen keyboard (e.g. onboard)
 # reader = command to launch screen reader (e.g. orca)
@@ -20,12 +19,21 @@
 # hide-user-image = true|false, false by default
 # screensaver-timeout = Timeout (in seconds) until the screen blanks when the greeter is called as lockscreen
 #
+# Extended "indicators" syntax:
+#   indicators = indicatorA: option1=value, option2=value, ...; indicatorB: option1=value, ...; ...
+# Common options:
+#   layout = text|image|text-image|image-text
+#   text = any text
+#   image = path or #icon-name
+#   expand = true|false
+#   align = start|center|end
+#
 # Template for monitor configuration:
-# [monitor: name]
-# background = overrides default value
-# user-background = overrides default value
-# laptop = true|false, false by default. Marks monitor as laptop display.
-# 
+#   [monitor: name]
+#   background = overrides default value
+#   user-background = overrides default value
+#   laptop = true|false, false by default. Marks monitor as laptop display.
+#
 [greeter]
 #background=
 #user-background=
@@ -36,8 +44,7 @@
 #xft-dpi=
 #xft-hintstyle=
 #xft-rgba=
-#show-indicators=
-#show-clock=
+#indicators=
 #clock-format=
 #keyboard=
 #reader=

=== modified file 'src/Makefile.am'
--- src/Makefile.am	2014-08-20 23:23:59 +0000
+++ src/Makefile.am	2014-10-26 18:59:04 +0000
@@ -3,7 +3,9 @@
 lightdm_gtk_greeter_built_sources = \
 	lightdm-gtk-greeter-ui.h \
 	lightdm-gtk-greeter-css-fallback.h \
-	lightdm-gtk-greeter-css-application.h
+	lightdm-gtk-greeter-css-application.h \
+	greetermarshalers.h \
+	greetermarshalers.c
 
 lightdm_gtk_greeter_SOURCES = \
 	$(lightdm_gtk_greeter_built_sources) \
@@ -54,6 +56,13 @@
 lightdm-gtk-greeter-css-application.h: $(srcdir)/lightdm-gtk-greeter-application.css Makefile
 	$(AM_V_GEN) exo-csource --static --name=lightdm_gtk_greeter_css_application $< >$@
 
+greetermarshalers.h: $(srcdir)/greetermarshalers.list Makefile
+	$(AM_V_GEN) glib-genmarshal --prefix=greeter_marshal --header $< >$@
+
+greetermarshalers.c: $(srcdir)/greetermarshalers.list Makefile
+	$(AM_V_GEN) echo '#include "greetermarshalers.h"' >$@
+	$(AM_V_GEN) glib-genmarshal --prefix=greeter_marshal --body $< >>$@
+
 DISTCLEANFILES = \
 	$(lightdm_gtk_greeter_built_sources)
 
@@ -65,4 +74,4 @@
 EXTRA_DIST = \
 	lightdm-gtk-greeter.glade \
 	lightdm-gtk-greeter-fallback.css \
-	lightdm-gtk-greeter-application.css
\ No newline at end of file
+	lightdm-gtk-greeter-application.css

=== modified file 'src/greeterbackground.c'
--- src/greeterbackground.c	2014-08-31 17:45:52 +0000
+++ src/greeterbackground.c	2014-10-26 18:59:04 +0000
@@ -212,6 +212,8 @@
                                                      GreeterBackground* background);
 static void greeter_background_monitors_changed_cb  (GdkScreen* screen,
                                                      GreeterBackground* background);
+static void greeter_background_child_destroyed_cb   (GtkWidget* child,
+                                                     GreeterBackground* background);
 
 /* struct BackgroundConfig */
 static gboolean background_config_initialize        (BackgroundConfig* config,
@@ -269,7 +271,7 @@
 	GObjectClass* gobject_class = G_OBJECT_CLASS(klass);
 
     background_signals[BACKGROUND_SIGNAL_ACTIVE_MONITOR_CHANGED] =
-                            g_signal_new("active-monitor-changed",
+                            g_signal_new(GREETER_BACKGROUND_SIGNAL_ACTIVE_MONITOR_CHANGED,
                                          G_TYPE_FROM_CLASS(gobject_class),
                                          G_SIGNAL_RUN_FIRST,
                                          0, /* class_offset */
@@ -302,11 +304,14 @@
     self->priv->laptop_lid_closed = FALSE;
 }
 
-GreeterBackground* 
+GreeterBackground*
 greeter_background_new(GtkWidget* child)
 {
+    g_return_val_if_fail(child != NULL, NULL);
+
     GreeterBackground* background = GREETER_BACKGROUND(g_object_new(greeter_background_get_type(), NULL));
     background->priv->child = child;
+    g_signal_connect(background->priv->child, "destroy", G_CALLBACK(greeter_background_child_destroyed_cb), background);
 	return background;
 }
 
@@ -418,12 +423,9 @@
     g_return_if_fail(GREETER_IS_BACKGROUND(background));
     g_return_if_fail(GDK_IS_SCREEN(screen));
 
-    g_debug("Connecting to screen");
+    g_debug("[Background] Connecting to screen");
 
     GreeterBackgroundPrivate* priv = background->priv;
-    gulong screen_monitors_changed_handler_id = (priv->screen == screen) ? priv->screen_monitors_changed_handler_id : 0;
-    if(screen_monitors_changed_handler_id)
-        priv->screen_monitors_changed_handler_id = 0;
 
     if(priv->screen)
         greeter_background_disconnect(background);
@@ -451,13 +453,13 @@
 
         if(!greeter_background_find_monitor_data(background, priv->configs, monitor, (gpointer*)&config))
         {
-            g_debug("No configuration options for monitor %s #%d, using default", printable_name, i);
+            g_debug("[Background] No configuration options for monitor %s #%d, using default", printable_name, i);
             config = priv->default_config;
         }
 
         gdk_screen_get_monitor_geometry(screen, i, &monitor->geometry);
 
-        g_debug("Monitor: %s #%d (%dx%d at %dx%d)%s", printable_name, i,
+        g_debug("[Background] Monitor: %s #%d (%dx%d at %dx%d)%s", printable_name, i,
                 monitor->geometry.width, monitor->geometry.height,
                 monitor->geometry.x, monitor->geometry.y,
                 (i == gdk_screen_get_primary_monitor(screen)) ? " primary" : "");
@@ -467,7 +469,7 @@
         {
             if(i < priv->monitors_size - 1 || first_not_skipped_monitor)
                 continue;
-            g_debug("Monitor %s #%d can not be skipped, using default configuration for it", printable_name, i);
+            g_debug("[Background] Monitor %s #%d can not be skipped, using default configuration for it", printable_name, i);
             if(priv->default_config->bg.type != BACKGROUND_TYPE_SKIP)
                 config = priv->default_config;
             else
@@ -534,12 +536,9 @@
     if(!priv->active_monitor)
         greeter_background_set_active_monitor(background, NULL);
 
-    if(screen_monitors_changed_handler_id)
-        priv->screen_monitors_changed_handler_id = screen_monitors_changed_handler_id;
-    else
-        priv->screen_monitors_changed_handler_id = g_signal_connect(G_OBJECT(screen), "monitors-changed",
-                                                                    G_CALLBACK(greeter_background_monitors_changed_cb),
-                                                                    background);
+    priv->screen_monitors_changed_handler_id = g_signal_connect(G_OBJECT(screen), "monitors-changed",
+                                                                G_CALLBACK(greeter_background_monitors_changed_cb),
+                                                                background);
 }
 
 void
@@ -548,13 +547,13 @@
     g_return_if_fail(GREETER_IS_BACKGROUND(background));
     GreeterBackgroundPrivate* priv = background->priv;
 
-    priv->screen = NULL;
-    priv->active_monitor = NULL;
-
     if(priv->screen_monitors_changed_handler_id)
         g_signal_handler_disconnect(priv->screen, priv->screen_monitors_changed_handler_id);
     priv->screen_monitors_changed_handler_id = 0;
 
+    priv->screen = NULL;
+    priv->active_monitor = NULL;
+
     gint i;
     for(i = 0; i < priv->monitors_size; ++i)
         monitor_finalize(&priv->monitors[i]);
@@ -647,12 +646,17 @@
 
     priv->active_monitor = active;
 
-    GtkWidget* old_parent = gtk_widget_get_parent(priv->child);
-    if(old_parent)
-        gtk_container_remove(GTK_CONTAINER(old_parent), priv->child);
-    gtk_container_add(GTK_CONTAINER(active->window), priv->child);
+    if(priv->child)
+    {
+        GtkWidget* old_parent = gtk_widget_get_parent(priv->child);
+        if(old_parent)
+            gtk_container_remove(GTK_CONTAINER(old_parent), priv->child);
+        gtk_container_add(GTK_CONTAINER(active->window), priv->child);
+    }
+    else
+        g_warning("[Background] Child widget is not defined or destroyed");
 
-    g_debug("Active monitor changed to: %s #%d", active->name, active->number);
+    g_debug("[Background] Active monitor changed to: %s #%d", active->name, active->number);
     g_signal_emit(background, background_signals[BACKGROUND_SIGNAL_ACTIVE_MONITOR_CHANGED], 0);
 
     gint x, y;
@@ -691,7 +695,7 @@
 static void
 greeter_background_try_init_dbus(GreeterBackground* background)
 {
-    g_debug("Creating DBus proxy");
+    g_debug("[Background] Creating DBus proxy");
     GError* error = NULL;
     GreeterBackgroundPrivate* priv = background->priv;
 
@@ -710,7 +714,7 @@
     if(!priv->laptop_upower_proxy)
     {
         if(error)
-            g_warning("Failed to create dbus proxy: %s", error->message);
+            g_warning("[Background] Failed to create dbus proxy: %s", error->message);
         g_clear_error(&error);
         return;
     }
@@ -719,7 +723,7 @@
     gboolean lid_present = g_variant_get_boolean(variant);
     g_variant_unref(variant);
 
-    g_debug("UPower.%s property value: %d", DBUS_UPOWER_PROP_LID_IS_PRESENT, lid_present);
+    g_debug("[Background] UPower.%s property value: %d", DBUS_UPOWER_PROP_LID_IS_PRESENT, lid_present);
 
     if(!lid_present)
         greeter_background_stop_dbus(background);
@@ -769,7 +773,7 @@
     if(new_state == priv->laptop_lid_closed)
         return;
 
-    g_debug("UPower: lid state changed to '%s'", priv->laptop_lid_closed ? "closed" : "opened");
+    g_debug("[Background] UPower: lid state changed to '%s'", priv->laptop_lid_closed ? "closed" : "opened");
 
     priv->laptop_lid_closed = new_state;
     if(priv->laptop_monitors)
@@ -787,6 +791,13 @@
     greeter_background_connect(background, screen);
 }
 
+static void
+greeter_background_child_destroyed_cb(GtkWidget* child,
+                                      GreeterBackground* background)
+{
+    background->priv->child = NULL;
+}
+
 void
 greeter_background_set_custom_background(GreeterBackground* background,
                                          const gchar* value)
@@ -904,7 +915,7 @@
             config->options.image.mode = SCALING_MODE_ZOOMED;
 
         config->options.image.path = g_strdup(value);
-        config->type = BACKGROUND_TYPE_IMAGE;        
+        config->type = BACKGROUND_TYPE_IMAGE;
     }
     return TRUE;
 }
@@ -958,7 +969,7 @@
                                              images_cache);
         if(!pixbuf)
         {
-            g_warning("Failed to read wallpaper: %s", config->options.image.path);
+            g_warning("[Background] Failed to read wallpaper: %s", config->options.image.path);
             return FALSE;
         }
         bg->options.image = pixbuf;
@@ -996,7 +1007,13 @@
     if(monitor->window_draw_handler_id)
         g_signal_handler_disconnect(monitor->window, monitor->window_draw_handler_id);
     if(monitor->window)
+    {
+        GtkWidget* child = gtk_bin_get_child(GTK_BIN(monitor->window));
+        if(child)
+            /* remove greeter widget to avoid "destroy" signal */
+            gtk_container_remove(GTK_CONTAINER(monitor->window), child);
         gtk_widget_destroy(GTK_WIDGET(monitor->window));
+    }
     monitor->name = NULL;
     monitor->window = NULL;
     monitor->window_draw_handler_id = 0;
@@ -1055,7 +1072,10 @@
     {
         key = g_strdup_printf("%s\n%d %dx%d", path, mode, width, height);
         if (g_hash_table_lookup_extended(cache, key, NULL, (gpointer*)&pixbuf))
+        {
+            g_free(key);
             return GDK_PIXBUF(g_object_ref(pixbuf));
+        }
     }
 
     if (!cache || !g_hash_table_lookup_extended(cache, path, NULL, (gpointer*)&pixbuf))
@@ -1064,21 +1084,24 @@
         pixbuf = gdk_pixbuf_new_from_file(path, &error);
         if(error)
         {
-            g_warning("Failed to load background: %s", error->message);
+            g_warning("[Background] Failed to load background: %s", error->message);
             g_clear_error(&error);
         }
         else if(cache)
-            g_hash_table_insert(cache, g_strdup(path), g_object_ref (pixbuf));
+            g_hash_table_insert(cache, g_strdup(path), g_object_ref(pixbuf));
     }
+    else
+        pixbuf = g_object_ref(pixbuf);
 
     if(pixbuf)
     {
         GdkPixbuf* scaled = scale_image(pixbuf, mode, width, height);
-        if (cache)
+        if(cache)
             g_hash_table_insert(cache, g_strdup(key), g_object_ref(scaled));
         g_object_unref(pixbuf);
         pixbuf = scaled;
     }
+    g_free(key);
 
     return pixbuf;
 }
@@ -1142,7 +1165,7 @@
     display = XOpenDisplay (gdk_display_get_name (gdk_screen_get_display (screen)));
     if (!display)
     {
-        g_warning ("Failed to create root pixmap");
+        g_warning("[Background] Failed to create root pixmap");
         return NULL;
     }
 
@@ -1165,7 +1188,7 @@
                    Display* display,
                    Pixmap xpixmap)
 {
-    
+
     Window xroot = RootWindow (display, gdk_screen_get_number (screen));
     char *atom_names[] = {"_XROOTPMAP_ID", "ESETROOT_PMAP_ID"};
     Atom atoms[G_N_ELEMENTS(atom_names)] = {0};
@@ -1214,7 +1237,7 @@
      */
     if (!XInternAtoms (display, atom_names, G_N_ELEMENTS(atom_names), False, atoms) ||
         atoms[0] == None || atoms[1] == None) {
-        g_warning("Could not create atoms needed to set root pixmap id/properties.\n");
+        g_warning("[Background] Could not create atoms needed to set root pixmap id/properties.\n");
         return;
     }
 

=== modified file 'src/greeterbackground.h'
--- src/greeterbackground.h	2014-08-31 17:45:52 +0000
+++ src/greeterbackground.h	2014-10-26 18:59:04 +0000
@@ -12,6 +12,8 @@
 #define GREETER_IS_BACKGROUND(obj)          (G_TYPE_CHECK_INSTANCE_TYPE((obj), GREETER_BACKGROUND_TYPE))
 #define GREETER_IS_BACKGROUND_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE((klass), GREETER_BACKGROUND_TYPE))
 
+#define GREETER_BACKGROUND_SIGNAL_ACTIVE_MONITOR_CHANGED    "active-monitor-changed"
+
 typedef struct _GreeterBackground           GreeterBackground;
 typedef struct _GreeterBackgroundClass      GreeterBackgroundClass;
 
@@ -34,6 +36,7 @@
 gchar** greeter_background_get_configured_monitors  (GreeterBackground* background);
 void greeter_background_connect                     (GreeterBackground* background,
                                                      GdkScreen* screen);
+void greeter_background_disconnect                  (GreeterBackground* background);
 void greeter_background_set_custom_background       (GreeterBackground* background,
                                                      const gchar* path);
 void greeter_background_save_xroot                  (GreeterBackground* background);

=== added file 'src/greetermarshalers.list'
--- src/greetermarshalers.list	1970-01-01 00:00:00 +0000
+++ src/greetermarshalers.list	2014-10-26 18:59:04 +0000
@@ -0,0 +1,28 @@
+# see glib-genmarshal(1) for a detailed description of the file format,
+# possible parameter types are:
+# VOID indicates no return type, or no extra
+# parameters. if VOID is used as the parameter
+# list, no additional parameters may be present.
+# BOOLEAN for boolean types (gboolean)
+# CHAR for signed char types (gchar)
+# UCHAR for unsigned char types (guchar)
+# INT for signed integer types (gint)
+# UINT for unsigned integer types (guint)
+# LONG for signed long integer types (glong)
+# ULONG for unsigned long integer types (gulong)
+# ENUM for enumeration types (gint)
+# FLAGS for flag enumeration types (guint)
+# FLOAT for single-precision float types (gfloat)
+# DOUBLE for double-precision float types (gdouble)
+# STRING for string types (gchar*)
+# BOXED for boxed (anonymous but reference counted) types (GBoxed*)
+# POINTER for anonymous pointer types (gpointer)
+# PARAM for GParamSpec or derived types (GParamSpec*)
+# OBJECT for GObject or derived types (GObject*)
+# NONE deprecated alias for VOID
+# BOOL deprecated alias for BOOLEAN
+#
+# GreeterMenuBar: item unknown-item(name, options)
+OBJECT: STRING, POINTER
+# GreeterMenuBar: applied unknown-item-option(item, item-name, option-name, option-value)
+BOOLEAN: OBJECT, STRING, STRING, STRING

=== modified file 'src/greetermenubar.c'
--- src/greetermenubar.c	2014-08-10 12:48:13 +0000
+++ src/greetermenubar.c	2014-10-26 18:59:04 +0000
@@ -1,39 +1,674 @@
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
 #include <gtk/gtk.h>
+
+#ifdef HAVE_LIBINDICATOR
+#include <libindicator/indicator-object.h>
+#ifdef HAVE_LIBINDICATOR_NG
+#include <libindicator/indicator-ng.h>
+#endif
+#endif
+
+#include "src/greetermarshalers.h"
+
 #include "greetermenubar.h"
 
-static void greeter_menu_bar_size_allocate(GtkWidget* widget, GtkAllocation* allocation);
-
-G_DEFINE_TYPE(GreeterMenuBar, greeter_menu_bar, GTK_TYPE_MENU_BAR);
+struct _GreeterMenuBar
+{
+	GtkMenuBar parent_instance;
+	struct _GreeterMenuBarPrivate* priv;
+};
+
+struct _GreeterMenuBarClass
+{
+	GtkMenuBarClass parent_class;
+};
+
+/* Styles */
+static const gchar* ITEM_STYLE_COMMON           = "panel-item";
+static const gchar* ITEM_STYLE_HOVERED          = "panel-item-hovered";
+static const gchar* ITEM_STYLE_INDICATOR        = "panel-item-indicator";
+static const gchar* ITEM_STYLE_TEXT             = "panel-item-text";
+static const gchar* ITEM_STYLE_SEPARATOR        = "panel-item-separator";
+static const gchar* ITEM_STYLE_SPACER           = "panel-item-spacer";
+
+/* Common options */
+static const gchar* ITEM_OPTION_NAME            = "name";
+static const gchar* ITEM_OPTION_LAYOUT          = "layout";
+static const gchar* ITEM_OPTION_TEXT            = "text";
+static const gchar* ITEM_OPTION_IMAGE           = "image";
+static const gchar* ITEM_OPTION_FALLBACK_IMAGE  = "fallback-image";
+static const gchar* ITEM_OPTION_TOOLTIP         = "tooltip";
+static const gchar* ITEM_OPTION_MARKUP          = "markup";
+static const gchar* ITEM_OPTION_EXPAND          = "expand";
+static const gchar* ITEM_OPTION_ALIGN           = "align";
+
+#ifdef HAVE_LIBINDICATOR
+static const gchar* COMMON_OPTIONS_KEY          = "*";
+#endif
+
+typedef enum
+{
+    ITEM_LAYOUT_INVALID     = 0x00,
+    ITEM_LAYOUT_TEXT        = 0x01,
+    ITEM_LAYOUT_IMAGE       = 0x02,
+    ITEM_LAYOUT_REVERSED    = 0x04,
+    ITEM_LAYOUT_NO_CHILDREN = 0x08, /* Special layout for separators and spacers */
+} ItemLayout;
+
+typedef struct
+{
+    GreeterMenuBar*menubar;
+
+    gint position;
+    ItemLayout layout;
+    const gchar* css_style;
+
+    GtkWidget* widget;
+    GtkWidget* label_widget;
+    GtkWidget* image_widget;
+
+    gboolean protect_label;
+    gboolean protect_image;
+    gboolean protect_tooltip;
+    gboolean label_markup;
+
+    gchar* fallback_image;
+
+    #ifdef HAVE_LIBINDICATOR
+    struct
+    {
+        IndicatorObject* object;
+        IndicatorObjectEntry* entry;
+    } indicator;
+    #endif
+} ChildInfo;
+
+#ifdef HAVE_LIBINDICATOR
+typedef struct
+{
+    GreeterMenuBar* menubar;
+
+    IndicatorObject* object;
+    GHashTable* entries;            /* <IndicatorObjectEntry*> => <ChildInfo*> */
+    GHashTable* options;
+
+    gint position;
+} IndicatorInfo;
+#endif
+
+typedef struct _GreeterMenuBarPrivate
+{
+    GHashTable* children;           /* <GtkMenuItem*> => <ChildInfo*> */
+    GHashTable* common_options;     /* Set of <const gchar*>, used to pass only unknown options to "unknown-item-option" handler */
+    gboolean items_visibility;
+
+    #ifdef HAVE_LIBINDICATOR
+    struct
+    {
+        GHashTable* list;           /* <IndicatorObject*> => <IndicatorInfo*> */
+        gboolean inited;
+    } indicators;
+    #endif
+} GreeterMenuBarPrivate;
+
+enum
+{
+    ITEMS_VISIBILITY_CHANGED_SIGNAL,
+    UNKNOWN_ITEM_SIGNAL,
+    UNKNOWN_ITEM_OPTION_SIGNAL,
+    LAST_SIGNAL
+};
+
+static guint menubar_signals[LAST_SIGNAL] = { 0 };
+
+typedef struct
+{
+    GreeterMenuBar* menubar;
+    ChildInfo* child_info;
+    const gchar* child_name;
+} UnknownOptionCallbackData;
+
+static void greeter_menu_bar_destroy                (GtkWidget* widget);
+static void greeter_menu_bar_size_allocate          (GtkWidget* widget,
+                                                     GtkAllocation* allocation);
+static void greeter_menu_bar_remove                 (GtkContainer* container,
+                                                     GtkWidget* widget);
+
+static void greeter_menu_bar_add_item               (GreeterMenuBar* menubar,
+                                                     ChildInfo* child,
+                                                     GHashTable* options);
+static void greeter_menu_bar_item_visibility_changed(GreeterMenuBar* menubar,
+                                                     ChildInfo* child_info);
+static void greeter_menu_bar_unknown_option_cb      (const gchar* option,
+                                                     const gchar* value,
+                                                     UnknownOptionCallbackData* data);
+static void item_set_text                           (ChildInfo* child,
+                                                     const gchar* value);
+static void item_set_image                          (ChildInfo* child,
+                                                     const gchar* value);
+static void item_set_tooltip                        (ChildInfo* child,
+                                                     const gchar* value);
+static gboolean item_enter_notify_cb                (GtkWidget* widget,
+                                                     GdkEvent* event,
+                                                     gpointer enter);
+#ifdef HAVE_LIBINDICATOR
+static void greeter_menu_bar_add_indicator_entry    (GreeterMenuBar* menubar,
+                                                     IndicatorInfo* indicator_info,
+                                                     IndicatorObjectEntry* entry);
+static void greeter_menu_bar_remove_indicator_entry (GreeterMenuBar* menubar,
+                                                     IndicatorInfo* indicator_info,
+                                                     IndicatorObjectEntry* entry);
+static gboolean indicator_item_scrolled_cb          (GtkWidget* menuitem,
+                                                     GdkEventScroll* event,
+                                                     ChildInfo* child_info);
+static void indicator_item_activated_cb             (GtkWidget* menuitem,
+                                                     ChildInfo* child_info);
+static void indicator_entry_visibility_cb           (GtkWidget* widget,
+                                                     ChildInfo* child_info);
+static void indicator_entry_sensitivity_cb          (GObject* obj,
+                                                     GParamSpec* pspec,
+                                                     ChildInfo* child_info);
+static void indicator_entry_added_cb                (IndicatorObject* io,
+                                                     IndicatorObjectEntry* entry,
+                                                     IndicatorInfo* indicator_info);
+static void indicator_entry_removed_cb              (IndicatorObject* io,
+                                                     IndicatorObjectEntry* entry,
+                                                     IndicatorInfo* indicator_info);
+static void indicator_entry_menu_show_cb            (IndicatorObject* io,
+                                                     IndicatorObjectEntry* entry,
+                                                     guint32 timestamp,
+                                                     IndicatorInfo* indicator_info);
+#endif
+
+static void child_info_free                         (ChildInfo* info);
+#ifdef HAVE_LIBINDICATOR
+static void indicator_info_free                     (IndicatorInfo* info);
+#endif
+
+static gint sort_minimal_size                       (gconstpointer a,
+                                                     gconstpointer b,
+                                                     GtkRequestedSize* sizes);
+static gchar* get_indicators_string_token           (const gchar* s,
+                                                     const gchar* delimiters,
+                                                     const gchar** end);
+static gboolean item_is_visible                     (GtkMenuItem* menuitem,
+                                                     ChildInfo* child_info,
+                                                     gpointer user_data);
+static void remove_widget_from_container            (GtkWidget* widget,
+                                                     GtkContainer* container);
+
+G_DEFINE_TYPE_WITH_PRIVATE(GreeterMenuBar, greeter_menu_bar, GTK_TYPE_MENU_BAR);
+
+/* Implementation */
+
+gboolean
+greeter_set_image_from_string(GtkImage* image,
+                              const gchar* value,
+                              GtkIconSize size)
+{
+    if(!value)
+        return FALSE;
+
+    if(value[0] == '#')
+    {
+        GtkIconTheme* theme = gtk_icon_theme_get_default();
+        const gchar* icon = &value[1];
+        gchar* fallback = NULL;
+
+        if(!gtk_icon_theme_has_icon(theme, icon))
+        {
+            if(g_str_has_suffix(icon, "-symbolic"))
+            {
+                gchar *fallback = g_strndup(icon, strlen(icon) - strlen("-symbolic"));
+                icon = gtk_icon_theme_has_icon(theme, fallback) ? fallback : NULL;
+            }
+            else
+                icon = NULL;
+        }
+
+        if(!icon)
+            g_debug("Current theme does not contain '%s' icon", &value[1]);
+
+        gtk_image_set_from_icon_name(image, icon ? icon : "error", size);
+        g_free(fallback);
+        return icon != NULL;
+    }
+
+    GError* error = NULL;
+    gint width = 16, height = 16;
+    gtk_icon_size_lookup(size, &width, &height);
+
+    GdkPixbuf* pixbuf = gdk_pixbuf_new_from_file_at_scale(value, width, height, TRUE, &error);
+    if(pixbuf)
+    {
+        gtk_image_set_from_pixbuf(image, pixbuf);
+        g_object_unref(pixbuf);
+    }
+    else
+    {
+        gtk_image_set_from_icon_name(image, "error", size);
+        g_warning("Failed to load image: %s", error->message);
+        g_clear_error(&error);
+    }
+
+    return pixbuf != NULL;
+}
+
+void
+greeter_set_env(const gchar* key,
+                const gchar* value)
+{
+    g_setenv(key, value, TRUE);
+
+    GDBusProxy* proxy = g_dbus_proxy_new_for_bus_sync(G_BUS_TYPE_SESSION,
+                                                      G_DBUS_PROXY_FLAGS_NONE,
+                                                      NULL,
+                                                      "org.freedesktop.DBus",
+                                                      "/org/freedesktop/DBus",
+                                                      "org.freedesktop.DBus",
+                                                      NULL, NULL);
+
+    GVariantBuilder* builder = g_variant_builder_new(G_VARIANT_TYPE_ARRAY);
+    g_variant_builder_add(builder, "{ss}", key, value);
+    GVariant* result = g_dbus_proxy_call_sync(proxy, "UpdateActivationEnvironment", g_variant_new("(a{ss})", builder),
+                                              G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL);
+    g_variant_unref(result);
+    g_variant_builder_unref(builder);
+    g_object_unref(proxy);
+}
+
+gboolean
+greeter_str_to_bool(const gchar* value)
+{
+    return g_strcmp0(value, "true") == 0 || g_strcmp0(value, "1") == 0;
+}
 
 static void
 greeter_menu_bar_class_init(GreeterMenuBarClass* klass)
 {
+    GObjectClass* object_class = G_OBJECT_CLASS(klass);
 	GtkWidgetClass* widget_class = GTK_WIDGET_CLASS(klass);
+    GtkContainerClass* container_class = GTK_CONTAINER_CLASS(klass);
+
+    widget_class->destroy = greeter_menu_bar_destroy;
 	widget_class->size_allocate = greeter_menu_bar_size_allocate;
+    container_class->remove = greeter_menu_bar_remove;
+
+    menubar_signals[ITEMS_VISIBILITY_CHANGED_SIGNAL] =
+        g_signal_new(GREETER_MENU_BAR_SIGNAL_ITEMS_VISIBILITY_CHANGED,
+                     G_TYPE_FROM_CLASS(object_class),
+                     G_SIGNAL_RUN_FIRST,
+                     0, /* class_offset */
+                     NULL /* accumulator */, NULL /* accu_data */,
+                     g_cclosure_marshal_VOID__BOOLEAN,
+                     G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
+
+    menubar_signals[UNKNOWN_ITEM_SIGNAL] =
+        g_signal_new(GREETER_MENU_BAR_SIGNAL_UNKNOWN_ITEM,
+                     G_TYPE_FROM_CLASS(object_class),
+                     G_SIGNAL_RUN_LAST,
+                     0, /* class_offset */
+                     NULL /* accumulator */, NULL /* accu_data */,
+                     greeter_marshal_OBJECT__STRING_POINTER,
+                     G_TYPE_OBJECT  /* new menuitem or NULL */, 2,
+                     G_TYPE_STRING  /* name */,
+                     G_TYPE_POINTER /* options */);
+
+    menubar_signals[UNKNOWN_ITEM_OPTION_SIGNAL] =
+        g_signal_new(GREETER_MENU_BAR_SIGNAL_UNKNOWN_ITEM_OPTION,
+                     G_TYPE_FROM_CLASS(object_class),
+                     G_SIGNAL_RUN_LAST,
+                     0, /* class_offset */
+                     NULL /* accumulator */, NULL /* accu_data */,
+                     greeter_marshal_BOOLEAN__OBJECT_STRING_STRING_STRING,
+                     G_TYPE_BOOLEAN /* success */, 4,
+                     G_TYPE_OBJECT  /* menuitem */,
+                     G_TYPE_STRING  /* item name */,
+                     G_TYPE_STRING  /* option name */,
+                     G_TYPE_STRING  /* option value */);
 }
 
 static void
-greeter_menu_bar_init(GreeterMenuBar* square)
+greeter_menu_bar_init(GreeterMenuBar* self)
 {
-
+    self->priv = G_TYPE_INSTANCE_GET_PRIVATE(self, GREETER_MENU_BAR_TYPE, GreeterMenuBarPrivate);
+
+    self->priv->children = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, (GDestroyNotify)child_info_free);
+    #ifdef HAVE_LIBINDICATOR
+    self->priv->indicators.list = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, (GDestroyNotify)indicator_info_free);
+    #endif
+
+    GHashTable* common_options = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL);
+    self->priv->common_options = common_options;
+    g_hash_table_add(common_options, (gpointer)ITEM_OPTION_NAME);
+    g_hash_table_add(common_options, (gpointer)ITEM_OPTION_LAYOUT);
+    g_hash_table_add(common_options, (gpointer)ITEM_OPTION_TEXT);
+    g_hash_table_add(common_options, (gpointer)ITEM_OPTION_IMAGE);
+    g_hash_table_add(common_options, (gpointer)ITEM_OPTION_FALLBACK_IMAGE);
+    g_hash_table_add(common_options, (gpointer)ITEM_OPTION_TOOLTIP);
+    g_hash_table_add(common_options, (gpointer)ITEM_OPTION_MARKUP);
+    g_hash_table_add(common_options, (gpointer)ITEM_OPTION_EXPAND);
+    g_hash_table_add(common_options, (gpointer)ITEM_OPTION_ALIGN);
 }
 
-GtkWidget* 
+GtkWidget*
 greeter_menu_bar_new()
 {
 	return GTK_WIDGET(g_object_new(greeter_menu_bar_get_type(), NULL));
 }
 
-static gint
-sort_minimal_size(gconstpointer a, gconstpointer b, GtkRequestedSize* sizes)
-{
-    gint a_size = sizes[GPOINTER_TO_INT(a)].natural_size;
-    gint b_size = sizes[GPOINTER_TO_INT(b)].natural_size;
-    return a_size == b_size ? 0 : a_size > b_size ? -1 : +1; 
-}
-
-static void
-greeter_menu_bar_size_allocate(GtkWidget* widget, GtkAllocation* allocation)
+void
+greeter_menu_bar_load_from_string(GreeterMenuBar* menubar,
+                                  const gchar* str)
+{
+    g_return_if_fail(GREETER_IS_MENU_BAR(menubar));
+
+    GSList* indicators = NULL;  /* list of <GHashTable*> options */
+
+    gtk_container_foreach(GTK_CONTAINER(menubar), (GtkCallback)remove_widget_from_container, menubar);
+
+    if(!str)
+        return;
+
+    while(*str)
+    {
+        gchar* name = get_indicators_string_token(str, ":;", &str);
+        if(!name)
+            continue;
+
+        GHashTable *options = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
+        indicators = g_slist_prepend(indicators, options);
+
+        g_hash_table_insert(options, g_strdup(ITEM_OPTION_NAME), name);
+
+        if(*str == ':')
+        {
+            do
+            {
+                gchar* option = get_indicators_string_token(++str, "=,;", &str);
+                gchar* value = (*str == '=') ? get_indicators_string_token(++str, ",;", &str) : NULL;
+                g_hash_table_insert(options, option, value);
+            } while(*str == ',');
+        }
+        if(*str)
+            ++str;
+    }
+
+    gint child_position = 0;
+    GSList* indicators_iter;
+
+    for(indicators_iter = indicators; indicators_iter; indicators_iter = g_slist_next(indicators_iter))
+    {
+        GHashTable* options = indicators_iter->data;
+        const gchar* name = g_hash_table_lookup(options, ITEM_OPTION_NAME);
+        if(!name || strlen(name) == 0)
+            continue;
+
+        if(name[0] == '~')
+        {
+            ChildInfo* child_info = g_new0(ChildInfo, 1);
+
+            /* Built-in indicators */
+            if(g_strcmp0(name, "~separator") == 0)
+            {
+                child_info->widget = gtk_separator_menu_item_new();
+                child_info->layout = ITEM_LAYOUT_NO_CHILDREN;
+                child_info->css_style = ITEM_STYLE_SEPARATOR;
+
+                GtkWidget* separator = gtk_separator_new(GTK_ORIENTATION_VERTICAL);
+                gtk_widget_show(separator);
+                gtk_container_add(GTK_CONTAINER(child_info->widget), separator);
+            }
+            else if(g_strcmp0(name, "~spacer") == 0)
+            {
+                child_info->widget = gtk_separator_menu_item_new();
+                child_info->layout = ITEM_LAYOUT_NO_CHILDREN;
+                child_info->css_style = ITEM_STYLE_SPACER;
+
+                gtk_menu_item_set_label(GTK_MENU_ITEM(child_info->widget), "");
+                gtk_widget_set_hexpand(child_info->widget, TRUE);
+            }
+            else if(name[1] == '~' || g_strcmp0(name, "~text") == 0)
+            {
+                child_info->widget = gtk_separator_menu_item_new();
+                child_info->layout = ITEM_LAYOUT_TEXT;
+                child_info->css_style = ITEM_STYLE_TEXT;
+
+                if(name[1] == '~' && !g_hash_table_lookup_extended(options, ITEM_OPTION_TEXT, NULL, NULL))
+                    g_hash_table_insert(options, g_strdup(ITEM_OPTION_TEXT), g_strdup(&name[2]));
+            }
+            else
+            {
+                g_signal_emit(menubar, menubar_signals[UNKNOWN_ITEM_SIGNAL], 0, name, options, &child_info->widget);
+                if(child_info->widget)
+                {
+                    child_info->layout = ITEM_LAYOUT_TEXT;
+                    child_info->css_style = ITEM_STYLE_INDICATOR;
+                }
+            }
+
+            if(child_info->widget)
+            {
+                child_info->position = child_position;
+                greeter_menu_bar_add_item(menubar, child_info, options);
+            }
+            else
+            {
+                child_info_free(child_info);
+                g_warning("[Panel] Unknown builtin item: '%s'", name);
+            }
+        }
+        #ifdef HAVE_LIBINDICATOR
+        else
+        {
+            GreeterMenuBarPrivate* priv = menubar->priv;
+            gchar* path = NULL;
+            IndicatorObject* io = NULL;
+
+            if(!priv->indicators.inited)
+            {
+                /* Set indicators to run with reduced functionality */
+                greeter_set_env("INDICATOR_GREETER_MODE", "1");
+                /* Don't allow virtual file systems? */
+                greeter_set_env("GIO_USE_VFS", "local");
+                greeter_set_env("GVFS_DISABLE_FUSE", "1");
+
+                priv->indicators.inited = TRUE;
+            }
+
+            if(g_path_is_absolute(name))
+            {   /* library with absolute path */
+                io = indicator_object_new_from_file(name);
+            }
+            else if(g_str_has_suffix(name, G_MODULE_SUFFIX))
+            {   /* library */
+                path = g_build_filename(INDICATOR_DIR, name, NULL);
+                io = indicator_object_new_from_file(path);
+            }
+            #ifdef HAVE_LIBINDICATOR_NG
+            else
+            {   /* service file */
+                if(strchr(name, '.'))
+                    path = g_strdup_printf("%s/%s", UNITY_INDICATOR_DIR, name);
+                else
+                    path = g_strdup_printf("%s/com.canonical.indicator.%s", UNITY_INDICATOR_DIR, name);
+                io = INDICATOR_OBJECT(indicator_ng_new_for_profile(path, "desktop_greeter", NULL));
+            }
+            #endif
+
+            if(io)
+            {
+                IndicatorInfo* indicator_info = g_new0(IndicatorInfo, 1);
+                indicator_info->menubar = menubar;
+                indicator_info->object = io;
+                indicator_info->position = child_position;
+                indicator_info->options = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)g_hash_table_unref);
+                indicator_info->entries = g_hash_table_new(g_direct_hash, g_direct_equal);
+
+                g_hash_table_insert(priv->indicators.list, io, indicator_info);
+
+                GHashTableIter options_iter;
+                const gchar* option;
+                const gchar* value;
+
+                /* Parsing options: option[/entry-name] = value */
+                g_hash_table_iter_init(&options_iter, options);
+                while(g_hash_table_iter_next(&options_iter, (gpointer*)&option, (gpointer*)&value))
+                {
+                    GHashTable* entry_options = NULL;
+
+                    const gchar* delim = strchr(option, '/');
+                    const gchar* entry_name = (delim && strlen(delim) > 1) ? &delim[1] : COMMON_OPTIONS_KEY;
+                    gchar* option_name = delim ? g_strndup(option, delim - option) : g_strdup(option);
+
+                    if(!g_hash_table_lookup_extended(indicator_info->options, entry_name, NULL, (gpointer*)&entry_options))
+                    {
+                        entry_options = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
+                        g_hash_table_insert(indicator_info->options, g_strdup(entry_name), entry_options);
+                    }
+
+                    g_hash_table_insert(entry_options, option_name, g_strdup(value));
+                }
+
+                /* Add common options to custom */
+                GHashTable* common_options = g_hash_table_lookup(indicator_info->options, COMMON_OPTIONS_KEY);
+                if(common_options && g_hash_table_size(indicator_info->options) > 1)
+                {
+                    GHashTableIter entries_iter;
+                    GHashTable* entry_options;
+                    const gchar* entry_name;
+
+                    g_hash_table_iter_init(&entries_iter, indicator_info->options);
+                    while(g_hash_table_iter_next(&entries_iter, (gpointer*)&entry_name, (gpointer*)&entry_options))
+                    {
+                        if(entry_options == common_options)
+                            continue;
+
+                        const gchar* name;
+                        const gchar* value;
+                        g_hash_table_iter_init(&options_iter, common_options);
+                        while(g_hash_table_iter_next(&options_iter,(gpointer*)&name, (gpointer*)&value))
+                        {
+                            if(!g_hash_table_lookup_extended(entry_options, name, NULL, NULL))
+                                g_hash_table_insert(entry_options, g_strdup(name), g_strdup(value));
+                        }
+                    }
+                }
+
+                g_signal_connect(G_OBJECT(io), INDICATOR_OBJECT_SIGNAL_ENTRY_ADDED, G_CALLBACK(indicator_entry_added_cb), indicator_info);
+                g_signal_connect(G_OBJECT(io), INDICATOR_OBJECT_SIGNAL_ENTRY_REMOVED, G_CALLBACK(indicator_entry_removed_cb), indicator_info);
+                g_signal_connect(G_OBJECT(io), INDICATOR_OBJECT_SIGNAL_MENU_SHOW, G_CALLBACK(indicator_entry_menu_show_cb), indicator_info);
+
+                GList* entries = indicator_object_get_entries(io);
+                GList* entries_iter;
+                for(entries_iter = entries; entries_iter; entries_iter = g_list_next(entries_iter))
+                    greeter_menu_bar_add_indicator_entry(menubar, indicator_info, entries_iter->data);
+                g_list_free(entries);
+            }
+            else
+            {
+                g_warning("[Panel] Indicator '%s': failed to load", name);
+            }
+
+            g_free(path);
+        }
+        #else
+        else
+            g_warning("[Panel] Invalid indicator: '%s' (this build do not have libindicator support)", name);
+        #endif
+        child_position++;
+    }
+    g_slist_free_full(indicators, (GDestroyNotify)g_hash_table_unref);
+}
+
+const gchar*
+greeter_menu_bar_get_item_text(GreeterMenuBar* menubar,
+                               GtkWidget* menuitem)
+{
+    g_return_if_fail(GREETER_IS_MENU_BAR(menubar));
+
+    ChildInfo* child_info = g_hash_table_lookup(menubar->priv->children, menuitem);
+
+    if(child_info && child_info->label_widget)
+        return gtk_label_get_label(GTK_LABEL(child_info->label_widget));
+    return NULL;
+}
+
+void
+greeter_menu_bar_set_item_text(GreeterMenuBar* menubar,
+                               GtkWidget* menuitem,
+                               const gchar* value)
+{
+    g_return_if_fail(GREETER_IS_MENU_BAR(menubar));
+
+    ChildInfo* child_info = g_hash_table_lookup(menubar->priv->children, menuitem);
+
+    if(child_info)
+        item_set_text(child_info, value);
+}
+
+void
+greeter_menu_bar_set_item_image(GreeterMenuBar* menubar,
+                                GtkWidget* menuitem,
+                                const gchar* value)
+{
+    g_return_if_fail(GREETER_IS_MENU_BAR(menubar));
+
+    ChildInfo* child_info = g_hash_table_lookup(menubar->priv->children, menuitem);
+
+    if(child_info)
+        item_set_image(child_info, value);
+}
+
+void
+greeter_menu_bar_set_item_tooltip(GreeterMenuBar* menubar,
+                                  GtkWidget* menuitem,
+                                  const gchar* value)
+{
+    g_return_if_fail(GREETER_IS_MENU_BAR(menubar));
+
+    ChildInfo* child_info = g_hash_table_lookup(menubar->priv->children, menuitem);
+
+    if(child_info)
+        item_set_tooltip(child_info, value);
+}
+
+gboolean
+greeter_menu_bar_item_have_text(GreeterMenuBar* menubar,
+                                GtkWidget* menuitem)
+{
+    g_return_if_fail(GREETER_IS_MENU_BAR(menubar));
+
+    ChildInfo* child_info = g_hash_table_lookup(menubar->priv->children, menuitem);
+
+    return child_info ? child_info->label_widget != NULL : FALSE;
+}
+
+gboolean
+greeter_menu_bar_item_have_image(GreeterMenuBar* menubar,
+                                 GtkWidget* menuitem)
+{
+    g_return_if_fail(GREETER_IS_MENU_BAR(menubar));
+
+    ChildInfo* child_info = g_hash_table_lookup(menubar->priv->children, menuitem);
+
+    return child_info ? child_info->image_widget != NULL : FALSE;
+}
+
+static void
+greeter_menu_bar_destroy(GtkWidget* widget)
+{
+    GTK_WIDGET_CLASS(greeter_menu_bar_parent_class)->destroy(widget);
+}
+
+static void
+greeter_menu_bar_size_allocate(GtkWidget* widget,
+                               GtkAllocation* allocation)
 {
     GList* item;
     GList* shell_children;
@@ -80,14 +715,14 @@
         gtk_style_context_get_padding(context, flags, &border);
         gtk_widget_style_get(widget, "shadow-type", &shadow_type, NULL);
 
-        remaining_space.x = (border_width + border.left);
-        remaining_space.y = (border_width + border.top);
+        remaining_space.x = border_width + border.left;
+        remaining_space.y = border_width + border.top;
         remaining_space.width = allocation->width -
                                 2 * border_width - border.left - border.right;
         remaining_space.height = allocation->height -
                                  2 * border_width - border.top - border.bottom;
 
-        if (shadow_type != GTK_SHADOW_NONE)
+        if(shadow_type != GTK_SHADOW_NONE)
         {
             gtk_style_context_get_border(context, flags, &border);
 
@@ -103,7 +738,7 @@
 
         for(item = shell_children; item; item = g_list_next(item))
         {
-            if (!gtk_widget_get_visible(item->data))
+            if(!gtk_widget_get_visible(item->data))
                 continue;
 
             request->data = item->data;
@@ -167,7 +802,7 @@
                 request->natural_size += dsize;
             }
         }
-        
+
         gint i;
         for(i = 0; i < visible_count; i++)
         {
@@ -176,7 +811,7 @@
 
             child_allocation.width = request->natural_size;
             remaining_space.width -= request->natural_size;
-            if (ltr)
+            if(ltr)
                 remaining_space.x += request->natural_size;
             else
                 child_allocation.x += remaining_space.width;
@@ -186,3 +821,564 @@
     }
     g_list_free(shell_children);
 }
+
+static void
+greeter_menu_bar_remove(GtkContainer* container,
+                        GtkWidget* widget)
+{
+    g_hash_table_remove(GREETER_MENU_BAR(container)->priv->children, widget);
+    GTK_CONTAINER_CLASS(greeter_menu_bar_parent_class)->remove(container, widget);
+}
+
+static void
+greeter_menu_bar_add_item(GreeterMenuBar* menubar,
+                          ChildInfo* child,
+                          GHashTable* options)
+{
+    GreeterMenuBarPrivate* priv = menubar->priv;
+
+    const gchar* name = g_hash_table_lookup(options, ITEM_OPTION_NAME);
+
+    g_debug("[Panel] New item added: %s", name);
+
+    /* Styling */
+    gtk_style_context_add_class(gtk_widget_get_style_context(child->widget), ITEM_STYLE_COMMON);
+    gtk_style_context_add_class(gtk_widget_get_style_context(child->widget), child->css_style);
+
+    /* Hover effect */
+    if(child->layout != ITEM_LAYOUT_NO_CHILDREN)
+    {
+        g_signal_connect(G_OBJECT(child->widget), "enter-notify-event",
+                         G_CALLBACK(item_enter_notify_cb), GINT_TO_POINTER(TRUE));
+        g_signal_connect(G_OBJECT(child->widget), "leave-notify-event",
+                         G_CALLBACK(item_enter_notify_cb), GINT_TO_POINTER(FALSE));
+    }
+    /* Options */
+    const gchar* value;
+
+    /* "markup" option. Value: any text. Without value: use "text" as markup */
+    if(g_hash_table_lookup_extended(options, ITEM_OPTION_MARKUP, NULL, (gpointer*)&value))
+    {
+        child->label_markup = TRUE;
+        if(value)
+            g_hash_table_insert(options, g_strdup(ITEM_OPTION_TEXT), g_strdup(value));
+    }
+
+    /* "layout" option. Values: image|text|image-text|text-mage */
+    if(child->layout != ITEM_LAYOUT_NO_CHILDREN)
+    {
+        value = g_hash_table_lookup(options, ITEM_OPTION_LAYOUT);
+        if(value)
+        {
+            struct
+            {
+                const gchar* name;
+                ItemLayout layout;
+            }
+            *layout_struct,
+            VALUES[] =
+            {
+                {"text",        ITEM_LAYOUT_TEXT},
+                {"image",       ITEM_LAYOUT_IMAGE},
+                {"text-image",  ITEM_LAYOUT_TEXT | ITEM_LAYOUT_IMAGE},
+                {"image-text",  ITEM_LAYOUT_TEXT | ITEM_LAYOUT_IMAGE | ITEM_LAYOUT_REVERSED},
+                {NULL,          ITEM_LAYOUT_INVALID}
+            };
+
+            for(layout_struct = VALUES; layout_struct->name; ++layout_struct)
+                if(g_strcmp0(layout_struct->name, value) == 0)
+                {
+                    child->layout = layout_struct->layout;
+                    break;
+                }
+            if(!layout_struct->name)
+                g_warning("[Panel] Invalid value for '%s' option: '%s'", ITEM_OPTION_LAYOUT, value);
+        }
+
+        /* Use "text" and "image" options to set up layout */
+        if(g_hash_table_contains(options, ITEM_OPTION_TEXT))
+            child->layout |= ITEM_LAYOUT_TEXT;
+        if(g_hash_table_contains(options, ITEM_OPTION_IMAGE))
+            child->layout |= ITEM_LAYOUT_IMAGE;
+
+        if(child->layout != ITEM_LAYOUT_INVALID)
+        {
+            GtkWidget* item_child = gtk_bin_get_child(GTK_BIN(child->widget));
+            if(item_child)
+                gtk_container_remove(GTK_CONTAINER(child->widget), item_child);
+
+            GtkBox* box = GTK_BOX(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 4));
+
+            if(child->layout & ITEM_LAYOUT_TEXT)
+            {
+                if(!child->label_widget || g_hash_table_lookup(options, ITEM_OPTION_TEXT))
+                {
+                    child->label_widget = gtk_label_new(NULL);
+                    gtk_widget_show(child->label_widget);
+                }
+                if(child->layout & ITEM_LAYOUT_REVERSED)
+                    gtk_box_pack_end(box, child->label_widget, FALSE, TRUE, 0);
+                else
+                    gtk_box_pack_start(box, child->label_widget, FALSE, TRUE, 0);
+            }
+
+            if(child->layout & ITEM_LAYOUT_IMAGE)
+            {
+                if(!child->image_widget || g_hash_table_lookup(options, ITEM_OPTION_IMAGE))
+                {
+                    child->image_widget = gtk_image_new();
+                    gtk_widget_show(child->image_widget);
+                }
+                gtk_box_pack_end(box, child->image_widget, FALSE, TRUE, 0);
+            }
+
+            gtk_widget_set_halign(GTK_WIDGET(box), GTK_ALIGN_CENTER);
+            gtk_widget_show(GTK_WIDGET(box));
+            gtk_container_add(GTK_CONTAINER(child->widget), GTK_WIDGET(box));
+        }
+    }
+
+    /* "text" option. Value: any text */
+    value = g_hash_table_lookup(options, ITEM_OPTION_TEXT);
+    if(value)
+    {
+        item_set_text(child, value);
+        child->protect_label = TRUE;
+    }
+
+    /* "fallback-image" option. Value: #icon-name or /path/to/file */
+    child->fallback_image = g_strdup(g_hash_table_lookup(options, ITEM_OPTION_FALLBACK_IMAGE));
+
+    /* "image" option. Value: #icon-name or /path/to/file */
+    value = g_hash_table_lookup(options, ITEM_OPTION_IMAGE);
+    if(value)
+    {
+        item_set_image(child, value);
+        child->protect_image = TRUE;
+    }
+    else
+        item_set_image(child, NULL);
+
+    /* "tooltip" option. Value: any text */
+    value = g_hash_table_lookup(options, ITEM_OPTION_TOOLTIP);
+    if(value)
+    {
+        item_set_tooltip(child, value);
+        child->protect_tooltip = TRUE;
+    }
+
+    /* "expand" option. Values: true|false. Short form value: true */
+    if(g_hash_table_lookup_extended(options, ITEM_OPTION_EXPAND, NULL, (gpointer*)&value))
+        gtk_widget_set_hexpand(child->widget, !value || greeter_str_to_bool(value));
+
+    /* "align" option. Values: start|center|end */
+    value = g_hash_table_lookup(options, ITEM_OPTION_ALIGN);
+    if(value)
+    {
+        GtkAlign align;
+        if(g_strcmp0(value, "start") == 0)
+            align = GTK_ALIGN_START;
+        else if(g_strcmp0(value, "center") == 0)
+            align = GTK_ALIGN_CENTER;
+        else if(g_strcmp0(value, "end") == 0)
+            align = GTK_ALIGN_END;
+        else
+        {
+            g_warning("[Panel] Invalid value for '%s' option: '%s'", ITEM_OPTION_ALIGN, value);
+            value = NULL;
+        }
+        if(value)
+            gtk_widget_set_halign(child->widget, align);
+    }
+
+    /* Custom options */
+    {
+        UnknownOptionCallbackData data =
+        {
+            .menubar = menubar,
+            .child_info = child,
+            .child_name = name,
+        };
+        g_hash_table_foreach(options, (GHFunc)greeter_menu_bar_unknown_option_cb, &data);
+    }
+
+    g_hash_table_insert(priv->children, child->widget, child);
+    gtk_widget_show(child->widget);
+
+    gint insert_pos = 0;
+    GList* items = gtk_container_get_children(GTK_CONTAINER(menubar));
+    GList* item;
+    for(item = items; item; item = g_list_next(item))
+    {
+        ChildInfo* item_info = g_hash_table_lookup(priv->children, item->data);
+        if(item_info && child->position < item_info->position)
+            break;
+        insert_pos++;
+    }
+    g_list_free(items);
+
+    gtk_menu_shell_insert(GTK_MENU_SHELL(menubar), child->widget, insert_pos);
+    greeter_menu_bar_item_visibility_changed(menubar, child);
+}
+
+static void
+greeter_menu_bar_item_visibility_changed(GreeterMenuBar* menubar,
+                                         ChildInfo* child_info)
+{
+    gboolean visible = FALSE;
+
+    #ifdef HAVE_LIBINDICATOR
+    if(child_info->indicator.entry)
+    {
+        /* Visibility controlled by IndicatorObject, so we watch for label and image visibility changes */
+        if(child_info->indicator.entry->label)
+            visible = visible || gtk_widget_get_visible(GTK_WIDGET(child_info->indicator.entry->label));
+        if(child_info->indicator.entry->image && !visible)
+            visible = visible || gtk_widget_get_visible(GTK_WIDGET(child_info->indicator.entry->image));
+        gtk_widget_set_visible(child_info->widget, visible);
+    }
+    else
+    #endif
+    {
+        /* Controlled by program, watch for menu item visibility changes */
+        visible = gtk_widget_get_visible(child_info->widget);
+    }
+
+    if(!visible)
+       visible = g_hash_table_find(menubar->priv->children, (GHRFunc)item_is_visible, NULL) != NULL;
+
+    if(visible != menubar->priv->items_visibility)
+    {
+        menubar->priv->items_visibility = visible;
+        g_signal_emit(menubar, menubar_signals[ITEMS_VISIBILITY_CHANGED_SIGNAL], 0, visible);
+    }
+}
+
+static void
+greeter_menu_bar_unknown_option_cb(const gchar* option,
+                                   const gchar* value,
+                                   UnknownOptionCallbackData* data)
+{
+    if(!g_hash_table_contains(data->menubar->priv->common_options, option))
+    {
+        gboolean result = FALSE;
+        g_signal_emit(data->menubar, menubar_signals[UNKNOWN_ITEM_OPTION_SIGNAL], 0,
+                      data->child_info->widget, data->child_name, option, value, &result);
+        if(!result)
+            g_warning("[Panel] Unknown option or invalid value for '%s' item: '%s' = '%s'", data->child_name, option, value);
+    }
+}
+
+static void
+item_set_text(ChildInfo* child,
+              const gchar* value)
+{
+    if(child->label_widget && !child->protect_label)
+    {
+        if(child->label_markup)
+            gtk_label_set_markup(GTK_LABEL(child->label_widget), value);
+        else
+            gtk_label_set_label(GTK_LABEL(child->label_widget), value);
+    }
+}
+
+static void
+item_set_image(ChildInfo* child,
+               const gchar* value)
+{
+    if(child->image_widget && !child->protect_image)
+    {
+        if(!value || !greeter_set_image_from_string(GTK_IMAGE(child->image_widget), value, GTK_ICON_SIZE_MENU))
+            if(child->fallback_image && g_strcmp0(value, child->fallback_image) != 0)
+                greeter_set_image_from_string(GTK_IMAGE(child->image_widget), child->fallback_image, GTK_ICON_SIZE_MENU);
+    }
+}
+
+static void
+item_set_tooltip(ChildInfo* child,
+                 const gchar* value)
+{
+    if(!child->protect_tooltip)
+        gtk_widget_set_tooltip_text(GTK_WIDGET(child->widget), value);
+}
+
+static gboolean
+item_enter_notify_cb(GtkWidget* widget,
+                     GdkEvent* event,
+                     gpointer enter)
+{
+    GtkStyleContext* context = gtk_widget_get_style_context(widget);
+    if(GPOINTER_TO_INT(enter))
+        gtk_style_context_add_class(context, ITEM_STYLE_HOVERED);
+    else
+        gtk_style_context_remove_class(context, ITEM_STYLE_HOVERED);
+    return FALSE;
+}
+
+#ifdef HAVE_LIBINDICATOR
+static void
+greeter_menu_bar_add_indicator_entry(GreeterMenuBar* menubar,
+                                     IndicatorInfo* indicator_info,
+                                     IndicatorObjectEntry* entry)
+{
+    ChildInfo* child_info = g_new0(ChildInfo, 1);
+
+    g_debug("[Panel] Adding new IndicatorObjectEntry: %s", entry->name_hint);
+
+    child_info->menubar = indicator_info->menubar;
+    child_info->widget = gtk_menu_item_new();
+    child_info->position = indicator_info->position;
+    child_info->css_style = ITEM_STYLE_INDICATOR;
+    child_info->layout = ITEM_LAYOUT_TEXT | ITEM_LAYOUT_IMAGE;
+    child_info->indicator.object = indicator_info->object;
+    child_info->indicator.entry = entry;
+
+    g_hash_table_insert(indicator_info->entries, entry, child_info);
+
+    gtk_widget_add_events(child_info->widget, GDK_SCROLL_MASK);
+    g_signal_connect(child_info->widget, "activate", G_CALLBACK(indicator_item_activated_cb), child_info);
+    g_signal_connect(child_info->widget, "scroll-event", G_CALLBACK(indicator_item_scrolled_cb), child_info);
+
+    if(entry->label)
+    {
+        g_object_connect(entry->label,
+                         "signal::show", G_CALLBACK(indicator_entry_visibility_cb), child_info,
+                         "signal::hide", G_CALLBACK(indicator_entry_visibility_cb), child_info,
+                         "signal::notify::sensitive", G_CALLBACK(indicator_entry_sensitivity_cb), child_info,
+                         NULL);
+        child_info->label_widget = GTK_WIDGET(entry->label);
+    }
+
+    if(entry->image)
+    {
+        g_object_connect(entry->image,
+                         "signal::show", G_CALLBACK(indicator_entry_visibility_cb), child_info,
+                         "signal::hide", G_CALLBACK(indicator_entry_visibility_cb), child_info,
+                         "signal::notify::sensitive", G_CALLBACK(indicator_entry_sensitivity_cb), child_info,
+                         NULL);
+        child_info->image_widget = GTK_WIDGET(entry->image);
+    }
+
+    if(entry->menu)
+        gtk_menu_item_set_submenu(GTK_MENU_ITEM(child_info->widget), GTK_WIDGET(entry->menu));
+
+    GHashTable* entry_options = g_hash_table_lookup(indicator_info->options, entry->name_hint);
+    if(!entry_options)
+        entry_options = g_hash_table_lookup(indicator_info->options, COMMON_OPTIONS_KEY);
+
+    greeter_menu_bar_add_item(indicator_info->menubar, child_info, entry_options);
+}
+
+static void
+greeter_menu_bar_remove_indicator_entry(GreeterMenuBar* menubar,
+                                        IndicatorInfo* indicator_info,
+                                        IndicatorObjectEntry* entry)
+{
+    ChildInfo* child_info = g_hash_table_lookup(indicator_info->entries, entry);
+
+    g_debug("[Panel] Removing IndicatorObjectEntry: %s", entry->name_hint);
+
+    if(child_info)
+    {
+        if(entry->label)
+            g_object_disconnect(entry->label,
+                                "any-signal", G_CALLBACK(indicator_entry_visibility_cb), child_info,
+                                "any-signal", G_CALLBACK(indicator_entry_sensitivity_cb), child_info,
+                                NULL);
+        if(entry->image)
+            g_object_disconnect(entry->image,
+                                "any-signal", G_CALLBACK(indicator_entry_visibility_cb), child_info,
+                                "any-signal", G_CALLBACK(indicator_entry_sensitivity_cb), child_info,
+                                NULL);
+
+        g_hash_table_remove(indicator_info->entries, entry);
+        gtk_widget_destroy(child_info->widget);
+    }
+}
+
+static gboolean
+indicator_item_scrolled_cb(GtkWidget* menuitem,
+                           GdkEventScroll* event,
+                           ChildInfo* child_info)
+{
+    g_return_val_if_fail(child_info != NULL, FALSE);
+    g_return_val_if_fail(child_info->indicator.object && child_info->indicator.entry, FALSE);
+
+    g_signal_emit_by_name(child_info->indicator.object, INDICATOR_OBJECT_SIGNAL_ENTRY_SCROLLED, child_info->indicator.entry, 1, event->direction);
+    return FALSE;
+}
+
+static void
+indicator_item_activated_cb(GtkWidget* menuitem,
+                            ChildInfo* child_info)
+{
+    g_return_val_if_fail(child_info != NULL, FALSE);
+    g_return_val_if_fail(child_info->indicator.object && child_info->indicator.entry, FALSE);
+
+    indicator_object_entry_activate(child_info->indicator.object, child_info->indicator.entry, gtk_get_current_event_time());
+}
+
+static void
+indicator_entry_visibility_cb(GtkWidget* widget,
+                              ChildInfo* child_info)
+{
+    g_return_val_if_fail(child_info != NULL, FALSE);
+
+    greeter_menu_bar_item_visibility_changed(child_info->menubar, child_info);
+}
+
+static void
+indicator_entry_sensitivity_cb(GObject* obj,
+                               GParamSpec* pspec,
+                               ChildInfo* child_info)
+{
+    g_return_val_if_fail(child_info != NULL, FALSE);
+
+    gboolean sensitive = FALSE;
+    if(child_info->indicator.entry->label)
+        sensitive = sensitive || gtk_widget_get_sensitive(GTK_WIDGET(child_info->indicator.entry->label));
+    if(child_info->indicator.entry->image && !sensitive)
+        sensitive = sensitive || gtk_widget_get_sensitive(GTK_WIDGET(child_info->indicator.entry->image));
+    gtk_widget_set_sensitive(child_info->widget, sensitive);
+}
+
+static void
+indicator_entry_added_cb(IndicatorObject* io,
+                         IndicatorObjectEntry* entry,
+                         IndicatorInfo* indicator_info)
+{
+    greeter_menu_bar_add_indicator_entry(indicator_info->menubar, indicator_info, entry);
+}
+
+static void
+indicator_entry_removed_cb(IndicatorObject* io,
+                           IndicatorObjectEntry* entry,
+                           IndicatorInfo* indicator_info)
+{
+    greeter_menu_bar_remove_indicator_entry(indicator_info->menubar, indicator_info, entry);
+}
+
+static void
+indicator_entry_menu_show_cb(IndicatorObject* io,
+                             IndicatorObjectEntry* entry,
+                             guint32 timestamp,
+                             IndicatorInfo* indicator_info)
+{
+    if(!entry)
+    {
+        /* Close any open menus instead of opening one */
+        GList *items = indicator_object_get_entries(io);
+        GList *item;
+        for(item = items; item; item = g_list_next(item))
+        {
+            entry = item->data;
+            gtk_menu_popdown(entry->menu);
+        }
+        g_list_free(items);
+
+        /* And tell the menuitem to exit activation mode too */
+        gtk_menu_shell_cancel(GTK_MENU_SHELL(indicator_info->menubar));
+    }
+}
+
+#endif
+
+static void
+child_info_free(ChildInfo* info)
+{
+    g_free(info->fallback_image);
+    info->fallback_image = NULL;
+}
+
+#ifdef HAVE_LIBINDICATOR
+static void
+indicator_info_free(IndicatorInfo* info)
+{
+    g_hash_table_unref(info->options);
+    g_hash_table_unref(info->entries);
+    info->options = NULL;
+    info->entries = NULL;
+}
+#endif
+
+static gint
+sort_minimal_size(gconstpointer a,
+                  gconstpointer b,
+                  GtkRequestedSize* sizes)
+{
+    gint a_size = sizes[GPOINTER_TO_INT(a)].natural_size;
+    gint b_size = sizes[GPOINTER_TO_INT(b)].natural_size;
+    return a_size == b_size ? 0 : a_size > b_size ? -1 : +1;
+}
+
+static gchar*
+get_indicators_string_token(const gchar* s,
+                            const gchar* delimiters,
+                            const gchar** end)
+{
+    GString *token = g_string_new(NULL);
+    gboolean quoted = FALSE;
+
+    /* strip at left */
+    while(*s && g_ascii_isspace(*s))
+        ++s;
+
+    const gchar *p = s;
+
+    for(p = s; *p; ++p)
+    {
+        if(*p == '"')
+        {
+            if(p > s && *(p-1) == '\\')
+            {
+                g_string_append_len(token, s, p - s - 1);
+                g_string_append_c(token, '"');
+            }
+            else
+            {
+                g_string_append_len(token, s, p - s);
+                quoted = !quoted;
+            }
+            s = p + 1;
+        }
+        else if(!quoted && strchr(delimiters, *p))
+            break;
+    }
+
+    *end = p;
+
+    if(quoted)
+    {
+        g_string_free(token, TRUE);
+        return NULL;
+    }
+
+    /* strip at right */
+    const gchar *q = p - 1;
+    while(s < q && g_ascii_isspace(*q))
+        --q;
+    if(q != p)
+        p = q + 1;
+
+    g_string_append_len(token, s, p - s);
+    gchar *result = token->str;
+    g_string_free(token, FALSE);
+
+    return result;
+}
+
+static gboolean
+item_is_visible(GtkMenuItem* menuitem,
+                ChildInfo* child_info,
+                gpointer user_data)
+{
+    if(child_info->layout != ITEM_LAYOUT_NO_CHILDREN)
+        return gtk_widget_get_visible(child_info->widget);
+    return FALSE;
+}
+
+static void
+remove_widget_from_container(GtkWidget* widget, GtkContainer* container)
+{
+    gtk_container_remove(container, widget);
+}

=== modified file 'src/greetermenubar.h'
--- src/greetermenubar.h	2014-08-10 12:48:13 +0000
+++ src/greetermenubar.h	2014-10-26 18:59:04 +0000
@@ -8,25 +8,54 @@
 
 #define GREETER_MENU_BAR_TYPE            (greeter_menu_bar_get_type())
 #define GREETER_MENU_BAR(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj), GREETER_MENU_BAR_TYPE, GreeterMenuBar))
-#define GREETER_MENU_BAR_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass), GREETER_MENU_BAR_TYPE, GreeterMenuBarClass))
+#define GREETER_MENU_BAR_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass),  GREETER_MENU_BAR_TYPE, GreeterMenuBarClass))
 #define GREETER_IS_MENU_BAR(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), GREETER_MENU_BAR_TYPE))
-#define GREETER_IS_MENU_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GREETER_MENU_BAR_TYPE))
+#define GREETER_IS_MENU_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),  GREETER_MENU_BAR_TYPE))
+
+#define GREETER_MENU_BAR_SIGNAL_ITEMS_VISIBILITY_CHANGED    "items-visibility-changed"
+#define GREETER_MENU_BAR_SIGNAL_UNKNOWN_ITEM                "unknown-item"
+#define GREETER_MENU_BAR_SIGNAL_UNKNOWN_ITEM_OPTION         "unknown-item-option"
 
 typedef struct _GreeterMenuBar       GreeterMenuBar;
 typedef struct _GreeterMenuBarClass  GreeterMenuBarClass;
 
-struct _GreeterMenuBar
-{
-	GtkMenuBar parent_instance;
-};
-
-struct _GreeterMenuBarClass
-{
-	GtkMenuBarClass parent_class;
-};
-
-GType greeter_menu_bar_get_type(void) G_GNUC_CONST;
-GtkWidget *greeter_menu_bar_new(void);
+typedef GtkWidget* (*GMBUnknownItemCallback)        (const gchar* name,
+                                                     gchar*** options,
+                                                     gpointer user_data);
+
+typedef gboolean (*GMBUnknownOptionCallback)        (GtkWidget* menuitem,
+                                                     const gchar* name,
+                                                     const gchar* option,
+                                                     const gchar* value,
+                                                     gpointer user_data);
+
+GType greeter_menu_bar_get_type                     (void) G_GNUC_CONST;
+GtkWidget *greeter_menu_bar_new                     (void);
+
+void greeter_menu_bar_load_from_string              (GreeterMenuBar* menubar,
+                                                     const gchar* str);
+const gchar* greeter_menu_bar_get_item_text         (GreeterMenuBar* menubar,
+                                                     GtkWidget* menuitem);
+void greeter_menu_bar_set_item_text                 (GreeterMenuBar* menubar,
+                                                     GtkWidget* menuitem,
+                                                     const gchar* value);
+void greeter_menu_bar_set_item_image                (GreeterMenuBar* menubar,
+                                                     GtkWidget* menuitem,
+                                                     const gchar* value);
+void greeter_menu_bar_set_item_tooltip              (GreeterMenuBar* menubar,
+                                                     GtkWidget* menuitem,
+                                                     const gchar* value);
+gboolean greeter_menu_bar_item_have_text            (GreeterMenuBar* menubar,
+                                                     GtkWidget* menuitem);
+gboolean greeter_menu_bar_item_have_image           (GreeterMenuBar* menubar,
+                                                     GtkWidget* menuitem);
+
+gboolean greeter_set_image_from_string              (GtkImage* image,
+                                                     const gchar* value,
+                                                     GtkIconSize size);
+void greeter_set_env                                (const gchar* key,
+                                                     const gchar* value);
+gboolean greeter_str_to_bool                        (const gchar* value);
 
 G_END_DECLS
 

=== modified file 'src/lightdm-gtk-greeter-fallback.css'
--- src/lightdm-gtk-greeter-fallback.css	2014-05-22 19:52:34 +0000
+++ src/lightdm-gtk-greeter-fallback.css	2014-10-26 18:59:04 +0000
@@ -1,12 +1,17 @@
-#layout_menuitem>GtkLabel
+#layout_menuitem>GtkContainer>GtkLabel
 {
-  border: 1px solid alpha(@menu_fg_color, 0.8);
-  border-radius: 0.5em;
-  padding: 0px 0.25em;
-  background: alpha(@menu_fg_color, 0.2);
+    border: 1px solid alpha(@menu_fg_color, 0.8);
+    border-radius: 0.5em;
+    padding: 0px 0.25em;
+    background: alpha(@menu_fg_color, 0.2);
 }
 
 #layout_menuitem
 {
-  padding: 1px;
+    padding: 1px;
+}
+
+#clock_menuitem
+{
+    font-weight: bold;
 }

=== modified file 'src/lightdm-gtk-greeter.c'
--- src/lightdm-gtk-greeter.c	2014-09-01 07:58:56 +0000
+++ src/lightdm-gtk-greeter.c	2014-10-26 18:59:04 +0000
@@ -29,13 +29,6 @@
 #include <gtk/gtkx.h>
 #include <glib/gslist.h>
 
-#ifdef HAVE_LIBINDICATOR
-#include <libindicator/indicator-object.h>
-#ifdef HAVE_LIBINDICATOR_NG
-#include <libindicator/indicator-ng.h>
-#endif
-#endif
-
 #ifdef HAVE_LIBIDO
 /* Some indicators need ido library */
 #include <libido/libido.h>
@@ -62,7 +55,7 @@
 static void save_state_file (void);
 
 /* Screen window */
-static GtkOverlay *screen_overlay;
+static GtkOverlay   *screen_overlay;
 
 /* Login window */
 static GtkWidget    *login_window;
@@ -74,12 +67,12 @@
 static GtkButton    *cancel_button, *login_button;
 
 /* Panel */
-static GtkWidget    *panel_window, *menubar;
+static GtkWidget    *panel_window;
+static GreeterMenuBar *menubar;
 static GtkWidget    *power_menuitem, *session_menuitem, *language_menuitem, *a11y_menuitem,
                     *layout_menuitem, *clock_menuitem, *host_menuitem;
 static GtkWidget    *suspend_menuitem, *hibernate_menuitem, *restart_menuitem, *shutdown_menuitem;
 static GtkWidget    *contrast_menuitem, *font_menuitem, *keyboard_menuitem, *reader_menuitem;
-static GtkWidget    *clock_label, *session_badge;
 static GtkMenu      *session_menu, *language_menu, *layout_menu;
 
 /* Power window */
@@ -88,13 +81,23 @@
 static GtkLabel     *power_title, *power_text;
 static GtkImage     *power_icon;
 
-static const gchar *POWER_WINDOW_DATA_LOOP = "power-window-loop";           /* <GMainLoop*> */
-static const gchar *POWER_WINDOW_DATA_RESPONSE = "power-window-response";   /* <GtkResponseType> */
+static const gchar *POWER_OPTION_HIDE_DISABLED  = "hide-disabled";
+static const gchar *POWER_DATA_HIDE_DISABLED    = "power-hide-disabled";    /* <gboolean> hide disabled items, attached to power_menuitem */
+static const gchar *POWER_WINDOW_DATA_LOOP      = "power-window-loop";      /* <GMainLoop*> */
+static const gchar *POWER_WINDOW_DATA_RESPONSE  = "power-window-response";  /* <GtkResponseType> */
 
 static gboolean show_power_prompt (const gchar *action, const gchar* icon, const gchar* title, const gchar* message);
 void power_button_clicked_cb (GtkButton *button, gpointer user_data);
 gboolean power_window_key_press_event_cb (GtkWidget *widget, GdkEventKey *event, gpointer user_data);
 
+static gboolean sigterm_cb (gpointer user_data);
+
+/* List of processes to kill at exit */
+GSList *spawned_pids;  /* list of <GPid> */
+
+gboolean spawn_and_remember (gchar **argv, GError **perror);
+static void close_pid_cb (gpointer pid_as_pointer);
+
 /* Handling window position */
 typedef struct
 {
@@ -110,7 +113,7 @@
 typedef struct
 {
     DimensionPosition x, y;
-    /* Flag to use width and height fileds */
+    /* Flag to use width and height fields */
     gboolean use_size;
     DimensionPosition width, height;
 } WindowPosition;
@@ -131,8 +134,9 @@
 static gboolean key_file_get_boolean_extended (GKeyFile *key_file, const gchar *group_name, const gchar *key, gboolean default_value);
 
 /* Clock */
+static const gchar *CLOCK_OPTION_CLOCK_FORMAT = "clock-format";
 static gchar *clock_format;
-static gboolean clock_timeout_thread (void);
+static gboolean clock_timeout_cb (gpointer user_data);
 
 /* Message label */
 static gboolean message_label_is_empty (void);
@@ -167,18 +171,51 @@
 static void a11y_menuitem_toggled_cb (GtkCheckMenuItem *item, const gchar* name);
 
 /* Session */
+static const gchar *SESSION_DATA_KEY = "session-key";       /* <gchar*> session name, attached to submenu items */
 static gchar *current_session;
 static gboolean is_valid_session (GList* items, const gchar* session);
 static gchar* get_session (void);
 static void set_session (const gchar *session);
 void session_selected_cb (GtkMenuItem *menuitem, gpointer user_data);
 
-/* Sesion language */
+/* Session language */
+static const gchar *LANGUAGE_DATA_CODE = "language-code";   /* <gchar*> e.g. "de_DE.UTF-8", attached to submenu items */
 static gchar *current_language;
 static gchar* get_language (void);
 static void set_language (const gchar *language);
 void language_selected_cb (GtkMenuItem *menuitem, gpointer user_data);
 
+/* Layout indicator */
+static const gchar *LAYOUT_DATA_LABEL = "layout-label";     /* <gchar*> e.g. "English (US)" */
+static void layout_selected_cb (GtkCheckMenuItem *menuitem, gpointer user_data);
+static void update_layouts_menu (void);
+static void update_layouts_menu_state (void);
+#ifdef HAVE_LIBXKLAVIER
+static XklEngine *xkl_engine;
+static const gchar *LAYOUT_DATA_GROUP = "layout-group";     /* <gchar*> */
+static void xkl_state_changed_cb (XklEngine *engine, XklEngineStateChange change, gint group, gboolean restore, gpointer user_data);
+static void xkl_config_changed_cb (XklEngine *engine, gpointer user_data);
+static GdkFilterReturn xkl_xevent_filter (GdkXEvent *xev, GdkEvent *event, gpointer  data);
+#else
+static const gchar *LAYOUT_DATA_NAME = "layout-name";       /* <gchar*> */
+#endif
+
+/* a11y indicator */
+static gchar *default_font_name,
+             *default_theme_name,
+             *default_icon_theme_name;
+void a11y_font_cb (GtkCheckMenuItem *item, gpointer user_data);
+void a11y_contrast_cb (GtkCheckMenuItem *item, gpointer user_data);
+void a11y_keyboard_cb (GtkCheckMenuItem *item, gpointer user_data);
+void a11y_reader_cb (GtkCheckMenuItem *item, gpointer user_data);
+
+/* Power indciator */
+void power_menuitem_activate_cb (GtkWidget *menuitem, gpointer userdata);
+void suspend_cb (GtkWidget *widget, LightDMGreeter *greeter);
+void hibernate_cb (GtkWidget *widget, LightDMGreeter *greeter);
+void restart_cb (GtkWidget *widget, LightDMGreeter *greeter);
+void shutdown_cb (GtkWidget *widget, LightDMGreeter *greeter);
+
 /* Screensaver values */
 static int timeout, interval, prefer_blanking, allow_exposures;
 
@@ -209,81 +246,63 @@
 
 /* Panel and indicators */
 
-typedef enum
-{
-    PANEL_ITEM_INDICATOR,
-    PANEL_ITEM_SPACER,
-    PANEL_ITEM_SEPARATOR,
-    PANEL_ITEM_TEXT
-} GreeterPanelItemType;
-
-static const gchar *PANEL_ITEM_STYLE = "panel-item";
-static const gchar *PANEL_ITEM_STYLE_HOVERED = "panel-item-hovered";
-static const gchar *PANEL_ITEM_STYLES[] =
-{
-    "panel-item-indicator",
-    "panel-item-spacer",
-    "panel-item-separator",
-    "panel-item-text"
-};
-
-static const gchar *PANEL_ITEM_DATA_INDEX = "panel-item-data-index";
-
-#ifdef HAVE_LIBINDICATOR
-static const gchar *INDICATOR_ITEM_DATA_OBJECT = "indicator-item-data-object";  /* <IndicatorObject*> */
-static const gchar *INDICATOR_ITEM_DATA_ENTRY  = "indicator-item-data-entry";   /* <IndicatorObjectEntry*> */
-static const gchar *INDICATOR_ITEM_DATA_BOX    = "indicator-item-data-box";     /* <GtkBox*> */
-static const gchar *INDICATOR_DATA_MENUITEMS   = "indicator-data-menuitems";    /* <GHashTable*> */
-#endif
-
-static const gchar *LANGUAGE_DATA_CODE = "language-code";   /* <gchar*> e.g. "de_DE.UTF-8" */
-static const gchar *SESSION_DATA_KEY = "session-key";       /* <gchar*> session name */
-
-/* Layout indicator */
-#ifdef HAVE_LIBXKLAVIER
-static XklEngine *xkl_engine;
-static const gchar *LAYOUT_DATA_GROUP = "layout-group";     /* <gchar*> */
-#else
-static const gchar *LAYOUT_DATA_NAME = "layout-name";       /* <gchar*> */
-#endif
-static const gchar *LAYOUT_DATA_LABEL = "layout-label";     /* <gchar*> e.g. "English (US)" */
-
-static gboolean panel_item_enter_notify_cb (GtkWidget *widget, GdkEvent *event, gpointer enter);
-static void panel_add_item (GtkWidget *widget, gint index, GreeterPanelItemType item_type);
-static gboolean menu_item_accel_closure_cb (GtkAccelGroup *accel_group, GObject *acceleratable, guint keyval, GdkModifierType modifier, gpointer data);
-/* Maybe unnecessary (in future) trick to enable accelerators for hidden/detached menu items */
-static void reassign_menu_item_accel (GtkWidget *item);
-
-#ifdef START_INDICATOR_SERVICES
-static void init_indicators (GKeyFile* config, GPid* indicator_pid, GPid* spi_pid);
-#else
-static void init_indicators (GKeyFile* config);
-#endif
-
-static void layout_selected_cb (GtkCheckMenuItem *menuitem, gpointer user_data);
-static void update_layouts_menu (void);
-static void update_layouts_menu_state (void);
-#ifdef HAVE_LIBXKLAVIER
-static void xkl_state_changed_cb (XklEngine *engine, XklEngineStateChange change, gint group, gboolean restore, gpointer user_data);
-static void xkl_config_changed_cb (XklEngine *engine, gpointer user_data);
-static GdkFilterReturn xkl_xevent_filter (GdkXEvent *xev, GdkEvent *event, gpointer  data);
-#endif
-
-/* a11y indicator */
-static gchar *default_font_name,
-             *default_theme_name,
-             *default_icon_theme_name;
-void a11y_font_cb (GtkCheckMenuItem *item);
-void a11y_contrast_cb (GtkCheckMenuItem *item);
-void a11y_keyboard_cb (GtkCheckMenuItem *item, gpointer user_data);
-void a11y_reader_cb (GtkCheckMenuItem *item, gpointer user_data);
-
-/* Power indciator */
-static void power_menu_cb (GtkWidget *menuitem, gpointer userdata);
-void suspend_cb (GtkWidget *widget, LightDMGreeter *greeter);
-void hibernate_cb (GtkWidget *widget, LightDMGreeter *greeter);
-void restart_cb (GtkWidget *widget, LightDMGreeter *greeter);
-void shutdown_cb (GtkWidget *widget, LightDMGreeter *greeter);
+typedef struct
+{
+    const gchar *name;
+    GtkWidget *menuitem;
+    gboolean used;
+    const gchar *options[10];
+} BuiltinPanelItemInfo;
+
+static GHashTable *panel_builtin_items;
+
+static void panel_init (GKeyFile *config);
+static void panel_items_visibility_cb (GreeterMenuBar *menubar, gboolean visible, gpointer user_data);
+static void panel_reassing_item_accel_cb (GtkMenuItem *menuitem);
+static void panel_reassing_item_accel_table_cb (const gchar *name, const BuiltinPanelItemInfo *info);
+static gboolean panel_item_accel_closure_cb (GtkAccelGroup *accel_group, GObject *acceleratable, guint keyval,
+                                             GdkModifierType modifier, GtkMenuItem *menuitem);
+static GtkWidget *panel_unknown_item_cb (GreeterMenuBar *menubar, const gchar *name, GHashTable *options, gpointer user_data);
+static gboolean panel_unknown_item_option_cb (GreeterMenuBar *menubar, GtkWidget *menuitem, const gchar *name,
+                                              const gchar *option, const gchar *value, gpointer user_data);
+
+
+static gboolean
+sigterm_cb (gpointer user_data)
+{
+    g_debug ("SIGTERM signal");
+
+    g_slist_free_full (spawned_pids, close_pid_cb);
+    spawned_pids = NULL;
+
+    gtk_main_quit ();
+
+    if (screen_overlay)
+        gtk_widget_destroy (GTK_WIDGET (screen_overlay));
+    screen_overlay = NULL;
+
+    if (greeter_background)
+        greeter_background_disconnect (greeter_background);
+
+    return G_SOURCE_REMOVE;
+}
+
+gboolean
+spawn_and_remember (gchar **argv, GError **perror)
+{
+    GPid pid = 0;
+    gboolean result = g_spawn_async (NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, &pid, perror);
+    if (result)
+        spawned_pids = g_slist_prepend (spawned_pids, GINT_TO_POINTER (pid));
+    return result;
+}
+
+static void close_pid_cb (gpointer pid_as_pointer)
+{
+    GPid pid = GPOINTER_TO_INT (pid_as_pointer);
+    kill (pid, SIGTERM);
+    waitpid (pid, NULL, 0);
+}
 
 /* State file */
 
@@ -554,22 +573,22 @@
 /* Clock */
 
 static gboolean
-clock_timeout_thread (void)
+clock_timeout_cb (gpointer user_data)
 {
-    time_t rawtime;
-    struct tm * timeinfo;
-    gchar time_str[50];
-    gchar *markup;
-
-    time (&rawtime);
-    timeinfo = localtime (&rawtime);
-
-    strftime (time_str, 50, clock_format, timeinfo);
-    markup = g_markup_printf_escaped ("<b>%s</b>", time_str);
-    if (g_strcmp0 (markup, gtk_label_get_label (GTK_LABEL (clock_label))) != 0)
-        gtk_label_set_markup (GTK_LABEL (clock_label), markup);
-    g_free (markup);
-
+    GDateTime *now = g_date_time_new_now_local ();
+    gchar *label = g_date_time_format (now, clock_format);
+    g_date_time_unref (now);
+    if (!label)
+    {
+        g_warning ("[Clock] Failed to format time: %s", clock_format);
+        label = g_strdup_printf (_("[error] %s"), clock_format);
+        greeter_menu_bar_set_item_text (menubar, clock_menuitem, label);
+        g_free (label);
+        return FALSE;
+    }
+    if (g_strcmp0 (label, greeter_menu_bar_get_item_text (menubar, clock_menuitem)) != 0)
+        greeter_menu_bar_set_item_text (menubar, clock_menuitem, label);
+    g_free (label);
     return TRUE;
 }
 
@@ -854,23 +873,34 @@
                 if (g_strcmp0 (session, g_object_get_data (G_OBJECT (menu_iter->data), SESSION_DATA_KEY)) == 0)
                 {
                     /* Set menuitem-image to session-badge */
-                    GtkIconTheme *icon_theme = gtk_icon_theme_get_default ();
                     gchar* session_name = g_ascii_strdown (session, -1);
-                    gchar* icon_name = g_strdup_printf ("%s_badge-symbolic", session_name);
+                    gchar* icon_name = g_strdup_printf ("#%s_badge-symbolic", session_name);
+                    greeter_menu_bar_set_item_image (menubar, session_menuitem, icon_name);
+                    greeter_menu_bar_set_item_text (menubar, session_menuitem, gtk_menu_item_get_label (GTK_MENU_ITEM (menu_iter->data)));
+                    g_free (icon_name);
                     g_free (session_name);
-                    if (gtk_icon_theme_has_icon (icon_theme, icon_name))
-                        gtk_image_set_from_icon_name (GTK_IMAGE (session_badge), icon_name, GTK_ICON_SIZE_MENU);
-                    else
-                        gtk_image_set_from_icon_name (GTK_IMAGE (session_badge), "document-properties-symbolic", GTK_ICON_SIZE_MENU);
-                    g_free (icon_name);
                     break;
                 }
         }
         if (!menu_iter)
             menu_iter = menu_items;
 
+        const gchar *tooltip = NULL;
+        gchar *tooltip_to_free = NULL;
+
         if (menu_iter)
-	        gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (menu_iter->data), TRUE);
+        {
+            gtk_check_menu_item_set_active (menu_iter->data, TRUE);
+
+            const gchar *label = gtk_menu_item_get_label (menu_iter->data);
+            if (g_strcmp0 (label, session) == 0)
+                tooltip = session;
+            else
+                tooltip = tooltip_to_free = g_strdup_printf ("%s (%s)", label, session);
+        }
+
+        greeter_menu_bar_set_item_tooltip (menubar, session_menuitem, tooltip);
+        g_free (tooltip_to_free);
     }
 
     g_free (current_session);
@@ -885,7 +915,6 @@
        set_session (g_object_get_data (G_OBJECT (menuitem), SESSION_DATA_KEY));
 }
 
-
 /* Session language */
 
 static gchar*
@@ -936,7 +965,7 @@
                 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (menu_iter->data), TRUE);
                 g_free (current_language);
                 current_language = g_strdup (language);
-                gtk_menu_item_set_label (GTK_MENU_ITEM (language_menuitem),language);
+                greeter_menu_bar_set_item_text (menubar, language_menuitem, language);
                 return;
             }
         }
@@ -946,7 +975,7 @@
     if (lightdm_get_language ())
     {
         default_language = lightdm_language_get_code (lightdm_get_language ());
-        gtk_menu_item_set_label (GTK_MENU_ITEM (language_menuitem), default_language);
+        greeter_menu_bar_set_item_text (menubar, language_menuitem, default_language);
     }
     if (default_language && g_strcmp0 (default_language, language) != 0)
         set_language (default_language);
@@ -957,7 +986,7 @@
         {
             if (gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (menu_iter->data)))
             {
-                gtk_menu_item_set_label (GTK_MENU_ITEM (language_menuitem), g_strdup (g_object_get_data (G_OBJECT (menu_iter->data), LANGUAGE_DATA_CODE)));
+                greeter_menu_bar_set_item_text (menubar, language_menuitem, g_object_get_data (G_OBJECT (menu_iter->data), LANGUAGE_DATA_CODE));
                 break;
             }
         }
@@ -1056,424 +1085,113 @@
 
 /* Panel and indicators */
 
-static gboolean
-panel_item_enter_notify_cb (GtkWidget *widget, GdkEvent *event, gpointer enter)
-{
-    GtkStyleContext *context = gtk_widget_get_style_context (widget);
-    if (GPOINTER_TO_INT (enter))
-        gtk_style_context_add_class (context, PANEL_ITEM_STYLE_HOVERED);
-    else
-        gtk_style_context_remove_class (context, PANEL_ITEM_STYLE_HOVERED);
-    return FALSE;
-}
-
-static void
-panel_add_item (GtkWidget *widget, gint index, GreeterPanelItemType item_type)
-{
-    gint insert_pos = 0;
-    GList* items = gtk_container_get_children (GTK_CONTAINER (menubar));
-    GList* item;
-    for (item = items; item; item = item->next)
-    {
-        if (GPOINTER_TO_INT (g_object_get_data (G_OBJECT (item->data), PANEL_ITEM_DATA_INDEX)) < index)
-            break;
-        insert_pos++;
-    }
-    g_list_free (items);
-
-    gtk_style_context_add_class (gtk_widget_get_style_context (widget), PANEL_ITEM_STYLE);
-    gtk_style_context_add_class (gtk_widget_get_style_context (widget), PANEL_ITEM_STYLES[item_type]);
-    if (item_type == PANEL_ITEM_INDICATOR)
-    {
-        g_signal_connect (G_OBJECT (widget), "enter-notify-event",
-                          G_CALLBACK (panel_item_enter_notify_cb), GINT_TO_POINTER (TRUE));
-        g_signal_connect (G_OBJECT (widget), "leave-notify-event",
-                          G_CALLBACK (panel_item_enter_notify_cb), GINT_TO_POINTER (FALSE));
-    }
-
-    gtk_widget_show (widget);
-    gtk_menu_shell_insert (GTK_MENU_SHELL (menubar), widget, insert_pos);
-}
-
-#ifdef HAVE_LIBINDICATOR
-static gboolean
-indicator_entry_scrolled_cb (GtkWidget *menuitem, GdkEventScroll *event, gpointer data)
-{
-    IndicatorObject      *io;
-    IndicatorObjectEntry *entry;
-
-    g_return_val_if_fail (GTK_IS_WIDGET (menuitem), FALSE);
-
-    io = g_object_get_data (G_OBJECT (menuitem), INDICATOR_ITEM_DATA_OBJECT);
-    entry = g_object_get_data (G_OBJECT (menuitem), INDICATOR_ITEM_DATA_ENTRY);
-
-    g_return_val_if_fail (INDICATOR_IS_OBJECT (io), FALSE);
-
-    g_signal_emit_by_name (io, "scroll", 1, event->direction);
-    g_signal_emit_by_name (io, "scroll-entry", entry, 1, event->direction);
-
-    return FALSE;
-}
-
-static void
-indicator_entry_activated_cb (GtkWidget *widget, gpointer user_data)
-{
-    IndicatorObject      *io;
-    IndicatorObjectEntry *entry;
-
-    g_return_if_fail (GTK_IS_WIDGET (widget));
-
-    io = g_object_get_data (G_OBJECT (widget), INDICATOR_ITEM_DATA_OBJECT);
-    entry = g_object_get_data (G_OBJECT (widget), INDICATOR_ITEM_DATA_ENTRY);
-
-    g_return_if_fail (INDICATOR_IS_OBJECT (io));
-
-    return indicator_object_entry_activate (io, entry, gtk_get_current_event_time ());
-}
-
-static GtkWidget*
-indicator_entry_create_menuitem (IndicatorObject *io, IndicatorObjectEntry *entry, GtkWidget *menubar)
-{
-    GtkWidget *box, *menuitem;
-    gint index = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (io), PANEL_ITEM_DATA_INDEX));
-    box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 3);
-    menuitem = gtk_menu_item_new ();
-
-    gtk_widget_add_events (GTK_WIDGET (menuitem), GDK_SCROLL_MASK);
-
-    g_object_set_data (G_OBJECT (menuitem), INDICATOR_ITEM_DATA_BOX, box);
-    g_object_set_data (G_OBJECT (menuitem), INDICATOR_ITEM_DATA_OBJECT, io);
-    g_object_set_data (G_OBJECT (menuitem), INDICATOR_ITEM_DATA_ENTRY, entry);
-    g_object_set_data (G_OBJECT (menuitem), PANEL_ITEM_DATA_INDEX, GINT_TO_POINTER (index));
-
-    g_signal_connect (G_OBJECT (menuitem), "activate", G_CALLBACK (indicator_entry_activated_cb), NULL);
-    g_signal_connect (G_OBJECT (menuitem), "scroll-event", G_CALLBACK (indicator_entry_scrolled_cb), NULL);
-
-    if (entry->image)
-        gtk_box_pack_start (GTK_BOX (box), GTK_WIDGET (entry->image), FALSE, FALSE, 1);
-
-    if (entry->label)
-        gtk_box_pack_start (GTK_BOX (box), GTK_WIDGET (entry->label), FALSE, FALSE, 1);
-
-    if (entry->menu)
-        gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), GTK_WIDGET (entry->menu));
-
-    gtk_container_add (GTK_CONTAINER (menuitem), box);
-    gtk_widget_show (box);
-    panel_add_item (menuitem, index, PANEL_ITEM_INDICATOR);
-
-    return menuitem;
-}
-
-static void
-indicator_entry_added_cb (IndicatorObject *io, IndicatorObjectEntry *entry, gpointer user_data)
-{
-    GHashTable *menuitem_lookup;
-    GtkWidget  *menuitem;
-
-    /* if the menuitem doesn't already exist, create it now */
-    menuitem_lookup = g_object_get_data (G_OBJECT (io), INDICATOR_DATA_MENUITEMS);
-    g_return_if_fail (menuitem_lookup);
-    menuitem = g_hash_table_lookup (menuitem_lookup, entry);
-    if (!GTK_IS_WIDGET (menuitem))
-    {
-        menuitem = indicator_entry_create_menuitem (io, entry, GTK_WIDGET (user_data));
-        g_hash_table_insert (menuitem_lookup, entry, menuitem);
-    }
-
-    gtk_widget_show (menuitem);
-}
-
-static void
-remove_indicator_entry_cb (GtkWidget *widget, gpointer userdata)
-{
-    IndicatorObject *io;
-    GHashTable      *menuitem_lookup;
-    GtkWidget       *menuitem;
-    gpointer         entry;
-
-    io = g_object_get_data (G_OBJECT (widget), INDICATOR_ITEM_DATA_OBJECT);
-    if (!INDICATOR_IS_OBJECT (io))
-        return;
-
-    entry = g_object_get_data (G_OBJECT (widget), INDICATOR_ITEM_DATA_ENTRY);
-    if (entry != userdata)
-        return;
-
-    menuitem_lookup = g_object_get_data (G_OBJECT (io), INDICATOR_DATA_MENUITEMS);
-    g_return_if_fail (menuitem_lookup);
-    menuitem = g_hash_table_lookup (menuitem_lookup, entry);
-    if (GTK_IS_WIDGET (menuitem))
-        gtk_widget_hide (menuitem);
-
-    gtk_widget_destroy (widget);
-}
-
-static void
-indicator_entry_removed_cb (IndicatorObject *io, IndicatorObjectEntry *entry, gpointer user_data)
-{
-    gtk_container_foreach (GTK_CONTAINER (user_data), remove_indicator_entry_cb, entry);
-}
-
-static void
-indicator_menu_show_cb (IndicatorObject *io, IndicatorObjectEntry *entry, guint32 timestamp, gpointer user_data)
-{
-    IndicatorObjectEntry *entrydata;
-    GtkWidget            *menuitem;
-    GList                *entries, *lp;
-
-    menuitem = GTK_WIDGET (user_data);
-
-    if (!entry)
-    {
-        /* Close any open menus instead of opening one */
-        entries = indicator_object_get_entries (io);
-        for (lp = entries; lp; lp = g_list_next (entry))
-        {
-            entrydata = lp->data;
-            gtk_menu_popdown (entrydata->menu);
-        }
-        g_list_free (entries);
-
-        /* And tell the menuitem to exit activation mode too */
-        gtk_menu_shell_cancel (GTK_MENU_SHELL (menuitem));
-    }
-}
-
-static void
-greeter_set_env (const gchar* key, const gchar* value)
-{
-    g_setenv (key, value, TRUE);
-
-    GDBusProxy* proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
-                                                       G_DBUS_PROXY_FLAGS_NONE,
-                                                       NULL,
-                                                       "org.freedesktop.DBus",
-                                                       "/org/freedesktop/DBus",
-                                                       "org.freedesktop.DBus",
-                                                       NULL, NULL);
-    GVariant *result;
-    GVariantBuilder *builder = g_variant_builder_new (G_VARIANT_TYPE_ARRAY);
-    g_variant_builder_add (builder, "{ss}", key, value);
-    result = g_dbus_proxy_call_sync (proxy, "UpdateActivationEnvironment", g_variant_new ("(a{ss})", builder),
-                                     G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL);
-    g_variant_unref (result);
-    g_variant_builder_unref (builder);
-    g_object_unref (proxy);
-}
-#endif
-
-static gboolean
-menu_item_accel_closure_cb (GtkAccelGroup *accel_group,
-                            GObject *acceleratable, guint keyval,
-                            GdkModifierType modifier, gpointer data)
-{
-    gtk_menu_item_activate (data);
-    return FALSE;
+static void
+panel_init (GKeyFile* config)
+{
+    const gchar *DEFAULT_LAYOUT =
+        "~host;"
+        "~spacer;"
+        "~clock;"
+        "~spacer;"
+        "~session;"
+        "~language;"
+        "~a11y;"
+        "~power";
+
+    gchar *key_value = g_key_file_get_string (config, "greeter", "indicators", NULL);
+
+    /* Do not remove our builtin items from menubar - load_from_string() will do it */
+    greeter_menu_bar_load_from_string(GREETER_MENU_BAR (menubar), key_value ? key_value : DEFAULT_LAYOUT);
+
+    g_free (key_value);
+
+    /* Accelerators for unused items */
+    g_hash_table_foreach (panel_builtin_items, (GHFunc)panel_reassing_item_accel_table_cb, NULL);
+}
+
+static void
+panel_items_visibility_cb (GreeterMenuBar *menubar, gboolean visible, gpointer user_data)
+{
+    gtk_widget_set_visible (panel_window, visible);
 }
 
 /* Maybe unnecessary (in future) trick to enable accelerators for hidden/detached menu items */
 static void
-reassign_menu_item_accel (GtkWidget *item)
+panel_reassing_item_accel_cb (GtkMenuItem *menuitem)
 {
     GtkAccelKey key;
-    const gchar *accel_path = gtk_menu_item_get_accel_path (GTK_MENU_ITEM (item));
+    const gchar *accel_path = gtk_menu_item_get_accel_path (menuitem);
 
     if (accel_path && gtk_accel_map_lookup_entry (accel_path, &key))
     {
-        GClosure *closure = g_cclosure_new (G_CALLBACK (menu_item_accel_closure_cb), item, NULL);
-        gtk_accel_group_connect (gtk_menu_get_accel_group (GTK_MENU (gtk_widget_get_parent (item))),
+        GClosure *closure = g_cclosure_new (G_CALLBACK (panel_item_accel_closure_cb), menuitem, NULL);
+        gtk_accel_group_connect (gtk_menu_get_accel_group (GTK_MENU (gtk_widget_get_parent (GTK_WIDGET (menuitem)))),
                                  key.accel_key, key.accel_mods, key.accel_flags, closure);
         g_closure_unref (closure);
     }
 
-    GtkWidget* submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (item));
+    GtkWidget* submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (menuitem));
     if (submenu)
-        gtk_container_foreach (GTK_CONTAINER (submenu), (GtkCallback)reassign_menu_item_accel, NULL);
+        gtk_container_foreach (GTK_CONTAINER (submenu), (GtkCallback)panel_reassing_item_accel_cb, NULL);
 }
 
 static void
-#ifdef START_INDICATOR_SERVICES
-init_indicators (GKeyFile* config, GPid* indicator_pid, GPid* spi_pid)
-#else
-init_indicators (GKeyFile* config)
-#endif
-{
-    gchar **names = NULL;
-    gsize length = 0;
-    guint i;
-    GHashTable *builtin_items = NULL;
-    GHashTableIter iter;
-    gpointer iter_value;
-    #ifdef HAVE_LIBINDICATOR
-    gboolean inited = FALSE;
-    #endif
-    gboolean fallback = FALSE;
-
-    const gchar *DEFAULT_LAYOUT[] = {"~host", "~spacer", "~clock", "~spacer",
-                                     "~session", "~language", "~a11y", "~power", NULL};
-
-#ifdef START_INDICATOR_SERVICES
-    GError *error = NULL;
-    gchar *AT_SPI_CMD[] = {"/usr/lib/at-spi2-core/at-spi-bus-launcher", "--launch-immediately", NULL};
-    gchar *INDICATORS_CMD[] = {"init", "--user", "--startup-event", "indicator-services-start", NULL};
-#endif
-
-    if (g_key_file_has_key (config, "greeter", "indicators", NULL))
-    {   /* no option = default list, empty value = empty list */
-        names = g_key_file_get_string_list (config, "greeter", "indicators", &length, NULL);
-    }
-    else if (g_key_file_has_key (config, "greeter", "show-indicators", NULL))
-    {   /* fallback mode: no option = empty value = default list */
-        names = g_key_file_get_string_list (config, "greeter", "show-indicators", &length, NULL);
-        if (length == 0)
-            fallback = TRUE;
-    }
-
-    if (!names || fallback)
-    {
-        names = (gchar**)DEFAULT_LAYOUT;
-        length = g_strv_length (names);
-    }
-
-    builtin_items = g_hash_table_new (g_str_hash, g_str_equal);
-
-    g_hash_table_insert (builtin_items, "~power", power_menuitem);
-    g_hash_table_insert (builtin_items, "~session", session_menuitem);
-    g_hash_table_insert (builtin_items, "~language", language_menuitem);
-    g_hash_table_insert (builtin_items, "~a11y", a11y_menuitem);
-    g_hash_table_insert (builtin_items, "~layout", layout_menuitem);
-    g_hash_table_insert (builtin_items, "~host", host_menuitem);
-    g_hash_table_insert (builtin_items, "~clock", clock_menuitem);
-
-    g_hash_table_iter_init (&iter, builtin_items);
-    while (g_hash_table_iter_next (&iter, NULL, &iter_value))
-        gtk_container_remove (GTK_CONTAINER (menubar), iter_value);
-
-    for (i = 0; i < length; ++i)
-    {
-        if (names[i][0] == '~')
-        {   /* Built-in indicators */
-            GreeterPanelItemType item_type = PANEL_ITEM_INDICATOR;
-            if (g_hash_table_lookup_extended (builtin_items, names[i], NULL, &iter_value))
-                g_hash_table_remove (builtin_items, (gconstpointer)names[i]);
-            else if (g_strcmp0 (names[i], "~separator") == 0)
-            {
-                GtkWidget *separator = gtk_separator_new (GTK_ORIENTATION_VERTICAL);
-                item_type = PANEL_ITEM_SEPARATOR;
-                iter_value = gtk_separator_menu_item_new ();
-                gtk_widget_show (separator);
-                gtk_container_add (iter_value, separator);
-            }
-            else if (g_strcmp0 (names[i], "~spacer") == 0)
-            {
-                item_type = PANEL_ITEM_SPACER;
-                iter_value = gtk_separator_menu_item_new ();
-                gtk_menu_item_set_label (iter_value, "");
-                gtk_widget_set_hexpand (iter_value, TRUE);
-            }
-            else if (names[i][1] == '~')
-            {
-                item_type = PANEL_ITEM_TEXT;
-                iter_value = gtk_separator_menu_item_new ();
-                gtk_menu_item_set_label (iter_value, &names[i][2]);
-            }
-            else
-                continue;
-
-            g_object_set_data (G_OBJECT (iter_value), PANEL_ITEM_DATA_INDEX, GINT_TO_POINTER (i));
-            panel_add_item (iter_value, i, item_type);
-            continue;
-        }
-
-        #ifdef HAVE_LIBINDICATOR
-        gchar* path = NULL;
-        IndicatorObject* io = NULL;
-
-        if (!inited)
-        {
-            /* Set indicators to run with reduced functionality */
-            greeter_set_env ("INDICATOR_GREETER_MODE", "1");
-            /* Don't allow virtual file systems? */
-            greeter_set_env ("GIO_USE_VFS", "local");
-            greeter_set_env ("GVFS_DISABLE_FUSE", "1");
-
-            #ifdef START_INDICATOR_SERVICES
-            if (!g_spawn_async (NULL, AT_SPI_CMD, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, spi_pid, &error))
-                g_warning ("Failed to run \"at-spi-bus-launcher\": %s", error->message);
-            g_clear_error (&error);
-
-            if (!g_spawn_async (NULL, INDICATORS_CMD, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, indicator_pid, &error))
-                g_warning ("Failed to run \"indicator-services\": %s", error->message);
-            g_clear_error (&error);
-            #endif
-            inited = TRUE;
-        }
-
-        if (g_path_is_absolute (names[i]))
-        {   /* library with absolute path */
-            io = indicator_object_new_from_file (names[i]);
-        }
-        else if (g_str_has_suffix (names[i], G_MODULE_SUFFIX))
-        {   /* library */
-            path = g_build_filename (INDICATOR_DIR, names[i], NULL);
-            io = indicator_object_new_from_file (path);
-        }
-        #ifdef HAVE_LIBINDICATOR_NG
-        else
-        {   /* service file */
-            if (strchr (names[i], '.'))
-                path = g_strdup_printf ("%s/%s", UNITY_INDICATOR_DIR, names[i]);
-            else
-                path = g_strdup_printf ("%s/com.canonical.indicator.%s", UNITY_INDICATOR_DIR, names[i]);
-            io = INDICATOR_OBJECT (indicator_ng_new_for_profile (path, "desktop_greeter", NULL));
-        }
-        #endif
-
-        if (io)
-        {
-            GList *entries, *lp;
-
-            /* used to store/fetch menu entries */
-            g_object_set_data_full (G_OBJECT (io), INDICATOR_DATA_MENUITEMS,
-                                    g_hash_table_new (g_direct_hash, g_direct_equal),
-                                    (GDestroyNotify) g_hash_table_destroy);
-            g_object_set_data (G_OBJECT (io), PANEL_ITEM_DATA_INDEX, GINT_TO_POINTER (i));
-
-            g_signal_connect (G_OBJECT (io), INDICATOR_OBJECT_SIGNAL_ENTRY_ADDED,
-                              G_CALLBACK (indicator_entry_added_cb), menubar);
-            g_signal_connect (G_OBJECT (io), INDICATOR_OBJECT_SIGNAL_ENTRY_REMOVED,
-                              G_CALLBACK (indicator_entry_removed_cb), menubar);
-            g_signal_connect (G_OBJECT (io), INDICATOR_OBJECT_SIGNAL_MENU_SHOW,
-                              G_CALLBACK (indicator_menu_show_cb), menubar);
-
-            entries = indicator_object_get_entries (io);
-            for (lp = entries; lp; lp = g_list_next (lp))
-                indicator_entry_added_cb (io, lp->data, menubar);
-            g_list_free (entries);
-        }
-        else
-        {
-            g_warning ("Indicator \"%s\": failed to load", names[i]);
-        }
-
-        g_free (path);
-        #endif
-    }
-    if (names && names != (gchar**)DEFAULT_LAYOUT)
-        g_strfreev (names);
-
-    if (builtin_items)
-    {
-        g_hash_table_iter_init (&iter, builtin_items);
-        while (g_hash_table_iter_next (&iter, NULL, &iter_value))
-        {
-            reassign_menu_item_accel (iter_value);
-            gtk_widget_hide (iter_value);
-        }
-
-        g_hash_table_unref (builtin_items);
-    }
+panel_reassing_item_accel_table_cb (const gchar *name, const BuiltinPanelItemInfo *info)
+{
+    if (!gtk_widget_get_parent (info->menuitem))
+        panel_reassing_item_accel_cb (GTK_MENU_ITEM (info->menuitem));
+}
+
+static gboolean
+panel_item_accel_closure_cb (GtkAccelGroup *accel_group, GObject *acceleratable, guint keyval,
+                             GdkModifierType modifier, GtkMenuItem *menuitem)
+{
+    gtk_menu_item_activate (menuitem);
+    return FALSE;
+}
+
+static GtkWidget*
+panel_unknown_item_cb (GreeterMenuBar *menubar, const gchar *name, GHashTable *options, gpointer user_data)
+{
+    BuiltinPanelItemInfo *info = g_hash_table_lookup (panel_builtin_items, name);
+    if (!info || info->used)
+        return NULL;
+
+    const gchar **pair;
+    for (pair = info->options; *pair; pair += 2)
+    {
+        const gchar *option = pair[0];
+        const gchar *value = pair[1];
+
+        if (!g_hash_table_contains (options, option))
+            g_hash_table_insert (options, g_strdup (option), g_strdup (value));
+    }
+
+    info->used = TRUE;
+    return info->menuitem;
+}
+
+static gboolean
+panel_unknown_item_option_cb(GreeterMenuBar *menubar, GtkWidget *menuitem,
+                             const gchar *name, const gchar *option, const gchar *value, gpointer user_data)
+{
+    if (menuitem == power_menuitem && g_strcmp0 (option, POWER_OPTION_HIDE_DISABLED) == 0)
+    {
+        if (!value || greeter_str_to_bool (value))
+        {
+            g_object_set_data (G_OBJECT (power_menuitem), POWER_DATA_HIDE_DISABLED, GINT_TO_POINTER (TRUE));
+            return TRUE;
+        }
+    }
+    else if (menuitem == clock_menuitem && g_strcmp0 (option, CLOCK_OPTION_CLOCK_FORMAT) == 0)
+    {
+        if (value)
+        {
+            clock_format = g_strdup (value);
+            return clock_format != NULL;
+        }
+    }
+    return FALSE;
 }
 
 /* Layout indicator */
@@ -1494,8 +1212,7 @@
             if (g_strcmp0 (name, lightdm_layout_get_name (item->data)) == 0)
             {
                 lightdm_set_layout (item->data);
-                gtk_menu_item_set_label (GTK_MENU_ITEM (layout_menuitem),
-                                         g_object_get_data (G_OBJECT (menuitem), LAYOUT_DATA_LABEL));
+                greeter_menu_bar_set_item_text (menubar, layout_menuitem, g_object_get_data (G_OBJECT (menuitem), LAYOUT_DATA_LABEL));
                 break;
             }
         }
@@ -1596,12 +1313,11 @@
 
     if (menu_item)
     {
-        gtk_menu_item_set_label (GTK_MENU_ITEM (layout_menuitem),
-                                 g_object_get_data (G_OBJECT (menu_item), LAYOUT_DATA_LABEL));
+        greeter_menu_bar_set_item_text (menubar, layout_menuitem, g_object_get_data (G_OBJECT (menu_item), LAYOUT_DATA_LABEL));
         gtk_check_menu_item_set_active (menu_item, TRUE);
     }
     else
-        gtk_menu_item_set_label (GTK_MENU_ITEM (layout_menuitem), "??");
+        greeter_menu_bar_set_item_text (menubar, layout_menuitem, "??");
     g_list_free (menu_items);
     #else
     LightDMLayout *layout = lightdm_get_layout ();
@@ -1615,8 +1331,7 @@
         if (g_strcmp0 (name, g_object_get_data (G_OBJECT (menu_iter->data), LAYOUT_DATA_NAME)) == 0)
         {
             gtk_check_menu_item_set_active (menu_iter->data, TRUE);
-            gtk_menu_item_set_label (GTK_MENU_ITEM (layout_menuitem),
-                                     g_object_get_data (G_OBJECT (menu_iter->data), LAYOUT_DATA_LABEL));
+            greeter_menu_bar_set_item_text (menubar, layout_menuitem, g_object_get_data (G_OBJECT (menu_iter->data), LAYOUT_DATA_LABEL));
             break;
         }
     }
@@ -1653,7 +1368,7 @@
 /* a11y indciator */
 
 void
-a11y_font_cb (GtkCheckMenuItem *item)
+a11y_font_cb (GtkCheckMenuItem *item, gpointer user_data)
 {
     if (gtk_check_menu_item_get_active (item))
     {
@@ -1685,7 +1400,7 @@
 }
 
 void
-a11y_contrast_cb (GtkCheckMenuItem *item)
+a11y_contrast_cb (GtkCheckMenuItem *item, gpointer user_data)
 {
     if (gtk_check_menu_item_get_active (item))
     {
@@ -1719,13 +1434,16 @@
 
 /* Power indicator */
 
-static void
-power_menu_cb (GtkWidget *menuitem, gpointer userdata)
+void
+power_menuitem_activate_cb (GtkWidget *menuitem, gpointer userdata)
 {
-    gtk_widget_set_sensitive (suspend_menuitem, lightdm_get_can_suspend ());
-    gtk_widget_set_sensitive (hibernate_menuitem, lightdm_get_can_hibernate ());
-    gtk_widget_set_sensitive (restart_menuitem, lightdm_get_can_restart ());
-    gtk_widget_set_sensitive (shutdown_menuitem, lightdm_get_can_shutdown ());
+    gboolean hide = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (power_menuitem), POWER_DATA_HIDE_DISABLED));
+    void (*set_state) (GtkWidget*, gboolean) = hide ? gtk_widget_set_visible : gtk_widget_set_sensitive;
+
+    set_state (suspend_menuitem, lightdm_get_can_suspend ());
+    set_state (hibernate_menuitem, lightdm_get_can_hibernate ());
+    set_state (restart_menuitem, lightdm_get_can_restart ());
+    set_state (shutdown_menuitem, lightdm_get_can_shutdown ());
 }
 
 void
@@ -2450,17 +2168,11 @@
     GKeyFile *config;
     GtkBuilder *builder;
     const GList *items, *item;
-    GtkWidget *image;
     gchar *value, **values, *state_dir;
-    GtkIconTheme *icon_theme;
     GtkCssProvider *css_provider;
     GError *error = NULL;
     Display *display;
 
-    #ifdef START_INDICATOR_SERVICES
-    GPid indicator_pid = 0, spi_pid = 0;
-    #endif
-
     /* Prevent memory from being swapped out, as we are dealing with passwords */
     mlockall (MCL_CURRENT | MCL_FUTURE);
 
@@ -2476,7 +2188,7 @@
     bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
     textdomain (GETTEXT_PACKAGE);
 
-    g_unix_signal_add (SIGTERM, (GSourceFunc)gtk_main_quit, NULL);
+    g_unix_signal_add (SIGTERM, sigterm_cb, NULL);
 
     /* init gtk */
     gtk_init (&argc, &argv);
@@ -2529,6 +2241,21 @@
         XSetScreenSaver (display, screensaver_timeout, 0, ScreenSaverActive, DefaultExposures);
     }
 
+    #ifdef START_INDICATOR_SERVICES
+    gchar *INDICATOR_SEVICES_CMD[] = {"init", "--user", "--startup-event", "indicator-services-start", NULL};
+    if (!spawn_and_remember (INDICATOR_SEVICES_CMD, &error))
+        g_warning ("Failed to run 'indicator-services': %s", error->message);
+    g_clear_error(&error);
+    #endif
+
+    #ifdef START_AT_SPI
+    gchar *AT_SPI_CMD[] = {"/usr/lib/at-spi2-core/at-spi-bus-launcher", "--launch-immediately", NULL};
+    g_setenv ("GTK_MODULES", "atk-bridge", TRUE);
+    if (!spawn_and_remember (AT_SPI_CMD, &error))
+        g_warning ("Failed to run 'at-spi-bus-launcher': %s", error->message);
+    g_clear_error (&error);
+    #endif
+
     /* Set GTK+ settings */
     value = g_key_file_get_value (config, "greeter", "theme-name", NULL);
     if (value)
@@ -2604,7 +2331,7 @@
 
     /* Panel window*/
     panel_window = GTK_WIDGET (gtk_builder_get_object (builder, "panel_window"));
-    menubar = GTK_WIDGET (gtk_builder_get_object (builder, "menubar"));
+    menubar = GREETER_MENU_BAR (gtk_builder_get_object (builder, "menubar"));
     session_menuitem = GTK_WIDGET (gtk_builder_get_object (builder, "session_menuitem"));
     session_menu = GTK_MENU (gtk_builder_get_object (builder, "session_menu"));
     language_menuitem = GTK_WIDGET (gtk_builder_get_object (builder, "language_menuitem"));
@@ -2620,6 +2347,31 @@
     clock_menuitem = GTK_WIDGET (gtk_builder_get_object (builder, "clock_menuitem"));
     host_menuitem = GTK_WIDGET (gtk_builder_get_object (builder, "host_menuitem"));
 
+    panel_builtin_items = g_hash_table_new(g_str_hash, g_str_equal);
+
+    const BuiltinPanelItemInfo PANEL_BUILTIN_ITEMS[] =
+    {
+        {.name = "~power",    .menuitem = power_menuitem,    .options = {"layout", "image", "fallback-image", "#system-shutdown-symbolic", NULL}},
+        {.name = "~session",  .menuitem = session_menuitem,  .options = {"layout", "image", "fallback-image", "#document-properties-symbolic", NULL}},
+        {.name = "~language", .menuitem = language_menuitem, .options = {"layout", "text",  NULL}},
+        {.name = "~a11y",     .menuitem = a11y_menuitem,     .options = {"layout", "image", "fallback-image", "#preferences-desktop-accessibility-symbolic", NULL}},
+        {.name = "~layout",   .menuitem = layout_menuitem,   .options = {"layout", "text",  NULL}},
+        {.name = "~host",     .menuitem = host_menuitem,     .options = {"layout", "text",  NULL}},
+        {.name = "~clock",    .menuitem = clock_menuitem,    .options = {"layout", "text",  NULL}},
+        {.name = NULL}
+    };
+    const BuiltinPanelItemInfo *builtin_panel_item;
+
+    for (builtin_panel_item = PANEL_BUILTIN_ITEMS; builtin_panel_item->name; ++builtin_panel_item)
+        g_hash_table_insert (panel_builtin_items, (gpointer)builtin_panel_item->name, (gpointer)builtin_panel_item);
+
+    g_signal_connect (G_OBJECT (menubar),
+                      GREETER_MENU_BAR_SIGNAL_ITEMS_VISIBILITY_CHANGED, G_CALLBACK (panel_items_visibility_cb), NULL);
+    g_signal_connect (G_OBJECT (menubar),
+                      GREETER_MENU_BAR_SIGNAL_UNKNOWN_ITEM, G_CALLBACK (panel_unknown_item_cb), NULL);
+    g_signal_connect (G_OBJECT (menubar),
+                      GREETER_MENU_BAR_SIGNAL_UNKNOWN_ITEM_OPTION, G_CALLBACK (panel_unknown_item_option_cb), NULL);
+
     /* Power dialog */
     power_window = GTK_WIDGET (gtk_builder_get_object (builder, "power_window"));
     power_ok_button = GTK_BUTTON (gtk_builder_get_object (builder, "power_ok_button"));
@@ -2629,7 +2381,7 @@
     power_icon = GTK_IMAGE (gtk_builder_get_object (builder, "power_icon"));
 
     gtk_overlay_add_overlay (screen_overlay, login_window);
-    gtk_overlay_add_overlay (screen_overlay, panel_window);
+    gtk_overlay_add_overlay (screen_overlay, panel_window); /* Hidden without indicators, see panel_item_visibility_changed () */
     gtk_overlay_add_overlay (screen_overlay, power_window);
 
     gtk_accel_map_add_entry ("<Login>/a11y/font", GDK_KEY_F1, 0);
@@ -2638,18 +2390,7 @@
     gtk_accel_map_add_entry ("<Login>/a11y/reader", GDK_KEY_F4, 0);
     gtk_accel_map_add_entry ("<Login>/power/shutdown", GDK_KEY_F4, GDK_MOD1_MASK);
 
-#ifdef START_INDICATOR_SERVICES
-    init_indicators (config, &indicator_pid, &spi_pid);
-#else
-    init_indicators (config);
-#endif
-
-    /* Hide empty panel */
-    GList *menubar_items = gtk_container_get_children (GTK_CONTAINER (menubar));
-    if (!menubar_items)
-        gtk_widget_hide (GTK_WIDGET (panel_window));
-    else
-        g_list_free (menubar_items);
+    panel_init (config);
 
     if (g_key_file_get_boolean (config, "greeter", "hide-user-image", NULL))
     {
@@ -2677,18 +2418,9 @@
         }
     }
 
-    icon_theme = gtk_icon_theme_get_default ();
-
     /* Session menu */
     if (gtk_widget_get_visible (session_menuitem))
     {
-        if (gtk_icon_theme_has_icon (icon_theme, "document-properties-symbolic"))
-            session_badge = gtk_image_new_from_icon_name ("document-properties-symbolic", GTK_ICON_SIZE_MENU);
-        else
-            session_badge = gtk_image_new_from_icon_name ("document-properties", GTK_ICON_SIZE_MENU);
-        gtk_widget_show (session_badge);
-        gtk_container_add (GTK_CONTAINER (session_menuitem), session_badge);
-
         items = lightdm_get_sessions ();
         GSList *sessions = NULL;
         for (item = items; item; item = item->next)
@@ -2746,12 +2478,7 @@
     /* a11y menu */
     if (gtk_widget_get_visible (a11y_menuitem))
     {
-        if (gtk_icon_theme_has_icon (icon_theme, "preferences-desktop-accessibility-symbolic"))
-            image = gtk_image_new_from_icon_name ("preferences-desktop-accessibility-symbolic", GTK_ICON_SIZE_MENU);
-        else
-            image = gtk_image_new_from_icon_name ("preferences-desktop-accessibility", GTK_ICON_SIZE_MENU);
-        gtk_widget_show (image);
-        gtk_container_add (GTK_CONTAINER (a11y_menuitem), image);
+        /* Nothing special to do */
     }
 
     value = g_key_file_get_value (config, "greeter", "keyboard", NULL);
@@ -2768,19 +2495,10 @@
     /* Power menu */
     if (gtk_widget_get_visible (power_menuitem))
     {
-        if (gtk_icon_theme_has_icon (icon_theme, "system-shutdown-symbolic"))
-            image = gtk_image_new_from_icon_name ("system-shutdown-symbolic", GTK_ICON_SIZE_MENU);
-        else
-            image = gtk_image_new_from_icon_name ("system-shutdown", GTK_ICON_SIZE_MENU);
-        gtk_widget_show (image);
-        gtk_container_add (GTK_CONTAINER (power_menuitem), image);
-
-        suspend_menuitem = (GTK_WIDGET (gtk_builder_get_object (builder, "suspend_menuitem")));
-        hibernate_menuitem = (GTK_WIDGET (gtk_builder_get_object (builder, "hibernate_menuitem")));
-        restart_menuitem = (GTK_WIDGET (gtk_builder_get_object (builder, "restart_menuitem")));
-        shutdown_menuitem = (GTK_WIDGET (gtk_builder_get_object (builder, "shutdown_menuitem")));
-
-        g_signal_connect (G_OBJECT (power_menuitem),"activate", G_CALLBACK (power_menu_cb), NULL);
+        suspend_menuitem = GTK_WIDGET (gtk_builder_get_object (builder, "suspend_menuitem"));
+        hibernate_menuitem = GTK_WIDGET (gtk_builder_get_object (builder, "hibernate_menuitem"));
+        restart_menuitem = GTK_WIDGET (gtk_builder_get_object (builder, "restart_menuitem"));
+        shutdown_menuitem = GTK_WIDGET (gtk_builder_get_object (builder, "shutdown_menuitem"));
     }
 
     /* Layout menu */
@@ -2814,19 +2532,17 @@
     }
 
     /* Host label */
-    if (gtk_widget_get_visible (host_menuitem))
-        gtk_menu_item_set_label (GTK_MENU_ITEM (host_menuitem), lightdm_get_hostname ());
+    greeter_menu_bar_set_item_text (menubar, host_menuitem, lightdm_get_hostname ());
 
     /* Clock label */
     if (gtk_widget_get_visible (clock_menuitem))
     {
-        gtk_menu_item_set_label (GTK_MENU_ITEM (clock_menuitem), "");
-        clock_label = gtk_bin_get_child (GTK_BIN (clock_menuitem));
-        clock_format = g_key_file_get_value (config, "greeter", "clock-format", NULL);
-        if (!clock_format)
-            clock_format = "%a, %H:%M";
-        clock_timeout_thread ();
-        gdk_threads_add_timeout (1000, (GSourceFunc) clock_timeout_thread, NULL);
+        if (!clock_format)
+            clock_format = g_key_file_get_value (config, "greeter", "clock-format", NULL);
+        if (!clock_format)
+            clock_format = g_strdup ("%a, %H:%M");
+        if (clock_timeout_cb (NULL))
+            gdk_threads_add_timeout (1000, clock_timeout_cb, NULL);
     }
 
     /* A bit of CSS */
@@ -2887,7 +2603,6 @@
 
     greeter_background_add_accel_group (greeter_background, GTK_ACCEL_GROUP (gtk_builder_get_object (builder, "a11y_accelgroup")));
     greeter_background_add_accel_group (greeter_background, GTK_ACCEL_GROUP (gtk_builder_get_object (builder, "power_accelgroup")));
-
     greeter_background_connect (greeter_background, gdk_screen_get_default ());
 
     if (lightdm_greeter_get_hide_users_hint (greeter))
@@ -2911,7 +2626,7 @@
         gtk_widget_set_valign (panel_window, GTK_ALIGN_END);
     g_free (value);
 
-    if (a11y_keyboard_command)
+    if (a11y_keyboard_command && a11y_keyboard_command->widget)
         g_object_set_data_full (G_OBJECT (a11y_keyboard_command->widget), WINDOW_DATA_POSITION,
                                 key_file_get_position (config, "greeter", "keyboard-position", &KEYBOARD_POSITION), g_free);
 
@@ -2965,19 +2680,8 @@
 
     gtk_main ();
 
-#ifdef START_INDICATOR_SERVICES
-    if (indicator_pid)
-    {
-		kill (indicator_pid, SIGTERM);
-		waitpid (indicator_pid, NULL, 0);
-    }
-
-    if (spi_pid)
-    {
-		kill (spi_pid, SIGTERM);
-		waitpid (spi_pid, NULL, 0);
-    }
-#endif
+    /* It's may be (and must be) already done in SIGTERM handler, anyway */
+    g_slist_free_full (spawned_pids, close_pid_cb);
 
     return EXIT_SUCCESS;
 }

=== modified file 'src/lightdm-gtk-greeter.glade'
--- src/lightdm-gtk-greeter.glade	2014-09-24 17:41:33 +0000
+++ src/lightdm-gtk-greeter.glade	2014-10-26 18:59:04 +0000
@@ -7,7 +7,6 @@
   <object class="GtkAccelGroup" id="power_accelgroup"/>
   <object class="GtkEventBox" id="panel_window">
     <property name="name">panel_window</property>
-    <property name="visible">True</property>
     <property name="can_focus">False</property>
     <property name="valign">start</property>
     <child>
@@ -22,6 +21,7 @@
             <property name="name">power_menuitem</property>
             <property name="visible">True</property>
             <property name="can_focus">False</property>
+            <signal name="activate" handler="power_menuitem_activate_cb" swapped="no"/>
             <child type="submenu">
               <object class="GtkMenu" id="power_menu">
                 <property name="visible">True</property>


Follow ups