[gnome-shell] Add application menu area to panel



commit 74eac2187086b82b001f88bec64d779fd92ab63c
Author: Colin Walters <walters verbum org>
Date:   Tue Aug 11 11:32:58 2009 -0400

    Add application menu area to panel
    
    This is a start at the "Active Appliction Item" component of the
    shell design.  Currently we just show the currently focused
    application.  When launching a new application, we show that as well.
    
    The implementation here is not complete; basically when launching
    we de-focus the active one, and the application well shows the
    most recent startup sequence.
    
    This kind of fails in the case of multiple sequences, and we
    also don't correctly de-focus the current window in other
    launch paths.

 configure.ac            |    6 ++-
 js/ui/panel.js          |  108 +++++++++++++++++++++++++++++++++++++++++++++
 src/shell-app-monitor.c |  111 +++++++++++++++++++++++++++++++++++++++++++++++
 src/shell-app-monitor.h |   12 +++++
 src/shell-app-system.c  |    9 +++-
 5 files changed, 244 insertions(+), 2 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 06b5d4b..a93de64 100644
--- a/configure.ac
+++ b/configure.ac
@@ -43,7 +43,11 @@ fi
 
 AM_CONDITIONAL(BUILD_RECORDER, $build_recorder)
 
-PKG_CHECK_MODULES(MUTTER_PLUGIN, gio-unix-2.0 gtk+-2.0 dbus-glib-1 mutter-plugins gjs-gi-1.0 libgnome-menu $recorder_modules gconf-2.0 gdk-x11-2.0 clutter-x11-1.0 clutter-glx-1.0 gnome-desktop-2.0 >= 2.26)
+# Collect more than 20 libraries for a prize!
+PKG_CHECK_MODULES(MUTTER_PLUGIN, gio-unix-2.0 gtk+-2.0 dbus-glib-1 mutter-plugins
+                                 gjs-gi-1.0 libgnome-menu $recorder_modules gconf-2.0
+                                 gdk-x11-2.0 clutter-x11-1.0 clutter-glx-1.0
+                                 gnome-desktop-2.0 >= 2.26 libstartup-notification-1.0)
 PKG_CHECK_MODULES(TIDY, clutter-1.0)
 PKG_CHECK_MODULES(BIG, clutter-1.0 gtk+-2.0 librsvg-2.0)
 PKG_CHECK_MODULES(GDMUSER, dbus-glib-1 gtk+-2.0)
diff --git a/js/ui/panel.js b/js/ui/panel.js
index 16d3f68..dda10ab 100644
--- a/js/ui/panel.js
+++ b/js/ui/panel.js
@@ -8,6 +8,7 @@ const Mainloop = imports.mainloop;
 const Meta = imports.gi.Meta;
 const Shell = imports.gi.Shell;
 const Tweener = imports.ui.tweener;
+const Signals = imports.signals;
 
 const Button = imports.ui.button;
 const Main = imports.ui.main;
@@ -17,10 +18,14 @@ const TRAY_HEIGHT = PANEL_HEIGHT - 1;
 
 const DEFAULT_PADDING = 4;
 
+const PANEL_ICON_SIZE = 24;
+
 const PANEL_BACKGROUND_COLOR = new Clutter.Color();
 PANEL_BACKGROUND_COLOR.from_pixel(0x000000ff);
 const PANEL_FOREGROUND_COLOR = new Clutter.Color();
 PANEL_FOREGROUND_COLOR.from_pixel(0xffffffff);
+const SN_BACKGROUND_COLOR = new Clutter.Color();
+SN_BACKGROUND_COLOR.from_pixel(0xffff00a0);
 
 const TRANSPARENT_COLOR = new Clutter.Color();
 TRANSPARENT_COLOR.from_pixel(0x00000000);
@@ -49,6 +54,105 @@ TRAY_BORDER_COLOR.from_pixel(0x00000033);
 const TRAY_CORNER_RADIUS = 5;
 const TRAY_BORDER_WIDTH = 0;
 
