summaryrefslogtreecommitdiff
path: root/addons/web/static/src
diff options
context:
space:
mode:
authorJiten (OpenERP) <jra@tinyerp.com>2012-10-19 11:18:45 +0530
committerJiten (OpenERP) <jra@tinyerp.com>2012-10-19 11:18:45 +0530
commitee40868da97fe5606592f00cc07c5d1a06c5de10 (patch)
tree4da679fcf66b62ad030a2035edb11020bdb3a86e /addons/web/static/src
parent399e13ec854189e87e5f0585565bb22de9b9f626 (diff)
parent4755454c2a7cbb37aab3ca29978fa7ac561ec9f6 (diff)
[MERGE] Merged trunk.
bzr revid: jra@tinyerp.com-20121016054400-14aecskaihyisudd bzr revid: jra@tinyerp.com-20121017104013-izsyo7vmhr34j6ta bzr revid: jra@tinyerp.com-20121018060329-7ww5t46blyw2a60q bzr revid: jra@tinyerp.com-20121019054845-47naqth4kfe6xczv
Diffstat (limited to 'addons/web/static/src')
-rw-r--r--addons/web/static/src/css/base.css29
-rw-r--r--addons/web/static/src/css/base.sass22
-rw-r--r--addons/web/static/src/js/chrome.js36
-rw-r--r--addons/web/static/src/js/corelib.js97
-rw-r--r--addons/web/static/src/js/data.js93
-rw-r--r--addons/web/static/src/js/formats.js2
-rw-r--r--addons/web/static/src/js/search.js65
-rw-r--r--addons/web/static/src/js/view_form.js557
-rw-r--r--addons/web/static/src/js/view_list.js84
-rw-r--r--addons/web/static/src/js/view_list_editable.js31
-rw-r--r--addons/web/static/src/js/views.js111
-rw-r--r--addons/web/static/src/xml/base.xml10
12 files changed, 512 insertions, 625 deletions
diff --git a/addons/web/static/src/css/base.css b/addons/web/static/src/css/base.css
index c0adb4e99df..36c5e96bd19 100644
--- a/addons/web/static/src/css/base.css
+++ b/addons/web/static/src/css/base.css
@@ -25,7 +25,6 @@
display: none !important;
}
}
-
.openerp.openerp_webclient_container {
height: 100%;
position: relative;
@@ -2562,6 +2561,12 @@
.openerp .oe_list.oe_list_editable td.oe_list_record_delete {
position: absolute;
}
+.openerp .oe_list.oe_list_editable.oe_editing .oe_edition .oe_list_field_cell:not(.oe_readonly) {
+ color: transparent;
+}
+.openerp .oe_list.oe_list_editable.oe_editing .oe_edition .oe_list_field_cell:not(.oe_readonly) * {
+ visibility: hidden;
+}
.openerp .oe_list.oe_list_editable.oe_editing .oe_m2o_drop_down_button {
top: 5px;
}
@@ -2580,10 +2585,10 @@
}
.openerp .oe_list.oe_list_editable.oe_editing .oe_form_field.oe_form_field_float input, .openerp .oe_list.oe_list_editable.oe_editing .oe_form_field.oe_form_view_integer input {
text-align: right;
- width: 100%;
+ width: 100% !important;
}
.openerp .oe_list.oe_list_editable.oe_editing .oe_form_field.oe_form_field_datetime > span, .openerp .oe_list.oe_list_editable.oe_editing .oe_form_field.oe_form_field_date > span {
- width: 100%;
+ width: 100% !important;
}
.openerp .oe_list.oe_list_editable.oe_editing .oe_form_field.oe_form_field_datetime input.oe_datepicker_master, .openerp .oe_list.oe_list_editable.oe_editing .oe_form_field.oe_form_field_date input.oe_datepicker_master {
width: 100% !important;
@@ -2592,6 +2597,9 @@
min-width: 0 !important;
max-width: none !important;
}
+.openerp .oe_list_group_name {
+ white-space: nowrap;
+}
.openerp .oe_form .oe_form_field_many2many > .oe_list .oe_list_pager_single_page {
display: none;
}
@@ -2651,14 +2659,17 @@
background: #eeeeee;
vertical-align: top;
}
-.openerp .oe_list_content > thead th {
- position: relative;
-}
.openerp .oe_list_content td, .openerp .oe_list_content th {
padding: 3px 6px;
line-height: 18px;
}
-.openerp .oe_list_content th:after {
+.openerp .oe_list_content th.oe_sortable, .openerp .oe_list_content th.oe_sortable div {
+ cursor: pointer;
+}
+.openerp .oe_list_content th.oe_sortable div {
+ position: relative;
+}
+.openerp .oe_list_content th.oe_sortable div:after {
position: absolute;
right: 6px;
content: "";
@@ -2668,12 +2679,12 @@
border-color: black transparent;
visibility: hidden;
}
-.openerp .oe_list_content th.sortup:after {
+.openerp .oe_list_content th.sortup div:after {
visibility: visible;
filter: alpha(opacity=60);
opacity: 0.6;
}
-.openerp .oe_list_content th.sortdown:after {
+.openerp .oe_list_content th.sortdown div:after {
border-bottom: none;
border-left: 4px solid transparent;
border-right: 4px solid transparent;
diff --git a/addons/web/static/src/css/base.sass b/addons/web/static/src/css/base.sass
index 09c39afafa9..5f9e4f2e31f 100644
--- a/addons/web/static/src/css/base.sass
+++ b/addons/web/static/src/css/base.sass
@@ -2026,6 +2026,10 @@ $sheet-max-width: 860px
td.oe_list_record_delete
position: absolute
.oe_list.oe_list_editable.oe_editing
+ .oe_edition .oe_list_field_cell:not(.oe_readonly)
+ *
+ visibility: hidden
+ color: transparent
.oe_m2o_drop_down_button
top: 5px
.oe_m2o_cm_button
@@ -2040,15 +2044,17 @@ $sheet-max-width: 860px
&.oe_form_field_float,&.oe_form_view_integer
input
text-align: right
- width: 100%
+ width: 100% !important
&.oe_form_field_datetime,&.oe_form_field_date
> span
- width: 100%
+ width: 100% !important
input.oe_datepicker_master
width: 100% !important
.oe_form_field_float,.oe_form_view_integer,&.oe_form_field_datetime,&.oe_form_field_date
min-width: 0 !important
max-width: none !important
+ .oe_list_group_name
+ white-space: nowrap
// }}}
// FormView.many2many {{{
.oe_form .oe_form_field_many2many > .oe_list
@@ -2098,12 +2104,14 @@ $sheet-max-width: 860px
border-bottom: 2px solid #cacaca
background: #eee
vertical-align: top
- th
- position: relative
td, th
padding: 3px 6px
line-height: 18px
- th:after
+ th.oe_sortable, th.oe_sortable div
+ cursor: pointer
+ th.oe_sortable div
+ position: relative
+ th.oe_sortable div:after
position: absolute
right: 6px
content: ""
@@ -2112,10 +2120,10 @@ $sheet-max-width: 860px
border-style: solid
border-color: #000 transparent
visibility: hidden
- th.sortup:after
+ th.sortup div:after
visibility: visible
@include opacity(0.6)
- th.sortdown:after
+ th.sortdown div:after
border-bottom: none
border-left: 4px solid transparent
border-right: 4px solid transparent
diff --git a/addons/web/static/src/js/chrome.js b/addons/web/static/src/js/chrome.js
index c993f3e3ad1..3cb4437d1c3 100644
--- a/addons/web/static/src/js/chrome.js
+++ b/addons/web/static/src/js/chrome.js
@@ -70,7 +70,9 @@ instance.web.Dialog = instance.web.Widget.extend({
autoOpen: false,
position: [false, 40],
buttons: {},
- beforeClose: function () { self.close(); },
+ beforeClose: function () {
+ self.trigger("closing");
+ },
resizeStop: this.on_resized
};
for (var f in this) {
@@ -84,6 +86,7 @@ instance.web.Dialog = instance.web.Widget.extend({
}
_.extend(this.dialog_options, options);
}
+ this.on("closing", this, this._closing);
},
get_options: function(options) {
var self = this,
@@ -126,13 +129,10 @@ instance.web.Dialog = instance.web.Widget.extend({
if (! this.dialog_inited)
this.init_dialog();
var o = this.get_options(options);
- if (! this.params_buttons) {
- this.$buttons.appendTo($("body"));
- }
+ this.$buttons.appendTo($("body"));
instance.web.dialog(this.$el, o).dialog('open');
- if (! this.params_buttons) {
- this.$buttons.appendTo(this.$el.dialog("widget"));
- }
+ this.$el.dialog("widget").find(".ui-dialog-buttonpane").remove();
+ this.$buttons.appendTo(this.$el.dialog("widget"));
if (o.height === 'auto' && o.max_height) {
this.$el.css({ 'max-height': o.max_height, 'overflow-y': 'auto' });
}
@@ -145,19 +145,23 @@ instance.web.Dialog = instance.web.Widget.extend({
if (! this.params_buttons) {
this.$buttons = $('<div class="ui-dialog-buttonpane ui-widget-content ui-helper-clearfix" />');
this.$el.dialog("widget").append(this.$buttons);
+ } else {
+ this.$buttons = this.$el.dialog("widget").find(".ui-dialog-buttonpane");
}
this.dialog_inited = true;
var res = this.start();
return res;
},
- close: function() {
+ close: function() {
+ this.$el.dialog('close');
+ },
+ _closing: function() {
if (this.__tmp_dialog_destroying)
return;
if (this.dialog_options.destroy_on_close) {
this.__tmp_dialog_closing = true;
this.destroy();
this.__tmp_dialog_closing = undefined;
- this.trigger("dialog_close");
}
},
on_resized: function() {
@@ -697,9 +701,12 @@ instance.web.Menu = instance.web.Widget.extend({
return this.do_reload();
},
do_reload: function() {
- return this.rpc("/web/menu/load", {}).then(this.on_loaded);
+ var self = this;
+ return this.rpc("/web/menu/load", {}).then(function(r) {
+ self.menu_loaded(r);
+ });
},
- on_loaded: function(data) {
+ menu_loaded: function(data) {
var self = this;
this.data = data;
this.renderElement();
@@ -716,6 +723,7 @@ instance.web.Menu = instance.web.Widget.extend({
if (self.current_menu) {
self.open_menu(self.current_menu);
}
+ this.trigger('menu_loaded', data);
this.has_been_loaded.resolve();
},
limit_entries: function() {
@@ -1116,9 +1124,11 @@ instance.web.WebClient = instance.web.Client.extend({
.pipe(function (result) {
var action = result;
if (options.needaction) {
- action.context.search_default_needaction_pending = true;
+ action.context.search_default_message_unread = true;
}
- return $.when(self.action_manager.do_action(action, null, true)).fail(function() {
+ return $.when(self.action_manager.do_action(action, {
+ clear_breadcrumbs: true,
+ })).fail(function() {
self.menu.open_menu(options.previous_menu_id);
});
});
diff --git a/addons/web/static/src/js/corelib.js b/addons/web/static/src/js/corelib.js
index 8c38724b1d5..6ecbfdd01ac 100644
--- a/addons/web/static/src/js/corelib.js
+++ b/addons/web/static/src/js/corelib.js
@@ -291,6 +291,17 @@ var Events = instance.web.Class.extend({
return this;
},
+ callbackList: function() {
+ var lst = [];
+ _.each(this._callbacks || {}, function(el, eventName) {
+ var node = el;
+ while ((node = node.next) && node.next) {
+ lst.push([eventName, node.callback, node.context]);
+ }
+ });
+ return lst;
+ },
+
trigger : function(events) {
var event, node, calls, tail, args, all, rest;
if (!(calls = this._callbacks))
@@ -369,9 +380,9 @@ instance.web.EventDispatcherMixin = _.extend({}, instance.web.ParentedMixin, {
event.source.__edispatcherEvents.off(event.name, event.func, self);
});
this.__edispatcherRegisteredEvents = [];
- if(!this.__edispatcherEvents) {
- debugger;
- }
+ _.each(this.__edispatcherEvents.callbackList(), function(cal) {
+ this.off(cal[0], cal[2], cal[1]);
+ }, this);
this.__edispatcherEvents.off();
instance.web.ParentedMixin.destroy.call(this);
}
@@ -382,8 +393,17 @@ instance.web.PropertiesMixin = _.extend({}, instance.web.EventDispatcherMixin, {
instance.web.EventDispatcherMixin.init.call(this);
this.__getterSetterInternalMap = {};
},
- set: function(map, options) {
- options = options || {};
+ set: function(arg1, arg2, arg3) {
+ var map;
+ var options;
+ if (typeof arg1 === "string") {
+ map = {};
+ map[arg1] = arg2;
+ options = arg3 || {};
+ } else {
+ map = arg1;
+ options = arg2 || {};
+ }
var self = this;
var changed = false;
_.each(map, function(val, key) {
@@ -410,71 +430,16 @@ instance.web.CallbackEnabledMixin = _.extend({}, instance.web.PropertiesMixin, {
init: function() {
instance.web.PropertiesMixin.init.call(this);
var self = this;
- var callback_maker = function(obj, name, method) {
- var callback = function() {
- var args = Array.prototype.slice.call(arguments);
- self.trigger.apply(self, [name].concat(args));
- var r;
- for(var i = 0; i < callback.callback_chain.length; i++) {
- var c = callback.callback_chain[i];
- if(c.unique) {
- callback.callback_chain.splice(i, 1);
- i -= 1;
- }
- var result = c.callback.apply(c.self, c.args.concat(args));
- if (c.callback === method) {
- // return the result of the original method
- r = result;
- }
- // TODO special value to stop the chain
- // instance.web.callback_stop
- }
- return r;
- };
- callback.callback_chain = [];
- callback.add = function(f) {
- if(typeof(f) == 'function') {
- f = { callback: f, args: Array.prototype.slice.call(arguments, 1) };
- }
- f.self = f.self || null;
- f.args = f.args || [];
- f.unique = !!f.unique;
- if(f.position == 'last') {
- callback.callback_chain.push(f);
- } else {
- callback.callback_chain.unshift(f);
- }
- return callback;
- };
- callback.add_first = function(f) {
- return callback.add.apply(null,arguments);
- };
- callback.add_last = function(f) {
- return callback.add({
- callback: f,
- args: Array.prototype.slice.call(arguments, 1),
- position: "last"
- });
- };
- callback.remove = function(f) {
- callback.callback_chain = _.difference(callback.callback_chain, _.filter(callback.callback_chain, function(el) {
- return el.callback === f;
- }));
- return callback;
- };
-
- return callback.add({
- callback: method,
- self:obj,
- args:Array.prototype.slice.call(arguments, 3)
- });
- };
// Transform on_/do_* methods into callbacks
+ var callback_maker = function(fn) {
+ return function() {
+ return fn.apply(self, arguments);
+ }
+ };
for (var name in this) {
if(typeof(this[name]) == "function") {
- this[name].debug_name = name;
if((/^on_|^do_/).test(name)) {
- this[name] = callback_maker(this, name, this[name]);
+ this[name] = callback_maker(this[name]);
}
}
}
diff --git a/addons/web/static/src/js/data.js b/addons/web/static/src/js/data.js
index fe7632d0612..30a8b9862fc 100644
--- a/addons/web/static/src/js/data.js
+++ b/addons/web/static/src/js/data.js
@@ -120,6 +120,10 @@ instance.web.Query = instance.web.Class.extend({
if (_.isEmpty(grouping)) { return null; }
var self = this;
+
+ // FIXME: when pyeval is merged
+ var ctx = instance.session.test_eval_contexts(
+ [this._model.context(this._context)]);
return this._model.call('read_group', {
groupby: grouping,
fields: _.uniq(grouping.concat(this._fields || [])),
@@ -130,6 +134,8 @@ instance.web.Query = instance.web.Class.extend({
orderby: instance.web.serialize_sort(this._order_by) || false
}).pipe(function (results) {
return _(results).map(function (result) {
+ // FIX: querygroup initialization
+ _.defaults(result.__context, ctx);
return new instance.web.QueryGroup(
self._model.name, grouping[0], result);
});
@@ -350,89 +356,6 @@ instance.web.Model = instance.web.Class.extend({
},
});
-instance.web.DataGroup = instance.web.CallbackEnabled.extend({
- /**
- * Management interface between views and grouped collections of OpenERP
- * records.
- *
- * The root DataGroup is instantiated with the relevant information
- * (a session, a model, a domain, a context and a group_by sequence), the
- * domain and context may be empty. It is then interacted with via
- * :js:func:`~instance.web.DataGroup.list`, which is used to read the
- * content of the current grouping level.
- *
- * @constructs instance.web.DataGroup
- * @extends instance.web.CallbackEnabled
- *
- * @param {instance.web.CallbackEnabled} parent widget
- * @param {String} model name of the model managed by this DataGroup
- * @param {Array} domain search domain for this DataGroup
- * @param {Object} context context of the DataGroup's searches
- * @param {Array} group_by sequence of fields by which to group
- * @param {Number} [level=0] nesting level of the group
- */
- init: function(parent, model, domain, context, group_by, level) {
- this._super(parent, null);
- this.model = new instance.web.Model(model, context, domain);
- this.group_by = group_by;
- this.context = context;
- this.domain = domain;
-
- this.level = level || 0;
- },
- list: function (fields, ifGroups, ifRecords) {
- var self = this;
- var query = this.model.query(fields).order_by(this.sort).group_by(this.group_by);
- $.when(query).then(function (querygroups) {
- // leaf node
- if (!querygroups) {
- var ds = new instance.web.DataSetSearch(self, self.model.name, self.model.context(), self.model.domain());
- ds._sort = self.sort;
- ifRecords(ds);
- return;
- }
- // internal node
- var child_datagroups = _(querygroups).map(function (group) {
- var child_context = _.extend(
- {}, self.model.context(), group.model.context());
- var child_dg = new instance.web.DataGroup(
- self, self.model.name, group.model.domain(),
- child_context, group.model._context.group_by,
- self.level + 1);
- child_dg.sort = self.sort;
- // copy querygroup properties
- child_dg.__context = child_context;
- child_dg.__domain = group.model.domain();
- child_dg.folded = group.get('folded');
- child_dg.grouped_on = group.get('grouped_on');
- child_dg.length = group.get('length');
- child_dg.value = group.get('value');
- child_dg.openable = group.get('has_children');
- child_dg.aggregates = group.get('aggregates');
- return child_dg;
- });
- ifGroups(child_datagroups);
- });
- }
-});
-
-instance.web.StaticDataGroup = instance.web.DataGroup.extend({
- /**
- * A specialization of data groups, relying on a single static
- * dataset as its records provider.
- *
- * @constructs instance.web.StaticDataGroup
- * @extends instance.web.DataGroup
- * @param {openep.web.DataSetStatic} dataset a static dataset backing the groups
- */
- init: function (dataset) {
- this.dataset = dataset;
- },
- list: function (fields, ifGroups, ifRecords) {
- ifRecords(this.dataset);
- }
-});
-
instance.web.DataSet = instance.web.CallbackEnabled.extend({
/**
* Collection of OpenERP records, used to share records and the current selection between views.
@@ -774,11 +697,11 @@ instance.web.DataSetSearch = instance.web.DataSet.extend({
if (self._length) {
self._length -= 1;
}
- if (this.index !== null) {
+ if (self.index !== null) {
self.index = self.index <= self.ids.length - 1 ?
self.index : (self.ids.length > 0 ? self.ids.length -1 : 0);
}
- this.trigger("dataset_changed", ids, callback, error_callback);
+ self.trigger("dataset_changed", ids, callback, error_callback);
});
},
size: function () {
diff --git a/addons/web/static/src/js/formats.js b/addons/web/static/src/js/formats.js
index 3e5e6fecd5e..03b2c5b775d 100644
--- a/addons/web/static/src/js/formats.js
+++ b/addons/web/static/src/js/formats.js
@@ -135,7 +135,7 @@ instance.web.format_value = function (value, descriptor, value_if_empty) {
Math.round((value % 1) * 60));
case 'many2one':
// name_get value format
- return value[1];
+ return value[1] ? value[1].split("\n")[0] : value[1];
case 'one2many':
case 'many2many':
if (typeof value === 'string') {
diff --git a/addons/web/static/src/js/search.js b/addons/web/static/src/js/search.js
index a5e1aaeb3ae..33d20980732 100644
--- a/addons/web/static/src/js/search.js
+++ b/addons/web/static/src/js/search.js
@@ -61,7 +61,7 @@ my.SearchQuery = B.Collection.extend({
this.on('change', function (facet) {
if(!facet.values.isEmpty()) { return; }
- this.remove(facet);
+ this.remove(facet, {silent: true});
}, this);
},
add: function (values, options) {
@@ -324,8 +324,9 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
context: this.dataset.get_context() });
$.when(load_view)
- .pipe(this.on_loaded)
- .fail(function () {
+ .pipe(function(r) {
+ self.search_view_loaded(r)
+ }).fail(function () {
self.ready.reject.apply(null, arguments);
});
}
@@ -645,7 +646,7 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
(new instance.web.search.Advanced(this));
},
- on_loaded: function(data) {
+ search_view_loaded: function(data) {
var self = this;
this.fields_view = data.fields_view;
if (data.fields_view.type !== 'search' ||
@@ -654,7 +655,6 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
"Got non-search view after asking for a search view: type %s, arch root %s",
data.fields_view.type, data.fields_view.arch.tag));
}
-
this.make_widgets(
data.fields_view['arch'].children,
data.fields_view.fields);
@@ -671,48 +671,12 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
'facet_for_defaults', this.defaults)).then(function () {
self.query.reset(_(arguments).compact(), {preventSearch: true});
});
-
+
return $.when(drawer_started, defaults_fetched)
- .then(function () { self.ready.resolve(); })
- },
- /**
- * Handle event when the user make a selection in the filters management select box.
- */
- on_filters_management: function(e) {
- var self = this;
- var select = this.$el.find(".oe_search-view-filters-management");
- var val = select.val();
- switch(val) {
- case 'advanced_filter':
- this.extended_search.on_activate();
- break;
- case '':
- this.do_clear();
- }
- if (val.slice(0, 4) == "get:") {
- val = val.slice(4);
- val = parseInt(val, 10);
- var filter = this.managed_filters[val];
- this.do_clear(false).then(_.bind(function() {
- select.val('get:' + val);
-
- var groupbys = [];
- var group_by = filter.context.group_by;
- if (group_by) {
- groupbys = _.map(
- group_by instanceof Array ? group_by : group_by.split(','),
- function (el) { return { group_by: el }; });
- }
- this.filter_data = {
- domains: [filter.domain],
- contexts: [filter.context],
- groupbys: groupbys
- };
- this.do_search();
- }, this));
- } else {
- select.val('');
- }
+ .then(function () {
+ self.trigger("search_view_loaded", data);
+ self.ready.resolve();
+ });
},
/**
* Extract search data from the view's facets.
@@ -763,7 +727,8 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
groupbys: groupbys,
errors: errors
};
- }, /**
+ },
+ /**
* Performs the search view collection of widget data.
*
* If the collection went well (all fields are valid), then triggers
@@ -771,8 +736,6 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
*
* If at least one field failed its validation, triggers
* :js:func:`instance.web.SearchView.on_invalid` instead.
- *
- * @param e jQuery event object coming from the "Search" button
*/
do_search: function (_query, options) {
if (options && options.preventSearch) {
@@ -783,7 +746,7 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
this.on_invalid(search.errors);
return;
}
- return this.on_search(search.domains, search.contexts, search.groupbys);
+ return this.trigger('search_data', search.domains, search.contexts, search.groupbys);
},
/**
* Triggered after the SearchView has collected all relevant domains and
@@ -801,8 +764,6 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
* @param {Array} contexts an array of literal contexts or context refs
* @param {Array} groupbys ordered contexts which may or may not have group_by keys
*/
- on_search: function (domains, contexts, groupbys) {
- },
/**
* Triggered after a validation error in the SearchView fields.
*
diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js
index 60c33090c56..d096e15b51d 100644
--- a/addons/web/static/src/js/view_form.js
+++ b/addons/web/static/src/js/view_form.js
@@ -110,6 +110,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
"not_interactible_on_create": false,
"initial_mode": "view",
"disable_autofocus": false,
+ "footer_to_buttons": false,
});
this.is_initialized = $.Deferred();
this.mutating_mutex = new $.Mutex();
@@ -118,7 +119,6 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
this.__clicked_inside = false;
this.__blur_timeout = null;
this.rendering_engine = new instance.web.form.FormRenderingEngine(this);
- this.qweb = null; // A QWeb instance will be created if the view is a QWeb template
self.set({actual_mode: self.options.initial_mode});
this.has_been_loaded.then(function() {
self.on("change:actual_mode", self, self.check_actual_mode);
@@ -127,6 +127,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
self.init_pager();
});
self.on("load_record", self, self.load_record);
+ this.on('view_loaded', self, self.load_form);
instance.web.bus.on('clear_uncommitted_changes', this, function(e) {
if (!this.can_be_discarded()) {
e.preventDefault();
@@ -143,13 +144,13 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
}
this._super();
},
- on_loaded: function(data) {
+ load_form: function(data) {
var self = this;
if (!data) {
throw new Error("No data provided.");
}
if (this.arch) {
- throw "Form view does not support multiple calls to on_loaded";
+ throw "Form view does not support multiple calls to load_form";
}
this.fields_order = [];
this.fields_view = data;
@@ -157,11 +158,9 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
this.rendering_engine.set_fields_registry(this.fields_registry);
this.rendering_engine.set_tags_registry(this.tags_registry);
this.rendering_engine.set_widgets_registry(this.widgets_registry);
- if (!this.extract_qweb_template(data)) {
- this.rendering_engine.set_fields_view(data);
- var $dest = this.$el.hasClass("oe_form_container") ? this.$el : this.$el.find('.oe_form_container');
- this.rendering_engine.render_to($dest);
- }
+ this.rendering_engine.set_fields_view(data);
+ var $dest = this.$el.hasClass("oe_form_container") ? this.$el : this.$el.find('.oe_form_container');
+ this.rendering_engine.render_to($dest);
this.$el.on('mousedown.formBlur', function () {
self.__clicked_inside = true;
@@ -169,6 +168,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
this.$buttons = $(QWeb.render("FormView.buttons", {'widget':self}));
if (this.options.$buttons) {
+ this.options.$buttons.children().remove();
this.$buttons.appendTo(this.options.$buttons);
} else {
this.$el.find('.oe_form_buttons').replaceWith(this.$buttons);
@@ -177,6 +177,9 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
this.$buttons.on('click', '.oe_form_button_edit', this.on_button_edit);
this.$buttons.on('click', '.oe_form_button_save', this.on_button_save);
this.$buttons.on('click', '.oe_form_button_cancel', this.on_button_cancel);
+ if (this.options.footer_to_buttons) {
+ this.$el.find('footer').appendTo(this.$buttons);
+ }
this.$sidebar = this.options.$sidebar || this.$el.find('.oe_form_sidebar');
if (!this.sidebar && this.options.$sidebar) {
@@ -211,60 +214,9 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
e.stopPropagation();
}
});
-
+ this.trigger('form_view_loaded', data);
return $.when();
},
- extract_qweb_template: function(fvg) {
- for (var i=0, ii=fvg.arch.children.length; i < ii; i++) {
- var child = fvg.arch.children[i];
- if (child.tag === "templates") {
- this.qweb = new QWeb2.Engine();
- this.qweb.add_template(instance.web.json_node_to_xml(child));
- if (!this.qweb.has_template('form')) {
- throw new Error("No QWeb template found for form view");
- }
- return true;
- }
- }
- this.qweb = null;
- return false;
- },
- get_fvg_from_qweb: function(record) {
- var view = this.qweb.render('form', this.get_qweb_context(record));
- var fvg = _.clone(this.fields_view);
- fvg.arch = instance.web.xml_to_json(instance.web.str_to_xml(view).firstChild);
- return fvg;
- },
- get_qweb_context: function(record) {
- var self = this,
- new_record = {};
- _.each(record, function(value_, name) {
- var r = _.clone(self.fields_view.fields[name] || {});
- if ((r.type === 'date' || r.type === 'datetime') && value_) {
- r.raw_value = instance.web.auto_str_to_date(value_);
- } else {
- r.raw_value = value_;
- }
- r.value = instance.web.format_value(value_, r);
- new_record[name] = r;
- });
- return {
- record : new_record,
- new_record : !record.id
- };
- },
- kill_current_form: function() {
- _.each(this.getChildren(), function(el) {
- el.destroy();
- });
- this.fields = {};
- this.fields_order = [];
- this.default_focus_field = null;
- this.default_focus_button = null;
- this.translatable_fields = [];
- this.$el.find('.oe_form_container').empty();
- },
-
widgetFocused: function() {
// Clear click flag if used to focus a widget
this.__clicked_inside = false;
@@ -368,13 +320,6 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
this._actualize_mode();
this.set({ 'title' : record.id ? record.display_name : "New" });
- if (this.qweb) {
- this.kill_current_form();
- this.rendering_engine.set_fields_view(this.get_fvg_from_qweb(record));
- var $dest = this.$el.hasClass("oe_form_container") ? this.$el : this.$el.find('.oe_form_container');
- this.rendering_engine.render_to($dest);
- }
-
_(this.fields).each(function (field, f) {
field._dirty_flag = false;
field._inhibit_on_change_flag = true;
@@ -395,6 +340,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
});
}
self.on_form_changed();
+ self.rendering_engine.init_fields();
self.is_initialized.resolve();
self.do_update_pager(record.id == null);
if (self.sidebar) {
@@ -738,7 +684,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
}
for (var i = 0; i < fields_order.length; i += 1) {
var field = this.fields[fields_order[i]];
- if (!field.get('effective_invisible') && !field.get('effective_readonly')) {
+ if (!field.get('effective_invisible') && !field.get('effective_readonly') && field.$label) {
if (field.focus() !== false) {
break;
}
@@ -845,7 +791,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
if (!first_invalid_field) {
first_invalid_field = f;
}
- } else if (f.name !== 'id' && (!self.datarecord.id || (!f.get("readonly") && f._dirty_flag))) {
+ } else if (f.name !== 'id' && !f.get("readonly") && (!self.datarecord.id || f._dirty_flag)) {
// Special case 'id' field, do not save this field
// on 'create' : save all non readonly fields
// on 'edit' : save non readonly modified fields
@@ -1037,6 +983,16 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
},
open_defaults_dialog: function () {
var self = this;
+ var display = function (field, value) {
+ if (field instanceof instance.web.form.FieldSelection) {
+ return _(field.values).find(function (option) {
+ return option[0] === value;
+ })[1];
+ } else if (field instanceof instance.web.form.FieldMany2One) {
+ return field.get_displayed();
+ }
+ return value;
+ }
var fields = _.chain(this.fields)
.map(function (field, name) {
var value = field.get_value();
@@ -1050,29 +1006,28 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
|| field.field.type === 'binary') {
return false;
}
- var displayed = value;
- if (field instanceof instance.web.form.FieldSelection) {
- displayed = _(field.values).find(function (option) {
- return option[0] === value;
- })[1];
- } else if (field instanceof instance.web.form.FieldMany2One) {
- displayed = field.get_displayed();
- }
return {
name: name,
string: field.string,
value: value,
- displayed: displayed,
- // convert undefined to false
- change_default: !!field.field.change_default
+ displayed: display(field, value),
}
})
.compact()
.sortBy(function (field) { return field.string; })
.value();
- var conditions = _.chain(fields)
- .filter(function (field) { return field.change_default; })
+ var conditions = _.chain(self.fields)
+ .filter(function (field) { return field.field.change_default; })
+ .map(function (field, name) {
+ var value = field.get_value();
+ return {
+ name: name,
+ string: field.string,
+ value: value,
+ displayed: display(field, value),
+ }
+ })
.value();
var d = new instance.web.Dialog(this, {
@@ -1228,7 +1183,9 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt
this.$form.appendTo(this.$target);
- var ws = _.map(this.fields_to_init, function($elem) {
+ this.to_replace = [];
+
+ _.each(this.fields_to_init, function($elem) {
var name = $elem.attr("name");
if (!self.fvg.fields[name]) {
throw new Error("Field '" + name + "' specified in view could not be found.");
@@ -1244,24 +1201,28 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt
}
self.alter_field(w);
self.view.register_field(w, $elem.attr("name"));
- return [w, $elem];
- });
- _.each(ws, function(w) {
- w[0].replace(w[1]);
+ self.to_replace.push([w, $elem]);
});
_.each(this.tags_to_init, function($elem) {
var tag_name = $elem[0].tagName.toLowerCase();
var obj = self.tags_registry.get_object(tag_name);
var w = new (obj)(self.view, instance.web.xml_to_json($elem[0]));
- w.replace($elem);
+ self.to_replace.push([w, $elem]);
});
_.each(this.widgets_to_init, function($elem) {
var widget_type = $elem.attr("type");
var obj = self.widgets_registry.get_object(widget_type);
var w = new (obj)(self.view, instance.web.xml_to_json($elem[0]));
- w.replace($elem);
+ self.to_replace.push([w, $elem]);
+ });
+ },
+ init_fields: function() {
+ var defs = [];
+ _.each(this.to_replace, function(el) {
+ defs.push(el[0].replace(el[1]));
});
- // TODO: return a deferred
+ this.to_replace = [];
+ return $.when.apply($, defs);
},
render_element: function(template /* dictionaries */) {
var dicts = [].slice.call(arguments).slice(1);
@@ -2010,12 +1971,9 @@ instance.web.form.FieldInterface = {
/**
* Called by the form view to indicate the value of the field.
*
- * set_value() may return an object that can be passed to $.when() that represents the moment when
- * the field has finished all operations necessary before the user can effectively use the widget.
- *
* Multiple calls to set_value() can occur at any time and must be handled correctly by the implementation,
- * regardless of any asynchronous operation currently running and the status of any promise that a
- * previous call to set_value() could have returned.
+ * regardless of any asynchronous operation currently running. Calls to set_value() can and will also occur
+ * before the widget is inserted into the DOM.
*
* set_value() must be able, at any moment, to handle the syntax returned by the "read" method of the
* osv class in the OpenERP server as well as the syntax used by the set_value() (see below). It must
@@ -2125,6 +2083,14 @@ instance.web.form.AbstractField = instance.web.form.FormWidget.extend(instance.w
this.field_manager.on("change:display_invalid_fields", this, this._check_css_flags);
this._check_css_flags();
},
+ start: function() {
+ var tmp = this._super();
+ this.on("change:value", this, function() {
+ if (! this.no_rerender)
+ this.render_value();
+ });
+ this.render_value();
+ },
/**
* Private. Do not use.
*/
@@ -2137,6 +2103,20 @@ instance.web.form.AbstractField = instance.web.form.FormWidget.extend(instance.w
get_value: function() {
return this.get('value');
},
+ /**
+ Utility method that all implementations should use to change the
+ value without triggering a re-rendering.
+ */
+ internal_set_value: function(value_) {
+ var tmp = this.no_render;
+ this.no_rerender = true;
+ this.set({'value': value_});
+ this.no_rerender = tmp;
+ },
+ /**
+ This method is called each time the value is modified.
+ */
+ render_value: function() {},
is_valid: function() {
return this.is_syntax_valid() && !(this.get('required') && this.is_false());
},
@@ -2173,6 +2153,20 @@ instance.web.form.AbstractField = instance.web.form.FormWidget.extend(instance.w
self.do_action(r);
});
},
+
+ set_dimensions: function (height, width) {
+ // remove width css property
+ this.$el.css('width', '');
+ // extract style (without width)
+ var old_style = this.$el.attr('style');
+ // jQuery doesn't understand/use !important
+ var style = 'width:' + width + 'px !important;';
+ if (old_style) {
+ style += old_style
+ }
+ this.$el.attr('style', style);
+ this.$el.css('minHeight', height);
+ }
});
/**
@@ -2181,11 +2175,11 @@ instance.web.form.AbstractField = instance.web.form.FormWidget.extend(instance.w
*/
instance.web.form.ReinitializeWidgetMixin = {
/**
- * Default implementation of start(), use it or call explicitly initialize_field().
+ * Default implementation of, you should not override it, use initialize_field() instead.
*/
start: function() {
- this._super();
this.initialize_field();
+ this._super();
},
initialize_field: function() {
this.on("change:effective_readonly", this, this.reinitialize);
@@ -2212,18 +2206,10 @@ instance.web.form.ReinitializeWidgetMixin = {
* switch.
*/
instance.web.form.ReinitializeFieldMixin = _.extend({}, instance.web.form.ReinitializeWidgetMixin, {
- initialize_field: function() {
- instance.web.form.ReinitializeWidgetMixin.initialize_field.call(this);
- this.render_value();
- },
reinitialize: function() {
instance.web.form.ReinitializeWidgetMixin.reinitialize.call(this);
this.render_value();
},
- /**
- * Called to render the value. Should also be explicitly called at the end of a set_value().
- */
- render_value: function() {},
});
instance.web.form.FieldChar = instance.web.form.AbstractField.extend(instance.web.form.ReinitializeFieldMixin, {
@@ -2237,14 +2223,10 @@ instance.web.form.FieldChar = instance.web.form.AbstractField.extend(instance.we
var self = this;
var $input = this.$el.find('input');
$input.change(function() {
- self.set({'value': self.parse_value($input.val())});
+ self.internal_set_value(self.parse_value($input.val()));
});
this.setupFocus($input);
},
- set_value: function(value_) {
- this._super(value_);
- this.render_value();
- },
render_value: function() {
var show_value = this.format_value(this.get('value'), '');
if (!this.get("effective_readonly")) {
@@ -2257,7 +2239,7 @@ instance.web.form.FieldChar = instance.web.form.AbstractField.extend(instance.we
}
},
is_syntax_valid: function() {
- if (!this.get("effective_readonly")) {
+ if (!this.get("effective_readonly") && this.$("input").size() > 0) {
try {
var value_ = this.parse_value(this.$el.find('input').val(), '');
return true;
@@ -2348,7 +2330,7 @@ instance.web.form.FieldFloat = instance.web.form.FieldChar.extend({
widget_class: 'oe_form_field_float',
init: function (field_manager, node) {
this._super(field_manager, node);
- this.set({'value': 0});
+ this.internal_set_value(0);
if (this.node.attrs.digits) {
this.digits = this.node.attrs.digits;
} else {
@@ -2478,16 +2460,12 @@ instance.web.form.FieldDatetime = instance.web.form.AbstractField.extend(instanc
if (!this.get("effective_readonly")) {
this.datewidget = this.build_widget();
this.datewidget.on('datetime_changed', this, _.bind(function() {
- this.set({'value': this.datewidget.get_value()});
+ this.internal_set_value(this.datewidget.get_value());
}, this));
this.datewidget.appendTo(this.$el);
this.setupFocus(this.datewidget.$input);
}
},
- set_value: function(value_) {
- this._super(value_);
- this.render_value();
- },
render_value: function() {
if (!this.get("effective_readonly")) {
this.datewidget.set_value(this.get('value'));
@@ -2496,7 +2474,7 @@ instance.web.form.FieldDatetime = instance.web.form.AbstractField.extend(instanc
}
},
is_syntax_valid: function() {
- if (!this.get("effective_readonly")) {
+ if (!this.get("effective_readonly") && this.datewidget) {
return this.datewidget.is_valid_();
}
return true;
@@ -2529,7 +2507,7 @@ instance.web.form.FieldText = instance.web.form.AbstractField.extend(instance.we
this.default_height = this.$textarea.css('height');
if (!this.get("effective_readonly")) {
this.$textarea.change(_.bind(function() {
- self.set({'value': instance.web.parse_value(self.$textarea.val(), self)});
+ self.internal_set_value(instance.web.parse_value(self.$textarea.val(), self));
}, this));
} else {
this.$textarea.attr('disabled', 'disabled');
@@ -2541,12 +2519,8 @@ instance.web.form.FieldText = instance.web.form.AbstractField.extend(instance.we
});
this.setupFocus(this.$textarea);
},
- set_value: function(value_) {
- this._super(value_);
- this.render_value();
- $(window).resize();
- },
render_value: function() {
+ $(window).resize();
var show_value = instance.web.format_value(this.get('value'), this, '');
if (show_value === '') {
this.$textarea.css('height', parseInt(this.default_height)+"px");
@@ -2555,7 +2529,7 @@ instance.web.form.FieldText = instance.web.form.AbstractField.extend(instance.we
this.$textarea.autosize();
},
is_syntax_valid: function() {
- if (!this.get("effective_readonly")) {
+ if (!this.get("effective_readonly") && this.$textarea) {
try {
var value_ = instance.web.parse_value(this.$textarea.val(), this, '');
return true;
@@ -2571,6 +2545,13 @@ instance.web.form.FieldText = instance.web.form.AbstractField.extend(instance.we
focus: function($el) {
this.$textarea.focus();
},
+ set_dimensions: function (height, width) {
+ this._super();
+ this.$textarea.css({
+ width: width,
+ minHeight: height
+ });
+ },
});
/**
@@ -2606,15 +2587,11 @@ instance.web.form.FieldTextHtml = instance.web.form.AbstractField.extend(instanc
this.$cleditor.change(function() {
if (! self._updating_editor) {
self.$cleditor.updateTextArea();
- self.set({'value': self.$textarea.val()});
+ self.internal_set_value(self.$textarea.val());
}
});
}
},
- set_value: function(value_) {
- this._super.apply(this, arguments);
- this.render_value();
- },
render_value: function() {
if (! this.get("effective_readonly")) {
this.$textarea.val(this.get('value') || '');
@@ -2631,21 +2608,20 @@ instance.web.form.FieldBoolean = instance.web.form.AbstractField.extend({
template: 'FieldBoolean',
start: function() {
var self = this;
- this._super.apply(this, arguments);
this.$checkbox = $("input", this.$el);
this.setupFocus(this.$checkbox);
this.$el.click(_.bind(function() {
- this.set({'value': this.$checkbox.is(':checked')});
+ this.internal_set_value(this.$checkbox.is(':checked'));
}, this));
var check_readonly = function() {
self.$checkbox.prop('disabled', self.get("effective_readonly"));
};
this.on("change:effective_readonly", this, check_readonly);
check_readonly.call(this);
- },
- set_value: function(value_) {
this._super.apply(this, arguments);
- this.$checkbox[0].checked = value_;
+ },
+ render_value: function() {
+ this.$checkbox[0].checked = this.get('value');
},
focus: function() {
this.$checkbox.focus();
@@ -2661,9 +2637,8 @@ instance.web.form.FieldProgressBar = instance.web.form.AbstractField.extend({
disabled: this.get("effective_readonly")
});
},
- set_value: function(value_) {
- this._super.apply(this, arguments);
- var show_value = Number(value_);
+ render_value: function() {
+ var show_value = Number(this.get('value'));
if (isNaN(show_value)) {
show_value = 0;
}
@@ -2701,7 +2676,7 @@ instance.web.form.FieldSelection = instance.web.form.AbstractField.extend(instan
var ischanging = false;
var $select = this.$el.find('select')
.change(_.bind(function() {
- this.set({'value': this.values[this.$el.find('select')[0].selectedIndex][0]});
+ this.internal_set_value(this.values[this.$el.find('select')[0].selectedIndex][0]);
}, this))
.change(function () { ischanging = true; })
.click(function () { ischanging = false; })
@@ -2716,7 +2691,6 @@ instance.web.form.FieldSelection = instance.web.form.AbstractField.extend(instan
value_ = value_ === null ? false : value_;
value_ = value_ instanceof Array ? value_[0] : value_;
this._super(value_);
- this.render_value();
},
render_value: function() {
if (!this.get("effective_readonly")) {
@@ -2732,13 +2706,6 @@ instance.web.form.FieldSelection = instance.web.form.AbstractField.extend(instan
this.$el.text(option ? option[1] : this.values[0][1]);
}
},
- is_syntax_valid: function() {
- if (this.get("effective_readonly")) {
- return true;
- }
- var value_ = this.values[this.$el.find('select')[0].selectedIndex];
- return !! value_;
- },
focus: function() {
this.$el.find('select:first').focus();
}
@@ -2936,26 +2903,27 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc
this.display_value = {};
this.last_search = [];
this.floating = false;
- this.inhibit_on_change = false;
this.current_display = null;
+ this.is_started = false;
},
- start: function() {
- this._super();
- instance.web.form.ReinitializeFieldMixin.start.call(this);
- this.on("change:value", this, function() {
- this.floating = false;
+ reinit_value: function(val) {
+ this.internal_set_value(val);
+ this.floating = false;
+ if (this.is_started)
this.render_value();
- });
+ },
+ initialize_field: function() {
+ this.is_started = true;
instance.web.bus.on('click', this, function() {
if (!this.get("effective_readonly") && this.$input && this.$input.autocomplete('widget').is(':visible')) {
this.$input.autocomplete("close");
}
});
+ instance.web.form.ReinitializeFieldMixin.initialize_field.call(this);
},
initialize_content: function() {
if (!this.get("effective_readonly"))
this.render_editable();
- this.render_value();
},
init_error_displayer: function() {
// nothing
@@ -3006,7 +2974,7 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc
if (self.current_display !== self.$input.val()) {
self.current_display = self.$input.val();
if (self.$input.val() === "") {
- self.set({value: false});
+ self.internal_set_value(false);
self.floating = false;
} else {
self.floating = true;
@@ -3038,15 +3006,14 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc
if (self.last_search[0][0] != self.get("value")) {
self.display_value = {};
self.display_value["" + self.last_search[0][0]] = self.last_search[0][1];
- self.set({value: self.last_search[0][0]});
+ self.reinit_value(self.last_search[0][0]);
} else {
used = true;
self.render_value();
}
} else {
used = true;
- self.set({value: false});
- self.render_value();
+ self.reinit_value(false);
}
self.floating = false;
}
@@ -3101,7 +3068,7 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc
if (item.id) {
self.display_value = {};
self.display_value["" + item.id] = item.name;
- self.set({value: item.id});
+ self.reinit_value(item.id);
} else if (item.action) {
item.action();
// Cancel widget blurring, to avoid form blur event
@@ -3192,22 +3159,22 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc
value_ = value_[0];
}
value_ = value_ || false;
- this.inhibit_on_change = true;
- this._super(value_);
- this.inhibit_on_change = false;
+ this.reinit_value(value_);
},
get_displayed: function() {
return this.display_value["" + this.get("value")];
},
add_id: function(id) {
this.display_value = {};
- this.set({value: id});
+ this.reinit_value(id);
},
is_false: function() {
return ! this.get("value");
},
focus: function () {
- this.$input.focus();
+ if (!this.get('effective_readonly')) {
+ this.$input.focus();
+ }
},
_quick_create: function() {
this.no_ed = true;
@@ -3274,27 +3241,26 @@ instance.web.form.FieldOne2Many = instance.web.form.AbstractField.extend({
lazy_build_o2m_kanban_view();
this.is_loaded = $.Deferred();
this.initial_is_loaded = this.is_loaded;
- this.is_setted = $.Deferred();
this.form_last_update = $.Deferred();
this.init_form_last_update = this.form_last_update;
- },
- start: function() {
- this._super.apply(this, arguments);
- this.$el.addClass('oe_form_field oe_form_field_one2many');
-
- var self = this;
-
+ this.is_started = false;
this.dataset = new instance.web.form.One2ManyDataSet(this, this.field.relation);
this.dataset.o2m = this;
this.dataset.parent_view = this.view;
this.dataset.child_name = this.name;
+ var self = this;
this.dataset.on('dataset_changed', this, function() {
self.trigger_on_change();
});
+ this.set_value([]);
+ },
+ start: function() {
+ this._super.apply(this, arguments);
+ this.$el.addClass('oe_form_field oe_form_field_one2many');
- this.is_setted.then(function() {
- self.load_views();
- });
+ var self = this;
+
+ self.load_views();
this.is_loaded.then(function() {
self.on("change:effective_readonly", self, function() {
self.is_loaded = self.is_loaded.pipe(function() {
@@ -3305,6 +3271,8 @@ instance.web.form.FieldOne2Many = instance.web.form.AbstractField.extend({
});
});
});
+ this.is_started = true;
+ this.reload_current_view();
},
trigger_on_change: function() {
var tmp = this.doing_on_change;
@@ -3406,10 +3374,8 @@ instance.web.form.FieldOne2Many = instance.web.form.AbstractField.extend({
}
});
});
- this.is_setted.then(function() {
- $.async_when().then(function () {
- self.viewmanager.appendTo(self.$el);
- });
+ $.async_when().then(function () {
+ self.viewmanager.appendTo(self.$el);
});
return def;
},
@@ -3489,9 +3455,12 @@ instance.web.form.FieldOne2Many = instance.web.form.AbstractField.extend({
if (this.dataset.index === null && this.dataset.ids.length > 0) {
this.dataset.index = 0;
}
- self.is_setted.resolve();
this.trigger_on_change();
- return self.reload_current_view();
+ if (this.is_started) {
+ return self.reload_current_view();
+ } else {
+ return $.when();
+ }
},
get_value: function() {
var self = this;
@@ -3857,7 +3826,7 @@ instance.web.form.One2ManyList = instance.web.ListView.List.extend({
instance.web.form.One2ManyFormView = instance.web.FormView.extend({
form_template: 'One2Many.formview',
- on_loaded: function(data) {
+ load_form: function(data) {
this._super(data);
var self = this;
this.$buttons.find('button.oe_form_button_create').click(function() {
@@ -3889,11 +3858,6 @@ instance.web.form.FieldMany2ManyTags = instance.web.form.AbstractField.extend(in
this._display_orderer = new instance.web.DropMisordered();
this._drop_shown = false;
},
- start: function() {
- this._super();
- instance.web.form.ReinitializeFieldMixin.start.call(this);
- this.on("change:value", this, this.render_value);
- },
initialize_content: function() {
if (this.get("effective_readonly"))
return;
@@ -4015,36 +3979,33 @@ instance.web.form.FieldMany2Many = instance.web.form.AbstractField.extend({
disable_utility_classes: true,
init: function(field_manager, node) {
this._super(field_manager, node);
- this.set({"value": []});
this.is_loaded = $.Deferred();
this.initial_is_loaded = this.is_loaded;
- this.is_setted = $.Deferred();
- },
- start: function() {
- this._super.apply(this, arguments);
- this.$el.addClass('oe_form_field oe_form_field_many2many');
-
- var self = this;
-
this.dataset = new instance.web.form.Many2ManyDataSet(this, this.field.relation);
this.dataset.m2m = this;
+ var self = this;
this.dataset.on('unlink', self, function(ids) {
self.dataset_changed();
});
+ this.set_value([]);
+ },
+ start: function() {
+ this.$el.addClass('oe_form_field oe_form_field_many2many');
- this.is_setted.then(function() {
- self.load_view();
- });
+ var self = this;
+
+ self.load_view();
this.is_loaded.then(function() {
self.on("change:effective_readonly", self, function() {
self.is_loaded = self.is_loaded.pipe(function() {
self.list_view.destroy();
return $.when(self.load_view()).then(function() {
- self.reload_content();
+ self.render_value();
});
});
});
});
+ this._super.apply(this, arguments);
},
set_value: function(value_) {
value_ = value_ || [];
@@ -4052,17 +4013,12 @@ instance.web.form.FieldMany2Many = instance.web.form.AbstractField.extend({
value_ = value_[0][2];
}
this._super(value_);
- this.dataset.set_ids(value_);
- var self = this;
- self.reload_content();
- this.is_setted.resolve();
},
get_value: function() {
return [commands.replace_with(this.get('value'))];
},
-
is_false: function () {
- return _(this.dataset.ids).isEmpty();
+ return _(this.get("value")).isEmpty();
},
load_view: function() {
var self = this;
@@ -4080,7 +4036,7 @@ instance.web.form.FieldMany2Many = instance.web.form.AbstractField.extend({
}
this.list_view.m2m_field = this;
var loaded = $.Deferred();
- this.list_view.on_loaded.add_last(function() {
+ this.list_view.on("list_view_loaded", self, function() {
self.initial_is_loaded.resolve();
loaded.resolve();
});
@@ -4089,14 +4045,15 @@ instance.web.form.FieldMany2Many = instance.web.form.AbstractField.extend({
});
return loaded;
},
- reload_content: function() {
+ render_value: function() {
var self = this;
+ this.dataset.set_ids(this.get("value"));
this.is_loaded = this.is_loaded.pipe(function() {
return self.list_view.reload_content();
});
},
dataset_changed: function() {
- this.set({'value': this.dataset.ids});
+ this.internal_set_value(this.dataset.ids);
},
});
@@ -4124,13 +4081,17 @@ instance.web.form.Many2ManyListView = instance.web.ListView.extend(/** @lends in
);
var self = this;
pop.on("elements_selected", self, function(element_ids) {
- _.each(element_ids, function(one_id) {
- if(! _.detect(self.dataset.ids, function(x) {return x == one_id;})) {
- self.dataset.set_ids([].concat(self.dataset.ids, [one_id]));
+ var reload = false;
+ _(element_ids).each(function (id) {
+ if(! _.detect(self.dataset.ids, function(x) {return x == id;})) {
+ self.dataset.set_ids(self.dataset.ids.concat([id]));
self.m2m_field.dataset_changed();
- self.reload_content();
+ reload = true;
}
});
+ if (reload) {
+ self.reload_content();
+ }
});
},
do_activate_record: function(index, id) {
@@ -4152,28 +4113,26 @@ instance.web.form.FieldMany2ManyKanban = instance.web.form.AbstractField.extend(
m2m_kanban_lazy_init();
this.is_loaded = $.Deferred();
this.initial_is_loaded = this.is_loaded;
- this.is_setted = $.Deferred();
- },
- start: function() {
- this._super.apply(this, arguments);
var self = this;
-
this.dataset = new instance.web.form.Many2ManyDataSet(this, this.field.relation);
this.dataset.m2m = this;
this.dataset.on('unlink', self, function(ids) {
self.dataset_changed();
});
+ },
+ start: function() {
+ this._super.apply(this, arguments);
- this.is_setted.then(function() {
- self.load_view();
- });
+ var self = this;
+
+ self.load_view();
this.is_loaded.then(function() {
self.on("change:effective_readonly", self, function() {
self.is_loaded = self.is_loaded.pipe(function() {
self.kanban_view.destroy();
return $.when(self.load_view()).then(function() {
- self.reload_content();
+ self.render_value();
});
});
});
@@ -4185,10 +4144,6 @@ instance.web.form.FieldMany2ManyKanban = instance.web.form.AbstractField.extend(
value_ = value_[0][2];
}
this._super(value_);
- this.dataset.set_ids(value_);
- var self = this;
- self.reload_content();
- this.is_setted.resolve();
},
load_view: function() {
var self = this;
@@ -4205,7 +4160,7 @@ instance.web.form.FieldMany2ManyKanban = instance.web.form.AbstractField.extend(
}
this.kanban_view.m2m = this;
var loaded = $.Deferred();
- this.kanban_view.on_loaded.add_last(function() {
+ this.kanban_view.on("kanban_view_loaded",self,function() {
self.initial_is_loaded.resolve();
loaded.resolve();
});
@@ -4215,8 +4170,9 @@ instance.web.form.FieldMany2ManyKanban = instance.web.form.AbstractField.extend(
});
return loaded;
},
- reload_content: function() {
+ render_value: function() {
var self = this;
+ this.dataset.set_ids(this.get("value"));
this.is_loaded = this.is_loaded.pipe(function() {
return self.kanban_view.do_search(self.build_domain(), self.dataset.get_context(), []);
});
@@ -4243,7 +4199,7 @@ instance.web.form.FieldMany2ManyKanban = instance.web.form.AbstractField.extend(
if(! _.detect(self.dataset.ids, function(x) {return x == one_id;})) {
self.dataset.set_ids([].concat(self.dataset.ids, [one_id]));
self.dataset_changed();
- self.reload_content();
+ self.render_value();
}
});
});
@@ -4254,7 +4210,7 @@ instance.web.form.FieldMany2ManyKanban = instance.web.form.AbstractField.extend(
title: _t("Open: ") + self.string,
write_function: function(id, data, options) {
return self.dataset.write(id, data, {}).then(function() {
- self.reload_content();
+ self.render_value();
});
},
alternative_form_view: self.field.views ? self.field.views["form"] : undefined,
@@ -4404,9 +4360,8 @@ instance.web.form.AbstractFormPopup = instance.web.Widget.extend({
self.check_exit(true);
},
title: this.options.title || "",
- buttons: [{text:"tmp"}],
}, this.$el).open();
- this.$buttonpane = dialog.$el.dialog("widget").find(".ui-dialog-buttonpane").html("");
+ this.$buttonpane = dialog.$buttons;
this.start();
},
setup_form_view: function() {
@@ -4429,7 +4384,7 @@ instance.web.form.AbstractFormPopup = instance.web.Widget.extend({
this.view_form.set_embedded_view(this.options.alternative_form_view);
}
this.view_form.appendTo(this.$el.find(".oe_popup_form"));
- this.view_form.on_loaded.add_last(function() {
+ this.view_form.on("form_view_loaded", self, function() {
var multi_select = self.row_id === null && ! self.options.disable_multiple_selection;
self.$buttonpane.html(QWeb.render("AbstractFormPopup.buttons", {
multi_select: multi_select,
@@ -4540,7 +4495,7 @@ instance.web.form.SelectCreatePopup = instance.web.form.AbstractFormPopup.extend
}
this.searchview = new instance.web.SearchView(this,
this.dataset, false, search_defaults);
- this.searchview.on_search.add(function(domains, contexts, groupbys) {
+ this.searchview.on('search_data', self, function(domains, contexts, groupbys) {
if (self.initial_ids) {
self.do_search(domains.concat([[["id", "in", self.initial_ids]], self.domain]),
contexts, groupbys);
@@ -4549,7 +4504,7 @@ instance.web.form.SelectCreatePopup = instance.web.form.AbstractFormPopup.extend
self.do_search(domains.concat([self.domain]), contexts.concat(self.context), groupbys);
}
});
- this.searchview.on_loaded.add_last(function () {
+ this.searchview.on("search_view_loaded", self, function() {
self.view_list = new instance.web.form.SelectCreateListView(self,
self.dataset, false,
_.extend({'deletable': false,
@@ -4566,7 +4521,7 @@ instance.web.form.SelectCreatePopup = instance.web.form.AbstractFormPopup.extend
}).pipe(function() {
self.searchview.do_search();
});
- self.view_list.on_loaded.add_last(function() {
+ self.view_list.on("list_view_loaded", self, function() {
self.$buttonpane.html(QWeb.render("SelectCreatePopup.search.buttons", {widget:self}));
var $cbutton = self.$buttonpane.find(".oe_selectcreatepopup-search-close");
$cbutton.click(function() {
@@ -4684,10 +4639,6 @@ instance.web.form.FieldReference = instance.web.form.AbstractField.extend(instan
is_false: function() {
return typeof(this.get_value()) !== 'string';
},
- set_value: function(value_) {
- this._super(value_);
- this.render_value();
- },
render_value: function() {
this.reference_ready = false;
var vals = [], sel_val, m2o_val;
@@ -4707,9 +4658,9 @@ instance.web.form.FieldReference = instance.web.form.AbstractField.extend(instan
var model = this.selection.get_value(),
id = this.m2o.get_value();
if (typeof(model) === 'string' && typeof(id) === 'number') {
- this.set({'value': model + ',' + id});
+ this.internal_set_value(model + ',' + id);
} else {
- this.set({'value': false});
+ this.internal_set_value(false);
}
},
});
@@ -4719,14 +4670,19 @@ instance.web.form.FieldBinary = instance.web.form.AbstractField.extend(instance.
var self = this;
this._super(field_manager, node);
this.binary_value = false;
- this.fileupload_id = _.uniqueId('oe_fileupload');
- $(window).on(this.fileupload_id, function() {
- var args = [].slice.call(arguments).slice(1);
- self.on_file_uploaded.apply(self, args);
- });
+ this.useFileAPI = !!window.FileReader;
+ if (!this.useFileAPI) {
+ this.fileupload_id = _.uniqueId('oe_fileupload');
+ $(window).on(this.fileupload_id, function() {
+ var args = [].slice.call(arguments).slice(1);
+ self.on_file_uploaded.apply(self, args);
+ });
+ }
},
stop: function() {
- $(window).off(this.fileupload_id);
+ if (!this.useFileAPI) {
+ $(window).off(this.fileupload_id);
+ }
this._super.apply(this, arguments);
},
initialize_content: function() {
@@ -4744,13 +4700,22 @@ instance.web.form.FieldBinary = instance.web.form.AbstractField.extend(instance.
return size.toFixed(2) + ' ' + units[i];
},
on_file_change: function(e) {
- // TODO: on modern browsers, we could directly read the file locally on client ready to be used on image cropper
- // http://www.html5rocks.com/tutorials/file/dndfiles/
- // http://deepliquid.com/projects/Jcrop/demos.php?demo=handler
-
- if ($(e.target).val() !== '') {
- this.$el.find('form.oe_form_binary_form input[name=session_id]').val(this.session.session_id);
- this.$el.find('form.oe_form_binary_form').submit();
+ var self = this;
+ var file_node = e.target;
+ if ((this.useFileAPI && file_node.files.length) || (!this.useFileAPI && $(file_node).val() !== '')) {
+ if (this.useFileAPI) {
+ var file = file_node.files[0];
+ var filereader = new FileReader();
+ filereader.readAsDataURL(file);
+ filereader.onloadend = function(upload) {
+ var data = upload.target.result;
+ data = data.split(',')[1];
+ self.on_file_uploaded(file.size, file.name, file.type, data);
+ };
+ } else {
+ this.$el.find('form.oe_form_binary_form input[name=session_id]').val(this.session.session_id);
+ this.$el.find('form.oe_form_binary_form').submit();
+ }
this.$el.find('.oe_form_binary_progress').show();
this.$el.find('.oe_form_binary').hide();
}
@@ -4808,7 +4773,7 @@ instance.web.form.FieldBinary = instance.web.form.AbstractField.extend(instance.
on_clear: function() {
if (this.get('value') !== false) {
this.binary_value = false;
- this.set({'value': false});
+ this.internal_set_value(false);
}
return false;
}
@@ -4828,10 +4793,6 @@ instance.web.form.FieldBinaryFile = instance.web.form.FieldBinary.extend({
});
}
},
- set_value: function(value_) {
- this._super.apply(this, arguments);
- this.render_value();
- },
render_value: function() {
if (!this.get("effective_readonly")) {
var show_value;
@@ -4853,7 +4814,7 @@ instance.web.form.FieldBinaryFile = instance.web.form.FieldBinary.extend({
},
on_file_uploaded_and_valid: function(size, name, content_type, file_base64) {
this.binary_value = true;
- this.set({'value': file_base64});
+ this.internal_set_value(file_base64);
var show_value = name + " (" + this.human_filesize(size) + ")";
this.$el.find('input').eq(0).val(show_value);
this.set_filename(name);
@@ -4867,10 +4828,6 @@ instance.web.form.FieldBinaryFile = instance.web.form.FieldBinary.extend({
instance.web.form.FieldBinaryImage = instance.web.form.FieldBinary.extend({
template: 'FieldBinaryImage',
- set_value: function(value_) {
- this._super.apply(this, arguments);
- this.render_value();
- },
render_value: function() {
var self = this;
var url;
@@ -4903,7 +4860,7 @@ instance.web.form.FieldBinaryImage = instance.web.form.FieldBinary.extend({
this._super.apply(this, arguments);
},
on_file_uploaded_and_valid: function(size, name, content_type, file_base64) {
- this.set({'value': file_base64});
+ this.internal_set_value(file_base64);
this.binary_value = true;
this.render_value();
this.set_filename(name);
@@ -4921,38 +4878,34 @@ instance.web.form.FieldStatus = instance.web.form.AbstractField.extend({
this._super(field_manager, node);
this.options.clickable = this.options.clickable || (this.node.attrs || {}).clickable || false;
this.options.visible = this.options.visible || (this.node.attrs || {}).statusbar_visible || false;
- this.selected_value = null;
+ this.set({value: false});
},
start: function() {
- this._super();
- // backward compatibility
- this.loaded = new $.Deferred();
if (this.options.clickable) {
this.$el.on('click','li',this.on_click_stage);
}
- // TODO move the following into css :after
if (this.$el.parent().is('header')) {
this.$el.after('<div class="oe_clear"/>');
}
+ this._super();
},
set_value: function(value_) {
- var self = this;
- this._super(value_);
- // find selected value:
- // - many2one: [2, "New"] -> 2
- // - selection: new -> new
- if (this.field.type == "many2one") {
- this.selected_value = value_[0];
- } else {
- this.selected_value = value_;
+ if (value_ instanceof Array) {
+ value_ = value_[0];
}
- // trick to be sure all values are loaded in the form, therefore
- // enabling the evaluation of dynamic domains
- self.selection = [];
- $.async_when().then(function() {
- self.get_selection();
+ this._super(value_);
+ },
+ render_value: function() {
+ var self = this;
+ self.get_selection().then(function() {
+ var content = QWeb.render("FieldStatus.content", {widget: self});
+ self.$el.html(content);
+ var colors = JSON.parse((self.node.attrs || {}).statusbar_colors || "{}");
+ var color = colors[self.get('value')];
+ if (color) {
+ self.$("oe_active").css("color", color);
+ }
});
- return this.loaded;
},
/** Get the selection and render it
* selection: [[identifier, value_to_display], ...]
@@ -4961,53 +4914,37 @@ instance.web.form.FieldStatus = instance.web.form.AbstractField.extend({
*/
get_selection: function() {
var self = this;
+ self.selection = [];
if (this.field.type == "many2one") {
var domain = [];
if(!_.isEmpty(this.field.domain) || !_.isEmpty(this.node.attrs.domain)) {
- domain = new instance.web.CompoundDomain(['|'], self.build_domain(), [['id', '=', self.selected_value]]);
+ domain = new instance.web.CompoundDomain(['|'], self.build_domain(), [['id', '=', self.get('value')]]);
}
var ds = new instance.web.DataSetSearch(this, this.field.relation, self.build_context(), domain);
- ds.read_slice(['name'], {}).done( function (records) {
+ return ds.read_slice(['name'], {}).pipe(function (records) {
for(var i = 0; i < records.length; i++) {
self.selection.push([records[i].id, records[i].name]);
}
- self.render_elements();
- self.loaded.resolve();
});
} else {
- this.loaded.resolve();
// For field type selection filter values according to
// statusbar_visible attribute of the field. For example:
// statusbar_visible="draft,open".
var selection = this.field.selection;
- for(var i=0; i< selection.length; i++) {
+ for(var i=0; i < selection.length; i++) {
var key = selection[i][0];
- if(key == this.selected_value || !this.options.visible || this.options.visible.indexOf(key) != -1) {
+ if(key == this.get('value') || !this.options.visible || this.options.visible.indexOf(key) != -1) {
this.selection.push(selection[i]);
}
}
- this.render_elements();
- }
- },
- /** Renders the widget. This function also checks for statusbar_colors='{"pending": "blue"}'
- * attribute in the widget. This allows to set a given color to a given
- * state (given by the key of (key, label)).
- */
- render_elements: function () {
- var self = this;
- var content = instance.web.qweb.render("FieldStatus.content", {widget: this});
- this.$el.html(content);
- var colors = JSON.parse((this.node.attrs || {}).statusbar_colors || "{}");
- var color = colors[this.selected_value];
- if (color) {
- this.$("oe_active").css("color", color);
+ return $.when();
}
},
on_click_stage: function (ev) {
var self = this;
var $li = $(ev.currentTarget);
var val = parseInt($li.data("id"));
- if (val != self.selected_value) {
+ if (val != self.get('value')) {
this.view.recursive_save().then(function() {
var change = {};
change[self.name] = val;
diff --git a/addons/web/static/src/js/view_list.js b/addons/web/static/src/js/view_list.js
index 88ee428fba4..29bc81ea9ec 100644
--- a/addons/web/static/src/js/view_list.js
+++ b/addons/web/static/src/js/view_list.js
@@ -64,9 +64,9 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
this.set_groups(new (this.options.GroupsType)(this));
if (this.dataset instanceof instance.web.DataSetStatic) {
- this.groups.datagroup = new instance.web.StaticDataGroup(this.dataset);
+ this.groups.datagroup = new StaticDataGroup(this.dataset);
} else {
- this.groups.datagroup = new instance.web.DataGroup(
+ this.groups.datagroup = new DataGroup(
this, this.model,
dataset.get_domain(),
dataset.get_context());
@@ -83,6 +83,7 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
});
this.no_leaf = false;
+ this.on('view_load', self, self.load_list);
},
set_default_options: function (options) {
this._super(options);
@@ -218,7 +219,7 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
* @param {Object} data.fields_view.arch current list view descriptor
* @param {Boolean} grouped Is the list view grouped
*/
- on_loaded: function(data, grouped) {
+ load_list: function(data, grouped) {
var self = this;
this.fields_view = data;
this.name = "" + this.fields_view.arch.attrs.string;
@@ -358,6 +359,7 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
this.sidebar.add_toolbar(this.fields_view.toolbar);
this.sidebar.$el.hide();
}
+ this.trigger('list_view_loaded', data, grouped);
},
/**
* Configures the ListView pager based on the provided dataset's information
@@ -399,14 +401,10 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
*/
setup_columns: function (fields, grouped) {
var registry = instance.web.list.columns;
- var reorder = this.options.reorderable;
this.columns.splice(0, this.columns.length);
this.columns.push.apply(this.columns,
_(this.fields_view.arch.children).map(function (field) {
var id = field.attrs.name;
- if(field.attrs.widget == 'handle' && !reorder){
- field.attrs.reorderable = reorder || true;
- }
return registry.for_(id, fields[id], field);
}));
if (grouped) {
@@ -415,8 +413,9 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
}
this.visible_columns = _.filter(this.columns, function (column) {
- return column.invisible !== '1' && !column.reorderable;
+ return column.invisible !== '1';
});
+
this.aggregate_columns = _(this.visible_columns).invoke('to_aggregate');
},
/**
@@ -471,7 +470,7 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
reload_view: function (grouped, context, initial) {
var self = this;
var callback = function (field_view_get) {
- self.on_loaded(field_view_get, grouped);
+ self.load_list(field_view_get, grouped);
};
if (this.embedded_view) {
return $.Deferred().then(callback).resolve(this.embedded_view);
@@ -557,7 +556,7 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
*/
do_search: function (domain, context, group_by) {
this.page = 0;
- this.groups.datagroup = new instance.web.DataGroup(
+ this.groups.datagroup = new DataGroup(
this, this.model, domain, context, group_by);
this.groups.datagroup.sort = this.dataset._sort;
@@ -1153,7 +1152,7 @@ instance.web.ListView.Groups = instance.web.Class.extend( /** @lends instance.we
passtrough_events: 'action deleted row_link',
/**
* Grouped display for the ListView. Handles basic DOM events and interacts
- * with the :js:class:`~instance.web.DataGroup` bound to it.
+ * with the :js:class:`~DataGroup` bound to it.
*
* Provides events similar to those of
* :js:class:`~instance.web.ListView.List`
@@ -1233,6 +1232,7 @@ instance.web.ListView.Groups = instance.web.Class.extend( /** @lends instance.we
.replaceWith(self.render());
});
this.$row.children().last()
+ .addClass('oe_list_group_pagination')
.append($prev)
.append('<span class="oe_list_pager_state"></span>')
.append($next);
@@ -1321,7 +1321,7 @@ instance.web.ListView.Groups = instance.web.Class.extend( /** @lends instance.we
} catch (e) {
group_label = row_data[group_column.id].value;
}
- $group_column.html(_.str.sprintf("%s (%d)",
+ $group_column.text(_.str.sprintf("%s (%d)",
group_label, group.length));
if (group.length && group.openable) {
@@ -1417,10 +1417,12 @@ instance.web.ListView.Groups = instance.web.Class.extend( /** @lends instance.we
}))
.end()
.find('button[data-pager-action=previous]')
- .attr('disabled', page === 0)
+ .css('visibility',
+ page === 0 ? 'hidden' : '')
.end()
.find('button[data-pager-action=next]')
- .attr('disabled', page === pages - 1);
+ .css('visibility',
+ page === pages - 1 ? 'hidden' : '');
}
}
@@ -1558,6 +1560,60 @@ instance.web.ListView.Groups = instance.web.Class.extend( /** @lends instance.we
}
});
+var DataGroup = instance.web.CallbackEnabled.extend({
+ init: function(parent, model, domain, context, group_by, level) {
+ this._super(parent, null);
+ this.model = new instance.web.Model(model, context, domain);
+ this.group_by = group_by;
+ this.context = context;
+ this.domain = domain;
+
+ this.level = level || 0;
+ },
+ list: function (fields, ifGroups, ifRecords) {
+ var self = this;
+ var query = this.model.query(fields).order_by(this.sort).group_by(this.group_by);
+ $.when(query).then(function (querygroups) {
+ // leaf node
+ if (!querygroups) {
+ var ds = new instance.web.DataSetSearch(self, self.model.name, self.model.context(), self.model.domain());
+ ds._sort = self.sort;
+ ifRecords(ds);
+ return;
+ }
+ // internal node
+ var child_datagroups = _(querygroups).map(function (group) {
+ var child_context = _.extend(
+ {}, self.model.context(), group.model.context());
+ var child_dg = new DataGroup(
+ self, self.model.name, group.model.domain(),
+ child_context, group.model._context.group_by,
+ self.level + 1);
+ child_dg.sort = self.sort;
+ // copy querygroup properties
+ child_dg.__context = child_context;
+ child_dg.__domain = group.model.domain();
+ child_dg.folded = group.get('folded');
+ child_dg.grouped_on = group.get('grouped_on');
+ child_dg.length = group.get('length');
+ child_dg.value = group.get('value');
+ child_dg.openable = group.get('has_children');
+ child_dg.aggregates = group.get('aggregates');
+ return child_dg;
+ });
+ ifGroups(child_datagroups);
+ });
+ }
+});
+var StaticDataGroup = DataGroup.extend({
+ init: function (dataset) {
+ this.dataset = dataset;
+ },
+ list: function (fields, ifGroups, ifRecords) {
+ ifRecords(this.dataset);
+ }
+});
+
/**
* @mixin Events
*/
diff --git a/addons/web/static/src/js/view_list_editable.js b/addons/web/static/src/js/view_list_editable.js
index 4bf61895a09..9bc0e96032f 100644
--- a/addons/web/static/src/js/view_list_editable.js
+++ b/addons/web/static/src/js/view_list_editable.js
@@ -100,7 +100,7 @@ openerp.web.list_editable = function (instance) {
this._super();
}
},
- on_loaded: function (data, grouped) {
+ load_list: function (data, grouped) {
var self = this;
// tree/@editable takes priority on everything else if present.
var result = this._super(data, grouped);
@@ -190,10 +190,10 @@ openerp.web.list_editable = function (instance) {
this.records.add(record, {
at: this.prepends_on_create() ? 0 : null});
}
- var $recordRow = this.groups.get_row_for(record);
- var cells = this.get_cells_for($recordRow);
return this.ensure_saved().pipe(function () {
+ var $recordRow = self.groups.get_row_for(record);
+ var cells = self.get_cells_for($recordRow);
self.fields_for_resize.splice(0, self.fields_for_resize.length);
return self.with_event('edit', {
record: record.attributes,
@@ -201,10 +201,7 @@ openerp.web.list_editable = function (instance) {
}, function () {
return self.editor.edit(item, function (field_name, field) {
var cell = cells[field_name];
- if (!cell || field.get('effective_readonly')) {
- // Readonly fields can just remain the list's,
- // form's usually don't have backgrounds &al
- field.set({invisible: true});
+ if (!cell) {
return;
}
@@ -254,12 +251,10 @@ openerp.web.list_editable = function (instance) {
var $cell = $(cell);
var position = $cell.position();
- // jquery does not understand !important
- field.$el.attr('style', 'width: '+$cell.outerWidth()+'px !important');
+ field.set_dimensions($cell.outerHeight(), $cell.outerWidth());
field.$el.css({
top: position.top,
left: position.left,
- minHeight: $cell.outerHeight()
});
},
/**
@@ -400,6 +395,20 @@ openerp.web.list_editable = function (instance) {
},
setup_events: function () {
var self = this;
+ _.each(this.editor.form.fields, function(field, field_name) {
+ var setting = false;
+ var set_invisible = function() {
+ if (!setting && field.get("effective_readonly")) {
+ setting = true;
+ field.set({invisible: true});
+ setting = false;
+ }
+ };
+ field.on("change:effective_readonly", self, set_invisible);
+ field.on("change:invisible", self, set_invisible);
+ set_invisible();
+ });
+
this.editor.$el.on('keyup keydown', function (e) {
if (!self.editor.is_editing()) { return; }
var key = _($.ui.keyCode).chain()
@@ -762,7 +771,7 @@ openerp.web.list_editable = function (instance) {
return this._super.apply(this, arguments);
}
var record_id = $(event.currentTarget).data('id');
- this.view.start_edition(
+ return this.view.start_edition(
record_id ? this.records.get(record_id) : null, {
focus_field: $(event.target).data('field')
});
diff --git a/addons/web/static/src/js/views.js b/addons/web/static/src/js/views.js
index b5bb3a7259d..705bc1b214e 100644
--- a/addons/web/static/src/js/views.js
+++ b/addons/web/static/src/js/views.js
@@ -113,6 +113,7 @@ instance.web.ActionManager = instance.web.Widget.extend({
var item = this.breadcrumbs[index];
item.show(subindex);
this.inner_widget = item.widget;
+ this.inner_action = item.action;
return true;
},
clear_breadcrumbs: function() {
@@ -131,6 +132,7 @@ instance.web.ActionManager = instance.web.Widget.extend({
if (!dups.length) {
if (this.getParent().has_uncommitted_changes()) {
this.inner_widget = item.widget;
+ this.inner_action = item.action;
this.breadcrumbs.splice(index, 0, item);
return false;
} else {
@@ -139,7 +141,10 @@ instance.web.ActionManager = instance.web.Widget.extend({
}
}
var last_widget = this.breadcrumbs.slice(-1)[0];
- this.inner_widget = last_widget && last_widget.widget;
+ if (last_widget) {
+ this.inner_widget = last_widget.widget;
+ this.inner_action = last_widget.action;
+ }
},
get_title: function() {
var titles = [];
@@ -234,14 +239,19 @@ instance.web.ActionManager = instance.web.Widget.extend({
}
});
},
- do_action: function(action, on_close, clear_breadcrumbs, on_reverse_breadcrumb) {
+ do_action: function(action, options) {
+ options = _.defaults(options || {}, {
+ clear_breadcrumbs: false,
+ on_reverse_breadcrumb: function() {},
+ on_close: function() {},
+ });
if (_.isString(action) && instance.web.client_actions.contains(action)) {
var action_client = { type: "ir.actions.client", tag: action };
- return this.do_action(action_client, on_close, clear_breadcrumbs, on_reverse_breadcrumb);
+ return this.do_action(action_client, options);
} else if (_.isNumber(action) || _.isString(action)) {
var self = this;
return self.rpc("/web/action/load", { action_id: action }).pipe(function(result) {
- return self.do_action(result, on_close, clear_breadcrumbs, on_reverse_breadcrumb);
+ return self.do_action(result, options);
});
}
if (!action.type) {
@@ -263,7 +273,7 @@ instance.web.ActionManager = instance.web.Widget.extend({
console.error("Action manager can't handle action of type " + action.type, action);
return $.Deferred().reject();
}
- return this[type](action, on_close, clear_breadcrumbs, on_reverse_breadcrumb);
+ return this[type](action, options);
},
null_action: function() {
this.dialog_stop();
@@ -276,33 +286,36 @@ instance.web.ActionManager = instance.web.Widget.extend({
* @param {Function<instance.web.Widget>} executor.widget function used to fetch the widget instance
* @param {String} executor.klass CSS class to add on the dialog root, if action.target=new
* @param {Function<instance.web.Widget, undefined>} executor.post_process cleanup called after a widget has been added as inner_widget
- * @param on_close
- * @param clear_breadcrumbs
+ * @param {Object} options
* @return {*}
*/
- ir_actions_common: function(executor, on_close, clear_breadcrumbs) {
+ ir_actions_common: function(executor, options) {
if (this.inner_widget && executor.action.target !== 'new') {
if (this.getParent().has_uncommitted_changes()) {
return $.Deferred().reject();
- } else if (clear_breadcrumbs) {
+ } else if (options.clear_breadcrumbs) {
this.clear_breadcrumbs();
}
}
var widget = executor.widget();
if (executor.action.target === 'new') {
- if (this.dialog === null) {
- // These buttons will be overwrited by <footer> if any
+ if (this.dialog === null || this.dialog.isDestroyed()) {
this.dialog = new instance.web.Dialog(this, {
- buttons: { "Close": function() { $(this).dialog("close"); }},
- dialogClass: executor.klass
+ buttons: {"Close": function() {$(this).dialog("close")}},
+ dialogClass: executor.klass,
});
- if (on_close) {
- this.dialog.on("dialog_close", this, on_close);
- }
+ this.dialog.on("closing", null, options.on_close);
+ this.dialog.init_dialog();
} else {
this.dialog_widget.destroy();
}
this.dialog.dialog_title = executor.action.name;
+ if (widget instanceof instance.web.ViewManager) {
+ _.extend(widget.flags, {
+ $buttons: this.dialog.$buttons,
+ footer_to_buttons: true,
+ });
+ }
this.dialog_widget = widget;
var initialized = this.dialog_widget.appendTo(this.dialog.$el);
this.dialog.open();
@@ -315,24 +328,24 @@ instance.web.ActionManager = instance.web.Widget.extend({
return this.inner_widget.appendTo(this.$el);
}
},
- ir_actions_act_window: function (action, on_close, clear_breadcrumbs, on_reverse_breadcrumb) {
+ ir_actions_act_window: function (action, options) {
var self = this;
return this.ir_actions_common({
widget: function () { return new instance.web.ViewManagerAction(self, action); },
action: action,
klass: 'oe_act_window',
- post_process: function (widget) { widget.add_breadcrumb(on_reverse_breadcrumb); }
- }, on_close, clear_breadcrumbs, on_reverse_breadcrumb);
+ post_process: function (widget) { widget.add_breadcrumb(options.on_reverse_breadcrumb); }
+ }, options);
},
- ir_actions_client: function (action, on_close, clear_breadcrumbs, on_reverse_breadcrumb) {
+ ir_actions_client: function (action, options) {
var self = this;
var ClientWidget = instance.web.client_actions.get_object(action.tag);
if (!(ClientWidget.prototype instanceof instance.web.Widget)) {
var next;
if (next = ClientWidget(this, action.params)) {
- return this.do_action(next, on_close, clear_breadcrumbs, on_reverse_breadcrumb);
+ return this.do_action(next, options);
}
return $.when();
}
@@ -345,30 +358,30 @@ instance.web.ActionManager = instance.web.Widget.extend({
self.push_breadcrumb({
widget: widget,
title: action.name,
- on_reverse_breadcrumb: on_reverse_breadcrumb,
+ on_reverse_breadcrumb: options.on_reverse_breadcrumb,
});
if (action.tag !== 'reload') {
self.do_push_state({});
}
}
- }, on_close, clear_breadcrumbs, on_reverse_breadcrumb);
+ }, options);
},
- ir_actions_act_window_close: function (action, on_closed) {
- if (!this.dialog && on_closed) {
- on_closed();
+ ir_actions_act_window_close: function (action, options) {
+ if (!this.dialog) {
+ options.on_close();
}
this.dialog_stop();
},
- ir_actions_server: function (action, on_closed, clear_breadcrumbs, on_reverse_breadcrumb) {
+ ir_actions_server: function (action, options) {
var self = this;
this.rpc('/web/action/run', {
action_id: action.id,
context: action.context || {}
}).then(function (action) {
- self.do_action(action, on_closed, clear_breadcrumbs, on_reverse_breadcrumb)
+ self.do_action(action, options)
});
},
- ir_actions_report_xml: function(action, on_closed) {
+ ir_actions_report_xml: function(action, options) {
var self = this;
instance.web.blockUI();
self.rpc("/web/session/eval_domain_and_context", {
@@ -382,8 +395,8 @@ instance.web.ActionManager = instance.web.Widget.extend({
data: {action: JSON.stringify(action)},
complete: instance.web.unblockUI,
success: function(){
- if (!self.dialog && on_closed) {
- on_closed();
+ if (!self.dialog) {
+ options.on_close();
}
self.dialog_stop();
},
@@ -400,6 +413,7 @@ instance.web.ViewManager = instance.web.Widget.extend({
template: "ViewManager",
init: function(parent, dataset, views, flags) {
this._super(parent);
+ this.url_states = {};
this.model = dataset ? dataset.model : undefined;
this.dataset = dataset;
this.searchview = null;
@@ -495,13 +509,6 @@ instance.web.ViewManager = instance.web.Widget.extend({
container.hide();
controller.do_hide();
}
- // put the <footer> in the dialog's buttonpane
- if (self.$el.parent('.ui-dialog-content') && self.$el.find('footer')) {
- self.$el.parent('.ui-dialog-content').parent().find('div.ui-dialog-buttonset').hide()
- self.$el.find('footer').appendTo(
- self.$el.parent('.ui-dialog-content').parent().find('div.ui-dialog-buttonpane')
- );
- }
}
});
self.trigger('switch_mode', view_type, no_store, view_options);
@@ -571,10 +578,11 @@ instance.web.ViewManager = instance.web.Widget.extend({
action: this.action,
show: function(index) {
var view_to_select = views[index];
- self.$el.show();
- if (self.active_view !== view_to_select) {
- self.switch_mode(view_to_select);
- }
+ var state = self.url_states[view_to_select];
+ self.do_push_state(state || {});
+ $.when(self.switch_mode(view_to_select)).then(function() {
+ self.$el.show();
+ });
},
get_title: function() {
var id;
@@ -640,7 +648,7 @@ instance.web.ViewManager = instance.web.Widget.extend({
}
this.searchview = new instance.web.SearchView(this, this.dataset, view_id, search_defaults, this.flags.search_view === false);
- this.searchview.on_search.add(this.do_searchview_search);
+ this.searchview.on('search_data', self, this.do_searchview_search);
return this.searchview.appendTo(this.$el.find(".oe_view_manager_view_search"));
},
do_searchview_search: function(domains, contexts, groupbys) {
@@ -912,6 +920,7 @@ instance.web.ViewManagerAction = instance.web.ViewManager.extend({
do_push_state: function(state) {
if (this.getParent() && this.getParent().do_push_state) {
state["view_type"] = this.active_view;
+ this.url_states[this.active_view] = state;
this.getParent().do_push_state(state);
}
},
@@ -1126,11 +1135,13 @@ instance.web.View = instance.web.Widget.extend({
return this.load_view();
},
load_view: function() {
+ var self = this;
if (this.embedded_view) {
var def = $.Deferred();
- var self = this;
$.async_when().then(function() {def.resolve(self.embedded_view);});
- return def.pipe(this.on_loaded);
+ return def.pipe(function(r) {
+ self.trigger('view_loaded', r);
+ });
} else {
var context = new instance.web.CompoundContext(this.dataset.get_context());
if (! this.view_type)
@@ -1141,15 +1152,11 @@ instance.web.View = instance.web.Widget.extend({
"view_type": this.view_type,
toolbar: !!this.options.$sidebar,
context: context
- }).pipe(this.on_loaded);
+ }).pipe(function(r) {
+ self.trigger('view_loaded', r);
+ });
}
},
- /**
- * Called after a successful call to fields_view_get.
- * Must return a promise.
- */
- on_loaded: function(fields_view_get) {
- },
set_default_options: function(options) {
this.options = options || {};
_.defaults(this.options, {
diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml
index a09897862f6..06603ad17a5 100644
--- a/addons/web/static/src/xml/base.xml
+++ b/addons/web/static/src/xml/base.xml
@@ -630,10 +630,10 @@
<input type="checkbox" class="oe_list_record_selector"/>
</th>
<t t-foreach="columns" t-as="column">
- <th t-if="!column.meta and column.invisible !== '1' and !column.reorderable" t-att-data-id="column.id"
- t-attf-class="oe_list_header_#{column.widget or column.type} #{((options.sortable and column.tag !== 'button') ? 'oe_sortable' : null)}">
+ <th t-if="!column.meta and column.invisible !== '1'" t-att-data-id="column.id"
+ t-attf-class="oe_list_header_#{column.widget or column.type} #{((options.sortable and column.tag !== 'button') ? 'oe_sortable' : null)}"><div>
<t t-if="column.tag !== 'button'"><t t-esc="column.string"/></t>
- </th>
+ </div></th>
</t>
<th t-if="options.deletable" class="oe_list_record_delete" width="13px"/>
</tr>
@@ -683,7 +683,7 @@
<t t-foreach="columns" t-as="column">
<t t-set="number" t-value="column.type === 'integer' or column.type == 'float'"/>
<t t-set="modifiers" t-value="column.modifiers_for(asData)"/>
- <td t-if="!column.meta and column.invisible !== '1' and !column.reorderable" t-att-title="column.help"
+ <td t-if="!column.meta and column.invisible !== '1'" t-att-title="column.help"
t-attf-class="oe_list_field_cell oe_list_field_#{column.widget or column.type} #{number ? 'oe_number' : ''} #{column.tag === 'button' ? 'oe-button' : ''} #{modifiers.readonly ? 'oe_readonly' : ''} #{modifiers.required ? 'oe_required' : ''}"
t-att-data-field="column.id"
><t t-raw="render_cell(record, column)"/></td>
@@ -1078,7 +1078,7 @@
</t>
<t t-name="FieldStatus.content">
<t t-foreach="widget.selection" t-as="i">
- <li t-att-class="i[0] === widget.selected_value ? 'oe_active' : ''" t-att-data-id="i[0]">
+ <li t-att-class="i[0] === widget.get('value') ? 'oe_active' : ''" t-att-data-id="i[0]">
<span class="label"><t t-esc="i[1]"/></span>
<!-- are you mit ? -->
<span class="arrow"><span></span></span>