~ubuntu-branches/ubuntu/vivid/ibus/vivid

« back to all changes in this revision

Viewing changes to ui/gtk3/propertypanel.vala

  • Committer: Package Import Robot
  • Author(s): Osamu Aoki
  • Date: 2014-01-26 16:28:28 UTC
  • mfrom: (1.5.4)
  • Revision ID: package-import@ubuntu.com-20140126162828-un06o3yylyb9xonf
Tags: 1.5.5-1
* New upstream release.
  - CVE-2013-4509 (also with FC19 patch).  Closes: #729065
  - Update ibus-setup message. Closes: #729472
* Sync with ibus-1.5.5-1.fc19.src.rpm
* Update debian/rules using DEB_*_MAINT_APPEND (compat=9).
* Update README.Debian. Closes: #732434
* Update Standards-Version: 3.9.5

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* vim:set et sts=4 sw=4:
 
2
 *
 
3
 * ibus - The Input Bus
 
4
 *
 
5
 * Copyright(c) 2013 Red Hat, Inc.
 
6
 * Copyright(c) 2013 Peng Huang <shawn.p.huang@gmail.com>
 
7
 * Copyright(c) 2013 Takao Fujiwara <takao.fujiwara1@gmail.com>
 
8
 *
 
9
 * This library is free software; you can redistribute it and/or
 
10
 * modify it under the terms of the GNU Lesser General Public
 
11
 * License as published by the Free Software Foundation; either
 
12
 * version 2.1 of the License, or (at your option) any later version.
 
13
 *
 
14
 * This library is distributed in the hope that it will be useful,
 
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
17
 * Lesser General Public License for more details.
 
18
 *
 
19
 * You should have received a copy of the GNU Lesser General Public
 
20
 * License along with this library; if not, write to the Free Software
 
21
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
 
22
 * USA
 
23
 */
 
24
 
 
25
enum PanelShow {
 
26
    DO_NOT_SHOW,
 
27
    AUTO_HIDE,
 
28
    ALWAYS
 
29
}
 
