[gnome-shell/wip/gdm-shell: 8/8] wip: add very rough initial cut at gdm mode (needs-split)



commit 63a4a800cdf9ff136fa4201893e3f84869693308
Author: Ray Strode <rstrode redhat com>
Date:   Mon Jun 27 17:20:38 2011 -0400

    wip: add very rough initial cut at gdm mode (needs-split)
    
    This is just my in-progress code sketch pushed to a branch so
    it's backed up.

 data/Makefile.am                |    1 +
 data/theme/gnome-shell.css      |  112 +++++
 data/theme/list-item-border.svg |   75 +++
 js/Makefile.am                  |    2 +
 js/ui/calendar.js               |   20 +-
 js/ui/chrome.js                 |   10 +-
 js/ui/ctrlAltTab.js             |    2 +-
 js/ui/dateMenu.js               |   76 ++--
 js/ui/dialog.js                 |  224 +++++++++
 js/ui/loginDialog.js            | 1014 +++++++++++++++++++++++++++++++++++++++
 js/ui/main.js                   |  131 ++++--
 js/ui/messageTray.js            |   68 ++--
 js/ui/notificationDaemon.js     |    8 +-
 js/ui/panel.js                  |  216 +++++----
 js/ui/status/accessibility.js   |   14 +-
 js/ui/status/keyboard.js        |   22 +-
 js/ui/status/power.js           |   13 +-
 js/ui/status/volume.js          |   14 +-
 js/ui/windowManager.js          |   32 +-
 src/st/st-adjustment.c          |    9 +
 20 files changed, 1811 insertions(+), 252 deletions(-)
---
diff --git a/data/Makefile.am b/data/Makefile.am
index 69fb40d..e7bdb86 100644
--- a/data/Makefile.am
+++ b/data/Makefile.am
@@ -30,6 +30,7 @@ dist_theme_DATA =				\
 	theme/filter-selected-ltr.svg		\
 	theme/filter-selected-rtl.svg		\
 	theme/gnome-shell.css			\
+	theme/list-item-border.svg		\
 	theme/panel-border.svg			\
 	theme/panel-button-border.svg		\
 	theme/panel-button-highlight-narrow.svg	\
