Source code for pympress.pointer

# -*- coding: utf-8 -*-
#
#       pointer.py
#
#       Copyright 2017 Cimbali <me@cimba.li>
#
#       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 2 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, write to the Free Software
#       Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
#       MA 02110-1301, USA.

"""
:mod:`pympress.pointer` -- Manage when and where to draw a software-emulated laser pointer on screen
----------------------------------------------------------------------------------------------------
"""

from __future__ import print_function, unicode_literals

import logging
logger = logging.getLogger(__name__)

import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gdk, GdkPixbuf

from pympress import util, extras


#: Pointer disabled, will not be drawn
POINTER_OFF = -1
#: Pointer enabled but hidden, will be drawn on ctrl + click
POINTER_HIDE = 0
#: Draw the pointer on the current slide
POINTER_SHOW = 1


[docs]class Pointer(object): #: :class:`~GdkPixbuf.Pixbuf` to read XML descriptions of GUIs and load them. pointer = GdkPixbuf.Pixbuf() #: `(float, float)` of position relative to slide, where the pointer should appear pointer_pos = (.5, .5) #: `bool` indicating whether we should show the pointer show_pointer = POINTER_OFF #: A reference to the UI's :class:`~pympress.config.Config`, to update the pointer preference config = None #: callback, to be connected to :func:`~pympress.ui.UI.redraw_current_slide` redraw_current_slide = lambda: None def __init__(self, config, builder): """ Setup the pointer management, and load the default pointer Args: config (:class:`~pympress.config.Config`): A config object containing preferences builder (:class:`~pympress.builder.Builder`): A builder from which to load widgets """ super(Pointer, self).__init__() self.config = config self.redraw_current_slide = builder.get_callback_handler('redraw_current_slide') default = 'pointer_' + config.get('presenter', 'pointer') self.load_pointer(default) for radio_name in ['pointer_red', 'pointer_blue', 'pointer_green', 'pointer_none']: radio = builder.get_object(radio_name) radio.set_name(radio_name) radio.set_active(radio_name == default)
[docs] def load_pointer(self, name): """ Perform the change of pointer using its name Args: name (`str`): Name of the pointer to load """ if name in ['pointer_red', 'pointer_green', 'pointer_blue']: self.show_pointer = POINTER_HIDE self.pointer = GdkPixbuf.Pixbuf.new_from_file(util.get_icon_path(name + '.png')) else: self.show_pointer = POINTER_OFF
[docs] def change_pointer(self, widget): """ Callback for a radio item selection as pointer color Args: widget (:class:`~Gtk.RadioMenuItem`): the selected radio item in the pointer type selection menu """ if widget.get_active(): assert(widget.get_name().startswith('pointer_')) self.load_pointer(widget.get_name()) self.config.set('presenter', 'pointer', widget.get_name()[len('pointer_'):])
[docs] def render_pointer(self, cairo_context, ww, wh): """ Draw the laser pointer on screen Args: cairo_context (:class:`~cairo.Context`): The canvas on which to render the pointer ww (`int`): The widget width wh (`int`): The widget height """ if self.show_pointer == POINTER_SHOW: x = ww * self.pointer_pos[0] - self.pointer.get_width() / 2 y = wh * self.pointer_pos[1] - self.pointer.get_height() / 2 Gdk.cairo_set_source_pixbuf(cairo_context, self.pointer, x, y) cairo_context.paint()
[docs] def track_pointer(self, widget, event): """ Move the laser pointer at the mouse location. Args: widget (:class:`~Gtk.Widget`): the widget which has received the event. event (:class:`~Gdk.Event`): the GTK event. Returns: `bool`: whether the event was consumed """ if self.show_pointer == POINTER_SHOW: ww, wh = widget.get_allocated_width(), widget.get_allocated_height() ex, ey = event.get_coords() self.pointer_pos = (ex / ww, ey / wh) self.redraw_current_slide() return True else: return False
[docs] def toggle_pointer(self, widget, event): """ Track events defining when the laser is pointing. Args: widget (:class:`~Gtk.Widget`): the widget which has received the event. event (:class:`~Gdk.Event`): the GTK event. Returns: `bool`: whether the event was consumed """ if self.show_pointer == POINTER_OFF: return False ctrl_pressed = event.get_state() & Gdk.ModifierType.CONTROL_MASK if ctrl_pressed and event.type == Gdk.EventType.BUTTON_PRESS: self.show_pointer = POINTER_SHOW extras.Cursor.set_cursor(widget, 'invisible') # Immediately place & draw the pointer return self.track_pointer(widget, event) elif self.show_pointer == POINTER_SHOW and event.type == Gdk.EventType.BUTTON_RELEASE: self.show_pointer = POINTER_HIDE extras.Cursor.set_cursor(widget, 'parent') self.redraw_current_slide() return True else: return False