[MERGE]: survey: fixed a bug in warning message when users exceed maximal number...
[odoo/odoo.git] / addons / web / static / src / js / view_form.js
index 5c50316..43f5923 100644 (file)
@@ -22,6 +22,9 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
      * @param {openerp.web.Session} session the current openerp session
      * @param {openerp.web.DataSet} dataset the dataset this view will work with
      * @param {String} view_id the identifier of the OpenERP view object
+     * @param {Object} options
+     *                  - sidebar : [true|false]
+     *                  - resize_textareas : [true|false|max_height]
      *
      * @property {openerp.web.Registry} registry=openerp.web.form.widgets widgets registry for this form view instance
      */
@@ -47,10 +50,10 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
         _.defaults(this.options, {
             "not_interactible_on_create": false
         });
-        this.mutating_lock = $.Deferred();
-        this.initial_mutating_lock = this.mutating_lock;
-        this.on_change_lock = $.Deferred().resolve();
-        this.reload_lock = $.Deferred().resolve();
+        this.is_initialized = $.Deferred();
+        this.mutating_mutex = new $.Mutex();
+        this.on_change_mutex = new $.Mutex();
+        this.reload_mutex = new $.Mutex();
     },
     start: function() {
         this._super();
@@ -132,21 +135,26 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
     },
 
     do_show: function () {
-        var self = this,
-            deferred = $.Deferred();
-        this.has_been_loaded.then(function() {
+        var self = this;
+        this.$element.show().css('visibility', 'hidden');
+        return this.has_been_loaded.pipe(function() {
+            var result;
             if (self.dataset.index === null) {
                 // null index means we should start a new record
-                deferred.pipe(self.on_button_new());
+                result = self.on_button_new();
             } else {
-                deferred.pipe(self.dataset.read_index(_.keys(self.fields_view.fields)).pipe(self.on_record_loaded));
+                result = self.dataset.read_index(_.keys(self.fields_view.fields), {
+                    context : { 'bin_size' : true }
+                }).pipe(self.on_record_loaded);
             }
-            self.$element.show();
+            result.pipe(function() {
+                self.$element.css('visibility', 'visible');
+            });
             if (self.sidebar) {
                 self.sidebar.$element.show();
             }
+            return result;
         });
-        return deferred;
     },
     do_hide: function () {
         this._super();
@@ -169,7 +177,7 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
                 field.validate();
             });
         });
