[FIX] display pager in listview after ungrouping (web)
[odoo/odoo.git] / addons / web / static / src / js / view_list.js
index 52124d5..97b3fde 100644 (file)
@@ -1,4 +1,7 @@
-openerp.web.list = function (instance) {
+(function() {
+
+var instance = openerp;
+openerp.web.list = {};
 var _t = instance.web._t,
     _lt = instance.web._lt;
 var QWeb = instance.web.qweb;
@@ -132,8 +135,8 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
 
         this.groups = groups;
         $(this.groups).bind({
-            'selected': function (e, ids, records) {
-                self.do_select(ids, records);
+            'selected': function (e, ids, records, deselected) {
+                self.do_select(ids, records, deselected);
             },
             'deleted': function (e, ids) {
                 self.do_delete(ids);
@@ -164,18 +167,20 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
      * @returns {String} CSS style declaration
      */
     style_for: function (record) {
-        var style= '';
+        var len, style= '';
 
         var context = _.extend({}, record.attributes, {
             uid: this.session.uid,
-            current_date: new Date().toString('yyyy-MM-dd')
+            current_date: moment().format('YYYY-MM-DD')
             // TODO: time, datetime, relativedelta
         });
-
+        var i;
+        var pair;
+        var expression;
         if (this.fonts) {
-            for(var i=0, len=this.fonts.length; i<len; ++i) {
-                var pair = this.fonts[i],
-                font = pair[0],
+            for(i=0, len=this.fonts.length; i<len; ++i) {
+                pair = this.fonts[i];
+                var font = pair[0];
                 expression = pair[1];
                 if (py.evaluate(expression, context).toJSON()) {
                     switch(font) {
@@ -194,10 +199,10 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
         }
 
         if (!this.colors) { return style; }
-        for(var i=0, len=this.colors.length; i<len; ++i) {
-            var pair = this.colors[i],
-                color = pair[0],
-                expression = pair[1];
+        for(i=0, len=this.colors.length; i<len; ++i) {
+            pair = this.colors[i];
+            var color = pair[0];
+            expression = pair[1];
             if (py.evaluate(expression, context).toJSON()) {
                 return style += 'color: ' + color + ';';
             }
@@ -284,7 +289,7 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
 
         // Pager
         if (!this.$pager) {
-            this.$pager = $(QWeb.render("ListView.pager", {'widget':self}));
+            this.$pager = $(QWeb.render("ListView.pager", {'widget':self})).hide();
             if (this.options.$buttons) {
                 this.$pager.appendTo(this.options.$pager);
             } else {
@@ -349,6 +354,12 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
             this.sidebar.$el.hide();
         }
         //Sort
+        var default_order = this.fields_view.arch.attrs.default_order,
+            unsorted = !this.dataset._sort.length;
+        if (unsorted && default_order) {
+            this.dataset.set_sort(default_order.split(','));
+        }
+
         if(this.dataset._sort.length){
             if(this.dataset._sort[0].indexOf('-') == -1){
                 this.$el.find('th[data-id=' + this.dataset._sort[0] + ']').addClass("sortdown");
@@ -361,11 +372,10 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
     sort_by_column: function (e) {
         e.stopPropagation();
         var $column = $(e.currentTarget);
-        var col_name = $column.data('id')
+        var col_name = $column.data('id');
         var field = this.fields_view.fields[col_name];
-        // test if the field is a function field with store=false, since it's impossible
-        // for the server to sort those fields we desactivate the feature
-        if (field && field.store === false) {
+        // test whether the field is sortable
+        if (field && !field.sortable) {
             return false;
         }
         this.dataset.sort(col_name);
@@ -394,11 +404,8 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
 
         var total = dataset.size();
         var limit = this.limit() || total;
-        if (total == 0)
-            this.$pager.hide();
-        else
-            this.$pager.css("display", "");
-        this.$pager.toggleClass('oe_list_pager_single_page', (total <= limit));
+        this.$pager.find('.oe-pager-button').toggle(total > limit);
+        this.$pager.find('.oe_pager_value').toggle(total !== 0);
         var spager = '-';
         if (total) {
             var range_start = this.page * limit + 1;
@@ -457,7 +464,7 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
      * @param {String} [view="page"] the view type to switch to
      */
     select_record:function (index, view) {
-        view = view || index == null ? 'form' : 'form';
+        view = view || index === null || index === undefined ? 'form' : 'form';
         this.dataset.index = index;
         _.delay(_.bind(function () {
             this.do_switch_view(view);
@@ -504,6 +511,9 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
         self.$el.find('.oe_list_record_selector').prop('checked', false);
         this.records.reset();
         var reloaded = $.Deferred();
+        reloaded.then(function () {
+            if (!self.grouped) self.$pager.show();
+        });
         this.$el.find('.oe_list_content').append(
             this.groups.render(function () {
                 if (self.dataset.index == null) {
@@ -528,7 +538,7 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
     },
     reload_record: function (record) {
         var self = this;
-        // Use of search_read instead of read to check if we can still read the record (security rules)
+        var fields = this.fields_view.fields;
         return this.dataset.read_ids(
             [record.get('id')],
             _.pluck(_(this.columns).filter(function (r) {
@@ -541,8 +551,10 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
                 self.records.remove(record);
                 return;
             }
-            _(_.keys(values)).each(function(key){
-                record.set(key, values[key], {silent: true});
+            _.each(values, function (value, key) {
+                if (fields[key] && fields[key].type === 'many2many')
+                    record.set(key + '__display', false, {silent: true});
+                record.set(key, value, {silent: true});            
             });
             record.trigger('change', record);
         });
@@ -622,7 +634,12 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
      * @param {Array} ids selected record ids
      * @param {Array} records selected record values
      */
-    do_select: function (ids, records) {
+    do_select: function (ids, records, deselected) {
+        // uncheck header hook if at least one row has been deselected
+        if (deselected) {
+            this.$('.oe_list_record_selector').prop('checked', false);
+        }
+
         if (!ids.length) {
             this.dataset.index = 0;
             if (this.sidebar) {
@@ -810,6 +827,29 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
         return ids;
     },
     /**
+     * Calculate the active domain of the list view. This should be done only
+     * if the header checkbox has been checked. This is done by evaluating the
+     * search results, and then adding the dataset domain (i.e. action domain).
+     */
+    get_active_domain: function () {
+        var self = this;
+        if (this.$('.oe_list_record_selector').prop('checked')) {
+            var search_view = this.getParent().searchview;
+            var search_data = search_view.build_search_data();
+            return instance.web.pyeval.eval_domains_and_contexts({
+                domains: search_data.domains,
+                contexts: search_data.contexts,
+                group_by_seq: search_data.groupbys || []
+            }).then(function (results) {
+                var domain = self.dataset.domain.concat(results.domain || []);
+                return domain
+            });
+        }
+        else {
+            return $.Deferred().resolve();
+        }
+    },
+    /**
      * Adds padding columns at the start or end of all table rows (including
      * field names row)
      *
@@ -863,9 +903,9 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
         this.$el.prepend(
             $('<div class="oe_view_nocontent">').html(this.options.action.help)
         );
-        var create_nocontent = this.$buttons;
+        var $buttons = this.$buttons;
         this.$el.find('.oe_view_nocontent').click(function() {
-            create_nocontent.openerpBounce();
+            $buttons.width($buttons.width() + 1).openerpBounce();
         });
     }
 });
@@ -970,8 +1010,9 @@ instance.web.ListView.List = instance.web.Class.extend( /** @lends instance.web.
             .delegate('th.oe_list_record_selector', 'click', function (e) {
                 e.stopPropagation();
                 var selection = self.get_selection();
+                var checked = $(e.currentTarget).find('input').prop('checked');
                 $(self).trigger(
-                        'selected', [selection.ids, selection.records]);
+                        'selected', [selection.ids, selection.records, ! checked]);
             })
             .delegate('td.oe_list_record_delete button', 'click', function (e) {
                 e.stopPropagation();
@@ -1111,11 +1152,7 @@ instance.web.ListView.List = instance.web.Class.extend( /** @lends instance.web.
             if (column.invisible === '1') {
                 return;
             }
-            if (column.tag === 'button') {
-                cells.push('<td class="oe_button" title="' + column.string + '">&nbsp;</td>');
-            } else {
-                cells.push('<td title="' + column.string + '">&nbsp;</td>');
-            }
+            cells.push('<td title="' + column.string + '">&nbsp;</td>');
         });
         if (this.options.deletable) {
             cells.push('<td class="oe_list_record_delete"><button type="button" style="visibility: hidden"> </button></td>');
@@ -1428,10 +1465,10 @@ instance.web.ListView.Groups = instance.web.Class.extend( /** @lends instance.we
     bind_child_events: function (child) {
         var $this = $(this),
              self = this;
-        $(child).bind('selected', function (e) {
+        $(child).bind('selected', function (e, _0, _1, deselected) {
             // can have selections spanning multiple links
             var selection = self.get_selection();
-            $this.trigger(e, [selection.ids, selection.records]);
+            $this.trigger(e, [selection.ids, selection.records, deselected]);
         }).bind(this.passthrough_events, function (e) {
             // additional positional parameters are provided to trigger as an
             // Array, following the event type or event object, but are
@@ -1457,7 +1494,7 @@ instance.web.ListView.Groups = instance.web.Class.extend( /** @lends instance.we
            limit = view.limit(),
             page = this.datagroup.openable ? this.page : view.page;
 
-        var fields = _.pluck(_.select(this.columns, function(x) {return x.tag == "field"}), 'name');
+        var fields = _.pluck(_.select(this.columns, function(x) {return x.tag == "field";}), 'name');
         var options = { offset: page * limit, limit: limit, context: {bin_size: true} };
         //TODO xmo: investigate why we need to put the setTimeout
         return $.async_when().then(function() {
@@ -1483,12 +1520,10 @@ instance.web.ListView.Groups = instance.web.Class.extend( /** @lends instance.we
                                 }))
                             .end()
                             .find('button[data-pager-action=previous]')
-                                .css('visibility',
-                                     page === 0 ? 'hidden' : '')
+                                .toggleClass('disabled', page === 0)
                             .end()
                             .find('button[data-pager-action=next]')
-                                .css('visibility',
-                                     page === pages - 1 ? 'hidden' : '');
+                                .toggleClass('disabled', page === pages - 1);
                     }
                 }
 
@@ -1546,20 +1581,21 @@ instance.web.ListView.Groups = instance.web.Class.extend( /** @lends instance.we
                     // if drag to 1st row (to = 0), start sequencing from 0
                     // (exclusive lower bound)
                     seq = to ? list.records.at(to - 1).get(seqname) : 0;
-                while (++seq, record = list.records.at(index++)) {
+                var fct = function (dataset, id, seq) {
+                    $.async_when().done(function () {
+                        var attrs = {};
+                        attrs[seqname] = seq;
+                        dataset.write(id, attrs);
+                    });
+                };
+                while (++seq, (record = list.records.at(index++))) {
                     // write are independent from one another, so we can just
                     // launch them all at the same time and we don't really
                     // give a fig about when they're done
                     // FIXME: breaks on o2ms (e.g. Accounting > Financial
                     //        Accounting > Taxes > Taxes, child tax accounts)
                     //        when synchronous (without setTimeout)
-                    (function (dataset, id, seq) {
-                        $.async_when().done(function () {
-                            var attrs = {};
-                            attrs[seqname] = seq;
-                            dataset.write(id, attrs);
-                        });
-                    }(dataset, record.get('id'), seq));
+                    fct(dataset, record.get('id'), seq);
                     record.set(seqname, seq);
                 }
             }
@@ -1572,7 +1608,7 @@ instance.web.ListView.Groups = instance.web.Class.extend( /** @lends instance.we
 
         this.datagroup.list(
             _(this.view.visible_columns).chain()
-                .filter(function (column) { return column.tag === 'field' })
+                .filter(function (column) { return column.tag === 'field';})
                 .pluck('name').value(),
             function (groups) {
                 self.view.$pager.hide();
@@ -1587,6 +1623,7 @@ instance.web.ListView.Groups = instance.web.Class.extend( /** @lends instance.we
                     self.setup_resequence_rows(list, dataset);
                 }).always(function() {
                     if (post_render) { post_render(); }
+                    self.view.trigger('view_list_rendered');
                 });
             });
         return $el;
@@ -1619,7 +1656,7 @@ instance.web.ListView.Groups = instance.web.Class.extend( /** @lends instance.we
             return {
                 count: this.datagroup.length,
                 values: this.datagroup.aggregates
-            }
+            };
         }
         return _(this.children).chain()
             .map(function (child) {
@@ -1879,7 +1916,7 @@ var Collection = instance.web.Class.extend(/** @lends Collection# */{
             var instance_ = (records[i] instanceof Record) ? records[i] : new Record(records[i]);
             instance_.bind(null, this._onRecordEvent);
             this._byId[instance_.get('id')] = instance_;
-            if (options.at == undefined) {
+            if (options.at === undefined || options.at === null) {
                 this.records.push(instance_);
                 if (!options.silent) {
                     this.trigger('add', this, instance_, this.records.length-1);
@@ -1921,7 +1958,8 @@ var Collection = instance.web.Class.extend(/** @lends Collection# */{
         if (!_(this._proxies).isEmpty()) {
             var record = null;
             _(this._proxies).detect(function (proxy) {
-                return record = proxy.get(id);
+                record = proxy.get(id);
+                return record;
             });
             return record;
         }
@@ -1935,10 +1973,11 @@ var Collection = instance.web.Class.extend(/** @lends Collection# */{
      * @returns {Collection}
      */
     proxy: function (section) {
-        return this._proxies[section] = new Collection(null, {
+        this._proxies[section] = new Collection(null, {
             parent: this,
             key: section
         }).bind(null, this._onRecordEvent);
+        return this._proxies[section];
     },
     /**
      * @param {Array} [records]
@@ -2008,7 +2047,7 @@ var Collection = instance.web.Class.extend(/** @lends Collection# */{
         var record;
         for(var section in this._proxies) {
             if (!this._proxies.hasOwnProperty(section)) {
-                continue
+                continue;
             }
             if ((record = this._proxies[section].find(callback))) {
                 return record;
@@ -2110,12 +2149,14 @@ instance.web.list.columns = new instance.web.Registry({
     'field': 'instance.web.list.Column',
     'field.boolean': 'instance.web.list.Boolean',
     'field.binary': 'instance.web.list.Binary',
+    'field.char': 'instance.web.list.Char',
     'field.progressbar': 'instance.web.list.ProgressBar',
     'field.handle': 'instance.web.list.Handle',
     'button': 'instance.web.list.Button',
     'field.many2onebutton': 'instance.web.list.Many2OneButton',
     'field.reference': 'instance.web.list.Reference',
-    'field.many2many': 'instance.web.list.Many2Many'
+    'field.many2many': 'instance.web.list.Many2Many',
+    'button.toggle_button': 'instance.web.list.toggle_button',
 });
 instance.web.list.columns.for_ = function (id, field, node) {
     var description = _.extend({tag: node.tag}, field, node.attrs);
@@ -2125,7 +2166,7 @@ instance.web.list.columns.for_ = function (id, field, node) {
         tag + '.'+ description.type,
         tag
     ]);
-    return new Type(id, node.tag, description)
+    return new Type(id, node.tag, description);
 };
 
 instance.web.list.Column = instance.web.Class.extend({
@@ -2229,8 +2270,8 @@ instance.web.list.Button = instance.web.list.Column.extend({
             attrs = this.modifiers_for(row_data);
         }
         if (attrs.invisible) { return ''; }
-
-        return QWeb.render('ListView.row.button', {
+        var template = this.icon && 'ListView.row.button' || 'ListView.row.text_button';
+        return QWeb.render(template, {
             widget: this,
             prefix: instance.session.prefix,
             disabled: attrs.readonly
@@ -2279,6 +2320,20 @@ instance.web.list.Binary = instance.web.list.Column.extend({
         });
     }
 });
+instance.web.list.Char = instance.web.list.Column.extend({
+    replacement: '*',
+    /**
+     * If password field, only display replacement characters (if value is
+     * non-empty)
+     */
+    _format: function (row_data, options) {
+        var value = row_data[this.id].value;
+        if (value && this.password === 'True') {
+            return value.replace(/[\s\S]/g, _.escape(this.replacement));
+        }
+        return this._super(row_data, options);
+    }
+});
 instance.web.list.ProgressBar = instance.web.list.Column.extend({
     /**
      * Return a formatted progress bar display
@@ -2340,5 +2395,18 @@ instance.web.list.Reference = instance.web.list.Column.extend({
         return this._super(row_data, options);
     }
 });
-};
-// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax:
+instance.web.list.toggle_button = instance.web.list.Column.extend({
+    format: function (row_data, options) {
+        this._super(row_data, options);
+        var button_tips = JSON.parse(this.options);
+        var fieldname = this.field_name;
+        var has_value = row_data[fieldname] && !!row_data[fieldname].value;
+        this.icon = has_value ? 'gtk-yes' : 'gtk-normal';
+        this.string = has_value ? _t(button_tips ? button_tips['active']: ''): _t(button_tips ? button_tips['inactive']: '');
+        return QWeb.render('toggle_button', {
+            widget: this,
+            prefix: instance.session.prefix,
+        });
+    },
+});
+})();