2008-12-01 14:51:43 -05:00
|
|
|
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
2008-11-20 19:53:11 -05:00
|
|
|
|
|
|
|
const Clutter = imports.gi.Clutter;
|
|
|
|
const Gtk = imports.gi.Gtk;
|
|
|
|
const Shell = imports.gi.Shell;
|
2009-04-01 15:51:17 -04:00
|
|
|
const Lang = imports.lang;
|
2009-02-02 18:02:16 -05:00
|
|
|
const Signals = imports.signals;
|
2009-11-12 17:46:59 -05:00
|
|
|
const St = imports.gi.St;
|
2009-06-30 16:35:39 -04:00
|
|
|
const Mainloop = imports.mainloop;
|
2009-08-14 09:30:48 -04:00
|
|
|
const Gettext = imports.gettext.domain('gnome-shell');
|
|
|
|
const _ = Gettext.gettext;
|
2008-11-20 19:53:11 -05:00
|
|
|
|
2009-10-15 19:28:29 -04:00
|
|
|
const AppFavorites = imports.ui.appFavorites;
|
2009-06-30 16:35:39 -04:00
|
|
|
const DND = imports.ui.dnd;
|
2010-07-20 22:22:19 -04:00
|
|
|
const IconGrid = imports.ui.iconGrid;
|
2009-07-31 17:20:26 -04:00
|
|
|
const Main = imports.ui.main;
|
2010-06-06 14:20:00 -04:00
|
|
|
const Overview = imports.ui.overview;
|
2010-05-20 11:18:46 -04:00
|
|
|
const PopupMenu = imports.ui.popupMenu;
|
2009-11-29 17:45:30 -05:00
|
|
|
const Search = imports.ui.search;
|
2010-06-06 14:20:00 -04:00
|
|
|
const Tweener = imports.ui.tweener;
|
2010-01-21 21:33:48 -05:00
|
|
|
const Workspace = imports.ui.workspace;
|
2010-06-22 12:39:14 -04:00
|
|
|
const Params = imports.misc.params;
|
2009-04-01 15:51:17 -04:00
|
|
|
|
2010-07-20 22:22:19 -04:00
|
|
|
const WELL_MAX_COLUMNS = 16;
|
2010-06-22 12:39:14 -04:00
|
|
|
const WELL_MAX_SEARCH_ROWS = 1;
|
2010-03-10 08:52:28 -05:00
|
|
|
const MENU_POPUP_TIMEOUT = 600;
|
2009-04-23 10:41:24 -04:00
|
|
|
|
2010-06-04 18:01:32 -04:00
|
|
|
function AlphabeticalView() {
|
2010-02-15 19:50:36 -05:00
|
|
|
this._init();
|
2008-11-20 19:53:11 -05:00
|
|
|
}
|
|
|
|
|
2010-06-04 18:01:32 -04:00
|
|
|
AlphabeticalView.prototype = {
|
|
|
|
_init: function() {
|
2010-02-15 19:50:36 -05:00
|
|
|
this.actor = new St.BoxLayout({ vertical: true });
|
2010-07-20 22:22:19 -04:00
|
|
|
this._grid = new IconGrid.IconGrid();
|
2010-02-15 19:50:36 -05:00
|
|
|
this._appSystem = Shell.AppSystem.get_default();
|
|
|
|
this.actor.add(this._grid.actor, { y_align: St.Align.START, expand: true });
|
2008-12-01 14:51:43 -05:00
|
|
|
},
|
2008-12-19 23:27:57 -05:00
|
|
|
|
2010-02-15 19:50:36 -05:00
|
|
|
_removeAll: function() {
|
|
|
|
this._grid.removeAll();
|
|
|
|
this._apps = [];
|
2009-07-02 00:35:26 -04:00
|
|
|
},
|
|
|
|
|
2010-02-15 19:50:36 -05:00
|
|
|
_addApp: function(app) {
|
2010-05-08 10:06:28 -04:00
|
|
|
let appIcon = new AppWellIcon(this._appSystem.get_app(app.get_id()));
|
|
|
|
appIcon.connect('launching', Lang.bind(this, function() {
|
2010-02-15 19:50:36 -05:00
|
|
|
this.emit('launching');
|
|
|
|
}));
|
2010-05-08 10:06:28 -04:00
|
|
|
appIcon._draggable.connect('drag-begin', Lang.bind(this, function() {
|
2010-02-15 19:50:36 -05:00
|
|
|
this.emit('drag-begin');
|
|
|
|
}));
|
2009-03-20 12:06:34 -04:00
|
|
|
|
2010-05-08 10:06:28 -04:00
|
|
|
this._grid.addItem(appIcon.actor);
|
2009-03-20 12:06:34 -04:00
|
|
|
|
2010-05-08 10:06:28 -04:00
|
|
|
this._apps.push(appIcon);
|
2009-06-29 15:08:48 -04:00
|
|
|
},
|
|
|
|
|
2010-02-15 19:50:36 -05:00
|
|
|
refresh: function(apps) {
|
|
|
|
let ids = [];
|
|
|
|
for (let i in apps)
|
|
|
|
ids.push(i);
|
|
|
|
ids.sort(function(a, b) {
|
|
|
|
return apps[a].get_name().localeCompare(apps[b].get_name());
|
|
|
|
});
|
|
|
|
|
|
|
|
this._removeAll();
|
2009-08-17 20:29:54 -04:00
|
|
|
|
2010-02-15 19:50:36 -05:00
|
|
|
for (let i = 0; i < ids.length; i++) {
|
|
|
|
this._addApp(apps[ids[i]]);
|
|
|
|
}
|
2009-03-20 12:06:34 -04:00
|
|
|
}
|
2008-12-19 23:27:57 -05:00
|
|
|
};
|
2008-11-20 19:53:11 -05:00
|
|
|
|
2010-06-04 18:01:32 -04:00
|
|
|
Signals.addSignalMethods(AlphabeticalView.prototype);
|
|
|
|
|
|
|
|
function ViewByCategories() {
|
|
|
|
this._init();
|
|
|
|
}
|
|
|
|
|
|
|
|
ViewByCategories.prototype = {
|
|
|
|
_init: function() {
|
|
|
|
this._appSystem = Shell.AppSystem.get_default();
|
|
|
|
this.actor = new St.BoxLayout({ vertical: true });
|
|
|
|
this.actor._delegate = this;
|
|
|
|
this._sections = [];
|
|
|
|
},
|
|
|
|
|
|
|
|
_updateSections: function(apps) {
|
|
|
|
this._removeAll();
|
|
|
|
|
|
|
|
let sections = this._appSystem.get_sections();
|
|
|
|
if (!sections)
|
|
|
|
return;
|
|
|
|
for (let i = 0; i < sections.length; i++) {
|
|
|
|
if (i) {
|
|
|
|
let actor = new St.Bin({ style_class: 'app-section-divider' });
|
|
|
|
let divider = new St.Bin({ style_class: 'app-section-divider-container',
|
|
|
|
child: actor,
|
|
|
|
x_fill: true });
|
|
|
|
|
|
|
|
this.actor.add(divider, { y_fill: false, expand: true });
|
|
|
|
}
|
|
|
|
let _apps = apps.filter(function(app) {
|
|
|
|
return app.get_section() == sections[i];
|
|
|
|
});
|
|
|
|
this._sections[i] = { view: new AlphabeticalView(),
|
|
|
|
apps: _apps,
|
|
|
|
name: sections[i] };
|
|
|
|
this._sections[i].view.connect('launching', Lang.bind(this, function() {
|
|
|
|
this.emit('launching');
|
|
|
|
}));
|
|
|
|
this._sections[i].view.connect('drag-begin', Lang.bind(this, function() {
|
|
|
|
this.emit('drag-begin');
|
|
|
|
}));
|
|
|
|
this.actor.add(this._sections[i].view.actor, { y_align: St.Align.START, expand: true });
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
_removeAll: function() {
|
|
|
|
this.actor.destroy_children();
|
|
|
|
this._sections.forEach(function (section) { section.view.disconnectAll(); });
|
|
|
|
|
|
|
|
this._sections = [];
|
|
|
|
},
|
|
|
|
|
|
|
|
refresh: function(apps) {
|
|
|
|
this._updateSections(apps);
|
|
|
|
for (let i = 0; i < this._sections.length; i++) {
|
|
|
|
this._sections[i].view.refresh(this._sections[i].apps);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
Signals.addSignalMethods(ViewByCategories.prototype);
|
2010-02-15 19:50:36 -05:00
|
|
|
|
2008-12-19 23:27:57 -05:00
|
|
|
/* This class represents a display containing a collection of application items.
|
2010-02-15 19:50:36 -05:00
|
|
|
* The applications are sorted based on their name.
|
2008-12-19 23:27:57 -05:00
|
|
|
*/
|
2010-02-15 19:50:36 -05:00
|
|
|
function AllAppDisplay() {
|
|
|
|
this._init();
|
2008-11-20 19:53:11 -05:00
|
|
|
}
|
|
|
|
|
2010-02-15 19:50:36 -05:00
|
|
|
AllAppDisplay.prototype = {
|
|
|
|
_init: function() {
|
2009-06-18 12:27:19 -04:00
|
|
|
this._appSystem = Shell.AppSystem.get_default();
|
2010-02-15 19:50:36 -05:00
|
|
|
this._appSystem.connect('installed-changed', Lang.bind(this, function() {
|
|
|
|
Main.queueDeferredWork(this._workId);
|
2009-04-23 10:41:24 -04:00
|
|
|
}));
|
2009-04-01 15:51:17 -04:00
|
|
|
|
2010-03-03 18:10:16 -05:00
|
|
|
let bin = new St.BoxLayout({ style_class: 'all-app-controls-panel',
|
|
|
|
reactive: true });
|
2010-02-15 19:50:36 -05:00
|
|
|
this.actor = new St.BoxLayout({ style_class: 'all-app', vertical: true });
|
|
|
|
this.actor.hide();
|
2009-04-01 15:51:17 -04:00
|
|
|
|
2010-02-22 17:07:42 -05:00
|
|
|
let view = new St.ScrollView({ x_fill: true,
|
|
|
|
y_fill: false,
|
|
|
|
style_class: 'all-app-scroll-view',
|
|
|
|
vshadows: true });
|
2010-02-15 19:50:36 -05:00
|
|
|
this._scrollView = view;
|
|
|
|
this.actor.add(bin);
|
|
|
|
this.actor.add(view, { expand: true, y_fill: false, y_align: St.Align.START });
|
2009-06-17 18:42:05 -04:00
|
|
|
|
2010-06-04 18:01:32 -04:00
|
|
|
this._appView = new ViewByCategories();
|
2010-02-15 19:50:36 -05:00
|
|
|
this._appView.connect('launching', Lang.bind(this, this.close));
|
|
|
|
this._appView.connect('drag-begin', Lang.bind(this, this.close));
|
|
|
|
this._scrollView.add_actor(this._appView.actor);
|
2008-12-01 14:51:43 -05:00
|
|
|
|
2010-02-22 11:54:16 -05:00
|
|
|
this._scrollView.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
|
2009-04-01 15:51:17 -04:00
|
|
|
|
2010-02-15 19:50:36 -05:00
|
|
|
this._workId = Main.initializeDeferredWork(this.actor, Lang.bind(this, this._redisplay));
|
2008-12-01 14:51:43 -05:00
|
|
|
},
|
|
|
|
|
2010-02-15 19:50:36 -05:00
|
|
|
_redisplay: function() {
|
|
|
|
let apps = this._appSystem.get_flattened_apps().filter(function(app) {
|
|
|
|
return !app.get_is_nodisplay();
|
|
|
|
});
|
2008-12-01 14:51:43 -05:00
|
|
|
|
2010-02-15 19:50:36 -05:00
|
|
|
this._appView.refresh(apps);
|
2008-12-01 14:51:43 -05:00
|
|
|
},
|
|
|
|
|
2010-02-15 19:50:36 -05:00
|
|
|
toggle: function() {
|
2010-06-06 14:20:00 -04:00
|
|
|
if (this.actor.visible) {
|
|
|
|
Tweener.addTween(this.actor,
|
|
|
|
{ opacity: 0,
|
|
|
|
time: Overview.PANE_FADE_TIME,
|
|
|
|
transition: 'easeOutQuad',
|
|
|
|
onComplete: Lang.bind(this,
|
|
|
|
function() {
|
|
|
|
this.actor.hide();
|
|
|
|
this.emit('open-state-changed',
|
|
|
|
this.actor.visible);
|
|
|
|
})
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
this.actor.show();
|
|
|
|
this.emit('open-state-changed', this.actor.visible);
|
|
|
|
this.actor.opacity = 0;
|
|
|
|
Tweener.addTween(this.actor,
|
|
|
|
{ opacity: 255,
|
|
|
|
time: Overview.PANE_FADE_TIME,
|
|
|
|
transition: 'easeOutQuad'
|
|
|
|
});
|
|
|
|
}
|
2008-12-01 14:51:43 -05:00
|
|
|
},
|
|
|
|
|
2010-02-15 19:50:36 -05:00
|
|
|
close: function() {
|
|
|
|
if (!this.actor.visible)
|
|
|
|
return;
|
|
|
|
this.toggle();
|
2009-04-01 15:51:17 -04:00
|
|
|
}
|
2008-12-01 14:51:43 -05:00
|
|
|
};
|
2008-11-20 19:53:11 -05:00
|
|
|
|
2010-02-15 19:50:36 -05:00
|
|
|
Signals.addSignalMethods(AllAppDisplay.prototype);
|
2009-06-30 16:35:39 -04:00
|
|
|
|
2010-02-22 11:39:10 -05:00
|
|
|
function AppSearchResultDisplay(provider) {
|
|
|
|
this._init(provider);
|
|
|
|
}
|
|
|
|
|
|
|
|
AppSearchResultDisplay.prototype = {
|
|
|
|
__proto__: Search.SearchResultDisplay.prototype,
|
|
|
|
|
|
|
|
_init: function (provider) {
|
|
|
|
Search.SearchResultDisplay.prototype._init.call(this, provider);
|
2010-07-20 22:22:19 -04:00
|
|
|
this._grid = new IconGrid.IconGrid({ rowLimit: WELL_MAX_SEARCH_ROWS });
|
2010-02-22 11:39:10 -05:00
|
|
|
this.actor = new St.Bin({ name: 'dashAppSearchResults',
|
|
|
|
x_align: St.Align.START });
|
2010-06-22 12:39:14 -04:00
|
|
|
this.actor.set_child(this._grid.actor);
|
2010-02-22 11:39:10 -05:00
|
|
|
},
|
|
|
|
|
|
|
|
renderResults: function(results, terms) {
|
|
|
|
let appSys = Shell.AppSystem.get_default();
|
2010-06-22 12:39:14 -04:00
|
|
|
let maxItems = WELL_MAX_SEARCH_ROWS * WELL_MAX_COLUMNS;
|
|
|
|
for (let i = 0; i < results.length && i < maxItems; i++) {
|
2010-02-22 11:39:10 -05:00
|
|
|
let result = results[i];
|
|
|
|
let app = appSys.get_app(result);
|
|
|
|
let display = new AppWellIcon(app);
|
2010-06-22 12:39:14 -04:00
|
|
|
this._grid.addItem(display.actor);
|
2010-02-22 11:39:10 -05:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
clear: function () {
|
2010-06-22 12:39:14 -04:00
|
|
|
this._grid.removeAll();
|
2010-02-22 11:39:10 -05:00
|
|
|
this.selectionIndex = -1;
|
|
|
|
},
|
|
|
|
|
|
|
|
getVisibleResultCount: function() {
|
2010-06-22 12:39:14 -04:00
|
|
|
return this._grid.visibleItemsCount();
|
2010-02-22 11:39:10 -05:00
|
|
|
},
|
|
|
|
|
|
|
|
selectIndex: function (index) {
|
|
|
|
let nVisible = this.getVisibleResultCount();
|
|
|
|
if (this.selectionIndex >= 0) {
|
2010-06-22 12:39:14 -04:00
|
|
|
let prevActor = this._grid.getItemAtIndex(this.selectionIndex);
|
2010-02-22 11:39:10 -05:00
|
|
|
prevActor._delegate.setSelected(false);
|
|
|
|
}
|
|
|
|
this.selectionIndex = -1;
|
|
|
|
if (index >= nVisible)
|
|
|
|
return false;
|
|
|
|
else if (index < 0)
|
|
|
|
return false;
|
2010-06-22 12:39:14 -04:00
|
|
|
let targetActor = this._grid.getItemAtIndex(index);
|
2010-02-22 11:39:10 -05:00
|
|
|
targetActor._delegate.setSelected(true);
|
|
|
|
this.selectionIndex = index;
|
|
|
|
return true;
|
2010-03-04 15:08:24 -05:00
|
|
|
},
|
|
|
|
|
|
|
|
activateSelected: function() {
|
|
|
|
if (this.selectionIndex < 0)
|
|
|
|
return;
|
2010-06-22 12:39:14 -04:00
|
|
|
let targetActor = this._grid.getItemAtIndex(this.selectionIndex);
|
2010-03-15 09:50:05 -04:00
|
|
|
this.provider.activateResult(targetActor._delegate.app.get_id());
|
2010-02-22 11:39:10 -05:00
|
|
|
}
|
2010-03-15 09:50:05 -04:00
|
|
|
};
|
2010-02-22 11:39:10 -05:00
|
|
|
|
2009-11-29 17:45:30 -05:00
|
|
|
function BaseAppSearchProvider() {
|
|
|
|
this._init();
|
|
|
|
}
|
|
|
|
|
|
|
|
BaseAppSearchProvider.prototype = {
|
|
|
|
__proto__: Search.SearchProvider.prototype,
|
|
|
|
|
|
|
|
_init: function(name) {
|
|
|
|
Search.SearchProvider.prototype._init.call(this, name);
|
|
|
|
this._appSys = Shell.AppSystem.get_default();
|
|
|
|
},
|
|
|
|
|
|
|
|
getResultMeta: function(resultId) {
|
|
|
|
let app = this._appSys.get_app(resultId);
|
|
|
|
if (!app)
|
|
|
|
return null;
|
|
|
|
return { 'id': resultId,
|
|
|
|
'name': app.get_name(),
|
|
|
|
'icon': app.create_icon_texture(Search.RESULT_ICON_SIZE)};
|
|
|
|
},
|
|
|
|
|
|
|
|
activateResult: function(id) {
|
2010-02-15 17:11:09 -05:00
|
|
|
let app = this._appSys.get_app(id);
|
Major ShellApp API cleanup, startup notification, window focus handling
This patch combines several high level changes which are conceptually
independent but in practice rather intertwined.
* Add a "state" property to ShellApp which reflects whether it's
stopped, starting, or started. This will allow us to later clean
up all the callers that are using ".get_windows().length > 0" as
a proxy for this property
* Replace shell_app_launch with shell_app_activate and shell_app_open_new_window
A lot of code was calling .launch, but it's signficantly clearer
if we call this ".open_new_window()", and later if we gain the ability
to call into an application's menu, we can implement this correctly rather
than trying to update all .launch callers.
* Because ShellApp now has a "starting" state, rebase panel.js on top of
this so that when we get a startup-notification sequence for an app
and transition it to starting, it becomes the focus app, and panel.js
cleanly just tracks the focus app, rather than bouncing between SN
sequences. This removes display of non-app startup sequences, which
I consider an acceptable action in light of the committed changes
to startup-notification and GTK+.
https://bugzilla.gnome.org/show_bug.cgi?id=614755
2010-04-03 14:07:44 -04:00
|
|
|
app.activate();
|
2010-02-15 17:11:09 -05:00
|
|
|
},
|
|
|
|
|
|
|
|
dragActivateResult: function(id) {
|
2009-11-29 17:45:30 -05:00
|
|
|
let app = this._appSys.get_app(id);
|
Major ShellApp API cleanup, startup notification, window focus handling
This patch combines several high level changes which are conceptually
independent but in practice rather intertwined.
* Add a "state" property to ShellApp which reflects whether it's
stopped, starting, or started. This will allow us to later clean
up all the callers that are using ".get_windows().length > 0" as
a proxy for this property
* Replace shell_app_launch with shell_app_activate and shell_app_open_new_window
A lot of code was calling .launch, but it's signficantly clearer
if we call this ".open_new_window()", and later if we gain the ability
to call into an application's menu, we can implement this correctly rather
than trying to update all .launch callers.
* Because ShellApp now has a "starting" state, rebase panel.js on top of
this so that when we get a startup-notification sequence for an app
and transition it to starting, it becomes the focus app, and panel.js
cleanly just tracks the focus app, rather than bouncing between SN
sequences. This removes display of non-app startup sequences, which
I consider an acceptable action in light of the committed changes
to startup-notification and GTK+.
https://bugzilla.gnome.org/show_bug.cgi?id=614755
2010-04-03 14:07:44 -04:00
|
|
|
app.open_new_window();
|
2009-11-29 17:45:30 -05:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
function AppSearchProvider() {
|
|
|
|
this._init();
|
|
|
|
}
|
|
|
|
|
|
|
|
AppSearchProvider.prototype = {
|
|
|
|
__proto__: BaseAppSearchProvider.prototype,
|
|
|
|
|
|
|
|
_init: function() {
|
|
|
|
BaseAppSearchProvider.prototype._init.call(this, _("APPLICATIONS"));
|
|
|
|
},
|
|
|
|
|
|
|
|
getInitialResultSet: function(terms) {
|
|
|
|
return this._appSys.initial_search(false, terms);
|
|
|
|
},
|
|
|
|
|
|
|
|
getSubsearchResultSet: function(previousResults, terms) {
|
|
|
|
return this._appSys.subsearch(false, previousResults, terms);
|
|
|
|
},
|
|
|
|
|
2010-02-22 11:39:10 -05:00
|
|
|
createResultContainerActor: function () {
|
|
|
|
return new AppSearchResultDisplay(this);
|
|
|
|
},
|
|
|
|
|
|
|
|
createResultActor: function (resultMeta, terms) {
|
|
|
|
return new AppIcon(resultMeta.id);
|
|
|
|
},
|
|
|
|
|
2009-11-29 17:45:30 -05:00
|
|
|
expandSearch: function(terms) {
|
2010-05-13 15:46:04 -04:00
|
|
|
log('TODO expand search');
|
2009-11-29 17:45:30 -05:00
|
|
|
}
|
2010-03-15 09:50:05 -04:00
|
|
|
};
|
2009-11-29 17:45:30 -05:00
|
|
|
|
|
|
|
function PrefsSearchProvider() {
|
|
|
|
this._init();
|
|
|
|
}
|
|
|
|
|
|
|
|
PrefsSearchProvider.prototype = {
|
|
|
|
__proto__: BaseAppSearchProvider.prototype,
|
|
|
|
|
|
|
|
_init: function() {
|
|
|
|
BaseAppSearchProvider.prototype._init.call(this, _("PREFERENCES"));
|
|
|
|
},
|
|
|
|
|
|
|
|
getInitialResultSet: function(terms) {
|
|
|
|
return this._appSys.initial_search(true, terms);
|
|
|
|
},
|
|
|
|
|
|
|
|
getSubsearchResultSet: function(previousResults, terms) {
|
|
|
|
return this._appSys.subsearch(true, previousResults, terms);
|
|
|
|
},
|
|
|
|
|
|
|
|
expandSearch: function(terms) {
|
|
|
|
let controlCenter = this._appSys.load_from_desktop_file('gnomecc.desktop');
|
|
|
|
controlCenter.launch();
|
|
|
|
Main.overview.hide();
|
|
|
|
}
|
2010-03-15 09:50:05 -04:00
|
|
|
};
|
2009-11-12 17:46:59 -05:00
|
|
|
|
2009-12-08 12:51:05 -05:00
|
|
|
function AppIcon(app) {
|
|
|
|
this._init(app);
|
2009-09-01 14:15:29 -04:00
|
|
|
}
|
|
|
|
|
2009-12-08 12:51:05 -05:00
|
|
|
AppIcon.prototype = {
|
2010-07-20 22:22:19 -04:00
|
|
|
__proto__: IconGrid.BaseIcon.prototype,
|
|
|
|
|
2009-12-08 12:51:05 -05:00
|
|
|
_init : function(app) {
|
2009-11-12 17:46:59 -05:00
|
|
|
this.app = app;
|
2009-09-11 17:13:50 -04:00
|
|
|
|
2010-07-20 22:22:19 -04:00
|
|
|
let label = this.app.get_name();
|
2009-11-12 17:46:59 -05:00
|
|
|
|
2010-07-20 22:22:19 -04:00
|
|
|
IconGrid.BaseIcon.prototype._init.call(this, label);
|
|
|
|
},
|
2009-11-12 17:46:59 -05:00
|
|
|
|
2010-07-20 22:22:19 -04:00
|
|
|
createIcon: function(iconSize) {
|
|
|
|
return this.app.create_icon_texture(iconSize);
|
2009-12-08 12:51:05 -05:00
|
|
|
}
|
2010-03-15 09:50:05 -04:00
|
|
|
};
|
2009-12-08 12:51:05 -05:00
|
|
|
|
|
|
|
function AppWellIcon(app) {
|
|
|
|
this._init(app);
|
|
|
|
}
|
|
|
|
|
|
|
|
AppWellIcon.prototype = {
|
|
|
|
_init : function(app) {
|
|
|
|
this.app = app;
|
|
|
|
this.actor = new St.Clickable({ style_class: 'app-well-app',
|
|
|
|
reactive: true,
|
|
|
|
x_fill: true,
|
|
|
|
y_fill: true });
|
|
|
|
this.actor._delegate = this;
|
|
|
|
|
|
|
|
this._icon = new AppIcon(app);
|
|
|
|
this.actor.set_child(this._icon.actor);
|
|
|
|
|
|
|
|
this.actor.connect('clicked', Lang.bind(this, this._onClicked));
|
2010-05-20 11:18:46 -04:00
|
|
|
|
2009-12-08 12:51:05 -05:00
|
|
|
this._menu = null;
|
2010-05-20 11:18:46 -04:00
|
|
|
this._menuManager = new PopupMenu.PopupMenuManager(this);
|
2009-12-08 12:51:05 -05:00
|
|
|
|
2010-03-26 21:35:34 -04:00
|
|
|
this._draggable = DND.makeDraggable(this.actor);
|
|
|
|
this._draggable.connect('drag-begin', Lang.bind(this,
|
|
|
|
function () {
|
|
|
|
this._removeMenuTimeout();
|
2010-05-08 10:06:28 -04:00
|
|
|
Main.overview.beginItemDrag(this);
|
|
|
|
}));
|
|
|
|
this._draggable.connect('drag-end', Lang.bind(this,
|
|
|
|
function () {
|
|
|
|
Main.overview.endItemDrag(this);
|
2010-03-26 21:35:34 -04:00
|
|
|
}));
|
2009-12-08 12:51:05 -05:00
|
|
|
|
|
|
|
this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
|
2010-06-05 18:35:26 -04:00
|
|
|
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
2010-01-07 00:40:21 -05:00
|
|
|
|
2010-03-10 08:52:28 -05:00
|
|
|
this._menuTimeoutId = 0;
|
Major ShellApp API cleanup, startup notification, window focus handling
This patch combines several high level changes which are conceptually
independent but in practice rather intertwined.
* Add a "state" property to ShellApp which reflects whether it's
stopped, starting, or started. This will allow us to later clean
up all the callers that are using ".get_windows().length > 0" as
a proxy for this property
* Replace shell_app_launch with shell_app_activate and shell_app_open_new_window
A lot of code was calling .launch, but it's signficantly clearer
if we call this ".open_new_window()", and later if we gain the ability
to call into an application's menu, we can implement this correctly rather
than trying to update all .launch callers.
* Because ShellApp now has a "starting" state, rebase panel.js on top of
this so that when we get a startup-notification sequence for an app
and transition it to starting, it becomes the focus app, and panel.js
cleanly just tracks the focus app, rather than bouncing between SN
sequences. This removes display of non-app startup sequences, which
I consider an acceptable action in light of the committed changes
to startup-notification and GTK+.
https://bugzilla.gnome.org/show_bug.cgi?id=614755
2010-04-03 14:07:44 -04:00
|
|
|
this._stateChangedId = this.app.connect('notify::state',
|
|
|
|
Lang.bind(this,
|
|
|
|
this._onStateChanged));
|
|
|
|
this._onStateChanged();
|
2010-01-07 00:40:21 -05:00
|
|
|
},
|
|
|
|
|
2010-06-05 18:35:26 -04:00
|
|
|
_onDestroy: function() {
|
Major ShellApp API cleanup, startup notification, window focus handling
This patch combines several high level changes which are conceptually
independent but in practice rather intertwined.
* Add a "state" property to ShellApp which reflects whether it's
stopped, starting, or started. This will allow us to later clean
up all the callers that are using ".get_windows().length > 0" as
a proxy for this property
* Replace shell_app_launch with shell_app_activate and shell_app_open_new_window
A lot of code was calling .launch, but it's signficantly clearer
if we call this ".open_new_window()", and later if we gain the ability
to call into an application's menu, we can implement this correctly rather
than trying to update all .launch callers.
* Because ShellApp now has a "starting" state, rebase panel.js on top of
this so that when we get a startup-notification sequence for an app
and transition it to starting, it becomes the focus app, and panel.js
cleanly just tracks the focus app, rather than bouncing between SN
sequences. This removes display of non-app startup sequences, which
I consider an acceptable action in light of the committed changes
to startup-notification and GTK+.
https://bugzilla.gnome.org/show_bug.cgi?id=614755
2010-04-03 14:07:44 -04:00
|
|
|
if (this._stateChangedId > 0)
|
|
|
|
this.app.disconnect(this._stateChangedId);
|
2010-06-05 18:35:26 -04:00
|
|
|
this._stateChangedId = 0;
|
2010-03-10 08:52:28 -05:00
|
|
|
this._removeMenuTimeout();
|
|
|
|
},
|
|
|
|
|
|
|
|
_removeMenuTimeout: function() {
|
|
|
|
if (this._menuTimeoutId > 0) {
|
|
|
|
Mainloop.source_remove(this._menuTimeoutId);
|
|
|
|
this._menuTimeoutId = 0;
|
|
|
|
}
|
2010-01-07 00:40:21 -05:00
|
|
|
},
|
|
|
|
|
Major ShellApp API cleanup, startup notification, window focus handling
This patch combines several high level changes which are conceptually
independent but in practice rather intertwined.
* Add a "state" property to ShellApp which reflects whether it's
stopped, starting, or started. This will allow us to later clean
up all the callers that are using ".get_windows().length > 0" as
a proxy for this property
* Replace shell_app_launch with shell_app_activate and shell_app_open_new_window
A lot of code was calling .launch, but it's signficantly clearer
if we call this ".open_new_window()", and later if we gain the ability
to call into an application's menu, we can implement this correctly rather
than trying to update all .launch callers.
* Because ShellApp now has a "starting" state, rebase panel.js on top of
this so that when we get a startup-notification sequence for an app
and transition it to starting, it becomes the focus app, and panel.js
cleanly just tracks the focus app, rather than bouncing between SN
sequences. This removes display of non-app startup sequences, which
I consider an acceptable action in light of the committed changes
to startup-notification and GTK+.
https://bugzilla.gnome.org/show_bug.cgi?id=614755
2010-04-03 14:07:44 -04:00
|
|
|
_onStateChanged: function() {
|
|
|
|
if (this.app.state != Shell.AppState.STOPPED)
|
|
|
|
this.actor.add_style_class_name('running');
|
|
|
|
else
|
|
|
|
this.actor.remove_style_class_name('running');
|
2009-11-12 17:46:59 -05:00
|
|
|
},
|
|
|
|
|
|
|
|
_onButtonPress: function(actor, event) {
|
2010-03-10 08:52:28 -05:00
|
|
|
let button = event.get_button();
|
|
|
|
if (button == 1) {
|
|
|
|
this._removeMenuTimeout();
|
|
|
|
this._menuTimeoutId = Mainloop.timeout_add(MENU_POPUP_TIMEOUT,
|
|
|
|
Lang.bind(this, function() {
|
2010-05-20 11:18:46 -04:00
|
|
|
this.popupMenu();
|
2010-03-10 08:52:28 -05:00
|
|
|
}));
|
|
|
|
}
|
2009-11-12 17:46:59 -05:00
|
|
|
},
|
|
|
|
|
|
|
|
_onClicked: function(actor, event) {
|
2010-03-10 08:52:28 -05:00
|
|
|
this._removeMenuTimeout();
|
|
|
|
|
2009-11-12 17:46:59 -05:00
|
|
|
let button = event.get_button();
|
|
|
|
if (button == 1) {
|
|
|
|
this._onActivate(event);
|
2010-04-14 16:22:41 -04:00
|
|
|
} else if (button == 2) {
|
2010-05-12 15:19:40 -04:00
|
|
|
let newWorkspace = Main.overview.workspaces.addWorkspace();
|
2010-05-13 14:29:00 -04:00
|
|
|
if (newWorkspace != null) {
|
|
|
|
newWorkspace.activate(global.get_current_time());
|
|
|
|
this.emit('launching');
|
|
|
|
this.app.open_new_window();
|
|
|
|
Main.overview.hide();
|
|
|
|
}
|
2009-11-12 17:46:59 -05:00
|
|
|
} else if (button == 3) {
|
2010-05-20 11:18:46 -04:00
|
|
|
this.popupMenu();
|
2009-11-12 17:46:59 -05:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
},
|
|
|
|
|
2010-02-15 19:50:36 -05:00
|
|
|
getId: function() {
|
|
|
|
return this.app.get_id();
|
|
|
|
},
|
|
|
|
|
2010-05-20 11:18:46 -04:00
|
|
|
popupMenu: function() {
|
2010-03-10 08:52:28 -05:00
|
|
|
this._removeMenuTimeout();
|
|
|
|
this.actor.fake_release();
|
|
|
|
|
2009-11-12 17:46:59 -05:00
|
|
|
if (!this._menu) {
|
|
|
|
this._menu = new AppIconMenu(this);
|
|
|
|
this._menu.connect('highlight-window', Lang.bind(this, function (menu, window) {
|
|
|
|
this.highlightWindow(window);
|
|
|
|
}));
|
|
|
|
this._menu.connect('activate-window', Lang.bind(this, function (menu, window) {
|
|
|
|
this.activateWindow(window);
|
|
|
|
}));
|
|
|
|
this._menu.connect('popup', Lang.bind(this, function (menu, isPoppedUp) {
|
|
|
|
if (isPoppedUp) {
|
|
|
|
this._onMenuPoppedUp();
|
2009-09-08 15:53:49 -04:00
|
|
|
} else {
|
2009-11-12 17:46:59 -05:00
|
|
|
this._onMenuPoppedDown();
|
2009-09-08 15:53:49 -04:00
|
|
|
}
|
2009-11-12 17:46:59 -05:00
|
|
|
}));
|
2010-05-20 11:18:46 -04:00
|
|
|
|
|
|
|
this._menuManager.addMenu(this._menu, true);
|
2009-11-12 17:46:59 -05:00
|
|
|
}
|
|
|
|
|
2010-05-20 11:18:46 -04:00
|
|
|
this._menu.popup();
|
|
|
|
this._menuManager.grab();
|
2009-11-12 17:46:59 -05:00
|
|
|
|
|
|
|
return false;
|
|
|
|
},
|
|
|
|
|
2009-12-08 12:51:05 -05:00
|
|
|
highlightWindow: function(metaWindow) {
|
2010-01-17 08:48:29 -05:00
|
|
|
if (this._didActivateWindow)
|
|
|
|
return;
|
2009-12-08 12:51:05 -05:00
|
|
|
if (!this._getRunning())
|
|
|
|
return;
|
|
|
|
Main.overview.getWorkspacesForWindow(metaWindow).setHighlightWindow(metaWindow);
|
|
|
|
},
|
|
|
|
|
|
|
|
activateWindow: function(metaWindow) {
|
|
|
|
if (metaWindow) {
|
|
|
|
this._didActivateWindow = true;
|
2010-02-17 14:05:06 -05:00
|
|
|
Main.activateWindow(metaWindow);
|
Major ShellApp API cleanup, startup notification, window focus handling
This patch combines several high level changes which are conceptually
independent but in practice rather intertwined.
* Add a "state" property to ShellApp which reflects whether it's
stopped, starting, or started. This will allow us to later clean
up all the callers that are using ".get_windows().length > 0" as
a proxy for this property
* Replace shell_app_launch with shell_app_activate and shell_app_open_new_window
A lot of code was calling .launch, but it's signficantly clearer
if we call this ".open_new_window()", and later if we gain the ability
to call into an application's menu, we can implement this correctly rather
than trying to update all .launch callers.
* Because ShellApp now has a "starting" state, rebase panel.js on top of
this so that when we get a startup-notification sequence for an app
and transition it to starting, it becomes the focus app, and panel.js
cleanly just tracks the focus app, rather than bouncing between SN
sequences. This removes display of non-app startup sequences, which
I consider an acceptable action in light of the committed changes
to startup-notification and GTK+.
https://bugzilla.gnome.org/show_bug.cgi?id=614755
2010-04-03 14:07:44 -04:00
|
|
|
} else {
|
2009-12-08 12:51:05 -05:00
|
|
|
Main.overview.hide();
|
Major ShellApp API cleanup, startup notification, window focus handling
This patch combines several high level changes which are conceptually
independent but in practice rather intertwined.
* Add a "state" property to ShellApp which reflects whether it's
stopped, starting, or started. This will allow us to later clean
up all the callers that are using ".get_windows().length > 0" as
a proxy for this property
* Replace shell_app_launch with shell_app_activate and shell_app_open_new_window
A lot of code was calling .launch, but it's signficantly clearer
if we call this ".open_new_window()", and later if we gain the ability
to call into an application's menu, we can implement this correctly rather
than trying to update all .launch callers.
* Because ShellApp now has a "starting" state, rebase panel.js on top of
this so that when we get a startup-notification sequence for an app
and transition it to starting, it becomes the focus app, and panel.js
cleanly just tracks the focus app, rather than bouncing between SN
sequences. This removes display of non-app startup sequences, which
I consider an acceptable action in light of the committed changes
to startup-notification and GTK+.
https://bugzilla.gnome.org/show_bug.cgi?id=614755
2010-04-03 14:07:44 -04:00
|
|
|
}
|
2009-12-08 12:51:05 -05:00
|
|
|
},
|
|
|
|
|
2010-02-22 11:39:10 -05:00
|
|
|
setSelected: function (isSelected) {
|
|
|
|
this._selected = isSelected;
|
Major ShellApp API cleanup, startup notification, window focus handling
This patch combines several high level changes which are conceptually
independent but in practice rather intertwined.
* Add a "state" property to ShellApp which reflects whether it's
stopped, starting, or started. This will allow us to later clean
up all the callers that are using ".get_windows().length > 0" as
a proxy for this property
* Replace shell_app_launch with shell_app_activate and shell_app_open_new_window
A lot of code was calling .launch, but it's signficantly clearer
if we call this ".open_new_window()", and later if we gain the ability
to call into an application's menu, we can implement this correctly rather
than trying to update all .launch callers.
* Because ShellApp now has a "starting" state, rebase panel.js on top of
this so that when we get a startup-notification sequence for an app
and transition it to starting, it becomes the focus app, and panel.js
cleanly just tracks the focus app, rather than bouncing between SN
sequences. This removes display of non-app startup sequences, which
I consider an acceptable action in light of the committed changes
to startup-notification and GTK+.
https://bugzilla.gnome.org/show_bug.cgi?id=614755
2010-04-03 14:07:44 -04:00
|
|
|
if (this._selected)
|
|
|
|
this.actor.add_style_class_name('selected');
|
|
|
|
else
|
|
|
|
this.actor.remove_style_class_name('selected');
|
2010-02-22 11:39:10 -05:00
|
|
|
},
|
|
|
|
|
2009-12-08 12:51:05 -05:00
|
|
|
_onMenuPoppedUp: function() {
|
|
|
|
if (this._getRunning()) {
|
|
|
|
Main.overview.getWorkspacesForWindow(null).setApplicationWindowSelection(this.app.get_id());
|
|
|
|
this._setWindowSelection = true;
|
2010-01-17 08:48:29 -05:00
|
|
|
this._didActivateWindow = false;
|
2009-12-08 12:51:05 -05:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
_onMenuPoppedDown: function() {
|
2010-03-19 13:47:34 -04:00
|
|
|
this.actor.sync_hover();
|
|
|
|
|
2009-12-08 12:51:05 -05:00
|
|
|
if (this._didActivateWindow)
|
|
|
|
return;
|
|
|
|
if (!this._setWindowSelection)
|
|
|
|
return;
|
|
|
|
|
|
|
|
Main.overview.getWorkspacesForWindow(null).setApplicationWindowSelection(null);
|
|
|
|
this._setWindowSelection = false;
|
2009-11-12 17:46:59 -05:00
|
|
|
},
|
|
|
|
|
2009-12-08 12:51:05 -05:00
|
|
|
_getRunning: function() {
|
Major ShellApp API cleanup, startup notification, window focus handling
This patch combines several high level changes which are conceptually
independent but in practice rather intertwined.
* Add a "state" property to ShellApp which reflects whether it's
stopped, starting, or started. This will allow us to later clean
up all the callers that are using ".get_windows().length > 0" as
a proxy for this property
* Replace shell_app_launch with shell_app_activate and shell_app_open_new_window
A lot of code was calling .launch, but it's signficantly clearer
if we call this ".open_new_window()", and later if we gain the ability
to call into an application's menu, we can implement this correctly rather
than trying to update all .launch callers.
* Because ShellApp now has a "starting" state, rebase panel.js on top of
this so that when we get a startup-notification sequence for an app
and transition it to starting, it becomes the focus app, and panel.js
cleanly just tracks the focus app, rather than bouncing between SN
sequences. This removes display of non-app startup sequences, which
I consider an acceptable action in light of the committed changes
to startup-notification and GTK+.
https://bugzilla.gnome.org/show_bug.cgi?id=614755
2010-04-03 14:07:44 -04:00
|
|
|
return this.app.state != Shell.AppState.STOPPED;
|
2009-12-08 12:51:05 -05:00
|
|
|
},
|
|
|
|
|
|
|
|
_onActivate: function (event) {
|
2010-02-15 19:50:36 -05:00
|
|
|
this.emit('launching');
|
Major ShellApp API cleanup, startup notification, window focus handling
This patch combines several high level changes which are conceptually
independent but in practice rather intertwined.
* Add a "state" property to ShellApp which reflects whether it's
stopped, starting, or started. This will allow us to later clean
up all the callers that are using ".get_windows().length > 0" as
a proxy for this property
* Replace shell_app_launch with shell_app_activate and shell_app_open_new_window
A lot of code was calling .launch, but it's signficantly clearer
if we call this ".open_new_window()", and later if we gain the ability
to call into an application's menu, we can implement this correctly rather
than trying to update all .launch callers.
* Because ShellApp now has a "starting" state, rebase panel.js on top of
this so that when we get a startup-notification sequence for an app
and transition it to starting, it becomes the focus app, and panel.js
cleanly just tracks the focus app, rather than bouncing between SN
sequences. This removes display of non-app startup sequences, which
I consider an acceptable action in light of the committed changes
to startup-notification and GTK+.
https://bugzilla.gnome.org/show_bug.cgi?id=614755
2010-04-03 14:07:44 -04:00
|
|
|
let modifiers = Shell.get_event_state(event);
|
2009-12-08 12:51:05 -05:00
|
|
|
|
Major ShellApp API cleanup, startup notification, window focus handling
This patch combines several high level changes which are conceptually
independent but in practice rather intertwined.
* Add a "state" property to ShellApp which reflects whether it's
stopped, starting, or started. This will allow us to later clean
up all the callers that are using ".get_windows().length > 0" as
a proxy for this property
* Replace shell_app_launch with shell_app_activate and shell_app_open_new_window
A lot of code was calling .launch, but it's signficantly clearer
if we call this ".open_new_window()", and later if we gain the ability
to call into an application's menu, we can implement this correctly rather
than trying to update all .launch callers.
* Because ShellApp now has a "starting" state, rebase panel.js on top of
this so that when we get a startup-notification sequence for an app
and transition it to starting, it becomes the focus app, and panel.js
cleanly just tracks the focus app, rather than bouncing between SN
sequences. This removes display of non-app startup sequences, which
I consider an acceptable action in light of the committed changes
to startup-notification and GTK+.
https://bugzilla.gnome.org/show_bug.cgi?id=614755
2010-04-03 14:07:44 -04:00
|
|
|
if (modifiers & Clutter.ModifierType.CONTROL_MASK
|
|
|
|
&& this.app.state == Shell.AppState.RUNNING) {
|
|
|
|
this.app.open_new_window();
|
2009-12-08 12:51:05 -05:00
|
|
|
} else {
|
Major ShellApp API cleanup, startup notification, window focus handling
This patch combines several high level changes which are conceptually
independent but in practice rather intertwined.
* Add a "state" property to ShellApp which reflects whether it's
stopped, starting, or started. This will allow us to later clean
up all the callers that are using ".get_windows().length > 0" as
a proxy for this property
* Replace shell_app_launch with shell_app_activate and shell_app_open_new_window
A lot of code was calling .launch, but it's signficantly clearer
if we call this ".open_new_window()", and later if we gain the ability
to call into an application's menu, we can implement this correctly rather
than trying to update all .launch callers.
* Because ShellApp now has a "starting" state, rebase panel.js on top of
this so that when we get a startup-notification sequence for an app
and transition it to starting, it becomes the focus app, and panel.js
cleanly just tracks the focus app, rather than bouncing between SN
sequences. This removes display of non-app startup sequences, which
I consider an acceptable action in light of the committed changes
to startup-notification and GTK+.
https://bugzilla.gnome.org/show_bug.cgi?id=614755
2010-04-03 14:07:44 -04:00
|
|
|
this.app.activate();
|
2009-12-08 12:51:05 -05:00
|
|
|
}
|
Major ShellApp API cleanup, startup notification, window focus handling
This patch combines several high level changes which are conceptually
independent but in practice rather intertwined.
* Add a "state" property to ShellApp which reflects whether it's
stopped, starting, or started. This will allow us to later clean
up all the callers that are using ".get_windows().length > 0" as
a proxy for this property
* Replace shell_app_launch with shell_app_activate and shell_app_open_new_window
A lot of code was calling .launch, but it's signficantly clearer
if we call this ".open_new_window()", and later if we gain the ability
to call into an application's menu, we can implement this correctly rather
than trying to update all .launch callers.
* Because ShellApp now has a "starting" state, rebase panel.js on top of
this so that when we get a startup-notification sequence for an app
and transition it to starting, it becomes the focus app, and panel.js
cleanly just tracks the focus app, rather than bouncing between SN
sequences. This removes display of non-app startup sequences, which
I consider an acceptable action in light of the committed changes
to startup-notification and GTK+.
https://bugzilla.gnome.org/show_bug.cgi?id=614755
2010-04-03 14:07:44 -04:00
|
|
|
Main.overview.hide();
|
2009-09-01 14:15:29 -04:00
|
|
|
},
|
|
|
|
|
2010-05-20 11:18:46 -04:00
|
|
|
// called by this._menuManager when it has the grab
|
|
|
|
menuEventFilter: function(event) {
|
|
|
|
return this._menu.menuEventFilter(event);
|
|
|
|
},
|
|
|
|
|
2009-08-17 20:29:54 -04:00
|
|
|
shellWorkspaceLaunch : function() {
|
Major ShellApp API cleanup, startup notification, window focus handling
This patch combines several high level changes which are conceptually
independent but in practice rather intertwined.
* Add a "state" property to ShellApp which reflects whether it's
stopped, starting, or started. This will allow us to later clean
up all the callers that are using ".get_windows().length > 0" as
a proxy for this property
* Replace shell_app_launch with shell_app_activate and shell_app_open_new_window
A lot of code was calling .launch, but it's signficantly clearer
if we call this ".open_new_window()", and later if we gain the ability
to call into an application's menu, we can implement this correctly rather
than trying to update all .launch callers.
* Because ShellApp now has a "starting" state, rebase panel.js on top of
this so that when we get a startup-notification sequence for an app
and transition it to starting, it becomes the focus app, and panel.js
cleanly just tracks the focus app, rather than bouncing between SN
sequences. This removes display of non-app startup sequences, which
I consider an acceptable action in light of the committed changes
to startup-notification and GTK+.
https://bugzilla.gnome.org/show_bug.cgi?id=614755
2010-04-03 14:07:44 -04:00
|
|
|
this.app.open_new_window();
|
2009-08-17 20:29:54 -04:00
|
|
|
},
|
|
|
|
|
2009-09-11 17:13:50 -04:00
|
|
|
getDragActor: function() {
|
2010-07-20 22:22:19 -04:00
|
|
|
return this.app.create_icon_texture(this._icon.iconSize);
|
2009-06-30 16:35:39 -04:00
|
|
|
},
|
|
|
|
|
2010-02-04 16:57:38 -05:00
|
|
|
// Returns the original actor that should align with the actor
|
|
|
|
// we show as the item is being dragged.
|
2009-06-30 16:35:39 -04:00
|
|
|
getDragActorSource: function() {
|
2010-02-04 16:57:38 -05:00
|
|
|
return this._icon.icon;
|
2009-09-01 14:15:29 -04:00
|
|
|
}
|
2010-03-15 09:50:05 -04:00
|
|
|
};
|
2009-12-08 12:51:05 -05:00
|
|
|
Signals.addSignalMethods(AppWellIcon.prototype);
|
2009-11-12 17:46:59 -05:00
|
|
|
|
|
|
|
function AppIconMenu(source) {
|
|
|
|
this._init(source);
|
|
|
|
}
|
|
|
|
|
|
|
|
AppIconMenu.prototype = {
|
2010-05-20 11:18:46 -04:00
|
|
|
__proto__: PopupMenu.PopupMenu.prototype,
|
|
|
|
|
2009-11-12 17:46:59 -05:00
|
|
|
_init: function(source) {
|
2010-05-20 11:18:46 -04:00
|
|
|
PopupMenu.PopupMenu.prototype._init.call(this, source.actor, St.Align.MIDDLE, St.Side.LEFT, 0);
|
|
|
|
|
2009-11-12 17:46:59 -05:00
|
|
|
this._source = source;
|
|
|
|
|
2010-05-20 11:18:46 -04:00
|
|
|
this.connect('active-changed', Lang.bind(this, this._onActiveChanged));
|
|
|
|
this.connect('activate', Lang.bind(this, this._onActivate));
|
|
|
|
this.connect('open-state-changed', Lang.bind(this, this._onOpenStateChanged));
|
|
|
|
|
|
|
|
this.actor.add_style_class_name('app-well-menu');
|
2009-11-12 17:46:59 -05:00
|
|
|
|
|
|
|
// Chain our visibility and lifecycle to that of the source
|
|
|
|
source.actor.connect('notify::mapped', Lang.bind(this, function () {
|
|
|
|
if (!source.actor.mapped)
|
2010-05-20 11:18:46 -04:00
|
|
|
this.close();
|
2009-11-12 17:46:59 -05:00
|
|
|
}));
|
|
|
|
source.actor.connect('destroy', Lang.bind(this, function () { this.actor.destroy(); }));
|
|
|
|
|
2010-05-06 17:18:10 -04:00
|
|
|
Main.uiGroup.add_actor(this.actor);
|
2009-11-12 17:46:59 -05:00
|
|
|
},
|
|
|
|
|
|
|
|
_redisplay: function() {
|
2010-05-20 11:18:46 -04:00
|
|
|
this.removeAll();
|
2009-11-12 17:46:59 -05:00
|
|
|
|
|
|
|
let windows = this._source.app.get_windows();
|
|
|
|
|
|
|
|
// Display the app windows menu items and the separator between windows
|
|
|
|
// of the current desktop and other windows.
|
|
|
|
let activeWorkspace = global.screen.get_active_workspace();
|
|
|
|
let separatorShown = windows.length > 0 && windows[0].get_workspace() != activeWorkspace;
|
|
|
|
|
|
|
|
for (let i = 0; i < windows.length; i++) {
|
|
|
|
if (!separatorShown && windows[i].get_workspace() != activeWorkspace) {
|
|
|
|
this._appendSeparator();
|
|
|
|
separatorShown = true;
|
|
|
|
}
|
2010-05-20 11:18:46 -04:00
|
|
|
let item = this._appendMenuItem(windows[i].title);
|
|
|
|
item._window = windows[i];
|
2009-11-12 17:46:59 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if (windows.length > 0)
|
|
|
|
this._appendSeparator();
|
|
|
|
|
|
|
|
let isFavorite = AppFavorites.getAppFavorites().isFavorite(this._source.app.get_id());
|
|
|
|
|
|
|
|
this._newWindowMenuItem = windows.length > 0 ? this._appendMenuItem(_("New Window")) : null;
|
|
|
|
|
|
|
|
if (windows.length > 0)
|
|
|
|
this._appendSeparator();
|
|
|
|
this._toggleFavoriteMenuItem = this._appendMenuItem(isFavorite ? _("Remove from Favorites")
|
|
|
|
: _("Add to Favorites"));
|
|
|
|
|
|
|
|
this._highlightedItem = null;
|
|
|
|
},
|
|
|
|
|
|
|
|
_appendSeparator: function () {
|
2010-05-20 11:18:46 -04:00
|
|
|
let separator = new PopupMenu.PopupSeparatorMenuItem();
|
|
|
|
this.addMenuItem(separator);
|
2009-11-12 17:46:59 -05:00
|
|
|
},
|
|
|
|
|
|
|
|
_appendMenuItem: function(labelText) {
|
2010-05-20 11:18:46 -04:00
|
|
|
// FIXME: app-well-menu-item style
|
|
|
|
let item = new PopupMenu.PopupMenuItem(labelText);
|
|
|
|
this.addMenuItem(item);
|
|
|
|
return item;
|
2009-11-12 17:46:59 -05:00
|
|
|
},
|
|
|
|
|
|
|
|
popup: function(activatingButton) {
|
|
|
|
this._redisplay();
|
2010-05-20 11:18:46 -04:00
|
|
|
this.open();
|
2009-11-12 17:46:59 -05:00
|
|
|
},
|
|
|
|
|
2010-05-20 11:18:46 -04:00
|
|
|
_onOpenStateChanged: function (menu, open) {
|
|
|
|
if (open) {
|
|
|
|
this.emit('popup', true);
|
|
|
|
} else {
|
|
|
|
this._updateHighlight(null);
|
|
|
|
this.emit('popup', false);
|
|
|
|
}
|
2009-11-12 17:46:59 -05:00
|
|
|
},
|
|
|
|
|
2010-05-20 11:18:46 -04:00
|
|
|
// called by this._menuManager when it has the grab
|
|
|
|
menuEventFilter: function(event) {
|
|
|
|
let eventType = event.type();
|
|
|
|
|
|
|
|
// Check if the user is interacting with a window representation
|
|
|
|
// rather than interacting with the menu
|
|
|
|
|
|
|
|
if (eventType == Clutter.EventType.BUTTON_RELEASE) {
|
|
|
|
let metaWindow = this._findMetaWindowForActor(event.get_source());
|
|
|
|
if (metaWindow)
|
|
|
|
this.emit('activate-window', metaWindow);
|
|
|
|
} else if (eventType == Clutter.EventType.ENTER) {
|
|
|
|
let metaWindow = this._findMetaWindowForActor(event.get_source());
|
|
|
|
if (metaWindow)
|
|
|
|
this._selectMenuItemForWindow(metaWindow, true);
|
|
|
|
} else if (eventType == Clutter.EventType.LEAVE) {
|
|
|
|
let metaWindow = this._findMetaWindowForActor(event.get_source());
|
|
|
|
if (metaWindow)
|
|
|
|
this._selectMenuItemForWindow(metaWindow, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
2009-11-12 17:46:59 -05:00
|
|
|
},
|
|
|
|
|
|
|
|
_findMetaWindowForActor: function (actor) {
|
2010-01-21 21:33:48 -05:00
|
|
|
if (actor._delegate instanceof Workspace.WindowClone)
|
2009-11-12 17:46:59 -05:00
|
|
|
return actor._delegate.metaWindow;
|
|
|
|
else if (actor.get_meta_window)
|
|
|
|
return actor.get_meta_window();
|
|
|
|
return null;
|
|
|
|
},
|
|
|
|
|
|
|
|
_updateHighlight: function (item) {
|
2010-05-20 11:18:46 -04:00
|
|
|
if (this._highlightedItem)
|
2009-11-12 17:46:59 -05:00
|
|
|
this.emit('highlight-window', null);
|
|
|
|
this._highlightedItem = item;
|
|
|
|
if (this._highlightedItem) {
|
|
|
|
let window = this._highlightedItem._window;
|
|
|
|
if (window)
|
|
|
|
this.emit('highlight-window', window);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2010-05-20 11:18:46 -04:00
|
|
|
_selectMenuItemForWindow: function (metaWindow, selected) {
|
|
|
|
let items = this.getMenuItems();
|
|
|
|
for (let i = 0; i < items.length; i++) {
|
|
|
|
let item = items[i];
|
|
|
|
let menuMetaWindow = item._window;
|
2009-11-12 17:46:59 -05:00
|
|
|
if (menuMetaWindow == metaWindow)
|
2010-05-20 11:18:46 -04:00
|
|
|
item.setActive(selected);
|
2009-11-12 17:46:59 -05:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2010-05-20 11:18:46 -04:00
|
|
|
_onActiveChanged: function (menu, child) {
|
2009-11-12 17:46:59 -05:00
|
|
|
this._updateHighlight(child);
|
|
|
|
},
|
|
|
|
|
2010-05-20 11:18:46 -04:00
|
|
|
_onActivate: function (actor, child) {
|
2009-11-12 17:46:59 -05:00
|
|
|
if (child._window) {
|
|
|
|
let metaWindow = child._window;
|
|
|
|
this.emit('activate-window', metaWindow);
|
|
|
|
} else if (child == this._newWindowMenuItem) {
|
Major ShellApp API cleanup, startup notification, window focus handling
This patch combines several high level changes which are conceptually
independent but in practice rather intertwined.
* Add a "state" property to ShellApp which reflects whether it's
stopped, starting, or started. This will allow us to later clean
up all the callers that are using ".get_windows().length > 0" as
a proxy for this property
* Replace shell_app_launch with shell_app_activate and shell_app_open_new_window
A lot of code was calling .launch, but it's signficantly clearer
if we call this ".open_new_window()", and later if we gain the ability
to call into an application's menu, we can implement this correctly rather
than trying to update all .launch callers.
* Because ShellApp now has a "starting" state, rebase panel.js on top of
this so that when we get a startup-notification sequence for an app
and transition it to starting, it becomes the focus app, and panel.js
cleanly just tracks the focus app, rather than bouncing between SN
sequences. This removes display of non-app startup sequences, which
I consider an acceptable action in light of the committed changes
to startup-notification and GTK+.
https://bugzilla.gnome.org/show_bug.cgi?id=614755
2010-04-03 14:07:44 -04:00
|
|
|
this._source.app.open_new_window();
|
2009-11-12 17:46:59 -05:00
|
|
|
this.emit('activate-window', null);
|
|
|
|
} else if (child == this._toggleFavoriteMenuItem) {
|
|
|
|
let favs = AppFavorites.getAppFavorites();
|
|
|
|
let isFavorite = favs.isFavorite(this._source.app.get_id());
|
|
|
|
if (isFavorite)
|
|
|
|
favs.removeFavorite(this._source.app.get_id());
|
|
|
|
else
|
|
|
|
favs.addFavorite(this._source.app.get_id());
|
|
|
|
}
|
2010-05-20 11:18:46 -04:00
|
|
|
this.close();
|
2009-11-12 17:46:59 -05:00
|
|
|
}
|
|
|
|
};
|
|
|
|
Signals.addSignalMethods(AppIconMenu.prototype);
|
2009-09-01 14:15:29 -04:00
|
|
|
|
2009-08-06 15:39:09 -04:00
|
|
|
function AppWell() {
|
|
|
|
this._init();
|
2009-06-30 16:35:39 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
AppWell.prototype = {
|
2009-08-06 15:39:09 -04:00
|
|
|
_init : function() {
|
2010-06-09 09:41:09 -04:00
|
|
|
this._placeholderText = null;
|
2009-06-30 16:35:39 -04:00
|
|
|
this._menus = [];
|
|
|
|
this._menuDisplays = [];
|
|
|
|
|
2009-10-15 19:28:29 -04:00
|
|
|
this._favorites = [];
|
|
|
|
|
2010-07-20 22:22:19 -04:00
|
|
|
this._grid = new IconGrid.IconGrid();
|
2010-03-31 16:40:32 -04:00
|
|
|
this.actor = this._grid.actor;
|
2009-08-06 15:39:09 -04:00
|
|
|
this.actor._delegate = this;
|
|
|
|
|
2009-12-03 12:19:38 -05:00
|
|
|
this._workId = Main.initializeDeferredWork(this.actor, Lang.bind(this, this._redisplay));
|
2009-10-02 20:17:34 -04:00
|
|
|
|
2009-10-15 19:28:29 -04:00
|
|
|
this._tracker = Shell.WindowTracker.get_default();
|
2009-06-30 16:35:39 -04:00
|
|
|
this._appSystem = Shell.AppSystem.get_default();
|
|
|
|
|
2009-12-03 12:19:38 -05:00
|
|
|
this._appSystem.connect('installed-changed', Lang.bind(this, this._queueRedisplay));
|
|
|
|
AppFavorites.getAppFavorites().connect('changed', Lang.bind(this, this._queueRedisplay));
|
2010-06-07 16:31:30 -04:00
|
|
|
this._tracker.connect('app-state-changed', Lang.bind(this, this._queueRedisplay));
|
2009-06-30 16:35:39 -04:00
|
|
|
},
|
|
|
|
|
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
2009-10-11 16:40:00 -04:00
|
|
|
_appIdListToHash: function(apps) {
|
|
|
|
let ids = {};
|
|
|
|
for (let i = 0; i < apps.length; i++)
|
|
|
|
ids[apps[i].get_id()] = apps[i];
|
|
|
|
return ids;
|
2009-08-06 15:39:09 -04:00
|
|
|
},
|
|
|
|
|
2009-12-03 12:19:38 -05:00
|
|
|
_queueRedisplay: function () {
|
|
|
|
Main.queueDeferredWork(this._workId);
|
2009-10-02 20:17:34 -04:00
|
|
|
},
|
|
|
|
|
2009-08-06 15:39:09 -04:00
|
|
|
_redisplay: function () {
|
|
|
|
this._grid.removeAll();
|
2009-07-30 15:40:26 -04:00
|
|
|
|
2009-10-15 19:28:29 -04:00
|
|
|
let favorites = AppFavorites.getAppFavorites().getFavoriteMap();
|
2009-08-06 15:39:09 -04:00
|
|
|
|
|
|
|
/* hardcode here pending some design about how exactly desktop contexts behave */
|
2010-05-13 15:46:04 -04:00
|
|
|
let contextId = '';
|
2009-07-30 15:40:26 -04:00
|
|
|
|
2009-10-15 19:28:29 -04:00
|
|
|
let running = this._tracker.get_running_apps(contextId);
|
2009-10-14 17:25:17 -04:00
|
|
|
let runningIds = this._appIdListToHash(running);
|
2009-08-06 15:39:09 -04:00
|
|
|
|
2009-11-12 17:46:59 -05:00
|
|
|
let nFavorites = 0;
|
2009-10-15 19:28:29 -04:00
|
|
|
for (let id in favorites) {
|
|
|
|
let app = favorites[id];
|
2009-12-08 12:51:05 -05:00
|
|
|
let display = new AppWellIcon(app);
|
2009-11-12 17:46:59 -05:00
|
|
|
this._grid.addItem(display.actor);
|
|
|
|
nFavorites++;
|
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
2009-10-11 16:40:00 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
for (let i = 0; i < running.length; i++) {
|
|
|
|
let app = running[i];
|
2009-10-15 19:28:29 -04:00
|
|
|
if (app.get_id() in favorites)
|
2009-10-14 15:36:58 -04:00
|
|
|
continue;
|
2009-12-08 12:51:05 -05:00
|
|
|
let display = new AppWellIcon(app);
|
2009-11-12 17:46:59 -05:00
|
|
|
this._grid.addItem(display.actor);
|
2009-08-06 15:39:09 -04:00
|
|
|
}
|
2010-06-09 09:41:09 -04:00
|
|
|
if (this._placeholderText) {
|
|
|
|
this._placeholderText.destroy();
|
|
|
|
this._placeholderText = null;
|
|
|
|
}
|
2009-10-27 07:54:23 -04:00
|
|
|
|
2009-11-12 17:46:59 -05:00
|
|
|
if (running.length == 0 && nFavorites == 0) {
|
2010-06-09 09:41:09 -04:00
|
|
|
this._placeholderText = new St.Label({ text: _("Drag here to add favorites") });
|
|
|
|
this.actor.add_actor(this._placeholderText);
|
2009-10-27 07:54:23 -04:00
|
|
|
}
|
2009-08-06 15:39:09 -04:00
|
|
|
},
|
|
|
|
|
2010-09-09 22:00:28 -04:00
|
|
|
handleDragOver : function(source, actor, x, y, time) {
|
|
|
|
let app = null;
|
|
|
|
if (source instanceof AppWellIcon)
|
|
|
|
app = this._appSystem.get_app(source.getId());
|
|
|
|
else if (source instanceof Workspace.WindowClone)
|
|
|
|
app = this._tracker.get_window_app(source.metaWindow);
|
|
|
|
|
|
|
|
// Don't allow favoriting of transient apps
|
|
|
|
if (app == null || app.is_transient())
|
|
|
|
return DND.DragMotionResult.NO_DROP;
|
|
|
|
|
|
|
|
let id = app.get_id();
|
|
|
|
|
|
|
|
let favorites = AppFavorites.getAppFavorites().getFavoriteMap();
|
|
|
|
|
|
|
|
let srcIsFavorite = (id in favorites);
|
|
|
|
|
|
|
|
if (srcIsFavorite)
|
|
|
|
return DND.DragMotionResult.NO_DROP;
|
|
|
|
|
|
|
|
return DND.DragMotionResult.COPY_DROP;
|
|
|
|
},
|
|
|
|
|
2009-08-06 15:39:09 -04:00
|
|
|
// Draggable target interface
|
|
|
|
acceptDrop : function(source, actor, x, y, time) {
|
2009-08-14 04:35:48 -04:00
|
|
|
let app = null;
|
2010-02-15 19:50:36 -05:00
|
|
|
if (source instanceof AppWellIcon) {
|
2009-10-15 19:28:29 -04:00
|
|
|
app = this._appSystem.get_app(source.getId());
|
2010-01-21 21:33:48 -05:00
|
|
|
} else if (source instanceof Workspace.WindowClone) {
|
2009-10-15 19:28:29 -04:00
|
|
|
app = this._tracker.get_window_app(source.metaWindow);
|
2009-08-06 15:39:09 -04:00
|
|
|
}
|
|
|
|
|
2009-08-14 04:35:48 -04:00
|
|
|
// Don't allow favoriting of transient apps
|
|
|
|
if (app == null || app.is_transient()) {
|
2009-08-06 15:39:09 -04:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2009-08-14 04:35:48 -04:00
|
|
|
let id = app.get_id();
|
|
|
|
|
2009-10-15 19:28:29 -04:00
|
|
|
let favorites = AppFavorites.getAppFavorites().getFavoriteMap();
|
2009-08-06 15:39:09 -04:00
|
|
|
|
2009-10-15 19:28:29 -04:00
|
|
|
let srcIsFavorite = (id in favorites);
|
2009-08-06 15:39:09 -04:00
|
|
|
|
2009-09-25 16:20:43 -04:00
|
|
|
if (srcIsFavorite) {
|
|
|
|
return false;
|
|
|
|
} else {
|
2009-10-15 19:28:29 -04:00
|
|
|
Mainloop.idle_add(Lang.bind(this, function () {
|
|
|
|
AppFavorites.getAppFavorites().addFavorite(id);
|
2009-08-06 15:39:09 -04:00
|
|
|
return false;
|
2009-10-15 19:28:29 -04:00
|
|
|
}));
|
2009-08-06 15:39:09 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
2009-06-30 16:35:39 -04:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
Signals.addSignalMethods(AppWell.prototype);
|