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

« back to all changes in this revision

Viewing changes to .pc/04_build-without-caribou.patch/js/ui/keyboard.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:
 
1
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
 
2
 
 
3
const Caribou = imports.gi.Caribou;
 
4
const Clutter = imports.gi.Clutter;
 
5
const DBus = imports.dbus;
 
6
const Gdk = imports.gi.Gdk;
 
7
const Gio = imports.gi.Gio;
 
8
const GLib = imports.gi.GLib;
 
9
const Lang = imports.lang;
 
10
const Shell = imports.gi.Shell;
 
11
const St = imports.gi.St;
 
12
 
 
13
const BoxPointer = imports.ui.boxpointer;
 
14
const Main = imports.ui.main;
 
15
const MessageTray = imports.ui.messageTray;
 
16
 
 
17
const KEYBOARD_SCHEMA = 'org.gnome.shell.keyboard';
 
18
const SHOW_KEYBOARD = 'show-keyboard';
 
19
const KEYBOARD_TYPE = 'keyboard-type';
 
20
 
 
21
// Key constants taken from Antler
 
22
// FIXME: ought to be moved into libcaribou
 
23
const PRETTY_KEYS = {
 
24
    'BackSpace': '\u232b',
 
25
    'space': ' ',
 
26
    'Return': '\u23ce',
 
27
    'Caribou_Prefs': '\u2328',
 
28
    'Caribou_ShiftUp': '\u2b06',
 
29
    'Caribou_ShiftDown': '\u2b07',
 
30
    'Caribou_Emoticons': '\u263a',
 
31
    'Caribou_Symbols': '123',
 
32
    'Caribou_Symbols_More': '{#*',
 
33
    'Caribou_Alpha': 'Abc',
 
34
    'Tab': 'Tab',
 
35
    'Escape': 'Esc',
 
36
    'Control_L': 'Ctrl',
 
37
    'Alt_L': 'Alt'
 
38
};
 
39
 
 
40
const CaribouKeyboardIface = {
 
41
    name: 'org.gnome.Caribou.Keyboard',
 
42
    methods:    [ { name: 'Show',
 
43
                    inSignature: 'u',
 
44
                    outSignature: ''
 
45
                  },
 
46
                  { name: 'Hide',
 
47
                    inSignature: 'u',
 
48
                    outSignature: ''
 
49
                  },
 
50
                  { name: 'SetCursorLocation',
 
51
                    inSignature: 'iiii',
 
52
                    outSignature: ''
 
53
                  },
 
54
                  { name: 'SetEntryLocation',
 
55
                    inSignature: 'iiii',
 
56
                    outSignature: ''
 
57
                  } ],
 
58
    properties: [ { name: 'Name',
 
59
                    signature: 's',
 
60
                    access: 'read' } ]
 
61
};
 
62
 
 
63
function Key() {
 
64
    this._init.apply(this, arguments);
 
65
}
 
