diff -r fcba908c65ca tryton/gui/window/form.py
--- a/tryton/gui/window/form.py	Thu Feb 03 01:04:30 2011 +0100
+++ b/tryton/gui/window/form.py	Fri Feb 04 21:58:42 2011 +0100
@@ -361,7 +361,7 @@
     def sig_reload(self, test_modified=True):
         if not hasattr(self, 'screen'):
             return False
-        if test_modified and self.screen.is_modified():
+        if test_modified and self.screen.modified():
             res = sur_3b(_('This record has been modified\n' \
                     'do you want to save it ?'), self.window)
             if res == 'ok':
@@ -441,7 +441,7 @@
     def _record_message(self, screen, signal_data):
         name = '_'
         if signal_data[0] >= 0:
-            name = str(signal_data[0] + 1)
+            name = str(signal_data[0])
         msg = name + ' / ' + str(signal_data[1])
         if signal_data[1] < signal_data[2]:
             msg += _(' of ') + str(signal_data[2])
@@ -455,7 +455,7 @@
         self.signal('attachment-count', signal_data)
 
     def modified_save(self, reload=True):
-        if self.screen.is_modified():
+        if self.screen.modified():
             value = sur_3b(
                     _('This record has been modified\n' \
                             'do you want to save it ?'),
diff -r fcba908c65ca tryton/gui/window/preference.py
--- a/tryton/gui/window/preference.py	Thu Feb 03 01:04:30 2011 +0100
+++ b/tryton/gui/window/preference.py	Fri Feb 04 21:58:42 2011 +0100
@@ -37,10 +37,10 @@
         args = ('model', 'res.user', 'get_preferences_fields_view',
                 rpc.CONTEXT)
         try:
-            res = rpc.execute(*args)
+            view = rpc.execute(*args)
         except Exception, exception:
-            res = common.process_exception(exception, parent, *args)
-            if not res:
+            view = common.process_exception(exception, parent, *args)
+            if not view:
                 self.win.destroy()
                 self.win = None
                 return
@@ -48,11 +48,9 @@
         title = gtk.Label(_('Edit User Preferences'))
         title.show()
         self.win.vbox.pack_start(title, expand=False, fill=True)
-        arch = res['arch']
-        fields = res['fields']
         self.screen = Screen('res.user', self.win, view_type=[])
         self.screen.new(default=False)
-        self.screen.add_view(arch, fields, display=True)
+        self.screen.add_view(view, display=True)
 
         args = ('model', 'res.user', 'get_preferences', False, rpc.CONTEXT)
         try:
diff -r fcba908c65ca tryton/gui/window/view_board/action.py
--- a/tryton/gui/window/view_board/action.py	Thu Feb 03 01:04:30 2011 +0100
+++ b/tryton/gui/window/view_board/action.py	Fri Feb 04 21:58:42 2011 +0100
@@ -220,7 +220,7 @@
     def _sig_label(self, screen, signal_data):
         name = '_'
         if signal_data[0] >= 0:
-            name = str(signal_data[0] + 1)
+            name = str(signal_data[0])
         line = '(%s/%s)' % (name, signal_data[1])
         self.label.set_text(line)
 
diff -r fcba908c65ca tryton/gui/window/view_form/model/field.py
--- a/tryton/gui/window/view_form/model/field.py	Thu Feb 03 01:04:30 2011 +0100
+++ b/tryton/gui/window/view_form/model/field.py	Fri Feb 04 21:58:42 2011 +0100
@@ -27,8 +27,7 @@
 
 class CharField(object):
 
-    def __init__(self, parent, attrs):
-        self.parent = parent
+    def __init__(self, attrs):
         self.attrs = attrs
         self.name = attrs['name']
         self.internal = False
@@ -67,7 +66,7 @@
 
     def context_get(self, record, check_load=True, eval_context=True):
         context = {}
-        context.update(self.parent.context)
+        context.update(record.parent.context)
         if eval_context:
             context.update(record.expr_eval(self.attrs.get('context', {}),
                 check_load=check_load))
@@ -112,8 +111,8 @@
     def set(self, record, value, modified=False):
         record.value[self.name] = value
         if modified:
-            record.modified = True
             record.modified_fields.setdefault(self.name)
+            record.signal('record-modified')
         return True
 
     def get(self, record, check_load=True, readonly=True, modified=False):
@@ -128,16 +127,16 @@
 
     def set_client(self, record, value, force_change=False):
         internal = record.value.get(self.name, False)
-        prev_modified = record.modified
+        prev_modified_fields = record.modified_fields.copy()
         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)
+            record.signal('record-modified')
             try:
                 self.sig_changed(record)
             except Exception:
                 record.value[self.name] = internal
-                record.modified = prev_modified
+                record.modified_fields = prev_modified_fields
                 return
             record.validate(softvalidation=True)
             record.signal('record-changed')
@@ -183,9 +182,6 @@
     def get_timestamp(self, record):
         return {}
 
-    def destroy(self):
-        self.parent = None
-
 
 class SelectionField(CharField):
 
@@ -230,19 +226,20 @@
 
     def set_client(self, record, value, force_change=False):
         internal = record.value.get(self.name)
