~vorlon/ubuntu/saucy/gourmet/trunk

« back to all changes in this revision

Viewing changes to src/lib/plugins/key_editor/keyEditor.py

  • Committer: Bazaar Package Importer
  • Author(s): Rolf Leggewie
  • Date: 2008-07-26 13:29:41 UTC
  • Revision ID: james.westby@ubuntu.com-20080726132941-6ldd73qmacrzz0bn
Tags: upstream-0.14.0
ImportĀ upstreamĀ versionĀ 0.14.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
import gtk, gtk.glade, gobject, re, os, os.path
 
2
from gourmet import gglobals, convert
 
3
from gourmet.gtk_extras import WidgetSaver, mnemonic_manager, pageable_store
 
4
from gourmet.gtk_extras import cb_extras as cb
 
5
from gourmet.gtk_extras import dialog_extras as de
 
6
from gettext import gettext as _
 
7
from gettext import ngettext
 
8
#import nutrition.nutritionDruid as nutritionDruid
 
9
 
 
10
try:
 
11
    current_path = os.path.split(os.path.join(os.getcwd(),__file__))[0]
 
12
except:
 
13
    current_path = ''
 
14
 
 
15
class KeyEditor:
 
16
 
 
17
    """KeyEditor sets up a GUI to allow editing which keys correspond to which items throughout
 
18
    the recipe database. It is useful for corrections or changes to keys en masse.
 
19
    """
 
20
    
 
21
    def __init__ (self, rd=None, rg=None):
 
22
        self.glade = gtk.glade.XML(os.path.join(current_path,'keyeditor.glade'))
 
23
        self.rd = rd
 
24
        self.rg = rg
 
25
        self.widget_names = ['treeview', 'searchByBox', 'searchEntry', 'searchButton', 'window',
 
26
                             'searchAsYouTypeToggle', 'regexpTog',
 
27
                             'changeKeyEntry',
 
28
                             'changeItemEntry',
 
29
                             'changeUnitEntry',
 
30
                             'changeAmountEntry',
 
31
                             'applyEntriesButton',
 
32
                             'clearEntriesButton']
 
33
        for w in self.widget_names:
 
34
            setattr(self,w,self.glade.get_widget(w))
 
35
        self.entries = {'ingkey':self.changeKeyEntry,
 
36
                        'item':self.changeItemEntry,
 
37
                        'unit':self.changeUnitEntry,
 
38
                        'amount':self.changeAmountEntry,
 
39
                        }
 
40
        # setup entry callback to sensitize/desensitize apply
 
41
        self.applyEntriesButton.set_sensitive(False)
 
42
        self.clearEntriesButton.set_sensitive(False)
 
43
        for e in self.entries.values():
 
44
            e.connect('changed',self.entryChangedCB)
 
45
        # Make our lovely model
 
46
        self.makeTreeModel()
 
47
        # setup completion in entry
 
48
        cb.make_completion(self.changeKeyEntry,self.rg.inginfo.key_model)
 
49
        # Setup next/prev/first/last buttons for view
 
50
        self.prev_button = self.glade.get_widget('prevButton')
 
51
        self.next_button = self.glade.get_widget('nextButton')
 
52
        self.first_button = self.glade.get_widget('firstButton')
 
53
        self.last_button = self.glade.get_widget('lastButton')
 
54
        self.showing_label = self.glade.get_widget('showingLabel')
 
55
        self.prev_button.connect('clicked',lambda *args: self.treeModel.prev_page())
 
56
        self.next_button.connect('clicked',lambda *args: self.treeModel.next_page())
 
57
        self.first_button.connect('clicked',lambda *args: self.treeModel.goto_first_page())
 
58
        self.last_button.connect('clicked',lambda *args: self.treeModel.goto_last_page())
 
59
        # Setup search stuff
 
60
        self.search_string=""
 
61
        self.search_by = _('Key')
 
62
        self.use_regexp=True
 
63
        self.setupTreeView()
 
64
        self.treeview.set_model(self.treeModel)
 
65
        self.treeview.get_selection().set_mode(gtk.SELECTION_MULTIPLE)
 
66
        #self.treeview.set_model(self.treeModel)
 