+
+function AppPanelMenu() {
+    this._init();
+}
+
+AppPanelMenu.prototype = {
+    _init: function() {
+        this._metaDisplay = Shell.Global.get().screen.get_display();
+
+        this._focusedApp = null;
+        this._activeSequence = null;
+        this._startupSequences = {};
+
+        this.actor = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
+                                   spacing: DEFAULT_PADDING,
+                                   y_align: Big.BoxAlignment.CENTER });
+        this._iconBox = new Big.Box({ width: PANEL_ICON_SIZE, height: PANEL_ICON_SIZE,
+                                      x_align: Big.BoxAlignment.CENTER,
+                                      y_align: Big.BoxAlignment.CENTER });
+        this.actor.append(this._iconBox, Big.BoxPackFlags.NONE);
+        let labelBox = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
+                                     y_align: Big.BoxAlignment.CENTER });
+        this._label = new Clutter.Text({ font_name: DEFAULT_FONT,
+                                         color: PANEL_FOREGROUND_COLOR,
+                                         text: "" });
+        labelBox.append(this._label, Big.BoxPackFlags.EXPAND);
+        this.actor.append(labelBox, Big.BoxPackFlags.NONE);
+
+        this._startupBox = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
+                                         y_align: Big.BoxAlignment.CENTER
+                                          });
+        this.actor.append(this._startupBox, Big.BoxPackFlags.NONE);
+
+        Main.overview.connect('hiding', Lang.bind(this, function () {
+            this.actor.opacity = 255;
+        }));
+        Main.overview.connect('showing', Lang.bind(this, function () {
+            this.actor.opacity = 192;
+        }));
+
+        this._metaDisplay.connect('notify::focus-window', Lang.bind(this, function () {
+            this._sync();
+        }));
+        Shell.AppMonitor.get_default().connect('startup-sequence-changed', Lang.bind(this, function() {
+            this._sync();
+        }));
+        this._sync();
+    },
+
+    _sync: function() {
+        let appMonitor = Shell.AppMonitor.get_default();
+
+        let focusWindow = this._metaDisplay.get_focus_window();
+        let focusedApp;
+        if (focusWindow == null) {
+           focusedApp = null;
+        } else {
+           focusedApp = appMonitor.get_window_app(focusWindow);
+        }
+
+        let lastSequence = null;
+        if (focusedApp == null) {
+            let sequences = appMonitor.get_startup_sequences();
+            if (sequences.length > 0)
+                lastSequence = sequences[sequences.length - 1];
+        }
+
+        // If the currently focused app hasn't changed and the current
+        // startup sequence hasn't changed, we have nothing to do
+        if (focusedApp == this._focusedApp
+            && ((lastSequence == null && this._activeSequence == null)
+                || (lastSequence != null && this._activeSequence != null
+                    && lastSequence.get_id() == this._activeSequence.get_id())))
+            return;
+
+        this._focusedApp = focusedApp;
+        this._activeSequence = lastSequence;
+
+        this._iconBox.remove_all();
+        this._iconBox.hide();
+        this._label.text = '';
+        if (this._focusedApp != null) {
+            let icon = focusedApp.create_icon_texture(PANEL_ICON_SIZE);
+            this._iconBox.append(icon, Big.BoxPackFlags.NONE);
+            this._iconBox.show();
+            this._label.text = focusedApp.get_name();
+        } else if (this._activeSequence != null) {
+            let icon = this._activeSequence.create_icon(PANEL_ICON_SIZE);
+            this._iconBox.append(icon, Big.BoxPackFlags.NONE);
+            this._iconBox.show();
+            this._label.text = this._activeSequence.get_name();
+        }
+
+        this.emit('changed');
+    }
+}
+
+Signals.addSignalMethods(AppPanelMenu.prototype);
+
 function Panel() {
     this._init();
 }
@@ -62,6 +166,7 @@ Panel.prototype = {
                                  });
         this._leftBox = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
                                       y_align: Big.BoxAlignment.CENTER,
+                                      spacing: DEFAULT_PADDING,
                                       padding_right: DEFAULT_PADDING });
         this._centerBox = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
                                         y_align: Big.BoxAlignment.CENTER });
