view FrmMainWindow.cs @ 11:4e5e382fbd2e

Re #10 (Code readability) and Re #86 (Complete GTK# GUI): * Update UnitDisplayWidget to use new way of getting unit stats
author IBBoard <dev@ibboard.co.uk>
date Wed, 27 May 2009 19:44:40 +0000
parents c687bbe901f8
children 685532d43a96
line wrap: on
line source

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

using System;
using System.IO;
using System.Collections.Generic;
using System.Configuration;
using Gtk;
using IBBoard;
using IBBoard.Commands;
using IBBoard.IO;
using IBBoard.Lang;
using IBBoard.Logging;
using IBBoard.CustomMath;
using IBBoard.Log4Net;
using IBBoard.WarFoundry.API;
using IBBoard.WarFoundry.API.Factories;
using IBBoard.WarFoundry.API.Factories.Xml;
using IBBoard.WarFoundry.API.Objects;
using IBBoard.WarFoundry.API.Commands;
using IBBoard.WarFoundry.API.Savers;
using IBBoard.WarFoundry.API.Requirements;
using IBBoard.WarFoundry.GTK.Widgets;
using IBBoard.WarFoundry.Plugin.Rollcall;
using IBBoard.Xml;
using log4net;

namespace IBBoard.WarFoundry.GTK
{	
	public partial class FrmMainWindow: Gtk.Window
	{
		private static readonly string AppTitle = "WarFoundry";
		private const int CATEGORY_BUTTON_SEPARATOR_INDEX = 6;

		private Preferences preferences;
		private ILog logger = LogManager.GetLogger(typeof(FrmMainWindow));

		private CommandStack commandStack;
		private Dictionary<ToolButton, Category> categoryMap = new Dictionary<ToolButton, Category>();
		private Dictionary<IBBoard.WarFoundry.API.Objects.Unit, UnitDisplayWidget> unitToWidgetMap = new Dictionary<IBBoard.WarFoundry.API.Objects.Unit,UnitDisplayWidget>();

		private ObjectAddDelegate UnitAddedMethod;
		private ObjectRemoveDelegate UnitRemovedMethod;
		private DoubleValChangedDelegate PointsValueChangedMethod;
		private FailedUnitRequirementDelegate FailedUnitRequirementMethod;
		private StringValChangedDelegate UnitNameChangedMethod;
		
		private GameSystem system;
		private string loadedArmyPath;
		
		private MenuToolButton undoMenuButton, redoMenuButton;
		
		public static void Main (string[] args)
		{			
			try
			{
				Application.Init();
				FrmMainWindow win = new FrmMainWindow(args);
				win.Show();
				Application.Run();
				LogManager.GetLogger(typeof(FrmMainWindow)).Debug("Application ended");
			}
			catch(Exception ex)
			{
				LogManager.GetLogger(typeof(FrmMainWindow)).Fatal("("+ex.GetType().Name+") "+ex.Message + Environment.NewLine + ex.StackTrace);
			}
		}
		
		public FrmMainWindow() : this(new string[0])
		{
			//Do nothing extra
		}
			
