2011-09-28 09:16:26 -04:00
|
|
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
2008-11-20 19:53:11 -05:00
|
|
|
|
|
|
|
const Clutter = imports.gi.Clutter;
|
2012-11-26 17:01:44 -05:00
|
|
|
const Gio = imports.gi.Gio;
|
2011-02-25 11:50:39 -05:00
|
|
|
const GLib = imports.gi.GLib;
|
2013-01-31 11:13:37 -05:00
|
|
|
const GObject = imports.gi.GObject;
|
2008-11-20 19:53:11 -05:00
|
|
|
const Gtk = imports.gi.Gtk;
|
Kill off ShellAppInfo, move into ShellApp
This dramatically thins down and sanitizes the application code.
The ShellAppSystem changes in a number of ways:
* Preferences are special cased more explicitly; they aren't apps,
they're shortcuts for an app), and we don't have many of them, so
don't need e.g. the optimizations in ShellAppSystem for searching.
* get_app() changes to lookup_app() and returns null if an app isn't
found. The semantics where it tried to find the .desktop file
if we didn't know about it were just broken; I am pretty sure no
caller needs this, and if they do we'll fix them.
* ShellAppSystem maintains two indexes on apps (by desktop file id
and by GMenuTreeEntry), but is no longer in the business of
dealing with GMenuTree as far as hierarchy and categories go. That
is moved up into js/ui/appDisplay.js. Actually, it flattens both
apps and settings.
Also, ShellWindowTracker is now the sole reference-owner for
window-backed apps. We still do the weird "window:0x1234beef" id
for these apps, but a reference is not stored in ShellAppSystem.
The js/ui/appDisplay.js code is rewritten, and sucks a lot less.
Variable names are clearer:
_apps -> _appIcons
_filterApp -> _visibleApps
_filters -> _categoryBox
Similarly for function names. We no longer call (for every app) a
recursive lookup in GMenuTree to see if it's in a particular section
on every category switch; it's all cached.
NOTE - this intentionally reverts the incremental loading code from
commit 7813c5b93f6bcde8c4beae286e82bfc472b2b656. It's fast enough
here without that.
https://bugzilla.gnome.org/show_bug.cgi?id=648149
2011-04-21 13:35:01 -04:00
|
|
|
const GMenu = imports.gi.GMenu;
|
2008-11-20 19:53:11 -05:00
|
|
|
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;
|
2011-04-25 15:42:03 -04:00
|
|
|
const Meta = imports.gi.Meta;
|
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;
|
2012-02-17 20:40:40 -05:00
|
|
|
const Atk = imports.gi.Atk;
|
2008-11-20 19:53:11 -05:00
|
|
|
|
2009-10-15 19:28:29 -04:00
|
|
|
const AppFavorites = imports.ui.appFavorites;
|
2013-01-31 11:13:37 -05:00
|
|
|
const BoxPointer = imports.ui.boxpointer;
|
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;
|
2013-02-18 14:18:08 -05:00
|
|
|
const OverviewControls = imports.ui.overviewControls;
|
2010-05-20 11:18:46 -04:00
|
|
|
const PopupMenu = imports.ui.popupMenu;
|
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;
|
2012-06-11 13:53:32 -04:00
|
|
|
const Util = imports.misc.util;
|
2009-04-01 15:51:17 -04:00
|
|
|
|
2011-04-25 15:42:03 -04:00
|
|
|
const MAX_APPLICATION_WORK_MILLIS = 75;
|
2010-03-10 08:52:28 -05:00
|
|
|
const MENU_POPUP_TIMEOUT = 600;
|
2013-02-15 10:47:39 -05:00
|
|
|
const MAX_COLUMNS = 6;
|
2009-04-23 10:41:24 -04:00
|
|
|
|
2013-02-18 15:04:51 -05:00
|
|
|
const INACTIVE_GRID_OPACITY = 77;
|
2013-01-31 11:13:37 -05:00
|
|
|
const FOLDER_SUBICON_FRACTION = .4;
|
|
|
|
|
|
|
|
|
2013-01-31 09:16:25 -05:00
|
|
|
// Recursively load a GMenuTreeDirectory; we could put this in ShellAppSystem too
|
|
|
|
function _loadCategory(dir, view) {
|
|
|
|
let iter = dir.iter();
|
|
|
|
let appSystem = Shell.AppSystem.get_default();
|
|
|
|
let nextType;
|
|
|
|
while ((nextType = iter.next()) != GMenu.TreeItemType.INVALID) {
|
|
|
|
if (nextType == GMenu.TreeItemType.ENTRY) {
|
|
|
|
let entry = iter.get_entry();
|
|
|
|
let app = appSystem.lookup_app_by_tree_entry(entry);
|
|
|
|
if (!entry.get_app_info().get_nodisplay())
|
|
|
|
view.addApp(app);
|
|
|
|
} else if (nextType == GMenu.TreeItemType.DIRECTORY) {
|
|
|
|
let itemDir = iter.get_directory();
|
|
|
|
if (!itemDir.get_is_nodisplay())
|
|
|
|
_loadCategory(itemDir, view);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2011-11-20 12:56:27 -05:00
|
|
|
const AlphabeticalView = new Lang.Class({
|
|
|
|
Name: 'AlphabeticalView',
|
2013-02-19 17:23:41 -05:00
|
|
|
Abstract: true,
|
2008-11-20 19:53:11 -05:00
|
|
|
|
2010-06-04 18:01:32 -04:00
|
|
|
_init: function() {
|
2013-02-15 10:47:39 -05:00
|
|
|
this._grid = new IconGrid.IconGrid({ xAlign: St.Align.MIDDLE,
|
|
|
|
columnLimit: MAX_COLUMNS });
|
2010-12-18 14:18:10 -05:00
|
|
|
|
2013-02-19 18:38:11 -05:00
|
|
|
// Standard hack for ClutterBinLayout
|
|
|
|
this._grid.actor.x_expand = true;
|
|
|
|
|
2013-02-19 17:23:41 -05:00
|
|
|
this._items = {};
|
|
|
|
this._allItems = [];
|
|
|
|
},
|
|
|
|
|
|
|
|
removeAll: function() {
|
|
|
|
this._grid.removeAll();
|
|
|
|
this._items = {};
|
|
|
|
this._allItems = [];
|
|
|
|
},
|
|
|
|
|
|
|
|
_getItemId: function(item) {
|
|
|
|
throw new Error('Not implemented');
|
|
|
|
},
|
|
|
|
|
|
|
|
_createItemIcon: function(item) {
|
|
|
|
throw new Error('Not implemented');
|
|
|
|
},
|
|
|
|
|
|
|
|
_compareItems: function(a, b) {
|
|
|
|
throw new Error('Not implemented');
|
|
|
|
},
|
|
|
|
|
|
|
|
_addItem: function(item) {
|
|
|
|
let id = this._getItemId(item);
|
|
|
|
if (this._items[id] !== undefined)
|
|
|
|
return null;
|
|
|
|
|
|
|
|
let itemIcon = this._createItemIcon(item);
|
2013-02-19 15:31:05 -05:00
|
|
|
this._allItems.push(item);
|
2013-02-19 17:23:41 -05:00
|
|
|
this._items[id] = itemIcon;
|
|
|
|
|
|
|
|
return itemIcon;
|
2013-02-19 15:31:05 -05:00
|
|
|
},
|
|
|
|
|
|
|
|
loadGrid: function() {
|
|
|
|
this._allItems.sort(this._compareItems);
|
|
|
|
|
|
|
|
for (let i = 0; i < this._allItems.length; i++) {
|
|
|
|
let id = this._getItemId(this._allItems[i]);
|
|
|
|
if (!id)
|
|
|
|
continue;
|
|
|
|
this._grid.addItem(this._items[id].actor);
|
|
|
|
}
|
2013-02-19 17:23:41 -05:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2013-01-31 11:13:37 -05:00
|
|
|
const FolderView = new Lang.Class({
|
|
|
|
Name: 'FolderView',
|
|
|
|
Extends: AlphabeticalView,
|
|
|
|
|
|
|
|
_init: function() {
|
|
|
|
this.parent();
|
|
|
|
this.actor = this._grid.actor;
|
|
|
|
},
|
|
|
|
|
|
|
|
_getItemId: function(item) {
|
|
|
|
return item.get_id();
|
|
|
|
},
|
|
|
|
|
|
|
|
_createItemIcon: function(item) {
|
|
|
|
return new AppIcon(item);
|
|
|
|
},
|
|
|
|
|
|
|
|
_compareItems: function(a, b) {
|
|
|
|
return a.compare_by_name(b);
|
|
|
|
},
|
|
|
|
|
|
|
|
addApp: function(app) {
|
|
|
|
this._addItem(app);
|
|
|
|
},
|
|
|
|
|
|
|
|
createFolderIcon: function(size) {
|
|
|
|
let icon = new St.Widget({ layout_manager: new Clutter.BinLayout(),
|
|
|
|
style_class: 'app-folder-icon',
|
|
|
|
width: size, height: size });
|
|
|
|
let subSize = Math.floor(FOLDER_SUBICON_FRACTION * size);
|
|
|
|
|
|
|
|
let aligns = [ Clutter.ActorAlign.START, Clutter.ActorAlign.END ];
|
|
|
|
for (let i = 0; i < Math.min(this._allItems.length, 4); i++) {
|
|
|
|
let texture = this._allItems[i].create_icon_texture(subSize);
|
|
|
|
let bin = new St.Bin({ child: texture,
|
|
|
|
x_expand: true, y_expand: true });
|
|
|
|
bin.set_x_align(aligns[i % 2]);
|
|
|
|
bin.set_y_align(aligns[Math.floor(i / 2)]);
|
|
|
|
icon.add_actor(bin);
|
|
|
|
}
|
|
|
|
|
|
|
|
return icon;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2013-03-14 14:08:46 -04:00
|
|
|
const AllViewLayout = new Lang.Class({
|
|
|
|
Name: 'AllViewLayout',
|
|
|
|
Extends: Clutter.BinLayout,
|
|
|
|
|
|
|
|
vfunc_get_preferred_height: function(container, forWidth) {
|
|
|
|
let minBottom = 0;
|
|
|
|
let naturalBottom = 0;
|
|
|
|
|
|
|
|
for (let child = container.get_first_child();
|
|
|
|
child;
|
|
|
|
child = child.get_next_sibling()) {
|
|
|
|
let childY = child.y;
|
|
|
|
let [childMin, childNatural] = child.get_preferred_height(forWidth);
|
|
|
|
|
|
|
|
if (childMin + childY > minBottom)
|
|
|
|
minBottom = childMin + childY;
|
|
|
|
|
|
|
|
if (childNatural + childY > naturalBottom)
|
|
|
|
naturalBottom = childNatural + childY;
|
|
|
|
}
|
|
|
|
return [minBottom, naturalBottom];
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2013-02-19 17:23:41 -05:00
|
|
|
const AllView = new Lang.Class({
|
|
|
|
Name: 'AllView',
|
|
|
|
Extends: AlphabeticalView,
|
|
|
|
|
|
|
|
_init: function() {
|
|
|
|
this.parent();
|
2010-12-18 14:18:10 -05:00
|
|
|
|
|
|
|
let box = new St.BoxLayout({ vertical: true });
|
2013-03-14 14:08:46 -04:00
|
|
|
this._stack = new St.Widget({ layout_manager: new AllViewLayout() });
|
2013-02-18 14:19:18 -05:00
|
|
|
this._stack.add_actor(this._grid.actor);
|
2013-02-18 15:04:51 -05:00
|
|
|
this._eventBlocker = new St.Widget({ x_expand: true, y_expand: true });
|
|
|
|
this._stack.add_actor(this._eventBlocker);
|
2013-02-18 14:19:18 -05:00
|
|
|
box.add(this._stack, { y_align: St.Align.START, expand: true });
|
2010-12-18 14:18:10 -05:00
|
|
|
|
|
|
|
this.actor = new St.ScrollView({ x_fill: true,
|
|
|
|
y_fill: false,
|
|
|
|
y_align: St.Align.START,
|
2013-02-15 10:47:39 -05:00
|
|
|
x_expand: true,
|
2013-02-21 18:02:49 -05:00
|
|
|
y_expand: true,
|
2013-02-21 16:49:42 -05:00
|
|
|
overlay_scrollbars: true,
|
|
|
|
style_class: 'all-apps vfade' });
|
2010-12-18 14:18:10 -05:00
|
|
|
this.actor.add_actor(box);
|
|
|
|
this.actor.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
|
2012-11-25 23:33:44 -05:00
|
|
|
let action = new Clutter.PanAction({ interpolate: true });
|
|
|
|
action.connect('pan', Lang.bind(this, this._onPan));
|
|
|
|
this.actor.add_action(action);
|
2013-02-18 15:04:51 -05:00
|
|
|
|
|
|
|
this._clickAction = new Clutter.ClickAction();
|
|
|
|
this._clickAction.connect('clicked', Lang.bind(this, function() {
|
|
|
|
if (!this._currentPopup)
|
|
|
|
return;
|
|
|
|
|
|
|
|
let [x, y] = this._clickAction.get_coords();
|
|
|
|
let actor = global.stage.get_actor_at_pos(Clutter.PickMode.ALL, x, y);
|
|
|
|
if (!this._currentPopup.actor.contains(actor))
|
|
|
|
this._currentPopup.popdown();
|
|
|
|
}));
|
|
|
|
this._eventBlocker.add_action(this._clickAction);
|
2012-11-25 23:33:44 -05:00
|
|
|
},
|
2011-01-21 17:01:07 -05:00
|
|
|
|
2012-11-25 23:33:44 -05:00
|
|
|
_onPan: function(action) {
|
2013-02-18 15:04:51 -05:00
|
|
|
this._clickAction.release();
|
|
|
|
|
2012-11-25 23:33:44 -05:00
|
|
|
let [dist, dx, dy] = action.get_motion_delta(0);
|
|
|
|
let adjustment = this.actor.vscroll.adjustment;
|
|
|
|
adjustment.value -= (dy / this.actor.height) * adjustment.page_size;
|
|
|
|
return false;
|
2008-12-01 14:51:43 -05:00
|
|
|
},
|
2008-12-19 23:27:57 -05:00
|
|
|
|
2013-02-19 17:23:41 -05:00
|
|
|
_getItemId: function(item) {
|
2013-01-31 09:32:02 -05:00
|
|
|
if (item instanceof Shell.App)
|
|
|
|
return item.get_id();
|
|
|
|
else if (item instanceof GMenu.TreeDirectory)
|
|
|
|
return item.get_menu_id();
|
|
|
|
else
|
|
|
|
return null;
|
2009-07-02 00:35:26 -04:00
|
|
|
},
|
|
|
|
|
2013-02-19 17:23:41 -05:00
|
|
|
_createItemIcon: function(item) {
|
2013-01-31 09:32:02 -05:00
|
|
|
if (item instanceof Shell.App)
|
|
|
|
return new AppIcon(item);
|
|
|
|
else if (item instanceof GMenu.TreeDirectory)
|
|
|
|
return new FolderIcon(item, this);
|
|
|
|
else
|
|
|
|
return null;
|
2013-02-19 17:23:41 -05:00
|
|
|
},
|
2009-03-20 12:06:34 -04:00
|
|
|
|
2013-01-31 09:32:02 -05:00
|
|
|
_compareItems: function(itemA, itemB) {
|
|
|
|
// bit of a hack: rely on both ShellApp and GMenuTreeDirectory
|
|
|
|
// having a get_name() method
|
|
|
|
let nameA = GLib.utf8_collate_key(itemA.get_name(), -1);
|
|
|
|
let nameB = GLib.utf8_collate_key(itemB.get_name(), -1);
|
|
|
|
return (nameA > nameB) ? 1 : (nameA < nameB ? -1 : 0);
|
2013-02-19 17:23:41 -05:00
|
|
|
},
|
2009-03-20 12:06:34 -04:00
|
|
|
|
2013-02-19 17:23:41 -05:00
|
|
|
addApp: function(app) {
|
|
|
|
let appIcon = this._addItem(app);
|
|
|
|
if (appIcon)
|
|
|
|
appIcon.actor.connect('key-focus-in',
|
|
|
|
Lang.bind(this, this._ensureIconVisible));
|
2009-06-29 15:08:48 -04:00
|
|
|
},
|
|
|
|
|
2013-01-31 09:32:02 -05:00
|
|
|
addFolder: function(dir) {
|
|
|
|
let folderIcon = this._addItem(dir);
|
|
|
|
if (folderIcon)
|
|
|
|
folderIcon.actor.connect('key-focus-in',
|
|
|
|
Lang.bind(this, this._ensureIconVisible));
|
|
|
|
},
|
|
|
|
|
2013-02-18 14:19:18 -05:00
|
|
|
addFolderPopup: function(popup) {
|
|
|
|
this._stack.add_actor(popup.actor);
|
2013-01-31 11:13:37 -05:00
|
|
|
popup.connect('open-state-changed', Lang.bind(this,
|
|
|
|
function(popup, isOpen) {
|
2013-02-18 15:04:51 -05:00
|
|
|
this._eventBlocker.reactive = isOpen;
|
|
|
|
this._currentPopup = isOpen ? popup : null;
|
|
|
|
this._updateIconOpacities(isOpen);
|
2013-01-31 11:13:37 -05:00
|
|
|
if (isOpen)
|
|
|
|
this._ensureIconVisible(popup.actor);
|
|
|
|
}));
|
2013-02-18 14:19:18 -05:00
|
|
|
},
|
|
|
|
|
2011-02-23 14:21:47 -05:00
|
|
|
_ensureIconVisible: function(icon) {
|
2013-03-11 13:43:38 -04:00
|
|
|
Util.ensureActorVisibleInScrollView(this.actor, icon);
|
2013-02-18 15:04:51 -05:00
|
|
|
},
|
|
|
|
|
|
|
|
_updateIconOpacities: function(folderOpen) {
|
|
|
|
for (let id in this._items) {
|
|
|
|
if (folderOpen && !this._items[id].actor.checked)
|
|
|
|
this._items[id].actor.opacity = INACTIVE_GRID_OPACITY;
|
|
|
|
else
|
|
|
|
this._items[id].actor.opacity = 255;
|
|
|
|
}
|
2009-03-20 12:06:34 -04:00
|
|
|
}
|
2011-11-20 12:56:27 -05:00
|
|
|
});
|
2008-11-20 19:53:11 -05:00
|
|
|
|
2013-02-18 14:18:08 -05:00
|
|
|
const FrequentView = new Lang.Class({
|
|
|
|
Name: 'FrequentView',
|
|
|
|
|
|
|
|
_init: function() {
|
|
|
|
this._grid = new IconGrid.IconGrid({ xAlign: St.Align.MIDDLE,
|
2013-02-21 22:29:57 -05:00
|
|
|
fillParent: true,
|
2013-02-18 14:18:08 -05:00
|
|
|
columnLimit: MAX_COLUMNS });
|
2013-02-21 22:29:57 -05:00
|
|
|
this.actor = new St.Widget({ style_class: 'frequent-apps',
|
|
|
|
x_expand: true, y_expand: true });
|
|
|
|
this.actor.add_actor(this._grid.actor);
|
2013-02-18 14:18:08 -05:00
|
|
|
|
|
|
|
this._usage = Shell.AppUsage.get_default();
|
|
|
|
},
|
|
|
|
|
|
|
|
removeAll: function() {
|
|
|
|
this._grid.removeAll();
|
|
|
|
},
|
|
|
|
|
|
|
|
loadApps: function() {
|
|
|
|
let mostUsed = this._usage.get_most_used ("");
|
|
|
|
for (let i = 0; i < mostUsed.length; i++) {
|
|
|
|
let appIcon = new AppIcon(mostUsed[i]);
|
|
|
|
this._grid.addItem(appIcon.actor, -1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
const Views = {
|
|
|
|
FREQUENT: 0,
|
|
|
|
ALL: 1
|
|
|
|
};
|
|
|
|
|
2013-01-30 17:58:42 -05:00
|
|
|
const AppDisplay = new Lang.Class({
|
|
|
|
Name: 'AppDisplay',
|
2010-06-04 18:01:32 -04:00
|
|
|
|
|
|
|
_init: function() {
|
|
|
|
this._appSystem = Shell.AppSystem.get_default();
|
2013-01-30 17:58:42 -05:00
|
|
|
this._appSystem.connect('installed-changed', Lang.bind(this, function() {
|
2013-02-18 14:18:08 -05:00
|
|
|
Main.queueDeferredWork(this._allAppsWorkId);
|
|
|
|
}));
|
|
|
|
Main.overview.connect('showing', Lang.bind(this, function() {
|
|
|
|
Main.queueDeferredWork(this._frequentAppsWorkId);
|
2013-01-30 17:58:42 -05:00
|
|
|
}));
|
2013-01-31 11:14:13 -05:00
|
|
|
global.settings.connect('changed::app-folder-categories', Lang.bind(this, function() {
|
2013-02-20 10:16:12 -05:00
|
|
|
Main.queueDeferredWork(this._allAppsWorkId);
|
2013-01-31 11:14:13 -05:00
|
|
|
}));
|
2013-01-30 17:58:42 -05:00
|
|
|
|
2013-02-18 14:18:08 -05:00
|
|
|
this._views = [];
|
|
|
|
|
|
|
|
let view, button;
|
|
|
|
view = new FrequentView();
|
|
|
|
button = new St.Button({ label: _("Frequent"),
|
|
|
|
style_class: 'app-view-control',
|
|
|
|
can_focus: true,
|
|
|
|
x_expand: true });
|
|
|
|
this._views[Views.FREQUENT] = { 'view': view, 'control': button };
|
|
|
|
|
|
|
|
view = new AllView();
|
|
|
|
button = new St.Button({ label: _("All"),
|
|
|
|
style_class: 'app-view-control',
|
|
|
|
can_focus: true,
|
|
|
|
x_expand: true });
|
|
|
|
this._views[Views.ALL] = { 'view': view, 'control': button };
|
|
|
|
|
|
|
|
this.actor = new St.BoxLayout({ style_class: 'app-display',
|
|
|
|
vertical: true,
|
|
|
|
x_expand: true, y_expand: true });
|
|
|
|
|
|
|
|
this._viewStack = new St.Widget({ layout_manager: new Clutter.BinLayout(),
|
|
|
|
x_expand: true, y_expand: true });
|
2013-02-21 18:02:49 -05:00
|
|
|
this.actor.add(this._viewStack, { expand: true });
|
2013-02-18 14:18:08 -05:00
|
|
|
|
|
|
|
let layout = new Clutter.BoxLayout({ homogeneous: true });
|
|
|
|
this._controls = new St.Widget({ style_class: 'app-view-controls',
|
|
|
|
layout_manager: layout });
|
|
|
|
this.actor.add(new St.Bin({ child: this._controls }));
|
|
|
|
|
|
|
|
|
|
|
|
for (let i = 0; i < this._views.length; i++) {
|
|
|
|
this._viewStack.add_actor(this._views[i].view.actor);
|
|
|
|
this._controls.add_actor(this._views[i].control);
|
|
|
|
|
|
|
|
let viewIndex = i;
|
|
|
|
this._views[i].control.connect('clicked', Lang.bind(this,
|
|
|
|
function(actor) {
|
|
|
|
this._showView(viewIndex);
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
this._showView(Views.FREQUENT);
|
2011-02-09 17:39:28 -05:00
|
|
|
|
2011-02-23 14:21:47 -05:00
|
|
|
// We need a dummy actor to catch the keyboard focus if the
|
|
|
|
// user Ctrl-Alt-Tabs here before the deferred work creates
|
|
|
|
// our real contents
|
|
|
|
this._focusDummy = new St.Bin({ can_focus: true });
|
2013-02-18 14:18:08 -05:00
|
|
|
this._viewStack.add_actor(this._focusDummy);
|
2013-01-30 17:58:42 -05:00
|
|
|
|
2013-02-18 14:18:08 -05:00
|
|
|
this._allAppsWorkId = Main.initializeDeferredWork(this.actor, Lang.bind(this, this._redisplayAllApps));
|
|
|
|
this._frequentAppsWorkId = Main.initializeDeferredWork(this.actor, Lang.bind(this, this._redisplayFrequentApps));
|
2010-06-04 18:01:32 -04:00
|
|
|
},
|
|
|
|
|
2013-02-18 14:18:08 -05:00
|
|
|
_showView: function(activeIndex) {
|
|
|
|
for (let i = 0; i < this._views.length; i++) {
|
|
|
|
let actor = this._views[i].view.actor;
|
|
|
|
let params = { time: OverviewControls.SIDE_CONTROLS_ANIMATION_TIME,
|
|
|
|
opacity: (i == activeIndex) ? 255 : 0 };
|
|
|
|
if (i == activeIndex)
|
|
|
|
actor.visible = true;
|
|
|
|
else
|
|
|
|
params.onComplete = function() { actor.hide(); };
|
|
|
|
Tweener.addTween(actor, params);
|
|
|
|
|
|
|
|
if (i == activeIndex)
|
|
|
|
this._views[i].control.add_style_pseudo_class('checked');
|
|
|
|
else
|
|
|
|
this._views[i].control.remove_style_pseudo_class('checked');
|
|
|
|
}
|
2010-06-04 18:01:32 -04:00
|
|
|
},
|
|
|
|
|
2013-01-30 17:58:42 -05:00
|
|
|
_redisplay: function() {
|
2013-02-18 14:18:08 -05:00
|
|
|
this._redisplayFrequentApps();
|
|
|
|
this._redisplayAllApps();
|
|
|
|
},
|
|
|
|
|
|
|
|
_redisplayFrequentApps: function() {
|
|
|
|
let view = this._views[Views.FREQUENT].view;
|
|
|
|
|
|
|
|
view.removeAll();
|
|
|
|
view.loadApps();
|
|
|
|
},
|
|
|
|
|
|
|
|
_redisplayAllApps: function() {
|
|
|
|
let view = this._views[Views.ALL].view;
|
|
|
|
|
|
|
|
view.removeAll();
|
2010-12-18 14:18:10 -05:00
|
|
|
|
2013-02-18 14:18:08 -05:00
|
|
|
let tree = this._appSystem.get_tree();
|
|
|
|
let root = tree.get_root_directory();
|
Kill off ShellAppInfo, move into ShellApp
This dramatically thins down and sanitizes the application code.
The ShellAppSystem changes in a number of ways:
* Preferences are special cased more explicitly; they aren't apps,
they're shortcuts for an app), and we don't have many of them, so
don't need e.g. the optimizations in ShellAppSystem for searching.
* get_app() changes to lookup_app() and returns null if an app isn't
found. The semantics where it tried to find the .desktop file
if we didn't know about it were just broken; I am pretty sure no
caller needs this, and if they do we'll fix them.
* ShellAppSystem maintains two indexes on apps (by desktop file id
and by GMenuTreeEntry), but is no longer in the business of
dealing with GMenuTree as far as hierarchy and categories go. That
is moved up into js/ui/appDisplay.js. Actually, it flattens both
apps and settings.
Also, ShellWindowTracker is now the sole reference-owner for
window-backed apps. We still do the weird "window:0x1234beef" id
for these apps, but a reference is not stored in ShellAppSystem.
The js/ui/appDisplay.js code is rewritten, and sucks a lot less.
Variable names are clearer:
_apps -> _appIcons
_filterApp -> _visibleApps
_filters -> _categoryBox
Similarly for function names. We no longer call (for every app) a
recursive lookup in GMenuTree to see if it's in a particular section
on every category switch; it's all cached.
NOTE - this intentionally reverts the incremental loading code from
commit 7813c5b93f6bcde8c4beae286e82bfc472b2b656. It's fast enough
here without that.
https://bugzilla.gnome.org/show_bug.cgi?id=648149
2011-04-21 13:35:01 -04:00
|
|
|
|
2013-02-18 14:18:08 -05:00
|
|
|
let iter = root.iter();
|
|
|
|
let nextType;
|
2013-01-31 11:14:13 -05:00
|
|
|
let folderCategories = global.settings.get_strv('app-folder-categories');
|
Kill off ShellAppInfo, move into ShellApp
This dramatically thins down and sanitizes the application code.
The ShellAppSystem changes in a number of ways:
* Preferences are special cased more explicitly; they aren't apps,
they're shortcuts for an app), and we don't have many of them, so
don't need e.g. the optimizations in ShellAppSystem for searching.
* get_app() changes to lookup_app() and returns null if an app isn't
found. The semantics where it tried to find the .desktop file
if we didn't know about it were just broken; I am pretty sure no
caller needs this, and if they do we'll fix them.
* ShellAppSystem maintains two indexes on apps (by desktop file id
and by GMenuTreeEntry), but is no longer in the business of
dealing with GMenuTree as far as hierarchy and categories go. That
is moved up into js/ui/appDisplay.js. Actually, it flattens both
apps and settings.
Also, ShellWindowTracker is now the sole reference-owner for
window-backed apps. We still do the weird "window:0x1234beef" id
for these apps, but a reference is not stored in ShellAppSystem.
The js/ui/appDisplay.js code is rewritten, and sucks a lot less.
Variable names are clearer:
_apps -> _appIcons
_filterApp -> _visibleApps
_filters -> _categoryBox
Similarly for function names. We no longer call (for every app) a
recursive lookup in GMenuTree to see if it's in a particular section
on every category switch; it's all cached.
NOTE - this intentionally reverts the incremental loading code from
commit 7813c5b93f6bcde8c4beae286e82bfc472b2b656. It's fast enough
here without that.
https://bugzilla.gnome.org/show_bug.cgi?id=648149
2011-04-21 13:35:01 -04:00
|
|
|
while ((nextType = iter.next()) != GMenu.TreeItemType.INVALID) {
|
|
|
|
if (nextType == GMenu.TreeItemType.DIRECTORY) {
|
2013-02-18 14:18:08 -05:00
|
|
|
let dir = iter.get_directory();
|
2011-09-04 19:15:13 -04:00
|
|
|
if (dir.get_is_nodisplay())
|
|
|
|
continue;
|
2012-11-29 19:21:41 -05:00
|
|
|
|
2013-01-31 11:14:13 -05:00
|
|
|
if (folderCategories.indexOf(dir.get_menu_id()) != -1)
|
2013-02-18 14:18:08 -05:00
|
|
|
view.addFolder(dir);
|
2013-01-31 11:14:13 -05:00
|
|
|
else
|
2013-02-18 14:18:08 -05:00
|
|
|
_loadCategory(dir, view);
|
Kill off ShellAppInfo, move into ShellApp
This dramatically thins down and sanitizes the application code.
The ShellAppSystem changes in a number of ways:
* Preferences are special cased more explicitly; they aren't apps,
they're shortcuts for an app), and we don't have many of them, so
don't need e.g. the optimizations in ShellAppSystem for searching.
* get_app() changes to lookup_app() and returns null if an app isn't
found. The semantics where it tried to find the .desktop file
if we didn't know about it were just broken; I am pretty sure no
caller needs this, and if they do we'll fix them.
* ShellAppSystem maintains two indexes on apps (by desktop file id
and by GMenuTreeEntry), but is no longer in the business of
dealing with GMenuTree as far as hierarchy and categories go. That
is moved up into js/ui/appDisplay.js. Actually, it flattens both
apps and settings.
Also, ShellWindowTracker is now the sole reference-owner for
window-backed apps. We still do the weird "window:0x1234beef" id
for these apps, but a reference is not stored in ShellAppSystem.
The js/ui/appDisplay.js code is rewritten, and sucks a lot less.
Variable names are clearer:
_apps -> _appIcons
_filterApp -> _visibleApps
_filters -> _categoryBox
Similarly for function names. We no longer call (for every app) a
recursive lookup in GMenuTree to see if it's in a particular section
on every category switch; it's all cached.
NOTE - this intentionally reverts the incremental loading code from
commit 7813c5b93f6bcde8c4beae286e82bfc472b2b656. It's fast enough
here without that.
https://bugzilla.gnome.org/show_bug.cgi?id=648149
2011-04-21 13:35:01 -04:00
|
|
|
}
|
|
|
|
}
|
2013-02-18 14:18:08 -05:00
|
|
|
view.loadGrid();
|
2010-12-18 14:18:10 -05:00
|
|
|
|
2011-02-23 14:21:47 -05:00
|
|
|
if (this._focusDummy) {
|
|
|
|
let focused = this._focusDummy.has_key_focus();
|
|
|
|
this._focusDummy.destroy();
|
|
|
|
this._focusDummy = null;
|
|
|
|
if (focused)
|
|
|
|
this.actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
|
|
|
|
}
|
2010-06-04 18:01:32 -04:00
|
|
|
}
|
2011-11-20 12:56:27 -05:00
|
|
|
});
|
2010-06-04 18:01:32 -04:00
|
|
|
|
2011-11-20 11:07:14 -05:00
|
|
|
const AppSearchProvider = new Lang.Class({
|
|
|
|
Name: 'AppSearchProvider',
|
2009-11-29 17:45:30 -05:00
|
|
|
|
Kill off ShellAppInfo, move into ShellApp
This dramatically thins down and sanitizes the application code.
The ShellAppSystem changes in a number of ways:
* Preferences are special cased more explicitly; they aren't apps,
they're shortcuts for an app), and we don't have many of them, so
don't need e.g. the optimizations in ShellAppSystem for searching.
* get_app() changes to lookup_app() and returns null if an app isn't
found. The semantics where it tried to find the .desktop file
if we didn't know about it were just broken; I am pretty sure no
caller needs this, and if they do we'll fix them.
* ShellAppSystem maintains two indexes on apps (by desktop file id
and by GMenuTreeEntry), but is no longer in the business of
dealing with GMenuTree as far as hierarchy and categories go. That
is moved up into js/ui/appDisplay.js. Actually, it flattens both
apps and settings.
Also, ShellWindowTracker is now the sole reference-owner for
window-backed apps. We still do the weird "window:0x1234beef" id
for these apps, but a reference is not stored in ShellAppSystem.
The js/ui/appDisplay.js code is rewritten, and sucks a lot less.
Variable names are clearer:
_apps -> _appIcons
_filterApp -> _visibleApps
_filters -> _categoryBox
Similarly for function names. We no longer call (for every app) a
recursive lookup in GMenuTree to see if it's in a particular section
on every category switch; it's all cached.
NOTE - this intentionally reverts the incremental loading code from
commit 7813c5b93f6bcde8c4beae286e82bfc472b2b656. It's fast enough
here without that.
https://bugzilla.gnome.org/show_bug.cgi?id=648149
2011-04-21 13:35:01 -04:00
|
|
|
_init: function() {
|
2009-11-29 17:45:30 -05:00
|
|
|
this._appSys = Shell.AppSystem.get_default();
|
2012-08-09 17:52:36 -04:00
|
|
|
this.id = 'applications';
|
2009-11-29 17:45:30 -05:00
|
|
|
},
|
|
|
|
|
2012-05-02 15:54:25 -04:00
|
|
|
getResultMetas: function(apps, callback) {
|
2012-02-17 10:39:27 -05:00
|
|
|
let metas = [];
|
|
|
|
for (let i = 0; i < apps.length; i++) {
|
|
|
|
let app = apps[i];
|
|
|
|
metas.push({ 'id': app,
|
|
|
|
'name': app.get_name(),
|
|
|
|
'createIcon': function(size) {
|
|
|
|
return app.create_icon_texture(size);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2012-05-02 15:45:37 -04:00
|
|
|
callback(metas);
|
2009-11-29 17:45:30 -05:00
|
|
|
},
|
|
|
|
|
2012-05-02 15:54:25 -04:00
|
|
|
getInitialResultSet: function(terms) {
|
2012-05-02 15:45:37 -04:00
|
|
|
this.searchSystem.pushResults(this, this._appSys.initial_search(terms));
|
Kill off ShellAppInfo, move into ShellApp
This dramatically thins down and sanitizes the application code.
The ShellAppSystem changes in a number of ways:
* Preferences are special cased more explicitly; they aren't apps,
they're shortcuts for an app), and we don't have many of them, so
don't need e.g. the optimizations in ShellAppSystem for searching.
* get_app() changes to lookup_app() and returns null if an app isn't
found. The semantics where it tried to find the .desktop file
if we didn't know about it were just broken; I am pretty sure no
caller needs this, and if they do we'll fix them.
* ShellAppSystem maintains two indexes on apps (by desktop file id
and by GMenuTreeEntry), but is no longer in the business of
dealing with GMenuTree as far as hierarchy and categories go. That
is moved up into js/ui/appDisplay.js. Actually, it flattens both
apps and settings.
Also, ShellWindowTracker is now the sole reference-owner for
window-backed apps. We still do the weird "window:0x1234beef" id
for these apps, but a reference is not stored in ShellAppSystem.
The js/ui/appDisplay.js code is rewritten, and sucks a lot less.
Variable names are clearer:
_apps -> _appIcons
_filterApp -> _visibleApps
_filters -> _categoryBox
Similarly for function names. We no longer call (for every app) a
recursive lookup in GMenuTree to see if it's in a particular section
on every category switch; it's all cached.
NOTE - this intentionally reverts the incremental loading code from
commit 7813c5b93f6bcde8c4beae286e82bfc472b2b656. It's fast enough
here without that.
https://bugzilla.gnome.org/show_bug.cgi?id=648149
2011-04-21 13:35:01 -04:00
|
|
|
},
|
|
|
|
|
2012-05-02 15:54:25 -04:00
|
|
|
getSubsearchResultSet: function(previousResults, terms) {
|
2012-05-02 15:45:37 -04:00
|
|
|
this.searchSystem.pushResults(this, this._appSys.subsearch(previousResults, terms));
|
Kill off ShellAppInfo, move into ShellApp
This dramatically thins down and sanitizes the application code.
The ShellAppSystem changes in a number of ways:
* Preferences are special cased more explicitly; they aren't apps,
they're shortcuts for an app), and we don't have many of them, so
don't need e.g. the optimizations in ShellAppSystem for searching.
* get_app() changes to lookup_app() and returns null if an app isn't
found. The semantics where it tried to find the .desktop file
if we didn't know about it were just broken; I am pretty sure no
caller needs this, and if they do we'll fix them.
* ShellAppSystem maintains two indexes on apps (by desktop file id
and by GMenuTreeEntry), but is no longer in the business of
dealing with GMenuTree as far as hierarchy and categories go. That
is moved up into js/ui/appDisplay.js. Actually, it flattens both
apps and settings.
Also, ShellWindowTracker is now the sole reference-owner for
window-backed apps. We still do the weird "window:0x1234beef" id
for these apps, but a reference is not stored in ShellAppSystem.
The js/ui/appDisplay.js code is rewritten, and sucks a lot less.
Variable names are clearer:
_apps -> _appIcons
_filterApp -> _visibleApps
_filters -> _categoryBox
Similarly for function names. We no longer call (for every app) a
recursive lookup in GMenuTree to see if it's in a particular section
on every category switch; it's all cached.
NOTE - this intentionally reverts the incremental loading code from
commit 7813c5b93f6bcde8c4beae286e82bfc472b2b656. It's fast enough
here without that.
https://bugzilla.gnome.org/show_bug.cgi?id=648149
2011-04-21 13:35:01 -04:00
|
|
|
},
|
|
|
|
|
2012-12-05 10:05:48 -05:00
|
|
|
activateResult: function(app) {
|
2011-07-16 15:27:05 -04:00
|
|
|
let event = Clutter.get_current_event();
|
2012-02-28 11:01:48 -05:00
|
|
|
let modifiers = event ? event.get_state() : 0;
|
2011-07-16 15:27:05 -04:00
|
|
|
let openNewWindow = modifiers & Clutter.ModifierType.CONTROL_MASK;
|
|
|
|
|
|
|
|
if (openNewWindow)
|
2012-12-05 10:05:48 -05:00
|
|
|
app.open_new_window(-1);
|
2011-07-16 15:27:05 -04:00
|
|
|
else
|
2012-12-05 10:05:48 -05:00
|
|
|
app.activate();
|
2010-02-15 17:11:09 -05:00
|
|
|
},
|
|
|
|
|
2011-01-30 16:09:58 -05:00
|
|
|
dragActivateResult: function(id, params) {
|
2011-08-11 05:35:23 -04:00
|
|
|
params = Params.parse(params, { workspace: -1,
|
|
|
|
timestamp: 0 });
|
2011-01-30 16:09:58 -05:00
|
|
|
|
Kill off ShellAppInfo, move into ShellApp
This dramatically thins down and sanitizes the application code.
The ShellAppSystem changes in a number of ways:
* Preferences are special cased more explicitly; they aren't apps,
they're shortcuts for an app), and we don't have many of them, so
don't need e.g. the optimizations in ShellAppSystem for searching.
* get_app() changes to lookup_app() and returns null if an app isn't
found. The semantics where it tried to find the .desktop file
if we didn't know about it were just broken; I am pretty sure no
caller needs this, and if they do we'll fix them.
* ShellAppSystem maintains two indexes on apps (by desktop file id
and by GMenuTreeEntry), but is no longer in the business of
dealing with GMenuTree as far as hierarchy and categories go. That
is moved up into js/ui/appDisplay.js. Actually, it flattens both
apps and settings.
Also, ShellWindowTracker is now the sole reference-owner for
window-backed apps. We still do the weird "window:0x1234beef" id
for these apps, but a reference is not stored in ShellAppSystem.
The js/ui/appDisplay.js code is rewritten, and sucks a lot less.
Variable names are clearer:
_apps -> _appIcons
_filterApp -> _visibleApps
_filters -> _categoryBox
Similarly for function names. We no longer call (for every app) a
recursive lookup in GMenuTree to see if it's in a particular section
on every category switch; it's all cached.
NOTE - this intentionally reverts the incremental loading code from
commit 7813c5b93f6bcde8c4beae286e82bfc472b2b656. It's fast enough
here without that.
https://bugzilla.gnome.org/show_bug.cgi?id=648149
2011-04-21 13:35:01 -04:00
|
|
|
let app = this._appSys.lookup_app(id);
|
2011-08-11 05:35:23 -04:00
|
|
|
app.open_new_window(workspace);
|
Kill off ShellAppInfo, move into ShellApp
This dramatically thins down and sanitizes the application code.
The ShellAppSystem changes in a number of ways:
* Preferences are special cased more explicitly; they aren't apps,
they're shortcuts for an app), and we don't have many of them, so
don't need e.g. the optimizations in ShellAppSystem for searching.
* get_app() changes to lookup_app() and returns null if an app isn't
found. The semantics where it tried to find the .desktop file
if we didn't know about it were just broken; I am pretty sure no
caller needs this, and if they do we'll fix them.
* ShellAppSystem maintains two indexes on apps (by desktop file id
and by GMenuTreeEntry), but is no longer in the business of
dealing with GMenuTree as far as hierarchy and categories go. That
is moved up into js/ui/appDisplay.js. Actually, it flattens both
apps and settings.
Also, ShellWindowTracker is now the sole reference-owner for
window-backed apps. We still do the weird "window:0x1234beef" id
for these apps, but a reference is not stored in ShellAppSystem.
The js/ui/appDisplay.js code is rewritten, and sucks a lot less.
Variable names are clearer:
_apps -> _appIcons
_filterApp -> _visibleApps
_filters -> _categoryBox
Similarly for function names. We no longer call (for every app) a
recursive lookup in GMenuTree to see if it's in a particular section
on every category switch; it's all cached.
NOTE - this intentionally reverts the incremental loading code from
commit 7813c5b93f6bcde8c4beae286e82bfc472b2b656. It's fast enough
here without that.
https://bugzilla.gnome.org/show_bug.cgi?id=648149
2011-04-21 13:35:01 -04:00
|
|
|
},
|
|
|
|
|
|
|
|
createResultActor: function (resultMeta, terms) {
|
|
|
|
let app = resultMeta['id'];
|
2013-01-30 17:51:43 -05:00
|
|
|
let icon = new AppIcon(app);
|
Kill off ShellAppInfo, move into ShellApp
This dramatically thins down and sanitizes the application code.
The ShellAppSystem changes in a number of ways:
* Preferences are special cased more explicitly; they aren't apps,
they're shortcuts for an app), and we don't have many of them, so
don't need e.g. the optimizations in ShellAppSystem for searching.
* get_app() changes to lookup_app() and returns null if an app isn't
found. The semantics where it tried to find the .desktop file
if we didn't know about it were just broken; I am pretty sure no
caller needs this, and if they do we'll fix them.
* ShellAppSystem maintains two indexes on apps (by desktop file id
and by GMenuTreeEntry), but is no longer in the business of
dealing with GMenuTree as far as hierarchy and categories go. That
is moved up into js/ui/appDisplay.js. Actually, it flattens both
apps and settings.
Also, ShellWindowTracker is now the sole reference-owner for
window-backed apps. We still do the weird "window:0x1234beef" id
for these apps, but a reference is not stored in ShellAppSystem.
The js/ui/appDisplay.js code is rewritten, and sucks a lot less.
Variable names are clearer:
_apps -> _appIcons
_filterApp -> _visibleApps
_filters -> _categoryBox
Similarly for function names. We no longer call (for every app) a
recursive lookup in GMenuTree to see if it's in a particular section
on every category switch; it's all cached.
NOTE - this intentionally reverts the incremental loading code from
commit 7813c5b93f6bcde8c4beae286e82bfc472b2b656. It's fast enough
here without that.
https://bugzilla.gnome.org/show_bug.cgi?id=648149
2011-04-21 13:35:01 -04:00
|
|
|
return icon.actor;
|
2009-11-29 17:45:30 -05:00
|
|
|
}
|
2011-11-20 11:07:14 -05:00
|
|
|
});
|
2009-11-29 17:45:30 -05:00
|
|
|
|
2013-01-31 11:13:37 -05:00
|
|
|
const FolderIcon = new Lang.Class({
|
|
|
|
Name: 'FolderIcon',
|
|
|
|
|
|
|
|
_init: function(dir, parentView) {
|
|
|
|
this._dir = dir;
|
|
|
|
this._parentView = parentView;
|
|
|
|
|
|
|
|
this.actor = new St.Button({ style_class: 'app-well-app app-folder',
|
|
|
|
button_mask: St.ButtonMask.ONE,
|
|
|
|
toggle_mode: true,
|
|
|
|
can_focus: true,
|
|
|
|
x_fill: true,
|
|
|
|
y_fill: true });
|
|
|
|
this.actor._delegate = this;
|
|
|
|
|
|
|
|
let label = this._dir.get_name();
|
|
|
|
this.icon = new IconGrid.BaseIcon(label,
|
|
|
|
{ createIcon: Lang.bind(this, this._createIcon) });
|
|
|
|
this.actor.set_child(this.icon.actor);
|
|
|
|
this.actor.label_actor = this.icon.label;
|
|
|
|
|
|
|
|
this.view = new FolderView();
|
|
|
|
this.view.actor.reactive = false;
|
|
|
|
_loadCategory(dir, this.view);
|
2013-02-19 15:31:05 -05:00
|
|
|
this.view.loadGrid();
|
2013-01-31 11:13:37 -05:00
|
|
|
|
|
|
|
this.actor.connect('clicked', Lang.bind(this,
|
|
|
|
function() {
|
|
|
|
this._ensurePopup();
|
|
|
|
this._popup.toggle();
|
|
|
|
}));
|
|
|
|
this.actor.connect('notify::mapped', Lang.bind(this,
|
|
|
|
function() {
|
|
|
|
if (!this.actor.mapped && this._popup)
|
|
|
|
this._popup.popdown();
|
|
|
|
}));
|
|
|
|
},
|
|
|
|
|
|
|
|
_createIcon: function(size) {
|
|
|
|
return this.view.createFolderIcon(size);
|
|
|
|
},
|
|
|
|
|
|
|
|
_ensurePopup: function() {
|
|
|
|
if (this._popup)
|
|
|
|
return;
|
|
|
|
|
|
|
|
let spaceTop = this.actor.y;
|
|
|
|
let spaceBottom = this._parentView.actor.height - (this.actor.y + this.actor.height);
|
|
|
|
let side = spaceTop > spaceBottom ? St.Side.BOTTOM : St.Side.TOP;
|
|
|
|
|
|
|
|
this._popup = new AppFolderPopup(this, side);
|
|
|
|
this._parentView.addFolderPopup(this._popup);
|
|
|
|
|
|
|
|
// Position the popup above or below the source icon
|
|
|
|
if (side == St.Side.BOTTOM) {
|
|
|
|
this._popup.actor.show();
|
|
|
|
this._popup.actor.y = this.actor.y - this._popup.actor.height;
|
|
|
|
this._popup.actor.hide();
|
|
|
|
} else {
|
|
|
|
this._popup.actor.y = this.actor.y + this.actor.height;
|
|
|
|
}
|
|
|
|
|
|
|
|
this._popup.connect('open-state-changed', Lang.bind(this,
|
|
|
|
function(popup, isOpen) {
|
|
|
|
if (!isOpen)
|
|
|
|
this.actor.checked = false;
|
|
|
|
}));
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
const AppFolderPopup = new Lang.Class({
|
|
|
|
Name: 'AppFolderPopup',
|
|
|
|
|
|
|
|
_init: function(source, side) {
|
|
|
|
this._source = source;
|
|
|
|
this._view = source.view;
|
|
|
|
this._arrowSide = side;
|
|
|
|
|
|
|
|
this._isOpen = false;
|
|
|
|
|
|
|
|
this.actor = new St.Widget({ layout_manager: new Clutter.BinLayout(),
|
2013-02-20 11:20:46 -05:00
|
|
|
visible: false,
|
|
|
|
// We don't want to expand really, but look
|
|
|
|
// at the layout manager of our parent...
|
|
|
|
//
|
|
|
|
// DOUBLE HACK: if you set one, you automatically
|
|
|
|
// get the effect for the other direction too, so
|
|
|
|
// we need to set the y_align
|
|
|
|
x_expand: true,
|
|
|
|
y_expand: true,
|
|
|
|
x_align: Clutter.ActorAlign.CENTER,
|
|
|
|
y_align: Clutter.ActorAlign.START });
|
2013-01-31 11:13:37 -05:00
|
|
|
this._boxPointer = new BoxPointer.BoxPointer(this._arrowSide,
|
|
|
|
{ style_class: 'app-folder-popup-bin',
|
|
|
|
x_fill: true,
|
|
|
|
y_fill: true,
|
|
|
|
x_align: St.Align.START });
|
|
|
|
|
|
|
|
this._boxPointer.actor.style_class = 'app-folder-popup';
|
|
|
|
this.actor.add_actor(this._boxPointer.actor);
|
|
|
|
this._boxPointer.bin.set_child(this._view.actor);
|
|
|
|
|
|
|
|
let closeButton = Util.makeCloseButton();
|
|
|
|
closeButton.connect('clicked', Lang.bind(this, this.popdown));
|
|
|
|
this.actor.add_actor(closeButton);
|
|
|
|
|
|
|
|
this._boxPointer.actor.bind_property('opacity', closeButton, 'opacity',
|
|
|
|
GObject.BindingFlags.SYNC_CREATE);
|
|
|
|
|
|
|
|
source.actor.connect('destroy', Lang.bind(this,
|
|
|
|
function() {
|
|
|
|
this.actor.destroy();
|
|
|
|
}));
|
|
|
|
},
|
|
|
|
|
|
|
|
toggle: function() {
|
|
|
|
if (this._isOpen)
|
|
|
|
this.popdown();
|
|
|
|
else
|
|
|
|
this.popup();
|
|
|
|
},
|
|
|
|
|
|
|
|
popup: function() {
|
|
|
|
if (this._isOpen)
|
|
|
|
return;
|
|
|
|
|
|
|
|
this.actor.show();
|
2013-02-20 08:13:46 -05:00
|
|
|
|
|
|
|
this._boxPointer.setArrowActor(this._source.actor);
|
2013-01-31 11:13:37 -05:00
|
|
|
this._boxPointer.show(BoxPointer.PopupAnimation.FADE |
|
|
|
|
BoxPointer.PopupAnimation.SLIDE);
|
|
|
|
|
|
|
|
this._isOpen = true;
|
|
|
|
this.emit('open-state-changed', true);
|
|
|
|
},
|
|
|
|
|
|
|
|
popdown: function() {
|
|
|
|
if (!this._isOpen)
|
|
|
|
return;
|
|
|
|
|
|
|
|
this._boxPointer.hide(BoxPointer.PopupAnimation.FADE |
|
|
|
|
BoxPointer.PopupAnimation.SLIDE);
|
|
|
|
this._isOpen = false;
|
|
|
|
this.emit('open-state-changed', false);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
Signals.addSignalMethods(AppFolderPopup.prototype);
|
2013-01-30 17:51:43 -05:00
|
|
|
|
2011-11-20 11:07:14 -05:00
|
|
|
const AppIcon = new Lang.Class({
|
|
|
|
Name: 'AppIcon',
|
2009-12-08 12:51:05 -05:00
|
|
|
|
2013-02-17 23:08:41 -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;
|
|
|
|
|
2013-01-30 17:51:43 -05:00
|
|
|
if (!iconParams)
|
|
|
|
iconParams = {};
|
|
|
|
|
|
|
|
iconParams['createIcon'] = Lang.bind(this, this._createIcon);
|
|
|
|
this.icon = new IconGrid.BaseIcon(app.get_name(), 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-03-08 13:33:57 -05:00
|
|
|
this.actor.label_actor = this.icon.label;
|
|
|
|
|
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);
|
|
|
|
}));
|
2011-03-09 10:40:48 -05:00
|
|
|
this._draggable.connect('drag-cancelled', Lang.bind(this,
|
|
|
|
function () {
|
|
|
|
Main.overview.cancelledItemDrag(this);
|
|
|
|
}));
|
2010-05-08 10:06:28 -04:00
|
|
|
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();
|
|
|
|
},
|
|
|
|
|
2013-01-30 17:51:43 -05:00
|
|
|
_createIcon: function(iconSize) {
|
|
|
|
return this.app.create_icon_texture(iconSize);
|
|
|
|
},
|
|
|
|
|
2010-03-10 08:52:28 -05:00
|
|
|
_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();
|
2013-02-20 10:55:02 -05:00
|
|
|
this._draggable.fakeRelease();
|
2010-03-10 08:52:28 -05:00
|
|
|
|
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);
|
|
|
|
}));
|
2011-09-15 15:49:13 -04:00
|
|
|
this._menu.connect('open-state-changed', 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
|
|
|
}
|
|
|
|
|
2012-11-03 16:03:33 -04:00
|
|
|
this.emit('menu-state-changed', true);
|
|
|
|
|
2011-03-22 10:43:27 -04:00
|
|
|
this.actor.set_hover(true);
|
2010-05-20 11:18:46 -04:00
|
|
|
this._menu.popup();
|
2012-02-29 19:09:41 -05:00
|
|
|
this._menuManager.ignoreRelease();
|
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();
|
2012-11-03 16:03:33 -04:00
|
|
|
this.emit('menu-state-changed', false);
|
2009-12-08 12:51:05 -05:00
|
|
|
},
|
|
|
|
|
|
|
|
_onActivate: function (event) {
|
2010-02-15 19:50:36 -05:00
|
|
|
this.emit('launching');
|
2012-02-28 11:01:48 -05:00
|
|
|
let modifiers = event.get_state();
|
2009-12-08 12:51:05 -05:00
|
|
|
|
2013-02-17 23:08:41 -05:00
|
|
|
if (modifiers & Clutter.ModifierType.CONTROL_MASK
|
|
|
|
&& this.app.state == Shell.AppState.RUNNING) {
|
|
|
|
this.app.open_new_window(-1);
|
2009-12-08 12:51:05 -05:00
|
|
|
} else {
|
2013-02-17 23:08:41 -05:00
|
|
|
this.app.activate();
|
2009-12-08 12:51:05 -05:00
|
|
|
}
|
2013-02-17 23:08:41 -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) {
|
2011-08-11 05:35:23 -04:00
|
|
|
params = Params.parse(params, { workspace: -1,
|
|
|
|
timestamp: 0 });
|
2011-01-30 16:09:58 -05:00
|
|
|
|
2011-08-11 05:35:23 -04:00
|
|
|
this.app.open_new_window(params.workspace);
|
2009-08-17 20:29:54 -04:00
|
|
|
},
|
|
|
|
|
2009-09-11 17:13:50 -04:00
|
|
|
getDragActor: function() {
|
2011-08-28 09:35:13 -04:00
|
|
|
return this.app.create_icon_texture(Main.overview.dashIconSize);
|
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
|
|
|
}
|
2011-11-20 12:56:27 -05:00
|
|
|
});
|
2013-01-30 17:51:43 -05:00
|
|
|
Signals.addSignalMethods(AppIcon.prototype);
|
2009-11-12 17:46:59 -05:00
|
|
|
|
2011-11-20 08:10:48 -05:00
|
|
|
const AppIconMenu = new Lang.Class({
|
|
|
|
Name: 'AppIconMenu',
|
|
|
|
Extends: PopupMenu.PopupMenu,
|
2010-05-20 11:18:46 -04:00
|
|
|
|
2009-11-12 17:46:59 -05:00
|
|
|
_init: function(source) {
|
2010-11-29 16:07:10 -05:00
|
|
|
let side = St.Side.LEFT;
|
2012-02-13 20:37:28 -05:00
|
|
|
if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)
|
2010-11-29 16:07:10 -05:00
|
|
|
side = St.Side.RIGHT;
|
|
|
|
|
2011-11-20 08:10:48 -05:00
|
|
|
this.parent(source.actor, 0.5, side);
|
2010-05-20 11:18:46 -04:00
|
|
|
|
2011-03-22 10:43:27 -04:00
|
|
|
// We want to keep the item hovered while the menu is up
|
|
|
|
this.blockSourceEvents = true;
|
|
|
|
|
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.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
|
|
|
}
|
|
|
|
|
2011-08-11 06:06:38 -04:00
|
|
|
if (!this._source.app.is_window_backed()) {
|
|
|
|
if (windows.length > 0)
|
|
|
|
this._appendSeparator();
|
2009-11-12 17:46:59 -05:00
|
|
|
|
2011-08-11 06:06:38 -04:00
|
|
|
let isFavorite = AppFavorites.getAppFavorites().isFavorite(this._source.app.get_id());
|
2009-11-12 17:46:59 -05:00
|
|
|
|
2011-08-11 06:06:38 -04:00
|
|
|
this._newWindowMenuItem = this._appendMenuItem(_("New Window"));
|
|
|
|
this._appendSeparator();
|
2009-11-12 17:46:59 -05:00
|
|
|
|
2011-08-11 06:06:38 -04:00
|
|
|
this._toggleFavoriteMenuItem = this._appendMenuItem(isFavorite ? _("Remove from Favorites")
|
|
|
|
: _("Add to Favorites"));
|
|
|
|
}
|
2009-11-12 17:46:59 -05:00
|
|
|
},
|
|
|
|
|
|
|
|
_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
|
|
|
_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
|
|
|
}
|
2011-11-20 08:10:48 -05:00
|
|
|
});
|
2009-11-12 17:46:59 -05:00
|
|
|
Signals.addSignalMethods(AppIconMenu.prototype);
|