[IMP] Web et al.: unique main ControlPanel

A unique main ControlPanel is instantiated by the ActionManager and it
is updated each time the state changes (i.e. when an action is performed
on the ActionManager or when switch_mode() is called on the ViewManager).

Introduction of State classes to facilitate the way of restoring
previously rendered states in ControlPanel. The notion of widgets
previously used in the ActionManager is now encapsulated in the notion
of states.

The search() function has been moved from ControlPanel to ViewManager as
it makes more sense like this.

The ViewManager doesn't have the reference to the ControlPanel but
rather communicate with it through a dedicated bus.

Some refactoring has been done on the ControlPanel as well so that it
doesn't need to know the ActionManager and the current Action anymore
(it communicates with the AM with events, and it updates itself using
information passed in arguments).

Improves the way the ViewManager requests the ControlPanel to
instantiate a SearchView, and how it waits for this SearchView to be
corretly loaded.

Move back the views logic to the ViewManager which directly asks
the ControlPanel to render the buttons and switch-buttons areas with
the views passed in arguments.

Removes option replace_breadcrumb in push_state() of ActionManager as it
is not used.

Side changes:
* Account: account_widget.js:
    Adapt code to retrieve the current state of the ActionManager.
    Required due to the introduction of State classes that encapsulate
    the notions of widgets and inner_widget in the ActionManager.
* Board: dashboard.js:
    Some refactoring to adapt the code according to the new Widgets
    structure. Several dependencies (e.g. searchview, action) were
    unnecessary. Do not append the "add to dashboard" option to the
    Favorite menu if there is no ViewManager available (e.g. in
    Messaging) instead of hidding it with a CSS rule, as a ViewManager
    required to retrieve the active_view.
* Google_spreadsheet: search.js:
    Change the way to retrieve the list_view_id s.t. it does not rely on
    parent of the SearchView anymore.
* Web: search.js:
    Change the way to retrieve the title in search.js for Favorites s.t.
    it does not rely on the parent of the SearchView anymore. Doing so,
    the ControlPanel does not need the title attribute anymore.
This commit is contained in:
Aaron Bohy
2015-03-20 16:18:33 +01:00
parent 53a9f6c081
commit 151c90743e
13 changed files with 661 additions and 392 deletions
+5 -5
View File
@@ -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
+28 -26
View File
@@ -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) {
@@ -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),
+20 -4
View File
@@ -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;
+13 -11
View File
@@ -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>
+47 -35
View File
@@ -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);
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;
}
.openerp .oe_application .oe_application {
width: 100%;
}
.openerp .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;
}
.openerp .oe-view-manager .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;
}
.openerp .oe-view-manager .oe-view-manager-content a {
color: #7C7BAD;
}
.openerp .oe-view-manager .oe-view-manager-content > div {
position: absolute;
position: static\9;
top: 0;
bottom: 0;
right: 0;
left: 0;
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;
}
+41 -35
View File
@@ -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)
+205 -84
View File
@@ -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 () {
+152 -117
View File
@@ -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');
// Hide the ControlPanel in headless mode
if (this.flags.headless) {
this.$control_panel.hide();
}
_.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();
});
// Create the searchview
this.search_view_loaded = this.setup_search_view();
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
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;
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;
var switch_buttons = QWeb.render('ControlPanel.switch-buttons', {views: views});
$(switch_buttons).appendTo(this.$switch_buttons);
// 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: {
+103 -41
View File
@@ -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);
});
return $.when(main_view_loaded, cp_loaded);
// 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(self.main_view_loaded, this.search_view_loaded);
},
/**
* Needed for dashboard.js to add Favorites to Dashboard
* Sets the external nodes in which the ViewManager and its views should insert elements
* @param {Object} [nodes] a dictionnary of jQuery nodes
*/
get_searchview: function() {
return this.control_panel.searchview;
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) {
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();
@@ -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);});
@@ -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;
+31 -29
View File
@@ -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">