Getting toggles to work
[multilist] / src / libview.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3
4 """
5 This file is part of Multilist.
6
7 Multilist is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
11
12 Multilist is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with Multilist.  If not, see <http://www.gnu.org/licenses/>.
19
20 Copyright (C) 2008 Christoph Würstle
21 """
22
23 import logging
24
25 import gobject
26 import gtk
27 import pango
28
29 import hildonize
30 import gtk_toolbox
31 import libliststorehandler
32
33
34 try:
35         _
36 except NameError:
37         _ = lambda x: x
38
39
40 _moduleLogger = logging.getLogger(__name__)
41
42
43 class TripleToggleCellRenderer(gtk.CellRendererToggle):
44
45         __gproperties__ = {
46                 "status": (gobject.TYPE_STRING, "Status", "Status", "", gobject.PARAM_READWRITE),
47         }
48
49         __gsignals__ = {
50                 'status_changed' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_INT, gobject.TYPE_STRING)),
51         }
52
53         def __init__(self):
54                 gtk.CellRendererToggle.__init__(self)
55                 self.set_property("activatable", True)
56                 self.connect('toggled', self._on_toggled)
57                 self.status = libliststorehandler.Liststorehandler.SHOW_NEW
58
59         @gtk_toolbox.log_exception(_moduleLogger)
60         def do_set_property(self, property, value):
61                 if getattr(self, property.name) == value or value is None:
62                         return
63
64                 setattr(self, property.name, value)
65
66                 if property.name == "status":
67                         active, inconsistent = {
68                                 libliststorehandler.Liststorehandler.SHOW_NEW: (False, False),
69                                 libliststorehandler.Liststorehandler.SHOW_ACTIVE: (False, True),
70                                 libliststorehandler.Liststorehandler.SHOW_COMPLETE: (True, False),
71                         }[value]
72                         self.set_property("active", active)
73                         self.set_property("inconsistent", inconsistent)
74
75         @gtk_toolbox.log_exception(_moduleLogger)
76         def do_get_property(self, property):
77                 return getattr(self, property.name)
78
79         @gtk_toolbox.log_exception(_moduleLogger)
80         def _on_toggled(self, widget, path):
81                 self.emit("status_changed", int(path), "-1")
82
83
84 gobject.type_register(TripleToggleCellRenderer)
85
86
87 class CellRendererTriple(gtk.GenericCellRenderer):
88         __gproperties__ = {
89                 "status": (gobject.TYPE_STRING, "Status", "Status", "", gobject.PARAM_READWRITE),
90         }
91
92         __gsignals__ = {
93                 'status_changed' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,(gobject.TYPE_INT,gobject.TYPE_STRING)),
94         }
95
96         def __init__(self):
97                 gtk.GenericCellRenderer.__init__(self)
98                 self.status = -1
99                 self.last_cell = None
100
101                 self.set_property('mode', gtk.CELL_RENDERER_MODE_ACTIVATABLE)
102                 self.set_property('visible', True)
103
104         def do_set_property(self,property,value):
105                 setattr(self, property.name, value)
106
107         def do_get_property(self, property):
108                 return getattr(self, property.name)
109
110         def _get_layout(self, widget):
111                 '''Gets the Pango layout used in the cell in a TreeView widget.'''
112
113                 layout = pango.Layout(widget.get_pango_context())
114                 layout.set_width(-1) # Do not wrap text.
115
116                 layout.set_text('  ')
117
118                 return layout
119
120         def on_get_size(self, widget, cell_area=None):
121                 layout = self._get_layout(widget)
122                 width, height = layout.get_pixel_size()
123
124                 xpad = 2
125                 ypad = 2
126
127                 xalign = 0
128                 yalign = 0.5
129
130                 x_offset = xpad
131                 y_offset = ypad
132
133                 if cell_area is not None:
134                         x_offset = xalign * (cell_area.width - width)
135                         x_offset = max(x_offset, xpad)
136                         x_offset = int(round(x_offset, 0))
137
138                         y_offset = yalign * (cell_area.height - height)
139                         y_offset = max(y_offset, ypad)
140                         y_offset = int(round(y_offset, 0))
141
142                 width = width + (xpad * 2)
143                 height = height + (ypad * 2)
144
145                 return x_offset, y_offset, width, height
146
147         def on_render(self, window, widget, background_area, cell_area, expose_area, flags ):
148                 self.last_cell = cell_area
149
150                 x = cell_area.x
151                 y = cell_area.y
152                 width = cell_area.width
153                 height = cell_area.height
154
155                 if False:
156                         # This is how it should work but due to theme issues on Maemo, it doesn't work
157                         if widget.state == gtk.STATE_INSENSITIVE:
158                                 state = gtk.STATE_INSENSITIVE
159                         elif flags & gtk.CELL_RENDERER_SELECTED:
160                                 if widget.is_focus():
161                                         state = gtk.STATE_SELECTED
162                                 else:
163                                         state = gtk.STATE_ACTIVE
164                         else:
165                                 state = gtk.STATE_NORMAL
166
167                 if self.status == libliststorehandler.Liststorehandler.SHOW_COMPLETE:
168                         shadow = gtk.SHADOW_IN
169                         state = gtk.STATE_NORMAL
170                 elif self.status == libliststorehandler.Liststorehandler.SHOW_ACTIVE:
171                         shadow = gtk.SHADOW_ETCHED_IN
172                         state = gtk.STATE_NORMAL
173                 elif self.status == libliststorehandler.Liststorehandler.SHOW_NEW:
174                         shadow = gtk.SHADOW_OUT
175                         state = gtk.STATE_SELECTED
176                 else:
177                         raise NotImplementedError(self.status)
178
179                 widget.style.paint_check(window, state, shadow, cell_area, widget, "cellcheck",x,y,width,height)
180
181         def on_activate(self, event, widget, path, background_area, cell_area, flags):
182                 self.emit("status_changed", int(path), "-1")
183                 return False
184
185
186 gobject.type_register(CellRendererTriple)
187
188
189 class View(gtk.VBox):
190
191         def __init__(self, db, liststorehandler, parent_window):
192                 self.db = db
193                 self.parent_window = parent_window
194                 self.liststorehandler = liststorehandler
195
196                 gtk.VBox.__init__(self, homogeneous = False, spacing = 0)
197
198                 logging.info("libview, init")
199
200                 self.scrolled_window = None
201                 self.reload_view()
202
203         def loadList(self):
204                 ls = self.liststorehandler.get_liststore()
205                 self.treeview.set_model(ls)
206
207         def del_active_row(self):
208                 path, col = self.treeview.get_cursor()
209                 if path is not None:
210                         irow = path[0]
211                         row_iter = self.treeview.get_model().get_iter(path)
212                         self.liststorehandler.del_row(irow, row_iter)
213
214         def reload_view(self):
215                 # create the TreeView using liststore
216                 self.modelsort = gtk.TreeModelSort(self.liststorehandler.get_liststore())
217                 self.modelsort.set_sort_column_id(2, gtk.SORT_ASCENDING)
218
219                 self.treeview = gtk.TreeView(self.modelsort)
220                 self.treeview.set_headers_visible(True)
221                 self.treeview.set_reorderable(False)
222
223                 self.cell = range(self.liststorehandler.get_colcount())
224                 self.tvcolumn = range(self.liststorehandler.get_colcount())
225
226                 m = self.liststorehandler.get_unitsstore()
227
228                 for i in range(self.liststorehandler.get_colcount()):
229                         if i in [1, 2]:
230                                 default = "1"
231                         else:
232                                 default = "0"
233                         if self.db.ladeDirekt("showcol_"+str(self.liststorehandler.get_colname(i)), default) == "1":
234                                 if i in [1]:
235                                         # HACK Hildon has theme issues with inconsistent items, so
236                                         # we have a hacked together toggle to make it work on
237                                         # hildon
238                                         if hildonize.IS_HILDON:
239                                                 self.cell[i] = CellRendererTriple()
240                                         else:
241                                                 self.cell[i] = TripleToggleCellRenderer()
242                                         self.cell[i].connect('status_changed', self._on_col_toggled)
243                                         self.tvcolumn[i] = gtk.TreeViewColumn("", self.cell[i])
244                                         self.tvcolumn[i].set_attributes( self.cell[i], status = i)
245                                 elif i in [3, 6]:
246                                         self.cell[i] = gtk.CellRendererCombo()
247                                         self.cell[i].set_property("model", m)
248                                         self.cell[i].set_property('text-column', i)
249                                         self.cell[i].set_property('editable', True)
250                                         self.cell[i].connect("edited", self._on_col_edited, i)
251                                         self.tvcolumn[i] = gtk.TreeViewColumn(
252                                                 self.liststorehandler.get_colname(i), self.cell[i]
253                                         )
254                                         self.tvcolumn[i].set_attributes( self.cell[i], text = i)
255                                 else:
256                                         self.cell[i] = gtk.CellRendererText()
257                                         self.cell[i].set_property('editable', True)
258                                         self.cell[i].set_property('editable-set', True)
259                                         self.cell[i].connect("edited", self._on_col_edited, i)
260                                         self.tvcolumn[i] = gtk.TreeViewColumn(
261                                                 self.liststorehandler.get_colname(i), self.cell[i]
262                                         )
263                                         self.tvcolumn[i].set_attributes(self.cell[i], text = i)
264
265                                 self.tvcolumn[i].set_sort_column_id(i)
266                                 self.tvcolumn[i].set_resizable(True)
267                                 self.treeview.append_column(self.tvcolumn[i])
268
269                 if self.scrolled_window is not None:
270                         self.scrolled_window.destroy()
271
272                 self.scrolled_window = gtk.ScrolledWindow()
273                 self.scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
274
275                 self.scrolled_window.add(self.treeview)
276                 self.pack_start(self.scrolled_window, expand = True, fill = True, padding = 0)
277                 self.loadList()
278
279                 self.show_all()
280
281         @gtk_toolbox.log_exception(_moduleLogger)
282         def _on_col_edited(self, cell, irow, new_text, icol = None):
283                 if (irow != 4):
284                         self.liststorehandler.update_row(irow, icol, new_text)
285                 else:
286                         print cell, irow, new_text, icol
287
288         @gtk_toolbox.log_exception(_moduleLogger)
289         def _on_col_toggled(self, widget, irow, status):
290                 ls = self.treeview.get_model()
291
292                 if self.liststorehandler.get_filter() == self.liststorehandler.SHOW_ACTIVE:
293                         if ls[irow][1] == "0":
294                                 self.liststorehandler.update_row(irow, 1, "1")
295                         else:
296                                 self.liststorehandler.update_row(irow, 1, "0")
297                 else:
298                         if ls[irow][1] == "1":
299                                 self.liststorehandler.update_row(irow, 1, "-1")
300                         elif ls[irow][1] == "0":
301                                 self.liststorehandler.update_row(irow, 1, "1")
302                         else:
303                                 self.liststorehandler.update_row(irow, 1, "0")