12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112 |
- // SPDX-FileCopyrightText: GSConnect Developers https://github.com/GSConnect
- //
- // SPDX-License-Identifier: GPL-2.0-or-later
- 'use strict';
- const Gio = imports.gi.Gio;
- const GLib = imports.gi.GLib;
- const GObject = imports.gi.GObject;
- const Gtk = imports.gi.Gtk;
- const Pango = imports.gi.Pango;
- const Config = imports.config;
- const Keybindings = imports.preferences.keybindings;
- // Build a list of plugins and shortcuts for devices
- const DEVICE_PLUGINS = [];
- const DEVICE_SHORTCUTS = {};
- for (const name in imports.service.plugins) {
- const module = imports.service.plugins[name];
- if (module.Metadata === undefined)
- continue;
- // Plugins
- DEVICE_PLUGINS.push(name);
- // Shortcuts (GActions without parameters)
- for (const [name, action] of Object.entries(module.Metadata.actions)) {
- if (action.parameter_type === null)
- DEVICE_SHORTCUTS[name] = [action.icon_name, action.label];
- }
- }
- /**
- * A Gtk.ListBoxHeaderFunc for sections that adds separators between each row.
- *
- * @param {Gtk.ListBoxRow} row - The current row
- * @param {Gtk.ListBoxRow} before - The previous row
- */
- function rowSeparators(row, before) {
- const header = row.get_header();
- if (before === null) {
- if (header !== null)
- header.destroy();
- return;
- }
- if (header === null)
- row.set_header(new Gtk.Separator({visible: true}));
- }
- /**
- * A Gtk.ListBoxSortFunc for SectionRow rows
- *
- * @param {Gtk.ListBoxRow} row1 - The first row
- * @param {Gtk.ListBoxRow} row2 - The second row
- * @return {number} -1, 0 or 1
- */
- function titleSortFunc(row1, row2) {
- if (!row1.title || !row2.title)
- return 0;
- return row1.title.localeCompare(row2.title);
- }
- /**
- * A row for a section of settings
- */
- const SectionRow = GObject.registerClass({
- GTypeName: 'GSConnectPreferencesSectionRow',
- Template: 'resource:///org/gnome/Shell/Extensions/GSConnect/ui/preferences-section-row.ui',
- Children: ['icon-image', 'title-label', 'subtitle-label'],
- Properties: {
- 'gicon': GObject.ParamSpec.object(
- 'gicon',
- 'GIcon',
- 'A GIcon for the row',
- GObject.ParamFlags.READWRITE,
- Gio.Icon.$gtype
- ),
- 'icon-name': GObject.ParamSpec.string(
- 'icon-name',
- 'Icon Name',
- 'An icon name for the row',
- GObject.ParamFlags.READWRITE,
- null
- ),
- 'subtitle': GObject.ParamSpec.string(
- 'subtitle',
- 'Subtitle',
- 'A subtitle for the row',
- GObject.ParamFlags.READWRITE,
- null
- ),
- 'title': GObject.ParamSpec.string(
- 'title',
- 'Title',
- 'A title for the row',
- GObject.ParamFlags.READWRITE,
- null
- ),
- 'widget': GObject.ParamSpec.object(
- 'widget',
- 'Widget',
- 'An action widget for the row',
- GObject.ParamFlags.READWRITE,
- Gtk.Widget.$gtype
- ),
- },
- }, class SectionRow extends Gtk.ListBoxRow {
- _init(params = {}) {
- super._init();
- // NOTE: we can't pass construct properties to _init() because the
- // template children are not assigned until after it runs.
- this.freeze_notify();
- Object.assign(this, params);
- this.thaw_notify();
- }
- get icon_name() {
- return this.icon_image.icon_name;
- }
- set icon_name(icon_name) {
- if (this.icon_name === icon_name)
- return;
- this.icon_image.visible = !!icon_name;
- this.icon_image.icon_name = icon_name;
- this.notify('icon-name');
- }
- get gicon() {
- return this.icon_image.gicon;
- }
- set gicon(gicon) {
- if (this.gicon === gicon)
- return;
- this.icon_image.visible = !!gicon;
- this.icon_image.gicon = gicon;
- this.notify('gicon');
- }
- get title() {
- return this.title_label.label;
- }
- set title(text) {
- if (this.title === text)
- return;
- this.title_label.visible = !!text;
- this.title_label.label = text;
- this.notify('title');
- }
- get subtitle() {
- return this.subtitle_label.label;
- }
- set subtitle(text) {
- if (this.subtitle === text)
- return;
- this.subtitle_label.visible = !!text;
- this.subtitle_label.label = text;
- this.notify('subtitle');
- }
- get widget() {
- if (this._widget === undefined)
- this._widget = null;
- return this._widget;
- }
- set widget(widget) {
- if (this.widget === widget)
- return;
- if (this.widget instanceof Gtk.Widget)
- this.widget.destroy();
- // Add the widget
- this._widget = widget;
- this.get_child().attach(widget, 2, 0, 1, 2);
- this.notify('widget');
- }
- });
- /**
- * Command Editor Dialog
- */
- const CommandEditor = GObject.registerClass({
- GTypeName: 'GSConnectPreferencesCommandEditor',
- Template: 'resource:///org/gnome/Shell/Extensions/GSConnect/ui/preferences-command-editor.ui',
- Children: [
- 'cancel-button', 'save-button',
- 'command-entry', 'name-entry', 'command-chooser',
- ],
- }, class CommandEditor extends Gtk.Dialog {
- _onBrowseCommand(entry, icon_pos, event) {
- this.command_chooser.present();
- }
- _onCommandChosen(dialog, response_id) {
- if (response_id === Gtk.ResponseType.OK)
- this.command_entry.text = dialog.get_filename();
- dialog.hide();
- }
- _onEntryChanged(entry, pspec) {
- this.save_button.sensitive = (this.command_name && this.command_line);
- }
- get command_line() {
- return this.command_entry.text;
- }
- set command_line(text) {
- this.command_entry.text = text;
- }
- get command_name() {
- return this.name_entry.text;
- }
- set command_name(text) {
- this.name_entry.text = text;
- }
- });
- /**
- * A widget for configuring a remote device.
- */
- var Panel = GObject.registerClass({
- GTypeName: 'GSConnectPreferencesDevicePanel',
- Properties: {
- 'device': GObject.ParamSpec.object(
- 'device',
- 'Device',
- 'The device being configured',
- GObject.ParamFlags.READWRITE,
- GObject.Object.$gtype
- ),
- },
- Template: 'resource:///org/gnome/Shell/Extensions/GSConnect/ui/preferences-device-panel.ui',
- Children: [
- 'sidebar', 'stack', 'infobar',
- // Sharing
- 'sharing', 'sharing-page',
- 'desktop-list', 'clipboard', 'clipboard-sync', 'mousepad', 'mpris', 'systemvolume',
- 'share', 'share-list', 'receive-files', 'receive-directory',
- // Battery
- 'battery',
- 'battery-device-label', 'battery-device', 'battery-device-list',
- 'battery-system-label', 'battery-system', 'battery-system-list',
- 'battery-custom-notification-value',
- // RunCommand
- 'runcommand', 'runcommand-page',
- 'command-list', 'command-add',
- // Notifications
- 'notification', 'notification-page',
- 'notification-list', 'notification-apps',
- // Telephony
- 'telephony', 'telephony-page',
- 'ringing-list', 'ringing-volume', 'talking-list', 'talking-volume',
- // Shortcuts
- 'shortcuts-page',
- 'shortcuts-actions', 'shortcuts-actions-title', 'shortcuts-actions-list',
- // Advanced
- 'advanced-page',
- 'plugin-list', 'experimental-list',
- 'device-menu',
- ],
- }, class Panel extends Gtk.Grid {
- _init(device) {
- super._init({
- device: device,
- });
- // GSettings
- this.settings = new Gio.Settings({
- settings_schema: Config.GSCHEMA.lookup(
- 'org.gnome.Shell.Extensions.GSConnect.Device',
- true
- ),
- path: `/org/gnome/shell/extensions/gsconnect/device/${device.id}/`,
- });
- // Infobar
- this.device.bind_property(
- 'paired',
- this.infobar,
- 'reveal-child',
- (GObject.BindingFlags.SYNC_CREATE |
- GObject.BindingFlags.INVERT_BOOLEAN)
- );
- this._setupActions();
- // Settings Pages
- this._sharingSettings();
- this._batterySettings();
- this._runcommandSettings();
- this._notificationSettings();
- this._telephonySettings();
- // --------------------------
- this._keybindingSettings();
- this._advancedSettings();
- // Separate plugins and other settings
- this.sidebar.set_header_func((row, before) => {
- if (row.get_name() === 'shortcuts')
- row.set_header(new Gtk.Separator({visible: true}));
- });
- }
- get menu() {
- if (this._menu === undefined) {
- this._menu = this.device_menu;
- this._menu.prepend_section(null, this.device.menu);
- this.insert_action_group('device', this.device.action_group);
- }
- return this._menu;
- }
- get_incoming_supported(type) {
- const incoming = this.settings.get_strv('incoming-capabilities');
- return incoming.includes(`kdeconnect.${type}`);
- }
- get_outgoing_supported(type) {
- const outgoing = this.settings.get_strv('outgoing-capabilities');
- return outgoing.includes(`kdeconnect.${type}`);
- }
- _onKeynavFailed(widget, direction) {
- if (direction === Gtk.DirectionType.UP && widget.prev)
- widget.prev.child_focus(direction);
- else if (direction === Gtk.DirectionType.DOWN && widget.next)
- widget.next.child_focus(direction);
- return true;
- }
- _onSwitcherRowSelected(box, row) {
- this.stack.set_visible_child_name(row.get_name());
- }
- _onSectionRowActivated(box, row) {
- if (row.widget !== undefined)
- row.widget.active = !row.widget.active;
- }
- _onToggleRowActivated(box, row) {
- const widget = row.get_child().get_child_at(1, 0);
- widget.active = !widget.active;
- }
- _onEncryptionInfo() {
- const dialog = new Gtk.MessageDialog({
- buttons: Gtk.ButtonsType.OK,
- text: _('Encryption Info'),
- secondary_text: this.device.encryption_info,
- modal: true,
- transient_for: this.get_toplevel(),
- });
- dialog.connect('response', (dialog) => dialog.destroy());
- dialog.present();
- }
- _deviceAction(action, parameter) {
- this.action_group.activate_action(action.name, parameter);
- }
- dispose() {
- if (this._commandEditor !== undefined)
- this._commandEditor.destroy();
- // Device signals
- this.device.action_group.disconnect(this._actionAddedId);
- this.device.action_group.disconnect(this._actionRemovedId);
- // GSettings
- for (const settings of Object.values(this._pluginSettings))
- settings.run_dispose();
- this.settings.disconnect(this._keybindingsId);
- this.settings.disconnect(this._disabledPluginsId);
- this.settings.disconnect(this._supportedPluginsId);
- this.settings.run_dispose();
- }
- pluginSettings(name) {
- if (this._pluginSettings === undefined)
- this._pluginSettings = {};
- if (!this._pluginSettings.hasOwnProperty(name)) {
- const meta = imports.service.plugins[name].Metadata;
- this._pluginSettings[name] = new Gio.Settings({
- settings_schema: Config.GSCHEMA.lookup(meta.id, -1),
- path: `${this.settings.path}plugin/${name}/`,
- });
- }
- return this._pluginSettings[name];
- }
- _setupActions() {
- this.actions = new Gio.SimpleActionGroup();
- this.insert_action_group('settings', this.actions);
- let settings = this.pluginSettings('battery');
- this.actions.add_action(settings.create_action('send-statistics'));
- this.actions.add_action(settings.create_action('low-battery-notification'));
- this.actions.add_action(settings.create_action('custom-battery-notification'));
- this.actions.add_action(settings.create_action('custom-battery-notification-value'));
- this.actions.add_action(settings.create_action('full-battery-notification'));
- settings = this.pluginSettings('clipboard');
- this.actions.add_action(settings.create_action('send-content'));
- this.actions.add_action(settings.create_action('receive-content'));
- settings = this.pluginSettings('contacts');
- this.actions.add_action(settings.create_action('contacts-source'));
- settings = this.pluginSettings('mousepad');
- this.actions.add_action(settings.create_action('share-control'));
- settings = this.pluginSettings('mpris');
- this.actions.add_action(settings.create_action('share-players'));
- settings = this.pluginSettings('notification');
- this.actions.add_action(settings.create_action('send-notifications'));
- this.actions.add_action(settings.create_action('send-active'));
- settings = this.pluginSettings('sftp');
- this.actions.add_action(settings.create_action('automount'));
- settings = this.pluginSettings('share');
- this.actions.add_action(settings.create_action('receive-files'));
- settings = this.pluginSettings('sms');
- this.actions.add_action(settings.create_action('legacy-sms'));
- settings = this.pluginSettings('systemvolume');
- this.actions.add_action(settings.create_action('share-sinks'));
- settings = this.pluginSettings('telephony');
- this.actions.add_action(settings.create_action('ringing-volume'));
- this.actions.add_action(settings.create_action('ringing-pause'));
- this.actions.add_action(settings.create_action('talking-volume'));
- this.actions.add_action(settings.create_action('talking-pause'));
- this.actions.add_action(settings.create_action('talking-microphone'));
- // Pair Actions
- const encryption_info = new Gio.SimpleAction({name: 'encryption-info'});
- encryption_info.connect('activate', this._onEncryptionInfo.bind(this));
- this.actions.add_action(encryption_info);
- const status_pair = new Gio.SimpleAction({name: 'pair'});
- status_pair.connect('activate', this._deviceAction.bind(this.device));
- this.settings.bind('paired', status_pair, 'enabled', 16);
- this.actions.add_action(status_pair);
- const status_unpair = new Gio.SimpleAction({name: 'unpair'});
- status_unpair.connect('activate', this._deviceAction.bind(this.device));
- this.settings.bind('paired', status_unpair, 'enabled', 0);
- this.actions.add_action(status_unpair);
- }
- /**
- * Sharing Settings
- */
- _sharingSettings() {
- // Share Plugin
- const settings = this.pluginSettings('share');
- settings.connect(
- 'changed::receive-directory',
- this._onReceiveDirectoryChanged.bind(this)
- );
- this._onReceiveDirectoryChanged(settings, 'receive-directory');
- // Visibility
- this.desktop_list.foreach(row => {
- const name = row.get_name();
- row.visible = this.get_outgoing_supported(`${name}.request`);
- });
- // Separators & Sorting
- this.desktop_list.set_header_func(rowSeparators);
- this.desktop_list.set_sort_func((row1, row2) => {
- row1 = row1.get_child().get_child_at(0, 0);
- row2 = row2.get_child().get_child_at(0, 0);
- return row1.label.localeCompare(row2.label);
- });
- this.share_list.set_header_func(rowSeparators);
- // Scroll with keyboard focus
- const sharing_box = this.sharing_page.get_child().get_child();
- sharing_box.set_focus_vadjustment(this.sharing_page.vadjustment);
- // Continue focus chain between lists
- this.desktop_list.next = this.share_list;
- this.share_list.prev = this.desktop_list;
- }
- _onReceiveDirectoryChanged(settings, key) {
- let receiveDir = settings.get_string(key);
- if (receiveDir.length === 0) {
- receiveDir = GLib.get_user_special_dir(
- GLib.UserDirectory.DIRECTORY_DOWNLOAD
- );
- // Account for some corner cases with a fallback
- const homeDir = GLib.get_home_dir();
- if (!receiveDir || receiveDir === homeDir)
- receiveDir = GLib.build_filenamev([homeDir, 'Downloads']);
- settings.set_string(key, receiveDir);
- }
- if (this.receive_directory.get_filename() !== receiveDir)
- this.receive_directory.set_filename(receiveDir);
- }
- _onReceiveDirectorySet(button) {
- const settings = this.pluginSettings('share');
- const receiveDir = settings.get_string('receive-directory');
- const filename = button.get_filename();
- if (filename !== receiveDir)
- settings.set_string('receive-directory', filename);
- }
- /**
- * Battery Settings
- */
- async _batterySettings() {
- try {
- this.battery_device_list.set_header_func(rowSeparators);
- this.battery_system_list.set_header_func(rowSeparators);
- const settings = this.pluginSettings('battery');
- const oldLevel = settings.get_uint('custom-battery-notification-value');
- this.battery_custom_notification_value.set_value(oldLevel);
- // If the device can't handle statistics we're done
- if (!this.get_incoming_supported('battery')) {
- this.battery_system_label.visible = false;
- this.battery_system.visible = false;
- return;
- }
- // Check UPower for a battery
- const hasBattery = await new Promise((resolve, reject) => {
- Gio.DBus.system.call(
- 'org.freedesktop.UPower',
- '/org/freedesktop/UPower/devices/DisplayDevice',
- 'org.freedesktop.DBus.Properties',
- 'Get',
- new GLib.Variant('(ss)', [
- 'org.freedesktop.UPower.Device',
- 'IsPresent',
- ]),
- null,
- Gio.DBusCallFlags.NONE,
- -1,
- null,
- (connection, res) => {
- try {
- const variant = connection.call_finish(res);
- const value = variant.deepUnpack()[0];
- const isPresent = value.get_boolean();
- resolve(isPresent);
- } catch (e) {
- resolve(false);
- }
- }
- );
- });
- this.battery_system_label.visible = hasBattery;
- this.battery_system.visible = hasBattery;
- } catch (e) {
- this.battery_system_label.visible = false;
- this.battery_system.visible = false;
- }
- }
- _setCustomChargeLevel(spin) {
- const settings = this.pluginSettings('battery');
- settings.set_uint('custom-battery-notification-value', spin.get_value_as_int());
- }
- /**
- * RunCommand Page
- */
- _runcommandSettings() {
- // Scroll with keyboard focus
- const runcommand_box = this.runcommand_page.get_child().get_child();
- runcommand_box.set_focus_vadjustment(this.runcommand_page.vadjustment);
- // Local Command List
- const settings = this.pluginSettings('runcommand');
- this._commands = settings.get_value('command-list').recursiveUnpack();
- this.command_list.set_sort_func(this._sortCommands);
- this.command_list.set_header_func(rowSeparators);
- for (const uuid of Object.keys(this._commands))
- this._insertCommand(uuid);
- }
- _sortCommands(row1, row2) {
- if (!row1.title || !row2.title)
- return 1;
- return row1.title.localeCompare(row2.title);
- }
- _insertCommand(uuid) {
- const row = new SectionRow({
- title: this._commands[uuid].name,
- subtitle: this._commands[uuid].command,
- activatable: false,
- });
- row.set_name(uuid);
- row.subtitle_label.ellipsize = Pango.EllipsizeMode.MIDDLE;
- const editButton = new Gtk.Button({
- image: new Gtk.Image({
- icon_name: 'document-edit-symbolic',
- pixel_size: 16,
- visible: true,
- }),
- tooltip_text: _('Edit'),
- valign: Gtk.Align.CENTER,
- vexpand: true,
- visible: true,
- });
- editButton.connect('clicked', this._onEditCommand.bind(this));
- editButton.get_accessible().set_name(_('Edit'));
- row.get_child().attach(editButton, 2, 0, 1, 2);
- const deleteButton = new Gtk.Button({
- image: new Gtk.Image({
- icon_name: 'edit-delete-symbolic',
- pixel_size: 16,
- visible: true,
- }),
- tooltip_text: _('Remove'),
- valign: Gtk.Align.CENTER,
- vexpand: true,
- visible: true,
- });
- deleteButton.connect('clicked', this._onDeleteCommand.bind(this));
- deleteButton.get_accessible().set_name(_('Remove'));
- row.get_child().attach(deleteButton, 3, 0, 1, 2);
- this.command_list.add(row);
- }
- _onEditCommand(widget) {
- if (this._commandEditor === undefined) {
- this._commandEditor = new CommandEditor({
- modal: true,
- transient_for: this.get_toplevel(),
- use_header_bar: true,
- });
- this._commandEditor.connect(
- 'response',
- this._onSaveCommand.bind(this)
- );
- this._commandEditor.resize(1, 1);
- }
- if (widget instanceof Gtk.Button) {
- const row = widget.get_ancestor(Gtk.ListBoxRow.$gtype);
- const uuid = row.get_name();
- this._commandEditor.uuid = uuid;
- this._commandEditor.command_name = this._commands[uuid].name;
- this._commandEditor.command_line = this._commands[uuid].command;
- } else {
- this._commandEditor.uuid = GLib.uuid_string_random();
- this._commandEditor.command_name = '';
- this._commandEditor.command_line = '';
- }
- this._commandEditor.present();
- }
- _storeCommands() {
- const variant = {};
- for (const [uuid, command] of Object.entries(this._commands))
- variant[uuid] = new GLib.Variant('a{ss}', command);
- this.pluginSettings('runcommand').set_value(
- 'command-list',
- new GLib.Variant('a{sv}', variant)
- );
- }
- _onDeleteCommand(button) {
- const row = button.get_ancestor(Gtk.ListBoxRow.$gtype);
- delete this._commands[row.get_name()];
- row.destroy();
- this._storeCommands();
- }
- _onSaveCommand(dialog, response_id) {
- if (response_id === Gtk.ResponseType.ACCEPT) {
- this._commands[dialog.uuid] = {
- name: dialog.command_name,
- command: dialog.command_line,
- };
- this._storeCommands();
- //
- let row = null;
- for (const child of this.command_list.get_children()) {
- if (child.get_name() === dialog.uuid) {
- row = child;
- break;
- }
- }
- if (row === null) {
- this._insertCommand(dialog.uuid);
- } else {
- row.set_name(dialog.uuid);
- row.title = dialog.command_name;
- row.subtitle = dialog.command_line;
- }
- }
- dialog.hide();
- }
- /**
- * Notification Settings
- */
- _notificationSettings() {
- const settings = this.pluginSettings('notification');
- settings.bind(
- 'send-notifications',
- this.notification_apps,
- 'sensitive',
- Gio.SettingsBindFlags.DEFAULT
- );
- // Separators & Sorting
- this.notification_list.set_header_func(rowSeparators);
- // Scroll with keyboard focus
- const notification_box = this.notification_page.get_child().get_child();
- notification_box.set_focus_vadjustment(this.notification_page.vadjustment);
- // Continue focus chain between lists
- this.notification_list.next = this.notification_apps;
- this.notification_apps.prev = this.notification_list;
- this.notification_apps.set_sort_func(titleSortFunc);
- this.notification_apps.set_header_func(rowSeparators);
- this._populateApplications(settings);
- }
- _toggleNotification(widget) {
- try {
- const row = widget.get_ancestor(Gtk.ListBoxRow.$gtype);
- const settings = this.pluginSettings('notification');
- let applications = {};
- try {
- applications = JSON.parse(settings.get_string('applications'));
- } catch (e) {
- applications = {};
- }
- applications[row.title].enabled = !applications[row.title].enabled;
- row.widget.state = applications[row.title].enabled;
- settings.set_string('applications', JSON.stringify(applications));
- } catch (e) {
- logError(e);
- }
- }
- _populateApplications(settings) {
- const applications = this._queryApplications(settings);
- for (const name in applications) {
- const row = new SectionRow({
- gicon: Gio.Icon.new_for_string(applications[name].iconName),
- title: name,
- height_request: 48,
- widget: new Gtk.Switch({
- state: applications[name].enabled,
- margin_start: 12,
- margin_end: 12,
- halign: Gtk.Align.END,
- valign: Gtk.Align.CENTER,
- vexpand: true,
- visible: true,
- }),
- });
- row.widget.connect('notify::active', this._toggleNotification.bind(this));
- this.notification_apps.add(row);
- }
- }
- _queryApplications(settings) {
- let applications = {};
- try {
- applications = JSON.parse(settings.get_string('applications'));
- } catch (e) {
- applications = {};
- }
- // Scan applications that statically declare to show notifications
- const ignoreId = 'org.gnome.Shell.Extensions.GSConnect.desktop';
- for (const appInfo of Gio.AppInfo.get_all()) {
- if (appInfo.get_id() === ignoreId)
- continue;
- if (!appInfo.get_boolean('X-GNOME-UsesNotifications'))
- continue;
- const appName = appInfo.get_name();
- if (appName === null || applications.hasOwnProperty(appName))
- continue;
- let icon = appInfo.get_icon();
- icon = (icon) ? icon.to_string() : 'application-x-executable';
- applications[appName] = {
- iconName: icon,
- enabled: true,
- };
- }
- settings.set_string('applications', JSON.stringify(applications));
- return applications;
- }
- /**
- * Telephony Settings
- */
- _telephonySettings() {
- // Continue focus chain between lists
- this.ringing_list.next = this.talking_list;
- this.talking_list.prev = this.ringing_list;
- this.ringing_list.set_header_func(rowSeparators);
- this.talking_list.set_header_func(rowSeparators);
- }
- /**
- * Keyboard Shortcuts
- */
- _keybindingSettings() {
- // Scroll with keyboard focus
- const shortcuts_box = this.shortcuts_page.get_child().get_child();
- shortcuts_box.set_focus_vadjustment(this.shortcuts_page.vadjustment);
- // Filter & Sort
- this.shortcuts_actions_list.set_filter_func(this._filterPluginKeybindings.bind(this));
- this.shortcuts_actions_list.set_header_func(rowSeparators);
- this.shortcuts_actions_list.set_sort_func(titleSortFunc);
- // Init
- for (const name in DEVICE_SHORTCUTS)
- this._addPluginKeybinding(name);
- this._setPluginKeybindings();
- // Watch for GAction and Keybinding changes
- this._actionAddedId = this.device.action_group.connect(
- 'action-added',
- () => this.shortcuts_actions_list.invalidate_filter()
- );
- this._actionRemovedId = this.device.action_group.connect(
- 'action-removed',
- () => this.shortcuts_actions_list.invalidate_filter()
- );
- this._keybindingsId = this.settings.connect(
- 'changed::keybindings',
- this._setPluginKeybindings.bind(this)
- );
- }
- _addPluginKeybinding(name) {
- const [icon_name, label] = DEVICE_SHORTCUTS[name];
- const widget = new Gtk.Label({
- label: _('Disabled'),
- visible: true,
- });
- widget.get_style_context().add_class('dim-label');
- const row = new SectionRow({
- height_request: 48,
- icon_name: icon_name,
- title: label,
- widget: widget,
- });
- row.icon_image.pixel_size = 16;
- row.action = name;
- this.shortcuts_actions_list.add(row);
- }
- _filterPluginKeybindings(row) {
- return this.device.action_group.has_action(row.action);
- }
- _setPluginKeybindings() {
- const keybindings = this.settings.get_value('keybindings').deepUnpack();
- this.shortcuts_actions_list.foreach(row => {
- if (keybindings[row.action]) {
- const accel = Gtk.accelerator_parse(keybindings[row.action]);
- row.widget.label = Gtk.accelerator_get_label(...accel);
- } else {
- row.widget.label = _('Disabled');
- }
- });
- }
- _onResetActionShortcuts(button) {
- const keybindings = this.settings.get_value('keybindings').deepUnpack();
- for (const action in keybindings) {
- // Don't reset remote command shortcuts
- if (!action.includes('::'))
- delete keybindings[action];
- }
- this.settings.set_value(
- 'keybindings',
- new GLib.Variant('a{ss}', keybindings)
- );
- }
- async _onShortcutRowActivated(box, row) {
- try {
- const keybindings = this.settings.get_value('keybindings').deepUnpack();
- let accel = keybindings[row.action] || null;
- accel = await Keybindings.getAccelerator(row.title, accel);
- if (accel)
- keybindings[row.action] = accel;
- else
- delete keybindings[row.action];
- this.settings.set_value(
- 'keybindings',
- new GLib.Variant('a{ss}', keybindings)
- );
- } catch (e) {
- logError(e);
- }
- }
- /**
- * Advanced Page
- */
- _advancedSettings() {
- // Scroll with keyboard focus
- const advanced_box = this.advanced_page.get_child().get_child();
- advanced_box.set_focus_vadjustment(this.advanced_page.vadjustment);
- // Sort & Separate
- this.plugin_list.set_header_func(rowSeparators);
- this.plugin_list.set_sort_func(titleSortFunc);
- this.experimental_list.set_header_func(rowSeparators);
- // Continue focus chain between lists
- this.plugin_list.next = this.experimental_list;
- this.experimental_list.prev = this.plugin_list;
- this._disabledPluginsId = this.settings.connect(
- 'changed::disabled-plugins',
- this._onPluginsChanged.bind(this)
- );
- this._supportedPluginsId = this.settings.connect(
- 'changed::supported-plugins',
- this._onPluginsChanged.bind(this)
- );
- this._onPluginsChanged(this.settings, null);
- for (const name of DEVICE_PLUGINS)
- this._addPlugin(name);
- }
- _onPluginsChanged(settings, key) {
- if (key === 'disabled-plugins' || this._disabledPlugins === undefined)
- this._disabledPlugins = settings.get_strv('disabled-plugins');
- if (key === 'supported-plugins' || this._supportedPlugins === undefined)
- this._supportedPlugins = settings.get_strv('supported-plugins');
- this._enabledPlugins = this._supportedPlugins.filter(name => {
- return !this._disabledPlugins.includes(name);
- });
- if (key !== null)
- this._updatePlugins();
- }
- _addPlugin(name) {
- const plugin = imports.service.plugins[name];
- const row = new SectionRow({
- height_request: 48,
- title: plugin.Metadata.label,
- subtitle: plugin.Metadata.description || '',
- visible: this._supportedPlugins.includes(name),
- widget: new Gtk.Switch({
- active: this._enabledPlugins.includes(name),
- valign: Gtk.Align.CENTER,
- vexpand: true,
- visible: true,
- }),
- });
- row.widget.connect('notify::active', this._togglePlugin.bind(this));
- row.set_name(name);
- if (this.hasOwnProperty(name))
- this[name].visible = row.widget.active;
- this.plugin_list.add(row);
- }
- _updatePlugins(settings, key) {
- for (const row of this.plugin_list.get_children()) {
- const name = row.get_name();
- row.visible = this._supportedPlugins.includes(name);
- row.widget.active = this._enabledPlugins.includes(name);
- if (this.hasOwnProperty(name))
- this[name].visible = row.widget.active;
- }
- }
- _togglePlugin(widget) {
- try {
- const name = widget.get_ancestor(Gtk.ListBoxRow.$gtype).get_name();
- const index = this._disabledPlugins.indexOf(name);
- // Either add or remove the plugin from the disabled list
- if (index > -1)
- this._disabledPlugins.splice(index, 1);
- else
- this._disabledPlugins.push(name);
- this.settings.set_strv('disabled-plugins', this._disabledPlugins);
- } catch (e) {
- logError(e);
- }
- }
- });
|