← Back to team overview

openerp-dev-web team mailing list archive

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

 

Ravi Gadhia (OpenERP) has proposed merging lp:~openerp-dev/openobject-client/trunk-m2o_with_selection-rga into lp:openobject-client.

Requested reviews:
  Naresh(OpenERP) (nch-openerp)

For more details, see:
https://code.launchpad.net/~openerp-dev/openobject-client/trunk-m2o_with_selection-rga/+merge/58245

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
-- 
https://code.launchpad.net/~openerp-dev/openobject-client/trunk-m2o_with_selection-rga/+merge/58245
Your team OpenERP R&D Team is subscribed to branch lp:~openerp-dev/openobject-client/trunk-m2o_with_selection-rga.
=== modified file 'bin/widget/model/field.py'
--- bin/widget/model/field.py	2011-04-13 13:24:05 +0000
+++ bin/widget/model/field.py	2011-04-19 08:45:00 +0000
@@ -45,12 +45,31 @@
         klass = TYPES.get(type, CharField)
         return klass
 
+class M2O_SelectionField(dict):
+    def __init__(self ,*args, **kwargs):
+        self.swap = {}
+
+    def __setitem__(self, key, value):
+        self.swap[value] = key
+        super(M2O_SelectionField, self).__setitem__(key, value)
+
+    def update(self, *args, **kwargs):
+        for key, val in args[0].items():
+            self.swap[val] = key
+        super(M2O_SelectionField, self).update(args[0])
+
+    def get_value(self, key):
+        return self.get(key, '')
+
+    def get_key(self,value):
+        return self.swap.get(value, False)
 
 class CharField(object):
     def __init__(self, parent, attrs):
         self.parent = parent
         self.attrs = attrs
         self.name = attrs['name']
+        self.selection = M2O_SelectionField()
         self.internal = False
         self.default_attrs = {}
 
@@ -104,12 +123,12 @@
         return True
 
     def get(self, model, check_load=True, readonly=True, modified=False):
-        return model.value.get(self.name, False) or False
+        return model.value.get(self.name, False)
 
     def set_client(self, model, value, test_state=True, force_change=False):
         internal = model.value.get(self.name, False)
         self.set(model, value, test_state)
-        if (internal or False) != (model.value.get(self.name,False) or False):
+        if (internal or False) != (model.value.get(self.name, False) or False):
             model.modified = True
             model.modified_fields.setdefault(self.name)
             self.sig_changed(model)
@@ -136,7 +155,7 @@
         try:
             attrs_changes = eval(self.attrs.get('attrs',"{}"))
         except:
-            attrs_changes = eval(self.attrs.get('attrs',"{}"),model.value)
+            attrs_changes = eval(self.attrs.get('attrs',"{}"), model.value)
             for k,v in attrs_changes.items():
                 for i in range(0,len(v)):
                    if v[i][2]:
@@ -242,14 +261,24 @@
 
 
 class SelectionField(CharField):
+    
+    def get(self, model, check_load=True, readonly=True, modified=False):
+        return model.value.get(self.name, False)    
+    
     def set(self, model, value, test_state=True, modified=False):
+        self.selection.update(dict(self.attrs.get('selection',[])))
+        
+        if isinstance(value,(list,tuple)) and len(value):
+            self.selection[value[0]] = value[1]
+                        
+        if value and isinstance(value, (int, long)) and self.attrs.get('relation', False):
+            rpc2 = RPCProxy(self.attrs['relation'])
+            result = rpc2.name_get([value], rpc.session.context)
+            self.selection.update(dict(result))
+        
         value = isinstance(value,(list,tuple)) and len(value) and value[0] or value
-
-        if not self.get_state_attrs(model).get('required', False) and value is None:
-            super(SelectionField, self).set(model, value, test_state, modified)
-
-        if value in [sel[0] for sel in self.attrs['selection']]:
-            super(SelectionField, self).set(model, value, test_state, modified)
+        
+        super(SelectionField, self).set(model, value, test_state, modified)
 
 class FloatField(CharField):
     def validate(self, model):