@@ -166,6 +271,9 @@ Panel.prototype = {
 
         this._leftBox.append(hotCorner, Big.BoxPackFlags.FIXED);
 
+        let appMenu = new AppPanelMenu();
+        this._leftBox.append(appMenu.actor, Big.BoxPackFlags.NONE);
+
         /* center */
 
         this._clock = new Clutter.Text({ font_name: DEFAULT_FONT,
diff --git a/src/shell-app-monitor.c b/src/shell-app-monitor.c
index b4d61b5..789dd14 100644
--- a/src/shell-app-monitor.c
+++ b/src/shell-app-monitor.c
@@ -11,6 +11,10 @@
 #include <gconf/gconf-client.h>
 #include <dbus/dbus-glib.h>
 
+#define SN_API_NOT_YET_FROZEN 1
+#include <libsn/sn.h>
+
+#include "shell-texture-cache.h"
 #include "shell-app-monitor.h"
 #include "shell-app-system.h"
 #include "shell-global.h"
@@ -151,6 +155,7 @@ struct AppUsage
 
 enum {
   CHANGED,
+  STARTUP_SEQUENCE_CHANGED,
 
   LAST_SIGNAL
 };
@@ -198,6 +203,14 @@ static void shell_app_monitor_class_init(ShellAppMonitorClass *klass)
                                    NULL, NULL,
                                    g_cclosure_marshal_VOID__VOID,
                                    G_TYPE_NONE, 0);
+
+  signals[STARTUP_SEQUENCE_CHANGED] = g_signal_new ("startup-sequence-changed",
+                                   SHELL_TYPE_APP_MONITOR,
+                                   G_SIGNAL_RUN_LAST,
+                                   0,
+                                   NULL, NULL,
+                                   g_cclosure_marshal_VOID__BOXED,
+                                   G_TYPE_NONE, 1, SHELL_TYPE_STARTUP_SEQUENCE);
 }
 
 static void
@@ -695,8 +708,18 @@ init_window_monitoring (ShellAppMonitor *self)
 }
 
 static void
