view FrmArmyTree.cs @ 44:1486ccd744dc

Fixes #135: Closing army crashes WinForms * Simple solution: Cache the open windows as an array then close them from the array to avoid iterating over an collection that gets modified!
author IBBoard <dev@ibboard.co.uk>
date Wed, 09 Sep 2009 19:37:14 +0000
parents 740350673006
children 1c74b51abac1
line wrap: on
line source

// This file (FrmArmyTree.cs) is a part of the IBBoard.WarFoundry.GUI.WinForms project and is copyright 2009 IBBoard.
//
// The file and the library/program it is in are licensed and distributed, without warranty, under the GNU Affero GPL license, either version 3 of the License or (at your option) any later version. Please see COPYING for more information and the full license.

using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using IBBoard.Commands;
using IBBoard.WarFoundry.API;
using IBBoard.WarFoundry.API.Commands;
using IBBoard.Windows.Forms;
using IBBoard.WarFoundry.API.Objects;

namespace IBBoard.WarFoundry.GUI.WinForms
{
	/// <summary>
	/// Summary description for FrmArmyTree.
	/// </summary>
	public class FrmArmyTree : IBBoard.Windows.Forms.IBBForm
	{
		private System.Windows.Forms.TreeView treeView;
		/// <summary>
		/// Required designer variable.
		/// </summary>
		private System.ComponentModel.Container components = null;
		private Hashtable htNodes;
		private System.Windows.Forms.ContextMenu contextMenu;
		private System.Windows.Forms.MenuItem miDelete;
		private System.Windows.Forms.MenuItem miEdit;
		private Hashtable htUnitWindows;
		
		private ObjectAddDelegate UnitAddedMethod;
		private ObjectRemoveDelegate UnitRemovedMethod;
		private StringValChangedDelegate UnitNameChangedMethod, ArmyNameChangedMethod, TreeNameChangedMethod;
		private CommandStack commandStack;

		public FrmArmyTree(CommandStack cmdStack)
		{
			commandStack = cmdStack;
			InitializeComponent();
			UnitAddedMethod = new ObjectAddDelegate(AddUnit);
			UnitRemovedMethod = new ObjectRemoveDelegate(RemoveUnit);
			UnitNameChangedMethod = new StringValChangedDelegate(UpdateUnitName);
			ArmyNameChangedMethod = new StringValChangedDelegate(UpdateArmyName);
			TreeNameChangedMethod = new StringValChangedDelegate(FrmArmyTree_TreeNameChanged);
			htNodes = new Hashtable();
			htUnitWindows = new Hashtable();
			this.Name = "ArmyTree";			
			WarFoundryCore.ArmyChanged+= new ArmyChangedDelegate(FrmArmyTree_ArmyChanged);
		}

		public FrmArmyTree(Army army, CommandStack cmdStack) : this(cmdStack)
		{
			SetArmy(army);
		}

		/// <summary>
		/// Clean up any resources being used.
		/// </summary>
		protected override void Dispose( bool disposing )
		{
			if( disposing )
			{
				if(components != null)
				{
					components.Dispose();
				}
			}
			base.Dispose( disposing );
		}

