~darkxst/ubuntu/quantal/gnome-shell/lp1128804

« back to all changes in this revision

Viewing changes to .pc/ubuntu-lightdm-user-switching.patch/js/ui/userMenu.js

  • Committer: Package Import Robot
  • Author(s): Jeremy Bicha
  • Date: 2012-11-26 15:57:51 UTC
  • mfrom: (1.1.46)
  • Revision ID: package-import@ubuntu.com-20121126155751-5unrgmmtkc7mtkoz
Tags: 3.6.2-0ubuntu0.1
* New upstream release. (LP: #1078155)
  - Fixes disable-user-list option (LP: #1072838)
  - Many other bugfixes
* debian/control.in
  - Bump minimum mutter
  - Build-depend on gtk-doc-tools to generate updated man page
* debian/patches/ubuntu_screensaver_fallback.patch:
  - Dropped, applied in new version
* debian/patches/ubuntu_lock_on_suspend.patch
  - Refreshed
* debian/patches/ubuntu-lightdm-user-switching.patch:
  - Restored. Accidentally dropped earlier but it just needed to
    be refreshed (LP: #1064269)
* debian/patches/git-set-ally-wm-theme.patch:
  - Git patch to set/reset HighContrast window theme too

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 
2
 
 
3
const AccountsService = imports.gi.AccountsService;
 
4
const Gdm = imports.gi.Gdm;
 
5
const Gio = imports.gi.Gio;
 
6
const GLib = imports.gi.GLib;
 
7
const Lang = imports.lang;
 
8
const Pango = imports.gi.Pango;
 
9
const Shell = imports.gi.Shell;
 
10
const St = imports.gi.St;
 
11
const Tp = imports.gi.TelepathyGLib;
 
12
const UPowerGlib = imports.gi.UPowerGlib;
 
13
const Atk = imports.gi.Atk;
 
14
 
 
15
const BoxPointer = imports.ui.boxpointer;
 
16
const GnomeSession = imports.misc.gnomeSession;
 
17
const Main = imports.ui.main;
 
18
const PanelMenu = imports.ui.panelMenu;
 
19
const PopupMenu = imports.ui.popupMenu;
 
20
const Params = imports.misc.params;
 
21
const Util = imports.misc.util;
 
22
 
 
23
const LOCKDOWN_SCHEMA = 'org.gnome.desktop.lockdown';
 
24
const SCREENSAVER_SCHEMA = 'org.gnome.desktop.screensaver';
 
25
const DISABLE_USER_SWITCH_KEY = 'disable-user-switching';
 
26
const DISABLE_LOCK_SCREEN_KEY = 'disable-lock-screen';
 
27
const DISABLE_LOG_OUT_KEY = 'disable-log-out';
 
28
const LOCK_ENABLED_KEY = 'lock-enabled';
 
29
const ALWAYS_SHOW_LOG_OUT_KEY = 'always-show-log-out';
 
30
 
 
31
const DIALOG_ICON_SIZE = 64;
 
32
 
 
33
const IMStatus = {
 
34
    AVAILABLE: 0,
 
35
    BUSY: 1,
 
36
    HIDDEN: 2,
 
37
    AWAY: 3,
 
38
    IDLE: 4,
 
39
    OFFLINE: 5,
 
40
    LAST: 6
 
41
};
 
42
 
 
43
// Adapted from gdm/gui/user-switch-applet/applet.c
 
44
//
 
45
// Copyright (C) 2004-2005 James M. Cape <jcape@ignore-your.tv>.
 
46
// Copyright (C) 2008,2009 Red Hat, Inc.
 
47
 
 
48
const UserAvatarWidget = new Lang.Class({
 
49
    Name: 'UserAvatarWidget',
 
50
 
 
51
    _init: function(user, params) {
 
52
        this._user = user;
 
53
        params = Params.parse(params, { reactive: false,
 
54
                                        iconSize: DIALOG_ICON_SIZE,
 
55
                                        styleClass: 'status-chooser-user-icon' });
 
56
        this._iconSize = params.iconSize;
 
57
 
 
58
        this.actor = new St.Bin({ style_class: params.styleClass,
 
59
                                  track_hover: params.reactive,
 
60
                                  reactive: params.reactive });
 
61
    },
 
62
 
 
63
    setSensitive: function(sensitive) {
 
64
        this.actor.can_focus = sensitive;
 
65
        this.actor.reactive = sensitive;
 
66
    },
 
67
 
 
68
    update: function() {
 
69
        let iconFile = this._user.get_icon_file();
 
70
        if (!GLib.file_test(iconFile, GLib.FileTest.EXISTS))
 
71
            iconFile = null;
 
72
 
 
73
        if (iconFile) {
 
74
            let file = Gio.File.new_for_path(iconFile);
 
75
            this.actor.child = null;
 
76
            this.actor.style = 'background-image: url("%s");'.format(iconFile);
 
77
 
 
78
            // AccountsService uses a fixed location for avatar images, so
 
79
            // we need to clear out all cached data to pick up image changes,
 
80
            // see https://bugzilla.gnome.org/show_bug.cgi?id=679268
 
81
            this.actor.clear_background_image();
 
82
        } else {
 
83
            this.actor.style = null;
 
84
            this.actor.child = new St.Icon({ icon_name: 'avatar-default-symbolic',
 
85
                                             icon_size: this._iconSize });
 
86
        }
 
87
    }
 
88
});
 
89
 
 
90
const IMStatusItem = new Lang.Class({
 
91
    Name: 'IMStatusItem',
 
92
    Extends: PopupMenu.PopupBaseMenuItem,
 
93
 
 
94
    _init: function(label, iconName) {
 
95
        this.parent();
 
96
 
 
97
        this.actor.add_style_class_name('status-chooser-status-item');
 
98
 
 
99
        this._icon = new St.Icon({ style_class: 'popup-menu-icon' });
 
100
        this.addActor(this._icon);
 
101
 
 
102
        if (iconName)
 
103
            this._icon.icon_name = iconName;
 
104
 
 
105
        this.label = new St.Label({ text: label });
 
106
        this.actor.label_actor = this.label;
 
107
        this.addActor(this.label);
 
108
    }
 
109
});
 
110
 
 
111
const IMUserNameItem = new Lang.Class({
 
112
    Name: 'IMUserNameItem',
 
113
    Extends: PopupMenu.PopupBaseMenuItem,
 
114
 
 
115
    _init: function() {
 
116
        this.parent({ reactive: false,
 
117
                      can_focus: false,
 
118
                      style_class: 'status-chooser-user-name' });
 
119
 
 
120
        this._wrapper = new Shell.GenericContainer();
 
121
        this._wrapper.connect('get-preferred-width',
 
122
                              Lang.bind(this, this._wrapperGetPreferredWidth));
 
123
        this._wrapper.connect('get-preferred-height',
 
124
                              Lang.bind(this, this._wrapperGetPreferredHeight));
 
125
        this._wrapper.connect('allocate',
 
126
                              Lang.bind(this, this._wrapperAllocate));
 
127
        this.addActor(this._wrapper, { expand: true, span: -1 });
 
128
 
 
129
        this.label = new St.Label();
 
130
        this.label.clutter_text.set_line_wrap(true);
 
131
        this.label.clutter_text.set_ellipsize(Pango.EllipsizeMode.NONE);
 
132
        this._wrapper.add_actor(this.label);
 
133
    },
 
134
 
 
135
    _wrapperGetPreferredWidth: function(actor, forHeight, alloc) {
 
136
        alloc.min_size = 1;
 
137
        alloc.natural_size = 1;
 
138
    },
 
139
 
 
140
    _wrapperGetPreferredHeight: function(actor, forWidth, alloc) {
 
141
        [alloc.min_size, alloc.natural_size] = this.label.get_preferred_height(forWidth);
 
142
    },
 
143
 
 
144
    _wrapperAllocate: function(actor, box, flags) {
 
145
        this.label.allocate(box, flags);
 
146
    }
 
147
});
 
148
 
 
149
const IMStatusChooserItem = new Lang.Class({
 
150
    Name: 'IMStatusChooserItem',
 
151
    Extends: PopupMenu.PopupBaseMenuItem,
 
152
 
 
153
    _init: function() {
 
154
        this.parent({ reactive: false,
 
155
                      can_focus: false,
 
156
                      style_class: 'status-chooser' });
 
157
 
 
158
        this._userManager = AccountsService.UserManager.get_default();
 
159
        this._user = this._userManager.get_user(GLib.get_user_name());
 
160
 
 
161
        this._avatar = new UserAvatarWidget(this._user, { reactive: true });
 
162
        this._iconBin = new St.Button({ child: this._avatar.actor });
 
163
        this.addActor(this._iconBin);
 
164
 
 
165
        this._iconBin.connect('clicked', Lang.bind(this,
 
166
            function() {
 
167
                this.activate();
 
168
            }));
 
169
 
 
170
        this._section = new PopupMenu.PopupMenuSection();
 
171
        this.addActor(this._section.actor);
 
172
 
 
173
        this._name = new IMUserNameItem();
 
174
        this._section.addMenuItem(this._name);
 
175
 
 
176
        this._combo = new PopupMenu.PopupComboBoxMenuItem({ style_class: 'status-chooser-combo' });
 
177
        this._section.addMenuItem(this._combo);
 
178
 
 
179
        let item;
 
180
 
 
181
        item = new IMStatusItem(_("Available"), 'user-available-symbolic');
 
182
        this._combo.addMenuItem(item, IMStatus.AVAILABLE);
 
183
 
 
184
        item = new IMStatusItem(_("Busy"), 'user-busy-symbolic');
 
185
        this._combo.addMenuItem(item, IMStatus.BUSY);
 
186
 
 
187
        item = new IMStatusItem(_("Invisible"), 'user-invisible-symbolic');
 
188
        this._combo.addMenuItem(item, IMStatus.HIDDEN);
 
189
 
 
190
        item = new IMStatusItem(_("Away"), 'user-away-symbolic');
 
191
        this._combo.addMenuItem(item, IMStatus.AWAY);
 
192
 
 
193
        item = new IMStatusItem(_("Idle"), 'user-idle-symbolic');
 
194
        this._combo.addMenuItem(item, IMStatus.IDLE);
 
195
 
 
196
        item = new IMStatusItem(_("Unavailable"), 'user-offline-symbolic');
 
197
        this._combo.addMenuItem(item, IMStatus.OFFLINE);
 
198
 
 
199
        this._combo.connect('active-item-changed',
 
200
                            Lang.bind(this, this._changeIMStatus));
 
201
 
 
202
        this._presence = new GnomeSession.Presence();
 
203
        this._presence.connectSignal('StatusChanged', Lang.bind(this, function(proxy, senderName, [status]) {
 
204
            this._sessionStatusChanged(status);
 
205
        }));
 
206
 
 
207
        this._sessionPresenceRestored = false;
 
208
        this._imPresenceRestored = false;
 
209
        this._currentPresence = undefined;
 
210
 
 
211
        this._accountMgr = Tp.AccountManager.dup();
 
212
        this._accountMgr.connect('most-available-presence-changed',
 
213
                                 Lang.bind(this, this._IMStatusChanged));
 
214
        this._accountMgr.connect('account-enabled',
 
215
                                 Lang.bind(this, this._IMAccountsChanged));
 
216
        this._accountMgr.connect('account-disabled',
 
217
                                 Lang.bind(this, this._IMAccountsChanged));
 
218
        this._accountMgr.connect('account-removed',
 
219
                                 Lang.bind(this, this._IMAccountsChanged));
 
220
        this._accountMgr.connect('account-validity-changed',
 
221
                                 Lang.bind(this, this._IMAccountsChanged));
 
222
        this._accountMgr.prepare_async(null, Lang.bind(this,
 
223
            function(mgr) {
 
224
                this._IMAccountsChanged(mgr);
 
225
 
 
226
                if (this._networkMonitor.network_available)
 
227
                    this._restorePresence();
 
228
                else
 
229
                    this._setComboboxPresence(Tp.ConnectionPresenceType.OFFLINE);
 
230
            }));
 
231
 
 
232
        this._networkMonitor = Gio.NetworkMonitor.get_default();
 
233
        this._networkMonitor.connect('network-changed',
 
234
            Lang.bind(this, function(monitor, available) {
 
235
                this._IMAccountsChanged(this._accountMgr);
 
236
 
 
237
                if (available && !this._imPresenceRestored)
 
238
                    this._restorePresence();
 
239
            }));
 
240
 
 
241
        this._userLoadedId = this._user.connect('notify::is-loaded',
 
242
                                                Lang.bind(this,
 
243
                                                          this._updateUser));
 
244
        this._userChangedId = this._user.connect('changed',
 
245
                                                 Lang.bind(this,
 
246
                                                           this._updateUser));
 
247
        this.actor.connect('notify::mapped', Lang.bind(this, function() {
 
248
            if (this.actor.mapped)
 
249
                this._updateUser();
 
250
        }));
 
251
 
 
252
        this.connect('sensitive-changed', function(sensitive) {
 
253
            this._avatar.setSensitive(sensitive);
 
254
        });
 
255
    },
 
256
 
 
257
    _restorePresence: function() {
 
258
        let [presence, status, msg] = this._accountMgr.get_most_available_presence();
 
259
 
 
260
        let savedPresence = global.settings.get_int('saved-im-presence');
 
261
 
 
262
        if (savedPresence == presence) {
 
263
            this._IMStatusChanged(this._accountMgr, presence, status, msg);
 
264
        } else {
 
265
            this._setComboboxPresence(savedPresence);
 
266
            status = this._statusForPresence(savedPresence);
 
267
            msg = msg ? msg : '';
 
268
            this._accountMgr.set_all_requested_presences(savedPresence, status, msg);
 
269
        }
 
270
    },
 
271
 
 
272
    destroy: function() {
 
273
        // clean up signal handlers
 
274
        if (this._userLoadedId != 0) {
 
275
            this._user.disconnect(this._userLoadedId);
 
276
            this._userLoadedId = 0;
 
277
        }
 
278
 
 
279
        if (this._userChangedId != 0) {
 
280
            this._user.disconnect(this._userChangedId);
 
281
            this._userChangedId = 0;
 
282
        }
 
283
 
 
284
        this.parent();
 
285
    },
 
286
 
 
287
    // Override getColumnWidths()/setColumnWidths() to make the item
 
288
    // independent from the overall column layout of the menu
 
289
    getColumnWidths: function() {
 
290
        return [];
 
291
    },
 
292
 
 
293
    setColumnWidths: function(widths) {
 
294
    },
 
295
 
 
296
    _updateUser: function() {
 
297
        if (this._user.is_loaded)
 
298
            this._name.label.set_text(this._user.get_real_name());
 
299
        else
 
300
            this._name.label.set_text("");
 
301
 
 
302
        this._avatar.update();
 
303
    },
 
304
 
 
305
    _statusForPresence: function(presence) {
 
306
        switch(presence) {
 
307
            case Tp.ConnectionPresenceType.AVAILABLE:
 
308
                return 'available';
 
309
            case Tp.ConnectionPresenceType.BUSY:
 
310
                return 'busy';
 
311
            case Tp.ConnectionPresenceType.OFFLINE:
 
312
                return 'offline';
 
313
            case Tp.ConnectionPresenceType.HIDDEN:
 
314
                return 'hidden';
 
315
            case Tp.ConnectionPresenceType.AWAY:
 
316
                return 'away';
 
317
            case Tp.ConnectionPresenceType.EXTENDED_AWAY:
 
318
                return 'xa';
 
319
            default:
 
320
                return 'unknown';
 
321
        }
 
322
    },
 
323
 
 
324
    _IMAccountsChanged: function(mgr) {
 
325
        let accounts = mgr.get_valid_accounts().filter(function(account) {
 
326
            return account.enabled;
 
327
        });
 
328
        let sensitive = accounts.length > 0 && this._networkMonitor.network_available;
 
329
        this._combo.setSensitive(sensitive);
 
330
    },
 
331
 
 
332
    _IMStatusChanged: function(accountMgr, presence, status, message) {
 
333
        if (!this._imPresenceRestored)
 
334
            this._imPresenceRestored = true;
 
335
 
 
336
        if (presence == this._currentPresence)
 
337
            return;
 
338
 
 
339
        this._currentPresence = presence;
 
340
        this._setComboboxPresence(presence);
 
341
 
 
342
        if (!this._sessionPresenceRestored) {
 
343
            this._sessionStatusChanged(this._presence.status);
 
344
            return;
 
345
        }
 
346
 
 
347
        if (presence == Tp.ConnectionPresenceType.AVAILABLE)
 
348
            this._presence.status = GnomeSession.PresenceStatus.AVAILABLE;
 
349
 
 
350
        // We ignore the actual value of _expectedPresence and never safe
 
351
        // the first presence change after an "automatic" change, assuming
 
352
        // that it is the response to our request; this is to account for
 
353
        // mission control falling back to "similar" presences if an account
 
354
        // type does not implement the requested presence.
 
355
        if (!this._expectedPresence)
 
356
            global.settings.set_int('saved-im-presence', presence);
 
357
        else
 
358
            this._expectedPresence = undefined;
 
359
    },
 
360
 
 
361
    _setComboboxPresence: function(presence) {
 
362
        let activatedItem;
 
363
 
 
364
        if (presence == Tp.ConnectionPresenceType.AVAILABLE)
 
365
            activatedItem = IMStatus.AVAILABLE;
 
366
        else if (presence == Tp.ConnectionPresenceType.BUSY)
 
367
            activatedItem = IMStatus.BUSY;
 
368
        else if (presence == Tp.ConnectionPresenceType.HIDDEN)
 
369
            activatedItem = IMStatus.HIDDEN;
 
370
        else if (presence == Tp.ConnectionPresenceType.AWAY)
 
371
            activatedItem = IMStatus.AWAY;
 
372
        else if (presence == Tp.ConnectionPresenceType.EXTENDED_AWAY)
 
373
            activatedItem = IMStatus.IDLE;
 
374
        else
 
375
            activatedItem = IMStatus.OFFLINE;
 
376
 
 
377
        this._combo.setActiveItem(activatedItem);
 
378
        for (let i = 0; i < IMStatus.LAST; i++) {
 
379
            if (i == IMStatus.AVAILABLE || i == IMStatus.OFFLINE)
 
380
                continue;   // always visible
 
381
 
 
382
            this._combo.setItemVisible(i, i == activatedItem);
 
383
        }
 
384
    },
 
385
 
 
386
    _changeIMStatus: function(menuItem, id) {
 
387
        let [presence, s, msg] = this._accountMgr.get_most_available_presence();
 
388
        let newPresence, status;
 
389
 
 
390
        if (id == IMStatus.AVAILABLE) {
 
391
            newPresence = Tp.ConnectionPresenceType.AVAILABLE;
 
392
        } else if (id == IMStatus.OFFLINE) {
 
393
            newPresence = Tp.ConnectionPresenceType.OFFLINE;
 
394
        } else
 
395
            return;
 
396
 
 
397
        status = this._statusForPresence(newPresence);
 
398
        msg = msg ? msg : '';
 
399
        this._accountMgr.set_all_requested_presences(newPresence, status, msg);
 
400
    },
 
401
 
 
402
    getIMPresenceForSessionStatus: function(sessionStatus) {
 
403
        // Restore the last user-set presence when coming back from
 
404
        // BUSY/IDLE (otherwise the last user-set presence matches
 
405
        // the current one)
 
406
        if (sessionStatus == GnomeSession.PresenceStatus.AVAILABLE)
 
407
            return global.settings.get_int('saved-im-presence');
 
408
 
 
409
        if (sessionStatus == GnomeSession.PresenceStatus.BUSY) {
 
410
            // Only change presence if the current one is "more present" than
 
411
            // busy, or if coming back from idle
 
412
            if (this._currentPresence == Tp.ConnectionPresenceType.AVAILABLE ||
 
413
                this._currentPresence == Tp.ConnectionPresenceType.EXTENDED_AWAY)
 
414
                return Tp.ConnectionPresenceType.BUSY;
 
415
        }
 
416
 
 
417
        if (sessionStatus == GnomeSession.PresenceStatus.IDLE) {
 
418
            // Only change presence if the current one is "more present" than
 
419
            // idle
 
420
            if (this._currentPresence != Tp.ConnectionPresenceType.OFFLINE &&
 
421
                this._currentPresence != Tp.ConnectionPresenceType.HIDDEN)
 
422
                return Tp.ConnectionPresenceType.EXTENDED_AWAY;
 
423
        }
 
424
 
 
425
        return this._currentPresence;
 
426
    },
 
427
 
 
428
    _sessionStatusChanged: function(sessionStatus) {
 
429
        if (!this._imPresenceRestored)
 
430
            return;
 
431
 
 
432
        let savedStatus = global.settings.get_int('saved-session-presence');
 
433
        if (!this._sessionPresenceRestored) {
 
434
 
 
435
            // We should never save/restore a status other than AVAILABLE
 
436
            // or BUSY
 
437
            if (savedStatus != GnomeSession.PresenceStatus.AVAILABLE &&
 
438
                savedStatus != GnomeSession.PresenceStatus.BUSY)
 
439
                savedStatus = GnomeSession.PresenceStatus.AVAILABLE;
 
440
 
 
441
            if (sessionStatus != savedStatus) {
 
442
                this._presence.status = savedStatus;
 
443
                return;
 
444
            }
 
445
            this._sessionPresenceRestored = true;
 
446
        }
 
447
 
 
448
        if ((sessionStatus == GnomeSession.PresenceStatus.AVAILABLE ||
 
449
             sessionStatus == GnomeSession.PresenceStatus.BUSY) &&
 
450
            savedStatus != sessionStatus)
 
451
            global.settings.set_int('saved-session-presence', sessionStatus);
 
452
 
 
453
        let [presence, s, msg] = this._accountMgr.get_most_available_presence();
 
454
        let newPresence, status;
 
455
 
 
456
        let newPresence = this.getIMPresenceForSessionStatus(sessionStatus);
 
457
 
 
458
        if (!newPresence || newPresence == presence)
 
459
            return;
 
460
 
 
461
        status = this._statusForPresence(newPresence);
 
462
        msg = msg ? msg : '';
 
463
 
 
464
        this._expectedPresence = newPresence;
 
465
        this._accountMgr.set_all_requested_presences(newPresence, status, msg);
 
466
    }
 
467
});
 
468
 
 
469
 
 
470
const UserMenuButton = new Lang.Class({
 
471
    Name: 'UserMenuButton',
 
472
    Extends: PanelMenu.Button,
 
473
 
 
474
    _init: function() {
 
475
        this.parent(0.0);
 
476
 
 
477
        this.actor.accessible_role = Atk.Role.MENU;
 
478
 
 
479
        let box = new St.BoxLayout({ name: 'panelUserMenu' });
 
480
        this.actor.add_actor(box);
 
481
 
 
482
        this._screenSaverSettings = new Gio.Settings({ schema: SCREENSAVER_SCHEMA });
 
483
        this._lockdownSettings = new Gio.Settings({ schema: LOCKDOWN_SCHEMA });
 
484
 
 
485
        this._userManager = AccountsService.UserManager.get_default();
 
486
 
 
487
        this._user = this._userManager.get_user(GLib.get_user_name());
 
488
        this._presence = new GnomeSession.Presence();
 
489
        this._session = new GnomeSession.SessionManager();
 
490
        this._haveShutdown = true;
 
491
 
 
492
        this._accountMgr = Tp.AccountManager.dup();
 
493
 
 
494
        this._upClient = new UPowerGlib.Client();
 
495
        this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
 
496
 
 
497
        this._iconBox = new St.Bin();
 
498
        box.add(this._iconBox, { y_align: St.Align.MIDDLE, y_fill: false });
 
499
 
 
500
        let textureCache = St.TextureCache.get_default();
 
501
        this._offlineIcon = new St.Icon({ icon_name: 'user-offline-symbolic',
 
502
                                          style_class: 'popup-menu-icon' });
 
503
        this._availableIcon = new St.Icon({ icon_name: 'user-available-symbolic',
 
504
                                            style_class: 'popup-menu-icon' });
 
505
        this._busyIcon = new St.Icon({ icon_name: 'user-busy-symbolic',
 
506
                                       style_class: 'popup-menu-icon' });
 
507
        this._invisibleIcon = new St.Icon({ icon_name: 'user-invisible-symbolic',
 
508
                                            style_class: 'popup-menu-icon' });
 
509
        this._awayIcon = new St.Icon({ icon_name: 'user-away-symbolic',
 
510
                                       style_class: 'popup-menu-icon' });
 
511
        this._idleIcon = new St.Icon({ icon_name: 'user-idle-symbolic',
 
512
                                       style_class: 'popup-menu-icon' });
 
513
        this._pendingIcon = new St.Icon({ icon_name: 'user-status-pending-symbolic',
 
514
                                          style_class: 'popup-menu-icon' });
 
515
        this._lockedIcon = new St.Icon({ icon_name: 'changes-prevent-symbolic',
 
516
                                         style_class: 'popup-menu-icon' });
 
517
 
 
518
        this._accountMgr.connect('most-available-presence-changed',
 
519
                                  Lang.bind(this, this._updatePresenceIcon));
 
520
        this._accountMgr.connect('account-enabled',
 
521
                                  Lang.bind(this, this._onAccountEnabled));
 
522
        this._accountMgr.connect('account-removed',
 
523
                                  Lang.bind(this, this._onAccountRemoved));
 
524
        this._accountMgr.prepare_async(null, Lang.bind(this,
 
525
            function(mgr) {
 
526
                let [presence, s, msg] = mgr.get_most_available_presence();
 
527
                this._updatePresenceIcon(mgr, presence, s, msg);
 
528
                this._setupAccounts();
 
529
            }));
 
530
 
 
531
        this._name = new St.Label();
 
532
        this.actor.label_actor = this._name;
 
533
        box.add(this._name, { y_align: St.Align.MIDDLE, y_fill: false });
 
534
        this._userLoadedId = this._user.connect('notify::is-loaded', Lang.bind(this, this._updateUserName));
 
535
        this._userChangedId = this._user.connect('changed', Lang.bind(this, this._updateUserName));
 
536
        this._updateUserName();
 
537
 
 
538
        this._createSubMenu();
 
539
 
 
540
        this._updateSwitch(this._presence.status);
 
541
        this._presence.connectSignal('StatusChanged', Lang.bind(this, function (proxy, senderName, [status]) {
 
542
            this._updateSwitch(status);
 
543
        }));
 
544
 
 
545
        this._userManager.connect('notify::is-loaded',
 
546
                                  Lang.bind(this, this._updateMultiUser));
 
547
        this._userManager.connect('notify::has-multiple-users',
 
548
                                  Lang.bind(this, this._updateMultiUser));
 
549
        this._userManager.connect('user-added',
 
550
                                  Lang.bind(this, this._updateMultiUser));
 
551
        this._userManager.connect('user-removed',
 
552
                                  Lang.bind(this, this._updateMultiUser));
 
553
        this._lockdownSettings.connect('changed::' + DISABLE_USER_SWITCH_KEY,
 
554
                                       Lang.bind(this, this._updateSwitchUser));
 
555
        this._lockdownSettings.connect('changed::' + DISABLE_LOG_OUT_KEY,
 
556
                                       Lang.bind(this, this._updateLogout));
 
557
 
 
558
        this._lockdownSettings.connect('changed::' + DISABLE_LOCK_SCREEN_KEY,
 
559
                                       Lang.bind(this, this._updateLockScreen));
 
560
        this._updateSwitchUser();
 
561
        this._updateLogout();
 
562
        this._updateLockScreen();
 
563
 
 
564
        this._updatesFile = Gio.File.new_for_path('/var/lib/PackageKit/prepared-update');
 
565
        this._updatesMonitor = this._updatesFile.monitor(Gio.FileMonitorFlags.NONE, null);
 
566
        this._updatesMonitor.connect('changed', Lang.bind(this, this._updateInstallUpdates));
 
567
 
 
568
        // Whether shutdown is available or not depends on both lockdown
 
569
        // settings (disable-log-out) and Polkit policy - the latter doesn't
 
570
        // notify, so we update the menu item each time the menu opens or
 
571
        // the lockdown setting changes, which should be close enough.
 
572
        this.menu.connect('open-state-changed', Lang.bind(this,
 
573
            function(menu, open) {
 
574
                if (open)
 
575
                    this._updateHaveShutdown();
 
576
            }));
 
577
        this._lockdownSettings.connect('changed::' + DISABLE_LOG_OUT_KEY,
 
578
                                       Lang.bind(this, this._updateHaveShutdown));
 
579
 
 
580
        this._upClient.connect('notify::can-suspend', Lang.bind(this, this._updateSuspendOrPowerOff));
 
581
 
 
582
        Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
 
583
        this._sessionUpdated();
 
584
    },
 
585
 
 
586
    _sessionUpdated: function() {
 
587
        this.actor.visible = !Main.sessionMode.isGreeter;
 
588
 
 
589
        let allowSettings = Main.sessionMode.allowSettings;
 
590
        this._statusChooser.setSensitive(allowSettings);
 
591
        this._systemSettings.visible = allowSettings;
 
592
 
 
593
        this.setSensitive(!Main.sessionMode.isLocked);
 
594
        this._updatePresenceIcon();
 
595
    },
 
596
 
 
597
    _onDestroy: function() {
 
598
        this._user.disconnect(this._userLoadedId);
 
599
        this._user.disconnect(this._userChangedId);
 
600
    },
 
601
 
 
602
    _updateUserName: function() {
 
603
        if (this._user.is_loaded)
 
604
            this._name.set_text(this._user.get_real_name());
 
605
        else
 
606
            this._name.set_text("");
 
607
    },
 
608
 
 
609
    _updateMultiUser: function() {
 
610
        this._updateSwitchUser();
 
611
        this._updateLogout();
 
612
    },
 
613
 
 
614
    _updateSwitchUser: function() {
 
615
        let allowSwitch = !this._lockdownSettings.get_boolean(DISABLE_USER_SWITCH_KEY);
 
616
        let multiUser = this._userManager.can_switch() && this._userManager.has_multiple_users;
 
617
 
 
618
        this._loginScreenItem.actor.visible = allowSwitch && multiUser;
 
619
    },
 
620
 
 
621
    _updateLogout: function() {
 
622
        let allowLogout = !this._lockdownSettings.get_boolean(DISABLE_LOG_OUT_KEY);
 
623
        let alwaysShow = global.settings.get_boolean(ALWAYS_SHOW_LOG_OUT_KEY);
 
624
        let systemAccount = this._user.system_account;
 
625
        let localAccount = this._user.local_account;
 
626
        let multiUser = this._userManager.has_multiple_users;
 
627
        let multiSession = Gdm.get_session_ids().length > 1;
 
628
 
 
629
        this._logoutItem.actor.visible = allowLogout && (alwaysShow || multiUser || multiSession || systemAccount || !localAccount);
 
630
    },
 
631
 
 
632
    _updateLockScreen: function() {
 
633
        let allowLockScreen = !this._lockdownSettings.get_boolean(DISABLE_LOCK_SCREEN_KEY);
 
634
        this._lockScreenItem.actor.visible = allowLockScreen;
 
635
    },
 
636
 
 
637
    _updateInstallUpdates: function() {
 
638
        let haveUpdates = this._updatesFile.query_exists(null);
 
639
        this._installUpdatesItem.actor.visible = haveUpdates && this._haveShutdown;
 
640
    },
 
641
 
 
642
    _updateHaveShutdown: function() {
 
643
        this._session.CanShutdownRemote(Lang.bind(this,
 
644
            function(result, error) {
 
645
                if (!error) {
 
646
                    this._haveShutdown = result[0];
 
647
                    this._updateInstallUpdates();
 
648
                    this._updateSuspendOrPowerOff();
 
649
                }
 
650
            }));
 
651
    },
 
652
 
 
653
    _updateSuspendOrPowerOff: function() {
 
654
        this._haveSuspend = this._upClient.get_can_suspend();
 
655
 
 
656
        if (!this._suspendOrPowerOffItem)
 
657
            return;
 
658
 
 
659
        this._suspendOrPowerOffItem.actor.visible = this._haveShutdown || this._haveSuspend;
 
660
 
 
661
        // If we can't power off show Suspend instead
 
662
        // and disable the alt key
 
663
        if (!this._haveShutdown) {
 
664
            this._suspendOrPowerOffItem.updateText(_("Suspend"), null);
 
665
        } else if (!this._haveSuspend) {
 
666
            this._suspendOrPowerOffItem.updateText(_("Power Off"), null);
 
667
        } else {
 
668
            this._suspendOrPowerOffItem.updateText(_("Power Off"), _("Suspend"));
 
669
        }
 
670
    },
 
671
 
 
672
    _updateSwitch: function(status) {
 
673
        let active = status == GnomeSession.PresenceStatus.AVAILABLE;
 
674
        this._notificationsSwitch.setToggleState(active);
 
675
    },
 
676
 
 
677
    _updatePresenceIcon: function(accountMgr, presence, status, message) {
 
678
        if (Main.sessionMode.isLocked)
 
679
            this._iconBox.child = this._lockedIcon;
 
680
        else if (presence == Tp.ConnectionPresenceType.AVAILABLE)
 
681
            this._iconBox.child = this._availableIcon;
 
682
        else if (presence == Tp.ConnectionPresenceType.BUSY)
 
683
            this._iconBox.child = this._busyIcon;
 
684
        else if (presence == Tp.ConnectionPresenceType.HIDDEN)
 
685
            this._iconBox.child = this._invisibleIcon;
 
686
        else if (presence == Tp.ConnectionPresenceType.AWAY)
 
687
            this._iconBox.child = this._awayIcon;
 
688
        else if (presence == Tp.ConnectionPresenceType.EXTENDED_AWAY)
 
689
            this._iconBox.child = this._idleIcon;
 
690
        else
 
691
            this._iconBox.child = this._offlineIcon;
 
692
    },
 
693
 
 
694
    _setupAccounts: function() {
 
695
        let accounts = this._accountMgr.get_valid_accounts();
 
696
        for (let i = 0; i < accounts.length; i++) {
 
697
            accounts[i]._changingId = accounts[i].connect('notify::connection-status',
 
698
                                                          Lang.bind(this, this._updateChangingPresence));
 
699
        }
 
700
        this._updateChangingPresence();
 
701
    },
 
702
 
 
703
    _onAccountEnabled: function(accountMgr, account) {
 
704
        if (!account._changingId)
 
705
            account._changingId = account.connect('notify::connection-status',
 
706
                                                  Lang.bind(this, this._updateChangingPresence));
 
707
        this._updateChangingPresence();
 
708
    },
 
709
 
 
710
    _onAccountRemoved: function(accountMgr, account) {
 
711
        if (account._changingId) {
 
712
            account.disconnect(account._changingId);
 
713
            account._changingId = 0;
 
714
        }
 
715
        this._updateChangingPresence();
 
716
    },
 
717
 
 
718
    _updateChangingPresence: function() {
 
719
        let accounts = this._accountMgr.get_valid_accounts();
 
720
        let changing = false;
 
721
        for (let i = 0; i < accounts.length; i++) {
 
722
            if (accounts[i].connection_status == Tp.ConnectionStatus.CONNECTING) {
 
723
                changing = true;
 
724
                break;
 
725
            }
 
726
        }
 
727
 
 
728
        if (changing) {
 
729
            this._iconBox.child = this._pendingIcon;
 
730
        } else {
 
731
            let [presence, s, msg] = this._accountMgr.get_most_available_presence();
 
732
            this._updatePresenceIcon(this._accountMgr, presence, s, msg);
 
733
        }
 
734
    },
 
735
 
 
736
    _createSubMenu: function() {
 
737
        let item;
 
738
 
 
739
        item = new IMStatusChooserItem();
 
740
        item.connect('activate', Lang.bind(this, this._onMyAccountActivate));
 
741
        this.menu.addMenuItem(item);
 
742
        this._statusChooser = item;
 
743
 
 
744
        item = new PopupMenu.PopupSwitchMenuItem(_("Notifications"));
 
745
        item.connect('toggled', Lang.bind(this, this._updatePresenceStatus));
 
746
        this.menu.addMenuItem(item);
 
747
        this._notificationsSwitch = item;
 
748
 
 
749
        item = new PopupMenu.PopupSeparatorMenuItem();
 
750
        this.menu.addMenuItem(item);
 
751
 
 
752
        item = new PopupMenu.PopupMenuItem(_("System Settings"));
 
753
        item.connect('activate', Lang.bind(this, this._onPreferencesActivate));
 
754
        this.menu.addMenuItem(item);
 
755
        this._systemSettings = item;
 
756
 
 
757
        item = new PopupMenu.PopupSeparatorMenuItem();
 
758
        this.menu.addMenuItem(item);
 
759
 
 
760
        item = new PopupMenu.PopupMenuItem(_("Switch User"));
 
761
        item.connect('activate', Lang.bind(this, this._onLoginScreenActivate));
 
762
        this.menu.addMenuItem(item);
 
763
        this._loginScreenItem = item;
 
764
 
 
765
        item = new PopupMenu.PopupMenuItem(_("Log Out"));
 
766
        item.connect('activate', Lang.bind(this, this._onQuitSessionActivate));
 
767
        this.menu.addMenuItem(item);
 
768
        this._logoutItem = item;
 
769
 
 
770
        item = new PopupMenu.PopupMenuItem(_("Lock"));
 
771
        item.connect('activate', Lang.bind(this, this._onLockScreenActivate));
 
772
        this.menu.addMenuItem(item);
 
773
        this._lockScreenItem = item;
 
774
 
 
775
        item = new PopupMenu.PopupSeparatorMenuItem();
 
776
        this.menu.addMenuItem(item);
 
777
 
 
778
        item = new PopupMenu.PopupAlternatingMenuItem(_("Power Off"),
 
779
                                                      _("Suspend"));
 
780
        this.menu.addMenuItem(item);
 
781
        item.connect('activate', Lang.bind(this, this._onSuspendOrPowerOffActivate));
 
782
        this._suspendOrPowerOffItem = item;
 
783
        this._updateSuspendOrPowerOff();
 
784
 
 
785
        item = new PopupMenu.PopupMenuItem(_("Install Updates & Restart"));
 
786
        item.connect('activate', Lang.bind(this, this._onInstallUpdatesActivate));
 
787
        this.menu.addMenuItem(item);
 
788
        this._installUpdatesItem = item;
 
789
    },
 
790
 
 
791
    _updatePresenceStatus: function(item, event) {
 
792
        let status;
 
793
 
 
794
        if (item.state) {
 
795
            status = GnomeSession.PresenceStatus.AVAILABLE;
 
796
        } else {
 
797
            status = GnomeSession.PresenceStatus.BUSY;
 
798
 
 
799
            let [presence, s, msg] = this._accountMgr.get_most_available_presence();
 
800
            let newPresence = this._statusChooser.getIMPresenceForSessionStatus(status);
 
801
            if (newPresence != presence &&
 
802
                newPresence == Tp.ConnectionPresenceType.BUSY)
 
803
                Main.notify(_("Your chat status will be set to busy"),
 
804
                            _("Notifications are now disabled, including chat messages. Your online status has been adjusted to let others know that you might not see their messages."));
 
805
        }
 
806
 
 
807
        this._presence.status = status;
 
808
    },
 
809
 
 
810
    _onMyAccountActivate: function() {
 
811
        Main.overview.hide();
 
812
        let app = Shell.AppSystem.get_default().lookup_setting('gnome-user-accounts-panel.desktop');
 
813
        app.activate();
 
814
    },
 
815
 
 
816
    _onPreferencesActivate: function() {
 
817
        Main.overview.hide();
 
818
        let app = Shell.AppSystem.get_default().lookup_app('gnome-control-center.desktop');
 
819
        app.activate();
 
820
    },
 
821
 
 
822
    _onLockScreenActivate: function() {
 
823
        this.menu.close(BoxPointer.PopupAnimation.NONE);
 
824
        Main.overview.hide();
 
825
        Main.screenShield.lock(true);
 
826
    },
 
827
 
 
828
    _onLoginScreenActivate: function() {
 
829
        this.menu.close(BoxPointer.PopupAnimation.NONE);
 
830
        Main.overview.hide();
 
831
        Main.screenShield.lock(false);
 
832
        Gdm.goto_login_session_sync(null);
 
833
    },
 
834
 
 
835
    _onQuitSessionActivate: function() {
 
836
        Main.overview.hide();
 
837
        this._session.LogoutRemote(0);
 
838
    },
 
839
 
 
840
    _onInstallUpdatesActivate: function() {
 
841
        Main.overview.hide();
 
842
        Util.spawn(['pkexec', '/usr/libexec/pk-trigger-offline-update']);
 
843
 
 
844
        this._session.RebootRemote();
 
845
    },
 
846
 
 
847
    _onSuspendOrPowerOffActivate: function() {
 
848
        Main.overview.hide();
 
849
 
 
850
        if (this._haveShutdown &&
 
851
            this._suspendOrPowerOffItem.state == PopupMenu.PopupAlternatingMenuItemState.DEFAULT) {
 
852
            this._session.ShutdownRemote();
 
853
        } else {
 
854
            if (this._screenSaverSettings.get_boolean(LOCK_ENABLED_KEY)) {
 
855
                let tmpId = Main.screenShield.connect('lock-screen-shown', Lang.bind(this, function() {
 
856
                    Main.screenShield.disconnect(tmpId);
 
857
 
 
858
                    this._upClient.suspend_sync(null);
 
859
                }));
 
860
 
 
861
                this.menu.close(BoxPointer.PopupAnimation.NONE);
 
862
                Main.screenShield.lock(true);
 
863
            } else {
 
864
                this._upClient.suspend_sync(null);
 
865
            }
 
866
        }
 
867
    }
 
868
});