-        prev_modified = record.modified
+        prev_modified_fields = record.modified_fields.copy()
         self.set(record, value)
         digits = record.expr_eval(self.attrs.get('digits', (16, 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(record).get('readonly', False):
-                record.modified = True
                 record.modified_fields.setdefault(self.name)
+                record.signal('record-modified')
                 try:
                     self.sig_changed(record)
                 except Exception:
                     record.value[self.name] = internal
-                    record.modified = prev_modified
+                    record.modified_fields = prev_modified_fields
+                    return
                 record.validate(softvalidation=True)
                 record.signal('record-changed')
 
@@ -252,20 +249,20 @@
     def set_client(self, record, value, force_change=False):
         value = Decimal(str(value))
         internal = record.value.get(self.name)
-        prev_modified = record.modified
+        prev_modified_fields = record.modified_fields.copy()
         self.set(record, value)
         digits = record.expr_eval(self.attrs.get('digits', (16, 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(record).get('readonly', False):
-                record.modified = True
                 record.modified_fields.setdefault(self.name)
+                record.signal('record-modified')
                 try:
                     self.sig_changed(record)
                 except Exception:
                     record.value[self.name] = internal
-                    record.modified = prev_modified
+                    record.prev_modified_fields = prev_modified_fields
                     return
                 record.validate(softvalidation=True)
                 record.signal('record-changed')
@@ -284,16 +281,16 @@
     def set_client(self, record, value, force_change=False):
         value = bool(value)
         internal = bool(record.value.get(self.name, False))
-        prev_modified = record.modified
+        prev_modified_fields = record.modified_fields.copy()
         self.set(record, value)
         if internal != bool(record.value.get(self.name, False)):
-            record.modified = True
             record.modified_fields.setdefault(self.name)
+            record.signal('record-modified')
             try:
                 self.sig_changed(record)
             except Exception:
                 record.value[self.name] = internal
-                record.modified = prev_modified
+                record.modified_fields = prev_modified_fields
                 return
             record.validate(softvalidation=True)
             record.signal('record-changed')
@@ -311,6 +308,8 @@
     '''
 
     def get(self, record, check_load=True, readonly=True, modified=False):
+        if record.parent_name == self.name:
+            return record.parent.id if record.parent else False
         if record.value.get(self.name):
             if isinstance(record.value[self.name], (int, basestring, long)):
                 self.set(record, record.value[self.name])
@@ -329,6 +328,8 @@
         return False
 
     def set(self, record, value, modified=False):
+        if record.parent_name == self.name:
+            value = record.parent.id if record.parent else False
         if value and isinstance(value, (int, long)):
             args = ('model', self.attrs['relation'], 'read', value,
                     ['rec_name'], rpc.CONTEXT)
@@ -350,21 +351,21 @@
                 record.value[self.name + '.rec_name'] = ''
         record.value[self.name] = value or (False, '')
         if modified:
-            record.modified = True
             record.modified_fields.setdefault(self.name)
+            record.signal('record-modified')
 
     def set_client(self, record, value, force_change=False):
         internal = record.value.get(self.name) or (False, '')
-        prev_modified = record.modified
+        prev_modified_fields = record.modified_fields.copy()
         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)
+            record.signal('record-modified')
             try:
                 self.sig_changed(record)
             except Exception:
                 record.value[self.name] = internal
-                record.modified = prev_modified
+                record.modified_fields = prev_modified_fields
                 return
             record.validate(softvalidation=True)
             record.signal('record-changed')
@@ -393,6 +394,12 @@
         screen_domain, attr_domain = self.domains_get(record)
         return localize_domain(inverse_leaf(screen_domain)) + attr_domain
 
+    def get_state_attrs(self, record):
+        result = super(M2OField, self).get_state_attrs(record)
+        if record.parent_name == self.name:
+            result = result.copy()
+            result['readonly'] = True
+        return result
 
 class O2OField(M2OField):
     pass
@@ -403,27 +410,42 @@
     internal = Group of the related objects
     '''
 
-    def __init__(self, parent, attrs):
-        super(O2MField, self).__init__(parent, attrs)
+    def __init__(self, attrs):
+        super(O2MField, self).__init__(attrs)
         self.context = {}
 
     def _group_changed(self, group, record):
-        record.parent.modified = True
+        if not record.parent:
+            return
         record.parent.modified_fields.setdefault(self.name)
+        record.parent.signal('record-modified')
         self.sig_changed(record.parent)
         record.parent.validate(softvalidation=True)
         record.parent.signal('record-changed')
+        record.parent.group.signal('group-changed', record.parent)
+
+    def _group_list_changed(self, group, signal):
+        group.parent.group.signal('group-list-changed', signal)
+
+    def _group_cleared(self, group):
+        group.parent.signal('group-cleared')
 
     def _set_default_value(self, record):
         if record.value.get(self.name) is not None:
             return
         from group import Group
+        parent_name = self.attrs.get('relation_field', '')
         group = Group(self.attrs['relation'], {}, record.window,
                 parent=record,
-                parent_name=self.attrs.get('relation_field', ''),
+                parent_name=parent_name,
+                child_name=self.name,
                 context=self.context,
                 parent_datetime_field=self.attrs.get('datetime_field'))
+        if record.model_name == self.attrs['relation']:
+            group.fields = record.group.fields
         group.signal_connect(group, 'group-changed', self._group_changed)
+        group.signal_connect(group, 'group-list-changed', self._group_list_changed)
+        group.signal_connect(group, 'group-cleared', self._group_cleared)
         record.value[self.name] = group
 
     def get_client(self, record):
@@ -438,7 +460,7 @@
             if record2.id > 0:
                 values = record2.get(check_load=check_load,
                     get_readonly=readonly, get_modifiedonly=modified)
-                if record2.is_modified() and values:
+                if record2.modified and values:
                     result.append(('write', record2.id, values))
                 result[0][1].append(record2.id)
             else:
@@ -476,18 +498,27 @@
     def set(self, record, value, modified=False):
         from group import Group
         group = record.value.get(self.name)
+        fields = {}
         if group is not None:
             group.signal_unconnect(group)
+            fields = group.fields
+        elif record.model_name == self.attrs['relation']:
+            fields = record.group.fields
+        parent_name = self.attrs.get('relation_field', '')
         group = Group(self.attrs['relation'], {}, record.window,
-                parent=record, parent_name=self.attrs.get('relation_field', ''),
+                parent=record, parent_name=parent_name,
+                child_name=self.name,
                 context=self.context,
                 parent_datetime_field=self.attrs.get('datetime_field'))
+        group.fields = fields
         record.value[self.name] = group
-        record.value[self.name].load(value, display=False)
+        group.load(value, display=False)
         group.signal_connect(group, 'group-changed', self._group_changed)
+        group.signal_connect(group, 'group-list-changed', self._group_list_changed)
+        group.signal_connect(group, 'group-cleared', self._group_cleared)
         if modified:
-            record.modified = True
             record.modified_fields.setdefault(self.name)
+            record.signal('record-modified')
 
     def set_client(self, record, value, force_change=False):
         pass
@@ -519,11 +550,17 @@
                     return False
 
         group = record.value.get(self.name)
+        fields = {}
         if group is not None:
             group.signal_unconnect(group)
+            fields = group.fields
+        elif record.model_name == self.attrs['relation']:
+            fields = record.group.fields
+        parent_name = self.attrs.get('relation_field', '')
         group = Group(
                 self.attrs['relation'], fields, record.window, parent=record,
-                parent_name=self.attrs.get('relation_field', ''),
+                parent_name=parent_name,
+                child_name=self.name,
                 context=self.context,
                 parent_datetime_field=self.attrs.get('datetime_field'))
         if record.value.get(self.name):
@@ -532,12 +569,14 @@
             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
+        group.fields = fields
         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)
-        group.signal_connect(group,
-                'group-changed', self._group_changed)
+        group.signal_connect(group, 'group-changed', self._group_changed)
+        group.signal_connect(group, 'group-list-changed', self._group_list_changed)
+        group.signal_connect(group, 'group-cleared', self._group_cleared)
         return True
 
     def set_on_change(self, record, value):
@@ -604,7 +643,7 @@
             if not record2.loaded:
                 continue
             if not record2.validate():
-                if not record2.is_modified():
+                if not record2.modified:
                     record.value[self.name].remove(record2)
                 else:
                     res = False
@@ -640,10 +679,16 @@
     def set(self, record, value, modified=False):
         from group import Group
         group = record.value.get(self.name)
+        fields = {}
         if group is not None:
             group.signal_unconnect(group)
+            fields = group.fields
+        elif record.model_name == self.attrs['relation']:
+            fields = record.group.fields
+        parent_name = self.attrs.get('relation_field', '')
         group = Group(self.attrs['relation'], {}, record.window,
-                parent=record, parent_name=self.attrs.get('relation_field', ''),
+                parent=record, parent_name=parent_name,
+                child_name=self.name,
                 context=self.context,
                 parent_datetime_field=self.attrs.get('datetime_field'))
         if record.value.get(self.name):
@@ -651,11 +696,14 @@
             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.fields = fields
+        group.load(value, display=False)
         group.signal_connect(group, 'group-changed', self._group_changed)
+        group.signal_connect(group, 'group-list-changed', self._group_list_changed)
+        group.signal_connect(group, 'group-cleared', self._group_cleared)
         if modified:
-            record.modified = True
             record.modified_fields.setdefault(self.name)
+            record.signal('record-modified')
 
 
 class ReferenceField(CharField):
@@ -677,16 +725,16 @@
 
     def set_client(self, record, value, force_change=False):
         internal = record.value.get(self.name)
-        prev_modified = record.modified
+        prev_modified_fields = record.modified_fields.copy()
         self.set(record, value)
         if (internal or False) != (record.value[self.name] or False):
-            record.modified = True
             record.modified_fields.setdefault(self.name)
+            record.signal('record-modified')
             try:
                 self.sig_changed(record)
             except Exception:
                 record.value[self.name] = internal
-                record.modified = prev_modified
+                record.modified_fields = prev_modified_fields
                 return
             record.validate(softvalidation=True)
             record.signal('record-changed')
@@ -725,8 +773,8 @@
             if self.name + '.rec_name' in record.value:
                 del record.value[self.name + '.rec_name']
         if modified:
-            record.modified = True
             record.modified_fields.setdefault(self.name)
+            record.signal('record-modified')
 
 TYPES = {
     'char': CharField,
diff -r fcba908c65ca tryton/gui/window/view_form/model/group.py
--- a/tryton/gui/window/view_form/model/group.py	Thu Feb 03 01:04:30 2011 +0100
+++ b/tryton/gui/window/view_form/model/group.py	Fri Feb 04 21:58:42 2011 +0100
@@ -1,5 +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 copy
 import tryton.rpc as rpc
 from record import Record
 from field import Field, O2MField
@@ -10,8 +11,8 @@
 class Group(SignalEvent, list):
 
     def __init__(self, model_name, fields, window, ids=None, parent=None,
-            parent_name='', context=None, domain=None, readonly=False,
-            parent_datetime_field=None):
+            parent_name='', child_name='', context=None, domain=None,
+            readonly=False, parent_datetime_field=None):
         super(Group, self).__init__()
         if domain is None:
             domain = []
@@ -20,6 +21,7 @@
         self.__window = window
         self.parent = parent
         self.parent_name = parent_name
+        self.child_name = child_name
         self.parent_datetime_field = parent_datetime_field
         self._context = context or {}
         self.model_name = model_name
@@ -34,6 +36,7 @@
         if self._context.get('_datetime'):
             self.readonly = True
         self.__id2record = {}
+        self.__field_childs = None
 
     def __get_window(self):
         return self.__window
@@ -46,6 +49,7 @@
     window = property(__get_window, __set_window)
 
     def insert(self, pos, record):
+        assert record.group is self
         if pos >= 1:
             self.__getitem__(pos - 1).next[id(self)] = record
         if pos < self.__len__():
@@ -55,16 +59,17 @@
         super(Group, self).insert(pos, record)
         self.__id2record[record.id] = record
         if not self.lock_signal:
-            self.signal('group-list-changed', ('record-added', pos))
+            self.signal('group-list-changed', ('record-added', record))
 
     def append(self, record):
+        assert record.group is self
         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))
+            self.signal('group-list-changed', ('record-added', record))
 
     def _remove(self, record):
         idx = self.index(record)
@@ -74,16 +79,16 @@
                         self.__getitem__(idx + 1)
             else:
                 self.__getitem__(idx - 1).next[id(self)] = None
+        if not self.lock_signal:
+            self.signal('group-list-changed', ('record-removed', record))
         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):
         if not self.lock_signal:
-            for record in self:
-                self.signal('group-list-changed', ('record-removed', 0))
-        del self[:]
+            for record in self[:]:
+                self.signal('group-list-changed', ('record-removed', record))
+                self.pop(0)
         if not self.lock_signal:
             self.signal('group-list-changed', ('group-cleared',))
         self.__id2record = {}
@@ -91,8 +96,8 @@
         self.record_deleted = []
 
     def move(self, record, pos):
-        self.lock_signal = True
-        if self.__len__() > pos:
+        #self.lock_signal = True
+        if self.__len__() > pos > 0:
             idx = self.index(record)
             self._remove(record)
             if pos > idx:
@@ -101,7 +106,7 @@
         else:
             self._remove(record)
             self.append(record)
-        self.lock_signal = False
+        #self.lock_signal = False
 
     def __setitem__(self, i, value):
         super(Group, self).__setitem__(i, value)
@@ -115,7 +120,7 @@
         for name, attr in fields.iteritems():
             field = Field(attr['type'])
             attr['name'] = name
-            self.fields[name] = field(self, attr)
+            self.fields[name] = field(attr)
             if isinstance(self.fields[name], O2MField) \
                     and '_datetime' in self._context:
                 self.fields[name].context.update({
@@ -125,15 +130,15 @@
     def save(self):
         for record in self:
             saved = record.save()
-            self.writen(saved)
 
     def writen(self, ids):
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        ids = [x for x in self.on_write_ids(ids) or [] if x not in ids]
-        if not ids:
-            return
-        self.reload(ids)
+        # FIXME
+        #if isinstance(ids, (int, long)):
+        #    ids = [ids]
+        #ids = [x for x in self.on_write_ids(ids) or [] if x not in ids]
+        #if not ids:
+        #    return
+        #self.reload(ids)
         return ids
 
     def reload(self, ids):
@@ -156,7 +161,15 @@
                 res += res2
         return list({}.fromkeys(res))
 
-    def load(self, ids, display=True, modified=False):
+    def reload(self, ids, display=True):
+        if not ids:
+            self.clear()
+            return
+        id2record = self.__id2record.copy()
+        self.clear()
+        self.load(ids, display=display, id2record=id2record)
+
+    def load(self, ids, display=True, modified=False, id2record=None):
         if not ids:
             return True
 
@@ -167,16 +180,19 @@
 
         if len(ids) > 1:
             self.lock_signal = True
+        if id2record is None:
+            id2record = {}
         new_records = []
-        for id in ids:
-            new_record = Record(self.model_name, id, self.window,
-                parent=self.parent, parent_name=self.parent_name, group=self)
-            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 id_ in ids:
+            record = id2record.get(id_)
+            if record is None:
+                record = Record(self.model_name, id_, self.window, group=self)
+                record.signal_connect(self, 'record-changed',
+                    self._record_changed)
+                record.signal_connect(self, 'record-modified',
+                    self._record_modified)
+            new_records.append(record)
+            self.append(record)
         for record in self.record_removed[:]:
             if record.id in ids:
                 self.record_removed.remove(record)
@@ -207,26 +223,20 @@
     context = property(_get_context)
 
     def add(self, record, position=-1, modified=True):
-        if not record.group is self:
-            fields = {}
-            for i in record.group.fields:
-                fields[record.group.fields[i].attrs['name']] = \
-                        record.group.fields[i].attrs
-            self.add_fields(fields)
+        if record.group is not self:
+            record.signal_unconnect(record.group)
             record.group = self
-
+            record.window = self.window
+            record.signal_connect(self, 'record-changed', self._record_changed)
+            record.signal_connect(self, 'record-modified', self._record_modified)
         if position == -1:
             self.append(record)
         else:
             self.insert(position, record)
         self.current_idx = position
-        record.parent = self.parent
-        record.parent_name = self.parent_name
-        record.window = self.window
         if modified:
-            record.modified = True
-        record.signal_connect(self, 'record-changed', self._record_changed)
-        record.signal_connect(self, 'record-modified', self._record_modified)
+            record.modified_fields.setdefault('id')
+            record.signal('record-modified')
         return record
 
     def set_sequence(self, field='sequence'):
@@ -239,9 +249,9 @@
                 else:
                     index = record[field].get(record)
 
-    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)
+    def new(self, default=True, domain=None, context=None, signal=True,
+            obj_id=None):
+        record = Record(self.model_name, obj_id, self.window, group=self)
         record.signal_connect(self, 'record-changed', self._record_changed)
         record.signal_connect(self, 'record-modified', self._record_modified)
         if default:
@@ -261,9 +271,11 @@
             else:
                 self.record_deleted.append(self[idx])
         if record.parent:
-            record.parent.modified = True
+            record.parent.modified_fields.setdefault('id')
+            record.parent.signal('record-modified')
         if modified:
-            record.modified = True
+            record.modified_fields.setdefault('id')
+            record.signal('record-modified')
         self._remove(self[idx])
 
         if len(self):
@@ -348,8 +360,6 @@
         super(Group, self).destroy()
         self.__window = None
         self.parent = None
-        for field in self.fields.itervalues():
-            field.destroy()
         self.fields = {}
         self.record_removed = None
         self.record_deleted = None
@@ -357,3 +367,19 @@
         for record in self:
             record.destroy()
         self[:] = []
+
+    def get_by_path(self, path):
+        'return record by path'
+        group = self
+        record = None
+        for child_name, id_ in path:
+            record = group.get(id_)
+            if not record:
+                return None
+            if not child_name:
+                continue
+            record[child_name]
+            group = record.value.get(child_name)
+            if not isinstance(group, Group):
+                return None
+        return record
diff -r fcba908c65ca tryton/gui/window/view_form/model/record.py
--- a/tryton/gui/window/view_form/model/record.py	Thu Feb 03 01:04:30 2011 +0100
+++ b/tryton/gui/window/view_form/model/record.py	Fri Feb 04 21:58:42 2011 +0100
@@ -14,29 +14,25 @@
 
     id = -1
 
-    def __init__(self, model_name, obj_id, window, group=None, parent=None,
-            parent_name=''):
+    def __init__(self, model_name, obj_id, window, group=None):
         super(Record, self).__init__()
-        self.window = window
+        self.__window = window
         self.model_name = model_name
         self.id = obj_id or Record.id
         if self.id < 0:
             Record.id -= 1
         self._loaded = set()
-        self.parent = parent
-        self.parent_name = parent_name
         self.group = group
         if group is not None:
             assert model_name == group.model_name
         self.state_attrs = {}
-        self.__modified = False
         self.modified_fields = {}
         self._timestamp = None
         self.attachment_count = -1
         self.next = {} # Used in Group list
         self.value = {}
 
-    def __getitem__(self, name):
+    def __getitem__(self, name, raise_exception=False):
         if name not in self._loaded and self.id > 0:
             ids =  [self.id]
             if name == '*':
@@ -67,6 +63,7 @@
                         if field.attrs.get('loading', 'eager') == 'eager']
             else:
                 fields = self.group.fields.keys()
+            fields = [fname for fname in fields if fname not in self._loaded]
             fields.extend(('%s.rec_name' % fname for fname in fields[:]
                     if self.group.fields[fname].attrs['type']
                     in ('many2one', 'one2one', 'reference')))
@@ -77,6 +74,8 @@
             try:
                 values = rpc.execute(*args)
             except Exception, exception:
+                if raise_exception:
+                    raise
                 values = common.process_exception(exception, self.window, *args)
                 if not values:
                     values = [{'id': x} for x in ids]
@@ -94,20 +93,67 @@
         return self.group.fields.get(name, False)
 
     def __repr__(self):
-        return '<Record %s@%s>' % (self.id, self.model_name)
+        return '<Record %s@%s at %s>' % (self.id, self.model_name, id(self))
 
-    def get_modified(self):
-        return self.__modified
+    @property
+    def modified(self):
+        return bool(self.modified_fields)
+
+    @property
+    def parent(self):
+        return self.group.parent
+
+    @property
+    def parent_name(self):
+        return self.group.parent_name
 
     def set_modified(self, value):
-        self.__modified = value
         if value:
             self.signal('record-modified')
 
-    modified = property(get_modified, set_modified)
+    def children_group(self, field_name, check_load=True):
+        if not field_name:
+            return []
+        if check_load:
+            self._check_load([field_name])
+        group = self.value.get(field_name)
+        if group is False or group is None:
+            return []
+        # XXX to check
+        if id(group.fields) != id(self.group.fields):
+            self.group.fields.update(group.fields)
+            group.fields = self.group.fields
+        group.on_write = self.group.on_write
+        group.readonly = self.group.readonly
+        if group.window != self.window:
+            group.window = self.window
+        return group
 
-    def is_modified(self):
-        return self.modified
+    def get_path(self, group):
+        path = []
+        i = self
+        child_name = ''
+        while i:
+            path.append((child_name, i.id))
+            if i.group is group:
+                break
+            child_name = i.group.child_name
+            i = i.parent
+        path.reverse()
+        return tuple(path)
+
+    def _get_window(self):
+        return self.__window
+
+    def _set_window(self, window):
+        self.__window = window
+        for fieldname, value in self.value.iteritems():
+            if fieldname not in self.group.fields:
+                continue
+            if isinstance(self.group.fields[fieldname], field.O2MField):
+                value.window = window
+
+    window = property(_get_window, _set_window)
 
     def fields_get(self):
         return self.group.fields
@@ -167,40 +213,48 @@
         return result
 
     def save(self, force_reload=True):
-        if self.id < 0:
-            value = self.get(get_readonly=True)
-            args = ('model', self.model_name, 'create', value,
-                self.context_get())
-            try:
-                res = rpc.execute(*args)
-            except Exception, exception:
-                res = common.process_exception(exception, self.window, *args)
-                if not res:
-                    return False
-            old_id = self.id
-            self.id = res
-            self.group.id_changed(old_id)
-        else:
-            if not self.is_modified():
-                return self.id
-            self._check_load()
-            value = self.get(get_readonly=True, get_modifiedonly=True)
-            context = self.context_get()
-            context = context.copy()
-            context['_timestamp'] = self.get_timestamp()
-            args = ('model', self.model_name, 'write', [self.id], value,
-                context)
-            try:
-                if not rpc.execute(*args):
-                    return False
-            except Exception, exception:
-                if not common.process_exception(exception, self.window, *args):
-                    return False
-        self._loaded.clear()
-        if force_reload:
-            self.reload()
-        if self.group:
-            self.group.writen(self.id)
+        if self.id < 0 or self.modified:
+            if self.id < 0:
+                value = self.get(get_readonly=True)
+                args = ('model', self.model_name, 'create', value,
+                    self.context_get())
+                try:
+                    res = rpc.execute(*args)
+                except Exception, exception:
+                    res = common.process_exception(exception, self.window,
+                            *args)
+                    if not res:
+                        return False
+                old_id = self.id
+                self.id = res
+                self.group.id_changed(old_id)
+            elif self.modified:
+                self._check_load()
+                value = self.get(get_readonly=True, get_modifiedonly=True,
+                        check_load=False)
+                if value:
+                    context = self.context_get()
+                    context = context.copy()
+                    context['_timestamp'] = self.get_timestamp()
+                    args = ('model', self.model_name, 'write', [self.id],
+                            value, context)
+                    try:
+                        if not rpc.execute(*args):
+                            return False
+                    except Exception, exception:
+                        res = common.process_exception(exception, self.window,
+                                *args)
+                        if not res:
+                            return False
+            self._loaded.clear()
+            self.modified_fields = {}
+            if force_reload:
+                self.reload()
+            if self.group:
+                self.group.writen(self.id)
+        if self.parent:
+            self.parent.modified_fields.pop(self.group.child_name, None)
+            self.parent.save(force_reload=force_reload)
         return self.id
 
     def default_get(self, domain=None, context=None):
@@ -213,6 +267,8 @@
                 vals = common.process_exception(exception, self.window, *args)
                 if not vals:
                     return
+            if self.parent_name and self.parent:
+                vals[self.parent_name] = self.parent.id
             self.set_default(vals)
 
     def rec_name(self):
@@ -307,9 +363,6 @@
         for fieldname, value in later.iteritems():
             self.group.fields[fieldname].set(self, value, modified=modified)
             self._loaded.add(fieldname)
-        self.modified = modified
-        if not self.modified:
-            self.modified_fields = {}
         if signal:
             self.signal('record-changed')
 
@@ -448,7 +501,6 @@
     def destroy(self):
         super(Record, self).destroy()
         self.window = None
-        self.parent = None
         self.group = None
         self.value = None
         self.next = None
diff -r fcba908c65ca tryton/gui/window/view_form/screen/screen.py
--- a/tryton/gui/window/view_form/screen/screen.py	Thu Feb 03 01:04:30 2011 +0100
+++ b/tryton/gui/window/view_form/screen/screen.py	Fri Feb 04 21:58:42 2011 +0100
@@ -89,10 +89,9 @@
                                 'tree', ctx)
                     except Exception:
                         return
-                self.filter_widget = Form(self.fields_view_tree['arch'],
-                        self.fields_view_tree['fields'], self.model_name,
-                        self.window, self.domain, (self, self.search_filter),
-                        self.context)
+                self.filter_widget = Form(self.fields_view_tree,
+                        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.search_prev, self.search_next)
@@ -223,10 +222,16 @@
         except Exception:
             offset = 0
         try:
-            pos = self.group.index(record)
-        except Exception:
-            pos = -1
-        self.signal('record-message', (pos + offset, len(self.group) + offset,
+            pos = self.group.index(record) + offset + 1
+        except ValueError:
+            pos = []
+            i = record
+            while i:
+                pos.append(i.group.index(i) + 1)
+                i = i.parent
+            pos.reverse()
+            pos = tuple(pos)
+        self.signal('record-message', (pos, len(self.group) + offset,
             self.search_count, record and record.id))
         attachment_count = 0
         if record and record.attachment_count > 0:
@@ -269,7 +274,8 @@
 
     def switch_view(self, view_type=None, default=True, context=None):
         self.current_view.set_value()
-        if self.current_record and self.current_record not in self.group:
+        if (self.current_record and
+                self.current_record not in self.current_record.group):
             self.current_record = None
         fields = self.current_view.get_fields()
         if self.current_record and not self.current_record.validate(fields):
@@ -323,17 +329,19 @@
                 view = common.process_exception(exception, self.window, *args)
                 if not view:
                     return
-            return self.add_view(view['arch'], view['fields'], display,
+            return self.add_view(view, display,
                     toolbar=view.get('toolbar', False), context=context)
 
-    def add_view(self, arch, fields, display=False, toolbar=None, context=None):
+    def add_view(self, view, display=False, toolbar=None, context=None):
         if toolbar is None:
             toolbar = {}
+        arch = view['arch']
+        fields = view['fields']
 
         xml_dom = xml.dom.minidom.parseString(arch)
         for node in xml_dom.childNodes:
             if node.localName == 'tree':
-                self.fields_view_tree = {'arch': arch, 'fields': fields}
+                self.fields_view_tree = view
             break
 
         if node.localName == 'tree':
@@ -344,22 +352,14 @@
             if field not in self.group.fields:
                 fields[field]['loading'] = loading
 
+        children_field = view.get('field_childs')
+
         from tryton.gui.window.view_form.view.widget_parse import WidgetParse
-        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)
 
-        if self.exclude_field:
-            if self.exclude_field in self.group.fields:
-                field = self.group.fields[self.exclude_field]
-                field.attrs['states'] = {'invisible': True}
-                field.attrs['readonly'] = True
-                field.attrs['invisible'] = True
-                field.attrs['tree_invisible'] = True
-                field.attrs['exclude_field'] = True
-
         parser = WidgetParse(parent=self.parent, window=self.window)
-        view = parser.parse(self, xml_dom, self.group.fields, toolbar=toolbar)
+        view = parser.parse(self, xml_dom, self.group.fields, toolbar=toolbar,
+                children_field=children_field)
 
         self.views.append(view)
 
@@ -405,8 +405,12 @@
         ctx.update(rpc.CONTEXT)
         ctx.update(self.context)
         ctx.update(context)
-        record = self.group.new(default, self.domain, ctx)
-        self.group.add(record, self.new_model_position())
+        if self.current_record:
+            group = self.current_record.group
+        else:
+            group = self.group
+        record = group.new(default, self.domain, ctx)
+        group.add(record, self.new_model_position())
         self.current_record = record
         fields = None
         if self.current_view:
@@ -443,6 +447,7 @@
         self.current_view.set_value()
         obj_id = False
         fields = self.current_view.get_fields()
+        path = self.current_record.get_path(self.group)
         if self.current_record.validate(fields):
             obj_id = self.current_record.save(force_reload=True)
         else:
@@ -450,20 +455,11 @@
             self.current_view.display()
             return False
         if self.current_view.view_type == 'tree':
-            for record in self.group:
-                if record.is_modified():
-                    if record.validate(fields):
-                        obj_id = record.save(force_reload=True)
-                    else:
-                        self.current_view.set_cursor()
-                        self.current_record = record
-                        self.current_view.set_cursor()
-                        self.display()
-                        return False
-            self.current_view.set_cursor()
+            self.group.save()
+        if path:
+            path = path[:-1] + ((path[-1][0], obj_id),)
+        self.current_record = self.group.get_by_path(path)
         self.display()
-        if self.current_record not in self.group:
-            self.group.add(self.current_record, modified=False)
         self.request_set()
         return obj_id
 
@@ -483,14 +479,14 @@
                 includeid=includeid, check_load=check_load,
                 get_modifiedonly=get_modifiedonly)
 
-    def is_modified(self):
+    def modified(self):
         self.current_view.set_value()
         res = False
         if self.current_view.view_type != 'tree':
-            res = self.current_record and self.current_record.is_modified()
+            res = self.current_record and self.current_record.modified
         else:
             for record in self.group:
-                if record.is_modified():
+                if record.modified:
                     res = True
         return res
 
@@ -527,35 +523,44 @@
                 if not res:
                     return False
             self.current_view.set_cursor()
-            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_record = None
-            if reload_ids:
-                self.group.reload(reload_ids)
+            record = self.current_record
+            idx = record.group.index(record)
+            record.group.remove(record, remove=remove)
+
+            if delete:
+                if (record.parent and
+                        record.parent.model_name == record.model_name):
+                    record.parent.save()
+
+            if record.group:
+                idx = min(idx, len(record.group) - 1)
+                self.current_record = record.group[idx]
+            elif (record.parent and
+                    record.parent.model_name == record.model_name):
+                self.current_record = record.parent
+            else:
+                self.current_record = None
+            # FIXME
+            #if reload_ids:
+            #    self.group.reload(reload_ids)
             self.display()
             res = True
         if self.current_view.view_type == 'tree':
-            ids = self.current_view.sel_ids_get()
-            if delete and ids:
+            records = self.current_view.selected_records()
+            if delete and records:
                 context = {}
                 context.update(rpc.CONTEXT)
                 context.update(self.context)
                 context['_timestamp'] = {}
-                for record_id in ids:
-                    record = self.group.get(record_id)
+                for record in records:
                     context['_timestamp'].update(record.get_timestamp())
-                reload_ids = self.group.on_write_ids(ids)
+                reload_ids = self.group.on_write_ids([x.id for x in records])
                 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)
+                    for record in records:
+                        if record.id in reload_ids:
+                            reload_ids.remove(record.id)
+                args = ('model', self.model_name, 'delete',
+                        [x.id for x in records], context)
                 try:
                     res = rpc.execute(*args)
                 except Exception, exception:
@@ -563,24 +568,33 @@
                             *args)
                 if not res:
                     return False