67
        self.glade.signal_autoconnect({
 
68
            'iSearch':self.isearchCB,
 
69
            'search':self.searchCB,
 
70
            'search_as_you_type_toggle':self.search_as_you_typeCB,
 
71
            'applyEntries':self.applyEntriesCB,
 
72
            'clearEntries':self.clearEntriesCB,
 
73
            'close_window': lambda *args: self.window.hide(),
 
74
            'editNutritionalInfo':self.editNutritionalInfoCB,
 
75
            })
 
76
        # setup mnemonic manager
 
77
        self.mm = mnemonic_manager.MnemonicManager()
 
78
        self.mm.sacred_cows.append('search for')
 
79
        self.mm.add_glade(self.glade)
 
80
        self.mm.add_treeview(self.treeview)
 
81
        self.mm.fix_conflicts_peacefully()
 
82
        # to set our regexp_toggled variable
 
83
        cb.set_model_from_list(self.searchByBox, [_('key'),_('item'),_('unit')])
 
84
        self.searchByBox.set_active(0)
 
85
        self.dont_ask = self.rg.prefs.get('dontAskDeleteKey',False)
 
86
        # setup WidgetSavers
 
87
        self.rg.conf.append(WidgetSaver.WidgetSaver(
 
88
            self.searchAsYouTypeToggle,
 
89
            self.rg.prefs.get('sautTog',
 
90
                           {'active':self.searchAsYouTypeToggle.get_active()}),
 
91
            ['toggled']))
 
92
        self.rg.conf.append(WidgetSaver.WidgetSaver(
 
93
            self.regexpTog,
 
94
            self.rg.prefs.get('regexpTog',
 
95
                           {'active':self.regexpTog.get_active()}),
 
96
            ['toggled']))
 
97
                
 
98
    def dont_ask_cb (self, widget, *args):
 
99
        self.dont_ask=widget.get_active()
 
100
        self.rg.prefs['dontAskDeleteKey']=self.dont_ask
 
101
    
 
102
    def setupTreeView (self):
 
103
        self.FIELD_COL = 1
 
104
        self.VALUE_COL = 2
 
105
        self.COUNT_COL = 3
 
106
        self.REC_COL = 4
 
107
        self.NUT_COL = 5
 
108
        for n,head in [[self.FIELD_COL,_('Field')],
 
109
                       [self.VALUE_COL,_('Value')],
 
110
                       [self.COUNT_COL,_('Count')],
 
111
                       [self.REC_COL, _('Recipes')],
 
112
                       [self.NUT_COL, _('Nutritional Info')],
 
113
                       ]:
 
114
            if n == self.NUT_COL:
 
115
                renderer = gtk.CellRendererToggle()
 
116
            else:
 
117
                renderer = gtk.CellRendererText()
 
118
            # If we have gtk > 2.8, set up text-wrapping
 
119
            try:
 
120
                renderer.get_property('wrap-width')
 
121
            except TypeError:
 
122
                pass
 
123
            else:
 
124
                renderer.set_property('wrap-mode',gtk.WRAP_WORD)
 
125
                if n == self.FIELD_COL:
 
126
                    renderer.set_property('wrap-width',60)
 
127
                elif n in [self.VALUE_COL,self.REC_COL]: renderer.set_property('wrap-width',250)
 
128
                else: renderer.set_property('wrap-width',100)
 
129
            if n==self.VALUE_COL:
 
130
                renderer.set_property('editable',True)
 
131
                renderer.connect('edited',self.tree_edited,n,head)
 
132
            if n == self.NUT_COL:
 
133
                col = gtk.TreeViewColumn(head, renderer, active=n, visible=n)
 
134
            else:
 
135
                col = gtk.TreeViewColumn(head, renderer, text=n)
 
136
            if n == self.VALUE_COL:
 
137
                col.set_property('expand',True)
 
138
            col.set_resizable(True)
 
139
            self.treeview.append_column(col)
 
140
 
 
141
 
 
142
    def tree_edited (self, renderer, path_string, text, n, head):
 
143
        indices = path_string.split(':')
 
144
        path = tuple( map(int, indices))
 
145
        itr = self.treeModel.get_iter(path)
 
146
        curdic,field = self.get_dic_describing_iter(itr)
 
147
        value = curdic[field]
 
148
        if value == text: return
 
