TemplateTool.cs (37982B)
1 // 2 // Copyright 2020 Electronic Arts Inc. 3 // 4 // The Command & Conquer Map Editor and corresponding source code is free 5 // software: you can redistribute it and/or modify it under the terms of 6 // the GNU General Public License as published by the Free Software Foundation, 7 // either version 3 of the License, or (at your option) any later version. 8 9 // The Command & Conquer Map Editor and corresponding source code is distributed 10 // in the hope that it will be useful, but with permitted additional restrictions 11 // under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT 12 // distributed with this program. You should have received a copy of the 13 // GNU General Public License along with permitted additional restrictions 14 // with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection 15 using MobiusEditor.Controls; 16 using MobiusEditor.Event; 17 using MobiusEditor.Interface; 18 using MobiusEditor.Model; 19 using MobiusEditor.Utility; 20 using MobiusEditor.Widgets; 21 using System; 22 using System.Collections.Generic; 23 using System.Drawing; 24 using System.Drawing.Drawing2D; 25 using System.Linq; 26 using System.Text.RegularExpressions; 27 using System.Windows.Forms; 28 29 namespace MobiusEditor.Tools 30 { 31 public class TemplateTool : ViewTool 32 { 33 private static readonly Regex CategoryRegex = new Regex(@"^([a-z]*)", RegexOptions.Compiled); 34 35 private readonly ListView templateTypeListView; 36 private readonly MapPanel templateTypeMapPanel; 37 private readonly ToolTip mouseTooltip; 38 39 private readonly Dictionary<int, Template> undoTemplates = new Dictionary<int, Template>(); 40 private readonly Dictionary<int, Template> redoTemplates = new Dictionary<int, Template>(); 41 42 private Map previewMap; 43 protected override Map RenderMap => previewMap; 44 45 private bool placementMode; 46 47 private bool boundsMode; 48 private Rectangle dragBounds; 49 private int dragEdge = -1; 50 51 private TemplateType selectedTemplateType; 52 private TemplateType SelectedTemplateType 53 { 54 get => selectedTemplateType; 55 set 56 { 57 if (selectedTemplateType != value) 58 { 59 if (placementMode && (selectedTemplateType != null)) 60 { 61 for (var y = 0; y < selectedTemplateType.IconHeight; ++y) 62 { 63 for (var x = 0; x < selectedTemplateType.IconWidth; ++x) 64 { 65 mapPanel.Invalidate(map, new Point(navigationWidget.MouseCell.X + x, navigationWidget.MouseCell.Y + y)); 66 } 67 } 68 } 69 70 selectedTemplateType = value; 71 72 templateTypeListView.BeginUpdate(); 73 templateTypeListView.SelectedIndexChanged -= TemplateTypeListView_SelectedIndexChanged; 74 foreach (ListViewItem item in templateTypeListView.Items) 75 { 76 item.Selected = item.Tag == selectedTemplateType; 77 } 78 if (templateTypeListView.SelectedIndices.Count > 0) 79 { 80 templateTypeListView.EnsureVisible(templateTypeListView.SelectedIndices[0]); 81 } 82 templateTypeListView.SelectedIndexChanged += TemplateTypeListView_SelectedIndexChanged; 83 templateTypeListView.EndUpdate(); 84 85 if (placementMode && (selectedTemplateType != null)) 86 { 87 for (var y = 0; y < selectedTemplateType.IconHeight; ++y) 88 { 89 for (var x = 0; x < selectedTemplateType.IconWidth; ++x) 90 { 91 mapPanel.Invalidate(map, new Point(navigationWidget.MouseCell.X + x, navigationWidget.MouseCell.Y + y)); 92 } 93 } 94 } 95 96 RefreshMapPanel(); 97 } 98 } 99 } 100 101 private Point? selectedIcon; 102 private Point? SelectedIcon 103 { 104 get => selectedIcon; 105 set 106 { 107 if (selectedIcon != value) 108 { 109 selectedIcon = value; 110 templateTypeMapPanel.Invalidate(); 111 112 if (placementMode && (SelectedTemplateType != null)) 113 { 114 for (var y = 0; y < SelectedTemplateType.IconHeight; ++y) 115 { 116 for (var x = 0; x < SelectedTemplateType.IconWidth; ++x) 117 { 118 mapPanel.Invalidate(map, new Point(navigationWidget.MouseCell.X + x, navigationWidget.MouseCell.Y + y)); 119 } 120 } 121 } 122 } 123 } 124 } 125 126 private NavigationWidget templateTypeNavigationWidget; 127 128 public TemplateTool(MapPanel mapPanel, MapLayerFlag layers, ToolStripStatusLabel statusLbl, ListView templateTypeListView, MapPanel templateTypeMapPanel, ToolTip mouseTooltip, IGamePlugin plugin, UndoRedoList<UndoRedoEventArgs> url) 129 : base(mapPanel, layers, statusLbl, plugin, url) 130 { 131 previewMap = map; 132 133 this.mapPanel.MouseDown += MapPanel_MouseDown; 134 this.mapPanel.MouseUp += MapPanel_MouseUp; 135 this.mapPanel.MouseMove += MapPanel_MouseMove; 136 (this.mapPanel as Control).KeyDown += TemplateTool_KeyDown; 137 (this.mapPanel as Control).KeyUp += TemplateTool_KeyUp; 138 139 this.templateTypeListView = templateTypeListView; 140 this.templateTypeListView.SelectedIndexChanged += TemplateTypeListView_SelectedIndexChanged; 141 142 string templateCategory(TemplateType template) 143 { 144 var m = CategoryRegex.Match(template.Name); 145 return m.Success ? m.Groups[1].Value : string.Empty; 146 } 147 148 var templateTypes = plugin.Map.TemplateTypes 149 .Where(t => 150 (t.Thumbnail != null) && 151 t.Theaters.Contains(plugin.Map.Theater) && 152 ((t.Flag & TemplateTypeFlag.Clear) == TemplateTypeFlag.None)) 153 .GroupBy(t => templateCategory(t)).OrderBy(g => g.Key); 154 var templateTypeImages = templateTypes.SelectMany(g => g).Select(t => t.Thumbnail); 155 156 var maxWidth = templateTypeImages.Max(t => t.Width); 157 var maxHeight = templateTypeImages.Max(t => t.Height); 158 159 var imageList = new ImageList(); 160 imageList.Images.AddRange(templateTypeImages.ToArray()); 161 imageList.ImageSize = new Size(maxWidth, maxHeight); 162 imageList.ColorDepth = ColorDepth.Depth24Bit; 163 164 this.templateTypeListView.BeginUpdate(); 165 this.templateTypeListView.LargeImageList = imageList; 166 167 var imageIndex = 0; 168 foreach (var templateTypeGroup in templateTypes) 169 { 170 var group = new ListViewGroup(templateTypeGroup.Key); 171 this.templateTypeListView.Groups.Add(group); 172 foreach (var templateType in templateTypeGroup) 173 { 174 var item = new ListViewItem(templateType.DisplayName, imageIndex++) 175 { 176 Group = group, 177 Tag = templateType 178 }; 179 this.templateTypeListView.Items.Add(item); 180 } 181 } 182 this.templateTypeListView.EndUpdate(); 183 184 this.templateTypeMapPanel = templateTypeMapPanel; 185 this.templateTypeMapPanel.MouseDown += TemplateTypeMapPanel_MouseDown; 186 this.templateTypeMapPanel.PostRender += TemplateTypeMapPanel_PostRender; 187 this.templateTypeMapPanel.BackColor = Color.Black; 188 this.templateTypeMapPanel.MaxZoom = 1; 189 190 this.mouseTooltip = mouseTooltip; 191 192 navigationWidget.MouseCellChanged += MouseoverWidget_MouseCellChanged; 193 194 url.Undone += Url_Undone; 195 url.Redone += Url_Redone; 196 197 SelectedTemplateType = templateTypes.First().First(); 198 199 UpdateStatus(); 200 } 201 202 private void Url_Redone(object sender, EventArgs e) 203 { 204 if (boundsMode && (map.Bounds != dragBounds)) 205 { 206 dragBounds = map.Bounds; 207 dragEdge = -1; 208 209 UpdateTooltip(); 210 mapPanel.Invalidate(); 211 } 212 } 213 214 private void Url_Undone(object sender, EventArgs e) 215 { 216 if (boundsMode && (map.Bounds != dragBounds)) 217 { 218 dragBounds = map.Bounds; 219 dragEdge = -1; 220 221 UpdateTooltip(); 222 mapPanel.Invalidate(); 223 } 224 } 225 226 private void TemplateTypeMapPanel_MouseDown(object sender, MouseEventArgs e) 227 { 228 if ((SelectedTemplateType == null) || ((SelectedTemplateType.IconWidth * SelectedTemplateType.IconHeight) == 1)) 229 { 230 SelectedIcon = null; 231 } 232 else 233 { 234 if (e.Button == MouseButtons.Left) 235 { 236 var templateTypeMouseCell = templateTypeNavigationWidget.MouseCell; 237 if ((templateTypeMouseCell.X >= 0) && (templateTypeMouseCell.X < SelectedTemplateType.IconWidth)) 238 { 239 if ((templateTypeMouseCell.Y >= 0) && (templateTypeMouseCell.Y < SelectedTemplateType.IconHeight)) 240 { 241 if (SelectedTemplateType.IconMask[templateTypeMouseCell.X, templateTypeMouseCell.Y]) 242 { 243 SelectedIcon = templateTypeMouseCell; 244 } 245 } 246 } 247 } 248 else if (e.Button == MouseButtons.Right) 249 { 250 SelectedIcon = null; 251 } 252 } 253 } 254 255 private void TemplateTypeMapPanel_PostRender(object sender, RenderEventArgs e) 256 { 257 if (SelectedIcon.HasValue) 258 { 259 var selectedIconPen = new Pen(Color.Yellow, 2); 260 var cellSize = new Size(Globals.OriginalTileWidth / 4, Globals.OriginalTileHeight / 4); 261 var rect = new Rectangle(new Point(SelectedIcon.Value.X * cellSize.Width, SelectedIcon.Value.Y * cellSize.Height), cellSize); 262 e.Graphics.DrawRectangle(selectedIconPen, rect); 263 } 264 265 if (SelectedTemplateType != null) 266 { 267 var sizeStringFormat = new StringFormat 268 { 269 Alignment = StringAlignment.Center, 270 LineAlignment = StringAlignment.Center 271 }; 272 var sizeBackgroundBrush = new SolidBrush(Color.FromArgb(128, Color.Black)); 273 var sizeTextBrush = new SolidBrush(Color.White); 274 275 var text = string.Format("{0} ({1}x{2})", SelectedTemplateType.DisplayName, SelectedTemplateType.IconWidth, SelectedTemplateType.IconHeight); 276 var textSize = e.Graphics.MeasureString(text, SystemFonts.CaptionFont) + new SizeF(6.0f, 6.0f); 277 var textBounds = new RectangleF(new PointF(0, 0), textSize); 278 e.Graphics.Transform = new Matrix(); 279 e.Graphics.FillRectangle(sizeBackgroundBrush, textBounds); 280 e.Graphics.DrawString(text, SystemFonts.CaptionFont, sizeTextBrush, textBounds, sizeStringFormat); 281 } 282 } 283 284 private void TemplateTool_KeyDown(object sender, KeyEventArgs e) 285 { 286 if (e.KeyCode == Keys.ShiftKey) 287 { 288 EnterPlacementMode(); 289 } 290 else if (e.KeyCode == Keys.ControlKey) 291 { 292 EnterBoundsMode(); 293 } 294 } 295 296 private void TemplateTool_KeyUp(object sender, KeyEventArgs e) 297 { 298 if ((e.KeyCode == Keys.ShiftKey) || (e.KeyCode == Keys.ControlKey)) 299 { 300 ExitAllModes(); 301 } 302 } 303 304 private void TemplateTypeListView_SelectedIndexChanged(object sender, EventArgs e) 305 { 306 SelectedTemplateType = (templateTypeListView.SelectedItems.Count > 0) ? (templateTypeListView.SelectedItems[0].Tag as TemplateType) : null; 307 SelectedIcon = null; 308 } 309 310 private void MapPanel_MouseDown(object sender, MouseEventArgs e) 311 { 312 if (boundsMode) 313 { 314 dragEdge = DetectDragEdge(); 315 316 UpdateStatus(); 317 } 318 else if (placementMode) 319 { 320 if (e.Button == MouseButtons.Left) 321 { 322 SetTemplate(navigationWidget.MouseCell); 323 } 324 else if (e.Button == MouseButtons.Right) 325 { 326 RemoveTemplate(navigationWidget.MouseCell); 327 } 328 } 329 else if ((e.Button == MouseButtons.Left) || (e.Button == MouseButtons.Right)) 330 { 331 PickTemplate(navigationWidget.MouseCell, e.Button == MouseButtons.Left); 332 } 333 } 334 335 private void MapPanel_MouseUp(object sender, MouseEventArgs e) 336 { 337 if (boundsMode) 338 { 339 if (dragBounds != map.Bounds) 340 { 341 var oldBounds = map.Bounds; 342 void undoAction(UndoRedoEventArgs ure) 343 { 344 ure.Map.Bounds = oldBounds; 345 ure.MapPanel.Invalidate(); 346 } 347 348 void redoAction(UndoRedoEventArgs ure) 349 { 350 ure.Map.Bounds = dragBounds; 351 ure.MapPanel.Invalidate(); 352 } 353 354 map.Bounds = dragBounds; 355 356 url.Track(undoAction, redoAction); 357 mapPanel.Invalidate(); 358 } 359 360 dragEdge = -1; 361 362 UpdateStatus(); 363 } 364 else 365 { 366 if ((undoTemplates.Count > 0) || (redoTemplates.Count > 0)) 367 { 368 CommitChange(); 369 } 370 } 371 } 372 373 private void MapPanel_MouseMove(object sender, MouseEventArgs e) 374 { 375 if (!placementMode && (Control.ModifierKeys == Keys.Shift)) 376 { 377 EnterPlacementMode(); 378 } 379 else if (!boundsMode && (Control.ModifierKeys == Keys.Control)) 380 { 381 EnterBoundsMode(); 382 } 383 else if ((placementMode || boundsMode) && (Control.ModifierKeys == Keys.None)) 384 { 385 ExitAllModes(); 386 } 387 388 var cursor = Cursors.Default; 389 if (boundsMode) 390 { 391 switch ((dragEdge >= 0) ? dragEdge : DetectDragEdge()) 392 { 393 case 0: 394 case 4: 395 cursor = Cursors.SizeNS; 396 break; 397 case 2: 398 case 6: 399 cursor = Cursors.SizeWE; 400 break; 401 case 1: 402 case 5: 403 cursor = Cursors.SizeNESW; 404 break; 405 case 3: 406 case 7: 407 cursor = Cursors.SizeNWSE; 408 break; 409 } 410 } 411 Cursor.Current = cursor; 412 413 UpdateTooltip(); 414 } 415 416 private void MouseoverWidget_MouseCellChanged(object sender, MouseCellChangedEventArgs e) 417 { 418 if (dragEdge >= 0) 419 { 420 var endDrag = navigationWidget.MouseCell; 421 map.Metrics.Clip(ref endDrag, new Size(1, 1), Size.Empty); 422 423 switch (dragEdge) 424 { 425 case 0: 426 case 1: 427 case 7: 428 if (endDrag.Y < dragBounds.Bottom) 429 { 430 dragBounds.Height = dragBounds.Bottom - endDrag.Y; 431 dragBounds.Y = endDrag.Y; 432 } 433 break; 434 } 435 436 switch (dragEdge) 437 { 438 case 5: 439 case 6: 440 case 7: 441 if (endDrag.X < dragBounds.Right) 442 { 443 dragBounds.Width = dragBounds.Right - endDrag.X; 444 dragBounds.X = endDrag.X; 445 } 446 break; 447 } 448 449 switch (dragEdge) 450 { 451 case 3: 452 case 4: 453 case 5: 454 if (endDrag.Y > dragBounds.Top) 455 { 456 dragBounds.Height = endDrag.Y - dragBounds.Top; 457 } 458 break; 459 } 460 461 switch (dragEdge) 462 { 463 case 1: 464 case 2: 465 case 3: 466 if (endDrag.X > dragBounds.Left) 467 { 468 dragBounds.Width = endDrag.X - dragBounds.Left; 469 } 470 break; 471 } 472 473 mapPanel.Invalidate(); 474 } 475 else if (placementMode) 476 { 477 if (Control.MouseButtons == MouseButtons.Right) 478 { 479 RemoveTemplate(navigationWidget.MouseCell); 480 } 481 482 if (SelectedTemplateType != null) 483 { 484 foreach (var location in new Point[] { e.OldCell, e.NewCell }) 485 { 486 for (var y = 0; y < SelectedTemplateType.IconHeight; ++y) 487 { 488 for (var x = 0; x < SelectedTemplateType.IconWidth; ++x) 489 { 490 mapPanel.Invalidate(map, new Point(location.X + x, location.Y + y)); 491 } 492 } 493 } 494 } 495 } 496 else if((Control.MouseButtons == MouseButtons.Left) || (Control.MouseButtons == MouseButtons.Right)) 497 { 498 PickTemplate(navigationWidget.MouseCell, Control.MouseButtons == MouseButtons.Left); 499 } 500 } 501 502 private void RefreshMapPanel() 503 { 504 if (templateTypeNavigationWidget != null) 505 { 506 templateTypeNavigationWidget.Dispose(); 507 templateTypeNavigationWidget = null; 508 } 509 510 if (SelectedTemplateType != null) 511 { 512 templateTypeMapPanel.MapImage = SelectedTemplateType.Thumbnail; 513 514 var templateTypeMetrics = new CellMetrics(SelectedTemplateType.IconWidth, SelectedTemplateType.IconHeight); 515 templateTypeNavigationWidget = new NavigationWidget(templateTypeMapPanel, templateTypeMetrics, 516 new Size(Globals.OriginalTileWidth / 4, Globals.OriginalTileHeight / 4)); 517 templateTypeNavigationWidget.MouseoverSize = Size.Empty; 518 } 519 else 520 { 521 templateTypeMapPanel.MapImage = null; 522 } 523 } 524 525 private void SetTemplate(Point location) 526 { 527 if (SelectedTemplateType != null) 528 { 529 if (SelectedIcon.HasValue) 530 { 531 if (map.Metrics.GetCell(location, out int cell)) 532 { 533 if (!undoTemplates.ContainsKey(cell)) 534 { 535 undoTemplates[cell] = map.Templates[location]; 536 } 537 538 var icon = (SelectedIcon.Value.Y * SelectedTemplateType.IconWidth) + SelectedIcon.Value.X; 539 var template = new Template { Type = SelectedTemplateType, Icon = icon }; 540 map.Templates[cell] = template; 541 redoTemplates[cell] = template; 542 mapPanel.Invalidate(map, cell); 543 plugin.Dirty = true; 544 } 545 } 546 else 547 { 548 for (int y = 0, icon = 0; y < SelectedTemplateType.IconHeight; ++y) 549 { 550 for (var x = 0; x < SelectedTemplateType.IconWidth; ++x, ++icon) 551 { 552 var subLocation = new Point(location.X + x, location.Y + y); 553 if (map.Metrics.GetCell(subLocation, out int cell)) 554 { 555 if (!undoTemplates.ContainsKey(cell)) 556 { 557 undoTemplates[cell] = map.Templates[subLocation]; 558 } 559 } 560 } 561 } 562 563 for (int y = 0, icon = 0; y < SelectedTemplateType.IconHeight; ++y) 564 { 565 for (var x = 0; x < SelectedTemplateType.IconWidth; ++x, ++icon) 566 { 567 if (!SelectedTemplateType.IconMask[x, y]) 568 { 569 continue; 570 } 571 572 var subLocation = new Point(location.X + x, location.Y + y); 573 if (map.Metrics.GetCell(subLocation, out int cell)) 574 { 575 var template = new Template { Type = SelectedTemplateType, Icon = icon }; 576 map.Templates[cell] = template; 577 redoTemplates[cell] = template; 578 mapPanel.Invalidate(map, cell); 579 plugin.Dirty = true; 580 } 581 } 582 } 583 } 584 } 585 } 586 587 private void RemoveTemplate(Point location) 588 { 589 if (SelectedTemplateType != null) 590 { 591 if (SelectedIcon.HasValue) 592 { 593 if (map.Metrics.GetCell(location, out int cell)) 594 { 595 if (!undoTemplates.ContainsKey(cell)) 596 { 597 undoTemplates[cell] = map.Templates[location]; 598 } 599 600 map.Templates[cell] = null; 601 redoTemplates[cell] = null; 602 mapPanel.Invalidate(map, cell); 603 plugin.Dirty = true; 604 } 605 } 606 else 607 { 608 for (int y = 0, icon = 0; y < SelectedTemplateType.IconHeight; ++y) 609 { 610 for (var x = 0; x < SelectedTemplateType.IconWidth; ++x, ++icon) 611 { 612 var subLocation = new Point(location.X + x, location.Y + y); 613 if (map.Metrics.GetCell(subLocation, out int cell)) 614 { 615 if (!undoTemplates.ContainsKey(cell)) 616 { 617 undoTemplates[cell] = map.Templates[subLocation]; 618 } 619 } 620 } 621 } 622 623 for (int y = 0, icon = 0; y < SelectedTemplateType.IconHeight; ++y) 624 { 625 for (var x = 0; x < SelectedTemplateType.IconWidth; ++x, ++icon) 626 { 627 var subLocation = new Point(location.X + x, location.Y + y); 628 if (map.Metrics.GetCell(subLocation, out int cell)) 629 { 630 map.Templates[cell] = null; 631 redoTemplates[cell] = null; 632 mapPanel.Invalidate(map, cell); 633 plugin.Dirty = true; 634 } 635 } 636 } 637 } 638 } 639 } 640 641 private void EnterPlacementMode() 642 { 643 if (placementMode || boundsMode) 644 { 645 return; 646 } 647 648 placementMode = true; 649 650 navigationWidget.MouseoverSize = Size.Empty; 651 652 if (SelectedTemplateType != null) 653 { 654 for (var y = 0; y < SelectedTemplateType.IconHeight; ++y) 655 { 656 for (var x = 0; x < SelectedTemplateType.IconWidth; ++x) 657 { 658 mapPanel.Invalidate(map, new Point(navigationWidget.MouseCell.X + x, navigationWidget.MouseCell.Y + y)); 659 } 660 } 661 } 662 663 UpdateStatus(); 664 } 665 666 private void EnterBoundsMode() 667 { 668 if (boundsMode || placementMode) 669 { 670 return; 671 } 672 673 boundsMode = true; 674 dragBounds = map.Bounds; 675 676 navigationWidget.MouseoverSize = Size.Empty; 677 678 if (SelectedTemplateType != null) 679 { 680 for (var y = 0; y < SelectedTemplateType.IconHeight; ++y) 681 { 682 for (var x = 0; x < SelectedTemplateType.IconWidth; ++x) 683 { 684 mapPanel.Invalidate(map, new Point(navigationWidget.MouseCell.X + x, navigationWidget.MouseCell.Y + y)); 685 } 686 } 687 } 688 689 UpdateTooltip(); 690 UpdateStatus(); 691 } 692 693 private void ExitAllModes() 694 { 695 if (!placementMode && !boundsMode) 696 { 697 return; 698 } 699 700 boundsMode = false; 701 dragEdge = -1; 702 dragBounds = Rectangle.Empty; 703 placementMode = false; 704 705 navigationWidget.MouseoverSize = new Size(1, 1); 706 707 if (SelectedTemplateType != null) 708 { 709 for (var y = 0; y < SelectedTemplateType.IconHeight; ++y) 710 { 711 for (var x = 0; x < SelectedTemplateType.IconWidth; ++x) 712 { 713 mapPanel.Invalidate(map, new Point(navigationWidget.MouseCell.X + x, navigationWidget.MouseCell.Y + y)); 714 } 715 } 716 } 717 718 UpdateTooltip(); 719 UpdateStatus(); 720 } 721 722 private void UpdateTooltip() 723 { 724 if (boundsMode) 725 { 726 var tooltip = string.Format("X = {0}\nY = {1}\nWidth = {2}\nHeight = {3}", dragBounds.Left, dragBounds.Top, dragBounds.Width, dragBounds.Height); 727 var textSize = TextRenderer.MeasureText(tooltip, SystemFonts.CaptionFont); 728 var tooltipSize = new Size(textSize.Width + 6, textSize.Height + 6); 729 730 var tooltipPosition = mapPanel.PointToClient(Control.MousePosition); 731 switch (dragEdge) 732 { 733 case -1: 734 case 0: 735 case 1: 736 case 7: 737 tooltipPosition.Y -= tooltipSize.Height; 738 break; 739 } 740 switch (dragEdge) 741 { 742 case -1: 743 case 5: 744 case 6: 745 case 7: 746 tooltipPosition.X -= tooltipSize.Width; 747 break; 748 } 749 750 var screenPosition = mapPanel.PointToScreen(tooltipPosition); 751 var screen = Screen.FromControl(mapPanel); 752 screenPosition.X = Math.Max(0, Math.Min(screen.WorkingArea.Width - tooltipSize.Width, screenPosition.X)); 753 screenPosition.Y = Math.Max(0, Math.Min(screen.WorkingArea.Height - tooltipSize.Height, screenPosition.Y)); 754 tooltipPosition = mapPanel.PointToClient(screenPosition); 755 756 mouseTooltip.Show(tooltip, mapPanel, tooltipPosition.X, tooltipPosition.Y); 757 } 758 else 759 { 760 mouseTooltip.Hide(mapPanel); 761 } 762 } 763 764 private void PickTemplate(Point location, bool wholeTemplate) 765 { 766 if (map.Metrics.GetCell(location, out int cell)) 767 { 768 var template = map.Templates[cell]; 769 if (template != null) 770 { 771 SelectedTemplateType = template.Type; 772 } 773 else 774 { 775 SelectedTemplateType = map.TemplateTypes.Where(t => t.Equals("clear1")).FirstOrDefault(); 776 } 777 778 if (!wholeTemplate && ((SelectedTemplateType.IconWidth * SelectedTemplateType.IconHeight) > 1)) 779 { 780 var icon = template?.Icon ?? 0; 781 SelectedIcon = new Point(icon % SelectedTemplateType.IconWidth, icon / SelectedTemplateType.IconWidth); 782 } 783 else 784 { 785 SelectedIcon = null; 786 } 787 } 788 } 789 790 private int DetectDragEdge() 791 { 792 var mouseCell = navigationWidget.MouseCell; 793 var mousePixel = navigationWidget.MouseSubPixel; 794 var topEdge = 795 ((mouseCell.Y == dragBounds.Top) && (mousePixel.Y <= (Globals.PixelHeight / 4))) || 796 ((mouseCell.Y == dragBounds.Top - 1) && (mousePixel.Y >= (3 * Globals.PixelHeight / 4))); 797 var bottomEdge = 798 ((mouseCell.Y == dragBounds.Bottom) && (mousePixel.Y <= (Globals.PixelHeight / 4))) || 799 ((mouseCell.Y == dragBounds.Bottom - 1) && (mousePixel.Y >= (3 * Globals.PixelHeight / 4))); 800 var leftEdge = 801 ((mouseCell.X == dragBounds.Left) && (mousePixel.X <= (Globals.PixelWidth / 4))) || 802 ((mouseCell.X == dragBounds.Left - 1) && (mousePixel.X >= (3 * Globals.PixelWidth / 4))); 803 var rightEdge = 804 ((mouseCell.X == dragBounds.Right) && (mousePixel.X <= (Globals.PixelHeight / 4))) || 805 ((mouseCell.X == dragBounds.Right - 1) && (mousePixel.X >= (3 * Globals.PixelHeight / 4))); 806 if (topEdge) 807 { 808 if (rightEdge) 809 { 810 return 1; 811 } 812 else if (leftEdge) 813 { 814 return 7; 815 } 816 else 817 { 818 return 0; 819 } 820 } 821 else if (bottomEdge) 822 { 823 if (rightEdge) 824 { 825 return 3; 826 } 827 else if (leftEdge) 828 { 829 return 5; 830 } 831 else 832 { 833 return 4; 834 } 835 } 836 else if (rightEdge) 837 { 838 return 2; 839 } 840 else if (leftEdge) 841 { 842 return 6; 843 } 844 else 845 { 846 return -1; 847 } 848 } 849 850 private void CommitChange() 851 { 852 var undoTemplates2 = new Dictionary<int, Template>(undoTemplates); 853 void undoAction(UndoRedoEventArgs e) 854 { 855 foreach (var kv in undoTemplates2) 856 { 857 e.Map.Templates[kv.Key] = kv.Value; 858 } 859 e.MapPanel.Invalidate(e.Map, undoTemplates2.Keys); 860 } 861 862 var redoTemplates2 = new Dictionary<int, Template>(redoTemplates); 863 void redoAction(UndoRedoEventArgs e) 864 { 865 foreach (var kv in redoTemplates2) 866 { 867 e.Map.Templates[kv.Key] = kv.Value; 868 } 869 e.MapPanel.Invalidate(e.Map, redoTemplates2.Keys); 870 } 871 872 undoTemplates.Clear(); 873 redoTemplates.Clear(); 874 875 url.Track(undoAction, redoAction); 876 } 877 878 private void UpdateStatus() 879 { 880 if (placementMode) 881 { 882 statusLbl.Text = "Left-Click to place template, Right-Click to clear template"; 883 } 884 else if (boundsMode) 885 { 886 if (dragEdge >= 0) 887 { 888 statusLbl.Text = "Release left button to end dragging map bounds edge"; 889 } 890 else 891 { 892 statusLbl.Text = "Left-Click a map bounds edge to start dragging"; 893 } 894 } 895 else 896 { 897 statusLbl.Text = "Shift to enter placement mode, Ctrl to enter map bounds mode, Left-Click to pick whole template, Right-Click to pick individual template tile"; 898 } 899 } 900 901 protected override void PreRenderMap() 902 { 903 base.PreRenderMap(); 904 905 previewMap = map.Clone(); 906 if (placementMode) 907 { 908 var location = navigationWidget.MouseCell; 909 if (SelectedTemplateType != null) 910 { 911 if (SelectedIcon.HasValue) 912 { 913 if (previewMap.Metrics.GetCell(location, out int cell)) 914 { 915 var icon = (SelectedIcon.Value.Y * SelectedTemplateType.IconWidth) + SelectedIcon.Value.X; 916 previewMap.Templates[cell] = new Template { Type = SelectedTemplateType, Icon = icon }; 917 } 918 } 919 else 920 { 921 int icon = 0; 922 for (var y = 0; y < SelectedTemplateType.IconHeight; ++y) 923 { 924 for (var x = 0; x < SelectedTemplateType.IconWidth; ++x, ++icon) 925 { 926 if (!SelectedTemplateType.IconMask[x, y]) 927 { 928 continue; 929 } 930 931 var subLocation = new Point(location.X + x, location.Y + y); 932 if (previewMap.Metrics.GetCell(subLocation, out int cell)) 933 { 934 previewMap.Templates[cell] = new Template { Type = SelectedTemplateType, Icon = icon }; 935 } 936 } 937 } 938 } 939 } 940 } 941 } 942 943 protected override void PostRenderMap(Graphics graphics) 944 { 945 base.PostRenderMap(graphics); 946 947 if (boundsMode) 948 { 949 var bounds = Rectangle.FromLTRB( 950 dragBounds.Left * Globals.TileWidth, 951 dragBounds.Top * Globals.TileHeight, 952 dragBounds.Right * Globals.TileWidth, 953 dragBounds.Bottom * Globals.TileHeight 954 ); 955 956 var boundsPen = new Pen(Color.Red, 8.0f); 957 graphics.DrawRectangle(boundsPen, bounds); 958 } 959 else if (placementMode) 960 { 961 var location = navigationWidget.MouseCell; 962 if (SelectedTemplateType != null) 963 { 964 var previewPen = new Pen(Color.Green, 4.0f); 965 var previewBounds = new Rectangle( 966 location.X * Globals.TileWidth, 967 location.Y * Globals.TileHeight, 968 (SelectedIcon.HasValue ? 1 : SelectedTemplateType.IconWidth) * Globals.TileWidth, 969 (SelectedIcon.HasValue ? 1 : SelectedTemplateType.IconHeight) * Globals.TileHeight 970 ); 971 graphics.DrawRectangle(previewPen, previewBounds); 972 } 973 } 974 } 975 976 #region IDisposable Support 977 private bool disposedValue = false; 978 979 protected override void Dispose(bool disposing) 980 { 981 if (!disposedValue) 982 { 983 if (disposing) 984 { 985 mapPanel.MouseDown -= MapPanel_MouseDown; 986 mapPanel.MouseUp -= MapPanel_MouseUp; 987 mapPanel.MouseMove -= MapPanel_MouseMove; 988 (mapPanel as Control).KeyDown -= TemplateTool_KeyDown; 989 (mapPanel as Control).KeyUp -= TemplateTool_KeyUp; 990 991 templateTypeListView.SelectedIndexChanged -= TemplateTypeListView_SelectedIndexChanged; 992 993 templateTypeMapPanel.MouseDown -= TemplateTypeMapPanel_MouseDown; 994 templateTypeMapPanel.PostRender -= TemplateTypeMapPanel_PostRender; 995 996 navigationWidget.MouseCellChanged -= MouseoverWidget_MouseCellChanged; 997 998 url.Undone -= Url_Undone; 999 url.Redone -= Url_Redone; 1000 } 1001 disposedValue = true; 1002 } 1003 1004 base.Dispose(disposing); 1005 } 1006 #endregion 1007 } 1008 }