Oreon-Lime-R2/arcmenu/arc-menu-27/menulayouts/baseMenuLayout.js

1128 lines
47 KiB
JavaScript

/*
* ArcMenu - A traditional application menu for GNOME 3
*
* ArcMenu Lead Developer and Maintainer
* Andrew Zaech https://gitlab.com/AndrewZaech
*
* ArcMenu Founder, Former Maintainer, and Former Graphic Designer
* LinxGem33 https://gitlab.com/LinxGem33 - (No Longer Active)
*
* 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 2 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 Me = imports.misc.extensionUtils.getCurrentExtension();
const {Clutter, GLib, Gio, GMenu, Gtk, Shell, St} = imports.gi;
const AppFavorites = imports.ui.appFavorites;
const appSys = Shell.AppSystem.get_default();
const ArcSearch = Me.imports.search;
const Constants = Me.imports.constants;
const Gettext = imports.gettext.domain(Me.metadata['gettext-domain']);
const Main = imports.ui.main;
const MenuLayouts = Me.imports.menulayouts;
const MW = Me.imports.menuWidgets;
const PlaceDisplay = Me.imports.placeDisplay;
const PopupMenu = imports.ui.popupMenu;
const { RecentFilesManager } = Me.imports.recentFilesManager;
const Utils = Me.imports.utils;
//This class handles the core functionality of all the menu layouts.
//Each menu layout extends this class.
var BaseLayout = class {
constructor(menuButton, layoutProperties){
this.menuButton = menuButton;
this._settings = menuButton._settings;
this.mainBox = menuButton.mainBox;
this.contextMenuManager = menuButton.contextMenuManager;
this.subMenuManager = menuButton.subMenuManager;
this.arcMenu = menuButton.arcMenu;
this.section = menuButton.section;
this.layout = this._settings.get_enum('menu-layout');
this.layoutProperties = layoutProperties;
this._focusChild = null;
this.shouldLoadPinnedApps = true;
this.hasPinnedApps = false;
if(this.layoutProperties.Search){
this.searchResults = new ArcSearch.SearchResults(this);
this.searchBox = new MW.SearchBox(this);
this._searchBoxChangedId = this.searchBox.connect('search-changed', this._onSearchBoxChanged.bind(this));
this._searchBoxKeyPressId = this.searchBox.connect('entry-key-press', this._onSearchBoxKeyPress.bind(this));
}
this._mainBoxKeyPressId = this.mainBox.connect('key-press-event', this._onMainBoxKeyPress.bind(this));
this._tree = new GMenu.Tree({ menu_basename: 'applications.menu' });
this._treeChangedId = this._tree.connect('changed', () => this.reloadApplications());
this._gnomeFavoritesReloadID = AppFavorites.getAppFavorites().connect('changed', () => {
if(this.categoryDirectories){
let categoryMenuItem = this.categoryDirectories.get(Constants.CategoryType.FAVORITES);
if(categoryMenuItem)
this._loadGnomeFavorites(categoryMenuItem);
}
});
this.mainBox.vertical = this.layoutProperties.VerticalMainBox;
this.createLayout();
this.updateStyle();
}
createLayout(){
this.disableFadeEffect = this._settings.get_boolean('disable-scrollview-fade-effect');
this.activeCategoryType = -1;
let layout = new Clutter.GridLayout({
orientation: Clutter.Orientation.VERTICAL,
column_spacing: this.layoutProperties.ColumnSpacing,
row_spacing: this.layoutProperties.RowSpacing
});
this.applicationsGrid = new St.Widget({
x_expand: true,
x_align: this.layoutProperties.DisplayType === Constants.DisplayType.LIST ? Clutter.ActorAlign.FILL : Clutter.ActorAlign.CENTER,
layout_manager: layout
});
layout.hookup_style(this.applicationsGrid);
}
setDefaultMenuView(){
if(this.layoutProperties.Search){
this.searchBox.clearWithoutSearchChangeEvent();
this.searchResults.setTerms([]);
}
this._clearActorsFromBox();
this.resetScrollBarPosition();
}
getColumnsFromActor(actor){
let gridIconWidth = this.getActorWidthFromStyleClass(actor.name);
return this.getBestFitColumns(gridIconWidth);
}
getColumnsFromGridIconSizeSetting(){
let gridIconWidth;
let iconSizeEnum = this._settings.get_enum("menu-item-grid-icon-size");
if(iconSizeEnum === Constants.GridIconSize.DEFAULT)
gridIconWidth = this.getActorWidthFromStyleClass(this.layoutProperties.DefaultIconGridStyle);
else{
Constants.GridIconInfo.forEach((info) => {
if(iconSizeEnum === info.ENUM){
gridIconWidth = info.SIZE;
return;
}
});
}
return this.getBestFitColumns(gridIconWidth);
}
getBestFitColumns(gridIconWidth){
let width = this.layoutProperties.MenuWidth;
let spacing = this.layoutProperties.ColumnSpacing;
let columns = Math.floor(width / (gridIconWidth + spacing));
return columns;
}
getActorWidthFromStyleClass(name){
let size;
Constants.GridIconInfo.forEach((info) => {
if(name === info.NAME){
size = info.SIZE;
return;
}
});
return size;
}
resetScrollBarPosition(){
let appsScrollBoxAdj;
if(this.applicationsScrollBox){
appsScrollBoxAdj = this.applicationsScrollBox.get_vscroll_bar().get_adjustment();
appsScrollBoxAdj.set_value(0);
}
if(this.categoriesScrollBox){
appsScrollBoxAdj = this.categoriesScrollBox.get_vscroll_bar().get_adjustment();
appsScrollBoxAdj.set_value(0);
}
if(this.shortcutsScrollBox){
appsScrollBoxAdj = this.shortcutsScrollBox.get_vscroll_bar().get_adjustment();
appsScrollBoxAdj.set_value(0);
}
if(this.actionsScrollBox){
appsScrollBoxAdj = this.actionsScrollBox.get_vscroll_bar().get_adjustment();
appsScrollBoxAdj.set_value(0);
}
}
reloadApplications(){
//Don't reload applications if the menu is open.
//Instead, reload on menu-closed event.
//Prevents the menu from jumping to its default view
//when reloadApplications() is called.
if(this.arcMenu.isOpen){
if(!this._menuClosedID){
this._menuClosedID = this.arcMenu.connect('menu-closed', () => {
this.reloadApplications();
if(this._menuClosedID){
this.arcMenu.disconnect(this._menuClosedID);
this._menuClosedID = null;
}
});
}
return;
}
if(this.applicationsMap){
this.applicationsMap.forEach((value,key,map)=>{
value.destroy();
});
this.applicationsMap = null;
}
if(this.categoryDirectories){
this.categoryDirectories.forEach((value,key,map)=>{
value.destroy();
});
this.categoryDirectories = null;
}
this.loadCategories();
this.setDefaultMenuView();
}
updateStyle(){
let customStyle = this._settings.get_boolean('enable-custom-arc-menu');
if(this.layoutProperties.Search){
this.searchBox.updateStyle(this._settings.get_boolean('disable-searchbox-border'))
customStyle ? this.searchResults.setStyle('arc-menu-status-text') : this.searchResults.setStyle('');
if(customStyle){
this.searchBox.remove_style_class_name('default-search-entry');
this.searchBox.add_style_class_name('arc-search-entry');
}
else{
this.searchBox.remove_style_class_name('arc-search-entry');
this.searchBox.add_style_class_name('default-search-entry');
}
}
}
loadCategories(displayType = Constants.DisplayType.LIST){
this.applicationsMap = new Map();
this._tree.load_sync();
let root = this._tree.get_root_directory();
let iter = root.iter();
let nextType;
while ((nextType = iter.next()) != GMenu.TreeItemType.INVALID) {
if (nextType == GMenu.TreeItemType.DIRECTORY) {
let dir = iter.get_directory();
if (!dir.get_is_nodisplay()) {
let categoryId = dir.get_menu_id();
let categoryMenuItem;
if(displayType === Constants.DisplayType.SIMPLE_CATEGORY)
categoryMenuItem = new MW.SimpleMenuItem(this, dir);
else if(displayType === Constants.DisplayType.SUBMENU_CATEGORY)
categoryMenuItem = new MW.CategorySubMenuItem(this, dir);
else
categoryMenuItem = new MW.CategoryMenuItem(this, dir, displayType);
this.categoryDirectories.set(categoryId, categoryMenuItem);
let foundRecentlyInstallApp = this._loadCategory(categoryId, dir);
categoryMenuItem.setRecentlyInstalledIndicator(foundRecentlyInstallApp);
//Sort the App List Alphabetically
categoryMenuItem.appList.sort((a, b) => {
return a.get_name().toLowerCase() > b.get_name().toLowerCase();
});
}
}
}
let categoryMenuItem = this.categoryDirectories.get(Constants.CategoryType.ALL_PROGRAMS);
if(categoryMenuItem){
let appList = [];
this.applicationsMap.forEach((value,key,map) => {
appList.push(key);
//Show Recently Installed Indicator on All Programs category
if(value.isRecentlyInstalled && !categoryMenuItem.isRecentlyInstalled)
categoryMenuItem.setRecentlyInstalledIndicator(true);
});
appList.sort((a, b) => {
return a.get_name().toLowerCase() > b.get_name().toLowerCase();
});
categoryMenuItem.appList = appList;
}
categoryMenuItem = this.categoryDirectories.get(Constants.CategoryType.FAVORITES);
if(categoryMenuItem){
this._loadGnomeFavorites(categoryMenuItem);
}
categoryMenuItem = this.categoryDirectories.get(Constants.CategoryType.FREQUENT_APPS);
if(categoryMenuItem){
let mostUsed = Shell.AppUsage.get_default().get_most_used();
for (let i = 0; i < mostUsed.length; i++) {
if (mostUsed[i] && mostUsed[i].get_app_info().should_show())
categoryMenuItem.appList.push(mostUsed[i]);
}
}
categoryMenuItem = this.categoryDirectories.get(Constants.CategoryType.PINNED_APPS);
if(categoryMenuItem){
this.hasPinnedApps = true;
categoryMenuItem.appList = categoryMenuItem.appList.concat(this.pinnedAppsArray);
}
categoryMenuItem = this.categoryDirectories.get(Constants.CategoryType.RECENT_FILES);
if(categoryMenuItem){
this._loadRecentFiles(categoryMenuItem);
}
}
_loadCategory(categoryId, dir, submenuItem) {
let iter = dir.iter();
let nextType;
let foundRecentlyInstallApp = false;
let isLayoutSimple2 = this.layout === Constants.MenuLayout.SIMPLE_2;
while ((nextType = iter.next()) != GMenu.TreeItemType.INVALID) {
if (nextType == GMenu.TreeItemType.ENTRY) {
let entry = iter.get_entry();
let id;
try {
id = entry.get_desktop_file_id();
} catch (e) {
continue;
}
let app = appSys.lookup_app(id);
if (!app)
app = new Shell.App({ app_info: entry.get_app_info() });
if (app.get_app_info().should_show()){
let item = this.applicationsMap.get(app);
if (!item) {
let isContainedInCategory = true;
item = new MW.ApplicationMenuItem(this, app, this.layoutProperties.DisplayType, null, isContainedInCategory);
}
let disabled = this._settings.get_boolean("disable-recently-installed-apps")
if(!disabled && item.isRecentlyInstalled)
foundRecentlyInstallApp = true;
if(!submenuItem){
let categoryMenuItem = this.categoryDirectories.get(categoryId);
categoryMenuItem.appList.push(app);
this.applicationsMap.set(app, item);
}
else{
submenuItem.applicationsMap.set(app, item);
}
}
}
else if (nextType == GMenu.TreeItemType.DIRECTORY) {
let subdir = iter.get_directory();
if (!subdir.get_is_nodisplay()){
if(this._settings.get_boolean('enable-sub-menus') && this.layoutProperties.DisplayType === Constants.DisplayType.LIST && !isLayoutSimple2){
let submenuItem = this.applicationsMap.get(subdir);
if (!submenuItem) {
submenuItem = new MW.CategorySubMenuItem(this, subdir);
submenuItem._setParent(this.arcMenu);
let categoryMenuItem = this.categoryDirectories.get(categoryId);
categoryMenuItem.appList.push(subdir);
this.applicationsMap.set(subdir, submenuItem);
}
let recentlyInstallApp = this._loadCategory(categoryId, subdir, submenuItem);
if(recentlyInstallApp)
foundRecentlyInstallApp = true;
submenuItem.setRecentlyInstalledIndicator(foundRecentlyInstallApp);
}
else{
let recentlyInstallApp = this._loadCategory(categoryId, subdir);
if(recentlyInstallApp)
foundRecentlyInstallApp = true;
}
}
}
}
return foundRecentlyInstallApp;
}
setRecentlyInstalledIndicator(){
let disabled = this._settings.get_boolean("disable-recently-installed-apps")
if(!disabled){
for(let categoryMenuItem of this.categoryDirectories.values()){
categoryMenuItem.setRecentlyInstalledIndicator(false);
for(let i = 0; i < categoryMenuItem.appList.length; i++){
let item = this.applicationsMap.get(categoryMenuItem.appList[i]);
if(!item)
continue;
if(item instanceof MW.CategorySubMenuItem){
item.setRecentlyInstalledIndicator(false);
for(let menuItem of item.applicationsMap.values()){
if(menuItem.isRecentlyInstalled){
item.setRecentlyInstalledIndicator(true);
break;
}
}
}
if(item.isRecentlyInstalled){
categoryMenuItem.setRecentlyInstalledIndicator(true);
break;
}
}
}
}
}
displayCategories(categoriesBox){
if(!categoriesBox){
categoriesBox = this.applicationsBox;
}
this._clearActorsFromBox(categoriesBox);
this._futureActiveItem = false;
for(let categoryMenuItem of this.categoryDirectories.values()){
if(categoryMenuItem.get_parent())
continue;
categoriesBox.add_child(categoryMenuItem.actor);
if(!this._futureActiveItem){
this._futureActiveItem = categoryMenuItem;
}
}
this.activeMenuItem = this._futureActiveItem;
}
_loadGnomeFavorites(categoryMenuItem){
let appList = AppFavorites.getAppFavorites().getFavorites();
//Show Recently Installed Indicator on GNOME favorites category
for(let i = 0; i < appList.length; i++){
let item = this.applicationsMap.get(appList[i]);
if(item && item.isRecentlyInstalled && !categoryMenuItem.isRecentlyInstalled)
categoryMenuItem.setRecentlyInstalledIndicator(true);
}
categoryMenuItem.appList = appList;
if(this.activeCategoryType === Constants.CategoryType.FAVORITES)
categoryMenuItem.displayAppList();
}
_loadRecentFiles(){
if(!this.recentFilesManager)
this.recentFilesManager = new RecentFilesManager();
}
displayRecentFiles(box = this.applicationsBox, callback){
const homeRegExp = new RegExp('^(' + GLib.get_home_dir() + ')');
this._clearActorsFromBox(box);
this._futureActiveItem = false;
this.recentFilesManager.filterRecentFiles(recentFile => {
let file = Gio.File.new_for_uri(recentFile.get_uri());
let filePath = file.get_path();
let name = recentFile.get_display_name();
let icon = Gio.content_type_get_symbolic_icon(recentFile.get_mime_type()).to_string();
let isContainedInCategory = true;
let placeMenuItem = this.createMenuItem([name, icon, filePath], Constants.DisplayType.LIST, isContainedInCategory);
placeMenuItem._path = file.get_parent()?.get_path() // can be null
placeMenuItem.style = "padding-right: 15px;";
placeMenuItem.description = recentFile.get_uri_display().replace(homeRegExp, '~');
placeMenuItem.fileUri = recentFile.get_uri();
placeMenuItem._removeBtn = new MW.ArcMenuButtonItem(this, null, 'edit-delete-symbolic');
placeMenuItem._removeBtn.toggleMenuOnClick = false;
placeMenuItem._removeBtn.x_align = Clutter.ActorAlign.END;
placeMenuItem._removeBtn.x_expand = true;
placeMenuItem._removeBtn.add_style_class_name("arcmenu-small-button");
placeMenuItem._removeBtn.setIconSize(14);
placeMenuItem._removeBtn.connect('activate', () => {
try {
let recentManager = this.recentFilesManager.getRecentManager();
recentManager.remove_item(placeMenuItem.fileUri);
} catch(err) {
log(err);
}
placeMenuItem.cancelPopupTimeout();
placeMenuItem.contextMenu?.close();
box.remove_child(placeMenuItem);
placeMenuItem.destroy();
});
placeMenuItem.add_child(placeMenuItem._removeBtn);
box.add_child(placeMenuItem);
if(!this._futureActiveItem){
this._futureActiveItem = placeMenuItem;
this.activeMenuItem = this._futureActiveItem;
}
if(callback)
callback();
});
}
_displayPlaces() {
var SHORTCUT_TRANSLATIONS = [_("Home"), _("Documents"), _("Downloads"), _("Music"), _("Pictures"), _("Videos"), _("Computer"), _("Network")];
let directoryShortcuts = this._settings.get_value('directory-shortcuts-list').deep_unpack();
for (let i = 0; i < directoryShortcuts.length; i++) {
let directory = directoryShortcuts[i];
let isContainedInCategory = false;
let placeMenuItem = this.createMenuItem(directory, Constants.DisplayType.LIST, isContainedInCategory);
if(placeMenuItem)
this.shortcutsBox.add_child(placeMenuItem.actor);
}
}
loadExtraPinnedApps(pinnedAppsArray, separatorIndex){
let pinnedApps = pinnedAppsArray;
//if the extraPinnedApps array is empty, create a default list of apps.
if(!pinnedApps.length || !Array.isArray(pinnedApps)){
pinnedApps = this._createExtraPinnedAppsList();
}
for(let i = 0;i < pinnedApps.length; i += 3){
if(i === separatorIndex * 3 && i !== 0)
this._addSeparator();
let isContainedInCategory = false;
let placeMenuItem = this.createMenuItem([pinnedApps[i], pinnedApps[i + 1], pinnedApps[i + 2]], Constants.DisplayType.BUTTON, isContainedInCategory);
placeMenuItem.actor.x_expand = false;
placeMenuItem.actor.y_expand = false;
placeMenuItem.actor.y_align = Clutter.ActorAlign.CENTER;
placeMenuItem.actor.x_align = Clutter.ActorAlign.CENTER;
this.actionsBox.add_child(placeMenuItem.actor);
}
}
createMenuItem(menuItemArray, displayType, isContainedInCategory){
let placeInfo, placeMenuItem;
let command = menuItemArray[2];
let app = Shell.AppSystem.get_default().lookup_app(command);
if(command === "ArcMenu_Home"){
let homePath = GLib.get_home_dir();
placeInfo = new MW.PlaceInfo(Gio.File.new_for_path(homePath), _("Home"));
placeMenuItem = new MW.PlaceMenuItem(this, placeInfo, displayType, isContainedInCategory);
}
else if(command === "ArcMenu_Computer"){
placeInfo = new PlaceDisplay.RootInfo();
placeMenuItem = new MW.PlaceMenuItem(this, placeInfo, displayType, isContainedInCategory);
}
else if(command === "ArcMenu_Network"){
placeInfo = new PlaceDisplay.PlaceInfo('network', Gio.File.new_for_uri('network:///'), _('Network'),'network-workgroup-symbolic');
placeMenuItem = new MW.PlaceMenuItem(this, placeInfo, displayType, isContainedInCategory);
}
else if(command === "ArcMenu_Software"){
let software = Utils.findSoftwareManager();
if(software)
placeMenuItem = new MW.ShortcutMenuItem(this, _("Software"), 'system-software-install-symbolic', software, displayType, isContainedInCategory);
}
else if(command === "ArcMenu_Trash"){
placeMenuItem = new MW.ShortcutMenuItem(this, _("Trash"), '', "ArcMenu_Trash", displayType, isContainedInCategory);
}
else if(command === Constants.ArcMenuSettingsCommand || command === "ArcMenu_Suspend" || command === "ArcMenu_LogOut" || command === "ArcMenu_PowerOff"
|| command === "ArcMenu_Lock" || command === "ArcMenu_Restart" || command === "ArcMenu_HybridSleep" || command === "ArcMenu_Hibernate" || app){
placeMenuItem = new MW.ShortcutMenuItem(this, menuItemArray[0], menuItemArray[1], menuItemArray[2], displayType, isContainedInCategory);
}
else if(command === "ArcMenu_Recent"){
let uri = "recent:///";
placeInfo = new MW.PlaceInfo(Gio.File.new_for_uri(uri), _(menuItemArray[0]));
placeMenuItem = new MW.PlaceMenuItem(this, placeInfo, displayType, isContainedInCategory);
}
else if(command.startsWith("ArcMenu_")){
let path = command.replace("ArcMenu_",'');
if(path === "Documents")
path = imports.gi.GLib.UserDirectory.DIRECTORY_DOCUMENTS;
else if(path === "Downloads")
path = imports.gi.GLib.UserDirectory.DIRECTORY_DOWNLOAD;
else if(path === "Music")
path = imports.gi.GLib.UserDirectory.DIRECTORY_MUSIC;
else if(path === "Pictures")
path = imports.gi.GLib.UserDirectory.DIRECTORY_PICTURES;
else if(path === "Videos")
path = imports.gi.GLib.UserDirectory.DIRECTORY_VIDEOS;
path = GLib.get_user_special_dir(path);
if (path !== null){
placeInfo = new MW.PlaceInfo(Gio.File.new_for_path(path), _(menuItemArray[0]));
placeMenuItem = new MW.PlaceMenuItem(this, placeInfo, displayType, isContainedInCategory);
}
}
else{
let path = command;
placeInfo = new MW.PlaceInfo(Gio.File.new_for_path(path), _(menuItemArray[0]), (menuItemArray[1] !== "ArcMenu_Folder") ? Gio.icon_new_for_string(menuItemArray[1]) : null);
placeMenuItem = new MW.PlaceMenuItem(this, placeInfo, displayType, isContainedInCategory);
}
return placeMenuItem;
}
loadPinnedApps(){
let pinnedApps = this._settings.get_strv('pinned-app-list');
this.pinnedAppsArray = null;
this.pinnedAppsArray = [];
let categoryMenuItem = this.categoryDirectories ? this.categoryDirectories.get(Constants.CategoryType.PINNED_APPS) : null;
let isContainedInCategory = categoryMenuItem ? true : false;
for(let i = 0; i < pinnedApps.length; i += 3){
if(i === 0 && pinnedApps[0] === "ArcMenu_WebBrowser")
this._updatePinnedAppsWebBrowser(pinnedApps);
let pinnedAppsMenuItem = new MW.PinnedAppsMenuItem(this, pinnedApps[i], pinnedApps[i + 1], pinnedApps[i + 2], this.layoutProperties.DisplayType, isContainedInCategory);
pinnedAppsMenuItem.connect('saveSettings', ()=> {
let array = [];
for(let i = 0; i < this.pinnedAppsArray.length; i++){
array.push(this.pinnedAppsArray[i]._name);
array.push(this.pinnedAppsArray[i]._iconPath);
array.push(this.pinnedAppsArray[i]._command);
}
this._settings.set_strv('pinned-app-list',array);
});
this.pinnedAppsArray.push(pinnedAppsMenuItem);
}
if(categoryMenuItem){
categoryMenuItem.appList = null;
categoryMenuItem.appList = [];
categoryMenuItem.appList = categoryMenuItem.appList.concat(this.pinnedAppsArray);
}
}
_updatePinnedAppsWebBrowser(pinnedApps){
//Find the Default Web Browser, if found add to pinned apps list, if not found delete the placeholder.
//Will only run if placeholder is found. Placeholder only found with default settings set.
if(pinnedApps[0] === "ArcMenu_WebBrowser"){
let browserName = '';
try{
//user may not have xdg-utils package installed which will throw error
let [res, stdout, stderr, status] = GLib.spawn_command_line_sync("xdg-settings get default-web-browser");
let webBrowser = String.fromCharCode(...stdout);
browserName = webBrowser.split(".desktop")[0];
browserName += ".desktop";
}
catch(error){
global.log("ArcMenu Error - Failed to find default web browser. Removing placeholder pinned app.")
}
this._app = appSys.lookup_app(browserName);
if(this._app){
pinnedApps[0] = this._app.get_name();
pinnedApps[1] = '';
pinnedApps[2] = this._app.get_id();
}
else{
pinnedApps.splice(0,3);
}
this.shouldLoadPinnedApps = false; // We don't want to trigger a setting changed event
this._settings.set_strv('pinned-app-list', pinnedApps);
this.shouldLoadPinnedApps = true;
}
}
displayPinnedApps(){
this._clearActorsFromBox();
this._displayAppList(this.pinnedAppsArray, Constants.CategoryType.PINNED_APPS, this.applicationsGrid);
}
placesAddSeparator(id){
let separator = new MW.ArcMenuSeparator(Constants.SeparatorStyle.SHORT, Constants.SeparatorAlignment.HORIZONTAL);
this._sections[id].add_child(separator);
}
_redisplayPlaces(id) {
if(this._sections[id].get_n_children() > 0){
this.bookmarksShorctus = false;
this.externalDevicesShorctus = false;
this.networkDevicesShorctus = false;
this._sections[id].destroy_all_children();
}
this._createPlaces(id);
}
_createPlaces(id) {
let places = this.placesManager.get(id);
if(this.placesManager.get('network').length > 0)
this.networkDevicesShorctus = true;
if(this.placesManager.get('devices').length > 0)
this.externalDevicesShorctus=true;
if(this.placesManager.get('bookmarks').length > 0)
this.bookmarksShorctus = true;
if(this._settings.get_boolean('show-bookmarks')){
if(id === 'bookmarks' && places.length > 0){
for (let i = 0; i < places.length; i++){
let item = new PlaceDisplay.PlaceMenuItem(this, places[i]);
this._sections[id].add_child(item);
}
//create a separator if bookmark and software shortcut are both shown
if(this.bookmarksShorctus && this.softwareShortcuts){
this.placesAddSeparator(id);
}
}
}
if(this._settings.get_boolean('show-external-devices')){
if(id === 'devices'){
for (let i = 0; i < places.length; i++){
let item = new PlaceDisplay.PlaceMenuItem(this, places[i]);
this._sections[id].add_child(item);
}
if((this.externalDevicesShorctus && !this.networkDevicesShorctus) && (this.bookmarksShorctus || this.softwareShortcuts))
this.placesAddSeparator(id);
}
if(id === 'network'){
for (let i = 0; i < places.length; i++){
let item = new PlaceDisplay.PlaceMenuItem(this, places[i]);
this._sections[id].add_child(item);
}
if(this.networkDevicesShorctus && (this.bookmarksShorctus || this.softwareShortcuts))
this.placesAddSeparator(id);
}
}
}
setActiveCategory(category, setActive = true){
this._futureActiveItem = category;
this.activeMenuItem = category;
}
setFrequentAppsList(categoryMenuItem){
categoryMenuItem.appList = [];
let mostUsed = Shell.AppUsage.get_default().get_most_used();
for (let i = 0; i < mostUsed.length; i++) {
if (mostUsed[i] && mostUsed[i].get_app_info().should_show())
categoryMenuItem.appList.push(mostUsed[i]);
}
}
_clearActorsFromBox(box){
this.recentFilesManager?.cancelCurrentQueries();
if(!box){
box = this.applicationsBox;
this.activeCategoryType = -1;
}
let parent = box.get_parent();
if(parent instanceof St.ScrollView){
let scrollBoxAdj = parent.get_vscroll_bar().get_adjustment();
scrollBoxAdj.set_value(0);
}
let actors = box.get_children();
for (let i = 0; i < actors.length; i++) {
let actor = actors[i];
if(actor instanceof St.Widget && actor.layout_manager instanceof Clutter.GridLayout){
actor.get_children().forEach(gridChild => {
if(gridChild instanceof MW.CategorySubMenuItem)
gridChild.menu.close();
});
}
box.remove_child(actor);
}
}
displayCategoryAppList(appList, category){
this._clearActorsFromBox();
this._displayAppList(appList, category, this.applicationsGrid);
}
_displayAppList(apps, category, grid){
this.activeCategoryType = category;
grid.remove_all_children();
let count = 0;
let top = -1;
let left = 0;
this._futureActiveItem = false;
let currentCharacter;
let alphabetizeAllPrograms = this._settings.get_boolean("alphabetize-all-programs") && this.layoutProperties.DisplayType === Constants.DisplayType.LIST;
let rtl = this.mainBox.get_text_direction() == Clutter.TextDirection.RTL;
let columns = -1;
for (let i = 0; i < apps.length; i++) {
let app = apps[i];
let item;
let shouldShow = true;
if(category === Constants.CategoryType.PINNED_APPS || category === Constants.CategoryType.HOME_SCREEN){
item = app;
if(!item.shouldShow)
shouldShow = false;
}
else{
item = this.applicationsMap.get(app);
if (!item) {
item = new MW.ApplicationMenuItem(this, app, this.layoutProperties.DisplayType);
this.applicationsMap.set(app, item);
}
}
if(item.actor.get_parent())
item.actor.get_parent().remove_child(item.actor);
if(shouldShow){
if(columns === -1){
if(grid.layout_manager.forceGridColumns)
columns = grid.layout_manager.forceGridColumns;
else if(this.layoutProperties.DisplayType === Constants.DisplayType.GRID)
columns = this.getColumnsFromActor(item);
else
columns = 1;
grid.layout_manager.gridColumns = columns;
}
if(!rtl && (count % columns === 0)){
top++;
left = 0;
}
else if(rtl && (left === 0)){
top++;
left = columns;
}
if(alphabetizeAllPrograms && category === Constants.CategoryType.ALL_PROGRAMS){
if(currentCharacter !== app.get_name().charAt(0).toLowerCase()){
currentCharacter = app.get_name().charAt(0).toLowerCase();
let label = this._createLabelWithSeparator(currentCharacter.toUpperCase());
grid.layout_manager.attach(label, left, top, 1, 1);
top++;
}
}
grid.layout_manager.attach(item, left, top, 1, 1);
item.gridLocation = [left, top];
if(item instanceof MW.CategorySubMenuItem){
top++;
grid.layout_manager.attach(item.menu.actor, left, top, 1, 1);
}
if(!rtl)
left++;
else if(rtl)
left--;
count++;
if(!this._futureActiveItem && grid === this.applicationsGrid){
this._futureActiveItem = item;
}
}
}
if(this.applicationsBox && !this.applicationsBox.contains(this.applicationsGrid))
this.applicationsBox.add_child(this.applicationsGrid);
if(this._futureActiveItem)
this.activeMenuItem = this._futureActiveItem;
}
displayAllApps(){
let appList = [];
this.applicationsMap.forEach((value,key,map) => {
appList.push(key);
});
appList.sort((a, b) => {
return a.get_name().toLowerCase() > b.get_name().toLowerCase();
});
this._clearActorsFromBox();
this._displayAppList(appList, Constants.CategoryType.ALL_PROGRAMS, this.applicationsGrid);
}
get activeMenuItem() {
return this._activeMenuItem;
}
set activeMenuItem(item) {
let itemChanged = item !== this._activeMenuItem;
if(itemChanged){
this._activeMenuItem = item;
if(this.arcMenu.isOpen && item && this.layoutProperties.SupportsCategoryOnHover)
item.grab_key_focus();
else if(this.arcMenu.isOpen)
this.mainBox.grab_key_focus();
if(this.layout === Constants.MenuLayout.LAUNCHER && !this.layoutProperties.StandaloneRunner && item)
this.createActiveSearchItemPanel(item);
}
}
_onSearchBoxChanged(searchBox, searchString) {
if(searchBox.isEmpty()){
this.searchResults.hide();
this.setDefaultMenuView();
}
else{
this._clearActorsFromBox();
let appsScrollBoxAdj = this.applicationsScrollBox.get_vscroll_bar().get_adjustment();
appsScrollBoxAdj.set_value(0);
this.applicationsBox.add_child(this.searchResults);
this.searchResults.show();
searchString = searchString.replace(/^\s+/g, '').replace(/\s+$/g, '');
this.searchResults.setTerms(searchString.split(/\s+/));
}
}
_onSearchBoxKeyPress(searchBox, event) {
let symbol = event.get_key_symbol();
switch (symbol) {
case Clutter.KEY_Up:
case Clutter.KEY_Down:
case Clutter.KEY_Left:
case Clutter.KEY_Right:
let direction;
if (symbol === Clutter.KEY_Down || symbol === Clutter.KEY_Up)
return Clutter.EVENT_PROPAGATE;
if (symbol === Clutter.KEY_Right)
direction = St.DirectionType.RIGHT;
if (symbol === Clutter.KEY_Left)
direction = St.DirectionType.LEFT;
let cursorPosition = this.searchBox.clutter_text.get_cursor_position();
if(cursorPosition === Constants.CaretPosition.END && symbol === Clutter.KEY_Right)
cursorPosition = Constants.CaretPosition.END;
else if(cursorPosition === Constants.CaretPosition.START && symbol === Clutter.KEY_Left)
cursorPosition = Constants.CaretPosition.START;
else
cursorPosition = Constants.CaretPosition.MIDDLE;
if(cursorPosition === Constants.CaretPosition.END || cursorPosition === Constants.CaretPosition.START){
let navigateActor = this.activeMenuItem;
if(this.searchResults.hasActiveResult()){
navigateActor = this.searchResults.getTopResult();
if(navigateActor.has_style_pseudo_class("active")){
navigateActor.grab_key_focus();
return this.mainBox.navigate_focus(navigateActor, direction, false);
}
navigateActor.grab_key_focus();
return Clutter.EVENT_STOP;
}
if(!navigateActor)
return Clutter.EVENT_PROPAGATE;
return this.mainBox.navigate_focus(navigateActor, direction, false);
}
return Clutter.EVENT_PROPAGATE;
default:
return Clutter.EVENT_PROPAGATE;
}
}
_onMainBoxKeyPress(actor, event) {
if (event.has_control_modifier()) {
if(this.searchBox)
this.searchBox.grab_key_focus();
return Clutter.EVENT_PROPAGATE;
}
let symbol = event.get_key_symbol();
let unicode = Clutter.keysym_to_unicode(symbol);
switch (symbol) {
case Clutter.KEY_BackSpace:
if(this.searchBox && !this.searchBox.hasKeyFocus() && !this.searchBox.isEmpty()){
this.searchBox.grab_key_focus();
let newText = this.searchBox.getText().slice(0, -1);
this.searchBox.setText(newText);
}
return Clutter.EVENT_PROPAGATE;
case Clutter.KEY_Up:
case Clutter.KEY_Down:
case Clutter.KEY_Left:
case Clutter.KEY_Right:
let direction;
if (symbol === Clutter.KEY_Down)
direction = St.DirectionType.DOWN;
if (symbol === Clutter.KEY_Right)
direction = St.DirectionType.RIGHT
if (symbol === Clutter.KEY_Up)
direction = St.DirectionType.UP;
if (symbol === Clutter.KEY_Left)
direction = St.DirectionType.LEFT;
if(this.layoutProperties.Search && this.searchBox.hasKeyFocus() && this.searchResults.hasActiveResult() && this.searchResults.get_parent()){
if(this.searchResults.getTopResult().has_style_pseudo_class("active")){
this.searchResults.getTopResult().actor.grab_key_focus();
return actor.navigate_focus(global.stage.key_focus, direction, false);
}
this.searchResults.getTopResult().actor.grab_key_focus();
return Clutter.EVENT_STOP;
}
else if(global.stage.key_focus === this.mainBox && symbol === Clutter.KEY_Up){
return actor.navigate_focus(global.stage.key_focus, direction, true);
}
else if(global.stage.key_focus === this.mainBox){
this.activeMenuItem.grab_key_focus();
return Clutter.EVENT_STOP;
}
return actor.navigate_focus(global.stage.key_focus, direction, false);
case Clutter.KEY_Tab:
case Clutter.KEY_KP_Tab:
case Clutter.KEY_KP_Enter:
case Clutter.KEY_Return:
case Clutter.KEY_Escape:
return Clutter.EVENT_PROPAGATE;
default:
if (unicode !== 0 && this.searchBox) {
global.stage.set_key_focus(this.searchBox.clutter_text);
let synthEvent = event.copy();
synthEvent.set_source(this.searchBox.clutter_text);
this.searchBox.clutter_text.event(synthEvent, false);
}
}
return Clutter.EVENT_PROPAGATE;
}
destroy(){
if(this.recentFilesManager){
this.recentFilesManager.destroy();
this.recentFilesManager = null;
}
if(this._treeChangedId){
this._tree.disconnect(this._treeChangedId);
this._treeChangedId = null;
this._tree = null;
}
if(this.applicationsBox){
if(this.applicationsBox.contains(this.applicationsGrid))
this.applicationsBox.remove_child(this.applicationsGrid);
}
if(this.network){
this.network.destroy();
this.networkMenuItem.destroy();
}
if(this.computer){
this.computer.destroy();
this.computerMenuItem.destroy();
}
if(this.placesManager){
for(let id in this._sections){
this._sections[id].get_children().forEach((child) =>{
child.destroy();
});
};
if(this.placeManagerUpdatedID){
this.placesManager.disconnect(this.placeManagerUpdatedID);
this.placeManagerUpdatedID = null;
}
this.placesManager.destroy();
this.placesManager = null
}
if(this._searchBoxChangedId){
this.searchBox?.disconnect(this._searchBoxChangedId);
this._searchBoxChangedId = null;;
}
if(this._searchBoxKeyPressId){
this.searchBox?.disconnect(this._searchBoxKeyPressId);
this._searchBoxKeyPressId = null;
}
if(this._searchBoxKeyFocusInId){
this.searchBox?.disconnect(this._searchBoxKeyFocusInId);
this._searchBoxKeyFocusInId = null;
}
if(this.searchBox)
this.searchBox.destroy();
if(this.searchResults){
this.searchResults.setTerms([]);
this.searchResults.destroy();
this.searchResults = null;
}
if (this._mainBoxKeyPressId) {
this.mainBox.disconnect(this._mainBoxKeyPressId);
this._mainBoxKeyPressId = null;
}
if(this._gnomeFavoritesReloadID){
AppFavorites.getAppFavorites().disconnect(this._gnomeFavoritesReloadID);
this._gnomeFavoritesReloadID = null;
}
if(this.pinnedAppsArray){
for(let i = 0; i < this.pinnedAppsArray.length; i++){
this.pinnedAppsArray[i].destroy();
}
this.pinnedAppsArray = null;
}
if(this.applicationsMap){
this.applicationsMap.forEach((value,key,map)=>{
value.destroy();
});
this.applicationsMap = null;
}
if(this.categoryDirectories){
this.categoryDirectories.forEach((value,key,map)=>{
value.destroy();
});
this.categoryDirectories = null;
}
this.mainBox.destroy_all_children();
}
_createScrollBox(params){
let scrollBox = new St.ScrollView(params);
let panAction = new Clutter.PanAction({ interpolate: false });
panAction.connect('pan', (action) => {
this._blockActivateEvent = true;
this.onPan(action, scrollBox);
});
panAction.connect('gesture-cancel',(action) => this.onPanEnd(action, scrollBox));
panAction.connect('gesture-end', (action) => this.onPanEnd(action, scrollBox));
scrollBox.add_action(panAction);
scrollBox.set_policy(St.PolicyType.NEVER, St.PolicyType.AUTOMATIC);
scrollBox.clip_to_allocation = true;
return scrollBox;
}
_createLabelWithSeparator(headerLabel){
let separator = new MW.ArcMenuSeparator(Constants.SeparatorStyle.HEADER_LABEL, Constants.SeparatorAlignment.HORIZONTAL, headerLabel);
return separator;
}
createLabelRow(title){
let labelRow = new PopupMenu.PopupMenuItem(_(title), {
hover: false,
can_focus: false
});
labelRow.actor.add_style_pseudo_class = () => { return false;};
labelRow.label.style = 'font-weight: bold;';
return labelRow;
}
_keyFocusIn(actor) {
if (this._focusChild == actor)
return;
this._focusChild = actor;
Utils.ensureActorVisibleInScrollView(actor);
}
onPan(action, scrollbox) {
let [dist_, dx_, dy] = action.get_motion_delta(0);
let adjustment = scrollbox.get_vscroll_bar().get_adjustment();
adjustment.value -= dy;
return false;
}
onPanEnd(action, scrollbox) {
let velocity = -action.get_velocity(0)[2];
let adjustment = scrollbox.get_vscroll_bar().get_adjustment();
let endPanValue = adjustment.value + velocity * 2;
adjustment.value = endPanValue;
}
};