From cd3e60aef10a74fd04580b6e5903ee319f160604 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Tue, 31 May 2011 13:23:39 +0200 Subject: [PATCH 01/36] [ADD] editability status to ListView options (hence all sub-objects of the list view) bzr revid: xmo@openerp.com-20110531112339-701lpf42p8h19mfj --- addons/base/static/src/js/list.js | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/addons/base/static/src/js/list.js b/addons/base/static/src/js/list.js index 2fd4aff2e1c..a64cb0c0021 100644 --- a/addons/base/static/src/js/list.js +++ b/addons/base/static/src/js/list.js @@ -14,7 +14,9 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi // sorted it can not be reordered anymore 'sortable': true, // whether the view rows can be reordered (via vertical drag & drop) - 'reorderable': true + 'reorderable': true, + // editability status of list rows + 'editable': null }, /** * Core class for list-type displays. @@ -39,6 +41,7 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi * @param {null|String} [options.addable="New"] should the new-record button be displayed, and what should its label be. Use ``null`` to hide the button. * @param {Boolean} [options.sortable=true] is it possible to sort the table by clicking on column headers * @param {Boolean} [options.reorderable=true] is it possible to reorder list rows + * @param {null|"bottom"|"top"} [options.editable=null] can rows be edited, and do new rows appear at the top or the bottom of the list * * @borrows openerp.base.ActionExecutor#execute_action as #execute_action */ @@ -270,6 +273,21 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi this.groups.apoptosis().render( $.proxy(this, 'compute_aggregates'))); }, + /** + * Sets editability status for the list, based on defaults, view + * architecture and the provided flag, if any. + * + * @param {Boolean} [force] forces the list to editability. Sets new row edition status to "bottom". + */ + set_editable: function (force) { + // If ``force``, set editability to bottom + // else if editability flag in view arch, use that + // otherwise rely on view default + this.options.editable = ( + (force && "bottom") + || this.fields_view.arch.attrs.editable + || this.defaults.editable); + }, /** * Event handler for a search, asks for the computation/folding of domains * and contexts (and group-by), then reloads the view's content. @@ -296,6 +314,8 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi if (_.isEmpty(results.group_by) && !results.context['group_by_no_leaf']) { results.group_by = null; } + self.set_editable(results.context['set_editable']); + self.reload_view(!!results.group_by).then( $.proxy(self, 'reload_content')); }); From 0d21e3b07444a6bad123b97e81994a946fcee4e0 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Wed, 1 Jun 2011 14:49:54 +0200 Subject: [PATCH 02/36] [ADD] display editable rows when selecting row in editable mode bzr revid: xmo@openerp.com-20110601124954-vog8ceoohczxgmqe --- addons/base/static/src/js/form.js | 6 ++- addons/base/static/src/js/list.js | 57 +++++++++++++++++++++++++---- addons/base/static/src/xml/base.xml | 3 ++ 3 files changed, 58 insertions(+), 8 deletions(-) diff --git a/addons/base/static/src/js/form.js b/addons/base/static/src/js/form.js index 98c8cd75267..3b55b86a471 100644 --- a/addons/base/static/src/js/form.js +++ b/addons/base/static/src/js/form.js @@ -8,6 +8,7 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormV * view should be displayed (if there is one active). */ searchable: false, + template: "FormView", /** * @constructs * @param {openerp.base.Session} session the current openerp session @@ -42,7 +43,7 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormV var frame = new openerp.base.form.WidgetFrame(this, this.fields_view.arch); - this.$element.html(QWeb.render("FormView", { 'frame': frame, 'view': this })); + this.$element.html(QWeb.render(this.template, { 'frame': frame, 'view': this })); _.each(this.widgets, function(w) { w.start(); }); @@ -449,6 +450,9 @@ openerp.base.form.Widget = openerp.base.Controller.extend({ } }, update_dom: function() { + if (this.attrs.name === 'someval') { + console.log(this.invisible); + } this.$element.toggle(!this.invisible); }, render: function() { diff --git a/addons/base/static/src/js/list.js b/addons/base/static/src/js/list.js index a64cb0c0021..e7922df67eb 100644 --- a/addons/base/static/src/js/list.js +++ b/addons/base/static/src/js/list.js @@ -532,8 +532,9 @@ openerp.base.ListView.List = Class.extend( /** @lends openerp.base.ListView.List * @constructs * @param {Object} opts display options, identical to those of :js:class:`openerp.base.ListView` */ - init: function (opts) { + init: function (group, opts) { var self = this; + this.group = group; this.options = opts.options; this.columns = opts.columns; @@ -563,11 +564,18 @@ openerp.base.ListView.List = Class.extend( /** @lends openerp.base.ListView.List }) .delegate('tr', 'click', function (e) { e.stopPropagation(); - $(self).trigger( - 'row_link', - [self.row_position(e.currentTarget), - self.row_id(e.currentTarget), - self.dataset]); + var index = self.row_position(e.currentTarget); + self.dataset.index = index; + if (self.options.editable) { + self.render_row_as_form( + index, e.currentTarget); + } else { + $(self).trigger( + 'row_link', + [index, + self.rows[index].data.id.value, + self.dataset]); + } }); }, render: function () { @@ -577,6 +585,41 @@ openerp.base.ListView.List = Class.extend( /** @lends openerp.base.ListView.List this.$current = this.$_element.clone(true); this.$current.empty().append($(QWeb.render('ListView.rows', this))); }, + get_fields_view: function () { + // deep copy of view + var view = $.extend(true, {}, this.group.view.fields_view); + _(view.arch.children).each(function (widget) { + widget.attrs.nolabel = true; + if (widget.tag === 'button') { + delete widget.attrs.string; + } + }); + view.arch.attrs.col = 2 * view.arch.children.length; + return view; + }, + render_row_as_form: function (row_num, row) { + var $new_row = $('', { + id: _.uniqueId('oe-editable-row-'), + 'class': $(row).attr('class'), + onclick: function (e) {e.stopPropagation();} + }).replaceAll(row); + var editable_row_form = new openerp.base.FormView( + null, this.group.view.session, $new_row.attr('id'), + this.dataset, false); + editable_row_form.template = 'ListView.row.form'; + editable_row_form.on_loaded({fields_view: this.get_fields_view()}); + editable_row_form.on_record_loaded.add({ + position: 'last', + unique: true, + callback: function () { + editable_row_form.$element.find('td') + // remove tr, tbody, table + .unwrap().unwrap().unwrap() + .removeAttr('width'); + } + }); + editable_row_form.do_show(); + }, /** * Gets the ids of all currently selected records, if any * @returns {Object} object with the keys ``ids`` and ``records``, holding respectively the ids of all selected records and the records themselves. @@ -817,7 +860,7 @@ openerp.base.ListView.Groups = Class.extend( /** @lends openerp.base.ListView.Gr }, render_dataset: function (dataset) { var rows = [], - list = new openerp.base.ListView.List({ + list = new openerp.base.ListView.List(this, { options: this.options, columns: this.columns, dataset: dataset, diff --git a/addons/base/static/src/xml/base.xml b/addons/base/static/src/xml/base.xml index b8412aa1b19..cbd3853a08f 100644 --- a/addons/base/static/src/xml/base.xml +++ b/addons/base/static/src/xml/base.xml @@ -253,6 +253,9 @@ + + +

