Upgrade to ExtJS 4.0.0 - Released 04/26/2011
[extjs.git] / docs / source / Model2.html
diff --git a/docs/source/Model2.html b/docs/source/Model2.html
new file mode 100644 (file)
index 0000000..3ac66bb
--- /dev/null
@@ -0,0 +1,583 @@
+<!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.
+ *
+ * DataBound UI widgets such as GridPanel, TreePanel, and ListView
+ * should subclass AbstractStoreSelectionModel 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.
+ */
+Ext.define('Ext.selection.Model', {
+    extend: 'Ext.util.Observable',
+    alternateClassName: 'Ext.AbstractStoreSelectionModel',
+    requires: ['Ext.data.StoreManager'],
+    // lastSelected
+
+<span id='Ext-selection.Model-cfg-mode'>    /**
+</span>     * @cfg {String} mode
+     * Modes of selection.
+     * Valid values are SINGLE, SIMPLE, and MULTI. Defaults to 'SINGLE'
+     */
+    
+<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.
+     */
+    allowDeselect: false,
+
+<span id='Ext-selection.Model-property-selected'>    /**
+</span>     * @property selected
+     * READ-ONLY A MixedCollection that maintains all of the currently selected
+     * records.
+     */
+    selected: null,
+    
+    
+<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.
+     * @private
+     */
+    pruneRemoved: true,
+
+    constructor: function(cfg) {
+        var me = this;
+        
+        cfg = cfg || {};
+        Ext.apply(me, cfg);
+        
+        me.addEvents(
+<span id='Ext-selection.Model-event-selectionchange'>            /**
+</span>             * @event selectionchange
+             * Fired after a selection change has occurred
+             * @param {Ext.selection.Model} this
+             * @param  {Array} selected The selected records
+             */
+             'selectionchange'
+        );
+
+        me.modes = {
+            SINGLE: true,
+            SIMPLE: true,
+            MULTI: true
+        };
+
+        // sets this.selectionMode
+        me.setSelectionMode(cfg.mode || me.mode);
+
+        // 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();
+            }else{
+                me.store.un(&quot;add&quot;, me.onStoreAdd, me);
+                me.store.un(&quot;clear&quot;, me.onStoreClear, me);
+                me.store.un(&quot;remove&quot;, me.onStoreRemove, me);
+                me.store.un(&quot;update&quot;, me.onStoreUpdate, me);
+            }
+        }
+        if(store){
+            store = Ext.data.StoreManager.lookup(store);
+            store.on({
+                add: me.onStoreAdd,
+                clear: me.onStoreClear,
+                remove: me.onStoreRemove,
+                update: me.onStoreUpdate,
+                scope: me
+            });
+        }
+        me.store = store;
+        if(store &amp;&amp; !initial) {
+            me.refresh();
+        }
+    },
+
+    selectAll: function(silent) {
+        var selections = this.store.getRange(),
+            i = 0,
+            len = selections.length;
+            
+        for (; i &lt; len; i++) {
+            this.doSelect(selections[i], true, silent);
+        }
+    },
+
+    deselectAll: function() {
+        var selections = this.getSelection(),
+            i = 0,
+            len = selections.length;
+            
+        for (; i &lt; len; i++) {
+            this.doDeselect(selections[i]);
+        }
+    },
+
+    // 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) {
+        var me = this;
+        
+        switch (me.selectionMode) {
+            case 'MULTI':
+                if (e.ctrlKey &amp;&amp; me.isSelected(record)) {
+                    me.doDeselect(record, false);
+                } else if (e.shiftKey &amp;&amp; me.lastFocused) {
+                    me.selectRange(me.lastFocused, record, e.ctrlKey);
+                } 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);
+                } else {
+                    me.doSelect(record, false);
+                }
+                break;
+            case 'SIMPLE':
+                if (me.isSelected(record)) {
+                    me.doDeselect(record);
+                } else {
+                    me.doSelect(record, true);
+                }
+                break;
+            case 'SINGLE':
+                // if allowDeselect is on and this record isSelected, deselect it
+                if (me.allowDeselect &amp;&amp; me.isSelected(record)) {
+                    me.doDeselect(record);
+                // select the record and do NOT maintain existing selections
+                } else {
+                    me.doSelect(record, false);
+                }
+                break;
+        }
+    },
+
+<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
+     */
+    selectRange : function(startRow, endRow, keepExisting, dir){
+        var me = this,
+            store = me.store,
+            selectedCount = 0,
+            i,
+            tmp,
+            dontDeselect,
+            records = [];
+        
+        if (me.isLocked()){
+            return;
+        }
+        
+        if (!keepExisting) {
+            me.clearSelections();
+        }
+        
+        if (!Ext.isNumber(startRow)) {
+            startRow = store.indexOf(startRow);
+        } 
+        if (!Ext.isNumber(endRow)) {
+            endRow = store.indexOf(endRow);
+        }
+        
+        // swap values
+        if (startRow &gt; endRow){
+            tmp = endRow;
+            endRow = startRow;
+            startRow = tmp;
+        }
+
+        for (i = startRow; i &lt;= endRow; i++) {
+            if (me.isSelected(store.getAt(i))) {
+                selectedCount++;
+            }
+        }
+
+        if (!dir) {
+            dontDeselect = -1;
+        } else {
+            dontDeselect = (dir == 'up') ? startRow : endRow;
+        }
+        
+        for (i = startRow; i &lt;= endRow; i++){
+            if (selectedCount == (endRow - startRow + 1)) {
+                if (i != dontDeselect) {
+                    me.doDeselect(i, true);
+                }
+            } else {
+                records.push(store.getAt(i));
+            }
+        }
+        me.doMultiSelect(records, true);
+    },
+    
+<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
+     */
+    select: function(records, keepExisting, suppressEvent) {
+        this.doSelect(records, keepExisting, suppressEvent);
+    },
+
+<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
+     */
+    deselect: function(records, suppressEvent) {
+        this.doDeselect(records, suppressEvent);
+    },
+    
+    doSelect: function(records, keepExisting, suppressEvent) {
+        var me = this,
+            record;
+            
+        if (me.locked) {
+            return;
+        }
+        if (typeof records === &quot;number&quot;) {
+            records = [me.store.getAt(records)];
+        }
+        if (me.selectionMode == &quot;SINGLE&quot; &amp;&amp; records) {
+            record = records.length ? records[0] : records;
+            me.doSingleSelect(record, suppressEvent);
+        } else {
+            me.doMultiSelect(records, keepExisting, suppressEvent);
+        }
+    },
+
+    doMultiSelect: function(records, keepExisting, suppressEvent) {
+        var me = this,
+            selected = me.selected,
+            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) {
+            change = true;
+            me.doDeselect(me.getSelection(), true);
+        }
+
+        for (; i &lt; len; i++) {
+            record = records[i];
+            if (keepExisting &amp;&amp; me.isSelected(record)) {
+                continue;
+            }
+            change = true;
+            me.lastSelected = record;
+            selected.add(record);
+
+            me.onSelectChange(record, true, suppressEvent);
+        }
+        me.setLastFocused(record, suppressEvent);
+        // fire selchange if there was a change and there is no suppressEvent flag
+        me.maybeFireSelectionChange(change &amp;&amp; !suppressEvent);
+    },
+
+    // records can be an index, a record or an array of records
+    doDeselect: function(records, suppressEvent) {
+        var me = this,
+            selected = me.selected,
+            change = false,
+            i = 0,
+            len, record;
+            
+        if (me.locked) {
+            return;
+        }
+
+        if (typeof records === &quot;number&quot;) {
+            records = [me.store.getAt(records)];
+        }
+
+        records = !Ext.isArray(records) ? [records] : records;
+        len = records.length;
+        for (; i &lt; len; i++) {
+            record = records[i];
+            if (selected.remove(record)) {
+                if (me.lastSelected == record) {
+                    me.lastSelected = selected.last();
+                }
+                me.onSelectChange(record, false, suppressEvent);
+                change = true;
+            }
+        }
+        // fire selchange if there was a change and there is no suppressEvent flag
+        me.maybeFireSelectionChange(change &amp;&amp; !suppressEvent);
+    },
+
+    doSingleSelect: function(record, suppressEvent) {
+        var me = this,
+            selected = me.selected;
+            
+        if (me.locked) {
+            return;
+        }
+        // already selected.
+        // should we also check beforeselect?
+        if (me.isSelected(record)) {
+            return;
+        }
+        if (selected.getCount() &gt; 0) {
+            me.doDeselect(me.lastSelected, suppressEvent);
+        }
+        selected.add(record);
+        me.lastSelected = record;
+        me.onSelectChange(record, true, suppressEvent);
+        if (!suppressEvent) {
+            me.setLastFocused(record);
+        }
+        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
+     * that the record has been selected.
+     */
+    setLastFocused: function(record, supressFocus) {
+        var me = this,
+            recordBeforeLast = me.lastFocused;
+        me.lastFocused = record;
+        me.onLastFocusChanged(recordBeforeLast, record, supressFocus);
+    },
+    
+<span id='Ext-selection.Model-method-isFocused'>    /**
+</span>     * Determines if this record is currently focused.
+     * @param Ext.data.Record record
+     */
+    isFocused: function(record) {
+        return record === this.getLastFocused();
+    },
+
+
+    // fire selection change as long as true is not passed
+    // into maybeFireSelectionChange
+    maybeFireSelectionChange: function(fireEvent) {
+        if (fireEvent) {
+            var me = this;
+            me.fireEvent('selectionchange', me, me.getSelection());
+        }
+    },
+
+<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>     * Returns an array of the currently selected records.
+     */
+    getSelection: function() {
+        return this.selected.getRange();
+    },
+
+<span id='Ext-selection.Model-method-getSelectionMode'>    /**
+</span>     * Returns the current 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.
+     */
+    setSelectionMode: function(selMode) {
+        selMode = selMode ? selMode.toUpperCase() : 'SINGLE';
+        // set to mode specified unless it doesnt exist, in that case
+        // use single.
+        this.selectionMode = this.modes[selMode] ? selMode : 'SINGLE';
+    },
+
+<span id='Ext-selection.Model-method-isLocked'>    /**
+</span>     * Returns true if the selections are locked.
+     * @return {Boolean}
+     */
+    isLocked: function() {
+        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
+     */
+    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
+     * @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.
+     * @return {Boolean}
+     */
+    hasSelection: function() {
+        return this.selected.getCount() &gt; 0;
+    },
+
+    refresh: function() {
+        var me = this,
+            toBeSelected = [],
+            oldSelections = me.getSelection(),
+            len = oldSelections.length,
+            selection,
+            change,
+            i = 0,
+            lastFocused = this.getLastFocused();
+
+        // check to make sure that there are no records
+        // missing after the refresh was triggered, prune
+        // them from what is to be selected if so
+        for (; i &lt; len; i++) {
+            selection = oldSelections[i];
+            if (!this.pruneRemoved || me.store.indexOf(selection) !== -1) {
+                toBeSelected.push(selection);
+            }
+        }
+
+        // there was a change from the old selected and
+        // the new selection
+        if (me.selected.getCount() != toBeSelected.length) {
+            change = true;
+        }
+
+        me.clearSelections();
+        
+        if (me.store.indexOf(lastFocused) !== -1) {
+            // restore the last focus but supress restoring focus
+            this.setLastFocused(lastFocused, true);
+        }
+
+        if (toBeSelected.length) {
+            // perform the selection again
+            me.doSelect(toBeSelected, false, true);
+        }
+        
+        me.maybeFireSelectionChange(change);
+    },
+
+    clearSelections: function() {
+        // reset the entire selection to nothing
+        var me = this;
+        me.selected.clear();
+        me.lastSelected = null;
+        me.setLastFocused(null);
+    },
+
+    // when a record is added to a store
+    onStoreAdd: function() {
+
+    },
+
+    // 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);
+        }
+    },
+
+    // prune records from the SelectionModel if
+    // they were selected at the time they were
+    // removed.
+    onStoreRemove: function(store, record) {
+        var me = this,
+            selected = me.selected;
+            
+        if (me.locked || !me.pruneRemoved) {
+            return;
+        }
+
+        if (selected.remove(record)) {
+            if (me.lastSelected == record) {
+                me.lastSelected = null;
+            }
+            if (me.getLastFocused() == record) {
+                me.setLastFocused(null);
+            }
+            me.maybeFireSelectionChange(true);
+        }
+    },
+
+    getCount: function() {
+        return this.selected.getCount();
+    },
+
+    // cleanup.
+    destroy: function() {
+
+    },
+
+    // if records are updated
+    onStoreUpdate: function() {
+
+    },
+
+    // @abstract
+    onSelectChange: function(record, isSelected, suppressEvent) {
+
+    },
+
+    // @abstract
+    onLastFocusChanged: function(oldFocused, newFocused) {
+
+    },
+
+    // @abstract
+    onEditorKey: function(field, e) {
+
+    },
+
+    // @abstract
+    bindComponent: function(cmp) {
+
+    }
+});</pre></pre></body></html>
\ No newline at end of file