112
118
this.mountOp.connect('show-processes-2',
113
119
Lang.bind(this, this._onShowProcesses2));
114
120
this.mountOp.connect('aborted',
115
Lang.bind(this, this._onAborted));
121
Lang.bind(this, this.close));
122
this.mountOp.connect('show-unmount-progress',
123
Lang.bind(this, this._onShowUnmountProgress));
117
125
this._gicon = source.get_icon();
128
_closeExistingDialog: function() {
129
if (!this._existingDialog)
132
this._existingDialog.close();
133
this._existingDialog = null;
120
136
_onAskQuestion: function(op, message, choices) {
137
this._closeExistingDialog();
121
138
this._dialog = new ShellMountQuestionDialog(this._gicon);
123
this._dialog.connect('response',
124
Lang.bind(this, function(object, choice) {
125
this.mountOp.set_choice(choice);
126
this.mountOp.reply(Gio.MountOperationResult.HANDLED);
140
this._dialogId = this._dialog.connect('response', Lang.bind(this,
141
function(object, choice) {
142
this.mountOp.set_choice(choice);
143
this.mountOp.reply(Gio.MountOperationResult.HANDLED);
128
this._dialog.close(global.get_current_time());
132
148
this._dialog.update(message, choices);
133
this._dialog.open(global.get_current_time());
136
_onAskPassword: function(op, message) {
137
this._notificationShowing = true;
138
this._source = new ShellMountPasswordSource(message, this._gicon, this._reaskPassword);
140
this._source.connect('password-ready',
141
Lang.bind(this, function(source, password) {
142
this.mountOp.set_password(password);
143
this.mountOp.reply(Gio.MountOperationResult.HANDLED);
145
this._notificationShowing = false;
146
this._source.destroy();
149
this._source.connect('destroy',
150
Lang.bind(this, function() {
151
if (!this._notificationShowing)
154
this._notificationShowing = false;
155
this.mountOp.reply(Gio.MountOperationResult.ABORTED);
159
_onAborted: function(op) {
163
this._dialog.close(global.get_current_time());
152
_onAskPassword: function(op, message, defaultUser, defaultDomain, flags) {
153
if (this._existingDialog) {
154
this._dialog = this._existingDialog;
155
this._dialog.reaskPassword();
157
this._dialog = new ShellMountPasswordDialog(message, this._gicon, flags);
160
this._dialogId = this._dialog.connect('response', Lang.bind(this,
161
function(object, choice, password, remember) {
163
this.mountOp.reply(Gio.MountOperationResult.ABORTED);
166
this.mountOp.set_password_save(Gio.PasswordSave.PERMANENTLY);
168
this.mountOp.set_password_save(Gio.PasswordSave.NEVER);
170
this.mountOp.set_password(password);
171
this.mountOp.reply(Gio.MountOperationResult.HANDLED);
177
close: function(op) {
178
this._closeExistingDialog();
179
this._processesDialog = null;
182
this._dialog.close();
186
if (this._notifier) {
187
this._notifier.done();
188
this._notifier = null;
167
192
_onShowProcesses2: function(op) {
193
this._closeExistingDialog();
168
195
let processes = op.get_show_processes_pids();
169
196
let choices = op.get_show_processes_choices();
170
197
let message = op.get_show_processes_message();
173
200
this._processesDialog = new ShellProcessesDialog(this._gicon);
174
201
this._dialog = this._processesDialog;
176
this._processesDialog.connect('response',
177
Lang.bind(this, function(object, choice) {
179
this.mountOp.reply(Gio.MountOperationResult.ABORTED);
181
this.mountOp.set_choice(choice);
182
this.mountOp.reply(Gio.MountOperationResult.HANDLED);
203
this._dialogId = this._processesDialog.connect('response', Lang.bind(this,
204
function(object, choice) {
206
this.mountOp.reply(Gio.MountOperationResult.ABORTED);
208
this.mountOp.set_choice(choice);
209
this.mountOp.reply(Gio.MountOperationResult.HANDLED);
185
this._processesDialog.close(global.get_current_time());
188
this._processesDialog.open(global.get_current_time());
214
this._processesDialog.open();
191
217
this._processesDialog.update(message, processes, choices);
220
_onShowUnmountProgress: function(op, message, timeLeft, bytesLeft) {
222
this._notifier = new ShellUnmountNotifier();
225
this._notifier.done(message);
227
this._notifier.show(message);
230
borrowDialog: function() {
231
if (this._dialogId != 0) {
232
this._dialog.disconnect(this._dialogId);
240
const ShellUnmountNotifier = new Lang.Class({
241
Name: 'ShellUnmountNotifier',
242
Extends: MessageTray.Source,
245
this.parent('', 'media-removable');
247
this._notification = null;
248
Main.messageTray.add(this);
251
show: function(message) {
252
let [header, text] = message.split('\n', 2);
254
if (!this._notification) {
255
this._notification = new MessageTray.Notification(this, header, text);
256
this._notification.setTransient(true);
257
this._notification.setUrgency(MessageTray.Urgency.CRITICAL);
259
this._notification.update(header, text);
262
this.notify(this._notification);
265
done: function(message) {
266
if (this._notification) {
267
this._notification.destroy();
268
this._notification = null;
272
let notification = new MessageTray.Notification(this, message, null);
273
notification.setTransient(true);
275
this.notify(notification);
195
280
const ShellMountQuestionDialog = new Lang.Class({
239
324
Signals.addSignalMethods(ShellMountQuestionDialog.prototype);
241
const ShellMountPasswordSource = new Lang.Class({
242
Name: 'ShellMountPasswordSource',
243
Extends: MessageTray.Source,
245
_init: function(message, gicon, reaskPassword) {
326
const ShellMountPasswordDialog = new Lang.Class({
327
Name: 'ShellMountPasswordDialog',
328
Extends: ModalDialog.ModalDialog,
330
_init: function(message, gicon, flags) {
248
331
let strings = message.split('\n');
249
this.parent(strings[0]);
250
this._notification = new ShellMountPasswordNotification(this, strings, reaskPassword);
252
// add ourselves as a source, and popup the notification
253
Main.messageTray.add(this);
254
this.notify(this._notification);
257
createNotificationIcon: function() {
258
return _createIcon(this._gicon);
261
Signals.addSignalMethods(ShellMountPasswordSource.prototype);
263
const ShellMountPasswordNotification = new Lang.Class({
264
Name: 'ShellMountPasswordNotification',
265
Extends: MessageTray.Notification,
267
_init: function(source, strings, reaskPassword) {
268
this.parent(source, strings[0], null, { customContent: true });
270
// set the notification to transient and urgent, so that it
272
this.setTransient(true);
273
this.setUrgency(MessageTray.Urgency.CRITICAL);
332
this.parent({ styleClass: 'prompt-dialog' });
334
let mainContentBox = new St.BoxLayout({ style_class: 'prompt-dialog-main-layout',
336
this.contentLayout.add(mainContentBox);
338
let icon = _createIcon(gicon);
339
mainContentBox.add(icon,
342
x_align: St.Align.END,
343
y_align: St.Align.START });
345
this._messageBox = new St.BoxLayout({ style_class: 'prompt-dialog-message-layout',
347
mainContentBox.add(this._messageBox,
348
{ y_align: St.Align.START, expand: true, x_fill: true, y_fill: true });
350
let subject = new St.Label({ style_class: 'prompt-dialog-headline' });
351
this._messageBox.add(subject,
353
y_align: St.Align.START });
355
subject.set_text(strings[0]);
357
let description = new St.Label({ style_class: 'prompt-dialog-description' });
358
description.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
359
description.clutter_text.line_wrap = true;
360
this._messageBox.add(description,
362
y_align: St.Align.START });
276
this.addBody(strings[1]);
279
let label = new St.Label({ style_class: 'mount-password-reask',
280
text: _("Wrong password, please try again") });
282
this.addActor(label);
364
description.set_text(strings[1]);
366
this._passwordBox = new St.BoxLayout({ vertical: false, style_class: 'prompt-dialog-password-box' });
367
this._messageBox.add(this._passwordBox);
369
this._passwordLabel = new St.Label(({ style_class: 'prompt-dialog-password-label',
370
text: _("Password") }));
371
this._passwordBox.add(this._passwordLabel, { y_fill: false, y_align: St.Align.MIDDLE });
373
this._passwordEntry = new St.Entry({ style_class: 'prompt-dialog-password-entry',
376
ShellEntry.addContextMenu(this._passwordEntry, { isPassword: true });
377
this._passwordEntry.clutter_text.connect('activate', Lang.bind(this, this._onEntryActivate));
378
this._passwordEntry.clutter_text.set_password_char('\u25cf'); // ● U+25CF BLACK CIRCLE
379
this._passwordBox.add(this._passwordEntry, {expand: true });
380
this.setInitialKeyFocus(this._passwordEntry);
382
this._errorMessageLabel = new St.Label({ style_class: 'prompt-dialog-error-label',
383
text: _("Sorry, that didn\'t work. Please try again.") });
384
this._errorMessageLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
385
this._errorMessageLabel.clutter_text.line_wrap = true;
386
this._errorMessageLabel.hide();
387
this._messageBox.add(this._errorMessageLabel);
389
if (flags & Gio.AskPasswordFlags.SAVING_SUPPORTED) {
390
this._rememberChoice = new CheckBox.CheckBox();
391
this._rememberChoice.getLabelActor().text = _("Remember Password");
392
this._rememberChoice.actor.checked =
393
global.settings.get_boolean(REMEMBER_MOUNT_PASSWORD_KEY);
394
this._messageBox.add(this._rememberChoice.actor);
396
this._rememberChoice = null;
285
this._responseEntry = new St.Entry({ style_class: 'mount-password-entry',
287
this.setActionArea(this._responseEntry);
289
this._responseEntry.clutter_text.connect('activate',
290
Lang.bind(this, this._onEntryActivated));
291
this._responseEntry.clutter_text.set_password_char('\u25cf'); // ● U+25CF BLACK CIRCLE
293
this._responseEntry.grab_key_focus();
296
_onEntryActivated: function() {
297
let text = this._responseEntry.get_text();
301
this.source.emit('password-ready', text);
399
let buttons = [{ label: _("Cancel"),
400
action: Lang.bind(this, this._onCancelButton),
403
{ label: _("Unlock"),
404
action: Lang.bind(this, this._onUnlockButton),
408
this.setButtons(buttons);
411
reaskPassword: function() {
412
this._passwordEntry.set_text('');
413
this._errorMessageLabel.show();
416
_onCancelButton: function() {
417
this.emit('response', -1, '', false);
420
_onUnlockButton: function() {
421
this._onEntryActivate();
424
_onEntryActivate: function() {
425
global.settings.set_boolean(REMEMBER_MOUNT_PASSWORD_KEY,
426
this._rememberChoice && this._rememberChoice.actor.checked);
427
this.emit('response', 1,
428
this._passwordEntry.get_text(),
429
this._rememberChoice &&
430
this._rememberChoice.actor.checked);
397
522
Signals.addSignalMethods(ShellProcessesDialog.prototype);
524
const GnomeShellMountOpIface = <interface name="org.Gtk.MountOperationHandler">
525
<method name="AskPassword">
526
<arg type="s" direction="in" name="object_id"/>
527
<arg type="s" direction="in" name="message"/>
528
<arg type="s" direction="in" name="icon_name"/>
529
<arg type="s" direction="in" name="default_user"/>
530
<arg type="s" direction="in" name="default_domain"/>
531
<arg type="u" direction="in" name="flags"/>
532
<arg type="u" direction="out" name="response"/>
533
<arg type="a{sv}" direction="out" name="response_details"/>
535
<method name="AskQuestion">
536
<arg type="s" direction="in" name="object_id"/>
537
<arg type="s" direction="in" name="message"/>
538
<arg type="s" direction="in" name="icon_name"/>
539
<arg type="as" direction="in" name="choices"/>
540
<arg type="u" direction="out" name="response"/>
541
<arg type="a{sv}" direction="out" name="response_details"/>
543
<method name="ShowProcesses">
544
<arg type="s" direction="in" name="object_id"/>
545
<arg type="s" direction="in" name="message"/>
546
<arg type="s" direction="in" name="icon_name"/>
547
<arg type="ai" direction="in" name="application_pids"/>
548
<arg type="as" direction="in" name="choices"/>
549
<arg type="u" direction="out" name="response"/>
550
<arg type="a{sv}" direction="out" name="response_details"/>
552
<method name="Close"/>
555
const ShellMountOperationType = {
562
const GnomeShellMountOpHandler = new Lang.Class({
563
Name: 'GnomeShellMountOpHandler',
566
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(GnomeShellMountOpIface, this);
567
this._dbusImpl.export(Gio.DBus.session, '/org/gtk/MountOperationHandler');
568
Gio.bus_own_name_on_connection(Gio.DBus.session, 'org.gtk.MountOperationHandler',
569
Gio.BusNameOwnerFlags.REPLACE, null, null);
572
this._volumeMonitor = Gio.VolumeMonitor.get();
574
this._ensureEmptyRequest();
577
_ensureEmptyRequest: function() {
578
this._currentId = null;
579
this._currentInvocation = null;
580
this._currentType = ShellMountOperationType.NONE;
583
_clearCurrentRequest: function(response, details) {
584
if (this._currentInvocation) {
585
this._currentInvocation.return_value(
586
GLib.Variant.new('(ua{sv})', [response, details]));
589
this._ensureEmptyRequest();
592
_setCurrentRequest: function(invocation, id, type) {
593
let oldId = this._currentId;
594
let oldType = this._currentType;
595
let requestId = id + '@' + invocation.get_sender();
597
this._clearCurrentRequest(Gio.MountOperationResult.UNHANDLED, {});
599
this._currentInvocation = invocation;
600
this._currentId = requestId;
601
this._currentType = type;
603
if (this._dialog && (oldId == requestId) && (oldType == type))
609
_closeDialog: function() {
611
this._dialog.close();
616
_createGIcon: function(iconName) {
617
let realIconName = iconName ? iconName : 'drive-harddisk';
618
return new Gio.ThemedIcon({ name: realIconName,
619
use_default_fallbacks: true });
624
* @id: an opaque ID identifying the object for which the operation is requested
625
* The ID must be unique in the context of the calling process.
626
* @message: the message to display
627
* @icon_name: the name of an icon to display
628
* @default_user: the default username for display
629
* @default_domain: the default domain for display
630
* @flags: a set of GAskPasswordFlags
631
* @response: a GMountOperationResult
632
* @response_details: a dictionary containing the response details as
633
* entered by the user. The dictionary MAY contain the following properties:
634
* - "password" -> (s): a password to be used to complete the mount operation
635
* - "password_save" -> (u): a GPasswordSave
637
* The dialog will stay visible until clients call the Close() method, or
638
* another dialog becomes visible.
639
* Calling AskPassword again for the same id will have the effect to clear
640
* the existing dialog and update it with a message indicating the previous
641
* attempt went wrong.
643
AskPasswordAsync: function(params, invocation) {
644
let [id, message, iconName, defaultUser, defaultDomain, flags] = params;
646
if (this._setCurrentRequest(invocation, id, ShellMountOperationType.ASK_PASSWORD)) {
647
this._dialog.reaskPassword();
653
this._dialog = new ShellMountPasswordDialog(message, this._createGIcon(iconName), flags);
654
this._dialog.connect('response', Lang.bind(this,
655
function(object, choice, password, remember) {
660
response = Gio.MountOperationResult.ABORTED;
662
response = Gio.MountOperationResult.HANDLED;
664
let passSave = remember ? Gio.PasswordSave.PERMANENTLY : Gio.PasswordSave.NEVER;
665
details['password_save'] = GLib.Variant.new('u', passSave);
666
details['password'] = GLib.Variant.new('s', password);
669
this._clearCurrentRequest(response, details);
676
* @id: an opaque ID identifying the object for which the operation is requested
677
* The ID must be unique in the context of the calling process.
678
* @message: the message to display
679
* @icon_name: the name of an icon to display
680
* @choices: an array of choice strings
682
* @response: a GMountOperationResult
683
* @response_details: a dictionary containing the response details as
684
* entered by the user. The dictionary MAY contain the following properties:
685
* - "choice" -> (i): the chosen answer among the array of strings passed in
687
* The dialog will stay visible until clients call the Close() method, or
688
* another dialog becomes visible.
689
* Calling AskQuestion again for the same id will have the effect to clear
690
* update the dialog with the new question.
692
AskQuestionAsync: function(params, invocation) {
693
let [id, message, iconName, choices] = params;
695
if (this._setCurrentRequest(invocation, id, ShellMountOperationType.ASK_QUESTION)) {
696
this._dialog.update(message, choices);
702
this._dialog = new ShellMountQuestionDialog(this._createGIcon(iconName), message);
703
this._dialog.connect('response', Lang.bind(this,
704
function(object, choice) {
705
this._clearCurrentRequest(Gio.MountOperationResult.HANDLED,
706
{ choice: GLib.Variant.new('i', choice) });
709
this._dialog.update(message, choices);
715
* @id: an opaque ID identifying the object for which the operation is requested
716
* The ID must be unique in the context of the calling process.
717
* @message: the message to display
718
* @icon_name: the name of an icon to display
719
* @application_pids: the PIDs of the applications to display
720
* @choices: an array of choice strings
721
* @response: a GMountOperationResult
722
* @response_details: a dictionary containing the response details as
723
* entered by the user. The dictionary MAY contain the following properties:
724
* - "choice" -> (i): the chosen answer among the array of strings passed in
726
* The dialog will stay visible until clients call the Close() method, or
727
* another dialog becomes visible.
728
* Calling ShowProcesses again for the same id will have the effect to clear
729
* the existing dialog and update it with the new message and the new list
732
ShowProcessesAsync: function(params, invocation) {
733
let [id, message, iconName, applicationPids, choices] = params;
735
if (this._setCurrentRequest(invocation, id, ShellMountOperationType.SHOW_PROCESSES)) {
736
this._dialog.update(message, applicationPids, choices);
742
this._dialog = new ShellProcessesDialog(this._createGIcon(iconName));
743
this._dialog.connect('response', Lang.bind(this,
744
function(object, choice) {
749
response = Gio.MountOperationResult.ABORTED;
751
response = Gio.MountOperationResult.HANDLED;
752
details['choice'] = GLib.Variant.new('i', choice);
755
this._clearCurrentRequest(response, details);
758
this._dialog.update(message, applicationPids, choices);
765
* Closes a dialog previously opened by AskPassword, AskQuestion or ShowProcesses.
766
* If no dialog is open, does nothing.
768
Close: function(params, invocation) {
769
this._clearCurrentRequest(Gio.MountOperationResult.UNHANDLED, {});