Adding panning
[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.scrolled_window = hildonize.hildonize_scrollwindow(self.scrolled_window)
207
208                 self.reload_view()
209
210         def loadList(self):
211                 ls = self.liststorehandler.get_liststore()
212                 self.treeview.set_model(ls)
213
214         def del_active_row(self):
215                 path, col = self.treeview.get_cursor()
216                 if path is not None:
217                         irow = path[0]
218                         row_iter = self.treeview.get_model().get_iter(path)
219                         self.liststorehandler.del_row(irow, row_iter)
220
221         def reload_view(self):
222                 # create the TreeView using liststore
223                 self.modelsort = gtk.TreeModelSort(self.liststorehandler.get_liststore())
224                 self.modelsort.set_sort_column_id(2, gtk.SORT_ASCENDING)
225
226                 if self.treeview is not None:
227                         self.scrolled_window.remove(self.treeview)
228                 self.treeview = gtk.TreeView(self.modelsort)
229                 self.treeview.set_headers_visible(True)
230                 self.treeview.set_reorderable(False)
231
232                 self.cell = range(self.liststorehandler.get_colcount())
233                 self.tvcolumn = range(self.liststorehandler.get_colcount())
234
235                 m = self.liststorehandler.get_unitsstore()
236
237                 for i in range(self.liststorehandler.get_colcount()):
238                         if i in [1, 2]: # status, title
239                                 default = "1"
240                         else:
241                                 default = "0"
242                         if self.db.ladeDirekt("showcol_"+str(self.liststorehandler.get_colname(i)), default) == "1":
243                                 if i in [1]: # status
244                                         # HACK Hildon has theme issues with inconsistent items, so
245                                         # we have a hacked together toggle to make it work on
246                                         # hildon
247                                         if hildonize.IS_HILDON_SUPPORTED:
248                                                 self.cell[i] = CellRendererTriple()
249                                         else:
250                                                 self.cell[i] = TripleToggleCellRenderer()
251                                         self.cell[i].connect('status_changed', self._on_col_toggled)
252                                         self.tvcolumn[i] = gtk.TreeViewColumn("", self.cell[i])
253                                         self.tvcolumn[i].set_attributes( self.cell[i], status = i)
254                                 elif i in [3, 5]: # quantity, price
255                                         self.cell[i] = gtk.CellRendererSpin()
256                                         adjustment = gtk.Adjustment(0, -sys.float_info.max, sys.float_info.max, 1)
257                                         self.cell[i].set_property('adjustment', adjustment)
258                                         self.cell[i].set_property('digits', 2 if i == 5 else 0)
259                                         self.cell[i].set_property('editable', True)
260                                         self.cell[i].connect("edited", self._on_col_edited, i)
261                                         self.tvcolumn[i] = gtk.TreeViewColumn(
262                                                 self.liststorehandler.get_colname(i), self.cell[i]
263                                         )
264                                         self.tvcolumn[i].set_attributes( self.cell[i], text = i)
265                                 elif i in [4, 6]: # unit, priority
266                                         self.cell[i] = gtk.CellRendererCombo()
267                                         self.cell[i].set_property("model", m)
268                                         self.cell[i].set_property('text-column', i)
269                                         self.cell[i].set_property('editable', True)
270                                         self.cell[i].connect("edited", self._on_col_edited, i)
271                                         self.tvcolumn[i] = gtk.TreeViewColumn(
272                                                 self.liststorehandler.get_colname(i), self.cell[i]
273                                         )
274                                         self.tvcolumn[i].set_attributes( self.cell[i], text = i)
275                                 else:
276                                         self.cell[i] = gtk.CellRendererText()
277                                         self.cell[i].set_property('editable', True)
278                                         self.cell[i].set_property('editable-set', True)
279                                         self.cell[i].connect("edited", self._on_col_edited, i)
280                                         self.tvcolumn[i] = gtk.TreeViewColumn(
281                                                 self.liststorehandler.get_colname(i), self.cell[i]
282                                         )
283                                         self.tvcolumn[i].set_attributes(self.cell[i], text = i)
284
285                                 self.tvcolumn[i].set_sort_column_id(i)
286                                 self.tvcolumn[i].set_resizable(True)
287                                 self.treeview.append_column(self.tvcolumn[i])
288
289                 self.scrolled_window.add(self.treeview)
290                 self.loadList()
291
292                 self.show_all()
293
294         @gtk_toolbox.log_exception(_moduleLogger)
295         def _on_col_edited(self, cell, irow, new_text, icol = None):
296                 if (irow != 4):
297                         self.liststorehandler.update_row(irow, icol, new_text)
298                 else:
299                         print cell, irow, new_text, icol
300
301         @gtk_toolbox.log_exception(_moduleLogger)
302         def _on_col_toggled(self, widget, irow, status):
303                 ls = self.treeview.get_model()
304
305                 if self.liststorehandler.get_filter() == self.liststorehandler.SHOW_ACTIVE:
306                         if ls[irow][1] == "0":
307                                 self.liststorehandler.update_row(irow, 1, "1")
308                         else:
309                                 self.liststorehandler.update_row(irow, 1, "0")
310                 else:
311                         if ls[irow][1] == "1":
312                                 self.liststorehandler.update_row(irow, 1, "-1")
313                         elif ls[irow][1] == "0":
314                                 self.liststorehandler.update_row(irow, 1, "1")
315                         else:
316                                 self.liststorehandler.update_row(irow, 1, "0")