4 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5 <title>The source code</title>
6 <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
7 <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
8 <style type="text/css">
9 .highlight { display: block; background-color: #ddd; }
11 <script type="text/javascript">
12 function highlight() {
13 document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
17 <body onload="prettyPrint(); highlight();">
18 <pre class="prettyprint lang-js"><span id='Ext-selection-Model'>/**
19 </span> * Tracks what records are currently selected in a databound component.
21 * This is an abstract class and is not meant to be directly used. Databound UI widgets such as
22 * {@link Ext.grid.Panel Grid} and {@link Ext.tree.Panel Tree} should subclass Ext.selection.Model
23 * and provide a way to binding to the component.
25 * The abstract methods `onSelectChange` and `onLastFocusChanged` should be implemented in these
26 * subclasses to update the UI widget.
28 Ext.define('Ext.selection.Model', {
29 extend: 'Ext.util.Observable',
30 alternateClassName: 'Ext.AbstractSelectionModel',
31 requires: ['Ext.data.StoreManager'],
34 <span id='Ext-selection-Model-cfg-mode'> /**
35 </span> * @cfg {String} mode
36 * Mode of selection. Valid values are:
38 * - **SINGLE** - Only allows selecting one item at a time. Use {@link #allowDeselect} to allow
39 * deselecting that item. This is the default.
40 * - **SIMPLE** - Allows simple selection of multiple items one-by-one. Each click in grid will either
41 * select or deselect an item.
42 * - **MULTI** - Allows complex selection of multiple items using Ctrl and Shift keys.
45 <span id='Ext-selection-Model-cfg-allowDeselect'> /**
46 </span> * @cfg {Boolean} allowDeselect
47 * Allow users to deselect a record in a DataView, List or Grid.
48 * Only applicable when the {@link #mode} is 'SINGLE'.
52 <span id='Ext-selection-Model-property-selected'> /**
53 </span> * @property {Ext.util.MixedCollection} selected
54 * A MixedCollection that maintains all of the currently selected records. Read-only.
58 <span id='Ext-selection-Model-property-pruneRemoved'> /**
59 </span> * Prune records when they are removed from the store from the selection.
60 * This is a private flag. For an example of its usage, take a look at
61 * Ext.selection.TreeModel.
66 constructor: function(cfg) {
73 <span id='Ext-selection-Model-event-selectionchange'> /**
75 * Fired after a selection change has occurred
76 * @param {Ext.selection.Model} this
77 * @param {Ext.data.Model[]} selected The selected records
88 // sets this.selectionMode
89 me.setSelectionMode(cfg.mode || me.mode);
91 // maintains the currently selected records.
92 me.selected = Ext.create('Ext.util.MixedCollection');
94 me.callParent(arguments);
97 // binds the store to the selModel.
98 bind : function(store, initial){
101 if(!initial && me.store){
102 if(store !== me.store && me.store.autoDestroy){
103 me.store.destroyStore();
105 me.store.un("add", me.onStoreAdd, me);
106 me.store.un("clear", me.onStoreClear, me);
107 me.store.un("remove", me.onStoreRemove, me);
108 me.store.un("update", me.onStoreUpdate, me);
112 store = Ext.data.StoreManager.lookup(store);
115 clear: me.onStoreClear,
116 remove: me.onStoreRemove,
117 update: me.onStoreUpdate,
122 if(store && !initial) {
127 <span id='Ext-selection-Model-method-selectAll'> /**
128 </span> * Selects all records in the view.
129 * @param {Boolean} suppressEvent True to suppress any select events
131 selectAll: function(suppressEvent) {
133 selections = me.store.getRange(),
135 len = selections.length,
136 start = me.getSelection().length;
138 me.bulkChange = true;
139 for (; i < len; i++) {
140 me.doSelect(selections[i], true, suppressEvent);
142 delete me.bulkChange;
143 // fire selection change only if the number of selections differs
144 me.maybeFireSelectionChange(me.getSelection().length !== start);
147 <span id='Ext-selection-Model-method-deselectAll'> /**
148 </span> * Deselects all records in the view.
149 * @param {Boolean} suppressEvent True to suppress any deselect events
151 deselectAll: function(suppressEvent) {
153 selections = me.getSelection(),
155 len = selections.length,
156 start = me.getSelection().length;
158 me.bulkChange = true;
159 for (; i < len; i++) {
160 me.doDeselect(selections[i], suppressEvent);
162 delete me.bulkChange;
163 // fire selection change only if the number of selections differs
164 me.maybeFireSelectionChange(me.getSelection().length !== start);
167 // Provides differentiation of logic between MULTI, SIMPLE and SINGLE
168 // selection modes. Requires that an event be passed so that we can know
169 // if user held ctrl or shift.
170 selectWithEvent: function(record, e, keepExisting) {
173 switch (me.selectionMode) {
175 if (e.ctrlKey && me.isSelected(record)) {
176 me.doDeselect(record, false);
177 } else if (e.shiftKey && me.lastFocused) {
178 me.selectRange(me.lastFocused, record, e.ctrlKey);
179 } else if (e.ctrlKey) {
180 me.doSelect(record, true, false);
181 } else if (me.isSelected(record) && !e.shiftKey && !e.ctrlKey && me.selected.getCount() > 1) {
182 me.doSelect(record, keepExisting, false);
184 me.doSelect(record, false);
188 if (me.isSelected(record)) {
189 me.doDeselect(record);
191 me.doSelect(record, true);
195 // if allowDeselect is on and this record isSelected, deselect it
196 if (me.allowDeselect && me.isSelected(record)) {
197 me.doDeselect(record);
198 // select the record and do NOT maintain existing selections
200 me.doSelect(record, false);
206 <span id='Ext-selection-Model-method-selectRange'> /**
207 </span> * Selects a range of rows if the selection model {@link #isLocked is not locked}.
208 * All rows in between startRow and endRow are also selected.
209 * @param {Ext.data.Model/Number} startRow The record or index of the first row in the range
210 * @param {Ext.data.Model/Number} endRow The record or index of the last row in the range
211 * @param {Boolean} [keepExisting] True to retain existing selections
213 selectRange : function(startRow, endRow, keepExisting, dir){
227 me.deselectAll(true);
230 if (!Ext.isNumber(startRow)) {
231 startRow = store.indexOf(startRow);
233 if (!Ext.isNumber(endRow)) {
234 endRow = store.indexOf(endRow);
238 if (startRow > endRow){
244 for (i = startRow; i <= endRow; i++) {
245 if (me.isSelected(store.getAt(i))) {
253 dontDeselect = (dir == 'up') ? startRow : endRow;
256 for (i = startRow; i <= endRow; i++){
257 if (selectedCount == (endRow - startRow + 1)) {
258 if (i != dontDeselect) {
259 me.doDeselect(i, true);
262 records.push(store.getAt(i));
265 me.doMultiSelect(records, true);
268 <span id='Ext-selection-Model-method-select'> /**
269 </span> * Selects a record instance by record instance or index.
270 * @param {Ext.data.Model[]/Number} records An array of records or an index
271 * @param {Boolean} [keepExisting] True to retain existing selections
272 * @param {Boolean} [suppressEvent] Set to true to not fire a select event
274 select: function(records, keepExisting, suppressEvent) {
275 // Automatically selecting eg store.first() or store.last() will pass undefined, so that must just return;
276 if (Ext.isDefined(records)) {
277 this.doSelect(records, keepExisting, suppressEvent);
281 <span id='Ext-selection-Model-method-deselect'> /**
282 </span> * Deselects a record instance by record instance or index.
283 * @param {Ext.data.Model[]/Number} records An array of records or an index
284 * @param {Boolean} [suppressEvent] Set to true to not fire a deselect event
286 deselect: function(records, suppressEvent) {
287 this.doDeselect(records, suppressEvent);
290 doSelect: function(records, keepExisting, suppressEvent) {
297 if (typeof records === "number") {
298 records = [me.store.getAt(records)];
300 if (me.selectionMode == "SINGLE" && records) {
301 record = records.length ? records[0] : records;
302 me.doSingleSelect(record, suppressEvent);
304 me.doMultiSelect(records, keepExisting, suppressEvent);
308 doMultiSelect: function(records, keepExisting, suppressEvent) {
310 selected = me.selected,
320 records = !Ext.isArray(records) ? [records] : records;
321 len = records.length;
322 if (!keepExisting && selected.getCount() > 0) {
323 if (me.doDeselect(me.getSelection(), suppressEvent) === false) {
326 // TODO - coalesce the selectionchange event in deselect w/the one below...
330 selected.add(record);
334 for (; i < len; i++) {
336 if (keepExisting && me.isSelected(record)) {
339 me.lastSelected = record;
341 me.onSelectChange(record, true, suppressEvent, commit);
343 me.setLastFocused(record, suppressEvent);
344 // fire selchange if there was a change and there is no suppressEvent flag
345 me.maybeFireSelectionChange(change && !suppressEvent);
348 // records can be an index, a record or an array of records
349 doDeselect: function(records, suppressEvent) {
351 selected = me.selected,
361 if (typeof records === "number") {
362 records = [me.store.getAt(records)];
363 } else if (!Ext.isArray(records)) {
369 selected.remove(record);
372 len = records.length;
374 for (; i < len; i++) {
376 if (me.isSelected(record)) {
377 if (me.lastSelected == record) {
378 me.lastSelected = selected.last();
381 me.onSelectChange(record, false, suppressEvent, commit);
385 // fire selchange if there was a change and there is no suppressEvent flag
386 me.maybeFireSelectionChange(accepted > 0 && !suppressEvent);
387 return accepted === attempted;
390 doSingleSelect: function(record, suppressEvent) {
393 selected = me.selected;
399 // should we also check beforeselect?
400 if (me.isSelected(record)) {
405 me.bulkChange = true;
406 if (selected.getCount() > 0 && me.doDeselect(me.lastSelected, suppressEvent) === false) {
407 delete me.bulkChange;
410 delete me.bulkChange;
412 selected.add(record);
413 me.lastSelected = record;
417 me.onSelectChange(record, true, suppressEvent, commit);
420 if (!suppressEvent) {
421 me.setLastFocused(record);
423 me.maybeFireSelectionChange(!suppressEvent);
427 <span id='Ext-selection-Model-method-setLastFocused'> /**
428 </span> * Sets a record as the last focused record. This does NOT mean
429 * that the record has been selected.
430 * @param {Ext.data.Model} record
432 setLastFocused: function(record, supressFocus) {
434 recordBeforeLast = me.lastFocused;
435 me.lastFocused = record;
436 me.onLastFocusChanged(recordBeforeLast, record, supressFocus);
439 <span id='Ext-selection-Model-method-isFocused'> /**
440 </span> * Determines if this record is currently focused.
441 * @param {Ext.data.Model} record
443 isFocused: function(record) {
444 return record === this.getLastFocused();
448 // fire selection change as long as true is not passed
449 // into maybeFireSelectionChange
450 maybeFireSelectionChange: function(fireEvent) {
452 if (fireEvent && !me.bulkChange) {
453 me.fireEvent('selectionchange', me, me.getSelection());
457 <span id='Ext-selection-Model-method-getLastSelected'> /**
458 </span> * Returns the last selected record.
460 getLastSelected: function() {
461 return this.lastSelected;
464 getLastFocused: function() {
465 return this.lastFocused;
468 <span id='Ext-selection-Model-method-getSelection'> /**
469 </span> * Returns an array of the currently selected records.
470 * @return {Ext.data.Model[]} The selected records
472 getSelection: function() {
473 return this.selected.getRange();
476 <span id='Ext-selection-Model-method-getSelectionMode'> /**
477 </span> * Returns the current selectionMode.
478 * @return {String} The selectionMode: 'SINGLE', 'MULTI' or 'SIMPLE'.
480 getSelectionMode: function() {
481 return this.selectionMode;
484 <span id='Ext-selection-Model-method-setSelectionMode'> /**
485 </span> * Sets the current selectionMode.
486 * @param {String} selModel 'SINGLE', 'MULTI' or 'SIMPLE'.
488 setSelectionMode: function(selMode) {
489 selMode = selMode ? selMode.toUpperCase() : 'SINGLE';
490 // set to mode specified unless it doesnt exist, in that case
492 this.selectionMode = this.modes[selMode] ? selMode : 'SINGLE';
495 <span id='Ext-selection-Model-method-isLocked'> /**
496 </span> * Returns true if the selections are locked.
499 isLocked: function() {
503 <span id='Ext-selection-Model-method-setLocked'> /**
504 </span> * Locks the current selection and disables any changes from happening to the selection.
505 * @param {Boolean} locked True to lock, false to unlock.
507 setLocked: function(locked) {
508 this.locked = !!locked;
511 <span id='Ext-selection-Model-method-isSelected'> /**
512 </span> * Returns true if the specified row is selected.
513 * @param {Ext.data.Model/Number} record The record or index of the record to check
516 isSelected: function(record) {
517 record = Ext.isNumber(record) ? this.store.getAt(record) : record;
518 return this.selected.indexOf(record) !== -1;
521 <span id='Ext-selection-Model-method-hasSelection'> /**
522 </span> * Returns true if there are any a selected records.
525 hasSelection: function() {
526 return this.selected.getCount() > 0;
529 refresh: function() {
532 oldSelections = me.getSelection(),
533 len = oldSelections.length,
537 lastFocused = this.getLastFocused();
539 // check to make sure that there are no records
540 // missing after the refresh was triggered, prune
541 // them from what is to be selected if so
542 for (; i < len; i++) {
543 selection = oldSelections[i];
544 if (!this.pruneRemoved || me.store.indexOf(selection) !== -1) {
545 toBeSelected.push(selection);
549 // there was a change from the old selected and
551 if (me.selected.getCount() != toBeSelected.length) {
555 me.clearSelections();
557 if (me.store.indexOf(lastFocused) !== -1) {
558 // restore the last focus but supress restoring focus
559 this.setLastFocused(lastFocused, true);
562 if (toBeSelected.length) {
563 // perform the selection again
564 me.doSelect(toBeSelected, false, true);
567 me.maybeFireSelectionChange(change);
570 <span id='Ext-selection-Model-method-clearSelections'> /**
571 </span> * A fast reset of the selections without firing events, updating the ui, etc.
572 * For private usage only.
575 clearSelections: function() {
576 // reset the entire selection to nothing
577 this.selected.clear();
578 this.lastSelected = null;
579 this.setLastFocused(null);
582 // when a record is added to a store
583 onStoreAdd: function() {
587 // when a store is cleared remove all selections
588 // (if there were any)
589 onStoreClear: function() {
590 if (this.selected.getCount > 0) {
591 this.clearSelections();
592 this.maybeFireSelectionChange(true);
596 // prune records from the SelectionModel if
597 // they were selected at the time they were
599 onStoreRemove: function(store, record) {
601 selected = me.selected;
603 if (me.locked || !me.pruneRemoved) {
607 if (selected.remove(record)) {
608 if (me.lastSelected == record) {
609 me.lastSelected = null;
611 if (me.getLastFocused() == record) {
612 me.setLastFocused(null);
614 me.maybeFireSelectionChange(true);
618 <span id='Ext-selection-Model-method-getCount'> /**
619 </span> * Returns the count of selected records.
620 * @return {Number} The number of selected records
622 getCount: function() {
623 return this.selected.getCount();
627 destroy: function() {
631 // if records are updated
632 onStoreUpdate: function() {
637 onSelectChange: function(record, isSelected, suppressEvent) {
642 onLastFocusChanged: function(oldFocused, newFocused) {
647 onEditorKey: function(field, e) {
652 bindComponent: function(cmp) {