149
        if field=='ingkey':
 
150
            key = curdic['ingkey']
 
151
            if de.getBoolean(label=_('Change all keys "%s" to "%s"?')%(key,text),
 
152
                             sublabel=_("You won't be able to undo this action. If there are already ingredients with the key \"%s\", you won't be able to distinguish between those items and the items you are changing now."%text)
 
153
                             ):
 
154
                self.rd.update(
 
155
                    self.rd.ingredients_table,
 
156
                    curdic,
 
157
                    {'ingkey':text}
 
158
                    )
 
159
                self.rd.delete_by_criteria(
 
160
                    self.rd.keylookup_table,
 
161
                    {'ingkey':key}
 
162
                    )
 
163
        elif field=='item':
 
164
            if de.getBoolean(label=_('Change all items "%s" to "%s"?')%(curdic['item'],text),
 
165
                             sublabel=_("You won't be able to undo this action. If there are already ingredients with the item \"%s\", you won't be able to distinguish between those items and the items you are changing now.")%text
 
166
                             ):
 
167
                self.rd.update(
 
168
                    self.rd.ingredients_table,
 
169
                    curdic,
 
170
                    {'item':text}
 
171
                    ) 
 
172
        elif field=='unit':
 
173
            unit = curdic['unit']; key = curdic['ingkey']; item = curdic['item']
 
174
            val = de.getRadio(label='Change unit',
 
175
                                options=[
 
176
                [_('Change _all instances of "%(unit)s" to "%(text)s"')%locals(),1],
 
177
                [_('Change "%(unit)s" to "%(text)s" only for _ingredients "%(item)s" with key "%(key)s"')%locals(),2],
 
178
                ],
 
179
                              default = 2,
 
180
                              )
 
181
            if val==1:
 
182
                self.rd.update(
 
183
                    self.rd.ingredients_table,
 
184
                    {'unit':unit},
 
185
                    {'unit':text},
 
186
                    )
 
187
            elif val==2:
 
188
                self.rd.update(
 
189
                    self.rd.ingredients_table,
 
190
                    curdic,
 
191
                    {'unit':text}
 
192
                    )
 
193
        elif field=='amount':
 
194
            amount = curdic['amount']; unit = curdic['unit']; key = curdic['ingkey']; item = curdic['item']
 
195
            try:
 
196
                new_amount = convert.frac_to_float(text)
 
197
            except:
 
198
                de.show_amount_error(text)
 
199
                return
 
200
            val = de.getRadio(label='Change amount',
 
201
                        options=[
 
202
                [_('Change _all instances of "%(amount)s" %(unit)s to %(text)s %(unit)s')%locals(),1],
 
203
                [_('Change "%(amount)s" %(unit)s to "%(text)s" %(unit)s only _where the ingredient key is %(key)s')%locals(),2],
 
204
                [_('Change "%(amount)s" %(unit)s to "%(text)s" %(unit)s only where the ingredient key is %(key)s _and where the item is %(item)s')%locals(),3],
 
205
                ],
 
206
                default=3,
 
207
                              )
 
208
            if val == 1:
 
209
                cond = {'unit':unit,'amount':amount}
 
210
            elif val == 2:
 
211
                cond = {'unit':unit,'amount':amount,'ingkey':key}
 
212
            elif val == 3:
 
213
                cond = curdic
 
214
            self.rd.update(
 
215
                self.rd.ingredients_table,
 
216
                {'unit':unit,'amount':convert.frac_to_float(amount)},
 
217
                {'unit':unit,'amount':new_amount}
 
218
                )
 
219
        else:
 
220
            return
 
221
        self.treeModel.set_value(itr, n, text)
 
222
        return
 
223
 
 
224
    def makeTreeModel (self):
 
225
        self.treeModel = KeyStore(self.rd,per_page=self.rg.prefs.get('recipes_per_page',12))
 
226
        #self.orig_view = self.treeModel.view
 
227
        self.treeModel.connect('page-changed',self.model_changed_cb)
 
228
        self.treeModel.connect('view-changed',self.model_changed_cb)
 
229
        
 
230
    def resetTree (self):
 
231
        self.search_string = 'NO ONE WOULD EVER SEARCH FOR THIS HACKISH STRING'
 
