Xavier (Open ERP) has proposed merging 
lp:~openerp-dev/openerp-web/trunk-readonly-formview-xmo into lp:openerp-web.

Requested reviews:
  Antony Lesuisse (OpenERP) (al-openerp)
  Fabien Meghazi (OpenERP) (fme)

For more details, see:
https://code.launchpad.net/~openerp-dev/openerp-web/trunk-readonly-formview-xmo/+merge/76206

Readonly & repositionable forms:

~Cosmetics
==========

Moved the template name of the form widgets to the class (from the instance), 
removed a bunch of now-redundant init methods.

Repositionable
==============
* Made widgets find their position in the rendered form template by looking up 
a class, not an id (since an id "can't" be present twice in the same document, 
and the widgets did a page-wise lookup which would always get their instance in 
the first form render)
* Made the form view memoize its template (if on_loaded is called again without 
any view data, the form just uses the previously computed template, of course 
this blows up if there is no such thing)
* Added a method letting callers "move" the form to a different root $element 
(caller still needs to call ``on_record_loaded`` afterwards to load a new 
record in the new root, see "BunchaForms" in web_tests addon)

"Repositionable readonly" mode renders 50 forms in ~300ms on my machine.

Note: because the same form is used everywhere and most events work form-wise, 
they're broken. I tried alleviating this by cloning the FormView (but keeping 
the generated widgets tree and pre-rendered template), does not work as widgets 
keep a reference to their FormView. This would be doable if it was possible to 
clone/rebind the widgets tree to a new view, but that would also be far, far 
more expensive (just cloning the TreeView and re-starting the widgets already 
added ~30% to execution time on 50 forms)

Readonly
========
* Added a second form registry inheriting from the first one
* This registry contains read-only version of some form widgets
* If the form has been created but not started yet, swapping to readonly only 
requires switching the registry on the form instance (see "BunchaForms" again)
* Otherwise need to cleanup some state, swap the registry and re-render the form
* Added a small switcher button, which just brute-forces the whole operation
-- 
https://code.launchpad.net/~openerp-dev/openerp-web/trunk-readonly-formview-xmo/+merge/76206
Your team OpenERP R&D Team is subscribed to branch 
lp:~openerp-dev/openerp-web/trunk-readonly-formview-xmo.
=== modified file 'addons/web/static/src/js/view_form.js'
--- addons/web/static/src/js/view_form.js	2011-09-20 10:08:23 +0000
+++ addons/web/static/src/js/view_form.js	2011-09-20 12:43:33 +0000
@@ -73,16 +73,23 @@
             w.stop();
         });
     },
