c4c5c4fd5c
Remove the `this.actor = ...` and `this.actor._delegate = this` patterns in most of classes, by inheriting all the actor container classes. Uses interfaces when needed for making sure that multiple classes will implement some required methods or to avoid redefining the same code multiple times. https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/559
155 lines
5.4 KiB
JavaScript
155 lines
5.4 KiB
JavaScript
/* exported AccessDialogDBus */
|
|
const { Clutter, Gio, GLib, GObject, Shell } = imports.gi;
|
|
|
|
const CheckBox = imports.ui.checkBox;
|
|
const Dialog = imports.ui.dialog;
|
|
const ModalDialog = imports.ui.modalDialog;
|
|
|
|
const { loadInterfaceXML } = imports.misc.fileUtils;
|
|
|
|
const RequestIface = loadInterfaceXML('org.freedesktop.impl.portal.Request');
|
|
const AccessIface = loadInterfaceXML('org.freedesktop.impl.portal.Access');
|
|
|
|
var DialogResponse = {
|
|
OK: 0,
|
|
CANCEL: 1,
|
|
CLOSED: 2
|
|
};
|
|
|
|
var AccessDialog = GObject.registerClass(
|
|
class AccessDialog extends ModalDialog.ModalDialog {
|
|
_init(invocation, handle, title, subtitle, body, options) {
|
|
super._init({ styleClass: 'access-dialog' });
|
|
|
|
this._invocation = invocation;
|
|
this._handle = handle;
|
|
|
|
this._requestExported = false;
|
|
this._request = Gio.DBusExportedObject.wrapJSObject(RequestIface, this);
|
|
|
|
for (let option in options)
|
|
options[option] = options[option].deep_unpack();
|
|
|
|
this._buildLayout(title, subtitle, body, options);
|
|
}
|
|
|
|
_buildLayout(title, subtitle, body, options) {
|
|
// No support for non-modal system dialogs, so ignore the option
|
|
//let modal = options['modal'] || true;
|
|
let denyLabel = options['deny_label'] || _("Deny Access");
|
|
let grantLabel = options['grant_label'] || _("Grant Access");
|
|
let iconName = options['icon'] || null;
|
|
let choices = options['choices'] || [];
|
|
|
|
let contentParams = { title, subtitle, body };
|
|
if (iconName)
|
|
contentParams.icon = new Gio.ThemedIcon({ name: iconName });
|
|
let content = new Dialog.MessageDialogContent(contentParams);
|
|
this.contentLayout.add_actor(content);
|
|
|
|
this._choices = new Map();
|
|
|
|
for (let i = 0; i < choices.length; i++) {
|
|
let [id, name, opts, selected] = choices[i];
|
|
if (opts.length > 0)
|
|
continue; // radio buttons, not implemented
|
|
|
|
let check = new CheckBox.CheckBox();
|
|
check.getLabelActor().text = name;
|
|
check.checked = selected == "true";
|
|
content.insertBeforeBody(check);
|
|
|
|
this._choices.set(id, check);
|
|
}
|
|
|
|
this.addButton({ label: denyLabel,
|
|
action: () => {
|
|
this._sendResponse(DialogResponse.CANCEL);
|
|
},
|
|
key: Clutter.KEY_Escape });
|
|
this.addButton({ label: grantLabel,
|
|
action: () => {
|
|
this._sendResponse(DialogResponse.OK);
|
|
} });
|
|
}
|
|
|
|
open() {
|
|
super.open();
|
|
|
|
let connection = this._invocation.get_connection();
|
|
this._requestExported = this._request.export(connection, this._handle);
|
|
}
|
|
|
|
CloseAsync(invocation, _params) {
|
|
if (this._invocation.get_sender() != invocation.get_sender()) {
|
|
invocation.return_error_literal(Gio.DBusError,
|
|
Gio.DBusError.ACCESS_DENIED,
|
|
'');
|
|
return;
|
|
}
|
|
|
|
this._sendResponse(DialogResponse.CLOSED);
|
|
}
|
|
|
|
_sendResponse(response) {
|
|
if (this._requestExported)
|
|
this._request.unexport();
|
|
this._requestExported = false;
|
|
|
|
let results = {};
|
|
if (response == DialogResponse.OK) {
|
|
for (let [id, check] of this._choices) {
|
|
let checked = check.checked ? 'true' : 'false';
|
|
results[id] = new GLib.Variant('s', checked);
|
|
}
|
|
}
|
|
|
|
// Delay actual response until the end of the close animation (if any)
|
|
this.connect('closed', () => {
|
|
this._invocation.return_value(new GLib.Variant('(ua{sv})',
|
|
[response, results]));
|
|
});
|
|
this.close();
|
|
}
|
|
});
|
|
|
|
var AccessDialogDBus = class {
|
|
constructor() {
|
|
this._accessDialog = null;
|
|
|
|
this._windowTracker = Shell.WindowTracker.get_default();
|
|
|
|
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(AccessIface, this);
|
|
this._dbusImpl.export(Gio.DBus.session, '/org/freedesktop/portal/desktop');
|
|
|
|
Gio.DBus.session.own_name('org.freedesktop.impl.portal.desktop.gnome', Gio.BusNameOwnerFlags.REPLACE, null, null);
|
|
}
|
|
|
|
AccessDialogAsync(params, invocation) {
|
|
if (this._accessDialog) {
|
|
invocation.return_error_literal(Gio.DBusError,
|
|
Gio.DBusError.LIMITS_EXCEEDED,
|
|
'Already showing a system access dialog');
|
|
return;
|
|
}
|
|
|
|
let [handle, appId, parentWindow_, title, subtitle, body, options] = params;
|
|
// We probably want to use parentWindow and global.display.focus_window
|
|
// for this check in the future
|
|
if (appId && `${appId}.desktop` != this._windowTracker.focus_app.id) {
|
|
invocation.return_error_literal(Gio.DBusError,
|
|
Gio.DBusError.ACCESS_DENIED,
|
|
'Only the focused app is allowed to show a system access dialog');
|
|
return;
|
|
}
|
|
|
|
let dialog = new AccessDialog(invocation, handle, title,
|
|
subtitle, body, options);
|
|
dialog.open();
|
|
|
|
dialog.connect('closed', () => (this._accessDialog = null));
|
|
|
|
this._accessDialog = dialog;
|
|
}
|
|
};
|