// // Copyright 2020 Electronic Arts Inc. // // The Command & Conquer Map Editor and corresponding source code is free // software: you can redistribute it and/or modify it under the terms of // the GNU General Public License as published by the Free Software Foundation, // either version 3 of the License, or (at your option) any later version. // The Command & Conquer Map Editor and corresponding source code is distributed // in the hope that it will be useful, but with permitted additional restrictions // under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT // distributed with this program. You should have received a copy of the // GNU General Public License along with permitted additional restrictions // with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection using MobiusEditor.Controls; using MobiusEditor.Event; using MobiusEditor.Interface; using MobiusEditor.Model; using MobiusEditor.Utility; using MobiusEditor.Widgets; using System; using System.Collections.Generic; using System.Drawing; using System.Drawing.Drawing2D; using System.Linq; using System.Text.RegularExpressions; using System.Windows.Forms; namespace MobiusEditor.Tools { public class TemplateTool : ViewTool { private static readonly Regex CategoryRegex = new Regex(@"^([a-z]*)", RegexOptions.Compiled); private readonly ListView templateTypeListView; private readonly MapPanel templateTypeMapPanel; private readonly ToolTip mouseTooltip; private readonly Dictionary undoTemplates = new Dictionary(); private readonly Dictionary redoTemplates = new Dictionary(); private Map previewMap; protected override Map RenderMap => previewMap; private bool placementMode; private bool boundsMode; private Rectangle dragBounds; private int dragEdge = -1; private TemplateType selectedTemplateType; private TemplateType SelectedTemplateType { get => selectedTemplateType; set { if (selectedTemplateType != value) { if (placementMode && (selectedTemplateType != null)) { for (var y = 0; y < selectedTemplateType.IconHeight; ++y) { for (var x = 0; x < selectedTemplateType.IconWidth; ++x) { mapPanel.Invalidate(map, new Point(navigationWidget.MouseCell.X + x, navigationWidget.MouseCell.Y + y)); } } } selectedTemplateType = value; templateTypeListView.BeginUpdate(); templateTypeListView.SelectedIndexChanged -= TemplateTypeListView_SelectedIndexChanged; foreach (ListViewItem item in templateTypeListView.Items) { item.Selected = item.Tag == selectedTemplateType; } if (templateTypeListView.SelectedIndices.Count > 0) { templateTypeListView.EnsureVisible(templateTypeListView.SelectedIndices[0]); } templateTypeListView.SelectedIndexChanged += TemplateTypeListView_SelectedIndexChanged; templateTypeListView.EndUpdate(); if (placementMode && (selectedTemplateType != null)) { for (var y = 0; y < selectedTemplateType.IconHeight; ++y) { for (var x = 0; x < selectedTemplateType.IconWidth; ++x) { mapPanel.Invalidate(map, new Point(navigationWidget.MouseCell.X + x, navigationWidget.MouseCell.Y + y)); } } } RefreshMapPanel(); } } } private Point? selectedIcon; private Point? SelectedIcon { get => selectedIcon; set { if (selectedIcon != value) { selectedIcon = value; templateTypeMapPanel.Invalidate(); if (placementMode && (SelectedTemplateType != null)) { for (var y = 0; y < SelectedTemplateType.IconHeight; ++y) { for (var x = 0; x < SelectedTemplateType.IconWidth; ++x) { mapPanel.Invalidate(map, new Point(navigationWidget.MouseCell.X + x, navigationWidget.MouseCell.Y + y)); } } } } } } private NavigationWidget templateTypeNavigationWidget; public TemplateTool(MapPanel mapPanel, MapLayerFlag layers, ToolStripStatusLabel statusLbl, ListView templateTypeListView, MapPanel templateTypeMapPanel, ToolTip mouseTooltip, IGamePlugin plugin, UndoRedoList url) : base(mapPanel, layers, statusLbl, plugin, url) { previewMap = map; this.mapPanel.MouseDown += MapPanel_MouseDown; this.mapPanel.MouseUp += MapPanel_MouseUp; this.mapPanel.MouseMove += MapPanel_MouseMove; (this.mapPanel as Control).KeyDown += TemplateTool_KeyDown; (this.mapPanel as Control).KeyUp += TemplateTool_KeyUp; this.templateTypeListView = templateTypeListView; this.templateTypeListView.SelectedIndexChanged += TemplateTypeListView_SelectedIndexChanged; string templateCategory(TemplateType template) { var m = CategoryRegex.Match(template.Name); return m.Success ? m.Groups[1].Value : string.Empty; } var templateTypes = plugin.Map.TemplateTypes .Where(t => (t.Thumbnail != null) && t.Theaters.Contains(plugin.Map.Theater) && ((t.Flag & TemplateTypeFlag.Clear) == TemplateTypeFlag.None)) .GroupBy(t => templateCategory(t)).OrderBy(g => g.Key); var templateTypeImages = templateTypes.SelectMany(g => g).Select(t => t.Thumbnail); var maxWidth = templateTypeImages.Max(t => t.Width); var maxHeight = templateTypeImages.Max(t => t.Height); var imageList = new ImageList(); imageList.Images.AddRange(templateTypeImages.ToArray()); imageList.ImageSize = new Size(maxWidth, maxHeight); imageList.ColorDepth = ColorDepth.Depth24Bit; this.templateTypeListView.BeginUpdate(); this.templateTypeListView.LargeImageList = imageList; var imageIndex = 0; foreach (var templateTypeGroup in templateTypes) { var group = new ListViewGroup(templateTypeGroup.Key); this.templateTypeListView.Groups.Add(group); foreach (var templateType in templateTypeGroup) { var item = new ListViewItem(templateType.DisplayName, imageIndex++) { Group = group, Tag = templateType }; this.templateTypeListView.Items.Add(item); } } this.templateTypeListView.EndUpdate(); this.templateTypeMapPanel = templateTypeMapPanel; this.templateTypeMapPanel.MouseDown += TemplateTypeMapPanel_MouseDown; this.templateTypeMapPanel.PostRender += TemplateTypeMapPanel_PostRender; this.templateTypeMapPanel.BackColor = Color.Black; this.templateTypeMapPanel.MaxZoom = 1; this.mouseTooltip = mouseTooltip; navigationWidget.MouseCellChanged += MouseoverWidget_MouseCellChanged; url.Undone += Url_Undone; url.Redone += Url_Redone; SelectedTemplateType = templateTypes.First().First(); UpdateStatus(); } private void Url_Redone(object sender, EventArgs e) { if (boundsMode && (map.Bounds != dragBounds)) { dragBounds = map.Bounds; dragEdge = -1; UpdateTooltip(); mapPanel.Invalidate(); } } private void Url_Undone(object sender, EventArgs e) { if (boundsMode && (map.Bounds != dragBounds)) { dragBounds = map.Bounds; dragEdge = -1; UpdateTooltip(); mapPanel.Invalidate(); } } private void TemplateTypeMapPanel_MouseDown(object sender, MouseEventArgs e) { if ((SelectedTemplateType == null) || ((SelectedTemplateType.IconWidth * SelectedTemplateType.IconHeight) == 1)) { SelectedIcon = null; } else { if (e.Button == MouseButtons.Left) { var templateTypeMouseCell = templateTypeNavigationWidget.MouseCell; if ((templateTypeMouseCell.X >= 0) && (templateTypeMouseCell.X < SelectedTemplateType.IconWidth)) { if ((templateTypeMouseCell.Y >= 0) && (templateTypeMouseCell.Y < SelectedTemplateType.IconHeight)) { if (SelectedTemplateType.IconMask[templateTypeMouseCell.X, templateTypeMouseCell.Y]) { SelectedIcon = templateTypeMouseCell; } } } } else if (e.Button == MouseButtons.Right) { SelectedIcon = null; } } } private void TemplateTypeMapPanel_PostRender(object sender, RenderEventArgs e) { if (SelectedIcon.HasValue) { var selectedIconPen = new Pen(Color.Yellow, 2); var cellSize = new Size(Globals.OriginalTileWidth / 4, Globals.OriginalTileHeight / 4); var rect = new Rectangle(new Point(SelectedIcon.Value.X * cellSize.Width, SelectedIcon.Value.Y * cellSize.Height), cellSize); e.Graphics.DrawRectangle(selectedIconPen, rect); } if (SelectedTemplateType != null) { var sizeStringFormat = new StringFormat { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center }; var sizeBackgroundBrush = new SolidBrush(Color.FromArgb(128, Color.Black)); var sizeTextBrush = new SolidBrush(Color.White); var text = string.Format("{0} ({1}x{2})", SelectedTemplateType.DisplayName, SelectedTemplateType.IconWidth, SelectedTemplateType.IconHeight); var textSize = e.Graphics.MeasureString(text, SystemFonts.CaptionFont) + new SizeF(6.0f, 6.0f); var textBounds = new RectangleF(new PointF(0, 0), textSize); e.Graphics.Transform = new Matrix(); e.Graphics.FillRectangle(sizeBackgroundBrush, textBounds); e.Graphics.DrawString(text, SystemFonts.CaptionFont, sizeTextBrush, textBounds, sizeStringFormat); } } private void TemplateTool_KeyDown(object sender, KeyEventArgs e) { if (e.KeyCode == Keys.ShiftKey) { EnterPlacementMode(); } else if (e.KeyCode == Keys.ControlKey) { EnterBoundsMode(); } } private void TemplateTool_KeyUp(object sender, KeyEventArgs e) { if ((e.KeyCode == Keys.ShiftKey) || (e.KeyCode == Keys.ControlKey)) { ExitAllModes(); } } private void TemplateTypeListView_SelectedIndexChanged(object sender, EventArgs e) { SelectedTemplateType = (templateTypeListView.SelectedItems.Count > 0) ? (templateTypeListView.SelectedItems[0].Tag as TemplateType) : null; SelectedIcon = null; } private void MapPanel_MouseDown(object sender, MouseEventArgs e) { if (boundsMode) { dragEdge = DetectDragEdge(); UpdateStatus(); } else if (placementMode) { if (e.Button == MouseButtons.Left) { SetTemplate(navigationWidget.MouseCell); } else if (e.Button == MouseButtons.Right) { RemoveTemplate(navigationWidget.MouseCell); } } else if ((e.Button == MouseButtons.Left) || (e.Button == MouseButtons.Right)) { PickTemplate(navigationWidget.MouseCell, e.Button == MouseButtons.Left); } } private void MapPanel_MouseUp(object sender, MouseEventArgs e) { if (boundsMode) { if (dragBounds != map.Bounds) { var oldBounds = map.Bounds; void undoAction(UndoRedoEventArgs ure) { ure.Map.Bounds = oldBounds; ure.MapPanel.Invalidate(); } void redoAction(UndoRedoEventArgs ure) { ure.Map.Bounds = dragBounds; ure.MapPanel.Invalidate(); } map.Bounds = dragBounds; url.Track(undoAction, redoAction); mapPanel.Invalidate(); } dragEdge = -1; UpdateStatus(); } else { if ((undoTemplates.Count > 0) || (redoTemplates.Count > 0)) { CommitChange(); } } } private void MapPanel_MouseMove(object sender, MouseEventArgs e) { if (!placementMode && (Control.ModifierKeys == Keys.Shift)) { EnterPlacementMode(); } else if (!boundsMode && (Control.ModifierKeys == Keys.Control)) { EnterBoundsMode(); } else if ((placementMode || boundsMode) && (Control.ModifierKeys == Keys.None)) { ExitAllModes(); } var cursor = Cursors.Default; if (boundsMode) { switch ((dragEdge >= 0) ? dragEdge : DetectDragEdge()) { case 0: case 4: cursor = Cursors.SizeNS; break; case 2: case 6: cursor = Cursors.SizeWE; break; case 1: case 5: cursor = Cursors.SizeNESW; break; case 3: case 7: cursor = Cursors.SizeNWSE; break; } } Cursor.Current = cursor; UpdateTooltip(); } private void MouseoverWidget_MouseCellChanged(object sender, MouseCellChangedEventArgs e) { if (dragEdge >= 0) { var endDrag = navigationWidget.MouseCell; map.Metrics.Clip(ref endDrag, new Size(1, 1), Size.Empty); switch (dragEdge) { case 0: case 1: case 7: if (endDrag.Y < dragBounds.Bottom) { dragBounds.Height = dragBounds.Bottom - endDrag.Y; dragBounds.Y = endDrag.Y; } break; } switch (dragEdge) { case 5: case 6: case 7: if (endDrag.X < dragBounds.Right) { dragBounds.Width = dragBounds.Right - endDrag.X; dragBounds.X = endDrag.X; } break; } switch (dragEdge) { case 3: case 4: case 5: if (endDrag.Y > dragBounds.Top) { dragBounds.Height = endDrag.Y - dragBounds.Top; } break; } switch (dragEdge) { case 1: case 2: case 3: if (endDrag.X > dragBounds.Left) { dragBounds.Width = endDrag.X - dragBounds.Left; } break; } mapPanel.Invalidate(); } else if (placementMode) { if (Control.MouseButtons == MouseButtons.Right) { RemoveTemplate(navigationWidget.MouseCell); } if (SelectedTemplateType != null) { foreach (var location in new Point[] { e.OldCell, e.NewCell }) { for (var y = 0; y < SelectedTemplateType.IconHeight; ++y) { for (var x = 0; x < SelectedTemplateType.IconWidth; ++x) { mapPanel.Invalidate(map, new Point(location.X + x, location.Y + y)); } } } } } else if((Control.MouseButtons == MouseButtons.Left) || (Control.MouseButtons == MouseButtons.Right)) { PickTemplate(navigationWidget.MouseCell, Control.MouseButtons == MouseButtons.Left); } } private void RefreshMapPanel() { if (templateTypeNavigationWidget != null) { templateTypeNavigationWidget.Dispose(); templateTypeNavigationWidget = null; } if (SelectedTemplateType != null) { templateTypeMapPanel.MapImage = SelectedTemplateType.Thumbnail; var templateTypeMetrics = new CellMetrics(SelectedTemplateType.IconWidth, SelectedTemplateType.IconHeight); templateTypeNavigationWidget = new NavigationWidget(templateTypeMapPanel, templateTypeMetrics, new Size(Globals.OriginalTileWidth / 4, Globals.OriginalTileHeight / 4)); templateTypeNavigationWidget.MouseoverSize = Size.Empty; } else { templateTypeMapPanel.MapImage = null; } } private void SetTemplate(Point location) { if (SelectedTemplateType != null) { if (SelectedIcon.HasValue) { if (map.Metrics.GetCell(location, out int cell)) { if (!undoTemplates.ContainsKey(cell)) { undoTemplates[cell] = map.Templates[location]; } var icon = (SelectedIcon.Value.Y * SelectedTemplateType.IconWidth) + SelectedIcon.Value.X; var template = new Template { Type = SelectedTemplateType, Icon = icon }; map.Templates[cell] = template; redoTemplates[cell] = template; mapPanel.Invalidate(map, cell); plugin.Dirty = true; } } else { for (int y = 0, icon = 0; y < SelectedTemplateType.IconHeight; ++y) { for (var x = 0; x < SelectedTemplateType.IconWidth; ++x, ++icon) { var subLocation = new Point(location.X + x, location.Y + y); if (map.Metrics.GetCell(subLocation, out int cell)) { if (!undoTemplates.ContainsKey(cell)) { undoTemplates[cell] = map.Templates[subLocation]; } } } } for (int y = 0, icon = 0; y < SelectedTemplateType.IconHeight; ++y) { for (var x = 0; x < SelectedTemplateType.IconWidth; ++x, ++icon) { if (!SelectedTemplateType.IconMask[x, y]) { continue; } var subLocation = new Point(location.X + x, location.Y + y); if (map.Metrics.GetCell(subLocation, out int cell)) { var template = new Template { Type = SelectedTemplateType, Icon = icon }; map.Templates[cell] = template; redoTemplates[cell] = template; mapPanel.Invalidate(map, cell); plugin.Dirty = true; } } } } } } private void RemoveTemplate(Point location) { if (SelectedTemplateType != null) { if (SelectedIcon.HasValue) { if (map.Metrics.GetCell(location, out int cell)) { if (!undoTemplates.ContainsKey(cell)) { undoTemplates[cell] = map.Templates[location]; } map.Templates[cell] = null; redoTemplates[cell] = null; mapPanel.Invalidate(map, cell); plugin.Dirty = true; } } else { for (int y = 0, icon = 0; y < SelectedTemplateType.IconHeight; ++y) { for (var x = 0; x < SelectedTemplateType.IconWidth; ++x, ++icon) { var subLocation = new Point(location.X + x, location.Y + y); if (map.Metrics.GetCell(subLocation, out int cell)) { if (!undoTemplates.ContainsKey(cell)) { undoTemplates[cell] = map.Templates[subLocation]; } } } } for (int y = 0, icon = 0; y < SelectedTemplateType.IconHeight; ++y) { for (var x = 0; x < SelectedTemplateType.IconWidth; ++x, ++icon) { var subLocation = new Point(location.X + x, location.Y + y); if (map.Metrics.GetCell(subLocation, out int cell)) { map.Templates[cell] = null; redoTemplates[cell] = null; mapPanel.Invalidate(map, cell); plugin.Dirty = true; } } } } } } private void EnterPlacementMode() { if (placementMode || boundsMode) { return; } placementMode = true; navigationWidget.MouseoverSize = Size.Empty; if (SelectedTemplateType != null) { for (var y = 0; y < SelectedTemplateType.IconHeight; ++y) { for (var x = 0; x < SelectedTemplateType.IconWidth; ++x) { mapPanel.Invalidate(map, new Point(navigationWidget.MouseCell.X + x, navigationWidget.MouseCell.Y + y)); } } } UpdateStatus(); } private void EnterBoundsMode() { if (boundsMode || placementMode) { return; } boundsMode = true; dragBounds = map.Bounds; navigationWidget.MouseoverSize = Size.Empty; if (SelectedTemplateType != null) { for (var y = 0; y < SelectedTemplateType.IconHeight; ++y) { for (var x = 0; x < SelectedTemplateType.IconWidth; ++x) { mapPanel.Invalidate(map, new Point(navigationWidget.MouseCell.X + x, navigationWidget.MouseCell.Y + y)); } } } UpdateTooltip(); UpdateStatus(); } private void ExitAllModes() { if (!placementMode && !boundsMode) { return; } boundsMode = false; dragEdge = -1; dragBounds = Rectangle.Empty; placementMode = false; navigationWidget.MouseoverSize = new Size(1, 1); if (SelectedTemplateType != null) { for (var y = 0; y < SelectedTemplateType.IconHeight; ++y) { for (var x = 0; x < SelectedTemplateType.IconWidth; ++x) { mapPanel.Invalidate(map, new Point(navigationWidget.MouseCell.X + x, navigationWidget.MouseCell.Y + y)); } } } UpdateTooltip(); UpdateStatus(); } private void UpdateTooltip() { if (boundsMode) { var tooltip = string.Format("X = {0}\nY = {1}\nWidth = {2}\nHeight = {3}", dragBounds.Left, dragBounds.Top, dragBounds.Width, dragBounds.Height); var textSize = TextRenderer.MeasureText(tooltip, SystemFonts.CaptionFont); var tooltipSize = new Size(textSize.Width + 6, textSize.Height + 6); var tooltipPosition = mapPanel.PointToClient(Control.MousePosition); switch (dragEdge) { case -1: case 0: case 1: case 7: tooltipPosition.Y -= tooltipSize.Height; break; } switch (dragEdge) { case -1: case 5: case 6: case 7: tooltipPosition.X -= tooltipSize.Width; break; } var screenPosition = mapPanel.PointToScreen(tooltipPosition); var screen = Screen.FromControl(mapPanel); screenPosition.X = Math.Max(0, Math.Min(screen.WorkingArea.Width - tooltipSize.Width, screenPosition.X)); screenPosition.Y = Math.Max(0, Math.Min(screen.WorkingArea.Height - tooltipSize.Height, screenPosition.Y)); tooltipPosition = mapPanel.PointToClient(screenPosition); mouseTooltip.Show(tooltip, mapPanel, tooltipPosition.X, tooltipPosition.Y); } else { mouseTooltip.Hide(mapPanel); } } private void PickTemplate(Point location, bool wholeTemplate) { if (map.Metrics.GetCell(location, out int cell)) { var template = map.Templates[cell]; if (template != null) { SelectedTemplateType = template.Type; } else { SelectedTemplateType = map.TemplateTypes.Where(t => t.Equals("clear1")).FirstOrDefault(); } if (!wholeTemplate && ((SelectedTemplateType.IconWidth * SelectedTemplateType.IconHeight) > 1)) { var icon = template?.Icon ?? 0; SelectedIcon = new Point(icon % SelectedTemplateType.IconWidth, icon / SelectedTemplateType.IconWidth); } else { SelectedIcon = null; } } } private int DetectDragEdge() { var mouseCell = navigationWidget.MouseCell; var mousePixel = navigationWidget.MouseSubPixel; var topEdge = ((mouseCell.Y == dragBounds.Top) && (mousePixel.Y <= (Globals.PixelHeight / 4))) || ((mouseCell.Y == dragBounds.Top - 1) && (mousePixel.Y >= (3 * Globals.PixelHeight / 4))); var bottomEdge = ((mouseCell.Y == dragBounds.Bottom) && (mousePixel.Y <= (Globals.PixelHeight / 4))) || ((mouseCell.Y == dragBounds.Bottom - 1) && (mousePixel.Y >= (3 * Globals.PixelHeight / 4))); var leftEdge = ((mouseCell.X == dragBounds.Left) && (mousePixel.X <= (Globals.PixelWidth / 4))) || ((mouseCell.X == dragBounds.Left - 1) && (mousePixel.X >= (3 * Globals.PixelWidth / 4))); var rightEdge = ((mouseCell.X == dragBounds.Right) && (mousePixel.X <= (Globals.PixelHeight / 4))) || ((mouseCell.X == dragBounds.Right - 1) && (mousePixel.X >= (3 * Globals.PixelHeight / 4))); if (topEdge) { if (rightEdge) { return 1; } else if (leftEdge) { return 7; } else { return 0; } } else if (bottomEdge) { if (rightEdge) { return 3; } else if (leftEdge) { return 5; } else { return 4; } } else if (rightEdge) { return 2; } else if (leftEdge) { return 6; } else { return -1; } } private void CommitChange() { var undoTemplates2 = new Dictionary(undoTemplates); void undoAction(UndoRedoEventArgs e) { foreach (var kv in undoTemplates2) { e.Map.Templates[kv.Key] = kv.Value; } e.MapPanel.Invalidate(e.Map, undoTemplates2.Keys); } var redoTemplates2 = new Dictionary(redoTemplates); void redoAction(UndoRedoEventArgs e) { foreach (var kv in redoTemplates2) { e.Map.Templates[kv.Key] = kv.Value; } e.MapPanel.Invalidate(e.Map, redoTemplates2.Keys); } undoTemplates.Clear(); redoTemplates.Clear(); url.Track(undoAction, redoAction); } private void UpdateStatus() { if (placementMode) { statusLbl.Text = "Left-Click to place template, Right-Click to clear template"; } else if (boundsMode) { if (dragEdge >= 0) { statusLbl.Text = "Release left button to end dragging map bounds edge"; } else { statusLbl.Text = "Left-Click a map bounds edge to start dragging"; } } else { 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"; } } protected override void PreRenderMap() { base.PreRenderMap(); previewMap = map.Clone(); if (placementMode) { var location = navigationWidget.MouseCell; if (SelectedTemplateType != null) { if (SelectedIcon.HasValue) { if (previewMap.Metrics.GetCell(location, out int cell)) { var icon = (SelectedIcon.Value.Y * SelectedTemplateType.IconWidth) + SelectedIcon.Value.X; previewMap.Templates[cell] = new Template { Type = SelectedTemplateType, Icon = icon }; } } else { int icon = 0; for (var y = 0; y < SelectedTemplateType.IconHeight; ++y) { for (var x = 0; x < SelectedTemplateType.IconWidth; ++x, ++icon) { if (!SelectedTemplateType.IconMask[x, y]) { continue; } var subLocation = new Point(location.X + x, location.Y + y); if (previewMap.Metrics.GetCell(subLocation, out int cell)) { previewMap.Templates[cell] = new Template { Type = SelectedTemplateType, Icon = icon }; } } } } } } } protected override void PostRenderMap(Graphics graphics) { base.PostRenderMap(graphics); if (boundsMode) { var bounds = Rectangle.FromLTRB( dragBounds.Left * Globals.TileWidth, dragBounds.Top * Globals.TileHeight, dragBounds.Right * Globals.TileWidth, dragBounds.Bottom * Globals.TileHeight ); var boundsPen = new Pen(Color.Red, 8.0f); graphics.DrawRectangle(boundsPen, bounds); } else if (placementMode) { var location = navigationWidget.MouseCell; if (SelectedTemplateType != null) { var previewPen = new Pen(Color.Green, 4.0f); var previewBounds = new Rectangle( location.X * Globals.TileWidth, location.Y * Globals.TileHeight, (SelectedIcon.HasValue ? 1 : SelectedTemplateType.IconWidth) * Globals.TileWidth, (SelectedIcon.HasValue ? 1 : SelectedTemplateType.IconHeight) * Globals.TileHeight ); graphics.DrawRectangle(previewPen, previewBounds); } } } #region IDisposable Support private bool disposedValue = false; protected override void Dispose(bool disposing) { if (!disposedValue) { if (disposing) { mapPanel.MouseDown -= MapPanel_MouseDown; mapPanel.MouseUp -= MapPanel_MouseUp; mapPanel.MouseMove -= MapPanel_MouseMove; (mapPanel as Control).KeyDown -= TemplateTool_KeyDown; (mapPanel as Control).KeyUp -= TemplateTool_KeyUp; templateTypeListView.SelectedIndexChanged -= TemplateTypeListView_SelectedIndexChanged; templateTypeMapPanel.MouseDown -= TemplateTypeMapPanel_MouseDown; templateTypeMapPanel.PostRender -= TemplateTypeMapPanel_PostRender; navigationWidget.MouseCellChanged -= MouseoverWidget_MouseCellChanged; url.Undone -= Url_Undone; url.Redone -= Url_Redone; } disposedValue = true; } base.Dispose(disposing); } #endregion } }