@@ -303,6 +332,7 @@
     def get_client(self, model):
         #model._check_load()
         if model.value[self.name]:
+            self.selection.update(dict([model.value[self.name]]))
             return model.value[self.name][1]
         return False
 

=== modified file 'bin/widget/model/group.py'
--- bin/widget/model/group.py	2011-04-13 05:28:58 +0000
+++ bin/widget/model/group.py	2011-04-19 08:45:00 +0000
@@ -343,7 +343,10 @@
         for f in fields.keys():
             add_field = True
             if f in models.fields:
-                if fields[f].get('widget','') == models.fields[f].get('widget',''):
+                if fields[f].get('widget','') == models.fields[f].get('widget','') :
+                    if  fields[f].get('widget','') == 'selection' and models.mfields[f].selection:
+                        selection = models.mfields[f].selection
+                        fields[f]['selection'] = zip(selection.keys(),selection.values())
                     models.fields[f].update(fields[f])
                     add_field = False
                 if f in models.mfields and fields[f].get('type','') == 'one2many':
@@ -352,7 +355,6 @@
                 models.fields[f] = fields[f]
                 models.fields[f]['name'] = f
                 to_add.append(f)
-
         self.mfields_load(to_add, models)
         for fname in to_add:
             for m in models.models:

=== modified file 'bin/widget/model/record.py'
--- bin/widget/model/record.py	2011-04-13 05:07:59 +0000
+++ bin/widget/model/record.py	2011-04-19 08:45:00 +0000
@@ -33,6 +33,7 @@
 from gtk import glade
 import tools
 from field import O2MField
+from field import SelectionField
 
 class EvalEnvironment(object):
     def __init__(self, parent):
@@ -232,6 +233,9 @@
             if self.mgroup.mfields[fieldname].attrs.get('on_change',False):
                 fields_with_on_change[fieldname] = value
             else:
+                if self.mgroup.mfields[fieldname].attrs.get('widget') == 'selection' and value:
+                    relation = self.mgroup.mfields[fieldname].attrs['relation']
+                    value = rpc.session.rpc_exec_auth('/object', 'execute', relation, 'name_search', '', [('id','=',value)], 'ilike')[0]
                 self.mgroup.mfields[fieldname].set_default(self, value)
         for field, value in fields_with_on_change.items():
             self.mgroup.mfields[field].set_default(self, value)

=== modified file 'bin/widget/screen/screen.py'
--- bin/widget/screen/screen.py	2011-04-13 09:12:58 +0000
+++ bin/widget/screen/screen.py	2011-04-19 08:45:00 +0000
@@ -592,13 +592,6 @@
                     if attrs['widget']=='one2many_list':
                         attrs['widget']='one2many'
                     attrs['type'] = attrs['widget']
-                if attrs.get('selection',[]):
-                    attrs['selection'] = eval(attrs['selection'])
-                    for att_key, att_val in attrs['selection'].items():
-                        for sel in fields[str(attrs['name'])]['selection']:
-                            if att_key == sel[0]:
-                                sel[1] = att_val
-                    attrs['selection'] = fields[str(attrs['name'])]['selection']
                 fields[unicode(attrs['name'])].update(attrs)
             for node2 in node:
                 _parse_fields(node2, fields)
@@ -617,7 +610,6 @@
             self.models.screen = self
             self.models.add_fields(fields, self.models, context=context)
         self.fields = self.models.fields
-
         parser = widget_parse(parent=self.parent, window=self.window)
         view = parser.parse(self, root_node, self.fields, toolbar=toolbar, submenu=submenu, help=help)
         if view:

=== modified file 'bin/widget/view/form_gtk/selection.py'
--- bin/widget/view/form_gtk/selection.py	2010-07-16 05:41:32 +0000
+++ bin/widget/view/form_gtk/selection.py	2011-04-19 08:45:00 +0000
@@ -23,7 +23,7 @@
 import interface
 import gtk
 import gobject