30
 
 
31
public class PropertyPanel : Gtk.Box {
 
32
    private Gtk.Window m_toplevel;
 
33
    private IBus.PropList m_props;
 
34
    private IPropToolItem[] m_items;
 
35
    private Gdk.Rectangle m_cursor_location = Gdk.Rectangle(){
 
36
            x = -1, y = -1, width = 0, height = 0 };
 
37
    private int m_show = PanelShow.AUTO_HIDE;
 
38
    private uint m_auto_hide_timeout = 10000;
 
39
    private uint m_auto_hide_timeout_id = 0;
 
40
 
 
41
    public PropertyPanel() {
 
42
        /* Chain up base class constructor */
 
43
        GLib.Object(orientation: Gtk.Orientation.HORIZONTAL,
 
44
                    spacing: 0);
 
45
 
 
46
        set_visible(true);
 
47
 
 
48
        m_toplevel = new Gtk.Window(Gtk.WindowType.POPUP);
 
49
        m_toplevel.add_events(Gdk.EventMask.BUTTON_PRESS_MASK);
 
50
 
 
51
        Handle handle = new Handle();
 
52
        handle.set_visible(true);
 
53
        pack_start(handle, false, false, 0);
 
54
 
 
55
        m_toplevel.add(this);
 
56
    }
 
57
 
 
58
    public void set_properties(IBus.PropList props) {
 
59
        foreach (var item in m_items)
 
60
            (item as Gtk.Widget).destroy();
 
61
        m_items = {};
 
62
 
 
63
        m_props = props;
 
64
 
 
65
        create_menu_items();
 
66
    }
 
67
 
 
68
    public void update_property(IBus.Property prop) {
 
69
        GLib.assert(prop != null);
 
70
 
 
71
        debug("update_property(prop.key = %s)\n", prop.get_key());
 
72
 
 
73
        if (m_props != null)
 
74
            m_props.update_property(prop);
 
75
 
 
76
        /* Need to update GUI since panel buttons are not redrawn. */
 
77
        foreach (var item in m_items)
 
78
            item.update_property(prop);
 
79
 
 
80
        show_with_auto_hide_timer();
 
81
    }
 
82
 
 
83
    public void set_cursor_location(int x, int y, int width, int height) {
 
84
        /* FIXME: set_cursor_location() has a different behavior
 
85
         * in embedded preedit by applications.
 
86
         * GtkTextView applications, e.g. gedit, always call
 
87
         * set_cursor_location() with and without preedit
 
88
         * but VTE applications, e.g. gnome-terminal, and xterm
 
89
         * do not call set_cursor_location() with preedit.
 
90
         * firefox and thunderbird do not call set_cursor_location()
 
91
         * without preedit.
 
92
         * This may treat GtkIMContext and XIM with different ways.
 
93
         * Maybe get_preedit_string() class method.
 
94
         */
 
95
 
 
96
        /* FIXME: When the cursor is at the bottom of the screen,
 
97
         * gedit returns the right cursor position but terminal applications
 
98
         * such as gnome-terminal, xfce4-terminal and etc, the position is
 
99
         * not accurate and the cursor and panel could be overlapped slightly.
 
100
         * Maybe it's a bug in vte.
 
101
         */
 
102
        Gdk.Rectangle location = Gdk.Rectangle(){
 
103
            x = x, y = y, width = width, height = height };
 
104
 
 
105
        if (m_cursor_location == location)
 
106
            return;
 
107
 
 
108
        debug("set_cursor_location(x = %d, y = %d, width = %d, height = %d)\n",
 
109
              x, y, width, height);
 
110
 
 
111
        /* Hide the panel in AUTO_HIDE mode when the cursor position is
 
112
         * chagned on the same input context by typing keyboard or
 
113
         * clicking mouse. (But not focus change or property change)
 
114
         */
 
115
        if (m_show == PanelShow.AUTO_HIDE)
 
116
            if (m_cursor_location.x != -1 || m_cursor_location.y != -1) {
 
117
                m_cursor_location = location;
 
118
                hide_if_necessary();
 
119
                adjust_window_position();
 
120
                return;
 
121
            }
 
122
 
 
123
        m_cursor_location = location;
 
124
        adjust_window_position();
 
125
        show_with_auto_hide_timer();
 
126
    }
 
127
 
 
128
    public void set_preedit_text(IBus.Text? text, uint cursor) {
 
129
        if (text == null && cursor == 0)
 
130
            return;
 
131
 
 
132
        debug("set_preedit_text(text, cursor = %u)\n", cursor);
 
133
 
 
134
        /* Hide the panel in AUTO_HIDE mode when embed-preedit-text value
 
135
         * is disabled and the preedit is changed on the same input context.
 
136
         */
 
137
        hide_if_necessary();
 
138
    }
 
139
 
 
140
    public void set_auxiliary_text(IBus.Text? text) {
 
141
        if (text == null)
 
142
            return;
 
143
 
 
144
        debug("set_auxiliary_text(text)\n");
 
145
 
 
146
        hide_if_necessary();
 
147
    }
 
148
 
 
149
    public void set_lookup_table(IBus.LookupTable? table) {
 
150
        if (table == null)
 
151
            return;
 
152
 
 
153
        debug("set_lookup_table(table)\n");
 
154
 
 
155
        hide_if_necessary();
 
156
    }
 
157
 
 
158
    public new void show() {
 
159
        if (m_show == PanelShow.DO_NOT_SHOW || m_items.length == 0) {
 
160
            m_toplevel.hide();
 
161
            return;
 
162
        }
 
163
        else if (m_show == PanelShow.ALWAYS) {
 
164
            m_toplevel.show_all();
 
165
            return;
 
166
        }
 
167
 
 
168
        /* Do not change the state here if m_show == AUTO_HIDE. */
 
169
    }
 
170
 
 
171
    public new void hide() {
 
172
        m_toplevel.hide();
 
173
    }
 
174
 
 
175
    public void focus_in() {
 
176
        debug("focus_in()\n");
 
177
 
 
178
        /* Reset m_auto_hide_timeout_id in previous focus-in */
 
179
        hide_if_necessary();
 
180
 
 
181
        /* Invalidate m_cursor_location before set_cursor_location()
 
182
         * is called because the position can be same even if the input
 
183
         * focus is changed.
 
184
         * E.g. Two tabs on gnome-terminal can keep the cursor position.
 
185
         */
 
186
        m_cursor_location = { -1, -1, 0, 0 };
 
187
 
 
188
       /* set_cursor_location() will be called later. */
 
189
    }
 
190
 
 
191
    public void set_show(int _show) {
 
192
        m_show = _show;
 
193
        show();
 
194
    }
 
195
 
 
196
    public void set_auto_hide_timeout(uint timeout) {
 
197
        m_auto_hide_timeout = timeout;
 
198
    }
 
199
 
 
200
    public override void get_preferred_width(out int minimum_width,
 
201
                                             out int natural_width) {
 
202
        base.get_preferred_width(out minimum_width, out natural_width);
 
203
        m_toplevel.resize(1, 1);
 
204
    }
 
205
 
 
206
    public override void get_preferred_height(out int minimum_width,
 
207
                                              out int natural_width) {
 
208
        base.get_preferred_height(out minimum_width, out natural_width);
 
209
        m_toplevel.resize(1, 1);
 
210
    }
 
211
 
 
212
    private void create_menu_items() {
 
213
        int i = 0;
 
214
        while (true) {
 
215
            IBus.Property prop = m_props.get(i);
 
216
            if (prop == null)
 
217
                break;
 
218
 
 
219
            i++;
 
220
            IPropToolItem item = null;
 
221
            switch(prop.get_prop_type()) {
 
222
                case IBus.PropType.NORMAL:
 
223
                    item = new PropToolButton(prop);
 
224
                    break;
 
225
                case IBus.PropType.TOGGLE:
 
226
                    item = new PropToggleToolButton(prop);
 
227
                    break;
 
228
                case IBus.PropType.MENU:
 
229
                    item = new PropMenuToolButton(prop);
 
230
                    break;
 
231
                case IBus.PropType.SEPARATOR:
 
232
                    item = new PropSeparatorToolItem(prop);
 
233
                    break;
 
234
                default:
 
235
                    warning("unknown property type %d",
 
236
                            (int) prop.get_prop_type());
 
237
                    break;
 
238
            }
 
239
            if (item != null) {
 
240
                pack_start(item as Gtk.Widget, false, false, 0);
 
241
                m_items += item;
 
242
                item.property_activate.connect((w, k, s) =>
 
243
                                               property_activate(k, s));
 
244
            }
 
245
        }
 
246
    }
 
247
 
 
248
    private void move(int x, int y) {
 
249
        m_toplevel.move(x, y);
 
250
    }
 
251
 
 
252
    private void adjust_window_position() {
 
253
        Gdk.Point cursor_right_bottom = {
 
254
                m_cursor_location.x + m_cursor_location.width,
 
255
                m_cursor_location.y + m_cursor_location.height
 
256
        };
 
257
 
 
258
        Gtk.Allocation allocation;
 
259
        m_toplevel.get_allocation(out allocation);
 
260
        Gdk.Point window_right_bottom = {
 
261
            cursor_right_bottom.x + allocation.width,
 
262
            cursor_right_bottom.y + allocation.height
 
263
        };
 
264
 
 
265
        Gdk.Window root = Gdk.get_default_root_window();
 
266
        int root_width = root.get_width();
 
267
        int root_height = root.get_height();
 
268
 
 
269
        int x, y;
 
270
        if (window_right_bottom.x > root_width)
 
271
            x = root_width - allocation.width;
 
272
        else
 
273
            x = cursor_right_bottom.x;
 
274
 
 
275
        if (window_right_bottom.y > root_height)
 
276
            y = m_cursor_location.y - allocation.height;
 
277
        else
 
278
            y = cursor_right_bottom.y;
 
279
 
 
280
        move(x, y);
 
281
    }
 
282
 
 
283
    private void show_with_auto_hide_timer() {
 
284
        if (m_show != PanelShow.AUTO_HIDE || m_items.length == 0)
 
285
            return;
 
286
 
 
287
        if (m_auto_hide_timeout_id != 0)
 
288
            GLib.Source.remove(m_auto_hide_timeout_id);
 
289
 
 
290
        m_toplevel.show_all();
 
291
 
 
292
        /* Change the priority because IME typing sometimes freezes. */
 
293
        m_auto_hide_timeout_id = GLib.Timeout.add(m_auto_hide_timeout, () => {
 
294
            m_toplevel.hide();
 
295
            m_auto_hide_timeout_id = 0;
 
296
            return false;
 
297
        },
 
298
        GLib.Priority.DEFAULT_IDLE);
 
299
    }
 
300
 
 
301
    private void hide_if_necessary() {
 
302
        if (m_show == PanelShow.AUTO_HIDE && m_auto_hide_timeout_id != 0) {
 
303
            GLib.Source.remove(m_auto_hide_timeout_id);
 
304
            m_auto_hide_timeout_id = 0;
 
305
            m_toplevel.hide();
 
306
        }
 
307
    }
 
308
 
 
309
    public signal void property_activate(string key, int state);
 
310
}
 
