Upgrade to ExtJS 4.0.2 - Released 06/09/2011
[extjs.git] / src / selection / CheckboxModel.js
1 /*
2
3 This file is part of Ext JS 4
4
5 Copyright (c) 2011 Sencha Inc
6
7 Contact:  http://www.sencha.com/contact
8
9 GNU General Public License Usage
10 This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file.  Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
11
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
13
14 */
15 /**
16  * @class Ext.selection.CheckboxModel
17  * @extends Ext.selection.RowModel
18  *
19  * A selection model that renders a column of checkboxes that can be toggled to
20  * select or deselect rows. The default mode for this selection model is MULTI.
21  *
22  * The selection model will inject a header for the checkboxes in the first view
23  * and according to the 'injectCheckbox' configuration.
24  */
25 Ext.define('Ext.selection.CheckboxModel', {
26     alias: 'selection.checkboxmodel',
27     extend: 'Ext.selection.RowModel',
28
29     /**
30      * @cfg {String} mode
31      * Modes of selection.
32      * Valid values are SINGLE, SIMPLE, and MULTI. Defaults to 'MULTI'
33      */
34     mode: 'MULTI',
35
36     /**
37      * @cfg {Mixed} injectCheckbox
38      * Instructs the SelectionModel whether or not to inject the checkbox header
39      * automatically or not. (Note: By not placing the checkbox in manually, the
40      * grid view will need to be rendered 2x on initial render.)
41      * Supported values are a Number index, false and the strings 'first' and 'last'.
42      * Default is 0.
43      */
44     injectCheckbox: 0,
45
46     /**
47      * @cfg {Boolean} checkOnly <tt>true</tt> if rows can only be selected by clicking on the
48      * checkbox column (defaults to <tt>false</tt>).
49      */
50     checkOnly: false,
51
52     // private
53     checkerOnCls: Ext.baseCSSPrefix + 'grid-hd-checker-on',
54
55     bindComponent: function() {
56         this.sortable = false;
57         this.callParent(arguments);
58
59         var view     = this.views[0],
60             headerCt = view.headerCt;
61
62         if (this.injectCheckbox !== false) {
63             if (this.injectCheckbox == 'first') {
64                 this.injectCheckbox = 0;
65             } else if (this.injectCheckbox == 'last') {
66                 this.injectCheckbox = headerCt.getColumnCount();
67             }
68             headerCt.add(this.injectCheckbox,  this.getHeaderConfig());
69         }
70         headerCt.on('headerclick', this.onHeaderClick, this);
71     },
72
73     /**
74      * Toggle the ui header between checked and unchecked state.
75      * @param {Boolean} isChecked
76      * @private
77      */
78     toggleUiHeader: function(isChecked) {
79         var view     = this.views[0],
80             headerCt = view.headerCt,
81             checkHd  = headerCt.child('gridcolumn[isCheckerHd]');
82
83         if (checkHd) {
84             if (isChecked) {
85                 checkHd.el.addCls(this.checkerOnCls);
86             } else {
87                 checkHd.el.removeCls(this.checkerOnCls);
88             }
89         }
90     },
91
92     /**
93      * Toggle between selecting all and deselecting all when clicking on
94      * a checkbox header.
95      */
96     onHeaderClick: function(headerCt, header, e) {
97         if (header.isCheckerHd) {
98             e.stopEvent();
99             var isChecked = header.el.hasCls(Ext.baseCSSPrefix + 'grid-hd-checker-on');
100             if (isChecked) {
101                 // We have to supress the event or it will scrollTo the change
102                 this.deselectAll(true);
103             } else {
104                 // We have to supress the event or it will scrollTo the change
105                 this.selectAll(true);
106             }
107         }
108     },
109
110     /**
111      * Retrieve a configuration to be used in a HeaderContainer.
112      * This should be used when injectCheckbox is set to false.
113      */
114     getHeaderConfig: function() {
115         return {
116             isCheckerHd: true,
117             text : '&#160;',
118             width: 24,
119             sortable: false,
120             fixed: true,
121             hideable: false,
122             menuDisabled: true,
123             dataIndex: '',
124             cls: Ext.baseCSSPrefix + 'column-header-checkbox ',
125             renderer: Ext.Function.bind(this.renderer, this)
126         };
127     },
128
129     /**
130      * Generates the HTML to be rendered in the injected checkbox column for each row.
131      * Creates the standard checkbox markup by default; can be overridden to provide custom rendering.
132      * See {@link Ext.grid.column.Column#renderer} for description of allowed parameters.
133      */
134     renderer: function(value, metaData, record, rowIndex, colIndex, store, view) {
135         metaData.tdCls = Ext.baseCSSPrefix + 'grid-cell-special';
136         return '<div class="' + Ext.baseCSSPrefix + 'grid-row-checker">&#160;</div>';
137     },
138
139     // override
140     onRowMouseDown: function(view, record, item, index, e) {
141         view.el.focus();
142         var me = this,
143             checker = e.getTarget('.' + Ext.baseCSSPrefix + 'grid-row-checker');
144
145         // checkOnly set, but we didn't click on a checker.
146         if (me.checkOnly && !checker) {
147             return;
148         }
149
150         if (checker) {
151             var mode = me.getSelectionMode();
152             // dont change the mode if its single otherwise
153             // we would get multiple selection
154             if (mode !== 'SINGLE') {
155                 me.setSelectionMode('SIMPLE');
156             }
157             me.selectWithEvent(record, e);
158             me.setSelectionMode(mode);
159         } else {
160             me.selectWithEvent(record, e);
161         }
162     },
163
164     /**
165      * Synchronize header checker value as selection changes.
166      * @private
167      */
168     onSelectChange: function() {
169         this.callParent(arguments);
170
171         // check to see if all records are selected
172         var hdSelectStatus = this.selected.getCount() === this.store.getCount();
173         this.toggleUiHeader(hdSelectStatus);
174     }
175 });
176