Blame ui/gtk3/propertypanel.vala

Packit Service 1d8f1c
/* vim:set et sts=4 sw=4:
Packit Service 1d8f1c
 *
Packit Service 1d8f1c
 * ibus - The Input Bus
Packit Service 1d8f1c
 *
Packit Service 1d8f1c
 * Copyright(c) 2013-2016 Red Hat, Inc.
Packit Service 1d8f1c
 * Copyright(c) 2013-2015 Peng Huang <shawn.p.huang@gmail.com>
Packit Service 1d8f1c
 * Copyright(c) 2013-2017 Takao Fujiwara <takao.fujiwara1@gmail.com>
Packit Service 1d8f1c
 *
Packit Service 1d8f1c
 * This library is free software; you can redistribute it and/or
Packit Service 1d8f1c
 * modify it under the terms of the GNU Lesser General Public
Packit Service 1d8f1c
 * License as published by the Free Software Foundation; either
Packit Service 1d8f1c
 * version 2.1 of the License, or (at your option) any later version.
Packit Service 1d8f1c
 *
Packit Service 1d8f1c
 * This library is distributed in the hope that it will be useful,
Packit Service 1d8f1c
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 1d8f1c
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit Service 1d8f1c
 * Lesser General Public License for more details.
Packit Service 1d8f1c
 *
Packit Service 1d8f1c
 * You should have received a copy of the GNU Lesser General Public
Packit Service 1d8f1c
 * License along with this library; if not, write to the Free Software
Packit Service 1d8f1c
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
Packit Service 1d8f1c
 * USA
Packit Service 1d8f1c
 */
