diff --git a/src/model.js b/src/model.js index da6f6e7d68..d75ca42451 100644 --- a/src/model.js +++ b/src/model.js @@ -120,8 +120,7 @@ } }; array.new_ = function(default_, id) { - var record = new Sao.Record(this.name, id); - record.model = this.model; + var record = new Sao.Record(this.model, id); record.group = this; if (default_) { record.default_get(); @@ -159,26 +158,26 @@ var idx = this.indexOf(record); if (record.id >= 0) { if (remove) { - if (record in this.record_deleted) { + if (this.record_deleted.indexOf(record) != -1) { this.record_deleted.splice( this.record_deleted.indexOf(record), 1); } this.record_removed.push(record); } else { - if (record in this.record_removed) { + if (this.record_removed.indexOf(record) != -1) { this.record_removed.splice( this.record_removed.indexOf(record), 1); } this.record_deleted.push(record); } } - if (record.parent) { - record.parent._changed.id = true; + if (record.group.parent) { + record.group.parent._changed.id = true; } if (modified) { record._changed.id = true; } - if (!(record.parent) || (record.id < 0) || force_remove) { + if (!(record.group.parent) || (record.id < 0) || force_remove) { this._remove(record); } record.group.changed(); @@ -190,6 +189,14 @@ var idx = this.indexOf(record); this.splice(idx, 1); }; + array.unremove = function(record) { + this.record_removed.splice(this.record_removed.indexOf(record), 1); + this.record_deleted.splice(this.record_deleted.indexOf(record), 1); + record.group.changed(); + record.group.root_group().screens.forEach(function(screen) { + screen.display(); + }); + }; array.changed = function() { if (!this.parent) { return; @@ -685,6 +692,12 @@ cancel: function() { this._loaded = {}; this._changed = {}; + }, + deleted: function() { + return this.group.record_deleted.indexOf(this) != -1; + }, + removed: function() { + return this.group.record_removed.indexOf(this) != -1; } }); @@ -782,8 +795,8 @@ }, get_context: function(record) { var context = jQuery.extend({}, record.get_context()); - if (record.parent) { - jQuery.extend(context, record.parent.get_context()); + if (record.group.parent) { + jQuery.extend(context, record.group.parent.get_context()); } // TODO eval context attribute return context; @@ -1302,7 +1315,7 @@ for (var i = 0, len = record._values[this.name].length; i < len; i++) { var record2 = group[i]; - if (!record2.deleted || !record2.removed) + if (!record2.deleted() || !record2.removed()) result.push(record2.get_on_change_value()); } return result; diff --git a/src/screen.js b/src/screen.js index 052a46b79e..93c9b017e5 100644 --- a/src/screen.js +++ b/src/screen.js @@ -5,6 +5,10 @@ Sao.ScreenContainer = Sao.class_(Object, { init: function(tab_domain) { + this.alternate_viewport = jQuery('
', { + 'class': 'screen-container' + }); + this.alternate_view = false; this.tab_domain = tab_domain || []; this.el = jQuery('', { 'class': 'screen-container' @@ -109,8 +113,14 @@ } }, set: function(widget) { - this.content_box.children().detach(); - this.content_box.append(widget); + if (this.alternate_view) { + this.alternate_viewport.children().detach(); + // TODO test if widget is content_box widget + this.alternate_viewport.append(widget); + } else { + this.content_box.children().detach(); + this.content_box.append(widget); + } } }); @@ -124,6 +134,7 @@ this.view_to_load = jQuery.extend([], attributes.mode || ['tree', 'form']); this.views = []; + this.exclude_field = attributes.exclude_field; this.context = attributes.context || {}; this.new_group(); this.current_view = null; @@ -143,9 +154,9 @@ this.domain_parser = null; }, load_next_view: function() { - if (this.view_to_load) { + if (!jQuery.isEmptyObject(this.view_to_load)) { var view_id; - if (this.view_ids) { + if (!jQuery.isEmptyObject(this.view_ids)) { view_id = this.view_ids.shift(); } var view_type = this.view_to_load.shift(); @@ -263,11 +274,13 @@ }, set_group: function(group) { if (this.group) { + jQuery.extend(group.model.fields, this.group.model.fields); this.group.screens.splice( this.group.screens.indexOf(this), 1); } group.screens.push(this); this.group = group; + this.model = group.model; if (jQuery.isEmptyObject(group)) { this.current_record = null; } else { @@ -285,11 +298,19 @@ if (this.views) { this.search_active(['tree', 'graph', 'calendar'].indexOf( this.current_view.view_type) > -1); - for (var i = 0; i < this.views.length; i++) - if (this.views[i]) + for (var i = 0; i < this.views.length; i++) { + if (this.views[i]) { this.views[i].display(); + } + } } }, + display_next: function() { + // TODO + }, + display_previous: function() { + // TODO + }, default_row_activate: function() { if ((this.current_view.view_type == 'tree') && this.current_view.keyword_open) { @@ -394,6 +415,12 @@ // TODO test view modified return false; }, + unremove: function() { + var records = this.current_view.selected_records(); + records.forEach(function(record) { + record.group.unremove(record); + }); + }, remove: function(delete_, remove, force_remove) { var result = jQuery.Deferred(); var records = null; @@ -418,14 +445,14 @@ var prms = []; if (delete_) { records.forEach(function(record) { - if (record.parent) { - prms.push(record.parent.save()); + if (record.group.parent) { + prms.push(record.group.parent.save()); } - if (record in record.group.record_deleted) { + if (record.group.record_deleted.indexOf(record) != -1) { record.group.record_deleted.splice( record.group.record_deleted.indexOf(record), 1); } - if (record in record.group.record_removed) { + if (record.group.record_removed.indexOf(record) != -1) { record.group.record_removed.splice( record.group.record_removed.indexOf(record), 1); } @@ -443,7 +470,7 @@ return result; }, search_active: function(active) { - if (active && !this.parent) { + if (active && !this.group.parent) { if (!this.fields_view_tree) { this.model.execute('fields_view_get', [false, 'tree'], this.context) diff --git a/src/view.js b/src/view.js index cf41649791..78739120b1 100644 --- a/src/view.js +++ b/src/view.js @@ -134,9 +134,12 @@ 'width': child.getAttribute('width'), 'orientation': child.getAttribute('orientation'), 'float_time': child.getAttribute('float_time'), - 'pre_validate': child.getAttribute('pre_validate') == 1, 'completion': child.getAttribute('completion') == 1 }; + if (name == this.screen.exclude_field) { + // TODO is it really the way to do it + return; + } if (attributes.widget === null) { attributes.widget = model.fields[name].description.type; } @@ -276,6 +279,11 @@ this.record.load(this.children_field).done( add_children.bind(this)); } + if (this.record.deleted() || this.record.removed()) { + this.el.css('text-decoration', 'line-through'); + } else { + this.el.css('text-decoration', 'inherit'); + } }, toggle_row: function() { if (this.is_expanded()) { @@ -508,7 +516,8 @@ 'name': child.getAttribute('name'), 'widget': child.getAttribute('widget'), 'string': child.getAttribute('string'), - 'states': child.getAttribute('states') + 'states': child.getAttribute('states'), + 'mode': child.getAttribute('mode') }; ['readonly', 'invisible'].forEach(function(name) { if (child.getAttribute(name)) { @@ -580,7 +589,11 @@ var name = attributes.name; var text = attributes.string; if (name in model.fields) { - // TODO exclude field + if (name == this.screen.exclude_field) { + container.add( + Number(node.getAttribute('colspan') || 1)); + return; + } if (!attributes.states && (name in model.fields)) { attributes.states = model.fields[name].description.states; } @@ -642,8 +655,7 @@ }, _parse_field: function(model, node, container, attributes) { var name = attributes.name; - // TODO exclude field - if (!(name in model.fields)) { + if (!(name in model.fields) || name == this.screen.exclude_field) { container.add( Number(node.getAttribute('colspan') || 1)); return; @@ -780,6 +792,12 @@ } else { this.screen.button(button.attributes); } + }, + selected_records: function() { + if (this.screen.current_record) { + return [this.screen.current_record]; + } + return []; } }); @@ -948,6 +966,10 @@ return Sao.View.Form.Text; case 'many2one': return Sao.View.Form.Many2One; + case 'one2many': + return Sao.View.Form.One2Many; + case 'many2many': + return Sao.View.Form.Many2Many; } }; @@ -1519,4 +1541,401 @@ } }); + Sao.View.Form.One2Many = Sao.class_(Sao.View.Form.Widget, { + class_: 'form-one2many', + init: function(field_name, model, attributes) { + Sao.View.Form.One2Many._super.init.call(this, field_name, model, + attributes); + + this.el = jQuery('', { + 'class': this.class_ + }); + this.menu = jQuery('', { + 'class': this.class_ + '-menu' + }); + this.el.append(this.menu); + + if (attributes.add_remove) { + this.wid_text = jQuery('', { + type: 'input' + }); + // TODO add completion + + this.but_add = jQuery('').button({ + icons: { + primary: 'ui-icon-plus' + }, + label: 'Add' + }); + this.but_add.click(this.add.bind(this)); + this.menu.append(this.but_add); + + this.but_remove = jQuery('').button({ + icons: { + primary: 'ui-icon-minus' + }, + label: 'Remove' + }); + this.but_remove.click(this.remove.bind(this)); + this.menu.append(this.but_remove); + } + + this.but_new = jQuery('').button({ + icons: { + primary: 'ui-icon-document' + }, + label: 'New' + }); + this.but_new.click(this.new_.bind(this)); + this.menu.append(this.but_new); + + this.but_open = jQuery('').button({ + icons: { + primary: 'ui-icon-folder-open' + }, + label: 'Open' + }); + this.but_open.click(this.open.bind(this)); + this.menu.append(this.but_open); + + this.but_del = jQuery('').button({ + icons: { + primary: 'ui-icon-trash' + }, + label: 'Delete' + }); + this.but_del.click(this.delete_.bind(this)); + this.menu.append(this.but_del); + + this.but_undel = jQuery('').button({ + icons: { + primary: 'ui-icon-arrowreturn-1-s' + }, + label: 'Undelete' + }); + this.but_undel.click(this.undelete.bind(this)); + this.menu.append(this.but_undel); + + this.but_previous = jQuery('').button({ + icons: { + primary: 'ui-icon-arrowthick-1-w' + }, + label: 'Previous' + }); + this.but_previous.click(this.previous.bind(this)); + this.menu.append(this.but_previous); + + this.label = jQuery('', { + 'class': this.class_ + '-label' + }); + this.label.text('(0, 0)'); + this.menu.append(this.label); + + this.but_next = jQuery('').button({ + icons: { + primary: 'ui-icon-arrowthick-1-e' + }, + label: 'Next' + }); + this.but_next.click(this.next.bind(this)); + this.menu.append(this.but_next); + + this.but_switch = jQuery('').button({ + icons: { + primary: 'ui-icon-newwin' + }, + label: 'Switch' + }); + this.but_switch.click(this.switch_.bind(this)); + this.menu.append(this.but_switch); + + this.content = jQuery('', { + 'class': this.class_ + '-content' + }); + this.el.append(this.content); + + var modes = (attributes.mode || 'tree,form').split(','); + this.screen = new Sao.Screen(attributes.relation, { + mode: modes, + view_ids: (attributes.view_ids || '').split(','), + views_preload: attributes.views || {}, + row_activate: this.activate.bind(this), + exclude_field: attributes.relation_field || null + }); + this.prm = this.screen.switch_view(modes[0]).done(function() { + this.content.append(this.screen.screen_container.el); + }.bind(this)); + // TODO sensitivity of buttons + }, + display: function(record, field) { + Sao.View.Form.One2Many._super.display.call(this, record, field); + + this.prm.done(function() { + if (!record) { + return; + } + if (field === undefined) { + this.screen.new_group(); + this.screen.current_record = null; + this.screen.parent = true; + this.screen.display(); + return; + } + + var new_group = record.field_get_client(this.field_name); + if (new_group != this.screen.group) { + this.screen.set_group(new_group); + // TODO handle editable tree + // TODO set readonly, domain, size_limit + } + this.screen.display(); + }.bind(this)); + }, + activate: function(event_) { + this.edit(); + }, + add: function(event_) { + }, + remove: function(event_) { + // TODO model access + this.screen.remove(false, true, false); + }, + new_: function(event_) { + // TODO model access + if (!this.validate()) { + return; + } + var context = jQuery.extend({}, + this.field().get_context(this.screen.current_record)); + // TODO sequence + if (this.screen.current_view.type == 'form' || + this.screen.current_view.editable) { + this.screen.new_(); + this.screen.current_view.el.prop('disabled', false); + } else { + var record = this.screen.current_record; + var field_size = record.expr_eval(this.attributes.size) || -1; + field_size -= this.field().get_eval(record); + var win = new Sao.Window.Form(this.screen, function() {}, { + new_: true, + many: field_size, + context: context + }); + } + }, + open: function(event_) { + this.edit(); + }, + delete_: function(event_) { + // TODO model access + this.screen.remove(false, false, false); + }, + undelete: function(event_) { + this.screen.unremove(); + }, + previous: function(event_) { + if (!this.validate()) { + return; + } + this.screen.display_previous(); + }, + next: function(event_) { + if (!this.validate()) { + return; + } + this.screen.display_next(); + }, + switch_: function(event_) { + this.screen.switch_view(); + // TODO color_set + }, + edit: function() { + // TODO model access + if (!this.validate()) { + return; + } + var record = this.screen.current_record; + if (record) { + var win = new Sao.Window.Form(this.screen, function() {}); + } + }, + validate: function() { + this.view.set_value(); + var record = this.screen.current_record; + if (record) { + var fields = this.screen.current_view.get_fields(); + if (!record.validate(fields)) { + this.screen.display(); + return false; + } + // TODO pre-validate + } + return true; + } + }); + + Sao.View.Form.Many2Many = Sao.class_(Sao.View.Form.Widget, { + class_: 'form-many2many', + init: function(field_name, model, attributes) { + Sao.View.Form.Many2Many._super.init.call(this, field_name, model, + attributes); + this.el = jQuery('', { + 'class': this.class_ + }); + this.menu = jQuery('', { + 'class': this.class_ + '-menu' + }); + this.el.append(this.menu); + + var label = jQuery('', { + text: attributes.string + }); + this.menu.append(label); + + this.entry = jQuery('', { + type: 'input' + }); + this.entry.on('keyup', this.key_press.bind(this)); + this.menu.append(this.entry); + + // TODO completion + + this.but_add = jQuery('').button({ + icons: { + primary: 'ui-icon-plus' + }, + text: false + }); + this.but_add.click(this.add.bind(this)); + this.menu.append(this.but_add); + + this.but_remove = jQuery('').button({ + icons: { + primary: 'ui-icon-minus' + }, + text: false + }); + this.but_remove.click(this.remove.bind(this)); + this.menu.append(this.but_remove); + + this.content = jQuery('', { + 'class': this.class_ + '-content' + }); + this.el.append(this.content); + + this.screen = new Sao.Screen(attributes.relation, { + mode: ['tree'], + view_ids: (attributes.view_ids || '').split(','), + views_preload: attributes.views || {}, + row_activate: this.activate.bind(this) + }); + this.prm = this.screen.switch_view('tree').done(function() { + this.content.append(this.screen.screen_container.el); + }.bind(this)); + }, + display: function(record, field) { + Sao.View.Form.Many2Many._super.display.call(this, record, field); + + this.prm.done(function() { + if (!record) { + return; + } + if (field === undefined) { + this.screen.new_group(); + this.screen.current_record = null; + this.screen.parent = true; + this.screen.display(); + return; + } + var new_group = record.field_get_client(this.field_name); + if (new_group != this.screen.group) { + this.screen.set_group(new_group); + } + this.screen.display(); + }.bind(this)); + }, + activate: function() { + this.edit(); + }, + add: function() { + var dom; + var domain = this.field().get_domain(this.record()); + var context = this.field().get_context(this.record()); + var value = this.entry.val(); + if (value) { + dom = [['rec_name', 'ilike', '%' + value + '%']].concat(domain); + } else { + dom = domain; + } + + var callback = function(result) { + if (!jQuery.isEmptyObject(result)) { + var ids = []; + var i, len; + for (i = 0, len = result.length; i < len; i++) { + ids.push(result[i][0]); + } + this.screen.group.load(ids, true); + this.screen.display(); + } + this.entry.val(''); + }.bind(this); + var model = new Sao.Model(this.attributes.relation); + var ids_prm = model.execute('search', + [dom, 0, Sao.config.limit, null], context); + ids_prm.done(function(ids) { + if (ids.length != 1) { + var win = new Sao.Window.Search(this.attributes.relation, + callback, { + sel_multi: true, + ids: ids, + context: context, + domain: domain, + view_ids: (this.attributes.view_ids || + '').split(','), + views_preload: this.attributes.views || {}, + new_: this.attributes.create + }); + } else { + callback([[ids[0], null]]); + } + }.bind(this)); + }, + remove: function() { + this.screen.remove(false, true, false); + }, + key_press: function(event_) { + var editable = true; // TODO compute editable + var activate_keys = [Sao.common.TAB_KEYCODE]; + if (!this.wid_completion) { + activate_keys.push(Sao.common.RETURN_KEYCODE); + } + + if (event_.which == Sao.common.F3_KEYCODE) { + this.add(); + event_.preventDefault(); + } else if ((jQuery.inArray(event_.which, activate_keys) > -1) && + editable) { + if (this.entry.val()) { + this.add(); + } + + } + }, + edit: function() { + if (this.screen.current_record) { + var callback = function(result) { + if (result) { + this.screen.current_record.save().done(function() { + this.screen.display(); + }.bind(this)); + } else { + this.screen.current_record.cancel(); + } + }; + var win = new Sao.Window.Form(this.screen, + callback.bind(this)); + } + } + }); }()); diff --git a/src/window.js b/src/window.js index e18c93d6a0..132767ea57 100644 --- a/src/window.js +++ b/src/window.js @@ -7,21 +7,27 @@ Sao.Window.Form = Sao.class_(Object, { init: function(screen, callback, kwargs) { + kwargs = kwargs || {}; this.screen = screen; + this.screen.screen_container.alternate_view = true; var view_type = kwargs.view_type || 'form'; + + var form_prm = jQuery.when(); var screen_views = []; - for (var i = 0, len = this.screen.views; i < len; i++) { + for (var i = 0, len = this.screen.views.length; i < len; i++) { screen_views.push(this.screen.views[i].view_type); } if (screen_views.indexOf(view_type) == -1 && this.screen.view_to_load.indexOf(view_type) == -1) { - this.screen.add_view_id(null, view_type); + form_prm = this.screen.add_view_id(null, view_type); } - var switch_prm = this.screen.switch_view(view_type); - switch_prm.done(function() { - if (kwargs.new_) { - this.screen.new_(); - } + + var switch_prm = form_prm.then(function() { + return this.screen.switch_view(view_type).done(function() { + if (kwargs.new_) { + this.screen.new_(); + } + }.bind(this)); }.bind(this)); this.many = kwargs.many || 0; this.domain = kwargs.domain || null; @@ -65,7 +71,7 @@ buttons: buttons, title: '' // this.screen.current_view }); - this.el.append(this.screen.screen_container.content_box); + this.el.append(this.screen.screen_container.alternate_viewport); this.el.dialog('open'); this.screen.display(); }.bind(this)); @@ -126,13 +132,20 @@ closing_prm.done(function() { this.callback(result); - this.el.dialog('destroy'); + this.destroy(); }.bind(this)); + }, + destroy: function() { + this.screen.screen_container.alternate_view = false; + this.screen.screen_container.alternate_viewport.children() + .detach(); + this.el.dialog('destroy'); } }); Sao.Window.Search = Sao.class_(Object, { init: function(model, callback, kwargs) { + kwargs = kwargs || {}; var views_preload = kwargs.views_preload || {}; this.model_name = model; this.domain = kwargs.domain || [];