+    reposition: function ($e) {
+        this.$element = $e;
+        this.on_loaded();
+    },
     on_loaded: function(data) {
         var self = this;
-        this.fields_view = data;
-        var frame = new (this.registry.get_object('frame'))(this, this.fields_view.arch);
+        if (data) {
+            this.fields_view = data;
+            var frame = new (this.registry.get_object('frame'))(this, this.fields_view.arch);
 
-        this.$element.html(QWeb.render(this.form_template, { 'frame': frame, 'view': this }));
+            this.rendered = QWeb.render(this.form_template, { 'frame': frame, 'view': this });
+        }
+        this.$element.html(this.rendered);
         _.each(this.widgets, function(w) {
             w.start();
         });
-        this.$form_header = this.$element.find('#' + this.element_id + '_header');
+        this.$form_header = this.$element.find('.oe_form_header');
         this.$form_header.find('div.oe_form_pager button[data-pager-action]').click(function() {
             var action = $(this).data('pager-action');
             self.on_pager_action(action);
@@ -93,6 +100,17 @@
         this.$form_header.find('button.oe_form_button_cancel').click(this.do_cancel);
         this.$form_header.find('button.oe_form_button_new').click(this.on_button_new);
         this.$form_header.find('button.oe_form_button_duplicate').click(this.on_button_duplicate);
+        this.$form_header.find('button.oe_form_button_toggle').click(function () {
+            self.translatable_fields = [];
+            self.widgets = {};
+            self.fields = {};
+            self.$form_header.find('button').unbind('click');
+            self.registry = self.registry === openerp.web.form.widgets
+                    ? openerp.web.form.readonly
+                    : openerp.web.form.widgets;
+            self.on_loaded(self.fields_view);
+            self.reload();
+        });
 
         if (this.options.sidebar && this.options.sidebar_id) {
             this.sidebar = new openerp.web.Sidebar(this, this.options.sidebar_id);
@@ -196,7 +214,7 @@
         }
     },
     do_update_pager: function(hide_index) {
-        var $pager = this.$element.find('#' + this.element_id + '_header div.oe_form_pager');
+        var $pager = this.$form_header.find('div.oe_form_pager');
         var index = hide_index ? '-' : this.dataset.index + 1;
         $pager.find('span.oe_pager_index').html(index);
         $pager.find('span.oe_pager_count').html(this.dataset.ids.length);
@@ -619,6 +637,7 @@
 
 openerp.web.form.Widget = openerp.web.Widget.extend(/** @lends openerp.web.form.Widget# */{
     template: 'Widget',
+    identifier_prefix: 'formview-widget-',
     /**
      * @constructs openerp.web.form.Widget
      * @extends openerp.web.Widget
@@ -632,11 +651,13 @@
         this.modifiers = JSON.parse(this.node.attrs.modifiers || '{}');
         this.type = this.type || node.tag;
         this.element_name = this.element_name || this.type;
-        this.element_id = [this.view.element_id, this.element_name, this.view.widgets_counter++].join("_");
-
-        this._super(view, this.element_id);
-
-        this.view.widgets[this.element_id] = this;
+        this.element_class = [
+            'formview', this.view.view_id, this.element_name,
+            this.view.widgets_counter++].join("_");
+
+        this._super(view);
+
+        this.view.widgets[this.element_class] = this;
         this.children = node.children;
         this.colspan = parseInt(node.attrs.colspan || 1, 10);
         this.decrease_max_width = 0;
@@ -649,7 +670,7 @@
         this.width = this.node.attrs.width;
     },
     start: function() {
-        this.$element = $('#' + this.element_id);
+        this.$element = this.view.$element.find('.' + this.element_class);
     },
     stop: function() {
         if (this.$element) {
@@ -758,13 +779,24 @@
         for (var i = 0; i < node.children.length; i++) {
             var n = node.children[i];
             if (n.tag == "page") {
-                var page = new openerp.web.form.WidgetNotebookPage(this.view, n, this, this.pages.length);
+                var page = new openerp.web.form.WidgetNotebookPage(
+                        this.view, n, this, this.pages.length);
                 this.pages.push(page);
             }
         }
     },
     start: function() {
+        var self = this;
         this._super.apply(this, arguments);
+        this.$element.find('> ul > li').each(function (index, tab_li) {
+            var page = self.pages[index],
+                id = _.uniqueId(self.element_name + '-');
+            page.element_id = id;
+            $(tab_li).find('a').attr('href', '#' + id);
+        });
+        this.$element.find('> div').each(function (index, page) {
+            page.id = self.pages[index].element_id;
+        });
         this.$element.tabs();
         this.view.on_button_new.add_last(this.do_select_first_visible_tab);
     },
@@ -786,11 +818,11 @@
         this.index = index;
         this.element_name = 'page_' + index;
         this._super(view, node);
-        this.element_tab_id = this.element_id + '_tab';
     },
     start: function() {
         this._super.apply(this, arguments);
-        this.$element_tab = $('#' + this.element_tab_id);
+        this.$element_tab = this.notebook.$element.find(
+                '> ul > li:eq(' + this.index + ')');
     },
     update_dom: function() {
         if (this.invisible && this.index === this.notebook.$element.tabs('option', 'selected')) {
@@ -802,9 +834,9 @@
 });
 
 openerp.web.form.WidgetSeparator = openerp.web.form.Widget.extend({
+    template: 'WidgetSeparator',
     init: function(view, node) {
         this._super(view, node);
-        this.template = "WidgetSeparator";
         this.orientation = node.attrs.orientation || 'horizontal';
         if (this.orientation === 'vertical') {
             this.width = '1';
@@ -814,9 +846,9 @@
 });
 
 openerp.web.form.WidgetButton = openerp.web.form.Widget.extend({
+    template: 'WidgetButton',
     init: function(view, node) {
         this._super(view, node);
-        this.template = "WidgetButton";
         if (this.string) {
             // We don't have button key bindings in the webclient
             this.string = this.string.replace(/_/g, '');
@@ -838,7 +870,7 @@
             });
         } else {
             if (this.node.attrs.confirm) {
-                var dialog = $('<div>' + this.node.attrs.confirm + '</div>').dialog({
+                $('<div>' + this.node.attrs.confirm + '</div>').dialog({
                     title: 'Confirm',
                     modal: true,
                     buttons: {
@@ -867,6 +899,7 @@
 });
 
 openerp.web.form.WidgetLabel = openerp.web.form.Widget.extend({
+    template: 'WidgetLabel',
     init: function(view, node) {
         this.element_name = 'label_' + node.attrs.name;
 
@@ -877,7 +910,6 @@
             this.template = "WidgetParagraph";
             this.colspan = parseInt(this.node.attrs.colspan || 1, 10);
         } else {
-            this.template = "WidgetLabel";
             this.colspan = 1;
             this.width = '1%';
             this.decrease_max_width = 1;
@@ -896,7 +928,7 @@
         var self = this;
         this.$element.find("label").dblclick(function() {
             var widget = self['for'] || self;
-            console.log(widget.element_id , widget);
+            console.log(widget.element_class , widget);
             window.w = widget;
         });
     }
@@ -1035,10 +1067,7 @@
 });
 
 openerp.web.form.FieldChar = openerp.web.form.Field.extend({
-    init: function(view, node) {
-        this._super(view, node);
-        this.template = "FieldChar";
-    },
+    template: 'FieldChar',
     start: function() {
         this._super.apply(this, arguments);
         this.$element.find('input').change(this.on_ui_change);
@@ -1047,6 +1076,7 @@
         this._super.apply(this, arguments);
         var show_value = openerp.web.format_value(value, this, '');
         this.$element.find('input').val(show_value);
+        return show_value;
     },
     update_dom: function() {
         this._super.apply(this, arguments);
@@ -1071,10 +1101,7 @@
 });
 
 openerp.web.form.FieldEmail = openerp.web.form.FieldChar.extend({
-    init: function(view, node) {
-        this._super(view, node);
-        this.template = "FieldEmail";
-    },
+    template: 'FieldEmail',
     start: function() {
         this._super.apply(this, arguments);
         this.$element.find('button').click(this.on_button_clicked);
@@ -1085,18 +1112,11 @@
         } else {
             location.href = 'mailto:' + this.value;
         }
-    },
-    set_value: function(value) {
-        this._super.apply(this, arguments);
-        this.$element.find('a').attr('href', 'mailto:' + this.$element.find('input').val());
     }
 });
 
 openerp.web.form.FieldUrl = openerp.web.form.FieldChar.extend({
-    init: function(view, node) {
-        this._super(view, node);
-        this.template = "FieldUrl";
-    },
+    template: 'FieldUrl',
     start: function() {
         this._super.apply(this, arguments);
         this.$element.find('button').click(this.on_button_clicked);
@@ -1122,9 +1142,9 @@
 });
 
 openerp.web.form.FieldDatetime = openerp.web.form.Field.extend({
+    template: 'FieldDate',
     init: function(view, node) {
         this._super(view, node);
-        this.template = "FieldDate";
         this.jqueryui_object = 'datetimepicker';
     },
     start: function() {
@@ -1215,10 +1235,7 @@
 });
 
 openerp.web.form.FieldText = openerp.web.form.Field.extend({
-    init: function(view, node) {
-        this._super(view, node);
-        this.template = "FieldText";
-    },
+    template: 'FieldText',
     start: function() {
         this._super.apply(this, arguments);
         this.$element.find('textarea').change(this.on_ui_change);
@@ -1251,10 +1268,7 @@
 });
 
 openerp.web.form.FieldBoolean = openerp.web.form.Field.extend({
-    init: function(view, node) {
-        this._super(view, node);
-        this.template = "FieldBoolean";
-    },
+    template: 'FieldBoolean',
     start: function() {
         var self = this;
         this._super.apply(this, arguments);
@@ -1285,10 +1299,7 @@
 });
 
 openerp.web.form.FieldProgressBar = openerp.web.form.Field.extend({
-    init: function(view, node) {
-        this._super(view, node);
-        this.template = "FieldProgressBar";
-    },
+    template: 'FieldProgressBar',
     start: function() {
         this._super.apply(this, arguments);
         this.$element.find('div').progressbar({
@@ -1311,10 +1322,10 @@
 });
 
 openerp.web.form.FieldSelection = openerp.web.form.Field.extend({
+    template: 'FieldSelection',
     init: function(view, node) {
         var self = this;
         this._super(view, node);
-        this.template = "FieldSelection";
         this.values = this.field.selection;
         _.each(this.values, function(v, i) {
             if (v[0] === false && v[1] === '') {
@@ -1421,9 +1432,9 @@
 };
 
 openerp.web.form.FieldMany2One = openerp.web.form.Field.extend({
+    template: 'FieldMany2One',
     init: function(view, node) {
         this._super(view, node);
-        this.template = "FieldMany2One";
         this.limit = 7;
         this.value = null;
         this.cm_id = _.uniqueId('m2o_cm_');
@@ -1748,10 +1759,10 @@
     }
 };
 openerp.web.form.FieldOne2Many = openerp.web.form.Field.extend({
+    template: 'FieldOne2Many',
     multi_selection: false,
     init: function(view, node) {
         this._super(view, node);
-        this.template = "FieldOne2Many";
         this.is_started = $.Deferred();
         this.form_last_update = $.Deferred();
         this.disable_utility_classes = true;
@@ -1932,9 +1943,8 @@
     },
     validate: function() {
         this.invalid = false;
-        var self = this;
-        var view = self.viewmanager.views[self.viewmanager.active_view].controller;
-        if (self.viewmanager.active_view === "form") {
+        var view = this.viewmanager.views[this.viewmanager.active_view].controller;
+        if (this.viewmanager.active_view === "form") {
             for (var f in view.fields) {
                 f = view.fields[f];
                 if (!f.is_valid()) {
@@ -2007,10 +2017,10 @@
 });
 
 openerp.web.form.FieldMany2Many = openerp.web.form.Field.extend({
+    template: 'FieldMany2Many',
     multi_selection: false,
     init: function(view, node) {
         this._super(view, node);
-        this.template = "FieldMany2Many";
         this.list_id = _.uniqueId("many2many");
         this.is_started = $.Deferred();
     },
@@ -2352,9 +2362,9 @@
 });
 
 openerp.web.form.FieldReference = openerp.web.form.Field.extend({
+    template: 'FieldReference',
     init: function(view, node) {
         this._super(view, node);
-        this.template = "FieldReference";
         this.fields_view = {
             fields: {
                 selection: {
@@ -2489,10 +2499,7 @@
 });
 
 openerp.web.form.FieldBinaryFile = openerp.web.form.FieldBinary.extend({
-    init: function(view, node) {
-        this._super(view, node);
-        this.template = "FieldBinaryFile";
-    },
+    template: 'FieldBinaryFile',
     set_value: function(value) {
         this._super.apply(this, arguments);
         var show_value = (value != null && value !== false) ? value : '';
@@ -2520,10 +2527,7 @@
 });
 
 openerp.web.form.FieldBinaryImage = openerp.web.form.FieldBinary.extend({
-    init: function(view, node) {
-        this._super(view, node);
-        this.template = "FieldBinaryImage";
-    },
+    template: 'FieldBinaryImage',
     start: function() {
         this._super.apply(this, arguments);
         this.$image = this.$element.find('img.oe-binary-image');
@@ -2598,6 +2602,93 @@
     }
 });
 
+openerp.web.form.WidgetNotebookReadonly = openerp.web.form.WidgetNotebook.extend({
+    template: 'WidgetNotebook.readonly'
+});
+openerp.web.form.FieldReadonly = openerp.web.form.Field.extend({
+
+});
+openerp.web.form.FieldCharReadonly = openerp.web.form.FieldReadonly.extend({
+    template: 'FieldChar.readonly',
+    set_value: function (value) {
+        this._super.apply(this, arguments);
+        var show_value = openerp.web.format_value(value, this, '');
+        this.$element.find('div').text(show_value);
+        return show_value;
+    }
+});
+openerp.web.form.FieldURIReadonly = openerp.web.form.FieldCharReadonly.extend({
+    template: 'FieldURI.readonly',
+    scheme: null,
+    set_value: function (value) {
+        var displayed = this._super.apply(this, arguments);
+        this.$element.find('a')
+                .attr('href', this.scheme + ':' + displayed)
+                .text(displayed);
+    }
+});
+openerp.web.form.FieldEmailReadonly = openerp.web.form.FieldURIReadonly.extend({
+    scheme: 'mailto'
+});
+openerp.web.form.FieldUrlReadonly = openerp.web.form.FieldURIReadonly.extend({
+    set_value: function (value) {
+        var s = /(\w+):(\.+)/.match(value);
+        if (!(s[0] === 'http' || s[0] === 'https')) { return; }
+        this.scheme = s[0];
+        this._super(s[1]);
+    }
+});
+openerp.web.form.FieldBooleanReadonly = openerp.web.form.FieldCharReadonly.extend({
+    set_value: function (value) {
+        this._super(value ? '\u2714' : '\u2718');
+    }
+});
+openerp.web.form.FieldSelectionReadonly = openerp.web.form.FieldReadonly.extend({
+    template: 'FieldChar.readonly',
+    init: function(view, node) {
+        // lifted straight from r/w version
+        var self = this;
+        this._super(view, node);
+        this.values = this.field.selection;
+        _.each(this.values, function(v, i) {
+            if (v[0] === false && v[1] === '') {
+                self.values.splice(i, 1);
+            }
+        });
+        this.values.unshift([false, '']);
+    },
+    set_value: function (value) {
+        value = value === null ? false : value;
+        value = value instanceof Array ? value[0] : value;
+        var option = _(this.values)
+            .detect(function (record) { return record[0] === value; });
+        this._super(value);
+        this.$element.find('div').text(option ? option[1] : this.values[0][1]);
+    }
+});
+openerp.web.form.FieldMany2OneReadonly = openerp.web.form.FieldCharReadonly.extend({
+    set_value: function (value) {
+        value = value || null;
+        this.invalid = false;
+        var self = this;
+        this.tmp_value = value;
+        self.update_dom();
+        self.on_value_changed();
+        var real_set_value = function(rval) {
+            self.$element.find('div').text(rval ? rval[1] : '');
+        };
+        if(typeof(value) === "number") {
+            var dataset = new openerp.web.DataSetStatic(
+                    this, this.field.relation, self.build_context());
+            dataset.name_get([value], function(data) {
+                real_set_value(data[0]);
+            }).fail(function() {self.tmp_value = undefined;});
+        } else {
+            setTimeout(function() {real_set_value(value);}, 0);
+        }
+    }
+});
+
 /**
  * Registry of form widgets, called by :js:`openerp.web.FormView`
  */
@@ -2630,6 +2721,22 @@
     'binary': 'openerp.web.form.FieldBinaryFile',
     'statusbar': 'openerp.web.form.FieldStatus'
 });
+openerp.web.form.readonly = openerp.web.form.widgets.clone({
+    'notebook': 'openerp.web.form.WidgetNotebookReadonly',
+    'char': 'openerp.web.form.FieldCharReadonly',
+    'email': 'openerp.web.form.FieldEmailReadonly',
+    'url': 'openerp.web.form.FieldUrlReadonly',
+    'text': 'openerp.web.form.FieldCharReadonly',
+    'text_wiki' : 'openerp.web.form.FieldCharReadonly',
+    'date': 'openerp.web.form.FieldCharReadonly',
+    'datetime': 'openerp.web.form.FieldCharReadonly',
+    'selection' : 'openerp.web.form.FieldSelectionReadonly',
+    'many2one': 'openerp.web.form.FieldMany2OneReadonly',
+    'boolean': 'openerp.web.form.FieldBooleanReadonly',
+    'float': 'openerp.web.form.FieldCharReadonly',
+    'integer': 'openerp.web.form.FieldCharReadonly',
+    'float_time': 'openerp.web.form.FieldCharReadonly'
+});
 
 };
 

=== modified file 'addons/web/static/src/xml/base.xml'
--- addons/web/static/src/xml/base.xml	2011-09-19 11:57:35 +0000
+++ addons/web/static/src/xml/base.xml	2011-09-20 12:43:33 +0000
@@ -639,7 +639,7 @@
     <t t-raw="frame.render()"/>
 </t>
 <t t-name="FormView">
-    <div class="oe_form_header" t-att-id="view.element_id + '_header'">
+    <div class="oe_form_header">
         <div class="oe_form_buttons" t-if="view.options.action_buttons !== false">
             <!--<button type="button" class="oe_form_button_save">
                 <span class="oe_form_on_update">Save</span>
@@ -652,6 +652,7 @@
             <!--<button type="button" class="oe_form_button_cancel">Cancel</button>-->
             <button type="button" class="oe_form_button_new">New</button>
             <button type="button" class="oe_form_button_duplicate oe_form_on_update">Duplicate</button>
+            <button type="button" class="oe_form_button_toggle">Readonly/Editable</button>
         </div>
         <div class="oe_form_pager" t-if="view.options.pager !== false">
             <button type="button" data-pager-action="first">First</button>
@@ -711,8 +712,7 @@
                 t-att-width="td.width"
                 t-att-nowrap="td.nowrap or td.is_field_m2o? 'true' : undefined"
                 t-att-valign="td.table ? 'top' : undefined"
-                t-att-id="td.element_id"
-                t-attf-class="oe_form_frame_cell #{td.classname}"
+                t-attf-class="oe_form_frame_cell #{td.classname} #{td.element_class}"
             >
                 <t t-raw="td.render()"/>
             </td>
@@ -722,8 +722,8 @@
 </t>
 <t t-name="WidgetNotebook">
     <ul>
-        <li t-foreach="widget.pages" t-as="page" t-att-id="page.element_tab_id">
-            <a t-att-href="'#' + page.element_id">
+        <li t-foreach="widget.pages" t-as="page">
+            <a href="#">
                 <t t-esc="page.string"/>
             </a>
         </li>
@@ -733,17 +733,23 @@
     </t>
 </t>
 <t t-name="WidgetNotebookPage">
-    <div t-att-id="widget.element_id">
+    <div>
         <t t-call="WidgetFrame"/>
     </div>
 </t>
+<t t-name="WidgetNotebook.readonly">
+    <t t-foreach="widget.pages" t-as="page">
+        <h3><t t-esc="page.string"/></h3>
+        <t t-raw="page.render()"/>
+    </t>
+</t>
 <t t-name="WidgetSeparator">
     <div t-if="widget.orientation !== 'vertical'" t-att-class="'separator ' + widget.orientation">
         <t t-esc="widget.string"/>
     </div>
 </t>
 <t t-name="WidgetLabel">
-    <label t-att-for="widget.element_id + '_field'"
+    <label t-att-for="widget.element_id"
            t-att-class="'oe_label' + (widget.help ? '_help' : '')"
            t-att-title="widget.help">
         <t t-esc="widget.string"/>
@@ -757,12 +763,22 @@
 <t t-name="FieldChar">
     <input type="text" size="1"
         t-att-name="widget.name"
-        t-att-id="widget.element_id + '_field'"
-        t-att-class="'field_' + widget.type"
+        t-att-id="widget.element_id"
+        t-attf-class="field_#{widget.type} #{widget.element_class}"
         t-attf-style="width: #{widget.field.translate ? '99' : '100'}%"
     />
     <img class="oe_field_translate" t-if="widget.field.translate" src="/web/static/src/img/icons/terp-translate.png" width="16" height="16" border="0"/>
 </t>
+<t t-name="FieldChar.readonly">
+    <div
+        t-att-id="widget.element_id"
+        t-attf-class="field_#{widget.type} #{widget.element_class}"
+        t-attf-style="width: #{widget.field.translate ? '99' : '100'}%">
+    </div>
+</t>
+<t t-name="FieldURI.readonly">
+    <a href="#">#</a>
+</t>
 <t t-name="FieldEmail">
     <table cellpadding="0" cellspacing="0" border="0" width="100%">
     <tr>
@@ -794,17 +810,19 @@
 <t t-name="FieldText">
     <textarea rows="6"
         t-att-name="widget.name"
-        t-att-id="widget.element_id + '_field'"
-        t-att-class="'field_' + widget.type"
+        t-att-id="widget.element_id"
+        t-attf-class="field_#{widget.type} #{widget.element_class}"
         t-attf-style="width: #{widget.field.translate ? '99' : '100'}%"
     ></textarea>
     <img class="oe_field_translate" t-if="widget.field.translate" src="/web/static/src/img/icons/terp-translate.png" width="16" height="16" border="0"/>
 </t>
 <t t-name="FieldDate">
     <t t-call="FieldChar"/>
-    <img class="oe_input_icon oe_datepicker_trigger" src="/web/static/src/img/ui/field_calendar.png"
+    <img
+         class="oe_input_icon oe_datepicker_trigger" src="/web/static/src/img/ui/field_calendar.png"
          title="Select date" width="16" height="16" border="0"/>
-    <div class="oe_datepicker ui-widget-content ui-corner-all" style="display: none; position: absolute; z-index: 1;">
+    <div
+         class="oe_datepicker ui-widget-content ui-corner-all" style="display: none; position: absolute; z-index: 1;">
         <div class="oe_datepicker_container"/>
         <button type="button" class="oe_datepicker_close ui-state-default ui-priority-primary ui-corner-all" style="float: right;">Done</button>
     </div>
@@ -813,7 +831,7 @@
     <select
         t-att-name="widget.name"
         t-att-id="widget.element_id + '_field'"
-        t-att-class="'field_' + widget.type"
+        t-attf-class="field_#{widget.type} #{widget.element_class}"
         style="width: 100%">
             <t t-foreach="widget.values" t-as="option">
                 <option><t t-esc="option[1]"/></option>
@@ -821,9 +839,9 @@
     </select>
 </t>
 <t t-name="FieldMany2One">
-    <div t-att-id="widget.element_id" class="oe-m2o">
-        <input t-att-id="widget.element_id + '_input'" type="text" size="1" style="width: 100%;"/>
-        <span class="oe-m2o-drop-down-button" t-att-id="widget.element_id + '_drop_down'">
+    <div t-att-class="widget.element_class" class="oe-m2o">
+        <input type="text" size="1" style="width: 100%;"/>
+        <span class="oe-m2o-drop-down-button">
             <img src="/web/static/src/img/down-arrow.png" /></span>
         <span class="oe-m2o-cm-button" t-att-id="widget.name + '_open'">
             <img src="/web/static/src/img/icons/gtk-index.png"/></span>
@@ -846,8 +864,6 @@
     </ul>
 </t>
 <t t-name="FieldOne2Many">
-    <div t-att-id="widget.element_id">
-    </div>
 </t>
 <t t-name="FieldMany2Many">
     <div t-att-id="widget.list_id"></div>
@@ -855,10 +871,10 @@
 <t t-name="FieldReference">
     <table border="0" width="100%" cellpadding="0" cellspacing="0" class="oe_frame oe_forms">
     <tr>
-        <td t-att-id="widget.selection.element_id" class="oe_form_frame_cell oe_form_selection">
+        <td t-attf-class="oe_form_frame_cell oe_form_selection #{widget.selection.element_class}">
             <t t-raw="widget.selection.render()"/>
         </td>
-        <td t-att-id="widget.m2o.element_id" class="oe_form_frame_cell oe_form_many2one" nowrap="true">
+        <td class="oe_form_frame_cell oe_form_many2one #{widget.selection.element_class}" nowrap="true">
             <t t-raw="widget.m2o.render()"/>
         </td>
     </tr>
@@ -868,7 +884,7 @@
     <input type="checkbox"
         t-att-name="widget.name"
         t-att-id="widget.element_id + '_field'"
-        t-att-class="'field_' + widget.type"/>
+        t-attf-class="field_#{widget.type} #{widget.element_class}"/>
 </t>
 <t t-name="FieldProgressBar">
     <div t-opentag="true" class="oe-progressbar">
@@ -883,7 +899,7 @@
                 t-att-border="widget.readonly ? 0 : 1"
                 t-att-id="widget.element_id + '_field'"
                 t-att-name="widget.name"
-                t-att-class="'field_' + widget.type"
+                t-attf-class="field_#{widget.type} #{widget.element_class}"
                 t-att-width="widget.node.attrs.img_width || widget.node.attrs.width"
                 t-att-height="widget.node.attrs.img_height || widget.node.attrs.height"
             />
@@ -931,7 +947,7 @@
             <input type="text" size="1"
                 t-att-name="widget.name"
                 t-att-id="widget.element_id + '_field'"
-                t-att-class="'field_' + widget.type" style="width: 100%"
+                t-attf-class="field_#{widget.type} #{widget.element_class}" style="width: 100%"
             />
         </td>
         <td class="oe-binary" nowrap="true">
@@ -976,7 +992,7 @@
 </t>
 <t t-name="WidgetButton">
     <button type="button"
-        t-att-id="widget.element_id + '_button'"
+        t-attf-class="#{widget.element_class}"
         t-att-title="widget.help"
         style="width: 100%" class="button">
         <img t-if="widget.node.attrs.icon" t-att-src="'/web/static/src/img/icons/' + widget.node.attrs.icon + '.png'" width="16" height="16"/>

=== added directory 'addons/web_tests'
=== added file 'addons/web_tests/__init__.py'
--- addons/web_tests/__init__.py	1970-01-01 00:00:00 +0000
+++ addons/web_tests/__init__.py	2011-09-20 12:43:33 +0000
@@ -0,0 +1,1 @@
+# -*- coding: utf-8 -*-

=== added file 'addons/web_tests/__openerp__.py'
--- addons/web_tests/__openerp__.py	1970-01-01 00:00:00 +0000
+++ addons/web_tests/__openerp__.py	2011-09-20 12:43:33 +0000
@@ -0,0 +1,8 @@
+{
+    "name": "Tests",
+    "version": "2.0",
+    "depends": [],
+    "js": ["static/src/js/*.js"],
+    "css": ['static/src/css/*.css'],
+    'active': True,
+}

=== added directory 'addons/web_tests/static'
=== added directory 'addons/web_tests/static/src'
=== added directory 'addons/web_tests/static/src/css'
=== added file 'addons/web_tests/static/src/css/web_tests.css'
--- addons/web_tests/static/src/css/web_tests.css	1970-01-01 00:00:00 +0000
+++ addons/web_tests/static/src/css/web_tests.css	2011-09-20 12:43:33 +0000
@@ -0,0 +1,3 @@
+.oe-bunchaforms > div {
+    float: left;
+}

=== added directory 'addons/web_tests/static/src/js'
=== added file 'addons/web_tests/static/src/js/web_tests.js'
--- addons/web_tests/static/src/js/web_tests.js	1970-01-01 00:00:00 +0000
+++ addons/web_tests/static/src/js/web_tests.js	2011-09-20 12:43:33 +0000
@@ -0,0 +1,37 @@
+openerp.web_tests = function (db) {
+    db.web.client_actions.add(
+        'buncha-forms', 'instance.web_tests.BunchaForms');
+    db.web_tests = {};
+    db.web_tests.BunchaForms = db.web.Widget.extend({
+        init: function (parent) {
+            this._super(parent);
+            this.dataset = new db.web.DataSetSearch(this, 'test.listview.relations');
+            this.form = new db.web.FormView(this, this.dataset, false, {
+                action_buttons: false,
+                pager: false
+            });
+            this.form.registry = db.web.form.readonly;
+        },
+        render: function () {
+            return '<div class="oe-bunchaforms"></div>';
+        },
+        start: function () {
+            $.when(
+                this.dataset.read_slice(),
+                this.form.appendTo(this.$element)).then(this.on_everything_loaded);
+        },
+        on_everything_loaded: function (slice) {
+            var records = slice[0].records;
+            if (!records.length) {
+                this.form.on_record_loaded({});
+                return;
+            }
+            this.form.on_record_loaded(records[0]);
+            _(records.slice(1)).each(function (record, index) {
+                this.dataset.index = index+1;
+                this.form.reposition($('<div>').appendTo(this.$element));
+                this.form.on_record_loaded(record);
+            }, this);
+        }
+    });
+};

=== modified file 'openerp-web.py'
--- openerp-web.py	2011-09-16 13:18:44 +0000
+++ openerp-web.py	2011-09-20 12:43:33 +0000
@@ -38,7 +38,7 @@
 optparser.add_option("--log-config", dest="log_config",
                      default='', help="Log config file", metavar="LOG_CONFIG")
 optparser.add_option('--multi-threaded', dest='threaded',
-                     default=False, action='store_true',
+                     default=True, action='store_true',
                      help="Use multiple threads to handle requests")
 
 import web.common.dispatch

_______________________________________________
Mailing list: https://launchpad.net/~openerp-dev-gtk
Post to     : [email protected]
Unsubscribe : https://launchpad.net/~openerp-dev-gtk
More help   : https://help.launchpad.net/ListHelp

Reply via email to