		public FrmMainWindow (string[] args): base (Gtk.WindowType.Toplevel)
		{
			logger.Info("Opening FrmMainWindow");
			LogNotifierHandler.RegisterNotifierHandler();
			Build ();
			//Replace the undo/redo buttons with menu versions, which Monodevelop's GUI editor doesn't currently support
			redoMenuButton = new MenuToolButton("gtk-redo");
			redoMenuButton.Label = "Redo";
			redoMenuButton.TooltipText = "Redo";
			redoMenuButton.Clicked+= redoTBButtonActivated;
			toolbar.Insert(redoMenuButton, CATEGORY_BUTTON_SEPARATOR_INDEX);
			undoMenuButton = new MenuToolButton("gtk-undo");
			undoMenuButton.Label = "Undo";
			undoMenuButton.TooltipText = "Undo";
			undoMenuButton.Clicked+= undoTBButtonActivated;
			toolbar.Insert(undoMenuButton, CATEGORY_BUTTON_SEPARATOR_INDEX);
			toolbar.Remove(toolbar.Children[CATEGORY_BUTTON_SEPARATOR_INDEX-1]);
			toolbar.Remove(toolbar.Children[CATEGORY_BUTTON_SEPARATOR_INDEX-2]);
			toolbar.ShowAll();
			
			Title = AppTitle;
			TreeViewColumn mainColumn = new TreeViewColumn ();
			mainColumn.Title = "Army Categories";
			CellRendererText mainCell = new CellRendererText ();
			mainColumn.PackStart (mainCell, true);
			treeUnits.AppendColumn(mainColumn);
			mainColumn.SetCellDataFunc(mainCell, new TreeCellDataFunc(RenderCategoryTreeObjectName));
			treeUnits.Model = new TreeStore(typeof(WarFoundryObject));
			logger.Debug("Loading preferences");
			Preferences = new Preferences("WarFoundryGTK");
			logger.Debug("Loading translations");
			
			try
			{
				Translation.InitialiseTranslations(Constants.ExecutablePath, Preferences["language"].ToString());
			}
			catch (TranslationLoadException ex)
			{
				logger.Error(ex);
				MessageDialog dialog = new MessageDialog(this, DialogFlags.Modal, MessageType.Error, ButtonsType.Ok, ex.Message);
				dialog.Title = "Translation loading failed";
				dialog.Run();
				dialog.Destroy();
			}
			
			logger.Debug("Initialising");
			commandStack = new CommandStack();
			commandStack.CommandStackUpdated+=new MethodInvoker(commandStack_CommandStackUpdated);
			WarFoundryCore.GameSystemChanged+= new GameSystemChangedDelegate(OnGameSystemChanged);
			WarFoundryCore.ArmyChanged+= new ArmyChangedDelegate(OnArmyChanged);
			Destroyed+= new EventHandler(OnWindowDestroyed);
			//TODO: Translate and subscribe to other events
			UnitAddedMethod = new ObjectAddDelegate(OnUnitAdded);
			UnitRemovedMethod = new ObjectRemoveDelegate(OnUnitRemoved);
			PointsValueChangedMethod = new DoubleValChangedDelegate(OnPointsValueChanged);
			FailedUnitRequirementMethod = new FailedUnitRequirementDelegate(OnFailedUnitRequirement);
			UnitNameChangedMethod = new StringValChangedDelegate(OnUnitNameChanged);
			logger.Debug("Initialising complete - trying to load default army or system");
			
			//FIXME: Temporary hack to add paths and factories
			WarFoundryLoader.GetDefault().AddLoadDirectory(new DirectoryInfo(Constants.ExecutablePath + Constants.DirectoryString + "data"));
			IWarFoundryFactory factory = WarFoundryFactoryFactory.GetFactoryFactory().GetFactory(typeof(WarFoundryXmlFactory));
			
			if (factory!=null && factory is WarFoundryXmlFactory)
			{
				WarFoundryLoader.GetDefault().RegisterFactory((WarFoundryXmlFactory)factory);
			}
			
			factory = WarFoundryFactoryFactory.GetFactoryFactory().GetFactory(typeof(RollcallFactory));
			
			if (factory!=null && factory is RollcallFactory)
			{
				WarFoundryLoader.GetDefault().RegisterNonNativeFactory((INonNativeWarFoundryFactory)factory);
			}
								
			if (args.Length == 1)
			{
				logger.Debug("Attempting to load from file");				
				FileInfo file = new FileInfo(args[0]);
				
				try
				{
					ICollection<IWarFoundryObject> objects = WarFoundryLoader.GetDefault().LoadFile(file);

					if (objects.Count == 1)
					{
						List<IWarFoundryObject> objectList = new List<IWarFoundryObject>();
						objectList.AddRange(objects);
						IWarFoundryObject loadedObject = objectList[0];
						
						if (loadedObject is Army)
						{
							WarFoundryCore.CurrentArmy = (Army)loadedObject;
							logger.InfoFormat("Loaded army from {0}", file.FullName);
						}
						else if (loadedObject is GameSystem)
						{
							WarFoundryCore.CurrentGameSystem = (GameSystem)loadedObject;
							logger.InfoFormat("Loaded game system from {0}", file.FullName);
						}
					}
				}
				catch (InvalidFileException ex)
				{
					//TODO: show error dialog
					logger.Error(ex);
				}
			}
			else
			{
				string gameSystemID = Preferences.GetStringProperty("currSystem");

				if (gameSystemID!=null && !"".Equals(gameSystemID))
				{
					logger.Debug("Attempting to load current game system from properties");
					GameSystem sys = WarFoundryLoader.GetDefault().GetGameSystem(gameSystemID);
					
					if (sys!=null)
					{
						WarFoundryCore.CurrentGameSystem = sys;
						logger.InfoFormat("Loaded game system {0} from properties", gameSystemID);
					}
				}
			}
		}
		
