mirror of
https://gitlab.com/thebiblelover7/dotfiles.git
synced 2025-12-14 04:03:50 +00:00
initial commit
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
'use strict';
|
||||
|
||||
const Gettext = imports.gettext;
|
||||
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
|
||||
const Config = imports.config;
|
||||
|
||||
|
||||
// Ensure config.js is setup properly
|
||||
const userDir = GLib.build_filenamev([GLib.get_user_data_dir(), 'gnome-shell']);
|
||||
|
||||
if (Config.PACKAGE_DATADIR.startsWith(userDir)) {
|
||||
Config.IS_USER = true;
|
||||
|
||||
Config.PACKAGE_LOCALEDIR = `${Config.PACKAGE_DATADIR}/locale`;
|
||||
Config.GSETTINGS_SCHEMA_DIR = `${Config.PACKAGE_DATADIR}/schemas`;
|
||||
}
|
||||
|
||||
|
||||
// Init Gettext
|
||||
String.prototype.format = imports.format.format;
|
||||
Gettext.bindtextdomain(Config.APP_ID, Config.PACKAGE_LOCALEDIR);
|
||||
globalThis._ = GLib.dgettext.bind(null, Config.APP_ID);
|
||||
globalThis.ngettext = GLib.dngettext.bind(null, Config.APP_ID);
|
||||
|
||||
|
||||
// Init GResources
|
||||
Gio.Resource.load(
|
||||
GLib.build_filenamev([Config.PACKAGE_DATADIR, `${Config.APP_ID}.gresource`])
|
||||
)._register();
|
||||
|
||||
|
||||
// Init GSchema
|
||||
Config.GSCHEMA = Gio.SettingsSchemaSource.new_from_directory(
|
||||
Config.GSETTINGS_SCHEMA_DIR,
|
||||
Gio.SettingsSchemaSource.get_default(),
|
||||
false
|
||||
);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,312 @@
|
||||
'use strict';
|
||||
|
||||
const Gdk = imports.gi.Gdk;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const GObject = imports.gi.GObject;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
|
||||
|
||||
/*
|
||||
* A list of modifier keysyms we ignore
|
||||
*/
|
||||
const _MODIFIERS = [
|
||||
Gdk.KEY_Alt_L,
|
||||
Gdk.KEY_Alt_R,
|
||||
Gdk.KEY_Caps_Lock,
|
||||
Gdk.KEY_Control_L,
|
||||
Gdk.KEY_Control_R,
|
||||
Gdk.KEY_Meta_L,
|
||||
Gdk.KEY_Meta_R,
|
||||
Gdk.KEY_Num_Lock,
|
||||
Gdk.KEY_Shift_L,
|
||||
Gdk.KEY_Shift_R,
|
||||
Gdk.KEY_Super_L,
|
||||
Gdk.KEY_Super_R,
|
||||
];
|
||||
|
||||
/**
|
||||
* Response enum for ShortcutChooserDialog
|
||||
*/
|
||||
var ResponseType = {
|
||||
CANCEL: Gtk.ResponseType.CANCEL,
|
||||
SET: Gtk.ResponseType.APPLY,
|
||||
UNSET: 2,
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A simplified version of the shortcut editor from GNOME Control Center
|
||||
*/
|
||||
var ShortcutChooserDialog = GObject.registerClass({
|
||||
GTypeName: 'GSConnectPreferencesShortcutEditor',
|
||||
Template: 'resource:///org/gnome/Shell/Extensions/GSConnect/ui/preferences-shortcut-editor.ui',
|
||||
Children: [
|
||||
'cancel-button', 'set-button',
|
||||
'stack', 'summary-label',
|
||||
'shortcut-label', 'conflict-label',
|
||||
],
|
||||
}, class ShortcutChooserDialog extends Gtk.Dialog {
|
||||
|
||||
_init(params) {
|
||||
super._init({
|
||||
transient_for: Gio.Application.get_default().get_active_window(),
|
||||
use_header_bar: true,
|
||||
});
|
||||
|
||||
this._seat = Gdk.Display.get_default().get_default_seat();
|
||||
|
||||
// Current accelerator or %null
|
||||
this.accelerator = params.accelerator;
|
||||
|
||||
// TRANSLATORS: Summary of a keyboard shortcut function
|
||||
// Example: Enter a new shortcut to change Messaging
|
||||
this.summary = _('Enter a new shortcut to change <b>%s</b>').format(
|
||||
params.summary
|
||||
);
|
||||
}
|
||||
|
||||
get accelerator() {
|
||||
return this.shortcut_label.accelerator;
|
||||
}
|
||||
|
||||
set accelerator(value) {
|
||||
this.shortcut_label.accelerator = value;
|
||||
}
|
||||
|
||||
get summary() {
|
||||
return this.summary_label.label;
|
||||
}
|
||||
|
||||
set summary(value) {
|
||||
this.summary_label.label = value;
|
||||
}
|
||||
|
||||
vfunc_key_press_event(event) {
|
||||
let keyvalLower = Gdk.keyval_to_lower(event.keyval);
|
||||
let realMask = event.state & Gtk.accelerator_get_default_mod_mask();
|
||||
|
||||
// TODO: Critical: 'WIDGET_REALIZED_FOR_EVENT (widget, event)' failed
|
||||
if (_MODIFIERS.includes(keyvalLower))
|
||||
return true;
|
||||
|
||||
// Normalize Tab
|
||||
if (keyvalLower === Gdk.KEY_ISO_Left_Tab)
|
||||
keyvalLower = Gdk.KEY_Tab;
|
||||
|
||||
// Put shift back if it changed the case of the key, not otherwise.
|
||||
if (keyvalLower !== event.keyval)
|
||||
realMask |= Gdk.ModifierType.SHIFT_MASK;
|
||||
|
||||
// HACK: we don't want to use SysRq as a keybinding (but we do want
|
||||
// Alt+Print), so we avoid translation from Alt+Print to SysRq
|
||||
if (keyvalLower === Gdk.KEY_Sys_Req && (realMask & Gdk.ModifierType.MOD1_MASK) !== 0)
|
||||
keyvalLower = Gdk.KEY_Print;
|
||||
|
||||
// A single Escape press cancels the editing
|
||||
if (realMask === 0 && keyvalLower === Gdk.KEY_Escape) {
|
||||
this.response(ResponseType.CANCEL);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Backspace disables the current shortcut
|
||||
if (realMask === 0 && keyvalLower === Gdk.KEY_BackSpace) {
|
||||
this.response(ResponseType.UNSET);
|
||||
return false;
|
||||
}
|
||||
|
||||
// CapsLock isn't supported as a keybinding modifier, so keep it from
|
||||
// confusing us
|
||||
realMask &= ~Gdk.ModifierType.LOCK_MASK;
|
||||
|
||||
if (keyvalLower !== 0 && realMask !== 0) {
|
||||
this._ungrab();
|
||||
|
||||
// Set the accelerator property/label
|
||||
this.accelerator = Gtk.accelerator_name(keyvalLower, realMask);
|
||||
|
||||
// TRANSLATORS: When a keyboard shortcut is unavailable
|
||||
// Example: [Ctrl]+[S] is already being used
|
||||
this.conflict_label.label = _('%s is already being used').format(
|
||||
Gtk.accelerator_get_label(keyvalLower, realMask)
|
||||
);
|
||||
|
||||
// Show Cancel button and switch to confirm/conflict page
|
||||
this.cancel_button.visible = true;
|
||||
this.stack.visible_child_name = 'confirm';
|
||||
|
||||
this._check();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
async _check() {
|
||||
try {
|
||||
const available = await checkAccelerator(this.accelerator);
|
||||
this.set_button.visible = available;
|
||||
this.conflict_label.visible = !available;
|
||||
} catch (e) {
|
||||
logError(e);
|
||||
this.response(ResponseType.CANCEL);
|
||||
}
|
||||
}
|
||||
|
||||
_grab() {
|
||||
const success = this._seat.grab(
|
||||
this.get_window(),
|
||||
Gdk.SeatCapabilities.KEYBOARD,
|
||||
true, // owner_events
|
||||
null, // cursor
|
||||
null, // event
|
||||
null
|
||||
);
|
||||
|
||||
if (success !== Gdk.GrabStatus.SUCCESS)
|
||||
return this.response(ResponseType.CANCEL);
|
||||
|
||||
if (!this._seat.get_keyboard() && !this._seat.get_pointer())
|
||||
return this.response(ResponseType.CANCEL);
|
||||
|
||||
this.grab_add();
|
||||
}
|
||||
|
||||
_ungrab() {
|
||||
this._seat.ungrab();
|
||||
this.grab_remove();
|
||||
}
|
||||
|
||||
// Override to use our own ungrab process
|
||||
response(response_id) {
|
||||
this.hide();
|
||||
this._ungrab();
|
||||
|
||||
return super.response(response_id);
|
||||
}
|
||||
|
||||
// Override with a non-blocking version of Gtk.Dialog.run()
|
||||
run() {
|
||||
this.show();
|
||||
|
||||
// Wait a bit before attempting grab
|
||||
GLib.timeout_add(GLib.PRIORITY_DEFAULT, 100, () => {
|
||||
this._grab();
|
||||
return GLib.SOURCE_REMOVE;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* Check the availability of an accelerator using GNOME Shell's DBus interface.
|
||||
*
|
||||
* @param {string} accelerator - An accelerator
|
||||
* @param {number} [modeFlags] - Mode Flags
|
||||
* @param {number} [grabFlags] - Grab Flags
|
||||
* @param {boolean} %true if available, %false on error or unavailable
|
||||
*/
|
||||
async function checkAccelerator(accelerator, modeFlags = 0, grabFlags = 0) {
|
||||
try {
|
||||
let result = false;
|
||||
|
||||
// Try to grab the accelerator
|
||||
const action = await new Promise((resolve, reject) => {
|
||||
Gio.DBus.session.call(
|
||||
'org.gnome.Shell',
|
||||
'/org/gnome/Shell',
|
||||
'org.gnome.Shell',
|
||||
'GrabAccelerator',
|
||||
new GLib.Variant('(suu)', [accelerator, modeFlags, grabFlags]),
|
||||
null,
|
||||
Gio.DBusCallFlags.NONE,
|
||||
-1,
|
||||
null,
|
||||
(connection, res) => {
|
||||
try {
|
||||
res = connection.call_finish(res);
|
||||
resolve(res.deepUnpack()[0]);
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
// If successful, use the result of ungrabbing as our return
|
||||
if (action !== 0) {
|
||||
result = await new Promise((resolve, reject) => {
|
||||
Gio.DBus.session.call(
|
||||
'org.gnome.Shell',
|
||||
'/org/gnome/Shell',
|
||||
'org.gnome.Shell',
|
||||
'UngrabAccelerator',
|
||||
new GLib.Variant('(u)', [action]),
|
||||
null,
|
||||
Gio.DBusCallFlags.NONE,
|
||||
-1,
|
||||
null,
|
||||
(connection, res) => {
|
||||
try {
|
||||
res = connection.call_finish(res);
|
||||
resolve(res.deepUnpack()[0]);
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
} catch (e) {
|
||||
logError(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Show a dialog to get a keyboard shortcut from a user.
|
||||
*
|
||||
* @param {string} summary - A description of the keybinding's function
|
||||
* @param {string} accelerator - An accelerator as taken by Gtk.ShortcutLabel
|
||||
* @return {string} An accelerator or %null if it should be unset.
|
||||
*/
|
||||
async function getAccelerator(summary, accelerator = null) {
|
||||
try {
|
||||
const dialog = new ShortcutChooserDialog({
|
||||
summary: summary,
|
||||
accelerator: accelerator,
|
||||
});
|
||||
|
||||
accelerator = await new Promise((resolve, reject) => {
|
||||
dialog.connect('response', (dialog, response) => {
|
||||
switch (response) {
|
||||
case ResponseType.SET:
|
||||
accelerator = dialog.accelerator;
|
||||
break;
|
||||
|
||||
case ResponseType.UNSET:
|
||||
accelerator = null;
|
||||
break;
|
||||
|
||||
case ResponseType.CANCEL:
|
||||
// leave the accelerator as passed in
|
||||
break;
|
||||
}
|
||||
|
||||
dialog.destroy();
|
||||
|
||||
resolve(accelerator);
|
||||
});
|
||||
|
||||
dialog.run();
|
||||
});
|
||||
|
||||
return accelerator;
|
||||
} catch (e) {
|
||||
logError(e);
|
||||
return accelerator;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,657 @@
|
||||
'use strict';
|
||||
|
||||
const Gdk = imports.gi.Gdk;
|
||||
const GdkPixbuf = imports.gi.GdkPixbuf;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const GObject = imports.gi.GObject;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
|
||||
const Config = imports.config;
|
||||
const Device = imports.preferences.device;
|
||||
const Remote = imports.utils.remote;
|
||||
|
||||
|
||||
/*
|
||||
* Header for support logs
|
||||
*/
|
||||
const LOG_HEADER = new GLib.Bytes(`
|
||||
GSConnect: ${Config.PACKAGE_VERSION} (${Config.IS_USER ? 'user' : 'system'})
|
||||
GJS: ${imports.system.version}
|
||||
Session: ${GLib.getenv('XDG_SESSION_TYPE')}
|
||||
OS: ${GLib.get_os_info('PRETTY_NAME')}
|
||||
--------------------------------------------------------------------------------
|
||||
`);
|
||||
|
||||
|
||||
/**
|
||||
* Generate a support log.
|
||||
*
|
||||
* @param {string} time - Start time as a string (24-hour notation)
|
||||
*/
|
||||
async function generateSupportLog(time) {
|
||||
try {
|
||||
const [file, stream] = Gio.File.new_tmp('gsconnect.XXXXXX');
|
||||
const logFile = stream.get_output_stream();
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
logFile.write_bytes_async(LOG_HEADER, 0, null, (file, res) => {
|
||||
try {
|
||||
resolve(file.write_bytes_finish(res));
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// FIXME: BSD???
|
||||
const proc = new Gio.Subprocess({
|
||||
flags: (Gio.SubprocessFlags.STDOUT_PIPE |
|
||||
Gio.SubprocessFlags.STDERR_MERGE),
|
||||
argv: ['journalctl', '--no-host', '--since', time],
|
||||
});
|
||||
proc.init(null);
|
||||
|
||||
logFile.splice_async(
|
||||
proc.get_stdout_pipe(),
|
||||
Gio.OutputStreamSpliceFlags.CLOSE_TARGET,
|
||||
GLib.PRIORITY_DEFAULT,
|
||||
null,
|
||||
(source, res) => {
|
||||
try {
|
||||
source.splice_finish(res);
|
||||
} catch (e) {
|
||||
logError(e);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
proc.wait_check_async(null, (proc, res) => {
|
||||
try {
|
||||
resolve(proc.wait_finish(res));
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const uri = file.get_uri();
|
||||
Gio.AppInfo.launch_default_for_uri_async(uri, null, null, null);
|
||||
} catch (e) {
|
||||
logError(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* "Connect to..." Dialog
|
||||
*/
|
||||
var ConnectDialog = GObject.registerClass({
|
||||
GTypeName: 'GSConnectConnectDialog',
|
||||
Template: 'resource:///org/gnome/Shell/Extensions/GSConnect/ui/connect-dialog.ui',
|
||||
Children: [
|
||||
'cancel-button', 'connect-button',
|
||||
'lan-grid', 'lan-ip', 'lan-port',
|
||||
],
|
||||
}, class ConnectDialog extends Gtk.Dialog {
|
||||
|
||||
_init(params = {}) {
|
||||
super._init(Object.assign({
|
||||
use_header_bar: true,
|
||||
}, params));
|
||||
}
|
||||
|
||||
vfunc_response(response_id) {
|
||||
if (response_id === Gtk.ResponseType.OK) {
|
||||
try {
|
||||
let address;
|
||||
|
||||
// Lan host/port entered
|
||||
if (this.lan_ip.text) {
|
||||
const host = this.lan_ip.text;
|
||||
const port = this.lan_port.value;
|
||||
address = GLib.Variant.new_string(`lan://${host}:${port}`);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.application.activate_action('connect', address);
|
||||
} catch (e) {
|
||||
logError(e);
|
||||
}
|
||||
}
|
||||
|
||||
this.destroy();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
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}));
|
||||
}
|
||||
|
||||
|
||||
var Window = GObject.registerClass({
|
||||
GTypeName: 'GSConnectPreferencesWindow',
|
||||
Properties: {
|
||||
'display-mode': GObject.ParamSpec.string(
|
||||
'display-mode',
|
||||
'Display Mode',
|
||||
'Display devices in either the Panel or User Menu',
|
||||
GObject.ParamFlags.READWRITE,
|
||||
null
|
||||
),
|
||||
},
|
||||
Template: 'resource:///org/gnome/Shell/Extensions/GSConnect/ui/preferences-window.ui',
|
||||
Children: [
|
||||
// HeaderBar
|
||||
'headerbar', 'infobar', 'stack',
|
||||
'service-menu', 'service-edit', 'refresh-button',
|
||||
'device-menu', 'prev-button',
|
||||
|
||||
// Popover
|
||||
'rename-popover', 'rename', 'rename-label', 'rename-entry', 'rename-submit',
|
||||
|
||||
// Focus Box
|
||||
'service-window', 'service-box',
|
||||
|
||||
// Device List
|
||||
'device-list', 'device-list-spinner', 'device-list-placeholder',
|
||||
],
|
||||
}, class PreferencesWindow extends Gtk.ApplicationWindow {
|
||||
|
||||
_init(params = {}) {
|
||||
super._init(params);
|
||||
|
||||
// Service Settings
|
||||
this.settings = new Gio.Settings({
|
||||
settings_schema: Config.GSCHEMA.lookup(
|
||||
'org.gnome.Shell.Extensions.GSConnect',
|
||||
true
|
||||
),
|
||||
});
|
||||
|
||||
// Service Proxy
|
||||
this.service = new Remote.Service();
|
||||
|
||||
this._deviceAddedId = this.service.connect(
|
||||
'device-added',
|
||||
this._onDeviceAdded.bind(this)
|
||||
);
|
||||
|
||||
this._deviceRemovedId = this.service.connect(
|
||||
'device-removed',
|
||||
this._onDeviceRemoved.bind(this)
|
||||
);
|
||||
|
||||
this._serviceChangedId = this.service.connect(
|
||||
'notify::active',
|
||||
this._onServiceChanged.bind(this)
|
||||
);
|
||||
|
||||
// HeaderBar (Service Name)
|
||||
this.headerbar.title = this.settings.get_string('name');
|
||||
this.rename_entry.text = this.headerbar.title;
|
||||
|
||||
// Scroll with keyboard focus
|
||||
this.service_box.set_focus_vadjustment(this.service_window.vadjustment);
|
||||
|
||||
// Device List
|
||||
this.device_list.set_header_func(rowSeparators);
|
||||
|
||||
// Discoverable InfoBar
|
||||
this.settings.bind(
|
||||
'discoverable',
|
||||
this.infobar,
|
||||
'reveal-child',
|
||||
Gio.SettingsBindFlags.INVERT_BOOLEAN
|
||||
);
|
||||
this.add_action(this.settings.create_action('discoverable'));
|
||||
|
||||
// Application Menu
|
||||
this._initMenu();
|
||||
|
||||
// Broadcast automatically every 5 seconds if there are no devices yet
|
||||
this._refreshSource = GLib.timeout_add_seconds(
|
||||
GLib.PRIORITY_DEFAULT,
|
||||
5,
|
||||
this._refresh.bind(this)
|
||||
);
|
||||
|
||||
// Restore window size/maximized/position
|
||||
this._restoreGeometry();
|
||||
|
||||
// Prime the service
|
||||
this._initService();
|
||||
}
|
||||
|
||||
get display_mode() {
|
||||
if (this.settings.get_boolean('show-indicators'))
|
||||
return 'panel';
|
||||
|
||||
return 'user-menu';
|
||||
}
|
||||
|
||||
set display_mode(mode) {
|
||||
this.settings.set_boolean('show-indicators', (mode === 'panel'));
|
||||
}
|
||||
|
||||
vfunc_delete_event(event) {
|
||||
if (this.service) {
|
||||
this.service.disconnect(this._deviceAddedId);
|
||||
this.service.disconnect(this._deviceRemovedId);
|
||||
this.service.disconnect(this._serviceChangedId);
|
||||
this.service.destroy();
|
||||
this.service = null;
|
||||
}
|
||||
|
||||
this._saveGeometry();
|
||||
GLib.source_remove(this._refreshSource);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
async _initService() {
|
||||
try {
|
||||
this.refresh_button.grab_focus();
|
||||
|
||||
this._onServiceChanged(this.service, null);
|
||||
await this.service.reload();
|
||||
} catch (e) {
|
||||
logError(e, 'GSConnect');
|
||||
}
|
||||
}
|
||||
|
||||
_initMenu() {
|
||||
// Panel/User Menu mode
|
||||
const displayMode = new Gio.PropertyAction({
|
||||
name: 'display-mode',
|
||||
property_name: 'display-mode',
|
||||
object: this,
|
||||
});
|
||||
this.add_action(displayMode);
|
||||
|
||||
// About Dialog
|
||||
const aboutDialog = new Gio.SimpleAction({name: 'about'});
|
||||
aboutDialog.connect('activate', this._aboutDialog.bind(this));
|
||||
this.add_action(aboutDialog);
|
||||
|
||||
// "Connect to..." Dialog
|
||||
const connectDialog = new Gio.SimpleAction({name: 'connect'});
|
||||
connectDialog.connect('activate', this._connectDialog.bind(this));
|
||||
this.add_action(connectDialog);
|
||||
|
||||
// "Generate Support Log" GAction
|
||||
const generateSupportLog = new Gio.SimpleAction({name: 'support-log'});
|
||||
generateSupportLog.connect('activate', this._generateSupportLog.bind(this));
|
||||
this.add_action(generateSupportLog);
|
||||
|
||||
// "Help" GAction
|
||||
const help = new Gio.SimpleAction({name: 'help'});
|
||||
help.connect('activate', this._help);
|
||||
this.add_action(help);
|
||||
}
|
||||
|
||||
_refresh() {
|
||||
if (this.service.active && this.device_list.get_children().length < 1) {
|
||||
this.device_list_spinner.active = true;
|
||||
this.service.activate_action('refresh', null);
|
||||
} else {
|
||||
this.device_list_spinner.active = false;
|
||||
}
|
||||
|
||||
return GLib.SOURCE_CONTINUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Window State
|
||||
*/
|
||||
_restoreGeometry() {
|
||||
this._windowState = new Gio.Settings({
|
||||
settings_schema: Config.GSCHEMA.lookup(
|
||||
'org.gnome.Shell.Extensions.GSConnect.WindowState',
|
||||
true
|
||||
),
|
||||
path: '/org/gnome/shell/extensions/gsconnect/preferences/',
|
||||
});
|
||||
|
||||
// Size
|
||||
const [width, height] = this._windowState.get_value('window-size').deepUnpack();
|
||||
|
||||
if (width && height)
|
||||
this.set_default_size(width, height);
|
||||
|
||||
// Maximized State
|
||||
if (this._windowState.get_boolean('window-maximized'))
|
||||
this.maximize();
|
||||
}
|
||||
|
||||
_saveGeometry() {
|
||||
const state = this.get_window().get_state();
|
||||
|
||||
// Maximized State
|
||||
const maximized = (state & Gdk.WindowState.MAXIMIZED);
|
||||
this._windowState.set_boolean('window-maximized', maximized);
|
||||
|
||||
// Leave the size at the value before maximizing
|
||||
if (maximized || (state & Gdk.WindowState.FULLSCREEN))
|
||||
return;
|
||||
|
||||
// Size
|
||||
const size = this.get_size();
|
||||
this._windowState.set_value('window-size', new GLib.Variant('(ii)', size));
|
||||
}
|
||||
|
||||
/**
|
||||
* About Dialog
|
||||
*/
|
||||
_aboutDialog() {
|
||||
if (this._about === undefined) {
|
||||
this._about = new Gtk.AboutDialog({
|
||||
application: Gio.Application.get_default(),
|
||||
authors: [
|
||||
'Andy Holmes <andrew.g.r.holmes@gmail.com>',
|
||||
'Bertrand Lacoste <getzze@gmail.com>',
|
||||
'Frank Dana <ferdnyc@gmail.com>',
|
||||
],
|
||||
comments: _('A complete KDE Connect implementation for GNOME'),
|
||||
logo: GdkPixbuf.Pixbuf.new_from_resource_at_scale(
|
||||
'/org/gnome/Shell/Extensions/GSConnect/icons/org.gnome.Shell.Extensions.GSConnect.svg',
|
||||
128,
|
||||
128,
|
||||
true
|
||||
),
|
||||
program_name: 'GSConnect',
|
||||
// TRANSLATORS: eg. 'Translator Name <your.email@domain.com>'
|
||||
translator_credits: _('translator-credits'),
|
||||
version: Config.PACKAGE_VERSION.toString(),
|
||||
website: Config.PACKAGE_URL,
|
||||
license_type: Gtk.License.GPL_2_0,
|
||||
modal: true,
|
||||
transient_for: this,
|
||||
});
|
||||
|
||||
// Persist
|
||||
this._about.connect('response', (dialog) => dialog.hide_on_delete());
|
||||
this._about.connect('delete-event', (dialog) => dialog.hide_on_delete());
|
||||
}
|
||||
|
||||
this._about.present();
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to..." Dialog
|
||||
*/
|
||||
_connectDialog() {
|
||||
new ConnectDialog({
|
||||
application: Gio.Application.get_default(),
|
||||
modal: true,
|
||||
transient_for: this,
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* "Generate Support Log" GAction
|
||||
*/
|
||||
_generateSupportLog() {
|
||||
const dialog = new Gtk.MessageDialog({
|
||||
text: _('Generate Support Log'),
|
||||
secondary_text: _('Debug messages are being logged. Take any steps necessary to reproduce a problem then review the log.'),
|
||||
});
|
||||
dialog.add_button(_('Cancel'), Gtk.ResponseType.CANCEL);
|
||||
dialog.add_button(_('Review Log'), Gtk.ResponseType.OK);
|
||||
|
||||
// Enable debug logging and mark the current time
|
||||
this.settings.set_boolean('debug', true);
|
||||
const now = GLib.DateTime.new_now_local().format('%R');
|
||||
|
||||
dialog.connect('response', (dialog, response_id) => {
|
||||
// Disable debug logging and destroy the dialog
|
||||
this.settings.set_boolean('debug', false);
|
||||
dialog.destroy();
|
||||
|
||||
// Only generate a log if instructed
|
||||
if (response_id === Gtk.ResponseType.OK)
|
||||
generateSupportLog(now);
|
||||
});
|
||||
|
||||
dialog.show_all();
|
||||
}
|
||||
|
||||
/*
|
||||
* "Help" GAction
|
||||
*/
|
||||
_help(action, parameter) {
|
||||
const uri = `${Config.PACKAGE_URL}/wiki/Help`;
|
||||
Gio.AppInfo.launch_default_for_uri_async(uri, null, null, null);
|
||||
}
|
||||
|
||||
/*
|
||||
* HeaderBar Callbacks
|
||||
*/
|
||||
_onPrevious(button, event) {
|
||||
// HeaderBar (Service)
|
||||
this.prev_button.visible = false;
|
||||
this.device_menu.visible = false;
|
||||
|
||||
this.refresh_button.visible = true;
|
||||
this.service_edit.visible = true;
|
||||
this.service_menu.visible = true;
|
||||
|
||||
this.headerbar.title = this.settings.get_string('name');
|
||||
this.headerbar.subtitle = null;
|
||||
|
||||
// Panel
|
||||
this.stack.visible_child_name = 'service';
|
||||
this._setDeviceMenu();
|
||||
}
|
||||
|
||||
_onEditServiceName(button, event) {
|
||||
this.rename_entry.text = this.headerbar.title;
|
||||
this.rename_entry.has_focus = true;
|
||||
}
|
||||
|
||||
_onSetServiceName(widget) {
|
||||
if (this.rename_entry.text.length) {
|
||||
this.headerbar.title = this.rename_entry.text;
|
||||
this.settings.set_string('name', this.rename_entry.text);
|
||||
}
|
||||
|
||||
this.service_edit.active = false;
|
||||
this.service_edit.grab_focus();
|
||||
}
|
||||
|
||||
/*
|
||||
* Context Switcher
|
||||
*/
|
||||
_getTypeLabel(device) {
|
||||
switch (device.type) {
|
||||
case 'laptop':
|
||||
return _('Laptop');
|
||||
case 'phone':
|
||||
return _('Smartphone');
|
||||
case 'tablet':
|
||||
return _('Tablet');
|
||||
case 'tv':
|
||||
return _('Television');
|
||||
default:
|
||||
return _('Desktop');
|
||||
}
|
||||
}
|
||||
|
||||
_setDeviceMenu(panel = null) {
|
||||
this.device_menu.insert_action_group('device', null);
|
||||
this.device_menu.insert_action_group('settings', null);
|
||||
this.device_menu.set_menu_model(null);
|
||||
|
||||
if (panel === null)
|
||||
return;
|
||||
|
||||
this.device_menu.insert_action_group('device', panel.device.action_group);
|
||||
this.device_menu.insert_action_group('settings', panel.actions);
|
||||
this.device_menu.set_menu_model(panel.menu);
|
||||
}
|
||||
|
||||
_onDeviceChanged(statusLabel, device, pspec) {
|
||||
switch (false) {
|
||||
case device.paired:
|
||||
statusLabel.label = _('Unpaired');
|
||||
break;
|
||||
|
||||
case device.connected:
|
||||
statusLabel.label = _('Disconnected');
|
||||
break;
|
||||
|
||||
default:
|
||||
statusLabel.label = _('Connected');
|
||||
}
|
||||
}
|
||||
|
||||
_createDeviceRow(device) {
|
||||
const row = new Gtk.ListBoxRow({
|
||||
height_request: 52,
|
||||
selectable: false,
|
||||
visible: true,
|
||||
});
|
||||
row.set_name(device.id);
|
||||
|
||||
const grid = new Gtk.Grid({
|
||||
column_spacing: 12,
|
||||
margin_left: 20,
|
||||
margin_right: 20,
|
||||
margin_bottom: 8,
|
||||
margin_top: 8,
|
||||
visible: true,
|
||||
});
|
||||
row.add(grid);
|
||||
|
||||
const icon = new Gtk.Image({
|
||||
gicon: new Gio.ThemedIcon({name: device.icon_name}),
|
||||
icon_size: Gtk.IconSize.BUTTON,
|
||||
visible: true,
|
||||
});
|
||||
grid.attach(icon, 0, 0, 1, 1);
|
||||
|
||||
const title = new Gtk.Label({
|
||||
halign: Gtk.Align.START,
|
||||
hexpand: true,
|
||||
valign: Gtk.Align.CENTER,
|
||||
vexpand: true,
|
||||
visible: true,
|
||||
});
|
||||
grid.attach(title, 1, 0, 1, 1);
|
||||
|
||||
const status = new Gtk.Label({
|
||||
halign: Gtk.Align.END,
|
||||
hexpand: true,
|
||||
valign: Gtk.Align.CENTER,
|
||||
vexpand: true,
|
||||
visible: true,
|
||||
});
|
||||
grid.attach(status, 2, 0, 1, 1);
|
||||
|
||||
// Keep name up to date
|
||||
device.bind_property(
|
||||
'name',
|
||||
title,
|
||||
'label',
|
||||
GObject.BindingFlags.SYNC_CREATE
|
||||
);
|
||||
|
||||
// Keep status up to date
|
||||
device.connect(
|
||||
'notify::connected',
|
||||
this._onDeviceChanged.bind(null, status)
|
||||
);
|
||||
device.connect(
|
||||
'notify::paired',
|
||||
this._onDeviceChanged.bind(null, status)
|
||||
);
|
||||
this._onDeviceChanged(status, device, null);
|
||||
|
||||
return row;
|
||||
}
|
||||
|
||||
_onDeviceAdded(service, device) {
|
||||
try {
|
||||
if (!this.stack.get_child_by_name(device.id)) {
|
||||
// Add the device preferences
|
||||
const prefs = new Device.Panel(device);
|
||||
this.stack.add_titled(prefs, device.id, device.name);
|
||||
|
||||
// Add a row to the device list
|
||||
prefs.row = this._createDeviceRow(device);
|
||||
this.device_list.add(prefs.row);
|
||||
}
|
||||
} catch (e) {
|
||||
logError(e);
|
||||
}
|
||||
}
|
||||
|
||||
_onDeviceRemoved(service, device) {
|
||||
try {
|
||||
const prefs = this.stack.get_child_by_name(device.id);
|
||||
|
||||
if (prefs === null)
|
||||
return;
|
||||
|
||||
if (prefs === this.stack.get_visible_child())
|
||||
this._onPrevious();
|
||||
|
||||
prefs.row.destroy();
|
||||
prefs.row = null;
|
||||
|
||||
prefs.dispose();
|
||||
prefs.destroy();
|
||||
} catch (e) {
|
||||
logError(e);
|
||||
}
|
||||
}
|
||||
|
||||
_onDeviceSelected(box, row) {
|
||||
try {
|
||||
if (row === null)
|
||||
return this._onPrevious();
|
||||
|
||||
// Transition the panel
|
||||
const name = row.get_name();
|
||||
const prefs = this.stack.get_child_by_name(name);
|
||||
|
||||
this.stack.visible_child = prefs;
|
||||
this._setDeviceMenu(prefs);
|
||||
|
||||
// HeaderBar (Device)
|
||||
this.refresh_button.visible = false;
|
||||
this.service_edit.visible = false;
|
||||
this.service_menu.visible = false;
|
||||
|
||||
this.prev_button.visible = true;
|
||||
this.device_menu.visible = true;
|
||||
|
||||
this.headerbar.title = prefs.device.name;
|
||||
this.headerbar.subtitle = this._getTypeLabel(prefs.device);
|
||||
} catch (e) {
|
||||
logError(e);
|
||||
}
|
||||
}
|
||||
|
||||
_onServiceChanged(service, pspec) {
|
||||
if (this.service.active)
|
||||
this.device_list_placeholder.label = _('Searching for devices…');
|
||||
else
|
||||
this.device_list_placeholder.label = _('Waiting for service…');
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user