BossBey File Manager
PHP:
8.2.30
OS:
Linux
User:
htmlandpixel
Root
/
usr
/
share
/
gnome-shell
/
extensions
/
desktop-icons@gnome-shell-extensions.gcampax.github.com
📤 Upload
📝 New File
📁 New Folder
Close
Editing: desktopGrid.js
/* 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 St = imports.gi.St; const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const GObject = imports.gi.GObject; const Shell = imports.gi.Shell; const Layout = imports.ui.layout; const Main = imports.ui.main; const BoxPointer = imports.ui.boxpointer; const PopupMenu = imports.ui.popupMenu; const GrabHelper = imports.ui.grabHelper; const Config = imports.misc.config; const ExtensionUtils = imports.misc.extensionUtils; const Me = ExtensionUtils.getCurrentExtension(); const CreateFolderDialog = Me.imports.createFolderDialog; const Extension = Me.imports.extension; const FileItem = Me.imports.fileItem; const Prefs = Me.imports.prefs; const DBusUtils = Me.imports.dbusUtils; const DesktopIconsUtil = Me.imports.desktopIconsUtil; const Util = imports.misc.util; const Clipboard = St.Clipboard.get_default(); const CLIPBOARD_TYPE = St.ClipboardType.CLIPBOARD; const Gettext = imports.gettext.domain('gnome-shell-extensions'); const _ = Gettext.gettext; /* From NautilusFileUndoManagerState */ var UndoStatus = { NONE: 0, UNDO: 1, REDO: 2, }; var StoredCoordinates = { PRESERVE: 0, OVERWRITE:1, ASSIGN:2, }; var Placeholder = GObject.registerClass({ GTypeName: 'DesktopIcons_Placeholder', }, class Placeholder extends St.Bin { }); var DesktopGrid = GObject.registerClass({ GTypeName: 'DesktopIcons_DesktopGrid', }, class DesktopGrid extends St.Widget { _init(bgManager) { this._bgManager = bgManager; this._fileItemHandlers = new Map(); this._fileItems = []; this.layout = new Clutter.GridLayout({ orientation: Clutter.Orientation.VERTICAL, column_homogeneous: true, row_homogeneous: true }); this._actorLayout = new Clutter.BinLayout({ x_align: Clutter.BinAlignment.FIXED, y_align: Clutter.BinAlignment.FIXED }); super._init({ layout_manager: this._actorLayout }); this._delegate = this; this.connect('style-changed', () => { if (this.isFirst) Extension.desktopManager.scheduleReLayoutChildren(); }); this._grid = new St.Widget({ name: 'DesktopGrid', layout_manager: this.layout, reactive: true, x_expand: true, y_expand: true, can_focus: true, opacity: 255 }); this.add_child(this._grid); this._menuManager = new PopupMenu.PopupMenuManager(this); this._bgManager._container.add_child(this); let monitorIndex = bgManager._monitorIndex; this._monitorConstraint = new Layout.MonitorConstraint({ index: monitorIndex, work_area: true }); this.add_constraint(this._monitorConstraint); this._addDesktopBackgroundMenu(); this._bgDestroyedId = bgManager.backgroundActor.connect('destroy', () => this._backgroundDestroyed()); this._grid.connect('button-press-event', (actor, event) => this._onPressButton(actor, event)); this._grid.connect('key-press-event', this._onKeyPress.bind(this)); this._grid.connect('notify::allocation', () => Extension.desktopManager.scheduleReLayoutChildren()); let themeNodeBase = this.get_theme_node(); let themeContext = St.ThemeContext.get_for_stage(global.stage); let themeNode = St.ThemeNode.new(themeContext, null, themeNodeBase.get_theme(), themeNodeBase.get_element_type(), null, "file-item", null, "fake-id {}"); this._extra_width = themeNode.get_margin(St.Side.LEFT) + themeNode.get_margin(St.Side.RIGHT) + themeNode.get_border_width(St.Side.LEFT) + themeNode.get_border_width(St.Side.RIGHT) + themeNode.get_horizontal_padding(); this._extra_height = themeNode.get_margin(St.Side.TOP) + themeNode.get_margin(St.Side.BOTTOM) + themeNode.get_border_width(St.Side.TOP) + themeNode.get_border_width(St.Side.BOTTOM) + themeNode.get_vertical_padding(); this.connect('destroy', () => this._onDestroy()); } _onKeyPress(actor, event) { if (global.stage.get_key_focus() != actor) return Clutter.EVENT_PROPAGATE; let symbol = event.get_key_symbol(); let isCtrl = (event.get_state() & Clutter.ModifierType.CONTROL_MASK) != 0; let isShift = (event.get_state() & Clutter.ModifierType.SHIFT_MASK) != 0; if (isCtrl && isShift && [Clutter.KEY_Z, Clutter.KEY_z].indexOf(symbol) > -1) { this._doRedo(); return Clutter.EVENT_STOP; } else if (isCtrl && [Clutter.KEY_Z, Clutter.KEY_z].indexOf(symbol) > -1) { this._doUndo(); return Clutter.EVENT_STOP; } else if (isCtrl && [Clutter.KEY_C, Clutter.KEY_c].indexOf(symbol) > -1) { Extension.desktopManager.doCopy(); return Clutter.EVENT_STOP; } else if (isCtrl && [Clutter.KEY_X, Clutter.KEY_x].indexOf(symbol) > -1) { Extension.desktopManager.doCut(); return Clutter.EVENT_STOP; } else if (isCtrl && [Clutter.KEY_V, Clutter.KEY_v].indexOf(symbol) > -1) { this._doPaste(); return Clutter.EVENT_STOP; } else if (symbol == Clutter.KEY_Return) { Extension.desktopManager.doOpen(); return Clutter.EVENT_STOP; } else if (symbol == Clutter.KEY_Delete) { Extension.desktopManager.doTrash(); return Clutter.EVENT_STOP; } else if (symbol == Clutter.KEY_F2) { // Support renaming other grids file items. Extension.desktopManager.doRename(); return Clutter.EVENT_STOP; } else if (symbol == Clutter.KEY_space) { Extension.desktopManager.doPreview(); return Clutter.EVENT_STOP; } return Clutter.EVENT_PROPAGATE; } _backgroundDestroyed() { this._bgDestroyedId = 0; if (this._bgManager == null) return; if (this._bgManager._backgroundSource) { this._bgDestroyedId = this._bgManager.backgroundActor.connect('destroy', () => this._backgroundDestroyed()); } else { this.destroy(); } } _onDestroy() { if (this._bgDestroyedId && this._bgManager.backgroundActor) this._bgManager.backgroundActor.disconnect(this._bgDestroyedId); this._bgDestroyedId = 0; this._bgManager = null; this._menuManager = null; } _onNewFolderClicked() { let dialog = new CreateFolderDialog.CreateFolderDialog(); dialog.connect('response', (dialog, name) => { let dir = DesktopIconsUtil.getDesktopDir().get_child(name); DBusUtils.NautilusFileOperationsProxy.CreateFolderRemote(dir.get_uri(), (result, error) => { if (error) throw new Error('Error creating new folder: ' + error.message); } ); }); dialog.open(); } _parseClipboardText(text) { if (text === null) return [false, false, null]; let lines = text.split('\n'); let [mime, action, ...files] = lines; if (mime != 'x-special/nautilus-clipboard') return [false, false, null]; if (!(['copy', 'cut'].includes(action))) return [false, false, null]; let isCut = action == 'cut'; /* Last line is empty due to the split */ if (files.length <= 1) return [false, false, null]; /* Remove last line */ files.pop(); return [true, isCut, files]; } _doPaste() { Clipboard.get_text(CLIPBOARD_TYPE, (clipboard, text) => { let [valid, is_cut, files] = this._parseClipboardText(text); if (!valid) return; let desktopDir = `${DesktopIconsUtil.getDesktopDir().get_uri()}`; if (is_cut) { DBusUtils.NautilusFileOperationsProxy.MoveURIsRemote(files, desktopDir, (result, error) => { if (error) throw new Error('Error moving files: ' + error.message); } ); } else { DBusUtils.NautilusFileOperationsProxy.CopyURIsRemote(files, desktopDir, (result, error) => { if (error) throw new Error('Error copying files: ' + error.message); } ); } } ); } _onPasteClicked() { this._doPaste(); } _doUndo() { DBusUtils.NautilusFileOperationsProxy.UndoRemote( (result, error) => { if (error) throw new Error('Error performing undo: ' + error.message); } ); } _onUndoClicked() { this._doUndo(); } _doRedo() { DBusUtils.NautilusFileOperationsProxy.RedoRemote( (result, error) => { if (error) throw new Error('Error performing redo: ' + error.message); } ); } _onRedoClicked() { this._doRedo(); } _onOpenDesktopInFilesClicked() { Gio.AppInfo.launch_default_for_uri_async(DesktopIconsUtil.getDesktopDir().get_uri(), null, null, (source, result) => { try { Gio.AppInfo.launch_default_for_uri_finish(result); } catch (e) { log('Error opening Desktop in Files: ' + e.message); } } ); } _onOpenTerminalClicked() { let desktopPath = DesktopIconsUtil.getDesktopDir().get_path(); DesktopIconsUtil.launchTerminal(desktopPath); } _syncUndoRedo() { this._undoMenuItem.visible = DBusUtils.NautilusFileOperationsProxy.UndoStatus == UndoStatus.UNDO; this._redoMenuItem.visible = DBusUtils.NautilusFileOperationsProxy.UndoStatus == UndoStatus.REDO; } _undoStatusChanged(proxy, properties, test) { if ('UndoStatus' in properties.deep_unpack()) this._syncUndoRedo(); } _createDesktopBackgroundMenu() { let menu = new PopupMenu.PopupMenu(Main.layoutManager.dummyCursor, 0, St.Side.TOP); menu.addAction(_("New Folder"), () => this._onNewFolderClicked()); this._submenu = new PopupMenu.PopupSubMenuMenuItem(_("New Document"), false); menu.addMenuItem(this._submenu); menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); this._pasteMenuItem = menu.addAction(_("Paste"), () => this._onPasteClicked()); this._undoMenuItem = menu.addAction(_("Undo"), () => this._onUndoClicked()); this._redoMenuItem = menu.addAction(_("Redo"), () => this._onRedoClicked()); menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); menu.addAction(_("Show Desktop in Files"), () => this._onOpenDesktopInFilesClicked()); menu.addAction(_("Open in Terminal"), () => this._onOpenTerminalClicked()); menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); menu.addSettingsAction(_("Change Background…"), 'gnome-background-panel.desktop'); menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); menu.addSettingsAction(_("Display Settings"), 'gnome-display-panel.desktop'); menu.addSettingsAction(_("Settings"), 'gnome-control-center.desktop'); menu.actor.add_style_class_name('background-menu'); Main.layoutManager.uiGroup.add_child(menu.actor); menu.actor.hide(); menu._propertiesChangedId = DBusUtils.NautilusFileOperationsProxy.connect('g-properties-changed', this._undoStatusChanged.bind(this)); this._syncUndoRedo(); menu.connect('destroy', () => DBusUtils.NautilusFileOperationsProxy.disconnect(menu._propertiesChangedId)); menu.connect('open-state-changed', (popupm, isOpen) => { if (isOpen) { Clipboard.get_text(CLIPBOARD_TYPE, (clipBoard, text) => { let [valid, is_cut, files] = this._parseClipboardText(text); this._pasteMenuItem.setSensitive(valid); } ); } } ); this._pasteMenuItem.setSensitive(false); return menu; } _openMenu(x, y) { Extension.desktopManager.endRubberBand(); Main.layoutManager.setDummyCursorGeometry(x, y, 0, 0); this._submenu.menu.removeAll(); let templates = Extension.templateManager.getTemplates(); if (templates.length == 0) { this._submenu.hide(); } else { for(let template of templates) { this._submenu.menu.addAction(template["name"], () => {this._newDocument(template)}, template["icon"]); } this._submenu.show(); } this._desktopBackgroundMenu.open(BoxPointer.PopupAnimation.NONE); /* Since the handler is in the press event it needs to ignore the release event * to not immediately close the menu on release */ this._menuManager.ignoreRelease(); } _newDocument(template) { let file = Extension.templateManager.getTemplateFile(template["file"]); if (file == null) return; let counter = 0; let finalName = `${template["name"]}${template["extension"]}`; let destination; do { if (counter != 0) { finalName = `${template["name"]} ${counter}${template["extension"]}` } destination = Gio.File.new_for_path( GLib.build_filenamev([GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_DESKTOP), finalName]) ); counter++; } while(destination.query_exists(null)); file.copy_async(destination, Gio.FileCopyFlags.NONE, GLib.PRIORITY_DEFAULT, null, null, (source, result) => { try { source.copy_finish(result); } catch(e) { log(`Failed to create template ${e.message}`); } }); } _addFileItemTo(fileItem, column, row, coordinatesAction) { let placeholder = this.layout.get_child_at(column, row); placeholder.child = fileItem; this._fileItems.push(fileItem); let fileItemHandler = {}; fileItemHandler.selectedId = fileItem.connect('selected', this._onFileItemSelected.bind(this)); fileItemHandler.renameId = fileItem.connect('rename-clicked', this._onRenameClicked.bind(this)); fileItemHandler.destroyId = fileItem.connect('destroy', () => { this._fileItemHandlers.delete(fileItem); }); this._fileItemHandlers.set(fileItem, fileItemHandler); /* If this file is new in the Desktop and hasn't yet * fixed coordinates, store the new possition to ensure * that the next time it will be shown in the same possition. * Also store the new possition if it has been moved by the user, * and not triggered by a screen change. */ if ((fileItem.savedCoordinates == null) || (coordinatesAction == StoredCoordinates.OVERWRITE)) { let [fileX, fileY] = placeholder.get_transformed_position(); fileItem.savedCoordinates = [Math.round(fileX), Math.round(fileY)]; } } addFileItemCloseTo(fileItem, x, y, coordinatesAction) { let [column, row] = this._getEmptyPlaceClosestTo(x, y, coordinatesAction); fileItem.set_margins(this._extra_width, this._extra_height); this._addFileItemTo(fileItem, column, row, coordinatesAction); } _getEmptyPlaceClosestTo(x, y, coordinatesAction) { let [actorX, actorY] = this._grid.get_transformed_position(); let actorWidth = this._grid.allocation.x2 - this._grid.allocation.x1; let actorHeight = this._grid.allocation.y2 - this._grid.allocation.y1; let placeX = Math.round((x - actorX) * this._columns / actorWidth); let placeY = Math.round((y - actorY) * this._rows / actorHeight); placeX = DesktopIconsUtil.clamp(placeX, 0, this._columns - 1); placeY = DesktopIconsUtil.clamp(placeY, 0, this._rows - 1); if (this.layout.get_child_at(placeX, placeY).child == null) return [placeX, placeY]; let found = false; let resColumn = null; let resRow = null; let minDistance = Infinity; for (let column = 0; column < this._columns; column++) { for (let row = 0; row < this._rows; row++) { let placeholder = this.layout.get_child_at(column, row); if (placeholder.child != null) continue; let [proposedX, proposedY] = placeholder.get_transformed_position(); if (coordinatesAction == StoredCoordinates.ASSIGN) return [column, row]; let distance = DesktopIconsUtil.distanceBetweenPoints(proposedX, proposedY, x, y); if (distance < minDistance) { found = true; minDistance = distance; resColumn = column; resRow = row; } } } if (!found) throw new Error(`Not enough place at monitor ${this._bgManager._monitorIndex}`); return [resColumn, resRow]; } removeFileItem(fileItem) { let index = this._fileItems.indexOf(fileItem); if (index > -1) this._fileItems.splice(index, 1); else throw new Error('Error removing children from container'); let [column, row] = this._getPosOfFileItem(fileItem); let placeholder = this.layout.get_child_at(column, row); placeholder.child = null; let fileItemHandler = this._fileItemHandlers.get(fileItem); Object.values(fileItemHandler).forEach(id => fileItem.disconnect(id)); this._fileItemHandlers.delete(fileItem); } _fillPlaceholders() { this._rows = this._getMaxRows(); this._columns = this._getMaxColumns(); for (let column = 0; column < this._columns; column++) { for (let row = 0; row < this._rows; row++) { this.layout.attach(new Placeholder(), column, row, 1, 1); } } } usingNewAllocationAPI() { return (this.get_fixed_position !== undefined); } reset() { let tmpFileItemsCopy = this._fileItems.slice(); for (let fileItem of tmpFileItemsCopy) this.removeFileItem(fileItem); this._grid.remove_all_children(); this._fillPlaceholders(); if (this.usingNewAllocationAPI()) this.allocate_preferred_size(0, 0); } _onStageMotion(actor, event) { if (this._drawingRubberBand) { let [x, y] = event.get_coords(); this._updateRubberBand(x, y); this._selectFromRubberband(x, y); } return Clutter.EVENT_PROPAGATE; } _onPressButton(actor, event) { let button = event.get_button(); let [x, y] = event.get_coords(); if (button == 1) { let shiftPressed = !!(event.get_state() & Clutter.ModifierType.SHIFT_MASK); let controlPressed = !!(event.get_state() & Clutter.ModifierType.CONTROL_MASK); if (!shiftPressed && !controlPressed) Extension.desktopManager.clearSelection(); let [gridX, gridY] = this._grid.get_transformed_position(); Extension.desktopManager.startRubberBand(x, y, gridX, gridY); return Clutter.EVENT_STOP; } if (button == 3) { this._openMenu(x, y); return Clutter.EVENT_STOP; } return Clutter.EVENT_PROPAGATE; } _addDesktopBackgroundMenu() { this._desktopBackgroundMenu = this._createDesktopBackgroundMenu(); this._menuManager.addMenu(this._desktopBackgroundMenu); this.connect('destroy', () => { this._desktopBackgroundMenu.destroy(); this._desktopBackgroundMenu = null; }); } _getMaxColumns() { let gridWidth = this._grid.allocation.x2 - this._grid.allocation.x1; let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor; let desiredWidth = Prefs.getDesiredWidth(scaleFactor, this._extra_width); return Math.floor(gridWidth / desiredWidth); } _getMaxRows() { let gridHeight = this._grid.allocation.y2 - this._grid.allocation.y1; let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor; let desiredHeight = Prefs.getDesiredHeight(scaleFactor, this._extra_height); return Math.floor(gridHeight / desiredHeight); } acceptDrop(source, actor, x, y, time) { /* Coordinates are relative to the grid, we want to transform them to * absolute coordinates to work across monitors */ let [gridX, gridY] = this.get_transformed_position(); let [absoluteX, absoluteY] = [x + gridX, y + gridY]; return Extension.desktopManager.acceptDrop(absoluteX, absoluteY); } _getPosOfFileItem(itemToFind) { if (itemToFind == null) throw new Error('Error at _getPosOfFileItem: child cannot be null'); let found = false; let column = 0; let row = 0; for (column = 0; column < this._columns; column++) { for (row = 0; row < this._rows; row++) { let item = this.layout.get_child_at(column, row); if (item.child && item.child.file.equal(itemToFind.file)) { found = true; break; } } if (found) break; } if (!found) throw new Error('Position of file item was not found'); return [column, row]; } _onFileItemSelected(fileItem, keepCurrentSelection, rubberBandSelection, addToSelection) { this._grid.grab_key_focus(); } _onRenameClicked(fileItem) { if (fileItem.menu && fileItem.menu.isOpen) { let id = fileItem.menu.connect('menu-closed', () => { fileItem.menu.disconnect(id); this.doRename(fileItem); }); } else { this.doRename(fileItem); } } doRename(fileItem) { let renamePopup = new RenamePopup(fileItem); this._menuManager.addMenu(renamePopup); this._menuManager.ignoreRelease(); renamePopup.connect('menu-closed', () => renamePopup.destroy()); renamePopup.open(); } }); var RenamePopupMenuItem = GObject.registerClass( class RenamePopupMenuItem extends PopupMenu.PopupBaseMenuItem { _init(fileItem) { super._init({ style_class: 'rename-popup-item', reactive: false, }); if (PopupMenu.Ornament.HIDDEN !== undefined) this.setOrnament(PopupMenu.Ornament.HIDDEN); else /* Support version prior 3.34.1 */ this._ornamentLabel.visible = false; this._fileItem = fileItem; // Entry this._entry = new St.Entry({ x_expand: true, width: 200, }); this.add_child(this._entry); this._entry.clutter_text.connect( 'notify::text', this._validate.bind(this)); this._entry.clutter_text.connect( 'activate', this._onRenameAccepted.bind(this)); // Rename button this._button = new St.Button({ style_class: 'button', reactive: true, button_mask: St.ButtonMask.ONE | St.ButtonMask.TWO, can_focus: true, label: _('Rename'), }); this.add_child(this._button); this._button.connect('clicked', this._onRenameAccepted.bind(this)); } vfunc_map() { this._entry.text = this._fileItem.displayName; this._entry.clutter_text.set_selection(0, -1); super.vfunc_map(); } vfunc_key_focus_in() { super.vfunc_key_focus_in(); this._entry.clutter_text.grab_key_focus(); } _isValidFolderName() { let newName = this._entry.text.trim(); return newName.length > 0 && newName.indexOf('/') === -1 && newName != this._fileItem.displayName; } _validate() { this._button.reactive = this._isValidFolderName(); } _onRenameAccepted() { if (!this._isValidFolderName()) return; DBusUtils.NautilusFileOperationsProxy.RenameFileRemote( this._fileItem.file.get_uri(), this._entry.text.trim(), (result, error) => { if (error) throw new Error('Error renaming file: ' + error.message); } ); this.activate(Clutter.get_current_event()); } }); var RenamePopup = class RenameFolderMenu extends PopupMenu.PopupMenu { constructor(fileItem) { super(fileItem, 0.5, St.Side.TOP); this.actor.add_style_class_name('rename-popup'); // We want to keep the item hovered while the menu is up this.blockSourceEvents = true; let menuItem = new RenamePopupMenuItem(fileItem); this.addMenuItem(menuItem); if (this.focusActor !== undefined) { // Focus the text entry on menu pop-up, works starting 3.34.1 this.focusActor = menuItem; } else { this.connect('open-state-changed', (_menu, state) => { if (state) this._openId = GLib.idle_add(GLib.PRIORITY_DEFAULT, () => { delete this._openId; menuItem.grab_key_focus() }); else if (this._openId) GLib.source_remove(this._openId); }); } // Chain our visibility and lifecycle to that of the fileItem source this._fileItemMappedId = fileItem.connect('notify::mapped', () => { if (!fileItem.mapped) this.close(); }); fileItem.connect('destroy', () => { fileItem.disconnect(this._fileItemMappedId); this.destroy(); }); Main.uiGroup.add_actor(this.actor); } };
Save
Cancel