Split ShellAppMonitor into ShellWindowTracker, ShellAppUsage

The two parts were mapping windows to applications, and
recording application usage statistics.  The latter part
(now called ShellAppUsage) is much more naturally built on top of
the former (now called ShellWindowTracker).

ShellWindowTracker retains the startup-notification handling.

ShellWindowTracker also gains a focus-app property, which is
what most things in the shell UI are interested in (instead of
window focus).

ShellAppSystem moves to exporting ShellApp from more of its
public API, rather than ShellAppInfo.  ShellAppSystem also
ensures that ShellApp instances are unique by holding
a hash on the ids.

ShellApp's private API is split off into a shell-app-private.h,
so shell-app.h can be included in shell-app-system.h.

Favorites handling is removed from ShellAppSystem, now inside
appFavorites.js.

Port all of the JavaScript for these changes.

https://bugzilla.gnome.org/show_bug.cgi?id=598646
This commit is contained in:
Colin Walters
2009-10-15 19:28:29 -04:00
parent 77cf9ae077
commit e941e8088b
20 changed files with 2162 additions and 2165 deletions

View File

@ -59,8 +59,8 @@ AltTabPopup.prototype = {
},
show : function(backward) {
let appMonitor = Shell.AppMonitor.get_default();
let apps = appMonitor.get_running_apps ("");
let tracker = Shell.WindowTracker.get_default();
let apps = tracker.get_running_apps ("");
if (!apps.length)
return false;

View File

@ -13,6 +13,7 @@ const Mainloop = imports.mainloop;
const Gettext = imports.gettext.domain('gnome-shell');
const _ = Gettext.gettext;
const AppFavorites = imports.ui.appFavorites;
const AppIcon = imports.ui.appIcon;
const DND = imports.ui.dnd;
const GenericDisplay = imports.ui.genericDisplay;
@ -57,7 +58,9 @@ AppDisplayItem.prototype = {
// Opens an application represented by this display item.
launch : function() {
let windows = Shell.AppMonitor.get_default().get_windows_for_app(this._appInfo.get_id());
let appSys = Shell.AppSystem.get_default();
let app = appSys.get_app(this._appInfo.get_id());
let windows = app.get_windows();
if (windows.length > 0) {
let mostRecentWindow = windows[0];
Main.overview.activateWindow(mostRecentWindow, Main.currentTime());
@ -181,17 +184,12 @@ AppDisplay.prototype = {
// We use a map of appIds instead of an array to ensure that we don't have duplicates and for easier lookup.
this._menuSearchAppMatches = {};
this._appMonitor = Shell.AppMonitor.get_default();
this._appSystem = Shell.AppSystem.get_default();
this._appsStale = true;
this._appSystem.connect('installed-changed', Lang.bind(this, function(appSys) {
this._appsStale = true;
this._redisplay(GenericDisplay.RedisplayFlags.NONE);
}));
this._appSystem.connect('favorites-changed', Lang.bind(this, function(appSys) {
this._appsStale = true;
this._redisplay(GenericDisplay.RedisplayFlags.NONE);
}));
this._focusInMenus = true;
this._activeMenuIndex = -1;
@ -301,9 +299,8 @@ AppDisplay.prototype = {
_getMostUsed: function() {
let context = "";
return this._appMonitor.get_most_used_apps(context, 30).map(Lang.bind(this, function (id) {
return this._appSystem.lookup_cached_app(id);
})).filter(function (e) { return e != null });
let usage = Shell.AppUsage.get_default();
return usage.get_most_used(context, 30);
},
_addMenuItem: function(name, id, index) {
@ -523,7 +520,7 @@ BaseWellItem.prototype = {
// as say Pidgin, but ideally what we do there is have the app
// express to us that it doesn't do relaunch=new-window in the
// .desktop file.
this.app.get_info().launch();
this.app.launch();
},
getDragActor: function() {
@ -557,7 +554,7 @@ RunningWellItem.prototype = {
let modifiers = Shell.get_event_state(event);
if (modifiers & Clutter.ModifierType.CONTROL_MASK) {
this.app.get_info().launch();
this.app.launch();
} else {
this.activateMostRecentWindow();
}
@ -611,7 +608,7 @@ InactiveWellItem.prototype = {
},
_onActivate: function() {
this.app.get_info().launch();
this.app.launch();
Main.overview.hide();
return true;
},
@ -791,6 +788,8 @@ AppWell.prototype = {
this._menus = [];
this._menuDisplays = [];
this._favorites = [];
this.actor = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
x_align: Big.BoxAlignment.CENTER });
this.actor._delegate = this;
@ -801,21 +800,13 @@ AppWell.prototype = {
this._grid = new WellGrid();
this.actor.append(this._grid.actor, Big.BoxPackFlags.EXPAND);
this._tracker = Shell.WindowTracker.get_default();
this._appSystem = Shell.AppSystem.get_default();
this._appMonitor = Shell.AppMonitor.get_default();
this._appSystem.connect('installed-changed', Lang.bind(this, function(appSys) {
this._redisplay();
}));
this._appSystem.connect('favorites-changed', Lang.bind(this, function(appSys) {
this._redisplay();
}));
this._appMonitor.connect('window-added', Lang.bind(this, function(monitor) {
this._redisplay();
}));
this._appMonitor.connect('window-removed', Lang.bind(this, function(monitor) {
this._redisplay();
}));
this._appSystem.connect('installed-changed', Lang.bind(this, this._redisplay));
AppFavorites.getAppFavorites().connect('changed', Lang.bind(this, this._redisplay));
this._tracker.connect('app-running-changed', Lang.bind(this, this._redisplay));
this._redisplay();
},
@ -843,17 +834,16 @@ AppWell.prototype = {
this._grid.removeAll();
let favorites = this._appMonitor.get_favorites();
let favoriteIds = this._appIdListToHash(favorites);
let favorites = AppFavorites.getAppFavorites().getFavoriteMap();
/* hardcode here pending some design about how exactly desktop contexts behave */
let contextId = "";
let running = this._appMonitor.get_running_apps(contextId);
let running = this._tracker.get_running_apps(contextId);
let runningIds = this._appIdListToHash(running);
for (let i = 0; i < favorites.length; i++) {
let app = favorites[i];
for (let id in favorites) {
let app = favorites[id];
let display;
if (app.get_windows().length > 0) {
display = new RunningWellItem(app, true);
@ -865,7 +855,7 @@ AppWell.prototype = {
for (let i = 0; i < running.length; i++) {
let app = running[i];
if (app.get_id() in favoriteIds)
if (app.get_id() in favorites)
continue;
let display = new RunningWellItem(app, false);
this._grid.actor.add_actor(display.actor);
@ -874,14 +864,11 @@ AppWell.prototype = {
// Draggable target interface
acceptDrop : function(source, actor, x, y, time) {
let appSystem = Shell.AppSystem.get_default();
let app = null;
if (source instanceof AppDisplayItem) {
app = appSystem.lookup_cached_app(source.getId());
app = this._appSystem.get_app(source.getId());
} else if (source instanceof Workspaces.WindowClone) {
let appMonitor = Shell.AppMonitor.get_default();
app = appMonitor.get_window_app(source.metaWindow);
app = this._tracker.get_window_app(source.metaWindow);
}
// Don't allow favoriting of transient apps
@ -891,18 +878,17 @@ AppWell.prototype = {
let id = app.get_id();
let favorites = this._appMonitor.get_favorites();
let favoriteIds = this._appIdListToHash(favorites);
let favorites = AppFavorites.getAppFavorites().getFavoriteMap();
let srcIsFavorite = (id in favoriteIds);
let srcIsFavorite = (id in favorites);
if (srcIsFavorite) {
return false;
} else {
Mainloop.idle_add(function () {
appSystem.add_favorite(id);
Mainloop.idle_add(Lang.bind(this, function () {
AppFavorites.getAppFavorites().addFavorite(id);
return false;
});
}));
}
return true;

90
js/ui/appFavorites.js Normal file
View File

@ -0,0 +1,90 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Shell = imports.gi.Shell;
const Lang = imports.lang;
const Signals = imports.signals;
function AppFavorites() {
this._init();
}
AppFavorites.prototype = {
FAVORITE_APPS_KEY: 'favorite_apps',
_init: function() {
this._favorites = {};
this._gconf = Shell.GConf.get_default();
this._gconf.connect('changed::' + this.FAVORITE_APPS_KEY, Lang.bind(this, this._onFavsChanged));
this._reload();
},
_onFavsChanged: function() {
this._reload();
this.emit('changed');
},
_reload: function() {
let ids = Shell.GConf.get_default().get_string_list('favorite_apps');
let appSys = Shell.AppSystem.get_default();
let apps = ids.map(function (id) {
return appSys.get_app(id);
}).filter(function (app) {
return app != null;
});
this._favorites = {};
for (let i = 0; i < apps.length; i++) {
let app = apps[i];
this._favorites[app.get_id()] = app;
}
},
_getIds: function() {
let ret = [];
for (let id in this._favorites)
ret.push(id);
return ret;
},
getFavoriteMap: function() {
return this._favorites;
},
getFavorites: function() {
let ret = [];
for (let id in this._favorites)
ret.push(this._favorites[id]);
return ret;
},
isFavorite: function(appId) {
return appId in this._favorites;
},
addFavorite: function(appId) {
if (appId in this._favorites)
return;
let app = Shell.AppSystem.get_default().get_app(appId);
if (!app)
return;
let ids = this._getIds();
ids.push(appId);
this._gconf.set_string_list(this.FAVORITE_APPS_KEY, ids);
this._favorites[appId] = app;
},
removeFavorite: function(appId) {
if (!appId in this._favorites)
return;
let ids = this._getIds().filter(function (id) { return id != appId; });
this._gconf.set_string_list(this.FAVORITE_APPS_KEY, ids);
}
};
Signals.addSignalMethods(AppFavorites.prototype);
var appFavoritesInstance = null;
function getAppFavorites() {
if (appFavoritesInstance == null)
appFavoritesInstance = new AppFavorites();
return appFavoritesInstance;
}

View File

@ -12,6 +12,7 @@ const Gettext = imports.gettext.domain('gnome-shell');
const _ = Gettext.gettext;
const GenericDisplay = imports.ui.genericDisplay;
const AppFavorites = imports.ui.appFavorites;
const Main = imports.ui.main;
const Workspaces = imports.ui.workspaces;
@ -404,20 +405,13 @@ AppIconMenu.prototype = {
if (windows.length > 0)
this._appendSeparator();
let isFavorite = AppFavorites.getAppFavorites().isFavorite(this._source.app.get_id());
this._newWindowMenuItem = windows.length > 0 ? this._appendMenuItem(null, _("New Window")) : null;
let favorites = Shell.AppSystem.get_default().get_favorites();
let id = this._source.app.get_id();
this._isFavorite = false;
for (let i = 0; i < favorites.length; i++) {
if (id == favorites[i]) {
this._isFavorite = true;
break;
}
}
if (windows.length > 0)
this._appendSeparator();
this._toggleFavoriteMenuItem = this._appendMenuItem(null, this._isFavorite ? _("Remove from Favorites")
this._toggleFavoriteMenuItem = this._appendMenuItem(null, isFavorite ? _("Remove from Favorites")
: _("Add to Favorites"));
this._highlightedItem = null;
@ -560,14 +554,14 @@ AppIconMenu.prototype = {
let metaWindow = child._window;
this.emit('activate-window', metaWindow);
} else if (child == this._newWindowMenuItem) {
this._source.app.get_info().launch();
this._source.app.launch();
this.emit('activate-window', null);
} else if (child == this._toggleFavoriteMenuItem) {
let appSys = Shell.AppSystem.get_default();
if (this._isFavorite)
appSys.remove_favorite(this._source.app.get_id());
let isFavorite = AppFavorites.getAppFavorites().isFavorite(this._source.app.get_id());
if (isFavorite)
favs.removeFavorite(this._source.app.get_id());
else
appSys.add_favorite(this._source.app.get_id());
favs.addFavorite(this._source.app.get_id());
}
this.popdown();
},

View File

@ -55,15 +55,16 @@ function start() {
Environment.init();
// Ensure ShellAppMonitor is initialized; this will
// Ensure ShellWindowTracker and ShellAppUsage are initialized; this will
// also initialize ShellAppSystem first. ShellAppSystem
// needs to load all the .desktop files, and ShellAppMonitor
// needs to load all the .desktop files, and ShellWindowTracker
// will use those to associate with windows. Right now
// the Monitor doesn't listen for installed app changes
// and recalculate application associations, so to avoid
// races for now we initialize it here. It's better to
// be predictable anyways.
Shell.AppMonitor.get_default();
Shell.WindowTracker.get_default();
Shell.AppUsage.get_default();
// The background color really only matters if there is no desktop
// window (say, nautilus) running. We set it mostly so things look good

View File

@ -109,34 +109,26 @@ AppPanelMenu.prototype = {
this.actor.opacity = 192;
}));
this._metaDisplay.connect('notify::focus-window', Lang.bind(this, this._sync));
let appMonitor = Shell.AppMonitor.get_default();
appMonitor.connect('startup-sequence-changed', Lang.bind(this, this._sync));
// For now just resync on application add/remove; this is mainly to handle
let tracker = Shell.WindowTracker.get_default();
tracker.connect('notify::focus-app', Lang.bind(this, this._sync));
tracker.connect('startup-sequence-changed', Lang.bind(this, this._sync));
// For now just resync on all running state changes; this is mainly to handle
// cases where the focused window's application changes without the focus
// changing. An example case is how we map Firefox based on the window
// title which is a dynamic property.
appMonitor.connect('app-added', Lang.bind(this, this._sync));
appMonitor.connect('app-removed', Lang.bind(this, this._sync));
tracker.connect('app-running-changed', Lang.bind(this, this._sync));
this._sync();
},
_sync: function() {
let appMonitor = Shell.AppMonitor.get_default();
let tracker = Shell.WindowTracker.get_default();
let focusWindow = this._metaDisplay.get_focus_window();
let focusedApp;
if (focusWindow == null) {
focusedApp = null;
} else {
focusedApp = appMonitor.get_window_app(focusWindow);
}
let focusedApp = tracker.focus_app;
let lastSequence = null;
if (focusedApp == null) {
let sequences = appMonitor.get_startup_sequences();
let sequences = tracker.get_startup_sequences();
if (sequences.length > 0)
lastSequence = sequences[sequences.length - 1];
}
@ -156,11 +148,10 @@ AppPanelMenu.prototype = {
this._iconBox.hide();
this._label.text = '';
if (this._focusedApp != null) {
let info = focusedApp.get_info();
let icon = info.create_icon_texture(PANEL_ICON_SIZE);
let icon = this._focusedApp.create_icon_texture(PANEL_ICON_SIZE);
this._iconBox.append(icon, Big.BoxPackFlags.NONE);
this._iconBox.show();
this._label.text = info.get_name();
this._label.text = this._focusedApp.get_name();
} else if (this._activeSequence != null) {
let icon = this._activeSequence.create_icon(PANEL_ICON_SIZE);
this._iconBox.append(icon, Big.BoxPackFlags.NONE);

View File

@ -12,6 +12,7 @@ const Signals = imports.signals;
const Gettext = imports.gettext.domain('gnome-shell');
const _ = Gettext.gettext;
const AppFavorites = imports.ui.appFavorites;
const DocInfo = imports.misc.docInfo;
const COLLAPSED_WIDTH = 24;
@ -318,12 +319,9 @@ AppsWidget.prototype = {
this.collapsedActor = new Big.Box({ spacing: 2});
let appSystem = Shell.AppSystem.get_default();
let apps = appSystem.get_favorites();
let apps = AppFavorites.getAppFavorites().getFavorites();
for (let i = 0; i < apps.length; i++) {
let app = appSystem.lookup_cached_app(apps[i]);
if (!app)
continue;
this.addItem(new AppsWidgetInfo(app));
this.addItem(new AppsWidgetInfo(apps[i]));
}
}
};

View File

@ -825,7 +825,6 @@ Workspace.prototype = {
* fashion by the order in which the windows were created.
*/
_orderWindowsByMotionAndStartup: function(windows, slots) {
let appMonitor = Shell.AppMonitor.get_default();
windows.sort(function(w1, w2) {
return w2.get_stable_sequence() - w1.get_stable_sequence();
});
@ -1220,14 +1219,13 @@ Workspace.prototype = {
// Tests if @win should be shown in the Overview
_isOverviewWindow : function (win) {
let appMon = Shell.AppMonitor.get_default()
return appMon.is_window_usage_tracked(win.get_meta_window());
let tracker = Shell.WindowTracker.get_default()
return tracker.is_window_interesting(win.get_meta_window());
},
_createWindowIcon: function(window) {
let appSys = Shell.AppSystem.get_default();
let appMon = Shell.AppMonitor.get_default()
let app = appMon.get_window_app(window.metaWindow);
let tracker = Shell.WindowTracker.get_default()
let app = tracker.get_window_app(window.metaWindow);
let iconTexture = null;
// The design is application based, so prefer the application
// icon here if we have it. FIXME - should move this fallback code
@ -1458,10 +1456,11 @@ Workspaces.prototype = {
this._windowSelectionAppId = appId;
let appSys = Shell.AppMonitor.get_default();
let appSys = Shell.AppSystem.get_default();
let showOnlyWindows = {};
let windows = appSys.get_windows_for_app(appId);
let app = appSys.get_app(appId);
let windows = app.get_windows();
for (let i = 0; i < windows.length; i++) {
showOnlyWindows[windows[i]] = 1;
}