~jbicha/ubuntu/oneiric/gnome-shell/oneiric-3.2.2.1

« back to all changes in this revision

Viewing changes to js/ui/appDisplay.js

  • Committer: Package Import Robot
  • Author(s): Jeremy Bicha
  • Date: 2011-09-07 09:09:05 UTC
  • mfrom: (1.1.29 upstream)
  • Revision ID: package-import@ubuntu.com-20110907090905-kbo4fewcg12zt99u
Tags: 3.1.90.1-0ubuntu1
* New upstream release.
* debian/control: Bump build-depends on new mutter
* debian/patches/01_favorite_apps.patch: Updated
* debian/patches/03_remove-glx-dependency-on-armel.patch: Refreshed
* debian/patches/04_build-without-caribou.patch
  - Build without caribou since Ubuntu uses onboard and our System 
    Settings doesn't support choosing a different screen keyboard yet

Show diffs side-by-side

added added

removed removed

Lines of Context:
3
3
const Clutter = imports.gi.Clutter;
4
4
const GLib = imports.gi.GLib;
5
5
const Gtk = imports.gi.Gtk;
 
6
const GMenu = imports.gi.GMenu;
6
7
const Shell = imports.gi.Shell;
7
8
const Lang = imports.lang;
8
9
const Signals = imports.signals;
35
36
        this._appSystem = Shell.AppSystem.get_default();
36
37
 
37
38
        this._pendingAppLaterId = 0;
38
 
        this._apps = [];
39
 
        this._filterApp = null;
 
39
        this._appIcons = {}; // desktop file id
40
40
 
41
41
        let box = new St.BoxLayout({ vertical: true });
42
42
        box.add(this._grid.actor, { y_align: St.Align.START, expand: true });
63
63
 
64
64
    _removeAll: function() {
65
65
        this._grid.removeAll();
66
 
        this._apps = [];
 
66
        this._appIcons = {};
67
67
    },
68
68
 
69
 
    _addApp: function(appInfo) {
70
 
        let appIcon = new AppWellIcon(this._appSystem.get_app(appInfo.get_id()));
 
69
    _addApp: function(app) {
 
70
        var id = app.get_id();
 
71
        let appIcon = new AppWellIcon(app);
71
72
 
72
73
        this._grid.addItem(appIcon.actor);
73
74
        appIcon.actor.connect('key-focus-in', Lang.bind(this, this._ensureIconVisible));
74
75
 
75
 
        appIcon._appInfo = appInfo;
76
 
        if (this._filterApp && !this._filterApp(appInfo))
77
 
            appIcon.actor.hide();
78
 
 
79
 
        this._apps.push(appIcon);
 
76
        this._appIcons[id] = appIcon;
80
77
    },
81
78
 
82
79
    _ensureIconVisible: function(icon) {
105
102
                           transition: 'easeOutQuad' });
106
103
    },
107
104
 
108
 
    setFilter: function(filter) {
109
 
        this._filterApp = filter;
110
 
        for (let i = 0; i < this._apps.length; i++)
111
 
            this._apps[i].actor.visible = filter(this._apps[i]._appInfo);
112
 
    },
113
 
 
114
 
    // Create actors for the applications in an idle to avoid blocking
115
 
    // for too long; see bug 647778
