/* * This work is licensed under the MIT License (MIT) * Copyright (c) 2014-2020 Davide Carpi * https://opensource.org/licenses/MIT */ using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Globalization; using System.Windows.Forms; using System.Xml; using SMath.Controls; using SMath.Drawing; using SMath.Manager; using SMath.Math; using SMath.Math.Numeric; using WriterManager; namespace PieChartRegion { /// /// Pie Chart RegionHolder. /// public class PieChartRegion : RegionHolder, IRegionForViewer { #region private fields private string PrevValues = null; // TNumber fails on boolean comparison defining again a variable already defined #endregion #region public fields public WriterArea WriterArea { get { return canv.WriterArea; } set { canv.WriterArea = value; } } public bool ObjectsFocused { get { return (this.canv.ChartHeader.Focused || this.canv.ChartFooter.Focused || this.canv.ChartLegend.Focused || this.canv.InnerLabels.Focused || this.canv.OuterLabels.Focused); } } #endregion internal SessionProfile Profile { get { return this.SessionProfile; } } #region RegionHolder Members public PieChartRegion(SessionProfile sessionProfile) : base(sessionProfile) { Tools.LogInfo("### PieChart()"); base.DescriptionLocation = ElementPosition.Top; this.Init(); } public PieChartRegion(PieChartRegion region) : base(region) { Tools.LogInfo("### PieChart(region)"); this.Init(); //this.canv.DataPreprocessing(); this.canv.UpdateSliceColors(); this.canv.ChartHeader.WriterArea.UpdateFields(); this.canv.ChartHeader.CalculateSize(); this.canv.ChartFooter.WriterArea.UpdateFields(); this.canv.ChartFooter.CalculateSize(); this.UpdateSizes(); } public override RegionBase Clone() { Tools.LogInfo("### Clone()"); return new PieChartRegion(this); } private void SetCursor(Cursor cursor) { // TODO: Support setting of cursor type //this.Parent.Parent.Cursor = cursor; } private void Init() { base.FixedLayout = true; this.canv.ChartHeader.WriterArea.Parent = this; this.canv.ChartHeader.WriterArea.PropertyChanged += delegate (object sender, PropertyChangedEventArgs e) { if (!e.PropertyName.Equals("CaretBlink")) { if (e.PropertyName.Equals("Size")) { this.canv.ChartHeader.CalculateSize(); this.UpdateSizes(); } else if (e.PropertyName.Equals("Cursor")) { SetCursor(this.canv.ChartHeader.WriterArea.GetCursor()); } this.CreateHistoryStep(e.PropertyName); } this.canv.Invalidate(); }; this.canv.ChartFooter.WriterArea.Parent = this; this.canv.ChartFooter.WriterArea.PropertyChanged += delegate (object sender, PropertyChangedEventArgs e) { if (!e.PropertyName.Equals("CaretBlink")) { if (e.PropertyName.Equals("Size")) { this.canv.ChartFooter.CalculateSize(); this.UpdateSizes(); } else if (e.PropertyName.Equals("Cursor")) { SetCursor(this.canv.ChartHeader.WriterArea.GetCursor()); } this.CreateHistoryStep(e.PropertyName); } this.canv.Invalidate(); }; /* this.canv.DoughnutLabel.WriterArea.Parent = this; this.canv.DoughnutLabel.WriterArea.PropertyChanged += delegate (object sender, PropertyChangedEventArgs e) { if (!e.PropertyName.Equals("CaretBlink")) { if (e.PropertyName.Equals("Size")) { this.UpdateSizes(); } else if (e.PropertyName.Equals("Cursor")) { this.Parent.Parent.Cursor = this.canv.ChartHeader.WriterArea.GetCursor(); } this.CreateHistoryStep(e.PropertyName); } this.canv.Invalidate(); }; */ this.canv.ChartLegend.SeriesWriterArea.Parent = this; this.canv.ChartLegend.SeriesWriterArea.PropertyChanged += delegate (object sender, PropertyChangedEventArgs e) { if (!this.canv.ChartLegend.SeriesWriterArea.Focused) return; if (!e.PropertyName.Equals("CaretBlink")) { if (e.PropertyName.Equals("Cursor")) { SetCursor(this.canv.ChartLegend.SeriesWriterArea.GetCursor()); } else { this.canv.ChartLegend.UpdateLabels(); this.UpdateSizes(); } this.CreateHistoryStep(e.PropertyName); } this.canv.Invalidate(); }; this.canv.ChartLegend.DataWriterArea.Parent = this; this.canv.ChartLegend.DataWriterArea.PropertyChanged += delegate (object sender, PropertyChangedEventArgs e) { if (!this.canv.ChartLegend.DataWriterArea.Focused) return; if (!e.PropertyName.Equals("CaretBlink")) { if (e.PropertyName.Equals("Cursor")) { SetCursor(this.canv.ChartLegend.DataWriterArea.GetCursor()); } else { this.canv.ChartLegend.UpdateLabels(); this.UpdateSizes(); } this.CreateHistoryStep(e.PropertyName); } this.canv.Invalidate(); }; this.canv.ChartLegend.ThresholdWriterArea.Parent = this; this.canv.ChartLegend.ThresholdWriterArea.PropertyChanged += delegate (object sender, PropertyChangedEventArgs e) { if (!this.canv.ChartLegend.ThresholdWriterArea.Focused) return; if (!e.PropertyName.Equals("CaretBlink")) { if (e.PropertyName.Equals("Cursor")) { SetCursor(this.canv.ChartLegend.ThresholdWriterArea.GetCursor()); } else { this.canv.ChartLegend.UpdateLabels(); this.UpdateSizes(); } this.CreateHistoryStep(e.PropertyName); } this.canv.Invalidate(); }; this.canv.InnerLabels.DataWriterArea.Parent = this; this.canv.InnerLabels.DataWriterArea.PropertyChanged += delegate (object sender, PropertyChangedEventArgs e) { if (!this.canv.InnerLabels.DataWriterArea.Focused) return; if (!e.PropertyName.Equals("CaretBlink")) { if (e.PropertyName.Equals("Cursor")) { SetCursor(this.canv.InnerLabels.DataWriterArea.GetCursor()); } else { for (int i = 0; i < this.canv.PieSeries.Count; i++) this.canv.PieSeries[i].InnerLabels.UpdateLabels(); this.UpdateSizes(); } this.CreateHistoryStep(e.PropertyName); } this.canv.Invalidate(); }; this.canv.InnerLabels.ThresholdWriterArea.Parent = this; this.canv.InnerLabels.ThresholdWriterArea.PropertyChanged += delegate (object sender, PropertyChangedEventArgs e) { if (!this.canv.InnerLabels.ThresholdWriterArea.Focused) return; if (!e.PropertyName.Equals("CaretBlink")) { if (e.PropertyName.Equals("Cursor")) { SetCursor(this.canv.InnerLabels.ThresholdWriterArea.GetCursor()); } else { for (int i = 0; i < this.canv.PieSeries.Count; i++) this.canv.PieSeries[i].InnerLabels.UpdateLabels(); this.UpdateSizes(); } this.CreateHistoryStep(e.PropertyName); } this.canv.Invalidate(); }; this.canv.OuterLabels.DataWriterArea.Parent = this; this.canv.OuterLabels.DataWriterArea.PropertyChanged += delegate (object sender, PropertyChangedEventArgs e) { if (!this.canv.OuterLabels.DataWriterArea.Focused) return; if (!e.PropertyName.Equals("CaretBlink")) { if (e.PropertyName.Equals("Cursor")) { SetCursor(this.canv.OuterLabels.DataWriterArea.GetCursor()); } else { for (int i = 0; i < this.canv.PieSeries.Count; i++) this.canv.PieSeries[i].OuterLabels.UpdateLabels(); this.UpdateSizes(); } this.CreateHistoryStep(e.PropertyName); } this.canv.Invalidate(); }; this.canv.OuterLabels.ThresholdWriterArea.Parent = this; this.canv.OuterLabels.ThresholdWriterArea.PropertyChanged += delegate (object sender, PropertyChangedEventArgs e) { if (!this.canv.OuterLabels.ThresholdWriterArea.Focused) return; if (!e.PropertyName.Equals("CaretBlink")) { if (e.PropertyName.Equals("Cursor")) { SetCursor(this.canv.OuterLabels.ThresholdWriterArea.GetCursor()); } else { for (int i = 0; i < this.canv.PieSeries.Count; i++) this.canv.PieSeries[i].OuterLabels.UpdateLabels(); this.UpdateSizes(); } this.CreateHistoryStep(e.PropertyName); } this.canv.Invalidate(); }; base.FocusChanged += delegate(RegionBase sender) { if (!base.Focused) { this.WriterArea = null; this.canv.FocusNone(); } }; base.canv.Resized += delegate(RegionBase sender) { this.PrePainter(); this.Invalidate(); }; } // ~PieChartRegion() // { // this.canv.InfoTimer.Dispose(); // this.canv.ChartLegend.Dispose(); // this.canv.ChartHeader.Dispose(); // this.canv.ChartFooter.Dispose(); // // for (int i = 0; i < this.canv.PieSeries.Count; i++) // this.canv.PieSeries[i].Dispose(); // } public override void OnEvaluation(Store store) { Tools.LogInfo("### OnEvaluation()"); int emptyElement = -1; for (int i = 0; i < base.Terms.Length; i++) { // Variable must be specified if (base.Terms[i].ArgsCount == 0 && base.Terms[i].Text == Symbols.EmptyOperand) { emptyElement = i; break; } } if (emptyElement != -1) { this.Clear(); this.ShowError(new MathException(Errors.EmptyElements), base.Terms[0]); return; } TNumber values; try { values = Computation.NumericCalculation(Entry.Create(base.Terms), store); } catch { this.Clear(); this.ShowError(new MathException(Errors.WrongArguments), Term.Empty); return; } string newValues = values.obj.ToString(); if (!newValues.Equals(this.PrevValues)) { List series = new List(); if (values.obj is TMatrix) { DataSeries ds = ParseData(values.obj); if (ds == null) return; series.Add(ds); } else if (values.obj is TSystem) { TSystem tSys = (TSystem)values.obj; for (int s = 0; s < tSys.matrix.GetLength(0); s++) { if (tSys.matrix[s].obj is TMatrix) { DataSeries ds = ParseData(tSys.matrix[s].obj); if (ds == null) return; series.Add(ds); } else { this.Clear(); this.ShowError(new MathException(Errors.WrongArguments), Term.Empty); return; } } } else { this.Clear(); this.ShowError(new MathException(Errors.WrongArguments), Term.Empty); return; } this.PrevValues = newValues; this.canv.SlicesPriority = new List(); this.canv.LabelsPriority = new List(); bool keepSettings = false; if (series.Count == this.canv.PieSeries.Count) { keepSettings = true; for (int i = 0; i < series.Count; i++) { if (series[i].DataEntries.Count != this.canv.PieSeries[i].DataEntries.Count || !string.Equals(series[i].Theme.Serialize().Replace(" ",""), this.canv.PieSeries[i].Theme.Serialize().Replace(" ",""), StringComparison.InvariantCultureIgnoreCase)) { keepSettings = false; break; } } if (keepSettings) { for (int i = 0; i < series.Count; i++) { for (int j = 0; j < series[i].DataEntries.Count; j++) { series[i].DataEntries[j].ThemeColor = this.canv.PieSeries[i].DataEntries[j].ThemeColor; series[i].DataEntries[j].RadialOffset = this.canv.PieSeries[i].DataEntries[j].RadialOffset; } } } } this.canv.PieSeries = new List(series); this.canv.DataPreprocessing(); if (!keepSettings) this.canv.UpdateThemeColors(); this.canv.UpdateSliceColors(); } if (this.UpdateChartID()) { this.canv.ChartHeader.WriterArea.UpdateFields(); this.canv.ChartHeader.CalculateSize(); this.canv.ChartFooter.WriterArea.UpdateFields(); this.canv.ChartFooter.CalculateSize(); } this.UpdateSizes(); base.OnEvaluation(store); } public override void OnPaint(PaintEventOptions e) { Tools.LogInfo("### OnPaint()"); this.canv.BackColor = this.BackColor; this.canv.Evaluable = GlobalProfile.IsAutoReCalc && this.Evaluable && String.IsNullOrEmpty(this.ErrorText); this.canv.ShowResizeMarker = this.Focused && !this.canv.IsViewerApplication && this.WriterArea == null; base.OnPaint(e); // HACK: fix the size of disabled holder on FixedLayout if (!this.Evaluable) { e.Graphics.FillRectangle(ColorBrushes.Black, e.ClipRectangle.Right - 4, e.ClipRectangle.Y - 2, 6, 6); } } public override void OnCommandSend(string value) { base.OnCommandSend(value); if (this.WriterArea != null && this.WriterArea.Focused) this.WriterArea.OnCommandSend(value); } public override void OnKeyDown(KeyEventOptions e) { Tools.LogInfo("### OnKeyDown()"); base.OnKeyDown(e); if (this.WriterArea != null && this.WriterArea.Focused) { this.WriterArea.OnKeyDown(e); } else if (this.canv.ChartLegend.Focused) { if ((Control.ModifierKeys & Keys.Control) == Keys.Control && (Control.ModifierKeys & Keys.Shift) == Keys.Shift) { if (e.KeyCode == (int)InputKeys.B) { // CTRL+SHIFT+B: border color types this.canv.ChartLegend.BorderColorType = Tools.NextEnum(this.canv.ChartLegend.BorderColorType); this.Invalidate(); } else if (e.KeyCode == (int)InputKeys.C) { // CTRL+SHIFT+C: loop background color types this.canv.ChartLegend.BackgroundColorType = Tools.NextEnum(this.canv.ChartLegend.BackgroundColorType); this.Invalidate(); } else if (e.KeyCode == (int)InputKeys.T) { // CTRL+SHIFT+T: loop text color types this.canv.ChartLegend.TextColorType = Tools.NextEnum(this.canv.ChartLegend.TextColorType); this.Invalidate(); } } } else if (this.canv.ChartHeader.Focused) { if ((Control.ModifierKeys & Keys.Control) == Keys.Control && (Control.ModifierKeys & Keys.Shift) == Keys.Shift) { if (e.KeyCode == (int)InputKeys.B) { // CTRL+SHIFT+B: border color types this.canv.ChartHeader.BorderColorType = Tools.NextEnum(this.canv.ChartHeader.BorderColorType); this.Invalidate(); } else if (e.KeyCode == (int)InputKeys.C) { // CTRL+SHIFT+C: loop background color types this.canv.ChartHeader.BackgroundColorType = Tools.NextEnum(this.canv.ChartHeader.BackgroundColorType); this.Invalidate(); } else if (e.KeyCode == (int)InputKeys.T) { // CTRL+SHIFT+T: loop text color types this.canv.ChartHeader.TextColorType = Tools.NextEnum(this.canv.ChartHeader.TextColorType); this.Invalidate(); } } } else if (this.canv.ChartFooter.Focused) { if ((Control.ModifierKeys & Keys.Control) == Keys.Control && (Control.ModifierKeys & Keys.Shift) == Keys.Shift) { if (e.KeyCode == (int)InputKeys.B) { // CTRL+SHIFT+B: border color types this.canv.ChartFooter.BorderColorType = Tools.NextEnum(this.canv.ChartFooter.BorderColorType); this.Invalidate(); } else if (e.KeyCode == (int)InputKeys.C) { // CTRL+SHIFT+C: loop background color types this.canv.ChartFooter.BackgroundColorType = Tools.NextEnum(this.canv.ChartFooter.BackgroundColorType); this.Invalidate(); } else if (e.KeyCode == (int)InputKeys.T) { // CTRL+SHIFT+T: loop text color types this.canv.ChartFooter.TextColorType = Tools.NextEnum(this.canv.ChartFooter.TextColorType); this.Invalidate(); } } } else if (this.canv.InnerLabels.Focused) { if ((Control.ModifierKeys & Keys.Control) == Keys.Control && (Control.ModifierKeys & Keys.Shift) == Keys.Shift) { if (e.KeyCode == (int)InputKeys.B) { // CTRL+SHIFT+B: loop border color types if (this.canv.InnerLabels.BackgroundColorType != DataLabelsSettings.BackColorTypes.Transparent) { this.canv.InnerLabels.BorderColorType = Tools.NextEnum(this.canv.InnerLabels.BorderColorType); this.Invalidate(); } } else if (e.KeyCode == (int)InputKeys.C) { // CTRL+SHIFT+C: loop background color types this.canv.InnerLabels.BackgroundColorType = Tools.NextEnum(this.canv.InnerLabels.BackgroundColorType); this.Invalidate(); } else if (e.KeyCode == (int)InputKeys.L) { // CTRL+SHIFT+L: loop text layouts this.canv.InnerLabels.TextLayout = Tools.NextEnum(this.canv.InnerLabels.TextLayout); for (int i = 0; i < this.canv.PieSeries.Count; i++) this.canv.PieSeries[i].InnerLabels.UpdateTextLayout(); this.canv.ShowInfo("Inner labels: multiline text layout", String.Format("{0}", this.canv.InnerLabels.TextLayout)); } else if (e.KeyCode == (int)InputKeys.T) { // CTRL+SHIFT+T: loop text color types this.canv.InnerLabels.TextColorType = Tools.NextEnum(this.canv.InnerLabels.TextColorType); this.canv.Invalidate(); } } } else if (this.canv.OuterLabels.Focused) { if ((Control.ModifierKeys & Keys.Control) == Keys.Control && (Control.ModifierKeys & Keys.Shift) == Keys.Shift) { if (e.KeyCode == (int)InputKeys.B) { // CTRL+SHIFT+B: loop border color types if (this.canv.OuterLabels.BackgroundColorType != DataLabelsSettings.BackColorTypes.Transparent) { this.canv.OuterLabels.BorderColorType = Tools.NextEnum(this.canv.OuterLabels.BorderColorType); this.Invalidate(); } } else if (e.KeyCode == (int)InputKeys.C) { // CTRL+SHIFT+C: loop background color types this.canv.OuterLabels.BackgroundColorType = Tools.NextEnum(this.canv.OuterLabels.BackgroundColorType); this.Invalidate(); } else if (e.KeyCode == (int)InputKeys.L) { // CTRL+SHIFT+L: loop text layouts this.canv.OuterLabels.TextLayout = Tools.NextEnum(this.canv.OuterLabels.TextLayout); for (int i = 0; i < this.canv.PieSeries.Count; i++) this.canv.PieSeries[i].OuterLabels.UpdateTextLayout(); this.canv.ShowInfo("Outer labels: multiline text layout", String.Format("{0}", this.canv.OuterLabels.TextLayout)); } else if (e.KeyCode == (int)InputKeys.T) { // CTRL+SHIFT+T: loop text color types this.canv.OuterLabels.TextColorType = Tools.NextEnum(this.canv.OuterLabels.TextColorType); this.canv.Invalidate(); } else if (e.KeyCode == (int)InputKeys.V) { // CTRL+SHIFT+V: loop vertical positions this.canv.OuterLabels.VerticalPosition = Tools.NextEnum(this.canv.OuterLabels.VerticalPosition); this.UpdateSizes(); this.canv.ShowInfo("Outer labels: label position", String.Format("{0}", this.canv.OuterLabels.VerticalPosition)); } } } else { // CTRL+SHIFT instead of CTRL to bypass the e.Handled that can't override the global hotkeys. if ((Control.ModifierKeys & Keys.Control) == Keys.Control && (Control.ModifierKeys & Keys.Shift) == Keys.Shift) { if (e.KeyCode == (int)InputKeys.A) { // CTRL+SHIFT+A: loop opacity values this.canv.FillOpacity -= 51; this.canv.FillOpacity = (this.canv.FillOpacity < 0) ? 255 : this.canv.FillOpacity; this.canv.UpdateSliceColors(); this.canv.ShowInfo("Opacity", String.Format("{0}/255", this.canv.FillOpacity)); } else if (e.KeyCode == (int)InputKeys.B) { // CTRL+SHIFT+B: canvas border ON/OFF this.canv.Border = !this.canv.Border; this.Invalidate(); } else if (e.KeyCode == (int)InputKeys.C) { // CTRL+SHIFT+C: loop fill colors types this.canv.FillColorType = Tools.NextEnum(this.canv.FillColorType); this.canv.UpdateSliceColors(); this.Invalidate(); } else if (e.KeyCode == (int)InputKeys.E) { // CTRL+SHIFT+E: loop edge color types this.canv.EdgeColorType = Tools.NextEnum(this.canv.EdgeColorType); this.canv.UpdateSliceColors(); this.Invalidate(); } else if (e.KeyCode == (int)InputKeys.F) { // CTRL+SHIFT+F: show or hide the footer. this.canv.ChartFooter.Visible = !this.canv.ChartFooter.Visible; this.UpdateSizes(); } else if (e.KeyCode == (int)InputKeys.H) { // CTRL+SHIFT+H: show or hide the header. this.canv.ChartHeader.Visible = !this.canv.ChartHeader.Visible; this.UpdateSizes(); } else if (e.KeyCode == (int)InputKeys.I) { // CTRL+SHIFT+I: show or hide the inner labels. this.canv.InnerLabels.Visible = !this.canv.InnerLabels.Visible; this.Invalidate(); } else if (e.KeyCode == (int)InputKeys.L) { // CTRL+SHIFT+L: show or hide the legend. this.canv.ChartLegend.Visible = !this.canv.ChartLegend.Visible; this.UpdateSizes(); } else if (e.KeyCode == (int)InputKeys.O) { // CTRL+SHIFT+O: show or hide the outer labels. this.canv.OuterLabels.Visible = !this.canv.OuterLabels.Visible; this.UpdateSizes(); } else if (e.KeyCode == (int)InputKeys.R) { // CTRL+SHIFT+R: reseed theme colors. this.canv.UpdateThemeColors(); this.canv.UpdateSliceColors(); this.canv.Invalidate(); } else if (e.KeyCode == (int)InputKeys.S) { // CTRL+SHIFT+S: loop 3D shapes. this.canv.Pie3DShape = Tools.NextEnum(this.canv.Pie3DShape); this.canv.GenerateSlicePaths(); this.canv.ShowInfo("3D shapes", String.Format("{0}", this.canv.Pie3DShape)); } else if (e.KeyCode == (int)InputKeys.T) { // CTRL+SHIFT+T: loop themes. int i = ChartThemes.Themes.FindIndex(x => x == this.canv.Theme); this.canv.Theme = ChartThemes.Themes[((i == ChartThemes.Themes.Count - 1) ? 0 : i + 1)]; this.canv.UpdateThemeColors(); this.canv.UpdateSliceColors(); this.canv.ShowInfo("Theme", this.canv.Theme.ToString()); } // else if (e.KeyCode == (int)InputKeys.X) // { // // CTRL+SHIFT+X: transparent/white canvas' background. // this.canv.BackgroundColorType = (this.canv.BackgroundColorType == CanvasColorTypes.Transparent) ? CanvasColorTypes.Custom : CanvasColorTypes.Transparent; // this.canv.Invalidate(); // } } } // HACK: history vs CTRL+something using (this.Parent.History.CreateTransaction()) { this.CreateHistoryStep("OnMouseDown"); } } public override void OnKeyUp(KeyEventOptions e) { base.OnKeyUp(e); if (this.WriterArea != null && this.WriterArea.Focused) this.WriterArea.OnKeyUp(e); } public override void OnMouseDown(MouseEventOptions e) { base.OnMouseDown(e); if (this.canv.ShowSettingsDialog) { var settingsDialog = new Settings.FormSettings(this.canv); settingsDialog.StartPosition = FormStartPosition.CenterParent; var historyStep = this.GetHistoryStep(); if (settingsDialog.ShowDialog() == DialogResult.OK) { //if (settingsDialog.SaveDefaultSettings) // this.SettingsSave(); this.CreateHistoryStep("SettingsDialog"); } else { historyStep.Apply(this); } this.canv.ShowSettingsDialog = false; } } public override void OnMouseMove(MouseEventOptions e) { base.OnMouseMove(e); if (this.WriterArea != null && this.WriterArea.Focused && !this.WriterArea.ReadOnly) SetCursor(this.WriterArea.GetCursor()); } public override void FromXml(StorageReader storage, FileParsingContext parsingContext) { Tools.LogInfo("### FromXml()"); // base.FromXml(reader, parsingContext);return; this.canv.ChartID = storage.Properties.GetInt32("id"); List series = new List(); var reader = storage.GetXmlReader(); string root = reader.Name; while ( reader.Read() ) { reader.MoveToContent(); if (reader.NodeType == XmlNodeType.Element) { if (reader.Name == "geometry") { this.canv.PieOrientation2D = Int32.Parse(reader.GetAttribute("orientation2D").TrimEnd('°')); this.canv.PieOrientation3D = Int32.Parse(reader.GetAttribute("orientation3D").TrimEnd('°')); if (!string.IsNullOrEmpty(reader.GetAttribute("pieCentralAngle"))) this.canv.PieCentralAngle = Int32.Parse(reader.GetAttribute("pieCentralAngle").TrimEnd('°')); this.canv.PieDiameter = Int32.Parse(reader.GetAttribute("pieDiameter").Replace("px","")); this.canv.PiePaddingRatio = Double.Parse(reader.GetAttribute("piePadding").TrimEnd('%'), NumberStyles.Number, CultureInfo.InvariantCulture) / 100; this.canv.DoughnutHoleRatio = Double.Parse(reader.GetAttribute("doughnutHole").TrimEnd('%'), NumberStyles.Number, CultureInfo.InvariantCulture) / 100; this.canv.PieThicknessRatio = Double.Parse(reader.GetAttribute("pieTickness").TrimEnd('%'), NumberStyles.Number, CultureInfo.InvariantCulture) / 100; this.canv.Pie3DShape = (PieChartCanvas.Pie3DShapes) Enum.Parse(typeof(PieChartCanvas.Pie3DShapes), reader.GetAttribute("pie3DShape"), true); this.canv.SeriesHoleRatio = Double.Parse(reader.GetAttribute("seriesHole").TrimEnd('%'), NumberStyles.Number, CultureInfo.InvariantCulture) / 100; this.canv.SeriesGapRatio = Double.Parse(reader.GetAttribute("seriesGap").TrimEnd('%'), NumberStyles.Number, CultureInfo.InvariantCulture) / 100; this.canv.SeriesInnerIsPie = Convert.ToBoolean(reader.GetAttribute("seriesInner")); } else if (reader.Name == "style") { this.canv.Border = Convert.ToBoolean(reader.GetAttribute("canvasBorder")); this.canv.BackgroundColorType = (CanvasColorTypes) Enum.Parse(typeof(CanvasColorTypes), reader.GetAttribute("canvasBgType"), true); this.canv.BackgroundColor = ColorTranslator.FromHtml(reader.GetAttribute("canvasBgColor")); this.canv.Theme = ChartThemes.Deserialize(reader.GetAttribute("theme")); this.canv.EdgeColorType = (EdgeColorTypes) Enum.Parse(typeof(EdgeColorTypes), reader.GetAttribute("edgeColorType"), true); this.canv.EdgeThickness = Int32.Parse(reader.GetAttribute("edgeWidth").Replace("px","")); this.canv.FillColorType = (FillColorTypes) Enum.Parse(typeof(FillColorTypes), reader.GetAttribute("fillColorType"), true); this.canv.FillOpacity = Int32.Parse(reader.GetAttribute("fillOpacity")); this.canv.RimDecorationRatio = float.Parse(reader.GetAttribute("rimDecoration").TrimEnd('%'), NumberStyles.Float, CultureInfo.InvariantCulture) / 100; } else if (reader.Name == "threshold") { string dt = reader.GetAttribute("data"); if (dt.EndsWith("%", StringComparison.InvariantCultureIgnoreCase)) { this.canv.DataThresholdOnPercents = true; this.canv.DataThreshold = Double.Parse(dt.TrimEnd('%'), NumberStyles.Number, CultureInfo.InvariantCulture) / 100; } else { this.canv.DataThresholdOnPercents = false; this.canv.DataThreshold = Double.Parse(dt, NumberStyles.Number, CultureInfo.InvariantCulture); } string lt = reader.GetAttribute("labels"); if (lt.EndsWith("%", StringComparison.InvariantCultureIgnoreCase)) { this.canv.LabelsThresholdOnPercents = true; this.canv.LabelsThreshold = Double.Parse(lt.TrimEnd('%'), NumberStyles.Number, CultureInfo.InvariantCulture) / 100; } else { this.canv.LabelsThresholdOnPercents = false; this.canv.LabelsThreshold = Double.Parse(lt, NumberStyles.Number, CultureInfo.InvariantCulture); } this.canv.ThresholdSliceColor = ColorTranslator.FromHtml(reader.GetAttribute("color")); this.canv.ThresholdSliceOffset = float.Parse(reader.GetAttribute("offset").TrimEnd('%'), NumberStyles.Float, CultureInfo.InvariantCulture) / 100; } else if (reader.Name == "series") { DataSeries ds = new DataSeries(reader.GetAttribute("title"), ChartThemes.Deserialize(reader.GetAttribute("theme"))){ UoM = reader.GetAttribute("units") }; while ( reader.Read() ) { if (reader.NodeType == XmlNodeType.Element && reader.Name == "data") { double value = Double.Parse(reader.GetAttribute("value"), NumberStyles.Number, CultureInfo.InvariantCulture); Color color = (reader.GetAttribute("color") != null) ? ColorTranslator.FromHtml(reader.GetAttribute("color")) : Color.Empty; Color theme = ColorTranslator.FromHtml(reader.GetAttribute("theme")); float offset = float.Parse(reader.GetAttribute("offset").TrimEnd('%'), NumberStyles.Float, CultureInfo.InvariantCulture) / 100; ds.AddEntry(reader.GetAttribute("tag"), value, color, theme, offset); } else if (reader.NodeType == XmlNodeType.EndElement && reader.Name == "series") { break; } } series.Add(ds); } else if (reader.Name == "legend") { this.canv.ChartLegend.FromXml(reader); } else if (reader.Name == "header") { this.canv.ChartHeader.FromXml(reader); } else if (reader.Name == "footer") { this.canv.ChartFooter.FromXml(reader); } else if (reader.Name == "innerLabels") { this.canv.InnerLabels.FromXml(reader); } else if (reader.Name == "outerLabels") { this.canv.OuterLabels.FromXml(reader); } } // HACK: an exit on "root" triggers an exception else if (reader.NodeType == XmlNodeType.EndElement && reader.Name == "outerLabels") { break; } } this.canv.UpdatePieSizes(); this.canv.PieSeries = new List(series); this.canv.DataPreprocessing(); this.canv.UpdateSliceColors(); this.UpdateSizes(); base.FromXml(storage, parsingContext); } public override void ToXml(StorageWriter storage, FileParsingContext parsingContext) { Tools.LogInfo("### ToXml()"); storage.Properties.SetInt32("id", this.canv.ChartID); var writer = storage.GetXmlWriter(); // chart geometry writer.WriteStartElement("geometry"); writer.WriteAttributeString("orientation2D", String.Format(CultureInfo.InvariantCulture, "{0}°", this.canv.PieOrientation2D)); writer.WriteAttributeString("orientation3D", String.Format(CultureInfo.InvariantCulture, "{0}°", this.canv.PieOrientation3D)); if (this.canv.PieCentralAngle < 360) writer.WriteAttributeString("pieCentralAngle", String.Format(CultureInfo.InvariantCulture, "{0}°", this.canv.PieCentralAngle)); writer.WriteAttributeString("pieDiameter", String.Format(CultureInfo.InvariantCulture, "{0}px", this.canv.PieDiameter)); writer.WriteAttributeString("piePadding", String.Format(CultureInfo.InvariantCulture, "{0}%", (this.canv.PiePaddingRatio * 100))); writer.WriteAttributeString("doughnutHole", String.Format(CultureInfo.InvariantCulture, "{0}%", (this.canv.DoughnutHoleRatio * 100))); writer.WriteAttributeString("pieTickness", String.Format(CultureInfo.InvariantCulture, "{0}%", (this.canv.PieThicknessRatio * 100))); writer.WriteAttributeString("pie3DShape", this.canv.Pie3DShape.ToString()); writer.WriteAttributeString("seriesHole", String.Format(CultureInfo.InvariantCulture, "{0}%", (this.canv.SeriesHoleRatio * 100))); writer.WriteAttributeString("seriesGap", String.Format(CultureInfo.InvariantCulture, "{0}%", (this.canv.SeriesGapRatio * 100))); writer.WriteAttributeString("seriesInner", this.canv.SeriesInnerIsPie.ToString()); writer.WriteEndElement(); // chart style writer.WriteStartElement("style"); writer.WriteAttributeString("canvasBorder", this.canv.Border.ToString().ToLowerInvariant()); writer.WriteAttributeString("canvasBgType", this.canv.BackgroundColorType.ToString()); writer.WriteAttributeString("canvasBgColor", ColorTranslator.ToHtml(this.canv.BackgroundColor)); writer.WriteAttributeString("theme", this.canv.Theme.Serialize()); writer.WriteAttributeString("edgeWidth", this.canv.EdgeThickness.ToString()); writer.WriteAttributeString("edgeColorType", this.canv.EdgeColorType.ToString()); writer.WriteAttributeString("fillColorType", this.canv.FillColorType.ToString()); writer.WriteAttributeString("fillOpacity", this.canv.FillOpacity.ToString(CultureInfo.InvariantCulture)); writer.WriteAttributeString("rimDecoration", String.Format(CultureInfo.InvariantCulture, "{0}%", (this.canv.RimDecorationRatio * 100))); writer.WriteEndElement(); // chart threshold writer.WriteStartElement("threshold"); writer.WriteAttributeString("data", this.canv.DataThresholdOnPercents ? String.Format(CultureInfo.InvariantCulture, "{0}%", (this.canv.DataThreshold * 100)) : this.canv.DataThreshold.ToString(CultureInfo.InvariantCulture)); writer.WriteAttributeString("labels", this.canv.LabelsThresholdOnPercents ? String.Format(CultureInfo.InvariantCulture, "{0}%", (this.canv.LabelsThreshold * 100)) : this.canv.LabelsThreshold.ToString(CultureInfo.InvariantCulture)); writer.WriteAttributeString("color", ColorTranslator.ToHtml(this.canv.ThresholdSliceColor)); writer.WriteAttributeString("offset", String.Format(CultureInfo.InvariantCulture, "{0}%", this.canv.ThresholdSliceOffset)); writer.WriteEndElement(); // Series for (int i = 0; i < this.canv.PieSeries.Count; i++) { writer.WriteStartElement("series"); writer.WriteAttributeString("id", i.ToString()); writer.WriteAttributeString("title", this.canv.PieSeries[i].Title); writer.WriteAttributeString("theme", this.canv.PieSeries[i].Theme.Serialize()); writer.WriteAttributeString("units", this.canv.PieSeries[i].UoM); for (int j = 0; j < this.canv.PieSeries[i].DataEntries.Count; j++) { writer.WriteStartElement("data"); writer.WriteAttributeString("tag", this.canv.PieSeries[i].DataEntries[j].Tag); writer.WriteAttributeString("value", this.canv.PieSeries[i].DataEntries[j].Value.ToString(CultureInfo.InvariantCulture)); // TODO: if (!this.canv.PieSeries[i].DataEntries[j].UserColor.IsEmpty) writer.WriteAttributeString("color", Tools.ColorToHtml(this.canv.PieSeries[i].DataEntries[j].UserColor)); writer.WriteAttributeString("theme", ColorTranslator.ToHtml(this.canv.PieSeries[i].DataEntries[j].ThemeColor)); writer.WriteAttributeString("offset", String.Format(CultureInfo.InvariantCulture, "{0}%", (this.canv.PieSeries[i].DataEntries[j].RadialOffset * 100))); writer.WriteEndElement(); } writer.WriteEndElement(); } // chart legend writer.WriteStartElement("legend"); this.canv.ChartLegend.ToXml(writer); writer.WriteEndElement(); // chart header writer.WriteStartElement("header"); this.canv.ChartHeader.ToXml(writer); writer.WriteEndElement(); // chart footer writer.WriteStartElement("footer"); this.canv.ChartFooter.ToXml(writer); writer.WriteEndElement(); // inner labels writer.WriteStartElement("innerLabels"); this.canv.InnerLabels.ToXml(writer); writer.WriteEndElement(); // outer labels writer.WriteStartElement("outerLabels"); this.canv.OuterLabels.ToXml(writer); writer.WriteEndElement(); base.ToXml(storage, parsingContext); } public override bool Copy(IClipboardManager clipboard) { if (this.WriterArea != null && this.WriterArea.Focused) return this.WriterArea.Copy(clipboard); return base.Copy(clipboard); } public override bool Paste(IClipboardManager clipboard) { if (this.WriterArea != null && this.WriterArea.Focused) return this.WriterArea.Paste(clipboard); return base.Paste(clipboard); } public override CursorType GetCursorType(MouseEventOptions e) { if (this.WriterArea != null && this.WriterArea.Focused)// && this.WriterArea.Bounds.Contains(e.Location)) return this.WriterArea.GetCursorType(e); // if (_ResizeMarker.Contains(e.Location.X, e.Location.Y)) // return CursorType.SizeNWSE; return base.GetCursorType(e); } public override RegionSelectionStatus GetSelectionStatus() { if (this.WriterArea != null && this.WriterArea.Focused) return this.WriterArea.GetSelectionStatus(); return base.GetSelectionStatus(); } public override HistoryStepBase GetHistoryStep() { return new PieChartHistoryStep(this); } public void Clear() { this.PrevValues = null; this.canv.Clear(); this.UpdateSizes(); } public void UpdateSizes() { Tools.LogInfo("### UpdateSize()"); this.canv.UpdateSizes(); this.PrePainter(); this.Invalidate(); } public bool UpdateChartID() { RegionsEnumerator enumerator = this.Parent.GetRegionsEnumerator(); enumerator.FilterNonEvaluable = true; enumerator.FilterInvisible = true; int n = 0; while (enumerator.MoveNext()) { if (enumerator.Current is PieChartRegion) { if (((PieChartRegion)enumerator.Current).ErrorId == Errors.NoError) n++; if (enumerator.Current == this) break; } } if (this.canv.ChartID != n) { this.canv.ChartID = n; return true; } return false; } private DataSeries ParseData(BaseEntry data) { if (!(data is TMatrix)) { this.Clear(); this.ShowError(new MathException(Errors.WrongArguments), Term.Empty); return null; } TMatrix tMat = (TMatrix)data; int rows = tMat.unit.GetLength(0); int cols = tMat.unit.GetLength(1); string title = String.Empty; string theme = String.Empty; if (rows == 1 && cols > 1) { if (tMat[0, 0].obj is TMatrix) { if (cols > 1 && tMat[0, 1].obj is TDouble && ((TDouble)tMat[0, 1].obj).isText) title = TermsConverter.DecodeText(((TDouble)tMat[0, 1].obj).Text.Trim(Symbols.StringChar[0])); if (cols > 2 && tMat[0, 2].obj is TDouble && ((TDouble)tMat[0, 2].obj).isText) theme = TermsConverter.DecodeText(((TDouble)tMat[0, 2].obj).Text.Trim(Symbols.StringChar[0])); tMat = (TMatrix)tMat[0, 0].obj; rows = tMat.unit.GetLength(0); cols = tMat.unit.GetLength(1); } /* // FIXME: old approach, deprecated (numbers in the rightmost placeholder) // SUPPRESSED on JAN 2024 else if (tMat[0, 0].obj is TDouble && ((TDouble)tMat[0, 0].obj).isText) { if (!(tMat[0, cols - 1].obj is TMatrix)) { this.Clear(); this.ShowError(new MathException(Errors.WrongArguments), Term.Empty); return null; } title = ((TDouble)tMat[0, 0].obj).Text.Trim(Symbols.StringChar[0]); if (cols == 2) { tMat = (TMatrix)tMat[0, 1].obj; } else if (cols == 3 && tMat[0, 1].obj is TDouble && ((TDouble)tMat[0, 1].obj).isText) { theme = ((TDouble)tMat[0, 1].obj).Text.Trim(Symbols.StringChar[0]); tMat = (TMatrix)tMat[0, 2].obj; } else { this.Clear(); this.ShowError(new MathException(Errors.WrongArguments), Term.Empty); return null; } rows = tMat.unit.GetLength(0); cols = tMat.unit.GetLength(1); } */ else { this.Clear(); this.ShowError(new MathException(Errors.WrongArguments), Term.Empty); return null; } } DataSeries ds = new DataSeries(title, ChartThemes.Deserialize(theme)); string units = String.Empty; for (int row = 0; row < rows; row++) { double rValue; try { rValue = tMat[row, 0].obj.ToDouble(); } catch { this.Clear(); this.ShowError(new MathException(Errors.CanNotParse), Term.Empty); return null; } string rTag = (cols > 1) ? TermsConverter.DecodeText(tMat[row, 1].obj.ToString().Trim(Symbols.StringChar[0])) : "", rColor = (cols > 2) ? TermsConverter.DecodeText(tMat[row, 2].obj.ToString().Trim(Symbols.StringChar[0])) : "", rUnits = tMat[row, 0].obj.Units.ToString(); if (rValue < 0 || double.IsNaN(rValue)) { this.Clear(); this.ShowError(new MathException(Errors.ArgumentMustBePositiveInteger), Term.Empty); return null; } if (units != rUnits && units != String.Empty && units != "0" && rValue != 0) { this.Clear(); this.ShowError(new MathException(Errors.UnitsDontMatch), Term.Empty); return null; } Color color = GlobalParams.GetColorByName(rColor); if (rColor.ToLowerInvariant() == ":hide:") { color = Color.Transparent; } else if (cols < 3 || rColor.Length < 3 || (rColor.ToLower() != "black" && !rColor.EndsWith("000000") && color.A == 255 && color.R == 0 && color.G == 0 && color.B == 0)) { color = Color.Empty; } ds.AddEntry(rTag, rValue, color); units = (rValue == 0 && units == String.Empty) ? "0" : rUnits; } ds.UoM = (units == "0") ? "1" : units; return ds; } #endregion #region IRegionForViewer Members public bool CreateControlContent(Control control, ref bool showOrdinal) { this.canv.IsViewerApplication = true; if (this.Evaluable && this.ErrorId == Errors.NoError) { this.InputDataVisibility = false; return false; } else { // Hide the control control.Size = new Size(0,0); control.Height -= 5; return true; } } public void UpdateLayout() { // do nothing } #endregion } }