-            sel_records = self.current_view.selected_records()
-            if not sel_records:
+            if not records:
                 return True
-            idx = self.group.index(sel_records[0])
-            for record in sel_records:
+            path = self.current_view.store.on_get_path(records[0])
+            for record in records:
                 # set current model to None to prevent __select_changed
                 # to save the previous_model as it can be already deleted.
                 self.current_record = None
-                self.group.remove(record, remove=remove, signal=False)
+                record.group.remove(record, remove=remove, signal=False)
+
             # send record-changed only once
             record.signal('record-changed')
-            if self.group:
-                idx = min(idx, len(self.group) - 1)
-                self.current_record = self.group[idx]
+
+            if delete:
+                for record in records:
+                    if record.parent:
+                        record.parent.save()
+
+            if path[-1] > 0:
+                path = path[:-1] + (path[-1] - 1,)
             else:
-                self.current_record = None
-            if reload_ids:
-                self.group.reload(reload_ids)
+                path = path[:-1]
+            if path:
+                iter_ = self.current_view.store.get_iter(path)
+                self.current_record = self.current_view.store.get_value(iter_, 0)
+            # FIXME
+            #if reload_ids:
+                #self.group.reload(reload_ids)
             self.current_view.set_cursor()
             self.display()
             res = True
@@ -619,54 +633,78 @@
                 self.current_view.set_cursor(reset_view=False)
 
     def display_next(self):
