From 999f2737a05cb63c10b246ee6d17a356fca0ca74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Fri, 12 Feb 2010 11:07:09 +0100 Subject: [PATCH] Refactoring - Better naming in model - Group extends list - Add an index to Group - New common windows for dialog of many2one, one2many and many2many - one2many and many2many dialog use the same screen than the widget - Reduce the number of option in Screen - Remove RPCProxy to handle logout exception on every server call - Better naming of event signals - Fix on_change detection for many2many for issue1400 --- CHANGELOG | 10 + tryton/gui/window/form.py | 37 +- tryton/gui/window/preference.py | 43 +- tryton/gui/window/tree.py | 5 +- tryton/gui/window/view_board/action.py | 4 +- tryton/gui/window/view_form/model/field.py | 707 +++++++++--------- tryton/gui/window/view_form/model/group.py | 492 ++++++------ tryton/gui/window/view_form/model/record.py | 294 ++++---- tryton/gui/window/view_form/screen/screen.py | 448 ++++++----- tryton/gui/window/view_form/view/form.py | 150 ++-- .../window/view_form/view/form_gtk/binary.py | 44 +- .../view_form/view/form_gtk/calendar.py | 50 +- .../window/view_form/view/form_gtk/char.py | 22 +- .../view_form/view/form_gtk/checkbox.py | 17 +- .../window/view_form/view/form_gtk/float.py | 22 +- .../view_form/view/form_gtk/float_time.py | 18 +- .../window/view_form/view/form_gtk/image.py | 31 +- .../window/view_form/view/form_gtk/integer.py | 18 +- .../view_form/view/form_gtk/interface.py | 85 ++- .../view_form/view/form_gtk/many2many.py | 56 +- .../view_form/view/form_gtk/many2one.py | 306 +++----- .../view_form/view/form_gtk/one2many.py | 697 +++++------------ .../window/view_form/view/form_gtk/parser.py | 135 ++-- .../view_form/view/form_gtk/progressbar.py | 7 +- .../view_form/view/form_gtk/reference.py | 109 +-- .../view_form/view/form_gtk/selection.py | 29 +- .../window/view_form/view/form_gtk/textbox.py | 25 +- .../gui/window/view_form/view/form_gtk/url.py | 10 +- tryton/gui/window/view_form/view/graph.py | 4 - .../window/view_form/view/graph_gtk/graph.py | 2 +- tryton/gui/window/view_form/view/list.py | 192 ++--- .../view_form/view/list_gtk/editabletree.py | 126 ++-- .../window/view_form/view/list_gtk/parser.py | 471 ++++++------ .../window/view_form/view/screen_container.py | 12 + .../gui/window/view_form/view/widget_parse.py | 2 +- tryton/gui/window/win_export.py | 82 +- tryton/gui/window/win_form.py | 309 ++++++++ tryton/gui/window/win_import.py | 31 +- tryton/gui/window/win_search.py | 97 +-- tryton/gui/window/wizard.py | 2 +- tryton/rpc.py | 23 +- tryton/signal_event.py | 2 +- tryton/wizard/main.py | 8 +- 43 files changed, 2468 insertions(+), 2766 deletions(-) create mode 100644 tryton/gui/window/win_form.py diff --git a/CHANGELOG b/CHANGELOG index 082be95d29..a5ce322697 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,13 @@ +* Refactoring: + * Better naming in model + * Group extends list + * Add an index to Group + * New common windows for dialog of many2one, one2many and many2many + * one2many and many2many dialog use the same screen than the widget + * Reduce the number of option in Screen + * Remove RPCProxy to handle logout exception on every server call + * Better naming of event signals + * Fix on_change detection for many2many for issue1400 * Add PySON to replace python string evaluation * Don't show "Spell Checking" option if gtkspell is not present * Use the same internal model for many2many and one2many fields diff --git a/tryton/gui/window/form.py b/tryton/gui/window/form.py index 8a648ce277..a59f54ec40 100644 --- a/tryton/gui/window/form.py +++ b/tryton/gui/window/form.py @@ -52,8 +52,7 @@ class Form(SignalEvent): self.screen = Screen(self.model, self.window, view_type=view_type, context=self.context, view_ids=view_ids, domain=domain, - hastoolbar=CONFIG['form.toolbar'], show_search=True, - limit=limit, readonly=bool(auto_refresh), form=self, + limit=limit, readonly=bool(auto_refresh), search_value=search_value) self.screen.signal_connect(self, 'record-message', self._record_message) self.screen.signal_connect(self, 'record-modified', self._record_modified) @@ -227,9 +226,9 @@ class Form(SignalEvent): return True def update_attachment_count(self, reload=False): - value = self.screen.current_model - if value: - attachment_count = value.get_attachment_count(reload=reload) + record = self.screen.current_record + if record: + attachment_count = record.get_attachment_count(reload=reload) else: attachment_count = 0 self.signal('attachment-count', attachment_count) @@ -296,19 +295,21 @@ class Form(SignalEvent): self.message_info(_('Records removed!'), 'green') def sig_import(self, widget=None): - fields = [] while(self.screen.view_to_load): self.screen.load_view_to_load() - win = WinImport(self.model, self.screen.fields, fields, - parent=self.window) + fields = {} + for name, field in self.screen.group.fields.iteritems(): + fields[name] = field.attrs + win = WinImport(self.model, fields, parent=self.window) win.run() def sig_save_as(self, widget=None): - fields = [] - while(self.screen.view_to_load): + while self.screen.view_to_load: self.screen.load_view_to_load() - win = WinExport(self.model, self.ids_get(), - self.screen.fields, fields, parent=self.window, + fields = {} + for name, field in self.screen.group.fields.iteritems(): + fields[name] = field.attrs + win = WinExport(self.model, self.ids_get(), fields, parent=self.window, context=self.context) win.run() @@ -373,9 +374,9 @@ class Form(SignalEvent): else: obj_id = self.id_get() self.screen.search_filter() - for model in self.screen.models: - if model.id == obj_id: - self.screen.current_model = model + for record in self.screen.group: + if record.id == obj_id: + self.screen.current_record = record self.screen.display(set_cursor=True) break self.message_info('') @@ -383,8 +384,8 @@ class Form(SignalEvent): def sig_action(self, keyword='form_action'): ids = self.ids_get() - if self.screen.current_model: - obj_id = self.screen.current_model.id + if self.screen.current_record: + obj_id = self.screen.current_record.id else: obj_id = False if self.screen.current_view.view_type == 'form': @@ -403,7 +404,7 @@ class Form(SignalEvent): if 'active_id' in ctx: del ctx['active_id'] res = Action.exec_keyword(keyword, self.window, { - 'model': self.screen.resource, + 'model': self.screen.model_name, 'id': obj_id or False, 'ids': ids, }, context=ctx, alwaysask=True) diff --git a/tryton/gui/window/preference.py b/tryton/gui/window/preference.py index 3ab988b869..98ffe57c03 100644 --- a/tryton/gui/window/preference.py +++ b/tryton/gui/window/preference.py @@ -3,7 +3,6 @@ "Preference" import gettext import gtk -from tryton.rpc import RPCProxy import tryton.rpc as rpc import copy from tryton.gui.window.view_form.screen import Screen @@ -35,19 +34,13 @@ class Preference(object): self.win.set_default_response(gtk.RESPONSE_OK) - user = RPCProxy('res.user') - + args = ('model', 'res.user', 'get_preferences_fields_view', + rpc.CONTEXT) try: - res = user.get_preferences_fields_view(rpc.CONTEXT) + res = rpc.execute(*args) except Exception, exception: - if not common.process_exception(exception, parent): - self.win.destroy() - self.win = None - return - try: - res = user.get_preferences_fields_view(rpc.CONTEXT) - except Exception, exception: - common.process_exception(exception, parent) + res = common.process_exception(exception, parent, *args) + if not res: self.win.destroy() self.win = None return @@ -59,15 +52,17 @@ class Preference(object): fields = res['fields'] self.screen = Screen('res.user', self.win, view_type=[]) self.screen.new(default=False) - self.screen.add_view_custom(arch, fields, display=True) + self.screen.add_view(arch, fields, display=True) + args = ('model', 'res.user', 'get_preferences', False, rpc.CONTEXT) try: - preferences = user.get_preferences(False, rpc.CONTEXT) + preferences = rpc.execute(*args) except Exception, exception: - common.process_exception(exception, parent) - self.win.destroy() - raise - self.screen.current_model.set(preferences) + preferences = common.process_exception(exception, parent, *args) + if not preferences: + self.win.destroy() + raise + self.screen.current_record.set(preferences) width, height = self.screen.screen_container.size_get() parent_width, parent_height = parent.get_size() @@ -85,7 +80,7 @@ class Preference(object): res = False while True: if self.win.run() == gtk.RESPONSE_OK: - if self.screen.current_model.validate(): + if self.screen.current_record.validate(): vals = copy.copy(self.screen.get(get_modifiedonly=True)) if 'password' in vals: password = common.ask(_('Current Password:'), @@ -94,12 +89,14 @@ class Preference(object): break else: password = False - user = RPCProxy('res.user') + args = ('model', 'res.user', 'set_preferences', vals, + password, rpc.CONTEXT) try: - user.set_preferences(vals, password, rpc.CONTEXT) + rpc.execute(*args) except Exception, exception: - common.process_exception(exception, self.win) - continue + if not common.process_exception(exception, self.win, + *args): + continue res = True break else: diff --git a/tryton/gui/window/tree.py b/tryton/gui/window/tree.py index c80aa803ee..af33b2f6be 100644 --- a/tryton/gui/window/tree.py +++ b/tryton/gui/window/tree.py @@ -402,9 +402,8 @@ class Tree(SignalEvent): return res def sig_save_as(self, widget=None): - win = WinExport(self.model, self.ids_get(), - self.tree_res.fields, [], parent=self.window, - context=self.context) + win = WinExport(self.model, self.ids_get(), self.tree_res.fields, + parent=self.window, context=self.context) win.run() def sig_close(self): diff --git a/tryton/gui/window/view_board/action.py b/tryton/gui/window/view_board/action.py index 00c0f9af1b..27a9be3028 100644 --- a/tryton/gui/window/view_board/action.py +++ b/tryton/gui/window/view_board/action.py @@ -146,7 +146,9 @@ class Action(object): self.screen = Screen(self.action['res_model'], self._window, view_type=self.action['view_mode'], context=self.context, view_ids=view_ids, domain=self.domain, readonly=True) - alignment.add(self.screen.widget) + self.screen.screen_container.alternate_view = True + self.screen.switch_view(view_type=self.action['view_mode']) + alignment.add(self.screen.screen_container.alternate_viewport) name = self.screen.current_view.title self.screen.signal_connect(self, 'record-message', self._sig_label) elif self.action['view_type'] == 'tree': diff --git a/tryton/gui/window/view_form/model/field.py b/tryton/gui/window/view_form/model/field.py index ee5720a290..d8efcbc4c5 100644 --- a/tryton/gui/window/view_form/model/field.py +++ b/tryton/gui/window/view_form/model/field.py @@ -1,8 +1,8 @@ #This file is part of Tryton. The COPYRIGHT file at the top level of #this repository contains the full copyright notices and license terms. -from tryton.rpc import RPCProxy import tryton.rpc as rpc from tryton.common import DT_FORMAT, DHM_FORMAT, HM_FORMAT, datetime_strftime +import tryton.common as common from tryton.pyson import PYSONDecoder import time import datetime @@ -10,7 +10,7 @@ from decimal import Decimal import logging -class ModelField(object): +class Field(object): ''' get: return the values to write to the server get_client: return the value for the client widget (form_gtk) @@ -32,128 +32,128 @@ class CharField(object): self.internal = False self.default_attrs = {} - def sig_changed(self, model): - if self.get_state_attrs(model).get('readonly', False): + def sig_changed(self, record): + if self.get_state_attrs(record).get('readonly', False): return if self.attrs.get('on_change', False): - model.on_change(self.name, self.attrs['on_change']) + record.on_change(self.name, self.attrs['on_change']) if self.attrs.get('change_default', False): - model.cond_default(self.attrs['name'], self.get(model)) - model.on_change_with(self.name) + record.cond_default(self.attrs['name'], self.get(record)) + record.on_change_with(self.name) - def domain_get(self, model): - return model.expr_eval(self.attrs.get('domain', [])) + def domain_get(self, record): + return record.expr_eval(self.attrs.get('domain', [])) - def context_get(self, model, check_load=True, eval_context=True): + def context_get(self, record, check_load=True, eval_context=True): context = {} context.update(self.parent.context) if eval_context: - context.update(model.expr_eval(self.attrs.get('context', {}), + context.update(record.expr_eval(self.attrs.get('context', {}), check_load=check_load)) return context - def validate(self, model): + def validate(self, record): res = True - if bool(int(self.get_state_attrs(model).get('required') or 0)): - if not self.get(model) \ - and not bool(int(self.get_state_attrs(model + if bool(int(self.get_state_attrs(record).get('required') or 0)): + if not self.get(record) \ + and not bool(int(self.get_state_attrs(record ).get('readonly') or 0)): res = False - self.get_state_attrs(model)['valid'] = res + self.get_state_attrs(record)['valid'] = res return res - def set(self, model, value, modified=False): - model.value[self.name] = value + def set(self, record, value, modified=False): + record.value[self.name] = value if modified: - model.modified = True - model.modified_fields.setdefault(self.name) + record.modified = True + record.modified_fields.setdefault(self.name) return True - def get(self, model, check_load=True, readonly=True, modified=False): - return model.value.get(self.name, False) or False + def get(self, record, check_load=True, readonly=True, modified=False): + return record.value.get(self.name, False) or False - def get_eval(self, model, check_load=True): - return self.get(model, check_load=check_load, readonly=True, + def get_eval(self, record, check_load=True): + return self.get(record, check_load=check_load, readonly=True, modified=False) - def set_client(self, model, value, force_change=False): - internal = model.value.get(self.name, False) - prev_modified = model.modified - self.set(model, value) - if (internal or False) != (model.value.get(self.name, False) or False): - model.modified = True - model.modified_fields.setdefault(self.name) + def set_client(self, record, value, force_change=False): + internal = record.value.get(self.name, False) + prev_modified = record.modified + self.set(record, value) + if (internal or False) != (record.value.get(self.name, False) or False): + record.modified = True + record.modified_fields.setdefault(self.name) try: - self.sig_changed(model) + self.sig_changed(record) except: - model.value[self.name] = internal - model.modified = prev_modified + record.value[self.name] = internal + record.modified = prev_modified return - model.signal('record-changed', model) + record.signal('record-changed') - def get_client(self, model): - return model.value[self.name] or False + def get_client(self, record): + return record.value.get(self.name) or False - def set_default(self, model, value, modified=False): - res = self.set(model, value, modified=modified) + def set_default(self, record, value, modified=False): + res = self.set(record, value, modified=modified) return res - def set_on_change(self, model, value): - return self.set(model, value, modified=True) + def set_on_change(self, record, value): + return self.set(record, value, modified=True) - def get_default(self, model): - return self.get(model) + def get_default(self, record): + return self.get(record) - def create(self, model): + def create(self, record): return False - def state_set(self, model, states=('readonly', 'required', 'invisible')): - state_changes = model.expr_eval(self.attrs.get('states', {}), + def state_set(self, record, states=('readonly', 'required', 'invisible')): + state_changes = record.expr_eval(self.attrs.get('states', {}), check_load=False) for key in states: if key == 'readonly' and self.attrs.get(key, False): continue if key in state_changes: - self.get_state_attrs(model)[key] = state_changes[key] + self.get_state_attrs(record)[key] = state_changes[key] elif key in self.attrs: - self.get_state_attrs(model)[key] = self.attrs[key] - if model.mgroup.readonly: - self.get_state_attrs(model)['readonly'] = True + self.get_state_attrs(record)[key] = self.attrs[key] + if record.group.readonly: + self.get_state_attrs(record)['readonly'] = True if 'value' in state_changes: value = state_changes['value'] if value: - self.set(model, value, modified=True) + self.set(record, value, modified=True) - def get_state_attrs(self, model): - if self.name not in model.state_attrs: - model.state_attrs[self.name] = self.attrs.copy() - if model.mgroup.readonly: - model.state_attrs[self.name]['readonly'] = True - return model.state_attrs[self.name] + def get_state_attrs(self, record): + if self.name not in record.state_attrs: + record.state_attrs[self.name] = self.attrs.copy() + if record.group.readonly: + record.state_attrs[self.name]['readonly'] = True + return record.state_attrs[self.name] - def get_timestamp(self, model): + def get_timestamp(self, record): return {} class SelectionField(CharField): - def set(self, model, value, modified=False): + def set(self, record, value, modified=False): if isinstance(value, (list, tuple)): value = value[0] - return super(SelectionField, self).set(model, value, + return super(SelectionField, self).set(record, value, modified=modified) class DateTimeField(CharField): - def set_client(self, model, value, force_change=False): + def set_client(self, record, value, force_change=False): if value: value = datetime.datetime(*time.strptime(value, DHM_FORMAT)[:6]) - return super(DateTimeField, self).set_client(model, value, + return super(DateTimeField, self).set_client(record, value, force_change=force_change) - def get_client(self, model): - value = super(DateTimeField, self).get_client(model) + def get_client(self, record): + value = super(DateTimeField, self).get_client(record) if not value: return False return datetime_strftime(value, DHM_FORMAT) @@ -161,14 +161,14 @@ class DateTimeField(CharField): class DateField(CharField): - def set_client(self, model, value, force_change=False): + def set_client(self, record, value, force_change=False): if value: value = datetime.date(*time.strptime(value, DT_FORMAT)[:3]) - return super(DateField, self).set_client(model, value, + return super(DateField, self).set_client(record, value, force_change=force_change) - def get_client(self, model): - value = super(DateField, self).get_client(model) + def get_client(self, record): + value = super(DateField, self).get_client(record) if not value: return False return datetime_strftime(value, DT_FORMAT) @@ -176,77 +176,78 @@ class DateField(CharField): class FloatField(CharField): - def set_client(self, model, value, force_change=False): - internal = model.value[self.name] - prev_modified = model.modified - self.set(model, value) - digits = model.expr_eval(self.attrs.get('digits', (12, 2))) - if abs(float(internal or 0.0) - float(model.value[self.name] or 0.0)) \ + def set_client(self, record, value, force_change=False): + internal = record.value.get(self.name) + prev_modified = record.modified + self.set(record, value) + digits = record.expr_eval(self.attrs.get('digits', (12, 2))) + if abs(float(internal or 0.0) - float(record.value[self.name] or 0.0)) \ >= (10.0**(-int(digits[1]))): - if not self.get_state_attrs(model).get('readonly', False): - model.modified = True - model.modified_fields.setdefault(self.name) + if not self.get_state_attrs(record).get('readonly', False): + record.modified = True + record.modified_fields.setdefault(self.name) try: - self.sig_changed(model) + self.sig_changed(record) except: - model.value[self.name] = internal - model.modified = prev_modified - model.signal('record-changed', model) + record.value[self.name] = internal + record.modified = prev_modified + record.signal('record-changed') class NumericField(CharField): - def set_client(self, model, value, force_change=False): + def set_client(self, record, value, force_change=False): value = Decimal(str(value)) - internal = model.value[self.name] - prev_modified = model.modified - self.set(model, value) - digits = model.expr_eval(self.attrs.get('digits', (12, 2))) - if abs((internal or Decimal('0.0')) - (model.value[self.name] or Decimal('0.0'))) \ + internal = record.value.get(self.name) + prev_modified = record.modified + self.set(record, value) + digits = record.expr_eval(self.attrs.get('digits', (12, 2))) + if abs((internal or Decimal('0.0')) - \ + (record.value[self.name] or Decimal('0.0'))) \ >= Decimal(str(10.0**(-int(digits[1])))): - if not self.get_state_attrs(model).get('readonly', False): - model.modified = True - model.modified_fields.setdefault(self.name) + if not self.get_state_attrs(record).get('readonly', False): + record.modified = True + record.modified_fields.setdefault(self.name) try: - self.sig_changed(model) + self.sig_changed(record) except: - model.value[self.name] = internal - model.modified = prev_modified + record.value[self.name] = internal + record.modified = prev_modified return - model.signal('record-changed', model) + record.signal('record-changed') class IntegerField(CharField): - def get(self, model, check_load=True, readonly=True, modified=False): - return model.value.get(self.name, 0) or 0 + def get(self, record, check_load=True, readonly=True, modified=False): + return record.value.get(self.name) or 0 - def get_client(self, model): - return model.value[self.name] or 0 + def get_client(self, record): + return record.value.get(self.name) or 0 class BooleanField(CharField): - def set_client(self, model, value, force_change=False): + def set_client(self, record, value, force_change=False): value = bool(value) - internal = bool(model.value.get(self.name, False)) - prev_modified = model.modified - self.set(model, value) - if internal != bool(model.value.get(self.name, False)): - model.modified = True - model.modified_fields.setdefault(self.name) + internal = bool(record.value.get(self.name, False)) + prev_modified = record.modified + self.set(record, value) + if internal != bool(record.value.get(self.name, False)): + record.modified = True + record.modified_fields.setdefault(self.name) try: - self.sig_changed(model) + self.sig_changed(record) except: - model.value[self.name] = internal - model.modified = prev_modified + record.value[self.name] = internal + record.modified = prev_modified return - model.signal('record-changed', model) + record.signal('record-changed') - def get(self, model, check_load=True, readonly=True, modified=False): - return bool(model.value.get(self.name, False)) + def get(self, record, check_load=True, readonly=True, modified=False): + return bool(record.value.get(self.name, False)) - def get_client(self, model): - return bool(model.value[self.name]) + def get_client(self, record): + return bool(record.value.get(self.name)) class M2OField(CharField): @@ -254,350 +255,358 @@ class M2OField(CharField): internal = (id, name) ''' - def create(self, model): + def create(self, record): return (False, '') - def get(self, model, check_load=True, readonly=True, modified=False): - if model.value[self.name]: - if isinstance(model.value[self.name], (int, basestring, long)): - self.set(model, model.value[self.name]) - if isinstance(model.value[self.name], (int, basestring, long)): - return model.value[self.name] - return model.value[self.name][0] or False + def get(self, record, check_load=True, readonly=True, modified=False): + if record.value.get(self.name): + if isinstance(record.value[self.name], (int, basestring, long)): + self.set(record, record.value[self.name]) + if isinstance(record.value[self.name], (int, basestring, long)): + return record.value[self.name] + return record.value[self.name][0] or False return False - def get_client(self, model): - #model._check_load() - if model.value[self.name]: - if isinstance(model.value[self.name], (int, basestring, long)): - self.set(model, model.value[self.name]) - if isinstance(model.value[self.name], (int, basestring, long)): - return model.value[self.name] - return model.value[self.name][1] + def get_client(self, record): + if record.value.get(self.name): + if isinstance(record.value[self.name], (int, basestring, long)): + self.set(record, record.value[self.name]) + if isinstance(record.value[self.name], (int, basestring, long)): + return record.value[self.name] + return record.value[self.name][1] return False - def set(self, model, value, modified=False): + def set(self, record, value, modified=False): if value and isinstance(value, (int, long)): - rpc2 = RPCProxy(self.attrs['relation']) + args = ('model', self.attrs['relation'], 'read', value, + ['rec_name'], rpc.CONTEXT) try: - result = rpc2.read(value, ['rec_name'], rpc.CONTEXT) - except: - return + result = rpc.execute(*args) + except Exception, exception: + result = common.process_exception(exception, record.window, + *args) + if not result: + return value = value, result['rec_name'] if value and len(value) != 2: value = (False, '') - model.value[self.name + '.rec_name'] = '' + record.value[self.name + '.rec_name'] = '' else: if value: - model.value[self.name + '.rec_name'] = value[1] + record.value[self.name + '.rec_name'] = value[1] else: - model.value[self.name + '.rec_name'] = '' - model.value[self.name] = value or (False, '') + record.value[self.name + '.rec_name'] = '' + record.value[self.name] = value or (False, '') if modified: - model.modified = True - model.modified_fields.setdefault(self.name) + record.modified = True + record.modified_fields.setdefault(self.name) - def set_client(self, model, value, force_change=False): - internal = model.value[self.name] - prev_modified = model.modified - self.set(model, value) - if (internal[0] or False) != (model.value[self.name][0] or False): - model.modified = True - model.modified_fields.setdefault(self.name) + def set_client(self, record, value, force_change=False): + internal = record.value.get(self.name) + prev_modified = record.modified + self.set(record, value) + if (internal[0] or False) != (record.value[self.name][0] or False): + record.modified = True + record.modified_fields.setdefault(self.name) try: - self.sig_changed(model) + self.sig_changed(record) except: - model.value[self.name] = internal - model.modified = prev_modified + record.value[self.name] = internal + record.modified = prev_modified return - model.signal('record-changed', model) + record.signal('record-changed') elif force_change: try: - self.sig_changed(model) + self.sig_changed(record) except: - model.value[self.name] = internal + record.value[self.name] = internal return - model.signal('record-changed', model) + record.signal('record-changed') - def context_get(self, model, check_load=True, eval_context=True): - context = super(M2OField, self).context_get(model, + def context_get(self, record, check_load=True, eval_context=True): + context = super(M2OField, self).context_get(record, check_load=check_load, eval_context=eval_context) if eval_context and self.attrs.get('datetime_field'): - context['_datetime'] = model.get_eval( + context['_datetime'] = record.get_eval( check_load=check_load)[self.attrs.get('datetime_field')] return context class O2MField(CharField): ''' - internal = ModelRecordGroup of the related objects + internal = Group of the related objects ''' def __init__(self, parent, attrs): super(O2MField, self).__init__(parent, attrs) self.context = {} - def create(self, model): - from group import ModelRecordGroup - mod = ModelRecordGroup(self.attrs['relation'], {}, model.window, - parent=model, parent_name=self.attrs.get('relation_field', ''), + def create(self, record): + from group import Group + group = Group(self.attrs['relation'], {}, record.window, + parent=record, parent_name=self.attrs.get('relation_field', ''), context=self.context, parent_datetime_field=self.attrs.get('datetime_field')) - mod.signal_connect(mod, 'model-changed', self._model_changed) - return mod + group.signal_connect(group, 'group-changed', self._group_changed) + return group - def _model_changed(self, group, model): - if model.modified: - model.parent.modified = True - model.parent.modified_fields.setdefault(self.name) - self.sig_changed(model.parent) - model.parent.signal('record-changed', model.parent) + def _group_changed(self, group, record): + record.parent.modified = True + record.parent.modified_fields.setdefault(self.name) + self.sig_changed(record.parent) + record.parent.signal('record-changed') - def get_client(self, model): - return model.value[self.name] + def get_client(self, record): + return record.value.get(self.name) - def get(self, model, check_load=True, readonly=True, modified=False): - if not model.value[self.name]: + def get(self, record, check_load=True, readonly=True, modified=False): + if record.value.get(self.name) is None: return [] result = [('add', [])] - for model2 in model.value[self.name].models: - if model2.id > 0: - values = model2.get(check_load=check_load, get_readonly=readonly, + for record2 in record.value[self.name]: + if record2.id > 0: + values = record2.get(check_load=check_load, get_readonly=readonly, get_modifiedonly=modified) - if model2.is_modified() and values: - result.append(('write', model2.id, values)) + if record2.is_modified() and values: + result.append(('write', record2.id, values)) else: - result[0][1].append(model2.id) + result[0][1].append(record2.id) else: result.append(('create', - model2.get(check_load=check_load, get_readonly=readonly))) - if model.value[self.name].model_removed: + record2.get(check_load=check_load, get_readonly=readonly))) + if record.value[self.name].record_removed: result.append(('unlink', [x.id for x in \ - model.value[self.name].model_removed])) - if model.value[self.name].model_deleted: + record.value[self.name].record_removed])) + if record.value[self.name].record_deleted: result.append(('delete', [x.id for x in \ - model.value[self.name].model_deleted])) + record.value[self.name].record_deleted])) return result - def get_timestamp(self, model): - if not model.value[self.name]: + def get_timestamp(self, record): + if record.value.get(self.name) is None: return {} result = {} - for model2 in (model.value[self.name].models \ - + model.value[self.name].model_removed \ - + model.value[self.name].model_deleted): - result.update(model2.get_timestamp()) + for record2 in (record.value[self.name] \ + + record.value[self.name].record_removed \ + + record.value[self.name].record_deleted): + result.update(record2.get_timestamp()) return result - def get_eval(self, model, check_load=True): + def get_eval(self, record, check_load=True): result = [] - if not model.value[self.name]: + if record.value.get(self.name) is None: return [] - for model2 in model.value[self.name].models: - result.append(model2.get_eval(check_load=check_load)) + for record2 in record.value[self.name]: + result.append(record2.get_eval(check_load=check_load)) return result - def set(self, model, value, modified=False): - from group import ModelRecordGroup - mod = ModelRecordGroup(self.attrs['relation'], {}, model.window, - parent=model, parent_name=self.attrs.get('relation_field', ''), + def set(self, record, value, modified=False): + from group import Group + group = record.value.get(self.name) + if group is not None: + group.signal_unconnect(group) + group = Group(self.attrs['relation'], {}, record.window, + parent=record, parent_name=self.attrs.get('relation_field', ''), context=self.context, parent_datetime_field=self.attrs.get('datetime_field')) - mod.signal_connect(mod, 'model-changed', self._model_changed) - mod.model_deleted.extend(x for x in model.value[self.name].models - if x.id > 0) - mod.model_deleted.extend(model.value[self.name].model_deleted) - mod.model_removed.extend(model.value[self.name].model_removed) - model.value[self.name] = mod - model.value[self.name].load(value, display=False) + record.value[self.name] = group + record.value[self.name].load(value, display=False) + group.signal_connect(group, 'group-changed', self._group_changed) if modified: - model.modified = True - model.modified_fields.setdefault(self.name) + record.modified = True + record.modified_fields.setdefault(self.name) - def set_client(self, model, value, force_change=False): - self.set(model, value) - model.signal('record-changed', model) + def set_client(self, record, value, force_change=False): + pass - def set_default(self, model, value, modified=False): - from group import ModelRecordGroup + def set_default(self, record, value, modified=False): + from group import Group if value and len(value) and isinstance(value[0], (int, long)): - return self.set(model, value, modified=modified) + return self.set(record, value, modified=modified) fields = {} if value and len(value): - context = self.context_get(model) - rpc2 = RPCProxy(self.attrs['relation']) + context = self.context_get(record) + field_names = [] + for val in value: + for fieldname in val.keys(): + if fieldname not in field_names: + field_names.append(fieldname) + args = ('model', self.attrs['relation'], 'fields_get', + field_names, context) try: - fields_name = [] - for val in value: - for fieldname in val.keys(): - if fieldname not in fields_name: - fields_name.append(fieldname) - fields = rpc2.fields_get(fields_name, context) - except: - return False + fields = rpc.execute(*args) + except Exception, exception: + fields = common.process_exception(exception, record.window, + *args) + if not fields: + return False - mod = ModelRecordGroup( - self.attrs['relation'], fields, model.window, parent=model, + group = record.value.get(self.name) + if group is not None: + group.signal_unconnect(group) + group = Group( + self.attrs['relation'], fields, record.window, parent=record, parent_name=self.attrs.get('relation_field', ''), context=self.context, parent_datetime_field=self.attrs.get('datetime_field')) - mod.signal_connect(model.value[self.name], - 'model-changed', self._model_changed) - mod.model_deleted.extend(x for x in model.value[self.name].models + group.signal_connect(group, + 'group-changed', self._group_changed) + group.record_deleted.extend(x for x in record.value[self.name] if x.id > 0) - mod.model_deleted.extend(model.value[self.name].model_deleted) - mod.model_removed.extend(model.value[self.name].model_removed) - model.value[self.name] = mod - mod = None - for record in (value or []): - mod = model.value[self.name].model_new(default=False) - mod.set_default(record, modified=modified) - model.value[self.name].model_add(mod) - model.value[self.name].current_model = mod - #mod.signal('record-changed') + group.record_deleted.extend(record.value[self.name].record_deleted) + group.record_removed.extend(record.value[self.name].record_removed) + record.value[self.name] = group + for vals in (value or []): + new_record = record.value[self.name].new(default=False) + new_record.set_default(vals, modified=modified) + group.add(new_record) return True - def set_on_change(self, model, value): + def set_on_change(self, record, value): if isinstance(value, (list, tuple)): - return self.set(model, value, modified=True) + return self.set(record, value, modified=True) if value and (value.get('add') or value.get('update')): - context = self.context_get(model) - rpc2 = RPCProxy(self.attrs['relation']) + context = self.context_get(record) + field_names = [] + for val in (value.get('add', []) + value.get('update', [])): + for fieldname in val.keys(): + if fieldname not in field_names: + field_names.append(fieldname) + args = ('model', self.attrs['relation'], 'fields_get', + field_names, context) try: - fields_name = [] - for val in (value.get('add', []) + value.get('update', [])): - for fieldname in val.keys(): - if fieldname not in fields_name: - fields_name.append(fieldname) - fields = rpc2.fields_get(fields_name, context) - except: - return False + fields = rpc.execute(*args) + except Exception, exception: + fields = common.process_exception(exception, record.window, + *args) + if not fields: + return False to_remove = [] - for mod in model.value[self.name]: - if not mod.id: - to_remove.append(mod) + for record2 in record.value[self.name]: + if not record2.id: + to_remove.append(record2) if value and value.get('remove'): - for model_id in value['remove']: - mod = model.value[self.name].get_by_id(model_id) - if mod: - to_remove.append(mod) - for mod in to_remove: - model.value[self.name].remove(mod, signal=False) + for record_id in value['remove']: + record2 = record.value[self.name].get(record_id) + if record2 is not None: + to_remove.append(record2) + for record2 in to_remove: + record.value[self.name].remove(record2, signal=False) - mod = None if value and value.get('add') or value.get('update', []): - model.value[self.name].add_fields(fields, model.value[self.name], - signal=False) - for record in value.get('add', []): - mod = model.value[self.name].model_new(default=False) - model.value[self.name].model_add(mod) - mod.set(record, modified=True, signal=False) + record.value[self.name].add_fields(fields, signal=False) + for vals in value.get('add', []): + new_record = record.value[self.name].new(default=False, + signal=False) + record.value[self.name].add(new_record) + new_record.set(vals, modified=True, signal=False) - for record in value.get('update', []): - if 'id' not in record: + for vals in value.get('update', []): + if 'id' not in vals: continue - mod = model.value[self.name].get_by_id(record['id']) - if mod: - mod.set(record, modified=True, signal=False) - model.value[self.name].current_model = mod + record2 = record.value[self.name].get(vals['id']) + if record2 is not None: + record2.set(vals, modified=True, signal=False) return True - def get_default(self, model): - res = [x.get_default() for x in model.value[self.name].models or []] + def get_default(self, record): + res = [x.get_default() for x in record.value.get(self.name) or []] return res - def validate(self, model): + def validate(self, record): res = True - for model2 in model.value[self.name].models: - if not model2.loaded: + for record2 in record.value[self.name]: + if not record2.loaded: continue - if not model2.validate(): - if not model2.is_modified(): - model.value[self.name].models.remove(model2) + if not record2.validate(): + if not record2.is_modified(): + record.value[self.name].remove(record2) else: res = False - if not super(O2MField, self).validate(model): + if not super(O2MField, self).validate(record): res = False - self.get_state_attrs(model)['valid'] = res + self.get_state_attrs(record)['valid'] = res return res - def state_set(self, model, states=('readonly', 'required', 'invisible')): - super(O2MField, self).state_set(model, states=states) - if self.get_state_attrs(model).get('readonly', False): - model.value[self.name].readonly = True + def state_set(self, record, states=('readonly', 'required', 'invisible')): + super(O2MField, self).state_set(record, states=states) + if self.get_state_attrs(record).get('readonly', False): + record.value[self.name].readonly = True else: - model.value[self.name].readonly = False + record.value[self.name].readonly = False - def get_removed_ids(self, model): - return [x.id for x in model.value[self.name].model_removed] + def get_removed_ids(self, record): + return [x.id for x in record.value[self.name].record_removed] class M2MField(O2MField): - def get_default(self, model): - return [x.id for x in model.value[self.name].models - if x.id > 0] + def get_default(self, record): + return [x.id for x in record.value.get(self.name) or [] if x.id > 0] - def get_eval(self, model, check_load=True): - return [x.id for x in model.value[self.name].models] + def get_eval(self, record, check_load=True): + return [x.id for x in record.value.get(self.name) or []] - def set(self, model, value, modified=False): - from group import ModelRecordGroup - mod = ModelRecordGroup(self.attrs['relation'], {}, model.window, - parent=model, parent_name=self.attrs.get('relation_field', ''), + def set(self, record, value, modified=False): + from group import Group + group = record.value.get(self.name) + if group is not None: + group.signal_unconnect(group) + group = Group(self.attrs['relation'], {}, record.window, + parent=record, parent_name=self.attrs.get('relation_field', ''), context=self.context, parent_datetime_field=self.attrs.get('datetime_field')) - mod.signal_connect(mod, 'model-changed', self._model_changed) - mod.model_removed.extend(model.value[self.name].models) - mod.model_deleted.extend(model.value[self.name].model_deleted) - mod.model_removed.extend(model.value[self.name].model_removed) - model.value[self.name] = mod - model.value[self.name].load(value, display=False) + if record.value.get(self.name): + group.record_removed.extend(record.value[self.name]) + group.record_deleted.extend(record.value[self.name].record_deleted) + group.record_removed.extend(record.value[self.name].record_removed) + record.value[self.name] = group + record.value[self.name].load(value, display=False) + group.signal_connect(group, 'group-changed', self._group_changed) if modified: - model.modified = True - model.modified_fields.setdefault(self.name) + record.modified = True + record.modified_fields.setdefault(self.name) class ReferenceField(CharField): - def get_client(self, model): - if model.value[self.name]: - return model.value[self.name] + def get_client(self, record): + if record.value.get(self.name): + return record.value[self.name] return False - def get(self, model, check_load=True, readonly=True, modified=False): - if model.value[self.name]: - if isinstance(model.value[self.name][1], (list, tuple)): - return '%s,%s' % (model.value[self.name][0], - str(model.value[self.name][1][0])) + def get(self, record, check_load=True, readonly=True, modified=False): + if record.value.get(self.name): + if isinstance(record.value[self.name][1], (list, tuple)): + return '%s,%s' % (record.value[self.name][0], + str(record.value[self.name][1][0])) else: - return '%s,%s' % (model.value[self.name][0], - str(model.value[self.name][1])) + return '%s,%s' % (record.value[self.name][0], + str(record.value[self.name][1])) return False - def set_client(self, model, value, force_change=False): - internal = model.value[self.name] - prev_modified = model.modified - #model.value[self.name] = value - self.set(model, value) - if (internal or False) != (model.value[self.name] or False): - model.modified = True - model.modified_fields.setdefault(self.name) + def set_client(self, record, value, force_change=False): + internal = record.value.get(self.name) + prev_modified = record.modified + self.set(record, value) + if (internal or False) != (record.value[self.name] or False): + record.modified = True + record.modified_fields.setdefault(self.name) try: - self.sig_changed(model) + self.sig_changed(record) except: - model.value[self.name] = internal - model.modified = prev_modified + record.value[self.name] = internal + record.modified = prev_modified return - model.signal('record-changed', model) + record.signal('record-changed') - def set(self, model, value, modified=False): + def set(self, record, value, modified=False): if not value: - model.value[self.name] = False + record.value[self.name] = False return ref_model, (ref_id, ref_str) = value if ref_model: @@ -605,28 +614,32 @@ class ReferenceField(CharField): if not ref_id: ref_str = '' if not ref_str and ref_id: - rpc2 = RPCProxy(ref_model) + args = ('model', ref_model, 'read', ref_id, + ['rec_name'], rpc.CONTEXT) try: - result = rpc2.read(ref_id, ['rec_name'], - rpc.CONTEXT)['rec_name'] - except: - return + result = rpc.execute(*args) + except Exception, exception: + result = common.process_exception(exception, record.window, + *args) + if not result: + return + result = result['rec_name'] if result: - model.value[self.name] = ref_model, (ref_id, result) - model.value[self.name + '.rec_name'] = result + record.value[self.name] = ref_model, (ref_id, result) + record.value[self.name + '.rec_name'] = result else: - model.value[self.name] = ref_model, (0, '') - model.value[self.name + '.rec_name'] = '' + record.value[self.name] = ref_model, (0, '') + record.value[self.name + '.rec_name'] = '' else: - model.value[self.name] = ref_model, (ref_id, ref_str) - model.value[self.name + '.rec_name'] = ref_str + record.value[self.name] = ref_model, (ref_id, ref_str) + record.value[self.name + '.rec_name'] = ref_str else: - model.value[self.name] = ref_model, (ref_id, ref_id) - if self.name + '.rec_name' in model.value: - del model.value[self.name + '.rec_name'] + record.value[self.name] = ref_model, (ref_id, ref_id) + if self.name + '.rec_name' in record.value: + del record.value[self.name + '.rec_name'] if modified: - model.modified = True - model.modified_fields.setdefault(self.name) + record.modified = True + record.modified_fields.setdefault(self.name) TYPES = { 'char': CharField, diff --git a/tryton/gui/window/view_form/model/group.py b/tryton/gui/window/view_form/model/group.py index 92172338c6..366ee0592d 100644 --- a/tryton/gui/window/view_form/model/group.py +++ b/tryton/gui/window/view_form/model/group.py @@ -1,120 +1,114 @@ #This file is part of Tryton. The COPYRIGHT file at the top level of #this repository contains the full copyright notices and license terms. -from tryton.rpc import RPCProxy import tryton.rpc as rpc -from record import ModelRecord -import field +from record import Record +from field import Field, O2MField from tryton.signal_event import SignalEvent import tryton.common as common -class ModelList(list): - def __init__(self, screen): - super(ModelList, self).__init__() - self.lock_signal = False - self.__screen = screen +class Group(SignalEvent, list): - def insert(self, pos, obj): - if pos >= 1: - self.__getitem__(pos - 1).next[id(self)] = obj - if pos < self.__len__(): - obj.next[id(self)] = self.__getitem__(pos) - else: - obj.next[id(self)] = None - super(ModelList, self).insert(pos, obj) - if not self.lock_signal: - self.__screen.signal('record-changed', ('record-added', pos)) - - def append(self, obj): - if self.__len__() >= 1: - self.__getitem__(self.__len__() - 1).next[id(self)] = obj - obj.next[id(self)] = None - super(ModelList, self).append(obj) - if not self.lock_signal: - self.__screen.signal('record-changed', ('record-added', -1)) - - def remove(self, obj): - idx = self.index(obj) - if idx >= 1: - if idx + 1 < self.__len__(): - self.__getitem__(idx - 1).next[id(self)] = self.__getitem__(idx + 1) - else: - self.__getitem__(idx - 1).next[id(self)] = None - super(ModelList, self).remove(obj) - if not self.lock_signal: - self.__screen.signal('record-changed', ('record-removed', idx)) - - def clear(self): - while self: - self.pop() - if not self.lock_signal: - self.__screen.signal('record-changed', - ('record-removed', len(self))) - - def move(self, obj, pos): - self.lock_signal = True - if self.__len__() > pos: - idx = self.index(obj) - self.remove(obj) - if pos > idx: - pos -= 1 - self.insert(pos, obj) - else: - self.remove(obj) - self.append(obj) - self.lock_signal = False - - def __setitem__(self, key, value): - super(ModelList, self).__setitem__(key, value) - if not self.lock_signal: - self.__screen.signal('record-changed', ('record-changed', key)) - - -class ModelRecordGroup(SignalEvent): - - def __init__(self, resource, fields, window, ids=None, parent=None, + def __init__(self, model_name, fields, window, ids=None, parent=None, parent_name='', context=None, readonly=False, parent_datetime_field=None): - super(ModelRecordGroup, self).__init__() + super(Group, self).__init__() + self.lock_signal = False self.window = window self.parent = parent self.parent_name = parent_name self.parent_datetime_field = parent_datetime_field self._context = context or {} - self.resource = resource - self.rpc = RPCProxy(resource) - self.fields = fields - self.mfields = {} - ModelRecordGroup.mfields_load(fields.keys(), self) - self.models = ModelList(self) + self.model_name = model_name + self.fields = {} + self.load_fields(fields) self.current_idx = None self.load(ids) - self.model_removed = [] - self.model_deleted = [] + self.record_removed = [] + self.record_deleted = [] self.on_write = set() self.readonly = readonly if self._context.get('_datetime'): self.readonly = True + self.__id2record = {} + + def insert(self, pos, record): + if pos >= 1: + self.__getitem__(pos - 1).next[id(self)] = record + if pos < self.__len__(): + record.next[id(self)] = self.__getitem__(pos) + else: + record.next[id(self)] = None + super(Group, self).insert(pos, record) + self.__id2record[record.id] = record + if not self.lock_signal: + self.signal('group-list-changed', ('record-added', pos)) + + def append(self, record): + if self.__len__() >= 1: + self.__getitem__(self.__len__() - 1).next[id(self)] = record + record.next[id(self)] = None + super(Group, self).append(record) + self.__id2record[record.id] = record + if not self.lock_signal: + self.signal('group-list-changed', ('record-added', -1)) + + def _remove(self, record): + idx = self.index(record) + if idx >= 1: + if idx + 1 < self.__len__(): + self.__getitem__(idx - 1).next[id(self)] = \ + self.__getitem__(idx + 1) + else: + self.__getitem__(idx - 1).next[id(self)] = None + super(Group, self).remove(record) + del self.__id2record[record.id] + if not self.lock_signal: + self.signal('group-list-changed', ('record-removed', idx)) + + def clear(self): + while len(self): + self.pop() + if not self.lock_signal: + self.signal('group-list-changed', ('record-removed', len(self))) + self.record_removed = [] + self.record_deleted = [] + + def move(self, record, pos): + self.lock_signal = True + if self.__len__() > pos: + idx = self.index(record) + self._remove(record) + if pos > idx: + pos -= 1 + self.insert(pos, record) + else: + self._remove(record) + self.append(record) + self.lock_signal = False + + def __setitem__(self, i, value): + super(Group, self).__setitem__(i, value) + if not self.lock_signal: + self.signal('group-list-changed', ('record-changed', key)) def __repr__(self): - return '' % (self.resource, id(self)) + return '' % (self.model_name, id(self)) - @staticmethod - def mfields_load(fkeys, models): - for fname in fkeys: - fvalue = models.fields[fname] - modelfield = field.ModelField(fvalue['type']) - fvalue['name'] = fname - models.mfields[fname] = modelfield(models, fvalue) - if isinstance(models.mfields[fname], field.O2MField) \ - and '_datetime' in models._context: - models.mfields[fname].context.update({ - '_datetime': models._context['_datetime'], + def load_fields(self, fields): + for name, attr in fields.iteritems(): + field = Field(attr['type']) + attr['name'] = name + self.fields[name] = field(self, attr) + if isinstance(self.fields[name], O2MField) \ + and '_datetime' in self._context: + self.fields[name].context.update({ + '_datetime': self._context['_datetime'], }) def save(self): - for model in self.models: - saved = model.save() + for record in self: + saved = record.save() self.writen(saved) def writen(self, ids): @@ -127,97 +121,63 @@ class ModelRecordGroup(SignalEvent): return ids def reload(self, ids): - for model in self.models: - if model.id in ids and not model.modified: - model._loaded.clear() + for record in self: + if record.id in ids and not record.modified: + record._loaded.clear() def on_write_ids(self, ids): if not self.on_write: return False res = [] for fnct in self.on_write: + args = ('model', self.model_name, fnct, ids, self.context) try: - res += getattr(self.rpc, fnct)(ids, self.context) + res += rpc.execute(*args) except Exception, exception: - common.process_exception(exception, self.window) - return False + res2 = common.process_exception(exception, self.window, *args) + if not res2: + return False + res += res2 return list({}.fromkeys(res)) - def _load_for(self, values): - if len(values)>10: - self.models.lock_signal = True - id2values = dict((value['id'], value) for value in values) - for model in self.models: - if model.id in id2values: - model.set(id2values[model.id]) - if len(values)>10: - self.models.lock_signal = False - self.signal('record-cleared') - def load(self, ids, display=True, modified=False): if not ids: return True - old_ids = [x.id for x in self.models] + old_ids = [x.id for x in self] ids = [x for x in ids if x not in old_ids] if not ids: return True - if len(ids) > 10: - self.models.lock_signal = True - newmod = None - newmods = [] + if len(ids) > 1: + self.lock_signal = True + new_records = [] for id in ids: - newmod = ModelRecord(self.resource, id, self.window, + new_record = Record(self.model_name, id, self.window, parent=self.parent, parent_name=self.parent_name, group=self) - self.models.append(newmod) - newmods.append(newmod) - newmod.signal_connect(self, 'record-changed', self._record_changed) - newmod.signal_connect(self, 'record-modified', self._record_modified) - for model in self.model_removed[:]: - if model.id in ids: - self.model_removed.remove(model) - for model in self.model_deleted[:]: - if model.id in ids: - self.model_deleted.remove(model) - if len(ids) > 10: - self.models.lock_signal = False - self.signal('record-cleared') + self.append(new_record) + new_records.append(new_record) + new_record.signal_connect(self, 'record-changed', self._record_changed) + new_record.signal_connect(self, 'record-modified', self._record_modified) + for record in self.record_removed[:]: + if record.id in ids: + self.record_removed.remove(record) + for record in self.record_deleted[:]: + if record.id in ids: + self.record_deleted.remove(record) + if self.lock_signal: + self.lock_signal = False + self.signal('group-cleared') - ctx = rpc.CONTEXT.copy() - ctx.update(self.context) - if self.fields: - try: - values = self.rpc.read(ids[:80], self.fields.keys() + \ - [x + '.rec_name' for x in self.fields - if self.fields[x]['type'] in \ - ('many2one', 'reference')] + \ - ['_timestamp'], ctx) - except Exception, exception: - common.process_exception(exception, self.window) - return False - if not values: - return False - self._load_for(values) + if new_records and display: + self.signal('group-changed', new_records[0]) - if newmod and display: - self.signal('model-changed', newmod) - - if modified and newmods: - for newmod in newmods: - newmod.modified = True - # send record-changed only once - if newmod.parent: - newmod.signal('record-changed', newmod.parent) + if new_records and modified: + new_records[0].signal('record-changed') self.current_idx = 0 return True - def clear(self): - self.models.clear() - self.model_removed = [] - self.model_deleted = [] - def _get_context(self): ctx = rpc.CONTEXT.copy() ctx.update(self._context) @@ -225,176 +185,146 @@ class ModelRecordGroup(SignalEvent): ctx['_datetime'] = self.parent.get_eval(check_load=False)\ [self.parent_datetime_field] return ctx + context = property(_get_context) - def model_add(self, model, position=-1, modified=True): - #TODO To be checked - if not model.mgroup is self: + def add(self, record, position=-1, modified=True): + if not record.group is self: fields = {} - for i in model.mgroup.fields: - fields[model.mgroup.fields[i]['name']] = \ - model.mgroup.fields[i] - self.add_fields(fields, self) - self.add_fields(self.fields, model.mgroup) - model.mgroup = self + for i in record.group.fields: + fields[record.group.fields[i].attrs['name']] = \ + record.group.fields[i].attrs + self.add_fields(fields) + record.group = self if position == -1: - self.models.append(model) + self.append(record) else: - self.models.insert(position, model) + self.insert(position, record) self.current_idx = position - model.parent = self.parent - model.parent_name = self.parent_name - model.window = self.window + record.parent = self.parent + record.parent_name = self.parent_name + record.window = self.window if modified: - model.modified = True - model.signal_connect(self, 'record-changed', self._record_changed) - model.signal_connect(self, 'record-modified', self._record_modified) - return model - - def model_move(self, model, position=0): - self.models.move(model, position) + record.modified = True + record.signal_connect(self, 'record-changed', self._record_changed) + record.signal_connect(self, 'record-modified', self._record_modified) + return record def set_sequence(self, field='sequence'): index = 0 - for model in self.models: - if model[field]: - if index >= model[field].get(model): + for record in self: + if record[field]: + if index >= record[field].get(record): index += 1 - model[field].set(model, index, modified=True) + record[field].set(record, index, modified=True) else: - index = model[field].get(model) + index = record[field].get(record) - def model_new(self, default=True, domain=None, context=None): - newmod = ModelRecord(self.resource, None, self.window, group=self, + def new(self, default=True, domain=None, context=None, signal=True): + record = Record(self.model_name, None, self.window, group=self, parent=self.parent, parent_name=self.parent_name, new=True) - newmod.signal_connect(self, 'record-changed', self._record_changed) - newmod.signal_connect(self, 'record-modified', self._record_modified) + record.signal_connect(self, 'record-changed', self._record_changed) + record.signal_connect(self, 'record-modified', self._record_modified) if default: ctx = {} ctx.update(context or {}) ctx.update(self.context) - newmod.default_get(domain, ctx) - self.signal('model-changed', newmod) - return newmod + record.default_get(domain, ctx) + if signal: + self.signal('group-changed', record) + return record - def model_remove(self, model): - idx = self.models.index(model) - self.models.remove(model) - if model.parent: - model.parent.modified = True - if self.models: - self.current_idx = min(idx, len(self.models)-1) + def remove(self, record, remove=False, modified=True, signal=True): + idx = self.index(record) + if self[idx].id > 0: + if remove: + self.record_removed.append(self[idx]) + else: + self.record_deleted.append(self[idx]) + if record.parent: + record.parent.modified = True + if modified: + record.modified = True + self._remove(self[idx]) + + if len(self): + self.current_idx = min(idx, len(self) - 1) else: self.current_idx = None - def _record_changed(self, model, signal_data): - self.signal('model-changed', model) + if signal: + record.signal('record-changed', record.parent) - def _record_modified(self, model, signal_data): - self.signal('record-modified', model) + def _record_changed(self, record, signal_data): + self.signal('group-changed', record) + + def _record_modified(self, record, signal_data): + self.signal('record-modified', record) def prev(self): - if self.models and self.current_idx is not None: - self.current_idx = (self.current_idx - 1) % len(self.models) - elif self.models: + if len(self) and self.current_idx is not None: + self.current_idx = (self.current_idx - 1) % len(self) + elif len(self): self.current_idx = 0 else: return None - return self.models[self.current_idx] + return self[self.current_idx] def next(self): - if self.models and self.current_idx is not None: - self.current_idx = (self.current_idx + 1) % len(self.models) - elif self.models: + if len(self) and self.current_idx is not None: + self.current_idx = (self.current_idx + 1) % len(self) + elif len(self): self.current_idx = 0 else: return None - return self.models[self.current_idx] + return self[self.current_idx] - def remove(self, model, remove=False, modified=True, signal=True): - idx = self.models.index(model) - if self.models[idx].id > 0: - if remove: - self.model_removed.append(self.models[idx]) - else: - self.model_deleted.append(self.models[idx]) - if model.parent: - model.parent.modified = True - if modified: - model.modified = True - self.models.remove(self.models[idx]) - if signal: - model.signal('record-changed', model.parent) - - def add_fields_custom(self, fields, models): - to_add = [] - for field_add in fields.iterkeys(): - if not field_add in models.fields: - models.fields[field_add] = fields[field_add] - models.fields[field_add]['name'] = field_add - to_add.append(field_add) - else: - models.fields[field_add].update(fields[field_add]) - ModelRecordGroup.mfields_load(to_add, models) - for fname in to_add: - for model in models.models: - model.value[fname] = self.mfields[fname].create(model) - return to_add - - def add_fields(self, fields, models, context=None, signal=True): + def add_fields(self, fields, context=None, signal=True): if context is None: context = {} - to_add = self.add_fields_custom(fields, models) - models = models.models - if not len(models): + + to_add = {} + for name, attr in fields.iteritems(): + if name not in self.fields: + to_add[name] = attr + else: + self.fields[name].attrs.update(attr) + self.load_fields(to_add) + for name in to_add: + for record in self: + record.value[name] = self.fields[name].create(record) + + if not len(self): return True - old = [] new = [] - for model in models: - if model.id > 0: - if model.is_modified(): - old.append(model.id) - else: - new.append(model) + for record in self: + if record.id <= 0: + new.append(record) ctx = context.copy() - if len(old) and len(to_add): - ctx.update(rpc.CONTEXT) - ctx.update(self.context) - try: - values = self.rpc.read(old, to_add + \ - [x + '.rec_name' for x in to_add - if self.fields[x]['type'] \ - in ('many2one', 'reference')], ctx) - except Exception, exception: - common.process_exception(exception, self.window) - return False - if values: - for value in values: - value_id = value['id'] - if 'id' not in to_add: - del value['id'] - self[value_id].set(value, signal=False) + if len(new) and len(to_add): ctx.update(self.context) + args = ('model', self.model_name, 'default_get', to_add.keys(), ctx) try: - values = self.rpc.default_get(to_add, ctx) + values = rpc.execute(*args) except Exception, exception: - common.process_exception(exception, self.window) - return False - for field_to_add in to_add: - if field_to_add not in values: - values[field_to_add] = False - for mod in new: - mod.set_default(values, signal=signal) + values = common.process_exception(exception, self.window, *args) + if not values: + return False + for name in to_add: + if name not in values: + values[name] = False + for record in new: + record.set_default(values, signal=signal) - def __iter__(self): - return iter(self.models) + def get(self, id): + 'Return record with the id' + return self.__id2record.get(id) - def get_by_id(self, m_id): - for model in self.models: - if model.id == m_id: - return model - - __getitem__ = get_by_id + def id_changed(self, old_id): + 'Update index for old id' + record = self.__id2record[old_id] + self.__id2record[record.id] = record + del self.__id2record[old_id] diff --git a/tryton/gui/window/view_form/model/record.py b/tryton/gui/window/view_form/model/record.py index 23e0678329..70d14461f8 100644 --- a/tryton/gui/window/view_form/model/record.py +++ b/tryton/gui/window/view_form/model/record.py @@ -1,15 +1,13 @@ #This file is part of Tryton. The COPYRIGHT file at the top level of #this repository contains the full copyright notices and license terms. -import re -import time -from tryton.rpc import RPCProxy import tryton.rpc as rpc from tryton.signal_event import SignalEvent +import tryton.common as common from tryton.pyson import PYSONDecoder import field import datetime -import tryton.common as common import logging +import time class EvalEnvironment(dict): @@ -43,96 +41,91 @@ class EvalEnvironment(dict): __repr__ = __str__ def __contains__(self, item): - return item in self.parent.mgroup.mfields + return item in self.parent.group.fields -class ModelRecord(SignalEvent): +class Record(SignalEvent): id = -1 - def __init__(self, resource, obj_id, window, group=None, parent=None, + def __init__(self, model_name, obj_id, window, group=None, parent=None, parent_name='', new=False): - global ID_COUNT - super(ModelRecord, self).__init__() + super(Record, self).__init__() self.window = window - self.resource = resource - self.rpc = RPCProxy(self.resource) - self.id = obj_id or ModelRecord.id + self.model_name = model_name + self.id = obj_id or Record.id if self.id < 0: - ModelRecord.id -= 1 + Record.id -= 1 self._loaded = set() self.parent = parent self.parent_name = parent_name - self.mgroup = group + self.group = group + if group is not None: + assert model_name == group.model_name self.value = {} self.state_attrs = {} self.__modified = False self.modified_fields = {} self._timestamp = None self.attachment_count = -1 - self.next = {} - for key, val in self.mgroup.mfields.items(): + self.next = {} # Used in Group list + for key, val in self.group.fields.items(): self.value[key] = val.create(self) if (new and val.attrs['type']=='one2many') \ and (val.attrs.get('mode','tree,form').startswith('form')): - mod = self.value[key].model_new() - self.value[key].model_add(mod) + record = self.value[key].new() + self.value[key].add(record) def __getitem__(self, name): if name not in self._loaded and self.id > 0: ids = [self.id] - if self in self.mgroup.models: - idx = self.mgroup.models.index(self) - length = len(self.mgroup.models) + if self in self.group: + idx = self.group.index(self) + length = len(self.group) n = 1 while len(ids) < 80 and (idx - n >= 0 or \ idx + n < length) and n < 100: if idx - n >= 0: - model = self.mgroup.models[idx - n] - if name not in model._loaded and model.id > 0: - ids.append(model.id) + record = self.group[idx - n] + if name not in record._loaded and record.id > 0: + ids.append(record.id) if idx + n < length: - model = self.mgroup.models[idx + n] - if name not in model._loaded and model.id > 0: - ids.append(model.id) + record = self.group[idx + n] + if name not in record._loaded and record.id > 0: + ids.append(record.id) n += 1 ctx = rpc.CONTEXT.copy() ctx.update(self.context_get()) - args = (ids, self.mgroup.mfields.keys() + \ - [x + '.rec_name' for x in self.mgroup.mfields.keys() - if self.mgroup.fields[x]['type'] \ + args = ('model', self.model_name, 'read', + ids, self.group.fields.keys() + \ + [x + '.rec_name' for x in self.group.fields.keys() + if self.group.fields[x].attrs['type'] \ in ('many2one', 'reference')] + \ ['_timestamp'], ctx) try: - values = self.rpc.read(*args) + values = rpc.execute(*args) except Exception, exception: - common.process_exception(exception, self.window) - try: - values = self.rpc.read(args) - except: + values = common.process_exception(exception, self.window, *args) + if not values: log = logging.getLogger('record') log.error('%s' % exception.args[-1]) values = [{'id': x} for x in ids] - model_set = None id2value = dict((value['id'], value) for value in values) if ids != [self.id]: - for model in self.mgroup.models: - value = id2value.get(model.id) - if value: - model.set(value, signal=False) - model_set = model + for id in ids: + record = self.group.get(id) + value = id2value.get(id) + if record and value: + record.set(value, signal=False) else: value = id2value.get(self.id) if value: self.set(value, signal=False) - model_set = self - if model_set: - model_set.signal('record-changed') - return self.mgroup.mfields.get(name, False) + return self.group.fields.get(name, False) def __repr__(self): - return '' % (self.id, self.resource) + return '' % (self.id, self.model_name) def get_modified(self): return self.__modified @@ -148,7 +141,7 @@ class ModelRecord(SignalEvent): return self.modified def fields_get(self): - return self.mgroup.mfields + return self.group.fields def _check_load(self): if not self.loaded: @@ -157,7 +150,7 @@ class ModelRecord(SignalEvent): return False def get_loaded(self): - return set(self.mgroup.mfields.keys()) == self._loaded + return set(self.group.fields.keys()) == self._loaded loaded = property(get_loaded) @@ -166,12 +159,12 @@ class ModelRecord(SignalEvent): if check_load: self._check_load() value = [] - for name, mfield in self.mgroup.mfields.items(): + for name, field in self.group.fields.iteritems(): if (get_readonly or \ - not mfield.get_state_attrs(self).get('readonly', False)) \ + not field.get_state_attrs(self).get('readonly', False)) \ and (not get_modifiedonly \ - or mfield.name in self.modified_fields): - value.append((name, mfield.get(self, check_load=check_load, + or field.name in self.modified_fields): + value.append((name, field.get(self, check_load=check_load, readonly=get_readonly, modified=get_modifiedonly))) value = dict(value) if includeid: @@ -182,8 +175,8 @@ class ModelRecord(SignalEvent): if check_load: self._check_load() value = {} - for name, mfield in self.mgroup.mfields.items(): - value[name] = mfield.get_eval(self, check_load=check_load) + for name, field in self.group.fields.iteritems(): + value[name] = field.get_eval(self, check_load=check_load) value['id'] = self.id return value @@ -192,23 +185,25 @@ class ModelRecord(SignalEvent): self.reload() def get_timestamp(self): - result = {self.resource + ',' + str(self.id): self._timestamp} - for name, mfield in self.mgroup.mfields.items(): - result.update(mfield.get_timestamp(self)) + result = {self.model_name + ',' + str(self.id): self._timestamp} + for name, field in self.group.fields.iteritems(): + result.update(field.get_timestamp(self)) return result def save(self, force_reload=True): self._check_load() if self.id < 0: value = self.get(get_readonly=True) - args = ('model', self.resource, 'create', value, self.context_get()) + args = ('model', self.model_name, 'create', value, self.context_get()) try: - self.id = rpc.execute(*args) + res = rpc.execute(*args) except Exception, exception: res = common.process_exception(exception, self.window, *args) if not res: return False - self.id = res + old_id = self.id + self.id = res + self.group.id_changed(old_id) else: if not self.is_modified(): return self.id @@ -216,7 +211,7 @@ class ModelRecord(SignalEvent): context = self.context_get() context = context.copy() context['_timestamp'] = self.get_timestamp() - args = ('model', self.resource, 'write', [self.id], value, context) + args = ('model', self.model_name, 'write', [self.id], value, context) try: if not rpc.execute(*args): return False @@ -226,35 +221,41 @@ class ModelRecord(SignalEvent): self._loaded.clear() if force_reload: self.reload() - if self.mgroup: - self.mgroup.writen(self.id) + if self.group: + self.group.writen(self.id) return self.id def default_get(self, domain=None, context=None): - if len(self.mgroup.fields): + if len(self.group.fields): + args = ('model', self.model_name, 'default_get', + self.group.fields.keys(), context) try: - val = self.rpc.default_get(self.mgroup.fields.keys(), context) + vals = rpc.execute(*args) except Exception, exception: - common.process_exception(exception, self.window) - val = self.rpc.default_get(self.mgroup.fields.keys(), context) - self.set_default(val) + vals = common.process_exception(exception, self.window, *args) + if not vals: + return + self.set_default(vals) def rec_name(self): ctx = rpc.CONTEXT.copy() ctx.update(self.context_get()) + args = ('model', self.model_name, 'read', self.id, ['rec_name'], ctx) try: - name = self.rpc.read(self.id, ['rec_name'], ctx)['rec_name'] + res = rpc.execute(*args) except Exception, exception: - common.process_exception(exception, self.window) - name = self.rpc.read(self.id, ['rec_name'], ctx)['rec_name'] - return name + res = common.process_exception(exception, self.window, *args) + if not res: + return '' + return res['rec_name'] def validate_set(self): - change = self._check_load() - for mfield in self.mgroup.mfields.itervalues(): + self._check_load() + change = False + for field in self.group.fields.itervalues(): change = change or \ - not mfield.get_state_attrs(self).get('valid', True) - mfield.get_state_attrs(self)['valid'] = True + not field.get_state_attrs(self).get('valid', True) + field.get_state_attrs(self)['valid'] = True if change: self.signal('record-changed') return change @@ -262,43 +263,44 @@ class ModelRecord(SignalEvent): def validate(self): self._check_load() res = True - for mfield in self.mgroup.mfields.itervalues(): - if not mfield.validate(self): + for field in self.group.fields.itervalues(): + if not field.validate(self): res = False return res def _get_invalid_fields(self): res = [] - for fname, mfield in self.mgroup.mfields.items(): - if not mfield.get_state_attrs(self).get('valid', True): - res.append((fname, mfield.attrs['string'])) + for fname, field in self.group.fields.iteritems(): + if not field.get_state_attrs(self).get('valid', True): + res.append((fname, field.attrs['string'])) return dict(res) + invalid_fields = property(_get_invalid_fields) def context_get(self): - return self.mgroup.context + return self.group.context def get_default(self): self._check_load() - value = dict([(name, mfield.get_default(self)) - for name, mfield in self.mgroup.mfields.items()]) + value = dict([(name, field.get_default(self)) + for name, field in self.group.fields.iteritems()]) return value def set_default(self, val, signal=True, modified=False): for fieldname, value in val.items(): - if fieldname not in self.mgroup.mfields: + if fieldname not in self.group.fields: continue - if isinstance(self.mgroup.mfields[fieldname], field.M2OField): + if isinstance(self.group.fields[fieldname], field.M2OField): if fieldname + '.rec_name' in val: value = (value, val[fieldname + '.rec_name']) - elif isinstance(self.mgroup.mfields[fieldname], field.ReferenceField): + elif isinstance(self.group.fields[fieldname], field.ReferenceField): if value: ref_model, ref_id = value.split(',', 1) if fieldname + '.rec_name' in val: value = ref_model, (ref_id, val[fieldname + '.rec_name']) else: value = ref_model, (ref_id, ref_id) - self.mgroup.mfields[fieldname].set_default(self, value, + self.group.fields[fieldname].set_default(self, value, modified=modified) self._loaded.add(fieldname) if signal: @@ -310,25 +312,25 @@ class ModelRecord(SignalEvent): if fieldname == '_timestamp': self._timestamp = value continue - if fieldname not in self.mgroup.mfields: + if fieldname not in self.group.fields: continue - if isinstance(self.mgroup.mfields[fieldname], field.O2MField): + if isinstance(self.group.fields[fieldname], field.O2MField): later[fieldname] = value continue - if isinstance(self.mgroup.mfields[fieldname], field.M2OField): + if isinstance(self.group.fields[fieldname], field.M2OField): if fieldname + '.rec_name' in val: value = (value, val[fieldname + '.rec_name']) - elif isinstance(self.mgroup.mfields[fieldname], field.ReferenceField): + elif isinstance(self.group.fields[fieldname], field.ReferenceField): if value: ref_model, ref_id = value.split(',', 1) if fieldname + '.rec_name' in val: value = ref_model, (ref_id, val[fieldname + '.rec_name']) else: value = ref_model, (ref_id, ref_id) - self.mgroup.mfields[fieldname].set(self, value, modified=modified) + self.group.fields[fieldname].set(self, value, modified=modified) self._loaded.add(fieldname) for fieldname, value in later.iteritems(): - self.mgroup.mfields[fieldname].set(self, value, modified=modified) + self.group.fields[fieldname].set(self, value, modified=modified) self._loaded.add(fieldname) self.modified = modified if not self.modified: @@ -348,8 +350,8 @@ class ModelRecord(SignalEvent): if check_load: self._check_load() ctx = rpc.CONTEXT.copy() - for name, mfield in self.mgroup.mfields.items(): - ctx[name] = mfield.get_eval(self, check_load=check_load) + for name, field in self.group.fields.items(): + ctx[name] = field.get_eval(self, check_load=check_load) ctx['context'] = self.context_get() ctx['active_id'] = self.id @@ -363,8 +365,8 @@ class ModelRecord(SignalEvent): def _get_on_change_args(self, args): res = {} values = {} - for name, mfield in self.mgroup.mfields.iteritems(): - values[name] = mfield.get_eval(self, check_load=False) + for name, field in self.group.fields.iteritems(): + values[name] = field.get_eval(self, check_load=False) if self.parent and self.parent_name: values['_parent_' + self.parent_name] = EvalEnvironment(self.parent, False) @@ -385,41 +387,41 @@ class ModelRecord(SignalEvent): ids = [self.id] ctx = rpc.CONTEXT.copy() ctx.update(self.context_get()) + args = ('model', self.model_name, 'on_change_' + fieldname, + ids, args, ctx) try: - res = getattr(self.rpc, 'on_change_' + fieldname)(ids, args, - ctx) + res = rpc.execute(*args) except Exception, exception: - common.process_exception(exception, self.window) - res = getattr(self.rpc, 'on_change_' + fieldname)(ids, args, - ctx) - if res: - later = {} - for fieldname, value in res.items(): - if fieldname not in self.mgroup.mfields: - continue - if isinstance(self.mgroup.mfields[fieldname], field.O2MField): - later[fieldname] = value - continue - if isinstance(self.mgroup.mfields[fieldname], field.M2OField): + res = common.process_exception(exception, self.window, *args) + if not res: + return + later = {} + for fieldname, value in res.items(): + if fieldname not in self.group.fields: + continue + if isinstance(self.group.fields[fieldname], field.O2MField): + later[fieldname] = value + continue + if isinstance(self.group.fields[fieldname], field.M2OField): + if fieldname + '.rec_name' in res: + value = (value, res[fieldname + '.rec_name']) + elif isinstance(self.group.fields[fieldname], + field.ReferenceField): + if value: + ref_mode, ref_id = value.split(',', 1) if fieldname + '.rec_name' in res: - value = (value, res[fieldname + '.rec_name']) - elif isinstance(self.mgroup.mfields[fieldname], - field.ReferenceField): - if value: - ref_mode, ref_id = value.split(',', 1) - if fieldname + '.rec_name' in res: - value = ref_model, (ref_id, - res[fieldname + '.rec_name']) - else: - value = ref_model, (ref_id, ref_id) - self.mgroup.mfields[fieldname].set_on_change(self, value) - for fieldname, value in later.items(): - self.mgroup.mfields[fieldname].set_on_change(self, value) - self.signal('record-changed') + value = ref_model, (ref_id, + res[fieldname + '.rec_name']) + else: + value = ref_model, (ref_id, ref_id) + self.group.fields[fieldname].set_on_change(self, value) + for fieldname, value in later.items(): + self.group.fields[fieldname].set_on_change(self, value) + self.signal('record-changed') def on_change_with(self, field_name): - for fieldname in self.mgroup.mfields: - on_change_with = self.mgroup.mfields[fieldname].attrs.get( + for fieldname in self.group.fields: + on_change_with = self.group.fields[fieldname].attrs.get( 'on_change_with') if not on_change_with: continue @@ -431,37 +433,39 @@ class ModelRecord(SignalEvent): ids = [self.id] ctx = rpc.CONTEXT.copy() ctx.update(self.context_get()) + args = ('model', self.model_name, 'on_change_with_' + fieldname, + ids, args, ctx) try: - res = getattr(self.rpc, 'on_change_with_' + fieldname)(ids, - args, ctx) + res = rpc.execute(*args) except Exception, exception: - common.process_exception(exception, self.window) - res = getattr(self.rpc, 'on_change_with_' + fieldname)(ids, - args, ctx) - self.mgroup.mfields[fieldname].set_on_change(self, res) + res = common.process_exception(exception, self.window, *args) + if not res: + return + self.group.fields[fieldname].set_on_change(self, res) def cond_default(self, field_name, value): - ir_default = RPCProxy('ir.default') ctx = rpc.CONTEXT.copy() ctx.update(self.context_get()) + args = ('model', 'ir.default', 'get_default', self.model_name, + field_name + '=' + str(value), ctx) try: - self.set_default(ir_default.get_default(self.resource, - field_name + '=' + str(value), ctx)) + res = rpc.execute(*args) except Exception, exception: - common.process_exception(exception, self.window) - self.set_default(ir_default.get_default(self.resource, - field_name + '=' + str(value), ctx)) + 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 if self.attachment_count < 0 or reload: - ir_attachment = RPCProxy('ir.attachment') + args = ('model', 'ir.attachment', 'search_count', [ + ('res_model', '=', self.model_name), + ('res_id', '=', self.id), + ]) try: - self.attachment_count = ir_attachment.search_count([ - ('res_model', '=', self.resource), - ('res_id', '=', self.id), - ]) + self.attachment_count = rpc.execute(*args) except: return 0 return self.attachment_count diff --git a/tryton/gui/window/view_form/screen/screen.py b/tryton/gui/window/view_form/screen/screen.py index cd2fc94618..335bc66aa3 100644 --- a/tryton/gui/window/view_form/screen/screen.py +++ b/tryton/gui/window/view_form/screen/screen.py @@ -1,27 +1,25 @@ #This file is part of Tryton. The COPYRIGHT file at the top level of #this repository contains the full copyright notices and license terms. "Screen" -import xml.dom.minidom -from tryton.rpc import RPCProxy import tryton.rpc as rpc -from tryton.gui.window.view_form.model.group import ModelRecordGroup +from tryton.gui.window.view_form.model.group import Group from tryton.gui.window.view_form.view.screen_container import ScreenContainer from tryton.gui.window.view_form.widget_search import Form from tryton.signal_event import SignalEvent from tryton.common import node_attributes -import gobject +from tryton.config import CONFIG import tryton.common as common +import gobject import copy +import xml.dom.minidom class Screen(SignalEvent): "Screen" def __init__(self, model_name, window, view_ids=None, view_type=None, - parent=None, parent_name='', context=None, views_preload=None, - tree_saves=True, domain=None, create_new=False, row_activate=None, - hastoolbar=False, default_get=None, show_search=False, limit=None, - readonly=False, form=None, exclude_field=None, sort=None, + context=None, views_preload=None, domain=None, row_activate=None, + limit=None, readonly=False, exclude_field=None, sort=None, search_value=None): if view_ids is None: view_ids = [] @@ -33,48 +31,35 @@ class Screen(SignalEvent): views_preload = {} if domain is None: domain = [] - if default_get is None: - default_get = {} super(Screen, self).__init__() - self.__current_model = None - self.show_search = show_search self.search_count = 0 - self.hastoolbar = hastoolbar - self.default_get = default_get if not row_activate: # TODO change for a function that switch to form view self.row_activate = self.switch_view else: self.row_activate = row_activate - self.create_new = create_new - self.name = model_name self.domain = domain self.views_preload = views_preload - self.resource = model_name - self.rpc = RPCProxy(model_name) + self.model_name = model_name self.context = context self.views = [] - self.fields = {} self.view_ids = view_ids - self.models = None - self.parent = parent - self.parent_name = parent_name + self.parent = None + self.parent_name = None self.window = window - models = ModelRecordGroup(model_name, self.fields, self.window, - parent=self.parent, parent_name=parent_name, context=self.context, + self.__group = None + self.group = Group(model_name, {}, self.window, context=self.context, readonly=readonly) - self.models_set(models) - self.current_model = None + self.__current_record = None + self.current_record = None self.screen_container = ScreenContainer() self.filter_widget = None self.widget = self.screen_container.widget_get() self.__current_view = 0 - self.tree_saves = tree_saves self.limit = limit self.search_value = search_value - self.form = form self.fields_view_tree = None self.exclude_field = exclude_field self.sort = sort @@ -89,8 +74,11 @@ class Screen(SignalEvent): self.screen_container.set(view.widget) self.display() + def __repr__(self): + return '' % (self.model_name, id(self)) + def search_active(self, active=True): - if active and self.show_search: + if active and not self.parent: if not self.filter_widget: if not self.fields_view_tree: ctx = {} @@ -98,13 +86,14 @@ class Screen(SignalEvent): ctx.update(self.context) try: self.fields_view_tree = rpc.execute('model', - self.name, 'fields_view_get', False, + self.model_name, 'fields_view_get', False, 'tree', ctx) except: return self.filter_widget = Form(self.fields_view_tree['arch'], - self.fields_view_tree['fields'], self.name, self.window, - self.domain, (self, self.search_filter), self.context) + self.fields_view_tree['fields'], self.model_name, + self.window, self.domain, (self, self.search_filter), + self.context) self.screen_container.add_filter(self.filter_widget.widget, self.search_filter, self.search_clear) self.filter_widget.set_limit(self.limit) @@ -126,7 +115,7 @@ class Screen(SignalEvent): offset = self.filter_widget.get_offset() values = self.filter_widget.value else: - values = [('id', 'in', [x.id for x in self.models])] + values = [('id', 'in', [x.id for x in self.group])] ctx = {} ctx.update(rpc.CONTEXT) ctx.update(self.context) @@ -137,21 +126,21 @@ class Screen(SignalEvent): values = self.domain try: try: - ids = rpc.execute('model', self.name, 'search', values, + ids = rpc.execute('model', self.model_name, 'search', values, offset, limit, self.sort, ctx) except Exception, exception: common.process_exception(exception, self.window) - ids = rpc.execute('model', self.name, 'search', values, + ids = rpc.execute('model', self.model_name, 'search', values, offset, limit, self.sort, ctx) if not only_ids: if len(ids) == limit: try: - self.search_count = rpc.execute('model', self.name, - 'search_count', values, ctx) + self.search_count = rpc.execute('model', + self.model_name, 'search_count', values, ctx) except Exception, exception: common.process_exception(exception, self.window) - self.search_count = rpc.execute('model', self.name, - 'search_count', values, ctx) + self.search_count = rpc.execute('model', + self.model_name, 'search_count', values, ctx) else: self.search_count = len(ids) except: @@ -160,76 +149,79 @@ class Screen(SignalEvent): return ids self.clear() self.load(ids) - return True + return bool(ids) - def models_set(self, models): - if self.models: - self.models.signal_unconnect(self) - self.models = models - self.parent = models.parent - self.parent_name = models.parent_name - if len(models.models): - self.current_model = models.models[0] + def __get_group(self): + return self.__group + + def __set_group(self, group): + fields = {} + if self.group is not None: + self.group.signal_unconnect(self) + for name, field in self.group.fields.iteritems(): + fields[name] = field.attrs + self.__group = group + self.parent = group.parent + self.parent_name = group.parent_name + if len(group): + self.current_record = group[0] else: - self.current_model = None - self.models.signal_connect(self, 'record-cleared', self._record_cleared) - self.models.signal_connect(self, 'record-changed', self._record_changed) - self.models.signal_connect(self, 'record-modified', self._record_modified) - self.models.signal_connect(self, 'model-changed', self._model_changed) - models.add_fields(self.fields, models) - self.fields.update(models.fields) + self.current_record = None + self.__group.signal_connect(self, 'group-cleared', self._group_cleared) + self.__group.signal_connect(self, 'group-list-changed', + self._group_list_changed) + self.__group.signal_connect(self, 'record-modified', self._record_modified) + self.__group.signal_connect(self, 'group-changed', self._group_changed) + self.__group.add_fields(fields) - def _record_cleared(self, model_group, signal, *args): + group = property(__get_group, __set_group) + + def _group_cleared(self, group, signal): for view in self.views: - view.reload = True + if hasattr(view, 'reload'): + view.reload = True - def _record_changed(self, model_group, signal, *args): + def _group_list_changed(self, group, signal): for view in self.views: - view.signal_record_changed(signal[0], model_group.models, - signal[1], *args) + if hasattr(view, 'group_list_changed'): + view.group_list_changed(group, signal) - def _record_modified(self, model_group, signal, *args): + def _record_modified(self, group, signal, *args): self.signal('record-modified') - def _model_changed(self, model_group, model): - if self.parent: - self.parent.signal('record-changed', self.parent) + def _group_changed(self, group, record): self.display() - def _get_current_model(self): - return self.__current_model + def __get_current_record(self): + return self.__current_record - # - # Check more or less fields than in the screen ! - # - def _set_current_model(self, value): - self.__current_model = value + def __set_current_record(self, record): + self.__current_record = record try: offset = int(self.filter_widget.get_offset()) except: offset = 0 try: - pos = self.models.models.index(value) + pos = self.group.index(record) except: pos = -1 - self.signal('record-message', (pos + offset, - len(self.models.models or []) + offset, - self.search_count, - value and value.id)) + self.signal('record-message', (pos + offset, len(self.group) + offset, + self.search_count, record and record.id)) attachment_count = 0 - if value and value.attachment_count > 0: - attachment_count = value.attachment_count + if record and record.attachment_count > 0: + attachment_count = record.attachment_count self.signal('attachment-count', attachment_count) # update attachment-count after 1 second - gobject.timeout_add(1000, self.update_attachment, value) + gobject.timeout_add(1000, self.update_attachment, record) return True - current_model = property(_get_current_model, _set_current_model) - def update_attachment(self, model): - if model != self.__current_model: + current_record = property(__get_current_record, __set_current_record) + + def update_attachment(self, record): + if record != self.current_record: return False - if model and self.signal_connected('attachment-count'): - attachment_count = model.get_attachment_count() + if record and self.signal_connected('attachment-count'): + attachment_count = record.get_attachment_count() self.signal('attachment-count', attachment_count) return False @@ -237,30 +229,32 @@ class Screen(SignalEvent): for view in self.views: view.destroy() del view - #del self.current_model - self.models.signal_unconnect(self) - del self.models + self.group.signal_unconnect(self) del self.views - def switch_view(self): + def switch_view(self, view_type=None): self.current_view.set_value() - if self.current_model and self.current_model not in self.models.models: - self.current_model = None - if len(self.view_to_load): - self.load_view_to_load() - self.__current_view = len(self.views) - 1 - else: - self.__current_view = (self.__current_view + 1) % len(self.views) + if self.current_record and self.current_record not in self.group: + self.current_record = None + for i in xrange(len(self.views) + len(self.view_to_load)): + if len(self.view_to_load): + self.load_view_to_load() + self.__current_view = len(self.views) - 1 + else: + self.__current_view = (self.__current_view + 1) % len(self.views) + if not view_type: + break + elif self.current_view.view_type == view_type: + break self.screen_container.set(self.current_view.widget) - if self.current_model: - self.current_model.validate_set() + if self.current_record: + self.current_record.validate_set() else: if self.current_view.view_type == 'form': self.new() self.current_view.set_cursor() self.current_view.cancel() self.display() - # TODO: set True or False accoring to the type def load_view_to_load(self): if len(self.view_to_load): @@ -272,9 +266,6 @@ class Screen(SignalEvent): view_type = self.view_to_load.pop(0) self.add_view_id(view_id, view_type) - def add_view_custom(self, arch, fields, display=False, toolbar=None): - return self.add_view(arch, fields, display, True, toolbar=toolbar) - def add_view_id(self, view_id, view_type, display=False, context=None): if view_type in self.views_preload: return self.add_view(self.views_preload[view_type]['arch'], @@ -285,13 +276,15 @@ class Screen(SignalEvent): ctx = {} ctx.update(rpc.CONTEXT) ctx.update(self.context) + args = ('model', self.model_name, 'fields_view_get', + view_id, view_type, ctx, + self.parent and False or CONFIG['form.toolbar']) try: - view = self.rpc.fields_view_get(view_id, view_type, ctx, - self.hastoolbar) + view = rpc.execute(*args) except Exception, exception: - common.process_exception(exception, self.window) - view = self.rpc.fields_view_get(view_id, view_type, ctx, - self.hastoolbar) + view = common.process_exception(exception, self.window, *args) + if not view: + return if self.exclude_field: if self.exclude_field in view['fields']: view['fields'][self.exclude_field]['states'] = {'invisible': True} @@ -302,8 +295,7 @@ class Screen(SignalEvent): return self.add_view(view['arch'], view['fields'], display, toolbar=view.get('toolbar', False), context=context) - def add_view(self, arch, fields, display=False, custom=False, toolbar=None, - context=None): + def add_view(self, arch, fields, display=False, toolbar=None, context=None): if toolbar is None: toolbar = {} def _parse_fields(node, fields): @@ -347,25 +339,26 @@ class Screen(SignalEvent): self.fields_view_tree = {'arch': arch, 'fields': fields} from tryton.gui.window.view_form.view.widget_parse import WidgetParse - models = self.models.models - if self.current_model and (self.current_model not in models): - models = models + [self.current_model] - if custom: - self.models.add_fields_custom(fields, self.models) - else: - self.models.add_fields(fields, self.models, context=context) - - self.fields = self.models.fields + if self.current_record and (self.current_record not in self.group): + self.group.append(self.current_record) + self.group.add_fields(fields, context=context) parser = WidgetParse(parent=self.parent, window=self.window) - view = parser.parse(self, xml_dom, self.fields, toolbar=toolbar) + view = parser.parse(self, xml_dom, self.group.fields, toolbar=toolbar) self.views.append(view) if display: self.__current_view = len(self.views) - 1 - self.current_view.display() - self.screen_container.set(view.widget) + self.screen_container.set(self.current_view.widget) + if self.current_record: + self.current_record.validate_set() + else: + if self.current_view.view_type == 'form': + self.new() + self.current_view.set_cursor() + self.current_view.cancel() + self.display() return view def editable_get(self): @@ -375,11 +368,12 @@ class Screen(SignalEvent): return False def new(self, default=True, context=None): - if self.models.readonly: + if self.group.readonly: return if context is None: context = {} - if self.current_view and ((self.current_view.view_type == 'tree' \ + if self.current_view and \ + ((self.current_view.view_type == 'tree' \ and not (hasattr(self.current_view.widget_tree, 'editable') \ and self.current_view.widget_tree.editable)) \ or self.current_view.view_type == 'graph'): @@ -393,18 +387,15 @@ class Screen(SignalEvent): ctx.update(rpc.CONTEXT) ctx.update(self.context) ctx.update(context) - model = self.models.model_new(default, self.domain, ctx) - if (not self.current_view) \ - or self.current_view.model_add_new \ - or self.create_new: - self.models.model_add(model, self.new_model_position()) - self.current_model = model - self.current_model.validate_set() + record = self.group.new(default, self.domain, ctx) + self.group.add(record, self.new_model_position()) + self.current_record = record + self.current_record.validate_set() self.display() if self.current_view: self.current_view.set_cursor(new=True) self.request_set() - return self.current_model + return self.current_record def new_model_position(self): position = -1 @@ -416,78 +407,77 @@ class Screen(SignalEvent): def set_on_write(self, func_name): if func_name: - self.models.on_write.add(func_name) + self.group.on_write.add(func_name) def cancel_current(self): - if self.current_model: - self.current_model.cancel() - if self.current_model.id < 0: + if self.current_record: + self.current_record.cancel() + if self.current_record.id < 0: self.remove() if self.current_view: self.current_view.cancel() def save_current(self): - if not self.current_model: + if not self.current_record: return False self.current_view.set_value() obj_id = False - if self.current_model.validate(): - obj_id = self.current_model.save(force_reload=True) + if self.current_record.validate(): + obj_id = self.current_record.save(force_reload=True) else: self.current_view.set_cursor() self.current_view.display() return False if self.current_view.view_type == 'tree': - for model in self.models.models: - if model.is_modified(): - if model.validate(): - obj_id = model.save(force_reload=True) + for record in self.group: + if record.is_modified(): + if record.validate(): + obj_id = record.save(force_reload=True) else: self.current_view.set_cursor() - self.current_model = model + self.current_record = record self.current_view.set_cursor() self.display() return False self.current_view.set_cursor() - self.display() - if self.current_model not in self.models: - self.models.model_add(self.current_model, modified=False) + self.display() + if self.current_record not in self.group: + self.group.add(self.current_record, modified=False) self.request_set() return obj_id - def _get_current_view(self): + def __get_current_view(self): if not len(self.views): return None return self.views[self.__current_view] - current_view = property(_get_current_view) + + current_view = property(__get_current_view) def get(self, get_readonly=True, includeid=False, check_load=True, get_modifiedonly=False): - if not self.current_model: + if not self.current_record: return None self.current_view.set_value() - return self.current_model.get(get_readonly=get_readonly, + return self.current_record.get(get_readonly=get_readonly, includeid=includeid, check_load=check_load, get_modifiedonly=get_modifiedonly) def is_modified(self): - if not self.current_model: - return False self.current_view.set_value() res = False if self.current_view.view_type != 'tree': - res = self.current_model.is_modified() + res = self.current_record and self.current_record.is_modified() else: - for model in self.models.models: - if model.is_modified(): + for record in self.group: + if record.is_modified(): res = True return res def reload(self, writen=False): ids = self.sel_ids_get() - self.models.reload(ids) + self.group.reload(ids) if writen: - self.models.writen(ids) + self.group.writen(ids) if self.parent: self.parent.reload() self.display() @@ -496,33 +486,36 @@ class Screen(SignalEvent): def remove(self, delete=False, remove=False): res = False reload_ids = [] - if self.current_view.view_type == 'form' and self.current_model: - obj_id = self.current_model.id - if delete and obj_id > 0: + if self.current_view.view_type == 'form' and self.current_record: + record_id = self.current_record.id + if delete and record_id > 0: context = {} context.update(rpc.CONTEXT) context.update(self.context) - context['_timestamp'] = self.current_model.get_timestamp() + context['_timestamp'] = self.current_record.get_timestamp() + reload_ids = self.group.on_write_ids(record_id) + if reload_ids and record_id in reload_ids: + reload_ids.remove(record_id) + args = ('model', self.model_name, 'delete', [record_id], + context) try: - reload_ids = self.models.on_write_ids(obj_id) - if reload_ids and obj_id in reload_ids: - reload_ids.remove(obj_id) - if not self.rpc.delete([obj_id], context): - return False + res = rpc.execute(*args) except Exception, exception: - common.process_exception(exception, self.window) + res = common.process_exception(exception, self.window, + *args) + if not res: return False self.current_view.set_cursor() - if self.current_model in self.models.models: - idx = self.models.models.index(self.current_model) - self.models.remove(self.current_model, remove=remove) - if self.models.models: - idx = min(idx, len(self.models.models)-1) - self.current_model = self.models.models[idx] + if self.current_record in self.group: + idx = self.group.index(self.current_record) + self.group.remove(self.current_record, remove=remove) + if self.group: + idx = min(idx, len(self.group) - 1) + self.current_record = self.group[idx] else: - self.current_model = None + self.current_record = None if reload_ids: - self.models.reload(reload_ids) + self.group.reload(reload_ids) self.display() res = True if self.current_view.view_type == 'tree': @@ -532,38 +525,41 @@ class Screen(SignalEvent): context.update(rpc.CONTEXT) context.update(self.context) context['_timestamp'] = {} - for obj_id in ids: - model = self.models.get_by_id(obj_id) - context['_timestamp'].update(model.get_timestamp()) + for record_id in ids: + record = self.group.get(record_id) + context['_timestamp'].update(record.get_timestamp()) + reload_ids = self.group.on_write_ids(ids) + if reload_ids: + for record_id in ids: + if record_id in reload_ids: + reload_ids.remove(record_id) + args = ('model', self.model_name, 'delete', ids, + context) try: - reload_ids = self.models.on_write_ids(ids) - if reload_ids: - for obj_id in ids: - if obj_id in reload_ids: - reload_ids.remove(obj_id) - if not self.rpc.delete(ids, context): - return False + res = rpc.execute(*args) except Exception, exception: - common.process_exception(exception, self.window) + res = common.process_exception(exception, self.window, + *args) + if not res: return False - sel_models = self.current_view.sel_models_get() - if not sel_models: + sel_records = self.current_view.selected_records() + if not sel_records: return True - idx = self.models.models.index(sel_models[0]) - for model in sel_models: + idx = self.group.index(sel_records[0]) + for record in sel_records: # set current model to None to prevent __select_changed # to save the previous_model as it can be already deleted. - self.current_model = None - self.models.remove(model, remove=remove, signal=False) + self.current_record = None + self.group.remove(record, remove=remove, signal=False) # send record-changed only once - model.signal('record-changed', model.parent) - if self.models.models: - idx = min(idx, len(self.models.models)-1) - self.current_model = self.models.models[idx] + record.signal('record-changed') + if self.group: + idx = min(idx, len(self.group) - 1) + self.current_record = self.group[idx] else: - self.current_model = None + self.current_record = None if reload_ids: - self.models.reload(reload_ids) + self.group.reload(reload_ids) self.current_view.set_cursor() self.display() res = True @@ -571,12 +567,12 @@ class Screen(SignalEvent): return res def load(self, ids, set_cursor=True, modified=False): - self.models.load(ids, display=False, modified=modified) + self.group.load(ids, display=False, modified=modified) self.current_view.reset() if ids: self.display(ids[0]) else: - self.current_model = None + self.current_record = None self.display() if set_cursor: self.current_view.set_cursor() @@ -584,7 +580,7 @@ class Screen(SignalEvent): def display(self, res_id=None, set_cursor=False): if res_id: - self.current_model = self.models[res_id] + self.current_record = self.group.get(res_id) if self.views: #XXX To remove when calendar will be implemented if self.current_view.view_type == 'calendar' and \ @@ -592,9 +588,9 @@ class Screen(SignalEvent): self.switch_view() self.current_view.display() self.current_view.widget.set_sensitive( - bool(self.models.models \ + bool(self.group \ or (self.current_view.view_type != 'form') \ - or self.current_model)) + or self.current_record)) self.search_active(self.current_view.view_type \ in ('tree', 'graph', 'calendar')) if set_cursor: @@ -603,32 +599,32 @@ class Screen(SignalEvent): def display_next(self): self.current_view.set_value() self.current_view.set_cursor(reset_view=False) - if self.current_model in self.models.models: - idx = self.models.models.index(self.current_model) + if self.current_record in self.group: + idx = self.group.index(self.current_record) inc = 1 if self.current_view.view_type == 'tree': start, end = self.current_view.widget_tree.get_visible_range() inc += end[0] - start[0] - if inc >= 4 and (end[0] + 1) < len(self.models.models): + if inc >= 4 and (end[0] + 1) < len(self.group): inc -= 3 vadjustment = self.current_view.widget_tree.get_vadjustment() vadjustment.value = vadjustment.value + vadjustment.page_increment idx = idx + inc - if idx >= len(self.models.models): - idx = len(self.models.models) - 1 - self.current_model = self.models.models[idx] + if idx >= len(self.group): + idx = len(self.group) - 1 + self.current_record = self.group[idx] else: - self.current_model = len(self.models.models) \ - and self.models.models[0] + self.current_record = len(self.group) \ + and self.group[0] self.current_view.set_cursor(reset_view=False) - if self.current_model: - self.current_model.validate_set() + if self.current_record: + self.current_record.validate_set() self.display() def display_prev(self): self.current_view.set_value() self.current_view.set_cursor(reset_view=False) - if self.current_model in self.models.models: + if self.current_record in self.group: inc = 1 if self.current_view.view_type == 'tree': range = self.current_view.widget_tree.get_visible_range() @@ -642,38 +638,38 @@ class Screen(SignalEvent): if vadjustment.value: vadjustment.value = vadjustment.value - \ vadjustment.page_increment - idx = self.models.models.index(self.current_model) - inc + idx = self.group.index(self.current_record) - inc if idx < 0: idx = 0 - self.current_model = self.models.models[idx] + self.current_record = self.group[idx] else: - self.current_model = len(self.models.models) \ - and self.models.models[-1] + self.current_record = len(self.group) \ + and self.group[-1] self.current_view.set_cursor(reset_view=False) - if self.current_model: - self.current_model.validate_set() + if self.current_record: + self.current_record.validate_set() self.display() def sel_ids_get(self): return self.current_view.sel_ids_get() def id_get(self): - if not self.current_model: + if not self.current_record: return False - return self.current_model.id + return self.current_record.id def ids_get(self): - return [x.id for x in self.models if x.id] + return [x.id for x in self.group if x.id] def clear(self): - self.models.clear() - self.current_model = None + self.group.clear() + self.current_record = None def on_change(self, fieldname, attr): - self.current_model.on_change(fieldname, attr) + self.current_record.on_change(fieldname, attr) self.display() def request_set(self): - if self.name == 'res.request': + if self.model_name == 'res.request': from tryton.gui.main import Main Main.get_main().request_set() diff --git a/tryton/gui/window/view_form/view/form.py b/tryton/gui/window/view_form/view/form.py index 25b86fd742..a14a975ae1 100644 --- a/tryton/gui/window/view_form/view/form.py +++ b/tryton/gui/window/view_form/view/form.py @@ -12,49 +12,6 @@ from tryton.pyson import PYSONEncoder _ = gettext.gettext -class ViewWidget(object): - - def __init__(self, parent, widget, widget_name): - self.view_form = parent - self.widget = widget - self.widget._view = self - self.widget_name = widget_name - - def display(self, model): - if not model: - self.widget.display(model, False) - return False - modelfield = model.mgroup.mfields.get(self.widget_name, None) - if modelfield: - modelfield.state_set(model) - self.widget.display(model, modelfield) - elif isinstance(self.widget, Action): - self.widget.display(model, False) - - def reset(self, model): - modelfield = None - if model: - modelfield = model.mgroup.mfields.get(self.widget_name, None) - if modelfield and 'valid' in modelfield.get_state_attrs(model): - modelfield.get_state_attrs(model)['valid'] = True - self.display(model) - - def set_value(self, model): - if self.widget_name in model.mgroup.mfields: - self.widget.set_value(model, model.mgroup.mfields[self.widget_name]) - - def _get_model(self): - return self.view_form.screen.current_model - - model = property(_get_model) - - def _get_modelfield(self): - if self.model: - return self.model.mgroup.mfields[self.widget_name] - - modelfield = property(_get_modelfield) - - class ViewForm(ParserView): def __init__(self, window, screen, widget, children=None, @@ -62,14 +19,14 @@ class ViewForm(ParserView): super(ViewForm, self).__init__(window, screen, widget, children, buttons, toolbar, notebooks, cursor_widget) self.view_type = 'form' - self.model_add_new = False for button in self.buttons: button.form = self - self.widgets = dict((name, [ViewWidget(self, widget, name) - for widget in widgets]) - for name, widgets in children.iteritems()) + self.widgets = children + for widgets in self.widgets.itervalues(): + for widget in widgets: + widget.view = self vbox = gtk.VBox() vp = gtk.Viewport() @@ -145,8 +102,8 @@ class ViewForm(ParserView): act = action.copy() if atype in ('print', 'action'): self.screen.save_current() - obj_id = self.screen.current_model \ - and self.screen.current_model.id + obj_id = self.screen.current_record \ + and self.screen.current_record.id if obj_id < 0: if atype in ('print'): message(_('You must save this record ' \ @@ -157,15 +114,15 @@ class ViewForm(ParserView): return False email = {} if 'email' in action: - email = self.screen.current_model.expr_eval(action['email']) + email = self.screen.current_record.expr_eval(action['email']) if not email: email = {} email['subject'] = action['name'].replace('_', '') act['email'] = email self.screen.display() if atype == 'relate': - obj_id = self.screen.current_model \ - and self.screen.current_model.id + obj_id = self.screen.current_record \ + and self.screen.current_record.id if not obj_id: message(_('You must select a record ' \ 'to be able to use the relate button !'), self.window) @@ -173,14 +130,14 @@ class ViewForm(ParserView): encoder = PYSONEncoder() if 'pyson_domain' in act: act['pyson_domain'] = encoder.encode( - self.screen.current_model.expr_eval( + self.screen.current_record.expr_eval( act['pyson_domain'], check_load=False)) if 'pyson_context' in act: act['pyson_context'] = encoder.encode( - self.screen.current_model.expr_eval( + self.screen.current_record.expr_eval( act['pyson_context'], check_load=False)) data = { - 'model': self.screen.name, + 'model': self.screen.model_name, 'id': obj_id, 'ids': [obj_id], } @@ -206,49 +163,60 @@ class ViewForm(ParserView): def cancel(self): for widgets in self.widgets.itervalues(): for widget in widgets: - widget.widget.cancel() + widget.cancel() def set_value(self): - model = self.screen.current_model - if model: - for widgets in self.widgets.itervalues(): - for widget in widgets: - widget.set_value(model) + record = self.screen.current_record + if record: + for name, widgets in self.widgets.iteritems(): + if name in record.group.fields: + field = record.group.fields[name] + for widget in widgets: + widget.set_value(record, field) def sel_ids_get(self): - if self.screen.current_model: - return [self.screen.current_model.id] + if self.screen.current_record: + return [self.screen.current_record.id] return [] - def sel_models_get(self): - if self.screen.current_model: - return [self.screen.current_model] + def selected_records(self): + if self.screen.current_record: + return [self.screen.current_record] return [] def reset(self): - model = self.screen.current_model - for name, widgets in self.widgets.iteritems(): - for widget in widgets: - widget.reset(model) + record = self.screen.current_record + if record: + for name, widgets in self.widgets.iteritems(): + field = record.group.fields.get(name) + if field and 'valid' in field.get_state_attrs(record): + for widget in widgets: + field.get_state_attrs(record)['valid'] = True + widget.display(record, field) def signal_record_changed(self, *args): for widgets in self.widgets.itervalues(): for widget in widgets: - if hasattr(widget.widget, 'screen'): - for view in widget.widget.screen.views: + if hasattr(widget, 'screen'): + for view in widget.screen.views: view.signal_record_changed(*args) def display(self): - model = self.screen.current_model - if model: - # Force to set mfields in model - for field in model.mgroup.fields: - model[field].get(model, check_load=False) - for widgets in self.widgets.itervalues(): + record = self.screen.current_record + if record: + # Force to set fields in record + for field in record.group.fields: + record[field].get(record, check_load=False) + for name, widgets in self.widgets.iteritems(): + field = None + if record: + field = record.group.fields.get(name) + if field: + field.state_set(record) for widget in widgets: - widget.display(model) + widget.display(record, field) for button in self.buttons: - button.state_set(model) + button.state_set(record) return True def set_cursor(self, new=False, reset_view=True): @@ -256,25 +224,25 @@ class ViewForm(ParserView): for notebook in self.notebooks: notebook.set_current_page(0) if self.cursor_widget in self.widgets: - self.widgets[self.cursor_widget][0].widget.grab_focus() - model = self.screen.current_model + self.widgets[self.cursor_widget][0].grab_focus() + record = self.screen.current_record position = reduce(lambda x, y: x + len(y), self.widgets, 0) focus_widget = None - if model: - for widgets in self.widgets.itervalues(): + if record: + for name, widgets in self.widgets.iteritems(): for widget in widgets: - modelfield = model.mgroup.mfields.get(widget.widget_name, None) - if not modelfield: + field = record.group.fields.get(name) + if not field: continue - if not modelfield.get_state_attrs(model).get('valid', True): - if widget.widget.position > position: + if not field.get_state_attrs(record).get('valid', True): + if widget.position > position: continue - position = widget.widget.position + position = widget.position focus_widget = widget if focus_widget: for notebook in self.notebooks: for i in range(notebook.get_n_pages()): child = notebook.get_nth_page(i) - if focus_widget.widget.widget.is_ancestor(child): + if focus_widget.widget.is_ancestor(child): notebook.set_current_page(i) - focus_widget.widget.grab_focus() + focus_widget.grab_focus() diff --git a/tryton/gui/window/view_form/view/form_gtk/binary.py b/tryton/gui/window/view_form/view/form_gtk/binary.py index 570c7a36f1..39fc22b19d 100644 --- a/tryton/gui/window/view_form/view/form_gtk/binary.py +++ b/tryton/gui/window/view_form/view/form_gtk/binary.py @@ -13,8 +13,9 @@ _ = gettext.gettext class Binary(WidgetInterface): "Binary" - def __init__(self, window, parent, model, attrs=None): - super(Binary, self).__init__(window, parent, model, attrs) + def __init__(self, field_name, model_name, window, attrs=None): + super(Binary, self).__init__(field_name, model_name, window, + attrs=attrs) self.tooltips = Tooltips() @@ -52,8 +53,6 @@ class Binary(WidgetInterface): self.tooltips.enable() - self.model_field = None - def _readonly_set(self, value): if value: self.but_new.hide() @@ -71,48 +70,47 @@ class Binary(WidgetInterface): def sig_new(self, widget=None): try: filename = file_selection(_('Open...'), - parent=self._window) - if filename and self.model_field: - self.model_field.set_client(self._view.model, + parent=self.window) + if filename and self.field: + self.field.set_client(self.record, base64.encodestring(file(filename, 'rb').read())) fname = self.attrs.get('fname_widget', False) if fname: self.parent.value = {fname:os.path.basename(filename)} - self.display(self._view.model, self.model_field) + self.display(self.record, self.field) except Exception, exception: warning(_('Error reading the file.\nError message:\n%s') \ - % str(exception), self._window, _('Error')) + % str(exception), self.window, _('Error')) def sig_save_as(self, widget=None): try: filename = file_selection(_('Save As...'), - parent=self._window, action=gtk.FILE_CHOOSER_ACTION_SAVE) - if filename and self.model_field: + parent=self.window, action=gtk.FILE_CHOOSER_ACTION_SAVE) + if filename and self.field: file_p = file(filename,'wb+') file_p.write(base64.decodestring( - self.model_field.get(self._view.model))) + self.field.get(self.record))) file_p.close() except Exception, exception: warning(_('Error writing the file.\nError message:\n%s') \ - % str(exception), self._window, _('Error')) + % str(exception), self.window, _('Error')) def sig_remove(self, widget=None): - if self.model_field: - self.model_field.set_client(self._view.model, False) + if self.field: + self.field.set_client(self.record, False) fname = self.attrs.get('fname_widget', False) if fname: self.parent.value = {fname:False} - self.display(self._view.model, self.model_field) + self.display(self.record, self.field) - def display(self, model, model_field): - super(Binary, self).display(model, model_field) - if not model_field: + def display(self, record, field): + super(Binary, self).display(record, field) + if not field: self.wid_text.set_text('') self.but_save_as.set_sensitive(False) return False - self.model_field = model_field - self.wid_text.set_text(self._size_get(model_field.get(model))) - self.but_save_as.set_sensitive(bool(model_field.get(model))) + self.wid_text.set_text(self._size_get(field.get(record))) + self.but_save_as.set_sensitive(bool(field.get(record))) return True def display_value(self): @@ -121,7 +119,7 @@ class Binary(WidgetInterface): def _size_get(self, value): return value and ('%d ' + _('bytes')) % len(value) or '' - def set_value(self, model, model_field): + def set_value(self, record, field): return def _color_widget(self): diff --git a/tryton/gui/window/view_form/view/form_gtk/calendar.py b/tryton/gui/window/view_form/view/form_gtk/calendar.py index d42ef5ef97..4328de9b01 100644 --- a/tryton/gui/window/view_form/view/form_gtk/calendar.py +++ b/tryton/gui/window/view_form/view/form_gtk/calendar.py @@ -18,8 +18,9 @@ _ = gettext.gettext class Calendar(WidgetInterface): "Calendar" - def __init__(self, window, parent=None, model=None, attrs=None): - super(Calendar, self).__init__(window, parent, model=model, attrs=attrs) + def __init__(self, field_name, model_name, window, attrs=None): + super(Calendar, self).__init__(field_name, model_name, window, + attrs=attrs) self.format = date_format() self.widget = date_widget.ComplexEntry(self.format, spacing=0) @@ -66,7 +67,7 @@ class Calendar(WidgetInterface): def grab_focus(self): return self.entry.grab_focus() - def get_value(self, model): + def get_value(self, record): value = self.entry.get_text() if value == '': return False @@ -76,16 +77,16 @@ class Calendar(WidgetInterface): return False return datetime_strftime(date, DT_FORMAT) - def set_value(self, model, model_field): - model_field.set_client(model, self.get_value(model)) + def set_value(self, record, field): + field.set_client(record, self.get_value(record)) return True - def display(self, model, model_field): - if not model_field: + def display(self, record, field): + if not field: self.entry.clear() return False - super(Calendar, self).display(model, model_field) - value = model_field.get_client(model) + super(Calendar, self).display(record, field) + value = field.get_client(record) if not value: self.entry.clear() else: @@ -109,7 +110,7 @@ class Calendar(WidgetInterface): return self.entry.get_text() def cal_open(self, widget): - win = gtk.Dialog(_('Date Selection'), self._window, + win = gtk.Dialog(_('Date Selection'), self.window, gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT, (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OK, gtk.RESPONSE_OK)) @@ -127,7 +128,7 @@ class Calendar(WidgetInterface): win.show_all() try: - val = self.get_value(self.model) + val = self.get_value(self.record) if val: cal.select_month(int(val[5:7])-1, int(val[0:4])) cal.select_day(int(val[8:10])) @@ -147,15 +148,16 @@ class Calendar(WidgetInterface): format = format.replace('%Y', '0%Y') self.entry.set_text(datetime_strftime(date, format)) self._focus_out() - self._window.present() + self.window.present() win.destroy() class DateTime(WidgetInterface): "DateTime" - def __init__(self, window, parent, model, attrs=None): - super(DateTime, self).__init__(window, parent, model, attrs=attrs) + def __init__(self, field_name, model_name, window, attrs=None): + super(DateTime, self).__init__(field_name, model_name, window, + attrs=attrs) self.format = date_format() + ' ' + HM_FORMAT self.widget = date_widget.ComplexEntry(self.format, spacing=0) @@ -201,7 +203,7 @@ class DateTime(WidgetInterface): def grab_focus(self): return self.entry.grab_focus() - def get_value(self, model, timezone=True): + def get_value(self, record, timezone=True): value = self.entry.get_text() if value == '': return False @@ -221,15 +223,15 @@ class DateTime(WidgetInterface): pass return datetime_strftime(date, DHM_FORMAT) - def set_value(self, model, model_field): - model_field.set_client(model, self.get_value(model)) + def set_value(self, record, field): + field.set_client(record, self.get_value(record)) return True - def display(self, model, model_field): - super(DateTime, self).display(model, model_field) - if not model_field: + def display(self, record, field): + super(DateTime, self).display(record, field) + if not field: return self.show(False) - self.show(model_field.get_client(model)) + self.show(field.get_client(record)) def show(self, dt_val, timezone=True): if not dt_val: @@ -263,7 +265,7 @@ class DateTime(WidgetInterface): return self.entry.get_text() def cal_open(self, widget): - win = gtk.Dialog(_('Date Time Selection'), self._window, + win = gtk.Dialog(_('Date Time Selection'), self.window, gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT, (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OK, gtk.RESPONSE_OK)) @@ -292,7 +294,7 @@ class DateTime(WidgetInterface): win.show_all() try: - val = self.get_value(self.model, timezone=False) + val = self.get_value(self.record, timezone=False) if val: widget_hour.set_value(int(val[11:13])) widget_minute.set_value(int(val[-5:-3])) @@ -314,5 +316,5 @@ class DateTime(WidgetInterface): value = datetime_strftime(date, DHM_FORMAT) self.show(value, timezone=False) self._focus_out() - self._window.present() + self.window.present() win.destroy() 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 4317a86b8f..a99550a0e0 100644 --- a/tryton/gui/window/view_form/view/form_gtk/char.py +++ b/tryton/gui/window/view_form/view/form_gtk/char.py @@ -6,10 +6,8 @@ from interface import WidgetInterface class Char(WidgetInterface): "Char" - def __init__(self, window, parent, model, attrs=None): - if attrs is None: - attrs = {} - super(Char, self).__init__(window, parent, model=model, attrs=attrs) + def __init__(self, field_name, model_name, window, attrs=None): + super(Char, self).__init__(field_name, model_name, window, attrs=attrs) self.widget = gtk.HBox() self.entry = gtk.Entry() @@ -29,15 +27,15 @@ class Char(WidgetInterface): def grab_focus(self): return self.entry.grab_focus() - def set_value(self, model, model_field): - return model_field.set_client(model, self.entry.get_text() or False) + def set_value(self, record, field): + return field.set_client(record, self.entry.get_text() or False) - def display(self, model, model_field): - super(Char, self).display(model, model_field) - if not model_field: + def display(self, record, field): + super(Char, self).display(record, field) + if not field: self.entry.set_text('') return False - self.entry.set_text(model_field.get(model) or '') + self.entry.set_text(field.get(record) or '') def _readonly_set(self, value): super(Char, self)._readonly_set(value) @@ -50,6 +48,6 @@ class Char(WidgetInterface): class Sha(Char): - def __init__(self, window, parent, model, attrs=None): - super(Sha, self).__init__(window, parent, model=model, attrs=attrs) + def __init__(self, field_name, model_name, window, attrs=None): + super(Sha, self).__init__(field_name, model_name, window, attrs=attrs) self.entry.set_visibility(False) diff --git a/tryton/gui/window/view_form/view/form_gtk/checkbox.py b/tryton/gui/window/view_form/view/form_gtk/checkbox.py index 67b849ff1a..acd5d9f6f4 100644 --- a/tryton/gui/window/view_form/view/form_gtk/checkbox.py +++ b/tryton/gui/window/view_form/view/form_gtk/checkbox.py @@ -8,8 +8,9 @@ _ = gettext.gettext class CheckBox(WidgetInterface): - def __init__(self, window, parent, model, attrs=None): - super(CheckBox, self).__init__(window, parent, model, attrs) + def __init__(self, field_name, model_name, window, attrs=None): + super(CheckBox, self).__init__(field_name, model_name, window, + attrs=attrs) self.widget = gtk.CheckButton() self.widget.connect('focus-in-event', lambda x, y: self._focus_in()) self.widget.connect('focus-out-event', lambda x, y: self._focus_out()) @@ -20,15 +21,15 @@ class CheckBox(WidgetInterface): super(CheckBox, self)._readonly_set(value) self.widget.set_sensitive(not value) - def set_value(self, model, model_field): - model_field.set_client(model, int(self.widget.get_active())) + def set_value(self, record, field): + field.set_client(record, self.widget.get_active()) - def display(self, model, model_field): - super(CheckBox, self).display(model, model_field) - if not model_field: + def display(self, record, field): + super(CheckBox, self).display(record, field) + if not field: self.widget.set_active(False) return False - self.widget.set_active(bool(model_field.get(model))) + self.widget.set_active(bool(field.get(record))) def display_value(self): if self.widget.get_active(): diff --git a/tryton/gui/window/view_form/view/form_gtk/float.py b/tryton/gui/window/view_form/view/form_gtk/float.py index 3dcee3b9b5..e1cd862253 100644 --- a/tryton/gui/window/view_form/view/form_gtk/float.py +++ b/tryton/gui/window/view_form/view/form_gtk/float.py @@ -1,6 +1,5 @@ #This file is part of Tryton. The COPYRIGHT file at the top level of #this repository contains the full copyright notices and license terms. -from char import Char from integer import Integer import locale @@ -8,28 +7,29 @@ import locale class Float(Integer): "Float" - def __init__(self, window, parent, model, attrs=None): - super(Float, self).__init__(window, parent, model=model, attrs=attrs) + def __init__(self, field_name, model_name, window, attrs=None): + super(Float, self).__init__(field_name, model_name, window, + attrs=attrs) self.digits = attrs.get('digits', (16, 2)) - def set_value(self, model, model_field): + def set_value(self, record, field): try: value = locale.atof(self.entry.get_text()) except: value = 0.0 - return model_field.set_client(model, value) + return field.set_client(record, value) - def display(self, model, model_field): - super(Char, self).display(model, model_field) - if not model_field: + def display(self, record, field): + super(Float, self).display(record, field) + if not field: self.entry.set_text('') return False if isinstance(self.digits, str): - digits = self._view.model.expr_eval(self.digits) + digits = record.expr_eval(self.digits) else: digits = self.digits self.entry.set_text(locale.format('%.' + str(digits[1]) + 'f', - model_field.get(model) or 0.0, True)) + field.get(record) or 0.0, True)) def sig_insert_text(self, entry, new_text, new_text_length, position): value = entry.get_text() @@ -42,7 +42,7 @@ class Float(Integer): return if isinstance(self.digits, str): - digits = self._view.model.expr_eval(self.digits) + digits = self.record.expr_eval(self.digits) else: digits = self.digits diff --git a/tryton/gui/window/view_form/view/form_gtk/float_time.py b/tryton/gui/window/view_form/view/form_gtk/float_time.py index b508360dc5..738b09eae6 100644 --- a/tryton/gui/window/view_form/view/form_gtk/float_time.py +++ b/tryton/gui/window/view_form/view/form_gtk/float_time.py @@ -8,8 +8,8 @@ import tryton.rpc as rpc class FloatTime(WidgetInterface): - def __init__(self, window, parent, model, attrs=None): - super(FloatTime, self).__init__(window, parent, model=model, + def __init__(self, field_name, model_name, window, attrs=None): + super(FloatTime, self).__init__(field_name, model_name, window, attrs=attrs) self.widget = gtk.HBox() @@ -33,19 +33,19 @@ class FloatTime(WidgetInterface): def grab_focus(self): return self.entry.grab_focus() - def set_value(self, model, model_field): + def set_value(self, record, field): value = self.entry.get_text() if not value: - return model_field.set_client(model, 0.0) - return model_field.set_client(model, + return field.set_client(record, 0.0) + return field.set_client(record, common.text_to_float_time(value, self.conv)) - def display(self, model, model_field): - super(FloatTime, self).display(model, model_field) - if not model_field: + def display(self, record, field): + super(FloatTime, self).display(record, field) + if not field: self.entry.set_text('00:00') return False - val = model_field.get(model) + val = field.get(record) self.entry.set_text(common.float_time_to_text(val, self.conv)) diff --git a/tryton/gui/window/view_form/view/form_gtk/image.py b/tryton/gui/window/view_form/view/form_gtk/image.py index 0abb53af68..4c3915da26 100644 --- a/tryton/gui/window/view_form/view/form_gtk/image.py +++ b/tryton/gui/window/view_form/view/form_gtk/image.py @@ -16,8 +16,9 @@ NOIMAGE = file(os.path.join(PIXMAPS_DIR, 'tryton-noimage.png'), 'rb').read() class Image(WidgetInterface): - def __init__(self, window, parent, model, attrs=None): - super(Image, self).__init__(window, parent, model, attrs=attrs) + def __init__(self, field_name, model_name, window, attrs=None): + super(Image, self).__init__(field_name, model_name, window, + attrs=attrs) self.height = int(attrs.get('img_height', 100)) self.width = int(attrs.get('img_width', 300)) @@ -96,22 +97,22 @@ class Image(WidgetInterface): for pat in ("*.png", "*.jpg", "*.gif", "*.tif", "*.xpm"): filter_image.add_pattern(pat) - filename = file_selection(_('Open...'), parent=self._window, + filename = file_selection(_('Open...'), parent=self.window, preview=True, filters=[filter_image, filter_all]) if filename: - self._view.modelfield.set_client(self._view.model, + self.field.set_client(self.record, encodestring(file(filename, 'rb').read())) self.update_img() def sig_save_as(self, widget): - filename = file_selection(_('Save As...'), parent=self._window, + filename = file_selection(_('Save As...'), parent=self.window, action=gtk.FILE_CHOOSER_ACTION_SAVE) if filename: file(filename, 'wb').write(decodestring( - self._view.modelfield.get_client(self._view.model))) + self.field.get_client(self.record))) def sig_remove(self, widget): - self._view.modelfield.set_client(self._view.model, False) + self.field.set_client(self.record, False) self.update_img() def drag_motion(self, widget, context, x, y, timestamp): @@ -127,26 +128,26 @@ class Image(WidgetInterface): if info == 0: uri = selection.get_text().split('\n')[0] if uri: - self._view.modelfield.set_client(self._view.model, + self.field.set_client(self.record, encodestring(urllib.urlopen(uri).read())) self.update_img() elif info == 1: uri = selection.data.split('\r\n')[0] if uri: - self._view.modelfield.set_client(self._view.model, + self.field.set_client(self.record, encodestring(urllib.urlopen(uri).read())) self.update_img() elif info == 2: data = selection.get_pixbuf() if data: - self._view.modelfield.set_client(self._view.model, + self.field.set_client(self.record, encodestring(data)) self.update_img() def update_img(self): value = None - if self._view: - value = self._view.modelfield.get_client(self._view.model) + if self.field: + value = self.field.get_client(self.record) if not value: data = NOIMAGE else: @@ -190,8 +191,8 @@ class Image(WidgetInterface): gtk.gdk.INTERP_BILINEAR) self.image.set_from_pixbuf(scaled) - def display(self, model, model_field): - if not model_field: + def display(self, record, field): + if not field: return False - super(Image, self).display(model, model_field) + super(Image, self).display(record, field) self.update_img() diff --git a/tryton/gui/window/view_form/view/form_gtk/integer.py b/tryton/gui/window/view_form/view/form_gtk/integer.py index ecf915d891..800cd77104 100644 --- a/tryton/gui/window/view_form/view/form_gtk/integer.py +++ b/tryton/gui/window/view_form/view/form_gtk/integer.py @@ -7,26 +7,28 @@ import locale class Integer(Char): "Integer" - def __init__(self, window, parent, model, attrs=None): - super(Integer, self).__init__(window, parent, model=model, attrs=attrs) + def __init__(self, field_name, model_name, window, attrs=None): + super(Integer, self).__init__(field_name, model_name, window, + attrs=attrs) self.entry.set_max_length(0) self.entry.set_alignment(1.0) self.entry.connect('insert_text', self.sig_insert_text) - def set_value(self, model, model_field): + def set_value(self, record, field): try: value = locale.atoi(self.entry.get_text()) except: value = 0 - return model_field.set_client(model, value) + return field.set_client(record, value) - def display(self, model, model_field): - super(Char, self).display(model, model_field) - if not model_field: + def display(self, record, field): + # skip Char call because set_text doesn't work with int + super(Char, self).display(record, field) + if not field: self.entry.set_text('') return False self.entry.set_text(locale.format('%d', - model_field.get(model) or 0, True)) + field.get(record) or 0, True)) def display_value(self): return self.entry.get_text() diff --git a/tryton/gui/window/view_form/view/form_gtk/interface.py b/tryton/gui/window/view_form/view/form_gtk/interface.py index 9e3825738c..61a2c9099a 100644 --- a/tryton/gui/window/view_form/view/form_gtk/interface.py +++ b/tryton/gui/window/view_form/view/form_gtk/interface.py @@ -1,7 +1,6 @@ #This file is part of Tryton. The COPYRIGHT file at the top level of #this repository contains the full copyright notices and license terms. import gtk -from tryton.rpc import RPCProxy import tryton.rpc as rpc from tryton.common import COLORS, process_exception, message from tryton.config import TRYTON_ICON @@ -16,7 +15,7 @@ _ATTRS_BOOLEAN = { 'readonly': False } -def field_pref_set(field, name, model, value, client_value, dependance, +def field_pref_set(field, name, model_name, value, client_value, dependance, window, reset=False): dialog = WidgetFieldPreference(window, reset=reset) if dependance is None: @@ -60,7 +59,7 @@ def field_pref_set(field, name, model, value, client_value, dependance, method = 'reset_default' else: method = 'set_default' - args = ('model', 'ir.default', method, model, field, clause, + args = ('model', 'ir.default', method, model_name, field, clause, value, user, rpc.CONTEXT) try: rpc.execute(*args) @@ -72,14 +71,12 @@ def field_pref_set(field, name, model, value, client_value, dependance, class WidgetInterface(object): - def __init__(self, window, parent=None, model=None, attrs=None): - if attrs is None: - attrs = {} - self.parent = parent - self.model = model - self._window = window - self._view = None - self.attrs = attrs + def __init__(self, field_name, model_name, window, attrs=None): + self.field_name = field_name + self.model_name = model_name + self.window = window + self.view = None # Filled by ViewForm + self.attrs = attrs or {} for key, val in _ATTRS_BOOLEAN.iteritems(): self.attrs[key] = attrs.get(key, False) not in ('False', '0', False) self.default_readonly = self.attrs.get('readonly', False) @@ -96,41 +93,51 @@ class WidgetInterface(object): self.colors = {} self.visible = True + def __get_record(self): + return self.view.screen.current_record + + record = property(__get_record) + + def __get_field(self): + if self.record: + return self.record.group.fields[self.field_name] + + field = property(__get_field) + def destroy(self): pass def _menu_sig_default_get(self): - if self._view.modelfield.get_state_attrs(self._view.model)\ - .get('readonly', False): + if self.field.get_state_attrs(self.record).get('readonly', False): return False - model = self._view.modelfield.parent.resource - args = ('model', model, 'default_get', [self.attrs['name']]) + model_name = self.field.parent.model_name + args = ('model', model_name, 'default_get', [self.attrs['name']]) try: res = rpc.execute(*args) except Exception, exception: - process_exception(exception, self._window, *args) - self._view.modelfield.set_default(self._view.model, + process_exception(exception, self.window, *args) + self.field.set_default(self.record, res.get(self.attrs['name'], False), modified=True) - self.display(self._view.model, self._view.modelfield) + self.display(self.record, self.field) def _menu_sig_default_set(self, reset=False): deps = [] - for wname, wviews in self._view.view_form.widgets.iteritems(): + for wname, wviews in self.view.widgets.iteritems(): for wview in wviews: if wview.modelfield.attrs.get('change_default', False): - wvalue = wview.modelfield.get(self._view.model) + wvalue = wview.modelfield.get(self.record) name = wview.modelfield.attrs.get('string', wname) - value = wview.modelfield.get_client(self._view.model) + value = wview.modelfield.get_client(self.record) deps.append((wname, name, wvalue, value)) - if not self._view.modelfield.validate(self._view.model): - message(_('Invalid field!'), parent=self._window) + if not self.field.validate(self.record): + message(_('Invalid field!'), parent=self.window) return - value = self._view.modelfield.get_default(self._view.model) + value = self.field.get_default(self.record) client_value = self.display_value() - model = self._view.modelfield.parent.resource - field_pref_set(self._view.widget_name, - self.attrs.get('string', self._view.widget_name), model, - value, client_value, deps, self._window, reset=reset) + model_name = self.field.parent.model_name + field_pref_set(self.field_name, + self.attrs.get('string', self.field_name), model_name, + value, client_value, deps, self.window, reset=reset) def sig_activate(self, widget=None): # emulate a focus_out so that the onchange is called if needed @@ -205,7 +212,7 @@ class WidgetInterface(object): widget.show() def display_value(self): - return self._view.modelfield.get_client(self._view.model) + return self.field.get_client(self.record) def _menu_open(self, obj, event): if event.button == 3: @@ -243,30 +250,30 @@ class WidgetInterface(object): pass def _focus_out(self): - if not self._view.modelfield: + if not self.field: return False if not self.visible: return False - self.set_value(self._view.model, self._view.modelfield) + self.set_value(self.record, self.field) - def display(self, model, modelfield): - if not modelfield: + def display(self, record, field): + if not field: self._readonly_set(self.attrs.get('readonly', False)) return - self._readonly_set(modelfield.get_state_attrs(model).\ + self._readonly_set(field.get_state_attrs(record).\ get('readonly', False)) - if modelfield.get_state_attrs(model).get('readonly', False): + if field.get_state_attrs(record).get('readonly', False): self.color_set('readonly') - elif not modelfield.get_state_attrs(model).get('valid', True): + elif not field.get_state_attrs(record).get('valid', True): self.color_set('invalid') - elif modelfield.get_state_attrs(model).get('required', False): + elif field.get_state_attrs(record).get('required', False): self.color_set('required') else: self.color_set('normal') - self.invisible_set(modelfield.get_state_attrs(model).\ + self.invisible_set(field.get_state_attrs(record).\ get('invisible', False)) - def set_value(self, model, model_field): + def set_value(self, record, field): pass def cancel(self): diff --git a/tryton/gui/window/view_form/view/form_gtk/many2many.py b/tryton/gui/window/view_form/view/form_gtk/many2many.py index 184534843e..7387abd0b5 100644 --- a/tryton/gui/window/view_form/view/form_gtk/many2many.py +++ b/tryton/gui/window/view_form/view/form_gtk/many2many.py @@ -3,9 +3,9 @@ import gtk from tryton.gui.window.view_form.screen import Screen from interface import WidgetInterface -from one2many import Dialog import tryton.rpc as rpc from tryton.gui.window.win_search import WinSearch +from tryton.gui.window.win_form import WinForm from tryton.gui.window.view_form.widget_search.form import _LIMIT import tryton.common as common import gettext @@ -15,8 +15,9 @@ _ = gettext.gettext class Many2Many(WidgetInterface): - def __init__(self, window, parent, model, attrs=None): - super(Many2Many, self).__init__(window, parent, model, attrs) + def __init__(self, field_name, model_name, window, attrs=None): + super(Many2Many, self).__init__(field_name, model_name, window, + attrs=attrs) self.widget = gtk.VBox(homogeneous=False, spacing=5) @@ -59,7 +60,7 @@ class Many2Many(WidgetInterface): hbox.set_focus_chain([self.wid_text]) - self.screen = Screen(attrs['relation'], self._window, + self.screen = Screen(attrs['relation'], self.window, view_type=['tree'], views_preload=attrs.get('views', {}), row_activate=self._on_activate) @@ -89,8 +90,8 @@ class Many2Many(WidgetInterface): del self.widget def _sig_add(self, *args): - domain = self._view.modelfield.domain_get(self._view.model) - context = self._view.modelfield.context_get(self._view.model) + domain = self.field.domain_get(self.record) + context = self.field.context_get(self.record) value = self.wid_text.get_text() try: @@ -101,11 +102,11 @@ class Many2Many(WidgetInterface): ids = rpc.execute('model', self.attrs['relation'], 'search', dom , 0, _LIMIT, None, context) except Exception, exception: - common.process_exception(exception, self._window) + common.process_exception(exception, self.window) return False if len(ids) != 1 or not value: win = WinSearch(self.attrs['relation'], sel_multi=True, ids=ids, - context=context, domain=domain, parent=self._window, + context=context, domain=domain, parent=self.window, views_preload=self.attrs.get('views', {})) ids = win.run() @@ -129,22 +130,11 @@ class Many2Many(WidgetInterface): self._sig_edit() def _sig_edit(self): - if self.screen.current_model: - readonly = False - domain = [] - if self._view.modelfield and self._view.model: - modelfield = self._view.modelfield - model = self._view.model - readonly = modelfield.get_state_attrs(model - ).get('readonly', False) - domain = modelfield.domain_get(self._view.model) - dia = Dialog(self.attrs['relation'], parent=self._view.model, - model=self.screen.current_model, attrs=self.attrs, - window=self._window, readonly=readonly, domain=domain) - res, record = dia.run() - if res: - record.save() - dia.destroy() + if self.screen.current_record: + win = WinForm(self.screen, self.window) + if win.run(): + self.screen.current_record.save() + win.destroy() def _readonly_set(self, value): super(Many2Many, self)._readonly_set(value) @@ -153,20 +143,20 @@ class Many2Many(WidgetInterface): self.wid_but_remove.set_sensitive(not value) self.wid_but_add.set_sensitive(not value) - def display(self, model, model_field): - if not model_field: - self.screen.current_model = None + def display(self, record, field): + if field is None: + self.screen.current_record = None self.screen.display() return False - super(Many2Many, self).display(model, model_field) - new_models = model_field.get_client(model) - if self.screen.models != new_models: - self.screen.models_set(new_models) + super(Many2Many, self).display(record, field) + new_group = field.get_client(record) + if id(self.screen.group) != id(new_group): + self.screen.group = new_group self.screen.display() return True def display_value(self): - ids = self._view.modelfield.get_default(self._view.model) + ids = self.field.get_default(self.record) try: result = rpc.execute('model', self.attrs['relation'], 'read', ids, ['rec_name'], rpc.CONTEXT) @@ -174,6 +164,6 @@ class Many2Many(WidgetInterface): return str(ids) return ', '.join(x['rec_name'] for x in result) - def set_value(self, model, model_field): + def set_value(self, record, field): self.screen.current_view.set_value() return True 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 059ede60f1..129b472cb2 100644 --- a/tryton/gui/window/view_form/view/form_gtk/many2one.py +++ b/tryton/gui/window/view_form/view/form_gtk/many2one.py @@ -8,7 +8,7 @@ from tryton.common import TRYTON_ICON, COLOR_SCHEMES import tryton.common as common from tryton.gui.window.view_form.screen import Screen from tryton.gui.window.win_search import WinSearch -from tryton.rpc import RPCProxy +from tryton.gui.window.win_form import WinForm import tryton.rpc as rpc from tryton.action import Action from tryton.gui.window.view_form.widget_search.form import _LIMIT @@ -18,151 +18,11 @@ import pango _ = gettext.gettext -class Dialog(object): - - def __init__(self, model, obj_id=None, attrs=None, domain=None, - context=None, window=None): - if attrs is None: - attrs = {} - - self.dia = gtk.Dialog(_('Link'), window, - gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT) - self.window = window - if ('string' in attrs) and attrs['string']: - self.dia.set_title(attrs['string']) - self.dia.set_property('default-width', 760) - self.dia.set_property('default-height', 500) - self.dia.set_position(gtk.WIN_POS_CENTER_ON_PARENT) - self.dia.set_icon(TRYTON_ICON) - self.dia.set_has_separator(False) - - self.accel_group = gtk.AccelGroup() - self.dia.add_accel_group(self.accel_group) - - icon_cancel = gtk.STOCK_CLOSE - if not obj_id: - icon_cancel = gtk.STOCK_CANCEL - self.but_cancel = self.dia.add_button(icon_cancel, - gtk.RESPONSE_CANCEL) - - self.but_ok = self.dia.add_button(gtk.STOCK_OK, gtk.RESPONSE_OK) - self.but_ok.add_accelerator('clicked', self.accel_group, - gtk.keysyms.Return, gtk.gdk.CONTROL_MASK, gtk.ACCEL_VISIBLE) - - self.dia.set_default_response(gtk.RESPONSE_OK) - self.dia.show() - - self.screen = Screen(model, self.dia, domain=domain, context=context, - view_type=['form'], views_preload=attrs.get('views', {})) - if obj_id: - self.screen.load([obj_id]) - else: - self.screen.new() - name = attrs.get('string', '') - if name: - name += ' - ' - name += self.screen.current_view.title - self.dia.set_title(name) - - title = gtk.Label() - title.set_use_markup(True) - title.modify_font(pango.FontDescription("12")) - title.set_label('' + name + '') - title.set_padding(20, 3) - title.set_alignment(0.0, 0.5) - title.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse("#000000")) - title.show() - - self.info_label = gtk.Label() - self.info_label.set_padding(3, 3) - self.info_label.set_alignment(1.0, 0.5) - - self.eb_info = gtk.EventBox() - self.eb_info.add(self.info_label) - self.eb_info.connect('button-release-event', - lambda *a: self.message_info('')) - - vbox = gtk.VBox() - vbox.pack_start(self.eb_info, expand=True, fill=True, padding=5) - vbox.show() - - hbox = gtk.HBox() - hbox.pack_start(title, expand=True, fill=True) - hbox.pack_start(vbox, expand=False, fill=True, padding=20) - hbox.show() - - frame = gtk.Frame() - frame.set_shadow_type(gtk.SHADOW_ETCHED_IN) - frame.add(hbox) - frame.show() - - eb = gtk.EventBox() - eb.add(frame) - eb.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse("#ffffff")) - eb.show() - - self.dia.vbox.pack_start(eb, expand=False, fill=True, padding=3) - - scroll = gtk.ScrolledWindow() - scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) - scroll.set_placement(gtk.CORNER_TOP_LEFT) - scroll.set_shadow_type(gtk.SHADOW_NONE) - scroll.show() - self.dia.vbox.pack_start(scroll, expand=True, fill=True) - - viewport = gtk.Viewport() - viewport.set_shadow_type(gtk.SHADOW_NONE) - viewport.show() - scroll.add(viewport) - - self.screen.widget.show() - viewport.add(self.screen.widget) - - i, j = self.screen.screen_container.size_get() - viewport.set_size_request(i, j + 30) - self.dia.show() - self.screen.current_view.set_cursor() - self.screen.display() - - def message_info(self, message, color='red'): - if message: - self.info_label.set_label(message) - self.eb_info.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse( - COLOR_SCHEMES.get(color, 'white'))) - self.eb_info.show_all() - else: - self.info_label.set_label('') - self.eb_info.hide() - - def run(self): - while True: - res = self.dia.run() - if res == gtk.RESPONSE_OK: - obj_id = None - try: - obj_id = self.screen.save_current() - except Exception, exception: - common.process_exception(exception, self.dia) - if obj_id: - return (True, (self.screen.current_model.id, - self.screen.current_model.rec_name())) - else: - self.screen.display() - else: - break - return (False, False) - - def destroy(self): - self.window.present() - self.screen.destroy() - self.dia.destroy() - class Many2One(WidgetInterface): - def __init__(self, window, parent, model, attrs=None): - if attrs is None: - attrs = {} - WidgetInterface.__init__(self, window, parent, model, attrs) + def __init__(self, field_name, model_name, window, attrs=None): + super(Many2One, self).__init__(field_name, model_name, window, + attrs=attrs) self.widget = gtk.HBox(spacing=0) self.widget.set_property('sensitive', True) @@ -208,7 +68,6 @@ class Many2One(WidgetInterface): self.tooltips.enable() self._readonly = False - self.model_type = attrs['relation'] self.completion = gtk.EntryCompletion() self.liststore = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING) @@ -219,7 +78,7 @@ class Many2One(WidgetInterface): ['rec_name']) names = [(x['id'], x['rec_name']) for x in result] except Exception, exception: - common.process_exception(exception, self._window) + common.process_exception(exception, self.window) names = [] if names: self.load_completion(names) @@ -227,12 +86,6 @@ class Many2One(WidgetInterface): def grab_focus(self): return self.wid_text.grab_focus() - def _focus_out(self): - return WidgetInterface._focus_out(self) - - def _focus_in(self): - return WidgetInterface._focus_in(self) - def load_completion(self, names): self.completion.set_match_func(self.match_func, None) self.completion.connect("match-selected", self.on_completion_match) @@ -248,8 +101,8 @@ class Many2One(WidgetInterface): return modelstr.startswith(key_string) def on_completion_match(self, completion, model, iter): - self._view.modelfield.set_client(self._view.model, int(model[iter][0])) - self.display(self._view.model, self._view.modelfield) + self.field.set_client(self.record, int(model[iter][0])) + self.display(self.record, self.field) return True def _readonly_set(self, value): @@ -267,19 +120,19 @@ class Many2One(WidgetInterface): def sig_activate(self, widget, event=None, key_press=False): if not self.focus_out: return - if not self._view.modelfield: + if not self.field: return self.changed = False - value = self._view.modelfield.get(self._view.model) + value = self.field.get(self.record) self.focus_out = False if not value: if not self._readonly and (self.wid_text.get_text() or \ - (self._view.modelfield.get_state_attrs( - self._view.model)['required']) and key_press): - domain = self._view.modelfield.domain_get(self._view.model) + (self.field.get_state_attrs( + self.record)['required']) and key_press): + domain = self.field.domain_get(self.record) context = rpc.CONTEXT.copy() - context.update(self._view.modelfield.context_get(self._view.model)) + context.update(self.field.context_get(self.record)) self.wid_text.grab_focus() try: @@ -293,70 +146,80 @@ class Many2One(WidgetInterface): 'search', dom, 0, _LIMIT, None, context) except Exception, exception: self.focus_out = True - common.process_exception(exception, self._window) + common.process_exception(exception, self.window) self.changed = True return False if len(ids)==1: - self._view.modelfield.set_client(self._view.model, ids[0], + self.field.set_client(self.record, ids[0], force_change=True) self.focus_out = True - self.display(self._view.model, self._view.modelfield) + self.display(self.record, self.field) return True win = WinSearch(self.attrs['relation'], sel_multi=False, ids=ids, context=context, domain=domain, - parent=self._window, + parent=self.window, views_preload=self.attrs.get('views', {})) ids = win.run() if ids: - self._view.modelfield.set_client(self._view.model, ids[0], + self.field.set_client(self.record, ids[0], force_change=True) self.focus_out = True - self.display(self._view.model, self._view.modelfield) + self.display(self.record, self.field) return True else: self.focus_out = True - self.display(self._view.model, self._view.modelfield) + self.display(self.record, self.field) return False self.focus_out = True - self.display(self._view.model, self._view.modelfield) + self.display(self.record, self.field) self.changed = True return True + def get_screen(self): + domain = self.field.domain_get(self.record) + context = self.field.context_get(self.record) + return Screen(self.attrs['relation'], self.window, domain=domain, + context=context, view_type=['form'], + views_preload=self.attrs.get('views', {})) + def sig_new(self, *args): self.focus_out = False - domain = self._view.modelfield.domain_get(self._view.model) - context = self._view.modelfield.context_get(self._view.model) - dia = Dialog(self.attrs['relation'], attrs=self.attrs, - window=self._window, domain=domain, context=context) - res, value = dia.run() - if res: - self._view.modelfield.set_client(self._view.model, value) - self.display(self._view.model, self._view.modelfield) - dia.destroy() + screen = self.get_screen() + win = WinForm(screen, self.window, new=True) + while win.run(): + if screen.save_current(): + value = (screen.current_record.id, + screen.current_record.rec_name()) + self.field.set_client(self.record, value) + break + else: + screen.display() + win.destroy() self.focus_out = True def sig_edit(self, widget): self.changed = False - value = self._view.modelfield.get(self._view.model) + value = self.field.get(self.record) self.focus_out = False if value: - domain = self._view.modelfield.domain_get(self._view.model) - context = self._view.modelfield.context_get(self._view.model) - dia = Dialog(self.attrs['relation'], - self._view.modelfield.get(self._view.model), - attrs=self.attrs, window=self._window, domain=domain, - context=context) - res, value = dia.run() - if res: - self._view.modelfield.set_client(self._view.model, value, - force_change=True) - dia.destroy() + screen = self.get_screen() + screen.load([self.field.get(self.record)]) + win = WinForm(screen, self.window) + while win.run(): + if screen.save_current(): + value = (screen.current_record.id, + screen.current_record.rec_name()) + self.field.set_client(self.record, value, force_change=True) + break + else: + screen.display() + win.destroy() else: if not self._readonly: - domain = self._view.modelfield.domain_get(self._view.model) + domain = self.field.domain_get(self.record) context = rpc.CONTEXT.copy() - context.update(self._view.modelfield.context_get(self._view.model)) + context.update(self.field.context_get(self.record)) self.wid_text.grab_focus() try: @@ -370,26 +233,26 @@ class Many2One(WidgetInterface): 'search', dom, 0, _LIMIT, None, context) except Exception, exception: self.focus_out = True - common.process_exception(exception, self._window) + common.process_exception(exception, self.window) self.changed = True return False if ids and len(ids)==1: - self._view.modelfield.set_client(self._view.model, ids[0], + self.field.set_client(self.record, ids[0], force_change=True) self.focus_out = True - self.display(self._view.model, self._view.modelfield) + self.display(self.record, self.field) return True win = WinSearch(self.attrs['relation'], sel_multi=False, ids=ids, context=context, - domain=domain, parent=self._window, + domain=domain, parent=self.window, views_preload=self.attrs.get('views', {})) ids = win.run() if ids: - self._view.modelfield.set_client(self._view.model, ids[0], + self.field.set_client(self.record, ids[0], force_change=True) self.focus_out = True - self.display(self._view.model, self._view.modelfield) + self.display(self.record, self.field) self.changed = True def sig_key_press(self, widget, event, *args): @@ -407,22 +270,22 @@ class Many2One(WidgetInterface): def sig_changed(self, *args): if not self.changed: return False - if self._view.modelfield.get(self._view.model): - self._view.modelfield.set_client(self._view.model, False) - self.display(self._view.model, self._view.modelfield) + if self.field.get(self.record): + self.field.set_client(self.record, False) + self.display(self.record, self.field) return False - def set_value(self, model, model_field): + def set_value(self, record, field): pass # No update of the model, the model is updated in real time ! - def display(self, model, model_field): + def display(self, record, field): self.changed = False - if not model_field: + if not field: self.wid_text.set_text('') self.changed = True return False - WidgetInterface.display(self, model, model_field) - res = model_field.get_client(model) + super(Many2One, self).display(record, field) + res = field.get_client(record) self.wid_text.set_text((res and str(res)) or '') img = gtk.Image() if res: @@ -436,10 +299,15 @@ class Many2One(WidgetInterface): self.changed = True def _populate_popup(self, widget, menu): - value = self._view.modelfield.get(self._view.model) - ir_action_keyword = RPCProxy('ir.action.keyword') - relates = ir_action_keyword.get_keyword('form_relate', - (self.model_type, 0), rpc.CONTEXT) + value = self.field.get(self.record) + args = ('model', 'ir.action.keyword', 'get_keyword', + 'form_relate', (self.attrs['relation'], 0), rpc.CONTEXT) + try: + relates = rpc.execute(*args) + except Exception, exception: + relates = common.process_exception(exception, self.window) + if not relates: + return False menu_entries = [] menu_entries.append((None, None, None)) menu_entries += self._menu_entries @@ -471,27 +339,27 @@ class Many2One(WidgetInterface): data = {} context = {} act = action.copy() - obj_id = self._view.modelfield.get(self._view.model) + obj_id = self.field.get(self.record) if not obj_id: common.message(_('You must select a record to use the relation!'), - self._window) + self.window) return False - screen = Screen(self.attrs['relation'], self._window) + screen = Screen(self.attrs['relation'], self.window) screen.load([obj_id]) encoder = PYSONEncoder() - act['domain'] = encoder.encode(screen.current_model.expr_eval( + act['domain'] = encoder.encode(screen.current_record.expr_eval( act.get('domain', []), check_load=False)) - act['context'] = encoder.encode(screen.current_model.expr_eval( + act['context'] = encoder.encode(screen.current_record.expr_eval( act.get('context', {}), check_load=False)) - data['model'] = self.model_type + data['model'] = self.attrs['relation'] data['id'] = obj_id data['ids'] = [obj_id] - return Action._exec_action(act, self._window, data, context) + return Action._exec_action(act, self.window, data, context) def click_and_action(self, atype): - obj_id = self._view.modelfield.get(self._view.model) - return Action.exec_keyword(atype, self._window, { - 'model': self.model_type, + obj_id = self.field.get(self.record) + return Action.exec_keyword(atype, self.window, { + 'model': self.attrs['relation'], 'id': obj_id or False, 'ids': [obj_id], }, alwaysask=True) diff --git a/tryton/gui/window/view_form/view/form_gtk/one2many.py b/tryton/gui/window/view_form/view/form_gtk/one2many.py index 673172fab8..a1ac0f0a40 100644 --- a/tryton/gui/window/view_form/view/form_gtk/one2many.py +++ b/tryton/gui/window/view_form/view/form_gtk/one2many.py @@ -5,8 +5,9 @@ import gettext from tryton.common import TRYTON_ICON, COLOR_SCHEMES from interface import WidgetInterface from tryton.gui.window.view_form.screen import Screen -from tryton.gui.window.view_form.model.group import ModelRecordGroup +from tryton.gui.window.view_form.model.group import Group from tryton.gui.window.win_search import WinSearch +from tryton.gui.window.win_form import WinForm from tryton.gui.window.view_form.widget_search.form import _LIMIT import tryton.common as common import tryton.rpc as rpc @@ -14,462 +15,153 @@ import pango _ = gettext.gettext -def _create_menu(self, attrs): - hbox = gtk.HBox(homogeneous=False, spacing=0) - menubar = gtk.MenuBar() - if hasattr(menubar, 'set_pack_direction') and \ - hasattr(menubar, 'set_child_pack_direction'): - menubar.set_pack_direction(gtk.PACK_DIRECTION_LTR) - menubar.set_child_pack_direction(gtk.PACK_DIRECTION_LTR) - - menuitem_title = gtk.ImageMenuItem(stock_id='tryton-preferences') - - menu_title = gtk.Menu() - menuitem_set_to_default = gtk.MenuItem(_('Set to default value'), True) - menuitem_set_to_default.connect('activate', - lambda *x: self._menu_sig_default_get()) - menu_title.add(menuitem_set_to_default) - menuitem_set_default = gtk.MenuItem(_('Set as default'), True) - menuitem_set_default.connect('activate', - lambda *x: self._menu_sig_default_set()) - menu_title.add(menuitem_set_default) - menuitem_reset_default = gtk.MenuItem(_('Reset default'), True) - menuitem_reset_default.connect('activate', - lambda *x: self._menu_sig_default_set(reset=True)) - menu_title.add(menuitem_reset_default) - menuitem_title.set_submenu(menu_title) - - menubar.add(menuitem_title) - hbox.pack_start(menubar, expand=True, fill=True) - - tooltips = common.Tooltips() - - if attrs.get('add_remove'): - - self.wid_text = gtk.Entry() - self.wid_text.set_property('width_chars', 13) - self.wid_text.connect('activate', self._sig_activate) - hbox.pack_start(self.wid_text, expand=True, fill=True) - - self.but_add = gtk.Button() - tooltips.set_tip(self.but_add, _('Add')) - self.but_add.connect('clicked', self._sig_add) - img_add = gtk.Image() - img_add.set_from_stock('tryton-list-add', gtk.ICON_SIZE_SMALL_TOOLBAR) - img_add.set_alignment(0.5, 0.5) - self.but_add.add(img_add) - self.but_add.set_relief(gtk.RELIEF_NONE) - hbox.pack_start(self.but_add, expand=False, fill=False) - - self.but_remove = gtk.Button() - tooltips.set_tip(self.but_remove, _('Remove')) - self.but_remove.connect('clicked', self._sig_remove, True) - img_remove = gtk.Image() - img_remove.set_from_stock('tryton-list-remove', gtk.ICON_SIZE_SMALL_TOOLBAR) - img_remove.set_alignment(0.5, 0.5) - self.but_remove.add(img_remove) - self.but_remove.set_relief(gtk.RELIEF_NONE) - hbox.pack_start(self.but_remove, expand=False, fill=False) - - hbox.pack_start(gtk.VSeparator(), expand=False, fill=True) - - self.but_new = gtk.Button() - tooltips.set_tip(self.but_new, _('Create a new record')) - self.but_new.connect('clicked', self._sig_new) - img_new = gtk.Image() - img_new.set_from_stock('tryton-new', gtk.ICON_SIZE_SMALL_TOOLBAR) - img_new.set_alignment(0.5, 0.5) - self.but_new.add(img_new) - self.but_new.set_relief(gtk.RELIEF_NONE) - hbox.pack_start(self.but_new, expand=False, fill=False) - - self.but_open = gtk.Button() - tooltips.set_tip(self.but_open, _('Edit selected record')) - self.but_open.connect('clicked', self._sig_edit) - img_open = gtk.Image() - img_open.set_from_stock('tryton-open', gtk.ICON_SIZE_SMALL_TOOLBAR) - img_open.set_alignment(0.5, 0.5) - self.but_open.add(img_open) - self.but_open.set_relief(gtk.RELIEF_NONE) - hbox.pack_start(self.but_open, expand=False, fill=False) - - self.but_del = gtk.Button() - tooltips.set_tip(self.but_del, _('Delete selected record')) - self.but_del.connect('clicked', self._sig_remove) - img_del = gtk.Image() - img_del.set_from_stock('tryton-delete', gtk.ICON_SIZE_SMALL_TOOLBAR) - img_del.set_alignment(0.5, 0.5) - self.but_del.add(img_del) - self.but_del.set_relief(gtk.RELIEF_NONE) - hbox.pack_start(self.but_del, expand=False, fill=False) - - hbox.pack_start(gtk.VSeparator(), expand=False, fill=True) - - but_pre = gtk.Button() - tooltips.set_tip(but_pre, _('Previous')) - but_pre.connect('clicked', self._sig_previous) - img_pre = gtk.Image() - img_pre.set_from_stock('tryton-go-previous', gtk.ICON_SIZE_SMALL_TOOLBAR) - img_pre.set_alignment(0.5, 0.5) - but_pre.add(img_pre) - but_pre.set_relief(gtk.RELIEF_NONE) - hbox.pack_start(but_pre, expand=False, fill=False) - - self.label = gtk.Label('(0,0)') - hbox.pack_start(self.label, expand=False, fill=False) - - but_next = gtk.Button() - tooltips.set_tip(but_next, _('Next')) - but_next.connect('clicked', self._sig_next) - img_next = gtk.Image() - img_next.set_from_stock('tryton-go-next', gtk.ICON_SIZE_SMALL_TOOLBAR) - img_next.set_alignment(0.5, 0.5) - but_next.add(img_next) - but_next.set_relief(gtk.RELIEF_NONE) - hbox.pack_start(but_next, expand=False, fill=False) - - hbox.pack_start(gtk.VSeparator(), expand=False, fill=True) - - but_switch = gtk.Button() - tooltips.set_tip(but_switch, _('Switch')) - but_switch.connect('clicked', self.switch_view) - img_switch = gtk.Image() - img_switch.set_from_stock('tryton-fullscreen', gtk.ICON_SIZE_SMALL_TOOLBAR) - img_switch.set_alignment(0.5, 0.5) - but_switch.add(img_switch) - but_switch.set_relief(gtk.RELIEF_NONE) - hbox.pack_start(but_switch, expand=False, fill=False) - - if attrs.get('add_remove'): - hbox.set_focus_chain([self.wid_text]) - else: - hbox.set_focus_chain([]) - - tooltips.enable() - - return hbox, menuitem_title - - -class Dialog(object): - - def __init__(self, model_name, parent, model=None, attrs=None, - model_ctx=None, window=None, default_get_ctx=None, readonly=False, - domain=None): - - if attrs is None: - attrs = {} - if model_ctx is None: - model_ctx = {} - if default_get_ctx is None: - default_get_ctx = {} - - self.attrs = attrs - self.model_name = model_name - self.parent=parent - self.model_ctx = model_ctx - self.default_get_ctx = default_get_ctx - - self.dia = gtk.Dialog(_('Link'), window, - gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT) - self.dia.connect('close', self._sig_close) - self.window = window - self.dia.set_property('default-width', 760) - self.dia.set_property('default-height', 500) - self.dia.set_position(gtk.WIN_POS_CENTER_ON_PARENT) - self.dia.set_icon(TRYTON_ICON) - self.dia.set_has_separator(False) - - self.accel_group = gtk.AccelGroup() - self.dia.add_accel_group(self.accel_group) - - self.but_cancel = None - if not model: - icon_cancel = gtk.STOCK_CANCEL - self.but_cancel = self.dia.add_button(icon_cancel, - gtk.RESPONSE_CANCEL) - - self.but_ok = self.dia.add_button(gtk.STOCK_OK, gtk.RESPONSE_OK) - self.but_ok.add_accelerator('clicked', self.accel_group, - gtk.keysyms.Return, gtk.gdk.CONTROL_MASK, gtk.ACCEL_VISIBLE) - - self.dia.set_default_response(gtk.RESPONSE_OK) - - self.default_get_ctx = default_get_ctx - - menuitem_title = None - if isinstance(model, ModelRecordGroup): - hbox, menuitem_title = _create_menu(self, attrs) - self.dia.vbox.pack_start(hbox, expand=False, fill=True) - self.dia.show() - - self.screen = Screen(model_name, self.dia, view_type=[], parent=parent, - parent_name=attrs.get('relation_field', ''), - exclude_field=attrs.get('relation_field', None), readonly=readonly, - domain=domain) - self.screen.models._context.update(model_ctx) - modified = False - if not model: - model = self.screen.new(context=default_get_ctx) - modified = True - if isinstance(model, ModelRecordGroup): - self.screen.tree_saves = False - self.screen.add_view_id(False, 'tree', display=True, - context=default_get_ctx) - self.screen.add_view_id(False, 'form', display=False, - context=default_get_ctx) - self.screen.signal_connect(self, 'record-message', self._sig_label) - self.screen.widget.connect('key_press_event', self.on_keypress) - self.screen.models_set(model) - else: - self.screen.models.model_add(model, modified=modified) - self.screen.current_model = model - if ('views' in attrs) and ('form' in attrs['views']): - arch = attrs['views']['form']['arch'] - fields = attrs['views']['form']['fields'] - if attrs.get('relation_field', False) \ - and attrs['relation_field'] in fields: - del fields[attrs['relation_field']] - self.screen.add_view(arch, fields, display=True, - context=default_get_ctx) - else: - self.screen.add_view_id(False, 'form', display=True, - context=default_get_ctx) - - name = self.screen.current_view.title - self.dia.set_title(name) - if menuitem_title: - menuitem_title.get_child().set_text(name) - - title = gtk.Label() - title.set_use_markup(True) - title.modify_font(pango.FontDescription("12")) - title.set_label('' + name + '') - title.set_padding(20, 3) - title.set_alignment(0.0, 0.5) - title.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse("#000000")) - title.show() - - self.info_label = gtk.Label() - self.info_label.set_padding(3, 3) - self.info_label.set_alignment(1.0, 0.5) - - self.eb_info = gtk.EventBox() - self.eb_info.add(self.info_label) - self.eb_info.connect('button-release-event', - lambda *a: self.message_info('')) - - vbox = gtk.VBox() - vbox.pack_start(self.eb_info, expand=True, fill=True, padding=5) - vbox.show() - - hbox = gtk.HBox() - hbox.pack_start(title, expand=True, fill=True) - hbox.pack_start(vbox, expand=False, fill=True, padding=20) - hbox.show() - - frame = gtk.Frame() - frame.set_shadow_type(gtk.SHADOW_ETCHED_IN) - frame.add(hbox) - frame.show() - - eb = gtk.EventBox() - eb.add(frame) - eb.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse("#ffffff")) - eb.show() - - self.dia.vbox.pack_start(eb, expand=False, fill=True, padding=3) - - scroll = gtk.ScrolledWindow() - scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) - scroll.set_placement(gtk.CORNER_TOP_LEFT) - scroll.set_shadow_type(gtk.SHADOW_NONE) - scroll.show() - self.dia.vbox.pack_start(scroll, expand=True, fill=True) - - viewport = gtk.Viewport() - viewport.set_shadow_type(gtk.SHADOW_NONE) - viewport.show() - scroll.add(viewport) - - self.screen.widget.show() - viewport.add(self.screen.widget) - - width, height = self.screen.screen_container.size_get() - viewport.set_size_request(width, height + 30) - self.screen.display() - self.screen.current_view.set_cursor() - - def message_info(self, message, color='red'): - if message: - self.info_label.set_label(message) - self.eb_info.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse( - COLOR_SCHEMES.get(color, 'white'))) - self.eb_info.show_all() - else: - self.info_label.set_label('') - self.eb_info.hide() - - def on_keypress(self, widget, event): - if (event.keyval == gtk.keysyms.F3) \ - and self.but_new.get_property('sensitive'): - self._sig_new(widget) - return False - if event.keyval == gtk.keysyms.F2 \ - and widget == self.screen.widget: - self._sig_edit(widget) - return False - if event.keyval in (gtk.keysyms.Delete, gtk.keysyms.KP_Delete) \ - and widget == self.screen.widget: - self._sig_remove(widget) - return False - - def switch_view(self, widget): - self.screen.switch_view() - - def _sig_new(self, widget): - if (self.screen.current_view.view_type == 'form') \ - or self.screen.editable_get(): - self.screen.new(context=self.default_get_ctx) - self.screen.current_view.widget.set_sensitive(True) - else: - dia = Dialog(self.model_name, parent=self.parent, - attrs=self.attrs, model_ctx=self.model_ctx, - default_get_ctx=self.default_get_ctx, - window=self.window) - res = True - while res: - res, value = dia.run() - if res: - self.screen.models.model_add(value) - value.signal('record-changed', value.parent) - self.screen.display() - dia.new() - dia.destroy() - - def _sig_edit(self, widget=None): - if self.screen.current_model: - dia = Dialog(self.model_name, parent=self.parent, - model=self.screen.current_model, attrs=self.attrs, - window=self.window) - res, value = dia.run() - dia.destroy() - - def _sig_next(self, widget): - self.screen.display_next() - - def _sig_previous(self, widget): - self.screen.display_prev() - - def _sig_remove(self, widget, remove=False): - self.screen.remove(remove=remove) - - def _sig_activate(self, *args): - self._sig_add() - self.wid_text.grab_focus() - - def _sig_add(self, *args): - domain = [] - context = rpc.CONTEXT.copy() - - try: - if self.wid_text.get_text(): - dom = [('rec_name', 'ilike', - '%' + self.wid_text.get_text() + '%'), domain] - else: - dom = domain - ids = rpc.execute('model', self.attrs['relation'], - 'search', dom, 0, _LIMIT, None, context) - except Exception, exception: - common.process_exception(exception, self._window) - return False - if len(ids) != 1: - win = WinSearch(self.attrs['relation'], sel_multi=True, ids=ids, - context=context, domain=domain, parent=self._window, - views_preload=self.attrs.get('views', {})) - ids = win.run() - - res_id = None - if ids: - res_id = ids[0] - self.screen.load(ids, modified=True) - self.screen.display(res_id=res_id) - if self.screen.current_view: - self.screen.current_view.set_cursor() - self.wid_text.set_text('') - - def _sig_label(self, screen, signal_data): - name = '_' - if signal_data[0] >= 0: - name = str(signal_data[0] + 1) - line = '(%s/%s)' % (name, signal_data[1]) - self.label.set_text(line) - - def _menu_sig_default_get(self): - pass - - def _menu_sig_default_set(self, reset=False): - pass - - def new(self): - model = self.screen.new(context=self.default_get_ctx) - self.screen.models.model_add(model) - return True - - def run(self): - end = False - while not end: - res = self.dia.run() - self.screen.current_view.set_value() - end = (res != gtk.RESPONSE_OK) \ - or (not self.screen.current_model \ - or self.screen.current_model.validate()) - if not end: - self.screen.current_view.set_cursor() - self.screen.display() - if self.but_cancel: - self.but_cancel.set_label(gtk.STOCK_CLOSE) - - if res == gtk.RESPONSE_OK: - model = self.screen.current_model - return (True, model) - return (False, None) - - def _sig_close(self, widget): - if self.screen.current_view: - self.screen.current_view.set_value() - if not self.but_cancel \ - and self.screen.current_model \ - and not self.screen.current_model.validate(): - if self.screen.current_view: - self.screen.current_view.set_cursor() - self.screen.display() - widget.emit_stop_by_name('close') - - def destroy(self): - self.screen.signal_unconnect(self) - self.window.present() - self.dia.destroy() - self.screen.destroy() - class One2Many(WidgetInterface): - def __init__(self, window, parent, model, attrs=None): - super(One2Many, self).__init__(window, parent, model, attrs) + def __init__(self, field_name, model_name, window, attrs=None): + super(One2Many, self).__init__(field_name, model_name, window, + attrs=attrs) self.widget = gtk.VBox(homogeneous=False, spacing=5) - hbox, menuitem_title = _create_menu(self, attrs) + hbox = gtk.HBox(homogeneous=False, spacing=0) + menubar = gtk.MenuBar() + if hasattr(menubar, 'set_pack_direction') and \ + hasattr(menubar, 'set_child_pack_direction'): + menubar.set_pack_direction(gtk.PACK_DIRECTION_LTR) + menubar.set_child_pack_direction(gtk.PACK_DIRECTION_LTR) + + menuitem_title = gtk.ImageMenuItem(stock_id='tryton-preferences') + + menu_title = gtk.Menu() + menuitem_set_to_default = gtk.MenuItem(_('Set to default value'), True) + menuitem_set_to_default.connect('activate', + lambda *x: self._menu_sig_default_get()) + menu_title.add(menuitem_set_to_default) + menuitem_set_default = gtk.MenuItem(_('Set as default'), True) + menuitem_set_default.connect('activate', + lambda *x: self._menu_sig_default_set()) + menu_title.add(menuitem_set_default) + menuitem_reset_default = gtk.MenuItem(_('Reset default'), True) + menuitem_reset_default.connect('activate', + lambda *x: self._menu_sig_default_set(reset=True)) + menu_title.add(menuitem_reset_default) + menuitem_title.set_submenu(menu_title) + + menubar.add(menuitem_title) + hbox.pack_start(menubar, expand=True, fill=True) + + tooltips = common.Tooltips() + + if attrs.get('add_remove'): + + self.wid_text = gtk.Entry() + self.wid_text.set_property('width_chars', 13) + self.wid_text.connect('activate', self._sig_activate) + hbox.pack_start(self.wid_text, expand=True, fill=True) + + self.but_add = gtk.Button() + tooltips.set_tip(self.but_add, _('Add')) + self.but_add.connect('clicked', self._sig_add) + img_add = gtk.Image() + img_add.set_from_stock('tryton-list-add', gtk.ICON_SIZE_SMALL_TOOLBAR) + img_add.set_alignment(0.5, 0.5) + self.but_add.add(img_add) + self.but_add.set_relief(gtk.RELIEF_NONE) + hbox.pack_start(self.but_add, expand=False, fill=False) + + self.but_remove = gtk.Button() + tooltips.set_tip(self.but_remove, _('Remove')) + self.but_remove.connect('clicked', self._sig_remove, True) + img_remove = gtk.Image() + img_remove.set_from_stock('tryton-list-remove', gtk.ICON_SIZE_SMALL_TOOLBAR) + img_remove.set_alignment(0.5, 0.5) + self.but_remove.add(img_remove) + self.but_remove.set_relief(gtk.RELIEF_NONE) + hbox.pack_start(self.but_remove, expand=False, fill=False) + + hbox.pack_start(gtk.VSeparator(), expand=False, fill=True) + + self.but_new = gtk.Button() + tooltips.set_tip(self.but_new, _('Create a new record')) + self.but_new.connect('clicked', self._sig_new) + img_new = gtk.Image() + img_new.set_from_stock('tryton-new', gtk.ICON_SIZE_SMALL_TOOLBAR) + img_new.set_alignment(0.5, 0.5) + self.but_new.add(img_new) + self.but_new.set_relief(gtk.RELIEF_NONE) + hbox.pack_start(self.but_new, expand=False, fill=False) + + self.but_open = gtk.Button() + tooltips.set_tip(self.but_open, _('Edit selected record')) + self.but_open.connect('clicked', self._sig_edit) + img_open = gtk.Image() + img_open.set_from_stock('tryton-open', gtk.ICON_SIZE_SMALL_TOOLBAR) + img_open.set_alignment(0.5, 0.5) + self.but_open.add(img_open) + self.but_open.set_relief(gtk.RELIEF_NONE) + hbox.pack_start(self.but_open, expand=False, fill=False) + + self.but_del = gtk.Button() + tooltips.set_tip(self.but_del, _('Delete selected record')) + self.but_del.connect('clicked', self._sig_remove) + img_del = gtk.Image() + img_del.set_from_stock('tryton-delete', gtk.ICON_SIZE_SMALL_TOOLBAR) + img_del.set_alignment(0.5, 0.5) + self.but_del.add(img_del) + self.but_del.set_relief(gtk.RELIEF_NONE) + hbox.pack_start(self.but_del, expand=False, fill=False) + + hbox.pack_start(gtk.VSeparator(), expand=False, fill=True) + + but_pre = gtk.Button() + tooltips.set_tip(but_pre, _('Previous')) + but_pre.connect('clicked', self._sig_previous) + img_pre = gtk.Image() + img_pre.set_from_stock('tryton-go-previous', gtk.ICON_SIZE_SMALL_TOOLBAR) + img_pre.set_alignment(0.5, 0.5) + but_pre.add(img_pre) + but_pre.set_relief(gtk.RELIEF_NONE) + hbox.pack_start(but_pre, expand=False, fill=False) + + self.label = gtk.Label('(0,0)') + hbox.pack_start(self.label, expand=False, fill=False) + + but_next = gtk.Button() + tooltips.set_tip(but_next, _('Next')) + but_next.connect('clicked', self._sig_next) + img_next = gtk.Image() + img_next.set_from_stock('tryton-go-next', gtk.ICON_SIZE_SMALL_TOOLBAR) + img_next.set_alignment(0.5, 0.5) + but_next.add(img_next) + but_next.set_relief(gtk.RELIEF_NONE) + hbox.pack_start(but_next, expand=False, fill=False) + + hbox.pack_start(gtk.VSeparator(), expand=False, fill=True) + + but_switch = gtk.Button() + tooltips.set_tip(but_switch, _('Switch')) + but_switch.connect('clicked', self.switch_view) + img_switch = gtk.Image() + img_switch.set_from_stock('tryton-fullscreen', gtk.ICON_SIZE_SMALL_TOOLBAR) + img_switch.set_alignment(0.5, 0.5) + but_switch.add(img_switch) + but_switch.set_relief(gtk.RELIEF_NONE) + hbox.pack_start(but_switch, expand=False, fill=False) + + if attrs.get('add_remove'): + hbox.set_focus_chain([self.wid_text]) + else: + hbox.set_focus_chain([]) + + tooltips.enable() self.widget.pack_start(hbox, expand=False, fill=True) - self.screen = Screen(attrs['relation'], self._window, - view_type=attrs.get('mode','tree,form').split(','), - parent=self.parent, parent_name=attrs.get('relation_field', ''), + self.screen = Screen(attrs['relation'], self.window, + view_type=attrs.get('mode', 'tree,form').split(','), views_preload=attrs.get('views', {}), - tree_saves=attrs.get('saves', False), create_new=True, row_activate=self._on_activate, - default_get=attrs.get('default_get', {}), exclude_field=attrs.get('relation_field', None)) self.screen.signal_connect(self, 'record-message', self._sig_label) menuitem_title.get_child().set_text(attrs.get('string', '')) @@ -515,17 +207,12 @@ class One2Many(WidgetInterface): self.but_remove.set_sensitive(not value) def _sig_new(self, widget): - self._view.view_form.set_value() - ctx = self._view.model.expr_eval(self.screen.default_get) - ctx.update(self._view.model.expr_eval(self.attrs.get('context', {}))) + self.view.set_value() + ctx = {} + ctx.update(self.record.expr_eval(self.attrs.get('context', {}))) sequence = None - idx = -1 if self.screen.current_view.view_type == 'tree': sequence = self.screen.current_view.widget_tree.sequence - select_ids = self.screen.sel_ids_get() - if select_ids: - model = self.screen.models.get_by_id(select_ids[0]) - idx = self.screen.models.models.index(model) if (self.screen.current_view.view_type == 'form') \ or self.screen.editable_get(): self.screen.new(context=ctx) @@ -533,48 +220,27 @@ class One2Many(WidgetInterface): else: readonly = False domain = [] - if self._view.modelfield and self._view.model: - modelfield = self._view.modelfield - model = self._view.model - readonly = modelfield.get_state_attrs(model + if self.field and self.record: + readonly = self.field.get_state_attrs(self.record ).get('readonly', False) - domain = modelfield.domain_get(self._view.model) - dia = Dialog(self.attrs['relation'], parent=self._view.model, - attrs=self.attrs, - model_ctx=self.screen.models._context, - default_get_ctx=ctx, window=self._window, - readonly=readonly, domain=domain) - res = True - while res: - res, value = dia.run() - if res: - if idx >= 0: - idx += 1 - self.screen.models.model_add(value, position=idx) - value.signal('record-changed', value.parent) - self.screen.display_next() - dia.new() - dia.destroy() + domain = self.field.domain_get(self.record) + win = WinForm(self.screen, self.window, new=True, + context=ctx) + while True: + if win.run(): + win.new() + else: + break + win.destroy() if sequence: - self.screen.models.set_sequence(field=sequence) + self.screen.group.set_sequence(field=sequence) def _sig_edit(self, widget=None): - self._view.view_form.set_value() - if self.screen.current_model: - readonly = False - domain = [] - if self._view.modelfield and self._view.model: - modelfield = self._view.modelfield - model = self._view.model - readonly = modelfield.get_state_attrs(model - ).get('readonly', False) - domain = modelfield.domain_get(self._view.model) - dia = Dialog(self.attrs['relation'], parent=self._view.model, - model=self.screen.current_model, attrs=self.attrs, - window=self._window, readonly=readonly, domain=domain, - model_ctx=self.screen.models.context) - res, value = dia.run() - dia.destroy() + self.view.set_value() + if self.screen.current_record: + win = WinForm(self.screen, self.window) + win.run() + win.destroy() def _sig_next(self, widget): self.screen.display_next() @@ -590,14 +256,14 @@ class One2Many(WidgetInterface): self.wid_text.grab_focus() def _sig_add(self, *args): - self._view.view_form.set_value() - domain = self._view.modelfield.domain_get(self._view.model) + self.view.set_value() + domain = self.field.domain_get(self.record) context = rpc.CONTEXT.copy() - context.update(self._view.modelfield.context_get(self._view.model)) + context.update(self.field.context_get(self.record)) domain = domain[:] - domain.extend(self._view.model.expr_eval(self.attrs.get('add_remove'), + domain.extend(self.record.expr_eval(self.attrs.get('add_remove'), context)) - removed_ids = self._view.modelfield.get_removed_ids(self._view.model) + removed_ids = self.field.get_removed_ids(self.record) try: if self.wid_text.get_text(): @@ -608,11 +274,11 @@ class One2Many(WidgetInterface): ids = rpc.execute('model', self.attrs['relation'], 'search', dom, 0, _LIMIT, None, context) except Exception, exception: - common.process_exception(exception, self._window) + common.process_exception(exception, self.window) return False if len(ids) != 1: win = WinSearch(self.attrs['relation'], sel_multi=True, ids=ids, - context=context, domain=domain, parent=self._window, + context=context, domain=domain, parent=self.window, views_preload=self.attrs.get('views', {})) ids = win.run() @@ -632,38 +298,35 @@ class One2Many(WidgetInterface): line = '(%s/%s)' % (name, signal_data[1]) self.label.set_text(line) - def display(self, model, model_field): - if not model_field: - self.screen.current_model = None + def display(self, record, field): + if field is None: + self.screen.current_record = None self.screen.display() return False - super(One2Many, self).display(model, model_field) - new_models = model_field.get_client(model) - if self.screen.models != new_models: - self.screen.models_set(new_models) + super(One2Many, self).display(record, field) + new_group = field.get_client(record) + if id(self.screen.group) != id(new_group): + self.screen.group = new_group if (self.screen.current_view.view_type == 'tree') \ and self.screen.editable_get(): - self.screen.current_model = None + self.screen.current_record = None readonly = False domain = [] - if self._view.modelfield and self._view.model: - modelfield = self._view.modelfield - model = self._view.model - readonly = modelfield.get_state_attrs(model - ).get('readonly', False) - domain = modelfield.domain_get(self._view.model) + if record: + readonly = field.get_state_attrs(record).get('readonly', False) + domain = field.domain_get(record) if self.screen.domain != domain: self.screen.domain = domain - self.screen.models.readonly = readonly + self.screen.group.readonly = readonly self.screen.display() return True def display_value(self): return '<' + self.attrs.get('string', '') + '>' - def set_value(self, model, model_field): + def set_value(self, record, field): self.screen.current_view.set_value() if self.screen.is_modified(): - model.modified = True - model.modified_fields.setdefault(model_field.name) + record.modified = True + record.modified_fields.setdefault(field.name) return True 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 f6b48b253c..12c6a04c04 100644 --- a/tryton/gui/window/view_form/view/form_gtk/parser.py +++ b/tryton/gui/window/view_form/view/form_gtk/parser.py @@ -40,16 +40,16 @@ class Button(object): def button_clicked(self, widget): if not self.form: return - model = self.form.screen.current_model + record = self.form.screen.current_record obj_id = self.form.screen.save_current() if obj_id: if not self.attrs.get('confirm', False) or \ common.sur(self.attrs['confirm'], self.form.window): button_type = self.attrs.get('type', 'workflow') ctx = rpc.CONTEXT.copy() - ctx.update(model.context_get()) + ctx.update(record.context_get()) if button_type == 'workflow': - args = ('model', self.form.screen.name, + args = ('model', self.form.screen.model_name, 'workflow_trigger_validate', obj_id, self.attrs['name'], ctx) try: @@ -58,7 +58,7 @@ class Button(object): common.process_exception(exception, self.form.window, *args) elif button_type == 'object': - args = ('model', self.form.screen.name, self.attrs['name'], + args = ('model', self.form.screen.model_name, self.attrs['name'], [obj_id], ctx) try: rpc.execute(*args) @@ -76,7 +76,7 @@ class Button(object): *args) if action_id: Action.execute(action_id, { - 'model': self.form.screen.name, + 'model': self.form.screen.model_name, 'id': obj_id, 'ids': [obj_id], }, self.form.window, context=ctx) @@ -84,14 +84,11 @@ class Button(object): raise Exception('Unallowed button type') self.form.screen.reload(writen=True) else: - if self.form.screen.form: - self.form.screen.form.message_info( - _('Invalid Form, correct red fields!')) self.form.screen.display() - def state_set(self, model): - if model: - state_changes = model.expr_eval(self.attrs.get('states', {}), + def state_set(self, record): + if record: + state_changes = record.expr_eval(self.attrs.get('states', {}), check_load=False) else: state_changes = {} @@ -120,9 +117,9 @@ class Label(gtk.Label): super(Label, self).__init__(str=str) self.attrs = attrs or {} - def state_set(self, model): - if model: - state_changes = model.expr_eval(self.attrs.get('states', {}), + def state_set(self, record): + if record: + state_changes = record.expr_eval(self.attrs.get('states', {}), check_load=False) else: state_changes = {} @@ -138,9 +135,9 @@ class VBox(gtk.VBox): super(VBox, self).__init__(homogeneous, spacing) self.attrs = attrs or {} - def state_set(self, model): - if model: - state_changes = model.expr_eval(self.attrs.get('states', {}), + def state_set(self, record): + if record: + state_changes = record.expr_eval(self.attrs.get('states', {}), check_load=False) else: state_changes = {} @@ -156,9 +153,9 @@ class Image(gtk.Image): super(Image, self).__init__() self.attrs = attrs or {} - def state_set(self, model): - if model: - state_changes = model.expr_eval(self.attrs.get('states', {}), + def state_set(self, record): + if record: + state_changes = record.expr_eval(self.attrs.get('states', {}), check_load=False) else: state_changes = {} @@ -180,9 +177,9 @@ class Frame(gtk.Frame): self.set_shadow_type(gtk.SHADOW_NONE) self.set_border_width(0) - def state_set(self, model): - if model: - state_changes = model.expr_eval(self.attrs.get('states', {}), + def state_set(self, record): + if record: + state_changes = record.expr_eval(self.attrs.get('states', {}), check_load=False) else: state_changes = {} @@ -199,9 +196,9 @@ class ScrolledWindow(gtk.ScrolledWindow): vadjustment=vadjustment) self.attrs = attrs or {} - def state_set(self, model): - if model: - state_changes = model.expr_eval(self.attrs.get('states', {}), + def state_set(self, record): + if record: + state_changes = record.expr_eval(self.attrs.get('states', {}), check_load=False) else: state_changes = {} @@ -298,7 +295,7 @@ class ParserForm(ParserInterface): screen=screen) self.widget_id = 0 - def parse(self, model, root_node, fields, notebook=None, paned=None, + def parse(self, model_name, root_node, fields, notebook=None, paned=None, tooltips=None): dict_widget = {} button_list = [] @@ -323,7 +320,7 @@ class ParserForm(ParserInterface): attrs = common.node_attributes(node) if not cursor_widget: if attrs.get('name') and fields.get(attrs['name']) \ - and not fields[attrs['name']].get('exclude_field', + and not fields[attrs['name']].attrs.get('exclude_field', False): cursor_widget = attrs.get('name') if node.localName == 'image': @@ -339,10 +336,10 @@ class ParserForm(ParserInterface): if 'string' in attrs or 'name' in attrs: if not text: if 'name' in attrs and attrs['name'] in fields: - if 'states' in fields[attrs['name']]: + if 'states' in fields[attrs['name']].attrs: attrs['states'] = \ - fields[attrs['name']]['states'] - text = fields[attrs['name']]['string'] + fields[attrs['name']].attrs['states'] + text = fields[attrs['name']].attrs['string'] vbox = VBox(attrs=attrs) button_list.append(vbox) if text: @@ -359,13 +356,16 @@ class ParserForm(ParserInterface): text = attrs.get('string', '') if 'name' in attrs and attrs['name'] in fields: if 'states' not in attrs \ - and 'states' in fields[attrs['name']]: - attrs['states'] = fields[attrs['name']]['states'] + and 'states' in fields[attrs['name']].attrs: + attrs['states'] = fields[attrs['name']].attrs['states'] if not text: - if gtk.widget_get_default_direction() == gtk.TEXT_DIR_RTL: - text = _(':') + fields[attrs['name']]['string'] + if gtk.widget_get_default_direction() == \ + gtk.TEXT_DIR_RTL: + text = _(':') + \ + fields[attrs['name']].attrs['string'] else: - text = fields[attrs['name']]['string'] + _(':') + text = fields[attrs['name']].attrs['string'] + \ + _(':') if 'align' not in attrs: attrs['align'] = 1.0 elif not text: @@ -436,7 +436,7 @@ class ParserForm(ParserInterface): container.wid_add(notebook, colspan=attrs.get('colspan', 4), expand=True, fill=True) widget, widgets, buttons, spam, notebook_list2, cursor_widget2 = \ - self.parse(model, node, fields, notebook, + self.parse(model_name, node, fields, notebook, tooltips=tooltips) max_width, max_height = -1, -1 window_width, window_height = Main.get_main().window.get_size() @@ -465,11 +465,11 @@ class ParserForm(ParserInterface): angle = 0 text = attrs.get('string', '') if 'name' in attrs and attrs['name'] in fields: - if 'states' in fields[attrs['name']]: + if 'states' in fields[attrs['name']].attrs: attrs['states'] = \ - fields[attrs['name']]['states'] + fields[attrs['name']].attrs['states'] if not text: - text = fields[attrs['name']]['string'] + text = fields[attrs['name']].attrs['string'] if not text: text = _('No String Attr.') if '_' not in text: @@ -478,7 +478,7 @@ class ParserForm(ParserInterface): label.set_angle(angle) label.set_use_underline(True) widget, widgets, buttons, spam, notebook_list2, cursor_widget2 = \ - self.parse(model, node, fields, notebook, + self.parse(model_name, node, fields, notebook, tooltips=tooltips) if not cursor_widget: cursor_widget = cursor_widget2 @@ -509,18 +509,18 @@ class ParserForm(ParserInterface): log = logging.getLogger('view') log.error('Unknown field "%s"' % str(name)) continue - ftype = attrs.get('widget', fields[name]['type']) - fields[name].update(attrs) - fields[name]['model'] = model + ftype = attrs.get('widget', fields[name].attrs['type']) + fields[name].attrs.update(attrs) + fields[name].attrs['model'] = model_name if not ftype in WIDGETS_TYPE: container.empty_add(int(attrs.get('colspan', 1))) continue - fields[name]['name'] = name + fields[name].attrs['name'] = name if 'saves' in attrs: - fields[name]['saves'] = attrs['saves'] - widget_act = WIDGETS_TYPE[ftype][0](self.window, self.parent, - model, fields[name]) + fields[name].attrs['saves'] = attrs['saves'] + widget_act = WIDGETS_TYPE[ftype][0](name, model_name, + self.window, fields[name].attrs) self.widget_id += 1 widget_act.position = self.widget_id dict_widget.setdefault(name, []) @@ -538,21 +538,22 @@ class ParserForm(ParserInterface): xfill = True if 'xfill' in attrs: xfill = bool(common.safe_eval(attrs['xfill'])) - hlp = fields[name].get('help', attrs.get('help', False)) + hlp = fields[name].attrs.get('help', attrs.get('help', False)) if attrs.get('height', False) or attrs.get('width', False): widget_act.widget.set_size_request( int(attrs.get('width', -1)), int(attrs.get('height', -1))) translate = False if ftype in ('char', 'text'): - translate = fields[name].get('translate', False) - container.wid_add(widget_act.widget, fields[name]['string'], - expand, translate=translate, colspan=size, fname=name, + translate = fields[name].attrs.get('translate', False) + container.wid_add(widget_act.widget, + fields[name].attrs['string'], expand, + translate=translate, colspan=size, fname=name, help_tip=hlp, fill=fill, xexpand=xexpand, xfill=xfill) elif node.localName == 'group': widget, widgets, buttons, spam, notebook_list2, cursor_widget2 = \ - self.parse(model, node, fields, tooltips=tooltips) + self.parse(model_name, node, fields, tooltips=tooltips) if not cursor_widget: cursor_widget = cursor_widget2 notebook_list.extend(notebook_list2) @@ -562,9 +563,9 @@ class ParserForm(ParserInterface): button_list += buttons text = '' if 'name' in attrs and attrs['name'] in fields: - if 'states' in fields[attrs['name']]: - attrs['states'] = fields[attrs['name']]['states'] - text = fields[attrs['name']]['string'] + if 'states' in fields[attrs['name']].attrs: + attrs['states'] = fields[attrs['name']].attrs['states'] + text = fields[attrs['name']].attrs['string'] if attrs.get('string'): text = attrs['string'] @@ -580,7 +581,7 @@ class ParserForm(ParserInterface): container.wid_add(hpaned, colspan=int(attrs.get('colspan', 4)), expand=True, fill=True) widget, widgets, buttons, spam, notebook_list2, cursor_widget2 = \ - self.parse(model, node, fields, paned=hpaned, + self.parse(model_name, node, fields, paned=hpaned, tooltips=tooltips) if not cursor_widget: cursor_widget = cursor_widget2 @@ -596,7 +597,7 @@ class ParserForm(ParserInterface): container.wid_add(vpaned, colspan=int(attrs.get('colspan', 4)), expand=True, fill=True) widget, widgets, buttons, spam, notebook_list, cursor_widget2 = \ - self.parse(model, node, fields, paned=vpaned, + self.parse(model_name, node, fields, paned=vpaned, tooltips=tooltips) if not cursor_widget: cursor_widget = cursor_widget2 @@ -609,7 +610,7 @@ class ParserForm(ParserInterface): vpaned.set_position(int(attrs['position'])) elif node.localName == 'child1': widget, widgets, buttons, spam, notebook_list2, cursor_widget2 = \ - self.parse(model, node, fields, paned=paned, + self.parse(model_name, node, fields, paned=paned, tooltips=tooltips) if not cursor_widget: cursor_widget = cursor_widget2 @@ -621,7 +622,7 @@ class ParserForm(ParserInterface): paned.pack1(widget, resize=True, shrink=True) elif node.localName == 'child2': widget, widgets, buttons, spam, notebook_list, cursor_widget2 = \ - self.parse(model, node, fields, paned=paned, + self.parse(model_name, node, fields, paned=paned, tooltips=tooltips) if not cursor_widget: cursor_widget = cursor_widget2 @@ -632,20 +633,20 @@ class ParserForm(ParserInterface): dict_widget[widget_name].extend(widgets) paned.pack2(widget, resize=True, shrink=True) for (button, src, name, widget) in container.trans_box: - button.connect('clicked', self.translate, model, name, + button.connect('clicked', self.translate, model_name, name, src, widget) return container.pop(), dict_widget, button_list, on_write, \ notebook_list, cursor_widget - def translate(self, widget, model, name, src, widget_entry): - obj_id = self.screen.current_model.id + def translate(self, widget, model_name, name, src, widget_entry): + obj_id = self.screen.current_record.id if obj_id < 0: common.message( _('You need to save the record before adding translations!'), parent=self.window) return False - obj_id = self.screen.current_model.save(force_reload=False) + obj_id = self.screen.current_record.save(force_reload=False) try: lang_ids = rpc.execute('model', 'ir.lang', 'search', [('translatable', '=', '1')]) @@ -744,7 +745,7 @@ class ParserForm(ParserInterface): context = copy.copy(rpc.CONTEXT) context['language'] = lang['code'] try: - val = rpc.execute('model', model, + val = rpc.execute('model', model_name, 'read', [obj_id], [name], context) except Exception, exception: common.process_exception(exception, self.window) @@ -795,7 +796,7 @@ class ParserForm(ParserInterface): value_set(widget_entry, new_val['value']) context = copy.copy(rpc.CONTEXT) context['language'] = new_val['code'] - args = ('model', model, 'write', [obj_id], + args = ('model', model_name, 'write', [obj_id], {str(name): new_val['value']}, context) try: rpc.execute(*args) @@ -805,7 +806,7 @@ class ParserForm(ParserInterface): self.window.present() win.destroy() return - self.screen.current_model.reload() + self.screen.current_record.reload() self.window.present() win.destroy() return True diff --git a/tryton/gui/window/view_form/view/form_gtk/progressbar.py b/tryton/gui/window/view_form/view/form_gtk/progressbar.py index 4b30306d29..de33a93725 100644 --- a/tryton/gui/window/view_form/view/form_gtk/progressbar.py +++ b/tryton/gui/window/view_form/view/form_gtk/progressbar.py @@ -14,8 +14,9 @@ class ProgressBar(WidgetInterface): 'top_to_bottom': gtk.PROGRESS_TOP_TO_BOTTOM, } - def __init__(self, window, parent, model, attrs=None): - super(ProgressBar, self).__init__(window, parent, model=model, attrs=attrs) + def __init__(self, field_name, model_name, window, attrs=None): + super(ProgressBar, self).__init__(field_name, model_name, window, + parent, attrs=attrs) self.digits = attrs.get('digits', (16, 2)) self.widget = gtk.ProgressBar() orientation = self.orientations.get(attrs.get('orientation', @@ -30,7 +31,7 @@ class ProgressBar(WidgetInterface): return False value = float(model_field.get(model) or 0.0) if isinstance(self.digits, str): - digits = self._view.model.expr_eval(self.digits) + digits = self.record.expr_eval(self.digits) else: digits = self.digits self.widget.set_text(locale.format('%.' + str(digits[1]) + 'f', diff --git a/tryton/gui/window/view_form/view/form_gtk/reference.py b/tryton/gui/window/view_form/view/form_gtk/reference.py index 9fbe878e8a..53443f646c 100644 --- a/tryton/gui/window/view_form/view/form_gtk/reference.py +++ b/tryton/gui/window/view_form/view/form_gtk/reference.py @@ -4,10 +4,10 @@ import gtk import gobject import gettext from interface import WidgetInterface -from many2one import Dialog from tryton.gui.window.win_search import WinSearch +from tryton.gui.window.win_form import WinForm +from tryton.gui.window.view_form.screen import Screen import tryton.rpc as rpc -from tryton.rpc import RPCProxy import tryton.common as common from tryton.gui.window.view_form.widget_search.form import _LIMIT @@ -16,10 +16,9 @@ _ = gettext.gettext class Reference(WidgetInterface): - def __init__(self, window, parent, model, attrs=None): - if attrs is None: - attrs = {} - super(Reference, self).__init__(window, parent, model, attrs) + def __init__(self, field_name, model_name, window, attrs=None): + super(Reference, self).__init__(field_name, model_name, window, + attrs=attrs) self.widget = gtk.HBox(spacing=0) @@ -81,9 +80,9 @@ class Reference(WidgetInterface): if not isinstance(selection, (list, tuple)): try: selection = rpc.execute('model', - self.model, selection, rpc.CONTEXT) + self.model_name, selection, rpc.CONTEXT) except Exception, exception: - common.process_exception(exception, self._window) + common.process_exception(exception, self.window) selection = [] selection.sort(lambda x, y: cmp(x[1], y[1])) self.set_popdown(selection) @@ -131,7 +130,7 @@ class Reference(WidgetInterface): def _color_widget(self): return self.wid_text - def set_value(self, model, model_field): + def set_value(self, record, field): return def sig_activate(self, widget=None): @@ -142,7 +141,7 @@ class Reference(WidgetInterface): return child = self.widget_combo.get_child() self.changed = False - value = self._view.modelfield.get_client(self._view.model) + value = self.field.get_client(self.record) self.focus_out = False if not value: @@ -155,17 +154,23 @@ class Reference(WidgetInterface): return False if model and obj_id: if not leave: - dia = Dialog(model, obj_id, attrs=self.attrs, - window=self._window) - res, value = dia.run() - if res: - self._view.modelfield.set_client(self._view.model, - (model, value), force_change=True) - dia.destroy() + screen = Screen(model, self.window, view_type=['form']) + screen.load([obj_id]) + win = WinForm(screen, self.window) + while win.run(): + if screen.save_current(): + value = (screen.current_record.id, + screen.current_record.rec_name()) + self.field(self.record, (model, value), + force_change=True) + break + else: + screen.display() + win.destroy() elif model: if not self._readonly and ( self.wid_text.get_text() or not leave): - domain = self._view.modelfield.domain_get(self._view.model) - context = self._view.modelfield.context_get(self._view.model) + domain = self.field.domain_get(self.record) + context = self.field.context_get(self.record) try: if self.wid_text.get_text(): @@ -179,40 +184,41 @@ class Reference(WidgetInterface): except Exception, exception: self.focus_out = True self.changed = True - common.process_exception(exception, self._window) + common.process_exception(exception, self.window) return False if ids and len(ids) == 1: - self._view.modelfield.set_client(self._view.model, - (model, (ids[0], ''))) - self.display(self._view.model, self._view.modelfield) + self.field.set_client(self.record, (model, (ids[0], ''))) + self.display(self.record, self.field) self.focus_out = True self.changed = True return True win = WinSearch(model, sel_multi=False, ids=ids, context=context, - domain=domain, parent=self._window) + domain=domain, parent=self.window) ids = win.run() if ids: - self._view.modelfield.set_client(self._view.model, - (model, (ids[0], ''))) + self.field.set_client(self.record, (model, (ids[0], ''))) else: - self._view.modelfield.set_client(self._view.model, - ('', (name, name))) + self.field.set_client(self.record, ('', (name, name))) self.focus_out = True self.changed = True - self.display(self._view.model, self._view.modelfield) + self.display(self.record, self.field) def sig_new(self, *args): model = self.get_model() if not model: return - dia = Dialog(model, window=self._window) - res, value = dia.run() - if res: - self._view.modelfield.set_client(self._view.model, - (model, value)) - self.display(self._view.model, self._view.modelfield) - dia.destroy() + screen = Screen(model, self.window, view_type=['form']) + win = WinForm(screen, self.window, new=True) + while win.run(): + if screen.save_current(): + value = (screen.current_record.id, + screen.current_record.rec_name()) + self.field.set_client(self.record, (model, value)) + break + else: + screen.display() + win.destroy() def sig_key_press(self, widget, event): editable = self.wid_text.get_editable() @@ -223,7 +229,7 @@ class Reference(WidgetInterface): self.sig_focus_out(widget, event) return True elif event.keyval in (gtk.keysyms.Tab, gtk.keysyms.Return) and editable: - if self._view.modelfield.get(self._view.model) or \ + if self.field.get(self.record) or \ not self.wid_text.get_text(): return False self.sig_focus_out(widget, event, leave=True) @@ -234,32 +240,30 @@ class Reference(WidgetInterface): if not self.changed: return self.wid_text.set_text('') - self._view.modelfield.set_client(self._view.model, - (self.get_model(), (0, ''))) + self.field.set_client(self.record, (self.get_model(), (0, ''))) def sig_changed(self, *args): if not self.changed: return False - val = self._view.modelfield.get_client(self._view.model) + val = self.field.get_client(self.record) if not val: model, (obj_id, name) = '', (0, '') else: model, (obj_id, name) = val if self.get_model() and obj_id: - self._view.modelfield.set_client(self._view.model, - (self.get_model(), (0, ''))) - self.display(self._view.model, self._view.modelfield) + self.field.set_client(self.record, (self.get_model(), (0, ''))) + self.display(self.record, self.field) return False - def display(self, model, model_field): + def display(self, record, field): child = self.widget_combo.get_child() self.changed = False - if not model_field: + if not field: child.set_text('') self.changed =True return False - super(Reference, self).display(model, model_field) - value = model_field.get_client(model) + super(Reference, self).display(record, field) + value = field.get_client(record) img = gtk.Image() if not value: model, (obj_id, name) = '', (0, '') @@ -268,12 +272,15 @@ class Reference(WidgetInterface): if model: child.set_text(self._selection2[model]) if not name and obj_id: + args = ('model', model, 'read', obj_id, ['rec_name'], + rpc.CONTEXT) try: - name = RPCProxy(model).read(obj_id, ['rec_name'], - rpc.CONTEXT)['rec_name'] + name = rpc.execute(*args) except Exception, exception: - common.process_exception(exception, self._window) - name = '???' + name = common.process_exception(exception, self.window, + *args) + if not name: + name = '???' self.wid_text.set_text(name) if obj_id: img.set_from_stock('tryton-open', gtk.ICON_SIZE_SMALL_TOOLBAR) diff --git a/tryton/gui/window/view_form/view/form_gtk/selection.py b/tryton/gui/window/view_form/view/form_gtk/selection.py index fa9e29f679..083ad054ef 100644 --- a/tryton/gui/window/view_form/view/form_gtk/selection.py +++ b/tryton/gui/window/view_form/view/form_gtk/selection.py @@ -9,8 +9,9 @@ import tryton.common as common class Selection(WidgetInterface): - def __init__(self, window, parent, model, attrs=None): - super(Selection, self).__init__(window, parent, model, attrs) + def __init__(self, field_name, model_name, window, attrs=None): + super(Selection, self).__init__(field_name, model_name, window, + attrs=attrs) self.widget = gtk.HBox(spacing=3) self.entry = gtk.ComboBoxEntry() @@ -40,16 +41,16 @@ class Selection(WidgetInterface): ['rec_name']) selection = [(x['id'], x['rec_name']) for x in result] except Exception, exception: - common.process_exception(exception, self._window) + common.process_exception(exception, self.window) selection = [] self.selection = selection[:] else: if not isinstance(selection, (list, tuple)): try: selection = rpc.execute('model', - self.model, selection, rpc.CONTEXT) + self.model_name, selection, rpc.CONTEXT) except Exception, exception: - common.process_exception(exception, self._window) + common.process_exception(exception, self.window) selection = [] self.selection = selection[:] @@ -123,24 +124,24 @@ class Selection(WidgetInterface): value = val if len(txt) == len(text): break - self._view.modelfield.set_client(self._view.model, value, force_change=True) - self.display(self._view.model, self._view.modelfield) + self.field.set_client(self.record, value, force_change=True) + self.display(self.record, self.field) - def set_value(self, model, model_field): - model_field.set_client(model, self.value_get()) + def set_value(self, record, field): + field.set_client(record, self.value_get()) def _menu_sig_default_set(self, reset=False): - self.set_value(self._view.model, self._view.modelfield) + self.set_value(self.record, self.field) super(Selection, self)._menu_sig_default_set(reset=reset) - def display(self, model, model_field): + def display(self, record, field): child = self.entry.get_child() self.changed = False - if not model_field: + if not field: child.set_text('') return False - super(Selection, self).display(model, model_field) - value = model_field.get(model) + super(Selection, self).display(record, field) + value = field.get(record) if isinstance(value, (list, tuple)): value = value[0] if not value: diff --git a/tryton/gui/window/view_form/view/form_gtk/textbox.py b/tryton/gui/window/view_form/view/form_gtk/textbox.py index d7d7812fcf..f580699a83 100644 --- a/tryton/gui/window/view_form/view/form_gtk/textbox.py +++ b/tryton/gui/window/view_form/view/form_gtk/textbox.py @@ -13,8 +13,9 @@ except: class TextBox(WidgetInterface): - def __init__(self, window, parent, model, attrs=None): - super(TextBox, self).__init__(window, parent, model, attrs) + def __init__(self, field_name, model_name, window, attrs=None): + super(TextBox, self).__init__(field_name, model_name, window, + attrs=attrs) self.widget = gtk.HBox() self.scrolledwindow = gtk.ScrolledWindow() @@ -55,8 +56,8 @@ class TextBox(WidgetInterface): if not value and self.attrs.get('spell') \ and CONFIG['client.spellcheck'] \ - and self._view and self._view.model: - language = self._view.model.expr_eval(self.attrs['spell']) + and self.record: + language = self.record.expr_eval(self.attrs['spell']) try: if not spell: spell = gtkspell.Spell(self.textview) @@ -76,16 +77,16 @@ class TextBox(WidgetInterface): def _color_widget(self): return self.textview - def set_value(self, model, model_field): + def set_value(self, record, field): buf = self.textview.get_buffer() iter_start = buf.get_start_iter() iter_end = buf.get_end_iter() current_text = buf.get_text(iter_start, iter_end, False) - model_field.set_client(model, current_text or False) + field.set_client(record, current_text or False) - def display(self, model, model_field): - super(TextBox, self).display(model, model_field) - value = model_field and model_field.get(model) + def display(self, record, field): + super(TextBox, self).display(record, field) + value = field and field.get(record) if not value: value = '' buf = self.textview.get_buffer() @@ -101,8 +102,8 @@ class TextBox(WidgetInterface): pass if self.attrs.get('spell') and CONFIG['client.spellcheck'] \ - and self._view and self._view.model: - language = self._view.model.expr_eval(self.attrs['spell']) + and self.record: + language = self.record.expr_eval(self.attrs['spell']) try: if not spell: spell = gtkspell.Spell(self.textview) @@ -120,7 +121,7 @@ class TextBox(WidgetInterface): del spell def display_value(self): - lines = (self._view.modelfield.get_client(self._view.model) or '').split('\n') + lines = (self.field.get_client(self.record) or '').split('\n') if len(lines) > 1: return lines[0] + '...' else: diff --git a/tryton/gui/window/view_form/view/form_gtk/url.py b/tryton/gui/window/view_form/view/form_gtk/url.py index 39bd64568d..9844ab0f06 100644 --- a/tryton/gui/window/view_form/view/form_gtk/url.py +++ b/tryton/gui/window/view_form/view/form_gtk/url.py @@ -10,10 +10,8 @@ import tryton.common as common class URL(Char): "url" - def __init__(self, window, parent, model, attrs=None): - if attrs is None: - attrs = {} - super(URL, self).__init__(window, parent, model, attrs=attrs) + def __init__(self, field_name, model_name, window, attrs=None): + super(URL, self).__init__(field_name, model_name, window, attrs=attrs) self.tooltips = common.Tooltips() self.button = gtk.Button() @@ -26,8 +24,8 @@ class URL(Char): self.widget.pack_start(self.button, expand=False, fill=False) self.widget.set_focus_chain([self.entry]) - def display(self, model, model_field): - super(URL, self).display(model, model_field) + def display(self, record, field): + super(URL, self).display(record, field) self.set_tooltips() def set_tooltips(self): diff --git a/tryton/gui/window/view_form/view/graph.py b/tryton/gui/window/view_form/view/graph.py index f651713274..310ed59fe9 100644 --- a/tryton/gui/window/view_form/view/graph.py +++ b/tryton/gui/window/view_form/view/graph.py @@ -10,7 +10,6 @@ class ViewGraph(ParserView): super(ViewGraph, self).__init__(window, screen, widget, children, buttons, toolbar, notebooks, cursor_widget) self.view_type = 'graph' - self.model_add_new = False self.widgets = children def __getitem__(self, name): @@ -37,9 +36,6 @@ class ViewGraph(ParserView): def reset(self): pass - def signal_record_changed(self, *args): - self.display() - def display(self): for widget in self.widgets.itervalues(): widget.display(self.screen.models) diff --git a/tryton/gui/window/view_form/view/graph_gtk/graph.py b/tryton/gui/window/view_form/view/graph_gtk/graph.py index 53bf920ce5..3cc47ca79e 100644 --- a/tryton/gui/window/view_form/view/graph_gtk/graph.py +++ b/tryton/gui/window/view_form/view/graph_gtk/graph.py @@ -354,7 +354,7 @@ class Graph(gtk.DrawingArea): if yfield.get('domain'): values = rpc.CONTEXT.copy() values['state'] = 'draft' - for field in model.mgroup.fields: + for field in model.group.fields: values[field] = model[field].get(model, check_load=False) if not PYSONDecoder(values).decode(yfield['domain']): continue diff --git a/tryton/gui/window/view_form/view/list.py b/tryton/gui/window/view_form/view/list.py index 53736ddd79..408ded5ff4 100644 --- a/tryton/gui/window/view_form/view/list.py +++ b/tryton/gui/window/view_form/view/list.py @@ -2,7 +2,6 @@ #this repository contains the full copyright notices and license terms. import gobject import gtk -from tryton.rpc import RPCProxy import tryton.rpc as rpc import locale from interface import ParserView @@ -13,6 +12,7 @@ import tryton.common as common from tryton.config import CONFIG from tryton.common.cellrendererbutton import CellRendererButton from tryton.common.cellrenderertoggle import CellRendererToggle +from tryton.pyson import PYSONEncoder import os _ = gettext.gettext @@ -20,17 +20,16 @@ _ = gettext.gettext class AdaptModelGroup(gtk.GenericTreeModel): - def __init__(self, model_group): + def __init__(self, group): super(AdaptModelGroup, self).__init__() - self.model_group = model_group - self.models = model_group.models + self.group = group self.last_sort = None self.sort_asc = True self.set_property('leak_references', False) def added(self, modellist, position): - if modellist is self.models: - model = self.models[position] + if modellist is self.group: + model = self.group[position] self.row_inserted(self.on_get_path(model), self.get_iter(self.on_get_path(model))) @@ -42,26 +41,26 @@ class AdaptModelGroup(gtk.GenericTreeModel): self.invalidate_iters() def append(self, model): - self.model_group.model_add(model) + self.group.add(model) def prepend(self, model): - self.model_group.model_add(model, 0) + self.group.add(model, 0) def remove(self, iter): idx = self.get_path(iter)[0] - self.model_group.model_remove(self.models[idx]) + self.group.remove(self.group[idx]) self.invalidate_iters() def move(self, path, position): idx = path[0] - self.model_group.model_move(self.models[idx], position) + self.group.move(self.group[idx], position) def sort(self, ids): ids2pos = {} pos = 0 new_order = [] - for model in self.models: - ids2pos[model.id] = pos + for record in self.group: + ids2pos[record.id] = pos new_order.append(pos) pos += 1 pos = 0 @@ -73,19 +72,19 @@ class AdaptModelGroup(gtk.GenericTreeModel): pos += 1 except: continue - self.models.sort(lambda x, y: \ + self.group.sort(lambda x, y: \ cmp(new_order[ids2pos[x.id]], new_order[ids2pos[y.id]])) prev = None - for model in self.models: + for record in self.group: if prev: - prev.next[id(self.models)] = model - prev = model + prev.next[id(self.group)] = record + prev = record if prev: - prev.next[id(self.models)] = None + prev.next[id(self.group)] = None self.rows_reordered(None, None, new_order) def __len__(self): - return len(self.models) + return len(self.group) def on_get_flags(self): return gtk.TREE_MODEL_LIST_ONLY @@ -97,17 +96,17 @@ class AdaptModelGroup(gtk.GenericTreeModel): return gobject.TYPE_PYOBJECT def on_get_path(self, iter): - if iter in self.models: - return self.models.index(iter) + if iter in self.group: + return self.group.index(iter) else: return 0 def on_get_iter(self, path): if isinstance(path, tuple): path = path[0] - if self.models: - if path < len(self.models): - return self.models[path] + if self.group is not None: + if path < len(self.group): + return self.group[path] else: return None else: @@ -119,7 +118,7 @@ class AdaptModelGroup(gtk.GenericTreeModel): def on_iter_next(self, node): try: - return node.next[id(self.models)] + return node.next[id(self.group)] except IndexError: return None @@ -131,11 +130,11 @@ class AdaptModelGroup(gtk.GenericTreeModel): def on_iter_n_children(self, node): if node is None: - return len(self.models) + return len(self.group) return 0 def on_iter_nth_child(self, node, nth): - if node is None and self.models: + if node is None and self.group is not None: return self.on_get_iter(0) return None @@ -151,7 +150,6 @@ class ViewList(ParserView): buttons, toolbar, notebooks, cursor_widget) self.store = None self.view_type = 'tree' - self.model_add_new = True self.widget = gtk.VBox() self.widget_tree = widget scroll = gtk.ScrolledWindow() @@ -272,13 +270,13 @@ class ViewList(ParserView): return False email = {} if action.get('email'): - email = self.screen.current_model.expr_eval(action['email']) + email = self.screen.current_record.expr_eval(action['email']) if not email: email = {} email['subject'] = action['name'].replace('_', '') act['email'] = email data = { - 'model': self.screen.name, + 'model': self.screen.model_name, 'id': obj_id, 'ids': obj_ids, } @@ -327,13 +325,13 @@ class ViewList(ParserView): self.copy_clear_func, selection) def copy_foreach(self, treemodel, path,iter, data): - model = treemodel.get_value(iter, 0) + record = treemodel.get_value(iter, 0) values = [] for col in self.widget_tree.get_columns(): if not col.get_visible() or not col.name: continue cell = self.widget_tree.cells[col.name] - values.append('"' + str(cell.get_textual_value(model)) + '"') + values.append('"' + str(cell.get_textual_value(record)) + '"') data.append('\t'.join(values)) return @@ -370,9 +368,9 @@ class ViewList(ParserView): info, etime): treeview.emit_stop_by_name('drag-data-received') if treeview.sequence: - for model in self.screen.models.models: - if model[treeview.sequence].get_state_attrs( - model).get('readonly', False): + for record in self.screen.group: + if record[treeview.sequence].get_state_attrs( + record).get('readonly', False): return if not selection.data: return @@ -389,7 +387,7 @@ class ViewList(ParserView): model.move(data, idx + 1) context.drop_finish(False, etime) if treeview.sequence: - self.screen.models.set_sequence(field=treeview.sequence) + self.screen.group.set_sequence(field=treeview.sequence) def drag_data_delete(self, treeview, context): treeview.emit_stop_by_name('drag-data-delete') @@ -404,18 +402,21 @@ class ViewList(ParserView): model = selection.get_selected_rows()[0] if (not path) or not path[0]: return False - model = model.models[path[0][0]] + record = model.group[path[0][0]] if hasattr(path[1], '_type') and path[1]._type == 'many2one': - value = model[path[1].name].get(model) - ir_action_keyword = RPCProxy('ir.action.keyword') + value = record[path[1].name].get(record) + args = ('model', 'ir.action.keyword', 'get_keyword', + 'form_relate', + (self.screen.fields[path[1].name]['relation'], 0), + rpc.CONTEXT) try: - relates = ir_action_keyword.get_keyword('form_relate', - (self.screen.fields[path[1].name]['relation'], 0), - rpc.CONTEXT) + relates = rpc.execute(*args) except Exception, exception: - common.process_exception(exception, self.window) - return False + relates = common.process_exception(exception, self.window, + *args) + if not relates: + return False menu_entries = [] menu_entries.append((None, None, None)) menu_entries.append((_('Actions'), @@ -457,11 +458,12 @@ class ViewList(ParserView): screen = Screen(self.screen.fields[path[1].name]['relation'], self.window) screen.load([value]) - act['domain'] = str(screen.current_model.expr_eval(act.get('domain', []), - check_load=False)) - act['context'] = str(screen.current_model.expr_eval(act.get('context', {}), - check_load=False)) - data['model'] = self.screen.name + encoder = PYSONEncoder() + act['domain'] = encoder.encode(screen.current_record.expr_eval( + act.get('domain', []), check_load=False)) + act['context'] = encoder.encode(screen.current_record.expr_eval( + act.get('context', {}), check_load=False)) + data['model'] = self.screen.model_name data['id'] = value data['ids'] = [value] return Action._exec_action(act, self.window, data, context) @@ -473,19 +475,19 @@ class ViewList(ParserView): 'ids': [value], }, alwaysask=True) - def signal_record_changed(self, signal, *args): - if self.store: - if signal == 'record-added': - self.store.added(*args) - elif signal == 'record-removed': - self.store.removed(*args) + def group_list_changed(self, group, signal): + if self.store is not None: + if signal[0] == 'record-added': + self.store.added(group, signal[1]) + elif signal[0] == 'record-removed': + self.store.removed(group, signal[1]) self.update_children() def cancel(self): pass def __str__(self): - return 'ViewList (%s)' % self.screen.resource + return 'ViewList (%s)' % self.screen.model_name def __getitem__(self, name): return None @@ -523,26 +525,26 @@ class ViewList(ParserView): self.screen.row_activate() def __select_changed(self, tree_sel): - previous_model = self.screen.current_model + previous_record = self.screen.current_record if tree_sel.get_mode() == gtk.SELECTION_SINGLE: model, iter = tree_sel.get_selected() if iter: path = model.get_path(iter)[0] - self.screen.current_model = model.models[path] + self.screen.current_record = model.group[path] elif tree_sel.get_mode() == gtk.SELECTION_MULTIPLE: model, paths = tree_sel.get_selected_rows() if paths: - self.screen.current_model = model.models[paths[0][0]] + self.screen.current_record = model.group[paths[0][0]] if hasattr(self.widget_tree, 'editable') \ and self.widget_tree.editable \ - and self.screen.tree_saves \ - and previous_model != self.screen.current_model: - if previous_model and \ - not (previous_model.validate() and previous_model.save()): - self.screen.current_model = previous_model + and not self.screen.parent \ + and previous_record != self.screen.current_record: + if previous_record and \ + not (previous_record.validate() and previous_record.save()): + self.screen.current_record = previous_record self.set_cursor() return True self.update_children() @@ -561,12 +563,12 @@ class ViewList(ParserView): def display(self): if self.reload \ or (not self.widget_tree.get_model()) \ - or self.screen.models != \ - self.widget_tree.get_model().model_group: - self.store = AdaptModelGroup(self.screen.models) + or self.screen.group != \ + self.widget_tree.get_model().group: + self.store = AdaptModelGroup(self.screen.group) self.widget_tree.set_model(self.store) self.reload = False - if not self.screen.current_model: + if not self.screen.current_record: # Should find a simpler solution to do something like #self.widget.set_cursor(None,None,False) if self.store: @@ -578,12 +580,12 @@ class ViewList(ParserView): self.update_children() def set_state(self): - model = self.screen.current_model - if model: - for field in model.mgroup.fields: - modelfield = model.mgroup.mfields.get(field, None) - if modelfield: - modelfield.state_set(model) + record = self.screen.current_record + if record: + for field in record.group.fields: + field = record.group.fields.get(field, None) + if field: + field.state_set(record) def update_children(self): ids = self.sel_ids_get() @@ -591,23 +593,23 @@ class ViewList(ParserView): value = 0.0 value_selected = 0.0 loaded = True - for model in self.screen.models.models: - if not model.loaded: + for record in self.screen.group: + if not record.loaded: loaded = False break - if model.id in ids or not ids: + if record.id in ids or not ids: if not value_selected: - value_selected = model.fields_get()[self.children[child][0]]\ - .get(model, check_load=False) + value_selected = record.fields_get()[self.children[child][0]]\ + .get(record, check_load=False) else: - value_selected += model.fields_get()[self.children[child][0]]\ - .get(model, check_load=False) + value_selected += record.fields_get()[self.children[child][0]]\ + .get(record, check_load=False) if not value: - value = model.fields_get()[self.children[child][0]]\ - .get(model, check_load=False) + value = record.fields_get()[self.children[child][0]]\ + .get(record, check_load=False) else: - value += model.fields_get()[self.children[child][0]]\ - .get(model, check_load=False) + value += record.fields_get()[self.children[child][0]]\ + .get(record, check_load=False) if loaded: label_str = locale.format('%.' + str(self.children[child][3]) + 'f', @@ -624,8 +626,8 @@ class ViewList(ParserView): def set_cursor(self, new=False, reset_view=True): self.widget_tree.grab_focus() - if self.screen.current_model: - path = self.store.on_get_path(self.screen.current_model) + if self.screen.current_record: + path = self.store.on_get_path(self.screen.current_record) focus_column = None for column in self.widget_tree.get_columns(): renderers = column.get_cell_renderers() @@ -647,21 +649,21 @@ class ViewList(ParserView): def sel_ids_get(self): def _func_sel_get(store, path, iter, ids): - model = store.on_get_iter(path) - if model and model.id > 0: - ids.append(model.id) + record = store.on_get_iter(path) + if record and record.id > 0: + ids.append(record.id) ids = [] sel = self.widget_tree.get_selection() sel.selected_foreach(_func_sel_get, ids) return ids - def sel_models_get(self): - def _func_sel_get(store, path, iter, models): - models.append(store.on_get_iter(path)) - models = [] + def selected_records(self): + def _func_sel_get(store, path, iter, records): + records.append(store.on_get_iter(path)) + records = [] sel = self.widget_tree.get_selection() - sel.selected_foreach(_func_sel_get, models) - return models + sel.selected_foreach(_func_sel_get, records) + return records def unset_editable(self): self.widget_tree.editable = False diff --git a/tryton/gui/window/view_form/view/list_gtk/editabletree.py b/tryton/gui/window/view_form/view/list_gtk/editabletree.py index eb4c98baa5..b7f13cf3e7 100644 --- a/tryton/gui/window/view_form/view/list_gtk/editabletree.py +++ b/tryton/gui/window/view_form/view/list_gtk/editabletree.py @@ -10,9 +10,9 @@ _ = gettext.gettext class EditableTreeView(gtk.TreeView): - leaving_model_events = (gtk.keysyms.Up, gtk.keysyms.Down, + leaving_record_events = (gtk.keysyms.Up, gtk.keysyms.Down, gtk.keysyms.Return) - leaving_events = leaving_model_events + (gtk.keysyms.Tab, + leaving_events = leaving_record_events + (gtk.keysyms.Tab, gtk.keysyms.ISO_Left_Tab, gtk.keysyms.KP_Enter) def __init__(self, position): @@ -20,38 +20,38 @@ class EditableTreeView(gtk.TreeView): self.editable = position self.cells = {} - def on_quit_cell(self, current_model, fieldname, value): - modelfield = current_model[fieldname] - if hasattr(modelfield, 'editabletree_entry'): - del modelfield.editabletree_entry + def on_quit_cell(self, current_record, fieldname, value): + field = current_record[fieldname] + if hasattr(field, 'editabletree_entry'): + del field.editabletree_entry cell = self.cells[fieldname] # The value has not changed and is valid ... do nothing. - if value == cell.get_textual_value(current_model) \ - and modelfield.validate(current_model): + if value == cell.get_textual_value(current_record) \ + and field.validate(current_record): return try: - real_value = cell.value_from_text(current_model, value) - modelfield.set_client(current_model, real_value) + real_value = cell.value_from_text(current_record, value) + field.set_client(current_record, real_value) except parser.UnsettableColumn: return - def on_open_remote(self, current_model, fieldname, create, value): - modelfield = current_model[fieldname] + def on_open_remote(self, current_record, fieldname, create, value): + field = current_record[fieldname] cell = self.cells[fieldname] - if value != cell.get_textual_value(current_model) or not value: + if value != cell.get_textual_value(current_record) or not value: changed = True else: changed = False try: - valid, value = cell.open_remote(current_model, create, + valid, value = cell.open_remote(current_record, create, changed, value) if valid: - modelfield.set_client(current_model, value) + field.set_client(current_record, value) except NotImplementedError: pass - return cell.get_textual_value(current_model) + return cell.get_textual_value(current_record) def on_create_line(self): model = self.get_model() @@ -60,12 +60,9 @@ class EditableTreeView(gtk.TreeView): else: method = model.append ctx = self.screen.context.copy() - if self.screen.current_model and self.screen.current_model.parent: - ctx.update(self.screen.current_model.parent.expr_eval( - self.screen.default_get)) - new_model = model.model_group.model_new(domain=self.screen.domain, + new_record = model.group.new(domain=self.screen.domain, context=ctx) - res = method(new_model) + res = method(new_record) return res def __next_column(self, col): @@ -115,24 +112,24 @@ class EditableTreeView(gtk.TreeView): def set_value(self): path, column = self.get_cursor() - store = self.get_model() + model = self.get_model() if not path or not column: return True - model = store.get_value(store.get_iter(path), 0) - modelfield = model[column.name] - if hasattr(modelfield, 'editabletree_entry'): - entry = modelfield.editabletree_entry + record = model.get_value(model.get_iter(path), 0) + field = record[column.name] + if hasattr(field, 'editabletree_entry'): + entry = field.editabletree_entry if isinstance(entry, gtk.Entry): txt = entry.get_text() else: txt = entry.get_active_text() - self.on_quit_cell(model, column.name, txt) + self.on_quit_cell(record, column.name, txt) return True def on_keypressed(self, entry, event): path, column = self.get_cursor() - store = self.get_model() - model = store.get_value(store.get_iter(path), 0) + model = self.get_model() + record = model.get_value(model.get_iter(path), 0) leaving = False if event.keyval == gtk.keysyms.Right: @@ -157,24 +154,20 @@ class EditableTreeView(gtk.TreeView): else: txt = entry.get_active_text() entry.disconnect(entry.editing_done_id) - self.on_quit_cell(model, column.name, txt) + self.on_quit_cell(record, column.name, txt) entry.editing_done_id = entry.connect('editing_done', self.on_editing_done) - if event.keyval in self.leaving_model_events: - if not model.validate(): - invalid_fields = model.invalid_fields + if event.keyval in self.leaving_record_events: + if not record.validate(): + invalid_fields = record.invalid_fields col = None for col in self.get_columns(): if col.name in invalid_fields: break self.set_cursor(path, col, True) - if self.screen.form: - self.screen.form.message_info( - _('Warning; field "%s" is required!') % \ - invalid_fields[col.name]) return True - if self.screen.tree_saves: - obj_id = model.save() + if not self.screen.parent: + obj_id = record.save() if not obj_id: return True if event.keyval in (gtk.keysyms.Tab, gtk.keysyms.KP_Enter) \ @@ -187,12 +180,12 @@ class EditableTreeView(gtk.TreeView): self.set_cursor(path, new_col, True) elif event.keyval == gtk.keysyms.Up: entry.disconnect(entry.editing_done_id) - self._key_up(path, store, column) + self._key_up(path, model, column) entry.editing_done_id = entry.connect('editing_done', self.on_editing_done) elif event.keyval == gtk.keysyms.Down: entry.disconnect(entry.editing_done_id) - self._key_down(path, store, column) + self._key_down(path, model, column) entry.editing_done_id = entry.connect('editing_done', self.on_editing_done) elif event.keyval in (gtk.keysyms.Return,): @@ -211,22 +204,22 @@ class EditableTreeView(gtk.TreeView): break entry.disconnect(entry.editing_done_id) if self.editable == 'top': - new_path = self._key_up(path, store, col) + new_path = self._key_up(path, model, col) else: - new_path = self._key_down(path, store, column) + new_path = self._key_down(path, model, column) entry.editing_done_id = entry.connect('editing_done', self.on_editing_done) elif event.keyval == gtk.keysyms.Escape: - if model.id < 0: - store.remove(store.get_iter(path)) - self.screen.current_model = False + if record.id < 0: + model.remove(model.get_iter(path)) + self.screen.current_record = False if not path[0]: - self.screen.current_model = False - if path[0] == len(self.screen.models.models) \ + self.screen.current_record = False + if path[0] == len(self.screen.group) \ and path[0]: path = (path[0] - 1,) self.screen.display() - if len(self.screen.models.models): + if len(self.screen.group): self.set_cursor(path, column, False) elif event.keyval in (gtk.keysyms.F3, gtk.keysyms.F2): if isinstance(entry, gtk.Entry): @@ -234,9 +227,8 @@ class EditableTreeView(gtk.TreeView): else: value = entry.get_active_text() entry.disconnect(entry.editing_done_id) - newval = self.on_open_remote(model, column.name, - create=(event.keyval==gtk.keysyms.F3), - value=value) + newval = self.on_open_remote(record, column.name, + create=(event.keyval==gtk.keysyms.F3), value=value) if isinstance(entry, gtk.Entry): entry.set_text(newval) else: @@ -245,31 +237,31 @@ class EditableTreeView(gtk.TreeView): self.on_editing_done) self.set_cursor(path, column, True) else: - modelfield = model[column.name] + field = record[column.name] if isinstance(entry, gtk.Entry): - entry.set_max_length(int(modelfield.attrs.get('size', 0))) - # store in the model the entry widget to get the value in set_value - modelfield.editabletree_entry = entry - model.modified = True - model.modified_fields.setdefault(column.name) + entry.set_max_length(int(field.attrs.get('size', 0))) + # store in the record the entry widget to get the value in set_value + field.editabletree_entry = entry + record.modified = True + record.modified_fields.setdefault(column.name) return False return True - def _key_down(self, path, store, column): - if path[0] == len(store) - 1 and self.editable == 'bottom': + def _key_down(self, path, model, column): + if path[0] == len(model) - 1 and self.editable == 'bottom': self.on_create_line() - new_path = (path[0] + 1) % len(store) + new_path = (path[0] + 1) % len(model) self.set_cursor(new_path, column, True) self.scroll_to_cell(new_path) return new_path - def _key_up(self, path, store, column): + def _key_up(self, path, model, column): if path[0] == 0 and self.editable == 'top': self.on_create_line() new_path = 0 else: - new_path = (path[0] - 1) % len(store) + new_path = (path[0] - 1) % len(model) self.set_cursor(new_path, column, True) self.scroll_to_cell(new_path) return new_path @@ -278,9 +270,9 @@ class EditableTreeView(gtk.TreeView): path, column = self.get_cursor() if not path: return True - store = self.get_model() - model = store.get_value(store.get_iter(path), 0) + model = self.get_model() + record = model.get_value(model.get_iter(path), 0) if isinstance(entry, gtk.Entry): - self.on_quit_cell(model, column.name, entry.get_text()) + self.on_quit_cell(record, column.name, entry.get_text()) elif isinstance(entry, gtk.ComboBoxEntry): - self.on_quit_cell(model, column.name, entry.get_active_text()) + self.on_quit_cell(record, column.name, entry.get_active_text()) diff --git a/tryton/gui/window/view_form/view/list_gtk/parser.py b/tryton/gui/window/view_form/view/list_gtk/parser.py index d30c6c8feb..a0fcc6ec67 100644 --- a/tryton/gui/window/view_form/view/list_gtk/parser.py +++ b/tryton/gui/window/view_form/view/list_gtk/parser.py @@ -1,23 +1,13 @@ #This file is part of Tryton. The COPYRIGHT file at the top level of #this repository contains the full copyright notices and license terms. -import locale -import gtk -from tryton.rpc import RPCProxy from editabletree import EditableTreeView from tryton.gui.window.view_form.view.interface import ParserInterface - -import time - -from tryton.gui.window.view_form.view.form_gtk.many2one import Dialog \ - as M2ODialog -from tryton.gui.window.view_form.view.form_gtk.one2many import Dialog \ - as O2MDialog from tryton.gui.window.win_search import WinSearch +from tryton.gui.window.win_form import WinForm +from tryton.gui.window.view_form.screen import Screen from tryton.gui.window.view_form.widget_search.form import _LIMIT - import tryton.rpc as rpc -import datetime from tryton.common import DT_FORMAT, DHM_FORMAT, COLORS, node_attributes, \ TRYTON_ICON, HM_FORMAT import tryton.common as common @@ -31,6 +21,10 @@ from tryton.common.cellrendererfloat import CellRendererFloat from tryton.action import Action from tryton.translate import date_format from tryton.pyson import PYSONDecoder +import gtk +import locale +import datetime +import time import gettext _ = gettext.gettext @@ -60,10 +54,10 @@ def sort_model(column, treeview, screen): else: column.arrow_show = False column.arrow.hide() - model = treeview.get_model() - if screen.search_count == len(model): + store = treeview.get_model() + if screen.search_count == len(store): ids = screen.search_filter(only_ids=True) - model.sort(ids) + store.sort(ids) else: screen.search_filter() @@ -73,7 +67,7 @@ class ParserTree(ParserInterface): super(ParserTree, self).__init__(window, parent, attrs, screen) self.treeview = None - def parse(self, model, root_node, fields): + def parse(self, model_name, root_node, fields): dict_widget = {} button_list = [] attrs = node_attributes(root_node) @@ -102,11 +96,11 @@ class ParserTree(ParserInterface): bool(int(node_attrs[boolean_fields])) if fname not in fields: continue - fields[fname].update(node_attrs) - node_attrs.update(fields[fname]) + fields[fname].attrs.update(node_attrs) + node_attrs.update(fields[fname].attrs) cell = CELLTYPES.get(node_attrs.get('widget', - fields[fname]['type']))(fname, model, - treeview, node_attrs, self.window) + fields[fname].attrs['type']))(fname, model_name, + treeview, self.window, node_attrs) treeview.cells[fname] = cell renderer = cell.renderer @@ -122,15 +116,15 @@ class ParserTree(ParserInterface): renderer.connect_after('editing-started', send_keys, treeview) - col = gtk.TreeViewColumn(fields[fname]['string'], renderer) + col = gtk.TreeViewColumn(fields[fname].attrs['string'], renderer) col.name = fname hbox = gtk.HBox(False, 2) - label = gtk.Label(fields[fname]['string']) + label = gtk.Label(fields[fname].attrs['string']) label.show() - help = fields[fname]['string'] - if fields[fname].get('help'): - help += '\n' + fields[fname]['help'] + help = fields[fname].attrs['string'] + if fields[fname].attrs.get('help'): + help += '\n' + fields[fname].attrs['help'] tooltips.set_tip(label, help) tooltips.enable() arrow = gtk.Arrow(gtk.ARROW_DOWN, gtk.SHADOW_IN) @@ -141,7 +135,7 @@ class ParserTree(ParserInterface): hbox.show() col.set_widget(hbox) - col._type = fields[fname]['type'] + col._type = fields[fname].attrs['type'] col.set_cell_data_func(renderer, cell.setter) col.set_clickable(True) twidth = { @@ -158,10 +152,10 @@ class ParserTree(ParserInterface): 'many2many': 50, 'boolean': 20, } - if 'width' in fields[fname]: - width = int(fields[fname]['width']) + if 'width' in fields[fname].attrs: + width = int(fields[fname].attrs['width']) else: - width = twidth.get(fields[fname]['type'], 100) + width = twidth.get(fields[fname].attrs['type'], 100) col.width = width if width > 0: col.set_fixed_width(width) @@ -171,30 +165,30 @@ class ParserTree(ParserInterface): col.connect('clicked', sort_model, treeview, self.screen) col.set_resizable(True) col.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED) - col.set_visible(not fields[fname].get('tree_invisible', False)) + col.set_visible(not fields[fname].attrs.get('tree_invisible', False)) i = treeview.append_column(col) - if 'sum' in fields[fname] and fields[fname]['type'] \ + if 'sum' in fields[fname].attrs and fields[fname].attrs['type'] \ in ('integer', 'biginteger', 'float', 'numeric', 'float_time'): label = gtk.Label() label.set_use_markup(True) - label_str = fields[fname]['sum'] + ': ' - label_bold = bool(int(fields[fname].get('sum_bold', 0))) + label_str = fields[fname].attrs['sum'] + ': ' + label_bold = bool(int(fields[fname].attrs.get('sum_bold', 0))) if label_bold: label.set_markup('%s' % label_str) else: label.set_markup(label_str) label_sum = gtk.Label() label_sum.set_use_markup(True) - if isinstance(fields[fname].get('digits'), str): + if isinstance(fields[fname].attrs.get('digits'), str): digits = 2 else: - digits = fields[fname].get('digits', (16, 2))[1] + digits = fields[fname].attrs.get('digits', (16, 2))[1] dict_widget[i] = (fname, label, label_sum, digits, label_bold) elif node.localName == 'button': #TODO add shortcut - cell = Button(treeview, node_attrs, self.window, self.screen) + cell = Button(treeview, self.window, self.screen, node_attrs) button_list.append(cell) renderer = cell.renderer string = node_attrs.get('string', _('Unknown')) @@ -239,24 +233,24 @@ class ParserTree(ParserInterface): class Char(object): - def __init__(self, field_name, model, treeview=None, attrs=None, - window=None): + def __init__(self, field_name, model_name, treeview, window, attrs=None): + super(Char, self).__init__() self.field_name = field_name - self.model = model + self.model_name = model_name self.attrs = attrs or {} self.renderer = CellRendererText() self.treeview = treeview self.window = window def setter(self, column, cell, store, iter): - model = store.get_value(iter, 0) - text = self.get_textual_value(model) + record = store.get_value(iter, 0) + text = self.get_textual_value(record) if isinstance(cell, CellRendererToggle): cell.set_active(bool(text)) else: cell.set_property('text', text) - fg_color = self.get_color(model) + fg_color = self.get_color(record) cell.set_property('foreground', fg_color) if fg_color == 'black': cell.set_property('foreground-set', False) @@ -274,22 +268,22 @@ class Char(object): and self.treeview.editable: states = ('readonly', 'required', 'invisible') - field = model[self.field_name] - field.state_set(model, states=states) - invisible = field.get_state_attrs(model).get('invisible', False) + field = record[self.field_name] + field.state_set(record, states=states) + invisible = field.get_state_attrs(record).get('invisible', False) cell.set_property('visible', not invisible) if hasattr(self.treeview, 'editable') \ and self.treeview.editable: - readonly = field.get_state_attrs(model).get('readonly', False) + readonly = field.get_state_attrs(record).get('readonly', False) if invisible: readonly = True if not isinstance(cell, CellRendererToggle): bg_color = 'white' - if not field.get_state_attrs(model).get('valid', True): + if not field.get_state_attrs(record).get('valid', True): bg_color = COLORS.get('invalid', 'white') - elif bool(int(field.get_state_attrs(model).get('required', 0))): + elif bool(int(field.get_state_attrs(record).get('required', 0))): bg_color = COLORS.get('required', 'white') cell.set_property('background', bg_color) if bg_color == 'white': @@ -308,50 +302,50 @@ class Char(object): cell.set_property('xalign', align) - def get_color(self, model): - return model.expr_eval(self.treeview.colors, check_load=False) + def get_color(self, record): + return record.expr_eval(self.treeview.colors, check_load=False) - def open_remote(self, model, create, changed=False, text=None): + def open_remote(self, record, create, changed=False, text=None): raise NotImplementedError - def get_textual_value(self, model): - return model[self.field_name].get_client(model) or '' + def get_textual_value(self, record): + return record[self.field_name].get_client(record) or '' - def value_from_text(self, model, text): + def value_from_text(self, record, text): return text class Int(Char): - def __init__(self, field_name, model, treeview=None, attrs=None, - window=None): - super(Int, self).__init__(field_name, model, treeview=treeview, - attrs=attrs, window=window) + def __init__(self, field_name, model_name, treeview, window, attrs=None): + super(Int, self).__init__(field_name, model_name, treeview, window, + attrs=attrs) self.renderer = CellRendererInteger() - def value_from_text(self, model, text): + def value_from_text(self, record, text): return int(text) - def get_textual_value(self, model): + def get_textual_value(self, record): return locale.format('%d', - model[self.field_name].get_client(model) or 0, True) + record[self.field_name].get_client(record) or 0, True) class Boolean(Int): - def __init__(self, *args): - super(Boolean, self).__init__(*args) + def __init__(self, field_name, model_name, treeview, window, attrs=None): + super(Boolean, self).__init__(field_name, model_name, treeview, window, + attrs=attrs) self.renderer = CellRendererToggle() self.renderer.connect('toggled', self._sig_toggled) - def get_textual_value(self, model): - return model[self.field_name].get_client(model) + def get_textual_value(self, record): + return record[self.field_name].get_client(record) def _sig_toggled(self, renderer, path): store = self.treeview.get_model() - model = store.get_value(store.get_iter(path), 0) - field = model[self.field_name] - if not field.get_state_attrs(model).get('readonly', False): - value = model[self.field_name].get_client(model) - model[self.field_name].set_client(model, int(not value)) + record = store.get_value(store.get_iter(path), 0) + field = record[self.field_name] + if not field.get_state_attrs(record).get('readonly', False): + value = record[self.field_name].get_client(record) + record[self.field_name].set_client(record, int(not value)) self.treeview.set_cursor(path) return True @@ -359,24 +353,20 @@ class Boolean(Int): class Date(Char): server_format = DT_FORMAT - def __init__(self, field_name, model, treeview=None, attrs=None, - window=None): + def __init__(self, field_name, model_name, treeview, window, attrs=None): + super(Date, self).__init__(field_name, model_name, treeview, window, + attrs=attrs) self.display_format = date_format() - self.field_name = field_name - self.model = model - self.attrs = attrs or {} self.renderer = CellRendererDate(self.display_format) - self.treeview = treeview - self.window = window - def get_textual_value(self, model): - value = model[self.field_name].get_client(model) + def get_textual_value(self, record): + value = record[self.field_name].get_client(record) if not value: return '' date = datetime.date(*time.strptime(value, self.server_format)[:3]) return common.datetime_strftime(date, self.display_format) - def value_from_text(self, model, text): + def value_from_text(self, record, text): if not text: return False try: @@ -389,14 +379,13 @@ class Date(Char): class Datetime(Date): server_format = DHM_FORMAT - def __init__(self, field_name, model, treeview=None, attrs=None, - window=None): - super(Datetime, self).__init__(field_name, model, - treeview=treeview, attrs=attrs, window=window) + def __init__(self, field_name, model_name, treeview, window, attrs=None): + super(Datetime, self).__init__(field_name, model_name, treeview, + window, attrs=attrs) self.display_format = date_format() + ' ' + HM_FORMAT - def get_textual_value(self, model): - value = model[self.field_name].get_client(model) + def get_textual_value(self, record): + value = record[self.field_name].get_client(record) if not value: return '' date = datetime.datetime(*time.strptime(value, @@ -413,7 +402,7 @@ class Datetime(Date): pass return common.datetime_strftime(date, self.display_format) - def value_from_text(self, model, text): + def value_from_text(self, record, text): if not text: return False try: @@ -436,30 +425,29 @@ class Datetime(Date): class Float(Char): - def __init__(self, field_name, model, treeview=None, attrs=None, - window=None): - super(Float, self).__init__(field_name, model, treeview=treeview, - attrs=attrs, window=window) + def __init__(self, field_name, model_name, treeview, window, attrs=None): + super(Float, self).__init__(field_name, model_name, treeview, window, + attrs=attrs) self.renderer = CellRendererFloat() def setter(self, column, cell, store, iter): super(Float, self).setter(column, cell, store, iter) - model = store.get_value(iter, 0) + record = store.get_value(iter, 0) if isinstance(self.attrs.get('digits'), str): - digits = model.expr_eval(self.attrs['digits']) + digits = record.expr_eval(self.attrs['digits']) else: digits = self.attrs.get('digits', (16, 2)) cell.digits = digits - def get_textual_value(self, model): + def get_textual_value(self, record): if isinstance(self.attrs.get('digits'), str): - digit = model.expr_eval(self.attrs['digits'])[1] + digit = record.expr_eval(self.attrs['digits'])[1] else: digit = self.attrs.get('digits', (16, 2))[1] return locale.format('%.'+str(digit)+'f', - model[self.field_name].get_client(model) or 0.0, True) + record[self.field_name].get_client(record) or 0.0, True) - def value_from_text(self, model, text): + def value_from_text(self, record, text): try: return locale.atof(text) except: @@ -468,72 +456,71 @@ class Float(Char): class FloatTime(Char): - def __init__(self, field_name, model, treeview=None, attrs=None, - window=None): - super(FloatTime, self).__init__(field_name, model, treeview=treeview, - attrs=attrs, window=window) + def __init__(self, field_name, model_name, treeview, window, attrs=None): + super(FloatTime, self).__init__(field_name, model_name, treeview, + window, attrs=attrs) self.conv = None if attrs and attrs.get('float_time'): self.conv = rpc.CONTEXT.get(attrs['float_time']) - def get_textual_value(self, model): - val = model[self.field_name].get_client(model) + def get_textual_value(self, record): + val = record[self.field_name].get_client(record) return common.float_time_to_text(val, self.conv) - def value_from_text(self, model, text): + def value_from_text(self, record, text): return common.text_to_float_time(text, self.conv) class M2O(Char): - def value_from_text(self, model, text): - modelfield = model.mgroup.mfields[self.field_name] - if not text and not modelfield.get_state_attrs( - model)['required']: + def value_from_text(self, record, text): + field = record.group.fields[self.field_name] + if not text and not field.get_state_attrs( + record)['required']: return False - relation = model[self.field_name].attrs['relation'] - rpc_relation = RPCProxy(relation) - - domain = model[self.field_name].domain_get(model) - context = model[self.field_name].context_get(model) - + relation = record[self.field_name].attrs['relation'] + domain = record[self.field_name].domain_get(record) + context = record[self.field_name].context_get(record) + if text: + dom = [('rec_name', 'ilike', '%' + text + '%'), + domain] + else: + dom = domain + args = ('model', relation, 'search', dom, 0, None, None, + context) try: - if text: - dom = [('rec_name', 'ilike', '%' + text + '%'), - domain] - else: - dom = domain - ids = rpc_relation.search(dom, 0, None, None, context) + ids = rpc.execute(*args) except Exception, exception: - common.process_exception(exception, self.window) - return '???' + ids = common.process_exception(exception, self.window, *args) + if not ids: + return '???' if len(ids) != 1: return self.search_remote(relation, ids, domain=domain, context=context)[0] return ids[0] - def open_remote(self, model, create=True, changed=False, text=None): - modelfield = model.mgroup.mfields[self.field_name] - relation = modelfield.attrs['relation'] + def open_remote(self, record, create=True, changed=False, text=None): + field = record.group.fields[self.field_name] + relation = field.attrs['relation'] - domain = modelfield.domain_get(model) - context = modelfield.context_get(model) + domain = field.domain_get(record) + context = field.context_get(record) if create: obj_id = None elif not changed: - obj_id = modelfield.get(model) + obj_id = field.get(record) else: - rpc_relation = RPCProxy(relation) - + if text: + dom = [('rec_name', 'ilike', '%' + text + '%'), domain] + else: + dom = domain + args = ('model', relation, 'search', dom, 0, None, None, context) try: - if text: - dom = [('rec_name', 'ilike', '%' + text + '%'), domain] - else: - dom = domain - ids = rpc_relation.search(dom, 0, None, None, context) + ids = rpc.execute(*args) except Exception, exception: - common.process_exception(exception, self.window) - return False, False + ids = common.process_exception(exception, self.window, *args) + if not ids: + return False, False if len(ids) == 1: return True, ids[0] searched = self.search_remote(relation, ids, domain=domain, @@ -541,29 +528,41 @@ class M2O(Char): if searched[0]: return True, searched return False, False - dia = M2ODialog(relation, obj_id, domain=domain, context=context, - window=self.window) - res, value = dia.run() - dia.destroy() - if res: + screen = Screen(relation, self.window, domain=domain, context=context, + view_type=['form']) + if obj_id: + screen.load([obj_id]) + win = WinForm(screen, self.window) + else: + win = WinForm(screen, self.window, new=True) + value = False + while win.run(): + if screen.save_current(): + value = (screen.current_record.id, + screen.current_record.rec_name()) + break + else: + screen.display() + win.destroy() + if value: return True, value else: return False, False def search_remote(self, relation, ids=None, domain=None, context=None): - rpc_relation = RPCProxy(relation) - win = WinSearch(relation, sel_multi=False, ids=ids, context=context, domain=domain, parent=self.window) found = win.run() if found: + arg = ('model', relation, 'read', found[0], ['rec_name'], + context) try: - name = rpc_relation.read(found[0], ['rec_name'], - context)['rec_name'] - return found[0], name + res = rpc.execute(*args) except Exception, exception: - common.process_exception(exception, self.window) - return False, None + res = common.process_exception(exception, self.window, *args) + if not res: + return False, None + return found[0], res['rec_name'] else: return False, None @@ -579,24 +578,25 @@ class O2M(Char): super(O2M, self).setter(column, cell, store, iter) cell.set_property('xalign', 0.5) - def get_textual_value(self, model): - return '( ' + str(len(model[self.field_name].\ - get_client(model).models)) + ' )' + def get_textual_value(self, record): + return '( ' + str(len(record[self.field_name].\ + get_client(record))) + ' )' - def value_from_text(self, model, text): + def value_from_text(self, record, text): pass - def open_remote(self, model, create=True, changed=False, text=None): - models = model.value[self.field_name] - modelfield = model.mgroup.mfields[self.field_name] - relation = modelfield.attrs['relation'] - context = modelfield.context_get(model) + def open_remote(self, record, create=True, changed=False, text=None): + group = record.value[self.field_name] + field = record.group.fields[self.field_name] + relation = field.attrs['relation'] + context = field.context_get(record) - dia = O2MDialog(relation, parent=model, model=models, - attrs=modelfield.attrs, default_get_ctx=context, - window=self.window) - res, value = dia.run() - dia.destroy() + screen = Screen(relation, self.window, view_type=['tree', 'form'], + exclude_field=field.attrs.get('relation_field')) + screen.group = group + win = WinForm(screen, self.window, view_type='tree', context=context) + win.run() + win.destroy() return False, False @@ -606,53 +606,55 @@ class M2M(Char): super(M2M, self).setter(column, cell, store, iter) cell.set_property('xalign', 0.5) - def get_textual_value(self, model): - return '( ' + str(len(model[self.field_name].\ - get_client(model).models)) + ' )' + def get_textual_value(self, record): + return '( ' + str(len(record[self.field_name].\ + get_client(record))) + ' )' - def value_from_text(self, model, text): + def value_from_text(self, record, text): if not text: return [] if not (text[0] != '('): - return model[self.field_name].get(model) - relation = model[self.field_name].attrs['relation'] - rpc_relation = RPCProxy(relation) - domain = model[self.field_name].domain_get(model) - context = model[self.field_name].context_get(model) + return record[self.field_name].get(record) + field = record[self.field_name] + relation = field.attrs['relation'] + domain = field.domain_get(record) + context = field.context_get(record) + if text: + dom = [('rec_name', 'ilike', '%' + text + '%'), + domain] + else: + dom = domain + args = ('model', relation, 'search', dom, 0, _LIMIT, None, context) try: - if text: - dom = [('rec_name', 'ilike', '%' + text + '%'), - domain] - else: - dom = domain - ids = rpc_relation.search(dom, 0, _LIMIT, None, context) + ids = rpc.execute(*args) except Exception, exception: - common.process_exception(exception, self.window) - return [] + ids = common.process_exception(exception, self.window, *args) + if ids is False: + return [] win = WinSearch(relation, sel_multi=True, ids=ids, context=context, domain=domain, parent=self.window) found = win.run() return found or [] - def open_remote(self, model, create=True, changed=False, text=None): - modelfield = model[self.field_name] - relation = modelfield.attrs['relation'] - - rpc_relation = RPCProxy(relation) - context = model[self.field_name].context_get(model) - domain = model[self.field_name].domain_get(model) + def open_remote(self, record, create=True, changed=False, text=None): + field = record[self.field_name] + relation = field.attrs['relation'] + context = field.context_get(record) + domain = field.domain_get(record) if create: if text and len(text) and text[0] != '(': domain.append(('name', '=', text)) + args = ('model', relation, 'search', domain) try: - ids = rpc_relation.search(domain) + ids = rpc.execute(*args) except Exception, exception: - common.process_exception(exception, self.window) - return False, None + ids = common.process_exception(exception, self.window, *args) + if ids is False: + return False, None if ids and len(ids)==1: return True, ids else: - ids = [x.id for x in model[self.field_name].get_client(model)] + ids = [x.id for x in field.get_client(record)] win = WinSearch(relation, sel_multi=True, ids=ids, context=context, domain=domain, parent=self.window) found = win.run() @@ -670,21 +672,22 @@ class Selection(Char): selection = self.attrs.get('selection', [])[:] self.selection = selection[:] if 'relation' in self.attrs: + args = ('model', self.attrs['relation'], 'search_read', + self.attrs.get('domain', []), 0, None, None, rpc.CONTEXT, + ['rec_name']) try: - result = rpc.execute('model', - self.attrs['relation'], 'search_read', - self.attrs.get('domain', []), - 0, None, None, rpc.CONTEXT, ['rec_name']) - selection = [(x['id'], x['rec_name']) for x in result] + result = rpc.execute(*args) except Exception, exception: - common.process_exception(exception, self.window) - selection = [] + result = common.process_exception(exception, self.window, *args) + if not result: + result = [] + selection = [(x['id'], x['rec_name']) for x in result] self.selection = selection[:] else: if not isinstance(selection, (list, tuple)): try: selection = rpc.execute('model', - self.model, selection, rpc.CONTEXT) + self.model_name, selection, rpc.CONTEXT) except Exception, exception: common.process_exception(exception, self.window) selection = [] @@ -710,13 +713,13 @@ class Selection(Char): self.renderer.set_property('model', selection_data) self.renderer.set_property('text-column', 1) - def get_textual_value(self, model): - value = model[self.field_name].get(model) + def get_textual_value(self, record): + value = record[self.field_name].get(record) if isinstance(value, (list, tuple)): value = value[0] return dict(self.selection).get(value, '') - def value_from_text(self, model, text): + def value_from_text(self, record, text): res = False for val, txt in self._selection: if txt[:len(text)].lower() == text.lower(): @@ -728,16 +731,15 @@ class Selection(Char): class Reference(Char): - def __init__(self, field_name, model, treeview=None, attrs=None, - window=None): - super(Reference, self).__init__(field_name, model, treeview=treeview, - attrs=attrs, window=window) + def __init__(self, field_name, model_name, treeview, window, attrs=None): + super(Reference, self).__init__(field_name, model_name, treeview, + window, attrs=attrs) self._selection = {} selection = attrs.get('selection', []) if not isinstance(selection, (list, tuple)): try: selection = rpc.execute('model', - model, selection, rpc.CONTEXT) + model_name, selection, rpc.CONTEXT) except Exception, exception: common.process_exception(exception, self.window) selection = [] @@ -745,25 +747,30 @@ class Reference(Char): for i, j in selection: self._selection[i] = str(j) - def get_textual_value(self, model): - value = model[self.field_name].get_client(model) + def get_textual_value(self, record): + value = record[self.field_name].get_client(record) if not value: model, (obj_id, name) = '', (0, '') else: model, (obj_id, name) = value if model: if not name and obj_id: + args = ('model', model, 'read', obj_id, ['rec_name'], + rpc.CONTEXT) try: - name = RPCProxy(model).read(obj_id, ['rec_name'], - rpc.CONTEXT)['rec_name'] + res = rpc.execute(*args) except Exception, exception: - common.process_exception(exception, self.window) + res = common.process_exception(exception, self.window, + *args) + if not res: name = '???' + else: + name = res['rec_name'] return self._selection.get(model, model) + ',' + name else: return name - def value_from_text(self, model, text): + def value_from_text(self, record, text): pass @@ -775,9 +782,10 @@ class ProgressBar(object): 'top_to_bottom': gtk.PROGRESS_TOP_TO_BOTTOM, } - def __init__(self, field_name, model, treeview=None, attrs=None, window=None): + def __init__(self, field_name, model_name, treeview, window, attrs=None): + super(ProgressBar, self).__init__() self.field_name = field_name - self.model = model + self.model_name = model_name self.attrs = attrs or {} self.renderer = gtk.CellRendererProgress() orientation = self.orientations.get(self.attrs.get('orientation', @@ -787,29 +795,29 @@ class ProgressBar(object): self.window = window def setter(self, column, cell, store, iter): - model = store.get_value(iter, 0) - value = float(self.get_textual_value(model) or 0.0) + record = store.get_value(iter, 0) + value = float(self.get_textual_value(record) or 0.0) cell.set_property('value', value) if isinstance(self.attrs.get('digits'), str): - digit = model.expr_eval(self.attrs['digits'])[1] + digit = record.expr_eval(self.attrs['digits'])[1] else: digit = self.attrs.get('digits', (16, 2))[1] text = locale.format('%.' + str(digit) + 'f', value, True) cell.set_property('text', text + '%') - def open_remote(self, model, create, changed=False, text=None): + def open_remote(self, record, create, changed=False, text=None): raise NotImplementedError - def get_textual_value(self, model): - return model[self.field_name].get_client(model) or '' + def get_textual_value(self, record): + return record[self.field_name].get_client(record) or '' - def value_from_text(self, model, text): + def value_from_text(self, record, text): return float(text) class Button(object): - def __init__(self, treeview=None, attrs=None, window=None, screen=None): + def __init__(self, treeview, windown, screen, attrs=None): super(Button, self).__init__() self.attrs = attrs or {} self.renderer = CellRendererButton(attrs.get('string', _('Unknown'))) @@ -823,24 +831,24 @@ class Button(object): if not path: return True store = self.treeview.get_model() - model = store.get_value(store.get_iter(path), 0) + record = store.get_value(store.get_iter(path), 0) - state_changes = PYSONDecoder().decode(model.expr_eval( + state_changes = PYSONDecoder().decode(record.expr_eval( self.attrs.get('states', {}), check_load=False)) if state_changes.get('invisible') \ or state_changes.get('readonly'): return True - self.screen.current_model = model + self.screen.current_record = record obj_id = self.screen.save_current() if obj_id: if not self.attrs.get('confirm', False) or \ common.sur(self.attrs['confirm'], self.window): button_type = self.attrs.get('type', 'workflow') ctx = rpc.CONTEXT.copy() - ctx.update(model.context_get()) + ctx.update(record.context_get()) if button_type == 'workflow': - args = ('model', self.screen.name, + args = ('model', self.screen.model_name, 'workflow_trigger_validate', obj_id, self.attrs['name'], ctx) try: @@ -848,7 +856,7 @@ class Button(object): except Exception, exception: common.process_exception(exception, self.window, *args) elif button_type == 'object': - args = ('model', self.screen.name, + args = ('model', self.screen.model_name, self.attrs['name'], [obj_id], ctx) try: rpc.execute(*args) @@ -865,7 +873,7 @@ class Button(object): self.window, *args) if action_id: Action.execute(action_id, { - 'model': self.screen.name, + 'model': self.screen.model_name, 'id': obj_id, 'ids': [obj_id], }, self.window, context=ctx) @@ -873,9 +881,6 @@ class Button(object): raise Exception('Unallowed button type') self.screen.reload(writen=True) else: - if self.screen.form: - self.screen.form.message_info( - _('Invalid Form, correct red fields!')) self.screen.display() CELLTYPES = { diff --git a/tryton/gui/window/view_form/view/screen_container.py b/tryton/gui/window/view_form/view/screen_container.py index 17e0bc4fc2..024565a5ee 100644 --- a/tryton/gui/window/view_form/view/screen_container.py +++ b/tryton/gui/window/view_form/view/screen_container.py @@ -15,6 +15,9 @@ class ScreenContainer(object): self.vbox.pack_end(self.viewport) self.filter_vbox = None self.button = None + self.alternate_viewport = gtk.Viewport() + self.alternate_viewport.set_shadow_type(gtk.SHADOW_NONE) + self.alternate_view = False def widget_get(self): return self.vbox @@ -70,6 +73,15 @@ class ScreenContainer(object): self.filter_vbox.hide() def set(self, widget): + if self.alternate_view: + if self.alternate_viewport.get_child(): + self.alternate_viewport.remove( + self.alternate_viewport.get_child()) + if widget == self.viewport.get_child(): + self.viewport.remove(self.viewport.get_child()) + self.alternate_viewport.add(widget) + self.alternate_viewport.show_all() + return if self.viewport.get_child(): self.viewport.remove(self.viewport.get_child()) self.viewport.add(widget) diff --git a/tryton/gui/window/view_form/view/widget_parse.py b/tryton/gui/window/view_form/view/widget_parse.py index 1d7d5ff2ff..742c26c3a2 100644 --- a/tryton/gui/window/view_form/view/widget_parse.py +++ b/tryton/gui/window/view_form/view/widget_parse.py @@ -36,7 +36,7 @@ class WidgetParse(ParserInterface): widget = PARSERS[node.localName](self.window, self.parent, self.attrs, screen) wid, child, buttons, on_write, notebooks, cursor_widget = \ - widget.parse(screen.resource, node, fields) + widget.parse(screen.model_name, node, fields) screen.set_on_write(on_write) res = PARSERS2[node.localName](self.window, screen, wid, child, buttons, toolbar, notebooks, cursor_widget) diff --git a/tryton/gui/window/win_export.py b/tryton/gui/window/win_export.py index ce9753c8f3..d4f3849bd1 100644 --- a/tryton/gui/window/win_export.py +++ b/tryton/gui/window/win_export.py @@ -14,14 +14,10 @@ import os _ = gettext.gettext - class WinExport(object): "Window export" - def __init__(self, model, ids, fields, preload=None, parent=None, - context=None): - if preload is None: - preload = [] + def __init__(self, model, ids, fields, parent=None, context=None): self.dialog = gtk.Dialog( title= _("Export to CSV"), parent=parent, @@ -187,9 +183,6 @@ class WinExport(object): self.model1 = gtk.TreeStore(gobject.TYPE_STRING, gobject.TYPE_STRING) self.model2 = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING) - for i in preload: - self.model2.set(self.model2.append(), 0, i[1], 1, i[0]) - self.fields = {} def model_populate(fields, prefix_node='', prefix=None, @@ -278,45 +271,64 @@ class WinExport(object): self.model2.clear() def fill_predefwin(self): - ir_export = rpc.RPCProxy('ir.export') - ir_export_line = rpc.RPCProxy('ir.export.line') + args = ('model', 'ir.export', 'search', + [('resource', '=', self.model)], 0, None, None, rpc.CONTEXT) try: - export_ids = ir_export.search([('resource', '=', self.model)]) + export_ids = rpc.execute(*args) except Exception, exception: - common.process_exception(exception, self.parent) - return - for export in ir_export.read(export_ids): - try: - fields = ir_export_line.read(export['export_fields']) - except Exception, exception: - common.process_exception(exception, self.parent) - continue - self.predef_model.append(( - export['id'], - [f['name'] for f in fields], + export_ids = common.process_exception(exception, self.parent, + *args) + if not export_ids: + return + args = ('model', 'ir.export', 'read', export_ids, None, rpc.CONTEXT) + try: + exports = rpc.execute(*args) + except Exception, exception: + exports = common.process_exception(exception, self.parent, *args) + if not exports: + return + args = ('model', 'ir.export.line', 'read', + [x['export_fields'] for x in exports], None, rpc.CONTEXT) + try: + lines = rpc.execute(*args) + except Exception, exception: + lines = common.process_exception(exception, self.parent, *args) + if not lines: + return + id2lines = {} + for line in lines: + id2lines.setdefault(line['export'], []).append(line) + for export in exports: + self.predef_model.append((export['id'], + [x['name'] for x in id2lines[export['id']]], export['name'], - ', '.join(self.fields_data[f['name']]['string'] \ - for f in fields), - )) + ','.join(self.fields_data[x['name']]['string'] + for x in id2lines[export['id']]))) self.pref_export.set_model(self.predef_model) def add_predef(self, widget): - name = common.ask('What is the name of this export?', self.parent) + name = common.ask(_('What is the name of this export?'), self.parent) if not name: return - ir_export = rpc.RPCProxy('ir.export') iter = self.model2.get_iter_root() fields = [] while iter: field_name = self.model2.get_value(iter, 1) fields.append(field_name) iter = self.model2.iter_next(iter) + args = ('model', 'ir.export', 'create', { + 'name': name, + 'resource': self.model, + 'export_fields': [('create', { + 'name': x, + }) for x in fields], + }, rpc.CONTEXT) try: - new_id = ir_export.create({'name' : name, 'resource' : self.model, - 'export_fields' : [('create', {'name' : f}) for f in fields]}) + new_id = rpc.execute(*args) except Exception, exception: - common.process_exception(exception, self.dialog) - return + new_ids = common.process_exception(exception, self.dialog, *args) + if not new_id: + return self.predef_model.append(( new_id, fields, @@ -331,13 +343,13 @@ class WinExport(object): (model, i) = sel if not i: return None - ir_export = rpc.RPCProxy('ir.export') export_id = model.get_value(i, 0) + args = ('model', 'ir.export', 'delete', export_id, rpc.CONTEXT) try: - ir_export.delete(export_id) + rpc.execute(*args) except Exception, exception: - common.process_exception(exception, self.dialog) - return + if not common.process_exception(exception, self.dialog, *args): + return for i in range(len(self.predef_model)): if self.predef_model[i][0] == export_id: del self.predef_model[i] diff --git a/tryton/gui/window/win_form.py b/tryton/gui/window/win_form.py new file mode 100644 index 0000000000..214302b517 --- /dev/null +++ b/tryton/gui/window/win_form.py @@ -0,0 +1,309 @@ +#This file is part of Tryton. The COPYRIGHT file at the top level of +#this repository contains the full copyright notices and license terms. +from tryton.common import TRYTON_ICON +import tryton.common as common +import gtk +import pango +import gettext + +_ = gettext.gettext + + +class WinForm(object): + "Form window" + + def __init__(self, screen, parent, view_type='form', new=False, + context=None): + + self.parent = parent + self.screen = screen + self.context = context + self.prev_view = self.screen.current_view + self.screen.screen_container.alternate_view = True + switch_new = False + if view_type == 'form' and not self.screen.current_record: + switch_new = True + if view_type not in (x.view_type for x in self.screen.views) and \ + view_type not in self.screen.view_to_load: + self.screen.add_view_id(False, view_type, display=True) + else: + self.screen.switch_view(view_type=view_type) + if new and not switch_new: + self.screen.new(context=self.context) + self.win = gtk.Dialog(_('Link'), parent, + gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT) + self.win.connect('close', self._sig_close) + self.win.set_property('default-width', 760) + self.win.set_property('default-height', 500) + self.win.set_position(gtk.WIN_POS_CENTER_ON_PARENT) + self.win.set_icon(TRYTON_ICON) + self.win.set_has_separator(False) + + self.accel_group = gtk.AccelGroup() + self.win.add_accel_group(self.accel_group) + + self.but_cancel = None + if new: + icon_cancel = gtk.STOCK_CANCEL + self.but_cancel = self.win.add_button(icon_cancel, + gtk.RESPONSE_CANCEL) + + self.but_ok = self.win.add_button(gtk.STOCK_OK, gtk.RESPONSE_OK) + self.but_ok.add_accelerator('clicked', self.accel_group, + gtk.keysyms.Return, gtk.gdk.CONTROL_MASK, gtk.ACCEL_VISIBLE) + + self.win.set_default_response(gtk.RESPONSE_OK) + + self.win.set_title(self.screen.current_view.title) + + title = gtk.Label() + title.set_use_markup(True) + title.modify_font(pango.FontDescription("12")) + title.set_label('' + self.screen.current_view.title + '') + title.set_padding(20, 3) + title.set_alignment(0.0, 0.5) + title.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse("#000000")) + title.show() + + self.info_label = gtk.Label() + self.info_label.set_padding(3, 3) + self.info_label.set_alignment(1.0, 0.5) + + self.eb_info = gtk.EventBox() + self.eb_info.add(self.info_label) + self.eb_info.connect('button-release-event', + lambda *a: self.message_info('')) + + vbox = gtk.VBox() + vbox.pack_start(self.eb_info, expand=True, fill=True, padding=5) + vbox.show() + + hbox = gtk.HBox() + hbox.pack_start(title, expand=True, fill=True) + hbox.pack_start(vbox, expand=False, fill=True, padding=20) + hbox.show() + + frame = gtk.Frame() + frame.set_shadow_type(gtk.SHADOW_ETCHED_IN) + frame.add(hbox) + frame.show() + + eb = gtk.EventBox() + eb.add(frame) + eb.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse("#ffffff")) + eb.show() + + self.win.vbox.pack_start(eb, expand=False, fill=True, padding=3) + + if view_type == 'tree': + hbox = gtk.HBox(homogeneous=False, spacing=0) + tooltips = common.Tooltips() + + self.but_new = gtk.Button() + tooltips.set_tip(self.but_new, _('Create a new record')) + self.but_new.connect('clicked', self._sig_new) + img_new = gtk.Image() + img_new.set_from_stock('tryton-new', gtk.ICON_SIZE_SMALL_TOOLBAR) + img_new.set_alignment(0.5, 0.5) + self.but_new.add(img_new) + self.but_new.set_relief(gtk.RELIEF_NONE) + hbox.pack_start(self.but_new, expand=False, fill=False) + + self.but_del = gtk.Button() + tooltips.set_tip(self.but_del, _('Delete selected record')) + self.but_del.connect('clicked', self._sig_remove) + img_del = gtk.Image() + img_del.set_from_stock('tryton-delete', gtk.ICON_SIZE_SMALL_TOOLBAR) + img_del.set_alignment(0.5, 0.5) + self.but_del.add(img_del) + self.but_del.set_relief(gtk.RELIEF_NONE) + hbox.pack_start(self.but_del, expand=False, fill=False) + + hbox.pack_start(gtk.VSeparator(), expand=False, fill=True) + + but_pre = gtk.Button() + tooltips.set_tip(but_pre, _('Previous')) + but_pre.connect('clicked', self._sig_previous) + img_pre = gtk.Image() + img_pre.set_from_stock('tryton-go-previous', gtk.ICON_SIZE_SMALL_TOOLBAR) + img_pre.set_alignment(0.5, 0.5) + but_pre.add(img_pre) + but_pre.set_relief(gtk.RELIEF_NONE) + hbox.pack_start(but_pre, expand=False, fill=False) + + self.label = gtk.Label('(0,0)') + hbox.pack_start(self.label, expand=False, fill=False) + + but_next = gtk.Button() + tooltips.set_tip(but_next, _('Next')) + but_next.connect('clicked', self._sig_next) + img_next = gtk.Image() + img_next.set_from_stock('tryton-go-next', gtk.ICON_SIZE_SMALL_TOOLBAR) + img_next.set_alignment(0.5, 0.5) + but_next.add(img_next) + but_next.set_relief(gtk.RELIEF_NONE) + hbox.pack_start(but_next, expand=False, fill=False) + + hbox.pack_start(gtk.VSeparator(), expand=False, fill=True) + + but_switch = gtk.Button() + tooltips.set_tip(but_switch, _('Switch')) + but_switch.connect('clicked', self.switch_view) + img_switch = gtk.Image() + img_switch.set_from_stock('tryton-fullscreen', gtk.ICON_SIZE_SMALL_TOOLBAR) + img_switch.set_alignment(0.5, 0.5) + but_switch.add(img_switch) + but_switch.set_relief(gtk.RELIEF_NONE) + hbox.pack_start(but_switch, expand=False, fill=False) + + tooltips.enable() + + alignment = gtk.Alignment(1.0) + alignment.add(hbox) + alignment.show_all() + + self.win.vbox.pack_start(alignment, expand=False, fill=True) + + scroll = gtk.ScrolledWindow() + scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) + scroll.set_placement(gtk.CORNER_TOP_LEFT) + scroll.set_shadow_type(gtk.SHADOW_NONE) + scroll.show() + self.win.vbox.pack_start(scroll, expand=True, fill=True) + + scroll.add(self.screen.screen_container.alternate_viewport) + + width, height = self.screen.current_view.widget.size_request() + scroll.set_size_request(width, height + 30) + + if view_type == 'tree': + self.screen.signal_connect(self, 'record-message', self._sig_label) + self.screen.screen_container.alternate_viewport.connect( + 'key-press-event', self.on_keypress) + + self.screen.display() + self.screen.current_view.set_cursor() + + def message_info(self, message, color='red'): + if message: + self.info_label.set_label(message) + self.eb_info.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse( + COLOR_SCHEMES.get(color, 'white'))) + self.eb_info.show_all() + else: + self.info_label.set_label('') + self.eb_info.hide() + + def on_keypress(self, widget, event): + if (event.keyval == gtk.keysyms.F3) \ + and self.but_new.get_property('sensitive'): + self._sig_new(widget) + return False + if event.keyval in (gtk.keysyms.Delete, gtk.keysyms.KP_Delete) \ + and widget == self.screen.screen_container.alternate_viewport: + self._sig_remove(widget) + return False + + def switch_view(self, widget): + self.screen.switch_view() + + def _sig_new(self, widget): + self.screen.new(context=self.context) + self.screen.current_view.widget.set_sensitive(True) + + def _sig_next(self, widget): + self.screen.display_next() + + def _sig_previous(self, widget): + self.screen.display_prev() + + def _sig_remove(self, widget): + self.screen.remove(delete=True) + + def _sig_activate(self, *args): + self._sig_add() + self.wid_text.grab_focus() + + def _sig_add(self, *args): + domain = [] + context = rpc.CONTEXT.copy() + + try: + if self.wid_text.get_text(): + dom = [('rec_name', 'ilike', + '%' + self.wid_text.get_text() + '%'), domain] + else: + dom = domain + ids = rpc.execute('model', self.attrs['relation'], + 'search', dom, 0, _LIMIT, None, context) + except Exception, exception: + common.process_exception(exception, self.window) + return False + if len(ids) != 1: + win = WinSearch(self.attrs['relation'], sel_multi=True, ids=ids, + context=context, domain=domain, parent=self.window, + views_preload=self.attrs.get('views', {})) + ids = win.run() + + res_id = None + if ids: + res_id = ids[0] + self.screen.load(ids, modified=True) + self.screen.display(res_id=res_id) + if self.screen.current_view: + self.screen.current_view.set_cursor() + self.wid_text.set_text('') + + def _sig_label(self, screen, signal_data): + name = '_' + if signal_data[0] >= 0: + name = str(signal_data[0] + 1) + line = '(%s/%s)' % (name, signal_data[1]) + self.label.set_text(line) + + def _sig_close(self, widget): + if self.screen.current_view: + self.screen.current_view.set_value() + if self.but_cancel: + self.screen.remove(delete=True) + elif self.screen.current_record and \ + not self.screen.current_record.validate(): + if self.screen.current_view: + self.screen.current_view.set_cursor() + self.screen.current_view.display() + widget.emit_stop_by_name('close') + + def run(self): + end = False + while not end: + res = self.win.run() + self.screen.current_view.set_value() + end = (res != gtk.RESPONSE_OK) \ + or (not self.screen.current_record \ + or self.screen.current_record.validate()) + if not end: + self.screen.current_view.set_cursor() + self.screen.display() + if self.but_cancel: + self.but_cancel.set_label(gtk.STOCK_CLOSE) + + if res == gtk.RESPONSE_OK: + return True + elif res == gtk.RESPONSE_CANCEL: + self.screen.remove(delete=True) + return False + + def new(self): + self.screen.new(context=self.context) + self.screen.current_view.display() + self.screen.current_view.set_cursor(new=True) + + def destroy(self): + self.screen.screen_container.alternate_view = False + viewport = self.screen.screen_container.alternate_viewport + if viewport.get_child(): + viewport.remove(viewport.get_child()) + self.screen.switch_view(view_type=self.prev_view.view_type) + self.screen.signal_unconnect(self) + self.win.destroy() + self.parent.present() diff --git a/tryton/gui/window/win_import.py b/tryton/gui/window/win_import.py index a6b6b9c7f3..eda20be241 100644 --- a/tryton/gui/window/win_import.py +++ b/tryton/gui/window/win_import.py @@ -14,7 +14,7 @@ _ = gettext.gettext class WinImport(object): "Window import" - def __init__(self, model, fields, preload=None, parent=None): + def __init__(self, model, fields, parent=None): self.dialog = gtk.Dialog( title=_("Import from CSV"), parent=parent, flags=gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT @@ -195,9 +195,6 @@ class WinImport(object): gobject.TYPE_STRING) self.model2 = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING) - for field in (preload or []): - self.model2.set(self.model2.append(), 0, field[1], 1, field[0]) - self.fields = {} self.fields_invert = {} @@ -207,28 +204,26 @@ class WinImport(object): fields_order.sort(lambda x, y: -cmp(fields[x].get('string', ''), fields[y].get('string', ''))) for field in fields_order: - if (fields[field]['type'] not in ('reference',)) \ - and (not fields[field].get('readonly', False) \ - or not dict(fields[field].get('states', {}).get( - 'draft', [('readonly', True)])).get('readonly', - True)): - self.fields_data[prefix_node+field] = fields[field] - st_name = prefix_value+fields[field]['string'] or field + if not fields[field].get('readonly', False): + self.fields_data[prefix_node + field] = fields[field] + st_name = prefix_value + fields[field]['string'] or field node = self.model1.insert(prefix, 0, [st_name, prefix_node+field, (fields[field].get('required', False) and \ '#ddddff') or 'white']) self.fields[prefix_node+field] = st_name - self.fields_invert[st_name] = prefix_node+field + self.fields_invert[st_name] = prefix_node + field if fields[field]['type'] == 'one2many' and level > 0: + args = ('model', fields[field]['relation'], + 'fields_get', None, rpc.CONTEXT) try: - fields2 = rpc.execute('model', - fields[field]['relation'], 'fields_get', - None, rpc.CONTEXT) + fields2 = rpc.execute(*args) except Exception, exception: - common.process_exception(exception, self.dialog) - continue - model_populate(fields2, prefix_node+field+'/', node, + fields2 = common.process_exception(exception, + self.dialog, *args) + if not fields2: + continue + model_populate(fields2, prefix_node + field + '/', node, st_name+'/', level-1) model_populate(fields) diff --git a/tryton/gui/window/win_search.py b/tryton/gui/window/win_search.py index 1f8bc7d620..8c18a1fd6f 100644 --- a/tryton/gui/window/win_search.py +++ b/tryton/gui/window/win_search.py @@ -7,6 +7,7 @@ from tryton.gui.window.view_form.screen import Screen import tryton.gui.window.view_form.widget_search as widget_search from tryton.config import TRYTON_ICON import tryton.common as common +from tryton.gui.window.win_form import WinForm _ = gettext.gettext @@ -47,7 +48,8 @@ class WinSearch(object): self.win.vbox.pack_start(scrollwindow, expand=True, fill=True) self.screen = Screen(model, self.win, view_type=['tree'], - context=context, views_preload=views_preload) + context=context, views_preload=views_preload, + row_activate=self.sig_activate) self.view = self.screen.current_view self.view.unset_editable() sel = self.view.widget_tree.get_selection() @@ -61,44 +63,12 @@ class WinSearch(object): viewport.add(self.screen.widget) scrollwindow.add(viewport) scrollwindow.show_all() - self.view.widget_tree.connect('row_activated', self.sig_activate) self.view.widget_tree.connect('button_press_event', self.sig_button) self.model_name = model - if 'tree' in views_preload: - view_form = views_preload['tree'] - else: - ctx = self.context.copy() - ctx.update(rpc.CONTEXT) - try: - args = ('model', self.model_name, 'fields_view_get', - False, 'tree', ctx) - view_form = rpc.execute(*args) - except Exception, exception: - view_form = common.process_exception(exception, self.parent, *args) - self.form = widget_search.Form(view_form['arch'], view_form['fields'], - model, parent=self.win, call=(self, self.find)) - - self.title = _('Search: %s') % self.form.name - self.title_results = _('Search: %s (%%d result(s))') % \ - self.form.name.replace('%', '%%') - self.win.set_title(self.title) - - hbox.pack_start(self.form.widget) - self.ids = ids - if self.ids: - self.reload() - self.old_search = None - self.old_offset = self.old_limit = None - if self.ids: - self.old_search = [] - self.old_limit = self.form.get_limit() - self.old_offset = self.form.get_offset() - - self.view.widget.show_all() - if self.form.focusable: - self.form.focusable.grab_focus() + if ids: + self.screen.load(ids) self.win.set_size_request(700, 500) @@ -113,37 +83,6 @@ class WinSearch(object): self.win.response(gtk.RESPONSE_OK) return False - def find(self, *args): - limit = self.form.get_limit() - offset = self.form.get_offset() - if (self.old_search == self.form.value) \ - and (self.old_limit==limit) \ - and (self.old_offset==offset): - self.win.response(gtk.RESPONSE_OK) - return False - self.old_offset = offset - self.old_limit = limit - value = self.form.value - value += self.domain - try: - self.ids = rpc.execute('model', self.model_name, 'search', value, - offset, limit, None, rpc.CONTEXT) - except Exception, exception: - common.process_exception(exception, self.win) - return False - self.reload() - self.old_search = self.form.value - self.win.set_title(self.title_results % len(self.ids)) - return True - - def reload(self): - self.screen.clear() - self.screen.load(self.ids) - sel = self.view.widget_tree.get_selection() - - def sel_ids_get(self): - return self.screen.sel_ids_get() - def destroy(self): self.parent.present() self.screen.destroy() @@ -154,22 +93,24 @@ class WinSearch(object): while not end: button = self.win.run() if button == gtk.RESPONSE_OK: - res = self.sel_ids_get() + res = self.screen.sel_ids_get() end = True elif button == gtk.RESPONSE_APPLY: - end = not self.find() + end = not self.screen.search_filter() if end: - res = self.sel_ids_get() + res = self.screen.sel_ids_get() elif button == gtk.RESPONSE_ACCEPT: - from tryton.gui.window.view_form.view.form_gtk.many2one \ - import Dialog - dia = Dialog(self.model_name, window=self.win, - domain=self.domain, context=self.context) - res, value = dia.run() - if res: - res = [value[0]] - else: - res = None + res = None + screen = Screen(self.model_name, self.win, domain=self.domain, + context=self.context, view_type=['form']) + win = WinForm(screen, self.win, new=True) + while win.run(): + if screen.save_current(): + res = [screen.current_record.id] + break + else: + screen.display() + win.destroy() end = True else: res = None diff --git a/tryton/gui/window/wizard.py b/tryton/gui/window/wizard.py index 74d8ad8109..cc70393351 100644 --- a/tryton/gui/window/wizard.py +++ b/tryton/gui/window/wizard.py @@ -175,7 +175,7 @@ class Wizard(SignalEvent): self.screen = Screen(obj_name, self.window, view_type=[], context=context) - self.screen.add_view_custom(arch, fields, display=True) + self.screen.add_view(arch, fields, display=True) self.screen.widget.show() title = gtk.Label() diff --git a/tryton/rpc.py b/tryton/rpc.py index be0139f811..bfa205555a 100644 --- a/tryton/rpc.py +++ b/tryton/rpc.py @@ -168,9 +168,8 @@ def logout(): def context_reload(): global CONTEXT, TIMEZONE - user = RPCProxy('res.user') try: - context = user.get_preferences(True, {}) + context = execute('model', 'res.user', 'get_preferences', True, {}) except: return CONTEXT = {} @@ -224,23 +223,3 @@ def execute(*args): def execute_nonblocking(*args): return _execute(False, *args) - -class RPCProxy(object): - - def __init__(self, name): - self.name = name - self.__attrs = {} - - def __getattr__(self, attr): - if attr not in self.__attrs: - self.__attrs[attr] = RPCFunction(self.name, attr) - return self.__attrs[attr] - -class RPCFunction(object): - - def __init__(self, name, func_name): - self.name = name - self.func = func_name - - def __call__(self, *args): - return execute('model', self.name, self.func, *args) diff --git a/tryton/signal_event.py b/tryton/signal_event.py index e5bfa5c2fb..86dd9eadd6 100644 --- a/tryton/signal_event.py +++ b/tryton/signal_event.py @@ -24,7 +24,7 @@ class SignalEvent(object): return True def signal_unconnect(self, key, signal=None): - if not signal: + if signal is None: signal = self.__connects.keys() else: signal = [signal] diff --git a/tryton/wizard/main.py b/tryton/wizard/main.py index 8c0bbaba0c..353a9e7d05 100644 --- a/tryton/wizard/main.py +++ b/tryton/wizard/main.py @@ -55,7 +55,7 @@ class Dialog(object): val[i] = fields[i]['value'] self.screen = Screen(obj_name, self.dia, view_type=[], context=context) - self.screen.add_view_custom(arch, fields, display=True) + self.screen.add_view(arch, fields, display=True) title = gtk.Label() title.set_use_markup(True) @@ -110,7 +110,7 @@ class Dialog(object): self.dia.set_title(self.screen.current_view.title) self.dia.show() self.screen.new(default=False) - self.screen.current_model.set(val) + self.screen.current_record.set(val) self.screen.current_view.set_cursor() def run(self, datas=None): @@ -119,7 +119,7 @@ class Dialog(object): while True: res = self.dia.run() self.screen.current_view.set_value() - if self.screen.current_model.validate() \ + if self.screen.current_record.validate() \ or (res<0) or (self.states[res]=='end'): break self.screen.display() @@ -206,7 +206,7 @@ class Wizard(object): dia = Dialog(res['arch'], res['fields'], res['state'], res['object'], parent, action=action, size=res['size'], context=ctx) - dia.screen.current_model.set(datas['form']) + dia.screen.current_record.set(datas['form']) res2 = dia.run(datas['form']) if not res2: break