aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/TextEditorWeakEventManager.cs
blob: 87057ead40fe66c849d272779f312dfdcaf0d80a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)

using Tango.Scripting.Editors.Utils;
using System;

namespace Tango.Scripting.Editors
{
	/// <summary>
	/// Contains weak event managers for <see cref="ITextEditorComponent"/>.
	/// </summary>
	public static class TextEditorWeakEventManager
	{
		/// <summary>
		/// Weak event manager for the <see cref="ITextEditorComponent.DocumentChanged"/> event.
		/// </summary>
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible")]
		public sealed class DocumentChanged : WeakEventManagerBase<DocumentChanged, ITextEditorComponent>
		{
			/// <inheritdoc/>
			protected override void StartListening(ITextEditorComponent source)
			{
				source.DocumentChanged += DeliverEvent;
			}
			
			/// <inheritdoc/>
			protected override void StopListening(ITextEditorComponent source)
			{
				source.DocumentChanged -= DeliverEvent;
			}
		}
		
		/// <summary>
		/// Weak event manager for the <see cref="ITextEditorComponent.OptionChanged"/> event.
		/// </summary>
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible")]
		public sealed class OptionChanged : WeakEventManagerBase<OptionChanged, ITextEditorComponent>
		{
			/// <inheritdoc/>
			protected override void StartListening(ITextEditorComponent source)
			{
				source.OptionChanged += DeliverEvent;
			}
			
			/// <inheritdoc/>
			protected override void StopListening(ITextEditorComponent source)
			{
				source.OptionChanged -= DeliverEvent;
			}
		}
	}
}
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using Tango.BL;
using Tango.BL.Builders;
using Tango.BL.Entities;
using Tango.Core.Commands;
using Tango.Core.DI;
using Tango.Integration.ExternalBridge;
using Tango.Logging;
using Tango.MachineStudio.Common;
using Tango.MachineStudio.Common.Authentication;
using Tango.MachineStudio.Common.Diagnostics;
using Tango.MachineStudio.Common.EventLogging;
using Tango.MachineStudio.Common.Messages;
using Tango.MachineStudio.Common.Modules;
using Tango.MachineStudio.Common.Navigation;
using Tango.MachineStudio.Common.Notifications;
using Tango.MachineStudio.Common.Speech;
using Tango.MachineStudio.Common.StudioApplication;
using Tango.MachineStudio.Common.Web;
using Tango.MachineStudio.UI.StudioApplication;
using Tango.MachineStudio.UI.SupervisingController;
using Tango.MachineStudio.UI.TFS;
using Tango.MachineStudio.UI.Views;
using Tango.MachineStudio.UI.Windows;
using Tango.PMR.Stubs;
using Tango.Settings;
using Tango.SharedUI;
using Tango.SharedUI.Controls;
using Tango.SharedUI.Helpers;
using Tango.Transport;
using Tango.Transport.Adapters;

namespace Tango.MachineStudio.UI.ViewModels
{
    /// <summary>
    /// Represents the Machine Studio main view, view model.
    /// </summary>
    /// <seealso cref="Tango.SharedUI.ViewModel{Tango.MachineStudio.UI.SupervisingController.IMainView}" />
    public class MainViewVM : StudioViewModel
    {
        private IStudioModule _currentModule;
        private INavigationManager _navigation;
        private bool _isDisconnecting;
        private Thread _updateCheckThread;
        private IEventLogger _eventLogger;
        private MachineStudioSettings _settings;
        private MachineStudioWebClient _machineStudioWebClient;

        /// <summary>
        /// Gets or sets the current loaded module.
        /// </summary>
        public IStudioModule CurrentModule
        {
            get { return _currentModule; }
            set { _currentModule = value; RaisePropertyChangedAuto(); }
        }

        private bool _isApplicationReady;
        public bool IsApplicationReady
        {
            get { return _isApplicationReady; }
            set { _isApplicationReady = value; RaisePropertyChangedAuto(); }
        }