-        self.current_view.set_value()
-        self.current_view.set_cursor(reset_view=False)
-        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.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.group):
-                idx = len(self.group) - 1
-            self.current_record = self.group[idx]
+        view = self.current_view
+        view.set_value()
+        view.set_cursor(reset_view=False)
+        if view.view_type == 'tree':
+            start, end = view.widget_tree.get_visible_range()
+            vadjustment = view.widget_tree.get_vadjustment()
+            vadjustment.value = vadjustment.value + vadjustment.page_increment
+            store = view.store
+            iter_ = store.get_iter(end)
+            self.current_record = store.get_value(iter_, 0)
+        elif self.current_record.group:
+            group = self.current_record.group
+            record = self.current_record
+            while group:
+                children = record.children_group(view.children_field)
+                if children:
+                    record = children[0]
+                    break
+                idx = group.index(record) + 1
+                if idx < len(group):
+                    record = group[idx]
+                    break
+                parent = record.parent
+                if not parent:
+                    break
+                next = parent.next.get(id(parent.group))
+                while not next:
+                    parent = parent.parent
+                    if not parent:
+                        break
+                    next = parent.next.get(id(parent.group))
+                if not next:
+                    break
+                record = next
+                break
+            self.current_record = record
         else:
-            self.current_record = len(self.group) \
-                    and self.group[0]
-        self.current_view.set_cursor(reset_view=False)
-        self.current_view.display()
+            self.current_record = len(self.group) and self.group[0]
+        view.set_cursor(reset_view=False)
+        view.display()
 
     def display_prev(self):
