view FrmMainWindow.cs @ 110:76b73f15d07e

Re #344: Show unit requirement failures * First pass using new Requirement objects on FrmNewUnit - needs more testing and styling * Remove old references from main window until we get a replacement
author IBBoard <dev@ibboard.co.uk>
date Mon, 01 Aug 2011 20:22:48 +0000
parents b51935854587
children f58f69ea2435
line source
1 // This file (FrmMainWindow.cs) is a part of the IBBoard.WarFoundry.GTK project and is copyright 2008, 2009 IBBoard.
2 //
3 // 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.
5 using System;
6 using System.Collections;
7 using System.Collections.Generic;
8 using System.Configuration;
9 using System.IO;
10 using GLib;
11 using Gtk;
12 using IBBoard;
13 using IBBoard.Commands;
14 using IBBoard.CustomMath;
15 using IBBoard.GtkSharp;
16 using IBBoard.GtkSharp.Translatable;
17 using IBBoard.IO;
18 using IBBoard.Lang;
19 using IBBoard.Logging;
20 using IBBoard.WarFoundry.API;
21 using IBBoard.WarFoundry.API.Commands;
22 using IBBoard.WarFoundry.API.Exporters;
23 using IBBoard.WarFoundry.API.Factories;
24 using IBBoard.WarFoundry.API.Factories.Xml;
25 using IBBoard.WarFoundry.API.Objects;
26 using IBBoard.WarFoundry.API.Savers;
27 using IBBoard.WarFoundry.GUI.GTK.Widgets;
28 using IBBoard.Xml;
29 using log4net;
30 using WFObjects = IBBoard.WarFoundry.API.Objects;
31 using IBBoard.WarFoundry.API.Savers.Xml;
33 namespace IBBoard.WarFoundry.GUI.GTK
34 {
35 public partial class FrmMainWindow: TranslatableWindowWithActions
36 {
37 private static readonly string AppTitle = "WarFoundry";
38 private const int CATEGORY_BUTTON_SEPARATOR_INDEX = 6;
39 private Preferences preferences;
40 private ILog logger = LogManager.GetLogger(typeof(FrmMainWindow));
41 private CommandStack commandStack;
42 private Dictionary<WFObjects.Unit, UnitDisplayWidget> unitToWidgetMap = new Dictionary<WFObjects.Unit,UnitDisplayWidget>();
43 private ObjectAddDelegate UnitAddedMethod;
44 private ObjectRemoveDelegate UnitRemovedMethod;
45 private DoubleValChangedDelegate PointsValueChangedMethod;
46 private StringValChangedDelegate UnitNameChangedMethod;
47 private GameSystem system;
48 private string loadedArmyPath;
49 private MenuToolButton undoMenuButton, redoMenuButton;
51 public static void Main(string[] args)
52 {
53 try
54 {
55 ExceptionManager.UnhandledException += HandleUnhandledException;
56 Application.Init();
57 FrmMainWindow win = new FrmMainWindow(args);
58 win.Show();
59 Application.Run();
60 LogManager.GetLogger(typeof(FrmMainWindow)).Debug("Application ended");
61 }
62 catch (Exception ex)
63 {
64 HandleUnhandledException(ex);
65 }
66 }
68 private static void HandleUnhandledException(UnhandledExceptionArgs args)
69 {
70 object obj = args.ExceptionObject;
71 Exception ex = null;
73 if (obj is Exception)
74 {
75 ex = (Exception)obj;
76 }
77 else
78 {
79 ex = new Exception("GLib returned unexpected exception object type " + obj.GetType());
80 }
82 HandleUnhandledException(ex);
83 }
85 private static void HandleUnhandledException(Exception ex)
86 {
87 string msg = String.Format("({0}) {1}", ex.GetType().FullName, ex.Message);
88 LogManager.GetLogger(typeof(FrmMainWindow)).Fatal(msg, ex);
89 MessageDialog dialog = new MessageDialog(null, DialogFlags.Modal, MessageType.Error, ButtonsType.Ok, false, "An unhandled exception occurred. Please check the log for more details.");
90 dialog.Run();
91 dialog.Hide();
92 dialog.Dispose();
93 }
95 public FrmMainWindow() : this(new string[0])
96 {
97 //Do nothing extra
98 }
100 public FrmMainWindow(string[] args) : base(Gtk.WindowType.Toplevel)
101 {
102 logger.Info("Opening FrmMainWindow");
103 Build();
104 //Replace the undo/redo buttons with menu versions, which Monodevelop's GUI editor doesn't currently support
105 redoMenuButton = new MenuToolButton("gtk-redo");
106 redoMenuButton.Name = "bttnRedo";
107 redoMenuButton.Clicked += redoTBButtonActivated;
108 redoMenuButton.Sensitive = false;
109 toolbar.Insert(redoMenuButton, CATEGORY_BUTTON_SEPARATOR_INDEX);
110 undoMenuButton = new MenuToolButton("gtk-undo");
111 undoMenuButton.Name = "bttnUndo";
112 undoMenuButton.Clicked += undoTBButtonActivated;
113 undoMenuButton.Sensitive = false;
114 toolbar.Insert(undoMenuButton, CATEGORY_BUTTON_SEPARATOR_INDEX);
115 toolbar.Remove(toolbar.Children[CATEGORY_BUTTON_SEPARATOR_INDEX - 1]);
116 toolbar.Remove(toolbar.Children[CATEGORY_BUTTON_SEPARATOR_INDEX - 2]);
117 toolbar.ShowAll();
119 TreeViewColumn mainColumn = new TreeViewColumn();
120 CellRendererText mainCell = new CellRendererText();
121 mainColumn.PackStart(mainCell, true);
122 treeUnits.AppendColumn(mainColumn);
123 mainColumn.SetCellDataFunc(mainCell, new TreeCellDataFunc(RenderCategoryTreeObjectName));
124 treeUnits.Model = new TreeStore(typeof(WarFoundryObject));
125 logger.Debug("Loading preferences");
126 Preferences = new Preferences("WarFoundry-GTK");
127 logger.Debug("Loading translations");
129 try
130 {
131 Translation.InitialiseTranslations(Constants.ExecutablePath, Preferences["language"].ToString());
132 }
133 catch (TranslationLoadException ex)
134 {
135 logger.Error(ex);
136 MessageDialog dialog = new MessageDialog(this, DialogFlags.Modal, MessageType.Error, ButtonsType.Ok, ex.Message);
137 dialog.Title = "Translation loading failed";
138 dialog.Run();
139 dialog.Destroy();
140 }
142 logger.Debug("Initialising");
143 commandStack = new CommandStack();
144 commandStack.CommandStackUpdated += new MethodInvoker(commandStack_CommandStackUpdated);
145 WarFoundryCore.GameSystemChanged += new GameSystemChangedDelegate(OnGameSystemChanged);
146 WarFoundryCore.ArmyChanged += new ArmyChangedDelegate(OnArmyChanged);
147 Destroyed += new EventHandler(OnWindowDestroyed);
148 Translation.TranslationChanged += Retranslate;
149 Translate();
150 UnitAddedMethod = new ObjectAddDelegate(OnUnitAdded);
151 UnitRemovedMethod = new ObjectRemoveDelegate(OnUnitRemoved);
152 PointsValueChangedMethod = new DoubleValChangedDelegate(OnPointsValueChanged);
153 UnitNameChangedMethod = new StringValChangedDelegate(OnUnitNameChanged);
155 //FIXME: Temporary hack to add paths and factories before we get preferences and plugins
156 WarFoundryLoader.GetDefault().AddLoadDirectory(new DirectoryInfo(Constants.ExecutablePath + Constants.DirectoryString + "data"));
157 WarFoundryLoader.GetDefault().RegisterFactory(WarFoundryXmlFactory.GetFactory());
158 WarFoundryLoader.GetDefault().FileLoadingFinished += FileLoadingFinished;
159 WarFoundrySaver.SetFileSaver(new WarFoundryXmlFileSaver());
161 logger.Debug("Initialising complete - seeing if we can load default army or system");
163 if (args.Length == 1)
164 {
165 logger.Debug("Attempting to load from file");
166 FileInfo file = new FileInfo(args[0]);
168 try
169 {
170 ICollection<IWarFoundryObject> objects = WarFoundryLoader.GetDefault().LoadFile(file);
172 if (objects.Count == 1)
173 {
174 List<IWarFoundryObject> objectList = new List<IWarFoundryObject>();
175 objectList.AddRange(objects);
176 IWarFoundryObject loadedObject = objectList[0];
178 if (loadedObject is Army)
179 {
180 WarFoundryCore.CurrentArmy = (Army)loadedObject;
181 logger.InfoFormat("Loaded army from {0}", file.FullName);
182 }
183 else
184 {
185 if (loadedObject is GameSystem)
186 {
187 WarFoundryCore.CurrentGameSystem = (GameSystem)loadedObject;
188 logger.InfoFormat("Loaded game system from {0}", file.FullName);
189 }
191 }
192 }
193 }
194 catch (InvalidFileException ex)
195 {
196 //TODO: show error dialog
197 logger.Error(ex);
198 }
199 }
200 else
201 {
202 string gameSystemID = Preferences.GetStringProperty("currSystem");
204 if (gameSystemID != null && !"".Equals(gameSystemID))
205 {
206 logger.Debug("Attempting to load current game system from properties");
207 GameSystem sys = WarFoundryLoader.GetDefault().GetGameSystem(gameSystemID);
209 if (sys != null)
210 {
211 WarFoundryCore.CurrentGameSystem = sys;
212 logger.InfoFormat("Loaded game system {0} from properties", gameSystemID);
213 }
214 }
215 }
216 }
218 protected override void Translate()
219 {
220 base.Translate();
221 SetAppTitle();
222 treeUnits.GetColumn(0).Title = Translation.GetTranslation("armyCategoryColumnTitle", "categories");
223 RebuildUndoRedoMenus();
224 }
226 private void Retranslate()
227 {
228 Translate();
229 }
231 public override void Dispose()
232 {
233 Translation.TranslationChanged -= Retranslate;
234 base.Dispose();
235 }
237 private void FileLoadingFinished(List<FileLoadFailure> failures)
238 {
239 foreach (FileLoadFailure failure in failures)
240 {
241 logger.Warn("Failed to load " + failure.FailedFile.FullName + ": " + failure.Message);
242 }
243 }
245 private void RenderCategoryTreeObjectName(TreeViewColumn column, CellRenderer cell, TreeModel model, TreeIter iter)
246 {
247 object o = model.GetValue(iter, 0);
249 if (o is ArmyCategory)
250 {
251 ArmyCategory c = (ArmyCategory)o;
252 string name = "";
254 if (!Preferences.GetBooleanProperty("ShowCatPercentage"))
255 {
256 name = Translation.GetTranslation("categoryTreeCatName", "{0} - {1}pts", c.Name, c.Points);
257 }
258 else
259 {
260 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));
261 }
263 (cell as CellRendererText).Text = name;
264 }
265 else
266 {
267 if (o is WFObjects.Unit)
268 {
269 WFObjects.Unit u = (WFObjects.Unit)o;
270 string name = Translation.GetTranslation("unitTreeCatName", "{0} - {1}pts", u.Name, u.Points);
271 (cell as CellRendererText).Text = name;
272 }
274 }
275 }
277 private void OnWindowDestroyed(object source, EventArgs args)
278 {
279 logger.Info("Exiting");
280 Application.Quit();
281 }
283 private void OnUnitNameChanged(WarFoundryObject val, string oldValue, string newValue)
284 {
285 WFObjects.Unit unit = (WFObjects.Unit)val;
286 logger.DebugFormat("Unit name changed for {0} - now called {1}", unit.ID, unit.Name);
287 UnitDisplayWidget widget;
288 unitToWidgetMap.TryGetValue(unit, out widget);
290 if (widget != null)
291 {
292 unitsNotebook.SetTabLabel(widget, NotebookUtil.CreateNotebookTabLabelWithClose(unitsNotebook, widget, unit.Name));
293 }
295 treeUnits.QueueDraw();
296 }
298 private void OnUnitAdded(WarFoundryObject val)
299 {
300 WFObjects.Unit unit = (WFObjects.Unit)val;
301 unit.NameChanged += UnitNameChangedMethod;
302 unit.PointsValueChanged += HandleUnitPointsValueChanged;
303 AddUnitToTree(unit);
304 }
306 private void HandleUnitPointsValueChanged(WarFoundryObject obj, double oldValue, double newValue)
307 {
308 treeUnits.QueueDraw();
309 }
311 private void AddUnitToTree(WFObjects.Unit unit)
312 {
313 TreeStore model = (TreeStore)treeUnits.Model;
314 TreeIter iter;
315 model.GetIterFirst(out iter);
317 do
318 {
319 object obj = model.GetValue(iter, 0);
321 if (obj is ArmyCategory)
322 {
323 ArmyCategory cat = (ArmyCategory)obj;
325 if (cat.Equals(unit.Category))
326 {
327 model.AppendValues(iter, unit);
328 TreePath path = model.GetPath(iter);
329 treeUnits.ExpandToPath(path);
330 }
331 }
332 }
333 while (model.IterNext(ref iter));
334 }
336 private void OnUnitRemoved(WarFoundryObject obj)
337 {
338 WFObjects.Unit unit = (WFObjects.Unit)obj;
339 unit.NameChanged -= UnitNameChangedMethod;
340 RemoveUnitFromTree(unit);
341 RemoveUnitTab(unit);
342 }
344 private void RemoveUnitFromTree(WFObjects.Unit unit)
345 {
346 TreeStore model = (TreeStore)treeUnits.Model;
347 TreeIter iter;
348 model.GetIterFirst(out iter);
349 bool removed = false;
351 do
352 {
353 object obj = model.GetValue(iter, 0);
355 if (obj is ArmyCategory)
356 {
357 ArmyCategory cat = (ArmyCategory)obj;
359 if (unit.Category == null || cat.Equals(unit.Category))
360 {
361 TreeIter innerIter;
362 model.IterChildren(out innerIter, iter);
364 do
365 {
366 object innerObj = model.GetValue(innerIter, 0);
368 if (unit.Equals(innerObj))
369 {
370 model.Remove(ref innerIter);
371 removed = true;
372 break;
373 }
374 }
375 while (model.IterNext(ref innerIter));
377 if (removed)
378 {
379 break;
380 }
381 }
382 }
383 }
384 while (model.IterNext(ref iter));
385 }
387 private void RemoveUnitTab(WFObjects.Unit unit)
388 {
389 UnitDisplayWidget widget = DictionaryUtils.GetValue(unitToWidgetMap, unit);
391 if (widget != null)
392 {
393 unitsNotebook.Remove(widget);
394 }
395 }
397 private void OnPointsValueChanged(WarFoundryObject obj, double before, double after)
398 {
399 SetPointsPanelText();
400 }
402 public Preferences Preferences
403 {
404 get { return preferences; }
405 set { preferences = value; }
406 }
408 protected void OnDeleteEvent(object sender, DeleteEventArgs a)
409 {
410 Application.Quit();
411 a.RetVal = true;
412 }
414 protected virtual void OnExitActivated(object sender, System.EventArgs e)
415 {
416 Application.Quit();
417 }
419 protected virtual void OnCreateArmyActivated(object sender, System.EventArgs e)
420 {
421 CreateNewArmy();
422 }
424 protected virtual void OnReloadFilesActivated(object sender, System.EventArgs e)
425 {
426 WarFoundryLoader.GetDefault().LoadFiles();
427 }
429 protected virtual void OnSaveArmyAsActivated(object sender, System.EventArgs e)
430 {
431 SaveCurrentArmyAs();
432 }
434 protected virtual void OnCloseArmyActivated(object sender, System.EventArgs e)
435 {
436 CloseCurrentArmy();
437 }
439 protected virtual void OnOpenArmyActivated(object sender, System.EventArgs e)
440 {
441 OpenArmy();
442 }
444 protected virtual void OnSaveArmyActivated(object sender, System.EventArgs e)
445 {
446 SaveCurrentArmyOrSaveAs();
447 }
449 protected virtual void OnAddUnitActivated(object sender, System.EventArgs e)
450 {
451 if (sender is ToolButton)
452 {
453 ToolButton toolButton = (ToolButton)sender;
454 Category cat = (Category)toolButton.Data["Category"];
456 if (cat != null)
457 {
458 logger.DebugFormat("Show FrmNewUnit for {0}", cat.Name);
459 FrmNewUnit newUnit = new FrmNewUnit(WarFoundryCore.CurrentArmy.Race, cat, WarFoundryCore.CurrentArmy);
460 ResponseType response = (ResponseType)newUnit.Run();
461 newUnit.Hide();
463 if (response == ResponseType.Ok)
464 {
465 CreateAndAddUnitCommand cmd = new CreateAndAddUnitCommand(newUnit.SelectedUnit, WarFoundryCore.CurrentArmy.GetCategory(cat));
466 commandStack.Execute(cmd);
467 ShowUnitWidget(cmd.Unit);
468 }
470 newUnit.Dispose();
471 }
472 }
473 }
475 public CommandStack CommandStack
476 {
477 get { return commandStack; }
478 }
480 private void SetAppTitle()
481 {
482 if (WarFoundryCore.CurrentArmy != null)
483 {
484 Title = AppTitle + " - " + WarFoundryCore.CurrentGameSystem.Name + " - " + WarFoundryCore.CurrentArmy.Name;
485 }
486 else
487 {
488 if (WarFoundryCore.CurrentGameSystem != null)
489 {
490 Title = AppTitle + " - " + WarFoundryCore.CurrentGameSystem.Name;
491 }
492 else
493 {
494 Title = AppTitle;
495 }
497 }
498 }
500 private void OnGameSystemChanged(GameSystem oldSys, GameSystem newSys)
501 {
502 system = newSys;
503 SetAppTitle();
504 RemoveCategoryButtons();
506 if (system != null)
507 {
508 AddCategoryButtons(system.Categories);
509 }
510 }
512 private void OnArmyChanged(Army oldArmy, Army newArmy)
513 {
514 loadedArmyPath = null;
515 SetAppTitle();
516 SetArmyTree(newArmy);
518 if (oldArmy != null)
519 {
520 oldArmy.UnitAdded -= UnitAddedMethod;
521 oldArmy.UnitRemoved -= UnitRemovedMethod;
522 oldArmy.PointsValueChanged -= PointsValueChangedMethod;
523 }
525 unitToWidgetMap.Clear();
527 while (unitsNotebook.NPages > 0)
528 {
529 unitsNotebook.RemovePage(0);
530 }
532 if (newArmy == null)
533 {
534 DisableCategoryButtons();
535 }
536 else
537 {
538 newArmy.UnitAdded += UnitAddedMethod;
539 newArmy.UnitRemoved += UnitRemovedMethod;
540 newArmy.PointsValueChanged += PointsValueChangedMethod;
541 //TODO: Clear all buttons
542 EnableCategoryButtons();
544 if (newArmy.Race.HasCategoryOverrides())
545 {
546 RemoveCategoryButtons();
547 AddCategoryButtons(newArmy.Race.Categories);
548 }
549 }
551 bool nonNullNewArmy = (newArmy != null);
552 miCloseArmy.Sensitive = nonNullNewArmy;
553 miSaveArmyAs.Sensitive = nonNullNewArmy;
554 miExportArmyAs.Sensitive = nonNullNewArmy;
555 hpaned2.Visible = nonNullNewArmy;
556 loadedArmyPath = null;
557 //New army has no changes, so we can't save it
558 miSaveArmy.Sensitive = false;
559 bttnSaveArmy.Sensitive = false;
561 CommandStack.Reset();
562 SetPointsPanelText();
563 }
565 private void SetArmyTree(Army army)
566 {
567 logger.Debug("Resetting tree");
568 TreeStore store = (TreeStore)treeUnits.Model;
569 store.Clear();
570 TreeIter iter;
572 if (army != null)
573 {
574 logger.Debug("Loading in categories to tree");
576 foreach (ArmyCategory cat in army.Categories)
577 {
578 logger.DebugFormat("Append category {0}", cat.Name);
579 iter = store.AppendValues(cat);
581 foreach (WFObjects.Unit unit in cat.GetUnits())
582 {
583 store.AppendValues(iter, unit);
584 }
585 }
587 logger.Debug("Finished loading tree categories");
588 }
589 }
591 private void DisableCategoryButtons()
592 {
593 SetCategoryButtonsSensitive(false);
594 }
596 private void EnableCategoryButtons()
597 {
598 SetCategoryButtonsSensitive(true);
599 }
601 private void SetCategoryButtonsSensitive(bool state)
602 {
603 int toolbarButtonCount = toolbar.Children.Length - 1;
604 logger.Debug("Last button index: " + toolbarButtonCount);
606 for (int i = toolbarButtonCount; i > CATEGORY_BUTTON_SEPARATOR_INDEX; i--)
607 {
608 logger.DebugFormat("Setting button {0} state to {1}", i, state);
609 toolbar.Children[i].Sensitive = state;
610 }
611 }
613 private void RemoveCategoryButtons()
614 {
615 int toolbarButtonCount = toolbar.Children.Length - 1;
617 for (int i = toolbarButtonCount; i > CATEGORY_BUTTON_SEPARATOR_INDEX; i--)
618 {
619 toolbar.Remove(toolbar.Children[i]);
620 }
621 }
623 private void AddCategoryButtons(Category[] cats)
624 {
625 if (cats != null && cats.Length > 0)
626 {
627 logger.DebugFormat("Toolbar button count: {0}. Adding {1} categories.", toolbar.Children.Length, cats.Length);
629 foreach (Category cat in cats)
630 {
631 ToolButton button = new ToolButton("gtk-add");
632 button.Label = cat.Name;
633 button.TooltipText = Translation.GetTranslation("bttnCreateFromCat", "{0}", cat.Name);
634 button.Data["Category"] = cat;
635 button.Clicked += new System.EventHandler(OnAddUnitActivated);
636 toolbar.Insert(button, -1);
637 }
638 }
640 toolbar.Children[CATEGORY_BUTTON_SEPARATOR_INDEX].Visible = cats != null && cats.Length > 0;
642 toolbar.ShowAll();
643 }
645 private void SetPointsPanelText()
646 {
647 if (WarFoundryCore.CurrentArmy != null)
648 {
649 lblTotalPoints.Text = Translation.GetTranslation("statusPanelPoints", "{0}pts of {1} pts", WarFoundryCore.CurrentArmy.Points, WarFoundryCore.CurrentArmy.MaxPoints);
650 }
651 else
652 {
653 lblTotalPoints.Text = "";
654 }
655 }
657 private void commandStack_CommandStackUpdated()
658 {
659 undoMenuButton.Sensitive = commandStack.CanUndo();
660 miUndo.Sensitive = undoMenuButton.Sensitive;
661 redoMenuButton.Sensitive = commandStack.CanRedo();
662 miRedo.Sensitive = redoMenuButton.Sensitive;
664 RebuildUndoRedoMenus();
666 bttnSaveArmy.Sensitive = commandStack.IsDirty() && WarFoundryCore.CurrentArmy != null;
667 miSaveArmy.Sensitive = commandStack.IsDirty() && WarFoundryCore.CurrentArmy != null;
668 }
670 private void RebuildUndoRedoMenus()
671 {
672 int redoLength = commandStack.RedoLength;
673 int maxRedo = Math.Min(10, redoLength);
675 if (redoLength > 0)
676 {
677 Menu menu = new Menu();
678 Command com;
679 MenuItem mi;
681 for (int i = 0; i < maxRedo; i++)
682 {
683 com = commandStack.PeekRedoCommand(i + 1);
685 if (com == null)
686 {
687 break;
688 }
690 mi = new MenuItem(com.Description);
691 mi.Activated += new EventHandler(RedoMenuActivated);
692 menu.Append(mi);
693 }
695 menu.ShowAll();
696 redoMenuButton.Menu = menu;
697 }
698 else
699 {
700 redoMenuButton.Menu = null;
701 }
703 int undoLength = commandStack.UndoLength;
704 int maxUndo = Math.Min(10, undoLength);
706 if (undoLength > 0)
707 {
708 Menu menu = new Menu();
709 Command com;
710 MenuItem mi;
712 for (int i = 0; i < maxUndo; i++)
713 {
714 com = commandStack.PeekUndoCommand(i + 1);
716 if (com == null)
717 {
718 break;
719 }
721 mi = new MenuItem(com.UndoDescription);
722 mi.Activated += new EventHandler(UndoMenuActivated);
723 menu.Add(mi);
724 }
726 menu.ShowAll();
727 undoMenuButton.Menu = menu;
728 }
729 else
730 {
731 undoMenuButton.Menu = null;
732 }
733 }
735 private void RedoMenuActivated(object sender, EventArgs e)
736 {
737 if (sender is MenuItem)
738 {
739 MenuItem item = (MenuItem)sender;
740 //we know it's an redo menu item so find it's index and redo everything
742 int max = Arrays.IndexOf(((Menu)redoMenuButton.Menu).Children, item);
744 if (max >= 0)
745 {
746 for (int i = 0; i <= max; i++)
747 {
748 commandStack.Redo();
749 }
750 }
751 }
752 }
754 private void UndoMenuActivated(object sender, EventArgs e)
755 {
756 if (sender is MenuItem)
757 {
759 MenuItem item = (MenuItem)sender;
760 //we know it's an undo menu item so find it's index and undo everything
762 int max = Arrays.IndexOf(((Menu)undoMenuButton.Menu).Children, item);
764 if (max >= 0)
765 {
766 for (int i = 0; i <= max; i++)
767 {
768 commandStack.Undo();
769 }
770 }
771 }
772 }
774 private bool SaveCurrentArmyOrSaveAs()
775 {
776 if (loadedArmyPath != null)
777 {
778 return SaveCurrentArmy();
779 }
780 else
781 {
782 return SaveCurrentArmyAs();
783 }
784 }
786 private bool OpenArmy()
787 {
788 string title = Translation.GetTranslation("openArmyDialog", "open army");
789 string cancelText = Translation.GetTranslation("bttnCancel", "cancel");
790 string openText = Translation.GetTranslation("bttnOpen", "open");
791 FileChooserDialog fileDialog = new FileChooserDialog(title, this, FileChooserAction.Open, cancelText, ResponseType.Cancel, openText, ResponseType.Accept);
792 FileFilter filter = new FileFilter();
793 filter.AddPattern("*.army");
794 filter.Name = Translation.GetTranslation("armyFileFilter", "WarFoundry .army files");
795 fileDialog.AddFilter(filter);
796 int response = fileDialog.Run();
797 string filePath = null;
799 if (response == (int)ResponseType.Accept)
800 {
801 filePath = fileDialog.Filename;
802 }
804 fileDialog.Hide();
805 fileDialog.Dispose();
807 bool success = false;
809 if (filePath != null)
810 {
811 FileInfo file = new FileInfo(filePath);
812 Army army = null;
814 try
815 {
816 army = WarFoundryLoader.GetDefault().LoadArmy(file);
817 }
818 catch (Exception ex)
819 {
820 logger.Error("Error while loading army file " + filePath, ex);
821 }
823 if (army != null)
824 {
825 logger.Debug("Loaded army " + army.ID);
826 success = true;
827 WarFoundryCore.CurrentArmy = army;
828 loadedArmyPath = filePath;
829 logger.Debug("Army loading complete");
830 }
831 else
832 {
833 MessageDialog dialog = new MessageDialog(this, DialogFlags.Modal, MessageType.Error, ButtonsType.Ok, Translation.GetTranslation("OpenFailed", "File open failed. Please check log file"));
834 dialog.Title = Translation.GetTranslation("OpenFailedTitle", "File open failed");
835 dialog.Run();
836 dialog.Hide();
837 dialog.Dispose();
838 }
839 }
840 else
841 {
842 logger.Debug("Army open requested but cancelled");
843 }
845 return success;
846 }
848 private bool SaveCurrentArmy()
849 {
850 bool success = false;
852 if (loadedArmyPath != null)
853 {
854 success = SaveArmyToPath(WarFoundryCore.CurrentArmy, loadedArmyPath);
855 }
857 return success;
858 }
860 private bool SaveCurrentArmyAs()
861 {
862 string title = Translation.GetTranslation("saveArmyDialog", "save army");
863 string cancelText = Translation.GetTranslation("bttnCancel", "cancel");
864 string saveText = Translation.GetTranslation("bttnSave", "save");
865 FileChooserDialog fileDialog = new FileChooserDialog(title, this, FileChooserAction.Save, cancelText, ResponseType.Cancel, saveText, ResponseType.Accept);
866 FileFilter filter = new FileFilter();
867 filter.AddPattern("*.army");
868 filter.Name = Translation.GetTranslation("armyFileFilter", "WarFoundry .army files");
869 fileDialog.AddFilter(filter);
870 int response = fileDialog.Run();
871 string filePath = null;
873 if (response == (int)ResponseType.Accept)
874 {
875 filePath = fileDialog.Filename;
876 }
878 fileDialog.Hide();
879 fileDialog.Dispose();
881 return SaveArmyToPath(WarFoundryCore.CurrentArmy, filePath);
882 }
884 private bool SaveArmyToPath(Army army, string filePath)
885 {
886 bool success = false;
888 if (filePath != null)
889 {
890 bool saveSuccess = false;
892 try
893 {
894 saveSuccess = WarFoundrySaver.GetSaver().Save(filePath, WarFoundryCore.CurrentArmy);
895 }
896 catch (Exception ex)
897 {
898 logger.Error("Error while saving army file to " + filePath, ex);
899 }
901 if (saveSuccess)
902 {
903 miSaveArmy.Sensitive = false;
904 bttnSaveArmy.Sensitive = false;
905 CommandStack.setCleanMark();
906 loadedArmyPath = filePath;
907 success = true;
908 }
909 else
910 {
911 MessageDialog dialog = new MessageDialog(this, DialogFlags.Modal, MessageType.Error, ButtonsType.Ok, Translation.GetTranslation("SaveFailed", "File save failed. Please check log file"));
912 dialog.Title = Translation.GetTranslation("SaveFailedTitle", "File save failed");
913 dialog.Run();
914 dialog.Hide();
915 dialog.Dispose();
916 }
917 }
918 //else user cancelled
920 return success;
921 }
923 private bool CloseCurrentArmy()
924 {
925 if (WarFoundryCore.CurrentArmy != null)
926 {
927 bool canClose = false;
929 if (CommandStack.IsDirty())
930 {
931 string message = Translation.GetTranslation("SaveChangesQuestion", "Save changes to army before closing?", WarFoundryCore.CurrentArmy.Name);
932 MessageDialog dia = new MessageDialog(this, DialogFlags.DestroyWithParent, MessageType.Question, ButtonsType.None, message);
933 dia.AddButton(Translation.GetTranslation("bttnDiscard", "lose changes"), ResponseType.No);
934 Button button = (Button)dia.AddButton(Translation.GetTranslation("bttnCancel", "cancel"), ResponseType.Cancel);
935 button.Image = new Image("gtk-cancel", IconSize.Button);
936 button = (Button)dia.AddButton(Translation.GetTranslation("bttnSave", "save"), ResponseType.Yes);
937 button.Image = new Image("gtk-save", IconSize.Button);
938 dia.Title = Translation.GetTranslation("SaveChangesTitle", "Save changes?");
939 ResponseType dr = (ResponseType)dia.Run();
940 dia.Hide();
941 dia.Dispose();
943 if (dr == ResponseType.Yes)
944 {
945 //They want to save so try to save it or prompt for save as
946 //If they cancel the save as then assume they don't want to close
947 canClose = SaveCurrentArmyOrSaveAs();
948 }
949 else
950 {
951 if (dr == ResponseType.No)
952 {
953 //They don't care about their changes
954 canClose = true;
955 }
956 else
957 {
958 //Assume cancel or close with the X button
959 canClose = false;
960 }
962 }
963 }
964 else
965 {
966 //Nothing has changed so we can safely close
967 canClose = true;
968 }
970 if (canClose)
971 {
972 //do close
973 WarFoundryCore.CurrentArmy = null;
974 return true;
975 }
976 else
977 {
978 return false;
979 }
980 }
981 else
982 {
983 //pretend we succeeded
984 return true;
985 }
986 }
988 private void CreateNewArmy()
989 {
990 logger.Debug("Create new army");
991 FrmNewArmy newArmy = new FrmNewArmy(WarFoundryCore.CurrentGameSystem);
992 ResponseType type = (ResponseType)newArmy.Run();
993 newArmy.Hide();
995 if (type == ResponseType.Ok)
996 {
997 if (CloseCurrentArmy())
998 {
999 WarFoundryCore.CurrentArmy = new Army(newArmy.SelectedRace, newArmy.ArmyName, newArmy.ArmySize);
1002 else
1004 logger.Debug("Create new army cancelled");
1007 newArmy.Destroy();
1010 protected virtual void undoTBButtonActivated(object sender, System.EventArgs e)
1012 CommandStack.Undo();
1015 protected virtual void redoTBButtonActivated(object sender, System.EventArgs e)
1017 CommandStack.Redo();
1020 protected virtual void saveTBButtonActivated(object sender, System.EventArgs e)
1022 SaveCurrentArmyOrSaveAs();
1025 protected virtual void openTBButtonActivated(object sender, System.EventArgs e)
1027 OpenArmy();
1030 protected virtual void newTBButtonActivated(object sender, System.EventArgs e)
1032 CreateNewArmy();
1035 protected virtual void ArmyRowActivated(object o, Gtk.RowActivatedArgs args)
1037 object obj = TreeUtils.GetItemAtPath(treeUnits, args.Path);
1039 if (obj is WFObjects.Unit)
1041 WFObjects.Unit unit = (WFObjects.Unit)obj;
1042 ShowUnitWidget(unit);
1046 private void ShowUnitWidget(WFObjects.Unit unit)
1048 UnitDisplayWidget widget;
1049 unitToWidgetMap.TryGetValue(unit, out widget);
1051 if (widget != null)
1053 logger.DebugFormat("Selecting existing page for " + unit.Name);
1054 unitsNotebook.Page = unitsNotebook.PageNum(widget);
1056 else
1058 widget = new UnitDisplayWidget(unit, CommandStack);
1059 logger.Debug("Adding page for " + unit.Name);
1060 unitToWidgetMap[unit] = widget;
1061 int pageNum = NotebookUtil.AddPageToNotebookWithCloseButton(unitsNotebook, widget, unit.Name);
1062 logger.Debug("Page added at index " + pageNum);
1063 unitsNotebook.ShowAll();
1064 unitsNotebook.CurrentPage = pageNum;
1065 unitsNotebook.SetTabReorderable(widget, true);
1069 protected virtual void OnMiExportAsBasicHtmlActivated(object sender, System.EventArgs e)
1071 string exportArmyTitle = Translation.GetTranslation("exportBasicHtmlDialogTitle", "export army");
1072 string cancelText = Translation.GetTranslation("exportBasicHtmlCancel", "cancel");
1073 string exportText = Translation.GetTranslation("exportBasicHtmlExport", "export");
1074 FileChooserDialog fileDialog = new FileChooserDialog(exportArmyTitle, this, FileChooserAction.Save, cancelText, ResponseType.Cancel, exportText, ResponseType.Accept);
1075 FileFilter filter = new FileFilter();
1076 filter.AddPattern("*.html");
1077 filter.Name = Translation.GetTranslation("exportBasicHtmlHtmlFilter", "HTML pages (*.html)");
1078 fileDialog.AddFilter(filter);
1079 int response = fileDialog.Run();
1080 string filePath = null;
1082 if (response == (int)ResponseType.Accept)
1084 filePath = fileDialog.Filename;
1087 fileDialog.Hide();
1088 fileDialog.Dispose();
1090 if (filePath != null)
1092 Army army = WarFoundryCore.CurrentArmy;
1093 logger.DebugFormat("Exporting {0} to {1} as basic HTML", army.Name, filePath);
1094 WarFoundryHtmlExporter.GetDefault().ExportArmy(army, filePath);
1098 protected virtual void OnTreeUnitsPopupMenu(object o, Gtk.PopupMenuArgs args)
1100 object selectedItem = TreeUtils.GetSelectedItem(treeUnits);
1102 if (selectedItem is WFObjects.Unit)
1104 Menu menu = new Menu();
1105 ImageMenuItem delete = new ImageMenuItem(Translation.GetTranslation("menuRemoveUnit", "remove unit"));
1106 delete.Image = new Gtk.Image(Stock.Delete, IconSize.Menu);
1107 delete.Activated += new EventHandler(OnUnitDelete);
1108 delete.Data["unit"] = selectedItem;
1109 menu.Append(delete);
1110 menu.ShowAll();
1111 menu.Popup();
1115 private void OnUnitDelete(object o, EventArgs args)
1117 RemoveUnitCommand command = new RemoveUnitCommand((WFObjects.Unit)((ImageMenuItem)o).Data["unit"]);
1118 commandStack.Execute(command);
1121 [GLib.ConnectBefore]
1123 protected virtual void UnitTreeButtonPressed(object o, Gtk.ButtonPressEventArgs args)
1125 TreePath path;
1126 treeUnits.GetPathAtPos((int)args.Event.X, (int)args.Event.Y, out path);
1128 if (!treeUnits.Selection.PathIsSelected(path))
1130 treeUnits.Selection.SelectPath(path);
1133 if (args.Event.Type == Gdk.EventType.ButtonPress && args.Event.Button == 3)
1135 OnTreeUnitsPopupMenu(o, null);
1139 protected virtual void NotebookPageRemoved(object o, Gtk.RemovedArgs args)
1141 Widget widget = args.Widget;
1143 if (widget is UnitDisplayWidget)
1145 unitToWidgetMap.Remove(((UnitDisplayWidget)widget).Unit);
1149 protected virtual void HelpAboutActivated(object sender, System.EventArgs e)
1151 FrmAbout form = FrmAbout.GetForm();
1152 form.Run();
1153 form.Hide();
1156 protected virtual void miPreferencesClicked(object sender, System.EventArgs e)
1158 FrmPreferences form = new FrmPreferences(Preferences);
1159 form.Run();
1160 form.Hide();
1163 public override ICollection<Gtk.Action> Actions
1165 get
1167 List<Gtk.Action> actions = new List<Gtk.Action>();
1169 foreach (ActionGroup actionGroup in this.UIManager.ActionGroups)
1171 actions.AddRange(actionGroup.ListActions());
1174 return actions;