        private bool _isModuleLoaded;
        /// <summary>
        /// Gets or sets a value indicating whether any module is loaded at the moment.
        /// </summary>
        public bool IsModuleLoaded
        {
            get { return _isModuleLoaded; }
            set { _isModuleLoaded = value; RaisePropertyChangedAuto(); }
        }

        private bool _isMenuOpened;
        /// <summary>
        /// Gets or sets a value indicating whether the side menu is opened.
        /// </summary>
        public bool IsMenuOpened
        {
            get { return _isMenuOpened; }
            set { _isMenuOpened = value; RaisePropertyChangedAuto(); }
        }

        private bool _isMachineErrorsOpened;
        /// <summary>
        /// Gets or sets a value indicating whether to display the machine errors.
        /// </summary>
        public bool IsMachineErrorsOpened
        {
            get { return _isMachineErrorsOpened; }
            set { _isMachineErrorsOpened = value; RaisePropertyChangedAuto(); }
        }

        /// <summary>
        /// Gets or sets the start module command.
        /// </summary>
        public RelayCommand<IStudioModule> StartModuleCommand { get; set; }

        /// <summary>
        /// Gets or sets the open module in window command.
        /// </summary>
        public RelayCommand<IStudioModule> OpenModuleInWindowCommand { get; set; }

        /// <summary>
        /// Gets or sets the home command.
        /// </summary>
        public RelayCommand HomeCommand { get; set; }

        /// <summary>
        /// Gets or sets the connect command.
        /// </summary>
        public RelayCommand ConnectCommand { get; set; }

        /// <summary>
        /// Gets or sets the disconnect command.
        /// </summary>
        public RelayCommand DisconnectCommand { get; set; }

        /// <summary>
        /// Gets or sets the sign-out command.
        /// </summary>
        public RelayCommand SignoutCommand { get; set; }

        /// <summary>
        /// Gets or sets the exit command.
        /// </summary>
        public RelayCommand ExitCommand { get; set; }

        /// <summary>
        /// Gets or sets the update center command.
        /// </summary>
        public RelayCommand UpdateCenterCommand { get; set; }

        /// <summary>
        /// Gets or sets the toggle speech command.
        /// </summary>
        public RelayCommand ToggleSpeechCommand { get; set; }

        /// <summary>
        /// Gets or sets the display HTML command.
        /// </summary>
        public RelayCommand<MachinesEvent> ResolveMachineEventCommand { get; set; }

        /// <summary>
        /// Gets or sets the report issue command.
        /// </summary>
        public RelayCommand ReportIssueCommand { get; set; }

        /// <summary>
        /// Gets or sets the open resolved bugs.
        /// </summary>
        public RelayCommand OpenResolvedBugsCommand { get; set; }

        /// <summary>
        /// Gets or sets the open developer console command.
        /// </summary>
        public RelayCommand OpenDeveloperConsoleCommand { get; set; }

        /// <summary>
        /// Gets or sets the about command.
        /// </summary>
        public RelayCommand AboutCommand { get; set; }

        private IAuthenticationProvider _authenticationProvider;
        /// <summary>
        /// Gets or sets the authentication provider.
        /// </summary>
        public IAuthenticationProvider AuthenticationProvider
        {
            get { return _authenticationProvider; }
            set { _authenticationProvider = value; RaisePropertyChangedAuto(); }
        }

        private IStudioModuleLoader _studioModuleLoader;
        /// <summary>
        /// Gets or sets the studio module loader.
        /// </summary>
        public IStudioModuleLoader StudioModuleLoader
        {
            get { return _studioModuleLoader; }
            set { _studioModuleLoader = value; RaisePropertyChangedAuto(); }
        }

        private INotificationProvider _notificationProvider;
        /// <summary>
        /// Gets or sets the notification provider.
        /// </summary>
        public INotificationProvider NotificationProvider
        {
            get { return _notificationProvider; }
            set { _notificationProvider = value; RaisePropertyChangedAuto(); }
        }

        private IStudioApplicationManager _applicationManager;
        /// <summary>
        /// Gets or sets the application manager.
        /// </summary>
        public IStudioApplicationManager ApplicationManager
        {
            get { return _applicationManager; }
            set { _applicationManager = value; RaisePropertyChangedAuto(); }
        }