-        self.current_view.set_value()
-        self.current_view.set_cursor(reset_view=False)
-        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()
-                if range:
-                    start, end = range
-                    inc += end[0] - start[0]
-                    if inc >= 4 and start[0] > 0:
-                        inc -= 3
-                    vadjustment = \
-                            self.current_view.widget_tree.get_vadjustment()
-                    if vadjustment.value:
-                        vadjustment.value = vadjustment.value - \
-                                vadjustment.page_increment
-            idx = self.group.index(self.current_record) - inc
-            if idx < 0:
-                idx = 0
-            self.current_record = self.group[idx]
+        view = self.current_view
+        view.set_value()
+        view.set_cursor(reset_view=False)
+        if view.view_type == 'tree':
+            start, end = view.widget_tree.get_visible_range()
+            vadjustment = view.widget_tree.get_vadjustment()
+            vadjustment.value = vadjustment.value - vadjustment.page_increment
+            store = view.store
+            iter_ = store.get_iter(start)
+            self.current_record = store.get_value(iter_, 0)
+        elif self.current_record.group:
+            group = self.current_record.group
+            record = self.current_record
+            idx = group.index(record) - 1
+            if idx >= 0:
+                record = group[idx]
+                children = True
+                while children:
+                    children = record.children_group(view.children_field)
+                    if children:
+                        record = children[-1]
+            else:
+                parent = record.parent
+                if parent:
+                    record = parent
+            self.current_record = record
         else:
-            self.current_record = len(self.group) \
-                    and self.group[-1]
-        self.current_view.set_cursor(reset_view=False)
-        self.current_view.display()
+            self.current_record = len(self.group) and self.group[-1]
+        view.set_cursor(reset_view=False)
+        view.display()
 
     def sel_ids_get(self):
         return self.current_view.sel_ids_get()
diff -r fcba908c65ca tryton/gui/window/view_form/view/form.py
--- a/tryton/gui/window/view_form/view/form.py	Thu Feb 03 01:04:30 2011 +0100
+++ b/tryton/gui/window/view_form/view/form.py	Fri Feb 04 21:58:42 2011 +0100
@@ -15,9 +15,10 @@
 class ViewForm(ParserView):
 
     def __init__(self, window, screen, widget, children=None,
-            buttons=None, toolbar=None, notebooks=None, cursor_widget=''):
+            buttons=None, toolbar=None, notebooks=None, cursor_widget='',
+            children_field=None):
         super(ViewForm, self).__init__(window, screen, widget, children,
-                buttons, toolbar, notebooks, cursor_widget)
+                buttons, toolbar, notebooks, cursor_widget, children_field)
         self.view_type = 'form'
 
         for button in self.buttons:
@@ -213,7 +214,10 @@
         record = self.screen.current_record
         if record:
             # Force to set fields in record
-            for field in record.group.fields:
+            fields = [(name, field.attrs.get('loading', 'eager'))
+                    for name, field in record.group.fields.iteritems()]
+            fields.sort(lambda x, y: cmp(y[1], x[1]))
+            for field, _ in fields:
                 record[field].get(record, check_load=False)
         for name, widgets in self.widgets.iteritems():
             field = None
diff -r fcba908c65ca tryton/gui/window/view_form/view/form_gtk/one2many.py
--- a/tryton/gui/window/view_form/view/form_gtk/one2many.py	Thu Feb 03 01:04:30 2011 +0100
+++ b/tryton/gui/window/view_form/view/form_gtk/one2many.py	Fri Feb 04 21:58:42 2011 +0100
@@ -319,17 +319,17 @@
 
     def _sig_label(self, screen, signal_data):
         name = '_'
-        if signal_data[0] >= 0:
-            name = str(signal_data[0] + 1)
+        if signal_data[0] >= 1:
+            name = str(signal_data[0])
             self.but_open.set_sensitive(True)
             self.but_del.set_sensitive(not self._readonly)
             if self.attrs.get('add_remove'):
                 self.but_remove.set_sensitive(not self._readonly)
-            if signal_data[0] + 1 < signal_data[1]:
+            if signal_data[0] < signal_data[1]:
                 self.but_next.set_sensitive(True)
             else:
                 self.but_next.set_sensitive(False)
-            if signal_data[0] > 0:
+            if signal_data[0] > 1:
                 self.but_pre.set_sensitive(True)
             else:
                 self.but_pre.set_sensitive(False)
