~ubuntu-branches/debian/sid/gnome-shell/sid

« back to all changes in this revision

Viewing changes to .pc/0003-windowManager-Replace-custom-shader-with-builtin-Clu.patch/js/ui/windowManager.js

  • Committer: Package Import Robot
  • Author(s): Andreas Henriksson
  • Date: 2013-08-22 22:29:45 UTC
  • Revision ID: package-import@ubuntu.com-20130822222945-gpqs7aq66kvzakcv
Tags: 3.4.2-15
* Add patches from upstream to replace custom shader (which is causing
  problems with recent clutter versions) with builtin ClutterEffects:
  - 0001-windowManager-Remove-unused-function.patch
  - 0002-windowManager-Remove-animate-parameter-from-un-dimWi.patch
  - 0003-windowManager-Replace-custom-shader-with-builtin-Clu.patch
  (First two patches are included only to make the third one apply)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 
2
 
 
3
const Clutter = imports.gi.Clutter;
 
4
const GLib = imports.gi.GLib;
 
5
const Gio = imports.gi.Gio;
 
6
const Lang = imports.lang;
 
7
const Meta = imports.gi.Meta;
 
8
const St = imports.gi.St;
 
9
const Shell = imports.gi.Shell;
 
10
 
 
11
const AltTab = imports.ui.altTab;
 
12
const WorkspaceSwitcherPopup = imports.ui.workspaceSwitcherPopup;
 
13
const Main = imports.ui.main;
 
14
const Tweener = imports.ui.tweener;
 
15
 
 
16
const SHELL_KEYBINDINGS_SCHEMA = 'org.gnome.shell.keybindings';
 
17
const WINDOW_ANIMATION_TIME = 0.25;
 
18
const DIM_TIME = 0.500;
 
19
const UNDIM_TIME = 0.250;
 
20
 
 
21
var dimShader = undefined;
 
22
 
 
23
function getDimShaderSource() {
 
24
    if (!dimShader)
 
25
        dimShader = Shell.get_file_contents_utf8_sync(global.datadir + '/shaders/dim-window.glsl');
 
26
    return dimShader;
 
27
}
 
28
 
 
29
const WindowDimmer = new Lang.Class({
 
30
    Name: 'WindowDimmer',
 
31
 
 
32
    _init: function(actor) {
 
33
        if (Clutter.feature_available(Clutter.FeatureFlags.SHADERS_GLSL)) {
 
34
            this._effect = new Clutter.ShaderEffect({ shader_type: Clutter.ShaderType.FRAGMENT_SHADER });
 
35
            this._effect.set_shader_source(getDimShaderSource());
 
36
        } else {
 
37
            this._effect = null;
 
38
        }
 
39
 
 
40
        this.actor = actor;
 
41
    },
 
42
 
 
43
    set dimFraction(fraction) {
 
44
        this._dimFraction = fraction;
 
45
 
 
46
        if (this._effect == null)
 
47
            return;
 
48
 
 
49
        if (!Meta.prefs_get_attach_modal_dialogs()) {
 
50
            this._effect.enabled = false;
 
51
            return;
 
52
        }
 
53
 
 
54
        if (fraction > 0.01) {
 
55
            Shell.shader_effect_set_double_uniform(this._effect, 'height', this.actor.get_height());
 
56
            Shell.shader_effect_set_double_uniform(this._effect, 'fraction', fraction);
 
57
 
 
58
            if (!this._effect.actor)
 
59
                this.actor.add_effect(this._effect);
 
60
        } else {
 
61
            if (this._effect.actor)
 
62
                this.actor.remove_effect(this._effect);
 
63
        }
 
64
    },
 
65
 
 
66
    get dimFraction() {
 
67
        return this._dimFraction;
 
68
    },
 
69
 
 
70
    _dimFraction: 0.0
 
71
});
 
72
 
 
73
function getWindowDimmer(actor) {
 
74
    if (!actor._windowDimmer)
 
75
        actor._windowDimmer = new WindowDimmer(actor);
 
76
 
 
77
    return actor._windowDimmer;
 
78
}
 
