mirror of
https://github.com/spiffcode/hostile-takeover.git
synced 2025-12-16 12:08:36 +00:00
1033 lines
32 KiB
C#
1033 lines
32 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.ComponentModel;
|
|
using System.Drawing;
|
|
using System.Drawing.Imaging;
|
|
using System.Drawing.Drawing2D;
|
|
using System.Data;
|
|
using System.Windows.Forms;
|
|
using System.Runtime.InteropServices;
|
|
using System.Diagnostics;
|
|
|
|
namespace m
|
|
{
|
|
public class LevelView : System.Windows.Forms.UserControl
|
|
{
|
|
System.ComponentModel.Container components = null;
|
|
Bitmap m_bm;
|
|
LevelDoc m_lvld;
|
|
Rectangle m_rcDragStart = Rectangle.Empty;
|
|
System.Windows.Forms.ContextMenu m_contextMenu;
|
|
System.Windows.Forms.MenuItem menuItemRemove;
|
|
IMapItem m_miContextMenu;
|
|
Point m_ptDragSelectAnchor = new Point(0, 0);
|
|
Rectangle m_rcDragSelect;
|
|
bool m_fDragSelect;
|
|
bool m_fJustSelected;
|
|
float m_xRatioView = 0.0f;
|
|
float m_yRatioView = 0.0f;
|
|
float m_flScale = 1.0f;
|
|
TemplateDoc m_tmpd = null;
|
|
LayerFlags m_lyrf = LayerFlags.Default;
|
|
Bitmap m_bmDrag = null;
|
|
private System.Windows.Forms.MenuItem menuItemSelectSame;
|
|
Rectangle m_rcDragBoundsLast = Rectangle.Empty;
|
|
Point m_ptMouseDown;
|
|
IMapItem m_miCapturedMouse = null;
|
|
|
|
public event EventHandler ScaleChanged;
|
|
|
|
public LevelView() {
|
|
m_lvld = null;
|
|
}
|
|
|
|
public void SetDocument(Document doc) {
|
|
// This call is required by the Windows.Forms Form Designer.
|
|
|
|
InitializeComponent();
|
|
|
|
// Add any initialization after the InitForm call
|
|
|
|
m_lvld = (LevelDoc)doc;
|
|
|
|
// Create the bitmap
|
|
|
|
AutoScroll = true;
|
|
VScroll = true;
|
|
HScroll = true;
|
|
CreateBitmap();
|
|
|
|
// Init view position
|
|
|
|
InitPosition();
|
|
|
|
// Draw it initially
|
|
|
|
m_lvld.Draw(m_bm, null, GetTileSize(), GetTemplateDoc(), m_lyrf);
|
|
|
|
// Need to know these events
|
|
|
|
m_lvld.ImageChanged += new LevelDoc.ImageChangedHandler(LevelDoc_ImageChanged);
|
|
m_lvld.ItemsRemoved += new LevelDoc.ItemsRemovedHandler(LevelDoc_ItemsRemoved);
|
|
|
|
// Check size
|
|
|
|
CheckSize();
|
|
}
|
|
|
|
public void SetScale(float flScale) {
|
|
if (flScale < 0.25f)
|
|
flScale = 0.25f;
|
|
if (flScale == m_flScale)
|
|
return;
|
|
m_flScale = flScale;
|
|
ResetClientSize();
|
|
DrawLevelImage(null);
|
|
if (ScaleChanged != null)
|
|
ScaleChanged(this, null);
|
|
}
|
|
|
|
public float GetScale() {
|
|
return m_flScale;
|
|
}
|
|
|
|
Point WorldToView(Point ptWorld) {
|
|
int xView = (int)((float)ptWorld.X * m_flScale);
|
|
int yView = (int)((float)ptWorld.Y * m_flScale);
|
|
return new Point(xView, yView);
|
|
}
|
|
|
|
Point ViewToWorld(Point ptView) {
|
|
int xWorld = (int)((float)ptView.X / m_flScale);
|
|
int yWorld = (int)((float)ptView.Y / m_flScale);
|
|
return new Point(xWorld, yWorld);
|
|
}
|
|
|
|
Size WorldToViewSize(Size sizWorld) {
|
|
Point ptWorld = new Point(sizWorld.Width, sizWorld.Height);
|
|
Point ptView = WorldToView(ptWorld);
|
|
return new Size(ptView.X, ptView.Y);
|
|
}
|
|
|
|
Size ViewToWorldSize(Size sizView) {
|
|
Point ptView = new Point(sizView.Width, sizView.Height);
|
|
Point ptWorld = ViewToWorld(ptView);
|
|
return new Size(ptWorld.X, ptWorld.Y);
|
|
}
|
|
|
|
void InitPosition() {
|
|
int txWorld = m_lvld.Bounds.X - 1;
|
|
if (txWorld < 0)
|
|
txWorld = 0;
|
|
int tyWorld = m_lvld.Bounds.Y - 1;
|
|
if (tyWorld < 0)
|
|
tyWorld = 0;
|
|
Size sizTile = GetTileSize();
|
|
int xWorld = txWorld * sizTile.Width;
|
|
int yWorld = tyWorld * sizTile.Height;
|
|
m_xRatioView = (float)xWorld / (float)m_bm.Width;
|
|
m_yRatioView = (float)yWorld / (float)m_bm.Height;
|
|
AutoScrollPosition = WorldToView(new Point(xWorld, yWorld));
|
|
}
|
|
|
|
protected unsafe override void WndProc(ref Message m) {
|
|
Point ptWorldScrollPos;
|
|
switch (m.Msg) {
|
|
// #define WM_HSCROLL 0x0114
|
|
case 0x114:
|
|
base.WndProc(ref m);
|
|
ptWorldScrollPos = ViewToWorld(AutoScrollPosition);
|
|
m_xRatioView = (float)-ptWorldScrollPos.X / (float)m_bm.Width;
|
|
break;
|
|
|
|
// #define WM_VSCROLL 0x0115
|
|
// #define WM_MOUSEWHEEL 0x020A
|
|
case 0x20a:
|
|
case 0x115:
|
|
base.WndProc(ref m);
|
|
ptWorldScrollPos = ViewToWorld(AutoScrollPosition);
|
|
m_yRatioView = (float)-ptWorldScrollPos.Y / (float)m_bm.Height;
|
|
break;
|
|
|
|
default:
|
|
base.WndProc(ref m);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Clean up any resources being used.
|
|
/// </summary>
|
|
protected override void Dispose( bool disposing )
|
|
{
|
|
m_lvld.ImageChanged -= new LevelDoc.ImageChangedHandler(LevelDoc_ImageChanged);
|
|
m_lvld.ItemsRemoved -= new LevelDoc.ItemsRemovedHandler(LevelDoc_ItemsRemoved);
|
|
|
|
if( disposing )
|
|
{
|
|
if(components != null)
|
|
{
|
|
components.Dispose();
|
|
}
|
|
}
|
|
base.Dispose( disposing );
|
|
}
|
|
|
|
#region Component Designer generated code
|
|
/// <summary>
|
|
/// Required method for Designer support - do not modify
|
|
/// the contents of this method with the code editor.
|
|
/// </summary>
|
|
private void InitializeComponent()
|
|
{
|
|
this.m_contextMenu = new System.Windows.Forms.ContextMenu();
|
|
this.menuItemRemove = new System.Windows.Forms.MenuItem();
|
|
this.menuItemSelectSame = new System.Windows.Forms.MenuItem();
|
|
//
|
|
// m_contextMenu
|
|
//
|
|
this.m_contextMenu.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
|
|
this.menuItemRemove,
|
|
this.menuItemSelectSame});
|
|
//
|
|
// menuItemRemove
|
|
//
|
|
this.menuItemRemove.Index = 0;
|
|
this.menuItemRemove.Text = "Remove";
|
|
this.menuItemRemove.Click += new System.EventHandler(this.menuItemRemove_Click);
|
|
//
|
|
// menuItemSelectSame
|
|
//
|
|
this.menuItemSelectSame.Index = 1;
|
|
this.menuItemSelectSame.Text = "Select Same";
|
|
this.menuItemSelectSame.Click += new System.EventHandler(this.menuItemSelectSame_Click);
|
|
//
|
|
// LevelView
|
|
//
|
|
this.AllowDrop = true;
|
|
this.BackColor = System.Drawing.Color.Black;
|
|
this.Name = "LevelView";
|
|
this.Size = new System.Drawing.Size(368, 288);
|
|
this.Resize += new System.EventHandler(this.LevelView_Resize);
|
|
this.DragEnter += new System.Windows.Forms.DragEventHandler(this.LevelView_DragEnter);
|
|
this.MouseUp += new System.Windows.Forms.MouseEventHandler(this.LevelView_MouseUp);
|
|
this.Paint += new System.Windows.Forms.PaintEventHandler(this.LevelView_Paint);
|
|
this.DragLeave += new System.EventHandler(this.LevelView_DragLeave);
|
|
this.DragDrop += new System.Windows.Forms.DragEventHandler(this.LevelView_DragDrop);
|
|
this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.LevelView_KeyDown);
|
|
this.DragOver += new System.Windows.Forms.DragEventHandler(this.LevelView_DragOver);
|
|
this.MouseMove += new System.Windows.Forms.MouseEventHandler(this.LevelView_MouseMove);
|
|
this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.LevelView_MouseDown);
|
|
|
|
}
|
|
#endregion
|
|
|
|
public void SetTemplateDoc(TemplateDoc tmpd) {
|
|
if (tmpd == m_tmpd)
|
|
return;
|
|
|
|
if (m_tmpd != null)
|
|
m_tmpd.BackgroundChanged -= new TemplateDoc.BackgroundChangedHandler(TemplateDoc_BackgroundChangedHandler);
|
|
|
|
m_tmpd = tmpd;
|
|
|
|
if (m_tmpd != null)
|
|
m_tmpd.BackgroundChanged += new TemplateDoc.BackgroundChangedHandler(TemplateDoc_BackgroundChangedHandler);
|
|
|
|
UpdateImage();
|
|
}
|
|
|
|
void TemplateDoc_BackgroundChangedHandler(TemplateDoc tmpd) {
|
|
UpdateImage();
|
|
}
|
|
|
|
public void SetLayerFlags(LayerFlags lyrf) {
|
|
// If already set to this, return
|
|
|
|
if (lyrf == m_lyrf)
|
|
return;
|
|
|
|
// Remove the appropriate items from the current selection
|
|
|
|
ArrayList alsmiSelected = m_lvld.Selection;
|
|
|
|
for (int imi = 0; imi < alsmiSelected.Count; ) {
|
|
IMapItem mi = (IMapItem)alsmiSelected[imi];
|
|
if (mi is Tile) {
|
|
if ((lyrf & LayerFlags.Templates) == 0) {
|
|
alsmiSelected.RemoveAt(imi);
|
|
continue;
|
|
}
|
|
} else if (mi is Area) {
|
|
if ((lyrf & LayerFlags.Areas) == 0) {
|
|
alsmiSelected.RemoveAt(imi);
|
|
continue;
|
|
}
|
|
} else {
|
|
if ((lyrf & LayerFlags.Gobs) == 0) {
|
|
alsmiSelected.RemoveAt(imi);
|
|
continue;
|
|
}
|
|
}
|
|
imi++;
|
|
}
|
|
m_lvld.Selection = alsmiSelected;
|
|
|
|
// Set new flags and redraw
|
|
|
|
m_lyrf = lyrf;
|
|
Redraw();
|
|
}
|
|
|
|
public LayerFlags GetLayerFlags() {
|
|
return m_lyrf;
|
|
}
|
|
|
|
TemplateDoc GetTemplateDoc() {
|
|
if (m_tmpd != null)
|
|
return m_tmpd;
|
|
return m_lvld.GetTemplateDoc();
|
|
}
|
|
|
|
Size GetTileSize() {
|
|
TemplateDoc tmpd = GetTemplateDoc();
|
|
if (tmpd == null)
|
|
return new Size(24, 24);
|
|
return tmpd.TileSize;
|
|
}
|
|
|
|
private void LevelView_Paint(object sender, System.Windows.Forms.PaintEventArgs e) {
|
|
if (m_lvld == null)
|
|
return;
|
|
DrawLevelImage(e.Graphics);
|
|
}
|
|
|
|
protected override void OnPaintBackground(System.Windows.Forms.PaintEventArgs e) {
|
|
if (this.DesignMode)
|
|
base.OnPaintBackground(e);
|
|
}
|
|
|
|
private void Redraw() {
|
|
m_lvld.Draw(m_bm, null, GetTileSize(), GetTemplateDoc(), m_lyrf);
|
|
using (Graphics gWin = CreateGraphics())
|
|
DrawLevelImage(gWin);
|
|
}
|
|
|
|
void DrawLevelImage(Graphics g) {
|
|
Graphics gT = g;
|
|
if (g == null)
|
|
gT = CreateGraphics();
|
|
Rectangle rcSrcWorld = new Rectangle(0, 0, m_bm.Width, m_bm.Height);
|
|
Rectangle rcDstView = new Rectangle(AutoScrollPosition, WorldToViewSize(m_bm.Size));
|
|
gT.InterpolationMode = InterpolationMode.NearestNeighbor;
|
|
gT.PixelOffsetMode = PixelOffsetMode.Half;
|
|
gT.DrawImage(m_bm, rcDstView, rcSrcWorld, GraphicsUnit.Pixel);
|
|
if (g == null)
|
|
gT.Dispose();
|
|
}
|
|
|
|
void CreateBitmap() {
|
|
Size sizTile = GetTileSize();
|
|
if (m_bm != null)
|
|
m_bm.Dispose();
|
|
m_bm = new Bitmap(m_lvld.Width * sizTile.Width, m_lvld.Height * sizTile.Height, PixelFormat.Format24bppRgb);
|
|
ResetClientSize();
|
|
}
|
|
|
|
void ResetClientSize() {
|
|
AutoScrollMinSize = WorldToViewSize(m_bm.Size);
|
|
|
|
// Force it to recalc where it thinks the client size should be based on frame decorations,
|
|
// and send a Resize event which'll cause us to recalc the client area
|
|
|
|
Size sizSav = Size;
|
|
Size sizT = new Size(sizSav.Width + 1, sizSav.Height + 1);
|
|
Size = sizT;
|
|
UpdateBounds();
|
|
Size = sizSav;
|
|
UpdateBounds();
|
|
}
|
|
|
|
private void LevelDoc_ImageChanged() {
|
|
UpdateImage();
|
|
}
|
|
|
|
void UpdateImage() {
|
|
Size sizTile = GetTileSize();
|
|
if (sizTile.Width * Width != m_bm.Width || sizTile.Height * Height != m_bm.Height) {
|
|
CreateBitmap();
|
|
}
|
|
Redraw();
|
|
}
|
|
|
|
private void LevelDoc_ItemsRemoved(IMapItem[] ami) {
|
|
ArrayList alsmiSelected = m_lvld.Selection;
|
|
foreach (IMapItem mi in ami) {
|
|
alsmiSelected.Remove(mi);
|
|
if (m_miContextMenu == mi)
|
|
m_miContextMenu = null;
|
|
if (m_miCapturedMouse == mi)
|
|
m_miCapturedMouse = null;
|
|
break;
|
|
}
|
|
m_lvld.Selection = alsmiSelected;
|
|
}
|
|
|
|
private void LevelView_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e) {
|
|
m_fJustSelected = false;
|
|
|
|
m_ptMouseDown = ViewToWorld(new Point(e.X - AutoScrollPosition.X, e.Y - AutoScrollPosition.Y));
|
|
|
|
// Select / clear items
|
|
IMapItem mi = m_lvld.HitTest(m_ptMouseDown.X, m_ptMouseDown.Y, GetTileSize(), GetTemplateDoc(), m_lyrf);
|
|
if (e.Button == MouseButtons.Left) {
|
|
if (mi == null) {
|
|
// Clear selection
|
|
m_lvld.Selection = new ArrayList();
|
|
Globals.PropertyGrid.SelectedObject = null;
|
|
m_ptDragSelectAnchor.X = m_ptMouseDown.X;
|
|
m_ptDragSelectAnchor.Y = m_ptMouseDown.Y;
|
|
m_rcDragSelect = new Rectangle(m_ptDragSelectAnchor, new Size(0, 0));
|
|
m_fDragSelect = true;
|
|
} else {
|
|
m_rcDragStart.X = m_ptMouseDown.X - 5;
|
|
m_rcDragStart.Y = m_ptMouseDown.Y - 5;
|
|
m_rcDragStart.Width = 10;
|
|
m_rcDragStart.Height = 10;
|
|
|
|
// Add to selection. Extend if control is down
|
|
ArrayList alsmiSelected = m_lvld.Selection;
|
|
if (!alsmiSelected.Contains(mi)) {
|
|
if ((Control.ModifierKeys & Keys.Control) != Keys.Control)
|
|
alsmiSelected.Clear();
|
|
alsmiSelected.Add(mi);
|
|
Globals.PropertyGrid.SelectedObjects = (IMapItem[])alsmiSelected.ToArray(typeof(IMapItem));
|
|
m_fJustSelected = true;
|
|
m_lvld.Selection = alsmiSelected;
|
|
}
|
|
|
|
if (mi.OnMouseDown(e, m_ptMouseDown, GetTileSize(), GetTemplateDoc()))
|
|
m_miCapturedMouse = mi;
|
|
}
|
|
Redraw();
|
|
} else if (e.Button == MouseButtons.Right) {
|
|
if (mi != null) {
|
|
// If this is not already selected, then clear selection and add this
|
|
if (!m_lvld.Selection.Contains(mi)) {
|
|
ArrayList als = new ArrayList();
|
|
als.Add(mi);
|
|
m_lvld.Selection = als;
|
|
Globals.PropertyGrid.SelectedObject = mi;
|
|
Redraw();
|
|
}
|
|
|
|
if (mi.OnMouseDown(e, m_ptMouseDown, GetTileSize(), GetTemplateDoc())) {
|
|
m_miCapturedMouse = mi;
|
|
return;
|
|
}
|
|
|
|
m_miContextMenu = mi;
|
|
m_contextMenu.Show(this, new Point(e.X, e.Y));
|
|
}
|
|
}
|
|
}
|
|
|
|
private Rectangle GetBoundingRect(IMapItem[] ami) {
|
|
Rectangle rc = new Rectangle();
|
|
TemplateDoc tmpd = GetTemplateDoc();
|
|
Size sizTile = GetTileSize();
|
|
foreach (IMapItem mi in ami) {
|
|
int x = (int)(mi.tx * sizTile.Width);
|
|
int y = (int)(mi.ty * sizTile.Height);
|
|
rc = UnionRect(rc, mi.GetBoundingRectAt(x, y, sizTile, tmpd));
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
private Rectangle GetSelectRect(int x, int y) {
|
|
int cx = x - m_ptDragSelectAnchor.X;
|
|
if (cx < 0)
|
|
cx = -cx;
|
|
int cy = y - m_ptDragSelectAnchor.Y;
|
|
if (cy < 0)
|
|
cy = -cy;
|
|
int xT = x;
|
|
if (m_ptDragSelectAnchor.X < x)
|
|
xT = m_ptDragSelectAnchor.X;
|
|
int yT = y;
|
|
if (m_ptDragSelectAnchor.Y < y)
|
|
yT = m_ptDragSelectAnchor.Y;
|
|
return new Rectangle(xT, yT, cx, cy);
|
|
}
|
|
|
|
private Rectangle UnionRect(Rectangle rc1, Rectangle rc2) {
|
|
if (rc1.IsEmpty)
|
|
return new Rectangle(rc2.Location, rc2.Size);
|
|
if (rc2.IsEmpty)
|
|
return new Rectangle(rc1.Location, rc1.Size);
|
|
int xLeft = rc1.Left < rc2.Left ? rc1.Left : rc2.Left;
|
|
int yTop = rc1.Top < rc2.Top ? rc1.Top : rc2.Top;
|
|
int xRight = rc1.Right > rc2.Right ? rc1.Right : rc2.Right;
|
|
int yBottom = rc1.Bottom > rc2.Bottom ? rc1.Bottom : rc2.Bottom;
|
|
return new Rectangle(xLeft, yTop, xRight - xLeft, yBottom - yTop);
|
|
}
|
|
|
|
private void LevelView_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e) {
|
|
Point ptMouse = ViewToWorld(new Point(e.X - AutoScrollPosition.X, e.Y - AutoScrollPosition.Y));
|
|
TemplateDoc tmpd = GetTemplateDoc();
|
|
Size sizTile = GetTileSize();
|
|
|
|
// Update status bar
|
|
|
|
int tx = ptMouse.X / sizTile.Width;
|
|
int ty = ptMouse.Y / sizTile.Height;
|
|
Globals.StatusBar.Text = String.Format("Coords: {0}, {1}", tx, ty);
|
|
|
|
// If we're dragging a selection, handle it here
|
|
|
|
if (m_fDragSelect) {
|
|
DragSelectExtend(e);
|
|
return;
|
|
}
|
|
|
|
// Send input to MapItem, see if it wants it
|
|
|
|
IMapItem mi = m_miCapturedMouse;
|
|
if (mi == null)
|
|
mi = m_lvld.HitTest(ptMouse.X, ptMouse.Y, sizTile, tmpd, m_lyrf);
|
|
if (mi != null) {
|
|
if (mi.OnMouseMove(e, ptMouse, sizTile, tmpd))
|
|
return;
|
|
}
|
|
|
|
// We're not extending a selection. Perhaps we're initiating a drag drop operation
|
|
// Check initiation conditions
|
|
|
|
if (m_lvld.Selection.Count == 0)
|
|
return;
|
|
if (MouseButtons != MouseButtons.Left)
|
|
return;
|
|
if (m_rcDragStart.Contains(ptMouse.X, ptMouse.Y))
|
|
return;
|
|
|
|
PerformDragDrop(e, m_ptMouseDown);
|
|
}
|
|
|
|
private void LevelView_MouseUp(object sender, System.Windows.Forms.MouseEventArgs e) {
|
|
Point ptMouse = ViewToWorld(new Point(e.X - AutoScrollPosition.X, e.Y - AutoScrollPosition.Y));
|
|
|
|
// If a MapItem has captured the mouse, give it a crack at the event
|
|
|
|
IMapItem miT = m_miCapturedMouse;
|
|
m_miCapturedMouse = null;
|
|
if (miT != null) {
|
|
if (miT.OnMouseUp(e, ptMouse, GetTileSize(), GetTemplateDoc()))
|
|
return;
|
|
}
|
|
|
|
if (e.Button != MouseButtons.Left)
|
|
return;
|
|
if (m_fDragSelect) {
|
|
m_fDragSelect = false;
|
|
Graphics gWin = CreateGraphics();
|
|
Rectangle rcSrcWorld = new Rectangle(m_rcDragSelect.Left, m_rcDragSelect.Top, m_rcDragSelect.Width, m_rcDragSelect.Height);
|
|
rcSrcWorld.Inflate(1, 1);
|
|
Rectangle rcDstView = new Rectangle(WorldToView(rcSrcWorld.Location), WorldToViewSize(rcSrcWorld.Size));
|
|
rcDstView.Offset(AutoScrollPosition);
|
|
gWin.InterpolationMode = InterpolationMode.NearestNeighbor;
|
|
gWin.PixelOffsetMode = PixelOffsetMode.Half;
|
|
gWin.DrawImage(m_bm, rcDstView, rcSrcWorld, GraphicsUnit.Pixel);
|
|
gWin.Dispose();
|
|
}
|
|
|
|
// Clear selected placement?
|
|
if (m_fJustSelected) {
|
|
m_fJustSelected = false;
|
|
return;
|
|
}
|
|
IMapItem mi = m_lvld.HitTest(ptMouse.X, ptMouse.Y, GetTileSize(), GetTemplateDoc(), m_lyrf);
|
|
if (mi == null)
|
|
return;
|
|
if ((Control.ModifierKeys & Keys.Control) != Keys.Control)
|
|
return;
|
|
ArrayList alsmiSelected = m_lvld.Selection;
|
|
if (!alsmiSelected.Contains(mi))
|
|
return;
|
|
alsmiSelected.Remove(mi);
|
|
m_lvld.Selection = alsmiSelected;
|
|
Globals.PropertyGrid.SelectedObject = null;
|
|
Redraw();
|
|
}
|
|
|
|
private void LevelView_DragOver(object sender, System.Windows.Forms.DragEventArgs e) {
|
|
// As the drag occurs, we show what is being dragged. This is done by drawing
|
|
// a properly aligned image on top of the view image as the drag occurs.
|
|
|
|
// Get the data. It's not what we want then don't allow
|
|
|
|
if (!e.Data.GetDataPresent(typeof(LevelData))) {
|
|
e.Effect = DragDropEffects.None;
|
|
return;
|
|
}
|
|
LevelData ldat = (LevelData)e.Data.GetData(typeof(LevelData));
|
|
|
|
// Figure out where we want to place these map items
|
|
|
|
PointF ptOrigin;
|
|
Point ptClient = PointToClient(new Point(e.X, e.Y));
|
|
Point ptMouse = ViewToWorld(new Point(ptClient.X - AutoScrollPosition.X, ptClient.Y - AutoScrollPosition.Y));
|
|
PointF[] aptPlace = GetPlacementPoints2(ptMouse, ldat, out ptOrigin);
|
|
|
|
// Calc the bounding rect of the map items
|
|
|
|
Size sizTile = GetTileSize();
|
|
Rectangle rcDragBoundsNew = GetBoundingRect(ldat.ami);
|
|
rcDragBoundsNew.Offset((int)(ptOrigin.X * sizTile.Width), (int)(ptOrigin.Y * sizTile.Height));
|
|
|
|
// If have an old drag image to erase, the drawing bounds needs to include it too so it
|
|
// gets erased during this update
|
|
|
|
Rectangle rcUnion = UnionRect(rcDragBoundsNew, m_rcDragBoundsLast);
|
|
|
|
// See if our drag compose buffer is suitably sized. If not, recreate it
|
|
|
|
if (m_bmDrag == null || m_bmDrag.Width < rcUnion.Width || m_bmDrag.Height < rcUnion.Height) {
|
|
if (m_bmDrag != null)
|
|
m_bmDrag.Dispose();
|
|
m_bmDrag = new Bitmap(rcUnion.Width + rcUnion.Width / 2, rcUnion.Height + rcUnion.Height / 2, PixelFormat.Format24bppRgb);
|
|
}
|
|
|
|
// Copy this rectangle from the map bitmap into our drag compose buffer
|
|
|
|
Graphics gMem = Graphics.FromImage(m_bmDrag);
|
|
gMem.DrawImage(m_bm, 0, 0, rcUnion, GraphicsUnit.Pixel);
|
|
|
|
// Now draw in map items
|
|
|
|
TemplateDoc tmpd = GetTemplateDoc();
|
|
for (LayerType layer = LayerType.Start; layer < LayerType.End; layer++) {
|
|
for (int imi = 0; imi < ldat.ami.Length; imi++) {
|
|
int x = (int)(aptPlace[imi].X * sizTile.Width);
|
|
int y = (int)(aptPlace[imi].Y * sizTile.Height);
|
|
ldat.ami[imi].Draw(gMem, x - rcUnion.X, y - rcUnion.Y, sizTile, tmpd, layer, true);
|
|
}
|
|
}
|
|
|
|
// Put it on screen
|
|
|
|
Graphics gWin = CreateGraphics();
|
|
Rectangle rcSrcWorld = new Rectangle(0, 0, rcUnion.Width, rcUnion.Height);
|
|
Rectangle rcDstView = new Rectangle(WorldToView(rcUnion.Location), WorldToViewSize(rcUnion.Size));
|
|
rcDstView.Offset(AutoScrollPosition.X, AutoScrollPosition.Y);
|
|
gWin.InterpolationMode = InterpolationMode.NearestNeighbor;
|
|
gWin.PixelOffsetMode = PixelOffsetMode.Half;
|
|
gWin.DrawImage(m_bmDrag, rcDstView, rcSrcWorld, GraphicsUnit.Pixel);
|
|
gMem.Dispose();
|
|
gWin.Dispose();
|
|
|
|
// Remember old bounds
|
|
|
|
m_rcDragBoundsLast = rcDragBoundsNew;
|
|
}
|
|
|
|
private void LevelView_DragEnter(object sender, System.Windows.Forms.DragEventArgs e) {
|
|
m_rcDragBoundsLast = Rectangle.Empty;
|
|
if (!e.Data.GetDataPresent(typeof(LevelData))) {
|
|
e.Effect = DragDropEffects.None;
|
|
return;
|
|
}
|
|
e.Effect = DragDropEffects.Copy | DragDropEffects.Move;
|
|
}
|
|
|
|
private void LevelView_DragLeave(object sender, System.EventArgs e) {
|
|
// Update the on-screen drag image so that it gets erased properly
|
|
// Copy appropriate part of the backing image
|
|
|
|
Graphics gMem = Graphics.FromImage(m_bmDrag);
|
|
gMem.DrawImage(m_bm, 0, 0, m_rcDragBoundsLast, GraphicsUnit.Pixel);
|
|
|
|
// Now draw in on screen in the appropriate spot
|
|
|
|
Graphics gWin = CreateGraphics();
|
|
Rectangle rcSrcWorld = new Rectangle(0, 0, m_rcDragBoundsLast.Width, m_rcDragBoundsLast.Height);
|
|
Rectangle rcDstView = new Rectangle(WorldToView(m_rcDragBoundsLast.Location), WorldToViewSize(m_rcDragBoundsLast.Size));
|
|
rcDstView.Offset(AutoScrollPosition);
|
|
gWin.InterpolationMode = InterpolationMode.NearestNeighbor;
|
|
gWin.PixelOffsetMode = PixelOffsetMode.Half;
|
|
gWin.DrawImage(m_bmDrag, rcDstView, rcSrcWorld, GraphicsUnit.Pixel);
|
|
gMem.Dispose();
|
|
gWin.Dispose();
|
|
|
|
// Not being used any more
|
|
|
|
m_bmDrag.Dispose();
|
|
m_bmDrag = null;
|
|
m_rcDragBoundsLast = Rectangle.Empty;
|
|
}
|
|
|
|
private void LevelView_DragDrop(object sender, System.Windows.Forms.DragEventArgs e) {
|
|
// Get the data. It's not what we want then don't allow
|
|
|
|
if (!e.Data.GetDataPresent(typeof(LevelData))) {
|
|
e.Effect = DragDropEffects.None;
|
|
return;
|
|
}
|
|
LevelData ldat = (LevelData)e.Data.GetData(typeof(LevelData));
|
|
|
|
// Place map items
|
|
|
|
Point ptClient = PointToClient(new Point(e.X, e.Y));
|
|
Point ptMouse = ViewToWorld(new Point(ptClient.X - AutoScrollPosition.X, ptClient.Y - AutoScrollPosition.Y));
|
|
PlaceMapItems(ptMouse, ldat);
|
|
e.Effect = e.AllowedEffect;
|
|
}
|
|
|
|
void PlaceMapItems(Point ptMouse, LevelData ldat) {
|
|
// Figure out where we want to place these map items
|
|
|
|
PointF ptOrigin;
|
|
PointF[] aptPlace = GetPlacementPoints2(ptMouse, ldat, out ptOrigin);
|
|
|
|
// Set their positions
|
|
|
|
IMapItem[] ami = new IMapItem[ldat.ami.Length];
|
|
for (int imi = 0; imi < ldat.ami.Length; imi++) {
|
|
ami[imi] = (IMapItem)ldat.ami[imi].Clone();
|
|
ami[imi].tx = aptPlace[imi].X;
|
|
ami[imi].ty = aptPlace[imi].Y;
|
|
}
|
|
|
|
// Add them to the level, make them selected
|
|
|
|
m_lvld.AddMapItems(ami);
|
|
ArrayList alsmiSelected = new ArrayList();
|
|
alsmiSelected.AddRange(ami);
|
|
m_lvld.Selection = alsmiSelected;
|
|
Globals.PropertyGrid.SelectedObjects = (Object[])alsmiSelected.ToArray(typeof(Object));
|
|
}
|
|
|
|
PointF[] GetPlacementPoints2(Point ptMouse, LevelData ldat, out PointF ptOrigin) {
|
|
Size sizTile = GetTileSize();
|
|
double txOrigin = (ptMouse.X / (double)sizTile.Width) - ldat.txMouse;
|
|
if (txOrigin < 0)
|
|
txOrigin = 0;
|
|
double tyOrigin = (ptMouse.Y / (double)sizTile.Height) - ldat.tyMouse;
|
|
if (tyOrigin < 0)
|
|
tyOrigin = 0;
|
|
|
|
// Keep the new positions on the grid
|
|
|
|
txOrigin = Math.Floor((txOrigin + ldat.Grid.Width / 2) / ldat.Grid.Width) * ldat.Grid.Width;
|
|
tyOrigin = Math.Floor((tyOrigin + ldat.Grid.Height / 2) / ldat.Grid.Height) * ldat.Grid.Height;
|
|
|
|
PointF[] aptPlace = new PointF[ldat.ami.Length];
|
|
for (int imi = 0; imi < ldat.ami.Length; imi++) {
|
|
double tx = txOrigin + ldat.ami[imi].tx;
|
|
if (tx > m_lvld.Width - 1)
|
|
tx = m_lvld.Width - 1;
|
|
double ty = tyOrigin + ldat.ami[imi].ty;
|
|
if (ty > m_lvld.Height - 1)
|
|
ty = m_lvld.Height - 1;
|
|
aptPlace[imi] = new PointF((float)tx, (float)ty);
|
|
}
|
|
|
|
ptOrigin = new PointF((float)txOrigin, (float)tyOrigin);
|
|
return aptPlace;
|
|
}
|
|
|
|
LevelData PrepareLevelData(int x, int y, Size sizTile, IMapItem[] ami) {
|
|
// Figure out relative spacing
|
|
|
|
double txMin = double.MaxValue;
|
|
double tyMin = double.MaxValue;
|
|
foreach (IMapItem mi in ami) {
|
|
if (mi.tx < txMin)
|
|
txMin = mi.tx;
|
|
if (mi.ty < tyMin)
|
|
tyMin = mi.ty;
|
|
}
|
|
|
|
LevelData ldat = new LevelData();
|
|
ldat.Grid.Width = 0.0000001f;
|
|
ldat.Grid.Height = 0.0000001f;
|
|
|
|
for (int imi = 0; imi < ami.Length; imi++) {
|
|
IMapItem mi = (IMapItem)ami[imi];
|
|
|
|
// Keep track of the maximum gridding required by the various items
|
|
// NOTE: all the grids must be evenly divisible into the largest grid
|
|
// or final placement of the items will have the indivisible ones
|
|
// realigning themselves.
|
|
|
|
ldat.Grid.Width = Math.Max(ldat.Grid.Width, mi.Grid.Width);
|
|
ldat.Grid.Height = Math.Max(ldat.Grid.Height, mi.Grid.Height);
|
|
}
|
|
|
|
// Offset tx/yMin so the clones will be grid-aligned
|
|
|
|
txMin -= Math.IEEERemainder(txMin, ldat.Grid.Width);
|
|
tyMin -= Math.IEEERemainder(tyMin, ldat.Grid.Height);
|
|
|
|
// Now clone the map items and readjust tile coordinates
|
|
// for origin of 0,0 (or as close as we can get and still be grid-aligned)
|
|
|
|
ldat.ami = new IMapItem[ami.Length];
|
|
for (int imi = 0; imi < ami.Length; imi++) {
|
|
IMapItem mi = (IMapItem)ami[imi].Clone();
|
|
ldat.ami[imi] = mi;
|
|
mi.tx -= txMin;
|
|
mi.ty -= tyMin;
|
|
}
|
|
|
|
// Figure out mouse position relative to this origin
|
|
|
|
int xOrigin = (int)(txMin * sizTile.Width);
|
|
int yOrigin = (int)(tyMin * sizTile.Height);
|
|
ldat.txMouse = (double)(x - xOrigin) / (double)sizTile.Width;
|
|
ldat.tyMouse = (double)(y - yOrigin) / (double)sizTile.Height;
|
|
|
|
// All done
|
|
|
|
return ldat;
|
|
}
|
|
|
|
void PerformDragDrop(MouseEventArgs e, Point ptMouse) {
|
|
// Remember what mi are selected. We may be deleting this if this is a move operation
|
|
|
|
ArrayList alsmiSelected = m_lvld.Selection;
|
|
IMapItem[] amiMove = (IMapItem[])alsmiSelected.ToArray(typeof(IMapItem));
|
|
|
|
// Prepare a data object for drag drop
|
|
|
|
LevelData ldat = PrepareLevelData(ptMouse.X, ptMouse.Y, GetTileSize(), (IMapItem[])alsmiSelected.ToArray(typeof(IMapItem)));
|
|
|
|
// Normal operation is a move unless control key is press in which case it is
|
|
// a copy
|
|
|
|
DragDropEffects eff = DragDropEffects.Move;
|
|
if ((Control.ModifierKeys & Keys.Control) == Keys.Control)
|
|
eff = DragDropEffects.Copy;
|
|
|
|
// Perform the drag drop. If it was cancelled, nothing to do.
|
|
|
|
DragDropEffects effActual = DoDragDrop(ldat, eff);
|
|
if (effActual == DragDropEffects.None)
|
|
return;
|
|
|
|
// If a move actually did occur, then remove the originals
|
|
|
|
if ((effActual & DragDropEffects.Move) != 0) {
|
|
m_lvld.RemoveMapItems(amiMove);
|
|
}
|
|
}
|
|
|
|
void DragSelectExtend(MouseEventArgs e) {
|
|
// Select all the mi inside
|
|
|
|
Point ptMouse = ViewToWorld(new Point(e.X - AutoScrollPosition.X, e.Y - AutoScrollPosition.Y));
|
|
Size sizTile = GetTileSize();
|
|
TemplateDoc tmpd = GetTemplateDoc();
|
|
Rectangle rcDragSelectNew = GetSelectRect(ptMouse.X, ptMouse.Y);
|
|
ArrayList alsmiSelected = m_lvld.HitTest(rcDragSelectNew, GetTileSize(), GetTemplateDoc(), m_lyrf);
|
|
Rectangle rcSelect = new Rectangle();
|
|
ArrayList alsmiSelectedOld = m_lvld.Selection;
|
|
if (!alsmiSelectedOld.Equals(alsmiSelected)) {
|
|
rcSelect = GetBoundingRect((IMapItem[])alsmiSelectedOld.ToArray(typeof(IMapItem)));
|
|
m_lvld.Selection = alsmiSelected;
|
|
rcSelect = UnionRect(rcSelect, GetBoundingRect((IMapItem[])alsmiSelected.ToArray(typeof(IMapItem))));
|
|
m_lvld.Draw(m_bm, null, sizTile, tmpd, m_lyrf);
|
|
Globals.PropertyGrid.SelectedObjects = (Object[])alsmiSelected.ToArray(typeof(Object));
|
|
}
|
|
|
|
// Fill a buffer from background
|
|
Rectangle rcDragUnion = UnionRect(rcSelect, UnionRect(rcDragSelectNew, m_rcDragSelect));
|
|
|
|
// Expand by 1 so that the boundary is definitely inside. Problems with portions of
|
|
// the drag boundary not erasing when scaled due to rounding errors
|
|
|
|
rcDragUnion.Inflate(1, 1);
|
|
#if false
|
|
if (rcDragUnion.IsEmpty)
|
|
return;
|
|
#else
|
|
if (rcDragUnion.Width == 0 || rcDragUnion.Height == 0)
|
|
return;
|
|
#endif
|
|
Bitmap bm = new Bitmap(rcDragUnion.Width, rcDragUnion.Height);
|
|
Graphics gMem = Graphics.FromImage(bm);
|
|
gMem.DrawImage(m_bm, 0, 0, rcDragUnion, GraphicsUnit.Pixel);
|
|
|
|
// Draw in the drag selection
|
|
Pen pen = new Pen(new SolidBrush(Color.Red));
|
|
gMem.DrawRectangle(pen, rcDragSelectNew.X - rcDragUnion.X, rcDragSelectNew.Y - rcDragUnion.Y, rcDragSelectNew.Width - 1, rcDragSelectNew.Height - 1);
|
|
gMem.Dispose();
|
|
|
|
// Drag onto the screen
|
|
Graphics gWinT = CreateGraphics();
|
|
Rectangle rcSrcWorld = new Rectangle(0, 0, bm.Width, bm.Height);
|
|
Point ptViewDst = WorldToView(rcDragUnion.Location);
|
|
ptViewDst.Offset(AutoScrollPosition.X, AutoScrollPosition.Y);
|
|
Rectangle rcDstView = new Rectangle(ptViewDst, WorldToViewSize(rcDragUnion.Size));
|
|
gWinT.InterpolationMode = InterpolationMode.NearestNeighbor;
|
|
gWinT.PixelOffsetMode = PixelOffsetMode.Half;
|
|
gWinT.DrawImage(bm, rcDstView, rcSrcWorld, GraphicsUnit.Pixel);
|
|
gWinT.Dispose();
|
|
bm.Dispose();
|
|
m_rcDragSelect = rcDragSelectNew;
|
|
}
|
|
|
|
private void LevelView_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e) {
|
|
switch (e.KeyCode) {
|
|
case Keys.Delete:
|
|
Delete();
|
|
break;
|
|
|
|
case Keys.C:
|
|
if ((Control.ModifierKeys & Keys.Control) != 0)
|
|
Copy();
|
|
break;
|
|
|
|
case Keys.X:
|
|
if ((Control.ModifierKeys & Keys.Control) != 0)
|
|
Cut();
|
|
break;
|
|
|
|
case Keys.V:
|
|
if ((Control.ModifierKeys & Keys.Control) != 0)
|
|
Paste();
|
|
break;
|
|
|
|
case Keys.OemMinus:
|
|
if ((Control.ModifierKeys & Keys.Control) != 0) {
|
|
SetScale(1.0f);
|
|
} else {
|
|
SetScale(m_flScale - 0.25f);
|
|
}
|
|
break;
|
|
|
|
case Keys.Oemplus:
|
|
if ((Control.ModifierKeys & Keys.Control) != 0) {
|
|
SetScale(1.0f);
|
|
} else {
|
|
SetScale(m_flScale + 0.25f);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
public void Delete() {
|
|
RemoveSelectedItems();
|
|
}
|
|
|
|
public void Copy() {
|
|
ArrayList alsmiSelected = m_lvld.Selection;
|
|
if (alsmiSelected.Count != 0) {
|
|
IMapItem[] ami = (IMapItem[])alsmiSelected.ToArray(typeof(IMapItem));
|
|
Rectangle rc = GetBoundingRect(ami);
|
|
LevelData ldat = PrepareLevelData(rc.X + rc.Width / 2, rc.Y + rc.Height / 2, GetTileSize(), ami);
|
|
//Clipboard.SetDataObject(ldat); doesn't work for some reason
|
|
Clipboard.SetDataObject(ldat.ami);
|
|
}
|
|
}
|
|
|
|
public void Cut() {
|
|
if (m_lvld.Selection.Count != 0) {
|
|
Copy();
|
|
RemoveSelectedItems();
|
|
}
|
|
}
|
|
|
|
public void Paste() {
|
|
// IMapItems present?
|
|
|
|
IDataObject data = Clipboard.GetDataObject();
|
|
if (!data.GetDataPresent(typeof(IMapItem[])))
|
|
return;
|
|
|
|
// For some reason the clipboard data object won't return a LevelData
|
|
|
|
LevelData ldat = new LevelData();
|
|
ldat.ami = (IMapItem[])data.GetData(typeof(IMapItem[]));
|
|
Rectangle rc = GetBoundingRect(ldat.ami);
|
|
Size sizTile = GetTileSize();
|
|
ldat.txMouse = (double)rc.Width / (double)sizTile.Width;
|
|
ldat.tyMouse = (double)rc.Height / (double)sizTile.Height;
|
|
|
|
ldat.Grid.Width = 0.0000001f;
|
|
ldat.Grid.Height = 0.0000001f;
|
|
foreach (IMapItem mi in ldat.ami) {
|
|
ldat.Grid.Width = Math.Max(mi.Grid.Width, ldat.Grid.Width);
|
|
ldat.Grid.Height = Math.Max(mi.Grid.Height, ldat.Grid.Height);
|
|
}
|
|
|
|
// Place map items
|
|
|
|
Point ptViewCenter = new Point(ClientSize.Width / 2, ClientSize.Height / 2);
|
|
Point ptMouse = ViewToWorld(new Point(ptViewCenter.X - AutoScrollPosition.X, ptViewCenter.Y - AutoScrollPosition.Y));
|
|
PlaceMapItems(ptMouse, ldat);
|
|
}
|
|
|
|
public void RemoveSelectedItems() {
|
|
IMapItem[] ami = (IMapItem[])m_lvld.Selection.ToArray(typeof(IMapItem));
|
|
m_lvld.Selection = new ArrayList();
|
|
Globals.PropertyGrid.SelectedObject = null;
|
|
m_lvld.RemoveMapItems(ami);
|
|
}
|
|
|
|
public void CheckSize() {
|
|
SuspendLayout();
|
|
Size sizBitmapView = WorldToViewSize(m_bm.Size);
|
|
if (ClientSize.Width > sizBitmapView.Width)
|
|
Width += sizBitmapView.Width - ClientSize.Width;
|
|
if (ClientSize.Height > sizBitmapView.Height)
|
|
Height += sizBitmapView.Height - ClientSize.Height;
|
|
ResumeLayout(false);
|
|
}
|
|
|
|
private void LevelView_Resize(object sender, System.EventArgs e) {
|
|
// Resize client area around m_bm
|
|
|
|
CheckSize();
|
|
|
|
// Reposition scrollbars to last mid point
|
|
|
|
Size sizBitmapView = WorldToViewSize(m_bm.Size);
|
|
int xViewMap = (int)(m_xRatioView * (float)sizBitmapView.Width);
|
|
if (ClientSize.Width >= sizBitmapView.Width)
|
|
xViewMap = 0;
|
|
int yViewMap = (int)(m_yRatioView * (float)sizBitmapView.Height);
|
|
if (ClientSize.Height >= sizBitmapView.Height)
|
|
yViewMap = 0;
|
|
AutoScrollPosition = new Point(xViewMap, yViewMap);
|
|
}
|
|
|
|
private void menuItemSelectSame_Click(object sender, System.EventArgs e) {
|
|
ArrayList alsmiSelected = new ArrayList();
|
|
foreach (IMapItem mi in m_lvld.MapItems) {
|
|
if (mi.GetType() != m_miContextMenu.GetType())
|
|
continue;
|
|
if (m_miContextMenu is Tile) {
|
|
if (mi is Tile) {
|
|
if (((Tile)mi).Name != ((Tile)m_miContextMenu).Name)
|
|
continue;
|
|
}
|
|
}
|
|
alsmiSelected.Add(mi);
|
|
}
|
|
m_lvld.Selection = alsmiSelected;
|
|
Redraw();
|
|
}
|
|
|
|
private void menuItemRemove_Click(object sender, System.EventArgs e) {
|
|
RemoveSelectedItems();
|
|
}
|
|
}
|
|
|
|
public class LevelData {
|
|
public double txMouse;
|
|
public double tyMouse;
|
|
public IMapItem[] ami;
|
|
public SizeF Grid;
|
|
}
|
|
}
|