Upgrade to ExtJS 4.0.7 - Released 10/19/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 {Number/Boolean/String} 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      */
43     injectCheckbox: 0,
44
45     /**
46      * @cfg {Boolean} checkOnly <tt>true</tt> if rows can only be selected by clicking on the
47      * checkbox column.
48      */
49     checkOnly: false,
50
51     headerWidth: 24,
52
53     // private
54     checkerOnCls: Ext.baseCSSPrefix + 'grid-hd-checker-on',
55
56     bindComponent: function(view) {
57         var me = this;
58
59         me.sortable = false;
60         me.callParent(arguments);
61         if (!me.hasLockedHeader() || view.headerCt.lockedCt) {
62             // if we have a locked header, only hook up to the first
63             view.headerCt.on('headerclick', me.onHeaderClick, me);
64             me.addCheckbox(true);
65             me.mon(view.ownerCt, 'reconfigure', me.addCheckbox, me);
66         }
67     },
68
69     hasLockedHeader: function(){
70         var hasLocked = false;
71         Ext.each(this.views, function(view){
72             if (view.headerCt.lockedCt) {
73                 hasLocked = true;
74                 return false;
75             }
76         });
77         return hasLocked;
78     },
79
80     /**
81      * Add the header checkbox to the header row
82      * @private
83      * @param {Boolean} initial True if we're binding for the first time.
84      */
85     addCheckbox: function(initial){
86         var me = this,
87             checkbox = me.injectCheckbox,
88             view = me.views[0],
89             headerCt = view.headerCt;
90
91         if (checkbox !== false) {
92             if (checkbox == 'first') {
93                 checkbox = 0;
94             } else if (checkbox == 'last') {
95                 checkbox = headerCt.getColumnCount();
96             }
97             headerCt.add(checkbox,  me.getHeaderConfig());
98         }
99
100         if (initial !== true) {
101             view.refresh();
102         }
103     },
104
105     /**
106      * Toggle the ui header between checked and unchecked state.
107      * @param {Boolean} isChecked
108      * @private
109      */
110     toggleUiHeader: function(isChecked) {
111         var view     = this.views[0],
112             headerCt = view.headerCt,
113             checkHd  = headerCt.child('gridcolumn[isCheckerHd]');
114
115         if (checkHd) {
116             if (isChecked) {
117                 checkHd.el.addCls(this.checkerOnCls);
118             } else {
119                 checkHd.el.removeCls(this.checkerOnCls);
120             }
121         }
122     },
123
124     /**
125      * Toggle between selecting all and deselecting all when clicking on
126      * a checkbox header.
127      */
128     onHeaderClick: function(headerCt, header, e) {
129         if (header.isCheckerHd) {
130             e.stopEvent();
131             var isChecked = header.el.hasCls(Ext.baseCSSPrefix + 'grid-hd-checker-on');
132             if (isChecked) {
133                 // We have to supress the event or it will scrollTo the change
134                 this.deselectAll(true);
135             } else {
136                 // We have to supress the event or it will scrollTo the change
137                 this.selectAll(true);
138             }
139         }
140     },
141
142     /**
143      * Retrieve a configuration to be used in a HeaderContainer.
144      * This should be used when injectCheckbox is set to false.
145      */
146     getHeaderConfig: function() {
147         var me = this;
148
149         return {
150             isCheckerHd: true,
151             text : '&#160;',
152             width: me.headerWidth,
153             sortable: false,
154             draggable: false,
155             resizable: false,
156             hideable: false,
157             menuDisabled: true,
158             dataIndex: '',
159             cls: Ext.baseCSSPrefix + 'column-header-checkbox ',
160             renderer: Ext.Function.bind(me.renderer, me),
161             locked: me.hasLockedHeader()
162         };
163     },
164
165     /**
166      * Generates the HTML to be rendered in the injected checkbox column for each row.
167      * Creates the standard checkbox markup by default; can be overridden to provide custom rendering.
168      * See {@link Ext.grid.column.Column#renderer} for description of allowed parameters.
169      */
170     renderer: function(value, metaData, record, rowIndex, colIndex, store, view) {
171         metaData.tdCls = Ext.baseCSSPrefix + 'grid-cell-special';
172         return '<div class="' + Ext.baseCSSPrefix + 'grid-row-checker">&#160;</div>';
173     },
174
175     // override
176     onRowMouseDown: function(view, record, item, index, e) {
177         view.el.focus();
178         var me = this,
179             checker = e.getTarget('.' + Ext.baseCSSPrefix + 'grid-row-checker');
180             
181         if (!me.allowRightMouseSelection(e)) {
182             return;
183         }
184
185         // checkOnly set, but we didn't click on a checker.
186         if (me.checkOnly && !checker) {
187             return;
188         }
189
190         if (checker) {
191             var mode = me.getSelectionMode();
192             // dont change the mode if its single otherwise
193             // we would get multiple selection
194             if (mode !== 'SINGLE') {
195                 me.setSelectionMode('SIMPLE');
196             }
197             me.selectWithEvent(record, e);
198             me.setSelectionMode(mode);
199         } else {
200             me.selectWithEvent(record, e);
201         }
202     },
203
204     /**
205      * Synchronize header checker value as selection changes.
206      * @private
207      */
208     onSelectChange: function() {
209         this.callParent(arguments);
210
211         // check to see if all records are selected
212         var hdSelectStatus = this.selected.getCount() === this.store.getCount();
213         this.toggleUiHeader(hdSelectStatus);
214     }
215 });
216