79
 
 
80
const WindowManager = new Lang.Class({
 
81
    Name: 'WindowManager',
 
82
 
 
83
    _init : function() {
 
84
        this._shellwm =  global.window_manager;
 
85
 
 
86
        this._minimizing = [];
 
87
        this._maximizing = [];
 
88
        this._unmaximizing = [];
 
89
        this._mapping = [];
 
90
        this._destroying = [];
 
91
 
 
92
        this._dimmedWindows = [];
 
93
 
 
94
        this._animationBlockCount = 0;
 
95
 
 
96
        this._switchData = null;
 
97
        this._shellwm.connect('kill-switch-workspace', Lang.bind(this, this._switchWorkspaceDone));
 
98
        this._shellwm.connect('kill-window-effects', Lang.bind(this, function (shellwm, actor) {
 
99
            this._minimizeWindowDone(shellwm, actor);
 
100
            this._maximizeWindowDone(shellwm, actor);
 
101
            this._unmaximizeWindowDone(shellwm, actor);
 
102
            this._mapWindowDone(shellwm, actor);
 
103
            this._destroyWindowDone(shellwm, actor);
 
104
        }));
 
105
 
 
106
        this._shellwm.connect('switch-workspace', Lang.bind(this, this._switchWorkspace));
 
107
        this._shellwm.connect('minimize', Lang.bind(this, this._minimizeWindow));
 
108
        this._shellwm.connect('maximize', Lang.bind(this, this._maximizeWindow));
 
109
        this._shellwm.connect('unmaximize', Lang.bind(this, this._unmaximizeWindow));
 
110
        this._shellwm.connect('map', Lang.bind(this, this._mapWindow));
 
111
        this._shellwm.connect('destroy', Lang.bind(this, this._destroyWindow));
 
112
 
 
113
        this._workspaceSwitcherPopup = null;
 
114
        Meta.keybindings_set_custom_handler('switch-to-workspace-left',
 
115
                                            Lang.bind(this, this._showWorkspaceSwitcher));
 
116
        Meta.keybindings_set_custom_handler('switch-to-workspace-right',
 
117
                                            Lang.bind(this, this._showWorkspaceSwitcher));
 
118
        Meta.keybindings_set_custom_handler('switch-to-workspace-up',
 
119
                                            Lang.bind(this, this._showWorkspaceSwitcher));
 
120
        Meta.keybindings_set_custom_handler('switch-to-workspace-down',
 
121
                                            Lang.bind(this, this._showWorkspaceSwitcher));
 
122
        Meta.keybindings_set_custom_handler('switch-windows',
 
123
                                            Lang.bind(this, this._startAppSwitcher));
 
124
        Meta.keybindings_set_custom_handler('switch-group',
 
125
                                            Lang.bind(this, this._startAppSwitcher));
 
126
        Meta.keybindings_set_custom_handler('switch-windows-backward',
 
127
                                            Lang.bind(this, this._startAppSwitcher));
 
128
        Meta.keybindings_set_custom_handler('switch-group-backward',
 
129
                                            Lang.bind(this, this._startAppSwitcher));
 
130
        Meta.keybindings_set_custom_handler('switch-panels',
 
131
                                            Lang.bind(this, this._startA11ySwitcher));
 
132
        global.display.add_keybinding('open-application-menu',
 
133
                                      new Gio.Settings({ schema: SHELL_KEYBINDINGS_SCHEMA }),
 
134
                                      Meta.KeyBindingFlags.NONE,
 
135
                                      Lang.bind(this, this._openAppMenu));
 
136
 
 
137
        Main.overview.connect('showing', Lang.bind(this, function() {
 
138
            for (let i = 0; i < this._dimmedWindows.length; i++)
 
139
                this._undimWindow(this._dimmedWindows[i]);
 
140
        }));
 
141
        Main.overview.connect('hiding', Lang.bind(this, function() {
 
142
            for (let i = 0; i < this._dimmedWindows.length; i++)
 
143
                this._dimWindow(this._dimmedWindows[i]);
 
144
        }));
 
145
    },
 
146
 
 
147
    blockAnimations: function() {
 
148
        this._animationBlockCount++;
 
149
    },
 
150
 
 
151
    unblockAnimations: function() {
 
152
        this._animationBlockCount = Math.max(0, this._animationBlockCount - 1);
 
153
    },
 
154
 
 
155
    _shouldAnimate : function(actor) {
 
156
        if (Main.overview.visible || this._animationBlockCount > 0)
 
157
            return false;
 
158
        if (actor && (actor.meta_window.get_window_type() != Meta.WindowType.NORMAL))
 
159
            return false;
 
160
        return true;
 
161
    },
 
162
 
 
163
    _removeEffect : function(list, actor) {
 
164
        let idx = list.indexOf(actor);
 
165
        if (idx != -1) {
 
166
            list.splice(idx, 1);
 
167
            return true;
 
168
        }
 
169
        return false;
 
170
    },
 
171
 
 
172
    _minimizeWindow : function(shellwm, actor) {
 
173
        if (!this._shouldAnimate(actor)) {
 
174
            shellwm.completed_minimize(actor);
 
175
            return;
 
176
        }
 
177
 
 
178
        actor.set_scale(1.0, 1.0);
 
179
        actor.move_anchor_point_from_gravity(Clutter.Gravity.CENTER);
 
180
 
 
181
        /* scale window down to 0x0.
 
182
         * maybe TODO: get icon geometry passed through and move the window towards it?
 
183
         */
 
184
        this._minimizing.push(actor);
 
185
 
 
186
        let primary = Main.layoutManager.primaryMonitor;
 
187
        let xDest = primary.x;
 
188
        if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)
 
189
            xDest += primary.width;
 
190
 
 
191
        Tweener.addTween(actor,
 
192
                         { scale_x: 0.0,
 
193
                           scale_y: 0.0,
 
194
                           x: xDest,
 
195
                           y: 0,
 
196
                           time: WINDOW_ANIMATION_TIME,
 
197
                           transition: 'easeOutQuad',
 
198
                           onComplete: this._minimizeWindowDone,
 
199
                           onCompleteScope: this,
 
200
                           onCompleteParams: [shellwm, actor],
 
201
                           onOverwrite: this._minimizeWindowOverwritten,
 
202
                           onOverwriteScope: this,
 
203
                           onOverwriteParams: [shellwm, actor]
 
204
                         });
 
205
    },
 
206
 
 
207
    _minimizeWindowDone : function(shellwm, actor) {
 
208
        if (this._removeEffect(this._minimizing, actor)) {
 
209
            Tweener.removeTweens(actor);
 
210
            actor.set_scale(1.0, 1.0);
 
211
            actor.move_anchor_point_from_gravity(Clutter.Gravity.NORTH_WEST);
 
212
 
 
213
            shellwm.completed_minimize(actor);
 
214
        }
 
215
    },
 
216
 
 
217
    _minimizeWindowOverwritten : function(shellwm, actor) {
 
218
        if (this._removeEffect(this._minimizing, actor)) {
 
219
            shellwm.completed_minimize(actor);
 
220
        }
 
221
    },
 
222
 
 
223
    _maximizeWindow : function(shellwm, actor, targetX, targetY, targetWidth, targetHeight) {
 
224
        shellwm.completed_maximize(actor);
 
225
    },
 
226
 
 
227
    _maximizeWindowDone : function(shellwm, actor) {
 
228
    },
 
229
 
 
230
    _maximizeWindowOverwrite : function(shellwm, actor) {
 
231
    },
 
232
 
 
233
    _unmaximizeWindow : function(shellwm, actor, targetX, targetY, targetWidth, targetHeight) {
 
234
        shellwm.completed_unmaximize(actor);
 
235
    },
 
236
 
 
237
    _unmaximizeWindowDone : function(shellwm, actor) {
 
238
    },
 
239
 
 
240
    _hasAttachedDialogs: function(window, ignoreWindow) {
 
241
        var count = 0;
 
242
        window.foreach_transient(function(win) {
 
243
            if (win != ignoreWindow && win.is_attached_dialog())
 
244
                count++;
 
245
            return false;
 
246
        });
 
247
        return count != 0;
 
248
    },
 
249
 
 
250
    _checkDimming: function(window, ignoreWindow) {
 
251
        let shouldDim = this._hasAttachedDialogs(window, ignoreWindow);
 
252
 
 
253
        if (shouldDim && !window._dimmed) {
 
254
            window._dimmed = true;
 
255
            this._dimmedWindows.push(window);
 
256
            if (!Main.overview.visible)
 
257
                this._dimWindow(window);
 
258
        } else if (!shouldDim && window._dimmed) {
 
259
            window._dimmed = false;
 
260
            this._dimmedWindows = this._dimmedWindows.filter(function(win) {
 
261
                                                                 return win != window;
 
262
                                                             });
 
263
            if (!Main.overview.visible)
 
264
                this._undimWindow(window);
 
265
        }
 
266
    },
 
267
 
 
268
    _dimWindow: function(window) {
 
269
        let actor = window.get_compositor_private();
 
270
        if (!actor)
 
271
            return;
 
272
        Tweener.addTween(getWindowDimmer(actor),
 
273
                         { dimFraction: 1.0,
 
274
                           time: DIM_TIME,
 
275
                           transition: 'linear'
 
276
                         });
 
277
    },
 
278
 
 
279
    _undimWindow: function(window) {
 
280
        let actor = window.get_compositor_private();
 
281
        if (!actor)
 
282
            return;
 
283
        Tweener.addTween(getWindowDimmer(actor),
 
284
                         { dimFraction: 0.0,
 
285
                           time: UNDIM_TIME,
 
286
                           transition: 'linear'
 
287
                         });
 
288
    },
 
289
 
 
290
    _mapWindow : function(shellwm, actor) {
 
291
        actor._windowType = actor.meta_window.get_window_type();
 
292
        actor._notifyWindowTypeSignalId = actor.meta_window.connect('notify::window-type', Lang.bind(this, function () {
 
293
            let type = actor.meta_window.get_window_type();
 
294
            if (type == actor._windowType)
 
295
                return;
 
296
            if (type == Meta.WindowType.MODAL_DIALOG ||
 
297
                actor._windowType == Meta.WindowType.MODAL_DIALOG) {
 
298
                let parent = actor.get_meta_window().get_transient_for();
 
299
                if (parent)
 
300
                    this._checkDimming(parent);
 
301
            }
 
302
 
 
303
            actor._windowType = type;
 
304
        }));
 
305
        if (actor.meta_window.is_attached_dialog()) {
 
306
            this._checkDimming(actor.get_meta_window().get_transient_for());
 
307
            if (this._shouldAnimate()) {
 
308
                actor.set_scale(1.0, 0.0);
 
309
                actor.show();
 
310
                this._mapping.push(actor);
 
311
 
 
312
                Tweener.addTween(actor,
 
313
                                 { scale_y: 1,
 
314
                                   time: WINDOW_ANIMATION_TIME,
 
315
                                   transition: "easeOutQuad",
 
316
                                   onComplete: this._mapWindowDone,
 
317
                                   onCompleteScope: this,
 
318
                                   onCompleteParams: [shellwm, actor],
 
319
                                   onOverwrite: this._mapWindowOverwrite,
 
320
                                   onOverwriteScope: this,
 
321
                                   onOverwriteParams: [shellwm, actor]
 
322
                                 });
 
323
                return;
 
324
            }
 
325
            shellwm.completed_map(actor);
 
326
            return;
 
327
        }
 
328
        if (!this._shouldAnimate(actor)) {
 
329
            shellwm.completed_map(actor);
 
330
            return;
 
331
        }
 
332
 
 
333
        actor.opacity = 0;
 
334
        actor.show();
 
335
 
 
336
        /* Fade window in */
 
337
        this._mapping.push(actor);
 
338
        Tweener.addTween(actor,
 
339
                         { opacity: 255,
 
340
                           time: WINDOW_ANIMATION_TIME,
 
341
                           transition: 'easeOutQuad',
 
342
                           onComplete: this._mapWindowDone,
 
343
                           onCompleteScope: this,
 
344
                           onCompleteParams: [shellwm, actor],
 
345
                           onOverwrite: this._mapWindowOverwrite,
 
346
                           onOverwriteScope: this,
 
347
                           onOverwriteParams: [shellwm, actor]
 
348
                         });
 
349
    },
 
350
 
 
351
    _mapWindowDone : function(shellwm, actor) {
 
352
        if (this._removeEffect(this._mapping, actor)) {
 
353
            Tweener.removeTweens(actor);
 
354
            actor.opacity = 255;
 
355
            shellwm.completed_map(actor);
 
356
        }
 
357
    },
 
358
 
 
359
    _mapWindowOverwrite : function(shellwm, actor) {
 
360
        if (this._removeEffect(this._mapping, actor)) {
 
361
            shellwm.completed_map(actor);
 
362
        }
 
363
    },
 
364
 
 
365
    _destroyWindow : function(shellwm, actor) {
 
366
        let window = actor.meta_window;
 
367
        if (actor._notifyWindowTypeSignalId) {
 
368
            window.disconnect(actor._notifyWindowTypeSignalId);
 
369
            actor._notifyWindowTypeSignalId = 0;
 
370
        }
 
371
        if (window._dimmed) {
 
372
            this._dimmedWindows = this._dimmedWindows.filter(function(win) {
 
373
                                                                 return win != window;
 
374
                                                             });
 
375
        }
 
376
        if (window.is_attached_dialog()) {
 
377
            let parent = window.get_transient_for();
 
378
            this._checkDimming(parent, window);
 
379
            if (!this._shouldAnimate()) {
 
380
                shellwm.completed_destroy(actor);
 
381
                return;
 
382
            }
 
383
 
 
384
            actor.set_scale(1.0, 1.0);
 
385
            actor.show();
 
386
            this._destroying.push(actor);
 
387
 
 
388
            actor._parentDestroyId = parent.connect('unmanaged', Lang.bind(this, function () {
 
389
                Tweener.removeTweens(actor);
 
390
                this._destroyWindowDone(shellwm, actor);
 
391
            }));
 
392
 
 
393
            Tweener.addTween(actor,
 
394
                             { scale_y: 0,
 
395
                               time: WINDOW_ANIMATION_TIME,
 
396
                               transition: "easeOutQuad",
 
397
                               onComplete: this._destroyWindowDone,
 
398
                               onCompleteScope: this,
 
399
                               onCompleteParams: [shellwm, actor],
 
400
                               onOverwrite: this._destroyWindowDone,
 
401
                               onOverwriteScope: this,
 
402
                               onOverwriteParams: [shellwm, actor]
 
403
                             });
 
404
            return;
 
405
        }
 
406
        shellwm.completed_destroy(actor);
 
407
    },
 
408
 
 
409
    _destroyWindowDone : function(shellwm, actor) {
 
410
        if (this._removeEffect(this._destroying, actor)) {
 
411
            let parent = actor.get_meta_window().get_transient_for();
 
412
            if (parent && actor._parentDestroyId) {
 
413
                parent.disconnect(actor._parentDestroyId);
 
414
                actor._parentDestroyId = 0;
 
415
            }
 
416
            shellwm.completed_destroy(actor);
 
417
        }
 
418
    },
 
419
 
 
420
    _switchWorkspace : function(shellwm, from, to, direction) {
 
421
        if (!this._shouldAnimate()) {
 
422
            shellwm.completed_switch_workspace();
 
423
            return;
 
424
        }
 
425
 
 
426
        let windows = global.get_window_actors();
 
427
 
 
428
        /* @direction is the direction that the "camera" moves, so the
 
429
         * screen contents have to move one screen's worth in the
 
430
         * opposite direction.
 
431
         */
 
432
        let xDest = 0, yDest = 0;
 
433
 
 
434
        if (direction == Meta.MotionDirection.UP ||
 
435
            direction == Meta.MotionDirection.UP_LEFT ||
 
436
            direction == Meta.MotionDirection.UP_RIGHT)
 
437
                yDest = global.screen_height;
 
438
        else if (direction == Meta.MotionDirection.DOWN ||
 
439
            direction == Meta.MotionDirection.DOWN_LEFT ||
 
440
            direction == Meta.MotionDirection.DOWN_RIGHT)
 
441
                yDest = -global.screen_height;
 
442
 
 
443
        if (direction == Meta.MotionDirection.LEFT ||
 
444
            direction == Meta.MotionDirection.UP_LEFT ||
 
445
            direction == Meta.MotionDirection.DOWN_LEFT)
 
446
                xDest = global.screen_width;
 
447
        else if (direction == Meta.MotionDirection.RIGHT ||
 
448
                 direction == Meta.MotionDirection.UP_RIGHT ||
 
449
                 direction == Meta.MotionDirection.DOWN_RIGHT)
 
450
                xDest = -global.screen_width;
 
451
 
 
452
        let switchData = {};
 
453
        this._switchData = switchData;
 
454
        switchData.inGroup = new Clutter.Group();
 
455
        switchData.outGroup = new Clutter.Group();
 
456
        switchData.windows = [];
 
457
 
 
458
        let wgroup = global.window_group;
 
459
        wgroup.add_actor(switchData.inGroup);
 
460
        wgroup.add_actor(switchData.outGroup);
 
461
 
 
462
        for (let i = 0; i < windows.length; i++) {
 
463
            let window = windows[i];
 
464
 
 
465
            if (!window.meta_window.showing_on_its_workspace())
 
466
                continue;
 
467
 
 
468
            if (window.get_workspace() == from) {
 
469
                switchData.windows.push({ window: window,
 
470
                                          parent: window.get_parent() });
 
471
                window.reparent(switchData.outGroup);
 
472
            } else if (window.get_workspace() == to) {
 
473
                switchData.windows.push({ window: window,
 
474
                                          parent: window.get_parent() });
 
475
                window.reparent(switchData.inGroup);
 
476
                window.show_all();
 
477
            }
 
478
        }
 
479
 
 
480
        switchData.inGroup.set_position(-xDest, -yDest);
 
481
        switchData.inGroup.raise_top();
 
482
 
 
483
        Tweener.addTween(switchData.outGroup,
 
484
                         { x: xDest,
 
485
                           y: yDest,
 
486
                           time: WINDOW_ANIMATION_TIME,
 
487
                           transition: 'easeOutQuad',
 
488
                           onComplete: this._switchWorkspaceDone,
 
489
                           onCompleteScope: this,
 
490
                           onCompleteParams: [shellwm]
 
491
                         });
 
492
        Tweener.addTween(switchData.inGroup,
 
493
                         { x: 0,
 
494
                           y: 0,
 
495
                           time: WINDOW_ANIMATION_TIME,
 
496
                           transition: 'easeOutQuad'
 
497
                         });
 
498
    },
 
499
 
 
500
    _switchWorkspaceDone : function(shellwm) {
 
501
        let switchData = this._switchData;
 
502
        if (!switchData)
 
503
            return;
 
504
        this._switchData = null;
 
505
 
 
506
        for (let i = 0; i < switchData.windows.length; i++) {
 
507
                let w = switchData.windows[i];
 
508
                if (w.window.is_destroyed()) // Window gone
 
509
                    continue;
 
510
                if (w.window.get_parent() == switchData.outGroup) {
 
511
                    w.window.reparent(w.parent);
 
512
                    w.window.hide();
 
513
                } else
 
514
                    w.window.reparent(w.parent);
 
515
        }
 
516
        Tweener.removeTweens(switchData.inGroup);
 
517
        Tweener.removeTweens(switchData.outGroup);
 
518
        switchData.inGroup.destroy();
 
519
        switchData.outGroup.destroy();
 
520
 
 
521
        shellwm.completed_switch_workspace();
 
522
    },
 
523
 
 
524
    _startAppSwitcher : function(display, screen, window, binding) {
 
525
        /* prevent a corner case where both popups show up at once */
 
526
        if (this._workspaceSwitcherPopup != null)
 
527
            this._workspaceSwitcherPopup.actor.hide();
 
528
 
 
529
        let tabPopup = new AltTab.AltTabPopup();
 
530
 
 
531
        let modifiers = binding.get_modifiers();
 
532
        let backwards = modifiers & Meta.VirtualModifier.SHIFT_MASK;
 
533
        if (!tabPopup.show(backwards, binding.get_name(), binding.get_mask()))
 
534
            tabPopup.destroy();
 
535
    },
 
536
 
 
537
    _startA11ySwitcher : function(display, screen, window, binding) {
 
538
        let modifiers = binding.get_modifiers();
 
539
        let backwards = modifiers & Meta.VirtualModifier.SHIFT_MASK;
 
540
        Main.ctrlAltTabManager.popup(backwards, binding.get_mask());
 
541
    },
 
542
 
 
543
    _openAppMenu : function(display, screen, window, event, binding) {
 
544
        Main.panel.openAppMenu();
 
545
    },
 
546
 
 
547
    _showWorkspaceSwitcher : function(display, screen, window, binding) {
 
548
        if (screen.n_workspaces == 1)
 
549
            return;
 
550
 
 
551
        if (this._workspaceSwitcherPopup == null)
 
552
            this._workspaceSwitcherPopup = new WorkspaceSwitcherPopup.WorkspaceSwitcherPopup();
 
553
 
 
554
        if (binding.get_name() == 'switch-to-workspace-up')
 
555
            this.actionMoveWorkspaceUp();
 
556
        else if (binding.get_name() == 'switch-to-workspace-down')
 
557
            this.actionMoveWorkspaceDown();
 
558
        // left/right would effectively act as synonyms for up/down if we enabled them;
 
559
        // but that could be considered confusing.
 
560
        // else if (binding.get_name() == 'switch-to-workspace-left')
 
561
        //   this.actionMoveWorkspaceLeft();
 
562
        // else if (binding.get_name() == 'switch-to-workspace-right')
 
563
        //   this.actionMoveWorkspaceRight();
 
564
    },
 
565
 
 
566
    actionMoveWorkspaceLeft: function() {
 
567
        let rtl = (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL);
 
568
        let activeWorkspaceIndex = global.screen.get_active_workspace_index();
 
569
        let indexToActivate = activeWorkspaceIndex;
 
570
        if (rtl && activeWorkspaceIndex < global.screen.n_workspaces - 1)
 
571
            indexToActivate++;
 
572
        else if (!rtl && activeWorkspaceIndex > 0)
 
573
            indexToActivate--;
 
574
 
 
575
        if (indexToActivate != activeWorkspaceIndex)
 
576
            global.screen.get_workspace_by_index(indexToActivate).activate(global.get_current_time());
 
577
 
 
578
        if (!Main.overview.visible)
 
579
            this._workspaceSwitcherPopup.display(WorkspaceSwitcherPopup.UP, indexToActivate);
 
580
    },
 
581
 
 
582
    actionMoveWorkspaceRight: function() {
 
583
        let rtl = (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL);
 
584
        let activeWorkspaceIndex = global.screen.get_active_workspace_index();
 
585
        let indexToActivate = activeWorkspaceIndex;
 
586
        if (rtl && activeWorkspaceIndex > 0)
 
587
            indexToActivate--;
 
588
        else if (!rtl && activeWorkspaceIndex < global.screen.n_workspaces - 1)
 
589
            indexToActivate++;
 
590
 
 
591
        if (indexToActivate != activeWorkspaceIndex)
 
592
            global.screen.get_workspace_by_index(indexToActivate).activate(global.get_current_time());
 
593
 
 
594
        if (!Main.overview.visible)
 
595
            this._workspaceSwitcherPopup.display(WorkspaceSwitcherPopup.DOWN, indexToActivate);
 
596
    },
 
597
 
 
598
    actionMoveWorkspaceUp: function() {
 
599
        let activeWorkspaceIndex = global.screen.get_active_workspace_index();
 
600
        let indexToActivate = activeWorkspaceIndex;
 
601
        if (activeWorkspaceIndex > 0)
 
602
            indexToActivate--;
 
603
 
 
604
        if (indexToActivate != activeWorkspaceIndex)
 
605
            global.screen.get_workspace_by_index(indexToActivate).activate(global.get_current_time());
 
606
 
 
607
        if (!Main.overview.visible)
 
608
            this._workspaceSwitcherPopup.display(WorkspaceSwitcherPopup.UP, indexToActivate);
 
609
    },
 
610
 
 
611
    actionMoveWorkspaceDown: function() {
 
612
        let activeWorkspaceIndex = global.screen.get_active_workspace_index();
 
613
        let indexToActivate = activeWorkspaceIndex;
 
614
        if (activeWorkspaceIndex < global.screen.n_workspaces - 1)
 
615
            indexToActivate++;
 
616
 
 
617
        if (indexToActivate != activeWorkspaceIndex)
 
618
            global.screen.get_workspace_by_index(indexToActivate).activate(global.get_current_time());
 
619
 
 
620
        if (!Main.overview.visible)
 
621
            this._workspaceSwitcherPopup.display(WorkspaceSwitcherPopup.DOWN, indexToActivate);
 
622
    }
 
623
});