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