~vorlon/ubuntu/saucy/gourmet/trunk

« back to all changes in this revision

Viewing changes to src/lib/valueEditor.py

  • Committer: Bazaar Package Importer
  • Author(s): Rolf Leggewie
  • Date: 2009-02-10 17:34:51 UTC
  • mfrom: (2.1.4 squeeze)
  • Revision ID: james.westby@ubuntu.com-20090210173451-bt0a6j0ut71oxqes
Tags: 0.14.5-1
* new upstream release
* 0.14.5 no longer conflicts with newer versions of python-pysqlite and
  python-sqlalchemy.  Relax dependencies accordingly.
* all patches have been pushed upstream, so they can be dropped in Debian.
  The manpage was forgotten when preparing the tarball upstream, so it
  will stick around for another release.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
import gtk, gtk.glade, gobject
2
 
import gglobals, convert, os.path
3
 
from gtk_extras import cb_extras as cb
4
 
from gtk_extras import dialog_extras as de
5
 
from gettext import ngettext
6
 
from gettext import gettext as _
7
 
 
8
 
 
9
 
class ValueEditor:
10
 
    """A generic "value" editor for mucking about with the database.
11
 
    """
12
 
 
13
 
    values = 'category','cuisine','source','link'
14
 
 
15
 
    def __init__ (self, rd, rg):
16
 
        self.field = None; self.other_field = None
17
 
        self.rd = rd; self.rg = rg
18
 
        self.glade = gtk.glade.XML(os.path.join(gglobals.gladebase,'valueEditor.glade'))
19
 
        self.__setup_widgets__()
20
 
        self.__setup_treeview__()
21
 
        self.glade.signal_autoconnect({
22
 
            'on_changeValueButton_toggled':self.changeValueButtonToggledCB,
23
 
            'on_fieldToEditCombo_changed':self.fieldChangedCB,
24
 
            'on_otherChangeCheckButton_toggled':self.otherChangeToggleCB,
25
 
            'on_otherExpander_activate':self.otherChangeToggleCB,
26
 
            'on_otherFieldCombo_changed':self.otherFieldComboChangedCB,
27
 
            })
28
 
        
29
 
    def __setup_widgets__ (self):
30
 
        for w in [
31
 
            'valueDialog',
32
 
            'treeview',
33
 
            'fieldToEditCombo','newValueComboBoxEntry',
34
 
            'newValueEntry','changeValueButton',
35
 
            'deleteValueButton','forEachLabel',
36
 
            'otherExpander','otherFieldCombo',
37
 
            'otherNewValueEntry','otherNewValueComboBoxEntry',
38
 
            'otherValueBlurbLabel','otherChangeCheckButton',
39
 
            'leaveValueButton'
40
 
            ]:
41
 
            setattr(self,w,self.glade.get_widget(w))
42
 
        self.act_on_selection_widgets = [
43
 
            self.deleteValueButton, self.changeValueButton,
44
 
            self.newValueEntry,self.otherChangeCheckButton,
45
 
            self.leaveValueButton
46
 
            ]
47
 
        # Set up the combo-widget at the top with the 
48
 
        self.fields = [gglobals.REC_ATTR_DIC[v] for v in self.values]
49
 
        cb.set_model_from_list(
50
 
            self.fieldToEditCombo,
51
 
            self.fields
52
 
            )
53
 
        cb.set_model_from_list(
54
 
            self.otherFieldCombo,
55
 
            self.fields
56
 
            )
57
 
        self.newValueComboBoxEntry.set_sensitive(False)
58
 
        self.otherValueBlurbLabel.hide()
59
 
        self.newValueEntryCompletion = gtk.EntryCompletion()
60
 
        self.newValueEntry.set_completion(self.newValueEntryCompletion)
61
 
        self.otherNewValueEntryCompletion = gtk.EntryCompletion()
62
 
        self.otherNewValueEntry.set_completion(
63
 
            self.otherNewValueEntryCompletion
64
 
            )
65
 
        self.valueDialog.connect('response',self.dialog_response_cb)
66
 
        self.valueDialog.set_response_sensitive(gtk.RESPONSE_APPLY,False)
67
 
 
68
 
    def __setup_treeview__ (self):
69
 
        renderer = gtk.CellRendererText()
70
 
        # If we have gtk > 2.8, set up text-wrapping
71
 
        try:
72
 
            renderer.get_property('wrap-width')
73
 
        except TypeError:
74
 
            pass
75
 
        else:
76
 
            renderer.set_property('wrap-mode',gtk.WRAP_WORD)
77
 
            renderer.set_property('wrap-width',400)
78
 
        col = gtk.TreeViewColumn('Value',
79
 
                           renderer,
80
 
                           text=0)
