Blob Blame History Raw
/* vim:set et sts=4 sw=4:
 *
 * ibus - The Input Bus
 *
 * Copyright(c) 2011-2016 Peng Huang <shawn.p.huang@gmail.com>
 * Copyright(c) 2016-2017 Takao Fujiwara <takao.fujiwara1@gmail.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
 * USA
 */

class Handle : Gtk.EventBox {
    private bool m_move_begined;
    private Gdk.Rectangle m_workarea;
    private Gdk.Point m_press_pos;

    public signal void move_begin();
    public signal void move_end();

    public Handle() {
        // Call base class constructor
        GLib.Object(
            name : "IBusHandle"
        );

        set_size_request(6, -1);
        Gdk.EventMask mask = Gdk.EventMask.EXPOSURE_MASK |
                             Gdk.EventMask.BUTTON_PRESS_MASK |
                             Gdk.EventMask.BUTTON_RELEASE_MASK |
                             Gdk.EventMask.BUTTON1_MOTION_MASK;
        set_events(mask);
        m_move_begined = false;

        // Currently it is too hard to notice this Handle on PropertyPanel
        // so now this widget is drawn by the gray color for the visibility.
        Gtk.CssProvider css_provider = new Gtk.CssProvider();
        try {
            css_provider.load_from_data(
                    "#IBusHandle { background-color: gray }", -1);
        } catch (GLib.Error error) {
            warning("Parse error in Handle: %s", error.message);
        }
        Gtk.StyleContext context = get_style_context();
        context.add_provider(css_provider,
                             Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION);
    }

    public override void realize() {
        base.realize();
    }

    public override bool button_press_event(Gdk.EventButton event) {
        if (event.button != 1)
            return false;
        m_workarea = Gdk.Rectangle(){
            x = 0, y = 0, width = int.MAX, height = int.MAX};
        do {
            Gdk.Window root = Gdk.get_default_root_window();
            Gdk.Atom property = Gdk.Atom.intern("_NET_CURRENT_DESKTOP", false);
            Gdk.Atom type = Gdk.Atom.intern("CARDINAL", false);
            Gdk.Atom actual_type;
            int format;
            uchar[] data;
            bool result;
            result = Gdk.property_get(root,
                                      property,
                                      type,
                                      0, long.MAX,
                                      0,
                                      out actual_type,
                                      out format,
                                      out data);
            if (!result || actual_type != type || format != 32 || data.length != 4)
                break;
            int index = data[0] |
                        data[1] << 8 |
                        data[2] << 16 |
                        data[3] << 24;
            property = Gdk.Atom.intern("_NET_WORKAREA", false);
            type = Gdk.Atom.intern("CARDINAL", false);
            result = Gdk.property_get(root,
                                      property,
                                      type,
                                      0, long.MAX,
                                      0,
                                      out actual_type,
                                      out format,
                                      out data);
            if (!result || actual_type != type || format != 32 || data.length < (index + 1) * 16)
                break;
            int i = index * 4 * 4;
            m_workarea.x = data[i] |
                           data[i + 1] << 8 |
                           data[i + 2] << 16 |
                           data[i + 3] << 24;
            i += 4;
            m_workarea.y = data[i] |
                           data[i + 1] << 8 |
                           data[i + 2] << 16 |
                           data[i + 3] << 24;
            i += 4;
            m_workarea.width = data[i] |
                               data[i + 1] << 8 |
                               data[i + 2] << 16 |
                               data[i + 3] << 24;
            i += 4;
            m_workarea.height = data[i] |
                                data[i + 1] << 8 |
                                data[i + 2] << 16 |
                                data[i + 3] << 24;
        } while (false);
        m_move_begined = true;
        int x, y;
        Gtk.Window toplevel = (Gtk.Window)get_toplevel();
        toplevel.get_position(out x, out y);
        m_press_pos.x = (int)event.x_root - x;
        m_press_pos.y = (int)event.y_root - y;
        move_begin();
        return true;
    }

    public override bool button_release_event(Gdk.EventButton event) {
        if (event.button != 1)
            return false;
        m_move_begined = false;
        m_press_pos.x = 0;
        m_press_pos.y = 0;
        get_window().set_cursor(new Gdk.Cursor.for_display(
                   Gdk.Display.get_default(),
                   Gdk.CursorType.FLEUR));
        move_end();
        return true;
    }

    public override bool motion_notify_event(Gdk.EventMotion event) {
        if (!m_move_begined)
            return false;
        Gtk.Window toplevel = (Gtk.Window)get_toplevel();
        int x = (int)(event.x_root - m_press_pos.x);
        int y = (int)(event.y_root - m_press_pos.y);

        if (x < m_workarea.x && x > m_workarea.x - 16)
            x = m_workarea.x;
        if (y < m_workarea.y && y > m_workarea.y - 16)
            y = m_workarea.y;
        int w, h;
        toplevel.get_size(out w, out h);
        if (x + w > m_workarea.x + m_workarea.width &&
            x + w < m_workarea.x + m_workarea.width + 16)
            x = m_workarea.x + m_workarea.width - w;
        if (y + h > m_workarea.y + m_workarea.height &&
            y + h < m_workarea.y + m_workarea.height + 16)
            y = m_workarea.y + m_workarea.height - w;
        toplevel.move(x, y);
        return true;
    }

    public override bool draw(Cairo.Context cr) {
        if (Gtk.cairo_should_draw_window(cr, get_window())) {
            Gtk.StyleContext context = get_style_context();
            Gtk.Allocation allocation;
            get_allocation(out allocation);
            context.render_handle(cr,
                allocation.x, allocation.y + (allocation.height - 40) / 2, allocation.width, 40.0);
        }
        return false;
    }
}