All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.smartclient.debug.public.sc.client.language.CellSelection.js Maven / Gradle / Ivy

The newest version!
/*
 * Isomorphic SmartClient
 * Version SC_SNAPSHOT-2011-08-08 (2011-08-08)
 * Copyright(c) 1998 and beyond Isomorphic Software, Inc. All rights reserved.
 * "SmartClient" is a trademark of Isomorphic Software, Inc.
 *
 * [email protected]
 *
 * http://smartclient.com/license
 */

 





//>	@class	CellSelection
//
//	Maintains a representation of selection over a 2-dimensional grid of objects.
// Automatically created to manage cell-selection on +link{class:CubeGrid} widgets. // // @visibility Analytics // @treeLocation Client Reference/System/Selection //< // When you create a CellSelection object, set: //
    //
  • cellSelection.data - to an array of records referenced by this selection //
  • cellSelection.numCols - to the number of columns/fields covered by this selection //
  • cellSelection.selectionProperty - to an alternative name for the selection property, // if you need to access this property manually //
// Implementation: // A uniquely-named selection property is added to each row/record of the data. // This property holds an array of numbers, one for each "chunk" of 32 columns, // whose bits (0-31) represent the selection state of the cell in the corresponding // column/field. // TODO: // * special case selectSingleCell/deselectSingleCell when we know only one cell can be selected // * convert 'select all' to not actually select all, but rather to set a bit? // * maybe also special case row/col selection for performance? // * hardcode COL_SELECTION_FLAGS table // // create the CellSelection class // isc.ClassFactory.defineClass("CellSelection"); isc.CellSelection.addClassProperties({ _selectionID : 0, //> @classAttr isc.CellSelection._selectionID (number : 0 : IRWA) // number to generate a unique ID and selectionProperty for each selection // @group selection // @visibility internal //< COL_SELECTION_FLAGS : null // generate when the first cellSelection is instantiated }); isc.CellSelection.addClassMethods({ generateFlagTable : function () { isc.CellSelection.COL_SELECTION_FLAGS = []; for (var i = 0; i < 32; i++) isc.CellSelection.COL_SELECTION_FLAGS[i] = Math.pow(2,i); } }); isc.CellSelection.addProperties( { data:null, numCols:0, selectionProperty:null, _dirty:true, _selectedCells:[], lastSelectedCell:[], changedCells:[] }); isc.CellSelection.addMethods({ //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //~~~~~~~~~~~~~~~~~~~~ Setup ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //> @attr cellSelection.data (Array | List: null : IRW) // Array (or +link{class:List}) of records to be referenced by this cell selection // @visibility serverSelection // @group selection //< //> @attr cellSelection.numCols (number : null : IRW) // the number of columns/fields covered by this selection // @visibility internal // @group selection //< //> @attr cellSelection.selectionProperty (string : null : IRWA) // A name for the selection property. This property will be set on the records in this // selection's data, and used to determine whether records are selected or not. // Automatically generated if not specified. // @visibility serverSelection // @group selection //< //> @method cellSelection.init() (A) // Initialize this selection instance.
// Note: if the data property is not set at init time, it should be passed to // the selection using the selection.setData method // // @group selection // // @param [all arguments] (object) objects with properties to override from default // @visibility serverSelection //< init : function () { if (!isc.CellSelection.COL_SELECTION_FLAGS) isc.CellSelection.generateFlagTable(); // get unique ID and selection properties if (!this.selectionProperty) this.selectionProperty = "_cellSelection_"+isc.CellSelection._selectionID++; // set the data object so we get notification for add and delete, etc. // NOTE: if the data object wasn't set, use a new arrays this.setData((this.data ? this.data : [])); }, //> @method cellSelection.setData() (A) // Initialize selection data.

