"use strict"; /* WorksheetView.js * * Author: Dmitry.Sokolov@avsmedia.net * Date: Nov 21, 2011 */ (/** * @param {jQuery} $ * @param {Window} window * @param {undefined} undefined */ function ( $, window, undefined ) { /* * Import * ----------------------------------------------------------------------------- */ var asc = window["Asc"]; var asc_applyFunction = asc.applyFunction; var asc_calcnpt = asc.calcNearestPt; var asc_getcvt = asc.getCvtRatio; var asc_floor = asc.floor; var asc_ceil = asc.ceil; var asc_obj2Color = asc.colorObjToAscColor; var asc_typeof = asc.typeOf; var asc_incDecFonSize = asc.incDecFonSize; var asc_debug = asc.outputDebugStr; var asc_Range = asc.Range; var asc_ActiveRange = asc.ActiveRange; var asc_CMM = asc.asc_CMouseMoveData; var asc_VR = asc.VisibleRange; var asc_CCellFlag = asc.asc_CCellFlag; var asc_CFont = asc.asc_CFont; var asc_CFill = asc.asc_CFill; var asc_CCellInfo = asc.asc_CCellInfo; var asc_CHyperlink = asc.asc_CHyperlink; var asc_CPageOptions = asc.asc_CPageOptions; var asc_CPageSetup = asc.asc_CPageSetup; var asc_CPageMargins = asc.asc_CPageMargins; var asc_CPagePrint = asc.CPagePrint; var asc_CSelectionMathInfo = asc.asc_CSelectionMathInfo; var asc_CAutoFilterInfo = asc.asc_CAutoFilterInfo; /* * Constants * ----------------------------------------------------------------------------- */ /** * header styles * @const */ var kHeaderDefault = 0; var kHeaderActive = 1; var kHeaderHighlighted = 2; /** * text alignment style * @const */ var khaLeft = "left"; var khaCenter = "center"; var khaRight = "right"; var khaJustify = "justify"; var kvaTop = "top"; var kvaCenter = "center"; var kvaBottom = "bottom"; var kNone = "none"; /** * cursor styles * @const */ var kCurDefault = "default"; var kCurCorner = "pointer"; var kCurColSelect = "pointer"; var kCurColResize = "col-resize"; var kCurRowSelect = "pointer"; var kCurRowResize = "row-resize"; // Курсор для автозаполнения var kCurFillHandle = "crosshair"; // Курсор для гиперссылки var kCurHyperlink = "pointer"; // Курсор для перемещения области выделения var kCurMove = "move"; var kCurSEResize = "se-resize"; var kCurNEResize = "ne-resize"; var kCurAutoFilter = "pointer"; var kCurCells = ''; var kCurFormatPainterExcel = ''; if ( AscBrowser.isIE ) { // Пути указаны относительно html в меню, не надо их исправлять // и коммитить на пути относительно тестового меню kCurCells = 'url(../../../sdk/Common/Images/plus.cur), pointer'; kCurFormatPainterExcel = 'url(../../../sdk/Common/Images/plus_copy.cur), pointer'; } else if ( AscBrowser.isOpera ) { kCurCells = 'cell'; kCurFormatPainterExcel = 'pointer'; } else { kCurCells = "url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAFJJREFUeNpidHFxYcAC/qPxGdEVMDHgALt37wZjXACnRkKA/hpZsAQEMYHFwAAM1f+kApAeipzK4OrqijU6cMnBNDJSNQEMznjECnAFCgwABBgAcX1BU/hbd0sAAAAASUVORK5CYII=') 6 6, pointer"; kCurFormatPainterExcel = "url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAUCAYAAABiS3YzAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAK1JREFUeNrUk+ESxBAMhG3Hm+GpeTfF0Eld0uLcj9uZTP3xdZMsxBjVRhXYsRNora2n5HSxbjLGtKPSX7uqCiHkD1adUlcfLnMdKw6zq94plXbOiVskKm1575GAF1iS6DQBSjECdUp+gFcoJ9LyBe6B09BuluCA09A8fwCK7AHsopiljCxOBLY5xVnVO2KWd779W/uKy2qLk5DjVyhGwz+qn7T/P1D9FPRVnQIMABDnBAmTp4GtAAAAAElFTkSuQmCC') 6 12, pointer"; } var kNewLine = "\n"; var kMaxAutoCompleteCellEdit = 20000; function calcDecades( num ) { return Math.abs( num ) < 10 ? 1 : 1 + calcDecades( asc_floor( num * 0.1 ) ); } function CacheElement() { if ( !(this instanceof CacheElement) ) { return new CacheElement(); } this.columnsWithText = {}; // Колонки, в которых есть текст this.columns = {}; this.erased = {}; return this; } function Cache() { if ( !(this instanceof Cache) ) { return new Cache(); } this.rows = {}; this.sectors = []; this.reset = function () { this.rows = {}; this.sectors = []; }; // Structure of cache // // cache : { // // rows : { // 0 : { // columns : { // 0 : { // text : { // cellHA : String, // cellVA : String, // cellW : Number, // color : String, // metrics : TextMetrics, // sideL : Number, // sideR : Number, // state : StringRenderInternalState // } // } // }, // erased : { // 1 : true, 2 : true // } // } // }, // // sectors: [ // 0 : Range // ] // // } } function CellFlags() { this.wrapText = false; this.shrinkToFit = false; this.merged = null; this.textAlign = kNone; } CellFlags.prototype.clone = function () { var oRes = new CellFlags(); oRes.wrapText = this.wrapText; oRes.shrinkToFit = this.shrinkToFit; oRes.merged = this.merged ? this.merged.clone() : null; oRes.textAlign = this.textAlign; return oRes; }; CellFlags.prototype.isMerged = function () { return null !== this.merged; }; function CellBorderObject( borders, mergeInfo, col, row ) { this.borders = borders; this.mergeInfo = mergeInfo; this.col = col; this.row = row; } CellBorderObject.prototype.isMerge = function () { return null != this.mergeInfo; }; CellBorderObject.prototype.getLeftBorder = function () { if ( !this.borders || (this.isMerge() && (this.col !== this.mergeInfo.c1 || this.col - 1 !== this.mergeInfo.c2)) ) { return null; } return this.borders.l; }; CellBorderObject.prototype.getRightBorder = function () { if ( !this.borders || (this.isMerge() && (this.col - 1 !== this.mergeInfo.c1 || this.col !== this.mergeInfo.c2)) ) { return null; } return this.borders.r; }; CellBorderObject.prototype.getTopBorder = function () { if ( !this.borders || (this.isMerge() && (this.row !== this.mergeInfo.r1 || this.row - 1 !== this.mergeInfo.r2)) ) { return null; } return this.borders.t; }; CellBorderObject.prototype.getBottomBorder = function () { if ( !this.borders || (this.isMerge() && (this.row - 1 !== this.mergeInfo.r1 || this.row !== this.mergeInfo.r2)) ) { return null; } return this.borders.b; }; /** * Widget for displaying and editing Worksheet object * ----------------------------------------------------------------------------- * @param {Woorksheet} model Worksheet * @param {asc_CHandlersList} handlers Event handlers * @param {Object} buffers DrawingContext + Overlay * @param {StringRender} stringRender StringRender * @param {Number} maxDigitWidth Максимальный размер цифры * @param {CCollaborativeEditing} collaborativeEditing * @param {Object} settings Settings * * @constructor * @memberOf Asc */ function WorksheetView( model, handlers, buffers, stringRender, maxDigitWidth, collaborativeEditing, settings ) { this.settings = settings; this.vspRatio = 1.275; this.handlers = handlers; this.model = model; this.buffers = buffers; this.drawingCtx = this.buffers.main; this.overlayCtx = this.buffers.overlay; this.drawingGraphicCtx = this.buffers.mainGraphic; this.overlayGraphicCtx = this.buffers.overlayGraphic; this.shapeCtx = this.buffers.shapeCtx; this.shapeOverlayCtx = this.buffers.shapeOverlayCtx; this.stringRender = stringRender; // Флаг, сигнализирует о том, что мы сделали resize, но это не активный лист (поэтому как только будем показывать, нужно перерисовать и пересчитать кеш) this.updateResize = false; // Флаг, сигнализирует о том, что мы сменили zoom, но это не активный лист (поэтому как только будем показывать, нужно перерисовать и пересчитать кеш) this.updateZoom = false; // ToDo Флаг-заглушка, для того, чтобы на mobile не было изменения высоты строк при zoom (по правильному высота просто не должна меняться) this.notUpdateRowHeight = false; this.cache = new Cache(); //---member declaration--- // Максимальная ширина числа из 0,1,2...,9, померенная в нормальном шрифте(дефалтовый для книги) в px(целое) // Ecma-376 Office Open XML Part 1, пункт 18.3.1.13 this.maxDigitWidth = maxDigitWidth; this.nBaseColWidth = 8; // Число символов для дефалтовой ширины (по умолчинию 8) this.defaultColWidthChars = 0; this.defaultColWidth = 0; this.defaultRowHeight = 0; this.defaultRowDescender = 0; this.headersLeft = 0; this.headersTop = 0; this.headersWidth = 0; this.headersHeight = 0; this.headersHeightByFont = 0; // Размер по шрифту (размер без скрытия заголовков) this.cellsLeft = 0; this.cellsTop = 0; this.cols = []; this.rows = []; this.width_1px = 0; this.width_2px = 0; this.width_3px = 0; this.width_4px = 0; this.width_padding = 0; this.height_1px = 0; this.height_2px = 0; this.height_3px = 0; this.height_4px = 0; this.highlightedCol = -1; this.highlightedRow = -1; this.topLeftFrozenCell = null; // Верхняя ячейка для закрепления диапазона this.visibleRange = new asc_Range( 0, 0, 0, 0 ); this.activeRange = new asc_ActiveRange( 0, 0, 0, 0 ); this.isChanged = false; this.isCellEditMode = false; this.isFormulaEditMode = false; this.isChartAreaEditMode = false; this.lockDraw = false; this.isSelectOnShape = false; // Выделен shape this.stateFormatPainter = c_oAscFormatPainterState.kOff; this.selectionDialogType = c_oAscSelectionDialogType.None; this.isSelectionDialogMode = false; this.copyActiveRange = null; this.startCellMoveResizeRange = null; this.startCellMoveResizeRange2 = null; this.moveRangeDrawingObjectTo = null; // Координаты ячейки начала перемещения диапазона this.startCellMoveRange = null; // Дипазон перемещения this.activeMoveRange = null; // Координаты fillHandle ("квадрата" автозаполнения) this.fillHandleL = 0; this.fillHandleT = 0; this.fillHandleR = 0; this.fillHandleB = 0; // Range fillHandle this.activeFillHandle = null; // Горизонтальное (0) или вертикальное (1) направление автозаполнения this.fillHandleDirection = -1; // Зона автозаполнения this.fillHandleArea = -1; this.nRowsCount = 0; this.nColsCount = 0; // Массив ячеек для текущей формулы this.arrActiveFormulaRanges = []; this.arrActiveFormulaRangesPosition = -1; this.arrActiveChartsRanges = []; //------------------------ this.collaborativeEditing = collaborativeEditing; // Auto filters this.autoFilters = new asc.AutoFilters( this ); this.drawingArea = new DrawingArea( this ); this.cellCommentator = new CCellCommentator( this ); this.objectRender = null; this._init(); return this; } WorksheetView.prototype.getCellVisibleRange = function ( col, row ) { var vr, offsetX = 0, offsetY = 0, cFrozen, rFrozen; if ( this.topLeftFrozenCell ) { cFrozen = this.topLeftFrozenCell.getCol0() - 1; rFrozen = this.topLeftFrozenCell.getRow0() - 1; if ( col <= cFrozen && row <= rFrozen ) { vr = new asc_Range( 0, 0, cFrozen, rFrozen ); } else if ( col <= cFrozen ) { vr = new asc_Range( 0, this.visibleRange.r1, cFrozen, this.visibleRange.r2 ); offsetY -= this.rows[rFrozen + 1].top - this.cellsTop; } else if ( row <= rFrozen ) { vr = new asc_Range( this.visibleRange.c1, 0, this.visibleRange.c2, rFrozen ); offsetX -= this.cols[cFrozen + 1].left - this.cellsLeft; } else { vr = this.visibleRange; offsetX -= this.cols[cFrozen + 1].left - this.cellsLeft; offsetY -= this.rows[rFrozen + 1].top - this.cellsTop; } } else { vr = this.visibleRange; } offsetX += this.cols[vr.c1].left - this.cellsLeft; offsetY += this.rows[vr.r1].top - this.cellsTop; return vr.contains( col, row ) ? new asc_VR( vr, offsetX, offsetY ) : null; }; WorksheetView.prototype.getCellMetrics = function ( col, row ) { var vr, nColSize, nRowSize; if ( vr = this.getCellVisibleRange( col, row ) ) { nColSize = this.getColSize( col ); nRowSize = this.getRowSize( row ); if ( nColSize && nRowSize ) { return { left : nColSize.left - vr.offsetX, top : nRowSize.top - vr.offsetY, width : nColSize.width, height: nRowSize.height }; } } return null; }; WorksheetView.prototype.getColSize = function ( col ) { return (col >= 0 && col < this.cols.length) ? this.cols[col] : null; }; WorksheetView.prototype.getRowSize = function ( row ) { return (row >= 0 && row < this.rows.length) ? this.rows[row] : null; }; WorksheetView.prototype.getFrozenCell = function () { return this.topLeftFrozenCell; }; WorksheetView.prototype.getVisibleRange = function () { return this.visibleRange; }; WorksheetView.prototype.updateVisibleRange = function () { return this._updateCellsRange( this.getVisibleRange() ); }; WorksheetView.prototype.getFirstVisibleCol = function ( allowPane ) { var tmp = 0; if ( allowPane && this.topLeftFrozenCell ) { tmp = this.topLeftFrozenCell.getCol0(); } return this.visibleRange.c1 - tmp; }; WorksheetView.prototype.getLastVisibleCol = function () { return this.visibleRange.c2; }; WorksheetView.prototype.getFirstVisibleRow = function ( allowPane ) { var tmp = 0; if ( allowPane && this.topLeftFrozenCell ) { tmp = this.topLeftFrozenCell.getRow0(); } return this.visibleRange.r1 - tmp; }; WorksheetView.prototype.getLastVisibleRow = function () { return this.visibleRange.r2; }; WorksheetView.prototype.getHorizontalScrollRange = function () { var ctxW = this.drawingCtx.getWidth() - this.cellsLeft; for ( var w = 0, i = this.cols.length - 1; i >= 0; --i ) { w += this.cols[i].width; if ( w > ctxW ) { break; } } return i; // Диапазон скрола должен быть меньше количества столбцов, чтобы не было прибавления столбцов при перетаскивании бегунка }; WorksheetView.prototype.getVerticalScrollRange = function () { var ctxH = this.drawingCtx.getHeight() - this.cellsTop; for ( var h = 0, i = this.rows.length - 1; i >= 0; --i ) { h += this.rows[i].height; if ( h > ctxH ) { break; } } return i; // Диапазон скрола должен быть меньше количества строк, чтобы не было прибавления строк при перетаскивании бегунка }; WorksheetView.prototype.getCellsOffset = function ( units ) { var u = units >= 0 && units <= 3 ? units : 0; return { left: this.cellsLeft * asc_getcvt( 1/*pt*/, u, this._getPPIX() ), top : this.cellsTop * asc_getcvt( 1/*pt*/, u, this._getPPIY() ) }; }; WorksheetView.prototype.getCellLeft = function ( column, units ) { if ( column >= 0 && column < this.cols.length ) { var u = units >= 0 && units <= 3 ? units : 0; return this.cols[column].left * asc_getcvt( 1/*pt*/, u, this._getPPIX() ); } return null; }; WorksheetView.prototype.getCellTop = function ( row, units ) { if ( row >= 0 && row < this.rows.length ) { var u = units >= 0 && units <= 3 ? units : 0; return this.rows[row].top * asc_getcvt( 1/*pt*/, u, this._getPPIY() ); } return null; }; WorksheetView.prototype.getCellLeftRelative = function ( col, units ) { if ( col < 0 || col >= this.cols.length ) { return null; } // С учетом видимой области var offsetX = 0; if ( this.topLeftFrozenCell ) { var cFrozen = this.topLeftFrozenCell.getCol0(); offsetX = (col < cFrozen) ? 0 : this.cols[this.visibleRange.c1].left - this.cols[cFrozen].left; } else { offsetX = this.cols[this.visibleRange.c1].left - this.cellsLeft; } var u = units >= 0 && units <= 3 ? units : 0; return (this.cols[col].left - offsetX) * asc_getcvt( 1/*pt*/, u, this._getPPIX() ); }; WorksheetView.prototype.getCellTopRelative = function ( row, units ) { if ( row < 0 || row >= this.rows.length ) { return null; } // С учетом видимой области var offsetY = 0; if ( this.topLeftFrozenCell ) { var rFrozen = this.topLeftFrozenCell.getRow0(); offsetY = (row < rFrozen) ? 0 : this.rows[this.visibleRange.r1].top - this.rows[rFrozen].top; } else { offsetY = this.rows[this.visibleRange.r1].top - this.cellsTop; } var u = units >= 0 && units <= 3 ? units : 0; return (this.rows[row].top - offsetY) * asc_getcvt( 1/*pt*/, u, this._getPPIY() ); }; WorksheetView.prototype.getColumnWidth = function ( index, units ) { if ( index >= 0 && index < this.cols.length ) { var u = units >= 0 && units <= 3 ? units : 0; return this.cols[index].width * asc_getcvt( 1/*pt*/, u, this._getPPIX() ); } return null; }; WorksheetView.prototype.getSelectedColumnWidthInSymbols = function () { var c, res = null; for ( c = this.activeRange.c1; c <= this.activeRange.c2 && c < this.cols.length; ++c ) { if ( null === res ) { res = this.cols[c].charCount; } else if ( res !== this.cols[c].charCount ) { return null; } } // ToDo сравнить с default для проверки выделения всего return res; }; WorksheetView.prototype.getSelectedRowHeight = function () { var r, res = null; for ( r = this.activeRange.r1; r <= this.activeRange.r2 && r < this.rows.length; ++r ) { if ( null === res ) { res = this.rows[r].heightReal; } else if ( res !== this.rows[r].heightReal ) { return null; } } // ToDo сравнить с default для проверки выделения всего return res; }; WorksheetView.prototype.getRowHeight = function ( index, units ) { if ( index >= 0 && index < this.rows.length ) { var u = units >= 0 && units <= 3 ? units : 0; return this.rows[index].height * asc_getcvt( 1/*pt*/, u, this._getPPIY() ); } return null; }; WorksheetView.prototype.getSelectedColumnIndex = function () { return this.activeRange.startCol; }; WorksheetView.prototype.getSelectedRowIndex = function () { return this.activeRange.startRow; }; WorksheetView.prototype.getSelectedRange = function () { return this._getRange( this.activeRange.c1, this.activeRange.r1, this.activeRange.c2, this.activeRange.r2 ); }; WorksheetView.prototype.resize = function ( isUpdate ) { if ( isUpdate ) { this._initCellsArea( true ); this._normalizeViewRange(); this._cleanCellsTextMetricsCache(); this._prepareCellTextMetricsCache(); this.updateResize = false; this.objectRender.resizeCanvas(); } else { this.updateResize = true; } return this; }; WorksheetView.prototype.getZoom = function () { return this.drawingCtx.getZoom(); }; WorksheetView.prototype.changeZoom = function ( isUpdate ) { if ( isUpdate ) { this.notUpdateRowHeight = true; this.cleanSelection(); this._initCellsArea( false ); this._normalizeViewRange(); this._cleanCellsTextMetricsCache(); this._shiftVisibleRange(); this._prepareCellTextMetricsCache(); this._shiftVisibleRange(); this.cellCommentator.updateCommentPosition(); this.handlers.trigger( "onDocumentPlaceChanged" ); this.objectRender.drawingArea.reinitRanges(); this.updateZoom = false; this.notUpdateRowHeight = false; } else { this.updateZoom = true; } return this; }; WorksheetView.prototype.changeZoomResize = function () { this.cleanSelection(); this._initCellsArea( true ); this._normalizeViewRange(); this._cleanCellsTextMetricsCache(); this._shiftVisibleRange(); this._prepareCellTextMetricsCache(); this._shiftVisibleRange(); this.cellCommentator.updateCommentPosition(); this.handlers.trigger( "onDocumentPlaceChanged" ); this.objectRender.drawingArea.reinitRanges(); this.updateResize = false; this.updateZoom = false; }; WorksheetView.prototype.getCellTextMetrics = function ( col, row ) { var ct = this._getCellTextCache( col, row ); return ct ? $.extend( {}, ct.metrics ) : undefined; }; WorksheetView.prototype.getSheetViewSettings = function () { return this.model.getSheetViewSettings(); }; WorksheetView.prototype.getFrozenPaneOffset = function ( noX, noY ) { var offsetX = 0, offsetY = 0, c = this.cols, r = this.rows; if ( this.topLeftFrozenCell ) { if ( !noX ) { var cFrozen = this.topLeftFrozenCell.getCol0(); offsetX = c[cFrozen].left - c[0].left; } if ( !noY ) { var rFrozen = this.topLeftFrozenCell.getRow0(); offsetY = r[rFrozen].top - r[0].top; } } return {offsetX: offsetX, offsetY: offsetY}; }; // mouseX - это разница стартовых координат от мыши при нажатии и границы WorksheetView.prototype.changeColumnWidth = function ( col, x2, mouseX ) { var t = this; x2 *= asc_getcvt( 0/*px*/, 1/*pt*/, t._getPPIX() ); // Учитываем координаты точки, где мы начали изменение размера x2 += mouseX; var offsetFrozenX = 0; var c1 = t.visibleRange.c1; if ( this.topLeftFrozenCell ) { var cFrozen = this.topLeftFrozenCell.getCol0() - 1; if ( 0 <= cFrozen ) { if ( col < c1 ) { c1 = 0; } else { offsetFrozenX = t.cols[cFrozen].left + t.cols[cFrozen].width - t.cols[0].left; } } } var offsetX = t.cols[c1].left - t.cellsLeft; offsetX -= offsetFrozenX; var x1 = t.cols[col].left - offsetX - this.width_1px; var w = Math.max( x2 - x1, 0 ); if ( w === t.cols[col].width ) { return; } var cc = Math.min( t._colWidthToCharCount( w ), c_oAscMaxColumnWidth ); var cw = t._charCountToModelColWidth( cc ); var onChangeWidthCallback = function ( isSuccess ) { if ( false === isSuccess ) { return; } t.model.setColWidth( cw, col, col ); t._cleanCache( new asc_Range( 0, 0, t.cols.length - 1, t.rows.length - 1 ) ); t.changeWorksheet( "update", {reinitRanges: true} ); t._updateVisibleColsCount(); if ( t.objectRender ) { t.objectRender.updateSizeDrawingObjects( {target: c_oTargetType.ColumnResize, col: col} ); } }; this._isLockedAll( onChangeWidthCallback ); }; // mouseY - это разница стартовых координат от мыши при нажатии и границы WorksheetView.prototype.changeRowHeight = function ( row, y2, mouseY ) { var t = this; y2 *= asc_getcvt( 0/*px*/, 1/*pt*/, t._getPPIY() ); // Учитываем координаты точки, где мы начали изменение размера y2 += mouseY; var offsetFrozenY = 0; var r1 = t.visibleRange.r1; if ( this.topLeftFrozenCell ) { var rFrozen = this.topLeftFrozenCell.getRow0() - 1; if ( 0 <= rFrozen ) { if ( row < r1 ) { r1 = 0; } else { offsetFrozenY = t.rows[rFrozen].top + t.rows[rFrozen].height - t.rows[0].top; } } } var offsetY = t.rows[r1].top - t.cellsTop; offsetY -= offsetFrozenY; var y1 = t.rows[row].top - offsetY - this.height_1px; var newHeight = Math.min( t.maxRowHeight, Math.max( y2 - y1, 0 ) ); if ( newHeight === t.rows[row].height ) { return; } var onChangeHeightCallback = function ( isSuccess ) { if ( false === isSuccess ) { return; } t.model.setRowHeight( newHeight, row, row, true ); t.autoFilters.reDrawFilter( null, row ); t._cleanCache( new asc_Range( 0, row, t.cols.length - 1, row ) ); t.changeWorksheet( "update", {reinitRanges: true} ); t._updateVisibleRowsCount(); if ( t.objectRender ) { t.objectRender.updateSizeDrawingObjects( {target: c_oTargetType.RowResize, row: row} ); } }; this._isLockedAll( onChangeHeightCallback ); }; // Проверяет, есть ли числовые значения в диапазоне WorksheetView.prototype._hasNumberValueInActiveRange = function () { var cell, cellType, isNumberFormat, arrCols = null, arrRows = null; if ( this._rangeIsSingleCell( this.activeRange ) ) { // Для одной ячейки не стоит ничего делать return null; } var mergedRange = this.model.getMergedByCell( this.activeRange.r1, this.activeRange.c1 ); if ( mergedRange && mergedRange.isEqual( this.activeRange ) ) { // Для одной ячейки не стоит ничего делать return null; } for ( var c = this.activeRange.c1; c <= this.activeRange.c2; ++c ) { for ( var r = this.activeRange.r1; r <= this.activeRange.r2; ++r ) { cell = this._getCellTextCache( c, r ); if ( cell ) { // Нашли не пустую ячейку, проверим формат cellType = cell.cellType; isNumberFormat = (null == cellType || CellValueType.Number === cellType); if ( isNumberFormat ) { if ( !arrCols ) { arrCols = []; arrRows = []; } arrCols.push( c ); arrRows.push( r ); } } } } if ( arrCols ) { // Делаем массивы уникальными и сортируем arrCols = arrCols.filter( fOnlyUnique ); arrRows = arrRows.filter( fOnlyUnique ); return {arrCols: arrCols.sort( fSortAscending ), arrRows: arrRows.sort( fSortAscending )}; } else { return null; } }; // Автодополняет формулу диапазоном, если это возможно WorksheetView.prototype.autoCompleteFormula = function ( functionName ) { var t = this; this.activeRange.normalize(); var ar = this.activeRange; var arCopy = null; var arHistorySelect = ar.clone( true ); var vr = this.visibleRange; // Первая верхняя не числовая ячейка var topCell = null; // Первая левая не числовая ячейка var leftCell = null; var r = ar.startRow - 1; var c = ar.startCol - 1; var cell, cellType, isNumberFormat; var result = {}; // Проверим, есть ли числовые значения в диапазоне var hasNumber = this._hasNumberValueInActiveRange(); var val, text; if ( hasNumber ) { var i; // Есть ли значения в последней строке и столбце var hasNumberInLastColumn = (ar.c2 === hasNumber.arrCols[hasNumber.arrCols.length - 1]); var hasNumberInLastRow = (ar.r2 === hasNumber.arrRows[hasNumber.arrRows.length - 1]); // Нужно уменьшить зону выделения (если она реально уменьшилась) var startCol = hasNumber.arrCols[0]; var startRow = hasNumber.arrRows[0]; // Старые границы диапазона var startColOld = ar.c1; var startRowOld = ar.r1; // Нужно ли перерисовывать var bIsUpdate = false; if ( startColOld !== startCol || startRowOld !== startRow ) { bIsUpdate = true; } if ( true === hasNumberInLastRow && true === hasNumberInLastColumn ) { bIsUpdate = true; } if ( bIsUpdate ) { this.cleanSelection(); ar.c1 = startCol; ar.r1 = startRow; if ( false === ar.contains( ar.startCol, ar.startRow ) ) { // Передвинуть первую ячейку в выделении ar.startCol = startCol; ar.startRow = startRow; } if ( true === hasNumberInLastRow && true === hasNumberInLastColumn ) { // Мы расширяем диапазон if ( 1 === hasNumber.arrRows.length ) { // Одна строка или только в последней строке есть значения... (увеличиваем вправо) ar.c2 += 1; } else { // Иначе вводим в строку вниз ar.r2 += 1; } } this._drawSelection(); } arCopy = ar.clone( true ); var functionAction = null; var changedRange = null; if ( false === hasNumberInLastColumn && false === hasNumberInLastRow ) { // Значений нет ни в последней строке ни в последнем столбце (значит нужно сделать формулы в каждой последней ячейке) changedRange = [new asc_Range( hasNumber.arrCols[0], arCopy.r2, hasNumber.arrCols[hasNumber.arrCols.length - 1], arCopy.r2 ), new asc_Range( arCopy.c2, hasNumber.arrRows[0], arCopy.c2, hasNumber.arrRows[hasNumber.arrRows.length - 1] )]; functionAction = function () { // Пройдемся по последней строке for ( i = 0; i < hasNumber.arrCols.length; ++i ) { c = hasNumber.arrCols[i]; cell = t._getVisibleCell( c, arCopy.r2 ); text = t._getCellTitle( c, arCopy.r1 ) + ":" + t._getCellTitle( c, arCopy.r2 - 1 ); val = "=" + functionName + "(" + text + ")"; // ToDo - при вводе формулы в заголовок автофильтра надо писать "0" cell.setValue( val ); } // Пройдемся по последнему столбцу for ( i = 0; i < hasNumber.arrRows.length; ++i ) { r = hasNumber.arrRows[i]; cell = t._getVisibleCell( arCopy.c2, r ); text = t._getCellTitle( arCopy.c1, r ) + ":" + t._getCellTitle( arCopy.c2 - 1, r ); val = "=" + functionName + "(" + text + ")"; cell.setValue( val ); } // Значение в правой нижней ячейке cell = t._getVisibleCell( arCopy.c2, arCopy.r2 ); text = t._getCellTitle( arCopy.c1, arCopy.r2 ) + ":" + t._getCellTitle( arCopy.c2 - 1, arCopy.r2 ); val = "=" + functionName + "(" + text + ")"; cell.setValue( val ); }; } else if ( true === hasNumberInLastRow && false === hasNumberInLastColumn ) { // Есть значения только в последней строке (значит нужно заполнить только последнюю колонку) changedRange = new asc_Range( arCopy.c2, hasNumber.arrRows[0], arCopy.c2, hasNumber.arrRows[hasNumber.arrRows.length - 1] ); functionAction = function () { // Пройдемся по последнему столбцу for ( i = 0; i < hasNumber.arrRows.length; ++i ) { r = hasNumber.arrRows[i]; cell = t._getVisibleCell( arCopy.c2, r ); text = t._getCellTitle( arCopy.c1, r ) + ":" + t._getCellTitle( arCopy.c2 - 1, r ); val = "=" + functionName + "(" + text + ")"; cell.setValue( val ); } }; } else if ( false === hasNumberInLastRow && true === hasNumberInLastColumn ) { // Есть значения только в последнем столбце (значит нужно заполнить только последнюю строчку) changedRange = new asc_Range( hasNumber.arrCols[0], arCopy.r2, hasNumber.arrCols[hasNumber.arrCols.length - 1], arCopy.r2 ); functionAction = function () { // Пройдемся по последней строке for ( i = 0; i < hasNumber.arrCols.length; ++i ) { c = hasNumber.arrCols[i]; cell = t._getVisibleCell( c, arCopy.r2 ); text = t._getCellTitle( c, arCopy.r1 ) + ":" + t._getCellTitle( c, arCopy.r2 - 1 ); val = "=" + functionName + "(" + text + ")"; cell.setValue( val ); } }; } else { // Есть значения и в последнем столбце, и в последней строке if ( 1 === hasNumber.arrRows.length ) { changedRange = new asc_Range( arCopy.c2, arCopy.r2, arCopy.c2, arCopy.r2 ); functionAction = function () { // Одна строка или только в последней строке есть значения... cell = t._getVisibleCell( arCopy.c2, arCopy.r2 ); // ToDo вводить в первое свободное место, а не сразу за диапазоном text = t._getCellTitle( arCopy.c1, arCopy.r2 ) + ":" + t._getCellTitle( arCopy.c2 - 1, arCopy.r2 ); val = "=" + functionName + "(" + text + ")"; cell.setValue( val ); }; } else { changedRange = new asc_Range( hasNumber.arrCols[0], arCopy.r2, hasNumber.arrCols[hasNumber.arrCols.length - 1], arCopy.r2 ); functionAction = function () { // Иначе вводим в строку вниз for ( i = 0; i < hasNumber.arrCols.length; ++i ) { c = hasNumber.arrCols[i]; cell = t._getVisibleCell( c, arCopy.r2 ); // ToDo вводить в первое свободное место, а не сразу за диапазоном text = t._getCellTitle( c, arCopy.r1 ) + ":" + t._getCellTitle( c, arCopy.r2 - 1 ); val = "=" + functionName + "(" + text + ")"; cell.setValue( val ); } }; } } var onAutoCompleteFormula = function ( isSuccess ) { if ( false === isSuccess ) { return; } History.Create_NewPoint(); History.SetSelection( arHistorySelect.clone() ); History.SetSelectionRedo( arCopy.clone() ); History.StartTransaction(); asc_applyFunction( functionAction ); t.handlers.trigger( "selectionMathInfoChanged", t.getSelectionMathInfo() ); History.EndTransaction(); }; // Можно ли применять автоформулу this._isLockedCells( changedRange, /*subType*/null, onAutoCompleteFormula ); result.notEditCell = true; return result; } // Ищем первую ячейку с числом for ( ; r >= vr.r1; --r ) { cell = this._getCellTextCache( ar.startCol, r ); if ( cell ) { // Нашли не пустую ячейку, проверим формат cellType = cell.cellType; isNumberFormat = (null === cellType || CellValueType.Number === cellType); if ( isNumberFormat ) { // Это число, мы нашли то, что искали topCell = { c: ar.startCol, r: r, isFormula: cell.isFormula }; // смотрим вторую ячейку if ( topCell.isFormula && r - 1 >= vr.r1 ) { cell = this._getCellTextCache( ar.startCol, r - 1 ); if ( cell && cell.isFormula ) { topCell.isFormulaSeq = true; } } break; } } } // Проверим, первой все равно должна быть колонка if ( null === topCell || topCell.r !== ar.startRow - 1 || topCell.isFormula && !topCell.isFormulaSeq ) { for ( ; c >= vr.c1; --c ) { cell = this._getCellTextCache( c, ar.startRow ); if ( cell ) { // Нашли не пустую ячейку, проверим формат cellType = cell.cellType; isNumberFormat = (null === cellType || CellValueType.Number === cellType); if ( isNumberFormat ) { // Это число, мы нашли то, что искали leftCell = { r: ar.startRow, c: c }; break; } } if ( null !== topCell ) { // Если это не первая ячейка слева от текущей и мы нашли верхнюю, то дальше не стоит искать break; } } } if ( leftCell ) { // Идем влево до первой не числовой ячейки --c; for ( ; c >= 0; --c ) { cell = this._getCellTextCache( c, ar.startRow ); if ( !cell ) { // Могут быть еще не закешированные данные this._addCellTextToCache( c, ar.startRow ); cell = this._getCellTextCache( c, ar.startRow ); if ( !cell ) { break; } } cellType = cell.cellType; isNumberFormat = (null === cellType || CellValueType.Number === cellType); if ( !isNumberFormat ) { break; } } // Мы ушли чуть дальше ++c; // Диапазон или только 1 ячейка if ( ar.startCol - 1 !== c ) { // Диапазон result = new asc_Range( c, leftCell.r, ar.startCol - 1, leftCell.r ); } else { // Одна ячейка result = new asc_Range( c, leftCell.r, c, leftCell.r ); } result.type = c_oAscSelectionType.RangeCells; this._fixSelectionOfMergedCells( result ); if ( result.c1 === result.c2 && result.r1 === result.r2 ) { result.text = this._getCellTitle( result.c1, result.r1 ); } else { result.text = this._getCellTitle( result.c1, result.r1 ) + ":" + this._getCellTitle( result.c2, result.r2 ); } return result; } if ( topCell ) { // Идем вверх до первой не числовой ячейки --r; for ( ; r >= 0; --r ) { cell = this._getCellTextCache( ar.startCol, r ); if ( !cell ) { // Могут быть еще не закешированные данные this._addCellTextToCache( ar.startCol, r ); cell = this._getCellTextCache( ar.startCol, r ); if ( !cell ) { break; } } cellType = cell.cellType; isNumberFormat = (null === cellType || CellValueType.Number === cellType); if ( !isNumberFormat ) { break; } } // Мы ушли чуть дальше ++r; // Диапазон или только 1 ячейка if ( ar.startRow - 1 !== r ) { // Диапазон result = new asc_Range( topCell.c, r, topCell.c, ar.startRow - 1 ); } else { // Одна ячейка result = new asc_Range( topCell.c, r, topCell.c, r ); } result.type = c_oAscSelectionType.RangeCells; this._fixSelectionOfMergedCells( result ); if ( result.c1 === result.c2 && result.r1 === result.r2 ) { result.text = this._getCellTitle( result.c1, result.r1 ); } else { result.text = this._getCellTitle( result.c1, result.r1 ) + ":" + this._getCellTitle( result.c2, result.r2 ); } return result; } }; // ----- Initialization ----- WorksheetView.prototype._init = function () { this._initConstValues(); this._initWorksheetDefaultWidth(); this._initPane(); this._initCellsArea( true ); this.autoFilters.addFiltersAfterOpen(); this._initConditionalFormatting(); this._cleanCellsTextMetricsCache(); this._prepareCellTextMetricsCache(); // initializing is completed this.handlers.trigger( "initialized" ); }; WorksheetView.prototype._initConditionalFormatting = function () { var oGradient = null; var aCFs = this.model.aConditionalFormatting; var aRules, oRule; var oRuleElement = null; var min = Number.MAX_VALUE; var max = -Number.MAX_VALUE; var tmp; var arrayCells = []; for ( var i in aCFs ) { aRules = aCFs[i].aRules; if ( 0 >= aRules.length ) { continue; } for ( var j in aRules ) { oRule = aRules[j]; // ToDo aboveAverage, beginsWith, cellIs, containsBlanks, containsErrors, // ToDo containsText, dataBar, duplicateValues, endsWith, expression, iconSet, notContainsBlanks, // ToDo notContainsErrors, notContainsText, timePeriod, top10, uniqueValues (page 2679) switch ( oRule.Type ) { case Asc.ECfType.colorScale: if ( 1 !== oRule.aRuleElements.length ) { break; } oRuleElement = oRule.aRuleElements[0]; // ToDo убрать null === aCFs[i].SqRefRange когда научимся мультиселект обрабатывать (\\192.168.5.2\source\DOCUMENTS\XLSX\Matematika Quantum Sedekah.xlsx) if ( !(oRuleElement instanceof asc.CColorScale) || null === aCFs[i].SqRefRange ) { break; } aCFs[i].SqRefRange._setPropertyNoEmpty( null, null, function ( c ) { if ( CellValueType.Number === c.getType() && false === c.isEmptyTextString() ) { tmp = parseFloat( c.getValueWithoutFormat() ); if ( isNaN( tmp ) ) { return; } arrayCells.push( {cell: c, val: tmp} ); min = Math.min( min, tmp ); max = Math.max( max, tmp ); } } ); // ToDo CFVO Type (formula, max, min, num, percent, percentile) (page 2681) // ToDo support 3 colors in rule if ( 0 < arrayCells.length && 2 === oRuleElement.aColors.length ) { oGradient = new asc.CGradient( oRuleElement.aColors[0], oRuleElement.aColors[1] ); oGradient.init( min, max ); for ( var cell in arrayCells ) { var dxf = new CellXfs(); dxf.fill = new Fill( {bg: oGradient.calculateColor( arrayCells[cell].val )} ); arrayCells[cell].cell.setConditionalFormattingStyle( dxf ); } } arrayCells.splice( 0, arrayCells.length ); min = Number.MAX_VALUE; max = -Number.MAX_VALUE; break; } } } }; WorksheetView.prototype._prepareComments = function () { // Теперь получение всех комментариев через asc_getWorkbookComments var commentList = this.cellCommentator.prepareComments( this.model.aComments ); if ( 0 < commentList.length ) { this.model.workbook.handlers.trigger( "asc_onAddComments", commentList ); } }; WorksheetView.prototype._prepareDrawingObjects = function () { this.objectRender = new DrawingObjects(); if ( !window["NATIVE_EDITOR_ENJINE"] || window['IS_NATIVE_EDITOR'] || window['DoctRendererMode'] ) { this.objectRender.init( this ); } }; WorksheetView.prototype._initWorksheetDefaultWidth = function () { this.nBaseColWidth = this.model.oSheetFormatPr.nBaseColWidth || this.nBaseColWidth; // Теперь рассчитываем число px var defaultColWidthChars = this._charCountToModelColWidth( this.nBaseColWidth ); this.defaultColWidthPx = this._modelColWidthToColWidth( defaultColWidthChars ) * asc_getcvt( 1/*pt*/, 0/*px*/, 96 ); // Делаем кратным 8 (http://support.microsoft.com/kb/214123) this.defaultColWidthPx = asc_ceil( this.defaultColWidthPx / 8 ) * 8; this.defaultColWidthChars = this._colWidthToCharCount( this.defaultColWidthPx * asc_getcvt( 0/*px*/, 1/*pt*/, 96 ) ); gc_dDefaultColWidthCharsAttribute = this._charCountToModelColWidth( this.defaultColWidthChars ); this.defaultColWidth = this._modelColWidthToColWidth( gc_dDefaultColWidthCharsAttribute ); var defaultFontSize = this.model.getDefaultFontSize(); // ToDo разобраться со значениями this._setFont( undefined, this.model.getDefaultFontName(), defaultFontSize ); var tm = this._roundTextMetrics( this.stringRender.measureString( "A" ) ); this.headersHeightByFont = tm.height; this.maxRowHeight = asc_calcnpt( c_oAscMaxRowHeight, this._getPPIY() ); this.defaultRowDescender = this._calcRowDescender( defaultFontSize ); gc_dDefaultRowHeightAttribute = this.defaultRowHeight = this.model.getDefaultHeight() || Math.max( asc_calcnpt( defaultFontSize * this.vspRatio, this._getPPIY() ) + this.height_1px, this.headersHeightByFont ); // Инициализируем число колонок и строк (при открытии). Причем нужно поставить на 1 больше, // чтобы могли показать последнюю строку/столбец (http://bugzserver/show_bug.cgi?id=23513) this.nColsCount = Math.min( this.model.getColsCount() + 1, gc_nMaxCol ); this.nRowsCount = Math.min( this.model.getRowsCount() + 1, gc_nMaxRow ); }; WorksheetView.prototype._initConstValues = function () { var ppiX = this._getPPIX(); var ppiY = this._getPPIY(); this.width_1px = asc_calcnpt( 0, ppiX, 1/*px*/ ); this.width_2px = asc_calcnpt( 0, ppiX, 2/*px*/ ); this.width_3px = asc_calcnpt( 0, ppiX, 3/*px*/ ); this.width_4px = asc_calcnpt( 0, ppiX, 4/*px*/ ); this.width_padding = asc_calcnpt( 0, ppiX, this.settings.cells.padding/*px*/ ); this.height_1px = asc_calcnpt( 0, ppiY, 1/*px*/ ); this.height_2px = asc_calcnpt( 0, ppiY, 2/*px*/ ); this.height_3px = asc_calcnpt( 0, ppiY, 3/*px*/ ); this.height_4px = asc_calcnpt( 0, ppiY, 4/*px*/ ); }; WorksheetView.prototype._initCellsArea = function ( fullRecalc ) { // calculate rows heights and visible rows if ( !(window["NATIVE_EDITOR_ENJINE"] && this.notUpdateRowHeight) ) { this._calcHeaderRowHeight(); this._calcHeightRows( fullRecalc ? 1 : 0 ); } this.visibleRange.r2 = 0; this._calcVisibleRows(); this._updateVisibleRowsCount( /*skipScrolReinit*/true ); // calculate columns widths and visible columns if ( !(window["NATIVE_EDITOR_ENJINE"] && this.notUpdateRowHeight) ) { this._calcHeaderColumnWidth(); this._calcWidthColumns( fullRecalc ? 1 : 0 ); } this.visibleRange.c2 = 0; this._calcVisibleColumns(); this._updateVisibleColsCount( /*skipScrolReinit*/true ); }; WorksheetView.prototype._initPane = function () { var pane = this.model.sheetViews[0].pane; if ( null !== pane && pane.isInit() ) { this.topLeftFrozenCell = pane.topLeftFrozenCell; this.visibleRange.r1 = this.topLeftFrozenCell.getRow0(); this.visibleRange.c1 = this.topLeftFrozenCell.getCol0(); } }; WorksheetView.prototype._fixVisibleRange = function ( range ) { var tmp; if ( null !== this.topLeftFrozenCell ) { tmp = this.topLeftFrozenCell.getRow0(); if ( range.r1 < tmp ) { range.r1 = tmp; tmp = this._findVisibleRow( range.r1, +1 ); if ( 0 < tmp ) { range.r1 = tmp; } } tmp = this.topLeftFrozenCell.getCol0(); if ( range.c1 < tmp ) { range.c1 = tmp; tmp = this._findVisibleCol( range.c1, +1 ); if ( 0 < tmp ) { range.c1 = tmp; } } } }; /** * Вычисляет ширину столбца для заданного количества символов * @param {Number} count Количество символов * @returns {Number} Ширина столбца в символах */ WorksheetView.prototype._charCountToModelColWidth = function ( count ) { if ( count <= 0 ) { return 0; } return asc_floor( (count * this.maxDigitWidth + this.settings.cells.paddingPlusBorder) / this.maxDigitWidth * 256 ) / 256; }; /** * Вычисляет ширину столбца в пунктах * @param {Number} mcw Количество символов * @returns {Number} Ширина столбца в пунктах (pt) */ WorksheetView.prototype._modelColWidthToColWidth = function ( mcw ) { var px = asc_floor( ((256 * mcw + asc_floor( 128 / this.maxDigitWidth )) / 256) * this.maxDigitWidth ); return px * asc_getcvt( 0/*px*/, 1/*pt*/, 96 ); }; /** * Вычисляет количество символов по ширине столбца * @param {Number} w Ширина столбца в пунктах * @returns {Number} Количество символов */ WorksheetView.prototype._colWidthToCharCount = function ( w ) { var px = w * asc_getcvt( 1/*pt*/, 0/*px*/, 96 ); var pxInOneCharacter = this.maxDigitWidth + this.settings.cells.paddingPlusBorder; // Когда меньше 1 символа, то просто считаем по пропорции относительно размера 1-го символа return px < pxInOneCharacter ? (1 - asc_floor( 100 * (pxInOneCharacter - px) / pxInOneCharacter + 0.49999 ) / 100) : asc_floor( (px - this.settings.cells.paddingPlusBorder) / this.maxDigitWidth * 100 + 0.5 ) / 100; }; /** * Вычисляет ширину столбца для отрисовки * @param {Number} w Ширина столбца в символах * @returns {Number} Ширина столбца в пунктах (pt) */ WorksheetView.prototype._calcColWidth = function ( w ) { var t = this; var res = {}; var useDefault = w === undefined || w === null || w === -1; var width; res.width = useDefault ? t.defaultColWidth : (width = t._modelColWidthToColWidth( w ), (width < t.width_1px ? 0 : width)); res.innerWidth = Math.max( res.width - this.width_padding * 2 - this.width_1px, 0 ); res.charCount = t._colWidthToCharCount( res.width ); return res; }; /** * Вычисляет Descender строки * @param {Number} fontSize * @returns {Number} */ WorksheetView.prototype._calcRowDescender = function ( fontSize ) { return asc_calcnpt( fontSize * (this.vspRatio - 1), this._getPPIY() ); // ToDo возможно стоит тоже использовать 96 }; /** Вычисляет ширину колонки заголовков (в pt) */ WorksheetView.prototype._calcHeaderColumnWidth = function () { if ( false === this.model.sheetViews[0].asc_getShowRowColHeaders() ) { this.headersWidth = 0; } else { // Ширина колонки заголовков считается - max число знаков в строке - перевести в символы - перевести в пикселы var numDigit = Math.max( calcDecades( this.visibleRange.r2 + 1 ), 3 ); var nCharCount = this._charCountToModelColWidth( numDigit ); this.headersWidth = this._modelColWidthToColWidth( nCharCount ); } //var w = this.emSize * Math.max( calcDecades(this.visibleRange.r2 + 1), 3) * 1.25; //this.headersWidth = asc_calcnpt(w, this._getPPIX()); this.cellsLeft = this.headersLeft + this.headersWidth; }; /** Вычисляет высоту строки заголовков (в pt) */ WorksheetView.prototype._calcHeaderRowHeight = function () { if ( false === this.model.sheetViews[0].asc_getShowRowColHeaders() ) { this.headersHeight = 0; } else //this.headersHeight = this.model.getDefaultHeight() || this.defaultRowHeight; { this.headersHeight = this.headersHeightByFont + this.height_1px; } //this.headersHeight = asc_calcnpt( this.settings.header.fontSize * this.vspRatio, this._getPPIY() ); this.cellsTop = this.headersTop + this.headersHeight; }; /** * Вычисляет ширину и позицию колонок (в pt) * @param {Number} fullRecalc 0 - без пересчета; 1 - пересчитываем все; 2 - пересчитываем новые строки */ WorksheetView.prototype._calcWidthColumns = function ( fullRecalc ) { var x = this.cellsLeft; var visibleW = this.drawingCtx.getWidth(); var obr = this.objectRender ? this.objectRender.getDrawingAreaMetrics() : {maxCol: 0, maxRow: 0}; var l = Math.max( this.model.getColsCount() + 1, this.nColsCount, obr.maxCol ); var i = 0, w, column, isBestFit, hiddenW = 0; // Берем дефалтовую ширину документа var defaultWidth = this.model.getDefaultWidth(); defaultWidth = (typeof defaultWidth === "number" && defaultWidth >= 0) ? defaultWidth : -1; if ( 1 === fullRecalc ) { this.cols = []; } else if ( 2 === fullRecalc ) { i = this.cols.length; x = this.cols[i - 1].left + this.cols[i - 1].width; } for ( ; ((0 !== fullRecalc) ? i < l || x + hiddenW < visibleW : i < this.cols.length) && i < gc_nMaxCol; ++i ) { // Получаем свойства колонки column = this.model._getColNoEmptyWithAll( i ); if ( !column ) { w = defaultWidth; // Используем дефолтное значение isBestFit = true; // Это уже оптимальная ширина } else if ( column.hd ) { w = 0; // Если столбец скрытый, ширину выставляем 0 isBestFit = false; hiddenW += this._calcColWidth( column.width ).width; } else { w = column.width || defaultWidth; isBestFit = !!(column.BestFit || (null === column.BestFit && null === column.CustomWidth)); } this.cols[i] = this._calcColWidth( w ); this.cols[i].isCustomWidth = !isBestFit; this.cols[i].left = x; x += this.cols[i].width; } this.nColsCount = Math.min( Math.max( this.nColsCount, i ), gc_nMaxCol ); }; /** * Вычисляет высоту и позицию строк (в pt) * @param {Number} fullRecalc 0 - без пересчета; 1 - пересчитываем все; 2 - пересчитываем новые строки */ WorksheetView.prototype._calcHeightRows = function ( fullRecalc ) { var y = this.cellsTop; var visibleH = this.drawingCtx.getHeight(); var obr = this.objectRender ? this.objectRender.getDrawingAreaMetrics() : {maxCol: 0, maxRow: 0}; var l = Math.max( this.model.getRowsCount() + 1, this.nRowsCount, obr.maxRow ); var defaultH = this.defaultRowHeight; var i = 0, h, hR, isCustomHeight, row, hiddenH = 0; if ( 1 === fullRecalc ) { this.rows = []; } else if ( 2 === fullRecalc ) { i = this.rows.length; y = this.rows[i - 1].top + this.rows[i - 1].height; } for ( ; ((0 !== fullRecalc) ? i < l || y + hiddenH < visibleH : i < this.rows.length) && i < gc_nMaxRow; ++i ) { row = this.model._getRowNoEmptyWithAll( i ); if ( !row ) { h = -1; // Будет использоваться дефолтная высота строки isCustomHeight = false; } else if ( 0 != (g_nRowFlag_hd & row.flags) ) { hR = h = 0; // Скрытая строка, высоту выставляем 0 isCustomHeight = true; hiddenH += row.h > 0 ? row.h - this.height_1px : defaultH; } else { isCustomHeight = 0 != (g_nRowFlag_CustomHeight & row.flags); // Берем высоту из модели, если она custom(баг 15618), либо дефолтную if ( row.h > 0 && isCustomHeight ) { hR = row.h; h = hR / 0.75; h = (h | h) * 0.75; // 0.75 - это размер 1px в pt (можно было 96/72) } else { h = -1; } } h = h < 0 ? (hR = defaultH) : h; this.rows[i] = { top : y, height : h, // Высота с точностью до 1 px heightReal : hR, // Реальная высота из файла (может быть не кратна 1 px, в Excel можно выставить через меню строки) descender : this.defaultRowDescender, isCustomHeight : isCustomHeight, isDefaultHeight: !(row && row.h > 0 && isCustomHeight) // Высота строки, вычисленная на основе текста }; y += this.rows[i].height; } this.nRowsCount = Math.min( Math.max( this.nRowsCount, i ), gc_nMaxRow ); }; /** Вычисляет диапазон индексов видимых колонок */ WorksheetView.prototype._calcVisibleColumns = function () { var l = this.cols.length; var w = this.drawingCtx.getWidth(); var sumW = this.topLeftFrozenCell ? this.cols[this.topLeftFrozenCell.getCol0()].left : this.cellsLeft; for ( var i = this.visibleRange.c1, f = false; i < l && sumW < w; ++i ) { sumW += this.cols[i].width; f = true; } this.visibleRange.c2 = i - (f ? 1 : 0); }; /** Вычисляет диапазон индексов видимых строк */ WorksheetView.prototype._calcVisibleRows = function () { var l = this.rows.length; var h = this.drawingCtx.getHeight(); var sumH = this.topLeftFrozenCell ? this.rows[this.topLeftFrozenCell.getRow0()].top : this.cellsTop; for ( var i = this.visibleRange.r1, f = false; i < l && sumH < h; ++i ) { sumH += this.rows[i].height; f = true; } this.visibleRange.r2 = i - (f ? 1 : 0); }; /** Обновляет позицию колонок (в pt) */ WorksheetView.prototype._updateColumnPositions = function () { var x = this.cellsLeft; for ( var l = this.cols.length, i = 0; i < l; ++i ) { this.cols[i].left = x; x += this.cols[i].width; } }; /** Обновляет позицию строк (в pt) */ WorksheetView.prototype._updateRowPositions = function () { var y = this.cellsTop; for ( var l = this.rows.length, i = 0; i < l; ++i ) { this.rows[i].top = y; y += this.rows[i].height; } }; /** * Добавляет колонки, пока общая ширина листа не превысит rightSide * @param {Number} rightSide Правая граница */ WorksheetView.prototype._appendColumns = function ( rightSide ) { var i = this.cols.length; var lc = this.cols[i - 1]; var done = false; for ( var x = lc.left + lc.width; i < gc_nMaxCol && (x < rightSide || !done); ++i ) { if ( x >= rightSide ) { // add +1 column at the end and exit cycle done = true; } this.cols[i] = this._calcColWidth( this.model.getColWidth( i ) ); this.cols[i].left = x; x += this.cols[i].width; this.isChanged = true; } this.nColsCount = Math.min( Math.max( this.nColsCount, i ), gc_nMaxCol ); }; /** Устанаваливает видимый диапазон ячеек максимально возможным */ WorksheetView.prototype._normalizeViewRange = function () { var t = this; var vr = t.visibleRange; var w = t.drawingCtx.getWidth() - t.cellsLeft; var h = t.drawingCtx.getHeight() - t.cellsTop; var c = t.cols; var r = t.rows; var vw = c[vr.c2].left + c[vr.c2].width - c[vr.c1].left; var vh = r[vr.r2].top + r[vr.r2].height - r[vr.r1].top; var i; var offsetFrozen = t.getFrozenPaneOffset(); vw += offsetFrozen.offsetX; vh += offsetFrozen.offsetY; if ( vw < w ) { for ( i = vr.c1 - 1; i >= 0; --i ) { vw += c[i].width; if ( vw > w ) { break; } } vr.c1 = i + 1; if ( vr.c1 >= vr.c2 ) { vr.c1 = vr.c2 - 1; } if ( vr.c1 < 0 ) { vr.c1 = 0; } } if ( vh < h ) { for ( i = vr.r1 - 1; i >= 0; --i ) { vh += r[i].height; if ( vh > h ) { break; } } vr.r1 = i + 1; if ( vr.r1 >= vr.r2 ) { vr.r1 = vr.r2 - 1; } if ( vr.r1 < 0 ) { vr.r1 = 0; } } }; WorksheetView.prototype._shiftVisibleRange = function ( range ) { var t = this; var vr = t.visibleRange; var arn = range ? range : t.activeRange.clone( true ); var i; var cFrozen = 0, rFrozen = 0; if ( this.topLeftFrozenCell ) { cFrozen = this.topLeftFrozenCell.getCol0(); rFrozen = this.topLeftFrozenCell.getRow0(); } do { if ( arn.r2 > vr.r2 ) { i = arn.r2 - vr.r2; vr.r1 += i; vr.r2 += i; t._calcVisibleRows(); continue; } if ( t._isRowDrawnPartially( arn.r2, vr.r1 ) ) { vr.r1 += 1; t._calcVisibleRows(); } if ( arn.r1 < vr.r1 && arn.r1 >= rFrozen ) { i = arn.r1 - vr.r1; vr.r1 += i; vr.r2 += i; t._calcVisibleRows(); } break; } while ( 1 ); do { if ( arn.c2 > vr.c2 ) { i = arn.c2 - vr.c2; vr.c1 += i; vr.c2 += i; t._calcVisibleColumns(); continue; } if ( t._isColDrawnPartially( arn.c2, vr.c1 ) ) { vr.c1 += 1; t._calcVisibleColumns(); } if ( arn.c1 < vr.c1 && arn.c1 >= cFrozen ) { i = arn.c1 - vr.c1; vr.c1 += i; vr.c2 += i; if ( vr.c1 < 0 ) { vr.c1 = 0; vr.c2 -= vr.c1; } t._calcVisibleColumns(); } break; } while ( 1 ); }; // ----- Drawing for print ----- WorksheetView.prototype.calcPagesPrint = function(pageOptions, printOnlySelection, indexWorksheet, bFitToWidth, bFitToHeight) { var range; var maxCols = this.model.getColsCount(); var maxRows = this.model.getRowsCount(); var lastC = -1, lastR = -1; var activeRange = printOnlySelection ? this.activeRange : null; if ( null === activeRange ) { range = new asc_Range( 0, 0, maxCols, maxRows ); this._prepareCellTextMetricsCache( range ); for ( var c = 0; c < maxCols; ++c ) { for ( var r = 0; r < maxRows; ++r ) { if ( !this._isCellEmptyOrMergedOrBackgroundColorOrBorders( c, r ) ) { var rightSide = 0; var ct = this._getCellTextCache( c, r ); if ( ct !== undefined ) { if ( !ct.flags.isMerged() && !ct.flags.wrapText ) { rightSide = ct.sideR; } } lastC = Math.max( lastC, c + rightSide ); lastR = Math.max( lastR, r ); } } } maxCols = lastC + 1; maxRows = lastR + 1; // Получаем максимальную колонку/строку для изображений/чатов var maxObjectsCoord = this.objectRender.getDrawingAreaMetrics(); if ( maxObjectsCoord ) { maxCols = Math.max( maxCols, maxObjectsCoord.maxCol ); maxRows = Math.max( maxRows, maxObjectsCoord.maxRow ); } } else { maxCols = activeRange.c2 + 1; maxRows = activeRange.r2 + 1; range = new asc_Range( 0, 0, maxCols, maxRows ); this._prepareCellTextMetricsCache( range ); } var pageMargins, pageSetup, pageGridLines, pageHeadings; if ( pageOptions instanceof asc_CPageOptions ) { pageMargins = pageOptions.asc_getPageMargins(); pageSetup = pageOptions.asc_getPageSetup(); pageGridLines = pageOptions.asc_getGridLines(); pageHeadings = pageOptions.asc_getHeadings(); } var pageWidth, pageHeight, pageOrientation; if ( pageSetup instanceof asc_CPageSetup ) { pageWidth = pageSetup.asc_getWidth(); pageHeight = pageSetup.asc_getHeight(); pageOrientation = pageSetup.asc_getOrientation(); } var pageLeftField, pageRightField, pageTopField, pageBottomField; if ( pageMargins instanceof asc_CPageMargins ) { pageLeftField = pageMargins.asc_getLeft(); pageRightField = pageMargins.asc_getRight(); pageTopField = pageMargins.asc_getTop(); pageBottomField = pageMargins.asc_getBottom(); } if ( null == pageGridLines ) { pageGridLines = c_oAscPrintDefaultSettings.PageGridLines; } if ( null == pageHeadings ) { pageHeadings = c_oAscPrintDefaultSettings.PageHeadings; } if ( null == pageWidth ) { pageWidth = c_oAscPrintDefaultSettings.PageWidth; } if ( null == pageHeight ) { pageHeight = c_oAscPrintDefaultSettings.PageHeight; } if ( null == pageOrientation ) { pageOrientation = c_oAscPrintDefaultSettings.PageOrientation; } if ( null == pageLeftField ) { pageLeftField = c_oAscPrintDefaultSettings.PageLeftField; } if ( null == pageRightField ) { pageRightField = c_oAscPrintDefaultSettings.PageRightField; } if ( null == pageTopField ) { pageTopField = c_oAscPrintDefaultSettings.PageTopField; } if ( null == pageBottomField ) { pageBottomField = c_oAscPrintDefaultSettings.PageBottomField; } if ( c_oAscPageOrientation.PageLandscape === pageOrientation ) { var tmp = pageWidth; pageWidth = pageHeight; pageHeight = tmp; } var arrResult = []; if ( 0 === maxCols || 0 === maxRows ) { // Ничего нет, возвращаем пустой массив return null; } else { var pageWidthWithFields = pageWidth - pageLeftField - pageRightField; var pageHeightWithFields = pageHeight - pageTopField - pageBottomField; var leftFieldInPt = pageLeftField / vector_koef; var topFieldInPt = pageTopField / vector_koef; var rightFieldInPt = pageRightField / vector_koef; var bottomFieldInPt = pageBottomField / vector_koef; if ( pageHeadings ) { // Рисуем заголовки, нужно чуть сдвинуться leftFieldInPt += this.cellsLeft; topFieldInPt += this.cellsTop; } var pageWidthWithFieldsHeadings = (pageWidth - pageRightField) / vector_koef - leftFieldInPt; var pageHeightWithFieldsHeadings = (pageHeight - pageBottomField) / vector_koef - topFieldInPt; var currentColIndex = (null !== activeRange) ? activeRange.c1 : 0; var currentWidth = 0; var currentRowIndex = (null !== activeRange) ? activeRange.r1 : 0; var currentHeight = 0; var isCalcColumnsWidth = true; var bIsAddOffset = false; var nCountOffset = 0; while ( true ) { if ( currentColIndex === maxCols && currentRowIndex === maxRows ) { break; } var newPagePrint = new asc_CPagePrint(); var colIndex = currentColIndex, rowIndex = currentRowIndex; newPagePrint.indexWorksheet = indexWorksheet; newPagePrint.pageWidth = pageWidth; newPagePrint.pageHeight = pageHeight; newPagePrint.pageClipRectLeft = pageLeftField / vector_koef; newPagePrint.pageClipRectTop = pageTopField / vector_koef; newPagePrint.pageClipRectWidth = pageWidthWithFields / vector_koef; newPagePrint.pageClipRectHeight = pageHeightWithFields / vector_koef; newPagePrint.leftFieldInPt = leftFieldInPt; newPagePrint.topFieldInPt = topFieldInPt; newPagePrint.rightFieldInPt = rightFieldInPt; newPagePrint.bottomFieldInPt = bottomFieldInPt; for ( rowIndex = currentRowIndex; rowIndex < maxRows; ++rowIndex ) { var currentRowHeight = this.rows[rowIndex].height; if (!bFitToHeight && currentHeight + currentRowHeight > pageHeightWithFieldsHeadings) { // Закончили рисовать страницу break; } if ( isCalcColumnsWidth ) { for ( colIndex = currentColIndex; colIndex < maxCols; ++colIndex ) { var currentColWidth = this.cols[colIndex].width; if ( bIsAddOffset ) { newPagePrint.startOffset = ++nCountOffset; newPagePrint.startOffsetPt = (pageWidthWithFieldsHeadings * newPagePrint.startOffset); currentColWidth -= newPagePrint.startOffsetPt; } if (!bFitToWidth && currentWidth + currentColWidth > pageWidthWithFieldsHeadings && colIndex !== currentColIndex) { break; } currentWidth += currentColWidth; if (!bFitToWidth && currentWidth > pageWidthWithFieldsHeadings && colIndex === currentColIndex) { // Смещаем в селедующий раз ячейку bIsAddOffset = true; ++colIndex; break; } else { bIsAddOffset = false; } } isCalcColumnsWidth = false; if ( pageHeadings ) { currentWidth += this.cellsLeft; } if (bFitToWidth) { newPagePrint.pageClipRectWidth = Math.max( currentWidth, newPagePrint.pageClipRectWidth ); newPagePrint.pageWidth = newPagePrint.pageClipRectWidth * vector_koef + (pageLeftField + pageRightField); } else { newPagePrint.pageClipRectWidth = Math.min( currentWidth, newPagePrint.pageClipRectWidth ); } } currentHeight += currentRowHeight; currentWidth = 0; } if (bFitToHeight) { newPagePrint.pageClipRectHeight = Math.max(currentHeight, newPagePrint.pageClipRectHeight); newPagePrint.pageHeight = newPagePrint.pageClipRectHeight * vector_koef + (pageTopField + pageBottomField); } // Нужно будет пересчитывать колонки isCalcColumnsWidth = true; // Рисуем сетку if ( pageGridLines ) { newPagePrint.pageGridLines = true; } if ( pageHeadings ) { // Нужно отрисовать заголовки newPagePrint.pageHeadings = true; } newPagePrint.pageRange = new asc_Range( currentColIndex, currentRowIndex, colIndex - 1, rowIndex - 1 ); if ( bIsAddOffset ) { // Мы еще не дорисовали колонку colIndex -= 1; } else { nCountOffset = 0; } if ( colIndex < maxCols ) { // Мы еще не все колонки отрисовали currentColIndex = colIndex; currentHeight = 0; } else { // Мы дорисовали все колонки, нужна новая строка и стартовая колонка currentColIndex = (null !== activeRange) ? activeRange.c1 : 0; currentRowIndex = rowIndex; currentHeight = 0; } if ( rowIndex === maxRows ) { // Мы вышли, т.к. дошли до конца отрисовки по строкам if ( colIndex < maxCols ) { currentColIndex = colIndex; currentHeight = 0; } else { // Мы дошли до конца отрисовки currentColIndex = colIndex; currentRowIndex = rowIndex; } } arrResult.push( newPagePrint ); } } return arrResult; }; WorksheetView.prototype.drawForPrint = function ( drawingCtx, printPagesData ) { var isAppBridge = (undefined != window['appBridge']); if ( null === printPagesData ) { // Напечатаем пустую страницу drawingCtx.BeginPage( c_oAscPrintDefaultSettings.PageWidth, c_oAscPrintDefaultSettings.PageHeight ); drawingCtx.EndPage(); } else { drawingCtx.BeginPage( printPagesData.pageWidth, printPagesData.pageHeight ); drawingCtx.AddClipRect( printPagesData.pageClipRectLeft, printPagesData.pageClipRectTop, printPagesData.pageClipRectWidth, printPagesData.pageClipRectHeight ); if ( isAppBridge ) { window['appBridge']['dummyCommandUpdate'](); } var offsetCols = printPagesData.startOffsetPt; var range = printPagesData.pageRange; var offsetX = this.cols[range.c1].left - printPagesData.leftFieldInPt + offsetCols; var offsetY = this.rows[range.r1].top - printPagesData.topFieldInPt; var tmpVisibleRange = this.visibleRange; // Сменим visibleRange для прохождения проверок отрисовки this.visibleRange = range; if ( isAppBridge ) { window['appBridge']['dummyCommandUpdate'](); } // Нужно отрисовать заголовки if ( printPagesData.pageHeadings ) { this._drawColumnHeaders( drawingCtx, range.c1, range.c2, /*style*/ undefined, offsetX, printPagesData.topFieldInPt - this.cellsTop ); this._drawRowHeaders( drawingCtx, range.r1, range.r2, /*style*/ undefined, printPagesData.leftFieldInPt - this.cellsLeft, offsetY ); } if ( isAppBridge ) { window['appBridge']['dummyCommandUpdate'](); } // Рисуем сетку if ( printPagesData.pageGridLines ) { this._drawGrid( drawingCtx, range, offsetX, offsetY, printPagesData.pageWidth / vector_koef, printPagesData.pageHeight / vector_koef ); } if ( isAppBridge ) { window['appBridge']['dummyCommandUpdate'](); } // Отрисовываем ячейки и бордеры this._drawCellsAndBorders( drawingCtx, range, offsetX, offsetY ); if ( isAppBridge ) { window['appBridge']['dummyCommandUpdate'](); } var drawingPrintOptions = { ctx: drawingCtx, printPagesData: printPagesData }; this.objectRender.showDrawingObjectsEx( false, null, drawingPrintOptions ); this.visibleRange = tmpVisibleRange; if ( isAppBridge ) { window['appBridge']['dummyCommandUpdate'](); } drawingCtx.RemoveClipRect(); drawingCtx.EndPage(); } }; // ----- Drawing ----- WorksheetView.prototype.draw = function ( lockDraw ) { if ( lockDraw || this.model.workbook.bCollaborativeChanges || window['IS_NATIVE_EDITOR'] ) { return this; } this._clean(); this._drawCorner(); this._drawColumnHeaders( /*drawingCtx*/ undefined ); this._drawRowHeaders( /*drawingCtx*/ undefined ); this._drawGrid( /*drawingCtx*/ undefined ); this._drawCellsAndBorders( /*drawingCtx*/undefined ); this._drawFrozenPane(); this._drawFrozenPaneLines(); this._fixSelectionOfMergedCells(); this._drawAutoF(); this.cellCommentator.drawCommentCells(); this.objectRender.showDrawingObjectsEx( true ); if ( this.overlayCtx ) { this._drawSelection(); } return this; }; WorksheetView.prototype._clean = function () { this.drawingCtx .setFillStyle( this.settings.cells.defaultState.background ) .fillRect( 0, 0, this.drawingCtx.getWidth(), this.drawingCtx.getHeight() ); if ( this.overlayCtx ) { this.overlayCtx.clear(); } }; WorksheetView.prototype.drawHighlightedHeaders = function ( col, row ) { this._activateOverlayCtx(); if ( col >= 0 && col !== this.highlightedCol ) { this._doCleanHighlightedHeaders(); this.highlightedCol = col; this._drawColumnHeaders( /*drawingCtx*/ undefined, col, col, kHeaderHighlighted ); } else if ( row >= 0 && row !== this.highlightedRow ) { this._doCleanHighlightedHeaders(); this.highlightedRow = row; this._drawRowHeaders( /*drawingCtx*/ undefined, row, row, kHeaderHighlighted ); } this._deactivateOverlayCtx(); return this; }; WorksheetView.prototype.cleanHighlightedHeaders = function () { this._activateOverlayCtx(); this._doCleanHighlightedHeaders(); this._deactivateOverlayCtx(); return this; }; WorksheetView.prototype._activateOverlayCtx = function () { this.drawingCtx = this.buffers.overlay; }; WorksheetView.prototype._deactivateOverlayCtx = function () { this.drawingCtx = this.buffers.main; }; WorksheetView.prototype._doCleanHighlightedHeaders = function () { var hlc = this.highlightedCol, hlr = this.highlightedRow, arn = this.activeRange.clone( true ); var hStyle = this.objectRender.selectedGraphicObjectsExists() ? kHeaderDefault : kHeaderActive; if ( hlc >= 0 ) { if ( hlc >= arn.c1 && hlc <= arn.c2 ) { this._drawColumnHeaders( /*drawingCtx*/ undefined, hlc, hlc, hStyle ); } else { this._cleanColumnHeaders( hlc ); if ( hlc + 1 === arn.c1 ) { this._drawColumnHeaders( /*drawingCtx*/ undefined, hlc + 1, hlc + 1, kHeaderActive ); } else if ( hlc - 1 === arn.c2 ) { this._drawColumnHeaders( /*drawingCtx*/ undefined, hlc - 1, hlc - 1, hStyle ); } } this.highlightedCol = -1; } if ( hlr >= 0 ) { if ( hlr >= arn.r1 && hlr <= arn.r2 ) { this._drawRowHeaders( /*drawingCtx*/ undefined, hlr, hlr, hStyle ); } else { this._cleanRowHeades( hlr ); if ( hlr + 1 === arn.r1 ) { this._drawRowHeaders( /*drawingCtx*/ undefined, hlr + 1, hlr + 1, kHeaderActive ); } else if ( hlr - 1 === arn.r2 ) { this._drawRowHeaders( /*drawingCtx*/ undefined, hlr - 1, hlr - 1, hStyle ); } } this.highlightedRow = -1; } }; WorksheetView.prototype._drawActiveHeaders = function () { var arn = this.activeRange.clone( true ), vr = this.visibleRange, c1 = Math.max( vr.c1, arn.c1 ), c2 = Math.min( vr.c2, arn.c2 ), r1 = Math.max( vr.r1, arn.r1 ), r2 = Math.min( vr.r2, arn.r2 ); this._activateOverlayCtx(); this._drawColumnHeaders( /*drawingCtx*/ undefined, c1, c2, kHeaderActive ); this._drawRowHeaders( /*drawingCtx*/ undefined, r1, r2, kHeaderActive ); if ( this.topLeftFrozenCell ) { var cFrozen = this.topLeftFrozenCell.getCol0() - 1; var rFrozen = this.topLeftFrozenCell.getRow0() - 1; if ( 0 <= cFrozen ) { c1 = Math.max( 0, arn.c1 ); c2 = Math.min( cFrozen, arn.c2 ); this._drawColumnHeaders( /*drawingCtx*/ undefined, c1, c2, kHeaderActive ); } if ( 0 <= rFrozen ) { r1 = Math.max( 0, arn.r1 ); r2 = Math.min( rFrozen, arn.r2 ); this._drawRowHeaders( /*drawingCtx*/ undefined, r1, r2, kHeaderActive ); } } this._deactivateOverlayCtx(); }; WorksheetView.prototype._drawCorner = function () { if ( false === this.model.sheetViews[0].asc_getShowRowColHeaders() ) { return; } var x2 = this.headersLeft + this.headersWidth; var x1 = x2 - this.headersHeight; var y2 = this.headersTop + this.headersHeight; var y1 = this.headersTop; var dx = 4 * this.width_1px; var dy = 4 * this.height_1px; this._drawHeader( /*drawingCtx*/ undefined, this.headersLeft, this.headersTop, this.headersWidth, this.headersHeight, kHeaderDefault, true, -1 ); this.drawingCtx.beginPath() .moveTo( x2 - dx, y1 + dy ) .lineTo( x2 - dx, y2 - dy ) .lineTo( x1 + dx, y2 - dy ) .lineTo( x2 - dx, y1 + dy ) .setFillStyle( this.settings.header.cornerColor ) .fill(); }; /** Рисует заголовки видимых колонок */ WorksheetView.prototype._drawColumnHeaders = function ( drawingCtx, start, end, style, offsetXForDraw, offsetYForDraw ) { if ( undefined === drawingCtx && false === this.model.sheetViews[0].asc_getShowRowColHeaders() ) { return; } var vr = this.visibleRange; var c = this.cols; var offsetX = (undefined !== offsetXForDraw) ? offsetXForDraw : c[vr.c1].left - this.cellsLeft; var offsetY = (undefined !== offsetYForDraw) ? offsetYForDraw : this.headersTop; if ( undefined === drawingCtx && this.topLeftFrozenCell && undefined === offsetXForDraw ) { var cFrozen = this.topLeftFrozenCell.getCol0(); if ( start < vr.c1 ) { offsetX = c[0].left - this.cellsLeft; } else { offsetX -= c[cFrozen].left - c[0].left; } } if ( asc_typeof( start ) !== "number" ) { start = vr.c1; } if ( asc_typeof( end ) !== "number" ) { end = vr.c2; } if ( style === undefined ) { style = kHeaderDefault; } this._setFont( drawingCtx, this.model.getDefaultFontName(), this.model.getDefaultFontSize() ); // draw column headers for ( var i = start; i <= end; ++i ) { this._drawHeader( drawingCtx, c[i].left - offsetX, offsetY, c[i].width, this.headersHeight, style, true, i ); } }; /** Рисует заголовки видимых строк */ WorksheetView.prototype._drawRowHeaders = function ( drawingCtx, start, end, style, offsetXForDraw, offsetYForDraw ) { if ( undefined === drawingCtx && false === this.model.sheetViews[0].asc_getShowRowColHeaders() ) { return; } var vr = this.visibleRange; var r = this.rows; var offsetX = (undefined !== offsetXForDraw) ? offsetXForDraw : this.headersLeft; var offsetY = (undefined !== offsetYForDraw) ? offsetYForDraw : r[vr.r1].top - this.cellsTop; if ( undefined === drawingCtx && this.topLeftFrozenCell && undefined === offsetYForDraw ) { var rFrozen = this.topLeftFrozenCell.getRow0(); if ( start < vr.r1 ) { offsetY = r[0].top - this.cellsTop; } else { offsetY -= r[rFrozen].top - r[0].top; } } if ( asc_typeof( start ) !== "number" ) { start = vr.r1; } if ( asc_typeof( end ) !== "number" ) { end = vr.r2; } if ( style === undefined ) { style = kHeaderDefault; } this._setFont( drawingCtx, this.model.getDefaultFontName(), this.model.getDefaultFontSize() ); // draw row headers for ( var i = start; i <= end; ++i ) { this._drawHeader( drawingCtx, offsetX, r[i].top - offsetY, this.headersWidth, r[i].height, style, false, i ); } }; /** * Рисует заголовок, принимает координаты и размеры в pt * @param {DrawingContext} drawingCtx * @param {Number} x Координата левого угла в pt * @param {Number} y Координата левого угла в pt * @param {Number} w Ширина в pt * @param {Number} h Высота в pt * @param {Number} style Стиль заголовка (kHeaderDefault, kHeaderActive, kHeaderHighlighted) * @param {Boolean} isColHeader Тип заголовка: true - колонка, false - строка * @param {Number} index Индекс столбца/строки или -1 */ WorksheetView.prototype._drawHeader = function ( drawingCtx, x, y, w, h, style, isColHeader, index ) { // Для отрисовки невидимого столбца/строки var isZeroHeader = false; if ( -1 !== index ) { if ( isColHeader ) { if ( w < this.width_1px ) { if ( style !== kHeaderDefault ) { return; } // Это невидимый столбец isZeroHeader = true; // Отрисуем только границу w = this.width_1px; // Возможно мы уже рисовали границу невидимого столбца (для последовательности невидимых) if ( 0 < index && 0 === this.cols[index - 1].width ) { // Мы уже нарисовали border для невидимой границы return; } } else if ( 0 < index && 0 === this.cols[index - 1].width ) { // Мы уже нарисовали border для невидимой границы (поэтому нужно чуть меньше рисовать для соседнего столбца) w -= this.width_1px; x += this.width_1px; } } else { if ( h < this.height_1px ) { if ( style !== kHeaderDefault ) { return; } // Это невидимая строка isZeroHeader = true; // Отрисуем только границу h = this.height_1px; // Возможно мы уже рисовали границу невидимой строки (для последовательности невидимых) if ( 0 < index && 0 === this.rows[index - 1].height ) { // Мы уже нарисовали border для невидимой границы return; } } else if ( 0 < index && 0 === this.rows[index - 1].height ) { // Мы уже нарисовали border для невидимой границы (поэтому нужно чуть меньше рисовать для соседней строки) h -= this.height_1px; y += this.height_1px; } } } var ctx = (drawingCtx) ? drawingCtx : this.drawingCtx; var st = this.settings.header.style[style]; var x2 = x + w; var y2 = y + h; var x2WithoutBorder = x2 - this.width_1px; var y2WithoutBorder = y2 - this.height_1px; // background только для видимых if ( !isZeroHeader ) { // draw background ctx.setFillStyle( st.background ) .fillRect( x, y, w, h ); } // draw border ctx.setStrokeStyle( st.border ) .setLineWidth( 1 ) .beginPath(); if ( style !== kHeaderDefault && !isColHeader ) { // Select row (top border) ctx.lineHorPrevPx( x, y, x2 ); } // Right border ctx.lineVerPrevPx( x2, y, y2 ); // Bottom border ctx.lineHorPrevPx( x, y2, x2 ); if ( style !== kHeaderDefault && isColHeader ) { // Select col (left border) ctx.lineVerPrevPx( x, y, y2 ); } ctx.stroke(); // Для невидимых кроме border-а ничего не рисуем if ( isZeroHeader || -1 === index ) { return; } // draw text var text = isColHeader ? this._getColumnTitle( index ) : this._getRowTitle( index ); var sr = this.stringRender; var tm = this._roundTextMetrics( sr.measureString( text ) ); var bl = y2WithoutBorder - (isColHeader ? this.defaultRowDescender : this.rows[index].descender); var textX = this._calcTextHorizPos( x, x2WithoutBorder, tm, tm.width < w ? khaCenter : khaLeft ); var textY = this._calcTextVertPos( y, y2WithoutBorder, bl, tm, kvaBottom ); if ( drawingCtx ) { ctx.AddClipRect( x, y, w, h ); ctx.setFillStyle( st.color ) .fillText( text, textX, textY + tm.baseline, undefined, sr.charWidths ); ctx.RemoveClipRect(); } else { ctx.save() .beginPath() .rect( x, y, w, h ) .clip() .setFillStyle( st.color ) .fillText( text, textX, textY + tm.baseline, undefined, sr.charWidths ) .restore(); } }; WorksheetView.prototype._cleanColumnHeaders = function ( colStart, colEnd ) { var offsetX = this.cols[this.visibleRange.c1].left - this.cellsLeft; var i, cFrozen = 0; if ( this.topLeftFrozenCell ) { cFrozen = this.topLeftFrozenCell.getCol0(); offsetX -= this.cols[cFrozen].left - this.cols[0].left; } if ( colEnd === undefined ) { colEnd = colStart; } var colStartTmp = Math.max( this.visibleRange.c1, colStart ); var colEndTmp = Math.min( this.visibleRange.c2, colEnd ); for ( i = colStartTmp; i <= colEndTmp; ++i ) { this.drawingCtx.clearRectByX( this.cols[i].left - offsetX, this.headersTop, this.cols[i].width, this.headersHeight ); } if ( 0 !== cFrozen ) { offsetX = this.cols[0].left - this.cellsLeft; // Почистим для pane colStart = Math.max( 0, colStart ); colEnd = Math.min( cFrozen, colEnd ); for ( i = colStart; i <= colEnd; ++i ) { this.drawingCtx.clearRectByX( this.cols[i].left - offsetX, this.headersTop, this.cols[i].width, this.headersHeight ); } } }; WorksheetView.prototype._cleanRowHeades = function ( rowStart, rowEnd ) { var offsetY = this.rows[this.visibleRange.r1].top - this.cellsTop; var i, rFrozen = 0; if ( this.topLeftFrozenCell ) { rFrozen = this.topLeftFrozenCell.getRow0(); offsetY -= this.rows[rFrozen].top - this.rows[0].top; } if ( rowEnd === undefined ) { rowEnd = rowStart; } var rowStartTmp = Math.max( this.visibleRange.r1, rowStart ); var rowEndTmp = Math.min( this.visibleRange.r2, rowEnd ); for ( i = rowStartTmp; i <= rowEndTmp; ++i ) { if ( this.height_1px > this.rows[i].height ) { continue; } this.drawingCtx.clearRectByY( this.headersLeft, this.rows[i].top - offsetY, this.headersWidth, this.rows[i].height ); } if ( 0 !== rFrozen ) { offsetY = this.rows[0].top - this.cellsTop; // Почистим для pane rowStart = Math.max( 0, rowStart ); rowEnd = Math.min( rFrozen, rowEnd ); for ( i = rowStart; i <= rowEnd; ++i ) { if ( this.height_1px > this.rows[i].height ) { continue; } this.drawingCtx.clearRectByY( this.headersLeft, this.rows[i].top - offsetY, this.headersWidth, this.rows[i].height ); } } }; WorksheetView.prototype._cleanColumnHeadersRect = function () { this.drawingCtx.clearRect( this.cellsLeft, this.headersTop, this.drawingCtx.getWidth() - this.cellsLeft, this.headersHeight ); }; /** Рисует сетку таблицы */ WorksheetView.prototype._drawGrid = function ( drawingCtx, range, leftFieldInPt, topFieldInPt, width, height ) { // Возможно сетку не нужно рисовать (при печати свои проверки) if ( undefined === drawingCtx && false === this.model.sheetViews[0].asc_getShowGridLines() ) { return; } if ( range === undefined ) { range = this.visibleRange; } var ctx = (drawingCtx) ? drawingCtx : this.drawingCtx; var c = this.cols; var r = this.rows; var widthCtx = (width) ? width : ctx.getWidth(); var heightCtx = (height) ? height : ctx.getHeight(); var offsetX = (undefined !== leftFieldInPt) ? leftFieldInPt : c[this.visibleRange.c1].left - this.cellsLeft; var offsetY = (undefined !== topFieldInPt) ? topFieldInPt : r[this.visibleRange.r1].top - this.cellsTop; if ( undefined === drawingCtx && this.topLeftFrozenCell ) { if ( undefined === leftFieldInPt ) { var cFrozen = this.topLeftFrozenCell.getCol0(); offsetX -= c[cFrozen].left - c[0].left; } if ( undefined === topFieldInPt ) { var rFrozen = this.topLeftFrozenCell.getRow0(); offsetY -= r[rFrozen].top - r[0].top; } } var x1 = c[range.c1].left - offsetX; var y1 = r[range.r1].top - offsetY; var x2 = Math.min( c[range.c2].left - offsetX + c[range.c2].width, widthCtx ); var y2 = Math.min( r[range.r2].top - offsetY + r[range.r2].height, heightCtx ); ctx.setFillStyle( this.settings.cells.defaultState.background ) .fillRect( x1, y1, x2 - x1, y2 - y1 ) .setStrokeStyle( this.settings.cells.defaultState.border ) .setLineWidth( 1 ).beginPath(); var w, h; for ( var i = range.c1, x = x1; i <= range.c2 && x <= x2; ++i ) { w = c[i].width; x += w; if ( w >= this.width_1px ) { ctx.lineVerPrevPx( x, y1, y2 ); } } for ( var j = range.r1, y = y1; j <= range.r2 && y <= y2; ++j ) { h = r[j].height; y += h; if ( h >= this.height_1px ) { ctx.lineHorPrevPx( x1, y, x2 ); } } ctx.stroke(); }; WorksheetView.prototype._drawCellsAndBorders = function ( drawingCtx, range, offsetXForDraw, offsetYForDraw ) { if ( range === undefined ) { range = this.visibleRange; } var left, top, cFrozen, rFrozen; var offsetX = (undefined === offsetXForDraw) ? this.cols[this.visibleRange.c1].left - this.cellsLeft : offsetXForDraw; var offsetY = (undefined === offsetYForDraw) ? this.rows[this.visibleRange.r1].top - this.cellsTop : offsetYForDraw; if ( undefined === drawingCtx && this.topLeftFrozenCell ) { if ( undefined === offsetXForDraw ) { cFrozen = this.topLeftFrozenCell.getCol0(); offsetX -= this.cols[cFrozen].left - this.cols[0].left; } if ( undefined === offsetYForDraw ) { rFrozen = this.topLeftFrozenCell.getRow0(); offsetY -= this.rows[rFrozen].top - this.rows[0].top; } } if ( !drawingCtx && !window['IS_NATIVE_EDITOR'] ) { left = this.cols[range.c1].left; top = this.rows[range.r1].top; // set clipping rect to cells area this.drawingCtx.save() .beginPath() .rect( left - offsetX, top - offsetY, Math.min( this.cols[range.c2].left - left + this.cols[range.c2].width, this.drawingCtx.getWidth() - this.cellsLeft ), Math.min( this.rows[range.r2].top - top + this.rows[range.r2].height, this.drawingCtx.getHeight() - this.cellsTop ) ) .clip(); } var mergedCells = this._drawCells( drawingCtx, range, offsetX, offsetY ); this._drawCellsBorders( drawingCtx, range, offsetX, offsetY, mergedCells ); if ( !drawingCtx && !window['IS_NATIVE_EDITOR'] ) { // restore canvas' original clipping range this.drawingCtx.restore(); } }; /** Рисует ячейки таблицы */ WorksheetView.prototype._drawCells = function ( drawingCtx, range, offsetX, offsetY ) { this._prepareCellTextMetricsCache( range ); var mergedCells = {}, mc, i; for ( var row = range.r1; row <= range.r2; ++row ) { $.extend( mergedCells, this._drawRowBG( drawingCtx, row, range.c1, range.c2, offsetX, offsetY, null ), this._drawRowText( drawingCtx, row, range.c1, range.c2, offsetX, offsetY ) ); } // draw merged cells at last stage to fix cells background issue for ( i in mergedCells ) { mc = mergedCells[i]; this._drawRowBG( drawingCtx, mc.r1, mc.c1, mc.c1, offsetX, offsetY, mc ); this._drawCellText( drawingCtx, mc.c1, mc.r1, range.c1, range.c2, offsetX, offsetY, true ); } return mergedCells; }; /** Рисует фон ячеек в строке */ WorksheetView.prototype._drawRowBG = function ( drawingCtx, row, colStart, colEnd, offsetX, offsetY, oMergedCell ) { if ( this.rows[row].height < this.height_1px && null === oMergedCell ) { return {}; } var mergedCells = {}; var ctx = (undefined === drawingCtx) ? this.drawingCtx : drawingCtx; for ( var col = colStart; col <= colEnd; ++col ) { if ( this.cols[col].width < this.width_1px && null === oMergedCell ) { continue; } // ToDo подумать, может стоит не брать ячейку из модели (а брать из кеш-а) var c = this._getVisibleCell( col, row ); if ( !c ) { continue; } var bg = c.getFill(); var mc = null; var mwidth = 0, mheight = 0; if ( null === oMergedCell ) { mc = this.model.getMergedByCell( row, col ); if ( null !== mc ) { mergedCells[mc.r1 + "_" + mc.c1] = {c1: mc.c1, r1: mc.r1, c2: mc.c2, r2: mc.r2}; col = mc.c2; continue; } } else { mc = oMergedCell; } if ( null !== mc ) { if ( col !== mc.c1 || row !== mc.r1 ) { continue; } for ( var i = mc.c1 + 1; i <= mc.c2 && i < this.cols.length; ++i ) { mwidth += this.cols[i].width; } for ( var j = mc.r1 + 1; j <= mc.r2 && j < this.rows.length; ++j ) { mheight += this.rows[j].height; } } else { if ( bg === null ) { if ( col === colEnd && col < this.cols.length - 1 && row < this.rows.length - 1 ) { var c2 = this._getVisibleCell( col + 1, row ); if ( c2 ) { var bg2 = c2.getFill(); if ( bg2 !== null ) { ctx.setFillStyle( bg2 ) .fillRect( this.cols[col + 1].left - offsetX - this.width_1px, this.rows[row].top - offsetY - this.height_1px, this.width_1px, this.rows[row].height + this.height_1px ); } } var c3 = this._getVisibleCell( col, row + 1 ); if ( c3 ) { var bg3 = c3.getFill(); if ( bg3 !== null ) { ctx.setFillStyle( bg3 ) .fillRect( this.cols[col].left - offsetX - this.width_1px, this.rows[row + 1].top - offsetY - this.height_1px, this.cols[col].width + this.width_1px, this.height_1px ); } } } continue; } } var x = this.cols[col].left - (bg !== null ? this.width_1px : 0); var y = this.rows[row].top - (bg !== null ? this.height_1px : 0); var w = this.cols[col].width + this.width_1px * (bg !== null ? +1 : -1) + mwidth; var h = this.rows[row].height + this.height_1px * (bg !== null ? +1 : -1) + mheight; var color = bg !== null ? bg : this.settings.cells.defaultState.background; ctx.setFillStyle( color ).fillRect( x - offsetX, y - offsetY, w, h ); } return mergedCells; }; /** Рисует текст ячеек в строке */ WorksheetView.prototype._drawRowText = function ( drawingCtx, row, colStart, colEnd, offsetX, offsetY ) { if ( this.rows[row].height < this.height_1px ) { return {}; } var dependentCells = {}, mergedCells = {}, i, mc, col; // draw cells' text for ( col = colStart; col <= colEnd; ++col ) { if ( this.cols[col].width < this.width_1px ) { continue; } mc = this._drawCellText( drawingCtx, col, row, colStart, colEnd, offsetX, offsetY, false ); if ( mc !== null ) { mergedCells[mc.index] = {c1: mc.c1, r1: mc.r1, c2: mc.c2, r2: mc.r2}; } // check if long text overlaps this cell i = this._findSourceOfCellText( col, row ); if ( i >= 0 ) { dependentCells[i] = (dependentCells[i] || []); dependentCells[i].push( col ); } } // draw long text that overlaps own cell's borders for ( i in dependentCells ) { var arr = dependentCells[i], j = arr.length - 1; col = i >> 0; // if source cell belongs to cells range then skip it (text has been drawn already) if ( col >= arr[0] && col <= arr[j] ) { continue; } // draw long text fragment this._drawCellText( drawingCtx, col, row, arr[0], arr[j], offsetX, offsetY, false ); } return mergedCells; }; /** Рисует текст ячейки */ WorksheetView.prototype._drawCellText = function ( drawingCtx, col, row, colStart, colEnd, offsetX, offsetY, drawMergedCells ) { var ct = this._getCellTextCache( col, row ); if ( ct === undefined ) { return null; } var isMerged = ct.flags.isMerged(), range = undefined, isWrapped = ct.flags.wrapText; var ctx = (undefined === drawingCtx) ? this.drawingCtx : drawingCtx; if ( isMerged ) { range = ct.flags.merged; if ( !drawMergedCells ) { return {c1: range.c1, r1: range.r1, c2: range.c2, r2: range.r2, index: range.r1 + "_" + range.c1}; } if ( col !== range.c1 || row !== range.r1 ) { return null; } } var colL = isMerged ? range.c1 : Math.max( colStart, col - ct.sideL ); var colR = isMerged ? Math.min( range.c2, this.nColsCount - 1 ) : Math.min( colEnd, col + ct.sideR ); var rowT = isMerged ? range.r1 : row; var rowB = isMerged ? Math.min( range.r2, this.nRowsCount - 1 ) : row; var isTrimmedR = !isMerged && colR !== col + ct.sideR; if ( !(ct.angle || 0) ) { if ( !isMerged && !isWrapped ) { this._eraseCellRightBorder( drawingCtx, colL, colR + (isTrimmedR ? 1 : 0), row, offsetX, offsetY ); } } var x1 = this.cols[colL].left - offsetX; var y1 = this.rows[rowT].top - offsetY; var w = this.cols[colR].left + this.cols[colR].width - offsetX - x1; var h = this.rows[rowB].top + this.rows[rowB].height - offsetY - y1; var x2 = x1 + w - (isTrimmedR ? 0 : this.width_1px); var y2 = y1 + h - this.height_1px; var bl = !isMerged ? (y2 - this.rows[rowB].descender) : (y2 - ct.metrics.height + ct.metrics.baseline - this.height_1px); var x1ct = isMerged ? x1 : this.cols[col].left - offsetX; var x2ct = isMerged ? x2 : x1ct + this.cols[col].width - this.width_1px; var textX = this._calcTextHorizPos( x1ct, x2ct, ct.metrics, ct.cellHA ); var textY = this._calcTextVertPos( y1, y2, bl, ct.metrics, ct.cellVA ); var textW = this._calcTextWidth( x1ct, x2ct, ct.metrics, ct.cellHA ); var xb1, yb1, wb, hb, colLeft, colRight, i; var txtRotX, txtRotW, clipUse = false; if ( drawingCtx ) { // для печати if ( ct.angle || 0 ) { xb1 = this.cols[col].left - offsetX; yb1 = this.rows[row].top - offsetY; wb = this.cols[col].width; hb = this.rows[row].height; txtRotX = ct.textBound.sx + xb1; txtRotW = ct.textBound.sw + xb1; if ( isMerged ) { wb = 0; for ( i = colL; i <= colR && i < this.nColsCount; ++i ) { wb += this.cols[i].width; } hb = 0; for ( i = rowT; i <= rowB && i < this.nRowsCount; ++i ) { hb += this.rows[i].height; } ctx.AddClipRect( xb1, yb1, wb, hb ); clipUse = true; } this.stringRender.angle = ct.angle; this.stringRender.fontNeedUpdate = true; if ( 90 === ct.angle || -90 === ct.angle ) { // клип по ячейке if ( !isMerged ) { ctx.AddClipRect( xb1, yb1, wb, hb ); clipUse = true; } } else { // клип по строке if ( !isMerged ) { ctx.AddClipRect( 0, yb1, this.drawingCtx.getWidth(), h ); clipUse = true; } if ( !isMerged && !isWrapped ) { colLeft = col; if ( 0 !== txtRotX ) { while ( true ) { if ( 0 == colLeft ) { break; } if ( txtRotX >= this.cols[colLeft].left ) { break; } --colLeft; } } colRight = Math.min( col, this.nColsCount - 1 ); if ( 0 !== txtRotW ) { while ( true ) { ++colRight; if ( colRight >= this.nColsCount ) { --colRight; break; } if ( txtRotW <= this.cols[colRight].left ) { --colRight; break; } } } colLeft = isMerged ? range.c1 : colLeft; colRight = isMerged ? Math.min( range.c2, this.nColsCount - 1 ) : colRight; this._eraseCellRightBorder( drawingCtx, colLeft, colRight + (isTrimmedR ? 1 : 0), row, offsetX, offsetY ); } } this.stringRender.rotateAtPoint( drawingCtx, ct.angle, xb1, yb1, ct.textBound.dx, ct.textBound.dy ); this.stringRender.restoreInternalState( ct.state ).renderForPrint( drawingCtx, 0, 0, textW, ct.color ); this.stringRender.resetTransform( drawingCtx ); if ( clipUse ) { ctx.RemoveClipRect(); } } else { ctx.AddClipRect( x1, y1, w, h ); this.stringRender.restoreInternalState( ct.state ).renderForPrint( drawingCtx, textX, textY, textW, ct.color ); ctx.RemoveClipRect(); } } else { // для отрисовки if ( ct.angle || 0 ) { xb1 = this.cols[col].left - offsetX; yb1 = this.rows[row].top - offsetY; wb = this.cols[col].width; hb = this.rows[row].height; txtRotX = ct.textBound.sx + xb1; txtRotW = ct.textBound.sw + xb1; if ( isMerged ) { wb = 0; for ( i = colL; i <= colR && i < this.nColsCount; ++i ) { wb += this.cols[i].width; } hb = 0; for ( i = rowT; i <= rowB && i < this.nRowsCount; ++i ) { hb += this.rows[i].height; } ctx.save().beginPath().rect( xb1, yb1, wb, hb ).clip(); clipUse = true; } this.stringRender.angle = ct.angle; this.stringRender.fontNeedUpdate = true; if ( 90 === ct.angle || -90 === ct.angle ) { // клип по ячейке if ( !isMerged ) { ctx.save().beginPath().rect( xb1, yb1, wb, hb ).clip(); clipUse = true; } } else { // клип по строке if ( !isMerged ) { ctx.save().beginPath().rect( 0, y1, this.drawingCtx.getWidth(), h ).clip(); clipUse = true; } if ( !isMerged && !isWrapped ) { colLeft = col; if ( 0 !== txtRotX ) { while ( true ) { if ( 0 == colLeft ) { break; } if ( txtRotX >= this.cols[colLeft].left ) { break; } --colLeft; } } colRight = Math.min( col, this.nColsCount - 1 ); if ( 0 !== txtRotW ) { while ( true ) { ++colRight; if ( colRight >= this.nColsCount ) { --colRight; break; } if ( txtRotW <= this.cols[colRight].left ) { --colRight; break; } } } colLeft = isMerged ? range.c1 : colLeft; colRight = isMerged ? Math.min( range.c2, this.nColsCount - 1 ) : colRight; this._eraseCellRightBorder( drawingCtx, colLeft, colRight + (isTrimmedR ? 1 : 0), row, offsetX, offsetY ); } } this.stringRender.rotateAtPoint( null, ct.angle, xb1, yb1, ct.textBound.dx, ct.textBound.dy ); this.stringRender.restoreInternalState( ct.state ).render( 0, 0, textW, ct.color ); this.stringRender.resetTransform( null ); if ( clipUse ) { ctx.restore(); } } else { ctx.save().beginPath().rect( x1, y1, w, h ).clip(); this.stringRender.restoreInternalState( ct.state ).render( textX, textY, textW, ct.color ); ctx.restore(); } } return null; }; /** Удаляет вертикальные границы ячейки, если текст выходит за границы и соседние ячейки пусты */ WorksheetView.prototype._eraseCellRightBorder = function ( drawingCtx, colBeg, colEnd, row, offsetX, offsetY ) { if ( colBeg >= colEnd ) { return; } var nextCell = -1; var ctx = (drawingCtx) ? drawingCtx : this.drawingCtx; ctx.setFillStyle( this.settings.cells.defaultState.background ); for ( var col = colBeg; col < colEnd; ++col ) { var c = -1 !== nextCell ? nextCell : this._getCell( col, row ); var bg = null !== c ? c.getFill() : null; if ( bg !== null ) { continue; } nextCell = this._getCell( col + 1, row ); bg = null !== nextCell ? nextCell.getFill() : null; if ( bg !== null ) { continue; } ctx.fillRect( this.cols[col].left + this.cols[col].width - offsetX - this.width_1px, this.rows[row].top - offsetY, this.width_1px, this.rows[row].height - this.height_1px ); } }; /** Рисует рамки для ячеек */ WorksheetView.prototype._drawCellsBorders = function ( drawingCtx, range, offsetX, offsetY, mergedCells ) { //TODO: использовать стили линий при рисовании границ var t = this; var ctx = (drawingCtx) ? drawingCtx : this.drawingCtx; var c = this.cols; var r = this.rows; var objectMergedCells = {}; // Двумерный map вида строка-колонка {1: {1: range, 4: range}} var i, mergeCellInfo, startCol, endRow, endCol, col, row; for ( i in mergedCells ) { mergeCellInfo = mergedCells[i]; startCol = Math.max( range.c1, mergeCellInfo.c1 ); endRow = Math.min( mergeCellInfo.r2, range.r2, this.nRowsCount ); endCol = Math.min( mergeCellInfo.c2, range.c2, this.nColsCount ); for ( row = Math.max( range.r1, mergeCellInfo.r1 ); row <= endRow; ++row ) { if ( !objectMergedCells.hasOwnProperty( row ) ) { objectMergedCells[row] = {}; } for ( col = startCol; col <= endCol; ++col ) objectMergedCells[row][col] = mergeCellInfo; } } var bc = null, bw = -1, isNotFirst = false; // cached border color function drawBorder( type, border, x1, y1, x2, y2 ) { var isStroke = false, isNewColor = !g_oColorManager.isEqual( bc, border.c ), isNewWidth = bw !== border.w; if ( isNotFirst && (isNewColor || isNewWidth) ) { ctx.stroke(); isStroke = true; } if ( isNewColor ) { bc = border.c; ctx.setStrokeStyle( bc ); } if ( isNewWidth ) { bw = border.w; ctx.setLineWidth( border.w ); } if ( isStroke || false === isNotFirst ) { isNotFirst = true; ctx.beginPath(); } switch ( type ) { case c_oAscBorderType.Hor: ctx.lineHor( x1, y1, x2 ); break; case c_oAscBorderType.Ver: ctx.lineVer( x1, y1, y2 ); break; case c_oAscBorderType.Diag: ctx.lineDiag( x1, y1, x2, y2 ); break; } } function drawVerticalBorder( borderLeftObject, borderRightObject, x, y1, y2 ) { var border, borderLeft = borderLeftObject ? borderLeftObject.borders : null, borderRight = borderRightObject ? borderRightObject.borders : null; if ( borderLeft && borderLeft.r.w ) { border = borderLeft.r; } else if ( borderRight && borderRight.l.w ) { border = borderRight.l; } if ( !border || border.w < 1 ) { return; } // ToDo переделать рассчет var tbw = t._calcMaxBorderWidth( borderLeftObject && borderLeftObject.getTopBorder(), borderRightObject && borderRightObject.getTopBorder() ); // top border width var bbw = t._calcMaxBorderWidth( borderLeftObject && borderLeftObject.getBottomBorder(), borderRightObject && borderRightObject.getBottomBorder() ); // bottom border width var dy1 = tbw > border.w ? tbw - 1 : (tbw > 1 ? -1 : 0); var dy2 = bbw > border.w ? -2 : (bbw > 2 ? 1 : 0); drawBorder( c_oAscBorderType.Ver, border, x, y1 + (-1 + dy1) * t.height_1px, x, y2 + (1 + dy2) * t.height_1px ); } function drawHorizontalBorder( borderTopObject, borderBottomObject, x1, y, x2 ) { var border, borderTop = borderTopObject ? borderTopObject.borders : null, borderBottom = borderBottomObject ? borderBottomObject.borders : null; if ( borderTop && borderTop.b.w ) { border = borderTop.b; } else if ( borderBottom && borderBottom.t.w ) { border = borderBottom.t; } if ( border && border.w > 0 ) { // ToDo переделать рассчет var lbw = t._calcMaxBorderWidth( borderTopObject && borderTopObject.getLeftBorder(), borderBottomObject && borderBottomObject.getLeftBorder() ); var rbw = t._calcMaxBorderWidth( borderTopObject && borderTopObject.getRightBorder(), borderTopObject && borderTopObject.getRightBorder() ); var dx1 = border.w > lbw ? (lbw > 1 ? -1 : 0) : (lbw > 2 ? 2 : 1); var dx2 = border.w > rbw ? (rbw > 2 ? 1 : 0) : (rbw > 1 ? -2 : -1); drawBorder( c_oAscBorderType.Hor, border, x1 + (-1 + dx1) * t.width_1px, y, x2 + (1 + dx2) * t.width_1px, y ); } } var arrPrevRow = [], arrCurrRow = [], arrNextRow = []; var objMCPrevRow = null, objMCRow = null, objMCNextRow = null; var bCur, bPrev, bNext, bTopCur, bTopPrev, bTopNext, bBotCur, bBotPrev, bBotNext; bCur = bPrev = bNext = bTopCur = bTopNext = bBotCur = bBotNext = null; row = range.r1 - 1; var prevCol = range.c1 - 1; // Определим первую колонку (т.к. могут быть скрытые колонки) while ( 0 <= prevCol && c[prevCol].width < t.width_1px ) --prevCol; // Сначала пройдемся по верхней строке (над отрисовываемым диапазоном) while ( 0 <= row ) { if ( r[row].height >= t.height_1px ) { objMCPrevRow = objectMergedCells[row]; for ( col = prevCol; col <= range.c2 && col < t.nColsCount; ++col ) { if ( 0 > col || c[col].width < t.width_1px ) { continue; } arrPrevRow[col] = new CellBorderObject( t._getVisibleCell( col, row ).getBorder(), objMCPrevRow ? objMCPrevRow[col] : null, col, row ); } break; } --row; } var mc = null, nextRow, isFirstRow = true; var isPrevColExist = (0 <= prevCol); for ( row = range.r1; row <= range.r2 && row < t.nRowsCount; row = nextRow ) { nextRow = row + 1; if ( r[row].height < t.height_1px ) { continue; } // Нужно отсеять пустые снизу for ( ; nextRow <= range.r2 && nextRow < t.nRowsCount; ++nextRow ) if ( r[nextRow].height >= t.height_1px ) { break; } var isFirstRowTmp = isFirstRow, isLastRow = nextRow > range.r2 || nextRow >= t.nRowsCount; isFirstRow = false; // Это уже не первая строка (определяем не по совпадению с range.r1, а по видимости) objMCRow = isFirstRowTmp ? objectMergedCells[row] : objMCNextRow; objMCNextRow = objectMergedCells[nextRow]; var rowCache = t._fetchRowCache( row ); var y1 = r[row].top - offsetY; var y2 = y1 + r[row].height - t.height_1px; var nextCol, isFirstCol = true; for ( col = range.c1; col <= range.c2 && col < t.nColsCount; col = nextCol ) { nextCol = col + 1; if ( c[col].width < t.width_1px ) { continue; } // Нужно отсеять пустые справа for ( ; nextCol <= range.c2 && nextCol < t.nColsCount; ++nextCol ) if ( c[nextCol].width >= t.width_1px ) { break; } var isFirstColTmp = isFirstCol, isLastCol = nextCol > range.c2 || nextCol >= t.nColsCount; isFirstCol = false; // Это уже не первая колонка (определяем не по совпадению с range.c1, а по видимости) mc = objMCRow ? objMCRow[col] : null; var x1 = c[col].left - offsetX; var x2 = x1 + c[col].width - this.width_1px; if ( row === t.nRowsCount ) { bBotPrev = bBotCur = bBotNext = null; } else { if ( isFirstColTmp ) { bBotPrev = arrNextRow[prevCol] = new CellBorderObject( isPrevColExist ? t._getVisibleCell( prevCol, nextRow ).getBorder() : null, objMCNextRow ? objMCNextRow[prevCol] : null, prevCol, nextRow ); bBotCur = arrNextRow[col] = new CellBorderObject( t._getVisibleCell( col, nextRow ).getBorder(), objMCNextRow ? objMCNextRow[col] : null, col, nextRow ); } else { bBotPrev = bBotCur; bBotCur = bBotNext; } } if ( isFirstColTmp ) { bPrev = arrCurrRow[prevCol] = new CellBorderObject( isPrevColExist ? t._getVisibleCell( prevCol, row ).getBorder() : null, objMCRow ? objMCRow[prevCol] : null, prevCol, row ); bCur = arrCurrRow[col] = new CellBorderObject( t._getVisibleCell( col, row ).getBorder(), mc, col, row ); bTopPrev = arrPrevRow[prevCol]; bTopCur = arrPrevRow[col]; } else { bPrev = bCur; bCur = bNext; bTopPrev = bTopCur; bTopCur = bTopNext; } if ( col === t.nColsCount ) { bNext = null; bTopNext = null; } else { bNext = arrCurrRow[nextCol] = new CellBorderObject( t._getVisibleCell( nextCol, row ).getBorder(), objMCRow ? objMCRow[nextCol] : null, nextCol, row ); bTopNext = arrPrevRow[nextCol]; if ( row === t.nRowsCount ) { bBotNext = null; } else { bBotNext = arrNextRow[nextCol] = new CellBorderObject( t._getVisibleCell( nextCol, nextRow ).getBorder(), objMCNextRow ? objMCNextRow[nextCol] : null, nextCol, nextRow ); } } if ( mc && row !== mc.r1 && row !== mc.r2 && col !== mc.c1 && col !== mc.c2 ) { continue; } // draw diagonal borders if ( (bCur.borders.dd || bCur.borders.du) && (!mc || (row === mc.r1 && col === mc.c1)) ) { var x2Diagonal = x2; var y2Diagonal = y2; if ( mc ) { // Merge cells x2Diagonal = c[mc.c2].left + c[mc.c2].width - offsetX - t.width_1px; y2Diagonal = r[mc.r2].top + r[mc.r2].height - offsetY - t.height_1px; } // ToDo Clip diagonal borders /*ctx.save() .beginPath() .rect(x1 + this.width_1px * (lb.w < 1 ? -1 : (lb.w < 3 ? 0 : +1)), y1 + this.width_1px * (tb.w < 1 ? -1 : (tb.w < 3 ? 0 : +1)), c[col].width + this.width_1px * ( -1 + (lb.w < 1 ? +1 : (lb.w < 3 ? 0 : -1)) + (rb.w < 1 ? +1 : (rb.w < 2 ? 0 : -1)) ), r[row].height + this.height_1px * ( -1 + (tb.w < 1 ? +1 : (tb.w < 3 ? 0 : -1)) + (bb.w < 1 ? +1 : (bb.w < 2 ? 0 : -1)) )) .clip(); */ if ( bCur.borders.dd ) { // draw diagonal line l,t - r,b drawBorder( c_oAscBorderType.Diag, bCur.borders.d, x1 - t.width_1px, y1 - t.height_1px, x2Diagonal, y2Diagonal ); } if ( bCur.borders.du ) { // draw diagonal line l,b - r,t drawBorder( c_oAscBorderType.Diag, bCur.borders.d, x1 - t.width_1px, y2Diagonal, x2Diagonal, y1 - t.height_1px ); } // ToDo Clip diagonal borders //ctx.restore(); // canvas context has just been restored, so destroy border color cache //bc = undefined; } // draw left border if ( isFirstColTmp && !t._isLeftBorderErased( col, rowCache ) ) { drawVerticalBorder( bPrev, bCur, x1 - t.width_1px, y1, y2 ); // Если мы в печати и печатаем первый столбец, то нужно напечатать бордеры // if (lb.w >= 1 && drawingCtx && 0 === col) { // Иначе они будут не такой ширины // ToDo посмотреть что с этим ? в печати будет обрезка // drawVerticalBorder(lb, tb, tbPrev, bb, bbPrev, x1, y1, y2); // } } // draw right border if ( (!mc || col === mc.c2) && !t._isRightBorderErased( col, rowCache ) ) { drawVerticalBorder( bCur, bNext, x2, y1, y2 ); } // draw top border if ( isFirstRowTmp ) { drawHorizontalBorder( bTopCur, bCur, x1, y1 - t.height_1px, x2 ); // Если мы в печати и печатаем первую строку, то нужно напечатать бордеры // if (tb.w > 0 && drawingCtx && 0 === row) { // ToDo посмотреть что с этим ? в печати будет обрезка // drawHorizontalBorder.call(this, tb, lb, lbPrev, rb, rbPrev, x1, y1, x2); // } } if ( !mc || row === mc.r2 ) { // draw bottom border drawHorizontalBorder( bCur, bBotCur, x1, y2, x2 ); } } arrPrevRow = arrCurrRow; arrCurrRow = arrNextRow; arrNextRow = []; } if ( isNotFirst ) { ctx.stroke(); } }; /** Рисует закрепленные области областей */ WorksheetView.prototype._drawFrozenPane = function ( noCells ) { if ( this.topLeftFrozenCell ) { var row = this.topLeftFrozenCell.getRow0(); var col = this.topLeftFrozenCell.getCol0(); var tmpRange, offsetX, offsetY; if ( 0 < row && 0 < col ) { offsetX = this.cols[0].left - this.cellsLeft; offsetY = this.rows[0].top - this.cellsTop; tmpRange = new asc_Range( 0, 0, col - 1, row - 1 ); if ( !noCells ) { this._drawGrid( /*drawingCtx*/ undefined, tmpRange, offsetX, offsetY ); this._drawCellsAndBorders( /*drawingCtx*/undefined, tmpRange, offsetX, offsetY ); } } if ( 0 < row ) { row -= 1; offsetX = undefined; offsetY = this.rows[0].top - this.cellsTop; tmpRange = new asc_Range( this.visibleRange.c1, 0, this.visibleRange.c2, row ); this._drawRowHeaders( /*drawingCtx*/ undefined, 0, row, kHeaderDefault, offsetX, offsetY ); if ( !noCells ) { this._drawGrid( /*drawingCtx*/ undefined, tmpRange, offsetX, offsetY ); this._drawCellsAndBorders( /*drawingCtx*/undefined, tmpRange, offsetX, offsetY ); } } if ( 0 < col ) { col -= 1; offsetX = this.cols[0].left - this.cellsLeft; offsetY = undefined; tmpRange = new asc_Range( 0, this.visibleRange.r1, col, this.visibleRange.r2 ); this._drawColumnHeaders( /*drawingCtx*/ undefined, 0, col, kHeaderDefault, offsetX, offsetY ); if ( !noCells ) { this._drawGrid( /*drawingCtx*/ undefined, tmpRange, offsetX, offsetY ); this._drawCellsAndBorders( /*drawingCtx*/undefined, tmpRange, offsetX, offsetY ); } } } }; /** Рисует закрепление областей */ WorksheetView.prototype._drawFrozenPaneLines = function ( drawingCtx ) { // Возможно стоит отрисовывать на overlay, а не на основной канве var ctx = drawingCtx ? drawingCtx : this.drawingCtx; var lockInfo = this.collaborativeEditing.getLockInfo( c_oAscLockTypeElem.Object, null, this.model.getId(), c_oAscLockNameFrozenPane ); var isLocked = this.collaborativeEditing.getLockIntersection( lockInfo, c_oAscLockTypes.kLockTypeOther, false ); var color = isLocked ? c_oAscCoAuthoringOtherBorderColor : this.settings.frozenColor; ctx.setLineWidth( 1 ).setStrokeStyle( color ).beginPath(); var fHorLine, fVerLine; if ( isLocked ) { fHorLine = ctx.dashLineCleverHor; fVerLine = ctx.dashLineCleverVer; } else { fHorLine = ctx.lineHorPrevPx; fVerLine = ctx.lineVerPrevPx; } if ( this.topLeftFrozenCell ) { var row = this.topLeftFrozenCell.getRow0(); var col = this.topLeftFrozenCell.getCol0(); if ( 0 < row ) { fHorLine.apply( ctx, [0, this.rows[row].top, ctx.getWidth()] ); } else { fHorLine.apply( ctx, [0, this.headersHeight, this.headersWidth] ); } if ( 0 < col ) { fVerLine.apply( ctx, [this.cols[col].left, 0, ctx.getHeight()] ); } else { fVerLine.apply( ctx, [this.headersWidth, 0, this.headersHeight] ); } } else if ( this.model.sheetViews[0].asc_getShowRowColHeaders() ) { fHorLine.apply( ctx, [0, this.headersHeight, this.headersWidth] ); fVerLine.apply( ctx, [this.headersWidth, 0, this.headersHeight] ); } ctx.stroke(); }; WorksheetView.prototype.drawFrozenGuides = function ( x, y, target ) { var data, offsetFrozen; var ctx = this.overlayCtx; ctx.clear(); this._drawSelection(); switch ( target ) { case c_oTargetType.FrozenAnchorV: x *= asc_getcvt( 0/*px*/, 1/*pt*/, this._getPPIX() ); data = this._findColUnderCursor( x, true, true ); if ( data ) { data.col += 1; if ( 0 <= data.col && data.col < this.cols.length ) { var h = ctx.getHeight(); var offsetX = this.cols[this.visibleRange.c1].left - this.cellsLeft; offsetFrozen = this.getFrozenPaneOffset( false, true ); offsetX -= offsetFrozen.offsetX; ctx.setFillPattern( this.settings.ptrnLineDotted1 ) .fillRect( this.cols[data.col].left - offsetX - this.width_1px, 0, this.width_1px, h ); } } break; case c_oTargetType.FrozenAnchorH: y *= asc_getcvt( 0/*px*/, 1/*pt*/, this._getPPIY() ); data = this._findRowUnderCursor( y, true, true ); if ( data ) { data.row += 1; if ( 0 <= data.row && data.row < this.rows.length ) { var w = ctx.getWidth(); var offsetY = this.rows[this.visibleRange.r1].top - this.cellsTop; offsetFrozen = this.getFrozenPaneOffset( true, false ); offsetY -= offsetFrozen.offsetY; ctx.setFillPattern( this.settings.ptrnLineDotted1 ) .fillRect( 0, this.rows[data.row].top - offsetY - this.height_1px, w, this.height_1px ); } } break; } }; WorksheetView.prototype._isFrozenAnchor = function ( x, y ) { var result = {result: false, cursor: "move", name: ""}; if ( false === this.model.sheetViews[0].asc_getShowRowColHeaders() ) { return result; } var _this = this; var frozenCell = this.topLeftFrozenCell ? this.topLeftFrozenCell : new CellAddress( 0, 0, 0 ); x *= asc_getcvt( 0/*px*/, 1/*pt*/, this._getPPIX() ); y *= asc_getcvt( 0/*px*/, 1/*pt*/, this._getPPIY() ); function isPointInAnchor( x, y, rectX, rectY, rectW, rectH ) { var delta = 2 * asc_getcvt( 0/*px*/, 1/*pt*/, _this._getPPIX() ); return (x >= rectX - delta) && (x <= rectX + rectW + delta) && (y >= rectY - delta) && (y <= rectY + rectH + delta); } // vertical var _x = this.getCellLeft( frozenCell.getCol0(), 1 ) - 0.5; var _y = _this.headersTop; var w = 0; var h = _this.headersHeight; if ( isPointInAnchor( x, y, _x, _y, w, h ) ) { result.result = true; result.name = c_oTargetType.FrozenAnchorV; } // horizontal _x = _this.headersLeft; _y = this.getCellTop( frozenCell.getRow0(), 1 ) - 0.5; w = _this.headersWidth - 0.5; h = 0; if ( isPointInAnchor( x, y, _x, _y, w, h ) ) { result.result = true; result.name = c_oTargetType.FrozenAnchorH; } return result; }; WorksheetView.prototype.applyFrozenAnchor = function ( x, y, target ) { var t = this; var onChangeFrozenCallback = function ( isSuccess ) { if ( false === isSuccess ) { t.overlayCtx.clear(); t._drawSelection(); return; } var lastCol = 0, lastRow = 0, data; if ( t.topLeftFrozenCell ) { lastCol = t.topLeftFrozenCell.getCol0(); lastRow = t.topLeftFrozenCell.getRow0(); } switch ( target ) { case c_oTargetType.FrozenAnchorV: x *= asc_getcvt( 0/*px*/, 1/*pt*/, t._getPPIX() ); data = t._findColUnderCursor( x, true, true ); if ( data ) { data.col += 1; if ( 0 <= data.col && data.col < t.cols.length ) { lastCol = data.col; } } break; case c_oTargetType.FrozenAnchorH: y *= asc_getcvt( 0/*px*/, 1/*pt*/, t._getPPIY() ); data = t._findRowUnderCursor( y, true, true ); if ( data ) { data.row += 1; if ( 0 <= data.row && data.row < t.rows.length ) { lastRow = data.row; } } break; } t._updateFreezePane( lastCol, lastRow ); }; this._isLockedFrozenPane( onChangeFrozenCallback ); }; /** Для api закрепленных областей */ WorksheetView.prototype.freezePane = function () { var t = this; var ar = this.activeRange.clone(); var onChangeFreezePane = function ( isSuccess ) { if ( false === isSuccess ) { return; } var col, row, mc; if ( null !== t.topLeftFrozenCell ) { col = row = 0; } else { col = ar.startCol; row = ar.startRow; if ( 0 !== row || 0 !== col ) { mc = t.model.getMergedByCell( row, col ); if ( mc ) { col = mc.c1; row = mc.r1; } } if ( 0 === col && 0 === row ) { col = ((t.visibleRange.c2 - t.visibleRange.c1) / 2) >> 0; row = ((t.visibleRange.r2 - t.visibleRange.r1) / 2) >> 0; } } t._updateFreezePane( col, row ); }; return this._isLockedFrozenPane( onChangeFreezePane ); }; WorksheetView.prototype._updateFreezePane = function ( col, row, lockDraw ) { var lastCol = 0, lastRow = 0; if ( this.topLeftFrozenCell ) { lastCol = this.topLeftFrozenCell.getCol0(); lastRow = this.topLeftFrozenCell.getRow0(); } History.Create_NewPoint(); var oData = new UndoRedoData_FromTo( new UndoRedoData_BBox( new asc_Range( lastCol, lastRow, lastCol, lastRow ) ), new UndoRedoData_BBox( new asc_Range( col, row, col, row ) ), null ); History.Add( g_oUndoRedoWorksheet, historyitem_Worksheet_ChangeFrozenCell, this.model.getId(), null, oData ); var isUpdate = false; if ( 0 === col && 0 === row ) { // Очистка if ( null !== this.topLeftFrozenCell ) { isUpdate = true; } this.topLeftFrozenCell = this.model.sheetViews[0].pane = null; } else { // Создание if ( null === this.topLeftFrozenCell ) { isUpdate = true; } var pane = this.model.sheetViews[0].pane = new asc.asc_CPane(); this.topLeftFrozenCell = pane.topLeftFrozenCell = new CellAddress( row, col, 0 ); } this.visibleRange.c1 = col; this.visibleRange.r1 = row; if ( col >= this.nColsCount ) { this.expandColsOnScroll( false, true, 0 ); } // Передаем 0, чтобы увеличить размеры if ( row >= this.nRowsCount ) { this.expandRowsOnScroll( false, true, 0 ); } // Передаем 0, чтобы увеличить размеры this.visibleRange.r2 = 0; this._calcVisibleRows(); this.visibleRange.c2 = 0; this._calcVisibleColumns(); this.handlers.trigger( "reinitializeScroll" ); if ( this.objectRender && this.objectRender.drawingArea ) { this.objectRender.drawingArea.init(); } if ( !lockDraw ) { this.draw(); } // Эвент на обновление if ( isUpdate && !this.model.workbook.bUndoChanges && !this.model.workbook.bRedoChanges ) { this.handlers.trigger( "updateSheetViewSettings" ); } }; /** */ WorksheetView.prototype._drawSelectionElement = function ( visibleRange, offsetX, offsetY, args ) { var range = args[0], isDashLine = args[1], lineWidth = args[2], strokeColor = args[3], fillColor = args[4], isAllRange = args[5]; var ctx = this.overlayCtx, c = this.cols, r = this.rows; var oIntersection = range.intersectionSimple( visibleRange ); if ( !oIntersection ) { return; } var fHorLine, fVerLine; if ( isDashLine ) { fHorLine = ctx.dashLineCleverHor; fVerLine = ctx.dashLineCleverVer; } else { fHorLine = ctx.lineHorPrevPx; fVerLine = ctx.lineVerPrevPx; } var firstCol = oIntersection.c1 === visibleRange.c1 && !isAllRange; var firstRow = oIntersection.r1 === visibleRange.r1 && !isAllRange; var drawLeftSide = oIntersection.c1 === range.c1; var drawRightSide = oIntersection.c2 === range.c2; var drawTopSide = oIntersection.r1 === range.r1; var drawBottomSide = oIntersection.r2 === range.r2; var x1 = c[oIntersection.c1].left - offsetX; var x2 = c[oIntersection.c2].left + c[oIntersection.c2].width - offsetX; var y1 = r[oIntersection.r1].top - offsetY; var y2 = r[oIntersection.r2].top + r[oIntersection.r2].height - offsetY; ctx.setLineWidth( lineWidth ).setStrokeStyle( strokeColor ); if ( fillColor ) { ctx.setFillStyle( fillColor ); } ctx.beginPath(); if ( drawTopSide && !firstRow ) { fHorLine.apply( ctx, [x1, y1, x2] ); } if ( drawBottomSide ) { fHorLine.apply( ctx, [x1, y2, x2] ); } if ( drawLeftSide && !firstCol ) { fVerLine.apply( ctx, [x1, y1, y2] ); } if ( drawRightSide ) { fVerLine.apply( ctx, [x2, y1, y2] ); } // Отрисовка квадратов для move/resize if ( fillColor ) { if ( drawLeftSide && drawTopSide ) { ctx.fillRect( x1 - this.width_1px, y1 - this.height_1px, this.width_4px, this.height_4px ); } if ( drawRightSide && drawTopSide ) { ctx.fillRect( x2 - this.width_4px, y1 - this.height_1px, this.width_4px, this.height_4px ); } if ( drawRightSide && drawBottomSide ) { ctx.fillRect( x2 - this.width_4px, y2 - this.height_4px, this.width_4px, this.height_4px ); } if ( drawLeftSide && drawBottomSide ) { ctx.fillRect( x1 - this.width_1px, y2 - this.height_4px, this.width_4px, this.height_4px ); } } ctx.closePath().stroke(); }; /**Отрисовывает диапазон с заданными параметрами*/ WorksheetView.prototype._drawElements = function ( thisArg, drawFunction ) { var cFrozen = 0, rFrozen = 0, args = Array.prototype.slice.call( arguments, 2 ), c = this.cols, r = this.rows, offsetX = c[this.visibleRange.c1].left - this.cellsLeft, offsetY = r[this.visibleRange.r1].top - this.cellsTop; if ( this.topLeftFrozenCell ) { cFrozen = this.topLeftFrozenCell.getCol0(); rFrozen = this.topLeftFrozenCell.getRow0(); offsetX -= this.cols[cFrozen].left - this.cols[0].left; offsetY -= this.rows[rFrozen].top - this.rows[0].top; var oFrozenRange; cFrozen -= 1; rFrozen -= 1; if ( 0 <= cFrozen && 0 <= rFrozen ) { oFrozenRange = new asc_Range( 0, 0, cFrozen, rFrozen ); drawFunction.call( thisArg, oFrozenRange, c[0].left - this.cellsLeft, r[0].top - this.cellsTop, args ); } if ( 0 <= cFrozen ) { oFrozenRange = new asc_Range( 0, this.visibleRange.r1, cFrozen, this.visibleRange.r2 ); drawFunction.call( thisArg, oFrozenRange, c[0].left - this.cellsLeft, offsetY, args ); } if ( 0 <= rFrozen ) { oFrozenRange = new asc_Range( this.visibleRange.c1, 0, this.visibleRange.c2, rFrozen ); drawFunction.call( thisArg, oFrozenRange, offsetX, r[0].top - this.cellsTop, args ); } } // Можно вместо call попользовать apply, но тогда нужно каждый раз соединять массив аргументов и 3 объекта drawFunction.call( thisArg, this.visibleRange, offsetX, offsetY, args ); }; /** * Рисует выделение вокруг ячеек * @param {Asc.Range} [range] */ WorksheetView.prototype._drawSelection = function ( range ) { if ( window['IS_NATIVE_EDITOR'] ) { return; } if ( !this.isSelectionDialogMode ) { this._drawCollaborativeElements( /*bIsDrawObjects*/true ); } if ((this.isSelectionDialogMode || this.isFormulaEditMode) && !this.handlers.trigger('isActive')) { if (this.isSelectionDialogMode) { this._drawSelectRange(this.activeRange.clone(true)); } else if (this.isFormulaEditMode) { this._drawFormulaRanges(this.arrActiveFormulaRanges); } } else { this._drawSelectionRange(range); } }; WorksheetView.prototype._drawSelectionRange = function ( range, isFrozen ) { isFrozen = !!isFrozen; if ( asc["editor"].isStartAddShape || this.objectRender.selectedGraphicObjectsExists() ) { if ( this.isChartAreaEditMode ) { this._drawFormulaRanges( this.arrActiveChartsRanges ); } return; } if ( c_oAscSelectionType.RangeMax === this.activeRange.type ) { this.activeRange.c2 = this.cols.length - 1; this.activeRange.r2 = this.rows.length - 1; } else if ( c_oAscSelectionType.RangeCol === this.activeRange.type ) { this.activeRange.r2 = this.rows.length - 1; } else if ( c_oAscSelectionType.RangeRow === this.activeRange.type ) { this.activeRange.c2 = this.cols.length - 1; } var diffWidth = 0, diffHeight = 0; if ( this.topLeftFrozenCell ) { var cFrozen = this.topLeftFrozenCell.getCol0(); var rFrozen = this.topLeftFrozenCell.getRow0(); diffWidth = this.cols[cFrozen].left - this.cols[0].left; diffHeight = this.rows[rFrozen].top - this.rows[0].top; if ( !isFrozen ) { var oFrozenRange; cFrozen -= 1; rFrozen -= 1; if ( 0 <= cFrozen && 0 <= rFrozen ) { oFrozenRange = new asc_Range( 0, 0, cFrozen, rFrozen ); this._drawSelectionRange( oFrozenRange, true ); } if ( 0 <= cFrozen ) { oFrozenRange = new asc_Range( 0, this.visibleRange.r1, cFrozen, this.visibleRange.r2 ); this._drawSelectionRange( oFrozenRange, true ); } if ( 0 <= rFrozen ) { oFrozenRange = new asc_Range( this.visibleRange.c1, 0, this.visibleRange.c2, rFrozen ); this._drawSelectionRange( oFrozenRange, true ); } } } var tmpRange = range; if ( !this.isSelectionDialogMode ) { range = this.activeRange.intersection( range !== undefined ? range : this.visibleRange ); } else { range = this.copyActiveRange.intersection( range !== undefined ? range : this.visibleRange ); } // Copy fill Handle var aFH = null; // Вхождение range var aFHIntersection = null; if ( this.activeFillHandle !== null ) { // Мы в режиме автозаполнения aFH = this.activeFillHandle.clone( true ); aFHIntersection = this.activeFillHandle.intersection( tmpRange !== undefined ? tmpRange : this.visibleRange ); } if ( !range && !aFHIntersection && !this.isFormulaEditMode && !this.activeMoveRange && !this.isChartAreaEditMode ) { if ( !isFrozen ) { this._drawActiveHeaders(); if ( this.isSelectionDialogMode ) { this._drawSelectRange( this.activeRange.clone( true ) ); } } return; } var ctx = this.overlayCtx; var opt = this.settings; var offsetX, offsetY; if ( isFrozen ) { if ( tmpRange.c1 !== this.visibleRange.c1 ) { diffWidth = 0; } if ( tmpRange.r1 !== this.visibleRange.r1 ) { diffHeight = 0; } offsetX = this.cols[tmpRange.c1].left - this.cellsLeft - diffWidth; offsetY = this.rows[tmpRange.r1].top - this.cellsTop - diffHeight; } else { offsetX = this.cols[this.visibleRange.c1].left - this.cellsLeft - diffWidth; offsetY = this.rows[this.visibleRange.r1].top - this.cellsTop - diffHeight; } var arn = (!this.isSelectionDialogMode) ? this.activeRange.clone( true ) : this.copyActiveRange.clone( true ); var x1 = (range) ? (this.cols[range.c1].left - offsetX - this.width_1px) : 0; var x2 = (range) ? (this.cols[range.c2].left + this.cols[range.c2].width - offsetX - this.width_1px) : 0; var y1 = (range) ? (this.rows[range.r1].top - offsetY) : 0; var y2 = (range) ? (this.rows[range.r2].top + this.rows[range.r2].height - offsetY - this.height_1px) : 0; var drawLeftSide = (range) ? (range.c1 === arn.c1) : false; var drawRightSide = (range) ? (range.c2 === arn.c2 && y2 >= y1) : false; var drawTopSide = (range) ? (range.r1 === arn.r1) : false; var drawBottomSide = (range) ? (range.r2 === arn.r2 && y2 >= y1) : false; var l, t, r, b, cr; // Размеры "квадрата" автозаполнения var fillHandleWidth = 2 * this.width_2px + this.width_1px; var fillHandleHeight = 2 * this.height_2px + this.height_1px; // Координаты выделения для автозаполнения var xFH1 = 0; var xFH2 = 0; var yFH1 = 0; var yFH2 = 0; // Рисуем ли мы стороны автозаполнения var drawLeftFillHandle; var drawRightFillHandle; var drawTopFillHandle; var drawBottomFillHandle; // set clipping rect to cells area ctx.save() .beginPath() .rect( this.cellsLeft, this.cellsTop, ctx.getWidth() - this.cellsLeft, ctx.getHeight() - this.cellsTop ) .clip(); // draw frame around cells range l = drawLeftSide ? -this.width_1px : 0; r = drawRightSide ? this.width_1px : 0; t = drawTopSide ? -this.height_1px : 0; b = drawBottomSide ? this.height_2px : 0; ctx.setStrokeStyle( opt.activeCellBorderColor ) .setLineWidth( 3 ) .beginPath(); if ( aFHIntersection ) { // Считаем координаты автозаполнения xFH1 = this.cols[aFHIntersection.c1].left - offsetX - this.width_1px; xFH2 = this.cols[aFHIntersection.c2].left + this.cols[aFHIntersection.c2].width - offsetX - this.width_1px; yFH1 = this.rows[aFHIntersection.r1].top - offsetY; yFH2 = this.rows[aFHIntersection.r2].top + this.rows[aFHIntersection.r2].height - offsetY - this.height_1px; drawLeftFillHandle = aFHIntersection.c1 === aFH.c1; drawRightFillHandle = aFHIntersection.c2 === aFH.c2; drawTopFillHandle = aFHIntersection.r1 === aFH.r1; drawBottomFillHandle = aFHIntersection.r2 === aFH.r2; // Если мы не в нулевом состоянии, то рисуем обводку автозаполнения (толстой линией) if ( aFHIntersection.c1 !== aFHIntersection.c2 || aFHIntersection.r1 !== aFHIntersection.r2 || 2 !== this.fillHandleArea ) { if ( drawTopFillHandle ) { ctx.lineHor( xFH1 + l, yFH1 - this.height_1px, xFH2 + this.width_1px + r ); } if ( drawBottomFillHandle ) { ctx.lineHor( xFH1 + l, yFH2, xFH2 + this.width_1px + r ); } if ( drawLeftFillHandle ) { ctx.lineVer( xFH1, yFH1 + t, yFH2 + b ); } if ( drawRightFillHandle ) { ctx.lineVer( xFH2, yFH1 + t, yFH2 + b ); } } // Для некоторых вариантов областей нужно дорисовывать обводку для выделенной области switch ( this.fillHandleArea ) { case 1: switch ( this.fillHandleDirection ) { case 0: // Горизонтальный if ( drawLeftSide ) { ctx.lineVer( x1, y1 + t, y2 + b ); } break; case 1: // Вертикальный if ( drawTopSide ) { ctx.lineHor( x1 + l, y1 - this.height_1px, x2 + this.width_1px + r ); } break; } break; case 2: // Для внутренней области нужны все обводки if ( drawTopSide ) { ctx.lineHor( x1 + l, y1 - this.height_1px, x2 + this.width_1px + r ); } if ( drawBottomSide ) { ctx.lineHor( x1 + l, y2, x2 + this.width_1px + r ); } if ( drawLeftSide ) { ctx.lineVer( x1, y1 + t, y2 + b ); } if ( drawRightSide ) { ctx.lineVer( x2, y1 + t, y2 + b ); } break; case 3: switch ( this.fillHandleDirection ) { case 0: // Горизонтальный if ( range && aFH.c2 !== range.c2 ) { if ( drawRightSide ) { ctx.lineVer( x2, y1 + t, y2 + b ); } } break; case 1: // Вертикальный if ( range && aFH.r2 !== range.r2 ) { if ( drawBottomSide ) { ctx.lineHor( x1 + l, y2, x2 + this.width_1px + r ); } } break; } break; } ctx.stroke(); } else { // Автозаполнения нет, просто рисуем обводку if ( drawTopSide ) { ctx.lineHor( x1 + l, y1 - this.height_1px, x2 + this.width_1px + r ); } if ( drawBottomSide ) { if ( isFrozen && !drawRightSide ) { fillHandleWidth = 0; } ctx.lineHor( x1 + l, y2, x2 + this.width_1px + r - fillHandleWidth ); } if ( drawLeftSide ) { ctx.lineVer( x1, y1 + t, y2 + b ); } if ( drawRightSide ) { if ( isFrozen && !drawBottomSide ) { fillHandleHeight = 0; } ctx.lineVer( x2, y1 + t, y2 + b - fillHandleHeight ); } } ctx.stroke(); // draw cells overlay if ( range ) { if ( y2 >= y1 ) { var lRect = x1 + (drawLeftSide ? this.width_3px : this.width_1px), rRect = x2 - (drawRightSide ? this.width_2px : 0), tRect = y1 + (drawTopSide ? this.height_2px : 0), bRect = y2 - (drawBottomSide ? this.width_2px : 0); ctx.setFillStyle( opt.activeCellBackground ) .fillRect( lRect, tRect, rRect - lRect, bRect - tRect ); var lRect2 = x1 + (drawLeftSide ? this.width_2px : this.width_1px), rRect2 = x2 - (drawRightSide ? this.width_2px : 0), tRect2 = y1 + (drawTopSide ? this.height_1px : 0), bRect2 = y2 - (drawBottomSide ? this.width_2px : 0); ctx.setStrokeStyle( opt.activeCellBorderColor2 ).setLineWidth( 1 ).beginPath() .strokeRect( lRect2, tRect2, rRect2 - lRect2, bRect2 - tRect2 ); var firstCell = (!this.isSelectionDialogMode) ? this.activeRange : this.copyActiveRange; cr = this.model.getMergedByCell( firstCell.startRow, firstCell.startCol ); // Проверяем скрыта ли ячейка выделения // Получаем активную ячейку в выделении cr = range.intersection( null !== cr ? cr : new asc_Range( firstCell.startCol, firstCell.startRow, firstCell.startCol, firstCell.startRow ) ); if ( cr !== null ) { ctx.save().beginPath().rect( lRect, tRect, rRect - lRect, bRect - tRect ).clip(); var _l = this.cols[cr.c1].left - offsetX - this.width_1px, _r = this.cols[cr.c2].left + this.cols[cr.c2].width - offsetX, _t = this.rows[cr.r1].top - offsetY - this.height_1px, _b = this.rows[cr.r2].top + this.rows[cr.r2].height - offsetY; ctx.clearRect( _l, _t, _r - _l, _b - _t ).restore(); } if ( !(isFrozen && (!drawRightSide || !drawBottomSide)) ) { // Рисуем "квадрат" для автозаполнения (располагается "квадрат" в правом нижнем углу последней ячейки выделения) cr = range.intersection( new asc_Range( range.c2, range.r2, range.c2, range.r2 ) ); if ( cr !== null ) { this.fillHandleL = this.cols[cr.c1].left - offsetX + this.cols[cr.c1].width - this.width_1px - this.width_2px; this.fillHandleR = this.fillHandleL + fillHandleWidth; this.fillHandleT = this.rows[cr.r1].top - offsetY + this.rows[cr.r1].height - this.height_1px - this.height_2px; this.fillHandleB = this.fillHandleT + fillHandleHeight; ctx.setFillStyle( opt.activeCellBorderColor ).fillRect( this.fillHandleL, this.fillHandleT, this.fillHandleR - this.fillHandleL, this.fillHandleB - this.fillHandleT ); ctx.setStrokeStyle( opt.activeCellBorderColor2 ).setLineWidth( 1 ).beginPath(); ctx.lineHorPrevPx( this.fillHandleL, this.fillHandleT, this.fillHandleR ); ctx.lineVerPrevPx( this.fillHandleL, this.fillHandleT, this.fillHandleB ); ctx.stroke(); } } } else { this.fillHandleL = this.fillHandleR = this.fillHandleT = this.fillHandleB = -1; } } // draw fill handle select if ( this.activeFillHandle !== null ) { if ( 2 === this.fillHandleArea && (aFH.c1 !== aFH.c2 || aFH.r1 !== aFH.r2) ) { // Для внутренней области мы должны "залить" еще и область автозаполнения var lFH = xFH1 + (drawLeftFillHandle ? this.width_3px : this.width_1px), rFH = xFH2 - (drawRightFillHandle ? this.width_2px : 0), tFH = yFH1 + (drawTopFillHandle ? this.height_2px : 0), bFH = yFH2 - (drawBottomFillHandle ? this.width_2px : 0); ctx.setFillStyle( opt.activeCellBackground ) .fillRect( lFH, tFH, rFH - lFH, bFH - tFH ); } ctx.setStrokeStyle( opt.fillHandleBorderColorSelect ).setLineWidth( 1 ).beginPath(); if ( aFH.c1 !== aFH.c2 || aFH.r1 !== aFH.r2 || 2 !== this.fillHandleArea ) { // Рисуем обводку для области автозаполнения, если мы выделили что-то if ( drawTopFillHandle ) { ctx.lineHor( xFH1 + l + this.width_1px, yFH1 - this.height_1px, xFH2 + r ); } if ( drawBottomFillHandle ) { ctx.lineHor( xFH1 + l + this.width_1px, yFH2, xFH2 + r ); } if ( drawLeftFillHandle ) { ctx.lineVer( xFH1, yFH1 + t + this.height_1px, yFH2 + b - this.height_1px ); } if ( drawRightFillHandle ) { ctx.lineVer( xFH2, yFH1 + t + this.height_1px, yFH2 + b - this.height_1px ); } } if ( 2 === this.fillHandleArea ) { // Если мы внутри, еще рисуем обводку для выделенной области if ( drawTopSide ) { ctx.lineHor( x1 + l + this.width_1px, y1 - this.height_1px, x2 + r - this.width_1px ); } if ( drawBottomSide ) { ctx.lineHor( x1 + l + this.width_1px, y2, x2 + r - this.width_1px ); } if ( drawLeftSide ) { ctx.lineVer( x1, y1 + t + this.height_1px, y2 + b - this.height_1px ); } if ( drawRightSide ) { ctx.lineVer( x2, y1 + t + this.height_1px, y2 + b - this.height_1px ); } } ctx.stroke(); } if ( !isFrozen && this.isFormulaEditMode ) { this._drawFormulaRanges( this.arrActiveFormulaRanges ); } if ( !isFrozen && this.isChartAreaEditMode ) { this._drawFormulaRanges( this.arrActiveChartsRanges ); } if ( !isFrozen && this.isSelectionDialogMode ) { this._drawSelectRange( this.activeRange.clone( true ) ); } if ( !isFrozen && this.stateFormatPainter ) { this._drawFormatPainterRange(); } if ( null !== this.activeMoveRange ) { ctx.setStrokeStyle( new CColor( 0, 0, 0 ) ) .setLineWidth( 1 ) .beginPath(); var aActiveMoveRangeIntersection = this.activeMoveRange.intersection( tmpRange !== undefined ? tmpRange : this.visibleRange ); if ( aActiveMoveRangeIntersection ) { var drawLeftSideMoveRange = aActiveMoveRangeIntersection.c1 === this.activeMoveRange.c1; var drawRightSideMoveRange = aActiveMoveRangeIntersection.c2 === this.activeMoveRange.c2; var drawTopSideMoveRange = aActiveMoveRangeIntersection.r1 === this.activeMoveRange.r1; var drawBottomSideMoveRange = aActiveMoveRangeIntersection.r2 === this.activeMoveRange.r2; var xMoveRange1 = this.cols[aActiveMoveRangeIntersection.c1].left - offsetX - this.width_1px; var xMoveRange2 = this.cols[aActiveMoveRangeIntersection.c2].left + this.cols[aActiveMoveRangeIntersection.c2].width - offsetX - this.width_1px; var yMoveRange1 = this.rows[aActiveMoveRangeIntersection.r1].top - offsetY; var yMoveRange2 = this.rows[aActiveMoveRangeIntersection.r2].top + this.rows[aActiveMoveRangeIntersection.r2].height - offsetY - this.height_1px; if ( drawTopSideMoveRange ) { ctx.lineHor( xMoveRange1, yMoveRange1 - this.height_1px, xMoveRange2 + this.width_1px ); } if ( drawBottomSideMoveRange ) { ctx.lineHor( xMoveRange1, yMoveRange2, xMoveRange2 + this.width_1px ); } if ( drawLeftSideMoveRange ) { ctx.lineVer( xMoveRange1, yMoveRange1, yMoveRange2 ); } if ( drawRightSideMoveRange ) { ctx.lineVer( xMoveRange2, yMoveRange1, yMoveRange2 ); } } ctx.stroke(); } // restore canvas' original clipping range ctx.restore(); if ( !isFrozen ) { this._drawActiveHeaders(); } }; WorksheetView.prototype._drawFormatPainterRange = function () { var lineWidth = 1, isDashLine = true, strokeColor = new CColor( 0, 0, 0 ); this._drawElements( this, this._drawSelectionElement, this.copyActiveRange, isDashLine, lineWidth, strokeColor ); }; WorksheetView.prototype._drawFormulaRanges = function ( arrRanges ) { var i, lineWidth = 1, isDashLine = false, length = c_oAscFormulaRangeBorderColor.length; var strokeColor, fillColor, colorIndex, uniqueColorIndex = 0, tmpColors = []; for ( i = 0; i < arrRanges.length; ++i ) { var oFormulaRange = arrRanges[i].clone( true ); colorIndex = asc.getUniqueRangeColor( arrRanges, i, tmpColors ); if ( null == colorIndex ) { colorIndex = uniqueColorIndex++; } tmpColors.push( colorIndex ); strokeColor = fillColor = c_oAscFormulaRangeBorderColor[colorIndex % length]; this._drawElements( this, this._drawSelectionElement, oFormulaRange, isDashLine, lineWidth, strokeColor, fillColor ); } }; WorksheetView.prototype._drawSelectRange = function ( oSelectRange ) { var lineWidth = 1, isDashLine = true, strokeColor = c_oAscCoAuthoringOtherBorderColor; this._drawElements( this, this._drawSelectionElement, oSelectRange, isDashLine, lineWidth, strokeColor ); }; WorksheetView.prototype._drawCollaborativeElements = function ( bIsDrawObjects ) { if ( this.collaborativeEditing.getCollaborativeEditing() ) { this._drawCollaborativeElementsMeOther( c_oAscLockTypes.kLockTypeMine, bIsDrawObjects ); this._drawCollaborativeElementsMeOther( c_oAscLockTypes.kLockTypeOther, bIsDrawObjects ); this._drawCollaborativeElementsAllLock(); } }; WorksheetView.prototype._drawCollaborativeElementsAllLock = function () { var currentSheetId = this.model.getId(); var nLockAllType = this.collaborativeEditing.isLockAllOther( currentSheetId ); if ( c_oAscMouseMoveLockedObjectType.None !== nLockAllType ) { var lineWidth = 1, isDashLine = true, isAllRange = true, strokeColor = (c_oAscMouseMoveLockedObjectType.TableProperties === nLockAllType) ? c_oAscCoAuthoringLockTablePropertiesBorderColor : c_oAscCoAuthoringOtherBorderColor, oAllRange = new asc_Range( 0, 0, gc_nMaxCol0, gc_nMaxRow0 ); this._drawElements( this, this._drawSelectionElement, oAllRange, isDashLine, lineWidth, strokeColor, null, isAllRange ); } }; WorksheetView.prototype._drawCollaborativeElementsMeOther = function ( type, bIsDrawObjects ) { var currentSheetId = this.model.getId(), i, lineWidth = 1, isDashLine = true, strokeColor, arrayCells, oCellTmp; if ( c_oAscLockTypes.kLockTypeMine === type ) { strokeColor = c_oAscCoAuthoringMeBorderColor; arrayCells = this.collaborativeEditing.getLockCellsMe( currentSheetId ); arrayCells = arrayCells.concat( this.collaborativeEditing.getArrayInsertColumnsBySheetId( currentSheetId ) ); arrayCells = arrayCells.concat( this.collaborativeEditing.getArrayInsertRowsBySheetId( currentSheetId ) ); } else { strokeColor = c_oAscCoAuthoringOtherBorderColor; arrayCells = this.collaborativeEditing.getLockCellsOther( currentSheetId ); } for ( i = 0; i < arrayCells.length; ++i ) { oCellTmp = new asc_Range( arrayCells[i].c1, arrayCells[i].r1, arrayCells[i].c2, arrayCells[i].r2 ); this._drawElements( this, this._drawSelectionElement, oCellTmp, isDashLine, lineWidth, strokeColor ); } }; WorksheetView.prototype._drawAutoF = function ( updatedRange, offsetX, offsetY ) { if ( updatedRange ) { this.autoFilters.drawAutoF( updatedRange, offsetX, offsetY ); } else { this._drawElements( this, this._drawAutoF ); } }; WorksheetView.prototype.cleanSelection = function ( range, isFrozen ) { if ( window['IS_NATIVE_EDITOR'] ) { return; } isFrozen = !!isFrozen; if ( range === undefined ) { range = this.visibleRange; } var ctx = this.overlayCtx; var arn = this.activeRange.clone( true ); var arnIntersection = arn.intersectionSimple( range ); var width = ctx.getWidth(); var height = ctx.getHeight(); var offsetX, offsetY, diffWidth = 0, diffHeight = 0; var x1 = Number.MAX_VALUE; var x2 = -Number.MAX_VALUE; var y1 = Number.MAX_VALUE; var y2 = -Number.MAX_VALUE; var i; if ( this.topLeftFrozenCell ) { var cFrozen = this.topLeftFrozenCell.getCol0(); var rFrozen = this.topLeftFrozenCell.getRow0(); diffWidth = this.cols[cFrozen].left - this.cols[0].left; diffHeight = this.rows[rFrozen].top - this.rows[0].top; if ( !isFrozen ) { var oFrozenRange; cFrozen -= 1; rFrozen -= 1; if ( 0 <= cFrozen && 0 <= rFrozen ) { oFrozenRange = new asc_Range( 0, 0, cFrozen, rFrozen ); this.cleanSelection( oFrozenRange, true ); } if ( 0 <= cFrozen ) { oFrozenRange = new asc_Range( 0, this.visibleRange.r1, cFrozen, this.visibleRange.r2 ); this.cleanSelection( oFrozenRange, true ); } if ( 0 <= rFrozen ) { oFrozenRange = new asc_Range( this.visibleRange.c1, 0, this.visibleRange.c2, rFrozen ); this.cleanSelection( oFrozenRange, true ); } } } if ( isFrozen ) { if ( range.c1 !== this.visibleRange.c1 ) { diffWidth = 0; } if ( range.r1 !== this.visibleRange.r1 ) { diffHeight = 0; } offsetX = this.cols[range.c1].left - this.cellsLeft - diffWidth; offsetY = this.rows[range.r1].top - this.cellsTop - diffHeight; } else { offsetX = this.cols[this.visibleRange.c1].left - this.cellsLeft - diffWidth; offsetY = this.rows[this.visibleRange.r1].top - this.cellsTop - diffHeight; } if ( arnIntersection ) { x1 = this.cols[arnIntersection.c1].left - offsetX - this.width_2px; x2 = this.cols[arnIntersection.c2].left + this.cols[arnIntersection.c2].width - offsetX + this.width_1px + /* Это ширина "квадрата" для автофильтра от границы ячейки */this.width_2px; y1 = this.rows[arnIntersection.r1].top - offsetY - this.height_2px; y2 = this.rows[arnIntersection.r2].top + this.rows[arnIntersection.r2].height - offsetY + this.height_1px + /* Это высота "квадрата" для автофильтра от границы ячейки */this.height_2px; } if ( !isFrozen ) { this._activateOverlayCtx(); this._cleanColumnHeaders( arn.c1, arn.c2 ); this._cleanRowHeades( arn.r1, arn.r2 ); this._deactivateOverlayCtx(); } // Если есть активное автозаполнения, то нужно его тоже очистить if ( this.activeFillHandle !== null ) { var activeFillClone = this.activeFillHandle.clone( true ); // Координаты для автозаполнения var xFH1 = this.cols[activeFillClone.c1].left - offsetX - this.width_2px; var xFH2 = this.cols[activeFillClone.c2].left + this.cols[activeFillClone.c2].width - offsetX + this.width_1px + this.width_2px; var yFH1 = this.rows[activeFillClone.r1].top - offsetY - this.height_2px; var yFH2 = this.rows[activeFillClone.r2].top + this.rows[activeFillClone.r2].height - offsetY + this.height_1px + this.height_2px; // Выбираем наибольший range для очистки x1 = Math.min( x1, xFH1 ); x2 = Math.max( x2, xFH2 ); y1 = Math.min( y1, yFH1 ); y2 = Math.max( y2, yFH2 ); } if ( this.collaborativeEditing.getCollaborativeEditing() ) { var currentSheetId = this.model.getId(); var nLockAllType = this.collaborativeEditing.isLockAllOther( currentSheetId ); if ( c_oAscMouseMoveLockedObjectType.None !== nLockAllType ) { this.overlayCtx.clear(); } else { var arrayElementsMe = this.collaborativeEditing.getLockCellsMe( currentSheetId ); var arrayElementsOther = this.collaborativeEditing.getLockCellsOther( currentSheetId ); var arrayElements = arrayElementsMe.concat( arrayElementsOther ); arrayElements = arrayElements.concat( this.collaborativeEditing.getArrayInsertColumnsBySheetId( currentSheetId ) ); arrayElements = arrayElements.concat( this.collaborativeEditing.getArrayInsertRowsBySheetId( currentSheetId ) ); for ( i = 0; i < arrayElements.length; ++i ) { var arFormulaTmp = new asc_Range( arrayElements[i].c1, arrayElements[i].r1, arrayElements[i].c2, arrayElements[i].r2 ); var aFormulaIntersection = arFormulaTmp.intersection( range ); if ( aFormulaIntersection ) { // Координаты для автозаполнения var xCE1 = this.cols[aFormulaIntersection.c1].left - offsetX - this.width_2px; var xCE2 = this.cols[aFormulaIntersection.c2].left + this.cols[aFormulaIntersection.c2].width - offsetX + this.width_1px + this.width_2px; var yCE1 = this.rows[aFormulaIntersection.r1].top - offsetY - this.height_2px; var yCE2 = this.rows[aFormulaIntersection.r2].top + this.rows[aFormulaIntersection.r2].height - offsetY + this.height_1px + this.height_2px; // Выбираем наибольший range для очистки x1 = Math.min( x1, xCE1 ); x2 = Math.max( x2, xCE2 ); y1 = Math.min( y1, yCE1 ); y2 = Math.max( y2, yCE2 ); } } } } if ( 0 < this.arrActiveFormulaRanges.length ) { for ( i = 0; i < this.arrActiveFormulaRanges.length; ++i ) { var activeFormula = this.arrActiveFormulaRanges[i].clone( true ); activeFormula = activeFormula.intersection( range ); if ( null === activeFormula ) { // это ссылка из формулы на еще не добавленный рэндж continue; } // Координаты для range формулы var xF1 = this.cols[activeFormula.c1].left - offsetX - this.width_2px; var xF2 = activeFormula.c2 > this.cols.length ? width : this.cols[activeFormula.c2].left + this.cols[activeFormula.c2].width - offsetX + this.width_1px; var yF1 = this.rows[activeFormula.r1].top - offsetY - this.height_2px; var yF2 = activeFormula.r2 > this.rows.length ? height : this.rows[activeFormula.r2].top + this.rows[activeFormula.r2].height - offsetY + this.height_1px; // Выбираем наибольший range для очистки x1 = Math.min( x1, xF1 ); x2 = Math.max( x2, xF2 ); y1 = Math.min( y1, yF1 ); y2 = Math.max( y2, yF2 ); } // Вышли из редактора, очистим массив //TODO: Нужно ли очищать массив ячеек из формулы в отрисовке??? /*if ( false === this.isFormulaEditMode && !isFrozen ) { this.arrActiveFormulaRanges = []; }*/ } if ( 0 < this.arrActiveChartsRanges.length ) { for ( i = 0; i < this.arrActiveChartsRanges.length; ++i ) { var activeFormula = this.arrActiveChartsRanges[i].clone( true ); activeFormula = activeFormula.intersection( range ); if ( null === activeFormula ) { // это ссылка из формулы на еще не добавленный рэндж continue; } // Координаты для range формулы var xF1 = this.cols[activeFormula.c1].left - offsetX - this.width_2px; var xF2 = activeFormula.c2 > this.cols.length ? width : this.cols[activeFormula.c2].left + this.cols[activeFormula.c2].width - offsetX + this.width_1px; var yF1 = this.rows[activeFormula.r1].top - offsetY - this.height_2px; var yF2 = activeFormula.r2 > this.rows.length ? height : this.rows[activeFormula.r2].top + this.rows[activeFormula.r2].height - offsetY + this.height_1px; // Выбираем наибольший range для очистки x1 = Math.min( x1, xF1 ); x2 = Math.max( x2, xF2 ); y1 = Math.min( y1, yF1 ); y2 = Math.max( y2, yF2 ); } } if ( null !== this.activeMoveRange ) { var activeMoveRangeClone = this.activeMoveRange.clone( true ); // Увеличиваем, если выходим за область видимости // Critical Bug 17413 while ( !this.cols[activeMoveRangeClone.c2] ) { this.expandColsOnScroll( true ); this.handlers.trigger( "reinitializeScrollX" ); } while ( !this.rows[activeMoveRangeClone.r2] ) { this.expandRowsOnScroll( true ); this.handlers.trigger( "reinitializeScrollY" ); } // Координаты для перемещения диапазона var xMR1 = this.cols[activeMoveRangeClone.c1].left - offsetX - this.width_2px; var xMR2 = this.cols[activeMoveRangeClone.c2].left + this.cols[activeMoveRangeClone.c2].width - offsetX + this.width_1px + this.width_2px; var yMR1 = this.rows[activeMoveRangeClone.r1].top - offsetY - this.height_2px; var yMR2 = this.rows[activeMoveRangeClone.r2].top + this.rows[activeMoveRangeClone.r2].height - offsetY + this.height_1px + this.height_2px; // Выбираем наибольший range для очистки x1 = Math.min( x1, xMR1 ); x2 = Math.max( x2, xMR2 ); y1 = Math.min( y1, yMR1 ); y2 = Math.max( y2, yMR2 ); } if ( null !== this.copyActiveRange ) { // Координаты для перемещения диапазона var xCopyAr1 = this.cols[this.copyActiveRange.c1].left - offsetX - this.width_2px; var xCopyAr2 = this.cols[this.copyActiveRange.c2].left + this.cols[this.copyActiveRange.c2].width - offsetX + this.width_1px + this.width_2px; var yCopyAr1 = this.rows[this.copyActiveRange.r1].top - offsetY - this.height_2px; var yCopyAr2 = this.rows[this.copyActiveRange.r2].top + this.rows[this.copyActiveRange.r2].height - offsetY + this.height_1px + this.height_2px; // Выбираем наибольший range для очистки x1 = Math.min( x1, xCopyAr1 ); x2 = Math.max( x2, xCopyAr2 ); y1 = Math.min( y1, yCopyAr1 ); y2 = Math.max( y2, yCopyAr2 ); } if ( !(Number.MAX_VALUE === x1 && -Number.MAX_VALUE === x2 && Number.MAX_VALUE === y1 && -Number.MAX_VALUE === y2) ) { ctx.save() .beginPath() .rect( this.cellsLeft, this.cellsTop, ctx.getWidth() - this.cellsLeft, ctx.getHeight() - this.cellsTop ) .clip() .clearRect( x1, y1, x2 - x1, y2 - y1 ) .restore(); } return this; }; WorksheetView.prototype.updateSelection = function () { this.cleanSelection(); this._drawSelection(); }; // mouseX - это разница стартовых координат от мыши при нажатии и границы WorksheetView.prototype.drawColumnGuides = function ( col, x, y, mouseX ) { x *= asc_getcvt( 0/*px*/, 1/*pt*/, this._getPPIX() ); // Учитываем координаты точки, где мы начали изменение размера x += mouseX; var ctx = this.overlayCtx; var offsetX = this.cols[this.visibleRange.c1].left - this.cellsLeft; var offsetFrozen = this.getFrozenPaneOffset( false, true ); offsetX -= offsetFrozen.offsetX; var x1 = this.cols[col].left - offsetX - this.width_1px; var h = ctx.getHeight(); var widthPt = (x - x1); if ( 0 > widthPt ) { widthPt = 0; } ctx.clear(); this._drawSelection(); ctx.setFillPattern( this.settings.ptrnLineDotted1 ) .fillRect( x1, 0, this.width_1px, h ) .fillRect( x, 0, this.width_1px, h ); return new asc_CMM( { type : c_oAscMouseMoveType.ResizeColumn, sizeCCOrPt: this._colWidthToCharCount( widthPt ), sizePx : widthPt * 96 / 72, x : (x1 + this.cols[col].width) * asc_getcvt( 1/*pt*/, 0/*px*/, this._getPPIX() ), y : this.cellsTop * asc_getcvt( 1/*pt*/, 0/*px*/, this._getPPIY() ) } ); }; // mouseY - это разница стартовых координат от мыши при нажатии и границы WorksheetView.prototype.drawRowGuides = function ( row, x, y, mouseY ) { y *= asc_getcvt( 0/*px*/, 1/*pt*/, this._getPPIY() ); // Учитываем координаты точки, где мы начали изменение размера y += mouseY; var ctx = this.overlayCtx; var offsetY = this.rows[this.visibleRange.r1].top - this.cellsTop; var offsetFrozen = this.getFrozenPaneOffset( true, false ); offsetY -= offsetFrozen.offsetY; var y1 = this.rows[row].top - offsetY - this.height_1px; var w = ctx.getWidth(); var heightPt = (y - y1); if ( 0 > heightPt ) { heightPt = 0; } ctx.clear(); this._drawSelection(); ctx.setFillPattern( this.settings.ptrnLineDotted1 ) .fillRect( 0, y1, w, this.height_1px ) .fillRect( 0, y, w, this.height_1px ); return new asc_CMM( { type : c_oAscMouseMoveType.ResizeRow, sizeCCOrPt: heightPt, sizePx : heightPt * 96 / 72, x : this.cellsLeft * asc_getcvt( 1/*pt*/, 0/*px*/, this._getPPIX() ), y : (y1 + this.rows[row].height) * asc_getcvt( 1/*pt*/, 0/*px*/, this._getPPIY() ) } ); }; // --- Cache --- WorksheetView.prototype._cleanCache = function ( range ) { var r, c, row; if ( range === undefined ) { range = this.activeRange.clone( true ); } for ( r = range.r1; r <= range.r2; ++r ) { row = this.cache.rows[r]; if ( row !== undefined ) { // Должны еще крайнюю удалить c = range.c1; if ( row.erased[c - 1] ) { delete row.erased[c - 1]; } for ( ; c <= range.c2; ++c ) { if ( row.columns[c] ) { delete row.columns[c]; } if ( row.columnsWithText[c] ) { delete row.columnsWithText[c]; } if ( row.erased[c] ) { delete row.erased[c]; } } } } }; // ----- Cell text cache ----- /** Очищает кэш метрик текста ячеек */ WorksheetView.prototype._cleanCellsTextMetricsCache = function () { var s = this.cache.sectors = []; var vr = this.visibleRange; var h = vr.r2 + 1 - vr.r1; var rl = this.rows.length; var rc = asc_floor( rl / h ) + (rl % h > 0 ? 1 : 0); var range = new asc_Range( 0, 0, this.cols.length - 1, h - 1 ); var j; for ( j = rc; j > 0; --j, range.r1 += h, range.r2 += h ) { if ( j === 1 && rl % h > 0 ) { range.r2 = rl - 1; } s.push( range.clone() ); } }; /** * Обновляет общий кэш и кэширует метрики текста ячеек для указанного диапазона * @param {Asc.Range} [range] Диапазон кэширования текта */ WorksheetView.prototype._prepareCellTextMetricsCache = function ( range ) { var firstUpdateRow = null; if ( !range ) { range = this.visibleRange; if ( this.topLeftFrozenCell ) { var row = this.topLeftFrozenCell.getRow0(); var col = this.topLeftFrozenCell.getCol0(); if ( 0 < row && 0 < col ) { firstUpdateRow = asc.getMinValueOrNull( firstUpdateRow, this._prepareCellTextMetricsCache2( new Asc.Range( 0, 0, col - 1, row - 1 ) ) ); } if ( 0 < row ) { firstUpdateRow = asc.getMinValueOrNull( firstUpdateRow, this._prepareCellTextMetricsCache2( new Asc.Range( this.visibleRange.c1, 0, this.visibleRange.c2, row - 1 ) ) ); } if ( 0 < col ) { firstUpdateRow = asc.getMinValueOrNull( firstUpdateRow, this._prepareCellTextMetricsCache2( new Asc.Range( 0, this.visibleRange.r1, col - 1, this.visibleRange.r2 ) ) ); } } } firstUpdateRow = asc.getMinValueOrNull( firstUpdateRow, this._prepareCellTextMetricsCache2( range ) ); if ( null !== firstUpdateRow || this.isChanged ) { // Убрал это из _calcCellsTextMetrics, т.к. вызов был для каждого сектора(добавляло тормоза: баг 20388) // Код нужен для бага http://bugzserver/show_bug.cgi?id=13875 this._updateRowPositions(); this._calcVisibleRows(); if ( this.objectRender ) { this.objectRender.updateSizeDrawingObjects( {target: c_oTargetType.RowResize, row: firstUpdateRow}, true ); } } }; /** * Обновляет общий кэш и кэширует метрики текста ячеек для указанного диапазона (сама реализация, напрямую не вызывать, только из _prepareCellTextMetricsCache) * @param {Asc.Range} [range] Диапазон кэширования текта */ WorksheetView.prototype._prepareCellTextMetricsCache2 = function ( range ) { var firstUpdateRow = null; var s = this.cache.sectors; for ( var i = 0; i < s.length; ) { if ( s[i].isIntersect( range ) ) { this._calcCellsTextMetrics( s[i] ); s.splice( i, 1 ); firstUpdateRow = null !== firstUpdateRow ? Math.min( range.r1, firstUpdateRow ) : range.r1; continue; } ++i; } return firstUpdateRow; }; /** * Кэширует метрики текста для диапазона ячеек * @param {Asc.Range} range description */ WorksheetView.prototype._calcCellsTextMetrics = function ( range ) { var colsLength = this.cols.length; if ( range === undefined ) { range = new Asc.Range( 0, 0, colsLength - 1, this.rows.length - 1 ); } var rowModel, rowCells, cellColl; for ( var row = range.r1; row <= range.r2; ++row ) { if ( this.height_1px > this.rows[row].height ) { continue; } // Теперь получаем только не пустые ячейки для строки rowModel = this.model._getRowNoEmpty( row ); if ( null === rowModel ) { continue; } rowCells = rowModel.getCells(); for ( cellColl in rowCells ) { cellColl = cellColl - 0; if ( colsLength <= cellColl || this.width_1px > this.cols[cellColl].width ) { continue; } this._addCellTextToCache( cellColl, row ); } } this.isChanged = false; }; WorksheetView.prototype._fetchRowCache = function ( row ) { return (this.cache.rows[row] = ( this.cache.rows[row] || new CacheElement() )); }; WorksheetView.prototype._fetchCellCache = function ( col, row ) { var r = this._fetchRowCache( row ); return (r.columns[col] = ( r.columns[col] || {} )); }; WorksheetView.prototype._fetchCellCacheText = function ( col, row ) { var r = this._fetchRowCache( row ); return (r.columnsWithText[col] = ( r.columnsWithText[col] || {} )); }; WorksheetView.prototype._getRowCache = function ( row ) { return this.cache.rows[row]; }; WorksheetView.prototype._getCellCache = function ( col, row ) { var r = this.cache.rows[row]; return r ? r.columns[col] : undefined; }; WorksheetView.prototype._getCellTextCache = function ( col, row, dontLookupMergedCells ) { var r = this.cache.rows[row], c = r ? r.columns[col] : undefined; if ( c && c.text ) { return c.text; } else if ( !dontLookupMergedCells ) { // ToDo проверить это условие, возможно оно избыточно var range = this.model.getMergedByCell( row, col ); return null !== range ? this._getCellTextCache( range.c1, range.r1, true ) : undefined; } return undefined; }; WorksheetView.prototype._changeColWidth = function ( col, width, pad ) { var cc = Math.min( this._colWidthToCharCount( width + pad ), c_oAscMaxColumnWidth ); var modelw = this._charCountToModelColWidth( cc ); var colw = this._calcColWidth( modelw ); if ( colw.width > this.cols[col].width ) { this.cols[col].width = colw.width; this.cols[col].innerWidth = colw.innerWidth; this.cols[col].charCount = colw.charCount; History.Create_NewPoint(); History.StartTransaction(); // Выставляем, что это bestFit this.model.setColBestFit( true, modelw, col, col ); History.EndTransaction(); this._updateColumnPositions(); this.isChanged = true; } }; WorksheetView.prototype._addCellTextToCache = function ( col, row, canChangeColWidth ) { var self = this; function makeFnIsGoodNumFormat( flags, width ) { return function ( str ) { return self.stringRender.measureString( str, flags, width ).width <= width; }; } var c = this._getCell( col, row ); if ( null === c ) { return col; } var bUpdateScrollX = false; var bUpdateScrollY = false; // Проверка на увеличение колличества столбцов if ( col >= this.cols.length ) { bUpdateScrollX = this.expandColsOnScroll( /*isNotActive*/ false, /*updateColsCount*/ true ); } // Проверка на увеличение колличества строк if ( row >= this.rows.length ) { bUpdateScrollY = this.expandRowsOnScroll( /*isNotActive*/ false, /*updateRowsCount*/ true ); } if ( bUpdateScrollX && bUpdateScrollY ) { this.handlers.trigger( "reinitializeScroll" ); } else if ( bUpdateScrollX ) { this.handlers.trigger( "reinitializeScrollX" ); } else if ( bUpdateScrollY ) { this.handlers.trigger( "reinitializeScrollY" ); } var str, tm, isMerged = false, strCopy; // Range для замерженной ячейки var fl = this._getCellFlags( c ); var mc = fl.merged; var fMergedColumns = false; // Замержены ли колонки (если да, то автоподбор ширины не должен работать) var fMergedRows = false; // Замержены ли строки (если да, то автоподбор высоты не должен работать) if ( null !== mc ) { if ( col !== mc.c1 || row !== mc.r1 ) { // Проверим внесена ли первая ячейка в cache (иначе если была скрыта первая строка или первый столбец, то мы не внесем) if (undefined === this._getCellTextCache(mc.c1, mc.r1, true)) { return this._addCellTextToCache(mc.c1, mc.r1, canChangeColWidth); } return mc.c2; } // skip other merged cell from range if ( mc.c1 !== mc.c2 ) { fMergedColumns = true; } if ( mc.r1 !== mc.r2 ) { fMergedRows = true; } isMerged = true; } var angle = c.getAngle(); if ( this._isCellEmptyTextString( c ) ) { if ( !angle && c.isNotDefaultFont() ) { // Пустая ячейка с измененной гарнитурой или размером, учитвается в высоте str = c.getValue2(); if ( 0 < str.length ) { // Без текста не будет толка strCopy = [str[0].clone()]; strCopy[0].text = 'A'; tm = this._roundTextMetrics( this.stringRender.measureString( strCopy, fl ) ); this._updateRowHeight( tm, col, row, isMerged, fMergedRows ); } } return mc ? mc.c2 : col; } var dDigitsCount = 0; var colWidth = 0; var cellType = c.getType(); fl.isNumberFormat = (null === cellType || CellValueType.String !== cellType); // Автоподбор делается по любому типу (кроме строки) var numFormatStr = c.getNumFormatStr(); var pad = this.width_padding * 2 + this.width_1px; var sstr, sfl, stm; if ( !this.cols[col].isCustomWidth && fl.isNumberFormat && !fMergedColumns && (c_oAscCanChangeColWidth.numbers === canChangeColWidth || c_oAscCanChangeColWidth.all === canChangeColWidth) ) { colWidth = this.cols[col].innerWidth; // Измеряем целую часть числа sstr = c.getValue2( gc_nMaxDigCountView, function () { return true; } ); if ( "General" === numFormatStr && c_oAscCanChangeColWidth.all !== canChangeColWidth ) { // asc.truncFracPart изменяет исходный массив, поэтому клонируем var fragmentsTmp = []; for ( var k = 0; k < sstr.length; ++k ) fragmentsTmp.push( sstr[k].clone() ); sstr = asc.truncFracPart( fragmentsTmp ); } sfl = fl.clone(); sfl.wrapText = false; stm = this._roundTextMetrics( this.stringRender.measureString( sstr, sfl, colWidth ) ); // Если целая часть числа не убирается в ячейку, то расширяем столбец if ( stm.width > colWidth ) { this._changeColWidth( col, stm.width, pad ); } // Обновленная ячейка dDigitsCount = this.cols[col].charCount; colWidth = this.cols[col].innerWidth; } else if ( null === mc ) { // Обычная ячейка dDigitsCount = this.cols[col].charCount; colWidth = this.cols[col].innerWidth; // подбираем ширину if ( !this.cols[col].isCustomWidth && !fMergedColumns && !fl.wrapText && c_oAscCanChangeColWidth.all === canChangeColWidth ) { sstr = c.getValue2( gc_nMaxDigCountView, function () { return true; } ); stm = this._roundTextMetrics( this.stringRender.measureString( sstr, fl, colWidth ) ); if ( stm.width > colWidth ) { this._changeColWidth( col, stm.width, pad ); // Обновленная ячейка dDigitsCount = this.cols[col].charCount; colWidth = this.cols[col].innerWidth; } } } else { // Замерженная ячейка, нужна сумма столбцов for ( var i = mc.c1; i <= mc.c2 && i < this.cols.length; ++i ) { colWidth += this.cols[i].width; dDigitsCount += this.cols[i].charCount; } colWidth -= pad; } // ToDo dDigitsCount нужно рассчитывать исходя не из дефалтового шрифта и размера, а исходя из текущего шрифта и размера ячейки str = c.getValue2( dDigitsCount, makeFnIsGoodNumFormat( fl, colWidth ) ); var ha = c.getAlignHorizontalByValue().toLowerCase(); var va = c.getAlignVertical().toLowerCase(); var maxW = fl.wrapText || fl.shrinkToFit || isMerged || asc.isFixedWidthCell( str ) ? this._calcMaxWidth( col, row, mc ) : undefined; tm = this._roundTextMetrics( this.stringRender.measureString( str, fl, maxW ) ); var cto = (isMerged || fl.wrapText) ? { maxWidth: maxW - this.cols[col].innerWidth + this.cols[col].width, leftSide: 0, rightSide: 0 } : this._calcCellTextOffset( col, row, ha, tm.width ); // check right side of cell text and append columns if it exceeds existing cells borders if ( !isMerged ) { var rside = this.cols[col - cto.leftSide].left + tm.width; var lc = this.cols[this.cols.length - 1]; if ( rside > lc.left + lc.width ) { this._appendColumns( rside ); cto = this._calcCellTextOffset( col, row, ha, tm.width ); } } var oFontColor = c.getFontcolor(); var rowHeight = this.rows[row].height; var textBound = {}; if ( angle ) { // повернутый текст учитывает мерж ячеек по строкам if ( fMergedRows ) { rowHeight = 0; for ( var j = mc.r1; j <= mc.r2 && j < this.nRowsCount; ++j ) { rowHeight += this.rows[j].height; } } textBound = this.stringRender.getTransformBound( angle, 0, 0, colWidth, rowHeight, tm.width, ha, va, maxW ); // NOTE: надо сделать как в экселе если проекция строчки на Y больше высоты ячейки подставлять # и рисовать все по центру // if (fl.isNumberFormat) { // var prj = Math.abs(Math.sin(angle * Math.PI / 180.0) * tm.width); // if (prj > rowHeight) { // //if (maxW === undefined) {} // maxW = rowHeight / Math.abs(Math.cos(angle * Math.PI / 180.0)); // str = c.getValue2(gc_nMaxDigCountView, makeFnIsGoodNumFormat(fl, maxW)); // // for (i = 0; i < str.length; ++i) { // var f = str[i].format; // if (f) f.repeat = true; // } // // tm = this._roundTextMetrics(this.stringRender.measureString(str, fl, maxW)); // } // } } this._fetchCellCache( col, row ).text = { state : this.stringRender.getInternalState(), flags : fl, color : (oFontColor || this.settings.cells.defaultState.color), metrics : tm, cellW : cto.maxWidth, cellHA : ha, cellVA : va, sideL : cto.leftSide, sideR : cto.rightSide, cellType : cellType, isFormula: c.isFormula(), angle : angle, textBound: textBound }; this._fetchCellCacheText( col, row ).hasText = true; if ( !angle && (cto.leftSide !== 0 || cto.rightSide !== 0) ) { this._addErasedBordersToCache( col - cto.leftSide, col + cto.rightSide, row ); } this._updateRowHeight( tm, col, row, isMerged, fMergedRows, va, ha, angle, maxW, colWidth, textBound ); return mc ? mc.c2 : col; }; WorksheetView.prototype._updateRowHeight = function ( tm, col, row, isMerged, fMergedRows, va, ha, angle, maxW, colWidth, textBound ) { var rowInfo = this.rows[row], rowHeight; // update row's descender if ( va !== kvaTop && va !== kvaCenter && !isMerged ) { rowInfo.descender = Math.max( rowInfo.descender, tm.height - tm.baseline ); } rowHeight = rowInfo.height; // update row's height if ( !rowInfo.isCustomHeight && !(window["NATIVE_EDITOR_ENJINE"] && this.notUpdateRowHeight) ) { // Замерженная ячейка (с 2-мя или более строками) не влияет на высоту строк! if ( !fMergedRows ) { var newHeight = tm.height; if ( angle ) { if ( textBound ) { newHeight = textBound.height; } } rowInfo.heightReal = rowInfo.height = Math.min( this.maxRowHeight, Math.max( rowInfo.height, newHeight ) ); if ( rowHeight !== rowInfo.height ) { // ToDo нужно по идее всегда выставлять, но тут проблема с тем, что не всегда можно это в историю заносить (открытие, например). А если не заносить в историю, то при сборке ничего не изменится... if ( !rowInfo.isDefaultHeight ) { this.model.setRowHeight( rowInfo.height + this.height_1px, row, row, false ); } if ( angle ) { this._fetchCellCache( col, row ).text.textBound = this.stringRender.getTransformBound( angle, 0, 0, colWidth, rowInfo.height, tm.width, ha, va, maxW ); } this.isChanged = true; } } } }; WorksheetView.prototype._calcMaxWidth = function ( col, row, mc ) { if ( null === mc ) { return this.cols[col].innerWidth; } var width = this.cols[mc.c1].innerWidth; for ( var c = mc.c1 + 1; c <= mc.c2 && c < this.cols.length; ++c ) { width += this.cols[c].width; } return width; }; WorksheetView.prototype._calcCellTextOffset = function ( col, row, textAlign, textWidth ) { var sideL = [0], sideR = [0], i; var maxWidth = this.cols[col].width; var ls = 0, rs = 0; var pad = this.settings.cells.padding * asc_getcvt( 0/*px*/, 1/*pt*/, this._getPPIX() ); var textW = textAlign === khaCenter ? (textWidth + maxWidth) * 0.5 : textWidth + pad; if ( textAlign === khaRight || textAlign === khaCenter ) { sideL = this._calcCellsWidth( col, 0, row ); // condition (sideL.lenght >= 1) is always true for ( i = 0; i < sideL.length && textW > sideL[i]; ++i ) {/* do nothing */ } ls = i !== sideL.length ? i : i - 1; } if ( textAlign !== khaRight ) { sideR = this._calcCellsWidth( col, this.cols.length - 1, row ); // condition (sideR.lenght >= 1) is always true for ( i = 0; i < sideR.length && textW > sideR[i]; ++i ) {/* do nothing */ } rs = i !== sideR.length ? i : i - 1; } if ( textAlign === khaCenter ) { maxWidth = (sideL[ls] - sideL[0]) + sideR[rs]; } else { maxWidth = textAlign === khaRight ? sideL[ls] : sideR[rs]; } return { maxWidth: maxWidth, leftSide: ls, rightSide: rs }; }; WorksheetView.prototype._calcCellsWidth = function ( colBeg, colEnd, row ) { var inc = colBeg <= colEnd ? 1 : -1, res = []; for ( var i = colBeg; (colEnd - i) * inc >= 0; i += inc ) { if ( i !== colBeg && !this._isCellEmptyOrMerged( i, row ) ) { break; } res.push( this.cols[i].width ); if ( res.length > 1 ) { res[res.length - 1] += res[res.length - 2]; } } return res; }; // Ищет текст в строке (columnsWithText - это колонки, в которых есть текст) WorksheetView.prototype._findSourceOfCellText = function ( col, row ) { var r = this._getRowCache( row ); if ( r ) { for ( var i in r.columnsWithText ) { if ( !r.columns[i] || 0 === this.cols[i].width ) { continue; } var ct = r.columns[i].text; if ( !ct ) { continue; } i >>= 0; var lc = i - ct.sideL, rc = i + ct.sideR; if ( col >= lc && col <= rc ) { return i; } } } return -1; }; // ----- Merged cells cache ----- WorksheetView.prototype._isMergedCells = function ( range ) { return range.isEqual( this.model.getMergedByCell( range.r1, range.c1 ) ); }; // ----- Cell borders cache ----- WorksheetView.prototype._addErasedBordersToCache = function ( colBeg, colEnd, row ) { var rc = this._fetchRowCache( row ); for ( var col = colBeg; col < colEnd; ++col ) { rc.erased[col] = true; } }; WorksheetView.prototype._isLeftBorderErased = function ( col, rowCache ) { return rowCache.erased[col - 1] === true; }; WorksheetView.prototype._isRightBorderErased = function ( col, rowCache ) { return rowCache.erased[col] === true; }; WorksheetView.prototype._calcMaxBorderWidth = function ( b1, b2 ) { // ToDo пересмотреть return Math.max( b1 && b1.w, b2 && b2.w ); }; // ----- Cells utilities ----- /** * Возвращает заголовок колонки по индексу * @param {Number} col Индекс колонки * @return {String} */ WorksheetView.prototype._getColumnTitle = function ( col ) { return g_oCellAddressUtils.colnumToColstrFromWsView( col + 1 ); }; /** * Возвращает заголовок строки по индексу * @param {Number} row Индекс строки * @return {String} */ WorksheetView.prototype._getRowTitle = function ( row ) { return "" + (row + 1); }; /** * Возвращает заголовок ячейки по индексу * @param {Number} col Индекс колонки * @param {Number} row Индекс строки * @return {String} */ WorksheetView.prototype._getCellTitle = function ( col, row ) { return this._getColumnTitle( col ) + this._getRowTitle( row ); }; /** * Возвращает ячейку таблицы (из Worksheet) * @param {Number} col Индекс колонки * @param {Number} row Индекс строки * @return {Range} */ WorksheetView.prototype._getCell = function ( col, row ) { if ( this.nRowsCount < this.model.getRowsCount() + 1 ) { this.expandRowsOnScroll( false, true, 0 ); } // Передаем 0, чтобы увеличить размеры if ( this.nColsCount < this.model.getColsCount() + 1 ) { this.expandColsOnScroll( false, true, 0 ); } // Передаем 0, чтобы увеличить размеры if ( col < 0 || col >= this.nColsCount || row < 0 || row >= this.nRowsCount ) { return null; } return this.model.getCell3( row, col ); }; WorksheetView.prototype._getVisibleCell = function ( col, row ) { return this.model.getCell3( row, col ); }; WorksheetView.prototype._getCellFlags = function ( col, row ) { var c = row !== undefined ? this._getCell( col, row ) : col; var fl = new CellFlags(); if ( null !== c ) { fl.wrapText = c.getWrap(); fl.shrinkToFit = c.getShrinkToFit(); fl.merged = c.hasMerged(); fl.textAlign = c.getAlignHorizontalByValue().toLowerCase(); } return fl; }; WorksheetView.prototype._isCellEmptyText = function ( col, row ) { var c = row !== undefined ? this._getCell( col, row ) : col; return null === c || c.isEmptyText(); }; WorksheetView.prototype._isCellEmptyTextString = function ( col, row ) { var c = row !== undefined ? this._getCell( col, row ) : col; return null === c || c.isEmptyTextString(); }; WorksheetView.prototype._isCellEmptyOrMerged = function ( col, row ) { var c = row !== undefined ? this._getCell( col, row ) : col; if ( null === c ) { return true; } if ( !c.isEmptyText() ) { return false; } return (null === c.hasMerged()); }; WorksheetView.prototype._isCellEmptyOrMergedOrBackgroundColorOrBorders = function ( col, row ) { var c = row !== undefined ? this._getCell( col, row ) : col; if ( null === c ) { return true; } if ( !c.isEmptyTextString() ) { return false; } if ( null !== c.hasMerged() ) { return false; } var bg = c.getFill(); if ( null !== bg ) { return false; } var cb = c.getBorder(); return !((cb.l && c_oAscBorderStyles.None !== cb.l.s) || (cb.r && c_oAscBorderStyles.None !== cb.r.s) || (cb.t && c_oAscBorderStyles.None !== cb.t.s) || (cb.b && c_oAscBorderStyles.None !== cb.b.s) || (cb.dd && c_oAscBorderStyles.None !== cb.dd.s) || (cb.du && c_oAscBorderStyles.None !== cb.du.s)); }; WorksheetView.prototype._getRange = function ( c1, r1, c2, r2 ) { return this.model.getRange3( r1, c1, r2, c2 ); }; WorksheetView.prototype._selectColumnsByRange = function () { var ar = this.activeRange; if ( c_oAscSelectionType.RangeMax === ar.type ) { return; } else { this.cleanSelection(); if ( c_oAscSelectionType.RangeRow === ar.type ) { ar.assign( 0, 0, this.cols.length - 1, this.rows.length - 1 ); ar.type = c_oAscSelectionType.RangeMax; } else { ar.type = c_oAscSelectionType.RangeCol; ar.assign( ar.c1, 0, ar.c2, this.rows.length - 1 ); } this._drawSelection(); } }; WorksheetView.prototype._selectRowsByRange = function () { var ar = this.activeRange; if ( c_oAscSelectionType.RangeMax === ar.type ) { return; } else { this.cleanSelection(); if ( c_oAscSelectionType.RangeCol === ar.type ) { ar.assign( 0, 0, this.cols.length - 1, this.rows.length - 1 ); ar.type = c_oAscSelectionType.RangeMax; } else { ar.type = c_oAscSelectionType.RangeRow; ar.assign( 0, ar.r1, this.cols.length - 1, ar.r2 ); } this._drawSelection(); } }; /** * Возвращает true, если диапазон больше видимой области, и операции над ним могут привести к задержкам * @param {Asc.Range} range Диапазон для проверки * @returns {Boolean} */ WorksheetView.prototype._isLargeRange = function ( range ) { var vr = this.visibleRange; return range.c2 - range.c1 + 1 > (vr.c2 - vr.c1 + 1) * 3 || range.r2 - range.r1 + 1 > (vr.r2 - vr.r1 + 1) * 3; }; /** * Возвращает true, если диапазон состоит из одной ячейки * @param {Asc.Range} range Диапазон * @returns {Boolean} */ WorksheetView.prototype._rangeIsSingleCell = function ( range ) { return range.c1 === range.c2 && range.r1 === range.r2; }; WorksheetView.prototype.drawDepCells = function () { var ctx = this.overlayCtx, _cc = this.cellCommentator, c, node, that = this; ctx.clear(); this._drawSelection(); var color = new CColor( 0, 0, 255 ); function draw_arrow( context, fromx, fromy, tox, toy ) { var headlen = 9, showArrow = tox > that.getCellLeft( 0, 0 ) && toy > that.getCellTop( 0, 0 ), dx = tox - fromx, dy = toy - fromy, tox = tox > that.getCellLeft( 0, 0 ) ? tox : that.getCellLeft( 0, 0 ), toy = toy > that.getCellTop( 0, 0 ) ? toy : that.getCellTop( 0, 0 ), angle = Math.atan2( dy, dx ), _a = Math.PI / 18; // ToDo посмотреть на четкость moveTo, lineTo context.save() .setLineWidth( 1 ) .beginPath() .lineDiag .moveTo( _cc.pxToPt( fromx ), _cc.pxToPt( fromy ) ) .lineTo( _cc.pxToPt( tox ), _cc.pxToPt( toy ) ); // .dashLine(_cc.pxToPt(fromx-.5), _cc.pxToPt(fromy-.5), _cc.pxToPt(tox-.5), _cc.pxToPt(toy-.5), 15, 5) if ( showArrow ) { context .moveTo( _cc.pxToPt( tox - headlen * Math.cos( angle - _a ) ), _cc.pxToPt( toy - headlen * Math.sin( angle - _a ) ) ) .lineTo( _cc.pxToPt( tox ), _cc.pxToPt( toy ) ) .lineTo( _cc.pxToPt( tox - headlen * Math.cos( angle + _a ) ), _cc.pxToPt( toy - headlen * Math.sin( angle + _a ) ) ) .lineTo( _cc.pxToPt( tox - headlen * Math.cos( angle - _a ) ), _cc.pxToPt( toy - headlen * Math.sin( angle - _a ) ) ); } context .setStrokeStyle( color ) .setFillStyle( color ) .stroke() .fill() .closePath() .restore(); } function gCM( _this, col, row ) { var metrics = {top: 0, left: 0, width: 0, height: 0, result: false}; // px var fvr = _this.getFirstVisibleRow(); var fvc = _this.getFirstVisibleCol(); var mergedRange = _this.model.getMergedByCell( row, col ); if ( mergedRange && (fvc < mergedRange.c2) && (fvr < mergedRange.r2) ) { var startCol = (mergedRange.c1 > fvc) ? mergedRange.c1 : fvc; var startRow = (mergedRange.r1 > fvr) ? mergedRange.r1 : fvr; metrics.top = _this.getCellTop( startRow, 0 ) - _this.getCellTop( fvr, 0 ) + _this.getCellTop( 0, 0 ); metrics.left = _this.getCellLeft( startCol, 0 ) - _this.getCellLeft( fvc, 0 ) + _this.getCellLeft( 0, 0 ); for ( var i = startCol; i <= mergedRange.c2; i++ ) { metrics.width += _this.getColumnWidth( i, 0 ) } for ( var i = startRow; i <= mergedRange.r2; i++ ) { metrics.height += _this.getRowHeight( i, 0 ) } metrics.result = true; } else { metrics.top = _this.getCellTop( row, 0 ) - _this.getCellTop( fvr, 0 ) + _this.getCellTop( 0, 0 ); metrics.left = _this.getCellLeft( col, 0 ) - _this.getCellLeft( fvc, 0 ) + _this.getCellLeft( 0, 0 ); metrics.width = _this.getColumnWidth( col, 0 ); metrics.height = _this.getRowHeight( row, 0 ); metrics.result = true; } return metrics } for ( var id in this.depDrawCells ) { c = this.depDrawCells[id].from; node = this.depDrawCells[id].to; var mainCellMetrics = gCM( this, c.nCol, c.nRow ), nodeCellMetrics, _t1, _t2; for ( var id in node ) { if ( !node[id].isArea ) { _t1 = gCM( this, node[id].returnCell().nCol, node[id].returnCell().nRow ) nodeCellMetrics = { t : _t1.top, l : _t1.left, w : _t1.width, h : _t1.height, apt: _t1.top + _t1.height / 2, apl: _t1.left + _t1.width / 4 }; } else { var _t1 = gCM( _wsV, me[id].getBBox().c1, me[id].getBBox().r1 ), _t2 = gCM( _wsV, me[id].getBBox().c2, me[id].getBBox().r2 ); nodeCellMetrics = { t : _t1.top, l : _t1.left, w : _t2.left + _t2.width - _t1.left, h : _t2.top + _t2.height - _t1.top, apt: _t1.top + _t1.height / 2, apl: _t1.left + _t1.width / 4 }; } var x1 = Math.floor( nodeCellMetrics.apl ), y1 = Math.floor( nodeCellMetrics.apt ), x2 = Math.floor( mainCellMetrics.left + mainCellMetrics.width / 4 ), y2 = Math.floor( mainCellMetrics.top + mainCellMetrics.height / 2 ); if ( x1 < 0 && x2 < 0 || y1 < 0 && y2 < 0 ) { continue; } if ( y1 < this.getCellTop( 0, 0 ) ) { y1 -= this.getCellTop( 0, 0 ); } if ( y1 < 0 && y2 > 0 ) { var _x1 = Math.floor( Math.sqrt( (x1 - x2) * (x1 - x2) * y1 * y1 / ((y2 - y1) * (y2 - y1)) ) ); // x1 -= (x1-x2>0?1:-1)*_x1; if ( x1 > x2 ) { x1 -= _x1; } else if ( x1 < x2 ) { x1 += _x1; } } else if ( y1 > 0 && y2 < 0 ) { var _x2 = Math.floor( Math.sqrt( (x1 - x2) * (x1 - x2) * y2 * y2 / ((y2 - y1) * (y2 - y1)) ) ); // x2 -= (x2-x1>0?1:-1)*_x2; if ( x2 > x1 ) { x2 -= _x2; } else if ( x2 < x1 ) { x2 += _x2; } } if ( x1 < 0 && x2 > 0 ) { var _y1 = Math.floor( Math.sqrt( (y1 - y2) * (y1 - y2) * x1 * x1 / ((x2 - x1) * (x2 - x1)) ) ) // y1 -= (y1-y2>0?1:-1)*_y1; if ( y1 > y2 ) { y1 -= _y1; } else if ( y1 < y2 ) { y1 += _y1; } } else if ( x1 > 0 && x2 < 0 ) { var _y2 = Math.floor( Math.sqrt( (y1 - y2) * (y1 - y2) * x2 * x2 / ((x2 - x1) * (x2 - x1)) ) ) // y2 -= (y2-y1>0?1:-1)*_y2; if ( y2 > y1 ) { y2 -= _y2; } else if ( y2 < y1 ) { y2 += _y2; } } draw_arrow( ctx, x1 < this.getCellLeft( 0, 0 ) ? this.getCellLeft( 0, 0 ) : x1, y1 < this.getCellTop( 0, 0 ) ? this.getCellTop( 0, 0 ) : y1, x2, y2 ); // draw_arrow(ctx, x1, y1, x2, y2); // ToDo посмотреть на четкость rect if ( nodeCellMetrics.apl > this.getCellLeft( 0, 0 ) && nodeCellMetrics.apt > this.getCellTop( 0, 0 ) ) { ctx.save() .beginPath() .arc( _cc.pxToPt( Math.floor( nodeCellMetrics.apl ) ), _cc.pxToPt( Math.floor( nodeCellMetrics.apt ) ), 3, 0, 2 * Math.PI, false, -0.5, -0.5 ) .setFillStyle( color ) .fill() .closePath() .setLineWidth( 1 ) .setStrokeStyle( color ) .rect( _cc.pxToPt( nodeCellMetrics.l ), _cc.pxToPt( nodeCellMetrics.t ), _cc.pxToPt( nodeCellMetrics.w - 1 ), _cc.pxToPt( nodeCellMetrics.h - 1 ) ) .stroke() .restore(); } } } }; WorksheetView.prototype.prepareDepCells = function ( se ) { var activeCell = this.activeRange, mc = this.model.getMergedByCell( activeCell.startRow, activeCell.startCol ), c1 = mc ? mc.c1 : activeCell.startCol, r1 = mc ? mc.r1 : activeCell.startRow, c = this._getVisibleCell( c1, r1 ), nodes = (se == c_oAscDrawDepOptions.Master) ? this.model.workbook.dependencyFormulas.getMasterNodes( this.model.getId(), c.getName() ) : this.model.workbook.dependencyFormulas.getSlaveNodes( this.model.getId(), c.getName() ); if ( !nodes ) { return; } if ( !this.depDrawCells ) { this.depDrawCells = {}; } if ( se == c_oAscDrawDepOptions.Master ) { c = c.getCells()[0]; var id = getVertexId( this.model.getId(), c.getName() ); this.depDrawCells[id] = {from: c, to: nodes}; } else { var to = {}, to1, id = getVertexId( this.model.getId(), c.getName() ); to[getVertexId( this.model.getId(), c.getName() )] = this.model.workbook.dependencyFormulas.getNode( this.model.getId(), c.getName() ); to1 = this.model.workbook.dependencyFormulas.getNode( this.model.getId(), c.getName() ); for ( var id2 in nodes ) { if ( this.depDrawCells[id2] ) { $.extend( this.depDrawCells[id2].to, to ) } else { this.depDrawCells[id2] = {} this.depDrawCells[id2].from = nodes[id2].returnCell() this.depDrawCells[id2].to = {} this.depDrawCells[id2].to[id] = to1; } } } this.drawDepCells(); }; WorksheetView.prototype.cleanDepCells = function () { this.depDrawCells = null; this.drawDepCells(); }; // ----- Text drawing ----- WorksheetView.prototype._getPPIX = function () { return this.drawingCtx.getPPIX(); }; WorksheetView.prototype._getPPIY = function () { return this.drawingCtx.getPPIY(); }; WorksheetView.prototype._setFont = function ( drawingCtx, name, size ) { var ctx = (drawingCtx) ? drawingCtx : this.drawingCtx; ctx.setFont( new asc.FontProperties( name, size ) ); }; /** * @param {Asc.TextMetrics} tm * @return {Asc.TextMetrics} */ WorksheetView.prototype._roundTextMetrics = function ( tm ) { tm.width = asc_calcnpt( tm.width, this._getPPIX() ); tm.height = asc_calcnpt( tm.height, 96 ); tm.baseline = asc_calcnpt( tm.baseline, 96 ); if ( tm.centerline !== undefined ) { tm.centerline = asc_calcnpt( tm.centerline, 96 ); } return tm; }; WorksheetView.prototype._calcTextHorizPos = function ( x1, x2, tm, halign ) { switch ( halign ) { case khaCenter: return asc_calcnpt( 0.5 * (x1 + x2 + this.width_1px - tm.width), this._getPPIX() ); case khaRight: return x2 + this.width_1px - this.width_padding - tm.width; case khaJustify: default: return x1 + this.width_padding; } }; WorksheetView.prototype._calcTextVertPos = function ( y1, y2, baseline, tm, valign ) { switch ( valign ) { case kvaCenter: return asc_calcnpt( 0.5 * (y1 + y2 - tm.height), this._getPPIY() ) - this.height_1px; // ToDo возможно стоит тоже использовать 96 case kvaTop: return y1 - this.height_1px; default: return baseline - tm.baseline; } }; WorksheetView.prototype._calcTextWidth = function ( x1, x2, tm, halign ) { switch ( halign ) { case khaJustify: return x2 + this.width_1px - this.width_padding * 2 - x1; default: return tm.width; } }; // ----- Scrolling ----- WorksheetView.prototype._calcCellPosition = function ( c, r, dc, dr ) { var t = this; var vr = t.visibleRange; function findNextCell( col, row, dx, dy ) { var state = t._isCellEmptyText( col, row ); var i = col + dx; var j = row + dy; while ( i >= 0 && i < t.cols.length && j >= 0 && j < t.rows.length ) { var newState = t._isCellEmptyText( i, j ); if ( newState !== state ) { var ret = {}; ret.col = state ? i : i - dx; ret.row = state ? j : j - dy; if ( ret.col !== col || ret.row !== row || state ) { return ret; } state = newState; } i += dx; j += dy; } // Проверки для перехода в самый конец (ToDo пока убрал, чтобы не добавлять тормозов) /*if (i === t.cols.length && state) i = gc_nMaxCol; if (j === t.rows.length && state) j = gc_nMaxRow;*/ return {col: i - dx, row: j - dy}; } function findEnd( col, row ) { var nc1, nc2 = col; do { nc1 = nc2; nc2 = findNextCell( nc1, row, +1, 0 ).col; } while ( nc1 !== nc2 ); return nc2; } function findEOT() { var obr = t.objectRender ? t.objectRender.getDrawingAreaMetrics() : {maxCol: 0, maxRow: 0}; var maxCols = t.model.getColsCount(); var maxRows = t.model.getRowsCount(); var lastC = -1, lastR = -1; for ( var col = 0; col < maxCols; ++col ) { for ( var row = 0; row < maxRows; ++row ) { if ( !t._isCellEmptyText( col, row ) ) { lastC = Math.max( lastC, col ); lastR = Math.max( lastR, row ); } } } return {col: Math.max( lastC, obr.maxCol ), row: Math.max( lastR, obr.maxRow )}; } var eot = dc > +2.0001 && dc < +2.9999 && dr > +2.0001 && dr < +2.9999 ? findEOT() : null; var newCol = (function () { if ( dc > +0.0001 && dc < +0.9999 ) { return c + (vr.c2 - vr.c1 + 1); } // PageDown if ( dc < -0.0001 && dc > -0.9999 ) { return c - (vr.c2 - vr.c1 + 1); } // PageUp if ( dc > +1.0001 && dc < +1.9999 ) { return findNextCell( c, r, +1, 0 ).col; } // Ctrl + -> if ( dc < -1.0001 && dc > -1.9999 ) { return findNextCell( c, r, -1, 0 ).col; } // Ctrl + <- if ( dc > +2.0001 && dc < +2.9999 ) { return !eot ? findEnd( c, r ) : eot.col; } // End if ( dc < -2.0001 && dc > -2.9999 ) { return 0; } // Home return c + dc; })(); var newRow = (function () { if ( dr > +0.0001 && dr < +0.9999 ) { return r + (vr.r2 - vr.r1 + 1); } if ( dr < -0.0001 && dr > -0.9999 ) { return r - (vr.r2 - vr.r1 + 1); } if ( dr > +1.0001 && dr < +1.9999 ) { return findNextCell( c, r, 0, +1 ).row; } if ( dr < -1.0001 && dr > -1.9999 ) { return findNextCell( c, r, 0, -1 ).row; } if ( dr > +2.0001 && dr < +2.9999 ) { return !eot ? 0 : eot.row; } if ( dr < -2.0001 && dr > -2.9999 ) { return 0; } return r + dr; })(); if ( newCol >= t.cols.length && newCol <= gc_nMaxCol0 ) { t.nColsCount = newCol + 1; t._calcWidthColumns( /*fullRecalc*/2 ); } if ( newRow >= t.rows.length && newRow <= gc_nMaxRow0 ) { t.nRowsCount = newRow + 1; t._calcHeightRows( /*fullRecalc*/2 ); } return { col: newCol < 0 ? 0 : Math.min( newCol, t.cols.length - 1 ), row: newRow < 0 ? 0 : Math.min( newRow, t.rows.length - 1 ) }; }; WorksheetView.prototype._isColDrawnPartially = function ( col, leftCol, diffWidth ) { if ( col <= leftCol || col >= this.nColsCount ) { return false; } diffWidth = diffWidth ? diffWidth : 0; var c = this.cols; return c[col].left + c[col].width - c[leftCol].left + this.cellsLeft + diffWidth > this.drawingCtx.getWidth(); }; WorksheetView.prototype._isRowDrawnPartially = function ( row, topRow, diffHeight ) { if ( row <= topRow || row >= this.nRowsCount ) { return false; } diffHeight = diffHeight ? diffHeight : 0; var r = this.rows; return r[row].top + r[row].height - r[topRow].top + this.cellsTop + diffHeight > this.drawingCtx.getHeight(); }; WorksheetView.prototype._isVisibleX = function () { var vr = this.visibleRange; var c = this.cols; var x = c[vr.c2].left + c[vr.c2].width; var offsetFrozen = this.getFrozenPaneOffset( false, true ); x += offsetFrozen.offsetX; return x - c[vr.c1].left + this.cellsLeft < this.drawingCtx.getWidth(); }; WorksheetView.prototype._isVisibleY = function () { var vr = this.visibleRange; var r = this.rows; var y = r[vr.r2].top + r[vr.r2].height; var offsetFrozen = this.getFrozenPaneOffset( true, false ); y += offsetFrozen.offsetY; return y - r[vr.r1].top + this.cellsTop < this.drawingCtx.getHeight(); }; WorksheetView.prototype._updateVisibleRowsCount = function ( skipScrollReinit ) { var isUpdate = false; this._calcVisibleRows(); while ( this._isVisibleY() && !this.isMaxRow() ) { // Добавим еще строки, чтоб не было видно фон под таблицей this.expandRowsOnScroll( true ); this._calcVisibleRows(); isUpdate = true; } if ( !skipScrollReinit && isUpdate ) { this.handlers.trigger( "reinitializeScrollY" ); } }; WorksheetView.prototype._updateVisibleColsCount = function ( skipScrollReinit ) { var isUpdate = false; this._calcVisibleColumns(); while ( this._isVisibleX() && !this.isMaxCol() ) { // Добавим еще столбцы, чтоб не было видно фон под таблицей this.expandColsOnScroll( true ); this._calcVisibleColumns(); isUpdate = true; } if ( !skipScrollReinit && isUpdate ) { this.handlers.trigger( "reinitializeScrollX" ); } }; WorksheetView.prototype.isMaxRow = function () { var rowsCountCurrent = this.rows.length; if ( gc_nMaxRow === rowsCountCurrent ) { return true; } var rowsCount = this.model.getRowsCount() + 1; return rowsCount <= rowsCountCurrent && this.model.isDefaultHeightHidden(); }; WorksheetView.prototype.isMaxCol = function () { var colsCountCurrent = this.cols.length; if ( gc_nMaxCol === colsCountCurrent ) { return true; } var colsCount = this.model.getColsCount() + 1; return colsCount <= colsCountCurrent && this.model.isDefaultWidthHidden(); }; WorksheetView.prototype.scrollVertical = function ( delta, editor ) { var vr = this.visibleRange; var start = this._calcCellPosition( vr.c1, vr.r1, 0, delta ).row; var fixStartRow = new asc_Range( vr.c1, start, vr.c2, start ); fixStartRow.startCol = vr.c1; fixStartRow.startRow = start; this._fixSelectionOfHiddenCells( 0, delta >= 0 ? +1 : -1, fixStartRow ); this._fixVisibleRange( fixStartRow ); var reinitScrollY = start !== fixStartRow.r1; if ( reinitScrollY && 0 > delta ) // Для скролла вверх обычный сдвиг + дорисовка { delta += fixStartRow.r1 - start; } start = fixStartRow.r1; if ( start === vr.r1 ) { if ( reinitScrollY ) { this.handlers.trigger( "reinitializeScrollY" ); } return this; } this.cleanSelection(); this.cellCommentator.cleanSelectedComment(); var ctx = this.drawingCtx; var ctxW = ctx.getWidth(); var ctxH = ctx.getHeight(); var offsetX, offsetY, diffWidth = 0, diffHeight = 0, cFrozen = 0, rFrozen = 0; if ( this.topLeftFrozenCell ) { cFrozen = this.topLeftFrozenCell.getCol0(); rFrozen = this.topLeftFrozenCell.getRow0(); diffWidth = this.cols[cFrozen].left - this.cols[0].left; diffHeight = this.rows[rFrozen].top - this.rows[0].top; } var oldVRE_isPartial = this._isRowDrawnPartially( vr.r2, vr.r1, diffHeight ); var oldVR = vr.clone(); var oldStart = vr.r1; var oldEnd = vr.r2; // ToDo стоит тут переделать весь scroll vr.r1 = start; this._updateVisibleRowsCount(); // Это необходимо для того, чтобы строки, у которых высота по тексту, рассчитались if ( !oldVR.intersectionSimple( vr ) ) { // Полностью обновилась область this._prepareCellTextMetricsCache( vr ); } else { if ( 0 > delta ) { // Идем вверх this._prepareCellTextMetricsCache( new asc_Range( vr.c1, start, vr.c2, oldStart - 1 ) ); } else { // Идем вниз this._prepareCellTextMetricsCache( new asc_Range( vr.c1, oldEnd + 1, vr.c2, vr.r2 ) ); } } var oldDec = Math.max( calcDecades( oldEnd + 1 ), 3 ); var oldW, x, dx; var dy = this.rows[start].top - this.rows[oldStart].top; var oldH = ctxH - this.cellsTop - Math.abs( dy ) - diffHeight; var scrollDown = (dy > 0 && oldH > 0); var y = this.cellsTop + (scrollDown ? dy : 0) + diffHeight; var lastRowHeight = (scrollDown && oldVRE_isPartial) ? ctxH - (this.rows[oldEnd].top - this.rows[oldStart].top + this.cellsTop + diffHeight) : 0; if ( this.isCellEditMode && editor && this.activeRange.r1 >= rFrozen ) { editor.move( this.cellsLeft + (this.activeRange.c1 >= cFrozen ? diffWidth : 0), this.cellsTop + diffHeight, ctxW, ctxH ); } var widthChanged = Math.max( calcDecades( vr.r2 + 1 ), 3 ) !== oldDec; if ( widthChanged ) { x = this.cellsLeft; this._calcHeaderColumnWidth(); this._updateColumnPositions(); this._calcVisibleColumns(); this._drawCorner(); this._cleanColumnHeadersRect(); this._drawColumnHeaders( /*drawingCtx*/ undefined ); dx = this.cellsLeft - x; oldW = ctxW - x - Math.abs( dx ); if ( rFrozen ) { ctx.drawImage( ctx.getCanvas(), x, this.cellsTop, oldW, diffHeight, x + dx, this.cellsTop, oldW, diffHeight ); // ToDo Посмотреть с объектами!!! } this._drawFrozenPane( true ); } else { dx = 0; x = this.headersLeft; oldW = ctxW; } // Перемещаем область var moveHeight = oldH - lastRowHeight; if ( moveHeight > 0 ) { ctx.drawImage( ctx.getCanvas(), x, y, oldW, moveHeight, x + dx, y - dy, oldW, moveHeight ); // Заглушка для safari (http://bugzserver/show_bug.cgi?id=25546). Режим 'copy' сначала затирает, а // потом рисует (а т.к. мы рисуем сами на себе, то уже картинка будет пустой) if ( AscBrowser.isSafari ) { this.drawingGraphicCtx.moveImageDataSafari( x, y, oldW, moveHeight, x + dx, y - dy ); } else { this.drawingGraphicCtx.moveImageData( x, y, oldW, moveHeight, x + dx, y - dy ); } } // Очищаем область var clearTop = this.cellsTop + diffHeight + (scrollDown && moveHeight > 0 ? moveHeight : 0); var clearHeight = (moveHeight > 0) ? Math.abs( dy ) + lastRowHeight : ctxH - (this.cellsTop + diffHeight); ctx.setFillStyle( this.settings.cells.defaultState.background ) .fillRect( this.headersLeft, clearTop, ctxW, clearHeight ); this.drawingGraphicCtx.clearRect( this.headersLeft, clearTop, ctxW, clearHeight ); if ( this.objectRender && this.objectRender.drawingArea ) { this.objectRender.drawingArea.reinitRanges(); } // Дорисовываем необходимое if ( dy < 0 || vr.r2 !== oldEnd || oldVRE_isPartial || dx !== 0 ) { var r1, r2; if ( moveHeight > 0 ) { if ( scrollDown ) { r1 = oldEnd + (oldVRE_isPartial ? 0 : 1); r2 = vr.r2; } else { r1 = vr.r1; r2 = vr.r1 - 1 - delta; } } else { r1 = vr.r1; r2 = vr.r2; } var range = new asc_Range( vr.c1, r1, vr.c2, r2 ); if ( dx === 0 ) { this._drawRowHeaders( /*drawingCtx*/ undefined, r1, r2 ); } else { // redraw all headres, because number of decades in row index has been changed this._drawRowHeaders( /*drawingCtx*/ undefined ); if ( dx < 0 ) { // draw last column var r_; var r1_ = r2 + 1; var r2_ = vr.r2; if ( r2_ >= r1_ ) { r_ = new asc_Range( vr.c2, r1_, vr.c2, r2_ ); this._drawGrid( /*drawingCtx*/ undefined, r_ ); this._drawCellsAndBorders( /*drawingCtx*/undefined, r_ ); } if ( 0 < rFrozen ) { r_ = new asc_Range( vr.c2, 0, vr.c2, rFrozen - 1 ); offsetY = this.rows[0].top - this.cellsTop; this._drawGrid( /*drawingCtx*/ undefined, r_, /*offsetXForDraw*/undefined, offsetY ); this._drawCellsAndBorders( /*drawingCtx*/undefined, r_, /*offsetXForDraw*/undefined, offsetY ); } } } offsetX = this.cols[this.visibleRange.c1].left - this.cellsLeft - diffWidth; offsetY = this.rows[this.visibleRange.r1].top - this.cellsTop - diffHeight; this._drawGrid( /*drawingCtx*/ undefined, range ); this._drawCellsAndBorders( /*drawingCtx*/undefined, range ); this._drawAutoF( range, offsetX, offsetY ); this.objectRender.showDrawingObjectsEx( false, new GraphicOption( this, c_oAscGraphicOption.ScrollVertical, range, { offsetX: offsetX, offsetY: offsetY } ) ); if ( 0 < cFrozen ) { range.c1 = 0; range.c2 = cFrozen - 1; offsetX = this.cols[0].left - this.cellsLeft; this._drawGrid( /*drawingCtx*/ undefined, range, offsetX ); this._drawCellsAndBorders( /*drawingCtx*/undefined, range, offsetX ); this._drawAutoF( range, offsetX, offsetY ); this.objectRender.showDrawingObjectsEx( false, new GraphicOption( this, c_oAscGraphicOption.ScrollVertical, range, { offsetX: offsetX, offsetY: offsetY } ) ); } // Отрисовывать нужно всегда, вдруг бордеры this._drawFrozenPaneLines(); this._fixSelectionOfMergedCells(); this._drawSelection(); if ( widthChanged ) { this.handlers.trigger( "reinitializeScrollX" ); } } if ( reinitScrollY ) { this.handlers.trigger( "reinitializeScrollY" ); } this.handlers.trigger( "onDocumentPlaceChanged" ); //ToDo this.drawDepCells(); this.cellCommentator.updateCommentPosition(); this.cellCommentator.drawCommentCells(); return this; }; WorksheetView.prototype.scrollHorizontal = function ( delta, editor ) { var vr = this.visibleRange; var start = this._calcCellPosition( vr.c1, vr.r1, delta, 0 ).col; var fixStartCol = new asc_Range( start, vr.r1, start, vr.r2 ); fixStartCol.startCol = start; fixStartCol.startRow = vr.r1; this._fixSelectionOfHiddenCells( delta >= 0 ? +1 : -1, 0, fixStartCol ); this._fixVisibleRange( fixStartCol ); var reinitScrollX = start !== fixStartCol.c1; if ( reinitScrollX && 0 > delta ) // Для скролла влево обычный сдвиг + дорисовка { delta += fixStartCol.c1 - start; } start = fixStartCol.c1; if ( start === vr.c1 ) { if ( reinitScrollX ) { this.handlers.trigger( "reinitializeScrollX" ); } return this; } this.cleanSelection(); this.cellCommentator.cleanSelectedComment(); var ctx = this.drawingCtx; var ctxW = ctx.getWidth(); var ctxH = ctx.getHeight(); var dx = this.cols[start].left - this.cols[vr.c1].left; var oldStart = vr.c1; var oldEnd = vr.c2; var offsetX, offsetY, diffWidth = 0, diffHeight = 0; var oldW = ctxW - this.cellsLeft - Math.abs( dx ); var scrollRight = (dx > 0 && oldW > 0); var x = this.cellsLeft + (scrollRight ? dx : 0); var y = this.headersTop; var cFrozen = 0, rFrozen = 0; if ( this.topLeftFrozenCell ) { rFrozen = this.topLeftFrozenCell.getRow0(); cFrozen = this.topLeftFrozenCell.getCol0(); diffWidth = this.cols[cFrozen].left - this.cols[0].left; diffHeight = this.rows[rFrozen].top - this.rows[0].top; x += diffWidth; oldW -= diffWidth; } var oldVCE_isPartial = this._isColDrawnPartially( vr.c2, vr.c1, diffWidth ); var oldVR = vr.clone(); // ToDo стоит тут переделать весь scroll vr.c1 = start; this._updateVisibleColsCount(); // Это необходимо для того, чтобы строки, у которых высота по тексту, рассчитались if ( !oldVR.intersectionSimple( vr ) ) { // Полностью обновилась область this._prepareCellTextMetricsCache( vr ); } else { if ( 0 > delta ) { // Идем влево this._prepareCellTextMetricsCache( new asc_Range( start, vr.r1, oldStart - 1, vr.r2 ) ); } else { // Идем вправо this._prepareCellTextMetricsCache( new asc_Range( oldEnd + 1, vr.r1, vr.c2, vr.r2 ) ); } } var lastColWidth = (scrollRight && oldVCE_isPartial) ? ctxW - (this.cols[oldEnd].left - this.cols[oldStart].left + this.cellsLeft + diffWidth) : 0; if ( this.isCellEditMode && editor && this.activeRange.c1 >= cFrozen ) { editor.move( this.cellsLeft + diffWidth, this.cellsTop + (this.activeRange.r1 >= rFrozen ? diffHeight : 0), ctxW, ctxH ); } // Перемещаем область var moveWidth = oldW - lastColWidth; if ( moveWidth > 0 ) { ctx.drawImage( ctx.getCanvas(), x, y, moveWidth, ctxH, x - dx, y, moveWidth, ctxH ); // Заглушка для safari (http://bugzserver/show_bug.cgi?id=25546). Режим 'copy' сначала затирает, а // потом рисует (а т.к. мы рисуем сами на себе, то уже картинка будет пустой) if ( AscBrowser.isSafari ) { this.drawingGraphicCtx.moveImageDataSafari( x, y, moveWidth, ctxH, x - dx, y ); } else { this.drawingGraphicCtx.moveImageData( x, y, moveWidth, ctxH, x - dx, y ); } } // Очищаем область var clearLeft = this.cellsLeft + diffWidth + (scrollRight && moveWidth > 0 ? moveWidth : 0); var clearWidth = (moveWidth > 0) ? Math.abs( dx ) + lastColWidth : ctxW - (this.cellsLeft + diffWidth); ctx.setFillStyle( this.settings.cells.defaultState.background ) .fillRect( clearLeft, y, clearWidth, ctxH ); this.drawingGraphicCtx.clearRect( clearLeft, y, clearWidth, ctxH ); if ( this.objectRender && this.objectRender.drawingArea ) { this.objectRender.drawingArea.reinitRanges(); } // Дорисовываем необходимое if ( dx < 0 || vr.c2 !== oldEnd || oldVCE_isPartial ) { var c1, c2; if ( moveWidth > 0 ) { if ( scrollRight ) { c1 = oldEnd + (oldVCE_isPartial ? 0 : 1); c2 = vr.c2; } else { c1 = vr.c1; c2 = vr.c1 - 1 - delta; } } else { c1 = vr.c1; c2 = vr.c2; } var range = new asc_Range( c1, vr.r1, c2, vr.r2 ); offsetX = this.cols[this.visibleRange.c1].left - this.cellsLeft - diffWidth; offsetY = this.rows[this.visibleRange.r1].top - this.cellsTop - diffHeight; this._drawColumnHeaders( /*drawingCtx*/ undefined, c1, c2 ); this._drawGrid( /*drawingCtx*/ undefined, range ); this._drawCellsAndBorders( /*drawingCtx*/undefined, range ); this._drawAutoF( range, offsetX, offsetY ); this.objectRender.showDrawingObjectsEx( false, new GraphicOption( this, c_oAscGraphicOption.ScrollHorizontal, range, { offsetX: offsetX, offsetY: offsetY } ) ); if ( rFrozen ) { range.r1 = 0; range.r2 = rFrozen - 1; offsetY = this.rows[0].top - this.cellsTop; this._drawGrid( /*drawingCtx*/ undefined, range, undefined, offsetY ); this._drawCellsAndBorders( /*drawingCtx*/undefined, range, undefined, offsetY ); this._drawAutoF( range, offsetX, offsetY ); this.objectRender.showDrawingObjectsEx( false, new GraphicOption( this, c_oAscGraphicOption.ScrollHorizontal, range, { offsetX: offsetX, offsetY: offsetY } ) ); } // Отрисовывать нужно всегда, вдруг бордеры this._drawFrozenPaneLines(); this._fixSelectionOfMergedCells(); this._drawSelection(); } if ( reinitScrollX ) { this.handlers.trigger( "reinitializeScrollX" ); } this.handlers.trigger( "onDocumentPlaceChanged" ); //ToDo this.drawDepCells(); this.cellCommentator.updateCommentPosition(); this.cellCommentator.drawCommentCells(); return this; }; // ----- Selection ----- // x,y - абсолютные координаты относительно листа (без учета заголовков) WorksheetView.prototype.findCellByXY = function ( x, y, canReturnNull, skipCol, skipRow ) { var r = 0, c = 0, tmpRow, tmpCol, result = new CCellObjectInfo(); if ( canReturnNull ) { result.col = result.row = null; } x += this.cellsLeft; y += this.cellsTop; if ( !skipCol ) { while ( c < this.cols.length ) { tmpCol = this.cols[c]; if ( x <= tmpCol.left + tmpCol.width ) { result.col = c; break; } ++c; } if ( null !== result.col ) { result.colOff = x - this.cols[result.col].left; } } if ( !skipRow ) { while ( r < this.rows.length ) { tmpRow = this.rows[r]; if ( y <= tmpRow.top + tmpRow.height ) { result.row = r; break; } ++r; } if ( null !== result.row ) { result.rowOff = y - this.rows[result.row].top; } } return result; }; // dX = true - считать с половиной следующей ячейки WorksheetView.prototype._findColUnderCursor = function ( x, canReturnNull, dX ) { var c = this.visibleRange.c1, offset = this.cols[c].left - this.cellsLeft, c2, x1, x2, cFrozen, widthDiff = 0; if ( x >= this.cellsLeft ) { if ( this.topLeftFrozenCell ) { cFrozen = this.topLeftFrozenCell.getCol0(); widthDiff = this.cols[cFrozen].left - this.cols[0].left; if ( x < this.cellsLeft + widthDiff && 0 !== widthDiff ) { c = 0; widthDiff = 0; } } for ( x1 = this.cellsLeft + widthDiff, c2 = this.cols.length - 1; c <= c2; ++c, x1 = x2 ) { x2 = x1 + this.cols[c].width; if ( x1 <= x && x < x2 ) { if ( dX ) { // Учитываем половину ячейки if ( x1 <= x && x < x1 + this.cols[c].width / 2.0 ) { // Это предыдущая ячейка --c; // Можем вернуть и -1 (но это только для fillHandle) } } return {col: c, left: x1, right: x2}; } } if ( !canReturnNull ) { return {col: c2, left: this.cols[c2].left - offset, right: x2}; } } else { if ( this.topLeftFrozenCell ) { cFrozen = this.topLeftFrozenCell.getCol0(); if ( 0 !== cFrozen ) { c = 0; offset = this.cols[c].left - this.cellsLeft; } } for ( x2 = this.cellsLeft + this.cols[c].width, c2 = 0; c >= c2; --c, x2 = x1 ) { x1 = this.cols[c].left - offset; if ( x1 <= x && x < x2 ) { if ( dX ) { // Учитываем половину ячейки if ( x1 <= x && x < x1 + this.cols[c].width / 2.0 ) { // Это предыдущая ячейка --c; // Можем вернуть и -1 (но это только для fillHandle) } } return {col: c, left: x1, right: x2}; } } if ( !canReturnNull ) { if ( dX ) { // Это предыдущая ячейка --c2; // Можем вернуть и -1 (но это только для fillHandle) return {col: c2}; } return {col: c2, left: x1, right: x1 + this.cols[c2].width}; } } return null; }; // dY = true - считать с половиной следующей ячейки WorksheetView.prototype._findRowUnderCursor = function ( y, canReturnNull, dY ) { var r = this.visibleRange.r1, offset = this.rows[r].top - this.cellsTop, r2, y1, y2, rFrozen, heightDiff = 0; if ( y >= this.cellsTop ) { if ( this.topLeftFrozenCell ) { rFrozen = this.topLeftFrozenCell.getRow0(); heightDiff = this.rows[rFrozen].top - this.rows[0].top; if ( y < this.cellsTop + heightDiff && 0 !== heightDiff ) { r = 0; heightDiff = 0; } } for ( y1 = this.cellsTop + heightDiff, r2 = this.rows.length - 1; r <= r2; ++r, y1 = y2 ) { y2 = y1 + this.rows[r].height; if ( y1 <= y && y < y2 ) { if ( dY ) { // Учитываем половину ячейки if ( y1 <= y && y < y1 + this.rows[r].height / 2.0 ) { // Это предыдущая ячейка --r; // Можем вернуть и -1 (но это только для fillHandle) } } return {row: r, top: y1, bottom: y2}; } } if ( !canReturnNull ) { return {row: r2, top: this.rows[r2].top - offset, bottom: y2}; } } else { if ( this.topLeftFrozenCell ) { rFrozen = this.topLeftFrozenCell.getRow0(); if ( 0 !== rFrozen ) { r = 0; offset = this.rows[r].top - this.cellsTop; } } for ( y2 = this.cellsTop + this.rows[r].height, r2 = 0; r >= r2; --r, y2 = y1 ) { y1 = this.rows[r].top - offset; if ( y1 <= y && y < y2 ) { if ( dY ) { // Учитываем половину ячейки if ( y1 <= y && y < y1 + this.rows[r].height / 2.0 ) { // Это предыдущая ячейка --r; // Можем вернуть и -1 (но это только для fillHandle) } } return {row: r, top: y1, bottom: y2}; } } if ( !canReturnNull ) { if ( dY ) { // Это предыдущая ячейка --r2; // Можем вернуть и -1 (но это только для fillHandle) return {row: r2}; } return {row: r2, top: y1, bottom: y1 + this.rows[r2].height}; } } return null; }; WorksheetView.prototype._getCursorFormulaOrChart = function ( vr, x, y, offsetX, offsetY ) { var i, l; var cursor, oFormulaRange, oFormulaRangeIn, xFormula1, xFormula2, yFormula1, yFormula2; var col = -1, row = -1; var arrRanges = this.isFormulaEditMode ? this.arrActiveFormulaRanges : this.arrActiveChartsRanges, targetArr = this.isFormulaEditMode ? 0 : -1; for ( i = 0, l = arrRanges.length; i < l; ++i ) { oFormulaRange = arrRanges[i].clone( true ); oFormulaRangeIn = oFormulaRange.intersectionSimple( vr ); if ( oFormulaRangeIn ) { xFormula1 = this.cols[oFormulaRangeIn.c1].left - offsetX; xFormula2 = this.cols[oFormulaRangeIn.c2].left + this.cols[oFormulaRangeIn.c2].width - offsetX; yFormula1 = this.rows[oFormulaRangeIn.r1].top - offsetY; yFormula2 = this.rows[oFormulaRangeIn.r2].top + this.rows[oFormulaRangeIn.r2].height - offsetY; if ( (x >= xFormula1 + 5 && x <= xFormula2 - 5) && ((y >= yFormula1 - this.height_2px && y <= yFormula1 + this.height_2px) || (y >= yFormula2 - this.height_2px && y <= yFormula2 + this.height_2px)) || (y >= yFormula1 + 5 && y <= yFormula2 - 5) && ((x >= xFormula1 - this.width_2px && x <= xFormula1 + this.width_2px) || (x >= xFormula2 - this.width_2px && x <= xFormula2 + this.width_2px)) ) { cursor = kCurMove; break; } else if ( x >= xFormula1 && x < xFormula1 + 5 && y >= yFormula1 && y < yFormula1 + 5 ) { cursor = kCurSEResize; col = oFormulaRange.c2; row = oFormulaRange.r2; break; } else if ( x > xFormula2 - 5 && x <= xFormula2 && y > yFormula2 - 5 && y <= yFormula2 ) { cursor = kCurSEResize; col = oFormulaRange.c1; row = oFormulaRange.r1; break; } else if ( x > xFormula2 - 5 && x <= xFormula2 && y >= yFormula1 && y < yFormula1 + 5 ) { cursor = kCurNEResize; col = oFormulaRange.c1; row = oFormulaRange.r2; break; } else if ( x >= xFormula1 && x < xFormula1 + 5 && y > yFormula2 - 5 && y <= yFormula2 ) { cursor = kCurNEResize; col = oFormulaRange.c2; row = oFormulaRange.r1; break; } } } return cursor ? { cursor : cursor, target : c_oTargetType.MoveResizeRange, col : col, row : row, formulaRange : oFormulaRange, indexFormulaRange: i, targetArr : targetArr } : null; }; WorksheetView.prototype._isCursorOnSelectionBorder = function ( ar, vr, x, y ) { var arIntersection = ar.intersectionSimple( vr ); var left, top, right, bottom, wEps = this.width_2px, hEps = this.height_2px; if ( arIntersection ) { left = ar.c1 === arIntersection.c1 ? this.cols[ar.c1].left : null; right = ar.c2 === arIntersection.c2 ? this.cols[ar.c2].left + this.cols[ar.c2].width : null; top = ar.r1 === arIntersection.r1 ? this.rows[ar.r1].top : null; bottom = ar.r2 === arIntersection.r2 ? this.rows[ar.r2].top + this.rows[ar.r2].height : null; var isLeft = (null !== left && x >= left - wEps && x <= left + wEps), isRight = (null !== right && x >= right - wEps && x <= right + wEps), isTop = (null !== top && y >= top - hEps && y <= top + hEps), isBottom = (null !== bottom && y >= bottom - hEps && y <= bottom + hEps), isHorMiddle = ((null === left || x >= left - wEps) && (null === right || x <= right + wEps)), isVerMiddle = ((null === top || y >= top - hEps) && (null === bottom || y <= bottom + hEps)); if ( ((isLeft || isRight) && isVerMiddle) || ((isTop || isBottom) && isHorMiddle) ) { // Мы навели на границу выделения return true; } } return false; }; WorksheetView.prototype.getCursorTypeFromXY = function ( x, y, isViewerMode ) { var c, r, f, i, offsetX, offsetY, cellCursor, sheetId = this.model.getId(), userId, lockRangePosLeft, lockRangePosTop, lockInfo, oHyperlink, widthDiff = 0, heightDiff = 0, isLocked = false, ar = this.activeRange, target = c_oTargetType.Cells, row = -1, col = -1, isSelGraphicObject, isNotFirst; var frozenCursor = this._isFrozenAnchor( x, y ); if ( !isViewerMode && frozenCursor.result ) { lockInfo = this.collaborativeEditing.getLockInfo( c_oAscLockTypeElem.Object, null, sheetId, c_oAscLockNameFrozenPane ); isLocked = this.collaborativeEditing.getLockIntersection( lockInfo, c_oAscLockTypes.kLockTypeOther, false ); if ( false !== isLocked ) { // Кто-то сделал lock var frozenCell = this.topLeftFrozenCell ? this.topLeftFrozenCell : new CellAddress( 0, 0, 0 ); userId = isLocked.UserId; lockRangePosLeft = this.getCellLeft( frozenCell.getCol0(), 0 ); lockRangePosTop = this.getCellTop( frozenCell.getRow0(), 0 ); } return { cursor : frozenCursor.cursor, target : frozenCursor.name, col : -1, row : -1, userId : userId, lockRangePosLeft: lockRangePosLeft, lockRangePosTop : lockRangePosTop }; } var drawingInfo = this.objectRender.checkCursorDrawingObject( x, y ); if ( asc["editor"].isStartAddShape && CheckIdSatetShapeAdd( this.objectRender.controller.curState ) ) { return {cursor: kCurFillHandle, target: c_oTargetType.Shape, col: -1, row: -1}; } if ( drawingInfo && drawingInfo.id ) { // Возможно картинка с lock lockInfo = this.collaborativeEditing.getLockInfo( c_oAscLockTypeElem.Object, null, sheetId, drawingInfo.id ); isLocked = this.collaborativeEditing.getLockIntersection( lockInfo, c_oAscLockTypes.kLockTypeOther, false ); if ( false !== isLocked ) { // Кто-то сделал lock userId = isLocked.UserId; lockRangePosLeft = drawingInfo.object.getVisibleLeftOffset( true ); lockRangePosTop = drawingInfo.object.getVisibleTopOffset( true ); } if ( drawingInfo.hyperlink instanceof ParaHyperlink ) { oHyperlink = new Hyperlink(); oHyperlink.Tooltip = drawingInfo.hyperlink.ToolTip; var spl = drawingInfo.hyperlink.Value.split( "!" ); if ( spl.length === 2 ) { oHyperlink.setLocation( drawingInfo.hyperlink.Value ); } else { oHyperlink.Hyperlink = drawingInfo.hyperlink.Value; } cellCursor = {cursor: drawingInfo.cursor, target: c_oTargetType.Cells, col: -1, row: -1, userId: userId}; return { cursor : kCurHyperlink, target : c_oTargetType.Hyperlink, hyperlink : new asc_CHyperlink( oHyperlink ), cellCursor: cellCursor, userId : userId }; } return { cursor : drawingInfo.cursor, target : c_oTargetType.Shape, drawingId : drawingInfo.id, col : -1, row : -1, userId : userId, lockRangePosLeft: lockRangePosLeft, lockRangePosTop : lockRangePosTop }; } x *= asc_getcvt( 0/*px*/, 1/*pt*/, this._getPPIX() ); y *= asc_getcvt( 0/*px*/, 1/*pt*/, this._getPPIY() ); if ( this.stateFormatPainter ) { if ( x <= this.cellsLeft && y >= this.cellsTop ) { r = this._findRowUnderCursor( y, true ); if ( r !== null ) { target = c_oTargetType.RowHeader; row = r.row; } } if ( y <= this.cellsTop && x >= this.cellsLeft ) { c = this._findColUnderCursor( x, true ); if ( c !== null ) { target = c_oTargetType.ColumnHeader; col = c.col; } } return {cursor: kCurFormatPainterExcel, target: target, col: col, row: row}; } var oResDefault = {cursor: kCurDefault, target: c_oTargetType.None, col: -1, row: -1}; if ( x < this.cellsLeft && y < this.cellsTop ) { return {cursor: kCurCorner, target: c_oTargetType.Corner, col: -1, row: -1}; } var cFrozen = -1, rFrozen = -1; offsetX = this.cols[this.visibleRange.c1].left - this.cellsLeft; offsetY = this.rows[this.visibleRange.r1].top - this.cellsTop; if ( this.topLeftFrozenCell ) { cFrozen = this.topLeftFrozenCell.getCol0(); rFrozen = this.topLeftFrozenCell.getRow0(); widthDiff = this.cols[cFrozen].left - this.cols[0].left; heightDiff = this.rows[rFrozen].top - this.rows[0].top; offsetX = (x < this.cellsLeft + widthDiff) ? 0 : offsetX - widthDiff; offsetY = (y < this.cellsTop + heightDiff) ? 0 : offsetY - heightDiff; } if ( x <= this.cellsLeft && y >= this.cellsTop ) { r = this._findRowUnderCursor( y, true ); if ( r === null ) { return oResDefault; } isNotFirst = (r.row !== (-1 !== rFrozen ? 0 : this.visibleRange.r1)); f = !isViewerMode && (isNotFirst && y < r.top + 3 || y >= r.bottom - 3); // ToDo В Excel зависимость epsilon от размера ячейки (у нас фиксированный 3) return { cursor: f ? kCurRowResize : kCurRowSelect, target: f ? c_oTargetType.RowResize : c_oTargetType.RowHeader, col : -1, row : r.row + (isNotFirst && f && y < r.top + 3 ? -1 : 0), mouseY: f ? ((y < r.top + 3) ? (r.top - y - this.height_1px) : (r.bottom - y - this.height_1px)) : null }; } if ( y <= this.cellsTop && x >= this.cellsLeft ) { c = this._findColUnderCursor( x, true ); if ( c === null ) { return oResDefault; } isNotFirst = c.col !== (-1 !== cFrozen ? 0 : this.visibleRange.c1); f = !isViewerMode && (isNotFirst && x < c.left + 3 || x >= c.right - 3); // ToDo В Excel зависимость epsilon от размера ячейки (у нас фиксированный 3) return { cursor: f ? kCurColResize : kCurColSelect, target: f ? c_oTargetType.ColumnResize : c_oTargetType.ColumnHeader, col : c.col + (isNotFirst && f && x < c.left + 3 ? -1 : 0), row : -1, mouseX: f ? ((x < c.left + 3) ? (c.left - x - this.width_1px) : (c.right - x - this.width_1px)) : null }; } if ( this.isFormulaEditMode || this.isChartAreaEditMode ) { var oFormulaOrChartCursor = this._getCursorFormulaOrChart( this.visibleRange, x, y, offsetX, offsetY ); if ( oFormulaOrChartCursor ) { return oFormulaOrChartCursor; } } var xWithOffset = x + offsetX; var yWithOffset = y + offsetY; isSelGraphicObject = this.objectRender.selectedGraphicObjectsExists(); if ( !isViewerMode && !isSelGraphicObject ) { // Эпсилон для fillHandle var fillHandleEpsilon = this.width_1px; if ( !this.isChartAreaEditMode && x >= (this.fillHandleL - fillHandleEpsilon) && x <= (this.fillHandleR + fillHandleEpsilon) && y >= (this.fillHandleT - fillHandleEpsilon) && y <= (this.fillHandleB + fillHandleEpsilon) ) { // Мы на "квадрате" для автозаполнения return {cursor: kCurFillHandle, target: c_oTargetType.FillHandle, col: -1, row: -1}; } // Навели на выделение (стоит вынести в отдельный метод) if ( this._isCursorOnSelectionBorder( ar, this.visibleRange, xWithOffset, yWithOffset ) ) { return {cursor: kCurMove, target: c_oTargetType.MoveRange, col: -1, row: -1}; } if ( this.topLeftFrozenCell ) { var oFrozenRange; cFrozen -= 1; rFrozen -= 1; if ( 0 <= cFrozen && 0 <= rFrozen ) { oFrozenRange = new asc_Range( 0, 0, cFrozen, rFrozen ); if ( this._isCursorOnSelectionBorder( ar, oFrozenRange, x, y ) ) { return {cursor: kCurMove, target: c_oTargetType.MoveRange, col: -1, row: -1}; } } if ( 0 <= cFrozen ) { oFrozenRange = new asc_Range( 0, this.visibleRange.r1, cFrozen, this.visibleRange.r2 ); if ( this._isCursorOnSelectionBorder( ar, oFrozenRange, x, yWithOffset ) ) { return {cursor: kCurMove, target: c_oTargetType.MoveRange, col: -1, row: -1}; } } if ( 0 <= rFrozen ) { oFrozenRange = new asc_Range( this.visibleRange.c1, 0, this.visibleRange.c2, rFrozen ); if ( this._isCursorOnSelectionBorder( ar, oFrozenRange, xWithOffset, y ) ) { return {cursor: kCurMove, target: c_oTargetType.MoveRange, col: -1, row: -1}; } } } } if ( x > this.cellsLeft && y > this.cellsTop ) { c = this._findColUnderCursor( x, true ); r = this._findRowUnderCursor( y, true ); if ( c === null || r === null ) { return oResDefault; } // Проверка на совместное редактирование var lockRange = undefined; var lockAllPosLeft = undefined; var lockAllPosTop = undefined; var userIdAllProps = undefined; var userIdAllSheet = undefined; if ( !isViewerMode && this.collaborativeEditing.getCollaborativeEditing() ) { var c1Recalc = null, r1Recalc = null; var selectRangeRecalc = new asc_Range( c.col, r.row, c.col, r.row ); // Пересчет для входящих ячеек в добавленные строки/столбцы var isIntersection = this._recalcRangeByInsertRowsAndColumns( sheetId, selectRangeRecalc ); if ( false === isIntersection ) { lockInfo = this.collaborativeEditing.getLockInfo( c_oAscLockTypeElem.Range, /*subType*/null, sheetId, new asc.asc_CCollaborativeRange( selectRangeRecalc.c1, selectRangeRecalc.r1, selectRangeRecalc.c2, selectRangeRecalc.r2 ) ); isLocked = this.collaborativeEditing.getLockIntersection( lockInfo, c_oAscLockTypes.kLockTypeOther, /*bCheckOnlyLockAll*/false ); if ( false !== isLocked ) { // Кто-то сделал lock userId = isLocked.UserId; lockRange = isLocked.Element["rangeOrObjectId"]; c1Recalc = this.collaborativeEditing.m_oRecalcIndexColumns[sheetId].getLockOther( lockRange["c1"], c_oAscLockTypes.kLockTypeOther ); r1Recalc = this.collaborativeEditing.m_oRecalcIndexRows[sheetId].getLockOther( lockRange["r1"], c_oAscLockTypes.kLockTypeOther ); if ( null !== c1Recalc && null !== r1Recalc ) { lockRangePosLeft = this.getCellLeft( c1Recalc, /*pt*/1 ); lockRangePosTop = this.getCellTop( r1Recalc, /*pt*/1 ); // Пересчитываем X и Y относительно видимой области lockRangePosLeft -= offsetX; lockRangePosTop -= offsetY; // Пересчитываем в px lockRangePosLeft *= asc_getcvt( 1/*pt*/, 0/*px*/, this._getPPIX() ); lockRangePosTop *= asc_getcvt( 1/*pt*/, 0/*px*/, this._getPPIY() ); } } } else { lockInfo = this.collaborativeEditing.getLockInfo( c_oAscLockTypeElem.Range, /*subType*/null, sheetId, null ); } // Проверим не удален ли весь лист (именно удален, т.к. если просто залочен, то не рисуем рамку вокруг) lockInfo["type"] = c_oAscLockTypeElem.Sheet; isLocked = this.collaborativeEditing.getLockIntersection( lockInfo, c_oAscLockTypes.kLockTypeOther, /*bCheckOnlyLockAll*/true ); if ( false !== isLocked ) { // Кто-то сделал lock userIdAllSheet = isLocked.UserId; lockAllPosLeft = this.cellsLeft * asc_getcvt( 1/*pt*/, 0/*px*/, this._getPPIX() ); lockAllPosTop = this.cellsTop * asc_getcvt( 1/*pt*/, 0/*px*/, this._getPPIY() ); } // Проверим не залочены ли все свойства листа (только если не удален весь лист) if ( undefined === userIdAllSheet ) { lockInfo["type"] = c_oAscLockTypeElem.Range; lockInfo["subType"] = c_oAscLockTypeElemSubType.InsertRows; isLocked = this.collaborativeEditing.getLockIntersection( lockInfo, c_oAscLockTypes.kLockTypeOther, /*bCheckOnlyLockAll*/true ); if ( false !== isLocked ) { // Кто-то сделал lock userIdAllProps = isLocked.UserId; lockAllPosLeft = this.cellsLeft * asc_getcvt( 1/*pt*/, 0/*px*/, this._getPPIX() ); lockAllPosTop = this.cellsTop * asc_getcvt( 1/*pt*/, 0/*px*/, this._getPPIY() ); } } } var autoFilterInfo = this.autoFilters.checkCursor( x, y, offsetX, offsetY, { cFrozen: cFrozen, rFrozen: rFrozen }, r, c ); if ( autoFilterInfo && !isViewerMode ) { return { cursor: kCurAutoFilter, target: c_oTargetType.FilterObject, col: -1, row: -1, idFilter: autoFilterInfo.id }; } // Проверим есть ли комменты var comments = this.cellCommentator.getComments( c.col, r.row ); var coords = undefined; var indexes = undefined; if ( 0 < comments.length ) { indexes = []; for ( i = 0; i < comments.length; ++i ) { indexes.push( comments[i].asc_getId() ); } coords = this.cellCommentator.getCommentsCoords( comments ); } // Проверим, может мы в гиперлинке oHyperlink = this.model.getHyperlinkByCell( r.row, c.col ); cellCursor = { cursor : kCurCells, target : c_oTargetType.Cells, col : (c ? c.col : -1), row : (r ? r.row : -1), userId : userId, lockRangePosLeft: lockRangePosLeft, lockRangePosTop : lockRangePosTop, userIdAllProps : userIdAllProps, lockAllPosLeft : lockAllPosLeft, lockAllPosTop : lockAllPosTop, userIdAllSheet : userIdAllSheet, commentIndexes : indexes, commentCoords : coords }; if ( null !== oHyperlink ) { return { cursor : kCurHyperlink, target : c_oTargetType.Hyperlink, hyperlink : new asc_CHyperlink( oHyperlink ), cellCursor : cellCursor, userId : userId, lockRangePosLeft: lockRangePosLeft, lockRangePosTop : lockRangePosTop, userIdAllProps : userIdAllProps, userIdAllSheet : userIdAllSheet, lockAllPosLeft : lockAllPosLeft, lockAllPosTop : lockAllPosTop, commentIndexes : indexes, commentCoords : coords }; } return cellCursor; } return oResDefault; }; WorksheetView.prototype._fixSelectionOfMergedCells = function ( fixedRange ) { var ar = fixedRange ? fixedRange : ((this.isFormulaEditMode) ? this.arrActiveFormulaRanges[this.arrActiveFormulaRangesPosition] : this.activeRange); if ( !ar ) { return; } if ( ar.type && ar.type !== c_oAscSelectionType.RangeCells ) { return; } // ToDo - переделать этот момент!!!! var res = this.model.expandRangeByMerged( ar.clone( true ) ); if ( ar.c1 !== res.c1 && ar.c1 !== res.c2 ) { ar.c1 = ar.c1 <= ar.c2 ? res.c1 : res.c2; } ar.c2 = ar.c1 === res.c1 ? res.c2 : (res.c1); if ( ar.r1 !== res.r1 && ar.r1 !== res.r2 ) { ar.r1 = ar.r1 <= ar.r2 ? res.r1 : res.r2; } ar.r2 = ar.r1 === res.r1 ? res.r2 : res.r1; ar.normalize(); }; WorksheetView.prototype._findVisibleCol = function ( from, dc, flag ) { var to = dc < 0 ? -1 : this.cols.length, c; for ( c = from; c !== to; c += dc ) { if ( this.cols[c].width > this.width_1px ) { return c; } } return flag ? -1 : this._findVisibleCol( from, dc * -1, true ); }; WorksheetView.prototype._findVisibleRow = function ( from, dr, flag ) { var to = dr < 0 ? -1 : this.rows.length, r; for ( r = from; r !== to; r += dr ) { if ( this.rows[r].height > this.height_1px ) { return r; } } return flag ? -1 : this._findVisibleRow( from, dr * -1, true ); }; WorksheetView.prototype._fixSelectionOfHiddenCells = function ( dc, dr, range ) { var ar = (range) ? range : this.activeRange, c1, c2, r1, r2, mc, i, arn = ar.clone( true ); if ( dc === undefined ) { dc = +1; } if ( dr === undefined ) { dr = +1; } if ( ar.c2 === ar.c1 ) { if ( this.cols[ar.c1].width < this.width_1px ) { c1 = c2 = this._findVisibleCol( ar.c1, dc ); } } else { if ( 0 !== dc && this.nColsCount > ar.c2 && this.cols[ar.c2].width < this.width_1px ) { // Проверка для одновременно замерженных и скрытых ячеек (A1:C1 merge, B:C hidden) for ( mc = null, i = arn.r1; i <= arn.r2; ++i ) { mc = this.model.getMergedByCell( i, ar.c2 ); if ( mc ) { break; } } if ( !mc ) { c2 = this._findVisibleCol( ar.c2, dc ); } } } if ( c1 < 0 || c2 < 0 ) { throw "Error: all columns are hidden"; } if ( ar.r2 === ar.r1 ) { if ( this.rows[ar.r1].height < this.height_1px ) { r1 = r2 = this._findVisibleRow( ar.r1, dr ); } } else { if ( 0 !== dr && this.nRowsCount > ar.r2 && this.rows[ar.r2].height < this.height_1px ) { //Проверка для одновременно замерженных и скрытых ячеек (A1:A3 merge, 2:3 hidden) for ( mc = null, i = arn.c1; i <= arn.c2; ++i ) { mc = this.model.getMergedByCell( ar.r2, i ); if ( mc ) { break; } } if ( !mc ) { r2 = this._findVisibleRow( ar.r2, dr ); } } } if ( r1 < 0 || r2 < 0 ) { throw "Error: all rows are hidden"; } ar.assign( c1 !== undefined ? c1 : ar.c1, r1 !== undefined ? r1 : ar.r1, c2 !== undefined ? c2 : ar.c2, r2 !== undefined ? r2 : ar.r2 ); // ToDo возможно это избыточные проверки if ( c1 >= 0 ) { ar.startCol = c1; } if ( r1 >= 0 ) { ar.startRow = r1; } if ( 0 !== dc && this.cols[ar.startCol].width < this.width_1px ) { c1 = this._findVisibleCol( ar.startCol, dc ); if ( c1 >= 0 ) { ar.startCol = c1; } } if ( 0 !== dr && this.rows[ar.startRow].height < this.height_1px ) { r1 = this._findVisibleRow( ar.startRow, dr ); if ( r1 >= 0 ) { ar.startRow = r1; } } }; WorksheetView.prototype._moveActiveCellToXY = function ( x, y ) { var c, r; var ar = (this.isFormulaEditMode) ? this.arrActiveFormulaRanges[this.arrActiveFormulaRangesPosition] : this.activeRange; x *= asc_getcvt( 0/*px*/, 1/*pt*/, this._getPPIX() ); y *= asc_getcvt( 0/*px*/, 1/*pt*/, this._getPPIY() ); if ( x < this.cellsLeft && y < this.cellsTop ) { ar.assign( 0, 0, this.cols.length - 1, this.rows.length - 1 ); ar.type = c_oAscSelectionType.RangeMax; ar.startCol = 0; ar.startRow = 0; this._fixSelectionOfHiddenCells(); } else if ( x < this.cellsLeft ) { r = this._findRowUnderCursor( y ).row; ar.assign( 0, r, this.cols.length - 1, r ); ar.type = c_oAscSelectionType.RangeRow; ar.startCol = 0; ar.startRow = r; this._fixSelectionOfHiddenCells(); } else if ( y < this.cellsTop ) { c = this._findColUnderCursor( x ).col; ar.assign( c, 0, c, this.rows.length - 1 ); ar.type = c_oAscSelectionType.RangeCol; ar.startCol = c; ar.startRow = 0; this._fixSelectionOfHiddenCells(); } else { c = this._findColUnderCursor( x ).col; r = this._findRowUnderCursor( y ).row; ar.assign( c, r, c, r ); ar.startCol = c; ar.startRow = r; ar.type = c_oAscSelectionType.RangeCells; this._fixSelectionOfMergedCells(); } }; WorksheetView.prototype._moveActiveCellToOffset = function ( dc, dr ) { var ar = (this.isFormulaEditMode) ? this.arrActiveFormulaRanges[this.arrActiveFormulaRangesPosition] : this.activeRange; var mc = this.model.getMergedByCell( ar.startRow, ar.startCol ); var c = mc ? ( dc < 0 ? mc.c1 : dc > 0 ? Math.min( mc.c2, this.nColsCount - 1 - dc ) : ar.startCol) : ar.startCol; var r = mc ? ( dr < 0 ? mc.r1 : dr > 0 ? Math.min( mc.r2, this.nRowsCount - 1 - dr ) : ar.startRow ) : ar.startRow; var p = this._calcCellPosition( c, r, dc, dr ); ar.assign( p.col, p.row, p.col, p.row ); ar.type = c_oAscSelectionType.RangeCells; ar.startCol = p.col; ar.startRow = p.row; this._fixSelectionOfMergedCells(); this._fixSelectionOfHiddenCells( dc >= 0 ? +1 : -1, dr >= 0 ? +1 : -1 ); }; // Движение активной ячейки в выделенной области WorksheetView.prototype._moveActivePointInSelection = function ( dc, dr ) { var ar = this.activeRange; var arn = this.activeRange.clone( true ); // Если мы на скрытой строке или ячейке, то двигаться в выделении нельзя (так делает и Excel) if ( this.width_1px > this.cols[ar.startCol].width || this.height_1px > this.rows[ar.startRow].height ) { return; } // Set active cell ar.startCol += dc; ar.startRow += dr; do { var done = true; // Обработка выхода за границы выделения if ( ar.startCol < arn.c1 ) { ar.startCol = arn.c2; ar.startRow -= 1; if ( ar.startRow < arn.r1 ) { ar.startRow = arn.r2; } } else if ( ar.startCol > arn.c2 ) { ar.startCol = arn.c1; ar.startRow += 1; if ( ar.startRow > arn.r2 ) { ar.startRow = arn.r1; } } if ( ar.startRow < arn.r1 ) { ar.startRow = arn.r2; ar.startCol -= 1; if ( ar.startCol < arn.c1 ) { ar.startCol = arn.c2; } } else if ( ar.startRow > arn.r2 ) { ar.startRow = arn.r1; ar.startCol += 1; if ( ar.startCol > arn.c2 ) { ar.startCol = arn.c1; } } // Обработка движения active point через merged cells var mc = this.model.getMergedByCell( ar.startRow, ar.startCol ); if ( mc ) { if ( dc > 0 && (ar.startCol > mc.c1 || ar.startRow !== mc.r1) ) { // Движение слева направо ar.startCol = mc.c2 + 1; done = false; } else if ( dc < 0 && (ar.startCol < mc.c2 || ar.startRow !== mc.r1) ) { // Движение справа налево ar.startCol = mc.c1 - 1; done = false; } if ( dr > 0 && (ar.startRow > mc.r1 || ar.startCol !== mc.c1) ) { // Движение сверху вниз ar.startRow = mc.r2 + 1; done = false; } else if ( dr < 0 && (ar.startRow < mc.r2 || ar.startCol !== mc.c1) ) { // Движение снизу вверх ar.startRow = mc.r1 - 1; done = false; } } if ( !done ) { continue; } // Обработка движения через скрытые столбцы/строки while ( ar.startCol >= arn.c1 && ar.startCol <= arn.c2 && this.cols[ar.startCol].width < this.width_1px ) { ar.startCol += dc || (dr > 0 ? +1 : -1); done = false; } if ( !done ) { continue; } while ( ar.startRow >= arn.r1 && ar.startRow <= arn.r2 && this.rows[ar.startRow].height < this.height_1px ) { ar.startRow += dr || (dc > 0 ? +1 : -1); done = false; } } while ( !done ); }; WorksheetView.prototype._calcSelectionEndPointByXY = function ( x, y ) { var ar = (this.isFormulaEditMode) ? this.arrActiveFormulaRanges[this.arrActiveFormulaRangesPosition] : this.activeRange; x *= asc_getcvt( 0/*px*/, 1/*pt*/, this._getPPIX() ); y *= asc_getcvt( 0/*px*/, 1/*pt*/, this._getPPIY() ); var res = new asc_Range( ar.startCol, ar.startRow, this._findColUnderCursor( x ).col, this._findRowUnderCursor( y ).row, true ); if ( ar.type === c_oAscSelectionType.RangeCells ) { this._fixSelectionOfMergedCells( res ); } return res; /*return { c2: ar.type === c_oAscSelectionType.RangeCol || ar.type === c_oAscSelectionType.RangeCells ? this._findColUnderCursor(x).col : ar.c2, r2: ar.type === c_oAscSelectionType.RangeRow || ar.type === c_oAscSelectionType.RangeCells ? this._findRowUnderCursor(y).row : ar.r2 };*/ }; WorksheetView.prototype._calcSelectionEndPointByOffset = function ( dc, dr ) { var ar = (this.isFormulaEditMode) ? this.arrActiveFormulaRanges[this.arrActiveFormulaRangesPosition] : this.activeRange; var startCol = ar.startCol, startRow = ar.startRow; var c1, r1, c2, r2, tmp; tmp = asc.getEndValueRange( dc, startCol, ar.c1, ar.c2 ); c1 = tmp.x1; c2 = tmp.x2; tmp = asc.getEndValueRange( dr, startRow, ar.r1, ar.r2 ); r1 = tmp.x1; r2 = tmp.x2; var p1 = this._calcCellPosition( c2, r2, dc, dr ), p2; var res = new asc_Range( c1, r1, c2 = p1.col, r2 = p1.row, true ); dc = Math.sign( dc ); dr = Math.sign( dr ); if ( c_oAscSelectionType.RangeCells === ar.type ) { this._fixSelectionOfMergedCells( res ); while ( ar.isEqual( res ) ) { p2 = this._calcCellPosition( c2, r2, dc, dr ); res.assign( c1, r1, c2 = p2.col, r2 = p2.row, true ); this._fixSelectionOfMergedCells( res ); if ( p1.c2 === p2.c2 && p1.r2 === p2.r2 ) { break; } p1 = p2; } } var bIsHidden = false; if ( 0 !== dc && this.cols[c2].width < this.width_1px ) { c2 = this._findVisibleCol( c2, dc ); bIsHidden = true; } if ( 0 !== dr && this.rows[r2].height < this.height_1px ) { r2 = this._findVisibleRow( r2, dr ); bIsHidden = true; } if ( bIsHidden ) { res.assign( c1, r1, c2, r2, true ); } return res; }; WorksheetView.prototype._calcActiveRangeOffsetIsCoord = function ( x, y ) { var vr = this.visibleRange; var ar = (this.isFormulaEditMode) ? this.arrActiveFormulaRanges[this.arrActiveFormulaRangesPosition] : this.activeRange; if ( this.isFormulaEditMode ) { // Для формул нужно сделать ограничение по range (у нас хранится полный диапазон) if ( ar.c2 >= this.nColsCount || ar.r2 >= this.nRowsCount ) { ar = ar.clone( true ); ar.c2 = (ar.c2 >= this.nColsCount) ? this.nColsCount - 1 : ar.c2; ar.r2 = (ar.r2 >= this.nRowsCount) ? this.nRowsCount - 1 : ar.r2; } } x *= asc_getcvt( 0/*px*/, 1/*pt*/, this._getPPIX() ); y *= asc_getcvt( 0/*px*/, 1/*pt*/, this._getPPIY() ); var d = {}; if ( y <= this.cellsTop + this.height_2px /*+ offsetFrozen.offsetY*/ ) { d.deltaY = -1; } else if ( y >= this.drawingCtx.getHeight() - this.height_2px ) { d.deltaY = 1; } if ( x <= this.cellsLeft + this.width_2px /*+ offsetFrozen.offsetX*/ ) { d.deltaX = -1; } else if ( x >= this.drawingCtx.getWidth() - this.width_2px ) { d.deltaX = 1; } if ( ar.type === c_oAscSelectionType.RangeRow ) { d.deltaX = 0; } else if ( ar.type === c_oAscSelectionType.RangeCol ) { d.deltaY = 0; } else if ( ar.type === c_oAscSelectionType.RangeMax ) { d.deltaX = 0; d.deltaY = 0; } return d; }; WorksheetView.prototype._calcActiveRangeOffset = function () { var vr = this.visibleRange; var ar = (this.isFormulaEditMode) ? this.arrActiveFormulaRanges[this.arrActiveFormulaRangesPosition] : this.activeRange; if ( this.isFormulaEditMode ) { // Для формул нужно сделать ограничение по range (у нас хранится полный диапазон) if ( ar.c2 >= this.nColsCount || ar.r2 >= this.nRowsCount ) { ar = ar.clone( true ); ar.c2 = (ar.c2 >= this.nColsCount) ? this.nColsCount - 1 : ar.c2; ar.r2 = (ar.r2 >= this.nRowsCount) ? this.nRowsCount - 1 : ar.r2; } } var arn = ar.clone( true ); var isMC = this._isMergedCells( arn ); var adjustRight = ar.c2 >= vr.c2 || ar.c1 >= vr.c2 && isMC; var adjustBottom = ar.r2 >= vr.r2 || ar.r1 >= vr.r2 && isMC; var incX = ar.c1 < vr.c1 && isMC ? arn.c1 - vr.c1 : ar.c2 < vr.c1 ? ar.c2 - vr.c1 : 0; var incY = ar.r1 < vr.r1 && isMC ? arn.r1 - vr.r1 : ar.r2 < vr.r1 ? ar.r2 - vr.r1 : 0; var offsetFrozen = this.getFrozenPaneOffset(); if ( adjustRight ) { while ( this._isColDrawnPartially( isMC ? arn.c2 : ar.c2, vr.c1 + incX, offsetFrozen.offsetX ) ) { ++incX; } } if ( adjustBottom ) { while ( this._isRowDrawnPartially( isMC ? arn.r2 : ar.r2, vr.r1 + incY, offsetFrozen.offsetY ) ) { ++incY; } } return { deltaX: ar.type === c_oAscSelectionType.RangeCol || ar.type === c_oAscSelectionType.RangeCells ? incX : 0, deltaY: ar.type === c_oAscSelectionType.RangeRow || ar.type === c_oAscSelectionType.RangeCells ? incY : 0 }; }; /** * @param {ActiveRange} [range] * @returns {{deltaX: number, deltaY: number}} */ WorksheetView.prototype._calcActiveCellOffset = function ( range ) { var vr = this.visibleRange; var ar = range ? range : this.activeRange; var mc = this.model.getMergedByCell( ar.startRow, ar.startCol ); var startCol = mc ? mc.c1 : ar.startCol; var startRow = mc ? mc.r1 : ar.startRow; var incX = startCol < vr.c1 ? startCol - vr.c1 : 0; var incY = startRow < vr.r1 ? startRow - vr.r1 : 0; var offsetFrozen = this.getFrozenPaneOffset(); // adjustRight if ( startCol >= vr.c2 ) { while ( this._isColDrawnPartially( startCol, vr.c1 + incX, offsetFrozen.offsetX ) ) { ++incX; } } // adjustBottom if ( startRow >= vr.r2 ) { while ( this._isRowDrawnPartially( startRow, vr.r1 + incY, offsetFrozen.offsetY ) ) { ++incY; } } return { deltaX: ar.type === c_oAscSelectionType.RangeCol || ar.type === c_oAscSelectionType.RangeCells ? incX : 0, deltaY: ar.type === c_oAscSelectionType.RangeRow || ar.type === c_oAscSelectionType.RangeCells ? incY : 0 }; }; WorksheetView.prototype._calcFillHandleOffset = function ( range ) { var vr = this.visibleRange; var ar = range ? range : this.activeFillHandle; var arn = ar.clone( true ); var isMC = this._isMergedCells( arn ); var adjustRight = ar.c2 >= vr.c2 || ar.c1 >= vr.c2 && isMC; var adjustBottom = ar.r2 >= vr.r2 || ar.r1 >= vr.r2 && isMC; var incX = ar.c1 < vr.c1 && isMC ? arn.c1 - vr.c1 : ar.c2 < vr.c1 ? ar.c2 - vr.c1 : 0; var incY = ar.r1 < vr.r1 && isMC ? arn.r1 - vr.r1 : ar.r2 < vr.r1 ? ar.r2 - vr.r1 : 0; var offsetFrozen = this.getFrozenPaneOffset(); if ( adjustRight ) { try { while ( this._isColDrawnPartially( isMC ? arn.c2 : ar.c2, vr.c1 + incX, offsetFrozen.offsetX ) ) { ++incX; } } catch( e ) { this.expandColsOnScroll( true ); this.handlers.trigger( "reinitializeScrollX" ); } } if ( adjustBottom ) { try { while ( this._isRowDrawnPartially( isMC ? arn.r2 : ar.r2, vr.r1 + incY, offsetFrozen.offsetY ) ) { ++incY; } } catch( e ) { this.expandRowsOnScroll( true ); this.handlers.trigger( "reinitializeScrollY" ); } } return { deltaX: incX, deltaY: incY }; }; // Потеряем ли мы что-то при merge ячеек WorksheetView.prototype.getSelectionMergeInfo = function ( options ) { var arn = this.activeRange.clone( true ); var notEmpty = false; var r, c; if ( this.cellCommentator.isMissComments( arn ) ) { return true; } switch ( options ) { case c_oAscMergeOptions.Merge: case c_oAscMergeOptions.MergeCenter: for ( r = arn.r1; r <= arn.r2; ++r ) { for ( c = arn.c1; c <= arn.c2; ++c ) { if ( false === this._isCellEmptyText( c, r ) ) { if ( notEmpty ) { return true; } notEmpty = true; } } } break; case c_oAscMergeOptions.MergeAcross: for ( r = arn.r1; r <= arn.r2; ++r ) { notEmpty = false; for ( c = arn.c1; c <= arn.c2; ++c ) { if ( false === this._isCellEmptyText( c, r ) ) { if ( notEmpty ) { return true; } notEmpty = true; } } } break; } return false; }; WorksheetView.prototype.getSelectionMathInfo = function () { var ar = this.activeRange; var range = this.model.getRange3( ar.r1, ar.c1, ar.r2, ar.c2 ); var tmp; var oSelectionMathInfo = new asc_CSelectionMathInfo(); var sum = 0; range._setPropertyNoEmpty( null, null, function ( c ) { if ( false === c.isEmptyTextString() ) { ++oSelectionMathInfo.count; if ( CellValueType.Number === c.getType() ) { tmp = parseFloat( c.getValueWithoutFormat() ); if ( isNaN( tmp ) ) { return; } if ( 0 === oSelectionMathInfo.countNumbers ) { oSelectionMathInfo.min = oSelectionMathInfo.max = tmp; } else { oSelectionMathInfo.min = Math.min( oSelectionMathInfo.min, tmp ); oSelectionMathInfo.max = Math.max( oSelectionMathInfo.max, tmp ); } ++oSelectionMathInfo.countNumbers; sum += tmp; } } } ); // Показываем только данные для 2-х или более ячеек (http://bugzserver/show_bug.cgi?id=24115) if ( 1 < oSelectionMathInfo.countNumbers ) { // Мы должны отдавать в формате активной ячейки var numFormat = range.getNumFormat(); if ( c_oAscNumFormatType.Time === numFormat.getType() ) { // Для времени нужно отдавать в формате [h]:mm:ss (http://bugzserver/show_bug.cgi?id=26271) numFormat = oNumFormatCache.get( '[h]:mm:ss' ); } oSelectionMathInfo.sum = numFormat.formatToMathInfo( sum, CellValueType.Number, this.settings.mathMaxDigCount ); oSelectionMathInfo.average = numFormat.formatToMathInfo( sum / oSelectionMathInfo.countNumbers, CellValueType.Number, this.settings.mathMaxDigCount ); oSelectionMathInfo.min = numFormat.formatToMathInfo( oSelectionMathInfo.min, CellValueType.Number, this.settings.mathMaxDigCount ); oSelectionMathInfo.max = numFormat.formatToMathInfo( oSelectionMathInfo.max, CellValueType.Number, this.settings.mathMaxDigCount ); } return oSelectionMathInfo; }; WorksheetView.prototype.getSelectionName = function ( bRangeText ) { if ( this.isSelectOnShape ) { return " "; } // Пока отправим пустое имя(с пробелом, пустое не воспринимаем в меню..) ToDo var ar = this.activeRange; var mc = this.model.getMergedByCell( ar.startRow, ar.startCol ); var c1 = mc ? mc.c1 : ar.startCol, r1 = mc ? mc.r1 : ar.startRow, ar_norm = ar.normalize(), mc_norm = mc ? mc.normalize() : null, c2 = mc_norm ? mc_norm.isEqual( ar_norm ) ? mc_norm.c1 : ar_norm.c2 : ar_norm.c2, r2 = mc_norm ? mc_norm.isEqual( ar_norm ) ? mc_norm.r1 : ar_norm.r2 : ar_norm.r2, selectionSize = !bRangeText ? "" : (function ( r ) { var rc = Math.abs( r.r2 - r.r1 ) + 1; var cc = Math.abs( r.c2 - r.c1 ) + 1; switch ( r.type ) { case c_oAscSelectionType.RangeCells: return rc + "R x " + cc + "C"; case c_oAscSelectionType.RangeCol: return cc + "C"; case c_oAscSelectionType.RangeRow: return rc + "R"; case c_oAscSelectionType.RangeMax: return gc_nMaxRow + "R x " + gc_nMaxCol + "C"; } return ""; })( ar ), cellName = this._getColumnTitle( c1 ) + this._getRowTitle( r1 ), defName = null, dN = new Asc.Range( ar_norm.c1, ar_norm.r1, c2, r2, true ).normalize(); /*if( c1==c2 && r1==r2 ){ defName = this.model.getName() + "!" + dN.getAbsName(); } else{ defName = this.model.getName() + "!" + dN.getAbsName(); }*/ defName = parserHelp.getEscapeSheetName( this.model.getName() ) + "!" + dN.getAbsName(); defName = defName ? this.model.workbook.findDefinesNames( defName, this.model.getId() ) : null; return selectionSize || defName || cellName; }; WorksheetView.prototype.getSelectionRangeValue = function () { // ToDo проблема с выбором целого столбца/строки var ar = this.activeRange.clone( true ); // ar.r1Abs = ar.c1Abs = ar.r2Abs = ar.c2Abs = true; var sName = ar.getAbsName(); return (c_oAscSelectionDialogType.FormatTable === this.selectionDialogType) ? sName : parserHelp.get3DRef( this.model.getName(), sName ); }; WorksheetView.prototype.getSelectionInfo = function ( bExt ) { return this.objectRender.selectedGraphicObjectsExists() ? this._getSelectionInfoObject( bExt ) : this._getSelectionInfoCell( bExt ); }; WorksheetView.prototype._getSelectionInfoCell = function ( bExt ) { var c_opt = this.settings.cells; var activeCell = this.activeRange; var mc = this.model.getMergedByCell( activeCell.startRow, activeCell.startCol ); var c1 = mc ? mc.c1 : activeCell.startCol; var r1 = mc ? mc.r1 : activeCell.startRow; var c = this._getVisibleCell( c1, r1 ); if ( c === undefined ) { asc_debug( "log", "Unknown cell's info: col = " + c1 + ", row = " + r1 ); return {}; } var fc = c.getFontcolor(); var bg = c.getFill(); var fa = c.getFontAlign().toLowerCase(); var cellType = c.getType(); var isNumberFormat = (!cellType || CellValueType.Number === cellType); var cell_info = new asc_CCellInfo(); cell_info.name = this._getColumnTitle( c1 ) + this._getRowTitle( r1 ); cell_info.formula = c.getFormula(); cell_info.text = c.getValueForEdit(); cell_info.halign = c.getAlignHorizontalByValue().toLowerCase(); cell_info.valign = c.getAlignVertical().toLowerCase(); var tablePartsOptions = this.autoFilters.searchRangeInTableParts( activeCell ); var curTablePart = tablePartsOptions >= 0 ? this.model.TableParts[tablePartsOptions] : null; cell_info.autoFilterInfo = new asc_CAutoFilterInfo(); cell_info.autoFilterInfo.tableStyleName = curTablePart ? curTablePart.TableStyleInfo.Name : null; cell_info.autoFilterInfo.tableName = curTablePart ? curTablePart.DisplayName : null; if ( -2 === tablePartsOptions ) { cell_info.autoFilterInfo.isAutoFilter = null; cell_info.autoFilterInfo.isApplyAutoFilter = false; } else { var checkApplyFilterOrSort = this.autoFilters.checkApplyFilterOrSort( tablePartsOptions ); cell_info.autoFilterInfo.isAutoFilter = checkApplyFilterOrSort.isAutoFilter; cell_info.autoFilterInfo.isApplyAutoFilter = checkApplyFilterOrSort.isFilterColumns; } cell_info.styleName = c.getStyleName(); cell_info.angle = c.getAngle(); cell_info.flags = new asc_CCellFlag(); cell_info.flags.shrinkToFit = c.getShrinkToFit(); cell_info.flags.wrapText = c.getWrap(); cell_info.flags.selectionType = this.activeRange.type; cell_info.flags.lockText = ("" !== cell_info.text && (isNumberFormat || "" !== cell_info.formula)); cell_info.font = new asc_CFont(); cell_info.font.name = c.getFontname(); cell_info.font.size = c.getFontsize(); cell_info.font.bold = c.getBold(); cell_info.font.italic = c.getItalic(); cell_info.font.underline = (Asc.EUnderline.underlineNone !== c.getUnderline()); // ToDo убрать, когда будет реализовано двойное подчеркивание cell_info.font.strikeout = c.getStrikeout(); cell_info.font.subscript = fa === "subscript"; cell_info.font.superscript = fa === "superscript"; cell_info.font.color = (fc ? asc_obj2Color( fc ) : new asc_CColor( c_opt.defaultState.color )); cell_info.fill = new asc_CFill( (null != bg) ? asc_obj2Color( bg ) : bg ); cell_info.numFormatType = c.getNumFormatType(); // Получаем гиперссылку var ar = this.activeRange.clone(); var range = this.model.getRange3( ar.r1, ar.c1, ar.r2, ar.c2 ); var hyperlink = range.getHyperlink(); var oHyperlink; if ( null !== hyperlink ) { // Гиперлинк oHyperlink = new asc_CHyperlink( hyperlink ); oHyperlink.asc_setText( cell_info.text ); cell_info.hyperlink = oHyperlink; } else { cell_info.hyperlink = null; } cell_info.comments = this.cellCommentator.getComments( ar.c1, ar.r1 ); cell_info.flags.merge = null !== range.hasMerged(); if ( bExt ) { cell_info.innertext = c.getValue(); cell_info.numFormat = c.getNumFormatStr(); } var sheetId = this.model.getId(); // Пересчет для входящих ячеек в добавленные строки/столбцы var isIntersection = this._recalcRangeByInsertRowsAndColumns( sheetId, ar ); if ( false === isIntersection ) { var lockInfo = this.collaborativeEditing.getLockInfo( c_oAscLockTypeElem.Range, /*subType*/null, sheetId, new asc.asc_CCollaborativeRange( ar.c1, ar.r1, ar.c2, ar.r2 ) ); if ( false !== this.collaborativeEditing.getLockIntersection( lockInfo, c_oAscLockTypes.kLockTypeOther, /*bCheckOnlyLockAll*/false ) ) { // Уже ячейку кто-то редактирует cell_info.isLocked = true; } } return cell_info; }; WorksheetView.prototype._getSelectionInfoObject = function () { var objectInfo = new asc_CCellInfo(); objectInfo.flags = new asc_CCellFlag(); var graphicObjects = this.objectRender.getSelectedGraphicObjects(); if ( graphicObjects.length ) { objectInfo.flags.selectionType = this.objectRender.getGraphicSelectionType( graphicObjects[0].Id ); } var textPr = this.objectRender.controller.getParagraphTextPr(); var theme = this.objectRender.controller.getTheme(); if ( textPr && theme && theme.themeElements && theme.themeElements.fontScheme ) { if ( textPr.FontFamily ) { textPr.FontFamily.Name = theme.themeElements.fontScheme.checkFont( textPr.FontFamily.Name ); } if ( textPr.RFonts ) { if ( textPr.RFonts.Ascii ) { textPr.RFonts.Ascii.Name = theme.themeElements.fontScheme.checkFont( textPr.RFonts.Ascii.Name ); } if ( textPr.RFonts.EastAsia ) { textPr.RFonts.EastAsia.Name = theme.themeElements.fontScheme.checkFont( textPr.RFonts.EastAsia.Name ); } if ( textPr.RFonts.HAnsi ) { textPr.RFonts.HAnsi.Name = theme.themeElements.fontScheme.checkFont( textPr.RFonts.HAnsi.Name ); } if ( textPr.RFonts.CS ) { textPr.RFonts.CS.Name = theme.themeElements.fontScheme.checkFont( textPr.RFonts.CS.Name ); } } } var paraPr = this.objectRender.controller.getParagraphParaPr(); if ( textPr && paraPr ) { objectInfo.text = this.objectRender.controller.Get_SelectedText( true ); var horAlign = "center"; switch ( paraPr.Jc ) { case align_Left : horAlign = "left"; break; case align_Right : horAlign = "right"; break; case align_Center : horAlign = "center"; break; case align_Justify : horAlign = "justify"; break; } var vertAlign = "center"; var shape_props = this.objectRender.controller.getDrawingProps().shapeProps; var angle = null; if ( shape_props ) { switch ( shape_props.verticalTextAlign ) { case VERTICAL_ANCHOR_TYPE_BOTTOM: vertAlign = "bottom"; break; case VERTICAL_ANCHOR_TYPE_CENTER: vertAlign = "center"; break; case VERTICAL_ANCHOR_TYPE_TOP: case VERTICAL_ANCHOR_TYPE_DISTRIBUTED: case VERTICAL_ANCHOR_TYPE_JUSTIFIED: vertAlign = "top"; break; } switch ( shape_props.vert ) { case nVertTTvert: angle = 90; break; case nVertTTvert270: angle = 270; break; default: angle = 0; break; } } objectInfo.halign = horAlign; objectInfo.valign = vertAlign; objectInfo.angle = angle; objectInfo.font = new asc_CFont(); objectInfo.font.name = textPr.FontFamily ? textPr.FontFamily.Name : ""; objectInfo.font.size = textPr.FontSize; objectInfo.font.bold = textPr.Bold; objectInfo.font.italic = textPr.Italic; objectInfo.font.underline = textPr.Underline; objectInfo.font.strikeout = textPr.Strikeout; objectInfo.font.subscript = textPr.VertAlign == vertalign_SubScript; objectInfo.font.superscript = textPr.VertAlign == vertalign_SuperScript; if ( textPr.Color ) { objectInfo.font.color = CreateAscColorCustom( textPr.Color.r, textPr.Color.g, textPr.Color.b ); } var shapeHyperlink = this.objectRender.controller.getHyperlinkInfo(); if ( shapeHyperlink && (shapeHyperlink instanceof ParaHyperlink) ) { var hyperlink = new Hyperlink(); hyperlink.Tooltip = shapeHyperlink.ToolTip; var spl = shapeHyperlink.Value.split( "!" ); if ( spl.length === 2 ) { hyperlink.setLocation( shapeHyperlink.Value ); } else { hyperlink.Hyperlink = shapeHyperlink.Value; } objectInfo.hyperlink = new asc_CHyperlink( hyperlink ); objectInfo.hyperlink.asc_setText( shapeHyperlink.Get_SelectedText( true, true ) ); } } else { // Может быть не задано текста, поэтому выставим по умолчанию objectInfo.font = new asc_CFont(); objectInfo.font.name = this.model.getDefaultFontName(); objectInfo.font.size = this.model.getDefaultFontSize(); } // Заливка не нужна как таковая objectInfo.fill = new asc_CFill( null ); // ToDo locks return objectInfo; }; // Получаем координаты активной ячейки WorksheetView.prototype.getActiveCellCoord = function () { var offsetX = 0, offsetY = 0; var vrCol = this.visibleRange.c1, vrRow = this.visibleRange.r1; if ( this.topLeftFrozenCell ) { var offsetFrozen = this.getFrozenPaneOffset(); var cFrozen = this.topLeftFrozenCell.getCol0(); var rFrozen = this.topLeftFrozenCell.getRow0(); if ( this.activeRange.startCol >= cFrozen ) { offsetX = offsetFrozen.offsetX; } else { vrCol = 0; } if ( this.activeRange.startRow >= rFrozen ) { offsetY = offsetFrozen.offsetY; } else { vrRow = 0; } } var xL = this.getCellLeft( this.activeRange.startCol, /*pt*/1 ); var yL = this.getCellTop( this.activeRange.startRow, /*pt*/1 ); // Пересчитываем X и Y относительно видимой области xL -= (this.cols[vrCol].left - this.cellsLeft); yL -= (this.rows[vrRow].top - this.cellsTop); // Пересчитываем X и Y относительно закрепленной области xL += offsetX; yL += offsetY; // Пересчитываем в px xL *= asc_getcvt( 1/*pt*/, 0/*px*/, this._getPPIX() ); yL *= asc_getcvt( 1/*pt*/, 0/*px*/, this._getPPIY() ); var width = this.getColumnWidth( this.activeRange.startCol, /*px*/0 ); var height = this.getRowHeight( this.activeRange.startRow, /*px*/0 ); if ( AscBrowser.isRetina ) { xL >>= 1; yL >>= 1; width >>= 1; height >>= 1; } return new asc_CRect( xL, yL, width, height ); }; WorksheetView.prototype._checkSelectionShape = function () { var isSelectOnShape = this.isSelectOnShape; if ( this.isSelectOnShape ) { this.isSelectOnShape = false; this.objectRender.unselectDrawingObjects(); } return isSelectOnShape; }; WorksheetView.prototype._updateSelectionNameAndInfo = function () { this.handlers.trigger( "selectionNameChanged", this.getSelectionName( /*bRangeText*/false ) ); this.handlers.trigger( "selectionChanged", this.getSelectionInfo() ); this.handlers.trigger( "selectionMathInfoChanged", this.getSelectionMathInfo() ); }; WorksheetView.prototype.getSelectionShape = function () { return this.isSelectOnShape; }; WorksheetView.prototype.setSelectionShape = function ( isSelectOnShape ) { this.isSelectOnShape = isSelectOnShape; // отправляем евент для получения свойств картинки, шейпа или группы this.model.workbook.handlers.trigger( "asc_onHideComment" ); this._updateSelectionNameAndInfo(); }; WorksheetView.prototype.getActiveRangeObj = function () { return this.activeRange.clone( true ); }; WorksheetView.prototype.setSelection = function ( range, validRange ) { // Проверка на валидность range. if ( validRange && (range.c2 >= this.nColsCount || range.r2 >= this.nRowsCount) ) { if ( range.c2 >= this.nColsCount ) { this.expandColsOnScroll( false, true, range.c2 + 1 ); } if ( range.r2 >= this.nRowsCount ) { this.expandRowsOnScroll( false, true, range.r2 + 1 ); } } this.cleanSelection(); // Проверка на всякий случай if ( !(range instanceof asc_Range) ) { range = new asc_Range( range.c1, range.r1, range.c2, range.r2 ); } if ( gc_nMaxCol0 === range.c2 || gc_nMaxRow0 === range.r2 ) { range = range.clone(); if ( gc_nMaxCol0 === range.c2 ) { range.c2 = this.cols.length - 1; } if ( gc_nMaxRow0 === range.r2 ) { range.r2 = this.rows.length - 1; } } this.activeRange = new asc_ActiveRange( range ); this.activeRange.type = c_oAscSelectionType.RangeCells; this.activeRange.startCol = range.c1; this.activeRange.startRow = range.r1; // Нормализуем range this.activeRange.normalize(); this._drawSelection(); this._updateSelectionNameAndInfo(); return this._calcActiveCellOffset(); }; WorksheetView.prototype.setSelectionUndoRedo = function ( range, validRange ) { var ar = (range instanceof asc_ActiveRange) ? range.clone() : new asc_ActiveRange( range ); // Проверка на валидность range. if ( validRange && (ar.c2 >= this.nColsCount || ar.r2 >= this.nRowsCount) ) { if ( ar.c2 >= this.nColsCount ) { this.expandColsOnScroll( false, true, ar.c2 + 1 ); } if ( ar.r2 >= this.nRowsCount ) { this.expandRowsOnScroll( false, true, ar.r2 + 1 ); } } var oRes = null; var type = ar.type; if ( type == c_oAscSelectionType.RangeCells || type == c_oAscSelectionType.RangeCol || type == c_oAscSelectionType.RangeRow || type == c_oAscSelectionType.RangeMax ) { this.cleanSelection(); this.activeRange = ar; this._drawSelection(); this._updateSelectionNameAndInfo(); oRes = this._calcActiveCellOffset(); } return oRes; }; WorksheetView.prototype.changeSelectionStartPoint = function ( x, y, isCoord, isSelectMode ) { var ar = ((this.isFormulaEditMode) ? this.arrActiveFormulaRanges[this.arrActiveFormulaRangesPosition] : this.activeRange).clone(); var ret = {}; var isChangeSelectionShape = false; this.cleanSelection(); var commentList = this.cellCommentator.getCommentsXY( x, y ); if ( !commentList.length ) { this.model.workbook.handlers.trigger( "asc_onHideComment" ); this.cellCommentator.resetLastSelectedId(); } if ( isCoord ) { // move active range to coordinates x,y this._moveActiveCellToXY( x, y ); isChangeSelectionShape = this._checkSelectionShape(); } else { // move active range to offset x,y this._moveActiveCellToOffset( x, y ); var x1 = this.getCellLeftRelative( this.activeRange.c1, /*pt*/0 ), y1 = this.getCellTopRelative( this.activeRange.r1, /*pt*/0 ); if ( isCoord ) { ret = this._calcActiveRangeOffsetIsCoord( x1, y1 ); } else { ret = this._calcActiveRangeOffset(); } } if ( this.isSelectionDialogMode ) { if ( !this.activeRange.isEqual( ar ) ) { // Смена диапазона this.handlers.trigger( "selectionRangeChanged", this.getSelectionRangeValue() ); } } else if ( !this.isCellEditMode ) { if ( isChangeSelectionShape || (!isCoord && !this.activeRange.isEqual( ar )) || (isCoord && (this.activeRange.startCol !== ar.startCol || this.activeRange.startRow !== ar.startRow)) ) { this.handlers.trigger( "selectionNameChanged", this.getSelectionName( /*bRangeText*/false ) ); if ( !isSelectMode ) { this.handlers.trigger( "selectionChanged", this.getSelectionInfo() ); this.handlers.trigger( "selectionMathInfoChanged", this.getSelectionMathInfo() ); } } } if ( !isChangeSelectionShape ) { this._drawSelection(); } //ToDo this.drawDepCells(); return ret; }; // Смена селекта по нажатию правой кнопки мыши WorksheetView.prototype.changeSelectionStartPointRightClick = function ( x, y ) { var ar = this.activeRange; var isChangeSelectionShape = this._checkSelectionShape(); this.model.workbook.handlers.trigger( "asc_onHideComment" ); // Получаем координаты левого верхнего угла выделения var xL = this.getCellLeft( ar.c1, /*pt*/1 ); var yL = this.getCellTop( ar.r1, /*pt*/1 ); // Получаем координаты правого нижнего угла выделения var xR = this.getCellLeft( ar.c2, /*pt*/1 ) + this.cols[ar.c2].width; var yR = this.getCellTop( ar.r2, /*pt*/1 ) + this.rows[ar.r2].height; // Пересчитываем координаты var _x = x * asc_getcvt( 0/*px*/, 1/*pt*/, this._getPPIX() ); var _y = y * asc_getcvt( 0/*px*/, 1/*pt*/, this._getPPIY() ); var isInSelection = false; var offsetX = this.cols[this.visibleRange.c1].left - this.cellsLeft, offsetY = this.rows[this.visibleRange.r1].top - this.cellsTop; var offsetFrozen = this.getFrozenPaneOffset(); offsetX -= offsetFrozen.offsetX; offsetY -= offsetFrozen.offsetY; // Проверяем попали ли мы в выделение if ( (_x < this.cellsLeft || _y < this.cellsTop) && c_oAscSelectionType.RangeMax === ar.type ) { // Выделено все isInSelection = true; } else if ( _x > this.cellsLeft && _y > this.cellsTop ) { // Пересчитываем X и Y относительно видимой области _x += offsetX; _y += offsetY; if ( xL <= _x && _x <= xR && yL <= _y && _y <= yR ) { // Попали в выделение ячеек isInSelection = true; } } else if ( _x <= this.cellsLeft && _y >= this.cellsTop && c_oAscSelectionType.RangeRow === ar.type ) { // Выделены строки // Пересчитываем Y относительно видимой области _y += offsetY; if ( yL <= _y && _y <= yR ) { // Попали в выделение ячеек isInSelection = true; } } else if ( _y <= this.cellsTop && _x >= this.cellsLeft && c_oAscSelectionType.RangeCol === ar.type ) { // Выделены столбцы // Пересчитываем X относительно видимой области _x += offsetX; if ( xL <= _x && _x <= xR ) { // Попали в выделение ячеек isInSelection = true; } } if ( !isInSelection ) { // Не попали в выделение (меняем первую точку) this.cleanSelection(); this._moveActiveCellToXY( x, y ); this._drawSelection(); this._updateSelectionNameAndInfo(); return false; } else if ( isChangeSelectionShape ) { // Попали в выделение, но были в объекте this.cleanSelection(); this._drawSelection(); this._updateSelectionNameAndInfo(); } return true; }; /** * * @param x - координата или прибавка к column * @param y - координата или прибавка к row * @param isCoord - выделение с помощью мышки (true) или с клавиатуры (false) * @param isSelectMode - при выделении с помощью мышки, не нужно отправлять эвенты о смене выделения и информации * @returns {*} */ WorksheetView.prototype.changeSelectionEndPoint = function ( x, y, isCoord, isSelectMode ) { var isChangeSelectionShape = false; if ( isCoord ) { isChangeSelectionShape = this._checkSelectionShape(); } var ar = (this.isFormulaEditMode) ? this.arrActiveFormulaRanges[this.arrActiveFormulaRangesPosition] : this.activeRange; var newRange = isCoord ? this._calcSelectionEndPointByXY( x, y ) : this._calcSelectionEndPointByOffset( x, y ); var isEqual = newRange.isEqual( ar ); if ( isEqual && !isCoord ) { // При движении стрелками можем попасть на замерженную ячейку } if ( !isEqual || isChangeSelectionShape ) { this.cleanSelection(); ar.assign2( newRange ); this._drawSelection(); //ToDo this.drawDepCells(); if ( !this.isCellEditMode ) { if ( !this.isSelectionDialogMode ) { this.handlers.trigger( "selectionNameChanged", this.getSelectionName( /*bRangeText*/true ) ); if ( !isSelectMode ) { this.handlers.trigger( "selectionChanged", this.getSelectionInfo( false ) ); this.handlers.trigger( "selectionMathInfoChanged", this.getSelectionMathInfo() ); } } else { // Смена диапазона this.handlers.trigger( "selectionRangeChanged", this.getSelectionRangeValue() ); } } } this.model.workbook.handlers.trigger( "asc_onHideComment" ); return isCoord ? this._calcActiveRangeOffsetIsCoord( x, y ) : this._calcActiveRangeOffset( this.getCellLeftRelative( x < 0 ? this.activeRange.c1 : this.activeRange.c2, /*pt*/0 ), this.getCellTopRelative( y < 0 ? this.activeRange.r1 : this.activeRange.r2, /*pt*/0 ) ); }; // Окончание выделения WorksheetView.prototype.changeSelectionDone = function () { if ( this.stateFormatPainter ) { this.applyFormatPainter(); } }; // Обработка движения в выделенной области WorksheetView.prototype.changeSelectionActivePoint = function ( dc, dr ) { var ret; var ar = this.activeRange; var mc = this.model.getMergedByCell( ar.r1, ar.c1 ); // Если в выделенной области только одна ячейка, то просто сдвигаемся if ( ar.c1 === ar.c2 && ar.r1 === ar.r2 || mc && ar.isEqual( mc ) ) { return this.changeSelectionStartPoint( dc, dr, /*isCoord*/false, /*isSelectMode*/false ); } // Очищаем выделение this.cleanSelection(); // Двигаемся по выделенной области this._moveActivePointInSelection( dc, dr ); // Перерисовываем this._drawSelection(); // Смотрим, ушли ли мы за границу видимой области ret = this._calcActiveCellOffset(); // Эвент обновления this.handlers.trigger( "selectionNameChanged", this.getSelectionName( /*bRangeText*/false ) ); this.handlers.trigger( "selectionChanged", this.getSelectionInfo() ); return ret; }; // ----- Changing cells ----- WorksheetView.prototype.applyFormatPainter = function () { var t = this; var from = t.copyActiveRange.getAllRange(), to = t.activeRange.getAllRange(); var oTmpRange = this._getRange( 0, 0, 0, 0 ); var onApplyFormatPainterCallback = function ( isSuccess ) { // Очищаем выделение t.cleanSelection(); if ( true === isSuccess ) { oTmpRange.promoteFromTo( from, to ); } t.expandColsOnScroll( false, true, to.c2 + 1 ); t.expandRowsOnScroll( false, true, to.r2 + 1 ); // Сбрасываем параметры t._updateCellsRange( to, /*canChangeColWidth*/c_oAscCanChangeColWidth.none, /*lockDraw*/true ); if ( c_oAscFormatPainterState.kMultiple !== t.stateFormatPainter ) { t.formatPainter(); } // Перерисовываем t._recalculateAfterUpdate( [to] ); }; var result = oTmpRange.preparePromoteFromTo( from, to ); if ( !result ) { // ToDo вывести ошибку onApplyFormatPainterCallback( false ); return; } this._isLockedCells( to, null, onApplyFormatPainterCallback ); }; WorksheetView.prototype.formatPainter = function ( stateFormatPainter ) { // Если передали состояние, то выставляем его. Если нет - то меняем на противоположное. this.stateFormatPainter = (null != stateFormatPainter) ? stateFormatPainter : ((c_oAscFormatPainterState.kOff !== this.stateFormatPainter) ? c_oAscFormatPainterState.kOff : c_oAscFormatPainterState.kOn); if ( this.stateFormatPainter ) { this.copyActiveRange = this.activeRange.clone( true ); this._drawFormatPainterRange(); } else { this.cleanSelection(); this.copyActiveRange = null; this._drawSelection(); this.handlers.trigger( "onStopFormatPainter" ); } }; /* Функция для работы автозаполнения (selection). (x, y) - координаты точки мыши на области */ WorksheetView.prototype.changeSelectionFillHandle = function ( x, y ) { // Возвращаемый результат var ret = null; // Если мы только первый раз попали сюда, то копируем выделенную область if ( null === this.activeFillHandle ) { this.activeFillHandle = this.activeRange.clone( true ); // Для первого раза нормализуем (т.е. первая точка - это левый верхний угол) this.activeFillHandle.normalize(); return ret; } // Пересчитываем координаты x *= asc_getcvt( 0/*px*/, 1/*pt*/, this._getPPIX() ); y *= asc_getcvt( 0/*px*/, 1/*pt*/, this._getPPIY() ); // Очищаем выделение, будем рисовать заново this.cleanSelection(); // Копируем выделенную область var ar = this.activeRange.clone( true ); // Получаем координаты левого верхнего угла выделения var xL = this.getCellLeft( ar.c1, /*pt*/1 ); var yL = this.getCellTop( ar.r1, /*pt*/1 ); // Получаем координаты правого нижнего угла выделения var xR = this.getCellLeft( ar.c2, /*pt*/1 ) + this.cols[ar.c2].width; var yR = this.getCellTop( ar.r2, /*pt*/1 ) + this.rows[ar.r2].height; // range для пересчета видимой области var activeFillHandleCopy; // Колонка по X и строка по Y var colByX = this._findColUnderCursor( x, /*canReturnNull*/false, /*dX*/true ).col; var rowByY = this._findRowUnderCursor( y, /*canReturnNull*/false, /*dX*/true ).row; // Колонка по X и строка по Y (без половинчатого счета). Для сдвига видимой области var colByXNoDX = this._findColUnderCursor( x, /*canReturnNull*/false, /*dX*/false ).col; var rowByYNoDY = this._findRowUnderCursor( y, /*canReturnNull*/false, /*dX*/false ).row; // Сдвиг в столбцах и строках от крайней точки var dCol; var dRow; // Пересчитываем X и Y относительно видимой области x += (this.cols[this.visibleRange.c1].left - this.cellsLeft); y += (this.rows[this.visibleRange.r1].top - this.cellsTop); // Вычисляем расстояние от (x, y) до (xL, yL) var dXL = x - xL; var dYL = y - yL; // Вычисляем расстояние от (x, y) до (xR, yR) var dXR = x - xR; var dYR = y - yR; var dXRMod; var dYRMod; // Определяем область попадания и точку /* (1) (2) (3) ------------|-----------------------|------------ | | (4) | (5) | (6) | | ------------|-----------------------|------------ (7) (8) (9) */ // Область точки (x, y) var _tmpArea = 0; if ( dXR <= 0 ) { // Области (1), (2), (4), (5), (7), (8) if ( dXL <= 0 ) { // Области (1), (4), (7) if ( dYR <= 0 ) { // Области (1), (4) if ( dYL <= 0 ) { // Область (1) _tmpArea = 1; } else { // Область (4) _tmpArea = 4; } } else { // Область (7) _tmpArea = 7; } } else { // Области (2), (5), (8) if ( dYR <= 0 ) { // Области (2), (5) if ( dYL <= 0 ) { // Область (2) _tmpArea = 2; } else { // Область (5) _tmpArea = 5; } } else { // Область (3) _tmpArea = 8; } } } else { // Области (3), (6), (9) if ( dYR <= 0 ) { // Области (3), (6) if ( dYL <= 0 ) { // Область (3) _tmpArea = 3; } else { // Область (6) _tmpArea = 6; } } else { // Область (9) _tmpArea = 9; } } // Проверяем, в каком направлении движение switch ( _tmpArea ) { case 2: case 8: // Двигаемся по вертикали. this.fillHandleDirection = 1; break; case 4: case 6: // Двигаемся по горизонтали. this.fillHandleDirection = 0; break; case 1: // Сравниваем расстояния от точки до левого верхнего угла выделения dXRMod = Math.abs( x - xL ); dYRMod = Math.abs( y - yL ); // Сдвиги по столбцам и строкам dCol = Math.abs( colByX - ar.c1 ); dRow = Math.abs( rowByY - ar.r1 ); // Определим направление позднее this.fillHandleDirection = -1; break; case 3: // Сравниваем расстояния от точки до правого верхнего угла выделения dXRMod = Math.abs( x - xR ); dYRMod = Math.abs( y - yL ); // Сдвиги по столбцам и строкам dCol = Math.abs( colByX - ar.c2 ); dRow = Math.abs( rowByY - ar.r1 ); // Определим направление позднее this.fillHandleDirection = -1; break; case 7: // Сравниваем расстояния от точки до левого нижнего угла выделения dXRMod = Math.abs( x - xL ); dYRMod = Math.abs( y - yR ); // Сдвиги по столбцам и строкам dCol = Math.abs( colByX - ar.c1 ); dRow = Math.abs( rowByY - ar.r2 ); // Определим направление позднее this.fillHandleDirection = -1; break; case 5: case 9: // Сравниваем расстояния от точки до правого нижнего угла выделения dXRMod = Math.abs( dXR ); dYRMod = Math.abs( dYR ); // Сдвиги по столбцам и строкам dCol = Math.abs( colByX - ar.c2 ); dRow = Math.abs( rowByY - ar.r2 ); // Определим направление позднее this.fillHandleDirection = -1; break; } //console.log(_tmpArea); // Возможно еще не определили направление if ( -1 === this.fillHandleDirection ) { // Проверим сдвиги по столбцам и строкам, если не поможет, то рассчитываем по расстоянию if ( 0 === dCol && 0 !== dRow ) { // Двигаемся по вертикали. this.fillHandleDirection = 1; } else if ( 0 !== dCol && 0 === dRow ) { // Двигаемся по горизонтали. this.fillHandleDirection = 0; } else if ( dXRMod >= dYRMod ) { // Двигаемся по горизонтали. this.fillHandleDirection = 0; } else { // Двигаемся по вертикали. this.fillHandleDirection = 1; } } // Проверяем, в каком направлении движение if ( 0 === this.fillHandleDirection ) { // Определяем область попадания и точку /* | | | | (1) | (2) | (3) | | | | */ if ( dXR <= 0 ) { // Область (1) или (2) if ( dXL <= 0 ) { // Область (1) this.fillHandleArea = 1; } else { // Область (2) this.fillHandleArea = 2; } } else { // Область (3) this.fillHandleArea = 3; } // Находим колонку для точки this.activeFillHandle.c2 = colByX; switch ( this.fillHandleArea ) { case 1: // Первая точка (xR, yR), вторая точка (x, yL) this.activeFillHandle.c1 = ar.c2; this.activeFillHandle.r1 = ar.r2; this.activeFillHandle.r2 = ar.r1; // Когда идем назад, должна быть колонка на 1 больше this.activeFillHandle.c2 += 1; // Случай, если мы еще не вышли из внутренней области if ( this.activeFillHandle.c2 == ar.c1 ) { this.fillHandleArea = 2; } break; case 2: // Первая точка (xR, yR), вторая точка (x, yL) this.activeFillHandle.c1 = ar.c2; this.activeFillHandle.r1 = ar.r2; this.activeFillHandle.r2 = ar.r1; // Когда идем назад, должна быть колонка на 1 больше this.activeFillHandle.c2 += 1; if ( this.activeFillHandle.c2 > this.activeFillHandle.c1 ) { // Ситуация половинки последнего столбца this.activeFillHandle.c1 = ar.c1; this.activeFillHandle.r1 = ar.r1; this.activeFillHandle.c2 = ar.c1; this.activeFillHandle.r2 = ar.r1; } break; case 3: // Первая точка (xL, yL), вторая точка (x, yR) this.activeFillHandle.c1 = ar.c1; this.activeFillHandle.r1 = ar.r1; this.activeFillHandle.r2 = ar.r2; break; } // Копируем в range для пересчета видимой области activeFillHandleCopy = this.activeFillHandle.clone(); activeFillHandleCopy.c2 = colByXNoDX; } else { // Определяем область попадания и точку /* (1) ____________________________ (2) ____________________________ (3) */ if ( dYR <= 0 ) { // Область (1) или (2) if ( dYL <= 0 ) { // Область (1) this.fillHandleArea = 1; } else { // Область (2) this.fillHandleArea = 2; } } else { // Область (3) this.fillHandleArea = 3; } // Находим строку для точки this.activeFillHandle.r2 = rowByY; switch ( this.fillHandleArea ) { case 1: // Первая точка (xR, yR), вторая точка (xL, y) this.activeFillHandle.c1 = ar.c2; this.activeFillHandle.r1 = ar.r2; this.activeFillHandle.c2 = ar.c1; // Когда идем назад, должна быть строка на 1 больше this.activeFillHandle.r2 += 1; // Случай, если мы еще не вышли из внутренней области if ( this.activeFillHandle.r2 == ar.r1 ) { this.fillHandleArea = 2; } break; case 2: // Первая точка (xR, yR), вторая точка (xL, y) this.activeFillHandle.c1 = ar.c2; this.activeFillHandle.r1 = ar.r2; this.activeFillHandle.c2 = ar.c1; // Когда идем назад, должна быть строка на 1 больше this.activeFillHandle.r2 += 1; if ( this.activeFillHandle.r2 > this.activeFillHandle.r1 ) { // Ситуация половинки последней строки this.activeFillHandle.c1 = ar.c1; this.activeFillHandle.r1 = ar.r1; this.activeFillHandle.c2 = ar.c1; this.activeFillHandle.r2 = ar.r1; } break; case 3: // Первая точка (xL, yL), вторая точка (xR, y) this.activeFillHandle.c1 = ar.c1; this.activeFillHandle.r1 = ar.r1; this.activeFillHandle.c2 = ar.c2; break; } // Копируем в range для пересчета видимой области activeFillHandleCopy = this.activeFillHandle.clone(); activeFillHandleCopy.r2 = rowByYNoDY; } //console.log ("row1: " + this.activeFillHandle.r1 + " col1: " + this.activeFillHandle.c1 + " row2: " + this.activeFillHandle.r2 + " col2: " + this.activeFillHandle.c2); // Перерисовываем this._drawSelection(); // Смотрим, ушли ли мы за границу видимой области ret = this._calcFillHandleOffset( activeFillHandleCopy ); this.model.workbook.handlers.trigger( "asc_onHideComment" ); return ret; }; /* Функция для применения автозаполнения */ WorksheetView.prototype.applyFillHandle = function ( x, y, ctrlPress ) { var t = this; // Текущее выделение (к нему применится автозаполнение) var arn = t.activeRange.clone( true ); arn.normalize(); var range = t.model.getRange3( arn.r1, arn.c1, arn.r2, arn.c2 ); // Были ли изменения var bIsHaveChanges = false; // Вычисляем индекс сдвига var nIndex = 0; /*nIndex*/ if ( 0 === this.fillHandleDirection ) { // Горизонтальное движение nIndex = this.activeFillHandle.c2 - arn.c1; if ( 2 === this.fillHandleArea ) { // Для внутренности нужно вычесть 1 из значения bIsHaveChanges = arn.c2 !== (this.activeFillHandle.c2 - 1); } else { bIsHaveChanges = arn.c2 !== this.activeFillHandle.c2; } } else { // Вертикальное движение nIndex = this.activeFillHandle.r2 - arn.r1; if ( 2 === this.fillHandleArea ) { // Для внутренности нужно вычесть 1 из значения bIsHaveChanges = arn.r2 !== (this.activeFillHandle.r2 - 1); } else { bIsHaveChanges = arn.r2 !== this.activeFillHandle.r2; } } // Меняли ли что-то if ( bIsHaveChanges && (this.activeFillHandle.r1 !== this.activeFillHandle.r2 || this.activeFillHandle.c1 !== this.activeFillHandle.c2) ) { // Диапазон ячеек, который мы будем менять var changedRange = this.activeRange.clone( true ); // Очищаем выделение this.cleanSelection(); if ( 2 === this.fillHandleArea ) { // Мы внутри, будет удаление, нормируем и cбрасываем первую ячейку this.activeRange.normalize(); // Проверяем, удалили ли мы все (если да, то область не меняется) if ( arn.c1 !== this.activeFillHandle.c2 || arn.r1 !== this.activeFillHandle.r2 ) { // Уменьшаем диапазон (мы удалили не все) if ( 0 === this.fillHandleDirection ) { // Горизонтальное движение (для внутренности необходимо вычесть 1) this.activeRange.c2 = this.activeFillHandle.c2 - 1; changedRange.c1 = changedRange.c2; changedRange.c2 = this.activeFillHandle.c2; } else { // Вертикальное движение (для внутренности необходимо вычесть 1) this.activeRange.r2 = this.activeFillHandle.r2 - 1; changedRange.r1 = changedRange.r2; changedRange.r2 = this.activeFillHandle.r2; } } } else { // Мы вне выделения. Увеличиваем диапазон if ( 0 === this.fillHandleDirection ) { // Горизонтальное движение if ( 1 === this.fillHandleArea ) { this.activeRange.c1 = this.activeFillHandle.c2; changedRange.c2 = changedRange.c1 - 1; changedRange.c1 = this.activeFillHandle.c2; } else { this.activeRange.c2 = this.activeFillHandle.c2; changedRange.c1 = changedRange.c2 + 1; changedRange.c2 = this.activeFillHandle.c2; } } else { // Вертикальное движение if ( 1 === this.fillHandleArea ) { this.activeRange.r1 = this.activeFillHandle.r2; changedRange.r2 = changedRange.r1 - 1; changedRange.r1 = this.activeFillHandle.r2; } else { this.activeRange.r2 = this.activeFillHandle.r2; changedRange.r1 = changedRange.r2 + 1; changedRange.r2 = this.activeFillHandle.r2; } } // После увеличения, нужно обновить больший range arn = this.activeRange.clone( true ); } changedRange.normalize(); var applyFillHandleCallback = function ( res ) { if ( res ) { // Автозаполняем ячейки if ( range.promote( /*bCtrl*/ctrlPress, /*bVertical*/(1 === t.fillHandleDirection), nIndex ) ) { // Вызываем функцию пересчета для заголовков форматированной таблицы t.autoFilters.renameTableColumn( arn ); } else { t.handlers.trigger( "onErrorEvent", c_oAscError.ID.CannotFillRange, c_oAscError.Level.NoCritical ); t.activeRange.assign2( range.bbox ); } } // Сбрасываем параметры автозаполнения t.activeFillHandle = null; t.fillHandleDirection = -1; // Обновляем выделенные ячейки t.isChanged = true; t._updateCellsRange( arn ); }; // Можно ли применять автозаполнение ? this._isLockedCells( changedRange, /*subType*/null, applyFillHandleCallback ); } else { // Ничего не менялось, сбрасываем выделение this.cleanSelection(); // Сбрасываем параметры автозаполнения this.activeFillHandle = null; this.fillHandleDirection = -1; // Перерисовываем this._drawSelection(); } }; /* Функция для работы перемещения диапазона (selection). (x, y) - координаты точки мыши на области * ToDo нужно переделать, чтобы moveRange появлялся только после сдвига от текущей ячейки */ WorksheetView.prototype.changeSelectionMoveRangeHandle = function ( x, y, ctrlKey ) { // Возвращаемый результат var ret = null; // Пересчитываем координаты x *= asc_getcvt( 0/*px*/, 1/*pt*/, this._getPPIX() ); y *= asc_getcvt( 0/*px*/, 1/*pt*/, this._getPPIY() ); //если выделена ячейка заголовка ф/т, меняем выделение с ячейки на столбец ф/т //если выделена вся видимая часть форматированной таблицы, но не выделены последние скрытые строчки if ( null === this.startCellMoveRange ) { this.autoFilters.changeSelectionTablePart( this.activeRange ); } var ar = this.activeRange.clone( true ); // Колонка по X и строка по Y var colByX = this._findColUnderCursor( x, /*canReturnNull*/false, /*dX*/false ).col; var rowByY = this._findRowUnderCursor( y, /*canReturnNull*/false, /*dY*/false ).row; if ( ar.type == c_oAscSelectionType.RangeRow ) { colByX = 0; } if ( ar.type == c_oAscSelectionType.RangeCol ) { rowByY = 0; } if ( ar.type == c_oAscSelectionType.RangeMax ) { colByX = 0; rowByY = 0; } // Если мы только первый раз попали сюда, то копируем выделенную область if ( null === this.startCellMoveRange ) { // Учитываем погрешность (мы должны быть внутри диапазона при старте) if ( colByX < ar.c1 ) { colByX = ar.c1; } else if ( colByX > ar.c2 ) { colByX = ar.c2; } if ( rowByY < ar.r1 ) { rowByY = ar.r1; } else if ( rowByY > ar.r2 ) { rowByY = ar.r2; } this.startCellMoveRange = new asc_Range( colByX, rowByY, colByX, rowByY ); this.startCellMoveRange.isChanged = false; // Флаг, сдвигались ли мы от первоначального диапазона return ret; } // Разница, на сколько мы сдвинулись var colDelta = colByX - this.startCellMoveRange.c1; var rowDelta = rowByY - this.startCellMoveRange.r1; // Проверяем, нужно ли отрисовывать перемещение (сдвигались или нет) if ( false === this.startCellMoveRange.isChanged && 0 === colDelta && 0 === rowDelta ) { return ret; } // Выставляем флаг this.startCellMoveRange.isChanged = true; // Очищаем выделение, будем рисовать заново this.cleanSelection(); this.activeMoveRange = ar; // Для первого раза нормализуем (т.е. первая точка - это левый верхний угол) this.activeMoveRange.normalize(); // Выставляем this.activeMoveRange.c1 += colDelta; if ( 0 > this.activeMoveRange.c1 ) { colDelta -= this.activeMoveRange.c1; this.activeMoveRange.c1 = 0; } this.activeMoveRange.c2 += colDelta; this.activeMoveRange.r1 += rowDelta; if ( 0 > this.activeMoveRange.r1 ) { rowDelta -= this.activeMoveRange.r1; this.activeMoveRange.r1 = 0; } this.activeMoveRange.r2 += rowDelta; this.activeMoveRange._updateAdditionalData(); // Увеличиваем, если выходим за область видимости // Critical Bug 17413 while ( !this.cols[this.activeMoveRange.c2] ) { this.expandColsOnScroll( true ); this.handlers.trigger( "reinitializeScrollX" ); } while ( !this.rows[this.activeMoveRange.r2] ) { this.expandRowsOnScroll( true ); this.handlers.trigger( "reinitializeScrollY" ); } // Перерисовываем this._drawSelection(); var d = {}; /*var d = { deltaX : this.activeMoveRange.c1 < this.visibleRange.c1 ? this.activeMoveRange.c1-this.visibleRange.c1 : this.activeMoveRange.c2>this.visibleRange.c2 ? this.activeMoveRange.c2-this.visibleRange.c2 : 0, deltaY : this.activeMoveRange.r1 < this.visibleRange.r1 ? this.activeMoveRange.r1-this.visibleRange.r1 : this.activeMoveRange.r2>this.visibleRange.r2 ? this.activeMoveRange.r2-this.visibleRange.r2 : 0 }; while ( this._isColDrawnPartially( this.activeMoveRange.c2, this.visibleRange.c1 + d.deltaX) ) {++d.deltaX;} while ( this._isRowDrawnPartially( this.activeMoveRange.r2, this.visibleRange.r1 + d.deltaY) ) {++d.deltaY;}*/ if ( y <= this.cellsTop + this.height_2px ) { d.deltaY = -1; } else if ( y >= this.drawingCtx.getHeight() - this.height_2px ) { d.deltaY = 1; } if ( x <= this.cellsLeft + this.width_2px ) { d.deltaX = -1; } else if ( x >= this.drawingCtx.getWidth() - this.width_2px ) { d.deltaX = 1; } this.model.workbook.handlers.trigger( "asc_onHideComment" ); if ( this.activeMoveRange.type === c_oAscSelectionType.RangeRow ) { d.deltaX = 0; } else if ( this.activeMoveRange.type === c_oAscSelectionType.RangeCol ) { d.deltaY = 0; } else if ( this.activeMoveRange.type === c_oAscSelectionType.RangeMax ) { d.deltaX = 0; d.deltaY = 0; } return d; }; WorksheetView.prototype.changeSelectionMoveResizeRangeHandle = function ( x, y, targetInfo, editor ) { // Возвращаемый результат if ( !targetInfo ) { return null; } var indexFormulaRange = targetInfo.indexFormulaRange, d = {deltaY: 0, deltaX: 0}, newFormulaRange = null; // Пересчитываем координаты x *= asc_getcvt( 0/*px*/, 1/*pt*/, this._getPPIX() ); y *= asc_getcvt( 0/*px*/, 1/*pt*/, this._getPPIY() ); var ar = 0 == targetInfo.targetArr ? this.arrActiveFormulaRanges[indexFormulaRange].clone( true ) : this.arrActiveChartsRanges[indexFormulaRange].clone( true ); // Колонка по X и строка по Y var colByX = this._findColUnderCursor( x, /*canReturnNull*/false, /*dX*/false ).col; var rowByY = this._findRowUnderCursor( y, /*canReturnNull*/false, /*dY*/false ).row; // Если мы только первый раз попали сюда, то копируем выделенную область if ( null === this.startCellMoveResizeRange ) { if ( (targetInfo.cursor == kCurNEResize || targetInfo.cursor == kCurSEResize) ) { this.startCellMoveResizeRange = ar.clone( true ); this.startCellMoveResizeRange2 = new asc_Range( targetInfo.col, targetInfo.row, targetInfo.col, targetInfo.row, true ); } else { this.startCellMoveResizeRange = ar.clone( true ); if ( colByX < ar.c1 ) { colByX = ar.c1; } else if ( colByX > ar.c2 ) { colByX = ar.c2; } if ( rowByY < ar.r1 ) { rowByY = ar.r1; } else if ( rowByY > ar.r2 ) { rowByY = ar.r2; } this.startCellMoveResizeRange2 = new asc_Range( colByX, rowByY, colByX, rowByY ); } return null; } // Очищаем выделение, будем рисовать заново // this.cleanSelection(); this.overlayCtx.clear(); if ( targetInfo.cursor == kCurNEResize || targetInfo.cursor == kCurSEResize ) { if ( colByX < this.startCellMoveResizeRange2.c1 ) { ar.c2 = this.startCellMoveResizeRange2.c1; ar.c1 = colByX; } else if ( colByX > this.startCellMoveResizeRange2.c1 ) { ar.c1 = this.startCellMoveResizeRange2.c1; ar.c2 = colByX; } else { ar.c1 = this.startCellMoveResizeRange2.c1; ar.c2 = this.startCellMoveResizeRange2.c1 } if ( rowByY < this.startCellMoveResizeRange2.r1 ) { ar.r2 = this.startCellMoveResizeRange2.r2; ar.r1 = rowByY; } else if ( rowByY > this.startCellMoveResizeRange2.r1 ) { ar.r1 = this.startCellMoveResizeRange2.r1; ar.r2 = rowByY; } else { ar.r1 = this.startCellMoveResizeRange2.r1; ar.r2 = this.startCellMoveResizeRange2.r1; } } else { this.startCellMoveResizeRange.normalize(); var colDelta = this.startCellMoveResizeRange.type != c_oAscSelectionType.RangeRow && this.startCellMoveResizeRange.type != c_oAscSelectionType.RangeMax ? colByX - this.startCellMoveResizeRange2.c1 : 0; var rowDelta = this.startCellMoveResizeRange.type != c_oAscSelectionType.RangeCol && this.startCellMoveResizeRange.type != c_oAscSelectionType.RangeMax ? rowByY - this.startCellMoveResizeRange2.r1 : 0; ar.c1 = this.startCellMoveResizeRange.c1 + colDelta; if ( 0 > ar.c1 ) { colDelta -= ar.c1; ar.c1 = 0; } ar.c2 = this.startCellMoveResizeRange.c2 + colDelta; ar.r1 = this.startCellMoveResizeRange.r1 + rowDelta; if ( 0 > ar.r1 ) { rowDelta -= ar.r1; ar.r1 = 0; } ar.r2 = this.startCellMoveResizeRange.r2 + rowDelta; } if ( y <= this.cellsTop + this.height_2px ) { d.deltaY = -1; } else if ( y >= this.drawingCtx.getHeight() - this.height_2px ) { d.deltaY = 1; } if ( x <= this.cellsLeft + this.width_2px ) { d.deltaX = -1; } else if ( x >= this.drawingCtx.getWidth() - this.width_2px ) { d.deltaX = 1; } if ( this.startCellMoveResizeRange.type === c_oAscSelectionType.RangeRow ) { d.deltaX = 0; } else if ( this.startCellMoveResizeRange.type === c_oAscSelectionType.RangeCol ) { d.deltaY = 0; } else if ( this.startCellMoveResizeRange.type === c_oAscSelectionType.RangeMax ) { d.deltaX = 0; d.deltaY = 0; } if ( 0 == targetInfo.targetArr ) { var _p = this.arrActiveFormulaRanges[indexFormulaRange].cursorePos, _l = this.arrActiveFormulaRanges[indexFormulaRange].formulaRangeLength; this.arrActiveFormulaRanges[indexFormulaRange] = ar.clone( true ); this.arrActiveFormulaRanges[indexFormulaRange].cursorePos = _p; this.arrActiveFormulaRanges[indexFormulaRange].formulaRangeLength = _l; newFormulaRange = this.arrActiveFormulaRanges[indexFormulaRange]; } else { this.arrActiveChartsRanges[indexFormulaRange] = ar.clone( true ); this.moveRangeDrawingObjectTo = ar; } this._drawSelection(); if ( newFormulaRange ) { editor.changeCellRange( newFormulaRange ); } return d; }; WorksheetView.prototype._cleanSelectionMoveRange = function () { // Перерисовываем и сбрасываем параметры this.cleanSelection(); this.activeMoveRange = null; this.startCellMoveRange = null; this._drawSelection(); }; /* Функция для применения перемещения диапазона */ WorksheetView.prototype.applyMoveRangeHandle = function ( ctrlKey ) { if ( null === this.activeMoveRange ) { // Сбрасываем параметры this.startCellMoveRange = null; return; } var arnFrom = this.activeRange.clone( true ); var arnTo = this.activeMoveRange.clone( true ); if ( arnFrom.isEqual( arnTo ) ) { this._cleanSelectionMoveRange(); return; } var resmove = this.model._prepareMoveRange( arnFrom, arnTo ); if ( resmove === -2 ) { this.handlers.trigger( "onErrorEvent", c_oAscError.ID.CannotMoveRange, c_oAscError.Level.NoCritical ); this._cleanSelectionMoveRange(); } else if ( resmove === -1 && this.autoFilters.checkMoveRangeIntoApplyAutoFilter( arnTo ) ) { var t = this; this.model.workbook.handlers.trigger( "asc_onConfirmAction", c_oAscConfirm.ConfirmReplaceRange, function ( can ) { if ( can ) { t.moveRangeHandle( arnFrom, arnTo, ctrlKey ); } else { t._cleanSelectionMoveRange(); } } ); } else { this.moveRangeHandle( arnFrom, arnTo, ctrlKey ); } }; WorksheetView.prototype.applyMoveResizeRangeHandle = function ( target ) { if ( -1 == target.targetArr && !this.startCellMoveResizeRange.isEqual( this.moveRangeDrawingObjectTo ) ) { this.objectRender.moveRangeDrawingObject( this.startCellMoveResizeRange, this.moveRangeDrawingObjectTo ); } this.startCellMoveResizeRange = null; this.startCellMoveResizeRange2 = null; this.moveRangeDrawingObjectTo = null; }; WorksheetView.prototype.moveRangeHandle = function ( arnFrom, arnTo, copyRange ) { var t = this; var onApplyMoveRangeHandleCallback = function ( isSuccess ) { if ( false === isSuccess ) { t._cleanSelectionMoveRange(); return; } var onApplyMoveAutoFiltersCallback = function ( isSuccess ) { if ( false === isSuccess ) { t._cleanSelectionMoveRange(); return; } // Очищаем выделение t.cleanSelection(); //ToDo t.cleanDepCells(); History.Create_NewPoint(); History.SetSelection( arnFrom.clone() ); History.SetSelectionRedo( arnTo.clone() ); History.StartTransaction(); t.autoFilters._preMoveAutoFilters( arnFrom, arnTo, copyRange ); t.model._moveRange( arnFrom, arnTo, copyRange ); t.cellCommentator.moveRangeComments( arnFrom, arnTo ); t.objectRender.moveRangeDrawingObject( arnFrom, arnTo ); t.autoFilters._moveAutoFilters( arnTo, arnFrom, null, copyRange, true ); // Вызываем функцию пересчета для заголовков форматированной таблицы t.autoFilters.renameTableColumn( arnFrom ); t.autoFilters.renameTableColumn( arnTo ); t.autoFilters.reDrawFilter( arnFrom ); History.EndTransaction(); t._updateCellsRange( arnTo, false, true ); t.activeRange = arnTo.clone( true ); // Сбрасываем параметры t.activeMoveRange = null; t.startCellMoveRange = null; t._updateCellsRange( arnFrom, false, true ); // Тут будет отрисовка select-а t._recalculateAfterUpdate( [arnFrom, arnTo] ); // Вызовем на всякий случай, т.к. мы можем уже обновиться из-за формул ToDo возможно стоит убрать это в дальнейшем (но нужна переработка формул) - http://bugzserver/show_bug.cgi?id=24505 t._updateSelectionNameAndInfo(); }; if ( t.autoFilters._searchFiltersInRange( arnFrom ) ) { t._isLockedAll( onApplyMoveAutoFiltersCallback ); } else { onApplyMoveAutoFiltersCallback(); } }; if ( this.autoFilters.isCheckMoveRange( arnFrom, arnTo ) ) { this._isLockedCells( [arnFrom, arnTo], null, onApplyMoveRangeHandleCallback ); } else { this._cleanSelectionMoveRange(); } }; WorksheetView.prototype.emptySelection = function ( options ) { // Удаляем выделенные графичекие объекты if ( this.objectRender.selectedGraphicObjectsExists() ) { this.objectRender.controller.deleteSelectedObjects(); } else { this.setSelectionInfo( "empty", options ); } }; WorksheetView.prototype.setSelectionInfo = function ( prop, val, onlyActive, isLocal ) { // Проверка глобального лока if ( this.collaborativeEditing.getGlobalLock() ) { return; } var t = this; var checkRange = null; var arn = t.activeRange.clone( true ); if ( onlyActive ) { checkRange = new asc_Range( arn.startCol, arn.startRow, arn.startCol, arn.startRow ); } else { checkRange = arn.getAllRange(); } var onSelectionCallback = function ( isSuccess ) { if ( false === isSuccess ) { return; } var range; var canChangeColWidth = c_oAscCanChangeColWidth.none; var bIsUpdate = true; if ( onlyActive ) { range = t.model.getRange3( arn.startRow, arn.startCol, arn.startRow, arn.startCol ); } else { if ( c_oAscSelectionType.RangeMax === arn.type ) { range = t.model.getRange3( /*arn.r1*/0, /*arn.c1*/0, gc_nMaxRow0, gc_nMaxCol0 ); } else if ( c_oAscSelectionType.RangeCol === arn.type ) { range = t.model.getRange3( /*arn.r1*/0, arn.c1, gc_nMaxRow0, arn.c2 ); } else if ( c_oAscSelectionType.RangeRow === arn.type ) { range = t.model.getRange3( arn.r1, /*arn.c1*/0, arn.r2, gc_nMaxCol0 ); } else { range = t.model.getRange3( arn.r1, arn.c1, arn.r2, arn.c2 ); } } var isLargeRange = t._isLargeRange( range.bbox ), callTrigger = false; var res; var mc, r, c, cell; function makeBorder( b ) { var border = new BorderProp(); if ( b === false ) { border.setStyle( c_oAscBorderStyles.None ); } else if ( b ) { if ( b.style !== null && b.style !== undefined ) { border.setStyle( b.style ); } if ( b.color !== null && b.color !== undefined ) { if ( b.color instanceof asc_CColor ) { border.c = CorrectAscColor( b.color ); } } } return border; } History.Create_NewPoint(); History.StartTransaction(); switch ( prop ) { case "fn": range.setFontname( val ); canChangeColWidth = c_oAscCanChangeColWidth.numbers; break; case "fs": range.setFontsize( val ); canChangeColWidth = c_oAscCanChangeColWidth.numbers; break; case "b": range.setBold( val ); break; case "i": range.setItalic( val ); break; case "u": range.setUnderline( val ); break; case "s": range.setStrikeout( val ); break; case "fa": range.setFontAlign( val ); break; case "a": range.setAlignHorizontal( val ); break; case "va": range.setAlignVertical( val ); break; case "c": range.setFontcolor( val ); break; case "bc": range.setFill( (val) ? (val) : null ); break; // ToDo можно делать просто отрисовку case "wrap": range.setWrap( val ); break; case "shrink": range.setShrinkToFit( val ); break; case "value": range.setValue( val ); break; case "format": range.setNumFormat( val ); canChangeColWidth = c_oAscCanChangeColWidth.numbers; break; case "angle": range.setAngle( val ); break; case "rh": range.removeHyperlink( null, true ); break; case "border": if ( isLargeRange ) { callTrigger = true; t.handlers.trigger( "slowOperation", true ); } // None if ( val.length < 1 ) { range.setBorder( null ); break; } res = new Border(); // Diagonal res.d = makeBorder( val[c_oAscBorderOptions.DiagD] || val[c_oAscBorderOptions.DiagU] ); res.dd = val[c_oAscBorderOptions.DiagD] ? true : false; res.du = val[c_oAscBorderOptions.DiagU] ? true : false; // Vertical res.l = makeBorder( val[c_oAscBorderOptions.Left] ); res.iv = makeBorder( val[c_oAscBorderOptions.InnerV] ); res.r = makeBorder( val[c_oAscBorderOptions.Right] ); // Horizontal res.t = makeBorder( val[c_oAscBorderOptions.Top] ); res.ih = makeBorder( val[c_oAscBorderOptions.InnerH] ); res.b = makeBorder( val[c_oAscBorderOptions.Bottom] ); // Change border range.setBorder( res ); break; case "merge": if ( isLargeRange ) { callTrigger = true; t.handlers.trigger( "slowOperation", true ); } switch ( val ) { case c_oAscMergeOptions.MergeCenter: case c_oAscMergeOptions.Merge: range.merge( val ); t.cellCommentator.mergeComments( range.getBBox0() ); break; case c_oAscMergeOptions.Unmerge: range.unmerge(); break; case c_oAscMergeOptions.MergeAcross: for ( res = arn.r1; res <= arn.r2; ++res ) { t.model.getRange3( res, arn.c1, res, arn.c2 ).merge( val ); cell = new asc_Range( arn.c1, res, arn.c2, res ); t.cellCommentator.mergeComments( cell ); } break; } break; case "sort": if ( isLargeRange ) { callTrigger = true; t.handlers.trigger( "slowOperation", true ); } t.cellCommentator.sortComments( range.sort( val, arn.startCol ) ); break; case "empty": if ( isLargeRange ) { callTrigger = true; t.handlers.trigger( "slowOperation", true ); } /* отключаем отрисовку на случай необходимости пересчета ячеек, заносим ячейку, при необходимости в список перерисовываемых */ lockDraw( t.model.workbook ); // Если нужно удалить автофильтры - удаляем if ( val === c_oAscCleanOptions.All || val === c_oAscCleanOptions.Text ) { t.autoFilters.isEmptyAutoFilters( arn ); } else if ( val === c_oAscCleanOptions.Format ) { t.autoFilters.cleanFormat( arn ); } if ( val === c_oAscCleanOptions.All ) { range.cleanAll(); // Удаляем комментарии t.cellCommentator.deleteCommentsRange( arn ); } else if ( val === c_oAscCleanOptions.Text || val === c_oAscCleanOptions.Formula ) { range.cleanText(); } else if ( val === c_oAscCleanOptions.Format ) { range.cleanFormat(); } else if ( val === c_oAscCleanOptions.Comments ) { t.cellCommentator.deleteCommentsRange( arn ); } else if ( val === c_oAscCleanOptions.Hyperlinks ) { range.cleanHyperlinks(); } // Вызываем функцию пересчета для заголовков форматированной таблицы t.autoFilters.renameTableColumn( arn ); /* возвращаем отрисовку. и перерисовываем ячейки с предварительным пересчетом */ buildRecalc( t.model.workbook ); unLockDraw( t.model.workbook ); break; case "changeDigNum": res = t.cols.slice( arn.c1, arn.c2 + 1 ).reduce( function ( r, c ) { r.push( c.charCount ); return r; }, [] ); range.shiftNumFormat( val, res ); canChangeColWidth = c_oAscCanChangeColWidth.numbers; break; case "changeFontSize": mc = t.model.getMergedByCell( arn.startRow, arn.startCol ); c = mc ? mc.c1 : arn.startCol; r = mc ? mc.r1 : arn.startRow; cell = t._getVisibleCell( c, r ); if ( undefined !== cell ) { var oldFontSize = cell.getFontsize(); var newFontSize = asc_incDecFonSize( val, oldFontSize ); if ( null !== newFontSize ) { range.setFontsize( newFontSize ); canChangeColWidth = c_oAscCanChangeColWidth.numbers; } } break; case "style": range.setCellStyle( val ); canChangeColWidth = c_oAscCanChangeColWidth.numbers; break; break; case "paste": // Вставляем текст из локального буфера или нет isLocal ? t._pasteFromLocalBuff( isLargeRange, isLocal, val, bIsUpdate, canChangeColWidth ) : t._pasteFromGlobalBuff( isLargeRange, isLocal, val, bIsUpdate, canChangeColWidth ); bIsUpdate = false; break; case "hyperlink": if ( val && val.hyperlinkModel ) { if ( c_oAscHyperlinkType.RangeLink === val.asc_getType() ) { var hyperlinkRangeTmp = t.model.getRange2( val.asc_getRange() ); if ( null === hyperlinkRangeTmp ) { bIsUpdate = false; break; } } val.hyperlinkModel.Ref = range; range.setHyperlink( val.hyperlinkModel ); // Вставим текст в активную ячейку (а не так, как MSExcel в первую ячейку диапазона) mc = t.model.getMergedByCell( arn.startRow, arn.startCol ); c = mc ? mc.c1 : arn.startCol; r = mc ? mc.r1 : arn.startRow; if ( null !== val.asc_getText() ) { t.model.getRange3( r, c, r, c ).setValue( val.asc_getText() ); // Вызываем функцию пересчета для заголовков форматированной таблицы t.autoFilters.renameTableColumn( arn ); } break; } else { bIsUpdate = false; break; } default: bIsUpdate = false; break; } if ( bIsUpdate ) { if ( callTrigger ) { t.handlers.trigger( "slowOperation", false ); } t.isChanged = true; t._updateCellsRange( arn, canChangeColWidth ); } //в случае, если вставляем из глобального буфера, транзакцию закрываем внутри функции _pasteFromGlobalBuff на callbacks от загрузки шрифтов и картинок if ( prop !== "paste" || (prop === "paste" && isLocal) ) { History.EndTransaction(); } }; if ( "paste" === prop && val.onlyImages !== true ) { // Для past свой диапазон if ( isLocal === "binary" ) { checkRange = t._pasteFromBinary( val, true ); } else { checkRange = t._setInfoAfterPaste( val, true ); } } if ( "paste" === prop && val.onlyImages === true ) { onSelectionCallback(); } else { this._isLockedCells( checkRange, /*subType*/null, onSelectionCallback ); } }; WorksheetView.prototype._pasteFromLocalBuff = function ( isLargeRange, isLocal, val, bIsUpdate, canChangeColWidth ) { var t = this; var callTrigger = false; if ( isLargeRange ) { callTrigger = true; t.handlers.trigger( "slowOperation", true ); } lockDraw( t.model.workbook ); var selectData; if ( isLocal === 'binary' ) { selectData = t._pasteFromBinary( val ); } else { selectData = t._setInfoAfterPaste( val ); } t.autoFilters.renameTableColumn( t.activeRange ); if ( !selectData ) { bIsUpdate = false; buildRecalc( t.model.workbook ); unLockDraw( t.model.workbook ); return; } t.expandColsOnScroll(); t.expandRowsOnScroll(); var arrFormula = selectData[1]; for ( var i = 0; i < arrFormula.length; ++i ) {//!!! var rangeF = arrFormula[i].range; var valF = arrFormula[i].val; if ( rangeF.isOneCell() ) { rangeF.setValue( valF, null, true ); } else { var oBBox = rangeF.getBBox0(); t.model._getCell( oBBox.r1, oBBox.c1 ).setValue( valF, null, true ); } } buildRecalc( t.model.workbook ); unLockDraw( t.model.workbook ); var arn = selectData[0]; var selectionRange = arn.clone( true ); //добавляем автофильтры и форматированные таблицы if ( isLocal === true && val.lStorage && val.lStorage.autoFilters && val.lStorage.autoFilters.length ) { var aFilters = val.lStorage.autoFilters; var range; for ( var aF = 0; aF < aFilters.length; aF++ ) { range = t.model.getRange3( aFilters[aF].range.r1 + selectionRange.r1, aFilters[aF].range.c1 + selectionRange.c1, aFilters[aF].range.r2 + selectionRange.r1, aFilters[aF].range.c2 + selectionRange.c1 ); //если область вставки содержит форматированную таблицу, которая пересекается с вставляемой форматированной таблицей var intersectionRangeWithTableParts = t.autoFilters._intersectionRangeWithTableParts( range.bbox, this.model ); if ( intersectionRangeWithTableParts ) continue; if ( aFilters[aF].style ) { range.cleanFormat(); } t.autoFilters.addAutoFilter( aFilters[aF].style, range.bbox, true, true ); if ( !aFilters[aF].autoFilter ) { t.autoFilters.addAutoFilter( null, range.bbox, true, true ); } } } else if ( isLocal === 'binary' && val.TableParts && val.TableParts.length ) { var aFilters = val.TableParts; var range; var tablePartRange; var activeRange = window["Asc"]["editor"].wb.clipboard.activeRange; var refInsertBinary = Asc.g_oRangeCache.getAscRange( activeRange ); var diffRow; var diffCol; for ( var aF = 0; aF < aFilters.length; aF++ ) { tablePartRange = aFilters[aF].Ref; diffRow = tablePartRange.r1 - refInsertBinary.r1; diffCol = tablePartRange.c1 - refInsertBinary.c1; range = t.model.getRange3( diffRow + selectionRange.r1, diffCol + selectionRange.c1, diffRow + selectionRange.r1 + (tablePartRange.r2 - tablePartRange.r1), diffCol + selectionRange.c1 + (tablePartRange.c2 - tablePartRange.c1) ); //если область вставки содержит форматированную таблицу, которая пересекается с вставляемой форматированной таблицей var intersectionRangeWithTableParts = t.autoFilters._intersectionRangeWithTableParts( range.bbox, this.model ); if ( intersectionRangeWithTableParts ) continue; if ( aFilters[aF].style ) { range.cleanFormat(); } var bWithoutFilter = false; if ( !aFilters[aF].AutoFilter ) { bWithoutFilter = true; } t.autoFilters.addAutoFilter( aFilters[aF].TableStyleInfo.Name, range.bbox, true, true, bWithoutFilter ); } } //делаем unmerge ф/т var arnToRange = t.activeRange; var intersectionRangeWithTableParts = t.autoFilters._intersectionRangeWithTableParts( arnToRange, this.model ); if ( intersectionRangeWithTableParts && intersectionRangeWithTableParts.length ) { var tablePart; for ( var i = 0; i < intersectionRangeWithTableParts.length; i++ ) { tablePart = intersectionRangeWithTableParts[i]; this.model.getRange3( tablePart.Ref.r1, tablePart.Ref.c1, tablePart.Ref.r2, tablePart.Ref.c2 ).unmerge(); } } if ( bIsUpdate ) { if ( callTrigger ) { t.handlers.trigger( "slowOperation", false ); } t.isChanged = true; t._updateCellsRange( arn, canChangeColWidth ); } var oSelection = History.GetSelection(); if ( null != oSelection ) { oSelection = oSelection.clone(); oSelection.assign( selectionRange.c1, selectionRange.r1, selectionRange.c2, selectionRange.r2 ); History.SetSelection( oSelection ); History.SetSelectionRedo( oSelection ); } }; WorksheetView.prototype._pasteFromGlobalBuff = function ( isLargeRange, isLocal, val, bIsUpdate, canChangeColWidth ) { var t = this; //загрузка шрифтов, в случае удачи на callback вставляем текст t._loadFonts( val.fontsNew, function () { var api = asc["editor"]; var isEndTransaction = false; if ( val.addImages && val.addImages.length != 0 && !(window["Asc"]["editor"] && window["Asc"]["editor"].isChartEditor) ) { if ( val.onlyImages !== true ) { t._pasteFromLocalBuff( isLargeRange, isLocal, val, bIsUpdate, canChangeColWidth ); } api.wb.clipboard._insertImagesFromHTML( t, val ); isEndTransaction = true; } else if ( val.addImagesFromWord && val.addImagesFromWord.length != 0 && !(window["Asc"]["editor"] && window["Asc"]["editor"].isChartEditor) ) { var oObjectsForDownload = GetObjectsForImageDownload( val._aPastedImages ); //if already load images on server if ( api.wb.clipboard.alreadyLoadImagesOnServer === true ) { var oImageMap = {}; for ( var i = 0, length = oObjectsForDownload.aBuilderImagesByUrl.length; i < length; ++i ) { var url = oObjectsForDownload.aUrls[i]; //get name from array already load on server urls var name = api.wb.clipboard.oImages[url]; var aImageElem = oObjectsForDownload.aBuilderImagesByUrl[i]; if ( name ) { if ( Array.isArray( aImageElem ) ) { for ( var j = 0; j < aImageElem.length; ++j ) { var imageElem = aImageElem[j]; if ( null != imageElem ) { imageElem.SetUrl( name ); } } } oImageMap[i] = name; } else { oImageMap[i] = url; } } if ( val.onlyImages !== true ) { t._pasteFromLocalBuff( isLargeRange, isLocal, val, bIsUpdate, canChangeColWidth ); } api.wb.clipboard._insertImagesFromBinaryWord( t, val, oImageMap ); isEndTransaction = true; } else { sendImgUrls( api, oObjectsForDownload.aUrls, function ( data ) { var oImageMap = {}; ResetNewUrls( data, oObjectsForDownload.aUrls, oObjectsForDownload.aBuilderImagesByUrl, oImageMap ); if ( val.onlyImages !== true ) { t._pasteFromLocalBuff( isLargeRange, isLocal, val, bIsUpdate, canChangeColWidth ); } api.wb.clipboard._insertImagesFromBinaryWord( t, val, oImageMap ); //закрываем транзакцию, поскольку в setSelectionInfo она не закроется History.EndTransaction(); }, true ); } } else if ( val.onlyImages !== true ) { t._pasteFromLocalBuff( isLargeRange, isLocal, val, bIsUpdate, canChangeColWidth ); isEndTransaction = true; } //закрываем транзакцию, поскольку в setSelectionInfo она не закроется if ( isEndTransaction ) { History.EndTransaction(); } } ); }; WorksheetView.prototype._setInfoAfterPaste = function ( values, isCheckSelection ) { var t = this; var wb = window["Asc"]["editor"].wb; var arn = wb && wb.clipboard && wb.clipboard.activeRange ? wb.clipboard.activeRange : t.activeRange.clone( true ); var arrFormula = []; var numFor = 0; var rMax = values.length + values.rowSpanSpCount; if ( values.rowCount && values.rowCount !== 0 && values.isOneTable ) { rMax = values.rowCount + arn.r1; } var cMax = values.cellCount + arn.c1; var isMultiple = false; var firstCell = t.model.getRange3( arn.r1, arn.c1, arn.r1, arn.c1 ); var isMergedFirstCell = firstCell.hasMerged(); var rangeUnMerge = t.model.getRange3( arn.r1, arn.c1, rMax - 1, cMax - 1 ); var isOneMerge = false; //если вставляем в мерженную ячейку, диапазон которой больше или равен if ( arn.c2 >= cMax - 1 && arn.r2 >= rMax - 1 && isMergedFirstCell && isMergedFirstCell.c1 === arn.c1 && isMergedFirstCell.c2 === arn.c2 && isMergedFirstCell.r1 === arn.r1 && isMergedFirstCell.r2 === arn.r2 && cMax - arn.c1 === values[arn.r1][arn.c1][0].colSpan && rMax - arn.r1 === values[arn.r1][arn.c1][0].rowSpan ) { if ( !isCheckSelection ) { values[arn.r1][arn.c1][0].colSpan = isMergedFirstCell.c2 - isMergedFirstCell.c1 + 1; values[arn.r1][arn.c1][0].rowSpan = isMergedFirstCell.r2 - isMergedFirstCell.r1 + 1; } isOneMerge = true; } else if ( arn.c2 >= cMax - 1 && arn.r2 >= rMax - 1 && values.isOneTable ) { //если область кратная куску вставки var widthArea = arn.c2 - arn.c1 + 1; var heightArea = arn.r2 - arn.r1 + 1; var widthPasteFr = cMax - arn.c1; var heightPasteFr = rMax - arn.r1; //если кратны, то обрабатываем if ( widthArea % widthPasteFr === 0 && heightArea % heightPasteFr === 0 ) { isMultiple = true; } else if ( firstCell.hasMerged() !== null )//в противном случае ошибка { if ( isCheckSelection ) { return arn; } else { this.handlers.trigger( "onError", c_oAscError.ID.PastInMergeAreaError, c_oAscError.Level.NoCritical ); return; } } } else { //проверка на наличие части объединённой ячейки в области куда осуществляем вставку for ( var rFirst = arn.r1; rFirst < rMax; ++rFirst ) { for ( var cFirst = arn.c1; cFirst < cMax; ++cFirst ) { range = t.model.getRange3( rFirst, cFirst, rFirst, cFirst ); var merged = range.hasMerged(); if ( merged ) { if ( merged.r1 < arn.r1 || merged.r2 > rMax - 1 || merged.c1 < arn.c1 || merged.c2 > cMax - 1 ) { //ошибка в случае если вставка происходит в часть объедененной ячейки if ( isCheckSelection ) { return arn; } else { this.handlers.trigger( "onErrorEvent", c_oAscError.ID.PastInMergeAreaError, c_oAscError.Level.NoCritical ); return; } } } } } } var rMax2 = rMax; var cMax2 = cMax; var rMax = values.length; var trueArn = t.activeRange; if ( isCheckSelection ) { var newArr = arn.clone( true ); newArr.r2 = rMax2 - 1; newArr.c2 = cMax2 - 1; if ( isMultiple || isOneMerge ) { newArr.r2 = trueArn.r2; newArr.c2 = trueArn.c2; } return newArr; } //если не возникает конфликт, делаем unmerge rangeUnMerge.unmerge(); if ( !isOneMerge ) { arn.r2 = (rMax2 - 1 > 0) ? (rMax2 - 1) : 0; arn.c2 = (cMax2 - 1 > 0) ? (cMax2 - 1) : 0; } var mergeArr = []; var n = 0; if ( isMultiple )//случай автозаполнения сложных форм { t.model.getRange3( trueArn.r1, trueArn.c1, trueArn.r2, trueArn.c2 ).unmerge(); var maxARow = heightArea / heightPasteFr; var maxACol = widthArea / widthPasteFr; var plRow = (rMax2 - arn.r1); var plCol = (arn.c2 - arn.c1) + 1; } else { var maxARow = 1; var maxACol = 1; var plRow = 0; var plCol = 0; } if ( isMultiple ) { if ( values[arn.r1] && values[arn.r1][arn.c1] ) { var currentObj = values[arn.r1][arn.c1][0]; var valFormat = ''; if ( currentObj[0] !== undefined ) { valFormat = currentObj[0].text; } } } for ( var autoR = 0; autoR < maxARow; ++autoR ) { for ( var autoC = 0; autoC < maxACol; ++autoC ) { for ( var r = arn.r1; r < rMax; ++r ) { for ( var c = arn.c1; c < values[r].length; ++c ) { if ( undefined !== values[r][c] ) { var range = t.model.getRange3( r + autoR * plRow, c + autoC * plCol, r + autoR * plRow, c + autoC * plCol ); var currentObj = values[r][c][0]; if ( currentObj.length === 1 ) { //if(!isMultiple) //{ var valFormat = currentObj[0].text; var nameFormat = false; //} if ( currentObj[0].cellFrom ) { var offset = range.getCells()[0].getOffset2( currentObj[0].cellFrom ), assemb, _p_ = new parserFormula( currentObj[0].text.substring( 1 ), "", range.worksheet ); if ( _p_.parse() ) { assemb = _p_.changeOffset( offset ).assemble(); //range.setValue("="+assemb); arrFormula[numFor] = {}; arrFormula[numFor].range = range; arrFormula[numFor].val = "=" + assemb; numFor++; } } else { range.setValue( valFormat ); } if ( nameFormat ) { range.setNumFormat( nameFormat ); } range.setBold( currentObj[0].format.b ); range.setItalic( currentObj[0].format.i ); range.setStrikeout( currentObj[0].format.s ); if ( !isOneMerge && currentObj[0].format && currentObj[0].format.c != null && currentObj[0].format.c != undefined ) { range.setFontcolor( currentObj[0].format.c ); } range.setUnderline( currentObj[0].format.u ); range.setAlignVertical( currentObj.va ); range.setFontname( currentObj[0].format.fn ); range.setFontsize( currentObj[0].format.fs ); } else { range.setValue2( currentObj ); range.setAlignVertical( currentObj.va ); } if ( currentObj.length === 1 && currentObj[0].format.fs !== '' && currentObj[0].format.fs !== null && currentObj[0].format.fs !== undefined ) { range.setFontsize( currentObj[0].format.fs ); } if ( !isOneMerge ) { range.setAlignHorizontal( currentObj.a ); } var isMerged = false; for ( var mergeCheck = 0; mergeCheck < mergeArr.length; ++mergeCheck ) { if ( r + 1 + autoR * plRow <= mergeArr[mergeCheck].r2 && r + 1 + autoR * plRow >= mergeArr[mergeCheck].r1 && c + autoC * plCol + 1 <= mergeArr[mergeCheck].c2 && c + 1 + autoC * plCol >= mergeArr[mergeCheck].c1 ) { isMerged = true; } } //обработка для мерженных ячеек if ( (currentObj.colSpan > 1 || currentObj.rowSpan > 1) && !isMerged ) { range.setOffsetLast( {offsetCol: currentObj.colSpan - 1, offsetRow: currentObj.rowSpan - 1} ); mergeArr[n] = { r1: range.first.row, r2: range.last.row, c1: range.first.col, c2: range.last.col }; n++; if ( currentObj[0] == undefined ) { range.setValue( '' ); } range.merge( c_oAscMergeOptions.Merge ); } if ( !isOneMerge ) { range.setBorderSrc( currentObj.borders ); } range.setWrap( currentObj.wrap ); if ( currentObj.bc && currentObj.bc.rgb ) { range.setFill( currentObj.bc ); } var link = values[r][c][0].hyperLink; if ( link ) { var newHyperlink = new Hyperlink(); if ( values[r][c][0].hyperLink.search( '#' ) === 0 ) { newHyperlink.setLocation( link.replace( '#', '' ) ); } else { newHyperlink.Hyperlink = link; } newHyperlink.Ref = range; newHyperlink.Tooltip = values[r][c][0].toolTip; range.setHyperlink( newHyperlink ); } } } } } } if ( isMultiple ) { arn.r2 = trueArn.r2; arn.c2 = trueArn.c2; } t.isChanged = true; t.activeRange.c2 = arn.c2; t.activeRange.r2 = arn.r2; var arnFor = []; arnFor[0] = arn; arnFor[1] = arrFormula; return arnFor; }; WorksheetView.prototype._pasteFromBinary = function ( val, isCheckSelection ) { var t = this; var arn = t.activeRange.clone( true ); var arrFormula = []; var numFor = 0; var pasteRange = window["Asc"]["editor"].wb.clipboard.activeRange; var activeCellsPasteFragment = Asc.g_oRangeCache.getAscRange( pasteRange ); var rMax = (activeCellsPasteFragment.r2 - activeCellsPasteFragment.r1) + arn.r1 + 1; var cMax = (activeCellsPasteFragment.c2 - activeCellsPasteFragment.c1) + arn.c1 + 1; if ( cMax > gc_nMaxCol0 ) { cMax = gc_nMaxCol0; } if ( rMax > gc_nMaxRow0 ) { rMax = gc_nMaxRow0; } var isMultiple = false; var firstCell = t.model.getRange3( arn.r1, arn.c1, arn.r1, arn.c1 ); var isMergedFirstCell = firstCell.hasMerged(); var isOneMerge = false; var startCell = val.getCell3( activeCellsPasteFragment.r1, activeCellsPasteFragment.c1 ); var isMergedStartCell = startCell.hasMerged(); var firstValuesCol; var firstValuesRow; if ( isMergedStartCell != null ) { firstValuesCol = isMergedStartCell.c2 - isMergedStartCell.c1; firstValuesRow = isMergedStartCell.r2 - isMergedStartCell.r1; } else { firstValuesCol = 0; firstValuesRow = 0; } var rowDiff = arn.r1 - activeCellsPasteFragment.r1; var colDiff = arn.c1 - activeCellsPasteFragment.c1; var newPasteRange = new Asc.Range( arn.c1 - colDiff, arn.r1 - rowDiff, arn.c2 - colDiff, arn.r2 - rowDiff ); //если вставляем в мерженную ячейку, диапазон которой больше или меньше, но не равен выделенной области if ( isMergedFirstCell && isMergedFirstCell.isEqual( arn ) && cMax - arn.c1 === (firstValuesCol + 1) && rMax - arn.r1 === (firstValuesRow + 1) && !newPasteRange.isEqual( activeCellsPasteFragment ) ) { isOneMerge = true; rMax = arn.r2 + 1; cMax = arn.c2 + 1; } else if ( arn.c2 >= cMax - 1 && arn.r2 >= rMax - 1 ) { //если область кратная куску вставки var widthArea = arn.c2 - arn.c1 + 1; var heightArea = arn.r2 - arn.r1 + 1; var widthPasteFr = cMax - arn.c1; var heightPasteFr = rMax - arn.r1; //если кратны, то обрабатываем if ( widthArea % widthPasteFr === 0 && heightArea % heightPasteFr === 0 ) { isMultiple = true; } else if ( firstCell.hasMerged() !== null )//в противном случае ошибка { if ( isCheckSelection ) { return arn; } else { this.handlers.trigger( "onError", c_oAscError.ID.PastInMergeAreaError, c_oAscError.Level.NoCritical ); return; } } } else { //проверка на наличие части объединённой ячейки в области куда осуществляем вставку for ( var rFirst = arn.r1; rFirst < rMax; ++rFirst ) { for ( var cFirst = arn.c1; cFirst < cMax; ++cFirst ) { range = t.model.getRange3( rFirst, cFirst, rFirst, cFirst ); var merged = range.hasMerged(); if ( merged ) { if ( merged.r1 < arn.r1 || merged.r2 > rMax - 1 || merged.c1 < arn.c1 || merged.c2 > cMax - 1 ) { //ошибка в случае если вставка происходит в часть объедененной ячейки if ( isCheckSelection ) { return arn; } else { this.handlers.trigger( "onErrorEvent", c_oAscError.ID.PastInMergeAreaError, c_oAscError.Level.NoCritical ); return; } } } } } } var rangeUnMerge = t.model.getRange3( arn.r1, arn.c1, rMax - 1, cMax - 1 ); var rMax2 = rMax; var cMax2 = cMax; //var rMax = values.length; var trueArn = t.activeRange; if ( isCheckSelection ) { var newArr = arn.clone( true ); newArr.r2 = rMax2 - 1; newArr.c2 = cMax2 - 1; if ( isMultiple || isOneMerge ) { newArr.r2 = trueArn.r2; newArr.c2 = trueArn.c2; } return newArr; } //если не возникает конфликт, делаем unmerge rangeUnMerge.unmerge(); if ( !isOneMerge ) { arn.r2 = rMax2 - 1; arn.c2 = cMax2 - 1; } var mergeArr = []; var n = 0; if ( isMultiple )//случай автозаполнения сложных форм { t.model.getRange3( trueArn.r1, trueArn.c1, trueArn.r2, trueArn.c2 ).unmerge(); var maxARow = heightArea / heightPasteFr; var maxACol = widthArea / widthPasteFr; var plRow = (rMax2 - arn.r1); var plCol = (arn.c2 - arn.c1) + 1; } else { var maxARow = 1; var maxACol = 1; var plRow = 0; var plCol = 0; } var newVal; var curMerge; var nRow, nCol; for ( var autoR = 0; autoR < maxARow; ++autoR ) { for ( var autoC = 0; autoC < maxACol; ++autoC ) { for ( var r = arn.r1; r < rMax; ++r ) { for ( var c = arn.c1; c < cMax; ++c ) { var pasteRow = r - arn.r1 + activeCellsPasteFragment.r1; var pasteCol = c - arn.c1 + activeCellsPasteFragment.c1; newVal = val.getCell3( pasteRow, pasteCol ); curMerge = newVal.hasMerged(); if ( undefined !== newVal ) { var isMerged = false, mergeCheck; nRow = r + autoR * plRow; if ( nRow > gc_nMaxRow0 ) { nRow = gc_nMaxRow0; } nCol = c + autoC * plCol; if ( nCol > gc_nMaxCol0 ) { nCol = gc_nMaxCol0; } var range = t.model.getRange3( nRow, nCol, nRow, nCol ); //range может далее изменится в связи с наличием мерженных ячеек, firstRange - не меняется(ему делаем setValue, как первой ячейке в диапазоне мерженных) var firstRange = range.clone(); //****paste comments**** if ( val.aComments && val.aComments.length ) { var comment; for ( var i = 0; i < val.aComments.length; i++ ) { comment = val.aComments[i]; if ( comment.nCol == pasteCol && comment.nRow == pasteRow ) { var commentData = new asc_CCommentData( comment ); //change nRow, nCol commentData.asc_putCol( c + autoC * plCol ); commentData.asc_putRow( r + autoR * plRow ); t.cellCommentator.addComment( commentData, true ); } } } if ( !isOneMerge ) { for ( mergeCheck = 0; mergeCheck < mergeArr.length; ++mergeCheck ) { if ( r + autoR * plRow <= mergeArr[mergeCheck].r2 && r + autoR * plRow >= mergeArr[mergeCheck].r1 && c + autoC * plCol <= mergeArr[mergeCheck].c2 && c + autoC * plCol >= mergeArr[mergeCheck].c1 ) { isMerged = true; } } if ( curMerge != null && !isMerged ) { var offsetCol = curMerge.c2 - curMerge.c1; if ( offsetCol + c + autoC * plCol >= gc_nMaxCol0 ) { offsetCol = gc_nMaxCol0 - (c + autoC * plCol); } var offsetRow = curMerge.r2 - curMerge.r1; if ( offsetRow + r + autoR * plRow >= gc_nMaxRow0 ) { offsetRow = gc_nMaxRow0 - (r + autoR * plRow); } range.setOffsetLast( {offsetCol: offsetCol, offsetRow: offsetRow} ); range.merge( c_oAscMergeOptions.Merge ); mergeArr[n] = { r1: curMerge.r1 + arn.r1 - activeCellsPasteFragment.r1 + autoR * plRow, r2: curMerge.r2 + arn.r1 - activeCellsPasteFragment.r1 + autoR * plRow, c1: curMerge.c1 + arn.c1 - activeCellsPasteFragment.c1 + autoC * plCol, c2: curMerge.c2 + arn.c1 - activeCellsPasteFragment.c1 + autoC * plCol }; n++; } } else { for ( mergeCheck = 0; mergeCheck < mergeArr.length; ++mergeCheck ) { if ( r + autoR * plRow <= mergeArr[mergeCheck].r2 && r + autoR * plRow >= mergeArr[mergeCheck].r1 && c + autoC * plCol <= mergeArr[mergeCheck].c2 && c + autoC * plCol >= mergeArr[mergeCheck].c1 ) { isMerged = true; } } if ( !isMerged ) { range.setOffsetLast( { offsetCol: (isMergedFirstCell.c2 - isMergedFirstCell.c1), offsetRow: (isMergedFirstCell.r2 - isMergedFirstCell.r1) } ); range.merge( c_oAscMergeOptions.Merge ); mergeArr[n] = { r1: isMergedFirstCell.r1, r2: isMergedFirstCell.r2, c1: isMergedFirstCell.c1, c2: isMergedFirstCell.c2 }; n++; } } //set style var cellStyle = newVal.getStyleName(); if ( cellStyle && !isOneMerge ) { range.setCellStyle( cellStyle ); } //add formula var numFormula = null; var skipFormat = null; var noSkipVal = null; var value2 = newVal.getValue2(); for ( var nF = 0; nF < value2.length; nF++ ) { if ( value2[nF] && value2[nF].sId ) { numFormula = nF; break; } else if ( value2[nF] && value2[nF].format && value2[nF].format.skip ) { skipFormat = true; } else if ( value2[nF] && value2[nF].format && !value2[nF].format.skip ) { noSkipVal = nF; } } if ( !isOneMerge )//settings for cell(format) { //format var numFormat = newVal.getNumFormat(); var nameFormat; if ( numFormat && numFormat.sFormat ) { nameFormat = numFormat.sFormat; } if ( nameFormat ) { range.setNumFormat( nameFormat ); } } //TODO вместо range где возможно использовать cell var cellFrom, cellTo; if ( value2.length == 1 || numFormula != null || (skipFormat != null && noSkipVal != null) ) { if ( numFormula == null ) { numFormula = 0; } var numStyle = 0; if ( skipFormat != null && noSkipVal != null ) { numStyle = noSkipVal; } //formula if ( newVal.getFormula() && !isOneMerge ) { var offset = range.getCells()[numFormula].getOffset2( value2[numFormula].sId ), assemb, _p_ = new parserFormula( value2[numFormula].sFormula, "", range.worksheet ); if ( _p_.parse() ) { assemb = _p_.changeOffset( offset ).assemble(); arrFormula[numFor] = {}; arrFormula[numFor].range = range; arrFormula[numFor].val = "=" + assemb; numFor++; } } else { cellFrom = newVal.getCells(); if ( isOneMerge && range && range.bbox ) { cellTo = this._getCell( range.bbox.c1, range.bbox.r1 ).getCells(); } else { cellTo = firstRange.getCells(); } if ( cellFrom && cellTo && cellFrom[0] && cellTo[0] ) { cellTo[0].setValueData( cellFrom[0].getValueData() ); } } if ( !isOneMerge )//settings for text { range.setBold( value2[numStyle].format.b ); range.setItalic( value2[numStyle].format.i ); range.setStrikeout( value2[numStyle].format.s ); if ( value2[numStyle].format && null != value2[numStyle].format.c ) { range.setFontcolor( value2[numStyle].format.c ); } range.setUnderline( value2[numStyle].format.u ); range.setFontname( value2[numStyle].format.fn ); range.setFontsize( value2[numStyle].format.fs ); } } else { firstRange.setValue2( value2 ); } if ( !isOneMerge )//settings for cell { //vertical align range.setAlignVertical( newVal.getAlignVertical() ); //horizontal align range.setAlignHorizontal( newVal.getAlignHorizontal() ); //borders var fullBorders = newVal.getBorderFull(); if ( range.bbox.c2 !== range.bbox.c1 && curMerge && fullBorders ) { //для мерженных ячеек, правая границу var endMergeCell = val.getCell3( pasteRow, curMerge.c2 ); var fullBordersEndMergeCell = endMergeCell.getBorderFull(); if ( fullBordersEndMergeCell && fullBordersEndMergeCell.r ) { fullBorders.r = fullBordersEndMergeCell.r; } } range.setBorder( fullBorders ); //fill range.setFill( newVal.getFill() ); //wrap range.setWrap( newVal.getWrap() ); //angle range.setAngle( newVal.getAngle() ); //hyperLink var hyperLink = newVal.getHyperlink(); if ( hyperLink != null ) { hyperLink.Ref = range; range.setHyperlink( hyperLink ); } } //если замержили range c = range.bbox.c2 - autoC * plCol; if ( c === cMax ) { r = range.bbox.r2 - autoC * plCol; } } } } } } if ( isMultiple ) { arn.r2 = trueArn.r2; arn.c2 = trueArn.c2; } t.isChanged = true; t.activeRange.c2 = arn.c2; t.activeRange.r2 = arn.r2; var arnFor = []; arnFor[0] = arn; arnFor[1] = arrFormula; return arnFor; }; // Залочена ли панель для закрепления WorksheetView.prototype._isLockedFrozenPane = function ( callback ) { var sheetId = this.model.getId(); var lockInfo = this.collaborativeEditing.getLockInfo( c_oAscLockTypeElem.Object, null, sheetId, c_oAscLockNameFrozenPane ); if ( false === this.collaborativeEditing.getCollaborativeEditing() ) { // Пользователь редактирует один: не ждем ответа, а сразу продолжаем редактирование asc_applyFunction( callback, true ); callback = undefined; } if ( false !== this.collaborativeEditing.getLockIntersection( lockInfo, c_oAscLockTypes.kLockTypeMine, /*bCheckOnlyLockAll*/false ) ) { // Редактируем сами asc_applyFunction( callback, true ); return; } else if ( false !== this.collaborativeEditing.getLockIntersection( lockInfo, c_oAscLockTypes.kLockTypeOther, /*bCheckOnlyLockAll*/false ) ) { // Уже ячейку кто-то редактирует asc_applyFunction( callback, false ); return; } this.collaborativeEditing.onStartCheckLock(); this.collaborativeEditing.addCheckLock( lockInfo ); this.collaborativeEditing.onEndCheckLock( callback ); }; WorksheetView.prototype._isLockedDefNames = function ( callback, defNameId ) { var sheetId = this.model.getId(); var lockInfo = this.collaborativeEditing.getLockInfo( c_oAscLockTypeElem.Object, null/*c_oAscLockTypeElemSubType.DefinedNames*/, -1, defNameId ); if ( false === this.collaborativeEditing.getCollaborativeEditing() ) { // Пользователь редактирует один: не ждем ответа, а сразу продолжаем редактирование asc_applyFunction( callback, true ); callback = undefined; } if ( false !== this.collaborativeEditing.getLockIntersection( lockInfo, c_oAscLockTypes.kLockTypeMine, /*bCheckOnlyLockAll*/false ) ) { // Редактируем сами asc_applyFunction( callback, true ); return; } else if ( false !== this.collaborativeEditing.getLockIntersection( lockInfo, c_oAscLockTypes.kLockTypeOther, /*bCheckOnlyLockAll*/false ) ) { // Уже ячейку кто-то редактирует asc_applyFunction( callback, false ); return; } this.collaborativeEditing.onStartCheckLock(); this.collaborativeEditing.addCheckLock( lockInfo ); this.collaborativeEditing.onEndCheckLock( callback ); }; // Залочен ли весь лист WorksheetView.prototype._isLockedAll = function ( callback ) { var sheetId = this.model.getId(); var subType = c_oAscLockTypeElemSubType.ChangeProperties; var ar = this.activeRange; var lockInfo = this.collaborativeEditing.getLockInfo( c_oAscLockTypeElem.Range, /*subType*/subType, sheetId, new asc.asc_CCollaborativeRange( ar.c1, ar.r1, ar.c2, ar.r2 ) ); if ( false === this.collaborativeEditing.getCollaborativeEditing() ) { // Пользователь редактирует один: не ждем ответа, а сразу продолжаем редактирование asc_applyFunction( callback, true ); callback = undefined; } if ( false !== this.collaborativeEditing.getLockIntersection( lockInfo, c_oAscLockTypes.kLockTypeMine, /*bCheckOnlyLockAll*/true ) ) { // Редактируем сами asc_applyFunction( callback, true ); return; } else if ( false !== this.collaborativeEditing.getLockIntersection( lockInfo, c_oAscLockTypes.kLockTypeOther, /*bCheckOnlyLockAll*/true ) ) { // Уже ячейку кто-то редактирует asc_applyFunction( callback, false ); return; } this.collaborativeEditing.onStartCheckLock(); this.collaborativeEditing.addCheckLock( lockInfo ); this.collaborativeEditing.onEndCheckLock( callback ); }; // Пересчет для входящих ячеек в добавленные строки/столбцы WorksheetView.prototype._recalcRangeByInsertRowsAndColumns = function ( sheetId, ar ) { var isIntersection = false, isIntersectionC1 = true, isIntersectionC2 = true, isIntersectionR1 = true, isIntersectionR2 = true; do { if ( isIntersectionC1 && this.collaborativeEditing.isIntersectionInCols( sheetId, ar.c1 ) ) { ar.c1 += 1; } else { isIntersectionC1 = false; } if ( isIntersectionR1 && this.collaborativeEditing.isIntersectionInRows( sheetId, ar.r1 ) ) { ar.r1 += 1; } else { isIntersectionR1 = false; } if ( isIntersectionC2 && this.collaborativeEditing.isIntersectionInCols( sheetId, ar.c2 ) ) { ar.c2 -= 1; } else { isIntersectionC2 = false; } if ( isIntersectionR2 && this.collaborativeEditing.isIntersectionInRows( sheetId, ar.r2 ) ) { ar.r2 -= 1; } else { isIntersectionR2 = false; } if ( ar.c1 > ar.c2 || ar.r1 > ar.r2 ) { isIntersection = true; break; } } while ( isIntersectionC1 || isIntersectionC2 || isIntersectionR1 || isIntersectionR2 ) ; if ( false === isIntersection ) { ar.c1 = this.collaborativeEditing.getLockMeColumn( sheetId, ar.c1 ); ar.c2 = this.collaborativeEditing.getLockMeColumn( sheetId, ar.c2 ); ar.r1 = this.collaborativeEditing.getLockMeRow( sheetId, ar.r1 ); ar.r2 = this.collaborativeEditing.getLockMeRow( sheetId, ar.r2 ); } return isIntersection; }; // Функция проверки lock (возвращаемый результат нельзя использовать в качестве ответа, он нужен только для редактирования ячейки) WorksheetView.prototype._isLockedCells = function ( range, subType, callback ) { var sheetId = this.model.getId(); var isIntersection = false; var newCallback = callback; var t = this; this.collaborativeEditing.onStartCheckLock(); var isArrayRange = Array.isArray( range ); var nLength = isArrayRange ? range.length : 1; var nIndex = 0; var ar = null; for ( ; nIndex < nLength; ++nIndex ) { ar = isArrayRange ? range[nIndex].clone( true ) : range.clone( true ); if ( c_oAscLockTypeElemSubType.InsertColumns !== subType && c_oAscLockTypeElemSubType.InsertRows !== subType ) { // Пересчет для входящих ячеек в добавленные строки/столбцы isIntersection = this._recalcRangeByInsertRowsAndColumns( sheetId, ar ); } if ( false === isIntersection ) { var lockInfo = this.collaborativeEditing.getLockInfo( c_oAscLockTypeElem.Range, /*subType*/subType, sheetId, new asc.asc_CCollaborativeRange( ar.c1, ar.r1, ar.c2, ar.r2 ) ); if ( false !== this.collaborativeEditing.getLockIntersection( lockInfo, c_oAscLockTypes.kLockTypeOther, /*bCheckOnlyLockAll*/false ) ) { // Уже ячейку кто-то редактирует asc_applyFunction( callback, false ); return false; } else { if ( c_oAscLockTypeElemSubType.InsertColumns === subType ) { newCallback = function ( isSuccess ) { if ( isSuccess ) { t.collaborativeEditing.addColsRange( sheetId, range.clone( true ) ); t.collaborativeEditing.addCols( sheetId, range.c1, range.c2 - range.c1 + 1 ); } callback( isSuccess ); }; } else if ( c_oAscLockTypeElemSubType.InsertRows === subType ) { newCallback = function ( isSuccess ) { if ( isSuccess ) { t.collaborativeEditing.addRowsRange( sheetId, range.clone( true ) ); t.collaborativeEditing.addRows( sheetId, range.r1, range.r2 - range.r1 + 1 ); } callback( isSuccess ); }; } else if ( c_oAscLockTypeElemSubType.DeleteColumns === subType ) { newCallback = function ( isSuccess ) { if ( isSuccess ) { t.collaborativeEditing.removeColsRange( sheetId, range.clone( true ) ); t.collaborativeEditing.removeCols( sheetId, range.c1, range.c2 - range.c1 + 1 ); } callback( isSuccess ); }; } else if ( c_oAscLockTypeElemSubType.DeleteRows === subType ) { newCallback = function ( isSuccess ) { if ( isSuccess ) { t.collaborativeEditing.removeRowsRange( sheetId, range.clone( true ) ); t.collaborativeEditing.removeRows( sheetId, range.r1, range.r2 - range.r1 + 1 ); } callback( isSuccess ); }; } this.collaborativeEditing.addCheckLock( lockInfo ); } } else { if ( c_oAscLockTypeElemSubType.InsertColumns === subType ) { t.collaborativeEditing.addColsRange( sheetId, range.clone( true ) ); t.collaborativeEditing.addCols( sheetId, range.c1, range.c2 - range.c1 + 1 ); } else if ( c_oAscLockTypeElemSubType.InsertRows === subType ) { t.collaborativeEditing.addRowsRange( sheetId, range.clone( true ) ); t.collaborativeEditing.addRows( sheetId, range.r1, range.r2 - range.r1 + 1 ); } else if ( c_oAscLockTypeElemSubType.DeleteColumns === subType ) { t.collaborativeEditing.removeColsRange( sheetId, range.clone( true ) ); t.collaborativeEditing.removeCols( sheetId, range.c1, range.c2 - range.c1 + 1 ); } else if ( c_oAscLockTypeElemSubType.DeleteRows === subType ) { t.collaborativeEditing.removeRowsRange( sheetId, range.clone( true ) ); t.collaborativeEditing.removeRows( sheetId, range.r1, range.r2 - range.r1 + 1 ); } } } if ( false === this.collaborativeEditing.getCollaborativeEditing() ) { // Пользователь редактирует один: не ждем ответа, а сразу продолжаем редактирование newCallback( true ); newCallback = undefined; } this.collaborativeEditing.onEndCheckLock( newCallback ); return true; }; WorksheetView.prototype.changeWorksheet = function ( prop, val ) { // Проверка глобального лока if ( this.collaborativeEditing.getGlobalLock() ) { return; } var t = this; var arn = t.activeRange.clone( true ); var checkRange = arn.getAllRange(); var range; var fullRecalc = false; var reinitRanges = false; var updateDrawingObjectsInfo = null; var cw; var isUpdateCols = false, isUpdateRows = false; var isCheckChangeAutoFilter; var functionModelAction = null; var lockDraw = false; // Параметр, при котором не будет отрисовки (т.к. мы просто обновляем информацию на неактивном листе) var oChangeData = new CChangeTableData( null, null, null, null ); // Обновление для диаграмм var onChangeWorksheetCallback = function ( isSuccess ) { if ( false === isSuccess ) { return; } asc_applyFunction( functionModelAction ); t._initCellsArea( fullRecalc ); if ( fullRecalc ) { t.cache.reset(); } t._cleanCellsTextMetricsCache(); t._prepareCellTextMetricsCache(); if ( reinitRanges && t.objectRender && t.objectRender.drawingArea ) { t.objectRender.drawingArea.reinitRanges(); } if ( null !== updateDrawingObjectsInfo && t.objectRender ) { t.objectRender.updateSizeDrawingObjects( updateDrawingObjectsInfo ); } t.objectRender.rebuildChartGraphicObjects( oChangeData ); t.draw( lockDraw ); t.handlers.trigger( "reinitializeScroll" ); if ( isUpdateCols ) { t._updateVisibleColsCount(); } if ( isUpdateRows ) { t._updateVisibleRowsCount(); } t.handlers.trigger( "selectionChanged", t.getSelectionInfo() ); t.handlers.trigger( "selectionMathInfoChanged", t.getSelectionMathInfo() ); }; switch ( prop ) { case "colWidth": functionModelAction = function () { cw = t._charCountToModelColWidth( val ); t.model.setColWidth( cw, checkRange.c1, checkRange.c2 ); isUpdateCols = true; fullRecalc = true; reinitRanges = true; updateDrawingObjectsInfo = {target: c_oTargetType.ColumnResize, col: checkRange.c1}; }; this._isLockedAll( onChangeWorksheetCallback ); break; case "showCols": functionModelAction = function () { t.model.setColHidden( /*bHidden*/false, arn.c1, arn.c2 ); fullRecalc = true; reinitRanges = true; updateDrawingObjectsInfo = {target: c_oTargetType.ColumnResize, col: arn.c1}; }; oChangeData.hided = new asc_Range( arn.c1, 0, arn.c2, gc_nMaxRow0 ); this._isLockedAll( onChangeWorksheetCallback ); break; case "hideCols": functionModelAction = function () { t.model.setColHidden( /*bHidden*/true, arn.c1, arn.c2 ); fullRecalc = true; reinitRanges = true; updateDrawingObjectsInfo = {target: c_oTargetType.ColumnResize, col: arn.c1}; }; oChangeData.hided = new asc_Range( arn.c1, 0, arn.c2, gc_nMaxRow0 ); this._isLockedAll( onChangeWorksheetCallback ); break; case "rowHeight": functionModelAction = function () { // Приводим к px (чтобы было ровно) val = val / 0.75; val = (val | val) * 0.75; // 0.75 - это размер 1px в pt (можно было 96/72) t.model.setRowHeight( Math.min( val, t.maxRowHeight ), checkRange.r1, checkRange.r2, true ); isUpdateRows = true; fullRecalc = true; reinitRanges = true; updateDrawingObjectsInfo = {target: c_oTargetType.RowResize, row: checkRange.r1}; }; return this._isLockedAll( onChangeWorksheetCallback ); case "showRows": functionModelAction = function () { t.model.setRowHidden( /*bHidden*/false, arn.r1, arn.r2 ); t.autoFilters.reDrawFilter( arn ); fullRecalc = true; reinitRanges = true; updateDrawingObjectsInfo = {target: c_oTargetType.RowResize, row: arn.r1}; }; oChangeData.hided = new asc_Range( 0, arn.r1, gc_nMaxCol0, arn.r2 ); this._isLockedAll( onChangeWorksheetCallback ); break; case "hideRows": functionModelAction = function () { t.model.setRowHidden( /*bHidden*/true, arn.r1, arn.r2 ); t.autoFilters.reDrawFilter( arn ); fullRecalc = true; reinitRanges = true; updateDrawingObjectsInfo = {target: c_oTargetType.RowResize, row: arn.r1}; }; oChangeData.hided = new asc_Range( 0, arn.r1, gc_nMaxCol0, arn.r2 ); this._isLockedAll( onChangeWorksheetCallback ); break; case "insCell": range = t.model.getRange3( arn.r1, arn.c1, arn.r2, arn.c2 ); switch ( val ) { case c_oAscInsertOptions.InsertCellsAndShiftRight: isCheckChangeAutoFilter = t.autoFilters.isActiveCellsCrossHalfFTable( arn, c_oAscInsertOptions.InsertCellsAndShiftRight, prop ); if ( isCheckChangeAutoFilter === false ) { return; } functionModelAction = function () { History.Create_NewPoint(); History.StartTransaction(); if ( range.addCellsShiftRight() ) { fullRecalc = true; reinitRanges = true; if ( isCheckChangeAutoFilter === true ) { t.autoFilters.insertColumn( prop, arn, c_oAscInsertOptions.InsertCellsAndShiftRight ); } t.cellCommentator.updateCommentsDependencies( true, val, arn ); t.objectRender.updateDrawingObject( true, val, arn ); } History.EndTransaction(); }; oChangeData.changedRange = new asc_Range( arn.c1, arn.r1, gc_nMaxCol0, arn.r2 ); this._isLockedCells( oChangeData.changedRange, null, onChangeWorksheetCallback ); break; case c_oAscInsertOptions.InsertCellsAndShiftDown: isCheckChangeAutoFilter = t.autoFilters.isActiveCellsCrossHalfFTable( arn, c_oAscInsertOptions.InsertCellsAndShiftDown, prop ); if ( isCheckChangeAutoFilter === false ) { return; } functionModelAction = function () { History.Create_NewPoint(); History.StartTransaction(); if ( range.addCellsShiftBottom() ) { fullRecalc = true; reinitRanges = true; if ( isCheckChangeAutoFilter === true ) { t.autoFilters.insertRows( prop, arn, c_oAscInsertOptions.InsertCellsAndShiftDown ); } t.cellCommentator.updateCommentsDependencies( true, val, arn ); t.objectRender.updateDrawingObject( true, val, arn ); } History.EndTransaction(); }; oChangeData.changedRange = new asc_Range( arn.c1, arn.r1, arn.c2, gc_nMaxRow0 ); this._isLockedCells( oChangeData.changedRange, null, onChangeWorksheetCallback ); break; case c_oAscInsertOptions.InsertColumns: isCheckChangeAutoFilter = t.autoFilters.isRangeIntersectionSeveralTableParts( arn, true ); if ( isCheckChangeAutoFilter === true ) { return; } functionModelAction = function () { History.Create_NewPoint(); History.StartTransaction(); fullRecalc = true; reinitRanges = true; t.model.insertColsBefore( arn.c1, arn.c2 - arn.c1 + 1 ); t.autoFilters.insertColumn( prop, arn, c_oAscInsertOptions.InsertColumns ); t.objectRender.updateDrawingObject( true, val, arn ); t.cellCommentator.updateCommentsDependencies( true, val, arn ); History.EndTransaction(); }; oChangeData.added = new asc_Range( arn.c1, 0, arn.c2, gc_nMaxRow0 ); this._isLockedCells( oChangeData.added, c_oAscLockTypeElemSubType.InsertColumns, onChangeWorksheetCallback ); break; case c_oAscInsertOptions.InsertRows: functionModelAction = function () { fullRecalc = true; reinitRanges = true; t.model.insertRowsBefore( arn.r1, arn.r2 - arn.r1 + 1 ); t.autoFilters.insertRows( prop, arn, c_oAscInsertOptions.InsertRows ); t.objectRender.updateDrawingObject( true, val, arn ); t.cellCommentator.updateCommentsDependencies( true, val, arn ); }; oChangeData.added = new asc_Range( 0, arn.r1, gc_nMaxCol0, arn.r2 ); this._isLockedCells( oChangeData.added, c_oAscLockTypeElemSubType.InsertRows, onChangeWorksheetCallback ); break; } break; case "delCell": range = t.model.getRange3( checkRange.r1, checkRange.c1, checkRange.r2, checkRange.c2 ); switch ( val ) { case c_oAscDeleteOptions.DeleteCellsAndShiftLeft: isCheckChangeAutoFilter = t.autoFilters.isActiveCellsCrossHalfFTable( arn, c_oAscDeleteOptions.DeleteCellsAndShiftLeft, prop ); if ( isCheckChangeAutoFilter === false ) { return; } functionModelAction = function () { History.Create_NewPoint(); History.StartTransaction(); if ( isCheckChangeAutoFilter === true ) { t.autoFilters.isEmptyAutoFilters( arn, c_oAscDeleteOptions.DeleteCellsAndShiftLeft ); } if ( range.deleteCellsShiftLeft( function () { t._cleanCache( oChangeData.changedRange ); t.cellCommentator.updateCommentsDependencies( false, val, checkRange ); } ) ) { // ToDo проверить, может перенести в функцию prechange if ( isCheckChangeAutoFilter === true ) { t.autoFilters.insertColumn( prop, checkRange, c_oAscDeleteOptions.DeleteCellsAndShiftLeft ); } t.objectRender.updateDrawingObject( false, val, checkRange ); } History.EndTransaction(); reinitRanges = true; }; oChangeData.changedRange = new asc_Range( checkRange.c1, checkRange.r1, gc_nMaxCol0, checkRange.r2 ); this._isLockedCells( oChangeData.changedRange, null, onChangeWorksheetCallback ); break; case c_oAscDeleteOptions.DeleteCellsAndShiftTop: isCheckChangeAutoFilter = t.autoFilters.isActiveCellsCrossHalfFTable( arn, c_oAscDeleteOptions.DeleteCellsAndShiftTop, prop ); if ( isCheckChangeAutoFilter === false ) { return; } functionModelAction = function () { History.Create_NewPoint(); History.StartTransaction(); if ( isCheckChangeAutoFilter === true ) { t.autoFilters.isEmptyAutoFilters( arn, c_oAscDeleteOptions.DeleteCellsAndShiftTop ); } if ( range.deleteCellsShiftUp( function () { t._cleanCache( oChangeData.changedRange ); t.cellCommentator.updateCommentsDependencies( false, val, checkRange ); } ) ) { // ToDo проверить, может перенести в функцию prechange if ( isCheckChangeAutoFilter === true ) { t.autoFilters.insertRows( prop, checkRange, c_oAscDeleteOptions.DeleteCellsAndShiftTop ); } t.objectRender.updateDrawingObject( false, val, checkRange ); } History.EndTransaction(); reinitRanges = true; }; oChangeData.changedRange = new asc_Range( checkRange.c1, checkRange.r1, checkRange.c2, gc_nMaxRow0 ); this._isLockedCells( oChangeData.changedRange, null, onChangeWorksheetCallback ); break; case c_oAscDeleteOptions.DeleteColumns: isCheckChangeAutoFilter = t.autoFilters.isActiveCellsCrossHalfFTable( checkRange, c_oAscDeleteOptions.DeleteColumns, prop ); if ( isCheckChangeAutoFilter === false ) { return; } functionModelAction = function () { fullRecalc = true; reinitRanges = true; History.Create_NewPoint(); History.StartTransaction(); t.cellCommentator.updateCommentsDependencies( false, val, checkRange ); t.autoFilters.isEmptyAutoFilters( arn, c_oAscDeleteOptions.DeleteColumns ); t.model.removeCols( checkRange.c1, checkRange.c2 ); t.autoFilters.insertColumn( prop, arn, c_oAscDeleteOptions.DeleteColumns ); t.objectRender.updateDrawingObject( false, val, checkRange ); History.EndTransaction(); }; oChangeData.removed = new asc_Range( checkRange.c1, 0, checkRange.c2, gc_nMaxRow0 ); this._isLockedCells( oChangeData.removed, c_oAscLockTypeElemSubType.DeleteColumns, onChangeWorksheetCallback ); break; case c_oAscDeleteOptions.DeleteRows: isCheckChangeAutoFilter = t.autoFilters.isActiveCellsCrossHalfFTable( checkRange, c_oAscDeleteOptions.DeleteRows, prop ); if ( isCheckChangeAutoFilter === false ) { return; } functionModelAction = function () { fullRecalc = true; reinitRanges = true; History.Create_NewPoint(); History.StartTransaction(); t.cellCommentator.updateCommentsDependencies( false, val, checkRange ); t.autoFilters.isEmptyAutoFilters( arn, c_oAscDeleteOptions.DeleteRows ); t.model.removeRows( checkRange.r1, checkRange.r2 ); t.autoFilters.insertRows( prop, checkRange, c_oAscDeleteOptions.DeleteRows ); t.objectRender.updateDrawingObject( false, val, checkRange ); History.EndTransaction(); }; oChangeData.removed = new asc_Range( 0, checkRange.r1, gc_nMaxCol0, checkRange.r2 ); this._isLockedCells( oChangeData.removed, c_oAscLockTypeElemSubType.DeleteRows, onChangeWorksheetCallback ); break; } this.handlers.trigger( "selectionNameChanged", t.getSelectionName( /*bRangeText*/false ) ); break; case "sheetViewSettings": functionModelAction = function () { t.model.setSheetViewSettings( val ); isUpdateCols = true; isUpdateRows = true; fullRecalc = true; reinitRanges = true; }; this._isLockedAll( onChangeWorksheetCallback ); break; case "update": if ( val !== undefined ) { fullRecalc = !!val.fullRecalc; lockDraw = true === val.lockDraw; reinitRanges = !!val.reinitRanges; } onChangeWorksheetCallback( true ); break; } }; WorksheetView.prototype.expandColsOnScroll = function ( isNotActive, updateColsCount, newColsCount ) { var t = this; var arn; var bIsMaxCols = false; var obr = this.objectRender ? this.objectRender.getDrawingAreaMetrics() : {maxCol: 0, maxRow: 0}; var maxc = Math.max( this.model.getColsCount() + 1, this.cols.length, obr.maxCol ); if ( newColsCount ) { maxc = Math.max( maxc, newColsCount ); } // Сохраняем старое значение var nLastCols = this.nColsCount; if ( isNotActive ) { this.nColsCount = maxc + 1; } else if ( updateColsCount ) { this.nColsCount = maxc; if ( this.cols.length < this.nColsCount ) { nLastCols = this.cols.length; } } else { arn = t.activeRange.clone( true ); if ( arn.c2 >= t.cols.length - 1 ) { this.nColsCount = maxc; if ( arn.c2 >= this.nColsCount - 1 ) { this.nColsCount = arn.c2 + 2; } } } // Проверяем ограничения по столбцам if ( gc_nMaxCol < this.nColsCount ) { this.nColsCount = gc_nMaxCol; bIsMaxCols = true; } t._calcWidthColumns( /*fullRecalc*/2 ); if ( this.objectRender && this.objectRender.drawingArea ) { this.objectRender.drawingArea.reinitRanges(); } return (nLastCols !== this.nColsCount || bIsMaxCols); }; WorksheetView.prototype.expandRowsOnScroll = function ( isNotActive, updateRowsCount, newRowsCount ) { var t = this; var arn; var bIsMaxRows = false; var obr = this.objectRender ? this.objectRender.getDrawingAreaMetrics() : {maxCol: 0, maxRow: 0}; var maxr = Math.max( this.model.getRowsCount() + 1, this.rows.length, obr.maxRow ); if ( newRowsCount ) { maxr = Math.max( maxr, newRowsCount ); } // Сохраняем старое значение var nLastRows = this.nRowsCount; if ( isNotActive ) { this.nRowsCount = maxr + 1; } else if ( updateRowsCount ) { this.nRowsCount = maxr; if ( this.rows.length < this.nRowsCount ) { nLastRows = this.rows.length; } } else { arn = t.activeRange.clone( true ); if ( arn.r2 >= t.rows.length - 1 ) { this.nRowsCount = maxr; if ( arn.r2 >= this.nRowsCount - 1 ) { this.nRowsCount = arn.r2 + 2; } } } // Проверяем ограничения по строкам if ( gc_nMaxRow < this.nRowsCount ) { this.nRowsCount = gc_nMaxRow; bIsMaxRows = true; } t._calcHeightRows( /*fullRecalc*/2 ); if ( this.objectRender && this.objectRender.drawingArea ) { this.objectRender.drawingArea.reinitRanges(); } return (nLastRows !== this.nRowsCount || bIsMaxRows); }; WorksheetView.prototype.onChangeWidthCallback = function ( col, r1, r2, onlyIfMore ) { var width = null; var row, ct, c, fl, str, maxW, tm, mc, isMerged, oldWidth, oldColWidth; var lastHeight = null; var filterButton = null; if ( null == r1 ) { r1 = 0; } if ( null == r2 ) { r2 = this.rows.length - 1; } oldColWidth = this.cols[col].charCount; this.cols[col].isCustomWidth = false; for ( row = r1; row <= r2; ++row ) { // пересчет метрик текста this._addCellTextToCache( col, row, /*canChangeColWidth*/c_oAscCanChangeColWidth.all ); ct = this._getCellTextCache( col, row ); if ( ct === undefined ) { continue; } fl = ct.flags; isMerged = fl.isMerged(); if ( isMerged ) { mc = fl.merged; // Для замерженных ячеек (с 2-мя или более колонками) оптимизировать не нужно if ( mc.c1 !== mc.c2 ) { continue; } } if ( ct.metrics.height > this.maxRowHeight ) { if ( isMerged ) { continue; } // Запоминаем старую ширину (в случае, если у нас по высоте не уберется) oldWidth = ct.metrics.width; lastHeight = null; // вычисление новой ширины столбца, чтобы высота текста была меньше maxRowHeight c = this._getCell( col, row ); str = c.getValue2(); maxW = ct.metrics.width + this.maxDigitWidth; while ( 1 ) { tm = this._roundTextMetrics( this.stringRender.measureString( str, fl, maxW ) ); if ( tm.height <= this.maxRowHeight ) { break; } if ( lastHeight === tm.height ) { // Ситуация, когда у нас текст не уберется по высоте (http://bugzserver/show_bug.cgi?id=19974) tm.width = oldWidth; break; } lastHeight = tm.height; maxW += this.maxDigitWidth; } width = Math.max( width, tm.width ); } else { filterButton = this.autoFilters.getSizeButton( col, row ); if ( null !== filterButton && CellValueType.String === ct.cellType ) { width = Math.max( width, ct.metrics.width + filterButton.width ); } else { width = Math.max( width, ct.metrics.width ); } } } var pad, cc, cw; if ( width > 0 ) { pad = this.width_padding * 2 + this.width_1px; cc = Math.min( this._colWidthToCharCount( width + pad ), c_oAscMaxColumnWidth ); cw = this._charCountToModelColWidth( cc ); } else { cw = gc_dDefaultColWidthCharsAttribute; cc = this.defaultColWidthChars; } if ( onlyIfMore && cc < oldColWidth ) { return -1; } History.Create_NewPoint(); if ( !onlyIfMore ) { var oSelection = History.GetSelection(); if ( null != oSelection ) { oSelection = oSelection.clone(); oSelection.assign( col, 0, col, gc_nMaxRow0 ); oSelection.type = c_oAscSelectionType.RangeCol; History.SetSelection( oSelection ); History.SetSelectionRedo( oSelection ); } } History.StartTransaction(); // Выставляем, что это bestFit this.model.setColBestFit( true, cw, col, col ); History.EndTransaction(); return oldColWidth !== cc ? cw : -1; }; WorksheetView.prototype.optimizeColWidth = function ( col ) { var t = this; return this._isLockedAll( function ( isSuccess ) { if ( false === isSuccess ) { return; } var w = t.onChangeWidthCallback( col, null, null ); if ( -1 !== w ) { t.cols[col] = t._calcColWidth( w ); t.cols[col].isCustomWidth = false; t._updateColumnPositions(); t._updateVisibleColsCount(); t._cleanCache( new asc_Range( col, 0, col, t.rows.length - 1 ) ); t.changeWorksheet( "update" ); } } ); }; WorksheetView.prototype.optimizeRowHeight = function ( row ) { var t = this; var onChangeHeightCallback = function ( isSuccess ) { if ( false === isSuccess ) { return; } var height = t.defaultRowHeight; var col, ct, mc; for ( col = 0; col < t.rows.length; ++col ) { ct = t._getCellTextCache( col, row ); if ( ct === undefined ) { continue; } if ( ct.flags.isMerged() ) { mc = ct.flags.merged; // Для замерженных ячеек (с 2-мя или более строками) оптимизировать не нужно if ( mc.r1 !== mc.r2 ) { continue; } } height = Math.max( height, ct.metrics.height ); } History.Create_NewPoint(); var oSelection = History.GetSelection(); if ( null != oSelection ) { oSelection = oSelection.clone(); oSelection.assign( 0, row, gc_nMaxCol0, row ); oSelection.type = c_oAscSelectionType.RangeRow; History.SetSelection( oSelection ); History.SetSelectionRedo( oSelection ); } History.StartTransaction(); // Выставляем, что это bestFit t.model.setRowBestFit( true, Math.min( height + t.height_1px, t.maxRowHeight ), row, row ); History.EndTransaction(); t.nRowsCount = 0; t._calcHeightRows( /*fullRecalc*/0 ); t._updateVisibleRowsCount(); t._cleanCache( new asc_Range( 0, row, t.cols.length - 1, row ) ); t.changeWorksheet( "update" ); }; return this._isLockedAll( onChangeHeightCallback ); }; // ----- Search ----- WorksheetView.prototype._setActiveCell = function ( col, row ) { var ar = this.activeRange, sc = ar.startCol, sr = ar.startRow, offs; this.cleanSelection(); ar.assign( col, row, col, row ); ar.type = c_oAscSelectionType.RangeCells; ar.startCol = col; ar.startRow = row; this._fixSelectionOfMergedCells(); this._fixSelectionOfHiddenCells(); this._drawSelection(); var x1 = this.getCellLeftRelative( this.activeRange.c1, /*pt*/0 ), y1 = this.getCellTopRelative( this.activeRange.r1, /*pt*/0 ); offs = this._calcActiveRangeOffset( x1, y1 ); if ( sc !== ar.startCol || sr !== ar.startRow ) { this.handlers.trigger( "selectionNameChanged", this.getSelectionName( /*bRangeText*/false ) ); this.handlers.trigger( "selectionChanged", this.getSelectionInfo() ); } return offs; }; WorksheetView.prototype._isCellEqual = function ( c, r, options ) { var cell, cellText; // Не пользуемся RegExp, чтобы не возиться со спец.символами var mc = this.model.getMergedByCell( r, c ); cell = mc ? this._getVisibleCell( mc.c1, mc.r1 ) : this._getVisibleCell( c, r ); cellText = (options.lookIn === c_oAscFindLookIn.Formulas) ? cell.getValueForEdit() : cell.getValue(); if ( true !== options.isMatchCase ) { cellText = cellText.toLowerCase(); } if ( (cellText.indexOf( options.findWhat ) >= 0) && (true !== options.isWholeCell || options.findWhat.length === cellText.length) ) { return (mc ? new asc_Range( mc.c1, mc.r1, mc.c1, mc.r1 ) : new asc_Range( c, r, c, r )); } return null; }; WorksheetView.prototype.findCellText = function ( options ) { var self = this; if ( true !== options.isMatchCase ) { options.findWhat = options.findWhat.toLowerCase(); } var ar = options.activeRange ? options.activeRange : this.activeRange; var c = ar.startCol; var r = ar.startRow; var minC = 0; var minR = 0; var maxC = this.cols.length - 1; var maxR = this.rows.length - 1; var inc = options.scanForward ? +1 : -1; var isEqual; // ToDo стоит переделать это место, т.к. для поиска не нужны измерения, а нужен только сам текст (http://bugzserver/show_bug.cgi?id=26136) this._prepareCellTextMetricsCache( new Asc.Range( 0, 0, this.model.getColsCount(), this.model.getRowsCount() ) ); function findNextCell() { var ct = undefined; do { if ( options.scanByRows ) { c += inc; if ( c < minC || c > maxC ) { c = options.scanForward ? minC : maxC; r += inc; } } else { r += inc; if ( r < minR || r > maxR ) { r = options.scanForward ? minR : maxR; c += inc; } } if ( c < minC || c > maxC || r < minR || r > maxR ) { return undefined; } ct = self._getCellTextCache( c, r, true ); } while ( !ct ); return ct; } while ( findNextCell() ) { isEqual = this._isCellEqual( c, r, options ); if ( null !== isEqual ) { return isEqual; } } // Продолжаем циклический поиск if ( options.scanForward ) { // Идем вперед с первой ячейки minC = 0; minR = 0; if ( options.scanByRows ) { c = -1; r = 0; maxC = this.cols.length - 1; maxR = ar.startRow; } else { c = 0; r = -1; maxC = ar.startCol; maxR = this.rows.length - 1; } } else { // Идем назад с последней c = this.cols.length - 1; r = this.rows.length - 1; if ( options.scanByRows ) { minC = 0; minR = ar.startRow; } else { minC = ar.startCol; minR = 0; } maxC = this.cols.length - 1; maxR = this.rows.length - 1; } while ( findNextCell() ) { isEqual = this._isCellEqual( c, r, options ); if ( null !== isEqual ) { return isEqual; } } return null; }; WorksheetView.prototype.replaceCellText = function ( options, lockDraw, callback ) { if ( true !== options.isMatchCase ) { options.findWhat = options.findWhat.toLowerCase(); } // Очищаем результаты options.countFind = 0; options.countReplace = 0; var t = this; var ar = this.activeRange.clone(); var aReplaceCells = []; if ( options.isReplaceAll ) { var aReplaceCellsIndex = {}; options.activeRange = ar; var findResult, index; while ( true ) { findResult = t.findCellText( options ); if ( null === findResult ) { break; } index = findResult.c1 + '-' + findResult.r1; if ( aReplaceCellsIndex[index] ) { break; } aReplaceCellsIndex[index] = true; aReplaceCells.push( findResult ); ar.startCol = findResult.c1; ar.startRow = findResult.r1; } } else { // Попробуем сначала найти var isEqual = this._isCellEqual( ar.startCol, ar.startRow, options ); if ( null === isEqual ) { return callback( options ); } aReplaceCells.push( isEqual ); } if ( 0 > aReplaceCells.length ) { return callback( options ); } return this._replaceCellsText( aReplaceCells, options, lockDraw, callback ); }; WorksheetView.prototype._replaceCellsText = function ( aReplaceCells, options, lockDraw, callback ) { var t = this; var findFlags = "g"; // Заменяем все вхождения if ( true !== options.isMatchCase ) { findFlags += "i"; } // Не чувствителен к регистру var valueForSearching = options.findWhat .replace( /(\\)/g, "\\\\" ).replace( /(\^)/g, "\\^" ) .replace( /(\()/g, "\\(" ).replace( /(\))/g, "\\)" ) .replace( /(\+)/g, "\\+" ).replace( /(\[)/g, "\\[" ) .replace( /(\])/g, "\\]" ).replace( /(\{)/g, "\\{" ) .replace( /(\})/g, "\\}" ).replace( /(\$)/g, "\\$" ) .replace( /(~)?\*/g, function ( $0, $1 ) { return $1 ? $0 : '(.*)'; } ) .replace( /(~)?\?/g, function ( $0, $1 ) { return $1 ? $0 : '.'; } ) .replace( /(~\*)/g, "\\*" ).replace( /(~\?)/g, "\\?" ); valueForSearching = new RegExp( valueForSearching, findFlags ); options.indexInArray = 0; options.countFind = aReplaceCells.length; options.countReplace = 0; if ( options.isReplaceAll && false === this.collaborativeEditing.getCollaborativeEditing() ) { this._isLockedCells( aReplaceCells, /*subType*/null, function () { t._replaceCellText( aReplaceCells, valueForSearching, options, lockDraw, callback, true ); } ); } else { this._replaceCellText( aReplaceCells, valueForSearching, options, lockDraw, callback, false ); } }; WorksheetView.prototype._replaceCellText = function ( aReplaceCells, valueForSearching, options, lockDraw, callback, oneUser ) { var t = this; if ( options.indexInArray >= aReplaceCells.length ) { this.draw( lockDraw ); return callback( options ); } var onReplaceCallback = function ( isSuccess ) { var cell = aReplaceCells[options.indexInArray]; ++options.indexInArray; if ( false !== isSuccess ) { ++options.countReplace; var c = t._getVisibleCell( cell.c1, cell.r1 ); if ( c === undefined ) { asc_debug( "log", "Unknown cell's info: col = " + cell.c1 + ", row = " + cell.r1 ); } else { var cellValue = c.getValueForEdit(); cellValue = cellValue.replace( valueForSearching, options.replaceWith ); var oCellEdit = new asc_Range( cell.c1, cell.r1, cell.c1, cell.r1 ); var v, newValue; // get first fragment and change its text v = c.getValueForEdit2().slice( 0, 1 ); // Создаем новый массив, т.к. getValueForEdit2 возвращает ссылку newValue = []; newValue[0] = new Fragment( {text: cellValue, format: v[0].format.clone()} ); t._saveCellValueAfterEdit( oCellEdit, c, newValue, /*flags*/undefined, /*skipNLCheck*/false, /*isNotHistory*/true, /*lockDraw*/true ); } } window.setTimeout( function () { t._replaceCellText( aReplaceCells, valueForSearching, options, lockDraw, callback, oneUser ); }, 1 ); }; return oneUser ? onReplaceCallback( true ) : this._isLockedCells( aReplaceCells[options.indexInArray], /*subType*/null, onReplaceCallback ); }; WorksheetView.prototype.findCell = function ( reference ) { var range = asc.g_oRangeCache.getAscRange( reference ); if ( !range ) { var _C2H50H_ = this.model.workbook.getDefinesNames( reference, this.model.workbook.getActiveWs().getId() ), sheetName, ref; if ( !_C2H50H_ ) { var actRange = this.getActiveRangeObj(), ascRange, mc = this.model.getMergedByCell( actRange.startRow, actRange.startCol ), c1 = mc ? mc.c1 : actRange.c1, r1 = mc ? mc.r1 : actRange.r1, ar_norm = actRange.normalize(), mc_norm = mc ? mc.normalize() : null, c2 = mc_norm ? ( mc_norm.isEqual( ar_norm ) ? mc_norm.c1 : ar_norm.c2 ) : ar_norm.c2, r2 = mc_norm ? ( mc_norm.isEqual( ar_norm ) ? mc_norm.r1 : ar_norm.r2 ) : ar_norm.r2, defName; ascRange = new asc_Range( c1, r1, c2, r2 ); /*TODO: сделать поиск по названиям автофигур, должен искать до того как вызвать поиск по именованным диапазонам*/ defName = new Asc.asc_CDefName( reference, this.model.getName() + "!" + ascRange.getAbsName() ); if ( this.collaborativeEditing.getGlobalLock() || !this.handlers.trigger( "getLockDefNameManagerStatus" ) ) { this.handlers.trigger( "onErrorEvent", c_oAscError.ID.LockCreateDefName, c_oAscError.Level.NoCritical ); this._updateSelectionNameAndInfo(); return true; } _C2H50H_ = this.model.workbook.editDefinesNames( null, defName ); } if ( _C2H50H_ ) { range = true; this._isLockedDefNames( null, _C2H50H_.nodeId ); if ( _C2H50H_.isTable ) { sheetName = _C2H50H_.Ref.split( "!" ); ref = sheetName[1]; sheetName = sheetName[0]; if ( sheetName[0] == "'" && sheetName[sheetName.length - 1] == "'" ) { sheetName = sheetName.substring( 1, sheetName.length - 1 ); } range = asc.g_oRangeCache.getAscRange( ref ); sheetName = this.model.workbook.getWorksheetByName( sheetName ); } else if ( _C2H50H_.parsedRef.RefPos.length == 1 && _C2H50H_.parsedRef.outStack.length == 1 ) { ref = _C2H50H_.parsedRef.outStack[0]; if ( ref.type == cElementType.cell3D ) { range = ref.range.getBBox0().clone( true ); sheetName = ref.getWS(); } else if ( ref.type == cElementType.cellsRange3D && ref.wsFrom == ref.wsTo ) { range = ref.getRange()[0].getBBox0().clone( true ); sheetName = this.model.workbook.getWorksheetById( ref.wsFrom ); } } if ( range && sheetName ) { ar_norm = range.normalize(); mc = sheetName.getMergedByCell( ar_norm.r1, ar_norm.c1 ) range = {range: mc ? mc : range, sheet: sheetName.getName()}; } } } else { var ar_norm = range.normalize(), mc = this.model.getMergedByCell( ar_norm.r1, ar_norm.c1 ); range = {range: mc ? mc : range, sheet: this.model.getName()}; } return range;// ? this.setSelection(range, true) : null; }; /* Ищет дополнение для ячейки */ WorksheetView.prototype.getCellAutoCompleteValues = function ( col, row, maxCount ) { var arrValues = [], objValues = {}; var range = this.findCellAutoComplete( col, row, 1, maxCount ); this.getColValues( range, col, arrValues, objValues ); range = this.findCellAutoComplete( col, row, -1, maxCount ); this.getColValues( range, col, arrValues, objValues ); arrValues.sort(); return arrValues; }; /* Ищет дополнение для ячейки (снизу или сверху) */ WorksheetView.prototype.findCellAutoComplete = function ( col, row, step, maxCount ) { row += step; if ( !maxCount ) { maxCount = Number.MAX_VALUE; } var count = 0, cell, end = 0 < step ? this.model.getRowsCount() - 1 : 0, isEnd = true, colsCount = this.model.getColsCount(), range = new asc_Range( col, row, col, row ); for ( ; row * step <= end && count < maxCount; row += step, isEnd = true, ++count ) { for ( col = range.c1; col <= range.c2; ++col ) { cell = this.model._getCellNoEmpty( row, col ); if ( cell && false === cell.isEmptyText() ) { isEnd = false; break; } } // Идем влево по колонкам for ( col = range.c1 - 1; col >= 0; --col ) { cell = this.model._getCellNoEmpty( row, col ); if ( null === cell || cell.isEmptyText() ) { break; } isEnd = false; } range.c1 = col + 1; // Идем вправо по колонкам for ( col = range.c2 + 1; col < colsCount; ++col ) { cell = this.model._getCellNoEmpty( row, col ); if ( null === cell || cell.isEmptyText() ) { break; } isEnd = false; } range.c2 = col - 1; if ( isEnd ) { break; } } if ( 0 < step ) { range.r2 = row - 1; } else { range.r1 = row + 1; } return range.r1 <= range.r2 ? range : null; }; /* Формирует уникальный массив */ WorksheetView.prototype.getColValues = function ( range, col, arrValues, objValues ) { if ( null === range ) { return; } var row, cell, value, valueLowCase; for ( row = range.r1; row <= range.r2; ++row ) { cell = this.model._getCellNoEmpty( row, col ); if ( cell ) { value = cell.getValue(); if ( !isNumber( value ) ) { valueLowCase = value.toLowerCase(); if ( !objValues.hasOwnProperty( valueLowCase ) ) { arrValues.push( value ); objValues[valueLowCase] = 1; } } } } }; // ----- Cell Editor ----- WorksheetView.prototype.setCellEditMode = function ( isCellEditMode ) { this.isCellEditMode = isCellEditMode; }; WorksheetView.prototype.setFormulaEditMode = function ( isFormulaEditMode ) { this.isFormulaEditMode = isFormulaEditMode; }; WorksheetView.prototype.setSelectionDialogMode = function ( selectionDialogType, selectRange ) { if ( selectionDialogType === this.selectionDialogType ) { return; } var oldSelectionDialogType = this.selectionDialogType; this.selectionDialogType = selectionDialogType; this.isSelectionDialogMode = c_oAscSelectionDialogType.None !== this.selectionDialogType; this.cleanSelection(); if ( false === this.isSelectionDialogMode ) { if ( null !== this.copyActiveRange ) { this.activeRange = this.copyActiveRange.clone( true ); } this.copyActiveRange = null; if ( oldSelectionDialogType === c_oAscSelectionDialogType.Chart ) { this.objectRender.controller.checkChartForProps( false ); } } else { this.copyActiveRange = this.activeRange.clone( true ); if ( selectRange ) { if ( typeof selectRange === 'string' ) { selectRange = this.model.getRange2( selectRange ); if ( selectRange ) { selectRange = selectRange.getBBox0(); } } if ( null != selectRange ) { this.activeRange.assign( selectRange.c1, selectRange.r1, selectRange.c2, selectRange.r2 ); } } if ( selectionDialogType === c_oAscSelectionDialogType.Chart ) { this.objectRender.controller.checkChartForProps( true ); } } this._drawSelection(); }; // Получаем свойство: редактируем мы сейчас или нет WorksheetView.prototype.getCellEditMode = function () { return this.isCellEditMode; }; WorksheetView.prototype._isFormula = function ( val ) { return (0 < val.length && 1 < val[0].text.length && '=' === val[0].text.charAt( 0 )); }; WorksheetView.prototype.getActiveCell = function ( x, y, isCoord ) { var t = this; var col, row; if ( isCoord ) { x *= asc_getcvt( 0/*px*/, 1/*pt*/, t._getPPIX() ); y *= asc_getcvt( 0/*px*/, 1/*pt*/, t._getPPIY() ); col = t._findColUnderCursor( x, true ); row = t._findRowUnderCursor( y, true ); if ( !col || !row ) { return false; } col = col.col; row = row.row; } else { col = t.activeRange.startCol; row = t.activeRange.startRow; } // Проверим замерженность var mergedRange = this.model.getMergedByCell( row, col ); return mergedRange ? mergedRange : new asc_Range( col, row, col, row ); }; WorksheetView.prototype._saveCellValueAfterEdit = function ( oCellEdit, c, val, flags, skipNLCheck, isNotHistory, lockDraw ) { var t = this; var oldMode = t.isFormulaEditMode; t.isFormulaEditMode = false; if ( !isNotHistory ) { History.Create_NewPoint(); History.StartTransaction(); } var isFormula = t._isFormula( val ); if ( isFormula ) { var ftext = val.reduce( function ( pv, cv ) { return pv + cv.text; }, "" ); var ret = true; // ToDo - при вводе формулы в заголовок автофильтра надо писать "0" c.setValue( ftext, function ( r ) { ret = r; } ); if ( !ret ) { t.isFormulaEditMode = oldMode; History.EndTransaction(); return false; } isFormula = c.isFormula(); } else { c.setValue2( val ); // Вызываем функцию пересчета для заголовков форматированной таблицы t.autoFilters.renameTableColumn( oCellEdit ); } if ( !isFormula ) { // Нужно ли выставлять WrapText (ищем символ новой строки в тексте) var bIsSetWrap = false; if ( !skipNLCheck ) { for ( var i = 0; i < val.length; ++i ) { if ( val[i].text.indexOf( kNewLine ) >= 0 ) { bIsSetWrap = true; break; } } } if ( bIsSetWrap ) { c.setWrap( true ); } // Для формулы обновление будет в коде рассчета формулы t._updateCellsRange( oCellEdit, /*canChangeColWidth*/c_oAscCanChangeColWidth.numbers, lockDraw ); } if ( !isNotHistory ) { History.EndTransaction(); } // если вернуть false, то редактор не закроется return true; }; WorksheetView.prototype.openCellEditor = function ( editor, x, y, isCoord, fragments, cursorPos, isFocus, isClearCell, isHideCursor, isQuickInput, activeRange ) { var t = this, tc = this.cols, tr = this.rows, col, row, c, fl, mc, bg, isMerged; var ar = this.activeRange; if ( activeRange ) { this.activeRange = activeRange.clone(); } if ( this.objectRender.checkCursorDrawingObject( x, y ) ) { return false; } function getVisibleRangeObject() { var vr = t.visibleRange.clone(), offsetX = 0, offsetY = 0; if ( t.topLeftFrozenCell ) { var cFrozen = t.topLeftFrozenCell.getCol0(); var rFrozen = t.topLeftFrozenCell.getRow0(); if ( 0 < cFrozen ) { if ( col >= cFrozen ) { offsetX = tc[cFrozen].left - tc[0].left; } else { vr.c1 = 0; vr.c2 = cFrozen - 1; } } if ( 0 < rFrozen ) { if ( row >= rFrozen ) { offsetY = tr[rFrozen].top - tr[0].top; } else { vr.r1 = 0; vr.r2 = rFrozen - 1; } } } return {vr: vr, offsetX: offsetX, offsetY: offsetY}; } if ( isCoord ) { x *= asc_getcvt( 0/*px*/, 1/*pt*/, this._getPPIX() ); y *= asc_getcvt( 0/*px*/, 1/*pt*/, this._getPPIY() ); col = this._findColUnderCursor( x, true ); row = this._findRowUnderCursor( y, true ); if ( !col || !row ) { return false; } col = col.col; row = row.row; } else { col = ar.startCol; row = ar.startRow; } // Возможно стоит заменить на ячейку из кеша c = this._getVisibleCell( col, row ); if ( !c ) { throw "Can not get cell data (col=" + col + ", row=" + row + ")"; } fl = this._getCellFlags( c ); isMerged = fl.isMerged(); if ( isMerged ) { mc = fl.merged; c = this._getVisibleCell( mc.c1, mc.r1 ); if ( !c ) { throw "Can not get merged cell data (col=" + mc.c1 + ", row=" + mc.r1 + ")"; } fl = this._getCellFlags( c ); } // Выставляем режим 'не редактируем' (иначе мы попытаемся переместить редактор, который еще не открыт) this.isCellEditMode = false; this.handlers.trigger( "onScroll", this._calcActiveCellOffset() ); this.isCellEditMode = true; bg = c.getFill(); this.isFormulaEditMode = false; // Очищаем массив ячеек для текущей формулы this.arrActiveFormulaRanges = []; var oFontColor = c.getFontcolor(); // Скрываем окно редактирования комментария this.model.workbook.handlers.trigger( "asc_onHideComment" ); if ( fragments === undefined ) { var _fragmentsTmp = c.getValueForEdit2(); fragments = []; for ( var i = 0; i < _fragmentsTmp.length; ++i ) fragments.push( _fragmentsTmp[i].clone() ); } var arrAutoComplete = this.getCellAutoCompleteValues( col, row, kMaxAutoCompleteCellEdit ); var arrAutoCompleteLC = asc.arrayToLowerCase( arrAutoComplete ); editor.open( { fragments : fragments, flags : fl, font : new asc.FontProperties( c.getFontname(), c.getFontsize() ), background : bg || this.settings.cells.defaultState.background, textColor : oFontColor || this.settings.cells.defaultState.color, cursorPos : cursorPos, zoom : this.getZoom(), focus : isFocus, isClearCell : isClearCell, isHideCursor : isHideCursor, isQuickInput : isQuickInput, isAddPersentFormat: isQuickInput && c_oAscNumFormatType.Percent === c.getNumFormat().getType(), autoComplete : arrAutoComplete, autoCompleteLC : arrAutoCompleteLC, saveValueCallback : function ( val, flags, skipNLCheck ) { var oCellEdit = isMerged ? new asc_Range( mc.c1, mc.r1, mc.c1, mc.r1 ) : new asc_Range( col, row, col, row ); return t._saveCellValueAfterEdit( oCellEdit, c, val, flags, skipNLCheck, /*isNotHistory*/false, /*lockDraw*/false ); }, getSides : function () { var _col = !isMerged ? col : mc.c1; var _row = !isMerged ? row : mc.r2; var vro = getVisibleRangeObject(); var i, w, h, arrLeftS = [], arrRightS = [], arrBottomS = []; var offsX = tc[vro.vr.c1].left - tc[0].left - vro.offsetX; var offsY = tr[vro.vr.r1].top - tr[0].top - vro.offsetY; var cellX = tc[_col].left - offsX, cellY = tr[!isMerged ? row : mc.r1].top - offsY; for ( i = _col; i >= vro.vr.c1; --i ) { if ( t.width_1px < tc[i].width ) { arrLeftS.push( tc[i].left - offsX ); } } arrLeftS.sort( fSortDescending ); // Для замерженных ячеек, можем уйти за границу if ( isMerged && _col > vro.vr.c2 ) { _col = vro.vr.c2; } for ( i = _col; i <= vro.vr.c2; ++i ) { w = tc[i].width; if ( t.width_1px < w ) { arrRightS.push( tc[i].left + w - offsX ); } } w = t.drawingCtx.getWidth(); if ( arrRightS[arrRightS.length - 1] > w ) { arrRightS[arrRightS.length - 1] = w; } arrRightS.sort( fSortAscending ); // Для замерженных ячеек, можем уйти за границу if ( isMerged && _row > vro.vr.r2 ) { _row = vro.vr.r2; } for ( i = _row; i <= vro.vr.r2; ++i ) { h = tr[i].height; if ( t.height_1px < h ) { arrBottomS.push( tr[i].top + h - offsY ); } } h = t.drawingCtx.getHeight(); if ( arrBottomS[arrBottomS.length - 1] > h ) { arrBottomS[arrBottomS.length - 1] = h; } arrBottomS.sort( fSortAscending ); return {l: arrLeftS, r: arrRightS, b: arrBottomS, cellX: cellX, cellY: cellY}; } } ); return true; }; WorksheetView.prototype.openCellEditorWithText = function ( editor, text, cursorPos, isFocus, activeRange ) { var t = this; var ar = (activeRange) ? activeRange : t.activeRange; var c = t._getVisibleCell( ar.startCol, ar.startRow ); var v, copyValue; if ( !c ) { throw "Can not get cell data (col=" + ar.startCol + ", row=" + ar.startCol + ")"; } // get first fragment and change its text v = c.getValueForEdit2().slice( 0, 1 ); // Создаем новый массив, т.к. getValueForEdit2 возвращает ссылку copyValue = []; copyValue[0] = new Fragment( {text: text, format: v[0].format.clone()} ); var bSuccess = t.openCellEditor( editor, 0, 0, /*isCoord*/false, /*fragments*/undefined, /*cursorPos*/undefined, isFocus, /*isClearCell*/true, /*isHideCursor*/false, /*isQuickInput*/false, activeRange ); if ( bSuccess ) { editor.paste( copyValue, cursorPos ); } return bSuccess; }; WorksheetView.prototype.getFormulaRanges = function () { return this.arrActiveFormulaRanges; }; /** * * @param {Object} ranges * @param canChangeColWidth * @param lockDraw * @param updateHeight */ WorksheetView.prototype.updateRanges = function ( ranges, canChangeColWidth, lockDraw, updateHeight ) { var arrRanges = [], range; for ( var i in ranges ) { range = ranges[i]; this.updateRange( range, canChangeColWidth, true ); arrRanges.push( range ); } if ( 0 < arrRanges.length ) { if ( updateHeight ) { this.isChanged = true; } this._recalculateAfterUpdate( arrRanges, lockDraw ); } }; // ToDo избавиться от этой функции!!!!Заглушка для принятия изменений WorksheetView.prototype._checkUpdateRange = function ( range ) { // Для принятия изменения нужно делать расширение диапазона if ( this.model.workbook.bCollaborativeChanges ) { var bIsUpdateX = false, bIsUpdateY = false; if ( range.c2 >= this.nColsCount ) { this.expandColsOnScroll( false, true, 0 ); // Передаем 0, чтобы увеличить размеры // Проверка, вдруг пришел диапазон за пределами существующей области if ( range.c2 >= this.nColsCount ) { if ( range.c1 >= this.nColsCount ) { return; } range.c2 = this.nColsCount - 1; } bIsUpdateX = true; } if ( range.r2 >= this.nRowsCount ) { this.expandRowsOnScroll( false, true, 0 ); // Передаем 0, чтобы увеличить размеры // Проверка, вдруг пришел диапазон за пределами существующей области if ( range.r2 >= this.nRowsCount ) { if ( range.r1 >= this.nRowsCount ) { return; } range.r2 = this.nRowsCount - 1; } bIsUpdateY = true; } if ( bIsUpdateX && bIsUpdateY ) { this.handlers.trigger( "reinitializeScroll" ); } else if ( bIsUpdateX ) { this.handlers.trigger( "reinitializeScrollX" ); } else if ( bIsUpdateY ) { this.handlers.trigger( "reinitializeScrollY" ); } } }; WorksheetView.prototype.updateRange = function ( range, canChangeColWidth, lockDraw ) { this._checkUpdateRange( range ); this._updateCellsRange( range, canChangeColWidth, lockDraw ); }; WorksheetView.prototype._updateCellsRange = function ( range, canChangeColWidth, lockDraw ) { var r, c, h, d, ct, isMerged; var mergedRange, bUpdateRowHeight; if ( range === undefined ) { range = this.activeRange.clone( true ); } else { // ToDo заглушка..пора уже переделать обновление данных if ( range.r1 >= this.nRowsCount || range.c1 >= this.nColsCount ) { return; } range.r2 = Math.min( range.r2, this.nRowsCount - 1 ); range.c2 = Math.min( range.c2, this.nColsCount - 1 ); } if ( gc_nMaxCol0 === range.c2 || gc_nMaxRow0 === range.r2 ) { range = range.clone(); if ( gc_nMaxCol0 === range.c2 ) { range.c2 = this.cols.length - 1; } if ( gc_nMaxRow0 === range.r2 ) { range.r2 = this.rows.length - 1; } } this._cleanCache( range ); // Если размер диапазона превышает размер видимой области больше чем в 3 раза, то очищаем весь кэш if ( this._isLargeRange( range ) ) { this.changeWorksheet( "update", {lockDraw: lockDraw} ); this._updateSelectionNameAndInfo(); return; } var cto; for ( r = range.r1; r <= range.r2; ++r ) { if ( this.height_1px > this.rows[r].height ) { continue; } for ( c = range.c1; c <= range.c2; ++c ) { if ( this.width_1px > this.cols[c].width ) { continue; } c = this._addCellTextToCache( c, r, canChangeColWidth ); // may change member 'this.isChanged' } for ( h = this.defaultRowHeight, d = this.defaultRowDescender, c = 0; c < this.cols.length; ++c ) { ct = this._getCellTextCache( c, r, true ); if ( !ct ) { continue; } /** * Пробегаемся по строке и смотрим не продолжается ли ячейка на соседние. * С помощью этой правки уйдем от обновления всей строки при каких-либо действиях */ if ( (c < range.c1 || c > range.c2) && (0 !== ct.sideL || 0 !== ct.sideR) ) { cto = this._calcCellTextOffset( c, r, ct.cellHA, ct.metrics.width ); ct.cellW = cto.maxWidth; ct.sideL = cto.leftSide; ct.sideR = cto.rightSide; } // Замерженная ячейка (с 2-мя или более строками) не влияет на высоту строк! isMerged = ct.flags.isMerged(); if ( !isMerged ) { bUpdateRowHeight = true; } else { mergedRange = ct.flags.merged; // Для замерженных ячеек (с 2-мя или более строками) оптимизировать не нужно bUpdateRowHeight = mergedRange.r1 === mergedRange.r2; } if ( bUpdateRowHeight ) { h = Math.max( h, ct.metrics.height ); } if ( ct.cellVA !== kvaTop && ct.cellVA !== kvaCenter && !isMerged ) { d = Math.max( d, ct.metrics.height - ct.metrics.baseline ); } } if ( Math.abs( h - this.rows[r].height ) > 0.000001 && !this.rows[r].isCustomHeight ) { if ( !this.rows[r].isDefaultHeight ) { this.rows[r].heightReal = this.rows[r].height = Math.min( h, this.maxRowHeight ); this.model.setRowHeight( this.rows[r].height + this.height_1px, r, r, false ); } this.isChanged = true; } if ( Math.abs( d - this.rows[r].descender ) > 0.000001 ) { this.rows[r].descender = d; this.isChanged = true; } } if ( !lockDraw ) { this._recalculateAfterUpdate( [range] ); } }; WorksheetView.prototype._recalculateAfterUpdate = function ( arrChanged, lockDraw ) { if ( this.isChanged ) { this.isChanged = false; this._initCellsArea( true ); this.cache.reset(); this._cleanCellsTextMetricsCache(); this._prepareCellTextMetricsCache(); this.handlers.trigger( "reinitializeScroll" ); } if ( !lockDraw ) { this._updateSelectionNameAndInfo(); } this.objectRender.rebuildChartGraphicObjects( new CChangeTableData( null, null, null, null, arrChanged ) ); this.cellCommentator.updateCommentPosition(); this.handlers.trigger( "onDocumentPlaceChanged" ); this.draw( lockDraw ); }; WorksheetView.prototype.enterCellRange = function ( editor ) { if ( !this.isFormulaEditMode ) { return; } var currentRange = this.arrActiveFormulaRanges[this.arrActiveFormulaRangesPosition].clone(); var startCol = currentRange.startCol, startRow = currentRange.startRow; // Замерженную ячейку должны отдать только левую верхнюю. var mergedRange = this.model.getMergedByCell( currentRange.r1, currentRange.c1 ); if ( mergedRange && currentRange.isEqual( mergedRange ) ) { currentRange.r2 = currentRange.r1; currentRange.c2 = currentRange.c1; } /* var defName = this.model.workbook.findDefinesNames(this.model.getName()+"!"+currentRange.getAbsName(),this.model.getId()); console.log("defName #2 " + defName);*/ var sheetName = "", cFEWSO = editor.handlers.trigger( "getCellFormulaEnterWSOpen" ); if( editor.formulaIsOperator() && cFEWSO && cFEWSO.model.getId() != this.model.getId() ){ sheetName = parserHelp.getEscapeSheetName(this.model.getName())+"!"; } editor.enterCellRange( /*defName || */sheetName + currentRange.getAllRange().getName() ); for ( var tmpRange, i = 0; i < this.arrActiveFormulaRanges.length; ++i ) { tmpRange = this.arrActiveFormulaRanges[i]; if ( tmpRange.isEqual( currentRange ) ) { tmpRange.startCol = startCol; tmpRange.startRow = startRow; break; } } }; WorksheetView.prototype.addFormulaRange = function ( range ) { var r = range !== undefined ? range : new asc_ActiveRange( this.activeRange.c1, this.activeRange.r1, this.activeRange.c2, this.activeRange.r2 ); this.arrActiveFormulaRanges.push( r ); this.arrActiveFormulaRangesPosition = this.arrActiveFormulaRanges.length - 1; this._fixSelectionOfMergedCells(); }; WorksheetView.prototype.activeFormulaRange = function ( range ) { this.arrActiveFormulaRangesPosition = -1; for ( var i = 0; i < this.arrActiveFormulaRanges.length; ++i ) { if ( this.arrActiveFormulaRanges[i].isEqual( range ) ) { this.arrActiveFormulaRangesPosition = i; return; } } }; WorksheetView.prototype.cleanFormulaRanges = function () { // Очищаем массив ячеек для текущей формулы this.arrActiveFormulaRanges = []; }; WorksheetView.prototype.addAutoFilter = function ( styleName, addFormatTableOptionsObj ) { // Проверка глобального лока if ( this.collaborativeEditing.getGlobalLock() ) { return; } if ( !this.handlers.trigger( "getLockDefNameManagerStatus" ) ) { this.handlers.trigger( "onErrorEvent", c_oAscError.ID.LockCreateDefName, c_oAscError.Level.NoCritical ); return; } var t = this; var ar = t.activeRange.clone( true ); var onChangeAutoFilterCallback = function ( isSuccess ) { if ( false === isSuccess ) { t.model.workbook.handlers.trigger( "asc_onError", c_oAscError.ID.LockedAllError, c_oAscError.Level.NoCritical ); t.handlers.trigger( "selectionChanged", t.getSelectionInfo() ); return; } if ( addFormatTableOptionsObj && t.autoFilters.isChangeAutoFilterToTablePart( addFormatTableOptionsObj ) === true ) { t.autoFilters.changeAutoFilterToTablePart( styleName, ar, addFormatTableOptionsObj ); } else { t.autoFilters.addAutoFilter( styleName, ar, addFormatTableOptionsObj ); } }; if ( t.autoFilters.checkAddAutoFilter( ar, styleName, addFormatTableOptionsObj ) === true ) { this._isLockedAll( onChangeAutoFilterCallback ); this._isLockedDefNames( null, null ); } else//для того, чтобы в случае ошибки кнопка отжималась! { t.handlers.trigger( "selectionChanged", t.getSelectionInfo() ); } }; WorksheetView.prototype.changeAutoFilter = function ( tableName, optionType, val ) { // Проверка глобального лока if ( this.collaborativeEditing.getGlobalLock() ) { return; } var t = this; var ar = t.activeRange.clone( true ); var onChangeAutoFilterCallback = function ( isSuccess ) { if ( false === isSuccess ) { t.handlers.trigger( "selectionChanged", t.getSelectionInfo() ); return; } switch ( optionType ) { case c_oAscChangeFilterOptions.filter: { if ( !val ) { t.autoFilters.deleteAutoFilter( ar, tableName ); } else { t.autoFilters.addAutoFilter( null, ar ); } break; } case c_oAscChangeFilterOptions.style: { t.autoFilters.changeTableStyleInfo( val, ar, tableName ); break; } } }; this._isLockedAll( onChangeAutoFilterCallback ); }; WorksheetView.prototype.applyAutoFilter = function ( type, autoFilterObject ) { var t = this; var ar = t.activeRange.clone( true ); var onChangeAutoFilterCallback = function ( isSuccess ) { if ( false === isSuccess ) { return; } var rowChange = t.autoFilters.applyAutoFilter( autoFilterObject, ar ); if ( null !== rowChange ) { t.objectRender.updateSizeDrawingObjects( {target: c_oTargetType.RowResize, row: rowChange} ); } }; this._isLockedAll( onChangeAutoFilterCallback ); }; WorksheetView.prototype.sortColFilter = function ( type, cellId, displayName ) { var t = this; var ar = this.activeRange.clone( true ); var onChangeAutoFilterCallback = function ( isSuccess ) { if ( false === isSuccess ) { return; } t.autoFilters.sortColFilter( type, cellId, ar, null, displayName ); }; this._isLockedAll( onChangeAutoFilterCallback ); }; WorksheetView.prototype.getAddFormatTableOptions = function ( range ) { var ar = this.activeRange.clone( true ); return this.autoFilters.getAddFormatTableOptions( ar, range ); }; WorksheetView.prototype.clearFilter = function () { var t = this; var ar = this.activeRange.clone( true ); var onChangeAutoFilterCallback = function ( isSuccess ) { if ( false === isSuccess ) { return; } t.autoFilters.isApplyAutoFilterInCell( ar, true ); }; this._isLockedAll( onChangeAutoFilterCallback ); }; /** * Обновление при изменениях форматированной таблицы * @param range - обновляемый диапазон (он же диапазон для выделения) * @param recalc - делать ли автоподбор по названию столбца * @param changeRowsOrMerge - менялись ли строки (скрытие раскрытие) или был unmerge * @private */ WorksheetView.prototype._onUpdateFormatTable = function ( range, recalc, changeRowsOrMerge ) { //ToDo заглушка, чтобы не падало. Нужно полностью переделывать этот код!!!! (Перенес выше из-за бага http://bugzserver/show_bug.cgi?id=26705) this._checkUpdateRange( range ); if ( !recalc ) { // При скрытии/открытии строк стоит делать update всему if ( changeRowsOrMerge ) { this.isChanged = true; } // Пока вызовем updateRange, но стоит делать просто draw this._updateCellsRange( range ); return; } if ( !this.activeRange.isEqual( range ) ) { this.setSelection( range ); } var i, r = range.r1, bIsUpdate = false, w; for ( i = range.c1; i <= range.c2; ++i ) { w = this.onChangeWidthCallback( i, r, r, /*onlyIfMore*/true ); if ( -1 !== w ) { this.cols[i] = this._calcColWidth( w ); this._cleanCache( new asc_Range( i, 0, i, this.rows.length - 1 ) ); bIsUpdate = true; } } if ( bIsUpdate ) { this._updateColumnPositions(); this._updateVisibleColsCount(); this.changeWorksheet( "update" ); } else if ( changeRowsOrMerge ) { // Был merge, нужно обновить (ToDo) this._initCellsArea( true ); this.cache.reset(); this._cleanCellsTextMetricsCache(); this._prepareCellTextMetricsCache(); if ( this.objectRender && this.objectRender.drawingArea ) { this.objectRender.drawingArea.reinitRanges(); } var oChangeData = new CChangeTableData( null, null, null, null ); // Обновление для диаграмм oChangeData.change = new asc_Range( range.c1, 0, range.c2, gc_nMaxRow0 ); this.objectRender.rebuildChartGraphicObjects( oChangeData ); this.draw(); this.handlers.trigger( "reinitializeScroll" ); } else { // Просто отрисуем this.draw(); } }; WorksheetView.prototype._loadFonts = function ( fonts, callback ) { var api = window["Asc"]["editor"]; api._loadFonts( fonts, callback ); }; WorksheetView.prototype.setData = function ( oData ) { History.Clear(); History.TurnOff(); var oAllRange = new Range( this.model, 0, 0, this.nRowsCount - 1, this.nColsCount - 1 ); oAllRange.cleanAll(); var row, oCell; for ( var r = 0; r < oData.length; ++r ) { row = oData[r]; for ( var c = 0; c < row.length; ++c ) { if ( row[c] ) { oCell = this._getVisibleCell( c, r ); oCell.setValue( row[c] ); } } } History.TurnOn(); this._updateCellsRange( oAllRange.bbox ); // ToDo Стоит обновить nRowsCount и nColsCount }; WorksheetView.prototype.getData = function () { var arrResult, arrCells = [], cell, c, r, row, lastC = -1, lastR = -1, val; var maxCols = Math.min( this.model.getColsCount(), gc_nMaxCol ); var maxRows = Math.min( this.model.getRowsCount(), gc_nMaxRow ); for ( r = 0; r < maxRows; ++r ) { row = []; for ( c = 0; c < maxCols; ++c ) { cell = this.model._getCellNoEmpty( r, c ); if ( cell && '' !== (val = cell.getValue()) ) { lastC = Math.max( lastC, c ); lastR = Math.max( lastR, r ); } else { val = ''; } row.push( val ); } arrCells.push( row ); } arrResult = arrCells.slice( 0, lastR + 1 ); ++lastC; if ( lastC < maxCols ) { for ( r = 0; r < arrResult.length; ++r ) { arrResult[r] = arrResult[r].slice( 0, lastC ); } } return arrResult; }; /* * Export * ----------------------------------------------------------------------------- */ window["Asc"].WorksheetView = WorksheetView; })( jQuery, window );