Files
system-taskbar/ding/app/gnomeShellDragDrop.js

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;
}
};