-
+import rpc
 import gettext
 
 class selection(interface.widget_interface):
@@ -31,109 +31,83 @@
         interface.widget_interface.__init__(self, window, parent, model, attrs)
 
         self.widget = gtk.HBox(spacing=3)
-        self.entry = gtk.ComboBoxEntry()
+        self.name = attrs['name']
+        self.attrs = attrs
+        self.entry = gtk.combo_box_entry_new_text()
+        self.entry.connect('notify::popup-shown', self.popup_show)
         self.child = self.entry.get_child()
+        self.relation_model = self.attrs.get('relation', '')
         self.child.set_property('activates_default', True)
         self.child.connect('changed', self.sig_changed)
         self.child.connect('populate-popup', self._menu_open)
-        self.child.connect('key_press_event', self.sig_key_press)
-        self.child.connect('activate', self.sig_activate)
         self.child.connect_after('focus-out-event', self.sig_activate)
-        self.entry.set_size_request(int(attrs.get('size', -1)), -1)
         self.widget.pack_start(self.entry, expand=True, fill=True)
 
         # the dropdown button is not focusable by a tab
         self.widget.set_focus_chain([self.child])
-        self.ok = True
+        self.set_popdown(attrs.get('selection',[]))
         self._selection={}
-
-        self.set_popdown(attrs.get('selection', []))
+        self.entry_text = ""
+
+
+    def popup_show(self, combobox, popup_show):
+        text = self.child.get_text()
+#        if self._view.modelfield.selection.get_key(text):
+#            text = ""
+        if combobox.get_property('popup-shown') and self.attrs.get('widget','') == 'selection':
+            domain = self._view.modelfield.domain_get(self._view.model)
+            context = self._view.modelfield.context_get(self._view.model)
+            selection = rpc.session.rpc_exec_auth('/object', 'execute', self.relation_model, 'name_search', text , domain , 'ilike', context , False)
+            self.set_popdown(selection)
 
     def set_popdown(self, selection):
-        self.model = gtk.ListStore(gobject.TYPE_STRING)
+        self.model = self.entry.get_model()
+        self.model.clear()
         self._selection={}
         lst = []
-        for (value, name) in selection:
-            name = str(name)
+        if not selection:
+            selection = [(False, '')]
+        for (i,j) in selection:
+            name = str(j)
             lst.append(name)
-            self._selection[name] = value
-            i = self.model.append()
-            self.model.set(i, 0, name)
-        self.entry.set_model(self.model)
-        self.entry.set_text_column(0)
-        return lst
+            self._selection[i]=name
+            self.entry.append_text(name)
 
     def _readonly_set(self, value):
         interface.widget_interface._readonly_set(self, value)
         self.entry.set_sensitive(not value)
 
-    def value_get(self):
-        res = self.child.get_text()
-        return self._selection.get(res, False)
-
-    def sig_key_press(self, widget, event):
-        # allow showing available entries by hitting "ctrl+space"
-        completion=gtk.EntryCompletion()
-        if hasattr(completion, 'set_inline_selection'):
-            completion.set_inline_selection(True)
-        if (event.type == gtk.gdk.KEY_PRESS) \
-            and ((event.state & gtk.gdk.CONTROL_MASK) != 0) \
-            and (event.keyval == gtk.keysyms.space):
-            self.entry.popup()
-        elif not (event.keyval == gtk.keysyms.Up or event.keyval == gtk.keysyms.Down):
-            completion.set_match_func(self.match_func,widget)
-            completion.set_model(self.model)
-            widget.set_completion(completion)
-            completion.set_text_column(0)
-    
-    def match_func(self, completion, key, iter, widget):
-         model = completion.get_model()
-         return model[iter][0].lower().find(widget.get_text().lower()) >= 0 and True or False
-     
     def sig_activate(self, *args):
         text = self.child.get_text()