232
        curpage = self.treeModel.page
 
233
        self.doSearch()
 
234
        self.treeModel.set_page(curpage)
 
235
 
 
236
    def doSearch (self):
 
237
        """Do the actual searching."""
 
238
        last_search = self.search_string
 
239
        self.search_string = self.searchEntry.get_text()
 
240
        last_by = self.search_by
 
241
        self.search_by = cb.cb_get_active_text(self.searchByBox)
 
242
        last_regexp = self.use_regexp
 
243
        self.use_regexp = self.regexpTog.get_active()
 
244
        if (self.search_by==last_by and
 
245
            self.search_string==last_search and
 
246
            self.use_regexp==last_regexp):
 
247
            # Don't do anything...
 
248
            return
 
249
        # RESET THE VIEW IF NEED BE
 
250
        if (self.search_string.find(last_search)!=0 or
 
251
            self.search_by != last_by or
 
252
            self.use_regexp != last_regexp):
 
253
            self.treeModel.reset_views()
 
254
        if self.search_by == _('item'):
 
255
            self.treeModel.limit_on_ingkey(self.search_string,
 
256
                                           search_options={'use_regexp':self.use_regexp,}
 
257
                                           )
 
258
        elif self.search_by == _('key'):
 
259
            self.treeModel.limit_on_item(self.search_string,
 
260
                                         search_options={'use_regexp':self.use_regexp})
 
261
        else: # self.search_by == _('unit'):
 
262
            self.treeModel.limit(self.search_string,
 
263
                                 'unit',
 
264
                                 search_options={'use_regexp':self.use_regexp}
 
265
                                 )
 
266
 
 
267
    def isearchCB (self, *args):
 
268
        if self.searchAsYouTypeToggle.get_active():
 
269
            self.doSearch()
 
270
 
 
271
    def searchCB (self, *args):
 
272
        self.doSearch()
 
273
 
 
274
    def search_as_you_typeCB (self, *args):
 
275
        if self.searchAsYouTypeToggle.get_active():
 
276
            self.searchButton.hide()
 
277
        else: self.searchButton.show()
 
278
 
 
279
    
 
280
    def clearEntriesCB (self, *args):
 
281
        for e in self.entries.values(): e.set_text('')
 
282
 
 
283
    def get_dic_describing_iter (self, itr):
 
284
        """Handed an itr in our tree, return a dictionary describing
 
285
        that row and the field described.
 
286
 
 
287
        For example, if we get the row
 
288
 
 
289
        KEY: Foo
 
290
 
 
291
        We return {'ingkey':'Foo'},'ingkey'
 
292
 
 
293
        If we get the row
 
294
 
 
295
        KEY: Foo
 
296
         |=> ITEM: Bar
 
297
 
 
298
        We return {'ingkey':'Foo','item':'Bar'},'item'
 
299
        """
 
300
        field = self.treeModel.get_value(itr, self.FIELD_COL)
 
301
        value = self.treeModel.get_value(itr, self.VALUE_COL)
 
302
        if field==self.treeModel.KEY:
 
303
            return {'ingkey':value},'ingkey'
 
304
        elif field==self.treeModel.ITEM:
 
305
            key = self.treeModel.get_value(
 
306
                self.treeModel.iter_parent(itr),
 
307
                self.VALUE_COL
 
308
                )
 
309
            return {'ingkey':key,'item':value},'item'
 
310
        elif field==self.treeModel.UNIT:
 
311
            item_itr = self.treeModel.iter_parent(itr)
 
312
            key_itr = self.treeModel.iter_parent(item_itr)
 
313
            item = self.treeModel.get_value(item_itr,self.VALUE_COL)
 
314
            key = self.treeModel.get_value(key_itr,self.VALUE_COL)
 
315
            unit = value
 
316
            return {'ingkey':key,'item':item,'unit':unit},'unit'
 
317
        elif field==self.treeModel.AMOUNT:
 
318
            unit_itr = self.treeModel.iter_parent(itr)
 
319
            item_itr = self.treeModel.iter_parent(unit_itr)
 
320
            key_itr = self.treeModel.iter_parent(item_itr)
 
321
            unit = self.treeModel.get_value(unit_itr,self.VALUE_COL)
 
