Index: Application/Ringtoets/src/Application.Ringtoets/App.xaml.cs =================================================================== diff -u -r8facffc48a20cd49759e32aea1ecc100047c68a2 -r6ef8fa312a72f486be984ebc3adde4be044811e2 --- Application/Ringtoets/src/Application.Ringtoets/App.xaml.cs (.../App.xaml.cs) (revision 8facffc48a20cd49759e32aea1ecc100047c68a2) +++ Application/Ringtoets/src/Application.Ringtoets/App.xaml.cs (.../App.xaml.cs) (revision 6ef8fa312a72f486be984ebc3adde4be044811e2) @@ -69,7 +69,7 @@ private static readonly ILog log = LogManager.GetLogger(typeof(App)); - private static RingtoetsGui gui; + private static GuiCore gui; private static int waitForProcessId = -1; private static string fileToOpen = String.Empty; @@ -153,7 +153,7 @@ ManualFilePath = "Ringtoets_Manual.pdf" }; var mainWindow = new MainWindow(); - gui = new RingtoetsGui(mainWindow, new StorageSqLite(), applicationCore, settings) + gui = new GuiCore(mainWindow, new StorageSqLite(), applicationCore, settings) { Plugins = { Index: Application/Ringtoets/test/Application.Ringtoets.Storage.Test/IntegrationTests/StorageSqLiteIntegrationTest.cs =================================================================== diff -u -r295ad67e03b81842c18b7688a4c80ab410ddd6d1 -r6ef8fa312a72f486be984ebc3adde4be044811e2 --- Application/Ringtoets/test/Application.Ringtoets.Storage.Test/IntegrationTests/StorageSqLiteIntegrationTest.cs (.../StorageSqLiteIntegrationTest.cs) (revision 295ad67e03b81842c18b7688a4c80ab410ddd6d1) +++ Application/Ringtoets/test/Application.Ringtoets.Storage.Test/IntegrationTests/StorageSqLiteIntegrationTest.cs (.../StorageSqLiteIntegrationTest.cs) (revision 6ef8fa312a72f486be984ebc3adde4be044811e2) @@ -110,7 +110,7 @@ var testFile = Path.Combine(testDataPath, "ValidRingtoetsDatabase.rtd"); var projectStore = new StorageSqLite(); - using (var gui = new RingtoetsGui(new MainWindow(), projectStore, new ApplicationCore(), new GuiCoreSettings())) + using (var gui = new GuiCore(new MainWindow(), projectStore, new ApplicationCore(), new GuiCoreSettings())) { // Call Action action = () => gui.Run(testFile); @@ -138,7 +138,7 @@ var testFile = "SomeFile"; var projectStore = new StorageSqLite(); - using (var gui = new RingtoetsGui(new MainWindow(), projectStore, new ApplicationCore(), new GuiCoreSettings())) + using (var gui = new GuiCore(new MainWindow(), projectStore, new ApplicationCore(), new GuiCoreSettings())) { // Call Action action = () => gui.Run(testFile); @@ -170,7 +170,7 @@ // Setup var projectStore = new StorageSqLite(); - using (var gui = new RingtoetsGui(new MainWindow(), projectStore, new ApplicationCore(), new GuiCoreSettings())) + using (var gui = new GuiCore(new MainWindow(), projectStore, new ApplicationCore(), new GuiCoreSettings())) { // Call Action action = () => gui.Run(testFile); Index: Core/Common/src/Core.Common.Gui/Core.Common.Gui.csproj =================================================================== diff -u -r5c6c1168c657e4be77fa2e7a2f3ed906675a1d9c -r6ef8fa312a72f486be984ebc3adde4be044811e2 --- Core/Common/src/Core.Common.Gui/Core.Common.Gui.csproj (.../Core.Common.Gui.csproj) (revision 5c6c1168c657e4be77fa2e7a2f3ed906675a1d9c) +++ Core/Common/src/Core.Common.Gui/Core.Common.Gui.csproj (.../Core.Common.Gui.csproj) (revision 6ef8fa312a72f486be984ebc3adde4be044811e2) @@ -161,7 +161,7 @@ - + @@ -183,7 +183,7 @@ - + Index: Core/Common/src/Core.Common.Gui/Forms/MainWindow/IMainWindow.cs =================================================================== diff -u -r4512af7782ee31b36941bb280b54d9da2953dd71 -r6ef8fa312a72f486be984ebc3adde4be044811e2 --- Core/Common/src/Core.Common.Gui/Forms/MainWindow/IMainWindow.cs (.../IMainWindow.cs) (revision 4512af7782ee31b36941bb280b54d9da2953dd71) +++ Core/Common/src/Core.Common.Gui/Forms/MainWindow/IMainWindow.cs (.../IMainWindow.cs) (revision 6ef8fa312a72f486be984ebc3adde4be044811e2) @@ -19,7 +19,6 @@ // Stichting Deltares and remain full property of Stichting Deltares at all times. // All rights reserved. -using System; using System.Windows.Forms; using Core.Common.Gui.Forms.MessageWindow; Index: Core/Common/src/Core.Common.Gui/Forms/MainWindow/MainWindow.xaml =================================================================== diff -u -rbffa029f6badebf2eb97472a50a7c305c8fb2ae5 -r6ef8fa312a72f486be984ebc3adde4be044811e2 --- Core/Common/src/Core.Common.Gui/Forms/MainWindow/MainWindow.xaml (.../MainWindow.xaml) (revision bffa029f6badebf2eb97472a50a7c305c8fb2ae5) +++ Core/Common/src/Core.Common.Gui/Forms/MainWindow/MainWindow.xaml (.../MainWindow.xaml) (revision 6ef8fa312a72f486be984ebc3adde4be044811e2) @@ -160,7 +160,7 @@ - + Index: Core/Common/src/Core.Common.Gui/Forms/MainWindow/MainWindow.xaml.cs =================================================================== diff -u -r8facffc48a20cd49759e32aea1ecc100047c68a2 -r6ef8fa312a72f486be984ebc3adde4be044811e2 --- Core/Common/src/Core.Common.Gui/Forms/MainWindow/MainWindow.xaml.cs (.../MainWindow.xaml.cs) (revision 8facffc48a20cd49759e32aea1ecc100047c68a2) +++ Core/Common/src/Core.Common.Gui/Forms/MainWindow/MainWindow.xaml.cs (.../MainWindow.xaml.cs) (revision 6ef8fa312a72f486be984ebc3adde4be044811e2) @@ -604,8 +604,6 @@ } } - private void OnLayoutRootPropertyChanged(object sender, PropertyChangedEventArgs e) {} - private void UpdateToolWindowButtonState() { if (toolViewController.ToolWindowViews != null) Index: Core/Common/src/Core.Common.Gui/Forms/MessageWindow/MessageWindowDialog.cs =================================================================== diff -u -r4512af7782ee31b36941bb280b54d9da2953dd71 -r6ef8fa312a72f486be984ebc3adde4be044811e2 --- Core/Common/src/Core.Common.Gui/Forms/MessageWindow/MessageWindowDialog.cs (.../MessageWindowDialog.cs) (revision 4512af7782ee31b36941bb280b54d9da2953dd71) +++ Core/Common/src/Core.Common.Gui/Forms/MessageWindow/MessageWindowDialog.cs (.../MessageWindowDialog.cs) (revision 6ef8fa312a72f486be984ebc3adde4be044811e2) @@ -26,14 +26,14 @@ namespace Core.Common.Gui.Forms.MessageWindow { /// - /// Class for showing a message dialog. + /// Dialog for showing a particular message. /// public partial class MessageWindowDialog : DialogBase { /// /// Constructs a new . /// - /// The owner of the dialog. + /// The owner of the dialog, for which this should show on top. /// The text to show in the dialog. public MessageWindowDialog(IWin32Window dialogParent, string text) : base(dialogParent, Resources.application_import_blue1, 200, 150) { Index: Core/Common/src/Core.Common.Gui/Forms/ViewManager/AvalonDockDockingManager.cs =================================================================== diff -u -r4512af7782ee31b36941bb280b54d9da2953dd71 -r6ef8fa312a72f486be984ebc3adde4be044811e2 --- Core/Common/src/Core.Common.Gui/Forms/ViewManager/AvalonDockDockingManager.cs (.../AvalonDockDockingManager.cs) (revision 4512af7782ee31b36941bb280b54d9da2953dd71) +++ Core/Common/src/Core.Common.Gui/Forms/ViewManager/AvalonDockDockingManager.cs (.../AvalonDockDockingManager.cs) (revision 6ef8fa312a72f486be984ebc3adde4be044811e2) @@ -43,6 +43,9 @@ namespace Core.Common.Gui.Forms.ViewManager { + /// + /// Provides view docking management based on the 'Avalon Docking framework'. + /// public class AvalonDockDockingManager : IDockingManager { public event EventHandler ViewBarClosing; @@ -52,17 +55,22 @@ public event Action ViewSelectionMouseDown; private readonly DockingManager dockingManager; - private readonly ViewLocation[] dockingLocations; + private readonly ViewLocation[] supportedDockingLocations; private readonly List views; private IView activeView; private readonly List hostControls; - public AvalonDockDockingManager(DockingManager dockingManager, ViewLocation[] dockingLocations) + /// + /// Initializes a new instance of the class. + /// + /// The Avalon docking manager. + /// The supported docking locations. + public AvalonDockDockingManager(DockingManager dockingManager, ViewLocation[] supportedDockingLocations) { this.dockingManager = dockingManager; - this.dockingLocations = dockingLocations; + this.supportedDockingLocations = supportedDockingLocations; views = new List(); hostControls = new List(); @@ -151,7 +159,7 @@ views.Add(view); - if (dockingLocations.Contains(ViewLocation.Document)) + if (supportedDockingLocations.Contains(ViewLocation.Document)) { AddDocumentView(view); } @@ -234,7 +242,7 @@ return; } - if (dockingLocations.Contains(ViewLocation.Document)) + if (supportedDockingLocations.Contains(ViewLocation.Document)) { var layoutDocument = dockingManager.Layout.Descendents().OfType().FirstOrDefault(a => ContainsView(a, view)); Index: Core/Common/src/Core.Common.Gui/Forms/ViewManager/ViewSelectionContextMenuController.cs =================================================================== diff -u -r295ad67e03b81842c18b7688a4c80ab410ddd6d1 -r6ef8fa312a72f486be984ebc3adde4be044811e2 --- Core/Common/src/Core.Common.Gui/Forms/ViewManager/ViewSelectionContextMenuController.cs (.../ViewSelectionContextMenuController.cs) (revision 295ad67e03b81842c18b7688a4c80ab410ddd6d1) +++ Core/Common/src/Core.Common.Gui/Forms/ViewManager/ViewSelectionContextMenuController.cs (.../ViewSelectionContextMenuController.cs) (revision 6ef8fa312a72f486be984ebc3adde4be044811e2) @@ -25,11 +25,17 @@ namespace Core.Common.Gui.Forms.ViewManager { + /// + /// This class provides a custom context menu for views hosted inside a docking manager. + /// public partial class ViewSelectionContextMenuController : UserControl { private IView selectedView; private IViewList viewManager; + /// + /// Initializes a new instance of the class. + /// public ViewSelectionContextMenuController() { InitializeComponent(); @@ -38,6 +44,12 @@ public new ContextMenuStrip ContextMenuStrip { get; private set; } + /// + /// Refreshes the context menu. + /// + /// The view for which the context menu is shown. + /// The view manager. + /// true if the context menu can be used, false otherwise. public bool ContextMenuStripValidate(IView view, IViewList viewManager) { this.viewManager = viewManager; Index: Core/Common/src/Core.Common.Gui/Forms/ViewManager/ViewSelectionMouseController.cs =================================================================== diff -u -r4512af7782ee31b36941bb280b54d9da2953dd71 -r6ef8fa312a72f486be984ebc3adde4be044811e2 --- Core/Common/src/Core.Common.Gui/Forms/ViewManager/ViewSelectionMouseController.cs (.../ViewSelectionMouseController.cs) (revision 4512af7782ee31b36941bb280b54d9da2953dd71) +++ Core/Common/src/Core.Common.Gui/Forms/ViewManager/ViewSelectionMouseController.cs (.../ViewSelectionMouseController.cs) (revision 6ef8fa312a72f486be984ebc3adde4be044811e2) @@ -26,14 +26,22 @@ namespace Core.Common.Gui.Forms.ViewManager { + /// + /// Controller for handling context menu logic, taking the docking manager into account. + /// public class ViewSelectionMouseController { - private readonly ViewSelectionContextMenuController contextMenuController; // expose when necessary + private readonly ViewSelectionContextMenuController contextMenuController; + private readonly IViewList viewManager; + /// + /// Initializes a new instance of the class. + /// + /// The docking manager. + /// The view list. public ViewSelectionMouseController(IDockingManager dockingManager, IViewList viewList) { - DockingManager = dockingManager; - ViewManager = viewList; + viewManager = viewList; dockingManager.ViewSelectionMouseDown += OnViewSelectionMouseDown; @@ -42,13 +50,9 @@ viewList.ActiveViewChanged += ViewManagerActiveViewChanged; } - public IDockingManager DockingManager { get; private set; } - - public IViewList ViewManager { get; set; } - private void ViewManagerActiveViewChanged(object sender, ActiveViewChangeEventArgs e) { - contextMenuController.ContextMenuStripValidate(null, ViewManager); + contextMenuController.ContextMenuStripValidate(null, viewManager); } private void OnViewSelectionMouseDown(object sender, MouseEventArgs e, IView selectedView) @@ -58,14 +62,14 @@ throw new ArgumentException(Resources.ViewSelectionMouseController_OnViewSelectionMouseDown_Sender_must_be_non_null_and_of_type_Control); } - if (!ViewManager.Contains(selectedView)) + if (!viewManager.Contains(selectedView)) { return; //View is not in our ViewList, don't handle this } if (e.Button == MouseButtons.Right) { - if (contextMenuController.ContextMenuStripValidate(selectedView, ViewManager)) + if (contextMenuController.ContextMenuStripValidate(selectedView, viewManager)) { contextMenuController.ContextMenuStrip.Show((sender as Control).PointToScreen(e.Location)); } Index: Core/Common/src/Core.Common.Gui/GuiCore.cs =================================================================== diff -u --- Core/Common/src/Core.Common.Gui/GuiCore.cs (revision 0) +++ Core/Common/src/Core.Common.Gui/GuiCore.cs (revision 6ef8fa312a72f486be984ebc3adde4be044811e2) @@ -0,0 +1,1058 @@ +// Copyright (C) Stichting Deltares 2016. All rights reserved. +// +// This file is part of Ringtoets. +// +// Ringtoets is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see . +// +// All names, logos, and references to "Deltares" are registered trademarks of +// Stichting Deltares and remain full property of Stichting Deltares at all times. +// All rights reserved. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.ComponentModel; +using System.Configuration; +using System.Diagnostics; +using System.Linq; +using System.Reflection; +using System.Windows; +using Core.Common.Base.Data; +using Core.Common.Base.Plugin; +using Core.Common.Base.Storage; +using Core.Common.Controls.TreeView; +using Core.Common.Controls.Views; +using Core.Common.Gui.Commands; +using Core.Common.Gui.ContextMenu; +using Core.Common.Gui.Forms; +using Core.Common.Gui.Forms.MainWindow; +using Core.Common.Gui.Forms.MessageWindow; +using Core.Common.Gui.Forms.PropertyGridView; +using Core.Common.Gui.Forms.ViewManager; +using Core.Common.Gui.Plugin; +using Core.Common.Gui.Properties; +using Core.Common.Gui.Selection; +using Core.Common.Gui.Settings; +using Core.Common.Utils.Events; +using Core.Common.Utils.Extensions; +using Core.Common.Utils.Reflection; +using log4net; +using log4net.Appender; +using log4net.Repository.Hierarchy; +using SplashScreen = Core.Common.Gui.Forms.SplashScreen.SplashScreen; +using WindowsApplication = System.Windows.Forms.Application; + +namespace Core.Common.Gui +{ + /// + /// Gui class provides graphical user functionality for the application. + /// + public class GuiCore : IGui + { + private static readonly ILog log = LogManager.GetLogger(typeof(GuiCore)); + + private static bool isAlreadyRunningInstanceOfIGui; + private static string instanceCreationStackTrace; + + private AvalonDockDockingManager toolWindowViewsDockingManager; + + private SplashScreen splashScreen; + + private bool runFinished; + private bool isExiting; + + /// + /// Initializes a new instance of the class. + /// + /// The main window. + /// The project store. + /// The application core. + /// The fixed settings. + /// When another + /// instance is running. + /// When any parameter is null. + public GuiCore(IMainWindow mainWindow, IStoreProject projectStore, ApplicationCore applicationCore, GuiCoreSettings fixedSettings) + { + // error detection code, make sure we use only a single instance of RingtoetsGui at a time + if (isAlreadyRunningInstanceOfIGui) + { + isAlreadyRunningInstanceOfIGui = false; // reset to that the consecutive creations won't fail. + throw new InvalidOperationException(Resources.RingtoetsGui_Only_a_single_instance_of_Ringtoets_is_allowed_at_the_same_time_per_process_Make_sure_that_the_previous_instance_was_disposed_correctly_stack_trace + instanceCreationStackTrace); + } + + if (mainWindow == null) + { + throw new ArgumentNullException("mainWindow"); + } + if (projectStore == null) + { + throw new ArgumentNullException("projectStore"); + } + if (applicationCore == null) + { + throw new ArgumentNullException("applicationCore"); + } + if (fixedSettings == null) + { + throw new ArgumentNullException("fixedSettings"); + } + MainWindow = mainWindow; + Storage = projectStore; + ApplicationCore = applicationCore; + FixedSettings = fixedSettings; + + isAlreadyRunningInstanceOfIGui = true; + instanceCreationStackTrace = new StackTrace().ToString(); + + Plugins = new List(); + + UserSettings = Properties.Settings.Default; + + viewCommandHandler = new ViewCommandHandler(this, this, this, this); + storageCommandHandler = new StorageCommandHandler(projectStore, this, this, this, this, viewCommandHandler); + exportImportCommandHandler = new ExportImportCommandHandler(MainWindow, ApplicationCore); + projectCommandsHandler = new ProjectCommandsHandler(this, MainWindow, ApplicationCore, this, this); + + WindowsApplication.EnableVisualStyles(); + ViewPropertyEditor.ViewCommands = ViewCommands; + + ProjectOpened += ApplicationProjectOpened; + } + + public ApplicationCore ApplicationCore { get; private set; } + + public IPropertyResolver PropertyResolver { get; private set; } + + public IStoreProject Storage { get; private set; } + + public void Dispose() + { + Dispose(true); + } + + public void Run() + { + Run(null); + } + + public void Run(string projectPath) + { + var startTime = DateTime.Now; + + ConfigureLogging(); + + ShowSplashScreen(); + + log.Info(Resources.RingtoetsGui_Run_Starting_application); + + InitializeProjectFromPath(projectPath); + + log.Info(Resources.RingtoetsGui_Run_Initializing_graphical_user_interface); + + Initialize(); + + log.InfoFormat(Resources.RingtoetsGui_Run_Started_in_0_f2_sec, (DateTime.Now - startTime).TotalSeconds); + + runFinished = true; + + HideSplashScreen(); + + MessageWindowLogAppender.Instance.Enabled = true; + } + + public void Exit() + { + if (isExiting) + { + return; //already got here before + } + + isExiting = true; + + CopyDefaultViewsToUserSettings(); + + mainWindow.ClearDocumentTabs(); + + mainWindow.SaveLayout(); // save before ApplicationCore.Exit + + if (userSettingsDirty) + { + UserSettings.Save(); + } + + UserSettings = null; + + // close faster (hide main window) + mainWindow.Visible = false; + + if (Application.Current != null) + { + Application.Current.Shutdown(); + } + } + + #region Implementation: IContextMenuBuilderProvider + + public IContextMenuBuilder Get(object dataObject, TreeViewControl treeViewControl) + { + if (applicationFeatureCommands == null) + { + throw new InvalidOperationException("Call IGui.Run in order to initialize dependencies before getting the ContextMenuBuilder."); + } + return new ContextMenuBuilder(applicationFeatureCommands, exportImportCommandHandler, ViewCommands, dataObject, treeViewControl); + } + + #endregion + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + if (Plugins != null) + { + foreach (var plugin in Plugins.ToArray()) + { + DeactivatePlugin(plugin); + } + } + + isExiting = true; + + if (mainWindow != null) + { + mainWindow.UnsubscribeFromGui(); + } + + Selection = null; + + Project = null; + + if (ToolWindowViews != null) + { + ToolWindowViews.CollectionChanged -= ToolWindowViewsOnCollectionChanged; + ToolWindowViews.Clear(); + ToolWindowViews.Dispose(); + ToolWindowViews = null; + } + + if (storageCommandHandler != null) + { + storageCommandHandler.Dispose(); + storageCommandHandler = null; + } + + if (DocumentViews != null) + { + DocumentViews.ActiveViewChanging -= ActiveViewChanging; + DocumentViews.ActiveViewChanged -= OnActiveViewChanged; + DocumentViews.CollectionChanged -= DocumentViewsCollectionChanged; + + DocumentViews.Clear(); + DocumentViews.Dispose(); + DocumentViews = null; + } + + if (toolWindowViewsDockingManager != null) + { + toolWindowViewsDockingManager.Dispose(); + toolWindowViewsDockingManager = null; + } + + // Dispose managed resources. TODO: double check if we need to dispose managed resources? + if (mainWindow != null && !mainWindow.IsWindowDisposed) + { + mainWindow.Dispose(); + mainWindow = null; + } + + DocumentViewsResolver = null; + + splashScreen = null; + + MessageWindowLogAppender.Instance.MessageWindow = null; + + if (ApplicationCore != null) + { + ApplicationCore.Dispose(); + } + + RemoveLogging(); + Plugins = null; + } + + #region prevent nasty Windows.Forms memory leak (keeps references to databinding objects / controls + + var systemAssembly = typeof(Component).Assembly; + var reflectTypeDescriptionProviderType = + systemAssembly.GetType("System.ComponentModel.ReflectTypeDescriptionProvider"); + var propertyCacheInfo = reflectTypeDescriptionProviderType.GetField("_propertyCache", + BindingFlags.Static | + BindingFlags.NonPublic); + var propertyCache = (Hashtable)propertyCacheInfo.GetValue(null); + if (propertyCache != null) + { + propertyCache.Clear(); + } + + var extendedPropertyCacheInfo = reflectTypeDescriptionProviderType.GetField( + "_extendedPropertyCache", BindingFlags.Static | BindingFlags.NonPublic); + var extendedPropertyCache = extendedPropertyCacheInfo.GetValue(null) as Hashtable; + if (extendedPropertyCache != null) + { + extendedPropertyCache.Clear(); + } + + var eventCacheInfo = reflectTypeDescriptionProviderType.GetField("_eventCache", + BindingFlags.Static | + BindingFlags.NonPublic); + var eventCache = eventCacheInfo.GetValue(null) as Hashtable; + if (eventCache != null) + { + eventCache.Clear(); + } + + var attributeCacheInfo = reflectTypeDescriptionProviderType.GetField("_attributeCache", + BindingFlags.Static | + BindingFlags.NonPublic); + var attributeCache = attributeCacheInfo.GetValue(null) as Hashtable; + if (attributeCache != null) + { + attributeCache.Clear(); + } + + var typeDescriptorType = systemAssembly.GetType("System.ComponentModel.TypeDescriptor"); + var providerTableInfo = typeDescriptorType.GetField("_providerTable", + BindingFlags.Static | BindingFlags.NonPublic); + var providerTable = providerTableInfo.GetValue(null) as Hashtable; + if (providerTable != null) + { + providerTable.Clear(); + } + + var providerTypeTableInfo = typeDescriptorType.GetField("_providerTypeTable", + BindingFlags.Static | BindingFlags.NonPublic); + var providerTypeTable = providerTypeTableInfo.GetValue(null) as Hashtable; + if (providerTypeTable != null) + { + providerTypeTable.Clear(); + } + + var defaultProvidersInfo = typeDescriptorType.GetField("_defaultProviders", + BindingFlags.Static | BindingFlags.NonPublic); + var defaultProviders = defaultProvidersInfo.GetValue(null) as Hashtable; + if (defaultProviders != null) + { + defaultProviders.Clear(); + } + + #endregion + + GC.Collect(); + + instanceCreationStackTrace = ""; + isAlreadyRunningInstanceOfIGui = false; + } + + private IProjectExplorer ProjectExplorer + { + get + { + return ToolWindowViews.OfType().FirstOrDefault(); + } + } + + private void InitializeProjectFromPath(string projectPath) + { + var setDefaultProject = string.IsNullOrWhiteSpace(projectPath); + if (!setDefaultProject) + { + setDefaultProject = !storageCommandHandler.OpenExistingProject(projectPath); + } + if (setDefaultProject) + { + log.Info(Resources.RingtoetsGui_Run_Creating_new_project); + Project = new Project(); + } + } + + private void DeactivatePlugin(GuiPlugin plugin) + { + try + { + plugin.Deactivate(); + } + catch (Exception exception) + { + log.Error(Resources.RingtoetsGui_ActivatePlugins_Exception_during_plugin_gui_deactivation, exception); + } + + plugin.Dispose(); + + Plugins.Remove(plugin); + } + + private void ResumeUI() + { + if (mainWindow != null) + { + mainWindow.ValidateItems(); + } + } + + private void ApplicationProjectOpened(Project project) + { + ResumeUI(); + } + + // Sets the tooltip for given view, assuming that ProjectExplorer is not null. + private void SetToolTipForView(IView view) + { + if (mainWindow == null || ProjectExplorer == null) + { + return; + } + + DocumentViews.SetTooltip(view, ProjectExplorer.TreeViewControl.TryGetPathForData(view.Data)); + } + + private void ConfigureLogging() + { + // configure logging + var rootLogger = ((Hierarchy) LogManager.GetRepository()).Root; + + if (!rootLogger.Appenders.Cast().Any(a => a is MessageWindowLogAppender)) + { + rootLogger.AddAppender(new MessageWindowLogAppender()); + rootLogger.Repository.Configured = true; + } + } + + private void RemoveLogging() + { + var rootLogger = ((Hierarchy) LogManager.GetRepository()).Root; + var messageWindowLogAppender = rootLogger.Appenders.Cast().OfType().FirstOrDefault(); + if (messageWindowLogAppender != null) + { + rootLogger.RemoveAppender(messageWindowLogAppender); + } + } + + private void Initialize() + { + InitializeWindows(); + + InitializeGuiPlugins(); + + CopyDefaultViewsFromUserSettings(); + + //enable activation AFTER initialization + DocumentViews.IgnoreActivation = false; + ToolWindowViews.IgnoreActivation = false; + + if (Properties.Settings.Default.SettingsKey.Contains("mruList")) + { + if (Properties.Settings.Default["mruList"] == null) + { + Properties.Settings.Default["mruList"] = new StringCollection(); + } + } + } + + private void ShowSplashScreen() + { + splashScreen = new SplashScreen + { + VersionText = SettingsHelper.ApplicationVersion, + CopyrightText = FixedSettings.Copyright, + LicenseText = FixedSettings.LicenseDescription, + }; + + splashScreen.IsVisibleChanged += delegate + { + if (splashScreen.IsVisible) + { + return; + } + + if (!runFinished) // splash screen was closed before gui started. + { + log.Info(Resources.RingtoetsGui_ShowSplashScreen_User_has_cancelled_start_Exiting); + Environment.Exit(1); + } + }; + + object showSplashScreen = UserSettings["showSplashScreen"]; + if (showSplashScreen != null && bool.Parse(showSplashScreen.ToString())) + { + splashScreen.Show(); + } + } + + private void HideSplashScreen() + { + splashScreen.Shutdown(); + } + + private void InitializeMainWindow() + { + mainWindow.Loaded += delegate + { + mainWindow.LoadLayout(); + + toolWindowViewsDockingManager.UpdateLayout(); + + // bug in Fluent ribbon (views removed during load layout are not cleared - no events), synchronize them manually + ToolWindowViews.RemoveAllExcept(toolWindowViewsDockingManager.Views.ToArray()); + + // make sure these windows come on top + if (ToolWindowViews.Contains(mainWindow.PropertyGrid)) + { + ToolWindowViews.ActiveView = mainWindow.PropertyGrid; + } + + if (ToolWindowViews.Contains(mainWindow.MessageWindow)) + { + ToolWindowViews.ActiveView = mainWindow.MessageWindow; + } + + if (ToolWindowViews.Contains(ProjectExplorer)) + { + ToolWindowViews.ActiveView = ProjectExplorer; + } + + mainWindow.ValidateItems(); + + mainWindow.ShowStartPage(); + }; + + mainWindow.Closing += delegate(object sender, CancelEventArgs e) + { + if (isExiting) + { + return; + } + + e.Cancel = true; //cancel closing: let Exit handle it + Exit(); + }; + } + + private void InitializeWindows() + { + log.Info(Resources.RingtoetsGui_InitializeWindows_Initializing_windows); + + InitializeMainWindow(); + + log.Info(Resources.RingtoetsGui_InitializeWindows_Creating_default_tool_windows); + InitializeToolWindows(); + + UpdateTitle(); + + log.Info(Resources.RingtoetsGui_InitializeWindows_All_windows_are_created); + } + + private void ActiveViewChanging(object sender, ActiveViewChangeEventArgs e) + { + if (e.View == null || mainWindow == null || mainWindow.IsWindowDisposed) + { + return; + } + + if (ProjectExplorer != null) + { + SetToolTipForView(e.View); + } + } + + private void InitializeToolWindows() + { + log.Info(Resources.RingtoetsGui_InitToolWindows_Creating_document_window_manager); + + InitializeDocumentViewController(); + + PropertyResolver = new PropertyResolver(Plugins.SelectMany(p => p.GetPropertyInfos())); + applicationFeatureCommands = new ApplicationFeatureCommandHandler(PropertyResolver, mainWindow, this); + + InitializeToolViewController(); + + log.Info(Resources.RingtoetsGui_InitToolWindows_Creating_tool_window_manager); + + mainWindow.InitializeToolWindows(); + + log.Debug(Resources.RingtoetsGui_InitToolWindows_Finished_InitToolWindows); + + mainWindow.SubscribeToGui(); + } + + private void InitializeDocumentViewController() + { + var allowedDocumentWindowLocations = new[] + { + ViewLocation.Document, + ViewLocation.Floating + }; + + var documentDockingManager = new AvalonDockDockingManager(mainWindow.DockingManager, allowedDocumentWindowLocations); + + var documentViewManager = new ViewList(documentDockingManager, ViewLocation.Document) + { + IgnoreActivation = true, + UpdateViewNameAction = v => UpdateViewName(v) + }; + + documentViewManager.EnableTabContextMenus(); + + documentViewManager.ActiveViewChanging += ActiveViewChanging; + documentViewManager.ActiveViewChanged += OnActiveViewChanged; + documentViewManager.CollectionChanged += DocumentViewsCollectionChanged; + + DocumentViews = documentViewManager; + + DocumentViewsResolver = new ViewResolver(documentViewManager, Plugins.SelectMany(p => p.GetViewInfos()), mainWindow); + } + + private void InitializeToolViewController() + { + var allowedToolWindowLocations = new[] + { + ViewLocation.Left, + ViewLocation.Right, + ViewLocation.Top, + ViewLocation.Bottom, + ViewLocation.Floating + }; + + toolWindowViewsDockingManager = new AvalonDockDockingManager(mainWindow.DockingManager, allowedToolWindowLocations); + + ToolWindowViews = new ViewList(toolWindowViewsDockingManager, ViewLocation.Left) + { + IgnoreActivation = true + }; + + ToolWindowViews.CollectionChanged += ToolWindowViewsOnCollectionChanged; + } + + private void OnActiveViewChanged(object sender, ActiveViewChangeEventArgs e) + { + if (mainWindow == null || mainWindow.IsWindowDisposed) + { + return; + } + + mainWindow.ValidateItems(); + FireActiveViewChanged(e); + } + + private void FireActiveViewChanged(ActiveViewChangeEventArgs e) + { + if (ActiveViewChanged != null) + { + ActiveViewChanged(null, e); + } + } + + private void UpdateViewName(IView view) + { + view.Text = DocumentViewsResolver.GetViewName(view); + SetToolTipForView(view); + } + + private void ToolWindowViewsOnCollectionChanged(object sender, NotifyCollectionChangeEventArgs notifyCollectionChangeEventArgs) + { + if (isExiting) + { + return; + } + mainWindow.ValidateItems(); + } + + private void DocumentViewsCollectionChanged(object sender, NotifyCollectionChangeEventArgs e) + { + if (isExiting || DocumentViews.Count != 0) + { + return; + } + + // if no new active view is set update toolbars + mainWindow.ValidateItems(); + } + + private void InitializeGuiPlugins() + { + Plugins.ForEachElementDo(p => p.Gui = this); + + var problematicPlugins = new List(); + + // Try to activate all plugins + foreach (var plugin in Plugins) + { + try + { + plugin.Activate(); + } + catch + { + problematicPlugins.Add(plugin); + } + } + + // Deactivate and remove all problematic plugins + foreach (var problematicPlugin in problematicPlugins) + { + DeactivatePlugin(problematicPlugin); + } + } + + private void CopyDefaultViewsFromUserSettings() + { + if (UserSettings["defaultViews"] == null) + { + return; + } + + var defaultViews = (StringCollection) UserSettings["defaultViews"]; + var defaultViewDataTypes = (StringCollection) UserSettings["defaultViewDataTypes"]; + + for (int i = 0; i < defaultViews.Count; i++) + { + string viewDataTypeName = defaultViewDataTypes[i]; + string viewTypeName = defaultViews[i]; + + Type viewDataType = AssemblyUtils.GetTypeByName(viewDataTypeName); + if (viewDataType != null) + { + DocumentViewsResolver.DefaultViewTypes.Add(viewDataType, AssemblyUtils.GetTypeByName(viewTypeName)); + } + } + } + + private void CopyDefaultViewsToUserSettings() + { + StringCollection defaultViews = new StringCollection(); + StringCollection defaultViewDataTypes = new StringCollection(); + + foreach (Type objectType in DocumentViewsResolver.DefaultViewTypes.Keys) + { + if (DocumentViewsResolver.DefaultViewTypes[objectType] == null) + { + continue; + } + defaultViews.Add(DocumentViewsResolver.DefaultViewTypes[objectType].ToString()); + defaultViewDataTypes.Add(objectType.ToString()); + } + + UserSettings["defaultViews"] = defaultViews; + UserSettings["defaultViewDataTypes"] = defaultViewDataTypes; + } + + ~GuiCore() + { + Dispose(false); + } + + #region Implementation: IProjectOwner + + private Project project; + + public event Action ProjectOpened; + public event Action ProjectClosing; + + public string ProjectFilePath { get; set; } + + public Project Project + { + get + { + return project; + } + set + { + if (project != null) + { + if (ProjectClosing != null) + { + ProjectClosing(project); + } + } + + project = value; + + if (project != null) + { + if (ProjectOpened != null) + { + ProjectOpened(project); + } + } + } + } + + #endregion + + #region Implementation: IApplicationSelection + + private object selection; + + private bool settingSelection; + + public event EventHandler SelectionChanged; // TODO: make it weak + + public object Selection + { + get + { + return selection; + } + set + { + if (selection == value || settingSelection) + { + return; + } + + if (null != DocumentViews) + { + DocumentViews.IgnoreActivation = true; + } + + selection = value; + + settingSelection = true; + + try + { + if (SelectionChanged != null) + { + SelectionChanged(selection, new SelectedItemChangedEventArgs(value)); + } + } + finally + { + settingSelection = false; + } + + if (!isExiting && mainWindow != null && !mainWindow.IsWindowDisposed) + { + mainWindow.ValidateItems(); + } + + if (null != DocumentViews) + { + DocumentViews.IgnoreActivation = false; + } + } + } + + #endregion + + #region Implementation: ICommandsOwner + + private ApplicationFeatureCommandHandler applicationFeatureCommands; + private readonly ViewCommandHandler viewCommandHandler; + private readonly ProjectCommandsHandler projectCommandsHandler; + private readonly ExportImportCommandHandler exportImportCommandHandler; + private StorageCommandHandler storageCommandHandler; + + public IApplicationFeatureCommands ApplicationCommands + { + get + { + return applicationFeatureCommands; + } + } + + public IStorageCommands StorageCommands + { + get + { + return storageCommandHandler; + } + } + + public IProjectCommands ProjectCommands + { + get + { + return projectCommandsHandler; + } + } + + public IViewCommands ViewCommands + { + get + { + return viewCommandHandler; + } + } + + #endregion + + #region Implementation: IDocumentViewController + + public event EventHandler ActiveViewChanged; + + public IView ActiveView + { + get + { + return DocumentViews != null ? + DocumentViews.ActiveView : + null; + } + } + + public IViewList DocumentViews { get; private set; } + + public IViewResolver DocumentViewsResolver { get; private set; } + + public void UpdateToolTips() + { + foreach (var view in DocumentViews.AllViews) + { + SetToolTipForView(view); + } + } + + #endregion + + #region Implementation: ISettingsOwner + + private bool userSettingsDirty; + private ApplicationSettingsBase userSettings; + + public GuiCoreSettings FixedSettings { get; private set; } + + public ApplicationSettingsBase UserSettings + { + get + { + return userSettings; + } + private set + { + if (userSettings != null) + { + userSettings.PropertyChanged -= UserSettingsPropertyChanged; + } + + userSettings = value; + + if (userSettings != null) + { + userSettings.PropertyChanged += UserSettingsPropertyChanged; + } + + userSettingsDirty = false; + } + } + + private void UserSettingsPropertyChanged(object sender, PropertyChangedEventArgs e) + { + userSettingsDirty = true; + } + + #endregion + + #region Implementation: IGuiPluginHost + + public IList Plugins { get; private set; } + + public IEnumerable GetTreeNodeInfos() + { + return Plugins.SelectMany(pluginGui => pluginGui.GetTreeNodeInfos()); + } + + public IEnumerable GetAllDataWithViewDefinitionsRecursively(object rootDataObject) + { + var resultSet = new HashSet(); + foreach (var childDataInstance in Plugins.SelectMany(p => p.GetChildDataWithViewDefinitions(rootDataObject)).Distinct()) + { + resultSet.Add(childDataInstance); + + if (!ReferenceEquals(rootDataObject, childDataInstance)) + { + foreach (var dataWithViewDefined in GetAllDataWithViewDefinitionsRecursively(childDataInstance)) + { + resultSet.Add(dataWithViewDefined); + } + } + } + return resultSet; + } + + #endregion + + #region Implementation: IMainWindowController + + private MainWindow mainWindow; + + public IMainWindow MainWindow + { + get + { + return mainWindow; + } + private set + { + mainWindow = (MainWindow) value; + mainWindow.SetGui(this); + } + } + + public void RefreshGui() + { + // Set the gui selection to the current project + Selection = Project; + + // Update the window title + UpdateTitle(); + } + + public void UpdateTitle() + { + if (mainWindow != null) + { + mainWindow.Title = string.Format("{0} - {1} {2}", + Project != null ? Project.Name : Resources.RingtoetsGui_UpdateTitle_Unknown, + FixedSettings.MainWindowTitle, + SettingsHelper.ApplicationVersion); + } + } + + #endregion + + #region Implementation: IToolViewController + + public IViewList ToolWindowViews { get; private set; } + + public void CloseToolView(IView toolView) + { + ToolWindowViews.Remove(toolView); + } + + public bool IsToolWindowOpen() + { + return ToolWindowViews.Any(t => t.GetType() == typeof(T)); + } + + public void OpenToolView(IView toolView) + { + ToolWindowViews.Add(toolView); + ToolWindowViews.ActiveView = toolView; + } + + #endregion + } +} \ No newline at end of file Index: Core/Common/src/Core.Common.Gui/Properties/Settings.cs =================================================================== diff -u -r4512af7782ee31b36941bb280b54d9da2953dd71 -r6ef8fa312a72f486be984ebc3adde4be044811e2 --- Core/Common/src/Core.Common.Gui/Properties/Settings.cs (.../Settings.cs) (revision 4512af7782ee31b36941bb280b54d9da2953dd71) +++ Core/Common/src/Core.Common.Gui/Properties/Settings.cs (.../Settings.cs) (revision 6ef8fa312a72f486be984ebc3adde4be044811e2) @@ -39,7 +39,7 @@ { public Settings() { - PortableSettingsProvider.SettingsFileName = Path.Combine(SettingsHelper.GetApplicationLocalUserSettingsDirectory(), "user.config"); + PortableSettingsProvider.SettingsFilePath = Path.Combine(SettingsHelper.GetApplicationLocalUserSettingsDirectory(), "user.config"); //add default intances for collections if (mruList == null) { Fisheye: Tag 6ef8fa312a72f486be984ebc3adde4be044811e2 refers to a dead (removed) revision in file `Core/Common/src/Core.Common.Gui/RingtoetsGui.cs'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 6ef8fa312a72f486be984ebc3adde4be044811e2 refers to a dead (removed) revision in file `Core/Common/src/Core.Common.Gui/Settings/CustomSettingsProvider.cs'. Fisheye: No comparison available. Pass `N' to diff? Index: Core/Common/src/Core.Common.Gui/Settings/PortableSettingsProvider.cs =================================================================== diff -u --- Core/Common/src/Core.Common.Gui/Settings/PortableSettingsProvider.cs (revision 0) +++ Core/Common/src/Core.Common.Gui/Settings/PortableSettingsProvider.cs (revision 6ef8fa312a72f486be984ebc3adde4be044811e2) @@ -0,0 +1,208 @@ +// Copyright (C) Stichting Deltares 2016. All rights reserved. +// +// This file is part of Ringtoets. +// +// Ringtoets is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see . +// +// All names, logos, and references to "Deltares" are registered trademarks of +// Stichting Deltares and remain full property of Stichting Deltares at all times. +// All rights reserved. + +using System; +using System.Collections; +using System.Collections.Specialized; +using System.Configuration; +using System.IO; +using System.Linq; +using System.Xml; + +using Core.Common.Gui.Properties; + +using log4net; + +//snatched from http://www.codeproject.com/KB/vb/CustomSettingsProvider.aspx + +namespace Core.Common.Gui.Settings +{ + /// + /// that allows for library settings (*.config) + /// to be stored at a different location then next to the corresponding library *.dll. + /// + public class PortableSettingsProvider : SettingsProvider + { + //XML Root Node + private const string SETTINGSROOT = "Settings"; + private static readonly ILog log = LogManager.GetLogger(typeof(PortableSettingsProvider)); + + private XmlDocument m_SettingsXML = null; + + public override string ApplicationName + { + get + { + if (System.Windows.Forms.Application.ProductName.Trim().Length > 0) + { + return System.Windows.Forms.Application.ProductName; + } + + var fi = new FileInfo(System.Windows.Forms.Application.ExecutablePath); + return fi.Name.Substring(0, fi.Name.Length - fi.Extension.Length); + } + //Do nothing + set {} + } + + /// + /// Gets or sets the file location where the settings should be stored. + /// + public static string SettingsFilePath { get; set; } + + public override void Initialize(string name, NameValueCollection col) + { + base.Initialize(ApplicationName, col); + } + + public override void SetPropertyValues(SettingsContext context, SettingsPropertyValueCollection propvals) + { + //Iterate through the settings to be stored + //Only dirty settings are included in propvals, and only ones relevant to this provider + foreach (SettingsPropertyValue propval in propvals) + { + SetValue(propval); + } + + try + { + SettingsXML.Save(SettingsFilePath); + } + catch + { + log.ErrorFormat(Resources.PortableSettingsProvider_SetPropertyValues_Error_storing_settings_to_0_, SettingsFilePath); + } + } + + public override SettingsPropertyValueCollection GetPropertyValues(SettingsContext context, SettingsPropertyCollection props) + { + //Create new collection of values + var values = new SettingsPropertyValueCollection(); + + //Iterate through the settings to be retrieved + + foreach (SettingsProperty setting in props) + { + var value = new SettingsPropertyValue(setting) + { + IsDirty = false, + SerializedValue = GetValue(setting) + }; + values.Add(value); + } + return values; + } + + private XmlDocument SettingsXML + { + get + { + //If we dont hold an xml document, try opening one. + //If it doesnt exist then create a new one ready. + if (m_SettingsXML == null) + { + m_SettingsXML = new XmlDocument(); + var filePath = SettingsFilePath; + if (File.Exists(filePath)) + { + try + { + m_SettingsXML.Load(filePath); + } + catch (Exception) + { + //Create new document + CreateNewSettingsXml(); + } + } + else + { + CreateNewSettingsXml(); + } + } + + return m_SettingsXML; + } + } + + private void CreateNewSettingsXml() + { + XmlDeclaration dec = m_SettingsXML.CreateXmlDeclaration("1.0", "utf-8", string.Empty); + m_SettingsXML.AppendChild(dec); + + XmlNode nodeRoot; + + nodeRoot = m_SettingsXML.CreateNode(XmlNodeType.Element, SETTINGSROOT, ""); + m_SettingsXML.AppendChild(nodeRoot); + } + + private string GetValue(SettingsProperty setting) + { + ThrowIfRoaming(setting); + var selectSingleNode = SettingsXML.SelectSingleNode("Settings/" + setting.Name); + if (selectSingleNode != null) + { + return selectSingleNode.InnerText; + } + return (setting.DefaultValue != null) ? setting.DefaultValue.ToString() : ""; + } + + private void SetValue(SettingsPropertyValue propVal) + { + ThrowIfRoaming(propVal.Property); + + XmlElement SettingNode; + + try + { + SettingNode = (XmlElement) SettingsXML.SelectSingleNode(SETTINGSROOT + "/" + propVal.Name); + } + catch (Exception) + { + SettingNode = null; + } + + //Check to see if the node exists, if so then set its new value + if ((SettingNode != null)) + { + SettingNode.InnerText = propVal.SerializedValue.ToString(); + } + else + { + //Store the value as an element of the Settings Root Node + SettingNode = SettingsXML.CreateElement(propVal.Name); + SettingNode.InnerText = propVal.SerializedValue.ToString(); + SettingsXML.SelectSingleNode(SETTINGSROOT).AppendChild(SettingNode); + } + } + + private static void ThrowIfRoaming(SettingsProperty prop) + { + //maybe we should treat everything as roaming and simlpy not throw here? + + //Determine if the setting is marked as Roaming + if ((from DictionaryEntry d in prop.Attributes select (Attribute) d.Value).OfType().Any()) + { + throw new NotImplementedException(String.Format(Resources.PortableSettingsProvider_ThrowIfRoaming_Setting_0_is_roaming_This_is_not_supported, prop.Name)); + } + } + } +} \ No newline at end of file Index: Core/Common/test/Core.Common.Gui.Test/Core.Common.Gui.Test.csproj =================================================================== diff -u -rd212065881d696420fd33c789c917501e09c7fc0 -r6ef8fa312a72f486be984ebc3adde4be044811e2 --- Core/Common/test/Core.Common.Gui.Test/Core.Common.Gui.Test.csproj (.../Core.Common.Gui.Test.csproj) (revision d212065881d696420fd33c789c917501e09c7fc0) +++ Core/Common/test/Core.Common.Gui.Test/Core.Common.Gui.Test.csproj (.../Core.Common.Gui.Test.csproj) (revision 6ef8fa312a72f486be984ebc3adde4be044811e2) @@ -148,7 +148,7 @@ - + Index: Core/Common/test/Core.Common.Gui.Test/GuiCoreTest.cs =================================================================== diff -u --- Core/Common/test/Core.Common.Gui.Test/GuiCoreTest.cs (revision 0) +++ Core/Common/test/Core.Common.Gui.Test/GuiCoreTest.cs (revision 6ef8fa312a72f486be984ebc3adde4be044811e2) @@ -0,0 +1,1181 @@ +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Configuration; +using System.Linq; +using System.Windows.Forms; +using System.Windows.Forms.VisualStyles; + +using Core.Common.Base.Data; +using Core.Common.Base.Plugin; +using Core.Common.Base.Storage; +using Core.Common.Controls.TreeView; +using Core.Common.Controls.Views; +using Core.Common.Gui.Commands; +using Core.Common.Gui.ContextMenu; +using Core.Common.Gui.Forms; +using Core.Common.Gui.Forms.MainWindow; +using Core.Common.Gui.Forms.MessageWindow; +using Core.Common.Gui.Forms.PropertyGridView; +using Core.Common.Gui.Plugin; +using Core.Common.Gui.Settings; +using Core.Common.Gui.Test.Forms.ViewManager; +using Core.Common.Gui.Theme; +using Core.Common.TestUtil; + +using log4net; +using log4net.Appender; +using log4net.Repository.Hierarchy; + +using NUnit.Framework; + +using Rhino.Mocks; + +namespace Core.Common.Gui.Test +{ + [TestFixture] + public class GuiCoreTests + { + private MessageWindowLogAppender originalMessageWindowLogAppender; + private IViewCommands originalViewPropertyEditor; + + [SetUp] + public void SetUp() + { + originalMessageWindowLogAppender = MessageWindowLogAppender.Instance; + MessageWindowLogAppender.Instance = new MessageWindowLogAppender(); + + originalViewPropertyEditor = ViewPropertyEditor.ViewCommands; + } + + [TearDown] + public void TearDown() + { + MessageWindowLogAppender.Instance = originalMessageWindowLogAppender; + ViewPropertyEditor.ViewCommands = originalViewPropertyEditor; + } + + [Test] + [STAThread] + public void ParameteredConstructor_ValidArguments_ExpectedValues() + { + // Setup + var mocks = new MockRepository(); + var projectStore = mocks.Stub(); + mocks.ReplayAll(); + + var applicationCore = new ApplicationCore(); + var guiCoreSettings = new GuiCoreSettings(); + + // Call + using (var mainWindow = new MainWindow()) + using (var gui = new GuiCore(mainWindow, projectStore, applicationCore, guiCoreSettings)) + { + // Assert + Assert.AreSame(applicationCore, gui.ApplicationCore); + Assert.AreEqual(null, gui.PropertyResolver); + Assert.AreSame(projectStore, gui.Storage); + + Assert.AreEqual(null, gui.ProjectFilePath); + Assert.AreEqual(null, gui.Project); + + Assert.AreEqual(null, gui.Selection); + + Assert.IsInstanceOf(gui.ProjectCommands); + Assert.IsInstanceOf(gui.StorageCommands); + Assert.IsInstanceOf(gui.ViewCommands); + Assert.AreEqual(null, gui.ApplicationCommands); + + Assert.AreEqual(null, gui.ActiveView); + Assert.AreEqual(null, gui.DocumentViews); + Assert.AreEqual(null, gui.DocumentViewsResolver); + + AssertDefaultUserSettings(gui.UserSettings); + Assert.AreSame(guiCoreSettings, gui.FixedSettings); + + CollectionAssert.IsEmpty(gui.Plugins); + + Assert.AreEqual(mainWindow, gui.MainWindow); + + Assert.AreEqual(null, gui.ToolWindowViews); + + Assert.AreSame(ViewPropertyEditor.ViewCommands, gui.ViewCommands); + + // Check for OS settings that allow visual styles to be rendered in the first place: + if (VisualStyleInformation.IsSupportedByOS && VisualStyleInformation.IsEnabledByUser && + (Application.VisualStyleState == VisualStyleState.ClientAreaEnabled || + Application.VisualStyleState == VisualStyleState.ClientAndNonClientAreasEnabled)) + { + Assert.IsTrue(Application.RenderWithVisualStyles, + "OS configured to support visual styles, therefore GUI should enable this rendering style."); + } + else + { + // + Assert.IsFalse(Application.RenderWithVisualStyles, + "OS not supporting visual styles, therefore application shouldn't be render with visual styles."); + } + } + mocks.VerifyAll(); + } + + [Test] + [STAThread] + [TestCase(0)] + [TestCase(1)] + [TestCase(2)] + [TestCase(3)] + public void ParameteredConstructor_SomeArgumentIsNull_ThrowArgumentNullException(int nullArgumentIndex) + { + // Setup + var mocks = new MockRepository(); + IStoreProject projectStore = nullArgumentIndex == 1 ? null : mocks.Stub(); + mocks.ReplayAll(); + + ApplicationCore applicationCore = nullArgumentIndex == 2 ? null : new ApplicationCore(); + GuiCoreSettings guiCoreSettings = nullArgumentIndex == 3 ? null : new GuiCoreSettings(); + + // Call + using (var mainWindow = new MainWindow()) + + { + // Call + TestDelegate call = () => new GuiCore(nullArgumentIndex == 0 ? null : mainWindow, projectStore, applicationCore, guiCoreSettings); + + // Assert + TestHelper.AssertThrowsArgumentExceptionAndTestMessage(call, "Value cannot be null."); + } + mocks.VerifyAll(); + } + + [Test] + [STAThread] + [ExpectedException(typeof(InvalidOperationException))] + public void Constructor_ConstuctedAfterAnotherInstanceHasBeenCreated_ThrowInvalidOperationException() + { + // Setup + var mocks = new MockRepository(); + var projectStore = mocks.Stub(); + mocks.ReplayAll(); + + var applicationCore = new ApplicationCore(); + var guiCoreSettings = new GuiCoreSettings(); + + // Call + using (var mainWindow = new MainWindow()) + using (var gui = new GuiCore(mainWindow, projectStore, applicationCore, guiCoreSettings)) + { + // Call + using (var gui2 = new GuiCore(mainWindow, projectStore, applicationCore, guiCoreSettings)) + { + // Assert + Assert.Fail("Expected an InvalidOperationException to be thrown."); + } + } + mocks.VerifyAll(); + } + + [Test] + [STAThread] + public void Dispose_ApplicationCoreSet_DisposesOfApplicationCore() + { + // Setup + var mocks = new MockRepository(); + var projectStore = mocks.Stub(); + var applicationCore = mocks.Stub(); + applicationCore.Expect(ac => ac.Dispose()); + mocks.ReplayAll(); + + var gui = new GuiCore(new MainWindow(), projectStore, applicationCore, new GuiCoreSettings()); + + // Call + gui.Dispose(); + + // Assert + mocks.VerifyAll(); + } + + [Test] + [STAThread] + public void Dispose_GuipluginsAdded_PluginsDisabledAndRemovedAndDisposed() + { + // Setup + var mocks = new MockRepository(); + var projectStore = mocks.Stub(); + var guiPluginMock = mocks.Stub(); + guiPluginMock.Expect(p => p.Deactivate()); + guiPluginMock.Expect(p => p.Dispose()); + mocks.ReplayAll(); + + var gui = new GuiCore(new MainWindow(), projectStore, new ApplicationCore(), new GuiCoreSettings()); + gui.Plugins.Add(guiPluginMock); + + // Call + gui.Dispose(); + + // Assert + Assert.IsNull(gui.Plugins); + mocks.VerifyAll(); + } + + [Test] + [STAThread] + public void Dispose_GuiPluginAddedButThrowsExceptionDuringDeactivation_LogErrorAndStillDisposeAndRemove() + { + // Setup + var mocks = new MockRepository(); + var projectStore = mocks.Stub(); + var guiPluginMock = mocks.Stub(); + guiPluginMock.Expect(p => p.Deactivate()).Throw(new Exception("Bad stuff happening!")); + guiPluginMock.Expect(p => p.Dispose()); + mocks.ReplayAll(); + + var gui = new GuiCore(new MainWindow(), projectStore, new ApplicationCore(), new GuiCoreSettings()); + gui.Plugins.Add(guiPluginMock); + + // Call + Action call = () => gui.Dispose(); + + // Assert + TestHelper.AssertLogMessageIsGenerated(call, "Kritieke fout opgetreden tijdens deactivering van de grafische interface plugin.", 1); + Assert.IsNull(gui.Plugins); + mocks.VerifyAll(); + } + + [Test] + [STAThread] + public void Dispose_HasSelection_ClearSelection() + { + // Setup + var mocks = new MockRepository(); + var projectStore = mocks.Stub(); + mocks.ReplayAll(); + + var gui = new GuiCore(new MainWindow(), projectStore, new ApplicationCore(), new GuiCoreSettings()) + { + Selection = new object() + }; + + // Call + gui.Dispose(); + + // Assert + Assert.IsNull(gui.Selection); + mocks.VerifyAll(); + } + + [Test] + [STAThread] + public void Dispose_HasProject_ClearProject() + { + // Setup + var mocks = new MockRepository(); + var projectStore = mocks.Stub(); + mocks.ReplayAll(); + + var gui = new GuiCore(new MainWindow(), projectStore, new ApplicationCore(), new GuiCoreSettings()) + { + Project = new Project() + }; + + // Call + gui.Dispose(); + + // Assert + Assert.IsNull(gui.Project); + mocks.VerifyAll(); + } + + [Test] + [STAThread] + public void Dispose_HasMainWindow_DiposeOfMainWindow() + { + // Setup + var mocks = new MockRepository(); + var projectStore = mocks.Stub(); + mocks.ReplayAll(); + + using (var mainWindow = new MainWindow()) + { + var gui = new GuiCore(mainWindow, projectStore, new ApplicationCore(), new GuiCoreSettings()); + + // Call + gui.Dispose(); + + // Assert + Assert.IsTrue(mainWindow.IsWindowDisposed); + Assert.IsNull(gui.MainWindow); + } + mocks.VerifyAll(); + } + + [Test] + [STAThread] + public void Dispose_HasInitializedMessageWindowForLogAppender_ClearMessageWindow() + { + // Setup + var mocks = new MockRepository(); + var projectStore = mocks.Stub(); + mocks.ReplayAll(); + + var messageWindowLogAppender = new MessageWindowLogAppender(); + + var rootLogger = ((Hierarchy)LogManager.GetRepository()).Root; + rootLogger.AddAppender(messageWindowLogAppender); + + try + { + using (var gui = new GuiCore(new MainWindow(), projectStore, new ApplicationCore(), new GuiCoreSettings())) + { + gui.Run(); + + // Precondition: + Assert.IsNotNull(MessageWindowLogAppender.Instance.MessageWindow); + Assert.IsNotNull(messageWindowLogAppender.MessageWindow); + + // Call + gui.Dispose(); + + // Assert + Assert.IsNull(MessageWindowLogAppender.Instance.MessageWindow); + Assert.IsNull(messageWindowLogAppender.MessageWindow); + CollectionAssert.DoesNotContain(rootLogger.Appenders, messageWindowLogAppender); + } + } + finally + { + rootLogger.RemoveAppender(messageWindowLogAppender); + } + mocks.VerifyAll(); + } + + [Test] + [STAThread] + public void Dispose_HasOpenedToolView_ToolViewsClearedAndViewsDisposed() + { + // Setup + var mocks = new MockRepository(); + var projectStore = mocks.Stub(); + mocks.ReplayAll(); + + using(var toolView = new TestView()) + using (var gui = new GuiCore(new MainWindow(), projectStore, new ApplicationCore(), new GuiCoreSettings())) + { + gui.Run(); + + gui.OpenToolView(toolView); + + // Call + gui.Dispose(); + + // Assert + Assert.IsNull(gui.ToolWindowViews); + Assert.IsTrue(toolView.IsDisposed); + } + mocks.VerifyAll(); + } + + [Test] + [STAThread] + public void Dispose_HasOpenedDocumentView_DocumentViewsClearedAndViewsDisposed() + { + // Setup + var mocks = new MockRepository(); + var projectStore = mocks.Stub(); + mocks.ReplayAll(); + + using (var documentView = new TestView()) + using (var gui = new GuiCore(new MainWindow(), projectStore, new ApplicationCore(), new GuiCoreSettings())) + { + gui.Run(); + + gui.DocumentViews.Add(documentView); + + // Call + gui.Dispose(); + + // Assert + Assert.IsNull(gui.DocumentViews); + Assert.IsNull(gui.DocumentViewsResolver); + Assert.IsTrue(documentView.IsDisposed); + } + mocks.VerifyAll(); + } + + [Test] + [STAThread] + public void Run_NoMessageWindowLogAppender_AddNewLogAppender() + { + // Setup + var mocks = new MockRepository(); + var projectStore = mocks.Stub(); + mocks.ReplayAll(); + + Logger rootLogger = ((Hierarchy)LogManager.GetRepository()).Root; + IAppender[] originalAppenders = rootLogger.Appenders.ToArray(); + rootLogger.RemoveAllAppenders(); + + try + { + using(var gui = new GuiCore(new MainWindow(), projectStore, new ApplicationCore(), new GuiCoreSettings())) + { + // Call + gui.Run(); + + // Assert + Assert.AreEqual(1, rootLogger.Appenders.Count); + IAppender appender = rootLogger.Appenders[0]; + Assert.IsInstanceOf(appender); + Assert.AreSame(appender, MessageWindowLogAppender.Instance); + Assert.IsTrue(rootLogger.Repository.Configured); + + Assert.IsTrue(MessageWindowLogAppender.Instance.Enabled); + } + } + finally + { + rootLogger.RemoveAllAppenders(); + foreach (var appender in originalAppenders) + { + rootLogger.AddAppender(appender); + } + } + mocks.VerifyAll(); + } + + [Test] + [STAThread] + public void Run_AlreadyHasMessageWindowLogAppender_NoChangesToLogAppenders() + { + // Setup + var mocks = new MockRepository(); + var projectStore = mocks.Stub(); + mocks.ReplayAll(); + + var appender = new MessageWindowLogAppender(); + + Logger rootLogger = ((Hierarchy)LogManager.GetRepository()).Root; + IAppender[] originalAppenders = rootLogger.Appenders.ToArray(); + rootLogger.RemoveAllAppenders(); + rootLogger.AddAppender(appender); + var rootloggerConfigured = rootLogger.Repository.Configured; + + try + { + using (var gui = new GuiCore(new MainWindow(), projectStore, new ApplicationCore(), new GuiCoreSettings())) + { + // Call + gui.Run(); + + // Assert + Assert.AreEqual(1, rootLogger.Appenders.Count); + Assert.AreSame(appender, rootLogger.Appenders[0]); + Assert.AreSame(appender, MessageWindowLogAppender.Instance); + Assert.AreEqual(rootloggerConfigured, rootLogger.Repository.Configured); + + Assert.IsTrue(MessageWindowLogAppender.Instance.Enabled); + } + } + finally + { + rootLogger.RemoveAllAppenders(); + foreach (var originalAppender in originalAppenders) + { + rootLogger.AddAppender(originalAppender); + } + } + mocks.VerifyAll(); + } + + [Test] + [STAThread] + public void Run_WithFile_LoadProjectFromFile() + { + // Setup + const string fileName = "SomeFile"; + string testFile = string.Format("{0}.rtd", fileName); + + var deserializedProject = new Project(); + + var mocks = new MockRepository(); + var projectStore = mocks.Stub(); + projectStore.Expect(ps => ps.LoadProject(testFile)).Return(deserializedProject); + mocks.ReplayAll(); + + var fixedSettings = new GuiCoreSettings + { + MainWindowTitle = "
" + }; + + using (var mainWindow = new MainWindow()) + using (var gui = new GuiCore(mainWindow, projectStore, new ApplicationCore(), fixedSettings)) + { + // Call + Action call = () => gui.Run(testFile); + + // Assert + var expectedMessages = new[] + { + "Openen van bestaand Ringtoetsproject.", + "Bestaand Ringtoetsproject succesvol geopend." + }; + TestHelper.AssertLogMessagesAreGenerated(call, expectedMessages); + Assert.AreEqual(testFile, gui.ProjectFilePath); + Assert.AreSame(deserializedProject, gui.Project); + Assert.AreEqual(fileName, gui.Project.Name, + "Project name should be updated to the name of the file."); + + Assert.AreSame(gui.Selection, gui.Project); + var expectedTitle = string.Format("{0} - {1} {2}", + fileName, fixedSettings.MainWindowTitle, SettingsHelper.ApplicationVersion); + Assert.AreEqual(expectedTitle, mainWindow.Title); + } + mocks.VerifyAll(); + } + + [Test] + [STAThread] + public void Run_LoadingFromFileThrowsStorageException_LogErrorAndLoadDefaultProjectInstead() + { + // Setup + const string fileName = "SomeFile"; + string testFile = string.Format("{0}.rtd", fileName); + + const string storageExceptionText = ""; + + var mocks = new MockRepository(); + var projectStore = mocks.Stub(); + projectStore.Expect(ps => ps.LoadProject(testFile)).Throw(new StorageException(storageExceptionText)); + mocks.ReplayAll(); + + var fixedSettings = new GuiCoreSettings + { + MainWindowTitle = "
" + }; + + using (var mainWindow = new MainWindow()) + using (var gui = new GuiCore(mainWindow, projectStore, new ApplicationCore(), fixedSettings)) + { + // Call + Action call = () => gui.Run(testFile); + + // Assert + var expectedMessages = new[] + { + "Openen van bestaand Ringtoetsproject.", + storageExceptionText, + "Het is niet gelukt om het Ringtoetsproject te laden.", + "Nieuw project aanmaken..." + }; + TestHelper.AssertLogMessagesAreGenerated(call, expectedMessages); + + Assert.IsNull(gui.ProjectFilePath); + const string expectedProjectName = "Project"; + Assert.AreEqual(expectedProjectName, gui.Project.Name); + Assert.AreEqual(string.Empty, gui.Project.Description); + CollectionAssert.IsEmpty(gui.Project.Items); + Assert.AreEqual(0, gui.Project.StorageId); + + Assert.AreSame(gui.Selection, gui.Project); + var expectedTitle = string.Format("{0} - {1} {2}", + expectedProjectName, fixedSettings.MainWindowTitle, SettingsHelper.ApplicationVersion); + Assert.AreEqual(expectedTitle, mainWindow.Title); + } + mocks.VerifyAll(); + } + + [Test] + [TestCase("")] + [TestCase(" ")] + [TestCase(null)] + [STAThread] + public void Run_WithoutFile_CreateNewDefaultProject(string path) + { + // Setup + var mocks = new MockRepository(); + var projectStore = mocks.StrictMock(); + mocks.ReplayAll(); + + var fixedSettings = new GuiCoreSettings + { + MainWindowTitle = "" + }; + + using(var mainWindow = new MainWindow()) + using (var gui = new GuiCore(mainWindow, projectStore, new ApplicationCore(), fixedSettings)) + { + // Call + Action call = () => gui.Run(path); + + // Assert + TestHelper.AssertLogMessageIsGenerated(call, "Nieuw project aanmaken..."); + + Assert.IsNull(gui.ProjectFilePath); + const string expectedProjectName = "Project"; + Assert.AreEqual(expectedProjectName, gui.Project.Name); + Assert.AreEqual(string.Empty, gui.Project.Description); + CollectionAssert.IsEmpty(gui.Project.Items); + Assert.AreEqual(0, gui.Project.StorageId); + + Assert.AreSame(gui.Selection, gui.Project); + var expectedTitle = string.Format("{0} - {1} {2}", + expectedProjectName, fixedSettings.MainWindowTitle, SettingsHelper.ApplicationVersion); + Assert.AreEqual(expectedTitle, mainWindow.Title); + } + mocks.VerifyAll(); + } + + [Test] + [STAThread] + public void Run_WithGuiPlugins_SetGuiAndActivatePlugins() + { + var mocks = new MockRepository(); + var projectStore = mocks.Stub<IStoreProject>(); + var guiPlugin = mocks.Stub<GuiPlugin>(); + guiPlugin.Stub(p => p.Deactivate()); + guiPlugin.Stub(p => p.Dispose()); + guiPlugin.Expect(p => p.Activate()); + guiPlugin.Expect(p => p.GetViewInfos()).Return(Enumerable.Empty<ViewInfo>()); + guiPlugin.Expect(p => p.GetPropertyInfos()).Return(Enumerable.Empty<PropertyInfo>()); + mocks.ReplayAll(); + + // Setup + using (var gui = new GuiCore(new MainWindow(), projectStore, new ApplicationCore(), new GuiCoreSettings())) + { + gui.Plugins.Add(guiPlugin); + + // Call + gui.Run(); + + // Assert + Assert.AreSame(gui, guiPlugin.Gui); + } + mocks.VerifyAll(); + } + + [Test] + [STAThread] + public void Run_WithGuiPluginThatThrowsExceptionWhenActivated_DeactivateAndDisposePlugin() + { + var mocks = new MockRepository(); + var projectStore = mocks.Stub<IStoreProject>(); + var guiPlugin = mocks.Stub<GuiPlugin>(); + guiPlugin.Stub(p => p.GetViewInfos()).Return(Enumerable.Empty<ViewInfo>()); + guiPlugin.Stub(p => p.GetPropertyInfos()).Return(Enumerable.Empty<PropertyInfo>()); + guiPlugin.Stub(p => p.Activate()).Throw(new Exception("ERROR!")); + guiPlugin.Expect(p => p.Deactivate()); + guiPlugin.Expect(p => p.Dispose()); + mocks.ReplayAll(); + + // Setup + using (var gui = new GuiCore(new MainWindow(), projectStore, new ApplicationCore(), new GuiCoreSettings())) + { + gui.Plugins.Add(guiPlugin); + + // Call + gui.Run(); + } + // Assert + mocks.VerifyAll(); // Expect calls on plugin + } + + [Test] + [STAThread] + public void Run_WithGuiPluginThatThrowsExceptionWhenActivatedAndDeactivated_LogErrorForDeactivatingThenDispose() + { + var mocks = new MockRepository(); + var projectStore = mocks.Stub<IStoreProject>(); + var guiPlugin = mocks.Stub<GuiPlugin>(); + guiPlugin.Stub(p => p.GetViewInfos()).Return(Enumerable.Empty<ViewInfo>()); + guiPlugin.Stub(p => p.GetPropertyInfos()).Return(Enumerable.Empty<PropertyInfo>()); + guiPlugin.Stub(p => p.Activate()).Throw(new Exception("ERROR!")); + guiPlugin.Stub(p => p.Deactivate()).Throw(new Exception("MORE ERROR!")); + guiPlugin.Expect(p => p.Dispose()); + mocks.ReplayAll(); + + // Setup + using (var gui = new GuiCore(new MainWindow(), projectStore, new ApplicationCore(), new GuiCoreSettings())) + { + gui.Plugins.Add(guiPlugin); + + // Call + Action call = () => gui.Run(); + + // Assert + var expectedMessage = "Kritieke fout opgetreden tijdens deactivering van de grafische interface plugin."; + TestHelper.AssertLogMessageIsGenerated(call, expectedMessage); + } + mocks.VerifyAll(); // Expect Dispose call on plugin + } + + [Test] + [STAThread] + public void Run_InitializesDocumentViewController() + { + // Setup + var mocks = new MockRepository(); + var projectStore = mocks.Stub<IStoreProject>(); + mocks.ReplayAll(); + + using (var gui = new GuiCore(new MainWindow(), projectStore, new ApplicationCore(), new GuiCoreSettings())) + { + // Call + Action call = () => gui.Run(); + + // Assert + var expectedMessage = "Schermmanager voor documenten aan het maken..."; + TestHelper.AssertLogMessageIsGenerated(call, expectedMessage); + + CollectionAssert.IsEmpty(gui.DocumentViews); + Assert.IsFalse(gui.DocumentViews.IgnoreActivation); + Assert.IsNull(gui.DocumentViews.ActiveView); + + Assert.IsNotNull(gui.DocumentViewsResolver); + CollectionAssert.IsEmpty(gui.DocumentViewsResolver.DefaultViewTypes); + } + mocks.VerifyAll(); + } + + [Test] + [STAThread] + public void Run_InitializesToolViewController() + { + // Setup + var mocks = new MockRepository(); + var projectStore = mocks.Stub<IStoreProject>(); + mocks.ReplayAll(); + + using (var gui = new GuiCore(new MainWindow(), projectStore, new ApplicationCore(), new GuiCoreSettings())) + { + // Call + gui.Run(); + + // Assert + Assert.AreEqual(2, gui.ToolWindowViews.Count); + Assert.AreEqual(1, gui.ToolWindowViews.Count(v => v is PropertyGridView)); + Assert.AreEqual(1, gui.ToolWindowViews.Count(v => v is MessageWindow)); + Assert.IsFalse(gui.ToolWindowViews.IgnoreActivation); + Assert.IsNull(gui.ToolWindowViews.ActiveView); + } + mocks.VerifyAll(); + } + + [Test] + [STAThread] + public void CheckViewPropertyEditorIsInitialized() + { + var mocks = new MockRepository(); + var projectStore = mocks.Stub<IStoreProject>(); + mocks.ReplayAll(); + + using (new GuiCore(new MainWindow(), projectStore, new ApplicationCore(), new GuiCoreSettings())) + { + Assert.NotNull(ViewPropertyEditor.ViewCommands); + } + mocks.VerifyAll(); + } + + [Test] + [STAThread] + public void GetAllDataWithViewDefinitionsRecursively_DataHasNoViewDefinitions_ReturnEmpty() + { + // Setup + var mocks = new MockRepository(); + var projectStore = mocks.Stub<IStoreProject>(); + mocks.ReplayAll(); + + using (var ringtoetsGui = new GuiCore(new MainWindow(), projectStore, new ApplicationCore(), new GuiCoreSettings())) + { + var rootData = new object(); + + // Call + var dataInstancesWithViewDefinitions = ringtoetsGui.GetAllDataWithViewDefinitionsRecursively(rootData); + + // Assert + CollectionAssert.IsEmpty(dataInstancesWithViewDefinitions); + } + mocks.VerifyAll(); + } + + [Test] + [STAThread] + public void GetAllDataWithViewDefinitionsRecursively_MultiplePluginsHaveViewDefinitionsForRoot_ReturnRootObject() + { + // Setup + var rootData = new object(); + + var mocks = new MockRepository(); + var projectStore = mocks.Stub<IStoreProject>(); + + var plugin1 = mocks.StrictMock<GuiPlugin>(); + plugin1.Expect(p => p.GetChildDataWithViewDefinitions(rootData)).Return(new[] + { + rootData + }); + plugin1.Stub(p => p.Dispose()); + plugin1.Stub(p => p.Deactivate()); + var plugin2 = mocks.StrictMock<GuiPlugin>(); + plugin2.Expect(p => p.GetChildDataWithViewDefinitions(rootData)).Return(new[] + { + rootData + }); + plugin2.Stub(p => p.Dispose()); + plugin2.Stub(p => p.Deactivate()); + mocks.ReplayAll(); + + using (var ringtoetsGui = new GuiCore(new MainWindow(), projectStore, new ApplicationCore(), new GuiCoreSettings())) + { + ringtoetsGui.Plugins.Add(plugin1); + ringtoetsGui.Plugins.Add(plugin2); + + // Call + var dataInstancesWithViewDefinitions = ringtoetsGui.GetAllDataWithViewDefinitionsRecursively(rootData).OfType<object>().ToArray(); + + // Assert + var expectedDataDefinitions = new[] + { + rootData + }; + CollectionAssert.AreEquivalent(expectedDataDefinitions, dataInstancesWithViewDefinitions); + } + mocks.VerifyAll(); + } + + [Test] + [STAThread] + public void GetAllDataWithViewDefinitionsRecursively_MultiplePluginsHaveViewDefinitionsForRootAndChild_ReturnRootAndChild() + { + // Setup + object rootData = 1; + object rootChild = 2; + + var mocks = new MockRepository(); + var projectStore = mocks.Stub<IStoreProject>(); + var plugin1 = mocks.StrictMock<GuiPlugin>(); + plugin1.Expect(p => p.GetChildDataWithViewDefinitions(rootData)).Return(new[] + { + rootData, rootChild + }); + plugin1.Expect(p => p.GetChildDataWithViewDefinitions(rootChild)).Return(new[] + { + rootChild + }); + plugin1.Stub(p => p.Dispose()); + plugin1.Stub(p => p.Deactivate()); + var plugin2 = mocks.StrictMock<GuiPlugin>(); + plugin2.Expect(p => p.GetChildDataWithViewDefinitions(rootData)).Return(new[] + { + rootChild, rootData + }); + plugin2.Expect(p => p.GetChildDataWithViewDefinitions(rootChild)).Return(new[] + { + rootChild + }); + plugin2.Stub(p => p.Dispose()); + plugin2.Stub(p => p.Deactivate()); + mocks.ReplayAll(); + + using (var ringtoetsGui = new GuiCore(new MainWindow(), projectStore, new ApplicationCore(), new GuiCoreSettings())) + { + ringtoetsGui.Plugins.Add(plugin1); + ringtoetsGui.Plugins.Add(plugin2); + + // Call + var dataInstancesWithViewDefinitions = ringtoetsGui.GetAllDataWithViewDefinitionsRecursively(rootData).OfType<object>().ToArray(); + + // Assert + var expectedDataDefinitions = new[] + { + rootData, rootChild + }; + CollectionAssert.AreEquivalent(expectedDataDefinitions, dataInstancesWithViewDefinitions); + } + mocks.VerifyAll(); + } + + [Test] + [RequiresSTA] + public void ActiveViewChanged_LastDocumentViewClosed_EventFired() + { + // Setup + var mocks = new MockRepository(); + var projectStore = mocks.Stub<IStoreProject>(); + mocks.ReplayAll(); + + using (var gui = new GuiCore(new MainWindow(), projectStore, new ApplicationCore(), new GuiCoreSettings())) + { + gui.Run(); + + var view = new TestView(); + + // Precondition + Assert.AreEqual(0, gui.DocumentViews.Count); + + gui.DocumentViews.Add(view); + + var hitCount = 0; + gui.ActiveViewChanged += (s, e) => hitCount++; + + // Call + gui.DocumentViews.RemoveAt(0); + + // Assert + Assert.AreEqual(0, gui.DocumentViews.Count); + Assert.AreEqual(1, hitCount); + } + mocks.VerifyAll(); + } + + [Test] + [RequiresSTA] + public void GetTreeNodeInfos_NoPluginsConfigured_EmptyList() + { + // Setup + var mocks = new MockRepository(); + var projectStore = mocks.Stub<IStoreProject>(); + mocks.ReplayAll(); + + using (var gui = new GuiCore(new MainWindow(), projectStore, new ApplicationCore(), new GuiCoreSettings())) + { + // Call + var result = gui.GetTreeNodeInfos(); + + // Assert + CollectionAssert.IsEmpty(result); + } + } + + [Test] + [RequiresSTA] + public void GetTreeNodeInfos_MultiplePluginsConfigured_RetrievesTreeNodeInfosFromPlugins() + { + // Setup + var nodesPluginA = new[] + { + new TreeNodeInfo(), + new TreeNodeInfo() + }; + var nodesPluginB = new[] + { + new TreeNodeInfo(), + }; + var nodesPluginC = new[] + { + new TreeNodeInfo(), + new TreeNodeInfo(), + new TreeNodeInfo(), + }; + + var mocks = new MockRepository(); + var projectStore = mocks.Stub<IStoreProject>(); + + var pluginA = mocks.Stub<GuiPlugin>(); + pluginA.Stub(p => p.GetTreeNodeInfos()).Return(nodesPluginA); + pluginA.Stub(p => p.Dispose()); + pluginA.Stub(p => p.Deactivate()); + var pluginB = mocks.Stub<GuiPlugin>(); + pluginB.Stub(p => p.GetTreeNodeInfos()).Return(nodesPluginB); + pluginB.Stub(p => p.Dispose()); + pluginB.Stub(p => p.Deactivate()); + var pluginC = mocks.Stub<GuiPlugin>(); + pluginC.Stub(p => p.GetTreeNodeInfos()).Return(nodesPluginC); + pluginC.Stub(p => p.Dispose()); + pluginC.Stub(p => p.Deactivate()); + mocks.ReplayAll(); + + using (var gui = new GuiCore(new MainWindow(), projectStore, new ApplicationCore(), new GuiCoreSettings())) + { + gui.Plugins.Add(pluginA); + gui.Plugins.Add(pluginB); + gui.Plugins.Add(pluginC); + + // Call + var result = gui.GetTreeNodeInfos(); + + // Assert + var expected = nodesPluginA.Concat(nodesPluginB).Concat(nodesPluginC); + CollectionAssert.AreEquivalent(expected, result); + } + + mocks.VerifyAll(); + } + + [Test] + [STAThread] + public void Get_GuiHasntRunYet_ThrowInvalidOperationException() + { + // Setup + var mocks = new MockRepository(); + var projectStore = mocks.Stub<IStoreProject>(); + mocks.ReplayAll(); + + using (var treeView = new TreeViewControl()) + using (var gui = new GuiCore(new MainWindow(), projectStore, new ApplicationCore(), new GuiCoreSettings())) + { + // Call + TestDelegate call = () => gui.Get(new object(), treeView); + + // Assert + var message = Assert.Throws<InvalidOperationException>(call).Message; + Assert.AreEqual("Call IGui.Run in order to initialize dependencies before getting the ContextMenuBuilder.", message); + + } + mocks.VerifyAll(); + } + + [Test] + [STAThread] + public void Get_GuiIsRunning_ReturnsContextMenuBuilder() + { + // Setup + var mocks = new MockRepository(); + var projectStore = mocks.Stub<IStoreProject>(); + mocks.ReplayAll(); + + using (var treeView = new TreeViewControl()) + using (var gui = new GuiCore(new MainWindow(), projectStore, new ApplicationCore(), new GuiCoreSettings())) + { + gui.Run(); + + // Call + IContextMenuBuilder builder = gui.Get(new object(), treeView); + + // Assert + ContextMenuStrip contextMenu = builder.AddRenameItem() + .AddCollapseAllItem() + .AddDeleteItem() + .AddExpandAllItem() + .AddImportItem() + .AddExportItem() + .AddOpenItem() + .AddSeparator() + .AddPropertiesItem() + .Build(); + Assert.AreEqual(9, contextMenu.Items.Count); + } + mocks.VerifyAll(); + } + + [Test] + [STAThread] + public void GivenGuiRunCalled_WhenMainWindowOpens_EnsurePropertyGridAndMessageWindowAreActivated() + { + // Setup + var mocks = new MockRepository(); + var projectStore = mocks.Stub<IStoreProject>(); + mocks.ReplayAll(); + + using (var mainWindow = new MainWindow()) + using (var gui = new GuiCore(mainWindow, projectStore, new ApplicationCore(), new GuiCoreSettings())) + { + gui.Run(); + + gui.ToolWindowViews.Add(new ProjectExplorerMock()); + + var activatedViewsDuringShow = new List<IView>(); + gui.ToolWindowViews.ActiveViewChanged += (sender, args) => + { + activatedViewsDuringShow.Add(args.View); + }; + + // Call + mainWindow.Show(); + + // Assert + Assert.AreEqual(2, activatedViewsDuringShow.Count); + Assert.AreEqual(1, activatedViewsDuringShow.Count(v => v is MessageWindow)); + Assert.AreEqual(1, activatedViewsDuringShow.Count(v => v is PropertyGrid)); + } + mocks.VerifyAll(); + } + + [Test] + [STAThread] + public void Project_SetNewValue_FireProjectClosingAndOpenedEvents() + { + // Setup + var mocks = new MockRepository(); + var storeProject = mocks.Stub<IStoreProject>(); + mocks.ReplayAll(); + + using (var gui = new GuiCore(new MainWindow(), storeProject, new ApplicationCore(), new GuiCoreSettings())) + { + var oldProject = new Project("A"); + var newProject = new Project("B"); + + gui.Project = oldProject; + + int closingCallCount = 0; + gui.ProjectClosing += project => + { + if (closingCallCount == 0) + { + Assert.AreSame(oldProject, project); + } + else + { + // Dispose causes this one: + Assert.AreSame(newProject, project); + } + closingCallCount++; + }; + int openedCallCount = 0; + gui.ProjectOpened += project => + { + Assert.AreSame(newProject, project); + openedCallCount++; + }; + + // Call + gui.Project = newProject; + + // Assert + Assert.AreEqual(1, closingCallCount); + Assert.AreEqual(1, openedCallCount); + } + mocks.VerifyAll(); + } + + private static void AssertDefaultUserSettings(SettingsBase settings) + { + Assert.IsNotNull(settings); + Assert.AreEqual(15, settings.Properties.Count); + + // Note: Cannot assert particular values, as they can be changed by user. + var mruList = (StringCollection)settings["mruList"]; + Assert.IsNotNull(mruList); + var defaultViewDataTypes = (StringCollection)settings["defaultViewDataTypes"]; + Assert.IsNotNull(defaultViewDataTypes); + var defaultViews = (StringCollection)settings["defaultViews"]; + Assert.IsNotNull(defaultViews); + var lastVisitedPath = (string)settings["lastVisitedPath"]; + Assert.IsNotNull(lastVisitedPath); + var isMainWindowFullScreen = (bool)settings["MainWindow_FullScreen"]; + Assert.IsNotNull(isMainWindowFullScreen); + var x = (int)settings["MainWindow_X"]; + Assert.IsNotNull(x); + var y = (int)settings["MainWindow_Y"]; + Assert.IsNotNull(y); + var width = (int)settings["MainWindow_Width"]; + Assert.IsNotNull(width); + var height = (int)settings["MainWindow_Height"]; + Assert.IsNotNull(height); + var startPageName = (string)settings["startPageName"]; + Assert.IsNotNull(startPageName); + var showStartPage = (bool)settings["showStartPage"]; + Assert.IsNotNull(showStartPage); + var showSplashScreen = (bool)settings["showSplashScreen"]; + Assert.IsNotNull(showSplashScreen); + var showHiddenDataItems = (bool)settings["showHiddenDataItems"]; + Assert.IsNotNull(showHiddenDataItems); + var colorTheme = (ColorTheme)settings["colorTheme"]; + Assert.IsNotNull(colorTheme); + } + + private class ProjectExplorerMock : UserControl, IProjectExplorer + { + public object Data { get; set; } + public TreeViewControl TreeViewControl { get; private set; } + } + } +} \ No newline at end of file Fisheye: Tag 6ef8fa312a72f486be984ebc3adde4be044811e2 refers to a dead (removed) revision in file `Core/Common/test/Core.Common.Gui.Test/RingtoetsGuiTests.cs'. Fisheye: No comparison available. Pass `N' to diff? Index: Core/Common/test/Core.Common.Integration.Test/Ringtoets/Application.Ringtoets/RingtoetsGuiIntegrationTest.cs =================================================================== diff -u -r11ba203152ddf37106da76d9a38d4ffd771026dd -r6ef8fa312a72f486be984ebc3adde4be044811e2 --- Core/Common/test/Core.Common.Integration.Test/Ringtoets/Application.Ringtoets/RingtoetsGuiIntegrationTest.cs (.../RingtoetsGuiIntegrationTest.cs) (revision 11ba203152ddf37106da76d9a38d4ffd771026dd) +++ Core/Common/test/Core.Common.Integration.Test/Ringtoets/Application.Ringtoets/RingtoetsGuiIntegrationTest.cs (.../RingtoetsGuiIntegrationTest.cs) (revision 6ef8fa312a72f486be984ebc3adde4be044811e2) @@ -32,7 +32,7 @@ var projectStore = mocks.Stub<IStoreProject>(); mocks.ReplayAll(); - using (var gui = new RingtoetsGui(new MainWindow(), projectStore, new ApplicationCore(), new GuiCoreSettings())) + using (var gui = new GuiCore(new MainWindow(), projectStore, new ApplicationCore(), new GuiCoreSettings())) { var applicationCore = gui.ApplicationCore; @@ -59,7 +59,7 @@ var projectStore = mocks.Stub<IStoreProject>(); mocks.ReplayAll(); - using (var gui = new RingtoetsGui(new MainWindow(), projectStore, new ApplicationCore(), new GuiCoreSettings())) + using (var gui = new GuiCore(new MainWindow(), projectStore, new ApplicationCore(), new GuiCoreSettings())) { gui.Plugins.Add(new ProjectExplorerGuiPlugin()); gui.Run(); @@ -79,7 +79,7 @@ var projectStore = mocks.Stub<IStoreProject>(); mocks.ReplayAll(); - using (var gui = new RingtoetsGui(new MainWindow(), projectStore, new ApplicationCore(), new GuiCoreSettings())) + using (var gui = new GuiCore(new MainWindow(), projectStore, new ApplicationCore(), new GuiCoreSettings())) { gui.Run(); int callCount = 0; @@ -95,7 +95,7 @@ var projectStore = mocks.Stub<IStoreProject>(); mocks.ReplayAll(); - using (var gui = new RingtoetsGui(new MainWindow(), projectStore, new ApplicationCore(), new GuiCoreSettings())) + using (var gui = new GuiCore(new MainWindow(), projectStore, new ApplicationCore(), new GuiCoreSettings())) { var applicationCore = gui.ApplicationCore; Index: Core/Plugins/test/Core.Plugins.DotSpatial.Test/DotSpatialGuiPluginTest.cs =================================================================== diff -u -r5c3f702f76f617808344416749d1ec1cfb104559 -r6ef8fa312a72f486be984ebc3adde4be044811e2 --- Core/Plugins/test/Core.Plugins.DotSpatial.Test/DotSpatialGuiPluginTest.cs (.../DotSpatialGuiPluginTest.cs) (revision 5c3f702f76f617808344416749d1ec1cfb104559) +++ Core/Plugins/test/Core.Plugins.DotSpatial.Test/DotSpatialGuiPluginTest.cs (.../DotSpatialGuiPluginTest.cs) (revision 6ef8fa312a72f486be984ebc3adde4be044811e2) @@ -128,7 +128,7 @@ var projectStore = mocks.Stub<IStoreProject>(); mocks.ReplayAll(); - using (var gui = new RingtoetsGui(new MainWindow(), projectStore, new ApplicationCore(), new GuiCoreSettings())) + using (var gui = new GuiCore(new MainWindow(), projectStore, new ApplicationCore(), new GuiCoreSettings())) { var plugin = new DotSpatialGuiPlugin(); var testMapView = new TestMapView(); Index: Core/Plugins/test/Core.Plugins.OxyPlot.Test/OxyPlotGuiPluginTest.cs =================================================================== diff -u -r11ba203152ddf37106da76d9a38d4ffd771026dd -r6ef8fa312a72f486be984ebc3adde4be044811e2 --- Core/Plugins/test/Core.Plugins.OxyPlot.Test/OxyPlotGuiPluginTest.cs (.../OxyPlotGuiPluginTest.cs) (revision 11ba203152ddf37106da76d9a38d4ffd771026dd) +++ Core/Plugins/test/Core.Plugins.OxyPlot.Test/OxyPlotGuiPluginTest.cs (.../OxyPlotGuiPluginTest.cs) (revision 6ef8fa312a72f486be984ebc3adde4be044811e2) @@ -129,7 +129,7 @@ var projectStore = mocks.Stub<IStoreProject>(); mocks.ReplayAll(); - using (var gui = new RingtoetsGui(new MainWindow(), projectStore, new ApplicationCore(), new GuiCoreSettings())) + using (var gui = new GuiCore(new MainWindow(), projectStore, new ApplicationCore(), new GuiCoreSettings())) { var plugin = new OxyPlotGuiPlugin(); var testChartView = new TestChartView(); Index: Ringtoets.sln =================================================================== diff -u -rd212065881d696420fd33c789c917501e09c7fc0 -r6ef8fa312a72f486be984ebc3adde4be044811e2 --- Ringtoets.sln (.../Ringtoets.sln) (revision d212065881d696420fd33c789c917501e09c7fc0) +++ Ringtoets.sln (.../Ringtoets.sln) (revision 6ef8fa312a72f486be984ebc3adde4be044811e2) @@ -814,6 +814,7 @@ {9E35F3EA-D3C3-4D80-8399-689C49B68324} = {89F6BC2C-78E6-42F1-A056-704C877B5453} {FE36E7E0-D5F7-48E3-9730-D05443351EAD} = {89F6BC2C-78E6-42F1-A056-704C877B5453} {502CA963-E0E9-4F4A-A1D4-612C6A08386B} = {89F6BC2C-78E6-42F1-A056-704C877B5453} + {57F5BCD5-5759-4C9F-A841-2A6C36A910C8} = {89F6BC2C-78E6-42F1-A056-704C877B5453} {85F88AB0-CA03-48D3-B5A0-DF0848C24AB0} = {57F5BCD5-5759-4C9F-A841-2A6C36A910C8} {041EDC04-3A1E-4FBA-BEB7-F391571C609F} = {05D133D6-D7D8-4872-9B53-2753DD3F8BF9} {F8FFD6DD-19DA-47CE-A0BD-53F4E7D8BB86} = {05D133D6-D7D8-4872-9B53-2753DD3F8BF9}