BossBey File Manager
PHP:
8.2.28
OS:
Linux
User:
www-data
Root
/
usr
/
share
/
cinnamon
/
js
/
ui
📤 Upload
📝 New File
📁 New Folder
Close
Editing: placesManager.js
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- const GLib = imports.gi.GLib; const Gio = imports.gi.Gio; const Cinnamon = imports.gi.Cinnamon; const Lang = imports.lang; const Mainloop = imports.mainloop; const Signals = imports.signals; const St = imports.gi.St; const Main = imports.ui.main; const MessageTray = imports.ui.messageTray; const Params = imports.misc.params; const Search = imports.ui.search; const Util = imports.misc.util; const DEBUG = false; /** * Represents a place object, which is most normally a bookmark entry, * a mount/volume, or a special place like the Home Folder, Computer, and Network. * * @name: String title * @iconFactory: A JavaScript callback which will create an icon texture given a size parameter * @launch: A JavaScript callback to launch the entry */ function PlaceInfo(id, name, iconFactory, launch) { this._init(id, name, iconFactory, launch); } PlaceInfo.prototype = { _init: function(id, name, iconFactory, launch) { this.id = id; this.idDecoded = decodeURIComponent(this.id); this.name = name; this._lowerName = name.toLowerCase(); this.iconFactory = iconFactory; this.launch = launch; }, matchTerms: function(terms) { let mtype = Search.MatchType.NONE; for (let i = 0; i < terms.length; i++) { let term = terms[i]; let idx = this._lowerName.indexOf(term); if (idx == 0) { mtype = Search.MatchType.PREFIX; } else if (idx > 0) { if (mtype == Search.MatchType.NONE) mtype = Search.MatchType.SUBSTRING; } else { return Search.MatchType.NONE; } } return mtype; }, isRemovable: function() { return false; } }; // Helper function to translate launch parameters into a GAppLaunchContext function _makeLaunchContext(params) { params = Params.parse(params, { workspace: -1, timestamp: 0 }); let launchContext = global.create_app_launch_context(); if (params.workspace != -1) launchContext.set_desktop(params.workspace); if (params.timestamp != 0) launchContext.set_timestamp(params.timestamp); return launchContext; } function PlaceDeviceInfo(mount) { this._init(mount); } PlaceDeviceInfo.prototype = { __proto__: PlaceInfo.prototype, _init: function(mount) { this._mount = mount; this.name = mount.get_name(); this._lowerName = this.name.toLowerCase(); this.id = 'mount:' + mount.get_root().get_uri(); this.idDecoded = decodeURIComponent(this.id); this.busyWaitId = 0; this.destroySignalId = 0; this.busyNotification = null; }, iconFactory: function(size) { let icon = this._mount.get_icon(); return new St.Icon( { gicon: icon, icon_size: size }); }, launch: function(params) { Gio.app_info_launch_default_for_uri(this._mount.get_root().get_uri(), _makeLaunchContext(params)); }, isRemovable: function() { return this._mount.can_unmount(); }, remove: function() { if (this.busyWaitId > 0) { Mainloop.source_remove(this.busyWaitId); this.busyWaitId = 0; } if (this.busyNotification != null) { this.busyNotification.destroy(); } this._tryRemove(); }, _tryRemove: function() { if (!this.isRemovable()) return; let mountOp = new Gio.MountOperation(this._mount); let drive = this._mount.get_drive(); let volume = this._mount.get_volume(); if (drive && drive.get_start_stop_type() == Gio.DriveStartStopType.SHUTDOWN && drive.can_stop()) { drive.stop(0, mountOp, null, Lang.bind(this, this._stopFinish)); } else { if (drive && drive.can_eject()) drive.eject_with_operation(0, mountOp, null, Lang.bind(this, this._ejectFinish, true)); else if (volume && volume.can_eject()) volume.eject_with_operation(0, mountOp, null, Lang.bind(this, this._ejectFinish, false)); else if (this._mount.can_eject()) this._mount.eject_with_operation(0, mountOp, null, Lang.bind(this, this._ejectFinish, false)); else if (this._mount.can_unmount()) this._mount.unmount_with_operation(0, mountOp, null, Lang.bind(this, this._removeFinish)); } this.busyWaitId = 0; return false; }, _sendNotification: function(msg1, msg2 = null, withButton = false, persistent = false) { if (Main.messageTray) { if (persistent && this.busyNotification != null) { return; } if (!persistent && this.busyNotification) { this.busyNotification.destroy(); this.busyNotification = null; } let source = new MessageTray.SystemNotificationSource(); Main.messageTray.add(source); let notification = new MessageTray.Notification(source, msg1, msg2); notification.setTransient(true); notification.setUrgency(persistent ? MessageTray.Urgency.CRITICAL : MessageTray.Urgency.NORMAL); if (withButton) { notification.addButton('system-undo', _("Retry")); notification.connect('action-invoked', Lang.bind(this, this.remove)); } source.notify(notification); if (persistent) { this.busyNotification = notification; this.destroySignalId = notification.connect("destroy", () => { this.busyNotification.disconnect(this.destroySignalId); this.busyNotification = null; this.destroySignalId = 0; }) } } else { if (msg2) global.log(msg1 + ': ' + msg2); else global.log(msg1); } }, _stopFinish: function(drive, res) { if (DEBUG) global.log("PlacesManager: **_stopFinish**"); let driveName = drive.get_name(); // Ex: USB Flash Drive let unixDevice = drive.get_identifier('unix-device'); // Ex: /dev/sdc let msg1 = _("%s (%s) has just been stopped.").format(driveName, this.name); let msg2 = _("Device %s can be turned off, if necessary.").format(unixDevice); let btn = false; // Show the 'Retry' button? try { drive.stop_finish(res); } catch(e) { if (e.code == Gio.IOErrorEnum.BUSY) { msg1 = _("Device %s is busy, please wait.".format(drive.get_name())); msg2 = _("Do not disconnect or data loss may occur."); this._sendNotification(msg1, msg2, false, true); this.busyWaitId = Mainloop.timeout_add_seconds(2, ()=>this._tryRemove()); return; } btn = true; msg1 = _("Unable to stop the drive %s (%s)").format(drive.get_name(), this.name); msg2 = e.message; } if (DEBUG) global.log(msg1 + ": " + msg2); this._sendNotification(msg1, msg2, btn); }, _ejectFinish: function(source, res, is_drive) { if (DEBUG) global.log("PlacesManager: **_ejectFinish**"); let msg1; let msg2 = null; let btn = false; if (is_drive) { let driveName = source.get_name(); // Ex: USB Flash Drive let unixDevice = source.get_identifier('unix-device'); // Ex: /dev/sdc msg1 = _("%s (%s) can be safely unplugged.").format(driveName, this.name); msg2 = _("Device %s can be removed.").format(unixDevice); } else { msg1 = _("%s (%s) has just been ejected.").format(source.get_name(), this.name); } try { source.eject_with_operation_finish(res); } catch(e) { if (e.code == Gio.IOErrorEnum.BUSY) { msg1 = _("Device %s is busy, please wait.".format(source.get_name())); msg2 = _("Do not remove or data loss may occur."); this._sendNotification(msg1, msg2, false, true); this.busyWaitId = Mainloop.timeout_add_seconds(2, ()=>this._tryRemove()); return; } btn = true; msg1 = _("Unable to eject the drive %s (%s)").format(source.get_name(), this.name); msg2 = e.message; } if (DEBUG) global.log(msg1 + ": " + msg2); this._sendNotification(msg1, msg2, btn); }, _removeFinish: function(o, res, data) { if (DEBUG) global.log("PlacesManager: **_removeFinish**"); let msg1 = _("Successfully unmounted %s (%s)").format(o.get_name(), this.name); let msg2 = null; let btn = false; // 'this._mount.can_eject()' seems to be ever false. Thus, only the 'else' part will be used. // If no issues are reported, these 19 lines of code commented below can be deleted. //~ if (this._mount.can_eject()) { //~ msg1 = _("%s (%s) can be safely unplugged").format(o.get_name(), this.name); //~ msg2 = _("Device can be removed"); //~ try { //~ this._mount.eject_with_operation_finish(res); //~ } catch(e) { //~ btn = true; //~ msg1 = _("Failed to eject %s (%s)").format(o.get_name(), this.name); //~ msg2 = e.message; //~ } //~ } else { //~ try { //~ this._mount.unmount_with_operation_finish(res); //~ } catch(e) { //~ btn = true; //~ msg1 = _("Failed to unmount %s (%s)").format(o.get_name(), this.name); //~ msg2 = e.message; //~ } //~ } // <--Beginning of the code replacing the 19 lines above: try { this._mount.unmount_with_operation_finish(res); } catch(e) { if (e.code == Gio.IOErrorEnum.BUSY) { msg1 = _("Device %s is busy, please wait.".format(o.get_name())); msg2 = _("Do not disconnect or data loss may occur."); this._sendNotification(msg1, msg2, false, true); this.busyWaitId = Mainloop.timeout_add_seconds(2, ()=>this._tryRemove()); return; } btn = true; msg1 = _("Failed to unmount %s (%s)").format(o.get_name(), this.name); msg2 = e.message; } // End of this code.--> if (DEBUG) global.log(msg1 + ": " + msg2); this._sendNotification(msg1, msg2, btn); } }; function PlacesManager() { this._init(); } PlacesManager.prototype = { _init: function() { this._defaultPlaces = []; this._mounts = []; this._bookmarks = []; let homeFile = Gio.file_new_for_path (GLib.get_home_dir()); let homeUri = homeFile.get_uri(); let homeLabel = Cinnamon.util_get_label_for_uri (homeUri); let homeIcon = Cinnamon.util_get_icon_for_uri (homeUri); this._home = new PlaceInfo('special:home', homeLabel, function(size) { return new St.Icon({ gicon: homeIcon, icon_size: size }); }, function(params) { Gio.app_info_launch_default_for_uri(homeUri, _makeLaunchContext(params)); }); let desktopPath = GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_DESKTOP); let desktopFile = Gio.file_new_for_path (desktopPath); let desktopUri = desktopFile.get_uri(); let desktopLabel = Cinnamon.util_get_label_for_uri (desktopUri); let desktopIcon = Cinnamon.util_get_icon_for_uri (desktopUri); this._desktopMenu = new PlaceInfo('special:desktop', desktopLabel, function(size) { return new St.Icon({ gicon: desktopIcon, icon_size: size }); }, function(params) { Gio.app_info_launch_default_for_uri(desktopUri, _makeLaunchContext(params)); }); this._defaultPlaces.push(this._home); this._defaultPlaces.push(this._desktopMenu); /* * Show devices, code more or less ported from nemo-places-sidebar.c */ this._volumeMonitor = Gio.VolumeMonitor.get(); this._volumeMonitor.connect('volume-added', Lang.bind(this, this._onVolumeAdded)); this._volumeMonitor.connect('volume-removed',Lang.bind(this, this._onVolumeRemoved)); this._volumeMonitor.connect('volume-changed', Lang.bind(this, this._onVolumeChanged)); this._volumeMonitor.connect('mount-added', Lang.bind(this, this._onMountAdded)); this._volumeMonitor.connect('mount-removed', Lang.bind(this, this._onMountRemoved)); this._volumeMonitor.connect('mount-changed', Lang.bind(this, this._onMountChanged)); this._volumeMonitor.connect('drive-connected', Lang.bind(this, this._onDriveConnected)); this._volumeMonitor.connect('drive-disconnected', Lang.bind(this, this._onDriveDisconnected)); this._volumeMonitor.connect('drive-changed', Lang.bind(this, this._onDriveChanged)); this._deviceUpdateAwaiting = false; this._updateDevices(); this._bookmarksFile = null; this._bookmarksPath = null; let bookmarksPath3 = GLib.build_filenamev([GLib.get_user_config_dir(), 'gtk-3.0', 'bookmarks']); let bookmarksFile3 = Gio.file_new_for_path(bookmarksPath3); if (!bookmarksFile3.query_exists(null)) { let bookmarksPath2 = GLib.build_filenamev([GLib.get_home_dir(), '.gtk-bookmarks']); let bookmarksFile2 = Gio.file_new_for_path(bookmarksPath2); if (bookmarksFile2.query_exists(null)) { this._bookmarksFile = bookmarksFile2; this._bookmarksPath = bookmarksPath2; } } if (this._bookmarksFile === null) { this._bookmarksPath = bookmarksPath3; this._bookmarksFile = bookmarksFile3; } this._bookmarkTimeoutId = 0; this.monitor = this._bookmarksFile.monitor_file(Gio.FileMonitorFlags.NONE, null); this.monitor.connect('changed', () => { if (this._bookmarkTimeoutId > 0) return; /* Defensive event compression */ this._bookmarkTimeoutId = Mainloop.timeout_add(100, () => { this._bookmarkTimeoutId = 0; this._reloadBookmarks(); return GLib.SOURCE_REMOVE; }); }); this._reloadBookmarks(); }, // Mounting a device triggers a lot of different events, wait 3 seconds and try to only call this._updateDevices() once _updateDevicesAsync: function() { if (this._deviceUpdateAwaiting == false) { this._deviceUpdateAwaiting = true; Mainloop.timeout_add(3000, Lang.bind(this, function () { this._updateDevices(); })); } }, _onVolumeAdded: function() { this._updateDevicesAsync(); }, _onVolumeRemoved: function() { this._updateDevicesAsync(); }, _onVolumeChanged: function() { this._updateDevicesAsync(); }, _onMountAdded: function() { this._updateDevicesAsync(); }, _onMountRemoved: function() { this._updateDevicesAsync(); }, _onMountChanged: function() { this._updateDevicesAsync(); }, _onDriveConnected: function() { Main.soundManager.play('plug'); this._updateDevicesAsync(); }, _onDriveDisconnected: function() { Main.soundManager.play('unplug'); this._updateDevicesAsync(); }, _onDriveChanged: function() { this._updateDevicesAsync(); }, _updateDevices: function() { this._deviceUpdateAwaiting = false; if (DEBUG) global.log("PlacesManager: Updating devices"); this._mounts = []; /* first go through all connected drives */ let drives = this._volumeMonitor.get_connected_drives(); for (let i = 0; i < drives.length; i++) { let volumes = drives[i].get_volumes(); for(let j = 0; j < volumes.length; j++) { let mount = volumes[j].get_mount(); if(mount != null) { this._addMount(mount); } } } /* add all volumes that is not associated with a drive */ let volumes = this._volumeMonitor.get_volumes(); for(let i = 0; i < volumes.length; i++) { if(volumes[i].get_drive() != null) continue; let mount = volumes[i].get_mount(); if(mount != null) { this._addMount(mount); } } /* add mounts that have no volume (/etc/mtab mounts, ftp, sftp,...) */ let mounts = this._volumeMonitor.get_mounts(); for(let i = 0; i < mounts.length; i++) { if(mounts[i].is_shadowed()) continue; if(mounts[i].get_volume()) continue; this._addMount(mounts[i]); } /* We emit two signals, one for a generic 'all places' update * and the other for one specific to mounts. We do this because * clients like PlaceDisplay may only care about places in general * being updated while clients like DashPlaceDisplay care which * specific type of place got updated. */ this.emit('mounts-updated'); this.emit('places-updated'); }, _reloadBookmarks: function() { let had_bookmarks = this._bookmarks.length > 0; this._bookmarks = []; if (!this._bookmarksFile.query_exists(null)) { if (had_bookmarks) { this.emit('bookmarks-updated'); this.emit('places-updated'); } return; } let bookmarksContent = Cinnamon.get_file_contents_utf8_sync(this._bookmarksPath); let bookmarks = bookmarksContent.split('\n'); let bookmarksToLabel = {}; let bookmarksOrder = []; for (let i = 0; i < bookmarks.length; i++) { let bookmarkLine = bookmarks[i]; let components = bookmarkLine.split(' '); let bookmark = components[0]; if (bookmark in bookmarksToLabel) continue; let label = null; if (components.length > 1) label = components.slice(1).join(' '); bookmarksToLabel[bookmark] = label; bookmarksOrder.push(bookmark); } for (let i = 0; i < bookmarksOrder.length; i++) { let bookmark = bookmarksOrder[i]; let label = bookmarksToLabel[bookmark]; let file = Gio.file_new_for_uri(bookmark); if (label == null) label = Cinnamon.util_get_label_for_uri(bookmark); if (label == null) continue; let item; if (file.query_exists(null)) { let icon = Cinnamon.util_get_icon_for_uri(bookmark); item = new PlaceInfo('bookmark:' + bookmark, label, function(size) { return new St.Icon({ gicon: icon, icon_size: size }); }, function(params) { Gio.app_info_launch_default_for_uri(bookmark, _makeLaunchContext(params)); }); } else { // Assume the bookmark is an unmounted network location // try to mount and open by the default file manager let icon = Gio.ThemedIcon.new('network-workgroup'); item = new PlaceInfo('bookmark:' + bookmark, label, function(size) { return new St.Icon({ gicon: icon, icon_size: size }); }, function(params) { let fileapp = Gio.app_info_get_default_for_type('inode/directory', true); if (fileapp) { fileapp.launch_uris([bookmark], _makeLaunchContext(params)); } }); } this._bookmarks.push(item); } /* See comment in _updateDevices for explanation why there are two signals. */ this.emit('bookmarks-updated'); this.emit('places-updated'); }, _addMount: function(mount) { let devItem = new PlaceDeviceInfo(mount); this._mounts.push(devItem); }, getAllPlaces: function () { return [...this.getDefaultPlaces(), ...this.getBookmarks(), ...this.getMounts()]; }, getDefaultPlaces: function () { return this._defaultPlaces; }, getBookmarks: function () { return this._bookmarks; }, getMounts: function () { return this._mounts; }, _lookupIndexById: function(sourceArray, id) { for (let i = 0; i < sourceArray.length; i++) { let place = sourceArray[i]; if (place.id == id) return i; } return -1; }, lookupPlaceById: function(id) { let colonIdx = id.indexOf(':'); let type = id.substring(0, colonIdx); let sourceArray = null; if (type == 'special') sourceArray = this._defaultPlaces; else if (type == 'mount') sourceArray = this._mounts; else if (type == 'bookmark') sourceArray = this._bookmarks; return sourceArray[this._lookupIndexById(sourceArray, id)]; }, _removeById: function(sourceArray, id) { sourceArray.splice(this._lookupIndexById(sourceArray, id), 1); } }; Signals.addSignalMethods(PlacesManager.prototype); function PlaceSearchProvider() { this._init(); } PlaceSearchProvider.prototype = { __proto__: Search.SearchProvider.prototype, _init: function() { Search.SearchProvider.prototype._init.call(this, "PLACES & DEVICES"); }, getResultMeta: function(resultId) { let placeInfo = Main.placesManager.lookupPlaceById(resultId); if (!placeInfo) return null; return { 'id': resultId, 'name': placeInfo.name, 'createIcon': function(size) { return placeInfo.iconFactory(size); } }; }, activateResult: function(id, params) { let placeInfo = Main.placesManager.lookupPlaceById(id); placeInfo.launch(params); }, _compareResultMeta: function (idA, idB) { let infoA = Main.placesManager.lookupPlaceById(idA); let infoB = Main.placesManager.lookupPlaceById(idB); return infoA.name.localeCompare(infoB.name); }, _searchPlaces: function(places, terms) { let multiplePrefixResults = []; let prefixResults = []; let multipleSubstringResults = []; let substringResults = []; terms = terms.map(String.toLowerCase); for (let i = 0; i < places.length; i++) { let place = places[i]; let mtype = place.matchTerms(terms); if (mtype == Search.MatchType.MULTIPLE_PREFIX) multiplePrefixResults.push(place.id); else if (mtype == Search.MatchType.PREFIX) prefixResults.push(place.id); else if (mtype == Search.MatchType.MULTIPLE_SUBSTRING) multipleSubstringResults.push(place.id); else if (mtype == Search.MatchType.SUBSTRING) substringResults.push(place.id); } multiplePrefixResults.sort(this._compareResultMeta); prefixResults.sort(this._compareResultMeta); multipleSubstringResults.sort(this._compareResultMeta); substringResults.sort(this._compareResultMeta); return [...multiplePrefixResults, ...prefixResults, ...multipleSubstringResults, ...substringResults]; }, getInitialResultSet: function(terms) { let places = Main.placesManager.getAllPlaces(); return this._searchPlaces(places, terms); }, getSubsearchResultSet: function(previousResults, terms) { let places = previousResults.map(function (id) { return Main.placesManager.lookupPlaceById(id); }); return this._searchPlaces(places, terms); } };
Save
Cancel