2011-09-05 11:03:09 +00:00
|
|
|
|
openerp.web.form = function (openerp) {
|
2011-08-18 17:43:22 +00:00
|
|
|
|
|
2011-12-16 13:00:00 +00:00
|
|
|
|
var _t = openerp.web._t,
|
|
|
|
|
_lt = openerp.web._lt;
|
2011-09-07 09:37:43 +00:00
|
|
|
|
var QWeb = openerp.web.qweb;
|
2011-03-30 14:00:48 +00:00
|
|
|
|
|
2011-09-05 11:03:09 +00:00
|
|
|
|
openerp.web.views.add('form', 'openerp.web.FormView');
|
|
|
|
|
openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# */{
|
2011-04-01 10:45:00 +00:00
|
|
|
|
/**
|
|
|
|
|
* Indicates that this view is not searchable, and thus that no search
|
|
|
|
|
* view should be displayed (if there is one active).
|
|
|
|
|
*/
|
|
|
|
|
searchable: false,
|
2011-11-07 13:45:39 +00:00
|
|
|
|
readonly : false,
|
2011-09-14 15:14:59 +00:00
|
|
|
|
form_template: "FormView",
|
2011-12-16 13:00:00 +00:00
|
|
|
|
display_name: _lt('Form'),
|
2011-04-01 10:45:00 +00:00
|
|
|
|
/**
|
2011-09-12 11:34:37 +00:00
|
|
|
|
* @constructs openerp.web.FormView
|
|
|
|
|
* @extends openerp.web.View
|
2011-10-19 12:50:34 +00:00
|
|
|
|
*
|
2011-09-05 11:03:09 +00:00
|
|
|
|
* @param {openerp.web.Session} session the current openerp session
|
|
|
|
|
* @param {openerp.web.DataSet} dataset the dataset this view will work with
|
2011-04-01 10:45:00 +00:00
|
|
|
|
* @param {String} view_id the identifier of the OpenERP view object
|
2011-12-21 17:16:02 +00:00
|
|
|
|
* @param {Object} options
|
|
|
|
|
* - sidebar : [true|false]
|
|
|
|
|
* - resize_textareas : [true|false|max_height]
|
2011-06-03 09:43:02 +00:00
|
|
|
|
*
|
2011-09-05 11:03:09 +00:00
|
|
|
|
* @property {openerp.web.Registry} registry=openerp.web.form.widgets widgets registry for this form view instance
|
2011-04-01 10:45:00 +00:00
|
|
|
|
*/
|
2011-09-14 14:30:28 +00:00
|
|
|
|
init: function(parent, dataset, view_id, options) {
|
|
|
|
|
this._super(parent);
|
2011-08-08 17:32:30 +00:00
|
|
|
|
this.set_default_options(options);
|
2011-03-30 14:00:48 +00:00
|
|
|
|
this.dataset = dataset;
|
|
|
|
|
this.model = dataset.model;
|
2011-09-20 10:08:23 +00:00
|
|
|
|
this.view_id = view_id || false;
|
2011-04-04 15:06:19 +00:00
|
|
|
|
this.fields_view = {};
|
2011-03-30 14:00:48 +00:00
|
|
|
|
this.widgets = {};
|
|
|
|
|
this.widgets_counter = 0;
|
|
|
|
|
this.fields = {};
|
2011-12-01 15:50:29 +00:00
|
|
|
|
this.fields_order = [];
|
2011-04-05 15:14:40 +00:00
|
|
|
|
this.datarecord = {};
|
2011-06-20 18:25:49 +00:00
|
|
|
|
this.default_focus_field = null;
|
|
|
|
|
this.default_focus_button = null;
|
2011-11-29 14:39:11 +00:00
|
|
|
|
this.registry = openerp.web.form.widgets;
|
2011-06-17 14:19:45 +00:00
|
|
|
|
this.has_been_loaded = $.Deferred();
|
2011-06-23 15:55:09 +00:00
|
|
|
|
this.$form_header = null;
|
2011-08-24 15:13:57 +00:00
|
|
|
|
this.translatable_fields = [];
|
2011-12-05 13:50:37 +00:00
|
|
|
|
_.defaults(this.options, {
|
|
|
|
|
"not_interactible_on_create": false
|
|
|
|
|
});
|
2012-01-10 14:41:13 +00:00
|
|
|
|
this.is_initialized = $.Deferred();
|
|
|
|
|
this.mutating_mutex = new $.Mutex();
|
2012-01-10 14:44:20 +00:00
|
|
|
|
this.on_change_mutex = new $.Mutex();
|
|
|
|
|
this.reload_mutex = new $.Mutex();
|
2011-03-30 14:00:48 +00:00
|
|
|
|
},
|
2011-06-06 07:04:51 +00:00
|
|
|
|
start: function() {
|
2011-09-14 14:30:28 +00:00
|
|
|
|
this._super();
|
2011-09-15 10:01:01 +00:00
|
|
|
|
return this.init_view();
|
2011-09-14 15:49:51 +00:00
|
|
|
|
},
|
|
|
|
|
init_view: function() {
|
2011-06-06 07:04:51 +00:00
|
|
|
|
if (this.embedded_view) {
|
2011-07-13 11:05:32 +00:00
|
|
|
|
var def = $.Deferred().then(this.on_loaded);
|
|
|
|
|
var self = this;
|
2011-12-19 16:28:05 +00:00
|
|
|
|
$.async_when().then(function() {def.resolve(self.embedded_view);});
|
2011-07-13 11:05:32 +00:00
|
|
|
|
return def.promise();
|
2011-06-01 08:23:58 +00:00
|
|
|
|
} else {
|
2011-09-05 11:03:09 +00:00
|
|
|
|
var context = new openerp.web.CompoundContext(this.dataset.get_context());
|
2011-09-06 20:54:38 +00:00
|
|
|
|
return this.rpc("/web/view/load", {
|
|
|
|
|
"model": this.model,
|
|
|
|
|
"view_id": this.view_id,
|
|
|
|
|
"view_type": "form",
|
|
|
|
|
toolbar: this.options.sidebar,
|
|
|
|
|
context: context
|
|
|
|
|
}, this.on_loaded);
|
2011-06-01 08:23:58 +00:00
|
|
|
|
}
|
2011-03-30 14:00:48 +00:00
|
|
|
|
},
|
2011-07-11 16:15:46 +00:00
|
|
|
|
stop: function() {
|
2011-07-26 21:00:05 +00:00
|
|
|
|
if (this.sidebar) {
|
|
|
|
|
this.sidebar.attachments.stop();
|
|
|
|
|
this.sidebar.stop();
|
|
|
|
|
}
|
2011-07-11 16:15:46 +00:00
|
|
|
|
_.each(this.widgets, function(w) {
|
[FIX] handling of focus on m2o fields (in editable list row)
* Throw out focusin/focusout: the m2o widget's completion list is
created at the page top (body) so the editable listview can't
generically handle arbitrary widgets via mere focusin/focusout.
* Move responsibility of focus/blur events to the form view and its
widgets.
* Events could not just be named ``focus`` and ``blur`` due to usage
of jquery's event system: jquery will automatically call a method of
the event's name if it exists on the object, which conflicts with
.web.form.Field#focus and leads to an infinite loop (as Field#focus
focuses the field's root element, which triggers the focus event,
which calls the focus method,...) => form-* and widget-*, can be
switched back in trunk
* m2o mess kind-of complex, basically:
- the core input and the menu button behave as blur/focus triggers
- when the autocompletion list is clicked, it will temporarily
remove the focus from the input (blurring it), and put it back
later... on a timer. Issue is the timer, we don't want to rely on
having a bigger timer (as later revisions of the library may
change our timings and it's iffy to rely on timers conserving
order perfectly); on the other hand we know the focus *will* come
back to the input eventually. So we can just avoid propagating
blur iif the blur is the consequence of having clicked on the
completion list.
- roughly the same thing happens when clicking on $drop_down (after
fixing the handling of its final focus to be consistent, as
$drop_down would not re-focus the input if it was *closing* the
completion list)
- pretty sure the menu thing does *not work at all*, but I don't
have the courage of fixing it before committing this part.
Date/datetime widget remains to be handled, basically the core focus
handling is the same as in e.g. a charfield *but* needs to handle (and
ignore) loss of focus from clicking inside the picker
widget. Expecting the level of suck to reach unknown heights.
bzr revid: xmo@openerp.com-20120619072518-lsrhzij5asxt2aea
2012-06-19 07:25:18 +00:00
|
|
|
|
$(w).unbind('.formBlur');
|
2011-07-11 16:15:46 +00:00
|
|
|
|
w.stop();
|
|
|
|
|
});
|
2011-09-28 15:16:13 +00:00
|
|
|
|
this._super();
|
2011-07-11 16:15:46 +00:00
|
|
|
|
},
|
2011-09-20 11:21:47 +00:00
|
|
|
|
reposition: function ($e) {
|
|
|
|
|
this.$element = $e;
|
|
|
|
|
this.on_loaded();
|
|
|
|
|
},
|
2011-03-30 14:00:48 +00:00
|
|
|
|
on_loaded: function(data) {
|
|
|
|
|
var self = this;
|
2011-09-20 11:21:47 +00:00
|
|
|
|
if (data) {
|
2011-12-01 15:50:29 +00:00
|
|
|
|
this.fields_order = [];
|
2011-09-20 11:21:47 +00:00
|
|
|
|
this.fields_view = data;
|
|
|
|
|
var frame = new (this.registry.get_object('frame'))(this, this.fields_view.arch);
|
2011-03-30 14:00:48 +00:00
|
|
|
|
|
2011-10-27 10:45:17 +00:00
|
|
|
|
this.rendered = QWeb.render(this.form_template, { 'frame': frame, 'widget': this });
|
2011-09-20 11:21:47 +00:00
|
|
|
|
}
|
|
|
|
|
this.$element.html(this.rendered);
|
2011-03-30 14:00:48 +00:00
|
|
|
|
_.each(this.widgets, function(w) {
|
|
|
|
|
w.start();
|
[FIX] handling of focus on m2o fields (in editable list row)
* Throw out focusin/focusout: the m2o widget's completion list is
created at the page top (body) so the editable listview can't
generically handle arbitrary widgets via mere focusin/focusout.
* Move responsibility of focus/blur events to the form view and its
widgets.
* Events could not just be named ``focus`` and ``blur`` due to usage
of jquery's event system: jquery will automatically call a method of
the event's name if it exists on the object, which conflicts with
.web.form.Field#focus and leads to an infinite loop (as Field#focus
focuses the field's root element, which triggers the focus event,
which calls the focus method,...) => form-* and widget-*, can be
switched back in trunk
* m2o mess kind-of complex, basically:
- the core input and the menu button behave as blur/focus triggers
- when the autocompletion list is clicked, it will temporarily
remove the focus from the input (blurring it), and put it back
later... on a timer. Issue is the timer, we don't want to rely on
having a bigger timer (as later revisions of the library may
change our timings and it's iffy to rely on timers conserving
order perfectly); on the other hand we know the focus *will* come
back to the input eventually. So we can just avoid propagating
blur iif the blur is the consequence of having clicked on the
completion list.
- roughly the same thing happens when clicking on $drop_down (after
fixing the handling of its final focus to be consistent, as
$drop_down would not re-focus the input if it was *closing* the
completion list)
- pretty sure the menu thing does *not work at all*, but I don't
have the courage of fixing it before committing this part.
Date/datetime widget remains to be handled, basically the core focus
handling is the same as in e.g. a charfield *but* needs to handle (and
ignore) loss of focus from clicking inside the picker
widget. Expecting the level of suck to reach unknown heights.
bzr revid: xmo@openerp.com-20120619072518-lsrhzij5asxt2aea
2012-06-19 07:25:18 +00:00
|
|
|
|
$(w).bind('widget-focus.formBlur', self.proxy('widgetFocused'))
|
|
|
|
|
.bind('widget-blur.formBlur', self.proxy('widgetBlurred'));
|
2011-03-30 14:00:48 +00:00
|
|
|
|
});
|
2011-09-29 09:02:05 +00:00
|
|
|
|
this.$form_header = this.$element.find('.oe_form_header:first');
|
2011-06-23 15:55:09 +00:00
|
|
|
|
this.$form_header.find('div.oe_form_pager button[data-pager-action]').click(function() {
|
2011-04-05 08:57:50 +00:00
|
|
|
|
var action = $(this).data('pager-action');
|
|
|
|
|
self.on_pager_action(action);
|
|
|
|
|
});
|
2011-04-05 14:34:25 +00:00
|
|
|
|
|
2011-11-09 15:12:39 +00:00
|
|
|
|
this.$form_header.find('button.oe_form_button_save').click(this.on_button_save);
|
2011-12-05 13:50:37 +00:00
|
|
|
|
this.$form_header.find('button.oe_form_button_cancel').click(this.on_button_cancel);
|
2011-04-05 14:34:25 +00:00
|
|
|
|
|
2011-11-22 10:26:09 +00:00
|
|
|
|
if (!this.sidebar && this.options.sidebar && this.options.sidebar_id) {
|
2011-09-05 11:03:09 +00:00
|
|
|
|
this.sidebar = new openerp.web.Sidebar(this, this.options.sidebar_id);
|
2011-07-26 21:00:05 +00:00
|
|
|
|
this.sidebar.start();
|
|
|
|
|
this.sidebar.do_unfold();
|
2011-10-10 12:34:32 +00:00
|
|
|
|
this.sidebar.attachments = new openerp.web.form.SidebarAttachments(this.sidebar, this);
|
2011-09-07 06:40:41 +00:00
|
|
|
|
this.sidebar.add_toolbar(this.fields_view.toolbar);
|
2011-07-28 15:15:45 +00:00
|
|
|
|
this.set_common_sidebar_sections(this.sidebar);
|
2012-02-08 12:21:53 +00:00
|
|
|
|
|
|
|
|
|
this.sidebar.add_section(_t('Customize'), 'customize');
|
|
|
|
|
this.sidebar.add_items('customize', [{
|
|
|
|
|
label: _t('Set Default'),
|
|
|
|
|
form: this,
|
|
|
|
|
callback: function (item) {
|
|
|
|
|
item.form.open_defaults_dialog();
|
|
|
|
|
}
|
|
|
|
|
}]);
|
2011-07-26 21:00:05 +00:00
|
|
|
|
}
|
2011-06-17 14:19:45 +00:00
|
|
|
|
this.has_been_loaded.resolve();
|
2011-03-30 14:00:48 +00:00
|
|
|
|
},
|
2011-12-15 14:29:39 +00:00
|
|
|
|
|
[FIX] handling of focus on m2o fields (in editable list row)
* Throw out focusin/focusout: the m2o widget's completion list is
created at the page top (body) so the editable listview can't
generically handle arbitrary widgets via mere focusin/focusout.
* Move responsibility of focus/blur events to the form view and its
widgets.
* Events could not just be named ``focus`` and ``blur`` due to usage
of jquery's event system: jquery will automatically call a method of
the event's name if it exists on the object, which conflicts with
.web.form.Field#focus and leads to an infinite loop (as Field#focus
focuses the field's root element, which triggers the focus event,
which calls the focus method,...) => form-* and widget-*, can be
switched back in trunk
* m2o mess kind-of complex, basically:
- the core input and the menu button behave as blur/focus triggers
- when the autocompletion list is clicked, it will temporarily
remove the focus from the input (blurring it), and put it back
later... on a timer. Issue is the timer, we don't want to rely on
having a bigger timer (as later revisions of the library may
change our timings and it's iffy to rely on timers conserving
order perfectly); on the other hand we know the focus *will* come
back to the input eventually. So we can just avoid propagating
blur iif the blur is the consequence of having clicked on the
completion list.
- roughly the same thing happens when clicking on $drop_down (after
fixing the handling of its final focus to be consistent, as
$drop_down would not re-focus the input if it was *closing* the
completion list)
- pretty sure the menu thing does *not work at all*, but I don't
have the courage of fixing it before committing this part.
Date/datetime widget remains to be handled, basically the core focus
handling is the same as in e.g. a charfield *but* needs to handle (and
ignore) loss of focus from clicking inside the picker
widget. Expecting the level of suck to reach unknown heights.
bzr revid: xmo@openerp.com-20120619072518-lsrhzij5asxt2aea
2012-06-19 07:25:18 +00:00
|
|
|
|
widgetFocused: function() {
|
|
|
|
|
if (this.__blur_timeout) {
|
|
|
|
|
clearTimeout(this.__blur_timeout);
|
|
|
|
|
delete this.__blur_timeout;
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
widgetBlurred: function() {
|
|
|
|
|
var self = this;
|
|
|
|
|
// clear timeout, if any
|
|
|
|
|
this.widgetFocused();
|
|
|
|
|
this.__blur_timeout = setTimeout(function () {
|
|
|
|
|
$(self).trigger('form-blur');
|
|
|
|
|
}, 0);
|
|
|
|
|
},
|
|
|
|
|
|
2012-02-01 15:44:42 +00:00
|
|
|
|
do_load_state: function(state, warm) {
|
2011-12-15 14:29:39 +00:00
|
|
|
|
if (state.id && this.datarecord.id != state.id) {
|
2012-02-01 15:44:42 +00:00
|
|
|
|
if (!this.dataset.get_id_index(state.id)) {
|
2012-01-25 08:53:10 +00:00
|
|
|
|
this.dataset.ids.push(state.id);
|
2011-12-15 14:29:39 +00:00
|
|
|
|
}
|
2012-02-01 15:44:42 +00:00
|
|
|
|
this.dataset.select_id(state.id);
|
|
|
|
|
if (warm) {
|
|
|
|
|
this.do_show();
|
|
|
|
|
}
|
2011-12-15 14:29:39 +00:00
|
|
|
|
}
|
2011-10-26 14:42:18 +00:00
|
|
|
|
},
|
2011-12-15 14:29:39 +00:00
|
|
|
|
|
2011-04-06 21:10:37 +00:00
|
|
|
|
do_show: function () {
|
2012-01-04 17:01:18 +00:00
|
|
|
|
var self = this;
|
2012-01-10 12:54:13 +00:00
|
|
|
|
this.$element.show().css('visibility', 'hidden');
|
2012-02-15 12:39:01 +00:00
|
|
|
|
this.$element.removeClass('oe_form_dirty');
|
2012-01-04 17:01:18 +00:00
|
|
|
|
return this.has_been_loaded.pipe(function() {
|
|
|
|
|
var result;
|
2011-12-19 16:30:51 +00:00
|
|
|
|
if (self.dataset.index === null) {
|
|
|
|
|
// null index means we should start a new record
|
2012-01-04 17:01:18 +00:00
|
|
|
|
result = self.on_button_new();
|
2011-12-19 16:30:51 +00:00
|
|
|
|
} else {
|
2012-01-11 12:49:11 +00:00
|
|
|
|
result = self.dataset.read_index(_.keys(self.fields_view.fields), {
|
|
|
|
|
context : { 'bin_size' : true }
|
|
|
|
|
}).pipe(self.on_record_loaded);
|
2011-12-19 16:30:51 +00:00
|
|
|
|
}
|
2012-01-09 10:45:40 +00:00
|
|
|
|
result.pipe(function() {
|
2012-01-10 12:54:13 +00:00
|
|
|
|
self.$element.css('visibility', 'visible');
|
2012-01-09 10:45:40 +00:00
|
|
|
|
});
|
2011-12-19 16:30:51 +00:00
|
|
|
|
if (self.sidebar) {
|
|
|
|
|
self.sidebar.$element.show();
|
|
|
|
|
}
|
2012-01-04 17:01:18 +00:00
|
|
|
|
return result;
|
2011-12-19 16:30:51 +00:00
|
|
|
|
});
|
2011-04-06 21:10:37 +00:00
|
|
|
|
},
|
|
|
|
|
do_hide: function () {
|
2011-12-14 17:18:11 +00:00
|
|
|
|
this._super();
|
2011-07-26 21:00:05 +00:00
|
|
|
|
if (this.sidebar) {
|
|
|
|
|
this.sidebar.$element.hide();
|
|
|
|
|
}
|
2011-04-06 21:10:37 +00:00
|
|
|
|
},
|
2011-03-30 14:00:48 +00:00
|
|
|
|
on_record_loaded: function(record) {
|
2011-12-13 15:33:43 +00:00
|
|
|
|
var self = this, set_values = [];
|
2011-06-23 15:55:09 +00:00
|
|
|
|
if (!record) {
|
2012-02-13 12:38:05 +00:00
|
|
|
|
this.do_warn("Form", "The record could not be found in the database.", true);
|
|
|
|
|
return $.Deferred().reject();
|
2011-06-23 15:55:09 +00:00
|
|
|
|
}
|
|
|
|
|
this.datarecord = record;
|
2011-11-17 16:00:09 +00:00
|
|
|
|
|
|
|
|
|
_(this.fields).each(function (field, f) {
|
2011-11-21 12:40:30 +00:00
|
|
|
|
field.reset();
|
2011-11-17 16:00:09 +00:00
|
|
|
|
var result = field.set_value(self.datarecord[f] || false);
|
2011-12-13 15:33:43 +00:00
|
|
|
|
set_values.push(result);
|
2011-11-17 16:00:09 +00:00
|
|
|
|
$.when(result).then(function() {
|
2011-11-17 12:43:19 +00:00
|
|
|
|
field.validate();
|
2011-11-17 16:00:09 +00:00
|
|
|
|
});
|
|
|
|
|
});
|
2012-01-04 17:01:18 +00:00
|
|
|
|
return $.when.apply(null, set_values).pipe(function() {
|
2011-11-17 12:43:19 +00:00
|
|
|
|
if (!record.id) {
|
2011-12-01 15:50:29 +00:00
|
|
|
|
// New record: Second pass in order to trigger the onchanges
|
|
|
|
|
// respecting the fields order defined in the view
|
|
|
|
|
_.each(self.fields_order, function(field_name) {
|
|
|
|
|
if (record[field_name] !== undefined) {
|
|
|
|
|
var field = self.fields[field_name];
|
2011-11-17 12:43:19 +00:00
|
|
|
|
field.dirty = true;
|
|
|
|
|
self.do_onchange(field);
|
|
|
|
|
}
|
2011-12-01 15:50:29 +00:00
|
|
|
|
});
|
2011-04-07 13:07:25 +00:00
|
|
|
|
}
|
2011-11-17 12:43:19 +00:00
|
|
|
|
self.on_form_changed();
|
2012-01-10 14:41:13 +00:00
|
|
|
|
self.is_initialized.resolve();
|
2011-11-17 12:43:19 +00:00
|
|
|
|
self.do_update_pager(record.id == null);
|
|
|
|
|
if (self.sidebar) {
|
|
|
|
|
self.sidebar.attachments.do_update();
|
|
|
|
|
}
|
2012-03-05 17:16:56 +00:00
|
|
|
|
if (self.default_focus_field) {
|
2011-11-17 12:43:19 +00:00
|
|
|
|
self.default_focus_field.focus();
|
|
|
|
|
}
|
2011-12-14 16:09:58 +00:00
|
|
|
|
if (record.id) {
|
|
|
|
|
self.do_push_state({id:record.id});
|
|
|
|
|
}
|
2012-01-30 14:07:25 +00:00
|
|
|
|
self.$element.removeClass('oe_form_dirty');
|
2011-11-17 12:43:19 +00:00
|
|
|
|
});
|
2011-03-30 14:00:48 +00:00
|
|
|
|
},
|
2012-02-15 10:34:05 +00:00
|
|
|
|
on_form_changed: function() {
|
2011-06-23 15:55:09 +00:00
|
|
|
|
for (var w in this.widgets) {
|
|
|
|
|
w = this.widgets[w];
|
2011-07-07 10:37:40 +00:00
|
|
|
|
w.process_modifiers();
|
2012-01-16 10:52:58 +00:00
|
|
|
|
if (w.field) {
|
|
|
|
|
w.validate();
|
|
|
|
|
}
|
2011-06-23 15:55:09 +00:00
|
|
|
|
w.update_dom();
|
2011-03-30 14:00:48 +00:00
|
|
|
|
}
|
|
|
|
|
},
|
2012-02-15 10:34:05 +00:00
|
|
|
|
do_notify_change: function() {
|
|
|
|
|
this.$element.addClass('oe_form_dirty');
|
|
|
|
|
},
|
2011-04-06 21:10:37 +00:00
|
|
|
|
on_pager_action: function(action) {
|
2011-09-14 21:09:02 +00:00
|
|
|
|
if (this.can_be_discarded()) {
|
|
|
|
|
switch (action) {
|
|
|
|
|
case 'first':
|
|
|
|
|
this.dataset.index = 0;
|
|
|
|
|
break;
|
|
|
|
|
case 'previous':
|
|
|
|
|
this.dataset.previous();
|
|
|
|
|
break;
|
|
|
|
|
case 'next':
|
|
|
|
|
this.dataset.next();
|
|
|
|
|
break;
|
|
|
|
|
case 'last':
|
|
|
|
|
this.dataset.index = this.dataset.ids.length - 1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
this.reload();
|
2011-03-30 14:00:48 +00:00
|
|
|
|
}
|
2011-04-06 21:10:37 +00:00
|
|
|
|
},
|
2011-04-11 11:55:07 +00:00
|
|
|
|
do_update_pager: function(hide_index) {
|
2011-09-16 13:22:28 +00:00
|
|
|
|
var $pager = this.$form_header.find('div.oe_form_pager');
|
2011-04-11 11:55:07 +00:00
|
|
|
|
var index = hide_index ? '-' : this.dataset.index + 1;
|
2012-01-25 08:53:10 +00:00
|
|
|
|
$pager.find('button').prop('disabled', this.dataset.ids.length < 2);
|
2011-04-11 12:32:07 +00:00
|
|
|
|
$pager.find('span.oe_pager_index').html(index);
|
2011-06-29 08:44:59 +00:00
|
|
|
|
$pager.find('span.oe_pager_count').html(this.dataset.ids.length);
|
2011-04-06 21:10:37 +00:00
|
|
|
|
},
|
2011-11-21 12:04:13 +00:00
|
|
|
|
parse_on_change: function (on_change, widget) {
|
|
|
|
|
var self = this;
|
|
|
|
|
var onchange = _.str.trim(on_change);
|
|
|
|
|
var call = onchange.match(/^\s?(.*?)\((.*?)\)\s?$/);
|
|
|
|
|
if (!call) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
2011-11-21 12:45:32 +00:00
|
|
|
|
var method = call[1];
|
2011-11-21 12:28:45 +00:00
|
|
|
|
if (!_.str.trim(call[2])) {
|
2011-11-21 12:45:32 +00:00
|
|
|
|
return {method: method, args: [], context_index: null}
|
2011-11-21 12:28:45 +00:00
|
|
|
|
}
|
|
|
|
|
|
2011-11-21 12:04:13 +00:00
|
|
|
|
var argument_replacement = {
|
|
|
|
|
'False': function () {return false;},
|
|
|
|
|
'True': function () {return true;},
|
|
|
|
|
'None': function () {return null;},
|
|
|
|
|
'context': function (i) {
|
|
|
|
|
context_index = i;
|
2012-01-12 15:34:19 +00:00
|
|
|
|
var ctx = new openerp.web.CompoundContext(self.dataset.get_context(), widget.build_context() ? widget.build_context() : {});
|
2011-11-21 12:04:13 +00:00
|
|
|
|
return ctx;
|
|
|
|
|
}
|
|
|
|
|
};
|
2011-11-21 12:45:32 +00:00
|
|
|
|
var parent_fields = null, context_index = null;
|
|
|
|
|
var args = _.map(call[2].split(','), function (a, i) {
|
2011-11-21 12:04:13 +00:00
|
|
|
|
var field = _.str.trim(a);
|
2011-11-21 12:41:45 +00:00
|
|
|
|
|
|
|
|
|
// literal constant or context
|
2011-11-21 12:04:13 +00:00
|
|
|
|
if (field in argument_replacement) {
|
2011-11-21 12:45:32 +00:00
|
|
|
|
return argument_replacement[field](i);
|
2011-11-21 12:41:45 +00:00
|
|
|
|
}
|
2011-11-22 12:43:53 +00:00
|
|
|
|
// literal number
|
|
|
|
|
if (/^-?\d+(\.\d+)?$/.test(field)) {
|
|
|
|
|
return Number(field);
|
|
|
|
|
}
|
2011-11-21 12:41:45 +00:00
|
|
|
|
// form field
|
|
|
|
|
if (self.fields[field]) {
|
2011-11-21 12:04:13 +00:00
|
|
|
|
var value = self.fields[field].get_on_change_value();
|
2011-11-21 12:45:32 +00:00
|
|
|
|
return value == null ? false : value;
|
2011-11-21 12:41:45 +00:00
|
|
|
|
}
|
|
|
|
|
// parent field
|
|
|
|
|
var splitted = field.split('.');
|
|
|
|
|
if (splitted.length > 1 && _.str.trim(splitted[0]) === "parent" && self.dataset.parent_view) {
|
|
|
|
|
if (parent_fields === null) {
|
2011-12-19 17:30:55 +00:00
|
|
|
|
parent_fields = self.dataset.parent_view.get_fields_values([self.dataset.child_name]);
|
2011-11-21 12:04:13 +00:00
|
|
|
|
}
|
2011-11-21 12:41:45 +00:00
|
|
|
|
var p_val = parent_fields[_.str.trim(splitted[1])];
|
|
|
|
|
if (p_val !== undefined) {
|
2011-11-21 12:45:32 +00:00
|
|
|
|
return p_val == null ? false : p_val;
|
2011-11-21 12:41:45 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// string literal
|
|
|
|
|
var first_char = field[0], last_char = field[field.length-1];
|
|
|
|
|
if ((first_char === '"' && last_char === '"')
|
|
|
|
|
|| (first_char === "'" && last_char === "'")) {
|
2011-11-21 12:45:32 +00:00
|
|
|
|
return field.slice(1, -1);
|
2011-11-21 12:04:13 +00:00
|
|
|
|
}
|
2011-11-21 12:41:45 +00:00
|
|
|
|
|
2011-11-21 12:05:09 +00:00
|
|
|
|
throw new Error("Could not get field with name '" + field +
|
|
|
|
|
"' for onchange '" + onchange + "'");
|
2011-11-21 12:04:13 +00:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
method: method,
|
|
|
|
|
args: args,
|
|
|
|
|
context_index: context_index
|
|
|
|
|
};
|
|
|
|
|
},
|
2011-04-07 17:09:29 +00:00
|
|
|
|
do_onchange: function(widget, processed) {
|
2011-10-12 15:08:30 +00:00
|
|
|
|
var self = this;
|
2012-01-10 14:47:09 +00:00
|
|
|
|
return this.on_change_mutex.exec(function() {
|
2011-10-12 15:08:30 +00:00
|
|
|
|
try {
|
2012-02-07 16:01:58 +00:00
|
|
|
|
var response = {}, can_process_onchange = $.Deferred();
|
2011-11-21 12:04:13 +00:00
|
|
|
|
processed = processed || [];
|
2012-02-07 16:17:33 +00:00
|
|
|
|
processed.push(widget.name);
|
2011-11-21 12:04:13 +00:00
|
|
|
|
var on_change = widget.node.attrs.on_change;
|
|
|
|
|
if (on_change) {
|
|
|
|
|
var change_spec = self.parse_on_change(on_change, widget);
|
|
|
|
|
if (change_spec) {
|
|
|
|
|
var ajax = {
|
2012-01-20 10:56:08 +00:00
|
|
|
|
url: '/web/dataset/onchange',
|
2011-11-21 12:04:13 +00:00
|
|
|
|
async: false
|
|
|
|
|
};
|
2012-02-07 16:01:58 +00:00
|
|
|
|
can_process_onchange = self.rpc(ajax, {
|
2011-11-21 12:04:13 +00:00
|
|
|
|
model: self.dataset.model,
|
|
|
|
|
method: change_spec.method,
|
|
|
|
|
args: [(self.datarecord.id == null ? [] : [self.datarecord.id])].concat(change_spec.args),
|
|
|
|
|
context_id: change_spec.context_index == undefined ? null : change_spec.context_index + 1
|
2012-02-07 16:01:58 +00:00
|
|
|
|
}).then(function(r) {
|
|
|
|
|
_.extend(response, r);
|
2011-11-21 12:04:13 +00:00
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
console.warn("Wrong on_change format", on_change);
|
|
|
|
|
}
|
2011-10-12 15:08:30 +00:00
|
|
|
|
}
|
2012-02-07 16:01:58 +00:00
|
|
|
|
// fail if onchange failed
|
|
|
|
|
if (can_process_onchange.isRejected()) {
|
|
|
|
|
return can_process_onchange;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (widget.field['change_default']) {
|
|
|
|
|
var fieldname = widget.name, value;
|
|
|
|
|
if (response.value && (fieldname in response.value)) {
|
|
|
|
|
// Use value from onchange if onchange executed
|
|
|
|
|
value = response.value[fieldname];
|
|
|
|
|
} else {
|
|
|
|
|
// otherwise get form value for field
|
|
|
|
|
value = self.fields[fieldname].get_on_change_value();
|
|
|
|
|
}
|
|
|
|
|
var condition = fieldname + '=' + value;
|
|
|
|
|
|
|
|
|
|
if (value) {
|
|
|
|
|
can_process_onchange = self.rpc({
|
|
|
|
|
url: '/web/dataset/call',
|
|
|
|
|
async: false
|
|
|
|
|
}, {
|
|
|
|
|
model: 'ir.values',
|
|
|
|
|
method: 'get_defaults',
|
|
|
|
|
args: [self.model, condition]
|
|
|
|
|
}).then(function (results) {
|
|
|
|
|
if (!results.length) { return; }
|
|
|
|
|
if (!response.value) {
|
|
|
|
|
response.value = {};
|
|
|
|
|
}
|
|
|
|
|
for(var i=0; i<results.length; ++i) {
|
|
|
|
|
// [whatever, key, value]
|
|
|
|
|
var triplet = results[i];
|
|
|
|
|
response.value[triplet[1]] = triplet[2];
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (can_process_onchange.isRejected()) {
|
|
|
|
|
return can_process_onchange;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return self.on_processed_onchange(response, processed);
|
2011-10-12 15:08:30 +00:00
|
|
|
|
} catch(e) {
|
|
|
|
|
console.error(e);
|
|
|
|
|
return $.Deferred().reject();
|
|
|
|
|
}
|
2012-01-10 14:47:09 +00:00
|
|
|
|
});
|
2011-04-06 14:51:13 +00:00
|
|
|
|
},
|
2011-04-07 17:09:29 +00:00
|
|
|
|
on_processed_onchange: function(response, processed) {
|
2011-10-12 15:08:30 +00:00
|
|
|
|
try {
|
2011-07-15 09:55:27 +00:00
|
|
|
|
var result = response;
|
2011-04-06 14:51:13 +00:00
|
|
|
|
if (result.value) {
|
|
|
|
|
for (var f in result.value) {
|
2012-01-20 10:56:08 +00:00
|
|
|
|
if (!result.value.hasOwnProperty(f)) { continue; }
|
2011-04-06 14:51:13 +00:00
|
|
|
|
var field = this.fields[f];
|
2011-07-14 13:18:49 +00:00
|
|
|
|
// If field is not defined in the view, just ignore it
|
2011-04-06 14:51:13 +00:00
|
|
|
|
if (field) {
|
|
|
|
|
var value = result.value[f];
|
2011-06-20 14:43:48 +00:00
|
|
|
|
if (field.get_value() != value) {
|
2011-04-06 14:51:13 +00:00
|
|
|
|
field.set_value(value);
|
2011-11-15 14:58:06 +00:00
|
|
|
|
field.dirty = true;
|
2012-02-07 16:17:33 +00:00
|
|
|
|
if (!_.contains(processed, field.name)) {
|
2011-04-07 17:09:29 +00:00
|
|
|
|
this.do_onchange(field, processed);
|
|
|
|
|
}
|
2011-04-06 14:51:13 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-04-07 17:09:29 +00:00
|
|
|
|
this.on_form_changed();
|
2011-04-06 14:51:13 +00:00
|
|
|
|
}
|
2011-07-06 08:32:14 +00:00
|
|
|
|
if (!_.isEmpty(result.warning)) {
|
2011-12-27 21:40:11 +00:00
|
|
|
|
$(QWeb.render("CrashManagerWarning", result.warning)).dialog({
|
2011-04-06 14:51:13 +00:00
|
|
|
|
modal: true,
|
2011-12-15 10:40:31 +00:00
|
|
|
|
buttons: [
|
|
|
|
|
{text: _t("Ok"), click: function() { $(this).dialog("close"); }}
|
|
|
|
|
]
|
2011-04-06 14:51:13 +00:00
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
if (result.domain) {
|
2012-01-20 10:56:08 +00:00
|
|
|
|
function edit_domain(node) {
|
|
|
|
|
var new_domain = result.domain[node.attrs.name];
|
|
|
|
|
if (new_domain) {
|
|
|
|
|
node.attrs.domain = new_domain;
|
|
|
|
|
}
|
|
|
|
|
_(node.children).each(edit_domain);
|
|
|
|
|
}
|
|
|
|
|
edit_domain(this.fields_view.arch);
|
2011-04-06 14:51:13 +00:00
|
|
|
|
}
|
2011-10-12 15:08:30 +00:00
|
|
|
|
return $.Deferred().resolve();
|
|
|
|
|
} catch(e) {
|
|
|
|
|
console.error(e);
|
|
|
|
|
return $.Deferred().reject();
|
|
|
|
|
}
|
2011-03-30 14:00:48 +00:00
|
|
|
|
},
|
2011-11-09 15:12:39 +00:00
|
|
|
|
on_button_save: function() {
|
2011-04-07 13:07:25 +00:00
|
|
|
|
var self = this;
|
2011-12-07 15:31:01 +00:00
|
|
|
|
return this.do_save().then(function(result) {
|
2012-01-24 09:30:33 +00:00
|
|
|
|
self.do_prev_view({'created': result.created, 'default': 'page'});
|
2011-04-07 13:07:25 +00:00
|
|
|
|
});
|
2011-03-30 14:00:48 +00:00
|
|
|
|
},
|
2011-12-05 13:50:37 +00:00
|
|
|
|
on_button_cancel: function() {
|
2012-02-15 12:39:01 +00:00
|
|
|
|
if (this.can_be_discarded()) {
|
|
|
|
|
return this.do_prev_view({'default': 'page'});
|
|
|
|
|
}
|
2011-11-07 13:45:39 +00:00
|
|
|
|
},
|
2011-12-01 12:22:33 +00:00
|
|
|
|
on_button_new: function() {
|
2011-11-07 13:45:39 +00:00
|
|
|
|
var self = this;
|
|
|
|
|
var def = $.Deferred();
|
|
|
|
|
$.when(this.has_been_loaded).then(function() {
|
2011-09-15 08:12:41 +00:00
|
|
|
|
if (self.can_be_discarded()) {
|
2011-12-01 12:22:33 +00:00
|
|
|
|
var keys = _.keys(self.fields_view.fields);
|
|
|
|
|
if (keys.length) {
|
|
|
|
|
self.dataset.default_get(keys).pipe(self.on_record_loaded).then(function() {
|
2011-11-10 10:36:50 +00:00
|
|
|
|
def.resolve();
|
|
|
|
|
});
|
|
|
|
|
} else {
|
2011-12-01 12:22:33 +00:00
|
|
|
|
self.on_record_loaded({}).then(function() {
|
|
|
|
|
def.resolve();
|
|
|
|
|
});
|
2011-11-10 10:36:50 +00:00
|
|
|
|
}
|
2011-09-15 08:12:41 +00:00
|
|
|
|
}
|
2011-04-07 13:07:25 +00:00
|
|
|
|
});
|
2011-07-18 15:28:07 +00:00
|
|
|
|
return def.promise();
|
2011-03-30 14:00:48 +00:00
|
|
|
|
},
|
2011-09-14 21:09:02 +00:00
|
|
|
|
can_be_discarded: function() {
|
2012-02-15 10:56:28 +00:00
|
|
|
|
return !this.$element.is('.oe_form_dirty') || confirm(_t("Warning, the record has been modified, your changes will be discarded."));
|
2011-09-14 21:09:02 +00:00
|
|
|
|
},
|
2011-06-06 13:11:08 +00:00
|
|
|
|
/**
|
|
|
|
|
* 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) {
|
2011-04-11 11:35:16 +00:00
|
|
|
|
var self = this;
|
2012-01-10 14:47:09 +00:00
|
|
|
|
return this.mutating_mutex.exec(function() { return self.is_initialized.pipe(function() {
|
2011-10-12 14:10:07 +00:00
|
|
|
|
try {
|
2011-11-15 14:58:06 +00:00
|
|
|
|
var form_invalid = false,
|
2011-10-11 15:35:35 +00:00
|
|
|
|
values = {},
|
|
|
|
|
first_invalid_field = null;
|
|
|
|
|
for (var f in self.fields) {
|
|
|
|
|
f = self.fields[f];
|
|
|
|
|
if (!f.is_valid()) {
|
|
|
|
|
form_invalid = true;
|
2012-01-16 11:14:31 +00:00
|
|
|
|
f.update_dom(true);
|
2011-10-11 15:35:35 +00:00
|
|
|
|
if (!first_invalid_field) {
|
|
|
|
|
first_invalid_field = f;
|
|
|
|
|
}
|
2011-11-21 12:40:30 +00:00
|
|
|
|
} else if (f.name !== 'id' && !f.readonly && (!self.datarecord.id || f.is_dirty())) {
|
|
|
|
|
// Special case 'id' field, do not save this field
|
|
|
|
|
// on 'create' : save all non readonly fields
|
|
|
|
|
// on 'edit' : save non readonly modified fields
|
2011-10-11 15:35:35 +00:00
|
|
|
|
values[f.name] = f.get_value();
|
2011-06-22 12:25:42 +00:00
|
|
|
|
}
|
2011-03-30 14:00:48 +00:00
|
|
|
|
}
|
2011-10-11 15:35:35 +00:00
|
|
|
|
if (form_invalid) {
|
|
|
|
|
first_invalid_field.focus();
|
|
|
|
|
self.on_invalid();
|
|
|
|
|
return $.Deferred().reject();
|
2011-04-11 11:35:16 +00:00
|
|
|
|
} else {
|
2011-12-08 12:38:46 +00:00
|
|
|
|
var save_deferral;
|
2011-10-11 15:35:35 +00:00
|
|
|
|
if (!self.datarecord.id) {
|
2012-01-12 12:44:56 +00:00
|
|
|
|
//console.log("FormView(", self, ") : About to create", values);
|
2011-12-08 12:38:46 +00:00
|
|
|
|
save_deferral = self.dataset.create(values).pipe(function(r) {
|
2011-10-11 15:35:35 +00:00
|
|
|
|
return self.on_created(r, undefined, prepend_on_create);
|
2011-12-08 12:38:46 +00:00
|
|
|
|
}, null);
|
2012-04-20 11:28:12 +00:00
|
|
|
|
} else if (_.isEmpty(values) && ! self.force_dirty) {
|
2012-01-12 12:44:56 +00:00
|
|
|
|
//console.log("FormView(", self, ") : Nothing to save");
|
2011-12-08 12:38:46 +00:00
|
|
|
|
save_deferral = $.Deferred().resolve({}).promise();
|
2011-10-11 15:35:35 +00:00
|
|
|
|
} else {
|
2012-04-20 11:28:12 +00:00
|
|
|
|
self.force_dirty = false;
|
2012-01-12 12:44:56 +00:00
|
|
|
|
//console.log("FormView(", self, ") : About to save", values);
|
2011-12-08 12:38:46 +00:00
|
|
|
|
save_deferral = self.dataset.write(self.datarecord.id, values, {}).pipe(function(r) {
|
2011-10-11 15:35:35 +00:00
|
|
|
|
return self.on_saved(r);
|
2011-12-08 12:38:46 +00:00
|
|
|
|
}, null);
|
2011-10-11 15:35:35 +00:00
|
|
|
|
}
|
2011-12-08 12:38:46 +00:00
|
|
|
|
return save_deferral.then(success);
|
2011-04-11 11:35:16 +00:00
|
|
|
|
}
|
2011-10-12 14:10:07 +00:00
|
|
|
|
} catch (e) {
|
|
|
|
|
console.error(e);
|
|
|
|
|
return $.Deferred().reject();
|
|
|
|
|
}
|
2012-01-10 14:47:09 +00:00
|
|
|
|
});});
|
2011-04-05 14:34:25 +00:00
|
|
|
|
},
|
2011-03-30 14:00:48 +00:00
|
|
|
|
on_invalid: function() {
|
2011-04-05 17:35:12 +00:00
|
|
|
|
var msg = "<ul>";
|
|
|
|
|
_.each(this.fields, function(f) {
|
2011-07-27 10:25:19 +00:00
|
|
|
|
if (!f.is_valid()) {
|
2011-04-05 17:35:12 +00:00
|
|
|
|
msg += "<li>" + f.string + "</li>";
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
msg += "</ul>";
|
2011-10-13 10:26:53 +00:00
|
|
|
|
this.do_warn("The following fields are invalid :", msg);
|
2011-03-30 14:00:48 +00:00
|
|
|
|
},
|
2011-04-11 15:59:46 +00:00
|
|
|
|
on_saved: function(r, success) {
|
2011-04-05 15:14:40 +00:00
|
|
|
|
if (!r.result) {
|
2011-09-12 14:07:53 +00:00
|
|
|
|
// should not happen in the server, but may happen for internal purpose
|
2011-09-26 12:39:29 +00:00
|
|
|
|
return $.Deferred().reject();
|
2011-04-05 15:53:48 +00:00
|
|
|
|
} else {
|
[FIX] synchronization of o2m widget saving, avoid race condition when clicking on button of unsaved record
After saving, the formview would both refresh the form and lauch the
action itself, which launches a refresh of its own.
Issue is that o2m's filling of themselves (a read) is async and
triggered by the set_value on the o2m field, so the second reload of
the form would be interspersed (between the first reload and the end
of the o2ms loading), resulting in corrupted state for the o2ms if the
button's action somehow changed the contents of the o2m (it would
remove the old records and return brand new ids during the refresh):
the set_value on the o2m would empty the o2m's dataset cache, and the
returning fetch request would try to find in cache values removed from
it, or something, blowing up everything.
Anyway, this was fixed by ensuring the button action is only executed
after the form is completely done doing its post-save reload (using
the new async set_value). This is a tiny bit brittle in that onchanges
are synchronous but call set_value, so a set_value on an o2m from an
onchange may have issues. It also increases the flicker of the view,
as the o2m is reloaded twice in quick succession.
lp bug: https://launchpad.net/bugs/885658 fixed
bzr revid: xmo@openerp.com-20111117161426-72jzhvv3dm387uom
2011-11-17 16:14:26 +00:00
|
|
|
|
return $.when(this.reload()).pipe(function () {
|
2012-06-13 15:34:12 +00:00
|
|
|
|
return r; })
|
|
|
|
|
.then(success);
|
2011-04-11 15:59:46 +00:00
|
|
|
|
}
|
|
|
|
|
},
|
2011-06-06 13:11:08 +00:00
|
|
|
|
/**
|
|
|
|
|
* 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) {
|
2011-04-11 15:59:46 +00:00
|
|
|
|
if (!r.result) {
|
2011-09-12 14:07:53 +00:00
|
|
|
|
// should not happen in the server, but may happen for internal purpose
|
2011-09-26 12:39:29 +00:00
|
|
|
|
return $.Deferred().reject();
|
2011-04-11 15:59:46 +00:00
|
|
|
|
} else {
|
2011-06-06 13:11:08 +00:00
|
|
|
|
this.datarecord.id = r.result;
|
|
|
|
|
if (!prepend_on_create) {
|
2012-02-14 15:20:31 +00:00
|
|
|
|
this.dataset.alter_ids(this.dataset.ids.concat([this.datarecord.id]));
|
2011-06-06 13:11:08 +00:00
|
|
|
|
this.dataset.index = this.dataset.ids.length - 1;
|
|
|
|
|
} else {
|
2012-02-14 15:20:31 +00:00
|
|
|
|
this.dataset.alter_ids([this.datarecord.id].concat(this.dataset.ids));
|
2011-06-06 13:11:08 +00:00
|
|
|
|
this.dataset.index = 0;
|
|
|
|
|
}
|
2011-04-11 15:59:46 +00:00
|
|
|
|
this.do_update_pager();
|
2011-07-26 21:00:05 +00:00
|
|
|
|
if (this.sidebar) {
|
|
|
|
|
this.sidebar.attachments.do_update();
|
|
|
|
|
}
|
2012-01-12 14:45:47 +00:00
|
|
|
|
//openerp.log("The record has been created with id #" + this.datarecord.id);
|
2012-06-13 15:34:12 +00:00
|
|
|
|
return $.when(this.reload()).pipe(function () {
|
|
|
|
|
return _.extend(r, {created: true}); })
|
|
|
|
|
.then(success);
|
2011-04-05 15:14:40 +00:00
|
|
|
|
}
|
2011-03-31 14:44:22 +00:00
|
|
|
|
},
|
2011-04-01 10:44:54 +00:00
|
|
|
|
on_action: function (action) {
|
2011-09-12 13:56:03 +00:00
|
|
|
|
console.debug('Executing action', action);
|
2011-04-08 10:37:36 +00:00
|
|
|
|
},
|
2011-07-26 21:00:05 +00:00
|
|
|
|
reload: function() {
|
2011-10-12 14:10:07 +00:00
|
|
|
|
var self = this;
|
2012-01-10 14:47:09 +00:00
|
|
|
|
return this.reload_mutex.exec(function() {
|
2011-10-12 14:10:07 +00:00
|
|
|
|
if (self.dataset.index == null || self.dataset.index < 0) {
|
|
|
|
|
return $.when(self.on_button_new());
|
|
|
|
|
} else {
|
2012-01-11 12:49:11 +00:00
|
|
|
|
return self.dataset.read_index(_.keys(self.fields_view.fields), {
|
|
|
|
|
context : { 'bin_size' : true }
|
|
|
|
|
}).pipe(self.on_record_loaded);
|
2011-10-12 14:10:07 +00:00
|
|
|
|
}
|
2012-01-10 14:47:09 +00:00
|
|
|
|
});
|
2011-07-26 21:00:05 +00:00
|
|
|
|
},
|
2011-12-19 17:30:55 +00:00
|
|
|
|
get_fields_values: function(blacklist) {
|
|
|
|
|
blacklist = blacklist || [];
|
2011-07-26 21:00:05 +00:00
|
|
|
|
var values = {};
|
2012-01-18 16:15:17 +00:00
|
|
|
|
var ids = this.get_selected_ids();
|
|
|
|
|
values["id"] = ids.length > 0 ? ids[0] : false;
|
2011-07-26 21:00:05 +00:00
|
|
|
|
_.each(this.fields, function(value, key) {
|
2011-12-19 17:30:55 +00:00
|
|
|
|
if (_.include(blacklist, key))
|
|
|
|
|
return;
|
2011-07-26 21:00:05 +00:00
|
|
|
|
var val = value.get_value();
|
|
|
|
|
values[key] = val;
|
|
|
|
|
});
|
|
|
|
|
return values;
|
2011-08-26 13:03:35 +00:00
|
|
|
|
},
|
|
|
|
|
get_selected_ids: function() {
|
|
|
|
|
var id = this.dataset.ids[this.dataset.index];
|
|
|
|
|
return id ? [id] : [];
|
2011-09-26 12:39:29 +00:00
|
|
|
|
},
|
|
|
|
|
recursive_save: function() {
|
|
|
|
|
var self = this;
|
|
|
|
|
return $.when(this.do_save()).pipe(function(res) {
|
2011-09-26 16:00:57 +00:00
|
|
|
|
if (self.dataset.parent_view)
|
|
|
|
|
return self.dataset.parent_view.recursive_save();
|
2011-09-26 12:39:29 +00:00
|
|
|
|
});
|
2011-09-30 15:46:12 +00:00
|
|
|
|
},
|
2011-11-15 14:58:06 +00:00
|
|
|
|
is_dirty: function() {
|
2011-11-16 07:52:46 +00:00
|
|
|
|
return _.any(this.fields, function (value) {
|
|
|
|
|
return value.is_dirty();
|
|
|
|
|
});
|
2011-11-15 14:58:06 +00:00
|
|
|
|
},
|
2011-09-30 15:46:12 +00:00
|
|
|
|
is_interactible_record: function() {
|
|
|
|
|
var id = this.datarecord.id;
|
|
|
|
|
if (!id) {
|
|
|
|
|
if (this.options.not_interactible_on_create)
|
|
|
|
|
return false;
|
|
|
|
|
} else if (typeof(id) === "string") {
|
|
|
|
|
if(openerp.web.BufferedDataSet.virtual_id_regex.test(id))
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
2011-11-21 14:14:18 +00:00
|
|
|
|
},
|
|
|
|
|
sidebar_context: function () {
|
2011-12-19 17:30:55 +00:00
|
|
|
|
return this.do_save().pipe(_.bind(function() {return this.get_fields_values();}, this));
|
2012-02-08 12:21:53 +00:00
|
|
|
|
},
|
|
|
|
|
open_defaults_dialog: function () {
|
|
|
|
|
var self = this;
|
|
|
|
|
var fields = _.chain(this.fields)
|
|
|
|
|
.map(function (field, name) {
|
|
|
|
|
var value = field.get_value();
|
|
|
|
|
// ignore fields which are empty, invisible, readonly, o2m
|
|
|
|
|
// or m2m
|
|
|
|
|
if (!value
|
|
|
|
|
|| field.invisible
|
|
|
|
|
|| field.readonly
|
|
|
|
|
|| field.field.type === 'one2many'
|
|
|
|
|
|| field.field.type === 'many2many') {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2012-02-09 15:14:10 +00:00
|
|
|
|
var displayed;
|
|
|
|
|
switch(field.field.type) {
|
|
|
|
|
case 'selection':
|
|
|
|
|
displayed = _(field.values).find(function (option) {
|
|
|
|
|
return option[0] === value;
|
|
|
|
|
})[1];
|
|
|
|
|
break;
|
|
|
|
|
case 'many2one':
|
|
|
|
|
displayed = field.value[1] || value;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
displayed = value;
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-08 12:21:53 +00:00
|
|
|
|
return {
|
|
|
|
|
name: name,
|
|
|
|
|
string: field.string,
|
|
|
|
|
value: value,
|
2012-02-09 15:14:10 +00:00
|
|
|
|
displayed: displayed,
|
2012-02-08 12:21:53 +00:00
|
|
|
|
// convert undefined to false
|
|
|
|
|
change_default: !!field.field.change_default
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.compact()
|
|
|
|
|
.sortBy(function (field) { return field.string; })
|
|
|
|
|
.value();
|
|
|
|
|
var conditions = _.chain(fields)
|
|
|
|
|
.filter(function (field) { return field.change_default; })
|
|
|
|
|
.value();
|
|
|
|
|
|
|
|
|
|
var d = new openerp.web.Dialog(this, {
|
|
|
|
|
title: _t("Set Default"),
|
|
|
|
|
args: {
|
|
|
|
|
fields: fields,
|
|
|
|
|
conditions: conditions
|
|
|
|
|
},
|
|
|
|
|
buttons: [
|
|
|
|
|
{text: _t("Close"), click: function () { d.close(); }},
|
|
|
|
|
{text: _t("Save default"), click: function () {
|
|
|
|
|
var $defaults = d.$element.find('#formview_default_fields');
|
|
|
|
|
var field_to_set = $defaults.val();
|
|
|
|
|
if (!field_to_set) {
|
|
|
|
|
$defaults.parent().addClass('invalid');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
var condition = d.$element.find('#formview_default_conditions').val(),
|
|
|
|
|
all_users = d.$element.find('#formview_default_all').is(':checked');
|
|
|
|
|
new openerp.web.DataSet(self, 'ir.values').call(
|
|
|
|
|
'set_default', [
|
|
|
|
|
self.dataset.model,
|
|
|
|
|
field_to_set,
|
|
|
|
|
self.fields[field_to_set].get_value(),
|
|
|
|
|
all_users,
|
|
|
|
|
false,
|
|
|
|
|
condition || false
|
|
|
|
|
]).then(function () { d.close(); });
|
|
|
|
|
}}
|
|
|
|
|
]
|
|
|
|
|
});
|
|
|
|
|
d.template = 'FormView.set_default';
|
|
|
|
|
d.open();
|
2011-07-26 21:00:05 +00:00
|
|
|
|
}
|
|
|
|
|
});
|
2011-09-07 14:53:11 +00:00
|
|
|
|
openerp.web.FormDialog = openerp.web.Dialog.extend({
|
|
|
|
|
init: function(parent, options, view_id, dataset) {
|
|
|
|
|
this._super(parent, options);
|
|
|
|
|
this.dataset = dataset;
|
|
|
|
|
this.view_id = view_id;
|
|
|
|
|
return this;
|
|
|
|
|
},
|
|
|
|
|
start: function() {
|
|
|
|
|
this._super();
|
2011-09-14 15:14:59 +00:00
|
|
|
|
this.form = new openerp.web.FormView(this, this.dataset, this.view_id, {
|
2011-09-07 14:53:11 +00:00
|
|
|
|
sidebar: false,
|
|
|
|
|
pager: false
|
|
|
|
|
});
|
2011-09-14 15:14:59 +00:00
|
|
|
|
this.form.appendTo(this.$element);
|
2011-09-07 14:53:11 +00:00
|
|
|
|
this.form.on_created.add_last(this.on_form_dialog_saved);
|
|
|
|
|
this.form.on_saved.add_last(this.on_form_dialog_saved);
|
|
|
|
|
return this;
|
|
|
|
|
},
|
2011-10-20 16:30:08 +00:00
|
|
|
|
select_id: function(id) {
|
|
|
|
|
if (this.form.dataset.select_id(id)) {
|
|
|
|
|
return this.form.do_show();
|
|
|
|
|
} else {
|
|
|
|
|
this.do_warn("Could not find id in dataset");
|
|
|
|
|
return $.Deferred().reject();
|
|
|
|
|
}
|
2011-09-07 14:53:11 +00:00
|
|
|
|
},
|
2011-09-12 13:36:39 +00:00
|
|
|
|
on_form_dialog_saved: function(r) {
|
2011-09-07 14:53:11 +00:00
|
|
|
|
this.close();
|
|
|
|
|
}
|
|
|
|
|
});
|
2011-07-26 21:00:05 +00:00
|
|
|
|
|
|
|
|
|
/** @namespace */
|
2011-09-05 11:03:09 +00:00
|
|
|
|
openerp.web.form = {};
|
2011-07-26 21:00:05 +00:00
|
|
|
|
|
2012-01-24 14:58:10 +00:00
|
|
|
|
openerp.web.form.SidebarAttachments = openerp.web.OldWidget.extend({
|
2011-10-10 12:34:32 +00:00
|
|
|
|
init: function(parent, form_view) {
|
2011-10-12 11:57:44 +00:00
|
|
|
|
var $section = parent.add_section(_t('Attachments'), 'attachments');
|
2011-10-10 12:34:32 +00:00
|
|
|
|
this.$div = $('<div class="oe-sidebar-attachments"></div>');
|
|
|
|
|
$section.append(this.$div);
|
|
|
|
|
|
|
|
|
|
this._super(parent, $section.attr('id'));
|
2011-07-26 21:00:05 +00:00
|
|
|
|
this.view = form_view;
|
|
|
|
|
},
|
|
|
|
|
do_update: function() {
|
|
|
|
|
if (!this.view.datarecord.id) {
|
2011-05-26 21:38:11 +00:00
|
|
|
|
this.on_attachments_loaded([]);
|
|
|
|
|
} else {
|
2011-09-05 11:03:09 +00:00
|
|
|
|
(new openerp.web.DataSetSearch(
|
2011-08-11 07:00:50 +00:00
|
|
|
|
this, 'ir.attachment', this.view.dataset.get_context(),
|
|
|
|
|
[
|
|
|
|
|
['res_model', '=', this.view.dataset.model],
|
|
|
|
|
['res_id', '=', this.view.datarecord.id],
|
|
|
|
|
['type', 'in', ['binary', 'url']]
|
2012-01-10 16:54:51 +00:00
|
|
|
|
])).read_slice(['name', 'url', 'type'], {}).then(this.on_attachments_loaded);
|
2011-05-26 21:38:11 +00:00
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
on_attachments_loaded: function(attachments) {
|
|
|
|
|
this.attachments = attachments;
|
2011-10-10 12:34:32 +00:00
|
|
|
|
this.$div.html(QWeb.render('FormView.sidebar.attachments', this));
|
2011-07-26 21:00:05 +00:00
|
|
|
|
this.$element.find('.oe-binary-file').change(this.on_attachment_changed);
|
|
|
|
|
this.$element.find('.oe-sidebar-attachment-delete').click(this.on_attachment_delete);
|
2011-05-26 21:38:11 +00:00
|
|
|
|
},
|
|
|
|
|
on_attachment_changed: function(e) {
|
2011-07-26 21:00:05 +00:00
|
|
|
|
window[this.element_id + '_iframe'] = this.do_update;
|
2011-05-26 21:38:11 +00:00
|
|
|
|
var $e = $(e.target);
|
|
|
|
|
if ($e.val() != '') {
|
2011-07-26 21:00:05 +00:00
|
|
|
|
this.$element.find('form.oe-binary-form').submit();
|
2011-12-14 13:23:06 +00:00
|
|
|
|
$e.parent().find('input[type=file]').prop('disabled', true);
|
|
|
|
|
$e.parent().find('button').prop('disabled', true).find('img, span').toggle();
|
2011-05-26 21:38:11 +00:00
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
on_attachment_delete: function(e) {
|
|
|
|
|
var self = this, $e = $(e.currentTarget);
|
2011-11-15 12:30:59 +00:00
|
|
|
|
var name = _.str.trim($e.parent().find('a.oe-sidebar-attachments-link').text());
|
2011-12-20 10:43:27 +00:00
|
|
|
|
if (confirm(_.str.sprintf(_t("Do you really want to delete the attachment %s?"), name))) {
|
2011-09-05 11:03:09 +00:00
|
|
|
|
this.rpc('/web/dataset/unlink', {
|
2011-05-26 21:38:11 +00:00
|
|
|
|
model: 'ir.attachment',
|
|
|
|
|
ids: [parseInt($e.attr('data-id'))]
|
|
|
|
|
}, function(r) {
|
|
|
|
|
$e.parent().remove();
|
2012-05-29 07:40:17 +00:00
|
|
|
|
self.do_update()
|
2011-10-13 10:26:53 +00:00
|
|
|
|
self.do_notify("Delete an attachment", "The attachment '" + name + "' has been deleted");
|
2011-05-26 21:38:11 +00:00
|
|
|
|
});
|
|
|
|
|
}
|
2011-03-30 14:00:48 +00:00
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2011-09-05 11:03:09 +00:00
|
|
|
|
openerp.web.form.compute_domain = function(expr, fields) {
|
2011-04-11 17:36:43 +00:00
|
|
|
|
var stack = [];
|
2011-04-11 17:50:25 +00:00
|
|
|
|
for (var i = expr.length - 1; i >= 0; i--) {
|
2011-04-11 17:36:43 +00:00
|
|
|
|
var ex = expr[i];
|
|
|
|
|
if (ex.length == 1) {
|
2011-04-11 17:50:25 +00:00
|
|
|
|
var top = stack.pop();
|
2011-05-17 09:35:03 +00:00
|
|
|
|
switch (ex) {
|
2011-04-11 17:50:25 +00:00
|
|
|
|
case '|':
|
|
|
|
|
stack.push(stack.pop() || top);
|
|
|
|
|
continue;
|
|
|
|
|
case '&':
|
|
|
|
|
stack.push(stack.pop() && top);
|
|
|
|
|
continue;
|
|
|
|
|
case '!':
|
|
|
|
|
stack.push(!top);
|
|
|
|
|
continue;
|
|
|
|
|
default:
|
2012-02-03 08:48:52 +00:00
|
|
|
|
throw new Error(_.str.sprintf(
|
|
|
|
|
_t("Unknown operator %s in domain %s"),
|
|
|
|
|
ex, JSON.stringify(expr)));
|
2011-04-11 17:50:25 +00:00
|
|
|
|
}
|
2011-04-11 17:36:43 +00:00
|
|
|
|
}
|
|
|
|
|
|
2011-08-10 15:42:38 +00:00
|
|
|
|
var field = fields[ex[0]];
|
|
|
|
|
if (!field) {
|
2012-02-03 08:48:52 +00:00
|
|
|
|
throw new Error(_.str.sprintf(
|
|
|
|
|
_t("Unknown field %s in domain %s"),
|
|
|
|
|
ex[0], JSON.stringify(expr)));
|
2011-08-10 15:42:38 +00:00
|
|
|
|
}
|
|
|
|
|
var field_value = field.get_value ? fields[ex[0]].get_value() : fields[ex[0]].value;
|
2011-04-11 17:36:43 +00:00
|
|
|
|
var op = ex[1];
|
|
|
|
|
var val = ex[2];
|
|
|
|
|
|
|
|
|
|
switch (op.toLowerCase()) {
|
|
|
|
|
case '=':
|
|
|
|
|
case '==':
|
2011-08-10 15:42:38 +00:00
|
|
|
|
stack.push(field_value == val);
|
2011-04-11 17:36:43 +00:00
|
|
|
|
break;
|
|
|
|
|
case '!=':
|
|
|
|
|
case '<>':
|
2011-08-10 15:42:38 +00:00
|
|
|
|
stack.push(field_value != val);
|
2011-04-11 17:36:43 +00:00
|
|
|
|
break;
|
|
|
|
|
case '<':
|
2011-08-10 15:42:38 +00:00
|
|
|
|
stack.push(field_value < val);
|
2011-04-11 17:36:43 +00:00
|
|
|
|
break;
|
|
|
|
|
case '>':
|
2011-08-10 15:42:38 +00:00
|
|
|
|
stack.push(field_value > val);
|
2011-04-11 17:36:43 +00:00
|
|
|
|
break;
|
|
|
|
|
case '<=':
|
2011-08-10 15:42:38 +00:00
|
|
|
|
stack.push(field_value <= val);
|
2011-04-11 17:36:43 +00:00
|
|
|
|
break;
|
|
|
|
|
case '>=':
|
2011-08-10 15:42:38 +00:00
|
|
|
|
stack.push(field_value >= val);
|
2011-04-11 17:36:43 +00:00
|
|
|
|
break;
|
|
|
|
|
case 'in':
|
2012-01-16 13:25:54 +00:00
|
|
|
|
if (!_.isArray(val)) val = [val];
|
2011-08-10 15:42:38 +00:00
|
|
|
|
stack.push(_(val).contains(field_value));
|
2011-04-11 17:36:43 +00:00
|
|
|
|
break;
|
|
|
|
|
case 'not in':
|
2012-01-16 13:25:54 +00:00
|
|
|
|
if (!_.isArray(val)) val = [val];
|
2011-08-10 15:42:38 +00:00
|
|
|
|
stack.push(!_(val).contains(field_value));
|
2011-04-11 17:36:43 +00:00
|
|
|
|
break;
|
|
|
|
|
default:
|
2012-02-03 08:48:52 +00:00
|
|
|
|
console.warn(
|
|
|
|
|
_t("Unsupported operator %s in domain %s"),
|
|
|
|
|
op, JSON.stringify(expr));
|
2011-04-11 17:36:43 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2011-07-14 14:29:58 +00:00
|
|
|
|
return _.all(stack, _.identity);
|
2011-05-17 09:35:03 +00:00
|
|
|
|
};
|
2011-05-11 14:49:10 +00:00
|
|
|
|
|
2012-01-24 14:58:10 +00:00
|
|
|
|
openerp.web.form.Widget = openerp.web.OldWidget.extend(/** @lends openerp.web.form.Widget# */{
|
2011-06-03 10:31:00 +00:00
|
|
|
|
template: 'Widget',
|
2011-09-12 11:43:50 +00:00
|
|
|
|
/**
|
|
|
|
|
* @constructs openerp.web.form.Widget
|
2012-01-24 14:58:10 +00:00
|
|
|
|
* @extends openerp.web.OldWidget
|
2011-09-12 11:43:50 +00:00
|
|
|
|
*
|
|
|
|
|
* @param view
|
|
|
|
|
* @param node
|
|
|
|
|
*/
|
2011-03-30 14:00:48 +00:00
|
|
|
|
init: function(view, node) {
|
|
|
|
|
this.view = view;
|
|
|
|
|
this.node = node;
|
2011-07-07 10:37:40 +00:00
|
|
|
|
this.modifiers = JSON.parse(this.node.attrs.modifiers || '{}');
|
2011-10-18 11:07:40 +00:00
|
|
|
|
this.always_invisible = (this.modifiers.invisible && this.modifiers.invisible === true);
|
2011-03-30 14:00:48 +00:00
|
|
|
|
this.type = this.type || node.tag;
|
|
|
|
|
this.element_name = this.element_name || this.type;
|
2011-09-16 13:22:28 +00:00
|
|
|
|
this.element_class = [
|
|
|
|
|
'formview', this.view.view_id, this.element_name,
|
|
|
|
|
this.view.widgets_counter++].join("_");
|
2011-03-30 14:00:48 +00:00
|
|
|
|
|
2011-09-16 13:22:28 +00:00
|
|
|
|
this._super(view);
|
2011-03-30 14:00:48 +00:00
|
|
|
|
|
2011-09-16 13:22:28 +00:00
|
|
|
|
this.view.widgets[this.element_class] = this;
|
2011-03-30 14:00:48 +00:00
|
|
|
|
this.children = node.children;
|
2011-08-31 12:45:38 +00:00
|
|
|
|
this.colspan = parseInt(node.attrs.colspan || 1, 10);
|
|
|
|
|
this.decrease_max_width = 0;
|
2011-03-30 14:00:48 +00:00
|
|
|
|
|
|
|
|
|
this.string = this.string || node.attrs.string;
|
|
|
|
|
this.help = this.help || node.attrs.help;
|
2011-07-07 10:37:40 +00:00
|
|
|
|
this.invisible = this.modifiers['invisible'] === true;
|
2011-08-31 12:45:38 +00:00
|
|
|
|
this.classname = 'oe_form_' + this.type;
|
|
|
|
|
|
2011-10-27 12:43:25 +00:00
|
|
|
|
this.align = parseFloat(this.node.attrs.align);
|
|
|
|
|
if (isNaN(this.align) || this.align === 1) {
|
|
|
|
|
this.align = 'right';
|
|
|
|
|
} else if (this.align === 0) {
|
|
|
|
|
this.align = 'left';
|
|
|
|
|
} else {
|
|
|
|
|
this.align = 'center';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2011-08-31 12:45:38 +00:00
|
|
|
|
this.width = this.node.attrs.width;
|
2011-03-30 14:00:48 +00:00
|
|
|
|
},
|
[FIX] handling of focus on m2o fields (in editable list row)
* Throw out focusin/focusout: the m2o widget's completion list is
created at the page top (body) so the editable listview can't
generically handle arbitrary widgets via mere focusin/focusout.
* Move responsibility of focus/blur events to the form view and its
widgets.
* Events could not just be named ``focus`` and ``blur`` due to usage
of jquery's event system: jquery will automatically call a method of
the event's name if it exists on the object, which conflicts with
.web.form.Field#focus and leads to an infinite loop (as Field#focus
focuses the field's root element, which triggers the focus event,
which calls the focus method,...) => form-* and widget-*, can be
switched back in trunk
* m2o mess kind-of complex, basically:
- the core input and the menu button behave as blur/focus triggers
- when the autocompletion list is clicked, it will temporarily
remove the focus from the input (blurring it), and put it back
later... on a timer. Issue is the timer, we don't want to rely on
having a bigger timer (as later revisions of the library may
change our timings and it's iffy to rely on timers conserving
order perfectly); on the other hand we know the focus *will* come
back to the input eventually. So we can just avoid propagating
blur iif the blur is the consequence of having clicked on the
completion list.
- roughly the same thing happens when clicking on $drop_down (after
fixing the handling of its final focus to be consistent, as
$drop_down would not re-focus the input if it was *closing* the
completion list)
- pretty sure the menu thing does *not work at all*, but I don't
have the courage of fixing it before committing this part.
Date/datetime widget remains to be handled, basically the core focus
handling is the same as in e.g. a charfield *but* needs to handle (and
ignore) loss of focus from clicking inside the picker
widget. Expecting the level of suck to reach unknown heights.
bzr revid: xmo@openerp.com-20120619072518-lsrhzij5asxt2aea
2012-06-19 07:25:18 +00:00
|
|
|
|
setupFocus: function ($e) {
|
|
|
|
|
var self = this;
|
|
|
|
|
$e.bind({
|
|
|
|
|
focus: function () { $(self).trigger('widget-focus'); },
|
|
|
|
|
blur: function () { $(self).trigger('widget-blur'); }
|
|
|
|
|
});
|
|
|
|
|
},
|
2011-03-30 14:00:48 +00:00
|
|
|
|
start: function() {
|
2011-10-04 10:20:50 +00:00
|
|
|
|
this.$element = this.view.$element.find(
|
2011-10-04 13:28:54 +00:00
|
|
|
|
'.' + this.element_class.replace(/[^\r\n\f0-9A-Za-z_-]/g, "\\$&"));
|
2011-03-30 14:00:48 +00:00
|
|
|
|
},
|
2012-01-26 14:37:34 +00:00
|
|
|
|
stop: function() {
|
|
|
|
|
this._super.apply(this, arguments);
|
2012-02-09 13:59:39 +00:00
|
|
|
|
$.fn.tipsy.clear();
|
2012-01-26 14:37:34 +00:00
|
|
|
|
},
|
2011-07-07 10:37:40 +00:00
|
|
|
|
process_modifiers: function() {
|
2011-09-05 11:03:09 +00:00
|
|
|
|
var compute_domain = openerp.web.form.compute_domain;
|
2011-07-07 10:37:40 +00:00
|
|
|
|
for (var a in this.modifiers) {
|
|
|
|
|
this[a] = compute_domain(this.modifiers[a], this.view.fields);
|
2011-03-30 14:00:48 +00:00
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
update_dom: function() {
|
|
|
|
|
this.$element.toggle(!this.invisible);
|
|
|
|
|
},
|
|
|
|
|
render: function() {
|
|
|
|
|
var template = this.template;
|
|
|
|
|
return QWeb.render(template, { "widget": this });
|
2011-10-13 09:38:14 +00:00
|
|
|
|
},
|
2011-11-14 20:59:16 +00:00
|
|
|
|
do_attach_tooltip: function(widget, trigger, options) {
|
|
|
|
|
widget = widget || this;
|
|
|
|
|
trigger = trigger || this.$element;
|
|
|
|
|
options = _.extend({
|
2012-01-26 13:24:41 +00:00
|
|
|
|
delayIn: 500,
|
|
|
|
|
delayOut: 0,
|
|
|
|
|
fade: true,
|
|
|
|
|
title: function() {
|
2011-11-14 20:59:16 +00:00
|
|
|
|
var template = widget.template + '.tooltip';
|
|
|
|
|
if (!QWeb.has_template(template)) {
|
|
|
|
|
template = 'WidgetLabel.tooltip';
|
|
|
|
|
}
|
|
|
|
|
return QWeb.render(template, {
|
|
|
|
|
debug: openerp.connection.debug,
|
|
|
|
|
widget: widget
|
2012-01-26 13:24:41 +00:00
|
|
|
|
})},
|
2012-02-09 10:54:01 +00:00
|
|
|
|
gravity: $.fn.tipsy.autoBounds(50, 'nw'),
|
2012-01-26 13:24:41 +00:00
|
|
|
|
html: true,
|
|
|
|
|
opacity: 0.85,
|
|
|
|
|
trigger: 'hover'
|
2011-11-14 20:59:16 +00:00
|
|
|
|
}, options || {});
|
2012-01-26 13:24:41 +00:00
|
|
|
|
trigger.tipsy(options);
|
2011-11-14 20:59:16 +00:00
|
|
|
|
},
|
2012-01-03 17:17:13 +00:00
|
|
|
|
_build_view_fields_values: function(blacklist) {
|
2011-10-13 09:38:14 +00:00
|
|
|
|
var a_dataset = this.view.dataset;
|
2012-01-03 17:17:13 +00:00
|
|
|
|
var fields_values = this.view.get_fields_values(blacklist);
|
2011-11-15 13:29:14 +00:00
|
|
|
|
var active_id = a_dataset.ids[a_dataset.index];
|
|
|
|
|
_.extend(fields_values, {
|
|
|
|
|
active_id: active_id || false,
|
|
|
|
|
active_ids: active_id ? [active_id] : [],
|
|
|
|
|
active_model: a_dataset.model,
|
2011-12-19 17:30:55 +00:00
|
|
|
|
parent: {}
|
2011-11-15 13:29:14 +00:00
|
|
|
|
});
|
2011-12-19 17:30:55 +00:00
|
|
|
|
if (a_dataset.parent_view) {
|
|
|
|
|
fields_values.parent = a_dataset.parent_view.get_fields_values([a_dataset.child_name]);
|
|
|
|
|
}
|
2011-10-13 09:38:14 +00:00
|
|
|
|
return fields_values;
|
|
|
|
|
},
|
2012-01-03 17:17:13 +00:00
|
|
|
|
_build_eval_context: function(blacklist) {
|
2011-10-13 09:38:14 +00:00
|
|
|
|
var a_dataset = this.view.dataset;
|
2012-01-03 17:17:13 +00:00
|
|
|
|
return new openerp.web.CompoundContext(a_dataset.get_context(), this._build_view_fields_values(blacklist));
|
2011-10-13 09:38:14 +00:00
|
|
|
|
},
|
|
|
|
|
/**
|
|
|
|
|
* Builds a new context usable for operations related to fields by merging
|
|
|
|
|
* the fields'context with the action's context.
|
|
|
|
|
*/
|
2012-01-03 17:17:13 +00:00
|
|
|
|
build_context: function(blacklist) {
|
2012-01-10 14:20:55 +00:00
|
|
|
|
// only use the model's context if there is not context on the node
|
|
|
|
|
var v_context = this.node.attrs.context;
|
|
|
|
|
if (! v_context) {
|
|
|
|
|
v_context = (this.field || {}).context || {};
|
2011-10-13 09:38:14 +00:00
|
|
|
|
}
|
2012-01-10 14:20:55 +00:00
|
|
|
|
|
|
|
|
|
if (v_context.__ref || true) { //TODO: remove true
|
2012-01-03 17:17:13 +00:00
|
|
|
|
var fields_values = this._build_eval_context(blacklist);
|
2012-01-10 14:20:55 +00:00
|
|
|
|
v_context = new openerp.web.CompoundContext(v_context).set_eval_context(fields_values);
|
2011-10-13 09:38:14 +00:00
|
|
|
|
}
|
2012-01-10 14:20:55 +00:00
|
|
|
|
return v_context;
|
2011-10-13 09:38:14 +00:00
|
|
|
|
},
|
|
|
|
|
build_domain: function() {
|
|
|
|
|
var f_domain = this.field.domain || [];
|
|
|
|
|
var n_domain = this.node.attrs.domain || null;
|
|
|
|
|
// if there is a domain on the node, overrides the model's domain
|
|
|
|
|
var final_domain = n_domain !== null ? n_domain : f_domain;
|
2012-01-03 17:17:13 +00:00
|
|
|
|
if (!(final_domain instanceof Array) || true) { //TODO: remove true
|
2011-10-13 09:38:14 +00:00
|
|
|
|
var fields_values = this._build_eval_context();
|
|
|
|
|
final_domain = new openerp.web.CompoundDomain(final_domain).set_eval_context(fields_values);
|
|
|
|
|
}
|
|
|
|
|
return final_domain;
|
2011-03-30 14:00:48 +00:00
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2011-09-05 11:03:09 +00:00
|
|
|
|
openerp.web.form.WidgetFrame = openerp.web.form.Widget.extend({
|
2011-06-03 10:31:00 +00:00
|
|
|
|
template: 'WidgetFrame',
|
2011-03-30 14:00:48 +00:00
|
|
|
|
init: function(view, node) {
|
|
|
|
|
this._super(view, node);
|
2011-08-31 12:45:38 +00:00
|
|
|
|
this.columns = parseInt(node.attrs.col || 4, 10);
|
2011-03-30 14:00:48 +00:00
|
|
|
|
this.x = 0;
|
|
|
|
|
this.y = 0;
|
|
|
|
|
this.table = [];
|
|
|
|
|
this.add_row();
|
|
|
|
|
for (var i = 0; i < node.children.length; i++) {
|
|
|
|
|
var n = node.children[i];
|
|
|
|
|
if (n.tag == "newline") {
|
|
|
|
|
this.add_row();
|
|
|
|
|
} else {
|
|
|
|
|
this.handle_node(n);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
this.set_row_cells_with(this.table[this.table.length - 1]);
|
|
|
|
|
},
|
|
|
|
|
add_row: function(){
|
|
|
|
|
if (this.table.length) {
|
|
|
|
|
this.set_row_cells_with(this.table[this.table.length - 1]);
|
|
|
|
|
}
|
|
|
|
|
var row = [];
|
|
|
|
|
this.table.push(row);
|
|
|
|
|
this.x = 0;
|
|
|
|
|
this.y += 1;
|
|
|
|
|
return row;
|
|
|
|
|
},
|
|
|
|
|
set_row_cells_with: function(row) {
|
2011-08-31 12:45:38 +00:00
|
|
|
|
var bypass = 0,
|
2011-10-18 11:07:40 +00:00
|
|
|
|
max_width = 100,
|
|
|
|
|
row_length = row.length;
|
2011-08-31 12:45:38 +00:00
|
|
|
|
for (var i = 0; i < row.length; i++) {
|
2011-10-18 11:07:40 +00:00
|
|
|
|
if (row[i].always_invisible) {
|
|
|
|
|
row_length--;
|
|
|
|
|
} else {
|
|
|
|
|
bypass += row[i].width === undefined ? 0 : 1;
|
|
|
|
|
max_width -= row[i].decrease_max_width;
|
|
|
|
|
}
|
2011-08-31 12:45:38 +00:00
|
|
|
|
}
|
|
|
|
|
var size_unit = Math.round(max_width / (this.columns - bypass)),
|
|
|
|
|
colspan_sum = 0;
|
2011-03-30 14:00:48 +00:00
|
|
|
|
for (var i = 0; i < row.length; i++) {
|
|
|
|
|
var w = row[i];
|
2011-10-18 11:07:40 +00:00
|
|
|
|
if (w.always_invisible) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2011-08-31 12:45:38 +00:00
|
|
|
|
colspan_sum += w.colspan;
|
|
|
|
|
if (w.width === undefined) {
|
2011-10-18 11:07:40 +00:00
|
|
|
|
var width = (i === row_length - 1 && colspan_sum === this.columns) ? max_width : Math.round(size_unit * w.colspan);
|
2011-08-31 12:45:38 +00:00
|
|
|
|
max_width -= width;
|
|
|
|
|
w.width = width + '%';
|
2011-03-30 14:00:48 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
2011-04-12 08:38:31 +00:00
|
|
|
|
handle_node: function(node) {
|
2011-07-11 15:48:02 +00:00
|
|
|
|
var type = {};
|
|
|
|
|
if (node.tag == 'field') {
|
|
|
|
|
type = this.view.fields_view.fields[node.attrs.name] || {};
|
2011-11-22 14:35:54 +00:00
|
|
|
|
if (node.attrs.widget == 'statusbar' && node.attrs.nolabel !== '1') {
|
2011-10-11 11:54:24 +00:00
|
|
|
|
// This way we can retain backward compatibility between addons and old clients
|
2011-11-22 14:35:54 +00:00
|
|
|
|
node.attrs.colspan = (parseInt(node.attrs.colspan, 10) || 1) + 1;
|
2011-10-11 11:54:24 +00:00
|
|
|
|
node.attrs.nolabel = '1';
|
|
|
|
|
}
|
2011-07-11 15:48:02 +00:00
|
|
|
|
}
|
2011-06-27 14:14:38 +00:00
|
|
|
|
var widget = new (this.view.registry.get_any(
|
|
|
|
|
[node.attrs.widget, type.type, node.tag])) (this.view, node);
|
2011-06-20 18:25:49 +00:00
|
|
|
|
if (node.tag == 'field') {
|
2011-06-22 12:25:42 +00:00
|
|
|
|
if (!this.view.default_focus_field || node.attrs.default_focus == '1') {
|
2011-06-20 18:25:49 +00:00
|
|
|
|
this.view.default_focus_field = widget;
|
|
|
|
|
}
|
|
|
|
|
if (node.attrs.nolabel != '1') {
|
|
|
|
|
var label = new (this.view.registry.get_object('label')) (this.view, node);
|
|
|
|
|
label["for"] = widget;
|
2011-07-13 15:18:16 +00:00
|
|
|
|
this.add_widget(label, widget.colspan + 1);
|
2011-06-20 18:25:49 +00:00
|
|
|
|
}
|
2011-03-30 14:00:48 +00:00
|
|
|
|
}
|
2011-04-04 20:08:29 +00:00
|
|
|
|
this.add_widget(widget);
|
2011-03-30 14:00:48 +00:00
|
|
|
|
},
|
2011-07-13 15:18:16 +00:00
|
|
|
|
add_widget: function(widget, colspan) {
|
2011-04-21 08:53:18 +00:00
|
|
|
|
var current_row = this.table[this.table.length - 1];
|
2011-10-18 11:07:40 +00:00
|
|
|
|
if (!widget.always_invisible) {
|
|
|
|
|
colspan = colspan || widget.colspan;
|
|
|
|
|
if (current_row.length && (this.x + colspan) > this.columns) {
|
|
|
|
|
current_row = this.add_row();
|
|
|
|
|
}
|
|
|
|
|
this.x += widget.colspan;
|
2011-03-30 14:00:48 +00:00
|
|
|
|
}
|
2011-04-21 08:53:18 +00:00
|
|
|
|
current_row.push(widget);
|
|
|
|
|
return widget;
|
2011-03-30 14:00:48 +00:00
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2011-11-14 10:11:18 +00:00
|
|
|
|
openerp.web.form.WidgetGroup = openerp.web.form.WidgetFrame.extend({
|
2011-11-22 10:19:50 +00:00
|
|
|
|
template: 'WidgetGroup'
|
2011-11-14 10:11:18 +00:00
|
|
|
|
}),
|
|
|
|
|
|
2011-09-05 11:03:09 +00:00
|
|
|
|
openerp.web.form.WidgetNotebook = openerp.web.form.Widget.extend({
|
2011-07-11 14:48:51 +00:00
|
|
|
|
template: 'WidgetNotebook',
|
2011-03-30 14:00:48 +00:00
|
|
|
|
init: function(view, node) {
|
|
|
|
|
this._super(view, node);
|
|
|
|
|
this.pages = [];
|
|
|
|
|
for (var i = 0; i < node.children.length; i++) {
|
|
|
|
|
var n = node.children[i];
|
|
|
|
|
if (n.tag == "page") {
|
2011-10-04 08:51:19 +00:00
|
|
|
|
var page = new (this.view.registry.get_object('notebookpage'))(
|
2011-09-16 14:24:58 +00:00
|
|
|
|
this.view, n, this, this.pages.length);
|
2011-03-30 14:00:48 +00:00
|
|
|
|
this.pages.push(page);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
start: function() {
|
2011-09-16 14:24:58 +00:00
|
|
|
|
var self = this;
|
2011-03-30 14:00:48 +00:00
|
|
|
|
this._super.apply(this, arguments);
|
2011-09-16 14:24:58 +00:00
|
|
|
|
this.$element.find('> ul > li').each(function (index, tab_li) {
|
|
|
|
|
var page = self.pages[index],
|
|
|
|
|
id = _.uniqueId(self.element_name + '-');
|
|
|
|
|
page.element_id = id;
|
|
|
|
|
$(tab_li).find('a').attr('href', '#' + id);
|
|
|
|
|
});
|
|
|
|
|
this.$element.find('> div').each(function (index, page) {
|
|
|
|
|
page.id = self.pages[index].element_id;
|
|
|
|
|
});
|
2011-03-30 14:00:48 +00:00
|
|
|
|
this.$element.tabs();
|
2011-10-04 16:28:41 +00:00
|
|
|
|
this.view.on_button_new.add_first(this.do_select_first_visible_tab);
|
2011-11-14 20:59:16 +00:00
|
|
|
|
if (openerp.connection.debug) {
|
|
|
|
|
this.do_attach_tooltip(this, this.$element.find('ul:first'), {
|
2012-01-26 13:24:41 +00:00
|
|
|
|
gravity: 's'
|
2011-11-14 20:59:16 +00:00
|
|
|
|
});
|
|
|
|
|
}
|
2011-08-08 12:05:00 +00:00
|
|
|
|
},
|
|
|
|
|
do_select_first_visible_tab: function() {
|
|
|
|
|
for (var i = 0; i < this.pages.length; i++) {
|
|
|
|
|
var page = this.pages[i];
|
|
|
|
|
if (page.invisible === false) {
|
|
|
|
|
this.$element.tabs('select', page.index);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-03-30 14:00:48 +00:00
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2011-09-05 11:03:09 +00:00
|
|
|
|
openerp.web.form.WidgetNotebookPage = openerp.web.form.WidgetFrame.extend({
|
2011-07-11 14:48:51 +00:00
|
|
|
|
template: 'WidgetNotebookPage',
|
|
|
|
|
init: function(view, node, notebook, index) {
|
|
|
|
|
this.notebook = notebook;
|
|
|
|
|
this.index = index;
|
|
|
|
|
this.element_name = 'page_' + index;
|
|
|
|
|
this._super(view, node);
|
|
|
|
|
},
|
|
|
|
|
start: function() {
|
|
|
|
|
this._super.apply(this, arguments);
|
2011-09-16 14:24:58 +00:00
|
|
|
|
this.$element_tab = this.notebook.$element.find(
|
|
|
|
|
'> ul > li:eq(' + this.index + ')');
|
2011-07-11 14:48:51 +00:00
|
|
|
|
},
|
|
|
|
|
update_dom: function() {
|
2011-08-08 12:05:00 +00:00
|
|
|
|
if (this.invisible && this.index === this.notebook.$element.tabs('option', 'selected')) {
|
|
|
|
|
this.notebook.do_select_first_visible_tab();
|
2011-07-11 14:48:51 +00:00
|
|
|
|
}
|
|
|
|
|
this.$element_tab.toggle(!this.invisible);
|
|
|
|
|
this.$element.toggle(!this.invisible);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2011-09-05 11:03:09 +00:00
|
|
|
|
openerp.web.form.WidgetSeparator = openerp.web.form.Widget.extend({
|
2011-09-19 10:15:17 +00:00
|
|
|
|
template: 'WidgetSeparator',
|
2011-03-30 14:00:48 +00:00
|
|
|
|
init: function(view, node) {
|
|
|
|
|
this._super(view, node);
|
2011-08-31 12:45:38 +00:00
|
|
|
|
this.orientation = node.attrs.orientation || 'horizontal';
|
|
|
|
|
if (this.orientation === 'vertical') {
|
|
|
|
|
this.width = '1';
|
|
|
|
|
}
|
|
|
|
|
this.classname += '_' + this.orientation;
|
2011-03-30 14:00:48 +00:00
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2011-09-05 11:03:09 +00:00
|
|
|
|
openerp.web.form.WidgetButton = openerp.web.form.Widget.extend({
|
2011-09-19 10:15:17 +00:00
|
|
|
|
template: 'WidgetButton',
|
2011-03-30 14:00:48 +00:00
|
|
|
|
init: function(view, node) {
|
|
|
|
|
this._super(view, node);
|
2011-09-30 16:15:06 +00:00
|
|
|
|
this.force_disabled = false;
|
2011-07-27 14:37:31 +00:00
|
|
|
|
if (this.string) {
|
|
|
|
|
// We don't have button key bindings in the webclient
|
|
|
|
|
this.string = this.string.replace(/_/g, '');
|
|
|
|
|
}
|
2011-06-20 18:25:49 +00:00
|
|
|
|
if (node.attrs.default_focus == '1') {
|
|
|
|
|
// TODO fme: provide enter key binding to widgets
|
|
|
|
|
this.view.default_focus_button = this;
|
|
|
|
|
}
|
2011-04-11 15:59:46 +00:00
|
|
|
|
},
|
|
|
|
|
start: function() {
|
|
|
|
|
this._super.apply(this, arguments);
|
[FIX] handling of focus on m2o fields (in editable list row)
* Throw out focusin/focusout: the m2o widget's completion list is
created at the page top (body) so the editable listview can't
generically handle arbitrary widgets via mere focusin/focusout.
* Move responsibility of focus/blur events to the form view and its
widgets.
* Events could not just be named ``focus`` and ``blur`` due to usage
of jquery's event system: jquery will automatically call a method of
the event's name if it exists on the object, which conflicts with
.web.form.Field#focus and leads to an infinite loop (as Field#focus
focuses the field's root element, which triggers the focus event,
which calls the focus method,...) => form-* and widget-*, can be
switched back in trunk
* m2o mess kind-of complex, basically:
- the core input and the menu button behave as blur/focus triggers
- when the autocompletion list is clicked, it will temporarily
remove the focus from the input (blurring it), and put it back
later... on a timer. Issue is the timer, we don't want to rely on
having a bigger timer (as later revisions of the library may
change our timings and it's iffy to rely on timers conserving
order perfectly); on the other hand we know the focus *will* come
back to the input eventually. So we can just avoid propagating
blur iif the blur is the consequence of having clicked on the
completion list.
- roughly the same thing happens when clicking on $drop_down (after
fixing the handling of its final focus to be consistent, as
$drop_down would not re-focus the input if it was *closing* the
completion list)
- pretty sure the menu thing does *not work at all*, but I don't
have the courage of fixing it before committing this part.
Date/datetime widget remains to be handled, basically the core focus
handling is the same as in e.g. a charfield *but* needs to handle (and
ignore) loss of focus from clicking inside the picker
widget. Expecting the level of suck to reach unknown heights.
bzr revid: xmo@openerp.com-20120619072518-lsrhzij5asxt2aea
2012-06-19 07:25:18 +00:00
|
|
|
|
var $button = this.$element.find('button');
|
|
|
|
|
$button.click(this.on_click);
|
2011-11-14 20:59:16 +00:00
|
|
|
|
if (this.help || openerp.connection.debug) {
|
|
|
|
|
this.do_attach_tooltip();
|
|
|
|
|
}
|
[FIX] handling of focus on m2o fields (in editable list row)
* Throw out focusin/focusout: the m2o widget's completion list is
created at the page top (body) so the editable listview can't
generically handle arbitrary widgets via mere focusin/focusout.
* Move responsibility of focus/blur events to the form view and its
widgets.
* Events could not just be named ``focus`` and ``blur`` due to usage
of jquery's event system: jquery will automatically call a method of
the event's name if it exists on the object, which conflicts with
.web.form.Field#focus and leads to an infinite loop (as Field#focus
focuses the field's root element, which triggers the focus event,
which calls the focus method,...) => form-* and widget-*, can be
switched back in trunk
* m2o mess kind-of complex, basically:
- the core input and the menu button behave as blur/focus triggers
- when the autocompletion list is clicked, it will temporarily
remove the focus from the input (blurring it), and put it back
later... on a timer. Issue is the timer, we don't want to rely on
having a bigger timer (as later revisions of the library may
change our timings and it's iffy to rely on timers conserving
order perfectly); on the other hand we know the focus *will* come
back to the input eventually. So we can just avoid propagating
blur iif the blur is the consequence of having clicked on the
completion list.
- roughly the same thing happens when clicking on $drop_down (after
fixing the handling of its final focus to be consistent, as
$drop_down would not re-focus the input if it was *closing* the
completion list)
- pretty sure the menu thing does *not work at all*, but I don't
have the courage of fixing it before committing this part.
Date/datetime widget remains to be handled, basically the core focus
handling is the same as in e.g. a charfield *but* needs to handle (and
ignore) loss of focus from clicking inside the picker
widget. Expecting the level of suck to reach unknown heights.
bzr revid: xmo@openerp.com-20120619072518-lsrhzij5asxt2aea
2012-06-19 07:25:18 +00:00
|
|
|
|
this.setupFocus($button);
|
2011-04-11 15:59:46 +00:00
|
|
|
|
},
|
2011-09-26 12:39:29 +00:00
|
|
|
|
on_click: function() {
|
2011-09-30 16:15:06 +00:00
|
|
|
|
var self = this;
|
|
|
|
|
this.force_disabled = true;
|
|
|
|
|
this.check_disable();
|
|
|
|
|
this.execute_action().always(function() {
|
|
|
|
|
self.force_disabled = false;
|
|
|
|
|
self.check_disable();
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
execute_action: function() {
|
2011-04-11 15:59:46 +00:00
|
|
|
|
var self = this;
|
2011-09-26 12:39:29 +00:00
|
|
|
|
var exec_action = function() {
|
|
|
|
|
if (self.node.attrs.confirm) {
|
2011-09-30 16:15:06 +00:00
|
|
|
|
var def = $.Deferred();
|
2011-09-26 12:39:29 +00:00
|
|
|
|
var dialog = $('<div>' + self.node.attrs.confirm + '</div>').dialog({
|
2011-12-15 10:06:52 +00:00
|
|
|
|
title: _t('Confirm'),
|
2011-04-11 15:59:46 +00:00
|
|
|
|
modal: true,
|
2011-12-15 10:40:31 +00:00
|
|
|
|
buttons: [
|
2012-01-17 15:33:33 +00:00
|
|
|
|
{text: _t("Cancel"), click: function() {
|
|
|
|
|
def.resolve();
|
|
|
|
|
$(this).dialog("close");
|
|
|
|
|
}
|
|
|
|
|
},
|
2011-12-15 10:40:31 +00:00
|
|
|
|
{text: _t("Ok"), click: function() {
|
|
|
|
|
self.on_confirmed().then(function() {
|
|
|
|
|
def.resolve();
|
|
|
|
|
});
|
|
|
|
|
$(this).dialog("close");
|
|
|
|
|
}
|
2011-04-11 15:59:46 +00:00
|
|
|
|
}
|
2011-12-15 10:40:31 +00:00
|
|
|
|
]
|
2011-04-11 15:59:46 +00:00
|
|
|
|
});
|
2011-09-30 16:15:06 +00:00
|
|
|
|
return def.promise();
|
2011-04-11 15:59:46 +00:00
|
|
|
|
} else {
|
2011-09-30 16:15:06 +00:00
|
|
|
|
return self.on_confirmed();
|
2011-04-11 15:59:46 +00:00
|
|
|
|
}
|
2011-09-26 12:39:29 +00:00
|
|
|
|
};
|
2011-10-12 14:10:07 +00:00
|
|
|
|
if (!this.node.attrs.special) {
|
2012-04-20 11:28:12 +00:00
|
|
|
|
this.view.force_dirty = true;
|
2011-09-30 16:15:06 +00:00
|
|
|
|
return this.view.recursive_save().pipe(exec_action);
|
2011-09-26 12:39:29 +00:00
|
|
|
|
} else {
|
2011-09-30 16:15:06 +00:00
|
|
|
|
return exec_action();
|
2011-04-11 15:59:46 +00:00
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
on_confirmed: function() {
|
2011-05-04 09:12:33 +00:00
|
|
|
|
var self = this;
|
2011-10-19 12:50:34 +00:00
|
|
|
|
|
2011-10-13 09:38:14 +00:00
|
|
|
|
var context = this.node.attrs.context;
|
|
|
|
|
if (context && context.__ref) {
|
|
|
|
|
context = new openerp.web.CompoundContext(context);
|
|
|
|
|
context.set_eval_context(this._build_eval_context());
|
|
|
|
|
}
|
2011-05-04 09:12:33 +00:00
|
|
|
|
|
2011-09-30 16:15:06 +00:00
|
|
|
|
return this.view.do_execute_action(
|
2011-10-13 09:38:14 +00:00
|
|
|
|
_.extend({}, this.node.attrs, {context: context}),
|
|
|
|
|
this.view.dataset, this.view.datarecord.id, function () {
|
2011-06-28 10:30:49 +00:00
|
|
|
|
self.view.reload();
|
2011-04-12 15:09:55 +00:00
|
|
|
|
});
|
2011-09-30 15:46:12 +00:00
|
|
|
|
},
|
|
|
|
|
update_dom: function() {
|
2012-01-16 11:14:31 +00:00
|
|
|
|
this._super.apply(this, arguments);
|
2011-09-30 16:15:06 +00:00
|
|
|
|
this.check_disable();
|
|
|
|
|
},
|
|
|
|
|
check_disable: function() {
|
2011-12-14 13:23:06 +00:00
|
|
|
|
var disabled = (this.readonly || this.force_disabled || !this.view.is_interactible_record());
|
|
|
|
|
this.$element.find('button').prop('disabled', disabled);
|
|
|
|
|
this.$element.find("button").css('color', disabled ? 'grey' : '');
|
2011-03-30 14:00:48 +00:00
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2011-09-05 11:03:09 +00:00
|
|
|
|
openerp.web.form.WidgetLabel = openerp.web.form.Widget.extend({
|
2011-09-19 10:15:17 +00:00
|
|
|
|
template: 'WidgetLabel',
|
2011-03-30 14:00:48 +00:00
|
|
|
|
init: function(view, node) {
|
|
|
|
|
this.element_name = 'label_' + node.attrs.name;
|
|
|
|
|
|
|
|
|
|
this._super(view, node);
|
|
|
|
|
|
2012-02-07 14:06:28 +00:00
|
|
|
|
if (this.node.tag == 'label' && !this.string && this.node.children.length) {
|
|
|
|
|
this.string = this.node.children[0];
|
|
|
|
|
this.align = 'left';
|
|
|
|
|
}
|
|
|
|
|
|
2011-10-27 12:43:25 +00:00
|
|
|
|
if (this.node.tag == 'label' && (this.align === 'left' || this.node.attrs.colspan || (this.string && this.string.length > 32))) {
|
2011-06-28 16:42:38 +00:00
|
|
|
|
this.template = "WidgetParagraph";
|
2011-08-31 12:45:38 +00:00
|
|
|
|
this.colspan = parseInt(this.node.attrs.colspan || 1, 10);
|
2011-11-30 15:55:34 +00:00
|
|
|
|
// Widgets default to right-aligned, but paragraph defaults to
|
|
|
|
|
// left-aligned
|
|
|
|
|
if (isNaN(parseFloat(this.node.attrs.align))) {
|
|
|
|
|
this.align = 'left';
|
|
|
|
|
}
|
2012-02-07 14:06:28 +00:00
|
|
|
|
|
|
|
|
|
this.multilines = this.string && _.str.lines(this.string).length > 1;
|
2011-06-28 16:42:38 +00:00
|
|
|
|
} else {
|
2011-07-27 14:31:59 +00:00
|
|
|
|
this.colspan = 1;
|
2011-08-31 12:45:38 +00:00
|
|
|
|
this.width = '1%';
|
|
|
|
|
this.decrease_max_width = 1;
|
|
|
|
|
this.nowrap = true;
|
2011-06-28 16:42:38 +00:00
|
|
|
|
}
|
2011-03-30 14:00:48 +00:00
|
|
|
|
},
|
|
|
|
|
render: function () {
|
|
|
|
|
if (this['for'] && this.type !== 'label') {
|
|
|
|
|
return QWeb.render(this.template, {widget: this['for']});
|
|
|
|
|
}
|
|
|
|
|
// Actual label widgets should not have a false and have type label
|
|
|
|
|
return QWeb.render(this.template, {widget: this});
|
2011-08-04 15:20:45 +00:00
|
|
|
|
},
|
|
|
|
|
start: function() {
|
|
|
|
|
this._super();
|
|
|
|
|
var self = this;
|
2011-11-14 20:59:16 +00:00
|
|
|
|
if (this['for'] && (this['for'].help || openerp.connection.debug)) {
|
|
|
|
|
this.do_attach_tooltip(self['for']);
|
|
|
|
|
}
|
[FIX] handling of focus on m2o fields (in editable list row)
* Throw out focusin/focusout: the m2o widget's completion list is
created at the page top (body) so the editable listview can't
generically handle arbitrary widgets via mere focusin/focusout.
* Move responsibility of focus/blur events to the form view and its
widgets.
* Events could not just be named ``focus`` and ``blur`` due to usage
of jquery's event system: jquery will automatically call a method of
the event's name if it exists on the object, which conflicts with
.web.form.Field#focus and leads to an infinite loop (as Field#focus
focuses the field's root element, which triggers the focus event,
which calls the focus method,...) => form-* and widget-*, can be
switched back in trunk
* m2o mess kind-of complex, basically:
- the core input and the menu button behave as blur/focus triggers
- when the autocompletion list is clicked, it will temporarily
remove the focus from the input (blurring it), and put it back
later... on a timer. Issue is the timer, we don't want to rely on
having a bigger timer (as later revisions of the library may
change our timings and it's iffy to rely on timers conserving
order perfectly); on the other hand we know the focus *will* come
back to the input eventually. So we can just avoid propagating
blur iif the blur is the consequence of having clicked on the
completion list.
- roughly the same thing happens when clicking on $drop_down (after
fixing the handling of its final focus to be consistent, as
$drop_down would not re-focus the input if it was *closing* the
completion list)
- pretty sure the menu thing does *not work at all*, but I don't
have the courage of fixing it before committing this part.
Date/datetime widget remains to be handled, basically the core focus
handling is the same as in e.g. a charfield *but* needs to handle (and
ignore) loss of focus from clicking inside the picker
widget. Expecting the level of suck to reach unknown heights.
bzr revid: xmo@openerp.com-20120619072518-lsrhzij5asxt2aea
2012-06-19 07:25:18 +00:00
|
|
|
|
var $label = this.$element.find('label');
|
|
|
|
|
$label.dblclick(function() {
|
2011-08-04 15:20:45 +00:00
|
|
|
|
var widget = self['for'] || self;
|
2011-11-09 15:12:39 +00:00
|
|
|
|
openerp.log(widget.element_class , widget);
|
2011-08-18 17:43:22 +00:00
|
|
|
|
window.w = widget;
|
2011-08-04 15:20:45 +00:00
|
|
|
|
});
|
[FIX] handling of focus on m2o fields (in editable list row)
* Throw out focusin/focusout: the m2o widget's completion list is
created at the page top (body) so the editable listview can't
generically handle arbitrary widgets via mere focusin/focusout.
* Move responsibility of focus/blur events to the form view and its
widgets.
* Events could not just be named ``focus`` and ``blur`` due to usage
of jquery's event system: jquery will automatically call a method of
the event's name if it exists on the object, which conflicts with
.web.form.Field#focus and leads to an infinite loop (as Field#focus
focuses the field's root element, which triggers the focus event,
which calls the focus method,...) => form-* and widget-*, can be
switched back in trunk
* m2o mess kind-of complex, basically:
- the core input and the menu button behave as blur/focus triggers
- when the autocompletion list is clicked, it will temporarily
remove the focus from the input (blurring it), and put it back
later... on a timer. Issue is the timer, we don't want to rely on
having a bigger timer (as later revisions of the library may
change our timings and it's iffy to rely on timers conserving
order perfectly); on the other hand we know the focus *will* come
back to the input eventually. So we can just avoid propagating
blur iif the blur is the consequence of having clicked on the
completion list.
- roughly the same thing happens when clicking on $drop_down (after
fixing the handling of its final focus to be consistent, as
$drop_down would not re-focus the input if it was *closing* the
completion list)
- pretty sure the menu thing does *not work at all*, but I don't
have the courage of fixing it before committing this part.
Date/datetime widget remains to be handled, basically the core focus
handling is the same as in e.g. a charfield *but* needs to handle (and
ignore) loss of focus from clicking inside the picker
widget. Expecting the level of suck to reach unknown heights.
bzr revid: xmo@openerp.com-20120619072518-lsrhzij5asxt2aea
2012-06-19 07:25:18 +00:00
|
|
|
|
this.setupFocus($label);
|
2011-03-30 14:00:48 +00:00
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2011-09-12 11:43:50 +00:00
|
|
|
|
openerp.web.form.Field = openerp.web.form.Widget.extend(/** @lends openerp.web.form.Field# */{
|
|
|
|
|
/**
|
|
|
|
|
* @constructs openerp.web.form.Field
|
|
|
|
|
* @extends openerp.web.form.Widget
|
|
|
|
|
*
|
|
|
|
|
* @param view
|
|
|
|
|
* @param node
|
|
|
|
|
*/
|
2011-03-30 14:00:48 +00:00
|
|
|
|
init: function(view, node) {
|
|
|
|
|
this.name = node.attrs.name;
|
|
|
|
|
this.value = undefined;
|
|
|
|
|
view.fields[this.name] = this;
|
2011-12-01 15:50:29 +00:00
|
|
|
|
view.fields_order.push(this.name);
|
2011-03-30 14:00:48 +00:00
|
|
|
|
this.type = node.attrs.widget || view.fields_view.fields[node.attrs.name].type;
|
|
|
|
|
this.element_name = "field_" + this.name + "_" + this.type;
|
|
|
|
|
|
|
|
|
|
this._super(view, node);
|
|
|
|
|
|
|
|
|
|
if (node.attrs.nolabel != '1' && this.colspan > 1) {
|
|
|
|
|
this.colspan--;
|
|
|
|
|
}
|
2011-04-12 08:38:31 +00:00
|
|
|
|
this.field = view.fields_view.fields[node.attrs.name] || {};
|
2011-03-30 14:00:48 +00:00
|
|
|
|
this.string = node.attrs.string || this.field.string;
|
|
|
|
|
this.help = node.attrs.help || this.field.help;
|
2011-07-07 10:37:40 +00:00
|
|
|
|
this.nolabel = (this.field.nolabel || node.attrs.nolabel) === '1';
|
|
|
|
|
this.readonly = this.modifiers['readonly'] === true;
|
|
|
|
|
this.required = this.modifiers['required'] === true;
|
2011-11-21 12:40:30 +00:00
|
|
|
|
this.invalid = this.dirty = false;
|
2011-08-31 12:45:38 +00:00
|
|
|
|
|
|
|
|
|
this.classname = 'oe_form_field_' + this.type;
|
2011-03-30 14:00:48 +00:00
|
|
|
|
},
|
2011-08-24 15:13:57 +00:00
|
|
|
|
start: function() {
|
|
|
|
|
this._super.apply(this, arguments);
|
|
|
|
|
if (this.field.translate) {
|
|
|
|
|
this.view.translatable_fields.push(this);
|
2012-02-14 16:34:43 +00:00
|
|
|
|
this.$element.addClass('oe_form_field_translatable');
|
2011-08-24 15:13:57 +00:00
|
|
|
|
this.$element.find('.oe_field_translate').click(this.on_translate);
|
|
|
|
|
}
|
2011-11-14 20:59:16 +00:00
|
|
|
|
if (this.nolabel && openerp.connection.debug) {
|
2012-01-26 13:24:41 +00:00
|
|
|
|
this.do_attach_tooltip(this, this.$element);
|
2011-11-14 20:59:16 +00:00
|
|
|
|
}
|
2011-08-24 15:13:57 +00:00
|
|
|
|
},
|
2011-03-30 14:00:48 +00:00
|
|
|
|
set_value: function(value) {
|
|
|
|
|
this.value = value;
|
2011-04-05 18:57:04 +00:00
|
|
|
|
this.invalid = false;
|
2011-04-07 13:07:25 +00:00
|
|
|
|
this.update_dom();
|
2011-08-18 17:43:22 +00:00
|
|
|
|
this.on_value_changed();
|
2011-03-30 14:00:48 +00:00
|
|
|
|
},
|
2011-04-11 11:35:16 +00:00
|
|
|
|
set_value_from_ui: function() {
|
2011-08-18 17:43:22 +00:00
|
|
|
|
this.on_value_changed();
|
|
|
|
|
},
|
|
|
|
|
on_value_changed: function() {
|
2011-04-11 11:35:16 +00:00
|
|
|
|
},
|
2011-08-24 15:13:57 +00:00
|
|
|
|
on_translate: function() {
|
|
|
|
|
this.view.open_translate_dialog(this);
|
|
|
|
|
},
|
2011-04-05 14:34:25 +00:00
|
|
|
|
get_value: function() {
|
|
|
|
|
return this.value;
|
2011-03-30 14:00:48 +00:00
|
|
|
|
},
|
2011-07-27 10:25:19 +00:00
|
|
|
|
is_valid: function() {
|
|
|
|
|
return !this.invalid;
|
|
|
|
|
},
|
|
|
|
|
is_dirty: function() {
|
2011-09-19 10:00:03 +00:00
|
|
|
|
return this.dirty && !this.readonly;
|
2011-07-27 10:25:19 +00:00
|
|
|
|
},
|
2011-06-29 16:21:36 +00:00
|
|
|
|
get_on_change_value: function() {
|
|
|
|
|
return this.get_value();
|
|
|
|
|
},
|
2012-01-16 11:14:31 +00:00
|
|
|
|
update_dom: function(show_invalid) {
|
2011-03-30 14:00:48 +00:00
|
|
|
|
this._super.apply(this, arguments);
|
2011-08-29 14:08:39 +00:00
|
|
|
|
if (this.field.translate) {
|
|
|
|
|
this.$element.find('.oe_field_translate').toggle(!!this.view.datarecord.id);
|
|
|
|
|
}
|
2011-07-27 09:09:05 +00:00
|
|
|
|
if (!this.disable_utility_classes) {
|
|
|
|
|
this.$element.toggleClass('disabled', this.readonly);
|
|
|
|
|
this.$element.toggleClass('required', this.required);
|
2012-01-16 11:14:31 +00:00
|
|
|
|
if (show_invalid) {
|
2011-07-27 12:24:42 +00:00
|
|
|
|
this.$element.toggleClass('invalid', !this.is_valid());
|
2011-07-27 09:09:05 +00:00
|
|
|
|
}
|
2011-04-11 11:35:16 +00:00
|
|
|
|
}
|
2011-03-30 14:00:48 +00:00
|
|
|
|
},
|
|
|
|
|
on_ui_change: function() {
|
2011-11-15 14:58:06 +00:00
|
|
|
|
this.dirty = true;
|
2011-04-11 11:35:16 +00:00
|
|
|
|
this.validate();
|
2011-07-27 10:25:19 +00:00
|
|
|
|
if (this.is_valid()) {
|
2011-06-20 14:43:48 +00:00
|
|
|
|
this.set_value_from_ui();
|
2011-06-23 15:55:09 +00:00
|
|
|
|
this.view.do_onchange(this);
|
2012-01-30 14:07:25 +00:00
|
|
|
|
this.view.on_form_changed(true);
|
2012-02-15 10:34:05 +00:00
|
|
|
|
this.view.do_notify_change();
|
2011-06-20 14:43:48 +00:00
|
|
|
|
} else {
|
2012-01-16 11:14:31 +00:00
|
|
|
|
this.update_dom(true);
|
2011-06-20 14:43:48 +00:00
|
|
|
|
}
|
2011-04-11 11:35:16 +00:00
|
|
|
|
},
|
|
|
|
|
validate: function() {
|
2011-06-20 14:43:48 +00:00
|
|
|
|
this.invalid = false;
|
2011-06-20 18:25:49 +00:00
|
|
|
|
},
|
2012-01-12 15:15:27 +00:00
|
|
|
|
focus: function($element) {
|
|
|
|
|
if ($element) {
|
|
|
|
|
setTimeout(function() {
|
|
|
|
|
$element.focus();
|
|
|
|
|
}, 50);
|
|
|
|
|
}
|
2011-11-21 12:40:30 +00:00
|
|
|
|
},
|
|
|
|
|
reset: function() {
|
|
|
|
|
this.dirty = false;
|
2011-12-21 15:08:31 +00:00
|
|
|
|
},
|
2011-12-21 15:32:29 +00:00
|
|
|
|
get_definition_options: function() {
|
|
|
|
|
if (!this.definition_options) {
|
2011-12-21 15:08:31 +00:00
|
|
|
|
var str = this.node.attrs.options || '{}';
|
2011-12-21 15:32:29 +00:00
|
|
|
|
this.definition_options = JSON.parse(str);
|
2011-12-21 15:08:31 +00:00
|
|
|
|
}
|
2011-12-21 15:32:29 +00:00
|
|
|
|
return this.definition_options;
|
2012-01-17 13:28:43 +00:00
|
|
|
|
}
|
2011-03-30 14:00:48 +00:00
|
|
|
|
});
|
|
|
|
|
|
2011-09-05 11:03:09 +00:00
|
|
|
|
openerp.web.form.FieldChar = openerp.web.form.Field.extend({
|
2011-09-19 10:15:17 +00:00
|
|
|
|
template: 'FieldChar',
|
2011-10-25 18:50:30 +00:00
|
|
|
|
init: function (view, node) {
|
|
|
|
|
this._super(view, node);
|
|
|
|
|
this.password = this.node.attrs.password === 'True' || this.node.attrs.password === '1';
|
|
|
|
|
},
|
2011-03-30 14:00:48 +00:00
|
|
|
|
start: function() {
|
|
|
|
|
this._super.apply(this, arguments);
|
[FIX] handling of focus on m2o fields (in editable list row)
* Throw out focusin/focusout: the m2o widget's completion list is
created at the page top (body) so the editable listview can't
generically handle arbitrary widgets via mere focusin/focusout.
* Move responsibility of focus/blur events to the form view and its
widgets.
* Events could not just be named ``focus`` and ``blur`` due to usage
of jquery's event system: jquery will automatically call a method of
the event's name if it exists on the object, which conflicts with
.web.form.Field#focus and leads to an infinite loop (as Field#focus
focuses the field's root element, which triggers the focus event,
which calls the focus method,...) => form-* and widget-*, can be
switched back in trunk
* m2o mess kind-of complex, basically:
- the core input and the menu button behave as blur/focus triggers
- when the autocompletion list is clicked, it will temporarily
remove the focus from the input (blurring it), and put it back
later... on a timer. Issue is the timer, we don't want to rely on
having a bigger timer (as later revisions of the library may
change our timings and it's iffy to rely on timers conserving
order perfectly); on the other hand we know the focus *will* come
back to the input eventually. So we can just avoid propagating
blur iif the blur is the consequence of having clicked on the
completion list.
- roughly the same thing happens when clicking on $drop_down (after
fixing the handling of its final focus to be consistent, as
$drop_down would not re-focus the input if it was *closing* the
completion list)
- pretty sure the menu thing does *not work at all*, but I don't
have the courage of fixing it before committing this part.
Date/datetime widget remains to be handled, basically the core focus
handling is the same as in e.g. a charfield *but* needs to handle (and
ignore) loss of focus from clicking inside the picker
widget. Expecting the level of suck to reach unknown heights.
bzr revid: xmo@openerp.com-20120619072518-lsrhzij5asxt2aea
2012-06-19 07:25:18 +00:00
|
|
|
|
var $input = this.$element.find('input');
|
|
|
|
|
$input.change(this.on_ui_change);
|
|
|
|
|
this.setupFocus($input);
|
2011-03-30 14:00:48 +00:00
|
|
|
|
},
|
|
|
|
|
set_value: function(value) {
|
|
|
|
|
this._super.apply(this, arguments);
|
2011-09-05 11:03:09 +00:00
|
|
|
|
var show_value = openerp.web.format_value(value, this, '');
|
2011-04-05 09:20:50 +00:00
|
|
|
|
this.$element.find('input').val(show_value);
|
2011-09-19 12:24:16 +00:00
|
|
|
|
return show_value;
|
2011-03-30 14:00:48 +00:00
|
|
|
|
},
|
|
|
|
|
update_dom: function() {
|
|
|
|
|
this._super.apply(this, arguments);
|
2012-02-13 16:31:20 +00:00
|
|
|
|
this.$element.find('input').prop('readonly', this.readonly);
|
2011-03-30 14:00:48 +00:00
|
|
|
|
},
|
2011-04-11 11:35:16 +00:00
|
|
|
|
set_value_from_ui: function() {
|
2011-09-05 11:03:09 +00:00
|
|
|
|
this.value = openerp.web.parse_value(this.$element.find('input').val(), this);
|
2011-08-18 17:43:22 +00:00
|
|
|
|
this._super();
|
2011-04-11 11:35:16 +00:00
|
|
|
|
},
|
|
|
|
|
validate: function() {
|
|
|
|
|
this.invalid = false;
|
2011-08-25 09:42:12 +00:00
|
|
|
|
try {
|
2011-09-05 11:03:09 +00:00
|
|
|
|
var value = openerp.web.parse_value(this.$element.find('input').val(), this, '');
|
2011-08-25 09:42:12 +00:00
|
|
|
|
this.invalid = this.required && value === '';
|
|
|
|
|
} catch(e) {
|
|
|
|
|
this.invalid = true;
|
2011-04-11 11:35:16 +00:00
|
|
|
|
}
|
2011-06-20 18:25:49 +00:00
|
|
|
|
},
|
2012-01-12 15:15:27 +00:00
|
|
|
|
focus: function($element) {
|
|
|
|
|
this._super($element || this.$element.find('input:first'));
|
2011-03-30 14:00:48 +00:00
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2012-01-25 14:04:38 +00:00
|
|
|
|
openerp.web.form.FieldID = openerp.web.form.FieldChar.extend({
|
|
|
|
|
update_dom: function() {
|
|
|
|
|
this._super.apply(this, arguments);
|
2012-02-13 16:31:20 +00:00
|
|
|
|
this.$element.find('input').prop('readonly', true);
|
2012-01-25 14:04:38 +00:00
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2011-09-05 11:03:09 +00:00
|
|
|
|
openerp.web.form.FieldEmail = openerp.web.form.FieldChar.extend({
|
2011-09-19 10:15:17 +00:00
|
|
|
|
template: 'FieldEmail',
|
2011-05-12 16:25:50 +00:00
|
|
|
|
start: function() {
|
|
|
|
|
this._super.apply(this, arguments);
|
[FIX] handling of focus on m2o fields (in editable list row)
* Throw out focusin/focusout: the m2o widget's completion list is
created at the page top (body) so the editable listview can't
generically handle arbitrary widgets via mere focusin/focusout.
* Move responsibility of focus/blur events to the form view and its
widgets.
* Events could not just be named ``focus`` and ``blur`` due to usage
of jquery's event system: jquery will automatically call a method of
the event's name if it exists on the object, which conflicts with
.web.form.Field#focus and leads to an infinite loop (as Field#focus
focuses the field's root element, which triggers the focus event,
which calls the focus method,...) => form-* and widget-*, can be
switched back in trunk
* m2o mess kind-of complex, basically:
- the core input and the menu button behave as blur/focus triggers
- when the autocompletion list is clicked, it will temporarily
remove the focus from the input (blurring it), and put it back
later... on a timer. Issue is the timer, we don't want to rely on
having a bigger timer (as later revisions of the library may
change our timings and it's iffy to rely on timers conserving
order perfectly); on the other hand we know the focus *will* come
back to the input eventually. So we can just avoid propagating
blur iif the blur is the consequence of having clicked on the
completion list.
- roughly the same thing happens when clicking on $drop_down (after
fixing the handling of its final focus to be consistent, as
$drop_down would not re-focus the input if it was *closing* the
completion list)
- pretty sure the menu thing does *not work at all*, but I don't
have the courage of fixing it before committing this part.
Date/datetime widget remains to be handled, basically the core focus
handling is the same as in e.g. a charfield *but* needs to handle (and
ignore) loss of focus from clicking inside the picker
widget. Expecting the level of suck to reach unknown heights.
bzr revid: xmo@openerp.com-20120619072518-lsrhzij5asxt2aea
2012-06-19 07:25:18 +00:00
|
|
|
|
var $button = this.$element.find('button');
|
|
|
|
|
$button.click(this.on_button_clicked);
|
|
|
|
|
this.setupFocus($button);
|
2011-05-12 16:25:50 +00:00
|
|
|
|
},
|
|
|
|
|
on_button_clicked: function() {
|
2011-07-27 10:25:19 +00:00
|
|
|
|
if (!this.value || !this.is_valid()) {
|
2011-10-13 10:26:53 +00:00
|
|
|
|
this.do_warn("E-mail error", "Can't send email to invalid e-mail address");
|
2011-05-12 16:25:50 +00:00
|
|
|
|
} else {
|
|
|
|
|
location.href = 'mailto:' + this.value;
|
|
|
|
|
}
|
2011-04-11 11:35:16 +00:00
|
|
|
|
}
|
2011-03-30 14:00:48 +00:00
|
|
|
|
});
|
|
|
|
|
|
2011-09-05 11:03:09 +00:00
|
|
|
|
openerp.web.form.FieldUrl = openerp.web.form.FieldChar.extend({
|
2011-09-19 10:15:17 +00:00
|
|
|
|
template: 'FieldUrl',
|
2011-05-19 13:55:22 +00:00
|
|
|
|
start: function() {
|
|
|
|
|
this._super.apply(this, arguments);
|
[FIX] handling of focus on m2o fields (in editable list row)
* Throw out focusin/focusout: the m2o widget's completion list is
created at the page top (body) so the editable listview can't
generically handle arbitrary widgets via mere focusin/focusout.
* Move responsibility of focus/blur events to the form view and its
widgets.
* Events could not just be named ``focus`` and ``blur`` due to usage
of jquery's event system: jquery will automatically call a method of
the event's name if it exists on the object, which conflicts with
.web.form.Field#focus and leads to an infinite loop (as Field#focus
focuses the field's root element, which triggers the focus event,
which calls the focus method,...) => form-* and widget-*, can be
switched back in trunk
* m2o mess kind-of complex, basically:
- the core input and the menu button behave as blur/focus triggers
- when the autocompletion list is clicked, it will temporarily
remove the focus from the input (blurring it), and put it back
later... on a timer. Issue is the timer, we don't want to rely on
having a bigger timer (as later revisions of the library may
change our timings and it's iffy to rely on timers conserving
order perfectly); on the other hand we know the focus *will* come
back to the input eventually. So we can just avoid propagating
blur iif the blur is the consequence of having clicked on the
completion list.
- roughly the same thing happens when clicking on $drop_down (after
fixing the handling of its final focus to be consistent, as
$drop_down would not re-focus the input if it was *closing* the
completion list)
- pretty sure the menu thing does *not work at all*, but I don't
have the courage of fixing it before committing this part.
Date/datetime widget remains to be handled, basically the core focus
handling is the same as in e.g. a charfield *but* needs to handle (and
ignore) loss of focus from clicking inside the picker
widget. Expecting the level of suck to reach unknown heights.
bzr revid: xmo@openerp.com-20120619072518-lsrhzij5asxt2aea
2012-06-19 07:25:18 +00:00
|
|
|
|
var $button = this.$element.find('button');
|
|
|
|
|
$button.click(this.on_button_clicked);
|
|
|
|
|
this.setupFocus($button);
|
2011-05-19 13:55:22 +00:00
|
|
|
|
},
|
|
|
|
|
on_button_clicked: function() {
|
|
|
|
|
if (!this.value) {
|
2011-10-13 10:26:53 +00:00
|
|
|
|
this.do_warn("Resource error", "This resource is empty");
|
2011-05-19 13:55:22 +00:00
|
|
|
|
} else {
|
|
|
|
|
window.open(this.value);
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-03-30 14:00:48 +00:00
|
|
|
|
});
|
|
|
|
|
|
2011-09-05 11:03:09 +00:00
|
|
|
|
openerp.web.form.FieldFloat = openerp.web.form.FieldChar.extend({
|
2011-10-21 12:22:56 +00:00
|
|
|
|
init: function (view, node) {
|
|
|
|
|
this._super(view, node);
|
|
|
|
|
if (node.attrs.digits) {
|
2012-03-05 19:58:00 +00:00
|
|
|
|
this.digits = py.eval(node.attrs.digits).toJSON();
|
2011-10-21 12:22:56 +00:00
|
|
|
|
} else {
|
|
|
|
|
this.digits = view.fields_view.fields[node.attrs.name].digits;
|
|
|
|
|
}
|
|
|
|
|
},
|
2011-03-30 14:00:48 +00:00
|
|
|
|
set_value: function(value) {
|
2011-06-20 14:43:48 +00:00
|
|
|
|
if (value === false || value === undefined) {
|
2011-05-09 15:19:23 +00:00
|
|
|
|
// As in GTK client, floats default to 0
|
|
|
|
|
value = 0;
|
|
|
|
|
}
|
2011-07-07 08:55:15 +00:00
|
|
|
|
this._super.apply(this, [value]);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2012-01-24 14:58:10 +00:00
|
|
|
|
openerp.web.DateTimeWidget = openerp.web.OldWidget.extend({
|
2011-09-21 10:41:36 +00:00
|
|
|
|
template: "web.datetimepicker",
|
|
|
|
|
jqueryui_object: 'datetimepicker',
|
|
|
|
|
type_of_date: "datetime",
|
2011-11-16 22:27:05 +00:00
|
|
|
|
init: function(parent) {
|
|
|
|
|
this._super(parent);
|
|
|
|
|
this.name = parent.name;
|
|
|
|
|
},
|
2011-04-05 14:34:25 +00:00
|
|
|
|
start: function() {
|
2011-09-14 15:46:10 +00:00
|
|
|
|
var self = this;
|
2011-11-16 22:27:05 +00:00
|
|
|
|
this.$input = this.$element.find('input.oe_datepicker_master');
|
|
|
|
|
this.$input_picker = this.$element.find('input.oe_datepicker_container');
|
|
|
|
|
this.$input.change(this.on_change);
|
2011-09-14 15:46:10 +00:00
|
|
|
|
this.picker({
|
|
|
|
|
onSelect: this.on_picker_select,
|
|
|
|
|
changeMonth: true,
|
|
|
|
|
changeYear: true,
|
|
|
|
|
showWeek: true,
|
2011-11-16 22:27:05 +00:00
|
|
|
|
showButtonPanel: true
|
2011-09-14 15:46:10 +00:00
|
|
|
|
});
|
|
|
|
|
this.$element.find('img.oe_datepicker_trigger').click(function() {
|
2012-02-14 09:44:30 +00:00
|
|
|
|
if (!self.readonly && !self.picker('widget').is(':visible')) {
|
2011-10-19 16:07:09 +00:00
|
|
|
|
self.picker('setDate', self.value ? openerp.web.auto_str_to_date(self.value) : new Date());
|
2011-11-16 22:27:05 +00:00
|
|
|
|
self.$input_picker.show();
|
|
|
|
|
self.picker('show');
|
|
|
|
|
self.$input_picker.hide();
|
2011-09-14 15:46:10 +00:00
|
|
|
|
}
|
|
|
|
|
});
|
2011-09-21 11:50:54 +00:00
|
|
|
|
this.set_readonly(false);
|
2011-09-21 12:31:39 +00:00
|
|
|
|
this.value = false;
|
2011-05-12 10:36:01 +00:00
|
|
|
|
},
|
2011-09-14 15:46:10 +00:00
|
|
|
|
picker: function() {
|
2011-11-16 22:27:05 +00:00
|
|
|
|
return $.fn[this.jqueryui_object].apply(this.$input_picker, arguments);
|
2011-09-14 15:46:10 +00:00
|
|
|
|
},
|
|
|
|
|
on_picker_select: function(text, instance) {
|
|
|
|
|
var date = this.picker('getDate');
|
2011-11-16 22:27:05 +00:00
|
|
|
|
this.$input.val(date ? this.format_client(date) : '').change();
|
2011-09-14 15:46:10 +00:00
|
|
|
|
},
|
2011-05-12 10:36:01 +00:00
|
|
|
|
set_value: function(value) {
|
2011-09-21 10:41:36 +00:00
|
|
|
|
this.value = value;
|
2011-11-16 22:27:05 +00:00
|
|
|
|
this.$input.val(value ? this.format_client(value) : '');
|
2011-09-14 15:46:10 +00:00
|
|
|
|
},
|
|
|
|
|
get_value: function() {
|
2011-09-21 12:31:39 +00:00
|
|
|
|
return this.value;
|
2011-05-12 10:36:01 +00:00
|
|
|
|
},
|
|
|
|
|
set_value_from_ui: function() {
|
2011-11-16 22:27:05 +00:00
|
|
|
|
var value = this.$input.val() || false;
|
2011-09-14 15:46:10 +00:00
|
|
|
|
this.value = this.parse_client(value);
|
2011-05-12 10:36:01 +00:00
|
|
|
|
},
|
2011-09-21 10:41:36 +00:00
|
|
|
|
set_readonly: function(readonly) {
|
|
|
|
|
this.readonly = readonly;
|
2012-02-13 16:31:20 +00:00
|
|
|
|
this.$input.prop('readonly', this.readonly);
|
2011-09-21 10:41:36 +00:00
|
|
|
|
this.$element.find('img.oe_datepicker_trigger').toggleClass('oe_input_icon_disabled', readonly);
|
2011-06-30 09:40:53 +00:00
|
|
|
|
},
|
2011-09-21 10:41:36 +00:00
|
|
|
|
is_valid: function(required) {
|
2011-11-16 22:27:05 +00:00
|
|
|
|
var value = this.$input.val();
|
2011-06-30 12:10:08 +00:00
|
|
|
|
if (value === "") {
|
2011-09-21 10:41:36 +00:00
|
|
|
|
return !required;
|
2011-06-30 12:10:08 +00:00
|
|
|
|
} else {
|
2011-09-14 15:46:10 +00:00
|
|
|
|
try {
|
|
|
|
|
this.parse_client(value);
|
2011-09-21 10:41:36 +00:00
|
|
|
|
return true;
|
2011-09-14 15:46:10 +00:00
|
|
|
|
} catch(e) {
|
2011-09-21 10:41:36 +00:00
|
|
|
|
return false;
|
2011-09-14 15:46:10 +00:00
|
|
|
|
}
|
2011-06-30 12:10:08 +00:00
|
|
|
|
}
|
2011-05-12 10:36:01 +00:00
|
|
|
|
},
|
2011-09-14 15:46:10 +00:00
|
|
|
|
parse_client: function(v) {
|
2011-09-21 10:41:36 +00:00
|
|
|
|
return openerp.web.parse_value(v, {"widget": this.type_of_date});
|
2011-09-14 15:46:10 +00:00
|
|
|
|
},
|
|
|
|
|
format_client: function(v) {
|
2011-09-21 10:41:36 +00:00
|
|
|
|
return openerp.web.format_value(v, {"widget": this.type_of_date});
|
|
|
|
|
},
|
|
|
|
|
on_change: function() {
|
|
|
|
|
if (this.is_valid()) {
|
|
|
|
|
this.set_value_from_ui();
|
|
|
|
|
}
|
2011-09-01 15:22:24 +00:00
|
|
|
|
}
|
2011-03-30 14:00:48 +00:00
|
|
|
|
});
|
|
|
|
|
|
2011-09-21 10:41:36 +00:00
|
|
|
|
openerp.web.DateWidget = openerp.web.DateTimeWidget.extend({
|
|
|
|
|
jqueryui_object: 'datepicker',
|
2011-11-16 22:27:05 +00:00
|
|
|
|
type_of_date: "date"
|
2011-03-30 14:00:48 +00:00
|
|
|
|
});
|
|
|
|
|
|
2011-09-21 10:41:36 +00:00
|
|
|
|
openerp.web.form.FieldDatetime = openerp.web.form.Field.extend({
|
|
|
|
|
template: "EmptyComponent",
|
|
|
|
|
build_widget: function() {
|
|
|
|
|
return new openerp.web.DateTimeWidget(this);
|
|
|
|
|
},
|
|
|
|
|
start: function() {
|
|
|
|
|
var self = this;
|
|
|
|
|
this._super.apply(this, arguments);
|
|
|
|
|
this.datewidget = this.build_widget();
|
2011-11-15 12:45:20 +00:00
|
|
|
|
this.datewidget.on_change.add_last(this.on_ui_change);
|
2011-09-21 10:41:36 +00:00
|
|
|
|
this.datewidget.appendTo(this.$element);
|
[FIX] handling of focus on m2o fields (in editable list row)
* Throw out focusin/focusout: the m2o widget's completion list is
created at the page top (body) so the editable listview can't
generically handle arbitrary widgets via mere focusin/focusout.
* Move responsibility of focus/blur events to the form view and its
widgets.
* Events could not just be named ``focus`` and ``blur`` due to usage
of jquery's event system: jquery will automatically call a method of
the event's name if it exists on the object, which conflicts with
.web.form.Field#focus and leads to an infinite loop (as Field#focus
focuses the field's root element, which triggers the focus event,
which calls the focus method,...) => form-* and widget-*, can be
switched back in trunk
* m2o mess kind-of complex, basically:
- the core input and the menu button behave as blur/focus triggers
- when the autocompletion list is clicked, it will temporarily
remove the focus from the input (blurring it), and put it back
later... on a timer. Issue is the timer, we don't want to rely on
having a bigger timer (as later revisions of the library may
change our timings and it's iffy to rely on timers conserving
order perfectly); on the other hand we know the focus *will* come
back to the input eventually. So we can just avoid propagating
blur iif the blur is the consequence of having clicked on the
completion list.
- roughly the same thing happens when clicking on $drop_down (after
fixing the handling of its final focus to be consistent, as
$drop_down would not re-focus the input if it was *closing* the
completion list)
- pretty sure the menu thing does *not work at all*, but I don't
have the courage of fixing it before committing this part.
Date/datetime widget remains to be handled, basically the core focus
handling is the same as in e.g. a charfield *but* needs to handle (and
ignore) loss of focus from clicking inside the picker
widget. Expecting the level of suck to reach unknown heights.
bzr revid: xmo@openerp.com-20120619072518-lsrhzij5asxt2aea
2012-06-19 07:25:18 +00:00
|
|
|
|
// FIXME: handle focus on datetime field
|
2011-09-21 10:41:36 +00:00
|
|
|
|
},
|
|
|
|
|
set_value: function(value) {
|
|
|
|
|
this._super(value);
|
|
|
|
|
this.datewidget.set_value(value);
|
|
|
|
|
},
|
|
|
|
|
get_value: function() {
|
|
|
|
|
return this.datewidget.get_value();
|
|
|
|
|
},
|
|
|
|
|
update_dom: function() {
|
|
|
|
|
this._super.apply(this, arguments);
|
|
|
|
|
this.datewidget.set_readonly(this.readonly);
|
|
|
|
|
},
|
|
|
|
|
validate: function() {
|
|
|
|
|
this.invalid = !this.datewidget.is_valid(this.required);
|
|
|
|
|
},
|
2012-01-12 15:15:27 +00:00
|
|
|
|
focus: function($element) {
|
2012-01-16 09:02:43 +00:00
|
|
|
|
this._super($element || this.datewidget.$input);
|
2011-09-21 10:41:36 +00:00
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
openerp.web.form.FieldDate = openerp.web.form.FieldDatetime.extend({
|
|
|
|
|
build_widget: function() {
|
|
|
|
|
return new openerp.web.DateWidget(this);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2011-09-05 11:03:09 +00:00
|
|
|
|
openerp.web.form.FieldText = openerp.web.form.Field.extend({
|
2011-09-19 10:15:17 +00:00
|
|
|
|
template: 'FieldText',
|
2011-03-31 16:03:23 +00:00
|
|
|
|
start: function() {
|
|
|
|
|
this._super.apply(this, arguments);
|
[FIX] handling of focus on m2o fields (in editable list row)
* Throw out focusin/focusout: the m2o widget's completion list is
created at the page top (body) so the editable listview can't
generically handle arbitrary widgets via mere focusin/focusout.
* Move responsibility of focus/blur events to the form view and its
widgets.
* Events could not just be named ``focus`` and ``blur`` due to usage
of jquery's event system: jquery will automatically call a method of
the event's name if it exists on the object, which conflicts with
.web.form.Field#focus and leads to an infinite loop (as Field#focus
focuses the field's root element, which triggers the focus event,
which calls the focus method,...) => form-* and widget-*, can be
switched back in trunk
* m2o mess kind-of complex, basically:
- the core input and the menu button behave as blur/focus triggers
- when the autocompletion list is clicked, it will temporarily
remove the focus from the input (blurring it), and put it back
later... on a timer. Issue is the timer, we don't want to rely on
having a bigger timer (as later revisions of the library may
change our timings and it's iffy to rely on timers conserving
order perfectly); on the other hand we know the focus *will* come
back to the input eventually. So we can just avoid propagating
blur iif the blur is the consequence of having clicked on the
completion list.
- roughly the same thing happens when clicking on $drop_down (after
fixing the handling of its final focus to be consistent, as
$drop_down would not re-focus the input if it was *closing* the
completion list)
- pretty sure the menu thing does *not work at all*, but I don't
have the courage of fixing it before committing this part.
Date/datetime widget remains to be handled, basically the core focus
handling is the same as in e.g. a charfield *but* needs to handle (and
ignore) loss of focus from clicking inside the picker
widget. Expecting the level of suck to reach unknown heights.
bzr revid: xmo@openerp.com-20120619072518-lsrhzij5asxt2aea
2012-06-19 07:25:18 +00:00
|
|
|
|
var $textarea = this.$element.find('textarea');
|
|
|
|
|
$textarea.change(this.on_ui_change);
|
2011-12-21 17:16:02 +00:00
|
|
|
|
this.resized = false;
|
[FIX] handling of focus on m2o fields (in editable list row)
* Throw out focusin/focusout: the m2o widget's completion list is
created at the page top (body) so the editable listview can't
generically handle arbitrary widgets via mere focusin/focusout.
* Move responsibility of focus/blur events to the form view and its
widgets.
* Events could not just be named ``focus`` and ``blur`` due to usage
of jquery's event system: jquery will automatically call a method of
the event's name if it exists on the object, which conflicts with
.web.form.Field#focus and leads to an infinite loop (as Field#focus
focuses the field's root element, which triggers the focus event,
which calls the focus method,...) => form-* and widget-*, can be
switched back in trunk
* m2o mess kind-of complex, basically:
- the core input and the menu button behave as blur/focus triggers
- when the autocompletion list is clicked, it will temporarily
remove the focus from the input (blurring it), and put it back
later... on a timer. Issue is the timer, we don't want to rely on
having a bigger timer (as later revisions of the library may
change our timings and it's iffy to rely on timers conserving
order perfectly); on the other hand we know the focus *will* come
back to the input eventually. So we can just avoid propagating
blur iif the blur is the consequence of having clicked on the
completion list.
- roughly the same thing happens when clicking on $drop_down (after
fixing the handling of its final focus to be consistent, as
$drop_down would not re-focus the input if it was *closing* the
completion list)
- pretty sure the menu thing does *not work at all*, but I don't
have the courage of fixing it before committing this part.
Date/datetime widget remains to be handled, basically the core focus
handling is the same as in e.g. a charfield *but* needs to handle (and
ignore) loss of focus from clicking inside the picker
widget. Expecting the level of suck to reach unknown heights.
bzr revid: xmo@openerp.com-20120619072518-lsrhzij5asxt2aea
2012-06-19 07:25:18 +00:00
|
|
|
|
this.setupFocus($textarea);
|
2011-04-05 09:55:49 +00:00
|
|
|
|
},
|
|
|
|
|
set_value: function(value) {
|
|
|
|
|
this._super.apply(this, arguments);
|
2011-09-05 11:03:09 +00:00
|
|
|
|
var show_value = openerp.web.format_value(value, this, '');
|
2011-04-05 14:34:25 +00:00
|
|
|
|
this.$element.find('textarea').val(show_value);
|
2011-12-21 17:16:02 +00:00
|
|
|
|
if (!this.resized && this.view.options.resize_textareas) {
|
|
|
|
|
this.do_resize(this.view.options.resize_textareas);
|
|
|
|
|
this.resized = true;
|
|
|
|
|
}
|
2011-04-05 09:55:49 +00:00
|
|
|
|
},
|
2011-04-05 14:34:25 +00:00
|
|
|
|
update_dom: function() {
|
|
|
|
|
this._super.apply(this, arguments);
|
2012-02-13 16:31:20 +00:00
|
|
|
|
this.$element.find('textarea').prop('readonly', this.readonly);
|
2011-04-05 14:34:25 +00:00
|
|
|
|
},
|
2011-04-11 11:35:16 +00:00
|
|
|
|
set_value_from_ui: function() {
|
2011-09-05 11:03:09 +00:00
|
|
|
|
this.value = openerp.web.parse_value(this.$element.find('textarea').val(), this);
|
2011-08-18 17:43:22 +00:00
|
|
|
|
this._super();
|
2011-04-11 11:35:16 +00:00
|
|
|
|
},
|
|
|
|
|
validate: function() {
|
|
|
|
|
this.invalid = false;
|
2011-08-25 09:42:12 +00:00
|
|
|
|
try {
|
2011-09-05 11:03:09 +00:00
|
|
|
|
var value = openerp.web.parse_value(this.$element.find('textarea').val(), this, '');
|
2011-08-25 09:42:12 +00:00
|
|
|
|
this.invalid = this.required && value === '';
|
|
|
|
|
} catch(e) {
|
|
|
|
|
this.invalid = true;
|
2011-04-11 11:35:16 +00:00
|
|
|
|
}
|
2011-06-20 18:25:49 +00:00
|
|
|
|
},
|
2012-01-12 15:15:27 +00:00
|
|
|
|
focus: function($element) {
|
|
|
|
|
this._super($element || this.$element.find('textarea:first'));
|
2011-12-21 17:16:02 +00:00
|
|
|
|
},
|
|
|
|
|
do_resize: function(max_height) {
|
|
|
|
|
max_height = parseInt(max_height, 10);
|
|
|
|
|
var $input = this.$element.find('textarea'),
|
|
|
|
|
$div = $('<div style="position: absolute; z-index: 1000; top: 0"/>').width($input.width()),
|
|
|
|
|
new_height;
|
|
|
|
|
$div.text($input.val());
|
|
|
|
|
_.each('font-family,font-size,white-space'.split(','), function(style) {
|
|
|
|
|
$div.css(style, $input.css(style));
|
|
|
|
|
});
|
2012-01-04 15:53:29 +00:00
|
|
|
|
$div.appendTo($('body'));
|
2011-12-21 17:16:02 +00:00
|
|
|
|
new_height = $div.height();
|
|
|
|
|
if (new_height < 90) {
|
|
|
|
|
new_height = 90;
|
|
|
|
|
}
|
|
|
|
|
if (!isNaN(max_height) && new_height > max_height) {
|
|
|
|
|
new_height = max_height;
|
|
|
|
|
}
|
|
|
|
|
$div.remove();
|
|
|
|
|
$input.height(new_height);
|
|
|
|
|
},
|
|
|
|
|
reset: function() {
|
|
|
|
|
this.resized = false;
|
2011-03-30 14:00:48 +00:00
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2011-09-05 11:03:09 +00:00
|
|
|
|
openerp.web.form.FieldBoolean = openerp.web.form.Field.extend({
|
2011-09-19 10:15:17 +00:00
|
|
|
|
template: 'FieldBoolean',
|
2011-04-05 09:55:49 +00:00
|
|
|
|
start: function() {
|
2011-04-05 14:34:25 +00:00
|
|
|
|
var self = this;
|
2011-04-05 09:55:49 +00:00
|
|
|
|
this._super.apply(this, arguments);
|
[FIX] handling of focus on m2o fields (in editable list row)
* Throw out focusin/focusout: the m2o widget's completion list is
created at the page top (body) so the editable listview can't
generically handle arbitrary widgets via mere focusin/focusout.
* Move responsibility of focus/blur events to the form view and its
widgets.
* Events could not just be named ``focus`` and ``blur`` due to usage
of jquery's event system: jquery will automatically call a method of
the event's name if it exists on the object, which conflicts with
.web.form.Field#focus and leads to an infinite loop (as Field#focus
focuses the field's root element, which triggers the focus event,
which calls the focus method,...) => form-* and widget-*, can be
switched back in trunk
* m2o mess kind-of complex, basically:
- the core input and the menu button behave as blur/focus triggers
- when the autocompletion list is clicked, it will temporarily
remove the focus from the input (blurring it), and put it back
later... on a timer. Issue is the timer, we don't want to rely on
having a bigger timer (as later revisions of the library may
change our timings and it's iffy to rely on timers conserving
order perfectly); on the other hand we know the focus *will* come
back to the input eventually. So we can just avoid propagating
blur iif the blur is the consequence of having clicked on the
completion list.
- roughly the same thing happens when clicking on $drop_down (after
fixing the handling of its final focus to be consistent, as
$drop_down would not re-focus the input if it was *closing* the
completion list)
- pretty sure the menu thing does *not work at all*, but I don't
have the courage of fixing it before committing this part.
Date/datetime widget remains to be handled, basically the core focus
handling is the same as in e.g. a charfield *but* needs to handle (and
ignore) loss of focus from clicking inside the picker
widget. Expecting the level of suck to reach unknown heights.
bzr revid: xmo@openerp.com-20120619072518-lsrhzij5asxt2aea
2012-06-19 07:25:18 +00:00
|
|
|
|
var $input = this.$element.find('input');
|
|
|
|
|
$input.click(self.on_ui_change);
|
|
|
|
|
this.setupFocus($input);
|
2011-04-05 09:55:49 +00:00
|
|
|
|
},
|
|
|
|
|
set_value: function(value) {
|
|
|
|
|
this._super.apply(this, arguments);
|
2011-04-05 14:34:25 +00:00
|
|
|
|
this.$element.find('input')[0].checked = value;
|
2011-04-05 09:55:49 +00:00
|
|
|
|
},
|
2011-04-11 11:35:16 +00:00
|
|
|
|
set_value_from_ui: function() {
|
|
|
|
|
this.value = this.$element.find('input').is(':checked');
|
2011-08-18 17:43:22 +00:00
|
|
|
|
this._super();
|
2011-04-11 11:35:16 +00:00
|
|
|
|
},
|
2011-04-05 14:34:25 +00:00
|
|
|
|
update_dom: function() {
|
|
|
|
|
this._super.apply(this, arguments);
|
2011-12-14 13:23:06 +00:00
|
|
|
|
this.$element.find('input').prop('disabled', this.readonly);
|
2011-04-05 14:34:25 +00:00
|
|
|
|
},
|
2012-01-12 15:15:27 +00:00
|
|
|
|
focus: function($element) {
|
|
|
|
|
this._super($element || this.$element.find('input:first'));
|
2011-03-30 14:00:48 +00:00
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2011-09-05 11:03:09 +00:00
|
|
|
|
openerp.web.form.FieldProgressBar = openerp.web.form.Field.extend({
|
2011-09-19 10:15:17 +00:00
|
|
|
|
template: 'FieldProgressBar',
|
2011-04-12 09:26:31 +00:00
|
|
|
|
start: function() {
|
|
|
|
|
this._super.apply(this, arguments);
|
|
|
|
|
this.$element.find('div').progressbar({
|
|
|
|
|
value: this.value,
|
|
|
|
|
disabled: this.readonly
|
|
|
|
|
});
|
2011-05-09 14:10:01 +00:00
|
|
|
|
},
|
|
|
|
|
set_value: function(value) {
|
|
|
|
|
this._super.apply(this, arguments);
|
|
|
|
|
var show_value = Number(value);
|
2011-07-04 12:10:57 +00:00
|
|
|
|
if (isNaN(show_value)) {
|
2011-05-09 14:10:01 +00:00
|
|
|
|
show_value = 0;
|
|
|
|
|
}
|
2012-01-16 10:43:30 +00:00
|
|
|
|
var formatted_value = openerp.web.format_value(show_value, { type : 'float' }, '0');
|
|
|
|
|
this.$element.find('div').progressbar('option', 'value', show_value).find('span').html(formatted_value + '%');
|
2011-04-12 09:26:31 +00:00
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2011-09-05 11:03:09 +00:00
|
|
|
|
openerp.web.form.FieldTextXml = openerp.web.form.Field.extend({
|
2011-03-30 14:00:48 +00:00
|
|
|
|
// to replace view editor
|
|
|
|
|
});
|
|
|
|
|
|
2011-09-05 11:03:09 +00:00
|
|
|
|
openerp.web.form.FieldSelection = openerp.web.form.Field.extend({
|
2011-09-19 10:15:17 +00:00
|
|
|
|
template: 'FieldSelection',
|
2011-03-30 14:00:48 +00:00
|
|
|
|
init: function(view, node) {
|
2011-09-07 16:33:15 +00:00
|
|
|
|
var self = this;
|
2011-03-30 14:00:48 +00:00
|
|
|
|
this._super(view, node);
|
2011-11-24 13:46:27 +00:00
|
|
|
|
this.values = _.clone(this.field.selection);
|
2011-09-07 16:33:15 +00:00
|
|
|
|
_.each(this.values, function(v, i) {
|
|
|
|
|
if (v[0] === false && v[1] === '') {
|
|
|
|
|
self.values.splice(i, 1);
|
|
|
|
|
}
|
2011-07-07 14:19:24 +00:00
|
|
|
|
});
|
2011-09-07 16:33:15 +00:00
|
|
|
|
this.values.unshift([false, '']);
|
2011-03-30 14:00:48 +00:00
|
|
|
|
},
|
2011-04-05 14:34:25 +00:00
|
|
|
|
start: function() {
|
2011-07-08 12:38:53 +00:00
|
|
|
|
// Flag indicating whether we're in an event chain containing a change
|
|
|
|
|
// event on the select, in order to know what to do on keyup[RETURN]:
|
|
|
|
|
// * If the user presses [RETURN] as part of changing the value of a
|
|
|
|
|
// selection, we should just let the value change and not let the
|
|
|
|
|
// event broadcast further (e.g. to validating the current state of
|
|
|
|
|
// the form in editable list view, which would lead to saving the
|
|
|
|
|
// current row or switching to the next one)
|
|
|
|
|
// * If the user presses [RETURN] with a select closed (side-effect:
|
|
|
|
|
// also if the user opened the select and pressed [RETURN] without
|
|
|
|
|
// changing the selected value), takes the action as validating the
|
|
|
|
|
// row
|
|
|
|
|
var ischanging = false;
|
2011-04-05 14:34:25 +00:00
|
|
|
|
this._super.apply(this, arguments);
|
[FIX] handling of focus on m2o fields (in editable list row)
* Throw out focusin/focusout: the m2o widget's completion list is
created at the page top (body) so the editable listview can't
generically handle arbitrary widgets via mere focusin/focusout.
* Move responsibility of focus/blur events to the form view and its
widgets.
* Events could not just be named ``focus`` and ``blur`` due to usage
of jquery's event system: jquery will automatically call a method of
the event's name if it exists on the object, which conflicts with
.web.form.Field#focus and leads to an infinite loop (as Field#focus
focuses the field's root element, which triggers the focus event,
which calls the focus method,...) => form-* and widget-*, can be
switched back in trunk
* m2o mess kind-of complex, basically:
- the core input and the menu button behave as blur/focus triggers
- when the autocompletion list is clicked, it will temporarily
remove the focus from the input (blurring it), and put it back
later... on a timer. Issue is the timer, we don't want to rely on
having a bigger timer (as later revisions of the library may
change our timings and it's iffy to rely on timers conserving
order perfectly); on the other hand we know the focus *will* come
back to the input eventually. So we can just avoid propagating
blur iif the blur is the consequence of having clicked on the
completion list.
- roughly the same thing happens when clicking on $drop_down (after
fixing the handling of its final focus to be consistent, as
$drop_down would not re-focus the input if it was *closing* the
completion list)
- pretty sure the menu thing does *not work at all*, but I don't
have the courage of fixing it before committing this part.
Date/datetime widget remains to be handled, basically the core focus
handling is the same as in e.g. a charfield *but* needs to handle (and
ignore) loss of focus from clicking inside the picker
widget. Expecting the level of suck to reach unknown heights.
bzr revid: xmo@openerp.com-20120619072518-lsrhzij5asxt2aea
2012-06-19 07:25:18 +00:00
|
|
|
|
var $select = this.$element.find('select');
|
|
|
|
|
$select
|
2011-07-08 12:38:53 +00:00
|
|
|
|
.change(this.on_ui_change)
|
|
|
|
|
.change(function () { ischanging = true; })
|
|
|
|
|
.click(function () { ischanging = false; })
|
|
|
|
|
.keyup(function (e) {
|
|
|
|
|
if (e.which !== 13 || !ischanging) { return; }
|
|
|
|
|
e.stopPropagation();
|
|
|
|
|
ischanging = false;
|
|
|
|
|
});
|
[FIX] handling of focus on m2o fields (in editable list row)
* Throw out focusin/focusout: the m2o widget's completion list is
created at the page top (body) so the editable listview can't
generically handle arbitrary widgets via mere focusin/focusout.
* Move responsibility of focus/blur events to the form view and its
widgets.
* Events could not just be named ``focus`` and ``blur`` due to usage
of jquery's event system: jquery will automatically call a method of
the event's name if it exists on the object, which conflicts with
.web.form.Field#focus and leads to an infinite loop (as Field#focus
focuses the field's root element, which triggers the focus event,
which calls the focus method,...) => form-* and widget-*, can be
switched back in trunk
* m2o mess kind-of complex, basically:
- the core input and the menu button behave as blur/focus triggers
- when the autocompletion list is clicked, it will temporarily
remove the focus from the input (blurring it), and put it back
later... on a timer. Issue is the timer, we don't want to rely on
having a bigger timer (as later revisions of the library may
change our timings and it's iffy to rely on timers conserving
order perfectly); on the other hand we know the focus *will* come
back to the input eventually. So we can just avoid propagating
blur iif the blur is the consequence of having clicked on the
completion list.
- roughly the same thing happens when clicking on $drop_down (after
fixing the handling of its final focus to be consistent, as
$drop_down would not re-focus the input if it was *closing* the
completion list)
- pretty sure the menu thing does *not work at all*, but I don't
have the courage of fixing it before committing this part.
Date/datetime widget remains to be handled, basically the core focus
handling is the same as in e.g. a charfield *but* needs to handle (and
ignore) loss of focus from clicking inside the picker
widget. Expecting the level of suck to reach unknown heights.
bzr revid: xmo@openerp.com-20120619072518-lsrhzij5asxt2aea
2012-06-19 07:25:18 +00:00
|
|
|
|
this.setupFocus($select);
|
2011-04-05 14:34:25 +00:00
|
|
|
|
},
|
2011-03-30 14:00:48 +00:00
|
|
|
|
set_value: function(value) {
|
2011-07-07 14:19:24 +00:00
|
|
|
|
value = value === null ? false : value;
|
|
|
|
|
value = value instanceof Array ? value[0] : value;
|
|
|
|
|
this._super(value);
|
2011-09-07 16:33:15 +00:00
|
|
|
|
var index = 0;
|
|
|
|
|
for (var i = 0, ii = this.values.length; i < ii; i++) {
|
|
|
|
|
if (this.values[i][0] === value) index = i;
|
|
|
|
|
}
|
|
|
|
|
this.$element.find('select')[0].selectedIndex = index;
|
2011-03-30 14:00:48 +00:00
|
|
|
|
},
|
2011-04-11 11:35:16 +00:00
|
|
|
|
set_value_from_ui: function() {
|
2011-09-07 16:33:15 +00:00
|
|
|
|
this.value = this.values[this.$element.find('select')[0].selectedIndex][0];
|
2011-08-18 17:43:22 +00:00
|
|
|
|
this._super();
|
2011-04-11 11:35:16 +00:00
|
|
|
|
},
|
2011-04-05 14:34:25 +00:00
|
|
|
|
update_dom: function() {
|
|
|
|
|
this._super.apply(this, arguments);
|
2011-12-14 13:23:06 +00:00
|
|
|
|
this.$element.find('select').prop('disabled', this.readonly);
|
2011-04-05 14:34:25 +00:00
|
|
|
|
},
|
2011-04-11 11:35:16 +00:00
|
|
|
|
validate: function() {
|
2011-09-07 16:33:15 +00:00
|
|
|
|
var value = this.values[this.$element.find('select')[0].selectedIndex];
|
|
|
|
|
this.invalid = !(value && !(this.required && value[0] === false));
|
2011-06-20 18:25:49 +00:00
|
|
|
|
},
|
2012-01-12 15:15:27 +00:00
|
|
|
|
focus: function($element) {
|
|
|
|
|
this._super($element || this.$element.find('select:first'));
|
2011-03-30 14:00:48 +00:00
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2011-06-10 13:22:37 +00:00
|
|
|
|
// jquery autocomplete tweak to allow html
|
2011-06-20 14:43:48 +00:00
|
|
|
|
(function() {
|
2011-06-10 13:22:37 +00:00
|
|
|
|
var proto = $.ui.autocomplete.prototype,
|
|
|
|
|
initSource = proto._initSource;
|
2011-06-20 14:43:48 +00:00
|
|
|
|
|
2011-06-10 13:22:37 +00:00
|
|
|
|
function filter( array, term ) {
|
|
|
|
|
var matcher = new RegExp( $.ui.autocomplete.escapeRegex(term), "i" );
|
|
|
|
|
return $.grep( array, function(value) {
|
|
|
|
|
return matcher.test( $( "<div>" ).html( value.label || value.value || value ).text() );
|
|
|
|
|
});
|
2011-06-10 10:31:55 +00:00
|
|
|
|
}
|
2011-06-20 14:43:48 +00:00
|
|
|
|
|
2011-06-10 13:22:37 +00:00
|
|
|
|
$.extend( proto, {
|
|
|
|
|
_initSource: function() {
|
|
|
|
|
if ( this.options.html && $.isArray(this.options.source) ) {
|
|
|
|
|
this.source = function( request, response ) {
|
|
|
|
|
response( filter( this.options.source, request.term ) );
|
|
|
|
|
};
|
|
|
|
|
} else {
|
|
|
|
|
initSource.call( this );
|
|
|
|
|
}
|
|
|
|
|
},
|
2011-06-20 14:43:48 +00:00
|
|
|
|
|
2011-06-10 13:22:37 +00:00
|
|
|
|
_renderItem: function( ul, item) {
|
|
|
|
|
return $( "<li></li>" )
|
|
|
|
|
.data( "item.autocomplete", item )
|
|
|
|
|
.append( $( "<a></a>" )[ this.options.html ? "html" : "text" ]( item.label ) )
|
|
|
|
|
.appendTo( ul );
|
|
|
|
|
}
|
|
|
|
|
});
|
2011-06-20 14:43:48 +00:00
|
|
|
|
})();
|
2011-06-10 10:31:55 +00:00
|
|
|
|
|
2011-09-05 11:03:09 +00:00
|
|
|
|
openerp.web.form.dialog = function(content, options) {
|
2011-07-14 10:09:33 +00:00
|
|
|
|
options = _.extend({
|
|
|
|
|
width: '90%',
|
2012-01-10 14:51:29 +00:00
|
|
|
|
height: 'auto',
|
2012-01-10 15:21:10 +00:00
|
|
|
|
min_width: '800px'
|
2011-07-14 10:09:33 +00:00
|
|
|
|
}, options || {});
|
2012-01-11 11:06:48 +00:00
|
|
|
|
var dialog = new openerp.web.Dialog(null, options, content).open();
|
|
|
|
|
return dialog.$element;
|
2011-09-06 08:58:53 +00:00
|
|
|
|
};
|
2011-07-14 10:09:33 +00:00
|
|
|
|
|
2011-09-05 11:03:09 +00:00
|
|
|
|
openerp.web.form.FieldMany2One = openerp.web.form.Field.extend({
|
2011-09-19 10:15:17 +00:00
|
|
|
|
template: 'FieldMany2One',
|
2011-03-30 14:00:48 +00:00
|
|
|
|
init: function(view, node) {
|
|
|
|
|
this._super(view, node);
|
2011-06-06 16:50:43 +00:00
|
|
|
|
this.limit = 7;
|
2011-06-08 15:29:44 +00:00
|
|
|
|
this.value = null;
|
2011-06-09 17:08:51 +00:00
|
|
|
|
this.cm_id = _.uniqueId('m2o_cm_');
|
2011-06-10 13:22:37 +00:00
|
|
|
|
this.last_search = [];
|
2011-06-23 12:57:25 +00:00
|
|
|
|
this.tmp_value = undefined;
|
2011-04-27 12:20:51 +00:00
|
|
|
|
},
|
|
|
|
|
start: function() {
|
2011-06-06 16:50:43 +00:00
|
|
|
|
this._super();
|
|
|
|
|
var self = this;
|
|
|
|
|
this.$input = this.$element.find("input");
|
2011-06-10 14:58:01 +00:00
|
|
|
|
this.$drop_down = this.$element.find(".oe-m2o-drop-down-button");
|
|
|
|
|
this.$menu_btn = this.$element.find(".oe-m2o-cm-button");
|
2011-09-02 05:56:32 +00:00
|
|
|
|
|
2011-08-25 14:37:45 +00:00
|
|
|
|
// context menu
|
2011-08-25 14:21:26 +00:00
|
|
|
|
var init_context_menu_def = $.Deferred().then(function(e) {
|
2011-09-05 11:03:09 +00:00
|
|
|
|
var rdataset = new openerp.web.DataSetStatic(self, "ir.values", self.build_context());
|
2011-08-25 15:17:01 +00:00
|
|
|
|
rdataset.call("get", ['action', 'client_action_relate',
|
|
|
|
|
[[self.field.relation, false]], false, rdataset.get_context()], false, 0)
|
|
|
|
|
.then(function(result) {
|
|
|
|
|
self.related_entries = result;
|
2011-09-02 05:56:32 +00:00
|
|
|
|
|
2011-08-25 15:17:01 +00:00
|
|
|
|
var $cmenu = $("#" + self.cm_id);
|
|
|
|
|
$cmenu.append(QWeb.render("FieldMany2One.context_menu", {widget: self}));
|
|
|
|
|
var bindings = {};
|
|
|
|
|
bindings[self.cm_id + "_search"] = function() {
|
2012-02-28 14:41:11 +00:00
|
|
|
|
if (self.readonly)
|
|
|
|
|
return;
|
2011-08-25 15:17:01 +00:00
|
|
|
|
self._search_create_popup("search");
|
|
|
|
|
};
|
|
|
|
|
bindings[self.cm_id + "_create"] = function() {
|
2012-02-28 14:41:11 +00:00
|
|
|
|
if (self.readonly)
|
|
|
|
|
return;
|
2011-08-25 15:17:01 +00:00
|
|
|
|
self._search_create_popup("form");
|
|
|
|
|
};
|
|
|
|
|
bindings[self.cm_id + "_open"] = function() {
|
|
|
|
|
if (!self.value) {
|
2012-06-19 09:36:51 +00:00
|
|
|
|
self.focus();
|
2011-08-25 15:17:01 +00:00
|
|
|
|
return;
|
2011-08-25 14:21:26 +00:00
|
|
|
|
}
|
2011-09-05 11:03:09 +00:00
|
|
|
|
var pop = new openerp.web.form.FormOpenPopup(self.view);
|
2011-12-22 09:40:12 +00:00
|
|
|
|
pop.show_element(
|
|
|
|
|
self.field.relation,
|
|
|
|
|
self.value[0],
|
|
|
|
|
self.build_context(),
|
|
|
|
|
{
|
|
|
|
|
title: _t("Open: ") + (self.string || self.name)
|
|
|
|
|
}
|
|
|
|
|
);
|
2011-08-25 15:17:01 +00:00
|
|
|
|
pop.on_write_completed.add_last(function() {
|
|
|
|
|
self.set_value(self.value[0]);
|
2012-06-19 09:36:51 +00:00
|
|
|
|
self.focus();
|
2011-08-25 15:17:01 +00:00
|
|
|
|
});
|
|
|
|
|
};
|
2011-08-25 17:04:10 +00:00
|
|
|
|
_.each(_.range(self.related_entries.length), function(i) {
|
|
|
|
|
bindings[self.cm_id + "_related_" + i] = function() {
|
|
|
|
|
self.open_related(self.related_entries[i]);
|
|
|
|
|
};
|
|
|
|
|
});
|
2012-01-11 14:02:22 +00:00
|
|
|
|
var cmenu = self.$menu_btn.contextMenu(self.cm_id, {'noRightClick': true,
|
2011-08-25 15:17:01 +00:00
|
|
|
|
bindings: bindings, itemStyle: {"color": ""},
|
|
|
|
|
onContextMenu: function() {
|
|
|
|
|
if(self.value) {
|
|
|
|
|
$("#" + self.cm_id + " .oe_m2o_menu_item_mandatory").removeClass("oe-m2o-disabled-cm");
|
|
|
|
|
} else {
|
|
|
|
|
$("#" + self.cm_id + " .oe_m2o_menu_item_mandatory").addClass("oe-m2o-disabled-cm");
|
|
|
|
|
}
|
2011-10-24 15:20:56 +00:00
|
|
|
|
if (!self.readonly) {
|
|
|
|
|
$("#" + self.cm_id + " .oe_m2o_menu_item_noreadonly").removeClass("oe-m2o-disabled-cm");
|
|
|
|
|
} else {
|
|
|
|
|
$("#" + self.cm_id + " .oe_m2o_menu_item_noreadonly").addClass("oe-m2o-disabled-cm");
|
|
|
|
|
}
|
2011-08-25 15:17:01 +00:00
|
|
|
|
return true;
|
|
|
|
|
}, menuStyle: {width: "200px"}
|
|
|
|
|
});
|
2011-12-19 16:28:05 +00:00
|
|
|
|
$.async_when().then(function() {self.$menu_btn.trigger(e);});
|
2011-08-25 14:21:26 +00:00
|
|
|
|
});
|
2011-06-10 13:22:37 +00:00
|
|
|
|
});
|
2011-08-25 14:21:26 +00:00
|
|
|
|
var ctx_callback = function(e) {init_context_menu_def.resolve(e); e.preventDefault()};
|
|
|
|
|
this.$menu_btn.click(ctx_callback);
|
2011-06-20 14:43:48 +00:00
|
|
|
|
|
2011-06-10 10:31:55 +00:00
|
|
|
|
// some behavior for input
|
|
|
|
|
this.$input.keyup(function() {
|
2011-06-08 15:29:44 +00:00
|
|
|
|
if (self.$input.val() === "") {
|
2011-06-10 14:58:01 +00:00
|
|
|
|
self._change_int_value(null);
|
2011-06-10 10:31:55 +00:00
|
|
|
|
} else if (self.value === null || (self.value && self.$input.val() !== self.value[1])) {
|
2011-06-10 14:58:01 +00:00
|
|
|
|
self._change_int_value(undefined);
|
2011-06-08 15:29:44 +00:00
|
|
|
|
}
|
|
|
|
|
});
|
2011-06-09 17:36:44 +00:00
|
|
|
|
this.$drop_down.click(function() {
|
2011-10-24 15:20:56 +00:00
|
|
|
|
if (self.readonly)
|
|
|
|
|
return;
|
2011-06-08 15:47:44 +00:00
|
|
|
|
if (self.$input.autocomplete("widget").is(":visible")) {
|
2011-06-08 15:29:44 +00:00
|
|
|
|
self.$input.autocomplete("close");
|
|
|
|
|
} else {
|
2011-06-10 13:55:26 +00:00
|
|
|
|
if (self.value) {
|
|
|
|
|
self.$input.autocomplete("search", "");
|
|
|
|
|
} else {
|
|
|
|
|
self.$input.autocomplete("search");
|
|
|
|
|
}
|
2011-06-08 15:29:44 +00:00
|
|
|
|
}
|
[FIX] handling of focus on m2o fields (in editable list row)
* Throw out focusin/focusout: the m2o widget's completion list is
created at the page top (body) so the editable listview can't
generically handle arbitrary widgets via mere focusin/focusout.
* Move responsibility of focus/blur events to the form view and its
widgets.
* Events could not just be named ``focus`` and ``blur`` due to usage
of jquery's event system: jquery will automatically call a method of
the event's name if it exists on the object, which conflicts with
.web.form.Field#focus and leads to an infinite loop (as Field#focus
focuses the field's root element, which triggers the focus event,
which calls the focus method,...) => form-* and widget-*, can be
switched back in trunk
* m2o mess kind-of complex, basically:
- the core input and the menu button behave as blur/focus triggers
- when the autocompletion list is clicked, it will temporarily
remove the focus from the input (blurring it), and put it back
later... on a timer. Issue is the timer, we don't want to rely on
having a bigger timer (as later revisions of the library may
change our timings and it's iffy to rely on timers conserving
order perfectly); on the other hand we know the focus *will* come
back to the input eventually. So we can just avoid propagating
blur iif the blur is the consequence of having clicked on the
completion list.
- roughly the same thing happens when clicking on $drop_down (after
fixing the handling of its final focus to be consistent, as
$drop_down would not re-focus the input if it was *closing* the
completion list)
- pretty sure the menu thing does *not work at all*, but I don't
have the courage of fixing it before committing this part.
Date/datetime widget remains to be handled, basically the core focus
handling is the same as in e.g. a charfield *but* needs to handle (and
ignore) loss of focus from clicking inside the picker
widget. Expecting the level of suck to reach unknown heights.
bzr revid: xmo@openerp.com-20120619072518-lsrhzij5asxt2aea
2012-06-19 07:25:18 +00:00
|
|
|
|
self.$input.focus();
|
2011-06-06 16:50:43 +00:00
|
|
|
|
});
|
2011-06-10 13:22:37 +00:00
|
|
|
|
var anyoneLoosesFocus = function() {
|
|
|
|
|
if (!self.$input.is(":focus") &&
|
|
|
|
|
!self.$input.autocomplete("widget").is(":visible") &&
|
|
|
|
|
!self.value) {
|
2011-09-06 08:58:53 +00:00
|
|
|
|
if (self.value === undefined && self.last_search.length > 0) {
|
2011-06-10 14:58:01 +00:00
|
|
|
|
self._change_int_ext_value(self.last_search[0]);
|
2011-06-10 13:22:37 +00:00
|
|
|
|
} else {
|
2011-06-10 14:58:01 +00:00
|
|
|
|
self._change_int_ext_value(null);
|
2011-06-10 13:22:37 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2011-09-06 08:58:53 +00:00
|
|
|
|
};
|
2011-06-10 13:22:37 +00:00
|
|
|
|
this.$input.focusout(anyoneLoosesFocus);
|
2011-06-20 14:43:48 +00:00
|
|
|
|
|
2011-07-12 14:16:10 +00:00
|
|
|
|
var isSelecting = false;
|
2011-06-10 10:31:55 +00:00
|
|
|
|
// autocomplete
|
2011-06-06 16:50:43 +00:00
|
|
|
|
this.$input.autocomplete({
|
2011-06-08 15:29:44 +00:00
|
|
|
|
source: function(req, resp) { self.get_search_result(req, resp); },
|
2011-06-09 13:20:04 +00:00
|
|
|
|
select: function(event, ui) {
|
2011-07-12 14:16:10 +00:00
|
|
|
|
isSelecting = true;
|
2011-06-08 15:29:44 +00:00
|
|
|
|
var item = ui.item;
|
|
|
|
|
if (item.id) {
|
2011-06-10 14:58:01 +00:00
|
|
|
|
self._change_int_value([item.id, item.name]);
|
2011-06-08 15:29:44 +00:00
|
|
|
|
} else if (item.action) {
|
2011-06-10 14:58:01 +00:00
|
|
|
|
self._change_int_value(undefined);
|
2011-06-08 15:29:44 +00:00
|
|
|
|
item.action();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2011-06-10 10:31:55 +00:00
|
|
|
|
},
|
2011-06-10 12:15:45 +00:00
|
|
|
|
focus: function(e, ui) {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
},
|
2011-06-10 13:22:37 +00:00
|
|
|
|
html: true,
|
2011-06-10 13:55:26 +00:00
|
|
|
|
close: anyoneLoosesFocus,
|
2011-06-10 13:58:35 +00:00
|
|
|
|
minLength: 0,
|
|
|
|
|
delay: 0
|
2011-06-06 16:50:43 +00:00
|
|
|
|
});
|
[FIX] handling of focus on m2o fields (in editable list row)
* Throw out focusin/focusout: the m2o widget's completion list is
created at the page top (body) so the editable listview can't
generically handle arbitrary widgets via mere focusin/focusout.
* Move responsibility of focus/blur events to the form view and its
widgets.
* Events could not just be named ``focus`` and ``blur`` due to usage
of jquery's event system: jquery will automatically call a method of
the event's name if it exists on the object, which conflicts with
.web.form.Field#focus and leads to an infinite loop (as Field#focus
focuses the field's root element, which triggers the focus event,
which calls the focus method,...) => form-* and widget-*, can be
switched back in trunk
* m2o mess kind-of complex, basically:
- the core input and the menu button behave as blur/focus triggers
- when the autocompletion list is clicked, it will temporarily
remove the focus from the input (blurring it), and put it back
later... on a timer. Issue is the timer, we don't want to rely on
having a bigger timer (as later revisions of the library may
change our timings and it's iffy to rely on timers conserving
order perfectly); on the other hand we know the focus *will* come
back to the input eventually. So we can just avoid propagating
blur iif the blur is the consequence of having clicked on the
completion list.
- roughly the same thing happens when clicking on $drop_down (after
fixing the handling of its final focus to be consistent, as
$drop_down would not re-focus the input if it was *closing* the
completion list)
- pretty sure the menu thing does *not work at all*, but I don't
have the courage of fixing it before committing this part.
Date/datetime widget remains to be handled, basically the core focus
handling is the same as in e.g. a charfield *but* needs to handle (and
ignore) loss of focus from clicking inside the picker
widget. Expecting the level of suck to reach unknown heights.
bzr revid: xmo@openerp.com-20120619072518-lsrhzij5asxt2aea
2012-06-19 07:25:18 +00:00
|
|
|
|
|
2011-07-12 14:16:10 +00:00
|
|
|
|
// used to correct a bug when selecting an element by pushing 'enter' in an editable list
|
|
|
|
|
this.$input.keyup(function(e) {
|
|
|
|
|
if (e.which === 13) {
|
|
|
|
|
if (isSelecting)
|
|
|
|
|
e.stopPropagation();
|
|
|
|
|
}
|
|
|
|
|
isSelecting = false;
|
|
|
|
|
});
|
[FIX] handling of focus on m2o fields (in editable list row)
* Throw out focusin/focusout: the m2o widget's completion list is
created at the page top (body) so the editable listview can't
generically handle arbitrary widgets via mere focusin/focusout.
* Move responsibility of focus/blur events to the form view and its
widgets.
* Events could not just be named ``focus`` and ``blur`` due to usage
of jquery's event system: jquery will automatically call a method of
the event's name if it exists on the object, which conflicts with
.web.form.Field#focus and leads to an infinite loop (as Field#focus
focuses the field's root element, which triggers the focus event,
which calls the focus method,...) => form-* and widget-*, can be
switched back in trunk
* m2o mess kind-of complex, basically:
- the core input and the menu button behave as blur/focus triggers
- when the autocompletion list is clicked, it will temporarily
remove the focus from the input (blurring it), and put it back
later... on a timer. Issue is the timer, we don't want to rely on
having a bigger timer (as later revisions of the library may
change our timings and it's iffy to rely on timers conserving
order perfectly); on the other hand we know the focus *will* come
back to the input eventually. So we can just avoid propagating
blur iif the blur is the consequence of having clicked on the
completion list.
- roughly the same thing happens when clicking on $drop_down (after
fixing the handling of its final focus to be consistent, as
$drop_down would not re-focus the input if it was *closing* the
completion list)
- pretty sure the menu thing does *not work at all*, but I don't
have the courage of fixing it before committing this part.
Date/datetime widget remains to be handled, basically the core focus
handling is the same as in e.g. a charfield *but* needs to handle (and
ignore) loss of focus from clicking inside the picker
widget. Expecting the level of suck to reach unknown heights.
bzr revid: xmo@openerp.com-20120619072518-lsrhzij5asxt2aea
2012-06-19 07:25:18 +00:00
|
|
|
|
|
2012-06-19 09:36:51 +00:00
|
|
|
|
// avoid triggering blur on the widget when it comes from clicking on
|
|
|
|
|
// a completion of the list, on the dropdown or on the m2o menu thing
|
[FIX] handling of focus on m2o fields (in editable list row)
* Throw out focusin/focusout: the m2o widget's completion list is
created at the page top (body) so the editable listview can't
generically handle arbitrary widgets via mere focusin/focusout.
* Move responsibility of focus/blur events to the form view and its
widgets.
* Events could not just be named ``focus`` and ``blur`` due to usage
of jquery's event system: jquery will automatically call a method of
the event's name if it exists on the object, which conflicts with
.web.form.Field#focus and leads to an infinite loop (as Field#focus
focuses the field's root element, which triggers the focus event,
which calls the focus method,...) => form-* and widget-*, can be
switched back in trunk
* m2o mess kind-of complex, basically:
- the core input and the menu button behave as blur/focus triggers
- when the autocompletion list is clicked, it will temporarily
remove the focus from the input (blurring it), and put it back
later... on a timer. Issue is the timer, we don't want to rely on
having a bigger timer (as later revisions of the library may
change our timings and it's iffy to rely on timers conserving
order perfectly); on the other hand we know the focus *will* come
back to the input eventually. So we can just avoid propagating
blur iif the blur is the consequence of having clicked on the
completion list.
- roughly the same thing happens when clicking on $drop_down (after
fixing the handling of its final focus to be consistent, as
$drop_down would not re-focus the input if it was *closing* the
completion list)
- pretty sure the menu thing does *not work at all*, but I don't
have the courage of fixing it before committing this part.
Date/datetime widget remains to be handled, basically the core focus
handling is the same as in e.g. a charfield *but* needs to handle (and
ignore) loss of focus from clicking inside the picker
widget. Expecting the level of suck to reach unknown heights.
bzr revid: xmo@openerp.com-20120619072518-lsrhzij5asxt2aea
2012-06-19 07:25:18 +00:00
|
|
|
|
var picking_completion = false;
|
2012-06-19 09:36:51 +00:00
|
|
|
|
this.$input.autocomplete('widget')
|
|
|
|
|
.add(this.$drop_down)
|
|
|
|
|
.add(this.$menu_btn)
|
[FIX] handling of focus on m2o fields (in editable list row)
* Throw out focusin/focusout: the m2o widget's completion list is
created at the page top (body) so the editable listview can't
generically handle arbitrary widgets via mere focusin/focusout.
* Move responsibility of focus/blur events to the form view and its
widgets.
* Events could not just be named ``focus`` and ``blur`` due to usage
of jquery's event system: jquery will automatically call a method of
the event's name if it exists on the object, which conflicts with
.web.form.Field#focus and leads to an infinite loop (as Field#focus
focuses the field's root element, which triggers the focus event,
which calls the focus method,...) => form-* and widget-*, can be
switched back in trunk
* m2o mess kind-of complex, basically:
- the core input and the menu button behave as blur/focus triggers
- when the autocompletion list is clicked, it will temporarily
remove the focus from the input (blurring it), and put it back
later... on a timer. Issue is the timer, we don't want to rely on
having a bigger timer (as later revisions of the library may
change our timings and it's iffy to rely on timers conserving
order perfectly); on the other hand we know the focus *will* come
back to the input eventually. So we can just avoid propagating
blur iif the blur is the consequence of having clicked on the
completion list.
- roughly the same thing happens when clicking on $drop_down (after
fixing the handling of its final focus to be consistent, as
$drop_down would not re-focus the input if it was *closing* the
completion list)
- pretty sure the menu thing does *not work at all*, but I don't
have the courage of fixing it before committing this part.
Date/datetime widget remains to be handled, basically the core focus
handling is the same as in e.g. a charfield *but* needs to handle (and
ignore) loss of focus from clicking inside the picker
widget. Expecting the level of suck to reach unknown heights.
bzr revid: xmo@openerp.com-20120619072518-lsrhzij5asxt2aea
2012-06-19 07:25:18 +00:00
|
|
|
|
.mousedown(function () {
|
|
|
|
|
picking_completion = true;
|
|
|
|
|
});
|
|
|
|
|
this.$input.add(this.$menu_btn).bind({
|
|
|
|
|
blur: function () {
|
|
|
|
|
if (!picking_completion) {
|
|
|
|
|
$(self).trigger('widget-blur');
|
|
|
|
|
}
|
|
|
|
|
picking_completion = false;
|
|
|
|
|
},
|
|
|
|
|
focus: function () {
|
|
|
|
|
$(self).trigger('widget-focus');
|
|
|
|
|
}
|
|
|
|
|
})
|
2011-06-06 16:50:43 +00:00
|
|
|
|
},
|
2011-06-10 10:31:55 +00:00
|
|
|
|
// autocomplete component content handling
|
2011-06-06 16:50:43 +00:00
|
|
|
|
get_search_result: function(request, response) {
|
|
|
|
|
var search_val = request.term;
|
|
|
|
|
var self = this;
|
2011-06-20 14:43:48 +00:00
|
|
|
|
|
2012-01-24 14:19:27 +00:00
|
|
|
|
if (this.abort_last) {
|
|
|
|
|
this.abort_last();
|
|
|
|
|
delete this.abort_last;
|
|
|
|
|
}
|
2011-09-05 11:03:09 +00:00
|
|
|
|
var dataset = new openerp.web.DataSetStatic(this, this.field.relation, self.build_context());
|
2011-06-20 14:43:48 +00:00
|
|
|
|
|
2011-06-30 13:07:13 +00:00
|
|
|
|
dataset.name_search(search_val, self.build_domain(), 'ilike',
|
2011-06-28 17:18:52 +00:00
|
|
|
|
this.limit + 1, function(data) {
|
2011-07-15 09:55:27 +00:00
|
|
|
|
self.last_search = data;
|
2011-06-10 10:31:55 +00:00
|
|
|
|
// possible selections for the m2o
|
2011-07-15 09:55:27 +00:00
|
|
|
|
var values = _.map(data, function(x) {
|
2012-01-31 09:02:26 +00:00
|
|
|
|
return {
|
|
|
|
|
label: _.str.escapeHTML(x[1]),
|
|
|
|
|
value:x[1],
|
|
|
|
|
name:x[1],
|
|
|
|
|
id:x[0]
|
|
|
|
|
};
|
2011-06-06 16:50:43 +00:00
|
|
|
|
});
|
2011-06-20 14:43:48 +00:00
|
|
|
|
|
2011-06-15 09:54:07 +00:00
|
|
|
|
// search more... if more results that max
|
2011-06-09 13:20:04 +00:00
|
|
|
|
if (values.length > self.limit) {
|
2012-06-06 12:47:17 +00:00
|
|
|
|
var open_search_popup = function(data) {
|
|
|
|
|
self._change_int_value(null);
|
|
|
|
|
self._search_create_popup("search", data);
|
|
|
|
|
};
|
2011-06-09 13:20:04 +00:00
|
|
|
|
values = values.slice(0, self.limit);
|
2011-08-16 07:54:05 +00:00
|
|
|
|
values.push({label: _t("<em> Search More...</em>"), action: function() {
|
2012-06-06 12:47:17 +00:00
|
|
|
|
if (!search_val) {
|
2012-05-25 13:14:16 +00:00
|
|
|
|
// search optimisation - in case user didn't enter any text we
|
|
|
|
|
// do not need to prefilter records; for big datasets (ex: more
|
|
|
|
|
// that 10.000 records) calling name_search() could be very very
|
|
|
|
|
// expensive!
|
2012-06-06 12:47:17 +00:00
|
|
|
|
open_search_popup();
|
|
|
|
|
return;
|
2012-05-25 13:14:16 +00:00
|
|
|
|
}
|
2012-06-06 12:47:17 +00:00
|
|
|
|
dataset.name_search(search_val, self.build_domain(),
|
|
|
|
|
'ilike', false, open_search_popup);
|
2011-06-09 13:20:04 +00:00
|
|
|
|
}});
|
2011-06-06 16:50:43 +00:00
|
|
|
|
}
|
2011-06-15 09:54:07 +00:00
|
|
|
|
// quick create
|
2011-09-06 08:58:53 +00:00
|
|
|
|
var raw_result = _(data.result).map(function(x) {return x[1];});
|
2011-06-14 08:10:46 +00:00
|
|
|
|
if (search_val.length > 0 &&
|
|
|
|
|
!_.include(raw_result, search_val) &&
|
|
|
|
|
(!self.value || search_val !== self.value[1])) {
|
2011-11-15 12:30:59 +00:00
|
|
|
|
values.push({label: _.str.sprintf(_t('<em> Create "<strong>%s</strong>"</em>'),
|
2011-08-16 07:54:05 +00:00
|
|
|
|
$('<span />').text(search_val).html()), action: function() {
|
2011-06-10 16:16:29 +00:00
|
|
|
|
self._quick_create(search_val);
|
2011-06-10 10:31:55 +00:00
|
|
|
|
}});
|
|
|
|
|
}
|
2011-06-15 09:54:07 +00:00
|
|
|
|
// create...
|
2011-08-16 07:54:05 +00:00
|
|
|
|
values.push({label: _t("<em> Create and Edit...</em>"), action: function() {
|
2011-06-10 15:24:13 +00:00
|
|
|
|
self._change_int_value(null);
|
2011-07-04 15:05:54 +00:00
|
|
|
|
self._search_create_popup("form", undefined, {"default_name": search_val});
|
2011-06-09 13:20:04 +00:00
|
|
|
|
}});
|
2011-06-20 14:43:48 +00:00
|
|
|
|
|
2011-06-06 16:50:43 +00:00
|
|
|
|
response(values);
|
|
|
|
|
});
|
2012-01-24 14:19:27 +00:00
|
|
|
|
this.abort_last = dataset.abort_last;
|
2011-04-05 09:20:50 +00:00
|
|
|
|
},
|
2011-06-10 16:16:29 +00:00
|
|
|
|
_quick_create: function(name) {
|
|
|
|
|
var self = this;
|
2012-01-25 08:53:10 +00:00
|
|
|
|
var slow_create = function () {
|
2011-06-10 16:16:29 +00:00
|
|
|
|
self._change_int_value(null);
|
2011-06-17 14:19:45 +00:00
|
|
|
|
self._search_create_popup("form", undefined, {"default_name": name});
|
2012-01-25 08:53:10 +00:00
|
|
|
|
};
|
2011-12-21 15:32:29 +00:00
|
|
|
|
if (self.get_definition_options().quick_create === undefined || self.get_definition_options().quick_create) {
|
2011-12-21 15:08:31 +00:00
|
|
|
|
var dataset = new openerp.web.DataSetStatic(this, this.field.relation, self.build_context());
|
|
|
|
|
dataset.name_create(name, function(data) {
|
|
|
|
|
self._change_int_ext_value(data);
|
|
|
|
|
}).fail(function(error, event) {
|
|
|
|
|
event.preventDefault();
|
|
|
|
|
slow_create();
|
|
|
|
|
});
|
|
|
|
|
} else
|
|
|
|
|
slow_create();
|
2011-06-10 16:16:29 +00:00
|
|
|
|
},
|
2011-06-10 10:31:55 +00:00
|
|
|
|
// all search/create popup handling
|
2011-06-17 14:19:45 +00:00
|
|
|
|
_search_create_popup: function(view, ids, context) {
|
2011-06-09 17:08:51 +00:00
|
|
|
|
var self = this;
|
2011-09-05 11:03:09 +00:00
|
|
|
|
var pop = new openerp.web.form.SelectCreatePopup(this);
|
2011-12-22 09:40:12 +00:00
|
|
|
|
pop.select_element(
|
|
|
|
|
self.field.relation,
|
|
|
|
|
{
|
|
|
|
|
title: (view === 'search' ? _t("Search: ") : _t("Create: ")) + (this.string || this.name),
|
2011-06-15 15:04:40 +00:00
|
|
|
|
initial_ids: ids ? _.map(ids, function(x) {return x[0]}) : undefined,
|
|
|
|
|
initial_view: view,
|
|
|
|
|
disable_multiple_selection: true
|
2011-12-22 09:40:12 +00:00
|
|
|
|
},
|
|
|
|
|
self.build_domain(),
|
|
|
|
|
new openerp.web.CompoundContext(self.build_context(), context || {})
|
|
|
|
|
);
|
2011-06-15 15:04:40 +00:00
|
|
|
|
pop.on_select_elements.add(function(element_ids) {
|
2011-09-05 11:03:09 +00:00
|
|
|
|
var dataset = new openerp.web.DataSetStatic(self, self.field.relation, self.build_context());
|
2011-06-28 17:18:52 +00:00
|
|
|
|
dataset.name_get([element_ids[0]], function(data) {
|
2011-07-15 09:55:27 +00:00
|
|
|
|
self._change_int_ext_value(data[0]);
|
2012-06-19 09:36:51 +00:00
|
|
|
|
self.focus();
|
2011-06-09 17:08:51 +00:00
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
},
|
2011-06-10 14:58:01 +00:00
|
|
|
|
_change_int_ext_value: function(value) {
|
|
|
|
|
this._change_int_value(value);
|
|
|
|
|
this.$input.val(this.value ? this.value[1] : "");
|
|
|
|
|
},
|
|
|
|
|
_change_int_value: function(value) {
|
|
|
|
|
this.value = value;
|
2011-06-28 16:22:06 +00:00
|
|
|
|
var back_orig_value = this.original_value;
|
|
|
|
|
if (this.value === null || this.value) {
|
|
|
|
|
this.original_value = this.value;
|
|
|
|
|
}
|
|
|
|
|
if (back_orig_value === undefined) { // first use after a set_value()
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (this.value !== undefined && ((back_orig_value ? back_orig_value[0] : null)
|
|
|
|
|
!== (this.value ? this.value[0] : null))) {
|
2011-06-10 14:58:01 +00:00
|
|
|
|
this.on_ui_change();
|
2011-04-05 14:34:25 +00:00
|
|
|
|
}
|
2011-05-03 08:50:44 +00:00
|
|
|
|
},
|
2011-06-10 14:58:01 +00:00
|
|
|
|
set_value: function(value) {
|
2011-06-23 10:42:22 +00:00
|
|
|
|
value = value || null;
|
2011-08-29 13:25:03 +00:00
|
|
|
|
this.invalid = false;
|
2011-06-23 12:40:16 +00:00
|
|
|
|
var self = this;
|
2011-06-23 12:57:25 +00:00
|
|
|
|
this.tmp_value = value;
|
2011-08-29 13:25:03 +00:00
|
|
|
|
self.update_dom();
|
|
|
|
|
self.on_value_changed();
|
2011-06-23 12:40:16 +00:00
|
|
|
|
var real_set_value = function(rval) {
|
2011-06-27 14:59:06 +00:00
|
|
|
|
self.tmp_value = undefined;
|
2011-08-29 13:25:03 +00:00
|
|
|
|
self.value = rval;
|
2011-06-28 16:22:06 +00:00
|
|
|
|
self.original_value = undefined;
|
2011-06-23 12:40:16 +00:00
|
|
|
|
self._change_int_ext_value(rval);
|
|
|
|
|
};
|
2011-10-31 11:06:20 +00:00
|
|
|
|
if (value && !(value instanceof Array)) {
|
2011-12-20 15:41:45 +00:00
|
|
|
|
// name_get in a m2o does not use the context of the field
|
|
|
|
|
var dataset = new openerp.web.DataSetStatic(this, this.field.relation, self.view.dataset.get_context());
|
2011-06-28 17:18:52 +00:00
|
|
|
|
dataset.name_get([value], function(data) {
|
2011-07-15 09:55:27 +00:00
|
|
|
|
real_set_value(data[0]);
|
2011-06-23 13:51:24 +00:00
|
|
|
|
}).fail(function() {self.tmp_value = undefined;});
|
2011-06-23 12:40:16 +00:00
|
|
|
|
} else {
|
2011-12-19 16:28:05 +00:00
|
|
|
|
$.async_when().then(function() {real_set_value(value);});
|
2011-06-23 12:40:16 +00:00
|
|
|
|
}
|
2011-06-10 14:58:01 +00:00
|
|
|
|
},
|
2011-05-03 08:50:44 +00:00
|
|
|
|
get_value: function() {
|
2011-06-23 12:57:25 +00:00
|
|
|
|
if (this.tmp_value !== undefined) {
|
|
|
|
|
if (this.tmp_value instanceof Array) {
|
|
|
|
|
return this.tmp_value[0];
|
|
|
|
|
}
|
2011-06-28 12:17:47 +00:00
|
|
|
|
return this.tmp_value ? this.tmp_value : false;
|
2011-06-23 12:57:25 +00:00
|
|
|
|
}
|
2011-06-10 13:29:26 +00:00
|
|
|
|
if (this.value === undefined)
|
2011-06-28 16:22:06 +00:00
|
|
|
|
return this.original_value ? this.original_value[0] : false;
|
2011-06-10 14:58:01 +00:00
|
|
|
|
return this.value ? this.value[0] : false;
|
|
|
|
|
},
|
|
|
|
|
validate: function() {
|
|
|
|
|
this.invalid = false;
|
2011-08-29 13:25:03 +00:00
|
|
|
|
var val = this.tmp_value !== undefined ? this.tmp_value : this.value;
|
|
|
|
|
if (val === null) {
|
2011-06-10 14:58:01 +00:00
|
|
|
|
this.invalid = this.required;
|
|
|
|
|
}
|
2011-08-25 17:04:10 +00:00
|
|
|
|
},
|
|
|
|
|
open_related: function(related) {
|
|
|
|
|
var self = this;
|
|
|
|
|
if (!self.value)
|
|
|
|
|
return;
|
2011-08-29 15:26:43 +00:00
|
|
|
|
var additional_context = {
|
2011-08-25 17:04:10 +00:00
|
|
|
|
active_id: self.value[0],
|
2011-08-26 12:44:18 +00:00
|
|
|
|
active_ids: [self.value[0]],
|
2011-08-25 17:04:10 +00:00
|
|
|
|
active_model: self.field.relation
|
2011-08-29 15:26:43 +00:00
|
|
|
|
};
|
2011-09-05 11:03:09 +00:00
|
|
|
|
self.rpc("/web/action/load", {
|
2011-08-29 15:26:43 +00:00
|
|
|
|
action_id: related[2].id,
|
|
|
|
|
context: additional_context
|
2011-08-25 17:04:10 +00:00
|
|
|
|
}, function(result) {
|
2011-08-29 15:26:43 +00:00
|
|
|
|
result.result.context = _.extend(result.result.context || {}, additional_context);
|
2011-08-26 12:44:18 +00:00
|
|
|
|
self.do_action(result.result);
|
2011-08-25 17:04:10 +00:00
|
|
|
|
});
|
2011-10-06 12:04:25 +00:00
|
|
|
|
},
|
2012-01-12 15:15:27 +00:00
|
|
|
|
focus: function ($element) {
|
|
|
|
|
this._super($element || this.$input);
|
2011-10-24 15:20:56 +00:00
|
|
|
|
},
|
|
|
|
|
update_dom: function() {
|
|
|
|
|
this._super.apply(this, arguments);
|
2012-02-13 16:31:20 +00:00
|
|
|
|
this.$input.prop('readonly', this.readonly);
|
2011-06-10 13:29:26 +00:00
|
|
|
|
}
|
2011-03-30 14:00:48 +00:00
|
|
|
|
});
|
|
|
|
|
|
2011-06-21 12:12:22 +00:00
|
|
|
|
/*
|
|
|
|
|
# Values: (0, 0, { fields }) create
|
|
|
|
|
# (1, ID, { fields }) update
|
|
|
|
|
# (2, ID) remove (delete)
|
|
|
|
|
# (3, ID) unlink one (target id or target of relation)
|
|
|
|
|
# (4, ID) link
|
|
|
|
|
# (5) unlink all (only valid for one2many)
|
|
|
|
|
*/
|
2011-06-28 08:22:55 +00:00
|
|
|
|
var commands = {
|
|
|
|
|
// (0, _, {values})
|
|
|
|
|
CREATE: 0,
|
2011-06-28 08:30:50 +00:00
|
|
|
|
'create': function (values) {
|
|
|
|
|
return [commands.CREATE, false, values];
|
|
|
|
|
},
|
2011-06-28 08:22:55 +00:00
|
|
|
|
// (1, id, {values})
|
|
|
|
|
UPDATE: 1,
|
2011-06-28 08:30:50 +00:00
|
|
|
|
'update': function (id, values) {
|
|
|
|
|
return [commands.UPDATE, id, values];
|
|
|
|
|
},
|
2011-06-28 08:22:55 +00:00
|
|
|
|
// (2, id[, _])
|
|
|
|
|
DELETE: 2,
|
2011-06-28 08:30:50 +00:00
|
|
|
|
'delete': function (id) {
|
|
|
|
|
return [commands.DELETE, id, false];
|
|
|
|
|
},
|
2011-06-28 08:22:55 +00:00
|
|
|
|
// (3, id[, _]) removes relation, but not linked record itself
|
|
|
|
|
FORGET: 3,
|
2011-06-28 08:30:50 +00:00
|
|
|
|
'forget': function (id) {
|
|
|
|
|
return [commands.FORGET, id, false];
|
|
|
|
|
},
|
2011-06-28 08:22:55 +00:00
|
|
|
|
// (4, id[, _])
|
|
|
|
|
LINK_TO: 4,
|
2011-06-28 08:30:50 +00:00
|
|
|
|
'link_to': function (id) {
|
|
|
|
|
return [commands.LINK_TO, id, false];
|
|
|
|
|
},
|
2011-06-28 08:22:55 +00:00
|
|
|
|
// (5[, _[, _]])
|
2011-06-29 15:35:26 +00:00
|
|
|
|
DELETE_ALL: 5,
|
|
|
|
|
'delete_all': function () {
|
2011-06-28 08:30:50 +00:00
|
|
|
|
return [5, false, false];
|
|
|
|
|
},
|
2011-06-28 08:22:55 +00:00
|
|
|
|
// (6, _, ids) replaces all linked records with provided ids
|
2011-06-28 08:30:50 +00:00
|
|
|
|
REPLACE_WITH: 6,
|
|
|
|
|
'replace_with': function (ids) {
|
|
|
|
|
return [6, false, ids];
|
|
|
|
|
}
|
2011-06-28 08:22:55 +00:00
|
|
|
|
};
|
2011-09-05 11:03:09 +00:00
|
|
|
|
openerp.web.form.FieldOne2Many = openerp.web.form.Field.extend({
|
2011-09-19 10:15:17 +00:00
|
|
|
|
template: 'FieldOne2Many',
|
2011-07-04 15:00:17 +00:00
|
|
|
|
multi_selection: false,
|
2011-03-30 14:00:48 +00:00
|
|
|
|
init: function(view, node) {
|
|
|
|
|
this._super(view, node);
|
2011-10-26 15:31:09 +00:00
|
|
|
|
this.is_loaded = $.Deferred();
|
|
|
|
|
this.initial_is_loaded = this.is_loaded;
|
2011-10-17 16:22:28 +00:00
|
|
|
|
this.is_setted = $.Deferred();
|
2011-07-18 15:28:07 +00:00
|
|
|
|
this.form_last_update = $.Deferred();
|
2011-10-12 16:13:22 +00:00
|
|
|
|
this.init_form_last_update = this.form_last_update;
|
2011-07-27 09:09:05 +00:00
|
|
|
|
this.disable_utility_classes = true;
|
2011-04-05 10:04:50 +00:00
|
|
|
|
},
|
|
|
|
|
start: function() {
|
|
|
|
|
this._super.apply(this, arguments);
|
2011-06-20 14:43:48 +00:00
|
|
|
|
|
2011-05-31 14:11:19 +00:00
|
|
|
|
var self = this;
|
2011-06-20 14:43:48 +00:00
|
|
|
|
|
2011-09-05 11:03:09 +00:00
|
|
|
|
this.dataset = new openerp.web.form.One2ManyDataSet(this, this.field.relation);
|
2011-06-29 14:35:06 +00:00
|
|
|
|
this.dataset.o2m = this;
|
|
|
|
|
this.dataset.parent_view = this.view;
|
2011-12-19 17:30:55 +00:00
|
|
|
|
this.dataset.child_name = this.name;
|
|
|
|
|
//this.dataset.child_name =
|
2011-06-21 12:12:22 +00:00
|
|
|
|
this.dataset.on_change.add_last(function() {
|
2012-01-03 17:17:13 +00:00
|
|
|
|
self.trigger_on_change();
|
2011-05-31 14:11:19 +00:00
|
|
|
|
});
|
2011-06-20 14:43:48 +00:00
|
|
|
|
|
2011-10-26 12:55:24 +00:00
|
|
|
|
this.is_setted.then(function() {
|
|
|
|
|
self.load_views();
|
|
|
|
|
});
|
|
|
|
|
},
|
2012-01-03 17:17:13 +00:00
|
|
|
|
trigger_on_change: function() {
|
|
|
|
|
var tmp = this.doing_on_change;
|
|
|
|
|
this.doing_on_change = true;
|
|
|
|
|
this.on_ui_change();
|
|
|
|
|
this.doing_on_change = tmp;
|
|
|
|
|
},
|
2011-10-31 10:27:52 +00:00
|
|
|
|
is_readonly: function() {
|
|
|
|
|
return this.readonly || this.force_readonly;
|
|
|
|
|
},
|
2011-10-26 12:55:24 +00:00
|
|
|
|
load_views: function() {
|
|
|
|
|
var self = this;
|
|
|
|
|
|
2011-06-06 07:04:51 +00:00
|
|
|
|
var modes = this.node.attrs.mode;
|
2011-10-25 12:42:51 +00:00
|
|
|
|
modes = !!modes ? modes.split(",") : ["tree"];
|
2011-06-06 07:04:51 +00:00
|
|
|
|
var views = [];
|
|
|
|
|
_.each(modes, function(mode) {
|
2011-08-08 17:32:30 +00:00
|
|
|
|
var view = {
|
|
|
|
|
view_id: false,
|
|
|
|
|
view_type: mode == "tree" ? "list" : mode,
|
|
|
|
|
options: { sidebar : false }
|
|
|
|
|
};
|
2011-06-06 07:04:51 +00:00
|
|
|
|
if (self.field.views && self.field.views[mode]) {
|
2011-06-16 14:25:30 +00:00
|
|
|
|
view.embedded_view = self.field.views[mode];
|
|
|
|
|
}
|
|
|
|
|
if(view.view_type === "list") {
|
2011-08-08 17:32:30 +00:00
|
|
|
|
view.options.selectable = self.multi_selection;
|
2011-10-31 10:27:52 +00:00
|
|
|
|
if (self.is_readonly()) {
|
2011-10-26 14:11:48 +00:00
|
|
|
|
view.options.addable = null;
|
|
|
|
|
view.options.deletable = null;
|
2011-12-28 16:29:52 +00:00
|
|
|
|
view.options.isClarkGable = false;
|
2011-10-26 14:11:48 +00:00
|
|
|
|
}
|
2011-09-30 15:46:12 +00:00
|
|
|
|
} else if (view.view_type === "form") {
|
2011-12-07 09:42:42 +00:00
|
|
|
|
if (self.is_readonly()) {
|
|
|
|
|
view.view_type = 'page';
|
|
|
|
|
}
|
2011-09-30 15:46:12 +00:00
|
|
|
|
view.options.not_interactible_on_create = true;
|
2011-06-06 07:04:51 +00:00
|
|
|
|
}
|
|
|
|
|
views.push(view);
|
|
|
|
|
});
|
2011-06-16 16:37:09 +00:00
|
|
|
|
this.views = views;
|
2011-12-07 09:42:42 +00:00
|
|
|
|
|
2011-12-21 15:32:29 +00:00
|
|
|
|
this.viewmanager = new openerp.web.ViewManager(this, this.dataset, views, {});
|
2011-12-13 14:49:55 +00:00
|
|
|
|
this.viewmanager.template = 'One2Many.viewmanager';
|
2012-02-08 16:18:51 +00:00
|
|
|
|
this.viewmanager.registry = openerp.web.views.extend({
|
2011-09-05 11:03:09 +00:00
|
|
|
|
list: 'openerp.web.form.One2ManyListView',
|
2011-12-21 16:43:35 +00:00
|
|
|
|
form: 'openerp.web.form.One2ManyFormView',
|
2011-12-07 09:42:42 +00:00
|
|
|
|
page: 'openerp.web.PageView'
|
2011-07-18 15:28:07 +00:00
|
|
|
|
});
|
|
|
|
|
var once = $.Deferred().then(function() {
|
2011-10-12 16:13:22 +00:00
|
|
|
|
self.init_form_last_update.resolve();
|
2011-06-28 08:22:28 +00:00
|
|
|
|
});
|
2011-10-26 12:55:24 +00:00
|
|
|
|
var def = $.Deferred().then(function() {
|
2011-10-26 15:31:09 +00:00
|
|
|
|
self.initial_is_loaded.resolve();
|
2011-10-26 12:55:24 +00:00
|
|
|
|
});
|
2011-06-01 08:23:58 +00:00
|
|
|
|
this.viewmanager.on_controller_inited.add_last(function(view_type, controller) {
|
2011-05-31 15:28:07 +00:00
|
|
|
|
if (view_type == "list") {
|
2011-06-16 16:37:09 +00:00
|
|
|
|
controller.o2m = self;
|
2011-10-31 10:27:52 +00:00
|
|
|
|
if (self.is_readonly())
|
2011-10-26 14:11:48 +00:00
|
|
|
|
controller.set_editable(false);
|
2011-12-07 09:42:42 +00:00
|
|
|
|
} else if (view_type == "form" || view_type == 'page') {
|
2011-12-23 11:12:11 +00:00
|
|
|
|
if (view_type == 'page' || self.is_readonly()) {
|
|
|
|
|
$(".oe_form_buttons", controller.$element).children().remove();
|
|
|
|
|
}
|
2011-07-18 15:28:07 +00:00
|
|
|
|
controller.on_record_loaded.add_last(function() {
|
|
|
|
|
once.resolve();
|
|
|
|
|
});
|
2011-07-27 14:03:32 +00:00
|
|
|
|
controller.on_pager_action.add_first(function() {
|
2011-11-10 11:02:06 +00:00
|
|
|
|
self.save_any_view();
|
2011-07-27 14:03:32 +00:00
|
|
|
|
});
|
2011-10-07 16:21:56 +00:00
|
|
|
|
} else if (view_type == "graph") {
|
|
|
|
|
self.reload_current_view()
|
2011-05-31 13:01:54 +00:00
|
|
|
|
}
|
2011-10-26 12:55:24 +00:00
|
|
|
|
def.resolve();
|
2011-05-31 13:01:54 +00:00
|
|
|
|
});
|
2011-10-26 15:39:44 +00:00
|
|
|
|
this.viewmanager.on_mode_switch.add_first(function(n_mode, b, c, d, e) {
|
2011-11-10 11:02:06 +00:00
|
|
|
|
$.when(self.save_any_view()).then(function() {
|
2011-10-26 15:39:44 +00:00
|
|
|
|
if(n_mode === "list")
|
2011-12-19 16:28:05 +00:00
|
|
|
|
$.async_when().then(function() {self.reload_current_view();});
|
2011-10-26 15:39:44 +00:00
|
|
|
|
});
|
2011-07-27 14:03:32 +00:00
|
|
|
|
});
|
2011-10-17 16:22:28 +00:00
|
|
|
|
this.is_setted.then(function() {
|
2011-12-19 16:28:05 +00:00
|
|
|
|
$.async_when().then(function () {
|
2011-10-17 16:22:28 +00:00
|
|
|
|
self.viewmanager.appendTo(self.$element);
|
2011-12-19 16:28:05 +00:00
|
|
|
|
});
|
2011-10-17 16:22:28 +00:00
|
|
|
|
});
|
2011-10-26 12:55:24 +00:00
|
|
|
|
return def;
|
2011-04-04 15:52:09 +00:00
|
|
|
|
},
|
2011-06-16 16:37:09 +00:00
|
|
|
|
reload_current_view: function() {
|
|
|
|
|
var self = this;
|
[FIX] synchronization of o2m widget saving, avoid race condition when clicking on button of unsaved record
After saving, the formview would both refresh the form and lauch the
action itself, which launches a refresh of its own.
Issue is that o2m's filling of themselves (a read) is async and
triggered by the set_value on the o2m field, so the second reload of
the form would be interspersed (between the first reload and the end
of the o2ms loading), resulting in corrupted state for the o2ms if the
button's action somehow changed the contents of the o2m (it would
remove the old records and return brand new ids during the refresh):
the set_value on the o2m would empty the o2m's dataset cache, and the
returning fetch request would try to find in cache values removed from
it, or something, blowing up everything.
Anyway, this was fixed by ensuring the button action is only executed
after the form is completely done doing its post-save reload (using
the new async set_value). This is a tiny bit brittle in that onchanges
are synchronous but call set_value, so a set_value on an o2m from an
onchange may have issues. It also increases the flicker of the view,
as the o2m is reloaded twice in quick succession.
lp bug: https://launchpad.net/bugs/885658 fixed
bzr revid: xmo@openerp.com-20111117161426-72jzhvv3dm387uom
2011-11-17 16:14:26 +00:00
|
|
|
|
return self.is_loaded = self.is_loaded.pipe(function() {
|
2011-12-07 09:42:42 +00:00
|
|
|
|
var active_view = self.viewmanager.active_view;
|
|
|
|
|
var view = self.viewmanager.views[active_view].controller;
|
|
|
|
|
if(active_view === "list") {
|
2011-10-26 15:31:09 +00:00
|
|
|
|
return view.reload_content();
|
2011-12-07 09:42:42 +00:00
|
|
|
|
} else if (active_view === "form" || active_view === 'page') {
|
2011-10-26 15:31:09 +00:00
|
|
|
|
if (self.dataset.index === null && self.dataset.ids.length >= 1) {
|
|
|
|
|
self.dataset.index = 0;
|
|
|
|
|
}
|
|
|
|
|
var act = function() {
|
|
|
|
|
return view.do_show();
|
2011-12-07 09:42:42 +00:00
|
|
|
|
};
|
2011-10-26 15:31:09 +00:00
|
|
|
|
self.form_last_update = self.form_last_update.pipe(act, act);
|
|
|
|
|
return self.form_last_update;
|
2011-12-07 09:42:42 +00:00
|
|
|
|
} else if (active_view === "graph") {
|
2011-10-26 15:31:09 +00:00
|
|
|
|
return view.do_search(self.build_domain(), self.dataset.get_context(), []);
|
2011-10-12 12:49:23 +00:00
|
|
|
|
}
|
2011-12-07 09:42:42 +00:00
|
|
|
|
}, undefined);
|
2011-06-16 16:37:09 +00:00
|
|
|
|
},
|
2011-04-11 11:35:16 +00:00
|
|
|
|
set_value: function(value) {
|
2011-06-27 09:05:32 +00:00
|
|
|
|
value = value || [];
|
2011-06-23 10:42:22 +00:00
|
|
|
|
var self = this;
|
2011-06-29 15:35:26 +00:00
|
|
|
|
this.dataset.reset_ids([]);
|
2011-06-27 09:05:32 +00:00
|
|
|
|
if(value.length >= 1 && value[0] instanceof Array) {
|
|
|
|
|
var ids = [];
|
2011-06-28 08:22:55 +00:00
|
|
|
|
_.each(value, function(command) {
|
|
|
|
|
var obj = {values: command[2]};
|
|
|
|
|
switch (command[0]) {
|
|
|
|
|
case commands.CREATE:
|
|
|
|
|
obj['id'] = _.uniqueId(self.dataset.virtual_id_prefix);
|
2011-09-28 11:55:37 +00:00
|
|
|
|
obj.defaults = {};
|
2011-06-28 08:22:55 +00:00
|
|
|
|
self.dataset.to_create.push(obj);
|
2012-02-14 16:15:58 +00:00
|
|
|
|
self.dataset.cache.push(_.extend(_.clone(obj), {values: _.clone(command[2])}));
|
2011-06-28 08:22:55 +00:00
|
|
|
|
ids.push(obj.id);
|
|
|
|
|
return;
|
|
|
|
|
case commands.UPDATE:
|
|
|
|
|
obj['id'] = command[1];
|
|
|
|
|
self.dataset.to_write.push(obj);
|
2012-02-14 16:15:58 +00:00
|
|
|
|
self.dataset.cache.push(_.extend(_.clone(obj), {values: _.clone(command[2])}));
|
2011-06-28 08:22:55 +00:00
|
|
|
|
ids.push(obj.id);
|
|
|
|
|
return;
|
|
|
|
|
case commands.DELETE:
|
|
|
|
|
self.dataset.to_delete.push({id: command[1]});
|
|
|
|
|
return;
|
|
|
|
|
case commands.LINK_TO:
|
|
|
|
|
ids.push(command[1]);
|
|
|
|
|
return;
|
2011-06-29 15:35:26 +00:00
|
|
|
|
case commands.DELETE_ALL:
|
|
|
|
|
self.dataset.delete_all = true;
|
|
|
|
|
return;
|
2011-06-27 09:05:32 +00:00
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
this._super(ids);
|
|
|
|
|
this.dataset.set_ids(ids);
|
2011-06-30 09:14:39 +00:00
|
|
|
|
} else if (value.length >= 1 && typeof(value[0]) === "object") {
|
|
|
|
|
var ids = [];
|
|
|
|
|
this.dataset.delete_all = true;
|
|
|
|
|
_.each(value, function(command) {
|
|
|
|
|
var obj = {values: command};
|
|
|
|
|
obj['id'] = _.uniqueId(self.dataset.virtual_id_prefix);
|
2011-09-28 11:55:37 +00:00
|
|
|
|
obj.defaults = {};
|
2011-06-30 09:14:39 +00:00
|
|
|
|
self.dataset.to_create.push(obj);
|
|
|
|
|
self.dataset.cache.push(_.clone(obj));
|
|
|
|
|
ids.push(obj.id);
|
|
|
|
|
});
|
|
|
|
|
this._super(ids);
|
|
|
|
|
this.dataset.set_ids(ids);
|
2011-06-27 09:05:32 +00:00
|
|
|
|
} else {
|
|
|
|
|
this._super(value);
|
|
|
|
|
this.dataset.reset_ids(value);
|
|
|
|
|
}
|
2011-07-27 09:09:05 +00:00
|
|
|
|
if (this.dataset.index === null && this.dataset.ids.length > 0) {
|
|
|
|
|
this.dataset.index = 0;
|
|
|
|
|
}
|
[FIX] synchronization of o2m widget saving, avoid race condition when clicking on button of unsaved record
After saving, the formview would both refresh the form and lauch the
action itself, which launches a refresh of its own.
Issue is that o2m's filling of themselves (a read) is async and
triggered by the set_value on the o2m field, so the second reload of
the form would be interspersed (between the first reload and the end
of the o2ms loading), resulting in corrupted state for the o2ms if the
button's action somehow changed the contents of the o2m (it would
remove the old records and return brand new ids during the refresh):
the set_value on the o2m would empty the o2m's dataset cache, and the
returning fetch request would try to find in cache values removed from
it, or something, blowing up everything.
Anyway, this was fixed by ensuring the button action is only executed
after the form is completely done doing its post-save reload (using
the new async set_value). This is a tiny bit brittle in that onchanges
are synchronous but call set_value, so a set_value on an o2m from an
onchange may have issues. It also increases the flicker of the view,
as the o2m is reloaded twice in quick succession.
lp bug: https://launchpad.net/bugs/885658 fixed
bzr revid: xmo@openerp.com-20111117161426-72jzhvv3dm387uom
2011-11-17 16:14:26 +00:00
|
|
|
|
self.is_setted.resolve();
|
|
|
|
|
return self.reload_current_view();
|
2011-05-31 13:38:17 +00:00
|
|
|
|
},
|
2011-06-21 12:12:22 +00:00
|
|
|
|
get_value: function() {
|
2011-06-27 09:05:32 +00:00
|
|
|
|
var self = this;
|
2011-06-29 14:35:06 +00:00
|
|
|
|
if (!this.dataset)
|
|
|
|
|
return [];
|
2011-12-05 15:38:13 +00:00
|
|
|
|
this.save_any_view();
|
2011-06-29 15:35:26 +00:00
|
|
|
|
var val = this.dataset.delete_all ? [commands.delete_all()] : [];
|
|
|
|
|
val = val.concat(_.map(this.dataset.ids, function(id) {
|
2011-06-27 09:05:32 +00:00
|
|
|
|
var alter_order = _.detect(self.dataset.to_create, function(x) {return x.id === id;});
|
|
|
|
|
if (alter_order) {
|
2011-06-28 08:30:50 +00:00
|
|
|
|
return commands.create(alter_order.values);
|
2011-06-27 09:05:32 +00:00
|
|
|
|
}
|
|
|
|
|
alter_order = _.detect(self.dataset.to_write, function(x) {return x.id === id;});
|
|
|
|
|
if (alter_order) {
|
2011-06-28 08:30:50 +00:00
|
|
|
|
return commands.update(alter_order.id, alter_order.values);
|
2011-06-27 09:05:32 +00:00
|
|
|
|
}
|
2011-06-28 08:30:50 +00:00
|
|
|
|
return commands.link_to(id);
|
2011-06-29 15:35:26 +00:00
|
|
|
|
}));
|
2011-06-28 08:30:50 +00:00
|
|
|
|
return val.concat(_.map(
|
|
|
|
|
this.dataset.to_delete, function(x) {
|
|
|
|
|
return commands['delete'](x.id);}));
|
2011-06-21 12:12:22 +00:00
|
|
|
|
},
|
2011-11-10 11:02:06 +00:00
|
|
|
|
save_any_view: function() {
|
2012-01-03 17:17:13 +00:00
|
|
|
|
if (this.doing_on_change)
|
|
|
|
|
return false;
|
2011-12-19 16:14:41 +00:00
|
|
|
|
return this.session.synchronized_mode(_.bind(function() {
|
|
|
|
|
if (this.viewmanager && this.viewmanager.views && this.viewmanager.active_view &&
|
|
|
|
|
this.viewmanager.views[this.viewmanager.active_view] &&
|
|
|
|
|
this.viewmanager.views[this.viewmanager.active_view].controller) {
|
|
|
|
|
var view = this.viewmanager.views[this.viewmanager.active_view].controller;
|
|
|
|
|
if (this.viewmanager.active_view === "form") {
|
2012-01-10 14:58:29 +00:00
|
|
|
|
if (!view.is_initialized.isResolved()) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2011-12-19 16:14:41 +00:00
|
|
|
|
var res = $.when(view.do_save());
|
|
|
|
|
if (!res.isResolved() && !res.isRejected()) {
|
|
|
|
|
console.warn("Asynchronous get_value() is not supported in form view.");
|
|
|
|
|
}
|
|
|
|
|
return res;
|
|
|
|
|
} else if (this.viewmanager.active_view === "list") {
|
|
|
|
|
var res = $.when(view.ensure_saved());
|
|
|
|
|
if (!res.isResolved() && !res.isRejected()) {
|
|
|
|
|
console.warn("Asynchronous get_value() is not supported in list view.");
|
|
|
|
|
}
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}, this));
|
2011-07-26 15:37:47 +00:00
|
|
|
|
},
|
2011-07-27 13:36:28 +00:00
|
|
|
|
is_valid: function() {
|
2011-10-12 09:11:47 +00:00
|
|
|
|
if (!this.viewmanager.views[this.viewmanager.active_view])
|
2012-06-13 10:08:19 +00:00
|
|
|
|
return true;
|
2011-09-16 13:22:28 +00:00
|
|
|
|
var view = this.viewmanager.views[this.viewmanager.active_view].controller;
|
2012-06-13 10:08:19 +00:00
|
|
|
|
switch (this.viewmanager.active_view) {
|
|
|
|
|
case 'form':
|
|
|
|
|
return _(view.fields).chain()
|
|
|
|
|
.invoke('is_valid')
|
|
|
|
|
.all(_.identity)
|
|
|
|
|
.value();
|
|
|
|
|
break;
|
|
|
|
|
case 'list':
|
|
|
|
|
return view.is_valid();
|
2011-07-26 15:37:47 +00:00
|
|
|
|
}
|
2012-06-13 10:08:19 +00:00
|
|
|
|
return true;
|
2011-07-27 13:36:28 +00:00
|
|
|
|
},
|
|
|
|
|
is_dirty: function() {
|
2011-11-10 11:02:06 +00:00
|
|
|
|
this.save_any_view();
|
2011-07-27 14:03:32 +00:00
|
|
|
|
return this._super();
|
2011-07-27 09:09:05 +00:00
|
|
|
|
},
|
|
|
|
|
update_dom: function() {
|
|
|
|
|
this._super.apply(this, arguments);
|
2011-10-26 12:55:24 +00:00
|
|
|
|
var self = this;
|
|
|
|
|
if (this.previous_readonly !== this.readonly) {
|
|
|
|
|
this.previous_readonly = this.readonly;
|
|
|
|
|
if (this.viewmanager) {
|
2011-10-26 15:31:09 +00:00
|
|
|
|
this.is_loaded = this.is_loaded.pipe(function() {
|
2011-10-26 12:55:24 +00:00
|
|
|
|
self.viewmanager.stop();
|
2011-10-26 15:31:09 +00:00
|
|
|
|
return $.when(self.load_views()).then(function() {
|
2011-10-26 12:55:24 +00:00
|
|
|
|
self.reload_current_view();
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-05-31 13:38:17 +00:00
|
|
|
|
}
|
2011-03-30 14:00:48 +00:00
|
|
|
|
});
|
|
|
|
|
|
2011-09-05 11:03:09 +00:00
|
|
|
|
openerp.web.form.One2ManyDataSet = openerp.web.BufferedDataSet.extend({
|
2011-06-29 14:35:06 +00:00
|
|
|
|
get_context: function() {
|
2012-01-03 17:17:13 +00:00
|
|
|
|
this.context = this.o2m.build_context([this.o2m.name]);
|
2011-06-29 14:35:06 +00:00
|
|
|
|
return this.context;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2011-09-05 11:03:09 +00:00
|
|
|
|
openerp.web.form.One2ManyListView = openerp.web.ListView.extend({
|
2011-12-13 14:49:55 +00:00
|
|
|
|
_template: 'One2Many.listview',
|
2012-06-14 12:37:31 +00:00
|
|
|
|
init: function (parent, dataset, view_id, options) {
|
|
|
|
|
this._super(parent, dataset, view_id, _.extend(options || {}, {
|
|
|
|
|
ListType: openerp.web.form.One2ManyList
|
|
|
|
|
}));
|
|
|
|
|
},
|
2012-06-13 10:08:19 +00:00
|
|
|
|
is_valid: function () {
|
|
|
|
|
var form;
|
|
|
|
|
// A list not being edited is always valid
|
|
|
|
|
if (!(form = this.first_edition_form())) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2012-06-13 15:35:55 +00:00
|
|
|
|
// If the form has not been modified, the view can only be valid
|
|
|
|
|
// NB: is_dirty will also be set on defaults/onchanges/whatever?
|
|
|
|
|
// oe_form_dirty seems to only be set on actual user actions
|
|
|
|
|
if (!form.$element.is('.oe_form_dirty')) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2012-06-13 10:08:19 +00:00
|
|
|
|
|
|
|
|
|
// Otherwise validate internal form
|
|
|
|
|
return _(form.fields).chain()
|
|
|
|
|
.invoke(function () {
|
|
|
|
|
this.validate();
|
|
|
|
|
this.update_dom(true);
|
|
|
|
|
return this.is_valid();
|
|
|
|
|
})
|
|
|
|
|
.all(_.identity)
|
|
|
|
|
.value();
|
|
|
|
|
},
|
|
|
|
|
first_edition_form: function () {
|
|
|
|
|
var get_form = function (group_or_list) {
|
|
|
|
|
if (group_or_list.edition) {
|
|
|
|
|
return group_or_list.edition_form;
|
|
|
|
|
}
|
|
|
|
|
return _(group_or_list.children).chain()
|
|
|
|
|
.map(get_form)
|
|
|
|
|
.compact()
|
|
|
|
|
.first()
|
|
|
|
|
.value();
|
|
|
|
|
};
|
|
|
|
|
return get_form(this.groups);
|
|
|
|
|
},
|
2011-06-16 16:37:09 +00:00
|
|
|
|
do_add_record: function () {
|
2011-07-04 08:38:58 +00:00
|
|
|
|
if (this.options.editable) {
|
|
|
|
|
this._super.apply(this, arguments);
|
|
|
|
|
} else {
|
|
|
|
|
var self = this;
|
2011-09-05 11:03:09 +00:00
|
|
|
|
var pop = new openerp.web.form.SelectCreatePopup(this);
|
2011-09-26 16:00:57 +00:00
|
|
|
|
pop.on_default_get.add(self.dataset.on_default_get);
|
2011-12-22 09:40:12 +00:00
|
|
|
|
pop.select_element(
|
|
|
|
|
self.o2m.field.relation,
|
|
|
|
|
{
|
|
|
|
|
title: _t("Create: ") + self.name,
|
|
|
|
|
initial_view: "form",
|
|
|
|
|
alternative_form_view: self.o2m.field.views ? self.o2m.field.views["form"] : undefined,
|
|
|
|
|
create_function: function(data, callback, error_callback) {
|
|
|
|
|
return self.o2m.dataset.create(data).then(function(r) {
|
|
|
|
|
self.o2m.dataset.set_ids(self.o2m.dataset.ids.concat([r.result]));
|
|
|
|
|
self.o2m.dataset.on_change();
|
|
|
|
|
}).then(callback, error_callback);
|
|
|
|
|
},
|
|
|
|
|
read_function: function() {
|
|
|
|
|
return self.o2m.dataset.read_ids.apply(self.o2m.dataset, arguments);
|
|
|
|
|
},
|
|
|
|
|
parent_view: self.o2m.view,
|
2012-01-03 16:34:42 +00:00
|
|
|
|
child_name: self.o2m.name,
|
2011-12-22 09:40:12 +00:00
|
|
|
|
form_view_options: {'not_interactible_on_create':true}
|
2011-10-10 12:45:16 +00:00
|
|
|
|
},
|
2011-12-22 09:40:12 +00:00
|
|
|
|
self.o2m.build_domain(),
|
|
|
|
|
self.o2m.build_context()
|
|
|
|
|
);
|
2011-08-11 16:03:47 +00:00
|
|
|
|
pop.on_select_elements.add_last(function() {
|
|
|
|
|
self.o2m.reload_current_view();
|
2011-06-23 17:07:34 +00:00
|
|
|
|
});
|
2011-07-04 08:38:58 +00:00
|
|
|
|
}
|
2011-07-13 11:05:32 +00:00
|
|
|
|
},
|
|
|
|
|
do_activate_record: function(index, id) {
|
|
|
|
|
var self = this;
|
2011-09-05 11:03:09 +00:00
|
|
|
|
var pop = new openerp.web.form.FormOpenPopup(self.o2m.view);
|
2011-12-22 09:52:48 +00:00
|
|
|
|
pop.show_element(self.o2m.field.relation, id, self.o2m.build_context(), {
|
|
|
|
|
title: _t("Open: ") + self.name,
|
2011-07-13 11:05:32 +00:00
|
|
|
|
auto_write: false,
|
|
|
|
|
alternative_form_view: self.o2m.field.views ? self.o2m.field.views["form"] : undefined,
|
2011-08-29 12:13:30 +00:00
|
|
|
|
parent_view: self.o2m.view,
|
2011-12-19 17:30:55 +00:00
|
|
|
|
child_name: self.o2m.name,
|
2011-08-29 12:13:30 +00:00
|
|
|
|
read_function: function() {
|
|
|
|
|
return self.o2m.dataset.read_ids.apply(self.o2m.dataset, arguments);
|
2011-09-30 15:46:12 +00:00
|
|
|
|
},
|
2011-10-31 10:43:04 +00:00
|
|
|
|
form_view_options: {'not_interactible_on_create':true},
|
|
|
|
|
readonly: self.o2m.is_readonly()
|
2011-07-13 11:05:32 +00:00
|
|
|
|
});
|
2012-04-17 14:53:30 +00:00
|
|
|
|
pop.dataset.call_button = function() {
|
|
|
|
|
var button_result = self.o2m.dataset.call_button.apply(self.o2m.dataset, arguments);
|
|
|
|
|
self.o2m.reload_current_view();
|
|
|
|
|
return button_result;
|
2012-06-13 10:08:19 +00:00
|
|
|
|
};
|
2011-07-13 11:05:32 +00:00
|
|
|
|
pop.on_write.add(function(id, data) {
|
2011-08-29 14:08:39 +00:00
|
|
|
|
self.o2m.dataset.write(id, data, {}, function(r) {
|
2011-07-13 11:05:32 +00:00
|
|
|
|
self.o2m.reload_current_view();
|
|
|
|
|
});
|
|
|
|
|
});
|
2011-12-22 13:24:47 +00:00
|
|
|
|
},
|
|
|
|
|
do_button_action: function (name, id, callback) {
|
|
|
|
|
var self = this;
|
|
|
|
|
var def = $.Deferred().then(callback).then(function() {self.o2m.view.reload();});
|
|
|
|
|
return this._super(name, id, _.bind(def.resolve, def));
|
2012-01-17 13:28:43 +00:00
|
|
|
|
}
|
2011-06-16 16:37:09 +00:00
|
|
|
|
});
|
2012-06-14 12:37:31 +00:00
|
|
|
|
openerp.web.form.One2ManyList = openerp.web.ListView.List.extend({
|
2012-06-19 11:03:50 +00:00
|
|
|
|
KEY_RETURN: 13,
|
|
|
|
|
// blurring caused by hitting the [Return] key, should skip the
|
|
|
|
|
// autosave-on-blur and let the handler for [Return] do its thing
|
|
|
|
|
__return_blur: false,
|
2012-06-14 12:37:31 +00:00
|
|
|
|
render_row_as_form: function () {
|
|
|
|
|
var self = this;
|
|
|
|
|
return this._super.apply(this, arguments).then(function () {
|
[FIX] handling of focus on m2o fields (in editable list row)
* Throw out focusin/focusout: the m2o widget's completion list is
created at the page top (body) so the editable listview can't
generically handle arbitrary widgets via mere focusin/focusout.
* Move responsibility of focus/blur events to the form view and its
widgets.
* Events could not just be named ``focus`` and ``blur`` due to usage
of jquery's event system: jquery will automatically call a method of
the event's name if it exists on the object, which conflicts with
.web.form.Field#focus and leads to an infinite loop (as Field#focus
focuses the field's root element, which triggers the focus event,
which calls the focus method,...) => form-* and widget-*, can be
switched back in trunk
* m2o mess kind-of complex, basically:
- the core input and the menu button behave as blur/focus triggers
- when the autocompletion list is clicked, it will temporarily
remove the focus from the input (blurring it), and put it back
later... on a timer. Issue is the timer, we don't want to rely on
having a bigger timer (as later revisions of the library may
change our timings and it's iffy to rely on timers conserving
order perfectly); on the other hand we know the focus *will* come
back to the input eventually. So we can just avoid propagating
blur iif the blur is the consequence of having clicked on the
completion list.
- roughly the same thing happens when clicking on $drop_down (after
fixing the handling of its final focus to be consistent, as
$drop_down would not re-focus the input if it was *closing* the
completion list)
- pretty sure the menu thing does *not work at all*, but I don't
have the courage of fixing it before committing this part.
Date/datetime widget remains to be handled, basically the core focus
handling is the same as in e.g. a charfield *but* needs to handle (and
ignore) loss of focus from clicking inside the picker
widget. Expecting the level of suck to reach unknown heights.
bzr revid: xmo@openerp.com-20120619072518-lsrhzij5asxt2aea
2012-06-19 07:25:18 +00:00
|
|
|
|
$(self.edition_form).bind('form-blur', function () {
|
2012-06-19 11:03:50 +00:00
|
|
|
|
if (self.__return_blur) {
|
|
|
|
|
delete self.__return_blur;
|
|
|
|
|
return;
|
|
|
|
|
}
|
[FIX] handling of focus on m2o fields (in editable list row)
* Throw out focusin/focusout: the m2o widget's completion list is
created at the page top (body) so the editable listview can't
generically handle arbitrary widgets via mere focusin/focusout.
* Move responsibility of focus/blur events to the form view and its
widgets.
* Events could not just be named ``focus`` and ``blur`` due to usage
of jquery's event system: jquery will automatically call a method of
the event's name if it exists on the object, which conflicts with
.web.form.Field#focus and leads to an infinite loop (as Field#focus
focuses the field's root element, which triggers the focus event,
which calls the focus method,...) => form-* and widget-*, can be
switched back in trunk
* m2o mess kind-of complex, basically:
- the core input and the menu button behave as blur/focus triggers
- when the autocompletion list is clicked, it will temporarily
remove the focus from the input (blurring it), and put it back
later... on a timer. Issue is the timer, we don't want to rely on
having a bigger timer (as later revisions of the library may
change our timings and it's iffy to rely on timers conserving
order perfectly); on the other hand we know the focus *will* come
back to the input eventually. So we can just avoid propagating
blur iif the blur is the consequence of having clicked on the
completion list.
- roughly the same thing happens when clicking on $drop_down (after
fixing the handling of its final focus to be consistent, as
$drop_down would not re-focus the input if it was *closing* the
completion list)
- pretty sure the menu thing does *not work at all*, but I don't
have the courage of fixing it before committing this part.
Date/datetime widget remains to be handled, basically the core focus
handling is the same as in e.g. a charfield *but* needs to handle (and
ignore) loss of focus from clicking inside the picker
widget. Expecting the level of suck to reach unknown heights.
bzr revid: xmo@openerp.com-20120619072518-lsrhzij5asxt2aea
2012-06-19 07:25:18 +00:00
|
|
|
|
if (!self.edition_form.widget_is_stopped) {
|
2012-06-18 07:34:31 +00:00
|
|
|
|
self.view.ensure_saved();
|
[FIX] handling of focus on m2o fields (in editable list row)
* Throw out focusin/focusout: the m2o widget's completion list is
created at the page top (body) so the editable listview can't
generically handle arbitrary widgets via mere focusin/focusout.
* Move responsibility of focus/blur events to the form view and its
widgets.
* Events could not just be named ``focus`` and ``blur`` due to usage
of jquery's event system: jquery will automatically call a method of
the event's name if it exists on the object, which conflicts with
.web.form.Field#focus and leads to an infinite loop (as Field#focus
focuses the field's root element, which triggers the focus event,
which calls the focus method,...) => form-* and widget-*, can be
switched back in trunk
* m2o mess kind-of complex, basically:
- the core input and the menu button behave as blur/focus triggers
- when the autocompletion list is clicked, it will temporarily
remove the focus from the input (blurring it), and put it back
later... on a timer. Issue is the timer, we don't want to rely on
having a bigger timer (as later revisions of the library may
change our timings and it's iffy to rely on timers conserving
order perfectly); on the other hand we know the focus *will* come
back to the input eventually. So we can just avoid propagating
blur iif the blur is the consequence of having clicked on the
completion list.
- roughly the same thing happens when clicking on $drop_down (after
fixing the handling of its final focus to be consistent, as
$drop_down would not re-focus the input if it was *closing* the
completion list)
- pretty sure the menu thing does *not work at all*, but I don't
have the courage of fixing it before committing this part.
Date/datetime widget remains to be handled, basically the core focus
handling is the same as in e.g. a charfield *but* needs to handle (and
ignore) loss of focus from clicking inside the picker
widget. Expecting the level of suck to reach unknown heights.
bzr revid: xmo@openerp.com-20120619072518-lsrhzij5asxt2aea
2012-06-19 07:25:18 +00:00
|
|
|
|
}
|
|
|
|
|
});
|
2012-06-14 12:37:31 +00:00
|
|
|
|
});
|
[FIX] handling of focus on m2o fields (in editable list row)
* Throw out focusin/focusout: the m2o widget's completion list is
created at the page top (body) so the editable listview can't
generically handle arbitrary widgets via mere focusin/focusout.
* Move responsibility of focus/blur events to the form view and its
widgets.
* Events could not just be named ``focus`` and ``blur`` due to usage
of jquery's event system: jquery will automatically call a method of
the event's name if it exists on the object, which conflicts with
.web.form.Field#focus and leads to an infinite loop (as Field#focus
focuses the field's root element, which triggers the focus event,
which calls the focus method,...) => form-* and widget-*, can be
switched back in trunk
* m2o mess kind-of complex, basically:
- the core input and the menu button behave as blur/focus triggers
- when the autocompletion list is clicked, it will temporarily
remove the focus from the input (blurring it), and put it back
later... on a timer. Issue is the timer, we don't want to rely on
having a bigger timer (as later revisions of the library may
change our timings and it's iffy to rely on timers conserving
order perfectly); on the other hand we know the focus *will* come
back to the input eventually. So we can just avoid propagating
blur iif the blur is the consequence of having clicked on the
completion list.
- roughly the same thing happens when clicking on $drop_down (after
fixing the handling of its final focus to be consistent, as
$drop_down would not re-focus the input if it was *closing* the
completion list)
- pretty sure the menu thing does *not work at all*, but I don't
have the courage of fixing it before committing this part.
Date/datetime widget remains to be handled, basically the core focus
handling is the same as in e.g. a charfield *but* needs to handle (and
ignore) loss of focus from clicking inside the picker
widget. Expecting the level of suck to reach unknown heights.
bzr revid: xmo@openerp.com-20120619072518-lsrhzij5asxt2aea
2012-06-19 07:25:18 +00:00
|
|
|
|
},
|
2012-06-19 11:03:50 +00:00
|
|
|
|
on_row_keyup: function (e) {
|
|
|
|
|
if (e.which === this.KEY_RETURN) {
|
|
|
|
|
this.__return_blur = true;
|
|
|
|
|
}
|
|
|
|
|
this._super(e);
|
|
|
|
|
}
|
2012-06-14 12:37:31 +00:00
|
|
|
|
});
|
2011-06-16 16:37:09 +00:00
|
|
|
|
|
2011-12-21 16:43:35 +00:00
|
|
|
|
openerp.web.form.One2ManyFormView = openerp.web.FormView.extend({
|
|
|
|
|
form_template: 'One2Many.formview',
|
|
|
|
|
on_loaded: function(data) {
|
|
|
|
|
this._super(data);
|
2012-01-11 14:37:12 +00:00
|
|
|
|
var self = this;
|
|
|
|
|
this.$form_header.find('button.oe_form_button_create').click(function() {
|
|
|
|
|
self.do_save().then(self.on_button_new);
|
|
|
|
|
});
|
2012-02-15 10:34:05 +00:00
|
|
|
|
},
|
|
|
|
|
do_notify_change: function() {
|
|
|
|
|
if (this.dataset.parent_view) {
|
|
|
|
|
this.dataset.parent_view.do_notify_change();
|
|
|
|
|
} else {
|
|
|
|
|
this._super.apply(this, arguments);
|
|
|
|
|
}
|
2011-12-21 16:43:35 +00:00
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2011-09-05 11:03:09 +00:00
|
|
|
|
openerp.web.form.FieldMany2Many = openerp.web.form.Field.extend({
|
2011-09-19 10:15:17 +00:00
|
|
|
|
template: 'FieldMany2Many',
|
2011-07-04 15:00:17 +00:00
|
|
|
|
multi_selection: false,
|
2011-03-30 14:00:48 +00:00
|
|
|
|
init: function(view, node) {
|
|
|
|
|
this._super(view, node);
|
2011-04-22 15:54:52 +00:00
|
|
|
|
this.list_id = _.uniqueId("many2many");
|
2011-10-26 15:23:05 +00:00
|
|
|
|
this.is_loaded = $.Deferred();
|
|
|
|
|
this.initial_is_loaded = this.is_loaded;
|
2011-10-25 16:20:31 +00:00
|
|
|
|
this.is_setted = $.Deferred();
|
2011-04-22 15:54:52 +00:00
|
|
|
|
},
|
|
|
|
|
start: function() {
|
|
|
|
|
this._super.apply(this, arguments);
|
2011-06-20 14:43:48 +00:00
|
|
|
|
|
2011-05-31 14:11:19 +00:00
|
|
|
|
var self = this;
|
2011-06-20 14:43:48 +00:00
|
|
|
|
|
2011-09-05 11:03:09 +00:00
|
|
|
|
this.dataset = new openerp.web.form.Many2ManyDataSet(this, this.field.relation);
|
2011-07-01 10:27:27 +00:00
|
|
|
|
this.dataset.m2m = this;
|
2011-05-31 14:11:19 +00:00
|
|
|
|
this.dataset.on_unlink.add_last(function(ids) {
|
|
|
|
|
self.on_ui_change();
|
|
|
|
|
});
|
2011-10-25 16:20:31 +00:00
|
|
|
|
|
|
|
|
|
this.is_setted.then(function() {
|
|
|
|
|
self.load_view();
|
2011-05-31 15:28:07 +00:00
|
|
|
|
});
|
2011-04-22 15:54:52 +00:00
|
|
|
|
},
|
|
|
|
|
set_value: function(value) {
|
2011-06-23 10:42:22 +00:00
|
|
|
|
value = value || [];
|
2011-06-27 09:05:32 +00:00
|
|
|
|
if (value.length >= 1 && value[0] instanceof Array) {
|
|
|
|
|
value = value[0][2];
|
|
|
|
|
}
|
2011-06-23 10:42:22 +00:00
|
|
|
|
this._super(value);
|
|
|
|
|
this.dataset.set_ids(value);
|
|
|
|
|
var self = this;
|
2011-10-26 15:23:05 +00:00
|
|
|
|
self.reload_content();
|
2011-10-25 16:20:31 +00:00
|
|
|
|
this.is_setted.resolve();
|
2011-04-26 12:11:46 +00:00
|
|
|
|
},
|
|
|
|
|
get_value: function() {
|
2011-06-28 08:33:07 +00:00
|
|
|
|
return [commands.replace_with(this.dataset.ids)];
|
2011-07-01 10:27:27 +00:00
|
|
|
|
},
|
|
|
|
|
validate: function() {
|
2012-06-07 06:06:33 +00:00
|
|
|
|
this.invalid = this.required && _(this.dataset.ids).isEmpty();
|
2011-10-25 16:20:31 +00:00
|
|
|
|
},
|
2011-10-31 10:27:52 +00:00
|
|
|
|
is_readonly: function() {
|
|
|
|
|
return this.readonly || this.force_readonly;
|
|
|
|
|
},
|
2011-10-25 16:20:31 +00:00
|
|
|
|
load_view: function() {
|
|
|
|
|
var self = this;
|
|
|
|
|
this.list_view = new openerp.web.form.Many2ManyListView(this, this.dataset, false, {
|
2011-12-15 10:06:52 +00:00
|
|
|
|
'addable': self.is_readonly() ? null : _t("Add"),
|
2011-10-31 10:27:52 +00:00
|
|
|
|
'deletable': self.is_readonly() ? false : true,
|
2011-12-28 16:29:52 +00:00
|
|
|
|
'selectable': self.multi_selection,
|
|
|
|
|
'isClarkGable': self.is_readonly() ? false : true
|
2011-10-25 16:20:31 +00:00
|
|
|
|
});
|
2011-11-10 14:07:54 +00:00
|
|
|
|
var embedded = (this.field.views || {}).tree;
|
|
|
|
|
if (embedded) {
|
|
|
|
|
this.list_view.set_embedded_view(embedded);
|
|
|
|
|
}
|
2011-10-25 16:20:31 +00:00
|
|
|
|
this.list_view.m2m_field = this;
|
|
|
|
|
var loaded = $.Deferred();
|
|
|
|
|
this.list_view.on_loaded.add_last(function() {
|
2011-10-26 15:23:05 +00:00
|
|
|
|
self.initial_is_loaded.resolve();
|
2011-10-25 16:20:31 +00:00
|
|
|
|
loaded.resolve();
|
|
|
|
|
});
|
2011-12-19 16:28:05 +00:00
|
|
|
|
$.async_when().then(function () {
|
2011-10-25 16:20:31 +00:00
|
|
|
|
self.list_view.appendTo($("#" + self.list_id));
|
2011-12-19 16:28:05 +00:00
|
|
|
|
});
|
2011-10-25 16:20:31 +00:00
|
|
|
|
return loaded;
|
|
|
|
|
},
|
2011-10-26 15:23:05 +00:00
|
|
|
|
reload_content: function() {
|
|
|
|
|
var self = this;
|
|
|
|
|
this.is_loaded = this.is_loaded.pipe(function() {
|
|
|
|
|
return self.list_view.reload_content();
|
|
|
|
|
});
|
|
|
|
|
},
|
2011-10-25 16:20:31 +00:00
|
|
|
|
update_dom: function() {
|
|
|
|
|
this._super.apply(this, arguments);
|
|
|
|
|
var self = this;
|
|
|
|
|
if (this.previous_readonly !== this.readonly) {
|
|
|
|
|
this.previous_readonly = this.readonly;
|
|
|
|
|
if (this.list_view) {
|
2011-10-26 15:23:05 +00:00
|
|
|
|
this.is_loaded = this.is_loaded.pipe(function() {
|
2011-10-25 16:20:31 +00:00
|
|
|
|
self.list_view.stop();
|
2011-10-26 15:23:05 +00:00
|
|
|
|
return $.when(self.load_view()).then(function() {
|
|
|
|
|
self.reload_content();
|
2011-10-25 16:20:31 +00:00
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-07-01 10:27:27 +00:00
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2011-09-05 11:03:09 +00:00
|
|
|
|
openerp.web.form.Many2ManyDataSet = openerp.web.DataSetStatic.extend({
|
2011-07-01 10:27:27 +00:00
|
|
|
|
get_context: function() {
|
|
|
|
|
this.context = this.m2m.build_context();
|
|
|
|
|
return this.context;
|
2011-03-30 14:00:48 +00:00
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2011-09-12 11:43:50 +00:00
|
|
|
|
/**
|
|
|
|
|
* @class
|
|
|
|
|
* @extends openerp.web.ListView
|
|
|
|
|
*/
|
|
|
|
|
openerp.web.form.Many2ManyListView = openerp.web.ListView.extend(/** @lends openerp.web.form.Many2ManyListView# */{
|
2011-05-27 15:10:00 +00:00
|
|
|
|
do_add_record: function () {
|
2011-09-05 11:03:09 +00:00
|
|
|
|
var pop = new openerp.web.form.SelectCreatePopup(this);
|
2011-12-22 09:40:12 +00:00
|
|
|
|
pop.select_element(
|
|
|
|
|
this.model,
|
|
|
|
|
{
|
|
|
|
|
title: _t("Add: ") + this.name
|
|
|
|
|
},
|
2011-09-05 11:03:09 +00:00
|
|
|
|
new openerp.web.CompoundDomain(this.m2m_field.build_domain(), ["!", ["id", "in", this.m2m_field.dataset.ids]]),
|
2011-12-22 09:40:12 +00:00
|
|
|
|
this.m2m_field.build_context()
|
|
|
|
|
);
|
2011-05-09 11:52:33 +00:00
|
|
|
|
var self = this;
|
2011-06-15 15:04:40 +00:00
|
|
|
|
pop.on_select_elements.add(function(element_ids) {
|
|
|
|
|
_.each(element_ids, function(element_id) {
|
|
|
|
|
if(! _.detect(self.dataset.ids, function(x) {return x == element_id;})) {
|
|
|
|
|
self.dataset.set_ids([].concat(self.dataset.ids, [element_id]));
|
2011-07-01 10:13:49 +00:00
|
|
|
|
self.m2m_field.on_ui_change();
|
2011-06-15 15:04:40 +00:00
|
|
|
|
self.reload_content();
|
|
|
|
|
}
|
|
|
|
|
});
|
2011-05-09 11:52:33 +00:00
|
|
|
|
});
|
2011-04-26 14:13:34 +00:00
|
|
|
|
},
|
2011-05-27 15:10:00 +00:00
|
|
|
|
do_activate_record: function(index, id) {
|
2011-07-13 09:59:49 +00:00
|
|
|
|
var self = this;
|
2011-09-05 11:03:09 +00:00
|
|
|
|
var pop = new openerp.web.form.FormOpenPopup(this);
|
2011-12-22 09:40:12 +00:00
|
|
|
|
pop.show_element(this.dataset.model, id, this.m2m_field.build_context(), {
|
2011-12-22 09:52:48 +00:00
|
|
|
|
title: _t("Open: ") + this.name,
|
2011-12-08 10:47:42 +00:00
|
|
|
|
readonly: this.widget_parent.is_readonly()
|
2011-12-22 09:40:12 +00:00
|
|
|
|
});
|
2011-07-13 09:59:49 +00:00
|
|
|
|
pop.on_write_completed.add_last(function() {
|
|
|
|
|
self.reload_content();
|
2011-04-26 14:13:34 +00:00
|
|
|
|
});
|
2011-03-30 14:00:48 +00:00
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2011-09-12 11:43:50 +00:00
|
|
|
|
/**
|
|
|
|
|
* @class
|
|
|
|
|
* @extends openerp.web.OldWidget
|
|
|
|
|
*/
|
|
|
|
|
openerp.web.form.SelectCreatePopup = openerp.web.OldWidget.extend(/** @lends openerp.web.form.SelectCreatePopup# */{
|
2011-06-16 15:17:22 +00:00
|
|
|
|
template: "SelectCreatePopup",
|
2011-06-15 15:04:40 +00:00
|
|
|
|
/**
|
|
|
|
|
* options:
|
|
|
|
|
* - initial_ids
|
|
|
|
|
* - initial_view: form or search (default search)
|
|
|
|
|
* - disable_multiple_selection
|
2011-06-16 16:37:09 +00:00
|
|
|
|
* - alternative_form_view
|
2011-08-11 16:03:47 +00:00
|
|
|
|
* - create_function (defaults to a naive saving behavior)
|
2011-06-29 14:35:06 +00:00
|
|
|
|
* - parent_view
|
2011-12-19 17:30:55 +00:00
|
|
|
|
* - child_name
|
2011-09-30 15:46:12 +00:00
|
|
|
|
* - form_view_options
|
|
|
|
|
* - list_view_options
|
2011-10-10 12:40:44 +00:00
|
|
|
|
* - read_function
|
2011-06-15 15:04:40 +00:00
|
|
|
|
*/
|
2011-06-17 08:41:33 +00:00
|
|
|
|
select_element: function(model, options, domain, context) {
|
2011-08-11 16:03:47 +00:00
|
|
|
|
var self = this;
|
2011-05-03 16:17:25 +00:00
|
|
|
|
this.model = model;
|
2011-06-17 12:08:34 +00:00
|
|
|
|
this.domain = domain || [];
|
|
|
|
|
this.context = context || {};
|
2011-08-11 16:03:47 +00:00
|
|
|
|
this.options = _.defaults(options || {}, {"initial_view": "search", "create_function": function() {
|
|
|
|
|
return self.create_row.apply(self, arguments);
|
2011-10-10 12:40:44 +00:00
|
|
|
|
}, read_function: null});
|
2011-06-15 15:04:40 +00:00
|
|
|
|
this.initial_ids = this.options.initial_ids;
|
2011-08-11 16:03:47 +00:00
|
|
|
|
this.created_elements = [];
|
2011-11-10 09:32:21 +00:00
|
|
|
|
this.render_element();
|
2011-12-22 09:40:12 +00:00
|
|
|
|
openerp.web.form.dialog(this.$element, {
|
|
|
|
|
close: function() {
|
|
|
|
|
self.check_exit();
|
|
|
|
|
},
|
|
|
|
|
title: options.title || ""
|
|
|
|
|
});
|
2011-05-03 16:17:25 +00:00
|
|
|
|
this.start();
|
2011-05-09 10:05:41 +00:00
|
|
|
|
},
|
|
|
|
|
start: function() {
|
2011-05-09 11:52:33 +00:00
|
|
|
|
this._super();
|
2011-10-10 09:44:38 +00:00
|
|
|
|
var self = this;
|
2011-10-10 12:40:44 +00:00
|
|
|
|
this.dataset = new openerp.web.ProxyDataSet(this, this.model,
|
2011-07-04 15:20:27 +00:00
|
|
|
|
this.context);
|
2011-10-10 09:44:38 +00:00
|
|
|
|
this.dataset.create_function = function() {
|
|
|
|
|
return self.options.create_function.apply(null, arguments).then(function(r) {
|
|
|
|
|
self.created_elements.push(r.result);
|
|
|
|
|
});
|
2011-10-10 12:40:44 +00:00
|
|
|
|
};
|
2011-10-10 09:44:38 +00:00
|
|
|
|
this.dataset.write_function = function() {
|
2011-10-10 13:12:02 +00:00
|
|
|
|
return self.write_row.apply(self, arguments);
|
2011-10-10 12:40:44 +00:00
|
|
|
|
};
|
|
|
|
|
this.dataset.read_function = this.options.read_function;
|
2011-06-29 14:35:06 +00:00
|
|
|
|
this.dataset.parent_view = this.options.parent_view;
|
2011-12-19 17:30:55 +00:00
|
|
|
|
this.dataset.child_name = this.options.child_name;
|
2011-09-26 16:00:57 +00:00
|
|
|
|
this.dataset.on_default_get.add(this.on_default_get);
|
2011-06-23 16:57:17 +00:00
|
|
|
|
if (this.options.initial_view == "search") {
|
2011-11-23 10:30:00 +00:00
|
|
|
|
self.rpc('/web/session/eval_domain_and_context', {
|
2011-11-24 10:43:05 +00:00
|
|
|
|
domains: [],
|
|
|
|
|
contexts: [this.context]
|
|
|
|
|
}, function (results) {
|
|
|
|
|
var search_defaults = {};
|
|
|
|
|
_.each(results.context, function (value, key) {
|
|
|
|
|
var match = /^search_default_(.*)$/.exec(key);
|
|
|
|
|
if (match) {
|
|
|
|
|
search_defaults[match[1]] = value;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
self.setup_search_view(search_defaults);
|
2011-11-23 10:30:00 +00:00
|
|
|
|
});
|
2011-06-09 13:20:04 +00:00
|
|
|
|
} else { // "form"
|
|
|
|
|
this.new_object();
|
2011-05-16 11:02:42 +00:00
|
|
|
|
}
|
2011-05-09 10:05:41 +00:00
|
|
|
|
},
|
2012-02-24 09:53:10 +00:00
|
|
|
|
stop: function () {
|
|
|
|
|
this.$element.dialog('close');
|
|
|
|
|
this._super();
|
|
|
|
|
},
|
2011-11-22 10:41:45 +00:00
|
|
|
|
setup_search_view: function(search_defaults) {
|
2011-05-09 10:05:41 +00:00
|
|
|
|
var self = this;
|
|
|
|
|
if (this.searchview) {
|
|
|
|
|
this.searchview.stop();
|
|
|
|
|
}
|
2011-09-05 11:03:09 +00:00
|
|
|
|
this.searchview = new openerp.web.SearchView(this,
|
2011-11-22 10:41:45 +00:00
|
|
|
|
this.dataset, false, search_defaults);
|
2011-05-09 11:52:33 +00:00
|
|
|
|
this.searchview.on_search.add(function(domains, contexts, groupbys) {
|
2011-06-09 13:20:04 +00:00
|
|
|
|
if (self.initial_ids) {
|
2011-10-06 09:22:47 +00:00
|
|
|
|
self.do_search(domains.concat([[["id", "in", self.initial_ids]], self.domain]),
|
2011-06-17 14:19:45 +00:00
|
|
|
|
contexts, groupbys);
|
2011-06-09 13:20:04 +00:00
|
|
|
|
self.initial_ids = undefined;
|
|
|
|
|
} else {
|
2012-01-27 11:20:50 +00:00
|
|
|
|
self.do_search(domains.concat([self.domain]), contexts.concat(self.context), groupbys);
|
2011-06-09 13:20:04 +00:00
|
|
|
|
}
|
2011-05-09 11:52:33 +00:00
|
|
|
|
});
|
2011-05-10 10:30:46 +00:00
|
|
|
|
this.searchview.on_loaded.add_last(function () {
|
2011-09-05 11:03:09 +00:00
|
|
|
|
self.view_list = new openerp.web.form.SelectCreateListView(self,
|
2011-09-14 15:22:08 +00:00
|
|
|
|
self.dataset, false,
|
2011-10-31 15:49:07 +00:00
|
|
|
|
_.extend({'deletable': false,
|
|
|
|
|
'selectable': !self.options.disable_multiple_selection
|
|
|
|
|
}, self.options.list_view_options || {}));
|
2011-05-10 12:15:51 +00:00
|
|
|
|
self.view_list.popup = self;
|
2011-09-15 10:08:08 +00:00
|
|
|
|
self.view_list.appendTo($("#" + self.element_id + "_view_list")).pipe(function() {
|
|
|
|
|
self.view_list.do_show();
|
|
|
|
|
}).pipe(function() {
|
2011-06-09 13:20:04 +00:00
|
|
|
|
self.searchview.do_search();
|
2011-05-10 12:15:51 +00:00
|
|
|
|
});
|
2011-12-28 15:53:33 +00:00
|
|
|
|
self.view_list.on_loaded.add_last(function() {
|
|
|
|
|
var $buttons = self.view_list.$element.find(".oe-actions");
|
|
|
|
|
$buttons.prepend(QWeb.render("SelectCreatePopup.search.buttons"));
|
|
|
|
|
var $cbutton = $buttons.find(".oe_selectcreatepopup-search-close");
|
|
|
|
|
$cbutton.click(function() {
|
|
|
|
|
self.stop();
|
|
|
|
|
});
|
|
|
|
|
var $sbutton = $buttons.find(".oe_selectcreatepopup-search-select");
|
|
|
|
|
if(self.options.disable_multiple_selection) {
|
|
|
|
|
$sbutton.hide();
|
|
|
|
|
}
|
|
|
|
|
$sbutton.click(function() {
|
|
|
|
|
self.on_select_elements(self.selected_ids);
|
|
|
|
|
self.stop();
|
|
|
|
|
});
|
|
|
|
|
});
|
2011-05-10 10:30:46 +00:00
|
|
|
|
});
|
2011-09-15 09:49:44 +00:00
|
|
|
|
this.searchview.appendTo($("#" + this.element_id + "_search"));
|
2011-05-09 10:05:41 +00:00
|
|
|
|
},
|
2011-10-06 09:22:47 +00:00
|
|
|
|
do_search: function(domains, contexts, groupbys) {
|
|
|
|
|
var self = this;
|
|
|
|
|
this.rpc('/web/session/eval_domain_and_context', {
|
|
|
|
|
domains: domains || [],
|
|
|
|
|
contexts: contexts || [],
|
|
|
|
|
group_by_seq: groupbys || []
|
|
|
|
|
}, function (results) {
|
|
|
|
|
self.view_list.do_search(results.domain, results.context, results.group_by);
|
|
|
|
|
});
|
|
|
|
|
},
|
2011-10-10 09:44:38 +00:00
|
|
|
|
create_row: function() {
|
|
|
|
|
var self = this;
|
|
|
|
|
var wdataset = new openerp.web.DataSetSearch(this, this.model, this.context, this.domain);
|
|
|
|
|
wdataset.parent_view = this.options.parent_view;
|
2011-12-19 17:30:55 +00:00
|
|
|
|
wdataset.child_name = this.options.child_name;
|
2011-10-10 09:44:38 +00:00
|
|
|
|
return wdataset.create.apply(wdataset, arguments);
|
|
|
|
|
},
|
|
|
|
|
write_row: function() {
|
2011-06-23 16:57:17 +00:00
|
|
|
|
var self = this;
|
2011-09-05 11:03:09 +00:00
|
|
|
|
var wdataset = new openerp.web.DataSetSearch(this, this.model, this.context, this.domain);
|
2011-07-13 09:44:11 +00:00
|
|
|
|
wdataset.parent_view = this.options.parent_view;
|
2011-12-19 17:30:55 +00:00
|
|
|
|
wdataset.child_name = this.options.child_name;
|
2011-10-10 09:44:38 +00:00
|
|
|
|
return wdataset.write.apply(wdataset, arguments);
|
2011-06-23 16:57:17 +00:00
|
|
|
|
},
|
2011-06-15 15:04:40 +00:00
|
|
|
|
on_select_elements: function(element_ids) {
|
|
|
|
|
},
|
|
|
|
|
on_click_element: function(ids) {
|
|
|
|
|
this.selected_ids = ids || [];
|
|
|
|
|
if(this.selected_ids.length > 0) {
|
2011-06-16 15:17:22 +00:00
|
|
|
|
this.$element.find(".oe_selectcreatepopup-search-select").removeAttr('disabled');
|
2011-06-15 15:04:40 +00:00
|
|
|
|
} else {
|
2011-06-16 15:17:22 +00:00
|
|
|
|
this.$element.find(".oe_selectcreatepopup-search-select").attr('disabled', "disabled");
|
2011-06-15 15:04:40 +00:00
|
|
|
|
}
|
2011-05-10 10:30:46 +00:00
|
|
|
|
},
|
|
|
|
|
new_object: function() {
|
|
|
|
|
var self = this;
|
2011-06-09 13:20:04 +00:00
|
|
|
|
if (this.searchview) {
|
|
|
|
|
this.searchview.hide();
|
|
|
|
|
}
|
|
|
|
|
if (this.view_list) {
|
|
|
|
|
this.view_list.$element.hide();
|
|
|
|
|
}
|
2011-05-10 10:30:46 +00:00
|
|
|
|
this.dataset.index = null;
|
2011-09-30 15:46:12 +00:00
|
|
|
|
this.view_form = new openerp.web.FormView(this, this.dataset, false, self.options.form_view_options);
|
2011-06-16 16:37:09 +00:00
|
|
|
|
if (this.options.alternative_form_view) {
|
|
|
|
|
this.view_form.set_embedded_view(this.options.alternative_form_view);
|
|
|
|
|
}
|
2011-09-14 15:14:59 +00:00
|
|
|
|
this.view_form.appendTo(this.$element.find("#" + this.element_id + "_view_form"));
|
2011-05-10 10:30:46 +00:00
|
|
|
|
this.view_form.on_loaded.add_last(function() {
|
|
|
|
|
var $buttons = self.view_form.$element.find(".oe_form_buttons");
|
2011-08-11 16:03:47 +00:00
|
|
|
|
$buttons.html(QWeb.render("SelectCreatePopup.form.buttons", {widget:self}));
|
|
|
|
|
var $nbutton = $buttons.find(".oe_selectcreatepopup-form-save-new");
|
|
|
|
|
$nbutton.click(function() {
|
2011-10-10 13:12:02 +00:00
|
|
|
|
$.when(self.view_form.do_save()).then(function() {
|
2012-01-10 16:58:02 +00:00
|
|
|
|
self.view_form.reload_mutex.exec(function() {
|
2011-10-12 14:10:07 +00:00
|
|
|
|
self.view_form.on_button_new();
|
|
|
|
|
});
|
2011-08-11 16:03:47 +00:00
|
|
|
|
});
|
|
|
|
|
});
|
2011-06-16 15:17:22 +00:00
|
|
|
|
var $nbutton = $buttons.find(".oe_selectcreatepopup-form-save");
|
2011-05-10 10:30:46 +00:00
|
|
|
|
$nbutton.click(function() {
|
2011-10-10 13:12:02 +00:00
|
|
|
|
$.when(self.view_form.do_save()).then(function() {
|
2012-01-10 16:58:02 +00:00
|
|
|
|
self.view_form.reload_mutex.exec(function() {
|
2011-10-12 14:10:07 +00:00
|
|
|
|
self.check_exit();
|
|
|
|
|
});
|
2011-08-11 16:03:47 +00:00
|
|
|
|
});
|
2011-05-10 10:30:46 +00:00
|
|
|
|
});
|
2011-06-16 15:17:22 +00:00
|
|
|
|
var $cbutton = $buttons.find(".oe_selectcreatepopup-form-close");
|
2011-05-10 10:30:46 +00:00
|
|
|
|
$cbutton.click(function() {
|
2011-08-11 16:03:47 +00:00
|
|
|
|
self.check_exit();
|
|
|
|
|
});
|
|
|
|
|
});
|
2011-05-10 10:30:46 +00:00
|
|
|
|
this.view_form.do_show();
|
2011-08-11 16:03:47 +00:00
|
|
|
|
},
|
|
|
|
|
check_exit: function() {
|
|
|
|
|
if (this.created_elements.length > 0) {
|
|
|
|
|
this.on_select_elements(this.created_elements);
|
|
|
|
|
}
|
|
|
|
|
this.stop();
|
2011-09-26 16:00:57 +00:00
|
|
|
|
},
|
|
|
|
|
on_default_get: function(res) {}
|
2011-05-03 16:17:25 +00:00
|
|
|
|
});
|
|
|
|
|
|
2011-09-05 11:03:09 +00:00
|
|
|
|
openerp.web.form.SelectCreateListView = openerp.web.ListView.extend({
|
2011-06-15 15:06:58 +00:00
|
|
|
|
do_add_record: function () {
|
|
|
|
|
this.popup.new_object();
|
|
|
|
|
},
|
2011-05-24 08:35:58 +00:00
|
|
|
|
select_record: function(index) {
|
2011-06-15 15:04:40 +00:00
|
|
|
|
this.popup.on_select_elements([this.dataset.ids[index]]);
|
2011-08-11 16:03:47 +00:00
|
|
|
|
this.popup.stop();
|
2011-06-15 15:04:40 +00:00
|
|
|
|
},
|
|
|
|
|
do_select: function(ids, records) {
|
|
|
|
|
this._super(ids, records);
|
|
|
|
|
this.popup.on_click_element(ids);
|
2011-05-09 11:52:33 +00:00
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2011-09-12 11:43:50 +00:00
|
|
|
|
/**
|
|
|
|
|
* @class
|
|
|
|
|
* @extends openerp.web.OldWidget
|
|
|
|
|
*/
|
|
|
|
|
openerp.web.form.FormOpenPopup = openerp.web.OldWidget.extend(/** @lends openerp.web.form.FormOpenPopup# */{
|
2011-07-13 09:44:11 +00:00
|
|
|
|
template: "FormOpenPopup",
|
|
|
|
|
/**
|
|
|
|
|
* options:
|
|
|
|
|
* - alternative_form_view
|
|
|
|
|
* - auto_write (default true)
|
2011-08-29 12:13:30 +00:00
|
|
|
|
* - read_function
|
2011-07-13 09:44:11 +00:00
|
|
|
|
* - parent_view
|
2011-12-19 17:30:55 +00:00
|
|
|
|
* - child_name
|
2011-09-30 15:46:12 +00:00
|
|
|
|
* - form_view_options
|
2011-10-31 10:43:04 +00:00
|
|
|
|
* - readonly
|
2011-07-13 09:44:11 +00:00
|
|
|
|
*/
|
|
|
|
|
show_element: function(model, row_id, context, options) {
|
|
|
|
|
this.model = model;
|
|
|
|
|
this.row_id = row_id;
|
|
|
|
|
this.context = context || {};
|
|
|
|
|
this.options = _.defaults(options || {}, {"auto_write": true});
|
2011-11-10 09:32:21 +00:00
|
|
|
|
this.render_element();
|
2011-12-22 09:40:12 +00:00
|
|
|
|
this.$element.dialog({
|
|
|
|
|
title: options.title || '',
|
|
|
|
|
modal: true,
|
|
|
|
|
width: 960,
|
|
|
|
|
height: 600
|
|
|
|
|
});
|
2011-07-13 09:44:11 +00:00
|
|
|
|
this.start();
|
|
|
|
|
},
|
|
|
|
|
start: function() {
|
|
|
|
|
this._super();
|
2011-09-05 11:03:09 +00:00
|
|
|
|
this.dataset = new openerp.web.form.FormOpenDataset(this, this.model, this.context);
|
2011-08-29 12:13:30 +00:00
|
|
|
|
this.dataset.fop = this;
|
2011-07-13 09:44:11 +00:00
|
|
|
|
this.dataset.ids = [this.row_id];
|
|
|
|
|
this.dataset.index = 0;
|
|
|
|
|
this.dataset.parent_view = this.options.parent_view;
|
2011-12-19 17:30:55 +00:00
|
|
|
|
this.dataset.child_name = this.options.child_name;
|
2011-07-13 09:44:11 +00:00
|
|
|
|
this.setup_form_view();
|
|
|
|
|
},
|
|
|
|
|
on_write: function(id, data) {
|
|
|
|
|
if (!this.options.auto_write)
|
|
|
|
|
return;
|
|
|
|
|
var self = this;
|
2011-09-05 11:03:09 +00:00
|
|
|
|
var wdataset = new openerp.web.DataSetSearch(this, this.model, this.context, this.domain);
|
2011-07-13 09:44:11 +00:00
|
|
|
|
wdataset.parent_view = this.options.parent_view;
|
2011-12-19 17:30:55 +00:00
|
|
|
|
wdataset.child_name = this.options.child_name;
|
2011-08-29 14:08:39 +00:00
|
|
|
|
wdataset.write(id, data, {}, function(r) {
|
2011-07-13 09:53:52 +00:00
|
|
|
|
self.on_write_completed();
|
2011-07-13 09:44:11 +00:00
|
|
|
|
});
|
|
|
|
|
},
|
2011-07-13 09:53:52 +00:00
|
|
|
|
on_write_completed: function() {},
|
2011-07-13 09:44:11 +00:00
|
|
|
|
setup_form_view: function() {
|
|
|
|
|
var self = this;
|
2011-12-07 09:42:42 +00:00
|
|
|
|
var FormClass = this.options.readonly
|
|
|
|
|
? openerp.web.views.get_object('page')
|
|
|
|
|
: openerp.web.views.get_object('form');
|
|
|
|
|
this.view_form = new FormClass(this, this.dataset, false, self.options.form_view_options);
|
2011-07-13 09:44:11 +00:00
|
|
|
|
if (this.options.alternative_form_view) {
|
|
|
|
|
this.view_form.set_embedded_view(this.options.alternative_form_view);
|
|
|
|
|
}
|
2011-09-14 15:14:59 +00:00
|
|
|
|
this.view_form.appendTo(this.$element.find("#" + this.element_id + "_view_form"));
|
2011-07-13 09:44:11 +00:00
|
|
|
|
this.view_form.on_loaded.add_last(function() {
|
|
|
|
|
var $buttons = self.view_form.$element.find(".oe_form_buttons");
|
|
|
|
|
$buttons.html(QWeb.render("FormOpenPopup.form.buttons"));
|
|
|
|
|
var $nbutton = $buttons.find(".oe_formopenpopup-form-save");
|
|
|
|
|
$nbutton.click(function() {
|
2011-10-13 11:58:28 +00:00
|
|
|
|
self.view_form.do_save().then(function() {
|
|
|
|
|
self.stop();
|
|
|
|
|
});
|
2011-07-13 09:44:11 +00:00
|
|
|
|
});
|
|
|
|
|
var $cbutton = $buttons.find(".oe_formopenpopup-form-close");
|
|
|
|
|
$cbutton.click(function() {
|
|
|
|
|
self.stop();
|
|
|
|
|
});
|
2011-10-31 11:09:37 +00:00
|
|
|
|
if (self.options.readonly) {
|
|
|
|
|
$nbutton.hide();
|
|
|
|
|
$cbutton.text(_t("Close"));
|
|
|
|
|
}
|
2011-07-13 09:44:11 +00:00
|
|
|
|
self.view_form.do_show();
|
|
|
|
|
});
|
|
|
|
|
this.dataset.on_write.add(this.on_write);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2011-10-10 12:40:44 +00:00
|
|
|
|
openerp.web.form.FormOpenDataset = openerp.web.ProxyDataSet.extend({
|
2011-08-29 12:13:30 +00:00
|
|
|
|
read_ids: function() {
|
|
|
|
|
if (this.fop.options.read_function) {
|
|
|
|
|
return this.fop.options.read_function.apply(null, arguments);
|
|
|
|
|
} else {
|
2011-08-29 12:28:37 +00:00
|
|
|
|
return this._super.apply(this, arguments);
|
2011-08-29 12:13:30 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2011-09-05 11:03:09 +00:00
|
|
|
|
openerp.web.form.FieldReference = openerp.web.form.Field.extend({
|
2011-09-19 10:15:17 +00:00
|
|
|
|
template: 'FieldReference',
|
2011-08-18 17:45:08 +00:00
|
|
|
|
init: function(view, node) {
|
|
|
|
|
this._super(view, node);
|
|
|
|
|
this.fields_view = {
|
|
|
|
|
fields: {
|
|
|
|
|
selection: {
|
|
|
|
|
selection: view.fields_view.fields[this.name].selection
|
|
|
|
|
},
|
|
|
|
|
m2o: {
|
|
|
|
|
relation: null
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-09-06 08:58:53 +00:00
|
|
|
|
};
|
2011-08-18 17:45:08 +00:00
|
|
|
|
this.get_fields_values = view.get_fields_values;
|
2012-01-24 10:38:47 +00:00
|
|
|
|
this.get_selected_ids = view.get_selected_ids;
|
2012-02-20 12:22:08 +00:00
|
|
|
|
this.do_onchange = this.on_form_changed = this.do_notify_change = this.on_nop;
|
2011-10-11 10:54:26 +00:00
|
|
|
|
this.dataset = this.view.dataset;
|
|
|
|
|
this.widgets_counter = 0;
|
|
|
|
|
this.view_id = 'reference_' + _.uniqueId();
|
2011-08-18 17:45:08 +00:00
|
|
|
|
this.widgets = {};
|
|
|
|
|
this.fields = {};
|
2011-12-01 15:50:29 +00:00
|
|
|
|
this.fields_order = [];
|
2011-09-05 11:03:09 +00:00
|
|
|
|
this.selection = new openerp.web.form.FieldSelection(this, { attrs: {
|
2011-08-18 17:45:08 +00:00
|
|
|
|
name: 'selection',
|
|
|
|
|
widget: 'selection'
|
|
|
|
|
}});
|
2011-12-08 12:28:51 +00:00
|
|
|
|
this.reference_ready = true;
|
2011-08-18 17:45:08 +00:00
|
|
|
|
this.selection.on_value_changed.add_last(this.on_selection_changed);
|
2011-09-05 11:03:09 +00:00
|
|
|
|
this.m2o = new openerp.web.form.FieldMany2One(this, { attrs: {
|
2011-08-18 17:45:08 +00:00
|
|
|
|
name: 'm2o',
|
|
|
|
|
widget: 'many2one'
|
|
|
|
|
}});
|
2012-01-25 10:08:24 +00:00
|
|
|
|
this.m2o.on_ui_change.add_last(this.on_ui_change);
|
2011-08-18 17:45:08 +00:00
|
|
|
|
},
|
|
|
|
|
on_nop: function() {
|
|
|
|
|
},
|
|
|
|
|
on_selection_changed: function() {
|
2011-12-08 12:28:51 +00:00
|
|
|
|
if (this.reference_ready) {
|
|
|
|
|
var sel = this.selection.get_value();
|
|
|
|
|
this.m2o.field.relation = sel;
|
|
|
|
|
this.m2o.set_value(null);
|
|
|
|
|
this.m2o.$element.toggle(sel !== false);
|
|
|
|
|
}
|
2011-08-18 17:45:08 +00:00
|
|
|
|
},
|
|
|
|
|
start: function() {
|
[FIX] handling of focus on m2o fields (in editable list row)
* Throw out focusin/focusout: the m2o widget's completion list is
created at the page top (body) so the editable listview can't
generically handle arbitrary widgets via mere focusin/focusout.
* Move responsibility of focus/blur events to the form view and its
widgets.
* Events could not just be named ``focus`` and ``blur`` due to usage
of jquery's event system: jquery will automatically call a method of
the event's name if it exists on the object, which conflicts with
.web.form.Field#focus and leads to an infinite loop (as Field#focus
focuses the field's root element, which triggers the focus event,
which calls the focus method,...) => form-* and widget-*, can be
switched back in trunk
* m2o mess kind-of complex, basically:
- the core input and the menu button behave as blur/focus triggers
- when the autocompletion list is clicked, it will temporarily
remove the focus from the input (blurring it), and put it back
later... on a timer. Issue is the timer, we don't want to rely on
having a bigger timer (as later revisions of the library may
change our timings and it's iffy to rely on timers conserving
order perfectly); on the other hand we know the focus *will* come
back to the input eventually. So we can just avoid propagating
blur iif the blur is the consequence of having clicked on the
completion list.
- roughly the same thing happens when clicking on $drop_down (after
fixing the handling of its final focus to be consistent, as
$drop_down would not re-focus the input if it was *closing* the
completion list)
- pretty sure the menu thing does *not work at all*, but I don't
have the courage of fixing it before committing this part.
Date/datetime widget remains to be handled, basically the core focus
handling is the same as in e.g. a charfield *but* needs to handle (and
ignore) loss of focus from clicking inside the picker
widget. Expecting the level of suck to reach unknown heights.
bzr revid: xmo@openerp.com-20120619072518-lsrhzij5asxt2aea
2012-06-19 07:25:18 +00:00
|
|
|
|
var self = this;
|
2011-08-18 17:45:08 +00:00
|
|
|
|
this._super();
|
|
|
|
|
this.selection.start();
|
|
|
|
|
this.m2o.start();
|
[FIX] handling of focus on m2o fields (in editable list row)
* Throw out focusin/focusout: the m2o widget's completion list is
created at the page top (body) so the editable listview can't
generically handle arbitrary widgets via mere focusin/focusout.
* Move responsibility of focus/blur events to the form view and its
widgets.
* Events could not just be named ``focus`` and ``blur`` due to usage
of jquery's event system: jquery will automatically call a method of
the event's name if it exists on the object, which conflicts with
.web.form.Field#focus and leads to an infinite loop (as Field#focus
focuses the field's root element, which triggers the focus event,
which calls the focus method,...) => form-* and widget-*, can be
switched back in trunk
* m2o mess kind-of complex, basically:
- the core input and the menu button behave as blur/focus triggers
- when the autocompletion list is clicked, it will temporarily
remove the focus from the input (blurring it), and put it back
later... on a timer. Issue is the timer, we don't want to rely on
having a bigger timer (as later revisions of the library may
change our timings and it's iffy to rely on timers conserving
order perfectly); on the other hand we know the focus *will* come
back to the input eventually. So we can just avoid propagating
blur iif the blur is the consequence of having clicked on the
completion list.
- roughly the same thing happens when clicking on $drop_down (after
fixing the handling of its final focus to be consistent, as
$drop_down would not re-focus the input if it was *closing* the
completion list)
- pretty sure the menu thing does *not work at all*, but I don't
have the courage of fixing it before committing this part.
Date/datetime widget remains to be handled, basically the core focus
handling is the same as in e.g. a charfield *but* needs to handle (and
ignore) loss of focus from clicking inside the picker
widget. Expecting the level of suck to reach unknown heights.
bzr revid: xmo@openerp.com-20120619072518-lsrhzij5asxt2aea
2012-06-19 07:25:18 +00:00
|
|
|
|
$(this.selection).add($(this.m2o)).bind({
|
|
|
|
|
'focus': function () { $(self).trigger('widget-focus'); },
|
|
|
|
|
'blur': function () { $(self).trigger('widget-blur'); }
|
|
|
|
|
})
|
2011-08-18 17:45:08 +00:00
|
|
|
|
},
|
|
|
|
|
is_valid: function() {
|
|
|
|
|
return this.required === false || typeof(this.get_value()) === 'string';
|
|
|
|
|
},
|
|
|
|
|
is_dirty: function() {
|
|
|
|
|
return this.selection.is_dirty() || this.m2o.is_dirty();
|
|
|
|
|
},
|
|
|
|
|
set_value: function(value) {
|
|
|
|
|
this._super(value);
|
2011-12-08 12:28:51 +00:00
|
|
|
|
this.reference_ready = false;
|
|
|
|
|
var vals = [], sel_val, m2o_val;
|
2011-08-18 17:45:08 +00:00
|
|
|
|
if (typeof(value) === 'string') {
|
2011-12-08 12:28:51 +00:00
|
|
|
|
vals = value.split(',');
|
2011-08-18 17:45:08 +00:00
|
|
|
|
}
|
2011-12-08 12:28:51 +00:00
|
|
|
|
sel_val = vals[0] || false;
|
|
|
|
|
m2o_val = vals[1] ? parseInt(vals[1], 10) : false;
|
|
|
|
|
this.selection.set_value(sel_val);
|
|
|
|
|
this.m2o.field.relation = sel_val;
|
|
|
|
|
this.m2o.set_value(m2o_val);
|
|
|
|
|
this.m2o.$element.toggle(sel_val !== false);
|
|
|
|
|
this.reference_ready = true;
|
2011-08-18 17:45:08 +00:00
|
|
|
|
},
|
|
|
|
|
get_value: function() {
|
|
|
|
|
var model = this.selection.get_value(),
|
|
|
|
|
id = this.m2o.get_value();
|
|
|
|
|
if (typeof(model) === 'string' && typeof(id) === 'number') {
|
|
|
|
|
return model + ',' + id;
|
|
|
|
|
} else {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2011-09-05 11:03:09 +00:00
|
|
|
|
openerp.web.form.FieldBinary = openerp.web.form.Field.extend({
|
2011-05-12 15:22:48 +00:00
|
|
|
|
init: function(view, node) {
|
|
|
|
|
this._super(view, node);
|
2011-05-18 22:36:16 +00:00
|
|
|
|
this.iframe = this.element_id + '_iframe';
|
2011-05-19 12:58:45 +00:00
|
|
|
|
this.binary_value = false;
|
2011-05-12 15:22:48 +00:00
|
|
|
|
},
|
2011-05-18 15:33:56 +00:00
|
|
|
|
start: function() {
|
|
|
|
|
this._super.apply(this, arguments);
|
2011-05-18 22:36:16 +00:00
|
|
|
|
this.$element.find('input.oe-binary-file').change(this.on_file_change);
|
2011-05-23 13:38:40 +00:00
|
|
|
|
this.$element.find('button.oe-binary-file-save').click(this.on_save_as);
|
2011-05-19 12:58:45 +00:00
|
|
|
|
this.$element.find('.oe-binary-file-clear').click(this.on_clear);
|
2011-05-18 15:33:56 +00:00
|
|
|
|
},
|
2011-05-19 12:58:45 +00:00
|
|
|
|
human_filesize : function(size) {
|
|
|
|
|
var units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
|
|
|
|
|
var i = 0;
|
|
|
|
|
while (size >= 1024) {
|
|
|
|
|
size /= 1024;
|
|
|
|
|
++i;
|
|
|
|
|
}
|
|
|
|
|
return size.toFixed(2) + ' ' + units[i];
|
2011-05-19 10:17:26 +00:00
|
|
|
|
},
|
2011-05-23 13:38:40 +00:00
|
|
|
|
on_file_change: function(e) {
|
2011-05-19 10:17:26 +00:00
|
|
|
|
// 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
|
2011-05-18 22:36:16 +00:00
|
|
|
|
window[this.iframe] = this.on_file_uploaded;
|
2011-05-23 13:38:40 +00:00
|
|
|
|
if ($(e.target).val() != '') {
|
|
|
|
|
this.$element.find('form.oe-binary-form input[name=session_id]').val(this.session.session_id);
|
|
|
|
|
this.$element.find('form.oe-binary-form').submit();
|
2011-10-10 13:33:52 +00:00
|
|
|
|
this.$element.find('.oe-binary-progress').show();
|
|
|
|
|
this.$element.find('.oe-binary').hide();
|
2011-05-23 13:38:40 +00:00
|
|
|
|
}
|
2011-05-18 22:36:16 +00:00
|
|
|
|
},
|
2011-09-06 11:49:21 +00:00
|
|
|
|
on_file_uploaded: function(size, name, content_type, file_base64) {
|
2011-05-18 22:36:16 +00:00
|
|
|
|
delete(window[this.iframe]);
|
|
|
|
|
if (size === false) {
|
2011-10-13 10:26:53 +00:00
|
|
|
|
this.do_warn("File Upload", "There was a problem while uploading your file");
|
2011-08-11 02:42:46 +00:00
|
|
|
|
// TODO: use openerp web crashmanager
|
2011-09-12 13:56:03 +00:00
|
|
|
|
console.warn("Error while uploading file : ", name);
|
2011-05-18 22:36:16 +00:00
|
|
|
|
} else {
|
2011-05-19 12:58:45 +00:00
|
|
|
|
this.on_file_uploaded_and_valid.apply(this, arguments);
|
2011-05-19 10:17:26 +00:00
|
|
|
|
this.on_ui_change();
|
2011-05-18 22:36:16 +00:00
|
|
|
|
}
|
2011-10-10 13:33:52 +00:00
|
|
|
|
this.$element.find('.oe-binary-progress').hide();
|
|
|
|
|
this.$element.find('.oe-binary').show();
|
2011-05-18 22:36:16 +00:00
|
|
|
|
},
|
2011-09-06 11:49:21 +00:00
|
|
|
|
on_file_uploaded_and_valid: function(size, name, content_type, file_base64) {
|
2011-05-19 12:58:45 +00:00
|
|
|
|
},
|
2011-05-23 13:38:40 +00:00
|
|
|
|
on_save_as: function() {
|
2012-02-08 10:39:35 +00:00
|
|
|
|
$.blockUI();
|
|
|
|
|
this.session.get_file({
|
|
|
|
|
url: '/web/binary/saveas_ajax',
|
|
|
|
|
data: {data: JSON.stringify({
|
|
|
|
|
model: this.view.dataset.model,
|
|
|
|
|
id: (this.view.datarecord.id || ''),
|
|
|
|
|
field: this.name,
|
|
|
|
|
filename_field: (this.node.attrs.filename || ''),
|
|
|
|
|
context: this.view.dataset.get_context()
|
|
|
|
|
})},
|
|
|
|
|
complete: $.unblockUI,
|
|
|
|
|
error: openerp.webclient.crashmanager.on_rpc_error
|
|
|
|
|
});
|
2011-05-23 13:38:40 +00:00
|
|
|
|
},
|
2012-04-05 11:26:19 +00:00
|
|
|
|
set_filename: function(value) {
|
|
|
|
|
var filename = this.node.attrs.filename;
|
|
|
|
|
if (this.view.fields[filename]) {
|
|
|
|
|
this.view.fields[filename].set_value(value);
|
|
|
|
|
this.view.fields[filename].on_ui_change();
|
|
|
|
|
}
|
|
|
|
|
},
|
2011-05-18 15:33:56 +00:00
|
|
|
|
on_clear: function() {
|
|
|
|
|
if (this.value !== false) {
|
|
|
|
|
this.value = false;
|
2011-05-19 12:58:45 +00:00
|
|
|
|
this.binary_value = false;
|
2011-05-18 15:33:56 +00:00
|
|
|
|
this.on_ui_change();
|
|
|
|
|
}
|
2011-05-19 10:17:26 +00:00
|
|
|
|
return false;
|
2011-05-19 12:58:45 +00:00
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2011-09-05 11:03:09 +00:00
|
|
|
|
openerp.web.form.FieldBinaryFile = openerp.web.form.FieldBinary.extend({
|
2011-09-19 10:15:17 +00:00
|
|
|
|
template: 'FieldBinaryFile',
|
2011-10-10 13:33:52 +00:00
|
|
|
|
update_dom: function() {
|
|
|
|
|
this._super.apply(this, arguments);
|
|
|
|
|
this.$element.find('.oe-binary-file-set, .oe-binary-file-clear').toggle(!this.readonly);
|
2012-02-13 16:31:20 +00:00
|
|
|
|
this.$element.find('input[type=text]').prop('readonly', this.readonly);
|
2011-10-10 13:33:52 +00:00
|
|
|
|
},
|
2011-05-19 12:58:45 +00:00
|
|
|
|
set_value: function(value) {
|
|
|
|
|
this._super.apply(this, arguments);
|
2011-12-15 12:04:15 +00:00
|
|
|
|
var show_value;
|
|
|
|
|
if (this.node.attrs.filename) {
|
|
|
|
|
show_value = this.view.datarecord[this.node.attrs.filename] || '';
|
|
|
|
|
} else {
|
|
|
|
|
show_value = (value != null && value !== false) ? value : '';
|
|
|
|
|
}
|
2011-05-19 12:58:45 +00:00
|
|
|
|
this.$element.find('input').eq(0).val(show_value);
|
|
|
|
|
},
|
2011-09-06 11:49:21 +00:00
|
|
|
|
on_file_uploaded_and_valid: function(size, name, content_type, file_base64) {
|
2011-05-19 12:58:45 +00:00
|
|
|
|
this.value = file_base64;
|
|
|
|
|
this.binary_value = true;
|
2011-12-15 12:04:15 +00:00
|
|
|
|
var show_value = name + " (" + this.human_filesize(size) + ")";
|
2011-05-19 12:58:45 +00:00
|
|
|
|
this.$element.find('input').eq(0).val(show_value);
|
|
|
|
|
this.set_filename(name);
|
|
|
|
|
},
|
|
|
|
|
on_clear: function() {
|
|
|
|
|
this._super.apply(this, arguments);
|
|
|
|
|
this.$element.find('input').eq(0).val('');
|
|
|
|
|
this.set_filename('');
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2011-09-05 11:03:09 +00:00
|
|
|
|
openerp.web.form.FieldBinaryImage = openerp.web.form.FieldBinary.extend({
|
2011-09-19 10:15:17 +00:00
|
|
|
|
template: 'FieldBinaryImage',
|
2011-05-19 12:58:45 +00:00
|
|
|
|
start: function() {
|
|
|
|
|
this._super.apply(this, arguments);
|
|
|
|
|
this.$image = this.$element.find('img.oe-binary-image');
|
|
|
|
|
},
|
2011-10-10 13:33:52 +00:00
|
|
|
|
update_dom: function() {
|
|
|
|
|
this._super.apply(this, arguments);
|
|
|
|
|
this.$element.find('.oe-binary').toggle(!this.readonly);
|
|
|
|
|
},
|
2011-07-14 13:29:57 +00:00
|
|
|
|
set_value: function(value) {
|
|
|
|
|
this._super.apply(this, arguments);
|
|
|
|
|
this.set_image_maxwidth();
|
2011-09-05 11:03:09 +00:00
|
|
|
|
var url = '/web/binary/image?session_id=' + this.session.session_id + '&model=' +
|
2011-09-06 08:58:53 +00:00
|
|
|
|
this.view.dataset.model +'&id=' + (this.view.datarecord.id || '') + '&field=' + this.name + '&t=' + (new Date().getTime());
|
2011-07-14 13:29:57 +00:00
|
|
|
|
this.$image.attr('src', url);
|
|
|
|
|
},
|
2011-05-19 12:58:45 +00:00
|
|
|
|
set_image_maxwidth: function() {
|
|
|
|
|
this.$image.css('max-width', this.$element.width());
|
|
|
|
|
},
|
|
|
|
|
on_file_change: function() {
|
|
|
|
|
this.set_image_maxwidth();
|
|
|
|
|
this._super.apply(this, arguments);
|
|
|
|
|
},
|
2011-09-06 11:49:21 +00:00
|
|
|
|
on_file_uploaded_and_valid: function(size, name, content_type, file_base64) {
|
2011-05-19 12:58:45 +00:00
|
|
|
|
this.value = file_base64;
|
|
|
|
|
this.binary_value = true;
|
|
|
|
|
this.$image.attr('src', 'data:' + (content_type || 'image/png') + ';base64,' + file_base64);
|
2012-04-05 11:26:19 +00:00
|
|
|
|
this.set_filename(name);
|
2011-05-19 12:58:45 +00:00
|
|
|
|
},
|
|
|
|
|
on_clear: function() {
|
|
|
|
|
this._super.apply(this, arguments);
|
2011-09-05 11:03:09 +00:00
|
|
|
|
this.$image.attr('src', '/web/static/src/img/placeholder.png');
|
2012-04-05 11:26:19 +00:00
|
|
|
|
this.set_filename('');
|
2011-05-12 15:22:48 +00:00
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2011-09-08 14:58:45 +00:00
|
|
|
|
openerp.web.form.FieldStatus = openerp.web.form.Field.extend({
|
2011-09-15 12:37:54 +00:00
|
|
|
|
template: "EmptyComponent",
|
2011-09-08 14:58:45 +00:00
|
|
|
|
start: function() {
|
2011-09-08 15:06:45 +00:00
|
|
|
|
this._super();
|
2011-09-08 16:35:36 +00:00
|
|
|
|
this.selected_value = null;
|
2011-10-19 12:50:34 +00:00
|
|
|
|
|
2011-09-08 15:06:45 +00:00
|
|
|
|
this.render_list();
|
2011-09-08 14:58:45 +00:00
|
|
|
|
},
|
|
|
|
|
set_value: function(value) {
|
|
|
|
|
this._super(value);
|
2011-09-08 16:35:36 +00:00
|
|
|
|
this.selected_value = value;
|
2011-10-19 12:50:34 +00:00
|
|
|
|
|
2011-09-08 15:06:45 +00:00
|
|
|
|
this.render_list();
|
2011-09-08 14:58:45 +00:00
|
|
|
|
},
|
2011-09-08 15:06:45 +00:00
|
|
|
|
render_list: function() {
|
2011-09-08 16:35:36 +00:00
|
|
|
|
var self = this;
|
|
|
|
|
var shown = _.map(((this.node.attrs || {}).statusbar_visible || "").split(","),
|
2011-11-15 12:30:59 +00:00
|
|
|
|
function(x) { return _.str.trim(x); });
|
2011-09-13 09:34:09 +00:00
|
|
|
|
shown = _.select(shown, function(x) { return x.length > 0; });
|
2011-10-19 12:50:34 +00:00
|
|
|
|
|
2011-09-08 16:35:36 +00:00
|
|
|
|
if (shown.length == 0) {
|
|
|
|
|
this.to_show = this.field.selection;
|
|
|
|
|
} else {
|
|
|
|
|
this.to_show = _.select(this.field.selection, function(x) {
|
|
|
|
|
return _.indexOf(shown, x[0]) !== -1 || x[0] === self.selected_value;
|
|
|
|
|
});
|
|
|
|
|
}
|
2011-10-19 12:50:34 +00:00
|
|
|
|
|
2011-09-08 15:37:15 +00:00
|
|
|
|
var content = openerp.web.qweb.render("FieldStatus.content", {widget: this, _:_});
|
2011-09-08 15:09:32 +00:00
|
|
|
|
this.$element.html(content);
|
2011-10-19 12:50:34 +00:00
|
|
|
|
|
2011-09-09 14:34:50 +00:00
|
|
|
|
var colors = JSON.parse((this.node.attrs || {}).statusbar_colors || "{}");
|
2011-09-08 17:00:12 +00:00
|
|
|
|
var color = colors[this.selected_value];
|
|
|
|
|
if (color) {
|
|
|
|
|
var elem = this.$element.find("li.oe-arrow-list-selected span");
|
|
|
|
|
elem.css("border-color", color);
|
2011-10-17 12:09:01 +00:00
|
|
|
|
if (this.check_white(color))
|
|
|
|
|
elem.css("color", "white");
|
2011-09-08 17:00:12 +00:00
|
|
|
|
elem = this.$element.find("li.oe-arrow-list-selected .oe-arrow-list-before");
|
2012-04-24 10:03:55 +00:00
|
|
|
|
elem.css("border-left-color", "transparent");
|
2011-09-08 17:00:12 +00:00
|
|
|
|
elem = this.$element.find("li.oe-arrow-list-selected .oe-arrow-list-after");
|
2012-04-24 10:03:55 +00:00
|
|
|
|
elem.css("border-color", "transparent");
|
2011-09-08 17:00:12 +00:00
|
|
|
|
elem.css("border-left-color", color);
|
|
|
|
|
}
|
2011-10-17 12:09:01 +00:00
|
|
|
|
},
|
|
|
|
|
check_white: function(color) {
|
|
|
|
|
var div = $("<div></div>");
|
|
|
|
|
div.css("display", "none");
|
|
|
|
|
div.css("color", color);
|
2012-01-04 15:53:29 +00:00
|
|
|
|
div.appendTo($("body"));
|
2011-10-17 12:09:01 +00:00
|
|
|
|
var ncolor = div.css("color");
|
|
|
|
|
div.remove();
|
|
|
|
|
var res = /^\s*rgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)\s*$/.exec(ncolor);
|
|
|
|
|
if (!res) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
var comps = [parseInt(res[1]), parseInt(res[2]), parseInt(res[3])];
|
|
|
|
|
var lum = comps[0] * 0.3 + comps[1] * 0.59 + comps[1] * 0.11;
|
|
|
|
|
if (lum < 128) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
2011-09-08 14:58:45 +00:00
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2011-12-19 09:59:24 +00:00
|
|
|
|
openerp.web.form.WidgetHtml = openerp.web.form.Widget.extend({
|
|
|
|
|
render: function () {
|
|
|
|
|
var $root = $('<div class="oe_form_html_view">');
|
|
|
|
|
this.render_children(this, $root);
|
|
|
|
|
return $root.html();
|
2011-10-25 18:50:30 +00:00
|
|
|
|
},
|
2011-12-19 09:59:24 +00:00
|
|
|
|
render_children: function (object, $into) {
|
|
|
|
|
var self = this,
|
|
|
|
|
fields = this.view.fields_view.fields;
|
|
|
|
|
_(object.children).each(function (child) {
|
|
|
|
|
if (typeof child === 'string') {
|
|
|
|
|
$into.text(child);
|
|
|
|
|
} else if (child.tag === 'field') {
|
2011-12-21 10:54:40 +00:00
|
|
|
|
$into.append(
|
|
|
|
|
new (self.view.registry.get_object('frame'))(
|
|
|
|
|
self.view, {tag: 'ueule', attrs: {}, children: [child] })
|
|
|
|
|
.render());
|
2011-12-19 09:59:24 +00:00
|
|
|
|
} else {
|
2011-12-21 10:37:50 +00:00
|
|
|
|
var $child = $(document.createElement(child.tag))
|
|
|
|
|
.attr(child.attrs)
|
2011-12-19 09:59:24 +00:00
|
|
|
|
.appendTo($into);
|
|
|
|
|
self.render_children(child, $child);
|
2011-09-19 13:57:17 +00:00
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
2011-03-31 10:57:51 +00:00
|
|
|
|
/**
|
2011-09-05 11:03:09 +00:00
|
|
|
|
* Registry of form widgets, called by :js:`openerp.web.FormView`
|
2011-03-31 10:57:51 +00:00
|
|
|
|
*/
|
2011-09-05 11:03:09 +00:00
|
|
|
|
openerp.web.form.widgets = new openerp.web.Registry({
|
|
|
|
|
'frame' : 'openerp.web.form.WidgetFrame',
|
2011-11-10 10:10:06 +00:00
|
|
|
|
'group' : 'openerp.web.form.WidgetGroup',
|
2011-09-05 11:03:09 +00:00
|
|
|
|
'notebook' : 'openerp.web.form.WidgetNotebook',
|
2011-10-04 07:43:21 +00:00
|
|
|
|
'notebookpage' : 'openerp.web.form.WidgetNotebookPage',
|
2011-09-05 11:03:09 +00:00
|
|
|
|
'separator' : 'openerp.web.form.WidgetSeparator',
|
|
|
|
|
'label' : 'openerp.web.form.WidgetLabel',
|
|
|
|
|
'button' : 'openerp.web.form.WidgetButton',
|
|
|
|
|
'char' : 'openerp.web.form.FieldChar',
|
2012-01-25 14:04:38 +00:00
|
|
|
|
'id' : 'openerp.web.form.FieldID',
|
2011-09-05 11:03:09 +00:00
|
|
|
|
'email' : 'openerp.web.form.FieldEmail',
|
|
|
|
|
'url' : 'openerp.web.form.FieldUrl',
|
|
|
|
|
'text' : 'openerp.web.form.FieldText',
|
|
|
|
|
'date' : 'openerp.web.form.FieldDate',
|
|
|
|
|
'datetime' : 'openerp.web.form.FieldDatetime',
|
|
|
|
|
'selection' : 'openerp.web.form.FieldSelection',
|
|
|
|
|
'many2one' : 'openerp.web.form.FieldMany2One',
|
|
|
|
|
'many2many' : 'openerp.web.form.FieldMany2Many',
|
|
|
|
|
'one2many' : 'openerp.web.form.FieldOne2Many',
|
|
|
|
|
'one2many_list' : 'openerp.web.form.FieldOne2Many',
|
|
|
|
|
'reference' : 'openerp.web.form.FieldReference',
|
|
|
|
|
'boolean' : 'openerp.web.form.FieldBoolean',
|
|
|
|
|
'float' : 'openerp.web.form.FieldFloat',
|
|
|
|
|
'integer': 'openerp.web.form.FieldFloat',
|
|
|
|
|
'float_time': 'openerp.web.form.FieldFloat',
|
|
|
|
|
'progressbar': 'openerp.web.form.FieldProgressBar',
|
|
|
|
|
'image': 'openerp.web.form.FieldBinaryImage',
|
2011-09-08 14:58:45 +00:00
|
|
|
|
'binary': 'openerp.web.form.FieldBinaryFile',
|
2011-12-19 09:59:24 +00:00
|
|
|
|
'statusbar': 'openerp.web.form.FieldStatus',
|
|
|
|
|
'html': 'openerp.web.form.WidgetHtml'
|
2011-03-31 10:57:51 +00:00
|
|
|
|
});
|
2011-10-31 10:27:52 +00:00
|
|
|
|
|
2011-03-30 14:00:48 +00:00
|
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax:
|