66
 
 
67
Key.prototype = {
 
68
    _init : function(key) {
 
69
        this._key = key;
 
70
 
 
71
        this.actor = this._makeKey();
 
72
 
 
73
        this._extended_keys = this._key.get_extended_keys();
 
74
        this._extended_keyboard = null;
 
75
 
 
76
        if (this._key.name == "Control_L" || this._key.name == "Alt_L")
 
77
            this._key.latch = true;
 
78
 
 
79
        this._key.connect('key-pressed', Lang.bind(this, function ()
 
80
                                                   { this.actor.checked = true }));
 
81
        this._key.connect('key-released', Lang.bind(this, function ()
 
82
                                                    { this.actor.checked = false; }));
 
83
 
 
84
        if (this._extended_keys.length > 0) {
 
85
            this._grabbed = false;
 
86
            this._eventCaptureId = 0;
 
87
            this._key.connect('notify::show-subkeys', Lang.bind(this, this._onShowSubkeysChanged));
 
88
            this._boxPointer = new BoxPointer.BoxPointer(St.Side.BOTTOM,
 
89
                                                         { x_fill: true,
 
90
                                                           y_fill: true,
 
91
                                                           x_align: St.Align.START });
 
92
            // Adds style to existing keyboard style to avoid repetition
 
93
            this._boxPointer.actor.add_style_class_name('keyboard-subkeys');
 
94
            this._getExtendedKeys();
 
95
            this.actor._extended_keys = this._extended_keyboard;
 
96
            this._boxPointer.actor.hide();
 
97
            Main.layoutManager.addChrome(this._boxPointer.actor, { visibleInFullscreen: true });
 
98
        }
 
99
    },
 
100
 
 
101
    _makeKey: function () {
 
102
        let label = this._key.name;
 
103
 
 
104
        if (label.length > 1) {
 
105
            let pretty = PRETTY_KEYS[label];
 
106
            if (pretty)
 
107
                label = pretty;
 
108
            else
 
109
                label = this._getUnichar(this._key);
 
110
        }
 
111
 
 
112
        label = GLib.markup_escape_text(label, -1);
 
113
        let button = new St.Button ({ label: label,
 
114
                                      style_class: 'keyboard-key' });
 
115
 
 
116
        button.key_width = this._key.width;
 
117
        button.connect('button-press-event', Lang.bind(this, function () { this._key.press(); }));
 
118
        button.connect('button-release-event', Lang.bind(this, function () { this._key.release(); }));
 
119
 
 
120
        return button;
 
121
    },
 
122
 
 
123
    _getUnichar: function(key) {
 
124
        let keyval = key.keyval;
 
125
        let unichar = Gdk.keyval_to_unicode(keyval);
 
126
        if (unichar) {
 
127
            return String.fromCharCode(unichar);
 
128
        } else {
 
129
            return key.name;
 
130
        }
 
131
    },
 
132
 
 
133
    _getExtendedKeys: function () {
 
134
        this._extended_keyboard = new St.BoxLayout({ style_class: 'keyboard-layout',
 
135
                                                     vertical: false });
 
136
        for (let i = 0; i < this._extended_keys.length; ++i) {
 
137
            let extended_key = this._extended_keys[i];
 
138
            let label = this._getUnichar(extended_key);
 
139
            let key = new St.Button({ label: label, style_class: 'keyboard-key' });
 
140
            key.extended_key = extended_key;
 
141
            key.connect('button-press-event', Lang.bind(this, function () { extended_key.press(); }));
 
142
            key.connect('button-release-event', Lang.bind(this, function () { extended_key.release(); }));
 
143
            this._extended_keyboard.add(key);
 
144
        }
 
145
        this._boxPointer.bin.add_actor(this._extended_keyboard);
 
146
    },
 
147
 
 
148
    _onEventCapture: function (actor, event) {
 
149
        let source = event.get_source();
 
150
        let type = event.type();
 
151
 
 
152
        if ((type == Clutter.EventType.BUTTON_PRESS ||
 
153
             type == Clutter.EventType.BUTTON_RELEASE) &&
 
154
            this._extended_keyboard.contains(source)) {
 
155
            source.extended_key.press();
 
156
            source.extended_key.release();
 
157
            return false;
 
158
        }
 
159
        if (type == Clutter.EventType.BUTTON_PRESS) {
 
160
            this._boxPointer.actor.hide();
 
161
            this._ungrab();
 
162
            return true;
 
163
        }
 
164
        return false;
 
165
    },
 
166
 
 
167
    _ungrab: function () {
 
168
        global.stage.disconnect(this._eventCaptureId);
 
169
        this._eventCaptureId = 0;
 
170
        this._grabbed = false;
 
171
        Main.popModal(this.actor);
 
172
    },
 
173
 
 
174
    _onShowSubkeysChanged: function () {
 
175
        if (this._key.show_subkeys) {
 
176
            this.actor.fake_release();
 
177
            this._boxPointer.actor.raise_top();
 
178
            this._boxPointer.setPosition(this.actor, 0.5);
 
179
            this._boxPointer.show(true);
 
180
            this.actor.set_hover(false);
 
181
            if (!this._grabbed) {
 
182
                 Main.pushModal(this.actor);
 
183
                 this._eventCaptureId = global.stage.connect('captured-event', Lang.bind(this, this._onEventCapture));
 
184
                 this._grabbed = true;
 
185
            }
 
186
            this._key.release();
 
187
        } else {
 
188
            if (this._grabbed)
 
189
                this._ungrab();
 
190
            this._boxPointer.hide(true);
 
191
        }
 
192
    }
 
193
};
 
194
 
 
195
function Keyboard() {
 
196
    this._init.apply(this, arguments);
 
197
}
 
198
 
 
199
Keyboard.prototype = {
 
200
    _init: function () {
 
201
        DBus.session.exportObject('/org/gnome/Caribou/Keyboard', this);
 
202
        DBus.session.acquire_name('org.gnome.Caribou.Keyboard', 0, null, null);
 
203
 
 
204
        this._timestamp = global.get_current_time();
 
205
        this.actor = new St.BoxLayout({ name: 'keyboard', vertical: true, reactive: true });
 
206
        Main.layoutManager.keyboardBox.add_actor(this.actor);
 
207
        Main.layoutManager.trackChrome(this.actor);
 
208
        Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._redraw));
 