// Call this method to associate the selection with a different data object.
// Note: No need to call this if the contents of the selection's data is modified // @group selection // @param newData (array) new data to maintain selection in // @visibility serverSelection //< setData : function (newData) { // if we are currently pointing to data, stop observing it if (this.data != null) this.ignoreData(this.data); // remember the new data this.data = newData; // observe the new data so we will update automatically when it changes if (this.data != null) this.observeData(this.data); }, //> @method cellSelection.observeData() (A) // Observe methods on the data so we change our state. // Called automatically by cellSelection.setData(). // @group selection // // @param data (array) new data to be observed // @visibility internal //< observeData : function (data) { this.observe(data, "dataChanged", "observer._dirty = true"); }, //> @method cellSelection.ignoreData() (A) // Stop observing methods on data when it goes out of scope. // Called automatically by setData // @group selection // // @param data (array) old data to be ignored // @visibility internal //< ignoreData : function (data) { this.ignore(data, "dataChanged"); }, //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //~~~~~~~~~~~~~~~~~~~~ Selection Tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //> @method cellSelection.cellIsSelected() // Return true if a particular item is selected // @group selection // // @param rowNum (number) row index of the cell to check // @param colNum (number) column index of the cell to check // @return (boolean) true == object is selected // false == object is not selected // @group selection // @visibility Analytics //< cellIsSelected : function (rowNum, colNum) { var row = this.data[rowNum], rowSelection = (row ? row[this.selectionProperty] : null), rowChunkSelection = (rowSelection ? rowSelection[Math.floor(colNum/32)] : null), colSelectionFlag = isc.CellSelection.COL_SELECTION_FLAGS[colNum%32]; return (rowChunkSelection != null && ((rowChunkSelection & colSelectionFlag) != 0)); }, rowHasSelection : function (rowNum) { var row = this.data[rowNum], rowSelection = (row ? row[this.selectionProperty] : null), numRowChunks = Math.ceil(this.numCols/32); // if row doesn't exist or row selection property is null/zero, return false if (!row || !row[this.selectionProperty]) return false; // otherwise check each chunk of the row for a selection (true == nonzero value) for (var i = 0; i < numRowChunks; i++) { if (rowSelection[i]) return true; } // made it through all row chunks with no selection, so return false return false; }, colHasSelection : function (colNum) { if (colNum > this.numCols - 1) return false; var colSelectionFlag = isc.CellSelection.COL_SELECTION_FLAGS[colNum%32], rowChunkNum = Math.floor(colNum/32); // iterate through all rows of data var rows = this.data, numRows = rows.length; for (var i = 0; i < numRows; i++) { // get selection property for the current row var rowSelection = rows[i][this.selectionProperty]; // if selection property exists, selection chunk is nonzero, and the flag for this column is set, return true if (rowSelection && rowSelection[rowChunkNum] && ((rowSelection[rowChunkNum] & colSelectionFlag) != 0)) return true; } // made it through all rows with no selection, so return false return false; }, //> @method cellSelection.anySelected() // Is anything in the list selected? // @group selection // // @return (boolean) true == at least one item is selected // false == nothing at all is selected // @visibility Analytics // @group selection //< anySelected : function () { var numRowChunks = Math.ceil(this.numCols/32); // iterate through all rows of data var rows = this.data, numRows = rows.length; for (var i = 0; i < numRows; i++) { // get selection property for the current row var rowSelection = rows[i][this.selectionProperty]; if (!rowSelection) continue; // check each chunk of the row for a selection (true == nonzero value) for (var j = 0; j < numRowChunks; j++) { if (rowSelection[j]) return true; } } // made it through all rows with no selection, so return false return false; }, //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //~~~~~~~~~~~~~~~~~~~~ Selection Getters ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //> @method cellSelection.getSelectedCells() // Returns an array of the currently selected cells. Each cell is returned as a 2 element // array in the form [rowNum, colNum]. // @group selection // @return (array) an array of the selected cells, as 2 element arrays // @visibility Analytics // @group selection //< getSelectedCells : function () { // first see if we already have a clean list of selected cells if (!this._dirty) return this._selectedCells; var selectedCells = [], colSelectionFlags = isc.CellSelection.COL_SELECTION_FLAGS, numRowChunks = Math.ceil(this.numCols/32), rows = this.data, numRows = rows.length, rowSelection; // iterate through all rows of data for (var i = 0; i < numRows; i++) { // get selection property for the current row rowSelection = rows[i][this.selectionProperty]; if (!rowSelection) continue; // iterate through all chunks in this row for (var j = 0, rowChunkSelection, numColsInChunk; j < numRowChunks; j++) { // get selection flags for this chunk rowChunkSelection = rowSelection[j]; if (!rowChunkSelection) continue; // how many columns in this chunk (32 for all but the last chunk) // NOTE: should use this.numCols%32 for non-chunked CellSelection too numColsInChunk = (j == numRowChunks - 1 && this.numCols%32 != 0) ? this.numCols%32 : 32; // iterate through the flags to find the selected cells in this chunk for (var k = 0; k < numColsInChunk; k++) { if ((rowChunkSelection & colSelectionFlags[k]) != 0) { selectedCells[selectedCells.length] = [i,j*32+k]; } } } } // cache and return the list of selected cells this._selectedCells = selectedCells; this._dirty = false; return selectedCells; }, // returns an array containing the numbers of all rows that have at least one cell selected getSelectionRowNums : function () { var selectionRowNums = [], numRowChunks = Math.ceil(this.numCols/32), rows = this.data, numRows = rows.length, rowSelection; // iterate through all rows of data for (var i = 0; i < numRows; i++) { // get selection property for the current row rowSelection = rows[i][this.selectionProperty]; if (!rowSelection) continue; // iterate through chunks in this row for (var j = 0, numColsInChunk; j < numRowChunks; j++) { // if any selection flags are set, add the current row's number to the list we'll return if (rowSelection[j]) { selectionRowNums[selectionRowNums.length] = i; break; } } } return selectionRowNums; }, getSelectionColNums : function () { var selectionColNums = [], allRowSelections = [], colSelectionFlags = isc.CellSelection.COL_SELECTION_FLAGS, numRowChunks = Math.ceil(this.numCols/32), rows = this.data, numRows = rows.length, rowSelection; // bitwise-OR the selection flags for every row into allRowSelections for (var i = 0; i < numRows; i++) { // get selection property for the current row rowSelection = rows[i][this.selectionProperty]; if (!rowSelection) continue; // iterate through chunks in this row for (var j = 0, numColsInChunk; j < numRowChunks; j++) { // if any selection flags are set, bitwise-OR with selection flags for all previous rows if (rowSelection[j]) { allRowSelections[j] = allRowSelections[j] | rowSelection[j]; } } } // if no selections, return now if (allRowSelections.length == 0) return selectionColNums; // compare allRowSelections flags against each constant in colSelectionFlags // to determine which columns have a selected cell for (var i = 0, numCols = this.numCols; i < numCols; i++) { if ((allRowSelections[Math.floor(i/32)] & colSelectionFlags[i%32]) != 0) selectionColNums[selectionColNums.length] = i; } return selectionColNums; }, getSelectionBounds : function () { var rows = this.getSelectionRowNums(), cols = this.getSelectionColNums(); return [rows.first(), cols.first(), rows.last(), cols.last()]; }, //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //~~~~~ Selection Setters (internal) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //> @method cellSelection._setCellSelection() // Select or deselect a particular cell. All other selection routines call this one. //