Packit Service 1d8f1c
Packit Service 1d8f1c
enum PanelShow {
Packit Service 1d8f1c
    DO_NOT_SHOW,
Packit Service 1d8f1c
    AUTO_HIDE,
Packit Service 1d8f1c
    ALWAYS
Packit Service 1d8f1c
}
Packit Service 1d8f1c
Packit Service 1d8f1c
public class PropertyPanel : Gtk.Box {
Packit Service 1d8f1c
    private unowned Gdk.Window m_root_window;
Packit Service 1d8f1c
    private unowned X.Display m_xdisplay;
Packit Service 1d8f1c
    private Gtk.Window m_toplevel;
Packit Service 1d8f1c
    private IBus.PropList m_props;
Packit Service 1d8f1c
    private IPropToolItem[] m_items;
Packit Service 1d8f1c
    private Gdk.Rectangle m_cursor_location = Gdk.Rectangle(){
Packit Service 1d8f1c
            x = -1, y = -1, width = 0, height = 0 };
Packit Service 1d8f1c
    private int m_show = PanelShow.DO_NOT_SHOW;
Packit Service 1d8f1c
    private uint m_auto_hide_timeout = 10000;
Packit Service 1d8f1c
    private uint m_auto_hide_timeout_id = 0;
Packit Service 1d8f1c
    private bool m_follow_input_cursor_when_always_shown = false;
Packit Service 1d8f1c
    // The timeout indicates milliseconds. 1000 msec == 1 sec
Packit Service 1d8f1c
    private const uint MONITOR_NET_WORKAREA_TIMEOUT = 300000;
Packit Service 1d8f1c
    private uint m_remove_filter_id;
Packit Service 1d8f1c
Packit Service 1d8f1c
    public PropertyPanel() {
Packit Service 1d8f1c
        /* Chain up base class constructor */
Packit Service 1d8f1c
        GLib.Object(orientation: Gtk.Orientation.HORIZONTAL,
Packit Service 1d8f1c
                    spacing: 0);
Packit Service 1d8f1c
Packit Service 1d8f1c
        set_visible(true);
Packit Service 1d8f1c
Packit Service 1d8f1c
        m_root_window = Gdk.get_default_root_window();
Packit Service 1d8f1c
        unowned Gdk.Display display = m_root_window.get_display();
Packit Service 1d8f1c
#if VALA_0_24
Packit Service 1d8f1c
        m_xdisplay = (display as Gdk.X11.Display).get_xdisplay();
Packit Service 1d8f1c
#else
Packit Service 1d8f1c
        m_xdisplay = Gdk.X11Display.get_xdisplay(display);
Packit Service 1d8f1c
#endif
Packit Service 1d8f1c
Packit Service 1d8f1c
        m_toplevel = new Gtk.Window(Gtk.WindowType.POPUP);
Packit Service 1d8f1c
        m_toplevel.add_events(Gdk.EventMask.BUTTON_PRESS_MASK);
Packit Service 1d8f1c
Packit Service 1d8f1c
        Handle handle = new Handle();
Packit Service 1d8f1c
        handle.set_visible(true);
Packit Service 1d8f1c
        pack_start(handle, false, false, 0);
Packit Service 1d8f1c
Packit Service 1d8f1c
        m_toplevel.add(this);
Packit Service 1d8f1c
Packit Service 1d8f1c
        m_toplevel.size_allocate.connect((w, a) => {
Packit Service 1d8f1c
            if (!m_follow_input_cursor_when_always_shown &&
Packit Service 1d8f1c
                m_show == PanelShow.ALWAYS && m_items.length > 0 &&
Packit Service 1d8f1c
                m_cursor_location.x == -1 && m_cursor_location.y == -1) {
Packit Service 1d8f1c
                set_default_location();
Packit Service 1d8f1c
                m_cursor_location.x = 0;
Packit Service 1d8f1c
                m_cursor_location.y = 0;
Packit Service 1d8f1c
            }
Packit Service 1d8f1c
        });
Packit Service 1d8f1c
Packit Service 1d8f1c
        // PropertyPanel runs before KDE5 panel runs and
Packit Service 1d8f1c
        // monitor the desktop size.
Packit Service 1d8f1c
        monitor_net_workarea_atom();
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
Packit Service 1d8f1c
    public void set_properties(IBus.PropList props) {
Packit Service 1d8f1c
        debug("set_properties()\n");
Packit Service 1d8f1c
Packit Service 1d8f1c
        // When click PropMenuToolButton, the focus is changed and
Packit Service 1d8f1c
        // set_properties() is called here while the menu button is active.
Packit Service 1d8f1c
        // Ignore that case here not to remove items.
Packit Service 1d8f1c
        bool has_active = false;
Packit Service 1d8f1c
        foreach (var item in m_items) {
Packit Service 1d8f1c
            Type type = item.get_type();
Packit Service 1d8f1c
            if (type == typeof(PropMenuToolButton) ||
Packit Service 1d8f1c
                type == typeof(PropToggleToolButton)) {
Packit Service 1d8f1c
                if ((item as Gtk.ToggleToolButton).get_active()) {
Packit Service 1d8f1c
                    has_active = true;
Packit Service 1d8f1c
                    break;
Packit Service 1d8f1c
                }
Packit Service 1d8f1c
            }
Packit Service 1d8f1c
        }
Packit Service 1d8f1c
        if (has_active)
Packit Service 1d8f1c
            return;
Packit Service 1d8f1c
Packit Service 1d8f1c
        foreach (var item in m_items)
Packit Service 1d8f1c
            remove((item as Gtk.Widget));
Packit Service 1d8f1c
        m_items = {};
Packit Service 1d8f1c
Packit Service 1d8f1c
        m_props = props;
Packit Service 1d8f1c
Packit Service 1d8f1c
        create_menu_items();
Packit Service 1d8f1c
Packit Service 1d8f1c
        /* show_with_auto_hide_timer() needs to call here because
Packit Service 1d8f1c
         * if the event order is, focus_in(), set_cursor_location() and
Packit Service 1d8f1c
         * set_properties(), m_items.length can be 0 when
Packit Service 1d8f1c
         * set_cursor_location() is called and Property Panel is not shown.
Packit Service 1d8f1c
         */
Packit Service 1d8f1c
        show_with_auto_hide_timer();
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
Packit Service 1d8f1c
    public void update_property(IBus.Property prop) {
Packit Service 1d8f1c
        GLib.assert(prop != null);
Packit Service 1d8f1c
Packit Service 1d8f1c
        debug("update_property(prop.key = %s)\n", prop.get_key());
Packit Service 1d8f1c
Packit Service 1d8f1c
        if (m_props != null)
Packit Service 1d8f1c
            m_props.update_property(prop);
Packit Service 1d8f1c
Packit Service 1d8f1c
        /* Need to update GUI since panel buttons are not redrawn. */
Packit Service 1d8f1c
        foreach (var item in m_items)
Packit Service 1d8f1c
            item.update_property(prop);
Packit Service 1d8f1c
Packit Service 1d8f1c
        show_with_auto_hide_timer();
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
Packit Service 1d8f1c
    public void set_cursor_location(int x, int y, int width, int height) {
Packit Service 1d8f1c
        if (!m_follow_input_cursor_when_always_shown &&
Packit Service 1d8f1c
            m_show == PanelShow.ALWAYS)
Packit Service 1d8f1c
            return;
Packit Service 1d8f1c
Packit Service 1d8f1c
        /* FIXME: set_cursor_location() has a different behavior
Packit Service 1d8f1c
         * in embedded preedit by applications.
Packit Service 1d8f1c
         * GtkTextView applications, e.g. gedit, always call
Packit Service 1d8f1c
         * set_cursor_location() with and without preedit
Packit Service 1d8f1c
         * but VTE applications, e.g. gnome-terminal, and xterm
Packit Service 1d8f1c
         * do not call set_cursor_location() with preedit.
Packit Service 1d8f1c
         * firefox and thunderbird do not call set_cursor_location()
Packit Service 1d8f1c
         * without preedit.
Packit Service 1d8f1c
         * This may treat GtkIMContext and XIM with different ways.
Packit Service 1d8f1c
         * Maybe get_preedit_string() class method.
Packit Service 1d8f1c
         */
Packit Service 1d8f1c
Packit Service 1d8f1c
        /* FIXME: When the cursor is at the bottom of the screen,
Packit Service 1d8f1c
         * gedit returns the right cursor position but terminal applications
Packit Service 1d8f1c
         * such as gnome-terminal, xfce4-terminal and etc, the position is
Packit Service 1d8f1c
         * not accurate and the cursor and panel could be overlapped slightly.
Packit Service 1d8f1c
         * Maybe it's a bug in vte.
Packit Service 1d8f1c
         */
Packit Service 1d8f1c
        Gdk.Rectangle location = Gdk.Rectangle(){
Packit Service 1d8f1c
            x = x, y = y, width = width, height = height };
Packit Service 1d8f1c
Packit Service 1d8f1c
        if (m_cursor_location == location)
Packit Service 1d8f1c
            return;
Packit Service 1d8f1c
Packit Service 1d8f1c
        debug("set_cursor_location(x = %d, y = %d, width = %d, height = %d)\n",
Packit Service 1d8f1c
              x, y, width, height);
Packit Service 1d8f1c
Packit Service 1d8f1c
        /* Hide the panel in AUTO_HIDE mode when the cursor position is
Packit Service 1d8f1c
         * chagned on the same input context by typing keyboard or
Packit Service 1d8f1c
         * clicking mouse. (But not focus change or property change)
Packit Service 1d8f1c
         */
Packit Service 1d8f1c
        if (m_show == PanelShow.AUTO_HIDE)
Packit Service 1d8f1c
            if (m_cursor_location.x != -1 || m_cursor_location.y != -1) {
Packit Service 1d8f1c
                m_cursor_location = location;
Packit Service 1d8f1c
                hide_if_necessary();
Packit Service 1d8f1c
                adjust_window_position();
Packit Service 1d8f1c
                return;
Packit Service 1d8f1c
            }
Packit Service 1d8f1c
Packit Service 1d8f1c
        m_cursor_location = location;
Packit Service 1d8f1c
        adjust_window_position();
Packit Service 1d8f1c
        show_with_auto_hide_timer();
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
Packit Service 1d8f1c
    public void set_preedit_text(IBus.Text? text, uint cursor) {
Packit Service 1d8f1c
        if (text == null && cursor == 0)
Packit Service 1d8f1c
            return;
Packit Service 1d8f1c
Packit Service 1d8f1c
        debug("set_preedit_text(text, cursor = %u)\n", cursor);
Packit Service 1d8f1c
Packit Service 1d8f1c
        /* Hide the panel in AUTO_HIDE mode when embed-preedit-text value
Packit Service 1d8f1c
         * is disabled and the preedit is changed on the same input context.
Packit Service 1d8f1c
         */
Packit Service 1d8f1c
        hide_if_necessary();
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
Packit Service 1d8f1c
    public void set_auxiliary_text(IBus.Text? text) {
Packit Service 1d8f1c
        if (text == null)
Packit Service 1d8f1c
            return;
Packit Service 1d8f1c
Packit Service 1d8f1c
        debug("set_auxiliary_text(text)\n");
Packit Service 1d8f1c
Packit Service 1d8f1c
        hide_if_necessary();
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
Packit Service 1d8f1c
    public void set_lookup_table(IBus.LookupTable? table) {
Packit Service 1d8f1c
        if (table == null)
Packit Service 1d8f1c
            return;
Packit Service 1d8f1c
Packit Service 1d8f1c
        debug("set_lookup_table(table)\n");
Packit Service 1d8f1c
Packit Service 1d8f1c
        hide_if_necessary();
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
Packit Service 1d8f1c
    public new void show() {
Packit Service 1d8f1c
        /* m_items.length is not checked here because set_properties()
Packit Service 1d8f1c
         * is not called yet when set_show() is called. */
Packit Service 1d8f1c
        if (m_show == PanelShow.DO_NOT_SHOW) {
Packit Service 1d8f1c
            m_toplevel.hide();
Packit Service 1d8f1c
            return;
Packit Service 1d8f1c
        }
Packit Service 1d8f1c
        else if (m_show == PanelShow.ALWAYS) {
Packit Service 1d8f1c
            m_toplevel.show_all();
Packit Service 1d8f1c
            return;
Packit Service 1d8f1c
        }
Packit Service 1d8f1c
Packit Service 1d8f1c
        /* Do not change the state here if m_show == AUTO_HIDE. */
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
Packit Service 1d8f1c
    public new void hide() {
Packit Service 1d8f1c
        m_toplevel.hide();
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
Packit Service 1d8f1c
    public void focus_in() {
Packit Service 1d8f1c
        debug("focus_in()\n");
Packit Service 1d8f1c
Packit Service 1d8f1c
        /* Reset m_auto_hide_timeout_id in previous focus-in */
Packit Service 1d8f1c
        hide_if_necessary();
Packit Service 1d8f1c
Packit Service 1d8f1c
        /* Invalidate m_cursor_location before set_cursor_location()
Packit Service 1d8f1c
         * is called because the position can be same even if the input
Packit Service 1d8f1c
         * focus is changed.
Packit Service 1d8f1c
         * E.g. Two tabs on gnome-terminal can keep the cursor position.
Packit Service 1d8f1c
         */
Packit Service 1d8f1c
        if (m_follow_input_cursor_when_always_shown ||
Packit Service 1d8f1c
            m_show != PanelShow.ALWAYS)
Packit Service 1d8f1c
            m_cursor_location = { -1, -1, 0, 0 };
Packit Service 1d8f1c
Packit Service 1d8f1c
       /* set_cursor_location() will be called later. */
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
Packit Service 1d8f1c
    public void set_show(int _show) {
Packit Service 1d8f1c
        m_show = _show;
Packit Service 1d8f1c
        show();
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
Packit Service 1d8f1c
    public void set_auto_hide_timeout(uint timeout) {
Packit Service 1d8f1c
        m_auto_hide_timeout = timeout;
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
Packit Service 1d8f1c
    public void set_follow_input_cursor_when_always_shown(bool is_follow) {
Packit Service 1d8f1c
        m_follow_input_cursor_when_always_shown = is_follow;
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
Packit Service 1d8f1c
    private void create_menu_items() {
Packit Service 1d8f1c
        int i = 0;
Packit Service 1d8f1c
        while (true) {
Packit Service 1d8f1c
            IBus.Property prop = m_props.get(i);
Packit Service 1d8f1c
            if (prop == null)
Packit Service 1d8f1c
                break;
Packit Service 1d8f1c
Packit Service 1d8f1c
            i++;
Packit Service 1d8f1c
            IPropToolItem item = null;
Packit Service 1d8f1c
            switch(prop.get_prop_type()) {
Packit Service 1d8f1c
                case IBus.PropType.NORMAL:
Packit Service 1d8f1c
                    item = new PropToolButton(prop);
Packit Service 1d8f1c
                    break;
Packit Service 1d8f1c
                case IBus.PropType.TOGGLE:
Packit Service 1d8f1c
                    item = new PropToggleToolButton(prop);
Packit Service 1d8f1c
                    break;
Packit Service 1d8f1c
                case IBus.PropType.MENU:
Packit Service 1d8f1c
                    item = new PropMenuToolButton(prop);
Packit Service 1d8f1c
                    break;
Packit Service 1d8f1c
                case IBus.PropType.SEPARATOR:
Packit Service 1d8f1c
                    item = new PropSeparatorToolItem(prop);
Packit Service 1d8f1c
                    break;
Packit Service 1d8f1c
                default:
Packit Service 1d8f1c
                    warning("unknown property type %d",
Packit Service 1d8f1c
                            (int) prop.get_prop_type());
Packit Service 1d8f1c
                    break;
Packit Service 1d8f1c
            }
Packit Service 1d8f1c
            if (item != null) {
Packit Service 1d8f1c
                pack_start(item as Gtk.Widget, false, false, 0);
Packit Service 1d8f1c
                m_items += item;
Packit Service 1d8f1c
                item.property_activate.connect((w, k, s) =>
Packit Service 1d8f1c
                                               property_activate(k, s));
Packit Service 1d8f1c
            }
Packit Service 1d8f1c
        }
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
Packit Service 1d8f1c
    private void move(int x, int y) {
Packit Service 1d8f1c
        m_toplevel.move(x, y);
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
Packit Service 1d8f1c
    private void adjust_window_position() {
Packit Service 1d8f1c
        Gdk.Point cursor_right_bottom = {
Packit Service 1d8f1c
                m_cursor_location.x + m_cursor_location.width,
Packit Service 1d8f1c
                m_cursor_location.y + m_cursor_location.height
Packit Service 1d8f1c
        };
Packit Service 1d8f1c
Packit Service 1d8f1c
        Gtk.Allocation allocation;
Packit Service 1d8f1c
        m_toplevel.get_allocation(out allocation);
Packit Service 1d8f1c
        Gdk.Point window_right_bottom = {
Packit Service 1d8f1c
            cursor_right_bottom.x + allocation.width,
Packit Service 1d8f1c
            cursor_right_bottom.y + allocation.height
Packit Service 1d8f1c
        };
Packit Service 1d8f1c
Packit Service 1d8f1c
        int root_width = m_root_window.get_width();
Packit Service 1d8f1c
        int root_height = m_root_window.get_height();
Packit Service 1d8f1c
Packit Service 1d8f1c
        int x, y;
Packit Service 1d8f1c
        if (window_right_bottom.x > root_width)
Packit Service 1d8f1c
            x = root_width - allocation.width;
Packit Service 1d8f1c
        else
Packit Service 1d8f1c
            x = cursor_right_bottom.x;
Packit Service 1d8f1c
Packit Service 1d8f1c
        if (window_right_bottom.y > root_height)
Packit Service 1d8f1c
            y = m_cursor_location.y - allocation.height;
Packit Service 1d8f1c
        else
Packit Service 1d8f1c
            y = cursor_right_bottom.y;
Packit Service 1d8f1c
Packit Service 1d8f1c
        move(x, y);
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
Packit Service 1d8f1c
    private bool is_bottom_panel() {
Packit Service 1d8f1c
        string desktop = Environment.get_variable("XDG_CURRENT_DESKTOP");
Packit Service 1d8f1c
        // LXDE has not implemented DesktopNames yet.
Packit Service 1d8f1c
        if (desktop == null)
Packit Service 1d8f1c
            desktop = Environment.get_variable("XDG_SESSION_DESKTOP");
Packit Service 1d8f1c
        switch (desktop) {
Packit Service 1d8f1c
            case "KDE":         return true;
Packit Service 1d8f1c
            case "LXDE":        return true;
Packit Service 1d8f1c
            default:            return false;
Packit Service 1d8f1c
        }
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
Packit Service 1d8f1c
    private void set_default_location() {
Packit Service 1d8f1c
        Gtk.Allocation allocation;
Packit Service 1d8f1c
        m_toplevel.get_allocation(out allocation);
Packit Service 1d8f1c
Packit Service 1d8f1c
        Gdk.Rectangle monitor_area;
Packit Service 1d8f1c
#if VALA_0_34
Packit Service 1d8f1c
        // gdk_screen_get_monitor_workarea() no longer return the correct
Packit Service 1d8f1c
        // area from "_NET_WORKAREA" atom in GTK 3.22
Packit Service 1d8f1c
        Gdk.Monitor monitor = Gdk.Display.get_default().get_monitor(0);
Packit Service 1d8f1c
        monitor_area = monitor.get_workarea();
Packit Service 1d8f1c
#else
Packit Service 1d8f1c
        Gdk.Screen screen = Gdk.Screen.get_default();
Packit Service 1d8f1c
        monitor_area = screen.get_monitor_workarea(0);
Packit Service 1d8f1c
#endif
Packit Service 1d8f1c
        int monitor_right = monitor_area.x + monitor_area.width;
Packit Service 1d8f1c
        int monitor_bottom = monitor_area.y + monitor_area.height;
Packit Service 1d8f1c
        int x, y;
Packit Service 1d8f1c
        if (is_bottom_panel()) {
Packit Service 1d8f1c
            /* Translators: If your locale is RTL, the msgstr is "default:RTL".
Packit Service 1d8f1c
             * Otherwise the msgstr is "default:LTR". */
Packit Service 1d8f1c
            if (_("default:LTR") != "default:RTL") {
Packit Service 1d8f1c
                x = monitor_right - allocation.width;
Packit Service 1d8f1c
                y = monitor_bottom - allocation.height;
Packit Service 1d8f1c
            } else {
Packit Service 1d8f1c
                x = monitor_area.x;
Packit Service 1d8f1c
                y = monitor_bottom - allocation.height;
Packit Service 1d8f1c
            }
Packit Service 1d8f1c
        } else {
Packit Service 1d8f1c
            if (_("default:LTR") != "default:RTL") {
Packit Service 1d8f1c
                x = monitor_right - allocation.width;
Packit Service 1d8f1c
                y = monitor_area.y;
Packit Service 1d8f1c
            } else {
Packit Service 1d8f1c
                x = monitor_area.x;
Packit Service 1d8f1c
                y = monitor_area.y;
Packit Service 1d8f1c
            }
Packit Service 1d8f1c
        }
Packit Service 1d8f1c
Packit Service 1d8f1c
        move(x, y);
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
Packit Service 1d8f1c
    private Gdk.FilterReturn root_window_filter(Gdk.XEvent gdkxevent,
Packit Service 1d8f1c
                                                Gdk.Event  event) {
Packit Service 1d8f1c
        X.Event *xevent = (X.Event*) gdkxevent;
Packit Service 1d8f1c
        if (xevent.type == X.EventType.PropertyNotify) {
Packit Service 1d8f1c
            string aname = m_xdisplay.get_atom_name(xevent.xproperty.atom);
Packit Service 1d8f1c
            if (aname == "_NET_WORKAREA" && xevent.xproperty.state == 0) {
Packit Service 1d8f1c
                set_default_location();
Packit Service 1d8f1c
                m_root_window.remove_filter(root_window_filter);
Packit Service 1d8f1c
                if (m_remove_filter_id > 0) {
Packit Service 1d8f1c
                    GLib.Source.remove(m_remove_filter_id);
Packit Service 1d8f1c
                    m_remove_filter_id = 0;
Packit Service 1d8f1c
                }
Packit Service 1d8f1c
                return Gdk.FilterReturn.CONTINUE;
Packit Service 1d8f1c
            }
Packit Service 1d8f1c
        }
Packit Service 1d8f1c
        return Gdk.FilterReturn.CONTINUE;
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
Packit Service 1d8f1c
    private void monitor_net_workarea_atom() {
Packit Service 1d8f1c
        Gdk.EventMask events = m_root_window.get_events();
Packit Service 1d8f1c
        if ((events & Gdk.EventMask.PROPERTY_CHANGE_MASK) == 0)
Packit Service 1d8f1c
            m_root_window.set_events (events |
Packit Service 1d8f1c
                                      Gdk.EventMask.PROPERTY_CHANGE_MASK);
Packit Service 1d8f1c
Packit Service 1d8f1c
        m_root_window.add_filter(root_window_filter);
Packit Service 1d8f1c
Packit Service 1d8f1c
        m_remove_filter_id = GLib.Timeout.add(MONITOR_NET_WORKAREA_TIMEOUT,
Packit Service 1d8f1c
                                              () => {
Packit Service 1d8f1c
            m_remove_filter_id = 0;
Packit Service 1d8f1c
            m_root_window.remove_filter(root_window_filter);
Packit Service 1d8f1c
            return false;
Packit Service 1d8f1c
        },
Packit Service 1d8f1c
        GLib.Priority.DEFAULT_IDLE);
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
Packit Service 1d8f1c
    private void show_with_auto_hide_timer() {
Packit Service 1d8f1c
        /* Do not call gtk_window_resize() in
Packit Service 1d8f1c
         * GtkWidgetClass->get_preferred_width()
Packit Service 1d8f1c
         * because the following warning is shown in GTK 3.20:
Packit Service 1d8f1c
         * "Allocating size to GtkWindow %x without calling
Packit Service 1d8f1c
         * gtk_widget_get_preferred_width/height(). How does the code
Packit Service 1d8f1c
         * know the size to allocate?"
Packit Service 1d8f1c
         * in gtk_widget_size_allocate_with_baseline() */
Packit Service 1d8f1c
        m_toplevel.resize(1, 1);
Packit Service 1d8f1c
Packit Service 1d8f1c
        if (m_items.length == 0) {
Packit Service 1d8f1c
            /* Do not blink the panel with focus-in in case the panel
Packit Service 1d8f1c
             * is always shown. */
Packit Service 1d8f1c
            if (m_follow_input_cursor_when_always_shown ||
Packit Service 1d8f1c
                m_show != PanelShow.ALWAYS)
Packit Service 1d8f1c
                m_toplevel.hide();
Packit Service 1d8f1c
Packit Service 1d8f1c
            return;
Packit Service 1d8f1c
        }
Packit Service 1d8f1c
Packit Service 1d8f1c
        if (m_show != PanelShow.AUTO_HIDE) {
Packit Service 1d8f1c
            show();
Packit Service 1d8f1c
            return;
Packit Service 1d8f1c
        }
Packit Service 1d8f1c
Packit Service 1d8f1c
        /* If all windows are closed, desktop background is focused and
Packit Service 1d8f1c
         * focus_in() and set_properties() are called but
Packit Service 1d8f1c
         * set_cursor_location() is not called.
Packit Service 1d8f1c
         * Then we should not show Property panel when m_cursor_location
Packit Service 1d8f1c
         * is (-1, -1) because of no windows.
Packit Service 1d8f1c
         */
Packit Service 1d8f1c
        if (m_cursor_location.x == -1 && m_cursor_location.y == -1)
Packit Service 1d8f1c
            return;
Packit Service 1d8f1c
Packit Service 1d8f1c
        if (m_auto_hide_timeout_id != 0)
Packit Service 1d8f1c
            GLib.Source.remove(m_auto_hide_timeout_id);
Packit Service 1d8f1c
Packit Service 1d8f1c
        m_toplevel.show_all();
Packit Service 1d8f1c
Packit Service 1d8f1c
        /* Change the priority because IME typing sometimes freezes. */
Packit Service 1d8f1c
        m_auto_hide_timeout_id = GLib.Timeout.add(m_auto_hide_timeout, () => {
Packit Service 1d8f1c
            m_toplevel.hide();
Packit Service 1d8f1c
            m_auto_hide_timeout_id = 0;
Packit Service 1d8f1c
            return false;
Packit Service 1d8f1c
        },
Packit Service 1d8f1c
        GLib.Priority.DEFAULT_IDLE);
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
Packit Service 1d8f1c
    private void hide_if_necessary() {
Packit Service 1d8f1c
        if (m_show == PanelShow.AUTO_HIDE && m_auto_hide_timeout_id != 0) {
Packit Service 1d8f1c
            GLib.Source.remove(m_auto_hide_timeout_id);
Packit Service 1d8f1c
            m_auto_hide_timeout_id = 0;
Packit Service 1d8f1c
            m_toplevel.hide();
Packit Service 1d8f1c
        }
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
Packit Service 1d8f1c
    public signal void property_activate(string key, int state);
Packit Service 1d8f1c
}
Packit Service 1d8f1c
Packit Service 1d8f1c
public interface IPropToolItem : GLib.Object {
Packit Service 1d8f1c
    public abstract void update_property(IBus.Property prop);
Packit Service 1d8f1c
    public signal void property_activate(string key, int state);
Packit Service 1d8f1c
}
Packit Service 1d8f1c
Packit Service 1d8f1c
public class PropMenu : Gtk.Menu, IPropToolItem {
Packit Service 1d8f1c
    private Gtk.Widget m_parent_button;
Packit Service 1d8f1c
    private IPropItem[] m_items;
Packit Service 1d8f1c
Packit Service 1d8f1c
    public PropMenu(IBus.Property prop) {
Packit Service 1d8f1c
        /* Chain up base class constructor */
Packit Service 1d8f1c
        GLib.Object();
Packit Service 1d8f1c
Packit Service 1d8f1c
        set_take_focus(false);
Packit Service 1d8f1c
        create_items(prop.get_sub_props());
Packit Service 1d8f1c
        show_all();
Packit Service 1d8f1c
        set_sensitive(prop.get_sensitive());
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
Packit Service 1d8f1c
    public void update_property(IBus.Property prop) {
Packit Service 1d8f1c
        foreach (var item in m_items)
Packit Service 1d8f1c
            item.update_property(prop);
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
Packit Service 1d8f1c
    public new void popup(uint       button,
Packit Service 1d8f1c
                          uint32     activate_time,
Packit Service 1d8f1c
                          Gtk.Widget widget) {
Packit Service 1d8f1c
#if VALA_0_34
Packit Service 1d8f1c
        base.popup_at_widget(widget,
Packit Service 1d8f1c
                             Gdk.Gravity.SOUTH_WEST,
Packit Service 1d8f1c
                             Gdk.Gravity.NORTH_WEST,
Packit Service 1d8f1c
                             null);
Packit Service 1d8f1c
#else
Packit Service 1d8f1c
        m_parent_button = widget;
Packit Service 1d8f1c
        base.popup(null, null, menu_position, button, activate_time);
Packit Service 1d8f1c
#endif
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
Packit Service 1d8f1c
    public override void destroy() {
Packit Service 1d8f1c
        m_parent_button = null;
Packit Service 1d8f1c
        foreach (var item in m_items)
Packit Service 1d8f1c
            remove((item as Gtk.Widget));
Packit Service 1d8f1c
        m_items = {};
Packit Service 1d8f1c
        base.destroy();
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
Packit Service 1d8f1c
    private void create_items(IBus.PropList props) {
Packit Service 1d8f1c
        int i = 0;
Packit Service 1d8f1c
        PropRadioMenuItem last_radio = null;
Packit Service 1d8f1c
Packit Service 1d8f1c
        while (true) {
Packit Service 1d8f1c
            IBus.Property prop = props.get(i);
Packit Service 1d8f1c
            if (prop == null)
Packit Service 1d8f1c
                break;
Packit Service 1d8f1c
Packit Service 1d8f1c
            i++;
Packit Service 1d8f1c
            IPropItem item = null;
Packit Service 1d8f1c
            switch(prop.get_prop_type()) {
Packit Service 1d8f1c
                case IBus.PropType.NORMAL:
Packit Service 1d8f1c
                    item = new PropImageMenuItem(prop);
Packit Service 1d8f1c
                    break;
Packit Service 1d8f1c
                case IBus.PropType.TOGGLE:
Packit Service 1d8f1c
                    item = new PropCheckMenuItem(prop);
Packit Service 1d8f1c
                    break;
Packit Service 1d8f1c
                case IBus.PropType.RADIO:
Packit Service 1d8f1c
                    last_radio = new PropRadioMenuItem(prop, last_radio);
Packit Service 1d8f1c
                    item = last_radio;
Packit Service 1d8f1c
                    break;
Packit Service 1d8f1c
                case IBus.PropType.MENU:
Packit Service 1d8f1c
                    {
Packit Service 1d8f1c
                        var menuitem = new PropImageMenuItem(prop);
Packit Service 1d8f1c
                        menuitem.set_submenu(new PropMenu(prop));
Packit Service 1d8f1c
                        item = menuitem;
Packit Service 1d8f1c
                    }
Packit Service 1d8f1c
                    break;
Packit Service 1d8f1c
                case IBus.PropType.SEPARATOR:
Packit Service 1d8f1c
                    item = new PropSeparatorMenuItem(prop);
Packit Service 1d8f1c
                    break;
Packit Service 1d8f1c
                default:
Packit Service 1d8f1c
                    warning("Unknown property type: %d",
Packit Service 1d8f1c
                            (int) prop.get_prop_type());
Packit Service 1d8f1c
                    break;
Packit Service 1d8f1c
            }
Packit Service 1d8f1c
            if (prop.get_prop_type() != IBus.PropType.RADIO)
Packit Service 1d8f1c
                last_radio = null;
Packit Service 1d8f1c
            if (item != null) {
Packit Service 1d8f1c
                append(item as Gtk.MenuItem);
Packit Service 1d8f1c
                item.property_activate.connect((w, k, s) =>
Packit Service 1d8f1c
                                               property_activate(k, s));
Packit Service 1d8f1c
                m_items += item;
Packit Service 1d8f1c
            }
Packit Service 1d8f1c
        }
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
Packit Service 1d8f1c
#if !VALA_0_34
Packit Service 1d8f1c
    private void menu_position(Gtk.Menu menu,
Packit Service 1d8f1c
                               out int  x,
Packit Service 1d8f1c
                               out int  y,
Packit Service 1d8f1c
                               out bool push_in) {
Packit Service 1d8f1c
        var button = m_parent_button;
Packit Service 1d8f1c
        var screen = button.get_screen();
Packit Service 1d8f1c
        var monitor = screen.get_monitor_at_window(button.get_window());
Packit Service 1d8f1c
Packit Service 1d8f1c
        Gdk.Rectangle monitor_location;
Packit Service 1d8f1c
        screen.get_monitor_geometry(monitor, out monitor_location);
Packit Service 1d8f1c
Packit Service 1d8f1c
        button.get_window().get_origin(out x, out y);
Packit Service 1d8f1c
Packit Service 1d8f1c
        Gtk.Allocation button_allocation;
Packit Service 1d8f1c
        button.get_allocation(out button_allocation);
Packit Service 1d8f1c
Packit Service 1d8f1c
        x += button_allocation.x;
Packit Service 1d8f1c
        y += button_allocation.y;
Packit Service 1d8f1c
Packit Service 1d8f1c
        int menu_width;
Packit Service 1d8f1c
        int menu_height;
Packit Service 1d8f1c
        menu.get_size_request(out menu_width, out menu_height);
Packit Service 1d8f1c
Packit Service 1d8f1c
        if (x + menu_width >= monitor_location.width)
Packit Service 1d8f1c
            x -= menu_width - button_allocation.width;
Packit Service 1d8f1c
        else if (x - menu_width <= 0)
Packit Service 1d8f1c
            ;
Packit Service 1d8f1c
        else {
Packit Service 1d8f1c
            if (x <= monitor_location.width * 3 / 4)
Packit Service 1d8f1c
                ;
Packit Service 1d8f1c
            else
Packit Service 1d8f1c
                x -= menu_width - button_allocation.width;
Packit Service 1d8f1c
        }
Packit Service 1d8f1c
Packit Service 1d8f1c
        if (y + button_allocation.height + menu_width
Packit Service 1d8f1c
                >= monitor_location.height)
Packit Service 1d8f1c
            y -= menu_height;
Packit Service 1d8f1c
        else if (y - menu_height <= 0)
Packit Service 1d8f1c
            y += button_allocation.height;
Packit Service 1d8f1c
        else {
Packit Service 1d8f1c
            if (y <= monitor_location.height * 3 / 4)
Packit Service 1d8f1c
                y += button_allocation.height;
Packit Service 1d8f1c
            else
Packit Service 1d8f1c
                y -= menu_height;
Packit Service 1d8f1c
        }
Packit Service 1d8f1c
Packit Service 1d8f1c
        push_in = false;
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
#endif
Packit Service 1d8f1c
}
Packit Service 1d8f1c
Packit Service 1d8f1c
public class PropToolButton : Gtk.ToolButton, IPropToolItem {
Packit Service 1d8f1c
    private IBus.Property m_prop = null;
Packit Service 1d8f1c
Packit Service 1d8f1c
    public PropToolButton(IBus.Property prop) {
Packit Service 1d8f1c
        /* Chain up base class constructor
Packit Service 1d8f1c
         *
Packit Service 1d8f1c
         * If the constructor sets "label" property, "halign" property
Packit Service 1d8f1c
         * does not work in KDE5 so use sync() for the label.
Packit Service 1d8f1c
         */
Packit Service 1d8f1c
        GLib.Object(halign: Gtk.Align.START);
Packit Service 1d8f1c
Packit Service 1d8f1c
        m_prop = prop;
Packit Service 1d8f1c
Packit Service 1d8f1c
        set_homogeneous(false);
Packit Service 1d8f1c
        sync();
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
Packit Service 1d8f1c
    public void update_property(IBus.Property prop) {
Packit Service 1d8f1c
        if (m_prop.get_key() != prop.get_key())
Packit Service 1d8f1c
            return;
Packit Service 1d8f1c
        m_prop.set_symbol(prop.get_symbol());
Packit Service 1d8f1c
        m_prop.set_tooltip(prop.get_tooltip());
Packit Service 1d8f1c
        m_prop.set_sensitive(prop.get_sensitive());
Packit Service 1d8f1c
        m_prop.set_icon(prop.get_icon());
Packit Service 1d8f1c
        m_prop.set_state(prop.get_state());
Packit Service 1d8f1c
        m_prop.set_visible(prop.get_visible());
Packit Service 1d8f1c
        sync();
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
Packit Service 1d8f1c
    private void sync() {
Packit Service 1d8f1c
        set_label(m_prop.get_symbol().get_text());
Packit Service 1d8f1c
        set_tooltip_text(m_prop.get_tooltip().get_text());
Packit Service 1d8f1c
        set_sensitive(m_prop.get_sensitive());
Packit Service 1d8f1c
        set_icon_name(m_prop.get_icon());
Packit Service 1d8f1c
Packit Service 1d8f1c
        if (m_prop.get_visible())
Packit Service 1d8f1c
            show();
Packit Service 1d8f1c
        else
Packit Service 1d8f1c
            hide();
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
Packit Service 1d8f1c
    public override void clicked() {
Packit Service 1d8f1c
        property_activate(m_prop.get_key(), m_prop.get_state());
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
Packit Service 1d8f1c
    public new void set_icon_name(string icon_name) {
Packit Service 1d8f1c
        string label = m_prop.get_symbol().get_text();
Packit Service 1d8f1c
        IconWidget icon_widget = null;
Packit Service 1d8f1c
Packit Service 1d8f1c
        if (label == "") {
Packit Service 1d8f1c
            label = null;
Packit Service 1d8f1c
            icon_widget = new IconWidget(icon_name, Gtk.IconSize.BUTTON);
Packit Service 1d8f1c
            set_is_important(false);
Packit Service 1d8f1c
        } else {
Packit Service 1d8f1c
            set_is_important(true);
Packit Service 1d8f1c
        }
Packit Service 1d8f1c
Packit Service 1d8f1c
        set_icon_widget(icon_widget);
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
}
Packit Service 1d8f1c
Packit Service 1d8f1c
public class PropToggleToolButton : Gtk.ToggleToolButton, IPropToolItem {
Packit Service 1d8f1c
    private IBus.Property m_prop = null;
Packit Service 1d8f1c
Packit Service 1d8f1c
    public PropToggleToolButton(IBus.Property prop) {
Packit Service 1d8f1c
        /* Chain up base class constructor
Packit Service 1d8f1c
         *
Packit Service 1d8f1c
         * Need to set halign for KDE5
Packit Service 1d8f1c
         */
Packit Service 1d8f1c
        GLib.Object(halign: Gtk.Align.START);
Packit Service 1d8f1c
Packit Service 1d8f1c
        m_prop = prop;
Packit Service 1d8f1c
Packit Service 1d8f1c
        set_homogeneous(false);
Packit Service 1d8f1c
        sync();
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
Packit Service 1d8f1c
    public new void set_property(IBus.Property prop) {
Packit Service 1d8f1c
        m_prop = prop;
Packit Service 1d8f1c
        sync();
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
Packit Service 1d8f1c
    public void update_property(IBus.Property prop) {
Packit Service 1d8f1c
        if (m_prop.get_key() != prop.get_key())
Packit Service 1d8f1c
            return;
Packit Service 1d8f1c
        m_prop.set_symbol(prop.get_symbol());
Packit Service 1d8f1c
        m_prop.set_tooltip(prop.get_tooltip());
Packit Service 1d8f1c
        m_prop.set_sensitive(prop.get_sensitive());
Packit Service 1d8f1c
        m_prop.set_icon(prop.get_icon());
Packit Service 1d8f1c
        m_prop.set_state(prop.get_state());
Packit Service 1d8f1c
        m_prop.set_visible(prop.get_visible());
Packit Service 1d8f1c
        sync();
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
Packit Service 1d8f1c
    private void sync() {
Packit Service 1d8f1c
        set_label(m_prop.get_symbol().get_text());
Packit Service 1d8f1c
        set_tooltip_text(m_prop.get_tooltip().get_text());
Packit Service 1d8f1c
        set_sensitive(m_prop.get_sensitive());
Packit Service 1d8f1c
        set_icon_name(m_prop.get_icon());
Packit Service 1d8f1c
        set_active(m_prop.get_state() == IBus.PropState.CHECKED);
Packit Service 1d8f1c
Packit Service 1d8f1c
        if (m_prop.get_visible())
Packit Service 1d8f1c
            show();
Packit Service 1d8f1c
        else
Packit Service 1d8f1c
            hide();
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
Packit Service 1d8f1c
    public override void toggled() {
Packit Service 1d8f1c
        /* Do not send property-activate to engine in case the event is
Packit Service 1d8f1c
         * sent from engine. */
Packit Service 1d8f1c
Packit Service 1d8f1c
        bool do_emit = false;
Packit Service 1d8f1c
Packit Service 1d8f1c
        if (get_active()) {
Packit Service 1d8f1c
            if (m_prop.get_state() != IBus.PropState.CHECKED)
Packit Service 1d8f1c
                do_emit = true;
Packit Service 1d8f1c
            m_prop.set_state(IBus.PropState.CHECKED);
Packit Service 1d8f1c
        } else {
Packit Service 1d8f1c
            if (m_prop.get_state() != IBus.PropState.UNCHECKED)
Packit Service 1d8f1c
                do_emit = true;
Packit Service 1d8f1c
            m_prop.set_state(IBus.PropState.UNCHECKED);
Packit Service 1d8f1c
        }
Packit Service 1d8f1c
Packit Service 1d8f1c
        if (do_emit)
Packit Service 1d8f1c
            property_activate(m_prop.get_key(), m_prop.get_state());
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
Packit Service 1d8f1c
    public new void set_icon_name(string icon_name) {
Packit Service 1d8f1c
        string label = m_prop.get_symbol().get_text();
Packit Service 1d8f1c
        IconWidget icon_widget = null;
Packit Service 1d8f1c
Packit Service 1d8f1c
        if (label == "") {
Packit Service 1d8f1c
            label = null;
Packit Service 1d8f1c
            icon_widget = new IconWidget(icon_name, Gtk.IconSize.BUTTON);
Packit Service 1d8f1c
            set_is_important(false);
Packit Service 1d8f1c
        } else {
Packit Service 1d8f1c
            set_is_important(true);
Packit Service 1d8f1c
        }
Packit Service 1d8f1c
Packit Service 1d8f1c
        set_icon_widget(icon_widget);
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
}
Packit Service 1d8f1c
Packit Service 1d8f1c
public class PropMenuToolButton : PropToggleToolButton, IPropToolItem {
Packit Service 1d8f1c
    private PropMenu m_menu = null;
Packit Service 1d8f1c
Packit Service 1d8f1c
    public PropMenuToolButton(IBus.Property prop) {
Packit Service 1d8f1c
        /* Chain up base class constructor
Packit Service 1d8f1c
         *
Packit Service 1d8f1c
         * Need to set halign for KDE5
Packit Service 1d8f1c
         */
Packit Service 1d8f1c
        GLib.Object(halign: Gtk.Align.START);
Packit Service 1d8f1c
Packit Service 1d8f1c
        m_menu = new PropMenu(prop);
Packit Service 1d8f1c
        m_menu.deactivate.connect((m) =>
Packit Service 1d8f1c
                                  set_active(false));
Packit Service 1d8f1c
        m_menu.property_activate.connect((k, s) =>
Packit Service 1d8f1c
                                         property_activate(k, s));
Packit Service 1d8f1c
Packit Service 1d8f1c
        base.set_property(prop);
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
Packit Service 1d8f1c
    public new void update_property(IBus.Property prop) {
Packit Service 1d8f1c
        base.update_property(prop);
Packit Service 1d8f1c
        m_menu.update_property(prop);
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
Packit Service 1d8f1c
    public override void toggled() {
Packit Service 1d8f1c
        if (get_active())
Packit Service 1d8f1c
            m_menu.popup(0, Gtk.get_current_event_time(), this);
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
Packit Service 1d8f1c
    public override void destroy() {
Packit Service 1d8f1c
        m_menu = null;
Packit Service 1d8f1c
        base.destroy();
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
}
Packit Service 1d8f1c
Packit Service 1d8f1c
public class PropSeparatorToolItem : Gtk.SeparatorToolItem, IPropToolItem {
Packit Service 1d8f1c
    public PropSeparatorToolItem(IBus.Property prop) {
Packit Service 1d8f1c
        /* Chain up base class constructor */
Packit Service 1d8f1c
        GLib.Object();
Packit Service 1d8f1c
Packit Service 1d8f1c
        set_homogeneous(false);
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
Packit Service 1d8f1c
    public void update_property(IBus.Property prop) {
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
}