-        return $.when.apply(null, set_values).then(function() {
+        return $.when.apply(null, set_values).pipe(function() {
             if (!record.id) {
                 self.show_invalid = false;
                 // New record: Second pass in order to trigger the onchanges
@@ -183,7 +191,7 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
                 });
             }
             self.on_form_changed();
-            self.initial_mutating_lock.resolve();
+            self.is_initialized.resolve();
             self.show_invalid = true;
             self.do_update_pager(record.id == null);
             if (self.sidebar) {
@@ -226,6 +234,7 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
     do_update_pager: function(hide_index) {
         var $pager = this.$form_header.find('div.oe_form_pager');
         var index = hide_index ? '-' : this.dataset.index + 1;
+        $pager.find('button').prop('disabled', this.dataset.ids.length < 2)
         $pager.find('span.oe_pager_index').html(index);
         $pager.find('span.oe_pager_count').html(this.dataset.ids.length);
     },
@@ -299,7 +308,7 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
     },
     do_onchange: function(widget, processed) {
         var self = this;
-        var act = function() {
+        return this.on_change_mutex.exec(function() {
             try {
                 processed = processed || [];
                 var on_change = widget.node.attrs.on_change;
@@ -326,9 +335,7 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
                 console.error(e);
                 return $.Deferred().reject();
             }
-        };
-        this.on_change_lock = this.on_change_lock.pipe(act, act);
-        return this.on_change_lock;
+        });
     },
     on_processed_onchange: function(response, processed) {
         try {
@@ -352,7 +359,7 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
             this.on_form_changed();
         }
         if (!_.isEmpty(result.warning)) {
-            $(QWeb.render("DialogWarning", result.warning)).dialog({
+            $(QWeb.render("CrashManagerWarning", result.warning)).dialog({
                 modal: true,
                 buttons: [
                     {text: _t("Ok"), click: function() { $(this).dialog("close"); }}
@@ -410,10 +417,8 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
      */
     do_save: function(success, prepend_on_create) {
         var self = this;
-        var action = function() {
+        return this.mutating_mutex.exec(function() { return self.is_initialized.pipe(function() {
             try {
-            if (!self.initial_mutating_lock.isResolved() && !self.initial_mutating_lock.isRejected())
-                return;
             var form_invalid = false,
                 values = {},
                 first_invalid_field = null;
@@ -439,15 +444,15 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
             } else {
                 var save_deferral;
                 if (!self.datarecord.id) {
-                    openerp.log("FormView(", self, ") : About to create", values);
+                    //console.log("FormView(", self, ") : About to create", values);
                     save_deferral = self.dataset.create(values).pipe(function(r) {
                         return self.on_created(r, undefined, prepend_on_create);
                     }, null);
                 } else if (_.isEmpty(values)) {
-                    openerp.log("FormView(", self, ") : Nothing to save");
+                    //console.log("FormView(", self, ") : Nothing to save");
                     save_deferral = $.Deferred().resolve({}).promise();
                 } else {
-                    openerp.log("FormView(", self, ") : About to save", values);
+                    //console.log("FormView(", self, ") : About to save", values);
                     save_deferral = self.dataset.write(self.datarecord.id, values, {}).pipe(function(r) {
                         return self.on_saved(r);
                     }, null);
@@ -458,9 +463,7 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
                 console.error(e);
                 return $.Deferred().reject();
             }
-        };
-        this.mutating_lock = this.mutating_lock.pipe(action, action);
-        return this.mutating_lock;
+        });});
     },
     on_invalid: function() {
         var msg = "<ul>";
@@ -511,7 +514,7 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
             if (this.sidebar) {
                 this.sidebar.attachments.do_update();
             }
-            openerp.log("The record has been created with id #" + this.datarecord.id);
+            //openerp.log("The record has been created with id #" + this.datarecord.id);
             this.reload();
             return $.when(_.extend(r, {created: true})).then(success);
         }
@@ -521,15 +524,15 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
     },
     reload: function() {
         var self = this;
-        var act = function() {
+        return this.reload_mutex.exec(function() {
             if (self.dataset.index == null || self.dataset.index < 0) {
                 return $.when(self.on_button_new());
             } else {
-                return self.dataset.read_index(_.keys(self.fields_view.fields)).pipe(self.on_record_loaded);
+                return self.dataset.read_index(_.keys(self.fields_view.fields), {
+                    context : { 'bin_size' : true }
+                }).pipe(self.on_record_loaded);
             }
-        };
-        this.reload_lock = this.reload_lock.pipe(act, act);
-        return this.reload_lock;
+        });
     },
     get_fields_values: function(blacklist) {
        blacklist = blacklist || [];
@@ -626,7 +629,7 @@ openerp.web.form.SidebarAttachments = openerp.web.Widget.extend({
                     ['res_model', '=', this.view.dataset.model],
                     ['res_id', '=', this.view.datarecord.id],
                     ['type', 'in', ['binary', 'url']]
-                ])).read_slice(['name', 'url', 'type'], {}, this.on_attachments_loaded);
+                ])).read_slice(['name', 'url', 'type'], {}).then(this.on_attachments_loaded);
         }
     },
     on_attachments_loaded: function(attachments) {
@@ -789,7 +792,7 @@ openerp.web.form.Widget = openerp.web.Widget.extend(/** @lends openerp.web.form.
         trigger = trigger || this.$element;
         options = _.extend({
                 delay: 1000,
-                maxWidth: openerp.connection.debug ? '300px' : '200px',
+                maxWidth: '500px',
                 content: function() {
                     var template = widget.template + '.tooltip';
                     if (!QWeb.has_template(template)) {
@@ -803,9 +806,9 @@ openerp.web.form.Widget = openerp.web.Widget.extend(/** @lends openerp.web.form.
             }, options || {});
         trigger.tipTip(options);
     },
-    _build_view_fields_values: function() {
+    _build_view_fields_values: function(blacklist) {
         var a_dataset = this.view.dataset;
-        var fields_values = this.view.get_fields_values();
+        var fields_values = this.view.get_fields_values(blacklist);
         var active_id = a_dataset.ids[a_dataset.index];
         _.extend(fields_values, {
             active_id: active_id || false,
@@ -818,39 +821,33 @@ openerp.web.form.Widget = openerp.web.Widget.extend(/** @lends openerp.web.form.
         }
         return fields_values;
     },
-    _build_eval_context: function() {
+    _build_eval_context: function(blacklist) {
         var a_dataset = this.view.dataset;
-        return new openerp.web.CompoundContext(a_dataset.get_context(), this._build_view_fields_values());
+        return new openerp.web.CompoundContext(a_dataset.get_context(), this._build_view_fields_values(blacklist));
     },
     /**
      * Builds a new context usable for operations related to fields by merging
      * the fields'context with the action's context.
      */
-    build_context: function() {
-        var f_context = (this.field || {}).context || {};
-        if (!!f_context.__ref) {
-            var fields_values = this._build_eval_context();
-            f_context = new openerp.web.CompoundDomain(f_context).set_eval_context(fields_values);
-        }
-        // maybe the default_get should only be used when we do a default_get?
-        var v_contexts = _.compact([this.node.attrs.default_get || null,
-            this.node.attrs.context || null]);
-        var v_context = new openerp.web.CompoundContext();
-        _.each(v_contexts, function(x) {v_context.add(x);});
-        if (_.detect(v_contexts, function(x) {return !!x.__ref;})) {
-            var fields_values = this._build_eval_context();
-            v_context.set_eval_context(fields_values);
+    build_context: function(blacklist) {
+        // only use the model's context if there is not context on the node
+        var v_context = this.node.attrs.context;
+        if (! v_context) {
+            v_context = (this.field || {}).context || {};
+        }
+        
+        if (v_context.__ref || true) { //TODO: remove true
+            var fields_values = this._build_eval_context(blacklist);
+            v_context = new openerp.web.CompoundContext(v_context).set_eval_context(fields_values);
         }
-        // if there is a context on the node, overrides the model's context
-        var ctx = v_contexts.length > 0 ? v_context : f_context;
-        return ctx;
+        return v_context;
     },
     build_domain: function() {
         var f_domain = this.field.domain || [];
         var n_domain = this.node.attrs.domain || null;
         // if there is a domain on the node, overrides the model's domain
         var final_domain = n_domain !== null ? n_domain : f_domain;
-        if (!(final_domain instanceof Array)) {
+        if (!(final_domain instanceof Array) || true) { //TODO: remove true
             var fields_values = this._build_eval_context();
             final_domain = new openerp.web.CompoundDomain(final_domain).set_eval_context(fields_values);
         }
@@ -1269,7 +1266,14 @@ openerp.web.form.Field = openerp.web.form.Widget.extend(/** @lends openerp.web.f
     },
     reset: function() {
         this.dirty = false;
-    }
+    },
+    get_definition_options: function() {
+        if (!this.definition_options) {
+            var str = this.node.attrs.options || '{}';
+            this.definition_options = JSON.parse(str);
+        }
+        return this.definition_options;
+    },
 });
 
 openerp.web.form.FieldChar = openerp.web.form.Field.extend({
@@ -1493,11 +1497,16 @@ openerp.web.form.FieldText = openerp.web.form.Field.extend({
     start: function() {
         this._super.apply(this, arguments);
         this.$element.find('textarea').change(this.on_ui_change);
+        this.resized = false;
     },
     set_value: function(value) {
         this._super.apply(this, arguments);
         var show_value = openerp.web.format_value(value, this, '');
         this.$element.find('textarea').val(show_value);
+        if (!this.resized && this.view.options.resize_textareas) {
+            this.do_resize(this.view.options.resize_textareas);
+            this.resized = true;
+        }
     },
     update_dom: function() {
         this._super.apply(this, arguments);
@@ -1518,6 +1527,29 @@ openerp.web.form.FieldText = openerp.web.form.Field.extend({
     },
     focus: function() {
         this.$element.find('textarea').focus();
+    },
+    do_resize: function(max_height) {
+        max_height = parseInt(max_height, 10);
+        var $input = this.$element.find('textarea'),
+            $div = $('<div style="position: absolute; z-index: 1000; top: 0"/>').width($input.width()),
+            new_height;
+        $div.text($input.val());
+        _.each('font-family,font-size,white-space'.split(','), function(style) {
+            $div.css(style, $input.css(style));
+        });
+        $div.appendTo($('body'));
+        new_height = $div.height();
+        if (new_height < 90) {
+            new_height = 90;
+        }
+        if (!isNaN(max_height) && new_height > max_height) {
+            new_height = max_height;
+        }
+        $div.remove();
+        $input.height(new_height);
+    },
+    reset: function() {
+        this.resized = false;
     }
 });
 
@@ -1666,16 +1698,12 @@ openerp.web.form.FieldSelection = openerp.web.form.Field.extend({
 
 openerp.web.form.dialog = function(content, options) {
     options = _.extend({
-        autoOpen: true,
         width: '90%',
-        height: '90%',
-        min_width: '800px',
-        min_height: '600px'
+        height: 'auto',
+        min_width: '800px'
     }, options || {});
-    options.autoOpen = true;
-    var dialog = new openerp.web.Dialog(null, options);
-    dialog.$dialog = $(content).dialog(dialog.dialog_options);
-    return dialog.$dialog;
+    var dialog = new openerp.web.Dialog(null, options, content).open();
+    return dialog.$element;
 };
 
 openerp.web.form.FieldMany2One = openerp.web.form.Field.extend({
@@ -1717,7 +1745,14 @@ openerp.web.form.FieldMany2One = openerp.web.form.Field.extend({
                         return;
                     }
                     var pop = new openerp.web.form.FormOpenPopup(self.view);
-                    pop.show_element(self.field.relation, self.value[0],self.build_context(), {});
+                    pop.show_element(
+                        self.field.relation,
+                        self.value[0],
+                        self.build_context(),
+                        {
+                            title: _t("Open: ") + (self.string || self.name)
+                        }
+                    );
                     pop.on_write_completed.add_last(function() {
                         self.set_value(self.value[0]);
                     });
@@ -1727,7 +1762,7 @@ openerp.web.form.FieldMany2One = openerp.web.form.Field.extend({
                         self.open_related(self.related_entries[i]);
                     };
                 });
-                var cmenu = self.$menu_btn.contextMenu(self.cm_id, {'leftClickToo': true,
+                var cmenu = self.$menu_btn.contextMenu(self.cm_id, {'noRightClick': true,
                     bindings: bindings, itemStyle: {"color": ""},
                     onContextMenu: function() {
                         if(self.value) {
@@ -1747,7 +1782,6 @@ openerp.web.form.FieldMany2One = openerp.web.form.Field.extend({
             });
         });
         var ctx_callback = function(e) {init_context_menu_def.resolve(e); e.preventDefault()};
-        this.$menu_btn.bind('contextmenu', ctx_callback);
         this.$menu_btn.click(ctx_callback);
 
         // some behavior for input
@@ -1864,25 +1898,36 @@ openerp.web.form.FieldMany2One = openerp.web.form.Field.extend({
     },
     _quick_create: function(name) {
         var self = this;
-        var dataset = new openerp.web.DataSetStatic(this, this.field.relation, self.build_context());
-        dataset.name_create(name, function(data) {
-            self._change_int_ext_value(data);
-        }).fail(function(error, event) {
-            event.preventDefault();
+        var slow_create = function() {
             self._change_int_value(null);
             self._search_create_popup("form", undefined, {"default_name": name});
-        });
+        }
+        if (self.get_definition_options().quick_create === undefined || self.get_definition_options().quick_create) {
+            var dataset = new openerp.web.DataSetStatic(this, this.field.relation, self.build_context());
+            dataset.name_create(name, function(data) {
+                self._change_int_ext_value(data);
+            }).fail(function(error, event) {
+                event.preventDefault();
+                slow_create();
+            });
+        } else
+            slow_create();
     },
     // all search/create popup handling
     _search_create_popup: function(view, ids, context) {
         var self = this;
         var pop = new openerp.web.form.SelectCreatePopup(this);
-        pop.select_element(self.field.relation,{
+        pop.select_element(
+            self.field.relation,
+            {
+                title: (view === 'search' ? _t("Search: ") : _t("Create: ")) + (this.string || this.name),
                 initial_ids: ids ? _.map(ids, function(x) {return x[0]}) : undefined,
                 initial_view: view,
                 disable_multiple_selection: true
-                }, self.build_domain(),
-                new openerp.web.CompoundContext(self.build_context(), context || {}));
+            },
+            self.build_domain(),
+            new openerp.web.CompoundContext(self.build_context(), context || {})
+        );
         pop.on_select_elements.add(function(element_ids) {
             var dataset = new openerp.web.DataSetStatic(self, self.field.relation, self.build_context());
             dataset.name_get([element_ids[0]], function(data) {
@@ -1922,7 +1967,8 @@ openerp.web.form.FieldMany2One = openerp.web.form.Field.extend({
             self._change_int_ext_value(rval);
         };
         if (value && !(value instanceof Array)) {
-            var dataset = new openerp.web.DataSetStatic(this, this.field.relation, self.build_context());
+            // name_get in a m2o does not use the context of the field
+            var dataset = new openerp.web.DataSetStatic(this, this.field.relation, self.view.dataset.get_context());
             dataset.name_get([value], function(data) {
                 real_set_value(data[0]);
             }).fail(function() {self.tmp_value = undefined;});
@@ -2042,13 +2088,19 @@ openerp.web.form.FieldOne2Many = openerp.web.form.Field.extend({
         this.dataset.child_name = this.name;
         //this.dataset.child_name = 
         this.dataset.on_change.add_last(function() {
-            self.on_ui_change();
+            self.trigger_on_change();
         });
 
         this.is_setted.then(function() {
             self.load_views();
         });
     },
+    trigger_on_change: function() {
+        var tmp = this.doing_on_change;
+        this.doing_on_change = true;
+        this.on_ui_change();
+        this.doing_on_change = tmp;
+    },
     is_readonly: function() {
         return this.readonly || this.force_readonly;
     },
@@ -2072,6 +2124,7 @@ openerp.web.form.FieldOne2Many = openerp.web.form.Field.extend({
                 if (self.is_readonly()) {
                     view.options.addable = null;
                     view.options.deletable = null;
+                    view.options.isClarkGable = false;
                 }
             } else if (view.view_type === "form") {
                 if (self.is_readonly()) {
@@ -2083,13 +2136,11 @@ openerp.web.form.FieldOne2Many = openerp.web.form.Field.extend({
         });
         this.views = views;
 
-        this.viewmanager = new openerp.web.ViewManager(this, this.dataset, views, {
-            action_buttons : false
-        });
+        this.viewmanager = new openerp.web.ViewManager(this, this.dataset, views, {});
         this.viewmanager.template = 'One2Many.viewmanager';
         this.viewmanager.registry = openerp.web.views.clone({
             list: 'openerp.web.form.One2ManyListView',
-            form: 'openerp.web.FormView',
+            form: 'openerp.web.form.One2ManyFormView',
             page: 'openerp.web.PageView'
         });
         var once = $.Deferred().then(function() {
@@ -2104,8 +2155,8 @@ openerp.web.form.FieldOne2Many = openerp.web.form.Field.extend({
                 if (self.is_readonly())
                     controller.set_editable(false);
             } else if (view_type == "form" || view_type == 'page') {
-                if (view_type == 'page') {
-                    controller.$element.find(".oe_form_buttons").hide();
+                if (view_type == 'page' || self.is_readonly()) {
+                    $(".oe_form_buttons", controller.$element).children().remove();
                 }
                 controller.on_record_loaded.add_last(function() {
                     once.resolve();
@@ -2232,12 +2283,17 @@ openerp.web.form.FieldOne2Many = openerp.web.form.Field.extend({
                 return commands['delete'](x.id);}));
     },
     save_any_view: function() {
+        if (this.doing_on_change)
+            return false;
        return this.session.synchronized_mode(_.bind(function() {
                if (this.viewmanager && this.viewmanager.views && this.viewmanager.active_view &&
                    this.viewmanager.views[this.viewmanager.active_view] &&
                    this.viewmanager.views[this.viewmanager.active_view].controller) {
                    var view = this.viewmanager.views[this.viewmanager.active_view].controller;
                    if (this.viewmanager.active_view === "form") {
+                       if (!view.is_initialized.isResolved()) {
+                           return false;
+                       }
                        var res = $.when(view.do_save());
                        if (!res.isResolved() && !res.isRejected()) {
                            console.warn("Asynchronous get_value() is not supported in form view.");
@@ -2296,7 +2352,7 @@ openerp.web.form.FieldOne2Many = openerp.web.form.Field.extend({
 
 openerp.web.form.One2ManyDataSet = openerp.web.BufferedDataSet.extend({
     get_context: function() {
-        this.context = this.o2m.build_context();
+        this.context = this.o2m.build_context([this.o2m.name]);
         return this.context;
     }
 });
@@ -2310,22 +2366,28 @@ openerp.web.form.One2ManyListView = openerp.web.ListView.extend({
             var self = this;
             var pop = new openerp.web.form.SelectCreatePopup(this);
             pop.on_default_get.add(self.dataset.on_default_get);
-            pop.select_element(self.o2m.field.relation,{
-                initial_view: "form",
-                alternative_form_view: self.o2m.field.views ? self.o2m.field.views["form"] : undefined,
-                create_function: function(data, callback, error_callback) {
-                    return self.o2m.dataset.create(data).then(function(r) {
-                        self.o2m.dataset.set_ids(self.o2m.dataset.ids.concat([r.result]));
-                        self.o2m.dataset.on_change();
-                    }).then(callback, error_callback);
+            pop.select_element(
+                self.o2m.field.relation,
+                {
+                    title: _t("Create: ") + self.name,
+                    initial_view: "form",
+                    alternative_form_view: self.o2m.field.views ? self.o2m.field.views["form"] : undefined,
+                    create_function: function(data, callback, error_callback) {
+                        return self.o2m.dataset.create(data).then(function(r) {
+                            self.o2m.dataset.set_ids(self.o2m.dataset.ids.concat([r.result]));
+                            self.o2m.dataset.on_change();
+                        }).then(callback, error_callback);
+                    },
+                    read_function: function() {
+                        return self.o2m.dataset.read_ids.apply(self.o2m.dataset, arguments);
+                    },
+                    parent_view: self.o2m.view,
+                    child_name: self.o2m.name,
+                    form_view_options: {'not_interactible_on_create':true}
                 },
-                read_function: function() {
-                    return self.o2m.dataset.read_ids.apply(self.o2m.dataset, arguments);
-                },
-                parent_view: self.o2m.view,
-                child_name: self.o2m.name,
-                form_view_options: {'not_interactible_on_create':true}
-            }, self.o2m.build_domain(), self.o2m.build_context());
+                self.o2m.build_domain(),
+                self.o2m.build_context()
+            );
             pop.on_select_elements.add_last(function() {
                 self.o2m.reload_current_view();
             });
@@ -2334,7 +2396,8 @@ openerp.web.form.One2ManyListView = openerp.web.ListView.extend({
     do_activate_record: function(index, id) {
         var self = this;
         var pop = new openerp.web.form.FormOpenPopup(self.o2m.view);
-        pop.show_element(self.o2m.field.relation, id, self.o2m.build_context(),{
+        pop.show_element(self.o2m.field.relation, id, self.o2m.build_context(), {
+            title: _t("Open: ") + self.name,
             auto_write: false,
             alternative_form_view: self.o2m.field.views ? self.o2m.field.views["form"] : undefined,
             parent_view: self.o2m.view,
@@ -2350,6 +2413,22 @@ openerp.web.form.One2ManyListView = openerp.web.ListView.extend({
                 self.o2m.reload_current_view();
             });
         });
+    },
+    do_button_action: function (name, id, callback) {
+        var self = this;
+        var def = $.Deferred().then(callback).then(function() {self.o2m.view.reload();});
+        return this._super(name, id, _.bind(def.resolve, def));
+    },
+});
+
+openerp.web.form.One2ManyFormView = openerp.web.FormView.extend({
+    form_template: 'One2Many.formview',
+    on_loaded: function(data) {
+        this._super(data);
+        var self = this;
+        this.$form_header.find('button.oe_form_button_create').click(function() {
+            self.do_save().then(self.on_button_new);
+        });
     }
 });
 
@@ -2403,7 +2482,8 @@ openerp.web.form.FieldMany2Many = openerp.web.form.Field.extend({
         this.list_view = new openerp.web.form.Many2ManyListView(this, this.dataset, false, {
                     'addable': self.is_readonly() ? null : _t("Add"),
                     'deletable': self.is_readonly() ? false : true,
-                    'selectable': self.multi_selection
+                    'selectable': self.multi_selection,
+                    'isClarkGable': self.is_readonly() ? false : true
             });
         var embedded = (this.field.views || {}).tree;
         if (embedded) {
@@ -2457,9 +2537,14 @@ openerp.web.form.Many2ManyDataSet = openerp.web.DataSetStatic.extend({
 openerp.web.form.Many2ManyListView = openerp.web.ListView.extend(/** @lends openerp.web.form.Many2ManyListView# */{
     do_add_record: function () {
         var pop = new openerp.web.form.SelectCreatePopup(this);
-        pop.select_element(this.model, {},
+        pop.select_element(
+            this.model,
+            {
+                title: _t("Add: ") + this.name
+            },
             new openerp.web.CompoundDomain(this.m2m_field.build_domain(), ["!", ["id", "in", this.m2m_field.dataset.ids]]),
-            this.m2m_field.build_context());
+            this.m2m_field.build_context()
+        );
         var self = this;
         pop.on_select_elements.add(function(element_ids) {
             _.each(element_ids, function(element_id) {
@@ -2475,6 +2560,7 @@ openerp.web.form.Many2ManyListView = openerp.web.ListView.extend(/** @lends open
         var self = this;
         var pop = new openerp.web.form.FormOpenPopup(this);
         pop.show_element(this.dataset.model, id, this.m2m_field.build_context(), {
+            title: _t("Open: ") + this.name,
             readonly: this.widget_parent.is_readonly()
         });
         pop.on_write_completed.add_last(function() {
@@ -2514,9 +2600,12 @@ openerp.web.form.SelectCreatePopup = openerp.web.OldWidget.extend(/** @lends ope
         this.initial_ids = this.options.initial_ids;
         this.created_elements = [];
         this.render_element();
-        openerp.web.form.dialog(this.$element, {close:function() {
-            self.check_exit();
-        }});
+        openerp.web.form.dialog(this.$element, {
+            close: function() {
+                self.check_exit();
+            },
+            title: options.title || ""
+        });
         this.start();
     },
     start: function() {
@@ -2571,20 +2660,6 @@ openerp.web.form.SelectCreatePopup = openerp.web.OldWidget.extend(/** @lends ope
             }
         });
         this.searchview.on_loaded.add_last(function () {
-            var $buttons = self.searchview.$element.find(".oe_search-view-buttons");
-            $buttons.append(QWeb.render("SelectCreatePopup.search.buttons"));
-            var $cbutton = $buttons.find(".oe_selectcreatepopup-search-close");
-            $cbutton.click(function() {
-                self.stop();
-            });
-            var $sbutton = $buttons.find(".oe_selectcreatepopup-search-select");
-            if(self.options.disable_multiple_selection) {
-                $sbutton.hide();
-            }
-            $sbutton.click(function() {
-                self.on_select_elements(self.selected_ids);
-                self.stop();
-            });
             self.view_list = new openerp.web.form.SelectCreateListView(self,
                     self.dataset, false,
                     _.extend({'deletable': false,
@@ -2596,6 +2671,22 @@ openerp.web.form.SelectCreatePopup = openerp.web.OldWidget.extend(/** @lends ope
             }).pipe(function() {
                 self.searchview.do_search();
             });
+            self.view_list.on_loaded.add_last(function() {
+                var $buttons = self.view_list.$element.find(".oe-actions");
+                $buttons.prepend(QWeb.render("SelectCreatePopup.search.buttons"));
+                var $cbutton = $buttons.find(".oe_selectcreatepopup-search-close");
+                $cbutton.click(function() {
+                    self.stop();
+                });
+                var $sbutton = $buttons.find(".oe_selectcreatepopup-search-select");
+                if(self.options.disable_multiple_selection) {
+                    $sbutton.hide();
+                }
+                $sbutton.click(function() {
+                    self.on_select_elements(self.selected_ids);
+                    self.stop();
+                });
+            });
         });
         this.searchview.appendTo($("#" + this.element_id + "_search"));
     },
@@ -2653,7 +2744,7 @@ openerp.web.form.SelectCreatePopup = openerp.web.OldWidget.extend(/** @lends ope
             var $nbutton = $buttons.find(".oe_selectcreatepopup-form-save-new");
             $nbutton.click(function() {
                 $.when(self.view_form.do_save()).then(function() {
-                    self.view_form.reload_lock.then(function() {
+                    self.view_form.reload_mutex.exec(function() {
                         self.view_form.on_button_new();
                     });
                 });
@@ -2661,7 +2752,7 @@ openerp.web.form.SelectCreatePopup = openerp.web.OldWidget.extend(/** @lends ope
             var $nbutton = $buttons.find(".oe_selectcreatepopup-form-save");
             $nbutton.click(function() {
                 $.when(self.view_form.do_save()).then(function() {
-                    self.view_form.reload_lock.then(function() {
+                    self.view_form.reload_mutex.exec(function() {
                         self.check_exit();
                     });
                 });
@@ -2719,10 +2810,12 @@ openerp.web.form.FormOpenPopup = openerp.web.OldWidget.extend(/** @lends openerp
         this.context = context || {};
         this.options = _.defaults(options || {}, {"auto_write": true});
         this.render_element();
-        this.$element.dialog({title: '',
-                    modal: true,
-                    width: 960,
-                    height: 600});
+        this.$element.dialog({
+            title: options.title || '',
+            modal: true,
+            width: 960,
+            height: 600
+        });
         this.start();
     },
     start: function() {
@@ -2921,7 +3014,7 @@ openerp.web.form.FieldBinary = openerp.web.form.Field.extend({
     on_save_as: function() {
         var url = '/web/binary/saveas?session_id=' + this.session.session_id + '&model=' +
             this.view.dataset.model +'&id=' + (this.view.datarecord.id || '') + '&field=' + this.name +
-            '&fieldname=' + (this.node.attrs.filename || '') + '&t=' + (new Date().getTime());
+            '&filename_field=' + (this.node.attrs.filename || '') + '&t=' + (new Date().getTime());
         window.open(url);
     },
     on_clear: function() {
@@ -3072,6 +3165,32 @@ openerp.web.form.FieldStatus = openerp.web.form.Field.extend({
     }
 });
 
+openerp.web.form.WidgetHtml = openerp.web.form.Widget.extend({
+    render: function () {
+        var $root = $('<div class="oe_form_html_view">');
+        this.render_children(this, $root);
+        return $root.html();
+    },
+    render_children: function (object, $into) {
+        var self = this,
+            fields = this.view.fields_view.fields;
+        _(object.children).each(function (child) {
+            if (typeof child === 'string') {
+                $into.text(child);
+            } else if (child.tag === 'field') {
+                $into.append(
+                    new (self.view.registry.get_object('frame'))(
+                        self.view, {tag: 'ueule', attrs: {}, children: [child] })
+                            .render());
+            } else {
+                var $child = $(document.createElement(child.tag))
+                        .attr(child.attrs)
+                        .appendTo($into);
+                self.render_children(child, $child);
+            }
+        });
+    }
+});
 
 
 /**
@@ -3105,7 +3224,8 @@ openerp.web.form.widgets = new openerp.web.Registry({
     'progressbar': 'openerp.web.form.FieldProgressBar',
     'image': 'openerp.web.form.FieldBinaryImage',
     'binary': 'openerp.web.form.FieldBinaryFile',
-    'statusbar': 'openerp.web.form.FieldStatus'
+    'statusbar': 'openerp.web.form.FieldStatus',
+    'html': 'openerp.web.form.WidgetHtml'
 });