		private void RenderCategoryTreeObjectName(TreeViewColumn column, CellRenderer cell, TreeModel model, TreeIter iter)
		{
			object o = model.GetValue(iter, 0);
			
			if (o is ArmyCategory)
			{
				ArmyCategory c = (ArmyCategory)o;
				string name = "";
				
				if (Preferences.GetBooleanProperty("ShowCatPercentage"))
				{
					name = Translation.GetTranslation("categoryTreeCatName", "{0} - {1}pts", c.Name, c.PointsTotal);
				}
				else
				{
					name = Translation.GetTranslation("categoryTreeCatNamePercentage", "{0} - {1}pts ({2}%)", c.Name, c.PointsTotal, (c.ParentArmy.PointsTotal > 0 ? Math.Round((c.PointsTotal / c.ParentArmy.PointsTotal) * 100) : 0));
				}
				
				(cell as CellRendererText).Text = name;
			}
			else if (o is IBBoard.WarFoundry.API.Objects.Unit)
			{
				IBBoard.WarFoundry.API.Objects.Unit u = (IBBoard.WarFoundry.API.Objects.Unit)o;
				string name = Translation.GetTranslation("categoryTreeCatName", "{0} - {1}pts", u.Name, u.PointsValue);
				(cell as CellRendererText).Text = name;
			}
		}
		
		private void OnWindowDestroyed(object source, EventArgs args)
		{
			logger.Info("Exiting");
			Application.Quit();
		}
		
		private void OnUnitNameChanged(WarFoundryObject val, string oldValue, string newValue)
		{
			IBBoard.WarFoundry.API.Objects.Unit unit = (IBBoard.WarFoundry.API.Objects.Unit)val;
			UnitDisplayWidget widget;
			unitToWidgetMap.TryGetValue(unit, out widget);
			
			if (widget!=null)
			{
				unitsNotebook.SetTabLabelText(widget, newValue);
			}
		}
		
		private void OnUnitAdded(WarFoundryObject val)
		{
			IBBoard.WarFoundry.API.Objects.Unit unit = (IBBoard.WarFoundry.API.Objects.Unit)val;
			unit.NameChanged+= UnitNameChangedMethod;
			AddUnitToTree(unit);
		}
		
		private void AddUnitToTree(IBBoard.WarFoundry.API.Objects.Unit unit)
		{
			TreeStore model = (TreeStore)treeUnits.Model;
			TreeIter iter;
			model.GetIterFirst(out iter);
			
			do
			{
				object obj = model.GetValue(iter, 0);
				
				if (obj is ArmyCategory)
				{
					ArmyCategory cat = (ArmyCategory)obj;
					
					if (cat.Equals(unit.Category))
					{
						model.AppendValues(iter, unit);
						TreePath path = model.GetPath(iter);
						treeUnits.ExpandToPath(path);
					}
				}
			}
			while (model.IterNext(ref iter));
		}
		
		private void OnUnitRemoved(WarFoundryObject obj)
		{
			IBBoard.WarFoundry.API.Objects.Unit unit = (IBBoard.WarFoundry.API.Objects.Unit)obj;
			unit.NameChanged-= UnitNameChangedMethod;
			RemoveUnitFromTree(unit);
			
			//See if unit has a tab open and close it if it does
		}
		
		private void RemoveUnitFromTree(IBBoard.WarFoundry.API.Objects.Unit unit)
		{
			TreeStore model = (TreeStore)treeUnits.Model;
			TreeIter iter;
			model.GetIterFirst(out iter);
			bool removed = false;
			
			do
			{
				object obj = model.GetValue(iter, 0);
				
				if (obj is ArmyCategory)
				{
					ArmyCategory cat = (ArmyCategory)obj;
					
					if (unit.Category == null || cat.Equals(unit.Category))
					{
						TreeIter innerIter;
						model.IterChildren(out innerIter, iter);
						
						do
						{
							object innerObj = model.GetValue(innerIter, 0);
							
							if (unit.Equals(innerObj))
							{
								model.Remove(ref innerIter);
								removed = true;
								break;
							}
						}
						while (model.IterNext(ref innerIter));
						
						if (removed)
						{
							break;
						}
					}
				}
			}
			while (model.IterNext(ref iter));
		}
		
		private void OnPointsValueChanged(WarFoundryObject obj, double before, double after)
		{
			//Set points in panel
		}
		
		private void OnFailedUnitRequirement(List<FailedUnitRequirement> failedRequirement)
		{
			//Show error message in panel
		}
		
		public Preferences Preferences
		{
			get { return preferences; }
			set { preferences = value; }		
		}
		
		/*public AbstractNativeWarFoundryFactory Factory
		{
			get { return WarFoundryFactoryFactory.GetFactoryFactory().GetFactory(Constants.ExecutablePath, factoryType); }
		}*/
		
		protected void OnDeleteEvent (object sender, DeleteEventArgs a)
		{
			Application.Quit ();
			a.RetVal = true;
		}

		protected virtual void OnExitActivated(object sender, System.EventArgs e)
		{
			Application.Quit();
		}

		protected virtual void OnChangeGameSystemActivated(object sender, System.EventArgs e)
		{
			ChangeCurrentGameSystem();
		}

		protected virtual void OnCreateArmyActivated(object sender, System.EventArgs e)
		{
			CreateNewArmy();
		}

		protected virtual void OnReloadFilesActivated(object sender, System.EventArgs e)
		{
		}

		protected virtual void OnSaveArmyAsActivated(object sender, System.EventArgs e)
		{
			SaveCurrentArmyAs();
		}

		protected virtual void OnCloseArmyActivated(object sender, System.EventArgs e)
		{
			CloseCurrentArmy();
		}

		protected virtual void OnOpenArmyActivated(object sender, System.EventArgs e)
		{
			OpenArmy();
		}

		protected virtual void OnSaveArmyActivated(object sender, System.EventArgs e)
		{
			SaveCurrentArmy();
		}
		
		protected virtual void OnAddUnitActivated(object sender, System.EventArgs e)
		{
			if (sender is ToolButton)
			{
				Category cat = null;
				categoryMap.TryGetValue((ToolButton)sender, out cat);
				
				if (cat!=null)
				{
					logger.DebugFormat("Show FrmNewUnit for {0}", cat.Name);
					FrmNewUnit newUnit = new FrmNewUnit(WarFoundryCore.CurrentArmy.Race, cat, WarFoundryCore.CurrentArmy);
					ResponseType response = (ResponseType)newUnit.Run();
					newUnit.Hide();
					
					if (response==ResponseType.Ok)
					{
						CreateAndAddUnitCommand cmd = new CreateAndAddUnitCommand(newUnit.SelectedUnit, WarFoundryCore.CurrentArmy);
						commandStack.Execute(cmd);
					}
					
					newUnit.Dispose();
				}
			}
		}
		
