351 lines
10 KiB
JavaScript
351 lines
10 KiB
JavaScript
/* ADW-DING: Desktop Icons New Generation for GNOME Shell
|
|
*
|
|
* Adw/Gtk4 Port Copyright (C) 2025 Sundeep Mediratta (smedius@gmail.com)
|
|
*
|
|
* 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/>.
|
|
*/
|
|
|
|
import {gettext, Gio, GLib, Gtk} from '../../dependencies/gi.js';
|
|
import {_} from '../../dependencies/gettext.js';
|
|
import {Enums} from '../../dependencies/localFiles.js';
|
|
|
|
export {DesktopFolderUtils};
|
|
|
|
const DesktopFolderUtils = class {
|
|
constructor(window) {
|
|
this._activeWindow = window;
|
|
this.Enums = Enums;
|
|
this._desktopDir = this.getDesktopDir();
|
|
// Bind xdg-user-dirs translation domain
|
|
gettext.bindtextdomain('xdg-user-dirs', '/usr/share/locale');
|
|
}
|
|
|
|
_monitorDesktopDirChanges() {
|
|
this._xdgUserDirs = this._getXdgUserDirs();
|
|
|
|
this._monitorXdgUserDirs = this._xdgUserDirs.monitor_file(
|
|
Gio.FileMonitorFlags.WATCH_MOVES, null);
|
|
|
|
this._monitorXdgUserDirs.set_rate_limit(2000);
|
|
|
|
this._connectMonitor();
|
|
}
|
|
|
|
_connectMonitor() {
|
|
this._monitorID = this._monitorXdgUserDirs.connect(
|
|
'changed',
|
|
(obj, file, otherFile, event) => {
|
|
if (!(event === Gio.FileMonitorEvent.CHANGES_DONE_HINT ||
|
|
event === Gio.FileMonitorEvent.RENAMED))
|
|
return;
|
|
|
|
if (this._changingDesktopDirID)
|
|
GLib.source_remove(this._changingDesktopDirID);
|
|
|
|
this._changingDesktopDirID =
|
|
GLib.timeout_add(GLib.PRIORITY_LOW, 500, () => {
|
|
const newDesktopDir =
|
|
this.getDesktopDir();
|
|
this.onDesktopFolderChanged(newDesktopDir);
|
|
this._changingDesktopDirID = null;
|
|
return GLib.SOURCE_REMOVE;
|
|
});
|
|
}
|
|
);
|
|
}
|
|
|
|
_disconnectMonitor() {
|
|
if (this._monitorID)
|
|
this._monitorXdgUserDirs.disconnect(this._monitorID);
|
|
this._monitorID = 0;
|
|
}
|
|
|
|
_stopMonitoring() {
|
|
this._disconnectMonitor();
|
|
this._monitorXdgUserDirs?.cancel();
|
|
}
|
|
|
|
onDesktopFolderChanged(newDesktopDir) {
|
|
this._desktopDir = newDesktopDir;
|
|
}
|
|
|
|
changeDesktop() {
|
|
const dialog = new Gtk.FileDialog();
|
|
dialog.set_title(_('Choose Desktop Folder'));
|
|
dialog.set_accept_label(_('Choose'));
|
|
dialog.set_modal(true);
|
|
|
|
dialog.set_initial_folder(
|
|
Gio.File.new_for_commandline_arg(GLib.get_home_dir())
|
|
);
|
|
|
|
dialog.select_folder(
|
|
this.activeWindow,
|
|
null,
|
|
this._finishChooseDesktopFolder.bind(this)
|
|
);
|
|
}
|
|
|
|
restoreDefaultDesktop() {
|
|
const localizedDesktopName = this.getSystemLocalizedDesktopDir();
|
|
const defaultDesktop = GLib.build_filenamev([GLib.get_home_dir(),
|
|
localizedDesktopName]);
|
|
|
|
const desktopFolder = Gio.File.new_for_path(defaultDesktop);
|
|
|
|
try {
|
|
if (!desktopFolder.query_exists(null))
|
|
GLib.mkdir_with_parents(desktopFolder.get_path(), 0o755);
|
|
} catch (e) {
|
|
console.error(
|
|
`Unable to create Folder ${defaultDesktop}: ${e.message}`
|
|
);
|
|
}
|
|
|
|
this._setDesktopFolder(desktopFolder);
|
|
}
|
|
|
|
getDesktopDir() {
|
|
const glibDesktopPath =
|
|
GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_DESKTOP);
|
|
|
|
let xdgDesktopPath = null;
|
|
|
|
try {
|
|
const userDirsGioFile = this._getXdgUserDirs();
|
|
|
|
if (!userDirsGioFile.query_exists(null)) {
|
|
throw new Error(
|
|
'User configuration file user-dirs.users does not exist'
|
|
);
|
|
}
|
|
|
|
const decoder = new TextDecoder();
|
|
|
|
const contents =
|
|
decoder
|
|
.decode(GLib.file_get_contents(userDirsGioFile.get_path())[1])
|
|
.trim();
|
|
|
|
if (contents)
|
|
xdgDesktopPath = this._parseUserDirsFile(contents);
|
|
} catch (e) {
|
|
console.error(e, `XDG Desktop not set, ${e}`);
|
|
}
|
|
|
|
const desktopPath = xdgDesktopPath ? xdgDesktopPath : glibDesktopPath;
|
|
|
|
return Gio.File.new_for_commandline_arg(desktopPath);
|
|
}
|
|
|
|
getSystemLocalizedDesktopDir() {
|
|
const systemDesktopDirName =
|
|
this._getSystemDesktopDir() ?? this.Enums.DEFAULT_DESKTOP_NAME;
|
|
const localizedDesktopName =
|
|
gettext.dgettext('xdg-user-dirs', systemDesktopDirName);
|
|
|
|
return localizedDesktopName ?? systemDesktopDirName;
|
|
}
|
|
|
|
_getSystemDesktopDir() {
|
|
const systemDirsGioFile = this._getXdgSystemDirs();
|
|
|
|
if (!systemDirsGioFile) {
|
|
console.error('No system xdg user-dirs.default file');
|
|
return null;
|
|
}
|
|
|
|
let xdgSystemDesktopPath = null;
|
|
const decoder = new TextDecoder();
|
|
|
|
try {
|
|
const contents =
|
|
decoder
|
|
.decode(GLib.file_get_contents(systemDirsGioFile.get_path())[1])
|
|
.trim();
|
|
|
|
if (contents) {
|
|
const parseSystemconfig = true;
|
|
|
|
xdgSystemDesktopPath =
|
|
this._parseUserDirsFile(contents, parseSystemconfig);
|
|
}
|
|
} catch (e) {
|
|
console.error(e, `XDG Desktop not set in user-dirs.default, ${e}`);
|
|
}
|
|
|
|
return xdgSystemDesktopPath;
|
|
}
|
|
|
|
|
|
async _finishChooseDesktopFolder(dialog, asyncResult) {
|
|
let newFolder = null;
|
|
|
|
try {
|
|
newFolder = dialog.select_folder_finish(asyncResult);
|
|
} catch (e) {
|
|
if (e.matches(Gtk.DialogError, Gtk.DialogError.CANCELLED) ||
|
|
e.matches(Gtk.DialogError, Gtk.DialogError.DISMISSED))
|
|
return;
|
|
console.error(e, `Error selecting folder: ${e.message}`);
|
|
}
|
|
|
|
if (!newFolder)
|
|
return;
|
|
|
|
await this._setDesktopFolder(newFolder);
|
|
}
|
|
|
|
async _setDesktopFolder(newFolder) {
|
|
const isFolder =
|
|
newFolder.query_file_type(
|
|
Gio.FileQueryInfoFlags.NOFOLLOW_SYMLINKS,
|
|
null
|
|
) === Gio.FileType.DIRECTORY;
|
|
|
|
if (!isFolder)
|
|
return;
|
|
|
|
if (newFolder.get_path() === this._desktopDir.get_path())
|
|
return;
|
|
|
|
await this._writeXdgUserDirsDesktopFile(newFolder.get_path());
|
|
}
|
|
|
|
_isDefaultDesktopFolder() {
|
|
const localizedDesktopName = this.getSystemLocalizedDesktopDir();
|
|
const defaultDesktop = GLib.build_filenamev([GLib.get_home_dir(),
|
|
localizedDesktopName]);
|
|
|
|
return this._desktopDir.get_path() === defaultDesktop;
|
|
}
|
|
|
|
_parseUserDirsFile(content, systemconfig = null) {
|
|
if (!content)
|
|
return null;
|
|
|
|
const serarchstring = systemconfig ? 'DESKTOP=' : 'XDG_DESKTOP_DIR=';
|
|
|
|
const lineArray = content.trim().split('\n');
|
|
|
|
const desktopline =
|
|
lineArray.filter(l => l.startsWith(serarchstring))[0];
|
|
|
|
let xdgDesktopPath = desktopline.split('=')[1].trim();
|
|
xdgDesktopPath = xdgDesktopPath.replace(/^"|"$/g, '');
|
|
xdgDesktopPath = xdgDesktopPath.replace('$HOME', GLib.get_home_dir());
|
|
|
|
return xdgDesktopPath;
|
|
}
|
|
|
|
async _writeXdgUserDirsDesktopFile(path) {
|
|
const userDirsGioFile = this._getXdgUserDirs();
|
|
|
|
if (path.startsWith(GLib.get_home_dir()))
|
|
path = path.replace(GLib.get_home_dir(), '$HOME');
|
|
|
|
const newline = `XDG_DESKTOP_DIR="${path}"`;
|
|
|
|
try {
|
|
const decoder = new TextDecoder();
|
|
|
|
const contents =
|
|
decoder
|
|
.decode(GLib.file_get_contents(userDirsGioFile.get_path())[1])
|
|
.trim();
|
|
|
|
const lineArray = contents.split('\n');
|
|
|
|
const newArray = lineArray.map(l => {
|
|
if (l.startsWith('XDG_DESKTOP_DIR='))
|
|
return newline;
|
|
return l;
|
|
});
|
|
|
|
const newContents = newArray.join('\n');
|
|
await this._replaceFileContentsAsync(userDirsGioFile, newContents);
|
|
} catch (e) {
|
|
console.error(e, `Failed to write XDG Desktop file with ${e}`);
|
|
}
|
|
}
|
|
|
|
async _replaceFileContentsAsync(destinationFile, contents) {
|
|
const textCoder = new TextEncoder();
|
|
const byteArray = new GLib.Bytes(textCoder.encode(contents));
|
|
|
|
try {
|
|
await destinationFile.replace_contents_async(
|
|
byteArray,
|
|
null,
|
|
false,
|
|
Gio.FileCreateFlags.REPLACE_DESTINATION,
|
|
null
|
|
);
|
|
} catch (e) {
|
|
if (e.matches(
|
|
Gio.IOErrorEnum,
|
|
Gio.IOErrorEnum.NOT_EMPTY
|
|
)) {
|
|
GLib.mkdir_with_parents(
|
|
GLib.path_get_dirname(destinationFile.get_path()),
|
|
0o700
|
|
);
|
|
|
|
this._replaceFileContentsAsync(destinationFile, contents);
|
|
return;
|
|
}
|
|
|
|
console.error(e, `Failed to write XDG Desktop file with ${e}`);
|
|
}
|
|
}
|
|
|
|
_getXdgUserDirs() {
|
|
const xdgUserDirspath =
|
|
Gio.File.new_build_filenamev(
|
|
[GLib.get_user_config_dir(), this.Enums.XDG_USER_DIRS]
|
|
);
|
|
|
|
return xdgUserDirspath;
|
|
}
|
|
|
|
_getXdgSystemDirs() {
|
|
const xdgSystemDirsArray = GLib.get_system_config_dirs();
|
|
|
|
for (let dir of xdgSystemDirsArray) {
|
|
const xdgSystemdir = Gio.File.new_build_filenamev(
|
|
[dir, this.Enums.XDG_SYSTEM_DIRS]
|
|
);
|
|
|
|
if (xdgSystemdir.query_exists(null))
|
|
return xdgSystemdir;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
get activeWindow() {
|
|
if (this._activeWindow)
|
|
return this._activeWindow;
|
|
|
|
return Gio.Application.get_default().get_active_window();
|
|
}
|
|
|
|
set activeWindow(window) {
|
|
this._activeWindow = window;
|
|
}
|
|
|
|
get isDefaultDesktop() {
|
|
return this._isDefaultDesktopFolder();
|
|
}
|
|
};
|