changeset 14:61bc9b44a695

Re #242: Create Qt# UI for WarFoundry * Add log4net logging output * Output as "WarFoundry.exe" * Wrap execution in try...catch to try to debug occasional crashes * Refactor common "connect action to menu" method Re #247: Implement menu options in Qt# app * Implement "open" action * Make "create" check for closing the old army
author IBBoard <dev@ibboard.co.uk>
date Sat, 13 Feb 2010 11:56:14 +0000
parents dbe784f0802c
children 03ed32fdc706
files AssemblyInfo.cs IBBoard.WarFoundry.GUI.QtSharp.csproj Main.cs MainWindow.cs WarFoundry.exe.log4net
diffstat 5 files changed, 183 insertions(+), 35 deletions(-) [+]
line wrap: on
line diff
--- a/AssemblyInfo.cs	Sat Feb 13 10:34:41 2010 +0000
+++ b/AssemblyInfo.cs	Sat Feb 13 11:56:14 2010 +0000
@@ -27,3 +27,4 @@
 
 //[assembly: AssemblyDelaySign(false)]
 //[assembly: AssemblyKeyFile("")]
+[assembly: log4net.Config.XmlConfigurator(ConfigFileExtension = "log4net", Watch = true)]
\ No newline at end of file
--- a/IBBoard.WarFoundry.GUI.QtSharp.csproj	Sat Feb 13 10:34:41 2010 +0000
+++ b/IBBoard.WarFoundry.GUI.QtSharp.csproj	Sat Feb 13 11:56:14 2010 +0000
@@ -8,7 +8,8 @@
     <ProjectGuid>{299D84D6-C84A-45CD-B709-AF536FCBA937}</ProjectGuid>
     <OutputType>Exe</OutputType>
     <RootNamespace>IBBoard.WarFoundry.GUI.QtSharp</RootNamespace>
-    <AssemblyName>IBBoard.WarFoundry.GUI.QtSharp</AssemblyName>
+    <AssemblyName>WarFoundry</AssemblyName>
+    <StartupObject>IBBoard.WarFoundry.GUI.QtSharp.MainClass</StartupObject>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
     <DebugSymbols>true</DebugSymbols>
@@ -79,6 +80,9 @@
     <None Include="lib\log4net.dll">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </None>
+    <None Include="WarFoundry.exe.log4net">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </None>
   </ItemGroup>
   <ItemGroup>
     <Folder Include="qt-gui\" />
--- a/Main.cs	Sat Feb 13 10:34:41 2010 +0000
+++ b/Main.cs	Sat Feb 13 11:56:14 2010 +0000
@@ -19,14 +19,22 @@
 		
 		public static void Main (string[] args)
 		{
-			new QApplication (args);
-			
-			SetUpWarFoundryEnvironment ();
-			
-			MainWindow win = new MainWindow ();
-			win.Show ();
-			
-			QApplication.Exec ();
+			try
+			{
+				new QApplication (args);
+				
+				SetUpWarFoundryEnvironment ();
+				
+				MainWindow win = new MainWindow ();
+				win.Show ();
+				
+				QApplication.Exec ();
+			}
+			catch(Exception ex)
+			{
+				logger.Fatal(ex);
+			}
+				
 		}
 		
 		private static void SetUpWarFoundryEnvironment ()
--- a/MainWindow.cs	Sat Feb 13 10:34:41 2010 +0000
+++ b/MainWindow.cs	Sat Feb 13 11:56:14 2010 +0000
@@ -2,11 +2,13 @@
 // 
 // 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.IO;
 using System.Collections.Generic;
 using Qyoto;
 using log4net;
 using IBBoard.Commands;
 using IBBoard.IO;
+using IBBoard.Lang;
 using IBBoard.WarFoundry.API;
 using IBBoard.WarFoundry.API.Factories;
 using IBBoard.WarFoundry.API.Objects;
@@ -23,6 +25,7 @@
 		private string loadedFilePath;
 		private CommandStack commandStack;
 		private QFileDialog saveArmyDialog;
+		private QFileDialog openArmyDialog;
 		
 		public MainWindow ()
 		{
@@ -32,6 +35,11 @@
 			SetAppTitle();
 			saveArmyDialog = new QFileDialog(this);
 			saveArmyDialog.acceptMode = QFileDialog.AcceptMode.AcceptSave;
+			saveArmyDialog.fileMode = QFileDialog.FileMode.AnyFile;
+			openArmyDialog = new QFileDialog(this);
+			openArmyDialog.acceptMode = QFileDialog.AcceptMode.AcceptOpen;
+			openArmyDialog.fileMode = QFileDialog.FileMode.ExistingFile;
+			openArmyDialog.SetNameFilter("*.army");
 			SetUpActionIcons();
 			ConnectMenuActions();
 			SetUpToolbar();
@@ -54,36 +62,45 @@
 			layout.actionAbout.icon = new QIcon("icons/ui/help-about.png");
 		}
 		
-		private void ConnectMenuActions()
+		private void ConnectMenuActions ()
 		{
-			QObject.Connect(layout.actionCreateArmy, SIGNAL("triggered()"), CreateNewArmy);
-			QObject.Connect(layout.actionUndo, SIGNAL("triggered()"), UndoAction);
-			QObject.Connect(layout.actionRedo, SIGNAL("triggered()"), RedoAction);
-			QObject.Connect(layout.actionSaveArmyAs, SIGNAL("triggered()"), DoSaveCurrentArmyAs);
-			QObject.Connect(layout.actionSaveArmy, SIGNAL("triggered()"), DoSaveCurrentArmy);
-			QObject.Connect(layout.actionCloseArmy, SIGNAL("triggered()"), CloseArmy);
+			ConnectMenuAction (layout.actionCreateArmy, CreateNewArmy);
+			ConnectMenuAction (layout.actionUndo, UndoAction);
+			ConnectMenuAction (layout.actionRedo,RedoAction);
+			ConnectMenuAction (layout.actionSaveArmyAs, DoSaveCurrentArmyAs);
+			ConnectMenuAction (layout.actionSaveArmy, DoSaveCurrentArmy);
+			ConnectMenuAction (layout.actionCloseArmy, DoCloseArmy);
+			ConnectMenuAction (layout.actionOpenArmy, DoOpenArmy);
+		}
+		
+		private void ConnectMenuAction (QAction menuAction, SlotFunc method)
+		{
+			QObject.Connect(menuAction, SIGNAL("triggered()"), method);
 		}
 		
 		private void CreateNewArmy()
 		{
-			NewArmyDialog dialog = new NewArmyDialog(this);
-			int result = dialog.Exec ();
-			
-			if (result == (int)QDialog.DialogCode.Accepted)
+			if (CloseCurrentArmy())
 			{
-				try
+				NewArmyDialog dialog = new NewArmyDialog(this);
+				int result = dialog.Exec ();
+				
+				if (result == (int)QDialog.DialogCode.Accepted)
 				{
-					CurrentArmy = new Army(dialog.GetSelectedRace(), dialog.GetArmyName(), dialog.GetArmySize());
-				}
-				catch (RequiredDataMissingException ex)
-				{
-					log.Error("Required data missing from race file", ex);
-					QMessageBox.Warning(this, "Invalid race file data", "the race file for the requested data could not be loaded as it did not contain some required data");
-				}
-				catch (InvalidFileException ex)
-				{
-					log.Error("Race file was invalid", ex);
-					QMessageBox.Warning(this, ex.Message,  "invalid race file");
+					try
+					{
+						CurrentArmy = new Army(dialog.GetSelectedRace(), dialog.GetArmyName(), dialog.GetArmySize());
+					}
+					catch (RequiredDataMissingException ex)
+					{
+						log.Error("Required data missing from race file", ex);
+						QMessageBox.Warning(this, "Invalid race file data", "the race file for the requested data could not be loaded as it did not contain some required data");
+					}
+					catch (InvalidFileException ex)
+					{
+						log.Error("Race file was invalid", ex);
+						QMessageBox.Warning(this, ex.Message,  "invalid race file");
+					}
 				}
 			}
 		}
@@ -167,6 +184,41 @@
 			}
 		}
 		
