/* vim:set et sts=4 sw=4: * * ibus - The Input Bus * * Copyright(c) 2011-2016 Peng Huang * Copyright(c) 2016-2017 Takao Fujiwara * * 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; } }