		public CommandStack CommandStack
		{
			get { return commandStack; }
		}
		
		private void SetAppTitle()
		{
			if (WarFoundryCore.CurrentArmy!=null)
			{
				Title = AppTitle + " - " + WarFoundryCore.CurrentGameSystem.Name + " - " + WarFoundryCore.CurrentArmy.Name;
			}
			else if (WarFoundryCore.CurrentGameSystem!=null)
			{
				Title = AppTitle + " - " + WarFoundryCore.CurrentGameSystem.Name;
			}
			else
			{
				Title = AppTitle;
			}
		}
		
		private void OnGameSystemChanged(GameSystem oldSys, GameSystem newSys)
		{
			system = newSys;
			SetAppTitle();
			miCreateArmy.Sensitive = system!=null;
			newArmyButton.Sensitive = system!=null;
			RemoveCategoryButtons();
			
			if (system!=null)
			{
				AddCategoryButtons(system.Categories);
			}
		}
		
		private void OnArmyChanged(Army oldArmy, Army newArmy)
		{
			loadedArmyPath = null;
			SetAppTitle();
			SetArmyTree(newArmy);
						
			if (oldArmy!=null)
			{
				oldArmy.UnitAdded-= UnitAddedMethod;
				oldArmy.UnitRemoved-= UnitRemovedMethod;
				oldArmy.PointsValueChanged-= PointsValueChangedMethod;
				oldArmy.FailedRequirement-=FailedUnitRequirementMethod;				
			}
			
			unitToWidgetMap.Clear();
			
			while (unitsNotebook.NPages > 0)
			{
				unitsNotebook.RemovePage(0);
			}
			
			if (newArmy==null)
			{
				DisableCategoryButtons();
			}
			else
			{
				newArmy.UnitAdded+= UnitAddedMethod;
				newArmy.UnitRemoved+= UnitRemovedMethod;
				newArmy.PointsValueChanged+= PointsValueChangedMethod;
				newArmy.FailedRequirement+=FailedUnitRequirementMethod;
				//TODO: Clear all buttons
				EnableCategoryButtons();

				if (newArmy.Race.HasCategoryOverrides())
				{
					RemoveCategoryButtons();
					AddCategoryButtons(newArmy.Race.Categories);
				}
			}
			
			miCloseArmy.Sensitive = newArmy!=null;
			miSaveArmyAs.Sensitive = newArmy!=null;
			//New army has no changes, so we can't save it
			miSaveArmy.Sensitive = false;
			saveArmyButton.Sensitive = false;

			CommandStack.Reset();
			SetPointsPanelText();
		}
		
		private void SetArmyTree(Army army)
		{
			logger.Debug("Resetting tree");
			TreeStore store = (TreeStore)treeUnits.Model;
			store.Clear();
			TreeIter iter;
			
			if (army!=null)
			{
				logger.Debug("Loading in categories to tree");
								
				foreach (ArmyCategory cat in army.Categories)
				{
					logger.DebugFormat("Append category {0}", cat.Name);
					iter = store.AppendValues(cat);
					
					foreach (IBBoard.WarFoundry.API.Objects.Unit unit in cat.GetUnits())
					{
						store.AppendValues(iter, unit);
					} 
				}
				
				logger.Debug("Finished loading tree categories");
			}
		}
		
		private void DisableCategoryButtons()
		{
			SetCategoryButtonsSensitive(false);
		}
		
		private void EnableCategoryButtons()
		{
			SetCategoryButtonsSensitive(true);
		}
		