311
 
 
312
public interface IPropToolItem : GLib.Object {
 
313
    public abstract void update_property(IBus.Property prop);
 
314
    public signal void property_activate(string key, int state);
 
315
}
 
316
 
 
317
public class PropMenu : Gtk.Menu, IPropToolItem {
 
318
    private Gtk.Widget m_parent_button;
 
319
    private IPropItem[] m_items;
 
320
 
 
321
    public PropMenu(IBus.Property prop) {
 
322
        /* Chain up base class constructor */
 
323
        GLib.Object();
 
324
 
 
325
        set_take_focus(false);
 
326
        create_items(prop.get_sub_props());
 
327
        show_all();
 
328
        set_sensitive(prop.get_sensitive());
 
329
    }
 
330
 
 
331
    public void update_property(IBus.Property prop) {
 
332
        foreach (var item in m_items)
 
333
            item.update_property(prop);
 
334
    }
 
335
 
 
336
    public new void popup(uint       button,
 
337
                          uint32     activate_time,
 
338
                          Gtk.Widget widget) {
 
339
        m_parent_button = widget;
 
340
        base.popup(null, null, menu_position, button, activate_time);
 
341
    }
 
342
 
 
343
    public override void destroy() {
 
344
        m_parent_button = null;
 
345
        m_items = {};
 
346
        base.destroy();
 
347
    }
 
348
 
 
349
    private void create_items(IBus.PropList props) {
 
350
        int i = 0;
 
351
        PropRadioMenuItem last_radio = null;
 
352
 
 
353
        while (true) {
 
354
            IBus.Property prop = props.get(i);
 
355
            if (prop == null)
 
356
                break;
 
357
 
 
358
            i++;
 
359
            IPropItem item = null;
 
360
            switch(prop.get_prop_type()) {
 
361
                case IBus.PropType.NORMAL:
 
362
                    item = new PropImageMenuItem(prop);
 
363
                    break;
 
364
                case IBus.PropType.TOGGLE:
 
365
                    item = new PropCheckMenuItem(prop);
 
366
                    break;
 
367
                case IBus.PropType.RADIO:
 
368
                    last_radio = new PropRadioMenuItem(prop, last_radio);
 
369
                    item = last_radio;
 
370
                    break;
 
371
                case IBus.PropType.MENU:
 
372
                    {
 
373
                        var menuitem = new PropImageMenuItem(prop);
 
374
                        menuitem.set_submenu(new PropMenu(prop));
 
375
                        item = menuitem;
 
376
                    }
 
377
                    break;
 
378
                case IBus.PropType.SEPARATOR:
 
379
                    item = new PropSeparatorMenuItem(prop);
 
380
                    break;
 
381
                default:
 
382
                    warning("Unknown property type: %d",
 
383
                            (int) prop.get_prop_type());
 
384
                    break;
 
385
            }
 
386
            if (prop.get_prop_type() != IBus.PropType.RADIO)
 
387
                last_radio = null;
 
388
            if (item != null) {
 
389
                append(item as Gtk.MenuItem);
 
390
                item.property_activate.connect((w, k, s) =>
 
391
                                               property_activate(k, s));
 
392
                m_items += item;
 
393
            }
 
394
        }
 
395
    }
 
396
 
 
397
    private void menu_position(Gtk.Menu menu,
 
398
                               out int  x,
 
399
                               out int  y,
 
400
                               out bool push_in) {
 
401
        var button = m_parent_button;
 
402
        var screen = button.get_screen();
 
403
        var monitor = screen.get_monitor_at_window(button.get_window());
 
404
 
 
405
        Gdk.Rectangle monitor_location;
 
406
        screen.get_monitor_geometry(monitor, out monitor_location);
 
407
 
 
408
        button.get_window().get_origin(out x, out y);
 
409
 
 
410
        Gtk.Allocation button_allocation;
 
411
        button.get_allocation(out button_allocation);
 
412
 
 
413
        x += button_allocation.x;
 
414
        y += button_allocation.y;
 
415
 
 
416
        int menu_width;
 
417
        int menu_height;
 
418
        menu.get_size_request(out menu_width, out menu_height);
 
419
 
 
420
        if (x + menu_width >= monitor_location.width)
 
421
            x -= menu_width - button_allocation.width;
 
422
        else if (x - menu_width <= 0)
 
423
            ;
 
424
        else {
 
425
            if (x <= monitor_location.width * 3 / 4)
 
426
                ;
 
427
            else
 
428
                x -= menu_width - button_allocation.width;
 
429
        }
 
430
 
 
431
        if (y + button_allocation.height + menu_width
 
432
                >= monitor_location.height)
 
433
            y -= menu_height;
 
434
        else if (y - menu_height <= 0)
 
435
            y += button_allocation.height;
 
436
        else {
 
437
            if (y <= monitor_location.height * 3 / 4)
 
438
                y += button_allocation.height;
 
439
            else
 
440
                y -= menu_height;
 
441
        }
 
442
 
 
443
        push_in = false;
 
444
    }
 
445
}
 