322
            item = self.treeModel.get_value(item_itr,self.VALUE_COL)
 
323
            key = self.treeModel.get_value(key_itr,self.VALUE_COL)
 
324
            amount = value
 
325
            return {'ingkey':key,'item':item,'unit':unit,'amount':amount},'amount'
 
326
        else:
 
327
            print 'WTF! WE SHOULD NEVER LAND HERE!',field,value
 
328
            raise 'WTF ERROR'
 
329
            
 
330
    def applyEntriesCB (self, *args):
 
331
        newdic = {}
 
332
        for k,e in self.entries.items():
 
333
            txt = e.get_text()
 
334
            if txt:
 
335
                if k=='amount':
 
336
                    try:
 
337
                        newdic[k]=convert.frac_to_float(txt)
 
338
                    except:
 
339
                        de.show_amount_error(txt)
 
340
                        return
 
341
                else:
 
342
                    newdic[k]=txt
 
343
        if not newdic:
 
344
            print 'We called applyEntriesCB with no text -- that shouldn\'t be possible'
 
345
            return
 
346
        mod,rows = self.treeview.get_selection().get_selected_rows()
 
347
        if not de.getBoolean(
 
348
        label=_("Change all selected rows?"),
 
349
        sublabel=(_('This action will not be undoable. Are you that for all %s selected rows, you want to set the following values:')%len(rows)
 
350
        + (newdic.has_key('ingkey') and _('\nKey to %s')%newdic['ingkey'] or '')
 
351
        + (newdic.has_key('item') and _('\nItem to %s')%newdic['item'] or '')
 
352
        + (newdic.has_key('unit') and _('\nUnit to %s')%newdic['unit'] or '')
 
353
        + (newdic.has_key('amount') and _('\nAmount to %s')%newdic['amount'] or ''))):
 
354
            return
 
355
        # Now actually apply our lovely new logic...
 
356
        changed_iters = True
 
357
        updated_iters = []
 
358
        for path in rows:
 
359
            itr=self.treeModel.get_iter(path)
 
360
            # We check to see if we've updated the parent of our iter,
 
361
            # in which case the changes would already be inherited by
 
362
            # the current row (i.e. if the tree has been expanded and
 
363
            # all rows have been selected).
 
364
            parent = mod.iter_parent(itr); already_updated = False
 
365
            while parent:
 
366
                if parent in updated_iters:
 
367
                    already_updated = True
 
368
                else:
 
369
                    parent = mod.iter_parent(parent)
 
370
            if already_updated: continue
 
371
            # Now that we're sure we really need to update...
 
372
            curdic,field = self.get_dic_describing_iter(itr)
 
373
            curkey = self.treeModel.get_value(itr,self.VALUE_COL)
 
374
            if not already_updated:
 
375
                self.rd.update(
 
376
                    self.rd.ingredients_table,
 
377
                    curdic,
 
378
                    newdic,
 
379
                    )
 
380
                if curdic.has_key('ingkey') and newdic.has_key('ingkey'):
 
381
                    self.rd.delete_by_criteria(
 
382
                        self.rd.keylookup_table,
 
383
                        {'ingkey':curdic['ingkey']}
 
384
                        )
 
385
        self.resetTree()
 
386
            #self.update_iter(itr,newdic) # A recursive method that
 
387
            #                             # will set values for us and
 
388
            #                             # our children as necessary
 
389
            #updated_iters.append(itr) 
 
390
 
 
391
    def editNutritionalInfoCB (self, *args):
 
392
        nid = nutritionDruid.NutritionInfoDruid(self.rg.nd, self.rg.prefs)
 
393
        mod,rows = self.treeview.get_selection().get_selected_rows()
 
394
        keys_to_update = {}
 
395
        for path in rows:
 
396
            itr = mod.get_iter(path)
 
397
            # Climb to the key-level for each selection -- we don't
 
398
            # care about anything else.
 
399
            parent = mod.iter_parent(itr)
 
400
            while parent:
 
401
                itr = parent
 
402
                parent = mod.iter_parent(itr)
 
403
            curkey = mod.get_value(itr,self.VALUE_COL)
 
404
            #if mod.get_value(itr,self.NUT_COL):
 
405
            #    print "We can't yet edit nutritional information..."
 
406
            #else:
 
