_t = instance.web._t;
instance.web.ActionManager = instance.web.Widget.extend({
+ template: "ActionManager",
init: function(parent) {
this._super(parent);
this.inner_action = null;
this.webclient = parent;
this.dialog = null;
this.dialog_widget = null;
- this.view_managers = [];
+ this.widgets = [];
this.on('history_back', this, this.proxy('history_back'));
},
dialog_stop: function (reason) {
this.dialog = null;
},
/**
- * Add a new item to the breadcrumb
+ * Add a new widget to the action manager
*
- * If the title of an item is an array, the multiple title mode is in use.
- * (eg: a widget with multiple views might need to display a title for each view)
- * In multiple title mode, the show() callback can check the index it receives
- * in order to detect which of its titles has been clicked on by the user.
- *
- * @param {Object} item breadcrumb item
- * @param {Object} item.widget widget containing the view(s) to be added to the breadcrumb added
- * @param {Function} [item.show] triggered whenever the widget should be shown back
- * @param {Function} [item.hide] triggered whenever the widget should be shown hidden
- * @param {Function} [item.destroy] triggered whenever the widget should be destroyed
- * @param {String|Array} [item.title] title(s) of the view(s) to be displayed in the breadcrumb
- * @param {Function} [item.get_title] should return the title(s) of the view(s) to be displayed in the breadcrumb
+ * widget: typically, widgets added are instance.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_view_manager: function(widget, action, clear_breadcrumbs) {
+ push_widget: function(widget, action, options) {
var self = this,
+ to_destroy,
+ options = options || {},
old_widget = this.inner_widget;
- if (clear_breadcrumbs) {
- var to_destroy = this.view_managers;
- this.view_managers = [];
+ 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);
}
- if (widget instanceof instance.web.ViewManager) {
- this.view_managers.push(widget);
+ if (widget instanceof instance.web.Widget) {
+ var title = widget.get('title') || action.display_name || action.name;
+ widget.set('title', title);
+ this.widgets.push(widget);
} else {
- this.view_managers.push({
+ this.widgets.push({
view_stack: [{
controller: {get: function () {return action.display_name || action.name; }},
}],
destroy: function () {},
});
}
+ _.last(this.widgets).__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 () {
(action.target !== 'inline') && (!action.flags.headless) && widget.$header && widget.$header.show();
old_widget && old_widget.$el.hide();
- if (clear_breadcrumbs) {
- self.clear_view_managers(to_destroy)
+ if (options.clear_breadcrumbs) {
+ self.clear_widgets(to_destroy)
}
});
},
get_breadcrumbs: function () {
- return _.flatten(_.map(this.view_managers, function (vm) {
- return vm.view_stack.map(function (view, index) {
- return {
- title: view.controller.get('title') || vm.title,
- index: index,
- view_manager: vm,
- };
- });
+ return _.flatten(_.map(this.widgets, function (widget) {
+ if (widget instanceof instance.web.ViewManager) {
+ return widget.view_stack.map(function (view, index) {
+ return {
+ title: view.controller.get('title') || widget.title,
+ index: index,
+ widget: widget,
+ };
+ });
+ } else {
+ return {title: widget.get('title'), widget: widget };
+ }
}), true);
},
+ get_title: function () {
+ if (this.widgets.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 instance.web.ViewManager && widget.view_stack.length === 1) {
+ return widget.title;
+ }
+ }
+ return _.pluck(this.get_breadcrumbs(), 'title').join(' / ');
+ },
+ get_widgets: function () {
+ return this.widgets.slice(0);
+ },
history_back: function() {
- var view_manager = _.last(this.view_managers),
- nbr_views = view_manager.view_stack.length;
- if (nbr_views > 1) {
- this.select_view_manager(view_manager, nbr_views - 2);
- } else if (this.view_managers.length > 1) {
- view_manager = this.view_managers[this.view_managers.length -2];
- nbr_views = view_manager.view_stack.length;
- this.select_view(view_managers, nbr_views - 2)
+ var widget = _.last(this.widgets);
+ if (widget instanceof instance.web.ViewManager) {
+ var nbr_views = widget.view_stack.length;
+ if (nbr_views > 1) {
+ return this.select_widget(widget, 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;
+ this.select_widget(widget, index);
}
},
- select_view_manager: function(view_manager, index) {
+ select_widget: function(widget, index) {
var self = this;
if (this.webclient.has_uncommitted_changes()) {
return false;
}
- var vm_index = this.view_managers.indexOf(view_manager);
- if (view_manager.select_view) {
- view_manager.select_view(index).done(function () {
- _.each(self.view_managers.splice(vm_index + 1), function (vm) {
- vm.destroy();
- });
- self.inner_widget = _.last(self.view_managers);
- self.inner_widget.display_breadcrumbs();
- self.inner_widget.$el.show();
+ var widget_index = this.widgets.indexOf(widget),
+ def = $.when(widget.select_view && widget.select_view(index));
+
+ def.done(function () {
+ if (widget.__on_reverse_breadcrumb) {
+ widget.__on_reverse_breadcrumb();
+ }
+ _.each(self.widgets.splice(widget_index + 1), function (w) {
+ w.destroy();
});
- }
- },
- clear_view_managers: function(vms) {
- _.each(vms || this.view_managers, function (vm) {
- vm.destroy();
+ self.inner_widget = _.last(self.widgets);
+ self.inner_widget.display_breadcrumbs && self.inner_widget.display_breadcrumbs();
+ self.inner_widget.do_show && self.inner_widget.do_show();
});
- if (!vms) {
- this.view_managers = [];
- this.inner_widget = null;
+ },
+ clear_widgets: function(widgets) {
+ _.invoke(widgets || this.widgets, 'destroy');
+ if (!widgets) {
+ this.widgets = [];
+ this.inner_widget = null;
}
},
do_push_state: function(state) {
// this action has been explicitly marked as not pushable
return;
}
- state.title = this.inner_action.name;
+ state.title = this.get_title();
if(this.inner_action.type == 'ir.actions.act_window') {
state.model = this.inner_action.res_model;
}
* @param {Number|String|Object} Can be either an action id, a client action or an action descriptor.
* @param {Object} [options]
* @param {Boolean} [options.clear_breadcrumbs=false] Clear the breadcrumbs history list
+ * @param {Boolean} [options.replace_breadcrumb=false] Replace the current breadcrumb with the action
* @param {Function} [options.on_reverse_breadcrumb] Callback to be executed whenever an anterior breadcrumb item is clicked on.
* @param {Function} [options.hide_breadcrumb] Do not display this widget's title in the breadcrumb
* @param {Function} [options.on_close] Callback to be executed when the dialog is closed (only relevant for target=new actions)
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 && !inline,
+ pager : (!popup || !form) && !inline,
display_title : !popup,
search_disable_custom_filters: action.context && action.context.search_disable_custom_filters
});
},
null_action: function() {
this.dialog_stop();
- this.clear_view_managers();
+ this.clear_widgets();
},
/**
*
this.dialog_widget.setParent(this.dialog);
var initialized = this.dialog_widget.appendTo(this.dialog.$el);
this.dialog.open();
- return initialized;
+ return $.when(initialized);
}
if (this.inner_widget && this.webclient.has_uncommitted_changes()) {
return $.Deferred().reject();
}
widget = executor.widget();
this.dialog_stop(executor.action);
- return this.push_view_manager(widget, executor.action, options.clear_breadcrumbs);
+ return this.push_widget(widget, executor.action, options);
},
ir_actions_act_window: function (action, options) {
var self = this;
if (!view) {
return $.Deferred().reject();
}
- if (view_type !== 'form') {
+ if ((view_type !== 'form') && (view_type !== 'diagram')) {
this.view_stack = [];
}
var self = this;
if (!this.action_manager) return;
var breadcrumbs = this.action_manager.get_breadcrumbs();
+ if (!breadcrumbs.length) return;
var $breadcrumbs = _.map(_.initial(breadcrumbs), function (bc) {
var $link = $('<a>').text(bc.title);
$link.click(function () {
- self.action_manager.select_view_manager(bc.view_manager, bc.index);
+ self.action_manager.select_widget(bc.widget, bc.index);
});
return $('<li>').append($link);
});
controller.on('view_loaded', this, function () {
view_loaded.resolve();
});
+ this.$('.oe-view-manager-pager > span').hide();
return $.when(controller.appendTo($container), view_loaded)
.done(function () {
self.trigger("controller_inited", view.type, controller);
return this.switch_mode(view_type);
},
/**
+ * @returns {Number|Boolean} the view id of the given type, false if not found
+ */
+ get_view_id: function(view_type) {
+ return this.views[view_type] && this.views[view_type].view_id || false;
+ },
+ /**
* Sets up the current viewmanager's search view.
*
* @param {Number|false} view_id the view to use or false for a default one
hidden: this.flags.search_view === false,
disable_custom_filters: this.flags.search_disable_custom_filters,
$buttons: this.$('.oe-search-options'),
+ action: this.action,
};
var SearchView = instance.web.SearchView;
this.searchview = new SearchView(this, this.dataset, view_id, search_defaults, options);
}
},
do_load_state: function(state, warm) {
- var self = this,
- def = this.active_view.created;
if (state.view_type && state.view_type !== this.active_view.type) {
- def = def.then(function() {
- return self.switch_mode(state.view_type, true);
- });
+ // warning: this code relies on the fact that switch_mode has an immediate side
+ // effect (setting the 'active_view' to its new value) AND an async effect (the
+ // view is created/loaded). So, the next statement (do_load_state) is executed
+ // on the new view, after it was initialized, but before it is fully loaded and
+ // in particular, before the do_show method is called.
+ this.switch_mode(state.view_type, true);
}
- def.done(function() {
- self.active_view.controller.do_load_state(state, warm);
- });
+ this.active_view.controller.do_load_state(state, warm);
},
on_debug_changed: function (evt) {
var self = this,
var dialog = new instance.web.Dialog(this, {
title: _.str.sprintf(_t("Metadata (%s)"), self.dataset.model),
size: 'medium',
+ buttons: {
+ Ok: function() { this.parents('.modal').modal('hide');}
+ },
}, QWeb.render('ViewManagerDebugViewLog', {
perm : result[0],
format : instance.web.format_value
new instance.web.Dialog(self, {
title: _.str.sprintf(_t("Model %s fields"),
self.dataset.model),
+ buttons: {
+ Ok: function() { this.parents('.modal').modal('hide');}
+ },
}, $root).open();
});
break;
data: {action: JSON.stringify(action)},
complete: instance.web.unblockUI
});
+ } else {
+ self.do_warn("Warning", "No record selected.");
}
break;
case 'leave_debug':