diff --git a/addons/web/static/src/css/base.css b/addons/web/static/src/css/base.css index 029b3ef4f10..fd2f0db381d 100644 --- a/addons/web/static/src/css/base.css +++ b/addons/web/static/src/css/base.css @@ -2353,6 +2353,11 @@ .openerp .oe_list { position: relative; } +.openerp .oe_list .oe_form .oe_form_nosheet { + margin: 0; + padding: 0; + border: none; +} .openerp .oe_list .oe_form .oe_form_field { width: auto; position: absolute; diff --git a/addons/web/static/src/css/base.sass b/addons/web/static/src/css/base.sass index 449d76b882b..0efd43dc5ac 100644 --- a/addons/web/static/src/css/base.sass +++ b/addons/web/static/src/css/base.sass @@ -1841,11 +1841,16 @@ $sheet-max-width: 860px .oe_list position: relative - .oe_form .oe_form_field - width: auto - position: absolute - margin: 0 !important // dammit - padding: 0 + .oe_form + .oe_form_nosheet + margin: 0 // FIXME: either class or border should not be by default + padding: 0 + border: none + .oe_form_field + width: auto + position: absolute + margin: 0 !important // dammit + padding: 0 .oe_list_content width: 100% diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index 506d08decd7..c2dac62366b 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -3349,6 +3349,7 @@ instance.web.form.One2ManyListView = instance.web.ListView.extend({ ListType: instance.web.form.One2ManyList })); this.on('edit:before', this, this.proxy('_before_edit')); + this.on('edit:after', this, this.proxy('_after_edit')); this.on('save:before cancel:before', this, this.proxy('_before_unedit')); this.records @@ -3459,6 +3460,12 @@ instance.web.form.One2ManyListView = instance.web.ListView.extend({ this.__ignore_blur = false; this.editor.form.on('blurred', this, this._on_form_blur); }, + _after_edit: function () { + // The form's blur thing may be jiggered during the edition setup, + // potentially leading to the o2m instasaving the row. Cancel any + // blurring triggered the edition startup here + this.editor.form.widgetFocused(); + }, _before_unedit: function () { this.editor.form.off('blurred', this, this._on_form_blur); }, @@ -3492,6 +3499,28 @@ instance.web.form.One2ManyListView = instance.web.ListView.extend({ // the current row *anyway*, then create a new one/edit the next one) this.__ignore_blur = true; this._super.apply(this, arguments); + }, + do_delete: function (ids) { + var self = this; + var next = $.when(); + var _super = this._super; + // handle deletion of an item which does not exist + // TODO: better handle that in the editable list? + var false_id_index = _(ids).indexOf(false); + if (false_id_index !== -1) { + ids.splice(false_id_index, 1); + next = this.cancel_edition(true); + } + return next.pipe(function () { + // wheeee + var confirm = window.confirm; + window.confirm = function () { return true; }; + try { + return _super.call(self, ids); + } finally { + window.confirm = confirm; + } + }); } }); instance.web.form.One2ManyList = instance.web.ListView.List.extend({ @@ -3512,27 +3541,34 @@ instance.web.form.One2ManyList = instance.web.ListView.List.extend({ var $cell = $('', { colspan: columns, 'class': 'oe_form_field_one2many_list_row_add' - }).text(_t("Add a row")) - .mousedown(function () { - // FIXME: needs to be an official API somehow - if (self.view.editor.is_editing()) { - self.view.__ignore_blur = true; - } - }) - .click(function (e) { - e.preventDefault(); - e.stopPropagation(); - // FIXME: there should also be an API for that one - if (self.view.editor.form.__blur_timeout) { - clearTimeout(self.view.editor.form.__blur_timeout); - self.view.editor.form.__blur_timeout = false; - } - self.view.ensure_saved().then(function () { - self.view.do_add_record(); - }); - }); - this.$current.append( - $('').append($cell)) + }).append( + $('', {href: '#'}).text(_t("Add a row")) + .mousedown(function () { + // FIXME: needs to be an official API somehow + if (self.view.editor.is_editing()) { + self.view.__ignore_blur = true; + } + }) + .click(function (e) { + e.preventDefault(); + e.stopPropagation(); + // FIXME: there should also be an API for that one + if (self.view.editor.form.__blur_timeout) { + clearTimeout(self.view.editor.form.__blur_timeout); + self.view.editor.form.__blur_timeout = false; + } + self.view.ensure_saved().then(function () { + self.view.do_add_record(); + }); + })); + + var $padding = this.$current.find('tr:not([data-id]):first'); + var $newrow = $('').append($cell); + if ($padding.length) { + $padding.before($newrow); + } else { + this.$current.append($newrow) + } } }); diff --git a/addons/web/static/src/js/view_list.js b/addons/web/static/src/js/view_list.js index 57be3ca6db1..4bce8d639f8 100644 --- a/addons/web/static/src/js/view_list.js +++ b/addons/web/static/src/js/view_list.js @@ -674,6 +674,10 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi * Base handling of buttons, can be called when overriding do_button_action * in order to bypass parent overrides. * + * The callback will be provided with the ``id`` as its parameter, in case + * handle_button's caller had to alter the ``id`` (or even create one) + * while not being ``callback``'s creator. + * * This method should not be overridden. * * @param {String} name action name @@ -699,7 +703,8 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi c.add(action.context); } action.context = c; - this.do_execute_action(action, this.dataset, id, callback); + this.do_execute_action( + action, this.dataset, id, _.bind(callback, null, id)); }, /** * Handles the activation of a record (clicking on it) @@ -991,8 +996,8 @@ instance.web.ListView.List = instance.web.Class.extend( /** @lends instance.web. // of digits, nice when storing actual numbers, not nice when // storing strings composed only of digits. Force the action // name to be a string - $(self).trigger('action', [field.toString(), record_id, function () { - return self.reload_record(self.records.get(record_id)); + $(self).trigger('action', [field.toString(), record_id, function (id) { + return self.reload_record(self.records.get(id)); }]); }) .delegate('a', 'click', function (e) { diff --git a/addons/web/static/src/js/view_list_editable.js b/addons/web/static/src/js/view_list_editable.js index 038593c54c3..78fef767f5a 100644 --- a/addons/web/static/src/js/view_list_editable.js +++ b/addons/web/static/src/js/view_list_editable.js @@ -135,10 +135,13 @@ openerp.web.list_editable = function (instance) { make_editor: function () { return new instance.web.list.Editor(this); }, - do_button_action: function () { + do_button_action: function (name, id, callback) { var self = this, args = arguments; - this.ensure_saved().then(function () { - self.handle_button.apply(self, args); + this.ensure_saved().then(function (done) { + if (!id && done.created) { + id = done.record.get('id'); + } + self.handle_button.call(self, name, id, callback); }); }, /** @@ -281,16 +284,17 @@ openerp.web.list_editable = function (instance) { }); }, /** + * @param {Boolean} [force=false] discards the data even if the form has been edited * @return {jQuery.Deferred} */ - cancel_edition: function () { + cancel_edition: function (force) { var self = this; return this.with_event('cancel', { editor: this.editor, form: this.editor.form, cancel: false }, function () { - return this.editor.cancel().pipe(function (attrs) { + return this.editor.cancel(force).pipe(function (attrs) { if (attrs.id) { var record = self.records.get(attrs.id); if (!record) { @@ -717,13 +721,13 @@ openerp.web.list_editable = function (instance) { return self.cancel(); }); }, - cancel: function () { - var record = this.record; - this.record = null; - if (!this.form.can_be_discarded()) { + cancel: function (force) { + if (!(force || this.form.can_be_discarded())) { return $.Deferred().reject({ message: "The form's data can not be discarded"}).promise(); } + var record = this.record; + this.record = null; this.form.do_hide(); return $.when(record); } diff --git a/doc/list-view.rst b/doc/list-view.rst index 83925d82f46..24c7bb3beb4 100644 --- a/doc/list-view.rst +++ b/doc/list-view.rst @@ -160,11 +160,14 @@ Interaction Methods updated) and ``record`` the reloaded record having been edited. -.. js:function:: openerp.web.ListView.cancel_edition +.. js:function:: openerp.web.ListView.cancel_edition([force=false]) Cancels pending edition, cleans up the list view in case of creation (removes the empty record being created). + :param Boolean force: doesn't check if the user has added any + data, discards the edition unconditionally + Utility Methods +++++++++++++++ @@ -345,11 +348,14 @@ formview, delegating instead to its from when it was passed in, aside from the ``id`` attribute. - .. js:function:: openerp.web.list.Editor.cancel + .. js:function:: openerp.web.list.Editor.cancel([force=false]) Attemps to cancel the edition of the internal form, then hide the form + :param Boolean force: unconditionally cancels the edition of + the internal form, even if the user has + already entered data in it. :returns: delegate to the record under edition .. js:class:: EditorOptions