Skip to content
Commits on Source (79)
......@@ -5,17 +5,13 @@
Desktop Icons NG for GNOME Shell. It is a fork/rewrite of the official 'Desktop Icons' extension,
with these advantages:
* Drag'n'Drop, both inside the desktop, between desktop and applications, and nautilus windows
* Allows to use "Open with..." option with several files
* When hovering or clicking on an icon with a name too large to fit, it shows the full name
* Doesn't hang the compositor when there is too much activity in the desktop folder
* Drag'n'Drop, both inside the desktop, between desktop and applications, and nautilus windows
* Allows to use "Open with..." option with several files
* When hovering or clicking on an icon with a name too large to fit, it shows the full name
* Doesn't hang the compositor when there is too much activity in the desktop folder
But it is still an alpha development, so it probably still have a lot of bugs. Use with care.
## Current version
Version 0.15.0
## Requirements
* GNOME Shell >= 3.38
......@@ -33,8 +29,8 @@ The code is divided in two parts: a classic Gtk program that manages the whole d
desktopIconsUtil.js, desktopManager.js, enums.js, fileItem.js and preferences.js), and a little
extension (extension.js) that have these roles:
* Launch the desktop program at startup and relaunch it if it dies
* Identify the desktop windows and keep it at the bottom of the windows stack, in all desktops
* Launch the desktop program at startup and relaunch it if it dies
* Identify the desktop windows and keep it at the bottom of the windows stack, in all desktops
This last part is paramount in Wayland systems, because there an application can't set its role
as freely as in X11.
......@@ -79,23 +75,21 @@ It accepts the following command line parameters:
files must be in the current path.
* -D: specifies a monitor. It is followed by another parameter in the form: X:Y:W:H:Z being each letter
a number with, respectively:
* X: the X coordinate of this monitor
* Y: the Y coordinate of this monitor
* W: the width in pixels of this monitor
* H: the height in pixels of this monitor
* Z: the zoom value for this monitor
* X: the X coordinate of this monitor
* Y: the Y coordinate of this monitor
* W: the width in pixels of this monitor
* H: the height in pixels of this monitor
* Z: the zoom value for this monitor
you can set several -D parameters in the same command line, one for each monitor. A single window
will be created for each monitor. If no -D parameter is specified, it will create a single monitor
with a size of 1280x720 pixels.
* -M: specifies which monitor is the primary index, to add there any new file icon.
## Manual installation
The easiest way of installing DING is to run the `local_install.sh` script. It performs the build steps
specified in the next section.
## Build with Meson
The project uses a build system called [Meson](https://mesonbuild.com/). You can install
......@@ -110,6 +104,7 @@ project and install it:
meson --prefix=$HOME/.local/ --localedir=share/gnome-shell/extensions/ding@rastersoft.com/locale .build
ninja -C .build install
```
It is strongly recommended to delete the destination folder
($HOME/.local/share/gnome-shell/extensions/ding@rastersoft.com) before doing this, to ensure that no old
data is kept.
......
/* 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/>.
*/
const Gtk = imports.gi.Gtk;
const Pango = imports.gi.Pango;
const Gettext = imports.gettext.domain('ding');
const _ = Gettext.gettext;
var AskConfirmPopup = class {
constructor(text, secondaryText, parentWindow) {
this._window = new Gtk.MessageDialog({window_position: Gtk.WindowPosition.CENTER_ON_PARENT,
transient_for: parentWindow,
message_type: Gtk.MessageType.WARNING,
buttons: Gtk.ButtonsType.NONE,
text: text,
secondary_text: secondaryText});
this._window.add_button(_("Cancel"), Gtk.ResponseType.CANCEL);
let deleteButton = this._window.add_button(_("Delete"), Gtk.ResponseType.OK);
deleteButton.get_style_context().add_class("destructive-action");
}
run() {
this._window.show_all();
let retval = this._window.run();
this._window.hide();
if (retval == Gtk.ResponseType.OK) {
return true;
} else {
return false;
}
}
};
......@@ -19,6 +19,7 @@
const Gtk = imports.gi.Gtk;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const DesktopIconsUtil = imports.desktopIconsUtil;
const Gettext = imports.gettext.domain('ding');
const _ = Gettext.gettext;
......@@ -36,6 +37,7 @@ var AskNamePopup = class {
this._window.add_button(_("Cancel"), Gtk.ResponseType.CANCEL);
this._window.set_modal(true);
this._window.set_title(title);
DesktopIconsUtil.windowHidePagerTaskbarModal(this._window, true);
let contentArea = this._window.get_content_area();
this._textArea = new Gtk.Entry();
if (filename) {
......
......@@ -31,7 +31,7 @@ var AskRenamePopup = class {
this._desktopPath = GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_DESKTOP);
this._fileItem = fileItem;
this._popover = new Gtk.Popover({relative_to: fileItem.actor,
this._popover = new Gtk.Popover({relative_to: fileItem.container,
modal: true});
let contentBox = new Gtk.Grid({row_spacing: 6,
column_spacing: 6,
......@@ -78,8 +78,9 @@ var AskRenamePopup = class {
}
_do_rename() {
DBusUtils.NautilusFileOperationsProxy.RenameFileRemote(this._fileItem.file.get_uri(),
this._textArea.text,
DBusUtils.NautilusFileOperations2Proxy.RenameURIRemote(
this._fileItem.file.get_uri(), this._textArea.text,
DBusUtils.NautilusFileOperations2Proxy.platformData(),
(result, error) => {
if (error)
throw new Error('Error renaming file: ' + error.message);
......
......@@ -18,7 +18,7 @@
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
var NautilusFileOperationsProxy;
var NautilusFileOperations2Proxy;
var FreeDesktopFileManagerProxy;
var GnomeNautilusPreviewProxy;
var SwitcherooControlProxyClass;
......@@ -27,37 +27,51 @@ var discreteGpuAvailable;
var GnomeArchiveManagerProxy;
var GtkVfsMetadataProxy;
const NautilusFileOperationsInterface = `<node>
<interface name='org.gnome.Nautilus.FileOperations'>
const NautilusFileOperations2Interface = `<node>
<interface name='org.gnome.Nautilus.FileOperations2'>
<method name='CopyURIs'>
<arg name='URIs' type='as' direction='in'/>
<arg name='Destination' type='s' direction='in'/>
<arg type='as' name='sources' direction='in'/>
<arg type='s' name='destination' direction='in'/>
<arg type='a{sv}' name='platform_data' direction='in'/>
</method>
<method name='MoveURIs'>
<arg name='URIs' type='as' direction='in'/>
<arg name='Destination' type='s' direction='in'/>
<arg type='as' name='sources' direction='in'/>
<arg type='s' name='destination' direction='in'/>
<arg type='a{sv}' name='platform_data' direction='in'/>
</method>
<method name='EmptyTrash'>
<arg type="b" name="ask_confirmation" direction='in'/>
<arg type='a{sv}' name='platform_data' direction='in'/>
</method>
<method name='TrashFiles'>
<arg name='URIs' type='as' direction='in'/>
<method name='TrashURIs'>
<arg type='as' name='uris' direction='in'/>
<arg type='a{sv}' name='platform_data' direction='in'/>
</method>
<method name='DeleteURIs'>
<arg type='as' name='uris' direction='in'/>
<arg type='a{sv}' name='platform_data' direction='in'/>
</method>
<method name='CreateFolder'>
<arg name='URI' type='s' direction='in'/>
<arg type='s' name='parent_uri' direction='in'/>
<arg type='s' name='new_folder_name' direction='in'/>
<arg type='a{sv}' name='platform_data' direction='in'/>
</method>
<method name='RenameFile'>
<arg name='URI' type='s' direction='in'/>
<arg name='NewName' type='s' direction='in'/>
<method name='RenameURI'>
<arg type='s' name='uri' direction='in'/>
<arg type='s' name='new_name' direction='in'/>
<arg type='a{sv}' name='platform_data' direction='in'/>
</method>
<method name='Undo'>
<arg type='a{sv}' name='platform_data' direction='in'/>
</method>
<method name='Redo'>
<arg type='a{sv}' name='platform_data' direction='in'/>
</method>
<property name='UndoStatus' type='i' access='read'/>
<property name="UndoStatus" type="i" access="read"/>
</interface>
</node>`;
const NautilusFileOperationsProxyInterface = Gio.DBusProxy.makeProxyWrapper(NautilusFileOperationsInterface);
const NautilusFileOperations2ProxyInterface = Gio.DBusProxy.makeProxyWrapper(NautilusFileOperations2Interface);
const FreeDesktopFileManagerInterface = `<node>
<interface name='org.freedesktop.FileManager1'>
......@@ -241,10 +255,10 @@ const GtkVfsMetadataInterface = `<node>
const GtkVfsMetadataProxyInterface = Gio.DBusProxy.makeProxyWrapper(GtkVfsMetadataInterface);
function init() {
NautilusFileOperationsProxy = new NautilusFileOperationsProxyInterface(
NautilusFileOperations2Proxy = new NautilusFileOperations2ProxyInterface(
Gio.DBus.session,
'org.gnome.Nautilus',
'/org/gnome/Nautilus',
'/org/gnome/Nautilus/FileOperations2',
(proxy, error) => {
if (error) {
log('Error connecting to Nautilus');
......@@ -252,6 +266,47 @@ function init() {
}
);
NautilusFileOperations2Proxy.platformData = params => {
const inShell = typeof global !== 'undefined';
const defaultParams = {
timestamp: inShell ? global.get_current_time() :
imports.gi.Gtk.get_current_event_time(),
parentWindow: inShell ? null :
imports.gi.Gtk.get_current_event().get_window(),
windowPosition: 'center',
};
const { parentWindow, timestamp, windowPosition } = {
...defaultParams,
...params,
};
let { parentHandle } = params ?? { parentHandle: ''};
if (!parentHandle && parentWindow) {
try {
imports.gi.versions.GdkX11 = '3.0';
const { GdkX11 } = imports.gi;
const topLevel = parentWindow.get_effective_toplevel();
if (topLevel.constructor.$gtype === GdkX11.X11Window.$gtype) {
const xid = GdkX11.X11Window.prototype.get_xid.call(topLevel);
parentHandle = `x11:${xid}`;
} /* else if (topLevel instanceof GdkWayland.Toplevel) {
FIXME: Need Gtk4 to use GdkWayland
const handle = GdkWayland.Toplevel.prototype.export_handle.call(topLevel);
parentHandle = `wayland:${handle}`;
} */
} catch (e) {
logError(e, 'Impossible to determine the parent window');
}
}
return {
'parent-handle': new GLib.Variant('s', parentHandle),
'timestamp': new GLib.Variant('u', timestamp),
'window-position': new GLib.Variant('s', windowPosition),
};
}
FreeDesktopFileManagerProxy = new FreeDesktopFileManagerProxyInterface(
Gio.DBus.session,
'org.freedesktop.FileManager1',
......
gnome-shell-extension-desktop-icons-ng (20-0ubuntu1) impish; urgency=medium
* New upstream release
* Modified d/watch and Standards-Version bumped
* Dropped d/p/upstream-commits-until-20210712.patch
* debian/control*:
- Dropped clutter dependencies, not needed any longer
-- Gunnar Hjalmarsson <gunnarhj@ubuntu.com> Thu, 19 Aug 2021 20:35:56 +0200
gnome-shell-extension-desktop-icons-ng (0.18.0-1) unstable; urgency=medium
* Initial release (closes: #987291)
-- Gunnar Hjalmarsson <gunnarhj@debian.org> Wed, 18 Aug 2021 12:09:42 +0200
gnome-shell-extension-desktop-icons-ng (0.18.0-0ubuntu3) impish; urgency=medium
* debian/control*: Further dependencies imposed by upstream
......
......@@ -7,12 +7,12 @@ Section: gnome
Priority: optional
Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
XSBC-Original-Maintainer: Debian GNOME Maintainers <pkg-gnome-maintainers@lists.alioth.debian.org>
Uploaders:
Uploaders: Gunnar Hjalmarsson <gunnarhj@debian.org>
Build-Depends: debhelper-compat (= 13),
dh-sequence-gnome,
libglib2.0-bin,
meson
Standards-Version: 4.5.1
Standards-Version: 4.6.0
Homepage: https://gitlab.com/rastersoft/desktop-icons-ng
Vcs-Git: https://salsa.debian.org/gnome-team/shell-extensions/gnome-shell-extension-desktop-icons-ng.git
Vcs-Browser: https://salsa.debian.org/gnome-team/shell-extensions/gnome-shell-extension-desktop-icons-ng
......@@ -22,9 +22,6 @@ Package: gnome-shell-extension-desktop-icons-ng
Architecture: all
Depends: ${misc:Depends},
gnome-shell (>= 3.38),
gir1.2-clutter-1.0,
gir1.2-clutter-gst-3.0,
gir1.2-gtkclutter-1.0,
gjs,
nautilus (>= 3.38),
xdg-desktop-portal
......
......@@ -8,7 +8,7 @@ Build-Depends: debhelper-compat (= 13),
dh-sequence-gnome,
libglib2.0-bin,
meson
Standards-Version: 4.5.1
Standards-Version: 4.6.0
Homepage: https://gitlab.com/rastersoft/desktop-icons-ng
Vcs-Git: https://salsa.debian.org/gnome-team/shell-extensions/gnome-shell-extension-desktop-icons-ng.git
Vcs-Browser: https://salsa.debian.org/gnome-team/shell-extensions/gnome-shell-extension-desktop-icons-ng
......@@ -18,9 +18,6 @@ Package: gnome-shell-extension-desktop-icons-ng
Architecture: all
Depends: ${misc:Depends},
gnome-shell (>= 3.38),
gir1.2-clutter-1.0,
gir1.2-clutter-gst-3.0,
gir1.2-gtkclutter-1.0,
gjs,
nautilus (>= 3.38),
xdg-desktop-portal
......
translations_not_source.patch
upstream-commits-until-20210712.patch
This diff is collapsed.
......@@ -2,4 +2,4 @@ version=4
opts=filenamemangle=s/.*\/archive\/@ANY_VERSION@\/(desktop-icons-ng-@ANY_VERSION@@ARCHIVE_EXT@)/$2/g,\
uversionmangle=s/(rc\d*|b\d*)/~$1/ \
https://gitlab.com/rastersoft/desktop-icons-ng/tags?sort=updated_desc \
.*/archive/(\d\.\S+)/.*\.tar\.gz
.*/archive/(\d+)/.*\.tar\.gz
......@@ -87,6 +87,9 @@ var DesktopGrid = class {
}
});
const scale = this._window.get_scale_factor();
this.gridGlobalRectangle = new Gdk.Rectangle({'x':this._x, 'y':this._y, 'width':(this._width*scale), 'height':(this._height*scale)});
this._eventBox = new Gtk.EventBox({ visible: true });
this._window.add(this._eventBox);
this._container = new Gtk.Fixed();
......@@ -217,18 +220,12 @@ var DesktopGrid = class {
}
_doDrawRubberBand(cr) {
if (this._desktopManager.rubberBand) {
let minX = Math.min(this._desktopManager.rubberBandInitX, this._desktopManager.mouseX);
let maxX = Math.max(this._desktopManager.rubberBandInitX, this._desktopManager.mouseX);
let minY = Math.min(this._desktopManager.rubberBandInitY, this._desktopManager.mouseY);
let maxY = Math.max(this._desktopManager.rubberBandInitY, this._desktopManager.mouseY);
if ((minX >= (this._x + this._width )) || (minY >= (this._y + this._height)) || (maxX < this._x) || (maxY < this._y)) {
if (this._desktopManager.rubberBand && this._desktopManager.selectionRectangle) {
if (! this.gridGlobalRectangle.intersect(this._desktopManager.selectionRectangle)[0]) {
return;
}
let [xInit, yInit] = this._coordinatesGlobalToLocal(minX, minY);
let [xFin, yFin] = this._coordinatesGlobalToLocal(maxX, maxY);
let [xInit, yInit] = this._coordinatesGlobalToLocal(this._desktopManager.x1, this._desktopManager.y1);
let [xFin, yFin] = this._coordinatesGlobalToLocal(this._desktopManager.x2, this._desktopManager.y2);
cr.rectangle(xInit + 0.5, yInit + 0.5, xFin - xInit, yFin - yInit);
Gdk.cairo_set_source_rgba(cr, new Gdk.RGBA({red: this._desktopManager.selectColor.red,
......@@ -306,7 +303,7 @@ var DesktopGrid = class {
let localX = Math.floor(this._width * column / this._maxColumns);
let localY = Math.floor(this._height * row / this._maxRows);
this._container.put(fileItem.actor, localX + elementSpacing, localY + elementSpacing);
this._container.put(fileItem.container, localX + elementSpacing, localY + elementSpacing);
this._setGridUse(column, row, true);
this._fileItems[fileItem.uri] = [column, row, fileItem];
let [x, y] = this._coordinatesLocalToGlobal(localX + elementSpacing, localY + elementSpacing);
......@@ -332,7 +329,7 @@ var DesktopGrid = class {
if (fileItem.uri in this._fileItems) {
let [column, row, tmp] = this._fileItems[fileItem.uri];
this._setGridUse(column, row, false);
this._container.remove(fileItem.actor);
this._container.remove(fileItem.container);
delete this._fileItems[fileItem.uri];
}
}
......
......@@ -19,6 +19,7 @@
const Gtk = imports.gi.Gtk;
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');
......@@ -127,9 +128,9 @@ function getMounts(volumeMonitor) {
for (let mount of mounts) {
try {
let is_drive = (mount.get_drive() != null) || (mount.get_volume() != null);
let uri = mount.get_root().get_uri();
let uri = mount.get_default_location().get_uri();
if (((is_drive && show_volumes) || (!is_drive && show_network)) && (!(uris.includes(uri)))) {
result.push([mount.get_root(), Enums.FileType.EXTERNAL_DRIVE, mount]);
result.push([mount.get_default_location(), Enums.FileType.EXTERNAL_DRIVE, mount]);
uris.push(uri);
}
} catch(e) {
......@@ -244,3 +245,28 @@ function writeTextFileToDesktop(text, filename, dropCoordinates) {
} catch(e) {}
}
}
function windowHidePagerTaskbarModal(window, modal) {
let using_X11 = Gdk.Display.get_default().constructor.$gtype.name === 'GdkX11Display';
if (using_X11) {
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 (modal) {
title = title + '@!HTD';
} else {
title = title + '@!H';
}
window.set_title(title);
}
if (modal) {
window.connect('focus-out-event', () => {
window.set_keep_above(true);
window.stick();
window.grab_focus();
});
window.grab_focus();
}
}
This diff is collapsed.
......@@ -47,7 +47,7 @@ class ManageWindow {
}
}));
this._signalIDs.push(window.connect('position-changed', () => {
if ((this._x !== null) && (this._y !== null)) {
if (this._fixed && (this._x !== null) && (this._y !== null)) {
this._window.move_frame(false, this._x, this._y);
}
}));
......@@ -80,6 +80,7 @@ class ManageWindow {
this._keepAtTop = false;
this._showInAllDesktops = false;
this._hideFromWindowList = false;
this._fixed = false;
let title = this._window.get_title();
if (title != null) {
let pos = title.search("@!");
......@@ -98,7 +99,7 @@ class ManageWindow {
print(`Exception ${e.message}`);
}
try {
let extra_chars = title.substring(pos2).trim().toUpperCase();
let extra_chars = title.substring(pos+2).trim().toUpperCase();
for (let char of extra_chars) {
switch (char) {
case 'B':
......@@ -115,6 +116,9 @@ class ManageWindow {
case 'H':
this._hideFromWindowList = true;
break;
case 'F':
this._fixed = true;
break;
}
}
} catch(e) {
......@@ -138,7 +142,7 @@ class ManageWindow {
if (this._keepAtBottom) {
this._window.lower();
}
if ((this._x !== null) && (this._y !== null)) {
if (this._fixed && (this._x !== null) && (this._y !== null)) {
this._window.move_frame(false, this._x, this._y);
}
}
......
......@@ -18,7 +18,7 @@
var ICON_SIZE = { 'tiny': 36, 'small': 48, 'standard': 64, 'large': 96 };
var ICON_WIDTH = { 'tiny': 70, 'small': 90, 'standard': 120, 'large': 130 };
var ICON_HEIGHT = { 'tiny': 70, 'small': 90, 'standard': 106, 'large': 138 };
var ICON_HEIGHT = { 'tiny': 80, 'small': 90, 'standard': 106, 'large': 138 };
var START_CORNER = { 'top-left': [false, false],
'top-right': [true, false],
......@@ -68,6 +68,15 @@ var WhatToDoWithExecutable = {
CANCEL: 3
};
var SortOrder = {
ORDER: 'arrangeorder',
NAME: 'name',
DESCENDINGNAME: 'descendingname',
MODIFIEDTIME: 'modifiedtime',
KIND: 'kind',
SIZE: 'size'
};
var DEFAULT_ATTRIBUTES = 'metadata::*,standard::*,access::*,time::modified,unix::mode';
var TERMINAL_SCHEMA = 'org.gnome.desktop.default-applications.terminal';
var SCHEMA_NAUTILUS = 'org.gnome.nautilus.preferences';
......
......@@ -38,8 +38,22 @@ function init() {
data.launchDesktopId = 0;
data.currentProcess = null;
data.reloadTime = 100;
data.x11Manager = new EmulateX11.EmulateX11WindowType();
// Ensure that there aren't "rogue" processes
/* The constructor of the EmulateX11 class only initializes some
* internal properties, but nothing else. In fact, it has its own
* enable() and disable() methods. That's why it could have been
* created here, in init(). But since the rule seems to be NO CLASS
* CREATION IN INIT UNDER NO CIRCUMSTANCES...
*/
data.x11Manager = null;
/* Ensures that there aren't "rogue" processes.
* This is a safeguard measure for the case of Gnome Shell being
* relaunched (for example, under X11, with Alt+F2 and R), to kill
* any old DING instance. That's why it must be here, in init(),
* and not in enable() or disable() (disable already guarantees that
* the current instance is killed).
*/
doKillAllOldDesktopProcesses();
}
......@@ -48,6 +62,9 @@ function init() {
* Enables the extension
*/
function enable() {
if (!data.x11Manager) {
data.x11Manager = new EmulateX11.EmulateX11WindowType();
}
// If the desktop is still starting up, we wait until it is ready
if (Main.layoutManager._startingUp) {
data.startupPreparedId = Main.layoutManager.connect('startup-complete', () => { innerEnable(true); });
......
This diff is collapsed.
......@@ -5,3 +5,4 @@ rm -rf .build
mkdir .build
meson --prefix=$HOME/.local/ --localedir=share/gnome-shell/extensions/ding@rastersoft.com/locale .build
ninja -C .build install
rm -rf .build
project('ding',
version: '0.18.0',
version: '0.20.0',
license: 'GPL3'
)
......@@ -14,7 +14,6 @@ extensions_dir = join_paths(prefix, 'share', 'gnome-shell', 'extensions', 'ding@
install_data([
# 'createFolderDialog.js',
'askConfirmPopup.js',
'askNamePopup.js',
'askRenamePopup.js',
'createThumbnail.js',
......