        /// <summary>
        /// Gets or sets the TFS client.
        /// </summary>
        public TeamFoundationServiceExtendedClient TFSClient { get; set; }

        /// <summary>
        /// Gets or sets the speech provider.
        /// </summary>
        public ISpeechProvider SpeechProvider { get; set; }

        private bool _isUpdateAvailable;
        /// <summary>
        /// Gets or sets a value indicating whether a new version update is available.
        /// </summary>
        public bool IsUpdateAvailable
        {
            get { return _isUpdateAvailable; }
            set { _isUpdateAvailable = value; RaisePropertyChangedAuto(); }
        }

        private String _latestVersion;
        /// <summary>
        /// Gets or sets the latest version.
        /// </summary>
        public String LatestVersion
        {
            get { return _latestVersion; }
            set { _latestVersion = value; RaisePropertyChangedAuto(); }
        }

        private bool _disableCheckForUpdates;
        /// <summary>
        /// Gets or sets a value indicating whether [disable check for updates].
        /// </summary>
        public bool DisableCheckForUpdates
        {
            get { return _disableCheckForUpdates; }
            set { _disableCheckForUpdates = value; RaisePropertyChangedAuto(); }
        }

        private IDiagnosticsFrameProvider _diagnosticsFrameProvider;
        /// <summary>
        /// Gets or sets the diagnostics frame provider.
        /// </summary>
        public IDiagnosticsFrameProvider DiagnosticsFrameProvider
        {
            get { return _diagnosticsFrameProvider; }
            set { _diagnosticsFrameProvider = value; RaisePropertyChangedAuto(); }
        }


        /// <summary>
        /// Initializes a new instance of the <see cref="MainViewVM"/> class.
        /// </summary>
        /// <param name="view">The view.</param>
        /// <param name="authenticationProvider">The authentication provider.</param>
        /// <param name="studioModuleLoader">The studio module loader.</param>
        /// <param name="notificationProvider">The notification provider.</param>
        /// <param name="applicationManager">The application manager.</param>
        /// <param name="navigationManager">The navigation manager.</param>
        public MainViewVM(
            IAuthenticationProvider authenticationProvider,
            IStudioModuleLoader studioModuleLoader,
            INotificationProvider notificationProvider,
            IStudioApplicationManager applicationManager,
            INavigationManager navigationManager,
            IEventLogger eventLogger,
            IDiagnosticsFrameProvider frameProvider,
            ISpeechProvider speechProvider,
            TeamFoundationServiceExtendedClient tfs,
            MachineStudioWebClient machineStudioWebClient) : base()
        {
            _machineStudioWebClient = machineStudioWebClient;
            TFSClient = tfs;
            _eventLogger = eventLogger;
            _navigation = navigationManager;
            AuthenticationProvider = authenticationProvider;
            StudioModuleLoader = studioModuleLoader;
            NotificationProvider = notificationProvider;
            ApplicationManager = applicationManager;
            DiagnosticsFrameProvider = frameProvider;
            SpeechProvider = speechProvider;

            _settings = SettingsManager.Default.GetOrCreate<MachineStudioSettings>();

            StartModuleCommand = new RelayCommand<IStudioModule>(StartModule);

            HomeCommand = new RelayCommand(Home);
            ConnectCommand = new RelayCommand(ConnectToMachine);
            SignoutCommand = new RelayCommand(SignOut);
            DisconnectCommand = new RelayCommand(DisconnectFromMachine, (x) => ApplicationManager.IsMachineConnected && !_isDisconnecting);
            OpenModuleInWindowCommand = new RelayCommand<IStudioModule>((x) => { OpenModuleInWindow(x); });
            ExitCommand = new RelayCommand(ExitApplication);
            UpdateCenterCommand = new RelayCommand(NavigateToUpdateCenter);

            _updateCheckThread = new Thread(UpdateCheckThreadMethod);
            _updateCheckThread.IsBackground = true;
            _updateCheckThread.Start();

            ApplicationManager.ConnectedMachineChanged += (sender, machine) =>
            {
                if (machine != null)
                {
                    machine.MachineEventsStateProvider.NewEvents -= MachineEventsStateProvider_NewEvents;
                    machine.MachineEventsStateProvider.NewEvents += MachineEventsStateProvider_NewEvents;

                    machine.MachineEventsStateProvider.EventsResolved -= MachineEventsStateProvider_EventsResolved;
                    machine.MachineEventsStateProvider.EventsResolved += MachineEventsStateProvider_EventsResolved;
                }
            };

            ToggleSpeechCommand = new RelayCommand(() => { SpeechProvider.Mute = !SpeechProvider.Mute; });

            ResolveMachineEventCommand = new RelayCommand<MachinesEvent>(ResolveMachineEvent);

            ReportIssueCommand = new RelayCommand(ReportIssue);
            OpenResolvedBugsCommand = new RelayCommand(OpenResolvedBugs);
            OpenDeveloperConsoleCommand = new RelayCommand(OpenDeveloperConsole);

            TangoMessenger.Default.Register<Messages.ForcedUpdateMessage>((x) => DisableCheckForUpdates = true);

            AboutCommand = new RelayCommand(ShowAboutDialog);
        }

