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