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