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;
|
2011-02-25 11:50:39 -05:00
|
|
|
const GLib = imports.gi.GLib;
|
2008-11-20 19:53:11 -05:00
|
|
|
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-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-11-29 13:16:20 -05:00
|
|
|
this._grid = new IconGrid.IconGrid({ xAlign: St.Align.START });
|
2010-02-15 19:50:36 -05:00
|
|
|
this._appSystem = Shell.AppSystem.get_default();
|
2010-12-18 14:18:10 -05:00
|
|
|
|
|
|
|
this._filterApp = null;
|
|
|
|
|
|
|
|
let box = new St.BoxLayout({ vertical: true });
|
|
|
|
box.add(this._grid.actor, { y_align: St.Align.START, expand: true });
|
|
|
|
|
|
|
|
this.actor = new St.ScrollView({ x_fill: true,
|
|
|
|
y_fill: false,
|
|
|
|
y_align: St.Align.START,
|
2011-01-20 14:25:21 -05:00
|
|
|
vfade: true });
|
2010-12-18 14:18:10 -05:00
|
|
|
this.actor.add_actor(box);
|
|
|
|
this.actor.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
|
2011-01-21 13:50:25 -05:00
|
|
|
this.actor.connect('notify::mapped', Lang.bind(this,
|
|
|
|
function() {
|
|
|
|
if (!this.actor.mapped)
|
|
|
|
return;
|
|
|
|
|
|
|
|
let adjustment = this.actor.vscroll.adjustment;
|
|
|
|
let direction = Overview.SwipeScrollDirection.VERTICAL;
|
|
|
|
Main.overview.setScrollAdjustment(adjustment, direction);
|
2011-01-21 17:01:07 -05:00
|
|
|
|
|
|
|
// Reset scroll on mapping
|
|
|
|
adjustment.value = 0;
|
2011-01-21 13:50:25 -05:00
|
|
|
}));
|
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-12-18 14:18:10 -05:00
|
|
|
_addApp: function(appInfo) {
|
|
|
|
let appIcon = new AppWellIcon(this._appSystem.get_app(appInfo.get_id()));
|
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-12-18 14:18:10 -05:00
|
|
|
appIcon._appInfo = appInfo;
|
|
|
|
if (this._filterApp && !this._filterApp(appInfo))
|
|
|
|
appIcon.actor.hide();
|
|
|
|
|
2010-05-08 10:06:28 -04:00
|
|
|
this._apps.push(appIcon);
|
2009-06-29 15:08:48 -04:00
|
|
|
},
|
|
|
|
|
2010-12-18 14:18:10 -05:00
|
|
|
setFilter: function(filter) {
|
|
|
|
this._filterApp = filter;
|
|
|
|
for (let i = 0; i < this._apps.length; i++)
|
|
|
|
this._apps[i].actor.visible = filter(this._apps[i]._appInfo);
|
|
|
|
},
|
|
|
|
|
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
|
|
|
function ViewByCategories() {
|
|
|
|
this._init();
|
|
|
|
}
|
|
|
|
|
|
|
|
ViewByCategories.prototype = {
|
|
|
|
_init: function() {
|
|
|
|
this._appSystem = Shell.AppSystem.get_default();
|
2010-12-18 14:18:10 -05:00
|
|
|
this.actor = new St.BoxLayout({ style_class: 'all-app' });
|
2010-06-04 18:01:32 -04:00
|
|
|
this.actor._delegate = this;
|
2010-12-18 14:18:10 -05:00
|
|
|
|
|
|
|
this._view = new AlphabeticalView();
|
|
|
|
|
2011-02-27 15:46:17 -05:00
|
|
|
// categories can be -1 (the All view) or 0...n-1, where n
|
|
|
|
// is the number of sections
|
|
|
|
// -2 is a flag to indicate that nothing is selected
|
|
|
|
// (used only before the actor is mapped the first time)
|
|
|
|
this._currentCategory = -2;
|
|
|
|
this._filters = new St.BoxLayout({ vertical: true, reactive: true });
|
|
|
|
this._filters.connect('scroll-event', Lang.bind(this, this._scrollFilter));
|
|
|
|
|
2010-12-18 14:18:10 -05:00
|
|
|
this.actor.add(this._view.actor, { expand: true, x_fill: true, y_fill: true });
|
|
|
|
this.actor.add(this._filters, { expand: false, y_fill: false, y_align: St.Align.START });
|
|
|
|
|
2011-02-09 17:39:28 -05:00
|
|
|
// Always select the "All" filter when switching to the app view
|
|
|
|
this.actor.connect('notify::mapped', Lang.bind(this,
|
|
|
|
function() {
|
2011-02-10 06:56:19 -05:00
|
|
|
if (this.actor.mapped && this._allFilter)
|
2011-02-09 17:39:28 -05:00
|
|
|
this._selectCategory(-1);
|
|
|
|
}));
|
|
|
|
|
2010-06-04 18:01:32 -04:00
|
|
|
this._sections = [];
|
|
|
|
},
|
|
|
|
|
2011-02-27 15:46:17 -05:00
|
|
|
_scrollFilter: function(actor, event) {
|
|
|
|
let direction = event.get_scroll_direction();
|
|
|
|
if (direction == Clutter.ScrollDirection.UP)
|
|
|
|
this._selectCategory(Math.max(this._currentCategory - 1, -1))
|
|
|
|
else if (direction == Clutter.ScrollDirection.DOWN)
|
|
|
|
this._selectCategory(Math.min(this._currentCategory + 1, this._sections.length - 1));
|
|
|
|
},
|
|
|
|
|
2010-12-18 14:18:10 -05:00
|
|
|
_selectCategory: function(num) {
|
2011-02-27 15:46:17 -05:00
|
|
|
if (this._currentCategory == num) // nothing to do
|
|
|
|
return;
|
|
|
|
|
|
|
|
this._currentCategory = num;
|
|
|
|
|
2010-12-18 14:18:10 -05:00
|
|
|
if (num != -1)
|
|
|
|
this._allFilter.remove_style_pseudo_class('selected');
|
|
|
|
else
|
|
|
|
this._allFilter.add_style_pseudo_class('selected');
|
2010-06-04 18:01:32 -04:00
|
|
|
|
2010-12-18 14:18:10 -05:00
|
|
|
this._view.setFilter(Lang.bind(this, function(app) {
|
|
|
|
if (num == -1)
|
|
|
|
return true;
|
|
|
|
return this._sections[num].name == app.get_section();
|
|
|
|
}));
|
|
|
|
|
|
|
|
for (let i = 0; i < this._sections.length; i++) {
|
|
|
|
if (i == num)
|
|
|
|
this._sections[i].filterActor.add_style_pseudo_class('selected');
|
|
|
|
else
|
|
|
|
this._sections[i].filterActor.remove_style_pseudo_class('selected');
|
2010-06-04 18:01:32 -04:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2010-12-18 14:18:10 -05:00
|
|
|
_addFilter: function(name, num) {
|
2011-02-25 11:50:39 -05:00
|
|
|
let button = new St.Button({ label: GLib.markup_escape_text (name, -1),
|
2010-12-18 14:18:10 -05:00
|
|
|
style_class: 'app-filter',
|
|
|
|
x_align: St.Align.START });
|
|
|
|
this._filters.add(button, { expand: true, x_fill: true, y_fill: false });
|
|
|
|
button.connect('clicked', Lang.bind(this, function() {
|
|
|
|
this._selectCategory(num);
|
|
|
|
}));
|
|
|
|
|
|
|
|
if (num != -1)
|
|
|
|
this._sections[num] = { filterActor: button,
|
|
|
|
name: name };
|
|
|
|
else
|
|
|
|
this._allFilter = button;
|
|
|
|
},
|
2010-06-04 18:01:32 -04:00
|
|
|
|
2010-12-18 14:18:10 -05:00
|
|
|
_removeAll: function() {
|
2010-06-04 18:01:32 -04:00
|
|
|
this._sections = [];
|
2010-12-18 14:18:10 -05:00
|
|
|
this._filters.destroy_children();
|
2010-06-04 18:01:32 -04:00
|
|
|
},
|
|
|
|
|
|
|
|
refresh: function(apps) {
|
2010-12-18 14:18:10 -05:00
|
|
|
this._removeAll();
|
|
|
|
|
|
|
|
let sections = this._appSystem.get_sections();
|
|
|
|
this._apps = apps;
|
|
|
|
this._view.refresh(apps);
|
|
|
|
|
2010-12-21 12:23:54 -05:00
|
|
|
/* Translators: Filter to display all applications */
|
2010-12-18 14:18:10 -05:00
|
|
|
this._addFilter(_("All"), -1);
|
|
|
|
|
|
|
|
if (!sections)
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (let i = 0; i < sections.length; i++)
|
|
|
|
this._addFilter(sections[i], i);
|
|
|
|
|
|
|
|
this._selectCategory(-1);
|
2010-06-04 18:01:32 -04: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-06-04 18:01:32 -04:00
|
|
|
this._appView = new ViewByCategories();
|
2010-12-18 14:18:10 -05:00
|
|
|
this.actor = new St.Bin({ child: this._appView.actor, x_fill: true, y_fill: true });
|
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);
|
2009-04-01 15:51:17 -04:00
|
|
|
}
|
2008-12-01 14:51:43 -05: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(),
|
2011-02-28 14:36:52 -05:00
|
|
|
'createIcon': function(size) {
|
|
|
|
return app.create_icon_texture(size);
|
|
|
|
}
|
|
|
|
};
|
2009-11-29 17:45:30 -05:00
|
|
|
},
|
|
|
|
|
2011-01-30 16:09:58 -05:00
|
|
|
activateResult: function(id, params) {
|
|
|
|
params = Params.parse(params, { workspace: null,
|
|
|
|
timestamp: null });
|
|
|
|
|
2010-02-15 17:11:09 -05:00
|
|
|
let app = this._appSys.get_app(id);
|
2011-01-30 16:09:58 -05:00
|
|
|
app.activate(params.workspace ? params.workspace.index() : -1);
|
2010-02-15 17:11:09 -05:00
|
|
|
},
|
|
|
|
|
2011-01-30 16:09:58 -05:00
|
|
|
dragActivateResult: function(id, params) {
|
|
|
|
params = Params.parse(params, { workspace: null,
|
|
|
|
timestamp: null });
|
|
|
|
|
2009-11-29 17:45:30 -05:00
|
|
|
let app = this._appSys.get_app(id);
|
2011-01-30 16:09:58 -05:00
|
|
|
app.open_new_window(params.workspace ? params.workspace.get_index() : -1);
|
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
|
|
|
createResultActor: function (resultMeta, terms) {
|
2010-10-14 08:27:28 -04:00
|
|
|
let app = this._appSys.get_app(resultMeta['id']);
|
|
|
|
let icon = new AppWellIcon(app);
|
|
|
|
return icon.actor;
|
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() {
|
2011-03-01 19:04:09 -05:00
|
|
|
BaseAppSearchProvider.prototype._init.call(this, _("SETTINGS"));
|
2009-11-29 17:45:30 -05:00
|
|
|
},
|
|
|
|
|
|
|
|
getInitialResultSet: function(terms) {
|
|
|
|
return this._appSys.initial_search(true, terms);
|
|
|
|
},
|
|
|
|
|
|
|
|
getSubsearchResultSet: function(previousResults, terms) {
|
|
|
|
return this._appSys.subsearch(true, previousResults, terms);
|
|
|
|
}
|
2010-03-15 09:50:05 -04:00
|
|
|
};
|
2009-11-12 17:46:59 -05:00
|
|
|
|
2011-01-13 10:02:55 -05:00
|
|
|
function AppIcon(app, params) {
|
|
|
|
this._init(app, params);
|
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,
|
|
|
|
|
2011-01-13 10:02:55 -05:00
|
|
|
_init : function(app, params) {
|
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-21 19:29:02 -04:00
|
|
|
IconGrid.BaseIcon.prototype._init.call(this,
|
|
|
|
label,
|
2011-01-13 10:02:55 -05:00
|
|
|
params);
|
2010-07-20 22:22:19 -04:00
|
|
|
},
|
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
|
|
|
|
2011-01-13 10:02:55 -05:00
|
|
|
function AppWellIcon(app, iconParams) {
|
|
|
|
this._init(app, iconParams);
|
2009-12-08 12:51:05 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
AppWellIcon.prototype = {
|
2011-01-13 10:02:55 -05:00
|
|
|
_init : function(app, iconParams) {
|
2009-12-08 12:51:05 -05:00
|
|
|
this.app = app;
|
2011-01-25 16:22:00 -05:00
|
|
|
this.actor = new St.Button({ style_class: 'app-well-app',
|
|
|
|
reactive: true,
|
|
|
|
button_mask: St.ButtonMask.ONE | St.ButtonMask.TWO,
|
2011-02-03 12:38:03 -05:00
|
|
|
can_focus: true,
|
2011-01-25 16:22:00 -05:00
|
|
|
x_fill: true,
|
|
|
|
y_fill: true });
|
2009-12-08 12:51:05 -05:00
|
|
|
this.actor._delegate = this;
|
|
|
|
|
2011-01-13 10:02:55 -05:00
|
|
|
this.icon = new AppIcon(app, iconParams);
|
2010-07-21 19:29:02 -04:00
|
|
|
this.actor.set_child(this.icon.actor);
|
2009-12-08 12:51:05 -05:00
|
|
|
|
2011-01-26 11:15:41 -05:00
|
|
|
this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
|
2009-12-08 12:51:05 -05:00
|
|
|
this.actor.connect('clicked', Lang.bind(this, this._onClicked));
|
2011-02-03 12:38:03 -05:00
|
|
|
this.actor.connect('popup-menu', Lang.bind(this, this._onKeyboardPopupMenu));
|
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
|
|
|
|
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
|
|
|
}));
|
2011-01-26 11:15:41 -05:00
|
|
|
} else if (button == 3) {
|
|
|
|
this.popupMenu();
|
|
|
|
return true;
|
2010-03-10 08:52:28 -05:00
|
|
|
}
|
2011-01-26 11:15:41 -05:00
|
|
|
return false;
|
2009-11-12 17:46:59 -05:00
|
|
|
},
|
|
|
|
|
2011-01-25 16:22:00 -05:00
|
|
|
_onClicked: function(actor, button) {
|
2010-03-10 08:52:28 -05:00
|
|
|
this._removeMenuTimeout();
|
|
|
|
|
2009-11-12 17:46:59 -05:00
|
|
|
if (button == 1) {
|
2011-01-25 16:22:00 -05:00
|
|
|
this._onActivate(Clutter.get_current_event());
|
2010-04-14 16:22:41 -04:00
|
|
|
} else if (button == 2) {
|
2011-01-25 16:45:58 -05:00
|
|
|
// Last workspace is always empty
|
|
|
|
let launchWorkspace = global.screen.get_workspace_by_index(global.screen.n_workspaces - 1);
|
|
|
|
launchWorkspace.activate(global.get_current_time());
|
|
|
|
this.emit('launching');
|
2011-01-30 16:09:58 -05:00
|
|
|
this.app.open_new_window(-1);
|
2011-01-25 16:45:58 -05:00
|
|
|
Main.overview.hide();
|
2009-11-12 17:46:59 -05:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
},
|
|
|
|
|
2011-02-03 12:38:03 -05:00
|
|
|
_onKeyboardPopupMenu: function() {
|
|
|
|
this.popupMenu();
|
|
|
|
this._menu.actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, 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('activate-window', Lang.bind(this, function (menu, window) {
|
|
|
|
this.activateWindow(window);
|
|
|
|
}));
|
|
|
|
this._menu.connect('popup', Lang.bind(this, function (menu, isPoppedUp) {
|
2011-02-12 14:03:44 -05:00
|
|
|
if (!isPoppedUp)
|
2009-11-12 17:46:59 -05:00
|
|
|
this._onMenuPoppedDown();
|
|
|
|
}));
|
2011-02-03 12:38:03 -05:00
|
|
|
Main.overview.connect('hiding', Lang.bind(this, function () { this._menu.close(); }));
|
2010-05-20 11:18:46 -04:00
|
|
|
|
2010-10-07 14:15:51 -04:00
|
|
|
this._menuManager.addMenu(this._menu);
|
2009-11-12 17:46:59 -05:00
|
|
|
}
|
|
|
|
|
2010-05-20 11:18:46 -04:00
|
|
|
this._menu.popup();
|
2009-11-12 17:46:59 -05:00
|
|
|
|
|
|
|
return false;
|
|
|
|
},
|
|
|
|
|
2009-12-08 12:51:05 -05:00
|
|
|
activateWindow: function(metaWindow) {
|
|
|
|
if (metaWindow) {
|
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
|
|
|
},
|
|
|
|
|
|
|
|
_onMenuPoppedDown: function() {
|
2010-03-19 13:47:34 -04:00
|
|
|
this.actor.sync_hover();
|
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) {
|
2011-01-30 16:09:58 -05:00
|
|
|
this.app.open_new_window(-1);
|
2009-12-08 12:51:05 -05:00
|
|
|
} else {
|
2011-01-30 16:09:58 -05:00
|
|
|
this.app.activate(-1);
|
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
|
|
|
},
|
|
|
|
|
2011-01-30 16:09:58 -05:00
|
|
|
shellWorkspaceLaunch : function(params) {
|
|
|
|
params = Params.parse(params, { workspace: null,
|
|
|
|
timestamp: null });
|
|
|
|
|
|
|
|
this.app.open_new_window(params.workspace ? params.workspace.index() : -1);
|
2009-08-17 20:29:54 -04:00
|
|
|
},
|
|
|
|
|
2009-09-11 17:13:50 -04:00
|
|
|
getDragActor: function() {
|
2011-02-10 10:07:05 -05:00
|
|
|
return this.app.create_icon_texture(Main.overview.dash.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-07-21 19:29:02 -04: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-11-29 16:07:10 -05:00
|
|
|
let side = St.Side.LEFT;
|
|
|
|
if (St.Widget.get_default_direction() == St.TextDirection.RTL)
|
|
|
|
side = St.Side.RIGHT;
|
|
|
|
|
2011-02-09 12:27:00 -05:00
|
|
|
PopupMenu.PopupMenu.prototype._init.call(this, source.actor, 0.5, side, 0);
|
2010-05-20 11:18:46 -04:00
|
|
|
|
2009-11-12 17:46:59 -05:00
|
|
|
this._source = source;
|
|
|
|
|
2010-05-20 11:18:46 -04:00
|
|
|
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());
|
|
|
|
|
2011-02-25 09:43:43 -05:00
|
|
|
this._newWindowMenuItem = this._appendMenuItem(_("New Window"));
|
|
|
|
this._appendSeparator();
|
2009-11-12 17:46:59 -05:00
|
|
|
|
|
|
|
this._toggleFavoriteMenuItem = this._appendMenuItem(isFavorite ? _("Remove from Favorites")
|
|
|
|
: _("Add to Favorites"));
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
_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.emit('popup', false);
|
|
|
|
}
|
2009-11-12 17:46:59 -05:00
|
|
|
},
|
|
|
|
|
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) {
|
2011-01-30 16:09:58 -05:00
|
|
|
this._source.app.open_new_window(-1);
|
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);
|