123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938 |
- /* Desktop Icons GNOME Shell extension
- *
- * Copyright (C) 2017 Carlos Soriano <csoriano@redhat.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, either version 3 of the License, or
- * (at your option) any later version.
- *
- * 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 Clutter = imports.gi.Clutter;
- const Gio = imports.gi.Gio;
- const GLib = imports.gi.GLib;
- const GObject = imports.gi.GObject;
- const St = imports.gi.St;
- const Pango = imports.gi.Pango;
- const Meta = imports.gi.Meta;
- const GdkPixbuf = imports.gi.GdkPixbuf;
- const Cogl = imports.gi.Cogl;
- const GnomeDesktop = imports.gi.GnomeDesktop;
- const Mainloop = imports.mainloop;
- const Config = imports.misc.config;
- const Background = imports.ui.background;
- const Main = imports.ui.main;
- const PopupMenu = imports.ui.popupMenu;
- const Util = imports.misc.util;
- const ExtensionUtils = imports.misc.extensionUtils;
- const Me = ExtensionUtils.getCurrentExtension();
- const Extension = Me.imports.extension;
- const Prefs = Me.imports.prefs;
- const DBusUtils = Me.imports.dbusUtils;
- const DesktopIconsUtil = Me.imports.desktopIconsUtil;
- const Gettext = imports.gettext.domain('desktop-icons');
- const _ = Gettext.gettext;
- const DRAG_TRESHOLD = 8;
- var S_IXUSR = 0o00100;
- var S_IWOTH = 0o00002;
- var State = {
- NORMAL: 0,
- GONE: 1,
- };
- var FileItem = GObject.registerClass({
- GTypeName: 'DesktopIcons_FileItem',
- Signals: {
- 'rename-clicked': {},
- 'selected': {
- param_types: [GObject.TYPE_BOOLEAN, GObject.TYPE_BOOLEAN, GObject.TYPE_BOOLEAN]
- }
- }
- }, class FileItem extends St.Bin {
- _init(file, fileInfo, fileExtra) {
- super._init({ visible: true });
- this._fileExtra = fileExtra;
- this._loadThumbnailDataCancellable = null;
- this._thumbnailScriptWatch = 0;
- this._setMetadataCancellable = null;
- this._queryFileInfoCancellable = null;
- this._isSpecial = this._fileExtra != Prefs.FileType.NONE;
- this._file = file;
- this._mount = null;
- if (this._fileExtra == Prefs.FileType.MOUNT_DISK)
- this._mount = this._file.find_enclosing_mount(null);
- this._savedCoordinates = null;
- let savedCoordinates = fileInfo.get_attribute_as_string('metadata::nautilus-icon-position');
- if (savedCoordinates != null)
- this._savedCoordinates = savedCoordinates.split(',').map(x => Number(x));
- this._state = State.NORMAL;
- try {
- this.set_fill(true, true);
- } catch(error) {}
- let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
- this._delegate = this;
- this.connect('destroy', () => this._onDestroy());
- this._container = new St.BoxLayout({ reactive: true,
- track_hover: true,
- can_focus: true,
- style_class: 'file-item',
- x_expand: true,
- y_expand: true,
- x_align: Clutter.ActorAlign.FILL,
- vertical: true });
- this.set_child(this._container);
- this._icon = new St.Bin();
- this._icon.set_height(Prefs.get_icon_size() * scaleFactor);
- this._iconAllocationIdleId = 0;
- this._iconAllocationId = this._icon.connect("notify::allocation", () => {
- if (this._iconAllocationIdleId)
- GLib.source_remove(this._iconAllocationIdleId);
- this._iconAllocationIdleId = GLib.idle_add(GLib.PRIORITY_DEFAULT, () => {
- GLib.source_remove(this._iconAllocationIdleId);
- this._iconAllocationIdleId = 0;
- this._updateIcon();
- return GLib.SOURCE_REMOVE;
- });
- });
- this._iconContainer = new St.Bin({ visible: true });
- this._iconContainer.child = this._icon;
- this._container.add_child(this._iconContainer);
- this._label = new St.Label({
- style_class: 'name-label'
- });
- this._container.add_child(this._label);
- let clutterText = this._label.get_clutter_text();
- /* TODO: Convert to gobject.set for 3.30 */
- clutterText.set_line_wrap(true);
- clutterText.set_line_wrap_mode(Pango.WrapMode.WORD_CHAR);
- clutterText.set_ellipsize(Pango.EllipsizeMode.END);
- this._container.connect('button-press-event', (actor, event) => this._onPressButton(actor, event));
- this._container.connect('motion-event', (actor, event) => this._onMotion(actor, event));
- this._container.connect('leave-event', (actor, event) => this._onLeave(actor, event));
- this._container.connect('enter-event', (actor, event) => this._onEnter(actor, event));
- this._container.connect('button-release-event', (actor, event) => this._onReleaseButton(actor, event));
- /* Set the metadata and update relevant UI */
- this._updateMetadataFromFileInfo(fileInfo);
- this._menuManager = null;
- this._menu = null;
- this._updateIcon();
- this._isSelected = false;
- this._primaryButtonPressed = false;
- if (this._attributeCanExecute && !this._isValidDesktopFile)
- this._execLine = this.file.get_path();
- if (fileExtra == Prefs.FileType.USER_DIRECTORY_TRASH) {
- // if this icon is the trash, monitor the state of the directory to update the icon
- this._trashChanged = false;
- this._queryTrashInfoCancellable = null;
- this._scheduleTrashRefreshId = 0;
- this._monitorTrashDir = this._file.monitor_directory(Gio.FileMonitorFlags.WATCH_MOVES, null);
- this._monitorTrashId = this._monitorTrashDir.connect('changed', (obj, file, otherFile, eventType) => {
- switch(eventType) {
- case Gio.FileMonitorEvent.DELETED:
- case Gio.FileMonitorEvent.MOVED_OUT:
- case Gio.FileMonitorEvent.CREATED:
- case Gio.FileMonitorEvent.MOVED_IN:
- if (this._queryTrashInfoCancellable || this._scheduleTrashRefreshId) {
- if (this._scheduleTrashRefreshId)
- GLib.source_remove(this._scheduleTrashRefreshId);
- this._scheduleTrashRefreshId = Mainloop.timeout_add(200, () => this._refreshTrashIcon());
- } else {
- this._refreshTrashIcon();
- }
- break;
- }
- });
- }
- this._writebleByOthersId = Extension.desktopManager.connect('notify::writable-by-others', () => {
- if (!this._isValidDesktopFile)
- return;
- this._refreshMetadataAsync(true);
- });
- }
- set_margins(width, height) {
- let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
- this.set_width(Prefs.getDesiredWidth(scaleFactor, width));
- this.set_height(Prefs.getDesiredHeight(scaleFactor, height));
- }
- onAttributeChanged() {
- this._refreshMetadataAsync(this._isDesktopFile);
- }
- _onDestroy() {
- /* Regular file data */
- if (this._setMetadataCancellable)
- this._setMetadataCancellable.cancel();
- if (this._queryFileInfoCancellable)
- this._queryFileInfoCancellable.cancel();
- Extension.desktopManager.disconnect(this._writebleByOthersId);
- /* Thumbnailing */
- if (this._thumbnailScriptWatch)
- GLib.source_remove(this._thumbnailScriptWatch);
- if (this._loadThumbnailDataCancellable)
- this._loadThumbnailDataCancellable.cancel();
- /* Desktop file */
- if (this._monitorDesktopFileId) {
- this._monitorDesktopFile.disconnect(this._monitorDesktopFileId);
- this._monitorDesktopFile.cancel();
- }
- /* Trash */
- if (this._monitorTrashDir) {
- this._monitorTrashDir.disconnect(this._monitorTrashId);
- this._monitorTrashDir.cancel();
- }
- if (this._queryTrashInfoCancellable)
- this._queryTrashInfoCancellable.cancel();
- if (this._scheduleTrashRefreshId)
- GLib.source_remove(this._scheduleTrashRefreshId);
- /* Icon */
- this._icon.disconnect(this._iconAllocationId);
- if (this._iconAllocationIdleId)
- GLib.source_remove(this._iconAllocationIdleId);
- /* Menu */
- this._removeMenu();
- }
- _refreshMetadataAsync(rebuild) {
- if (this._queryFileInfoCancellable)
- this._queryFileInfoCancellable.cancel();
- this._queryFileInfoCancellable = new Gio.Cancellable();
- this._file.query_info_async(DesktopIconsUtil.DEFAULT_ATTRIBUTES,
- Gio.FileQueryInfoFlags.NONE,
- GLib.PRIORITY_DEFAULT,
- this._queryFileInfoCancellable,
- (source, result) => {
- try {
- let newFileInfo = source.query_info_finish(result);
- this._queryFileInfoCancellable = null;
- this._updateMetadataFromFileInfo(newFileInfo);
- if (rebuild)
- this._recreateMenu();
- this._updateIcon();
- } catch(error) {
- if (!error.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
- global.log("Error getting the file info: " + error);
- }
- });
- }
- _updateMetadataFromFileInfo(fileInfo) {
- this._fileInfo = fileInfo;
- let oldLabelText = this._label.text;
- this._displayName = fileInfo.get_attribute_as_string('standard::display-name');
- this._attributeCanExecute = fileInfo.get_attribute_boolean('access::can-execute');
- this._unixmode = fileInfo.get_attribute_uint32('unix::mode');
- this._writableByOthers = (this._unixmode & S_IWOTH) != 0;
- this._trusted = fileInfo.get_attribute_as_string('metadata::trusted') == 'true';
- this._attributeContentType = fileInfo.get_content_type();
- this._isDesktopFile = this._attributeContentType == 'application/x-desktop';
- if (this._isDesktopFile && this._writableByOthers)
- log(`desktop-icons: File ${this._displayName} is writable by others - will not allow launching`);
- if (this._isDesktopFile) {
- this._desktopFile = Gio.DesktopAppInfo.new_from_filename(this._file.get_path());
- if (!this._desktopFile) {
- log(`Couldn’t parse ${this._displayName} as a desktop file, will treat it as a regular file.`);
- this._isValidDesktopFile = false;
- } else {
- this._isValidDesktopFile = true;
- }
- } else {
- this._isValidDesktopFile = false;
- }
- if (this.displayName != oldLabelText) {
- this._label.text = this.displayName;
- }
- this._fileType = fileInfo.get_file_type();
- this._isDirectory = this._fileType == Gio.FileType.DIRECTORY;
- this._isSpecial = this._fileExtra != Prefs.FileType.NONE;
- this._isHidden = fileInfo.get_is_hidden() | fileInfo.get_is_backup();
- this._isSymlink = fileInfo.get_is_symlink();
- this._modifiedTime = this._fileInfo.get_attribute_uint64("time::modified");
- /*
- * This is a glib trick to detect broken symlinks. If a file is a symlink, the filetype
- * points to the final file, unless it is broken; thus if the file type is SYMBOLIC_LINK,
- * it must be a broken link.
- * https://developer.gnome.org/gio/stable/GFile.html#g-file-query-info
- */
- this._isBrokenSymlink = this._isSymlink && this._fileType == Gio.FileType.SYMBOLIC_LINK;
- }
- onFileRenamed(file) {
- this._file = file;
- this._refreshMetadataAsync(false);
- }
- _updateIcon() {
- if (this._fileExtra == Prefs.FileType.USER_DIRECTORY_TRASH) {
- this._icon.child = this._createEmblemedStIcon(this._fileInfo.get_icon(), null);
- return;
- }
- if (this._fileExtra == Prefs.FileType.MOUNT_DISK) {
- this._icon.child = this._createEmblemedStIcon(this._mount.get_icon(), null);
- return;
- }
- let thumbnailFactory = GnomeDesktop.DesktopThumbnailFactory.new(GnomeDesktop.DesktopThumbnailSize.LARGE);
- if ((Prefs.nautilusSettings.get_string('show-image-thumbnails') != 'never') &&
- (thumbnailFactory.can_thumbnail(this._file.get_uri(),
- this._attributeContentType,
- this._modifiedTime))) {
- let thumbnail = thumbnailFactory.lookup(this._file.get_uri(), this._modifiedTime);
- if (thumbnail == null) {
- if (!thumbnailFactory.has_valid_failed_thumbnail(this._file.get_uri(),
- this._modifiedTime)) {
- let argv = [];
- argv.push(GLib.build_filenamev([ExtensionUtils.getCurrentExtension().path,
- 'createThumbnail.js']));
- argv.push(this._file.get_path());
- let [success, pid] = GLib.spawn_async(null, argv, null,
- GLib.SpawnFlags.SEARCH_PATH | GLib.SpawnFlags.DO_NOT_REAP_CHILD, null);
- if (this._thumbnailScriptWatch)
- GLib.source_remove(this._thumbnailScriptWatch);
- this._thumbnailScriptWatch = GLib.child_watch_add(GLib.PRIORITY_DEFAULT,
- pid,
- (pid, exitCode) => {
- this._thumbnailScriptWatch = 0;
- if (exitCode == 0)
- this._updateIcon();
- else
- global.log('Failed to generate thumbnail for ' + this._filePath);
- GLib.spawn_close_pid(pid);
- return false;
- }
- );
- }
- } else {
- if (this._loadThumbnailDataCancellable)
- this._loadThumbnailDataCancellable.cancel();
- this._loadThumbnailDataCancellable = new Gio.Cancellable();
- let thumbnailFile = Gio.File.new_for_path(thumbnail);
- thumbnailFile.load_bytes_async(this._loadThumbnailDataCancellable,
- (source, result) => {
- try {
- this._loadThumbnailDataCancellable = null;
- let [thumbnailData, etag_out] = source.load_bytes_finish(result);
- let thumbnailStream = Gio.MemoryInputStream.new_from_bytes(thumbnailData);
- let thumbnailPixbuf = GdkPixbuf.Pixbuf.new_from_stream(thumbnailStream, null);
- if (thumbnailPixbuf != null) {
- let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
- let thumbnailImage = new Clutter.Image();
- thumbnailImage.set_data(thumbnailPixbuf.get_pixels(),
- thumbnailPixbuf.has_alpha ? Cogl.PixelFormat.RGBA_8888 : Cogl.PixelFormat.RGB_888,
- thumbnailPixbuf.width,
- thumbnailPixbuf.height,
- thumbnailPixbuf.rowstride
- );
- let icon = new Clutter.Actor();
- icon.set_content(thumbnailImage);
- let containerWidth = (this._icon.allocation.x2 - this._icon.allocation.x1) * scaleFactor;
- let containerHeight = Prefs.get_icon_size() * scaleFactor;
- let containerAspectRatio = containerWidth / containerHeight;
- let iconAspectRatio = thumbnailPixbuf.width / thumbnailPixbuf.height;
- if (containerAspectRatio > iconAspectRatio) {
- let iconWidth = containerHeight * iconAspectRatio;
- icon.set_size(iconWidth, containerHeight);
- let margin = (containerWidth - iconWidth) / 2;
- icon.margin_left = Math.ceil(margin);
- icon.margin_right = Math.floor(margin);
- } else {
- let iconHeight = containerWidth / iconAspectRatio;
- icon.set_size(containerWidth, iconHeight);
- let margin = (containerHeight - iconHeight) / 2;
- icon.margin_top = Math.ceil(margin);
- icon.margin_bottom = Math.floor(margin);
- }
- this._icon.child = icon;
- }
- } catch (error) {
- if (!error.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
- global.log('Error while loading thumbnail: ' + error);
- this._icon.child = this._createEmblemedStIcon(this._fileInfo.get_icon(), null);
- }
- }
- }
- );
- }
- }
- if (this._isBrokenSymlink) {
- this._icon.child = this._createEmblemedStIcon(null, 'text-x-generic');
- } else {
- if (this.trustedDesktopFile && this._desktopFile.has_key('Icon'))
- this._icon.child = this._createEmblemedStIcon(null, this._desktopFile.get_string('Icon'));
- else
- this._icon.child = this._createEmblemedStIcon(this._fileInfo.get_icon(), null);
- }
- }
- _refreshTrashIcon() {
- if (this._queryTrashInfoCancellable)
- this._queryTrashInfoCancellable.cancel();
- this._queryTrashInfoCancellable = new Gio.Cancellable();
- this._file.query_info_async(DesktopIconsUtil.DEFAULT_ATTRIBUTES,
- Gio.FileQueryInfoFlags.NONE,
- GLib.PRIORITY_DEFAULT,
- this._queryTrashInfoCancellable,
- (source, result) => {
- try {
- this._fileInfo = source.query_info_finish(result);
- this._queryTrashInfoCancellable = null;
- this._updateIcon();
- } catch(error) {
- if (!error.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
- global.log('Error getting the number of files in the trash: ' + error);
- }
- });
- this._scheduleTrashRefreshId = 0;
- return false;
- }
- get file() {
- return this._file;
- }
- get isHidden() {
- return this._isHidden;
- }
- _createEmblemedStIcon(icon, iconName) {
- if (icon == null) {
- if (GLib.path_is_absolute(iconName)) {
- let iconFile = Gio.File.new_for_commandline_arg(iconName);
- icon = new Gio.FileIcon({ file: iconFile });
- } else {
- icon = Gio.ThemedIcon.new_with_default_fallbacks(iconName);
- }
- }
- let itemIcon = Gio.EmblemedIcon.new(icon, null);
- if (this._isSymlink) {
- if (this._isBrokenSymlink)
- itemIcon.add_emblem(Gio.Emblem.new(Gio.ThemedIcon.new('emblem-unreadable')));
- else
- itemIcon.add_emblem(Gio.Emblem.new(Gio.ThemedIcon.new('emblem-symbolic-link')));
- } else if (this.trustedDesktopFile) {
- itemIcon.add_emblem(Gio.Emblem.new(Gio.ThemedIcon.new('emblem-symbolic-link')));
- }
- return new St.Icon({ gicon: itemIcon,
- icon_size: Prefs.get_icon_size()
- });
- }
- doRename() {
- if (!this.canRename()) {
- log (`Error: ${this.file.get_uri()} cannot be renamed`);
- return;
- }
- this.emit('rename-clicked');
- }
- _doOpenContext(context) {
- if (this._isBrokenSymlink) {
- log(`Error: Can’t open ${this.file.get_uri()} because it is a broken symlink.`);
- return;
- }
- if (this.trustedDesktopFile) {
- this._desktopFile.launch_uris_as_manager([], context, GLib.SpawnFlags.SEARCH_PATH, null, null);
- return;
- }
- if (this._attributeCanExecute &&
- !this._isDirectory &&
- !this._isValidDesktopFile &&
- Gio.content_type_can_be_executable(this._attributeContentType)) {
- if (this._execLine)
- Util.spawnCommandLine(this._execLine);
- return;
- }
- Gio.AppInfo.launch_default_for_uri_async(this.file.get_uri(),
- null, null,
- (source, result) => {
- try {
- Gio.AppInfo.launch_default_for_uri_finish(result);
- } catch (e) {
- log('Error opening file ' + this.file.get_uri() + ': ' + e.message);
- }
- }
- );
- }
- doOpen() {
- this._doOpenContext(null);
- }
- _doDiscreteGpu() {
- let gpus = Extension.desktopManager.switcherooProxyGPUs;
- if (!gpus) {
- log('Could not apply discrete GPU environment, no GPUs in list');
- }
- for(let gpu in gpus) {
- if (!gpus[gpu])
- continue;
- let default_variant = gpus[gpu]['Default'];
- if (!default_variant || default_variant.get_boolean())
- continue;
- let env = gpus[gpu]['Environment'];
- if (!env)
- continue;
- let env_s = env.get_strv();
- let context = new Gio.AppLaunchContext;
- for (let i = 0; i < env_s.length; i=i+2) {
- context.setenv(env_s[i], env_s[i+1]);
- }
- this._doOpenContext(context);
- return;
- }
- log('Could not find discrete GPU data in switcheroo-control');
- }
- _onCopyClicked() {
- Extension.desktopManager.doCopy();
- }
- _onCutClicked() {
- Extension.desktopManager.doCut();
- }
- _onShowInFilesClicked() {
- DBusUtils.FreeDesktopFileManagerProxy.ShowItemsRemote([this.file.get_uri()], '',
- (result, error) => {
- if (error)
- log('Error showing file on desktop: ' + error.message);
- }
- );
- }
- _onPropertiesClicked() {
- DBusUtils.FreeDesktopFileManagerProxy.ShowItemPropertiesRemote([this.file.get_uri()], '',
- (result, error) => {
- if (error)
- log('Error showing properties: ' + error.message);
- }
- );
- }
- _onMoveToTrashClicked() {
- Extension.desktopManager.doTrash();
- }
- _onEmptyTrashClicked() {
- Extension.desktopManager.doEmptyTrash();
- }
- _onEjectClicked() {
- DesktopIconsUtil.eject(this._mount);
- }
- get _allowLaunchingText() {
- if (this.trustedDesktopFile)
- return _("Don’t Allow Launching");
- return _("Allow Launching");
- }
- get metadataTrusted() {
- return this._trusted;
- }
- set metadataTrusted(value) {
- this._trusted = value;
- let info = new Gio.FileInfo();
- info.set_attribute_string('metadata::trusted',
- value ? 'true' : 'false');
- this._file.set_attributes_async(info,
- Gio.FileQueryInfoFlags.NONE,
- GLib.PRIORITY_LOW,
- null,
- (source, result) => {
- try {
- source.set_attributes_finish(result);
- this._refreshMetadataAsync(true);
- } catch(e) {
- log(`Failed to set metadata::trusted: ${e.message}`);
- }
- });
- }
- _onAllowDisallowLaunchingClicked() {
- this.metadataTrusted = !this.trustedDesktopFile;
- /*
- * we're marking as trusted, make the file executable too. note that we
- * do not ever remove the executable bit, since we don't know who set
- * it.
- */
- if (this.metadataTrusted && !this._attributeCanExecute) {
- let info = new Gio.FileInfo();
- let newUnixMode = this._unixmode | S_IXUSR;
- info.set_attribute_uint32(Gio.FILE_ATTRIBUTE_UNIX_MODE, newUnixMode);
- this._file.set_attributes_async(info,
- Gio.FileQueryInfoFlags.NONE,
- GLib.PRIORITY_LOW,
- null,
- (source, result) => {
- try {
- source.set_attributes_finish (result);
- } catch(e) {
- log(`Failed to set unix mode: ${e.message}`);
- }
- });
- }
- }
- canRename() {
- return !this.trustedDesktopFile && this._fileExtra == Prefs.FileType.NONE;
- }
- _doOpenWith() {
- DBusUtils.openFileWithOtherApplication(this.file.get_path());
- }
- _getSelectionStyle() {
- let rgba = DesktopIconsUtil.getGtkClassBackgroundColor('view', Gtk.StateFlags.SELECTED);
- let background_color =
- 'rgba(' + rgba.red * 255 + ', ' + rgba.green * 255 + ', ' + rgba.blue * 255 + ', 0.6)';
- let border_color =
- 'rgba(' + rgba.red * 255 + ', ' + rgba.green * 255 + ', ' + rgba.blue * 255 + ', 0.8)';
- return 'background-color: ' + background_color + ';' +
- 'border-color: ' + border_color + ';';
- }
- get menu() {
- return this._menu;
- }
- _removeMenu() {
- if (this._menu != null) {
- if (this._menuManager != null)
- this._menuManager.removeMenu(this._menu);
- Main.layoutManager.uiGroup.remove_child(this._menu.actor);
- this._menu.destroy();
- this._menu = null;
- }
- this._menuManager = null;
- }
- _recreateMenu() {
- this._removeMenu();
- this._menuManager = new PopupMenu.PopupMenuManager(this);
- let side = St.Side.LEFT;
- if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)
- side = St.Side.RIGHT;
- this._menu = new PopupMenu.PopupMenu(this, 0.5, side);
- this._menu.addAction(_('Open'), () => this.doOpen());
- switch (this._fileExtra) {
- case Prefs.FileType.NONE:
- if (!this._isDirectory) {
- this._actionOpenWith = this._menu.addAction(_('Open With Other Application'), () => this._doOpenWith());
- if (Extension.desktopManager.discreteGpuAvailable)
- this._menu.addAction(_('Launch using Dedicated Graphics Card'), () => this._doDiscreteGpu());
- } else {
- this._actionOpenWith = null;
- }
- this._menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
- this._actionCut = this._menu.addAction(_('Cut'), () => this._onCutClicked());
- this._actionCopy = this._menu.addAction(_('Copy'), () => this._onCopyClicked());
- if (this.canRename())
- this._menu.addAction(_('Rename…'), () => this.doRename());
- this._actionTrash = this._menu.addAction(_('Move to Trash'), () => this._onMoveToTrashClicked());
- if (this._isValidDesktopFile && !Extension.desktopManager.writableByOthers && !this._writableByOthers) {
- this._menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
- this._allowLaunchingMenuItem = this._menu.addAction(this._allowLaunchingText,
- () => this._onAllowDisallowLaunchingClicked());
- }
- break;
- case Prefs.FileType.USER_DIRECTORY_TRASH:
- this._menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
- this._menu.addAction(_('Empty Trash'), () => this._onEmptyTrashClicked());
- break;
- case Prefs.FileType.MOUNT_DISK:
- this._menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
- this._menu.addAction(_('Eject'), () => this._onEjectClicked());
- break;
- default:
- break;
- }
- this._menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
- this._menu.addAction(_('Properties'), () => this._onPropertiesClicked());
- this._menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
- this._menu.addAction(_('Show in Files'), () => this._onShowInFilesClicked());
- if (this._isDirectory && this.file.get_path() != null)
- this._actionOpenInTerminal = this._menu.addAction(_('Open in Terminal'), () => this._onOpenTerminalClicked());
- this._menuManager.addMenu(this._menu);
- Main.layoutManager.uiGroup.add_child(this._menu.actor);
- this._menu.actor.hide();
- }
- _ensureMenu() {
- if (this._menu == null)
- this._recreateMenu();
- return this._menu;
- }
- _onOpenTerminalClicked () {
- DesktopIconsUtil.launchTerminal(this.file.get_path());
- }
- _onPressButton(actor, event) {
- let button = event.get_button();
- if (button == 3) {
- if (!this.isSelected)
- this.emit('selected', false, false, true);
- this._ensureMenu().toggle();
- if (this._actionOpenWith) {
- let allowOpenWith = (Extension.desktopManager.getNumberOfSelectedItems() == 1);
- this._actionOpenWith.setSensitive(allowOpenWith);
- }
- let specialFilesSelected = Extension.desktopManager.checkIfSpecialFilesAreSelected();
- if (this._actionCut)
- this._actionCut.setSensitive(!specialFilesSelected);
- if (this._actionCopy)
- this._actionCopy.setSensitive(!specialFilesSelected);
- if (this._actionTrash)
- this._actionTrash.setSensitive(!specialFilesSelected);
- return Clutter.EVENT_STOP;
- } else if (button == 1) {
- if (event.get_click_count() == 1) {
- let [x, y] = event.get_coords();
- this._primaryButtonPressed = true;
- this._buttonPressInitialX = x;
- this._buttonPressInitialY = y;
- let shiftPressed = !!(event.get_state() & Clutter.ModifierType.SHIFT_MASK);
- let controlPressed = !!(event.get_state() & Clutter.ModifierType.CONTROL_MASK);
- if (controlPressed || shiftPressed)
- this.emit('selected', true, false, !this._isSelected);
- else
- this.emit('selected', false, false, true);
- }
- return Clutter.EVENT_STOP;
- }
- return Clutter.EVENT_PROPAGATE;
- }
- _onEnter(actor, event) {
- if (Prefs.CLICK_POLICY_SINGLE)
- global.display.set_cursor(Meta.Cursor.POINTING_HAND);
- else
- global.display.set_cursor(Meta.Cursor.DEFAULT);
- }
- _onLeave(actor, event) {
- this._primaryButtonPressed = false;
- if (Prefs.CLICK_POLICY_SINGLE)
- global.display.set_cursor(Meta.Cursor.DEFAULT);
- }
- _onMotion(actor, event) {
- let [x, y] = event.get_coords();
- if (this._primaryButtonPressed) {
- let xDiff = x - this._buttonPressInitialX;
- let yDiff = y - this._buttonPressInitialY;
- let distance = Math.sqrt(Math.pow(xDiff, 2) + Math.pow(yDiff, 2));
- if (distance > DRAG_TRESHOLD) {
- // Don't need to track anymore this if we start drag, and also
- // avoids reentrance here
- this._primaryButtonPressed = false;
- let event = Clutter.get_current_event();
- let [x, y] = event.get_coords();
- Extension.desktopManager.dragStart();
- }
- }
- return Clutter.EVENT_PROPAGATE;
- }
- _onReleaseButton(actor, event) {
- let button = event.get_button();
- if (button == 1) {
- // primaryButtonPressed is TRUE only if the user has pressed the button
- // over an icon, and if (s)he has not started a drag&drop operation
- if (this._primaryButtonPressed) {
- this._primaryButtonPressed = false;
- let shiftPressed = !!(event.get_state() & Clutter.ModifierType.SHIFT_MASK);
- let controlPressed = !!(event.get_state() & Clutter.ModifierType.CONTROL_MASK);
- if ((event.get_click_count() == 1) && Prefs.CLICK_POLICY_SINGLE && !shiftPressed && !controlPressed)
- this.doOpen();
- return Clutter.EVENT_STOP;
- }
- if ((event.get_click_count() == 2) && (!Prefs.CLICK_POLICY_SINGLE))
- this.doOpen();
- }
- return Clutter.EVENT_PROPAGATE;
- }
- get savedCoordinates() {
- return this._savedCoordinates;
- }
- _onSetMetadataFileFinished(source, result) {
- try {
- let [success, info] = source.set_attributes_finish(result);
- } catch (error) {
- if (!error.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
- log(`Error setting metadata to desktop files ${error}`);
- }
- }
- set savedCoordinates(pos) {
- if (this._setMetadataCancellable)
- this._setMetadataCancellable.cancel();
- this._setMetadataCancellable = new Gio.Cancellable();
- this._savedCoordinates = [pos[0], pos[1]];
- let info = new Gio.FileInfo();
- info.set_attribute_string('metadata::nautilus-icon-position',
- `${pos[0]},${pos[1]}`);
- this.file.set_attributes_async(info,
- Gio.FileQueryInfoFlags.NONE,
- GLib.PRIORITY_DEFAULT,
- this._setMetadataCancellable,
- (source, result) => {
- this._setMetadataCancellable = null;
- this._onSetMetadataFileFinished(source, result);
- }
- );
- }
- intersectsWith(argX, argY, argWidth, argHeight) {
- let rect = new Meta.Rectangle({ x: argX, y: argY, width: argWidth, height: argHeight });
- let [containerX, containerY] = this._container.get_transformed_position();
- let boundingBox = new Meta.Rectangle({ x: containerX,
- y: containerY,
- width: this._container.allocation.x2 - this._container.allocation.x1,
- height: this._container.allocation.y2 - this._container.allocation.y1 });
- let [intersects, _] = rect.intersect(boundingBox);
- return intersects;
- }
- set isSelected(isSelected) {
- isSelected = !!isSelected;
- if (isSelected == this._isSelected)
- return;
- if (isSelected) {
- this._container.set_style(this._getSelectionStyle());
- } else {
- this._container.set_style('background-color: transparent');
- this._container.set_style('border-color: transparent');
- }
- this._isSelected = isSelected;
- }
- get isSelected() {
- return this._isSelected;
- }
- get isSpecial() {
- return this._isSpecial;
- }
- get state() {
- return this._state;
- }
- set state(state) {
- if (state == this._state)
- return;
- this._state = state;
- }
- get isDirectory() {
- return this._isDirectory;
- }
- get trustedDesktopFile() {
- return this._isValidDesktopFile &&
- this._attributeCanExecute &&
- this.metadataTrusted &&
- !Extension.desktopManager.writableByOthers &&
- !this._writableByOthers;
- }
- get fileName() {
- return this._fileInfo.get_name();
- }
- get displayName() {
- if (this._fileExtra == Prefs.FileType.USER_DIRECTORY_HOME)
- return _("Home");
- if (this.trustedDesktopFile)
- return this._desktopFile.get_name();
- return this._displayName || null;
- }
- acceptDrop() {
- return Extension.desktopManager.selectionDropOnFileItem(this);
- }
- });
|