// This method:

// - Initializes row selection property if none.
// - Saves cell coordinates in this.lastSelectedCell if selection state of
//   this cell changes.
// - Marks selection as dirty if selection state of this cell changes.
// 
// // @param rowNum (number) number of row // @param colNum (number) number of column // @param newState (boolean) desired selection state for this cell // // @return (boolean) true if selection state of this cell is changed, // false otherwise // @visibility internal //< _setCellSelection : function (rowNum, colNum, newState) { var row = this.data[rowNum], rowSelection = (row ? row[this.selectionProperty] : null), rowChunkNum = Math.floor(colNum/32), rowChunkSelection = (rowSelection ? rowSelection[Math.floor(colNum/32)] : 0), colSelectionFlag = isc.CellSelection.COL_SELECTION_FLAGS[colNum%32]; // if the row or column does not exist, return false if (!row || colNum > this.numCols - 1) return false; // if the row is not enabled, return false if (row.enabled == false) return false; // if the row has no selection property, initialize it now if (rowSelection == null) { rowSelection = row[this.selectionProperty] = []; for (var i = 0, numChunks = Math.ceil(this.numCols/32); i < numChunks; i++) rowSelection[i] = 0; } // if this chunk in the row has no selection flags yet, initialize them now // NOTE: this will only happen if numCols is changed without throwing away this selection...would this ever happen? else if (rowChunkSelection == null) { rowSelection[rowChunkNum] = 0; } // if the cell's selection state is already set to the new state, return false if (((rowChunkSelection & colSelectionFlag) != 0) == newState) return false; // cell exists, is enabled, and has a different state; so change the state via bitwise XOR rowSelection[rowChunkNum] = rowChunkSelection ^ colSelectionFlag; // if selecting, remember that this is the last selected cell if (newState) this.lastSelectedCell = [rowNum, colNum]; // mark the cached selection as dirty this._dirty = true; // return true to indicate that the cell's state was actually changed return true; }, setCellRangeSelection : function (startRowNum, startColNum, endRowNum, endColNum, newState) { this.changedCells = this._setCellRangeSelection(startRowNum, startColNum, endRowNum, endColNum, newState); return this._cellSelectionsChanged(); }, // returns array of [rowNum,colNum] arrays representing cells whose selection state was actually // changed _setCellRangeSelection : function (startRowNum, startColNum, endRowNum, endColNum, newState) { var changedCells = [], minRowNum, maxRowNum, minColNum, maxColNum; if (startRowNum <= endRowNum) { minRowNum = startRowNum; maxRowNum = endRowNum; } else { minRowNum = endRowNum; maxRowNum = startRowNum; } if (startColNum <= endColNum) { minColNum = startColNum; maxColNum = endColNum; } else { minColNum = endColNum; maxColNum = startColNum; } //>DEBUG if (this.logIsDebugEnabled()) { this.logDebug((newState ? "selecting " : "deselecting ") + [minRowNum, minColNum] + " through " + [maxRowNum, maxColNum]); } // 0) { this.selectionChanged(); return true; } else return false; }, //> @method cellSelection.selectionChanged() // Observable handler fired whenever the cell selection is modified // @group selection // // @visibility internal // @group selection //< selectionChanged : function () {}, //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //~~~~~ Selection Setters (public) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ setCellSelection : function (rowNum, colNum, newState) { if (this._setCellSelection(rowNum, colNum, newState)) { this.changedCells = [[rowNum, colNum]]; this.selectionChanged(); return true; } else return false; }, //> @method cellSelection.selectCell() // Select a particular cell // @group selection // // @param rowNum (number) row index of cell to select // @param colNum (number) column index of cell to select // @return (boolean) true == selection actually changed, false == no change // @visibility Analytics //< selectCell : function (rowNum, colNum) { return this.setCellSelection(rowNum, colNum, true); }, //> @method cellSelection.deselectCell() // Deselect a particular cell // @group selection // // @param rowNum (number) row index of the cell to select // @param colNum (number) column index of the cell to select // @return (boolean) true == selection actually changed, false == no change // @visibility Analytics //< deselectCell : function (rowNum, colNum) { return this.setCellSelection(rowNum, colNum, false); }, selectCellRange : function (startRowNum, startColNum, endRowNum, endColNum) { this.changedCells = this._setCellRangeSelection(startRowNum, startColNum, endRowNum, endColNum, true); return this._cellSelectionsChanged(); }, deselectCellRange : function (startRowNum, startColNum, endRowNum, endColNum) { this.changedCells = this._setCellRangeSelection(startRowNum, startColNum, endRowNum, endColNum, false); return this._cellSelectionsChanged(); }, // simply setting the selectionProperty for each row to 0 or ~0 would seem a // good shortcut in the following methods, but this wouldn't tell us // exactly which cells have changed--and setting the style of a cell unnecessarily // is ~really~ expensive selectRow : function (rowNum) { return this.selectCellRange(rowNum, 0, rowNum, this.numCols-1); }, deselectRow : function (rowNum) { return this.deselectCellRange(rowNum, 0, rowNum, this.numCols-1); }, selectCol : function (colNum) { return this.selectCellRange(0, colNum, this.data.length-1, colNum); }, deselectCol : function (colNum) { return this.deselectCellRange(0, colNum, this.data.length-1, colNum); }, selectAll : function () { return this.selectCellRange(0, 0, this.data.length-1, this.numCols-1); }, deselectAll : function () { return this.deselectCellRange(0, 0, this.data.length-1, this.numCols-1); }, //> @method cellSelection.selectCellList() // select an array of cells // @group selection // @param list (array[]) Array of cells to select. Each cell can be specified // as a 2 element array [rowNum, colNum] // @return (boolean) true == selection actually changed, false == no change // @visibility Analytics //< selectCellList : function (cellList) { return this.setCellListSelection(cellList, true); }, //> @method cellSelection.deselectCellList() // deselect an array of cells // // @group selection // @param list (array[]) Array of cells to deselect. Each cell can be specified // as a 2 element array [rowNum, colNum] // @return (boolean) true == selection actually changed, false == no change // @visibility Analytics //< deselectCellList : function (cellList) { return this.setCellListSelection(cellList, false); }, //> @method cellSelection.selectSingleCell() // select a single cell and deselect everything else // @group selection // @param rowNum (number) row index of cell to select // @param colNum (number) column index of cell to select // @return (boolean) true == selection actually changed, false == no change // @visibility Analytics //< selectSingleCell : function (rowNum, colNum) { // remember whether this cell was selected before we deselect all cells var cellWasSelected = this.cellIsSelected(rowNum, colNum); // deselect all cells, using the helper method so we don't call selectionChanged() yet this.changedCells = this._setCellRangeSelection(0, 0, this.data.length-1, this.numCols-1, false); // select this cell this._setCellSelection(rowNum, colNum, true); // if this cell wasn't selected before, add it to changedCells if (!cellWasSelected) this.changedCells[this.changedCells.length] = [rowNum, colNum]; // XXX else remove it from changedCells... return this._cellSelectionsChanged(); }, selectSingleRow : function (rowNum) { var changedCells = []; // deselect rows before this one if (rowNum > 0) changedCells = this._setCellRangeSelection(0, 0, rowNum-1, this.numCols-1, false); // select this row changedCells = changedCells.concat(this._setCellRangeSelection(rowNum, 0, rowNum, this.numCols-1, true)); // deselect rows after this one if (rowNum < this.data.length-1) changedCells = changedCells.concat(this._setCellRangeSelection(rowNum+1, 0, this.data.length-1, this.numCols-1, false)); this.changedCells = changedCells; return this._cellSelectionsChanged(); }, selectSingleCol : function (colNum) { var changedCells = []; // deselect columns before this one if (colNum > 0) changedCells = this._setCellRangeSelection(0, 0, this.data.length-1, colNum-1, false); // select this column changedCells = changedCells.concat(this._setCellRangeSelection(0, colNum, this.data.length-1, colNum, true)); // deselect columns after this one if (colNum < this.numCols-1) changedCells = changedCells.concat(this._setCellRangeSelection(0, colNum+1, this.data.length-1, this.numCols-1, false)); this.changedCells = changedCells; return this._cellSelectionsChanged(); }, //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //~~~~~ Event-based Selection ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //> @method cellSelection.selectOnMouseDown() (A) // @group selection, mouseEvents // Update the selection as the result of a mouseDown event. // Handles shift, control, etc. key selection as well. // Call this from a mouseDown handler. // @see GridRenderer.mouseDown() // @see ListGrid.mouseDown() // // @param target (Canvas) target object // @param recordNum (number) record number mouse went down on // // @return (boolean) true == selection was changed, false == no change //< selectOnMouseDown : function (target, rowNum, colNum) { // if the target's selectionType is NONE, just bail if (target.selectionType == isc.Selection.NONE) return false; // remember mouseDown location in case we start drag selecting this.startRow = this.lastRow = rowNum; this.startCol = this.lastCol = colNum; var cellSelected = this.cellIsSelected(rowNum, colNum), selection = this.getSelectedCells(), selectionBounds = this.getSelectionBounds(); // clear flags for deselecting cells on mouseUp // these are set in the simple and normal cases below (3 and 5) // see selectOnMouseUp() for details this.deselectCellOnMouseUp = false; this.deselectOthersOnMouseUp = false; // Case 1: SINGLE selection if (target.selectionType == isc.Selection.SINGLE) { this.selectSingleCell(rowNum, colNum); return true; // Case 2: Shift-selection (select contiguous range of cells) } else if (isc.EventHandler.shiftKeyDown()) { // if nothing was selected, if (selection.length == 0) { // simply select that cell this.selectCell(rowNum, colNum); return true; // otherwise since something was selected } else { // select a range of cells var startRow = selectionBounds[0], startCol = selectionBounds[1], endRow = selectionBounds[2], endCol = selectionBounds[3]; if (rowNum < startRow) startRow = rowNum; else if (rowNum >= endRow) endRow = rowNum; else { // XXX if rowNum=startRow, rowNum+1 isn't correct this.deselectCellRange(rowNum+1,startCol,endRow,endCol); endRow = rowNum; } if (colNum < startCol) startCol = colNum; else if (colNum >= endCol) endCol = colNum; else { this.deselectCellRange(startRow,colNum+1,endRow,endCol); endCol = colNum; } this.selectCellRange(startRow,startCol,endRow,endCol); return true; } // Case 3: SIMPLE selection (toggle selection of this cell, but defer deselection until mouseUp) } else if (target.selectionType == isc.Selection.SIMPLE) { if (!cellSelected) { this.selectCell(rowNum, colNum); return true; } else { this.deselectCellOnMouseUp = true; return false; } // Case 4: meta-key selection (simply toggle selection of this record) } else if (isc.Browser.isMac ? isc.EventHandler.metaKeyDown() : isc.EventHandler.ctrlKeyDown()) { this.setCellSelection(rowNum, colNum, !cellSelected); return true; // Case 5: normal selection (no modifier keys) } else { if (!cellSelected) { // if you click outside of the selection, select the new cell and deselect everything // else this.selectSingleCell(rowNum, colNum); return true; } else if (isc.EventHandler.rightButtonDown()) { // never deselect if you right click on the selection, unless you start drag selecting this.deselectOnDragMove = true; return false; } else { // simpleDeselect mode: this mode is designed to make it easy to entirely get rid of // your selection, so you don't have to know about ctrl-clicking. In a nutshell, if you // click on the existing selection, it will be entirely deselected. if (this.dragSelection) { if (this.simpleDeselect) { // if you click on the selection, deselect the entire selection including the // clicked-on cell. Later, if a drag begins, select the clicked-on cell. this.deselectAll(); this.selectOriginOnDragMove = true; return true; } // for a drag selection, deselect others immediately; otherwise we'll be dragging // out a new selection within/overlapping with an existing selection, which we only // want to do on a ctrl-click. This matches Excel. this.selectSingleCell(rowNum, colNum); return true; } else { if (this.simpleDeselect) { // deselect everything on mouseUp, including the cell clicked on this.deselectAllOnMouseUp = true; } else { // if we click in a multiple selection, deselect everything but the clicked-on // item, but don't do it until mouseUp in order to allow dragging the current // selection. This matches Windows Explorer. this.deselectOthersOnMouseUp = (selection.length > 1); } return false; } } } }, //> @method cellSelection.selectOnDragMove() (A) // during drag selection, update the selection as a result of a dragMove event // @group selection, mouseEvents //< selectOnDragMove : function (target, currRow, currCol) { var startRow = this.startRow, startCol = this.startCol, lastRow = this.lastRow, lastCol = this.lastCol; if (currRow < 0 || currCol < 0) { //>DEBUG this.logWarn("selectOnDragMove: aborting due to negative coordinate: " + [currRow, currCol]); //= startRow && startRow >= currRow) || (currRow >= startRow && startRow >= lastRow))) || // or we moved on the "cols" axis and crossed the origin (currCol != lastCol && ((lastCol >= startCol && startCol >= currCol) || (currCol >= startCol && startCol >= lastCol))) ) { // NOTE: The start cell doesn't change, but it would appear to deselect and reselect if we // naively deselected the old rect and selected the new, so we manually deselect and select // the start cell, so it isn't listed in the changed cells. // deselect the entire range that has been drag selected so far this._setCellSelection(startRow, startCol, false); changedCells.addList( this._setCellRangeSelection(startRow, startCol, lastRow, lastCol, false)); // then select the new area. this._setCellSelection(startRow, startCol, true); changedCells.addList( this._setCellRangeSelection(startRow, startCol, currRow, currCol, true)); this.changedCells = changedCells; this._cellSelectionsChanged(); this.lastRow = currRow; this.lastCol = currCol; return; } // The other four orderings of last, start and current indicate a shrinking or growth of the // selection area, with the new selection area overlapping the old. // The increase or decrease in selection always consists of two rectangles, one for the // increase/decrease of selection in the horizontal direction, one for the increase/decrease of // selection in the vertical direction. These rectangles will potentially overlap at the // corner, so the vertical case includes the corner and the horizontal case does not. // NOTE: we manually combine the changed cell list from the two selection changes, so that // there's only one selectionChanged() event seen by observers. if (currRow >= 0 && currRow != lastRow) { // moved vertically if (startRow >= lastRow && lastRow > currRow) { // increasing selection upward (last < start) changedCells.addList( this._setCellRangeSelection(currRow, startCol, lastRow-1, lastCol, true)); } else if (startRow >= currRow && currRow > lastRow) { // decreasing selection downward (last < start) changedCells.addList( this._setCellRangeSelection(lastRow, startCol, currRow-1, lastCol, false)); } else if (startRow <= currRow && currRow < lastRow) { // decreasing selection upward (last > start) changedCells.addList( this._setCellRangeSelection(currRow+1, startCol, lastRow, lastCol, false)); } else if (startRow <= lastRow && lastRow < currRow) { // increasing selection downward (last > start) changedCells.addList( this._setCellRangeSelection(lastRow+1, startCol, currRow, lastCol, true)); } // NOTE: we change lastRow because we want the horizontal case to handle the corner area // between current and last. lastRow = this.lastRow = currRow; } if (currCol >= 0 && currCol != lastCol) { // moved horizontally if (startCol >= lastCol && lastCol > currCol) { // increasing selection on the left (last < start) changedCells.addList( this._setCellRangeSelection(startRow, currCol, lastRow, lastCol-1, true)); } else if (startCol >= currCol && currCol > lastCol) { // decreasing selection on the left (last < start) changedCells.addList( this._setCellRangeSelection(startRow, lastCol, lastRow, currCol-1, false)); } else if (startCol <= currCol && currCol < lastCol) { // decreasing selection on the right (last > start) changedCells.addList( this._setCellRangeSelection(startRow, currCol+1, lastRow, lastCol, false)); } else if (startCol <= lastCol && lastCol < currCol) { // increasing selection on the right (last > start) changedCells.addList( this._setCellRangeSelection(startRow, lastCol+1, lastRow, currCol, true)); } this.lastCol = currCol; } this.changedCells = changedCells; this._cellSelectionsChanged(); }, //> @method cellSelection.selectOnMouseUp() (A)s // Update the selection as the result of a mouseUp event. // We currently use this to defer deselection for drag-and-drop of multiple records. // Call this from a mouseUp handler. // @group selection, mouseEvents // @see ListGrid.mouseUp() // // @param target (Canvas) target object // @param recordNum (number) record number mouse went down on // // @return (boolean) true == selection was changed, false == no change //< selectOnMouseUp : function (target, rowNum, colNum) { // if the target's selectionType is NONE, just bail if (target.selectionType == isc.Selection.NONE) return false; // If multiselection is on and no modifier keys are down, we need to // deselect any cells other than the one that is clicked. BUT, we can't do this in // selectOnMouseDown() because the user might be clicking on a cell in a multiple selection // to initiate a drag operation with all of the selected cells. So in selectOnMouseDown() // we set a deselectOthersOnMouseUp flag that we can check here and do the deselection // if necessary. if (this.deselectOthersOnMouseUp) { this.selectSingleCell(rowNum, colNum); this.deselectOthersOnMouseUp = false; return true; // Similarly, if SIMPLE selection is enabled we don't want to deselect the current // cell if the user is initiating a drag. We set a deselectRecordOnMouseUp flag in this case. } else if (this.deselectRecordOnMouseUp) { this.deselectCell(rowNum, colNum); this.deselectRecordOnMouseUp = false; return true; } else if (this.deselectAllOnMouseUp) { this.deselectAll(); this.deselectAllOnMouseUp = false; } else return false; } }); // END isc.CellSelection.addMethods()




© 2015 - 2025 Weber Informatics LLC | Privacy Policy