        private void MachineEventsStateProvider_EventsResolved(object sender, IEnumerable<MachinesEvent> e)
        {
            if (ApplicationManager.ConnectedMachine.MachineEventsStateProvider.Events.Count == 0)
            {
                IsMachineErrorsOpened = false;
            }
        }

        private void MachineEventsStateProvider_NewEvents(object sender, IEnumerable<MachinesEvent> e)
        {
            IsMachineErrorsOpened = true;
        }

        private void UpdateCheckThreadMethod()
        {
            while (!DisableCheckForUpdates)
            {
                Thread.Sleep(TimeSpan.FromMinutes(0.2));

                try
                {
                    if (_authenticationProvider.CurrentUser != null)
                    {
                        CheckForUpdatesResponse response = _machineStudioWebClient.CheckForUpdates(new CheckForUpdatesRequest()
                        {
                            Version = _applicationManager.Version.ToString(),
                        }).Result;

                        IsUpdateAvailable = response.IsUpdateAvailable;
                        LatestVersion = response.Version;
                    }
                }
                catch (Exception ex)
                {
                    LogManager.Log(ex, "Error in version update periodic check...");
                }

                Thread.Sleep(TimeSpan.FromMinutes(4));
            }
        }

        /// <summary>
        /// Disconnected from the current connected machine.
        /// </summary>
        private async void DisconnectFromMachine()
        {
            using (_notificationProvider.PushTaskItem("Disconnecting from machine..."))
            {
                try
                {
                    _isDisconnecting = true;
                    InvalidateRelayCommands();
                    String serial = ApplicationManager.ConnectedMachine.SerialNumber;
                    await ApplicationManager.ConnectedMachine.Disconnect();
                    ApplicationManager.SetConnectedMachine(null);
                    _eventLogger.Log("Disconnected from machine " + serial);

                    PostMessage(new MachineConnectionChangedMessage() { Machine = null });
                }
                catch (Exception ex)
                {
                    _eventLogger.Log(ex, "Error disconnecting from machine.");
                    LogManager.Log(ex, "Could not disconnect from machine.");
                }
                finally
                {
                    _isDisconnecting = false;
                    InvalidateRelayCommands();
                }
            }
        }

        /// <summary>
        /// Signs-out the current logged-in user.
        /// </summary>
        private void SignOut()
        {
            _authenticationProvider.Logout();
            Home();
            _navigation.NavigateTo(NavigationView.LoginView);
            CurrentModule = null;
            IsMenuOpened = false;
        }

