Create ShellApp, rebase things on it
Previously, we had ShellAppInfo, which contains fundamental information about an application, and methods on ShellAppMonitor to retrieve "live" information like the window list. AppIcon ended up being used as the "App" class which was painful for various reasons; among them that we need to handle window list changes, and some consumers weren't ready for that. Clean things up a bit by introducing a new ShellApp class in C, which currently wraps a ShellAppInfo. AppIcon then is more like the display actor for a ShellApp. Notably, the ".windows" property moves out of it. The altTab code which won't handle dynamic changes instead is changed to maintain a cached version. ShellAppMonitor gains some more methods related to ShellApp now. In the future, we might consider changing ShellApp to be a GInterface, which could be implemented by ShellDesktopFileApp, ShellWindowApp. Then we could axe ShellAppInfo from the "public" API and it would return to being an internal loss mitigation layer for GMenu. https://bugzilla.gnome.org/show_bug.cgi?id=598227
This commit is contained in:
parent
6623ca1287
commit
38c06ca837
@ -93,7 +93,7 @@ AltTabPopup.prototype = {
|
||||
|
||||
// Make the initial selection
|
||||
if (this._appIcons.length == 1) {
|
||||
if (!backward && this._appIcons[0].windows.length > 1) {
|
||||
if (!backward && this._appIcons[0].cachedWindows.length > 1) {
|
||||
// For compatibility with the multi-app case below
|
||||
this._select(0, 1);
|
||||
} else
|
||||
@ -101,9 +101,10 @@ AltTabPopup.prototype = {
|
||||
} else if (backward) {
|
||||
this._select(this._appIcons.length - 1);
|
||||
} else {
|
||||
if (this._appIcons[0].windows.length > 1) {
|
||||
let curAppNextWindow = this._appIcons[0].windows[1];
|
||||
let nextAppWindow = this._appIcons[1].windows[0];
|
||||
let firstWindows = this._appIcons[0].cachedWindows;
|
||||
if (firstWindows.length > 1) {
|
||||
let curAppNextWindow = firstWindows[1];
|
||||
let nextAppWindow = this._appIcons[1].cachedWindows[0];
|
||||
|
||||
// If the next window of the current app is more-recently-used
|
||||
// than the first window of the next app, then select it.
|
||||
@ -139,11 +140,11 @@ AltTabPopup.prototype = {
|
||||
|
||||
_nextWindow : function() {
|
||||
return mod(this._currentWindows[this._currentApp] + 1,
|
||||
this._appIcons[this._currentApp].windows.length);
|
||||
this._appIcons[this._currentApp].cachedWindows.length);
|
||||
},
|
||||
_previousWindow : function() {
|
||||
return mod(this._currentWindows[this._currentApp] - 1,
|
||||
this._appIcons[this._currentApp].windows.length);
|
||||
this._appIcons[this._currentApp].cachedWindows.length);
|
||||
},
|
||||
|
||||
_keyPressEvent : function(actor, event) {
|
||||
@ -188,7 +189,7 @@ AltTabPopup.prototype = {
|
||||
},
|
||||
|
||||
_appActivated : function(appSwitcher, n) {
|
||||
Main.activateWindow(this._appIcons[n].windows[this._currentWindows[n]]);
|
||||
Main.activateWindow(this._appIcons[n].cachedWindows[this._currentWindows[n]]);
|
||||
this.destroy();
|
||||
},
|
||||
|
||||
@ -200,7 +201,7 @@ AltTabPopup.prototype = {
|
||||
},
|
||||
|
||||
_windowActivated : function(thumbnailList, n) {
|
||||
Main.activateWindow(this._appIcons[this._currentApp].windows[n]);
|
||||
Main.activateWindow(this._appIcons[this._currentApp].cachedWindows[n]);
|
||||
this.destroy();
|
||||
},
|
||||
|
||||
@ -225,7 +226,7 @@ AltTabPopup.prototype = {
|
||||
|
||||
_finish : function() {
|
||||
let app = this._appIcons[this._currentApp];
|
||||
let window = app.windows[this._currentWindows[this._currentApp]];
|
||||
let window = app.cachedWindows[this._currentWindows[this._currentApp]];
|
||||
Main.activateWindow(window);
|
||||
this.destroy();
|
||||
},
|
||||
@ -267,7 +268,7 @@ AltTabPopup.prototype = {
|
||||
this._appSwitcher.showArrow(app);
|
||||
} else {
|
||||
this._appSwitcher.highlight(app);
|
||||
if (this._appIcons[this._currentApp].windows.length > 1)
|
||||
if (this._appIcons[this._currentApp].cachedWindows.length > 1)
|
||||
this._appSwitcher.showArrow(app);
|
||||
}
|
||||
|
||||
@ -276,7 +277,7 @@ AltTabPopup.prototype = {
|
||||
this._createThumbnails();
|
||||
this._currentWindows[this._currentApp] = window;
|
||||
this._thumbnails.highlight(window);
|
||||
} else if (this._appIcons[this._currentApp].windows.length > 1 &&
|
||||
} else if (this._appIcons[this._currentApp].cachedWindows.length > 1 &&
|
||||
!noTimeout) {
|
||||
this._thumbnailTimeoutId = Mainloop.timeout_add (
|
||||
THUMBNAIL_POPUP_TIME,
|
||||
@ -289,7 +290,7 @@ AltTabPopup.prototype = {
|
||||
},
|
||||
|
||||
_createThumbnails : function() {
|
||||
this._thumbnails = new ThumbnailList (this._appIcons[this._currentApp].windows);
|
||||
this._thumbnails = new ThumbnailList (this._appIcons[this._currentApp].cachedWindows);
|
||||
this._thumbnails.connect('item-activated', Lang.bind(this, this._windowActivated));
|
||||
this._thumbnails.connect('item-hovered', Lang.bind(this, this._windowHovered));
|
||||
|
||||
@ -552,8 +553,11 @@ AppSwitcher.prototype = {
|
||||
let workspaceIcons = [];
|
||||
let otherIcons = [];
|
||||
for (let i = 0; i < apps.length; i++) {
|
||||
let appIcon = new AppIcon.AppIcon({ appInfo: apps[i],
|
||||
let appIcon = new AppIcon.AppIcon({ app: apps[i],
|
||||
size: POPUP_APPICON_SIZE });
|
||||
// Cache the window list now; we don't handle dynamic changes here,
|
||||
// and we don't want to be continually retrieving it
|
||||
appIcon.cachedWindows = appIcon.app.get_windows();
|
||||
if (this._hasWindowsOnWorkspace(appIcon, activeWorkspace))
|
||||
workspaceIcons.push(appIcon);
|
||||
else
|
||||
@ -621,35 +625,16 @@ AppSwitcher.prototype = {
|
||||
},
|
||||
|
||||
_hasWindowsOnWorkspace: function(appIcon, workspace) {
|
||||
for (let i = 0; i < appIcon.windows.length; i++) {
|
||||
if (appIcon.windows[i].get_workspace() == workspace)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
_hasVisibleWindows : function(appIcon) {
|
||||
for (let i = 0; i < appIcon.windows.length; i++) {
|
||||
if (appIcon.windows[i].showing_on_its_workspace())
|
||||
let windows = appIcon.cachedWindows;
|
||||
for (let i = 0; i < windows.length; i++) {
|
||||
if (windows[i].get_workspace() == workspace)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
_sortAppIcon : function(appIcon1, appIcon2) {
|
||||
let vis1 = this._hasVisibleWindows(appIcon1);
|
||||
let vis2 = this._hasVisibleWindows(appIcon2);
|
||||
|
||||
if (vis1 && !vis2) {
|
||||
return -1;
|
||||
} else if (vis2 && !vis1) {
|
||||
return 1;
|
||||
} else {
|
||||
// The app's most-recently-used window is first
|
||||
// in its list
|
||||
return (appIcon2.windows[0].get_user_time() -
|
||||
appIcon1.windows[0].get_user_time());
|
||||
}
|
||||
return appIcon1.app.compare(appIcon2.app);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -476,15 +476,15 @@ AppDisplay.prototype = {
|
||||
|
||||
Signals.addSignalMethods(AppDisplay.prototype);
|
||||
|
||||
function BaseWellItem(appInfo, isFavorite, hasMenu) {
|
||||
this._init(appInfo, isFavorite, hasMenu);
|
||||
function BaseWellItem(app, isFavorite, hasMenu) {
|
||||
this._init(app, isFavorite, hasMenu);
|
||||
}
|
||||
|
||||
BaseWellItem.prototype = {
|
||||
__proto__: AppIcon.AppIcon.prototype,
|
||||
|
||||
_init: function(appInfo, isFavorite) {
|
||||
AppIcon.AppIcon.prototype._init.call(this, { appInfo: appInfo,
|
||||
_init: function(app, isFavorite) {
|
||||
AppIcon.AppIcon.prototype._init.call(this, { app: app,
|
||||
menuType: AppIcon.MenuType.ON_RIGHT,
|
||||
glow: true });
|
||||
|
||||
@ -523,7 +523,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.appInfo.launch();
|
||||
this.app.get_info().launch();
|
||||
},
|
||||
|
||||
getDragActor: function() {
|
||||
@ -537,15 +537,15 @@ BaseWellItem.prototype = {
|
||||
}
|
||||
}
|
||||
|
||||
function RunningWellItem(appInfo, isFavorite) {
|
||||
this._init(appInfo, isFavorite);
|
||||
function RunningWellItem(app, isFavorite) {
|
||||
this._init(app, isFavorite);
|
||||
}
|
||||
|
||||
RunningWellItem.prototype = {
|
||||
__proto__: BaseWellItem.prototype,
|
||||
|
||||
_init: function(appInfo, isFavorite) {
|
||||
BaseWellItem.prototype._init.call(this, appInfo, isFavorite);
|
||||
_init: function(app, isFavorite) {
|
||||
BaseWellItem.prototype._init.call(this, app, isFavorite);
|
||||
|
||||
this._dragStartX = 0;
|
||||
this._dragStartY = 0;
|
||||
@ -557,15 +557,14 @@ RunningWellItem.prototype = {
|
||||
let modifiers = Shell.get_event_state(event);
|
||||
|
||||
if (modifiers & Clutter.ModifierType.CONTROL_MASK) {
|
||||
this.appInfo.launch();
|
||||
this.app.get_info().launch();
|
||||
} else {
|
||||
this.activateMostRecentWindow();
|
||||
}
|
||||
},
|
||||
|
||||
activateMostRecentWindow: function () {
|
||||
// The _get_windows_for_app sorts them for us
|
||||
let mostRecentWindow = this.windows[0];
|
||||
let mostRecentWindow = this.app.get_windows()[0];
|
||||
Main.overview.activateWindow(mostRecentWindow, Main.currentTime());
|
||||
},
|
||||
|
||||
@ -582,7 +581,7 @@ RunningWellItem.prototype = {
|
||||
},
|
||||
|
||||
menuPoppedUp: function() {
|
||||
Main.overview.getWorkspacesForWindow(null).setApplicationWindowSelection(this.appInfo.get_id());
|
||||
Main.overview.getWorkspacesForWindow(null).setApplicationWindowSelection(this.app.get_id());
|
||||
},
|
||||
|
||||
menuPoppedDown: function() {
|
||||
@ -593,15 +592,15 @@ RunningWellItem.prototype = {
|
||||
}
|
||||
};
|
||||
|
||||
function InactiveWellItem(appInfo, isFavorite) {
|
||||
this._init(appInfo, isFavorite);
|
||||
function InactiveWellItem(app, isFavorite) {
|
||||
this._init(app, isFavorite);
|
||||
}
|
||||
|
||||
InactiveWellItem.prototype = {
|
||||
__proto__: BaseWellItem.prototype,
|
||||
|
||||
_init : function(appInfo, isFavorite) {
|
||||
BaseWellItem.prototype._init.call(this, appInfo, isFavorite);
|
||||
_init : function(app, isFavorite) {
|
||||
BaseWellItem.prototype._init.call(this, app, isFavorite);
|
||||
|
||||
this.actor.connect('notify::pressed', Lang.bind(this, this._onPressedChanged));
|
||||
this.actor.connect('activate', Lang.bind(this, this._onActivate));
|
||||
@ -612,12 +611,9 @@ InactiveWellItem.prototype = {
|
||||
},
|
||||
|
||||
_onActivate: function() {
|
||||
if (this.windows.length == 0) {
|
||||
this.appInfo.launch();
|
||||
Main.overview.hide();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
this.app.get_info().launch();
|
||||
Main.overview.hide();
|
||||
return true;
|
||||
},
|
||||
|
||||
menuPoppedUp: function() {
|
||||
@ -824,21 +820,11 @@ AppWell.prototype = {
|
||||
this._redisplay();
|
||||
},
|
||||
|
||||
_lookupApps: function(appIds) {
|
||||
let result = [];
|
||||
for (let i = 0; i < appIds.length; i++) {
|
||||
let id = appIds[i];
|
||||
let app = this._appSystem.lookup_cached_app(id);
|
||||
if (!app)
|
||||
continue;
|
||||
result.push(app);
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
_arrayValues: function(array) {
|
||||
return array.reduce(function (values, id, index) {
|
||||
values[id] = index; return values; }, {});
|
||||
_appIdListToHash: function(apps) {
|
||||
let ids = {};
|
||||
for (let i = 0; i < apps.length; i++)
|
||||
ids[apps[i].get_id()] = apps[i];
|
||||
return ids;
|
||||
},
|
||||
|
||||
_onMappedNotify: function() {
|
||||
@ -857,32 +843,29 @@ AppWell.prototype = {
|
||||
|
||||
this._grid.removeAll();
|
||||
|
||||
let favoriteIds = this._appSystem.get_favorites();
|
||||
let favoriteIdsHash = this._arrayValues(favoriteIds);
|
||||
let favorites = this._appMonitor.get_favorites();
|
||||
let favoriteIds = this._appIdListToHash(favorites);
|
||||
|
||||
/* hardcode here pending some design about how exactly desktop contexts behave */
|
||||
let contextId = "";
|
||||
|
||||
let running = this._appMonitor.get_running_apps(contextId).filter(function (e) {
|
||||
return !(e.get_id() in favoriteIdsHash);
|
||||
});
|
||||
let favorites = this._lookupApps(favoriteIds);
|
||||
let running = this._appMonitor.get_running_apps(contextId);
|
||||
let runningIds = this._appIdListToHash(running)
|
||||
|
||||
let displays = []
|
||||
this._addApps(favorites, true);
|
||||
this._addApps(running, false);
|
||||
this._displays = displays;
|
||||
},
|
||||
|
||||
_addApps: function(apps, isFavorite) {
|
||||
for (let i = 0; i < apps.length; i++) {
|
||||
let app = apps[i];
|
||||
let windows = this._appMonitor.get_windows_for_app(app.get_id());
|
||||
for (let i = 0; i < favorites.length; i++) {
|
||||
let app = favorites[i];
|
||||
let display;
|
||||
if (windows.length > 0)
|
||||
display = new RunningWellItem(app, isFavorite);
|
||||
else
|
||||
display = new InactiveWellItem(app, isFavorite);
|
||||
if (app.get_windows().length > 0) {
|
||||
display = new RunningWellItem(app, true);
|
||||
} else {
|
||||
display = new InactiveWellItem(app, true);
|
||||
}
|
||||
this._grid.actor.add_actor(display.actor);
|
||||
}
|
||||
|
||||
for (let i = 0; i < running.length; i++) {
|
||||
let app = running[i];
|
||||
let display = new RunningWellItem(app, false);
|
||||
this._grid.actor.add_actor(display.actor);
|
||||
}
|
||||
},
|
||||
|
@ -55,9 +55,9 @@ function AppIcon(params) {
|
||||
|
||||
AppIcon.prototype = {
|
||||
_init : function(params) {
|
||||
this.appInfo = params.appInfo;
|
||||
if (!this.appInfo)
|
||||
throw new Error('AppIcon constructor requires "appInfo" param');
|
||||
this.app = params.app;
|
||||
if (!this.app)
|
||||
throw new Error('AppIcon constructor requires "app" param');
|
||||
|
||||
this._menuType = ('menuType' in params) ? params.menuType : MenuType.NONE;
|
||||
this._iconSize = ('size' in params) ? params.size : APPICON_DEFAULT_ICON_SIZE;
|
||||
@ -70,20 +70,6 @@ AppIcon.prototype = {
|
||||
reactive: true });
|
||||
this.actor._delegate = this;
|
||||
this.highlight_border_color = APPICON_DEFAULT_BORDER_COLOR;
|
||||
this._signalIds = [];
|
||||
|
||||
// Note, we don't presently update the window list dynamically here; this actor
|
||||
// gets destroyed and recreated by AppWell, and in alt-tab the whole thing is
|
||||
// created each time
|
||||
this.windows = Shell.AppMonitor.get_default().get_windows_for_app(this.appInfo.get_id());
|
||||
for (let i = 0; i < this.windows.length; i++) {
|
||||
let sigId = this.windows[i].connect('notify::user-time', Lang.bind(this, this._resortWindows));
|
||||
this._signalIds.push([this.windows[i], sigId]);
|
||||
}
|
||||
this._resortWindows();
|
||||
|
||||
this.actor.connect('destroy', Lang.bind(this, this._destroy));
|
||||
this.actor.connect('notify::mapped', Lang.bind(this, this._onMappedChanged));
|
||||
|
||||
if (this._menuType != MenuType.NONE) {
|
||||
this.actor.connect('button-press-event', Lang.bind(this, this._updateMenuOnButtonPress));
|
||||
@ -99,7 +85,7 @@ AppIcon.prototype = {
|
||||
y_align: Big.BoxAlignment.CENTER,
|
||||
width: this._iconSize,
|
||||
height: this._iconSize });
|
||||
this.icon = this.appInfo.create_icon_texture(this._iconSize);
|
||||
this.icon = this.app.create_icon_texture(this._iconSize);
|
||||
iconBox.append(this.icon, Big.BoxPackFlags.NONE);
|
||||
|
||||
this.actor.append(iconBox, Big.BoxPackFlags.EXPAND);
|
||||
@ -114,12 +100,13 @@ AppIcon.prototype = {
|
||||
font_name: "Sans 12px",
|
||||
line_alignment: Pango.Alignment.CENTER,
|
||||
ellipsize: Pango.EllipsizeMode.END,
|
||||
text: this.appInfo.get_name() });
|
||||
text: this.app.get_name() });
|
||||
nameBox.add_actor(this._name);
|
||||
if (showGlow) {
|
||||
this._glowBox = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL });
|
||||
let glowPath = GLib.filename_to_uri(global.imagedir + 'app-well-glow.png', '');
|
||||
for (let i = 0; i < this.windows.length && i < 3; i++) {
|
||||
let windows = this.app.get_windows();
|
||||
for (let i = 0; i < windows.length && i < 3; i++) {
|
||||
let glow = Shell.TextureCache.get_default().load_uri_sync(Shell.TextureCachePolicy.FOREVER,
|
||||
glowPath, -1, -1);
|
||||
glow.keep_aspect_ratio = false;
|
||||
@ -175,53 +162,11 @@ AppIcon.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
_destroy: function() {
|
||||
for (let i = 0; i < this._signalIds.length; i++) {
|
||||
let [obj, sigId] = this._signalIds[i];
|
||||
obj.disconnect(sigId);
|
||||
}
|
||||
},
|
||||
|
||||
_onMappedChanged: function() {
|
||||
let mapped = this.actor.mapped;
|
||||
if (mapped && this._windowSortStale)
|
||||
this._resortWindows();
|
||||
},
|
||||
|
||||
_resortWindows: function() {
|
||||
let mapped = this.actor.mapped;
|
||||
if (!mapped) {
|
||||
this._windowSortStale = true;
|
||||
return;
|
||||
}
|
||||
this._windowSortStale = false;
|
||||
this.windows.sort(function (a, b) {
|
||||
let activeWorkspace = global.screen.get_active_workspace();
|
||||
let wsA = a.get_workspace() == activeWorkspace;
|
||||
let wsB = b.get_workspace() == activeWorkspace;
|
||||
|
||||
if (wsA && !wsB)
|
||||
return -1;
|
||||
else if (wsB && !wsA)
|
||||
return 1;
|
||||
|
||||
let visA = a.showing_on_its_workspace();
|
||||
let visB = b.showing_on_its_workspace();
|
||||
|
||||
if (visA && !visB)
|
||||
return -1;
|
||||
else if (visB && !visA)
|
||||
return 1;
|
||||
else
|
||||
return b.get_user_time() - a.get_user_time();
|
||||
});
|
||||
},
|
||||
|
||||
// AppIcon itself is not a draggable, but if you want to make
|
||||
// a subclass of it draggable, you can use this method to create
|
||||
// a drag actor
|
||||
createDragActor: function() {
|
||||
return this.appInfo.create_icon_texture(this._iconSize);
|
||||
return this.app.create_icon_texture(this._iconSize);
|
||||
},
|
||||
|
||||
setHighlight: function(highlight) {
|
||||
@ -421,7 +366,7 @@ AppIconMenu.prototype = {
|
||||
_redisplay: function() {
|
||||
this._windowContainer.remove_all();
|
||||
|
||||
let windows = this._source.windows;
|
||||
let windows = this._source.app.get_windows();
|
||||
|
||||
this._windowContainer.show();
|
||||
|
||||
@ -462,7 +407,7 @@ AppIconMenu.prototype = {
|
||||
this._newWindowMenuItem = windows.length > 0 ? this._appendMenuItem(null, _("New Window")) : null;
|
||||
|
||||
let favorites = Shell.AppSystem.get_default().get_favorites();
|
||||
let id = this._source.appInfo.get_id();
|
||||
let id = this._source.app.get_id();
|
||||
this._isFavorite = false;
|
||||
for (let i = 0; i < favorites.length; i++) {
|
||||
if (id == favorites[i]) {
|
||||
@ -615,14 +560,14 @@ AppIconMenu.prototype = {
|
||||
let metaWindow = child._window;
|
||||
this.emit('activate-window', metaWindow);
|
||||
} else if (child == this._newWindowMenuItem) {
|
||||
this._source.appInfo.launch();
|
||||
this._source.app.get_info().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.appInfo.get_id());
|
||||
appSys.remove_favorite(this._source.app.get_id());
|
||||
else
|
||||
appSys.add_favorite(this._source.appInfo.get_id());
|
||||
appSys.add_favorite(this._source.app.get_id());
|
||||
}
|
||||
this.popdown();
|
||||
},
|
||||
|
@ -148,10 +148,11 @@ AppPanelMenu.prototype = {
|
||||
this._iconBox.hide();
|
||||
this._label.text = '';
|
||||
if (this._focusedApp != null) {
|
||||
let icon = focusedApp.create_icon_texture(PANEL_ICON_SIZE);
|
||||
let info = focusedApp.get_info();
|
||||
let icon = info.create_icon_texture(PANEL_ICON_SIZE);
|
||||
this._iconBox.append(icon, Big.BoxPackFlags.NONE);
|
||||
this._iconBox.show();
|
||||
this._label.text = focusedApp.get_name();
|
||||
this._label.text = info.get_name();
|
||||
} else if (this._activeSequence != null) {
|
||||
let icon = this._activeSequence.create_icon(PANEL_ICON_SIZE);
|
||||
this._iconBox.append(icon, Big.BoxPackFlags.NONE);
|
||||
|
@ -1227,7 +1227,7 @@ Workspace.prototype = {
|
||||
_createWindowIcon: function(window) {
|
||||
let appSys = Shell.AppSystem.get_default();
|
||||
let appMon = Shell.AppMonitor.get_default()
|
||||
let appInfo = appMon.get_window_app(window.metaWindow);
|
||||
let appInfo = appMon.get_window_app(window.metaWindow).get_info();
|
||||
let iconTexture = null;
|
||||
// The design is application based, so prefer the application
|
||||
// icon here if we have it. FIXME - should move this fallback code
|
||||
|
@ -54,6 +54,8 @@ CLEANFILES += $(SHELL_STAMP_FILES)
|
||||
libgnome_shell_la_SOURCES = \
|
||||
$(shell_built_sources) \
|
||||
gnome-shell-plugin.c \
|
||||
shell-app.c \
|
||||
shell-app.h \
|
||||
shell-app-monitor.c \
|
||||
shell-app-monitor.h \
|
||||
shell-app-system.c \
|
||||
|
@ -129,9 +129,12 @@ struct _ShellAppMonitor
|
||||
long watch_start_time;
|
||||
MetaWindow *watched_window;
|
||||
|
||||
/* <MetaWindow * window, ShellAppInfo *app> */
|
||||
/* <MetaWindow * window, ShellApp *app> */
|
||||
GHashTable *window_to_app;
|
||||
|
||||
/* <const char *, ShellApp *app> */
|
||||
GHashTable *running_apps;
|
||||
|
||||
/* <char *context, GHashTable<char *appid, AppUsage *usage>> */
|
||||
GHashTable *app_usages_for_context;
|
||||
};
|
||||
@ -214,35 +217,35 @@ static void shell_app_monitor_class_init(ShellAppMonitorClass *klass)
|
||||
G_SIGNAL_RUN_LAST,
|
||||
0,
|
||||
NULL, NULL,
|
||||
_shell_marshal_VOID__BOXED,
|
||||
g_cclosure_marshal_VOID__OBJECT,
|
||||
G_TYPE_NONE, 1,
|
||||
SHELL_TYPE_APP_INFO);
|
||||
SHELL_TYPE_APP);
|
||||
signals[APP_REMOVED] = g_signal_new ("app-removed",
|
||||
SHELL_TYPE_APP_MONITOR,
|
||||
G_SIGNAL_RUN_LAST,
|
||||
0,
|
||||
NULL, NULL,
|
||||
_shell_marshal_VOID__BOXED,
|
||||
g_cclosure_marshal_VOID__OBJECT,
|
||||
G_TYPE_NONE, 1,
|
||||
SHELL_TYPE_APP_INFO);
|
||||
SHELL_TYPE_APP);
|
||||
|
||||
signals[WINDOW_ADDED] = g_signal_new ("window-added",
|
||||
SHELL_TYPE_APP_MONITOR,
|
||||
G_SIGNAL_RUN_LAST,
|
||||
0,
|
||||
NULL, NULL,
|
||||
_shell_marshal_VOID__BOXED_OBJECT,
|
||||
_shell_marshal_VOID__OBJECT_OBJECT,
|
||||
G_TYPE_NONE, 2,
|
||||
SHELL_TYPE_APP_INFO,
|
||||
SHELL_TYPE_APP,
|
||||
META_TYPE_WINDOW);
|
||||
signals[WINDOW_REMOVED] = g_signal_new ("window-removed",
|
||||
SHELL_TYPE_APP_MONITOR,
|
||||
G_SIGNAL_RUN_LAST,
|
||||
0,
|
||||
NULL, NULL,
|
||||
_shell_marshal_VOID__BOXED_OBJECT,
|
||||
_shell_marshal_VOID__OBJECT_OBJECT,
|
||||
G_TYPE_NONE, 2,
|
||||
SHELL_TYPE_APP_INFO,
|
||||
SHELL_TYPE_APP,
|
||||
META_TYPE_WINDOW);
|
||||
|
||||
signals[STARTUP_SEQUENCE_CHANGED] = g_signal_new ("startup-sequence-changed",
|
||||
@ -396,12 +399,6 @@ shell_app_monitor_is_window_usage_tracked (MetaWindow *window)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static ShellAppInfo *
|
||||
create_transient_app_for_window (MetaWindow *window)
|
||||
{
|
||||
return shell_app_system_create_from_window (shell_app_system_get_default (), window);
|
||||
}
|
||||
|
||||
/**
|
||||
* get_app_for_window_direct:
|
||||
*
|
||||
@ -409,12 +406,13 @@ create_transient_app_for_window (MetaWindow *window)
|
||||
* an application based on WM_CLASS. If that fails, then
|
||||
* a "transient" application is created.
|
||||
*
|
||||
* Return value: (transfer full): A newly-referenced #ShellAppInfo
|
||||
* Return value: (transfer full): A newly-referenced #ShellApp
|
||||
*/
|
||||
static ShellAppInfo *
|
||||
static ShellApp *
|
||||
get_app_for_window_direct (MetaWindow *window)
|
||||
{
|
||||
ShellAppInfo *result;
|
||||
ShellApp *app;
|
||||
ShellAppInfo *appinfo;
|
||||
ShellAppSystem *appsys;
|
||||
char *wmclass;
|
||||
char *with_desktop;
|
||||
@ -422,26 +420,28 @@ get_app_for_window_direct (MetaWindow *window)
|
||||
wmclass = get_cleaned_wmclass_for_window (window);
|
||||
|
||||
if (!wmclass)
|
||||
return create_transient_app_for_window (window);
|
||||
return _shell_app_new_for_window (window);
|
||||
|
||||
with_desktop = g_strjoin (NULL, wmclass, ".desktop", NULL);
|
||||
g_free (wmclass);
|
||||
|
||||
appsys = shell_app_system_get_default ();
|
||||
result = shell_app_system_lookup_heuristic_basename (appsys, with_desktop);
|
||||
appinfo = shell_app_system_lookup_heuristic_basename (appsys, with_desktop);
|
||||
g_free (with_desktop);
|
||||
|
||||
if (result == NULL)
|
||||
if (appinfo == NULL)
|
||||
{
|
||||
const char *id = get_app_id_from_title (window);
|
||||
|
||||
if (id != NULL)
|
||||
result = shell_app_system_load_from_desktop_file (appsys, id, NULL);
|
||||
appinfo = shell_app_system_load_from_desktop_file (appsys, id, NULL);
|
||||
}
|
||||
if (result == NULL)
|
||||
result = create_transient_app_for_window (window);
|
||||
if (appinfo == NULL)
|
||||
return _shell_app_new_for_window (window);
|
||||
|
||||
return result;
|
||||
app = _shell_app_new (appinfo);
|
||||
shell_app_info_unref (appinfo);
|
||||
return app;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -451,11 +451,11 @@ get_app_for_window_direct (MetaWindow *window)
|
||||
* all available information such as the window's MetaGroup,
|
||||
* and what we know about other windows.
|
||||
*/
|
||||
static ShellAppInfo *
|
||||
static ShellApp *
|
||||
get_app_for_window (ShellAppMonitor *monitor,
|
||||
MetaWindow *window)
|
||||
{
|
||||
ShellAppInfo *result;
|
||||
ShellApp *result;
|
||||
MetaWindow *source_window;
|
||||
GSList *group_windows;
|
||||
MetaGroup *group;
|
||||
@ -489,7 +489,7 @@ get_app_for_window (ShellAppMonitor *monitor,
|
||||
|
||||
if (result != NULL)
|
||||
{
|
||||
shell_app_info_ref (result);
|
||||
g_object_ref (result);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -502,6 +502,12 @@ get_window_context (MetaWindow *window)
|
||||
return "";
|
||||
}
|
||||
|
||||
static const char *
|
||||
get_app_context (ShellApp *app)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
static GHashTable *
|
||||
get_usages_for_context (ShellAppMonitor *monitor,
|
||||
const char *context)
|
||||
@ -543,7 +549,7 @@ static AppUsage *
|
||||
get_app_usage_from_window (ShellAppMonitor *monitor,
|
||||
MetaWindow *window)
|
||||
{
|
||||
ShellAppInfo *app;
|
||||
ShellApp *app;
|
||||
const char *context;
|
||||
|
||||
app = g_hash_table_lookup (monitor->window_to_app, window);
|
||||
@ -552,7 +558,7 @@ get_app_usage_from_window (ShellAppMonitor *monitor,
|
||||
|
||||
context = get_window_context (window);
|
||||
|
||||
return get_app_usage_for_context_and_id (monitor, context, shell_app_info_get_id (app));
|
||||
return get_app_usage_for_context_and_id (monitor, context, shell_app_get_id (app));
|
||||
}
|
||||
|
||||
static MetaWindow *
|
||||
@ -714,14 +720,9 @@ on_transient_window_title_changed (MetaWindow *window,
|
||||
ShellAppMonitor *self)
|
||||
{
|
||||
ShellAppSystem *appsys;
|
||||
ShellAppInfo *current_app;
|
||||
ShellAppInfo *new_app;
|
||||
const char *id;
|
||||
|
||||
current_app = g_hash_table_lookup (self->window_to_app, window);
|
||||
/* Can't have lost the app */
|
||||
g_assert (current_app != NULL);
|
||||
|
||||
/* Check if we now have a mapping using the window title */
|
||||
id = get_app_id_from_title (window);
|
||||
if (id == NULL)
|
||||
@ -742,7 +743,7 @@ static void
|
||||
track_window (ShellAppMonitor *self,
|
||||
MetaWindow *window)
|
||||
{
|
||||
ShellAppInfo *app;
|
||||
ShellApp *app;
|
||||
AppUsage *usage;
|
||||
|
||||
if (!window_is_tracked (window))
|
||||
@ -764,7 +765,7 @@ track_window (ShellAppMonitor *self,
|
||||
return;
|
||||
|
||||
usage = get_app_usage_from_window (self, window);
|
||||
usage->transient = shell_app_info_is_transient (app);
|
||||
usage->transient = shell_app_info_is_transient (shell_app_get_info (app));
|
||||
|
||||
if (usage->transient)
|
||||
{
|
||||
@ -776,6 +777,8 @@ track_window (ShellAppMonitor *self,
|
||||
g_signal_connect (window, "notify::title", G_CALLBACK (on_transient_window_title_changed), self);
|
||||
}
|
||||
|
||||
_shell_app_add_window (app, window);
|
||||
|
||||
/* Keep track of the number of windows open for this app, when it
|
||||
* switches between 0 and 1 we emit an app-added signal.
|
||||
*/
|
||||
@ -784,7 +787,12 @@ track_window (ShellAppMonitor *self,
|
||||
usage->initially_seen_sequence = ++self->initially_seen_sequence;
|
||||
usage->last_seen = get_time ();
|
||||
if (usage->window_count == 1)
|
||||
g_signal_emit (self, signals[APP_ADDED], 0, app);
|
||||
{
|
||||
/* key is owned by the app */
|
||||
g_hash_table_insert (self->running_apps, (char*)shell_app_get_id (app),
|
||||
app);
|
||||
g_signal_emit (self, signals[APP_ADDED], 0, app);
|
||||
}
|
||||
|
||||
/* Emit window-added after app-added */
|
||||
g_signal_emit (self, signals[WINDOW_ADDED], 0, app, window);
|
||||
@ -800,18 +808,17 @@ shell_app_monitor_on_window_added (MetaWorkspace *workspace,
|
||||
track_window (self, window);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
disassociate_window (ShellAppMonitor *self,
|
||||
MetaWindow *window)
|
||||
{
|
||||
ShellAppInfo *app;
|
||||
ShellApp *app;
|
||||
|
||||
app = g_hash_table_lookup (self->window_to_app, window);
|
||||
if (!app)
|
||||
return;
|
||||
|
||||
shell_app_info_ref (app);
|
||||
g_object_ref (app);
|
||||
|
||||
if (window == self->watched_window)
|
||||
self->watched_window = NULL;
|
||||
@ -827,18 +834,22 @@ disassociate_window (ShellAppMonitor *self,
|
||||
|
||||
g_hash_table_remove (self->window_to_app, window);
|
||||
|
||||
_shell_app_remove_window (app, window);
|
||||
|
||||
g_signal_emit (self, signals[WINDOW_REMOVED], 0, app, window);
|
||||
|
||||
if (usage->window_count == 0)
|
||||
{
|
||||
const char *id = shell_app_get_id (app);
|
||||
g_hash_table_remove (self->running_apps, id);
|
||||
g_signal_emit (self, signals[APP_REMOVED], 0, app);
|
||||
reset_usage (self, context, shell_app_info_get_id (app), usage);
|
||||
reset_usage (self, context, id, usage);
|
||||
}
|
||||
}
|
||||
else
|
||||
g_hash_table_remove (self->window_to_app, window);
|
||||
|
||||
shell_app_info_unref (app);
|
||||
g_object_unref (app);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -931,8 +942,8 @@ shell_app_monitor_get_windows_for_app (ShellAppMonitor *self,
|
||||
while (g_hash_table_iter_next (&iter, &key, &value))
|
||||
{
|
||||
MetaWindow *window = key;
|
||||
ShellAppInfo *app = value;
|
||||
const char *id = shell_app_info_get_id (app);
|
||||
ShellApp *app = value;
|
||||
const char *id = shell_app_get_id (app);
|
||||
|
||||
if (!shell_app_monitor_is_window_usage_tracked (window))
|
||||
continue;
|
||||
@ -1033,7 +1044,9 @@ shell_app_monitor_init (ShellAppMonitor *self)
|
||||
(GDestroyNotify) g_hash_table_destroy);
|
||||
|
||||
self->window_to_app = g_hash_table_new_full (g_direct_hash, g_direct_equal,
|
||||
NULL, (GDestroyNotify) shell_app_info_unref);
|
||||
NULL, (GDestroyNotify) g_object_unref);
|
||||
|
||||
self->running_apps = g_hash_table_new (g_str_hash, g_str_equal);
|
||||
|
||||
g_object_get (shell_global_get(), "configdir", &shell_config_dir, NULL),
|
||||
path = g_build_filename (shell_config_dir, DATA_FILENAME, NULL);
|
||||
@ -1068,6 +1081,7 @@ shell_app_monitor_finalize (GObject *object)
|
||||
gconf_client_notify_remove (self->gconf_client, self->gconf_notify);
|
||||
g_object_unref (self->gconf_client);
|
||||
g_object_unref (self->display);
|
||||
g_hash_table_destroy (self->running_apps);
|
||||
g_hash_table_destroy (self->app_usages_for_context);
|
||||
for (i = 0; title_patterns[i].app_id; i++)
|
||||
g_regex_unref (title_patterns[i].regex);
|
||||
@ -1108,48 +1122,22 @@ shell_app_monitor_get_most_used_apps (ShellAppMonitor *monitor,
|
||||
*
|
||||
* Returns: Application associated with window
|
||||
*/
|
||||
ShellAppInfo *
|
||||
ShellApp *
|
||||
shell_app_monitor_get_window_app (ShellAppMonitor *monitor,
|
||||
MetaWindow *metawin)
|
||||
{
|
||||
MetaWindow *transient_for;
|
||||
ShellAppInfo *info;
|
||||
ShellApp *app;
|
||||
|
||||
transient_for = meta_window_get_transient_for (metawin);
|
||||
if (transient_for != NULL)
|
||||
metawin = transient_for;
|
||||
|
||||
info = g_hash_table_lookup (monitor->window_to_app, metawin);
|
||||
if (info)
|
||||
shell_app_info_ref (info);
|
||||
return info;
|
||||
}
|
||||
app = g_hash_table_lookup (monitor->window_to_app, metawin);
|
||||
if (app)
|
||||
g_object_ref (app);
|
||||
|
||||
typedef struct {
|
||||
ShellAppMonitor *self;
|
||||
const char *context_id;
|
||||
} AppOpenSequenceSortData;
|
||||
|
||||
static int
|
||||
sort_apps_by_open_sequence (gconstpointer a,
|
||||
gconstpointer b,
|
||||
gpointer datap)
|
||||
{
|
||||
AppOpenSequenceSortData *data = datap;
|
||||
ShellAppInfo *app_a = (ShellAppInfo*)a;
|
||||
ShellAppInfo *app_b = (ShellAppInfo*)b;
|
||||
const char *id_a = shell_app_info_get_id (app_a);
|
||||
const char *id_b = shell_app_info_get_id (app_b);
|
||||
AppUsage *usage_a;
|
||||
AppUsage *usage_b;
|
||||
|
||||
usage_a = get_app_usage_for_context_and_id (data->self, data->context_id, id_a);
|
||||
usage_b = get_app_usage_for_context_and_id (data->self, data->context_id, id_b);
|
||||
if (usage_a->initially_seen_sequence == usage_b->initially_seen_sequence)
|
||||
return 0;
|
||||
if (usage_a->initially_seen_sequence < usage_b->initially_seen_sequence)
|
||||
return -1;
|
||||
return 1;
|
||||
return app;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1158,50 +1146,94 @@ sort_apps_by_open_sequence (gconstpointer a,
|
||||
* @context: Activity identifier
|
||||
*
|
||||
* Returns the set of applications which currently have at least one open
|
||||
* window in the given context.
|
||||
* window in the given context. The returned list will be sorted
|
||||
* by shell_app_compare().
|
||||
*
|
||||
* Returns: (element-type ShellAppInfo) (transfer container): Active applications
|
||||
* Returns: (element-type ShellApp) (transfer container): Active applications
|
||||
*/
|
||||
GSList *
|
||||
shell_app_monitor_get_running_apps (ShellAppMonitor *monitor,
|
||||
const char *context)
|
||||
{
|
||||
GHashTableIter iter;
|
||||
gpointer key, value;
|
||||
GSList *ret;
|
||||
AppOpenSequenceSortData data;
|
||||
GHashTable *unique_apps;
|
||||
GHashTableIter iter;
|
||||
|
||||
unique_apps = g_hash_table_new (g_str_hash, g_str_equal);
|
||||
|
||||
g_hash_table_iter_init (&iter, monitor->window_to_app);
|
||||
g_hash_table_iter_init (&iter, monitor->running_apps);
|
||||
|
||||
ret = NULL;
|
||||
while (g_hash_table_iter_next (&iter, &key, &value))
|
||||
{
|
||||
MetaWindow *window = key;
|
||||
ShellAppInfo *app = value;
|
||||
const char *id;
|
||||
ShellApp *app = value;
|
||||
|
||||
if (strcmp (get_window_context (window), context) != 0)
|
||||
if (strcmp (context, get_app_context (app)) != 0)
|
||||
continue;
|
||||
|
||||
if (!shell_app_monitor_is_window_usage_tracked (window))
|
||||
continue;
|
||||
|
||||
id = shell_app_info_get_id (app);
|
||||
|
||||
if (g_hash_table_lookup (unique_apps, id))
|
||||
continue;
|
||||
g_hash_table_insert (unique_apps, (gpointer)id, (gpointer)id);
|
||||
|
||||
ret = g_slist_prepend (ret, app);
|
||||
}
|
||||
g_hash_table_destroy (unique_apps);
|
||||
|
||||
data.self = monitor;
|
||||
data.context_id = context;
|
||||
return g_slist_sort_with_data (ret, sort_apps_by_open_sequence, &data);
|
||||
ret = g_slist_sort (ret, (GCompareFunc)shell_app_compare);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_app_monitor_get_app:
|
||||
* @montior:
|
||||
* @id: Application identifier
|
||||
*
|
||||
* For running applications, returns the existing instance
|
||||
* of the running application model object. Otherwise,
|
||||
* returns a new object.
|
||||
*
|
||||
* Returns: (transfer full): Application associated with id
|
||||
*/
|
||||
ShellApp *
|
||||
shell_app_monitor_get_app (ShellAppMonitor *monitor,
|
||||
const char *id)
|
||||
{
|
||||
ShellApp *app;
|
||||
ShellAppInfo *info;
|
||||
|
||||
app = g_hash_table_lookup (monitor->running_apps, id);
|
||||
if (app)
|
||||
return g_object_ref (app);
|
||||
|
||||
info = shell_app_system_lookup_cached_app (shell_app_system_get_default(), id);
|
||||
if (!info)
|
||||
return NULL;
|
||||
|
||||
app = _shell_app_new (info);
|
||||
shell_app_info_unref (info);
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_app_monitor_get_favorites:
|
||||
* @monitor:
|
||||
*
|
||||
* Returns: (transfer full) (element-type ShellApp): List of favorite applications
|
||||
*/
|
||||
GSList *
|
||||
shell_app_monitor_get_favorites (ShellAppMonitor *monitor)
|
||||
{
|
||||
GSList *apps = NULL;
|
||||
GList *favorite_ids, *iter;
|
||||
|
||||
favorite_ids = shell_app_system_get_favorites (shell_app_system_get_default ());
|
||||
for (iter = favorite_ids; iter; iter = iter->next)
|
||||
{
|
||||
const char *id = iter->data;
|
||||
ShellApp *app;
|
||||
|
||||
app = shell_app_monitor_get_app (monitor, id);
|
||||
if (app)
|
||||
apps = g_slist_prepend (apps, g_object_ref (app));
|
||||
}
|
||||
apps = g_slist_reverse (apps);
|
||||
|
||||
return apps;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
|
@ -6,9 +6,10 @@
|
||||
#include <glib.h>
|
||||
|
||||
#include "window.h"
|
||||
#include "shell-app.h"
|
||||
#include "shell-app-system.h"
|
||||
|
||||
/*
|
||||
/*
|
||||
* This object provides monitoring of system application directories (.desktop files)
|
||||
* and activity-based statistics about applications usage
|
||||
*/
|
||||
@ -29,14 +30,17 @@ typedef struct _ShellAppMonitorPrivate ShellAppMonitorPrivate;
|
||||
struct _ShellAppMonitorClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
|
||||
};
|
||||
|
||||
GType shell_app_monitor_get_type (void) G_GNUC_CONST;
|
||||
|
||||
ShellAppMonitor* shell_app_monitor_get_default(void);
|
||||
|
||||
ShellAppInfo *shell_app_monitor_get_window_app (ShellAppMonitor *monitor, MetaWindow *metawin);
|
||||
ShellApp *shell_app_monitor_get_window_app (ShellAppMonitor *monitor, MetaWindow *metawin);
|
||||
|
||||
ShellApp *shell_app_monitor_get_app (ShellAppMonitor *monitor, const char *id);
|
||||
|
||||
GSList *shell_app_monitor_get_favorites (ShellAppMonitor *monitor);
|
||||
|
||||
GList *shell_app_monitor_get_most_used_apps (ShellAppMonitor *monitor,
|
||||
const char *context,
|
||||
|
331
src/shell-app.c
Normal file
331
src/shell-app.c
Normal file
@ -0,0 +1,331 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
|
||||
#include "shell-app.h"
|
||||
#include "shell-global.h"
|
||||
|
||||
/**
|
||||
* SECTION:shell-app
|
||||
* @short_description: Object representing an application
|
||||
*
|
||||
* This object wraps a #ShellAppInfo, providing methods and signals
|
||||
* primarily useful for running applications.
|
||||
*/
|
||||
struct _ShellApp
|
||||
{
|
||||
GObject parent;
|
||||
|
||||
ShellAppInfo *info;
|
||||
|
||||
guint workspace_switch_id;
|
||||
|
||||
gboolean window_sort_stale;
|
||||
GSList *windows;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (ShellApp, shell_app, G_TYPE_OBJECT);
|
||||
|
||||
enum {
|
||||
WINDOWS_CHANGED,
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
static guint shell_app_signals[LAST_SIGNAL] = { 0 };
|
||||
|
||||
const char *
|
||||
shell_app_get_id (ShellApp *app)
|
||||
{
|
||||
return shell_app_info_get_id (app->info);
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_app_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_create_icon_texture (ShellApp *app,
|
||||
float size)
|
||||
{
|
||||
return shell_app_info_create_icon_texture (app->info, size);
|
||||
}
|
||||
|
||||
char *
|
||||
shell_app_get_name (ShellApp *app)
|
||||
{
|
||||
return shell_app_info_get_name (app->info);
|
||||
}
|
||||
|
||||
char *
|
||||
shell_app_get_description (ShellApp *app)
|
||||
{
|
||||
return shell_app_info_get_description (app->info);
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_app_get_info:
|
||||
*
|
||||
* Returns: (transfer none): Associated app info
|
||||
*/
|
||||
ShellAppInfo *
|
||||
shell_app_get_info (ShellApp *app)
|
||||
{
|
||||
return app->info;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
ShellApp *app;
|
||||
MetaWorkspace *active_workspace;
|
||||
} CompareWindowsData;
|
||||
|
||||
static int
|
||||
shell_app_compare_windows (gconstpointer a,
|
||||
gconstpointer b,
|
||||
gpointer datap)
|
||||
{
|
||||
MetaWindow *win_a = (gpointer)a;
|
||||
MetaWindow *win_b = (gpointer)b;
|
||||
CompareWindowsData *data = datap;
|
||||
gboolean ws_a, ws_b;
|
||||
gboolean vis_a, vis_b;
|
||||
|
||||
ws_a = meta_window_get_workspace (win_a) == data->active_workspace;
|
||||
ws_b = meta_window_get_workspace (win_b) == data->active_workspace;
|
||||
|
||||
if (ws_a && !ws_b)
|
||||
return -1;
|
||||
else if (!ws_a && ws_b)
|
||||
return 1;
|
||||
|
||||
vis_a = meta_window_showing_on_its_workspace (win_a);
|
||||
vis_b = meta_window_showing_on_its_workspace (win_b);
|
||||
|
||||
if (vis_a && !vis_b)
|
||||
return -1;
|
||||
else if (!vis_a && vis_b)
|
||||
return 1;
|
||||
|
||||
return meta_window_get_user_time (win_b) - meta_window_get_user_time (win_a);
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_app_get_windows:
|
||||
* @app:
|
||||
*
|
||||
* Get the toplevel, interesting windows which are associated with this
|
||||
* application. The returned list will be sorted first by whether
|
||||
* they're on the active workspace, then by whether they're visible,
|
||||
* and finally by the time the user last interacted with them.
|
||||
*
|
||||
* Returns: (transfer none) (element-type MetaWindow): List of windows
|
||||
*/
|
||||
GSList *
|
||||
shell_app_get_windows (ShellApp *app)
|
||||
{
|
||||
if (app->window_sort_stale)
|
||||
{
|
||||
CompareWindowsData data;
|
||||
data.app = app;
|
||||
data.active_workspace = meta_screen_get_active_workspace (shell_global_get_screen (shell_global_get ()));
|
||||
app->windows = g_slist_sort_with_data (app->windows, shell_app_compare_windows, &data);
|
||||
app->window_sort_stale = FALSE;
|
||||
}
|
||||
|
||||
return app->windows;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
shell_app_has_visible_windows (ShellApp *app)
|
||||
{
|
||||
GSList *iter;
|
||||
|
||||
for (iter = app->windows; iter; iter = iter->next)
|
||||
{
|
||||
MetaWindow *window = iter->data;
|
||||
|
||||
if (!meta_window_showing_on_its_workspace (window))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
shell_app_is_on_workspace (ShellApp *app,
|
||||
MetaWorkspace *workspace)
|
||||
{
|
||||
GSList *iter;
|
||||
|
||||
for (iter = app->windows; iter; iter = iter->next)
|
||||
{
|
||||
if (meta_window_get_workspace (iter->data) == workspace)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_app_compare:
|
||||
* @app:
|
||||
* @other: A #ShellApp
|
||||
*
|
||||
* Compare one #ShellApp instance to another, in the following way:
|
||||
* - If one of them has visible windows and the other does not, the one
|
||||
* with visible windows is first.
|
||||
* - If one has no windows at all (i.e. it's not running) and the other
|
||||
* does, the one with windows is first.
|
||||
* - Finally, the application which the user interacted with most recently
|
||||
* compares earlier.
|
||||
*/
|
||||
int
|
||||
shell_app_compare (ShellApp *app,
|
||||
ShellApp *other)
|
||||
{
|
||||
gboolean vis_app, vis_other;
|
||||
GSList *windows_app, *windows_other;
|
||||
|
||||
vis_app = shell_app_has_visible_windows (app);
|
||||
vis_other = shell_app_has_visible_windows (other);
|
||||
|
||||
if (vis_app && !vis_other)
|
||||
return -1;
|
||||
else if (!vis_app && vis_other)
|
||||
return 1;
|
||||
|
||||
if (app->windows && !other->windows)
|
||||
return -1;
|
||||
else if (!app->windows && other->windows)
|
||||
return 1;
|
||||
|
||||
windows_app = shell_app_get_windows (app);
|
||||
windows_other = shell_app_get_windows (other);
|
||||
|
||||
return meta_window_get_user_time (windows_other->data) - meta_window_get_user_time (windows_app->data);
|
||||
}
|
||||
|
||||
ShellApp *
|
||||
_shell_app_new_for_window (MetaWindow *window)
|
||||
{
|
||||
ShellApp *app;
|
||||
|
||||
app = g_object_new (SHELL_TYPE_APP, NULL);
|
||||
app->info = shell_app_system_create_from_window (shell_app_system_get_default (), window);
|
||||
_shell_app_add_window (app, window);
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
ShellApp *
|
||||
_shell_app_new (ShellAppInfo *info)
|
||||
{
|
||||
ShellApp *app;
|
||||
|
||||
app = g_object_new (SHELL_TYPE_APP, NULL);
|
||||
app->info = shell_app_info_ref (info);
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
static void
|
||||
shell_app_on_unmanaged (MetaWindow *window,
|
||||
ShellApp *app)
|
||||
{
|
||||
_shell_app_remove_window (app, window);
|
||||
}
|
||||
|
||||
static void
|
||||
shell_app_on_ws_switch (ShellApp *self)
|
||||
{
|
||||
self->window_sort_stale = TRUE;
|
||||
g_signal_emit (self, shell_app_signals[WINDOWS_CHANGED], 0);
|
||||
}
|
||||
|
||||
void
|
||||
_shell_app_add_window (ShellApp *app,
|
||||
MetaWindow *window)
|
||||
{
|
||||
if (g_slist_find (app->windows, window))
|
||||
return;
|
||||
|
||||
app->windows = g_slist_prepend (app->windows, g_object_ref (window));
|
||||
g_signal_connect (window, "unmanaged", G_CALLBACK(shell_app_on_unmanaged), app);
|
||||
app->window_sort_stale = TRUE;
|
||||
|
||||
g_signal_emit (app, shell_app_signals[WINDOWS_CHANGED], 0);
|
||||
|
||||
if (app->workspace_switch_id == 0)
|
||||
{
|
||||
MetaScreen *screen = shell_global_get_screen (shell_global_get ());
|
||||
|
||||
app->workspace_switch_id =
|
||||
g_signal_connect (screen, "workspace-switched", G_CALLBACK(shell_app_on_ws_switch), app);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
disconnect_workspace_switch (ShellApp *app)
|
||||
{
|
||||
MetaScreen *screen;
|
||||
|
||||
if (app->workspace_switch_id == 0)
|
||||
return;
|
||||
|
||||
screen = shell_global_get_screen (shell_global_get ());
|
||||
g_signal_handler_disconnect (screen, app->workspace_switch_id);
|
||||
app->workspace_switch_id = 0;
|
||||
}
|
||||
|
||||
void
|
||||
_shell_app_remove_window (ShellApp *app,
|
||||
MetaWindow *window)
|
||||
{
|
||||
g_object_unref (window);
|
||||
app->windows = g_slist_remove (app->windows, window);
|
||||
if (app->windows == NULL)
|
||||
disconnect_workspace_switch (app);
|
||||
}
|
||||
|
||||
static void
|
||||
shell_app_init (ShellApp *self)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
shell_app_dispose (GObject *object)
|
||||
{
|
||||
ShellApp *app = SHELL_APP (object);
|
||||
|
||||
if (app->info)
|
||||
{
|
||||
shell_app_info_unref (app->info);
|
||||
app->info = NULL;
|
||||
}
|
||||
|
||||
if (app->windows)
|
||||
{
|
||||
g_slist_foreach (app->windows, (GFunc) g_object_unref, NULL);
|
||||
g_slist_free (app->windows);
|
||||
app->windows = NULL;
|
||||
}
|
||||
|
||||
disconnect_workspace_switch (app);
|
||||
}
|
||||
|
||||
static void
|
||||
shell_app_class_init(ShellAppClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
gobject_class->dispose = shell_app_dispose;
|
||||
|
||||
shell_app_signals[WINDOWS_CHANGED] = g_signal_new ("windows-changed",
|
||||
SHELL_TYPE_APP,
|
||||
G_SIGNAL_RUN_LAST,
|
||||
0,
|
||||
NULL, NULL,
|
||||
g_cclosure_marshal_VOID__VOID,
|
||||
G_TYPE_NONE, 0);
|
||||
}
|
56
src/shell-app.h
Normal file
56
src/shell-app.h
Normal file
@ -0,0 +1,56 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
#ifndef __SHELL_APP_H__
|
||||
#define __SHELL_APP_H__
|
||||
|
||||
#include <glib-object.h>
|
||||
#include <glib.h>
|
||||
|
||||
#include "window.h"
|
||||
#include "shell-app-system.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef struct _ShellApp ShellApp;
|
||||
typedef struct _ShellAppClass ShellAppClass;
|
||||
typedef struct _ShellAppPrivate ShellAppPrivate;
|
||||
|
||||
#define SHELL_TYPE_APP (shell_app_get_type ())
|
||||
#define SHELL_APP(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), SHELL_TYPE_APP, ShellApp))
|
||||
#define SHELL_APP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SHELL_TYPE_APP, ShellAppClass))
|
||||
#define SHELL_IS_APP(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), SHELL_TYPE_APP))
|
||||
#define SHELL_IS_APP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SHELL_TYPE_APP))
|
||||
#define SHELL_APP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SHELL_TYPE_APP, ShellAppClass))
|
||||
|
||||
struct _ShellAppClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
|
||||
};
|
||||
|
||||
GType shell_app_get_type (void) G_GNUC_CONST;
|
||||
|
||||
const char *shell_app_get_id (ShellApp *app);
|
||||
|
||||
ClutterActor *shell_app_create_icon_texture (ShellApp *app, float size);
|
||||
char *shell_app_get_name (ShellApp *app);
|
||||
char *shell_app_get_description (ShellApp *app);
|
||||
|
||||
ShellAppInfo *shell_app_get_info (ShellApp *app);
|
||||
|
||||
GSList *shell_app_get_windows (ShellApp *app);
|
||||
|
||||
gboolean shell_app_is_on_workspace (ShellApp *app, MetaWorkspace *workspace);
|
||||
|
||||
int shell_app_compare (ShellApp *app, ShellApp *other);
|
||||
|
||||
ShellApp* _shell_app_new_for_window (MetaWindow *window);
|
||||
|
||||
ShellApp* _shell_app_new (ShellAppInfo *appinfo);
|
||||
|
||||
void _shell_app_add_window (ShellApp *app, MetaWindow *window);
|
||||
|
||||
void _shell_app_remove_window (ShellApp *app, MetaWindow *window);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __SHELL_APP_H__ */
|
@ -2,4 +2,5 @@ VOID:INT,INT,INT
|
||||
VOID:OBJECT,INT,INT,INT,INT
|
||||
VOID:BOXED
|
||||
VOID:BOXED,OBJECT
|
||||
VOID:OBJECT,OBJECT
|
||||
VOID:STRING,OBJECT,BOOLEAN
|
||||
|
Loading…
Reference in New Issue
Block a user