diff --git a/apps/common/main/lib/component/ComboBorderSize.js b/apps/common/main/lib/component/ComboBorderSize.js index a22123a92..8f01b7071 100644 --- a/apps/common/main/lib/component/ComboBorderSize.js +++ b/apps/common/main/lib/component/ComboBorderSize.js @@ -268,4 +268,108 @@ define([ formcontrol.css('background-position', '0 -' + record.get('offsety') + 'px'); } }, Common.UI.ComboBorderType || {})); + + Common.UI.ComboBoxColor = Common.UI.ComboBox.extend(_.extend({ + template: _.template([ + '
', + '
', + '
', + '
', + '', + '', + '
' + ].join('')), + + itemClicked: function (e) { + var el = $(e.currentTarget).parent(); + + this._selectedItem = this.store.findWhere({ + id: el.attr('id') + }); + if (this._selectedItem) { + $('.selected', $(this.el)).removeClass('selected'); + el.addClass('selected'); + this.updateFormControl(this._selectedItem); + + this.trigger('selected', this, _.extend({}, this._selectedItem.toJSON()), e); + e.preventDefault(); + } + }, + + updateFormControl: function(record) { + var formcontrol = $(this.el).find('.form-control > div'); + + if (record.get('value')!=-1) { + formcontrol[0].innerHTML = ''; + formcontrol.css({'background': '#' + record.get('value'), 'margin-top': '0'}); + } else { + formcontrol[0].innerHTML = record.get('displayValue'); + formcontrol.css({'background': '', 'margin-top': '1px'}); + } + }, + + setValue: function(value) { + var obj; + this._selectedItem = this.store.findWhere((obj={}, obj[this.valueField]=value, obj)); + + $('.selected', $(this.el)).removeClass('selected'); + + if (this._selectedItem) { + this.updateFormControl(this._selectedItem); + $('#' + this._selectedItem.get('id'), $(this.el)).addClass('selected'); + } else { + var formcontrol = $(this.el).find('.form-control > div'); + formcontrol[0].innerHTML = ''; + formcontrol.css('background', ''); + } + }, + + onResetItems: function() { + if (this.itemsTemplate) { + $(this.el).find('ul').html( $(this.itemsTemplate({ + items: this.store.toJSON(), + scope: this + }))); + } else { + $(this.el).find('ul').html(_.template([ + '<% _.each(items, function(item) { %>', + '<% if (item.value==-1) { %>', + '
  • <%= scope.getDisplayValue(item) %>
  • ', + '<% } else { %>', + '
  • ', + '
    ', + '
  • ', + '<% } %>', + '<% }); %>' + ].join(''))({ + items: this.store.toJSON(), + scope: this + })); + } + + if (!_.isUndefined(this.scroller)) { + this.scroller.destroy(); + delete this.scroller; + } + this.scroller = new Common.UI.Scroller(_.extend({ + el: $('.dropdown-menu', this.cmpEl), + minScrollbarLength : 40, + includePadding : true, + wheelSpeed: 10, + alwaysVisibleY: this.scrollAlwaysVisible + }, this.options.scroller)); + } + + }, Common.UI.ComboBoxColor || {})); + }); \ No newline at end of file diff --git a/apps/common/main/lib/component/ComboBox.js b/apps/common/main/lib/component/ComboBox.js index a243b9895..bd4229d95 100644 --- a/apps/common/main/lib/component/ComboBox.js +++ b/apps/common/main/lib/component/ComboBox.js @@ -217,6 +217,11 @@ define([ }); } + var $list = el.find('.dropdown-menu'); + if ($list.hasClass('menu-absolute')) { + $list.css('min-width', el.outerWidth()); + } + el.on('show.bs.dropdown', _.bind(me.onBeforeShowMenu, me)); el.on('shown.bs.dropdown', _.bind(me.onAfterShowMenu, me)); el.on('hide.bs.dropdown', _.bind(me.onBeforeHideMenu, me)); @@ -292,6 +297,12 @@ define([ tip.hide(); } } + + var $list = this.cmpEl.find('ul'); + if ($list.hasClass('menu-absolute')) { + var offset = this.cmpEl.offset(); + $list.css({left: offset.left, top: offset.top + this.cmpEl.outerHeight() + 2}); + } }, onAfterShowMenu: function(e) { diff --git a/apps/common/main/lib/component/ListView.js b/apps/common/main/lib/component/ListView.js index e1a565af6..080d6e15f 100644 --- a/apps/common/main/lib/component/ListView.js +++ b/apps/common/main/lib/component/ListView.js @@ -67,11 +67,15 @@ define([ this.trigger('items:reset', this); }, - onAddItem: function(record, store, opts) { - var view = new Common.UI.DataViewItem({ + createNewItem: function(record) { + return new Common.UI.DataViewItem({ template: this.itemTemplate, model: record }); + }, + + onAddItem: function(record, store, opts) { + var view = this.createNewItem(record); if (!this.innerEl) this.innerEl = $(this.el).find('.inner'); diff --git a/apps/common/main/lib/component/RadioBox.js b/apps/common/main/lib/component/RadioBox.js index 9e8e2d159..b14082b51 100644 --- a/apps/common/main/lib/component/RadioBox.js +++ b/apps/common/main/lib/component/RadioBox.js @@ -111,6 +111,7 @@ define([ return; if (disabled !== this.disabled) { + this.$label.toggleClass('disabled', disabled); this.$radio.toggleClass('disabled', disabled); (disabled) ? this.$radio.attr({disabled: disabled}) : this.$radio.removeAttr('disabled'); } diff --git a/apps/common/main/resources/less/combobox.less b/apps/common/main/resources/less/combobox.less index 9b87f75da..e05416470 100644 --- a/apps/common/main/resources/less/combobox.less +++ b/apps/common/main/resources/less/combobox.less @@ -106,4 +106,8 @@ padding-top: 0; padding-bottom: 0; } + + .dropdown-menu.menu-absolute { + position: fixed; + } } diff --git a/apps/spreadsheeteditor/main/app/controller/DataTab.js b/apps/spreadsheeteditor/main/app/controller/DataTab.js index fbf16ba4a..1c236b871 100644 --- a/apps/spreadsheeteditor/main/app/controller/DataTab.js +++ b/apps/spreadsheeteditor/main/app/controller/DataTab.js @@ -42,7 +42,8 @@ define([ 'core', 'spreadsheeteditor/main/app/view/DataTab', - 'spreadsheeteditor/main/app/view/GroupDialog' + 'spreadsheeteditor/main/app/view/GroupDialog', + 'spreadsheeteditor/main/app/view/SortDialog' ], function () { 'use strict'; @@ -86,7 +87,8 @@ define([ 'data:tocolumns': this.onTextToColumn, 'data:show': this.onShowClick, 'data:hide': this.onHideClick, - 'data:groupsettings': this.onGroupSettings + 'data:groupsettings': this.onGroupSettings, + 'data:sortcustom': this.onCustomSort }, 'Statusbar': { 'sheet:changed': this.onApiSheetChanged @@ -212,9 +214,55 @@ define([ this.api.asc_changeGroupDetails(false); }, + onCustomSort: function() { + var me = this; + if (this.api) { + var res = this.api.asc_sortCellsRangeExpand(); + if (res) { + var config = { + width: 500, + title: this.toolbar.txtSorting, + msg: this.toolbar.txtExpandSort, + + buttons: [ {caption: this.toolbar.txtExpand, primary: true, value: 'expand'}, + {caption: this.toolbar.txtSortSelected, primary: true, value: 'sort'}, + 'cancel'], + callback: function(btn){ + if (btn == 'expand' || btn == 'sort') { + setTimeout(function(){ + me.showCustomSort(btn == 'expand'); + },1); + } + } + }; + Common.UI.alert(config); + } else + me.showCustomSort(res !== null); + } + }, + + showCustomSort: function(expand) { + var me = this, + props = me.api.asc_getSortProps(expand); + // props = new Asc.CSortProperties(); + if (props) { + (new SSE.Views.SortDialog({ + props: props, + api: me.api, + handler: function (result, settings) { + if (result == 'ok') { + if (me && me.api) { + me.api.asc_setSortProps(settings); + } + } + } + })).show(); + } + }, + onWorksheetLocked: function(index,locked) { if (index == this.api.asc_getActiveWorksheetIndex()) { - Common.Utils.lockControls(SSE.enumLock.sheetLock, locked, {array: [this.view.btnGroup, this.view.btnUngroup]}); + Common.Utils.lockControls(SSE.enumLock.sheetLock, locked, {array: this.view.btnsSortDown.concat(this.view.btnsSortUp, this.view.btnCustomSort, this.view.btnGroup, this.view.btnUngroup)}); } }, diff --git a/apps/spreadsheeteditor/main/app/controller/Main.js b/apps/spreadsheeteditor/main/app/controller/Main.js index a57a8786c..33c36f870 100644 --- a/apps/spreadsheeteditor/main/app/controller/Main.js +++ b/apps/spreadsheeteditor/main/app/controller/Main.js @@ -119,7 +119,9 @@ define([ 'Date': this.txtDate, 'Time': this.txtTime, 'Tab': this.txtTab, - 'File': this.txtFile + 'File': this.txtFile, + 'Column': this.txtColumn, + 'Row': this.txtRow }; styleNames.forEach(function(item){ translate[item] = me['txtStyle_' + item.replace(/ /g, '_')] || item; @@ -2459,6 +2461,8 @@ define([ txtTab: 'Tab', txtFile: 'File', errorFileSizeExceed: 'The file size exceeds the limitation set for your server.
    Please contact your Document Server administrator for details.', + txtColumn: 'Column', + txtRow: 'Row', errorUpdateVersionOnDisconnect: 'Internet connection has been restored, and the file version has been changed.
    Before you can continue working, you need to download the file or copy its content to make sure nothing is lost, and then reload this page.' } })(), SSE.Controllers.Main || {})) diff --git a/apps/spreadsheeteditor/main/app/controller/Toolbar.js b/apps/spreadsheeteditor/main/app/controller/Toolbar.js index b47ba53ea..0d2bf2bcd 100644 --- a/apps/spreadsheeteditor/main/app/controller/Toolbar.js +++ b/apps/spreadsheeteditor/main/app/controller/Toolbar.js @@ -2263,7 +2263,7 @@ define([ } need_disable = this._state.controlsdisabled.filters || (val===null); toolbar.lockToolbar(SSE.enumLock.ruleFilter, need_disable, - { array: toolbar.btnsSetAutofilter.concat(toolbar.btnsSortDown, toolbar.btnsSortUp, toolbar.btnTableTemplate, toolbar.btnInsertTable) }); + { array: toolbar.btnsSetAutofilter.concat(toolbar.btnsSortDown, toolbar.btnsSortUp, toolbar.btnCustomSort, toolbar.btnTableTemplate, toolbar.btnInsertTable) }); val = (formatTableInfo) ? formatTableInfo.asc_getTableStyleName() : null; if (this._state.tablestylename !== val && this.toolbar.mnuTableTemplatePicker) { @@ -2293,10 +2293,10 @@ define([ toolbar.lockToolbar(SSE.enumLock.multiselect, this._state.multiselect, { array: [toolbar.btnTableTemplate, toolbar.btnInsertHyperlink, toolbar.btnInsertTable]}); this._state.inpivot = !!info.asc_getPivotTableInfo(); - toolbar.lockToolbar(SSE.enumLock.editPivot, this._state.inpivot, { array: toolbar.btnsSetAutofilter.concat(toolbar.btnsClearAutofilter, toolbar.btnsSortDown, toolbar.btnsSortUp, toolbar.btnMerge, toolbar.btnInsertHyperlink, toolbar.btnInsertTable)}); + toolbar.lockToolbar(SSE.enumLock.editPivot, this._state.inpivot, { array: toolbar.btnsSetAutofilter.concat(toolbar.btnsClearAutofilter, toolbar.btnsSortDown, toolbar.btnsSortUp, toolbar.btnCustomSort, toolbar.btnMerge, toolbar.btnInsertHyperlink, toolbar.btnInsertTable)}); need_disable = !this.appConfig.canModifyFilter; - toolbar.lockToolbar(SSE.enumLock.cantModifyFilter, need_disable, { array: toolbar.btnsSetAutofilter.concat(toolbar.btnsSortDown, toolbar.btnsSortUp, toolbar.btnTableTemplate, toolbar.btnClearStyle.menu.items[0], toolbar.btnClearStyle.menu.items[2], + toolbar.lockToolbar(SSE.enumLock.cantModifyFilter, need_disable, { array: toolbar.btnsSetAutofilter.concat(toolbar.btnsSortDown, toolbar.btnsSortUp, toolbar.btnCustomSort, toolbar.btnTableTemplate, toolbar.btnClearStyle.menu.items[0], toolbar.btnClearStyle.menu.items[2], toolbar.btnInsertTable)}); } @@ -3163,6 +3163,7 @@ define([ me.toolbar.btnsSortUp = datatab.getButtons('sort-up'); me.toolbar.btnsSetAutofilter = datatab.getButtons('set-filter'); me.toolbar.btnsClearAutofilter = datatab.getButtons('clear-filter'); + me.toolbar.btnCustomSort = datatab.getButtons('sort-custom'); var formulatab = me.getApplication().getController('FormulaDialog'); formulatab.setConfig({toolbar: me}); diff --git a/apps/spreadsheeteditor/main/app/template/SortDialog.template b/apps/spreadsheeteditor/main/app/template/SortDialog.template new file mode 100644 index 000000000..d4f8168b9 --- /dev/null +++ b/apps/spreadsheeteditor/main/app/template/SortDialog.template @@ -0,0 +1,28 @@ +
    +
    + + + + + + + + + + +
    + + + +
    +
    + +
    + + + +
    +
    +
    +
    +
    \ No newline at end of file diff --git a/apps/spreadsheeteditor/main/app/template/Toolbar.template b/apps/spreadsheeteditor/main/app/template/Toolbar.template index 89327cd50..052149916 100644 --- a/apps/spreadsheeteditor/main/app/template/Toolbar.template +++ b/apps/spreadsheeteditor/main/app/template/Toolbar.template @@ -186,14 +186,17 @@
    - - + +
    - - + +
    +
    + +
    diff --git a/apps/spreadsheeteditor/main/app/view/CellRangeDialog.js b/apps/spreadsheeteditor/main/app/view/CellRangeDialog.js index 3e140366b..19c89d13b 100644 --- a/apps/spreadsheeteditor/main/app/view/CellRangeDialog.js +++ b/apps/spreadsheeteditor/main/app/view/CellRangeDialog.js @@ -155,7 +155,8 @@ define([ if (this.inputRange.checkValidate() !== true) return; } - this.options.handler.call(this, this, state); + if (this.options.handler.call(this, this, state)) + return; } this.close(); diff --git a/apps/spreadsheeteditor/main/app/view/DataTab.js b/apps/spreadsheeteditor/main/app/view/DataTab.js index d40b527ae..d1ab7788d 100644 --- a/apps/spreadsheeteditor/main/app/view/DataTab.js +++ b/apps/spreadsheeteditor/main/app/view/DataTab.js @@ -93,6 +93,9 @@ define([ me.fireEvent('data:clearfilter', [Asc.c_oAscSortOptions.Descending]); }); }); + me.btnCustomSort.on('click', function (b, e) { + me.fireEvent('data:sortcustom'); + }); } return { @@ -163,11 +166,21 @@ define([ // Common.Utils.injectComponent($host.find('#slot-btn-hide-details'), this.btnHide); // this.lockedControls.push(this.btnHide); + this.btnCustomSort = new Common.UI.Button({ + cls: 'btn-toolbar x-huge icon-top', + iconCls: 'toolbar__icon btn-to-columns', + caption: this.capBtnTextCustomSort, + disabled: true, + lock: [_set.editCell, _set.selChart, _set.selChartText, _set.selShape, _set.selShapeText, _set.selImage, _set.lostConnect, _set.coAuth, _set.ruleFilter, _set.editPivot, _set.cantModifyFilter, _set.sheetLock] + }); + Common.Utils.injectComponent($host.find('#slot-btn-custom-sort'), this.btnCustomSort); + this.lockedControls.push(this.btnCustomSort); + this.btnsSortDown = Common.Utils.injectButtons($host.find('.slot-sortdesc'), '', 'toolbar__icon btn-sort-down', '', - [_set.editCell, _set.selChart, _set.selChartText, _set.selShape, _set.selShapeText, _set.selImage, _set.lostConnect, _set.coAuth, _set.ruleFilter, _set.editPivot, _set.cantModifyFilter]); + [_set.editCell, _set.selChart, _set.selChartText, _set.selShape, _set.selShapeText, _set.selImage, _set.lostConnect, _set.coAuth, _set.ruleFilter, _set.editPivot, _set.cantModifyFilter, _set.sheetLock]); this.btnsSortUp = Common.Utils.injectButtons($host.find('.slot-sortasc'), '', 'toolbar__icon btn-sort-up', '', - [_set.editCell, _set.selChart, _set.selChartText, _set.selShape, _set.selShapeText, _set.selImage, _set.lostConnect, _set.coAuth, _set.ruleFilter, _set.editPivot, _set.cantModifyFilter]); + [_set.editCell, _set.selChart, _set.selChartText, _set.selShape, _set.selShapeText, _set.selImage, _set.lostConnect, _set.coAuth, _set.ruleFilter, _set.editPivot, _set.cantModifyFilter, _set.sheetLock]); this.btnsSetAutofilter = Common.Utils.injectButtons($host.find('.slot-btn-setfilter'), '', 'toolbar__icon btn-autofilter', '', [_set.editCell, _set.selChart, _set.selChartText, _set.selShape, _set.selShapeText, _set.selImage, _set.lostConnect, _set.coAuth, _set.ruleFilter, _set.editPivot, _set.cantModifyFilter], @@ -226,6 +239,7 @@ define([ me.btnsClearAutofilter.forEach( function(btn) { btn.updateHint(me.toolbar.txtClearFilter); }); + me.btnCustomSort.updateHint(me.tipCustomSort); setEvents.call(me); }); @@ -241,6 +255,8 @@ define([ return this.btnsSortDown; else if (type == 'sort-up') return this.btnsSortUp; + else if (type == 'sort-custom') + return this.btnCustomSort; else if (type == 'set-filter') return this.btnsSetAutofilter; else if (type == 'clear-filter') @@ -272,7 +288,9 @@ define([ capBtnTextShow: 'Show details', capBtnTextHide: 'Hide details', textBelow: 'Summary rows below detail', - textRightOf: 'Summary columns to right of detail' + textRightOf: 'Summary columns to right of detail', + capBtnTextCustomSort: 'Custom Sort', + tipCustomSort: 'Custom sort' } }()), SSE.Views.DataTab || {})); }); diff --git a/apps/spreadsheeteditor/main/app/view/SortDialog.js b/apps/spreadsheeteditor/main/app/view/SortDialog.js new file mode 100644 index 000000000..a16184738 --- /dev/null +++ b/apps/spreadsheeteditor/main/app/view/SortDialog.js @@ -0,0 +1,680 @@ +/* + * + * (c) Copyright Ascensio System SIA 2010-2019 + * + * 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 20A-12 Ernesta Birznieka-Upisha + * street, Riga, Latvia, EU, LV-1050. + * + * 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 + * +*/ +/** + * + * SortDialog.js + * + * Created by Julia.Radzhabova on 05.10.19 + * Copyright (c) 2019 Ascensio System SIA. All rights reserved. + * + */ + +define([ 'text!spreadsheeteditor/main/app/template/SortDialog.template', + 'common/main/lib/view/AdvancedSettingsWindow', + 'common/main/lib/component/ComboBox', + 'common/main/lib/component/ListView', + 'spreadsheeteditor/main/app/view/SortOptionsDialog' +], function (contentTemplate) { + 'use strict'; + + SSE.Views = SSE.Views || {}; + + var _CustomItem = Common.UI.DataViewItem.extend({ + initialize : function(options) { + Common.UI.BaseView.prototype.initialize.call(this, options); + + var me = this; + + me.template = me.options.template || me.template; + + me.listenTo(me.model, 'change:sort', function() { + me.render(); + me.trigger('change', me, me.model); + }); + me.listenTo(me.model, 'change:selected', function() { + var el = me.$el || $(me.el); + el.toggleClass('selected', me.model.get('selected') && me.model.get('allowSelected')); + me.onSelectChange(); + }); + me.listenTo(me.model, 'remove', me.remove); + } + }); + + SSE.Views.SortDialog = Common.Views.AdvancedSettingsWindow.extend(_.extend({ + + options: { + alias: 'SortDialog', + contentWidth: 500, + height: 294, + buttons: ['ok', 'cancel'] + }, + + initialize: function (options) { + var me = this; + _.extend(this.options, { + title: this.txtTitle, + template: [ + '
    ', + '
    ' + _.template(contentTemplate)({scope: this}) + '
    ', + '
    ' + ].join('') + }, options); + + this.api = options.api; + this.handler = options.handler; + this.props = options.props; + this.levels = []; + + this.sortOptions = {}; + + this.options.handler = function(result, value) { + if ( result != 'ok' || this.isListValid() ) { + if (options.handler) + options.handler.call(this, result, value); + return; + } + return true; + }; + + Common.Views.AdvancedSettingsWindow.prototype.initialize.call(this, this.options); + }, + render: function () { + Common.Views.AdvancedSettingsWindow.prototype.render.call(this); + var me = this; + + this.sortList = new Common.UI.ListView({ + el: $('#sort-dialog-list', this.$window), + store: new Common.UI.DataViewStore(), + emptyText: '', + enableKeyEvents: false, + template: _.template(['
    '].join('')), + itemTemplate: _.template([ + '
    ', + '
    ', + '
    ', + '<% if (sort==Asc.c_oAscSortOptions.ByColorFill || sort==Asc.c_oAscSortOptions.ByColorFont) { %>', + '
    ', + '
    ', + '<% } else { %>', + '
    ', + '<% } %>', + '
    ' + ].join('')) + }); + this.sortList.createNewItem = function(record) { + return new _CustomItem({ + template: this.itemTemplate, + model: record + }); + }; + this.sortList.on('item:select', _.bind(this.onSelectLevel, this)) + .on('item:keydown', _.bind(this.onKeyDown, this)); + + this.btnAdd = new Common.UI.Button({ + el: $('#sort-dialog-btn-add') + }); + this.btnAdd.on('click', _.bind(this.onAddLevel, this, false)); + + this.btnDelete = new Common.UI.Button({ + el: $('#sort-dialog-btn-delete') + }); + this.btnDelete.on('click', _.bind(this.onDeleteLevel, this)); + + this.btnCopy = new Common.UI.Button({ + el: $('#sort-dialog-btn-copy') + }); + this.btnCopy.on('click', _.bind(this.onCopyLevel, this, false)); + + this.btnOptions = new Common.UI.Button({ + el: $('#sort-dialog-btn-options') + }); + this.btnOptions.on('click', _.bind(this.onOptions, this, false)); + + this.btnUp = new Common.UI.Button({ + cls: 'btn-toolbar', + iconCls: 'btn-incfont', + hint: this.textUp + }); + this.btnUp.render($('#sort-dialog-btn-up')) ; + this.btnUp.on('click', _.bind(this.onMoveClick, this, true)); + + this.btnDown = new Common.UI.Button({ + cls: 'btn-toolbar', + iconCls: 'btn-decfont', + hint: this.textDown + }); + this.btnDown.render($('#sort-dialog-btn-down')) ; + this.btnDown.on('click', _.bind(this.onMoveClick, this, false)); + + this.lblColumn = $('#sort-dialog-label-column'); + + this.afterRender(); + }, + + afterRender: function() { + this._setDefaults(this.props); + }, + + _setDefaults: function (props) { + if (props) { + this.sortOptions = { + headers: props.asc_getHasHeaders(), + // sensitive: props.asc_getCaseSensitive(), + sortcol: props.asc_getColumnSort(), + lockHeaders: !!props.asc_getLockChangeHeaders(), + lockOrientation: !!props.asc_getLockChangeOrientation() + }; + + this.lblColumn.text(props.asc_getColumnSort() ? this.textColumn : this.textRow); + + // get name from props + this.fillSortValues(); + + this.sort_data = [ + { value: Asc.c_oAscSortOptions.ByValue, displayValue: this.textValues }, + { value: Asc.c_oAscSortOptions.ByColorFill, displayValue: this.textCellColor }, + { value: Asc.c_oAscSortOptions.ByColorFont, displayValue: this.textFontColor } + ]; + this.order_data = [ + { value: Asc.c_oAscSortOptions.Ascending, displayValue: this.textAZ }, + { value: Asc.c_oAscSortOptions.Descending, displayValue: this.textZA } + ]; + + this.sortList.on('item:add', _.bind(this.addControls, this)); + this.sortList.on('item:change', _.bind(this.addControls, this)); + this.refreshList(props.asc_getLevels()); + } + }, + + refreshList: function(levels) { + this.levels = []; + + var arr = []; + if (levels) { + for (var i=0; i0) && this.sortList.selectByIndex(0); + + this.updateButtons(); + }, + + addControls: function(listView, itemView, item) { + if (!item) return; + + var me = this, + i = item.get('levelIndex'), + cmpEl = this.sortList.cmpEl.find('#sort-dialog-item-' + i); + if (!this.levels[i]) + this.levels[i] = { + order_data: this.order_data + }; + var level = this.levels[i]; + var combo = new Common.UI.ComboBox({ + el : cmpEl.find('#sort-dialog-cmb-col-' + i), + editable : false, + cls : 'input-group-nr', + menuCls : 'menu-absolute', + menuStyle : 'max-height: 135px;', + data : this.column_data + }).on('selected', function(combo, record) { + if (record.value==-1) { + var index = item.get('columnIndex'); + combo.setValue(index!==null ? index : ''); + me.onSelectOther(combo, item); + } else { + item.set('columnIndex', record.value); + level.levelProps = me.props.asc_getLevelProps(record.value); + me.updateOrderList(i, item); + } + }); + var val = item.get('columnIndex'); + (val!==null) && combo.setValue(item.get('columnIndex')); + level.cmbColumn = combo; + + combo = new Common.UI.ComboBox({ + el : cmpEl.find('#sort-dialog-cmb-sort-' + i), + editable : false, + cls : 'input-group-nr', + menuCls : 'menu-absolute', + data : this.sort_data + }).on('selected', function(combo, record) { + item.set('sort', record.value); + me.updateOrderList(i, item); + }); + val = item.get('sort'); + (val!==null) && combo.setValue(val); + level.cmbSort = combo; + + var sort = item.get('sort'); + if (sort==Asc.c_oAscSortOptions.ByColorFill || sort==Asc.c_oAscSortOptions.ByColorFont) { + combo = new Common.UI.ComboBoxColor({ + el : cmpEl.find('#sort-dialog-btn-color-' + i), + editable : false, + menuCls : 'menu-absolute', + menuStyle : 'max-height: 135px;', + data : level.color_data + }).on('selected', function(combo, record) { + item.set('color', record.color); + }); + val = item.get('color'); + combo.setValue(val ? Common.Utils.ThemeColor.getHexColor(val.get_r(), val.get_g(), val.get_b()).toLocaleUpperCase() : -1); + var rec = combo.getSelectedRecord(); + rec && item.set('color', rec.color); + level.cmbColor = combo; + } + + combo = new Common.UI.ComboBox({ + el : cmpEl.find('#sort-dialog-cmb-order-' + i), + editable : false, + cls : 'input-group-nr', + menuCls : 'menu-absolute', + data : level.order_data + }).on('selected', function(combo, record) { + item.set('order', record.value); + }); + val = item.get('order'); + (val!==null) && combo.setValue(val); + level.cmbOrder = combo; + + cmpEl.on('mousedown', '.combobox', function(){ + me.sortList.selectRecord(item); + }); + }, + + onOptions: function () { + var me = this; + + var win = new SSE.Views.SortOptionsDialog({ + props: me.sortOptions, + handler : function(result, settings) { + if (result == 'ok' && settings) { + me.lblColumn.text(settings.sortcol ? me.textColumn : me.textRow); + me.props.asc_setHasHeaders(settings.headers); + // me.props.asc_setCaseSensitive(settings.sensitive); + me.props.asc_setColumnSort(settings.sortcol); + me.props.asc_updateSortList(me.sortOptions.sortcol == settings.sortcol); + me.sortOptions = settings; + me.updateSortValues(); + } + } + }); + win.show(); + }, + + fillSortValues: function() { + var values = this.props.asc_getSortList(), + len = values.length; + this.column_data = []; + for (var i=0; i=500) + this.column_data.push({ value: -1, displayValue: this.sortOptions.sortcol ? this.textMoreCols : this.textMoreRows }); + }, + + updateSortValues: function() { + this.fillSortValues(); + var me = this; + this.sortList.store.each(function(item) { + var columnIndex = (item.get('sort')==Asc.c_oAscSortOptions.ByValue) ? null : 0, + levelIndex = item.get('levelIndex'); + item.set('columnIndex', columnIndex, {silent: true} ); + item.set('order', Asc.c_oAscSortOptions.Ascending, {silent: true} ); + item.set('color', null, {silent: true} ); + me.levels[levelIndex].levelProps = (columnIndex!==null) ? me.props.asc_getLevelProps(columnIndex) : undefined; + me.addControls(null, null, item); + me.updateOrderList(levelIndex, item); + }); + }, + + onAddLevel: function() { + var store = this.sortList.store, + rec = this.sortList.getSelectedRec(); + rec = store.add({ + columnIndex: null, + levelIndex: this.levels.length, + sort: Asc.c_oAscSortOptions.ByValue, + order: Asc.c_oAscSortOptions.Ascending + }, {at: rec ? store.indexOf(rec)+1 : store.length}); + if (rec) { + this.sortList.selectRecord(rec); + this.sortList.scrollToRecord(rec); + } + this.updateButtons(); + }, + + onCopyLevel: function() { + var store = this.sortList.store, + rec = this.sortList.getSelectedRec(), + levelIndex = this.levels.length, + copyLevel = this.levels[rec ? rec.get('levelIndex') : null]; + + this.levels[levelIndex] = { + levelProps: copyLevel ? copyLevel.levelProps : null, + order_data: copyLevel ? copyLevel.order_data : null, + color_data: copyLevel ? copyLevel.color_data : null + }; + rec = store.add({ + levelIndex: levelIndex, + columnIndex: rec ? rec.get('columnIndex') : null, + sort: rec ? rec.get('sort') : Asc.c_oAscSortOptions.ByValue, + order: rec ? rec.get('order') : Asc.c_oAscSortOptions.Ascending, + color: rec ? rec.get('color') : null + }, {at: rec ? store.indexOf(rec)+1 : store.length}); + if (rec) { + this.sortList.selectRecord(rec); + this.sortList.scrollToRecord(rec); + } + this.updateButtons(); + }, + + onDeleteLevel: function() { + var store = this.sortList.store, + rec = this.sortList.getSelectedRec(); + if (rec) { + var index = rec.get('levelIndex'); + this.levels[index] = undefined; + index = store.indexOf(rec); + store.remove(rec); + (store.length>0) && this.sortList.selectByIndex(index0) && level.cmbColor.setValue(level.color_data[0].value); + + storeItem && storeItem.set('color', null); + } + }, + + getSettings: function() { + var props = new Asc.CSortProperties(); + props.asc_setHasHeaders(this.sortOptions.headers); + // props.asc_setCaseSensitive(this.sortOptions.sensitive); + props.asc_setColumnSort(this.sortOptions.sortcol); + + var me = this, + arr = []; + this.sortList.store.each(function(item) { + var columnIndex = item.get('columnIndex'), + levelProp = me.levels[item.get('levelIndex')]; + if (columnIndex!==null && levelProp) { + var level = new Asc.CSortPropertiesLevel(); + level.asc_setIndex(columnIndex); + level.asc_setSortBy(levelProp.cmbSort.getValue()); + level.asc_setDescending(levelProp.cmbOrder.getValue()); + if (levelProp.cmbSort.getValue() == Asc.c_oAscSortOptions.ByColorFill || levelProp.cmbSort.getValue()==Asc.c_oAscSortOptions.ByColorFont) { + var rec = levelProp.cmbColor.getSelectedRecord(); + if (rec) { + level.asc_setColor(rec.color); + } + } + arr.push(level); + } + }); + props.asc_setLevels(arr); + return props; + }, + + isListValid: function() { + var rec = this.sortList.store.findWhere({columnIndex: null}); + if (rec) + Common.UI.warning({msg: this.errorEmpty}); + else { + var store = this.sortList.store, + len = store.length; + for (var index=0; index=0; i--) { + var itemcheck = store.at(i), + levelcheck = this.levels[itemcheck.get('levelIndex')]; + if (item.get('columnIndex') == itemcheck.get('columnIndex') && sort == levelcheck.cmbSort.getValue()) { + if (sort == Asc.c_oAscSortOptions.ByColorFill || sort==Asc.c_oAscSortOptions.ByColorFont) { + var colorcheck = levelcheck.cmbColor.getSelectedRecord(); + if (color && colorcheck && color.value == colorcheck.value) { + rec = levelProp.cmbColumn.getSelectedRecord().displayValue; + rec = this.errorSameColumnColor.replace('%1', rec); + break; + } + } else { + rec = levelProp.cmbColumn.getSelectedRecord().displayValue; + rec = this.errorSameColumnValue.replace('%1', rec); + break; + } + } + } + if (rec) + break; + } + rec && Common.UI.warning({msg: rec}); + } + return !rec; + }, + + close: function () { + Common.Views.AdvancedSettingsWindow.prototype.close.call(this); + }, + + onKeyDown: function (lisvView, record, e) { + if (e.keyCode==Common.UI.Keys.DELETE && !this.btnDelete.isDisabled()) + this.onDeleteLevel(); + }, + + updateButtons: function() { + this.btnAdd.setDisabled(this.sortList.store.length>63); + this.btnCopy.setDisabled(this.sortList.store.length<1); + this.btnDelete.setDisabled(this.sortList.store.length<1); + this.updateMoveButtons(); + this.sortList.scroller && this.sortList.scroller.update(); + }, + + updateMoveButtons: function() { + var rec = this.sortList.getSelectedRec(), + index = rec ? this.sortList.store.indexOf(rec) : -1; + this.btnUp.setDisabled(index<1); + this.btnDown.setDisabled(index<0 || index==this.sortList.store.length-1); + }, + + onSelectOther: function(combo, item) { + var me = this; + if (me.api) { + var handlerDlg = function(dlg, result) { + if (result == 'ok') { + var range = dlg.getSettings(); + var isvalid; + if (!_.isEmpty(range)) { + isvalid = me.api.asc_checkDataRange(Asc.c_oAscSelectionDialogType.CustomSort, range, true, !me.sortOptions.sortcol); + } + + if (isvalid == Asc.c_oAscError.ID.No) { + var index = me.props.asc_addBySortList(range); + me.fillSortValues(); + combo.setData(me.column_data); + combo.setValue(index); + item.set('columnIndex', index); + me.levels[item.get('levelIndex')].levelProps = me.props.asc_getLevelProps(index); + me.updateOrderList(item.get('levelIndex'), item); + return false; + } else if (isvalid == Asc.c_oAscError.ID.CustomSortMoreOneSelectedError) + Common.UI.warning({msg: me.sortOptions.sortcol ? me.errorMoreOneCol: me.errorMoreOneRow}); + else if (isvalid == Asc.c_oAscError.ID.CustomSortNotOriginalSelectError) + Common.UI.warning({msg: me.sortOptions.sortcol ? me.errorNotOriginalCol : me.errorNotOriginalRow}); + else + Common.UI.warning({msg: me.txtInvalidRange}); + return true; + } + }; + + var win = new SSE.Views.CellRangeDialog({ + handler: handlerDlg + }).on('close', function() { + me.show(); + }); + + var xy = me.$window.offset(); + me.hide(); + win.show(xy.left + 65, xy.top + 77); + win.setSettings({ + api : me.api, + range : me.props.asc_getRangeStr(), + type : Asc.c_oAscSelectionDialogType.CustomSort + }); + } + }, + + txtTitle: 'Sort', + textAdd: 'Add level', + textDelete: 'Delete level', + textCopy: 'Copy level', + textColumn: 'Column', + textRow: 'Row', + textSort: 'Sort on', + textOrder: 'Order', + textUp: 'Move level up', + textDown: 'Move level down', + textOptions: 'Options', + textValues: 'Values', + textCellColor: 'Cell color', + textFontColor: 'Font color', + textAZ: 'A to Z', + textZA: 'Z to A', + textDesc: 'Descending', + textAsc: 'Ascending', + textTop: 'Top', + textBelow: 'Below', + textLeft: 'Left', + textRight: 'Right', + errorEmpty: 'All sort criteria must have a column or row specified.', + textAuto: 'Automatic', + textNone: 'None', + errorNotOriginalCol: 'The column you selected is not in the original selected range.', + errorNotOriginalRow: 'The row you selected is not in the original selected range.', + errorMoreOneRow: 'More than one row is selected.', + errorMoreOneCol: 'More than one column is selected.', + txtInvalidRange: 'Invalid cells range.', + textMoreRows: '(More rows...)', + textMoreCols: '(More columns...)', + errorSameColumnValue: "%1 is being sorted by values more than once.
    Delete the duplicate sort criteria and try again.", + errorSameColumnColor: "%1 is being sorted by the same color more than once.
    Delete the duplicate sort criteria and try again." + + }, SSE.Views.SortDialog || {})); +}); \ No newline at end of file diff --git a/apps/spreadsheeteditor/main/app/view/SortOptionsDialog.js b/apps/spreadsheeteditor/main/app/view/SortOptionsDialog.js new file mode 100644 index 000000000..3dae5613c --- /dev/null +++ b/apps/spreadsheeteditor/main/app/view/SortOptionsDialog.js @@ -0,0 +1,158 @@ +/* + * + * (c) Copyright Ascensio System SIA 2010-2019 + * + * 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 20A-12 Ernesta Birznieka-Upisha + * street, Riga, Latvia, EU, LV-1050. + * + * 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 + * + */ + +/** + * SortOptionsDialog.js + * + * Created by Julia Radzhabova on 05.10.2019 + * Copyright (c) 2019 Ascensio System SIA. All rights reserved. + * + */ +define([ + 'common/main/lib/util/utils', + 'common/main/lib/component/CheckBox', + 'common/main/lib/component/RadioBox', + 'common/main/lib/view/AdvancedSettingsWindow' +], function () { 'use strict'; + + SSE.Views.SortOptionsDialog = Common.Views.AdvancedSettingsWindow.extend(_.extend({ + options: { + contentWidth: 230, + height: 200 + }, + + initialize : function(options) { + var me = this; + + _.extend(this.options, { + title: this.textTitle, + template: [ + '
    ', + '
    ', + '
    ', + '', + '', + '', + '', + // '', + // '', + // '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '
    ', + '
    ', + '
    ', + // '
    ', + // '
    ', + '', + '
    ', + '
    ', + '
    ', + '
    ', + '
    ', + '
    ', + '
    ', + '
    ' + ].join('') + }, options); + + this.props = options.props; + + Common.Views.AdvancedSettingsWindow.prototype.initialize.call(this, this.options); + }, + + render: function() { + Common.Views.AdvancedSettingsWindow.prototype.render.call(this); + + this.chHeaders = new Common.UI.CheckBox({ + el: $('#sort-options-chk-headers'), + labelText: this.textHeaders + }); + + // this.chCase = new Common.UI.CheckBox({ + // el: $('#sort-options-chk-case'), + // labelText: this.textCase + // }); + + this.radioTop = new Common.UI.RadioBox({ + el: $('#sort-options-radio-row'), + labelText: this.textTopBottom, + name: 'asc-radio-sort-orient' + }).on('change', _.bind(function(field, newValue, eOpts) { + newValue && this.chHeaders.setDisabled(this.props.lockHeaders); + }, this)); + + this.radioLeft = new Common.UI.RadioBox({ + el: $('#sort-options-radio-col'), + labelText: this.textLeftRight, + name: 'asc-radio-sort-orient' + }).on('change', _.bind(function(field, newValue, eOpts) { + newValue && this.chHeaders.setDisabled(true); + }, this)); + + this.afterRender(); + }, + + afterRender: function() { + this._setDefaults(this.props); + }, + + _setDefaults: function (props) { + if (props) { + this.chHeaders.setValue(props.headers); + // this.chCase.setValue(props.sensitive); + (props.sortcol || props.lockOrientation) ? this.radioTop.setValue(true) : this.radioLeft.setValue(true); + this.radioLeft.setDisabled(props.lockOrientation); + } + }, + + getSettings: function () { + return {headers: this.radioTop.getValue() && (this.chHeaders.getValue()=='checked'), /*sensitive: this.chCase.getValue()=='checked',*/ sortcol: this.radioTop.getValue(), lockHeaders: this.props.lockHeaders, lockOrientation: this.props.lockOrientation}; + }, + + textTitle: 'Sort Options', + textHeaders: 'My data has headers', + textCase: 'Case sensitive', + textOrientation: 'Orientation', + textTopBottom: 'Sort top to bottom', + textLeftRight: 'Sort left to right' + + }, SSE.Views.SortOptionsDialog || {})) +}); diff --git a/apps/spreadsheeteditor/main/locale/en.json b/apps/spreadsheeteditor/main/locale/en.json index b427c8bc5..806bc1532 100644 --- a/apps/spreadsheeteditor/main/locale/en.json +++ b/apps/spreadsheeteditor/main/locale/en.json @@ -771,6 +771,8 @@ "SSE.Controllers.Main.warnNoLicense": "This version of %1 editors has certain limitations for concurrent connections to the document server.
    If you need more please consider purchasing a commercial license.", "SSE.Controllers.Main.warnNoLicenseUsers": "This version of %1 editors has certain limitations for concurrent users.
    If you need more please consider purchasing a commercial license.", "SSE.Controllers.Main.warnProcessRightsChange": "You have been denied the right to edit the file.", + "SSE.Controllers.Main.txtColumn": "Column", + "SSE.Controllers.Main.txtRow": "Row", "SSE.Controllers.Print.strAllSheets": "All Sheets", "SSE.Controllers.Print.textWarning": "Warning", "SSE.Controllers.Print.txtCustom": "Custom", @@ -1366,6 +1368,8 @@ "SSE.Views.DataTab.tipGroup": "Group range of cells", "SSE.Views.DataTab.tipToColumns": "Separate cell text into columns", "SSE.Views.DataTab.tipUngroup": "Ungroup range of cells", + "SSE.Views.DataTab.capBtnTextCustomSort": "Custom Sort", + "SSE.Views.DataTab.tipCustomSort": "Custom sort", "SSE.Views.DigitalFilterDialog.capAnd": "And", "SSE.Views.DigitalFilterDialog.capCondition1": "equals", "SSE.Views.DigitalFilterDialog.capCondition10": "does not end with", @@ -2087,6 +2091,46 @@ "SSE.Views.Spellcheck.txtDictionaryLanguage": "Dictionary Language", "SSE.Views.Spellcheck.txtNextTip": "Go to the next word", "SSE.Views.Spellcheck.txtSpelling": "Spelling", + "SSE.Views.SortDialog.txtTitle": "Sort", + "SSE.Views.SortDialog.textAdd": "Add level", + "SSE.Views.SortDialog.textDelete": "Delete level", + "SSE.Views.SortDialog.textCopy": "Copy level", + "SSE.Views.SortDialog.textColumn": "Column", + "SSE.Views.SortDialog.textRow": "Row", + "SSE.Views.SortDialog.textSort": "Sort on", + "SSE.Views.SortDialog.textOrder": "Order", + "SSE.Views.SortDialog.textUp": "Move level up", + "SSE.Views.SortDialog.textDown": "Move level down", + "SSE.Views.SortDialog.textOptions": "Options", + "SSE.Views.SortDialog.textValues": "Values", + "SSE.Views.SortDialog.textCellColor": "Cell color", + "SSE.Views.SortDialog.textFontColor": "Font color", + "SSE.Views.SortDialog.textAZ": "A to Z", + "SSE.Views.SortDialog.textZA": "Z to A", + "SSE.Views.SortDialog.textDesc": "Descending", + "SSE.Views.SortDialog.textAsc": "Ascending", + "SSE.Views.SortDialog.textTop": "Top", + "SSE.Views.SortDialog.textBelow": "Below", + "SSE.Views.SortDialog.textLeft": "Left", + "SSE.Views.SortDialog.textRight": "Right", + "SSE.Views.SortDialog.errorEmpty": "All sort criteria must have a column or row specified.", + "SSE.Views.SortDialog.textAuto": "Automatic", + "SSE.Views.SortDialog.textNone": "None", + "SSE.Views.SortDialog.errorNotOriginalCol": "The column you selected is not in the original selected range.", + "SSE.Views.SortDialog.errorNotOriginalRow": "The row you selected is not in the original selected range.", + "SSE.Views.SortDialog.errorMoreOneRow": "More than one row is selected.", + "SSE.Views.SortDialog.errorMoreOneCol": "More than one column is selected.", + "SSE.Views.SortDialog.txtInvalidRange": "Invalid cells range.", + "SSE.Views.SortDialog.textMoreRows": "(More rows...)", + "SSE.Views.SortDialog.textMoreCols": "(More columns...)", + "SSE.Views.SortDialog.errorSameColumnValue": "%1 is being sorted by values more than once.
    Delete the duplicate sort criteria and try again.", + "SSE.Views.SortDialog.errorSameColumnColor": "%1 is being sorted by the same color more than once.
    Delete the duplicate sort criteria and try again.", + "SSE.Views.SortOptionsDialog.textTitle": "Sort Options", + "SSE.Views.SortOptionsDialog.textHeaders": "My data has headers", + "SSE.Views.SortOptionsDialog.textCase": "Case sensitive", + "SSE.Views.SortOptionsDialog.textOrientation": "Orientation", + "SSE.Views.SortOptionsDialog.textTopBottom": "Sort top to bottom", + "SSE.Views.SortOptionsDialog.textLeftRight": "Sort left to right", "SSE.Views.Statusbar.CopyDialog.itemCopyToEnd": "(Copy to end)", "SSE.Views.Statusbar.CopyDialog.itemMoveToEnd": "(Move to end)", "SSE.Views.Statusbar.CopyDialog.textCopyBefore": "Copy before sheet", diff --git a/apps/spreadsheeteditor/main/locale/ru.json b/apps/spreadsheeteditor/main/locale/ru.json index a9d76e45f..cf850f125 100644 --- a/apps/spreadsheeteditor/main/locale/ru.json +++ b/apps/spreadsheeteditor/main/locale/ru.json @@ -769,6 +769,8 @@ "SSE.Controllers.Main.warnNoLicense": "Эта версия редакторов %1 имеет некоторые ограничения по количеству одновременных подключений к серверу документов.
    Если требуется больше, рассмотрите вопрос о покупке коммерческой лицензии.", "SSE.Controllers.Main.warnNoLicenseUsers": "Эта версия редакторов %1 имеет некоторые ограничения по числу одновременно работающих пользователей.
    Если требуется больше, рассмотрите вопрос о покупке коммерческой лицензии.", "SSE.Controllers.Main.warnProcessRightsChange": "Вам было отказано в праве на редактирование этого файла.", + "SSE.Controllers.Main.txtColumn": "Столбец", + "SSE.Controllers.Main.txtRow": "Строка", "SSE.Controllers.Print.strAllSheets": "Все листы", "SSE.Controllers.Print.textWarning": "Предупреждение", "SSE.Controllers.Print.txtCustom": "Пользовательская",