        /// <summary>
        /// Opens the machine connection dialog to allow the user to scan and connect to remote machines view USB/TCP.
        /// </summary>
        private void ConnectToMachine()
        {
            if (ApplicationManager.ConnectedMachine == null)
            {
                _notificationProvider.ShowModalDialog<MachineConnectionViewVM>(async (x) =>
                {
                    if (x.SelectedMachine != null)
                    {
                        if (ApplicationManager.IsMachineConnected)
                        {
                            using (_notificationProvider.PushTaskItem("Disconnecting..."))
                            {
                                await ApplicationManager.ConnectedMachine.Disconnect();
                                await Task.Delay(1000);
                            }
                        }

                        x.SelectedMachine.EnableDiagnostics = x.EnableDiagnostics;
                        x.SelectedMachine.EnableEmbeddedDebugging = x.EnableDiagnostics;
                        x.SelectedMachine.EnableEventsNotification = x.EnableDiagnostics;
                        x.SelectedMachine.UseKeepAlive = x.EnableKeepAlive;

                        if (x.SelectedMachine.RequiresAuthentication)
                        {
                            //Check machine exist on my database first
                            if (x.SelectedMachine.Machine == null)
                            {
                                _notificationProvider.ShowError($"The specified machine '{x.SelectedMachine.SerialNumber}' could not be found on the database. Aborting connection.");
                                return;
                            }

                            _notificationProvider.ShowModalDialog<MachineLoginViewVM>(async (login) =>
                            {
                                using (NotificationProvider.PushTaskItem("Connecting to machine " + x.SelectedMachine.ToString() + "..."))
                                {
                                    try
                                    {
                                        await x.SelectedMachine.As<IExternalBridgeSecureClient>().Connect(new PMR.Integration.ExternalBridgeLoginRequest()
                                        {
                                            AppID = "Machine Studio",
                                            HostName = Environment.MachineName,
                                            Password = login.Password,
                                            UserGuid = AuthenticationProvider.CurrentUser.Guid,
                                            Intent = login.Intent,
                                        });

                                        ApplicationManager.SetConnectedMachine(x.SelectedMachine);
                                        (x.SelectedMachine as IExternalBridgeSecureClient).SessionClosed += (_, __) =>
                                        {
                                            InvokeUI(() =>
                                            {
                                                _notificationProvider.ShowError("The remote machine has closed the current session. Machine disconnected.");
                                                ApplicationManager.SetConnectedMachine(null);
                                            });
                                        };
                                        PostMessage(new MachineConnectionChangedMessage() { Machine = x.SelectedMachine });
                                        _eventLogger.Log(String.Format("Successfully connected to machine {0} via TCP", x.SelectedMachine.SerialNumber));

                                        if (x.UploadHardwareConfiguration)
                                        {
                                            UploadHardwareConfiguration(false);
                                        }

                                    }
                                    catch (ResponseErrorException ex)
                                    {
                                        LogManager.Log(ex);
                                        _eventLogger.Log(ex, "Error connecting to machine " + x.SelectedMachine.SerialNumber);
                                        _notificationProvider.ShowError("Could not connect to the selected machine." + Environment.NewLine + ex.Container.ErrorMessage);
                                    }
                                    catch (Exception ex)
                                    {
                                        LogManager.Log(ex);
                                        _eventLogger.Log(ex, "Error connecting to machine " + x.SelectedMachine.SerialNumber);
                                        _notificationProvider.ShowError("Could not connect to the selected machine." + Environment.NewLine + ex.Message);
                                    }

                                    InvalidateRelayCommands();
                                }
                            });
                        }
                        else
                        {
                            _notificationProvider.ShowModalDialog<MachineSerialViewVM>(async (vm) =>
                            {
                                if (vm.SelectedMachine != null)
                                {
                                    using (NotificationProvider.PushTaskItem("Connecting to " + x.SelectedMachine.ToString() + "..."))
                                    {
                                        try
                                        {
                                            await x.SelectedMachine.Connect();
                                            x.SelectedMachine.SerialNumber = vm.SelectedMachine.SerialNumber;
                                            ApplicationManager.SetConnectedMachine(x.SelectedMachine);

                                            PostMessage(new MachineConnectionChangedMessage() { Machine = x.SelectedMachine });
                                            _eventLogger.Log(String.Format("Successfully connected to machine {0} via USB", x.SelectedMachine.SerialNumber));
                                            _settings.LastVirtualMachineSerialNumber = vm.SelectedMachine.SerialNumber;
                                            _settings.Save();

                                            if (x.UploadHardwareConfiguration)
                                            {
                                                UploadHardwareConfiguration(false);
                                            }
                                        }
                                        catch (Exception ex)
                                        {
                                            LogManager.Log(ex);

                                            if (x.SelectedMachine != null)
                                            {
                                                _eventLogger.Log(ex, "Error connecting to machine " + x.SelectedMachine.SerialNumber);
                                            }

                                            _notificationProvider.ShowError("Could not connect to the selected machine." + Environment.NewLine + ex.Message);

                                        }

                                        InvalidateRelayCommands();
                                    }
                                }
                            });
                        }

                        InvalidateRelayCommands();
                    }

                    InvalidateRelayCommands();
                });
            }
            else
            {
                _notificationProvider.ShowModalDialog<ConnectedMachineViewVM>(async (x) =>
                {
                    if (x.Result == ConnectedMachineViewVM.ConnectedMachineVMResult.Disconnect)
                    {
                        DisconnectFromMachine();
                    }
                    else if (x.Result == ConnectedMachineViewVM.ConnectedMachineVMResult.UploadHardwareConfig)
                    {
                        if (NotificationProvider.ShowQuestion("This will reset the machine hardware configuration to the database configuration. Are you sure?"))
                        {
                            using (NotificationProvider.PushTaskItem("Uploading hardware configuration..."))
                            {
                                UploadHardwareConfiguration();
                            }
                        }
                    }
                    else if (x.Result == ConnectedMachineViewVM.ConnectedMachineVMResult.Reset)
                    {
                        if (NotificationProvider.ShowQuestion("This will reset the embedded device. Are you sure?"))
                        {
                            using (NotificationProvider.PushTaskItem("Resetting the embedded device..."))
                            {
                                try
                                {
                                    await ApplicationManager.ConnectedMachine.Reset();
                                    NotificationProvider.ShowInfo("Embedded device has been restarted.");
                                }
                                catch (Exception ex)
                                {
                                    LogManager.Log(ex, "Error resetting embedded device.");
                                    NotificationProvider.ShowError("Error resetting embedded device." + Environment.NewLine + ex.Message);
                                }
                            }
                        }
                    }
                    else if (x.Result == ConnectedMachineViewVM.ConnectedMachineVMResult.TurnOffHeaters)
                    {
                        if (NotificationProvider.ShowQuestion("This will reset the process parameters. Are you sure?"))
                        {
                            using (NotificationProvider.PushTaskItem("Resetting process parameters..."))
                            {
                                try
                                {
                                    await ApplicationManager.ConnectedMachine.UploadProcessParameters(new ProcessParametersTable());
                                    NotificationProvider.ShowInfo("Heaters are turned off.");
                                }
                                catch (Exception ex)
                                {
                                    LogManager.Log(ex, "Error resetting process parameters.");
                                    NotificationProvider.ShowError("Error resetting process parameters." + Environment.NewLine + ex.Message);
                                }
                            }
                        }
                    }
                });
            }

            InvalidateRelayCommands();
        }

