Files
web-apps/apps/spreadsheeteditor/main/app/controller/ChartTab.js
2026-01-13 13:23:42 +03:00

609 lines
27 KiB
JavaScript

/*
* (c) Copyright Ascensio System SIA 2010-2024
*
* 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-6 Ernesta Birznieka-Upish
* 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
*
*/
/**
* ChartTab.js
*
* Created on 06.10.2025
*
*/
define([
'core',
'spreadsheeteditor/main/app/view/ChartTab'
], function () {
'use strict';
SSE.Controllers.ChartTab = Backbone.Controller.extend(_.extend({
models : [],
collections : [
],
views : [
'ChartTab'
],
sdkViewName : '#id_main',
initialize: function () {
this._state = {
Width: 0,
Height: 0,
ChartStyle: 1,
ChartType: -1,
SeveralCharts: false,
DisabledControls: false,
keepRatio: false,
};
this._nRatio = 1;
this.spinners = [];
this.lockedControls = [];
this._locked = false;
this.defColor = {color: '4f81bd', effectId: 24};
this.isChart = true;
this._noApply = false;
this._originalProps = null;
this.addListeners({
'ChartTab': {
'charttab:advanced': _.bind(this.openAdvancedSettings, this),
'charttab:type': _.bind(this.onChangeType, this),
'charttab:selectdata': _.bind(this.onSelectData, this),
'charttab:rowscols': _.bind(this.onSwitchRowsCols, this),
'charttab:widthchange': _.bind(this.onWidthChange, this),
'charttab:heightchange': _.bind(this.onHeightChange, this),
'charttab:ratio': _.bind(this.onToggleRatio, this),
'charttab:3dsettings': _.bind(this.open3DSettings, this),
'charttab:selectstyle': _.bind(this.onSelectStyle, this),
},
'Toolbar': {
'tab:active': _.bind(this.onActiveTab, this)
}
});
},
open3DSettings: function () {
if (this.view.btn3DSettings.isDisabled() || !Common.Controllers.LaunchController.isScriptLoaded()) return;
var me = this;
var win, props;
if (me.api){
props = (me.isChart) ? me.api.asc_getChartSettings() : me._originalProps;
if (props) {
var oView3D = props.getView3d();
(new Common.Views.Charts3DDlg(
{
oView3D: oView3D,
chartProps: props,
X: me._state.X,
Y: me._state.Y,
RightAngle: me._state.RightAngle,
Perspective: me._state.Perspective,
Depth: me._state.Depth,
Height3d: me._state.Height3d,
api: me.api,
handler: function(result, obj, props) {
if (result == 'ok') {
if (me.api) {
if (props) {
if (obj) {
props.startEdit();
props.setView3d(obj);
props.endEdit();
}
}
}
}
Common.NotificationCenter.trigger('edit:complete', me);
}
})).show();
}
}
},
onToggleRatio: function (value) {
if (value && this.view.spnHeight.getNumberValue()>0) {
this._nRatio = this.view.spnWidth.getNumberValue()/this.view.spnHeight.getNumberValue();
}
if (this.api) {
var props = new Asc.asc_CImgProperty();
props.asc_putLockAspect(value);
this.api.asc_setGraphicObjectProps(props);
}
},
ChangeSettings: function(props) {
setTimeout(() => {
this.createDelayedElements();
}, 0)
if (this._isEditType) {
this._props = props;
return;
}
var isChart = !!(props && props.asc_getChartProperties && props.asc_getChartProperties()),
chartSettings = isChart ? this.api.asc_getChartSettings(true) : null, // don't lock chart object
props3d = chartSettings ? chartSettings.getView3d() : null;
this._state.is3D=!!props3d;
if (this.api && props){
if (isChart) { // chart
this._originalProps = new Asc.asc_CImgProperty(props);
this.isChart = true;
this._noApply = true;
this.chartProps = props.asc_getChartProperties();
var severalCharts = props.asc_getSeveralCharts();
Common.Utils.lockControls(Common.enumLock.SeveralCharts, severalCharts, {array: [this.view.btnAdvancedSettings]});
this._state.SeveralCharts = severalCharts;
var value = props.asc_getSeveralChartTypes();
var type = (this._state.SeveralCharts && value) ? null : this.chartProps.getType();
if (this._state.ChartType !== type) {
var isCombo =
type === null ||
type === Asc.c_oAscChartTypeSettings.comboBarLine ||
type === Asc.c_oAscChartTypeSettings.comboBarLineSecondary ||
type === Asc.c_oAscChartTypeSettings.comboAreaBar ||
type === Asc.c_oAscChartTypeSettings.comboCustom;
this.ShowCombinedProps(type);
if (!isCombo) {
this.updateChartStyles(this.api.asc_getChartPreviews(type, undefined, true));
}
this.view.$el.find('.separator-chart-styles')[isCombo ? 'hide' : 'show']();
this._state.ChartType = type;
}
if (!(type==Asc.c_oAscChartTypeSettings.comboBarLine || type==Asc.c_oAscChartTypeSettings.comboBarLineSecondary ||
type==Asc.c_oAscChartTypeSettings.comboAreaBar || type==Asc.c_oAscChartTypeSettings.comboCustom)) {
value = props.asc_getSeveralChartStyles();
if (this._state.SeveralCharts && value) {
this.view.chartStyles.fieldPicker.deselectAll();
this.view.chartStyles.menuPicker.deselectAll();
this._state.ChartStyle = null;
} else {
value = this.chartProps.getStyle();
if (this._state.ChartStyle!==value || this._isChartStylesChanged) {
this._state.ChartStyle=value;
var arr = this.selectCurrentChartStyle();
this._isChartStylesChanged && this.api.asc_generateChartPreviews(this._state.ChartType, arr);
}
}
this._isChartStylesChanged = false;
}
this._noApply = false;
value = props.asc_getWidth();
if ( Math.abs(this._state.Width-value)>0.001 ||
(this._state.Width===null || value===null)&&(this._state.Width!==value)) {
this.view.spnWidth.setValue((value!==null) ? Common.Utils.Metric.fnRecalcFromMM(value) : '', true);
this._state.Width = value;
}
value = props.asc_getHeight();
if ( Math.abs(this._state.Height-value)>0.001 ||
(this._state.Height===null || value===null)&&(this._state.Height!==value)) {
this.view.spnHeight.setValue((value!==null) ? Common.Utils.Metric.fnRecalcFromMM(value) : '', true);
this._state.Height = value;
}
if (props.asc_getHeight()>0)
this._nRatio = props.asc_getWidth()/props.asc_getHeight();
value = props.asc_getLockAspect();
if (this._state.keepRatio!==value) {
this.view.chRatio.setValue(value, true);
this._state.keepRatio=value;
}
var series = chartSettings ? chartSettings.getSeries() : null;
Common.Utils.lockControls(Common.enumLock.noRange, !series || series.length<1 || !chartSettings ||
!chartSettings.getRange(), {array: [this.view.btnSwitchRowsCols]});
if (props3d) {
value = props3d.asc_getRotX();
if ((this._state.X===undefined || value===undefined)&&(this._state.X!==value) ||
Math.abs(this._state.X-value)>0.001) {
this._state.X = value;
}
value = props3d.asc_getRotY();
if ( (this._state.Y===undefined || value===undefined)&&(this._state.Y!==value) ||
Math.abs(this._state.Y-value)>0.001) {
this._state.Y = value;
}
value = props3d.asc_getRightAngleAxes();
if ( this._state.RightAngle!==value ) {
this._state.RightAngle=value;
}
value = props3d.asc_getPerspective();
if ( (this._state.Perspective===undefined || value===undefined)&&(this._state.Perspective!==value) ||
Math.abs(this._state.Perspective-value)>0.001) {
this._state.Perspective = value;
}
value = props3d.asc_getDepth();
if ( Math.abs(this._state.Depth-value)>0.001 ||
(this._state.Depth===undefined || value===undefined)&&(this._state.Depth!==value)) {
this._state.Depth = value;
}
value = props3d.asc_getHeight();
if ( Math.abs(this._state.Height3d-value)>0.001 ||
(this._state.Height3d===undefined || this._state.Height3d===null || value===null)&&(this._state.Height3d!==value)) {
this._state.Height3d = value;
}
}
this.view.$el.find('.separator-chart-3d')[props3d ? 'show' : 'hide']();
this.view.btn3DSettings.setVisible(props3d)
}
}
},
createDelayedElements: function () {
if (this.spinners.length === 0) {
this.spinners.push(this.view.spnWidth);
this.spinners.push(this.view.spnHeight);
}
this.updateMetricUnit();
},
updateMetricUnit: function() {
if (this.spinners) {
for (var i=0; i<this.spinners.length; i++) {
var spinner = this.spinners[i];
spinner.setDefaultUnit(Common.Utils.Metric.getCurrentMetricName());
spinner.setStep(Common.Utils.Metric.getCurrentMetric()==Common.Utils.Metric.c_MetricUnits.pt ? 1 : 0.1);
}
this.view.spnWidth && this.view.spnWidth.setValue((this._state.Width!==null) ? Common.Utils.Metric.fnRecalcFromMM(this._state.Width) : '', true);
this.view.spnHeight && this.view.spnHeight.setValue((this._state.Height!==null) ? Common.Utils.Metric.fnRecalcFromMM(this._state.Height) : '', true);
}
},
openAdvancedSettings: function() {
if (this.view.btnAdvancedSettings.isDisabled() || !Common.Controllers.LaunchController.isScriptLoaded()) return;
var me = this;
var win, props;
if (me.api){
props = (me.isChart) ? me.api.asc_getChartSettings() : me._originalProps;
if (props) {
(new SSE.Views.ChartSettingsDlg(
{
chartSettings: props,
imageSettings: (me.isChart) ? me._originalProps : null,
sparklineStyles: me.sparklineStyles,
isChart: me.isChart,
api: me.api,
handler: function(result, value) {
if (result == 'ok') {
if (me.api) {
if (me.isChart) {
if (value.imageSettings) {
value.imageSettings.asc_putChartProperties(value.chartSettings);
me.api.asc_setGraphicObjectProps(value.imageSettings);
} else
me.api.asc_applyChartSettings(value.chartSettings);
}
}
}
Common.NotificationCenter.trigger('edit:complete', me);
}
})).show();
}
}
},
onChangeType: function() {
var me = this;
if (me.api){
me._isEditType = true;
var win = new SSE.Views.ChartWizardDialog({
api: me.api,
props: {recommended: me.api.asc_getRecommendedChartData()},
type: me._state.ChartType,
isEdit: true,
handler: function(result, value) {
if (result == 'ok') {
me._isEditType = false;
me.api && me.api.asc_addChartSpace(value);
}
Common.NotificationCenter.trigger('edit:complete', me.toolbar);
}
}).on('close', function() {
me._isEditType = false;
});
win.show();
}
},
onSelectData: function() {
if (!Common.Controllers.LaunchController.isScriptLoaded()) {
return;
}
var me = this;
var props;
if (me.api){
props = me.api.asc_getChartSettings();
if (props) {
me._isEditRanges = true;
props.startEdit();
var win = new SSE.Views.ChartDataDialog({
chartSettings: props,
api: me.api,
handler: function(result, value) {
if (result == 'ok') {
props.endEdit();
me._isEditRanges = false;
}
Common.NotificationCenter.trigger('edit:complete', me);
}
}).on('close', function() {
me._isEditRanges && props.cancelEdit();
me._isEditRanges = false;
});
win.show();
}
}
},
onSwitchRowsCols: function() {
if (this.api){
var props = this.api.asc_getChartSettings(true);
if (props) {
props.startEdit();
var res = props.switchRowCol();
if (res === Asc.c_oAscError.ID.MaxDataSeriesError) {
props.cancelEdit();
Common.UI.warning({msg: this.errorMaxRows, maxwidth: 600});
} else
props.endEdit();
}
}
},
onWidthChange: function(field, newValue, oldValue, eOpts){
var w = field.getNumberValue();
var h = this.view.spnHeight.getNumberValue();
if (this.view.chRatio.getValue() === 'checked') {
h = w/this._nRatio;
if (h>this.view.spnHeight.options.maxValue) {
h = this.view.spnHeight.options.maxValue;
w = h * this._nRatio;
this.view.spnWidth.setValue(w, true);
}
this.view.spnHeight.setValue(h, true);
}
if (this.api) {
var props = new Asc.asc_CImgProperty();
props.asc_putWidth(Common.Utils.Metric.fnRecalcToMM(w));
props.asc_putHeight(Common.Utils.Metric.fnRecalcToMM(h));
this.api.asc_setGraphicObjectProps(props);
}
},
onHeightChange: function(field, newValue, oldValue, eOpts){
var h = field.getNumberValue(), w = this.view.spnWidth.getNumberValue();
if (this.view.chRatio.getValue() === 'checked') {
w = h * this._nRatio;
if (w>this.view.spnWidth.options.maxValue) {
w = this.view.spnWidth.options.maxValue;
h = w/this._nRatio;
this.view.spnHeight.setValue(h, true);
}
this.view.spnWidth.setValue(w, true);
}
if (this.api) {
var props = new Asc.asc_CImgProperty();
props.asc_putWidth(Common.Utils.Metric.fnRecalcToMM(w));
props.asc_putHeight(Common.Utils.Metric.fnRecalcToMM(h));
this.api.asc_setGraphicObjectProps(props);
}
},
ShowCombinedProps: function(type) {
this.view.chartStyles.setVisible(!(type===null || type==Asc.c_oAscChartTypeSettings.comboBarLine || type==Asc.c_oAscChartTypeSettings.comboBarLineSecondary ||
type==Asc.c_oAscChartTypeSettings.comboAreaBar || type==Asc.c_oAscChartTypeSettings.comboCustom));
},
updateChartStyles: function(styles) {
var me = this;
this._isChartStylesChanged = true;
if (styles && styles.length>0){
var stylesStore = this.view.chartStyles.menuPicker.store;
if (stylesStore) {
var stylearray = [];
_.each(styles, function(item, index){
stylearray.push({
imageUrl: item.asc_getImage(),
data : item.asc_getName(),
tip : me.textStyle + ' ' + item.asc_getName()
});
});
stylesStore.reset(stylearray, {silent: false});
}
} else {
this.view.chartStyles.menuPicker.store.reset();
this.view.chartStyles.clearComboView();
}
Common.Utils.lockControls(Common.enumLock.noStyles, !styles || styles.length<1, {array: [this.view.chartStyles]});
},
onSelectStyle: function(combo, record) {
if (this._noApply) return;
if (this.api && !this._noApply && this.chartProps) {
var props = new Asc.asc_CImgProperty();
this.chartProps.putStyle(record.get('data'));
props.asc_putChartProperties(this.chartProps);
this.api.asc_setGraphicObjectProps(props);
}
Common.NotificationCenter.trigger('edit:complete', this);
},
selectCurrentChartStyle: function() {
if (!this.view.chartStyles) return;
this.view.chartStyles.suspendEvents();
var rec = this.view.chartStyles.menuPicker.store.findWhere({data: this._state.ChartStyle});
this.view.chartStyles.menuPicker.selectRecord(rec);
this.view.chartStyles.resumeEvents();
if (this._isChartStylesChanged) {
var currentRecords;
if (rec)
currentRecords = this.view.chartStyles.fillComboView(this.view.chartStyles.menuPicker.getSelectedRec(), true);
else
currentRecords = this.view.chartStyles.fillComboView(this.view.chartStyles.menuPicker.store.at(0), true);
if (currentRecords && currentRecords.length>0) {
var arr = [];
_.each(currentRecords, function(style, index){
arr.push(style.get('data'));
});
return arr;
}
}
},
setApi: function(api) {
if (api) {
this.api = api;
this.api.asc_registerCallback('asc_onCoAuthoringDisconnect',_.bind(this.SetDisabled, this));
Common.NotificationCenter.on('api:disconnect', _.bind(this.SetDisabled, this));
this.api.asc_registerCallback('asc_onSelectionChanged', _.bind(this.onSelectionChanged, this));
this.api.asc_registerCallback('asc_onAddChartStylesPreview', _.bind(this.onAddChartStylesPreview, this));
this.api.asc_registerCallback('asc_onUpdateChartStyles', _.bind(this._onUpdateChartStyles, this));
Common.NotificationCenter.on('cells:range', _.bind(this.onCellsRange, this));
Common.NotificationCenter.on('uitheme:changed', _.bind(this.onThemeChanged, this));
}
return this;
},
onAddChartStylesPreview: function(styles){
if (this._isEditType || !this.view.chartStyles) return;
if (styles && styles.length>0){
var stylesStore = this.view.chartStyles.menuPicker.store;
if (stylesStore) {
_.each(styles, function(item, index){
var rec = stylesStore.findWhere({
data: item.asc_getName()
});
rec && rec.set('imageUrl', item.asc_getImage());
});
}
}
},
_onUpdateChartStyles: function() {
if (this.api && this._state.ChartType!==null && this._state.ChartType>-1 &&
!(this._state.ChartType==Asc.c_oAscChartTypeSettings.comboBarLine || this._state.ChartType==Asc.c_oAscChartTypeSettings.comboBarLineSecondary ||
this._state.ChartType==Asc.c_oAscChartTypeSettings.comboAreaBar || this._state.ChartType==Asc.c_oAscChartTypeSettings.comboCustom)) {
this.updateChartStyles(this.api.asc_getChartPreviews(this._state.ChartType, undefined, true));
this.api.asc_generateChartPreviews(this._state.ChartType, this.selectCurrentChartStyle());
}
},
setMode: function(mode) {
this.appConfig = mode;
return this;
},
setConfig: function(config) {
this.toolbar = config.toolbar;
this.view = this.createView('ChartTab', {
toolbar: config.toolbar.toolbar
});
},
SetDisabled: function(state) {
this.view && this.view.SetDisabled(state);
},
createToolbarPanel: function() {
return this.view.getPanel();
},
getView: function(name) {
return !name && this.view ?
this.view : Backbone.Controller.prototype.getView.call(this, name);
},
onCoAuthoringDisconnect: function() {
this.SetDisabled(true);
},
onSelectionChanged: function(info) {
if (this.rangeSelectionMode || !this.appConfig.isEdit || !this.view || !this.toolbar.toolbar.isTabActive('charttab')) return;
var selectType = info.asc_getSelectionType();
var selectedObjects = this.api.asc_getGraphicObjectProps();
if ((selectType == Asc.c_oAscSelectionType.RangeChart || selectType == Asc.c_oAscSelectionType.RangeChartText) && selectedObjects)
for (var i = 0; i < selectedObjects.length; i++) {
if (selectedObjects[i].asc_getObjectType() == Asc.c_oAscTypeSelectElement.Image) {
var elValue = selectedObjects[i].asc_getObjectValue();
if ( elValue.asc_getChartProperties() ) {
this.ChangeSettings(this.api.asc_getGraphicObjectProps()[i].asc_getObjectValue());
break;
}
}
}
},
onCellsRange: function(status) {
this.rangeSelectionMode = (status != Asc.c_oAscSelectionDialogType.None);
},
onActiveTab: function(tab) {
if (tab==='charttab') {
this.onSelectionChanged(this.api.asc_getCellInfo());
if (this._themeChanged!==false)
this.onThemeChanged();
}
},
onThemeChanged: function() {
this._themeChanged = !(this.toolbar && this.toolbar.toolbar && this.toolbar.toolbar.isTabActive('charttab'));
if (!this._themeChanged && this.view) {
this.view.onThemeChanged();
this.toolbar.toolbar.onThemeChanged();
}
}
}, SSE.Controllers.ChartTab || {}));
});