[gnome-shell] Port ShellStatusMenu to javascript



commit 985d7077884fa7670de906e2db31383fd28f73cf
Author: Dan Winship <danw gnome org>
Date:   Tue Nov 10 12:13:58 2009 -0500

    Port ShellStatusMenu to javascript
    
    https://bugzilla.gnome.org/show_bug.cgi?id=601458

 data/theme/gnome-shell.css |    7 +
 js/ui/Makefile.am          |    1 +
 js/ui/panel.js             |   12 +-
 js/ui/statusMenu.js        |  202 ++++++++++++
 src/Makefile.am            |    2 -
 src/shell-global.c         |   33 ++
 src/shell-global.h         |    4 +
 src/shell-status-menu.c    |  732 --------------------------------------------
 src/shell-status-menu.h    |   45 ---
 9 files changed, 253 insertions(+), 785 deletions(-)
---
diff --git a/data/theme/gnome-shell.css b/data/theme/gnome-shell.css
index 3e02884..351b4aa 100644
--- a/data/theme/gnome-shell.css
+++ b/data/theme/gnome-shell.css
@@ -348,3 +348,10 @@ StTooltip {
     width: 1px;
     background: rgba(255,255,255,0.33);
 }
+
+/* Status Menu */
+#StatusMenu {
+    spacing: 4px;
+    font: 16px sans-serif;
+    color: white;
+}
diff --git a/js/ui/Makefile.am b/js/ui/Makefile.am
index ebc7f53..a74fb86 100644
--- a/js/ui/Makefile.am
+++ b/js/ui/Makefile.am
@@ -23,6 +23,7 @@ dist_jsui_DATA =		\
 	runDialog.js		\
 	shellDBus.js		\
 	sidebar.js		\
+	statusMenu.js		\
 	tweener.js		\
 	widget.js		\
 	widgetBox.js		\
diff --git a/js/ui/panel.js b/js/ui/panel.js
index 447bb85..9e2fdb4 100644
--- a/js/ui/panel.js
+++ b/js/ui/panel.js
@@ -16,6 +16,7 @@ const _ = Gettext.gettext;
 const Button = imports.ui.button;
 const Calendar = imports.ui.calendar;
 const Main = imports.ui.main;
+const StatusMenu = imports.ui.statusMenu;
 
 const PANEL_HEIGHT = 26;
 const TRAY_HEIGHT = PANEL_HEIGHT - 1;
@@ -176,6 +177,8 @@ Panel.prototype = {
 
         this.actor = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL
                                  });