446
 
 
447
public class PropToolButton : Gtk.ToolButton, IPropToolItem {
 
448
    private IBus.Property m_prop = null;
 
449
 
 
450
    public PropToolButton(IBus.Property prop) {
 
451
        string label = prop.get_symbol().get_text();
 
452
 
 
453
        /* Chain up base class constructor */
 
454
        GLib.Object(label: label);
 
455
 
 
456
        m_prop = prop;
 
457
 
 
458
        set_homogeneous(false);
 
459
        sync();
 
460
    }
 
461
 
 
462
    public void update_property(IBus.Property prop) {
 
463
        if (m_prop.get_key() != prop.get_key())
 
464
            return;
 
465
        m_prop.set_symbol(prop.get_symbol());
 
466
        m_prop.set_tooltip(prop.get_tooltip());
 
467
        m_prop.set_sensitive(prop.get_sensitive());
 
468
        m_prop.set_icon(prop.get_icon());
 
469
        m_prop.set_state(prop.get_state());
 
470
        m_prop.set_visible(prop.get_visible());
 
471
        sync();
 
472
    }
 
473
 
 
474
    private void sync() {
 
475
        set_label(m_prop.get_symbol().get_text());
 
476
        set_tooltip_text(m_prop.get_tooltip().get_text());
 
477
        set_sensitive(m_prop.get_sensitive());
 
478
        set_icon_name(m_prop.get_icon());
 
479
 
 
480
        if (m_prop.get_visible())
 
481
            show();
 
482
        else
 
483
            hide();
 
484
    }
 
485
 
 
486
    public override void clicked() {
 
487
        property_activate(m_prop.get_key(), m_prop.get_state());
 
488
    }
 
489
 
 
490
    public new void set_icon_name(string icon_name) {
 
491
        string label = m_prop.get_symbol().get_text();
 
492
        IconWidget icon_widget = null;
 
493
 
 
494
        if (label == "") {
 
495
            label = null;
 
496
            icon_widget = new IconWidget(icon_name, Gtk.IconSize.BUTTON);
 
497
            set_is_important(false);
 
498
        } else {
 
499
            set_is_important(true);
 
500
        }
 
501
 
 
502
        set_icon_widget(icon_widget);
 
503
    }
 
504
}
 
