[IMP] Web and all views: ControlPanel atomic update

Change the logic of rendering and displaying/hidding the control
elements (buttons, sidebar and pager) of all views. The views do no
more render automatically their elements as this is rather done
through the render_[element] function called by the ViewManager.
This function only appends the element to the $node given as argument.
If no argument is given, then it may appends it to the DOM directly,
within the adequate div of its template.
This allows the ViewManager to attach/detach all elements of the
ControlPanel in an atomic may, to prevent the ControlPanel from
flickering.
Also detach the contents of the ControlPanel in headless mode. Headless
means that the ControlPanel exists, but is hidden. Detaching its
contents permits to have a cleaner DOM.

Also remove the oe_form_dirty class on the buttons in the FormView, as
this class is only used on the view itself.

Side changes:
* base_import: import.js:
    Extend render_buttons function to be compliant with the new buttons
    rendering logic
* board: dashboard.js:
    Set flag search_view to True as the searchview is needed for the
    Views displayed in the Dashboard to retrieve the records to display.
    Necessary since we do no more construct the searchview when this
    is set to false
* google_drive: drive.js,
  share: share.js:
    Module extending the Sidebar widget often add their own buttons
    by extending the start(), which is called each time the Widget
    is appended to the DOM. With our new design, this could be done
    in start() anymore as it should be done only one. It is now done
    in init() instead.
