Mercurial > repos > IBBoard.WarFoundry.GUI.QtSharp
view MainWindow.cs @ 26:55d4f16c982b
Fixes #250: QtSharp app doesn't always quit properly
* Override Close event to dispose of file dialogs (open and save)
* Remove unnecessary event handler hooked to close
* Change app initialisation to match pattern used by Synapse
* Create file dialogs without specifying "this" as parent - seems to be main fix, for some reason.
author | IBBoard <dev@ibboard.co.uk> |
---|---|
date | Sat, 31 Jul 2010 16:03:14 +0000 |
parents | 9641addafffe |
children | 5ee15def17e7 |
line wrap: on
line source
// This file (MainWindow.cs) is a part of the IBBoard.WarFoundry.API 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.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.Exporters; using IBBoard.WarFoundry.API.Factories; using IBBoard.WarFoundry.API.Objects; using IBBoard.WarFoundry.API.Savers; namespace IBBoard.WarFoundry.GUI.QtSharp { public class MainWindow : QMainWindow { private static readonly string AppTitle = "WarFoundry"; private Ui_MainWindowLayout layout; private readonly ILog log = LogManager.GetLogger(typeof(MainWindow)); private string loadedFilePath; private CommandStack commandStack; private QFileDialog saveArmyDialog; private QFileDialog openArmyDialog; private Preferences preferences; public MainWindow () { layout = new Ui_MainWindowLayout(); layout.SetupUi(this); WindowIcon = new QIcon("icons/App.png"); Preferences = new Preferences("WarFoundryQt"); try { Translation.InitialiseTranslations(Constants.ExecutablePath, Preferences["language"].ToString()); } catch (TranslationLoadException ex) { log.Error(ex); QMessageBox.Critical(this, "Translation loading failed", ex.Message); } SetAppTitle(); saveArmyDialog = new QFileDialog(); saveArmyDialog.acceptMode = QFileDialog.AcceptMode.AcceptSave; saveArmyDialog.fileMode = QFileDialog.FileMode.AnyFile; saveArmyDialog.SetDirectory(new QDir(Environment.GetFolderPath(Environment.SpecialFolder.Personal))); openArmyDialog = new QFileDialog(); openArmyDialog.acceptMode = QFileDialog.AcceptMode.AcceptOpen; openArmyDialog.fileMode = QFileDialog.FileMode.ExistingFile; openArmyDialog.SetNameFilter("*.army"); openArmyDialog.SetDirectory(new QDir(Environment.GetFolderPath(Environment.SpecialFolder.Personal))); SetUpActionIcons(); ConnectMenuActions(); SetUpToolbar(); layout.unitTabs.Clear(); WarFoundryCore.ArmyChanged+= HandleWarFoundryCoreArmyChanged; CommandStack.CommandStackUpdated+= HandleCommandStackCommandStackUpdated; } protected override void CloseEvent(QCloseEvent arg1) { base.CloseEvent(arg1); saveArmyDialog.Dispose(); openArmyDialog.Dispose(); } private void SetUpActionIcons() { layout.actionCreateArmy.icon = new QIcon("icons/ui/filenew.png"); layout.actionOpenArmy.icon = new QIcon("icons/ui/fileopen.png"); layout.actionSaveArmy.icon = new QIcon("icons/ui/filesave.png"); layout.actionSaveArmyAs.icon = new QIcon("icons/ui/filesaveas.png"); layout.menuExportArmyAs.icon = new QIcon("icons/ui/export.png"); layout.actionCloseArmy.icon = new QIcon("icons/ui/window-close.png"); layout.actionExit.icon = new QIcon("icons/ui/exit.png"); layout.actionUndo.icon = new QIcon("icons/ui/edit-undo.png"); layout.actionRedo.icon = new QIcon("icons/ui/edit-redo.png"); layout.actionAbout.icon = new QIcon("icons/ui/help-about.png"); } private void ConnectMenuActions() { 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); ConnectMenuAction(layout.actionExportBasicHtml, DoExportBasicHTML); } private void ConnectMenuAction(QAction menuAction, SlotFunc method) { QObject.Connect(menuAction, SIGNAL("triggered()"), method); } private void CreateNewArmy() { if (CloseCurrentArmy()) { NewArmyDialog dialog = new NewArmyDialog(this); int result = dialog.Exec (); if (result == (int)QDialog.DialogCode.Accepted) { try { CurrentArmy = new Army(dialog.GetSelectedRace(), dialog.GetArmyName(), dialog.GetArmySize()); } catch (RequiredDataMissingException ex) { log.Error("Required data missing from race file", ex); QMessageBox.Critical(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.Critical(this, "invalid race file", ex.Message); } } } } private void SetUpToolbar() { layout.toolBar.AddAction(layout.actionCreateArmy); layout.toolBar.AddAction(layout.actionOpenArmy); layout.toolBar.AddAction(layout.actionSaveArmy); layout.toolBar.AddSeparator(); layout.toolBar.AddAction(layout.actionUndo); layout.toolBar.AddAction(layout.actionRedo); layout.toolBar.AddSeparator(); } private void HandleWarFoundryCoreArmyChanged(Army oldValue, Army newValue) { CommandStack.Reset(); loadedFilePath = null; layout.actionSaveArmy.Enabled = false; SetPointsPanelText(); SetAppTitle(); } private void SetPointsPanelText () { //TODO: implement panel and points panel } private void SetAppTitle() { string str = AppTitle; if (CurrentGameSystem!=null) { str+= " - " + CurrentGameSystem.Name; if (CurrentArmy!=null) { str+= " - " + CurrentArmy.Name; } } this.WindowTitle = str; } public CommandStack CommandStack { get { if (commandStack == null) { commandStack = new CommandStack(); } return commandStack; } } private void HandleCommandStackCommandStackUpdated() { layout.actionUndo.Enabled = CommandStack.CanUndo(); layout.actionRedo.Enabled = CommandStack.CanRedo(); layout.actionSaveArmy.Enabled = CommandStack.IsDirty(); } private void UndoAction() { if (commandStack.CanUndo()) { commandStack.Undo(); } } private void RedoAction() { if (commandStack.CanRedo()) { commandStack.Redo(); } } 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(); } private bool SaveCurrentArmy() { log.Debug("Save current army"); bool saved = false; string filePath = loadedFilePath; if (filePath == null) { filePath = PromptForArmyFilePath(); } if (filePath != null) { saved = SaveCurrentArmyToFile(filePath); } return saved; } private void DoSaveCurrentArmyAs() { SaveCurrentArmyAs(); } private bool SaveCurrentArmyAs() { log.Debug("Saving current army as a different file"); bool saved = false; string filePath = PromptForArmyFilePath(); if (filePath != null) { saved = SaveCurrentArmyToFile(filePath); } return saved; } 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(); return true; } else { log.Debug("Save failed"); QMessageBox.Critical(this, "file save failed", "file save failed - check log for details"); return false; } } private string PromptForArmyFilePath() { return PromptForFilePath (saveArmyDialog); } private string PromptForFilePath(QFileDialog qFileDialog) { int result = qFileDialog.Exec(); string path = null; if (result == (int)QDialog.DialogCode.Accepted) { path = qFileDialog.SelectedFiles()[0]; } return path; } public GameSystem CurrentGameSystem { get { return WarFoundryCore.CurrentGameSystem; } set { WarFoundryCore.CurrentGameSystem = value; } } public Army CurrentArmy { get { return WarFoundryCore.CurrentArmy; } set { SetArmy(value); } } private void SetArmy(Army newArmy) { IgnoreArmy(CurrentArmy); CloseAllUnitWindows(); if (newArmy == null) { SetNullArmyState(); } else { WarFoundryCore.CurrentGameSystem = newArmy.GameSystem; ListenToArmy(newArmy); SetNonNullArmyState(newArmy); } WarFoundryCore.CurrentArmy = newArmy; } private void IgnoreArmy(Army oldArmy) { if (oldArmy != null) { oldArmy.UnitAdded -= UnitAddedMethod; oldArmy.UnitRemoved -= UnitRemovedMethod; oldArmy.PointsValueChanged -= PointsValueChangedMethod; } } private void UnitAddedMethod(object unitObj) { if (unitObj is Unit) { Unit unit = (Unit)unitObj; //TODO set error panel //sbErrorPanel.Text = ""; //TODO add unit to tree } } private void UnitRemovedMethod(object unitObj) { if (unitObj is Unit) { Unit unit = (Unit)unitObj; //TODO set error panel //sbErrorPanel.Text = ""; //TODO check if window is open, and close it if it is //TODO remove unit from tree } } private void PointsValueChangedMethod(WarFoundryObject obj, double oldVal, double newVal) { if (obj is Army) { SetPointsPanelText(); } } private void CloseAllUnitWindows() { // FrmUnit[] unitForms = DictionaryUtils.ToArray(unitWindows); // // foreach (FrmUnit window in unitForms) // { // window.Close(); // } // // unitWindows.Clear(); } private void ListenToArmy(Army newArmy) { if (newArmy != null) { newArmy.UnitAdded += UnitAddedMethod; newArmy.UnitRemoved += UnitRemovedMethod; newArmy.PointsValueChanged += PointsValueChangedMethod; } } private void SetNullArmyState() { layout.actionSaveArmyAs.Enabled = false; layout.actionCloseArmy.Enabled = false; layout.menuExportArmyAs.Enabled = false; foreach (QAction exportAction in layout.menuExportArmyAs.Actions()) { exportAction.Enabled = false; } layout.armyTree.SetModel(new QStandardItemModel()); DisableCategoryButtons(); } void DisableCategoryButtons () { //TODO handle category buttons } private void SetNonNullArmyState(Army newArmy) { SetCategoryButtons(newArmy.Race.Categories); EnableCategoryButtons(); layout.actionSaveArmyAs.Enabled = true; layout.actionCloseArmy.Enabled = true; layout.menuExportArmyAs.Enabled = true; foreach (QAction exportAction in layout.menuExportArmyAs.Actions()) { exportAction.Enabled = true; } FillArmyTree(newArmy); } private void FillArmyTree (Army army) { QStandardItemModel treeModel = new QStandardItemModel(); List<string> headers = new List<string>(); headers.Add(army.Name); treeModel.SetHorizontalHeaderLabels(headers); foreach (var category in army.Categories) { QStandardItem item = CreateTreeItem(category); treeModel.AppendRow(item); CreateUnitNodes(category, item); } layout.armyTree.SetModel(treeModel); } private QStandardItem CreateTreeItem(WarFoundryObject obj) { QVariant wrappedObject = QVariant.FromValue<WarFoundryObject>(obj); QStandardItem item = new QStandardItem(obj.Name); item.SetData(wrappedObject); return item; } private void CreateUnitNodes(ArmyCategory category, QStandardItem categoryItem) { foreach (Unit unit in category.GetUnits()) { CreateTreeSubItem(unit, categoryItem); } } private QStandardItem CreateTreeSubItem(WarFoundryObject obj, QStandardItem rootItem) { QStandardItem item = CreateTreeItem(obj); rootItem.AppendRow(item); return item; } void SetCategoryButtons (Category[] categories) { //TODO create category buttons } void EnableCategoryButtons () { //TODO enable category buttons } private void DoCloseArmy() { CloseCurrentArmy(); } private bool CloseCurrentArmy() { bool closed = false; if (CurrentArmy!=null) { log.Debug("Closing "+CurrentArmy.Name); bool canClose = false; if (CommandStack.IsDirty()) { 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; } log.Debug("Army "+(closed ? "" : "not")+" closed"); } else { //pretend we succeeded closed = true; } return closed; } public Preferences Preferences { get { return preferences; } set { preferences = value; } } private void DoExportBasicHTML() { log.Debug("Exporting to HTML"); QFileDialog fileDialog = new QFileDialog(this); fileDialog.acceptMode = QFileDialog.AcceptMode.AcceptSave; fileDialog.fileMode = QFileDialog.FileMode.AnyFile; fileDialog.SetDirectory(new QDir(Environment.GetFolderPath(Environment.SpecialFolder.Personal))); log.Debug("Requesting export path"); int result = fileDialog.Exec(); if (result == (int)QDialog.DialogCode.Accepted) { string path = fileDialog.SelectedFiles()[0]; log.DebugFormat("Exporting to {0}", path); WarFoundryHtmlExporter.GetDefault().ExportArmy(CurrentArmy, path); log.Debug("Exported"); } } } }