        private async void UploadHardwareConfiguration(bool showSuccessMessage = true)
        {
            try
            {
                using (ObservablesContext db = ObservablesContext.CreateDefault())
                {
                    var config = (await new ConfigurationBuilder(db).Set(ApplicationManager.Machine.ConfigurationGuid).WithIdsPacks().WithHardwareVersion().BuildAsync());

                    await ApplicationManager.ConnectedMachine.UploadHardwareConfiguration(config.HardwareVersion, config);
                }

                if (showSuccessMessage)
                {
                    NotificationProvider.ShowInfo("Hardware configuration uploaded successfully.");
                }
            }
            catch (Exception ex)
            {
                LogManager.Log(ex, "Error uploading hardware configuration.");
                NotificationProvider.ShowError("Error uploading hardware configuration." + Environment.NewLine + ex.Message);
            }
        }

        /// <summary>
        /// Navigates to the home screen.
        /// </summary>
        private void Home()
        {
            StartModule(null);
        }

        /// <summary>
        /// Starts the specified module.
        /// </summary>
        /// <param name="module">The module.</param>
        internal void StartModule(IStudioModule module)
        {
            IsMenuOpened = false;

            if ((_applicationManager as DefaultStudioApplicationManager).IsModuleInNewWindow(module)) return;

            if (module != null)
            {
                LogManager.Log(String.Format("Starting module '{0}'...", module.Name));

                if (!(MainView.Self as MainView).NavigationControl.Elements.ToList().Exists(x => x.GetType() == module.MainViewType))
                {
                    LogManager.Log("Module was not initialized. Initializing...");

                    FrameworkElement view = Activator.CreateInstance(module.MainViewType) as FrameworkElement;
                    NavigationControl.SetNavigationName(view, module.Name);
                    (MainView.Self as MainView).NavigationControl.Elements.Add(view);
                }
            }

            foreach (var m in StudioModuleLoader.AllModules.Where(x => x != module && !x.InNewWindow))
            {
                m.IsLoaded = false;
            }

            CurrentModule = module;

            if (module != null)
            {
                CurrentModule.IsLoaded = true;
                IsModuleLoaded = true;

                LogManager.Log(String.Format("Navigating to module '{0}'...", module.Name));
                (MainView.Self as MainView).NavigationControl.NavigateTo(module.Name);
            }
            else
            {
                IsModuleLoaded = false;
                LogManager.Log(String.Format("Navigating to Home..."));
                (MainView.Self as MainView).NavigationControl.NavigateTo("Home");
            }
        }