407
            if True:
 
408
                keys_to_update[curkey]=[]
 
409
                child = mod.iter_children(itr)
 
410
                while child:
 
411
                    grandchild = mod.iter_children(child)
 
412
                    while grandchild:
 
413
                        # Grand children are units...
 
414
                        unit = mod.get_value(grandchild,self.VALUE_COL)
 
415
                        amounts = []
 
416
                        greatgrandchild = mod.iter_children(grandchild)
 
417
                        while greatgrandchild:
 
418
                            amount = mod.get_value(
 
419
                                greatgrandchild,
 
420
                                self.VALUE_COL
 
421
                                )
 
422
                            keys_to_update[curkey].append((convert.frac_to_float(amount),unit))
 
423
                            greatgrandchild = mod.iter_next(greatgrandchild)
 
424
                        grandchild = mod.iter_next(grandchild)
 
425
                    child = mod.iter_next(child)
 
426
                nid.add_ingredients(keys_to_update.items())
 
427
                nid.connect('finish',self.update_nutinfo)
 
428
                nid.show()
 
429
            
 
430
    def update_nutinfo (self, *args):
 
431
        print 'KeyEditor.update_nutinfo'
 
432
        self.treeModel.reset_views()
 
433
 
 
434
    def update_iter (self, itr, newdic):
 
435
        """Update iter and its children based on values in newdic"""
 
436
        field = self.treeModel.get_value(itr,self.FIELD_COL)
 
437
        if newdic.has_key('item') and field==self.treeModel.ITEM:
 
438
            self.treeModel.set_value(itr,self.VALUE_COL,newdic['item'])
 
439
        elif newdic.has_key('ingkey') and field==self.treeModel.KEY:
 
440
            self.treeModel.set_value(itr,self.VALUE_COL,newdic['ingkey'])
 
441
        elif newdic.has_key('unit') and field==self.treeModel.UNIT:
 
442
            self.treeModel.set_value(itr,self.VALUE_COL,newdic['unit'])
 
443
        elif newdic.has_key('amount') and field==self.treeModel.AMOUNT:
 
444
            self.treeModel.set_value(itr,self.VALUE_COL,newdic['amount'])
 
445
        c = self.treeModel.iter_children(itr)
 
446
        while c:
 
447
            self.update_iter(c,newdic)
 
448
            c = self.treeModel.iter_next(c)
 
449
        
 
450
    def entryChangedCB (self, *args):
 
451
        """Set sensitivity of apply and clear buttons.
 
452
 
 
453
        We are sensitive if we have text to apply or clear"""
 
454
        for e in self.entries.values():
 
455
            if e.get_text():
 
456
                self.applyEntriesButton.set_sensitive(True)
 
457
                self.clearEntriesButton.set_sensitive(True)
 
458
                return
 
459
        self.applyEntriesButton.set_sensitive(False)
 
460
        self.clearEntriesButton.set_sensitive(False)
 
461
 
 
462
    def reset_tree (self):
 
463
        self.treeModel.reset_views()
 
464
        self.search_by = None
 
465
        self.search_string = ''
 
466
        self.doSearch()
 
467
 
 
468
    # Paging handlers
 
469
    def model_changed_cb (self, model):
 
470
        if model.page==0:
 
471
            self.prev_button.set_sensitive(False)
 
472
            self.first_button.set_sensitive(False)
 
473
        else:
 
474
            self.prev_button.set_sensitive(True)
 
475
            self.first_button.set_sensitive(True)
 
476
        if model.get_last_page()==model.page:
 
477
            self.next_button.set_sensitive(False)
 
478
            self.last_button.set_sensitive(False)
 
479
        else:
 
480
            self.next_button.set_sensitive(True)
 
481
            self.last_button.set_sensitive(True)
 
482
        self.update_showing_label()
 
483
        
 
484
    def update_showing_label (self):
 
485
        bottom,top,total = self.treeModel.showing()
 
486
        if top >= total and bottom==1:
 
487
            lab = ngettext('%s ingredient','%s ingredients',top)%top
 
488
        else:
 
489
            # Do not translate bottom, top and total -- I use these fancy formatting
 
490
            # strings in case your language needs the order changed!
 