209
 
 
210
        this._keyboardSettings = new Gio.Settings({ schema: KEYBOARD_SCHEMA });
 
211
        this._keyboardSettings.connect('changed', Lang.bind(this, this._settingsChanged));
 
212
        this._settingsChanged();
 
213
    },
 
214
 
 
215
    init: function () {
 
216
        if (this._enableKeyboard)
 
217
            this._redraw();
 
218
    },
 
219
 
 
220
    _settingsChanged: function () {
 
221
        this._enableKeyboard = this._keyboardSettings.get_boolean(SHOW_KEYBOARD);
 
222
        if (!this._enableKeyboard && !this._keyboard)
 
223
            return;
 
224
        if (this._enableKeyboard && this._keyboard &&
 
225
            this._keyboard.keyboard_type == this._keyboardSettings.get_string(KEYBOARD_TYPE))
 
226
            return;
 
227
 
 
228
        if (this._keyboard)
 
229
            this._destroyKeyboard();
 
230
        if (this._enableKeyboard)
 
231
            this._setupKeyboard();
 
232
        else
 
233
            Main.layoutManager.hideKeyboard(true);
 
234
    },
 
235
 
 
236
    _destroyKeyboard: function() {
 
237
        if (this._keyboardNotifyId)
 
238
            this._keyboard.disconnect(this._keyboardNotifyId);
 
239
        if (this._focusNotifyId)
 
240
            global.stage.disconnect(this._focusNotifyId);
 
241
        this._keyboard = null;
 
242
        this.actor.destroy_children();
 
243
 
 
244
        this._destroySource();
 
245
    },
 
246
 
 
247
    _setupKeyboard: function() {
 
248
        this._keyboard = new Caribou.KeyboardModel({ keyboard_type: this._keyboardSettings.get_string(KEYBOARD_TYPE) });
 
249
        this._groups = {};
 
250
        this._current_page = null;
 
251
 
 
252
        // Initialize keyboard key measurements
 
253
        this._numOfHorizKeys = 0;
 
254
        this._numOfVertKeys = 0;
 
255
 
 
256
        this._addKeys();
 
257
 
 
258
        this._keyboardNotifyId = this._keyboard.connect('notify::active-group', Lang.bind(this, this._onGroupChanged));
 
259
        this._focusNotifyId = global.stage.connect('notify::key-focus', Lang.bind(this, this._onKeyFocusChanged));
 
260
        this._createSource();
 
261
    },
 
262
 
 
263
    _onKeyFocusChanged: function () {
 
264
        let focus = global.stage.key_focus;
 
265
 
 
266
        if (focus == global.stage || focus == null)
 
267
            return;
 
268
 
 
269
        if (focus instanceof Clutter.Text)
 
270
            this.show();
 
271
        else {
 
272
            if (focus._extended_keys == null)
 
273
                this.hide();
 
274
        }
 
275
    },
 
276
 
 
277
    _addKeys: function () {
 
278
        let groups = this._keyboard.get_groups();
 
279
        for (let i = 0; i < groups.length; ++i) {
 
280
             let gname = groups[i];
 
281
             let group = this._keyboard.get_group(gname);
 
282
             group.connect('notify::active-level', Lang.bind(this, this._onLevelChanged));
 
283
             let layers = {};
 
284
             let levels = group.get_levels();
 
285
             for (let j = 0; j < levels.length; ++j) {
 
286
                 let lname = levels[j];
 
287
                 let level = group.get_level(lname);
 
288
                 let layout = new St.BoxLayout({ style_class: 'keyboard-layout',
 
289
                                                 vertical: true });
 
290
                 this._loadRows(level, layout);
 
291
                 layers[lname] = layout;
 
292
                 this.actor.add(layout, { x_fill: false });
 
293
 
 
294
                 layout.hide();
 
295
             }
 
296
             this._groups[gname] = layers;
 
297
        }
 
298
 
 
299
        this._setActiveLayer();
 
300
    },
 
