diff --git a/addons/web/static/src/css/base.css b/addons/web/static/src/css/base.css index 00d80d6085b..05c0a9b68b8 100644 --- a/addons/web/static/src/css/base.css +++ b/addons/web/static/src/css/base.css @@ -240,6 +240,9 @@ .openerp.ui-dialog .ui-dialog-buttonpane button { margin-left: 8px; } +.openerp.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset { + float: left; +} .openerp.ui-dialog .ui-dialog-titlebar-close { padding: 0; } @@ -389,7 +392,7 @@ .openerp .oe_avatar { margin: 0 16px 0 0; } -.openerp .oe_avatar img { +.openerp .oe_avatar > img { height: 50px; -moz-border-radius: 3px; -webkit-border-radius: 3px; @@ -444,6 +447,32 @@ .openerp .oe_webclient .oe_star_on { color: gold; } +.openerp .oe_tags .text-wrap { + width: 100% !important; +} +.openerp .oe_tags .text-wrap textarea { + width: 100% !important; +} +.openerp .oe_tags .oe_tag { + border-radius: 2px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + -ms-box-sizing: border-box; + box-sizing: border-box; + position: relative; + float: left; + border: 1px solid #9daccc; + background: #e2e6f0; + color: black; + padding: 0px 3px 0px 3px; + margin: 0 2px 2px 0; + height: 16px; + font: 11px "lucida grande", tahoma, verdana, arial, sans-serif; +} +.openerp .oe_tags .text-core .text-wrap .text-dropdown .text-list .text-suggestion em { + font-style: italic; + text-decoration: none; +} .openerp.oe_tooltip { font-size: 12px; } @@ -1759,7 +1788,7 @@ display: inline-block; float: right; } -.openerp .oe_form footer { +.openerp .oe_form div.oe_chatter { min-width: 650px; max-width: 860px; margin: 0 auto; @@ -2011,32 +2040,6 @@ padding-top: 4px; width: auto; } -.openerp .oe_form .oe_form_field_many2manytags .text-wrap { - width: 100% !important; -} -.openerp .oe_form .oe_form_field_many2manytags .text-wrap textarea { - width: 100% !important; -} -.openerp .oe_form .oe_form_field_many2manytags .oe_form_field_many2manytags_box { - border-radius: 2px; - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - -ms-box-sizing: border-box; - box-sizing: border-box; - position: relative; - float: left; - border: 1px solid #9daccc; - background: #e2e6f0; - color: black; - padding: 0px 3px 0px 3px; - margin: 0 2px 2px 0; - height: 16px; - font: 11px "lucida grande", tahoma, verdana, arial, sans-serif; -} -.openerp .oe_form .oe_form_field_many2manytags .text-core .text-wrap .text-dropdown .text-list .text-suggestion em { - font-style: italic; - text-decoration: none; -} .openerp .oe_form .oe_datepicker_container { display: none; } @@ -2119,6 +2122,7 @@ } .openerp .oe_form .oe_form_field_image .oe_form_field_image_controls { position: absolute; + white-space: nowrap; top: 1px; padding: 3px 0 0 0; margin: 0 1px; diff --git a/addons/web/static/src/css/base.sass b/addons/web/static/src/css/base.sass index b7d866245f1..f6e3e9a6ea2 100644 --- a/addons/web/static/src/css/base.sass +++ b/addons/web/static/src/css/base.sass @@ -235,6 +235,8 @@ $sheet-max-width: 860px @include radius(0 0 2px 2px) button margin-left: 8px + .ui-dialog-buttonset + float: left .ui-dialog-titlebar-close padding: 0 .ui-icon-closethick @@ -330,7 +332,7 @@ $sheet-max-width: 860px margin: 4px .oe_avatar margin: 0 16px 0 0 - img + > img height: 50px @include radius(3px) @include box-shadow(0 1px 3px rgba(0, 0, 0, 0.3)) @@ -356,8 +358,28 @@ $sheet-max-width: 860px text-decoration: none .oe_star_on color: gold - - //.oe_edit_only  + // }}} + // Tags (for many2many tags, among others) {{{ + .oe_tags + .text-wrap + width: 100% !important + textarea + width: 100% !important + .oe_tag + border-radius: 2px + @include box-sizing(border) + position: relative + float: left + border: 1px solid #9DACCC + background: #E2E6F0 + color: black + padding: 0px 3px 0px 3px + margin: 0 2px 2px 0 + height: 16px + font: 11px "lucida grande", tahoma, verdana, arial, sans-serif + .text-core .text-wrap .text-dropdown .text-list .text-suggestion em + font-style: italic + text-decoration: none // }}} // Tooltips {{{ &.oe_tooltip @@ -817,7 +839,7 @@ $sheet-max-width: 860px text-shadow: 0 1px 1px rgba(0,0,0,0.2) @include radius(4px) @include box-shadow(inset 0 1px 1px rgba(0, 0, 0, 0.2)) - .oe_menu_counter + .oe_menu_counter float: right background: #8a89ba color: #eee @@ -840,7 +862,7 @@ $sheet-max-width: 860px color: $colour4 text-shadow: 0 1px 1px white @include box-shadow(0 1px 1px rgba(0, 0, 0, 0.2)) - .oe_menu_counter + .oe_menu_counter background: #eee color: #8a89ba .oe_menu_toggler:before @@ -1306,7 +1328,7 @@ $sheet-max-width: 860px margin: 0 0 0 4px padding: 0 - + // }}} // Views Common {{{ .oe_view_nocontent @@ -1350,7 +1372,7 @@ $sheet-max-width: 860px display: none !important .oe_form .oe_form_field_date width: auto - .oe_form_nosheet + .oe_form_nosheet margin-left: 10px margin-right: 10px .oe_form_nosheet > header @@ -1358,17 +1380,17 @@ $sheet-max-width: 860px margin-right: -10px // }}} // FormView.custom tags and classes {{{ - .oe_form + .oe_form header position: relative border-bottom: 1px solid #cacaca @include vertical-gradient(#fcfcfc, #dedede) padding: 0 8px - line-height: 30px + line-height: 30px ul display: inline-block float: right - footer + div.oe_chatter min-width: 650px max-width: $sheet-max-width margin: 0 auto @@ -1381,12 +1403,12 @@ $sheet-max-width: 860px vertical-align: top margin-left: 8px li - border-right: none + border-right: none padding: 0 margin: 0 - float: left + float: left vertical-align: top - height: 30px + height: 30px padding: 0 0 0 12px &:first-child border-left: 1px solid #cacaca @@ -1422,7 +1444,7 @@ $sheet-max-width: 860px .oe_form_sheetbg background: url(/web/static/src/img/form_sheetbg.png) padding: 8px 0 - border-bottom: 1px solid #ddd + border-bottom: 1px solid #ddd .oe_form_sheet_width min-width: 650px max-width: $sheet-max-width @@ -1566,26 +1588,6 @@ $sheet-max-width: 860px .oe_form_field_boolean padding-top: 4px width: auto - .oe_form_field_many2manytags - .text-wrap - width: 100% !important - textarea - width: 100% !important - .oe_form_field_many2manytags_box - border-radius: 2px - @include box-sizing(border) - position: relative - float: left - border: 1px solid #9DACCC - background: #E2E6F0 - color: black - padding: 0px 3px 0px 3px - margin: 0 2px 2px 0 - height: 16px - font: 11px "lucida grande", tahoma, verdana, arial, sans-serif - .text-core .text-wrap .text-dropdown .text-list .text-suggestion em - font-style: italic - text-decoration: none .oe_datepicker_container display: none .oe_datepicker_root @@ -1663,6 +1665,7 @@ $sheet-max-width: 860px vertical-align: top .oe_form_field_image_controls position: absolute + white-space: nowrap top: 1px padding: 3px 0 0 0 margin: 0 1px diff --git a/addons/web/static/src/js/chrome.js b/addons/web/static/src/js/chrome.js index bd7ee28dd6e..e4d543c69ce 100644 --- a/addons/web/static/src/js/chrome.js +++ b/addons/web/static/src/js/chrome.js @@ -63,7 +63,7 @@ instance.web.Dialog = instance.web.Widget.extend({ this.dialog_options = { modal: true, destroy_on_close: true, - width: 580, + width: 700, min_width: 0, max_width: '95%', height: 'auto', @@ -86,7 +86,7 @@ instance.web.Dialog = instance.web.Widget.extend({ if (this.dialog_options.autoOpen) { this.open(); } else { - instance.web.dialog(this.$element, this.get_options()); + test = instance.web.dialog(this.$element, this.get_options()); } }, get_options: function(options) { @@ -145,9 +145,9 @@ instance.web.Dialog = instance.web.Widget.extend({ _.each(this.getChildren(), function(el) { el.destroy(); }); - if (! this.isDestroyed()) { - this.$element.dialog('destroy'); - } + if (! this.isDestroyed()) { + this.$element.dialog('destroy'); + } this._super(); } }); @@ -212,11 +212,11 @@ instance.web.CrashManager = instance.web.CallbackEnabled.extend({ dialog.$element.html(QWeb.render('CrashManager.error', {session: instance.connection, error: error})); }, on_javascript_exception: function(exception) { - this.on_traceback({ - type: _t("Client Error"), - message: exception, - data: {debug: ""} - }); + this.on_traceback({ + type: _t("Client Error"), + message: exception, + data: {debug: ""} + }); }, }); @@ -822,8 +822,8 @@ instance.web.UserMenu = instance.web.Widget.extend({ {text: _t("Change password"), click: function(){ self.change_password(); }}, {text: _t("Cancel"), click: function(){ $(this).dialog('destroy'); }}, {text: _t("Save"), click: function(){ - var inner_viewmanager = action_manager.inner_viewmanager; - inner_viewmanager.views[inner_viewmanager.active_view].controller.do_save() + var inner_widget = action_manager.inner_widget; + inner_widget.views[inner_widget.active_view].controller.do_save() .then(function() { self.dialog.destroy(); // needs to refresh interface in case language changed @@ -1037,6 +1037,7 @@ instance.web.WebClient = instance.web.Widget.extend({ if (options.needaction) { action.context.search_default_needaction_pending = true; } + self.action_manager.clear_breadcrumbs(); self.action_manager.do_action(action); }); }, diff --git a/addons/web/static/src/js/view_editor.js b/addons/web/static/src/js/view_editor.js index 8733a191b21..6df78679ecb 100644 --- a/addons/web/static/src/js/view_editor.js +++ b/addons/web/static/src/js/view_editor.js @@ -53,7 +53,7 @@ instance.web.ViewEditor = instance.web.OldWidget.extend({ this.main_view_id = this.parent.fields_view.view_id; this.action_manager = new instance.web.ActionManager(this); $.when(this.action_manager.do_action(action)).then(function() { - var viewmanager = self.action_manager.inner_viewmanager, + var viewmanager = self.action_manager.inner_widget, controller = viewmanager.views[viewmanager.active_view].controller; self.action_manager.appendTo(self.view_edit_dialog.$element); self.action_manager.renderElement(self.view_edit_dialog); @@ -88,7 +88,7 @@ instance.web.ViewEditor = instance.web.OldWidget.extend({ } else { $.when(self.do_save_view(view_values)).then(function() { self.create_view_dialog.close(); - var controller = self.action_manager.inner_viewmanager.views[self.action_manager.inner_viewmanager.active_view].controller; + var controller = self.action_manager.inner_widget.views[self.action_manager.inner_widget.active_view].controller; controller.reload_content(); }); } @@ -167,7 +167,7 @@ instance.web.ViewEditor = instance.web.OldWidget.extend({ do_delete_view: function() { var self = this; if (confirm(_t("Do you really want to remove this view?"))) { - var controller = this.action_manager.inner_viewmanager.views[this.action_manager.inner_viewmanager.active_view].controller; + var controller = this.action_manager.inner_widget.views[this.action_manager.inner_widget.active_view].controller; this.dataset.unlink([this.main_view_id]).then(function() { controller.reload_content(); self.main_view_id = self.parent.fields_view.view_id; @@ -397,7 +397,7 @@ instance.web.ViewEditor = instance.web.OldWidget.extend({ action_manager.do_action(action); }}, {text: _t("Close"), click: function(){ - self.action_manager.inner_viewmanager.views[self.action_manager.inner_viewmanager.active_view].controller.reload_content(); + self.action_manager.inner_widget.views[self.action_manager.inner_widget.active_view].controller.reload_content(); self.edit_xml_dialog.close(); }} ] @@ -998,7 +998,7 @@ instance.web.ViewEditor = instance.web.OldWidget.extend({ }; var action_manager = new instance.web.ActionManager(self); $.when(action_manager.do_action(action)).then(function() { - var controller = action_manager.dialog_viewmanager.views['form'].controller; + var controller = action_manager.dialog_widget.views['form'].controller; controller.on_button_cancel.add_last(function(){ action_manager.destroy() }); diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index d629b8e9b3d..3a3c49b2375 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -9,7 +9,7 @@ instance.web.form = {}; /** * Interface implemented by the form view or any other object * able to provide the features necessary for the fields to work. - * + * * Properties: * - display_invalid_fields : if true, all fields where is_valid() return true should * be displayed as invalid. @@ -287,10 +287,12 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM on_record_loaded: function(record) { var self = this, set_values = []; if (!record) { + this.set({ 'title' : undefined }); this.do_warn("Form", "The record could not be found in the database.", true); return $.Deferred().reject(); } this.datarecord = record; + this.set({ 'title' : record.id ? record.name : "New record" }); if (this.qweb) { this.kill_current_form(); @@ -524,7 +526,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM this.on_form_changed(); } if (!_.isEmpty(result.warning)) { - instance.web.dialog($(QWeb.render("CrashManager.warning", result.warning)), { + instance.web.dialog($(QWeb.render("CrashManager.warning", result.warning)), { title:result.warning.title, modal: true, buttons: [ @@ -953,7 +955,7 @@ instance.web.form.FormRenderingEngineInterface = instance.web.Class.extend({ /** * Default rendering engine for the form view. - * + * * It is necessary to set the view using set_view() before usage. */ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInterface.extend({ @@ -1276,7 +1278,7 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt page_attrs.__page = $new_page; page_attrs.__ic = ic; pages.push(page_attrs); - + $new_page.children().each(function() { self.process($(this)); }); @@ -1302,7 +1304,7 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt } }); }); - + this.handle_common_properties($new_notebook, $notebook); return $new_notebook; }, @@ -1603,7 +1605,7 @@ instance.web.form.FormWidget = instance.web.Widget.extend(instance.web.form.Invi if (! v_context) { v_context = (this.field || {}).context || {}; } - + if (v_context.__ref || true) { //TODO: remove true var fields_values = this._build_eval_context(blacklist); v_context = new instance.web.CompoundContext(v_context).set_eval_context(fields_values); @@ -1711,14 +1713,14 @@ instance.web.form.WidgetButton = instance.web.form.FormWidget.extend({ /** * Interface to be implemented by fields. - * + * * Properties: * - readonly: boolean. If set to true the field should appear in readonly mode. * - force_readonly: boolean, When it is true, the field should always appear * in read only mode, no matter what the value of the "readonly" property can be. * Events: * - changed_value: triggered to inform the view to check on_changes - * + * */ instance.web.form.FieldInterface = { /** @@ -1729,14 +1731,14 @@ instance.web.form.FieldInterface = { init: function(field_manager, node) {}, /** * Called by the form view to indicate the value of the field. - * + * * set_value() may return an object that can be passed to $.when() that represents the moment when * the field has finished all operations necessary before the user can effectively use the widget. - * + * * Multiple calls to set_value() can occur at any time and must be handled correctly by the implementation, * regardless of any asynchronous operation currently running and the status of any promise that a * previous call to set_value() could have returned. - * + * * set_value() must be able, at any moment, to handle the syntax returned by the "read" method of the * osv class in the OpenERP server as well as the syntax used by the set_value() (see below). It must * also be able to handle any other format commonly used in the _defaults key on the models in the addons @@ -1746,16 +1748,16 @@ instance.web.form.FieldInterface = { set_value: function(value_) {}, /** * Get the current value of the widget. - * + * * Must always return a syntaxically correct value to be passed to the "write" method of the osv class in * the OpenERP server, although it is not assumed to respect the constraints applied to the field. * For example if the field is marqued as "required", a call to get_value() can return false. - * + * * get_value() can also be called *before* a call to set_value() and, in that case, is supposed to * return a defaut value according to the type of field. - * + * * This method is always assumed to perform synchronously, it can not return a promise. - * + * * If there was no user interaction to modify the value of the field, it is always assumed that * get_value() return the same semantic value than the one passed in the last call to set_value(), * altough the syntax can be different. This can be the case for type of fields that have a different @@ -1785,14 +1787,14 @@ instance.web.form.FieldInterface = { /** * Abstract class for classes implementing FieldInterface. - * + * * Properties: * - effective_readonly: when it is true, the widget is displayed as readonly. Vary depending * the values of the "readonly" property and the "force_readonly" property on the field manager. * - value: useful property to hold the value of the field. By default, set_value() and get_value() * set and retrieve the value property. Changing the value property also triggers automatically * a 'changed_value' event that inform the view to trigger on_changes. - * + * */ instance.web.form.AbstractField = instance.web.form.FormWidget.extend(instance.web.form.FieldInterface, { /** @@ -1811,7 +1813,7 @@ instance.web.form.AbstractField = instance.web.form.FormWidget.extend(instance.w this.string = this.node.attrs.string || this.field.string || this.name; this.set({'value': false}); this.set({required: this.modifiers['required'] === true}); - + // some events to make the property "effective_readonly" sync automatically with "readonly" and // "force_readonly" this.set({"readonly": this.modifiers['readonly'] === true}); @@ -1821,7 +1823,7 @@ instance.web.form.AbstractField = instance.web.form.FormWidget.extend(instance.w this.on("change:readonly", this, test_effective_readonly); this.on("change:force_readonly", this, test_effective_readonly); _.bind(test_effective_readonly, this)(); - + this.on("change:value", this, function() { if (! this._inhibit_on_change) this.trigger('changed_value'); @@ -2003,7 +2005,7 @@ instance.web.form.FieldChar = instance.web.form.AbstractField.extend(instance.we }); instance.web.form.FieldID = instance.web.form.FieldChar.extend({ - + }); instance.web.form.FieldEmail = instance.web.form.FieldChar.extend({ @@ -2396,7 +2398,7 @@ instance.web.form.FieldSelection = instance.web.form.AbstractField.extend(instan } else { var self = this; var option = _(this.values) - .detect(function (record) { return record[0] === self.get('value'); }); + .detect(function (record) { return record[0] === self.get('value'); }); this.$element.text(option ? option[1] : this.values[0][1]); } }, @@ -2575,7 +2577,7 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc render_editable: function() { var self = this; this.$input = this.$element.find("input"); - + self.$input.tipsy({ title: function() { return "No element was selected, you should create or select one from the dropdown list."; @@ -2583,10 +2585,10 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc trigger:'manual', fade: true, }); - + this.$drop_down = this.$element.find(".oe_m2o_drop_down_button"); this.$follow_button = $(".oe_m2o_cm_button", this.$element); - + this.$follow_button.click(function() { if (!self.get('value')) { self.focus(); @@ -2880,7 +2882,7 @@ instance.web.form.FieldOne2Many = instance.web.form.AbstractField.extend({ }, load_views: function() { var self = this; - + var modes = this.node.attrs.mode; modes = !!modes ? modes.split(",") : ["tree"]; var views = []; @@ -3462,7 +3464,7 @@ instance.web.form.FieldMany2ManyTags = instance.web.form.AbstractField.extend(in $("textarea", self.$element).css("padding-left", "3px"); self.tags.addTags(_.map(data, function(el) {return {name: el[1], id:el[0]};})); } else { - self.$element.html(QWeb.render("FieldMany2ManyTags.box", {elements: data})); + self.$element.html(QWeb.render("FieldMany2ManyTag", {elements: data})); } }; if (! self.get('values') || self.get('values').length > 0) { @@ -3634,7 +3636,7 @@ instance.web.form.FieldMany2ManyKanban = instance.web.form.AbstractField.extend( this.dataset.on_unlink.add_last(function(ids) { self.dataset_changed(); }); - + this.is_setted.then(function() { self.load_view(); }); @@ -3750,7 +3752,7 @@ instance.web.form.Many2ManyKanbanView = instance.web_kanban.KanbanView.extend({ }); instance.web.form.Many2ManyQuickCreate = instance.web.Widget.extend({ template: 'Many2ManyKanban.quick_create', - + /** * close_btn: If true, the widget will display a "Close" button able to trigger * a "close" event. @@ -4443,7 +4445,7 @@ instance.web.form.FieldStatus = instance.web.form.AbstractField.extend({ this.selection = []; // get fold information from widget var fold = ((this.node.attrs || {}).statusbar_fold || true); - // build final domain: if fold option required, add the + // build final domain: if fold option required, add the if (fold == true) { var domain = new instance.web.CompoundDomain(['|'], ['&'], self.build_domain(), [['fold', '=', false]], [['id', '=', self.selected_value]]); } else { @@ -4475,7 +4477,7 @@ instance.web.form.FieldStatus = instance.web.form.AbstractField.extend({ var shown = _.map(((this.node.attrs || {}).statusbar_visible || "").split(","), function(x) { return _.str.trim(x); }); shown = _.select(shown, function(x) { return x.length > 0; }); - + if (shown.length == 0) { this.to_show = this.selection; } else { diff --git a/addons/web/static/src/js/views.js b/addons/web/static/src/js/views.js index 6586807264d..1bfd7d18776 100644 --- a/addons/web/static/src/js/views.js +++ b/addons/web/static/src/js/views.js @@ -10,28 +10,111 @@ instance.web.ActionManager = instance.web.Widget.extend({ init: function(parent) { this._super(parent); this.inner_action = null; - this.inner_viewmanager = null; + this.inner_widget = null; this.dialog = null; - this.dialog_viewmanager = null; - this.client_widget = null; + this.dialog_widget = null; + this.breadcrumbs = []; + }, + start: function() { + this._super.apply(this, arguments); + this.$element.on('click', '.oe_breadcrumb_item', this.on_breadcrumb_clicked); }, dialog_stop: function () { if (this.dialog) { - this.dialog_viewmanager.destroy(); - this.dialog_viewmanager = null; + this.dialog_widget.destroy(); + this.dialog_widget = null; this.dialog.destroy(); this.dialog = null; } }, - content_stop: function () { - if (this.inner_viewmanager) { - this.inner_viewmanager.destroy(); - this.inner_viewmanager = null; + /** + * Add a new item to the breadcrumb + * + * 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 + */ + push_breadcrumb: function(item) { + var last = this.breadcrumbs.slice(-1)[0]; + if (last) { + last.hide(); } - if (this.client_widget) { - this.client_widget.destroy(); - this.client_widget = null; + var item = _.extend({ + show: function(index) { + this.widget.$element.show(); + }, + hide: function() { + this.widget.$element.hide(); + }, + destroy: function() { + this.widget.destroy(); + }, + get_title: function() { + return this.title || this.widget.get('title'); + } + }, item); + item.id = _.uniqueId('breadcrumb_'); + this.breadcrumbs.push(item); + }, + on_breadcrumb_clicked: function(ev) { + var $e = $(ev.target); + var id = $e.data('id'); + var item; + for (var i = this.breadcrumbs.length - 1; i >= 0; i--) { + var it = this.breadcrumbs[i]; + if (it.id == id) { + item = it; + break; + } + this.remove_breadcrumb(i); } + var index = $e.parent().find('.oe_breadcrumb_item[data-id=' + $e.data('id') + ']').index($e); + item.show(index, $e); + this.inner_widget = item.widget; + }, + clear_breadcrumbs: function() { + while (this.breadcrumbs.length) { + this.remove_breadcrumb(0); + } + }, + remove_breadcrumb: function(index) { + var item = this.breadcrumbs.splice(index, 1)[0]; + if (item) { + var dups = _.filter(this.breadcrumbs, function(it) { + return item.widget === it.widget; + }); + if (!dups.length) { + item.destroy(); + } + } + }, + get_title: function() { + var titles = []; + for (var i = 0; i < this.breadcrumbs.length; i += 1) { + var item = this.breadcrumbs[i]; + var tit = item.get_title(); + if (!_.isArray(tit)) { + tit = [tit]; + } + for (var j = 0; j < tit.length; j += 1) { + var label = _.escape(tit[j]); + if (i === this.breadcrumbs.length - 1 && j === tit.length - 1) { + titles.push(label); + } else { + titles.push(_.str.sprintf('%s', item.id, label)); + } + } + } + return titles.join(' / '); }, do_push_state: function(state) { if (this.getParent() && this.getParent().do_push_state) { @@ -49,7 +132,7 @@ instance.web.ActionManager = instance.web.Widget.extend({ var self = this, action_loaded; if (state.action_id) { - var run_action = (!this.inner_viewmanager) || this.inner_viewmanager.action.id !== state.action_id; + var run_action = (!this.inner_widget || !this.inner_widget.action) || this.inner_widget.action.id !== state.action_id; if (run_action) { this.null_action(); action_loaded = this.do_action(state.action_id); @@ -89,8 +172,8 @@ instance.web.ActionManager = instance.web.Widget.extend({ } $.when(action_loaded || null).then(function() { - if (self.inner_viewmanager) { - self.inner_viewmanager.do_load_state(state, warm); + if (self.inner_widget && self.inner_widget.do_load_state) { + self.inner_widget.do_load_state(state, warm); } }); }, @@ -124,7 +207,7 @@ instance.web.ActionManager = instance.web.Widget.extend({ }, null_action: function() { this.dialog_stop(); - this.content_stop(); + this.clear_breadcrumbs(); }, ir_actions_act_window: function (action, on_close) { var self = this; @@ -137,29 +220,30 @@ instance.web.ActionManager = instance.web.Widget.extend({ } if (action.target === 'new') { if (this.dialog === null) { - this.dialog = new instance.web.Dialog(this, { width: '80%' }); + // These buttons will be overwrited by