diff --git a/addons/account/static/src/js/widgets.js b/addons/account/static/src/js/widgets.js index 054735cb60d1997a8ad0fd154a326509b7579534..852acbc3795e5efab57c8a6ba35e73669bd4472d 100644 --- a/addons/account/static/src/js/widgets.js +++ b/addons/account/static/src/js/widgets.js @@ -539,12 +539,12 @@ var bankStatementReconciliation = Widget.extend({ .call("get_object_reference", ['account', 'action_bank_statement_tree']) .then(function (result) { var action_id = result[1]; - var breadcrumbs = self.action_manager.get_widgets(); - var widget = _.find(breadcrumbs, function(widget){ - return widget.action && widget.action.id === action_id; + var breadcrumbs = self.action_manager.get_states(); + var state = _.find(breadcrumbs, function(state){ + return state.widget.action && state.widget.action.id === action_id; }); - if (widget) { - self.action_manager.select_widget(widget, 0); + if (state) { + self.action_manager.select_state(state, 0); } else { self.action_manager.do_action(action_id, { clear_breadcrumbs: true diff --git a/addons/board/static/src/js/dashboard.js b/addons/board/static/src/js/dashboard.js index fc7e12322e3af2aed302401169ddfd3bed7d8316..b2c91b23c5e3c6dbcbd31d79b12f2bd6d0e38f37 100644 --- a/addons/board/static/src/js/dashboard.js +++ b/addons/board/static/src/js/dashboard.js @@ -334,17 +334,24 @@ FavoriteMenu.include({ prepare_dropdown_menu: function (filters) { var self = this; this._super(filters); - this.$('.favorites-menu').append(QWeb.render('SearchView.addtodashboard')); - var $add_to_dashboard = this.$('.add-to-dashboard'); - this.$add_dashboard_btn = $add_to_dashboard.eq(2).find('button'); - this.$add_dashboard_input = $add_to_dashboard.eq(1).find('input'); - this.$add_dashboard_link = $add_to_dashboard.first(); - var title = this.searchview.getParent().title; - this.$add_dashboard_input.val(title); - this.$add_dashboard_link.click(function () { - self.toggle_dashboard_menu(); + var am = this.findAncestor(function (a) { + return a instanceof ActionManager; }); - this.$add_dashboard_btn.click(this.proxy('add_dashboard')); + if (am && am.get_inner_widget() instanceof ViewManager) { + this.view_manager = am.get_inner_widget(); + this.add_to_dashboard_available = true; + this.$('.favorites-menu').append(QWeb.render('SearchView.addtodashboard')); + var $add_to_dashboard = this.$('.add-to-dashboard'); + this.$add_dashboard_btn = $add_to_dashboard.eq(2).find('button'); + this.$add_dashboard_input = $add_to_dashboard.eq(1).find('input'); + this.$add_dashboard_link = $add_to_dashboard.first(); + var title = this.searchview.get_title(); + this.$add_dashboard_input.val(title); + this.$add_dashboard_link.click(function () { + self.toggle_dashboard_menu(); + }); + this.$add_dashboard_btn.click(this.proxy('add_dashboard')); + } }, toggle_dashboard_menu: function (is_open) { this.$add_dashboard_link @@ -357,29 +364,24 @@ FavoriteMenu.include({ } }, close_menus: function () { - this.toggle_dashboard_menu(false); + if (this.add_to_dashboard_available) { + this.toggle_dashboard_menu(false); + } this._super(); }, add_dashboard: function () { - var self = this, - view_manager = this.findAncestor(function (a) { - return a instanceof ViewManager; - }); - if (!view_manager.action) { - this.do_warn(_t("Can't find dashboard action")); - return; - } - var searchview = view_manager.get_searchview(); - var search_data = searchview.build_search_data(); - var context = new data.CompoundContext(searchview.dataset.get_context() || []); - var domain = new data.CompoundDomain(searchview.dataset.get_domain() || []); + var self = this; + + var search_data = this.searchview.build_search_data(), + context = new data.CompoundContext(this.searchview.dataset.get_context() || []), + domain = new data.CompoundDomain(this.searchview.dataset.get_domain() || []); _.each(search_data.contexts, context.add, context); _.each(search_data.domains, domain.add, domain); context.add({ group_by: pyeval.eval('groupbys', search_data.groupbys || []) }); - context.add(view_manager.active_view.controller.get_context()); + context.add(this.view_manager.active_view.controller.get_context()); var c = pyeval.eval('context', context); for(var k in c) { if (c.hasOwnProperty(k) && /^search_default_/.test(k)) { @@ -396,10 +398,10 @@ FavoriteMenu.include({ .then(function (board_list) { return self.rpc('/board/add_to_dashboard', { menu_id: board_list[0].id, - action_id: view_manager.action.id, + action_id: self.action_id, context_to_save: c, domain: d, - view_mode: view_manager.active_view.type, + view_mode: self.view_manager.active_view.type, name: name, }); }).then(function (r) { diff --git a/addons/google_spreadsheet/static/src/js/search.js b/addons/google_spreadsheet/static/src/js/search.js index 2736088fd8cbd354e4512014a1563b2d8e0a9a9b..030c84b1a84287b9e609622081258712fedef074 100644 --- a/addons/google_spreadsheet/static/src/js/search.js +++ b/addons/google_spreadsheet/static/src/js/search.js @@ -1,11 +1,13 @@ odoo.define('google_spreadsheet.google.spreadsheet', function (require) { "use strict"; +var ActionManager = require('web.ActionManager'); var core = require('web.core'); var data = require('web.data'); var FavoriteMenu = require('web.FavoriteMenu'); var FormView = require('web.FormView'); var pyeval = require('web.pyeval'); +var ViewManager = require('web.ViewManager'); var QWeb = core.qweb; @@ -27,14 +29,19 @@ FormView.include({ FavoriteMenu.include({ prepare_dropdown_menu: function (filters) { this._super(filters); - this.$('.favorites-menu').append(QWeb.render('SearchView.addtogooglespreadsheet')); - this.$('.add-to-spreadsheet').click(this.add_to_spreadsheet.bind(this)); + var am = this.findAncestor(function(a) { + return a instanceof ActionManager; + }); + if (am && am.get_inner_widget() instanceof ViewManager) { + this.view_manager = am.get_inner_widget(); + this.$('.favorites-menu').append(QWeb.render('SearchView.addtogooglespreadsheet')); + this.$('.add-to-spreadsheet').click(this.add_to_spreadsheet.bind(this)); + } }, add_to_spreadsheet: function () { var sv_data = this.searchview.build_search_data(), model = this.searchview.dataset.model, - view_manager = this.searchview.getParent(), - list_view = view_manager.views.list, + list_view = this.view_manager.views.list, list_view_id = list_view ? list_view.view_id : false, context = this.searchview.dataset.get_context() || [], compound_context = new data.CompoundContext(context), diff --git a/addons/mail/static/src/css/mail.css b/addons/mail/static/src/css/mail.css index 687ae228b737c3eb15164b4d3974609b0e61c074..cd6da8a4dc3171738969c9f6ed72dde82b2a86d1 100644 --- a/addons/mail/static/src/css/mail.css +++ b/addons/mail/static/src/css/mail.css @@ -25,6 +25,26 @@ margin-top: -2px; } +/* -------- Mail Wall + Control Panel --------- */ +.openerp .oe_mail_wall{ + display: -webkit-box; + display: -moz-box; + display: -ms-flexbox; + display: -webkit-flex; + display: flex; + -ms-flex-direction: column; + -webkit-flex-direction: column; + flex-direction: column; +} +.openerp .oe_mail_wall .oe-view-manager-content{ + -webkit-box-flex: 1; + -webkit-flex: 1 1 auto; + -ms-flex: 1 1 auto; + flex: 1 1 auto; + overflow: auto; + position: static; +} + /* ---------------- MESSAGES ------------------ */ .openerp .oe_mail .oe_msg{ @@ -731,10 +751,6 @@ margin-bottom: 10px; } -.oe_mail_wall .oe-groupby-menu, .oe_mail_wall .add-to-dashboard { - display: none; -} - .oe-view-manager-content .oe_mail { display: inline-block !important; position: relative !important; diff --git a/addons/mail/static/src/xml/mail.xml b/addons/mail/static/src/xml/mail.xml index b88f254cf4e7a1ccabfdee242ad2ab81965ae753..f97d036bb2a54a20260920272aac911ecb175b51 100644 --- a/addons/mail/static/src/xml/mail.xml +++ b/addons/mail/static/src/xml/mail.xml @@ -171,18 +171,20 @@ Template used to display the communication history in the wall. --> <div t-name="mail.wall" class="oe-view-manager oe_mail_wall oe_view_manager_current"> - <div class="oe-control-panel container-fluid"> - <div class="row"> - <div class="col-md-6 oe-cp-title"> - <ol class="oe-view-title breadcrumb"> - <li class="active"><t t-esc="widget.action.name"/></li> - </ol> + <div class="oe-control-panel"> + <div class="container-fluid"> + <div class="row"> + <div class="col-md-6 oe-cp-title"> + <ol class="oe-view-title breadcrumb"> + <li class="active"><t t-esc="widget.action.name"/></li> + </ol> + </div> + <div class="oe-cp-search-view col-md-6" /> </div> - <div class="oe-cp-search-view col-md-6" /> - </div> - <div class="row"> - <div class="col-sm-6 col-sm-offset-6"> - <div class="oe-search-options btn-group"/> + <div class="row"> + <div class="col-sm-6 col-sm-offset-6"> + <div class="oe-search-options btn-group"/> + </div> </div> </div> </div> diff --git a/addons/web/static/src/css/base.css b/addons/web/static/src/css/base.css index 5626ccec148ba680a8ccce8dcd687b883b57586c..5181e22a2628ff964ff32b1f43659d78fcc82f9b 100644 --- a/addons/web/static/src/css/base.css +++ b/addons/web/static/src/css/base.css @@ -923,60 +923,74 @@ } .openerp .oe_application { height: 100%; - -webkit-flex-grow: 1; - flex-grow: 1; - -ms-flex-negative: 1; - display: inline-block\9; - overflow: auto\9; - width: -webkit-calc(100% - 220px); - width: calc(100% - 220px); -} -.openerp .oe_application .oe_application { - width: 100%; -} -.openerp .oe-view-manager { width: 100%; - height: 100%; + display: -webkit-box; + display: -moz-box; + display: -ms-flexbox; display: -webkit-flex; display: flex; + -ms-flex-direction: column; -webkit-flex-direction: column; flex-direction: column; } -.openerp .oe-view-manager .oe-view-manager-content { +.openerp .oe_application .oe_application { + width: 100%; +} +.openerp .oe-view-manager { + -webkit-box-flex: 1; + -webkit-flex: 1 1 auto; + -ms-flex: 1 1 auto; + flex: 1 1 auto; overflow: auto; - -webkit-flex-grow: 1; - flex-grow: 1; position: relative; } -.openerp .oe-view-manager .oe-view-manager-content a { - color: #7C7BAD; -} -.openerp .oe-view-manager .oe-view-manager-content > div { +.openerp .oe-view-manager .oe-view-manager-content { position: absolute; - position: static\9; top: 0; bottom: 0; right: 0; left: 0; +} +.openerp .oe-view-manager .oe-view-manager-content a { + color: #7C7BAD; +} +.openerp .oe-view-manager .oe-view-manager-content > div { + height: 100%; + position: static; display: none; } +.openerp .oe-view-manager .oe-view-manager-content .oe-view-manager-content { + position: static; +} .openerp .oe-view-manager .oe-view-manager-content .oe-view-manager-content > div { - position: relative; display: block; } .openerp .oe-view-manager .oe-view-manager-debug { margin-right: 5px; } +.openerp .oe-o2m-control-panel { + width: 100%; +} +.openerp .oe-o2m-control-panel .oe-cp-buttons div { + display: inline-block; + padding: 5px 5px; +} +.openerp .oe-o2m-control-panel .oe-cp-pager div { + display: inline-block; + padding: 5px 5px; +} .openerp .oe-control-panel { background-color: #f0eeee; border-bottom: 1px solid #afafb6; - -webkit-flex-shrink: 0; - flex-shrink: 0; - width: 100%; - -webkit-user-select: none; - -moz-user-select: none; + -webkit-box-flex: 0; + -webkit-flex: 0 0 auto; + -ms-flex: 0 0 auto; + flex: 0 0 auto; user-select: none; } +.openerp .oe-control-panel > .container-fluid { + width: 100%; +} .openerp .oe-control-panel .oe-button-column { height: 30px; } @@ -1092,7 +1106,8 @@ } .openerp .oe-control-panel .oe-cp-sidebar .oe_sidebar_delete_item { padding: 0; - display: inline-block; + position: absolute; + right: 10px; } .openerp .oe-control-panel .oe-cp-sidebar .dropdown-menu li a { width: 100%; @@ -1110,9 +1125,6 @@ .openerp .oe-control-panel .oe-pager-buttons { min-height: 30px; } -.openerp .oe_view_manager_inline > .oe-control-panel-content, .openerp .oe_view_manager_inlineview > .oe-control-panel-content { - display: none; -} .openerp .o-modal-header > div { margin-left: 45%; } @@ -2010,7 +2022,7 @@ margin: 4px 7px; } .openerp .oe_form .oe-view-manager-content { - overflow: visible; + overflow: hidden; } .openerp .oe_form_editable .oe_form .oe_form_field_integer input { width: 6em; @@ -3206,7 +3218,7 @@ body.oe_single_form .oe_single_form_container { .modal .oe_act_window.modal-body { padding: 0; } -.modal .oe-view-manager-content > div { +.modal .oe-view-manager-content { position: static !important; } diff --git a/addons/web/static/src/css/base.sass b/addons/web/static/src/css/base.sass index 63ef0cd5bdbafe63ff86bb38a489b401e7dcc973..a4250f78d9166530aa2a46527ae84efd14440adf 100644 --- a/addons/web/static/src/css/base.sass +++ b/addons/web/static/src/css/base.sass @@ -801,54 +801,64 @@ $sheet-padding: 16px text-decoration: underline .oe_application height: 100% - -webkit-flex-grow: 1 - flex-grow: 1 - -ms-flex-negative: 1 - display: inline-block\9 - overflow: auto\9 - width: -webkit-calc(100% - 220px) - width: calc(100% - 220px) + width: 100% + display: -webkit-box + display: -moz-box + display: -ms-flexbox + display: -webkit-flex + display: flex + -ms-flex-direction: column + -webkit-flex-direction: column + flex-direction: column .oe_application width: 100% // }}} // ViewManager common {{{ .oe-view-manager - width: 100% - height: 100% - display: -webkit-flex - display: flex - -webkit-flex-direction: column - flex-direction: column + -webkit-box-flex: 1 + -webkit-flex: 1 1 auto + -ms-flex: 1 1 auto + flex: 1 1 auto + overflow: auto + position: relative .oe-view-manager-content - overflow: auto - -webkit-flex-grow: 1 - flex-grow: 1 - position: relative + position: absolute + top: 0 + bottom: 0 + right: 0 + left: 0 a color: $link-color > div - position: absolute - position: static\9 - top: 0 - bottom: 0 - right: 0 - left: 0 + height: 100% + position: static display: none .oe-view-manager-content + position: static > div - position: relative display: block .oe-view-manager-debug margin-right: 5px + .oe-o2m-control-panel + width: 100% + .oe-cp-buttons + div + display: inline-block + padding: 5px 5px + .oe-cp-pager + div + display: inline-block + padding: 5px 5px .oe-control-panel background-color: rgb(240, 238, 238) border-bottom: 1px solid #afafb6 - -webkit-flex-shrink: 0 - flex-shrink: 0 - width: 100% - -webkit-user-select: none - -moz-user-select: none + -webkit-box-flex: 0 + -webkit-flex: 0 0 auto + -ms-flex: 0 0 auto + flex: 0 0 auto user-select: none + > .container-fluid + width: 100% .oe-button-column height: 30px .dropdown-menu @@ -950,9 +960,6 @@ $sheet-padding: 16px .oe-pager-buttons min-height: 30px - .oe_view_manager_inline, .oe_view_manager_inlineview - > .oe-control-panel-content - display: none // }}} // FormPopup {{{ @@ -1673,8 +1680,7 @@ $sheet-padding: 16px margin: 4px 7px // Override ViewManager overflow auto for One2Many fields .oe-view-manager-content - overflow: visible - + overflow: hidden .oe_form_editable .oe_form .oe_form_field_integer input @@ -2641,7 +2647,7 @@ body.oe_single_form margin: 0 4px 0 0 .oe_act_window.modal-body padding: 0 - .oe-view-manager-content > div + .oe-view-manager-content position: static !important @media (min-width: 768px) diff --git a/addons/web/static/src/js/action_manager.js b/addons/web/static/src/js/action_manager.js index 61686dc4e7e6e44735346d7b167a8e19277052c4..8c560830493a41cdf93d1c4efa1454e3b16f4028 100644 --- a/addons/web/static/src/js/action_manager.js +++ b/addons/web/static/src/js/action_manager.js @@ -1,6 +1,7 @@ odoo.define('web.ActionManager', function (require) { "use strict"; +var ControlPanel = require('web.ControlPanel'); var core = require('web.core'); var crash_manager = require('web.crash_manager'); var data = require('web.data'); @@ -11,7 +12,63 @@ var session = require('web.session'); var ViewManager = require('web.ViewManager'); var Widget = require('web.Widget'); -var CompoundContext = data.CompoundContext; +var State = core.Class.extend({ + init: function(title, flags) { + this.title = title; + this.flags = flags; + }, + enable: function() { return $.when(); }, + disable: function() {}, + destroy: function() {}, + get_widget: function() {}, + get_title: function() { return this.title; }, + get_flags: function() { return this.flags; }, + get_action: function() {}, + get_active_view: function() {}, +}); + +var WidgetState = State.extend({ + init: function(title, flags, widget) { + this._super(title, flags); + + this.widget = widget; + if (this.widget.get('title')) { + this.title = title; + } else { + this.widget.set('title', title); + } + this.widget.on("do_search", this, function() { + // active_search = this.control_panel.activate_search(view.created); + // call activate search on search view + // domain context and groupby computed are important + }); + }, + set_cp_content: function(content) { this.cp_content = content; }, + get_cp_content: function() { return this.cp_content; }, + get_action: function() { return this.widget.action; }, + get_active_view: function() { return this.widget.active_view; }, + destroy: function() { + if (this.cp_content && this.cp_content.searchview) { + this.cp_content.searchview.destroy(); + } + this.widget.destroy(); + }, + get_widget: function() { + return this.widget; + }, +}); + +var FunctionState = State.extend({ + init: function(title, flags) { + this._super(title, flags); + + this.widget = { + view_stack: [{ + controller: { get: function () { return this.title; }} + }] + }; + } +}); var ActionManager = Widget.extend({ template: "ActionManager", @@ -22,9 +79,28 @@ var ActionManager = Widget.extend({ this.webclient = parent; this.dialog = null; this.dialog_widget = null; - this.widgets = []; + this.states = []; this.on('history_back', this, this.proxy('history_back')); }, + start: function() { + this._super(); + + // Instantiate the unique main control panel used by every widget in this.states + this.main_control_panel = new ControlPanel(this); + // Listen to event "switch_view" trigerred on the control panel when clicking + // on switch buttons. Forward this event to the current inner_widget + this.main_control_panel.on("switch_view", this, function(view_type) { + this.inner_widget.trigger("switch_view", view_type); + }); + // Listen to event "on_breadcrumb_click" trigerred on the control panel when + // clicking on a part of the breadcrumbs. Call select_state for this breadcrumb. + this.main_control_panel.on("on_breadcrumb_click", this, function(state, index) { + this.select_state(state, index); + }); + + // Append the main control panel to the DOM (inside the ActionManager jQuery element) + this.main_control_panel.appendTo(this.$el); + }, dialog_stop: function (reason) { if (this.dialog) { this.dialog.destroy(reason); @@ -32,125 +108,150 @@ var ActionManager = Widget.extend({ this.dialog = null; }, /** - * Add a new widget to the action manager + * Add a new state to the action manager * - * widget: typically, widgets added are instance.web.ViewManager. The action manager + * widget: typically, widgets added are web.ViewManager. The action manager * uses this list of widget to handle the breadcrumbs. * action: new action * options.on_reverse_breadcrumb: will be called when breadcrumb is selected * options.clear_breadcrumbs: boolean, if true, current widgets are destroyed - * options.replace_breadcrumb: boolean, if true, replace current breadcrumb */ - push_widget: function(widget, action, options) { + push_state: function(widget, action, options) { var self = this, to_destroy, - old_widget = this.inner_widget; + old_widget = this.inner_widget, + old_state = this.get_current_state(); options = options || {}; if (options.clear_breadcrumbs) { - to_destroy = this.widgets; - this.widgets = []; - } else if (options.replace_breadcrumb) { - to_destroy = _.last(this.widgets); - this.widgets = _.initial(this.widgets); + to_destroy = this.states; + this.states = []; } + var new_state, + title = action.display_name || action.name; if (widget instanceof Widget) { - var title = widget.get('title') || action.display_name || action.name; - widget.set('title', title); - this.widgets.push(widget); + new_state = new WidgetState(title, action.flags, widget); } else { - this.widgets.push({ - view_stack: [{ - controller: {get: function () {return action.display_name || action.name; }}, - }], - destroy: function () {}, - }); + new_state = new FunctionState(title, action.flags); } - _.last(this.widgets).__on_reverse_breadcrumb = options.on_reverse_breadcrumb; + this.states.push(new_state); + + this.get_current_state().__on_reverse_breadcrumb = options.on_reverse_breadcrumb; this.inner_action = action; this.inner_widget = widget; - return $.when(this.inner_widget.appendTo(this.$el)).done(function () { - if ((action.target !== 'inline') && (!action.flags.headless) && widget.$header) { - widget.$header.show(); + + // 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()); } + } + + return $.when(this.inner_widget.appendTo(this.$el)).done(function () { if (old_widget) { old_widget.$el.hide(); } if (options.clear_breadcrumbs) { - self.clear_widgets(to_destroy); + self.clear_states(to_destroy); } }); }, get_breadcrumbs: function () { - return _.flatten(_.map(this.widgets, function (widget) { - if (widget instanceof ViewManager) { - return widget.view_stack.map(function (view, index) { + return _.flatten(_.map(this.states, function (state) { + if (state.widget instanceof ViewManager) { + return state.widget.view_stack.map(function (view, index) { return { - title: view.controller.get('title') || widget.title, + title: view.controller.get('title') || state.get_title(), index: index, - widget: widget, + widget: state, }; }); } else { - return {title: widget.get('title'), widget: widget }; + return { title: state.get_title(), widget: state }; } }), true); }, get_title: function () { - if (this.widgets.length === 1) { + if (this.states.length === 1) { // horrible hack to display the action title instead of "New" for the actions // that use a form view to edit something that do not correspond to a real model // for example, point of sale "Your Session" or most settings form, - var widget = this.widgets[0]; - if (widget instanceof ViewManager && widget.view_stack.length === 1) { - return widget.title; + var state = this.states[0]; + if (state.widget instanceof ViewManager && state.widget.view_stack.length === 1) { + return state.get_title(); } } return _.pluck(this.get_breadcrumbs(), 'title').join(' / '); }, - get_widgets: function () { - return this.widgets.slice(0); + get_states: function () { + return this.states; + }, + get_current_state: function() { + return _.last(this.states); + }, + get_inner_action: function() { + return this.inner_action; + }, + get_inner_widget: function() { + return this.inner_widget; }, history_back: function() { - var widget = _.last(this.widgets); - if (widget instanceof ViewManager) { - var nbr_views = widget.view_stack.length; + var state = this.get_current_state(); + if (state.widget instanceof ViewManager) { + var nbr_views = state.widget.view_stack.length; if (nbr_views > 1) { - return this.select_widget(widget, nbr_views - 2); + return this.select_state(state, nbr_views - 2); } } - if (this.widgets.length > 1) { - widget = this.widgets[this.widgets.length - 2]; - var index = widget.view_stack && widget.view_stack.length - 1; - return this.select_widget(widget, index); + if (this.states.length > 1) { + state = this.states[this.states.length - 2]; + var index = state.widget.view_stack && state.widget.view_stack.length - 1; + return this.select_state(state, index); } return $.Deferred().reject(); }, - select_widget: function(widget, index) { + select_state: function(state, index) { var self = this; if (this.webclient.has_uncommitted_changes()) { return $.Deferred().reject(); } - var widget_index = this.widgets.indexOf(widget), - def = $.when(widget.select_view && widget.select_view(index)); + // Client widget (-> put in ClientState?) + if (state.__on_reverse_breadcrumb) { + state.__on_reverse_breadcrumb(); + } + // 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()); + + 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; return def.done(function () { - if (widget.__on_reverse_breadcrumb) { - widget.__on_reverse_breadcrumb(); - } - _.each(self.widgets.splice(widget_index + 1), function (w) { - w.destroy(); - }); - self.inner_widget = _.last(self.widgets); if (self.inner_widget.do_show) { self.inner_widget.do_show(); } }); }, - clear_widgets: function(widgets) { - _.invoke(widgets || this.widgets, 'destroy'); - if (!widgets) { - this.widgets = []; + clear_states: function(states) { + _.map(states || this.states, function(state) { + state.destroy(); + }); + if (!states) { + this.states = []; this.inner_widget = null; } }, @@ -202,7 +303,7 @@ var ActionManager = Widget.extend({ var self = this, action_loaded; if (state.action) { - if (_.isString(state.action) && core.action_registry.contains(state.action)) { + if (_.isString(state.action) && core.action_regisry.contains(state.action)) { var action_client = { type: "ir.actions.client", tag: state.action, @@ -300,7 +401,7 @@ var ActionManager = Widget.extend({ if (action === false) { action = { type: 'ir.actions.act_window_close' }; - } else if (_.isString(action) && core.action_registry.contains(action)) { + } else if (_.isString(action) && core.action_regisry.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)) { @@ -318,7 +419,7 @@ var ActionManager = Widget.extend({ core.bus.trigger('action', action); // Ensure context & domain are evaluated and can be manipulated/used - var ncontext = new CompoundContext(options.additional_context, action.context || {}); + var ncontext = new data.CompoundContext(options.additional_context, action.context || {}); action.context = pyeval.eval('context', ncontext); if (action.context.active_id || action.context.active_ids) { // Here we assume that when an `active_id` or `active_ids` is used @@ -335,39 +436,56 @@ var ActionManager = Widget.extend({ console.error("No type for action", action); return $.Deferred().reject(); } + var type = action.type.replace(/\./g,'_'); - var popup = action.target === 'new'; - var inline = action.target === 'inline' || action.target === 'inlineview'; - var form = _.str.startsWith(action.view_mode, 'form'); - action.flags = _.defaults(action.flags || {}, { - views_switcher : !popup && !inline, - search_view : !popup && !inline, - action_buttons : !popup && !inline, - sidebar : !popup && !inline, - pager : (!popup || !form) && !inline, - display_title : !popup, - headless: (popup || inline) && form, - search_disable_custom_filters: action.context && action.context.search_disable_custom_filters - }); action.menu_id = options.action_menu_id; action.context.params = _.extend({ 'action' : action.id }, action.context.params); if (!(type in this)) { console.error("Action manager can't handle action of type " + action.type, action); return $.Deferred().reject(); } + + // Special case for Dashboards, this should definitively be done upstream + if (action.res_model === 'board.board' && action.view_mode === 'form') { + action.target = 'inline'; + _.extend(action.flags, { + headless: true, + views_switcher: false, + display_title: false, + search_view: false, + pager: false, + sidebar: false, + action_buttons: false + }); + } else { + var popup = action.target === 'new'; + var inline = action.target === 'inline' || action.target === 'inlineview'; + var form = _.str.startsWith(action.view_mode, 'form'); + action.flags = _.defaults(action.flags || {}, { + views_switcher : !popup && !inline, + search_view : !popup && !inline, + action_buttons : !popup && !inline, + sidebar : !popup && !inline, + pager : (!popup || !form) && !inline, + display_title : !popup, + headless: (popup || inline) && form, + search_disable_custom_filters: action.context && action.context.search_disable_custom_filters + }); + } + return this[type](action, options); }, null_action: function() { this.dialog_stop(); - this.clear_widgets(); + this.clear_states(); }, /** * * @param {Object} executor * @param {Object} executor.action original action - * @param {Function<instance.web.Widget>} executor.widget function used to fetch the widget instance + * @param {Function<web.Widget>} executor.widget function used to fetch the widget instance * @param {String} executor.klass CSS class to add on the dialog root, if action.target=new - * @param {Function<instance.web.Widget, undefined>} executor.post_process cleanup called after a widget has been added as inner_widget + * @param {Function<web.Widget, undefined>} executor.post_process cleanup called after a widget has been added as inner_widget * @param {Object} options * @return {*} */ @@ -425,14 +543,13 @@ var ActionManager = Widget.extend({ } widget = executor.widget(); this.dialog_stop(executor.action); - return this.push_widget(widget, executor.action, options); + return this.push_state(widget, executor.action, options); }, 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); + return new ViewManager(self, null, null, null, action, self.main_control_panel.get_bus()); }, action: action, klass: 'oe_act_window', @@ -440,7 +557,7 @@ var ActionManager = Widget.extend({ }, ir_actions_client: function (action, options) { var self = this; - var ClientWidget = core.action_registry.get(action.tag); + var ClientWidget = core.action_registry.get_object(action.tag); if (!ClientWidget) { return self.do_warn("Action Error", "Could not find client action '" + action.tag + "'."); } @@ -454,7 +571,11 @@ var ActionManager = Widget.extend({ } return this.ir_actions_common({ - widget: function () { return new ClientWidget(self, action); }, + widget: function () { + // AAB: temporary fix: Hide main control panel as client actions do not use it + self.main_control_panel.do_hide(); + return new ClientWidget(self, action); + }, action: action, klass: 'oe_act_client', }, options).then(function () { diff --git a/addons/web/static/src/js/control_panel.js b/addons/web/static/src/js/control_panel.js index d5c016522f503e8a2b35d7a2b3df2a92c9c70cf6..b71aefa60f1022e0d4d2961bdb08fad59f47bc1a 100644 --- a/addons/web/static/src/js/control_panel.js +++ b/addons/web/static/src/js/control_panel.js @@ -5,12 +5,13 @@ var core = require('web.core'); var Dialog = require('web.Dialog'); var formats = require('web.formats'); var framework = require('web.framework'); -var Model = require('web.Model'); -var pyeval = require('web.pyeval'); var SearchView = require('web.SearchView'); var utils = require('web.utils'); var Widget = require('web.Widget'); +var QWeb = core.qweb; +var _t = core._t; + var ControlPanel = Widget.extend({ template: 'ControlPanel', events: { @@ -27,58 +28,118 @@ var ControlPanel = Widget.extend({ this.template = template; } - this.view_manager = parent; - this.action_manager = this.view_manager.action_manager; - this.action = this.view_manager.action; - this.dataset = this.view_manager.dataset; - this.active_view = this.view_manager.active_view; - this.views = this.view_manager.views; - this.flags = this.view_manager.flags; - this.title = this.view_manager.title; // needed for Favorites of searchview - this.view_order = this.view_manager.view_order; - this.multiple_views = (this.view_order.length > 1); + this.bus = new core.Bus(); + this.bus.on("setup_search_view", this, this.setup_search_view); + 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.searchview = null; + + this.flags = null; + this.dataset = null; + this.active_view = null; + this.title = null; // Needed for Favorites of searchview }, start: function() { - var self = this; - - // Retrieve control panel elements + // Retrieve ControlPanel jQuery nodes this.$control_panel = this.$('.oe-control-panel-content'); this.$breadcrumbs = this.$('.oe-view-title'); - this.$switch_buttons = this.$('.oe-cp-switch-buttons button'); + 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'); - // AAB: Use sidebar and pager of the ControlPanel only if it is displayed, otherwise set them - // to undefined to that the view uses its own elements to sidebar and pager, as follows: - // this.$sidebar = !this.flags.headless && this.flags.sidebar ? this.$('.oe-cp-sidebar') : undefined, - // this.$pager = !this.flags.headless ? this.$('.oe-cp-pager') : undefined; - // But rather use the following definition to keep behavior as it is for now (i.e. it does not - // display the pager in one2many list views) - this.$sidebar = this.flags.sidebar ? this.$('.oe-cp-sidebar') : undefined; + this.$searchview = this.$(".oe-cp-search-view"); + this.$searchview_buttons = this.$('.oe-search-options'); this.$pager = this.$('.oe-cp-pager'); + this.$sidebar = this.$('.oe-cp-sidebar'); + + return this._super(); + }, + /** + * @return {Object} Dictionnary of ControlPanel nodes + */ + 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) + 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.active_view = state.widget.active_view; + this.dataset = state.widget.dataset; + this.title = state.widget.title; // needed for Favorites of searchview // Hide the ControlPanel in headless mode - if (this.flags.headless) { - this.$control_panel.hide(); + 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) { + var self = this; - _.each(this.views, function (view) { - // Expose control panel elements to the views so that they can insert stuff in them - view.options = _.extend(view.options, { - $buttons : !self.flags.headless ? self.$('.oe-' + view.type + '-buttons') : undefined, - $sidebar : self.$sidebar, - $pager : self.$pager, - }, self.flags, self.flags[view.type], view.options); - // Show $buttons as views will put their own buttons inside it and show/hide them - if (view.options.$buttons) view.options.$buttons.show(); - self.$('.oe-cp-switch-' + view.type).tooltip(); + 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(); }); + }, + render_switch_buttons: function(views) { + if (views.length > 1) { + var self = this; - // Create the searchview - this.search_view_loaded = this.setup_search_view(); + var switch_buttons = QWeb.render('ControlPanel.switch-buttons', {views: views}); + $(switch_buttons).appendTo(this.$switch_buttons); - return this._super(); + // Create tooltips + _.each(views, function(view) { + self.$('.oe-cp-switch-' + view.type).tooltip(); + }); + } }, - /** + /** * Triggers an event when switch-buttons are clicked on */ on_switch_buttons_click: function(event) { @@ -87,22 +148,34 @@ var ControlPanel = Widget.extend({ }, /** * 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) { - this.active_view = active_view; + update: function(active_view, search_view_hidden, breadcrumbs) { + this.active_view = active_view; // this.active_view only used for debug view - this.update_search_view(); - this.update_breadcrumbs(); + this.update_switch_buttons(active_view); + this.update_search_view(search_view_hidden); + this.update_breadcrumbs(breadcrumbs); this.render_debug_view(); - - // Update switch-buttons - this.$switch_buttons.removeClass('active'); - this.$('.oe-cp-switch-' + this.active_view.type).addClass('active'); }, - update_breadcrumbs: function () { + /** + * Removes active class on all switch-buttons and adds it to the one of the active view + * @param {Object} [active_view] the active_view + */ + update_switch_buttons: function(active_view) { + _.each(this.$switch_buttons.contents('button'), function(button) { + $(button).removeClass('active'); + }); + this.$('.oe-cp-switch-' + active_view.type).addClass('active'); + }, + /** + * Updates the breadcrumbs + **/ + update_breadcrumbs: function (breadcrumbs) { var self = this; - if (!this.action_manager) return; - var breadcrumbs = this.action_manager.get_breadcrumbs(); + if (!breadcrumbs.length) return; var $breadcrumbs = breadcrumbs.map(function (bc, index) { @@ -119,27 +192,27 @@ var ControlPanel = Widget.extend({ .toggleClass('active', is_last); if (!is_last) { $bc.click(function () { - self.action_manager.select_widget(bc.widget, bc.index); + self.trigger("on_breadcrumb_click", bc.widget, bc.index); }); } return $bc; } }, /** - * Sets up the search view. + * Sets up the search view and calls set_search_view on the widget requesting it * - * @returns {jQuery.Deferred} search view startup deferred + * @param {Object} [src] the widget requesting a search_view + * @param {Object} [action] the action (required to instantiated the SearchView) + * @param {Object} [dataset] the dataset (required to instantiated the SearchView) + * @param {Object} [flags] a dictionnary of Booleans */ - setup_search_view: function() { - if (this.searchview) { - this.searchview.destroy(); - } - - var view_id = (this.action && this.action.search_view_id && this.action.search_view_id[0]) || false; + 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 = {}; - var context = this.action ? this.action.context : []; + var context = action ? action.context : []; _.each(context, function (value, key) { var match = /^search_default_(.*)$/.exec(key); if (match) { @@ -148,67 +221,29 @@ var ControlPanel = Widget.extend({ }); var options = { - hidden: this.flags.search_view === false, - disable_custom_filters: this.flags.search_disable_custom_filters, - $buttons: this.$('.oe-search-options'), - action: this.action, + hidden: flags.search_view === false, + disable_custom_filters: flags.search_disable_custom_filters, + $buttons: this.$searchview_buttons, + action: action, }; - this.searchview = new SearchView(this, this.dataset, view_id, search_defaults, options); - this.searchview.on('search_data', this, this.search.bind(this)); - return this.searchview.appendTo(this.$(".oe-cp-search-view:first")); + // 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); }, - update_search_view: function() { + /** + * Updates the SearchView's visibility and extend the breadcrumbs area if the SearchView is not visible + * @param {Boolean} [is_hidden] visibility of the searchview + **/ + update_search_view: function(is_hidden) { if (this.searchview) { - var is_hidden = this.active_view.controller.searchable === false; 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); } }, - search: function(domains, contexts, groupbys) { - var self = this, - controller = this.active_view.controller, - action_context = this.action.context || {}, - view_context = controller.get_context(); - pyeval.eval_domains_and_contexts({ - domains: [this.action.domain || []].concat(domains || []), - contexts: [action_context, view_context].concat(contexts || []), - group_by_seq: groupbys || [] - }).done(function (results) { - if (results.error) { - self.active_search.resolve(); - throw new Error( - _.str.sprintf(_t("Failed to evaluate search criterions")+": \n%s", - JSON.stringify(results.error))); - } - self.dataset._model = new Model( - self.dataset.model, results.context, results.domain); - var groupby = results.group_by.length ? - results.group_by : - action_context.group_by; - if (_.isString(groupby)) { - groupby = [groupby]; - } - if (!controller.grouped && !_.isEmpty(groupby)){ - self.dataset.set_sort([]); - } - $.when(controller.do_search(results.domain, results.context, groupby || [])).then(function() { - self.active_search.resolve(); - }); - }); - }, - activate_search: function(view_created_def) { - this.active_search = $.Deferred(); - if (this.searchview && - this.flags.auto_search && - this.active_view.controller.searchable !== false) { - $.when(this.search_view_loaded,view_created_def).done(this.searchview.do_search); - } else { - this.active_search.resolve(); - } - return this.active_search; - }, on_debug_changed: function (evt) { var self = this, params = $(evt.target).data(), @@ -231,7 +266,7 @@ var ControlPanel = Widget.extend({ var ids = current_view.get_selected_ids(); if (ids.length === 1) { this.dataset.call('get_metadata', [ids]).done(function(result) { - var dialog = new Dialog(this, { + new Dialog(this, { title: _.str.sprintf(_t("Metadata (%s)"), self.dataset.model), size: 'medium', buttons: { diff --git a/addons/web/static/src/js/view_manager.js b/addons/web/static/src/js/view_manager.js index 0474f0e127be0287a5347d840e9afbab41140d6d..94af0912907207f4f2c2afbfe73a6096fdab074e 100644 --- a/addons/web/static/src/js/view_manager.js +++ b/addons/web/static/src/js/view_manager.js @@ -1,21 +1,13 @@ odoo.define('web.ViewManager', function (require) { "use strict"; -var ControlPanel = require('web.ControlPanel'); var core = require('web.core'); var data = require('web.data'); -var Dialog = require('web.Dialog'); -var formats = require('web.formats'); -var framework = require('web.framework'); var Model = require('web.Model'); var pyeval = require('web.pyeval'); -var utils = require('web.utils'); -var SearchView = require('web.SearchView'); -var session = require('web.session'); var Widget = require('web.Widget'); var _t = core._t; -var QWeb = core.qweb; var ViewManager = Widget.extend({ template: "ViewManager", @@ -23,25 +15,14 @@ var ViewManager = Widget.extend({ * @param {Object} [dataset] null object (... historical reasons) * @param {Array} [views] List of [view_id, view_type] * @param {Object} [flags] various boolean describing UI state + * @param {Object} [cp_bus] Bus to allow communication with ControlPanel */ - init: function(parent, dataset, views, flags, action) { + init: function(parent, dataset, views, flags, action, cp_bus) { if (action) { flags = action.flags || {}; if (!('auto_search' in flags)) { flags.auto_search = action.auto_search !== false; } - if (action.res_model === 'board.board' && action.view_mode === 'form') { - action.target = 'inline'; - // Special case for Dashboards - _.extend(flags, { - views_switcher : false, - display_title : false, - search_view : false, - pager : false, - sidebar : false, - action_buttons : false - }); - } this.action = action; this.action_manager = parent; dataset = new data.DataSetSearch(this, action.res_model, action.context, action.domain); @@ -62,6 +43,7 @@ var ViewManager = Widget.extend({ this.active_view = null; this.registry = core.view_registry; this.title = this.action && this.action.name; + this.cp_bus = cp_bus; _.each(views, function (view) { var view_type = view[1] || view.view_type, @@ -81,10 +63,8 @@ var ViewManager = Widget.extend({ self.views[view_type] = view_descr; }); - // Instantiate ControlPanel - this.control_panel = new ControlPanel(self); - // Listen to event 'switch_view' trigerred when clicking on switch-buttons - this.control_panel.on('switch_view', this, function(view_type) { + // Listen to event 'switch_view' indicating that the VM must now display view wiew_type + this.on('switch_view', this, function(view_type) { if (view_type === 'form' && this.active_view && this.active_view.type === 'form') { this._display_view(view_type); } else { @@ -114,17 +94,85 @@ var ViewManager = Widget.extend({ this.$el.addClass("oe_view_manager_" + ((this.action && this.action.target) || 'current')); - // Insert ControlPanel in the DOM - var cp_loaded = this.control_panel.prependTo(this.$el); - var main_view_loaded = this.switch_mode(default_view, null, default_options); + 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); + } + + _.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(main_view_loaded, cp_loaded); + 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; }, /** - * Needed for dashboard.js to add Favorites to Dashboard + * 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 */ - get_searchview: function() { - return this.control_panel.searchview; + set_search_view: function(searchview, search_view_loaded_def) { + this.searchview = searchview; + this.search_view_loaded = search_view_loaded_def; + }, + /** + * Executed on event "search_data" thrown by the SearchView + */ + search: function(domains, contexts, groupbys) { + var self = this, + controller = this.active_view.controller, // AAB: Correct view must be loaded here + action_context = this.action.context || {}, + view_context = controller.get_context(); + pyeval.eval_domains_and_contexts({ + domains: [this.action.domain || []].concat(domains || []), + contexts: [action_context, view_context].concat(contexts || []), + group_by_seq: groupbys || [] + }).done(function (results) { + if (results.error) { + self.active_search.resolve(); + throw new Error( + _.str.sprintf(_t("Failed to evaluate search criterions")+": \n%s", + JSON.stringify(results.error))); + } + self.dataset._model = new Model( + self.dataset.model, results.context, results.domain); + var groupby = results.group_by.length + ? results.group_by + : action_context.group_by; + if (_.isString(groupby)) { + groupby = [groupby]; + } + if (!controller.grouped && !_.isEmpty(groupby)){ + self.dataset.set_sort([]); + } + $.when(controller.do_search(results.domain, results.context, groupby || [])).then(function() { + self.active_search.resolve(); + }); + }); }, get_default_view: function() { return this.flags.default_view || this.view_order[0].type; @@ -147,15 +195,22 @@ var ViewManager = Widget.extend({ if (this.active_view.controller) this.active_view.controller.do_hide(); if (this.active_view.$container) this.active_view.$container.hide(); } - this.active_view = this.control_panel.active_view = view; + this.active_view = view; if (!view.created) { view.created = this.create_view.bind(this)(view, view_options); } - // Tell the ControlPanel to call do_search on its searchview - var active_search = this.control_panel.activate_search(view.created); - return $.when(view.created, active_search).done(function () { + // Call do_search on the searchview to compute domains, contexts and groupbys + if (this.searchview && + this.flags.auto_search && + view.controller.searchable !== false) { + this.active_search = $.Deferred(); + $.when(this.search_view_loaded, view.created).done(function() { + self.searchview.do_search(); + }); + } + return $.when(view.created, this.active_search).done(function () { self._display_view(view_options); self.trigger('switch_mode', view_type, no_store, view_options); }); @@ -165,17 +220,21 @@ var ViewManager = Widget.extend({ this.active_view.$container.show(); $.when(this.active_view.controller.do_show(view_options)).done(function () { // Tell the ControlPanel to update its elemnts - self.control_panel.update(self.active_view); + 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); + } }); }, create_view: function(view, view_options) { var self = this, - View = this.registry.get_object(view.type), + View = this.registry.get(view.type), options = _.clone(view.options), view_loaded = $.Deferred(); - if (view.type === "form" && ((this.action && (this.action.target === 'new' || this.action.target === 'inline')) || - (view_options && view_options.mode === 'edit'))) { + if (view.type === "form" && ((this.action && (this.action.target === 'new' || this.action.target === 'inline')) + || (view_options && view_options.mode === 'edit'))) { options.initial_mode = 'edit'; } var controller = new View(this, this.dataset, view.view_id, options), @@ -193,7 +252,10 @@ var ViewManager = Widget.extend({ if (self.action_manager) self.action_manager.trigger('history_back'); }); controller.on("change:title", this, function() { - self.control_panel.update_breadcrumbs(); + if (self.cp_bus) { + var breadcrumbs = self.action_manager.get_breadcrumbs(); + self.cp_bus.trigger("update_breadcrumbs", breadcrumbs); + } }); controller.on('view_loaded', this, function () { view_loaded.resolve(); diff --git a/addons/web/static/src/js/views/search_menus.js b/addons/web/static/src/js/views/search_menus.js index 54d83ba8f78a67b0f00ae8b692747ba7d4808ab6..0860c8aa553f98034c843a13a78b1c0fd7618398 100644 --- a/addons/web/static/src/js/views/search_menus.js +++ b/addons/web/static/src/js/views/search_menus.js @@ -37,7 +37,7 @@ return Widget.extend({ this.$save_name = this.$('.oe-save-name'); this.$inputs = this.$save_name.find('input'); this.$divider = this.$('.divider'); - this.$inputs.eq(0).val(this.searchview.getParent().title); + this.$inputs.eq(0).val(this.searchview.get_title()); var $shared_filter = this.$inputs.eq(1), $default_filter = this.$inputs.eq(2); $shared_filter.click(function () {$default_filter.prop('checked', false);}); diff --git a/addons/web/static/src/js/views/search_view.js b/addons/web/static/src/js/views/search_view.js index af18bdea9aca8fb8c00742c6af4fec06bbcb2045..6e78e69b46622532c59502e5ab097fdb593f0571 100644 --- a/addons/web/static/src/js/views/search_view.js +++ b/addons/web/static/src/js/views/search_view.js @@ -376,6 +376,7 @@ var SearchView = Widget.extend(/** @lends instance.web.SearchView# */{ this.query = undefined; this.dataset = dataset; this.view_id = view_id; + this.title = options.action && options.action.name; this.search_fields = []; this.filters = []; this.groupbys = []; @@ -411,6 +412,9 @@ var SearchView = Widget.extend(/** @lends instance.web.SearchView# */{ .toggleClass('fa-caret-up', this.visible_filters); return this.alive($.when(this._super(), this.alive(load_view).then(this.view_loaded.bind(this)))); }, + get_title: function() { + return this.title; + }, view_loaded: function (r) { var custom_filters_ready; this.fields_view_get = r; diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml index 3758aec3366a54399a03f22f3f32614756550685..d148ce2f17a90039409cf07ddf7134bbce1c2fb5 100644 --- a/addons/web/static/src/xml/base.xml +++ b/addons/web/static/src/xml/base.xml @@ -225,7 +225,6 @@ </td> </tr> <tr> - <td><label for="backup_pwd">Master Password:</label></td> <td><input type="password" name="backup_pwd" class="required" /></td> </tr> </table> @@ -470,42 +469,45 @@ <div t-name="ActionManager" class="oe_application" /> <t t-name="ControlPanel"> - <div class="oe-control-panel container-fluid"> - <div class="row"> - <div class="col-md-6 oe-cp-title"> - <div t-if="widget.debug" class="oe_debug_view btn-group btn-group-sm"/> - <ol class="oe-view-title breadcrumb" t-if="widget.flags.display_title !== false"> - </ol> - </div> - <div class="oe-cp-search-view col-md-6" /> - </div> - <div class="row"> - <div class="col-md-6 oe-button-column"> - <div class="oe-cp-buttons"> - <t t-foreach="widget.views" t-as="view"> - <div t-attf-class="oe-#{view}-buttons"/> - </t> + <div class="oe-control-panel"> + <div class="container-fluid"> + <div class="row"> + <div class="col-md-6 oe-cp-title"> + <div t-if="widget.debug" class="oe_debug_view btn-group btn-group-sm"/> + <ol class="oe-view-title breadcrumb"> + </ol> </div> - <div class="oe-cp-sidebar"></div> + <div class="oe-cp-search-view col-md-6" /> </div> - <div class="col-md-6"> - <div class="oe-search-options btn-group"/> - <div class="oe-right-toolbar"> - <div class="oe-cp-pager"></div> - <t t-if="widget.multiple_views"> - <div class="oe-cp-switch-buttons btn-group btn-group-sm"> - <t t-foreach="widget.view_order" 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"> - </button> - </t> - </div> - </t> + <div class="row"> + <div class="col-md-6 oe-button-column"> + <div class="oe-cp-buttons"></div> + <div class="oe-cp-sidebar"></div> + </div> + <div class="col-md-6"> + <div class="oe-search-options btn-group"/> + <div class="oe-right-toolbar"> + <div class="oe-cp-pager"></div> + <div class="oe-cp-switch-buttons btn-group btn-group-sm"></div> + </div> </div> </div> </div> </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"/> + </t> +</t> + <div t-name="ViewManager" class="oe-view-manager"> <div class="oe-view-manager-content"> <t t-foreach="widget.views" t-as="view">