        /// <summary>
        /// Opens the module in a new window.
        /// </summary>
        /// <param name="module">The module.</param>
        private void OpenModuleInWindow(IStudioModule module, Rect? bounds = null, WindowState? state = null)
        {
            if (module == null) return;

            try
            {
                module.InNewWindow = true;

                StartModule(null);

                LogManager.Log(String.Format("Starting module '{0}' in new window...", module.Name));

                if (!(MainView.Self as MainView).NavigationControl.Elements.ToList().Exists(x => x.GetType() == module.MainViewType))
                {
                    LogManager.Log("Module was not initialized. Initializing...");
                    FrameworkElement v = Activator.CreateInstance(module.MainViewType) as FrameworkElement;
                    NavigationControl.SetNavigationName(v, module.Name);
                    (MainView.Self as MainView).NavigationControl.Elements.Add(v);
                }

                LogManager.Log("Detaching module view...");
                var view = (MainView.Self as MainView).NavigationControl.GetAndDetach(module.Name);

                ModuleWindowVM vm = new ModuleWindowVM(module);
                ModuleWindow window = new ModuleWindow(this, vm, view);

                if (bounds.HasValue)
                {
                    window.WindowStartupLocation = WindowStartupLocation.Manual;
                    window.WindowState = WindowState.Normal;
                    window.Left = bounds.Value.Left;
                    window.Top = bounds.Value.Top;
                    window.Width = bounds.Value.Width;
                    window.Height = bounds.Value.Height;
                    window.Loaded += (_, __) =>
                    {
                        window.WindowState = state.Value;
                    };
                }

                window.Closing += (x, y) =>
                {
                    LogManager.Log(String.Format("Closing module '{0}' on new window...", module.Name));

                    window.grid.Children.Remove(view);
                    module.InNewWindow = false;
                };

                window.Owner = MainWindow.Instance;

                LogManager.Log("Opening new window...");
                window.Show();

                (_applicationManager as DefaultStudioApplicationManager).RegisterOpenedWindow(window);
            }
            catch (Exception ex)
            {
                LogManager.Log(ex, "Error popping out module " + module.Name);
                _notificationProvider.ShowError("Error popping out module " + module.Name);
            }
        }

        /// <summary>
        /// Navigates to update center.
        /// </summary>
        private void NavigateToUpdateCenter()
        {
            _navigation.NavigateTo(NavigationView.UpdateView);
        }

        /// <summary>
        /// Exits the application.
        /// </summary>
        private void ExitApplication()
        {
            _applicationManager.ShutDown();
        }