81
 
        self.treeview.append_column(col)
82
 
        self.treeview.get_selection().connect('changed',self.treeViewSelectionChanged)
83
 
        self.treeview.get_selection().set_mode(gtk.SELECTION_MULTIPLE)
84
 
 
85
 
    def changeValueButtonToggledCB (self, tb):
86
 
        if tb.get_active():
87
 
            self.newValueComboBoxEntry.set_sensitive(True)
88
 
        else:
89
 
            self.newValueComboBoxEntry.set_sensitive(False)
90
 
 
91
 
    def treeViewSelectionChanged (self, tvSelection):
92
 
        vals = self.get_selected_values(tvSelection)
93
 
        if len(vals) == 1: val_string = vals[0]
94
 
        elif len(vals) == 2: val_string = ' or '.join(vals)
95
 
        elif len(vals) > 0:
96
 
            val_string = ' or '.join([', '.join(vals[:-1]),vals[-1]])
97
 
        else: # len(vals)==0
98
 
            self.forEachLabel.set_text(_('For each selected value'))
99
 
        if vals:
100
 
            self.val_string = val_string
101
 
            self.forEachLabel.set_text(
102
 
                _('Where %(field)s is %(value)s')%{'value':val_string,
103
 
                                                      'field':self.field}
104
 
                )
105
 
        self.valueDialog.set_response_sensitive(gtk.RESPONSE_APPLY,(vals and True or False))
106
 
        
107
 
    def fieldChangedCB (self, combobox):
108
 
        name = cb.cb_get_active_text(combobox)
109
 
        self.field = gglobals.NAME_TO_ATTR[name]
110
 
        self.populate_treeview()
111
 
        other_fields = self.fields[:]
112
 
        if self.field != 'category':
113
 
            other_fields.remove(gglobals.REC_ATTR_DIC[self.field])
114
 
        cb.set_model_from_list(
115
 
            self.otherFieldCombo,
116
 
            other_fields
117
 
            )
118
 
 
119
 
    def otherFieldComboChangedCB (self, combobox):
120
 
        name = cb.cb_get_active_text(combobox)
121
 
        self.other_field = gglobals.NAME_TO_ATTR[name]
122
 
        if self.other_field == 'category':
123
 
            self.otherValueBlurbLabel.hide()
124
 
        else:
125
 
            self.otherValueBlurbLabel.show()
126
 
        mod = self.make_model_for_field(self.other_field)
127
 
        self.otherNewValueComboBoxEntry.set_model(mod)
128
 
        if self.otherNewValueComboBoxEntry.get_text_column()==-1:
129
 
            self.otherNewValueComboBoxEntry.set_text_column(0)
130
 
        self.otherNewValueEntryCompletion.set_model(mod)
131
 
        self.otherNewValueEntryCompletion.set_text_column(0)
132
 
        
133
 
 
134
 
    def populate_treeview (self):
135
 
        """Assume that self.field is set"""
136
 
        mod = self.make_model_for_field(self.field)
137
 
        self.treeview.set_model(mod)
138
 
        self.newValueComboBoxEntry.set_model(mod)
139
 
        if self.newValueComboBoxEntry.get_text_column()==-1:
140
 
            self.newValueComboBoxEntry.set_text_column(0)
141
 
        self.newValueEntryCompletion.set_model(mod)
142
 
        self.newValueEntryCompletion.set_text_column(0)
143
 
 
144
 
    def make_model_for_field (self, field):
145
 
        vals = self.rd.get_unique_values(field)
146
 
        mod = gtk.ListStore(str)
147
 
        for v in vals: mod.append((v,))
148
 
        return mod
149
 
        
150
 
    def run (self): return self.valueDialog.run()
151
 
    def show (self): return self.valueDialog.show()
152
 
    def hide (self): return self.valueDialog.hide()    
153
 
 
154
 
    def dialog_response_cb (self, dialog, response_id):
155
 
        if response_id == gtk.RESPONSE_CLOSE:
156
 
            self.valueDialog.hide()
157
 
        if response_id == gtk.RESPONSE_APPLY:
158
 
            criteria,table = self.get_criteria_and_table()
159
 
            count = self.rd.fetch_len(table,**criteria)
160
 
            count_text = ngettext('Change will affect %s recipe',
161
 
                                  'Change will affect %s recipes',
162
 
                                  count)%count
163
 
            if self.deleteValueButton.get_active():
164
 
                label = _('Delete %s where it is %s?')%(self.field,self.val_string)
165
 
                yes = gtk.STOCK_DELETE
166
 
            else:
167
 
                label = _('Change %s from %s to "%s"?')%(self.field,self.val_string,
168
 
                                                                                self.newValueEntry.get_text())
169
 
                yes = '_Change'
170
 
            if de.getBoolean(label=label,
171
 
                             sublabel='\n\n'.join([
172
 
                count_text,
173
 
                _('<i>This change is not reversable.</i>')
174
 
                ]),
175
 
                             custom_yes=yes,
176
 
                             custom_no=gtk.STOCK_CANCEL,
177
 
                             cancel=False):
178
 
 
179
 
                self.apply_changes(criteria,table)
180
 
                self.populate_treeview()
181
 
 
182
 
    def otherChangeToggleCB (self, widg):
183
 
        if widg!=self.otherChangeCheckButton:
184
 
            self.otherChangeCheckButton.activate()
185
 
        if self.otherChangeCheckButton.get_active():
186
 
            self.otherExpander.set_expanded(True)
187
 
        else:
188
 
            self.otherExpander.set_expanded(False)
189
 
 
190
 
    def get_changes (self):
191
 
        if self.deleteValueButton.get_active():
192
 
            value = None
193
 
        elif self.changeValueButton.get_active():
194
 
            value = self.newValueEntry.get_text()
195
 
        return {self.field:value}
196
 
 
197
 
    def get_other_changes (self):
198
 
        if self.otherChangeCheckButton.get_active():
199
 
            return {self.other_field:self.otherNewValueEntry.get_text()}
200
 
        else:
201
 
            return {}
202
 
 
203
 
    def get_selected_values (self, ts=None):
204
 
        if not ts:
205
 
            ts = self.treeview.get_selection()
206
 
        mod,paths = ts.get_selected_rows()
207
 
        values = []
208
 
        for p in paths:
209
 
            values.append(
210
 
                mod.get_value(mod.get_iter(p),0)
211
 
                )
212
 
        return values
213
 
 
214
 
    def get_criteria_and_table (self):
215
 
        values = self.get_selected_values()
216
 
        if len(values) > 1:
217
 
            criteria = {self.field:('==',('or',values))}
218
 
        elif len(values)==1:
219
 
            criteria = {self.field:values[0]}
220
 
        if self.field == 'category':
221
 
            table = self.rd.categories_table
222
 
        else:
223
 
            table = self.rd.recipe_table
224
 
        return criteria,table
225
 
 
226
 
    def apply_changes (self, criteria, table):
227
 
        changes = self.get_changes()
228
 
        other_changes = self.get_other_changes()
229
 
        if self.field != 'category' and self.other_field != 'category':
230
 
            changes.update(other_changes)
231
 
        elif other_changes:
232
 
            if self.other_field == 'category':
233
 
                # Inefficient, but works with our current backend
234
 
                # interface... and shouldn't be called often, so we'll
235
 
                # deal with the ugliness for now
236
 
                for r in self.rd.fetch_all(self.rd.recipe_table,**criteria):
237
 
                    if not self.rd.fetch_one(self.rd.categories_table,{'id':r.id}):
238
 
                        self.rd.do_add_cat({'id':r.id,'category':other_changes['category']})
239
 
            else:
240
 
                if self.field=='category':
241
 
                    IDs = [r.id for r in self.rd.fetch_all(self.rd.categories_table,**criteria)]
242
 
                    new_criteria = {'id':('==',('or',IDs))}
243
 
                    self.rd.update_by_criteria(
244
 
                        self.rd.recipe_table,
245
 
                        new_criteria,
246
 
                        other_changes
247
 
                        )
248
 
                else:
249
 
                    self.rd.update_by_criteria(
250
 
                        self.rd.recipe_table,
251
 
                        criteria,
252
 
                        other_changes
253
 
                        )
254
 
        if self.field=='category' and not changes['category']:
255
 
            self.rd.delete_by_criteria(table,criteria)
256
 
        else:
257
 
            if self.field=='category':
258
 
                table = self.rd.categories_table
259
 
            else:
260
 
                table = self.rd.recipe_table
261
 
            self.rd.update_by_criteria(table,criteria,changes)
262
 
        self.rg.reset_search()
263
 
 
264
 
if __name__ == '__main__':
265
 
    import recipeManager
266
 
    rm = recipeManager.default_rec_manager()
267
 
    class DummyRG:
268
 
        def reset_search (): pass
269
 
    w = gtk.Window()
270
 
    b = gtk.Button('edit me now')
271
 
    w.add(b); w.show_all()
272
 
    ve = ValueEditor(rm,DummyRG())
273
 
    b.connect('clicked',lambda *args: ve.run())
274
 
    w.connect('delete-event',gtk.main_quit)
275
 
    gtk.main()