116
 
    _addPendingApps: function() {
117
 
        let i;
118
 
        let startTimeMillis = new Date().getTime();
119
 
        for (i = 0; i < this._pendingAppIds.length; i++) {
120
 
            let id = this._pendingAppIds[i];
121
 
            this._addApp(this._pendingApps[id]);
122
 
 
123
 
            let currentTimeMillis = new Date().getTime();
124
 
            if (currentTimeMillis - startTimeMillis > MAX_APPLICATION_WORK_MILLIS)
125
 
                break;
126
 
        }
127
 
        this._pendingAppIds.splice(0, i + 1);
128
 
        if (this._pendingAppIds.length > 0) {
129
 
            return true;
 
105
    setVisibleApps: function(apps) {
 
106
        if (apps == null) { // null implies "all"
 
107
            for (var id in this._appIcons) {
 
108
                var icon = this._appIcons[id];
 
109
                icon.actor.visible = true;
 
110
            }
130
111
        } else {
131
 
            this._pendingAppLaterId = 0;
132
 
            this._pendingAppIds = null;
133
 
            this._pendingApps = null;
134
 
            return false;
 
112
            // Set everything to not-visible, then set to visible what we should see
 
113
            for (var id in this._appIcons) {
 
114
                var icon = this._appIcons[id];
 
115
                icon.actor.visible = false;
 
116
            }
 
117
            for (var i = 0; i < apps.length; i++) {
 
118
                var app = apps[i];
 
119
                var id = app.get_id();
 
120
                var icon = this._appIcons[id];
 
121
                icon.actor.visible = true;
 
122
            }
135
123
        }
136
124
    },
137
125
 
138
 
    refresh: function(apps) {
139
 
        let ids = [];
140
 
        for (let i in apps)
141
 
            ids.push(i);
142
 
        ids.sort(function(a, b) {
143
 
            return apps[a].get_name().localeCompare(apps[b].get_name());
144
 
        });
145
 
 
 
126
    setAppList: function(apps) {
146
127
        this._removeAll();
147
 
 
148
 
        this._pendingAppIds = ids;
149
 
        this._pendingApps = apps;
150
 
        if (this._pendingAppLaterId)
151
 
            Meta.later_remove(this._pendingAppLaterId);
152
 
        this._pendingAppLaterId = Meta.later_add(Meta.LaterType.BEFORE_REDRAW,
153
 
                                                 Lang.bind(this, this._addPendingApps));
 
128
        for (var i = 0; i < apps.length; i++) {
 
129
            var app = apps[i];
 
130
            this._addApp(app);
 
131
         }
154
132
    }
155
133
};
156
134
 
171
149
        // -2 is a flag to indicate that nothing is selected
172
150
        // (used only before the actor is mapped the first time)
173
151
        this._currentCategory = -2;
174
 
        this._filters = new St.BoxLayout({ vertical: true, reactive: true });
175
 
        this._filtersBox = new St.ScrollView({ x_fill: false,
176
 
                                               y_fill: false,
177
 
                                               style_class: 'vfade' });
178
 
        this._filtersBox.add_actor(this._filters);
 
152
        this._categories = [];
 
153
        this._apps = null;
 
154
 
 
155
        this._categoryBox = new St.BoxLayout({ vertical: true, reactive: true });
 
156
        this._categoryScroll = new St.ScrollView({ x_fill: false,
 
157
                                                   y_fill: false,
 
158
                                                   style_class: 'vfade' });
 
159
        this._categoryScroll.add_actor(this._categoryBox);
179
160
        this.actor.add(this._view.actor, { expand: true, x_fill: true, y_fill: true });
180
 
        this.actor.add(this._filtersBox, { expand: false, y_fill: false, y_align: St.Align.START });
 
161
        this.actor.add(this._categoryScroll, { expand: false, y_fill: false, y_align: St.Align.START });
181
162
 
182
163
        // Always select the "All" filter when switching to the app view
183
164
        this.actor.connect('notify::mapped', Lang.bind(this,
184
165
            function() {
185
 
                if (this.actor.mapped && this._allFilter)
 
166
                if (this.actor.mapped && this._allCategoryButton)
186
167
                    this._selectCategory(-1);
187
168
            }));
188
169
 
189
 
        this._sections = [];
190
 
 
191
170
        // We need a dummy actor to catch the keyboard focus if the
192
171
        // user Ctrl-Alt-Tabs here before the deferred work creates
193
172
        // our real contents
201
180
 
202
181
        this._currentCategory = num;
203
182
 
204
 
        if (num != -1)
205
 
            this._allFilter.remove_style_pseudo_class('selected');
206
 
        else
207
 
            this._allFilter.add_style_pseudo_class('selected');
208
 
 
209
 
        this._view.setFilter(Lang.bind(this, function(app) {
210
 
            if (num == -1)
211
 
                return true;
212
 
            return this._sections[num].name == app.get_section();
213
 
        }));
214
 
 
215
 
        for (let i = 0; i < this._sections.length; i++) {
 
183
        if (num != -1) {
 
184
            var category = this._categories[num];
 
185
            this._allCategoryButton.remove_style_pseudo_class('selected');
 
186
            this._view.setVisibleApps(category.apps);
 
187
        } else {
 
188
            this._allCategoryButton.add_style_pseudo_class('selected');
 
189
            this._view.setVisibleApps(null);
 
190
        }
 
191
 
 
192
        for (var i = 0; i < this._categories.length; i++) {
216
193
            if (i == num)
217
 
                this._sections[i].filterActor.add_style_pseudo_class('selected');
 
194
                this._categories[i].button.add_style_pseudo_class('selected');
218
195
            else
219
 
                this._sections[i].filterActor.remove_style_pseudo_class('selected');
220
 
        }
221
 
    },
222
 
 
223
 
    _addFilter: function(name, num) {
 
196
                this._categories[i].button.remove_style_pseudo_class('selected');
 
197
        }
 
198
    },
 
199
 
 
200
    // Recursively load a GMenuTreeDirectory; we could put this in ShellAppSystem too
 
201
    _loadCategory: function(dir, appList) {
 
202
        var iter = dir.iter();
 
203
        var nextType;
 
204
        while ((nextType = iter.next()) != GMenu.TreeItemType.INVALID) {
 
205
            if (nextType == GMenu.TreeItemType.ENTRY) {
 
206
                var entry = iter.get_entry();
 
207
                var app = this._appSystem.lookup_app_by_tree_entry(entry);
 
208
                if (!entry.get_app_info().get_nodisplay())
 
209
                    appList.push(app);
 
210
            } else if (nextType == GMenu.TreeItemType.DIRECTORY) {
 
211
                this._loadCategory(iter.get_directory(), appList);
 
212
            }
 
213
        }
 
214
    },
 
215
 
 
216
    _addCategory: function(name, index, dir, allApps) {
224
217
        let button = new St.Button({ label: GLib.markup_escape_text (name, -1),
225
218
                                     style_class: 'app-filter',
226
219
                                     x_align: St.Align.START,
227
220
                                     can_focus: true });
228
 
        this._filters.add(button, { expand: true, x_fill: true, y_fill: false });
229
221
        button.connect('clicked', Lang.bind(this, function() {
230
 
            this._selectCategory(num);
 
222
            this._selectCategory(index);
231
223
        }));
232
224
 
233
 
        if (num != -1)
234
 
            this._sections[num] = { filterActor: button,
235
 
                                    name: name };
236
 
        else
237
 
            this._allFilter = button;
 
225
        var apps;
 
226
        if (dir == null) {
 
227
            apps = allApps;
 
228
            this._allCategoryButton = button;
 
229
        } else {
 
230
            apps = [];
 
231
            this._loadCategory(dir, apps);
 
232
            this._categories.push({ apps: apps,
 
233
                                    name: name,
 
234
                                    button: button });
 
235
        }
 
236
 
 
237
        this._categoryBox.add(button, { expand: true, x_fill: true, y_fill: false });
238
238
    },
239
239
 
240
240
    _removeAll: function() {
241
 
        this._sections = [];
242
 
        this._filters.destroy_children();
 
241
        this._categories = [];
 
242
        this._categoryBox.destroy_children();
243
243
    },
244
244
 
245
 
    refresh: function(apps) {
 
245
    refresh: function() {
246
246
        this._removeAll();
247
247
 
248
 
        let sections = this._appSystem.get_sections();
249
 
        this._apps = apps;
 
248
        var allApps = Shell.AppSystem.get_default().get_all();
 
249
        allApps.sort(function(a, b) {
 
250
            return a.compare_by_name(b);
 
251
        });
250
252
 
251
253
        /* Translators: Filter to display all applications */
252
 
        this._addFilter(_("All"), -1);
253
 
 
254
 
        if (!sections)
255
 
            return;
256
 
 
257
 
        for (let i = 0; i < sections.length; i++)
258
 
            this._addFilter(sections[i], i);
259
 
 
 
254
        this._addCategory(_("All"), -1, null, allApps);
 
255
 
 
256
        var tree = this._appSystem.get_tree();
 
257
        var root = tree.get_root_directory();
 
258
 
 
259
        var iter = root.iter();
 
260
        var nextType;
 
261
        var i = 0;
 
262
        while ((nextType = iter.next()) != GMenu.TreeItemType.INVALID) {
 
263
            if (nextType == GMenu.TreeItemType.DIRECTORY) {
 
264
                var dir = iter.get_directory();
 
265
                this._addCategory(dir.get_name(), i, dir);
 
266
                i++;
 
267
            }
 
268
        }
 
269
 
 
270
        this._view.setAppList(allApps);
260
271
        this._selectCategory(-1);
261
 
        this._view.refresh(apps);
262
272
 
263
273
        if (this._focusDummy) {
264
274
            let focused = this._focusDummy.has_key_focus();
291
301
    },
292
302
 
293
303
    _redisplay: function() {
294
 
        let apps = this._appSystem.get_flattened_apps().filter(function(app) {
295
 
            return !app.get_is_nodisplay();
296
 
        });
297
 
 
298
 
        this._appView.refresh(apps);
 
304
        this._appView.refresh();
299
305
    }
300
306
};
301
307
 
302
 
function BaseAppSearchProvider() {
 
308
function AppSearchProvider() {
303
309
    this._init();
304
310
}
305
311
 
306
 
BaseAppSearchProvider.prototype = {
 
312
AppSearchProvider.prototype = {
307
313
    __proto__: Search.SearchProvider.prototype,
308
314
 
309
 
    _init: function(name) {
310
 
        Search.SearchProvider.prototype._init.call(this, name);
 
315
    _init: function() {
 
316
        Search.SearchProvider.prototype._init.call(this, _("APPLICATIONS"));
311
317
        this._appSys = Shell.AppSystem.get_default();
312
318
    },
313
319
 
314
 
    getResultMeta: function(resultId) {
315
 
        let app = this._appSys.get_app(resultId);
316
 
        if (!app)
317
 
            return null;
318
 
        return { 'id': resultId,
 
320
    getResultMeta: function(app) {
 
321
        return { 'id': app,
319
322
                 'name': app.get_name(),
320
323
                 'createIcon': function(size) {
321
324
                                   return app.create_icon_texture(size);
323
326
               };
324
327
    },
325
328
 
326
 
    activateResult: function(id, params) {
327
 
        params = Params.parse(params, { workspace: null,
328
 
                                        timestamp: null });
329
 
 
330
 
        let workspace = params.workspace ? params.workspace.index() : -1;
 
329
    getInitialResultSet: function(terms) {
 
330
        return this._appSys.initial_search(terms);
 
331
    },
 
332
 
 
333
    getSubsearchResultSet: function(previousResults, terms) {
 
334
        return this._appSys.subsearch(previousResults, terms);
 
335
    },
 
336
 
 
337
    activateResult: function(app, params) {
 
338
        params = Params.parse(params, { workspace: -1,
 
339
                                        timestamp: 0 });
 
340
 
331
341
        let event = Clutter.get_current_event();
332
342
        let modifiers = event ? Shell.get_event_state(event) : 0;
333
343
        let openNewWindow = modifiers & Clutter.ModifierType.CONTROL_MASK;
334
344
 
335
 
        let app = this._appSys.get_app(id);
336
345
        if (openNewWindow)
337
 
            app.open_new_window(workspace);
 
346
            app.open_new_window(params.workspace);
338
347
        else
339
 
            app.activate(workspace);
 
348
            app.activate_full(params.workspace, params.timestamp);
340
349
    },
341
350
 
342
351
    dragActivateResult: function(id, params) {
343
 
        params = Params.parse(params, { workspace: null,
344
 
                                        timestamp: null });
345
 
 
346
 
        let app = this._appSys.get_app(id);
347
 
        app.open_new_window(params.workspace ? params.workspace.index() : -1);
348
 
    }
349
 
};
350
 
 
351
 
function AppSearchProvider() {
352
 
    this._init();
353
 
}
354
 
 
355
 
AppSearchProvider.prototype = {
356
 
    __proto__: BaseAppSearchProvider.prototype,
357
 
 
358
 
    _init: function() {
359
 
         BaseAppSearchProvider.prototype._init.call(this, _("APPLICATIONS"));
360
 
    },
361
 
 
362
 
    getInitialResultSet: function(terms) {
363
 
        return this._appSys.initial_search(false, terms);
364
 
    },
365
 
 
366
 
    getSubsearchResultSet: function(previousResults, terms) {
367
 
        return this._appSys.subsearch(false, previousResults, terms);
368
 
    },
369
 
 
370
 
    createResultActor: function (resultMeta, terms) {
371
 
        let app = this._appSys.get_app(resultMeta['id']);
372
 
        let icon = new AppWellIcon(app);
373
 
        return icon.actor;
374
 
    }
375
 
};
376
 
 
377
 
function PrefsSearchProvider() {
378
 
    this._init();
379
 
}
380
 
 
381
 
PrefsSearchProvider.prototype = {
382
 
    __proto__: BaseAppSearchProvider.prototype,
383
 
 
384
 
    _init: function() {
385
 
        BaseAppSearchProvider.prototype._init.call(this, _("SETTINGS"));
386
 
    },
387
 
 
388
 
    getInitialResultSet: function(terms) {
389
 
        return this._appSys.initial_search(true, terms);
390
 
    },
391
 
 
392
 
    getSubsearchResultSet: function(previousResults, terms) {
393
 
        return this._appSys.subsearch(true, previousResults, terms);
 
352
        params = Params.parse(params, { workspace: -1,
 
353
                                        timestamp: 0 });
 
354
 
 
355
        let app = this._appSys.lookup_app(id);
 
356
        app.open_new_window(workspace);
 
357
    },
 
358
 
 
359
    createResultActor: function (resultMeta, terms) {
 
360
        let app = resultMeta['id'];
 
361
        let icon = new AppWellIcon(app);
 
362
        return icon.actor;
 
363
    }
 
364
};
 
365
 
 
366
function SettingsSearchProvider() {
 
367
    this._init();
 
368
}
 
369
 
 
370
SettingsSearchProvider.prototype = {
 
371
    __proto__: Search.SearchProvider.prototype,
 
372
 
 
373
    _init: function() {
 
374
        Search.SearchProvider.prototype._init.call(this, _("SETTINGS"));
 
375
        this._appSys = Shell.AppSystem.get_default();
 
376
        this._gnomecc = this._appSys.lookup_app('gnome-control-center.desktop');
 
377
    },
 
378
 
 
379
    getResultMeta: function(pref) {
 
380
        return { 'id': pref,
 
381
                 'name': pref.get_name(),
 
382
                 'createIcon': function(size) {
 
383
                                   return pref.create_icon_texture(size);
 
384
                               }
 
385
               };
 
386
    },
 
387
 
 
388
    getInitialResultSet: function(terms) {
 
389
        return this._appSys.search_settings(terms);
 
390
    },
 
391
 
 
392
    getSubsearchResultSet: function(previousResults, terms) {
 
393
        return this._appSys.search_settings(terms);
 
394
    },
 
395
 
 
396
    activateResult: function(pref, params) {
 
397
        params = Params.parse(params, { workspace: -1,
 
398
                                        timestamp: 0 });
 
399
 
 
400
        pref.activate_full(params.workspace, params.timestamp);
 
401
    },
 
402
 
 
403
    dragActivateResult: function(pref, params) {
 
404
        this.activateResult(pref, params);
 
405
    },
 
406
 
 
407
    createResultActor: function (resultMeta, terms) {
 
408
        let app = resultMeta['id'];
 
409
        let icon = new AppWellIcon(app);
 
410
        return icon.actor;
394
411
    }
395
412
};
396
413
 
416
433
    }
417
434
};
418
435
 
419
 
function AppWellIcon(app, iconParams) {
420
 
    this._init(app, iconParams);
 
436
function AppWellIcon(app, iconParams, onActivateOverride) {
 
437
    this._init(app, iconParams, onActivateOverride);
421
438
}
422
439
 
423
440
AppWellIcon.prototype = {
424
 
    _init : function(app, iconParams) {
 
441
    _init : function(app, iconParams, onActivateOverride) {
425
442
        this.app = app;
426
443
        this.actor = new St.Button({ style_class: 'app-well-app',
427
444
                                     reactive: true,
436
453
 
437
454
        this.actor.label_actor = this.icon.label;
438
455
 
 
456
        // A function callback to override the default "app.activate()"; used by preferences
 
457
        this._onActivateOverride = onActivateOverride;
439
458
        this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
440
459
        this.actor.connect('clicked', Lang.bind(this, this._onClicked));
441
460
        this.actor.connect('popup-menu', Lang.bind(this, this._onKeyboardPopupMenu));
569
588
        this.emit('launching');
570
589
        let modifiers = Shell.get_event_state(event);
571
590
 
572
 
        if (modifiers & Clutter.ModifierType.CONTROL_MASK
573
 
            && this.app.state == Shell.AppState.RUNNING) {
574
 
            this.app.open_new_window(-1);
 
591
        if (this._onActivateOverride) {
 
592
            this._onActivateOverride(event);
575
593
        } else {
576
 
            this.app.activate(-1);
 
594
            if (modifiers & Clutter.ModifierType.CONTROL_MASK
 
595
                && this.app.state == Shell.AppState.RUNNING) {
 
596
                this.app.open_new_window(-1);
 
597
            } else {
 
598
                this.app.activate();
 
599
            }
577
600
        }
578
601
        Main.overview.hide();
579
602
    },
580
603
 
581
604
    shellWorkspaceLaunch : function(params) {
582
 
        params = Params.parse(params, { workspace: null,
583
 
                                        timestamp: null });
 
605
        params = Params.parse(params, { workspace: -1,
 
606
                                        timestamp: 0 });
584
607
 
585
 
        this.app.open_new_window(params.workspace ? params.workspace.index() : -1);
 
608
        this.app.open_new_window(params.workspace);
586
609
    },
587
610
 
588
611
    getDragActor: function() {
589
 
        return this.app.create_icon_texture(Main.overview.dash.iconSize);
 
612
        return this.app.create_icon_texture(Main.overview.dashIconSize);
590
613
    },
591
614
 
592
615
    // Returns the original actor that should align with the actor
609
632
        if (St.Widget.get_default_direction() == St.TextDirection.RTL)
610
633
            side = St.Side.RIGHT;
611
634
 
612
 
        PopupMenu.PopupMenu.prototype._init.call(this, source.actor, 0.5, side, 0);
 
635
        PopupMenu.PopupMenu.prototype._init.call(this, source.actor, 0.5, side);
613
636
 
614
637
        // We want to keep the item hovered while the menu is up
615
638
        this.blockSourceEvents = true;
650
673
            item._window = windows[i];
651
674
        }
652
675
 
653
 
        if (windows.length > 0)
 
676
        if (!this._source.app.is_window_backed()) {
 
677
            if (windows.length > 0)
 
678
                this._appendSeparator();
 
679
 
 
680
            let isFavorite = AppFavorites.getAppFavorites().isFavorite(this._source.app.get_id());
 
681
 
 
682
            this._newWindowMenuItem = this._appendMenuItem(_("New Window"));
654
683
            this._appendSeparator();
655
684
 
656
 
        let isFavorite = AppFavorites.getAppFavorites().isFavorite(this._source.app.get_id());
657
 
 
658
 
        this._newWindowMenuItem = this._appendMenuItem(_("New Window"));
659
 
        this._appendSeparator();
660
 
 
661
 
        this._toggleFavoriteMenuItem = this._appendMenuItem(isFavorite ? _("Remove from Favorites")
662
 
                                                                    : _("Add to Favorites"));
663
 
 
 
685
            this._toggleFavoriteMenuItem = this._appendMenuItem(isFavorite ? _("Remove from Favorites")
 
686
                                                                : _("Add to Favorites"));
 
687
        }
664
688
    },
665
689
 
666
690
    _appendSeparator: function () {