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 _
10
"""A generic "value" editor for mucking about with the database.
13
values = 'category','cuisine','source','link'
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,
29
def __setup_widgets__ (self):
33
'fieldToEditCombo','newValueComboBoxEntry',
34
'newValueEntry','changeValueButton',
35
'deleteValueButton','forEachLabel',
36
'otherExpander','otherFieldCombo',
37
'otherNewValueEntry','otherNewValueComboBoxEntry',
38
'otherValueBlurbLabel','otherChangeCheckButton',
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,
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,
53
cb.set_model_from_list(
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
65
self.valueDialog.connect('response',self.dialog_response_cb)
66
self.valueDialog.set_response_sensitive(gtk.RESPONSE_APPLY,False)
68
def __setup_treeview__ (self):
69
renderer = gtk.CellRendererText()
70
# If we have gtk > 2.8, set up text-wrapping
72
renderer.get_property('wrap-width')
76
renderer.set_property('wrap-mode',gtk.WRAP_WORD)
77
renderer.set_property('wrap-width',400)
78
col = gtk.TreeViewColumn('Value',
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)
85
def changeValueButtonToggledCB (self, tb):
87
self.newValueComboBoxEntry.set_sensitive(True)
89
self.newValueComboBoxEntry.set_sensitive(False)
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)
96
val_string = ' or '.join([', '.join(vals[:-1]),vals[-1]])
98
self.forEachLabel.set_text(_('For each selected value'))
100
self.val_string = val_string
101
self.forEachLabel.set_text(
102
_('Where %(field)s is %(value)s')%{'value':val_string,
105
self.valueDialog.set_response_sensitive(gtk.RESPONSE_APPLY,(vals and True or False))
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,
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()
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)
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)
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,))
150
def run (self): return self.valueDialog.run()
151
def show (self): return self.valueDialog.show()
152
def hide (self): return self.valueDialog.hide()
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',
163
if self.deleteValueButton.get_active():
164
label = _('Delete %s where it is %s?')%(self.field,self.val_string)
165
yes = gtk.STOCK_DELETE
167
label = _('Change %s from %s to "%s"?')%(self.field,self.val_string,
168
self.newValueEntry.get_text())
170
if de.getBoolean(label=label,
171
sublabel='\n\n'.join([
173
_('<i>This change is not reversable.</i>')
176
custom_no=gtk.STOCK_CANCEL,
179
self.apply_changes(criteria,table)
180
self.populate_treeview()
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)
188
self.otherExpander.set_expanded(False)
190
def get_changes (self):
191
if self.deleteValueButton.get_active():
193
elif self.changeValueButton.get_active():
194
value = self.newValueEntry.get_text()
195
return {self.field:value}
197
def get_other_changes (self):
198
if self.otherChangeCheckButton.get_active():
199
return {self.other_field:self.otherNewValueEntry.get_text()}
203
def get_selected_values (self, ts=None):
205
ts = self.treeview.get_selection()
206
mod,paths = ts.get_selected_rows()
210
mod.get_value(mod.get_iter(p),0)
214
def get_criteria_and_table (self):
215
values = self.get_selected_values()
217
criteria = {self.field:('==',('or',values))}
219
criteria = {self.field:values[0]}
220
if self.field == 'category':
221
table = self.rd.categories_table
223
table = self.rd.recipe_table
224
return criteria,table
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)
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']})
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,
249
self.rd.update_by_criteria(
250
self.rd.recipe_table,
254
if self.field=='category' and not changes['category']:
255
self.rd.delete_by_criteria(table,criteria)
257
if self.field=='category':
258
table = self.rd.categories_table
260
table = self.rd.recipe_table
261
self.rd.update_by_criteria(table,criteria,changes)
262
self.rg.reset_search()
264
if __name__ == '__main__':
266
rm = recipeManager.default_rec_manager()
268
def reset_search (): pass
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)