1
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
3
* Copyright 2011 Red Hat, Inc
5
* This program is free software; you can redistribute it and/or modify
6
* it under the terms of the GNU General Public License as published by
7
* the Free Software Foundation; either version 2, or (at your option)
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License
16
* along with this program; if not, write to the Free Software
17
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
21
const AccountsService = imports.gi.AccountsService;
22
const Clutter = imports.gi.Clutter;
23
const CtrlAltTab = imports.ui.ctrlAltTab;
24
const Gio = imports.gi.Gio;
25
const GLib = imports.gi.GLib;
26
const Gtk = imports.gi.Gtk;
27
const Mainloop = imports.mainloop;
28
const Meta = imports.gi.Meta;
29
const Lang = imports.lang;
30
const Pango = imports.gi.Pango;
31
const Signals = imports.signals;
32
const Shell = imports.gi.Shell;
33
const St = imports.gi.St;
34
const Gdm = imports.gi.Gdm;
36
const Batch = imports.gdm.batch;
37
const Fprint = imports.gdm.fingerprint;
38
const GdmUtil = imports.gdm.util;
39
const Lightbox = imports.ui.lightbox;
40
const Main = imports.ui.main;
41
const ModalDialog = imports.ui.modalDialog;
42
const Tweener = imports.ui.tweener;
43
const UserMenu = imports.ui.userMenu;
45
const _RESIZE_ANIMATION_TIME = 0.25;
46
const _SCROLL_ANIMATION_TIME = 0.5;
47
const _TIMED_LOGIN_IDLE_THRESHOLD = 5.0;
48
const _LOGO_ICON_NAME_SIZE = 48;
50
let _loginDialog = null;
52
function _smoothlyResizeActor(actor, width, height) {
57
finalWidth = actor.width;
62
finalHeight = actor.height;
66
actor.set_size(actor.width, actor.height);
68
if (actor.width == finalWidth && actor.height == finalHeight)
71
let hold = new Batch.Hold();
73
Tweener.addTween(actor,
76
time: _RESIZE_ANIMATION_TIME,
77
transition: 'easeOutQuad',
78
onComplete: Lang.bind(this, function() {
85
const UserListItem = new Lang.Class({
88
_init: function(user) {
90
this._userChangedId = this.user.connect('changed',
91
Lang.bind(this, this._onUserChanged));
93
let layout = new St.BoxLayout({ vertical: false });
94
this.actor = new St.Button({ style_class: 'login-dialog-user-list-item',
98
x_align: St.Align.START,
101
this._userAvatar = new UserMenu.UserAvatarWidget(this.user,
102
{ styleClass: 'login-dialog-user-list-item-icon' });
103
layout.add(this._userAvatar.actor);
104
let textLayout = new St.BoxLayout({ style_class: 'login-dialog-user-list-item-text-box',
106
layout.add(textLayout, { expand: true });
108
this._nameLabel = new St.Label({ style_class: 'login-dialog-user-list-item-name' });
109
this.actor.label_actor = this._nameLabel;
110
textLayout.add(this._nameLabel,
112
y_align: St.Align.MIDDLE,
115
this._timedLoginIndicator = new St.Bin({ style_class: 'login-dialog-timed-login-indicator',
117
textLayout.add(this._timedLoginIndicator,
119
x_align: St.Align.MIDDLE,
121
y_align: St.Align.END });
123
this.actor.connect('clicked', Lang.bind(this, this._onClicked));
124
this._onUserChanged();
127
_onUserChanged: function() {
128
this._nameLabel.set_text(this.user.get_real_name());
129
this._userAvatar.update();
130
this._updateLoggedIn();
133
syncStyleClasses: function() {
134
this._updateLoggedIn();
136
if (global.stage.get_key_focus() == this.actor)
137
this.actor.add_style_pseudo_class('focus');
139
this.actor.remove_style_pseudo_class('focus');
142
_updateLoggedIn: function() {
143
if (this.user.is_logged_in())
144
this.actor.add_style_pseudo_class('logged-in');
146
this.actor.remove_style_pseudo_class('logged-in');
149
_onClicked: function() {
150
this.emit('activate');
153
showTimedLoginIndicator: function(time) {
154
let hold = new Batch.Hold();
156
this.hideTimedLoginIndicator();
157
Tweener.addTween(this._timedLoginIndicator,
160
transition: 'linear',
161
onComplete: function() {
164
onCompleteScope: this
169
hideTimedLoginIndicator: function() {
170
Tweener.removeTweens(this._timedLoginIndicator);
171
this._timedLoginIndicator.scale_x = 0.;
174
Signals.addSignalMethods(UserListItem.prototype);
176
const UserList = new Lang.Class({
180
this.actor = new St.ScrollView({ style_class: 'login-dialog-user-list-view'});
181
this.actor.set_policy(Gtk.PolicyType.NEVER,
182
Gtk.PolicyType.AUTOMATIC);
184
this._box = new St.BoxLayout({ vertical: true,
185
style_class: 'login-dialog-user-list',
186
pseudo_class: 'expanded' });
188
this.actor.add_actor(this._box);
191
this.actor.connect('key-focus-in', Lang.bind(this, this._moveFocusToItems));
194
_moveFocusToItems: function() {
195
let hasItems = Object.keys(this._items).length > 0;
200
if (global.stage.get_key_focus() != this.actor)
203
let focusSet = this.actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
205
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() {
206
this._moveFocusToItems();
212
_showItem: function(item) {
213
let tasks = [function() {
214
return GdmUtil.fadeInActor(item.actor);
217
let batch = new Batch.ConsecutiveBatch(this, tasks);
221
_onItemActivated: function(activatedItem) {
222
this.emit('activate', activatedItem);
225
giveUpWhitespace: function() {
226
let container = this.actor.get_parent();
228
container.child_set(this.actor, { expand: false });
231
takeOverWhitespace: function() {
232
let container = this.actor.get_parent();
234
container.child_set(this.actor, { expand: true });
237
pinInPlace: function() {
238
this._box.set_size(this._box.width, this._box.height);
241
shrinkToNaturalHeight: function() {
242
let oldWidth = this._box.width;
243
let oldHeight = this._box.height;
244
this._box.set_size(-1, -1);
245
let [minHeight, naturalHeight] = this._box.get_preferred_height(-1);
246
this._box.set_size(oldWidth, oldHeight);
248
let batch = new Batch.ConsecutiveBatch(this,
250
return _smoothlyResizeActor(this._box, -1, naturalHeight);
254
this._box.set_size(-1, -1);
261
hideItemsExcept: function(exception) {
264
for (let userName in this._items) {
265
let item = this._items[userName];
267
item.actor.set_hover(false);
268
item.actor.reactive = false;
269
item.actor.can_focus = false;
270
item.syncStyleClasses();
271
item._timedLoginIndicator.scale_x = 0.;
272
if (item != exception)
273
tasks.push(function() {
274
return GdmUtil.fadeOutActor(item.actor);
278
let batch = new Batch.ConsecutiveBatch(this,
280
return GdmUtil.fadeOutActor(this.actor.vscroll);
283
new Batch.ConcurrentBatch(this, tasks),
286
this._box.remove_style_pseudo_class('expanded');
293
hideItems: function() {
294
return this.hideItemsExcept(null);
297
_getExpandedHeight: function() {
298
let hiddenActors = [];
299
for (let userName in this._items) {
300
let item = this._items[userName];
301
if (!item.actor.visible) {
303
hiddenActors.push(item.actor);
307
if (!this._box.visible) {
309
hiddenActors.push(this._box);
312
this._box.set_size(-1, -1);
313
let [minHeight, naturalHeight] = this._box.get_preferred_height(-1);
315
for (let i = 0; i < hiddenActors.length; i++) {
316
let actor = hiddenActors[i];
320
return naturalHeight;
323
showItems: function() {
326
for (let userName in this._items) {
327
let item = this._items[userName];
328
item.actor.sync_hover();
329
item.actor.reactive = true;
330
item.actor.can_focus = true;
331
item.syncStyleClasses();
332
tasks.push(function() {
333
return this._showItem(item);
337
let batch = new Batch.ConsecutiveBatch(this,
339
this.takeOverWhitespace();
343
let fullHeight = this._getExpandedHeight();
344
return _smoothlyResizeActor(this._box, -1, fullHeight);
348
this._box.add_style_pseudo_class('expanded');
351
new Batch.ConcurrentBatch(this, tasks),
354
this.actor.set_size(-1, -1);
358
return GdmUtil.fadeInActor(this.actor.vscroll);
363
scrollToItem: function(item) {
364
let box = item.actor.get_allocation_box();
366
let adjustment = this.actor.get_vscroll_bar().get_adjustment();
368
let value = (box.y1 + adjustment.step_increment / 2.0) - (adjustment.page_size / 2.0);
369
Tweener.removeTweens(adjustment);
370
Tweener.addTween (adjustment,
372
time: _SCROLL_ANIMATION_TIME,
373
transition: 'easeOutQuad' });
376
jumpToItem: function(item) {
377
let box = item.actor.get_allocation_box();
379
let adjustment = this.actor.get_vscroll_bar().get_adjustment();
381
let value = (box.y1 + adjustment.step_increment / 2.0) - (adjustment.page_size / 2.0);
383
adjustment.set_value(value);
386
getItemFromUserName: function(userName) {
387
let item = this._items[userName];
395
addUser: function(user) {
399
if (user.is_system_account())
405
let userName = user.get_user_name();
410
this.removeUser(user);
412
let item = new UserListItem(user);
413
this._box.add(item.actor, { x_fill: true });
415
this._items[userName] = item;
417
item.connect('activate',
418
Lang.bind(this, this._onItemActivated));
420
// Try to keep the focused item front-and-center
421
item.actor.connect('key-focus-in',
424
this.scrollToItem(item);
427
this._moveFocusToItems();
429
this.emit('item-added', item);
432
removeUser: function(user) {
436
let userName = user.get_user_name();
441
let item = this._items[userName];
446
item.actor.destroy();
447
delete this._items[userName];
450
Signals.addSignalMethods(UserList.prototype);
452
const SessionListItem = new Lang.Class({
453
Name: 'SessionListItem',
455
_init: function(id, name) {
458
this.actor = new St.Button({ style_class: 'login-dialog-session-list-item',
462
x_align: St.Align.START });
464
this._box = new St.BoxLayout({ style_class: 'login-dialog-session-list-item-box' });
466
this.actor.add_actor(this._box);
467
this.actor.connect('clicked', Lang.bind(this, this._onClicked));
469
this._dot = new St.DrawingArea({ style_class: 'login-dialog-session-list-item-dot' });
470
this._dot.connect('repaint', Lang.bind(this, this._onRepaintDot));
471
this._box.add_actor(this._dot);
472
this.setShowDot(false);
474
let label = new St.Label({ style_class: 'login-dialog-session-list-item-label',
476
this.actor.label_actor = label;
478
this._box.add_actor(label);
481
setShowDot: function(show) {
483
this._dot.opacity = 255;
485
this._dot.opacity = 0;
488
_onRepaintDot: function(area) {
489
let cr = area.get_context();
490
let [width, height] = area.get_surface_size();
491
let color = area.get_theme_node().get_foreground_color();
493
cr.setSourceRGBA (color.red / 255,
497
cr.arc(width / 2, height / 2, width / 3, 0, 2 * Math.PI);
501
_onClicked: function() {
502
this.emit('activate');
505
Signals.addSignalMethods(SessionListItem.prototype);
507
const SessionList = new Lang.Class({
511
this.actor = new St.Bin();
513
this._box = new St.BoxLayout({ style_class: 'login-dialog-session-list',
515
this.actor.child = this._box;
517
this._button = new St.Button({ style_class: 'login-dialog-session-list-button',
521
let box = new St.BoxLayout();
522
this._button.add_actor(box);
524
this._triangle = new St.Label({ style_class: 'login-dialog-session-list-triangle',
526
box.add_actor(this._triangle);
528
let label = new St.Label({ style_class: 'login-dialog-session-list-label',
529
text: _("Session...") });
530
box.add_actor(label);
532
this._button.connect('clicked',
533
Lang.bind(this, this._onClicked));
534
this._box.add_actor(this._button);
535
this._scrollView = new St.ScrollView({ style_class: 'login-dialog-session-list-scroll-view'});
536
this._scrollView.set_policy(Gtk.PolicyType.NEVER,
537
Gtk.PolicyType.AUTOMATIC);
538
this._box.add_actor(this._scrollView);
539
this._itemList = new St.BoxLayout({ style_class: 'login-dialog-session-item-list',
541
this._scrollView.add_actor(this._itemList);
542
this._scrollView.hide();
551
this._button.add_style_pseudo_class('open');
552
this._scrollView.show();
553
this._triangle.set_text('\u25BE');
562
this._button.remove_style_pseudo_class('open');
563
this._scrollView.hide();
564
this._triangle.set_text('\u25B8');
569
_onClicked: function() {
576
setActiveSession: function(sessionId) {
577
if (sessionId == this._activeSessionId)
580
if (this._activeSessionId)
581
this._items[this._activeSessionId].setShowDot(false);
583
this._items[sessionId].setShowDot(true);
584
this._activeSessionId = sessionId;
586
this.emit('session-activated', this._activeSessionId);
589
_populate: function() {
590
this._itemList.destroy_all_children();
591
this._activeSessionId = null;
594
let ids = Gdm.get_session_ids();
597
if (ids.length <= 1) {
605
for (let i = 0; i < ids.length; i++) {
606
let [sessionName, sessionDescription] = Gdm.get_session_name_and_description(ids[i]);
608
let item = new SessionListItem(ids[i], sessionName);
609
this._itemList.add_actor(item.actor);
610
this._items[ids[i]] = item;
612
if (!this._activeSessionId)
613
this.setActiveSession(ids[i]);
615
item.connect('activate',
616
Lang.bind(this, function() {
617
this.setActiveSession(item.id);
622
Signals.addSignalMethods(SessionList.prototype);
624
const LoginDialog = new Lang.Class({
626
Extends: ModalDialog.ModalDialog,
628
_init: function(parentActor) {
629
this.parent({ shellReactive: true,
630
styleClass: 'login-dialog',
631
parentActor: parentActor,
632
shouldFadeIn: false });
633
this.connect('destroy',
634
Lang.bind(this, this._onDestroy));
635
this.connect('opened',
636
Lang.bind(this, this._onOpened));
638
this._userManager = AccountsService.UserManager.get_default()
639
this._greeterClient = new Gdm.Client();
641
this._greeter = this._greeterClient.get_greeter_sync(null);
643
this._greeter.connect('default-session-name-changed',
644
Lang.bind(this, this._onDefaultSessionChanged));
646
this._greeter.connect('session-opened',
647
Lang.bind(this, this._onSessionOpened));
648
this._greeter.connect('timed-login-requested',
649
Lang.bind(this, this._onTimedLoginRequested));
651
this._userVerifier = new GdmUtil.ShellUserVerifier(this._greeterClient);
652
this._userVerifier.connect('ask-question', Lang.bind(this, this._askQuestion));
653
this._userVerifier.connect('show-message', Lang.bind(this, this._showMessage));
654
this._userVerifier.connect('reset', Lang.bind(this, this._reset));
655
this._userVerifier.connect('show-login-hint', Lang.bind(this, this._showLoginHint));
656
this._userVerifier.connect('hide-login-hint', Lang.bind(this, this._hideLoginHint));
657
this._verifyingUser = false;
659
this._settings = new Gio.Settings({ schema: GdmUtil.LOGIN_SCREEN_SCHEMA });
661
this._settings.connect('changed::' + GdmUtil.LOGO_KEY,
662
Lang.bind(this, this._updateLogo));
663
this._settings.connect('changed::' + GdmUtil.BANNER_MESSAGE_KEY,
664
Lang.bind(this, this._updateBanner));
665
this._settings.connect('changed::' + GdmUtil.BANNER_MESSAGE_TEXT_KEY,
666
Lang.bind(this, this._updateBanner));
667
this._settings.connect('changed::' + GdmUtil.DISABLE_USER_LIST_KEY,
668
Lang.bind(this, this._updateDisableUserList));
670
this._logoBox = new St.Bin({ style_class: 'login-dialog-logo-box' });
671
this.contentLayout.add(this._logoBox);
674
this._bannerLabel = new St.Label({ style_class: 'login-dialog-banner',
676
this.contentLayout.add(this._bannerLabel);
677
this._updateBanner();
679
this._titleLabel = new St.Label({ style_class: 'login-dialog-title',
680
text: C_("title", "Sign In"),
683
this.contentLayout.add(this._titleLabel,
685
y_align: St.Align.START });
687
this._userList = new UserList();
688
this.contentLayout.add(this._userList.actor,
693
this.setInitialKeyFocus(this._userList.actor);
695
this._promptBox = new St.BoxLayout({ style_class: 'login-dialog-prompt-layout',
697
this.contentLayout.add(this._promptBox,
701
x_align: St.Align.START });
702
this._promptLabel = new St.Label({ style_class: 'login-dialog-prompt-label' });
704
this._promptBox.add(this._promptLabel,
708
x_align: St.Align.START });
709
this._promptEntry = new St.Entry({ style_class: 'login-dialog-prompt-entry',
711
this._promptBox.add(this._promptEntry,
715
x_align: St.Align.START });
717
this._promptMessage = new St.Label({ visible: false });
718
this._promptBox.add(this._promptMessage, { x_fill: true });
720
this._promptLoginHint = new St.Label({ style_class: 'login-dialog-prompt-login-hint-message' });
721
this._promptLoginHint.hide();
722
this._promptBox.add(this._promptLoginHint);
724
this._sessionList = new SessionList();
725
this._sessionList.connect('session-activated',
726
Lang.bind(this, function(list, sessionId) {
727
this._greeter.call_select_session_sync (sessionId, null);
730
this._promptBox.add(this._sessionList.actor,
734
x_align: St.Align.START });
735
this._promptBox.hide();
737
// translators: this message is shown below the user list on the
738
// login screen. It can be activated to reveal an entry for
739
// manually entering the username.
740
let notListedLabel = new St.Label({ text: _("Not listed?"),
741
style_class: 'login-dialog-not-listed-label' });
742
this._notListedButton = new St.Button({ style_class: 'login-dialog-not-listed-button',
744
child: notListedLabel,
746
x_align: St.Align.START,
749
this._notListedButton.connect('clicked', Lang.bind(this, this._hideUserListAndLogIn));
751
this.contentLayout.add(this._notListedButton,
753
x_align: St.Align.START,
756
if (!this._userManager.is_loaded)
757
this._userManagerLoadedId = this._userManager.connect('notify::is-loaded',
758
Lang.bind(this, function() {
759
if (this._userManager.is_loaded) {
760
this._loadUserList();
761
this._userManager.disconnect(this._userManagerLoadedId);
762
this._userManagerLoadedId = 0;
766
this._loadUserList();
768
this._userList.connect('activate',
769
Lang.bind(this, function(userList, item) {
770
this._onUserListActivated(item);
775
_updateDisableUserList: function() {
776
let disableUserList = this._settings.get_boolean(GdmUtil.DISABLE_USER_LIST_KEY);
778
// If this is the first time around, set initial focus
779
if (this._disableUserList == undefined && disableUserList)
780
this.setInitialKeyFocus(this._promptEntry);
782
if (disableUserList != this._disableUserList) {
783
this._disableUserList = disableUserList;
785
if (!this._verifyingUser)
790
_updateLogo: function() {
791
this._logoBox.child = null;
792
let path = this._settings.get_string(GdmUtil.LOGO_KEY);
795
let file = Gio.file_new_for_path(path);
796
let uri = file.get_uri();
798
let textureCache = St.TextureCache.get_default();
799
this._logoBox.child = textureCache.load_uri_async(uri, -1, _LOGO_ICON_NAME_SIZE);
804
_updateBanner: function() {
805
let enabled = this._settings.get_boolean(GdmUtil.BANNER_MESSAGE_KEY);
806
let text = this._settings.get_string(GdmUtil.BANNER_MESSAGE_TEXT_KEY);
808
if (enabled && text) {
809
this._bannerLabel.set_text(text);
810
this._fadeInBanner();
812
this._fadeOutBanner();
817
this._promptMessage.hide();
819
this._verifyingUser = false;
821
if (this._disableUserList)
822
this._hideUserListAndLogIn();
824
this._showUserList();
827
_onDefaultSessionChanged: function(client, sessionId) {
828
this._sessionList.setActiveSession(sessionId);
831
_showMessage: function(userVerifier, message, styleClass) {
833
this._promptMessage.text = message;
834
this._promptMessage.styleClass = styleClass;
835
GdmUtil.fadeInActor(this._promptMessage);
837
GdmUtil.fadeOutActor(this._promptMessage);
841
_showLoginHint: function(verifier, message) {
842
this._promptLoginHint.set_text(message)
843
GdmUtil.fadeInActor(this._promptLoginHint);
846
_hideLoginHint: function() {
847
GdmUtil.fadeOutActor(this._promptLoginHint);
848
this._promptLoginHint.set_text('');
852
this._userVerifier.cancel();
855
_fadeInPrompt: function() {
856
let tasks = [function() {
857
return GdmUtil.fadeInActor(this._promptLabel);
861
return GdmUtil.fadeInActor(this._promptEntry);
865
// Show it with 0 opacity so we preallocate space for it
866
// in the event we need to fade in the message
867
this._promptLoginHint.opacity = 0;
868
this._promptLoginHint.show();
872
return GdmUtil.fadeInActor(this._promptBox);
876
if (this._user && this._user.is_logged_in())
879
if (!this._verifyingUser)
882
return GdmUtil.fadeInActor(this._sessionList.actor);
886
this._promptEntry.grab_key_focus();
889
this._sessionList.actor.hide();
890
let batch = new Batch.ConcurrentBatch(this, tasks);
894
_showPrompt: function() {
895
let hold = new Batch.Hold();
897
let cancelButtonInfo = { action: Lang.bind(this, this.cancel),
899
key: Clutter.Escape };
900
let okButtonInfo = { action: Lang.bind(this, function() {
903
label: C_("button", "Sign In"),
906
if (!this._disableUserList || this._verifyingUser)
907
buttons.push(cancelButtonInfo);
908
buttons.push(okButtonInfo);
910
let tasks = [function() {
911
return this._fadeInPrompt();
915
this.setButtons(buttons);
920
let batch = new Batch.ConcurrentBatch(this, tasks);
925
_hidePrompt: function() {
928
let tasks = [function() {
929
return GdmUtil.fadeOutActor(this._promptBox);
933
this._promptLoginHint.hide();
934
this._promptEntry.reactive = true;
935
this._promptEntry.set_text('');
938
let batch = new Batch.ConsecutiveBatch(this, tasks);
943
_askQuestion: function(verifier, serviceName, question, passwordChar) {
944
this._promptLabel.set_text(question);
946
this._promptEntry.set_text('');
947
this._promptEntry.clutter_text.set_password_char(passwordChar);
949
let tasks = [this._showPrompt,
952
let _text = this._promptEntry.get_text();
953
this._promptEntry.reactive = false;
954
this._userVerifier.answerQuery(serviceName, _text);
957
let batch = new Batch.ConsecutiveBatch(this, tasks);
961
_askForUsernameAndLogIn: function() {
962
this._promptLabel.set_text(_("Username: "));
963
this._promptEntry.set_text('');
964
this._promptEntry.clutter_text.set_password_char('');
966
let tasks = [this._showPrompt,
969
let userName = this._promptEntry.get_text();
970
this._promptEntry.reactive = false;
971
return this._beginVerificationForUser(userName);
974
let batch = new Batch.ConsecutiveBatch(this, tasks);
978
_onSessionOpened: function(client, serviceName) {
979
this._greeter.call_start_session_when_ready_sync(serviceName, true, null);
982
_waitForItemForUser: function(userName) {
983
let item = this._userList.getItemFromUserName(userName);
988
let hold = new Batch.Hold();
989
let signalId = this._userList.connect('item-added',
990
Lang.bind(this, function() {
991
let item = this._userList.getItemFromUserName(userName);
997
hold.connect('release', Lang.bind(this, function() {
998
this._userList.disconnect(signalId);
1004
_showTimedLoginAnimation: function() {
1005
this._timedLoginItem.actor.grab_key_focus();
1006
return this._timedLoginItem.showTimedLoginIndicator(this._timedLoginAnimationTime);
1009
_blockTimedLoginUntilIdle: function() {
1010
// This blocks timed login from starting until a few
1011
// seconds after the user stops interacting with the
1014
// We skip this step if the timed login delay is very
1016
if ((this._timedLoginDelay - _TIMED_LOGIN_IDLE_THRESHOLD) <= 0)
1019
let hold = new Batch.Hold();
1021
this._timedLoginIdleTimeOutId = Mainloop.timeout_add_seconds(_TIMED_LOGIN_IDLE_THRESHOLD,
1023
this._timedLoginAnimationTime -= _TIMED_LOGIN_IDLE_THRESHOLD;
1029
_startTimedLogin: function(userName, delay) {
1030
this._timedLoginItem = null;
1031
this._timedLoginDelay = delay;
1032
this._timedLoginAnimationTime = delay;
1034
let tasks = [function() {
1035
return this._waitForItemForUser(userName);
1039
this._timedLoginItem = this._userList.getItemFromUserName(userName);
1043
// If we're just starting out, start on the right
1045
if (!this.is_loaded) {
1046
this._userList.jumpToItem(this._timedLoginItem);
1050
this._blockTimedLoginUntilIdle,
1053
this._userList.scrollToItem(this._timedLoginItem);
1056
this._showTimedLoginAnimation,
1059
this._timedLoginBatch = null;
1060
this._greeter.call_begin_auto_login_sync(userName, null);
1063
this._timedLoginBatch = new Batch.ConsecutiveBatch(this, tasks);
1065
return this._timedLoginBatch.run();
1068
_resetTimedLogin: function() {
1069
if (this._timedLoginBatch) {
1070
this._timedLoginBatch.cancel();
1071
this._timedLoginBatch = null;
1074
if (this._timedLoginItem)
1075
this._timedLoginItem.hideTimedLoginIndicator();
1077
let userName = this._timedLoginItem.user.get_user_name();
1080
this._startTimedLogin(userName, this._timedLoginDelay);
1083
_onTimedLoginRequested: function(client, userName, seconds) {
1084
this._startTimedLogin(userName, seconds);
1086
global.stage.connect('captured-event',
1087
Lang.bind(this, function(actor, event) {
1088
if (this._timedLoginDelay == undefined)
1091
if (event.type() == Clutter.EventType.KEY_PRESS ||
1092
event.type() == Clutter.EventType.BUTTON_PRESS) {
1093
if (this._timedLoginBatch) {
1094
this._timedLoginBatch.cancel();
1095
this._timedLoginBatch = null;
1097
} else if (event.type() == Clutter.EventType.KEY_RELEASE ||
1098
event.type() == Clutter.EventType.BUTTON_RELEASE) {
1099
this._resetTimedLogin();
1106
_hideUserListAndLogIn: function() {
1107
let tasks = [function() {
1108
return this._userList.hideItems();
1112
return this._userList.giveUpWhitespace();
1116
this._userList.actor.hide();
1119
new Batch.ConcurrentBatch(this, [this._fadeOutTitleLabel,
1120
this._fadeOutNotListedButton,
1121
this._fadeOutLogo]),
1124
return this._askForUsernameAndLogIn();
1127
let batch = new Batch.ConsecutiveBatch(this, tasks);
1131
_fadeInLogo: function() {
1132
return GdmUtil.fadeInActor(this._logoBox);
1135
_fadeOutLogo: function() {
1136
return GdmUtil.fadeOutActor(this._logoBox);
1139
_showUserList: function() {
1140
let tasks = [this._hidePrompt,
1142
new Batch.ConcurrentBatch(this, [this._fadeInTitleLabel,
1143
this._fadeInNotListedButton,
1147
this._sessionList.close();
1148
this._promptLoginHint.hide();
1149
this._userList.actor.show();
1150
this._userList.actor.opacity = 255;
1151
return this._userList.showItems();
1155
this._userList.actor.reactive = true;
1156
this._userList.actor.grab_key_focus();
1159
let batch = new Batch.ConsecutiveBatch(this, tasks);
1163
_fadeInBanner: function() {
1164
return GdmUtil.fadeInActor(this._bannerLabel);
1167
_fadeOutBanner: function() {
1168
return GdmUtil.fadeOutActor(this._bannerLabel);
1171
_fadeInTitleLabel: function() {
1172
return GdmUtil.fadeInActor(this._titleLabel);
1175
_fadeOutTitleLabel: function() {
1176
return GdmUtil.fadeOutActor(this._titleLabel);
1179
_fadeInNotListedButton: function() {
1180
return GdmUtil.fadeInActor(this._notListedButton);
1183
_fadeOutNotListedButton: function() {
1184
return GdmUtil.fadeOutActor(this._notListedButton);
1187
_beginVerificationForUser: function(userName) {
1188
let hold = new Batch.Hold();
1190
this._userVerifier.begin(userName, hold);
1191
this._verifyingUser = true;
1195
_onUserListActivated: function(activatedItem) {
1198
let tasks = [function() {
1199
this._userList.actor.reactive = false;
1200
return this._userList.pinInPlace();
1204
return this._userList.hideItemsExcept(activatedItem);
1208
return this._userList.giveUpWhitespace();
1211
new Batch.ConcurrentBatch(this, [this._fadeOutTitleLabel,
1212
this._fadeOutNotListedButton,
1213
this._fadeOutLogo]),
1216
return this._userList.shrinkToNaturalHeight();
1220
userName = activatedItem.user.get_user_name();
1222
return this._beginVerificationForUser(userName);
1225
this._user = activatedItem.user;
1227
let batch = new Batch.ConsecutiveBatch(this, tasks);
1231
_onDestroy: function() {
1232
if (this._userManagerLoadedId) {
1233
this._userManager.disconnect(this._userManagerLoadedId);
1234
this._userManagerLoadedId = 0;
1238
_loadUserList: function() {
1239
let users = this._userManager.list_users();
1241
for (let i = 0; i < users.length; i++) {
1242
this._userList.addUser(users[i]);
1245
this._updateDisableUserList();
1247
this._userManager.connect('user-added',
1248
Lang.bind(this, function(userManager, user) {
1249
this._userList.addUser(user);
1252
this._userManager.connect('user-removed',
1253
Lang.bind(this, function(userManager, user) {
1254
this._userList.removeUser(user);
1257
// emitted in idle so caller doesn't have to explicitly check if
1258
// it's loaded immediately after construction
1259
// (since there's no way the caller could be listening for
1261
Mainloop.idle_add(Lang.bind(this, function() {
1262
this.emit('loaded');
1263
this.is_loaded = true;
1267
_onOpened: function() {
1268
Main.ctrlAltTabManager.addGroup(this.dialogLayout,
1270
'dialog-password-symbolic',
1271
{ sortGroup: CtrlAltTab.SortGroup.MIDDLE });
1278
Main.ctrlAltTabManager.removeGroup(this.dialogLayout);