		private void SetCategoryButtonsSensitive(bool state)
		{
			int toolbarButtonCount = toolbar.Children.Length - 1;
			logger.Debug("Last button index: "+toolbarButtonCount);
			
			for (int i = toolbarButtonCount; i > CATEGORY_BUTTON_SEPARATOR_INDEX; i--)
			{
				logger.DebugFormat("Setting button {0} state to {1}", i, state);
				toolbar.Children[i].Sensitive = state;
			}
		}
		
		private void RemoveCategoryButtons()
		{
			int toolbarButtonCount = toolbar.Children.Length - 1;
			
			for (int i = toolbarButtonCount; i > CATEGORY_BUTTON_SEPARATOR_INDEX; i--)
			{
				toolbar.Remove(toolbar.Children[i]);
			}
			
			categoryMap.Clear();
		}
		
		private void AddCategoryButtons(Category[] cats)
		{
			if (cats!=null && cats.Length > 0)
			{
				logger.DebugFormat("Toolbar button count: {0}. Adding {1} categories.", toolbar.Children.Length, cats.Length);
				
				foreach (Category cat in cats)
				{
					ToolButton button = new ToolButton("gtk-add");
					button.Label = cat.Name;
					button.TooltipText = "Add unit from "+cat.Name;
					//TODO: See if we can associate data in some way, the same as we can with SWF. For now we just use the map.
					categoryMap.Add(button, cat);
					button.Clicked+= new System.EventHandler(OnAddUnitActivated);
					toolbar.Insert(button, -1);
				}
			}
			
			toolbar.Children[CATEGORY_BUTTON_SEPARATOR_INDEX].Visible = cats!=null && cats.Length>0;
			
			toolbar.ShowAll();
		}
		
		private void SetPointsPanelText()
		{
			//TODO: Set the points value in the status bar
		}
		
		private void commandStack_CommandStackUpdated()
		{
			undoMenuButton.Sensitive = commandStack.CanUndo();
			miUndo.Sensitive = undoMenuButton.Sensitive;
			redoMenuButton.Sensitive = commandStack.CanRedo();
			miRedo.Sensitive = redoMenuButton.Sensitive;
			int redoLength = commandStack.RedoLength;
			//TODO: Build menus for undo/redo and find way of adding tooltips
			/*int maxRedo = Math.Min(10, redoLength);
			MenuItem[] menuItems = null;
		
			if (redoLength > 0)
			{
				menuItems = new MenuItem[maxRedo];
				Command com;
				MenuItem mi;

				for (int i = 0; i < maxRedo; i++)
				{
					com = commandStack.PeekRedoCommand(i+1);

					if (com == null)
					{
						break; 
					}

					mi = new MenuItem(com.Description);
					mi.Click+=new EventHandler(redoMenu_Click);
					menuItems[i] = mi;
				}
			}

			redoMenu.MenuItems.Clear();

			if (menuItems!=null && menuItems[0]!=null)
			{
				bttnRedo.ToolTipText = menuItems[0].Text;
				redoMenu.MenuItems.AddRange(menuItems);
			}*/
			//TODO: Put above code back when we have a dropdown version of the redo button
			if (redoLength > 0)
			{
				//redoMenuButton.Tooltip = CommandStack.PeekRedoCommand().Description;
			}

			int undoLength = commandStack.UndoLength;
			/*int maxUndo = Math.Min(10, undoLength);
			MenuItem[] menuItemsUndo = null;
			
			if (undoLength > 0)
			{
				menuItemsUndo = new MenuItem[maxUndo];
				Command com;
				MenuItem mi;

				for (int i = 0; i < maxUndo; i++)
				{
					com = commandStack.PeekUndoCommand(i+1);

					if (com == null)
					{
						break; 
					}

					mi = new MenuItem(com.UndoDescription);
					mi.Click+=new EventHandler(undoMenu_Click);
					menuItemsUndo[i] = mi;
				}
			}

			undoMenu.MenuItems.Clear();

			if (menuItemsUndo!=null && menuItemsUndo[0]!=null)
			{
				bttnUndo.ToolTipText = menuItemsUndo[0].Text;
				undoMenu.MenuItems.AddRange(menuItemsUndo);
			}*/			
			//TODO: Put above code back when we have a dropdown version of the undo button
			if (undoLength > 0)
			{
				//undoMenuButton.Tooltip = CommandStack.PeekUndoCommand().UndoDescription;
			}
			
			saveArmyButton.Sensitive = commandStack.IsDirty() && WarFoundryCore.CurrentArmy!=null && CanSave();
			miSaveArmy.Sensitive = commandStack.IsDirty() && WarFoundryCore.CurrentArmy!=null && CanSave();
		}	
		
