Merging upstream version 20250212.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
ad845f6499
commit
0a7a7dc344
192 changed files with 32577 additions and 3262 deletions
|
@ -0,0 +1,316 @@
|
|||
/**
|
||||
* Vertical Workspaces
|
||||
* recentFilesSearchProvider.js
|
||||
*
|
||||
* @author GdH <G-dH@github.com>
|
||||
* @copyright 2022 - 2024
|
||||
* @license GPL-3.0
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import GLib from 'gi://GLib';
|
||||
import St from 'gi://St';
|
||||
import Gio from 'gi://Gio';
|
||||
|
||||
import * as Main from 'resource:///org/gnome/shell/ui/main.js';
|
||||
|
||||
let Me;
|
||||
let opt;
|
||||
// gettext
|
||||
let _;
|
||||
|
||||
// prefix helps to eliminate results from other search providers
|
||||
// so it needs to be something less common
|
||||
// needs to be accessible from vw module
|
||||
export const PREFIX = 'fq//';
|
||||
const ID = 'recent-files';
|
||||
|
||||
export const RecentFilesSearchProviderModule = class {
|
||||
// export for other modules
|
||||
static _PREFIX = PREFIX;
|
||||
constructor(me) {
|
||||
Me = me;
|
||||
opt = Me.opt;
|
||||
_ = Me.gettext;
|
||||
|
||||
this._firstActivation = true;
|
||||
this.moduleEnabled = false;
|
||||
this._recentFilesSearchProvider = null;
|
||||
this._enableTimeoutId = 0;
|
||||
}
|
||||
|
||||
cleanGlobals() {
|
||||
Me = null;
|
||||
opt = null;
|
||||
_ = null;
|
||||
}
|
||||
|
||||
update(reset) {
|
||||
this.moduleEnabled = opt.get('recentFilesSearchProviderModule');
|
||||
|
||||
reset = reset || !this.moduleEnabled;
|
||||
|
||||
if (reset && !this._firstActivation) {
|
||||
this._disableModule();
|
||||
} else if (!reset) {
|
||||
this._firstActivation = false;
|
||||
this._activateModule();
|
||||
}
|
||||
if (reset && this._firstActivation)
|
||||
console.debug(' RecentFilesSearchProviderModule - Keeping untouched');
|
||||
}
|
||||
|
||||
_activateModule() {
|
||||
// delay because Fedora had problem to register a new provider soon after Shell restarts
|
||||
this._enableTimeoutId = GLib.timeout_add(
|
||||
GLib.PRIORITY_DEFAULT,
|
||||
2000,
|
||||
() => {
|
||||
if (!this._recentFilesSearchProvider) {
|
||||
this._recentFilesSearchProvider = new RecentFilesSearchProvider();
|
||||
this._registerProvider(this._recentFilesSearchProvider);
|
||||
}
|
||||
this._enableTimeoutId = 0;
|
||||
return GLib.SOURCE_REMOVE;
|
||||
}
|
||||
);
|
||||
|
||||
console.debug(' RecentFilesSearchProviderModule - Activated');
|
||||
}
|
||||
|
||||
_disableModule() {
|
||||
if (this._recentFilesSearchProvider) {
|
||||
this._unregisterProvider(this._recentFilesSearchProvider);
|
||||
this._recentFilesSearchProvider = null;
|
||||
}
|
||||
if (this._enableTimeoutId) {
|
||||
GLib.source_remove(this._enableTimeoutId);
|
||||
this._enableTimeoutId = 0;
|
||||
}
|
||||
|
||||
console.debug(' RecentFilesSearchProviderModule - Disabled');
|
||||
}
|
||||
|
||||
_registerProvider(provider) {
|
||||
const searchResults = Main.overview.searchController._searchResults;
|
||||
provider.searchInProgress = false;
|
||||
|
||||
searchResults._providers.push(provider);
|
||||
|
||||
// create results display and add it to the _content
|
||||
searchResults._ensureProviderDisplay.bind(searchResults)(provider);
|
||||
}
|
||||
|
||||
_unregisterProvider(provider) {
|
||||
const searchResults = Main.overview.searchController._searchResults;
|
||||
searchResults._unregisterProvider(provider);
|
||||
}
|
||||
};
|
||||
|
||||
class RecentFilesSearchProvider {
|
||||
constructor() {
|
||||
this.id = ID;
|
||||
const appId = 'org.gnome.Nautilus.desktop';
|
||||
|
||||
// A real appInfo created from a commandline has often issues with overriding get_id() method, so we use dict instead
|
||||
this.appInfo = {
|
||||
get_id: () => appId,
|
||||
get_name: () => _('Recent Files'),
|
||||
get_icon: () => Gio.icon_new_for_string('focus-windows-symbolic'),
|
||||
should_show: () => true,
|
||||
get_commandline: () => '/usr/bin/nautilus -w recent:///',
|
||||
launch: () => {},
|
||||
};
|
||||
|
||||
this.canLaunchSearch = true;
|
||||
this.isRemoteProvider = false;
|
||||
|
||||
this._recentFilesManager = new RecentFilesManager();
|
||||
}
|
||||
|
||||
getInitialResultSet(terms/* , cancellable*/) {
|
||||
const rfm = this._recentFilesManager;
|
||||
rfm.loadFromFile();
|
||||
|
||||
const uris = rfm.getUris();
|
||||
const dict = {};
|
||||
for (let uri of uris) {
|
||||
dict[uri] = {};
|
||||
dict[uri]['uri'] = uri;
|
||||
dict[uri]['path'] = rfm.getPath(uri);
|
||||
dict[uri]['filename'] = rfm.getDisplayName(uri);
|
||||
dict[uri]['dir'] = rfm.getDirPath(uri);
|
||||
dict[uri]['age'] = rfm.getAge(uri);
|
||||
dict[uri]['appInfo'] = rfm.getDefaultAppAppInfo(uri);
|
||||
}
|
||||
this.files = dict;
|
||||
|
||||
return new Promise(resolve => resolve(this._getResultSet(terms)));
|
||||
}
|
||||
|
||||
_getResultSet(terms) {
|
||||
if (!terms[0].startsWith(PREFIX))
|
||||
return [];
|
||||
// do not modify original terms
|
||||
let termsCopy = [...terms];
|
||||
// search for terms without prefix
|
||||
termsCopy[0] = termsCopy[0].replace(PREFIX, '');
|
||||
|
||||
const candidates = Object.values(this.files);
|
||||
const _terms = [].concat(termsCopy);
|
||||
|
||||
const term = _terms.join(' ');
|
||||
|
||||
const results = [];
|
||||
let m;
|
||||
for (let file of candidates) {
|
||||
if (opt.SEARCH_FUZZY)
|
||||
m = Me.Util.fuzzyMatch(term, file.filename);
|
||||
else
|
||||
m = Me.Util.strictMatch(term, file.filename);
|
||||
|
||||
if (m !== -1)
|
||||
results.push(file);
|
||||
}
|
||||
|
||||
results.sort((a, b) => a.age > b.age);
|
||||
|
||||
const resultIds = results.map(item => item.uri);
|
||||
return resultIds;
|
||||
}
|
||||
|
||||
getResultMetas(resultIds/* , callback = null*/) {
|
||||
const metas = resultIds.map(id => this.getResultMeta(id));
|
||||
return new Promise(resolve => resolve(metas));
|
||||
}
|
||||
|
||||
getResultMeta(resultId) {
|
||||
const result = this.files[resultId];
|
||||
return {
|
||||
'id': resultId,
|
||||
'name': `${Math.floor(result.age)}: ${result.filename}`,
|
||||
'description': `${result.dir}`,
|
||||
'createIcon': size =>
|
||||
this._recentFilesManager.getDefaultAppIcon(resultId, size),
|
||||
};
|
||||
}
|
||||
|
||||
launchSearch(terms, timeStamp) {
|
||||
const appInfo = Gio.AppInfo.create_from_commandline('/usr/bin/nautilus -w recent:///', 'Nautilus', null);
|
||||
appInfo.launch([], global.create_app_launch_context(timeStamp, -1));
|
||||
|
||||
// unlike on 42, on 44 if a window with the same uri is already open it will not get focus/activation
|
||||
// Gio.app_info_launch_default_for_uri('recent:///', global.create_app_launch_context(timeStamp, -1));
|
||||
|
||||
// following solution for some reason ignores the recent:/// uri
|
||||
// this.appInfo.launch_uris(['recent:///'], global.create_app_launch_context(timeStamp, -1));
|
||||
}
|
||||
|
||||
activateResult(resultId, terms, timeStamp) {
|
||||
const uri = resultId;
|
||||
const context = global.create_app_launch_context(timeStamp, -1);
|
||||
if (Me.Util.isShiftPressed()) {
|
||||
Main.overview.toggle();
|
||||
this.appInfo.launch_uris([uri], context);
|
||||
} else if (Gio.app_info_launch_default_for_uri(uri, context)) {
|
||||
// update recent list after successful activation
|
||||
this._recentFilesManager.updateAdded(resultId);
|
||||
this._recentFilesManager.saveToFile();
|
||||
} else {
|
||||
this.appInfo.launch_uris([uri], context);
|
||||
}
|
||||
}
|
||||
|
||||
filterResults(results /* , maxResults*/) {
|
||||
// return results.slice(0, maxResults);
|
||||
return results.slice(0, 20);
|
||||
}
|
||||
|
||||
getSubsearchResultSet(previousResults, terms/* , cancellable*/) {
|
||||
return this.getInitialResultSet(terms);
|
||||
}
|
||||
}
|
||||
|
||||
class RecentFilesManager {
|
||||
constructor(path) {
|
||||
path = path ?? GLib.build_filenamev([GLib.get_user_data_dir(), 'recently-used.xbel']);
|
||||
this._recentlyUsedPath = path;
|
||||
this._bookmarks = new GLib.BookmarkFile();
|
||||
}
|
||||
|
||||
loadFromFile() {
|
||||
try {
|
||||
this._bookmarks.load_from_file(this._recentlyUsedPath);
|
||||
} catch (e) {
|
||||
if (!e.matches(GLib.BookmarkFileError, GLib.BookmarkFileError.FILE_NOT_FOUND))
|
||||
console.error(`Could not open recent files: ${e.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
saveToFile() {
|
||||
try {
|
||||
this._bookmarks.to_file(this._recentlyUsedPath);
|
||||
} catch (e) {
|
||||
if (!e.matches(GLib.BookmarkFileError, GLib.BookmarkFileError.FILE_NOT_FOUND))
|
||||
console.error(`Could not open recent files to save data: ${e.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
getUris() {
|
||||
return this._bookmarks.get_uris();
|
||||
}
|
||||
|
||||
getPath(uri) {
|
||||
// GLib.filename_from_uri() removes uri schema and converts string to utf-8
|
||||
return GLib.filename_from_uri(uri)[0]; // result is array
|
||||
}
|
||||
|
||||
getDisplayName(uri) {
|
||||
const path = this.getPath(uri);
|
||||
return GLib.filename_display_basename(path);
|
||||
}
|
||||
|
||||
getDirPath(uri) {
|
||||
const path = this.getPath(uri);
|
||||
const filename = this.getDisplayName(uri);
|
||||
return path.replace(`${filename}`, '');
|
||||
}
|
||||
|
||||
getMimeType(uri) {
|
||||
return this._bookmarks.get_mime_type(uri);
|
||||
}
|
||||
|
||||
getAdded(uri) {
|
||||
return this._bookmarks.get_added(uri);
|
||||
}
|
||||
|
||||
updateAdded(uri) {
|
||||
this._bookmarks.set_added_date_time(uri, GLib.DateTime.new_now_local());
|
||||
}
|
||||
|
||||
// age in days (float)
|
||||
getAge(uri) {
|
||||
return (Date.now() / 1000 - this._bookmarks.get_added(uri)) / 60 / 60 / 24;
|
||||
}
|
||||
|
||||
getDefaultAppAppInfo(uri) {
|
||||
const mimeType = this.getMimeType(uri);
|
||||
return Gio.AppInfo.get_default_for_type(mimeType, false);
|
||||
}
|
||||
|
||||
getDefaultAppIcon(uri, size) {
|
||||
let icon, gicon;
|
||||
|
||||
const appInfo = this.getDefaultAppAppInfo(uri);
|
||||
if (appInfo)
|
||||
gicon = appInfo.get_icon();
|
||||
|
||||
if (gicon)
|
||||
icon = new St.Icon({ gicon, icon_size: size });
|
||||
else
|
||||
icon = new St.Icon({ icon_name: 'icon-missing', icon_size: size });
|
||||
|
||||
return icon;
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue