| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453 | <!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-CellModel'>/**</span> * */Ext.define('Ext.selection.CellModel', {    extend: 'Ext.selection.Model',    alias: 'selection.cellmodel',    requires: ['Ext.util.KeyNav'],    isCellModel: true,<span id='Ext-selection-CellModel-cfg-enableKeyNav'>    /**</span>     * @cfg {Boolean} enableKeyNav     * Turns on/off keyboard navigation within the grid.     */    enableKeyNav: true,<span id='Ext-selection-CellModel-cfg-preventWrap'>    /**</span>     * @cfg {Boolean} preventWrap     * Set this configuration to true to prevent wrapping around of selection as     * a user navigates to the first or last column.     */    preventWrap: false,    // private property to use when firing a deselect when no old selection exists.    noSelection: {        row: -1,        column: -1    },    constructor: function() {        this.addEvents(<span id='Ext-selection-CellModel-event-deselect'>            /**</span>             * @event deselect             * Fired after a cell is deselected             * @param {Ext.selection.CellModel} this             * @param {Ext.data.Model} record The record of the deselected cell             * @param {Number} row The row index deselected             * @param {Number} column The column index deselected             */            'deselect',<span id='Ext-selection-CellModel-event-select'>            /**</span>             * @event select             * Fired after a cell is selected             * @param {Ext.selection.CellModel} this             * @param {Ext.data.Model} record The record of the selected cell             * @param {Number} row The row index selected             * @param {Number} column The column index selected             */            'select'        );        this.callParent(arguments);    },    bindComponent: function(view) {        var me = this,            grid = view.ownerCt;        me.primaryView = view;        me.views = me.views || [];        me.views.push(view);        me.bindStore(view.getStore(), true);        view.on({            cellmousedown: me.onMouseDown,            refresh: me.onViewRefresh,            scope: me        });        if (grid.optimizedColumnMove !== false) {            grid.on('columnmove', me.onColumnMove, me);        }        if (me.enableKeyNav) {            me.initKeyNav(view);        }    },    initKeyNav: function(view) {        var me = this;        if (!view.rendered) {            view.on('render', Ext.Function.bind(me.initKeyNav, me, [view], 0), me, {single: true});            return;        }        view.el.set({            tabIndex: -1        });        // view.el has tabIndex -1 to allow for        // keyboard events to be passed to it.        me.keyNav = new Ext.util.KeyNav({            target: view.el,            ignoreInputFields: true,            up: me.onKeyUp,            down: me.onKeyDown,            right: me.onKeyRight,            left: me.onKeyLeft,            tab: me.onKeyTab,            scope: me        });    },    getHeaderCt: function() {        var selection = this.getCurrentPosition(),            view = selection ? selection.view : this.primaryView;        return view.headerCt;    },    onKeyUp: function(e, t) {        this.keyNavigation = true;        this.move('up', e);        this.keyNavigation = false;    },    onKeyDown: function(e, t) {        this.keyNavigation = true;        this.move('down', e);        this.keyNavigation = false;    },    onKeyLeft: function(e, t) {        this.keyNavigation = true;        this.move('left', e);        this.keyNavigation = false;    },    onKeyRight: function(e, t) {        this.keyNavigation = true;        this.move('right', e);        this.keyNavigation = false;    },    move: function(dir, e) {        var me = this,            pos = me.getCurrentPosition(),                        // Calculate the new row and column position            newPos = pos.view.walkCells(pos, dir, e, me.preventWrap);        // If walk was successful, select new Position        if (newPos) {            newPos.view = pos.view;            return me.setCurrentPosition(newPos);        }        // <debug>        // Enforce code correctness in unbuilt source.        return null;        // </debug>    },<span id='Ext-selection-CellModel-method-getCurrentPosition'>    /**</span>     * Returns the current position in the format {row: row, column: column}     */    getCurrentPosition: function() {        return this.selection;    },<span id='Ext-selection-CellModel-method-setCurrentPosition'>    /**</span>     * Sets the current position     * @param {Object} position The position to set.     */    setCurrentPosition: function(pos) {        var me = this;        // onSelectChange uses lastSelection and nextSelection        me.lastSelection = me.selection;        if (me.selection) {            me.onCellDeselect(me.selection);        }        if (pos) {            me.nextSelection = new me.Selection(me);            me.nextSelection.setPosition(pos);            me.onCellSelect(me.nextSelection);            // Deselect triggered by new selection will kill the selection property, so restore it here.            return me.selection = me.nextSelection;        }        // <debug>        // Enforce code correctness in unbuilt source.        return null;        // </debug>    },    // Keep selection model in consistent state upon record deletion.    onStoreRemove: function(store, record, index) {        var me = this,            pos = me.getCurrentPosition();        me.callParent(arguments);        if (pos) {            // Deleting the row containing the selection.            // Attempt to reselect the same cell which has moved up if there is one            if (pos.row == index) {                if (index < store.getCount() - 1) {                    pos.setPosition(index, pos.column);                    me.setCurrentPosition(pos);                } else {                    delete me.selection;                }            }            // Deleting a row before the selection.            // Move the selection up by one row            else if (index < pos.row) {                pos.setPosition(pos.row - 1, pos.column);                me.setCurrentPosition(pos);            }        }    },<span id='Ext-selection-CellModel-method-onMouseDown'>    /**</span>     * Set the current position based on where the user clicks.     * @private     */    onMouseDown: function(view, cell, cellIndex, record, row, rowIndex, e) {        this.setCurrentPosition({            view: view,            row: rowIndex,            column: cellIndex        });    },    // notify the view that the cell has been selected to update the ui    // appropriately and bring the cell into focus    onCellSelect: function(position, supressEvent) {        if (position && position.row !== undefined && position.row > -1) {            this.doSelect(position.view.getStore().getAt(position.row), /*keepExisting*/false, supressEvent);        }    },    // notify view that the cell has been deselected to update the ui    // appropriately    onCellDeselect: function(position, supressEvent) {        if (position && position.row !== undefined) {            this.doDeselect(position.view.getStore().getAt(position.row), supressEvent);        }    },    onSelectChange: function(record, isSelected, suppressEvent, commitFn) {        var me = this,            pos,            eventName,            view;        if (isSelected) {            pos = me.nextSelection;            eventName = 'select';        } else {            pos = me.lastSelection || me.noSelection;            eventName = 'deselect';        }        // CellModel may be shared between two sides of a Lockable.        // The position must include a reference to the view in which the selection is current.        // Ensure we use the view specifiied by the position.        view = pos.view || me.primaryView;        if ((suppressEvent || me.fireEvent('before' + eventName, me, record, pos.row, pos.column)) !== false &&                commitFn() !== false) {            if (isSelected) {                view.onCellSelect(pos);                view.onCellFocus(pos);            } else {                view.onCellDeselect(pos);                delete me.selection;            }            if (!suppressEvent) {                me.fireEvent(eventName, me, record, pos.row, pos.column);            }        }    },    // Tab key from the View's KeyNav, *not* from an editor.    onKeyTab: function(e, t) {        var me = this,            editingPlugin = me.getCurrentPosition().view.editingPlugin;        // If we were in editing mode, but just focused on a non-editable cell, behave as if we tabbed off an editable field        if (editingPlugin && me.wasEditing) {            me.onEditorTab(editingPlugin, e)        } else {            me.move(e.shiftKey ? 'left' : 'right', e);        }    },    onEditorTab: function(editingPlugin, e) {        var me = this,            direction = e.shiftKey ? 'left' : 'right',            position  = me.move(direction, e);        // Navigation had somewhere to go.... not hit the buffers.        if (position) {            // If we were able to begin editing clear the wasEditing flag. It gets set during navigation off an active edit.            if (editingPlugin.startEditByPosition(position)) {                me.wasEditing = false;            }            // If we could not continue editing...            // Set a flag that we should go back into editing mode upon next onKeyTab call            else {                me.wasEditing = true;                if (!position.columnHeader.dataIndex) {                    me.onEditorTab(editingPlugin, e);                }            }        }    },    refresh: function() {        var pos = this.getCurrentPosition(),            selRowIdx;        // Synchronize the current position's row with the row of the last selected record.        if (pos && (selRowIdx = this.store.indexOf(this.selected.last())) !== -1) {            pos.row = selRowIdx;        }    },<span id='Ext-selection-CellModel-method-onColumnMove'>    /**</span>     * @private     * When grid uses {@link Ext.panel.Table#optimizedColumnMove optimizedColumnMove} (the default), this is added as a     * {@link Ext.panel.Table#columnmove columnmove} handler to correctly maintain the     * selected column using the same column header.     *      * If optimizedColumnMove === false, (which some grid Features set) then the view is refreshed,     * so this is not added as a handler because the selected column.     */    onColumnMove: function(headerCt, header, fromIdx, toIdx) {        var grid = headerCt.up('tablepanel');        if (grid) {            this.onViewRefresh(grid.view);        }    },    onViewRefresh: function(view) {        var me = this,            pos = me.getCurrentPosition(),            headerCt = view.headerCt,            record, columnHeader;        // Re-establish selection of the same cell coordinate.        // DO NOT fire events because the selected         if (pos && pos.view === view) {            record = pos.record;            columnHeader = pos.columnHeader;            // After a refresh, recreate the selection using the same record and grid column as before            if (!columnHeader.isDescendantOf(headerCt)) {                // column header is not a child of the header container                // this happens when the grid is reconfigured with new columns                // make a best effor to select something by matching on id, then text, then dataIndex                columnHeader = headerCt.queryById(columnHeader.id) ||                                headerCt.down('[text="' + columnHeader.text + '"]') ||                               headerCt.down('[dataIndex="' + columnHeader.dataIndex + '"]');            }            // If we have a columnHeader (either the column header that already exists in            // the headerCt, or a suitable match that was found after reconfiguration)            // AND the record still exists in the store (or a record matching the id of            // the previously selected record) We are ok to go ahead and set the selection            if (columnHeader && (view.store.indexOfId(record.getId()) !== -1)) {                me.setCurrentPosition({                    row: record,                    column: columnHeader,                    view: view                });            }        }    },    selectByPosition: function(position) {        this.setCurrentPosition(position);    }}, function() {        // Encapsulate a single selection position.    // Maintains { row: n, column: n, record: r, columnHeader: c}    var Selection = this.prototype.Selection = function(model) {        this.model = model;    };    // Selection row/record & column/columnHeader    Selection.prototype.setPosition = function(row, col) {        var me = this,            view;        // We were passed {row: 1, column: 2, view: myView}        if (arguments.length === 1) {                        // SelectionModel is shared between both sides of a locking grid.            // It can be positioned on either view.            if (row.view) {                me.view = view = row.view;            }            col = row.column;            row = row.row;        }                // If setting the position without specifying a view, and the position is already without a view        // use the owning Model's primary view        if (!view) {            me.view = view = me.model.primaryView;        }        // Row index passed        if (typeof row === 'number') {            me.row = row;            me.record = view.store.getAt(row);        }        // row is a Record        else if (row.isModel) {            me.record = row;            me.row = view.indexOf(row);        }        // row is a grid row        else if (row.tagName) {            me.record = view.getRecord(row);            me.row = view.indexOf(me.record);        }                // column index passed        if (typeof col === 'number') {            me.column = col;            me.columnHeader = view.getHeaderAtIndex(col);        }        // col is a column Header        else {            me.columnHeader = col;            me.column = col.getIndex();        }        return me;    }});</pre></body></html>
 |