        /// <summary>
        /// Displays the HTML.
        /// </summary>
        /// <param name="machineEvent">The HTML page.</param>
        private async void ResolveMachineEvent(MachinesEvent machineEvent)
        {

        }

        /// <summary>
        /// Shows the about dialog.
        /// </summary>
        private void ShowAboutDialog()
        {
            NotificationProvider.ShowModalDialog<AboutViewVM>((x) => { });
        }

        private void ReportIssue()
        {
            Tango.TFS.WorkItem bug = null;

            try
            {
                bug = TFSClient.CreateBug();
            }
            catch (Exception ex)
            {
                _notificationProvider.ShowError(ex.Message);
                return;
            }

            _notificationProvider.ShowModalDialog<ReportIssueViewVM, ReportIssueView>(new ReportIssueViewVM(TFSClient.Project, bug), async (vm) =>
             {
                 using (_notificationProvider.PushTaskItem("Uploading bug report..."))
                 {
                     try
                     {
                         TFSClient.FinalizeBug(vm.WorkItem);
                         await TFSClient.UploadWorkItem(vm.WorkItem);
                     }
                     catch (Exception ex)
                     {
                         LogManager.Log(ex, "Error while trying to upload bug report.");
                         _notificationProvider.ShowError("An error occurred while trying to create the issue." + Environment.NewLine + ex.Message);
                     }
                 }

             }, null);
        }

        private void OpenResolvedBugs()
        {
            ResolvedIssuesViewVM vm = null;

            vm = new ResolvedIssuesViewVM(TFSClient, async (item) =>
            {
                //Approve
                using (_notificationProvider.PushTaskItem("Approving issue..."))
                {
                    vm.IsAvailable = false;
                    try
                    {
                        await TFSClient.CloseWorkItem(item);
                    }
                    catch (Exception ex)
                    {
                        LogManager.Log(ex, $"Error while trying to approve resolved bug '{item.ID}'.");
                        _notificationProvider.ShowError("An error occurred while trying to update the issue." + Environment.NewLine + ex.Message);
                    }
                    vm.IsAvailable = true;

                    if (TFSClient.ResolvedWorkItems.Count == 0)
                    {
                        vm.Close();
                    }
                }
            }, async (item) =>
            {
                //Decline
                using (_notificationProvider.PushTaskItem("Reactivating issue..."))
                {
                    vm.IsAvailable = false;
                    try
                    {
                        await TFSClient.ReactivateWorkItem(item);
                    }
                    catch (Exception ex)
                    {
                        LogManager.Log(ex, $"Error while trying to decline resolved bug '{item.ID}'.");
                        _notificationProvider.ShowError("An error occurred while trying to update the issue." + Environment.NewLine + ex.Message);
                    }

                    vm.IsAvailable = true;

                    if (TFSClient.ResolvedWorkItems.Count == 0)
                    {
                        vm.Close();
                    }
                }
            });

            _notificationProvider.ShowModalDialog<ResolvedIssuesViewVM, ResolvedIssuesView>(vm, (_) => { }, null);
        }

        private void OpenDeveloperConsole()
        {
            Console.ConsoleWindow console = new Console.ConsoleWindow();
            ApplicationManager.RegisterOpenedWindow(console);
            console.Owner = MainWindow.Instance;
            console.Show();
        }

        public override void OnApplicationReady()
        {
            if (!IsApplicationReady)
            {
                if (!Keyboard.IsKeyDown(Key.LeftCtrl))
                {
                    var settings = SettingsManager.Default.GetOrCreate<MachineStudioSettings>();

                    foreach (var item in settings.StudioModulesBounds)
                    {
                        var module = StudioModuleLoader.AllModules.SingleOrDefault(x => x.Name == item.Name);

                        if (module != null && !module.InNewWindow)
                        {
                            OpenModuleInWindow(module, item.Bounds, item.State);
                        }
                    }

                    if (settings.LastMainModuleName != null)
                    {
                        var m = StudioModuleLoader.UserModules.SingleOrDefault(x => x.Name == settings.LastMainModuleName);

                        if (m != null)
                        {
                            StartModule(m);
                        }
                    }
                }

                IsApplicationReady = true;
            }
        }
    }
}