505
 
 
506
public class PropToggleToolButton : Gtk.ToggleToolButton, IPropToolItem {
 
507
    private IBus.Property m_prop = null;
 
508
 
 
509
    public PropToggleToolButton(IBus.Property prop) {
 
510
        /* Chain up base class constructor */
 
511
        GLib.Object();
 
512
 
 
513
        m_prop = prop;
 
514
 
 
515
        set_homogeneous(false);
 
516
        sync();
 
517
    }
 
518
 
 
519
    public new void set_property(IBus.Property prop) {
 
520
        m_prop = prop;
 
521
        sync();
 
522
    }
 
523
 
 
524
    public void update_property(IBus.Property prop) {
 
525
        if (m_prop.get_key() != prop.get_key())
 
526
            return;
 
527
        m_prop.set_symbol(prop.get_symbol());
 
528
        m_prop.set_tooltip(prop.get_tooltip());
 
529
        m_prop.set_sensitive(prop.get_sensitive());
 
530
        m_prop.set_icon(prop.get_icon());
 
531
        m_prop.set_state(prop.get_state());
 
532
        m_prop.set_visible(prop.get_visible());
 
533
        sync();
 
534
    }
 
535
 
 
536
    private void sync() {
 
537
        set_label(m_prop.get_symbol().get_text());
 
538
        set_tooltip_text(m_prop.get_tooltip().get_text());
 
539
        set_sensitive(m_prop.get_sensitive());
 
540
        set_icon_name(m_prop.get_icon());
 
541
        set_active(m_prop.get_state() == IBus.PropState.CHECKED);
 
542
 
 
543
        if (m_prop.get_visible())
 
544
            show();
 
545
        else
 
546
            hide();
 
547
    }
 
548
 
 
549
    public override void toggled() {
 
550
        /* Do not send property-activate to engine in case the event is
 
551
         * sent from engine. */
 
552
 
 
553
        bool do_emit = false;
 
554
 
 
555
        if (get_active()) {
 
556
            if (m_prop.get_state() != IBus.PropState.CHECKED)
 
557
                do_emit = true;
 
558
            m_prop.set_state(IBus.PropState.CHECKED);
 
559
        } else {
 
560
            if (m_prop.get_state() != IBus.PropState.UNCHECKED)
 
561
                do_emit = true;
 
562
            m_prop.set_state(IBus.PropState.UNCHECKED);
 
563
        }
 
564
 
 
565
        if (do_emit)
 
566
            property_activate(m_prop.get_key(), m_prop.get_state());
 
567
    }
 
568
 
 
569
    public new void set_icon_name(string icon_name) {
 
570
        string label = m_prop.get_symbol().get_text();
 
571
        IconWidget icon_widget = null;
 
572
 
 
573
        if (label == "") {
 
574
            label = null;
 
575
            icon_widget = new IconWidget(icon_name, Gtk.IconSize.BUTTON);
 
576
            set_is_important(false);
 
577
        } else {
 
578
            set_is_important(true);
 
579
        }
 
580
 
 
581
        set_icon_widget(icon_widget);
 
582
    }
 
583
}
 