491
            lab = _('Showing ingredients %(bottom)s to %(top)s of %(total)s'%locals())
 
492
        self.showing_label.set_markup('<i>' + lab + '</i>')
 
493
 
 
494
 
 
495
class KeyStore (pageable_store.PageableTreeStore,pageable_store.PageableViewStore):
 
496
    """A ListStore to show our beautiful keys.
 
497
    """
 
498
    __gsignals__ = {
 
499
        'view-changed':(gobject.SIGNAL_RUN_LAST,
 
500
                        gobject.TYPE_NONE,
 
501
                        ()),
 
502
        }
 
503
 
 
504
    KEY = _('Key')+':'
 
505
    ITEM = _('Item')+':'
 
506
    UNIT = _('Unit')+':'
 
507
    AMOUNT = _('Amount')+':'    
 
508
    
 
509
    columns = ['obj','ingkey','item','count','recipe','ndbno']
 
510
    def __init__ (self, rd, per_page=15):
 
511
        self.rd = rd
 
512
        pageable_store.PageableTreeStore.__init__(self,
 
513
                                                  [gobject.TYPE_PYOBJECT, # row ref
 
514
                                                   str, # column
 
515
                                                   str, # value
 
516
                                                   int, # count
 
517
                                                   str, # recipe
 
518
                                                   int, # nutritional information equivalent
 
519
                                                   ],
 
520
                                                  per_page=per_page)
 
521
 
 
522
    def reset_views (self):
 
523
        self.view = self.rd.get_ingkeys_with_count()
 
524
        for n in range(self._get_length_()):
 
525
            parent = (n,)
 
526
            path = parent
 
527
            try:
 
528
                itr = self.get_iter(path)
 
529
            except ValueError:
 
530
                print 'Trouble with path',path
 
531
                return
 
532
            self.emit('row-changed',path,itr)
 
533
            child = self.iter_children(itr)
 
534
            while child:
 
535
                path = self.get_path(child)
 
536
                self.emit('row-changed',path,child)
 
537
                child = self.iter_next(child)
 
538
        #self.keylookup_table = self.rd.filter(self.rd.keylookup_table,lambda row: row.item)
 
539
        # Limit ingredients_table to ingkeys only, then select the unique values of that, then
 
540
        # filter ourselves to values that have keys
 
541
        #self.view = self.rd.filter(self.rd.ingredients_table.project(self.rd.ingredients_table.ingkey).unique(),
 
542
        #                           lambda foo: foo.ingkey)
 
543
 
 
544
    def _setup_parent_ (self, *args, **kwargs):
 
545
        self.reset_views()
 
546
 
 
547
    def limit_on_ingkey (self, txt, search_options={}):
 
548
        self.limit(txt,self.rd.ingredients_table+'.ingkey',search_options)
 
549
 
 
550
    def limit_on_item (self, txt, search_options={}):
 
551
        self.limit(txt,'item',search_options)
 
552
 
 
553
    def limit (self, txt, column='ingkey', search_options={}):
 
554
        if not txt: self.reset_views()
 
555
        if search_options['use_regexp']:
 
556
            s = {'search':txt,'operator':'REGEXP'}
 
557
        else:
 
558
            s = {'search':'%'+txt.replace('%','%%')+'%','operator':'LIKE'}
 
559
        s['column']=column
 
560
        self.change_view(self.rd.get_ingkeys_with_count(s))
 
561
      
 
562
    def _get_length_ (self):
 
563
        return len(self.view)
 
564
 
 
565
    get_last_page = pageable_store.PageableViewStore.get_last_page
 
566
 
 
567
    def _get_slice_ (self, bottom, top):
 
568
        return [self.get_row(i) for i in self.view[bottom:top]]
 
569
 
 
570
    def _get_item_ (self, path):
 
571
        return self.get_row(self.view[indx])
 
572
        
 
573
    def get_row (self, row):
 
574
        return [row,
 
575
                self.KEY,
 
576
                row.ingkey,
 
577
                # avoidable slowdown (look here if code seems sluggish)
 
578
                row.count,
 
579
                None,
 
580
                row.ndbno or 0,
 
581
                ]
 
582
 
 
583
    def _get_children_ (self,itr):
 
584
        ret = []
 
585
        field = self.get_value(itr,1)
 
