Major rework of application data structures and caching
Before, we looked up application data in several ways; the ShellAppSystem exported just application ids (though it parsed the .desktop files internally), and we'd create a Gio.DesktopAppInfo object (reparsing the desktop file again), wrapping that inside a JavaScript AppInfo class, and finally the AppDisplay would again parse the .desktop file to get the categories. Also, to look up applications by id previously, we traversed the entire menu structure each time. Some qualities such as the NoDisplay flag were not easily exposed in the old system. And if we wanted to expose them we'd have to change several different application information wrapper classes. All in all, it was quite suboptimal. The theme of this new code is basically "just use libgnome-menus". We do not call into Gio for app lookups anymore. The new Shell.AppInfo class is a disguised pointer for the GMenuTreeEntry item. To fix the caching, we keep a simple hash table of desktop id -> ShellAppInfo.
This commit is contained in:
parent
72e6e7839f
commit
cc2d3fd56d
@ -41,7 +41,7 @@ fi
|
|||||||
|
|
||||||
AM_CONDITIONAL(BUILD_RECORDER, $build_recorder)
|
AM_CONDITIONAL(BUILD_RECORDER, $build_recorder)
|
||||||
|
|
||||||
PKG_CHECK_MODULES(MUTTER_PLUGIN, gtk+-2.0 dbus-glib-1 mutter-plugins gjs-gi-1.0 xscrnsaver libgnome-menu $recorder_modules gconf-2.0 gdk-x11-2.0 clutter-x11-0.9 clutter-glx-0.9)
|
PKG_CHECK_MODULES(MUTTER_PLUGIN, gio-unix-2.0 gtk+-2.0 dbus-glib-1 mutter-plugins gjs-gi-1.0 xscrnsaver libgnome-menu $recorder_modules gconf-2.0 gdk-x11-2.0 clutter-x11-0.9 clutter-glx-0.9)
|
||||||
PKG_CHECK_MODULES(TIDY, clutter-0.9)
|
PKG_CHECK_MODULES(TIDY, clutter-0.9)
|
||||||
PKG_CHECK_MODULES(BIG, clutter-0.9 gtk+-2.0 librsvg-2.0)
|
PKG_CHECK_MODULES(BIG, clutter-0.9 gtk+-2.0 librsvg-2.0)
|
||||||
PKG_CHECK_MODULES(GDMUSER, dbus-glib-1 gtk+-2.0)
|
PKG_CHECK_MODULES(GDMUSER, dbus-glib-1 gtk+-2.0)
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
jsmiscdir = $(pkgdatadir)/js/misc
|
jsmiscdir = $(pkgdatadir)/js/misc
|
||||||
|
|
||||||
dist_jsmisc_DATA = \
|
dist_jsmisc_DATA = \
|
||||||
appInfo.js \
|
|
||||||
docInfo.js
|
docInfo.js
|
||||||
|
@ -1,134 +0,0 @@
|
|||||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
|
||||||
|
|
||||||
const Clutter = imports.gi.Clutter;
|
|
||||||
const Gio = imports.gi.Gio;
|
|
||||||
const Gtk = imports.gi.Gtk;
|
|
||||||
const Shell = imports.gi.Shell;
|
|
||||||
|
|
||||||
const Main = imports.ui.main;
|
|
||||||
|
|
||||||
function AppInfo(appId) {
|
|
||||||
this._init(appId);
|
|
||||||
}
|
|
||||||
|
|
||||||
AppInfo.prototype = {
|
|
||||||
_init : function(appId) {
|
|
||||||
this.appId = appId;
|
|
||||||
this._gAppInfo = Gio.DesktopAppInfo.new(appId);
|
|
||||||
if (!this._gAppInfo) {
|
|
||||||
throw new Error('Unknown appId ' + appId);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.id = this._gAppInfo.get_id();
|
|
||||||
this.name = this._gAppInfo.get_name();
|
|
||||||
this.description = this._gAppInfo.get_description();
|
|
||||||
this.executable = this._gAppInfo.get_executable();
|
|
||||||
|
|
||||||
this._gicon = this._gAppInfo.get_icon();
|
|
||||||
},
|
|
||||||
|
|
||||||
createIcon : function(size) {
|
|
||||||
if (this._gicon)
|
|
||||||
return Shell.TextureCache.get_default().load_gicon(this._gicon, size);
|
|
||||||
else
|
|
||||||
return new Clutter.Texture({ width: size, height: size });
|
|
||||||
},
|
|
||||||
|
|
||||||
getIconPath : function(size) {
|
|
||||||
if (this._gicon) {
|
|
||||||
let iconTheme = Gtk.IconTheme.get_default();
|
|
||||||
let previewIconInfo = iconTheme.lookup_by_gicon(this._gicon, size, 0);
|
|
||||||
if (previewIconInfo)
|
|
||||||
return previewIconInfo.get_filename();
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
|
|
||||||
launch : function() {
|
|
||||||
this._gAppInfo.launch([], Main.createAppLaunchContext());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var _infos = {};
|
|
||||||
|
|
||||||
// getAppInfo:
|
|
||||||
// @appId: an appId
|
|
||||||
//
|
|
||||||
// Gets an #AppInfo for @appId. This is preferable to calling
|
|
||||||
// new AppInfo() directly, because it caches #AppInfos.
|
|
||||||
//
|
|
||||||
// Return value: the new or cached #AppInfo, or %null if @appId
|
|
||||||
// doesn't point to a valid .desktop file
|
|
||||||
function getAppInfo(appId) {
|
|
||||||
let info = _infos[appId];
|
|
||||||
if (info === undefined) {
|
|
||||||
try {
|
|
||||||
info = _infos[appId] = new AppInfo(appId);
|
|
||||||
} catch (e) {
|
|
||||||
info = _infos[appId] = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return info;
|
|
||||||
}
|
|
||||||
|
|
||||||
// getTopApps:
|
|
||||||
// @count: maximum number of apps to retrieve
|
|
||||||
//
|
|
||||||
// Gets a list of #AppInfos for the @count most-frequently-used
|
|
||||||
// applications, with explicitly-chosen favorites first.
|
|
||||||
//
|
|
||||||
// Return value: the list of #AppInfo
|
|
||||||
function getTopApps(count) {
|
|
||||||
let appMonitor = Shell.AppMonitor.get_default();
|
|
||||||
|
|
||||||
let matches = [], alreadyAdded = {};
|
|
||||||
|
|
||||||
let favs = getFavorites();
|
|
||||||
for (let i = 0; i < favs.length && favs.length <= count; i++) {
|
|
||||||
let appId = favs[i].appId;
|
|
||||||
|
|
||||||
if (alreadyAdded[appId])
|
|
||||||
continue;
|
|
||||||
alreadyAdded[appId] = true;
|
|
||||||
|
|
||||||
matches.push(favs[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ask for more apps than we need, since the list of recently used
|
|
||||||
// apps might contain an app we don't have a desktop file for
|
|
||||||
let apps = appMonitor.get_most_used_apps (0, Math.round(count * 1.5));
|
|
||||||
for (let i = 0; i < apps.length && matches.length <= count; i++) {
|
|
||||||
let appId = apps[i] + ".desktop";
|
|
||||||
if (alreadyAdded[appId])
|
|
||||||
continue;
|
|
||||||
alreadyAdded[appId] = true;
|
|
||||||
let appInfo = getAppInfo(appId);
|
|
||||||
if (appInfo) {
|
|
||||||
matches.push(appInfo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return matches;
|
|
||||||
}
|
|
||||||
|
|
||||||
function _idListToInfos(ids) {
|
|
||||||
let infos = [];
|
|
||||||
for (let i = 0; i < ids.length; i++) {
|
|
||||||
let display = getAppInfo(ids[i]);
|
|
||||||
if (display == null)
|
|
||||||
continue;
|
|
||||||
infos.push(display);
|
|
||||||
}
|
|
||||||
return infos;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getFavorites() {
|
|
||||||
let system = Shell.AppSystem.get_default();
|
|
||||||
|
|
||||||
return _idListToInfos(system.get_favorites());
|
|
||||||
}
|
|
||||||
|
|
||||||
function getRunning() {
|
|
||||||
let monitor = Shell.AppMonitor.get_default();
|
|
||||||
return _idListToInfos(monitor.get_running_app_ids());
|
|
||||||
}
|
|
@ -3,6 +3,7 @@
|
|||||||
const Big = imports.gi.Big;
|
const Big = imports.gi.Big;
|
||||||
const Clutter = imports.gi.Clutter;
|
const Clutter = imports.gi.Clutter;
|
||||||
const Pango = imports.gi.Pango;
|
const Pango = imports.gi.Pango;
|
||||||
|
const GLib = imports.gi.GLib;
|
||||||
const Gio = imports.gi.Gio;
|
const Gio = imports.gi.Gio;
|
||||||
const Gtk = imports.gi.Gtk;
|
const Gtk = imports.gi.Gtk;
|
||||||
const Tidy = imports.gi.Tidy;
|
const Tidy = imports.gi.Tidy;
|
||||||
@ -11,7 +12,6 @@ const Lang = imports.lang;
|
|||||||
const Signals = imports.signals;
|
const Signals = imports.signals;
|
||||||
const Mainloop = imports.mainloop;
|
const Mainloop = imports.mainloop;
|
||||||
|
|
||||||
const AppInfo = imports.misc.appInfo;
|
|
||||||
const DND = imports.ui.dnd;
|
const DND = imports.ui.dnd;
|
||||||
const GenericDisplay = imports.ui.genericDisplay;
|
const GenericDisplay = imports.ui.genericDisplay;
|
||||||
const Workspaces = imports.ui.workspaces;
|
const Workspaces = imports.ui.workspaces;
|
||||||
@ -43,11 +43,11 @@ AppDisplayItem.prototype = {
|
|||||||
GenericDisplay.GenericDisplayItem.prototype._init.call(this, availableWidth);
|
GenericDisplay.GenericDisplayItem.prototype._init.call(this, availableWidth);
|
||||||
this._appInfo = appInfo;
|
this._appInfo = appInfo;
|
||||||
|
|
||||||
this._setItemInfo(appInfo.name, appInfo.description);
|
this._setItemInfo(appInfo.get_name(), appInfo.get_description());
|
||||||
},
|
},
|
||||||
|
|
||||||
getId: function() {
|
getId: function() {
|
||||||
return this._appInfo.appId;
|
return this._appInfo.get_id();
|
||||||
},
|
},
|
||||||
|
|
||||||
//// Public method overrides ////
|
//// Public method overrides ////
|
||||||
@ -61,7 +61,7 @@ AppDisplayItem.prototype = {
|
|||||||
|
|
||||||
// Returns an icon for the item.
|
// Returns an icon for the item.
|
||||||
_createIcon : function() {
|
_createIcon : function() {
|
||||||
return this._appInfo.createIcon(GenericDisplay.ITEM_DISPLAY_ICON_SIZE);
|
return this._appInfo.create_icon_texture(GenericDisplay.ITEM_DISPLAY_ICON_SIZE);
|
||||||
},
|
},
|
||||||
|
|
||||||
// Ensures the preview icon is created.
|
// Ensures the preview icon is created.
|
||||||
@ -69,7 +69,15 @@ AppDisplayItem.prototype = {
|
|||||||
if (!this._showPreview || this._previewIcon)
|
if (!this._showPreview || this._previewIcon)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
let previewIconPath = this._appInfo.getIconPath(GenericDisplay.PREVIEW_ICON_SIZE);
|
let gicon = this._appInfo.get_icon();
|
||||||
|
let previewIconPath = null;
|
||||||
|
if (gicon) {
|
||||||
|
let iconTheme = Gtk.IconTheme.get_default();
|
||||||
|
let previewIconInfo = iconTheme.lookup_by_gicon(gicon, GenericDisplay.PREVIEW_ICON_SIZE, 0);
|
||||||
|
if (previewIconInfo)
|
||||||
|
previewIconPath = previewIconInfo.get_filename();
|
||||||
|
}
|
||||||
|
|
||||||
if (previewIconPath) {
|
if (previewIconPath) {
|
||||||
try {
|
try {
|
||||||
this._previewIcon = new Clutter.Texture({ width: GenericDisplay.PREVIEW_ICON_SIZE, height: GenericDisplay.PREVIEW_ICON_SIZE});
|
this._previewIcon = new Clutter.Texture({ width: GenericDisplay.PREVIEW_ICON_SIZE, height: GenericDisplay.PREVIEW_ICON_SIZE});
|
||||||
@ -200,7 +208,7 @@ AppDisplay.prototype = {
|
|||||||
this._redisplay(false);
|
this._redisplay(false);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Load the GAppInfos now so it doesn't slow down the first
|
// Load the apps now so it doesn't slow down the first
|
||||||
// transition into the overlay
|
// transition into the overlay
|
||||||
this._refreshCache();
|
this._refreshCache();
|
||||||
|
|
||||||
@ -257,7 +265,8 @@ AppDisplay.prototype = {
|
|||||||
// Protected overrides
|
// Protected overrides
|
||||||
|
|
||||||
_filterActive: function() {
|
_filterActive: function() {
|
||||||
return !!this._search || this._activeMenuIndex >= 0;
|
// We always have a filter now since a menu must be selected
|
||||||
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
_filterReset: function() {
|
_filterReset: function() {
|
||||||
@ -277,6 +286,12 @@ AppDisplay.prototype = {
|
|||||||
this._menuDisplays[index].setState(MENU_SELECTED);
|
this._menuDisplays[index].setState(MENU_SELECTED);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_getMostUsed: function() {
|
||||||
|
return this._appMonitor.get_most_used_apps(0, 30).map(Lang.bind(this, function (id) {
|
||||||
|
return this._appSystem.lookup_app(id + '.desktop');
|
||||||
|
})).filter(function (e) { return e != null });
|
||||||
|
},
|
||||||
|
|
||||||
_addMenuItem: function(name, id, icon, index) {
|
_addMenuItem: function(name, id, icon, index) {
|
||||||
let display = new MenuItem(name, id, icon);
|
let display = new MenuItem(name, id, icon);
|
||||||
this._menuDisplays.push(display);
|
this._menuDisplays.push(display);
|
||||||
@ -291,7 +306,7 @@ AppDisplay.prototype = {
|
|||||||
this._activeMenuIndex = index;
|
this._activeMenuIndex = index;
|
||||||
this._activeMenu = display;
|
this._activeMenu = display;
|
||||||
if (id == null) {
|
if (id == null) {
|
||||||
this._activeMenuApps = this._appMonitor.get_most_used_apps(0, 30);
|
this._activeMenuApps = this._getMostUsed();
|
||||||
} else {
|
} else {
|
||||||
this._activeMenuApps = this._appSystem.get_applications_for_menu(id);
|
this._activeMenuApps = this._appSystem.get_applications_for_menu(id);
|
||||||
}
|
}
|
||||||
@ -310,21 +325,9 @@ AppDisplay.prototype = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_addAppForId: function(appId) {
|
|
||||||
let appInfo = AppInfo.getAppInfo(appId);
|
|
||||||
if (appInfo != null) {
|
|
||||||
this._addApp(appInfo);
|
|
||||||
} else {
|
|
||||||
log("appInfo for " + appId + " was not found.");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_addApp: function(appInfo) {
|
_addApp: function(appInfo) {
|
||||||
let appId = appInfo.id;
|
let appId = appInfo.get_id();
|
||||||
this._allItems[appId] = appInfo;
|
this._allItems[appId] = appInfo;
|
||||||
// [] is returned if we could not get the categories or the list of categories was empty
|
|
||||||
let categories = Shell.get_categories_for_desktop_file(appId);
|
|
||||||
this._appCategories[appId] = categories;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
//// Protected method overrides ////
|
//// Protected method overrides ////
|
||||||
@ -338,15 +341,14 @@ AppDisplay.prototype = {
|
|||||||
this._appCategories = {};
|
this._appCategories = {};
|
||||||
|
|
||||||
this._menus = this._appSystem.get_menus();
|
this._menus = this._appSystem.get_menus();
|
||||||
|
|
||||||
// Loop over the toplevel menu items, load the set of desktop file ids
|
// Loop over the toplevel menu items, load the set of desktop file ids
|
||||||
// associated with each one
|
// associated with each one
|
||||||
for (let i = 0; i < this._menus.length; i++) {
|
for (let i = 0; i < this._menus.length; i++) {
|
||||||
let menu = this._menus[i];
|
let menu = this._menus[i];
|
||||||
let menuApps = this._appSystem.get_applications_for_menu(menu.id);
|
let menuApps = this._appSystem.get_applications_for_menu(menu.id);
|
||||||
for (let j = 0; j < menuApps.length; j++) {
|
for (let j = 0; j < menuApps.length; j++) {
|
||||||
let appId = menuApps[j];
|
let app = menuApps[j];
|
||||||
this._addAppForId(appId);
|
this._addApp(app);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -354,17 +356,16 @@ AppDisplay.prototype = {
|
|||||||
// These show up in search, but not with the rest of apps.
|
// These show up in search, but not with the rest of apps.
|
||||||
let settings = this._appSystem.get_all_settings();
|
let settings = this._appSystem.get_all_settings();
|
||||||
for (let i = 0; i < settings.length; i++) {
|
for (let i = 0; i < settings.length; i++) {
|
||||||
let appId = settings[i];
|
let app = settings[i];
|
||||||
this._addAppForId(appId);
|
this._addApp(app);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._appsStale = false;
|
this._appsStale = false;
|
||||||
},
|
},
|
||||||
|
|
||||||
// Sets the list of the displayed items based on the most used apps.
|
// Stub this out; the app display always has a category selected
|
||||||
_setDefaultList : function() {
|
_setDefaultList : function() {
|
||||||
let matchedInfos = AppInfo.getTopApps(MAX_ITEMS);
|
this._matchedItems = [];
|
||||||
this._matchedItems = matchedInfos.map(function(info) { return info.appId; });
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// Compares items associated with the item ids based on the alphabetical order
|
// Compares items associated with the item ids based on the alphabetical order
|
||||||
@ -373,14 +374,17 @@ AppDisplay.prototype = {
|
|||||||
_compareItems : function(itemIdA, itemIdB) {
|
_compareItems : function(itemIdA, itemIdB) {
|
||||||
let appA = this._allItems[itemIdA];
|
let appA = this._allItems[itemIdA];
|
||||||
let appB = this._allItems[itemIdB];
|
let appB = this._allItems[itemIdB];
|
||||||
return appA.name.localeCompare(appB.name);
|
return appA.get_name().localeCompare(appB.get_name());
|
||||||
},
|
},
|
||||||
|
|
||||||
// Checks if the item info can be a match for the search string by checking
|
// Checks if the item info can be a match for the search string by checking
|
||||||
// the name, description, execution command, and categories for the application.
|
// the name, description, execution command, and categories for the application.
|
||||||
// Item info is expected to be GAppInfo.
|
// Item info is expected to be Shell.AppInfo.
|
||||||
// Returns a boolean flag indicating if itemInfo is a match.
|
// Returns a boolean flag indicating if itemInfo is a match.
|
||||||
_isInfoMatching : function(itemInfo, search) {
|
_isInfoMatching : function(itemInfo, search) {
|
||||||
|
// Don't show nodisplay items here
|
||||||
|
if (itemInfo.get_is_nodisplay())
|
||||||
|
return false;
|
||||||
// Search takes precedence; not typically useful to search within a
|
// Search takes precedence; not typically useful to search within a
|
||||||
// menu
|
// menu
|
||||||
if (this._activeMenu == null || search != "")
|
if (this._activeMenu == null || search != "")
|
||||||
@ -390,10 +394,10 @@ AppDisplay.prototype = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
_isInfoMatchingMenu : function(itemInfo, search) {
|
_isInfoMatchingMenu : function(itemInfo, search) {
|
||||||
let id = itemInfo.id;
|
let id = itemInfo.get_id();
|
||||||
for (let i = 0; i < this._activeMenuApps.length; i++) {
|
for (let i = 0; i < this._activeMenuApps.length; i++) {
|
||||||
let activeId = this._activeMenuApps[i];
|
let activeApp = this._activeMenuApps[i];
|
||||||
if (activeId == id)
|
if (activeApp.get_id() == id)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@ -403,29 +407,34 @@ AppDisplay.prototype = {
|
|||||||
if (search == null || search == '')
|
if (search == null || search == '')
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
let name = itemInfo.name.toLowerCase();
|
let fold = function(s) {
|
||||||
|
return s;
|
||||||
|
if (!s)
|
||||||
|
return s;
|
||||||
|
return GLib.utf8_casefold(GLib.utf8_normalize(s, -1,
|
||||||
|
GLib.NormalizeMode.ALL), -1);
|
||||||
|
};
|
||||||
|
let name = fold(itemInfo.get_name());
|
||||||
if (name.indexOf(search) >= 0)
|
if (name.indexOf(search) >= 0)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
let description = itemInfo.description;
|
let description = fold(itemInfo.get_description());
|
||||||
if (description) {
|
if (description) {
|
||||||
description = description.toLowerCase();
|
|
||||||
if (description.indexOf(search) >= 0)
|
if (description.indexOf(search) >= 0)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (itemInfo.executable == null) {
|
let exec = fold(itemInfo.get_executable());
|
||||||
|
if (exec == null) {
|
||||||
log("Missing an executable for " + itemInfo.name);
|
log("Missing an executable for " + itemInfo.name);
|
||||||
} else {
|
} else {
|
||||||
let exec = itemInfo.executable.toLowerCase();
|
|
||||||
if (exec.indexOf(search) >= 0)
|
if (exec.indexOf(search) >= 0)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// we expect this._appCategories.hasOwnProperty(itemInfo.id) to always be true here
|
let categories = itemInfo.get_categories();
|
||||||
let categories = this._appCategories[itemInfo.id];
|
|
||||||
for (let i = 0; i < categories.length; i++) {
|
for (let i = 0; i < categories.length; i++) {
|
||||||
let category = categories[i].toLowerCase();
|
let category = fold(categories[i]);
|
||||||
if (category.indexOf(search) >= 0)
|
if (category.indexOf(search) >= 0)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -433,7 +442,7 @@ AppDisplay.prototype = {
|
|||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
|
|
||||||
// Creates an AppDisplayItem based on itemInfo, which is expected be an AppInfo object.
|
// Creates an AppDisplayItem based on itemInfo, which is expected be an Shell.AppInfo object.
|
||||||
_createDisplayItem: function(itemInfo, width) {
|
_createDisplayItem: function(itemInfo, width) {
|
||||||
return new AppDisplayItem(itemInfo, width);
|
return new AppDisplayItem(itemInfo, width);
|
||||||
}
|
}
|
||||||
@ -479,12 +488,12 @@ WellDisplayItem.prototype = {
|
|||||||
|
|
||||||
let iconBox = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
|
let iconBox = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
|
||||||
x_align: Big.BoxAlignment.CENTER });
|
x_align: Big.BoxAlignment.CENTER });
|
||||||
this._icon = appInfo.createIcon(APP_ICON_SIZE);
|
this._icon = appInfo.create_icon_texture(APP_ICON_SIZE);
|
||||||
iconBox.append(this._icon, Big.BoxPackFlags.NONE);
|
iconBox.append(this._icon, Big.BoxPackFlags.NONE);
|
||||||
|
|
||||||
this.actor.append(iconBox, Big.BoxPackFlags.NONE);
|
this.actor.append(iconBox, Big.BoxPackFlags.NONE);
|
||||||
|
|
||||||
this._windows = Shell.AppMonitor.get_default().get_windows_for_app(appInfo.appId)
|
this._windows = Shell.AppMonitor.get_default().get_windows_for_app(appInfo.get_id());
|
||||||
|
|
||||||
let nameBox = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
|
let nameBox = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
|
||||||
x_align: Big.BoxAlignment.CENTER });
|
x_align: Big.BoxAlignment.CENTER });
|
||||||
@ -494,7 +503,7 @@ WellDisplayItem.prototype = {
|
|||||||
line_alignment: Pango.Alignment.CENTER,
|
line_alignment: Pango.Alignment.CENTER,
|
||||||
line_wrap: true,
|
line_wrap: true,
|
||||||
line_wrap_mode: Pango.WrapMode.WORD_CHAR,
|
line_wrap_mode: Pango.WrapMode.WORD_CHAR,
|
||||||
text: appInfo.name });
|
text: appInfo.get_name() });
|
||||||
nameBox.append(this._name, Big.BoxPackFlags.EXPAND);
|
nameBox.append(this._name, Big.BoxPackFlags.EXPAND);
|
||||||
if (this._windows.length > 0) {
|
if (this._windows.length > 0) {
|
||||||
let runningBox = new Big.Box({ /* border_color: GenericDisplay.ITEM_DISPLAY_NAME_COLOR,
|
let runningBox = new Big.Box({ /* border_color: GenericDisplay.ITEM_DISPLAY_NAME_COLOR,
|
||||||
@ -524,7 +533,7 @@ WellDisplayItem.prototype = {
|
|||||||
|
|
||||||
// Draggable interface - FIXME deduplicate with GenericDisplay
|
// Draggable interface - FIXME deduplicate with GenericDisplay
|
||||||
getDragActor: function(stageX, stageY) {
|
getDragActor: function(stageX, stageY) {
|
||||||
this.dragActor = this.appInfo.createIcon(APP_ICON_SIZE);
|
this.dragActor = this.appInfo.create_icon_texture(APP_ICON_SIZE);
|
||||||
|
|
||||||
// If the user dragged from the icon itself, then position
|
// If the user dragged from the icon itself, then position
|
||||||
// the dragActor over the original icon. Otherwise center it
|
// the dragActor over the original icon. Otherwise center it
|
||||||
@ -571,12 +580,13 @@ WellArea.prototype = {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
for (let i = 0; i < infos.length; i++) {
|
for (let i = 0; i < infos.length; i++) {
|
||||||
let display = new WellDisplayItem(infos[i], this.isFavorite);
|
let app = infos[i];
|
||||||
|
let display = new WellDisplayItem(app, this.isFavorite);
|
||||||
display.connect('activated', Lang.bind(this, function (display) {
|
display.connect('activated', Lang.bind(this, function (display) {
|
||||||
this.emit('activated', display);
|
this.emit('activated', display);
|
||||||
}));
|
}));
|
||||||
this.actor.add_actor(display.actor);
|
this.actor.add_actor(display.actor);
|
||||||
};
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// Draggable target interface
|
// Draggable target interface
|
||||||
@ -585,12 +595,13 @@ WellArea.prototype = {
|
|||||||
|
|
||||||
let id = null;
|
let id = null;
|
||||||
if (source instanceof WellDisplayItem) {
|
if (source instanceof WellDisplayItem) {
|
||||||
id = source.appInfo.appId;
|
id = source.appInfo.get_id();
|
||||||
} else if (source instanceof AppDisplayItem) {
|
} else if (source instanceof AppDisplayItem) {
|
||||||
id = source.getId();
|
id = source.getId();
|
||||||
} else if (source instanceof Workspaces.WindowClone) {
|
} else if (source instanceof Workspaces.WindowClone) {
|
||||||
let appMonitor = Shell.AppMonitor.get_default();
|
let appMonitor = Shell.AppMonitor.get_default();
|
||||||
id = appMonitor.get_window_id(source.metaWindow);
|
let app = appMonitor.get_window_app(source.metaWindow);
|
||||||
|
id = app.get_id();
|
||||||
if (id === null)
|
if (id === null)
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
@ -663,6 +674,14 @@ AppWell.prototype = {
|
|||||||
this._redisplay();
|
this._redisplay();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_lookupApp: function(id) {
|
||||||
|
let app = this._appSystem.lookup_app(id);
|
||||||
|
if (!app) {
|
||||||
|
log("failed to look up app " + id);
|
||||||
|
};
|
||||||
|
return app;
|
||||||
|
},
|
||||||
|
|
||||||
_redisplay: function() {
|
_redisplay: function() {
|
||||||
let arrayToObject = function(a) {
|
let arrayToObject = function(a) {
|
||||||
let o = {};
|
let o = {};
|
||||||
@ -670,11 +689,13 @@ AppWell.prototype = {
|
|||||||
o[a[i]] = 1;
|
o[a[i]] = 1;
|
||||||
return o;
|
return o;
|
||||||
};
|
};
|
||||||
let favorites = AppInfo.getFavorites();
|
let favoriteIds = this._appSystem.get_favorites();
|
||||||
let favoriteIds = arrayToObject(favorites.map(function (e) { return e.appId; }));
|
let favoriteIdsObject = arrayToObject(favoriteIds);
|
||||||
let running = AppInfo.getRunning().filter(function (e) {
|
let runningIds = this._appMonitor.get_running_app_ids().filter(function (e) {
|
||||||
return !(e.appId in favoriteIds);
|
return !(e in favoriteIdsObject);
|
||||||
});
|
});
|
||||||
|
let favorites = favoriteIds.map(Lang.bind(this, this._lookupApp));
|
||||||
|
let running = runningIds.map(Lang.bind(this, this._lookupApp));
|
||||||
this._favoritesArea.redisplay(favorites);
|
this._favoritesArea.redisplay(favorites);
|
||||||
this._runningArea.redisplay(running);
|
this._runningArea.redisplay(running);
|
||||||
// If it's empty, we hide it so the top border doesn't show up
|
// If it's empty, we hide it so the top border doesn't show up
|
||||||
|
@ -10,7 +10,6 @@ const Pango = imports.gi.Pango;
|
|||||||
const Shell = imports.gi.Shell;
|
const Shell = imports.gi.Shell;
|
||||||
const Signals = imports.signals;
|
const Signals = imports.signals;
|
||||||
|
|
||||||
const AppInfo = imports.misc.appInfo;
|
|
||||||
const DocDisplay = imports.ui.docDisplay;
|
const DocDisplay = imports.ui.docDisplay;
|
||||||
const DocInfo = imports.misc.docInfo;
|
const DocInfo = imports.misc.docInfo;
|
||||||
|
|
||||||
@ -284,6 +283,24 @@ LauncherWidget.prototype = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function AppsWidgetInfo(appInfo) {
|
||||||
|
this._init(appInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
AppsWidgetInfo.prototype = {
|
||||||
|
_init : function(appInfo) {
|
||||||
|
this._info = appInfo;
|
||||||
|
},
|
||||||
|
|
||||||
|
createIcon : function(size) {
|
||||||
|
return this._info.create_icon_texture(size);
|
||||||
|
},
|
||||||
|
|
||||||
|
launch : function() {
|
||||||
|
this._info.launch();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function AppsWidget() {
|
function AppsWidget() {
|
||||||
this._init.apply(this, arguments);
|
this._init.apply(this, arguments);
|
||||||
}
|
}
|
||||||
@ -298,9 +315,12 @@ AppsWidget.prototype = {
|
|||||||
this.actor = new Big.Box({ spacing: 2 });
|
this.actor = new Big.Box({ spacing: 2 });
|
||||||
this.collapsedActor = new Big.Box({ spacing: 2});
|
this.collapsedActor = new Big.Box({ spacing: 2});
|
||||||
|
|
||||||
let apps = AppInfo.getTopApps(5);
|
let appSystem = Shell.AppSystem.get_default();
|
||||||
for (let i = 0; i < apps.length; i++)
|
let apps = appSystem.get_favorites();
|
||||||
this.addItem(apps[i]);
|
for (let i = 0; i < apps.length; i++) {
|
||||||
|
let app = appSystem.lookup_app(apps[i]);
|
||||||
|
this.addItem(new AppsWidgetInfo(app));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -99,8 +99,8 @@ struct _ShellAppMonitor
|
|||||||
/* <char * appid, guint window_count> */
|
/* <char * appid, guint window_count> */
|
||||||
GHashTable *running_appids;
|
GHashTable *running_appids;
|
||||||
|
|
||||||
/* <MetaWindow * window, char * appid> */
|
/* <MetaWindow * window, ShellAppInfo *app> */
|
||||||
GHashTable *window_to_appid;
|
GHashTable *window_to_app;
|
||||||
|
|
||||||
GHashTable *apps_by_wm_class; /* Seen apps by wm_class */
|
GHashTable *apps_by_wm_class; /* Seen apps by wm_class */
|
||||||
GHashTable *popularities; /* One AppPopularity struct list per activity */
|
GHashTable *popularities; /* One AppPopularity struct list per activity */
|
||||||
@ -269,17 +269,17 @@ get_cleaned_wmclass_for_window (MetaWindow *window)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get_appid_for_window:
|
* get_app_for_window:
|
||||||
*
|
*
|
||||||
* Returns a desktop file ID for an application, or %NULL if
|
* Returns the application associated with a window, or %NULL if
|
||||||
* we're unable to determine one.
|
* we're unable to determine one.
|
||||||
*/
|
*/
|
||||||
static char *
|
static ShellAppInfo *
|
||||||
get_appid_for_window (MetaWindow *window)
|
get_app_for_window (MetaWindow *window)
|
||||||
{
|
{
|
||||||
char *wmclass;
|
char *wmclass;
|
||||||
char *with_desktop;
|
char *with_desktop;
|
||||||
char *result;
|
ShellAppInfo *result;
|
||||||
ShellAppSystem *appsys;
|
ShellAppSystem *appsys;
|
||||||
|
|
||||||
wmclass = get_cleaned_wmclass_for_window (window);
|
wmclass = get_cleaned_wmclass_for_window (window);
|
||||||
@ -291,7 +291,7 @@ get_appid_for_window (MetaWindow *window)
|
|||||||
g_free (wmclass);
|
g_free (wmclass);
|
||||||
|
|
||||||
appsys = shell_app_system_get_default ();
|
appsys = shell_app_system_get_default ();
|
||||||
result = shell_app_system_lookup_basename (appsys, with_desktop);
|
result = shell_app_system_lookup_heuristic_basename (appsys, with_desktop);
|
||||||
g_free (with_desktop);
|
g_free (with_desktop);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@ -301,14 +301,18 @@ static void
|
|||||||
track_window (ShellAppMonitor *self,
|
track_window (ShellAppMonitor *self,
|
||||||
MetaWindow *window)
|
MetaWindow *window)
|
||||||
{
|
{
|
||||||
char *appid;
|
ShellAppInfo *app;
|
||||||
guint window_count;
|
guint window_count;
|
||||||
|
const char *appid;
|
||||||
|
|
||||||
appid = get_appid_for_window (window);
|
app = get_app_for_window (window);
|
||||||
if (!appid)
|
if (!app)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
g_hash_table_insert (self->window_to_appid, window, appid);
|
/* Steal the ref */
|
||||||
|
g_hash_table_insert (self->window_to_app, window, app);
|
||||||
|
|
||||||
|
appid = shell_app_info_get_id (app);
|
||||||
|
|
||||||
window_count = GPOINTER_TO_UINT (g_hash_table_lookup (self->running_appids, appid));
|
window_count = GPOINTER_TO_UINT (g_hash_table_lookup (self->running_appids, appid));
|
||||||
|
|
||||||
@ -334,13 +338,15 @@ shell_app_monitor_on_window_removed (MetaWorkspace *workspace,
|
|||||||
gpointer user_data)
|
gpointer user_data)
|
||||||
{
|
{
|
||||||
ShellAppMonitor *self = SHELL_APP_MONITOR (user_data);
|
ShellAppMonitor *self = SHELL_APP_MONITOR (user_data);
|
||||||
char *appid;
|
ShellAppInfo *app;
|
||||||
|
const char *appid;
|
||||||
guint window_count;
|
guint window_count;
|
||||||
|
|
||||||
appid = g_hash_table_lookup (self->window_to_appid, window);
|
app = g_hash_table_lookup (self->window_to_app, window);
|
||||||
if (!appid)
|
if (!app)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
appid = shell_app_info_get_id (app);
|
||||||
window_count = GPOINTER_TO_UINT (g_hash_table_lookup (self->running_appids, appid));
|
window_count = GPOINTER_TO_UINT (g_hash_table_lookup (self->running_appids, appid));
|
||||||
|
|
||||||
window_count -= 1;
|
window_count -= 1;
|
||||||
@ -354,7 +360,7 @@ shell_app_monitor_on_window_removed (MetaWorkspace *workspace,
|
|||||||
g_hash_table_insert (self->running_appids, g_strdup (appid),
|
g_hash_table_insert (self->running_appids, g_strdup (appid),
|
||||||
GUINT_TO_POINTER (window_count));
|
GUINT_TO_POINTER (window_count));
|
||||||
}
|
}
|
||||||
g_hash_table_remove (self->window_to_appid, window);
|
g_hash_table_remove (self->window_to_app, window);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -392,12 +398,13 @@ shell_app_monitor_get_windows_for_app (ShellAppMonitor *self,
|
|||||||
gpointer key, value;
|
gpointer key, value;
|
||||||
GSList *ret = NULL;
|
GSList *ret = NULL;
|
||||||
|
|
||||||
g_hash_table_iter_init (&iter, self->window_to_appid);
|
g_hash_table_iter_init (&iter, self->window_to_app);
|
||||||
|
|
||||||
while (g_hash_table_iter_next (&iter, &key, &value))
|
while (g_hash_table_iter_next (&iter, &key, &value))
|
||||||
{
|
{
|
||||||
MetaWindow *window = key;
|
MetaWindow *window = key;
|
||||||
const char *id = value;
|
ShellAppInfo *app = value;
|
||||||
|
const char *id = shell_app_info_get_id (app);
|
||||||
|
|
||||||
if (strcmp (id, appid) != 0)
|
if (strcmp (id, appid) != 0)
|
||||||
continue;
|
continue;
|
||||||
@ -485,8 +492,8 @@ shell_app_monitor_init (ShellAppMonitor *self)
|
|||||||
self->running_appids = g_hash_table_new_full (g_str_hash, g_str_equal,
|
self->running_appids = g_hash_table_new_full (g_str_hash, g_str_equal,
|
||||||
g_free, NULL);
|
g_free, NULL);
|
||||||
|
|
||||||
self->window_to_appid = g_hash_table_new_full (g_direct_hash, g_direct_equal,
|
self->window_to_app = g_hash_table_new_full (g_direct_hash, g_direct_equal,
|
||||||
NULL, (GDestroyNotify) g_free);
|
NULL, (GDestroyNotify) shell_app_info_unref);
|
||||||
|
|
||||||
load_initial_windows (self);
|
load_initial_windows (self);
|
||||||
init_window_monitoring (self);
|
init_window_monitoring (self);
|
||||||
@ -568,16 +575,19 @@ shell_app_monitor_get_most_used_apps (ShellAppMonitor *monitor,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* shell_app_monitor_get_window_id
|
* shell_app_monitor_get_window_app
|
||||||
* @monitor: An app monitor instance
|
* @monitor: An app monitor instance
|
||||||
* @metawin: A #MetaWindow
|
* @metawin: A #MetaWindow
|
||||||
*
|
*
|
||||||
* Returns: (transfer full): Desktop file id associated with window
|
* Returns: Desktop file id associated with window
|
||||||
*/
|
*/
|
||||||
char *
|
ShellAppInfo *
|
||||||
shell_app_monitor_get_window_id (ShellAppMonitor *monitor, MetaWindow *metawin)
|
shell_app_monitor_get_window_app (ShellAppMonitor *monitor, MetaWindow *metawin)
|
||||||
{
|
{
|
||||||
return g_hash_table_lookup (monitor->window_to_appid, metawin);
|
ShellAppInfo *info = g_hash_table_lookup (monitor->window_to_app, metawin);
|
||||||
|
if (info)
|
||||||
|
shell_app_info_ref (info);
|
||||||
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -4,9 +4,15 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include <gio/gio.h>
|
#include <gio/gio.h>
|
||||||
|
#include <gio/gdesktopappinfo.h>
|
||||||
|
#include <gtk/gtk.h>
|
||||||
#include <gconf/gconf.h>
|
#include <gconf/gconf.h>
|
||||||
#include <gconf/gconf-client.h>
|
#include <gconf/gconf-client.h>
|
||||||
|
|
||||||
|
#include "shell-global.h"
|
||||||
|
#include "shell-texture-cache.h"
|
||||||
|
#include "display.h"
|
||||||
|
|
||||||
#define GMENU_I_KNOW_THIS_IS_UNSTABLE
|
#define GMENU_I_KNOW_THIS_IS_UNSTABLE
|
||||||
#include <gmenu-tree.h>
|
#include <gmenu-tree.h>
|
||||||
|
|
||||||
@ -29,9 +35,11 @@ struct _ShellAppSystemPrivate {
|
|||||||
GMenuTree *apps_tree;
|
GMenuTree *apps_tree;
|
||||||
GMenuTree *settings_tree;
|
GMenuTree *settings_tree;
|
||||||
|
|
||||||
|
GHashTable *app_id_to_app;
|
||||||
|
|
||||||
GSList *cached_app_menus; /* ShellAppMenuEntry */
|
GSList *cached_app_menus; /* ShellAppMenuEntry */
|
||||||
|
|
||||||
GSList *cached_setting_ids; /* utf8 */
|
GSList *cached_settings; /* ShellAppInfo */
|
||||||
|
|
||||||
GHashTable *cached_favorites; /* <utf8,integer> */
|
GHashTable *cached_favorites; /* <utf8,integer> */
|
||||||
|
|
||||||
@ -46,6 +54,18 @@ static void reread_favorite_apps (ShellAppSystem *system);
|
|||||||
|
|
||||||
G_DEFINE_TYPE(ShellAppSystem, shell_app_system, G_TYPE_OBJECT);
|
G_DEFINE_TYPE(ShellAppSystem, shell_app_system, G_TYPE_OBJECT);
|
||||||
|
|
||||||
|
ShellAppInfo*
|
||||||
|
shell_app_info_ref (ShellAppInfo *info)
|
||||||
|
{
|
||||||
|
return gmenu_tree_item_ref ((GMenuTreeItem*)info);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
shell_app_info_unref (ShellAppInfo *info)
|
||||||
|
{
|
||||||
|
gmenu_tree_item_unref ((GMenuTreeItem *)info);
|
||||||
|
}
|
||||||
|
|
||||||
static gpointer
|
static gpointer
|
||||||
shell_app_menu_entry_copy (gpointer entryp)
|
shell_app_menu_entry_copy (gpointer entryp)
|
||||||
{
|
{
|
||||||
@ -109,6 +129,10 @@ shell_app_system_init (ShellAppSystem *self)
|
|||||||
(GDestroyNotify)g_free,
|
(GDestroyNotify)g_free,
|
||||||
NULL);
|
NULL);
|
||||||
|
|
||||||
|
/* The key is owned by the value */
|
||||||
|
priv->app_id_to_app = g_hash_table_new_full (g_str_hash, g_str_equal,
|
||||||
|
NULL, (GDestroyNotify) shell_app_info_unref);
|
||||||
|
|
||||||
/* For now, we want to pick up Evince, Nautilus, etc. We'll
|
/* For now, we want to pick up Evince, Nautilus, etc. We'll
|
||||||
* handle NODISPLAY semantics at a higher level or investigate them
|
* handle NODISPLAY semantics at a higher level or investigate them
|
||||||
* case by case.
|
* case by case.
|
||||||
@ -140,13 +164,15 @@ shell_app_system_finalize (GObject *object)
|
|||||||
gmenu_tree_unref (priv->apps_tree);
|
gmenu_tree_unref (priv->apps_tree);
|
||||||
gmenu_tree_unref (priv->settings_tree);
|
gmenu_tree_unref (priv->settings_tree);
|
||||||
|
|
||||||
|
g_hash_table_destroy (priv->app_id_to_app);
|
||||||
|
|
||||||
g_slist_foreach (priv->cached_app_menus, (GFunc)shell_app_menu_entry_free, NULL);
|
g_slist_foreach (priv->cached_app_menus, (GFunc)shell_app_menu_entry_free, NULL);
|
||||||
g_slist_free (priv->cached_app_menus);
|
g_slist_free (priv->cached_app_menus);
|
||||||
priv->cached_app_menus = NULL;
|
priv->cached_app_menus = NULL;
|
||||||
|
|
||||||
g_slist_foreach (priv->cached_setting_ids, (GFunc)g_free, NULL);
|
g_slist_foreach (priv->cached_settings, (GFunc)shell_app_info_unref, NULL);
|
||||||
g_slist_free (priv->cached_setting_ids);
|
g_slist_free (priv->cached_settings);
|
||||||
priv->cached_setting_ids = NULL;
|
priv->cached_settings = NULL;
|
||||||
|
|
||||||
g_hash_table_destroy (priv->cached_favorites);
|
g_hash_table_destroy (priv->cached_favorites);
|
||||||
|
|
||||||
@ -200,7 +226,7 @@ reread_directories (ShellAppSystem *self, GSList **cache, GMenuTree *tree)
|
|||||||
|
|
||||||
static GSList *
|
static GSList *
|
||||||
gather_entries_recurse (ShellAppSystem *monitor,
|
gather_entries_recurse (ShellAppSystem *monitor,
|
||||||
GSList *ids,
|
GSList *apps,
|
||||||
GMenuTreeDirectory *root)
|
GMenuTreeDirectory *root)
|
||||||
{
|
{
|
||||||
GSList *contents;
|
GSList *contents;
|
||||||
@ -215,27 +241,25 @@ gather_entries_recurse (ShellAppSystem *monitor,
|
|||||||
{
|
{
|
||||||
case GMENU_TREE_ITEM_ENTRY:
|
case GMENU_TREE_ITEM_ENTRY:
|
||||||
{
|
{
|
||||||
GMenuTreeEntry *entry = (GMenuTreeEntry *)item;
|
apps = g_slist_prepend (apps, item);
|
||||||
const char *id = gmenu_tree_entry_get_desktop_file_id (entry);
|
|
||||||
if (!gmenu_tree_entry_get_is_nodisplay (entry))
|
|
||||||
ids = g_slist_prepend (ids, g_strdup (id));
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case GMENU_TREE_ITEM_DIRECTORY:
|
case GMENU_TREE_ITEM_DIRECTORY:
|
||||||
{
|
{
|
||||||
GMenuTreeDirectory *dir = (GMenuTreeDirectory*)item;
|
GMenuTreeDirectory *dir = (GMenuTreeDirectory*)item;
|
||||||
ids = gather_entries_recurse (monitor, ids, dir);
|
apps = gather_entries_recurse (monitor, apps, dir);
|
||||||
}
|
}
|
||||||
|
gmenu_tree_item_unref (item);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
gmenu_tree_item_unref (item);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
gmenu_tree_item_unref (item);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
g_slist_free (contents);
|
g_slist_free (contents);
|
||||||
|
|
||||||
return ids;
|
return apps;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -247,7 +271,7 @@ reread_entries (ShellAppSystem *self,
|
|||||||
|
|
||||||
trunk = gmenu_tree_get_root_directory (tree);
|
trunk = gmenu_tree_get_root_directory (tree);
|
||||||
|
|
||||||
g_slist_foreach (*cache, (GFunc)g_free, NULL);
|
g_slist_foreach (*cache, (GFunc)shell_app_info_unref, NULL);
|
||||||
g_slist_free (*cache);
|
g_slist_free (*cache);
|
||||||
*cache = NULL;
|
*cache = NULL;
|
||||||
|
|
||||||
@ -256,11 +280,41 @@ reread_entries (ShellAppSystem *self,
|
|||||||
gmenu_tree_item_unref (trunk);
|
gmenu_tree_item_unref (trunk);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
cache_by_id (ShellAppSystem *self, GSList *apps, gboolean ref)
|
||||||
|
{
|
||||||
|
GSList *iter;
|
||||||
|
|
||||||
|
for (iter = apps; iter; iter = iter->next)
|
||||||
|
{
|
||||||
|
ShellAppInfo *info = iter->data;
|
||||||
|
if (ref)
|
||||||
|
gmenu_tree_item_ref ((GMenuTreeItem*) info);
|
||||||
|
/* the name is owned by the info itself */
|
||||||
|
g_hash_table_insert (self->priv->app_id_to_app, (char*)shell_app_info_get_id (info),
|
||||||
|
info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
reread_menus (ShellAppSystem *self)
|
reread_menus (ShellAppSystem *self)
|
||||||
{
|
{
|
||||||
|
GSList *apps;
|
||||||
|
GMenuTreeDirectory *trunk;
|
||||||
|
|
||||||
reread_directories (self, &(self->priv->cached_app_menus), self->priv->apps_tree);
|
reread_directories (self, &(self->priv->cached_app_menus), self->priv->apps_tree);
|
||||||
reread_entries (self, &(self->priv->cached_setting_ids), self->priv->settings_tree);
|
|
||||||
|
reread_entries (self, &(self->priv->cached_settings), self->priv->settings_tree);
|
||||||
|
|
||||||
|
/* Now loop over applications.menu and settings.menu, inserting each by desktop file
|
||||||
|
* ID into a hash */
|
||||||
|
g_hash_table_remove_all (self->priv->app_id_to_app);
|
||||||
|
trunk = gmenu_tree_get_root_directory (self->priv->apps_tree);
|
||||||
|
apps = gather_entries_recurse (self, NULL, trunk);
|
||||||
|
gmenu_tree_item_unref (trunk);
|
||||||
|
cache_by_id (self, apps, FALSE);
|
||||||
|
g_slist_free (apps);
|
||||||
|
cache_by_id (self, self->priv->cached_settings, TRUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -320,6 +374,19 @@ on_favorite_apps_changed (GConfClient *client,
|
|||||||
g_signal_emit (G_OBJECT (system), signals[FAVORITES_CHANGED], 0);
|
g_signal_emit (G_OBJECT (system), signals[FAVORITES_CHANGED], 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GType
|
||||||
|
shell_app_info_get_type (void)
|
||||||
|
{
|
||||||
|
static GType gtype = G_TYPE_INVALID;
|
||||||
|
if (gtype == G_TYPE_INVALID)
|
||||||
|
{
|
||||||
|
gtype = g_boxed_type_register_static ("ShellAppInfo",
|
||||||
|
(GBoxedCopyFunc)shell_app_info_ref,
|
||||||
|
(GBoxedFreeFunc)shell_app_info_unref);
|
||||||
|
}
|
||||||
|
return gtype;
|
||||||
|
}
|
||||||
|
|
||||||
GType
|
GType
|
||||||
shell_app_menu_entry_get_type (void)
|
shell_app_menu_entry_get_type (void)
|
||||||
{
|
{
|
||||||
@ -339,7 +406,7 @@ shell_app_menu_entry_get_type (void)
|
|||||||
* Traverses a toplevel menu, and returns all items under it. Nested items
|
* Traverses a toplevel menu, and returns all items under it. Nested items
|
||||||
* are flattened.
|
* are flattened.
|
||||||
*
|
*
|
||||||
* Return value: (transfer full) (element-type utf8): List of desktop file ids
|
* Return value: (transfer container) (element-type ShellAppInfo): List of applications
|
||||||
*/
|
*/
|
||||||
GSList *
|
GSList *
|
||||||
shell_app_system_get_applications_for_menu (ShellAppSystem *monitor,
|
shell_app_system_get_applications_for_menu (ShellAppSystem *monitor,
|
||||||
@ -364,7 +431,7 @@ shell_app_system_get_applications_for_menu (ShellAppSystem *monitor,
|
|||||||
/**
|
/**
|
||||||
* shell_app_system_get_menus:
|
* shell_app_system_get_menus:
|
||||||
*
|
*
|
||||||
* Returns a list of toplevel menu names, like "Accessories", "Programming", etc.
|
* Returns a list of toplevel #ShellAppMenuEntry items
|
||||||
*
|
*
|
||||||
* Return value: (transfer none) (element-type AppMenuEntry): List of toplevel menus
|
* Return value: (transfer none) (element-type AppMenuEntry): List of toplevel menus
|
||||||
*/
|
*/
|
||||||
@ -377,14 +444,14 @@ shell_app_system_get_menus (ShellAppSystem *monitor)
|
|||||||
/**
|
/**
|
||||||
* shell_app_system_get_all_settings:
|
* shell_app_system_get_all_settings:
|
||||||
*
|
*
|
||||||
* Returns a list of all desktop file ids under "settings.menu".
|
* Returns a list of application items under "settings.menu".
|
||||||
*
|
*
|
||||||
* Return value: (transfer none) (element-type utf8): List of desktop file ids
|
* Return value: (transfer none) (element-type ShellAppInfo): List of applications
|
||||||
*/
|
*/
|
||||||
GSList *
|
GSList *
|
||||||
shell_app_system_get_all_settings (ShellAppSystem *monitor)
|
shell_app_system_get_all_settings (ShellAppSystem *monitor)
|
||||||
{
|
{
|
||||||
return monitor->priv->cached_setting_ids;
|
return monitor->priv->cached_settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -475,87 +542,225 @@ shell_app_system_remove_favorite (ShellAppSystem *system, const char *id)
|
|||||||
gconf_client_set (client, SHELL_APP_FAVORITES_KEY, val, NULL);
|
gconf_client_set (client, SHELL_APP_FAVORITES_KEY, val, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
/**
|
||||||
desktop_id_exists (ShellAppSystem *system,
|
* shell_app_system_lookup_app:
|
||||||
const char *target_id,
|
*
|
||||||
GMenuTreeDirectory *root)
|
* Return value: (transfer full): The #ShellAppInfo for id, or %NULL if none
|
||||||
|
*/
|
||||||
|
ShellAppInfo *
|
||||||
|
shell_app_system_lookup_app (ShellAppSystem *self, const char *id)
|
||||||
{
|
{
|
||||||
gboolean found = FALSE;
|
GMenuTreeEntry *entry;
|
||||||
GSList *contents, *iter;
|
|
||||||
|
|
||||||
contents = gmenu_tree_directory_get_contents (root);
|
entry = g_hash_table_lookup (self->priv->app_id_to_app, id);
|
||||||
|
if (entry)
|
||||||
for (iter = contents; iter; iter = iter->next)
|
gmenu_tree_item_ref ((GMenuTreeItem*) entry);
|
||||||
{
|
return (ShellAppInfo*)entry;
|
||||||
GMenuTreeItem *item = iter->data;
|
|
||||||
|
|
||||||
if (found)
|
|
||||||
break;
|
|
||||||
|
|
||||||
switch (gmenu_tree_item_get_type (item))
|
|
||||||
{
|
|
||||||
case GMENU_TREE_ITEM_ENTRY:
|
|
||||||
{
|
|
||||||
GMenuTreeEntry *entry = (GMenuTreeEntry *)item;
|
|
||||||
const char *id = gmenu_tree_entry_get_desktop_file_id (entry);
|
|
||||||
if (strcmp (id, target_id) == 0)
|
|
||||||
found = TRUE;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case GMENU_TREE_ITEM_DIRECTORY:
|
|
||||||
{
|
|
||||||
GMenuTreeDirectory *dir = (GMenuTreeDirectory*)item;
|
|
||||||
found = desktop_id_exists (system, target_id, dir);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
gmenu_tree_item_unref (item);
|
|
||||||
}
|
|
||||||
|
|
||||||
g_slist_free (contents);
|
|
||||||
|
|
||||||
return found;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* shell_app_system_lookup_basename:
|
* shell_app_system_lookup_heuristic_basename:
|
||||||
* @name: Probable application identifier
|
* @name: Probable application identifier
|
||||||
*
|
*
|
||||||
* Determine whether a valid .desktop file ID corresponding to a given
|
* Find a valid application corresponding to a given
|
||||||
* heuristically determined application identifier
|
* heuristically determined application identifier
|
||||||
* string.
|
* string, or %NULL if none.
|
||||||
|
*
|
||||||
|
* Returns: (transfer full): A #ShellAppInfo for name
|
||||||
*/
|
*/
|
||||||
char *
|
ShellAppInfo *
|
||||||
shell_app_system_lookup_basename (ShellAppSystem *system,
|
shell_app_system_lookup_heuristic_basename (ShellAppSystem *system,
|
||||||
const char *name)
|
const char *name)
|
||||||
{
|
{
|
||||||
GMenuTreeDirectory *root;
|
char *tmpid;
|
||||||
char *result;
|
ShellAppInfo *result;
|
||||||
|
|
||||||
root = gmenu_tree_get_directory_from_path (system->priv->apps_tree, "/");
|
result = shell_app_system_lookup_app (system, name);
|
||||||
g_assert (root != NULL);
|
if (result != NULL)
|
||||||
|
return result;
|
||||||
if (desktop_id_exists (system, name, root))
|
|
||||||
{
|
|
||||||
result = g_strdup (name);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* These are common "vendor prefixes". But using
|
/* These are common "vendor prefixes". But using
|
||||||
* WM_CLASS as a source, we don't get the vendor
|
* WM_CLASS as a source, we don't get the vendor
|
||||||
* prefix. So try stripping them.
|
* prefix. So try stripping them.
|
||||||
*/
|
*/
|
||||||
result = g_strjoin ("", "gnome-", name, NULL);
|
tmpid = g_strjoin ("", "gnome-", name, NULL);
|
||||||
if (desktop_id_exists (system, result, root))
|
result = shell_app_system_lookup_app (system, tmpid);
|
||||||
goto out;
|
g_free (tmpid);
|
||||||
|
if (result != NULL)
|
||||||
|
return result;
|
||||||
|
|
||||||
result = g_strjoin ("", "fedora-", name, NULL);
|
tmpid = g_strjoin ("", "fedora-", name, NULL);
|
||||||
if (desktop_id_exists (system, result, root))
|
result = shell_app_system_lookup_app (system, tmpid);
|
||||||
goto out;
|
g_free (tmpid);
|
||||||
|
if (result != NULL)
|
||||||
|
return result;
|
||||||
|
|
||||||
out:
|
return NULL;
|
||||||
gmenu_tree_item_unref (root);
|
}
|
||||||
return result;
|
|
||||||
|
const char *
|
||||||
|
shell_app_info_get_id (ShellAppInfo *info)
|
||||||
|
{
|
||||||
|
return gmenu_tree_entry_get_desktop_file_id ((GMenuTreeEntry*)info);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
shell_app_info_get_name (ShellAppInfo *info)
|
||||||
|
{
|
||||||
|
return gmenu_tree_entry_get_name ((GMenuTreeEntry*)info);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
shell_app_info_get_description (ShellAppInfo *info)
|
||||||
|
{
|
||||||
|
return gmenu_tree_entry_get_comment ((GMenuTreeEntry*)info);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
shell_app_info_get_executable (ShellAppInfo *info)
|
||||||
|
{
|
||||||
|
return gmenu_tree_entry_get_exec ((GMenuTreeEntry*)info);
|
||||||
|
}
|
||||||
|
|
||||||
|
GIcon *
|
||||||
|
shell_app_info_get_icon (ShellAppInfo *info)
|
||||||
|
{
|
||||||
|
const char *iconname;
|
||||||
|
GIcon *icon;
|
||||||
|
|
||||||
|
/* This code adapted from gdesktopappinfo.c
|
||||||
|
* Copyright (C) 2006-2007 Red Hat, Inc.
|
||||||
|
* Copyright © 2007 Ryan Lortie
|
||||||
|
* LGPL
|
||||||
|
*/
|
||||||
|
|
||||||
|
iconname = gmenu_tree_entry_get_icon ((GMenuTreeEntry*)info);
|
||||||
|
if (!iconname)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (g_path_is_absolute (iconname))
|
||||||
|
{
|
||||||
|
GFile *file;
|
||||||
|
|
||||||
|
file = g_file_new_for_path (iconname);
|
||||||
|
icon = G_ICON (g_file_icon_new (file));
|
||||||
|
g_object_unref (file);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
char *tmp_name, *p;
|
||||||
|
tmp_name = strdup (iconname);
|
||||||
|
/* Work around a common mistake in desktop files */
|
||||||
|
if ((p = strrchr (tmp_name, '.')) != NULL &&
|
||||||
|
(strcmp (p, ".png") == 0 ||
|
||||||
|
strcmp (p, ".xpm") == 0 ||
|
||||||
|
strcmp (p, ".svg") == 0))
|
||||||
|
{
|
||||||
|
*p = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
icon = g_themed_icon_new (tmp_name);
|
||||||
|
g_free (tmp_name);
|
||||||
|
}
|
||||||
|
return icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
GSList *
|
||||||
|
shell_app_info_get_categories (ShellAppInfo *info)
|
||||||
|
{
|
||||||
|
return NULL; /* TODO */
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
shell_app_info_get_is_nodisplay (ShellAppInfo *info)
|
||||||
|
{
|
||||||
|
return gmenu_tree_entry_get_is_nodisplay ((GMenuTreeEntry*)info);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* shell_app_info_create_icon_texture:
|
||||||
|
*
|
||||||
|
* Look up the icon for this application, and create a #ClutterTexture
|
||||||
|
* for it at the given size.
|
||||||
|
*
|
||||||
|
* Return value: (transfer none): A floating #ClutterActor
|
||||||
|
*/
|
||||||
|
ClutterActor *
|
||||||
|
shell_app_info_create_icon_texture (ShellAppInfo *info, float size)
|
||||||
|
{
|
||||||
|
GIcon *icon;
|
||||||
|
ClutterActor *ret;
|
||||||
|
|
||||||
|
icon = shell_app_info_get_icon (info);
|
||||||
|
if (!icon)
|
||||||
|
{
|
||||||
|
ret = clutter_texture_new ();
|
||||||
|
g_object_set (ret, "width", size, "height", size, NULL);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return shell_texture_cache_load_gicon (shell_texture_cache_get_default (), icon, (int)size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* shell_app_info_launch_full:
|
||||||
|
* @timestamp: Event timestamp, or 0 for current event timestamp
|
||||||
|
* @uris: List of uris to pass to application
|
||||||
|
* @workspace: Start on this workspace, or -1 for default
|
||||||
|
* @startup_id: (out): Returned startup notification ID, or %NULL if none
|
||||||
|
* @error: A #GError
|
||||||
|
*/
|
||||||
|
gboolean
|
||||||
|
shell_app_info_launch_full (ShellAppInfo *info,
|
||||||
|
guint timestamp,
|
||||||
|
GList *uris,
|
||||||
|
int workspace,
|
||||||
|
char **startup_id,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
GDesktopAppInfo *gapp;
|
||||||
|
const char *filename;
|
||||||
|
GdkAppLaunchContext *context;
|
||||||
|
gboolean ret;
|
||||||
|
ShellGlobal *global;
|
||||||
|
MetaScreen *screen;
|
||||||
|
MetaDisplay *display;
|
||||||
|
|
||||||
|
if (startup_id)
|
||||||
|
*startup_id = NULL;
|
||||||
|
|
||||||
|
filename = gmenu_tree_entry_get_desktop_file_path ((GMenuTreeEntry*) info);
|
||||||
|
|
||||||
|
gapp = g_desktop_app_info_new_from_filename (filename);
|
||||||
|
if (!gapp)
|
||||||
|
{
|
||||||
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "Not found");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
global = shell_global_get ();
|
||||||
|
screen = shell_global_get_screen (global);
|
||||||
|
display = meta_screen_get_display (screen);
|
||||||
|
|
||||||
|
if (timestamp == 0)
|
||||||
|
timestamp = meta_display_get_current_time (display);
|
||||||
|
if (workspace < 0)
|
||||||
|
workspace = meta_screen_get_active_workspace_index (screen);
|
||||||
|
|
||||||
|
context = gdk_app_launch_context_new ();
|
||||||
|
gdk_app_launch_context_set_timestamp (context, timestamp);
|
||||||
|
gdk_app_launch_context_set_desktop (context, workspace);
|
||||||
|
|
||||||
|
ret = g_app_info_launch (G_APP_INFO (gapp), uris, (GAppLaunchContext*) context, error);
|
||||||
|
|
||||||
|
g_object_unref (G_OBJECT (gapp));
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
shell_app_info_launch (ShellAppInfo *info,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
return shell_app_info_launch_full (info, 0, NULL, -1, NULL, error);
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
#ifndef __SHELL_APP_SYSTEM_H__
|
#ifndef __SHELL_APP_SYSTEM_H__
|
||||||
#define __SHELL_APP_SYSTEM_H__
|
#define __SHELL_APP_SYSTEM_H__
|
||||||
|
|
||||||
#include <glib-object.h>
|
#include <gio/gio.h>
|
||||||
|
#include <clutter/clutter.h>
|
||||||
|
|
||||||
#define SHELL_TYPE_APP_SYSTEM (shell_app_system_get_type ())
|
#define SHELL_TYPE_APP_SYSTEM (shell_app_system_get_type ())
|
||||||
#define SHELL_APP_SYSTEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SHELL_TYPE_APP_SYSTEM, ShellAppSystem))
|
#define SHELL_APP_SYSTEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SHELL_TYPE_APP_SYSTEM, ShellAppSystem))
|
||||||
@ -44,7 +45,34 @@ struct _ShellAppMenuEntry {
|
|||||||
|
|
||||||
GType shell_app_menu_entry_get_type (void);
|
GType shell_app_menu_entry_get_type (void);
|
||||||
|
|
||||||
char * shell_app_system_lookup_basename (ShellAppSystem *system, const char *id);
|
/* Hidden typedef for a GMenuTreeEntry */
|
||||||
|
typedef struct _ShellAppInfo ShellAppInfo;
|
||||||
|
|
||||||
|
GType shell_app_info_get_type (void);
|
||||||
|
|
||||||
|
ShellAppInfo* shell_app_info_ref (ShellAppInfo *info);
|
||||||
|
void shell_app_info_unref (ShellAppInfo *info);
|
||||||
|
|
||||||
|
const char *shell_app_info_get_id (ShellAppInfo *info);
|
||||||
|
const char *shell_app_info_get_name (ShellAppInfo *info);
|
||||||
|
const char *shell_app_info_get_description (ShellAppInfo *info);
|
||||||
|
const char *shell_app_info_get_executable (ShellAppInfo *info);
|
||||||
|
GIcon *shell_app_info_get_icon (ShellAppInfo *info);
|
||||||
|
ClutterActor *shell_app_info_create_icon_texture (ShellAppInfo *info, float size);
|
||||||
|
GSList *shell_app_info_get_categories (ShellAppInfo *info);
|
||||||
|
gboolean shell_app_info_get_is_nodisplay (ShellAppInfo *info);
|
||||||
|
gboolean shell_app_info_launch_full (ShellAppInfo *info,
|
||||||
|
guint timestamp,
|
||||||
|
GList *uris,
|
||||||
|
int workspace,
|
||||||
|
char **startup_id,
|
||||||
|
GError **error);
|
||||||
|
gboolean shell_app_info_launch (ShellAppInfo *info,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
|
ShellAppInfo *shell_app_system_lookup_app (ShellAppSystem *system, const char *id);
|
||||||
|
|
||||||
|
ShellAppInfo *shell_app_system_lookup_heuristic_basename (ShellAppSystem *system, const char *id);
|
||||||
|
|
||||||
GSList *shell_app_system_get_menus (ShellAppSystem *system);
|
GSList *shell_app_system_get_menus (ShellAppSystem *system);
|
||||||
|
|
||||||
|
@ -406,68 +406,6 @@ shell_get_thumbnail(const gchar *uri,
|
|||||||
return pixbuf;
|
return pixbuf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* shell_get_categories_for_desktop_file:
|
|
||||||
*
|
|
||||||
* @desktop_file_name: name of the desktop file for which to retrieve categories
|
|
||||||
*
|
|
||||||
* Return value: (element-type char*) (transfer full): List of categories
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
GSList *
|
|
||||||
shell_get_categories_for_desktop_file(const char *desktop_file_name)
|
|
||||||
{
|
|
||||||
GKeyFile *key_file;
|
|
||||||
const char * const *search_dirs;
|
|
||||||
char **categories = NULL;
|
|
||||||
GSList *categories_list = NULL;
|
|
||||||
GError *error = NULL;
|
|
||||||
gsize len;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
key_file = g_key_file_new ();
|
|
||||||
search_dirs = get_applications_search_path();
|
|
||||||
|
|
||||||
g_key_file_load_from_dirs (key_file, desktop_file_name, (const char **)search_dirs, NULL, 0, &error);
|
|
||||||
|
|
||||||
if (error != NULL)
|
|
||||||
{
|
|
||||||
g_warning ("Error when loading a key file for %s: %s", desktop_file_name, error->message);
|
|
||||||
g_clear_error (&error);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
categories = g_key_file_get_string_list (key_file,
|
|
||||||
"Desktop Entry",
|
|
||||||
"Categories",
|
|
||||||
&len,
|
|
||||||
&error);
|
|
||||||
if (error != NULL)
|
|
||||||
{
|
|
||||||
// "Categories" is not a required key in the desktop files, so it's ok if we didn't find it
|
|
||||||
g_clear_error (&error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
g_key_file_free (key_file);
|
|
||||||
|
|
||||||
if (categories == NULL)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
// gjs currently does not support returning arrays (other than a NULL value for an array), so we need
|
|
||||||
// to convert the array we are returning to GSList, returning which gjs supports.
|
|
||||||
// See http://bugzilla.gnome.org/show_bug.cgi?id=560567 for more info on gjs array support.
|
|
||||||
for (i = 0; categories[i]; i++)
|
|
||||||
{
|
|
||||||
categories_list = g_slist_prepend (categories_list, g_strdup (categories[i]));
|
|
||||||
}
|
|
||||||
|
|
||||||
g_strfreev (categories);
|
|
||||||
|
|
||||||
return categories_list;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* shell_get_event_key_symbol:
|
* shell_get_event_key_symbol:
|
||||||
*
|
*
|
||||||
|
@ -38,8 +38,6 @@ gboolean shell_clutter_texture_set_from_pixbuf (ClutterTexture *texture,
|
|||||||
|
|
||||||
GdkPixbuf *shell_get_thumbnail(const gchar *uri, const gchar *mime_type);
|
GdkPixbuf *shell_get_thumbnail(const gchar *uri, const gchar *mime_type);
|
||||||
|
|
||||||
GSList *shell_get_categories_for_desktop_file(const char *desktop_file_name);
|
|
||||||
|
|
||||||
guint16 shell_get_event_key_symbol(ClutterEvent *event);
|
guint16 shell_get_event_key_symbol(ClutterEvent *event);
|
||||||
|
|
||||||
guint16 shell_get_button_event_click_count(ClutterEvent *event);
|
guint16 shell_get_button_event_click_count(ClutterEvent *event);
|
||||||
|
Loading…
Reference in New Issue
Block a user