1
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
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;
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;
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';
31
const DIALOG_ICON_SIZE = 64;
43
// Adapted from gdm/gui/user-switch-applet/applet.c
45
// Copyright (C) 2004-2005 James M. Cape <jcape@ignore-your.tv>.
46
// Copyright (C) 2008,2009 Red Hat, Inc.
48
const UserAvatarWidget = new Lang.Class({
49
Name: 'UserAvatarWidget',
51
_init: function(user, params) {
53
params = Params.parse(params, { reactive: false,
54
iconSize: DIALOG_ICON_SIZE,
55
styleClass: 'status-chooser-user-icon' });
56
this._iconSize = params.iconSize;
58
this.actor = new St.Bin({ style_class: params.styleClass,
59
track_hover: params.reactive,
60
reactive: params.reactive });
63
setSensitive: function(sensitive) {
64
this.actor.can_focus = sensitive;
65
this.actor.reactive = sensitive;
69
let iconFile = this._user.get_icon_file();
70
if (!GLib.file_test(iconFile, GLib.FileTest.EXISTS))
74
let file = Gio.File.new_for_path(iconFile);
75
this.actor.child = null;
76
this.actor.style = 'background-image: url("%s");'.format(iconFile);
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();
83
this.actor.style = null;
84
this.actor.child = new St.Icon({ icon_name: 'avatar-default-symbolic',
85
icon_size: this._iconSize });
90
const IMStatusItem = new Lang.Class({
92
Extends: PopupMenu.PopupBaseMenuItem,
94
_init: function(label, iconName) {
97
this.actor.add_style_class_name('status-chooser-status-item');
99
this._icon = new St.Icon({ style_class: 'popup-menu-icon' });
100
this.addActor(this._icon);
103
this._icon.icon_name = iconName;
105
this.label = new St.Label({ text: label });
106
this.actor.label_actor = this.label;
107
this.addActor(this.label);
111
const IMUserNameItem = new Lang.Class({
112
Name: 'IMUserNameItem',
113
Extends: PopupMenu.PopupBaseMenuItem,
116
this.parent({ reactive: false,
118
style_class: 'status-chooser-user-name' });
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 });
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);
135
_wrapperGetPreferredWidth: function(actor, forHeight, alloc) {
137
alloc.natural_size = 1;
140
_wrapperGetPreferredHeight: function(actor, forWidth, alloc) {
141
[alloc.min_size, alloc.natural_size] = this.label.get_preferred_height(forWidth);
144
_wrapperAllocate: function(actor, box, flags) {
145
this.label.allocate(box, flags);
149
const IMStatusChooserItem = new Lang.Class({
150
Name: 'IMStatusChooserItem',
151
Extends: PopupMenu.PopupBaseMenuItem,
154
this.parent({ reactive: false,
156
style_class: 'status-chooser' });
158
this._userManager = AccountsService.UserManager.get_default();
159
this._user = this._userManager.get_user(GLib.get_user_name());
161
this._avatar = new UserAvatarWidget(this._user, { reactive: true });
162
this._iconBin = new St.Button({ child: this._avatar.actor });
163
this.addActor(this._iconBin);
165
this._iconBin.connect('clicked', Lang.bind(this,
170
this._section = new PopupMenu.PopupMenuSection();
171
this.addActor(this._section.actor);
173
this._name = new IMUserNameItem();
174
this._section.addMenuItem(this._name);
176
this._combo = new PopupMenu.PopupComboBoxMenuItem({ style_class: 'status-chooser-combo' });
177
this._section.addMenuItem(this._combo);
181
item = new IMStatusItem(_("Available"), 'user-available-symbolic');
182
this._combo.addMenuItem(item, IMStatus.AVAILABLE);
184
item = new IMStatusItem(_("Busy"), 'user-busy-symbolic');
185
this._combo.addMenuItem(item, IMStatus.BUSY);
187
item = new IMStatusItem(_("Invisible"), 'user-invisible-symbolic');
188
this._combo.addMenuItem(item, IMStatus.HIDDEN);
190
item = new IMStatusItem(_("Away"), 'user-away-symbolic');
191
this._combo.addMenuItem(item, IMStatus.AWAY);
193
item = new IMStatusItem(_("Idle"), 'user-idle-symbolic');
194
this._combo.addMenuItem(item, IMStatus.IDLE);
196
item = new IMStatusItem(_("Unavailable"), 'user-offline-symbolic');
197
this._combo.addMenuItem(item, IMStatus.OFFLINE);
199
this._combo.connect('active-item-changed',
200
Lang.bind(this, this._changeIMStatus));
202
this._presence = new GnomeSession.Presence();
203
this._presence.connectSignal('StatusChanged', Lang.bind(this, function(proxy, senderName, [status]) {
204
this._sessionStatusChanged(status);
207
this._sessionPresenceRestored = false;
208
this._imPresenceRestored = false;
209
this._currentPresence = undefined;
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,
224
this._IMAccountsChanged(mgr);
226
if (this._networkMonitor.network_available)
227
this._restorePresence();
229
this._setComboboxPresence(Tp.ConnectionPresenceType.OFFLINE);
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);
237
if (available && !this._imPresenceRestored)
238
this._restorePresence();
241
this._userLoadedId = this._user.connect('notify::is-loaded',
244
this._userChangedId = this._user.connect('changed',
247
this.actor.connect('notify::mapped', Lang.bind(this, function() {
248
if (this.actor.mapped)
252
this.connect('sensitive-changed', function(sensitive) {
253
this._avatar.setSensitive(sensitive);
257
_restorePresence: function() {
258
let [presence, status, msg] = this._accountMgr.get_most_available_presence();
260
let savedPresence = global.settings.get_int('saved-im-presence');
262
if (savedPresence == presence) {
263
this._IMStatusChanged(this._accountMgr, presence, status, msg);
265
this._setComboboxPresence(savedPresence);
266
status = this._statusForPresence(savedPresence);
267
msg = msg ? msg : '';
268
this._accountMgr.set_all_requested_presences(savedPresence, status, msg);
272
destroy: function() {
273
// clean up signal handlers
274
if (this._userLoadedId != 0) {
275
this._user.disconnect(this._userLoadedId);
276
this._userLoadedId = 0;
279
if (this._userChangedId != 0) {
280
this._user.disconnect(this._userChangedId);
281
this._userChangedId = 0;
287
// Override getColumnWidths()/setColumnWidths() to make the item
288
// independent from the overall column layout of the menu
289
getColumnWidths: function() {
293
setColumnWidths: function(widths) {
296
_updateUser: function() {
297
if (this._user.is_loaded)
298
this._name.label.set_text(this._user.get_real_name());
300
this._name.label.set_text("");
302
this._avatar.update();
305
_statusForPresence: function(presence) {
307
case Tp.ConnectionPresenceType.AVAILABLE:
309
case Tp.ConnectionPresenceType.BUSY:
311
case Tp.ConnectionPresenceType.OFFLINE:
313
case Tp.ConnectionPresenceType.HIDDEN:
315
case Tp.ConnectionPresenceType.AWAY:
317
case Tp.ConnectionPresenceType.EXTENDED_AWAY:
324
_IMAccountsChanged: function(mgr) {
325
let accounts = mgr.get_valid_accounts().filter(function(account) {
326
return account.enabled;
328
let sensitive = accounts.length > 0 && this._networkMonitor.network_available;
329
this._combo.setSensitive(sensitive);
332
_IMStatusChanged: function(accountMgr, presence, status, message) {
333
if (!this._imPresenceRestored)
334
this._imPresenceRestored = true;
336
if (presence == this._currentPresence)
339
this._currentPresence = presence;
340
this._setComboboxPresence(presence);
342
if (!this._sessionPresenceRestored) {
343
this._sessionStatusChanged(this._presence.status);
347
if (presence == Tp.ConnectionPresenceType.AVAILABLE)
348
this._presence.status = GnomeSession.PresenceStatus.AVAILABLE;
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);
358
this._expectedPresence = undefined;
361
_setComboboxPresence: function(presence) {
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;
375
activatedItem = IMStatus.OFFLINE;
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
382
this._combo.setItemVisible(i, i == activatedItem);
386
_changeIMStatus: function(menuItem, id) {
387
let [presence, s, msg] = this._accountMgr.get_most_available_presence();
388
let newPresence, status;
390
if (id == IMStatus.AVAILABLE) {
391
newPresence = Tp.ConnectionPresenceType.AVAILABLE;
392
} else if (id == IMStatus.OFFLINE) {
393
newPresence = Tp.ConnectionPresenceType.OFFLINE;
397
status = this._statusForPresence(newPresence);
398
msg = msg ? msg : '';
399
this._accountMgr.set_all_requested_presences(newPresence, status, msg);
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
406
if (sessionStatus == GnomeSession.PresenceStatus.AVAILABLE)
407
return global.settings.get_int('saved-im-presence');
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;
417
if (sessionStatus == GnomeSession.PresenceStatus.IDLE) {
418
// Only change presence if the current one is "more present" than
420
if (this._currentPresence != Tp.ConnectionPresenceType.OFFLINE &&
421
this._currentPresence != Tp.ConnectionPresenceType.HIDDEN)
422
return Tp.ConnectionPresenceType.EXTENDED_AWAY;
425
return this._currentPresence;
428
_sessionStatusChanged: function(sessionStatus) {
429
if (!this._imPresenceRestored)
432
let savedStatus = global.settings.get_int('saved-session-presence');
433
if (!this._sessionPresenceRestored) {
435
// We should never save/restore a status other than AVAILABLE
437
if (savedStatus != GnomeSession.PresenceStatus.AVAILABLE &&
438
savedStatus != GnomeSession.PresenceStatus.BUSY)
439
savedStatus = GnomeSession.PresenceStatus.AVAILABLE;
441
if (sessionStatus != savedStatus) {
442
this._presence.status = savedStatus;
445
this._sessionPresenceRestored = true;
448
if ((sessionStatus == GnomeSession.PresenceStatus.AVAILABLE ||
449
sessionStatus == GnomeSession.PresenceStatus.BUSY) &&
450
savedStatus != sessionStatus)
451
global.settings.set_int('saved-session-presence', sessionStatus);
453
let [presence, s, msg] = this._accountMgr.get_most_available_presence();
454
let newPresence, status;
456
let newPresence = this.getIMPresenceForSessionStatus(sessionStatus);
458
if (!newPresence || newPresence == presence)
461
status = this._statusForPresence(newPresence);
462
msg = msg ? msg : '';
464
this._expectedPresence = newPresence;
465
this._accountMgr.set_all_requested_presences(newPresence, status, msg);
470
const UserMenuButton = new Lang.Class({
471
Name: 'UserMenuButton',
472
Extends: PanelMenu.Button,
477
this.actor.accessible_role = Atk.Role.MENU;
479
let box = new St.BoxLayout({ name: 'panelUserMenu' });
480
this.actor.add_actor(box);
482
this._screenSaverSettings = new Gio.Settings({ schema: SCREENSAVER_SCHEMA });
483
this._lockdownSettings = new Gio.Settings({ schema: LOCKDOWN_SCHEMA });
485
this._userManager = AccountsService.UserManager.get_default();
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;
492
this._accountMgr = Tp.AccountManager.dup();
494
this._upClient = new UPowerGlib.Client();
495
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
497
this._iconBox = new St.Bin();
498
box.add(this._iconBox, { y_align: St.Align.MIDDLE, y_fill: false });
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' });
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,
526
let [presence, s, msg] = mgr.get_most_available_presence();
527
this._updatePresenceIcon(mgr, presence, s, msg);
528
this._setupAccounts();
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();
538
this._createSubMenu();
540
this._updateSwitch(this._presence.status);
541
this._presence.connectSignal('StatusChanged', Lang.bind(this, function (proxy, senderName, [status]) {
542
this._updateSwitch(status);
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));
558
this._lockdownSettings.connect('changed::' + DISABLE_LOCK_SCREEN_KEY,
559
Lang.bind(this, this._updateLockScreen));
560
this._updateSwitchUser();
561
this._updateLogout();
562
this._updateLockScreen();
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));
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) {
575
this._updateHaveShutdown();
577
this._lockdownSettings.connect('changed::' + DISABLE_LOG_OUT_KEY,
578
Lang.bind(this, this._updateHaveShutdown));
580
this._upClient.connect('notify::can-suspend', Lang.bind(this, this._updateSuspendOrPowerOff));
582
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
583
this._sessionUpdated();
586
_sessionUpdated: function() {
587
this.actor.visible = !Main.sessionMode.isGreeter;
589
let allowSettings = Main.sessionMode.allowSettings;
590
this._statusChooser.setSensitive(allowSettings);
591
this._systemSettings.visible = allowSettings;
593
this.setSensitive(!Main.sessionMode.isLocked);
594
this._updatePresenceIcon();
597
_onDestroy: function() {
598
this._user.disconnect(this._userLoadedId);
599
this._user.disconnect(this._userChangedId);
602
_updateUserName: function() {
603
if (this._user.is_loaded)
604
this._name.set_text(this._user.get_real_name());
606
this._name.set_text("");
609
_updateMultiUser: function() {
610
this._updateSwitchUser();
611
this._updateLogout();
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;
618
this._loginScreenItem.actor.visible = allowSwitch && multiUser;
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;
629
this._logoutItem.actor.visible = allowLogout && (alwaysShow || multiUser || multiSession || systemAccount || !localAccount);
632
_updateLockScreen: function() {
633
let allowLockScreen = !this._lockdownSettings.get_boolean(DISABLE_LOCK_SCREEN_KEY);
634
this._lockScreenItem.actor.visible = allowLockScreen;
637
_updateInstallUpdates: function() {
638
let haveUpdates = this._updatesFile.query_exists(null);
639
this._installUpdatesItem.actor.visible = haveUpdates && this._haveShutdown;
642
_updateHaveShutdown: function() {
643
this._session.CanShutdownRemote(Lang.bind(this,
644
function(result, error) {
646
this._haveShutdown = result[0];
647
this._updateInstallUpdates();
648
this._updateSuspendOrPowerOff();
653
_updateSuspendOrPowerOff: function() {
654
this._haveSuspend = this._upClient.get_can_suspend();
656
if (!this._suspendOrPowerOffItem)
659
this._suspendOrPowerOffItem.actor.visible = this._haveShutdown || this._haveSuspend;
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);
668
this._suspendOrPowerOffItem.updateText(_("Power Off"), _("Suspend"));
672
_updateSwitch: function(status) {
673
let active = status == GnomeSession.PresenceStatus.AVAILABLE;
674
this._notificationsSwitch.setToggleState(active);
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;
691
this._iconBox.child = this._offlineIcon;
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));
700
this._updateChangingPresence();
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();
710
_onAccountRemoved: function(accountMgr, account) {
711
if (account._changingId) {
712
account.disconnect(account._changingId);
713
account._changingId = 0;
715
this._updateChangingPresence();
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) {
729
this._iconBox.child = this._pendingIcon;
731
let [presence, s, msg] = this._accountMgr.get_most_available_presence();
732
this._updatePresenceIcon(this._accountMgr, presence, s, msg);
736
_createSubMenu: function() {
739
item = new IMStatusChooserItem();
740
item.connect('activate', Lang.bind(this, this._onMyAccountActivate));
741
this.menu.addMenuItem(item);
742
this._statusChooser = item;
744
item = new PopupMenu.PopupSwitchMenuItem(_("Notifications"));
745
item.connect('toggled', Lang.bind(this, this._updatePresenceStatus));
746
this.menu.addMenuItem(item);
747
this._notificationsSwitch = item;
749
item = new PopupMenu.PopupSeparatorMenuItem();
750
this.menu.addMenuItem(item);
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;
757
item = new PopupMenu.PopupSeparatorMenuItem();
758
this.menu.addMenuItem(item);
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;
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;
770
item = new PopupMenu.PopupMenuItem(_("Lock"));
771
item.connect('activate', Lang.bind(this, this._onLockScreenActivate));
772
this.menu.addMenuItem(item);
773
this._lockScreenItem = item;
775
item = new PopupMenu.PopupSeparatorMenuItem();
776
this.menu.addMenuItem(item);
778
item = new PopupMenu.PopupAlternatingMenuItem(_("Power Off"),
780
this.menu.addMenuItem(item);
781
item.connect('activate', Lang.bind(this, this._onSuspendOrPowerOffActivate));
782
this._suspendOrPowerOffItem = item;
783
this._updateSuspendOrPowerOff();
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;
791
_updatePresenceStatus: function(item, event) {
795
status = GnomeSession.PresenceStatus.AVAILABLE;
797
status = GnomeSession.PresenceStatus.BUSY;
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."));
807
this._presence.status = status;
810
_onMyAccountActivate: function() {
811
Main.overview.hide();
812
let app = Shell.AppSystem.get_default().lookup_setting('gnome-user-accounts-panel.desktop');
816
_onPreferencesActivate: function() {
817
Main.overview.hide();
818
let app = Shell.AppSystem.get_default().lookup_app('gnome-control-center.desktop');
822
_onLockScreenActivate: function() {
823
this.menu.close(BoxPointer.PopupAnimation.NONE);
824
Main.overview.hide();
825
Main.screenShield.lock(true);
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);
835
_onQuitSessionActivate: function() {
836
Main.overview.hide();
837
this._session.LogoutRemote(0);
840
_onInstallUpdatesActivate: function() {
841
Main.overview.hide();
842
Util.spawn(['pkexec', '/usr/libexec/pk-trigger-offline-update']);
844
this._session.RebootRemote();
847
_onSuspendOrPowerOffActivate: function() {
848
Main.overview.hide();
850
if (this._haveShutdown &&
851
this._suspendOrPowerOffItem.state == PopupMenu.PopupAlternatingMenuItemState.DEFAULT) {
852
this._session.ShutdownRemote();
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);
858
this._upClient.suspend_sync(null);
861
this.menu.close(BoxPointer.PopupAnimation.NONE);
862
Main.screenShield.lock(true);
864
this._upClient.suspend_sync(null);