586
        value = self.get_value(itr,2)
 
587
        if field==self.KEY:
 
588
            ingkey = value
 
589
            for item in self.rd.get_unique_values('item',self.rd.ingredients_table,ingkey=ingkey):
 
590
                ret.append([None,
 
591
                            self.ITEM,
 
592
                            item,
 
593
                            self.rd.fetch_len(self.rd.ingredients_table,ingkey=ingkey,item=item),
 
594
                            self.get_recs(ingkey,item),
 
595
                            0
 
596
                            ])
 
597
        elif field==self.ITEM:
 
598
            ingkey = self.get_value(self.iter_parent(itr),2)
 
599
            item = value
 
600
            for unit in self.rd.get_unique_values('unit',self.rd.ingredients_table,ingkey=ingkey,item=item):
 
601
                ret.append([None,
 
602
                            self.UNIT,
 
603
                            unit,
 
604
                            self.rd.fetch_len(self.rd.ingredients_table,ingkey=ingkey,item=item,unit=unit),
 
605
                            None,
 
606
                            0])
 
607
            if not ret:
 
608
                ret.append([None,
 
609
                            self.UNIT,
 
610
                            '',
 
611
                            self.get_value(self.iter_parent(itr),3),
 
612
                            None,
 
613
                            0])                
 
614
        elif field==self.UNIT:
 
615
            item = self.get_value(self.iter_parent(itr),2)
 
616
            ingkey = self.get_value(self.iter_parent(
 
617
                self.iter_parent(itr)),2)
 
618
            unit = self.get_value(itr,2)
 
619
            amounts = []
 
620
            for i in self.rd.fetch_all(self.rd.ingredients_table,ingkey=ingkey,item=item,unit=unit):
 
621
                astring = self.rd.get_amount_as_string(i)
 
622
                if astring in amounts: continue
 
623
                ret.append([None,
 
624
                            self.AMOUNT,
 
625
                            astring,
 
626
                            (i.rangeamount
 
627
                             and self.rd.fetch_len(self.rd.ingredients_table,
 
628
                                                   ingkey=ingkey,item=item,
 
629
                                                   unit=unit,
 
630
                                                   amount=i.amount,rangeamount=i.rangeamount)
 
631
                             or  self.rd.fetch_len(self.rd.ingredients_table,
 
632
                                                   ingkey=ingkey,item=item,
 
633
                                                   unit=unit,
 
634
                                                   amount=i.amount)),
 
635
                            None,
 
636
                            0])
 
637
                amounts.append(astring)
 
638
            if not ret:
 
639
                ret.append([None,
 
640
                            self.AMOUNT,
 
641
                            '',
 
642
                            self.get_value(self.iter_parent(itr),3),
 
643
                            None,
 
644
                            0])
 
645
            
 
646
        return ret
 
647
        #row = row[0]
 
648
        #return [[subrow,
 
649
        #         row.ingkey,
 
650
        #         subrow.item,
 
651
        #         subrow.count,
 
652
        #         self.get_recs(row.ingkey,subrow.item)] for subrow in row.grouped]
 
653
 
 
654
 
 
655
    def get_recs (self, key, item):
 
656
        """Return a string with a list of recipes containing an ingredient with key and item"""
 
657
        recs = [i.id for i in self.rd.fetch_all(self.rd.ingredients_table,ingkey=key,item=item)]
 
658
        titles = []
 
659
        looked_at = []
 
660
        for r_id in recs:
 
661
            if r_id in looked_at: continue
 
662
            rec = self.rd.get_rec(r_id)
 
663
            if rec:
 
664
                titles.append(rec.title)
 
665
        return ", ".join(titles)
 
666
 
 
667
if gtk.pygtk_version[1]<8:
 
668
    gobject.type_register(KeyStore)    
 
669
 
 
670
if __name__ == '__main__':
 
671
    import recipeManager
 
672
    rm = recipeManager.default_rec_manager()
 
673
    import sys
 
674
    sys.path.append(os.path.realpath('../tests'))
 
675
    import testExtras
 
676
    rg = testExtras.FakeRecGui(rm)
 
677
    ke=KeyEditor(rm,rg)
 
678
    ke.window.connect('delete-event',gtk.main_quit)
 
679
    gtk.main()