This commit is contained in:
Aaron Bohy
2015-03-20 16:34:39 +01:00
parent 7aa0836420
commit 1280bf2513
21 changed files with 671 additions and 492 deletions
+9 -3
View File
@@ -47,13 +47,18 @@ function jsonp(form, attributes, callback) {
// if true, the 'Import', 'Export', etc... buttons will be shown
ListView.prototype.defaults.import_enabled = true;
ListView.include({
load_list: function () {
/**
* Extend the render_buttons function of ListView by adding an event listener
* on the import button.
* @return {jQuery} the rendered buttons
*/
render_buttons: function() {
var self = this;
var add_button = false;
if (!this.$buttons) {
if (!this.$buttons) { // Ensures that this is only done once
add_button = true;
}
this._super.apply(this, arguments);
this._super.apply(this, arguments); // Sets this.$buttons
if(add_button) {
this.$buttons.on('click', '.oe_list_button_import', function() {
self.do_action({
@@ -75,6 +80,7 @@ ListView.include({
return false;
});
}
return this.$buttons;
}
});
+1 -1
View File
@@ -204,7 +204,7 @@ var DashBoard = form_common.FormWidget.extend({
}
action.flags = {
search_view : false,
search_view : true, // Required to get the records
sidebar : false,
views_switcher : false,
action_buttons : false,
+1 -1
View File
@@ -6,7 +6,7 @@ var Model = require('web.Model');
var Sidebar = require('web.Sidebar');
Sidebar.include({
start: function () {
init: function () {
var self = this;
var ids;
this._super.apply(this, arguments);
+2 -3
View File
@@ -61,10 +61,9 @@ function has_share(yes, no) {
/* Extend the Sidebar to add Share and Embed links in the 'More' menu */
Sidebar.include({
start: function() {
init: function(parent) {
var self = this;
this._super(this);
this._super(parent);
has_share(function() {
self.add_items('other', [
{ label: _t('Share'),
-18
View File
@@ -361,21 +361,6 @@
background: #dc5f59;
font-weight: bold;
}
.openerp .oe_form_dirty button.oe_highlight_on_dirty {
background-color: #c74a44;
background-image: -webkit-gradient(linear, left top, left bottom, from(#dc5f59), to(#b33630));
background-image: -webkit-linear-gradient(top, #dc5f59, #b33630);
background-image: -moz-linear-gradient(top, #dc5f59, #b33630);
background-image: -ms-linear-gradient(top, #dc5f59, #b33630);
background-image: -o-linear-gradient(top, #dc5f59, #b33630);
background-image: linear-gradient(to bottom, #dc5f59, #b33630);
-moz-box-shadow: none;
-webkit-box-shadow: none;
box-shadow: none;
}
.openerp .oe_form_dirty button.oe_highlight_on_dirty:hover {
background: #ED6F6A;
}
.openerp .oe_warning_redirect {
border: none !important;
padding: 0 !important;
@@ -1081,9 +1066,6 @@
.openerp .oe-control-panel .oe-cp-buttons {
display: inline-block;
}
.openerp .oe-control-panel .oe-cp-buttons > div {
display: none;
}
.openerp .oe-control-panel .oe-cp-sidebar {
display: inline-block;
float: right;
-7
View File
@@ -370,11 +370,6 @@ $sheet-padding: 16px
color: white
background: #dc5f59
font-weight: bold
button.oe_highlight_on_dirty
@include vertical-gradient(#dc5f59, #b33630)
@include box-shadow(none)
&:hover
background: #ED6F6A
.oe_warning_redirect
border: none !important
padding: 0 !important
@@ -922,8 +917,6 @@ $sheet-padding: 16px
content: "\f036"
.oe-cp-buttons
display: inline-block
> div
display: none
.oe-cp-sidebar
display: inline-block
float: right
+21 -24
View File
@@ -22,7 +22,7 @@ var State = core.Class.extend({
destroy: function() {},
get_widget: function() {},
get_title: function() { return this.title; },
get_flags: function() { return this.flags; },
is_headless: function() { return this.flags.headless; },
get_action: function() {},
get_active_view: function() {},
get_dataset: function() {},
@@ -147,17 +147,14 @@ var ActionManager = Widget.extend({
// Sets the main ControlPanel state
// AAB: temporary restrict the use of main control panel to act_window actions
if (action.type === 'ir.actions.act_window') {
// if (old_state) old_state.disable();
// new_state.enable();
this.main_control_panel.set_state(new_state, old_state);
// Expose the ControlPanel nodes to the inner_widget only if the ControlPanel is
// displayed so that the inner_widget don't insert stuff in hidden nodes
var new_state_flags = new_state.get_flags();
if (!new_state_flags.headless) {
this.inner_widget.set_external_nodes(this.main_control_panel.get_cp_nodes());
if (old_state) {
// Save the previous state control_panel content to restore it later
old_state.cp_content = this.main_control_panel.get_content();
}
}
// Append the inner_widget and hide the old one
return $.when(this.inner_widget.appendTo(this.$el)).done(function () {
if (old_widget) {
old_widget.$el.hide();
@@ -175,7 +172,7 @@ var ActionManager = Widget.extend({
title: view.controller.get('title') || state.get_title(),
index: index,
widget: state,
};
};
});
} else {
return { title: state.get_title(), widget: state };
@@ -233,17 +230,16 @@ var ActionManager = Widget.extend({
}
// Set the control panel new state
// Put in VMState?
// Inform the ControlPanel that the current state changed
self.main_control_panel.set_state(state, this.get_current_state());
// Inform the ControlPanel that the current state changed (mainly restore the searchview
// for the ViewManager to be able to do select_view, i.e. switch_mode)
// Storing in old_state is not necessary here
var old_state = this.get_current_state();
this.main_control_panel.set_state(state, old_state);
var state_index = this.states.indexOf(state),
def = $.when(state.widget.select_view && state.widget.select_view(index));
self.clear_states(self.states.splice(state_index + 1));
var last_state = _.last(self.states);
self.inner_widget = last_state.widget;
// AAB: preceeding two lines probably equal to
// self.inner_widget = state;
this.clear_states(this.states.splice(state_index + 1));
this.inner_widget = state.widget;
return def.done(function () {
if (self.inner_widget.do_show) {
self.inner_widget.do_show();
@@ -269,7 +265,7 @@ var ActionManager = Widget.extend({
// this action has been explicitly marked as not pushable
return;
}
state.title = this.get_title();
state.title = this.get_title();
if(this.inner_action.type == 'ir.actions.act_window') {
state.model = this.inner_action.res_model;
}
@@ -307,7 +303,7 @@ var ActionManager = Widget.extend({
var self = this,
action_loaded;
if (state.action) {
if (_.isString(state.action) && core.action_regisry.contains(state.action)) {
if (_.isString(state.action) && core.action_registry.contains(state.action)) {
var action_client = {
type: "ir.actions.client",
tag: state.action,
@@ -402,10 +398,9 @@ var ActionManager = Widget.extend({
action_menu_id: null,
additional_context: {},
});
if (action === false) {
action = { type: 'ir.actions.act_window_close' };
} else if (_.isString(action) && core.action_regisry.contains(action)) {
} else if (_.isString(action) && core.action_registry.contains(action)) {
var action_client = { type: "ir.actions.client", tag: action, params: {} };
return this.do_action(action_client, options);
} else if (_.isNumber(action) || _.isString(action)) {
@@ -476,6 +471,8 @@ var ActionManager = Widget.extend({
search_disable_custom_filters: action.context && action.context.search_disable_custom_filters
});
}
// Do not permit popups to communicate with the main control panel
options.cp_bus = !popup && this.main_control_panel.get_bus();
return this[type](action, options);
},
@@ -552,8 +549,8 @@ var ActionManager = Widget.extend({
ir_actions_act_window: function (action, options) {
var self = this;
return this.ir_actions_common({
widget: function () {
return new ViewManager(self, null, null, null, action, self.main_control_panel.get_bus());
widget: function () {
return new ViewManager(self, null, null, null, action, options.cp_bus);
},
action: action,
klass: 'oe_act_window',
@@ -576,7 +573,7 @@ var ActionManager = Widget.extend({
return this.ir_actions_common({
widget: function () {
// AAB: temporary fix: Hide main control panel as client actions do not use it
// Hide main control panel as client actions do not use it
self.main_control_panel.do_hide();
return new ClientWidget(self, action);
},
+124 -97
View File
@@ -24,131 +24,156 @@ var ControlPanel = Widget.extend({
this.bus = new core.Bus();
this.bus.on("setup_search_view", this, this.setup_search_view);
this.bus.on("render_switch_buttons", this, this.render_switch_buttons);
this.bus.on("update", this, this.update);
this.bus.on("update_breadcrumbs", this, this.update_breadcrumbs);
this.bus.on("render_buttons", this, this.render_buttons);
this.bus.on("render_switch_buttons", this, this.render_switch_buttons);
this.state = null;
this.searchview = null;
this.flags = null;
this.title = null; // Needed for Favorites of searchview
},
start: function() {
// Retrieve ControlPanel jQuery nodes
this.$control_panel = this.$('.oe-control-panel-content');
this.$title_col = this.$('.oe-cp-title');
this.$breadcrumbs = this.$('.oe-view-title');
this.$buttons = this.$('.oe-cp-buttons');
this.$switch_buttons = this.$('.oe-cp-switch-buttons');
this.$title_col = this.$control_panel.find('.oe-cp-title');
this.$search_col = this.$control_panel.find('.oe-cp-search-view');
this.$searchview = this.$(".oe-cp-search-view");
this.$searchview = this.$('.oe-cp-search-view');
this.$searchview_buttons = this.$('.oe-search-options');
this.$pager = this.$('.oe-cp-pager');
this.$buttons = this.$('.oe-cp-buttons');
this.$sidebar = this.$('.oe-cp-sidebar');
this.$pager = this.$('.oe-cp-pager');
this.$switch_buttons = this.$('.oe-cp-switch-buttons');
// By default, hide the ControlPanel and remove its contents from the DOM
this.$el.hide();
this.contents = this.$el.contents().detach();
return this._super();
},
/**
* @return {Object} Dictionnary of ControlPanel nodes
* @return {Object} the Bus the ControlPanel is listening on
*/
get_cp_nodes: function() {
return {
$buttons: this.$buttons,
$switch_buttons: this.$switch_buttons,
$sidebar: this.$sidebar,
$pager: this.$pager
};
},
get_bus: function() {
return this.bus;
},
/**
* Sets the state of the controlpanel (in the case of a viewmanager, set_state must be
* called before switch_mode for the controlpanel and the viewmanager to be synchronized)
* @return {Object} the current State of the ControlPanel
*/
set_state: function(state, old_state) {
// Detach control panel elements in which sub-elements are inserted by other widgets
var old_content = {
'$buttons': this.$buttons.contents().detach(),
'$switch_buttons': this.$switch_buttons.contents().detach(),
'$pager': this.$pager.contents().detach(),
'$sidebar': this.$sidebar.contents().detach(),
'searchview': this.searchview, // AAB: do better with searchview
'$searchview': this.$searchview.contents().detach(),
'$searchview_buttons': this.$searchview_buttons.contents().detach(),
};
if (old_state) {
// Store them to re-attach them if we come back to that state (e.g. using breadcrumbs)
old_state.set_cp_content(old_content);
}
this.state = state;
this.flags = state.get_flags();
this.flags = state.widget.flags;
this.title = state.widget.title; // needed for Favorites of searchview
// Hide the ControlPanel in headless mode
this.$el.toggle(!this.flags.headless);
var content = state.get_cp_content();
if (content) {
// This state has already been rendered once
content.$buttons.appendTo(this.$buttons);
content.$switch_buttons.appendTo(this.$switch_buttons);
content.$pager.appendTo(this.$pager);
content.$sidebar.appendTo(this.$sidebar);
this.searchview = content.searchview;
content.$searchview.appendTo(this.$searchview);
content.$searchview_buttons.appendTo(this.$searchview_buttons);
}
},
get_state: function() {
return this.state;
},
render_buttons: function(views) {
/**
* Sets the state of the controlpanel (in the case of a viewmanager, set_state must be
* called before switch_mode for the controlpanel and the viewmanager to be synchronized)
* @param {Object} [state] the new state
*/
set_state: function(state) {
this.state = state;
// Set the new state searchview
this.searchview = state.get_searchview();
},
/**
* Returns the content of the ControlPanel without detaching anything from it
* @return {Object} dictionnary containing the detached jQuery elements
*/
get_content: function() {
return {
'$buttons': this.$buttons.contents(),
'$switch_buttons': this.$switch_buttons.contents(),
'$pager': this.$pager.contents(),
'$sidebar': this.$sidebar.contents(),
'$searchview': this.$searchview.contents(),
'$searchview_buttons': this.$searchview_buttons.contents(),
};
},
/**
* Hides (or shows) the ControlPanel in headless (resp. non-headless) mode
* Also detaches or attaches its contents to clean the DOM
*/
toggle_visibility: function(hidden) {
this.$el.toggle(!hidden);
if (hidden && !this.contents) {
this.contents = this.$el.contents().detach();
} else if (this.contents) {
this.contents.appendTo(this.$el);
this.contents = null;
}
},
/**
* Detaches the content of the ControlPanel
* @return {Object} dictionnary containing the detached jQuery elements
*/
_detach_content: function() {
var cp_content = this.get_content();
_.each(cp_content, function($content) {
$content.detach();
});
return cp_content;
},
/**
* Attaches content to the ControlPanel
* @param {Object} [content] dictionnary of jQuery elements to attach, whose keys
* are jQuery nodes identifiers
*/
_attach_content: function(content) {
var self = this;
var buttons_divs = QWeb.render('ControlPanel.buttons', {views: views});
$(buttons_divs).appendTo(this.$buttons);
// Show each div as views will put their own buttons inside it and show/hide them
_.each(views, function(view) {
self.$('.oe-' + view.type + '-buttons').show();
_.each(content, function($nodeset, $element) {
if ($nodeset && self[$element]) {
$nodeset.appendTo(self[$element]);
}
});
},
render_switch_buttons: function(views) {
/**
* Updates the content and display of the ControlPanel
* @param {Object} [status.active_view] the current active view
* @param {Array} [status.breadcrumbs] the breadcrumbs to display
* @param {Object} [status.cp_content] dictionnary containing the new ControlPanel jQuery elements
* @param {Boolean} [status.hidden] true if the ControlPanel should be hidden
* @param {openerp.web.SearchView} [status.searchview] the searchview widget
* @param {Boolean} [status.search_view_hidden] true if the searchview is hidden, false otherwise
*/
update: function(status) {
this.toggle_visibility(status.hidden);
if (!status.hidden) {
// Don't update the ControlPanel in headless mode as the views have
// inserted themselves the buttons where they want, so inserting them
// again in the ControlPanel will removed them from there they should be
this._detach_content();
this._attach_content(status.cp_content);
if (status.active_view) this.update_switch_buttons(status.active_view);
if (status.searchview) this.update_search_view(status.searchview, status.search_view_hidden);
if (status.breadcrumbs) this.update_breadcrumbs(status.breadcrumbs);
}
},
/**
* Renders the switch buttons and call set_switch_buttons on the src
* Does not append the switch buttons to the DOM
* @param {Object} [src] the source requesting the switch_buttons
* @param {Array} [views] the array of views
*/
render_switch_buttons: function(src, views) {
if (views.length > 1) {
var self = this;
var switch_buttons = QWeb.render('ControlPanel.switch-buttons', {views: views});
$(switch_buttons).appendTo(this.$switch_buttons);
// Render switch buttons but do not append them to the DOM as this will
// be done later, simultaneously to all other ControlPanel elements
var $switch_buttons = $(QWeb.render('ControlPanel.switch-buttons', {views: views}));
// Create tooltips
// Create bootstrap tooltips
_.each(views, function(view) {
self.$('.oe-cp-switch-' + view.type).tooltip();
});
}
// Set the buttons jQuery elements in the Widget which made the request
src.set_switch_buttons($switch_buttons);
},
/**
/**
* Triggers an event when switch-buttons are clicked on
*/
on_switch_buttons_click: function(event) {
var view_type = $(event.target).data('view-type');
this.trigger('switch_view', view_type);
},
/**
* Updates its status according to the active_view
* @param {Object} [active_view] the current active view
* @param {Boolean} [search_view_hidden] true if the searchview is hidden, false otherwise
* @param {Array} [breadcrumbs] the breadcrumbs to display
*/
update: function(active_view, search_view_hidden, breadcrumbs) {
this.update_switch_buttons(active_view);
this.update_search_view(search_view_hidden);
this.update_breadcrumbs(breadcrumbs);
},
/**
* Removes active class on all switch-buttons and adds it to the one of the active view
* @param {Object} [active_view] the active_view
@@ -196,7 +221,6 @@ var ControlPanel = Widget.extend({
* @param {Object} [flags] a dictionnary of Booleans
*/
setup_search_view: function(src, action, dataset, flags) {
var self = this;
var view_id = (action && action.search_view_id && action.search_view_id[0]) || false;
var search_defaults = {};
@@ -212,26 +236,29 @@ var ControlPanel = Widget.extend({
var options = {
hidden: flags.search_view === false,
disable_custom_filters: flags.search_disable_custom_filters,
$buttons: this.$searchview_buttons,
$buttons: $("<div>"),
action: action,
};
// Instantiate the SearchView and append it to the DOM
this.searchview = new SearchView(this, dataset, view_id, search_defaults, options);
var search_view_loaded = this.searchview.appendTo(this.$searchview);
// Sets the SearchView in the widget which made the request
src.set_search_view(self.searchview, search_view_loaded);
// Instantiate the SearchView, but do not append it nor its buttons to the DOM as this will
// be done later, simultaneously to all other ControlPanel elements
var searchview = new SearchView(this, dataset, view_id, search_defaults, options);
searchview.appendTo($("<div>")).then(function() {
// Set the SearchView in the widget which made the request
src.set_search_view(searchview);
});
},
/**
* Updates the SearchView's visibility and extend the breadcrumbs area if the SearchView is not visible
* @param {openerp.web.SearchView} [searchview] the searchview Widget
* @param {Boolean} [is_hidden] visibility of the searchview
**/
update_search_view: function(is_hidden) {
if (this.searchview) {
this.searchview.toggle_visibility(!is_hidden);
this.$title_col.toggleClass('col-md-6', !is_hidden).toggleClass('col-md-12', is_hidden);
this.$search_col.toggle(!is_hidden);
}
update_search_view: function(searchview, is_hidden) {
// Set the $buttons div (in the DOM) of the searchview as the $buttons
// have been appended to a jQuery node not in the DOM at SearchView initialization
searchview.$buttons = this.$searchview_buttons;
searchview.toggle_visibility(!is_hidden);
this.$title_col.toggleClass('col-md-6', !is_hidden).toggleClass('col-md-12', is_hidden);
},
});
+29 -8
View File
@@ -18,7 +18,6 @@ var View = Widget.extend({
this.ViewManager = parent;
this.dataset = dataset;
this.view_id = view_id;
this.$buttons = options && options.$buttons;
this.set_default_options(options);
},
start: function () {
@@ -158,9 +157,6 @@ var View = Widget.extend({
},
do_show: function () {
var self = this;
if (this.$buttons) {
this.$buttons.show();
}
this.$el.show();
setTimeout(function () {
self.$el.parent().addClass('in');
@@ -169,15 +165,13 @@ var View = Widget.extend({
core.bus.trigger('view_shown', this);
},
do_hide: function () {
if (this.$buttons) {
this.$buttons.hide();
}
this.$el.parent().removeClass('in');
this.$el.hide();
},
is_active: function () {
return this.ViewManager.active_view.controller === this;
}, /**
},
/**
* Wraps fn to only call it if the current view is the active one. If the
* current view is not active, doesn't call fn.
*
@@ -201,6 +195,33 @@ var View = Widget.extend({
},
do_load_state: function (state, warm) {
},
/**
* This function should render the buttons of the view, set this.$buttons to
* the produced jQuery element and define some listeners on it.
* This function should be called after start().
* @param {jQuery} [$node] a jQuery node where the rendered buttons should be inserted
* $node may be undefined, in which case the View can insert the buttons somewhere else
*/
render_buttons: function($node) {
},
/**
* This function should instantiate and render the sidebar of the view, set this.sidebar to
* the instantiated Sidebar Widget and possibly add custom items in it.
* This function should be called after start().
* @param {jQuery} [$node] a jQuery node where the sidebar should be inserted
* $node may be undefined, in which case the View can insert the sidebar somewhere else
*/
render_sidebar: function($node) {
},
/**
* This function should render the pager of the view, set this.$pager to
* the produced jQuery element and define some listeners on it.
* This function should be called after start().
* @param {jQuery} [$node] a jQuery node where the pager should be inserted
* $node may be undefined, in which case the View can insert the pager somewhere else
*/
render_pager: function($node) {
},
/**
* Switches to a specific view type
+75 -39
View File
@@ -95,49 +95,46 @@ var ViewManager = Widget.extend({
this.$el.addClass("oe_view_manager_" + ((this.action && this.action.target) || 'current'));
if (this.cp_bus) {
// Tell the ControlPanel to setup its search view
this.search_view_loaded = $.Deferred();
this.cp_bus.trigger('setup_search_view', this, this.action, this.dataset, this.flags);
$.when(this.search_view_loaded).then(function() {
self.searchview.on('search_data', self, self.search);
});
// Tell the ControlPanel to render and append the (switch-)buttons to the DOM
this.cp_bus.trigger('render_buttons', this.views);
this.cp_bus.trigger('render_switch_buttons', this.view_order);
this.control_elements = {};
if (this.flags.search_view) {
// Ask the ControlPanel to setup the search view
this.search_view_loaded = $.Deferred();
this.cp_bus.trigger('setup_search_view', this, this.action, this.dataset, this.flags);
$.when(this.search_view_loaded).then(function() {
self.searchview.on('search_data', self, self.search);
});
}
if (this.flags.views_switcher) {
// Ask the ControlPanel to render the switch-buttons
self.cp_bus.trigger('render_switch_buttons', self, self.view_order);
}
}
_.each(this.views, function (view) {
// Expose buttons, sidebar and pager elements to the views so that they can insert stuff in them
view.options = _.extend(view.options, {
$buttons : self.$ext_buttons ? self.$ext_buttons.find('.oe-' + view.type + '-buttons') : undefined,
$sidebar : self.$ext_sidebar,
$pager : self.$ext_pager,
}, self.flags, self.flags[view.type], view.options);
});
// Switch to the default_view to load it
this.main_view_loaded = this.switch_mode(default_view, null, default_options);
return $.when(self.main_view_loaded, this.search_view_loaded);
},
/**
* Sets the external nodes in which the ViewManager and its views should insert elements
* @param {Object} [nodes] a dictionnary of jQuery nodes
*/
set_external_nodes: function(nodes) {
this.$ext_buttons = nodes.$buttons;
this.$ext_sidebar = nodes.$sidebar;
this.$ext_pager = nodes.$pager;
},
/**
* Executed by the ControlPanel when the searchview requested by this ViewManager is loaded
* @param {Widget} [searchview] the SearchView
* @param {Deferred} [search_view_loaded_def] will be resolved when the SearchView is loaded
*/
set_search_view: function(searchview, search_view_loaded_def) {
set_search_view: function(searchview) {
this.searchview = searchview;
this.search_view_loaded = search_view_loaded_def;
_.extend(this.control_elements, {
$searchview: searchview.$el,
$searchview_buttons: searchview.$buttons.contents(),
});
this.search_view_loaded.resolve();
},
/**
* Executed by the ControlPanel when the switch_buttons requested by this ViewManager are rendered
* @param {jQuery} [$switch_buttons] the rendered switch_buttons
*/
set_switch_buttons: function($switch_buttons) {
_.extend(this.control_elements, {
$switch_buttons: $switch_buttons,
});
},
get_search_view: function() {
return this.searchview;
@@ -147,7 +144,7 @@ var ViewManager = Widget.extend({
*/
search: function(domains, contexts, groupbys) {
var self = this,
controller = this.active_view.controller, // AAB: Correct view must be loaded here
controller = this.active_view.controller, // the correct view must be loaded here
action_context = this.action.context || {},
view_context = controller.get_context();
pyeval.eval_domains_and_contexts({
@@ -205,7 +202,7 @@ var ViewManager = Widget.extend({
}
// Call do_search on the searchview to compute domains, contexts and groupbys
if (this.searchview &&
if (this.search_view_loaded &&
this.flags.auto_search &&
view.controller.searchable !== false) {
this.active_search = $.Deferred();
@@ -220,13 +217,24 @@ var ViewManager = Widget.extend({
},
_display_view: function (view_options) {
var self = this;
var view_controller = this.active_view.controller;
// Show the view
this.active_view.$container.show();
$.when(this.active_view.controller.do_show(view_options)).done(function () {
// Tell the ControlPanel to update its elemnts
$.when(view_controller.do_show(view_options)).done(function () {
if (self.cp_bus) {
var search_view_hidden = self.active_view.controller.searchable === false;
var breadcrumbs = self.action_manager.get_breadcrumbs();
self.cp_bus.trigger("update", self.active_view, search_view_hidden, breadcrumbs);
// Render the control elements of the active view (buttons, sidebar, pager)
var view_elements = self.render_view_control_elements();
var status = {
active_view: self.active_view,
breadcrumbs: self.action_manager && self.action_manager.get_breadcrumbs(),
cp_content: _.extend({}, self.control_elements, view_elements),
hidden: self.flags.headless,
searchview: self.searchview,
search_view_hidden: view_controller.searchable === false,
};
// Tell the ControlPanel to update its elements
self.cp_bus.trigger('update', status);
}
});
},
@@ -255,7 +263,7 @@ var ViewManager = Widget.extend({
if (self.action_manager) self.action_manager.trigger('history_back');
});
controller.on("change:title", this, function() {
if (self.cp_bus) {
if (self.cp_bus && self.action_manager) {
var breadcrumbs = self.action_manager.get_breadcrumbs();
self.cp_bus.trigger("update_breadcrumbs", breadcrumbs);
}
@@ -273,6 +281,34 @@ var ViewManager = Widget.extend({
this.view_stack.splice(index);
return this.switch_mode(view_type);
},
/**
* Renders the control elements (buttons, sidebar, pager) of the current view
* This must be done when active_search is resolved (for KanbanViews)
* Fills this.active_view.control_elements dictionnary with the rendered elements
*/
render_view_control_elements: function() {
if (!this.active_view.control_elements) {
var view_controller = this.active_view.controller;
var elements = {};
if (!this.flags.headless) {
elements = {
$buttons: !this.flags.footer_to_buttons ? $("<div>") : undefined,
$sidebar: $("<div>"),
$pager: $("<div>"),
};
}
view_controller.render_buttons(elements.$buttons);
view_controller.render_sidebar(elements.$sidebar);
view_controller.render_pager(elements.$pager);
// Remove the unnecessary outer div
elements = _.mapObject(elements, function($node) {
return $node && $node.contents();
});
// Store the rendered elements in the active_view to allow restoring them later
this.active_view.control_elements = elements;
}
return this.active_view.control_elements;
},
/**
* @returns {Number|Boolean} the view id of the given type, false if not found
*/
+136 -94
View File
@@ -81,10 +81,11 @@ var FormView = View.extend(common.FieldManagerMixin, {
self.set({actual_mode: self.options.initial_mode});
this.has_been_loaded.done(function() {
self._build_onchange_specs();
self.on("change:actual_mode", self, self.check_actual_mode);
self.check_actual_mode();
self.on("change:actual_mode", self, self.init_pager);
self.init_pager();
self.on("change:actual_mode", self, self.check_actual_mode);
self.on("change:actual_mode", self, self.toggle_buttons);
self.on("change:actual_mode", self, self.toggle_sidebar);
self.on("change:actual_mode", self, self.do_update_pager);
});
self.on("load_record", self, self.load_record);
core.bus.on('clear_uncommitted_changes', this, function(e) {
@@ -128,43 +129,12 @@ var FormView = View.extend(common.FieldManagerMixin, {
self.__clicked_inside = true;
});
this.$buttons = $(QWeb.render("FormView.buttons", {'widget':self}));
if (this.options.$buttons) {
this.$buttons.appendTo(this.options.$buttons);
} else {
this.$el.find('.oe_form_buttons').replaceWith(this.$buttons);
}
this.$buttons.on('click', '.oe_form_button_create',
this.guard_active(this.on_button_create));
this.$buttons.on('click', '.oe_form_button_edit',
this.guard_active(this.on_button_edit));
this.$buttons.on('click', '.oe_form_button_save',
this.guard_active(this.on_button_save));
this.$buttons.on('click', '.oe_form_button_cancel',
this.guard_active(this.on_button_cancel));
if (this.options.footer_to_buttons) {
this.$el.find('footer').appendTo(this.$buttons);
}
this.$sidebar = this.options.$sidebar || this.$el.find('.oe_form_sidebar');
if (!this.sidebar && this.options.$sidebar) {
this.sidebar = new Sidebar(this);
this.sidebar.appendTo(this.$sidebar);
if (this.fields_view.toolbar) {
this.sidebar.add_toolbar(this.fields_view.toolbar);
}
this.sidebar.add_items('other', _.compact([
self.is_action_enabled('delete') && { label: _t('Delete'), callback: self.on_button_delete },
self.is_action_enabled('create') && { label: _t('Duplicate'), callback: self.on_button_duplicate }
]));
}
this.has_been_loaded.resolve();
// Add bounce effect on button 'Edit' when click on readonly page view.
this.$el.find(".oe_form_group_row,.oe_form_field,label,h1,.oe_title,.oe_notebook_page, .oe_list_content").on('click', function (e) {
if(self.get("actual_mode") == "view") {
var $button = self.options.$buttons.find(".oe_form_button_edit");
var $button = self.$buttons.find(".oe_form_button_edit");
$button.openerpBounce();
e.stopPropagation();
core.bus.trigger('click', e);
@@ -181,6 +151,121 @@ var FormView = View.extend(common.FieldManagerMixin, {
this.trigger('form_view_loaded', data);
return $.when();
},
/**
* Render the buttons according to the FormView.buttons template and add listeners on it.
* Set this.$buttons with the produced jQuery element
* @param {jQuery} [$node] a jQuery node where the rendered buttons should be inserted
* $node may be undefined, in which case the FormView inserts them into this.options.$buttons
* or into a div of its template
*/
render_buttons: function($node) {
this.$buttons = $(QWeb.render("FormView.buttons", {'widget': this}));
// Show or hide the buttons according to the view mode
this.toggle_buttons();
this.$buttons.on('click', '.oe_form_button_create',
this.guard_active(this.on_button_create));
this.$buttons.on('click', '.oe_form_button_edit',
this.guard_active(this.on_button_edit));
this.$buttons.on('click', '.oe_form_button_save',
this.guard_active(this.on_button_save));
this.$buttons.on('click', '.oe_form_button_cancel',
this.guard_active(this.on_button_cancel));
if (this.options.footer_to_buttons) {
this.$el.find('footer').appendTo(this.$buttons);
}
$node = $node || this.options.$buttons;
if ($node) {
this.$buttons.appendTo($node);
} else {
this.$('.oe_form_buttons').replaceWith(this.$buttons);
}
},
/**
* Instantiate and render the sidebar if a sidebar is requested
* Sets this.sidebar
* @param {jQuery} [$node] a jQuery node where the sidebar should be inserted
* $node may be undefined, in which case the FormView inserts the sidebar in a
* div of its template
**/
render_sidebar: function($node) {
if (!this.sidebar && this.options.sidebar) {
this.sidebar = new Sidebar(this);
if (this.fields_view.toolbar) {
this.sidebar.add_toolbar(this.fields_view.toolbar);
}
this.sidebar.add_items('other', _.compact([
this.is_action_enabled('delete') && { label: _t('Delete'), callback: this.on_button_delete },
this.is_action_enabled('create') && { label: _t('Duplicate'), callback: this.on_button_duplicate }
]));
$node = $node || this.$('.oe_form_sidebar');
this.sidebar.appendTo($node);
// Show or hide the sidebar according to the view mode
this.toggle_sidebar();
}
},
/**
* Render the pager according to the FormView.pager template and add listeners on it.
* Set this.$pager with the produced jQuery element
* @param {jQuery} [$node] a jQuery node where the rendered pager should be inserted
* $node may be undefined, in which case the FormView inserts the pager into this.options.$pager
* or into a div of its template
*/
render_pager: function($node) {
if (this.options.pager) {
var self = this;
if (this.$pager) {
this.$pager.remove();
}
if (this.get("actual_mode") === "create") {
return;
}
this.$pager = $(QWeb.render("FormView.pager", {'widget': self}));
this.$pager.on('click','a[data-pager-action]',function() {
var $el = $(this);
if ($el.attr("disabled")) {
return;
}
var action = $el.data('pager-action');
var def = $.when(self.execute_pager_action(action));
$el.attr("disabled");
def.always(function() {
$el.removeAttr("disabled");
});
});
this.do_update_pager();
$node = $node || this.options.$pager;
if ($node) {
this.$pager.appendTo($node);
} else {
this.$('.oe_form_pager').replaceWith(this.$pager);
}
}
},
/**
* Show or hide the buttons according to the actual_mode
*/
toggle_buttons: function() {
var view_mode = this.get("actual_mode") === "view";
if (this.$buttons) {
this.$buttons.find('.oe_form_buttons_view').toggle(view_mode);
this.$buttons.find('.oe_form_buttons_edit').toggle(!view_mode);
}
},
/**
* Show or hide the sidebar according to the actual_mode
*/
toggle_sidebar: function() {
var view_mode = this.get("actual_mode") === "view";
if (this.sidebar) {
this.sidebar.$el.toggle(view_mode);
}
},
widgetFocused: function() {
// Clear click flag if used to focus a widget
this.__clicked_inside = false;
@@ -223,17 +308,11 @@ var FormView = View.extend(common.FieldManagerMixin, {
do_show: function (options) {
var self = this;
options = options || {};
if (this.sidebar) {
this.sidebar.$el.show();
}
if (this.$buttons) {
this.$buttons.show();
}
this.$el.show().css({
opacity: '0',
filter: 'alpha(opacity = 0)'
});
this.$el.add(this.$buttons).removeClass('oe_form_dirty');
this.$el.removeClass('oe_form_dirty');
var shown = this.has_been_loaded;
if (options.reload !== false) {
@@ -260,15 +339,6 @@ var FormView = View.extend(common.FieldManagerMixin, {
core.bus.trigger('form_view_shown', self);
});
},
do_hide: function () {
if (this.sidebar) {
this.sidebar.$el.hide();
}
if (this.$pager) {
this.$pager.hide();
}
this._super();
},
load_record: function(record) {
var self = this, set_values = [];
if (!record) {
@@ -304,7 +374,7 @@ var FormView = View.extend(common.FieldManagerMixin, {
} else {
self.do_push_state({});
}
self.$el.add(self.$buttons).removeClass('oe_form_dirty');
self.$el.removeClass('oe_form_dirty');
self.autofocus();
});
},
@@ -328,7 +398,7 @@ var FormView = View.extend(common.FieldManagerMixin, {
this.trigger("view_content_has_changed");
},
do_notify_change: function() {
this.$el.add(this.$buttons).addClass('oe_form_dirty');
this.$el.addClass('oe_form_dirty');
},
execute_pager_action: function(action) {
if (this.can_be_discarded()) {
@@ -352,37 +422,16 @@ var FormView = View.extend(common.FieldManagerMixin, {
}
return $.when();
},
init_pager: function() {
var self = this;
if (this.$pager)
this.$pager.remove();
if (this.get("actual_mode") === "create")
return;
this.$pager = $(QWeb.render("FormView.pager", {'widget':self}));
if (this.options.$pager) {
this.$pager.appendTo(this.options.$pager);
} else {
this.$el.find('.oe_form_pager').replaceWith(this.$pager);
}
this.$pager.on('click','a[data-pager-action]',function() {
var $el = $(this);
if ($el.attr("disabled"))
return;
var action = $el.data('pager-action');
var def = $.when(self.execute_pager_action(action));
$el.attr("disabled");
def.always(function() {
$el.removeAttr("disabled");
});
});
this.do_update_pager();
},
do_update_pager: function(hide_index) {
this.$pager.toggle(this.dataset.ids.length > 1);
if (hide_index) {
$(".oe_form_pager_state", this.$pager).html("");
} else {
$(".oe_form_pager_state", this.$pager).html(_.str.sprintf(_t("%d / %d"), this.dataset.index + 1, this.dataset.ids.length));
if (this.$pager) {
// Hide the pager in create mode or when there is only one record
var pager_visible = (this.get("actual_mode") !== "create") && (this.dataset.ids.length > 1);
this.$pager.toggle(pager_visible);
if (hide_index === true) {
this.$pager.find(".oe_form_pager_state").html("");
} else {
this.$pager.find(".oe_form_pager_state").html(_.str.sprintf(_t("%d / %d"), this.dataset.index + 1, this.dataset.ids.length));
}
}
},
@@ -512,7 +561,7 @@ var FormView = View.extend(common.FieldManagerMixin, {
return this.onchanges_mutex.def;
} catch(e) {
console.error(e);
crash_manager.show_message(e);
crashmanager.show_message(e);
return $.Deferred().reject();
}
},
@@ -542,7 +591,7 @@ var FormView = View.extend(common.FieldManagerMixin, {
return $.Deferred().resolve();
} catch(e) {
console.error(e);
crash_manager.show_message(e);
crashmanager.show_message(e);
return $.Deferred().reject();
}
},
@@ -642,14 +691,8 @@ var FormView = View.extend(common.FieldManagerMixin, {
var self = this;
if(this.get("actual_mode") === "view") {
self.$el.removeClass('oe_form_editable').addClass('oe_form_readonly');
self.$buttons.find('.oe_form_buttons_edit').hide();
self.$buttons.find('.oe_form_buttons_view').show();
self.$sidebar.show();
} else {
self.$el.removeClass('oe_form_readonly').addClass('oe_form_editable');
self.$buttons.find('.oe_form_buttons_edit').show();
self.$buttons.find('.oe_form_buttons_view').hide();
self.$sidebar.hide();
this.autofocus();
}
},
@@ -979,9 +1022,8 @@ var FormView = View.extend(common.FieldManagerMixin, {
open_defaults_dialog: function () {
var self = this;
var display = function (field, value) {
var FieldSelection = core.form_widget_registry.get('selection'),
FieldMany2One = core.form_widget_registry.get('many2one');
var FieldSelection = core.form_widget_registry.get('selection');
var FieldMany2One = core.form_widget_registry.get('many2one');
if (!value) { return value; }
if (field instanceof FieldSelection) {
return _(field.get('values')).find(function (option) {
+13 -5
View File
@@ -31,17 +31,25 @@ var GraphView = View.extend({
var load_fields = this.model.call('fields_get', [])
.then(this.prepare_fields.bind(this));
return $.when(this._super(), load_fields).then(this.render_buttons.bind(this));
return $.when(this._super(), load_fields);
},
render_buttons: function () {
// Render buttons only in non-headless mode as buttons are in the header
if (!this.options.headless) {
/**
* Render the buttons according to the GraphView.buttons and
* add listeners on it.
* Set this.$buttons with the produced jQuery element
* @param {jQuery} [$node] a jQuery node where the rendered buttons should be inserted
* $node may be undefined, in which case the GraphView does nothing
*/
render_buttons: function ($node) {
if ($node) {
var context = {measures: _.pairs(_.omit(this.measures, '__count__'))};
this.$buttons.html(QWeb.render('GraphView.buttons', context));
this.$buttons = $(QWeb.render('GraphView.buttons', context));
this.$measure_list = this.$buttons.find('.oe-measure-list');
this.update_measure();
this.$buttons.find('button').tooltip();
this.$buttons.click(this.on_button_click.bind(this));
this.$buttons.appendTo($node);
}
},
update_measure: function () {
+111 -81
View File
@@ -239,7 +239,6 @@ var ListView = View.extend( /** @lends instance.web.ListView# */ {
* * Sets up showing/hiding the top-level [Delete] button based on records being selected or not
* * Sets up event handlers for action buttons and per-row deletion button
* * Hooks global callback for clicking on a row
* * Sets up its sidebar, if any
*
* @param {Object} data wrapped fields_view_get result
* @param {Object} data.fields_view fields_view_get result (processed)
@@ -288,28 +287,81 @@ var ListView = View.extend( /** @lends instance.web.ListView# */ {
'selected', [selection.ids, selection.records]);
});
// Add button
if (!this.$buttons) {
this.$buttons = $(QWeb.render("ListView.buttons", {'widget':self}));
if (this.options.$buttons) {
this.$buttons.appendTo(this.options.$buttons);
} else {
this.$el.find('.oe_list_buttons').replaceWith(this.$buttons);
}
this.$buttons.find('.oe_list_add')
.click(this.proxy('do_add_record'))
.prop('disabled', this.grouped);
//Sort
var default_order = this.fields_view.arch.attrs.default_order,
unsorted = !this.dataset._sort.length;
if (unsorted && default_order && !this.grouped) {
this.dataset.set_sort(default_order.split(','));
}
// Pager
if (!this.$pager) {
this.$pager = $(QWeb.render("ListView.pager", {'widget':self})).hide();
if (this.options.$pager) {
this.$pager.appendTo(this.options.$pager);
} else {
this.$el.find('.oe_list_pager').replaceWith(this.$pager);
if(this.dataset._sort.length){
if(this.dataset._sort[0].indexOf('-') == -1){
this.$el.find('th[data-id=' + this.dataset._sort[0] + ']').addClass("sortdown");
}else {
this.$el.find('th[data-id=' + this.dataset._sort[0].split('-')[1] + ']').addClass("sortup");
}
}
this.trigger('list_view_loaded', data, this.grouped);
},
/**
* Render the buttons according to the ListView.buttons template and
* add listeners on it.
* Set this.$buttons with the produced jQuery element
* @param {jQuery} [$node] a jQuery node where the rendered buttons should be inserted
* $node may be undefined, in which case the ListView inserts them into this.options.$buttons
* or into a div of its template
*/
render_buttons: function($node) {
if (!this.$buttons) {
this.$buttons = $(QWeb.render("ListView.buttons", {'widget': this}));
this.$buttons.find('.oe_list_add').click(this.proxy('do_add_record'));
$node = $node || this.options.$buttons;
if ($node) {
this.$buttons.appendTo($node);
} else {
this.$('.oe_list_buttons').replaceWith(this.$buttons);
}
}
},
/**
* Instantiate and render the sidebar.
* Sets this.sidebar
* @param {jQuery} [$node] a jQuery node where the sidebar should be inserted
* $node may be undefined, in which case the ListView inserts the sidebar in
* this.options.$sidebar or in a div of its template
**/
render_sidebar: function($node) {
if (!this.sidebar && this.options.sidebar) {
this.sidebar = new Sidebar(this);
if (this.fields_view.toolbar) {
this.sidebar.add_toolbar(this.fields_view.toolbar);
}
this.sidebar.add_items('other', _.compact([
{ label: _t("Export"), callback: this.on_sidebar_export },
this.is_action_enabled('delete') && { label: _t('Delete'), callback: this.do_delete_selected }
]));
$node = $node || this.options.$sidebar || this.$('.oe_list_sidebar');
this.sidebar.appendTo($node);
// Hide the sidebar by default (it will be shown as soon as a record is selected)
this.sidebar.do_hide();
}
},
/**
* Render the pager according to the ListView.pager template and add listeners on it.
* Set this.$pager with the produced jQuery element
* @param {jQuery} [$node] a jQuery node where the rendered pager should be inserted
* $node may be undefined, in which case the ListView inserts the pager into this.options.$pager
* or into a div of its template
*/
render_pager: function($node) {
if (!this.$pager && this.options.pager) {
var self = this;
this.$pager = $(QWeb.render("ListView.pager", {'widget': self}));
this.$pager
.on('click', 'a[data-pager-action]', function () {
var $this = $(this);
@@ -357,34 +409,15 @@ var ListView = View.extend( /** @lends instance.web.ListView# */ {
})
.val(self._limit || 'NaN');
});
}
this.configure_pager(this.dataset);
// Sidebar
if (!this.sidebar && this.options.$sidebar) {
this.sidebar = new Sidebar(this);
this.sidebar.appendTo(this.options.$sidebar);
this.sidebar.add_items('other', _.compact([
{ label: _t("Export"), callback: this.on_sidebar_export },
self.is_action_enabled('delete') && { label: _t('Delete'), callback: this.do_delete_selected }
]));
this.sidebar.add_toolbar(this.fields_view.toolbar);
this.sidebar.$el.hide();
}
//Sort
var default_order = this.fields_view.arch.attrs.default_order,
unsorted = !this.dataset._sort.length;
if (unsorted && default_order && !this.grouped) {
this.dataset.set_sort(default_order.split(','));
}
if(this.dataset._sort.length){
if(this.dataset._sort[0].indexOf('-') == -1){
this.$el.find('th[data-id=' + this.dataset._sort[0] + ']').addClass("sortdown");
}else {
this.$el.find('th[data-id=' + this.dataset._sort[0].split('-')[1] + ']').addClass("sortup");
$node = $node || this.options.$pager;
if ($node) {
this.$pager.appendTo($node);
} else {
this.$('.oe_list_pager').replaceWith(this.$pager);
}
}
this.trigger('list_view_loaded', data, this.grouped);
},
sort_by_column: function (e) {
e.stopPropagation();
@@ -418,25 +451,32 @@ var ListView = View.extend( /** @lends instance.web.ListView# */ {
if (dataset._length) {
this.dataset._length = dataset._length;
}
if (this.$pager) {
if (this.grouped) {
// page count is irrelevant on grouped page, replace by limit
this.$pager.find('.oe-pager-buttons').hide();
this.$pager.find('.oe_list_pager_state').text(this.limit() ? this.limit() : '∞');
} else {
var total = dataset.size();
var limit = this.limit() || total;
this.$pager.find('.oe-pager-buttons').toggle(total > limit);
this.$pager.find('.oe_pager_value').toggle(total !== 0);
var spager = '-';
if (total) {
var range_start = this.page * limit + 1;
var range_stop = range_start - 1 + limit;
if (this.records.length) {
range_stop = range_start - 1 + this.records.length;
}
if (range_stop > total) {
range_stop = total;
}
spager = _.str.sprintf(_t("%d-%d of %d"), range_start, range_stop, total);
}
var total = dataset.size();
var limit = this.limit() || total;
this.$pager.find('.oe-pager-buttons').toggle(total > limit);
this.$pager.find('.oe_pager_value').toggle(total !== 0);
var spager = '-';
if (total) {
var range_start = this.page * limit + 1;
var range_stop = range_start - 1 + limit;
if (this.records.length) {
range_stop = range_start - 1 + this.records.length;
this.$pager.find('.oe_list_pager_state').text(spager);
}
if (range_stop > total) {
range_stop = total;
}
spager = _.str.sprintf(_t("%d-%d of %d"), range_start, range_stop, total);
}
this.$pager.find('.oe_list_pager_state').text(spager);
},
/**
* Sets up the listview's columns: merges view and fields data, move
@@ -488,18 +528,10 @@ var ListView = View.extend( /** @lends instance.web.ListView# */ {
},
do_show: function () {
this._super();
if (this.$pager) {
this.$pager.show();
}
},
do_hide: function () {
if (this.sidebar) {
this.sidebar.$el.hide();
// Hide the sidebar by default (will be shown once a record is selected)
this.sidebar.do_hide();
}
if (this.$pager) {
this.$pager.hide();
}
this._super();
},
/**
* Reloads the list view based on the current settings (dataset & al)
@@ -522,7 +554,7 @@ var ListView = View.extend( /** @lends instance.web.ListView# */ {
this.records.reset();
var reloaded = $.Deferred();
reloaded.then(function () {
if (!self.grouped) self.$pager.show();
self.configure_pager(self.dataset);
});
this.$el.find('.oe_list_content').append(
this.groups.render(function () {
@@ -656,7 +688,7 @@ var ListView = View.extend( /** @lends instance.web.ListView# */ {
if (!ids.length) {
this.dataset.index = 0;
if (this.sidebar) {
this.sidebar.$el.hide();
this.sidebar.do_hide();
}
this.compute_aggregates();
return;
@@ -664,8 +696,7 @@ var ListView = View.extend( /** @lends instance.web.ListView# */ {
this.dataset.index = _(this.dataset.ids).indexOf(ids[0]);
if (this.sidebar) {
this.options.$sidebar.show();
this.sidebar.$el.show();
this.sidebar.do_show();
}
this.compute_aggregates(_(records).map(function (record) {
@@ -915,10 +946,12 @@ var ListView = View.extend( /** @lends instance.web.ListView# */ {
this.$el.prepend(
$('<div class="oe_view_nocontent">').html(this.options.action.help)
);
var $buttons = this.$buttons;
this.$el.find('.oe_view_nocontent').click(function() {
$buttons.width($buttons.width() + 1).openerpBounce();
});
if (this.$buttons) {
var $buttons = this.$buttons;
this.$el.find('.oe_view_nocontent').click(function() {
$buttons.width($buttons.width() + 1).openerpBounce();
});
}
}
});
core.view_registry.add('list', ListView);
@@ -1637,9 +1670,6 @@ ListView.Groups = Class.extend( /** @lends instance.web.ListView.Groups# */{
.filter(function (column) { return column.tag === 'field';})
.pluck('name').value(),
function (groups) {
// page count is irrelevant on grouped page, replace by limit
self.view.$pager.find('.oe-pager-buttons').hide();
self.view.$pager.find('.oe_list_pager_state').text(self.view._limit ? self.view._limit : '∞');
$el[0].appendChild(
self.render_groups(groups));
if (post_render) { post_render(); }
@@ -286,15 +286,6 @@ ListView.include(/** @lends instance.web.ListView# */{
if (this.editable()) {
this.$el.addClass('oe_list_editable');
// FIXME: any hook available to ensure this is only done once?
this.$buttons
.off('click', '.oe_list_save')
.on('click', '.oe_list_save', this.proxy('save_edition'))
.off('click', '.oe_list_discard')
.on('click', '.oe_list_discard', function (e) {
e.preventDefault();
self.cancel_edition();
});
var editor_ready = this.editor.prependTo(this.$el)
.done(this.proxy('setup_events'));
@@ -305,6 +296,30 @@ ListView.include(/** @lends instance.web.ListView# */{
return result;
},
/**
* Extend the render_buttons function of ListView by adding event listeners
* in the case of an editable list.
* @return {jQuery} the rendered buttons
*/
render_buttons: function() {
var self = this;
var add_button = false;
if (!this.$buttons) { // Ensures that this is only done once
add_button = true;
}
this._super.apply(this, arguments); // Sets this.$buttons
if (add_button && this.editable()) {
this.$buttons
.off('click', '.oe_list_save')
.on('click', '.oe_list_save', this.proxy('save_edition'))
.off('click', '.oe_list_discard')
.on('click', '.oe_list_discard', function (e) {
e.preventDefault();
self.cancel_edition();
});
}
return this.$buttons;
},
/**
* Builds a new editor object
*
+48 -36
View File
@@ -35,7 +35,6 @@ var PivotView = View.extend({
this._super(parent, dataset, view_id, options);
this.model = new Model(dataset.model, {group_by_no_leaf: true});
this.$sidebar = options.$sidebar;
this.fields = {};
this.measures = {};
this.groupable_fields = {};
@@ -62,46 +61,68 @@ var PivotView = View.extend({
this.last_header_selected = null;
this.sorted_column = {};
},
start: function () {
willStart: function () {
var self = this;
return session.rpc('/web/pivot/check_xlwt').then(function(result) {
self.xlwt_installed = result;
});
},
start: function () {
this.$table_container = this.$('.o-pivot-table');
var load_fields = this.model.call('fields_get', [])
.then(this.prepare_fields.bind(this));
if (this.$sidebar) {
this.rpc('/web/pivot/check_xlwt').then(function (result) {
if (result) {
self.sidebar = new Sidebar(self);
self.sidebar.appendTo(self.$sidebar);
self.sidebar.add_items('other', [{
label: _t("Download xls"),
callback: self.download_table.bind(self)
}]);
}
});
}
return $.when(this._super(), load_fields).then(this.render_buttons.bind(this));
return $.when(this._super(), load_fields).then(this.render_field_selection.bind(this));
},
render_buttons: function () {
render_field_selection: function () {
var self = this;
// Render buttons only in non-headless mode as buttons are in the header
if (!this.options.headless) {
var context = {fields: _.chain(this.groupable_fields).pairs().sortBy(function(f){return f[1].string;}).value()};
this.$field_selection = this.$('.o-field-selection');
this.$field_selection.html(QWeb.render('PivotView.FieldSelection', context));
core.bus.on('click', self, function () {
self.$field_selection.find('ul').first().hide();
});
},
/**
* Render the buttons according to the PivotView.buttons template and
* add listeners on it.
* Set this.$buttons with the produced jQuery element
* @param {jQuery} [$node] a jQuery node where the rendered buttons should be inserted
* $node may be undefined, in which case the PivotView does nothing
*/
render_buttons: function ($node) {
if ($node) {
var self = this;
var context = {measures: _.pairs(_.omit(this.measures, '__count__'))};
this.$buttons.html(QWeb.render('PivotView.buttons', context));
this.$buttons = $(QWeb.render('PivotView.buttons', context));
this.$buttons.click(this.on_button_click.bind(this));
this.active_measures.forEach(function (measure) {
self.$buttons.find('li[data-field="' + measure + '"]').addClass('selected');
});
this.$buttons.find('button').tooltip();
this.$buttons.appendTo($node);
}
},
/**
* Instantiate and render the sidebar.
* Sets this.sidebar
* @param {jQuery} [$node] a jQuery node where the sidebar should be inserted
* $node may be undefined, in which case the PivotView does nothing
**/
render_sidebar: function($node) {
if (this.xlwt_installed && $node && this.options.sidebar) {
this.sidebar = new Sidebar(this);
this.sidebar.add_items('other', [{
label: _t("Download xls"),
callback: this.download_table.bind(this),
}]);
this.sidebar.appendTo($node);
}
var another_ctx = {fields: _.chain(this.groupable_fields).pairs().sortBy(function(f){return f[1].string;}).value()};
this.$field_selection = this.$('.o-field-selection');
this.$field_selection.html(QWeb.render('PivotView.FieldSelection', another_ctx));
core.bus.on('click', self, function () {
self.$field_selection.find('ul').first().hide();
});
},
view_loading: function (fvg) {
var self = this;
@@ -172,9 +193,6 @@ var PivotView = View.extend({
},
do_show: function () {
var self = this;
if (this.sidebar) {
this.sidebar.$el.show();
}
this.do_push_state({});
this.data_loaded.done(function () {
self.display_table();
@@ -182,12 +200,6 @@ var PivotView = View.extend({
});
return this._super();
},
do_hide: function () {
if (this.sidebar) {
this.sidebar.$el.hide();
}
this._super();
},
get_context: function () {
return !this.ready ? {} : {
pivot_measures: this.active_measures,
@@ -597,7 +609,7 @@ var PivotView = View.extend({
.addClass(rows[i].expanded ? 'oe-opened' : 'oe-closed');
if (rows[i].indent > 0) $header.attr('title', groupby_labels[rows[i].indent - 1]);
$header.appendTo($row);
for (j = 0; j < length; j++) {
for (j = 0; j < length; j++) {
value = formats.format_value(rows[i].values[j], {type: measure_types[j % nbr_measures]});
$cell = $('<td>')
.data('id', rows[i].id)
@@ -698,7 +710,7 @@ var PivotView = View.extend({
traverse_tree(self.main_col.root, add_cells, header.id, values, col_ids);
if (self.main_col.width > 1) {
aggregates = self.get_value(header.id, self.main_col.root.id);
for (i = 0; i < self.active_measures.length; i++) {
for (i = 0; i < self.active_measures.length; i++) {
values.push(aggregates && aggregates[self.active_measures[i]]);
}
col_ids.push( self.main_col.root.id);
+2 -8
View File
@@ -495,12 +495,6 @@
</div>
</t>
<t t-name="ControlPanel.buttons">
<t t-foreach="views" t-as="view">
<div t-attf-class="oe-#{view}-buttons"/>
</t>
</t>
<t t-name="ControlPanel.switch-buttons">
<t t-foreach="views" t-as="view">
<button type="button" t-attf-class="btn btn-default fa oe-cp-switch-#{view.type}" t-att-data-view-type="view.type" t-att-title="view.label"/>
@@ -575,10 +569,10 @@
</div>
</t>
<span t-name="ViewPager">
<div class="oe_pager_value">
<div class="oe_pager_value" style="display: inline-block">
<t t-raw="0"/>
</div>
<div class="btn-group btn-group-sm oe-pager-buttons">
<div class="btn-group btn-group-sm oe-pager-buttons" style="display: inline-block">
<a class="fa fa-chevron-left btn btn-default oe-pager-button" type="button" data-pager-action="previous"></a>
<a class="fa fa-chevron-right btn btn-default oe-pager-button" type="button" data-pager-action="next"></a>
</div>
@@ -121,19 +121,6 @@ var CalendarView = View.extend({
this.info_fields = [];
/* buttons */
this.$buttons = $(QWeb.render("CalendarView.buttons", {'widget': this}));
if (this.options.$buttons) {
this.$buttons.appendTo(this.options.$buttons);
} else {
this.$el.find('.oe_calendar_buttons').replaceWith(this.$buttons);
}
this.$buttons.on('click', 'button.oe_calendar_button_new', function () {
self.dataset.index = null;
self.do_switch_view('form');
});
if (!attrs.date_start) {
throw new Error(_t("Calendar view has not defined 'date_start' attribute."));
}
@@ -251,6 +238,28 @@ var CalendarView = View.extend({
self.trigger('calendar_view_loaded', self.fields_view);
});
},
/**
* Render the buttons according to the CalendarView.buttons template and
* add listeners on it.
* Set this.$buttons with the produced jQuery element
* @param {jQuery} [$node] a jQuery node where the rendered buttons should be inserted
* $node may be undefined, in which case the ListView inserts them into this.options.$buttons
* or into a div of its template
*/
render_buttons: function($node) {
var self = this;
this.$buttons = $(QWeb.render("CalendarView.buttons", {'widget': this}));
this.$buttons.on('click', 'button.oe_calendar_button_new', function () {
self.dataset.index = null;
self.do_switch_view('form');
});
$node = $node || this.options.$buttons;
if ($node) {
this.$buttons.appendTo($node);
} else {
this.$('.oe_calendar_buttons').replaceWith(this.$buttons);
}
},
get_fc_init_options: function () {
//Documentation here : http://arshaw.com/fullcalendar/docs/
var self = this;
+26 -27
View File
@@ -64,8 +64,6 @@ var DiagramView = View.extend({
self.$el.find('.oe_diagram_header').append(html_label);
});
this.init_pager();
// New Node,Edge
this.$el.find('#new_node.oe_diagram_button_new').click(function(){self.add_node();});
@@ -361,29 +359,29 @@ var DiagramView = View.extend({
});
},
do_hide: function () {
if (this.$pager) {
this.$pager.hide();
}
this._super();
},
init_pager: function() {
/**
* Render the pager according to the DiagramView.pager template and add listeners on it.
* Set this.$pager with the produced jQuery element
* @param {jQuery} [$node] a jQuery node where the rendered pager should be inserted
* $node may be undefined, in which case the DiagramView inserts the pager into this.options.$pager
* or into a div of its template
*/
render_pager: function($node) {
var self = this;
if (this.$pager)
this.$pager.remove();
this.$pager = $(QWeb.render("DiagramView.pager", {'widget':self})).hide();
if (this.options.$pager) {
this.$pager.appendTo(this.options.$pager);
} else {
this.$el.find('.oe_diagram_pager').replaceWith(this.$pager);
}
this.$pager.on('click','a[data-pager-action]',function() {
this.$pager = $(QWeb.render("DiagramView.pager", {'widget': self}));
this.$pager.find('a[data-pager-action]').click(function() {
var action = $(this).data('pager-action');
self.execute_pager_action(action);
});
this.do_update_pager();
this.execute_pager_action('reload');
$node = $node || this.options.$pager;
if ($node) {
this.$pager.appendTo($node);
} else {
this.$('.oe_diagram_pager').replaceWith(this.$pager);
}
},
pager_action_trigger: function(){
@@ -409,15 +407,16 @@ var DiagramView = View.extend({
break;
}
this.trigger('pager_action_executed');
},
do_update_pager: function(hide_index) {
this.$pager.toggle(this.dataset.ids.length > 1);
if (hide_index) {
$(".oe_diagram_pager_state", this.$pager).html("");
} else {
$(".oe_diagram_pager_state", this.$pager).html(_.str.sprintf(_t("%d / %d"), this.dataset.index + 1, this.dataset.ids.length));
if (this.$pager) {
this.$pager.toggle(this.dataset.ids.length > 1);
if (hide_index) {
this.$pager.find(".oe_diagram_pager_state").html("");
} else {
this.$pager.find(".oe_diagram_pager_state").html(_.str.sprintf(_t("%d / %d"), this.dataset.index + 1, this.dataset.ids.length));
}
}
},
@@ -1,9 +1,7 @@
<template>
<t t-name="DiagramView.pager">
<t t-if="widget.options.pager !== false" t-call="ViewPager">
<span class="oe_diagram_pager_state"></span>
</t>
<t t-name="DiagramView.pager" t-if="widget.options.pager !== false" t-call="ViewPager">
<span class="oe_diagram_pager_state"></span>
</t>
<t t-name="DiagramView">
+24 -13
View File
@@ -50,9 +50,6 @@ var KanbanView = View.extend({
this.currently_dragging = {};
this.limit = options.limit || 40;
this.add_group_mutex = new utils.Mutex();
if (!this.options.$buttons || !this.options.$buttons.length) {
this.options.$buttons = false;
}
},
view_loading: function(r) {
return this.load_kanban(r);
@@ -83,15 +80,6 @@ var KanbanView = View.extend({
this.dataset.set_sort(default_order.split(','));
}
this.$el.addClass(this.fields_view.arch.attrs['class']);
this.$buttons = $(QWeb.render("KanbanView.buttons", {'widget': this}));
if (this.options.$buttons) {
this.$buttons.appendTo(this.options.$buttons);
} else {
this.$('.oe_kanban_buttons').replaceWith(this.$buttons);
}
this.$buttons
.on('click', 'button.oe_kanban_button_new', this.do_add_record)
.on('click', '.oe_kanban_add_column', this.do_add_group);
this.$groups = this.$el.find('.oe_kanban_groups tr');
this.fields_keys = _.keys(this.fields_view.fields);
this.add_qweb_template();
@@ -99,6 +87,30 @@ var KanbanView = View.extend({
this.trigger('kanban_view_loaded', data);
return $.when();
},
/**
* Render the buttons according to the KanbanView.buttons template and
* add listeners on it.
* Set this.$buttons with the produced jQuery element
* @param {jQuery} [$node] a jQuery node where the rendered buttons should be inserted
* $node may be undefined, in which case the ListView inserts them into this.options.$buttons
* or into a div of its template
*/
render_buttons: function($node) {
this.$buttons = $(QWeb.render("KanbanView.buttons", {'widget': this}));
this.$buttons
.on('click', 'button.oe_kanban_button_new', this.do_add_record)
.on('click', '.oe_kanban_add_column', this.do_add_group);
// Important: This should be done after do_search is finished so that
// this.grouped_by_m2o is set
this.$buttons.find('.oe_alternative').toggle(this.grouped_by_m2o);
$node = $node || this.options.$buttons;
if ($node) {
this.$buttons.appendTo($node);
} else {
this.$('.oe_kanban_buttons').replaceWith(this.$buttons);
}
},
_is_quick_create_enabled: function() {
if (!this.options.quick_creatable || !this.is_action_enabled('create'))
return false;
@@ -206,7 +218,6 @@ var KanbanView = View.extend({
self.group_by = group_by.length ? group_by[0] : self.fields_view.arch.attrs.default_group_by;
self.group_by_field = self.fields_view.fields[self.group_by] || {};
self.grouped_by_m2o = (self.group_by_field.type === 'many2one');
self.$buttons.find('.oe_alternative').toggle(self.grouped_by_m2o);
self.$el.toggleClass('oe_kanban_grouped_by_m2o', self.grouped_by_m2o);
var grouping_fields = self.group_by ? [self.group_by] : undefined;
if (!_.isEmpty(grouping_fields)) {
@@ -59,7 +59,7 @@ var ViewEditor = Widget.extend({
deletable: false,
views_switcher: false,
action_buttons: false,
search_view: false,
search_view: true,
headless: true,
pager: false,
radio: true,