// This file (FrmMainWindow.cs) is a part of the IBBoard.WarFoundry.GTK project and is copyright 2008, 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.Collections; using System.Collections.Generic; using System.Configuration; using System.IO; using GLib; using Gtk; using IBBoard; using IBBoard.Commands; using IBBoard.CustomMath; using IBBoard.GtkSharp; using IBBoard.GtkSharp.Translatable; using IBBoard.IO; using IBBoard.Lang; using IBBoard.Logging; using IBBoard.WarFoundry.API; using IBBoard.WarFoundry.API.Commands; using IBBoard.WarFoundry.API.Exporters; using IBBoard.WarFoundry.API.Factories; using IBBoard.WarFoundry.API.Factories.Xml; using IBBoard.WarFoundry.API.Objects; using IBBoard.WarFoundry.API.Savers; using IBBoard.WarFoundry.GUI.GTK.Widgets; using IBBoard.Xml; using log4net; using WFObjects = IBBoard.WarFoundry.API.Objects; using IBBoard.WarFoundry.API.Savers.Xml; namespace IBBoard.WarFoundry.GUI.GTK { public partial class FrmMainWindow: TranslatableWindowWithActions { 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 unitToWidgetMap = new Dictionary(); private ObjectAddDelegate UnitAddedMethod; private ObjectRemoveDelegate UnitRemovedMethod; private DoubleValChangedDelegate PointsValueChangedMethod; private StringValChangedDelegate UnitNameChangedMethod; private GameSystem system; private string loadedArmyPath; private MenuToolButton undoMenuButton, redoMenuButton; public static void Main(string[] args) { try { ExceptionManager.UnhandledException += HandleUnhandledException; Application.Init(); FrmMainWindow win = new FrmMainWindow(args); win.Show(); Application.Run(); LogManager.GetLogger(typeof(FrmMainWindow)).Debug("Application ended"); } catch (Exception ex) { HandleUnhandledException(ex); } } private static void HandleUnhandledException(UnhandledExceptionArgs args) { object obj = args.ExceptionObject; Exception ex = null; if (obj is Exception) { ex = (Exception)obj; } else { ex = new Exception("GLib returned unexpected exception object type " + obj.GetType()); } HandleUnhandledException(ex); } private static void HandleUnhandledException(Exception ex) { string msg = String.Format("({0}) {1}", ex.GetType().FullName, ex.Message); LogManager.GetLogger(typeof(FrmMainWindow)).Fatal(msg, ex); MessageDialog dialog = new MessageDialog(null, DialogFlags.Modal, MessageType.Error, ButtonsType.Ok, false, "An unhandled exception occurred. Please check the log for more details."); dialog.Run(); dialog.Hide(); dialog.Dispose(); } public FrmMainWindow() : this(new string[0]) { //Do nothing extra } public FrmMainWindow(string[] args) : base(Gtk.WindowType.Toplevel) { logger.Info("Opening FrmMainWindow"); Build(); //Replace the undo/redo buttons with menu versions, which Monodevelop's GUI editor doesn't currently support redoMenuButton = new MenuToolButton("gtk-redo"); redoMenuButton.Name = "bttnRedo"; redoMenuButton.Clicked += redoTBButtonActivated; redoMenuButton.Sensitive = false; toolbar.Insert(redoMenuButton, CATEGORY_BUTTON_SEPARATOR_INDEX); undoMenuButton = new MenuToolButton("gtk-undo"); undoMenuButton.Name = "bttnUndo"; undoMenuButton.Clicked += undoTBButtonActivated; undoMenuButton.Sensitive = false; 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(); TreeViewColumn mainColumn = new TreeViewColumn(); 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("WarFoundry-GTK"); 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); Translation.TranslationChanged += Retranslate; Translate(); UnitAddedMethod = new ObjectAddDelegate(OnUnitAdded); UnitRemovedMethod = new ObjectRemoveDelegate(OnUnitRemoved); PointsValueChangedMethod = new DoubleValChangedDelegate(OnPointsValueChanged); UnitNameChangedMethod = new StringValChangedDelegate(OnUnitNameChanged); //FIXME: Temporary hack to add paths and factories before we get preferences and plugins WarFoundryLoader.GetDefault().AddLoadDirectory(new DirectoryInfo(Constants.ExecutablePath + Constants.DirectoryString + "data")); WarFoundryLoader.GetDefault().RegisterFactory(WarFoundryXmlFactory.GetFactory()); WarFoundryLoader.GetDefault().FileLoadingFinished += FileLoadingFinished; WarFoundrySaver.SetFileSaver(new WarFoundryXmlFileSaver()); logger.Debug("Initialising complete - seeing if we can load default army or system"); if (args.Length == 1) { logger.Debug("Attempting to load from file"); FileInfo file = new FileInfo(args[0]); try { ICollection objects = WarFoundryLoader.GetDefault().LoadFile(file); if (objects.Count == 1) { List objectList = new List(); 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); } } } } protected override void Translate() { base.Translate(); SetAppTitle(); treeUnits.GetColumn(0).Title = Translation.GetTranslation("armyCategoryColumnTitle", "categories"); RebuildUndoRedoMenus(); } private void Retranslate() { Translate(); } public override void Dispose() { Translation.TranslationChanged -= Retranslate; base.Dispose(); } private void FileLoadingFinished(List failures) { foreach (FileLoadFailure failure in failures) { logger.Warn("Failed to load " + failure.FailedFile.FullName + ": " + failure.Message); } } 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.Points); } else { name = Translation.GetTranslation("categoryTreeCatNamePercentage", "{0} - {1}pts ({2}%)", c.Name, c.Points, (c.ParentArmy.Points > 0 ? Math.Round((c.Points / c.ParentArmy.Points) * 100) : 0)); } (cell as CellRendererText).Text = name; } else { if (o is WFObjects.Unit) { WFObjects.Unit u = (WFObjects.Unit)o; string name = Translation.GetTranslation("unitTreeCatName", "{0} - {1}pts", u.Name, u.Points); (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) { WFObjects.Unit unit = (WFObjects.Unit)val; logger.DebugFormat("Unit name changed for {0} - now called {1}", unit.ID, unit.Name); UnitDisplayWidget widget; unitToWidgetMap.TryGetValue(unit, out widget); if (widget != null) { unitsNotebook.SetTabLabel(widget, NotebookUtil.CreateNotebookTabLabelWithClose(unitsNotebook, widget, unit.Name)); } treeUnits.QueueDraw(); } private void OnUnitAdded(WarFoundryObject val) { WFObjects.Unit unit = (WFObjects.Unit)val; unit.NameChanged += UnitNameChangedMethod; unit.PointsValueChanged += HandleUnitPointsValueChanged; AddUnitToTree(unit); } private void HandleUnitPointsValueChanged(WarFoundryObject obj, double oldValue, double newValue) { treeUnits.QueueDraw(); } private void AddUnitToTree(WFObjects.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) { WFObjects.Unit unit = (WFObjects.Unit)obj; unit.NameChanged -= UnitNameChangedMethod; RemoveUnitFromTree(unit); RemoveUnitTab(unit); } private void RemoveUnitFromTree(WFObjects.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 RemoveUnitTab(WFObjects.Unit unit) { UnitDisplayWidget widget = DictionaryUtils.GetValue(unitToWidgetMap, unit); if (widget != null) { unitsNotebook.Remove(widget); } } private void OnPointsValueChanged(WarFoundryObject obj, double before, double after) { SetPointsPanelText(); } public Preferences Preferences { get { return preferences; } set { preferences = value; } } 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 OnCreateArmyActivated(object sender, System.EventArgs e) { CreateNewArmy(); } protected virtual void OnReloadFilesActivated(object sender, System.EventArgs e) { WarFoundryLoader.GetDefault().LoadFiles(); } 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) { SaveCurrentArmyOrSaveAs(); } protected virtual void OnAddUnitActivated(object sender, System.EventArgs e) { if (sender is ToolButton) { ToolButton toolButton = (ToolButton)sender; Category cat = (Category)toolButton.Data["Category"]; 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.GetCategory(cat)); commandStack.Execute(cmd); ShowUnitWidget(cmd.Unit); } 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(); 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; } unitToWidgetMap.Clear(); while (unitsNotebook.NPages > 0) { unitsNotebook.RemovePage(0); } if (newArmy == null) { DisableCategoryButtons(); } else { newArmy.UnitAdded += UnitAddedMethod; newArmy.UnitRemoved += UnitRemovedMethod; newArmy.PointsValueChanged += PointsValueChangedMethod; //TODO: Clear all buttons EnableCategoryButtons(); if (newArmy.Race.HasCategoryOverrides()) { RemoveCategoryButtons(); AddCategoryButtons(newArmy.Race.Categories); } } bool nonNullNewArmy = (newArmy != null); miCloseArmy.Sensitive = nonNullNewArmy; miSaveArmyAs.Sensitive = nonNullNewArmy; miExportArmyAs.Sensitive = nonNullNewArmy; hpaned2.Visible = nonNullNewArmy; loadedArmyPath = null; //New army has no changes, so we can't save it miSaveArmy.Sensitive = false; bttnSaveArmy.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 (WFObjects.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]); } } 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 = Translation.GetTranslation("bttnCreateFromCat", "{0}", cat.Name); button.Data["Category"] = 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() { if (WarFoundryCore.CurrentArmy != null) { lblTotalPoints.Text = Translation.GetTranslation("statusPanelPoints", "{0}pts of {1} pts", WarFoundryCore.CurrentArmy.Points, WarFoundryCore.CurrentArmy.MaxPoints); } else { lblTotalPoints.Text = ""; } } private void commandStack_CommandStackUpdated() { undoMenuButton.Sensitive = commandStack.CanUndo(); miUndo.Sensitive = undoMenuButton.Sensitive; redoMenuButton.Sensitive = commandStack.CanRedo(); miRedo.Sensitive = redoMenuButton.Sensitive; RebuildUndoRedoMenus(); bttnSaveArmy.Sensitive = commandStack.IsDirty() && WarFoundryCore.CurrentArmy != null; miSaveArmy.Sensitive = commandStack.IsDirty() && WarFoundryCore.CurrentArmy != null; } private void RebuildUndoRedoMenus() { int redoLength = commandStack.RedoLength; int maxRedo = Math.Min(10, redoLength); if (redoLength > 0) { Menu menu = new Menu(); 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.Activated += new EventHandler(RedoMenuActivated); menu.Append(mi); } menu.ShowAll(); redoMenuButton.Menu = menu; } else { redoMenuButton.Menu = null; } int undoLength = commandStack.UndoLength; int maxUndo = Math.Min(10, undoLength); if (undoLength > 0) { Menu menu = new Menu(); 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.Activated += new EventHandler(UndoMenuActivated); menu.Add(mi); } menu.ShowAll(); undoMenuButton.Menu = menu; } else { undoMenuButton.Menu = null; } } private void RedoMenuActivated(object sender, EventArgs e) { if (sender is MenuItem) { MenuItem item = (MenuItem)sender; //we know it's an redo menu item so find it's index and redo everything int max = Arrays.IndexOf(((Menu)redoMenuButton.Menu).Children, item); if (max >= 0) { for (int i = 0; i <= max; i++) { commandStack.Redo(); } } } } private void UndoMenuActivated(object sender, EventArgs e) { if (sender is MenuItem) { MenuItem item = (MenuItem)sender; //we know it's an undo menu item so find it's index and undo everything int max = Arrays.IndexOf(((Menu)undoMenuButton.Menu).Children, item); if (max >= 0) { for (int i = 0; i <= max; i++) { commandStack.Undo(); } } } } private bool SaveCurrentArmyOrSaveAs() { if (loadedArmyPath != null) { return SaveCurrentArmy(); } else { return SaveCurrentArmyAs(); } } private bool OpenArmy() { string title = Translation.GetTranslation("openArmyDialog", "open army"); string cancelText = Translation.GetTranslation("bttnCancel", "cancel"); string openText = Translation.GetTranslation("bttnOpen", "open"); FileChooserDialog fileDialog = new FileChooserDialog(title, this, FileChooserAction.Open, cancelText, ResponseType.Cancel, openText, ResponseType.Accept); FileFilter filter = new FileFilter(); filter.AddPattern("*.army"); filter.Name = Translation.GetTranslation("armyFileFilter", "WarFoundry .army files"); fileDialog.AddFilter(filter); int response = fileDialog.Run(); string filePath = null; if (response == (int)ResponseType.Accept) { filePath = fileDialog.Filename; } fileDialog.Hide(); fileDialog.Dispose(); bool success = false; if (filePath != null) { FileInfo file = new FileInfo(filePath); Army army = null; try { army = WarFoundryLoader.GetDefault().LoadArmy(file); } catch (Exception ex) { logger.Error("Error while loading army file " + filePath, ex); } if (army != null) { logger.Debug("Loaded army " + army.ID); success = true; WarFoundryCore.CurrentArmy = army; loadedArmyPath = filePath; logger.Debug("Army loading complete"); } else { MessageDialog dialog = new MessageDialog(this, DialogFlags.Modal, MessageType.Error, ButtonsType.Ok, Translation.GetTranslation("OpenFailed", "File open failed. Please check log file")); dialog.Title = Translation.GetTranslation("OpenFailedTitle", "File open failed"); dialog.Run(); dialog.Hide(); dialog.Dispose(); } } else { logger.Debug("Army open requested but cancelled"); } return success; } private bool SaveCurrentArmy() { bool success = false; if (loadedArmyPath != null) { success = SaveArmyToPath(WarFoundryCore.CurrentArmy, loadedArmyPath); } return success; } private bool SaveCurrentArmyAs() { string title = Translation.GetTranslation("saveArmyDialog", "save army"); string cancelText = Translation.GetTranslation("bttnCancel", "cancel"); string saveText = Translation.GetTranslation("bttnSave", "save"); FileChooserDialog fileDialog = new FileChooserDialog(title, this, FileChooserAction.Save, cancelText, ResponseType.Cancel, saveText, ResponseType.Accept); FileFilter filter = new FileFilter(); filter.AddPattern("*.army"); filter.Name = Translation.GetTranslation("armyFileFilter", "WarFoundry .army files"); fileDialog.AddFilter(filter); int response = fileDialog.Run(); string filePath = null; if (response == (int)ResponseType.Accept) { filePath = fileDialog.Filename; } fileDialog.Hide(); fileDialog.Dispose(); return SaveArmyToPath(WarFoundryCore.CurrentArmy, filePath); } private bool SaveArmyToPath(Army army, string filePath) { bool success = false; if (filePath != null) { bool saveSuccess = false; try { saveSuccess = WarFoundrySaver.GetSaver().Save(filePath, WarFoundryCore.CurrentArmy); } catch (Exception ex) { logger.Error("Error while saving army file to " + filePath, ex); } if (saveSuccess) { miSaveArmy.Sensitive = false; bttnSaveArmy.Sensitive = false; CommandStack.setCleanMark(); loadedArmyPath = filePath; success = true; } else { MessageDialog dialog = new MessageDialog(this, DialogFlags.Modal, MessageType.Error, ButtonsType.Ok, Translation.GetTranslation("SaveFailed", "File save failed. Please check log file")); dialog.Title = Translation.GetTranslation("SaveFailedTitle", "File save failed"); dialog.Run(); dialog.Hide(); dialog.Dispose(); } } //else user cancelled return success; } private bool CloseCurrentArmy() { if (WarFoundryCore.CurrentArmy != null) { bool canClose = false; if (CommandStack.IsDirty()) { string message = Translation.GetTranslation("SaveChangesQuestion", "Save changes to army before closing?", WarFoundryCore.CurrentArmy.Name); MessageDialog dia = new MessageDialog(this, DialogFlags.DestroyWithParent, MessageType.Question, ButtonsType.None, message); dia.AddButton(Translation.GetTranslation("bttnDiscard", "lose changes"), ResponseType.No); Button button = (Button)dia.AddButton(Translation.GetTranslation("bttnCancel", "cancel"), ResponseType.Cancel); button.Image = new Image("gtk-cancel", IconSize.Button); button = (Button)dia.AddButton(Translation.GetTranslation("bttnSave", "save"), ResponseType.Yes); button.Image = new Image("gtk-save", IconSize.Button); dia.Title = Translation.GetTranslation("SaveChangesTitle", "Save changes?"); ResponseType dr = (ResponseType)dia.Run(); dia.Hide(); dia.Dispose(); 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; } } } 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(); } 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) { SaveCurrentArmyOrSaveAs(); } 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) { object obj = TreeUtils.GetItemAtPath(treeUnits, args.Path); if (obj is WFObjects.Unit) { WFObjects.Unit unit = (WFObjects.Unit)obj; ShowUnitWidget(unit); } } private void ShowUnitWidget(WFObjects.Unit unit) { 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); logger.Debug("Adding page for " + unit.Name); unitToWidgetMap[unit] = widget; int pageNum = NotebookUtil.AddPageToNotebookWithCloseButton(unitsNotebook, widget, unit.Name); logger.Debug("Page added at index " + pageNum); unitsNotebook.ShowAll(); unitsNotebook.CurrentPage = pageNum; unitsNotebook.SetTabReorderable(widget, true); } } protected virtual void OnMiExportAsBasicHtmlActivated(object sender, System.EventArgs e) { string exportArmyTitle = Translation.GetTranslation("exportBasicHtmlDialogTitle", "export army"); string cancelText = Translation.GetTranslation("exportBasicHtmlCancel", "cancel"); string exportText = Translation.GetTranslation("exportBasicHtmlExport", "export"); FileChooserDialog fileDialog = new FileChooserDialog(exportArmyTitle, this, FileChooserAction.Save, cancelText, ResponseType.Cancel, exportText, ResponseType.Accept); FileFilter filter = new FileFilter(); filter.AddPattern("*.html"); filter.Name = Translation.GetTranslation("exportBasicHtmlHtmlFilter", "HTML pages (*.html)"); fileDialog.AddFilter(filter); int response = fileDialog.Run(); string filePath = null; if (response == (int)ResponseType.Accept) { filePath = fileDialog.Filename; } fileDialog.Hide(); fileDialog.Dispose(); if (filePath != null) { Army army = WarFoundryCore.CurrentArmy; logger.DebugFormat("Exporting {0} to {1} as basic HTML", army.Name, filePath); WarFoundryHtmlExporter.GetDefault().ExportArmy(army, filePath); } } protected virtual void OnTreeUnitsPopupMenu(object o, Gtk.PopupMenuArgs args) { object selectedItem = TreeUtils.GetSelectedItem(treeUnits); if (selectedItem is WFObjects.Unit) { Menu menu = new Menu(); ImageMenuItem delete = new ImageMenuItem(Translation.GetTranslation("menuRemoveUnit", "remove unit")); delete.Image = new Gtk.Image(Stock.Delete, IconSize.Menu); delete.Activated += new EventHandler(OnUnitDelete); delete.Data["unit"] = selectedItem; menu.Append(delete); menu.ShowAll(); menu.Popup(); } } private void OnUnitDelete(object o, EventArgs args) { RemoveUnitCommand command = new RemoveUnitCommand((WFObjects.Unit)((ImageMenuItem)o).Data["unit"]); commandStack.Execute(command); } [GLib.ConnectBefore] protected virtual void UnitTreeButtonPressed(object o, Gtk.ButtonPressEventArgs args) { TreePath path; treeUnits.GetPathAtPos((int)args.Event.X, (int)args.Event.Y, out path); if (!treeUnits.Selection.PathIsSelected(path)) { treeUnits.Selection.SelectPath(path); } if (args.Event.Type == Gdk.EventType.ButtonPress && args.Event.Button == 3) { OnTreeUnitsPopupMenu(o, null); } } protected virtual void NotebookPageRemoved(object o, Gtk.RemovedArgs args) { Widget widget = args.Widget; if (widget is UnitDisplayWidget) { unitToWidgetMap.Remove(((UnitDisplayWidget)widget).Unit); } } protected virtual void HelpAboutActivated(object sender, System.EventArgs e) { FrmAbout form = FrmAbout.GetForm(); form.Run(); form.Hide(); } protected virtual void miPreferencesClicked(object sender, System.EventArgs e) { FrmPreferences form = new FrmPreferences(Preferences); form.Run(); form.Hide(); } public override ICollection Actions { get { List actions = new List(); foreach (ActionGroup actionGroup in this.UIManager.ActionGroups) { actions.AddRange(actionGroup.ListActions()); } return actions; } } } }