301
 
 
302
    _getTrayIcon: function () {
 
303
        let trayButton = new St.Button ({ label: "tray", style_class: 'keyboard-key' });
 
304
        trayButton.key_width = 1;
 
305
        trayButton.connect('button-press-event', Lang.bind(this, function () {
 
306
            Main.messageTray.toggle();
 
307
        }));
 
308
 
 
309
        Main.overview.connect('showing', Lang.bind(this, function () {
 
310
            trayButton.reactive = false;
 
311
            trayButton.add_style_pseudo_class('grayed');
 
312
        }));
 
313
        Main.overview.connect('hiding', Lang.bind(this, function () {
 
314
            trayButton.reactive = true;
 
315
            trayButton.remove_style_pseudo_class('grayed');
 
316
        }));
 
317
 
 
318
        return trayButton;
 
319
    },
 
320
 
 
321
    _addRows : function (keys, layout) {
 
322
        let keyboard_row = new St.BoxLayout();
 
323
        for (let i = 0; i < keys.length; ++i) {
 
324
            let children = keys[i].get_children();
 
325
            let right_box = new St.BoxLayout({ style_class: 'keyboard-row' });
 
326
            let left_box = new St.BoxLayout({ style_class: 'keyboard-row' });
 
327
            for (let j = 0; j < children.length; ++j) {
 
328
                if (this._numOfHorizKeys == 0)
 
329
                    this._numOfHorizKeys = children.length;
 
330
                let key = children[j];
 
331
                let button = new Key(key);
 
332
 
 
333
                if (key.align == 'right')
 
334
                    right_box.add(button.actor);
 
335
                else
 
336
                    left_box.add(button.actor);
 
337
                if (key.name == "Caribou_Prefs") {
 
338
                    key.connect('key-released', Lang.bind(this, this.hide));
 
339
 
 
340
                    // Add new key for hiding message tray
 
341
                    right_box.add(this._getTrayIcon());
 
342
                }
 
343
            }
 
344
            keyboard_row.add(left_box, { expand: true, x_fill: false, x_align: St.Align.START });
 
345
            keyboard_row.add(right_box, { expand: true, x_fill: false, x_align: St.Align.END });
 
346
        }
 
347
        layout.add(keyboard_row);
 
348
    },
 
349
 
 
350
    _loadRows : function (level, layout) {
 
351
        let rows = level.get_rows();
 
352
        for (let i = 0; i < rows.length; ++i) {
 
353
            let row = rows[i];
 
354
            if (this._numOfVertKeys == 0)
 
355
                this._numOfVertKeys = rows.length;
 
356
            this._addRows(row.get_columns(), layout);
 
357
        }
 
358
 
 
359
    },
 
360
 
 
361
    _redraw: function () {
 
362
        let monitor = Main.layoutManager.bottomMonitor;
 
363
        let maxHeight = monitor.height / 3;
 
364
        this.actor.width = monitor.width;
 
365
 
 
366
        let layout = this._current_page;
 
367
        let verticalSpacing = layout.get_theme_node().get_length('spacing');
 
368
        let padding = layout.get_theme_node().get_length('padding');
 
369
 
 
370
        let box = layout.get_children()[0].get_children()[0];
 
371
        let horizontalSpacing = box.get_theme_node().get_length('spacing');
 
372
        let allHorizontalSpacing = (this._numOfHorizKeys - 1) * horizontalSpacing;
 
373
        let keyWidth = Math.floor((this.actor.width - allHorizontalSpacing - 2 * padding) / this._numOfHorizKeys);
 
374
 
 
375
        let allVerticalSpacing = (this._numOfVertKeys - 1) * verticalSpacing;
 
376
        let keyHeight = Math.floor((maxHeight - allVerticalSpacing - 2 * padding) / this._numOfVertKeys);
 
377
 
 
378
        let keySize = Math.min(keyWidth, keyHeight);
 
379
        this.actor.height = keySize * this._numOfVertKeys + allVerticalSpacing + 2 * padding;
 
380
 
 
381
        let rows = this._current_page.get_children();
 
382
        for (let i = 0; i < rows.length; ++i) {
 
383
            let keyboard_row = rows[i];
 
384
            let boxes = keyboard_row.get_children();
 
385
            for (let j = 0; j < boxes.length; ++j) {
 
386
                let keys = boxes[j].get_children();
 
387
                for (let k = 0; k < keys.length; ++k) {
 
388
                    let child = keys[k];
 
389
                    child.width = keySize * child.key_width;
 
390
                    child.height = keySize;
 
391
                    if (child._extended_keys) {
 
392
                        let extended_keys = child._extended_keys.get_children();
 
393
                        for (let n = 0; n < extended_keys.length; ++n) {
 
394
                            let extended_key = extended_keys[n];
 
395
                            extended_key.width = keySize;
 
396
                            extended_key.height = keySize;
 
397
                        }
 
398
                    }
 
399
                }
 
400
            }
 
401
        }
 
402
    },
 
