summaryrefslogtreecommitdiff
path: root/src/renderers/pieWindow.vala
diff options
context:
space:
mode:
Diffstat (limited to 'src/renderers/pieWindow.vala')
-rwxr-xr-xsrc/renderers/pieWindow.vala500
1 files changed, 500 insertions, 0 deletions
diff --git a/src/renderers/pieWindow.vala b/src/renderers/pieWindow.vala
new file mode 100755
index 0000000..5accb15
--- /dev/null
+++ b/src/renderers/pieWindow.vala
@@ -0,0 +1,500 @@
+/////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2011-2015 by Simon Schneegans
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or (at
+// your option) any later version.
+//
+// This program 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
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+/////////////////////////////////////////////////////////////////////////
+
+using GLib.Math;
+
+namespace GnomePie {
+
+/////////////////////////////////////////////////////////////////////////
+/// An invisible window. Used to draw Pies onto.
+/////////////////////////////////////////////////////////////////////////
+
+public class PieWindow : Gtk.Window {
+
+ /////////////////////////////////////////////////////////////////////
+ /// Signal which gets emitted when the PieWindow is about to close.
+ /////////////////////////////////////////////////////////////////////
+
+ public signal void on_closing();
+
+ /////////////////////////////////////////////////////////////////////
+ /// Signal which gets emitted when the PieWindow is closed.
+ /////////////////////////////////////////////////////////////////////
+
+ public signal void on_closed();
+
+ /////////////////////////////////////////////////////////////////////
+ /// The background image used for fake transparency if
+ /// has_compositing is false.
+ /////////////////////////////////////////////////////////////////////
+
+ public Image background { get; private set; default=null; }
+
+ /////////////////////////////////////////////////////////////////////
+ /// The background image position and size.
+ /////////////////////////////////////////////////////////////////////
+
+ private int back_x;
+ private int back_y;
+ private int back_sz_x;
+ private int back_sz_y;
+
+ /////////////////////////////////////////////////////////////////////
+ /// Some panels moves the window after it was realized.
+ /// This value set the maximum allowed panel height or width.
+ /// (how many pixels the window could be moved in every direction
+ /// from the screen borders towards the center)
+ /////////////////////////////////////////////////////////////////////
+
+ private int panel_sz = 64;
+
+ /////////////////////////////////////////////////////////////////////
+ /// This value set the maximum allowed mouse movement in pixels
+ /// from the capture to the show point in every direction.
+ /////////////////////////////////////////////////////////////////////
+
+ private int mouse_move = 30;
+
+ /////////////////////////////////////////////////////////////////////
+ /// The owned renderer.
+ /////////////////////////////////////////////////////////////////////
+
+ private PieRenderer renderer;
+
+ /////////////////////////////////////////////////////////////////////
+ /// True, if the Pie is currently fading out.
+ /////////////////////////////////////////////////////////////////////
+
+ private bool closing = false;
+ private bool closed = false;
+
+ /////////////////////////////////////////////////////////////////////
+ /// A timer used for calculating the frame time.
+ /////////////////////////////////////////////////////////////////////
+
+ private GLib.Timer timer;
+
+ /////////////////////////////////////////////////////////////////////
+ /// True, if the screen supports compositing.
+ /////////////////////////////////////////////////////////////////////
+
+ private bool has_compositing = false;
+
+ /////////////////////////////////////////////////////////////////////
+ /// When a Pie is opened, pressed buttons are accumulated and
+ /// matches are searched in all slices.
+ /////////////////////////////////////////////////////////////////////
+
+ private string search_string = "";
+
+ /////////////////////////////////////////////////////////////////////
+ /// C'tor, sets up the window.
+ /////////////////////////////////////////////////////////////////////
+
+ public PieWindow() {
+ this.renderer = new PieRenderer();
+
+ this.set_title("Gnome-Pie");
+ this.set_skip_taskbar_hint(true);
+ this.set_skip_pager_hint(true);
+ this.set_keep_above(true);
+ this.set_type_hint(Gdk.WindowTypeHint.POPUP_MENU);
+ this.set_decorated(false);
+ this.set_resizable(false);
+ this.icon_name = "gnome-pie";
+ this.set_accept_focus(false);
+ this.app_paintable = true;
+
+ // check for compositing
+ if (this.screen.is_composited()) {
+ this.set_visual(this.screen.get_rgba_visual());
+ this.has_compositing = true;
+ }
+
+ //add_events() call was removed because it causes that gnome-pie sometimes enter
+ //and infinte loop while processing some mouse-motion events.
+ //(this was seen in Ubuntu 14.04.2 64/32-bits -Glib 2.19- and in MATE 14.04.2)
+ // set up event filter
+ //this.add_events(Gdk.EventMask.BUTTON_RELEASE_MASK |
+ // Gdk.EventMask.KEY_RELEASE_MASK |
+ // Gdk.EventMask.KEY_PRESS_MASK |
+ // Gdk.EventMask.POINTER_MOTION_MASK |
+ // Gdk.EventMask.SCROLL_MASK );
+
+ // activate on left click
+ this.button_release_event.connect ((e) => {
+ if (e.button == 1 || PieManager.get_is_turbo(this.renderer.id)) this.activate_slice(e.time);
+ return true;
+ });
+
+ // cancel on right click
+ this.button_press_event.connect ((e) => {
+ if (e.button == 3) this.cancel();
+ return true;
+ });
+
+ // remember last pressed key in order to disable key repeat
+ uint last_key = 0;
+ uint32 last_time_stamp = 0;
+ this.key_press_event.connect((e) => {
+ if (e.keyval != last_key) {
+ this.handle_key_press(e.keyval, e.time, last_time_stamp, e.str);
+ last_key = e.keyval;
+ last_time_stamp = e.time;
+ }
+ return true;
+ });
+
+ // activate on key release if turbo_mode is enabled
+ this.key_release_event.connect((e) => {
+ last_key = 0;
+ if (PieManager.get_is_turbo(this.renderer.id))
+ this.activate_slice(e.time);
+ else
+ this.handle_key_release(e.keyval);
+ return true;
+ });
+
+ // notify the renderer of mouse move events
+ this.motion_notify_event.connect((e) => {
+ this.renderer.on_mouse_move();
+ return true;
+ });
+
+ this.show.connect_after(() => {
+ Gtk.grab_add(this);
+ FocusGrabber.grab(this.get_window(), true, true, false);
+ });
+
+ this.scroll_event.connect((e) => {
+ if (e.direction == Gdk.ScrollDirection.UP)
+ this.renderer.select_prevpage();
+
+ else if (e.direction == Gdk.ScrollDirection.DOWN)
+ this.renderer.select_nextpage();
+ return true;
+ });
+
+ // draw the pie on expose
+ this.draw.connect(this.draw_window);
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Loads a Pie to be rendered.
+ /////////////////////////////////////////////////////////////////////
+
+ public void load_pie(Pie pie) {
+ this.renderer.load_pie(pie);
+ this.set_window_position(pie);
+ this.set_size_request(renderer.size_w, renderer.size_h);
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Opens the window. load_pie should have been called before.
+ /////////////////////////////////////////////////////////////////////
+
+ public void open() {
+ this.realize();
+ // capture the background image if there is no compositing
+ if (!this.has_compositing) {
+ this.get_position(out this.back_x, out this.back_y);
+ this.get_size(out this.back_sz_x, out this.back_sz_y);
+ this.back_sz_x++;
+ this.back_sz_y++;
+
+ int screenx= Gdk.Screen.width();
+ int screeny= Gdk.Screen.height();
+
+ //allow some window movement from the screen borders
+ //(some panels moves the window after it was realized)
+ int dx = this.panel_sz - this.back_x;
+ if (dx > 0)
+ this.back_sz_x += dx;
+ dx = this.panel_sz - (screenx - this.back_x - this.back_sz_x +1);
+ if (dx > 0) {
+ this.back_sz_x += dx;
+ this.back_x -= dx;
+ }
+
+ int dy = this.panel_sz - this.back_y;
+ if (dy > 0)
+ this.back_sz_y += dy;
+ dy = this.panel_sz - (screeny - this.back_y - this.back_sz_y +1);
+ if (dy > 0) {
+ this.back_sz_y += dy;
+ this.back_y -= dy;
+ }
+
+ //also tolerate some mouse movement
+ this.back_x -= this.mouse_move;
+ this.back_sz_x += this.mouse_move*2;
+ this.back_y -= this.mouse_move;
+ this.back_sz_y += this.mouse_move*2;
+
+ //make sure we don't go outside the screen
+ if (this.back_x < 0) {
+ this.back_sz_x += this.back_x;
+ this.back_x = 0;
+ }
+ if (this.back_y < 0) {
+ this.back_sz_y += this.back_y;
+ this.back_y = 0;
+ }
+ if (this.back_x + this.back_sz_x > screenx)
+ this.back_sz_x = screenx - this.back_x;
+ if (this.back_y + this.back_sz_y > screeny)
+ this.back_sz_y = screeny - this.back_y;
+ this.background = new Image.capture_screen(this.back_x, this.back_y, this.back_sz_x, this.back_sz_y);
+ }
+
+ // capture the input focus
+ this.show();
+
+ // start the timer
+ this.timer = new GLib.Timer();
+ this.timer.start();
+ this.queue_draw();
+
+ bool warp_pointer = PieManager.get_is_warp(this.renderer.id);
+
+ // the main draw loop
+ GLib.Timeout.add((uint)(1000.0/Config.global.refresh_rate), () => {
+ if (this.closed)
+ return false;
+
+ if (warp_pointer) {
+ warp_pointer = false;
+ int x, y;
+ this.get_center_pos(out x, out y);
+ this.set_mouse_position(x, y);
+ }
+
+ this.queue_draw();
+ return this.visible;
+ });
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Gets the center position of the window.
+ /////////////////////////////////////////////////////////////////////
+
+ public void get_center_pos(out int out_x, out int out_y) {
+ int x=0, y=0; //width=0, height=0;
+ this.get_position(out x, out y);
+ out_x = x + renderer.center_x;
+ out_y = y + renderer.center_y;
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Gets the absolute position of the mouse pointer.
+ /////////////////////////////////////////////////////////////////////
+
+ private void get_mouse_position(out int mx, out int my) {
+ // get the mouse position
+ mx = 0;
+ my = 0;
+ Gdk.ModifierType mask;
+
+ var display = Gdk.Display.get_default();
+ var manager = display.get_device_manager();
+ GLib.List<weak Gdk.Device?> list = manager.list_devices(Gdk.DeviceType.MASTER);
+
+ foreach(var device in list) {
+ if (device.input_source != Gdk.InputSource.KEYBOARD) {
+ this.get_window().get_device_position(device, out mx, out my, out mask);
+ }
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Sets the absolute position of the mouse pointer.
+ /////////////////////////////////////////////////////////////////////
+
+ private void set_mouse_position(int mx, int my) {
+ var display = Gdk.Display.get_default();
+ var manager = display.get_device_manager();
+ GLib.List<weak Gdk.Device?> list = manager.list_devices(Gdk.DeviceType.MASTER);
+ foreach(var device in list) {
+ if (device.input_source != Gdk.InputSource.KEYBOARD) {
+ device.warp(Gdk.Screen.get_default(), mx, my);
+ }
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Draw the Pie.
+ /////////////////////////////////////////////////////////////////////
+
+ private bool draw_window(Cairo.Context ctx) {
+ // paint the background image if there is no compositing
+ if (this.has_compositing) {
+ ctx.set_operator (Cairo.Operator.CLEAR);
+ ctx.paint();
+ ctx.set_operator (Cairo.Operator.OVER);
+ } else {
+ //correct the background position if the window was moved
+ //since the background image was captured
+ int x, y;
+ this.get_position(out x, out y);
+ int dx = this.back_x - x;
+ int dy = this.back_y - y;
+ ctx.save();
+ ctx.translate(dx, dy);
+ ctx.set_operator (Cairo.Operator.OVER);
+ ctx.set_source_surface(background.surface, -1, -1);
+ ctx.paint();
+ ctx.restore();
+ }
+
+ // align the context to the center of the PieWindow
+ ctx.translate(this.renderer.center_x, this.renderer.center_y);
+
+ // get the mouse position
+ int mouse_x, mouse_y;
+ get_mouse_position( out mouse_x, out mouse_y );
+
+ // store the frame time
+ double frame_time = this.timer.elapsed();
+ this.timer.reset();
+
+ // render the Pie
+ this.renderer.draw(frame_time, ctx, mouse_x - (int)this.renderer.center_x,
+ mouse_y - (int)this.renderer.center_y);
+
+ return true;
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Activates the currently activate slice.
+ /////////////////////////////////////////////////////////////////////
+
+ private void activate_slice(uint32 time_stamp) {
+ if (!this.closing) {
+ this.closing = true;
+ this.on_closing();
+ Gtk.grab_remove(this);
+ FocusGrabber.ungrab();
+
+ GLib.Timeout.add(10, () => {
+ this.renderer.activate(time_stamp);
+ return false;
+ });
+
+ GLib.Timeout.add((uint)(Config.global.theme.fade_out_time*1000), () => {
+ this.closed = true;
+ this.on_closed();
+ this.destroy();
+ return false;
+ });
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Activates no slice and closes the PieWindow.
+ /////////////////////////////////////////////////////////////////////
+
+ private void cancel() {
+ if (!this.closing) {
+ this.closing = true;
+ this.on_closing();
+ Gtk.grab_remove(this);
+ FocusGrabber.ungrab();
+ this.renderer.cancel();
+
+ GLib.Timeout.add((uint)(Config.global.theme.fade_out_time*1000), () => {
+ this.closed = true;
+ this.on_closed();
+ this.destroy();
+ return false;
+ });
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Sets the position of the window to the center of the screen or to
+ /// the mouse.
+ /////////////////////////////////////////////////////////////////////
+
+ private void set_window_position(Pie pie) {
+ if(PieManager.get_is_centered(pie.id)) this.set_position(Gtk.WindowPosition.CENTER);
+ else this.set_position(Gtk.WindowPosition.MOUSE);
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Do some useful stuff when keys are pressed.
+ /////////////////////////////////////////////////////////////////////
+
+ private void handle_key_press(uint key, uint32 time_stamp, uint32 last_time_stamp, string text) {
+ if (last_time_stamp + 1000 < time_stamp) {
+ this.search_string = "";
+ }
+
+ if (Gdk.keyval_name(key) == "Escape") this.cancel();
+ else if (Gdk.keyval_name(key) == "Return") this.activate_slice(time_stamp);
+ else if (Gdk.keyval_name(key) == "KP_Enter") this.activate_slice(time_stamp);
+ else if (!PieManager.get_is_turbo(this.renderer.id)) {
+ if (Gdk.keyval_name(key) == "Up") this.renderer.select_up();
+ else if (Gdk.keyval_name(key) == "Down") this.renderer.select_down();
+ else if (Gdk.keyval_name(key) == "Left") this.renderer.select_left();
+ else if (Gdk.keyval_name(key) == "Right") this.renderer.select_right();
+ else if (Gdk.keyval_name(key) == "Page_Down") this.renderer.select_nextpage();
+ else if (Gdk.keyval_name(key) == "Page_Up") this.renderer.select_prevpage();
+ else if (Gdk.keyval_name(key) == "Tab") this.renderer.select_nextpage();
+ else if (Gdk.keyval_name(key) == "ISO_Left_Tab") this.renderer.select_prevpage();
+ else if (Gdk.keyval_name(key) == "Alt_L" && !Config.global.search_by_string) this.renderer.show_hotkeys = true;
+ else {
+
+ if (Config.global.search_by_string) {
+ this.search_string += text;
+ this.renderer.select_by_string(search_string.down());
+
+ } else {
+
+ int index = -1;
+
+ if (key >= 48 && key <= 57) index = ((int)key - 39)%10;
+ else if (key >= 97 && key <= 122) index = (int)key - 87;
+ else if (key >= 65 && key <= 90) index = (int)key - 55;
+
+ if (index >= 0 && index < this.renderer.slice_count()) {
+ this.renderer.key_board_control = true;
+ this.renderer.select_by_index(index);
+
+ if (this.renderer.active_slice == index) {
+ GLib.Timeout.add((uint)(Config.global.theme.transition_time*1000.0), ()=> {
+ this.activate_slice(time_stamp);
+ return false;
+ });
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Do some useful stuff when keys are released.
+ /////////////////////////////////////////////////////////////////////
+
+ private void handle_key_release(uint key) {
+ if (!PieManager.get_is_turbo(this.renderer.id)) {
+ if (Gdk.keyval_name(key) == "Alt_L") this.renderer.show_hotkeys = false;
+ }
+ }
+}
+
+}