+        this.actor._delegate = this;
+
         let backgroundGradient = Shell.create_vertical_gradient(BACKGROUND_TOP,
                                                                 BACKGROUND_BOTTOM);
         this.actor.connect('notify::allocation', Lang.bind(this, function () {
@@ -371,11 +374,8 @@ Panel.prototype = {
         this._traymanager.manage_stage(global.stage);
 
         let statusbox = new Big.Box();
-        let statusmenu = this._statusmenu = new Shell.StatusMenu();
-        statusmenu.get_icon().hide();
-        statusmenu.get_name().fontName = DEFAULT_FONT;
-        statusmenu.get_name().color = PANEL_FOREGROUND_COLOR;
-        statusbox.append(this._statusmenu, Big.BoxPackFlags.NONE);
+        let statusmenu = this._statusmenu = new StatusMenu.StatusMenu();
+        statusbox.append(this._statusmenu.actor, Big.BoxPackFlags.NONE);
         let statusbutton = new Button.Button(statusbox,
                                              PANEL_BUTTON_COLOR,
                                              PRESSED_BUTTON_BACKGROUND_COLOR,
@@ -385,7 +385,7 @@ Panel.prototype = {
             if (e.get_button() == 1 && e.get_click_count() == 1) {
                 statusmenu.toggle(e);
                 // The statusmenu might not pop up if it couldn't get a pointer grab
-                if (statusmenu.is_active())
+                if (statusmenu.isActive())
                     statusbutton.actor.active = true;
                 return true;
             } else {
diff --git a/js/ui/statusMenu.js b/js/ui/statusMenu.js
new file mode 100644
index 0000000..a4de8bf
--- /dev/null
+++ b/js/ui/statusMenu.js
@@ -0,0 +1,202 @@
+/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
+
+const Gdm = imports.gi.Gdm;
+const GLib = imports.gi.GLib;
+const Gtk = imports.gi.Gtk;
+const Lang = imports.lang;
+const Shell = imports.gi.Shell;
+const St = imports.gi.St;
+const Signals = imports.signals;
+const Gettext = imports.gettext.domain('gnome-shell');
+const _ = Gettext.gettext;
+
+const Panel = imports.ui.panel;
+
+// Adapted from gdm/gui/user-switch-applet/applet.c
+//
+// Copyright (C) 2004-2005 James M. Cape <jcape ignore-your tv>.
+// Copyright (C) 2008,2009 Red Hat, Inc.
+
+const SIDEBAR_VISIBLE_KEY = 'sidebar/visible';
+
+function StatusMenu() {
+    this._init();
+}
+
+StatusMenu.prototype = {
+    _init: function() {
+        this._gdm = Gdm.UserManager.ref_default();
+        this._user = this._gdm.get_user(GLib.get_user_name());
+
+        this.actor = new St.BoxLayout({ name: 'StatusMenu' });
+        this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
+
+        this._name = new St.Label({ text: this._user.get_real_name() });
+        this.actor.add(this._name, { expand: true, y_align: St.Align.MIDDLE });
+        this._userNameChangedId = this._user.connect('notify::display-name', Lang.bind(this, this._updateUserName));
+
+        this._createSubMenu();
+        this._gdm.connect('users-loaded', Lang.bind(this, this._updateSwitchUser));
+        this._gdm.connect('user-added', Lang.bind(this, this._updateSwitchUser));
+        this._gdm.connect('user-removed', Lang.bind(this, this._updateSwitchUser));
+    },
+
+    _onDestroy: function() {
+        this._user.disconnect(this._userNameChangedId);
+    },
+
+    _updateUserName: function() {
+        this._name.set_text(this._user.get_real_name());
+    },
+
+    _updateSwitchUser: function() {
+        let users = this._gdm.list_users();
+        if (users.length > 1)
+            this._loginScreenItem.show();
+        else
+            this._loginScreenItem.hide();
+    },
+
+    // The menu
+
+    _createImageMenuItem: function(label, iconName) {
+        let image = new Gtk.Image();
+        let item = new Gtk.ImageMenuItem({ label: label,
+                                           image: image });
+        item.connect('style-set', Lang.bind(this,
+            function() {
+                image.set_from_icon_name(iconName, Gtk.IconSize.MENU);
+            }));
+
+        return item;
+    },
+
+    _createSubMenu: function() {
+        this._menu = new Gtk.Menu();
+        this._menu.connect('deactivate', Lang.bind(this, function() { this.emit('deactivated'); }));
+
+        let item;
+
+        item = this._createImageMenuItem(_('Account Information...'), 'user-info');
+        item.connect('activate', Lang.bind(this, this._onAccountInformationActivate));
+        this._menu.append(item);
+        item.show();
+
+        let gconf = Shell.GConf.get_default();
+        item = new Gtk.CheckMenuItem({ label: _('Sidebar'),
+                                       active: gconf.get_boolean(SIDEBAR_VISIBLE_KEY) });
+        item.connect('activate', Lang.bind(this,
+            function() {
+                gconf.set_boolean(SIDEBAR_VISIBLE_KEY, this._sidebarItem.active);
+            }));
+        this._menu.append(item);
+        item.show();
+
+        item = this._createImageMenuItem(_('System Preferences...'), 'preferences-desktop');
+        item.connect('activate', Lang.bind(this, this._onPreferencesActivate));
+        this._menu.append(item);
+        item.show();
+
+        item = new Gtk.SeparatorMenuItem();
+        this._menu.append(item);
+        item.show();
+
+        item = this._createImageMenuItem(_('Lock Screen'), 'system-lock-screen');
+        item.connect('activate', Lang.bind(this, this._onLockScreenActivate));
+        this._menu.append(item);
+        item.show();
+
+        item = this._createImageMenuItem(_('Switch User'), 'system-users');
+        item.connect('activate', Lang.bind(this, this._onLoginScreenActivate));
+        this._menu.append(item);
+        item.show();
+        this._loginScreenItem = item;
+
+        item = this._createImageMenuItem(_('Log Out...'), 'system-log-out');
+        item.connect('activate', Lang.bind(this, this._onQuitSessionActivate));
+        this._menu.append(item);
+        item.show();
+
+        item = this._createImageMenuItem(_('Shut Down...'), 'system-shutdown');
+        item.connect('activate', Lang.bind(this, this._onShutDownActivate));
+        this._menu.append(item);
+        item.show();
+    },
+
+    _onAccountInformationActivate: function() {
+        this._spawn(['gnome-about-me']);
+    },
+
+    _onPreferencesActivate: function() {
+        this._spawn(['gnome-control-center']);
+    },
+
+    _onLockScreenActivate: function() {
+        this._spawn(['gnome-screensaver-command', '--lock']);
+    },
+
+    _onLoginScreenActivate: function() {
+        this._gdm.goto_login_session();
+        this._onLockScreenActivate();
+    },
+
+    _onQuitSessionActivate: function() {
+        this._spawn(['gnome-session-save', '--logout-dialog']);
+    },
+
+    _onShutDownActivate: function() {
+        this._spawn(['gnome-session-save', '--shutdown-dialog']);
+    },
+
+    _spawn: function(args) {
+        // FIXME: once Shell.Process gets support for signalling
+        // errors we should pop up an error dialog or something here
+        // on failure
+        let p = new Shell.Process({'args' : args});
+        p.run();
+    },
+
+    // shell_status_menu_toggle:
+    // @event: event causing the toggle
+    //
+    // If the menu is not currently up, pops it up. Otherwise, hides it.
+    // Popping up may fail if another grab is already active; check with
+    // isActive().
+    toggle: function(event) {
+        if (this._menu.visible)
+            this._menu.popdown();
+        else {
+            // We don't want to overgrab a Mutter grab with the grab
+            // that GTK+ uses on menus.
+            if (global.display_is_grabbed())
+                return;
+
+            let [menuWidth, menuHeight] = this._menu.get_size_request ();
+
+            let panel;
+            for (panel = this.actor; panel; panel = panel.get_parent()) {
+                if (panel._delegate instanceof Panel.Panel)
+                    break;
+            }
+
+            let [panelX, panelY] = panel.get_transformed_position();
+            let [panelWidth, panelHeight] = panel.get_transformed_size();
+
+            let menuX = Math.round(panelX + panelWidth - menuWidth);
+            let menuY = Math.round(panelY + panelHeight);
+
+            Shell.popup_menu(this._menu, event.get_button(), event.get_time(),
+                             menuX, menuY);
+        }
+    },
+    
+    //  isActive:
+    //  
+    //  Gets whether the menu is currently popped up
+    //  
+    //  Return value: %true if the menu is currently popped up
+    isActive: function() {
+        return this._menu.visible;
+    }
+};
+Signals.addSignalMethods(StatusMenu.prototype);
diff --git a/src/Makefile.am b/src/Makefile.am
index 1970e85..4b11e83 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -87,8 +87,6 @@ libgnome_shell_la_SOURCES =			\
 	shell-global.c				\
 	shell-global.h				\
 	shell-global-private.h			\
-	shell-status-menu.c				\
-	shell-status-menu.h				\
 	shell-stack.c				\
 	shell-stack.h				\
 	shell-tray-manager.c			\
diff --git a/src/shell-global.c b/src/shell-global.c
index b07d090..5bb7e69 100644
--- a/src/shell-global.c
+++ b/src/shell-global.c
@@ -1026,3 +1026,36 @@ shell_get_event_state (ClutterEvent *event)
   ClutterModifierType state = clutter_event_get_state (event);
   return state & CLUTTER_MODIFIER_MASK;
 }
+
+static void
+shell_popup_menu_position_func (GtkMenu   *menu,
+                                int       *x,
+                                int       *y,
+                                gboolean  *push_in,
+                                gpointer   user_data)
+{
+  *x = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (menu), "shell-menu-x"));
+  *y = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (menu), "shell-menu-y"));
+}
+
+/**
+ * shell_popup_menu:
+ * @menu: a #GtkMenu
+ * @button: mouse button that triggered the menu
+ * @time: timestamp of event that triggered the menu
+ * @menu_x: x coordinate to display the menu at
+ * @menu_y: y coordinate to display the menu at
+ *
+ * Wraps gtk_menu_popup(), but using @menu_x, @menu_y for the location
+ * rather than needing a callback.
+ **/
+void
+shell_popup_menu (GtkMenu *menu, int button, guint32 time,
+                  int menu_x, int menu_y)
+{
+  g_object_set_data (G_OBJECT (menu), "shell-menu-x", GINT_TO_POINTER (menu_x));
+  g_object_set_data (G_OBJECT (menu), "shell-menu-y", GINT_TO_POINTER (menu_y));
+
+  gtk_menu_popup (menu, NULL, NULL, shell_popup_menu_position_func, NULL,
+                  button, time);
+}
diff --git a/src/shell-global.h b/src/shell-global.h
index 55844a3..60b9cb5 100644
--- a/src/shell-global.h
+++ b/src/shell-global.h
@@ -79,6 +79,10 @@ GdkModifierType shell_global_get_modifier_keys (ShellGlobal *global);
 
 ClutterModifierType shell_get_event_state (ClutterEvent *event);
 
+void shell_popup_menu (GtkMenu *menu, int button, guint32 time,
+                       int menu_x, int menu_y);
+
+
 G_END_DECLS
 
 #endif /* __SHELL_GLOBAL_H__ */



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