@@ -373,7 +373,7 @@
 
     def set_value(self, record, field):
         self.screen.current_view.set_value()
-        if self.screen.is_modified():
-            record.modified = True
+        if self.screen.modified(): # TODO check if required
             record.modified_fields.setdefault(field.name)
+            record.signal('record-modified')
         return True
diff -r fcba908c65ca tryton/gui/window/view_form/view/form_gtk/parser.py
--- a/tryton/gui/window/view_form/view/form_gtk/parser.py	Thu Feb 03 01:04:30 2011 +0100
+++ b/tryton/gui/window/view_form/view/form_gtk/parser.py	Fri Feb 04 21:58:42 2011 +0100
@@ -320,8 +320,7 @@
             attrs = common.node_attributes(node)
             if not cursor_widget:
                 if attrs.get('name') and fields.get(attrs['name']) \
-                        and not fields[attrs['name']].attrs.get('exclude_field',
-                                False):
+                        and attrs['name'] != self.screen.exclude_field:
                     cursor_widget = attrs.get('name')
             if node.localName == 'image':
                 icon = Image(attrs)
@@ -356,6 +355,9 @@
             elif node.localName == 'label':
                 text = attrs.get('string', '')
                 if 'name' in attrs and attrs['name'] in fields:
+                    if attrs['name'] == self.screen.exclude_field:
+                        container.empty_add(int(attrs.get('colspan', 1)))
+                        continue
                     for attr_name in ('states', 'invisible'):
                         if attr_name not in attrs \
                                 and attr_name in fields[attrs['name']].attrs:
@@ -468,6 +470,8 @@
                     angle = 0
                 text = attrs.get('string', '')
                 if 'name' in attrs and attrs['name'] in fields:
+                    if attrs['name'] == self.screen.exclude_field:
+                        continue
                     for attr_name in ('states', 'invisible'):
                         if attr_name in fields[attrs['name']].attrs:
                             attrs[attr_name] = \
@@ -507,6 +511,9 @@
 
             elif node.localName == 'field':
                 name = str(attrs['name'])
+                if name == self.screen.exclude_field:
+                    container.empty_add(int(attrs.get('colspan', 1)))
+                    continue
                 if name not in fields:
                     container.empty_add(int(attrs.get('colspan', 1)))
                     log = logging.getLogger('view')
@@ -567,6 +574,9 @@
                 button_list += buttons
                 text = ''
                 if 'name' in attrs and attrs['name'] in fields:
+                    if attrs['name'] == self.screen.exclude_field:
+                        container.empty_add(int(attrs.get('colspan', 1)))
+                        continue
                     for attr_name in ('states', 'invisible'):
                         if attr_name in fields[attrs['name']].attrs:
                             attrs[attr_name] = fields[attrs['name']
diff -r fcba908c65ca tryton/gui/window/view_form/view/graph.py
--- a/tryton/gui/window/view_form/view/graph.py	Thu Feb 03 01:04:30 2011 +0100
+++ b/tryton/gui/window/view_form/view/graph.py	Fri Feb 04 21:58:42 2011 +0100
@@ -6,9 +6,10 @@
 class ViewGraph(ParserView):
 
     def __init__(self, window, screen, widget, children=None,
-            buttons=None, toolbar=None, notebooks=None, cursor_widget=None):
+            buttons=None, toolbar=None, notebooks=None, cursor_widget=None,
+            children_field=None):
         super(ViewGraph, self).__init__(window, screen, widget, children,
-                buttons, toolbar, notebooks, cursor_widget)
+                buttons, toolbar, notebooks, cursor_widget, children_field)
         self.view_type = 'graph'
         self.widgets = children
 
diff -r fcba908c65ca tryton/gui/window/view_form/view/interface.py
--- a/tryton/gui/window/view_form/view/interface.py	Thu Feb 03 01:04:30 2011 +0100
+++ b/tryton/gui/window/view_form/view/interface.py	Fri Feb 04 21:58:42 2011 +0100
@@ -17,7 +17,8 @@
 class ParserView(object):
 
     def __init__(self, window, screen, widget, children=None, buttons=None,
-            toolbar=None, notebooks=None, cursor_widget=None):
+            toolbar=None, notebooks=None, cursor_widget=None,
+            children_field=None):
         self.window = window
         self.screen = screen
         self.widget = widget
@@ -30,6 +31,7 @@
             notebooks = []
         self.notebooks = notebooks
         self.cursor_widget = cursor_widget
+        self.children_field = children_field
 
     def get_fields(self):
         return self.children.keys()
diff -r fcba908c65ca tryton/gui/window/view_form/view/list.py
--- a/tryton/gui/window/view_form/view/list.py	Thu Feb 03 01:04:30 2011 +0100
+++ b/tryton/gui/window/view_form/view/list.py	Fri Feb 04 21:58:42 2011 +0100
@@ -2,6 +2,11 @@
 #this repository contains the full copyright notices and license terms.
 import gobject
 import gtk
+import sys
+if sys.version_info < (2, 6):
+    import simplejson as json
+else:
+    import json
 import tryton.rpc as rpc
 import locale
 from interface import ParserView
@@ -17,28 +22,57 @@
 
 _ = gettext.gettext
 
+from functools import wraps
+def print_exception(f):
+    @wraps(f)
+    def wrapper(*args, **kwargs):
+        try:
+            return f(*args, **kwargs)
+        except Exception, e:
+            print "Exception:", e
+            raise
+    return wrapper
+
 
 class AdaptModelGroup(gtk.GenericTreeModel):
 
-    def __init__(self, group):
+    def __init__(self, group, children_field=None):
         super(AdaptModelGroup, self).__init__()
         self.group = group
         self.last_sort = None
         self.sort_asc = True
         self.set_property('leak_references', False)
+        self.children_field = children_field
+        self.__removed = None # XXX dirty hack to allow update of has_child
 
-    def added(self, modellist, 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)))
+    def added(self, group, record):
+        if group is self.group:
+            path = self.on_get_path(record)
+            iter_ = self.get_iter(path)
+            self.row_inserted(path, iter_)
+            if record.children_group(self.children_field):
+                self.row_has_child_toggled(path, iter_)
+            if (record.parent and
+                    record.group is not self.group):
+                path = self.on_get_path(record.parent)
+                iter_ = self.get_iter(path)
+                self.row_has_child_toggled(path, iter_)
 
     def cancel(self):
         pass
 
-    def removed(self, lst, position):
-        self.row_deleted(position)
-        self.invalidate_iters()
+    def removed(self, group, record):
+        if group is self.group:
+            path = self.on_get_path(record)
+            self.row_deleted(path)
+            if (record.parent and
+                    record.group != self.group and
+                    len(record.children_group(self.children_field)) <= 1):
+                path = self.on_get_path(record.parent)
+                iter_ = self.get_iter(path)
+                self.__removed = record # XXX check for thread
+                self.row_has_child_toggled(path, iter_)
+                self.__removed = None
 
     def append(self, model):
         self.group.add(model)
@@ -46,14 +80,44 @@
     def prepend(self, model):
         self.group.add(model, 0)
 
-    def remove(self, iter):
-        idx = self.get_path(iter)[0]
-        self.group.remove(self.group[idx])
+    def remove(self, iter_):
+        record = self.get_value(iter_, 0)
+        value.group.remove(record)
         self.invalidate_iters()
 
-    def move(self, path, position):
-        idx = path[0]
-        self.group.move(self.group[idx], position)
+    def __move(self, record, path, offset=0):
+        iter_ = self.get_iter(path)
+        pos = self.get_value(iter_, 0)
+        group = pos.group
+        if group is not record.group:
+            record.group.remove(record, remove=True)
+            group.add(record)
+            record.modified_fields.setdefault(record.parent_name)
+        group.move(record, (group.index(pos) + offset))
+
+    def move_before(self, record, path):
+        self.__move(record, path)
+
+    def move_after(self, record, path):
+        self.__move(record, path, 1)
+
+    def move_into(self, record, path):
+        iter_ = self.get_iter(path)
+        parent = self.get_value(iter_, 0)
+        group = parent.children_group(self.children_field, check_load=True)
+        if group is not record.group:
+            record.group.remove(record, remove=True)
+            group.add(record)
+            record.modified_fields.setdefault(record.parent_name or 'id')
+        group.move(record, 0)
+
+    def move_end(self, record):
+        group = self.group
+        if group != record.group:
+            record.group.remove(record, remove=True)
+            group.add(record)
+            record.modified_fields.setdefault(record.parent_name or 'id')
+        group.move(record, -1)
 
     def sort(self, ids):
         ids2pos = {}
@@ -86,68 +150,107 @@
     def __len__(self):
         return len(self.group)
 
+    @print_exception
     def on_get_flags(self):
-        return gtk.TREE_MODEL_LIST_ONLY
+        # XXX gtk.TREE_MODEL_ITERS_PERSIST
+        if not self.children_field:
+            return gtk.TREE_MODEL_LIST_ONLY
+        return 0
 
+    @print_exception
     def on_get_n_columns(self):
+        # XXX
         return 1
 
