.dotfiles/.local/share/gnome-shell/extensions/ding@rastersoft.com/app/desktopIconsUtil.js

357 lines
10 KiB
JavaScript

/* DING: Desktop Icons New Generation for GNOME Shell
*
* Copyright (C) 2019 Sergio Costas (rastersoft@gmail.com)
* Based on code original (C) Carlos Soriano
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* exported getModifiersInDnD, getDesktopDir, getScriptsDir, getTemplatesDir, clamp,
spawnCommandLine, launchTerminal, getFilteredEnviron, distanceBetweenPoints, getExtraFolders,
getMounts, getFileExtensionOffset, getFilesFromNautilusDnD, writeTextFileToDesktop,
windowHidePagerTaskbarModal, waitDelayMs */
'use strict';
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Gdk = imports.gi.Gdk;
const Prefs = imports.preferences;
const Enums = imports.enums;
const Gettext = imports.gettext.domain('ding');
const _ = Gettext.gettext;
/**
*
* @param context
* @param modifiersToCheck
*/
function getModifiersInDnD(context, modifiersToCheck) {
let device = context.get_device();
let display = device.get_display();
let keymap = Gdk.Keymap.get_for_display(display);
let modifiers = keymap.get_modifier_state();
return (modifiers & modifiersToCheck) != 0;
}
/**
*
*/
function getDesktopDir() {
let desktopPath = GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_DESKTOP);
return Gio.File.new_for_commandline_arg(desktopPath);
}
/**
*
*/
function getScriptsDir() {
let scriptsDir = GLib.build_filenamev([GLib.get_home_dir(), Enums.NAUTILUS_SCRIPTS_DIR]);
return Gio.File.new_for_commandline_arg(scriptsDir);
}
/**
*
*/
function getTemplatesDir() {
let templatesDir = GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_TEMPLATES);
if ((templatesDir == GLib.get_home_dir()) || (templatesDir == null)) {
return null;
}
return Gio.File.new_for_commandline_arg(templatesDir);
}
/**
*
* @param value
* @param min
* @param max
*/
function clamp(value, min, max) {
return Math.max(Math.min(value, max), min);
}
/**
*
* @param commandLine
* @param environ
*/
function spawnCommandLine(commandLine, environ = null) {
try {
let [success, argv] = GLib.shell_parse_argv(commandLine);
trySpawn(null, argv, environ);
} catch (err) {
print(`${commandLine} failed with ${err}`);
}
}
/**
*
* @param workdir
* @param command
*/
function launchTerminal(workdir, command) {
let terminalSettings = new Gio.Settings({schema_id: Enums.TERMINAL_SCHEMA});
let exec = terminalSettings.get_string(Enums.EXEC_KEY);
let argv = [exec, `--working-directory=${workdir}`];
if (command) {
argv.push('-e');
argv.push(command);
}
trySpawn(workdir, argv, null);
}
/**
*
* @param workdir
* @param argv
* @param environ
*/
function trySpawn(workdir, argv, environ = null) {
/* The following code has been extracted from GNOME Shell's
* source code in Misc.Util.trySpawn function and modified to
* set the working directory.
*
* https://gitlab.gnome.org/GNOME/gnome-shell/blob/gnome-3-30/js/misc/util.js
*/
var success, pid;
try {
[success, pid] = GLib.spawn_async(workdir, argv, environ,
GLib.SpawnFlags.SEARCH_PATH | GLib.SpawnFlags.DO_NOT_REAP_CHILD,
null);
} catch (err) {
/* Rewrite the error in case of ENOENT */
if (err.matches(GLib.SpawnError, GLib.SpawnError.NOENT)) {
throw new GLib.SpawnError({
code: GLib.SpawnError.NOENT,
message: _('Command not found'),
});
} else if (err instanceof GLib.Error) {
// The exception from gjs contains an error string like:
// Error invoking GLib.spawn_command_line_async: Failed to
// execute child process "foo" (No such file or directory)
// We are only interested in the part in the parentheses. (And
// we can't pattern match the text, since it gets localized.)
let message = err.message.replace(/.*\((.+)\)/, '$1');
throw new err.constructor({
code: err.code,
message,
});
} else {
throw err;
}
}
// Dummy child watch; we don't want to double-fork internally
// because then we lose the parent-child relationship, which
// can break polkit. See https://bugzilla.redhat.com//show_bug.cgi?id=819275
GLib.child_watch_add(GLib.PRIORITY_DEFAULT, pid, () => {});
}
/**
*
*/
function getFilteredEnviron() {
let environ = [];
for (let env of GLib.get_environ()) {
/* It's a must to remove the WAYLAND_SOCKET environment variable
because, under Wayland, DING uses an specific socket to allow the
extension to detect its windows. But the scripts must run under
the normal socket */
if (env.startsWith('WAYLAND_SOCKET=')) {
continue;
}
environ.push(env);
}
return environ;
}
/**
*
* @param x
* @param y
* @param x2
* @param y2
*/
function distanceBetweenPoints(x, y, x2, y2) {
return Math.pow(x - x2, 2) + Math.pow(y - y2, 2);
}
/**
*
*/
function getExtraFolders() {
let extraFolders = [];
if (Prefs.desktopSettings.get_boolean('show-home')) {
extraFolders.push([Gio.File.new_for_commandline_arg(GLib.get_home_dir()), Enums.FileType.USER_DIRECTORY_HOME]);
}
if (Prefs.desktopSettings.get_boolean('show-trash')) {
extraFolders.push([Gio.File.new_for_uri('trash:///'), Enums.FileType.USER_DIRECTORY_TRASH]);
}
return extraFolders;
}
/**
*
* @param volumeMonitor
*/
function getMounts(volumeMonitor) {
let showVolumes = Prefs.desktopSettings.get_boolean('show-volumes');
let showNetwork = Prefs.desktopSettings.get_boolean('show-network-volumes');
try {
var mounts = volumeMonitor.get_mounts();
} catch (e) {
print(`Failed to get the list of mounts with ${e}`);
return [];
}
let result = [];
let uris = [];
for (let mount of mounts) {
try {
let isDrive = (mount.get_drive() != null) || (mount.get_volume() != null);
let uri = mount.get_default_location().get_uri();
if (((isDrive && showVolumes) || (!isDrive && showNetwork)) && !uris.includes(uri)) {
result.push([mount.get_default_location(), Enums.FileType.EXTERNAL_DRIVE, mount]);
uris.push(uri);
}
} catch (e) {
print(`Failed with ${e} while getting volume`);
}
}
return result;
}
/**
*
* @param filename
* @param opts
*/
function getFileExtensionOffset(filename, opts = {'isDirectory': false}) {
let offset = filename.length;
let extension = '';
if (!opts.isDirectory) {
const doubleExtensions = ['.gz', '.bz2', '.sit', '.Z', '.bz', '.xz'];
for (const item of doubleExtensions) {
if (filename.endsWith(item)) {
offset -= item.length;
extension = filename.substring(offset);
filename = filename.substring(0, offset);
break;
}
}
let lastDot = filename.lastIndexOf('.');
if (lastDot > 0) {
offset = lastDot;
extension = filename.substring(offset) + extension;
filename = filename.substring(0, offset);
}
}
return {offset, 'basename': filename, extension};
}
/**
*
* @param selection
* @param type
*/
function getFilesFromNautilusDnD(selection, type) {
let data = String.fromCharCode.apply(null, selection.get_data());
let retval = [];
let elements = data.split('\r\n');
for (let item of elements) {
if (item.length == 0) {
continue;
}
if (type == 1) {
// x-special/gnome-icon-list
let entry = item.split('\r');
retval.push(entry[0]);
} else {
// text/uri-list
if (item[0] == '#') {
continue;
}
retval.push(item);
}
}
return retval;
}
/**
*
* @param text
* @param filename
* @param dropCoordinates
*/
function writeTextFileToDesktop(text, filename, dropCoordinates) {
let path = GLib.build_filenamev([GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_DESKTOP), filename]);
let file = Gio.File.new_for_path(path);
const PERMISSIONS_MODE = 0o744;
if (GLib.mkdir_with_parents(file.get_parent().get_path(), PERMISSIONS_MODE) === 0) {
let [success, tag] = file.replace_contents(text, null, false, Gio.FileCreateFlags.REPLACE_DESTINATION, null);
}
if (dropCoordinates != null) {
let info = new Gio.FileInfo();
info.set_attribute_string('metadata::nautilus-drop-position', `${dropCoordinates[0]},${dropCoordinates[1]}`);
try {
file.set_attributes_from_info(info, Gio.FileQueryInfoFlags.NONE, null);
} catch (e) {}
}
}
/**
*
* @param window
* @param modal
*/
function windowHidePagerTaskbarModal(window, modal) {
let usingX11 = Gdk.Display.get_default().constructor.$gtype.name === 'GdkX11Display';
if (usingX11) {
window.set_type_hint(Gdk.WindowTypeHint.NORMAL);
window.set_skip_taskbar_hint(true);
window.set_skip_pager_hint(true);
} else {
let title = window.get_title();
if (title == null) {
title = '';
}
if (modal) {
title += ' ';
} else {
title += ' ';
}
window.set_title(title);
}
if (modal) {
window.connect('focus-out-event', () => {
window.set_keep_above(true);
window.stick();
window.grab_focus();
});
window.grab_focus();
}
}
/**
*
* @param ms
*/
function waitDelayMs(ms) {
return new Promise((resolve, reject) => {
GLib.timeout_add(GLib.PRIORITY_DEFAULT, ms, () => {
resolve();
return false;
});
});
}