From 3895bcb954798c818b47e56bf6600a1a9d9b86dd Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Fri, 3 Jun 2011 11:36:06 +0200 Subject: [PATCH 03/36] [MOVE] editability code for listviews in a separate files: customizations of Form View require load-time dependencies on form.js, but form.js already depends on ListView (in order to create m2m widgets and sub-widgets), so we need to execute that part after both openep.base.list and openerp.base.form have loaded bzr revid: xmo@openerp.com-20110603093606-gw00iidxfakjmvt3 --- addons/base/static/src/base.html | 1 + addons/base/static/src/js/base.js | 1 + addons/base/static/src/js/list-editable.js | 66 +++++++++++++++ addons/base/static/src/js/list.js | 97 +++++++--------------- 4 files changed, 97 insertions(+), 68 deletions(-) create mode 100644 addons/base/static/src/js/list-editable.js diff --git a/addons/base/static/src/base.html b/addons/base/static/src/base.html index 467bccb92ff..71a6a2aabf6 100644 --- a/addons/base/static/src/base.html +++ b/addons/base/static/src/base.html @@ -28,6 +28,7 @@ + diff --git a/addons/base/static/src/js/base.js b/addons/base/static/src/js/base.js index 6316715df89..2cab4460e5c 100644 --- a/addons/base/static/src/js/base.js +++ b/addons/base/static/src/js/base.js @@ -132,6 +132,7 @@ openerp.base = function(instance) { openerp.base.tree(instance); openerp.base.m2o(instance); openerp.base.form(instance); + openerp.base.list.editable(instance); }; // vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax: diff --git a/addons/base/static/src/js/list-editable.js b/addons/base/static/src/js/list-editable.js new file mode 100644 index 00000000000..2ff9e71e98b --- /dev/null +++ b/addons/base/static/src/js/list-editable.js @@ -0,0 +1,66 @@ +/** + * @namespace handles editability case for lists, because it depends on form and forms already depends on lists it had to be split out + */ +openerp.base.list.editable = function (openerp) { + // editability status of list rows + openerp.base.ListView.prototype.defaults.editable = null; + + var old_actual_search = openerp.base.ListView.prototype.do_actual_search; + _.extend(openerp.base.ListView.prototype, { + /** + * Sets editability status for the list, based on defaults, view + * architecture and the provided flag, if any. + * + * @param {Boolean} [force] forces the list to editability. Sets new row edition status to "bottom". + */ + set_editable: function (force) { + // If ``force``, set editability to bottom + // else if editability flag in view arch, use that + // otherwise rely on view default + this.options.editable = ( + (force && "bottom") + || this.fields_view.arch.attrs.editable + || this.defaults.editable); + }, + /** + * Replace do_actual_search to handle editability process + */ + do_actual_search: function (results) { + this.set_editable(results.context['set_editable']); + old_actual_search.call(this, results); + } + }); + + var old_list_row_clicked = openerp.base.ListView.List.prototype.row_clicked; + _.extend(openerp.base.ListView.List.prototype, { + row_clicked: function (event, index) { + if (!this.options.editable) { + return old_list_row_clicked.call(this, event, index); + } + this.render_row_as_form(index, event.currentTarget); + }, + render_row_as_form: function (row_num, row) { + var $new_row = $('', { + id: _.uniqueId('oe-editable-row-'), + 'class': $(row).attr('class'), + onclick: function (e) {e.stopPropagation();} + }).replaceAll(row); + var editable_row_form = new openerp.base.FormView( + null, this.group.view.session, $new_row.attr('id'), + this.dataset, false); + editable_row_form.template = 'ListView.row.form'; + editable_row_form.on_loaded({fields_view: this.get_fields_view()}); + editable_row_form.on_record_loaded.add({ + position: 'last', + unique: true, + callback: function () { + editable_row_form.$element.find('td') + // remove tr, tbody, table + .unwrap().unwrap().unwrap() + .removeAttr('width'); + } + }); + editable_row_form.do_show(); + } + }); +}; diff --git a/addons/base/static/src/js/list.js b/addons/base/static/src/js/list.js index e7922df67eb..cd4bcde8008 100644 --- a/addons/base/static/src/js/list.js +++ b/addons/base/static/src/js/list.js @@ -14,9 +14,7 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi // sorted it can not be reordered anymore 'sortable': true, // whether the view rows can be reordered (via vertical drag & drop) - 'reorderable': true, - // editability status of list rows - 'editable': null + 'reorderable': true }, /** * Core class for list-type displays. @@ -41,7 +39,6 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi * @param {null|String} [options.addable="New"] should the new-record button be displayed, and what should its label be. Use ``null`` to hide the button. * @param {Boolean} [options.sortable=true] is it possible to sort the table by clicking on column headers * @param {Boolean} [options.reorderable=true] is it possible to reorder list rows - * @param {null|"bottom"|"top"} [options.editable=null] can rows be edited, and do new rows appear at the top or the bottom of the list * * @borrows openerp.base.ActionExecutor#execute_action as #execute_action */ @@ -273,21 +270,6 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi this.groups.apoptosis().render( $.proxy(this, 'compute_aggregates'))); }, - /** - * Sets editability status for the list, based on defaults, view - * architecture and the provided flag, if any. - * - * @param {Boolean} [force] forces the list to editability. Sets new row edition status to "bottom". - */ - set_editable: function (force) { - // If ``force``, set editability to bottom - // else if editability flag in view arch, use that - // otherwise rely on view default - this.options.editable = ( - (force && "bottom") - || this.fields_view.arch.attrs.editable - || this.defaults.editable); - }, /** * Event handler for a search, asks for the computation/folding of domains * and contexts (and group-by), then reloads the view's content. @@ -298,27 +280,32 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi * @returns {$.Deferred} fold request evaluation promise */ do_search: function (domains, contexts, groupbys) { - var self = this; return this.rpc('/base/session/eval_domain_and_context', { domains: domains, contexts: contexts, group_by_seq: groupbys - }, function (results) { - self.dataset.context = results.context; - self.dataset.domain = results.domain; - self.groups.datagroup = new openerp.base.DataGroup( - self.session, self.model, - results.domain, results.context, - results.group_by); + }, $.proxy(this, 'do_actual_search')); + }, + /** + * Handler for the result of eval_domain_and_context, actually perform the + * searching + * + * @param {Object} results results of evaluating domain and process for a search + */ + do_actual_search: function (results) { + this.dataset.context = results.context; + this.dataset.domain = results.domain; + this.groups.datagroup = new openerp.base.DataGroup( + this.session, this.model, + results.domain, results.context, + results.group_by); - if (_.isEmpty(results.group_by) && !results.context['group_by_no_leaf']) { - results.group_by = null; - } - self.set_editable(results.context['set_editable']); + if (_.isEmpty(results.group_by) && !results.context['group_by_no_leaf']) { + results.group_by = null; + } - self.reload_view(!!results.group_by).then( - $.proxy(self, 'reload_content')); - }); + this.reload_view(!!results.group_by).then( + $.proxy(this, 'reload_content')); }, /** * Handles the signal to delete a line from the DOM @@ -566,18 +553,16 @@ openerp.base.ListView.List = Class.extend( /** @lends openerp.base.ListView.List e.stopPropagation(); var index = self.row_position(e.currentTarget); self.dataset.index = index; - if (self.options.editable) { - self.render_row_as_form( - index, e.currentTarget); - } else { - $(self).trigger( - 'row_link', - [index, - self.rows[index].data.id.value, - self.dataset]); - } + self.row_clicked(e, index); }); }, + row_clicked: function (event, index) { + $(this).trigger( + 'row_link', + [index, + this.rows[index].data.id.value, + this.dataset]); + }, render: function () { if (this.$current) { this.$current.remove(); @@ -597,29 +582,6 @@ openerp.base.ListView.List = Class.extend( /** @lends openerp.base.ListView.List view.arch.attrs.col = 2 * view.arch.children.length; return view; }, - render_row_as_form: function (row_num, row) { - var $new_row = $('', { - id: _.uniqueId('oe-editable-row-'), - 'class': $(row).attr('class'), - onclick: function (e) {e.stopPropagation();} - }).replaceAll(row); - var editable_row_form = new openerp.base.FormView( - null, this.group.view.session, $new_row.attr('id'), - this.dataset, false); - editable_row_form.template = 'ListView.row.form'; - editable_row_form.on_loaded({fields_view: this.get_fields_view()}); - editable_row_form.on_record_loaded.add({ - position: 'last', - unique: true, - callback: function () { - editable_row_form.$element.find('td') - // remove tr, tbody, table - .unwrap().unwrap().unwrap() - .removeAttr('width'); - } - }); - editable_row_form.do_show(); - }, /** * Gets the ids of all currently selected records, if any * @returns {Object} object with the keys ``ids`` and ``records``, holding respectively the ids of all selected records and the records themselves. @@ -679,7 +641,6 @@ openerp.base.ListView.List = Class.extend( /** @lends openerp.base.ListView.List }); } // drag and drop - // editable? }); openerp.base.ListView.Groups = Class.extend( /** @lends openerp.base.ListView.Groups# */{ /** From 3990af373cca7b702262911623d4a9dcfef7a881 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Fri, 3 Jun 2011 11:38:17 +0200 Subject: [PATCH 04/36] [IMP] add ability to clone-and-extend registries bzr revid: xmo@openerp.com-20110603093817-om68h34w9s8qhxpv --- addons/base/static/src/js/chrome.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/addons/base/static/src/js/chrome.js b/addons/base/static/src/js/chrome.js index bc7bec93d59..cb4c8070b2b 100644 --- a/addons/base/static/src/js/chrome.js +++ b/addons/base/static/src/js/chrome.js @@ -149,6 +149,16 @@ openerp.base.Registry = Class.extend( /** @lends openerp.base.Registry# */ { add: function (key, object_path) { this.map[key] = object_path; return this; + }, + /** + * Creates and returns a copy of the current mapping, with the provided + * mapping argument added in (replacing existing keys if needed) + * + * @param {Object} [mapping={}] a mapping of keys to object-paths + */ + clone: function (mapping) { + return new openerp.base.Registry( + _.extend({}, this.map, mapping || {})); } }); From a2ba109315b2d2c5ed2ff8668658c54a30bb20c6 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Fri, 3 Jun 2011 11:43:02 +0200 Subject: [PATCH 05/36] [IMP] make form widgets registry pluggable, have form go get its root Frame widget in the registry instead of hardcoding the relevant class bzr revid: xmo@openerp.com-20110603094302-hh09kzswb5kcfpye --- addons/base/static/src/js/form.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/addons/base/static/src/js/form.js b/addons/base/static/src/js/form.js index 3b55b86a471..04fe95ee37f 100644 --- a/addons/base/static/src/js/form.js +++ b/addons/base/static/src/js/form.js @@ -15,6 +15,8 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormV * @param {String} element_id this view's root element id * @param {openerp.base.DataSet} dataset the dataset this view will work with * @param {String} view_id the identifier of the OpenERP view object + * + * @property {openerp.base.Registry} registry=openerp.base.form.widgets widgets registry for this form view instance */ init: function(view_manager, session, element_id, dataset, view_id) { this._super(session, element_id); @@ -31,6 +33,7 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormV this.show_invalid = true; this.touched = false; this.flags = this.view_manager.action.flags || {}; + this.registry = openerp.base.form.widgets; }, start: function() { //this.log('Starting FormView '+this.model+this.view_id) @@ -41,7 +44,7 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormV var self = this; this.fields_view = data.fields_view; - var frame = new openerp.base.form.WidgetFrame(this, this.fields_view.arch); + var frame = new (this.registry.get_object('frame'))(this, this.fields_view.arch); this.$element.html(QWeb.render(this.template, { 'frame': frame, 'view': this })); _.each(this.widgets, function(w) { @@ -506,9 +509,9 @@ openerp.base.form.WidgetFrame = openerp.base.form.Widget.extend({ handle_node: function(node) { var type = this.view.fields_view.fields[node.attrs.name] || {}; var widget_type = node.attrs.widget || type.type || node.tag; - var widget = new (openerp.base.form.widgets.get_object(widget_type)) (this.view, node); + var widget = new (this.view.registry.get_object(widget_type)) (this.view, node); if (node.tag == 'field' && node.attrs.nolabel != '1') { - var label = new (openerp.base.form.widgets.get_object('label')) (this.view, node); + var label = new (this.view.registry.get_object('label')) (this.view, node); label["for"] = widget; this.add_widget(label); } @@ -1370,6 +1373,7 @@ openerp.base.form.FieldBinaryImage = openerp.base.form.FieldBinary.extend({ * Registry of form widgets, called by :js:`openerp.base.FormView` */ openerp.base.form.widgets = new openerp.base.Registry({ + 'frame' : 'openerp.base.form.WidgetFrame', 'group' : 'openerp.base.form.WidgetFrame', 'notebook' : 'openerp.base.form.WidgetNotebook', 'separator' : 'openerp.base.form.WidgetSeparator', From 6037c0a65041b023f08cbd4474fd7d41a1e7c048 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Fri, 3 Jun 2011 12:31:00 +0200 Subject: [PATCH 06/36] [ADD] basic overriding of WidgetFrame template: remove and display levels bzr revid: xmo@openerp.com-20110603103100-c0di95c1z5rd1ogh --- addons/base/static/src/js/form.js | 4 ++-- addons/base/static/src/js/list-editable.js | 17 ++++++++++++----- addons/base/static/src/xml/base.xml | 15 +++++++++++++++ 3 files changed, 29 insertions(+), 7 deletions(-) diff --git a/addons/base/static/src/js/form.js b/addons/base/static/src/js/form.js index 04fe95ee37f..dd837960e26 100644 --- a/addons/base/static/src/js/form.js +++ b/addons/base/static/src/js/form.js @@ -424,6 +424,7 @@ openerp.base.form.compute_domain = function(expr, fields) { }; openerp.base.form.Widget = openerp.base.Controller.extend({ + template: 'Widget', init: function(view, node) { this.view = view; this.node = node; @@ -437,7 +438,6 @@ openerp.base.form.Widget = openerp.base.Controller.extend({ this.view.widgets[this.element_id] = this; this.children = node.children; this.colspan = parseInt(node.attrs.colspan || 1); - this.template = "Widget"; this.string = this.string || node.attrs.string; this.help = this.help || node.attrs.help; @@ -465,9 +465,9 @@ openerp.base.form.Widget = openerp.base.Controller.extend({ }); openerp.base.form.WidgetFrame = openerp.base.form.Widget.extend({ + template: 'WidgetFrame', init: function(view, node) { this._super(view, node); - this.template = "WidgetFrame"; this.columns = node.attrs.col || 4; this.x = 0; this.y = 0; diff --git a/addons/base/static/src/js/list-editable.js b/addons/base/static/src/js/list-editable.js index 2ff9e71e98b..dd57ae2b69a 100644 --- a/addons/base/static/src/js/list-editable.js +++ b/addons/base/static/src/js/list-editable.js @@ -45,22 +45,29 @@ openerp.base.list.editable = function (openerp) { 'class': $(row).attr('class'), onclick: function (e) {e.stopPropagation();} }).replaceAll(row); - var editable_row_form = new openerp.base.FormView( + var editable_row_form = _.extend(new openerp.base.FormView( null, this.group.view.session, $new_row.attr('id'), - this.dataset, false); - editable_row_form.template = 'ListView.row.form'; + this.dataset, false), { + template: 'ListView.row.form', + registry: openerp.base.list.form.widgets + }); editable_row_form.on_loaded({fields_view: this.get_fields_view()}); editable_row_form.on_record_loaded.add({ position: 'last', unique: true, callback: function () { editable_row_form.$element.find('td') - // remove tr, tbody, table - .unwrap().unwrap().unwrap() .removeAttr('width'); } }); editable_row_form.do_show(); } }); + openerp.base.list = {form: {}}; + openerp.base.list.form.WidgetFrame = openerp.base.form.WidgetFrame.extend({ + template: 'ListView.row.frame' + }); + openerp.base.list.form.widgets = openerp.base.form.widgets.clone({ + 'frame': 'openerp.base.list.form.WidgetFrame' + }); }; diff --git a/addons/base/static/src/xml/base.xml b/addons/base/static/src/xml/base.xml index cbd3853a08f..d40ed614176 100644 --- a/addons/base/static/src/xml/base.xml +++ b/addons/base/static/src/xml/base.xml @@ -790,4 +790,19 @@ + + + + $(document.createElement('t')) + .append(this.contents()) + .attr({ + 't-foreach': this.attr('t-foreach'), + 't-as': this.attr('t-as') + }) + .replaceAll(this) + .before($(document.createElement('td'))) + .after($(document.createElement('td'))) + .unwrap(); + + From 63484a8570e832af16a865eb882369726bfc5cd6 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Fri, 3 Jun 2011 13:06:39 +0200 Subject: [PATCH 07/36] [FIX] style of listview editable row buttons, remove borders (to match non-editable rows) bzr revid: xmo@openerp.com-20110603110639-9j17krhnsuf5q1bg --- addons/base/static/src/css/base.css | 1 + addons/base/static/src/js/form.js | 3 --- addons/base/static/src/xml/base.xml | 5 +++++ 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/addons/base/static/src/css/base.css b/addons/base/static/src/css/base.css index d9042e7e251..ffe23ad16a3 100644 --- a/addons/base/static/src/css/base.css +++ b/addons/base/static/src/css/base.css @@ -543,6 +543,7 @@ background: linear-gradient(top, #ffffff 0%,#d8d8d8 11%,#afafaf 86%,#333333 91%, padding: 0; border: none; background: none; + width: 100%; } .openerp .oe-listview .oe-field-cell button:active { opacity: 0.5; diff --git a/addons/base/static/src/js/form.js b/addons/base/static/src/js/form.js index dd837960e26..d225a9cb47a 100644 --- a/addons/base/static/src/js/form.js +++ b/addons/base/static/src/js/form.js @@ -453,9 +453,6 @@ openerp.base.form.Widget = openerp.base.Controller.extend({ } }, update_dom: function() { - if (this.attrs.name === 'someval') { - console.log(this.invisible); - } this.$element.toggle(!this.invisible); }, render: function() { diff --git a/addons/base/static/src/xml/base.xml b/addons/base/static/src/xml/base.xml index d40ed614176..c63a7429d6e 100644 --- a/addons/base/static/src/xml/base.xml +++ b/addons/base/static/src/xml/base.xml @@ -800,6 +800,11 @@ 't-as': this.attr('t-as') }) .replaceAll(this) + .find('td') + .attr('t-att-class', function (index, value) { + return "(" + value + ") + ' oe-field-cell'"; + }) + .end() .before($(document.createElement('td'))) .after($(document.createElement('td'))) .unwrap(); From 8c9d79e1325da5251915224d4d37fed27099204d Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Fri, 3 Jun 2011 13:10:44 +0200 Subject: [PATCH 08/36] [IMP] move addition of oe-field-cell on cells of editable rows to post-processing rather than template alteration bzr revid: xmo@openerp.com-20110603111044-hcrqubshpnc6mzux --- addons/base/static/src/js/list-editable.js | 1 + addons/base/static/src/xml/base.xml | 5 ----- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/addons/base/static/src/js/list-editable.js b/addons/base/static/src/js/list-editable.js index dd57ae2b69a..5bf6021f341 100644 --- a/addons/base/static/src/js/list-editable.js +++ b/addons/base/static/src/js/list-editable.js @@ -57,6 +57,7 @@ openerp.base.list.editable = function (openerp) { unique: true, callback: function () { editable_row_form.$element.find('td') + .addClass('oe-field-cell') .removeAttr('width'); } }); diff --git a/addons/base/static/src/xml/base.xml b/addons/base/static/src/xml/base.xml index c63a7429d6e..d40ed614176 100644 --- a/addons/base/static/src/xml/base.xml +++ b/addons/base/static/src/xml/base.xml @@ -800,11 +800,6 @@ 't-as': this.attr('t-as') }) .replaceAll(this) - .find('td') - .attr('t-att-class', function (index, value) { - return "(" + value + ") + ' oe-field-cell'"; - }) - .end() .before($(document.createElement('td'))) .after($(document.createElement('td'))) .unwrap(); From 47d696ef4e350a69ad5156f57dd5c9b0c40ba409 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Fri, 3 Jun 2011 13:57:57 +0200 Subject: [PATCH 09/36] [IMP] awful hack to hide the value but not the cell itself in editable row cells when it is invisible from an attrs evaluation rather than from @invisible bzr revid: xmo@openerp.com-20110603115757-4z67r08g1sfogfgw --- addons/base/static/src/js/list-editable.js | 25 +++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/addons/base/static/src/js/list-editable.js b/addons/base/static/src/js/list-editable.js index 5bf6021f341..8c66dd71dd5 100644 --- a/addons/base/static/src/js/list-editable.js +++ b/addons/base/static/src/js/list-editable.js @@ -68,7 +68,30 @@ openerp.base.list.editable = function (openerp) { openerp.base.list.form.WidgetFrame = openerp.base.form.WidgetFrame.extend({ template: 'ListView.row.frame' }); - openerp.base.list.form.widgets = openerp.base.form.widgets.clone({ + var form_widgets = openerp.base.form.widgets; + openerp.base.list.form.widgets = form_widgets.clone({ 'frame': 'openerp.base.list.form.WidgetFrame' }); + // All form widgets inherit a problematic behavior from + // openerp.base.form.WidgetFrame: the cell itself is removed when invisible + // whether it's @invisible or @attrs[invisible]. In list view, only the + // former should completely remove the cell. We need to override update_dom + // on all widgets since we can't just hit on widget itself (I think) + var list_form_widgets = openerp.base.list.form.widgets; + _(list_form_widgets.map).each(function (widget_path, key) { + if (key === 'frame') { return; } + var new_path = 'openerp.base.list.form.' + key; + + openerp.base.list.form[key] = (form_widgets.get_object(key)).extend({ + update_dom: function () { + this.$element.children().css('visibility', ''); + if (this.invisible && this.node.attrs.invisible !== '1') { + this.$element.children().css('visibility', 'hidden'); + } else { + this._super(); + } + } + }); + list_form_widgets.add(key, new_path); + }); }; From af71f903dc40f07d97641ece8b55854e1a253c73 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Fri, 3 Jun 2011 14:24:43 +0200 Subject: [PATCH 10/36] [ADD] basic (and crummy) saving of editable rows TODO: * Switch back to read row after saving * Edit next row if [return] * How do things interact with onchanges? * Handle validation errors bzr revid: xmo@openerp.com-20110603122443-fvmfouuybh69b48g --- addons/base/static/src/js/list-editable.js | 47 ++++++++++++++++++---- addons/base/static/src/xml/base.xml | 4 +- 2 files changed, 42 insertions(+), 9 deletions(-) diff --git a/addons/base/static/src/js/list-editable.js b/addons/base/static/src/js/list-editable.js index 8c66dd71dd5..d005d58022d 100644 --- a/addons/base/static/src/js/list-editable.js +++ b/addons/base/static/src/js/list-editable.js @@ -40,28 +40,59 @@ openerp.base.list.editable = function (openerp) { this.render_row_as_form(index, event.currentTarget); }, render_row_as_form: function (row_num, row) { + var self = this; var $new_row = $('', { id: _.uniqueId('oe-editable-row-'), 'class': $(row).attr('class'), onclick: function (e) {e.stopPropagation();} - }).replaceAll(row); - var editable_row_form = _.extend(new openerp.base.FormView( + }).replaceAll(row) + .keyup(function (e) { + if (e.which !== 13) { + return; + } + self.save_row(row_num, true); + }) + .delegate('button.oe-list-save', 'click', function () { + self.save_row(row_num); + }); + this.edition_form = _.extend(new openerp.base.FormView( null, this.group.view.session, $new_row.attr('id'), this.dataset, false), { template: 'ListView.row.form', registry: openerp.base.list.form.widgets }); - editable_row_form.on_loaded({fields_view: this.get_fields_view()}); - editable_row_form.on_record_loaded.add({ + this.edition_form.on_loaded({fields_view: this.get_fields_view()}); + this.edition_form.on_record_loaded.add({ position: 'last', unique: true, callback: function () { - editable_row_form.$element.find('td') - .addClass('oe-field-cell') - .removeAttr('width'); + self.edition_form.$element + .find('td') + .addClass('oe-field-cell') + .removeAttr('width') + .end() + .find('td:first').removeClass('oe-field-cell').end() + .find('td:last').removeClass('oe-field-cell').end(); + } + }); + this.edition_form.do_show(); + }, + /** + * Saves the current row, and triggers the edition of its following + * sibling if asked. + * + * @param {Number} row_num the row to save + * @param {Boolean} [edit_next=false] should the next row become editable + */ + save_row: function (row_num, edit_next) { + var self = this; + this.edition_form.do_save(function () { + // nuke form + // re-render current row + if (edit_next) { + // self.row_clicked({currentTarget: current_row.nextSibling}, row_num + 1) } }); - editable_row_form.do_show(); } }); openerp.base.list = {form: {}}; diff --git a/addons/base/static/src/xml/base.xml b/addons/base/static/src/xml/base.xml index d40ed614176..d1633bdb150 100644 --- a/addons/base/static/src/xml/base.xml +++ b/addons/base/static/src/xml/base.xml @@ -800,7 +800,9 @@ 't-as': this.attr('t-as') }) .replaceAll(this) - .before($(document.createElement('td'))) + .before($(document.createElement('td')).append( + $(document.createElement('button')).attr({ + 'class': 'oe-list-save', 'type': 'button'}).text('Save'))) .after($(document.createElement('td'))) .unwrap(); From 9250a5a3f6142908292333800a84ed9fbd603787 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Fri, 3 Jun 2011 15:24:38 +0200 Subject: [PATCH 11/36] [IMP] re-render record to readonly after saving it bzr revid: xmo@openerp.com-20110603132438-uhkzql3lsy6h9ptn --- addons/base/static/src/js/list-editable.js | 21 +++++-- addons/base/static/src/js/list.js | 66 ++++++++++++++++++---- 2 files changed, 71 insertions(+), 16 deletions(-) diff --git a/addons/base/static/src/js/list-editable.js b/addons/base/static/src/js/list-editable.js index d005d58022d..0ece61d1f99 100644 --- a/addons/base/static/src/js/list-editable.js +++ b/addons/base/static/src/js/list-editable.js @@ -87,11 +87,22 @@ openerp.base.list.editable = function (openerp) { save_row: function (row_num, edit_next) { var self = this; this.edition_form.do_save(function () { - // nuke form - // re-render current row - if (edit_next) { - // self.row_clicked({currentTarget: current_row.nextSibling}, row_num + 1) - } + self.dataset.read_index( + _.filter(_.pluck(self.columns, 'name'), _.identity), + function (record) { + var form_record = self.transform_record(record); + self.rows.splice(row_num, 1, form_record); + self.reload_record(row_num); + self.edition_form.stop(); + delete self.edition_form; + if (edit_next && self.rows.length > row_num + 1) { + self.dataset.index += 1; + self.row_clicked({ + currentTarget: self.$current.children().eq(row_num + 1) + }, row_num + 1); + } + } + ); }); } }); diff --git a/addons/base/static/src/js/list.js b/addons/base/static/src/js/list.js index cd4bcde8008..03c8eba97fb 100644 --- a/addons/base/static/src/js/list.js +++ b/addons/base/static/src/js/list.js @@ -639,6 +639,59 @@ openerp.base.ListView.List = Class.extend( /** @lends openerp.base.ListView.List }); return record; }); + }, + /** + * Transforms a record from what is returned by a dataset read (a simple + * mapping of ``$fieldname: $value``) to the format expected by list rows + * and form views: + * + * data: { + * $fieldname: { + * value: $value + * } + * } + * + * This format allows for the insertion of a bunch of metadata (names, + * colors, etc...) + * + * @param {Object} record original record, in dataset format + * @returns {Object} record displayable in a form or list view + */ + transform_record: function (record) { + // TODO: colors handling + var form_data = {}, + form_record = {data: form_data}; + + _(record).each(function (value, key) { + form_data[key] = {value: value}; + }); + + return form_record; + }, + /** + * Reloads the record at index ``row_index`` in the list's rows. + * + * This does not re-fetch the record, it only re-renders it, replacing the + * current rendering. + * + * @param {Number} record_index index of the record to reload + */ + reload_record: function (record_index) { + this.$current.children().eq(record_index) + .replaceWith(this.render_record(record_index)); + }, + /** + * Renders a list record to HTML + * + * @param {Number} record_index index of the record to render in ``this.rows`` + * @returns {String} QWeb rendering of the selected record + */ + render_record: function (record_index) { + return QWeb.render('ListView.row', { + columns: this.columns, + options: this.options, + row: this.rows[record_index] + }); } // drag and drop }); @@ -834,17 +887,8 @@ openerp.base.ListView.Groups = Class.extend( /** @lends openerp.base.ListView.Gr _.filter(_.pluck(this.columns, 'name'), _.identity), 0, false, function (records) { - var form_records = _(records).map(function (record) { - // TODO: colors handling - var form_data = {}, - form_record = {data: form_data}; - - _(record).each(function (value, key) { - form_data[key] = {value: value}; - }); - - return form_record; - }); + var form_records = _(records).map( + $.proxy(list, 'transform_record')); rows.splice(0, rows.length); rows.push.apply(rows, form_records); From aaa459a29daf862fcb6343786bd576ec74798dd0 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Fri, 3 Jun 2011 16:16:38 +0200 Subject: [PATCH 12/36] [ADD] edition cancelling bzr revid: xmo@openerp.com-20110603141638-giwlzwrp795bq78h --- addons/base/static/src/js/list-editable.js | 32 ++++++++++++++++++---- addons/base/static/src/js/list.js | 3 +- addons/base/static/src/xml/base.xml | 6 ++-- 3 files changed, 33 insertions(+), 8 deletions(-) diff --git a/addons/base/static/src/js/list-editable.js b/addons/base/static/src/js/list-editable.js index 0ece61d1f99..ee2af7d258b 100644 --- a/addons/base/static/src/js/list-editable.js +++ b/addons/base/static/src/js/list-editable.js @@ -2,6 +2,9 @@ * @namespace handles editability case for lists, because it depends on form and forms already depends on lists it had to be split out */ openerp.base.list.editable = function (openerp) { + var KEY_RETURN = 13, + KEY_ESCAPE = 27; + // editability status of list rows openerp.base.ListView.prototype.defaults.editable = null; @@ -47,13 +50,22 @@ openerp.base.list.editable = function (openerp) { onclick: function (e) {e.stopPropagation();} }).replaceAll(row) .keyup(function (e) { - if (e.which !== 13) { - return; + switch (e.which) { + case KEY_RETURN: + self.save_row(row_num, true); + break; + case KEY_ESCAPE: + self.cancel_edition(row_num); + break; + default: + return; } - self.save_row(row_num, true); }) - .delegate('button.oe-list-save', 'click', function () { + .delegate('button.oe-edit-row-save', 'click', function () { self.save_row(row_num); + }) + .delegate('button.oe-edit-row-cancel', 'click', function () { + self.cancel_edition(row_num); }); this.edition_form = _.extend(new openerp.base.FormView( null, this.group.view.session, $new_row.attr('id'), @@ -96,7 +108,7 @@ openerp.base.list.editable = function (openerp) { self.edition_form.stop(); delete self.edition_form; if (edit_next && self.rows.length > row_num + 1) { - self.dataset.index += 1; + self.dataset.index++; self.row_clicked({ currentTarget: self.$current.children().eq(row_num + 1) }, row_num + 1); @@ -104,6 +116,16 @@ openerp.base.list.editable = function (openerp) { } ); }); + }, + /** + * Cancels the edition of the row at index ``row_num``. + * + * @param {Number} row_num index of the row being edited + */ + cancel_edition: function (row_num) { + this.reload_record(row_num); + this.edition_form.stop(); + delete this.edition_form; } }); openerp.base.list = {form: {}}; diff --git a/addons/base/static/src/js/list.js b/addons/base/static/src/js/list.js index 03c8eba97fb..c627330fd70 100644 --- a/addons/base/static/src/js/list.js +++ b/addons/base/static/src/js/list.js @@ -690,7 +690,8 @@ openerp.base.ListView.List = Class.extend( /** @lends openerp.base.ListView.List return QWeb.render('ListView.row', { columns: this.columns, options: this.options, - row: this.rows[record_index] + row: this.rows[record_index], + row_parity: (record_index % 2 === 0) ? 'even' : 'odd' }); } // drag and drop diff --git a/addons/base/static/src/xml/base.xml b/addons/base/static/src/xml/base.xml index d1633bdb150..f1c1d3df4cf 100644 --- a/addons/base/static/src/xml/base.xml +++ b/addons/base/static/src/xml/base.xml @@ -802,8 +802,10 @@ .replaceAll(this) .before($(document.createElement('td')).append( $(document.createElement('button')).attr({ - 'class': 'oe-list-save', 'type': 'button'}).text('Save'))) - .after($(document.createElement('td'))) + 'class': 'oe-edit-row-save', 'type': 'button'}).text('Save'))) + .after($(document.createElement('td')).append( + $(document.createElement('button')).attr({ + 'class': 'oe-edit-row-cancel', 'type': 'button'}).text('Cancel'))) .unwrap(); From 7ba2688e39c9c929a1306353764c38f7b0c343b0 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Fri, 3 Jun 2011 16:37:23 +0200 Subject: [PATCH 13/36] [IMP] reload of rows after action: only reload the row, not the whole tree Also improve DataSet slightly: have read_index and read_ids return their RPC promises bzr revid: xmo@openerp.com-20110603143723-go25vripams72bqb --- addons/base/static/src/js/data.js | 6 +-- addons/base/static/src/js/list-editable.js | 24 ++++------ addons/base/static/src/js/list.js | 52 +++++++++++++--------- 3 files changed, 42 insertions(+), 40 deletions(-) diff --git a/addons/base/static/src/js/data.js b/addons/base/static/src/js/data.js index 7582e99b662..b7739a3da27 100644 --- a/addons/base/static/src/js/data.js +++ b/addons/base/static/src/js/data.js @@ -259,7 +259,7 @@ openerp.base.DataSet = openerp.base.Controller.extend( /** @lends openerp.base. */ read_ids: function (ids, fields, callback) { var self = this; - this.rpc('/base/dataset/get', { + return this.rpc('/base/dataset/get', { model: this.model, ids: ids, fields: fields @@ -279,10 +279,10 @@ openerp.base.DataSet = openerp.base.Controller.extend( /** @lends openerp.base. */ read_index: function (fields, callback) { if (_.isEmpty(this.ids)) { - callback([]); + return $.Deferred().reject().promise(); } else { fields = fields || false; - this.read_ids([this.ids[this.index]], fields, function(records) { + return this.read_ids([this.ids[this.index]], fields, function(records) { callback(records[0]); }); } diff --git a/addons/base/static/src/js/list-editable.js b/addons/base/static/src/js/list-editable.js index ee2af7d258b..d51fa695de9 100644 --- a/addons/base/static/src/js/list-editable.js +++ b/addons/base/static/src/js/list-editable.js @@ -99,22 +99,16 @@ openerp.base.list.editable = function (openerp) { save_row: function (row_num, edit_next) { var self = this; this.edition_form.do_save(function () { - self.dataset.read_index( - _.filter(_.pluck(self.columns, 'name'), _.identity), - function (record) { - var form_record = self.transform_record(record); - self.rows.splice(row_num, 1, form_record); - self.reload_record(row_num); - self.edition_form.stop(); - delete self.edition_form; - if (edit_next && self.rows.length > row_num + 1) { - self.dataset.index++; - self.row_clicked({ - currentTarget: self.$current.children().eq(row_num + 1) - }, row_num + 1); - } + self.reload_record(row_num, true).then(function () { + self.edition_form.stop(); + delete self.edition_form; + if (edit_next && self.rows.length > row_num + 1) { + self.dataset.index++; + self.row_clicked({ + currentTarget: self.$current.children().eq(row_num + 1) + }, row_num + 1); } - ); + }); }); }, /** diff --git a/addons/base/static/src/js/list.js b/addons/base/static/src/js/list.js index c627330fd70..8d19635968a 100644 --- a/addons/base/static/src/js/list.js +++ b/addons/base/static/src/js/list.js @@ -545,9 +545,13 @@ openerp.base.ListView.List = Class.extend( /** @lends openerp.base.ListView.List e.stopPropagation(); var $target = $(e.currentTarget), field = $target.closest('td').data('field'), - record_id = self.row_id($target.closest('tr')); + $row = $target.closest('tr'), + record_id = self.row_id($row), + index = self.row_position($row); - $(self).trigger('action', [field, record_id]); + $(self).trigger('action', [field, record_id, function () { + self.reload_record(index, true); + }]); }) .delegate('tr', 'click', function (e) { e.stopPropagation(); @@ -671,14 +675,32 @@ openerp.base.ListView.List = Class.extend( /** @lends openerp.base.ListView.List /** * Reloads the record at index ``row_index`` in the list's rows. * - * This does not re-fetch the record, it only re-renders it, replacing the - * current rendering. + * By default, simply re-renders the record. If the ``fetch`` parameter is + * provided and ``true``, will first fetch the record anew. * * @param {Number} record_index index of the record to reload + * @param {Boolean} fetch fetches the record from remote before reloading it */ - reload_record: function (record_index) { - this.$current.children().eq(record_index) - .replaceWith(this.render_record(record_index)); + reload_record: function (record_index, fetch) { + var self = this; + var read_p = null; + if (fetch) { + // save index to restore it later, if already set + var old_index = this.dataset.index; + this.dataset.index = record_index; + read_p = this.dataset.read_index( + _.filter(_.pluck(this.columns, 'name'), _.identity), + function (record) { + var form_record = self.transform_record(record); + self.rows.splice(record_index, 1, form_record); + self.dataset.index = old_index; + } + ) + } + + return $.when(read_p).then(function () { + self.$current.children().eq(record_index) + .replaceWith(self.render_record(record_index)); }) }, /** * Renders a list record to HTML @@ -848,21 +870,7 @@ openerp.base.ListView.Groups = Class.extend( /** @lends openerp.base.ListView.Gr // can have selections spanning multiple links var selection = self.get_selection(); $this.trigger(e, [selection.ids, selection.records]); - }).bind('action', function (e, name, id, callback) { - if (!callback) { - callback = function () { - var $prev = child.$current.prev(); - if (!$prev.is('tbody')) { - // ungrouped - $(self.elements[0]).replaceWith(self.render()); - } else { - // ghetto reload child (and its siblings) - $prev.children().last().click(); - } - }; - } - $this.trigger(e, [name, id, callback]); - }).bind('deleted row_link', function (e) { + }).bind('action deleted row_link', function (e) { // additional positional parameters are provided to trigger as an // Array, following the event type or event object, but are // provided to the .bind event handler as *args. From 8d35fb9a7357c8f555fc8942ee11073be8c047ed Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Mon, 6 Jun 2011 10:46:22 +0200 Subject: [PATCH 14/36] [FIX] send current search context to listview load (fields_view_get) so @invisible can be computed correctly bzr revid: xmo@openerp.com-20110606084622-iwq5tn3lpegabibe --- addons/base/static/src/js/list.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/addons/base/static/src/js/list.js b/addons/base/static/src/js/list.js index 8d19635968a..7bcfc26c61b 100644 --- a/addons/base/static/src/js/list.js +++ b/addons/base/static/src/js/list.js @@ -257,7 +257,8 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi return this.rpc('/base/listview/load', { model: this.model, view_id: this.view_id, - toolbar: !!this.flags.sidebar + toolbar: !!this.flags.sidebar, + context: this.dataset.context }, function (field_view_get) { self.on_loaded(field_view_get, grouped); }); From 69132745e0e7232666c5ee62cb3ade51d4260c36 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Mon, 6 Jun 2011 11:52:54 +0200 Subject: [PATCH 15/36] [FIX] remove fields_view_get preprocessing of set_editable/@editable, done in JS bzr revid: xmo@openerp.com-20110606095254-h7r8v42y8q42sjj4 --- addons/base/controllers/main.py | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/addons/base/controllers/main.py b/addons/base/controllers/main.py index 70feda44bc0..dcb5ae9e4bd 100644 --- a/addons/base/controllers/main.py +++ b/addons/base/controllers/main.py @@ -571,20 +571,6 @@ class ListView(View): fields_view = self.fields_view_get(req, model, view_id, 'tree', toolbar=toolbar) return {'fields_view': fields_view} - def fields_view_get(self, request, model, view_id, view_type="tree", - transform=True, toolbar=False, submenu=False): - """ Sets @editable on the view's arch if it isn't already set and - ``set_editable`` is present in the request context - """ - view = super(ListView, self).fields_view_get( - request, model, view_id, view_type, transform, toolbar, submenu) - - view_attributes = view['arch']['attrs'] - if request.context.get('set_editable')\ - and 'editable' not in view_attributes: - view_attributes['editable'] = 'bottom' - return view - def process_colors(self, view, row, context): colors = view['arch']['attrs'].get('colors') From efbde798bc2fbfe4ab659c08801551e7bece7c1a Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Mon, 6 Jun 2011 11:59:18 +0200 Subject: [PATCH 16/36] [FIX] handling of editable list views via @editable, add basis for adding records in editable list mode bzr revid: xmo@openerp.com-20110606095918-9kcm3x1m8bncl092 --- addons/base/static/src/js/list-editable.js | 42 ++++++++++++++++++++-- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/addons/base/static/src/js/list-editable.js b/addons/base/static/src/js/list-editable.js index d51fa695de9..c0a9d846496 100644 --- a/addons/base/static/src/js/list-editable.js +++ b/addons/base/static/src/js/list-editable.js @@ -9,6 +9,8 @@ openerp.base.list.editable = function (openerp) { openerp.base.ListView.prototype.defaults.editable = null; var old_actual_search = openerp.base.ListView.prototype.do_actual_search; + var old_add_record = openerp.base.ListView.prototype.do_add_record; + var old_on_loaded = openerp.base.ListView.prototype.on_loaded; _.extend(openerp.base.ListView.prototype, { /** * Sets editability status for the list, based on defaults, view @@ -18,11 +20,11 @@ openerp.base.list.editable = function (openerp) { */ set_editable: function (force) { // If ``force``, set editability to bottom - // else if editability flag in view arch, use that // otherwise rely on view default + // view' @editable is handled separately as we have not yet + // fetched and processed the view at this point. this.options.editable = ( (force && "bottom") - || this.fields_view.arch.attrs.editable || this.defaults.editable); }, /** @@ -31,6 +33,29 @@ openerp.base.list.editable = function (openerp) { do_actual_search: function (results) { this.set_editable(results.context['set_editable']); old_actual_search.call(this, results); + }, + /** + * Replace do_add_record to handle editability (and adding new record + * as an editable row at the top or bottom of the list) + */ + do_add_record: function () { + if (this.options.editable) { + this.groups.new_record(); + } else { + old_add_record.call(this); + } + }, + on_loaded: function (data, grouped) { + // tree/@editable takes priority on everything else if present. + this.options.editable = data.fields_view.arch.editable || this.options.editable; + return old_on_loaded.call(this, data, grouped); + } + }); + + _.extend(openerp.base.ListView.Groups.prototype, { + new_record: function () { + // TODO: handle multiple children + this.children[null].new_record(); } }); @@ -48,7 +73,7 @@ openerp.base.list.editable = function (openerp) { id: _.uniqueId('oe-editable-row-'), 'class': $(row).attr('class'), onclick: function (e) {e.stopPropagation();} - }).replaceAll(row) + }) .keyup(function (e) { switch (e.which) { case KEY_RETURN: @@ -67,6 +92,13 @@ openerp.base.list.editable = function (openerp) { .delegate('button.oe-edit-row-cancel', 'click', function () { self.cancel_edition(row_num); }); + if (row) { + $new_row.replaceAll(row); + } else if (this.options.editable === 'top') { + this.$current.prepend($new_row); + } else if (this.options.editable) { + this.$current.append($new_row); + } this.edition_form = _.extend(new openerp.base.FormView( null, this.group.view.session, $new_row.attr('id'), this.dataset, false), { @@ -120,6 +152,10 @@ openerp.base.list.editable = function (openerp) { this.reload_record(row_num); this.edition_form.stop(); delete this.edition_form; + }, + new_record: function () { + this.dataset.index = null; + this.render_row_as_form(-1, null); } }); openerp.base.list = {form: {}}; From 0c673bb2483fd3d25efdd78b440caf490c7d5bc6 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Mon, 6 Jun 2011 13:42:04 +0200 Subject: [PATCH 17/36] [IMP] make edition indexing work based on the current dataset bzr revid: xmo@openerp.com-20110606114204-sjnil5xuhef0l2la --- addons/base/static/src/js/list-editable.js | 43 +++++++++++----------- addons/base/static/src/js/list.js | 14 +++---- 2 files changed, 28 insertions(+), 29 deletions(-) diff --git a/addons/base/static/src/js/list-editable.js b/addons/base/static/src/js/list-editable.js index c0a9d846496..44b3d68398d 100644 --- a/addons/base/static/src/js/list-editable.js +++ b/addons/base/static/src/js/list-editable.js @@ -61,13 +61,13 @@ openerp.base.list.editable = function (openerp) { var old_list_row_clicked = openerp.base.ListView.List.prototype.row_clicked; _.extend(openerp.base.ListView.List.prototype, { - row_clicked: function (event, index) { + row_clicked: function (event) { if (!this.options.editable) { - return old_list_row_clicked.call(this, event, index); + return old_list_row_clicked.call(this, event); } - this.render_row_as_form(index, event.currentTarget); + this.render_row_as_form(event.currentTarget); }, - render_row_as_form: function (row_num, row) { + render_row_as_form: function (row) { var self = this; var $new_row = $('', { id: _.uniqueId('oe-editable-row-'), @@ -77,20 +77,20 @@ openerp.base.list.editable = function (openerp) { .keyup(function (e) { switch (e.which) { case KEY_RETURN: - self.save_row(row_num, true); + self.save_row(true); break; case KEY_ESCAPE: - self.cancel_edition(row_num); + self.cancel_edition(); break; default: return; } }) .delegate('button.oe-edit-row-save', 'click', function () { - self.save_row(row_num); + self.save_row(); }) .delegate('button.oe-edit-row-cancel', 'click', function () { - self.cancel_edition(row_num); + self.cancel_edition(); }); if (row) { $new_row.replaceAll(row); @@ -125,37 +125,38 @@ openerp.base.list.editable = function (openerp) { * Saves the current row, and triggers the edition of its following * sibling if asked. * - * @param {Number} row_num the row to save * @param {Boolean} [edit_next=false] should the next row become editable */ - save_row: function (row_num, edit_next) { + save_row: function (edit_next) { var self = this; this.edition_form.do_save(function () { - self.reload_record(row_num, true).then(function () { + self.reload_record(self.dataset.index, true).then(function () { self.edition_form.stop(); delete self.edition_form; - if (edit_next && self.rows.length > row_num + 1) { - self.dataset.index++; + if (edit_next) { + self.dataset.next(); self.row_clicked({ - currentTarget: self.$current.children().eq(row_num + 1) - }, row_num + 1); + currentTarget: self.$current.children().eq( + self.dataset.index) + }); } }); }); }, /** - * Cancels the edition of the row at index ``row_num``. - * - * @param {Number} row_num index of the row being edited + * Cancels the edition of the row for the current dataset index */ - cancel_edition: function (row_num) { - this.reload_record(row_num); + cancel_edition: function () { + if (this.dataset.index !== null) { + this.reload_record(this.dataset.index); + } this.edition_form.stop(); + this.edition_form.$element.remove(); delete this.edition_form; }, new_record: function () { this.dataset.index = null; - this.render_row_as_form(-1, null); + this.render_row_as_form(); } }); openerp.base.list = {form: {}}; diff --git a/addons/base/static/src/js/list.js b/addons/base/static/src/js/list.js index 6081c8c0eb8..678b7f9f5a7 100644 --- a/addons/base/static/src/js/list.js +++ b/addons/base/static/src/js/list.js @@ -83,8 +83,8 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi 'action': function (e, action_name, id, callback) { self.do_action(action_name, id, callback); }, - 'row_link': function (e, index, id, dataset) { - self.do_activate_record(index, id, dataset); + 'row_link': function (e, id, dataset) { + self.do_activate_record(dataset.index, id, dataset); } }); }, @@ -565,16 +565,14 @@ openerp.base.ListView.List = Class.extend( /** @lends openerp.base.ListView.List }) .delegate('tr', 'click', function (e) { e.stopPropagation(); - var index = self.row_position(e.currentTarget); - self.dataset.index = index; - self.row_clicked(e, index); + self.dataset.index = self.row_position(e.currentTarget); + self.row_clicked(e); }); }, - row_clicked: function (event, index) { + row_clicked: function () { $(this).trigger( 'row_link', - [index, - this.rows[index].data.id.value, + [this.rows[this.dataset.index].data.id.value, this.dataset]); }, render: function () { From f9a4afe8892e3783e278dd4691af46b7b4c9f773 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Mon, 6 Jun 2011 14:32:29 +0200 Subject: [PATCH 18/36] [FIX] editable is an attribute on tree view bzr revid: xmo@openerp.com-20110606123229-2agzw9djd99ojczi --- addons/base/static/src/js/list-editable.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/base/static/src/js/list-editable.js b/addons/base/static/src/js/list-editable.js index 44b3d68398d..6f862c52e2c 100644 --- a/addons/base/static/src/js/list-editable.js +++ b/addons/base/static/src/js/list-editable.js @@ -47,7 +47,7 @@ openerp.base.list.editable = function (openerp) { }, on_loaded: function (data, grouped) { // tree/@editable takes priority on everything else if present. - this.options.editable = data.fields_view.arch.editable || this.options.editable; + this.options.editable = data.fields_view.arch.attrs.editable || this.options.editable; return old_on_loaded.call(this, data, grouped); } }); From 8ec4ee1ad21df771a0a0eaaf57b9af85b31d6019 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Mon, 6 Jun 2011 15:11:08 +0200 Subject: [PATCH 19/36] [IMP] Add flags to prepend records on save, and signal whether the record was updated or created in save callback bzr revid: xmo@openerp.com-20110606131108-3aoi1326xj88cwtt --- addons/base/static/src/js/form.js | 40 +++++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/addons/base/static/src/js/form.js b/addons/base/static/src/js/form.js index b2d3e60b321..24ae0983f79 100644 --- a/addons/base/static/src/js/form.js +++ b/addons/base/static/src/js/form.js @@ -226,7 +226,15 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormV self.on_record_loaded(result.result); }); }, - do_save: function(success) { + /** + * Triggers saving the form's record. Chooses between creating a new + * record or saving an existing one depending on whether the record + * already has an id property. + * + * @param {Function} success callback on save success + * @param {Boolean} [prepend_on_create=false] if ``do_save`` creates a new record, should that record be inserted at the start of the dataset (by default, records are added at the end) + */ + do_save: function(success, prepend_on_create) { var self = this; if (!this.ready) { return false; @@ -249,7 +257,7 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormV this.log("About to save", values); if (!this.datarecord.id) { this.dataset.create(values, function(r) { - self.on_created(r, success); + self.on_created(r, success, prepend_on_create); }); } else { this.dataset.write(this.datarecord.id, values, function(r) { @@ -287,19 +295,37 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormV } } }, - on_created: function(r, success) { + /** + * Updates the form' dataset to contain the new record: + * + * * Adds the newly created record to the current dataset (at the end by + * default) + * * Selects that record (sets the dataset's index to point to the new + * record's id). + * * Updates the pager and sidebar displays + * + * @param {Object} r + * @param {Function} success callback to execute after having updated the dataset + * @param {Boolean} [prepend_on_create=false] adds the newly created record at the beginning of the dataset instead of the end + */ + on_created: function(r, success, prepend_on_create) { if (!r.result) { this.notification.warn("Record not created", "Problem while creating record."); } else { - this.datarecord.id = arguments[0].result; - this.dataset.ids.push(this.datarecord.id); - this.dataset.index = this.dataset.ids.length - 1; + this.datarecord.id = r.result; + if (!prepend_on_create) { + this.dataset.ids.push(this.datarecord.id); + this.dataset.index = this.dataset.ids.length - 1; + } else { + this.dataset.ids.unshift(this.datarecord.id); + this.dataset.index = 0; + } this.dataset.count++; this.do_update_pager(); this.do_update_sidebar(); this.notification.notify("Record created", "The record has been created with id #" + this.datarecord.id); if (success) { - success(r); + success(_.extend(r, {created: true})); } } }, From 587131e3ee418e0463ca34b9209832171fd98837 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Mon, 6 Jun 2011 15:16:30 +0200 Subject: [PATCH 20/36] [FIX] saving of newly created records * Refresh correct record in @editable=top * Follow up with new record after creation (instead of editing semi-random one) bzr revid: xmo@openerp.com-20110606131630-vq2bwtrozm8jybpo --- addons/base/static/src/js/list-editable.js | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/addons/base/static/src/js/list-editable.js b/addons/base/static/src/js/list-editable.js index 6f862c52e2c..ae9d9c400b3 100644 --- a/addons/base/static/src/js/list-editable.js +++ b/addons/base/static/src/js/list-editable.js @@ -129,19 +129,23 @@ openerp.base.list.editable = function (openerp) { */ save_row: function (edit_next) { var self = this; - this.edition_form.do_save(function () { + this.edition_form.do_save(function (result) { self.reload_record(self.dataset.index, true).then(function () { self.edition_form.stop(); delete self.edition_form; - if (edit_next) { - self.dataset.next(); - self.row_clicked({ - currentTarget: self.$current.children().eq( - self.dataset.index) - }); + if (!edit_next) { + return; } + if (result.created) { + self.new_record(); + return; + } + self.dataset.next(); + self.render_row_as_form( + self.$current.children().eq( + self.dataset.index)); }); - }); + }, this.options.editable === 'top'); }, /** * Cancels the edition of the row for the current dataset index From ea92194cc6c105722c1cf93612b2e10d4ca94047 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Mon, 6 Jun 2011 15:43:36 +0200 Subject: [PATCH 21/36] =?UTF-8?q?[FIX]=20ensure=20only=20one=20row=20at=20?= =?UTF-8?q?a=20time=20is=20edited,=20integrate=20dataset=20index=20in=20ro?= =?UTF-8?q?w=20markup=20to=20create=20a=20more=20solid=20mapping=20between?= =?UTF-8?q?=20records=20and=20table=20rows=20in=20ListView.List=20(than=20?= =?UTF-8?q?checking=20the=20number=20of=20ancestors,=20which=20=E2=80=94?= =?UTF-8?q?=20it=20turns=20out=20=E2=80=94=20is=20a=20bit=20brittle=20when?= =?UTF-8?q?=20adding=20new=20rows=20at=20the=20front=20of=20the=20table?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit bzr revid: xmo@openerp.com-20110606134336-mmk00344x7gicd0z --- addons/base/static/src/js/list-editable.js | 44 +++++++++++++++++----- addons/base/static/src/js/list.js | 7 ++-- addons/base/static/src/xml/base.xml | 3 +- 3 files changed, 40 insertions(+), 14 deletions(-) diff --git a/addons/base/static/src/js/list-editable.js b/addons/base/static/src/js/list-editable.js index ae9d9c400b3..c500b307d27 100644 --- a/addons/base/static/src/js/list-editable.js +++ b/addons/base/static/src/js/list-editable.js @@ -65,9 +65,28 @@ openerp.base.list.editable = function (openerp) { if (!this.options.editable) { return old_list_row_clicked.call(this, event); } - this.render_row_as_form(event.currentTarget); + this.edit_record(); + }, + /** + * Checks if a record is being edited, and if so cancels it + */ + cancel_pending_edition: function () { + if (!this.edition) { + return; + } + + if (this.edition_index !== null) { + this.reload_record(this.edition_index); + } + this.edition_form.stop(); + this.edition_form.$element.remove(); + delete this.edition_form; + delete this.edition_index; + delete this.edition; }, render_row_as_form: function (row) { + this.cancel_pending_edition(); + var self = this; var $new_row = $('', { id: _.uniqueId('oe-editable-row-'), @@ -99,6 +118,8 @@ openerp.base.list.editable = function (openerp) { } else if (this.options.editable) { this.$current.append($new_row); } + this.edition = true; + this.edition_index = this.dataset.index; this.edition_form = _.extend(new openerp.base.FormView( null, this.group.view.session, $new_row.attr('id'), this.dataset, false), { @@ -133,6 +154,8 @@ openerp.base.list.editable = function (openerp) { self.reload_record(self.dataset.index, true).then(function () { self.edition_form.stop(); delete self.edition_form; + delete self.edition_index; + delete self.edition; if (!edit_next) { return; } @@ -141,9 +164,6 @@ openerp.base.list.editable = function (openerp) { return; } self.dataset.next(); - self.render_row_as_form( - self.$current.children().eq( - self.dataset.index)); }); }, this.options.editable === 'top'); }, @@ -151,12 +171,16 @@ openerp.base.list.editable = function (openerp) { * Cancels the edition of the row for the current dataset index */ cancel_edition: function () { - if (this.dataset.index !== null) { - this.reload_record(this.dataset.index); - } - this.edition_form.stop(); - this.edition_form.$element.remove(); - delete this.edition_form; + this.cancel_pending_edition(); + }, + /** + * Edits record currently selected via dataset + */ + edit_record: function () { + this.render_row_as_form( + this.$current.children( + _.sprintf('[data-index=%d]', + this.dataset.index))); }, new_record: function () { this.dataset.index = null; diff --git a/addons/base/static/src/js/list.js b/addons/base/static/src/js/list.js index 678b7f9f5a7..30a83eaa8bd 100644 --- a/addons/base/static/src/js/list.js +++ b/addons/base/static/src/js/list.js @@ -607,7 +607,7 @@ openerp.base.ListView.List = Class.extend( /** @lends openerp.base.ListView.List this.$current.find('th.oe-record-selector input:checked') .closest('tr').each(function () { var record = {}; - _(rows[$(this).prevAll().length].data).each(function (obj, key) { + _(rows[$(this).data('index')].data).each(function (obj, key) { record[key] = obj.value; }); result.ids.push(record.id); @@ -622,7 +622,7 @@ openerp.base.ListView.List = Class.extend( /** @lends openerp.base.ListView.List * @returns {Number} the position of the row in this.rows */ row_position: function (row) { - return $(row).prevAll().length; + return $(row).data('index'); }, /** * Returns the identifier of the object displayed in the provided table @@ -721,7 +721,8 @@ openerp.base.ListView.List = Class.extend( /** @lends openerp.base.ListView.List columns: this.columns, options: this.options, row: this.rows[record_index], - row_parity: (record_index % 2 === 0) ? 'even' : 'odd' + row_parity: (record_index % 2 === 0) ? 'even' : 'odd', + row_index: record_index }); } // drag and drop diff --git a/addons/base/static/src/xml/base.xml b/addons/base/static/src/xml/base.xml index 03b33c117da..fe502231f17 100644 --- a/addons/base/static/src/xml/base.xml +++ b/addons/base/static/src/xml/base.xml @@ -288,7 +288,8 @@ - + - + - +
From 26bd0653c42327ffe764d7797b4aefbd6d189ab6 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Mon, 6 Jun 2011 16:01:54 +0200 Subject: [PATCH 22/36] [IMP] add empty padding columns to editable rows when group_by active bzr revid: xmo@openerp.com-20110606140154-39f2ivk5csfkc5x9 --- addons/base/static/src/js/list-editable.js | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/addons/base/static/src/js/list-editable.js b/addons/base/static/src/js/list-editable.js index c500b307d27..c0ad2e7379d 100644 --- a/addons/base/static/src/js/list-editable.js +++ b/addons/base/static/src/js/list-editable.js @@ -131,13 +131,18 @@ openerp.base.list.editable = function (openerp) { position: 'last', unique: true, callback: function () { - self.edition_form.$element - .find('td') - .addClass('oe-field-cell') - .removeAttr('width') - .end() - .find('td:first').removeClass('oe-field-cell').end() - .find('td:last').removeClass('oe-field-cell').end(); + var $e = self.edition_form.$element; + $e.find('td') + .addClass('oe-field-cell') + .removeAttr('width') + .end() + .find('td:first').removeClass('oe-field-cell').end() + .find('td:last').removeClass('oe-field-cell').end(); + _(self.columns).each(function (column) { + if (column.meta) { + $e.prepend(''); + } + }); } }); this.edition_form.do_show(); From f0bce0a0456b7a0bc1174a05b5d1abd055e55553 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Mon, 6 Jun 2011 16:13:09 +0200 Subject: [PATCH 23/36] [FIX] small warnings bzr revid: xmo@openerp.com-20110606141309-fum7jw44nqljdk38 --- addons/base/static/src/js/views.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/addons/base/static/src/js/views.js b/addons/base/static/src/js/views.js index 4c259babd8f..ecd16d77d94 100644 --- a/addons/base/static/src/js/views.js +++ b/addons/base/static/src/js/views.js @@ -9,7 +9,7 @@ openerp.base.ActionManager = openerp.base.Controller.extend({ init: function(session, element_id) { this._super(session, element_id); this.viewmanager = null; - this.dialog_stack = [] + this.dialog_stack = []; // Temporary linking view_manager to session. // Will use controller_parent to find it when implementation will be done. session.action_manager = this; @@ -33,8 +33,7 @@ openerp.base.ActionManager = openerp.base.Controller.extend({ case 'ir.actions.act_window': if (action.target == 'new') { var element_id = _.uniqueId("act_window_dialog"); - var dialog = $('
'); - dialog.dialog({ + $('
', {id: element_id}).dialog({ title: action.name, modal: true, width: '50%', @@ -347,7 +346,7 @@ openerp.base.Sidebar = openerp.base.BaseWidget.extend({ var action = self.sections[index[0]].elements[index[1]]; action.flags = { new_window : true - } + }; self.session.action_manager.do_action(action); e.stopPropagation(); e.preventDefault(); From 3d5af3fa7308b13209d5ad94dcc74d21df99c599 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Tue, 7 Jun 2011 10:57:17 +0200 Subject: [PATCH 24/36] [IMP] hide [new] button when editable list grouped: we don't know where to put the new record, so let's remove the ability to create one as long as it's not a requested feature bzr revid: xmo@openerp.com-20110607085717-qstun0iymupoyw35 --- addons/base/static/src/js/list.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/addons/base/static/src/js/list.js b/addons/base/static/src/js/list.js index 30a83eaa8bd..550221847b2 100644 --- a/addons/base/static/src/js/list.js +++ b/addons/base/static/src/js/list.js @@ -134,7 +134,9 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi this.$element.html(QWeb.render("ListView", this)); // Head hook - this.$element.find('#oe-list-add').click(this.do_add_record); + this.$element.find('#oe-list-add') + .click(this.do_add_record) + .toggle(!(grouped && this.options.editable)); this.$element.find('#oe-list-delete') .hide() .click(this.do_delete_selected); From 8a7a64bcd1e8e96bb7b1604d2f74e3145315246e Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Tue, 7 Jun 2011 12:30:24 +0200 Subject: [PATCH 25/36] [IMP] make events passed through from children (lists and groups) to self by ListView.Groups pluggable bzr revid: xmo@openerp.com-20110607103024-6m5b1g52l6cfrfi6 --- addons/base/static/src/js/list.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/addons/base/static/src/js/list.js b/addons/base/static/src/js/list.js index 550221847b2..41ff6e9ec14 100644 --- a/addons/base/static/src/js/list.js +++ b/addons/base/static/src/js/list.js @@ -730,6 +730,7 @@ openerp.base.ListView.List = Class.extend( /** @lends openerp.base.ListView.List // drag and drop }); openerp.base.ListView.Groups = Class.extend( /** @lends openerp.base.ListView.Groups# */{ + passtrough_events: 'action deleted row_link', /** * Grouped display for the ListView. Handles basic DOM events and interacts * with the :js:class:`~openerp.base.DataGroup` bound to it. @@ -881,7 +882,7 @@ openerp.base.ListView.Groups = Class.extend( /** @lends openerp.base.ListView.Gr // can have selections spanning multiple links var selection = self.get_selection(); $this.trigger(e, [selection.ids, selection.records]); - }).bind('action deleted row_link', function (e) { + }).bind(this.passtrough_events, function (e) { // additional positional parameters are provided to trigger as an // Array, following the event type or event object, but are // provided to the .bind event handler as *args. From 08f6d5d9eecc2e82cb46d591f7dd992a2d66eba6 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Tue, 7 Jun 2011 12:55:57 +0200 Subject: [PATCH 26/36] [IMP] switching to form view selecting the correct record while in edition mode with a record being edited bzr revid: xmo@openerp.com-20110607105557-izu1cm5udiqkktsg --- addons/base/static/src/js/list-editable.js | 42 ++++++++++++++++++---- addons/base/static/src/js/list.js | 2 +- 2 files changed, 37 insertions(+), 7 deletions(-) diff --git a/addons/base/static/src/js/list-editable.js b/addons/base/static/src/js/list-editable.js index c0ad2e7379d..ec9f21f61b7 100644 --- a/addons/base/static/src/js/list-editable.js +++ b/addons/base/static/src/js/list-editable.js @@ -8,10 +8,35 @@ openerp.base.list.editable = function (openerp) { // editability status of list rows openerp.base.ListView.prototype.defaults.editable = null; - var old_actual_search = openerp.base.ListView.prototype.do_actual_search; - var old_add_record = openerp.base.ListView.prototype.do_add_record; - var old_on_loaded = openerp.base.ListView.prototype.on_loaded; - _.extend(openerp.base.ListView.prototype, { + var old_init = openerp.base.ListView.prototype.init, + old_actual_search = openerp.base.ListView.prototype.do_actual_search, + old_add_record = openerp.base.ListView.prototype.do_add_record, + old_on_loaded = openerp.base.ListView.prototype.on_loaded; + // TODO: not sure second @lends on existing item is correct, to check + _.extend(openerp.base.ListView.prototype, /** @lends openerp.base.ListView# */{ + init: function () { + var self = this; + old_init.apply(this, arguments); + $(this.groups).bind({ + 'edit': function (e, id, dataset) { + self.do_edit(dataset.index, id, dataset); + } + }) + }, + /** + * Handles the activation of a record in editable mode (making a record + * editable), called *after* the record has become editable. + * + * The default behavior is to setup the listview's dataset to match + * whatever dataset was provided by the editing List + * + * @param {Number} index index of the record in the dataset + * @param {Object} id identifier of the record being edited + * @param {openerp.base.DataSet} dataset dataset in which the record is available + */ + do_edit: function (index, id, dataset) { + _.extend(this.dataset, dataset); + }, /** * Sets editability status for the list, based on defaults, view * architecture and the provided flag, if any. @@ -52,7 +77,8 @@ openerp.base.list.editable = function (openerp) { } }); - _.extend(openerp.base.ListView.Groups.prototype, { + _.extend(openerp.base.ListView.Groups.prototype, /** @lends openerp.base.ListView.Groups# */{ + passtrough_events: openerp.base.ListView.Groups.prototype.passtrough_events + " edit", new_record: function () { // TODO: handle multiple children this.children[null].new_record(); @@ -60,7 +86,7 @@ openerp.base.list.editable = function (openerp) { }); var old_list_row_clicked = openerp.base.ListView.List.prototype.row_clicked; - _.extend(openerp.base.ListView.List.prototype, { + _.extend(openerp.base.ListView.List.prototype, /** @lends openerp.base.ListView.List */{ row_clicked: function (event) { if (!this.options.editable) { return old_list_row_clicked.call(this, event); @@ -169,6 +195,7 @@ openerp.base.list.editable = function (openerp) { return; } self.dataset.next(); + self.edit_record(); }); }, this.options.editable === 'top'); }, @@ -186,6 +213,9 @@ openerp.base.list.editable = function (openerp) { this.$current.children( _.sprintf('[data-index=%d]', this.dataset.index))); + $(this).trigger( + 'edit', + [this.rows[this.dataset.index].data.id.value, this.dataset]); }, new_record: function () { this.dataset.index = null; diff --git a/addons/base/static/src/js/list.js b/addons/base/static/src/js/list.js index 41ff6e9ec14..dea3feb3970 100644 --- a/addons/base/static/src/js/list.js +++ b/addons/base/static/src/js/list.js @@ -392,7 +392,7 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi * * @param {Number} index index of the record in the dataset * @param {Object} id identifier of the activated record - * @param {openobject.base.DataSet} dataset dataset in which the record is available (may not be the listview's dataset in case of nested groups) + * @param {openerp.base.DataSet} dataset dataset in which the record is available (may not be the listview's dataset in case of nested groups) */ do_activate_record: function (index, id, dataset) { var self = this; From 9ef70d841ff5f9c65c91ae2e1c1fb0ae0f223f50 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Tue, 7 Jun 2011 13:05:29 +0200 Subject: [PATCH 27/36] [IMP] right-align integer and float columns bzr revid: xmo@openerp.com-20110607110529-yqwoig3kb4c0ewps --- addons/base/static/src/css/base.css | 4 ++++ addons/base/static/src/xml/base.xml | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/addons/base/static/src/css/base.css b/addons/base/static/src/css/base.css index b801fb69f09..d41d792b1bf 100644 --- a/addons/base/static/src/css/base.css +++ b/addons/base/static/src/css/base.css @@ -27,6 +27,10 @@ body.openerp { overflow-y: scroll; } +.openerp .oe-number { + text-align: right !important; +} + /* STATES */ .openerp .on_logged { display: none; diff --git a/addons/base/static/src/xml/base.xml b/addons/base/static/src/xml/base.xml index fe502231f17..cbb405886f3 100644 --- a/addons/base/static/src/xml/base.xml +++ b/addons/base/static/src/xml/base.xml @@ -299,8 +299,10 @@ +
+ t-att-class="'oe-field-cell' + (align ? ' oe-number' : '')" + t-att-data-field="column.id"> From 16fcca94a3d55772d15465a5b48f2ebb5012e07a Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Tue, 7 Jun 2011 13:36:29 +0200 Subject: [PATCH 28/36] [IMP] move list-wise aggregation results in the corresponding table columns, move labels to popover titles bzr revid: xmo@openerp.com-20110607113629-0jexwkti3bsiebxb --- addons/base/static/src/js/list.js | 21 ++++++++++++--------- addons/base/static/src/xml/base.xml | 8 +++++--- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/addons/base/static/src/js/list.js b/addons/base/static/src/js/list.js index dea3feb3970..235b3bc814b 100644 --- a/addons/base/static/src/js/list.js +++ b/addons/base/static/src/js/list.js @@ -201,10 +201,11 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi return column.invisible !== '1'; }); - this.aggregate_columns = _(this.columns).chain() - .filter(function (column) { - return column['sum'] || column['avg'];}) + this.aggregate_columns = _(this.visible_columns) .map(function (column) { + if (!(column['sum'] || column['avg'])) { + return {}; + } var func = column['sum'] ? 'sum' : 'avg'; return { field: column.id, @@ -212,7 +213,7 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi 'function': func, label: column[func] }; - }).value(); + }); }, /** * Used to handle a click on a table row, if no other handler caught the @@ -493,12 +494,14 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi return aggregator; }, display_aggregates: function (aggregation) { - var $footer = this.$element.find('.oe-list-footer').empty(); + var $footer_cells = this.$element.find('.oe-list-footer'); _(this.aggregate_columns).each(function (column) { - $(_.sprintf( - "%s: %.2f", - column.label, aggregation[column.field])) - .appendTo($footer); + if (!column['function']) { + return; + } + var pattern = (column.type == 'integer') ? '%d' : '%.2f'; + $footer_cells.filter(_.sprintf('[data-field=%s]', column.field)) + .text(_.sprintf(pattern, aggregation[column.field])); }); } // TODO: implement reorder (drag and drop rows) diff --git a/addons/base/static/src/xml/base.xml b/addons/base/static/src/xml/base.xml index cbb405886f3..56cc6295f76 100644 --- a/addons/base/static/src/xml/base.xml +++ b/addons/base/static/src/xml/base.xml @@ -272,11 +272,13 @@
+
From e4ddec2cb1ae626384bbe1a98891a5ad1a466279 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Tue, 7 Jun 2011 16:02:48 +0200 Subject: [PATCH 29/36] [IMP] disable [new|add] and [delete] list-level buttons instead of removing them entirely from view bzr revid: xmo@openerp.com-20110607140248-4zu9m1q28vh7h6yq --- addons/base/static/src/js/list.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/addons/base/static/src/js/list.js b/addons/base/static/src/js/list.js index 235b3bc814b..64282c51a43 100644 --- a/addons/base/static/src/js/list.js +++ b/addons/base/static/src/js/list.js @@ -136,9 +136,9 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi // Head hook this.$element.find('#oe-list-add') .click(this.do_add_record) - .toggle(!(grouped && this.options.editable)); + .attr('disabled', grouped && this.options.editable); this.$element.find('#oe-list-delete') - .hide() + .attr('disabled', true) .click(this.do_delete_selected); this.$element.find('thead').delegate('th[data-id]', 'click', function (e) { e.stopPropagation(); @@ -360,7 +360,7 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi */ do_select: function (ids, records) { this.$element.find('#oe-list-delete') - .toggle(!!ids.length); + .attr('disabled', !ids.length); if (!records.length) { this.compute_aggregates(); From dc47f5a0c319243665678e14c4b5047b880be7c6 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Tue, 7 Jun 2011 16:13:58 +0200 Subject: [PATCH 30/36] [IMP] swap [save] and [cancel] buttons in editable list view bzr revid: xmo@openerp.com-20110607141358-9o6litpyk74njgp3 --- addons/base/static/src/xml/base.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/base/static/src/xml/base.xml b/addons/base/static/src/xml/base.xml index 56cc6295f76..c65356a3477 100644 --- a/addons/base/static/src/xml/base.xml +++ b/addons/base/static/src/xml/base.xml @@ -872,10 +872,10 @@ 't-as': this.attr('t-as') }) .replaceAll(this) - .before($(document.createElement('td')).append( + .after($(document.createElement('td')).append( $(document.createElement('button')).attr({ 'class': 'oe-edit-row-save', 'type': 'button'}).text('Save'))) - .after($(document.createElement('td')).append( + .before($(document.createElement('td')).append( $(document.createElement('button')).attr({ 'class': 'oe-edit-row-cancel', 'type': 'button'}).text('Cancel'))) .unwrap(); From dd4a3b80479bbe974a71a4d269abe237b52e31c6 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Tue, 7 Jun 2011 16:42:15 +0200 Subject: [PATCH 31/36] [IMP] mandate that @group_operator and listwise aggregation functions be coherent on a listview field, so we can aggregate groups correctly bzr revid: xmo@openerp.com-20110607144215-g6beueaoo3tu350r --- addons/base/static/src/js/list.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/addons/base/static/src/js/list.js b/addons/base/static/src/js/list.js index 64282c51a43..60aa252cedb 100644 --- a/addons/base/static/src/js/list.js +++ b/addons/base/static/src/js/list.js @@ -203,15 +203,20 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi this.aggregate_columns = _(this.visible_columns) .map(function (column) { - if (!(column['sum'] || column['avg'])) { + if (column.type !== 'integer' && column.type !== 'float') { return {}; } - var func = column['sum'] ? 'sum' : 'avg'; + var aggregation_func = column['group_operator'] || 'sum'; + + if (!column[aggregation_func]) { + return {}; + } + return { field: column.id, type: column.type, - 'function': func, - label: column[func] + 'function': aggregation_func, + label: column[aggregation_func] }; }); }, From d6f59ca25b3d5428bde62503d1c92dde023e6524 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Wed, 8 Jun 2011 14:15:45 +0200 Subject: [PATCH 32/36] [IMP] simplify list-wise aggregation computations, improve sub-grouping so sub-groups are created immediately rather than on render bzr revid: xmo@openerp.com-20110608121545-aq7xmog2plmddcxe --- addons/base/static/src/js/list.js | 122 ++++++++++++------------------ 1 file changed, 47 insertions(+), 75 deletions(-) diff --git a/addons/base/static/src/js/list.js b/addons/base/static/src/js/list.js index 60aa252cedb..d1ae08bfb94 100644 --- a/addons/base/static/src/js/list.js +++ b/addons/base/static/src/js/list.js @@ -436,67 +436,38 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi * @param {Array} [records] */ compute_aggregates: function (records) { - if (_.isEmpty(this.aggregate_columns)) { - return; - } + var columns = _(this.aggregate_columns).filter(function (column) { + return column['function']; }); + + if (_.isEmpty(columns)) { return; } + if (_.isEmpty(records)) { records = this.groups.get_records(); } - var aggregator = this.build_aggregator(this.aggregate_columns); - this.display_aggregates( - _(records).reduce(aggregator, aggregator).value()); - }, - /** - * Creates a stateful callable aggregator object, which can be reduced over - * a collection of records in order to build the aggregations described - * by the parameter - * - * @param {Array} aggregation_descriptors - */ - build_aggregator: function (aggregation_descriptors) { - var values = {}; - var descriptors = {}; - _(aggregation_descriptors).each(function (descriptor) { - values[descriptor.field] = []; - descriptors[descriptor.field] = descriptor; + var count = 0, sums = {}; + _(columns).each(function (column) { sums[column.field] = 0; }); + _(records).each(function (record) { + count += 1; //(record.count || 1); + _(columns).each(function (column) { + sums[column.field] += record[column.field]; + }); }); - var aggregator = function (_i, record) { - _(values).each(function (collection, key) { - collection.push(record[key]); - }); + var aggregates = {}; + _(columns).each(function (column) { + var field = column.field; + switch (column['function']) { + case 'sum': + aggregates[field] = sums[field]; + break; + case 'avg': + aggregates[field] = sums[field] / count; + break; + } + }); - return aggregator; - }; - aggregator.value = function () { - var result = {}; - - _(values).each(function (collection, key) { - var value; - switch(descriptors[key]['function']) { - case 'avg': - value = (_(collection).chain() - .filter(function (item) { - return !_.isUndefined(item); }) - .reduce(function (total, item) { - return total + item; }, 0).value() - / collection.length); - break; - case 'sum': - value = (_(collection).chain() - .filter(function (item) { - return !_.isUndefined(item); }) - .reduce(function (total, item) { - return total + item; }, 0).value()); - break; - } - result[key] = value; - }); - - return result; - }; - return aggregator; + this.display_aggregates(aggregates); }, display_aggregates: function (aggregation) { var $footer_cells = this.$element.find('.oe-list-footer'); @@ -790,24 +761,11 @@ openerp.base.ListView.Groups = Class.extend( /** @lends openerp.base.ListView.Gr } return red_letter_tboday; }, - open_group: function (e, group) { - var row = e.currentTarget; - - if (this.children[group.value]) { - this.children[group.value].apoptosis(); - delete this.children[group.value]; - } - var prospekt = this.children[group.value] = new openerp.base.ListView.Groups(this.view, { - options: this.options, - columns: this.columns - }); - this.bind_child_events(prospekt); - prospekt.datagroup = group; - prospekt.render().insertAfter( - this.point_insertion(row)); - $(row).find('span.ui-icon') - .removeClass('ui-icon-triangle-1-e') - .addClass('ui-icon-triangle-1-s'); + open: function (point_insertion) { + this.render().insertAfter(point_insertion); + }, + close: function () { + this.apoptosis(); }, /** * Prefixes ``$node`` with floated spaces in order to indent it relative @@ -827,18 +785,32 @@ openerp.base.ListView.Groups = Class.extend( /** @lends openerp.base.ListView.Gr var self = this; var placeholder = this.make_fragment(); _(datagroups).each(function (group) { + if (self.children[group.value]) { + self.children[group.value].apoptosis(); + delete self.children[group.value]; + } + var child = self.children[group.value] = new openerp.base.ListView.Groups(self.view, { + options: self.options, + columns: self.columns + }); + self.bind_child_events(child); + child.datagroup = group; + var $row = $(''); if (group.openable) { $row.click(function (e) { if (!$row.data('open')) { - $row.data('open', true); - self.open_group(e, group); + $row.data('open', true) + .find('span.ui-icon') + .removeClass('ui-icon-triangle-1-e') + .addClass('ui-icon-triangle-1-s'); + child.open(self.point_insertion(e.currentTarget)); } else { $row.removeData('open') .find('span.ui-icon') .removeClass('ui-icon-triangle-1-s') .addClass('ui-icon-triangle-1-e'); - _(self.children).each(function (child) {child.apoptosis();}); + child.close(); } }); } From 80e26dce12293b3f31cbdc75c1bbc3599f1e2691 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Wed, 8 Jun 2011 14:55:31 +0200 Subject: [PATCH 33/36] [FIX] list-wise aggregation in case of groups bzr revid: xmo@openerp.com-20110608125531-qmsey70ec127asyt --- addons/base/static/src/js/list.js | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/addons/base/static/src/js/list.js b/addons/base/static/src/js/list.js index d1ae08bfb94..b4528de5afd 100644 --- a/addons/base/static/src/js/list.js +++ b/addons/base/static/src/js/list.js @@ -371,7 +371,9 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi this.compute_aggregates(); return; } - this.compute_aggregates(records); + this.compute_aggregates(_(records).map(function (record) { + return {count: 1, values: record}; + })); }, /** * Handles action button signals on a record @@ -448,9 +450,17 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi var count = 0, sums = {}; _(columns).each(function (column) { sums[column.field] = 0; }); _(records).each(function (record) { - count += 1; //(record.count || 1); + count += record.count || 1; _(columns).each(function (column) { - sums[column.field] += record[column.field]; + var field = column.field; + switch (column['function']) { + case 'sum': + sums[field] += record.values[field]; + break; + case 'avg': + sums[field] += record.count * record.values[field]; + break; + } }); }); @@ -630,7 +640,7 @@ openerp.base.ListView.List = Class.extend( /** @lends openerp.base.ListView.List _(row.data).each(function (obj, key) { record[key] = obj.value; }); - return record; + return {count: 1, values: record}; }); }, /** @@ -941,6 +951,12 @@ openerp.base.ListView.Groups = Class.extend( /** @lends openerp.base.ListView.Gr return this; }, get_records: function () { + if (_(this.children).isEmpty()) { + return { + count: this.datagroup.length, + values: this.datagroup.aggregates + } + } return _(this.children).chain() .map(function (child) { return child.get_records(); From f824cd6aa3589838b0fe5bccd6455131c81845e8 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Wed, 8 Jun 2011 15:33:01 +0200 Subject: [PATCH 34/36] [FIX] recompute aggregates after saving a record bzr revid: xmo@openerp.com-20110608133301-l3cdm9r2254zs20u --- addons/base/static/src/js/list-editable.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/addons/base/static/src/js/list-editable.js b/addons/base/static/src/js/list-editable.js index ec9f21f61b7..fd20a51c0ef 100644 --- a/addons/base/static/src/js/list-editable.js +++ b/addons/base/static/src/js/list-editable.js @@ -20,6 +20,12 @@ openerp.base.list.editable = function (openerp) { $(this.groups).bind({ 'edit': function (e, id, dataset) { self.do_edit(dataset.index, id, dataset); + }, + 'saved': function () { + if (self.groups.get_selection().length) { + return; + } + self.compute_aggregates(); } }) }, @@ -78,7 +84,7 @@ openerp.base.list.editable = function (openerp) { }); _.extend(openerp.base.ListView.Groups.prototype, /** @lends openerp.base.ListView.Groups# */{ - passtrough_events: openerp.base.ListView.Groups.prototype.passtrough_events + " edit", + passtrough_events: openerp.base.ListView.Groups.prototype.passtrough_events + " edit saved", new_record: function () { // TODO: handle multiple children this.children[null].new_record(); @@ -187,6 +193,8 @@ openerp.base.list.editable = function (openerp) { delete self.edition_form; delete self.edition_index; delete self.edition; + + $(self).trigger('saved', [self.dataset]); if (!edit_next) { return; } From a905ab7f178ee082642c4d7bf9908cdb3d69fd3d Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Wed, 8 Jun 2011 15:51:41 +0200 Subject: [PATCH 35/36] [IMP] don't wait for the bloody record to be fully loaded to fixup cell styles and add groupby columns (in case of grouping), it makes the list titles jittery which looks terrible. Instead, can fixup the row right after rendering, which makes the whole thing much stabler bzr revid: xmo@openerp.com-20110608135141-g6rz1g77ykyg2cja --- addons/base/static/src/js/list-editable.js | 35 ++++++++++------------ 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/addons/base/static/src/js/list-editable.js b/addons/base/static/src/js/list-editable.js index fd20a51c0ef..9bbde25a74d 100644 --- a/addons/base/static/src/js/list-editable.js +++ b/addons/base/static/src/js/list-editable.js @@ -158,26 +158,23 @@ openerp.base.list.editable = function (openerp) { template: 'ListView.row.form', registry: openerp.base.list.form.widgets }); - this.edition_form.on_loaded({fields_view: this.get_fields_view()}); - this.edition_form.on_record_loaded.add({ - position: 'last', - unique: true, - callback: function () { - var $e = self.edition_form.$element; - $e.find('td') - .addClass('oe-field-cell') - .removeAttr('width') - .end() - .find('td:first').removeClass('oe-field-cell').end() - .find('td:last').removeClass('oe-field-cell').end(); - _(self.columns).each(function (column) { - if (column.meta) { - $e.prepend(''); - } - }); - } + $.when(this.edition_form.on_loaded({fields_view: this.get_fields_view()})).then(function () { + // put in $.when just in case FormView.on_loaded becomes asynchronous + $new_row.find('td') + .addClass('oe-field-cell') + .removeAttr('width') + .end() + .find('td:first').removeClass('oe-field-cell').end() + .find('td:last').removeClass('oe-field-cell').end(); + // pad in case of groupby + _(self.columns).each(function (column) { + if (column.meta) { + $new_row.prepend(''); + } + }); + + self.edition_form.do_show(); }); - this.edition_form.do_show(); }, /** * Saves the current row, and triggers the edition of its following From be26be0d7c04e6668d5a13f307793503a89e5212 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Wed, 8 Jun 2011 16:58:50 +0200 Subject: [PATCH 36/36] [FIX] property name for click even in jQuery is 'click', not 'onclick' and Firefox does *not* like the wrong one bzr revid: xmo@openerp.com-20110608145850-9fvq4celpj1v4ept --- addons/base/static/src/js/list-editable.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/base/static/src/js/list-editable.js b/addons/base/static/src/js/list-editable.js index 9bbde25a74d..f7eb3e27bca 100644 --- a/addons/base/static/src/js/list-editable.js +++ b/addons/base/static/src/js/list-editable.js @@ -123,7 +123,7 @@ openerp.base.list.editable = function (openerp) { var $new_row = $('', { id: _.uniqueId('oe-editable-row-'), 'class': $(row).attr('class'), - onclick: function (e) {e.stopPropagation();} + click: function (e) {e.stopPropagation();} }) .keyup(function (e) { switch (e.which) {