-        value = False
-        if text:
-            for txt, val in self._selection.items():
-                if not val:
-                    continue
-                if txt[:len(text)].lower() == text.lower():
-                    value = val
-                    if len(txt) == len(text):
-                        break
+        value = self._view.modelfield.selection.get_key(text)
+        if not value:
+            self.entry_text = text
         self._view.modelfield.set_client(self._view.model, value, force_change=True)
         self.display(self._view.model, self._view.modelfield)
 
-
     def set_value(self, model, model_field):
-        model_field.set_client(model, self.value_get())
-
-    def _menu_sig_default_set(self):
-        self.set_value(self._view.model, self._view.modelfield)
-        super(selection, self)._menu_sig_default_set()
+        model_field.selection.update(self._selection)
+        text = self.child.get_text()
+        value = False
+        if text:
+            model_field.selection.get_key(text)
+            value  = model_field.selection.get_key(text)
+        model_field.set_client(model, value)
 
     def display(self, model, model_field):
-        self.ok = False
         if not model_field:
             self.child.set_text('')
-            self.ok = True
-            return False
+            return
+        model_field.selection.update(dict(self.attrs.get('selection',[])))
         super(selection, self).display(model, model_field)
-        value = model_field.get(model)
-        if not value:
-            self.child.set_text('')
-        else:
-            found = False
-            for long_text, sel_value in self._selection.items():
-                if sel_value == value:
-                    self.child.set_text(long_text)
-                    found = True
-                    break
-        self.ok = True
+        key = model_field.get(model, False)
+#        model_field.selection.update(self._selection)
+        text = model_field.selection.get_value(key)
+        self.child.set_text(self.entry_text or text)
+        self.entry_text = ""
 
-    def sig_changed(self, *args):
-        if self.ok:
-            self._focus_out()
+    def sig_changed(self, combox):
+        self._focus_out()
 
     def _color_widget(self):
         return self.child
@@ -141,4 +115,3 @@
     def grab_focus(self):
         return self.entry.grab_focus()
 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
-

=== modified file 'bin/widget/view/list.py'
--- bin/widget/view/list.py	2011-04-12 08:48:06 +0000
+++ bin/widget/view/list.py	2011-04-19 08:45:00 +0000
@@ -860,7 +860,7 @@
                     if col in self.widget_tree.handlers:
                         if self.widget_tree.handlers[col]:
                             renderer.disconnect(self.widget_tree.handlers[col])
-                    self.widget_tree.handlers[col] = renderer.connect_after('editing-started', send_keys, self.widget_tree)
+                    self.widget_tree.handlers[col] = renderer.connect_after('editing-started', send_keys, self.widget_tree, col.name)
 
 
     def set_invisible_attr(self):

=== modified file 'bin/widget/view/tree_gtk/editabletree.py'
--- bin/widget/view/tree_gtk/editabletree.py	2010-12-23 09:52:21 +0000
+++ bin/widget/view/tree_gtk/editabletree.py	2011-04-19 08:45:00 +0000
@@ -149,7 +149,12 @@
     def get_cursor(self):
         res = super(EditableTreeView, self).get_cursor()
         return res
-
+    
+    def get_current_model(self):
+        path, column = self.get_cursor()
+        store = self.get_model()
+        return store.get_value(store.get_iter(path), 0)
+        
     def set_value(self):
         path, column = self.get_cursor()
         store = self.get_model()
@@ -175,7 +180,7 @@
     def on_keypressed(self, entry, event, cell_value):
         path, column = self.get_cursor()
         store = self.get_model()
-        model = store.get_value(store.get_iter(path), 0)
+        model = self.get_current_model()
         if event.keyval in self.leaving_events:
             shift_pressed = bool(gtk.gdk.SHIFT_MASK & event.state)
             if isinstance(entry, gtk.Entry):

