diff --git a/tryton/gui/window/view_form/model/field.py b/tryton/gui/window/view_form/model/field.py index 0493b4956f..44941ad073 100644 --- a/tryton/gui/window/view_form/model/field.py +++ b/tryton/gui/window/view_form/model/field.py @@ -40,6 +40,7 @@ class CharField(object): if self.attrs.get('on_change', False): record.on_change(self.name, self.attrs['on_change']) record.on_change_with(self.name) + record.autocomplete_with(self.name) def domains_get(self, record): screen_domain = domain_inversion(record.group.domain, diff --git a/tryton/gui/window/view_form/model/record.py b/tryton/gui/window/view_form/model/record.py index c32beaa02d..23abaf8c0c 100644 --- a/tryton/gui/window/view_form/model/record.py +++ b/tryton/gui/window/view_form/model/record.py @@ -35,6 +35,7 @@ class Record(SignalEvent): self.attachment_count = -1 self.next = {} # Used in Group list self.value = {} + self.autocompletion = {} def __getitem__(self, name): if name not in self._loaded and self.id > 0: @@ -233,6 +234,10 @@ class Record(SignalEvent): if not vals: return self.set_default(vals) + for fieldname, fieldinfo in self.group.fields.iteritems(): + if not fieldinfo.attrs.get('autocomplete'): + continue + self.do_autocomplete(fieldname) def rec_name(self): ctx = rpc.CONTEXT.copy() @@ -438,6 +443,43 @@ class Record(SignalEvent): return self.group.fields[fieldname].set_on_change(self, res) + def autocomplete_with(self, field_name): + for fieldname, fieldinfo in self.group.fields.iteritems(): + autocomplete = fieldinfo.attrs.get('autocomplete', []) + if field_name not in autocomplete: + continue + self.do_autocomplete(fieldname) + + def do_autocomplete(self, fieldname): + self.autocompletion[fieldname] = [] + autocomplete = self.group.fields[fieldname].attrs['autocomplete'] + args = self._get_on_change_args(autocomplete) + ctx = rpc.CONTEXT.copy() + ctx.update(self.context_get()) + args = ('model', self.model_name, 'autocomplete_' + fieldname, args, + ctx) + try: + res = rpc.execute(*args) + except Exception, exception: + res = common.process_exception(exception, self.window, *args) + if not res: + # ensure res is a list + res = [] + self.autocompletion[fieldname] = res + + def cond_default(self, field_name, value): + ctx = rpc.CONTEXT.copy() + ctx.update(self.context_get()) + args = ('model', 'ir.default', 'get_default', self.model_name, + field_name + '=' + str(value), ctx) + try: + res = rpc.execute(*args) + except Exception, exception: + res = common.process_exception(exception, self.window, *args) + if not res: + return + self.set_default(res) + def get_attachment_count(self, reload=False): if self.id < 0: return 0 diff --git a/tryton/gui/window/view_form/view/form_gtk/char.py b/tryton/gui/window/view_form/view/form_gtk/char.py index 4bf4df841e..1a53c66f04 100644 --- a/tryton/gui/window/view_form/view/form_gtk/char.py +++ b/tryton/gui/window/view_form/view/form_gtk/char.py @@ -2,12 +2,14 @@ #this repository contains the full copyright notices and license terms. import gettext +import gobject import gtk from interface import WidgetInterface from tryton.common import Tooltips _ = gettext.gettext + class Char(WidgetInterface): "Char" @@ -15,12 +17,25 @@ class Char(WidgetInterface): super(Char, self).__init__(field_name, model_name, window, attrs=attrs) self.widget = gtk.HBox() - self.entry = gtk.Entry() - self.entry.set_property('activates_default', True) - self.entry.set_max_length(int(attrs.get('size', 0))) - self.entry.set_width_chars(5) + self.autocomplete = bool(attrs.get('autocomplete')) + if self.autocomplete: + self.entry = gtk.ComboBoxEntry() + self.entry_store = gtk.ListStore(gobject.TYPE_STRING) + self.entry.set_model(self.entry_store) + self.entry.set_text_column(0) + completion = gtk.EntryCompletion() + completion.set_model(self.entry_store) + completion.set_text_column(0) + self.entry.get_child().set_completion(completion) + else: + self.entry = gtk.Entry() + self.entry.set_property('activates_default', True) + self.entry.set_max_length(int(attrs.get('size', 0))) + self.entry.set_width_chars(5) + + self.entry.connect('populate-popup', self._populate_popup) + self.entry.connect('activate', self.sig_activate) - self.entry.connect('activate', self.sig_activate) self.entry.connect('focus-in-event', lambda x, y: self._focus_in()) self.entry.connect('focus-out-event', lambda x, y: self._focus_out()) self.widget.pack_start(self.entry) @@ -32,18 +47,46 @@ class Char(WidgetInterface): return self.entry.grab_focus() def set_value(self, record, field): - return field.set_client(record, self.entry.get_text() or False) + entry = self.entry.get_child() if self.autocomplete else self.entry + value = entry.get_text() or False + return field.set_client(record, value) def display(self, record, field): super(Char, self).display(record, field) + if record and self.autocomplete: + autocompletion = record.autocompletion.get(self.field_name, []) + current = [elem[0] for elem in self.entry_store] + if autocompletion != current: + self.entry_store.clear() + for row in autocompletion: + self.entry_store.append((row,)) + elif self.autocomplete: + self.entry_store.clear() if not field: - self.entry.set_text('') - return False - self.entry.set_text(field.get(record) or '') + value = '' + else: + value = field.get(record) or '' + self.display_value(value) + + def display_value(self, value): + if not self.autocomplete: + self.entry.set_text(value) + return + for idx, row in enumerate(self.entry_store): + if row[0] == value: + self.entry.set_active(idx) + return + else: + self.entry.get_child().set_text(value) def _readonly_set(self, value): + sensitivity = {True: gtk.SENSITIVITY_OFF, False: gtk.SENSITIVITY_AUTO} super(Char, self)._readonly_set(value) - self.entry.set_editable(not value) + if self.autocomplete: + self.entry.get_child().set_editable(not value) + self.entry.set_button_sensitivity(sensitivity[value]) + else: + self.entry.set_editable(not value) if value: self.widget.set_focus_chain([]) else: diff --git a/tryton/gui/window/view_form/view/form_gtk/many2one.py b/tryton/gui/window/view_form/view/form_gtk/many2one.py index d8290a7075..d1e94464c6 100644 --- a/tryton/gui/window/view_form/view/form_gtk/many2one.py +++ b/tryton/gui/window/view_form/view/form_gtk/many2one.py @@ -68,42 +68,9 @@ class Many2One(WidgetInterface): self._readonly = False - self.completion = gtk.EntryCompletion() - self.liststore = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING) - if attrs.get('completion', False): - try: - result = rpc.execute('model', self.attrs['relation'], - 'search_read', [], 0, None, None, ['rec_name'], - rpc.CONTEXT) - names = [(x['id'], x['rec_name']) for x in result] - except Exception, exception: - common.process_exception(exception, self.window) - names = [] - if names: - self.load_completion(names) - def grab_focus(self): return self.wid_text.grab_focus() - def load_completion(self, names): - self.completion.set_match_func(self.match_func, None) - self.completion.connect("match-selected", self.on_completion_match) - self.wid_text.set_completion(self.completion) - self.completion.set_model(self.liststore) - self.completion.set_text_column(1) - for object_id, name in names: - self.liststore.append([object_id, name]) - - def match_func(self, completion, key_string, iter, data): - model = self.completion.get_model() - modelstr = model[iter][1].lower() - return modelstr.startswith(key_string) - - def on_completion_match(self, completion, model, iter): - self.field.set_client(self.record, int(model[iter][0])) - self.display(self.record, self.field) - return True - def _readonly_set(self, value): self._readonly = value self.wid_text.set_editable(not value) diff --git a/tryton/gui/window/view_form/view/form_gtk/parser.py b/tryton/gui/window/view_form/view/form_gtk/parser.py index f45a5d7357..265bcd6641 100644 --- a/tryton/gui/window/view_form/view/form_gtk/parser.py +++ b/tryton/gui/window/view_form/view/form_gtk/parser.py @@ -543,7 +543,8 @@ class ParserForm(ParserInterface): continue for attr_name in ('relation', 'domain', 'selection', 'relation_field', 'string', 'views', 'invisible', - 'add_remove', 'sort', 'context', 'size', 'filename'): + 'add_remove', 'sort', 'context', 'size', 'filename', + 'autocomplete'): if attr_name in fields[name].attrs and \ not attr_name in attrs: attrs[attr_name] = fields[name].attrs[attr_name]