584
 
 
585
public class PropMenuToolButton : PropToggleToolButton, IPropToolItem {
 
586
    private PropMenu m_menu = null;
 
587
 
 
588
    public PropMenuToolButton(IBus.Property prop) {
 
589
        /* Chain up base class constructor */
 
590
        GLib.Object();
 
591
 
 
592
        m_menu = new PropMenu(prop);
 
593
        m_menu.deactivate.connect((m) =>
 
594
                                  set_active(false));
 
595
        m_menu.property_activate.connect((w, k, s) =>
 
596
                                         property_activate(k, s));
 
597
 
 
598
        base.set_property(prop);
 
599
    }
 
600
 
 
601
    public new void update_property(IBus.Property prop) {
 
602
        base.update_property(prop);
 
603
        m_menu.update_property(prop);
 
604
    }
 
605
 
 
606
    public override void toggled() {
 
607
        if (get_active())
 
608
            m_menu.popup(0, Gtk.get_current_event_time(), this);
 
609
    }
 
610
 
 
611
    public override void destroy() {
 
612
        m_menu = null;
 
613
        base.destroy();
 
614
    }
 
615
}
 
616
 
 
617
public class PropSeparatorToolItem : Gtk.SeparatorToolItem, IPropToolItem {
 
618
    public PropSeparatorToolItem(IBus.Property prop) {
 
619
        /* Chain up base class constructor */
 
620
        GLib.Object();
 
621
 
 
622
        set_homogeneous(false);
 
623
    }
 
624
 
 
625
    public void update_property(IBus.Property prop) {
 
626
    }
 
627
}