[gnome-shell] mount-operation: implement org.Gtk.MountOperationHandler



commit 6b5f9a647a9e2020766cbf4477aa684b3ebe4170
Author: Cosimo Cecchi <cosimoc gnome org>
Date:   Wed Jun 20 17:23:34 2012 -0400

    mount-operation: implement org.Gtk.MountOperationHandler
    
    Use the ShellMountOperation dialogs we have to implement a DBus API
    allowing other processes to display them.
    Since GtkMountOperation now tries to call into our DBus implementation,
    every application that uses a GtkMountOperation will gain integration
    with our shell dialogs (but will still handle the actual communication
    with GVfs).
    
    https://bugzilla.gnome.org/show_bug.cgi?id=678516

 js/ui/main.js                |    3 +
 js/ui/shellMountOperation.js |  272 ++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 267 insertions(+), 8 deletions(-)
---
diff --git a/js/ui/main.js b/js/ui/main.js
index bc5a3df..b2ef94d 100644
--- a/js/ui/main.js
+++ b/js/ui/main.js
@@ -33,6 +33,7 @@ const WindowAttentionHandler = imports.ui.windowAttentionHandler;
 const Scripting = imports.ui.scripting;
 const SessionMode = imports.ui.sessionMode;
 const ShellDBus = imports.ui.shellDBus;
+const ShellMountOperation = imports.ui.shellMountOperation;
 const TelepathyClient = imports.ui.telepathyClient;
 const WindowManager = imports.ui.windowManager;
 const Magnifier = imports.ui.magnifier;
@@ -60,6 +61,7 @@ let ctrlAltTabManager = null;
 let recorder = null;
 let sessionMode = null;
 let shellDBusService = null;
+let shellMountOpDBusService = null;
 let modalCount = 0;
 let modalActorFocusStack = [];
 let uiGroup = null;
