diff --git a/addons/base_import/static/src/js/import.js b/addons/base_import/static/src/js/import.js index 8843cde167c..991dbee7462 100644 --- a/addons/base_import/static/src/js/import.js +++ b/addons/base_import/static/src/js/import.js @@ -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; } }); diff --git a/addons/board/static/src/js/dashboard.js b/addons/board/static/src/js/dashboard.js index b2c91b23c5e..72c4eee1eca 100644 --- a/addons/board/static/src/js/dashboard.js +++ b/addons/board/static/src/js/dashboard.js @@ -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, diff --git a/addons/google_drive/static/src/js/gdrive.js b/addons/google_drive/static/src/js/gdrive.js index 87de791b480..f7d33a53979 100644 --- a/addons/google_drive/static/src/js/gdrive.js +++ b/addons/google_drive/static/src/js/gdrive.js @@ -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); diff --git a/addons/share/static/src/js/share.js b/addons/share/static/src/js/share.js index 2fec003e9ff..0b8923d60ae 100644 --- a/addons/share/static/src/js/share.js +++ b/addons/share/static/src/js/share.js @@ -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'), diff --git a/addons/web/static/src/css/base.css b/addons/web/static/src/css/base.css index 9ff0c1d103f..740b8b452fe 100644 --- a/addons/web/static/src/css/base.css +++ b/addons/web/static/src/css/base.css @@ -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; diff --git a/addons/web/static/src/css/base.sass b/addons/web/static/src/css/base.sass index d5011ea2abb..665fc6dcdc4 100644 --- a/addons/web/static/src/css/base.sass +++ b/addons/web/static/src/css/base.sass @@ -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 diff --git a/addons/web/static/src/js/action_manager.js b/addons/web/static/src/js/action_manager.js index 5ae35308bfc..b212e328433 100644 --- a/addons/web/static/src/js/action_manager.js +++ b/addons/web/static/src/js/action_manager.js @@ -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); }, diff --git a/addons/web/static/src/js/control_panel.js b/addons/web/static/src/js/control_panel.js index d452c70120e..768f7179511 100644 --- a/addons/web/static/src/js/control_panel.js +++ b/addons/web/static/src/js/control_panel.js @@ -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: $("