369 lines
12 KiB
JavaScript
369 lines
12 KiB
JavaScript
/* DING: Desktop Icons New Generation for GNOME Shell
|
|
*
|
|
* Gtk4 Port Copyright (C) 2023 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 {Gdk, Gio, GLib, DesktopAppInfo} from '../dependencies/gi.js';
|
|
import {_} from '../dependencies/gettext.js';
|
|
|
|
export {GnomeShellDrag};
|
|
|
|
const GnomeShellDrag = class {
|
|
constructor(desktopManager) {
|
|
this._DBusUtils = desktopManager.DBusUtils;
|
|
|
|
if (!this._DBusUtils.RemoteExtensionControl.isAvailable)
|
|
return;
|
|
|
|
this._desktopManager = desktopManager;
|
|
this._dragManager = desktopManager.dragManager;
|
|
this._dragItem = this._dragManager.dragItem;
|
|
this._DesktopIconsUtil = desktopManager.DesktopIconsUtil;
|
|
this._Enums = desktopManager.Enums;
|
|
this._Prefs = desktopManager.Prefs;
|
|
this._selectedFiles = desktopManager.getCurrentSelection();
|
|
this._selectedFilesURI = desktopManager.getCurrentSelectionAsUri();
|
|
this._dockSpringOpenFile = null;
|
|
this._currentDesktopFileAppPath = null;
|
|
this._dockSpringOpenTime = GLib.get_monotonic_time();
|
|
this._dockSpringOpenComplete = false;
|
|
this._startMonitoringDockUriNavigation();
|
|
}
|
|
|
|
destroy() {
|
|
this._stopMonitoringDockUriNavigation();
|
|
}
|
|
|
|
_startMonitoringDockUriNavigation() {
|
|
// Careful, we have to and are calling an async function
|
|
// in the timer, which will always return true,
|
|
// therefore the function has to kill itself if
|
|
// not killed by drag end...
|
|
this._dockUriSpringTimerID = GLib.timeout_add(
|
|
GLib.PRIORITY_DEFAULT,
|
|
this._Enums.DND_SHELL_HOVER_POLL,
|
|
async () => {
|
|
try {
|
|
await this._dockUriSpringTimerFunction();
|
|
} catch (e) {
|
|
console.error(e);
|
|
}
|
|
return GLib.SOURCE_REMOVE;
|
|
}
|
|
);
|
|
}
|
|
|
|
async _lookOffLeftEdgeForDrop(shellDropCoordinates) {
|
|
let leftEdge = shellDropCoordinates;
|
|
|
|
// look upto 50 pixels away for a drop target,
|
|
// off the drag cursor to the left, increment 10
|
|
// 5 iterations
|
|
for (let i = 0; i <= 50; i += 10) {
|
|
leftEdge[0] -= i;
|
|
|
|
this._currentDesktopFileAppPath =
|
|
// eslint-disable-next-line no-await-in-loop
|
|
await this._DBusUtils.RemoteExtensionControl
|
|
.getDropTargetAppInfoDesktopFile(leftEdge)
|
|
.catch(e => console.error(e));
|
|
|
|
if (this._currentDesktopFileAppPath)
|
|
break;
|
|
}
|
|
|
|
this._setShellDropCursor();
|
|
}
|
|
|
|
async _dockUriSpringTimerFunction() {
|
|
// Failsafe kill the function - remove the timer if going on for
|
|
// too long, default 30 seconds
|
|
if (!this._dragItem ||
|
|
(GLib.get_monotonic_time() - this._dockSpringOpenTime) >
|
|
this._Enums.DND_SHELL_HOVER_POLL * 150000
|
|
) {
|
|
const stopID = this._dockUriSpringTimerID;
|
|
this._dockUriSpringTimerID = 0;
|
|
this._setShellDropCursor(this._Enums.ShellDropCursor.DEFAULT);
|
|
|
|
if (stopID)
|
|
GLib.Source.remove(stopID);
|
|
return GLib.SOURCE_REMOVE;
|
|
}
|
|
|
|
const shellDropCoordinates =
|
|
await this._DBusUtils.RemoteExtensionControl
|
|
.getDropTargetCoordinates()
|
|
.catch(e => console.error(e));
|
|
|
|
this._lookOffLeftEdgeForDrop(shellDropCoordinates);
|
|
|
|
if (!this._currentDesktopFileAppPath ||
|
|
!(this._currentDesktopFileAppPath.endsWith('Nautilus.desktop') ||
|
|
this._currentDesktopFileAppPath.startsWith('file://') ||
|
|
this._currentDesktopFileAppPath.startsWith('davs://'))
|
|
)
|
|
return GLib.SOURCE_CONTINUE;
|
|
|
|
|
|
// On a URI, start hover timing and reset timer
|
|
if (!this._dockSpringOpenFile) {
|
|
this._dockSpringOpenFile = this._currentDesktopFileAppPath;
|
|
this._dockSpringOpenTime = GLib.get_monotonic_time();
|
|
return GLib.SOURCE_CONTINUE;
|
|
}
|
|
|
|
// Open the URI, got here after hover timing started
|
|
if (this._dockSpringOpenFile === this._currentDesktopFileAppPath &&
|
|
!this._dockSpringOpenComplete &&
|
|
((GLib.get_monotonic_time() - this._dockSpringOpenTime) >
|
|
this._Enums.DND_HOVER_TIMEOUT * 1000)
|
|
) {
|
|
const context = Gdk.Display.get_default().get_app_launch_context();
|
|
context.set_timestamp(Gdk.CURRENT_TIME);
|
|
let uri;
|
|
|
|
try {
|
|
if (this._dockSpringOpenFile.endsWith('Nautilus.desktop'))
|
|
uri = this._desktopDir.get_uri();
|
|
else
|
|
uri = this._dockSpringOpenFile;
|
|
|
|
if (this._Prefs.openFolderOnDndHover)
|
|
Gio.AppInfo.launch_default_for_uri(uri, context);
|
|
|
|
this._dockSpringOpenComplete = true;
|
|
} catch (e) {
|
|
console.error(
|
|
e, `Error opening ${uri} in GNOME Files: ${e.message}`
|
|
);
|
|
}
|
|
return GLib.SOURCE_CONTINUE;
|
|
}
|
|
|
|
// URI is the same, window is opened, do nothing
|
|
if (this._dockSpringOpenFile === this._currentDesktopFileAppPath &&
|
|
this._dockSpringOpenComplete
|
|
)
|
|
return GLib.SOURCE_CONTINUE;
|
|
|
|
// If still alive, window is opened and uri is changed, reset
|
|
if (this._dockSpringOpenFile !== this._currentDesktopFileAppPath &&
|
|
this._dockSpringOpenComplete
|
|
) {
|
|
this._dockSpringOpenFile = null;
|
|
this._dockSpringOpenComplete = false;
|
|
|
|
return GLib.SOURCE_CONTINUE;
|
|
}
|
|
|
|
return GLib.SOURCE_REMOVE;
|
|
}
|
|
|
|
_stopMonitoringDockUriNavigation() {
|
|
if (this._dockUriSpringTimerID) {
|
|
GLib.Source.remove(this._dockUriSpringTimerID);
|
|
this._currentDesktopFileAppPath = null;
|
|
this._setShellDropCursor(this._Enums.ShellDropCursor.DEFAULT);
|
|
}
|
|
|
|
this._dockUriSpringTimerID = 0;
|
|
}
|
|
|
|
_setShellDropCursor(cursor = null) {
|
|
if (!this._DBusUtils.RemoteExtensionControl.isAvailable)
|
|
return;
|
|
|
|
if (cursor) {
|
|
this._DBusUtils.RemoteExtensionControl.setDragCursor(cursor);
|
|
|
|
return;
|
|
}
|
|
|
|
if (!this._currentDesktopFileAppPath) {
|
|
this._DBusUtils.RemoteExtensionControl
|
|
.setDragCursor(this._Enums.ShellDropCursor.NODROP);
|
|
|
|
return;
|
|
}
|
|
|
|
try {
|
|
if (this._currentDesktopFileAppPath.endsWith('.desktop')) {
|
|
const desktopFile =
|
|
DesktopAppInfo.new_from_filename(
|
|
GLib.build_filenamev(
|
|
[this._currentDesktopFileAppPath]
|
|
)
|
|
);
|
|
|
|
if (!desktopFile) {
|
|
console.log(
|
|
'Could not parse desktopFile as a desktop file,' +
|
|
' cannot set shell cursor'
|
|
);
|
|
|
|
this._DBusUtils.RemoteExtensionControl
|
|
.setDragCursor(this._Enums.ShellDropCursor.NODROP);
|
|
|
|
return;
|
|
}
|
|
|
|
let object =
|
|
this._DesktopIconsUtil
|
|
.checkAppOpensFileType(
|
|
desktopFile,
|
|
null,
|
|
this._selectedFiles[0].attributeContentType
|
|
);
|
|
|
|
if (object.canopenFile) {
|
|
this._DBusUtils.RemoteExtensionControl
|
|
.setDragCursor(this._Enums.ShellDropCursor.COPY);
|
|
|
|
return;
|
|
} else if (
|
|
this._currentDesktopFileAppPath
|
|
.endsWith('Nautilus.desktop') &&
|
|
this._Prefs.openFolderOnDndHover
|
|
) {
|
|
this._DBusUtils.RemoteExtensionControl
|
|
.setDragCursor(this._Enums.ShellDropCursor.MOVE);
|
|
|
|
return;
|
|
} else {
|
|
this._DBusUtils.RemoteExtensionControl
|
|
.setDragCursor(this._Enums.ShellDropCursor.NODROP);
|
|
}
|
|
} else if (
|
|
this._currentDesktopFileAppPath.startsWith('file://') ||
|
|
this._currentDesktopFileAppPath.startsWith('davs://') ||
|
|
this._currentDesktopFileAppPath.startsWith('trash://')
|
|
) {
|
|
this._DBusUtils.RemoteExtensionControl
|
|
.setDragCursor(this._Enums.ShellDropCursor.MOVE);
|
|
|
|
return;
|
|
} else {
|
|
this._DBusUtils.RemoteExtensionControl
|
|
.setDragCursor(this._Enums.ShellDropCursor.NODROP);
|
|
|
|
return;
|
|
}
|
|
} catch (e) {
|
|
console.error(e,
|
|
'Error reading desktop file. Cannot set shell Cursor'
|
|
);
|
|
}
|
|
|
|
this._DBusUtils.RemoteExtensionControl
|
|
.setDragCursor(this._Enums.ShellDropCursor.NODROP);
|
|
}
|
|
|
|
async completeGnomeShellDrop() {
|
|
if (!this._currentDesktopFileAppPath)
|
|
return false;
|
|
|
|
if (this._currentDesktopFileAppPath.endsWith('.desktop')) {
|
|
try {
|
|
const desktopFile =
|
|
DesktopAppInfo
|
|
.new_from_filename(
|
|
GLib.build_filenamev([this._currentDesktopFileAppPath])
|
|
);
|
|
|
|
if (!desktopFile) {
|
|
console.log('Could not parse desktopFile as desktop file');
|
|
|
|
return false;
|
|
}
|
|
|
|
const object =
|
|
this._DesktopIconsUtil
|
|
.checkAppOpensFileType(
|
|
desktopFile,
|
|
null,
|
|
this._selectedFiles[0].attributeContentType
|
|
);
|
|
|
|
if (object.canopenFile) {
|
|
const context =
|
|
Gdk.Display.get_default().get_app_launch_context();
|
|
|
|
context.set_timestamp(Gdk.CURRENT_TIME);
|
|
|
|
desktopFile.launch_uris_as_manager(
|
|
this._selectedFilesURI,
|
|
context,
|
|
GLib.SpawnFlags.SEARCH_PATH,
|
|
null,
|
|
null
|
|
);
|
|
|
|
return true;
|
|
} else {
|
|
this._showAppCannotOpenError(object.Appname);
|
|
|
|
return false;
|
|
}
|
|
} catch (e) {
|
|
console.error(e,
|
|
'Error reading desktop file. Cannot launch application.'
|
|
);
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (this._currentDesktopFileAppPath === 'trash:///') {
|
|
this._desktopManager.mainApp.activate_action('movetotrash', null);
|
|
|
|
return true;
|
|
}
|
|
|
|
if (this._currentDesktopFileAppPath.startsWith('file:///') ||
|
|
this._currentDesktopFileAppPath.startsWith('davs://')
|
|
) {
|
|
await this._desktopManager.copyOrMoveUris(
|
|
this._selectedFilesURI,
|
|
this._currentDesktopFileAppPath,
|
|
{},
|
|
{}
|
|
)
|
|
.catch(e => console.error(e));
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
_showAppCannotOpenError(Appname) {
|
|
const timeout = 3000; // In ms
|
|
|
|
this._desktopManager.showError(
|
|
_('Could not open File'),
|
|
_('$appName$ can not open files of this Type!')
|
|
.replace('$appName$', Appname),
|
|
null,
|
|
timeout
|
|
);
|
|
|
|
return false;
|
|
}
|
|
|
|
get _desktopDir() {
|
|
return this._desktopManager._desktopDir;
|
|
}
|
|
};
|