1 /* 2 * "GEDKeeper", the personal genealogical database editor. 3 * Copyright (C) 2009-2018 by Sergey V. Zhdanovskih. 4 * 5 * This file is part of "GEDKeeper". 6 * 7 * This program is free software: you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation, either version 3 of the License, or 10 * (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program. If not, see <http://www.gnu.org/licenses/>. 19 */ 20 21 using System; 22 using System.Drawing; 23 using System.Drawing.Imaging; 24 using System.Windows.Forms; 25 using BSLib; 26 using BSLib.Design.Graphics; 27 using BSLib.Design.Handlers; 28 using GDModel; 29 using GKCore; 30 using GKCore.Charts; 31 32 namespace GKUI.Components 33 { 34 public abstract class CustomChart : ScrollablePanel, IPrintable 35 { 36 private static readonly object EventNavRefresh; 37 38 39 private readonly NavigationStack<GDMRecord> fNavman; 40 protected ChartRenderer fRenderer; 41 42 43 public event EventHandler NavRefresh 44 { 45 add { Events.AddHandler(EventNavRefresh, value); } 46 remove { Events.RemoveHandler(EventNavRefresh, value); } 47 } 48 49 CustomChart()50 static CustomChart() 51 { 52 EventNavRefresh = new object(); 53 } 54 CustomChart()55 protected CustomChart() 56 { 57 fNavman = new NavigationStack<GDMRecord>(); 58 } 59 Dispose(bool disposing)60 protected override void Dispose(bool disposing) 61 { 62 if (disposing) { 63 //if (fNavman != null) fNavman.Dispose(); 64 } 65 base.Dispose(disposing); 66 } 67 IsInputKey(Keys keyData)68 protected override bool IsInputKey(Keys keyData) 69 { 70 switch (keyData) { 71 case Keys.Left: 72 case Keys.Right: 73 case Keys.Up: 74 case Keys.Down: 75 case Keys.Back: 76 return true; 77 78 default: 79 return base.IsInputKey(keyData); 80 } 81 } 82 OnKeyDown(KeyEventArgs e)83 protected override void OnKeyDown(KeyEventArgs e) 84 { 85 e.Handled = true; 86 switch (e.KeyCode) { 87 case Keys.Left: 88 HorizontalScroll.Value = 89 Math.Max(HorizontalScroll.Value - HorizontalScroll.SmallChange, 0); 90 PerformLayout(); 91 break; 92 93 case Keys.Right: 94 HorizontalScroll.Value += HorizontalScroll.SmallChange; 95 PerformLayout(); 96 break; 97 98 case Keys.Up: 99 VerticalScroll.Value = 100 Math.Max(VerticalScroll.Value - VerticalScroll.SmallChange, 0); 101 PerformLayout(); 102 break; 103 104 case Keys.Down: 105 VerticalScroll.Value += VerticalScroll.SmallChange; 106 PerformLayout(); 107 break; 108 109 case Keys.PageUp: 110 if (Keys.None == ModifierKeys) { 111 VerticalScroll.Value = 112 Math.Max(VerticalScroll.Value - VerticalScroll.LargeChange, 0); 113 } else if (Keys.Shift == ModifierKeys) { 114 HorizontalScroll.Value = 115 Math.Max(HorizontalScroll.Value - HorizontalScroll.LargeChange, 0); 116 } 117 PerformLayout(); 118 break; 119 120 case Keys.PageDown: 121 if (Keys.None == ModifierKeys) { 122 VerticalScroll.Value += VerticalScroll.LargeChange; 123 } else if (Keys.Shift == ModifierKeys) { 124 HorizontalScroll.Value += HorizontalScroll.LargeChange; 125 } 126 PerformLayout(); 127 break; 128 129 case Keys.Home: 130 if (Keys.None == ModifierKeys) { 131 VerticalScroll.Value = 0; 132 } else if (Keys.Shift == ModifierKeys) { 133 HorizontalScroll.Value = 0; 134 } 135 PerformLayout(); 136 break; 137 138 case Keys.End: 139 if (Keys.None == ModifierKeys) { 140 VerticalScroll.Value = VerticalScroll.Maximum; 141 } else if (Keys.Shift == ModifierKeys) { 142 HorizontalScroll.Value = HorizontalScroll.Maximum; 143 } 144 PerformLayout(); 145 break; 146 147 case Keys.Back: 148 NavPrev(); 149 break; 150 151 default: 152 base.OnKeyDown(e); 153 break; 154 } 155 } 156 OnMouseUp(MouseEventArgs e)157 protected override void OnMouseUp(MouseEventArgs e) 158 { 159 if (MouseButtons.XButton1 == e.Button) { 160 NavPrev(); 161 } else if (MouseButtons.XButton2 == e.Button) { 162 NavNext(); 163 } else { 164 base.OnMouseUp(e); 165 } 166 } 167 OnMouseWheel(MouseEventArgs e)168 protected override void OnMouseWheel(MouseEventArgs e) 169 { 170 if (Keys.None == ModifierKeys) { 171 VerticalScroll.Value = Math.Max(VerticalScroll.Value - e.Delta, 0); 172 PerformLayout(); 173 } else if (Keys.Shift == ModifierKeys) { 174 HorizontalScroll.Value = Math.Max(HorizontalScroll.Value - e.Delta, 0); 175 PerformLayout(); 176 } else { 177 base.OnMouseWheel(e); 178 } 179 } 180 181 #region Print and snaphots support 182 GetImageViewPort()183 protected Rectangle GetImageViewPort() 184 { 185 Rectangle viewPort; 186 187 var imageSize = GetImageSize(); 188 if (!imageSize.IsEmpty) { 189 Rectangle clientRect = GetClientRect(true); 190 191 int x = !HScroll ? (clientRect.Width - (imageSize.Width + Padding.Horizontal)) / 2 : 0; 192 int y = !VScroll ? (clientRect.Height - (imageSize.Height + Padding.Vertical)) / 2 : 0; 193 194 int width = Math.Min(imageSize.Width - Math.Abs(AutoScrollPosition.X), clientRect.Width); 195 int height = Math.Min(imageSize.Height - Math.Abs(AutoScrollPosition.Y), clientRect.Height); 196 197 viewPort = new Rectangle(x + clientRect.Left, y + clientRect.Top, width, height); 198 } else { 199 viewPort = Rectangle.Empty; 200 } 201 202 return viewPort; 203 } 204 GetImageSize()205 public abstract ExtSize GetImageSize(); RenderImage(RenderTarget target, bool forciblyCentered = false)206 public abstract void RenderImage(RenderTarget target, bool forciblyCentered = false); 207 IsLandscape()208 public bool IsLandscape() 209 { 210 ExtSize imageSize = GetImageSize(); 211 return (imageSize.Height < imageSize.Width); 212 } 213 GetPrintableImage()214 public IImage GetPrintableImage() 215 { 216 ExtSize imageSize = GetImageSize(); 217 var frameRect = new Rectangle(0, 0, imageSize.Width, imageSize.Height); 218 219 Image image; 220 using (var gfx = CreateGraphics()) { 221 image = new Metafile(gfx.GetHdc(), frameRect, MetafileFrameUnit.Pixel, EmfType.EmfOnly); 222 } 223 224 using (Graphics gfx = Graphics.FromImage(image)) { 225 fRenderer.SetTarget(gfx); 226 RenderImage(RenderTarget.Printer); 227 } 228 229 return new ImageHandler(image); 230 } 231 232 /* TODO(zsv): Need to find an appropriate icon in the general style 233 * for the main toolbar - screenshot capture for windows with charts. */ SaveSnapshot(string fileName)234 public void SaveSnapshot(string fileName) 235 { 236 string ext = FileHelper.GetFileExtension(fileName); 237 238 ExtSize imageSize = GetImageSize(); 239 240 if (ext == ".svg") { 241 var prevRenderer = fRenderer; 242 SetRenderer(new SVGRenderer(fileName, imageSize.Width, imageSize.Height)); 243 fRenderer.BeginDrawing(); 244 try { 245 using (var gfx = CreateGraphics()) { 246 fRenderer.SetTarget(gfx); 247 248 RenderImage(RenderTarget.SVG); 249 } 250 } finally { 251 fRenderer.EndDrawing(); 252 SetRenderer(prevRenderer); 253 } 254 255 return; 256 } 257 258 if ((ext == ".bmp" || ext == ".jpg") && imageSize.Width >= 65535) { 259 AppHost.StdDialogs.ShowError(LangMan.LS(LSID.LSID_TooMuchWidth)); 260 } else { 261 ImageFormat imFmt = ImageFormat.Png; 262 if (ext == ".bmp") { 263 imFmt = ImageFormat.Bmp; 264 } else if (ext == ".png") { 265 imFmt = ImageFormat.Png; 266 } else if (ext == ".gif") { 267 imFmt = ImageFormat.Gif; 268 } else if (ext == ".jpg") { 269 imFmt = ImageFormat.Jpeg; 270 } else if (ext == ".emf") { 271 imFmt = ImageFormat.Emf; 272 } 273 274 Image pic; 275 if (Equals(imFmt, ImageFormat.Emf)) { 276 using (var gfx = CreateGraphics()) { 277 pic = new Metafile(fileName, gfx.GetHdc()); 278 } 279 } else { 280 pic = new Bitmap(imageSize.Width, imageSize.Height, PixelFormat.Format24bppRgb); 281 } 282 283 try { 284 using (Graphics gfx = Graphics.FromImage(pic)) { 285 fRenderer.SetTarget(gfx); 286 RenderImage(RenderTarget.RasterFile); 287 } 288 289 pic.Save(fileName, imFmt); 290 } finally { 291 pic.Dispose(); 292 } 293 } 294 } 295 SetRenderer(ChartRenderer renderer)296 public virtual void SetRenderer(ChartRenderer renderer) 297 { 298 fRenderer = renderer; 299 } 300 301 #endregion 302 303 #region Navigation support 304 DoNavRefresh()305 private void DoNavRefresh() 306 { 307 var eventHandler = (EventHandler)Events[EventNavRefresh]; 308 if (eventHandler != null) eventHandler(this, null); 309 } 310 SetNavObject(object obj)311 protected abstract void SetNavObject(object obj); 312 NavAdd(object obj)313 public bool NavAdd(object obj) 314 { 315 if (obj != null) { 316 fNavman.Current = (GDMRecord)obj; 317 return true; 318 } 319 return false; 320 } 321 NavCanBackward()322 public bool NavCanBackward() 323 { 324 return fNavman.CanBackward(); 325 } 326 NavCanForward()327 public bool NavCanForward() 328 { 329 return fNavman.CanForward(); 330 } 331 NavNext()332 public void NavNext() 333 { 334 if (!fNavman.CanForward()) return; 335 336 SetNavObject(fNavman.Next()); 337 DoNavRefresh(); 338 } 339 NavPrev()340 public void NavPrev() 341 { 342 if (!fNavman.CanBackward()) return; 343 344 SetNavObject(fNavman.Back()); 345 DoNavRefresh(); 346 } 347 348 #endregion 349 } 350 } 351