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