=== modified file 'bin/widget/view/tree_gtk/parser.py'
--- bin/widget/view/tree_gtk/parser.py	2011-03-20 11:26:23 +0000
+++ bin/widget/view/tree_gtk/parser.py	2011-04-19 08:45:00 +0000
@@ -33,6 +33,7 @@
 from editabletree import EditableTreeView
 from widget.view import interface
 from widget.view.list import group_record
+from widget.model.field import SelectionField, M2OField
 import time
 import date_renderer
 
@@ -47,13 +48,16 @@
 import gobject
 import pango
 
-def send_keys(renderer, entry, position, treeview):
-    if entry:
+def send_keys(renderer, entry, position, treeview, col_name):
+     if entry:
         entry.connect('key_press_event', treeview.on_keypressed, renderer.get_property('text'))
         entry.set_data('renderer', renderer)
         entry.editing_done_id = entry.connect('editing_done', treeview.on_editing_done)
         if isinstance(entry, gtk.ComboBoxEntry):
             entry.connect('changed', treeview.on_editing_done)
+        if isinstance(entry, gtk.ComboBoxEntry):
+            popup = treeview.cells[col_name].popup
+            entry.connect('notify::popup-shown', popup, treeview)
 
 def sort_model(column, screen):
     unsaved_model =  [x for x in screen.models if x.id == None or x.modified]
@@ -107,10 +111,8 @@
         treeview.sequence = False
         treeview.connect("motion-notify-event", treeview.set_tooltip)
         treeview.connect('key-press-event', treeview.on_tree_key_press)
-
         for node in root_node:
             node_attrs = tools.node_attributes(node)
-
             if node.tag == 'button':
                 cell = Cell('button')(node_attrs['string'], treeview, node_attrs)
                 cell.name = node_attrs['name']
@@ -158,16 +160,17 @@
                         self.window)
                 treeview.cells[fname] = cell
                 renderer = cell.renderer
-
+                col = gtk.TreeViewColumn(None, renderer)
+                col.name = fname
                 write_enable = editable and not node_attrs.get('readonly', False)
                 if isinstance(renderer, gtk.CellRendererToggle):
                     renderer.set_property('activatable', write_enable)
                 elif isinstance(renderer, (gtk.CellRendererText, gtk.CellRendererCombo, date_renderer.DecoratorRenderer)):
                     renderer.set_property('editable', write_enable)
                 if write_enable:
-                    handler_id = renderer.connect_after('editing-started', send_keys, treeview)
-
-                col = gtk.TreeViewColumn(None, renderer)
+                    handler_id = renderer.connect_after('editing-started', send_keys, treeview, col.name)
+
+
                 treeview.handlers[col] = handler_id
                 col_label = gtk.Label('')
                 if fields[fname].get('required', False):
@@ -176,7 +179,7 @@
                     col_label.set_text(fields[fname]['string'])
                 col_label.show()
                 col.set_widget(col_label)
-                col.name = fname
+              
                 col._type = fields[fname]['type']
                 col.set_cell_data_func(renderer, cell.setter)
                 col.set_clickable(True)
@@ -549,6 +552,13 @@
             return rpc.name_get([found[0]], context)[0]
         else:
             return False, None
+    
+    def get_textual_value(self, model):
+        if isinstance(model[self.field_name], SelectionField):
+            key =  model[self.field_name].get_client(model)
+            return model[self.field_name].selection.get_value(key)
+        return model[self.field_name].get_client(model) or ''
+
 
 
 class O2M(Char):
@@ -609,29 +619,46 @@
     def __init__(self, *args):
         super(Selection, self).__init__(*args)
         self.renderer = gtk.CellRendererCombo()
-        selection_data = gtk.ListStore(object, str)
-        for x in self.attrs.get('selection', []):
-            selection_data.append(x)
-        self.renderer.set_property('model', selection_data)
+        self.selection_data = gtk.ListStore(str, str)
+        selection = self.attrs.get('selection', [])
+        for x in selection:
+            self.selection_data.append(x)
+        if not selection:
+            self.selection_data.append([False, ''])
+        self.renderer.set_property('model', self.selection_data)
         self.renderer.set_property('text-column', 1)
