1# Copyright (C) 2010 by Brian Parma 2# 3# This program is free software; you can redistribute it and/or modify 4# it under the terms of the GNU General Public License as published by 5# the Free Software Foundation; either version 1, or (at your option) 6# any later version. 7# 8# This program 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 11# GNU General Public License for more details. 12# 13# You should have received a copy of the GNU General Public License 14# along with this program; if not, write to the Free Software 15# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 16 17from gi.repository import Gdk 18from gi.repository import Gtk 19from gi.repository import GObject 20 21 22class CellRendererDays(Gtk.CellRendererText): 23 '''Custom Cell Renderer for showing a ListView of 7 days with checkboxes, based off pygtk FAQ example''' 24 25 __gtype_name__ = 'CellRendererDays' 26 __gproperties__ = { 27 'days': (object, 'days', 'List of enabled days', GObject.ParamFlags.READWRITE) 28 } 29 __gsignals__ = { 30 'days-changed': (GObject.SignalFlags.RUN_FIRST, None, (str, object)) 31 } 32 property_names = list(__gproperties__.keys()) 33 34 def __init__(self): 35 Gtk.CellRendererText.__init__(self) 36 self.model = Gtk.ListStore(bool, str) 37 self.view = None 38 self.view_window = None 39 40 for day in [ 41 'Sunday', 42 'Monday', 43 'Tuesday', 44 'Wednesday', 45 'Thursday', 46 'Friday', 47 'Saturday', 48 ]: 49 self.model.append([True, day]) 50 51 self.set_property('text', 'Edit me') 52 53 def _create_view(self, treeview): 54 '''Create the Window and View to display when editing''' 55 self.view_window = Gtk.Window() 56 self.view_window.set_decorated(False) 57 self.view_window.set_property('skip-taskbar-hint', True) 58 59 self.view = Gtk.TreeView() 60 61 self.view.set_model(self.model) 62 self.view.set_headers_visible(False) 63 64 cr = Gtk.CellRendererToggle() 65 cr.connect('toggled', self._toggle) 66 col = Gtk.TreeViewColumn('Enabled', cr, active=0) 67 self.view.append_column(col) 68 69 cr = Gtk.CellRendererText() 70 col = Gtk.TreeViewColumn('Day', cr, text=1) 71 self.view.append_column(col) 72 73 # events 74 self.view.connect('focus-out-event', self._close) 75 self.view.connect('key-press-event', self._key_pressed) 76 77 # should be automatic 78 self.view_window.set_modal(False) 79 self.view_window.set_transient_for(None) # cancel the modality of dialog 80 self.view_window.add(self.view) 81 82 # necessary for getting the (width, height) of calendar_window 83 self.view.show() 84 self.view_window.realize() 85 86 def do_set_property(self, pspec, value): 87 '''Set property overload''' 88 setattr(self, pspec.name, value) 89 90 def do_get_property(self, pspec): 91 '''Get property overload''' 92 return getattr(self, pspec.name) 93 94 def do_start_editing( 95 self, event, treeview, path, background_area, cell_area, flags 96 ): 97 '''Called when user starts editing the cell''' 98 99 if not self.get_property('editable'): 100 return 101 102 # create window/view if it doesn't exist 103 if not self.view_window: 104 self._create_view(treeview) 105 else: 106 self.view_window.show() 107 108 # set display to reflect 'days' property 109 for i, row in enumerate(self.model): 110 row[0] = self.days[i] 111 112 # position the popup below the edited cell (and try hard to keep the popup within the toplevel window) 113 (tree_x, tree_y) = treeview.get_bin_window().get_origin()[1:] 114 (tree_w, tree_h) = treeview.get_window().get_geometry()[2:4] 115 (my_w, my_h) = self.view_window.get_window().get_geometry()[2:4] 116 x = tree_x + min(cell_area.x, tree_w - my_w + treeview.get_visible_rect().x) 117 y = tree_y + min(cell_area.y, tree_h - my_h + treeview.get_visible_rect().y) 118 self.view_window.move(x, y) 119 120 # save the path so we can return it in _done, and we aren't using dialog so we can't block.... 121 self._path = path 122 123 return None # don't return any editable, our Gtk.Dialog did the work already 124 125 def _done(self): 126 '''Called when we are done editing''' 127 days = [row[0] for row in self.model] 128 129 if days != self.days: 130 self.emit('days-changed', self._path, days) 131 132 self.view_window.hide() 133 134 def _key_pressed(self, view, event): 135 '''Key pressed event handler, finish editing on Return''' 136 # event == None for day selected via doubleclick 137 if ( 138 not event 139 or event.type == Gdk.EventType.KEY_PRESS 140 and Gdk.keyval_name(event.keyval) == 'Return' 141 ): 142 self._done() 143 return True 144 145 def _toggle(self, cell, path): 146 '''Checkbox toggle event handler''' 147 active = self.model[path][0] 148 self.model[path][0] = not active 149 return True 150 151 def _close(self, view, event): 152 '''Focus-out-event handler''' 153 self._done() 154 return True 155