Upgrade to ExtJS 4.0.7 - Released 10/19/2011
[extjs.git] / docs / source / Model2.html
index 3ac66bb..7a717ec 100644 (file)
@@ -1,45 +1,61 @@
-<!DOCTYPE html><html><head><title>Sencha Documentation Project</title><link rel="stylesheet" href="../reset.css" type="text/css"><link rel="stylesheet" href="../prettify.css" type="text/css"><link rel="stylesheet" href="../prettify_sa.css" type="text/css"><script type="text/javascript" src="../prettify.js"></script></head><body onload="prettyPrint()"><pre class="prettyprint"><pre><span id='Ext-selection.Model'>/**
-</span> * @class Ext.selection.Model
- * @extends Ext.util.Observable
- *
- * Tracks what records are currently selected in a databound widget.
- *
- * This is an abstract class and is not meant to be directly used.
+<!DOCTYPE html>
+<html>
+<head>
+  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+  <title>The source code</title>
+  <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
+  <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
+  <style type="text/css">
+    .highlight { display: block; background-color: #ddd; }
+  </style>
+  <script type="text/javascript">
+    function highlight() {
+      document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
+    }
+  </script>
+</head>
+<body onload="prettyPrint(); highlight();">
+  <pre class="prettyprint lang-js"><span id='Ext-selection-Model'>/**
+</span> * Tracks what records are currently selected in a databound component.
  *
- * DataBound UI widgets such as GridPanel, TreePanel, and ListView
- * should subclass AbstractStoreSelectionModel and provide a way
- * to binding to the component.
+ * This is an abstract class and is not meant to be directly used. Databound UI widgets such as
+ * {@link Ext.grid.Panel Grid} and {@link Ext.tree.Panel Tree} should subclass Ext.selection.Model
+ * and provide a way to binding to the component.
  *
- * The abstract methods onSelectChange and onLastFocusChanged should
- * be implemented in these subclasses to update the UI widget.
+ * The abstract methods `onSelectChange` and `onLastFocusChanged` should be implemented in these
+ * subclasses to update the UI widget.
  */
 Ext.define('Ext.selection.Model', {
     extend: 'Ext.util.Observable',
-    alternateClassName: 'Ext.AbstractStoreSelectionModel',
+    alternateClassName: 'Ext.AbstractSelectionModel',
     requires: ['Ext.data.StoreManager'],
     // lastSelected
 
-<span id='Ext-selection.Model-cfg-mode'>    /**
+<span id='Ext-selection-Model-cfg-mode'>    /**
 </span>     * @cfg {String} mode
-     * Modes of selection.
-     * Valid values are SINGLE, SIMPLE, and MULTI. Defaults to 'SINGLE'
+     * Mode of selection.  Valid values are:
+     *
+     * - **SINGLE** - Only allows selecting one item at a time.  Use {@link #allowDeselect} to allow
+     *   deselecting that item.  This is the default.
+     * - **SIMPLE** - Allows simple selection of multiple items one-by-one. Each click in grid will either
+     *   select or deselect an item.
+     * - **MULTI** - Allows complex selection of multiple items using Ctrl and Shift keys.
      */
-    
-<span id='Ext-selection.Model-cfg-allowDeselect'>    /**
+
+<span id='Ext-selection-Model-cfg-allowDeselect'>    /**
 </span>     * @cfg {Boolean} allowDeselect
-     * Allow users to deselect a record in a DataView, List or Grid. Only applicable when the SelectionModel's mode is 'SINGLE'. Defaults to false.
+     * Allow users to deselect a record in a DataView, List or Grid.
+     * Only applicable when the {@link #mode} is 'SINGLE'.
      */
     allowDeselect: false,
 
-<span id='Ext-selection.Model-property-selected'>    /**
-</span>     * @property selected
-     * READ-ONLY A MixedCollection that maintains all of the currently selected
-     * records.
+<span id='Ext-selection-Model-property-selected'>    /**
+</span>     * @property {Ext.util.MixedCollection} selected
+     * A MixedCollection that maintains all of the currently selected records. Read-only.
      */
     selected: null,
-    
-    
-<span id='Ext-selection.Model-property-pruneRemoved'>    /**
+
+<span id='Ext-selection-Model-property-pruneRemoved'>    /**
 </span>     * Prune records when they are removed from the store from the selection.
      * This is a private flag. For an example of its usage, take a look at
      * Ext.selection.TreeModel.
@@ -49,18 +65,18 @@ Ext.define('Ext.selection.Model', {
 
     constructor: function(cfg) {
         var me = this;
-        
+
         cfg = cfg || {};
         Ext.apply(me, cfg);
-        
+
         me.addEvents(
-<span id='Ext-selection.Model-event-selectionchange'>            /**
-</span>             * @event selectionchange
+<span id='Ext-selection-Model-event-selectionchange'>            /**
+</span>             * @event
              * Fired after a selection change has occurred
              * @param {Ext.selection.Model} this
-             * @param  {Array} selected The selected records
+             * @param {Ext.data.Model[]} selected The selected records
              */
-             'selectionchange'
+            'selectionchange'
         );
 
         me.modes = {
@@ -74,17 +90,17 @@ Ext.define('Ext.selection.Model', {
 
         // maintains the currently selected records.
         me.selected = Ext.create('Ext.util.MixedCollection');
-        
+
         me.callParent(arguments);
     },
 
     // binds the store to the selModel.
     bind : function(store, initial){
         var me = this;
-        
+
         if(!initial &amp;&amp; me.store){
             if(store !== me.store &amp;&amp; me.store.autoDestroy){
-                me.store.destroy();
+                me.store.destroyStore();
             }else{
                 me.store.un(&quot;add&quot;, me.onStoreAdd, me);
                 me.store.un(&quot;clear&quot;, me.onStoreClear, me);
@@ -108,32 +124,52 @@ Ext.define('Ext.selection.Model', {
         }
     },
 
-    selectAll: function(silent) {
-        var selections = this.store.getRange(),
+<span id='Ext-selection-Model-method-selectAll'>    /**
+</span>     * Selects all records in the view.
+     * @param {Boolean} suppressEvent True to suppress any select events
+     */
+    selectAll: function(suppressEvent) {
+        var me = this,
+            selections = me.store.getRange(),
             i = 0,
-            len = selections.length;
-            
+            len = selections.length,
+            start = me.getSelection().length;
+
+        me.bulkChange = true;
         for (; i &lt; len; i++) {
-            this.doSelect(selections[i], true, silent);
+            me.doSelect(selections[i], true, suppressEvent);
         }
+        delete me.bulkChange;
+        // fire selection change only if the number of selections differs
+        me.maybeFireSelectionChange(me.getSelection().length !== start);
     },
 
-    deselectAll: function() {
-        var selections = this.getSelection(),
+<span id='Ext-selection-Model-method-deselectAll'>    /**
+</span>     * Deselects all records in the view.
+     * @param {Boolean} suppressEvent True to suppress any deselect events
+     */
+    deselectAll: function(suppressEvent) {
+        var me = this,
+            selections = me.getSelection(),
             i = 0,
-            len = selections.length;
-            
+            len = selections.length,
+            start = me.getSelection().length;
+
+        me.bulkChange = true;
         for (; i &lt; len; i++) {
-            this.doDeselect(selections[i]);
+            me.doDeselect(selections[i], suppressEvent);
         }
+        delete me.bulkChange;
+        // fire selection change only if the number of selections differs
+        me.maybeFireSelectionChange(me.getSelection().length !== start);
     },
 
     // Provides differentiation of logic between MULTI, SIMPLE and SINGLE
     // selection modes. Requires that an event be passed so that we can know
     // if user held ctrl or shift.
-    selectWithEvent: function(record, e) {
+    selectWithEvent: function(record, e, keepExisting) {
         var me = this;
-        
+
         switch (me.selectionMode) {
             case 'MULTI':
                 if (e.ctrlKey &amp;&amp; me.isSelected(record)) {
@@ -143,7 +179,7 @@ Ext.define('Ext.selection.Model', {
                 } else if (e.ctrlKey) {
                     me.doSelect(record, true, false);
                 } else if (me.isSelected(record) &amp;&amp; !e.shiftKey &amp;&amp; !e.ctrlKey &amp;&amp; me.selected.getCount() &gt; 1) {
-                    me.doSelect(record, false, false);
+                    me.doSelect(record, keepExisting, false);
                 } else {
                     me.doSelect(record, false);
                 }
@@ -167,12 +203,12 @@ Ext.define('Ext.selection.Model', {
         }
     },
 
-<span id='Ext-selection.Model-method-selectRange'>    /**
+<span id='Ext-selection-Model-method-selectRange'>    /**
 </span>     * Selects a range of rows if the selection model {@link #isLocked is not locked}.
      * All rows in between startRow and endRow are also selected.
      * @param {Ext.data.Model/Number} startRow The record or index of the first row in the range
      * @param {Ext.data.Model/Number} endRow The record or index of the last row in the range
-     * @param {Boolean} keepExisting (optional) True to retain existing selections
+     * @param {Boolean} [keepExisting] True to retain existing selections
      */
     selectRange : function(startRow, endRow, keepExisting, dir){
         var me = this,
@@ -182,22 +218,22 @@ Ext.define('Ext.selection.Model', {
             tmp,
             dontDeselect,
             records = [];
-        
+
         if (me.isLocked()){
             return;
         }
-        
+
         if (!keepExisting) {
-            me.clearSelections();
+            me.deselectAll(true);
         }
-        
+
         if (!Ext.isNumber(startRow)) {
             startRow = store.indexOf(startRow);
-        } 
+        }
         if (!Ext.isNumber(endRow)) {
             endRow = store.indexOf(endRow);
         }
-        
+
         // swap values
         if (startRow &gt; endRow){
             tmp = endRow;
@@ -216,7 +252,7 @@ Ext.define('Ext.selection.Model', {
         } else {
             dontDeselect = (dir == 'up') ? startRow : endRow;
         }
-        
+
         for (i = startRow; i &lt;= endRow; i++){
             if (selectedCount == (endRow - startRow + 1)) {
                 if (i != dontDeselect) {
@@ -228,30 +264,33 @@ Ext.define('Ext.selection.Model', {
         }
         me.doMultiSelect(records, true);
     },
-    
-<span id='Ext-selection.Model-method-select'>    /**
+
+<span id='Ext-selection-Model-method-select'>    /**
 </span>     * Selects a record instance by record instance or index.
-     * @param {Ext.data.Model/Index} records An array of records or an index
-     * @param {Boolean} keepExisting
-     * @param {Boolean} suppressEvent Set to false to not fire a select event
+     * @param {Ext.data.Model[]/Number} records An array of records or an index
+     * @param {Boolean} [keepExisting] True to retain existing selections
+     * @param {Boolean} [suppressEvent] Set to true to not fire a select event
      */
     select: function(records, keepExisting, suppressEvent) {
-        this.doSelect(records, keepExisting, suppressEvent);
+        // Automatically selecting eg store.first() or store.last() will pass undefined, so that must just return;
+        if (Ext.isDefined(records)) {
+            this.doSelect(records, keepExisting, suppressEvent);
+        }
     },
 
-<span id='Ext-selection.Model-method-deselect'>    /**
+<span id='Ext-selection-Model-method-deselect'>    /**
 </span>     * Deselects a record instance by record instance or index.
-     * @param {Ext.data.Model/Index} records An array of records or an index
-     * @param {Boolean} suppressEvent Set to false to not fire a deselect event
+     * @param {Ext.data.Model[]/Number} records An array of records or an index
+     * @param {Boolean} [suppressEvent] Set to true to not fire a deselect event
      */
     deselect: function(records, suppressEvent) {
         this.doDeselect(records, suppressEvent);
     },
-    
+
     doSelect: function(records, keepExisting, suppressEvent) {
         var me = this,
             record;
-            
+
         if (me.locked) {
             return;
         }
@@ -272,17 +311,24 @@ Ext.define('Ext.selection.Model', {
             change = false,
             i = 0,
             len, record;
-            
+
         if (me.locked) {
             return;
         }
-        
+
 
         records = !Ext.isArray(records) ? [records] : records;
         len = records.length;
         if (!keepExisting &amp;&amp; selected.getCount() &gt; 0) {
+            if (me.doDeselect(me.getSelection(), suppressEvent) === false) {
+                return;
+            }
+            // TODO - coalesce the selectionchange event in deselect w/the one below...
+        }
+
+        function commit () {
+            selected.add(record);
             change = true;
-            me.doDeselect(me.getSelection(), true);
         }
 
         for (; i &lt; len; i++) {
@@ -290,11 +336,9 @@ Ext.define('Ext.selection.Model', {
             if (keepExisting &amp;&amp; me.isSelected(record)) {
                 continue;
             }
-            change = true;
             me.lastSelected = record;
-            selected.add(record);
 
-            me.onSelectChange(record, true, suppressEvent);
+            me.onSelectChange(record, true, suppressEvent, commit);
         }
         me.setLastFocused(record, suppressEvent);
         // fire selchange if there was a change and there is no suppressEvent flag
@@ -305,38 +349,49 @@ Ext.define('Ext.selection.Model', {
     doDeselect: function(records, suppressEvent) {
         var me = this,
             selected = me.selected,
-            change = false,
             i = 0,
-            len, record;
-            
+            len, record,
+            attempted = 0,
+            accepted = 0;
+
         if (me.locked) {
-            return;
+            return false;
         }
 
         if (typeof records === &quot;number&quot;) {
             records = [me.store.getAt(records)];
+        } else if (!Ext.isArray(records)) {
+            records = [records];
+        }
+
+        function commit () {
+            ++accepted;
+            selected.remove(record);
         }
 
-        records = !Ext.isArray(records) ? [records] : records;
         len = records.length;
+
         for (; i &lt; len; i++) {
             record = records[i];
-            if (selected.remove(record)) {
+            if (me.isSelected(record)) {
                 if (me.lastSelected == record) {
                     me.lastSelected = selected.last();
                 }
-                me.onSelectChange(record, false, suppressEvent);
-                change = true;
+                ++attempted;
+                me.onSelectChange(record, false, suppressEvent, commit);
             }
         }
+
         // fire selchange if there was a change and there is no suppressEvent flag
-        me.maybeFireSelectionChange(change &amp;&amp; !suppressEvent);
+        me.maybeFireSelectionChange(accepted &gt; 0 &amp;&amp; !suppressEvent);
+        return accepted === attempted;
     },
 
     doSingleSelect: function(record, suppressEvent) {
         var me = this,
+            changed = false,
             selected = me.selected;
-            
+
         if (me.locked) {
             return;
         }
@@ -345,22 +400,34 @@ Ext.define('Ext.selection.Model', {
         if (me.isSelected(record)) {
             return;
         }
-        if (selected.getCount() &gt; 0) {
-            me.doDeselect(me.lastSelected, suppressEvent);
+
+        function commit () {
+            me.bulkChange = true;
+            if (selected.getCount() &gt; 0 &amp;&amp; me.doDeselect(me.lastSelected, suppressEvent) === false) {
+                delete me.bulkChange;
+                return false;
+            }
+            delete me.bulkChange;
+
+            selected.add(record);
+            me.lastSelected = record;
+            changed = true;
         }
-        selected.add(record);
-        me.lastSelected = record;
-        me.onSelectChange(record, true, suppressEvent);
-        if (!suppressEvent) {
-            me.setLastFocused(record);
+
+        me.onSelectChange(record, true, suppressEvent, commit);
+
+        if (changed) {
+            if (!suppressEvent) {
+                me.setLastFocused(record);
+            }
+            me.maybeFireSelectionChange(!suppressEvent);
         }
-        me.maybeFireSelectionChange(!suppressEvent);
     },
 
-<span id='Ext-selection.Model-method-setLastFocused'>    /**
-</span>     * @param {Ext.data.Model} record
-     * Set a record as the last focused record. This does NOT mean
+<span id='Ext-selection-Model-method-setLastFocused'>    /**
+</span>     * Sets a record as the last focused record. This does NOT mean
      * that the record has been selected.
+     * @param {Ext.data.Model} record
      */
     setLastFocused: function(record, supressFocus) {
         var me = this,
@@ -368,10 +435,10 @@ Ext.define('Ext.selection.Model', {
         me.lastFocused = record;
         me.onLastFocusChanged(recordBeforeLast, record, supressFocus);
     },
-    
-<span id='Ext-selection.Model-method-isFocused'>    /**
+
+<span id='Ext-selection-Model-method-isFocused'>    /**
 </span>     * Determines if this record is currently focused.
-     * @param Ext.data.Record record
+     * @param {Ext.data.Model} record
      */
     isFocused: function(record) {
         return record === this.getLastFocused();
@@ -381,39 +448,42 @@ Ext.define('Ext.selection.Model', {
     // fire selection change as long as true is not passed
     // into maybeFireSelectionChange
     maybeFireSelectionChange: function(fireEvent) {
-        if (fireEvent) {
-            var me = this;
+        var me = this;
+        if (fireEvent &amp;&amp; !me.bulkChange) {
             me.fireEvent('selectionchange', me, me.getSelection());
         }
     },
 
-<span id='Ext-selection.Model-method-getLastSelected'>    /**
+<span id='Ext-selection-Model-method-getLastSelected'>    /**
 </span>     * Returns the last selected record.
      */
     getLastSelected: function() {
         return this.lastSelected;
     },
-    
+
     getLastFocused: function() {
         return this.lastFocused;
     },
 
-<span id='Ext-selection.Model-method-getSelection'>    /**
+<span id='Ext-selection-Model-method-getSelection'>    /**
 </span>     * Returns an array of the currently selected records.
+     * @return {Ext.data.Model[]} The selected records
      */
     getSelection: function() {
         return this.selected.getRange();
     },
 
-<span id='Ext-selection.Model-method-getSelectionMode'>    /**
-</span>     * Returns the current selectionMode. SINGLE, MULTI or SIMPLE.
+<span id='Ext-selection-Model-method-getSelectionMode'>    /**
+</span>     * Returns the current selectionMode.
+     * @return {String} The selectionMode: 'SINGLE', 'MULTI' or 'SIMPLE'.
      */
     getSelectionMode: function() {
         return this.selectionMode;
     },
 
-<span id='Ext-selection.Model-method-setSelectionMode'>    /**
-</span>     * Sets the current selectionMode. SINGLE, MULTI or SIMPLE.
+<span id='Ext-selection-Model-method-setSelectionMode'>    /**
+</span>     * Sets the current selectionMode.
+     * @param {String} selModel 'SINGLE', 'MULTI' or 'SIMPLE'.
      */
     setSelectionMode: function(selMode) {
         selMode = selMode ? selMode.toUpperCase() : 'SINGLE';
@@ -422,7 +492,7 @@ Ext.define('Ext.selection.Model', {
         this.selectionMode = this.modes[selMode] ? selMode : 'SINGLE';
     },
 
-<span id='Ext-selection.Model-method-isLocked'>    /**
+<span id='Ext-selection-Model-method-isLocked'>    /**
 </span>     * Returns true if the selections are locked.
      * @return {Boolean}
      */
@@ -430,27 +500,26 @@ Ext.define('Ext.selection.Model', {
         return this.locked;
     },
 
-<span id='Ext-selection.Model-method-setLocked'>    /**
-</span>     * Locks the current selection and disables any changes from
-     * happening to the selection.
-     * @param {Boolean} locked
+<span id='Ext-selection-Model-method-setLocked'>    /**
+</span>     * Locks the current selection and disables any changes from happening to the selection.
+     * @param {Boolean} locked  True to lock, false to unlock.
      */
     setLocked: function(locked) {
         this.locked = !!locked;
     },
 
-<span id='Ext-selection.Model-method-isSelected'>    /**
-</span>     * Returns &lt;tt&gt;true&lt;/tt&gt; if the specified row is selected.
-     * @param {Record/Number} record The record or index of the record to check
+<span id='Ext-selection-Model-method-isSelected'>    /**
+</span>     * Returns true if the specified row is selected.
+     * @param {Ext.data.Model/Number} record The record or index of the record to check
      * @return {Boolean}
      */
     isSelected: function(record) {
         record = Ext.isNumber(record) ? this.store.getAt(record) : record;
         return this.selected.indexOf(record) !== -1;
     },
-    
-<span id='Ext-selection.Model-method-hasSelection'>    /**
-</span>     * Returns true if there is a selected record.
+
+<span id='Ext-selection-Model-method-hasSelection'>    /**
+</span>     * Returns true if there are any a selected records.
      * @return {Boolean}
      */
     hasSelection: function() {
@@ -484,7 +553,7 @@ Ext.define('Ext.selection.Model', {
         }
 
         me.clearSelections();
-        
+
         if (me.store.indexOf(lastFocused) !== -1) {
             // restore the last focus but supress restoring focus
             this.setLastFocused(lastFocused, true);
@@ -494,16 +563,20 @@ Ext.define('Ext.selection.Model', {
             // perform the selection again
             me.doSelect(toBeSelected, false, true);
         }
-        
+
         me.maybeFireSelectionChange(change);
     },
 
+<span id='Ext-selection-Model-method-clearSelections'>    /**
+</span>     * A fast reset of the selections without firing events, updating the ui, etc.
+     * For private usage only.
+     * @private
+     */
     clearSelections: function() {
         // reset the entire selection to nothing
-        var me = this;
-        me.selected.clear();
-        me.lastSelected = null;
-        me.setLastFocused(null);
+        this.selected.clear();
+        this.lastSelected = null;
+        this.setLastFocused(null);
     },
 
     // when a record is added to a store
@@ -514,14 +587,9 @@ Ext.define('Ext.selection.Model', {
     // when a store is cleared remove all selections
     // (if there were any)
     onStoreClear: function() {
-        var me = this,
-            selected = this.selected;
-            
-        if (selected.getCount &gt; 0) {
-            selected.clear();
-            me.lastSelected = null;
-            me.setLastFocused(null);
-            me.maybeFireSelectionChange(true);
+        if (this.selected.getCount &gt; 0) {
+            this.clearSelections();
+            this.maybeFireSelectionChange(true);
         }
     },
 
@@ -531,7 +599,7 @@ Ext.define('Ext.selection.Model', {
     onStoreRemove: function(store, record) {
         var me = this,
             selected = me.selected;
-            
+
         if (me.locked || !me.pruneRemoved) {
             return;
         }
@@ -547,6 +615,10 @@ Ext.define('Ext.selection.Model', {
         }
     },
 
+<span id='Ext-selection-Model-method-getCount'>    /**
+</span>     * Returns the count of selected records.
+     * @return {Number} The number of selected records
+     */
     getCount: function() {
         return this.selected.getCount();
     },
@@ -580,4 +652,6 @@ Ext.define('Ext.selection.Model', {
     bindComponent: function(cmp) {
 
     }
-});</pre></pre></body></html>
\ No newline at end of file
+});</pre>
+</body>
+</html>