diff --git a/apps/common/main/lib/component/ComboBorderSize.js b/apps/common/main/lib/component/ComboBorderSize.js index a22123a92a..8f01b70713 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 a243b98951..bd4229d959 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 e1a565af61..080d6e15f8 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 9e8e2d1597..b14082b517 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 9b87f75da9..e054164700 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 fbf16ba4a4..1c236b8718 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 a57a8786cf..33c36f8700 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 b47ba53ea2..0d2bf2bcd4 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 0000000000..d4f8168b90 --- /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 89327cd50d..0521499162 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 3e140366b8..19c89d13b0 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 d40b527ae7..d1ab7788dc 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 0000000000..a161847384 --- /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 0000000000..3dae5613cf --- /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 b427c8bc58..806bc15321 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 a9d76e45f2..cf850f1250 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": "Пользовательская",