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