		private bool CanSave()
		{
			return loadedArmyPath!=null && WarFoundryCore.CurrentArmy!=null && WarFoundrySaver.GetSaver()!=null;
		}

		private bool SaveCurrentArmyOrSaveAs()
		{
			if (CanSave())
			{
				return SaveCurrentArmy();
			}
			else 
			{
				return SaveCurrentArmyAs();
			}
		}
		
		private bool OpenArmy()
		{
			//TODO: Open dialog for file selection then open army
			bool success = false;
			loadedArmyPath = null;//TODO: Set loaded file path
			return success;
		}

		private bool SaveCurrentArmy()
		{
			bool success = false;
			
			if (CanSave())
			{
				try
				{
					if (WarFoundrySaver.GetSaver().Save(WarFoundryCore.CurrentArmy, loadedArmyPath))
					{
						saveArmyButton.Sensitive = false;
						miSaveArmy.Sensitive = false;
						CommandStack.setCleanMark();
						success = true;
					}
				}
				catch (IOException ex)
				{
					logger.Error("Saving army failed", ex);
					MessageDialog md = new MessageDialog(this, DialogFlags.Modal, MessageType.Error, ButtonsType.Ok, "An error occured while saving the army. Please check the logs for details on what failed");
					md.Show();
				}
			}
			
			return success;
		}

		private bool SaveCurrentArmyAs()
		{
			/*if (saveArmyDialog.Filter == "")
			{
				string savePath = UserDataPath+Constants.DirectoryString+"armies"+Constants.DirectoryString;
				
				if (!Directory.Exists(savePath))
				{
					Directory.CreateDirectory(savePath);
				}

				saveArmyDialog.InitialDirectory = savePath;
				saveArmyDialog.Filter = Translation.GetTranslation("armyFileFilter")+"|*.army";
				saveArmyDialog.Title = Translation.GetTranslation("saveArmyDialog");
			}

			DialogResult dr = saveArmyDialog.ShowDialog(this);

			if (dr == DialogResult.OK)
			{
				if (WarFoundrySaver.GetSaver().Save(WarFoundryCore.CurrentArmy, saveArmyDialog.FileName))
				{
					miSaveArmy.Enabled = false;
					bttnSaveArmy.Enabled = false;
					CommandStack.setCleanMark();
					loadedArmyPath = saveArmyDialog.FileName;
					return true;
				}
				else
				{
					MessageBox.Show(this, Translation.GetTranslation("SaveFailed"), Translation.GetTranslation("SaveFailedTitle"), MessageBoxButtons.OK, MessageBoxIcon.Error);
					return false;
				}
			}
			else
			{
				return false;
			}*/
			return false;
		}
		