@@ -150,6 +152,7 @@ function start() {
 
     sessionMode = new SessionMode.SessionMode();
     shellDBusService = new ShellDBus.GnomeShell();
+    shellMountOpDBusService = new ShellMountOperation.GnomeShellMountOpHandler();
 
     // Ensure ShellWindowTracker and ShellAppUsage are initialized; this will
     // also initialize ShellAppSystem first.  ShellAppSystem
diff --git a/js/ui/shellMountOperation.js b/js/ui/shellMountOperation.js
index dd4910a..465891a 100644
--- a/js/ui/shellMountOperation.js
+++ b/js/ui/shellMountOperation.js
@@ -4,6 +4,7 @@ const Clutter = imports.gi.Clutter;
 const Lang = imports.lang;
 const Signals = imports.signals;
 const Gio = imports.gi.Gio;
+const GLib = imports.gi.GLib;
 const Gtk = imports.gi.Gtk;
 const Pango = imports.gi.Pango;
 const St = imports.gi.St;
@@ -144,17 +145,17 @@ const ShellMountOperation = new Lang.Class({
         this._dialog.open();
     },
 
-    _onAskPassword: function(op, message) {
+    _onAskPassword: function(op, message, defaultUser, defaultDomain, flags) {
         if (this._existingDialog) {
             this._dialog = this._existingDialog;
             this._dialog.reaskPassword();
         } else {
-            this._dialog = new ShellMountPasswordDialog(message, this._gicon);
+            this._dialog = new ShellMountPasswordDialog(message, this._gicon, flags);
         }
 
         this._dialogId = this._dialog.connect('response', Lang.bind(this,
             function(object, choice, password, remember) {
-                if (choice == '-1') {
+                if (choice == -1) {
                     this.mountOp.reply(Gio.MountOperationResult.ABORTED);
                 } else {
                     if (remember)
@@ -267,7 +268,7 @@ const ShellMountPasswordDialog = new Lang.Class({
     Name: 'ShellMountPasswordDialog',
     Extends: ModalDialog.ModalDialog,
 
-    _init: function(message, gicon) {
+    _init: function(message, gicon, flags) {
         let strings = message.split('\n');
         this.parent({ styleClass: 'prompt-dialog' });
 
@@ -326,10 +327,14 @@ const ShellMountPasswordDialog = new Lang.Class({
         this._errorMessageLabel.hide();
         this._messageBox.add(this._errorMessageLabel);
 
-        this._rememberChoice = new CheckBox.CheckBox();
-        this._rememberChoice.getLabelActor().text = _("Remember Passphrase");
-        this._rememberChoice.actor.checked = true;
-        this._messageBox.add(this._rememberChoice.actor);
+        if (flags & Gio.AskPasswordFlags.SAVING_SUPPORTED) {
+            this._rememberChoice = new CheckBox.CheckBox();
+            this._rememberChoice.getLabelActor().text = _("Remember Passphrase");
+            this._rememberChoice.actor.checked = true;
+            this._messageBox.add(this._rememberChoice.actor);
+        } else {
+            this._rememberChoice = null;
+        }
 
         let buttons = [{ label: _("Cancel"),
                          action: Lang.bind(this, this._onCancelButton),
@@ -358,6 +363,7 @@ const ShellMountPasswordDialog = new Lang.Class({
     _onEntryActivate: function() {
         this.emit('response', 1,
             this._passwordEntry.get_text(),
+            this._rememberChoice &&
             this._rememberChoice.actor.checked);
     }
 });
@@ -455,3 +461,253 @@ const ShellProcessesDialog = new Lang.Class({
     }
 });
 Signals.addSignalMethods(ShellProcessesDialog.prototype);
+
+const GnomeShellMountOpIface = <interface name="org.Gtk.MountOperationHandler">
+<method name="AskPassword">
+    <arg type="s" direction="in" name="object_id"/>
+    <arg type="s" direction="in" name="message"/>
+    <arg type="s" direction="in" name="icon_name"/>
+    <arg type="s" direction="in" name="default_user"/>
+    <arg type="s" direction="in" name="default_domain"/>
+    <arg type="u" direction="in" name="flags"/>
+    <arg type="u" direction="out" name="response"/>
+    <arg type="a{sv}" direction="out" name="response_details"/>
+</method>
+<method name="AskQuestion">
+    <arg type="s" direction="in" name="object_id"/>
+    <arg type="s" direction="in" name="message"/>
+    <arg type="s" direction="in" name="icon_name"/>
+    <arg type="as" direction="in" name="choices"/>
+    <arg type="u" direction="out" name="response"/>
+    <arg type="a{sv}" direction="out" name="response_details"/>
+</method>
+<method name="ShowProcesses">
+    <arg type="s" direction="in" name="object_id"/>
+    <arg type="s" direction="in" name="message"/>
+    <arg type="s" direction="in" name="icon_name"/>
+    <arg type="ai" direction="in" name="application_pids"/>
+    <arg type="as" direction="in" name="choices"/>
+    <arg type="u" direction="out" name="response"/>
+    <arg type="a{sv}" direction="out" name="response_details"/>
+</method>
+<method name="Close"/>
+</interface>;
+
+const ShellMountOperationType = {
+    NONE: 0,
+    ASK_PASSWORD: 1,
+    ASK_QUESTION: 2,
+    SHOW_PROCESSES: 3
+};
+
+const GnomeShellMountOpHandler = new Lang.Class({
+    Name: 'GnomeShellMountOpHandler',
+
+    _init: function() {
+        this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(GnomeShellMountOpIface, this);
+        this._dbusImpl.export(Gio.DBus.session, '/org/gtk/MountOperationHandler');
+        Gio.bus_own_name_on_connection(Gio.DBus.session, 'org.gtk.MountOperationHandler',
+                                       Gio.BusNameOwnerFlags.REPLACE, null, null);
+
+        this._dialog = null;
+        this._volumeMonitor = Gio.VolumeMonitor.get();
+
+        this._ensureEmptyRequest();
+    },
+
+    _ensureEmptyRequest: function() {
+        this._currentId = null;
+        this._currentInvocation = null;
+        this._currentType = ShellMountOperationType.NONE;
+    },
+
+    _clearCurrentRequest: function(response, details) {
+        if (this._currentInvocation) {
+            this._currentInvocation.return_value(
+                GLib.Variant.new('(ua{sv})', [response, details]));
+        }
+
+        this._ensureEmptyRequest();
+    },
+
+    _setCurrentRequest: function(invocation, id, type) {
+        let oldId = this._currentId;
+        let oldType = this._currentType;
+        let requestId = id + '@' + invocation.get_sender();
+
+        this._clearCurrentRequest(Gio.MountOperationResult.UNHANDLED, {});
+
+        this._currentInvocation = invocation;
+        this._currentId = requestId;
+        this._currentType = type;
+
+        if (this._dialog && (oldId == requestId) && (oldType == type))
+            return true;
+
+        return false;
+    },
+
+    _closeDialog: function() {
+        if (this._dialog) {
+            this._dialog.close();
+            this._dialog = null;
+        }
+    },
+
+    _createGIcon: function(iconName) {
+        let realIconName = iconName ? iconName : 'drive-harddisk';
+        return new Gio.ThemedIcon({ name: realIconName,
+                                    use_default_fallbacks: true });
+    },
+
+    /**
+     * AskPassword:
+     * @id: an opaque ID identifying the object for which the operation is requested
+     *      The ID must be unique in the context of the calling process.
+     * @message: the message to display
+     * @icon_name: the name of an icon to display
+     * @default_user: the default username for display
+     * @default_domain: the default domain for display
+     * @flags: a set of GAskPasswordFlags
+     * @response: a GMountOperationResult
+     * @response_details: a dictionary containing the response details as
+     * entered by the user. The dictionary MAY contain the following properties:
+     *   - "password" -> (s): a password to be used to complete the mount operation
+     *   - "password_save" -> (u): a GPasswordSave
+     *
+     * The dialog will stay visible until clients call the Close() method, or
+     * another dialog becomes visible.
+     * Calling AskPassword again for the same id will have the effect to clear
+     * the existing dialog and update it with a message indicating the previous
+     * attempt went wrong.
+     */
+    AskPasswordAsync: function(params, invocation) {
+        let [id, message, iconName, defaultUser, defaultDomain, flags] = params;
+
+        if (this._setCurrentRequest(invocation, id, ShellMountOperationType.ASK_PASSWORD)) {
+            this._dialog.reaskPassword();
+            return;
+        }
+
+        this._closeDialog();
+
+        this._dialog = new ShellMountPasswordDialog(message, this._createGIcon(iconName), flags);
+        this._dialog.connect('response', Lang.bind(this,
+            function(object, choice, password, remember) {
+                let details = {};
+                let response;
+
+                if (choice == -1) {
+                    response = Gio.MountOperationResult.ABORTED;
+                } else {
+                    response = Gio.MountOperationResult.HANDLED;
+
+                    let passSave = remember ? Gio.PasswordSave.PERMANENTLY : Gio.PasswordSave.NEVER;
+                    details['password_save'] = GLib.Variant.new('u', passSave);
+                    details['password'] = GLib.Variant.new('s', password);
+                }
+
+                this._clearCurrentRequest(response, details);
+            }));
+        this._dialog.open();
+    },
+
+    /**
+     * AskQuestion:
+     * @id: an opaque ID identifying the object for which the operation is requested
+     *      The ID must be unique in the context of the calling process.
+     * @message: the message to display
+     * @icon_name: the name of an icon to display
+     * @choices: an array of choice strings
+     * GetResponse:
+     * @response: a GMountOperationResult
+     * @response_details: a dictionary containing the response details as
+     * entered by the user. The dictionary MAY contain the following properties:
+     *   - "choice" -> (i): the chosen answer among the array of strings passed in
+     *
+     * The dialog will stay visible until clients call the Close() method, or
+     * another dialog becomes visible.
+     * Calling AskQuestion again for the same id will have the effect to clear
+     * update the dialog with the new question.
+     */
+    AskQuestionAsync: function(params, invocation) {
+        let [id, message, iconName, choices] = params;
+
+        if (this._setCurrentRequest(invocation, id, ShellMountOperationType.ASK_QUESTION)) {
+            this._dialog.update(message, choices);
+            return;
+        }
+
+        this._closeDialog();
+
+        this._dialog = new ShellMountQuestionDialog(this._createGIcon(iconName), message);
+        this._dialog.connect('response', Lang.bind(this,
+            function(object, choice) {
+                this._clearCurrentRequest(Gio.MountOperationResult.HANDLED,
+                                          { choice: GLib.Variant.new('i', choice) });
+            }));
+
+        this._dialog.update(message, choices);
+        this._dialog.open();
+    },
+
+    /**
+     * ShowProcesses:
+     * @id: an opaque ID identifying the object for which the operation is requested
+     *      The ID must be unique in the context of the calling process.
+     * @message: the message to display
+     * @icon_name: the name of an icon to display
+     * @application_pids: the PIDs of the applications to display
+     * @choices: an array of choice strings
+     * @response: a GMountOperationResult
+     * @response_details: a dictionary containing the response details as
+     * entered by the user. The dictionary MAY contain the following properties:
+     *   - "choice" -> (i): the chosen answer among the array of strings passed in
+     *
+     * The dialog will stay visible until clients call the Close() method, or
+     * another dialog becomes visible.
+     * Calling ShowProcesses again for the same id will have the effect to clear
+     * the existing dialog and update it with the new message and the new list
+     * of processes.
+     */
+    ShowProcessesAsync: function(params, invocation) {
+        let [id, message, iconName, applicationPids, choices] = params;
+
+        if (this._setCurrentRequest(invocation, id, ShellMountOperationType.SHOW_PROCESSES)) {
+            this._dialog.update(message, applicationPids, choices);
+            return;
+        }
+
+        this._closeDialog();
+
+        this._dialog = new ShellProcessesDialog(this._createGIcon(iconName));
+        this._dialog.connect('response', Lang.bind(this,
+            function(object, choice) {
+                let response;
+                let details = {};
+
+                if (choice == -1) {
+                    response = Gio.MountOperationResult.ABORTED;
+                } else {
+                    response = Gio.MountOperationResult.HANDLED;
+                    details['choice'] = GLib.Variant.new('i', choice);
+                }
+
+                this._clearCurrentRequest(response, details);
+            }));
+
+        this._dialog.update(message, applicationPids, choices);
+        this._dialog.open();
+    },
+
+    /**
+     * Close:
+     *
+     * Closes a dialog previously opened by AskPassword, AskQuestion or ShowProcesses.
+     * If no dialog is open, does nothing.
+     */
+    Close: function(params, invocation) {
+        this._clearCurrentRequest(Gio.MountOperationResult.UNHANDLED, {});
+        this._closeDialog();
+    }
+});



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]