		#region Windows Form 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.treeView = new System.Windows.Forms.TreeView();
			this.contextMenu = new System.Windows.Forms.ContextMenu();
			this.miEdit = new System.Windows.Forms.MenuItem();
			this.miDelete = new System.Windows.Forms.MenuItem();
			this.SuspendLayout();
			// 
			// treeView
			// 
			this.treeView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
				| System.Windows.Forms.AnchorStyles.Left) 
				| System.Windows.Forms.AnchorStyles.Right)));
			this.treeView.ContextMenu = this.contextMenu;
			this.treeView.FullRowSelect = true;
			this.treeView.ImageIndex = -1;
			this.treeView.Location = new System.Drawing.Point(0, 0);
			this.treeView.Name = "treeView";
			this.treeView.SelectedImageIndex = -1;
			this.treeView.Size = new System.Drawing.Size(240, 278);
			this.treeView.TabIndex = 0;
			this.treeView.MouseDown += new System.Windows.Forms.MouseEventHandler(this.treeView_MouseDown);
			this.treeView.DoubleClick += new System.EventHandler(this.treeView_DoubleClick);
			// 
			// contextMenu
			// 
			this.contextMenu.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
																						this.miEdit,
																						this.miDelete});
			this.contextMenu.Popup += new System.EventHandler(this.contextMenu_Popup);
			// 
			// miEdit
			// 
			this.miEdit.Index = 0;
			this.miEdit.Text = "&Edit unit";
			this.miEdit.Click += new System.EventHandler(this.miEdit_Click);
			// 
			// miDelete
			// 
			this.miDelete.Index = 1;
			this.miDelete.Text = "&Delete unit";
			this.miDelete.Click += new System.EventHandler(this.miDelete_Click);
			// 
			// FrmArmyTree
			// 
			this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
			this.ClientSize = new System.Drawing.Size(240, 277);
			this.ControlBox = false;
			this.Controls.Add(this.treeView);
			this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.SizableToolWindow;
			this.MaximizeBox = false;
			this.MinimizeBox = false;
			this.Name = "FrmArmyTree";
			this.Text = "FrmArmyTree";
			this.ResumeLayout(false);

		}
		#endregion

		private void ClearArmy()
		{
			htNodes.Clear();
			treeView.Nodes.Clear();
			ICollection unitFormsCollection = htUnitWindows.Values;
			FrmUnit[] unitForms = new FrmUnit[unitFormsCollection.Count];
			unitFormsCollection.CopyTo(unitForms, 0);

			foreach (FrmUnit window in unitForms)
			{
				window.Close();
			}

			htUnitWindows.Clear();
		}

		private void SetArmy(Army army)
		{
			ClearArmy();

			if (army != null)
			{
				ArmyCategory[] cats = army.Categories;
				TreeNode[] catNodes = new TreeNode[cats.Length];
				Unit[] units;

				for (int i = 0; i < cats.Length; i++)
				{
					units = army.GetUnits(cats[i]);
					cats[i].NameChanged += TreeNameChangedMethod;
					TreeNode[] unitNodes = new TreeNode[units.Length];
					TreeNode temp;

					for (int j = 0; j < units.Length; j++)
					{
						unitNodes[j] = createTreeNode(units[j]);
					}

					temp = new TreeNode(cats[i].Name, unitNodes);
					temp.Tag = cats[i];
					catNodes[i] = temp;
					htNodes[cats[i].ID] = temp;
				}

				TreeNode root = new TreeNode(army.Name, catNodes);
				root.Tag = army;
				treeView.Nodes.Add(root);
				root.ExpandAll();
			}
		}

		private TreeNode createTreeNode(Unit unit)
		{
			TreeNode temp = new TreeNode(unit.Name);
			temp.Tag = unit;
			unit.NameChanged+= UnitNameChangedMethod;
			htNodes[unit.ID] = temp;
			return temp;
		}

		/*private void FrmArmyTree_Move(object sender, System.EventArgs e)
		{
			if (ParentForm is FrmMain)
			{
				FrmMain main = (FrmMain)ParentForm;
				main.Invoke(new MethodInvoker(main.MdiChildMoved));
			}
		}*/

		public void FrmArmyTree_ArmyChanged(Army oldArmy, Army newArmy)
		{
			if (oldArmy != null)
			{
				oldArmy.UnitAdded -= UnitAddedMethod;
				oldArmy.UnitRemoved -= UnitRemovedMethod;
			}

			if (newArmy != null)
			{
				newArmy.UnitAdded += UnitAddedMethod;
				newArmy.UnitRemoved += UnitRemovedMethod;
			}

			SetArmy(newArmy);
		}
		
		private void AddUnit(object obj)
		{
			if (obj is Unit)
			{
				Unit unit = (Unit)obj;
				ArmyCategory cat = unit.Category;
				TreeNode parent = (TreeNode)htNodes[cat.ID];
				TreeNode unitNode = createTreeNode(unit);
				parent.Nodes.Add(unitNode);
				parent.Expand(); //make sure it's expanded
			}
		}

		private void RemoveUnit(object obj)
		{
			if (obj is Unit)
			{
				Unit unit = (Unit)obj;
				removeUnit(unit);
			}
		}

		private void removeUnit(Unit unit)
		{
			TreeNode unitNode = (TreeNode)htNodes[unit.ID];
			unit.NameChanged-= UnitNameChangedMethod;

			if (unitNode!=null)
			{
				unitNode.Remove();
				htNodes.Remove(unit.ID);
			}
		}


		private void contextMenu_Popup(object sender, System.EventArgs e)
		{
			TreeNode node = treeView.SelectedNode;
			if (node!=null && node.Tag is Unit)
			{
				foreach(MenuItem item in contextMenu.MenuItems)
				{
					item.Visible = true;
				}
			}
			else
			{
				foreach(MenuItem item in contextMenu.MenuItems)
				{
					item.Visible = false;
				}
			}
		}

		private void treeView_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
		{
			if (e.Button == MouseButtons.Right)
			{
				TreeNode tn = treeView.GetNodeAt(e.X, e.Y);

				if (tn!=null)
				{
					treeView.SelectedNode = tn;
				}
				else
				{
					treeView.SelectedNode = null;
				}
			}
		}

		private void miDelete_Click(object sender, System.EventArgs e)
		{
			TreeNode selected = treeView.SelectedNode;

			if (selected.Tag!=null && selected.Tag is Unit)
			{
				Unit unit = (Unit)selected.Tag;
				commandStack.Execute(new RemoveUnitCommand(unit));
			}
		}

		private void miEdit_Click(object sender, System.EventArgs e)
		{	
			editUnit();
		}

		private void treeView_DoubleClick(object sender, System.EventArgs e)
		{
			editUnit();			
		}

		private void editUnit()
		{
			TreeNode selected = treeView.SelectedNode;

			if (selected.Tag!=null && selected.Tag is Unit)
			{
				Unit unit = (Unit)selected.Tag;

				if (htUnitWindows.ContainsKey(unit.ID))
				{
					((FrmUnit)htUnitWindows[unit.ID]).Focus();
				}
				else
				{
					FrmUnit unitForm = new FrmUnit(unit, commandStack);
					htUnitWindows.Add(unit.ID, unitForm);
					unitForm.MdiParent = this.MdiParent;
					unitForm.Closing+=new CancelEventHandler(unitForm_Closing);
					unitForm.Show();
				}
			}
		}

		private void UpdateUnitName(WarFoundryObject obj, string oldValue, string newValue)
		{
			if (obj is Unit)
			{
				Unit unit = (Unit)obj;
				TreeNode node = (TreeNode)htNodes[unit.ID];
				
				if (node!=null)
				{
					node.Text = unit.Name;
				}
			}
		}

		private void UpdateArmyName(WarFoundryObject obj, string oldValue, string newValue)
		{
			if (obj is Army)
			{
				Army army = (Army)obj;
				TreeNode node = treeView.Nodes[0];
				
				if (node!=null)
				{
					node.Text = army.Name;
				}
			}
		}

		private void unitForm_Closing(object sender, CancelEventArgs e)
		{
			if (sender is FrmUnit)
			{
				FrmUnit unitForm = (FrmUnit)sender;
				htUnitWindows.Remove(unitForm.Unit.ID);
			}
		}

		private void FrmArmyTree_TreeNameChanged(WarFoundryObject obj, string oldValue, string newValue)
		{
			TreeNode node = (TreeNode)htNodes[obj.ID];

			if (node!=null)
			{
				node.Text = obj.Name;
			}
		}
	}
}