1 /*
2  *  This file is part of the "GKMap".
3  *  GKMap project borrowed from GMap.NET (by radioman).
4  *
5  *  Copyright (C) 2009-2018 by radioman (email@radioman.lt).
6  *  This program is licensed under the FLAT EARTH License.
7  */
8 
9 //#define DEBUG_TILE_COORDS
10 //#define DEBUG_CENTER
11 //#define DEBUG_RENDER
12 
13 using System;
14 using System.ComponentModel;
15 using System.Diagnostics;
16 using System.Drawing;
17 using System.Drawing.Drawing2D;
18 using System.Drawing.Imaging;
19 using System.IO;
20 using System.Runtime.InteropServices;
21 using System.Windows.Forms;
22 using GKMap.MapObjects;
23 using GKMap.MapProviders;
24 
25 namespace GKMap.WinForms
26 {
MarkerClick(MapMarker item, MouseEventArgs e)27     public delegate void MarkerClick(MapMarker item, MouseEventArgs e);
MarkerDoubleClick(MapMarker item, MouseEventArgs e)28     public delegate void MarkerDoubleClick(MapMarker item, MouseEventArgs e);
29 
PolygonClick(MapPolygon item, MouseEventArgs e)30     public delegate void PolygonClick(MapPolygon item, MouseEventArgs e);
PolygonDoubleClick(MapPolygon item, MouseEventArgs e)31     public delegate void PolygonDoubleClick(MapPolygon item, MouseEventArgs e);
32 
RouteClick(MapRoute item, MouseEventArgs e)33     public delegate void RouteClick(MapRoute item, MouseEventArgs e);
RouteDoubleClick(MapRoute item, MouseEventArgs e)34     public delegate void RouteDoubleClick(MapRoute item, MouseEventArgs e);
35 
36     /// <summary>
37     /// GKMap control for Windows Forms
38     /// </summary>
39     public class GMapControl : UserControl, IMapControl
40     {
41         public static readonly bool IsDesignerHosted = LicenseManager.UsageMode == LicenseUsageMode.Designtime;
42 
43         private Bitmap fBackBuffer;
44         private MapCore fCore;
45         private Cursor fCursorBefore = Cursors.Default;
46         private bool fForceDoubleBuffer;
47         private Graphics fGxOff;
48         private bool fIsMouseOverMarker;
49         private bool fIsMouseOverPolygon;
50         private bool fIsMouseOverRoute;
51         private int fOverObjectCount;
52 
53         private readonly StringFormat BottomFormat = new StringFormat();
54         private readonly StringFormat CenterFormat = new StringFormat();
55         private readonly Font CopyrightFont = new Font(FontFamily.GenericSansSerif, 7, FontStyle.Regular);
56         private readonly Color EmptyMapBackground = Color.WhiteSmoke;
57         private readonly Pen EmptyTileBorders = new Pen(Brushes.White, 1);
58         private readonly Brush EmptyTileBrush = new SolidBrush(Color.Navy);
59         private readonly string EmptyTileText = "We are sorry, but we don't\nhave imagery at this zoom\nlevel for this region.";
60         private readonly Font MissingDataFont = new Font(FontFamily.GenericSansSerif, 11, FontStyle.Bold);
61         private readonly Font ScaleFont = new Font(FontFamily.GenericSansSerif, 5, FontStyle.Italic);
62         private readonly ImageAttributes TileFlipXYAttributes = new ImageAttributes();
63 
64 #if DEBUG_CENTER || DEBUG_RENDER
65         private readonly Pen CenterPen = new Pen(Brushes.Red, 1);
66         private readonly Pen ScalePen = new Pen(Brushes.Blue, 1);
67 #endif
68 
69         #region Properties
70 
71         /// <summary>
72         /// location of cache
73         /// </summary>
74         [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
75         [Browsable(false)]
76         public string CacheLocation
77         {
78             get {
79 #if !DESIGN
80                 return GMaps.CacheLocation;
81 #else
82                 return string.Empty;
83 #endif
84             }
85             set {
86 #if !DESIGN
87                 GMaps.CacheLocation = value;
88 #endif
89             }
90         }
91 
92         MapCore IMapControl.Core { get { return fCore; } }
93 
94         /// <summary>
95         /// stops immediate marker/route/polygon invalidation;
96         /// call Refresh to perform single refresh and reset invalidation state
97         /// </summary>
98         public bool HoldInvalidation { get; set; }
99 
100         /// <summary>
101         /// reverses MouseWheel zooming direction
102         /// </summary>
103         public bool InvertedMouseWheelZooming
104         {
105             get {
106                 return fCore.InvertedMouseWheelZooming;
107             }
108             set {
109                 fCore.InvertedMouseWheelZooming = value;
110             }
111         }
112 
113 
114         /// <summary>
115         /// is mouse over marker
116         /// </summary>
117         [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
118         [Browsable(false)]
119         public bool IsMouseOverMarker
120         {
121             get {
122                 return fIsMouseOverMarker;
123             }
124             set {
125                 fIsMouseOverMarker = value;
126                 fOverObjectCount += value ? 1 : -1;
127             }
128         }
129 
130         /// <summary>
131         /// is mouse over polygon
132         /// </summary>
133         [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
134         [Browsable(false)]
135         public bool IsMouseOverPolygon
136         {
137             get {
138                 return fIsMouseOverPolygon;
139             }
140             set {
141                 fIsMouseOverPolygon = value;
142                 fOverObjectCount += value ? 1 : -1;
143             }
144         }
145 
146         /// <summary>
147         /// is mouse over route
148         /// </summary>
149         [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
150         [Browsable(false)]
151         public bool IsMouseOverRoute
152         {
153             get {
154                 return fIsMouseOverRoute;
155             }
156             set {
157                 fIsMouseOverRoute = value;
158                 fOverObjectCount += value ? 1 : -1;
159             }
160         }
161 
162         [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
163         [Browsable(false)]
164         public GMapProvider MapProvider
165         {
166             get {
167                 return fCore.Provider;
168             }
169             set {
170                 if (fCore.Provider == null || !fCore.Provider.Equals(value)) {
171                     Debug.WriteLine("MapType: " + fCore.Provider.Name + " -> " + value.Name);
172 
173                     RectLatLng viewarea = fCore.ViewArea;
174 
175                     fCore.Provider = value;
176 
177                     if (fCore.IsStarted) {
178                         if (fCore.ZoomToArea) {
179                             // restore zoom rect as close as possible
180                             if (viewarea != RectLatLng.Empty && viewarea != fCore.ViewArea) {
181                                 int bestZoom = fCore.GetMaxZoomToFitRect(viewarea);
182                                 if (bestZoom > 0 && Zoom != bestZoom) {
183                                     Zoom = bestZoom;
184                                 }
185                             }
186                         } else {
187                             fCore.ForceUpdateOverlays();
188                         }
189                     }
190                 }
191             }
192         }
193 
194         /// <summary>
195         /// max zoom
196         /// </summary>
197         [Category("GKMap")]
198         [Description("maximum zoom level of map")]
199         public int MaxZoom
200         {
201             get {
202                 return fCore.MaxZoom;
203             }
204             set {
205                 fCore.MaxZoom = value;
206             }
207         }
208 
209         /// <summary>
210         /// min zoom
211         /// </summary>
212         [Category("GKMap")]
213         [Description("minimum zoom level of map")]
214         public int MinZoom
215         {
216             get {
217                 return fCore.MinZoom;
218             }
219             set {
220                 fCore.MinZoom = value;
221             }
222         }
223 
224         /// <summary>
225         /// list of overlays, should be thread safe
226         /// </summary>
227         public ObservableCollectionThreadSafe<MapOverlay> Overlays { get; private set; }
228 
229         /// <summary>
230         /// current map center position
231         /// </summary>
232         [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
233         [Browsable(false)]
234         public PointLatLng Position
235         {
236             get {
237                 return fCore.Position;
238             }
239             set {
240                 fCore.Position = value;
241 
242                 if (fCore.IsStarted) {
243                     fCore.ForceUpdateOverlays();
244                 }
245             }
246         }
247 
248         [Category("GKMap"), DefaultValue(0)]
249         public int Zoom
250         {
251             get {
252                 return fCore.Zoom;
253             }
254             set {
255                 if (fCore.Zoom != value) {
256                     Debug.WriteLine("ZoomPropertyChanged: " + fCore.Zoom + " -> " + value);
257 
258                     if (value > MaxZoom) {
259                         fCore.Zoom = MaxZoom;
260                     } else if (value < MinZoom) {
261                         fCore.Zoom = MinZoom;
262                     } else {
263                         fCore.Zoom = value;
264                     }
265 
266                     if (fCore.IsStarted && !fCore.IsDragging) {
267                         fCore.ForceUpdateOverlays();
268                     }
269                 }
270             }
271         }
272 
273         #endregion
274 
275         #region Events
276 
277         /// <summary>
278         /// occurs when current position is changed
279         /// </summary>
280         public event PositionChanged OnPositionChanged
281         {
282             add {
283                 fCore.OnCurrentPositionChanged += value;
284             }
285             remove {
286                 fCore.OnCurrentPositionChanged -= value;
287             }
288         }
289 
290         /// <summary>
291         /// occurs when tile set load is complete
292         /// </summary>
293         public event TileLoadComplete OnTileLoadComplete
294         {
295             add {
296                 fCore.OnTileLoadComplete += value;
297             }
298             remove {
299                 fCore.OnTileLoadComplete -= value;
300             }
301         }
302 
303         /// <summary>
304         /// occurs when tile set is starting to load
305         /// </summary>
306         public event TileLoadStart OnTileLoadStart
307         {
308             add {
309                 fCore.OnTileLoadStart += value;
310             }
311             remove {
312                 fCore.OnTileLoadStart -= value;
313             }
314         }
315 
316         /// <summary>
317         /// occurs on map drag
318         /// </summary>
319         public event MapDrag OnMapDrag
320         {
321             add {
322                 fCore.OnMapDrag += value;
323             }
324             remove {
325                 fCore.OnMapDrag -= value;
326             }
327         }
328 
329         /// <summary>
330         /// occurs on map zoom changed
331         /// </summary>
332         public event MapZoomChanged OnMapZoomChanged
333         {
334             add {
335                 fCore.OnMapZoomChanged += value;
336             }
337             remove {
338                 fCore.OnMapZoomChanged -= value;
339             }
340         }
341 
342         /// <summary>
343         /// occurs on map type changed
344         /// </summary>
345         public event MapTypeChanged OnMapTypeChanged
346         {
347             add {
348                 fCore.OnMapTypeChanged += value;
349             }
350             remove {
351                 fCore.OnMapTypeChanged -= value;
352             }
353         }
354 
355         /// <summary>
356         /// occurs when clicked on marker
357         /// </summary>
358         public event MarkerClick OnMarkerClick;
359 
360         /// <summary>
361         /// occurs when double clicked on marker
362         /// </summary>
363         public event MarkerDoubleClick OnMarkerDoubleClick;
364 
365         /// <summary>
366         /// occurs on mouse enters marker area
367         /// </summary>
368         public event MarkerEnter OnMarkerEnter
369         {
370             add {
371                 fCore.OnMarkerEnter += value;
372             }
373             remove {
374                 fCore.OnMarkerEnter -= value;
375             }
376         }
377 
378         /// <summary>
379         /// occurs on mouse leaves marker area
380         /// </summary>
381         public event MarkerLeave OnMarkerLeave
382         {
383             add {
384                 fCore.OnMarkerLeave += value;
385             }
386             remove {
387                 fCore.OnMarkerLeave -= value;
388             }
389         }
390 
391         /// <summary>
392         /// occurs when clicked on polygon
393         /// </summary>
394         public event PolygonClick OnPolygonClick;
395 
396         /// <summary>
397         /// occurs when double clicked on polygon
398         /// </summary>
399         public event PolygonDoubleClick OnPolygonDoubleClick;
400 
401         /// <summary>
402         /// occurs on mouse enters Polygon area
403         /// </summary>
404         public event PolygonEnter OnPolygonEnter
405         {
406             add {
407                 fCore.OnPolygonEnter += value;
408             }
409             remove {
410                 fCore.OnPolygonEnter -= value;
411             }
412         }
413 
414         /// <summary>
415         /// occurs on mouse leaves Polygon area
416         /// </summary>
417         public event PolygonLeave OnPolygonLeave
418         {
419             add {
420                 fCore.OnPolygonLeave += value;
421             }
422             remove {
423                 fCore.OnPolygonLeave -= value;
424             }
425         }
426 
427         /// <summary>
428         /// occurs when clicked on route
429         /// </summary>
430         public event RouteClick OnRouteClick;
431 
432         /// <summary>
433         /// occurs when double clicked on route
434         /// </summary>
435         public event RouteDoubleClick OnRouteDoubleClick;
436 
437         /// <summary>
438         /// occurs on mouse enters route area
439         /// </summary>
440         public event RouteEnter OnRouteEnter
441         {
442             add {
443                 fCore.OnRouteEnter += value;
444             }
445             remove {
446                 fCore.OnRouteEnter -= value;
447             }
448         }
449 
450         /// <summary>
451         /// occurs on mouse leaves route area
452         /// </summary>
453         public event RouteLeave OnRouteLeave
454         {
455             add {
456                 fCore.OnRouteLeave += value;
457             }
458             remove {
459                 fCore.OnRouteLeave -= value;
460             }
461         }
462 
463         #endregion
464 
465 
GMapControl()466         static GMapControl()
467         {
468             if (!IsDesignerHosted) {
469                 GMaps.Initialize(GMapImageProxy.Instance);
470             }
471         }
472 
473 #if !DESIGN
474         /// <summary>
475         /// constructor
476         /// </summary>
GMapControl()477         public GMapControl()
478         {
479             fCore = new MapCore(this);
480 
481             Overlays = new ObservableCollectionThreadSafe<MapOverlay>();
482 
483             InvertedMouseWheelZooming = false;
484             MaxZoom = 17;
485             MinZoom = 2;
486             Zoom = 0;
487 
488             if (!IsDesignerHosted) {
489                 SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
490                 SetStyle(ControlStyles.AllPaintingInWmPaint, true);
491                 SetStyle(ControlStyles.UserPaint, true);
492                 SetStyle(ControlStyles.Opaque, true);
493                 ResizeRedraw = true;
494 
495                 TileFlipXYAttributes.SetWrapMode(WrapMode.TileFlipXY);
496 
497                 CenterFormat.Alignment = StringAlignment.Center;
498                 CenterFormat.LineAlignment = StringAlignment.Center;
499 
500                 BottomFormat.Alignment = StringAlignment.Center;
501                 BottomFormat.LineAlignment = StringAlignment.Far;
502 
503                 Overlays.CollectionChanged += Overlays_CollectionChanged;
504             }
505         }
506 
507 #endif
508 
Dispose(bool disposing)509         protected override void Dispose(bool disposing)
510         {
511             if (disposing) {
512                 fCore.OnMapClose();
513 
514                 Overlays.CollectionChanged -= Overlays_CollectionChanged;
515 
516                 foreach (var o in Overlays) {
517                     o.Dispose();
518                 }
519                 Overlays.Clear();
520 
521 #if DEBUG_CENTER
522                 CenterPen.Dispose();
523                 ScalePen.Dispose();
524 #endif
525 
526                 ScaleFont.Dispose();
527                 CenterFormat.Dispose();
528                 BottomFormat.Dispose();
529                 CopyrightFont.Dispose();
530                 EmptyTileBorders.Dispose();
531                 EmptyTileBrush.Dispose();
532                 ClearBackBuffer();
533             }
534             base.Dispose(disposing);
535         }
536 
537         /// <summary>
538         /// Call it to empty tile cache & reload tiles
539         /// </summary>
ReloadMap()540         public void ReloadMap()
541         {
542             fCore.ReloadMap();
543         }
544 
545         /// <summary>
546         /// set current position using keywords
547         /// </summary>
548         /// <param name="keys"></param>
549         /// <returns>true if successful</returns>
SetPositionByKeywords(string keys)550         public GeocoderStatusCode SetPositionByKeywords(string keys)
551         {
552             return fCore.SetPositionByKeywords(keys);
553         }
554 
555         /// <summary>
556         /// gets world coordinate from local control coordinate
557         /// </summary>
558         /// <param name="x"></param>
559         /// <param name="y"></param>
560         /// <returns></returns>
FromLocalToLatLng(int x, int y)561         public PointLatLng FromLocalToLatLng(int x, int y)
562         {
563             return fCore.FromLocalToLatLng(x, y);
564         }
565 
566         /// <summary>
567         /// gets local coordinate from world coordinate
568         /// </summary>
569         /// <param name="point"></param>
570         /// <returns></returns>
FromLatLngToLocal(PointLatLng point)571         public GPoint FromLatLngToLocal(PointLatLng point)
572         {
573             return fCore.FromLatLngToLocal(point);
574         }
575 
576         /// <summary>
577         /// call this to stop HoldInvalidation and perform single forced instant refresh
578         /// </summary>
Refresh()579         public override void Refresh()
580         {
581             HoldInvalidation = false;
582 
583             lock (fCore.InvalidationLock) {
584                 fCore.LastInvalidation = DateTime.Now;
585             }
586 
587             base.Refresh();
588         }
589 
590 #if !DESIGN
591         /// <summary>
592         /// enqueue built-in thread safe invalidation
593         /// </summary>
Invalidate()594         public new void Invalidate()
595         {
596             if (fCore.RefreshEvent != null && !HoldInvalidation) {
597                 fCore.RefreshEvent.Set();
598             }
599         }
600 #endif
601 
602         /// <summary>
603         /// sets to max zoom to fit all markers and centers them in map
604         /// </summary>
605         /// <param name="overlayId">overlay id or null to check all</param>
606         /// <returns></returns>
ZoomAndCenterMarkers(string overlayId)607         public bool ZoomAndCenterMarkers(string overlayId)
608         {
609             return fCore.ZoomAndCenterMarkers(overlayId);
610         }
611 
612         /// <summary>
613         /// zooms and centers all route
614         /// </summary>
615         /// <param name="overlayId">overlay id or null to check all</param>
616         /// <returns></returns>
ZoomAndCenterRoutes(string overlayId)617         public bool ZoomAndCenterRoutes(string overlayId)
618         {
619             return fCore.ZoomAndCenterRoutes(overlayId);
620         }
621 
622         /// <summary>
623         /// zooms and centers route
624         /// </summary>
625         /// <param name="figure"></param>
626         /// <returns></returns>
ZoomAndCenterFigure(MapFigure figure)627         public bool ZoomAndCenterFigure(MapFigure figure)
628         {
629             return fCore.ZoomAndCenterFigure(figure);
630         }
631 
632         /// <summary>
633         /// gets image of the current view
634         /// </summary>
635         /// <returns></returns>
ToImage()636         public Image ToImage()
637         {
638             Image ret;
639 
640             bool r = fForceDoubleBuffer;
641             try {
642                 UpdateBackBuffer();
643 
644                 if (!r) {
645                     fForceDoubleBuffer = true;
646                 }
647 
648                 Refresh();
649                 Application.DoEvents();
650 
651                 using (MemoryStream ms = new MemoryStream()) {
652                     using (var frame = (fBackBuffer.Clone() as Bitmap)) {
653                         frame.Save(ms, ImageFormat.Png);
654                     }
655                     ret = Image.FromStream(ms);
656                 }
657             } catch (Exception) {
658                 throw;
659             } finally {
660                 if (!r) {
661                     fForceDoubleBuffer = false;
662                     ClearBackBuffer();
663                 }
664             }
665             return ret;
666         }
667 
668         /// <summary>
669         /// offset position in pixels
670         /// </summary>
671         /// <param name="x"></param>
672         /// <param name="y"></param>
Offset(int x, int y)673         public void Offset(int x, int y)
674         {
675             if (IsHandleCreated) {
676                 fCore.DragOffset(new GPoint(x, y));
677             }
678         }
679 
680         #region UserControl Events
681 
OnLoad(EventArgs e)682         protected override void OnLoad(EventArgs e)
683         {
684             base.OnLoad(e);
685 
686             if (!IsDesignerHosted) {
687                 fCore.ResetZoomToFitRect();
688                 fCore.OnMapOpen().ProgressChanged += invalidatorEngage;
689                 fCore.ForceUpdateOverlays();
690             }
691         }
692 
OnCreateControl()693         protected override void OnCreateControl()
694         {
695             base.OnCreateControl();
696 
697             if (!IsDesignerHosted) {
698                 var f = ParentForm;
699                 if (f != null) {
700                     while (f.ParentForm != null) {
701                         f = f.ParentForm;
702                     }
703 
704                     f.FormClosing += ParentForm_FormClosing;
705                 }
706             }
707         }
708 
709 #if !DESIGN
710 #if DEBUG
711         private int fCounter;
712         private readonly Font fDebugFont = new Font(FontFamily.GenericSansSerif, 12, FontStyle.Regular);
713         private DateTime fStart;
714         private DateTime fEnd;
715         private int fDelta;
716 #endif
717 
OnPaint(PaintEventArgs e)718         protected override void OnPaint(PaintEventArgs e)
719         {
720 #if DEBUG
721             fStart = DateTime.Now;
722 #endif
723 
724             if (fForceDoubleBuffer) {
725                 if (fGxOff != null) {
726                     DrawGraphics(fGxOff);
727                     e.Graphics.DrawImage(fBackBuffer, 0, 0);
728                 }
729             } else {
730                 DrawGraphics(e.Graphics);
731             }
732 
733             base.OnPaint(e);
734 
735 #if DEBUG
736             fEnd = DateTime.Now;
737             fDelta = (int)(fEnd - fStart).TotalMilliseconds;
738 #endif
739         }
740 
DrawGraphics(Graphics g)741         private void DrawGraphics(Graphics g)
742         {
743             // render white background
744             g.Clear(EmptyMapBackground);
745             g.TranslateTransform(fCore.RenderOffset.X, fCore.RenderOffset.Y);
746             fCore.DrawMap(g);
747             OnPaintOverlays(g);
748         }
749 #endif
750 
751         /// <summary>
752         /// override, to render something more
753         /// </summary>
754         /// <param name="g"></param>
OnPaintOverlays(Graphics g)755         protected virtual void OnPaintOverlays(Graphics g)
756         {
757             g.SmoothingMode = SmoothingMode.HighQuality;
758             foreach (GMapOverlay o in Overlays) {
759                 if (o.IsVisible) {
760                     o.OnRender(g);
761                 }
762             }
763 
764             // center in virtual space...
765 #if DEBUG_CENTER
766             g.DrawLine(ScalePen, -20, 0, 20, 0);
767             g.DrawLine(ScalePen, 0, -20, 0, 20);
768             g.DrawString("debug: virtual space center", CopyrightFont, Brushes.Blue, 2, CopyrightFont.Height);
769 #endif
770 
771             g.ResetTransform();
772 
773             // copyright
774             if (!string.IsNullOrEmpty(fCore.Provider.Copyright)) {
775                 g.DrawString(fCore.Provider.Copyright, CopyrightFont, Brushes.Navy, 3, Height - CopyrightFont.Height - 5);
776             }
777 
778 #if DEBUG_RENDER
779             // show center
780             g.DrawLine(CenterPen, Width / 2 - 5, Height / 2, Width / 2 + 5, Height / 2);
781             g.DrawLine(CenterPen, Width / 2, Height / 2 - 5, Width / 2, Height / 2 + 5);
782 
783             // debug info
784             g.DrawString(string.Format("{0:0.0}", Zoom) + "z, " + MapProvider + ", refresh: " + fCounter++ + ", render: " + fDelta + "ms", fDebugFont, Brushes.Blue, fDebugFont.Height, fDebugFont.Height + 20);
785 #endif
786         }
787 
OnSizeChanged(EventArgs e)788         protected override void OnSizeChanged(EventArgs e)
789         {
790             base.OnSizeChanged(e);
791 
792             if (Width == 0 || Height == 0) {
793                 Debug.WriteLine("minimized");
794                 return;
795             }
796 
797             if (Width == fCore.Width && Height == fCore.Height) {
798                 Debug.WriteLine("maximized");
799                 return;
800             }
801 
802             if (!IsDesignerHosted) {
803                 if (fForceDoubleBuffer) {
804                     UpdateBackBuffer();
805                 }
806 
807                 fCore.OnMapSizeChanged(Width, Height);
808 
809                 if (Visible && IsHandleCreated && fCore.IsStarted) {
810                     fCore.ForceUpdateOverlays();
811                 }
812             }
813         }
814 
OnMouseDown(MouseEventArgs e)815         protected override void OnMouseDown(MouseEventArgs e)
816         {
817             base.OnMouseDown(e);
818 
819             if (!IsMouseOverMarker && e.Button == MouseButtons.Left) {
820                 fCore.MouseDown = new GPoint(e.X, e.Y);
821                 Invalidate();
822             }
823         }
824 
OnMouseUp(MouseEventArgs e)825         protected override void OnMouseUp(MouseEventArgs e)
826         {
827             base.OnMouseUp(e);
828 
829             if (fCore.IsDragging) {
830                 RestoreCursor();
831                 fCore.EndDrag();
832             } else {
833                 if (e.Button == MouseButtons.Left) {
834                     fCore.MouseDown = GPoint.Empty;
835                 }
836 
837                 Invalidate();
838             }
839         }
840 
OnMouseClick(MouseEventArgs e)841         protected override void OnMouseClick(MouseEventArgs e)
842         {
843             base.OnMouseClick(e);
844 
845             fCore.ProcessMouseClick(e.X, e.Y, e);
846         }
847 
OnMouseDoubleClick(MouseEventArgs e)848         protected override void OnMouseDoubleClick(MouseEventArgs e)
849         {
850             base.OnMouseDoubleClick(e);
851 
852             fCore.ProcessMouseDoubleClick(e.X, e.Y, e);
853         }
854 
OnMouseMove(MouseEventArgs e)855         protected override void OnMouseMove(MouseEventArgs e)
856         {
857             base.OnMouseMove(e);
858 
859             if (!fCore.IsDragging && !fCore.MouseDown.IsEmpty) {
860                 // Gets the width and height of a rectangle centered on the point the mouse
861                 // button was pressed, within which a drag operation will not begin.
862                 var dragSize = SystemInformation.DragSize;
863 
864                 if (Math.Abs(e.X - fCore.MouseDown.X) * 2 >= dragSize.Width || Math.Abs(e.Y - fCore.MouseDown.Y) * 2 >= dragSize.Height) {
865                     fCore.BeginDrag(fCore.MouseDown);
866                 }
867             }
868 
869             if (fCore.IsDragging) {
870                 SetCursorDrag();
871                 fCore.MouseCurrent = new GPoint(e.X, e.Y);
872                 fCore.Drag(fCore.MouseCurrent);
873                 base.Invalidate();
874             } else {
875                 if (fCore.MouseDown.IsEmpty) {
876                     fCore.ProcessOverlaysMouseMove(e.X, e.Y);
877                 }
878             }
879         }
880 
OnMouseEnter(EventArgs e)881         protected override void OnMouseEnter(EventArgs e)
882         {
883             base.OnMouseEnter(e);
884 
885             Focus();
886             fCore.MouseIn = true;
887         }
888 
OnMouseLeave(EventArgs e)889         protected override void OnMouseLeave(EventArgs e)
890         {
891             base.OnMouseLeave(e);
892 
893             fCore.MouseIn = false;
894         }
895 
OnMouseWheel(MouseEventArgs e)896         protected override void OnMouseWheel(MouseEventArgs e)
897         {
898             base.OnMouseWheel(e);
899 
900             fCore.ProcessMouseWheel(e.X, e.Y, e.Delta);
901         }
902 
903         #endregion
904 
905         #region Core callbacks and aux private methods
906 
ParentForm_FormClosing(object sender, FormClosingEventArgs e)907         private void ParentForm_FormClosing(object sender, FormClosingEventArgs e)
908         {
909             if (e.CloseReason == CloseReason.WindowsShutDown || e.CloseReason == CloseReason.TaskManagerClosing) {
910                 GMaps.Instance.CancelTileCaching();
911             }
912         }
913 
UpdateBackBuffer()914         private void UpdateBackBuffer()
915         {
916             ClearBackBuffer();
917 
918             fBackBuffer = new Bitmap(Width, Height);
919             fGxOff = Graphics.FromImage(fBackBuffer);
920         }
921 
ClearBackBuffer()922         private void ClearBackBuffer()
923         {
924             if (fBackBuffer != null) {
925                 fBackBuffer.Dispose();
926                 fBackBuffer = null;
927             }
928             if (fGxOff != null) {
929                 fGxOff.Dispose();
930                 fGxOff = null;
931             }
932         }
933 
Overlays_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)934         private void Overlays_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
935         {
936             if (e.NewItems != null) {
937                 foreach (GMapOverlay obj in e.NewItems) {
938                     if (obj != null) {
939                         obj.Control = this;
940                     }
941                 }
942 
943                 if (fCore.IsStarted) {
944                     Invalidate();
945                 }
946             }
947         }
948 
invalidatorEngage(object sender, ProgressChangedEventArgs e)949         private void invalidatorEngage(object sender, ProgressChangedEventArgs e)
950         {
951             base.Invalidate();
952         }
953 
IMapControl.DrawTile(object go, PureImage pureImage, ref bool found)954         void IMapControl.DrawTile(object go, PureImage pureImage, ref bool found)
955         {
956             Graphics g = (Graphics)go;
957             var img = (GMapImage)pureImage;
958             if (img != null && img.Img != null) {
959                 if (!found)
960                     found = true;
961 
962                 if (!img.IsParent) {
963                     g.DrawImage(img.Img, fCore.TileRect.X, fCore.TileRect.Y, fCore.TileRect.Width, fCore.TileRect.Height);
964                 } else {
965                     float wix = ((float)img.Img.Width / img.Ix);
966                     float hix = ((float)img.Img.Height / img.Ix);
967                     RectangleF srcRect = new RectangleF(img.Xoff * wix, img.Yoff * hix, wix, hix);
968                     Rectangle dst = new Rectangle((int)fCore.TileRect.X, (int)fCore.TileRect.Y, (int)fCore.TileRect.Width, (int)fCore.TileRect.Height);
969                     g.DrawImage(img.Img, dst, srcRect.X, srcRect.Y, srcRect.Width, srcRect.Height, GraphicsUnit.Pixel, TileFlipXYAttributes);
970                 }
971             }
972         }
973 
IMapControl.DrawLowerTile(object go, PureImage pureImage, long Ix, long xoff, long yoff, ref bool found)974         void IMapControl.DrawLowerTile(object go, PureImage pureImage, long Ix, long xoff, long yoff, ref bool found)
975         {
976             Graphics g = (Graphics)go;
977             var img = (GMapImage)pureImage;
978             if (img != null && img.Img != null && !img.IsParent) {
979                 if (!found)
980                     found = true;
981 
982                 RectangleF srcRect = new RectangleF(xoff * (img.Img.Width / Ix),
983                     yoff * (img.Img.Height / Ix), (img.Img.Width / Ix), (img.Img.Height / Ix));
984                 Rectangle dst = new Rectangle((int)fCore.TileRect.X, (int)fCore.TileRect.Y,
985                     (int)fCore.TileRect.Width, (int)fCore.TileRect.Height);
986 
987                 g.DrawImage(img.Img, dst, srcRect.X, srcRect.Y, srcRect.Width, srcRect.Height,
988                     GraphicsUnit.Pixel, TileFlipXYAttributes);
989             }
990         }
991 
IMapControl.DrawMissingTile(object go, Exception ex)992         void IMapControl.DrawMissingTile(object go, Exception ex)
993         {
994             Graphics g = (Graphics)go;
995 
996             g.FillRectangle(EmptyTileBrush,
997                 new RectangleF(fCore.TileRect.X, fCore.TileRect.Y, fCore.TileRect.Width,
998                     fCore.TileRect.Height));
999 
1000             g.DrawString("Exception: " + ex.Message, MissingDataFont, Brushes.Red,
1001                 new RectangleF(fCore.TileRect.X + 11, fCore.TileRect.Y + 11,
1002                     fCore.TileRect.Width - 11, fCore.TileRect.Height - 11));
1003 
1004             g.DrawString(EmptyTileText, MissingDataFont, Brushes.Blue,
1005                 new RectangleF(fCore.TileRect.X, fCore.TileRect.Y, fCore.TileRect.Width,
1006                     fCore.TileRect.Height), CenterFormat);
1007 
1008             g.DrawRectangle(EmptyTileBorders, (int)fCore.TileRect.X, (int)fCore.TileRect.Y,
1009                 (int)fCore.TileRect.Width, (int)fCore.TileRect.Height);
1010         }
1011 
IMapControl.ShowTileGridLines(object go, DrawTile tilePoint)1012         void IMapControl.ShowTileGridLines(object go, DrawTile tilePoint)
1013         {
1014 #if DEBUG_TILE_COORDS
1015             Graphics g = (Graphics)go;
1016 
1017             // show tile grid lines
1018             g.DrawRectangle(EmptyTileBorders, (int)fCore.TileRect.X, (int)fCore.TileRect.Y,
1019                 (int)fCore.TileRect.Width, (int)fCore.TileRect.Height);
1020             g.DrawString(
1021                 (tilePoint.PosXY == fCore.CenterTileXYLocation ? "CENTER: " : "TILE: ") + tilePoint,
1022                 MissingDataFont, Brushes.DimGray,
1023                 new RectangleF(fCore.TileRect.X, fCore.TileRect.Y, fCore.TileRect.Width, fCore.TileRect.Height),
1024                 CenterFormat);
1025 #endif
1026         }
1027 
IMapControl.DoMouseClick(MapObject obj, EventArgs e)1028         void IMapControl.DoMouseClick(MapObject obj, EventArgs e)
1029         {
1030             if (obj is MapMarker) {
1031                 if (OnMarkerClick != null) {
1032                     OnMarkerClick(obj as MapMarker, (MouseEventArgs)e);
1033                 }
1034             } else if (obj is MapRoute) {
1035                 if (OnRouteClick != null) {
1036                     OnRouteClick(obj as MapRoute, (MouseEventArgs)e);
1037                 }
1038             } else if (obj is MapPolygon) {
1039                 if (OnPolygonClick != null) {
1040                     OnPolygonClick(obj as MapPolygon, (MouseEventArgs)e);
1041                 }
1042             }
1043         }
1044 
IMapControl.DoMouseDoubleClick(MapObject obj, EventArgs e)1045         void IMapControl.DoMouseDoubleClick(MapObject obj, EventArgs e)
1046         {
1047             if (obj is MapMarker) {
1048                 if (OnMarkerDoubleClick != null) {
1049                     OnMarkerDoubleClick(obj as MapMarker, (MouseEventArgs)e);
1050                 }
1051             } else if (obj is MapRoute) {
1052                 if (OnRouteDoubleClick != null) {
1053                     OnRouteDoubleClick(obj as MapRoute, (MouseEventArgs)e);
1054                 }
1055             } else if (obj is MapPolygon) {
1056                 if (OnPolygonDoubleClick != null) {
1057                     OnPolygonDoubleClick(obj as MapPolygon, (MouseEventArgs)e);
1058                 }
1059             }
1060         }
1061 
SetCursorHand()1062         private void SetCursorHand()
1063         {
1064             fCursorBefore = Cursor;
1065             Cursor = Cursors.Hand;
1066         }
1067 
SetCursorDrag()1068         private void SetCursorDrag()
1069         {
1070             fCursorBefore = Cursor;
1071             Cursor = Cursors.SizeAll;
1072         }
1073 
RestoreCursor()1074         private void RestoreCursor()
1075         {
1076             Cursor = Cursors.Arrow;
1077             fCursorBefore = null;
1078         }
1079 
IMapControl.RestoreCursorOnLeave()1080         void IMapControl.RestoreCursorOnLeave()
1081         {
1082             if (fOverObjectCount <= 0 && fCursorBefore != null) {
1083                 fOverObjectCount = 0;
1084                 RestoreCursor();
1085             }
1086         }
1087 
IMapControl.SetCursorHandOnEnter()1088         void IMapControl.SetCursorHandOnEnter()
1089         {
1090             if (fOverObjectCount <= 0 && Cursor != Cursors.Hand) {
1091                 fOverObjectCount = 0;
1092                 SetCursorHand();
1093             }
1094         }
1095 
IMapControl.SetMousePositionToMapCenter()1096         void IMapControl.SetMousePositionToMapCenter()
1097         {
1098             if (!fCore.IsRunningOnMono) {
1099                 Point p = PointToScreen(new Point(Width / 2, Height / 2));
1100                 SetCursorPos(p.X, p.Y);
1101             }
1102         }
1103 
1104         [DllImport("user32.dll", EntryPoint = "SetCursorPos")]
1105         [return: MarshalAs(UnmanagedType.Bool)]
SetCursorPos(int x, int y)1106         private static extern bool SetCursorPos(int x, int y);
1107 
1108         #endregion
1109     }
1110 }
1111