+on_startup_sequence_changed (MetaScreen            *screen,
+                             SnStartupSequence     *sequence,
+                             ShellAppMonitor       *self)
+{
+  /* Just proxy the signal */
+  g_signal_emit (G_OBJECT (self), signals[STARTUP_SEQUENCE_CHANGED], 0, sequence);
+}
+
+static void
 shell_app_monitor_init (ShellAppMonitor *self)
 {
+  MetaScreen *screen;
   GdkDisplay *display;
   char *path;
   char *shell_config_dir;
@@ -704,6 +727,7 @@ shell_app_monitor_init (ShellAppMonitor *self)
 
   /* FIXME: should we create as many monitors as there are GdkScreens? */
   display = gdk_display_get_default ();
+  screen = shell_global_get_screen (shell_global_get ());
 
   session_bus = dbus_g_bus_get (DBUS_BUS_SESSION, NULL);
   self->session_proxy = dbus_g_proxy_new_for_name (session_bus, "org.gnome.SessionManager",
@@ -735,6 +759,9 @@ shell_app_monitor_init (ShellAppMonitor *self)
   load_initial_windows (self);
   init_window_monitoring (self);
 
+  g_signal_connect (G_OBJECT (screen), "startup-sequence-changed",
+                    G_CALLBACK (on_startup_sequence_changed), self);
+
   self->gconf_client = gconf_client_get_default ();
   gconf_client_add_dir (self->gconf_client, APP_MONITOR_GCONF_DIR,
                         GCONF_CLIENT_PRELOAD_NONE, NULL);
@@ -1339,6 +1366,90 @@ on_enable_monitoring_key_changed (GConfClient *client,
   update_enable_monitoring ((ShellAppMonitor *) monitor);
 }
 
+
+/**
+ * shell_app_monitor_get_startup_sequences:
+ * @self:
+ *
+ * Returns: (transfer none) (element-type ShellStartupSequence): Currently active startup sequences
+ */
+GSList *
+shell_app_monitor_get_startup_sequences (ShellAppMonitor *self)
+{
+  ShellGlobal *global = shell_global_get ();
+  MetaScreen *screen = shell_global_get_screen (global);
+  return meta_screen_get_startup_sequences (screen);
+}
+
+/* sn_startup_sequence_ref returns void, so make a
+ * wrapper which returns self */
+SnStartupSequence *
+sequence_ref (SnStartupSequence *sequence)
+{
+  sn_startup_sequence_ref (sequence);
+  return sequence;
+}
+
+GType
+shell_startup_sequence_get_type (void)
+{
+  static GType gtype = G_TYPE_INVALID;
+  if (gtype == G_TYPE_INVALID)
+    {
+      gtype = g_boxed_type_register_static ("ShellStartupSequence",
+          (GBoxedCopyFunc)sequence_ref,
+          (GBoxedFreeFunc)sn_startup_sequence_unref);
+    }
+  return gtype;
+}
+
+const char *
+shell_startup_sequence_get_id (ShellStartupSequence *sequence)
+{
+  return sn_startup_sequence_get_id ((SnStartupSequence*)sequence);
+}
+
+const char *
+shell_startup_sequence_get_name (ShellStartupSequence *sequence)
+{
+  return sn_startup_sequence_get_name ((SnStartupSequence*)sequence);
+}
+
+gboolean
+shell_startup_sequence_get_completed (ShellStartupSequence *sequence)
+{
+  return sn_startup_sequence_get_completed ((SnStartupSequence*)sequence);
+}
+
+/**
+ * shell_startup_sequence_create_icon:
+ * @sequence:
+ * @size: Size in pixels of icon
+ *
+ * Returns: (transfer none): A new #ClutterTexture containing an icon for the sequence
+ */
+ClutterActor *
+shell_startup_sequence_create_icon (ShellStartupSequence *sequence, guint size)
+{
+  GThemedIcon *themed;
+  const char *icon_name;
+  ClutterActor *texture;
+
+  icon_name = sn_startup_sequence_get_icon_name ((SnStartupSequence*)sequence);
+  if (!icon_name)
+    {
+      texture = clutter_texture_new ();
+      clutter_actor_set_size (texture, size, size);
+      return texture;
+    }
+
+  themed = (GThemedIcon*)g_themed_icon_new (icon_name);
+  texture = shell_texture_cache_load_gicon (shell_texture_cache_get_default (),
+                                            G_ICON (themed), size);
+  g_object_unref (G_OBJECT (themed));
+  return texture;
+}
+
 /**
  * shell_app_monitor_get_default:
  *
diff --git a/src/shell-app-monitor.h b/src/shell-app-monitor.h
index 41f7129..491f860 100644
--- a/src/shell-app-monitor.h
+++ b/src/shell-app-monitor.h
@@ -49,6 +49,18 @@ GSList *shell_app_monitor_get_windows_for_app (ShellAppMonitor *monitor, const c
 /* Get whatever's running right now */
 GSList *shell_app_monitor_get_running_app_ids (ShellAppMonitor *monitor, const char *context);
 
+GSList *shell_app_monitor_get_startup_sequences (ShellAppMonitor *monitor);
+
+/* Hidden typedef for SnStartupSequence */
+typedef struct _ShellStartupSequence ShellStartupSequence;
+#define SHELL_TYPE_STARTUP_SEQUENCE (shell_startup_sequence_get_type ())
+GType shell_startup_sequence_get_type (void);
+
+const char *shell_startup_sequence_get_id (ShellStartupSequence *sequence);
+const char *shell_startup_sequence_get_name (ShellStartupSequence *sequence);
+gboolean shell_startup_sequence_get_completed (ShellStartupSequence *sequence);
+ClutterActor *shell_startup_sequence_create_icon (ShellStartupSequence *sequence, guint size);
+
 G_END_DECLS
 
 #endif /* __SHELL_APP_MONITOR_H__ */
diff --git a/src/shell-app-system.c b/src/shell-app-system.c
index dc2f53b..d415f18 100644
--- a/src/shell-app-system.c
+++ b/src/shell-app-system.c
@@ -8,6 +8,7 @@
 #include <gtk/gtk.h>
 #include <gconf/gconf.h>
 #include <gconf/gconf-client.h>
+#include <clutter/clutter.h>
 
 #include "shell-global.h"
 #include "shell-texture-cache.h"
@@ -922,7 +923,13 @@ shell_app_info_launch_full (ShellAppInfo *info,
   display = meta_screen_get_display (screen);
 
   if (timestamp == 0)
-    timestamp = meta_display_get_current_time (display);
+    timestamp = clutter_get_current_event_time ();
+
+  /* Shell design calls for on application launch, no window is focused,
+   * and we have startup notification displayed.
+   */
+  meta_display_focus_the_no_focus_window (display, screen, timestamp);
+
   if (workspace < 0)
     workspace = meta_screen_get_active_workspace_index (screen);
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]