+        self.relation = self.attrs.get('relation')
 
     def get_textual_value(self, model):
-        selection = dict(self.attrs['selection'])
-        selection_value = selection.get(model[self.field_name].get(model), '')
+        key = model[self.field_name].get(model)
+        selection_value = model[self.field_name].selection.get_value(key)
         if isinstance(model, group_record):
             return selection_value +  model[self.field_name].count
         return selection_value
+    
+    def popup(self, combobox, para, treeview):
+        if combobox.get_property('popup-shown') and self.relation:
+            entry = combobox.get_child()
+            text = entry.get_property('text')
+            rpc = RPCProxy(self.relation)
+            model = treeview.get_current_model()
+            domain = model[self.field_name].domain_get(model)
+            context = model[self.field_name].context_get(model)
+            key = model[self.field_name].selection.get_key(text)
+            if key:
+                text = ''
+            selection = rpc.name_search(text, domain, 'ilike',context, False)
+            if selection:
+                self.selection_data.clear()
+            model = treeview.get_current_model()
+            model[self.field_name].selection.update(dict(selection))
+            for x in selection:
+                self.selection_data.append(x)
 
     def value_from_text(self, model, text):
-        selection = self.attrs['selection']
-        text = tools.ustr(text)
-        res = False
-        for val, txt in selection:
-            if txt[:len(text)].lower() == text.lower():
-                if len(txt) == len(text):
-                    return val
-                res = val
-        return res
+        key = model[self.field_name].get(model)
+        selection_value = model[self.field_name].selection.get_value(key)
+        return model[self.field_name].selection.get_key(text)
 
 
 class ProgressBar(object):

=== modified file 'bin/widget_search/selection.py'
--- bin/widget_search/selection.py	2011-04-04 11:19:53 +0000
+++ bin/widget_search/selection.py	2011-04-19 08:45:00 +0000
@@ -28,25 +28,37 @@
 class selection(wid_int.wid_int):
     def __init__(self, name, parent, attrs={}, model=None, screen=None):
         wid_int.wid_int.__init__(self, name, parent, attrs, screen)
-
         self.widget = gtk.combo_box_entry_new_text()
+        self.widget.connect('notify::popup-shown', self.popup_show)
+        self.context = screen.context
+        self.relation_model = self.attrs.get('relation', '')
         self.widget.child.set_editable(True)
         self.attrs = attrs
         self._selection = {}
         self.name = name
-        self.val_id = False
-        if 'selection' in attrs:
-            self.set_popdown(attrs.get('selection',[]))
+        self.set_popdown(attrs.get('selection',[]))
         if self.default_search:
             if isinstance(self.default_search,list):
                 self.default_search = self.default_search[0]
             if self.attrs['type'] == 'many2one':
-                self._value_set(int(self.default_search))
+                 sel = rpc.session.rpc_exec_auth('/object', 'execute', self.relation_model, 'name_search', '' , [('id','=',self.default_search)] , 'ilike', self.context, False)
+                 self.set_popdown(sel)
+                 self._value_set(int(self.default_search))
             else:
                 self._value_set(str(self.default_search))
                 if self.widget.child.get_text() in self._selection.keys():
                     self.widget.set_active(self.indexes[self.widget.child.get_text()]-1)
 
+    def popup_show(self, combobox, popup_show):
+        search_text = self.widget.child.get_text()
+#        if self._selection.get(search_text, False):
+#            search_text =''
+        if combobox.get_property('popup-shown') and self.attrs['type'] == 'many2one':
+            selection = rpc.session.rpc_exec_auth('/object', 'execute', self.relation_model, 'name_search', search_text , [] , 'ilike', self.context, False)
+            self.set_popdown(selection)
+            if not selection:
+                 self.widget.child.set_text('')
+
     def set_popdown(self, selection):
         self.model = self.widget.get_model()
         self.model.clear()