1
/* -*- mode: js2; js2-basic-offset: 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 Lang = imports.lang;
29
const Pango = imports.gi.Pango;
30
const Signals = imports.signals;
31
const Shell = imports.gi.Shell;
32
const St = imports.gi.St;
33
const GdmGreeter = imports.gi.GdmGreeter;
35
const Batch = imports.gdm.batch;
36
const Lightbox = imports.ui.lightbox;
37
const Main = imports.ui.main;
38
const ModalDialog = imports.ui.modalDialog;
39
const Tweener = imports.ui.tweener;
41
const _FADE_ANIMATION_TIME = 0.16;
42
const _RESIZE_ANIMATION_TIME = 0.25;
43
const _SCROLL_ANIMATION_TIME = 2.0;
44
const _TIMED_LOGIN_IDLE_THRESHOLD = 5.0;
46
let _loginDialog = null;
48
function _fadeInActor(actor) {
49
let hold = new Batch.Hold();
51
if (actor.opacity == 255 && actor.visible)
55
let [minHeight, naturalHeight] = actor.get_preferred_height(-1);
59
Tweener.addTween(actor,
61
height: naturalHeight,
62
time: _FADE_ANIMATION_TIME,
63
transition: 'easeOutQuad',
64
onComplete: function() {
73
function _fadeOutActor(actor) {
74
let hold = new Batch.Hold();
81
if (actor.opacity == 0) {
86
Tweener.addTween(actor,
89
time: _FADE_ANIMATION_TIME,
90
transition: 'easeOutQuad',
91
onComplete: function() {
101
function _smoothlyResizeActor(actor, width, height) {
106
finalWidth = actor.width;
111
finalHeight = actor.height;
113
finalHeight = height;
115
actor.set_size(actor.width, actor.height);
117
if (actor.width == finalWidth && actor.height == finalHeight)
120
let hold = new Batch.Hold();
122
Tweener.addTween(actor,
125
time: _RESIZE_ANIMATION_TIME,
126
transition: 'easeOutQuad',
127
onComplete: Lang.bind(this, function() {
134
function UserListItem(user, reason) {
135
this._init(user, reason);
138
UserListItem.prototype = {
139
_init: function(user) {
141
this._userChangedId = this.user.connect('changed',
142
Lang.bind(this, this._onUserChanged));
144
this._verticalBox = new St.BoxLayout({ style_class: 'login-dialog-user-list-item-vertical-layout',
147
this.actor = new St.Button({ style_class: 'login-dialog-user-list-item',
149
child: this._verticalBox,
151
x_align: St.Align.START,
153
let layout = new St.BoxLayout({ vertical: false });
155
this._verticalBox.add(layout,
160
this._focusBin = new St.Bin({ style_class: 'login-dialog-user-list-item-focus-bin' });
161
this._verticalBox.add(this._focusBin,
163
x_align: St.Align.MIDDLE,
167
this._iconBin = new St.Bin();
168
layout.add(this._iconBin);
169
let textLayout = new St.BoxLayout({ style_class: 'login-dialog-user-list-item-text-box',
171
layout.add(textLayout,
173
y_align: St.Align.MIDDLE,
176
this._nameLabel = new St.Label({ text: this.user.get_real_name(),
177
style_class: 'login-dialog-user-list-item-name' });
178
textLayout.add(this._nameLabel);
182
this.actor.connect('clicked', Lang.bind(this, this._onClicked));
185
_onUserChanged: function() {
186
this._nameLabel.set_text(this.user.get_real_name());
190
_setIconFromFile: function(iconFile, styleClass) {
192
this._iconBin.set_style_class_name(styleClass);
193
this._iconBin.set_style(null);
195
this._iconBin.child = null;
197
this._iconBin.show();
198
// We use background-image instead of, say, St.TextureCache
199
// so the theme writers can add a rounded frame around the image
200
// and so theme writers can pick the icon size.
201
this._iconBin.set_style('background-image: url("' + iconFile + '");');
203
this._iconBin.hide();
207
_setIconFromName: function(iconName, styleClass) {
209
this._iconBin.set_style_class_name(styleClass);
210
this._iconBin.set_style(null);
212
if (iconName != null) {
213
let icon = new St.Icon();
214
icon.set_icon_name(iconName)
216
this._iconBin.child = icon;
217
this._iconBin.show();
219
this._iconBin.child = null;
220
this._iconBin.hide();
224
_updateIcon: function() {
225
let iconFileName = this.user.get_icon_file();
228
if (GLib.file_test(iconFileName, GLib.FileTest.EXISTS))
229
this._setIconFromFile(iconFileName, 'login-dialog-user-list-item-icon');
231
this._setIconFromName('avatar-default', 'login-dialog-user-list-item-icon');
234
_onClicked: function() {
235
this.emit('activate');
238
fadeOutName: function() {
239
return _fadeOutActor(this._nameLabel);
242
fadeInName: function() {
243
return _fadeInActor(this._nameLabel);
246
showFocusAnimation: function(time) {
247
let hold = new Batch.Hold();
249
let box = this._verticalBox.get_allocation_box();
251
Tweener.removeTweens(this._focusBin);
252
this._focusBin.width = 0;
253
Tweener.addTween(this._focusBin,
254
{ width: box.x2 - box.x1,
256
transition: 'linear',
257
onComplete: function() {
260
onCompleteScope: this
266
Signals.addSignalMethods(UserListItem.prototype);
268
function UserList() {
269
this._init.apply(this, arguments);
272
UserList.prototype = {
274
this.actor = new St.ScrollView({ style_class: 'login-dialog-user-list-view'});
275
this.actor.set_policy(Gtk.PolicyType.NEVER,
276
Gtk.PolicyType.AUTOMATIC);
278
this._box = new St.BoxLayout({ vertical: true,
279
style_class: 'login-dialog-user-list' });
281
this.actor.add_actor(this._box,
284
x_align: St.Align.START,
285
y_align: St.Align.MIDDLE });
289
_showItem: function(item) {
290
let tasks = [function() {
291
return _fadeInActor(item.actor);
295
return item.fadeInName();
298
let batch = new Batch.ConsecutiveBatch(this, tasks);
302
_onItemActivated: function(activatedItem) {
303
this.emit('activate', activatedItem);
306
giveUpWhitespace: function() {
307
let container = this.actor.get_parent();
309
container.child_set(this.actor, { expand: false });
312
takeOverWhitespace: function() {
313
let container = this.actor.get_parent();
315
container.child_set(this.actor, { expand: true });
318
pinInPlace: function() {
319
this._box.set_size(this._box.width, this._box.height);
322
shrinkToNaturalHeight: function() {
323
let oldWidth = this._box.width;
324
let oldHeight = this._box.height;
325
this._box.set_size(-1, -1);
326
let [minHeight, naturalHeight] = this._box.get_preferred_height(-1);
327
this._box.set_size(oldWidth, oldHeight);
329
let batch = new Batch.ConsecutiveBatch(this,
331
return _smoothlyResizeActor(this._box, -1, naturalHeight);
335
this._box.set_size(-1, -1);
342
hideItemsExcept: function(exception) {
345
for (let userName in this._items) {
346
let item = this._items[userName];
348
item.actor.can_focus = false;
349
item._focusBin.width = 0;
350
if (item != exception)
351
tasks.push(function() {
352
return _fadeOutActor(item.actor);
356
let batch = new Batch.ConsecutiveBatch(this,
358
return _fadeOutActor(this.actor.vscroll);
361
new Batch.ConcurrentBatch(this, tasks)
367
hideItems: function() {
368
return this.hideItemsExcept(null);
371
_getExpandedHeight: function() {
372
let hiddenActors = [];
373
for (let userName in this._items) {
374
let item = this._items[userName];
375
if (!item.actor.visible) {
377
hiddenActors.push(item.actor);
381
if (!this._box.visible) {
383
hiddenActors.push(this._box);
386
this._box.set_size(-1, -1);
387
let [minHeight, naturalHeight] = this._box.get_preferred_height(-1);
389
for (let i = 0; i < hiddenActors.length; i++) {
390
let actor = hiddenActors[i];
394
return naturalHeight;
397
showItems: function() {
400
for (let userName in this._items) {
401
let item = this._items[userName];
402
item.actor.can_focus = true;
403
tasks.push(function() {
404
return this._showItem(item);
408
let batch = new Batch.ConsecutiveBatch(this,
410
this.takeOverWhitespace();
414
let fullHeight = this._getExpandedHeight();
415
return _smoothlyResizeActor(this._box, -1, fullHeight);
418
new Batch.ConcurrentBatch(this, tasks),
421
this.actor.set_size(-1, -1);
425
return _fadeInActor(this.actor.vscroll);
430
scrollToItem: function(item) {
431
let box = item.actor.get_allocation_box();
433
let adjustment = this.actor.get_vscroll_bar().get_adjustment();
435
let value = (box.y1 + adjustment.step_increment / 2.0) - (adjustment.page_size / 2.0);
436
Tweener.removeTweens(adjustment);
437
Tweener.addTween (adjustment,
439
time: _SCROLL_ANIMATION_TIME,
440
transition: 'linear' });
443
jumpToItem: function(item) {
444
let box = item.actor.get_allocation_box();
446
let adjustment = this.actor.get_vscroll_bar().get_adjustment();
448
let value = (box.y1 + adjustment.step_increment / 2.0) - (adjustment.page_size / 2.0);
450
adjustment.set_value(value);
453
getItemFromUserName: function(userName) {
454
let item = this._items[userName];
462
addUser: function(user) {
466
if (user.is_system_account())
469
let userName = user.get_user_name();
474
this.removeUser(user);
476
let item = new UserListItem(user);
477
this._box.add(item.actor, { x_fill: true });
479
this._items[userName] = item;
481
item.connect('activate',
482
Lang.bind(this, this._onItemActivated));
484
// Try to keep the focused item front-and-center
485
item.actor.connect('key-focus-in',
488
this.scrollToItem(item);
489
item.showFocusAnimation(0);
492
this.emit('item-added', item);
495
removeUser: function(user) {
499
let userName = user.get_user_name();
504
let item = this._items[userName];
509
item.actor.destroy();
510
delete this._items[userName];
513
Signals.addSignalMethods(UserList.prototype);
515
function SessionListItem(id, name) {
516
this._init(id, name);
519
SessionListItem.prototype = {
520
_init: function(id, name) {
523
this.actor = new St.Button({ style_class: 'login-dialog-session-list-item',
527
x_align: St.Align.START });
529
this._box = new St.BoxLayout({ style_class: 'login-dialog-session-list-item-box' });
531
this.actor.add_actor(this._box,
535
this.actor.connect('clicked', Lang.bind(this, this._onClicked));
537
this._dot = new St.DrawingArea({ style_class: 'login-dialog-session-list-item-dot' });
538
this._dot.connect('repaint', Lang.bind(this, this._onRepaintDot));
539
this._box.add_actor(this._dot);
540
this.setShowDot(false);
542
let label = new St.Label({ style_class: 'login-dialog-session-list-item-label',
545
this._box.add_actor(label,
551
setShowDot: function(show) {
553
this._dot.opacity = 255;
555
this._dot.opacity = 0;
558
_onRepaintDot: function(area) {
559
let cr = area.get_context();
560
let [width, height] = area.get_surface_size();
561
let color = area.get_theme_node().get_foreground_color();
563
cr.setSourceRGBA (color.red / 255,
567
cr.arc(width / 2, height / 2, width / 3, 0, 2 * Math.PI);
571
_onClicked: function() {
572
this.emit('activate');
575
Signals.addSignalMethods(SessionListItem.prototype);
577
function SessionList() {
581
SessionList.prototype = {
583
this.actor = new St.BoxLayout({ style_class: 'login-dialog-session-list',
586
this._button = new St.Button({ style_class: 'login-dialog-session-list-button',
590
let box = new St.BoxLayout();
591
this._button.add_actor(box,
596
this._triangle = new St.Label({ style_class: 'login-dialog-session-list-triangle',
598
box.add_actor(this._triangle);
600
let label = new St.Label({ style_class: 'login-dialog-session-list-label',
601
text: _("Session...") });
607
this._button.connect('clicked',
608
Lang.bind(this, this._onClicked));
609
this.actor.add_actor(this._button,
613
this._scrollView = new St.ScrollView({ style_class: 'login-dialog-session-list-scroll-view'});
614
this._scrollView.set_policy(Gtk.PolicyType.NEVER,
615
Gtk.PolicyType.AUTOMATIC);
616
this.actor.add_actor(this._scrollView,
620
this._itemList = new St.BoxLayout({ style_class: 'login-dialog-session-item-list',
622
this._scrollView.add_actor(this._itemList,
626
this._scrollView.hide();
635
this._button.add_style_pseudo_class('open');
636
this._scrollView.show();
637
this._triangle.set_text('\u25BE');
646
this._button.remove_style_pseudo_class('open');
647
this._scrollView.hide();
648
this._triangle.set_text('\u25B8');
653
_onClicked: function() {
660
setActiveSession: function(sessionId) {
661
if (sessionId == this._activeSessionId)
664
if (this._activeSessionId)
665
this._items[this._activeSessionId].setShowDot(false);
667
this._items[sessionId].setShowDot(true);
668
this._activeSessionId = sessionId;
670
this.emit('session-activated', this._activeSessionId);
673
_populate: function() {
674
this._itemList.destroy_children();
675
this._activeSessionId = null;
678
let ids = GdmGreeter.get_session_ids();
686
for (let i = 0; i < ids.length; i++) {
687
let [sessionName, sessionDescription] = GdmGreeter.get_session_name_and_description(ids[i]);
689
let item = new SessionListItem(ids[i], sessionName);
690
this._itemList.add_actor(item.actor,
691
{ x_align: St.Align.START,
692
y_align: St.Align.START,
695
this._items[ids[i]] = item;
697
if (!this._activeSessionId)
698
this.setActiveSession(ids[i]);
700
item.connect('activate',
701
Lang.bind(this, function() {
702
this.setActiveSession(item.id);
707
Signals.addSignalMethods(SessionList.prototype);
709
function LoginDialog() {
710
if (_loginDialog == null) {
718
LoginDialog.prototype = {
719
__proto__: ModalDialog.ModalDialog.prototype,
722
ModalDialog.ModalDialog.prototype._init.call(this, { shellReactive: true,
723
styleClass: 'login-dialog' });
724
this.connect('destroy',
725
Lang.bind(this, this._onDestroy));
726
this.connect('opened',
727
Lang.bind(this, this._onOpened));
729
this._userManager = AccountsService.UserManager.get_default()
730
this._greeterClient = new GdmGreeter.Client();
732
this._greeterClient.open_connection();
734
this._greeterClient.call_start_conversation('gdm-password');
736
this._greeterClient.connect('reset',
737
Lang.bind(this, this._onReset));
738
this._greeterClient.connect('default-session-changed',
739
Lang.bind(this, this._onDefaultSessionChanged));
740
this._greeterClient.connect('info',
741
Lang.bind(this, this._onInfo));
742
this._greeterClient.connect('problem',
743
Lang.bind(this, this._onProblem));
744
this._greeterClient.connect('info-query',
745
Lang.bind(this, this._onInfoQuery));
746
this._greeterClient.connect('secret-info-query',
747
Lang.bind(this, this._onSecretInfoQuery));
748
this._greeterClient.connect('session-opened',
749
Lang.bind(this, this._onSessionOpened));
750
this._greeterClient.connect('timed-login-requested',
751
Lang.bind(this, this._onTimedLoginRequested));
752
this._greeterClient.connect('authentication-failed',
753
Lang.bind(this, this._onAuthenticationFailed));
754
this._greeterClient.connect('conversation-stopped',
755
Lang.bind(this, this._onConversationStopped));
757
this._titleLabel = new St.Label({ style_class: 'login-dialog-title',
758
text: _("Sign In") });
760
this.contentLayout.add(this._titleLabel,
762
y_align: St.Align.START });
764
let mainContentBox = new St.BoxLayout({ vertical: false });
765
this.contentLayout.add(mainContentBox,
770
this._userList = new UserList();
771
mainContentBox.add(this._userList.actor,
776
this.setInitialKeyFocus(this._userList.actor);
778
this._promptBox = new St.BoxLayout({ style_class: 'login-dialog-prompt-layout',
780
mainContentBox.add(this._promptBox,
784
x_align: St.Align.START });
785
this._promptLabel = new St.Label({ style_class: 'login-dialog-prompt-label' });
787
this._mainContentBox = mainContentBox;
789
this._promptBox.add(this._promptLabel,
793
x_align: St.Align.START });
794
this._promptEntry = new St.Entry({ style_class: 'login-dialog-prompt-entry',
796
this._promptBox.add(this._promptEntry,
800
x_align: St.Align.START });
802
this._sessionList = new SessionList();
803
this._sessionList.connect('session-activated',
804
Lang.bind(this, function(list, sessionId) {
805
this._greeterClient.call_select_session (sessionId);
808
this._promptBox.add(this._sessionList.actor,
812
x_align: St.Align.START,
813
y_align: St.Align.START});
814
this._promptBox.hide();
816
let notListedLabel = new St.Label({ text: _("Not listed?"),
817
style_class: 'login-dialog-not-listed-label' });
818
this._notListedButton = new St.Button({ style_class: 'login-dialog-not-listed-button',
820
child: notListedLabel,
822
x_align: St.Align.START,
825
this._notListedButton.connect('clicked', Lang.bind(this, this._onNotListedClicked));
827
this.contentLayout.add(this._notListedButton,
829
x_align: St.Align.START,
832
if (!this._userManager.is_loaded)
833
this._userManagerLoadedId = this._userManager.connect('notify::is-loaded',
834
Lang.bind(this, function() {
835
if (this._userManager.is_loaded) {
836
this._loadUserList();
837
this._userManager.disconnect(this._userManagerLoadedId);
838
this._userManagerLoadedId = 0;
842
this._loadUserList();
844
this._userList.connect('activate',
845
Lang.bind(this, function(userList, item) {
846
this._onUserListActivated(item);
851
_onReset: function(client, serviceName) {
852
this._greeterClient.call_start_conversation('gdm-password');
854
let tasks = [this._hidePrompt,
856
new Batch.ConcurrentBatch(this, [this._fadeInTitleLabel,
857
this._fadeInNotListedButton]),
860
this._sessionList.close();
861
this._userList.actor.show();
862
this._userList.actor.opacity = 255;
863
return this._userList.showItems();
867
this._userList.actor.reactive = true;
868
this._userList.actor.grab_key_focus();
873
let batch = new Batch.ConsecutiveBatch(this, tasks);
877
_onDefaultSessionChanged: function(client, sessionId) {
878
this._sessionList.setActiveSession(sessionId);
881
_onInfo: function(client, serviceName, info) {
882
Main.notifyError(info);
885
_onProblem: function(client, serviceName, problem) {
886
Main.notifyError(problem);
889
_onCancel: function(client) {
890
this._greeterClient.call_cancel();
893
_fadeInPrompt: function() {
894
let tasks = [function() {
895
return _fadeInActor(this._promptLabel);
899
return _fadeInActor(this._promptEntry);
903
return _fadeInActor(this._promptBox);
907
if (this._user && this._user.is_logged_in())
910
return _fadeInActor(this._sessionList.actor);
914
this._promptEntry.grab_key_focus();
917
this._sessionList.actor.hide();
918
let batch = new Batch.ConcurrentBatch(this, tasks);
922
_showPrompt: function() {
923
let hold = new Batch.Hold();
925
let buttons = [{ action: Lang.bind(this, this._onCancel),
927
key: Clutter.Escape },
928
{ action: Lang.bind(this, function() {
931
label: _("Sign In") }];
933
this._promptEntryActivateCallbackId = this._promptEntry.clutter_text.connect('activate',
934
Lang.bind(this, function() {
937
hold.connect('release', Lang.bind(this, function() {
938
this._promptEntry.clutter_text.disconnect(this._promptEntryActivateCallbackId);
939
this._promptEntryActivateCallbackId = null;
942
let tasks = [function() {
943
return this._fadeInPrompt();
947
this.setButtons(buttons);
952
let batch = new Batch.ConcurrentBatch(this, tasks);
957
_hidePrompt: function() {
958
if (this._promptEntryActivateCallbackId) {
959
this._promptEntry.clutter_text.disconnect(this._promptEntryActivateCallbackId);
960
this._promptEntryActivateCallbackId = null;
965
let tasks = [function() {
966
return _fadeOutActor(this._promptBox);
970
this._promptEntry.set_text('');
973
let batch = new Batch.ConsecutiveBatch(this, tasks);
978
_askQuestion: function(serviceName, question) {
979
this._promptLabel.set_text(question);
981
let tasks = [this._showPrompt,
984
let _text = this._promptEntry.get_text();
985
this._promptEntry.set_text('');
986
this._greeterClient.call_answer_query(serviceName, _text);
989
let batch = new Batch.ConsecutiveBatch(this, tasks);
992
_onInfoQuery: function(client, serviceName, question) {
993
this._promptEntry.set_text('');
994
this._promptEntry.clutter_text.set_password_char('');
995
this._askQuestion(serviceName, question);
998
_onSecretInfoQuery: function(client, serviceName, secretQuestion) {
999
this._promptEntry.set_text('');
1000
this._promptEntry.clutter_text.set_password_char('\u25cf');
1001
this._askQuestion(serviceName, secretQuestion);
1004
_onSessionOpened: function(client, serviceName) {
1005
this._greeterClient.call_start_session_when_ready(serviceName, true);
1008
_waitForItemForUser: function(userName) {
1009
let item = this._userList.getItemFromUserName(userName);
1014
let hold = new Batch.Hold();
1015
let signalId = this._userList.connect('item-added',
1016
Lang.bind(this, function() {
1017
let item = this._userList.getItemFromUserName(userName);
1023
hold.connect('release', Lang.bind(this, function() {
1024
this._userList.disconnect(signalId);
1030
_showTimedLoginAnimation: function() {
1031
this._timedLoginItem.actor.grab_key_focus();
1032
return this._timedLoginItem.showFocusAnimation(this._timedLoginAnimationTime);
1035
_blockTimedLoginUntilIdle: function() {
1036
// This blocks timed login from starting until a few
1037
// seconds after the user stops interacting with the
1040
// We skip this step if the timed login delay is very
1042
if ((this._timedLoginDelay - _TIMED_LOGIN_IDLE_THRESHOLD) <= 0)
1045
let hold = new Batch.Hold();
1047
this._timedLoginIdleTimeOutId = Mainloop.timeout_add_seconds(_TIMED_LOGIN_IDLE_THRESHOLD,
1049
this._timedLoginAnimationTime -= _TIMED_LOGIN_IDLE_THRESHOLD;
1055
_startTimedLogin: function(userName, delay) {
1056
this._timedLoginItem = null;
1057
this._timedLoginDelay = delay;
1058
this._timedLoginAnimationTime = delay;
1060
let tasks = [function() {
1061
return this._waitForItemForUser(userName);
1065
this._timedLoginItem = this._userList.getItemFromUserName(userName);
1069
// If we're just starting out, start on the right
1071
if (!this.is_loaded) {
1072
this._userList.jumpToItem(this._timedLoginItem);
1073
this._timedLoginItem.showFocusAnimation(0);
1077
this._blockTimedLoginUntilIdle,
1080
this._userList.scrollToItem(this._timedLoginItem);
1083
this._showTimedLoginAnimation,
1086
this._timedLoginBatch = null;
1087
this._greeterClient.call_begin_auto_login(userName);
1090
this._timedLoginBatch = new Batch.ConsecutiveBatch(this, tasks);
1092
return this._timedLoginBatch.run();
1095
_resetTimedLogin: function() {
1096
if (this._timedLoginBatch) {
1097
this._timedLoginBatch.cancel();
1098
this._timedLoginBatch = null;
1101
let userName = this._timedLoginItem.user.get_user_name();
1104
this._startTimedLogin(userName, this._timedLoginDelay);
1107
_onTimedLoginRequested: function(client, userName, seconds) {
1108
this._startTimedLogin(userName, seconds);
1110
global.stage.connect('captured-event',
1111
Lang.bind(this, function(actor, event) {
1112
if (this._timedLoginDelay == undefined)
1115
if (event.type() == Clutter.EventType.KEY_PRESS ||
1116
event.type() == Clutter.EventType.BUTTON_PRESS) {
1117
if (this._timedLoginBatch) {
1118
this._timedLoginBatch.cancel();
1119
this._timedLoginBatch = null;
1121
} else if (event.type() == Clutter.EventType.KEY_RELEASE ||
1122
event.type() == Clutter.EventType.BUTTON_RELEASE) {
1123
this._resetTimedLogin();
1130
_onAuthenticationFailed: function(client) {
1131
this._greeterClient.call_cancel();
1134
_onConversationStopped: function(client, serviceName) {
1135
this._greeterClient.call_cancel();
1138
_onNotListedClicked: function(user) {
1139
let tasks = [function() {
1140
return this._userList.hideItems();
1144
return this._userList.giveUpWhitespace();
1148
this._userList.actor.hide();
1151
new Batch.ConcurrentBatch(this, [this._fadeOutTitleLabel,
1152
this._fadeOutNotListedButton]),
1155
this._greeterClient.call_begin_verification('gdm-password');
1158
let batch = new Batch.ConsecutiveBatch(this, tasks);
1162
_fadeInTitleLabel: function() {
1163
return _fadeInActor(this._titleLabel);
1166
_fadeOutTitleLabel: function() {
1167
return _fadeOutActor(this._titleLabel);
1170
_fadeInNotListedButton: function() {
1171
return _fadeInActor(this._notListedButton);
1174
_fadeOutNotListedButton: function() {
1175
return _fadeOutActor(this._notListedButton);
1178
_onUserListActivated: function(activatedItem) {
1179
let tasks = [function() {
1180
this._userList.actor.reactive = false;
1181
return this._userList.pinInPlace();
1185
return this._userList.hideItemsExcept(activatedItem);
1189
return this._userList.giveUpWhitespace();
1193
return activatedItem.fadeOutName();
1196
new Batch.ConcurrentBatch(this, [this._fadeOutTitleLabel,
1197
this._fadeOutNotListedButton]),
1200
return this._userList.shrinkToNaturalHeight();
1204
let userName = activatedItem.user.get_user_name();
1205
this._greeterClient.call_begin_verification_for_user('gdm-password',
1209
this._user = activatedItem.user;
1211
let batch = new Batch.ConsecutiveBatch(this, tasks);
1215
_onDestroy: function() {
1216
if (this._userManagerLoadedId) {
1217
this._userManager.disconnect(this._userManagerLoadedId);
1218
this._userManagerLoadedId = 0;
1222
_loadUserList: function() {
1223
let users = this._userManager.list_users();
1225
for (let i = 0; i < users.length; i++) {
1226
this._userList.addUser(users[i]);
1229
this._userManager.connect('user-added',
1230
Lang.bind(this, function(userManager, user) {
1231
this._userList.addUser(user);
1234
this._userManager.connect('user-removed',
1235
Lang.bind(this, function(userManager, user) {
1236
this._userList.removeUser(user);
1239
// emitted in idle so caller doesn't have to explicitly check if
1240
// it's loaded immediately after construction
1241
// (since there's no way the caller could be listening for
1243
Mainloop.idle_add(Lang.bind(this, function() {
1244
this.emit('loaded');
1245
this.is_loaded = true;
1249
_onOpened: function() {
1250
Main.ctrlAltTabManager.addGroup(this._mainContentBox,
1253
{ sortGroup: CtrlAltTab.SortGroup.MIDDLE });
1258
ModalDialog.ModalDialog.prototype.close.call(this);
1260
Main.ctrlAltTabManager.removeGroup(this._group);