// extension.js // GPLv3 const Clutter = imports.gi.Clutter; const Gio = imports.gi.Gio; const St = imports.gi.St; const Main = imports.ui.main; const AppDisplay = imports.ui.appDisplay; const PopupMenu = imports.ui.popupMenu; const Meta = imports.gi.Meta; const Mainloop = imports.mainloop; const ExtensionUtils = imports.misc.extensionUtils; const Me = ExtensionUtils.getCurrentExtension(); const Convenience = Me.imports.convenience; const AppfolderDialog = Me.imports.appfolderDialog; const DragAndDrop = Me.imports.dragAndDrop; const Gettext = imports.gettext.domain('appfolders-manager'); const _ = Gettext.gettext; let FOLDER_SCHEMA; let FOLDER_LIST; let INIT_TIME; function init () { Convenience.initTranslations(); INIT_TIME = getTimeStamp(); } function getTimeStamp () { let today = new Date(); let str = today.getDate() + '' + today.getHours() + '' + today.getMinutes() + '' + today.getSeconds(); return parseInt(str); } //------------------------------------------------------------------------------ /* do not edit this section */ function injectToFunction(parent, name, func) { let origin = parent[name]; parent[name] = function() { let ret; ret = origin.apply(this, arguments); if (ret === undefined) ret = func.apply(this, arguments); return ret; } return origin; } function removeInjection(object, injection, name) { if (injection[name] === undefined) delete object[name]; else object[name] = injection[name]; } var injections=[]; //------------------------------------------------------------------------------ /* this function injects items (1 or 2 submenus) in AppIconMenu's _redisplay method. */ function injectionInAppsMenus() { injections['_redisplay'] = injectToFunction(AppDisplay.AppIconMenu.prototype, '_redisplay', function() { if (Main.overview.viewSelector.getActivePage() == 2 || Main.overview.viewSelector.getActivePage() == 3) { //ok } else { return; } this._appendSeparator(); //TODO injecter ailleurs dans le menu? let mainAppView = Main.overview.viewSelector.appDisplay._views[1].view; FOLDER_LIST = FOLDER_SCHEMA.get_strv('folder-children'); //------------------------------------------------------------------ let addto = new PopupMenu.PopupSubMenuMenuItem(_("Add to")); let newAppFolder = new PopupMenu.PopupMenuItem('+ ' + _("New AppFolder")); newAppFolder.connect('activate', () => { this._source._menuManager._grabHelper.ungrab({ actor: this.actor }); // XXX broken scrolling ?? // We can't popdown the folder immediately because the // AppDisplay.AppFolderPopup.popdown() method tries to ungrab // the global focus from the folder's popup actor, which isn't // having the focus since the menu is still open. Menus' animation // last ~0.25s so we will wait 0.30s before doing anything. let a = Mainloop.timeout_add(300, () => { if (mainAppView._currentPopup) { mainAppView._currentPopup.popdown(); } createNewFolder(this._source); mainAppView._redisplay(); Mainloop.source_remove(a); }); }); addto.menu.addMenuItem(newAppFolder); for (var i = 0 ; i < FOLDER_LIST.length ; i++) { let _folder = FOLDER_LIST[i]; let shouldShow = !isInFolder( this._source.app.get_id(), _folder ); let iFolderSchema = folderSchema(_folder); let item = new PopupMenu.PopupMenuItem( AppDisplay._getFolderName(iFolderSchema) ); if ( Convenience.getSettings('org.gnome.shell.extensions.appfolders-manager').get_boolean('debug') ) { shouldShow = true; //TODO ??? et l'exclusion ? } if(shouldShow) { item.connect('activate', () => { this._source._menuManager._grabHelper.ungrab({ actor: this.actor }); // XXX broken scrolling ?? // We can't popdown the folder immediatly because the // AppDisplay.AppFolderPopup.popdown() method tries to // ungrab the global focus from the folder's popup actor, // which isn't having the focus since the menu is still // open. Menus' animation last ~0.25s so we will wait 0.30s // before doing anything. let a = Mainloop.timeout_add(300, () => { if (mainAppView._currentPopup) { mainAppView._currentPopup.popdown(); } addToFolder(this._source, _folder); mainAppView._redisplay(); Mainloop.source_remove(a); }); }); addto.menu.addMenuItem(item); } } this.addMenuItem(addto); //---------------------------------------------------------------------- let removeFrom = new PopupMenu.PopupSubMenuMenuItem(_("Remove from")); let shouldShow2 = false; for (var i = 0 ; i < FOLDER_LIST.length ; i++) { let _folder = FOLDER_LIST[i]; let appId = this._source.app.get_id(); let shouldShow = isInFolder(appId, _folder); let iFolderSchema = folderSchema(_folder); let item = new PopupMenu.PopupMenuItem( AppDisplay._getFolderName(iFolderSchema) ); if ( Convenience.getSettings('org.gnome.shell.extensions.appfolders-manager').get_boolean('debug') ) { shouldShow = true; //FIXME ??? et l'exclusion ? } if(shouldShow) { item.connect('activate', () => { this._source._menuManager._grabHelper.ungrab({ actor: this.actor }); // XXX broken scrolling ?? // We can't popdown the folder immediatly because the // AppDisplay.AppFolderPopup.popdown() method tries to // ungrab the global focus from the folder's popup actor, // which isn't having the focus since the menu is still // open. Menus' animation last ~0.25s so we will wait 0.30s // before doing anything. let a = Mainloop.timeout_add(300, () => { if (mainAppView._currentPopup) { mainAppView._currentPopup.popdown(); } removeFromFolder(appId, _folder); mainAppView._redisplay(); Mainloop.source_remove(a); }); }); removeFrom.menu.addMenuItem(item); shouldShow2 = true; } } if (shouldShow2) { this.addMenuItem(removeFrom); } }); } //------------------------------------------------ function injectionInIcons() { // Right-click on a FolderIcon launches a new AppfolderDialog AppDisplay.FolderIcon = class extends AppDisplay.FolderIcon { constructor (id, path, parentView) { super(id, path, parentView); if (!this.isCustom) { this.actor.connect('button-press-event', this._onButtonPress.bind(this)); } this.isCustom = true; } _onButtonPress (actor, event) { let button = event.get_button(); if (button == 3) { let tmp = new Gio.Settings({ schema_id: 'org.gnome.desktop.app-folders.folder', path: '/org/gnome/desktop/app-folders/folders/' + this.id + '/' }); let dialog = new AppfolderDialog.AppfolderDialog(tmp, null, this.id); dialog.open(); } return Clutter.EVENT_PROPAGATE; } }; // Dragging an AppIcon triggers the DND mode AppDisplay.AppIcon = class extends AppDisplay.AppIcon { constructor (app, params) { super(app, params); if (!this.isCustom) { this._draggable.connect('drag-begin', this.onDragBeginExt.bind(this)); this._draggable.connect('drag-cancelled', this.onDragCancelledExt.bind(this)); this._draggable.connect('drag-end', this.onDragEndExt.bind(this)); } this.isCustom = true; } onDragBeginExt () { if (Main.overview.viewSelector.getActivePage() != 2) { return; } this._removeMenuTimeout(); // why ? Main.overview.beginItemDrag(this); DragAndDrop.OVERLAY_MANAGER.on_drag_begin(); } onDragEndExt () { Main.overview.endItemDrag(this); DragAndDrop.OVERLAY_MANAGER.on_drag_end(); } onDragCancelledExt () { Main.overview.cancelledItemDrag(this); DragAndDrop.OVERLAY_MANAGER.on_drag_cancelled(); } }; } //------------------------------------------------------------------------------ //---------------------------------- Generic ----------------------------------- //--------------------------------- functions ---------------------------------- //------------------------------------------------------------------------------ /* These functions perform the requested actions but do not care about popdowning * open menu/open folder, nor about hiding/showing/activating dropping areas, nor * about redisplaying the view. */ function removeFromFolder (app_id, folder_id) { let folder_schema = folderSchema(folder_id); if ( isInFolder(app_id, folder_id) ) { let pastContent = folder_schema.get_strv('apps'); let presentContent = []; for(var i=0; i { let tmp = []; FOLDER_LIST = FOLDER_SCHEMA.get_strv('folder-children'); for(var j=0;j < FOLDER_LIST.length;j++){ if(FOLDER_LIST[j] != folder_id) { tmp.push(FOLDER_LIST[j]); } } FOLDER_LIST = tmp; FOLDER_SCHEMA.set_strv('folder-children', FOLDER_LIST); // ?? XXX (ne fonctionne pas mieux hors du meta.later_add) if ( Convenience.getSettings('org.gnome.shell.extensions.appfolders-manager').get_boolean('total-deletion') ) { let folder_schema = folderSchema (folder_id); folder_schema.reset('apps'); // génère un bug volumineux ? folder_schema.reset('categories'); // génère un bug volumineux ? folder_schema.reset('excluded-apps'); // génère un bug volumineux ? folder_schema.reset('name'); // génère un bug volumineux ? } }); return true; } //------------------------------------------------------------------------------ function mergeFolders (folder_staying_id, folder_dying_id) { //unused XXX let folder_dying_schema = folderSchema (folder_dying_id); let folder_staying_schema = folderSchema (folder_staying_id); let newerContent = folder_dying_schema.get_strv('categories'); let presentContent = folder_staying_schema.get_strv('categories'); for(var i=0;i 0) { folder_schema.set_strv('excluded-apps', presentExcluded); } //actually add the app let content = folder_schema.get_strv('apps'); content.push(id); folder_schema.set_strv('apps', content); //XXX verbose errors //update icons in the ugliest possible way let icons = Main.overview.viewSelector.appDisplay._views[1].view.folderIcons; for (let i=0; i 105) { Main.overview.viewSelector.appDisplay._views[1].view._redisplay(); } } function disable() { AppDisplay.FolderIcon.prototype._onButtonPress = null; AppDisplay.FolderIcon.prototype.popupMenu = null; removeInjection(AppDisplay.AppIconMenu.prototype, injections, '_redisplay'); // Overwrite my shit for FolderIcon AppDisplay.FolderIcon = class extends AppDisplay.FolderIcon { _onButtonPress (actor, event) { return Clutter.EVENT_PROPAGATE; } }; // Overwrite my shit for AppIcon AppDisplay.AppIcon = class extends AppDisplay.AppIcon { onDragBeginExt () {} onDragEndExt () {} onDragCancelledExt () {} }; DragAndDrop.OVERLAY_MANAGER.destroy(); } //------------------------------------------------------------------------------