/* * (c) Copyright Ascensio System SIA 2010-2016 * * This program is a free software product. You can redistribute it and/or * modify it under the terms of the GNU Affero General Public License (AGPL) * version 3 as published by the Free Software Foundation. In accordance with * Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect * that Ascensio System SIA expressly excludes the warranty of non-infringement * of any third-party rights. * * This program is distributed WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For * details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html * * You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, * EU, LV-1021. * * The interactive user interfaces in modified source and object code versions * of the Program must display Appropriate Legal Notices, as required under * Section 5 of the GNU AGPL version 3. * * Pursuant to Section 7(b) of the License you must retain the original Product * logo when distributing the program. Pursuant to Section 7(e) we decline to * grant you any rights under trademark law for use of our trademarks. * * All the Product's GUI elements, including illustrations and icon sets, as * well as technical writing content are licensed under the terms of the * Creative Commons Attribution-ShareAlike 4.0 International. See the License * terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode * */ "use strict"; (/** * @param {Window} window * @param {undefined} undefined */ function (window, undefined) { /* * Import * ----------------------------------------------------------------------------- */ var CellValueType = AscCommon.CellValueType; var c_oAscBorderStyles = AscCommon.c_oAscBorderStyles; var c_oAscBorderType = AscCommon.c_oAscBorderType; var c_oAscLockTypes = AscCommon.c_oAscLockTypes; var c_oAscFormatPainterState = AscCommon.c_oAscFormatPainterState; var c_oAscPrintDefaultSettings = AscCommon.c_oAscPrintDefaultSettings; var AscBrowser = AscCommon.AscBrowser; var CColor = AscCommon.CColor; var fSortAscending = AscCommon.fSortAscending; var parserHelp = AscCommon.parserHelp; var gc_nMaxDigCountView = AscCommon.gc_nMaxDigCountView; var gc_nMaxRow0 = AscCommon.gc_nMaxRow0; var gc_nMaxCol0 = AscCommon.gc_nMaxCol0; var gc_nMaxRow = AscCommon.gc_nMaxRow; var gc_nMaxCol = AscCommon.gc_nMaxCol; var History = AscCommon.History; var asc = window["Asc"]; var asc_applyFunction = AscCommonExcel.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_CMM = AscCommonExcel.asc_CMouseMoveData; var asc_VR = AscCommonExcel.VisibleRange; var asc_CFont = AscCommonExcel.asc_CFont; var asc_CFill = AscCommonExcel.asc_CFill; var asc_CCellInfo = AscCommonExcel.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 = AscCommonExcel.CPagePrint; var asc_CAutoFilterInfo = AscCommonExcel.asc_CAutoFilterInfo; var c_oTargetType = AscCommonExcel.c_oTargetType; var c_oAscCanChangeColWidth = AscCommonExcel.c_oAscCanChangeColWidth; var c_oAscLockTypeElemSubType = AscCommonExcel.c_oAscLockTypeElemSubType; var c_oAscLockTypeElem = AscCommonExcel.c_oAscLockTypeElem; var c_oAscGraphicOption = AscCommonExcel.c_oAscGraphicOption; var c_oAscError = asc.c_oAscError; var c_oAscMergeOptions = asc.c_oAscMergeOptions; var c_oAscInsertOptions = asc.c_oAscInsertOptions; var c_oAscDeleteOptions = asc.c_oAscDeleteOptions; var c_oAscBorderOptions = asc.c_oAscBorderOptions; var c_oAscCleanOptions = asc.c_oAscCleanOptions; var c_oAscSelectionType = asc.c_oAscSelectionType; var c_oAscSelectionDialogType = asc.c_oAscSelectionDialogType; var c_oAscAutoFilterTypes = asc.c_oAscAutoFilterTypes; var c_oAscChangeTableStyleInfo = asc.c_oAscChangeTableStyleInfo; var c_oAscChangeSelectionFormatTable = asc.c_oAscChangeSelectionFormatTable; var asc_CSelectionMathInfo = AscCommonExcel.asc_CSelectionMathInfo; var vector_koef = AscCommonExcel.vector_koef; /* * Constants * ----------------------------------------------------------------------------- */ /** * header styles * @const */ var kHeaderDefault = 0; var kHeaderActive = 1; var kHeaderHighlighted = 2; /** * 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 в меню, не надо их исправлять // и коммитить на пути относительно тестового меню var cursorRetina = AscBrowser.isRetina ? '_2x' : ''; cursorRetina = ''; // ToDo 2x cursors kCurCells = 'url(../../../../sdkjs/common/Images/plus' + cursorRetina + '.cur), pointer'; kCurFormatPainterExcel = 'url(../../../../sdkjs/common/Images/plus_copy' + cursorRetina + '.cur), pointer'; } else if (AscBrowser.isOpera) { kCurCells = 'cell'; kCurFormatPainterExcel = 'pointer'; } else { // ToDo 2x cursors /*if (AscBrowser.isRetina) { kCurCells = "url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAYAAAByDd+UAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAGtJREFUeNpidHFxYSAS/Ccgz0iMIUwMdAYspGrYvXs3Ct/V1ZUk/XT34aiFoxYOfgtZiChBqFUSDXBJg16CkFvy4AKwEmk0ldIuDokt9YdcbcFCbE09ZGv8UQtHLRxCJQ2xgNSSZcB9CBBgAA9FEd4uFbt8AAAAAElFTkSuQmCC') 12 12, pointer"; kCurFormatPainterExcel = "url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACoAAAAqCAYAAADFw8lbAAAACXBIWXMAAAsTAAALEwEAmpwYAAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9a89+bN/rXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH/w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA/g88wAAKCRFRHgg/P9eM4Ors7ONo62Dl8t6r8G/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl7gRoXgugdfeLZrIPQLUAoOnaV/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl/AV/1s+X48/Pf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAAXkQkLlTKsz/HCAAARKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQSgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8lsKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2epO6iHqmeob1Q/pH5Z/YkGWcNMw09DpFGgsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY/R27iz2qqaE5QzNKM1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3nU9lT3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L/1U/W36p/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8Wuw+6TvZN9un2N/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofcn8w0nymeWTNz0MPIQ+BR5dE/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh7xc+9j5yn+M+4zw33jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU85ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt/87fOH4p3iC+N7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi/RNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z+pn5mZ2y6xlhbL+xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a/zYnKOZarnivN7cyzytuQN5zvn//tEsIS4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1+1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaql+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXHtxwPSA/0HIw6217nU1R3SPVRSj9Yr60cOxx++/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtbYlu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh0kX/i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89HcR/cGhYPP/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6i/suuFxYvfvjV69fO0ZjRoZfyl5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o/2j5sfVT0Kf7kxmTk/8EA5jz/GMzLdsAAAAgY0hSTQAAeiUAAICDAAD5/wAAgOkAAHUwAADqYAAAOpgAABdvkl/FRgAAASFJREFUeNrsmU0SgyAMhQnD0eDUcre4KU5EwMQpf0pW2kX7zePlJVpARDVD6YHZcAZQjGH1qJDW2tO9HhmSwpre3ouLwCnvfbgEPQOkUkqZjt7LVoC01kK4hkY5KoYMSoYyPSAJTPLYewQ+C5JTphUkBUx9NswIjbNRqixwDM6Jltj827Zlm6jk0Vwz1VYUOOpxxBJ79KfUUc45Dix670/HT7LyNppaehRSDcWFrAqaOM6sDegkGmVxvsBSJUtergZa+NEDlqNkM0VjLyZ8CJxMrTaZQhoAwOmepAZIvm/kh7uLov/a81DSUG96XE57NJ44TyfVnWff+AJigbbxKNdDD7an7ynKzTXRhr+aaYEu0AX6tckk3dyXorlpMssfYvsAnpmCy8p26IoAAAAASUVORK5CYII=') 12 25, 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 = null; } 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 {AscCommonExcel.Woorksheet} model Worksheet * @param {AscCommonExcel.asc_CHandlersList} handlers Event handlers * @param {Object} buffers DrawingContext + Overlay * @param {AscCommonExcel.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.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; // 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.arrActiveChartRanges = [new AscCommonExcel.SelectionRange(this.model)]; //------------------------ this.collaborativeEditing = collaborativeEditing; this.drawingArea = new AscFormat.DrawingArea(this); this.cellCommentator = new AscCommonExcel.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; var range = this.model.selectionRange.getLast(); for (c = range.c1; c <= range.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; var range = this.model.selectionRange.getLast(); for (r = range.r1; r <= range.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.getSelectedRange = function () { // ToDo multiselect ? var lastRange = this.model.selectionRange.getLast(); return this._getRange(lastRange.c1, lastRange.r1, lastRange.c2, lastRange.r2); }; WorksheetView.prototype.resize = function (isUpdate) { if (isUpdate) { this._initCellsArea(AscCommonExcel.recalcType.newLines); this._normalizeViewRange(); 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(AscCommonExcel.recalcType.recalc); 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(AscCommonExcel.recalcType.full); 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.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), Asc.c_oAscMaxColumnWidth); var onChangeWidthCallback = function (isSuccess) { if (false === isSuccess) { return; } t.model.setColWidth(cc, 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.model.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 = [], arrRows = []; // ToDo multiselect var selectionRange = this.model.selectionRange.getLast(); if (selectionRange.isOneCell()) { // Для одной ячейки не стоит ничего делать return null; } var mergedRange = this.model.getMergedByCell(selectionRange.r1, selectionRange.c1); if (mergedRange && mergedRange.isEqual(selectionRange)) { // Для одной ячейки не стоит ничего делать return null; } for (var c = selectionRange.c1; c <= selectionRange.c2; ++c) { for (var r = selectionRange.r1; r <= selectionRange.r2; ++r) { cell = this._getCellTextCache(c, r); if (cell) { // Нашли не пустую ячейку, проверим формат cellType = cell.cellType; isNumberFormat = (null == cellType || CellValueType.Number === cellType); if (isNumberFormat) { arrCols.push(c); arrRows.push(r); } } } } if (0 !== arrCols.length) { // Делаем массивы уникальными и сортируем arrCols = arrCols.filter(AscCommon.fOnlyUnique); arrRows = arrRows.filter(AscCommon.fOnlyUnique); return {arrCols: arrCols.sort(fSortAscending), arrRows: arrRows.sort(fSortAscending)}; } else { return null; } }; // Автодополняет формулу диапазоном, если это возможно WorksheetView.prototype.autoCompleteFormula = function (functionName) { var t = this; // ToDo autoComplete with multiselect var activeCell = this.model.selectionRange.activeCell; var ar = this.model.selectionRange.getLast(); var arCopy = null; var arHistorySelect = ar.clone(true); var vr = this.visibleRange; // Первая верхняя не числовая ячейка var topCell = null; // Первая левая не числовая ячейка var leftCell = null; var r = activeCell.row - 1; var c = activeCell.col - 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(activeCell.col, activeCell.row)) { // Передвинуть первую ячейку в выделении activeCell.col = startCol; activeCell.row = 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(activeCell.col, r); if (cell) { // Нашли не пустую ячейку, проверим формат cellType = cell.cellType; isNumberFormat = (null === cellType || CellValueType.Number === cellType); if (isNumberFormat) { // Это число, мы нашли то, что искали topCell = { c: activeCell.col, r: r, isFormula: cell.isFormula }; // смотрим вторую ячейку if (topCell.isFormula && r - 1 >= vr.r1) { cell = this._getCellTextCache(activeCell.col, r - 1); if (cell && cell.isFormula) { topCell.isFormulaSeq = true; } } break; } } } // Проверим, первой все равно должна быть колонка if (null === topCell || topCell.r !== activeCell.row - 1 || topCell.isFormula && !topCell.isFormulaSeq) { for (; c >= vr.c1; --c) { cell = this._getCellTextCache(c, activeCell.row); if (cell) { // Нашли не пустую ячейку, проверим формат cellType = cell.cellType; isNumberFormat = (null === cellType || CellValueType.Number === cellType); if (isNumberFormat) { // Это число, мы нашли то, что искали leftCell = { r: activeCell.row, c: c }; break; } } if (null !== topCell) { // Если это не первая ячейка слева от текущей и мы нашли верхнюю, то дальше не стоит искать break; } } } if (leftCell) { // Идем влево до первой не числовой ячейки --c; for (; c >= 0; --c) { cell = this._getCellTextCache(c, activeCell.row); if (!cell) { // Могут быть еще не закешированные данные this._addCellTextToCache(c, activeCell.row); cell = this._getCellTextCache(c, activeCell.row); if (!cell) { break; } } cellType = cell.cellType; isNumberFormat = (null === cellType || CellValueType.Number === cellType); if (!isNumberFormat) { break; } } // Мы ушли чуть дальше ++c; // Диапазон или только 1 ячейка if (activeCell.col - 1 !== c) { // Диапазон result = new asc_Range(c, leftCell.r, activeCell.col - 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(activeCell.col, r); if (!cell) { // Могут быть еще не закешированные данные this._addCellTextToCache(activeCell.col, r); cell = this._getCellTextCache(activeCell.col, r); if (!cell) { break; } } cellType = cell.cellType; isNumberFormat = (null === cellType || CellValueType.Number === cellType); if (!isNumberFormat) { break; } } // Мы ушли чуть дальше ++r; // Диапазон или только 1 ячейка if (activeCell.row - 1 !== r) { // Диапазон result = new asc_Range(topCell.c, r, topCell.c, activeCell.row - 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(AscCommonExcel.recalcType.full); this.model.setTableStyleAfterOpen(); this._cleanCellsTextMetricsCache(); this._prepareCellTextMetricsCache(); // initializing is completed this.handlers.trigger("initialized"); }; WorksheetView.prototype._prepareComments = function () { // ToDo возможно не нужно это делать именно тут.. if (0 < this.model.aComments.length) { this.model.workbook.handlers.trigger("asc_onAddComments", this.model.aComments); } }; WorksheetView.prototype._prepareDrawingObjects = function () { this.objectRender = new AscFormat.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.model.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)); AscCommonExcel.oDefaultMetrics.ColWidthChars = this.model.charCountToModelColWidth(this.defaultColWidthChars); this.defaultColWidth = this._modelColWidthToColWidth(AscCommonExcel.oDefaultMetrics.ColWidthChars); 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.height_1px; this.maxRowHeight = asc_calcnpt(Asc.c_oAscMaxRowHeight, this._getPPIY()); this.defaultRowDescender = this._calcRowDescender(defaultFontSize); AscCommonExcel.oDefaultMetrics.RowHeight = this.defaultRowHeight = Math.min(this.maxRowHeight, this.model.getDefaultHeight() || this.headersHeightByFont); // Инициализируем число колонок и строк (при открытии). Причем нужно поставить на 1 больше, // чтобы могли показать последнюю строку/столбец (http://bugzilla.onlyoffice.com/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 (type) { // calculate rows heights and visible rows if (!(window["NATIVE_EDITOR_ENJINE"] && this.notUpdateRowHeight)) { this._calcHeaderRowHeight(); this._calcHeightRows(type); } 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(type); } 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._getSelection = function () { return (this.isFormulaEditMode) ? this.arrActiveFormulaRanges[this.arrActiveFormulaRangesPosition] : this.model.selectionRange; }; 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} 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.model.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.headersHeight = asc_calcnpt( this.settings.header.fontSize * this.vspRatio, this._getPPIY() ); this.cellsTop = this.headersTop + this.headersHeight; }; /** * Вычисляет ширину и позицию колонок (в pt) * @param {AscCommonExcel.recalcType} type */ WorksheetView.prototype._calcWidthColumns = function (type) { var x = this.cellsLeft; var visibleW = this.drawingCtx.getWidth(); var maxColObjects = this.objectRender ? this.objectRender.getMaxColRow().col : -1; var l = Math.max(this.model.getColsCount() + 1, this.nColsCount, maxColObjects); var i = 0, w, column, isBestFit, hiddenW = 0; // Берем дефалтовую ширину документа var defaultWidth = this.model.getDefaultWidth(); defaultWidth = (typeof defaultWidth === "number" && defaultWidth >= 0) ? defaultWidth : -1; if (AscCommonExcel.recalcType.full === type) { this.cols = []; } else if (AscCommonExcel.recalcType.newLines === type) { i = this.cols.length; x = this.cols[i - 1].left + this.cols[i - 1].width; } for (; ((AscCommonExcel.recalcType.recalc !== type) ? 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 {AscCommonExcel.recalcType} type */ WorksheetView.prototype._calcHeightRows = function (type) { var y = this.cellsTop; var visibleH = this.drawingCtx.getHeight(); var maxRowObjects = this.objectRender ? this.objectRender.getMaxColRow().row : -1; var l = Math.max(this.model.getRowsCount() + 1, this.nRowsCount, maxRowObjects); var defaultH = this.defaultRowHeight; var i = 0, h, hR, isCustomHeight, row, hiddenH = 0; if (AscCommonExcel.recalcType.full === type) { this.rows = []; } else if (AscCommonExcel.recalcType.newLines === type) { i = this.rows.length; y = this.rows[i - 1].top + this.rows[i - 1].height; } // ToDo calc all rows (not visible) for (; ((AscCommonExcel.recalcType.recalc !== type) ? 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 != (AscCommonExcel.g_nRowFlag_hd & row.flags)) { hR = h = 0; // Скрытая строка, высоту выставляем 0 isCustomHeight = true; hiddenH += row.h > 0 ? row.h - this.height_1px : defaultH; } else { isCustomHeight = 0 != (AscCommonExcel.g_nRowFlag_CustomHeight & row.flags); // Берем высоту из модели, если она custom(баг 15618), либо дефолтную if (row.h > 0 && (isCustomHeight || (AscCommonExcel.g_nRowFlag_CalcHeight & row.flags))) { 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 }; 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 : this.model.selectionRange.getLast(); 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, arrPages) { var range; var maxCols = this.model.getColsCount(); var maxRows = this.model.getRowsCount(); var lastC = -1, lastR = -1; // ToDo print each range on new page (now only last) var selectionRange = printOnlySelection ? this.model.selectionRange.getLast() : null; var bFitToWidth = false; var bFitToHeight = false; if (null === selectionRange) { range = new asc_Range(0, 0, maxCols, maxRows); this._prepareCellTextMetricsCache(range); var rowModel, rowCells, rowCache, rightSide, c; for (var r = 0; r <= maxRows; ++r) { if (this.height_1px > this.rows[r].height) { continue; } // Теперь получаем только не пустые ячейки для строки rowModel = this.model._getRowNoEmpty(r); if (!rowModel) { continue; } rowCache = this._getRowCache(r); rowCells = rowModel.getCells(); for (c in rowCells) { c = c - 0; if (this.width_1px > this.cols[c].width) { continue; } var style = rowCells[c].getStyle(); if (style && (null !== style.fill || (null !== style.border && style.border.notEmpty()))) { lastC = Math.max(lastC, c); lastR = Math.max(lastR, r); } if (rowCache && rowCache.columnsWithText[c]) { 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); } } } } var maxCell = this.model.autoFilters.getMaxColRow(); lastC = Math.max(lastC, maxCell.col); lastR = Math.max(lastR, maxCell.row); maxCols = lastC + 1; maxRows = lastR + 1; // Получаем максимальную колонку/строку для изображений/чатов maxCell = this.objectRender.getMaxColRow(); maxCols = Math.max(maxCols, maxCell.col); maxRows = Math.max(maxRows, maxCell.row); } else { maxCols = selectionRange.c2 + 1; maxRows = selectionRange.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(); bFitToWidth = pageSetup.asc_getFitToWidth(); bFitToHeight = pageSetup.asc_getFitToHeight(); } var pageLeftField, pageRightField, pageTopField, pageBottomField; if (pageMargins instanceof asc_CPageMargins) { pageLeftField = Math.max(pageMargins.asc_getLeft(), c_oAscPrintDefaultSettings.MinPageLeftField); pageRightField = Math.max(pageMargins.asc_getRight(), c_oAscPrintDefaultSettings.MinPageRightField); pageTopField = Math.max(pageMargins.asc_getTop(), c_oAscPrintDefaultSettings.MinPageTopField); pageBottomField = Math.max(pageMargins.asc_getBottom(), c_oAscPrintDefaultSettings.MinPageBottomField); } 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 (Asc.c_oAscPageOrientation.PageLandscape === pageOrientation) { var tmp = pageWidth; pageWidth = pageHeight; pageHeight = tmp; } if (0 === maxCols || 0 === maxRows) { // Ничего нет, возвращаем пустой массив return null; } else { var pageWidthWithFields = pageWidth - pageLeftField - pageRightField; var pageHeightWithFields = pageHeight - pageTopField - pageBottomField; // 1px offset for borders var leftFieldInPt = pageLeftField / vector_koef + this.width_1px; var topFieldInPt = pageTopField / vector_koef + this.height_1px; var rightFieldInPt = pageRightField / vector_koef + this.width_1px; var bottomFieldInPt = pageBottomField / vector_koef + this.height_1px; 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 !== selectionRange) ? selectionRange.c1 : 0; var currentWidth = 0; var currentRowIndex = (null !== selectionRange) ? selectionRange.r1 : 0; var currentHeight = 0; var isCalcColumnsWidth = true; var bIsAddOffset = false; var nCountOffset = 0; while (AscCommonExcel.c_kMaxPrintPages > arrPages.length) { 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 !== selectionRange) ? selectionRange.c1 : 0; currentRowIndex = rowIndex; currentHeight = 0; } if (rowIndex === maxRows) { // Мы вышли, т.к. дошли до конца отрисовки по строкам if (colIndex < maxCols) { currentColIndex = colIndex; currentHeight = 0; } else { // Мы дошли до конца отрисовки currentColIndex = colIndex; currentRowIndex = rowIndex; } } arrPages.push(newPagePrint); } } }; WorksheetView.prototype.drawForPrint = function(drawingCtx, printPagesData) { 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); 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 (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 (printPagesData.pageGridLines) { this._drawGrid(drawingCtx, range, offsetX, offsetY, printPagesData.pageWidth / vector_koef, printPagesData.pageHeight / vector_koef); } // Отрисовываем ячейки и бордеры this._drawCellsAndBorders(drawingCtx, range, offsetX, offsetY); var drawingPrintOptions = { ctx: drawingCtx, printPagesData: printPagesData }; this.objectRender.showDrawingObjectsEx(false, null, drawingPrintOptions); this.visibleRange = tmpVisibleRange; 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._drawElements(this.af_drawButtons); 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 () { // ToDo highlighted! var hlc = this.highlightedCol, hlr = this.highlightedRow, arn = this.model.selectionRange.getLast(); 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._cleanRowHeaders(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 vr = this.visibleRange; var range, c1, c2, r1, r2; this._activateOverlayCtx(); for (var i = 0; i < this.model.selectionRange.ranges.length; ++i) { range = this.model.selectionRange.ranges[i]; c1 = Math.max(vr.c1, range.c1); c2 = Math.min(vr.c2, range.c2); r1 = Math.max(vr.r1, range.r1); r2 = Math.min(vr.r2, range.r2); 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, range.c1); c2 = Math.min(cFrozen, range.c2); this._drawColumnHeaders(/*drawingCtx*/ undefined, c1, c2, kHeaderActive); } if (0 <= rFrozen) { r1 = Math.max(0, range.r1); r2 = Math.min(rFrozen, range.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 ? AscCommon.align_Center : AscCommon.align_Left); var textY = this._calcTextVertPos(y, y2WithoutBorder, bl, tm, Asc.c_oAscVAlign.Bottom); 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._cleanRowHeaders = 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._drawSparklines = function(drawingCtx, range, offsetX, offsetY) { var ctx = (undefined === drawingCtx) ? this.drawingCtx : drawingCtx; this.objectRender.drawSparkLineGroups(ctx, this.model.aSparklineGroups, range, offsetX, offsetY); }; /** Рисует ячейки таблицы */ WorksheetView.prototype._drawCells = function ( drawingCtx, range, offsetX, offsetY ) { this._prepareCellTextMetricsCache( range ); var mergedCells = [], mc, i; for ( var row = range.r1; row <= range.r2; ++row ) { mergedCells = mergedCells.concat(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 = 0; i < mergedCells.length; ++i) { if (i === mergedCells.indexOf(mergedCells[i])) { 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); } } this._drawSparklines(drawingCtx, range, offsetX, offsetY); return mergedCells; }; /** Рисует фон ячеек в строке */ WorksheetView.prototype._drawRowBG = function ( drawingCtx, row, colStart, colEnd, offsetX, offsetY, oMergedCell ) { var mergedCells = []; if ( this.rows[row].height < this.height_1px && null === oMergedCell ) { return 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 ); 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.push(mc); 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 ); 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 ); 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 ) { var mergedCells = []; if ( this.rows[row].height < this.height_1px ) { return mergedCells; } var dependentCells = {}, 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.push(mc); } // 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 range; } 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, textAlign; 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 = xb1 - ct.textBound.offsetX; txtRotW = ct.textBound.width + xb1 - ct.textBound.offsetX; 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 ); textAlign = this.stringRender.flags.textAlign; if (isWrapped) { if (ct.angle < 0) { if (Asc.c_oAscVAlign.Top === ct.cellVA) this.stringRender.flags.textAlign = AscCommon.align_Left; else if (Asc.c_oAscVAlign.Center === ct.cellVA) this.stringRender.flags.textAlign = AscCommon.align_Center; else if (Asc.c_oAscVAlign.Bottom === ct.cellVA) this.stringRender.flags.textAlign = AscCommon.align_Right; } else { if (Asc.c_oAscVAlign.Top === ct.cellVA) this.stringRender.flags.textAlign = AscCommon.align_Right; else if (Asc.c_oAscVAlign.Center === ct.cellVA) this.stringRender.flags.textAlign = AscCommon.align_Center; else if (Asc.c_oAscVAlign.Bottom === ct.cellVA) this.stringRender.flags.textAlign = AscCommon.align_Left; } } 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 = xb1 - ct.textBound.offsetX; txtRotW = ct.textBound.width + xb1 - ct.textBound.offsetX; 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); textAlign = this.stringRender.flags.textAlign; if (isWrapped) { if (ct.angle < 0) { if (Asc.c_oAscVAlign.Top === ct.cellVA) this.stringRender.flags.textAlign = AscCommon.align_Left; else if (Asc.c_oAscVAlign.Center === ct.cellVA) this.stringRender.flags.textAlign = AscCommon.align_Center; else if (Asc.c_oAscVAlign.Bottom === ct.cellVA) this.stringRender.flags.textAlign = AscCommon.align_Right; } else { if (Asc.c_oAscVAlign.Top === ct.cellVA) this.stringRender.flags.textAlign = AscCommon.align_Right; else if (Asc.c_oAscVAlign.Center === ct.cellVA) this.stringRender.flags.textAlign = AscCommon.align_Center; else if (Asc.c_oAscVAlign.Bottom === ct.cellVA) this.stringRender.flags.textAlign = AscCommon.align_Left; } } this.stringRender.render(0, 0, textW, ct.color); // var color = new CColor(0, 0, 255, 0.5); // // ctx.setStrokeStyle(color). // moveTo(0, 0). // lineTo(ct.metrics.width, 0). // lineTo(ct.metrics.width, ct.metrics.height). // lineTo(0, ct.metrics.height). // closePath(). // stroke(); this.stringRender.resetTransform( null ); if ( clipUse ) { ctx.restore(); } // color = new CColor( 255, 0, 0, 0.5 ); // ctx.save(). // setStrokeStyle(color). // strokeRect(xb1 - ct.textBound.offsetX, yb1, ct.textBound.width, ct.textBound.height). // 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, bs = c_oAscBorderStyles.None, isNotFirst = false; // cached border color function drawBorder(type, border, x1, y1, x2, y2) { var isStroke = false, isNewColor = !AscCommonExcel.g_oColorManager.isEqual(bc, border.c), isNewStyle = bs !== border.s; if (isNotFirst && (isNewColor || isNewStyle)) { ctx.stroke(); isStroke = true; } if (isNewColor) { bc = border.c; ctx.setStrokeStyle(bc); } if (isNewStyle) { bs = border.s; ctx.setLineWidth(border.w); ctx.setLineDash(border.getDashSegments()); } 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(), AscCommonExcel.c_oAscLockNameFrozenPane ); var isLocked = this.collaborativeEditing.getLockIntersection( lockInfo, c_oAscLockTypes.kLockTypeOther, false ); var color = isLocked ? AscCommonExcel.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 AscCommon.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 activeCell = this.model.selectionRange.activeCell.clone(); var onChangeFreezePane = function (isSuccess) { if (false === isSuccess) { return; } var col, row, mc; if (null !== t.topLeftFrozenCell) { col = row = 0; } else { col = activeCell.col; row = activeCell.row; 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 AscCommonExcel.UndoRedoData_FromTo(new AscCommonExcel.UndoRedoData_BBox(new asc_Range(lastCol, lastRow, lastCol, lastRow)), new AscCommonExcel.UndoRedoData_BBox(new asc_Range(col, row, col, row)), null); History.Add(AscCommonExcel.g_oUndoRedoWorksheet, AscCH.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 AscCommonExcel.asc_CPane(); this.topLeftFrozenCell = pane.topLeftFrozenCell = new AscCommon.CellAddress(row, col, 0); } this.visibleRange.c1 = col; this.visibleRange.r1 = row; if (col >= this.nColsCount) { this.expandColsOnScroll(false, true); } if (row >= this.nRowsCount) { this.expandRowsOnScroll(false, true); } 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]; var selectionLineType = args[1]; var strokeColor = args[2]; var isAllRange = args[3]; var colorN = this.settings.activeCellBorderColor2; var ctx = this.overlayCtx; var c = this.cols; var r = this.rows; var oIntersection = range.intersectionSimple(visibleRange); var ppiX = this._getPPIX(), ppiY = this._getPPIY(); if (!oIntersection) { return true; } var width_1px = asc_calcnpt(0, ppiX, 1/*px*/), width_2px = asc_calcnpt(0, ppiX, 2 /*px*/), width_3px = asc_calcnpt(0, ppiX, 3/*px*/), width_4px = asc_calcnpt(0, ppiX, 4 /*px*/), width_5px = asc_calcnpt(0, ppiX, 5/*px*/), width_7px = asc_calcnpt(0, ppiX, 7/*px*/), height_1px = asc_calcnpt(0, ppiY, 1/*px*/), height_2px = asc_calcnpt(0, ppiY, 2 /*px*/), height_3px = asc_calcnpt(0, ppiY, 3/*px*/), height_4px = asc_calcnpt(0, ppiY, 4 /*px*/), height_5px = asc_calcnpt(0, ppiY, 5/*px*/), height_7px = asc_calcnpt(0, ppiY, 7/*px*/); var fHorLine, fVerLine; var canFill = AscCommonExcel.selectionLineType.Selection & selectionLineType; var isDashLine = AscCommonExcel.selectionLineType.Dash & selectionLineType; 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; if (canFill) { var fillColor = strokeColor.Copy(); fillColor.a = 0.15; ctx.setFillStyle(fillColor).fillRect(x1, y1, x2 - x1, y2 - y1); } ctx.setLineWidth(isDashLine ? 1 : 2).setStrokeStyle(strokeColor); ctx.beginPath(); if (drawTopSide && !firstRow) { fHorLine.apply(ctx, [x1 - !isDashLine * width_2px, y1, x2 + !isDashLine * width_1px]); } if (drawBottomSide) { fHorLine.apply(ctx, [x1, y2 + !isDashLine * height_1px, x2]); } if (drawLeftSide && !firstCol) { fVerLine.apply(ctx, [x1, y1, y2 + !isDashLine * height_1px]); } if (drawRightSide) { fVerLine.apply(ctx, [x2 + !isDashLine * width_1px, y1, y2 + !isDashLine * height_1px]); } ctx.closePath().stroke(); // draw active cell in selection var isActive = AscCommonExcel.selectionLineType.ActiveCell & selectionLineType; if (isActive) { var cell = (this.isSelectionDialogMode ? this.copyActiveRange : this.model.selectionRange).activeCell; var fs = this.model.getMergedByCell(cell.row, cell.col); fs = range.intersectionSimple( fs ? fs : new asc_Range(cell.col, cell.row, cell.col, cell.row)); if (fs) { var _x1 = c[fs.c1].left - offsetX + width_1px; var _y1 = r[fs.r1].top - offsetY + height_1px; var _w = c[fs.c2].left - c[fs.c1].left + c[fs.c2].width - width_2px; var _h = r[fs.r2].top - r[fs.r1].top + r[fs.r2].height - height_2px; if (0 < _w && 0 < _h) { ctx.clearRect(_x1, _y1, _w, _h); } } } if (canFill) {/*Отрисовка светлой полосы при выборе ячеек для формулы*/ ctx.setLineWidth(1); ctx.setStrokeStyle(colorN); ctx.beginPath(); if (drawTopSide) { fHorLine.apply(ctx, [x1, y1 + height_1px, x2 - width_1px]); } if (drawBottomSide) { fHorLine.apply(ctx, [x1, y2 - height_1px, x2 - width_1px]); } if (drawLeftSide) { fVerLine.apply(ctx, [x1 + width_1px, y1, y2 - height_2px]); } if (drawRightSide) { fVerLine.apply(ctx, [x2 - width_1px, y1, y2 - height_2px]); } ctx.closePath().stroke(); } // Отрисовка квадратов для move/resize var isResize = AscCommonExcel.selectionLineType.Resize & selectionLineType; var isPromote = AscCommonExcel.selectionLineType.Promote & selectionLineType; if (isResize || isPromote) { ctx.setFillStyle(colorN); if (drawRightSide && drawBottomSide) { ctx.fillRect(x2 - width_4px, y2 - height_4px, width_7px, height_7px); } ctx.setFillStyle(strokeColor); if (drawRightSide && drawBottomSide) { ctx.fillRect(x2 - width_3px, y2 - height_3px, width_5px, height_5px); } if (isResize) { ctx.setFillStyle(colorN); if (drawLeftSide && drawTopSide) { ctx.fillRect(x1 - width_4px, y1 - height_4px, width_7px, height_7px); } if (drawRightSide && drawTopSide) { ctx.fillRect(x2 - width_4px, y1 - height_4px, width_7px, height_7px); } if (drawLeftSide && drawBottomSide) { ctx.fillRect(x1 - width_4px, y2 - height_4px, width_7px, height_7px); } ctx.setFillStyle(strokeColor); if (drawLeftSide && drawTopSide) { ctx.fillRect(x1 - width_3px, y1 - height_3px, width_5px, height_5px); } if (drawRightSide && drawTopSide) { ctx.fillRect(x2 - width_3px, y1 - height_3px, width_5px, height_5px); } if (drawLeftSide && drawBottomSide) { ctx.fillRect(x1 - width_3px, y2 - height_3px, width_5px, height_5px); } } } return true; }; /**Отрисовывает диапазон с заданными параметрами*/ WorksheetView.prototype._drawElements = function (drawFunction) { var cFrozen = 0, rFrozen = 0, args = Array.prototype.slice.call(arguments, 1), c = this.cols, r = this.rows, offsetX = c[this.visibleRange.c1].left - this.cellsLeft, offsetY = r[this.visibleRange.r1].top - this.cellsTop, res; 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); res = drawFunction.call(this, oFrozenRange, c[0].left - this.cellsLeft, r[0].top - this.cellsTop, args); if (!res) { return; } } if (0 <= cFrozen) { oFrozenRange = new asc_Range(0, this.visibleRange.r1, cFrozen, this.visibleRange.r2); res = drawFunction.call(this, oFrozenRange, c[0].left - this.cellsLeft, offsetY, args); if (!res) { return; } } if (0 <= rFrozen) { oFrozenRange = new asc_Range(this.visibleRange.c1, 0, this.visibleRange.c2, rFrozen); res = drawFunction.call(this, oFrozenRange, offsetX, r[0].top - this.cellsTop, args); if (!res) { return; } } } // Можно вместо call попользовать apply, но тогда нужно каждый раз соединять массив аргументов и 3 объекта drawFunction.call(this, this.visibleRange, offsetX, offsetY, args); }; /** * Рисует выделение вокруг ячеек */ WorksheetView.prototype._drawSelection = function () { var isShapeSelect = false; if (window['IS_NATIVE_EDITOR']) { return; } // set clipping rect to cells area var ctx = this.overlayCtx; ctx.save().beginPath() .rect(this.cellsLeft, this.cellsTop, ctx.getWidth() - this.cellsLeft, ctx.getHeight() - this.cellsTop) .clip(); if (!this.isSelectionDialogMode) { this._drawCollaborativeElements(); } var isOtherSelectionMode = this.isSelectionDialogMode || this.isFormulaEditMode; if (isOtherSelectionMode && !this.handlers.trigger('isActive')) { if (this.isSelectionDialogMode) { this._drawSelectRange(); } else if (this.isFormulaEditMode) { this._drawFormulaRanges(this.arrActiveFormulaRanges); } } else { isShapeSelect = (asc["editor"].isStartAddShape || this.objectRender.selectedGraphicObjectsExists()); if (isShapeSelect) { if (this.isChartAreaEditMode) { this._drawFormulaRanges(this.arrActiveChartRanges); } } else { this._drawFormulaRanges(this.arrActiveFormulaRanges); if (this.isChartAreaEditMode) { this._drawFormulaRanges(this.arrActiveChartRanges); } this._drawSelectionRange(); if (this.activeFillHandle) { this._drawElements(this._drawSelectionElement, this.activeFillHandle.clone(true), AscCommonExcel.selectionLineType.None, this.settings.activeCellBorderColor); } if (this.isSelectionDialogMode) { this._drawSelectRange(); } if (this.stateFormatPainter && this.handlers.trigger('isActive')) { this._drawFormatPainterRange(); } if (null !== this.activeMoveRange) { this._drawElements(this._drawSelectionElement, this.activeMoveRange, AscCommonExcel.selectionLineType.None, new CColor(0, 0, 0)); } } } // restore canvas' original clipping range ctx.restore(); if (!isOtherSelectionMode && !isShapeSelect) { this._drawActiveHeaders(); } }; WorksheetView.prototype._drawSelectionRange = function () { var ranges = (this.isSelectionDialogMode ? this.copyActiveRange : this.model.selectionRange).ranges; var range, selectionLineType; for (var i = 0, l = ranges.length; i < l; ++i) { range = ranges[i]; if (c_oAscSelectionType.RangeMax === range.type) { range.c2 = this.cols.length - 1; range.r2 = this.rows.length - 1; } else if (c_oAscSelectionType.RangeCol === range.type) { range.r2 = this.rows.length - 1; } else if (c_oAscSelectionType.RangeRow === range.type) { range.c2 = this.cols.length - 1; } selectionLineType = AscCommonExcel.selectionLineType.Selection; if (1 === l) { selectionLineType |= AscCommonExcel.selectionLineType.ActiveCell | AscCommonExcel.selectionLineType.Promote; } else if (i === this.model.selectionRange.activeCellId) { selectionLineType |= AscCommonExcel.selectionLineType.ActiveCell; } this._drawElements(this._drawSelectionElement, range, selectionLineType, this.settings.activeCellBorderColor); } this.handlers.trigger("drawMobileSelection", this.settings.activeCellBorderColor); }; WorksheetView.prototype._drawFormatPainterRange = function () { var t = this, color = new CColor(0, 0, 0); this.copyActiveRange.ranges.forEach(function (item) { t._drawElements(t._drawSelectionElement, item, AscCommonExcel.selectionLineType.Dash, color); }); }; WorksheetView.prototype._drawFormulaRanges = function (arrRanges) { var i, ranges, length = AscCommonExcel.c_oAscFormulaRangeBorderColor.length; var strokeColor, colorIndex, uniqueColorIndex = 0, tmpColors = []; for (i = 0; i < arrRanges.length; ++i) { ranges = arrRanges[i].ranges; for (var j = 0, l = ranges.length; j < l; ++j) { colorIndex = asc.getUniqueRangeColor(ranges, j, tmpColors); if (null == colorIndex) { colorIndex = uniqueColorIndex++; } tmpColors.push(colorIndex); if (ranges[j].noColor) { colorIndex = 0; } strokeColor = AscCommonExcel.c_oAscFormulaRangeBorderColor[colorIndex % length]; this._drawElements(this._drawSelectionElement, ranges[j], AscCommonExcel.selectionLineType.Selection | (ranges[j].isName ? AscCommonExcel.selectionLineType.None : AscCommonExcel.selectionLineType.Resize), strokeColor); } } }; WorksheetView.prototype._drawSelectRange = function () { var ranges = this.model.selectionRange.ranges; for (var i = 0, l = ranges.length; i < l; ++i) { this._drawElements(this._drawSelectionElement, ranges[i], AscCommonExcel.selectionLineType.Dash, AscCommonExcel.c_oAscCoAuthoringOtherBorderColor); } }; WorksheetView.prototype._drawCollaborativeElements = function () { if ( this.collaborativeEditing.getCollaborativeEditing() ) { this._drawCollaborativeElementsMeOther(c_oAscLockTypes.kLockTypeMine); this._drawCollaborativeElementsMeOther(c_oAscLockTypes.kLockTypeOther); this._drawCollaborativeElementsAllLock(); } }; WorksheetView.prototype._drawCollaborativeElementsAllLock = function () { var currentSheetId = this.model.getId(); var nLockAllType = this.collaborativeEditing.isLockAllOther(currentSheetId); if (Asc.c_oAscMouseMoveLockedObjectType.None !== nLockAllType) { var isAllRange = true, strokeColor = (Asc.c_oAscMouseMoveLockedObjectType.TableProperties === nLockAllType) ? AscCommonExcel.c_oAscCoAuthoringLockTablePropertiesBorderColor : AscCommonExcel.c_oAscCoAuthoringOtherBorderColor, oAllRange = new asc_Range(0, 0, gc_nMaxCol0, gc_nMaxRow0); this._drawElements(this._drawSelectionElement, oAllRange, AscCommonExcel.selectionLineType.Dash, strokeColor, isAllRange); } }; WorksheetView.prototype._drawCollaborativeElementsMeOther = function (type) { var currentSheetId = this.model.getId(), i, strokeColor, arrayCells, oCellTmp; if (c_oAscLockTypes.kLockTypeMine === type) { strokeColor = AscCommonExcel.c_oAscCoAuthoringMeBorderColor; arrayCells = this.collaborativeEditing.getLockCellsMe(currentSheetId); arrayCells = arrayCells.concat(this.collaborativeEditing.getArrayInsertColumnsBySheetId(currentSheetId)); arrayCells = arrayCells.concat(this.collaborativeEditing.getArrayInsertRowsBySheetId(currentSheetId)); } else { strokeColor = AscCommonExcel.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._drawSelectionElement, oCellTmp, AscCommonExcel.selectionLineType.Dash, strokeColor); } }; 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 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 _x1, _x2, _y1, _y2; 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; } this._activateOverlayCtx(); var t = this; this.model.selectionRange.ranges.forEach(function (item) { var arnIntersection = item.intersectionSimple(range); if (arnIntersection) { _x1 = t.cols[arnIntersection.c1].left - offsetX - t.width_2px; _x2 = t.cols[arnIntersection.c2].left + t.cols[arnIntersection.c2].width - offsetX + t.width_1px + /* Это ширина "квадрата" для автофильтра от границы ячейки */t.width_2px; _y1 = t.rows[arnIntersection.r1].top - offsetY - t.height_2px; _y2 = t.rows[arnIntersection.r2].top + t.rows[arnIntersection.r2].height - offsetY + t.height_1px + /* Это высота "квадрата" для автофильтра от границы ячейки */t.height_2px; x1 = Math.min(x1, _x1); x2 = Math.max(x2, _x2); y1 = Math.min(y1, _y1); y2 = Math.max(y2, _y2); } if (!isFrozen) { t._cleanColumnHeaders(item.c1, item.c2); t._cleanRowHeaders(item.r1, item.r2); } }); this._deactivateOverlayCtx(); // Если есть активное автозаполнения, то нужно его тоже очистить if (this.activeFillHandle !== null) { var activeFillClone = this.activeFillHandle.clone(true); // Координаты для автозаполнения _x1 = this.cols[activeFillClone.c1].left - offsetX - this.width_2px; _x2 = this.cols[activeFillClone.c2].left + this.cols[activeFillClone.c2].width - offsetX + this.width_1px + this.width_2px; _y1 = this.rows[activeFillClone.r1].top - offsetY - this.height_2px; _y2 = this.rows[activeFillClone.r2].top + this.rows[activeFillClone.r2].height - offsetY + this.height_1px + this.height_2px; // Выбираем наибольший range для очистки x1 = Math.min(x1, _x1); x2 = Math.max(x2, _x2); y1 = Math.min(y1, _y1); y2 = Math.max(y2, _y2); } if (this.collaborativeEditing.getCollaborativeEditing()) { var currentSheetId = this.model.getId(); var nLockAllType = this.collaborativeEditing.isLockAllOther(currentSheetId); if (Asc.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) { // Координаты для автозаполнения _x1 = this.cols[aFormulaIntersection.c1].left - offsetX - this.width_2px; _x2 = this.cols[aFormulaIntersection.c2].left + this.cols[aFormulaIntersection.c2].width - offsetX + this.width_1px + this.width_2px; _y1 = this.rows[aFormulaIntersection.r1].top - offsetY - this.height_2px; _y2 = this.rows[aFormulaIntersection.r2].top + this.rows[aFormulaIntersection.r2].height - offsetY + this.height_1px + this.height_2px; // Выбираем наибольший range для очистки x1 = Math.min(x1, _x1); x2 = Math.max(x2, _x2); y1 = Math.min(y1, _y1); y2 = Math.max(y2, _y2); } } } } for (i = 0; i < this.arrActiveFormulaRanges.length; ++i) { this.arrActiveFormulaRanges[i].ranges.forEach(function (item) { var arnIntersection = item.intersectionSimple(range); if (arnIntersection) { _x1 = t.cols[arnIntersection.c1].left - offsetX - t.width_3px; _x2 = arnIntersection.c2 > t.cols.length ? width : t.cols[arnIntersection.c2].left + t.cols[arnIntersection.c2].width - offsetX + t.width_1px + t.width_2px; _y1 = t.rows[arnIntersection.r1].top - offsetY - t.height_3px; _y2 = arnIntersection.r2 > t.rows.length ? height : t.rows[arnIntersection.r2].top + t.rows[arnIntersection.r2].height - offsetY + t.height_1px + t.height_2px; x1 = Math.min(x1, _x1); x2 = Math.max(x2, _x2); y1 = Math.min(y1, _y1); y2 = Math.max(y2, _y2); } }); } for (i = 0; i < this.arrActiveChartRanges.length; ++i) { this.arrActiveChartRanges[i].ranges.forEach(function (item) { var arnIntersection = item.intersectionSimple(range); if (arnIntersection) { _x1 = t.cols[arnIntersection.c1].left - offsetX - t.width_3px; _x2 = arnIntersection.c2 > t.cols.length ? width : t.cols[arnIntersection.c2].left + t.cols[arnIntersection.c2].width - offsetX + t.width_1px + t.width_2px; _y1 = t.rows[arnIntersection.r1].top - offsetY - t.height_3px; _y2 = arnIntersection.r2 > t.rows.length ? height : t.rows[arnIntersection.r2].top + t.rows[arnIntersection.r2].height - offsetY + t.height_1px + t.height_2px; x1 = Math.min(x1, _x1); x2 = Math.max(x2, _x2); y1 = Math.min(y1, _y1); y2 = Math.max(y2, _y2); } }); } 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"); } // Координаты для перемещения диапазона _x1 = this.cols[activeMoveRangeClone.c1].left - offsetX - this.width_2px; _x2 = this.cols[activeMoveRangeClone.c2].left + this.cols[activeMoveRangeClone.c2].width - offsetX + this.width_1px + this.width_2px; _y1 = this.rows[activeMoveRangeClone.r1].top - offsetY - this.height_2px; _y2 = this.rows[activeMoveRangeClone.r2].top + this.rows[activeMoveRangeClone.r2].height - offsetY + this.height_1px + this.height_2px; // Выбираем наибольший range для очистки x1 = Math.min(x1, _x1); x2 = Math.max(x2, _x2); y1 = Math.min(y1, _y1); y2 = Math.max(y2, _y2); } if (null !== this.copyActiveRange) { this.copyActiveRange.ranges.forEach(function (item) { var arnIntersection = item.intersectionSimple(range); if (arnIntersection) { _x1 = t.cols[arnIntersection.c1].left - offsetX - t.width_2px; _x2 = t.cols[arnIntersection.c2].left + t.cols[arnIntersection.c2].width - offsetX + t.width_1px + /* Это ширина "квадрата" для автофильтра от границы ячейки */t.width_2px; _y1 = t.rows[arnIntersection.r1].top - offsetY - t.height_2px; _y2 = t.rows[arnIntersection.r2].top + t.rows[arnIntersection.r2].height - offsetY + t.height_1px + /* Это высота "квадрата" для автофильтра от границы ячейки */t.height_2px; x1 = Math.min(x1, _x1); x2 = Math.max(x2, _x2); y1 = Math.min(y1, _y1); y2 = Math.max(y2, _y2); } }); } 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(); }; WorksheetView.prototype.updateSelectionWithSparklines = function () { if (!this.checkSelectionSparkline()) { 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 : Asc.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 : Asc.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.model.selectionRange.getLast(); } 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://bugzilla.onlyoffice.com/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 (!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), Asc.c_oAscMaxColumnWidth); var modelw = this.model.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(); var va = c.getAlignVertical(); if (this._isCellEmptyTextString(c)) { if (!angle && c.isNotDefaultFont()) { // Пустая ячейка с измененной гарнитурой или размером, учитвается в высоте str = c.getValue2(); if (0 < str.length) { strCopy = str[0]; if (!(tm = AscCommonExcel.g_oCacheMeasureEmpty.get(strCopy.format))) { // Без текста не будет толка strCopy = strCopy.clone(); strCopy.text = 'A'; tm = this._roundTextMetrics(this.stringRender.measureString([strCopy], fl)); AscCommonExcel.g_oCacheMeasureEmpty.add(strCopy.format, tm); } this._updateRowHeight(tm, col, row, fl, isMerged, fMergedRows, va); } } 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; } var rowHeight = this.rows[row].height; // ToDo dDigitsCount нужно рассчитывать исходя не из дефалтового шрифта и размера, а исходя из текущего шрифта и размера ячейки str = c.getValue2(dDigitsCount, makeFnIsGoodNumFormat(fl, colWidth)); var ha = c.getAlignHorizontalByValue(); 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 textBound = {}; if (angle) { // повернутый текст учитывает мерж ячеек по строкам if (fMergedRows) { rowHeight = 0; for (var j = mc.r1; j <= mc.r2 && j < this.nRowsCount; ++j) { rowHeight += this.rows[j].height; } } var textW = tm.width; if (fl.wrapText) { if (this.rows[row].isCustomHeight) { tm = this._roundTextMetrics(this.stringRender.measureString(str, fl, rowHeight)); textBound = this.stringRender.getTransformBound(angle, colWidth, rowHeight, tm.width, ha, va, rowHeight); } else { if (!fMergedRows) { rowHeight = tm.height; } tm = this._roundTextMetrics(this.stringRender.measureString(str, fl, rowHeight)); textBound = this.stringRender.getTransformBound(angle, colWidth, rowHeight, tm.width, ha, va, tm.width); } } else { textBound = this.stringRender.getTransformBound(angle, colWidth, rowHeight, textW, 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: (c.getFont().getColor() || 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); } fMergedRows = fMergedRows || (isMerged && fl.wrapText); this._updateRowHeight(tm, col, row, fl, isMerged, fMergedRows, va, ha, angle, maxW, colWidth, textBound); return mc ? mc.c2 : col; }; WorksheetView.prototype._updateRowHeight = function (tm, col, row, flags, isMerged, fMergedRows, va, ha, angle, maxW, colWidth, textBound) { var rowInfo = this.rows[row]; // update row's descender if (va !== Asc.c_oAscVAlign.Top && va !== Asc.c_oAscVAlign.Center && !isMerged) { rowInfo.descender = Math.max(rowInfo.descender, tm.height - tm.baseline); } // update row's height // Замерженная ячейка (с 2-мя или более строками) не влияет на высоту строк! if (!rowInfo.isCustomHeight && !(window["NATIVE_EDITOR_ENJINE"] && this.notUpdateRowHeight) && !fMergedRows) { var newHeight = tm.height + this.height_1px; if (angle && textBound) { newHeight = Math.max(rowInfo.height, textBound.height); } newHeight = Math.min(this.maxRowHeight, Math.max(rowInfo.height, newHeight)); if (newHeight !== rowInfo.height) { rowInfo.heightReal = rowInfo.height = newHeight; History.TurnOff(); this.model.setRowHeight(rowInfo.height, row, row, false); History.TurnOn(); if (angle) { if (flags.wrapText && !rowInfo.isCustomHeight) { maxW = tm.width; } textBound = this.stringRender.getTransformBound(angle, colWidth, rowInfo.height, tm.width, ha, va, maxW); this._fetchCellCache(col, row).text.textBound = textBound; } 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 === AscCommon.align_Center ? (textWidth + maxWidth) * 0.5 : textWidth + pad; if (textAlign === AscCommon.align_Right || textAlign === AscCommon.align_Center) { 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 !== AscCommon.align_Right) { 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 === AscCommon.align_Center) { maxWidth = (sideL[ls] - sideL[0]) + sideR[rs]; } else { maxWidth = textAlign === AscCommon.align_Right ? 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 AscCommon.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(); } 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.model.selectionRange.getLast(); if (c_oAscSelectionType.RangeMax !== ar.type) { 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(); this._updateSelectionNameAndInfo(); } }; WorksheetView.prototype._selectRowsByRange = function () { var ar = this.model.selectionRange.getLast(); if (c_oAscSelectionType.RangeMax !== ar.type) { 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(); this._updateSelectionNameAndInfo(); } }; /** * Возвращает 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; }; 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) { 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 AscCommon.align_Center: return asc_calcnpt(0.5 * (x1 + x2 + this.width_1px - tm.width), this._getPPIX()); case AscCommon.align_Right: return x2 + this.width_1px - this.width_padding - tm.width; case AscCommon.align_Justify: default: return x1 + this.width_padding; } }; WorksheetView.prototype._calcTextVertPos = function (y1, y2, baseline, tm, valign) { switch (valign) { case Asc.c_oAscVAlign.Center: return asc_calcnpt(0.5 * (y1 + y2 - tm.height), this._getPPIY()) - this.height_1px; // ToDo возможно стоит тоже использовать 96 case Asc.c_oAscVAlign.Top: return y1 - this.height_1px; default: return baseline - tm.baseline; } }; WorksheetView.prototype._calcTextWidth = function (x1, x2, tm, halign) { switch (halign) { case AscCommon.align_Justify: 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.getMaxColRow() : new AscCommon.CellBase(-1, -1); 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.col), row: Math.max(lastR, obr.row)}; } 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(AscCommonExcel.recalcType.newLines); } if (newRow >= t.rows.length && newRow <= gc_nMaxRow0) { t.nRowsCount = newRow + 1; t._calcHeightRows(AscCommonExcel.recalcType.newLines); } 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 fixStartRow = new asc_Range(vr.c1, vr.r1, vr.c2, vr.r1); this._fixSelectionOfHiddenCells(0, delta >= 0 ? +1 : -1, fixStartRow); var start = this._calcCellPosition(vr.c1, fixStartRow.r1, 0, delta).row; fixStartRow.assign(vr.c1, start, vr.c2, 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.model.selectionRange.activeCell.row >= rFrozen) { editor.move(this.cellsLeft + (this.model.selectionRange.activeCell.col >= 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://bugzilla.onlyoffice.com/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.af_drawButtons(range, offsetX, offsetY); this.objectRender.showDrawingObjectsEx(false, new AscFormat.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.af_drawButtons(range, offsetX, offsetY); this.objectRender.showDrawingObjectsEx(false, new AscFormat.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(); window["Asc"]["editor"].wb.clipboard.updateSpecialPasteOptionsPosition(this); return this; }; WorksheetView.prototype.scrollHorizontal = function (delta, editor) { var vr = this.visibleRange; var fixStartCol = new asc_Range(vr.c1, vr.r1, vr.c1, vr.r2); this._fixSelectionOfHiddenCells(delta >= 0 ? +1 : -1, 0, fixStartCol); var start = this._calcCellPosition(fixStartCol.c1, vr.r1, delta, 0).col; fixStartCol.assign(start, vr.r1, start, vr.r2); 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.model.selectionRange.activeCell.col >= cFrozen) { editor.move(this.cellsLeft + diffWidth, this.cellsTop + (this.model.selectionRange.activeCell.row >= 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://bugzilla.onlyoffice.com/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.af_drawButtons(range, offsetX, offsetY); this.objectRender.showDrawingObjectsEx(false, new AscFormat.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.af_drawButtons(range, offsetX, offsetY); this.objectRender.showDrawingObjectsEx(false, new AscFormat.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 AscFormat.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._hitResizeCorner = function (x1, y1, x2, y2) { var wEps = this.width_1px, hEps = this.height_1px; return Math.abs(x2 - x1) <= wEps + this.width_2px && Math.abs(y2 - y1) <= hEps + this.height_2px; }; WorksheetView.prototype._hitInRange = function (range, rangeType, vr, x, y, offsetX, offsetY) { var wEps = this.width_2px, hEps = this.height_2px; var cursor, x1, x2, y1, y2, isResize; var col = -1, row = -1; var oFormulaRangeIn = range.intersectionSimple(vr); if (oFormulaRangeIn) { x1 = this.cols[oFormulaRangeIn.c1].left - offsetX; x2 = this.cols[oFormulaRangeIn.c2].left + this.cols[oFormulaRangeIn.c2].width - offsetX; y1 = this.rows[oFormulaRangeIn.r1].top - offsetY; y2 = this.rows[oFormulaRangeIn.r2].top + this.rows[oFormulaRangeIn.r2].height - offsetY; isResize = AscCommonExcel.selectionLineType.Resize & rangeType; if (isResize && this._hitResizeCorner(x1 - this.width_1px, y1 - this.height_1px, x, y)) { /*TOP-LEFT*/ cursor = kCurSEResize; col = range.c2; row = range.r2; } else if (isResize && this._hitResizeCorner(x2, y1 - this.height_1px, x, y)) { /*TOP-RIGHT*/ cursor = kCurNEResize; col = range.c1; row = range.r2; } else if (isResize && this._hitResizeCorner(x1 - this.width_1px, y2, x, y)) { /*BOTTOM-LEFT*/ cursor = kCurNEResize; col = range.c2; row = range.r1; } else if (this._hitResizeCorner(x2, y2, x, y)) { /*BOTTOM-RIGHT*/ cursor = kCurSEResize; col = range.c1; row = range.r1; } else if ((((range.c1 === oFormulaRangeIn.c1 && Math.abs(x - x1) <= wEps) || (range.c2 === oFormulaRangeIn.c2 && Math.abs(x - x2) <= wEps)) && hEps <= y - y1 && y - y2 <= hEps) || (((range.r1 === oFormulaRangeIn.r1 && Math.abs(y - y1) <= hEps) || (range.r2 === oFormulaRangeIn.r2 && Math.abs(y - y2) <= hEps)) && wEps <= x - x1 && x - x2 <= wEps)) { cursor = kCurMove; } } return cursor ? { cursor: cursor, col: col, row: row } : null; }; WorksheetView.prototype._hitCursorSelectionRange = function (vr, x, y, offsetX, offsetY) { var res = this._hitInRange(this.model.selectionRange.getLast(), AscCommonExcel.selectionLineType.Selection | AscCommonExcel.selectionLineType.ActiveCell | AscCommonExcel.selectionLineType.Promote, vr, x, y, offsetX, offsetY); return res ? { cursor: kCurMove === res.cursor ? kCurMove : kCurFillHandle, target: kCurMove === res.cursor ? c_oTargetType.MoveRange : c_oTargetType.FillHandle, col: -1, row: -1 } : null; }; WorksheetView.prototype._hitCursorFormulaOrChart = function (vr, x, y, offsetX, offsetY) { var i, l, res; var oFormulaRange; var arrRanges = this.isFormulaEditMode ? this.arrActiveFormulaRanges : this.arrActiveChartRanges; var targetArr = this.isFormulaEditMode ? 0 : -1; for (i = 0, l = arrRanges.length; i < l; ++i) { oFormulaRange = arrRanges[i].getLast(); res = !oFormulaRange.isName && this._hitInRange(oFormulaRange, AscCommonExcel.selectionLineType.Resize, vr, x, y, offsetX, offsetY); if (res) { break; } } return res ? { cursor: res.cursor, target: c_oTargetType.MoveResizeRange, col: res.col, row: res.row, formulaRange: oFormulaRange, indexFormulaRange: i, targetArr: targetArr } : null; }; WorksheetView.prototype.getCursorTypeFromXY = function (x, y, isViewerMode) { var res, c, r, f, i, offsetX, offsetY, cellCursor; var sheetId = this.model.getId(), userId, lockRangePosLeft, lockRangePosTop, lockInfo, oHyperlink; var widthDiff = 0, heightDiff = 0, isLocked = false, 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, AscCommonExcel.c_oAscLockNameFrozenPane); isLocked = this.collaborativeEditing.getLockIntersection(lockInfo, c_oAscLockTypes.kLockTypeOther, false); if (false !== isLocked) { // Кто-то сделал lock var frozenCell = this.topLeftFrozenCell ? this.topLeftFrozenCell : new AscCommon.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 && AscCommonExcel.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 AscCommonExcel.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()); 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.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}; } if (this.isFormulaEditMode || this.isChartAreaEditMode) { this._drawElements(function (_vr, _offsetX, _offsetY) { return (null === (res = this._hitCursorFormulaOrChart(_vr, x, y, _offsetX, _offsetY))); }); if (res) { return res; } } isSelGraphicObject = this.objectRender.selectedGraphicObjectsExists(); if (!isViewerMode && !isSelGraphicObject && this.model.selectionRange.isSingleRange()) { this._drawElements(function (_vr, _offsetX, _offsetY) { return (null === (res = this._hitCursorSelectionRange(_vr, x, y, _offsetX, _offsetY))); }); if (res) { return res; } } 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 AscCommonExcel.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.af_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 tmpSelection = this._getSelection(); var ar = fixedRange ? fixedRange : (tmpSelection ? tmpSelection.getLast() : null); 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(); if (!fixedRange) { tmpSelection.update(); } }; 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.model.selectionRange.getLast(), 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); }; WorksheetView.prototype._moveActiveCellToXY = function (x, y) { var c, r; var ar = this._getSelection().getLast(); 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.model.selectionRange.setCell(0, 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.model.selectionRange.setCell(r, 0); 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(); this.model.selectionRange.setCell(0, c); } 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.model.selectionRange.setCell(r, c); this._fixSelectionOfMergedCells(); } }; WorksheetView.prototype._moveActiveCellToOffset = function (dc, dr) { var tmpSelection = this._getSelection(); var ar = tmpSelection.getLast(); var activeCell = tmpSelection.activeCell; var mc = this.model.getMergedByCell(activeCell.row, activeCell.col); var c = mc ? (dc < 0 ? mc.c1 : dc > 0 ? Math.min(mc.c2, this.nColsCount - 1 - dc) : activeCell.col) : activeCell.col; var r = mc ? (dr < 0 ? mc.r1 : dr > 0 ? Math.min(mc.r2, this.nRowsCount - 1 - dr) : activeCell.row) : activeCell.row; var p = this._calcCellPosition(c, r, dc, dr); ar.assign(p.col, p.row, p.col, p.row); ar.type = c_oAscSelectionType.RangeCells; this.model.selectionRange.setCell(p.row, p.col); this._fixSelectionOfHiddenCells(dc >= 0 ? +1 : -1, dr >= 0 ? +1 : -1); this._fixSelectionOfMergedCells(); }; // Движение активной ячейки в выделенной области WorksheetView.prototype._moveActivePointInSelection = function (dc, dr) { var t = this, cell = this.model.selectionRange.activeCell; // Если мы на скрытой строке или ячейке, то двигаться в выделении нельзя (так делает и Excel) if (this.width_1px > this.cols[cell.col].width || this.height_1px > this.rows[cell.row].height) { return; } return this.model.selectionRange.offsetCell(dr, dc, function (row, col) { return (0 <= row) ? (t.rows[row].height < t.height_1px) : (t.cols[col].width < t.width_1px); }); }; WorksheetView.prototype._calcSelectionEndPointByXY = function (x, y) { var tmpSelection = this._getSelection(); var ar = tmpSelection.getLast(); x *= asc_getcvt(0/*px*/, 1/*pt*/, this._getPPIX()); y *= asc_getcvt(0/*px*/, 1/*pt*/, this._getPPIY()); var res = new asc_Range(tmpSelection.activeCell.col, tmpSelection.activeCell.row, this._findColUnderCursor( x).col, this._findRowUnderCursor(y).row, true); if (ar.type === c_oAscSelectionType.RangeCells) { this._fixSelectionOfMergedCells(res); } return res; }; WorksheetView.prototype._calcSelectionEndPointByOffset = function (dc, dr) { var tmpSelection = this._getSelection(); var ar = tmpSelection.getLast(); var c1, r1, c2, r2, tmp; tmp = asc.getEndValueRange(dc, tmpSelection.activeCell.col, ar.c1, ar.c2); c1 = tmp.x1; c2 = tmp.x2; tmp = asc.getEndValueRange(dr, tmpSelection.activeCell.row, 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 ar = this._getSelection().getLast(); 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._getSelection().getLast(); 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 {Range} [range] * @returns {{deltaX: number, deltaY: number}} */ WorksheetView.prototype._calcActiveCellOffset = function (range) { var vr = this.visibleRange; var activeCell = this.model.selectionRange.activeCell; var ar = range ? range : this.model.selectionRange.getLast(); var mc = this.model.getMergedByCell(activeCell.row, activeCell.col); var startCol = mc ? mc.c1 : activeCell.col; var startRow = mc ? mc.r1 : activeCell.row; 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) { // ToDo now check only last selection range var arn = this.model.selectionRange.getLast().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.getSelectionSortInfo = function () { //в случае попытки сортировать мультиселект, необходимо выдавать ошибку var arn = this.model.selectionRange.getLast().clone(true); //null - не выдавать сообщение и не расширять, false - не выдавать сообщение и расширЯть, true - выдавать сообщение var bResult = false; //в случае одной выделенной ячейки - всегда не выдаём сообщение и автоматически расширяем if(!arn.isOneCell()) { var colCount = arn.c2 - arn.c1 + 1; var rowCount = arn.r2 - arn.r1 + 1; //если выделено более одного столбца и более одной строки - не выдаем сообщение и не расширяем if(colCount > 1 && rowCount > 1) { bResult = null; } else { //далее проверяем есть ли смежные ячейки у startCol/startRow var activeCell = this.model.selectionRange.activeCell; var activeCellRange = new Asc.Range(activeCell.col, activeCell.row, activeCell.col, activeCell.row); var expandRange = this.model.autoFilters._getAdjacentCellsAF(activeCellRange); //если диапазон не расширяется за счет близлежащих ячеек - не выдаем сообщение и не расширяем if(arn.isEqual(expandRange) || activeCellRange.isEqual(expandRange)) { bResult = null; } else { bResult = true; } } } return bResult; }; WorksheetView.prototype.getSelectionMathInfo = function () { var oSelectionMathInfo = new asc_CSelectionMathInfo(); var sum = 0; var oExistCells = {}; if (window["NATIVE_EDITOR_ENJINE"]) { return oSelectionMathInfo; } var t = this; this.model.selectionRange.ranges.forEach(function (item) { var tmp; var range = t.model.getRange3(item.r1, item.c1, item.r2, item.c2); range._setPropertyNoEmpty(null, null, function (cell, r) { var idCell = cell.nCol + '-' + cell.nRow; if (!oExistCells[idCell] && !cell.isEmptyTextString() && t.height_1px <= t.rows[r].height) { oExistCells[idCell] = true; ++oSelectionMathInfo.count; if (CellValueType.Number === cell.getType()) { tmp = parseFloat(cell.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://bugzilla.onlyoffice.com/show_bug.cgi?id=24115) if (1 < oSelectionMathInfo.countNumbers) { // Мы должны отдавать в формате активной ячейки var numFormat = this.model.getRange3(this.model.selectionRange.row, this.model.selectionRange.cell, this.model.selectionRange.row, this.model.selectionRange.cell).getNumFormat(); if (Asc.c_oAscNumFormatType.Time === numFormat.getType()) { // Для времени нужно отдавать в формате [h]:mm:ss (http://bugzilla.onlyoffice.com/show_bug.cgi?id=26271) numFormat = AscCommon.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.model.selectionRange.getLast(); var cell = this.model.selectionRange.activeCell; var mc = this.model.getMergedByCell(cell.row, cell.col); var c1 = mc ? mc.c1 : cell.col, r1 = mc ? mc.r1 : cell.row, 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); if (selectionSize) { return selectionSize; } var dN = new Asc.Range(ar_norm.c1, ar_norm.r1, c2, r2, true); var defName = parserHelp.get3DRef(this.model.getName(), dN.getAbsName()); defName = this.model.workbook.findDefinesNames(defName, this.model.getId()); if (defName) { return defName; } return this._getColumnTitle(c1) + this._getRowTitle(r1); }; WorksheetView.prototype.getSelectionRangeValue = function () { // ToDo проблема с выбором целого столбца/строки var ar = this.model.selectionRange.getLast().clone(true); var sName = ar.getAbsName(); return (c_oAscSelectionDialogType.FormatTable === this.selectionDialogType) ? sName : parserHelp.get3DRef(this.model.getName(), sName); }; WorksheetView.prototype.getSelectionInfo = function () { return this.objectRender.selectedGraphicObjectsExists() ? this._getSelectionInfoObject() : this._getSelectionInfoCell(); }; WorksheetView.prototype._getSelectionInfoCell = function () { var c_opt = this.settings.cells; var selectionRange = this.model.selectionRange; var cell = selectionRange.activeCell; var mc = this.model.getMergedByCell(cell.row, cell.col); var c1 = mc ? mc.c1 : cell.col; var r1 = mc ? mc.r1 : cell.row; var c = this._getVisibleCell(c1, r1); var font = c.getFont(); var fa = font.getVerticalAlign(); var fc = font.getColor(); var bg = c.getFill(); 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 = AscCommonExcel.horizontalAlignToString(c.getAlignHorizontal()); cell_info.valign = AscCommonExcel.verticalAlignToString(c.getAlignVertical()); var tablePartsOptions = selectionRange.isSingleRange() ? this.model.autoFilters.searchRangeInTableParts(selectionRange.getLast()) : -2; var curTablePart = tablePartsOptions >= 0 ? this.model.TableParts[tablePartsOptions] : null; var tableStyleInfo = curTablePart && curTablePart.TableStyleInfo ? curTablePart.TableStyleInfo : null; cell_info.autoFilterInfo = new asc_CAutoFilterInfo(); if (-2 === tablePartsOptions) { cell_info.autoFilterInfo.isAutoFilter = null; cell_info.autoFilterInfo.isApplyAutoFilter = false; } else { var checkApplyFilterOrSort = this.model.autoFilters.checkApplyFilterOrSort(tablePartsOptions); cell_info.autoFilterInfo.isAutoFilter = checkApplyFilterOrSort.isAutoFilter; cell_info.autoFilterInfo.isApplyAutoFilter = checkApplyFilterOrSort.isFilterColumns; } if (curTablePart !== null) { cell_info.formatTableInfo = new AscCommonExcel.asc_CFormatTableInfo(); cell_info.formatTableInfo.tableName = curTablePart.DisplayName; if (tableStyleInfo) { cell_info.formatTableInfo.tableStyleName = tableStyleInfo.Name; cell_info.formatTableInfo.bandVer = tableStyleInfo.ShowColumnStripes; cell_info.formatTableInfo.firstCol = tableStyleInfo.ShowFirstColumn; cell_info.formatTableInfo.lastCol = tableStyleInfo.ShowLastColumn; cell_info.formatTableInfo.bandHor = tableStyleInfo.ShowRowStripes; } cell_info.formatTableInfo.lastRow = curTablePart.TotalsRowCount !== null; cell_info.formatTableInfo.firstRow = curTablePart.HeaderRowCount === null; cell_info.formatTableInfo.tableRange = curTablePart.Ref.getAbsName(); cell_info.formatTableInfo.filterButton = curTablePart.isShowButton(); this.af_setDisableProps(curTablePart, cell_info.formatTableInfo); } cell_info.styleName = c.getStyleName(); cell_info.angle = c.getAngle(); cell_info.flags = new AscCommonExcel.asc_CCellFlag(); cell_info.flags.shrinkToFit = c.getShrinkToFit(); cell_info.flags.wrapText = c.getWrap(); // ToDo activeRange type cell_info.flags.selectionType = selectionRange.getLast().type; cell_info.flags.multiselect = !selectionRange.isSingleRange(); cell_info.flags.lockText = ("" !== cell_info.text && (isNumberFormat || "" !== cell_info.formula)); cell_info.font = new asc_CFont(); cell_info.font.name = font.getName(); cell_info.font.size = font.getSize(); cell_info.font.bold = font.getBold(); cell_info.font.italic = font.getItalic(); // ToDo убрать, когда будет реализовано двойное подчеркивание cell_info.font.underline = (Asc.EUnderline.underlineNone !== font.getUnderline()); cell_info.font.strikeout = font.getStrikeout(); cell_info.font.subscript = fa === AscCommon.vertalign_SubScript; cell_info.font.superscript = fa === AscCommon.vertalign_SuperScript; cell_info.font.color = (fc ? asc_obj2Color(fc) : new Asc.asc_CColor(c_opt.defaultState.color)); cell_info.fill = new asc_CFill((null != bg) ? asc_obj2Color(bg) : bg); cell_info.numFormatType = c.getNumFormatType(); // Получаем гиперссылку (//ToDo) var ar = selectionRange.getLast().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(); var sheetId = this.model.getId(); var lockInfo; // Пересчет для входящих ячеек в добавленные строки/столбцы var isIntersection = this._recalcRangeByInsertRowsAndColumns(sheetId, ar); if (false === isIntersection) { lockInfo = this.collaborativeEditing.getLockInfo(c_oAscLockTypeElem.Range, /*subType*/null, sheetId, new AscCommonExcel.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; } } if (null !== curTablePart) { var tableAr = curTablePart.Ref.clone(); isIntersection = this._recalcRangeByInsertRowsAndColumns(sheetId, tableAr); if (false === isIntersection) { lockInfo = this.collaborativeEditing.getLockInfo(c_oAscLockTypeElem.Range, /*subType*/null, sheetId, new AscCommonExcel.asc_CCollaborativeRange(tableAr.c1, tableAr.r1, tableAr.c2, tableAr.r2)); if (false !== this.collaborativeEditing.getLockIntersection(lockInfo, c_oAscLockTypes.kLockTypeOther, /*bCheckOnlyLockAll*/false)) { // Уже таблицу кто-то редактирует cell_info.isLockedTable = true; } } } cell_info.sparklineInfo = this.model.getSparklineGroup(c1, r1); if (cell_info.sparklineInfo) { lockInfo = this.collaborativeEditing.getLockInfo(c_oAscLockTypeElem.Object, /*subType*/null, sheetId, cell_info.sparklineInfo.Get_Id()); if (false !== this.collaborativeEditing.getLockIntersection(lockInfo, c_oAscLockTypes.kLockTypeOther, /*bCheckOnlyLockAll*/false)) { cell_info.isLockedSparkline = true; } } return cell_info; }; WorksheetView.prototype._getSelectionInfoObject = function () { var objectInfo = new asc_CCellInfo(); objectInfo.flags = new AscCommonExcel.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 (!paraPr && textPr) { paraPr = new CParaPr(); } if (textPr && paraPr) { objectInfo.text = this.objectRender.controller.Get_SelectedText(true); var horAlign = paraPr.Jc; var vertAlign = Asc.c_oAscVAlign.Center; var shape_props = this.objectRender.controller.getDrawingProps().shapeProps; var angle = null; if (shape_props) { switch (shape_props.verticalTextAlign) { case AscFormat.VERTICAL_ANCHOR_TYPE_BOTTOM: vertAlign = Asc.c_oAscVAlign.Bottom; break; case AscFormat.VERTICAL_ANCHOR_TYPE_CENTER: vertAlign = Asc.c_oAscVAlign.Center; break; case AscFormat.VERTICAL_ANCHOR_TYPE_TOP: case AscFormat.VERTICAL_ANCHOR_TYPE_DISTRIBUTED: case AscFormat.VERTICAL_ANCHOR_TYPE_JUSTIFIED: vertAlign = Asc.c_oAscVAlign.Top; break; } switch (shape_props.vert) { case AscFormat.nVertTTvert: angle = 90; break; case AscFormat.nVertTTvert270: angle = 270; break; default: angle = 0; break; } } objectInfo.halign = AscCommonExcel.horizontalAlignToString(horAlign); objectInfo.valign = AscCommonExcel.verticalAlignToString(vertAlign); objectInfo.angle = angle; objectInfo.font = new asc_CFont(); objectInfo.font.name = textPr.FontFamily ? textPr.FontFamily.Name : null; 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 == AscCommon.vertalign_SubScript; objectInfo.font.superscript = textPr.VertAlign == AscCommon.vertalign_SuperScript; if (textPr.Color) { objectInfo.font.color = AscCommon.CreateAscColorCustom(textPr.Color.r, textPr.Color.g, textPr.Color.b); } var shapeHyperlink = this.objectRender.controller.getHyperlinkInfo(); if (shapeHyperlink && (shapeHyperlink instanceof ParaHyperlink)) { var hyperlink = new AscCommonExcel.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 = null; objectInfo.font.size = null; } // Заливка не нужна как таковая objectInfo.fill = new asc_CFill(null); // ToDo locks return objectInfo; }; // Получаем координаты активной ячейки WorksheetView.prototype.getActiveCellCoord = function () { return this.getCellCoord(this.model.selectionRange.activeCell.col, this.model.selectionRange.activeCell.row); }; WorksheetView.prototype.getCellCoord = function (col, row) { 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 ( col >= cFrozen ) { offsetX = offsetFrozen.offsetX; } else { vrCol = 0; } if ( row >= rFrozen ) { offsetY = offsetFrozen.offsetY; } else { vrRow = 0; } } var xL = this.getCellLeft( col, /*pt*/1 ); var yL = this.getCellTop( row, /*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( col, /*px*/0 ); var height = this.getRowHeight( row, /*px*/0 ); if ( AscBrowser.isRetina ) { xL >>= 1; yL >>= 1; width >>= 1; height >>= 1; } return new AscCommon.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.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.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); } } var oRes = null; var type = range.type; if (type == c_oAscSelectionType.RangeCells || type == c_oAscSelectionType.RangeCol || type == c_oAscSelectionType.RangeRow || type == c_oAscSelectionType.RangeMax) { this.cleanSelection(); this.model.selectionRange.assign2(range); this._fixSelectionOfMergedCells(); this.updateSelectionWithSparklines(); this._updateSelectionNameAndInfo(); oRes = this._calcActiveCellOffset(); } return oRes; }; WorksheetView.prototype.changeSelectionStartPoint = function (x, y, isCoord, isSelectMode, isCtrl) { this.cleanSelection(); if (!this.isFormulaEditMode) { this.cleanFormulaRanges(); if (isCtrl) { this.model.selectionRange.addRange(); } else { this.model.selectionRange.clean(); } } var ar = this._getSelection().getLast().clone(); var ret = {}; var isChangeSelectionShape = false; 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); ret = this._calcActiveRangeOffset(); } if (this.isSelectionDialogMode) { if (!this.model.selectionRange.isEqual(ar)) { // Смена диапазона this.handlers.trigger("selectionRangeChanged", this.getSelectionRangeValue()); } } else if (!this.isCellEditMode) { if (isChangeSelectionShape || !this.model.selectionRange.isEqual(ar)) { this.handlers.trigger("selectionNameChanged", this.getSelectionName(/*bRangeText*/false)); if (!isSelectMode) { this.handlers.trigger("selectionChanged"); this.handlers.trigger("selectionMathInfoChanged", this.getSelectionMathInfo()); } } } if (!isChangeSelectionShape) { if (!isCoord) { this.updateSelectionWithSparklines(); } else { this._drawSelection(); } } //ToDo this.drawDepCells(); return ret; }; // Смена селекта по нажатию правой кнопки мыши WorksheetView.prototype.changeSelectionStartPointRightClick = function (x, y) { var isChangeSelectionShape = this._checkSelectionShape(); this.model.workbook.handlers.trigger("asc_onHideComment"); var _x = x * asc_getcvt(0/*px*/, 1/*pt*/, this._getPPIX()); var _y = y * asc_getcvt(0/*px*/, 1/*pt*/, this._getPPIY()); var val, c1, c2, r1, r2; val = this._findColUnderCursor(_x, true); if (val) { c1 = c2 = val.col; } else { c1 = 0; c2 = gc_nMaxCol0; } val = this._findRowUnderCursor(_y, true); if (val) { r1 = r2 = val.row; } else { r1 = 0; r2 = gc_nMaxRow0; } if (isChangeSelectionShape) { // Попали в выделение, но были в объекте this.cleanSelection(); this._drawSelection(); this._updateSelectionNameAndInfo(); } else if (!this.model.selectionRange.containsRange(new asc_Range(c1, r1, c2, r2))) { // Не попали в выделение (меняем первую точку) this.cleanSelection(); this.model.selectionRange.clean(); this._moveActiveCellToXY(x, y); this._drawSelection(); this._updateSelectionNameAndInfo(); return false; } 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._getSelection().getLast(); 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.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(); }; // Окончание выделения WorksheetView.prototype.changeSelectionDone = function () { if (this.stateFormatPainter) { this.applyFormatPainter(); } else { this.checkSelectionSparkline(); } }; // Обработка движения в выделенной области WorksheetView.prototype.changeSelectionActivePoint = function (dc, dr) { var ret; if (!this._moveActivePointInSelection(dc, dr)) { return this.changeSelectionStartPoint(dc, dr, /*isCoord*/false, /*isSelectMode*/false, false); } // Очищаем выделение this.cleanSelection(); // Перерисовываем this.updateSelectionWithSparklines(); // Смотрим, ушли ли мы за границу видимой области ret = this._calcActiveCellOffset(); // Эвент обновления this.handlers.trigger("selectionNameChanged", this.getSelectionName(/*bRangeText*/false)); this.handlers.trigger("selectionChanged"); return ret; }; WorksheetView.prototype.checkSelectionSparkline = function () { if (!this.getSelectionShape() && !this.isFormulaEditMode && !this.isCellEditMode) { var cell = this.model.selectionRange.activeCell; var mc = this.model.getMergedByCell(cell.row, cell.col); var c1 = mc ? mc.c1 : cell.col; var r1 = mc ? mc.r1 : cell.row; var oSparklineInfo = this.model.getSparklineGroup(c1, r1); if (oSparklineInfo) { this.cleanSelection(); this.cleanFormulaRanges(); var range = oSparklineInfo.getLocationRanges(); range.ranges.forEach(function (item) { item.isName = true; item.noColor = true; }); this.arrActiveFormulaRanges.push(range); this._drawSelection(); return true; } } }; // ----- Changing cells ----- WorksheetView.prototype.applyFormatPainter = function () { var t = this; var from = t.handlers.trigger('getRangeFormatPainter').getLast(), to = this.model.selectionRange.getLast().getAllRange(); var onApplyFormatPainterCallback = function (isSuccess) { // Очищаем выделение t.cleanSelection(); if (true === isSuccess) { AscCommonExcel.promoteFromTo(from, t.model, to, t.model); } 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.handlers.trigger('onStopFormatPainter'); } // Перерисовываем t._recalculateAfterUpdate([to]); }; var result = AscCommonExcel.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.model.selectionRange.clone(); this._drawFormatPainterRange(); } else { this.cleanSelection(); this.copyActiveRange = null; this._drawSelection(); } return this.copyActiveRange; }; /* Функция для работы автозаполнения (selection). (x, y) - координаты точки мыши на области */ WorksheetView.prototype.changeSelectionFillHandle = function (x, y) { // Возвращаемый результат var ret = null; // Если мы только первый раз попали сюда, то копируем выделенную область if (null === this.activeFillHandle) { this.activeFillHandle = this.model.selectionRange.getLast().clone(); // Для первого раза нормализуем (т.е. первая точка - это левый верхний угол) 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.model.selectionRange.getLast().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.model.selectionRange.getLast(); 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 = arn.clone(); // Очищаем выделение this.cleanSelection(); if (2 === this.fillHandleArea) { // Мы внутри, будет удаление cбрасываем первую ячейку // Проверяем, удалили ли мы все (если да, то область не меняется) if (arn.c1 !== this.activeFillHandle.c2 || arn.r1 !== this.activeFillHandle.r2) { // Уменьшаем диапазон (мы удалили не все) if (0 === this.fillHandleDirection) { // Горизонтальное движение (для внутренности необходимо вычесть 1) arn.c2 = this.activeFillHandle.c2 - 1; changedRange.c1 = changedRange.c2; changedRange.c2 = this.activeFillHandle.c2; } else { // Вертикальное движение (для внутренности необходимо вычесть 1) arn.r2 = this.activeFillHandle.r2 - 1; changedRange.r1 = changedRange.r2; changedRange.r2 = this.activeFillHandle.r2; } } } else { // Мы вне выделения. Увеличиваем диапазон if (0 === this.fillHandleDirection) { // Горизонтальное движение if (1 === this.fillHandleArea) { arn.c1 = this.activeFillHandle.c2; changedRange.c2 = changedRange.c1 - 1; changedRange.c1 = this.activeFillHandle.c2; } else { arn.c2 = this.activeFillHandle.c2; changedRange.c1 = changedRange.c2 + 1; changedRange.c2 = this.activeFillHandle.c2; } } else { // Вертикальное движение if (1 === this.fillHandleArea) { arn.r1 = this.activeFillHandle.r2; changedRange.r2 = changedRange.r1 - 1; changedRange.r1 = this.activeFillHandle.r2; } else { arn.r2 = this.activeFillHandle.r2; changedRange.r1 = changedRange.r2 + 1; changedRange.r2 = this.activeFillHandle.r2; } } } changedRange.normalize(); var applyFillHandleCallback = function (res) { if (res) { // Автозаполняем ячейки var oCanPromote = range.canPromote(/*bCtrl*/ctrlPress, /*bVertical*/(1 === t.fillHandleDirection), nIndex); if (null != oCanPromote) { History.Create_NewPoint(); History.StartTransaction(); range.promote(/*bCtrl*/ctrlPress, /*bVertical*/(1 === t.fillHandleDirection), nIndex, oCanPromote); // Вызываем функцию пересчета для заголовков форматированной таблицы t.model.autoFilters.renameTableColumn(arn); // Сбрасываем параметры автозаполнения t.activeFillHandle = null; t.fillHandleDirection = -1; // Обновляем выделенные ячейки t.isChanged = true; t._updateCellsRange(arn); History.SetSelection(range.bbox.clone()); History.SetSelectionRedo(oCanPromote.to.clone()); History.EndTransaction(); } else { t.handlers.trigger("onErrorEvent", c_oAscError.ID.CannotFillRange, c_oAscError.Level.NoCritical); t.model.selectionRange.assign2(range.bbox); // Сбрасываем параметры автозаполнения t.activeFillHandle = null; t.fillHandleDirection = -1; t.updateSelection(); } } }; // Можно ли применять автозаполнение ? 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) { // Возвращаемый результат var ret = null; // Пересчитываем координаты x *= asc_getcvt(0/*px*/, 1/*pt*/, this._getPPIX()); y *= asc_getcvt(0/*px*/, 1/*pt*/, this._getPPIY()); //если выделена ячейка заголовка ф/т, меняем выделение с ячейки на столбец ф/т //если выделена вся видимая часть форматированной таблицы, но не выделены последние скрытые строчки var selectionRange = this.model.selectionRange.getLast().clone(); if (null === this.startCellMoveRange) { this.af_changeSelectionTablePart(selectionRange); } // Колонка по X и строка по Y var colByX = this._findColUnderCursor(x, /*canReturnNull*/false, /*dX*/false).col; var rowByY = this._findRowUnderCursor(y, /*canReturnNull*/false, /*dY*/false).row; if (selectionRange.type == c_oAscSelectionType.RangeRow) { colByX = 0; } if (selectionRange.type == c_oAscSelectionType.RangeCol) { rowByY = 0; } if (selectionRange.type == c_oAscSelectionType.RangeMax) { colByX = 0; rowByY = 0; } // Если мы только первый раз попали сюда, то копируем выделенную область if (null === this.startCellMoveRange) { // Учитываем погрешность (мы должны быть внутри диапазона при старте) if (colByX < selectionRange.c1) { colByX = selectionRange.c1; } else if (colByX > selectionRange.c2) { colByX = selectionRange.c2; } if (rowByY < selectionRange.r1) { rowByY = selectionRange.r1; } else if (rowByY > selectionRange.r2) { rowByY = selectionRange.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 = selectionRange; // Для первого раза нормализуем (т.е. первая точка - это левый верхний угол) 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; // Увеличиваем, если выходим за область видимости // 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] : this.arrActiveChartRanges[indexFormulaRange]).getLast().clone(); // Колонка по 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].getLast().assign2(ar.clone(true)); this.arrActiveFormulaRanges[indexFormulaRange].cursorePos = _p; this.arrActiveFormulaRanges[indexFormulaRange].formulaRangeLength = _l; newFormulaRange = this.arrActiveFormulaRanges[indexFormulaRange].getLast(); } else { this.arrActiveChartRanges[indexFormulaRange].getLast().assign2(ar.clone(true)); this.moveRangeDrawingObjectTo = ar.clone(); } 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.model.selectionRange.getLast(); 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) { var t = this; this.model.workbook.handlers.trigger("asc_onConfirmAction", Asc.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.model.workbook.handlers.trigger("asc_onError", c_oAscError.ID.LockedAllError, c_oAscError.Level.NoCritical); t._cleanSelectionMoveRange(); return; } var onApplyMoveAutoFiltersCallback = function (isSuccess) { if (false === isSuccess) { t.model.workbook.handlers.trigger("asc_onError", c_oAscError.ID.LockedAllError, c_oAscError.Level.NoCritical); t._cleanSelectionMoveRange(); return; } // Очищаем выделение t.cleanSelection(); //ToDo t.cleanDepCells(); History.Create_NewPoint(); History.SetSelection(arnFrom.clone()); History.SetSelectionRedo(arnTo.clone()); History.StartTransaction(); t.model.autoFilters._preMoveAutoFilters(arnFrom, arnTo, copyRange); t.model._moveRange(arnFrom, arnTo, copyRange); t.cellCommentator.moveRangeComments(arnFrom, arnTo, copyRange); t.objectRender.moveRangeDrawingObject(arnFrom, arnTo); // Вызываем функцию пересчета для заголовков форматированной таблицы t.model.autoFilters.renameTableColumn(arnFrom); t.model.autoFilters.renameTableColumn(arnTo); t.model.autoFilters.reDrawFilter(arnFrom); t.model.autoFilters.afterMoveAutoFilters(arnFrom, arnTo); t._updateCellsRange(arnTo, false, true); t.model.selectionRange.assign2(arnTo); // Сбрасываем параметры t.activeMoveRange = null; t.startCellMoveRange = null; t._updateCellsRange(arnFrom, false, true); // Тут будет отрисовка select-а t._recalculateAfterUpdate([arnFrom, arnTo]); // Вызовем на всякий случай, т.к. мы можем уже обновиться из-за формул ToDo возможно стоит убрать это в дальнейшем (но нужна переработка формул) - http://bugzilla.onlyoffice.com/show_bug.cgi?id=24505 t._updateSelectionNameAndInfo(); if (null !== t.model.getRange3(arnTo.r1, arnTo.c1, arnTo.r2, arnTo.c2).hasMerged() && false !== t.model.autoFilters._intersectionRangeWithTableParts(arnTo)) { t.model.autoFilters.unmergeTablesAfterMove(arnTo); t._updateCellsRange(arnTo, false, true); t._recalculateAfterUpdate([arnFrom, arnTo]); //не делаем действий в asc_onConfirmAction, потому что во время диалога может выполниться autosave и новые измения добавятся в точку, которую уже отправили //тем более результат диалога ни на что не влияет t.model.workbook.handlers.trigger("asc_onConfirmAction", Asc.c_oAscConfirm.ConfirmPutMergeRange, function () { }); } History.EndTransaction(); }; if (t.model.autoFilters._searchFiltersInRange(arnFrom, true)) { t._isLockedAll(onApplyMoveAutoFiltersCallback); if(copyRange){ t._isLockedDefNames(null, null); } } else { onApplyMoveAutoFiltersCallback(); } }; if (this.af_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, fromBinary, sortColor) { // Проверка глобального лока if (this.collaborativeEditing.getGlobalLock()) { return; } var t = this; var checkRange = []; var activeCell = this.model.selectionRange.activeCell.clone(); var arn = this.model.selectionRange.getLast().clone(true); if (onlyActive) { checkRange.push(new asc_Range(activeCell.col, activeCell.row, activeCell.col, activeCell.row)); } else { this.model.selectionRange.ranges.forEach(function (item) { checkRange.push(item.getAllRange()); }); } var onSelectionCallback = function (isSuccess) { if (false === isSuccess) { return; } var bIsUpdate = true; var oUpdateRanges = {}, hasUpdates = false; var callTrigger = false; var res; var mc, r, c, cell; function makeBorder(b) { var border = new AscCommonExcel.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.asc_CColor) { border.c = AscCommonExcel.CorrectAscColor(b.color); } } } return border; } History.Create_NewPoint(); History.StartTransaction(); checkRange.forEach(function (item, i) { var range = t.model.getRange3(item.r1, item.c1, item.r2, item.c2); var isLargeRange = t._isLargeRange(range.bbox); var canChangeColWidth = c_oAscCanChangeColWidth.none; 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) { callTrigger = true; t.handlers.trigger("slowOperation", true); } // None if (val.length < 1) { range.setBorder(null); break; } res = new AscCommonExcel.Border(); // Diagonal res.d = makeBorder(val[c_oAscBorderOptions.DiagD] || val[c_oAscBorderOptions.DiagU]); res.dd = !!val[c_oAscBorderOptions.DiagD]; res.du = !!val[c_oAscBorderOptions.DiagU]; // 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) { 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) { callTrigger = true; t.handlers.trigger("slowOperation", true); } t.cellCommentator.sortComments(range.sort(val, activeCell.col, sortColor, true)); break; case "empty": if (isLargeRange && !callTrigger) { callTrigger = true; t.handlers.trigger("slowOperation", true); } /* отключаем отрисовку на случай необходимости пересчета ячеек, заносим ячейку, при необходимости в список перерисовываемых */ t.model.workbook.dependencyFormulas.lockRecal(); // Если нужно удалить автофильтры - удаляем if (val === c_oAscCleanOptions.All || val === c_oAscCleanOptions.Text) { t.model.autoFilters.isEmptyAutoFilters(arn); } else if (val === c_oAscCleanOptions.Format) { t.model.autoFilters.cleanFormat(arn); } switch(val) { case c_oAscCleanOptions.All: range.cleanAll(); t.model.removeSparklines(arn); // Удаляем комментарии t.cellCommentator.deleteCommentsRange(arn); break; case c_oAscCleanOptions.Text: case c_oAscCleanOptions.Formula: range.cleanText(); break; case c_oAscCleanOptions.Format: range.cleanFormat(); break; case c_oAscCleanOptions.Comments: t.cellCommentator.deleteCommentsRange(arn); break; case c_oAscCleanOptions.Hyperlinks: range.cleanHyperlinks(); break; case c_oAscCleanOptions.Sparklines: t.model.removeSparklines(arn); break; case c_oAscCleanOptions.SparklineGroups: t.model.removeSparklineGroups(arn); break; } // Вызываем функцию пересчета для заголовков форматированной таблицы if (val === c_oAscCleanOptions.All || val === c_oAscCleanOptions.Text) { t.model.autoFilters.renameTableColumn(arn); } /* возвращаем отрисовку. и перерисовываем ячейки с предварительным пересчетом */ t.model.workbook.dependencyFormulas.unlockRecal(); 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(activeCell.row, activeCell.col); c = mc ? mc.c1 : activeCell.col; r = mc ? mc.r1 : activeCell.row; cell = t._getVisibleCell(c, r); var oldFontSize = cell.getFont().getSize(); 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": var _clipboard = window["Asc"]["editor"].wb.clipboard; _clipboard.specialPasteProps = _clipboard.specialPasteProps ? _clipboard.specialPasteProps : new Asc.SpecialPasteProps(); t._loadDataBeforePaste(isLargeRange, fromBinary, val, bIsUpdate, canChangeColWidth); bIsUpdate = false; break; case "hyperlink": if (val && val.hyperlinkModel) { if (Asc.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(activeCell.row, activeCell.col); c = mc ? mc.c1 : activeCell.col; r = mc ? mc.r1 : activeCell.row; if (null !== val.asc_getText()) { t.model.getRange3(r, c, r, c).setValue(val.asc_getText()); // Вызываем функцию пересчета для заголовков форматированной таблицы t.model.autoFilters.renameTableColumn(arn); } break; } else { bIsUpdate = false; break; } default: bIsUpdate = false; break; } if (bIsUpdate) { hasUpdates = true; oUpdateRanges[i] = item; oUpdateRanges[i].canChangeColWidth = canChangeColWidth; bIsUpdate = false; } }); if (hasUpdates) { t.updateRanges(oUpdateRanges, false, true); } if (callTrigger) { t.handlers.trigger("slowOperation", false); } //в случае, если вставляем из глобального буфера, транзакцию закрываем внутри функции _loadDataBeforePaste на callbacks от загрузки шрифтов и картинок if (prop !== "paste" || (prop === "paste" && fromBinary)) { History.EndTransaction(); window["Asc"]["editor"].wb.clipboard.end_paste(); } }; //получаем диапазон вставки if ("paste" === prop && val.onlyImages !== true) { var newRange; if (fromBinary) { newRange = this._pasteFromBinary(val, true); } else { newRange = this._pasteFromHTML(val, true); } if(!window["Asc"]["editor"].wb.clipboard.specialPasteProps) { var sBinary = window["Asc"]["editor"].wb.clipboard.copyProcessor.getBinaryForCopy(this, newRange); var specialPasteSelectionRange = new AscCommonExcel.SelectionRange(); specialPasteSelectionRange.ranges[0] = newRange.clone(); window['AscCommon'].g_clipboardBase.specialPasteData.activeRange = specialPasteSelectionRange; window['AscCommon'].g_clipboardBase.specialPasteUndoData.data = sBinary; } checkRange = [newRange]; } if ("paste" === prop && val.onlyImages === true) { onSelectionCallback(); } else { this._isLockedCells(checkRange, /*subType*/null, onSelectionCallback); } }; WorksheetView.prototype.specialPaste = function (preSpecialPasteData, specialPasteData, props) { var api = window["Asc"]["editor"]; var t = this; if(!specialPasteData) { return; } //не вызываю для отката api.wb.clipboard.pasteData, потому что внутри асинхронные методы - isLockedCells var undoPreviousPaste = function() { //откатываем данные в ячейках var sBase64 = preSpecialPasteData.data.split('xslData;')[1]; var oBinaryFileReader = new AscCommonExcel.BinaryFileReader(true); var tempWorkbook = new AscCommonExcel.Workbook(); pptx_content_loader.Start_UseFullUrl(); oBinaryFileReader.Read(sBase64, tempWorkbook); window["Asc"]["editor"].wb.clipboard.pasteProcessor.activeRange = oBinaryFileReader.copyPasteObj.activeRange; var pasteData = null; if (tempWorkbook) pasteData = tempWorkbook.aWorksheets[0]; if(pasteData) { t._pasteFromBinary(pasteData, null, null, api.wb.clipboard.specialPasteProps); } //удаляем вставленные изображения if(preSpecialPasteData.images) { var images = preSpecialPasteData.images; for(var i = 0; i < images.length; i++) { var id = images[i]; var oObject = AscCommon.g_oTableId.Get_ById(id); oObject.deleteDrawingBase(true); oObject.setBDeleted(true); } } }; //транзакция закроется в end_paste History.Create_NewPoint(); History.StartTransaction(); window["Asc"]["editor"].wb.clipboard.start_specialpaste(); window["Asc"]["editor"].wb.clipboard.start_paste(); //откатываемся до того, что было до вставки if(preSpecialPasteData && preSpecialPasteData.data) { var tempProps = new Asc.SpecialPasteProps(); api.wb.clipboard.specialPasteProps = tempProps; //меняем activeRange if(specialPasteData.activeRange) { this.model.selectionRange = specialPasteData.activeRange.clone(this.model); } undoPreviousPaste(); } //далее специальная вставка api.wb.clipboard.specialPasteProps = props; //TODO пока для закрытия транзации выставляю флаг. пересмотреть! window["Asc"]["editor"].wb.clipboard.bIsEndTransaction = true; api.wb.clipboard.pasteData(this, specialPasteData._format, specialPasteData.data1, specialPasteData.data2, specialPasteData.text_data, true); }; WorksheetView.prototype._pasteData = function (isLargeRange, fromBinary, val, bIsUpdate, canChangeColWidth) { var t = this; var _clipboard = window["Asc"]["editor"].wb.clipboard; var specialPasteProps = _clipboard.specialPasteProps; if ( val.props && val.props.onlyImages === true ) { if(!window["Asc"]["editor"].wb.clipboard.specialPasteStart) { this.handlers.trigger("showSpecialPasteOptions", [Asc.c_oSpecialPasteProps.picture]); } return; } var callTrigger = false; if (isLargeRange) { callTrigger = true; t.handlers.trigger("slowOperation", true); } //добавляем форматированные таблицы var arnToRange = t.model.selectionRange.getLast(); var tablesMap = null; if (fromBinary && val.TableParts && val.TableParts.length) { var range, tablePartRange, tables = val.TableParts, diffRow, diffCol, curTable, bIsAddTable; var activeRange = window["Asc"]["editor"].wb.clipboard.pasteProcessor.activeRange; var refInsertBinary = AscCommonExcel.g_oRangeCache.getAscRange(activeRange); for (var i = 0; i < tables.length; i++) { curTable = tables[i]; tablePartRange = curTable.Ref; diffRow = tablePartRange.r1 - refInsertBinary.r1 + arnToRange.r1; diffCol = tablePartRange.c1 - refInsertBinary.c1 + arnToRange.c1; range = t.model.getRange3(diffRow, diffCol, diffRow + (tablePartRange.r2 - tablePartRange.r1), diffCol + (tablePartRange.c2 - tablePartRange.c1)); //если область вставки содержит форматированную таблицу, которая пересекается с вставляемой форматированной таблицей var intersectionRangeWithTableParts = t.model.autoFilters._intersectionRangeWithTableParts(range.bbox); if (intersectionRangeWithTableParts) { continue; } if (curTable.style) { range.cleanFormat(); } //TODO использовать bWithoutFilter из tablePart var bWithoutFilter = false; if (!curTable.AutoFilter) { bWithoutFilter = true; } var offset = { offsetCol: range.bbox.c1 - tablePartRange.c1, offsetRow: range.bbox.r1 - tablePartRange.r1 }; var newDisplayName = this.model.workbook.dependencyFormulas.getNextTableName(); var props = { bWithoutFilter: bWithoutFilter, tablePart: curTable, offset: offset, displayName: newDisplayName }; t.model.autoFilters.addAutoFilter(curTable.TableStyleInfo.Name, range.bbox, true, true, props); if (null === tablesMap) { tablesMap = {}; } tablesMap[curTable.DisplayName] = newDisplayName; } if(bIsAddTable) { t._isLockedDefNames(null, null); } } //делаем unmerge ф/т var intersectionRangeWithTableParts = t.model.autoFilters._intersectionRangeWithTableParts(arnToRange); 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(); } } t.model.workbook.dependencyFormulas.lockRecal(); var selectData; if (fromBinary) { selectData = t._pasteFromBinary(val, null, tablesMap, specialPasteProps); } else { selectData = t._pasteFromHTML(val, null, specialPasteProps); } t.model.autoFilters.renameTableColumn(t.model.selectionRange.getLast()); if (!selectData) { bIsUpdate = false; t.model.workbook.dependencyFormulas.unlockRecal(); if (callTrigger) { t.handlers.trigger("slowOperation", false); } return; } this.expandColsOnScroll(false, true); this.expandRowsOnScroll(false, true); 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); } } t.model.workbook.dependencyFormulas.unlockRecal(); var arn = selectData[0]; var selectionRange = arn.clone(true); 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); } //for special paste if(!window["Asc"]["editor"].wb.clipboard.specialPasteStart) { //var specialPasteShowOptions = new Asc.SpecialPasteShowOptions(); var allowedSpecialPasteProps; var sProps = Asc.c_oSpecialPasteProps; if(fromBinary) { allowedSpecialPasteProps = [sProps.paste, sProps.pasteOnlyFormula, sProps.formulaNumberFormat, sProps.formulaAllFormatting, sProps.formulaWithoutBorders, sProps.formulaColumnWidth, sProps.pasteOnlyValues, sProps.valueNumberFormat, sProps.valueAllFormating, sProps.pasteOnlyFormating/*, sProps.link*/]; } else { //matchDestinationFormatting - пока не добавляю, так как работает как и values allowedSpecialPasteProps = [sProps.sourceformatting, sProps.destinationFormatting]; } _clipboard.showSpecialPasteOptions(this, allowedSpecialPasteProps, selectData[0]); } }; WorksheetView.prototype._loadDataBeforePaste = function ( isLargeRange, fromBinary, pasteContent, bIsUpdate, canChangeColWidth ) { var t = this; var _clipboard = window["Asc"]["editor"].wb.clipboard; var specialPasteProps = _clipboard.specialPasteProps; //paste from excel binary if(fromBinary) { t._pasteData(isLargeRange, fromBinary, pasteContent, bIsUpdate, canChangeColWidth); } else { var callbackLoadFonts = function() { var api = asc["editor"]; var isEndTransaction = false; var imagesFromWord = pasteContent.props.addImagesFromWord; if ( imagesFromWord && imagesFromWord.length != 0 && !(window["Asc"]["editor"] && window["Asc"]["editor"].isChartEditor) && specialPasteProps.images) { var oObjectsForDownload = AscCommon.GetObjectsForImageDownload( pasteContent.props._aPastedImages ); //if already load images on server if ( api.wb.clipboard.pasteProcessor.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.pasteProcessor.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; } } t._pasteData( isLargeRange, fromBinary, pasteContent, bIsUpdate, canChangeColWidth ); api.wb.clipboard.pasteProcessor._insertImagesFromBinaryWord( t, pasteContent, oImageMap ); isEndTransaction = true; } else { if(window["NATIVE_EDITOR_ENJINE"]) { var oImageMap = {}; AscCommon.ResetNewUrls( data, oObjectsForDownload.aUrls, oObjectsForDownload.aBuilderImagesByUrl, oImageMap ); t._pasteData( isLargeRange, fromBinary, pasteContent, bIsUpdate, canChangeColWidth ); api.wb.clipboard.pasteProcessor._insertImagesFromBinaryWord( t, pasteContent, oImageMap ); isEndTransaction = true; } else { AscCommon.sendImgUrls( api, oObjectsForDownload.aUrls, function ( data ) { var oImageMap = {}; AscCommon.ResetNewUrls( data, oObjectsForDownload.aUrls, oObjectsForDownload.aBuilderImagesByUrl, oImageMap ); t._pasteData( isLargeRange, fromBinary, pasteContent, bIsUpdate, canChangeColWidth ); api.wb.clipboard.pasteProcessor._insertImagesFromBinaryWord( t, pasteContent, oImageMap ); //закрываем транзакцию, поскольку в setSelectionInfo она не закроется History.EndTransaction(); window["Asc"]["editor"].wb.clipboard.end_paste(); }, true ); } } } else { t._pasteData( isLargeRange, fromBinary, pasteContent, bIsUpdate, canChangeColWidth ); isEndTransaction = true; } //закрываем транзакцию, поскольку в setSelectionInfo она не закроется if ( isEndTransaction ) { History.EndTransaction(); window["Asc"]["editor"].wb.clipboard.end_paste(); } }; //загрузка шрифтов, в случае удачи на callback вставляем текст t._loadFonts( pasteContent.props.fontsNew, callbackLoadFonts); } }; WorksheetView.prototype._pasteFromHTML = function (pasteContent, isCheckSelection, specialPasteProps) { var t = this; var wb = window["Asc"]["editor"].wb; var lastSelection = this.model.selectionRange.getLast(); var arn = wb && wb.clipboard && wb.clipboard.pasteProcessor && wb.clipboard.pasteProcessor.activeRange ? wb.clipboard.pasteProcessor.activeRange : lastSelection; var arrFormula = []; var numFor = 0; var rMax = pasteContent.content.length + pasteContent.props.rowSpanSpCount + arn.r1; var cMax = pasteContent.props.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; //если вставляем в мерженную ячейку, диапазон которой больше или равен var fPasteCell = pasteContent.content[0][0]; if (arn.c2 >= cMax - 1 && arn.r2 >= rMax - 1 && isMergedFirstCell && isMergedFirstCell.isEqual(arn) && cMax - arn.c1 === fPasteCell.colSpan && rMax - arn.r1 === fPasteCell.rowSpan) { if (!isCheckSelection) { pasteContent.content[0][0].colSpan = isMergedFirstCell.c2 - isMergedFirstCell.c1 + 1; pasteContent.content[0][0].rowSpan = isMergedFirstCell.r2 - isMergedFirstCell.r1 + 1; } isOneMerge = true; } else { //проверка на наличие части объединённой ячейки в области куда осуществляем вставку for (var rFirst = arn.r1; rFirst < rMax; ++rFirst) { for (var cFirst = arn.c1; cFirst < cMax; ++cFirst) { var 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 = pasteContent.content.length; if (isCheckSelection) { var newArr = arn.clone(true); newArr.r2 = rMax2 - 1; newArr.c2 = cMax2 - 1; if (isMultiple || isOneMerge) { newArr.r2 = lastSelection.r2; newArr.c2 = lastSelection.c2; } return newArr; } //если не возникает конфликт, делаем unmerge rangeUnMerge.unmerge(); if (!isOneMerge) { arn.r2 = (rMax2 - 1 > 0) ? (rMax2 - 1) : 0; arn.c2 = (cMax2 - 1 > 0) ? (cMax2 - 1) : 0; } if (isMultiple)//случай автозаполнения сложных форм { t.model.getRange3(lastSelection.r1, lastSelection.c1, lastSelection.r2, lastSelection.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 mergeArr = []; var putInsertedCellIntoRange = function(row, col, currentObj) { var pastedRangeProps = {}; var contentCurrentObj = currentObj.content; var range = t.model.getRange3(row, col, row, col); //value if (contentCurrentObj.length === 1) { var onlyOneChild = contentCurrentObj[0]; var valFormat = onlyOneChild.text; pastedRangeProps.val = valFormat; pastedRangeProps.font = onlyOneChild.format; } else { pastedRangeProps.value2 = contentCurrentObj; pastedRangeProps.alignVertical = currentObj.va; pastedRangeProps.val = currentObj.textVal; } if (contentCurrentObj.length === 1 && contentCurrentObj[0].format) { var fs = contentCurrentObj[0].format.getSize(); if (fs !== '' && fs !== null && fs !== undefined) { pastedRangeProps.fontSize = fs; } } //AlignHorizontal if (!isOneMerge) { pastedRangeProps.alignHorizontal = currentObj.a; } //for merge var isMerged = false; for (var mergeCheck = 0; mergeCheck < mergeArr.length; ++mergeCheck) { if (mergeArr[mergeCheck].contains(col, row)) { isMerged = true; } } if ((currentObj.colSpan > 1 || currentObj.rowSpan > 1) && !isMerged) { var offsetCol = currentObj.colSpan - 1; var offsetRow = currentObj.rowSpan - 1; pastedRangeProps.offsetLast = {offsetCol: offsetCol, offsetRow: offsetRow}; mergeArr.push(new Asc.Range(range.bbox.c1, range.bbox.r1, range.bbox.c2 + offsetCol, range.bbox.r2 + offsetRow)); if (contentCurrentObj[0] == undefined) { pastedRangeProps.val = ''; } pastedRangeProps.merge = c_oAscMergeOptions.Merge; } //borders if (!isOneMerge) { pastedRangeProps.borders = currentObj.borders; } //wrap pastedRangeProps.wrap = currentObj.wrap; //fill if (currentObj.bc && currentObj.bc.rgb) { pastedRangeProps.fill = currentObj.bc; } //hyperlink var link = currentObj.hyperLink; if (link) { pastedRangeProps.hyperLink = currentObj; } //apply props by cell t._setPastedDataByCurrentRange(range, pastedRangeProps, null, specialPasteProps); }; for (var autoR = 0; autoR < maxARow; ++autoR) { for (var autoC = 0; autoC < maxACol; ++autoC) { for (var r = 0; r < rMax; ++r) { for (var c = 0; c < pasteContent.content[r].length; ++c) { if (undefined !== pasteContent.content[r][c]) { var pasteIntoRow = r + autoR * plRow + arn.r1; var pasteIntoCol = c + autoC * plCol + arn.c1; var currentObj = pasteContent.content[r][c]; putInsertedCellIntoRange(pasteIntoRow, pasteIntoCol, currentObj); } } } } } if (isMultiple) { arn.r2 = lastSelection.r2; arn.c2 = lastSelection.c2; } t.isChanged = true; lastSelection.c2 = arn.c2; lastSelection.r2 = arn.r2; var arnFor = [arn, arrFormula]; var _clipboard = window["Asc"]["editor"].wb.clipboard; _clipboard.specialPasteProps = null; return arnFor; }; WorksheetView.prototype._pasteFromBinary = function (val, isCheckSelection, tablesMap, specialPasteProps) { var t = this; var arn = t.model.selectionRange.getLast(); var arrFormula = []; var pasteRange = window["Asc"]["editor"].wb.clipboard.pasteProcessor.activeRange; var activeCellsPasteFragment = typeof pasteRange === "string" ? AscCommonExcel.g_oRangeCache.getAscRange(pasteRange) : 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) { var 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; if (isCheckSelection) { var newArr = arn.clone(true); newArr.r2 = rMax2 - 1; newArr.c2 = cMax2 - 1; if (isMultiple || isOneMerge) { newArr.r2 = arn.r2; newArr.c2 = arn.c2; } return newArr; } //если не возникает конфликт, делаем unmerge rangeUnMerge.unmerge(); if (!isOneMerge) { arn.r2 = rMax2 - 1; arn.c2 = cMax2 - 1; } if (isMultiple)//случай автозаполнения сложных форм { t.model.getRange3(arn.r1, arn.c1, arn.r2, arn.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 addComments = function(pasteRow, pasteCol, comments) { var comment; for (var i = 0; i < comments.length; i++) { comment = comments[i]; if (comment.nCol == pasteCol && comment.nRow == pasteRow) { var commentData = new Asc.asc_CCommentData(comment); //change nRow, nCol commentData.asc_putCol(nCol); commentData.asc_putRow(nRow); t.cellCommentator.addComment(commentData, true); } } }; var mergeArr = []; var checkMerge = function(range, curMerge, nRow, nCol, rowDiff, colDiff, pastedRangeProps) { var isMerged = false; for (var mergeCheck = 0; mergeCheck < mergeArr.length; ++mergeCheck) { if (mergeArr[mergeCheck].contains(nCol, nRow)) { isMerged = true; } } if (!isOneMerge) { if (curMerge != null && !isMerged) { var offsetCol = curMerge.c2 - curMerge.c1; if (offsetCol + nCol >= gc_nMaxCol0) { offsetCol = gc_nMaxCol0 - nCol; } var offsetRow = curMerge.r2 - curMerge.r1; if (offsetRow + nRow >= gc_nMaxRow0) { offsetRow = gc_nMaxRow0 - nRow; } pastedRangeProps.offsetLast = {offsetCol: offsetCol, offsetRow: offsetRow}; mergeArr.push(new Asc.Range( curMerge.c1 + arn.c1 - activeCellsPasteFragment.c1 + colDiff, curMerge.r1 + arn.r1 - activeCellsPasteFragment.r1 + rowDiff, curMerge.c2 + arn.c1 - activeCellsPasteFragment.c1 + colDiff, curMerge.r2 + arn.r1 - activeCellsPasteFragment.r1 + rowDiff )); } } else { if (!isMerged) { pastedRangeProps.offsetLast = {offsetCol: isMergedFirstCell.c2 - isMergedFirstCell.c1, offsetRow: isMergedFirstCell.r2 - isMergedFirstCell.r1}; mergeArr.push({r1: isMergedFirstCell.r1, r2: isMergedFirstCell.r2, c1: isMergedFirstCell.c1, c2: isMergedFirstCell.c2}); } } }; var putInsertedCellIntoRange = function(nRow, nCol, pasteRow, pasteCol, rowDiff, colDiff, range, newVal) { var pastedRangeProps = {}; //range может далее изменится в связи с наличием мерженных ячеек, firstRange - не меняется(ему делаем setValue, как первой ячейке в диапазоне мерженных) var firstRange = range.clone(); //****paste comments**** if (val.aComments && val.aComments.length) { addComments(pasteRow, pasteCol, val.aComments); } //merge checkMerge(range, curMerge, nRow, nCol, rowDiff, colDiff, pastedRangeProps); //set style if (!isOneMerge) { pastedRangeProps.cellStyle = newVal.getStyleName(); } if (!isOneMerge)//settings for cell(format) { //format var numFormat = newVal.getNumFormat(); var nameFormat; if (numFormat && numFormat.sFormat) { nameFormat = numFormat.sFormat; } pastedRangeProps.numFormat = nameFormat; } if (!isOneMerge)//settings for cell { //vertical align pastedRangeProps.alignVertical = newVal.getAlignVertical(); //horizontal align pastedRangeProps.alignHorizontal = newVal.getAlignHorizontal(); //borders var fullBorders = newVal.getBorderFull(); if (pastedRangeProps.offsetLast && pastedRangeProps.offsetLast.offsetCol > 0 && curMerge && fullBorders) { //для мерженных ячеек, правая границу var endMergeCell = val.getCell3(pasteRow, curMerge.c2); var fullBordersEndMergeCell = endMergeCell.getBorderFull(); if (fullBordersEndMergeCell && fullBordersEndMergeCell.r) { fullBorders.r = fullBordersEndMergeCell.r; } } pastedRangeProps.bordersFull = fullBorders; //fill pastedRangeProps.fill = newVal.getFill(); //wrap //range.setWrap(newVal.getWrap()); pastedRangeProps.wrap = newVal.getWrap(); //angle pastedRangeProps.angle = newVal.getAngle(); //hyperlink pastedRangeProps.hyperlinkObj = newVal.getHyperlink(); } //apply props by cell var formulaProps = {firstRange: firstRange, arrFormula: arrFormula, tablesMap: tablesMap, newVal: newVal, isOneMerge: isOneMerge, val: val}; t._setPastedDataByCurrentRange(range, pastedRangeProps, formulaProps, specialPasteProps); }; 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; var newVal = val.getCell3(pasteRow, pasteCol); var curMerge = newVal.hasMerged(); if (undefined !== newVal) { var nRow = r + autoR * plRow; if (nRow > gc_nMaxRow0) { nRow = gc_nMaxRow0; } var nCol = c + autoC * plCol; if (nCol > gc_nMaxCol0) { nCol = gc_nMaxCol0; } var range = t.model.getRange3(nRow, nCol, nRow, nCol); putInsertedCellIntoRange(nRow, nCol, pasteRow, pasteCol, autoR * plRow, autoC * plCol, range, newVal); //если замержили range c = range.bbox.c2 - autoC * plCol; if (c === cMax) { r = range.bbox.r2 - autoC * plCol; } } } } } } t.isChanged = true; var arnFor = [arn, arrFormula]; var _clipboard = window["Asc"]["editor"].wb.clipboard; _clipboard.specialPasteProps = null; return arnFor; }; WorksheetView.prototype._setPastedDataByCurrentRange = function(range, rangeStyle, formulaProps, specialPasteProps) { var t = this; var firstRange, arrFormula, tablesMap, newVal, isOneMerge, val; if(formulaProps) { //TODO firstRange возможно стоит убрать(добавлено было для правки бага 27745) firstRange = formulaProps.firstRange; arrFormula = formulaProps.arrFormula; tablesMap = formulaProps.tablesMap; newVal = formulaProps.newVal; isOneMerge = formulaProps.isOneMerge; val = formulaProps.val; } //set formula - for paste from binary var calculateValueAndBinaryFormula = function(newVal, firstRange, range) { var numFormula = null; var skipFormat = null; var noSkipVal = null; rangeStyle.val = newVal.getValue(); 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.getSkip()) { skipFormat = true; } else if (value2[nF] && value2[nF].format && !value2[nF].format.getSkip()) { noSkipVal = nF; } } //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); var assemb, _p_ = new AscCommonExcel.parserFormula(value2[numFormula].sFormula, null, val); if (_p_.parse()) { if(null !== tablesMap) { var renameParams = {}; renameParams.offset = offset; renameParams.tableNameMap = tablesMap; _p_.renameSheetCopy(renameParams); assemb = _p_.assemble(true) } else { assemb = _p_.changeOffset(offset).assemble(true); } rangeStyle.formula = {range: range, val: "=" + assemb}; //arrFormula.push({range: range, val: "=" + assemb}); } } else { cellFrom = newVal.getCells(); if (isOneMerge && range && range.bbox) { cellTo = t._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()); rangeStyle.cellValueData = {valueData: cellFrom[0].getValueData(), cell: cellTo[0]}; } } if (!isOneMerge)//settings for text { rangeStyle.font = value2[numStyle].format; //range.setFont(value2[numStyle].format); } } else { rangeStyle.value2 = value2; //firstRange.setValue2(value2); } }; //for formula if(formulaProps) { calculateValueAndBinaryFormula(newVal, firstRange, range); } //cellStyle if(rangeStyle.cellStyle && specialPasteProps.cellStyle) { range.setCellStyle(rangeStyle.cellStyle); } //numFormat if(rangeStyle.numFormat && specialPasteProps.numFormat) { range.setNumFormat(rangeStyle.numFormat); } //font if(rangeStyle.font && specialPasteProps.font) { range.setFont(rangeStyle.font); } //***value*** if(rangeStyle.formula && specialPasteProps.formula) { arrFormula.push(rangeStyle.formula); } else if(rangeStyle.cellValueData && specialPasteProps.font && specialPasteProps.val) { rangeStyle.cellValueData.cell.setValueData(rangeStyle.cellValueData.valueData); } else if(rangeStyle.value2 && specialPasteProps.font && specialPasteProps.val) { if(formulaProps) { firstRange.setValue2(rangeStyle.value2); } else { range.setValue2(rangeStyle.value2); } } else if(rangeStyle.val && specialPasteProps.val) { range.setValue(rangeStyle.val); } //alignVertical if(rangeStyle.alignVertical && specialPasteProps.alignVertical) { range.setAlignVertical(rangeStyle.alignVertical); } //alignHorizontal if(rangeStyle.alignHorizontal && specialPasteProps.alignHorizontal) { range.setAlignHorizontal(rangeStyle.alignHorizontal); } //fontSize if(rangeStyle.fontSize && specialPasteProps.fontSize) { range.setFontsize(rangeStyle.fontSize); } //offsetLast if(rangeStyle.offsetLast && specialPasteProps.merge) { range.setOffsetLast(rangeStyle.offsetLast); range.merge(rangeStyle.merge); } //borders if(rangeStyle.borders && specialPasteProps.borders) { range.setBorderSrc(rangeStyle.borders); } //bordersFull if(rangeStyle.bordersFull && specialPasteProps.borders) { range.setBorder(rangeStyle.bordersFull); } //wrap if(rangeStyle.wrap && specialPasteProps.wrap) { range.setWrap(rangeStyle.wrap); } //fill if(specialPasteProps.fill) { range.setFill(rangeStyle.fill); } //angle if(rangeStyle.angle && specialPasteProps.angle) { range.setAngle(rangeStyle.angle); } //hyperLink if (rangeStyle.hyperLink && specialPasteProps.hyperlink) { var _link = rangeStyle.hyperLink.hyperLink; var newHyperlink = new AscCommonExcel.Hyperlink(); if (_link.search('#') === 0) { newHyperlink.setLocation(_link.replace('#', '')); } else { newHyperlink.Hyperlink = _link; } newHyperlink.Ref = range; newHyperlink.Tooltip = rangeStyle.hyperLink.toolTip; range.setHyperlink(newHyperlink); } else if (rangeStyle.hyperlinkObj && specialPasteProps.hyperlink) { rangeStyle.hyperlinkObj.Ref = range; range.setHyperlink(rangeStyle.hyperlinkObj, true); } }; // Залочена ли панель для закрепления WorksheetView.prototype._isLockedFrozenPane = function ( callback ) { var sheetId = this.model.getId(); var lockInfo = this.collaborativeEditing.getLockInfo( c_oAscLockTypeElem.Object, null, sheetId, AscCommonExcel.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 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.model.selectionRange.getLast(); var lockInfo = this.collaborativeEditing.getLockInfo(c_oAscLockTypeElem.Range, /*subType*/subType, sheetId, new AscCommonExcel.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 AscCommonExcel.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 = this.model.selectionRange.getLast().clone(); var checkRange = arn.getAllRange(); var range; var oRecalcType = AscCommonExcel.recalcType.recalc; var reinitRanges = false; var updateDrawingObjectsInfo = null; var updateDrawingObjectsInfo2 = null;//{bInsert: false, operType: c_oAscInsertOptions.InsertColumns, updateRange: arn} var isUpdateCols = false, isUpdateRows = false; var isCheckChangeAutoFilter; var functionModelAction = null; var lockDraw = false; // Параметр, при котором не будет отрисовки (т.к. мы просто обновляем информацию на неактивном листе) var lockRange, arrChangedRanges = []; var onChangeWorksheetCallback = function (isSuccess) { if (false === isSuccess) { return; } asc_applyFunction(functionModelAction); t._initCellsArea(oRecalcType); if (oRecalcType) { t.cache.reset(); } t._cleanCellsTextMetricsCache(); t._prepareCellTextMetricsCache(); if (t.objectRender) { if (reinitRanges && t.objectRender.drawingArea) { t.objectRender.drawingArea.reinitRanges(); } if (null !== updateDrawingObjectsInfo) { t.objectRender.updateSizeDrawingObjects(updateDrawingObjectsInfo); } if (null !== updateDrawingObjectsInfo2) { t.objectRender.updateDrawingObject(updateDrawingObjectsInfo2.bInsert, updateDrawingObjectsInfo2.operType, updateDrawingObjectsInfo2.updateRange); } t.model.onUpdateRanges(arrChangedRanges); t.objectRender.rebuildChartGraphicObjects(arrChangedRanges); } t.draw(lockDraw); t.handlers.trigger("reinitializeScroll"); if (isUpdateCols) { t._updateVisibleColsCount(); } if (isUpdateRows) { t._updateVisibleRowsCount(); } t.handlers.trigger("selectionChanged"); t.handlers.trigger("selectionMathInfoChanged", t.getSelectionMathInfo()); }; switch (prop) { case "colWidth": functionModelAction = function () { t.model.setColWidth(val, checkRange.c1, checkRange.c2); isUpdateCols = true; oRecalcType = AscCommonExcel.recalcType.full; 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); oRecalcType = AscCommonExcel.recalcType.full; reinitRanges = true; updateDrawingObjectsInfo = {target: c_oTargetType.ColumnResize, col: arn.c1}; }; arrChangedRanges.push(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); oRecalcType = AscCommonExcel.recalcType.full; reinitRanges = true; updateDrawingObjectsInfo = {target: c_oTargetType.ColumnResize, col: arn.c1}; }; arrChangedRanges.push(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; oRecalcType = AscCommonExcel.recalcType.full; 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.model.autoFilters.reDrawFilter(arn); oRecalcType = AscCommonExcel.recalcType.full; reinitRanges = true; updateDrawingObjectsInfo = {target: c_oTargetType.RowResize, row: arn.r1}; }; arrChangedRanges.push(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.model.autoFilters.reDrawFilter(arn); oRecalcType = AscCommonExcel.recalcType.full; reinitRanges = true; updateDrawingObjectsInfo = {target: c_oTargetType.RowResize, row: arn.r1}; }; arrChangedRanges.push(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.af_checkInsDelCells(arn, c_oAscInsertOptions.InsertCellsAndShiftRight, prop); if (isCheckChangeAutoFilter === false) { return; } functionModelAction = function () { History.Create_NewPoint(); History.StartTransaction(); if (range.addCellsShiftRight()) { oRecalcType = AscCommonExcel.recalcType.full; reinitRanges = true; t.cellCommentator.updateCommentsDependencies(true, val, arn); updateDrawingObjectsInfo2 = {bInsert: true, operType: val, updateRange: arn}; } History.EndTransaction(); }; arrChangedRanges.push(lockRange = new asc_Range(arn.c1, arn.r1, gc_nMaxCol0, arn.r2)); this._isLockedCells(lockRange, null, onChangeWorksheetCallback); break; case c_oAscInsertOptions.InsertCellsAndShiftDown: isCheckChangeAutoFilter = t.af_checkInsDelCells(arn, c_oAscInsertOptions.InsertCellsAndShiftDown, prop); if (isCheckChangeAutoFilter === false) { return; } functionModelAction = function () { History.Create_NewPoint(); History.StartTransaction(); if (range.addCellsShiftBottom()) { oRecalcType = AscCommonExcel.recalcType.full; reinitRanges = true; t.cellCommentator.updateCommentsDependencies(true, val, arn); updateDrawingObjectsInfo2 = {bInsert: true, operType: val, updateRange: arn}; } History.EndTransaction(); }; arrChangedRanges.push(lockRange = new asc_Range(arn.c1, arn.r1, arn.c2, gc_nMaxRow0)); this._isLockedCells(lockRange, null, onChangeWorksheetCallback); break; case c_oAscInsertOptions.InsertColumns: isCheckChangeAutoFilter = t.model.autoFilters.isRangeIntersectionSeveralTableParts(arn); if (isCheckChangeAutoFilter === true) { this.model.workbook.handlers.trigger("asc_onError", c_oAscError.ID.AutoFilterChangeFormatTableError, c_oAscError.Level.NoCritical); return; } functionModelAction = function () { History.Create_NewPoint(); History.StartTransaction(); oRecalcType = AscCommonExcel.recalcType.full; reinitRanges = true; t.model.insertColsBefore(arn.c1, arn.c2 - arn.c1 + 1); updateDrawingObjectsInfo2 = {bInsert: true, operType: val, updateRange: arn}; t.cellCommentator.updateCommentsDependencies(true, val, arn); History.EndTransaction(); }; arrChangedRanges.push(lockRange = new asc_Range(arn.c1, 0, arn.c2, gc_nMaxRow0)); this._isLockedCells(lockRange, c_oAscLockTypeElemSubType.InsertColumns, onChangeWorksheetCallback); break; case c_oAscInsertOptions.InsertRows: functionModelAction = function () { oRecalcType = AscCommonExcel.recalcType.full; reinitRanges = true; t.model.insertRowsBefore(arn.r1, arn.r2 - arn.r1 + 1); updateDrawingObjectsInfo2 = {bInsert: true, operType: val, updateRange: arn}; t.cellCommentator.updateCommentsDependencies(true, val, arn); }; arrChangedRanges.push(lockRange = new asc_Range(0, arn.r1, gc_nMaxCol0, arn.r2)); this._isLockedCells(lockRange, 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.af_checkInsDelCells(arn, c_oAscDeleteOptions.DeleteCellsAndShiftLeft, prop); if (isCheckChangeAutoFilter === false) { return; } functionModelAction = function () { History.Create_NewPoint(); History.StartTransaction(); if (isCheckChangeAutoFilter === true) { t.model.autoFilters.isEmptyAutoFilters(arn, c_oAscDeleteOptions.DeleteCellsAndShiftLeft); } if (range.deleteCellsShiftLeft(function () { t._cleanCache(lockRange); t.cellCommentator.updateCommentsDependencies(false, val, checkRange); })) { updateDrawingObjectsInfo2 = {bInsert: false, operType: val, updateRange: arn}; } History.EndTransaction(); reinitRanges = true; }; arrChangedRanges.push( lockRange = new asc_Range(checkRange.c1, checkRange.r1, gc_nMaxCol0, checkRange.r2)); this._isLockedCells(lockRange, null, onChangeWorksheetCallback); break; case c_oAscDeleteOptions.DeleteCellsAndShiftTop: isCheckChangeAutoFilter = t.af_checkInsDelCells(arn, c_oAscDeleteOptions.DeleteCellsAndShiftTop, prop); if (isCheckChangeAutoFilter === false) { return; } functionModelAction = function () { History.Create_NewPoint(); History.StartTransaction(); if (isCheckChangeAutoFilter === true) { t.model.autoFilters.isEmptyAutoFilters(arn, c_oAscDeleteOptions.DeleteCellsAndShiftTop); } if (range.deleteCellsShiftUp(function () { t._cleanCache(lockRange); t.cellCommentator.updateCommentsDependencies(false, val, checkRange); })) { updateDrawingObjectsInfo2 = {bInsert: false, operType: val, updateRange: arn}; } History.EndTransaction(); reinitRanges = true; }; arrChangedRanges.push( lockRange = new asc_Range(checkRange.c1, checkRange.r1, checkRange.c2, gc_nMaxRow0)); this._isLockedCells(lockRange, null, onChangeWorksheetCallback); break; case c_oAscDeleteOptions.DeleteColumns: isCheckChangeAutoFilter = t.model.autoFilters.isActiveCellsCrossHalfFTable(checkRange, c_oAscDeleteOptions.DeleteColumns, prop); if (isCheckChangeAutoFilter === false) { return; } functionModelAction = function () { oRecalcType = AscCommonExcel.recalcType.full; reinitRanges = true; History.Create_NewPoint(); History.StartTransaction(); t.cellCommentator.updateCommentsDependencies(false, val, checkRange); t.model.autoFilters.isEmptyAutoFilters(arn, c_oAscDeleteOptions.DeleteColumns); t.model.removeCols(checkRange.c1, checkRange.c2); updateDrawingObjectsInfo2 = {bInsert: false, operType: val, updateRange: arn}; History.EndTransaction(); }; arrChangedRanges.push(lockRange = new asc_Range(checkRange.c1, 0, checkRange.c2, gc_nMaxRow0)); this._isLockedCells(lockRange, c_oAscLockTypeElemSubType.DeleteColumns, onChangeWorksheetCallback); break; case c_oAscDeleteOptions.DeleteRows: isCheckChangeAutoFilter = t.model.autoFilters.isActiveCellsCrossHalfFTable(checkRange, c_oAscDeleteOptions.DeleteRows, prop); if (isCheckChangeAutoFilter === false) { return; } functionModelAction = function () { oRecalcType = AscCommonExcel.recalcType.full; reinitRanges = true; History.Create_NewPoint(); History.StartTransaction(); checkRange = t.model.autoFilters.checkDeleteAllRowsFormatTable(checkRange, true); t.cellCommentator.updateCommentsDependencies(false, val, checkRange); t.model.autoFilters.isEmptyAutoFilters(arn, c_oAscDeleteOptions.DeleteRows); t.model.removeRows(checkRange.r1, checkRange.r2); updateDrawingObjectsInfo2 = {bInsert: false, operType: val, updateRange: arn}; History.EndTransaction(); }; arrChangedRanges.push(lockRange = new asc_Range(0, checkRange.r1, gc_nMaxCol0, checkRange.r2)); this._isLockedCells(lockRange, c_oAscLockTypeElemSubType.DeleteRows, onChangeWorksheetCallback); break; } this.handlers.trigger("selectionNameChanged", t.getSelectionName(/*bRangeText*/false)); break; case "sheetViewSettings": functionModelAction = function () { if (AscCH.historyitem_Worksheet_SetDisplayGridlines === val.type) { t.model.setDisplayGridlines(val.value); } else { t.model.setDisplayHeadings(val.value); } isUpdateCols = true; isUpdateRows = true; oRecalcType = AscCommonExcel.recalcType.full; reinitRanges = true; }; this._isLockedAll(onChangeWorksheetCallback); break; case "update": if (val !== undefined) { lockDraw = true === val.lockDraw; reinitRanges = !!val.reinitRanges; } onChangeWorksheetCallback(true); break; } }; WorksheetView.prototype.expandColsOnScroll = function (isNotActive, updateColsCount, newColsCount) { var maxColObjects = this.objectRender ? this.objectRender.getMaxColRow().col : -1; var maxc = Math.max(this.model.getColsCount() + 1, this.cols.length, maxColObjects); 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; } } // Проверяем ограничения по столбцам if (gc_nMaxCol < this.nColsCount) { this.nColsCount = gc_nMaxCol; } this._calcWidthColumns(AscCommonExcel.recalcType.newLines); if (this.objectRender && this.objectRender.drawingArea) { this.objectRender.drawingArea.reinitRanges(); } return nLastCols !== this.nColsCount; }; WorksheetView.prototype.expandRowsOnScroll = function (isNotActive, updateRowsCount, newRowsCount) { var maxRowObjects = this.objectRender ? this.objectRender.getMaxColRow().row : -1; var maxr = Math.max(this.model.getRowsCount() + 1, this.rows.length, maxRowObjects); 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; } } // Проверяем ограничения по строкам if (gc_nMaxRow < this.nRowsCount) { this.nRowsCount = gc_nMaxRow; } this._calcHeightRows(AscCommonExcel.recalcType.newLines); if (this.objectRender && this.objectRender.drawingArea) { this.objectRender.drawingArea.reinitRanges(); } return nLastRows !== this.nRowsCount; }; 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://bugzilla.onlyoffice.com/show_bug.cgi?id=19974) tm.width = oldWidth; break; } lastHeight = tm.height; maxW += this.maxDigitWidth; } width = Math.max(width, tm.width); } else { filterButton = this.af_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), Asc.c_oAscMaxColumnWidth); cw = this.model.charCountToModelColWidth(cc); } else { cw = AscCommonExcel.oDefaultMetrics.ColWidthChars; cc = this.defaultColWidthChars; } if (cc === oldColWidth || (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.autoFitColumnWidth = function (col1, col2) { var t = this; return this._isLockedAll(function (isSuccess) { if (false === isSuccess) { return; } if (null === col1) { var lastSelection = t.model.selectionRange.getLast(); col1 = lastSelection.c1; col2 = lastSelection.c2; } var w, bUpdate = false; History.Create_NewPoint(); History.StartTransaction(); for (var c = col1; c <= col2; ++c) { w = t.onChangeWidthCallback(c, null, null); if (-1 !== w) { t.cols[c] = t._calcColWidth(w); t.cols[c].isCustomWidth = false; bUpdate = true; t._cleanCache(new asc_Range(c, 0, c, t.rows.length - 1)); } } if (bUpdate) { t._updateColumnPositions(); t._updateVisibleColsCount(); t._calcHeightRows(AscCommonExcel.recalcType.recalc); t._updateVisibleRowsCount(); t.objectRender.drawingArea.reinitRanges(); t.changeWorksheet("update"); } History.EndTransaction(); }); }; WorksheetView.prototype.autoFitRowHeight = function (row1, row2) { var t = this; var onChangeHeightCallback = function (isSuccess) { if (false === isSuccess) { return; } if (null === row1) { var lastSelection = t.model.selectionRange.getLast(); row1 = lastSelection.r1; row2 = lastSelection.r2; } History.Create_NewPoint(); var oSelection = History.GetSelection(); if (null != oSelection) { oSelection = oSelection.clone(); oSelection.assign(0, row1, gc_nMaxCol0, row2); oSelection.type = c_oAscSelectionType.RangeRow; History.SetSelection(oSelection); History.SetSelectionRedo(oSelection); } History.StartTransaction(); var height, col, ct, mc; for (var r = row1; r <= row2; ++r) { height = t.defaultRowHeight; for (col = 0; col < t.cols.length; ++col) { ct = t._getCellTextCache(col, r); 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 + t.height_1px); } t.model.setRowBestFit(true, Math.min(height, t.maxRowHeight), r, r); } t.nRowsCount = 0; t._calcHeightRows(AscCommonExcel.recalcType.recalc); t._updateVisibleRowsCount(); t._cleanCache(new asc_Range(0, row1, t.cols.length - 1, row2)); t.objectRender.drawingArea.reinitRanges(); t.changeWorksheet("update"); History.EndTransaction(); }; return this._isLockedAll(onChangeHeightCallback); }; // ----- Search ----- 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 === Asc.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.activeCell ? options.activeCell : this.model.selectionRange.activeCell; var c = ar.col; var r = ar.row; 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://bugzilla.onlyoffice.com/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.row; } else { c = 0; r = -1; maxC = ar.col; maxR = this.rows.length - 1; } } else { // Идем назад с последней c = this.cols.length - 1; r = this.rows.length - 1; if (options.scanByRows) { minC = 0; minR = ar.row; } else { minC = ar.col; 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 activeCell = this.model.selectionRange.activeCell.clone(); var aReplaceCells = []; if (options.isReplaceAll) { var aReplaceCellsIndex = {}; options.activeCell = activeCell; 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); activeCell.col = findResult.c1; activeCell.row = findResult.r1; } } else { // Попробуем сначала найти var isEqual = this._isCellEqual(activeCell.col, activeCell.row, 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, "\\?").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); 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 AscCommonExcel.Fragment({text: cellValue, format: v[0].format.clone()}); if (!t._saveCellValueAfterEdit(oCellEdit, c, newValue, /*flags*/undefined, /*skipNLCheck*/false, /*isNotHistory*/true, /*lockDraw*/true)) { options.error = true; t.draw(lockDraw); return callback(options); } } 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, isViewMode) { var mc, ranges = AscCommonExcel.getRangeByRef(reference, this.model, true); if (0 === ranges.length && !isViewMode) { /*TODO: сделать поиск по названиям автофигур, должен искать до того как вызвать поиск по именованным диапазонам*/ if (this.collaborativeEditing.getGlobalLock() || !this.handlers.trigger("getLockDefNameManagerStatus")) { this.handlers.trigger("onErrorEvent", c_oAscError.ID.LockCreateDefName, c_oAscError.Level.NoCritical); this._updateSelectionNameAndInfo(); return true; } // ToDo multiselect defined names var selectionLast = this.model.selectionRange.getLast(); mc = selectionLast.isOneCell() ? this.model.getMergedByCell(selectionLast.r1, selectionLast.c1) : null; var defName = this.model.workbook.editDefinesNames(null, new Asc.asc_CDefName(reference, parserHelp.get3DRef(this.model.getName(), (mc || selectionLast).getAbsName()))); if (defName) { this._isLockedDefNames(null, defName.getNodeId()); } else { this.handlers.trigger("asc_onError", c_oAscError.ID.InvalidReferenceOrName, c_oAscError.Level.NoCritical); } } return ranges; }; /* Ищет дополнение для ячейки */ WorksheetView.prototype.getCellAutoCompleteValues = function (cell, maxCount) { var arrValues = [], objValues = {}; var range = this.findCellAutoComplete(cell, 1, maxCount); this.getColValues(range, cell.col, arrValues, objValues); range = this.findCellAutoComplete(cell, -1, maxCount); this.getColValues(range, cell.col, arrValues, objValues); arrValues.sort(); return arrValues; }; /* Ищет дополнение для ячейки (снизу или сверху) */ WorksheetView.prototype.findCellAutoComplete = function (cellActive, step, maxCount) { var col = cellActive.col, row = cellActive.row; 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 (!AscCommon.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.model.selectionRange = this.copyActiveRange.clone(); } this.copyActiveRange = null; if (oldSelectionDialogType === c_oAscSelectionDialogType.Chart) { this.objectRender.controller.checkChartForProps(false); } } else { this.copyActiveRange = this.model.selectionRange.clone(); if (selectRange) { if (typeof selectRange === 'string') { selectRange = this.model.getRange2(selectRange); if (selectRange) { selectRange = selectRange.getBBox0(); } } if (null != selectRange) { this.model.selectionRange.assign2(selectRange); } } 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.model.selectionRange.activeCell.col; row = t.model.selectionRange.activeCell.row; } // Проверим замерженность 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(); t.model.autoFilters.renameTableColumn(oCellEdit); } else { c.setValue2(val); // Вызываем функцию пересчета для заголовков форматированной таблицы t.model.autoFilters.renameTableColumn(oCellEdit); } if (!isFormula) { // Нужно ли выставлять WrapText (ищем символ новой строки в тексте) if (!skipNLCheck) { for (var i = 0; i < val.length; ++i) { if (-1 !== val[i].text.indexOf(kNewLine)) { c.setWrap(true); break; } } } } t._updateCellsRange(oCellEdit, isNotHistory ? c_oAscCanChangeColWidth.none : c_oAscCanChangeColWidth.numbers, lockDraw); if (!isNotHistory) { History.EndTransaction(); } // если вернуть false, то редактор не закроется return true; }; WorksheetView.prototype.openCellEditor = function (editor, fragments, cursorPos, isFocus, isClearCell, isHideCursor, isQuickInput, selectionRange) { var t = this, tc = this.cols, tr = this.rows, col, row, c, fl, mc, bg, isMerged; if (selectionRange) { this.model.selectionRange = selectionRange; } if (0 < this.arrActiveFormulaRanges.length) { this.cleanSelection(); this.cleanFormulaRanges(); this._drawSelection(); } var cell = this.model.selectionRange.activeCell; 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}; } col = cell.col; row = cell.row; // Возможно стоит заменить на ячейку из кеша c = this._getVisibleCell(col, row); fl = this._getCellFlags(c); isMerged = fl.isMerged(); if (isMerged) { mc = fl.merged; c = this._getVisibleCell(mc.c1, mc.r1); fl = this._getCellFlags(c); } // Выставляем режим 'не редактируем' (иначе мы попытаемся переместить редактор, который еще не открыт) this.isCellEditMode = false; this.handlers.trigger("onScroll", this._calcActiveCellOffset()); this.isCellEditMode = true; bg = c.getFill(); this.isFormulaEditMode = false; var font = c.getFont(); // Скрываем окно редактирования комментария 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(cell, kMaxAutoCompleteCellEdit); var arrAutoCompleteLC = asc.arrayToLowerCase(arrAutoComplete); editor.open({ fragments: fragments, flags: fl, font: new asc.FontProperties(font.getName(), font.getSize()), background: bg || this.settings.cells.defaultState.background, textColor: font.getColor() || this.settings.cells.defaultState.color, cursorPos: cursorPos, zoom: this.getZoom(), focus: isFocus, isClearCell: isClearCell, isHideCursor: isHideCursor, isQuickInput: isQuickInput, isAddPersentFormat: isQuickInput && Asc.c_oAscNumFormatType.Percent === c.getNumFormat().getType(), autoComplete: arrAutoComplete, autoCompleteLC: arrAutoCompleteLC, cellName: c.getName(), cellNumFormat: c.getNumFormatType(), 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(AscCommon.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, selectionRange) { var t = this; selectionRange = (selectionRange) ? selectionRange : this.model.selectionRange; var activeCell = selectionRange.activeCell; var c = t._getVisibleCell(activeCell.col, activeCell.row); var v, copyValue; // get first fragment and change its text v = c.getValueForEdit2().slice(0, 1); // Создаем новый массив, т.к. getValueForEdit2 возвращает ссылку copyValue = []; copyValue[0] = new AscCommonExcel.Fragment({text: text, format: v[0].format.clone()}); var bSuccess = t.openCellEditor(editor, /*fragments*/undefined, /*cursorPos*/undefined, isFocus, /*isClearCell*/ true, /*isHideCursor*/false, /*isQuickInput*/false, selectionRange); 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, lockDraw, updateHeight) { var arrRanges = [], range; for (var i in ranges) { range = ranges[i]; this.updateRange(range, range.canChangeColWidth || c_oAscCanChangeColWidth.none, 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.model.selectionRange.getLast().clone(); } 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 + this.height_1px); } if (ct.cellVA !== Asc.c_oAscVAlign.Top && ct.cellVA !== Asc.c_oAscVAlign.Center && !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) { this.rows[r].heightReal = this.rows[r].height = Math.min(h, this.maxRowHeight); History.TurnOff(); this.model.setRowHeight(this.rows[r].height, r, r, false); History.TurnOn(); 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(AscCommonExcel.recalcType.full); this.cache.reset(); this._cleanCellsTextMetricsCache(); this._prepareCellTextMetricsCache(); this.handlers.trigger("reinitializeScroll"); } if (!lockDraw) { this._updateSelectionNameAndInfo(); } this.model.onUpdateRanges(arrChanged); this.objectRender.rebuildChartGraphicObjects(arrChanged); this.cellCommentator.updateCommentPosition(); this.handlers.trigger("onDocumentPlaceChanged"); this.draw(lockDraw); }; WorksheetView.prototype.setChartRange = function (range) { this.isChartAreaEditMode = true; this.arrActiveChartRanges[0].assign2(range); }; WorksheetView.prototype.endEditChart = function () { if (this.isChartAreaEditMode) { this.isChartAreaEditMode = false; this.arrActiveChartRanges[0].clean(); } }; WorksheetView.prototype.enterCellRange = function (editor) { if (!this.isFormulaEditMode) { return; } var currentFormula = this.arrActiveFormulaRanges[this.arrActiveFormulaRangesPosition]; var currentRange = currentFormula.getLast().clone(); var activeCellId = currentFormula.activeCellId; var activeCell = currentFormula.activeCell.clone(); // Замерженную ячейку должны отдать только левую верхнюю. 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.getLast().isEqual(currentRange)) { tmpRange.activeCellId = activeCellId; tmpRange.activeCell.col = activeCell.col; tmpRange.activeCell.row = activeCell.row; break; } } }; WorksheetView.prototype.addFormulaRange = function (range) { var r = this.model.selectionRange.clone(); if (range) { r.assign2(range); var lastSelection = r.getLast(); lastSelection.cursorePos = range.cursorePos; lastSelection.formulaRangeLength = range.formulaRangeLength; lastSelection.colorRangePos = range.colorRangePos; lastSelection.colorRangeLength = range.colorRangeLength; lastSelection.isName = range.isName; } 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].getLast().isEqual(range)) { this.arrActiveFormulaRangesPosition = i; return; } } }; WorksheetView.prototype.removeFormulaRange = function (range) { this.arrActiveFormulaRangesPosition = -1; for (var i = 0; i < this.arrActiveFormulaRanges.length; ++i) { if (this.arrActiveFormulaRanges[i].getLast().isEqual(range)) { this.arrActiveFormulaRanges.splice(i, 1); return; } } }; WorksheetView.prototype.cleanFormulaRanges = function () { // Очищаем массив ячеек для текущей формулы this.arrActiveFormulaRangesPosition = -1; 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 = this.model.selectionRange.getLast().clone(); var isChangeAutoFilterToTablePart = function (addFormatTableOptionsObj) { var res = false; var worksheet = t.model; var activeRange = AscCommonExcel.g_oRangeCache.getAscRange(addFormatTableOptionsObj.asc_getRange()); if (activeRange && worksheet.AutoFilter && activeRange.containsRange(worksheet.AutoFilter.Ref) && activeRange.r1 === worksheet.AutoFilter.Ref.r1) { res = true; } return res; }; 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"); return; } var addNameColumn, filterRange; if (addFormatTableOptionsObj && isChangeAutoFilterToTablePart(addFormatTableOptionsObj) === true)//CHANGE FILTER TO TABLEPART { filterRange = t.model.AutoFilter.Ref.clone(); var addFilterCallBack = function () { History.Create_NewPoint(); History.StartTransaction(); t.model.autoFilters.changeAutoFilterToTablePart(styleName, ar, addFormatTableOptionsObj); t._onUpdateFormatTable(filterRange, !!(styleName), true); History.EndTransaction(); }; addNameColumn = false; if (addFormatTableOptionsObj === false) { addNameColumn = true; } else if (typeof addFormatTableOptionsObj == 'object') { addNameColumn = !addFormatTableOptionsObj.asc_getIsTitle(); } if (addNameColumn) { filterRange.r2 = filterRange.r2 + 1; } t._isLockedCells(filterRange, /*subType*/null, addFilterCallBack); } else//ADD { var addFilterCallBack = function () { History.Create_NewPoint(); History.StartTransaction(); t.model.autoFilters.addAutoFilter(styleName, ar, addFormatTableOptionsObj); //updates if (styleName && addNameColumn) { t.setSelection(filterRange); } t._onUpdateFormatTable(filterRange, !!(styleName), true); History.EndTransaction(); }; if (styleName === null) { addFilterCallBack(); } else { var filterInfo = t.model.autoFilters._getFilterInfoByAddTableProps(ar, addFormatTableOptionsObj); filterRange = filterInfo.filterRange addNameColumn = filterInfo.addNameColumn; t._isLockedCells(filterRange, null, addFilterCallBack) } } }; if (t._checkAddAutoFilter(ar, styleName, addFormatTableOptionsObj) === true) { this._isLockedAll(onChangeAutoFilterCallback); this._isLockedDefNames(null, null); } else//для того, чтобы в случае ошибки кнопка отжималась! { t.handlers.trigger("selectionChanged"); } }; WorksheetView.prototype.changeAutoFilter = function (tableName, optionType, val) { // Проверка глобального лока if (this.collaborativeEditing.getGlobalLock()) { return; } var t = this; var ar = this.model.selectionRange.getLast().clone(); var onChangeAutoFilterCallback = function (isSuccess) { if (false === isSuccess) { t.handlers.trigger("selectionChanged"); return; } switch (optionType) { case Asc.c_oAscChangeFilterOptions.filter: { //DELETE if (!val) { var filterRange = null; var tablePartsContainsRange = t.model.autoFilters._isTablePartsContainsRange(ar); if (tablePartsContainsRange && tablePartsContainsRange.Ref) { filterRange = tablePartsContainsRange.Ref.clone(); } else if (t.model.AutoFilter) { filterRange = t.model.AutoFilter.Ref; } if (null === filterRange) { return; } var deleteFilterCallBack = function () { t.model.autoFilters.deleteAutoFilter(ar, tableName); t.af_drawButtons(filterRange); t._onUpdateFormatTable(filterRange, false, true); } t._isLockedCells(filterRange, /*subType*/null, deleteFilterCallBack); } else//ADD ONLY FILTER { var addFilterCallBack = function () { History.Create_NewPoint(); History.StartTransaction(); t.model.autoFilters.addAutoFilter(null, ar); t._onUpdateFormatTable(filterRange, false, true); History.EndTransaction(); }; var filterInfo = t.model.autoFilters._getFilterInfoByAddTableProps(ar); var filterRange = filterInfo.filterRange t._isLockedCells(filterRange, null, addFilterCallBack) } break; } case Asc.c_oAscChangeFilterOptions.style://CHANGE STYLE { var changeStyleFilterCallBack = function () { History.Create_NewPoint(); History.StartTransaction(); //TODO внутри вызывается _isTablePartsContainsRange t.model.autoFilters.changeTableStyleInfo(val, ar, tableName); t._onUpdateFormatTable(filterRange, false, true); History.EndTransaction(); }; var filterRange; //calculate lock range and callback parameters var isTablePartsContainsRange = t.model.autoFilters._isTablePartsContainsRange(ar); if (isTablePartsContainsRange !== null)//if one of the tableParts contains activeRange { filterRange = isTablePartsContainsRange.Ref.clone(); } t._isLockedCells(filterRange, /*subType*/null, changeStyleFilterCallBack); break; } } }; if(Asc.c_oAscChangeFilterOptions.style === optionType) { onChangeAutoFilterCallback(true); } else { this._isLockedAll(onChangeAutoFilterCallback); } }; WorksheetView.prototype.applyAutoFilter = function (autoFilterObject) { var t = this; var ar = this.model.selectionRange.getLast().clone(); var onChangeAutoFilterCallback = function (isSuccess) { if (false === isSuccess) { return; } var applyFilterProps = t.model.autoFilters.applyAutoFilter(autoFilterObject, ar); var minChangeRow = applyFilterProps.minChangeRow; var rangeOldFilter = applyFilterProps.rangeOldFilter; if (null !== rangeOldFilter && !t.model.workbook.bUndoChanges && !t.model.workbook.bRedoChanges) { t._onUpdateFormatTable(rangeOldFilter, false, true); if (applyFilterProps.nOpenRowsCount !== applyFilterProps.nAllRowsCount) { t.handlers.trigger('onFilterInfo', applyFilterProps.nOpenRowsCount, applyFilterProps.nAllRowsCount); } } if (null !== minChangeRow) { t.objectRender.updateSizeDrawingObjects({target: c_oTargetType.RowResize, row: minChangeRow}); } }; this._isLockedAll(onChangeAutoFilterCallback); }; WorksheetView.prototype.reapplyAutoFilter = function (tableName) { var t = this; var ar = this.model.selectionRange.getLast().clone(); var onChangeAutoFilterCallback = function (isSuccess) { if (false === isSuccess) { return; } //reApply var applyFilterProps = t.model.autoFilters.reapplyAutoFilter(tableName, ar); //reSort var filter = applyFilterProps.filter; if (filter && filter.SortState && filter.SortState.SortConditions && filter.SortState.SortConditions[0]) { var sortState = filter.SortState; var rangeWithoutHeaderFooter = filter.getRangeWithoutHeaderFooter(); var sortRange = t.model.getRange3(rangeWithoutHeaderFooter.r1, rangeWithoutHeaderFooter.c1, rangeWithoutHeaderFooter.r2, rangeWithoutHeaderFooter.c2); var startCol = sortState.SortConditions[0].Ref.c1; var type; var rgbColor = null; switch (sortState.SortConditions[0].ConditionSortBy) { case Asc.ESortBy.sortbyCellColor: { type = Asc.c_oAscSortOptions.ByColorFill; rgbColor = sortState.SortConditions[0].dxf.fill.bg; break; } case Asc.ESortBy.sortbyFontColor: { type = Asc.c_oAscSortOptions.ByColorFont; rgbColor = sortState.SortConditions[0].dxf.font.getColor(); break; } default: { type = Asc.c_oAscSortOptions.ByColorFont; if (sortState.SortConditions[0].ConditionDescending) { type = Asc.c_oAscSortOptions.Ascending; } else { type = Asc.c_oAscSortOptions.Descending; } } } var sort = sortRange.sort(type, startCol, rgbColor); t.cellCommentator.sortComments(sort); } t.model.autoFilters._resetTablePartStyle(); var minChangeRow = applyFilterProps.minChangeRow; var updateRange = applyFilterProps.updateRange; if (updateRange && !t.model.workbook.bUndoChanges && !t.model.workbook.bRedoChanges) { t._onUpdateFormatTable(updateRange, false, true); } if (null !== minChangeRow) { t.objectRender.updateSizeDrawingObjects({target: c_oTargetType.RowResize, row: minChangeRow}); } }; this._isLockedAll(onChangeAutoFilterCallback); }; WorksheetView.prototype.applyAutoFilterByType = function (autoFilterObject) { var t = this; var activeCell = this.model.selectionRange.activeCell.clone(); var ar = this.model.selectionRange.getLast().clone(); var isStartRangeIntoFilterOrTable = t.model.autoFilters.isStartRangeContainIntoTableOrFilter(activeCell); var isApplyAutoFilter = null, isAddAutoFilter = null, cellId = null, isFromatTable = null; if (null !== isStartRangeIntoFilterOrTable)//into autofilter or format table { isFromatTable = !(-1 === isStartRangeIntoFilterOrTable); var filterRef = isFromatTable ? t.model.TableParts[isStartRangeIntoFilterOrTable].Ref : t.model.AutoFilter.Ref; cellId = t.model.autoFilters._rangeToId(Asc.Range(ar.c1, filterRef.r1, ar.c1, filterRef.r1)); isApplyAutoFilter = true; if (isFromatTable && !t.model.TableParts[isStartRangeIntoFilterOrTable].AutoFilter)//add autofilter to tablepart { isAddAutoFilter = true; } } else//without filter { isAddAutoFilter = true; isApplyAutoFilter = true; } var onChangeAutoFilterCallback = function (isSuccess) { if (false === isSuccess) { return; } History.Create_NewPoint(); History.StartTransaction(); if (null !== isAddAutoFilter) { //delete old filter if (!isFromatTable && t.model.AutoFilter && t.model.AutoFilter.Ref) { t.model.autoFilters.isEmptyAutoFilters(t.model.AutoFilter.Ref); } //add new filter t.model.autoFilters.addAutoFilter(null, ar, null); //generate cellId if (null === cellId) { cellId = t.model.autoFilters._rangeToId( Asc.Range(activeCell.col, t.model.AutoFilter.Ref.r1, activeCell.col, t.model.AutoFilter.Ref.r1)); } } if (null !== isApplyAutoFilter) { autoFilterObject.asc_setCellId(cellId); var filter = autoFilterObject.filter; if (c_oAscAutoFilterTypes.CustomFilters === filter.type) { var cell = t.model._getCell(activeCell.row, activeCell.col); var val = cell.getValueWithoutFormat(); filter.filter.CustomFilters[0].Val = val; } else if (c_oAscAutoFilterTypes.ColorFilter === filter.type) { var cell = t.model._getCell(activeCell.row, activeCell.col); if (filter.filter && filter.filter.dxf && filter.filter.dxf.fill) { if (false === filter.filter.CellColor) { var fontColor = cell.xfs && cell.xfs.font ? cell.xfs.font.getColor() : null; //TODO добавлять дефолтовый цвет шрифта в случае, если цвет шрифта не указан if (null !== fontColor) { filter.filter.dxf.fill.bg = fontColor; } } else { //TODO просмотерть ситуации без заливки var color = cell.getStyle(); var cellColor = null !== color && color.fill && color.fill.bg ? color.fill.bg : null; filter.filter.dxf.fill.bg = null !== cellColor ? new AscCommonExcel.RgbColor(cellColor.rgb) : new AscCommonExcel.RgbColor(null); } } } var applyFilterProps = t.model.autoFilters.applyAutoFilter(autoFilterObject, ar); var minChangeRow = applyFilterProps.minChangeRow; var rangeOldFilter = applyFilterProps.rangeOldFilter; if (null !== rangeOldFilter && !t.model.workbook.bUndoChanges && !t.model.workbook.bRedoChanges) { t._onUpdateFormatTable(rangeOldFilter, false, true); } if (null !== minChangeRow) { t.objectRender.updateSizeDrawingObjects({target: c_oTargetType.RowResize, row: minChangeRow}); } } History.EndTransaction(); }; if (null === isAddAutoFilter)//do not add autoFilter { this._isLockedAll(onChangeAutoFilterCallback); } else//add autofilter + apply { if (t._checkAddAutoFilter(ar, null, autoFilterObject, true) === true) { this._isLockedAll(onChangeAutoFilterCallback); this._isLockedDefNames(null, null); } } }; WorksheetView.prototype.sortRange = function (type, cellId, displayName, color, bIsExpandRange) { var t = this; var ar = this.model.selectionRange.getLast().clone(); var onChangeAutoFilterCallback = function (isSuccess) { if (false === isSuccess) { return; } var sortProps = t.model.autoFilters.getPropForSort(cellId, ar, displayName); var onSortAutoFilterCallBack = function () { History.Create_NewPoint(); History.StartTransaction(); var rgbColor = color ? new AscCommonExcel.RgbColor((color.asc_getR() << 16) + (color.asc_getG() << 8) + color.asc_getB()) : null; var sort = sortProps.sortRange.sort(type, sortProps.startCol, rgbColor); t.cellCommentator.sortComments(sort); t.model.autoFilters.sortColFilter(type, cellId, ar, sortProps, displayName, rgbColor); t._onUpdateFormatTable(sortProps.sortRange.bbox, false); History.EndTransaction(); }; if (null === sortProps) { var rgbColor = color ? new AscCommonExcel.RgbColor((color.asc_getR() << 16) + (color.asc_getG() << 8) + color.asc_getB()) : null; //expand selectionRange if(bIsExpandRange) { var selectionRange = t.model.selectionRange; var activeCell = selectionRange.activeCell; var activeCellRange = new Asc.Range(activeCell.col, activeCell.row, activeCell.col, activeCell.row); var expandRange = t.model.autoFilters._getAdjacentCellsAF(activeCellRange); //change selection t.setSelection(expandRange); } //sort t.setSelectionInfo("sort", type, null, null, rgbColor); //TODO возможно стоит возвратить selection обратно } else if (false !== sortProps) { t._isLockedCells(sortProps.sortRange.bbox, /*subType*/null, onSortAutoFilterCallBack); } }; this._isLockedAll(onChangeAutoFilterCallback); }; WorksheetView.prototype.getAddFormatTableOptions = function (range) { var selectionRange = this.model.selectionRange.getLast(); //TODO возможно стоит перенести getAddFormatTableOptions во view return this.model.autoFilters.getAddFormatTableOptions(selectionRange, range); }; WorksheetView.prototype.clearFilter = function () { var t = this; var ar = this.model.selectionRange.getLast().clone(); var onChangeAutoFilterCallback = function (isSuccess) { if (false === isSuccess) { return; } var updateRange = t.model.autoFilters.isApplyAutoFilterInCell(ar, true); if (false !== updateRange) { t._onUpdateFormatTable(updateRange, false, true); } }; this._isLockedAll(onChangeAutoFilterCallback); }; WorksheetView.prototype.clearFilterColumn = function (cellId, displayName) { var t = this; var onChangeAutoFilterCallback = function (isSuccess) { if (false === isSuccess) { return; } var updateRange = t.model.autoFilters.clearFilterColumn(cellId, displayName); if (false !== updateRange) { t._onUpdateFormatTable(updateRange, false, true); } }; this._isLockedAll(onChangeAutoFilterCallback); }; /** * Обновление при изменениях форматированной таблицы * @param range - обновляемый диапазон (он же диапазон для выделения) * @param recalc - делать ли автоподбор по названию столбца * @param changeRowsOrMerge - менялись ли строки (скрытие раскрытие) или был unmerge * @private */ WorksheetView.prototype._onUpdateFormatTable = function (range, recalc, changeRowsOrMerge) { //ToDo заглушка, чтобы не падало. Нужно полностью переделывать этот код!!!! (Перенес выше из-за бага http://bugzilla.onlyoffice.com/show_bug.cgi?id=26705) this._checkUpdateRange(range); if (!recalc) { // При скрытии/открытии строк стоит делать update всему if (changeRowsOrMerge) { this.isChanged = true; } // Пока вызовем updateRange, но стоит делать просто draw this._updateCellsRange(range); // ToDo убрать совсем reinitRanges if (this.objectRender && this.objectRender.drawingArea) { this.objectRender.drawingArea.reinitRanges(); } return; } if (!this.model.selectionRange.getLast().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(AscCommonExcel.recalcType.full); this.cache.reset(); this._cleanCellsTextMetricsCache(); this._prepareCellTextMetricsCache(); if (this.objectRender && this.objectRender.drawingArea) { this.objectRender.drawingArea.reinitRanges(); } var arrChanged = [new asc_Range(range.c1, 0, range.c2, gc_nMaxRow0)]; this.model.onUpdateRanges(arrChanged); this.objectRender.rebuildChartGraphicObjects(arrChanged); this.draw(); this.handlers.trigger("reinitializeScroll"); this._updateSelectionNameAndInfo(); } else { // Просто отрисуем this.draw(); this._updateSelectionNameAndInfo(); } }; 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 AscCommonExcel.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; }; WorksheetView.prototype.af_drawButtons = function (updatedRange, offsetX, offsetY) { var ws = this; var aWs = this.model; var t = this; var m_oColor = new CColor(120, 120, 120); if (aWs.workbook.bUndoChanges || aWs.workbook.bRedoChanges) { return false; } var _drawFilterMark = function (x, y, height, index) { var size = 5.25 * index; var halfSize = Math.round((size / 2) / 0.75) * 0.75; var meanLine = Math.round((size * Math.sqrt(3) / 3) / 0.75) * 0.75;//длина биссектрисы равностороннего треугольника //округляем + смещаем x = Math.round((x) / 0.75) * 0.75; y = Math.round((y) / 0.75) * 0.75; var y1 = y - height; ws.drawingCtx .beginPath() .moveTo(x, y) .lineTo(x, y1) .setStrokeStyle(m_oColor) .stroke(); ws.drawingCtx .beginPath() .moveTo(x + halfSize, y1) .lineTo(x + halfSize, y1) .lineTo(x, y1 + meanLine) .lineTo(x - halfSize, y1) .lineTo(x, y1) .setFillStyle(m_oColor) .fill(); }; var _drawFilterDreieck = function (x, y, index) { var size = 5.25 * index; //сюда приходят координаты центра кнопки //чтобы кнопка была в центре, необходимо сместить var leftDiff = size / 2; var upDiff = Math.round(((size * Math.sqrt(3)) / 6) / 0.75) * 0.75;//радиус вписанной окружности в треугольник //округляем + смещаем x = Math.round((x - leftDiff) / 0.75) * 0.75; y = Math.round((y - upDiff) / 0.75) * 0.75; var meanLine = Math.round((size * Math.sqrt(3) / 3) / 0.75) * 0.75;//длина биссектрисы равностороннего треугольника var halfSize = Math.round((size / 2) / 0.75) * 0.75; //рисуем ws.drawingCtx .beginPath() .moveTo(x, y) .lineTo(x + size, y) .lineTo(x + halfSize, y + meanLine) .lineTo(x, y) .setFillStyle(m_oColor) .fill(); }; var _drawButton = function (x1, y1, options) { var isSet = options.isSetFilter; var height = 11.25; var width = 11.25; var rowHeight = ws.rows[options.row].height; var colWidth = ws.cols[options.col].width; var index = 1; var diffX = 0; var diffY = 0; if ((colWidth - 2) < width && rowHeight < (height + 2)) { if (rowHeight < colWidth) { index = rowHeight / height; width = width * index; height = rowHeight; } else { index = colWidth / width; diffY = width - colWidth; diffX = width - colWidth; width = colWidth; height = height * index; } } else if ((colWidth - 2) < width) { index = colWidth / width; //смещения по x и y diffY = width - colWidth; diffX = width - colWidth + 2; width = colWidth; height = height * index; } else if (rowHeight < height) { index = rowHeight / height; width = width * index; height = rowHeight; } //квадрат кнопки рисуем ws.drawingCtx .setFillStyle(ws.settings.cells.defaultState.background) .setLineWidth(1) .setStrokeStyle(ws.settings.cells.defaultState.border) .fillRect(x1 + diffX, y1 + diffY, width, height) .strokeRect(x1 + diffX, y1 + diffY, width, height); //координаты левого верхнего угла кнопки var upLeftXButton = x1 + diffX; var upLeftYButton = y1 + diffY; var centerX, centerY; if (isSet) { centerX = upLeftXButton + (width / 2); var heigthObj = Math.ceil((height / 2) / 0.75) * 0.75; var marginTop = Math.floor(((height - heigthObj) / 2) / 0.75) * 0.75; centerY = upLeftYButton + heigthObj + marginTop; _drawFilterMark(centerX, centerY, heigthObj, index); } else { //центр кнопки centerX = upLeftXButton + (width / 2); centerY = upLeftYButton + (height / 2); _drawFilterDreieck(centerX, centerY, index); } }; var drawCurrentFilterButton = function (filter, isTablePart) { var range = new Asc.Range(filter.Ref.c1, filter.Ref.r1, filter.Ref.c2, filter.Ref.r1); if (range.isIntersect(updatedRange)) { //TODO сделать isSetFilter var isSetFilter = false; var isShowButton = true; var row = range.r1; for (var col = range.c1; col <= range.c2; col++) { if (col >= updatedRange.c1 && col <= updatedRange.c2) { isSetFilter = false; isShowButton = true; if (filter.FilterColumns && filter.FilterColumns.length) { var colId = !isTablePart ? t.model.autoFilters._getTrueColId(filter, col - range.c1) : col - range.c1; var filterColumn = null, filterColumnWithMerge = null; for (var i = 0; i < filter.FilterColumns.length; i++) { if (filter.FilterColumns[i].ColId === col - range.c1) { filterColumn = filter.FilterColumns[i]; } if (colId === col - range.c1 && filterColumn !== null) { filterColumnWithMerge = filterColumn; break; } else if (filter.FilterColumns[i].ColId === colId) { filterColumnWithMerge = filter.FilterColumns[i]; } } if (filterColumnWithMerge && filterColumnWithMerge.isApplyAutoFilter()) { isSetFilter = true; } if (filterColumn && filterColumn.ShowButton === false) { isShowButton = false; } } if (isShowButton === false) { continue; } var width = 13; var height = 13; var rowHeight = ws.rows[row].height; if (rowHeight < height) { width = width * (rowHeight / height); height = rowHeight; } var x1 = ws.cols[col].left + ws.cols[col].width - width - 0.5; var y1 = ws.rows[row].top + ws.rows[row].height - height - 0.5; _drawButton(x1 - offsetX, y1 - offsetY, {sortState: false, isSetFilter: isSetFilter, row: row, col: col}); } } } }; if (aWs.AutoFilter) { drawCurrentFilterButton(aWs.AutoFilter); } if (aWs.TableParts && aWs.TableParts.length) { for (var i = 0; i < aWs.TableParts.length; i++) { if (aWs.TableParts[i].AutoFilter && aWs.TableParts[i].HeaderRowCount !== 0) { drawCurrentFilterButton(aWs.TableParts[i].AutoFilter, true); } } } return true; }; WorksheetView.prototype.af_checkCursor = function (x, y, offsetX, offsetY, frozenObj, r, c) { var ws = this; var aWs = this.model; var result = false; var t = this; var _checkClickFrozenArea = function (x, y, offsetX, offsetY, frozenObj) { var frosenPosX = frozenObj && frozenObj.cFrozen != undefined && ws.cols[frozenObj.cFrozen] ? ws.cols[frozenObj.cFrozen].left : null; var frosenPosY = frozenObj && frozenObj.rFrozen != undefined && ws.rows[frozenObj.rFrozen] ? ws.rows[frozenObj.rFrozen].top : null; var result; if (frosenPosX != null && frosenPosY != null && x < frosenPosX && y < frosenPosY) { result = {x: x, y: y}; } else if (frosenPosX != null && x < frosenPosX) { result = {x: x, y: y + offsetY}; } else if (frosenPosY != null && y < frosenPosY) { result = {x: x + offsetX, y: y}; } else { result = {x: x + offsetX, y: y + offsetY}; } return result; }; var _isShowButtonInFilter = function (col, filter) { var result = true; var autoFilter = filter.isAutoFilter() ? filter : filter.AutoFilter; if (filter.HeaderRowCount === 0) { result = false; } else if (autoFilter && autoFilter.FilterColumns)//проверяем скрытые ячейки { var colId = col - autoFilter.Ref.c1; for (var i = 0; i < autoFilter.FilterColumns.length; i++) { if (autoFilter.FilterColumns[i].ColId === colId) { if (autoFilter.FilterColumns[i].ShowButton === false) { result = false; } break; } } } else if (!filter.isAutoFilter() && autoFilter === null)//если форматированная таблица и отсутсвует а/ф { result = false; } return result; }; var checkCurrentFilter = function (filter, num) { var range = new Asc.Range(filter.Ref.c1, filter.Ref.r1, filter.Ref.c2, filter.Ref.r1); if (range.contains(c.col, r.row) && _isShowButtonInFilter(c.col, filter)) { var row = range.r1; for (var col = range.c1; col <= range.c2; col++) { if (col === c.col) { var width = 13; var height = 13; var rowHeight = ws.rows[row].height; if (rowHeight < height) { width = width * (rowHeight / height); height = rowHeight; } var x1 = ws.cols[col].left + ws.cols[col].width - width - 0.5; var y1 = ws.rows[row].top + ws.rows[row].height - height - 0.5; var x2 = ws.cols[col].left + ws.cols[col].width - 0.5; var y2 = ws.rows[row].top + ws.rows[row].height - 0.5; if (x >= x1 && x <= x2 && y >= y1 && y <= y2) { result = { id: {id: num, colId: col - range.c1}, target: c_oTargetType.FilterObject, col: -1, row: -1 }; return; } } } } }; var checkFrozenArea = _checkClickFrozenArea(x, y, offsetX, offsetY, frozenObj); if (checkFrozenArea) { x = checkFrozenArea.x; y = checkFrozenArea.y; } if (aWs.AutoFilter && aWs.AutoFilter.Ref) { checkCurrentFilter(aWs.AutoFilter, null); } if (aWs.TableParts && aWs.TableParts.length && !result) { for (var i = 0; i < aWs.TableParts.length; i++) { if (aWs.TableParts[i].AutoFilter) { checkCurrentFilter(aWs.TableParts[i], i); } } } return result; }; WorksheetView.prototype._checkAddAutoFilter = function (activeRange, styleName, addFormatTableOptionsObj, filterByCellContextMenu) { //write error, if not add autoFilter and return false var result = true; var worksheet = this.model; var filter = worksheet.AutoFilter; if (filter && styleName && filter.Ref.isIntersect(activeRange) && !(filter.Ref.containsRange(activeRange) && (activeRange.isOneCell() || (filter.Ref.isEqual(activeRange))) || (filter.Ref.r1 === activeRange.r1 && activeRange.containsRange(filter.Ref)))) { worksheet.workbook.handlers.trigger("asc_onError", c_oAscError.ID.AutoFilterDataRangeError, c_oAscError.Level.NoCritical); result = false; } else if (!styleName && worksheet.autoFilters._isEmptyRange(activeRange, 1))//add filter to empty range { worksheet.workbook.handlers.trigger("asc_onError", c_oAscError.ID.AutoFilterDataRangeError, c_oAscError.Level.NoCritical); result = false; } else if (!styleName && filterByCellContextMenu && false === worksheet.autoFilters._getAdjacentCellsAF(activeRange, this).isIntersect(activeRange))//add filter to empty range { worksheet.workbook.handlers.trigger("asc_onError", c_oAscError.ID.AutoFilterDataRangeError, c_oAscError.Level.NoCritical); result = false; }else if (styleName && addFormatTableOptionsObj && addFormatTableOptionsObj.isTitle === false && worksheet.autoFilters._isEmptyCellsUnderRange(activeRange) == false && worksheet.autoFilters._isPartTablePartsUnderRange(activeRange))//add format table without title if down another format table { worksheet.workbook.handlers.trigger("asc_onError", c_oAscError.ID.AutoFilterChangeFormatTableError, c_oAscError.Level.NoCritical); result = false; } return result; }; WorksheetView.prototype.af_getSizeButton = function (c, r) { var ws = this; var result = null; var isCellContainsAutoFilterButton = function (col, row) { var aWs = ws.model; if (aWs.TableParts) { var tablePart; for (var i = 0; i < aWs.TableParts.length; i++) { tablePart = aWs.TableParts[i]; //TODO добавить проверку на isHidden у кнопки if (tablePart.Ref.contains(col, row) && tablePart.Ref.r1 === row) { return true; } } } //TODO добавить проверку на isHidden у кнопки if (aWs.AutoFilter && aWs.AutoFilter.Ref.contains(col, row) && aWs.AutoFilter.Ref.r1 === row) { return true; } return false; }; if (isCellContainsAutoFilterButton(c, r)) { var height = 11; var width = 11; var rowHeight = ws.rows[r].height; var index = 1; if (rowHeight < height) { index = rowHeight / height; width = width * index; height = rowHeight; } result = {width: width, height: height}; } return result; }; WorksheetView.prototype.af_setDialogProp = function (filterProp, isReturnProps) { var ws = this.model; //get filter var filter, autoFilter, displayName = null; if (filterProp.id === null) { autoFilter = ws.AutoFilter; filter = ws.AutoFilter; } else { autoFilter = ws.TableParts[filterProp.id].AutoFilter; filter = ws.TableParts[filterProp.id]; displayName = filter.DisplayName; } //get values var colId = filterProp.colId; var openAndClosedValues = ws.autoFilters._getOpenAndClosedValues(filter, colId); var values = openAndClosedValues.values; var automaticRowCount = openAndClosedValues.automaticRowCount; var filters = ws.autoFilters._getFilterColumn(autoFilter, colId); var rangeButton = Asc.Range(autoFilter.Ref.c1 + colId, autoFilter.Ref.r1, autoFilter.Ref.c1 + colId, autoFilter.Ref.r1); var cellId = ws.autoFilters._rangeToId(rangeButton); var cellCoord = this.getCellCoord(autoFilter.Ref.c1 + colId, autoFilter.Ref.r1); //get filter object var filterObj = new Asc.AutoFilterObj(); if (filters && filters.ColorFilter) { filterObj.type = c_oAscAutoFilterTypes.ColorFilter; filterObj.filter = filters.ColorFilter.clone(); } else if (filters && filters.CustomFiltersObj && filters.CustomFiltersObj.CustomFilters) { filterObj.type = c_oAscAutoFilterTypes.CustomFilters; filterObj.filter = filters.CustomFiltersObj; } else if (filters && filters.DynamicFilter) { filterObj.type = c_oAscAutoFilterTypes.DynamicFilter; filterObj.filter = filters.DynamicFilter.clone(); } else if (filters && filters.Top10) { filterObj.type = c_oAscAutoFilterTypes.Top10; filterObj.filter = filters.Top10.clone(); } else if (filters) { filterObj.type = c_oAscAutoFilterTypes.Filters; } else { filterObj.type = c_oAscAutoFilterTypes.None; } //get sort var sortVal = null; var sortColor = null; if (filter && filter.SortState && filter.SortState.SortConditions && filter.SortState.SortConditions[0]) { var SortConditions = filter.SortState.SortConditions[0]; if (rangeButton.r1 == SortConditions.Ref.r1 && rangeButton.c1 == SortConditions.Ref.c1) { var conditionSortBy = SortConditions.ConditionSortBy; switch (conditionSortBy) { case Asc.ESortBy.sortbyCellColor: { sortVal = Asc.c_oAscSortOptions.ByColorFill; sortColor = SortConditions.dxf && SortConditions.dxf.fill ? SortConditions.dxf.fill.bg : null; break; } case Asc.ESortBy.sortbyFontColor: { sortVal = Asc.c_oAscSortOptions.ByColorFont; sortColor = SortConditions.dxf && SortConditions.dxf.font ? SortConditions.dxf.font.getColor() : null; break; } default: { if (filter.SortState.SortConditions[0].ConditionDescending == false) { sortVal = Asc.c_oAscSortOptions.Descending; } else { sortVal = Asc.c_oAscSortOptions.Ascending; } break; } } } } var ascColor = null; if (null !== sortColor) { ascColor = new Asc.asc_CColor(); ascColor.asc_putR(sortColor.getR()); ascColor.asc_putG(sortColor.getG()); ascColor.asc_putB(sortColor.getB()); ascColor.asc_putA(sortColor.getA()); } //set menu object var autoFilterObject = new Asc.AutoFiltersOptions(); autoFilterObject.asc_setSortState(sortVal); autoFilterObject.asc_setCellCoord(cellCoord); autoFilterObject.asc_setCellId(cellId); autoFilterObject.asc_setValues(values); autoFilterObject.asc_setFilterObj(filterObj); autoFilterObject.asc_setAutomaticRowCount(automaticRowCount); autoFilterObject.asc_setDiplayName(displayName); autoFilterObject.asc_setSortColor(ascColor); var columnRange = Asc.Range(rangeButton.c1, autoFilter.Ref.r1 + 1, rangeButton.c1, autoFilter.Ref.r2); var filterTypes = this.af_getFilterTypes(columnRange); autoFilterObject.asc_setIsTextFilter(filterTypes.text); autoFilterObject.asc_setColorsFill(filterTypes.colors); autoFilterObject.asc_setColorsFont(filterTypes.fontColors); if (isReturnProps) { return autoFilterObject; } else { this.handlers.trigger("setAutoFiltersDialog", autoFilterObject); } }; WorksheetView.prototype.af_getFilterTypes = function (columnRange) { var t = this; var ws = this.model; var res = {text: true, colors: [], fontColors: []}; var alreadyAddColors = {}, alreadyAddFontColors = {}; var getAscColor = function (color) { var ascColor = new Asc.asc_CColor(); ascColor.asc_putR(color.getR()); ascColor.asc_putG(color.getG()); ascColor.asc_putB(color.getB()); ascColor.asc_putA(color.getA()); return ascColor; }; var addFontColorsToArray = function (fontColor) { var rgb = null === fontColor || fontColor && 0 === fontColor.rgb ? null : fontColor.rgb; var isDefaultFontColor = !!(null === rgb); if (true !== alreadyAddFontColors[rgb]) { if (isDefaultFontColor) { res.fontColors.push(null); alreadyAddFontColors[null] = true; } else { var ascFontColor = getAscColor(fontColor); res.fontColors.push(ascFontColor); alreadyAddFontColors[rgb] = true; } } }; var addCellColorsToArray = function (color) { var rgb = null !== color && color.fill && color.fill.bg ? color.fill.bg.rgb : null; var isDefaultCellColor = !!(null === rgb); if (true !== alreadyAddColors[rgb]) { if (isDefaultCellColor) { res.colors.push(null); alreadyAddColors[null] = true; } else { var ascColor = getAscColor(color.fill.bg); res.colors.push(ascColor); alreadyAddColors[rgb] = true; } } }; var tempText = 0, tempDigit = 0; for (var i = columnRange.r1; i <= columnRange.r2; i++) { var cell = ws._getCellNoEmpty(i, columnRange.c1); //добавляем без цвета ячейку if (!cell) { if (true !== alreadyAddColors[null]) { alreadyAddColors[null] = true; res.colors.push(null); } continue; } if (false === cell.isEmptyText()) { var type = cell.getType(); if (type === 0) { tempDigit++; } else { tempText++; } } //font colors if (null !== cell.oValue.multiText) { for (var j = 0; j < cell.oValue.multiText.length; j++) { var fontColor = cell.oValue.multiText[j].format ? cell.oValue.multiText[j].format.getColor() : null; addFontColorsToArray(fontColor); } } else { var fontColor = cell.xfs && cell.xfs.font ? cell.xfs.font.getColor() : null; addFontColorsToArray(fontColor); } //cell colors addCellColorsToArray(cell.getStyle()); } //если один элемент в массиве, не отправляем его в меню if (res.colors.length === 1) { res.colors = []; } if (res.fontColors.length === 1) { res.fontColors = []; } res.text = tempDigit > tempText ? false : true; return res; }; WorksheetView.prototype.af_changeSelectionTablePart = function (activeRange) { var t = this; var tableParts = t.model.TableParts; var _changeSelectionToAllTablePart = function () { var tablePart; for (var i = 0; i < tableParts.length; i++) { tablePart = tableParts[i]; if (tablePart.Ref.intersection(activeRange)) { if (t.model.autoFilters._activeRangeContainsTablePart(activeRange, tablePart.Ref)) { var newActiveRange = new Asc.Range(tablePart.Ref.c1, tablePart.Ref.r1, tablePart.Ref.c2, tablePart.Ref.r2); t.setSelection(newActiveRange); } break; } } }; var _changeSelectionFromCellToColumn = function () { if (tableParts && tableParts.length && activeRange.isOneCell()) { for (var i = 0; i < tableParts.length; i++) { if (tableParts[i].HeaderRowCount !== 0 && tableParts[i].Ref.containsRange(activeRange) && tableParts[i].Ref.r1 === activeRange.r1) { var newActiveRange = new Asc.Range(activeRange.c1, activeRange.r1, activeRange.c1, tableParts[i].Ref.r2); if (!activeRange.isEqual(newActiveRange)) { t.setSelection(newActiveRange); } break; } } } }; if (activeRange.isOneCell()) { _changeSelectionFromCellToColumn(activeRange); } else { _changeSelectionToAllTablePart(activeRange); } }; WorksheetView.prototype.af_isCheckMoveRange = function (arnFrom, arnTo) { var ws = this.model; var tableParts = ws.TableParts; var tablePart; var checkMoveRangeIntoApplyAutoFilter = function (arnTo) { if (ws.AutoFilter && ws.AutoFilter.Ref && arnTo.intersection(ws.AutoFilter.Ref)) { //если затрагиваем скрытые строки а/ф - выдаём ошибку if (ws.autoFilters._searchHiddenRowsByFilter(ws.AutoFilter, arnTo)) { return false; } } return true; }; //1) если выделена часть форматированной таблицы и ещё часть(либо полностью) var counterIntersection = 0; var counterContains = 0; for (var i = 0; i < tableParts.length; i++) { tablePart = tableParts[i]; if (tablePart.Ref.intersection(arnFrom)) { if (arnFrom.containsRange(tablePart.Ref)) { counterContains++; } else { counterIntersection++; } } } if ((counterIntersection > 0 && counterContains > 0) || (counterIntersection > 1)) { ws.workbook.handlers.trigger("asc_onError", c_oAscError.ID.AutoFilterDataRangeError, c_oAscError.Level.NoCritical); return false; } //2)если затрагиваем перемещаемым диапазоном часть а/ф со скрытыми строчками if (!checkMoveRangeIntoApplyAutoFilter(arnTo)) { ws.workbook.handlers.trigger("asc_onError", c_oAscError.ID.AutoFilterMoveToHiddenRangeError, c_oAscError.Level.NoCritical); return false; } return true; }; WorksheetView.prototype.af_changeSelectionFormatTable = function (tableName, optionType) { var t = this; var ws = this.model; var tablePart = ws.autoFilters._getFilterByDisplayName(tableName); if (!tablePart || (tablePart && !tablePart.Ref)) { return false; } var refTablePart = tablePart.Ref; var lastSelection = this.model.selectionRange.getLast(); var startCol = lastSelection.c1; var endCol = lastSelection.c2; var startRow = lastSelection.r1; var endRow = lastSelection.r2; switch (optionType) { case c_oAscChangeSelectionFormatTable.all: { startCol = refTablePart.c1; endCol = refTablePart.c2; startRow = refTablePart.r1; endRow = refTablePart.r2; break; } case c_oAscChangeSelectionFormatTable.data: { //TODO проверить есть ли строка заголовков startCol = refTablePart.c1; endCol = refTablePart.c2; startRow = refTablePart.r1 + 1; endRow = refTablePart.r2; break; } case c_oAscChangeSelectionFormatTable.row: { startCol = refTablePart.c1; endCol = refTablePart.c2; startRow = lastSelection.r1 < refTablePart.r1 ? refTablePart.r1 : lastSelection.r1; endRow = lastSelection.r2 > refTablePart.r2 ? refTablePart.r2 : lastSelection.r2; break; } case c_oAscChangeSelectionFormatTable.column: { startCol = lastSelection.c1 < refTablePart.c1 ? refTablePart.c1 : lastSelection.c1; endCol = lastSelection.c2 > refTablePart.c2 ? refTablePart.c2 : lastSelection.c2; startRow = refTablePart.r1; endRow = refTablePart.r2; break; } } t.setSelection(new Asc.Range(startCol, startRow, endCol, endRow)); }; WorksheetView.prototype.af_changeFormatTableInfo = function (tableName, optionType, val) { var tablePart = this.model.autoFilters._getFilterByDisplayName(tableName); var t = this; var ar = this.model.selectionRange.getLast(); if (!tablePart || (tablePart && !tablePart.TableStyleInfo)) { return false; } var isChangeTableInfo = this.af_checkChangeTableInfo(tablePart, optionType); if (isChangeTableInfo !== false) { var callback = function (isSuccess) { if (false === isSuccess) { t.handlers.trigger("selectionChanged"); return; } History.Create_NewPoint(); History.StartTransaction(); var newTableRef = t.model.autoFilters.changeFormatTableInfo(tableName, optionType, val); if (newTableRef.r1 > ar.r1 || newTableRef.r2 < ar.r2) { var startRow = newTableRef.r1 > ar.r1 ? newTableRef.r1 : ar.r1; var endRow = newTableRef.r2 < ar.r2 ? newTableRef.r2 : ar.r2; var newActiveRange = new Asc.Range(ar.c1, startRow, ar.c2, endRow); t.setSelection(newActiveRange); History.SetSelectionRedo(newActiveRange); } t._onUpdateFormatTable(isChangeTableInfo, false, true); History.EndTransaction(); }; var lockRange = t.af_getRangeForChangeTableInfo(tablePart, optionType, val); if (lockRange) { t._isLockedCells(lockRange, null, callback); } else { callback(); } } }; WorksheetView.prototype.af_checkChangeTableInfo = function (tablePart, optionType) { var res = tablePart.Ref; var ws = this.model, rangeUpTable; if (optionType === c_oAscChangeTableStyleInfo.rowHeader && tablePart.HeaderRowCount !== null) { //add header row rangeUpTable = new Asc.Range(tablePart.Ref.c1, tablePart.Ref.r1 - 1, tablePart.Ref.c2, tablePart.Ref.r1 - 1); } else if (optionType === c_oAscChangeTableStyleInfo.rowTotal && tablePart.TotalsRowCount === null) { //add total row rangeUpTable = new Asc.Range(tablePart.Ref.c1, tablePart.Ref.r2 + 1, tablePart.Ref.c2, tablePart.Ref.r2 + 1); //add total table if down another format table if(ws.autoFilters._isPartTablePartsUnderRange(tablePart.Ref)){ ws.workbook.handlers.trigger("asc_onError", c_oAscError.ID.AutoFilterChangeFormatTableError, c_oAscError.Level.NoCritical); return false; } } if (rangeUpTable && this.model.autoFilters._isEmptyRange(rangeUpTable, 0) && this.model.autoFilters._isPartTablePartsUnderRange(tablePart.Ref) === true) { ws.workbook.handlers.trigger("asc_onError", c_oAscError.ID.AutoFilterMoveToHiddenRangeError, c_oAscError.Level.NoCritical); res = false; } return res; }; WorksheetView.prototype.af_getRangeForChangeTableInfo = function (tablePart, optionType, val) { var res = null; switch (optionType) { case c_oAscChangeTableStyleInfo.columnBanded: case c_oAscChangeTableStyleInfo.columnFirst: case c_oAscChangeTableStyleInfo.columnLast: case c_oAscChangeTableStyleInfo.rowBanded: case c_oAscChangeTableStyleInfo.filterButton: { res = tablePart.Ref; break; } case c_oAscChangeTableStyleInfo.rowTotal: { if (val === false) { res = tablePart.Ref; } else { var rangeUpTable = new Asc.Range(tablePart.Ref.c1, tablePart.Ref.r2 + 1, tablePart.Ref.c2, tablePart.Ref.r2 + 1); if(this.model.autoFilters._isEmptyRange(rangeUpTable, 0) && this.model.autoFilters.searchRangeInTableParts(rangeUpTable) === -1){ res = new Asc.Range(tablePart.Ref.c1, tablePart.Ref.r1, tablePart.Ref.c2, tablePart.Ref.r2 + 1); } else{ res = new Asc.Range(tablePart.Ref.c1, tablePart.Ref.r2 + 1, tablePart.Ref.c2, gc_nMaxRow0); } } break; } case c_oAscChangeTableStyleInfo.rowHeader: { if (val === false) { res = tablePart.Ref; } else { var rangeUpTable = new Asc.Range(tablePart.Ref.c1, tablePart.Ref.r1 - 1, tablePart.Ref.c2, tablePart.Ref.r1 - 1); if(this.model.autoFilters._isEmptyRange(rangeUpTable, 0) && this.model.autoFilters.searchRangeInTableParts(rangeUpTable) === -1){ res = new Asc.Range(tablePart.Ref.c1, tablePart.Ref.r1 - 1, tablePart.Ref.c2, tablePart.Ref.r2); } else{ res = new Asc.Range(tablePart.Ref.c1, tablePart.Ref.r1 - 1, tablePart.Ref.c2, gc_nMaxRow0); } } break; } } return res; }; WorksheetView.prototype.af_insertCellsInTable = function (tableName, optionType) { var t = this; var ws = this.model; var tablePart = ws.autoFilters._getFilterByDisplayName(tableName); if (!tablePart || (tablePart && !tablePart.Ref)) { return false; } var insertCellsAndShiftDownRight = function (arn, displayName, type) { var range = t.model.getRange3(arn.r1, arn.c1, arn.r2, arn.c2); var isCheckChangeAutoFilter = t.af_checkInsDelCells(arn, type, "insCell", true); if (isCheckChangeAutoFilter === false) { return; } var callback = function (isSuccess) { if (false === isSuccess) { return; } History.Create_NewPoint(); History.StartTransaction(); var shiftCells = type === c_oAscInsertOptions.InsertCellsAndShiftRight ? range.addCellsShiftRight(displayName) : range.addCellsShiftBottom(displayName); if (shiftCells) { t.cellCommentator.updateCommentsDependencies(true, type, arn); t.objectRender.updateDrawingObject(true, type, arn); t._onUpdateFormatTable(range, false, true); } History.EndTransaction(); }; var changedRange = new asc_Range(tablePart.Ref.c1, tablePart.Ref.r1, tablePart.Ref.c2, tablePart.Ref.r2); t._isLockedCells(changedRange, null, callback); }; var newActiveRange = this.model.selectionRange.getLast().clone(); var displayName = null; var type = null; switch (optionType) { case c_oAscInsertOptions.InsertTableRowAbove: { newActiveRange.c1 = tablePart.Ref.c1; newActiveRange.c2 = tablePart.Ref.c2; type = c_oAscInsertOptions.InsertCellsAndShiftDown; break; } case c_oAscInsertOptions.InsertTableRowBelow: { newActiveRange.c1 = tablePart.Ref.c1; newActiveRange.c2 = tablePart.Ref.c2; newActiveRange.r1 = tablePart.Ref.r2 + 1; newActiveRange.r2 = tablePart.Ref.r2 + 1; displayName = tableName; type = c_oAscInsertOptions.InsertCellsAndShiftDown; break; } case c_oAscInsertOptions.InsertTableColLeft: { newActiveRange.r1 = tablePart.Ref.r1; newActiveRange.r2 = tablePart.Ref.r2; type = c_oAscInsertOptions.InsertCellsAndShiftRight; break; } case c_oAscInsertOptions.InsertTableColRight: { newActiveRange.c1 = tablePart.Ref.c2 + 1; newActiveRange.c2 = tablePart.Ref.c2 + 1; newActiveRange.r1 = tablePart.Ref.r1; newActiveRange.r2 = tablePart.Ref.r2; displayName = tableName; type = c_oAscInsertOptions.InsertCellsAndShiftRight; break; } } insertCellsAndShiftDownRight(newActiveRange, displayName, type) }; WorksheetView.prototype.af_deleteCellsInTable = function (tableName, optionType) { var t = this; var ws = this.model; var tablePart = ws.autoFilters._getFilterByDisplayName(tableName); if (!tablePart || (tablePart && !tablePart.Ref)) { return false; } var deleteCellsAndShiftLeftTop = function (arn, type) { var isCheckChangeAutoFilter = t.af_checkInsDelCells(arn, type, "delCell", true); if (isCheckChangeAutoFilter === false) { return; } var callback = function (isSuccess) { if (false === isSuccess) { return; } History.Create_NewPoint(); History.StartTransaction(); if (isCheckChangeAutoFilter === true) { t.model.autoFilters.isEmptyAutoFilters(arn, type); } var preDeleteAction = function () { t.cellCommentator.updateCommentsDependencies(false, type, arn); }; var res; var range; if (type === c_oAscInsertOptions.InsertCellsAndShiftRight) { range = t.model.getRange3(arn.r1, arn.c1, arn.r2, arn.c2); res = range.deleteCellsShiftLeft(preDeleteAction); } else { arn = t.model.autoFilters.checkDeleteAllRowsFormatTable(arn, true); range = t.model.getRange3(arn.r1, arn.c1, arn.r2, arn.c2); res = range.deleteCellsShiftUp(preDeleteAction); } if (res) { t.objectRender.updateDrawingObject(true, type, arn); t._onUpdateFormatTable(range, false, true); } History.EndTransaction(); }; var changedRange = new asc_Range(tablePart.Ref.c1, tablePart.Ref.r1, tablePart.Ref.c2, tablePart.Ref.r2); t._isLockedCells(changedRange, null, callback); }; var deleteTableCallback = function (ref) { var callback = function (isSuccess) { if (false === isSuccess) { return; } History.Create_NewPoint(); History.StartTransaction(); t.model.autoFilters.isEmptyAutoFilters(ref); var cleanRange = t.model.getRange3(ref.r1, ref.c1, ref.r2, ref.c2); cleanRange.cleanAll(); t.cellCommentator.deleteCommentsRange(cleanRange.bbox); t._onUpdateFormatTable(ref, false, true); History.EndTransaction(); }; t._isLockedCells(ref, null, callback); }; var newActiveRange = this.model.selectionRange.getLast().clone(); var val = null; switch (optionType) { case c_oAscDeleteOptions.DeleteColumns: { newActiveRange.r1 = tablePart.Ref.r1; newActiveRange.r2 = tablePart.Ref.r2; val = c_oAscDeleteOptions.DeleteCellsAndShiftLeft; break; } case c_oAscDeleteOptions.DeleteRows: { newActiveRange.c1 = tablePart.Ref.c1; newActiveRange.c2 = tablePart.Ref.c2; val = c_oAscDeleteOptions.DeleteCellsAndShiftTop; break; } case c_oAscDeleteOptions.DeleteTable: { deleteTableCallback(tablePart.Ref.clone()); break; } } if (val !== null) { deleteCellsAndShiftLeftTop(newActiveRange, val); } }; WorksheetView.prototype.af_changeDisplayNameTable = function (tableName, newName) { this.model.autoFilters.changeDisplayNameTable(tableName, newName); }; WorksheetView.prototype.af_checkInsDelCells = function (activeRange, val, prop, isFromFormatTable) { var ws = this.model; var res = true; var intersectionTableParts = ws.autoFilters.getTableIntersectionRange(activeRange); var isPartTablePartsUnderRange = ws.autoFilters._isPartTablePartsUnderRange(activeRange); var isPartTablePartsRightRange = ws.autoFilters.isPartTablePartsRightRange(activeRange); var isOneTableIntersection = intersectionTableParts && intersectionTableParts.length === 1 ? intersectionTableParts[0] : null; var checkInsCells = function () { switch (val) { case c_oAscInsertOptions.InsertCellsAndShiftDown: { if (isFromFormatTable) { //если внизу находится часть форматированной таблицы или это часть форматированной таблицы if (isPartTablePartsUnderRange) { res = false; } else if (isOneTableIntersection !== null && !(isOneTableIntersection.Ref.c1 === activeRange.c1 && isOneTableIntersection.Ref.c2 === activeRange.c2)) { res = false; } } else { if (isPartTablePartsUnderRange) { res = false; } else if (intersectionTableParts && null !== isOneTableIntersection) { res = false; } else if (isOneTableIntersection && !isOneTableIntersection.Ref.isEqual(activeRange)) { res = false; } } break; } case c_oAscInsertOptions.InsertCellsAndShiftRight: { //если справа находится часть форматированной таблицы или это часть форматированной таблицы if (isFromFormatTable) { if (isPartTablePartsRightRange) { res = false; } } else { if (isPartTablePartsRightRange) { res = false; } else if (intersectionTableParts && null !== isOneTableIntersection) { res = false; } else if (isOneTableIntersection && !isOneTableIntersection.Ref.isEqual(activeRange)) { res = false; } } break; } case c_oAscInsertOptions.InsertColumns: { break; } case c_oAscInsertOptions.InsertRows: { break; } } }; var checkDelCells = function () { switch (val) { case c_oAscDeleteOptions.DeleteCellsAndShiftTop: { if (isFromFormatTable) { if (isPartTablePartsUnderRange) { res = false; } } else { if (isPartTablePartsUnderRange) { res = false; } else if (!isOneTableIntersection && null !== isOneTableIntersection) { res = false; } else if (isOneTableIntersection && !isOneTableIntersection.Ref.isEqual(activeRange)) { res = false; } } break; } case c_oAscDeleteOptions.DeleteCellsAndShiftLeft: { if (isFromFormatTable) { if (isPartTablePartsRightRange) { res = false; } } else { if (isPartTablePartsRightRange) { res = false; } else if (!isOneTableIntersection && null !== isOneTableIntersection) { res = false; } else if (isOneTableIntersection && !isOneTableIntersection.Ref.isEqual(activeRange)) { res = false; } } break; } case c_oAscDeleteOptions.DeleteColumns: { break; } case c_oAscDeleteOptions.DeleteRows: { break; } } }; prop === "insCell" ? checkInsCells() : checkDelCells(); if (res === false) { ws.workbook.handlers.trigger("asc_onError", c_oAscError.ID.AutoFilterChangeFormatTableError, c_oAscError.Level.NoCritical); } return res; }; WorksheetView.prototype.af_setDisableProps = function (tablePart, formatTableInfo) { var selectionRange = this.model.selectionRange; var lastRange = selectionRange.getLast(); var activeCell = selectionRange.activeCell; if (!tablePart) { return false; } var refTable = tablePart.Ref; var refTableContainsActiveRange = selectionRange.isSingleRange() && refTable.containsRange(lastRange); //если курсор стоит в нижней строке, то разрешаем добавление нижней строки formatTableInfo.isInsertRowBelow = (refTableContainsActiveRange && ((tablePart.TotalsRowCount === null && activeCell.row === refTable.r2) || (tablePart.TotalsRowCount !== null && activeCell.row === refTable.r2 - 1))); //если курсор стоит в правом столбце, то разрешаем добавление одного столбца правее formatTableInfo.isInsertColumnRight = (refTableContainsActiveRange && activeCell.col === refTable.c2); //если внутри находится вся активная область или если выходит активная область за границу справа formatTableInfo.isInsertColumnLeft = refTableContainsActiveRange; //если внутри находится вся активная область(кроме строки заголовков) или если выходит активная область за границу снизу formatTableInfo.isInsertRowAbove = (refTableContainsActiveRange && ((lastRange.r1 > refTable.r1 && tablePart.HeaderRowCount === null) || (lastRange.r1 >= refTable.r1 && tablePart.HeaderRowCount !== null))); formatTableInfo.isDeleteRow = refTableContainsActiveRange && !(lastRange.r1 <= refTable.r1 && lastRange.r2 >= refTable.r1 && null === tablePart.HeaderRowCount); formatTableInfo.isDeleteColumn = true; formatTableInfo.isDeleteTable = true; }; WorksheetView.prototype.af_changeTableRange = function (tableName, range) { var t = this; range = AscCommonExcel.g_oRangeCache.getAscRange(range); var callback = function (isSuccess) { if (false === isSuccess) { return; } History.Create_NewPoint(); History.StartTransaction(); t.model.autoFilters.changeTableRange(tableName, range); t._onUpdateFormatTable(range, false, true); //TODO добавить перерисовку таблицы и перерисовку шаблонов History.EndTransaction(); }; //TODO возможно не стоит лочить весь диапазон. проверить: когда один ползователь меняет диапазон, другой снимает а/ф с ф/т. в этом случае в deleteAutoFilter передавать не range а имя ф/т var table = t.model.autoFilters._getFilterByDisplayName(tableName); var tableRange = null !== table ? table.Ref : null; var lockRange = range; if (null !== tableRange) { var r1 = tableRange.r1 < range.r1 ? tableRange.r1 : range.r1; var r2 = tableRange.r2 > range.r2 ? tableRange.r2 : range.r2; var c1 = tableRange.c1 < range.c1 ? tableRange.c1 : range.c1; var c2 = tableRange.c2 > range.c2 ? tableRange.c2 : range.c2; lockRange = new Asc.Range(c1, r1, c2, r2); } var callBackLockedDefNames = function (isSuccess) { if (false === isSuccess) { return; } t._isLockedCells(lockRange, null, callback); }; //лочим данный именованный диапазон при смене размера ф/т var defNameId = t.model.workbook.dependencyFormulas.getDefNameByName(tableName, t.model.getId()); defNameId = defNameId ? defNameId.getNodeId() : null; t._isLockedDefNames(callBackLockedDefNames, defNameId); }; WorksheetView.prototype.af_checkChangeRange = function (range) { var res = null; var intersectionTables = this.model.autoFilters.getTableIntersectionRange(range); if (0 < intersectionTables.length) { var tablePart = intersectionTables[0]; if (range.isOneCell()) { res = c_oAscError.ID.FTChangeTableRangeError } else if (range.r1 !== tablePart.Ref.r1)//первая строка таблицы не равна первой строке выделенного диапазона { res = c_oAscError.ID.FTChangeTableRangeError; } else if (intersectionTables.length !== 1)//выделено несколько таблиц { res = c_oAscError.ID.FTRangeIncludedOtherTables; } else if (this.model.AutoFilter && this.model.AutoFilter.Ref && this.model.AutoFilter.Ref.isIntersect(range)) { res = c_oAscError.ID.FTChangeTableRangeError; } } else { res = c_oAscError.ID.FTChangeTableRangeError; } return res; }; // Convert coordinates methods WorksheetView.prototype.ConvertXYToLogic = function (x, y) { x *= asc_getcvt(0/*px*/, 1/*pt*/, this._getPPIX()); y *= asc_getcvt(0/*px*/, 1/*pt*/, this._getPPIY()); var c = this.visibleRange.c1, cFrozen, widthDiff; var r = this.visibleRange.r1, rFrozen, heightDiff; 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; } rFrozen = this.topLeftFrozenCell.getRow0(); heightDiff = this.rows[rFrozen].top - this.rows[0].top; if (y < this.cellsTop + heightDiff && 0 !== heightDiff) { r = 0; } } x += this.cols[c].left - this.cellsLeft - this.cellsLeft; y += this.rows[r].top - this.cellsTop - this.cellsTop; x *= asc_getcvt(1/*pt*/, 3/*mm*/, this._getPPIX()); y *= asc_getcvt(1/*pt*/, 3/*mm*/, this._getPPIY()); return {X: x, Y: y}; }; WorksheetView.prototype.ConvertLogicToXY = function (xL, yL) { xL *= asc_getcvt(3/*mm*/, 1/*pt*/, this._getPPIX()); yL *= asc_getcvt(3/*mm*/, 1/*pt*/, this._getPPIY()); var c = this.visibleRange.c1, cFrozen, widthDiff; var r = this.visibleRange.r1, rFrozen, heightDiff; if (this.topLeftFrozenCell) { cFrozen = this.topLeftFrozenCell.getCol0(); widthDiff = this.cols[cFrozen].left - this.cols[0].left; if (xL < this.cellsLeft + widthDiff && 0 !== widthDiff) { c = 0; } rFrozen = this.topLeftFrozenCell.getRow0(); heightDiff = this.rows[rFrozen].top - this.rows[0].top; if (yL < this.cellsTop + heightDiff && 0 !== heightDiff) { r = 0; } } xL -= (this.cols[c].left - this.cellsLeft - this.cellsLeft); yL -= (this.rows[r].top - this.cellsTop - this.cellsTop); xL *= asc_getcvt(1/*pt*/, 0/*px*/, this._getPPIX()); yL *= asc_getcvt(1/*pt*/, 0/*px*/, this._getPPIY()); return {X: xL, Y: yL}; }; //------------------------------------------------------------export--------------------------------------------------- window['AscCommonExcel'] = window['AscCommonExcel'] || {}; window["AscCommonExcel"].WorksheetView = WorksheetView; })(window);