Merge lp:~openerp-dev/openobject-client/trunk-m2o_with_selection-rga into lp:openobject-client

Proposed by Ravi Gadhia (OpenERP)
Status: Superseded
Proposed branch: lp:~openerp-dev/openobject-client/trunk-m2o_with_selection-rga
Merge into: lp:openobject-client
Diff against target: 531 lines (+159/-110)
8 files modified
bin/widget/model/field.py (+33/-6)
bin/widget/model/record.py (+4/-0)
bin/widget/screen/screen.py (+2/-0)
bin/widget/view/form_gtk/selection.py (+47/-73)
bin/widget/view/list.py (+1/-1)
bin/widget/view/tree_gtk/editabletree.py (+7/-2)
bin/widget/view/tree_gtk/parser.py (+51/-24)
bin/widget_search/selection.py (+14/-4)
To merge this branch: bzr merge lp:~openerp-dev/openobject-client/trunk-m2o_with_selection-rga
Reviewer Review Type Date Requested Status
Naresh(OpenERP) Needs Resubmitting
Review via email: mp+53391@code.launchpad.net

This proposal has been superseded by a proposal from 2011-04-19.

Description of the change

Hello,

Improve selection field.

Now M2O field with widget="selection" no need to pre-load selection value (at time of fields_view_get) it's get value on popup by name_search and we can apply domain as like M2O field

related server branch:
https://code.launchpad.net/~openerp-dev/openobject-server/trunk-M2O_with_selection-rga

To post a comment you must log in.
Revision history for this message
Naresh(OpenERP) (nch-openerp) wrote :

Hello,

For the client side stuff, there are few bugs I found.

1: The feature of search view "search_default_XXXX" in the context is not working causing a regression here with the above change.

2: setting value of the field which has widget="selection" thru an onchange does not work. Again a regression.

3:If a O2M has a subfield which has widget="selection" attribute behaves very odd

4:Once I have selected the value from the list that the name_search returns then on my next click the domain should take the text that is already selected in the field. currently it takes a ""(blank) value resulting in all records.

Thanks

review: Needs Resubmitting
1838. By Ravi Gadhia (OpenERP)

[Fix] Search view: set default value on (M2O) widget=selection field

1839. By Ravi Gadhia (OpenERP)

[FIX] when widget=selection on one2many field's form view (where each time new modelfield created whid dialog box popup)

1840. By Ravi Gadhia (OpenERP)

[IMP] If selection field has not key:val then get it by rpc call

1841. By Ravi Gadhia (OpenERP)

[IMP] search text should not clear even it's in selection list(model)

1842. By Ravi Gadhia (OpenERP)

Merge with trunk client

Unmerged revisions

1842. By Ravi Gadhia (OpenERP)

Merge with trunk client

1841. By Ravi Gadhia (OpenERP)

[IMP] search text should not clear even it's in selection list(model)

1840. By Ravi Gadhia (OpenERP)

[IMP] If selection field has not key:val then get it by rpc call

1839. By Ravi Gadhia (OpenERP)

[FIX] when widget=selection on one2many field's form view (where each time new modelfield created whid dialog box popup)

1838. By Ravi Gadhia (OpenERP)

[Fix] Search view: set default value on (M2O) widget=selection field

1837. By Ravi Gadhia (OpenERP)

Remove un-used variable

1836. By Ravi Gadhia (OpenERP)

[IMP] Editable list view: add context, domain on selection field

1835. By Ravi Gadhia (OpenERP)

[IMP] Editable list view: M2O field with selection make rpc call at the time of popup (no preloded selection data)

1834. By Ravi Gadhia (OpenERP)

[IMP]Form view: add context, domain on selection field + usability imrovement

1833. By Ravi Gadhia (OpenERP)