+    @print_exception
     def on_get_column_type(self, index):
+        # XXX
         return gobject.TYPE_PYOBJECT
 
-    def on_get_path(self, iter):
-        if iter in self.group:
-            return self.group.index(iter)
+    @print_exception
+    def on_get_path(self, iter_):
+        if isinstance(iter_, tuple):
+            return tuple(x[0] for x in iter_)
         else:
-            return 0
+            path = []
+            i = iter_
+            while i:
+                path.append(i.group.index(i))
+                if i.group is self.group:
+                    break
+                i = i.parent
+            path.reverse()
+            return tuple(path)
 
+    @print_exception
+    def on_get_tree_path(self, iter):
+        return self.on_get_path(iter)
+
+    @print_exception
     def on_get_iter(self, path):
-        if isinstance(path, tuple):
-            path = path[0]
-        if self.group is not None:
-            if path < len(self.group):
-                return self.group[path]
-            else:
+        group = self.group
+        record = None
+        for i in path:
+            if group is None or i >= len(group):
                 return None
-        else:
+            record = group[i]
+            group = record.children_group(self.children_field, check_load=True)
+        return record
+
+    @print_exception
+    def on_get_value(self, record, column):
+        return record
+
+    @print_exception
+    def on_iter_next(self, record):
+        if not record:
             return None
+        return record.next.get(id(record.group))
 
-    def on_get_value(self, node, column):
-        assert column == 0
-        return node
+    @print_exception
+    def on_iter_has_child(self, record):
+        children = record.children_group(self.children_field)
+        length = len(children)
+        if self.__removed and self.__removed in children:
+            length -= 1
+        return bool(length)
 
-    def on_iter_next(self, node):
-        try:
-            return node.next[id(self.group)]
-        except IndexError:
-            return None
-
-    def on_iter_has_child(self, node):
-        return False
-
-    def on_iter_children(self, node):
+    @print_exception
+    def on_iter_children(self, record):
+        if record.children_group(self.children_field):
+            return record.children_group(self.children_field)[0]
         return None
 
-    def on_iter_n_children(self, node):
-        if node is None:
+    @print_exception
+    def on_iter_n_children(self, record):
+        if record is None:
             return len(self.group)
-        return 0
+        return len(record.children_group(self.children_field))
 
-    def on_iter_nth_child(self, node, nth):
-        if node is None and self.group is not None:
-            return self.on_get_iter(0)
+    @print_exception
+    def on_iter_nth_child(self, record, nth):
+        if record is None and self.group is not None:
+            if nth < len(self.group):
+                return self.group[nth]
+            return None
+        if nth < len(record.children_group(self.children_field)):
+            return record.children_group(self.children_field)[nth]
         return None
 
-    def on_iter_parent(self, node):
-        return None
+    @print_exception
+    def on_iter_parent(self, record):
+        if record is None:
+            return None
+        return record.parent
 
 
 class ViewList(ParserView):
 
     def __init__(self, window, screen, widget, children=None, buttons=None,
-            toolbar=None, notebooks=None, cursor_widget=None):
+            toolbar=None, notebooks=None, cursor_widget=None,
+            children_field=None):
         super(ViewList, self).__init__(window, screen, widget, children,
-                buttons, toolbar, notebooks, cursor_widget)
+                buttons, toolbar, notebooks, cursor_widget, children_field)
         self.store = None
         self.view_type = 'tree'
 
@@ -246,10 +349,20 @@
         selection.set_mode(gtk.SELECTION_MULTIPLE)
         selection.connect('changed', self.__select_changed)
 
+        dnd = False
+        if self.children_field:
+            children_field = self.widget_tree.cells.get(self.children_field)
+            if children_field:
+                parent_name = children_field.attrs.get('relation_field')
+                dnd = parent_name in self.widget_tree.cells
+        elif self.widget_tree.sequence:
+            dnd = True
         # Disable DnD on mac until it is fully supported
-        if self.widget_tree.sequence \
-                and not (os.name == 'mac' \
-                    or (hasattr(os, 'uname') and os.uname()[0] == 'Darwin')):
+        if os.name == 'mac':
+            dnd = False
+        elif hasattr(os, 'uname') and os.uname()[0] == 'Darwin':
+            dnd = False
+        if dnd:
             self.widget_tree.enable_model_drag_source(gtk.gdk.BUTTON1_MASK,
                     [('MY_TREE_MODEL_ROW', gtk.TARGET_SAME_WIDGET, 0),],
                     gtk.gdk.ACTION_MOVE)
@@ -266,6 +379,9 @@
             self.widget_tree.connect('drag-data-delete', self.drag_data_delete)
 
         self.widget_tree.connect('key_press_event', self.on_keypress)
+        self.widget_tree.connect('test-expand-row', self.test_expand_row)
+        # XXX
+        self.widget_tree.set_expander_column(self.widget_tree.get_column(0))
 
     def get_fields(self):
         return [col.name for col in self.widget_tree.get_columns() if col.name]
@@ -303,16 +419,53 @@
             self.on_copy()
             return False
         if event.keyval in (gtk.keysyms.Down, gtk.keysyms.Up):
-            path, column = self.widget_tree.get_cursor()
+            path, column = widget.get_cursor()
             if not path:
                 return False
-            store = self.widget_tree.get_model()
+            model = widget.get_model()
             if event.keyval == gtk.keysyms.Down:
-                if path[0] ==  len(store) - 1:
+                iter_ = model.get_iter(path)
+                if (path[0] ==  len(model) - 1
+                        and not model.iter_next(iter_)
+                        and (not model.iter_has_child(iter_)
+                            or not widget.row_expanded(path))):
                     return True
             elif event.keyval == gtk.keysyms.Up:
-                if path[0] == 0:
+                if path == (0,):
                     return True
+        if event.keyval in (gtk.keysyms.Left, gtk.keysyms.Right):
+            selection = widget.get_selection()
+            model, paths = selection.get_selected_rows()
+            if event.keyval == gtk.keysyms.Left:
+                if len(paths) == 1:
+                    path, = paths
+                    if not widget.row_expanded(path):
+                        path = path[:-1]
+                        selection.select_path(path)
+                        widget.collapse_row(path)
+                for path in paths:
+                    widget.collapse_row(path)
+            elif event.keyval == gtk.keysyms.Right:
+                for path in paths:
+                    widget.expand_row(path, False)
+
+    def test_expand_row(self, widget, iter_, path):
+        model = widget.get_model()
+        iter_ = model.iter_children(iter_)
+        if not iter_:
+            return False
+        fields = [col.name for col in self.widget_tree.get_columns()
+                if col.name]
+        while iter_:
+            record = model.get_value(iter_, 0)
+            if not record.get_loaded(fields):
+                try:
+                    for field in fields:
+                        record.__getitem__(field, True)
+                except Exception, exception:
+                    return True
+            iter_ = model.iter_next(iter_)
+        return False
 
     def on_copy(self):
         clipboard = self.widget_tree.get_clipboard(gtk.gdk.SELECTION_CLIPBOARD)
@@ -367,8 +520,9 @@
     def drag_data_get(self, treeview, context, selection, target_id,
             etime):
         treeview.emit_stop_by_name('drag-data-get')
-        def _func_sel_get(store, path, iter, data):
-            data.append(path)
+        def _func_sel_get(store, path, iter_, data):
+            value = store.get_value(iter_, 0)
+            data.append(json.dumps(value.get_path(store.group)))
         data = []
         treeselection = treeview.get_selection()
         treeselection.selected_foreach(_func_sel_get, data)
@@ -388,20 +542,28 @@
                     return
         if not selection.data:
             return
-        model = treeview.get_model()
-        data = common.safe_eval(selection.data)
+        store = treeview.get_model()
+        try:
+            data = json.loads(selection.data)
+        except ValueError:
+            return
+        record = store.group.get_by_path(data)
         drop_info = treeview.get_dest_row_at_pos(x, y)
+        #TODO check drop recursion
         if drop_info:
             path, position = drop_info
-            idx = path[0]
-            if position in (gtk.TREE_VIEW_DROP_BEFORE,
-                    gtk.TREE_VIEW_DROP_INTO_OR_BEFORE):
-                model.move(data, idx)
+            if position == gtk.TREE_VIEW_DROP_BEFORE:
+                store.move_before(record, path)
+            elif position == gtk.TREE_VIEW_DROP_AFTER:
+                store.move_after(record, path)
             else:
-                model.move(data, idx + 1)
+                store.move_into(record, path)
+        else:
+            store.move_end(record)
         context.drop_finish(False, etime)
-        if treeview.sequence:
-            self.screen.group.set_sequence(field=treeview.sequence)
+        # XXX
+        #if 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')
@@ -545,15 +707,17 @@
         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_record = model.group[path]
+            model, iter_ = tree_sel.get_selected()
+            if model and iter_:
+                record = model.get_value(iter_, 0)
+                self.screen.current_record = record
 
         elif tree_sel.get_mode() == gtk.SELECTION_MULTIPLE:
             model, paths = tree_sel.get_selected_rows()