		private bool CloseCurrentArmy()
		{
			if (WarFoundryCore.CurrentArmy!=null)
			{
				bool canClose = false;

				if (CommandStack.IsDirty())
				{
					MessageDialog dia  = new MessageDialog(this, DialogFlags.DestroyWithParent, MessageType.Question, ButtonsType.YesNo | ButtonsType.Cancel, "The army \""+WarFoundryCore.CurrentArmy.Name+"\" has been modified.\r\nSave changes before closing army?");   
					ResponseType dr = (ResponseType)dia.Run();

					if (dr == ResponseType.Yes)
					{
						//They want to save so try to save it or prompt for save as
						//If they cancel the save as then assume they don't want to close
						canClose = SaveCurrentArmyOrSaveAs();
					}
					else if (dr == ResponseType.No)
					{
						//They don't care about their changes
						canClose = true;
					}
					else
					{
						//Assume cancel or close with the X button
						canClose = false;
					}
					
					dia.Dispose();
				}
				else
				{
					//Nothing has changed so we can safely close
					canClose = true;
				}

				if (canClose)
				{
					//do close
					WarFoundryCore.CurrentArmy = null;
					return true;
				}
				else
				{
					return false;
				}
			}
			else
			{
				//pretend we succeeded
				return true;
			}
		}

		private void CreateNewArmy()
		{
			logger.Debug("Create new army");
			FrmNewArmy newArmy = new FrmNewArmy(WarFoundryCore.CurrentGameSystem);
			ResponseType type = (ResponseType)newArmy.Run();
			newArmy.Hide();
		
			if (type == ResponseType.Ok)
			{
				if (CloseCurrentArmy())
				{
					WarFoundryCore.CurrentArmy = new Army(newArmy.SelectedRace, newArmy.ArmyName, newArmy.ArmySize);
				}
			}
			else
			{
				logger.Debug("Create new army cancelled");
			}
			
			newArmy.Destroy();
		}
		
		private void ChangeCurrentGameSystem()
		{				
			logger.Debug("Changing game system");
			FrmChangeGameSystem dialog = new FrmChangeGameSystem(this);
			ResponseType type = (ResponseType)dialog.Run();
			dialog.Hide();
			
			if (type == ResponseType.Ok)
			{
				WarFoundryCore.CurrentGameSystem = dialog.SelectedSystem;
			}
			else
			{
				logger.Debug("Game system change cancelled");
			}
			
			dialog.Destroy();
		}

		protected virtual void undoTBButtonActivated (object sender, System.EventArgs e)
		{
			CommandStack.Undo();
		}

		protected virtual void redoTBButtonActivated (object sender, System.EventArgs e)
		{
			CommandStack.Redo();
		}

		protected virtual void saveTBButtonActivated (object sender, System.EventArgs e)
		{
			SaveCurrentArmy();
		}

		protected virtual void openTBButtonActivated (object sender, System.EventArgs e)
		{
			OpenArmy();
		}

		protected virtual void newTBButtonActivated (object sender, System.EventArgs e)
		{
			CreateNewArmy();
		}

		protected virtual void ArmyRowActivated (object o, Gtk.RowActivatedArgs args)
		{
			TreeModel model = treeUnits.Model;
			TreeIter iter;
			model.GetIter(out iter, args.Path);
			object obj = model.GetValue(iter, 0);
			
			if (obj is IBBoard.WarFoundry.API.Objects.Unit)
			{
				IBBoard.WarFoundry.API.Objects.Unit unit = (IBBoard.WarFoundry.API.Objects.Unit)obj;
				
				UnitDisplayWidget widget;
				unitToWidgetMap.TryGetValue(unit, out widget);
				
				if (widget!=null)
				{
					logger.DebugFormat("Selecting existing page for "+unit.Name);
					unitsNotebook.Page = unitsNotebook.PageNum(widget);
				}
				else
				{
					widget = new UnitDisplayWidget(unit, CommandStack);
					Label label = new Label(unit.Name);
					logger.Debug("Adding page for "+unit.Name);
					unitToWidgetMap[unit] = widget;
					int pageNum = unitsNotebook.AppendPage(widget, label);
					logger.Debug("Page added at index "+pageNum);
					unitsNotebook.ShowAll();
					unitsNotebook.Page = pageNum;
				}
			}
		}
	}
}