403
 
 
404
    _onLevelChanged: function () {
 
405
        this._setActiveLayer();
 
406
        this._redraw();
 
407
    },
 
408
 
 
409
    _onGroupChanged: function () {
 
410
        this._setActiveLayer();
 
411
        this._redraw();
 
412
    },
 
413
 
 
414
    _setActiveLayer: function () {
 
415
        let active_group_name = this._keyboard.active_group;
 
416
        let active_group = this._keyboard.get_group(active_group_name);
 
417
        let active_level = active_group.active_level;
 
418
        let layers = this._groups[active_group_name];
 
419
 
 
420
        if (this._current_page != null) {
 
421
            this._current_page.hide();
 
422
        }
 
423
 
 
424
        this._current_page = layers[active_level];
 
425
        this._current_page.show();
 
426
    },
 
427
 
 
428
    _createSource: function () {
 
429
        if (this._source == null) {
 
430
            this._source = new KeyboardSource(this);
 
431
            this._source.setTransient(true);
 
432
            Main.messageTray.add(this._source);
 
433
        }
 
434
    },
 
435
 
 
436
    _destroySource: function () {
 
437
        if (this._source) {
 
438
            this._source.destroy();
 
439
            this._source = null;
 
440
        }
 
441
    },
 
442
 
 
443
    show: function () {
 
444
        this._redraw();
 
445
 
 
446
        Main.layoutManager.showKeyboard();
 
447
        this._destroySource();
 
448
    },
 
449
 
 
450
    hide: function () {
 
451
        Main.layoutManager.hideKeyboard();
 
452
        this._createSource();
 
453
    },
 
454
 
 
455
    _moveTemporarily: function () {
 
456
        let currentWindow = global.screen.get_display().focus_window;
 
457
        let rect = currentWindow.get_outer_rect();
 
458
 
 
459
        let newX = rect.x;
 
460
        let newY = 3 * this.actor.height / 2;
 
461
        currentWindow.move_frame(true, newX, newY);
 
462
    },
 
463
 
 
464
    _setLocation: function (x, y) {
 
465
        if (y >= 2 * this.actor.height)
 
466
            this._moveTemporarily();
 
467
    },
 
468
 
 
469
    // D-Bus methods
 
470
    Show: function(timestamp) {
 
471
        if (timestamp - this._timestamp < 0)
 
472
            return;
 
473
 
 
474
        this._timestamp = timestamp;
 
475
        this.show();
 
476
    },
 
477
 
 
478
    Hide: function(timestamp) {
 
479
        if (timestamp - this._timestamp < 0)
 
480
            return;
 
481
 
 
482
        this._timestamp = timestamp;
 
483
        this.hide();
 
484
    },
 
485
 
 
486
    SetCursorLocation: function(x, y, w, h) {
 
487
        this._setLocation(x, y);
 
488
    },
 
489
 
 
490
    SetEntryLocation: function(x, y, w, h) {
 
491
        this._setLocation(x, y);
 
492
    },
 
493
 
 
494
    get Name() {
 
495
        return 'gnome-shell';
 
496
    }
 
497
};
 
498
DBus.conformExport(Keyboard.prototype, CaribouKeyboardIface);
 
499
 
 
500
function KeyboardSource() {
 
501
    this._init.apply(this, arguments);
 
502
}
 
503
 
 
504
KeyboardSource.prototype = {
 
505
    __proto__: MessageTray.Source.prototype,
 
506
 
 
507
    _init: function(keyboard) {
 
508
        this._keyboard = keyboard;
 
509
        MessageTray.Source.prototype._init.call(this, _("Keyboard"));
 
510
 
 
511
        this._setSummaryIcon(this.createNotificationIcon());
 
512
    },
 
513
 
 
514
    createNotificationIcon: function() {
 
515
        return new St.Icon({ icon_name: 'input-keyboard',
 
516
                             icon_type: St.IconType.SYMBOLIC,
 
517
                             icon_size: this.ICON_SIZE });
 
518
    },
 
519
 
 
520
     handleSummaryClick: function() {
 
521
        let event = Clutter.get_current_event();
 
522
        if (event.type() != Clutter.EventType.BUTTON_RELEASE)
 
523
            return false;
 
524
 
 
525
        this.open();
 
526
        return true;
 
527
    },
 
528
 
 
529
    open: function() {
 
530
        this._keyboard.show();
 
531
    }
 
532
};