-            if paths:
-                self.screen.current_record = model.group[paths[0][0]]
+            if model and paths:
+                iter_ = model.get_iter(paths[0])
+                record = model.get_value(iter_, 0)
+                self.screen.current_record = record
 
         if hasattr(self.widget_tree, 'editable') \
                 and self.widget_tree.editable \
@@ -583,7 +747,8 @@
                 or (not self.widget_tree.get_model()) \
                     or self.screen.group != \
                         self.widget_tree.get_model().group:
-            self.store = AdaptModelGroup(self.screen.group)
+            self.store = AdaptModelGroup(self.screen.group,
+                    self.children_field)
             self.widget_tree.set_model(self.store)
         self.reload = False
         if not self.screen.current_record:
@@ -643,6 +808,8 @@
         self.widget_tree.grab_focus()
         if self.screen.current_record:
             path = self.store.on_get_path(self.screen.current_record)
+            if self.store.get_flags() & gtk.TREE_MODEL_LIST_ONLY:
+                path = (path[0],)
             focus_column = None
             for column in self.widget_tree.get_columns():
                 renderers = column.get_cell_renderers()
@@ -660,6 +827,8 @@
                     focus_column = column
                     break
             self.widget_tree.scroll_to_cell(path, focus_column, use_align=False)
+            if path[:-1]:
+                self.widget_tree.expand_to_path(path[:-1])
             self.widget_tree.set_cursor(path, focus_column, new)
 
     def sel_ids_get(self):
diff -r fcba908c65ca tryton/gui/window/view_form/view/list_gtk/parser.py
--- a/tryton/gui/window/view_form/view/list_gtk/parser.py	Thu Feb 03 01:04:30 2011 +0100
+++ b/tryton/gui/window/view_form/view/list_gtk/parser.py	Fri Feb 04 21:58:42 2011 +0100
@@ -176,6 +176,8 @@
                 col.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
                 col.set_visible(not node_attrs.get('tree_invisible',
                     fields[fname].attrs.get('tree_invisible', False)))
+                if fname == self.screen.exclude_field:
+                    col.set_visible(False)
                 i = treeview.append_column(col)
                 if 'sum' in node_attrs and fields[fname].attrs['type'] \
                         in ('integer', 'biginteger', 'float', 'numeric',
@@ -312,6 +314,8 @@
         raise NotImplementedError
 
     def get_textual_value(self, record):
+        if not record:
+            return ''
         return record[self.field_name].get_client(record) or ''
 
     def value_from_text(self, record, text):
diff -r fcba908c65ca tryton/gui/window/view_form/view/widget_parse.py
--- a/tryton/gui/window/view_form/view/widget_parse.py	Thu Feb 03 01:04:30 2011 +0100
+++ b/tryton/gui/window/view_form/view/widget_parse.py	Fri Feb 04 21:58:42 2011 +0100
@@ -27,7 +27,8 @@
 
 class WidgetParse(ParserInterface):
 
-    def parse(self, screen, root_node, fields, toolbar=None):
+    def parse(self, screen, root_node, fields, toolbar=None,
+            children_field=None):
         widget = None
         for node in root_node.childNodes:
             if not node.nodeType == node.ELEMENT_NODE:
@@ -39,7 +40,8 @@
                         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)
+                        buttons, toolbar, notebooks, cursor_widget,
+                        children_field)
                 res.title = widget.title
                 widget = res
                 break
diff -r fcba908c65ca tryton/gui/window/view_form/widget_search/form.py
--- a/tryton/gui/window/view_form/widget_search/form.py	Thu Feb 03 01:04:30 2011 +0100
+++ b/tryton/gui/window/view_form/widget_search/form.py	Fri Feb 04 21:58:42 2011 +0100
@@ -201,16 +201,16 @@
 
 class Form(object):
 
-    def __init__(self, xml, fields, model=None, parent=None, domain=None,
+    def __init__(self, view, model=None, parent=None, domain=None,
             call=None, context=None):
         if domain is None:
             domain = []
         if context is None:
             context = {}
-        parser = Parse(parent, fields, model=model, context=context,
+        parser = Parse(parent, view['fields'], model=model, context=context,
                 on_change=self.on_change)
         self.parent = parent
-        self.fields = fields
+        self.fields = view['fields']
         self.model = model
         self.parser = parser
         self.call = call
@@ -219,7 +219,7 @@
         width = 640
         if self.parent:
             width = self.parent.size_request()[0]
-        (self.widgets, self.widget) = parser.parse(xml, width)
+        (self.widgets, self.widget) = parser.parse(view['arch'], width)
         self.widget.show()
         self.spin_limit = parser.spin_limit
         self.spin_limit.connect('value-changed', self.limit_changed)
diff -r fcba908c65ca tryton/gui/window/win_form.py
--- a/tryton/gui/window/win_form.py	Thu Feb 03 01:04:30 2011 +0100
+++ b/tryton/gui/window/win_form.py	Fri Feb 04 21:58:42 2011 +0100
@@ -268,7 +268,7 @@
     def _sig_label(self, screen, signal_data):
         name = '_'
         if signal_data[0] >= 0:
-            name = str(signal_data[0] + 1)
+            name = str(signal_data[0])
         line = '(%s/%s)' % (name, signal_data[1])
         self.label.set_text(line)
 
@@ -303,7 +303,7 @@
         if res == gtk.RESPONSE_OK:
             return True
         elif res == gtk.RESPONSE_CANCEL:
-            self.screen.remove(delete=True)
+            self.screen.remove()
         return False
 
     def new(self):
diff -r fcba908c65ca tryton/gui/window/wizard.py
--- a/tryton/gui/window/wizard.py	Thu Feb 03 01:04:30 2011 +0100
+++ b/tryton/gui/window/wizard.py	Fri Feb 04 21:58:42 2011 +0100
@@ -83,8 +83,7 @@
             elif res['type'] == 'form':
                 self.datas['form'] = {}
             if res['type'] == 'form':
-                self.update(res['arch'], res['fields'], res['state'],
-                            res['object'], context=ctx)
+                self.update(res, res['state'], res['object'], context=ctx)
                 self.screen.current_record.set_default(self.datas['form'])
                 break
             elif res['type'] == 'action':
@@ -148,7 +147,7 @@
         self.state = state
         self.process()
 
-    def update(self, arch, fields, state, obj_name, context=None):
+    def update(self, view, state, obj_name, context=None):
         self.model = obj_name
 
         hbuttonbox = gtk.HButtonBox()
@@ -169,13 +168,14 @@
             hbuttonbox.pack_start(but)
 
         val = {}
+        fields = view['fields']
         for i in fields:
             if 'value' in fields[i]:
                 val[i] = fields[i]['value']
 
         self.screen = Screen(obj_name, self.window, view_type=[],
                 context=context)
-        self.screen.add_view(arch, fields, display=True)
+        self.screen.add_view(view, display=True)
         self.screen.widget.show()
 
         title = gtk.Label()
diff -r fcba908c65ca tryton/signal_event.py
--- a/tryton/signal_event.py	Thu Feb 03 01:04:30 2011 +0100
+++ b/tryton/signal_event.py	Fri Feb 04 21:58:42 2011 +0100
@@ -31,7 +31,7 @@
         for sig in signal:
             i = 0
             while i < len(self.__connects[sig]):
-                if self.__connects[sig][i][2] == key:
+                if self.__connects[sig][i][2] is key:
                     del self.__connects[sig][i]
                 else:
                     i += 1
diff -r fcba908c65ca tryton/wizard/main.py
--- a/tryton/wizard/main.py	Thu Feb 03 01:04:30 2011 +0100
+++ b/tryton/wizard/main.py	Fri Feb 04 21:58:42 2011 +0100
@@ -16,7 +16,7 @@
 class Dialog(object):
     "Dialog for wizard"
 
-    def __init__(self, arch, fields, state, obj_name, parent,
+    def __init__(self, view, state, obj_name, parent,
             action='', size=(0, 0), context=None):
         self.parent = parent
         self.action = action
@@ -50,12 +50,13 @@
             self.states.append(i[0])
 
         val = {}
+        fields = view['fields']
         for i in fields:
             if 'value' in fields[i]:
                 val[i] = fields[i]['value']
 
         self.screen = Screen(obj_name, self.dia, view_type=[], context=context)
-        self.screen.add_view(arch, fields, display=True)
+        self.screen.add_view(view, display=True)
 
         title = gtk.Label()
         title.set_use_markup(True)
@@ -208,9 +209,8 @@
                 datas['form'] = {}
             if res['type'] == 'form':
                 if not dia:
-                    dia = Dialog(res['arch'], res['fields'], res['state'],
-                            res['object'], parent, action=action,
-                            size=res['size'], context=ctx)
+                    dia = Dialog(res, res['state'], res['object'], parent,
+                            action=action, size=res['size'], context=ctx)
                     dia.screen.current_record.set_default(datas['form'])
                 res2 = dia.run(datas['form'])
                 if not res2:
