Ticket #4023: 0001-Add-SelectionPalette-widget.patch

File 0001-Add-SelectionPalette-widget.patch, 8.5 KB (added by garnacho, 12 years ago)

Patch, add selection palette to sugar-toolkit-gtk3, so it can be hooked on text widgets

  • src/sugar3/graphics/Makefile.am

    From 5044c42c1fc6205de55a13582780bf19cb371ff4 Mon Sep 17 00:00:00 2001
    From: Carlos Garnacho <carlos@lanedo.com>
    Date: Wed, 10 Oct 2012 16:54:07 +0200
    Subject: [PATCH] Add SelectionPalette widget
    
    This is a floating toolbar window meant to provide a touch interface
    to text widgets' selection manipulation (cut/copy/paste for now).
    It can connect to a text widget, so the options are placed accordingly
    and popped up as the user interacts with the entry.
    It has API to connect to a text widget to be manipulated and place
    ---
     src/sugar3/graphics/Makefile.am         |   1 +
     src/sugar3/graphics/selectionpalette.py | 190 ++++++++++++++++++++++++++++++++
     2 files changed, 191 insertions(+)
     create mode 100644 src/sugar3/graphics/selectionpalette.py
    
    diff --git a/src/sugar3/graphics/Makefile.am b/src/sugar3/graphics/Makefile.am
    index a298a32..253e6b2 100644
    a b sugar_PYTHON = \ 
    1717        panel.py                \
    1818        radiopalette.py         \
    1919        radiotoolbutton.py      \
     20        selectionpalette.py     \
    2021        style.py                \
    2122        toggletoolbutton.py     \
    2223        toolbarbox.py           \
  • new file src/sugar3/graphics/selectionpalette.py

    diff --git a/src/sugar3/graphics/selectionpalette.py b/src/sugar3/graphics/selectionpalette.py
    new file mode 100644
    index 0000000..6d40fb1
    - +  
     1# Copyright (C) 2012, One Laptop Per Child
     2#
     3# This library is free software; you can redistribute it and/or
     4# modify it under the terms of the GNU Lesser General Public
     5# License as published by the Free Software Foundation; either
     6# version 2 of the License, or (at your option) any later version.
     7#
     8# This library is distributed in the hope that it will be useful,
     9# but WITHOUT ANY WARRANTY; without even the implied warranty of
     10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     11# Lesser General Public License for more details.
     12#
     13# You should have received a copy of the GNU Lesser General Public
     14# License along with this library; if not, write to the
     15# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
     16# Boston, MA 02111-1307, USA.
     17#
     18# Author: Carlos Garnacho  <carlos@lanedo.com>
     19
     20from gi.repository import Gtk
     21from gi.repository import Gdk
     22from gi.repository import GObject
     23from sugar3.graphics.toolbarbox import ToolbarBox, ToolbarButton
     24
     25class SelectionPalette(Gtk.Window):
     26    _PADDING = 10
     27
     28    __gtype_name__ = "SugarSelectionPalette"
     29
     30    __gsignals__ = {
     31        'cut': (GObject.SignalFlags.RUN_LAST, None, ()),
     32        'copy': (GObject.SignalFlags.RUN_LAST, None, ()),
     33        'paste': (GObject.SignalFlags.RUN_LAST, None, ()),
     34    }
     35
     36    def _cut_clicked_cb(self, button):
     37        self.emit('cut')
     38
     39        if not self._update_state(False, True, self._clipboard_has_contents(button)):
     40            self.hide()
     41
     42    def _copy_clicked_cb(self, button):
     43        self.emit('copy')
     44
     45    def _paste_clicked_cb(self, button):
     46        self.emit('paste')
     47
     48    def __init__(self, widget=None):
     49        Gtk.Window.__init__(self, type=Gtk.WindowType.POPUP)
     50        self.set_default_size(-1,-1)
     51
     52        self._box = Gtk.Toolbar(icon_size = Gtk.IconSize.LARGE_TOOLBAR,
     53                                icon_size_set = True, show_arrow = False)
     54
     55        self._cut = Gtk.ToolButton(icon_name = 'gtk-cut')
     56        self._cut.connect('clicked', self._cut_clicked_cb)
     57        self._box.add(self._cut)
     58
     59        self._copy = Gtk.ToolButton(icon_name = 'gtk-copy')
     60        self._copy.connect('clicked', self._copy_clicked_cb)
     61        self._box.add(self._copy)
     62
     63        self._paste = Gtk.ToolButton(icon_name = 'gtk-paste')
     64        self._paste.connect('clicked', self._paste_clicked_cb)
     65        self._box.add(self._paste)
     66
     67        self._box.show_all()
     68        self.add(self._box)
     69
     70        if widget is not None:
     71            self.connect_to_widget(widget)
     72
     73    def _position_palette(self, widget):
     74        if not widget.get_realized():
     75            return
     76
     77        self.realize()
     78        allocation = widget.get_allocation()
     79        palette_allocation = self.get_allocation()
     80
     81        toplevel = widget.get_toplevel()
     82
     83        if not toplevel.is_toplevel():
     84            return
     85
     86        allocation.x, allocation.y = widget.translate_coordinates(toplevel, 0, 0)
     87        allocation.x, allocation.y = toplevel.get_window().get_root_coords(allocation.x,
     88                                                                           allocation.y)
     89
     90        if allocation.width <= palette_allocation.width or \
     91                allocation.height <= palette_allocation.height + self._PADDING :
     92            self.move (allocation.x + (allocation.width / 2) - (palette_allocation.width / 2),
     93                       allocation.y + allocation.height + self._PADDING)
     94        else:
     95            self.move (allocation.x + (allocation.width / 2) - (palette_allocation.width / 2),
     96                       allocation.y + allocation.height - palette_allocation.height - self._PADDING)
     97
     98        self.show()
     99
     100    def _clipboard_has_contents(self, widget):
     101        display = widget.get_display()
     102        clipboard = Gtk.Clipboard.get_for_display(display, Gdk.Atom.intern('CLIPBOARD', True))
     103
     104        return clipboard.wait_is_text_available ()
     105
     106    def _update_state(self, has_selection, editable, has_clipboard_content):
     107        self._cut.set_sensitive(has_selection)
     108        self._copy.set_sensitive(has_selection)
     109        self._paste.set_sensitive(editable and has_clipboard_content)
     110
     111        return has_selection or (editable and has_clipboard_content)
     112
     113    def _widget_key_release_cb(self, widget, event):
     114        self.hide()
     115        return False
     116
     117    def _widget_focus_out_cb(self, widget, event):
     118        self.hide()
     119        return False
     120
     121    def _entry_button_release_cb(self, widget, event):
     122        if event.type != Gdk.EventType.BUTTON_RELEASE and \
     123                event.type != Gdk.EventType.TOUCH_END :
     124            return False
     125
     126        if event.get_source_device().get_source() != Gdk.InputSource.TOUCHSCREEN:
     127            self.hide()
     128            return False
     129
     130        has_selection = widget.get_selection_bounds()
     131
     132        if self._update_state(has_selection, True, self._clipboard_has_contents(widget)):
     133            self._position_palette(widget)
     134        else:
     135            self.hide()
     136
     137        return False
     138
     139    def _textview_button_release_cb(self, widget, event):
     140        if event.type != Gdk.EventType.BUTTON_RELEASE and \
     141                event.type != Gdk.EventType.TOUCH_END :
     142            return False
     143
     144        if event.get_source_device().get_source() != Gdk.InputSource.TOUCHSCREEN:
     145            self.hide()
     146            return False
     147
     148        has_selection = widget.get_buffer().get_selection_bounds()
     149
     150        if self._update_state(has_selection, widget.get_editable(), \
     151                              self._clipboard_has_contents(widget)):
     152            self._position_palette(widget)
     153        else:
     154            self.hide()
     155
     156        return False
     157
     158    def _widget_cut_clipboard(self, selection, widget):
     159        widget.emit('cut-clipboard')
     160
     161    def _widget_copy_clipboard(self, selection, widget):
     162        widget.emit('copy-clipboard')
     163
     164    def _widget_paste_clipboard(self, selection, widget):
     165        widget.emit('paste-clipboard')
     166
     167    def connect_to_widget(self, widget):
     168        if isinstance(widget, Gtk.Entry):
     169            self._button_release_id = widget.connect('button-release-event', self._entry_button_release_cb)
     170            self._touch_event_id = widget.connect('touch-event', self._entry_button_release_cb)
     171        elif isinstance(widget, Gtk.TextView):
     172            self._button_release_id = widget.connect('button-release-event', self._textview_button_release_cb)
     173            self._touch_event_id = widget.connect('touch-event', self._textview_button_release_cb)
     174        else:
     175            return False
     176
     177        self._key_release_id = widget.connect('key-release-event', self._widget_key_release_cb)
     178        self._focus_out_id = widget.connect('focus-out-event', self._widget_focus_out_cb)
     179
     180        self.connect('cut', self._widget_cut_clipboard, widget)
     181        self.connect('copy', self._widget_copy_clipboard, widget)
     182        self.connect('paste', self._widget_paste_clipboard, widget)
     183
     184        return True
     185
     186    def disconnect_from_widget(self, widget):
     187        widget.signal_handler_disconnect(self._button_release_id)
     188        widget.signal_handler_disconnect(self._touch_event_id)
     189        widget.signal_handler_disconnect(self._key_release_id)
     190        widget.signal_handler_disconnect(self._focus_out_id)