+		private void DoOpenArmy()
+		{
+			OpenArmy();
+		}
+		
+		private bool OpenArmy()
+		{
+			log.Debug("Opening army");
+			string newFilePath = PromptForFilePath (openArmyDialog);
+			bool openedFile = false;
+
+			if (newFilePath != null && CloseCurrentArmy())
+			{
+				try
+				{
+					log.DebugFormat("Opening {0}", newFilePath);
+					CurrentArmy = WarFoundryLoader.GetDefault().LoadArmy(new FileInfo(newFilePath));
+					loadedFilePath = newFilePath;
+					openedFile = true;
+				}
+				catch (RequiredDataMissingException ex)
+				{
+					log.Error(ex);
+					QMessageBox.Critical(this, Translation.GetTranslation("InvalidArmyFileBoxTitle", "invalid army file"), ex.Message);
+				}
+				catch (InvalidFileException ex)
+				{
+					log.Error(ex);
+					QMessageBox.Critical(this, Translation.GetTranslation("InvalidArmyFileBoxTitle", "invalid army file"), ex.Message);
+				}
+			}
+	
+			return openedFile;
+		}
+		
 		private void DoSaveCurrentArmy()
 		{
 			SaveCurrentArmy();
@@ -174,6 +226,7 @@
 
 		private bool SaveCurrentArmy()
 		{
+			log.Debug("Save current army");
 			bool saved = false;
 
 			string filePath = loadedFilePath;
@@ -198,6 +251,7 @@
 		
 		private bool SaveCurrentArmyAs()
 		{
+			log.Debug("Saving current army as a different file");
 			bool saved = false;
 			string filePath = PromptForArmyFilePath();
 
@@ -211,8 +265,11 @@
 
 		private bool SaveCurrentArmyToFile(string filePath)
 		{
+			log.DebugFormat("Save to {0}", filePath);
+			
 			if (WarFoundrySaver.GetSaver().Save(CurrentArmy, filePath))
 			{
+				log.Debug("Army saved");
 				loadedFilePath = filePath;
 				layout.actionSaveArmy.Enabled = false;
 				CommandStack.setCleanMark();
@@ -220,6 +277,7 @@
 			}
 			else
 			{
+				log.Debug("Save failed");
 				QMessageBox.Critical(this, "file save failed", "file save failed - check log for details");
 				return false;
 			}
@@ -227,7 +285,12 @@
 
 		private string PromptForArmyFilePath()
 		{
-			int result = saveArmyDialog.Exec();
+			return PromptForFilePath (saveArmyDialog);
+		}
+		
+		private string PromptForFilePath(QFileDialog qFileDialog)
+		{
+			int result = qFileDialog.Exec();
 			string path = null;
 
 			if (result == (int)QDialog.DialogCode.Accepted)
@@ -407,9 +470,58 @@
 			//TODO enable category buttons
 		}
 		
-		private void CloseArmy()
+		private void DoCloseArmy()
+		{
+			CloseCurrentArmy();
+		}
+		
+		private bool CloseCurrentArmy()
 		{
-			CurrentArmy = null;
+			bool closed = false;
+			
+			if (CurrentArmy!=null)
+			{
+				log.Debug("Closing "+CurrentArmy.Name);
+				bool canClose = false;
+
+				if (CommandStack.IsDirty() || true)
+				{
+					log.Debug("Unsaved changes");
+					string saveChanges = Translation.GetTranslation("SaveChangesQuestion", "the army \"{0}\" has been modified\r\nsave changes before closing army?", CurrentArmy.Name);
+					string saveChangesTitle = Translation.GetTranslation("SaveChangesTitle", "unsaved changes");
+					QMessageBox.StandardButton response = QMessageBox.Question(this, saveChangesTitle, saveChanges, (uint) (QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No | QMessageBox.StandardButton.Cancel), QMessageBox.StandardButton.Cancel);
+					
+					if (response == QMessageBox.StandardButton.Yes)
+					{
+						canClose = SaveCurrentArmy();
+					}
+					else if (response == QMessageBox.StandardButton.No)
+					{
+						log.Debug("User didn't save army");
+						canClose = true;
+					}
+					//else they said cancel and we default to "canClose = false" so do nothing
+				}
+				else
+				{
+					canClose = true;
+				}
+
+				if (canClose)
+				{
+					CurrentArmy = null;
+					closed = true;
+				}
+			}
+			else
+			{
+				//pretend we succeeded
+				closed = true;
+			}
+			
+			log.Debug("Army "+(closed ? "" : "not")+" closed");
+			
+			return closed;
 		}
 	}
 }
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WarFoundry.exe.log4net	Sat Feb 13 11:56:14 2010 +0000
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<log4net>
+	<appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender">
+		<layout type="log4net.Layout.PatternLayout">
+			<conversionPattern value="%-5p [%d{HH:MM:ss}]: %C{1}.%M() - Line: %L - %m%n" />
+		</layout>
+	</appender>
+	<appender name="RollingLogFileAppender" type="log4net.Appender.RollingFileAppender">
+		<file value="logs/WarFoundry.log" />
+		<appendToFile value="false" />
+		<rollingStyle value="Size" />
+		<maxSizeRollBackups value="-1" />
+		<maximumFileSize value="100MB" />
+		<layout type="log4net.Layout.PatternLayout">
+			<conversionPattern value="%-5p [%d{HH:MM:ss}]: %C{1}.%M() - Line: %L - %m%n" />
+		</layout>
+	</appender>
+	<root>
+		<level value="DEBUG" />
+		<appender-ref ref="ConsoleAppender" />
+		<appender-ref ref="RollingLogFileAppender" />
+	</root>
+</log4net>
\ No newline at end of file