[IMP] Form view: m20 with selection widget make rpc call when combobox popup

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'bin/widget/model/field.py'
2--- bin/widget/model/field.py 2011-02-14 11:00:02 +0000
3+++ bin/widget/model/field.py 2011-03-15 09:59:07 +0000
4@@ -45,12 +45,31 @@
5 klass = TYPES.get(type, CharField)
6 return klass
7
8+class M2O_SelectionField(dict):
9+ def __init__(self ,*args, **kwargs):
10+ self.swap = {}
11+
12+ def __setitem__(self, key, value):
13+ self.swap[value] = key
14+ super(M2O_SelectionField, self).__setitem__(key, value)
15+
16+ def update(self, *args, **kwargs):
17+ for key, val in args[0].items():
18+ self.swap[val] = key
19+ super(M2O_SelectionField, self).update(args[0])
20+
21+ def get_value(self, key):
22+ return self.get(key, '')
23+
24+ def get_key(self,value):
25+ return self.swap.get(value, False)
26
27 class CharField(object):
28 def __init__(self, parent, attrs):
29 self.parent = parent
30 self.attrs = attrs
31 self.name = attrs['name']
32+ self.selection = M2O_SelectionField()
33 self.internal = False
34 self.default_attrs = {}
35
36@@ -103,12 +122,12 @@
37 return True
38
39 def get(self, model, check_load=True, readonly=True, modified=False):
40- return model.value.get(self.name, False) or False
41+ return model.value.get(self.name, False)
42
43 def set_client(self, model, value, test_state=True, force_change=False):
44 internal = model.value.get(self.name, False)
45 self.set(model, value, test_state)
46- if (internal or False) != (model.value.get(self.name,False) or False):
47+ if (internal or False) != (model.value.get(self.name, False) or False):
48 model.modified = True
49 model.modified_fields.setdefault(self.name)
50 self.sig_changed(model)
51@@ -135,7 +154,7 @@
52 try:
53 attrs_changes = eval(self.attrs.get('attrs',"{}"))
54 except:
55- attrs_changes = eval(self.attrs.get('attrs',"{}"),model.value)
56+ attrs_changes = eval(self.attrs.get('attrs',"{}"), model.value)
57 for k,v in attrs_changes.items():
58 for i in range(0,len(v)):
59 if v[i][2]:
60@@ -236,14 +255,21 @@
61
62
63 class SelectionField(CharField):
64+
65+ def get(self, model, check_load=True, readonly=True, modified=False):
66+ return model.value.get(self.name, False)
67+
68 def set(self, model, value, test_state=True, modified=False):
69+ self.selection.update(dict(self.attrs.get('selection',[])))
70+ if isinstance(value,(list,tuple)) and len(value):
71+ self.selection[value[0]] = value[1]
72+
73 value = isinstance(value,(list,tuple)) and len(value) and value[0] or value
74
75 if not self.get_state_attrs(model).get('required', False) and value is None:
76 super(SelectionField, self).set(model, value, test_state, modified)
77-
78- if value in [sel[0] for sel in self.attrs['selection']]:
79- super(SelectionField, self).set(model, value, test_state, modified)
80+
81+ super(SelectionField, self).set(model, value, test_state, modified)
82
83 class FloatField(CharField):
84 def validate(self, model):
85@@ -297,6 +323,7 @@
86 def get_client(self, model):
87 #model._check_load()
88 if model.value[self.name]:
89+ self.selection.update(dict([model.value[self.name]]))
90 return model.value[self.name][1]
91 return False
92
93
94=== modified file 'bin/widget/model/record.py'
95--- bin/widget/model/record.py 2011-02-04 06:19:22 +0000
96+++ bin/widget/model/record.py 2011-03-15 09:59:07 +0000
97@@ -33,6 +33,7 @@
98 from gtk import glade
99 import tools
100 from field import O2MField
101+from field import SelectionField
102
103 class EvalEnvironment(object):
104 def __init__(self, parent):
105@@ -230,6 +231,9 @@
106 if self.mgroup.mfields[fieldname].attrs.get('on_change',False):
107 fields_with_on_change[fieldname] = value
108 else:
109+ if self.mgroup.mfields[fieldname].attrs.get('widget') == 'selection' and value:
110+ relation = self.mgroup.mfields[fieldname].attrs['relation']
111+ value = rpc.session.rpc_exec_auth('/object', 'execute', relation, 'name_search', '', [('id','=',value)], 'ilike')[0]
112 self.mgroup.mfields[fieldname].set_default(self, value)
113 for field, value in fields_with_on_change.items():
114 self.mgroup.mfields[field].set_default(self, value)
115
116=== modified file 'bin/widget/screen/screen.py'
117--- bin/widget/screen/screen.py 2011-01-27 12:43:03 +0000
118+++ bin/widget/screen/screen.py 2011-03-15 09:59:07 +0000
119@@ -593,6 +593,8 @@
120 if attrs.get('widget', False):
121 if attrs['widget']=='one2many_list':
122 attrs['widget']='one2many'
123+# attrs['py_field_type'] = attrs['type']
124+ attrs['py_type'] = fields[str(attrs['name'])]['type']
125 attrs['type'] = attrs['widget']
126 if attrs.get('selection',[]):
127 attrs['selection'] = eval(attrs['selection'])
128
129=== modified file 'bin/widget/view/form_gtk/selection.py'
130--- bin/widget/view/form_gtk/selection.py 2010-07-16 05:41:32 +0000
131+++ bin/widget/view/form_gtk/selection.py 2011-03-15 09:59:07 +0000
132@@ -23,7 +23,7 @@
133 import interface
134 import gtk
135 import gobject
136-
137+import rpc
138 import gettext
139
140 class selection(interface.widget_interface):
141@@ -31,109 +31,84 @@
142 interface.widget_interface.__init__(self, window, parent, model, attrs)
143
144 self.widget = gtk.HBox(spacing=3)
145- self.entry = gtk.ComboBoxEntry()
146+ self.name = attrs['name']
147+ self.attrs = attrs
148+ self.entry = gtk.combo_box_entry_new_text()
149+ self.entry.connect('notify::popup-shown', self.popup_show)
150 self.child = self.entry.get_child()
151+ self.relation_model = self.attrs.get('relation', '')
152 self.child.set_property('activates_default', True)
153 self.child.connect('changed', self.sig_changed)
154 self.child.connect('populate-popup', self._menu_open)
155- self.child.connect('key_press_event', self.sig_key_press)
156- self.child.connect('activate', self.sig_activate)
157 self.child.connect_after('focus-out-event', self.sig_activate)
158- self.entry.set_size_request(int(attrs.get('size', -1)), -1)
159 self.widget.pack_start(self.entry, expand=True, fill=True)
160
161 # the dropdown button is not focusable by a tab
162 self.widget.set_focus_chain([self.child])
163- self.ok = True
164+ self.set_popdown(attrs.get('selection',[]))
165 self._selection={}
166-
167- self.set_popdown(attrs.get('selection', []))
168+ self.entry_text = ""
169+
170+
171+ def popup_show(self, combobox, popup_show):
172+ text = self.child.get_text()
173+ if self._view.modelfield.selection.get_key(text):
174+ text = ""
175+# self.child.set_text(self.last_txt)
176+ if combobox.get_property('popup-shown') and self.attrs.get('widget','') == 'selection':
177+ domain = self._view.modelfield.domain_get(self._view.model)
178+ context = self._view.modelfield.context_get(self._view.model)
179+ selection = rpc.session.rpc_exec_auth('/object', 'execute', self.relation_model, 'name_search', text , domain , 'ilike', context , False)
180+ self.set_popdown(selection)
181
182 def set_popdown(self, selection):
183- self.model = gtk.ListStore(gobject.TYPE_STRING)
184+ self.model = self.entry.get_model()
185+ self.model.clear()
186 self._selection={}
187 lst = []
188- for (value, name) in selection:
189- name = str(name)
190+ if not selection:
191+ selection = [(False, '')]
192+ for (i,j) in selection:
193+ name = str(j)
194 lst.append(name)
195- self._selection[name] = value
196- i = self.model.append()
197- self.model.set(i, 0, name)
198- self.entry.set_model(self.model)
199- self.entry.set_text_column(0)
200- return lst
201+ self._selection[i]=name
202+ self.entry.append_text(name)
203
204 def _readonly_set(self, value):
205 interface.widget_interface._readonly_set(self, value)
206 self.entry.set_sensitive(not value)
207
208- def value_get(self):
209- res = self.child.get_text()
210- return self._selection.get(res, False)
211-
212- def sig_key_press(self, widget, event):
213- # allow showing available entries by hitting "ctrl+space"
214- completion=gtk.EntryCompletion()
215- if hasattr(completion, 'set_inline_selection'):
216- completion.set_inline_selection(True)
217- if (event.type == gtk.gdk.KEY_PRESS) \
218- and ((event.state & gtk.gdk.CONTROL_MASK) != 0) \
219- and (event.keyval == gtk.keysyms.space):
220- self.entry.popup()
221- elif not (event.keyval == gtk.keysyms.Up or event.keyval == gtk.keysyms.Down):
222- completion.set_match_func(self.match_func,widget)
223- completion.set_model(self.model)
224- widget.set_completion(completion)
225- completion.set_text_column(0)
226-
227- def match_func(self, completion, key, iter, widget):
228- model = completion.get_model()
229- return model[iter][0].lower().find(widget.get_text().lower()) >= 0 and True or False
230-
231 def sig_activate(self, *args):
232 text = self.child.get_text()
233- value = False
234- if text:
235- for txt, val in self._selection.items():
236- if not val:
237- continue
238- if txt[:len(text)].lower() == text.lower():
239- value = val
240- if len(txt) == len(text):
241- break
242+ value = self._view.modelfield.selection.get_key(text)
243+ if not value:
244+ self.entry_text = text
245 self._view.modelfield.set_client(self._view.model, value, force_change=True)
246 self.display(self._view.model, self._view.modelfield)
247
248-
249 def set_value(self, model, model_field):
250- model_field.set_client(model, self.value_get())
251-
252- def _menu_sig_default_set(self):
253- self.set_value(self._view.model, self._view.modelfield)
254- super(selection, self)._menu_sig_default_set()
255+ model_field.selection.update(self._selection)
256+ text = self.child.get_text()
257+ value = False
258+ if text:
259+ model_field.selection.get_key(text)
260+ value = model_field.selection.get_key(text)
261+ model_field.set_client(model, value)
262
263 def display(self, model, model_field):
264- self.ok = False
265 if not model_field:
266 self.child.set_text('')
267- self.ok = True
268- return False
269+ return
270+ model_field.selection.update(dict(self.attrs.get('selection',[])))
271 super(selection, self).display(model, model_field)
272- value = model_field.get(model)
273- if not value:
274- self.child.set_text('')
275- else:
276- found = False
277- for long_text, sel_value in self._selection.items():
278- if sel_value == value:
279- self.child.set_text(long_text)
280- found = True
281- break
282- self.ok = True
283+ key = model_field.get(model, False)
284+# model_field.selection.update(self._selection)
285+ text = model_field.selection.get_value(key)
286+ self.child.set_text(self.entry_text or text)
287+ self.entry_text = ""
288
289- def sig_changed(self, *args):
290- if self.ok:
291- self._focus_out()
292+ def sig_changed(self, combox):
293+ self._focus_out()
294
295 def _color_widget(self):
296 return self.child
297@@ -141,4 +116,3 @@
298 def grab_focus(self):
299 return self.entry.grab_focus()
300 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
301-
302
303=== modified file 'bin/widget/view/list.py'
304--- bin/widget/view/list.py 2011-03-04 12:04:50 +0000
305+++ bin/widget/view/list.py 2011-03-15 09:59:07 +0000
306@@ -848,7 +848,7 @@
307 if col in self.widget_tree.handlers:
308 if self.widget_tree.handlers[col]:
309 renderer.disconnect(self.widget_tree.handlers[col])
310- self.widget_tree.handlers[col] = renderer.connect_after('editing-started', send_keys, self.widget_tree)
311+ self.widget_tree.handlers[col] = renderer.connect_after('editing-started', send_keys, self.widget_tree, col.name)
312
313
314 def set_invisible_attr(self):
315
316=== modified file 'bin/widget/view/tree_gtk/editabletree.py'
317--- bin/widget/view/tree_gtk/editabletree.py 2010-12-23 09:52:21 +0000
318+++ bin/widget/view/tree_gtk/editabletree.py 2011-03-15 09:59:07 +0000
319@@ -149,7 +149,12 @@
320 def get_cursor(self):
321 res = super(EditableTreeView, self).get_cursor()
322 return res
323-
324+
325+ def get_current_model(self):
326+ path, column = self.get_cursor()
327+ store = self.get_model()
328+ return store.get_value(store.get_iter(path), 0)
329+
330 def set_value(self):
331 path, column = self.get_cursor()
332 store = self.get_model()
333@@ -175,7 +180,7 @@
334 def on_keypressed(self, entry, event, cell_value):
335 path, column = self.get_cursor()
336 store = self.get_model()
337- model = store.get_value(store.get_iter(path), 0)
338+ model = self.get_current_model()
339 if event.keyval in self.leaving_events:
340 shift_pressed = bool(gtk.gdk.SHIFT_MASK & event.state)
341 if isinstance(entry, gtk.Entry):
342
343=== modified file 'bin/widget/view/tree_gtk/parser.py'
344--- bin/widget/view/tree_gtk/parser.py 2011-03-04 12:04:50 +0000
345+++ bin/widget/view/tree_gtk/parser.py 2011-03-15 09:59:07 +0000
346@@ -33,6 +33,7 @@
347 from editabletree import EditableTreeView
348 from widget.view import interface
349 from widget.view.list import group_record
350+from widget.model.field import SelectionField, M2OField
351 import time
352 import date_renderer
353
354@@ -47,13 +48,16 @@
355 import gobject
356 import pango
357
358-def send_keys(renderer, entry, position, treeview):
359- if entry:
360+def send_keys(renderer, entry, position, treeview, col_name):
361+ if entry:
362 entry.connect('key_press_event', treeview.on_keypressed, renderer.get_property('text'))
363 entry.set_data('renderer', renderer)
364 entry.editing_done_id = entry.connect('editing_done', treeview.on_editing_done)
365 if isinstance(entry, gtk.ComboBoxEntry):
366 entry.connect('changed', treeview.on_editing_done)
367+ if isinstance(entry, gtk.ComboBoxEntry):
368+ popup = treeview.cells[col_name].popup
369+ entry.connect('notify::popup-shown', popup, treeview)
370
371 def sort_model(column, screen):
372 unsaved_model = [x for x in screen.models if x.id == None or x.modified]
373@@ -107,10 +111,8 @@
374 treeview.sequence = False
375 treeview.connect("motion-notify-event", treeview.set_tooltip)
376 treeview.connect('key-press-event', treeview.on_tree_key_press)
377-
378 for node in root_node:
379 node_attrs = tools.node_attributes(node)
380-
381 if node.tag == 'button':
382 cell = Cell('button')(node_attrs['string'], treeview, node_attrs)
383 cell.name = node_attrs['name']
384@@ -158,16 +160,17 @@
385 self.window)
386 treeview.cells[fname] = cell
387 renderer = cell.renderer
388-
389+ col = gtk.TreeViewColumn(None, renderer)
390+ col.name = fname
391 write_enable = editable and not node_attrs.get('readonly', False)
392 if isinstance(renderer, gtk.CellRendererToggle):
393 renderer.set_property('activatable', write_enable)
394 elif isinstance(renderer, (gtk.CellRendererText, gtk.CellRendererCombo, date_renderer.DecoratorRenderer)):
395 renderer.set_property('editable', write_enable)
396 if write_enable:
397- handler_id = renderer.connect_after('editing-started', send_keys, treeview)
398-
399- col = gtk.TreeViewColumn(None, renderer)
400+ handler_id = renderer.connect_after('editing-started', send_keys, treeview, col.name)
401+
402+
403 treeview.handlers[col] = handler_id
404 col_label = gtk.Label('')
405 if fields[fname].get('required', False):
406@@ -176,7 +179,7 @@
407 col_label.set_text(fields[fname]['string'])
408 col_label.show()
409 col.set_widget(col_label)
410- col.name = fname
411+
412 col._type = fields[fname]['type']
413 col.set_cell_data_func(renderer, cell.setter)
414 col.set_clickable(True)
415@@ -549,6 +552,13 @@
416 return rpc.name_get([found[0]], context)[0]
417 else:
418 return False, None
419+
420+ def get_textual_value(self, model):
421+ if isinstance(model[self.field_name], SelectionField):
422+ key = model[self.field_name].get_client(model)
423+ return model[self.field_name].selection.get_value(key)
424+ return model[self.field_name].get_client(model) or ''
425+
426
427
428 class O2M(Char):
429@@ -609,29 +619,46 @@
430 def __init__(self, *args):
431 super(Selection, self).__init__(*args)
432 self.renderer = gtk.CellRendererCombo()
433- selection_data = gtk.ListStore(str, str)
434- for x in self.attrs.get('selection', []):
435- selection_data.append(x)
436- self.renderer.set_property('model', selection_data)
437+ self.selection_data = gtk.ListStore(str, str)
438+ selection = self.attrs.get('selection', [])
439+ for x in selection:
440+ self.selection_data.append(x)
441+ if not selection:
442+ self.selection_data.append([False, ''])
443+ self.renderer.set_property('model', self.selection_data)
444 self.renderer.set_property('text-column', 1)
445+ self.relation = self.attrs.get('relation')
446
447 def get_textual_value(self, model):
448- selection = dict(self.attrs['selection'])
449- selection_value = selection.get(model[self.field_name].get(model), '')
450+ key = model[self.field_name].get(model)
451+ selection_value = model[self.field_name].selection.get_value(key)
452 if isinstance(model, group_record):
453 return selection_value + model[self.field_name].count
454 return selection_value
455+
456+ def popup(self, combobox, para, treeview):
457+ if combobox.get_property('popup-shown') and self.relation:
458+ entry = combobox.get_child()
459+ text = entry.get_property('text')
460+ rpc = RPCProxy(self.relation)
461+ model = treeview.get_current_model()
462+ domain = model[self.field_name].domain_get(model)
463+ context = model[self.field_name].context_get(model)
464+ key = model[self.field_name].selection.get_key(text)
465+ if key:
466+ text = ''
467+ selection = rpc.name_search(text, domain, 'ilike',context, False)
468+ if selection:
469+ self.selection_data.clear()
470+ model = treeview.get_current_model()
471+ model[self.field_name].selection.update(dict(selection))
472+ for x in selection:
473+ self.selection_data.append(x)
474
475 def value_from_text(self, model, text):
476- selection = self.attrs['selection']
477- text = tools.ustr(text)
478- res = False
479- for val, txt in selection:
480- if txt[:len(text)].lower() == text.lower():
481- if len(txt) == len(text):
482- return val
483- res = val
484- return res
485+ key = model[self.field_name].get(model)
486+ selection_value = model[self.field_name].selection.get_value(key)
487+ return model[self.field_name].selection.get_key(text)
488
489
490 class ProgressBar(object):
491
492=== modified file 'bin/widget_search/selection.py'
493--- bin/widget_search/selection.py 2011-01-17 19:11:21 +0000
494+++ bin/widget_search/selection.py 2011-03-15 09:59:07 +0000
495@@ -28,15 +28,15 @@
496 class selection(wid_int.wid_int):
497 def __init__(self, name, parent, attrs={}, model=None, screen=None):
498 wid_int.wid_int.__init__(self, name, parent, attrs, screen)
499-
500 self.widget = gtk.combo_box_entry_new_text()
501+ self.widget.connect('notify::popup-shown', self.popup_show)
502+ self.context = screen.context
503+ self.relation_model = self.attrs.get('relation', '')
504 self.widget.child.set_editable(True)
505 self.attrs = attrs
506 self._selection = {}
507 self.name = name
508- self.val_id = False
509- if 'selection' in attrs:
510- self.set_popdown(attrs.get('selection',[]))
511+ self.set_popdown(attrs.get('selection',[]))
512 if self.default_search:
513 if self.attrs['type'] == 'many2one':
514 self._value_set(int(self.default_search))
515@@ -45,6 +45,16 @@
516 if self.widget.child.get_text() in self._selection.keys():
517 self.widget.set_active(self.indexes[self.widget.child.get_text()]-1)
518
519+ def popup_show(self, combobox, popup_show):
520+ search_text = self.widget.child.get_text()
521+ if self._selection.get(search_text, False):
522+ search_text =''
523+ if combobox.get_property('popup-shown') and self.attrs['type'] == 'many2one':
524+ selection = rpc.session.rpc_exec_auth('/object', 'execute', self.relation_model, 'name_search', search_text , [] , 'ilike', self.context, False)
525+ self.set_popdown(selection)
526+ if not selection:
527+ self.widget.child.set_text('')
528+
529 def set_popdown(self, selection):
530 self.model = self.widget.get_model()
531 self.model.clear()