From 7f5c60013343889924b336f2322aa7284730ffae Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Thu, 15 Oct 2009 09:44:09 -0400 Subject: [PATCH] Use a fixed ordering for well-known icons Define the ordering for well-known icons; see the page http://live.gnome.org/Features/StandardIconOrdering https://bugzilla.gnome.org/show_bug.cgi?id=598313 --- js/ui/panel.js | 49 ++++++++++++++++---- src/shell-tray-manager.c | 97 +++++++++++++++++++++++++++++++++------- src/shell-tray-manager.h | 3 +- 3 files changed, 123 insertions(+), 26 deletions(-) diff --git a/js/ui/panel.js b/js/ui/panel.js index c7966354b..9b481236c 100644 --- a/js/ui/panel.js +++ b/js/ui/panel.js @@ -63,6 +63,14 @@ const TRAY_BORDER_WIDTH = 0; const HOT_CORNER_ACTIVATION_TIMEOUT = 0.5; +const STANDARD_TRAY_ICON_ORDER = ['keyboard', 'volume', 'bluetooth', 'network', 'battery'] +const STANDARD_TRAY_ICON_IMPLEMENTATIONS = { + 'bluetooth-applet': 'bluetooth', + 'gnome-volume-control-applet': 'volume', + 'nm-applet': 'network', + 'gnome-power-manager': 'battery' +}; + function AppPanelMenu() { this._init(); } @@ -358,14 +366,7 @@ Panel.prototype = { trayContainer.append(trayBox, Big.BoxPackFlags.NONE); this._traymanager = new Shell.TrayManager({ bg_color: TRAY_BACKGROUND_COLOR }); - this._traymanager.connect('tray-icon-added', - Lang.bind(this, function(o, icon) { - trayBox.append(icon, Big.BoxPackFlags.NONE); - - // Make sure the trayBox is shown. - trayBox.show(); - this._recomputeTraySize(); - })); + this._traymanager.connect('tray-icon-added', Lang.bind(this, this._onTrayIconAdded)); this._traymanager.connect('tray-icon-removed', Lang.bind(this, function(o, icon) { trayBox.remove_actor(icon); @@ -442,6 +443,38 @@ Panel.prototype = { }); }, + _onTrayIconAdded: function(o, icon, wmClass) { + let role = STANDARD_TRAY_ICON_IMPLEMENTATIONS[wmClass]; + if (!role) { + // Unknown icons go first in undefined order + this._trayBox.prepend(icon, Big.BoxPackFlags.NONE); + } else { + icon._role = role; + // Figure out the index in our well-known order for this icon + let position = STANDARD_TRAY_ICON_ORDER.indexOf(role); + icon._rolePosition = position; + let children = this._trayBox.get_children(); + let i; + // Walk children backwards, until we find one that isn't + // well-known, or one where we should follow + for (i = children.length - 1; i >= 0; i--) { + let rolePosition = children[i]._rolePosition; + if (!rolePosition || position > rolePosition) { + this._trayBox.insert_after(icon, children[i], Big.BoxPackFlags.NONE); + break; + } + } + if (i == -1) { + // If we didn't find a position, we must be first + this._trayBox.prepend(icon, Big.BoxPackFlags.NONE); + } + } + + // Make sure the trayBox is shown. + this._trayBox.show(); + this._recomputeTraySize(); + }, + // By default, tray icons have a spacing of TRAY_SPACING. However this // starts to fail if we have too many as can sadly happen; just jump down // to a spacing of 8 if we're over 6. diff --git a/src/shell-tray-manager.c b/src/shell-tray-manager.c index 681fba3f9..9940461a3 100644 --- a/src/shell-tray-manager.c +++ b/src/shell-tray-manager.c @@ -4,11 +4,16 @@ #include #include +#include + +#include + #include "shell-tray-manager.h" #include "na-tray-manager.h" #include "shell-gtk-embed.h" #include "shell-embedded-window.h" +#include "shell-global.h" struct _ShellTrayManagerPrivate { NaTrayManager *na_manager; @@ -23,6 +28,7 @@ typedef struct { GtkWidget *socket; GtkWidget *window; ClutterActor *actor; + gboolean emitted_plugged; } ShellTrayManagerChild; enum { @@ -148,21 +154,22 @@ shell_tray_manager_class_init (ShellTrayManagerClass *klass) shell_tray_manager_signals[TRAY_ICON_ADDED] = g_signal_new ("tray-icon-added", - G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (ShellTrayManagerClass, tray_icon_added), - NULL, NULL, - g_cclosure_marshal_VOID__OBJECT, - G_TYPE_NONE, 1, - CLUTTER_TYPE_ACTOR); + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (ShellTrayManagerClass, tray_icon_added), + NULL, NULL, + gi_cclosure_marshal_generic, + G_TYPE_NONE, 2, + CLUTTER_TYPE_ACTOR, + G_TYPE_STRING); shell_tray_manager_signals[TRAY_ICON_REMOVED] = g_signal_new ("tray-icon-removed", - G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (ShellTrayManagerClass, tray_icon_removed), - NULL, NULL, - g_cclosure_marshal_VOID__OBJECT, - G_TYPE_NONE, 1, + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (ShellTrayManagerClass, tray_icon_removed), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, CLUTTER_TYPE_ACTOR); /* Lifting the CONSTRUCT_ONLY here isn't hard; you just need to @@ -265,6 +272,64 @@ shell_tray_manager_child_on_realize (GtkWidget *widget, } } +static char * +get_lowercase_wm_class_from_socket (ShellTrayManager *manager, + GtkSocket *socket) +{ + GdkWindow *window; + MetaScreen *screen; + MetaDisplay *display; + XClassHint class_hint; + gboolean success; + char *result; + + window = gtk_socket_get_plug_window (socket); + g_return_val_if_fail (window != NULL, NULL); + + screen = shell_global_get_screen (shell_global_get ()); + display = meta_screen_get_display (screen); + + gdk_error_trap_push (); + + success = XGetClassHint (meta_display_get_xdisplay (display), GDK_WINDOW_XWINDOW (window), &class_hint); + + gdk_error_trap_pop (); + + if (!success) + return NULL; + + result = g_ascii_strdown (class_hint.res_class, -1); + XFree (class_hint.res_name); + XFree (class_hint.res_class); + return result; +} + +static void +on_plug_added (GtkSocket *socket, + ShellTrayManager *manager) +{ + ShellTrayManagerChild *child; + char *wm_class; + + child = g_hash_table_lookup (manager->priv->icons, socket); + /* Only emit this signal once; the point of waiting until we + * get the first plugged notification is to be able to get the WM_CLASS + * from the child window. But we don't want to emit this signal twice + * if for some reason the socket gets replugged. + */ + if (child->emitted_plugged) + return; + child->emitted_plugged = TRUE; + + wm_class = get_lowercase_wm_class_from_socket (manager, socket); + if (!wm_class) + return; + + g_signal_emit (manager, shell_tray_manager_signals[TRAY_ICON_ADDED], 0, + child->actor, wm_class); + g_free (wm_class); +} + static void na_tray_icon_added (NaTrayManager *na_manager, GtkWidget *socket, gpointer user_data) @@ -289,7 +354,7 @@ na_tray_icon_added (NaTrayManager *na_manager, GtkWidget *socket, * the window we put it in match that as well */ gtk_widget_set_colormap (win, gtk_widget_get_colormap (socket)); - child = g_slice_new (ShellTrayManagerChild); + child = g_slice_new0 (ShellTrayManagerChild); child->manager = manager; child->window = win; child->socket = socket; @@ -304,9 +369,7 @@ na_tray_icon_added (NaTrayManager *na_manager, GtkWidget *socket, child->actor = g_object_ref (icon); g_hash_table_insert (manager->priv->icons, socket, child); - g_signal_emit (manager, - shell_tray_manager_signals[TRAY_ICON_ADDED], 0, - icon); + g_signal_connect (socket, "plug-added", G_CALLBACK (on_plug_added), manager); } static void diff --git a/src/shell-tray-manager.h b/src/shell-tray-manager.h index 7df38bb6f..d9916319a 100644 --- a/src/shell-tray-manager.h +++ b/src/shell-tray-manager.h @@ -30,7 +30,8 @@ struct _ShellTrayManagerClass GObjectClass parent_class; void (* tray_icon_added) (ShellTrayManager *manager, - ClutterActor *icon); + ClutterActor *icon, + const char *lowercase_wm_class); void (* tray_icon_removed) (ShellTrayManager *manager, ClutterActor *icon);