diff --git a/apps/common/main/lib/component/Mixtbar.js b/apps/common/main/lib/component/Mixtbar.js index 7a40660a8d..61d99d021f 100644 --- a/apps/common/main/lib/component/Mixtbar.js +++ b/apps/common/main/lib/component/Mixtbar.js @@ -96,11 +96,13 @@ define([ '' + '' + '' + diff --git a/apps/common/main/lib/component/SynchronizeTip.js b/apps/common/main/lib/component/SynchronizeTip.js index 9ddedf9738..d012a2d977 100644 --- a/apps/common/main/lib/component/SynchronizeTip.js +++ b/apps/common/main/lib/component/SynchronizeTip.js @@ -111,7 +111,9 @@ define([ } else if (this.placement == 'top') this.cmpEl.css({bottom : innerHeight - showxy.top + 'px', right: Common.Utils.innerWidth() - showxy.left - this.target.width()/2 + 'px'}); - else {// left or right + else if (this.placement == 'target') { + this.cmpEl.css({top : (showxy.top+5) + 'px', left: (showxy.left+5) + 'px'}); + } else {// left or right var top = showxy.top + this.target.height()/2, height = this.cmpEl.height(); if (top+height>innerHeight) diff --git a/apps/common/main/lib/component/Tab.js b/apps/common/main/lib/component/Tab.js index 5d61c243a8..e7c644aaa6 100644 --- a/apps/common/main/lib/component/Tab.js +++ b/apps/common/main/lib/component/Tab.js @@ -51,9 +51,15 @@ define([ this.active = false; this.label = 'Tab'; this.cls = ''; + this.iconCls = ''; + this.iconVisible = false; + this.iconTitle = ''; this.index = -1; - this.template = _.template(['
  • ', - '<%- label %>', + this.template = _.template(['
  • ', + '', + '
    ', + '<%- label %>', + '
    ', '
  • '].join('')); this.initialize.call(this, opts); @@ -126,6 +132,16 @@ define([ setCaption: function(text) { this.$el.find('> span').text(text); + }, + + changeIconState: function(visible, title) { + if (this.iconCls.length) { + this.iconVisible = visible; + this.iconTitle = title || ''; + this[visible ? 'addClass' : 'removeClass']('icon-visible'); + if (title) + this.$el.find('.' + this.iconCls).attr('title', title); + } } }); diff --git a/apps/common/main/lib/view/EditNameDialog.js b/apps/common/main/lib/view/EditNameDialog.js new file mode 100644 index 0000000000..d4f6d4f514 --- /dev/null +++ b/apps/common/main/lib/view/EditNameDialog.js @@ -0,0 +1,127 @@ +/* + * + * (c) Copyright Ascensio System SIA 2010-2020 + * + * 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 + * +*/ +/** + * EditNameDialog.js + * + * Created by Julia Radzhabova on 10.07.2020 + * Copyright (c) 2020 Ascensio System SIA. All rights reserved. + * + */ + +define([ + 'common/main/lib/component/Window', + 'common/main/lib/component/InputField' +], function () { 'use strict'; + + Common.Views.EditNameDialog = Common.UI.Window.extend(_.extend({ + options: { + width: 330, + header: false, + cls: 'modal-dlg', + buttons: ['ok', 'cancel'] + }, + + initialize : function(options) { + _.extend(this.options, options || {}); + + this.template = [ + '
    ', + '
    ', + '', + '
    ', + '
    ', + '
    ' + ].join(''); + + this.options.tpl = _.template(this.template)(this.options); + + Common.UI.Window.prototype.initialize.call(this, this.options); + }, + + render: function() { + Common.UI.Window.prototype.render.call(this); + + var me = this; + me.inputLabel = new Common.UI.InputField({ + el : $('#id-dlg-label-caption'), + allowBlank : false, + blankError : me.options.error ? me.options.error : me.textLabelError, + style : 'width: 100%;', + validateOnBlur: false, + validation : function(value) { + return value ? true : ''; + } + }); + me.inputLabel.setValue(this.options.value || '' ); + + var $window = this.getChild(); + $window.find('.btn').on('click', _.bind(this.onBtnClick, this)); + }, + + show: function() { + Common.UI.Window.prototype.show.apply(this, arguments); + + var me = this; + _.delay(function(){ + me.getChild('input').focus(); + },50); + }, + + onPrimary: function(event) { + this._handleInput('ok'); + return false; + }, + + onBtnClick: function(event) { + this._handleInput(event.currentTarget.attributes['result'].value); + }, + + _handleInput: function(state) { + if (this.options.handler) { + if (state == 'ok') { + if (this.inputLabel.checkValidate() !== true) { + this.inputLabel.cmpEl.find('input').focus(); + return; + } + } + + this.options.handler.call(this, state, this.inputLabel.getValue()); + } + + this.close(); + }, + + textLabel: 'Label:', + textLabelError: 'Label must not be empty.' + }, Common.Views.EditNameDialog || {})); +}); \ No newline at end of file diff --git a/apps/common/main/lib/view/Header.js b/apps/common/main/lib/view/Header.js index 4f00bebb7b..013d52043d 100644 --- a/apps/common/main/lib/view/Header.js +++ b/apps/common/main/lib/view/Header.js @@ -664,9 +664,11 @@ define([ fakeMenuItem: function() { return { - conf: {checked: false}, + conf: {checked: false, disabled: false}, setChecked: function (val) { this.conf.checked = val; }, - isChecked: function () { return this.conf.checked; } + isChecked: function () { return this.conf.checked; }, + setDisabled: function (val) { this.conf.disabled = val; }, + isDisabled: function () { return this.conf.disabled; } }; }, diff --git a/apps/common/main/resources/less/synchronize-tip.less b/apps/common/main/resources/less/synchronize-tip.less index 9213105538..205c3595e2 100644 --- a/apps/common/main/resources/less/synchronize-tip.less +++ b/apps/common/main/resources/less/synchronize-tip.less @@ -23,6 +23,16 @@ } } + &.no-arrow { + .tip-arrow { + display: none; + } + + .asc-synchronizetip { + padding-right: 30px; + } + } + &.inc-index { z-index: @zindex-navbar + 4; } diff --git a/apps/spreadsheeteditor/main/app.js b/apps/spreadsheeteditor/main/app.js index f274e5365a..98f81f10a0 100644 --- a/apps/spreadsheeteditor/main/app.js +++ b/apps/spreadsheeteditor/main/app.js @@ -158,6 +158,7 @@ require([ 'Main', 'PivotTable', 'DataTab', + 'ViewTab', 'Common.Controllers.Fonts', 'Common.Controllers.Chat', 'Common.Controllers.Comments', @@ -181,6 +182,7 @@ require([ 'spreadsheeteditor/main/app/controller/Print', 'spreadsheeteditor/main/app/controller/PivotTable', 'spreadsheeteditor/main/app/controller/DataTab', + 'spreadsheeteditor/main/app/controller/ViewTab', 'spreadsheeteditor/main/app/view/FileMenuPanels', 'spreadsheeteditor/main/app/view/ParagraphSettings', 'spreadsheeteditor/main/app/view/ImageSettings', diff --git a/apps/spreadsheeteditor/main/app/controller/DocumentHolder.js b/apps/spreadsheeteditor/main/app/controller/DocumentHolder.js index 4a0b94ab24..a85e080f13 100644 --- a/apps/spreadsheeteditor/main/app/controller/DocumentHolder.js +++ b/apps/spreadsheeteditor/main/app/controller/DocumentHolder.js @@ -1959,6 +1959,7 @@ define([ documentHolder.menuHyperlink.setDisabled(isCellLocked || inPivot); documentHolder.menuAddHyperlink.setDisabled(isCellLocked || inPivot); documentHolder.pmiInsFunction.setDisabled(isCellLocked || inPivot); + documentHolder.pmiFreezePanes.setDisabled(this.api.asc_isWorksheetLockedOrDeleted(this.api.asc_getActiveWorksheetIndex())); if (showMenu) this.showPopupMenu(documentHolder.ssMenu, {}, event); } else if (this.permissions.isEditDiagram && seltype == Asc.c_oAscSelectionType.RangeChartText) { diff --git a/apps/spreadsheeteditor/main/app/controller/Main.js b/apps/spreadsheeteditor/main/app/controller/Main.js index e1756ab83a..5017242a19 100644 --- a/apps/spreadsheeteditor/main/app/controller/Main.js +++ b/apps/spreadsheeteditor/main/app/controller/Main.js @@ -358,6 +358,7 @@ define([ this.appOptions.mentionShare = !((typeof (this.appOptions.customization) == 'object') && (this.appOptions.customization.mentionShare==false)); this.appOptions.canMakeActionLink = this.editorConfig.canMakeActionLink; this.appOptions.canFeaturePivot = true; + this.appOptions.canFeatureViews = !!this.api.asc_isSupportFeature("sheet-views"); this.headerView = this.getApplication().getController('Viewport').getView('Common.Views.Header'); this.headerView.setCanBack(this.appOptions.canBackToFolder === true, (this.appOptions.canBackToFolder) ? this.editorConfig.customization.goback.text : ''); @@ -1533,6 +1534,10 @@ define([ config.msg = this.errorMoveSlicerError; break; + case Asc.c_oAscError.ID.LockedEditView: + config.msg = this.errorEditView; + break; + default: config.msg = (typeof id == 'string') ? id : this.errorDefaultMessage.replace('%1', id); break; @@ -2665,7 +2670,8 @@ define([ errorPasteSlicerError: 'Table slicers cannot be copied from one workbook to another.', errorFrmlMaxLength: 'You cannot add this formula as its length exceeded the allowed number of characters.
    Please edit it and try again.', errorFrmlMaxReference: 'You cannot enter this formula because it has too many values,
    cell references, and/or names.', - errorMoveSlicerError: 'Table slicers cannot be copied from one workbook to another.
    Try again by selecting the entire table and the slicers.' + errorMoveSlicerError: 'Table slicers cannot be copied from one workbook to another.
    Try again by selecting the entire table and the slicers.', + errorEditView: 'The existing sheet view cannot be edited and the new ones cannot be created at the moment as some of them are being edited.' } })(), SSE.Controllers.Main || {})) }); diff --git a/apps/spreadsheeteditor/main/app/controller/Statusbar.js b/apps/spreadsheeteditor/main/app/controller/Statusbar.js index 779b1c9d02..9e257d3711 100644 --- a/apps/spreadsheeteditor/main/app/controller/Statusbar.js +++ b/apps/spreadsheeteditor/main/app/controller/Statusbar.js @@ -104,6 +104,7 @@ define([ this.api.asc_registerCallback('asc_onError', _.bind(this.onError, this)); this.api.asc_registerCallback('asc_onFilterInfo', _.bind(this.onApiFilterInfo , this)); this.api.asc_registerCallback('asc_onActiveSheetChanged', _.bind(this.onApiActiveSheetChanged, this)); + this.api.asc_registerCallback('asc_onRefreshNamedSheetViewList', _.bind(this.onRefreshNamedSheetViewList, this)); this.statusbar.setApi(api); }, @@ -711,12 +712,61 @@ define([ onApiActiveSheetChanged: function (index) { this.statusbar.tabMenu.hide(); + if (this._sheetViewTip && this._sheetViewTip.isVisible() && !this.api.asc_getActiveNamedSheetView(index)) { // hide tip when sheet in the default mode + this._sheetViewTip.hide(); + } + }, + + onRefreshNamedSheetViewList: function() { + var views = this.api.asc_getNamedSheetViews(), + active = false, + name="", + me = this; + for (var i=0; i--> +
    +
    + +
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    +
    + +
    +
    +
    +
    + +
    +
    +
    +
    + +
    +
    + +
    +
    +
    +
    + +
    +
    +
    +
    +
    \ No newline at end of file diff --git a/apps/spreadsheeteditor/main/app/view/Statusbar.js b/apps/spreadsheeteditor/main/app/view/Statusbar.js index 7ec57c21ca..697f6a861d 100644 --- a/apps/spreadsheeteditor/main/app/view/Statusbar.js +++ b/apps/spreadsheeteditor/main/app/view/Statusbar.js @@ -489,11 +489,12 @@ define([ if (this.api) { var wc = this.api.asc_getWorksheetsCount(), i = -1; - var hidentems = [], items = [], tab, locked; + var hidentems = [], items = [], tab, locked, name; var sindex = this.api.asc_getActiveWorksheetIndex(); while (++i < wc) { locked = me.api.asc_isWorksheetLockedOrDeleted(i); + name = me.api.asc_getActiveNamedSheetView(i) || ''; tab = { sheetindex : i, index : items.length, @@ -501,7 +502,10 @@ define([ label : me.api.asc_getWorksheetName(i), // reorderable : !locked, cls : locked ? 'coauth-locked':'', - isLockTheDrag : locked + isLockTheDrag : locked, + iconCls : 'btn-sheet-view', + iconTitle : name, + iconVisible : name!=='' }; this.api.asc_isWorksheetHidden(i)? hidentems.push(tab) : items.push(tab); @@ -765,7 +769,7 @@ define([ showCustomizeStatusBar: function (e) { var el = $(e.target); if ($('#status-zoom-box').find(el).length > 0 - || $(e.target).parent().hasClass('list-item') + || $(e.target).closest('.statusbar .list-item').length>0 || $('#status-tabs-scroll').find(el).length > 0 || $('#status-addtabs-box').find(el).length > 0) return; this.customizeStatusBarMenu.hide(); diff --git a/apps/spreadsheeteditor/main/app/view/Toolbar.js b/apps/spreadsheeteditor/main/app/view/Toolbar.js index bfb0783e16..e65dee66ca 100644 --- a/apps/spreadsheeteditor/main/app/view/Toolbar.js +++ b/apps/spreadsheeteditor/main/app/view/Toolbar.js @@ -325,7 +325,9 @@ define([ { caption: me.textTabInsert, action: 'ins', extcls: 'canedit'}, {caption: me.textTabLayout, action: 'layout', extcls: 'canedit'}, {caption: me.textTabFormula, action: 'formula', extcls: 'canedit'}, - {caption: me.textTabData, action: 'data', extcls: 'canedit'} + {caption: me.textTabData, action: 'data', extcls: 'canedit'}, + undefined, undefined, undefined, + {caption: me.textTabView, action: 'view', extcls: 'canedit'} ]} ); @@ -2433,6 +2435,7 @@ define([ tipPrintTitles: 'Print titles', capBtnInsSlicer: 'Slicer', tipInsertSlicer: 'Insert slicer', - textVertical: 'Vertical Text' + textVertical: 'Vertical Text', + textTabView: 'View' }, SSE.Views.Toolbar || {})); }); \ No newline at end of file diff --git a/apps/spreadsheeteditor/main/app/view/ViewManagerDlg.js b/apps/spreadsheeteditor/main/app/view/ViewManagerDlg.js new file mode 100644 index 0000000000..3c71202c23 --- /dev/null +++ b/apps/spreadsheeteditor/main/app/view/ViewManagerDlg.js @@ -0,0 +1,346 @@ +/* + * + * (c) Copyright Ascensio System SIA 2010-2020 + * + * 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 + * +*/ +/** + * + * ViewManagerDlg.js + * + * Created by Julia.Radzhabova on 09.07.2020 + * Copyright (c) 2020 Ascensio System SIA. All rights reserved. + * + */ + +define([ + 'common/main/lib/view/EditNameDialog', + 'common/main/lib/view/AdvancedSettingsWindow', + 'common/main/lib/component/ComboBox', + 'common/main/lib/component/ListView', + 'common/main/lib/component/InputField' +], function () { + 'use strict'; + + SSE.Views = SSE.Views || {}; + + SSE.Views.ViewManagerDlg = Common.Views.AdvancedSettingsWindow.extend(_.extend({ + options: { + alias: 'ViewManagerDlg', + contentWidth: 460, + height: 330, + buttons: null + }, + + initialize: function (options) { + var me = this; + _.extend(this.options, { + title: this.txtTitle, + template: [ + '
    ', + '
    ', + '
    ', + '
    ', + '', + '', + '', + '', + '', + '', + '', + '
    ', + '', + '
    ', + '
    ', + '', + '', + '', + '', + '
    ', + '
    ', + '
    ', + '
    ', + '
    ', + '
    ', + '' + ].join('') + }, options); + + this.api = options.api; + this.views = options.views || []; + this.userTooltip = true; + this.currentView = undefined; + + this.wrapEvents = { + onRefreshNamedSheetViewList: _.bind(this.onRefreshNamedSheetViewList, this) + }; + + Common.Views.AdvancedSettingsWindow.prototype.initialize.call(this, this.options); + }, + render: function () { + Common.Views.AdvancedSettingsWindow.prototype.render.call(this); + var me = this; + + this.viewList = new Common.UI.ListView({ + el: $('#view-manager-list', this.$window), + store: new Common.UI.DataViewStore(), + simpleAddMode: true, + emptyText: this.textEmpty, + template: _.template(['
    '].join('')), + itemTemplate: _.template([ + '
    ', + '
    <%= name %>
    ', + '<% if (lock) { %>', + '
    <%=lockuser%>
    ', + '<% } %>', + '
    ' + ].join('')) + }); + this.viewList.on('item:select', _.bind(this.onSelectItem, this)) + .on('item:keydown', _.bind(this.onKeyDown, this)) + .on('item:dblclick', _.bind(this.onDblClickItem, this)) + .on('entervalue', _.bind(this.onDblClickItem, this)); + + this.btnNew = new Common.UI.Button({ + el: $('#view-manager-btn-new') + }); + this.btnNew.on('click', _.bind(this.onNew, this, false)); + + this.btnRename = new Common.UI.Button({ + el: $('#view-manager-btn-rename') + }); + this.btnRename.on('click', _.bind(this.onRename, this)); + + this.btnDuplicate = new Common.UI.Button({ + el: $('#view-manager-btn-duplicate') + }); + this.btnDuplicate.on('click', _.bind(this.onNew, this, true)); + + this.btnDelete = new Common.UI.Button({ + el: $('#view-manager-btn-delete') + }); + this.btnDelete.on('click', _.bind(this.onDelete, this)); + + this.afterRender(); + }, + + afterRender: function() { + this._setDefaults(); + }, + + _setDefaults: function (props) { + this.refreshList(this.views, 0); + this.api.asc_registerCallback('asc_onRefreshNamedSheetViewList', this.wrapEvents.onRefreshNamedSheetViewList); + }, + + onRefreshNamedSheetViewList: function() { + this.refreshList(this.api.asc_getNamedSheetViews(), this.currentView); + }, + + refreshList: function(views, selectedItem) { + if (views) { + this.views = views; + var arr = []; + for (var i=0; i0) { + if (selectedItem===undefined || selectedItem===null) selectedItem = 0; + if (_.isNumber(selectedItem)) { + if (selectedItem>val-1) selectedItem = val-1; + this.viewList.selectByIndex(selectedItem); + setTimeout(function() { + me.viewList.scrollToRecord(me.viewList.store.at(selectedItem)); + }, 50); + + } + if (this.userTooltip===true && this.viewList.cmpEl.find('.lock-user').length>0) + this.viewList.cmpEl.on('mouseover', _.bind(this.onMouseOverLock, this)).on('mouseout', _.bind(this.onMouseOutLock, this)); + } + + var me = this; + _.delay(function () { + me.viewList.cmpEl.find('.listview').focus(); + me.viewList.scroller.update({alwaysVisibleY: true}); + }, 100, this); + }, + + onMouseOverLock: function (evt, el, opt) { + if (this.userTooltip===true && $(evt.target).hasClass('lock-user')) { + var me = this, + tipdata = $(evt.target).tooltip({title: this.tipIsLocked,trigger:'manual'}).data('bs.tooltip'); + + this.userTooltip = tipdata.tip(); + this.userTooltip.css('z-index', parseInt(this.$window.css('z-index')) + 10); + tipdata.show(); + + setTimeout(function() { me.userTipHide(); }, 5000); + } + }, + + userTipHide: function () { + if (typeof this.userTooltip == 'object') { + this.userTooltip.remove(); + this.userTooltip = undefined; + this.viewList.cmpEl.off('mouseover').off('mouseout'); + } + }, + + onMouseOutLock: function (evt, el, opt) { + if (typeof this.userTooltip == 'object') this.userTipHide(); + }, + + onNew: function (duplicate) { + var rec = duplicate ? this.viewList.getSelectedRec().get('view') : undefined; + this.currentView = this.viewList.store.length; + this.api.asc_addNamedSheetView(rec); + }, + + onDelete: function () { + var me = this, + rec = this.viewList.getSelectedRec(), + res; + if (rec) { + if (rec.get('active')) { + Common.UI.warning({ + msg: this.warnDeleteView.replace('%1', rec.get('name')), + buttons: ['yes', 'no'], + primary: 'yes', + callback: function(btn) { + if (btn == 'yes') { + me.api.asc_deleteNamedSheetViews([rec.get('view')]); + } + } + }); + } else { + this.api.asc_deleteNamedSheetViews([rec.get('view')]); + } + } + }, + + onRename: function () { + var rec = this.viewList.getSelectedRec(); + if (rec) { + var me = this; + (new Common.Views.EditNameDialog({ + label: this.textRenameLabel, + error: this.textRenameError, + value: rec.get('name'), + handler: function(result, value) { + if (result == 'ok') { + rec.get('view').asc_setName(value); + } + } + })).show(); + } + }, + + getSettings: function() { + var rec = this.viewList.getSelectedRec(); + return rec ? rec.get('name') : null; + }, + + getUserName: function(id){ + var usersStore = SSE.getCollection('Common.Collections.Users'); + if (usersStore){ + var rec = usersStore.findUser(id); + if (rec) + return Common.Utils.UserInfoParser.getParsedName(rec.get('username')); + } + return this.guestText; + }, + + onSelectItem: function(lisvView, itemView, record) { + this.userTipHide(); + var rawData = {}, + isViewSelect = _.isFunction(record.toJSON); + + if (isViewSelect){ + if (record.get('selected')) { + rawData = record.toJSON(); + } else {// record deselected + return; + } + this.currentView = _.indexOf(this.viewList.store.models, record); + this.btnRename.setDisabled(rawData.lock); + this.btnDelete.setDisabled(rawData.lock); + } + }, + + close: function () { + this.userTipHide(); + this.api.asc_unregisterCallback('asc_onRefreshNamedSheetViewList', this.wrapEvents.onRefreshNamedSheetViewList); + + Common.Views.AdvancedSettingsWindow.prototype.close.call(this); + }, + + onKeyDown: function (lisvView, record, e) { + if (e.keyCode==Common.UI.Keys.DELETE && !this.btnDelete.isDisabled()) + this.onDelete(); + }, + + onDblClickItem: function (lisvView, record, e) { + this.onPrimary(); + }, + + txtTitle: 'Sheet View Manager', + textViews: 'Sheet views', + closeButtonText : 'Close', + textNew: 'New', + textRename: 'Rename', + textDuplicate: 'Duplicate', + textDelete: 'Delete', + textGoTo: 'Go to view...', + textEmpty: 'No views have been created yet.', + guestText: 'Guest', + tipIsLocked: 'This element is being edited by another user.', + textRenameLabel: 'Rename view', + textRenameError: 'View name must not be empty.', + warnDeleteView: "You are trying to delete the currently enabled view '%1'.
    Close this view and delete it?" + + }, SSE.Views.ViewManagerDlg || {})); +}); \ No newline at end of file diff --git a/apps/spreadsheeteditor/main/app/view/ViewTab.js b/apps/spreadsheeteditor/main/app/view/ViewTab.js new file mode 100644 index 0000000000..0420a14954 --- /dev/null +++ b/apps/spreadsheeteditor/main/app/view/ViewTab.js @@ -0,0 +1,321 @@ +/* + * + * (c) Copyright Ascensio System SIA 2010-2020 + * + * 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 + * + */ +/** + * ViewTab.js + * + * Created by Julia Radzhabova on 08.07.2020 + * Copyright (c) 2020 Ascensio System SIA. All rights reserved. + * + */ + +define([ + 'common/main/lib/util/utils', + 'common/main/lib/component/BaseView', + 'common/main/lib/component/Layout' +], function () { + 'use strict'; + + SSE.Views.ViewTab = Common.UI.BaseView.extend(_.extend((function(){ + function setEvents() { + var me = this; + if ( me.appConfig.canFeatureViews ) { + me.btnCloseView.on('click', function (btn, e) { + me.fireEvent('viewtab:openview', [{name: 'default', value: 'default'}]); + }); + me.btnCreateView.on('click', function (btn, e) { + me.fireEvent('viewtab:createview'); + }); + } + + me.btnFreezePanes.on('click', function (btn, e) { + me.fireEvent('viewtab:freeze', [btn.pressed]); + }); + this.chFormula.on('change', function (field, value) { + me.fireEvent('viewtab:formula', [0, value]); + }); + this.chHeadings.on('change', function (field, value) { + me.fireEvent('viewtab:headings', [1, value]); + }); + this.chGridlines.on('change', function (field, value) { + me.fireEvent('viewtab:gridlines', [2, value]); + }); + this.cmbZoom.on('selected', function(combo, record) { + me.fireEvent('viewtab:zoom', [record.value]); + }); + } + + return { + options: {}, + + initialize: function (options) { + Common.UI.BaseView.prototype.initialize.call(this); + this.toolbar = options.toolbar; + this.appConfig = options.mode; + + this.lockedControls = []; + + var me = this, + $host = me.toolbar.$el, + _set = SSE.enumLock; + + if ( me.appConfig.canFeatureViews ) { + this.btnSheetView = new Common.UI.Button({ + parentEl: $host.find('#slot-btn-sheet-view'), + cls: 'btn-toolbar x-huge icon-top', + iconCls: 'toolbar__icon btn-sheet-view', + caption: me.capBtnSheetView, + lock : [_set.lostConnect, _set.coAuth], + menu: true + }); + this.lockedControls.push(this.btnSheetView); + + this.btnCreateView = new Common.UI.Button({ + id : 'id-toolbar-btn-createview', + cls : 'btn-toolbar', + iconCls : 'toolbar__icon btn-sheet-view-new', + caption : this.textCreate, + lock : [_set.coAuth, _set.lostConnect] + }); + this.lockedControls.push(this.btnCreateView); + Common.Utils.injectComponent($host.find('#slot-createview'), this.btnCreateView); + + this.btnCloseView = new Common.UI.Button({ + id : 'id-toolbar-btn-closeview', + cls : 'btn-toolbar', + iconCls : 'toolbar__icon btn-sheet-view-close', + caption : this.textClose, + lock : [_set.coAuth, _set.lostConnect] + }); + this.lockedControls.push(this.btnCloseView); + Common.Utils.injectComponent($host.find('#slot-closeview'), this.btnCloseView); + } + + this.btnFreezePanes = new Common.UI.Button({ + parentEl: $host.find('#slot-btn-freeze'), + cls: 'btn-toolbar x-huge icon-top', + iconCls: 'toolbar__icon btn-freeze-panes', + caption: this.capBtnFreeze, + split: false, + enableToggle: true, + lock: [_set.sheetLock, _set.lostConnect, _set.coAuth] + }); + this.lockedControls.push(this.btnFreezePanes); + + this.cmbZoom = new Common.UI.ComboBox({ + el : $host.find('#slot-field-zoom'), + cls : 'input-group-nr', + menuStyle : 'min-width: 55px;', + hint : me.tipFontSize, + editable : false, + lock : [_set.coAuth, _set.lostConnect], + data : [ + { displayValue: "50%", value: 50 }, + { displayValue: "75%", value: 75 }, + { displayValue: "100%", value: 100 }, + { displayValue: "125%", value: 125 }, + { displayValue: "150%", value: 150 }, + { displayValue: "175%", value: 175 }, + { displayValue: "200%", value: 200 } + ] + }); + this.cmbZoom.setValue(100); + + this.chFormula = new Common.UI.CheckBox({ + el: $host.findById('#slot-chk-formula'), + labelText: this.textFormula, + value: !Common.localStorage.getBool('sse-hidden-formula'), + lock : [_set.lostConnect, _set.coAuth] + }); + this.lockedControls.push(this.chFormula); + + this.chHeadings = new Common.UI.CheckBox({ + el: $host.findById('#slot-chk-heading'), + labelText: this.textHeadings, + lock : [_set.sheetLock, _set.lostConnect, _set.coAuth] + }); + this.lockedControls.push(this.chHeadings); + + this.chGridlines = new Common.UI.CheckBox({ + el: $host.findById('#slot-chk-gridlines'), + labelText: this.textGridlines, + lock : [_set.sheetLock, _set.lostConnect, _set.coAuth] + }); + this.lockedControls.push(this.chGridlines); + + $host.find('#slot-lbl-zoom').text(this.textZoom); + + Common.NotificationCenter.on('app:ready', this.onAppReady.bind(this)); + }, + + render: function (el) { + return this; + }, + + onAppReady: function (config) { + var me = this; + (new Promise(function (accept, reject) { + accept(); + })).then(function(){ + if (!config.canFeatureViews) { + me.toolbar && me.toolbar.$el.find('.group.sheet-views').hide(); + me.toolbar && me.toolbar.$el.find('.separator.sheet-views').hide(); + } else { + me.btnSheetView.updateHint( me.tipSheetView ); + me.setButtonMenu(me.btnSheetView); + + me.btnCreateView.updateHint(me.tipCreate); + me.btnCloseView.updateHint(me.tipClose); + } + me.btnFreezePanes.updateHint(me.tipFreeze); + + setEvents.call(me); + }); + }, + + focusInner: function(menu, e) { + if (e.keyCode == Common.UI.Keys.UP) + menu.items[menu.items.length-1].cmpEl.find('> a').focus(); + else + menu.items[0].cmpEl.find('> a').focus(); + }, + + focusOuter: function(menu, e) { + menu.items[2].cmpEl.find('> a').focus(); + }, + + onBeforeKeyDown: function(menu, e) { + if (e.keyCode == Common.UI.Keys.RETURN) { + e.preventDefault(); + e.stopPropagation(); + var li = $(e.target).closest('li'); + (li.length>0) && li.click(); + Common.UI.Menu.Manager.hideAll(); + } else if (e.namespace!=="after.bs.dropdown" && (e.keyCode == Common.UI.Keys.DOWN || e.keyCode == Common.UI.Keys.UP)) { + var $items = $('> [role=menu] > li:not(.divider):not(.disabled):visible', menu.$el).find('> a'); + if (!$items.length) return; + var index = $items.index($items.filter(':focus')), + me = this; + if (menu._outerMenu && (e.keyCode == Common.UI.Keys.UP && index==0 || e.keyCode == Common.UI.Keys.DOWN && index==$items.length - 1) || + menu._innerMenu && (e.keyCode == Common.UI.Keys.UP || e.keyCode == Common.UI.Keys.DOWN) && index!==-1) { + e.preventDefault(); + e.stopPropagation(); + _.delay(function() { + menu._outerMenu ? me.focusOuter(menu._outerMenu, e) : me.focusInner(menu._innerMenu, e); + }, 10); + } + } + }, + + setButtonMenu: function(btn) { + var me = this, + arr = [{caption: me.textDefault, value: 'default', checkable: true, allowDepress: false}]; + btn.setMenu(new Common.UI.Menu({ + items: [ + {template: _.template('
    ')}, + { caption: '--' }, + { + caption: me.textManager, + value: 'manager' + } + ] + })); + btn.menu.items[2].on('click', function (item, e) { + me.fireEvent('viewtab:manager'); + }); + btn.menu.on('show:after', function (menu, e) { + var internalMenu = menu._innerMenu; + internalMenu.scroller.update({alwaysVisibleY: true}); + _.delay(function() { + menu._innerMenu && menu._innerMenu.cmpEl.focus(); + }, 10); + }).on('show:before', function (menu, e) { + me.fireEvent('viewtab:showview'); + }).on('keydown:before', _.bind(me.onBeforeKeyDown, this)); + + var menu = new Common.UI.Menu({ + maxHeight: 300, + cls: 'internal-menu', + items: arr + }); + menu.render(btn.menu.items[0].cmpEl.children(':first')); + menu.cmpEl.css({ + display : 'block', + position : 'relative', + left : 0, + top : 0 + }); + menu.cmpEl.attr({tabindex: "-1"}); + menu.on('item:toggle', function (menu, item, state, e) { + if (!!state) + me.fireEvent('viewtab:openview', [{name: item.caption, value: item.value}]); + }).on('keydown:before', _.bind(me.onBeforeKeyDown, this)); + btn.menu._innerMenu = menu; + menu._outerMenu = btn.menu; + }, + + show: function () { + Common.UI.BaseView.prototype.show.call(this); + this.fireEvent('show', this); + }, + + getButtons: function(type) { + if (type===undefined) + return this.lockedControls; + return []; + }, + + SetDisabled: function (state) { + this.lockedControls && this.lockedControls.forEach(function(button) { + if ( button ) { + button.setDisabled(state); + } + }, this); + }, + + capBtnSheetView: 'Sheet View', + capBtnFreeze: 'Freeze Panes', + textZoom: 'Zoom', + tipSheetView: 'Sheet view', + textDefault: 'Default', + textManager: 'View manager', + tipFreeze: 'Freeze panes', + tipCreate: 'Create sheet view', + tipClose: 'Close sheet view', + textCreate: 'New', + textClose: 'Close', + textFormula: 'Formula bar', + textHeadings: 'Headings', + textGridlines: 'Gridlines' + } + }()), SSE.Views.ViewTab || {})); +}); diff --git a/apps/spreadsheeteditor/main/app_dev.js b/apps/spreadsheeteditor/main/app_dev.js index d3b10623ad..2ca0622ca3 100644 --- a/apps/spreadsheeteditor/main/app_dev.js +++ b/apps/spreadsheeteditor/main/app_dev.js @@ -148,6 +148,7 @@ require([ 'Main', 'PivotTable', 'DataTab', + 'ViewTab', 'Common.Controllers.Fonts', 'Common.Controllers.Chat', 'Common.Controllers.Comments', @@ -171,6 +172,7 @@ require([ 'spreadsheeteditor/main/app/controller/Print', 'spreadsheeteditor/main/app/controller/PivotTable', 'spreadsheeteditor/main/app/controller/DataTab', + 'spreadsheeteditor/main/app/controller/ViewTab', 'spreadsheeteditor/main/app/view/FileMenuPanels', 'spreadsheeteditor/main/app/view/ParagraphSettings', 'spreadsheeteditor/main/app/view/ImageSettings', diff --git a/apps/spreadsheeteditor/main/locale/en.json b/apps/spreadsheeteditor/main/locale/en.json index cb50918148..490eaa33f6 100644 --- a/apps/spreadsheeteditor/main/locale/en.json +++ b/apps/spreadsheeteditor/main/locale/en.json @@ -103,6 +103,8 @@ "Common.Views.CopyWarningDialog.textToPaste": "for Paste", "Common.Views.DocumentAccessDialog.textLoading": "Loading...", "Common.Views.DocumentAccessDialog.textTitle": "Sharing Settings", + "Common.Views.EditNameDialog.textLabel": "Label:", + "Common.Views.EditNameDialog.textLabelError": "Label must not be empty.", "Common.Views.Header.labelCoUsersDescr": "Users who are editing the file:", "Common.Views.Header.textAdvSettings": "Advanced settings", "Common.Views.Header.textBack": "Open file location", @@ -850,6 +852,7 @@ "SSE.Controllers.Main.warnNoLicense": "You've reached the limit for simultaneous connections to %1 editors. This document will be opened for viewing only.
    Contact %1 sales team for personal upgrade terms.", "SSE.Controllers.Main.warnNoLicenseUsers": "You've reached the user limit for %1 editors. Contact %1 sales team for personal upgrade terms.", "SSE.Controllers.Main.warnProcessRightsChange": "You have been denied the right to edit the file.", + "SSE.Controllers.Main.errorEditView": "The existing sheet view cannot be edited and the new ones cannot be created at the moment as some of them are being edited.", "SSE.Controllers.Print.strAllSheets": "All Sheets", "SSE.Controllers.Print.textFirstCol": "First column", "SSE.Controllers.Print.textFirstRow": "First row", @@ -867,6 +870,7 @@ "SSE.Controllers.Statusbar.strSheet": "Sheet", "SSE.Controllers.Statusbar.warnDeleteSheet": "The selected worksheets might contain data. Are you sure you want to proceed?", "SSE.Controllers.Statusbar.zoomText": "Zoom {0}%", + "SSE.Controllers.Statusbar.textSheetViewTip": "You are in Sheet View mode. Filters and sorting are visible only to you and those who are still in this view.", "SSE.Controllers.Toolbar.confirmAddFontName": "The font you are going to save is not available on the current device.
    The text style will be displayed using one of the system fonts, the saved font will be used when it is available.
    Do you want to continue?", "SSE.Controllers.Toolbar.errorMaxRows": "ERROR! The maximum number of data series per chart is 255", "SSE.Controllers.Toolbar.errorStockChart": "Incorrect row order. To build a stock chart place the data on the sheet in the following order:
    opening price, max price, min price, closing price.", @@ -2891,6 +2895,7 @@ "SSE.Views.Toolbar.txtTime": "Time", "SSE.Views.Toolbar.txtUnmerge": "Unmerge Cells", "SSE.Views.Toolbar.txtYen": "¥ Yen", + "SSE.Views.Toolbar.textTabView": "View", "SSE.Views.Top10FilterDialog.textType": "Show", "SSE.Views.Top10FilterDialog.txtBottom": "Bottom", "SSE.Views.Top10FilterDialog.txtBy": "by", @@ -2927,5 +2932,33 @@ "SSE.Views.ValueFieldSettingsDialog.txtSum": "Sum", "SSE.Views.ValueFieldSettingsDialog.txtSummarize": "Summarize value field by", "SSE.Views.ValueFieldSettingsDialog.txtVar": "Var", - "SSE.Views.ValueFieldSettingsDialog.txtVarp": "Varp" + "SSE.Views.ValueFieldSettingsDialog.txtVarp": "Varp", + "SSE.Views.ViewManagerDlg.txtTitle": "Sheet View Manager", + "SSE.Views.ViewManagerDlg.textViews": "Sheet views", + "SSE.Views.ViewManagerDlg.closeButtonText": "Close", + "SSE.Views.ViewManagerDlg.textNew": "New", + "SSE.Views.ViewManagerDlg.textRename": "Rename", + "SSE.Views.ViewManagerDlg.textDuplicate": "Duplicate", + "SSE.Views.ViewManagerDlg.textDelete": "Delete", + "SSE.Views.ViewManagerDlg.textGoTo": "Go to view...", + "SSE.Views.ViewManagerDlg.textEmpty": "No views have been created yet.", + "SSE.Views.ViewManagerDlg.guestText": "Guest", + "SSE.Views.ViewManagerDlg.tipIsLocked": "This element is being edited by another user.", + "SSE.Views.ViewManagerDlg.textRenameLabel": "Rename view", + "SSE.Views.ViewManagerDlg.textRenameError": "View name must not be empty.", + "SSE.Views.ViewManagerDlg.warnDeleteView": "You are trying to delete the currently enabled view '%1'.
    Close this view and delete it?", + "SSE.Views.ViewTab.capBtnSheetView": "Sheet View", + "SSE.Views.ViewTab.capBtnFreeze": "Freeze Panes", + "SSE.Views.ViewTab.textZoom": "Zoom", + "SSE.Views.ViewTab.tipSheetView": "Sheet view", + "SSE.Views.ViewTab.textDefault": "Default", + "SSE.Views.ViewTab.textManager": "View manager", + "SSE.Views.ViewTab.tipFreeze": "Freeze panes", + "SSE.Views.ViewTab.tipCreate": "Create sheet view", + "SSE.Views.ViewTab.tipClose": "Close sheet view", + "SSE.Views.ViewTab.textCreate": "New", + "SSE.Views.ViewTab.textClose": "Close", + "SSE.Views.ViewTab.textFormula": "Formula bar", + "SSE.Views.ViewTab.textHeadings": "Headings", + "SSE.Views.ViewTab.textGridlines": "Gridlines" } \ No newline at end of file diff --git a/apps/spreadsheeteditor/main/resources/img/toolbar/1.25x/btn-sheet-view.png b/apps/spreadsheeteditor/main/resources/img/toolbar/1.25x/btn-sheet-view.png new file mode 100644 index 0000000000..76dd2895ac Binary files /dev/null and b/apps/spreadsheeteditor/main/resources/img/toolbar/1.25x/btn-sheet-view.png differ diff --git a/apps/spreadsheeteditor/main/resources/img/toolbar/1.5x/btn-sheet-view.png b/apps/spreadsheeteditor/main/resources/img/toolbar/1.5x/btn-sheet-view.png new file mode 100644 index 0000000000..f9d9e25b38 Binary files /dev/null and b/apps/spreadsheeteditor/main/resources/img/toolbar/1.5x/btn-sheet-view.png differ diff --git a/apps/spreadsheeteditor/main/resources/img/toolbar/1.75x/btn-sheet-view.png b/apps/spreadsheeteditor/main/resources/img/toolbar/1.75x/btn-sheet-view.png new file mode 100644 index 0000000000..f793aea61d Binary files /dev/null and b/apps/spreadsheeteditor/main/resources/img/toolbar/1.75x/btn-sheet-view.png differ diff --git a/apps/spreadsheeteditor/main/resources/img/toolbar/1x/btn-sheet-view.png b/apps/spreadsheeteditor/main/resources/img/toolbar/1x/btn-sheet-view.png new file mode 100644 index 0000000000..142fdc3add Binary files /dev/null and b/apps/spreadsheeteditor/main/resources/img/toolbar/1x/btn-sheet-view.png differ diff --git a/apps/spreadsheeteditor/main/resources/img/toolbar/2x/btn-sheet-view.png b/apps/spreadsheeteditor/main/resources/img/toolbar/2x/btn-sheet-view.png new file mode 100644 index 0000000000..fdd10ee875 Binary files /dev/null and b/apps/spreadsheeteditor/main/resources/img/toolbar/2x/btn-sheet-view.png differ diff --git a/apps/spreadsheeteditor/main/resources/less/statusbar.less b/apps/spreadsheeteditor/main/resources/less/statusbar.less index 57d21312f9..10116eef90 100644 --- a/apps/spreadsheeteditor/main/resources/less/statusbar.less +++ b/apps/spreadsheeteditor/main/resources/less/statusbar.less @@ -236,6 +236,20 @@ } } + &.icon-visible { + > span { + padding-left: 25px; + > .toolbar__icon { + width: 20px; + height: 20px; + position: absolute; + top: 0; + left: 0; + margin: 3px; + } + } + } + &.disabled { opacity: 0.5; @@ -271,6 +285,16 @@ padding-left: 10px; } } + &.icon-visible { + > span { + padding-left: 24px; + } + &.right { + > span { + padding-left: 25px; + } + } + } } &.separator-item { margin-left: 20px; diff --git a/apps/spreadsheeteditor/main/resources/less/toolbar.less b/apps/spreadsheeteditor/main/resources/less/toolbar.less index ebc025adcf..c532ae5507 100644 --- a/apps/spreadsheeteditor/main/resources/less/toolbar.less +++ b/apps/spreadsheeteditor/main/resources/less/toolbar.less @@ -161,3 +161,8 @@ width: 38px; height: 38px; } + +#slot-field-zoom { + float: left; + min-width: 46px; +} \ No newline at end of file