diff --git a/data/theme/gnome-shell.css b/data/theme/gnome-shell.css
index d193b17..683331c 100644
--- a/data/theme/gnome-shell.css
+++ b/data/theme/gnome-shell.css
@@ -1839,3 +1839,115 @@ StTooltip StLabel {
 .magnifier-zoom-region.full-screen {
     border-width: 0px;
 }
+/* Copyright 2011, Red Hat, Inc.
+ *
+ * Portions adapted from Mx's data/style/default.css
+ *   Copyright 2009 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+.login-dialog-title {
+    font-size: 14pt;
+    font-weight: bold;
+    color: #666666;
+    padding-bottom: 2em;
+}
+
+.login-dialog {
+    border-radius: 16px;
+    min-height: 150px;
+    max-height: 700px;
+    min-width: 350px;
+}
+
+.login-dialog-user-list-view {
+    -st-vfade-offset: 1em;
+}
+
+.login-dialog-user-list {
+    spacing: 12px;
+}
+
+.login-dialog-user-list:rtl {
+}
+
+.login-dialog-user-list-item {
+    color: #666666;
+    padding-bottom: 6px;
+}
+
+.login-dialog-user-list-item:focus {
+    border-image: url("list-item-border.svg") 0 0 2 0;
+    padding-bottom: 4px;
+    border-color: #666666;
+    border-bottom: 2px;
+}
+
+.login-dialog-user-list-item:ltr {
+    padding-right: 1em;
+}
+
+.login-dialog-user-list-item:rtl {
+    padding-left: 1em;
+}
+
+.login-dialog-user-list-item .login-dialog-user-list-item-name {
+    font-size: 20pt;
+    padding-left: 1em;
+    color: #666666;
+}
+
+.login-dialog-user-list-item:hover .login-dialog-user-list-item-name {
+    color: white;
+}
+
+.login-dialog-user-list-item:focus .login-dialog-user-list-item-name {
+    color: white;
+    text-shadow: black 0px 2px 2px;
+}
+
+.login-dialog-user-list-item-icon {
+    border: 2px solid #8b8b8b;
+    border-radius: 8px;
+    width: 64px;
+    height: 64px;
+}
+
+.login-dialog-not-listed-button {
+    padding-top: 2em;
+}
+.login-dialog-not-listed-label {
+    font-size: 14pt;
+    font-weight: bold;
+    color: #666666;
+}
+
+.login-dialog-prompt-layout {
+    padding-bottom: 64px;
+}
+.login-dialog-prompt-label {
+    color: white;
+    font-size: 20pt;
+}
+
+.login-dialog-prompt-entry {
+    padding: 4px;
+    border-radius: 4px;
+    border: 2px solid #5656cc;
+    color: black;
+    background-color: white;
+    caret-color: black;
+    caret-size: 1px;
+}
diff --git a/data/theme/list-item-border.svg b/data/theme/list-item-border.svg
new file mode 100644
index 0000000..f380327
--- /dev/null
+++ b/data/theme/list-item-border.svg
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/";
+   xmlns:cc="http://creativecommons.org/ns#";
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#";
+   xmlns:svg="http://www.w3.org/2000/svg";
+   xmlns="http://www.w3.org/2000/svg";
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd";
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape";
+   width="10"
+   height="70"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.48.1 r9760"
+   sodipodi:docname="list-item-border.svg">
+  <sodipodi:namedview
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1"
+     objecttolerance="10"
+     gridtolerance="10"
+     guidetolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:window-width="1440"
+     inkscape:window-height="850"
+     id="namedview8"
+     showgrid="false"
+     inkscape:zoom="22.627417"
+     inkscape:cx="14.025478"
+     inkscape:cy="3.5810836"
+     inkscape:window-x="0"
+     inkscape:window-y="26"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="layer1" />
+  <defs
+     id="defs4">
+    <linearGradient
+       id="linearGradient3777">
+      <stop
+         style="stop-color:#ffff00;stop-opacity:1;"
+         offset="0"
+         id="stop3779" />
+      <stop
+         style="stop-color:#18cc00;stop-opacity:1;"
+         offset="1"
+         id="stop3781" />
+    </linearGradient>
+  </defs>
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage"; />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     id="layer1"
+     transform="translate(0,60)">
+    <rect
+       style="fill:#666666;fill-opacity:1;stroke-width:0.43599999;stroke-miterlimit:4;stroke-dasharray:none"
+       id="rect3796"
+       width="2"
+       height="8"
+       x="-10"
+       y="1"
+       transform="matrix(0,-1,1,0,0,0)" />
+  </g>
+</svg>
diff --git a/js/Makefile.am b/js/Makefile.am
index 72ad54b..ab19d71 100644
--- a/js/Makefile.am
+++ b/js/Makefile.am
@@ -25,6 +25,7 @@ nobase_dist_js_DATA = 	\
 	ui/ctrlAltTab.js	\
 	ui/dash.js		\
 	ui/dateMenu.js		\
+	ui/dialog.js		\
 	ui/dnd.js		\
 	ui/docDisplay.js	\
 	ui/endSessionDialog.js	\
@@ -34,6 +35,7 @@ nobase_dist_js_DATA = 	\
 	ui/layout.js		\
 	ui/lightbox.js		\
 	ui/link.js		\
+	ui/loginDialog.js	\
 	ui/lookingGlass.js	\
 	ui/magnifier.js		\
 	ui/magnifierDBus.js	\
diff --git a/js/ui/calendar.js b/js/ui/calendar.js
index 0f87305..0220f50 100644
--- a/js/ui/calendar.js
+++ b/js/ui/calendar.js
@@ -351,12 +351,14 @@ function Calendar(eventSource) {
 
 Calendar.prototype = {
     _init: function(eventSource) {
-        this._eventSource = eventSource;
+        if (eventSource) {
+            this._eventSource = eventSource;
 
-        this._eventSource.connect('changed', Lang.bind(this,
-                                                       function() {
-                                                           this._update(false);
-                                                       }));
+            this._eventSource.connect('changed', Lang.bind(this,
+                                                           function() {
+                                                               this._update(false);
+                                                           }));
+        }
 
         // FIXME: This is actually the fallback method for GTK+ for the week start;
         // GTK+ by preference uses nl_langinfo (NL_TIME_FIRST_WEEKDAY). We probably
@@ -573,7 +575,7 @@ Calendar.prototype = {
                 this.setDate(newlySelectedDate, false);
             }));
 
-            let hasEvents = this._eventSource.hasEvents(iter);
+            let hasEvents = this._eventSource && this._eventSource.hasEvents(iter);
             let styleClass = 'calendar-day-base calendar-day';
             if (_isWorkDay(iter))
                 styleClass += ' calendar-work-day'
@@ -620,7 +622,8 @@ Calendar.prototype = {
         }
         // Signal to the event source that we are interested in events
         // only from this date range
-        this._eventSource.requestRange(beginDate, iter, forceReload);
+        if (this._eventSource)
+            this._eventSource.requestRange(beginDate, iter, forceReload);
     }
 };
 
@@ -667,6 +670,9 @@ EventsList.prototype = {
     },
 
     _addPeriod: function(header, begin, end, includeDayName, showNothingScheduled) {
+        if (!this._eventSource)
+            return;
+
         let events = this._eventSource.getEvents(begin, end);
 
         let clockFormat = this._desktopSettings.get_string(CLOCK_FORMAT_KEY);;
diff --git a/js/ui/chrome.js b/js/ui/chrome.js
index f2a7d14..2f2c21d 100644
--- a/js/ui/chrome.js
+++ b/js/ui/chrome.js
@@ -44,10 +44,12 @@ Chrome.prototype = {
         global.screen.connect('notify::n-workspaces',
                               Lang.bind(this, this._queueUpdateRegions));
 
-        Main.overview.connect('showing',
-                             Lang.bind(this, this._overviewShowing));
-        Main.overview.connect('hidden',
-                             Lang.bind(this, this._overviewHidden));
+        if (Main.overview) {
+            Main.overview.connect('showing',
+                                 Lang.bind(this, this._overviewShowing));
+            Main.overview.connect('hidden',
+                                 Lang.bind(this, this._overviewHidden));
+        }
 
         this._relayout();
     },
diff --git a/js/ui/ctrlAltTab.js b/js/ui/ctrlAltTab.js
index d0845a3..5412b7c 100644
--- a/js/ui/ctrlAltTab.js
+++ b/js/ui/ctrlAltTab.js
@@ -99,7 +99,7 @@ CtrlAltTabManager.prototype = {
         let items = this._items.filter(function (item) { return item.proxy.mapped; });
 
         // And add the windows metacity would show in its Ctrl-Alt-Tab list
-        if (!Main.overview.visible) {
+        if (!Main.overview || !Main.overview.visible) {
             let screen = global.screen;
             let display = screen.get_display();
             let windows = display.get_tab_list(Meta.TabList.DOCKS, screen, screen.get_active_workspace ());
diff --git a/js/ui/dateMenu.js b/js/ui/dateMenu.js
index 41f56f0..30c474d 100644
--- a/js/ui/dateMenu.js
+++ b/js/ui/dateMenu.js
@@ -50,7 +50,9 @@ DateMenuButton.prototype = {
         let hbox;
         let vbox;
 
-        this._eventSource = new Calendar.DBusEventSource();
+        if (global.session_type == Shell.SessionType.USER) {
+            this._eventSource = new Calendar.DBusEventSource();
+        }
 
         let menuAlignment = 0.25;
         if (St.Widget.get_default_direction() == St.TextDirection.RTL)
@@ -73,43 +75,51 @@ DateMenuButton.prototype = {
         this._date.style_class = 'datemenu-date-label';
         vbox.add(this._date);
 
-        this._eventList = new Calendar.EventsList(this._eventSource);
+        if (this._eventSource) {
+            this._eventList = new Calendar.EventsList(this._eventSource);
+        }
 
         // Calendar
         this._calendar = new Calendar.Calendar(this._eventSource);
-        this._calendar.connect('selected-date-changed',
-                               Lang.bind(this, function(calendar, date) {
-                                   this._eventList.setDate(date);
-                               }));
+        if (this._eventList) {
+            this._calendar.connect('selected-date-changed',
+                                   Lang.bind(this, function(calendar, date) {
+                                       this._eventList.setDate(date);
+                                   }));
+        }
         vbox.add(this._calendar.actor);
 
-        item = new PopupMenu.PopupSeparatorMenuItem();
-        item.setColumnWidths(1);
-        vbox.add(item.actor, {y_align: St.Align.END, expand: true, y_fill: false});
-        item = new PopupMenu.PopupMenuItem(_("Date and Time Settings"));
-        item.connect('activate', Lang.bind(this, this._onPreferencesActivate));
-        item.actor.can_focus = false;
-        vbox.add(item.actor);
-
-        // Add vertical separator
-
-        item = new St.DrawingArea({ style_class: 'calendar-vertical-separator',
-                                    pseudo_class: 'highlighted' });
-        item.connect('repaint', Lang.bind(this, _onVertSepRepaint));
-        hbox.add(item);
-
-        // Fill up the second column
-        vbox = new St.BoxLayout({ name:     'calendarEventsArea',
-                                  vertical: true});
-        hbox.add(vbox, { expand: true });
-
-        // Event list
-        vbox.add(this._eventList.actor, { expand: true });
-
-        item = new PopupMenu.PopupMenuItem(_("Open Calendar"));
-        item.connect('activate', Lang.bind(this, this._onOpenCalendarActivate));
-        item.actor.can_focus = false;
-        vbox.add(item.actor, {y_align: St.Align.END, expand: true, y_fill: false});
+        if (global.session_type == Shell.SessionType.USER) {
+            item = new PopupMenu.PopupSeparatorMenuItem();
+            item.setColumnWidths(1);
+            vbox.add(item.actor, {y_align: St.Align.END, expand: true, y_fill: false});
+            item = new PopupMenu.PopupMenuItem(_("Date and Time Settings"));
+            item.connect('activate', Lang.bind(this, this._onPreferencesActivate));
+            item.actor.can_focus = false;
+            vbox.add(item.actor);
+
+            // Add vertical separator
+
+            item = new St.DrawingArea({ style_class: 'calendar-vertical-separator',
+                                        pseudo_class: 'highlighted' });
+            item.connect('repaint', Lang.bind(this, _onVertSepRepaint));
+            hbox.add(item);
+
+            // Fill up the second column
+            vbox = new St.BoxLayout({ name:     'calendarEventsArea',
+                                      vertical: true});
+            hbox.add(vbox, { expand: true });
+
+            // Event list
+            if (this._eventList) {
+                vbox.add(this._eventList.actor, { expand: true });
+            }
+
+            item = new PopupMenu.PopupMenuItem(_("Open Calendar"));
+            item.connect('activate', Lang.bind(this, this._onOpenCalendarActivate));
+            item.actor.can_focus = false;
+            vbox.add(item.actor, {y_align: St.Align.END, expand: true, y_fill: false});
+        }
 
         // Whenever the menu is opened, select today
         this.menu.connect('open-state-changed', Lang.bind(this, function(menu, isOpen) {
diff --git a/js/ui/dialog.js b/js/ui/dialog.js
new file mode 100644
index 0000000..8a103a4
--- /dev/null
+++ b/js/ui/dialog.js
@@ -0,0 +1,224 @@
+/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
+
+const Clutter = imports.gi.Clutter;
+const Gdk = imports.gi.Gdk;
+const Gio = imports.gi.Gio;
+const GLib = imports.gi.GLib;
+const Lang = imports.lang;
+const Meta = imports.gi.Meta;
+const Pango = imports.gi.Pango;
+const St = imports.gi.St;
+const Shell = imports.gi.Shell;
+const Signals = imports.signals;
+const Gettext = imports.gettext.domain('gnome-shell');
+const _ = Gettext.gettext;
+
+const Params = imports.misc.params;
+
+const Lightbox = imports.ui.lightbox;
+const Main = imports.ui.main;
+const Tweener = imports.ui.tweener;
+
+const OPEN_AND_CLOSE_TIME = 0.1;
+const FADE_OUT_DIALOG_TIME = 1.0;
+
+const State = {
+    OPENED: 0,
+    CLOSED: 1,
+    OPENING: 2,
+    CLOSING: 3,
+    FADED_OUT: 4
+};
+
+function Dialog() {
+    this._init();
+}
+
+Dialog.prototype = {
+    _init: function(params) {
+        params = Params.parse(params, { lowerBelowChrome: null,
+                                        styleClass: null });
+
+        this.state = State.CLOSED;
+
+        this._group = new St.Group({ visible: false,
+                                     x: 0,
+                                     y: 0 });
+        Main.uiGroup.add_actor(this._group);
+
+        if (params.lowerBelowChrome)
+            this._group.lower(Main.chrome.actor);
+
+        let constraint = new Clutter.BindConstraint({ source: global.stage,
+                                                      coordinate: Clutter.BindCoordinate.POSITION | Clutter.BindCoordinate.SIZE });
+        this._group.add_constraint(constraint);
+
+        this._group.connect('destroy', Lang.bind(this, this._onGroupDestroy));
+
+        this._actionKeys = {};
+        this._group.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent));
+
+        this._backgroundBin = new St.Bin();
+        this._group.add_actor(this._backgroundBin);
+
+        this._dialogLayout = new St.BoxLayout({ style_class: 'modal-dialog',
+                                                reactive:     true,
+                                                can_focus:    true,
+                                                vertical:     true });
+        if (params.styleClass != null) {
+            this._dialogLayout.add_style_class_name(params.styleClass);
+        }
+
+        this._backgroundBin.add_actor(this._dialogLayout);
+
+        this.contentLayout = new St.BoxLayout({ vertical: true });
+        this._dialogLayout.add(this.contentLayout,
+                               { x_fill:  true,
+                                 y_fill:  true,
+                                 x_align: St.Align.MIDDLE,
+                                 y_align: St.Align.START });
+
+        this._buttonLayout = new St.BoxLayout({ style_class: 'modal-dialog-button-box',
+                                                opacity:     220,
+                                                vertical:    false });
+        this._dialogLayout.add(this._buttonLayout,
+                               { expand:  true,
+                                 x_align: St.Align.MIDDLE,
+                                 y_align: St.Align.END });
+
+        global.focus_manager.add_group(this._dialogLayout);
+        this._initialKeyFocus = this._dialogLayout;
+        this._savedKeyFocus = null;
+    },
+
+    setButtons: function(buttons) {
+        this._actionKeys = {};
+
+        let hadChildren = this._buttonLayout.get_children() > 0;
+
+        this._buttonLayout.destroy_children();
+
+        let i = 0;
+        for (let index in buttons) {
+            let buttonInfo = buttons[index];
+            let label = buttonInfo['label'];
+            let action = buttonInfo['action'];
+            let key = buttonInfo['key'];
+
+            let button = new St.Button({ style_class: 'modal-dialog-button',
+                                         reactive:    true,
+                                         can_focus:   true,
+                                         label:       label });
+
+            let x_alignment;
+            if (buttons.length == 1)
+                x_alignment = St.Align.END;
+            else if (i == 0)
+                x_alignment = St.Align.START;
+            else if (i == buttons.length - 1)
+                x_alignment = St.Align.END;
+            else
+                x_alignment = St.Align.MIDDLE;
+
+            this._initialKeyFocus = button;
+            this._buttonLayout.add(button,
+                                   { expand: true,
+                                     x_fill: false,
+                                     y_fill: false,
+                                     x_align: x_alignment,
+                                     y_align: St.Align.MIDDLE });
+
+            if (!hadChildren) {
+                this._buttonLayout.opacity = 0;
+                Tweener.addTween(this._buttonLayout,
+                                 { opacity: 255,
+                                   time: 0.33,
+                                   transition: 'easeOutQuad',
+                                   onComplete: Lang.bind(this, function() {
+                                   })
+                                 });
+            }
+
+            button.connect('clicked', action);
+
+            if (key)
+                this._actionKeys[key] = action;
+            i++;
+        }
+    },
+
+    _onKeyPressEvent: function(object, keyPressEvent) {
+        let symbol = keyPressEvent.get_key_symbol();
+        let action = this._actionKeys[symbol];
+
+        if (action)
+            action();
+    },
+
+    _onGroupDestroy: function() {
+        this.emit('destroy');
+    },
+
+    _fadeOpen: function() {
+        let monitor = Main.layoutManager.focusMonitor;
+
+        this._backgroundBin.set_position(monitor.x, monitor.y);
+        this._backgroundBin.set_size(monitor.width, monitor.height);
+
+        this.state = State.OPENING;
+
+        this._initialKeyFocus.grab_key_focus();
+
+        this._dialogLayout.opacity = 255;
+        this._group.opacity = 0;
+        this._group.show();
+        Tweener.addTween(this._group,
+                         { opacity: 255,
+                           time: OPEN_AND_CLOSE_TIME,
+                           transition: 'easeOutQuad',
+                           onComplete: Lang.bind(this,
+                               function() {
+                                   this.state = State.OPENED;
+                                   this.emit('opened');
+                               })
+                         });
+    },
+
+    setInitialKeyFocus: function(actor) {
+        if (actor)
+            this._initialKeyFocus = actor;
+    },
+
+    open: function() {
+        if (this.state == State.OPENED || this.state == State.OPENING)
+            return true;
+
+        Main.pushModal(this._dialogLayout);
+
+        this._fadeOpen();
+        return true;
+    },
+
+    close: function() {
+        if (this.state == State.CLOSED || this.state == State.CLOSING)
+            return;
+
+        this.state = State.CLOSING;
+        this._savedKeyFocus = null;
+
+        Main.popModal(this._dialogLayout);
+
+        Tweener.addTween(this._group,
+                         { opacity: 0,
+                           time: OPEN_AND_CLOSE_TIME,
+                           transition: 'easeOutQuad',
+                           onComplete: Lang.bind(this,
+                               function() {
+                                   this.state = State.CLOSED;
+                                   this._group.hide();
+                               })
+                         });
+    },
+
+};
+Signals.addSignalMethods(Dialog.prototype);
diff --git a/js/ui/loginDialog.js b/js/ui/loginDialog.js
new file mode 100644
index 0000000..0419a20
--- /dev/null
+++ b/js/ui/loginDialog.js
@@ -0,0 +1,1014 @@
+/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*-
+ *
+ * Copyright 2010 Red Hat, Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+const Lang = imports.lang;
+const Signals = imports.signals;
+
+const Gettext = imports.gettext.domain('gnome-shell');
+const _ = Gettext.gettext;
+
+const Clutter = imports.gi.Clutter;
+const CtrlAltTab = imports.ui.ctrlAltTab;
+const Gdm = imports.gi.Gdm;
+const Gio = imports.gi.Gio;
+const GLib = imports.gi.GLib;
+const Gtk = imports.gi.Gtk;
+const Mainloop = imports.mainloop;
+const Pango = imports.gi.Pango;
+const St = imports.gi.St;
+const Shell = imports.gi.Shell;
+const GdmGreeter = imports.gi.GdmGreeter;
+const AccountsService = imports.gi.AccountsService;
+
+const Batch = imports.misc.batch;
+const Dialog = imports.ui.dialog;
+const Lightbox = imports.ui.lightbox;
+const Main = imports.ui.main;
+const ModalDialog = imports.ui.modalDialog;
+const Tweener = imports.ui.tweener;
+
+const _DIALOG_ICON_SIZE = 64;
+const _FADE_ANIMATION_TIME = 0.16;
+const _RESIZE_ANIMATION_TIME = 0.25;
+const _SCROLL_ANIMATION_TIME = 2.0;
+const _TIMED_LOGIN_IDLE_THRESHOLD = 5.0;
+
+let _loginDialog = null;
+
+function _fadeInActor(actor) {
+    let hold = new Batch.Hold();
+
+    if (actor.opacity == 255 && actor.visible)
+      return null;
+
+    actor.show();
+    let [minHeight, naturalHeight] = actor.get_preferred_height(-1);
+
+    actor.opacity = 0;
+    actor.set_height(0);
+    Tweener.addTween(actor,
+                     { opacity: 255,
+                       height: naturalHeight,
+                       time: _FADE_ANIMATION_TIME,
+                       transition: 'easeOutQuad',
+                       onComplete: function() {
+                           actor.set_height(-1);
+                           hold.release();
+                       },
+                       onCompleteScope: this
+                     });
+    return hold;
+}
+
+function _fadeOutActor(actor) {
+    let hold = new Batch.Hold();
+
+    if (!actor.visible) {
+      actor.opacity = 0;
+      return null;
+    }
+
+    if (actor.opacity == 0) {
+        actor.hide();
+        return null;
+    }
+
+    Tweener.addTween(actor,
+                     { opacity: 0,
+                       height: 0,
+                       time: _FADE_ANIMATION_TIME,
+                       transition: 'easeOutQuad',
+                       onComplete: function() {
+                           actor.hide();
+                           actor.set_height(-1);
+                           hold.release();
+                       },
+                       onCompleteScope: this
+                     });
+    return hold;
+}
+
+function _smoothlyResizeActor(actor, width, height) {
+    let finalWidth;
+    let finalHeight;
+
+    if (width < 0) {
+        finalWidth = actor.width;
+    } else {
+        finalWidth = width;
+    }
+
+    if (height < 0) {
+        finalHeight = actor.height;
+    } else {
+        finalHeight = height;
+    }
+
+    actor.set_size(actor.width, actor.height);
+
+    if (actor.width == finalWidth &&
+        actor.height == finalHeight) {
+        return null;
+    }
+
+    let hold = new Batch.Hold();
+
+    Tweener.addTween(actor,
+                     { width: finalWidth,
+                       height: finalHeight,
+                       time: _RESIZE_ANIMATION_TIME,
+                       transition: 'easeOutQuad',
+                       onComplete: Lang.bind(this, function() {
+                                       hold.release();
+                                   })
+                     });
+    return hold;
+}
+
+function _freezeActorSize(actor) {
+    actor.set_size(actor.width, actor.height);
+}
+
+function _thawActorSize(actor) {
+    let oldWidth = actor.width;
+    let oldHeight = actor.height;
+    actor.set_size(-1, -1);
+    let [minHeight, naturalHeight] = actor.get_preferred_height(-1);
+    actor.set_size(oldWidth, oldHeight);
+
+    let batch = new Batch.ConsecutiveBatch(this,
+                                           [function() {
+                                                return _smoothlyResizeActor(actor, -1, naturalHeight);
+                                            },
+
+                                            function() {
+                                                actor.set_size(-1, -1);
+                                            }
+                                           ]);
+
+    return batch.run();
+}
+
+function ListItem(user, reason) {
+    this._init(user, reason);
+}
+
+ListItem.prototype = {
+    _init: function(user) {
+        this.user = user;
+
+        let layout = new St.BoxLayout({ vertical: false });
+
+        this.actor = new St.Button({ style_class: 'login-dialog-user-list-item',
+                                     can_focus:   true,
+                                     child:       layout,
+                                     reactive:    true,
+                                     x_align:     St.Align.START,
+                                     x_fill:      true });
+        this._userChangedId = this.user.connect('changed',
+                                                 Lang.bind(this, this._onUserChanged));
+        this._iconBin = new St.Bin();
+        layout.add(this._iconBin);
+        let textLayout = new St.BoxLayout({ style_class: 'login-dialog-user-list-item-text-box',
+                                            vertical:    true });
+        layout.add(textLayout,
+                   { y_fill: false,
+                     y_align: St.Align.MIDDLE,
+                     expand: true });
+
+        this._nameLabel = new St.Label({ text:        this.user.get_real_name(),
+                                         style_class: 'login-dialog-user-list-item-name' });
+        textLayout.add(this._nameLabel);
+
+        this._updateIcon();
+
+        this.actor.connect('clicked', Lang.bind(this, this._onClicked));
+    },
+
+    _onUserChanged: function() {
+        this._nameLabel.set_text(this.user.get_real_name());
+        this._updateIcon();
+    },
+
+    _setIconFromFile: function(iconFile, styleClass) {
+        if (styleClass)
+            this._iconBin.set_style_class_name(styleClass);
+        this._iconBin.set_style(null);
+
+        this._iconBin.child = null;
+        if (iconFile) {
+            this._iconBin.show();
+            this._iconBin.set_style('background-image: url("' + iconFile + '");');
+        } else {
+            this._iconBin.hide();
+        }
+    },
+
+    _setIconFromName: function(iconName, styleClass) {
+        if (styleClass)
+            this._iconBin.set_style_class_name(styleClass);
+        this._iconBin.set_style(null);
+
+        if (iconName != null) {
+            let gicon = new Gio.ThemedIcon({ name: iconName });
+            let icon = new St.Icon();
+            icon.set_gicon(gicon);
+
+            this._iconBin.child = icon;
+            this._iconBin.show();
+        } else {
+            this._iconBin.child = null;
+            this._iconBin.hide();
+        }
+    },
+
+    _updateIcon: function() {
+        let iconFileName = this.user.get_icon_file();
+        let gicon = null;
+
+        if (GLib.file_test(iconFileName, GLib.FileTest.EXISTS)) {
+            this._setIconFromFile(iconFileName, 'login-dialog-user-list-item-icon');
+        } else {
+            this._setIconFromName('avatar-default', 'login-dialog-user-list-item-icon');
+        }
+    },
+
+    _onClicked: function() {
+        this.emit('activate');
+    },
+
+    fadeOutName: function() {
+        return _fadeOutActor(this._nameLabel);
+    },
+
+    fadeInName: function() {
+        return _fadeInActor(this._nameLabel);
+    }
+};
+Signals.addSignalMethods(ListItem.prototype);
+
+function UserList() {
+    this._init.apply(this, arguments);
+}
+
+UserList.prototype = {
+    _init: function() {
+        this.actor = new St.ScrollView({ enable_swipe_scrolling: true,
+                                         style_class:            'login-dialog-user-list-view'});
+        this.actor.set_policy(Gtk.PolicyType.NEVER,
+                              Gtk.PolicyType.AUTOMATIC);
+
+        this._layout = new St.BoxLayout({ vertical:    true,
+                                          style_class: 'login-dialog-user-list' });
+
+        this.actor.add_actor(this._layout,
+                             { x_fill:  true,
+                               y_fill:  true,
+                               x_align: St.Align.START,
+                               y_align: St.Align.MIDDLE });
+        this._items = {};
+    },
+
+    _showItem: function(item) {
+        let tasks = [function() {
+                         return _fadeInActor(item.actor);
+                     },
+
+                     function() {
+                         return item.fadeInName();
+                     }];
+
+        let batch = new Batch.ConsecutiveBatch(this, tasks);
+        return batch.run();
+    },
+
+    _onItemActivated: function(activatedItem) {
+        this.emit('activate', activatedItem);
+    },
+
+    giveUpWhitespace: function() {
+        let container = this.actor.get_parent();
+
+        container.child_set(this.actor, { expand: false });
+    },
+
+    takeOverWhitespace: function() {
+        let container = this.actor.get_parent();
+
+        container.child_set(this.actor, { expand: true });
+    },
+
+    freezeSize: function() {
+        return _freezeActorSize(this._layout);
+    },
+
+    thawSize: function() {
+        return _thawActorSize(this._layout);
+    },
+
+    hideItems: function(exception) {
+        let tasks = [];
+
+        for (let userName in this._items) {
+            let item = this._items[userName];
+            if (item != exception) {
+                tasks.push(function() {
+                    return _fadeOutActor(item.actor);
+                });
+            }
+        }
+
+        let batch = new Batch.ConsecutiveBatch(this,
+                                               [function() {
+                                                    return _fadeOutActor(this.actor.vscroll);
+                                                },
+
+                                                new Batch.ConcurrentBatch(this, tasks)
+                                               ]);
+
+        return batch.run();
+    },
+
+    _getExpandedHeight: function() {
+        let hiddenActors = [];
+        for (let userName in this._items) {
+            let item = this._items[userName];
+            if (!item.actor.visible) {
+                item.actor.show();
+                hiddenActors.push(item.actor);
+            }
+        }
+
+        if (!this._layout.visible) {
+            this._layout.show();
+            hiddenActors.push(this._layout);
+        }
+
+        this._layout.set_size(-1, -1);
+        let [minHeight, naturalHeight] = this._layout.get_preferred_height(-1);
+
+        for (let i = 0; i < hiddenActors.length; i++) {
+            let actor = hiddenActors[i];
+            actor.hide();
+        }
+
+        return naturalHeight;
+    },
+
+    showItems: function() {
+        let tasks = [];
+
+        for (let userName in this._items) {
+            let item = this._items[userName];
+            tasks.push(function() {
+                return this._showItem(item);
+            });
+        }
+
+        let batch = new Batch.ConsecutiveBatch(this,
+                                               [function() {
+                                                    this.takeOverWhitespace();
+                                                },
+
+                                                function() {
+                                                    let fullHeight = this._getExpandedHeight();
+                                                    return _smoothlyResizeActor(this._layout, -1, fullHeight);
+                                                },
+
+                                                new Batch.ConcurrentBatch(this, tasks),
+
+                                                function() {
+                                                    this.actor.set_size(-1, -1);
+                                                },
+
+                                                function() {
+                                                    return _fadeInActor(this.actor.vscroll);
+                                                }]);
+        return batch.run();
+    },
+
+    scrollToItem: function(item) {
+        let box = item.actor.get_allocation_box();
+
+        let adjustment = this.actor.get_vscroll_bar().get_adjustment();
+
+        let value = (box.y1 + adjustment.step_increment / 2.0) - (adjustment.page_size / 2.0);
+        adjustment.interpolate(null, value, _SCROLL_ANIMATION_TIME * 1000);
+    },
+
+    jumpToItem: function(item) {
+        let box = item.actor.get_allocation_box();
+
+        let adjustment = this.actor.get_vscroll_bar().get_adjustment();
+
+        let value = (box.y1 + adjustment.step_increment / 2.0) - (adjustment.page_size / 2.0);
+
+        adjustment.set_value(value);
+    },
+
+    getItemFromUserName: function(userName) {
+        let item = this._items[userName];
+
+        if (!item)
+            return null;
+
+        return item;
+    },
+
+    addUser: function(user) {
+        if (!user.is_loaded)
+            return;
+
+        if (user.is_system_account())
+            return;
+
+        let userName = user.get_user_name();
+
+        if (!userName)
+            return;
+
+        this.removeUser(user);
+
+        let item = new ListItem(user);
+        this._layout.add(item.actor, { x_fill: true });
+
+        this._items[userName] = item;
+
+        item.connect('activate',
+                     Lang.bind(this, this._onItemActivated));
+
+        // Try to keep the focused item front-and-center
+        item.actor.connect('key-focus-in',
+                           Lang.bind(this,
+                                     function() {
+                                         this.scrollToItem(item);
+                                     }));
+
+        this.emit('item-added', item);
+    },
+
+    removeUser: function(user) {
+        if (!user.is_loaded)
+            return;
+
+        let userName = user.get_user_name();
+
+        if (!userName)
+            return;
+
+        let item = this._items[userName];
+
+        if (!item)
+            return;
+
+        item.actor.destroy();
+        delete this._items[userName];
+    }
+};
+Signals.addSignalMethods(UserList.prototype);
+
+function LoginDialog() {
+    if (_loginDialog == null) {
+        this._init();
+        _loginDialog = this;
+    }
+
+    return _loginDialog;
+}
+
+LoginDialog.prototype = {
+    __proto__: Dialog.Dialog.prototype,
+
+    _init: function() {
+        Dialog.Dialog.prototype._init.call(this, { lowerBelowChrome: true,
+                                                   styleClass: 'login-dialog' });
+        this.connect('destroy',
+                     Lang.bind(this, this._onDestroy));
+        this.connect('opened',
+                     Lang.bind(this, this._onOpened));
+
+        this._userManager = AccountsService.UserManager.get_default()
+        this._greeterClient = new GdmGreeter.Client();
+
+        this._greeterClient.open_connection();
+
+        this._greeterClient.call_start_conversation('gdm-password');
+
+        this._greeterClient.connect('ready',
+                                    Lang.bind(this, this._onReady));
+        this._greeterClient.connect('reset',
+                                    Lang.bind(this, this._onReset));
+        this._greeterClient.connect('info',
+                                    Lang.bind(this, this._onInfo));
+        this._greeterClient.connect('problem',
+                                    Lang.bind(this, this._onProblem));
+        this._greeterClient.connect('info-query',
+                                    Lang.bind(this, this._onInfoQuery));
+        this._greeterClient.connect('secret-info-query',
+                                    Lang.bind(this, this._onSecretInfoQuery));
+        this._greeterClient.connect('session-opened',
+                                    Lang.bind(this, this._onSessionOpened));
+        this._greeterClient.connect('timed-login-requested',
+                                    Lang.bind(this, this._onTimedLoginRequested));
+        this._greeterClient.connect('authentication-failed',
+                                    Lang.bind(this, this._onAuthenticationFailed));
+        this._greeterClient.connect('conversation-stopped',
+                                    Lang.bind(this, this._onConversationStopped));
+
+        this._titleLabel = new St.Label({ style_class: 'login-dialog-title',
+                                          text: _("Sign In") });
+
+        this.contentLayout.add(this._titleLabel,
+                              { y_fill:  false,
+                                y_align: St.Align.START });
+
+        let mainContentLayout = new St.BoxLayout({ vertical: false });
+        this.contentLayout.add(mainContentLayout,
+                               { expand: true,
+                                 x_fill: true,
+                                 y_fill: false });
+
+        this._userList = new UserList();
+        mainContentLayout.add(this._userList.actor,
+                              { expand: true,
+                                x_fill: true,
+                                y_fill: true });
+
+        this.setInitialKeyFocus(this._userList.actor);
+
+        this._promptLayout = new St.BoxLayout({ style_class: 'login-dialog-prompt-layout',
+                                                vertical:    true });
+        mainContentLayout.add(this._promptLayout,
+                              { expand: true,
+                                x_fill: true,
+                                y_fill: true,
+                                x_align: St.Align.START });
+        this._promptLabel = new St.Label({ style_class: 'login-dialog-prompt-label' });
+
+        this._mainContentLayout = mainContentLayout;
+
+        this._promptLayout.add(this._promptLabel,
+                              { expand: true,
+                                x_fill: true,
+                                y_fill: true,
+                                x_align: St.Align.START });
+        this._promptEntry = new St.Entry({ style_class: 'login-dialog-prompt-entry' });
+        this._promptLayout.add(this._promptEntry,
+                              { expand: true,
+                                x_fill: true,
+                                y_fill: false,
+                                x_align: St.Align.START});
+        this._promptLayout.hide();
+
+        let notListedLabel = new St.Label({ text:        _("Not listed?"),
+                                            style_class: 'login-dialog-not-listed-label' });
+        this._notListedButton = new St.Button({ style_class: 'login-dialog-not-listed-button',
+                                                can_focus:   true,
+                                                child:       notListedLabel,
+                                                reactive:    true,
+                                                x_align:     St.Align.START,
+                                                x_fill:      true });
+
+        this._notListedButton.connect('clicked', Lang.bind(this, this._onNotListedClicked));
+
+        this.contentLayout.add(this._notListedButton,
+                               { expand: false,
+                                 x_align: St.Align.START,
+                                 x_fill: true });
+
+        if (!this._userManager.is_loaded) {
+            this._userManagerLoadedId = this._userManager.connect('notify::is-loaded',
+                                                                  Lang.bind(this, function() {
+                                                                      if (this._userManager.is_loaded) {
+                                                                          this._loadUserList();
+                                                                          this._userManager.disconnect(this._userManagerLoadedId);
+                                                                          this._userManagerLoadedId = 0;
+                                                                      }
+                                                                  }));
+        } else {
+            this._loadUserList();
+        }
+
+        this._userList.connect('activate',
+                               Lang.bind(this, function(userList, item) {
+                                   this._onUserListActivated(item);
+                               }));
+
+    },
+
+    _onReady: function(client, serviceName) {
+    },
+
+    _onReset: function(client, serviceName) {
+        this._greeterClient.call_start_conversation('gdm-password');
+
+        let tasks = [this._hidePrompt,
+
+                     new Batch.ConcurrentBatch(this, [this._fadeInTitleLabel,
+                                                      this._fadeInNotListedButton]),
+
+                     function() {
+                         this._userList.actor.show();
+                         this._userList.actor.opacity = 255;
+                         return this._userList.showItems();
+                     },
+
+                     function() {
+                         this._userList.actor.grab_key_focus();
+                     },
+                    ];
+
+        let batch = new Batch.ConsecutiveBatch(this, tasks);
+
+        batch.run();
+    },
+
+    _onInfo: function(client, serviceName, info) {
+        Main.notifyError(info);
+    },
+
+    _onProblem: function(client, serviceName, problem) {
+        Main.notifyError(problem);
+    },
+
+    _onCancel: function(client) {
+        this._greeterClient.call_cancel();
+    },
+
+    _fadeInPrompt: function() {
+        let tasks = [function() {
+                         return _fadeInActor(this._promptLabel);
+                     },
+
+                     function() {
+                         return _fadeInActor(this._promptEntry);
+                     },
+
+                     function() {
+                         return _fadeInActor(this._promptLayout);
+                     },
+
+                     function() {
+                         this._promptEntry.grab_key_focus();
+                     }];
+
+        let batch = new Batch.ConcurrentBatch(this, tasks);
+        return batch.run();
+    },
+
+    _showPrompt: function() {
+        let hold = new Batch.Hold();
+
+        let buttons = [{ action: Lang.bind(this, this._onCancel),
+                         label:  _("Cancel"),
+                         key:    Clutter.Escape },
+                       { action: Lang.bind(this, function() {
+                                     hold.release();
+                                 }),
+                         label:  _("Sign In") }];
+
+        this._promptEntryActivateCallbackId = this._promptEntry.clutter_text.connect('activate',
+                                                                                     Lang.bind(this, function() {
+                                                                                         hold.release();
+                                                                                     }));
+        hold.connect('release', Lang.bind(this, function() {
+                         this._promptEntry.clutter_text.disconnect(this._promptEntryActivateCallbackId);
+                         this._promptEntryActivateCallbackId = null;
+                     }));
+
+        let tasks = [function() {
+                         return this._fadeInPrompt();
+                     },
+
+                     function() {
+                         this.setButtons(buttons);
+                     },
+
+                     hold];
+
+        let batch = new Batch.ConcurrentBatch(this, tasks);
+
+        return batch.run();
+    },
+
+    _hidePrompt: function() {
+        if (this._promptEntryActivateCallbackId) {
+            this._promptEntry.clutter_text.disconnect(this._promptEntryActivateCallbackId);
+            this._promptEntryActivateCallbackId = null;
+        }
+
+        this.setButtons([]);
+
+        let tasks = [function() {
+                         return _fadeOutActor(this._promptLayout);
+                     },
+
+                     function() {
+                         this._promptEntry.set_text('');
+                     }];
+
+        let batch = new Batch.ConsecutiveBatch(this, tasks);
+
+        return batch.run();
+    },
+
+    _askQuestion: function(serviceName, question) {
+        this._promptLabel.set_text(question);
+
+        let tasks = [this._showPrompt,
+
+                     function() {
+                         let _text = this._promptEntry.get_text();
+                         this._promptEntry.set_text('');
+                         this._greeterClient.call_answer_query(serviceName, _text);
+                     }];
+
+        let batch = new Batch.ConsecutiveBatch(this, tasks);
+        return batch.run();
+    },
+    _onInfoQuery: function(client, serviceName, question) {
+        this._promptEntry.set_text('');
+        this._promptEntry.clutter_text.set_password_char('');
+        this._askQuestion(serviceName, question);
+    },
+
+    _onSecretInfoQuery: function(client, serviceName, secretQuestion) {
+        this._promptEntry.set_text('');
+        this._promptEntry.clutter_text.set_password_char('\u25cf');
+        this._askQuestion(serviceName, secretQuestion);
+    },
+
+    _onSessionOpened: function(client, serviceName) {
+        this._greeterClient.call_start_session_when_ready(serviceName, true);
+    },
+
+    _waitForItemForUser: function(userName) {
+        let item = this._userList.getItemFromUserName(userName);
+
+        if (item)
+          return null;
+
+        let hold = new Batch.Hold();
+        let signalId = this._userList.connect('item-added',
+                                              Lang.bind(this, function() {
+                                                  let item = this._userList.getItemFromUserName(userName);
+
+                                                  if (item) {
+                                                      hold.release();
+                                                  }
+                                              }));
+
+        hold.connect('release', Lang.bind(this, function() {
+                         this._userList.disconnect(signalId);
+                     }));
+
+        return hold;
+    },
+
+    _showTimedLoginAnimation: function() {
+        let hold = new Batch.Hold();
+
+        Tweener.addTween(this._userList.actor,
+                         { opacity: 255,
+                           time: this._timedLoginAnimationTime,
+                           transition: 'linear',
+                           onComplete: function() {
+                               hold.release();
+                           },
+                           onCompleteScope: this
+                         });
+        return hold;
+    },
+
+    _blockTimedLoginUntilIdle: function() {
+        // This blocks timed login from starting until a few
+        // seconds after the user stops interacting with the
+        // login screen.
+        //
+        // We skip this step if the timed login delay is very
+        // short.
+        if ((this._timedLoginDelay - _TIMED_LOGIN_IDLE_THRESHOLD) <= 0)
+          return null;
+
+        let hold = new Batch.Hold();
+
+        this._timedLoginIdleTimeOutId = Mainloop.timeout_add_seconds(_TIMED_LOGIN_IDLE_THRESHOLD,
+                                                                     function() {
+                                                                         this._timedLoginAnimationTime -= _TIMED_LOGIN_IDLE_THRESHOLD;
+                                                                         hold.release();
+                                                                     });
+        return hold;
+    },
+
+    _startTimedLogin: function(userName, delay) {
+        this._timedLoginItem = null;
+        this._timedLoginDelay = delay;
+        this._timedLoginAnimationTime = delay;
+
+        let tasks = [function() {
+                         return this._waitForItemForUser(userName);
+                     },
+
+                     function() {
+                         this._timedLoginItem = this._userList.getItemFromUserName(userName);
+                     },
+
+                     function() {
+                         // If we're just starting out, start on the right
+                         // item.
+                         if (!this.is_loaded)
+                             this._userList.jumpToItem(this._timedLoginItem);
+                     },
+
+                     this._blockTimedLoginUntilIdle,
+
+                     function() {
+                         this._userList.scrollToItem(this._timedLoginItem);
+                     },
+
+                     this._showTimedLoginAnimation(),
+
+                     function() {
+                         this._timedLoginBatch = null;
+                         this._greeterClient.call_begin_auto_login(userName);
+                     }];
+
+        this._timedLoginBatch = new Batch.ConsecutiveBatch(this, tasks);
+
+        return this._timedLoginBatch.run();
+    },
+
+    _resetTimedLogin: function() {
+        this._timedLoginBatch.cancel();
+
+        let userName = this._timedLoginItem.user.get_user_name();
+
+        if (userName)
+            this._startTimedLogin(userName, this._timedLoginDelay);
+    },
+
+    _onTimedLoginRequested: function(client, userName, seconds) {
+        this._startTimedLogin(userName, seconds);
+
+        global.stage.connect('captured-event',
+                             Lang.bind(this, function(actor, event) {
+                                if (this._timedLoginBatch &&
+                                    (event.type() == Clutter.EventType.KEY_PRESS ||
+                                     event.type() == Clutter.EventType.KEY_RELEASE ||
+                                     event.type() == Clutter.EventType.BUTTON_PRESS ||
+                                     event.type() == Clutter.EventType.BUTTON_RELEASE))
+                                    this._resetTimedLogin();
+                            }));
+    },
+
+    _onAuthenticationFailed: function(client) {
+        this._greeterClient.call_cancel();
+    },
+
+    _onConversationStopped: function(client, serviceName) {
+        this._greeterClient.call_cancel();
+    },
+
+    _onNotListedClicked: function(user) {
+        let tasks = [function() {
+                         return this._userList.hideItems();
+                     },
+
+                     function() {
+                         return this._userList.giveUpWhitespace();
+                     },
+
+                     function() {
+                         this._userList.actor.hide();
+                     },
+
+                     new Batch.ConcurrentBatch(this, [this._fadeOutTitleLabel,
+                                                      this._fadeOutNotListedButton]),
+
+                     function() {
+                         this._greeterClient.call_begin_verification('gdm-password');
+                     }];
+
+        let batch = new Batch.ConsecutiveBatch(this, tasks);
+        batch.run();
+    },
+
+    _fadeInTitleLabel: function() {
+        return _fadeInActor(this._titleLabel);
+    },
+
+    _fadeOutTitleLabel: function() {
+        return _fadeOutActor(this._titleLabel);
+    },
+
+    _fadeInNotListedButton: function() {
+        return _fadeInActor(this._notListedButton);
+    },
+
+    _fadeOutNotListedButton: function() {
+        return _fadeOutActor(this._notListedButton);
+    },
+
+    _onUserListActivated: function(activatedItem) {
+        let tasks = [function() {
+                         return this._userList.freezeSize();
+                     },
+
+                     function() {
+                         return this._userList.hideItems(activatedItem);
+                     },
+
+                     function() {
+                         return this._userList.giveUpWhitespace();
+                     },
+
+                     function() {
+                         return this._userList.thawSize();
+                     },
+
+                     function() {
+                         return activatedItem.fadeOutName();
+                     },
+
+                     function() {
+                         return this._userList.freezeSize();
+                     },
+
+                     new Batch.ConcurrentBatch(this, [this._fadeOutTitleLabel,
+                                                      this._fadeOutNotListedButton]),
+
+                     function() {
+                         return this._userList.thawSize();
+                     },
+
+                     function() {
+                         let userName = activatedItem.user.get_user_name();
+                         this._greeterClient.call_begin_verification_for_user('gdm-password',
+                                                                              userName);
+                     }];
+
+        let batch = new Batch.ConsecutiveBatch(this, tasks);
+        batch.run();
+    },
+
+    _onDestroy: function() {
+        if (this._userManagerLoadedId) {
+            this._userManager.disconnect(this._userManagerLoadedId);
+            this._userManagerLoadedId = 0;
+        }
+    },
+
+    _loadUserList: function() {
+        let users = this._userManager.list_users();
+
+        for (let i = 0; i < users.length; i++) {
+            this._userList.addUser(users[i]);
+        }
+
+        this._userManager.connect('user-added',
+                                  Lang.bind(this, function(userManager, user) {
+                                      this._userList.addUser(user);
+                                  }));
+
+        this._userManager.connect('user-removed',
+                                  Lang.bind(this, function(userManager, user) {
+                                      this._userList.removeUser(user);
+                                  }));
+
+        Mainloop.idle_add(Lang.bind(this, function() {
+            this.emit('loaded');
+            this.is_loaded = true;
+        }));
+    },
+
+    _onOpened: function() {
+        Main.ctrlAltTabManager.addGroup(this._mainContentLayout,
+                                        _("Login Window"),
+                                        'dialog-password',
+                                        { sortGroup: CtrlAltTab.SortGroup.MIDDLE });
+
+    },
+
+    close: function() {
+        Dialog.Dialog.prototype.close.call(this);
+
+        Main.ctrlAltTabManager.removeGroup(this._group);
+    }
+};
diff --git a/js/ui/main.js b/js/ui/main.js
index 6cb58fd..40f759a 100644
--- a/js/ui/main.js
+++ b/js/ui/main.js
@@ -1,8 +1,11 @@
 /* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
 
+imports.gi.versions.Clutter = '1.0';
+
 const Clutter = imports.gi.Clutter;
 const DBus = imports.dbus;
 const Gdk = imports.gi.Gdk;
+const GdmGreeter = imports.gi.GdmGreeter;
 const Gio = imports.gi.Gio;
 const GLib = imports.gi.GLib;
 const GConf = imports.gi.GConf;
@@ -20,6 +23,7 @@ const EndSessionDialog = imports.ui.endSessionDialog;
 const PolkitAuthenticationAgent = imports.ui.polkitAuthenticationAgent;
 const Environment = imports.ui.environment;
 const ExtensionSystem = imports.ui.extensionSystem;
+const LoginDialog = imports.ui.loginDialog;
 const MessageTray = imports.ui.messageTray;
 const Overview = imports.ui.overview;
 const Panel = imports.ui.panel;
@@ -44,10 +48,12 @@ DEFAULT_BACKGROUND_COLOR.from_pixel(0x2266bbff);
 let automountManager = null;
 let autorunManager = null;
 let chrome = null;
+let greeterClient = null;
 let panel = null;
 let hotCorners = [];
 let placesManager = null;
 let overview = null;
+let loginDialog = null;
 let runDialog = null;
 let lookingGlass = null;
 let wm = null;
@@ -115,15 +121,17 @@ function start() {
     _defaultCssStylesheet = global.datadir + '/theme/gnome-shell.css';
     loadTheme();
 
-    let shellwm = global.window_manager;
-    shellwm.takeover_keybinding('panel_main_menu');
-    shellwm.connect('keybinding::panel_main_menu', function () {
-        overview.toggle();
-    });
-    shellwm.takeover_keybinding('panel_run_dialog');
-    shellwm.connect('keybinding::panel_run_dialog', function () {
-       getRunDialog().open();
-    });
+    if (global.session_type == Shell.SessionType.USER) {
+        let shellwm = global.window_manager;
+        shellwm.takeover_keybinding('panel_main_menu');
+        shellwm.connect('keybinding::panel_main_menu', function () {
+            overview.toggle();
+        });
+        shellwm.takeover_keybinding('panel_run_dialog');
+        shellwm.connect('keybinding::panel_run_dialog', function () {
+           getRunDialog().open();
+        });
+    }
 
     // Set up stage hierarchy to group all UI actors under one container.
     uiGroup = new Clutter.Group();
@@ -133,10 +141,14 @@ function start() {
     global.stage.add_actor(uiGroup);
 
     layoutManager = new Layout.LayoutManager();
-    placesManager = new PlaceDisplay.PlacesManager();
+    if (global.session_type == Shell.SessionType.USER) {
+        placesManager = new PlaceDisplay.PlacesManager();
+    }
     xdndHandler = new XdndHandler.XdndHandler();
     ctrlAltTabManager = new CtrlAltTab.CtrlAltTabManager();
-    overview = new Overview.Overview();
+    if (global.session_type == Shell.SessionType.USER) {
+        overview = new Overview.Overview();
+    }
     chrome = new Chrome.Chrome();
     magnifier = new Magnifier.Magnifier();
     statusIconDispatcher = new StatusIconDispatcher.StatusIconDispatcher();
@@ -145,46 +157,72 @@ function start() {
     messageTray = new MessageTray.MessageTray();
     notificationDaemon = new NotificationDaemon.NotificationDaemon();
     windowAttentionHandler = new WindowAttentionHandler.WindowAttentionHandler();
+
     telepathyClient = new TelepathyClient.Client();
-    automountManager = new AutomountManager.AutomountManager();
-    autorunManager = new AutorunManager.AutorunManager();
+    if (global.session_type == Shell.SessionType.USER) {
+        automountManager = new AutomountManager.AutomountManager();
+    }
+
+    if (global.session_type == Shell.SessionType.USER) {
+        autorunManager = new AutorunManager.AutorunManager();
+    }
 
     layoutManager.init();
-    overview.init();
+
+    if (global.session_type == Shell.SessionType.USER) {
+        telepathyClient = new TelepathyClient.Client();
+    }
+
+    if (global.session_type == Shell.SessionType.USER) {
+        overview.init();
+    }
     statusIconDispatcher.start(messageTray.actor);
 
     _startDate = new Date();
 
-    let recorderSettings = new Gio.Settings({ schema: 'org.gnome.shell.recorder' });
+    if (global.session_type == Shell.SessionType.USER) {
+        let recorderSettings = new Gio.Settings({ schema: 'org.gnome.shell.recorder' });
 
-    global.screen.connect('toggle-recording', function() {
-        if (recorder == null) {
-            recorder = new Shell.Recorder({ stage: global.stage });
-        }
+        global.screen.connect('toggle-recording', function() {
+            if (recorder == null) {
+                recorder = new Shell.Recorder({ stage: global.stage });
+            }
 
-        if (recorder.is_recording()) {
-            recorder.pause();
-        } else {
-            // read the parameters from GSettings always in case they have changed
-            recorder.set_framerate(recorderSettings.get_int('framerate'));
-            recorder.set_filename('shell-%d%u-%c.' + recorderSettings.get_string('file-extension'));
-            let pipeline = recorderSettings.get_string('pipeline');
+            if (recorder.is_recording()) {
+                recorder.pause();
+            } else {
+                // read the parameters from GSettings always in case they have changed
+                recorder.set_framerate(recorderSettings.get_int('framerate'));
+                recorder.set_filename('shell-%d%u-%c.' + recorderSettings.get_string('file-extension'));
+                let pipeline = recorderSettings.get_string('pipeline');
 
-            if (!pipeline.match(/^\s*$/))
-                recorder.set_pipeline(pipeline);
-            else
-                recorder.set_pipeline(null);
+                if (!pipeline.match(/^\s*$/))
+                    recorder.set_pipeline(pipeline);
+                else
+                    recorder.set_pipeline(null);
 
-            recorder.record();
-        }
-    });
+                recorder.record();
+            }
+        });
+    }
 
-    global.screen.override_workspace_layout(Meta.ScreenCorner.TOPLEFT, false, -1, 1);
+    if (global.session_type == Shell.SessionType.LOGIN) {
+        global.screen.override_workspace_layout(Meta.ScreenCorner.TOPLEFT, false, 1, 1);
+    } else {
+        global.screen.override_workspace_layout(Meta.ScreenCorner.TOPLEFT, false, -1, 1);
+    }
 
     // Provide the bus object for gnome-session to
     // initiate logouts.
     EndSessionDialog.init();
 
+    if (global.session_type == Shell.SessionType.LOGIN) {
+        loginDialog = new LoginDialog.LoginDialog();
+        loginDialog.connect('loaded', function() {
+                                loginDialog.open();
+                            });
+    }
+
     // Attempt to become a PolicyKit authentication agent
     PolkitAuthenticationAgent.init()
 
@@ -194,8 +232,10 @@ function start() {
     panel.startStatusArea();
     panel.startupAnimation();
 
-    let display = global.screen.get_display();
-    display.connect('overlay-key', Lang.bind(overview, overview.toggle));
+    if (global.session_type == Shell.SessionType.USER) {
+        let display = global.screen.get_display();
+        display.connect('overlay-key', Lang.bind(overview, overview.toggle));
+    }
 
     global.stage.connect('captured-event', _globalKeyPressHandler);
 
@@ -528,8 +568,18 @@ function _globalKeyPressHandler(actor, event) {
 
     // Other bindings are only available when the overview is up and
     // no modal dialog is present.
-    if (!overview.visible || modalCount > 1)
+    if ((overview && !overview.visible) || modalCount > 1)
+        return false;
+
+    if (action == Meta.KeyBindingAction.SWITCH_PANELS) {
+            ctrlAltTabManager.popup(modifierState & Clutter.ModifierType.SHIFT_MASK);
+            return true;
+    }
+
+    if (global.session_type != Shell.SessionType.USER) {
         return false;
+    }
+
 
     // This isn't a Meta.KeyBindingAction yet
     if (symbol == Clutter.Super_L || symbol == Clutter.Super_R) {
@@ -560,9 +610,6 @@ function _globalKeyPressHandler(actor, event) {
         case Meta.KeyBindingAction.PANEL_MAIN_MENU:
             overview.hide();
             return true;
-        case Meta.KeyBindingAction.SWITCH_PANELS:
-            ctrlAltTabManager.popup(modifierState & Clutter.ModifierType.SHIFT_MASK);
-            return true;
     }
 
     return false;
@@ -725,7 +772,9 @@ function activateWindow(window, time, workspaceNum) {
         window.activate(time);
     }
 
-    overview.hide();
+    if (global.session_type == Shell.SessionType.USER) {
+        overview.hide();
+    }
 }
 
 // TODO - replace this timeout with some system to guess when the user might
diff --git a/js/ui/messageTray.js b/js/ui/messageTray.js
index bf72a0f..c18f460 100644
--- a/js/ui/messageTray.js
+++ b/js/ui/messageTray.js
@@ -213,14 +213,16 @@ FocusGrabber.prototype = {
         this._capturedEventId = 0;
         this._togglingFocusGrabMode = false;
 
-        Main.overview.connect('showing', Lang.bind(this,
-            function() {
-                this._toggleFocusGrabMode();
-            }));
-        Main.overview.connect('hidden', Lang.bind(this,
-            function() {
-                this._toggleFocusGrabMode();
-            }));
+        if (Main.overview) {
+            Main.overview.connect('showing', Lang.bind(this,
+                function() {
+                    this._toggleFocusGrabMode();
+                }));
+            Main.overview.connect('hidden', Lang.bind(this,
+                function() {
+                    this._toggleFocusGrabMode();
+                }));
+        }
     },
 
     grabFocus: function(actor) {
@@ -234,7 +236,7 @@ FocusGrabber.prototype = {
         this._prevFocusedWindow = metaDisplay.focus_window;
         this._prevKeyFocusActor = global.stage.get_key_focus();
 
-        if (!Main.overview.visible)
+        if (!Main.overview || !Main.overview.visible)
             global.set_stage_input_mode(Shell.StageInputMode.FOCUSED);
 
         // Use captured-event to notice clicks outside the focused actor
@@ -1325,7 +1327,11 @@ MessageTray.prototype = {
         this._notificationExpandedId = 0;
         this._summaryBoxPointerState = State.HIDDEN;
         this._summaryBoxPointerTimeoutId = 0;
-        this._overviewVisible = Main.overview.visible;
+        if (Main.overview && Main.overview.visible) {
+            this._overviewVisible = true;
+        } else {
+            this._overviewVisible = false;
+        }
         this._notificationRemoved = false;
         this._reNotifyAfterHideNotification = null;
 
@@ -1338,26 +1344,28 @@ MessageTray.prototype = {
 
         this._setSizePosition();
 
-        Main.overview.connect('showing', Lang.bind(this,
-            function() {
-                this._overviewVisible = true;
-                if (this._locked) {
-                    this._unsetClickedSummaryItem();
-                    this._unlock();
-                } else {
-                    this._updateState();
-                }
-            }));
-        Main.overview.connect('hiding', Lang.bind(this,
-            function() {
-                this._overviewVisible = false;
-                if (this._locked) {
-                    this._unsetClickedSummaryItem();
-                    this._unlock();
-                } else {
-                    this._updateState();
-                }
-            }));
+        if (Main.overview) {
+            Main.overview.connect('showing', Lang.bind(this,
+                function() {
+                    this._overviewVisible = true;
+                    if (this._locked) {
+                        this._unsetClickedSummaryItem();
+                        this._unlock();
+                    } else {
+                        this._updateState();
+                    }
+                }));
+            Main.overview.connect('hiding', Lang.bind(this,
+                function() {
+                    this._overviewVisible = false;
+                    if (this._locked) {
+                        this._unsetClickedSummaryItem();
+                        this._unlock();
+                    } else {
+                        this._updateState();
+                    }
+                }));
+        }
 
         this._summaryItems = [];
         // We keep a list of new summary items that were added to the summary since the last
diff --git a/js/ui/notificationDaemon.js b/js/ui/notificationDaemon.js
index 19288a7..ec62e91 100644
--- a/js/ui/notificationDaemon.js
+++ b/js/ui/notificationDaemon.js
@@ -102,8 +102,10 @@ NotificationDaemon.prototype = {
 
         Shell.WindowTracker.get_default().connect('notify::focus-app',
             Lang.bind(this, this._onFocusAppChanged));
-        Main.overview.connect('hidden',
-            Lang.bind(this, this._onFocusAppChanged));
+        if (Main.overview) {
+            Main.overview.connect('hidden',
+                Lang.bind(this, this._onFocusAppChanged));
+        }
     },
 
     _iconForNotificationData: function(icon, hints, size) {
@@ -495,7 +497,7 @@ Source.prototype = {
             this.notifications.length > 0)
             return false;
 
-        if (Main.overview.visible) {
+        if (Main.overview && Main.overview.visible) {
             // We can't just connect to Main.overview's 'hidden' signal,
             // because it's emitted *before* it calls popModal()...
             let id = global.connect('notify::stage-input-mode', Lang.bind(this,
diff --git a/js/ui/panel.js b/js/ui/panel.js
index 2f20167..8248e93 100644
--- a/js/ui/panel.js
+++ b/js/ui/panel.js
@@ -48,6 +48,14 @@ try {
     log('NMApplet is not supported. It is possible that your NetworkManager version is too old');
 }
 
+const LOGIN_TRAY_ICON_ORDER = ['a11y', 'display', 'keyboard', 'volume', 'battery'];
+const LOGIN_TRAY_ICON_SHELL_IMPLEMENTATION = {
+    'a11y': imports.ui.status.accessibility.ATIndicator,
+    'volume': imports.ui.status.volume.Indicator,
+    'battery': imports.ui.status.power.Indicator,
+    'keyboard': imports.ui.status.keyboard.XKBIndicator
+};
+
 // To make sure the panel corners blend nicely with the panel,
 // we draw background and borders the same way, e.g. drawing
 // them as filled shapes from the outside inwards instead of
@@ -56,6 +64,8 @@ try {
 // to blend border and background color together.
 // For that purpose we use the following helper methods, taken
 // from st-theme-node-drawing.c
+
+
 function _norm(x) {
     return Math.round(x / 255);
 }
@@ -65,6 +75,7 @@ function _over(srcColor, dstColor) {
     let dst = _premultiply(dstColor);
     let result = new Clutter.Color();
 
+
     result.alpha = src.alpha + _norm((255 - src.alpha) * dst.alpha);
     result.red = src.red + _norm((255 - src.alpha) * dst.red);
     result.green = src.green + _norm((255 - src.alpha) * dst.green);
@@ -270,15 +281,17 @@ AppMenuButton.prototype = {
         this.menu.addMenuItem(this._quitMenu);
         this._quitMenu.connect('activate', Lang.bind(this, this._onQuit));
 
-        this._visible = !Main.overview.visible;
-        if (!this._visible)
-            this.actor.hide();
-        Main.overview.connect('hiding', Lang.bind(this, function () {
-            this.show();
-        }));
-        Main.overview.connect('showing', Lang.bind(this, function () {
-            this.hide();
-        }));
+        if (Main.overview) {
+            this._visible = !Main.overview.visible;
+            if (!this._visible)
+                this.actor.hide();
+            Main.overview.connect('hiding', Lang.bind(this, function () {
+                this.show();
+            }));
+            Main.overview.connect('showing', Lang.bind(this, function () {
+                this.hide();
+            }));
+        }
 
         this._stop = true;
 
@@ -820,12 +833,22 @@ Panel.prototype = {
 
         this._statusArea = {};
 
-        Main.overview.connect('shown', Lang.bind(this, function () {
-            this.actor.add_style_class_name('in-overview');
-        }));
-        Main.overview.connect('hiding', Lang.bind(this, function () {
-            this.actor.remove_style_class_name('in-overview');
-        }));
+        if (Main.overview) {
+            Main.overview.connect('shown', Lang.bind(this, function () {
+                this.actor.add_style_class_name('in-overview');
+            }));
+            Main.overview.connect('hiding', Lang.bind(this, function () {
+                this.actor.remove_style_class_name('in-overview');
+            }));
+        }
+
+        if (global.session_type == Shell.SessionType.LOGIN) {
+            this._tray_icon_order = LOGIN_TRAY_ICON_ORDER;
+            this._tray_icon_shell_implementation = LOGIN_TRAY_ICON_SHELL_IMPLEMENTATION;
+        } else {
+            this._tray_icon_order = STANDARD_TRAY_ICON_ORDER;
+            this._tray_icon_shell_implementation = STANDARD_TRAY_ICON_SHELL_IMPLEMENTATION;
+        }
 
         this._leftPointerBarrier = 0;
         this._rightPointerBarrier = 0;
@@ -909,46 +932,50 @@ Panel.prototype = {
             this._rightBox.allocate(childBox, flags);
         }));
 
-        /* Button on the left side of the panel. */
-        /* Translators: If there is no suitable word for "Activities" in your language, you can use the word for "Overview". */
-        let label = new St.Label({ text: _("Activities") });
-        this.button = new St.Button({ name: 'panelActivities',
-                                      style_class: 'panel-button',
-                                      reactive: true,
-                                      can_focus: true });
-        this._activities = this.button;
-        this.button.set_child(label);
-        this.button._delegate = this.button;
-        this.button._xdndTimeOut = 0;
-        this.button.handleDragOver = Lang.bind(this,
-            function(source, actor, x, y, time) {
-                 if (source == Main.xdndHandler) {
-                    if (this.button._xdndTimeOut != 0)
-                        Mainloop.source_remove(this.button._xdndTimeOut);
-                    this.button._xdndTimeOut = Mainloop.timeout_add(BUTTON_DND_ACTIVATION_TIMEOUT,
-                                                                    Lang.bind(this,
-                                                                                function() {
-                                                                                    this._xdndShowOverview(actor);
-                                                                                }));
-                 }
-            });
-        this._leftBox.add(this.button);
-
-        // Synchronize the buttons pseudo classes with its corner
-        this.button.connect('style-changed', Lang.bind(this,
-            function(actor) {
-                let rtl = actor.get_direction() == St.TextDirection.RTL;
-                let corner = rtl ? this._rightCorner : this._leftCorner;
-                let pseudoClass = actor.get_style_pseudo_class();
-                corner.actor.set_style_pseudo_class(pseudoClass);
-            }));
+        if (global.session_type == Shell.SessionType.USER) {
+            /* Button on the left side of the panel. */
+            /* Translators: If there is no suitable word for "Activities" in your language, you can use the word for "Overview". */
+            let label = new St.Label({ text: _("Activities") });
+            this.button = new St.Button({ name: 'panelActivities',
+                                          style_class: 'panel-button',
+                                          reactive: true,
+                                          can_focus: true });
+            this._activities = this.button;
+            this.button.set_child(label);
+            this.button._delegate = this.button;
+            this.button._xdndTimeOut = 0;
+            this.button.handleDragOver = Lang.bind(this,
+                function(source, actor, x, y, time) {
+                     if (source == Main.xdndHandler) {
+                        if (this.button._xdndTimeOut != 0)
+                            Mainloop.source_remove(this.button._xdndTimeOut);
+                        this.button._xdndTimeOut = Mainloop.timeout_add(BUTTON_DND_ACTIVATION_TIMEOUT,
+                                                                        Lang.bind(this,
+                                                                                    function() {
+                                                                                        this._xdndShowOverview(actor);
+                                                                                    }));
+                     }
+                });
+            this._leftBox.add(this.button);
+
+            // Synchronize the buttons pseudo classes with its corner
+            this.button.connect('style-changed', Lang.bind(this,
+                function(actor) {
+                    let rtl = actor.get_direction() == St.TextDirection.RTL;
+                    let corner = rtl ? this._rightCorner : this._leftCorner;
+                    let pseudoClass = actor.get_style_pseudo_class();
+                    corner.actor.set_style_pseudo_class(pseudoClass);
+                }));
+        }
 
         this._hotCorner = null;
 
-        this._appMenu = new AppMenuButton();
-        this._leftBox.add(this._appMenu.actor);
+        if (global.session_type == Shell.SessionType.USER) {
+            this._appMenu = new AppMenuButton();
+            this._leftBox.add(this._appMenu.actor);
 
-        this._menus.addMenu(this._appMenu.menu);
+            this._menus.addMenu(this._appMenu.menu);
+        }
 
         /* center */
         this._dateMenu = new DateMenu.DateMenuButton();
@@ -967,43 +994,50 @@ Panel.prototype = {
         this._rightBox.add(this._trayBox);
         this._rightBox.add(this._statusBox);
 
-        this._userMenu = new StatusMenu.StatusMenuButton();
-        this._userMenu.actor.name = 'panelStatus';
-        this._rightBox.add(this._userMenu.actor);
-
-        // Synchronize the buttons pseudo classes with its corner
-        this._userMenu.actor.connect('style-changed', Lang.bind(this,
-            function(actor) {
-                let rtl = actor.get_direction() == St.TextDirection.RTL;
-                let corner = rtl ? this._leftCorner : this._rightCorner;
-                let pseudoClass = actor.get_style_pseudo_class();
-                corner.actor.set_style_pseudo_class(pseudoClass);
-            }));
+        if (global.session_type == Shell.SessionType.USER) {
+            this._userMenu = new StatusMenu.StatusMenuButton();
+            this._userMenu.actor.name = 'panelStatus';
+            this._rightBox.add(this._userMenu.actor);
+
+            // Synchronize the buttons pseudo classes with its corner
+            this._userMenu.actor.connect('style-changed', Lang.bind(this,
+                function(actor) {
+                    let rtl = actor.get_direction() == St.TextDirection.RTL;
+                    let corner = rtl ? this._leftCorner : this._rightCorner;
+                    let pseudoClass = actor.get_style_pseudo_class();
+                    corner.actor.set_style_pseudo_class(pseudoClass);
+                }));
+        }
 
         Main.statusIconDispatcher.connect('status-icon-added', Lang.bind(this, this._onTrayIconAdded));
         Main.statusIconDispatcher.connect('status-icon-removed', Lang.bind(this, this._onTrayIconRemoved));
 
-        // TODO: decide what to do with the rest of the panel in the Overview mode (make it fade-out, become non-reactive, etc.)
-        // We get into the Overview mode on button-press-event as opposed to button-release-event because eventually we'll probably
-        // have the Overview act like a menu that allows the user to release the mouse on the activity the user wants
-        // to switch to.
-        this.button.connect('clicked', Lang.bind(this, function(b) {
-            if (!Main.overview.animationInProgress) {
-                this._hotCorner.maybeToggleOverviewOnClick();
-                return true;
-            } else {
-                return false;
-            }
-        }));
-        // In addition to pressing the button, the Overview can be entered and exited by other means, such as
-        // pressing the System key, Alt+F1 or Esc. We want the button to be pressed in when the Overview is entered
-        // and to be released when it is exited regardless of how it was triggered.
-        Main.overview.connect('showing', Lang.bind(this, function() {
-            this.button.checked = true;
-        }));
-        Main.overview.connect('hiding', Lang.bind(this, function() {
-            this.button.checked = false;
-        }));
+        if (global.session_type == Shell.SessionType.USER) {
+            // TODO: decide what to do with the rest of the panel in the Overview mode (make it fade-out, become non-reactive, etc.)
+            // We get into the Overview mode on button-press-event as opposed to button-release-event because eventually we'll probably
+            // have the Overview act like a menu that allows the user to release the mouse on the activity the user wants
+            // to switch to.
+            this.button.connect('clicked', Lang.bind(this, function(b) {
+                if (!Main.overview.animationInProgress) {
+                    this._hotCorner.maybeToggleOverviewOnClick();
+                    return true;
+                } else {
+                    return false;
+                }
+            }));
+        }
+
+        if (global.session_type == Shell.SessionType.USER) {
+            // In addition to pressing the button, the Overview can be entered and exited by other means, such as
+            // pressing the System key, Alt+F1 or Esc. We want the button to be pressed in when the Overview is entered
+            // and to be released when it is exited regardless of how it was triggered.
+            Main.overview.connect('showing', Lang.bind(this, function() {
+                this.button.checked = true;
+            }));
+            Main.overview.connect('hiding', Lang.bind(this, function() {
+                this.button.checked = false;
+            }));
+        }
 
         Main.chrome.addActor(this.actor);
         Main.chrome.addActor(this._leftCorner.actor, { affectsStruts: false,
@@ -1046,9 +1080,9 @@ Panel.prototype = {
     },
 
     startStatusArea: function() {
-        for (let i = 0; i < STANDARD_TRAY_ICON_ORDER.length; i++) {
-            let role = STANDARD_TRAY_ICON_ORDER[i];
-            let constructor = STANDARD_TRAY_ICON_SHELL_IMPLEMENTATION[role];
+        for (let i = 0; i < this._tray_icon_order.length; i++) {
+            let role = this._tray_icon_order[i];
+            let constructor = this._tray_icon_shell_implementation[role];
             if (!constructor) {
                 // This icon is not implemented (this is a bug)
                 continue;
@@ -1062,7 +1096,9 @@ Panel.prototype = {
 
         // PopupMenuManager depends on menus being added in order for
         // keyboard navigation
-        this._menus.addMenu(this._userMenu.menu);
+        if (this._userMenu) {
+            this._menus.addMenu(this._userMenu.menu);
+        }
     },
 
     startupAnimation: function() {
@@ -1116,13 +1152,13 @@ Panel.prototype = {
     _onTrayIconAdded: function(o, icon, role) {
         icon.height = PANEL_ICON_SIZE;
 
-        if (STANDARD_TRAY_ICON_SHELL_IMPLEMENTATION[role]) {
+        if (this._tray_icon_shell_implementation[role]) {
             // This icon is legacy, and replaced by a Shell version
             // Hide it
             return;
         }
         // Figure out the index in our well-known order for this icon
-        let position = STANDARD_TRAY_ICON_ORDER.indexOf(role);
+        let position = this._tray_icon_order.indexOf(role);
         icon._rolePosition = position;
         let children = this._trayBox.get_children();
         let i;
diff --git a/js/ui/status/accessibility.js b/js/ui/status/accessibility.js
index 560042c..f1ffbf9 100644
--- a/js/ui/status/accessibility.js
+++ b/js/ui/status/accessibility.js
@@ -87,12 +87,14 @@ ATIndicator.prototype = {
         let mouseKeys = this._buildItem(_("Mouse Keys"), A11Y_SCHEMA, KEY_MOUSE_KEYS_ENABLED);
         this.menu.addMenuItem(mouseKeys);
 
-        this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
-        this.menu.addAction(_("Universal Access Settings"), function() {
-            Main.overview.hide();
-            let app = Shell.AppSystem.get_default().get_app('gnome-universal-access-panel.desktop');
-            app.activate(-1);
-        });
+        if (global.session_type == Shell.SessionType.USER) {
+            this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
+            this.menu.addAction(_("Universal Access Settings"), function() {
+                Main.overview.hide();
+                let app = Shell.AppSystem.get_default().get_app('gnome-universal-access-panel.desktop');
+                app.activate(-1);
+            });
+        }
     },
 
     _buildItemExtended: function(string, initial_value, writable, on_set) {
diff --git a/js/ui/status/keyboard.js b/js/ui/status/keyboard.js
index 3048da0..37af3a8 100644
--- a/js/ui/status/keyboard.js
+++ b/js/ui/status/keyboard.js
@@ -67,16 +67,18 @@ XKBIndicator.prototype = {
 
         this._sync_config();
 
-        this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
-        this.menu.addAction(_("Show Keyboard Layout..."), Lang.bind(this, function() {
-            Main.overview.hide();
-            Util.spawn(['gkbd-keyboard-display', '-g', String(this._config.get_current_group() + 1)]);
-        }));
-        this.menu.addAction(_("Localization Settings"), function() {
-            Main.overview.hide();
-            let app = Shell.AppSystem.get_default().get_app('gnome-region-panel.desktop');
-            app.activate(-1);
-        });
+        if (global.session_type == Shell.SessionType.USER) {
+            this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
+            this.menu.addAction(_("Show Keyboard Layout..."), Lang.bind(this, function() {
+                Main.overview.hide();
+                Util.spawn(['gkbd-keyboard-display', '-g', String(this._config.get_current_group() + 1)]);
+            }));
+            this.menu.addAction(_("Localization Settings"), function() {
+                Main.overview.hide();
+                let app = Shell.AppSystem.get_default().get_app('gnome-region-panel.desktop');
+                app.activate(-1);
+            });
+        }
     },
 
     _sync_config: function() {
diff --git a/js/ui/status/power.js b/js/ui/status/power.js
index a64a2ba..1453ff1 100644
--- a/js/ui/status/power.js
+++ b/js/ui/status/power.js
@@ -78,13 +78,14 @@ Indicator.prototype = {
         this._deviceSep = new PopupMenu.PopupSeparatorMenuItem();
         this.menu.addMenuItem(this._deviceSep);
         this._otherDevicePosition = 2;
-        this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
 
-        this.menu.addAction(_("Power Settings"),function() {
-            Main.overview.hide();
-            let app = Shell.AppSystem.get_default().get_app('gnome-power-panel.desktop');
-            app.activate(-1);
-        });
+        if (global.session_type == Shell.SessionType.USER) {
+            this.menu.addAction(_("Power Settings"),function() {
+                Main.overview.hide();
+                let app = Shell.AppSystem.get_default().get_app('gnome-power-panel.desktop');
+                app.activate(-1);
+            });
+        }
 
         this._proxy.connect('Changed', Lang.bind(this, this._devicesChanged));
         this._devicesChanged();
diff --git a/js/ui/status/volume.js b/js/ui/status/volume.js
index bd74cd9..bd669fe 100644
--- a/js/ui/status/volume.js
+++ b/js/ui/status/volume.js
@@ -60,12 +60,14 @@ Indicator.prototype = {
         this.menu.addMenuItem(this._inputTitle);
         this.menu.addMenuItem(this._inputSlider);
 
-        this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
-        this.menu.addAction(_("Sound Settings"), function() {
-            Main.overview.hide();
-            let app = Shell.AppSystem.get_default().get_app('gnome-sound-panel.desktop');
-            app.activate(-1);
-        });
+        if (global.session_type == Shell.SessionType.USER) {
+            this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
+            this.menu.addAction(_("Sound Settings"), function() {
+                Main.overview.hide();
+                let app = Shell.AppSystem.get_default().get_app('gnome-sound-panel.desktop');
+                app.activate(-1);
+            });
+        }
 
         this.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
         this._control.open();
diff --git a/js/ui/windowManager.js b/js/ui/windowManager.js
index e9c1192..554b83d 100644
--- a/js/ui/windowManager.js
+++ b/js/ui/windowManager.js
@@ -123,14 +123,16 @@ WindowManager.prototype = {
         this.setKeybindingHandler('switch_group_backward', Lang.bind(this, this._startAppSwitcher));
         this.setKeybindingHandler('switch_panels', Lang.bind(this, this._startA11ySwitcher));
 
-        Main.overview.connect('showing', Lang.bind(this, function() {
-            for (let i = 0; i < this._dimmedWindows.length; i++)
-                this._undimWindow(this._dimmedWindows[i], true);
-        }));
-        Main.overview.connect('hiding', Lang.bind(this, function() {
-            for (let i = 0; i < this._dimmedWindows.length; i++)
-                this._dimWindow(this._dimmedWindows[i], true);
-        }));
+        if (Main.overview) {
+            Main.overview.connect('showing', Lang.bind(this, function() {
+                for (let i = 0; i < this._dimmedWindows.length; i++)
+                    this._undimWindow(this._dimmedWindows[i], true);
+            }));
+            Main.overview.connect('hiding', Lang.bind(this, function() {
+                for (let i = 0; i < this._dimmedWindows.length; i++)
+                    this._dimWindow(this._dimmedWindows[i], true);
+            }));
+        }
     },
 
     setKeybindingHandler: function(keybinding, handler){
@@ -152,7 +154,7 @@ WindowManager.prototype = {
     },
 
     _shouldAnimate : function(actor) {
-        if (Main.overview.visible || this._animationsBlocked > 0)
+        if ((Main.overview && Main.overview.visible) || this._animationsBlocked > 0)
             return false;
         if (actor && (actor.meta_window.get_window_type() != Meta.WindowType.NORMAL))
             return false;
@@ -252,14 +254,14 @@ WindowManager.prototype = {
         if (shouldDim && !window._dimmed) {
             window._dimmed = true;
             this._dimmedWindows.push(window);
-            if (!Main.overview.visible)
+            if (!Main.overview || !Main.overview.visible)
                 this._dimWindow(window, true);
         } else if (!shouldDim && window._dimmed) {
             window._dimmed = false;
             this._dimmedWindows = this._dimmedWindows.filter(function(win) {
                                                                  return win != window;
                                                              });
-            if (!Main.overview.visible)
+            if (!Main.overview || !Main.overview.visible)
                 this._undimWindow(window, true);
         }
     },
@@ -575,7 +577,7 @@ WindowManager.prototype = {
         if (indexToActivate != activeWorkspaceIndex)
             global.screen.get_workspace_by_index(indexToActivate).activate(global.get_current_time());
 
-        if (!Main.overview.visible)
+        if (!Main.overview || !Main.overview.visible)
             this._workspaceSwitcherPopup.display(WorkspaceSwitcherPopup.UP, indexToActivate);
     },
 
@@ -591,7 +593,7 @@ WindowManager.prototype = {
         if (indexToActivate != activeWorkspaceIndex)
             global.screen.get_workspace_by_index(indexToActivate).activate(global.get_current_time());
 
-        if (!Main.overview.visible)
+        if (!Main.overview || !Main.overview.visible)
             this._workspaceSwitcherPopup.display(WorkspaceSwitcherPopup.DOWN, indexToActivate);
     },
 
@@ -604,7 +606,7 @@ WindowManager.prototype = {
         if (indexToActivate != activeWorkspaceIndex)
             global.screen.get_workspace_by_index(indexToActivate).activate(global.get_current_time());
 
-        if (!Main.overview.visible)
+        if (!Main.overview || !Main.overview.visible)
             this._workspaceSwitcherPopup.display(WorkspaceSwitcherPopup.UP, indexToActivate);
     },
 
@@ -617,7 +619,7 @@ WindowManager.prototype = {
         if (indexToActivate != activeWorkspaceIndex)
             global.screen.get_workspace_by_index(indexToActivate).activate(global.get_current_time());
 
-        if (!Main.overview.visible)
+        if (!Main.overview || !Main.overview.visible)
             this._workspaceSwitcherPopup.display(WorkspaceSwitcherPopup.DOWN, indexToActivate);
     }
 };
diff --git a/src/st/st-adjustment.c b/src/st/st-adjustment.c
index 43fb615..c519aac 100644
--- a/src/st/st-adjustment.c
+++ b/src/st/st-adjustment.c
@@ -702,6 +702,15 @@ interpolation_completed_cb (ClutterTimeline *timeline,
    }
  */
 
+/**
+ * st_adjustment_interpolate:
+ * @adjustment: a #StAdjustment
+ * @alpha: (allow-none): a #ClutterAlpha to control interpolation
+ * @value: end value
+ * @duration: how long to animate before reaching @value
+ *
+ * Animate to @value over the course of @duration milliseconds.
+ */
 void
 st_adjustment_interpolate (StAdjustment *adjustment,
                            ClutterAlpha *alpha,



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