#region License Information /* HeuristicLab * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL) * * This file is part of HeuristicLab. * * HeuristicLab is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * HeuristicLab is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with HeuristicLab. If not, see . */ #endregion using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Imaging; using System.Linq; using System.Windows.Forms; namespace HeuristicLab.Visualization { public partial class ChartControl : UserControl { protected Bitmap Picture; protected bool RenderingRequired; protected Dictionary Modes = new Dictionary(); protected Stack ModeStack = new Stack(); protected List Shortcuts = new List(); protected ChartMode.Shortcut ActiveShortcut; protected bool SuppressEvents { get; set; } private bool SuppressRender { get; set; } public bool ShowToolBar { get { return !splitContainer.Panel1Collapsed; } set { splitContainer.Panel1Collapsed = !value; } } private IChart chart; public IChart Chart { get { return chart; } protected set { if (chart != null) DeregisterChartEvents(chart); chart = value; chart.Enabled = Enabled; pictureBox.Enabled = Enabled; if (chart != null) RegisterChartEvents(chart); } } private ChartMode mode; private ChartMode defaultMode; public ChartMode Mode { get { return mode; } set { if (mode == value) return; mode = value; if (defaultMode == null) defaultMode = mode; if (mode == null) mode = defaultMode; SetModeButtonCheckedState(); SetModeMenuItemCheckedState(); SetPictureBoxCursor(GetPictureBoxCursor()); OnModeChanged(); if (chart != null) { GenerateImage(); } } } public SmoothingMode SmoothingMode { get; set; } public bool ScaleOnResize { get; set; } public bool IsRenderingPaused { get { return SuppressRender; } } public ChartControl() : this(null) { } public ChartControl(IChart defaultChart) { InitializeComponent(); ScaleOnResize = true; Chart = defaultChart ?? new Chart(0, 0, Width, Height); } public void AddChartModes(params ChartMode[] chartModes) { foreach (var chartMode in chartModes) { Modes.Add(chartMode.GetType(), chartMode); foreach (var shortcut in chartMode.Shortcuts) Shortcuts.Add(shortcut); } ReloadModeToolBar(); ReloadModeContextMenu(); } public void SetTempChartMode(ChartMode chartMode) { if (Mode == chartMode) return; ModeStack.Push(mode); Mode = chartMode; } public void ResetTempChartMode() { if (ModeStack.Any()) Mode = ModeStack.Pop(); } public void SuspendRendering() { SuppressRender = true; } public void ResumeRendering() { SuppressRender = false; if (Chart != null) GenerateImage(); } public void RefreshPicture() { pictureBox.Refresh(); } public void UpdatePicture() { pictureBox.Update(); } public Graphics CreatePictureGraphics() { return pictureBox.CreateGraphics(); } public System.Drawing.Rectangle GetPictureBounds() { return pictureBox.Bounds; } protected virtual void ReloadModeToolBar() { splitContainer.Panel1.Controls.Clear(); var chartModes = Modes.Values.ToList(); for (int i = 0; i < chartModes.Count; i++) { var chartMode = chartModes[i]; var rb = CreateChartModeRadioButton(chartMode); rb.Location = new Point(3, 3 + i * (rb.Height + rb.Margin.Vertical)); rb.TabIndex = i; toolTip.SetToolTip(rb, chartMode.ToolTipText); splitContainer.Panel1.Controls.Add(rb); } SetModeButtonCheckedState(); } protected virtual void ReloadModeContextMenu() { modeToolStripMenuItem.DropDownItems.Clear(); foreach (var chartMode in Modes.Values) { var mi = CreateChartModeMenuItem(chartMode); modeToolStripMenuItem.DropDownItems.Add(mi); } SetModeMenuItemCheckedState(); } protected virtual void SetModeButtonCheckedState() { foreach (var rb in splitContainer.Panel1.Controls.OfType()) rb.Checked = rb.Tag == Mode; } protected virtual void SetModeMenuItemCheckedState() { foreach (var mi in modeToolStripMenuItem.DropDownItems.OfType()) mi.Checked = mi.Tag == Mode; } protected virtual void SetPictureBoxCursor(Cursor cursor) { if (pictureBox.Cursor != cursor) pictureBox.Cursor = cursor; } protected virtual Cursor GetPictureBoxCursor() { return mode != null ? mode.Cursor : null; } protected virtual void RegisterChartEvents(IChart chart) { chart.RedrawRequired += ChartOnUpdateRequired; } protected virtual void DeregisterChartEvents(IChart chart) { chart.RedrawRequired -= ChartOnUpdateRequired; } protected virtual void ChartOnUpdateRequired(object sender, EventArgs e) { if (SuppressRender) return; GenerateImage(); } #region PictureBox Events protected virtual void PictureBoxOnSizeChanged(object sender, EventArgs e) { if (Chart == null) return; if (ScaleOnResize) { if ((pictureBox.Width > 0) && (pictureBox.Height > 0) && (Chart != null)) { var point = Chart.TransformPixelToWorld(new Point(pictureBox.Width, Chart.SizeInPixels.Height - pictureBox.Height)); var diff = Chart.UpperRight - point; Chart.SetPosition(new PointD(Chart.LowerLeft.X, Chart.LowerLeft.Y), new PointD(Chart.UpperRight.X - diff.DX, Chart.UpperRight.Y - diff.DY)); } } GenerateImage(); } protected virtual void PictureBoxOnVisibleChanged(object sender, EventArgs e) { if (pictureBox.Visible && RenderingRequired) GenerateImage(); } protected virtual void PictureBoxOnMouseClick(object sender, MouseEventArgs e) { if (mode != null) mode.HandleOnMouseClick(sender, e); } protected virtual void PictureBoxOnMouseDoubleClick(object sender, MouseEventArgs e) { if (mode != null) mode.HandleOnMouseDoubleClick(sender, e); } protected virtual void OnKeyDown(object sender, KeyEventArgs e) { if (mode != null) { mode.HandleOnKeyDown(sender, e); if (e.Handled || ActiveShortcut != null) return; foreach (var shortcut in Shortcuts) { if (e.KeyCode == shortcut.Key && e.Modifiers == shortcut.Modifiers) { ActiveShortcut = shortcut; var downAction = shortcut.DownAction; if (downAction != null) downAction(); e.Handled = true; break; } } } } protected virtual void OnKeyUp(object sender, KeyEventArgs e) { if (mode != null) { mode.HandleOnKeyUp(sender, e); if (e.Handled || ActiveShortcut == null) return; var modifiers = new List(from key in Enum.GetValues(typeof(Keys)).Cast() where e.Modifiers.HasFlag(key) select key); if (ActiveShortcut.Key == e.KeyCode || ActiveShortcut.GetModifiers().Except(modifiers).Any()) { var upAction = ActiveShortcut.UpAction; if (upAction != null) upAction(); ActiveShortcut = null; e.Handled = true; } } } protected virtual void PictureBoxOnMouseWheel(object sender, MouseEventArgs e) { if (mode != null) mode.HandleOnMouseWheel(sender, e); } protected virtual void PictureBoxOnMouseDown(object sender, MouseEventArgs e) { if (mode != null) mode.HandleOnMouseDown(sender, e); } protected virtual void PictureBoxOnMouseUp(object sender, MouseEventArgs e) { if (mode != null) mode.HandleOnMouseUp(sender, e); if (Chart == null) return; if (e.Button == MouseButtons.Right) { propertiesToolStripMenuItem.Enabled = Chart.Group.SelectedPrimitives.Count() == 1; oneLayerUpToolStripMenuItem.Enabled = Chart.Group.SelectedPrimitives.Count() == 1; oneLayerDownToolStripMenuItem.Enabled = Chart.Group.SelectedPrimitives.Count() == 1; intoForegroundToolStripMenuItem.Enabled = Chart.Group.SelectedPrimitives.Count() == 1; intoBackgroundToolStripMenuItem.Enabled = Chart.Group.SelectedPrimitives.Count() == 1; } } protected virtual void PictureBoxOnMouseMove(object sender, MouseEventArgs e) { if (InvokeRequired) { Invoke((Action)PictureBoxOnMouseMove, sender, e); } else { var toolTipText = Chart.GetToolTipText(e.Location); if (toolTip.GetToolTip(pictureBox) != toolTipText) toolTip.SetToolTip(pictureBox, toolTipText); if (mode != null) mode.HandleOnMouseMove(sender, e); SetPictureBoxCursor(GetPictureBoxCursor()); } } protected virtual void PictureBoxOnMouseEnter(object sender, EventArgs e) { if (InvokeRequired) { Invoke((Action)PictureBoxOnMouseEnter, sender, e); return; } if (!Focused) pictureBox.Focus(); if (mode != null) mode.HandleOnMouseEnter(sender, e); } protected virtual void PictureBoxOnMouseLeave(object sender, EventArgs e) { if (mode != null) mode.HandleOnMouseLeave(sender, e); } protected virtual void PictureBoxOnClick(object sender, EventArgs e) { if (InvokeRequired) { Invoke((Action)PictureBoxOnClick, sender, e); return; } if (!Focused) pictureBox.Focus(); if (mode != null) mode.HandleOnClick(sender, e); } #endregion protected virtual void PictureBoxContextMenuStripOnOpening(object sender, CancelEventArgs e) { } protected virtual void oneLayerUpToolStripMenuItem_Click(object sender, EventArgs e) { if (Chart == null) return; if (Chart.Group.SelectedPrimitives.Count() == 1) { Chart.OneLayerUp(Chart.Group.SelectedPrimitives.First()); } } protected virtual void oneLayerDownToolStripMenuItem_Click(object sender, EventArgs e) { if (Chart == null) return; if (Chart.Group.SelectedPrimitives.Count() == 1) { Chart.OneLayerDown(Chart.Group.SelectedPrimitives.First()); } } protected virtual void intoForegroundToolStripMenuItem_Click(object sender, EventArgs e) { if (Chart == null) return; if (Chart.Group.SelectedPrimitives.Count() == 1) { Chart.IntoForeground(Chart.Group.SelectedPrimitives.First()); } } protected virtual void intoBackgroundToolStripMenuItem_Click(object sender, EventArgs e) { if (Chart == null) return; if (Chart.Group.SelectedPrimitives.Count() == 1) { Chart.IntoBackground(Chart.Group.SelectedPrimitives.First()); } } protected virtual void propertiesToolStripMenuItem_Click(object sender, EventArgs e) { if (Chart == null) return; if (Chart.Group.SelectedPrimitives.Count() == 1) { using (var dialog = new PropertiesDialog(Chart.Group.SelectedPrimitives.First())) { dialog.ShowDialog(this); } } } protected virtual void saveImageToolStripMenuItem_Click(object sender, EventArgs e) { if (Picture == null) return; if (saveFileDialog.ShowDialog() == DialogResult.OK) { var format = ImageFormat.Bmp; var filename = saveFileDialog.FileName.ToLower(); if (filename.EndsWith("emf")) { using (var g1 = Graphics.FromImage(Picture)) { var rectangle = new System.Drawing.Rectangle(0, 0, Picture.Width, Picture.Height); using (var metafile = new Metafile(filename, g1.GetHdc(), rectangle, MetafileFrameUnit.Pixel, EmfType.EmfPlusDual)) using (var g2 = Graphics.FromImage(metafile)) Chart.Render(g2, pictureBox.Width, pictureBox.Height); } } else { using (var graphics = Graphics.FromImage(Picture)) Chart.Render(graphics, Picture.Width, Picture.Height); if (filename.EndsWith("jpg")) { format = ImageFormat.Jpeg; } else if (filename.EndsWith("gif")) { format = ImageFormat.Gif; } else if (filename.EndsWith("png")) { format = ImageFormat.Png; } else if (filename.EndsWith("tif")) { format = ImageFormat.Tiff; } Picture.Save(saveFileDialog.FileName, format); } } } protected virtual void GenerateImage() { if (InvokeRequired) { Invoke((Action)GenerateImage); return; } if (!Visible) { RenderingRequired = true; } else { if ((pictureBox.Width == 0) || (pictureBox.Height == 0)) { pictureBox.Image = pictureBox.ErrorImage; if (Picture != null) Picture.Dispose(); Picture = null; } else { if (Picture != null) Picture.Dispose(); Picture = new Bitmap(pictureBox.Width, pictureBox.Height); using (var graphics = Graphics.FromImage(Picture)) { graphics.SmoothingMode = SmoothingMode; graphics.SetClip(new System.Drawing.Rectangle(0, 0, pictureBox.Width, pictureBox.Height)); Chart.Render(graphics, pictureBox.Width, pictureBox.Height); } } pictureBox.Image = Picture; RenderingRequired = false; } } public event EventHandler ModeChanged; protected void OnModeChanged() { var handler = ModeChanged; if (handler != null) handler(this, EventArgs.Empty); } private void ChartControl_Load(object sender, EventArgs e) { if (!SuppressRender) GenerateImage(); } #region Helpers private RadioButton CreateChartModeRadioButton(ChartMode chartMode) { var rb = new RadioButton { Appearance = Appearance.Button, Image = chartMode.Image, Size = new Size(24, 24), TabStop = true, Tag = chartMode, UseVisualStyleBackColor = true }; rb.CheckedChanged += (sender, args) => { if (rb.Checked) chartMode.Select(); }; return rb; } private ToolStripMenuItem CreateChartModeMenuItem(ChartMode chartMode) { var mi = new ToolStripMenuItem(chartMode.Text) { CheckOnClick = true, Image = chartMode.Image, Tag = chartMode }; mi.CheckedChanged += (sender, args) => { if (mi.Checked) chartMode.Select(); }; return mi; } #endregion } }