Merge pull request #352 from mRemoteNG/299_better_testing_of_connection_tree

Refactor connection tree to be more testable
This commit is contained in:
David Sparer
2017-01-18 09:03:26 -07:00
committed by GitHub
42 changed files with 1547 additions and 836 deletions

View File

@@ -0,0 +1,46 @@
using System;
using mRemoteNG.Connection;
using mRemoteNG.Container;
using mRemoteNG.Tree;
using mRemoteNG.UI.Controls;
using NSubstitute;
using NUnit.Framework;
namespace mRemoteNGTests.Tree
{
public class ExpandNodeClickHandlerTests
{
private ExpandNodeClickHandler _clickHandler;
private IConnectionTree _connectionTree;
[SetUp]
public void Setup()
{
_connectionTree = Substitute.For<IConnectionTree>();
_clickHandler = new ExpandNodeClickHandler(_connectionTree);
}
[Test]
public void TargetedNodeIsExpanded()
{
var folder = new ContainerInfo();
_clickHandler.Execute(folder);
_connectionTree.Received().ToggleExpansion(folder);
}
[Test]
public void NothingHappensWhenConnectionInfoProvided()
{
_clickHandler.Execute(new ConnectionInfo());
_connectionTree.DidNotReceiveWithAnyArgs().ToggleExpansion(new ConnectionInfo());
}
[Test]
public void ExceptionThrownOnConstructorNullArg()
{
// ReSharper disable once ObjectCreationAsStatement
Assert.Throws<ArgumentNullException>(() => new ExpandNodeClickHandler(null));
}
}
}

View File

@@ -0,0 +1,51 @@
using System;
using mRemoteNG.Connection;
using mRemoteNG.Container;
using mRemoteNG.Tree;
using NSubstitute;
using NUnit.Framework;
namespace mRemoteNGTests.Tree
{
public class OpenConnectionClickHandlerTests
{
private OpenConnectionClickHandler _clickHandler;
private IConnectionInitiator _connectionInitiator;
[SetUp]
public void Setup()
{
_connectionInitiator = Substitute.For<IConnectionInitiator>();
_clickHandler = new OpenConnectionClickHandler(_connectionInitiator);
}
[Test]
public void ConnectionOpened()
{
var connectionInfo = new ConnectionInfo();
_clickHandler.Execute(connectionInfo);
_connectionInitiator.Received().OpenConnection(connectionInfo);
}
[Test]
public void DoesNothingWhenGivenContainerInfo()
{
_clickHandler.Execute(new ContainerInfo());
_connectionInitiator.DidNotReceiveWithAnyArgs().OpenConnection(new ConnectionInfo());
}
[Test]
public void ExceptionThrownWhenConstructorGivenNullArg()
{
// ReSharper disable once ObjectCreationAsStatement
Assert.Throws<ArgumentNullException>(() => new OpenConnectionClickHandler(null));
}
[Test]
public void ThrowWhenExecuteGivenNullArg()
{
Assert.Throws<ArgumentNullException>(() => _clickHandler.Execute(null));
}
}
}

View File

@@ -0,0 +1,51 @@
using System;
using mRemoteNG.Connection;
using mRemoteNG.Container;
using mRemoteNG.Tree;
using NSubstitute;
using NUnit.Framework;
namespace mRemoteNGTests.Tree
{
public class SwitchToConnectionClickHandlerTests
{
private SwitchToConnectionClickHandler _clickHandler;
private IConnectionInitiator _connectionInitiator;
[SetUp]
public void Setup()
{
_connectionInitiator = Substitute.For<IConnectionInitiator>();
_clickHandler = new SwitchToConnectionClickHandler(_connectionInitiator);
}
[Test]
public void SwitchesToConnection()
{
var connectionInfo = new ConnectionInfo();
_clickHandler.Execute(connectionInfo);
_connectionInitiator.Received().SwitchToOpenConnection(connectionInfo);
}
[Test]
public void DoesNothingWhenGivenContainerInfo()
{
_clickHandler.Execute(new ContainerInfo());
_connectionInitiator.DidNotReceiveWithAnyArgs().SwitchToOpenConnection(new ConnectionInfo());
}
[Test]
public void ExceptionThrownWhenConstructorGivenNullArg()
{
// ReSharper disable once ObjectCreationAsStatement
Assert.Throws<ArgumentNullException>(() => new SwitchToConnectionClickHandler(null));
}
[Test]
public void ThrowWhenExecuteGivenNullArg()
{
Assert.Throws<ArgumentNullException>(() => _clickHandler.Execute(null));
}
}
}

View File

@@ -0,0 +1,39 @@
using System;
using mRemoteNG.Connection;
using mRemoteNG.Tree;
using NSubstitute;
using NUnit.Framework;
namespace mRemoteNGTests.Tree
{
public class TreeNodeCompositeClickHandlerTests
{
private TreeNodeCompositeClickHandler _clickHandler;
private ConnectionInfo _connectionInfo;
[SetUp]
public void Setup()
{
_clickHandler = new TreeNodeCompositeClickHandler();
_connectionInfo = new ConnectionInfo();
}
[Test]
public void ExecutesAllItsHandlers()
{
var handler1 = Substitute.For<ITreeNodeClickHandler>();
var handler2 = Substitute.For<ITreeNodeClickHandler>();
_clickHandler.ClickHandlers = new[] {handler1, handler2};
_clickHandler.Execute(_connectionInfo);
handler1.Received().Execute(_connectionInfo);
handler2.Received().Execute(_connectionInfo);
}
[Test]
public void ThrowWhenExecuteGivenNullArg()
{
Assert.Throws<ArgumentNullException>(() => _clickHandler.Execute(null));
}
}
}

View File

@@ -0,0 +1,51 @@
using System.Threading;
using mRemoteNG.Container;
using mRemoteNG.Tree;
using mRemoteNG.Tree.Root;
using mRemoteNG.UI.Controls;
using NUnit.Framework;
namespace mRemoteNGTests.Tree
{
public class ConnectionTreeTests
{
private ConnectionTree _connectionTree;
private ConnectionTreeModel _connectionTreeModel;
[SetUp]
public void Setup()
{
_connectionTreeModel = CreateConnectionTreeModel();
_connectionTree = new ConnectionTree
{
PostSetupActions = new IConnectionTreeDelegate[] {new RootNodeExpander()}
};
}
[TearDown]
public void Teardown()
{
_connectionTree.Dispose();
}
[Test, Apartment(ApartmentState.STA)]
public void CanDeleteLastFolderInTheTree()
{
var lastFolder = new ContainerInfo();
_connectionTreeModel.RootNodes[0].AddChild(lastFolder);
_connectionTree.ConnectionTreeModel = _connectionTreeModel;
_connectionTree.SelectObject(lastFolder);
_connectionTree.DeleteSelectedNode();
Assert.That(_connectionTree.GetRootConnectionNode().HasChildren, Is.False);
}
private ConnectionTreeModel CreateConnectionTreeModel()
{
var connectionTreeModel = new ConnectionTreeModel();
connectionTreeModel.AddRootNode(new RootNodeInfo(RootNodeType.Connection));
return connectionTreeModel;
}
}
}

View File

@@ -0,0 +1,50 @@
using System;
using mRemoteNG.Connection;
using mRemoteNG.Tree;
using mRemoteNG.Tree.Root;
using mRemoteNG.UI.Controls;
using NSubstitute;
using NUnit.Framework;
namespace mRemoteNGTests.Tree
{
public class PreviousSessionOpenerTests
{
private PreviousSessionOpener _previousSessionOpener;
private IConnectionInitiator _connectionInitiator;
private IConnectionTree _connectionTree;
[SetUp]
public void Setup()
{
_connectionInitiator = Substitute.For<IConnectionInitiator>();
_previousSessionOpener = new PreviousSessionOpener(_connectionInitiator);
_connectionTree = Substitute.For<IConnectionTree>();
_connectionTree.GetRootConnectionNode().Returns(BuildTree());
}
[Test]
public void AllRequestedSessionsAreReopened()
{
_previousSessionOpener.Execute(_connectionTree);
_connectionInitiator.ReceivedWithAnyArgs(2).OpenConnection(new ConnectionInfo());
}
[Test]
public void ExceptionThrownWhenConstructorGivenNullArg()
{
// ReSharper disable once ObjectCreationAsStatement
Assert.Throws<ArgumentNullException>(() => new PreviousSessionOpener(null));
}
private RootNodeInfo BuildTree()
{
var root = new RootNodeInfo(RootNodeType.Connection);
root.AddChild(new ConnectionInfo { PleaseConnect = true });
root.AddChild(new ConnectionInfo());
root.AddChild(new ConnectionInfo { PleaseConnect = true });
return root;
}
}
}

View File

@@ -0,0 +1,49 @@
using System.Linq;
using mRemoteNG.Connection;
using mRemoteNG.Container;
using mRemoteNG.Tree;
using mRemoteNG.Tree.Root;
using mRemoteNG.UI.Controls;
using NSubstitute;
using NUnit.Framework;
namespace mRemoteNGTests.Tree
{
public class PreviouslyOpenedFolderExpanderTests
{
private PreviouslyOpenedFolderExpander _folderExpander;
private IConnectionTree _connectionTree;
[SetUp]
public void Setup()
{
_folderExpander = new PreviouslyOpenedFolderExpander();
_connectionTree = Substitute.For<IConnectionTree>();
}
[Test]
public void ExpandsAllFoldersThatAreMarkedForExpansion()
{
var connectionTreeModel = GenerateConnectionTreeModel();
_connectionTree.ConnectionTreeModel.Returns(connectionTreeModel);
_connectionTree.GetRootConnectionNode().Returns(connectionTreeModel.RootNodes[0]);
_folderExpander.Execute(_connectionTree);
Assert.That(_connectionTree.ExpandedObjects, Is.EquivalentTo(connectionTreeModel.GetRecursiveChildList().OfType<ContainerInfo>().Where(info => info.IsExpanded)));
}
private ConnectionTreeModel GenerateConnectionTreeModel()
{
var connectionTreeModel = new ConnectionTreeModel();
var root = new RootNodeInfo(RootNodeType.Connection) { IsExpanded = true };
var folder1 = new ContainerInfo { IsExpanded = true };
var folder2 = new ContainerInfo();
var con1 = new ConnectionInfo();
root.AddChild(folder1);
folder1.AddChild(folder2);
root.AddChild(con1);
connectionTreeModel.AddRootNode(root);
return connectionTreeModel;
}
}
}

View File

@@ -0,0 +1,47 @@
using System.Windows.Forms;
using mRemoteNG.Connection;
using mRemoteNG.Tree;
using mRemoteNG.UI.Controls;
using NSubstitute;
using NUnit.Framework;
namespace mRemoteNGTests.Tree
{
public class SelectedConnectionDeletionConfirmerTests
{
private SelectedConnectionDeletionConfirmer _deletionConfirmer;
private IConnectionTree _connectionTree;
[SetUp]
public void Setup()
{
_connectionTree = Substitute.For<IConnectionTree>();
_connectionTree.SelectedNode.Returns(new ConnectionInfo());
}
[Test]
public void ClickingYesReturnsTrue()
{
_deletionConfirmer = new SelectedConnectionDeletionConfirmer(_connectionTree, MockClickYes);
Assert.That(_deletionConfirmer.Confirm(), Is.True);
}
[Test]
public void ClickingNoReturnsFalse()
{
_deletionConfirmer = new SelectedConnectionDeletionConfirmer(_connectionTree, MockClickNo);
Assert.That(_deletionConfirmer.Confirm(), Is.False);
}
private DialogResult MockClickYes(string promptMessage, string title, MessageBoxButtons buttons, MessageBoxIcon icon)
{
return DialogResult.Yes;
}
private DialogResult MockClickNo(string promptMessage, string title, MessageBoxButtons buttons, MessageBoxIcon icon)
{
return DialogResult.No;
}
}
}

View File

@@ -0,0 +1,32 @@
using System.Threading;
using mRemoteNG.UI.Window;
using NUnit.Framework;
using WeifenLuo.WinFormsUI.Docking;
namespace mRemoteNGTests.UI.Window
{
public class ConnectionTreeWindowTests
{
private ConnectionTreeWindow _connectionTreeWindow;
[SetUp]
public void Setup()
{
_connectionTreeWindow = new ConnectionTreeWindow(new DockContent());
}
[TearDown]
public void Teardown()
{
_connectionTreeWindow.Close();
}
[Test, Apartment(ApartmentState.STA)]
public void CanShowWindow()
{
_connectionTreeWindow.Show();
Assert.That(_connectionTreeWindow.Visible);
}
}
}

View File

@@ -101,7 +101,9 @@
</When>
<Otherwise>
<ItemGroup>
<Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework" />
<Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework">
<Private>False</Private>
</Reference>
</ItemGroup>
</Otherwise>
</Choose>
@@ -128,6 +130,7 @@
<Compile Include="Security\KeyDerivation\Pkcs5S2KeyGeneratorTests.cs" />
<Compile Include="Security\SecureStringExtensionsTests.cs" />
<Compile Include="Tools\ExternalToolsArgumentParserTests.cs" />
<Compile Include="Tree\ClickHandlers\TreeNodeCompositeClickHandlerTests.cs" />
<Compile Include="Tree\ConnectionTreeDragAndDropHandlerTests.cs" />
<Compile Include="Tree\ConnectionTreeModelTests.cs" />
<Compile Include="Connection\ConnectionInfoInheritanceTests.cs" />
@@ -147,8 +150,15 @@
<Compile Include="Security\CryptographyProviderFactoryTests.cs" />
<Compile Include="Security\EncryptedSecureStringTests.cs" />
<Compile Include="Security\LegacyRijndaelCryptographyProviderTests.cs" />
<Compile Include="Tree\ConnectionTreeTests.cs" />
<Compile Include="Tree\ClickHandlers\ExpandNodeClickHandlerTests.cs" />
<Compile Include="Tree\NodeSearcherTests.cs" />
<Compile Include="Tree\ClickHandlers\OpenConnectionClickHandlerTests.cs" />
<Compile Include="Tree\PreviouslyOpenedFolderExpanderTests.cs" />
<Compile Include="Tree\PreviousSessionOpenerTests.cs" />
<Compile Include="Tree\RootNodeInfoTests.cs" />
<Compile Include="Tree\ClickHandlers\SwitchToConnectionClickHandlerTests.cs" />
<Compile Include="Tree\SelectedConnectionDeletionConfirmerTests.cs" />
<Compile Include="UI\Controls\TestForm.cs">
<SubType>Form</SubType>
</Compile>
@@ -158,6 +168,7 @@
<Compile Include="UI\Forms\OptionsFormSetupAndTeardown.cs" />
<Compile Include="UI\Forms\PasswordFormTests.cs" />
<Compile Include="UI\WindowListTests.cs" />
<Compile Include="UI\Window\ConnectionTreeWindowTests.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />

View File

@@ -0,0 +1,2 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=tree_005Cclickhandlers/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

View File

@@ -42,8 +42,8 @@ namespace mRemoteNG.App
public static SecureString EncryptionKey { get; set; } = new RootNodeInfo(RootNodeType.Connection).PasswordString.ConvertToSecureString();
public static ConnectionTreeModel ConnectionTreeModel
{
get { return Windows.TreeForm.ConnectionTreeModel; }
set { Windows.TreeForm.ConnectionTreeModel = value; }
get { return Windows.TreeForm.ConnectionTree.ConnectionTreeModel; }
set { Windows.TreeForm.ConnectionTree.ConnectionTreeModel = value; }
}
#endregion
@@ -237,7 +237,7 @@ namespace mRemoteNG.App
// Load config
connectionsLoader.ConnectionFileName = filename;
ConnectionTreeModel = connectionsLoader.LoadConnections(false);
Windows.TreeForm.ConnectionTreeModel = ConnectionTreeModel;
Windows.TreeForm.ConnectionTree.ConnectionTreeModel = ConnectionTreeModel;
}
catch (Exception ex)
{
@@ -288,7 +288,7 @@ namespace mRemoteNG.App
connectionsLoader.UseDatabase = Settings.Default.UseSQLServer;
ConnectionTreeModel = connectionsLoader.LoadConnections(false);
Windows.TreeForm.ConnectionTreeModel = ConnectionTreeModel;
Windows.TreeForm.ConnectionTree.ConnectionTreeModel = ConnectionTreeModel;
if (Settings.Default.UseSQLServer)
{
@@ -593,7 +593,8 @@ namespace mRemoteNG.App
connectionInfo.Protocol = url.StartsWith("https:") ? ProtocolType.HTTPS : ProtocolType.HTTP;
connectionInfo.SetDefaultPort();
connectionInfo.IsQuickConnect = true;
ConnectionInitiator.OpenConnection(connectionInfo, ConnectionInfo.Force.DoNotJump);
var connectionInitiator = new ConnectionInitiator();
connectionInitiator.OpenConnection(connectionInfo, ConnectionInfo.Force.DoNotJump);
}
public static void GoToWebsite()

View File

@@ -12,14 +12,52 @@ using TabPage = Crownwood.Magic.Controls.TabPage;
namespace mRemoteNG.Connection
{
public static class ConnectionInitiator
public class ConnectionInitiator : IConnectionInitiator
{
public static void OpenConnection(ContainerInfo containerInfo, ConnectionInfo.Force force = ConnectionInfo.Force.None)
public void OpenConnection(ContainerInfo containerInfo, ConnectionInfo.Force force = ConnectionInfo.Force.None)
{
OpenConnection(containerInfo, force, null);
}
private static void OpenConnection(ContainerInfo containerInfo, ConnectionInfo.Force force, Form conForm)
public void OpenConnection(ConnectionInfo connectionInfo)
{
try
{
OpenConnection(connectionInfo, ConnectionInfo.Force.None);
}
catch (Exception ex)
{
Runtime.MessageCollector.AddExceptionStackTrace(Language.strConnectionOpenFailed, ex);
}
}
public void OpenConnection(ConnectionInfo connectionInfo, ConnectionInfo.Force force)
{
try
{
OpenConnection(connectionInfo, force, null);
}
catch (Exception ex)
{
Runtime.MessageCollector.AddExceptionStackTrace(Language.strConnectionOpenFailed, ex);
}
}
public bool SwitchToOpenConnection(ConnectionInfo connectionInfo)
{
var interfaceControl = FindConnectionContainer(connectionInfo);
if (interfaceControl == null) return false;
var connectionWindow = (ConnectionWindow)interfaceControl.FindForm();
connectionWindow?.Focus();
var findForm = (ConnectionWindow)interfaceControl.FindForm();
findForm?.Show(frmMain.Default.pnlDock);
var tabPage = (TabPage)interfaceControl.Parent;
tabPage.Selected = true;
return true;
}
#region Private
private void OpenConnection(ContainerInfo containerInfo, ConnectionInfo.Force force, Form conForm)
{
var children = containerInfo.Children;
if (children.Count == 0) return;
@@ -33,31 +71,7 @@ namespace mRemoteNG.Connection
}
}
public static void OpenConnection(ConnectionInfo connectionInfo)
{
try
{
OpenConnection(connectionInfo, ConnectionInfo.Force.None);
}
catch (Exception ex)
{
Runtime.MessageCollector.AddExceptionStackTrace(Language.strConnectionOpenFailed, ex);
}
}
public static void OpenConnection(ConnectionInfo connectionInfo, ConnectionInfo.Force force)
{
try
{
OpenConnection(connectionInfo, force, null);
}
catch (Exception ex)
{
Runtime.MessageCollector.AddExceptionStackTrace(Language.strConnectionOpenFailed, ex);
}
}
private static void OpenConnection(ConnectionInfo connectionInfo, ConnectionInfo.Force force, Form conForm)
private void OpenConnection(ConnectionInfo connectionInfo, ConnectionInfo.Force force, Form conForm)
{
try
{
@@ -115,19 +129,6 @@ namespace mRemoteNG.Connection
extA?.Start(connectionInfo);
}
public static bool SwitchToOpenConnection(ConnectionInfo nCi)
{
var IC = FindConnectionContainer(nCi);
if (IC == null) return false;
var connectionWindow = (ConnectionWindow)IC.FindForm();
connectionWindow?.Focus();
var findForm = (ConnectionWindow)IC.FindForm();
findForm?.Show(frmMain.Default.pnlDock);
var tabPage = (TabPage)IC.Parent;
tabPage.Selected = true;
return true;
}
private static InterfaceControl FindConnectionContainer(ConnectionInfo connectionInfo)
{
if (connectionInfo.OpenConnections.Count <= 0) return null;
@@ -214,10 +215,9 @@ namespace mRemoteNG.Connection
{
newProtocol.InterfaceControl = new InterfaceControl(connectionContainer, newProtocol, connectionInfo);
}
#endregion
#region Event handlers
private static void Prot_Event_Disconnected(object sender, string disconnectedMessage)
{
try
@@ -288,5 +288,6 @@ namespace mRemoteNG.Connection
Runtime.MessageCollector.AddExceptionStackTrace(Language.strConnectionEventConnectionFailed, ex);
}
}
#endregion
}
}

View File

@@ -0,0 +1,15 @@
using mRemoteNG.Container;
namespace mRemoteNG.Connection
{
public interface IConnectionInitiator
{
void OpenConnection(ConnectionInfo connectionInfo);
void OpenConnection(ContainerInfo containerInfo, ConnectionInfo.Force force = ConnectionInfo.Force.None);
void OpenConnection(ConnectionInfo connectionInfo, ConnectionInfo.Force force);
bool SwitchToOpenConnection(ConnectionInfo connectionInfo);
}
}

View File

@@ -11,6 +11,7 @@ namespace mRemoteNG.Tools
{
public class ExternalTool
{
private readonly IConnectionInitiator _connectionInitiator = new ConnectionInitiator();
#region Public Properties
public string DisplayName { get; set; }
public string FileName { get; set; }
@@ -80,7 +81,7 @@ namespace mRemoteNG.Tools
try
{
var newConnectionInfo = BuildConnectionInfoForIntegratedApp();
ConnectionInitiator.OpenConnection(newConnectionInfo);
_connectionInitiator.OpenConnection(newConnectionInfo);
}
catch (Exception ex)
{

View File

@@ -15,8 +15,10 @@ namespace mRemoteNG.Tools
private NotifyIcon _nI;
private ContextMenuStrip _cMen;
private ToolStripMenuItem _cMenCons;
private readonly IConnectionInitiator _connectionInitiator = new ConnectionInitiator();
public bool Disposed { get; set; }
public bool Disposed { get; set; }
public NotificationAreaIcon()
{
@@ -124,7 +126,7 @@ namespace mRemoteNG.Tools
{
ShowForm();
}
ConnectionInitiator.OpenConnection((ConnectionInfo)((Control)sender).Tag);
_connectionInitiator.OpenConnection((ConnectionInfo)((Control)sender).Tag);
}
private void cMenExit_Click(Object sender, EventArgs e)

View File

@@ -0,0 +1,11 @@

namespace mRemoteNG.Tree
{
public class AlwaysConfirmYes : IConfirm
{
public bool Confirm()
{
return true;
}
}
}

View File

@@ -0,0 +1,28 @@
using System;
using mRemoteNG.Connection;
using mRemoteNG.Container;
using mRemoteNG.UI.Controls;
namespace mRemoteNG.Tree
{
public class ExpandNodeClickHandler : ITreeNodeClickHandler
{
private readonly IConnectionTree _connectionTree;
public ExpandNodeClickHandler(IConnectionTree connectionTree)
{
if (connectionTree == null)
throw new ArgumentNullException(nameof(connectionTree));
_connectionTree = connectionTree;
}
public void Execute(ConnectionInfo clickedNode)
{
var clickedNodeAsContainer = clickedNode as ContainerInfo;
if (clickedNodeAsContainer == null) return;
_connectionTree.ToggleExpansion(clickedNodeAsContainer);
}
}
}

View File

@@ -0,0 +1,10 @@
using mRemoteNG.Connection;
namespace mRemoteNG.Tree
{
public interface ITreeNodeClickHandler
{
void Execute(ConnectionInfo clickedNode);
}
}

View File

@@ -0,0 +1,26 @@
using System;
using mRemoteNG.Connection;
namespace mRemoteNG.Tree
{
public class OpenConnectionClickHandler : ITreeNodeClickHandler
{
private readonly IConnectionInitiator _connectionInitiator;
public OpenConnectionClickHandler(IConnectionInitiator connectionInitiator)
{
if (connectionInitiator == null)
throw new ArgumentNullException(nameof(connectionInitiator));
_connectionInitiator = connectionInitiator;
}
public void Execute(ConnectionInfo clickedNode)
{
if (clickedNode == null)
throw new ArgumentNullException(nameof(clickedNode));
if (clickedNode.GetTreeNodeType() != TreeNodeType.Connection && clickedNode.GetTreeNodeType() != TreeNodeType.PuttySession) return;
_connectionInitiator.OpenConnection(clickedNode);
}
}
}

View File

@@ -0,0 +1,26 @@
using System;
using mRemoteNG.Connection;
namespace mRemoteNG.Tree
{
public class SwitchToConnectionClickHandler : ITreeNodeClickHandler
{
private readonly IConnectionInitiator _connectionInitiator;
public SwitchToConnectionClickHandler(IConnectionInitiator connectionInitiator)
{
if (connectionInitiator == null)
throw new ArgumentNullException(nameof(connectionInitiator));
_connectionInitiator = connectionInitiator;
}
public void Execute(ConnectionInfo clickedNode)
{
if (clickedNode == null)
throw new ArgumentNullException(nameof(clickedNode));
if (clickedNode.GetTreeNodeType() != TreeNodeType.Connection && clickedNode.GetTreeNodeType() != TreeNodeType.PuttySession) return;
_connectionInitiator.SwitchToOpenConnection(clickedNode);
}
}
}

View File

@@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using mRemoteNG.Connection;
namespace mRemoteNG.Tree
{
public class TreeNodeCompositeClickHandler : ITreeNodeClickHandler
{
public IEnumerable<ITreeNodeClickHandler> ClickHandlers { get; set; } = new ITreeNodeClickHandler[0];
public void Execute(ConnectionInfo clickedNode)
{
if (clickedNode == null)
throw new ArgumentNullException(nameof(clickedNode));
foreach (var handler in ClickHandlers)
{
handler.Execute(clickedNode);
}
}
}
}

View File

@@ -0,0 +1,7 @@
namespace mRemoteNG.Tree
{
public interface IConfirm
{
bool Confirm();
}
}

View File

@@ -1,24 +0,0 @@
using System.Windows.Forms;
using BrightIdeasSoftware;
namespace mRemoteNG.Tree
{
public static class ObjectListViewExtensions
{
public static void Invoke(this Control control, MethodInvoker action)
{
control.Invoke(action);
}
public static void InvokeExpand(this TreeListView control, object model)
{
control.Invoke(() => control.Expand(model));
}
public static void InvokeRebuildAll(this TreeListView control, bool preserveState)
{
control.Invoke(() => control.RebuildAll(preserveState));
}
}
}

View File

@@ -0,0 +1,31 @@
using System;
using System.Linq;
using mRemoteNG.Connection;
using mRemoteNG.Container;
using mRemoteNG.UI.Controls;
namespace mRemoteNG.Tree
{
public class PreviousSessionOpener : IConnectionTreeDelegate
{
private readonly IConnectionInitiator _connectionInitiator;
public PreviousSessionOpener(IConnectionInitiator connectionInitiator)
{
if (connectionInitiator == null)
throw new ArgumentNullException(nameof(connectionInitiator));
_connectionInitiator = connectionInitiator;
}
public void Execute(IConnectionTree connectionTree)
{
var connectionInfoList = connectionTree.GetRootConnectionNode().GetRecursiveChildList().Where(node => !(node is ContainerInfo));
var previouslyOpenedConnections = connectionInfoList.Where(item => item.PleaseConnect);
foreach (var connectionInfo in previouslyOpenedConnections)
{
_connectionInitiator.OpenConnection(connectionInfo);
}
}
}
}

View File

@@ -0,0 +1,19 @@
using System.Linq;
using mRemoteNG.Container;
using mRemoteNG.UI.Controls;
namespace mRemoteNG.Tree
{
public class PreviouslyOpenedFolderExpander : IConnectionTreeDelegate
{
public void Execute(IConnectionTree connectionTree)
{
var rootNode = connectionTree.GetRootConnectionNode();
var containerList = connectionTree.ConnectionTreeModel.GetRecursiveChildList(rootNode).OfType<ContainerInfo>();
var previouslyExpandedNodes = containerList.Where(container => container.IsExpanded);
connectionTree.ExpandedObjects = previouslyExpandedNodes;
connectionTree.InvokeRebuildAll(true);
}
}
}

View File

@@ -0,0 +1,14 @@
using mRemoteNG.UI.Controls;
namespace mRemoteNG.Tree
{
public class RootNodeExpander : IConnectionTreeDelegate
{
public void Execute(IConnectionTree connectionTree)
{
var rootConnectionNode = connectionTree.GetRootConnectionNode();
connectionTree.InvokeExpand(rootConnectionNode);
}
}
}

View File

@@ -0,0 +1,56 @@
using System;
using System.Windows.Forms;
using mRemoteNG.Connection;
using mRemoteNG.Container;
using mRemoteNG.UI.Controls;
namespace mRemoteNG.Tree
{
public class SelectedConnectionDeletionConfirmer : IConfirm
{
private readonly IConnectionTree _connectionTree;
private readonly Func<string, string, MessageBoxButtons, MessageBoxIcon, DialogResult> _confirmationFunc;
public SelectedConnectionDeletionConfirmer(IConnectionTree connectionTree, Func<string, string, MessageBoxButtons, MessageBoxIcon, DialogResult> confirmationFunc)
{
_connectionTree = connectionTree;
_confirmationFunc = confirmationFunc;
}
public bool Confirm()
{
var deletionTarget = _connectionTree.SelectedNode;
var deletionTargetAsContainer = deletionTarget as ContainerInfo;
if (deletionTargetAsContainer != null)
return deletionTargetAsContainer.HasChildren()
? UserConfirmsNonEmptyFolderDeletion(deletionTargetAsContainer)
: UserConfirmsEmptyFolderDeletion(deletionTargetAsContainer);
return UserConfirmsConnectionDeletion(deletionTarget);
}
private bool UserConfirmsEmptyFolderDeletion(AbstractConnectionInfoData deletionTarget)
{
var messagePrompt = string.Format(Language.strConfirmDeleteNodeFolder, deletionTarget.Name);
return PromptUser(messagePrompt);
}
private bool UserConfirmsNonEmptyFolderDeletion(AbstractConnectionInfoData deletionTarget)
{
var messagePrompt = string.Format(Language.strConfirmDeleteNodeFolderNotEmpty, deletionTarget.Name);
return PromptUser(messagePrompt);
}
private bool UserConfirmsConnectionDeletion(AbstractConnectionInfoData deletionTarget)
{
var messagePrompt = string.Format(Language.strConfirmDeleteNodeConnection, deletionTarget.Name);
return PromptUser(messagePrompt);
}
private bool PromptUser(string promptMessage)
{
var msgBoxResponse = _confirmationFunc.Invoke(promptMessage, Application.ProductName, MessageBoxButtons.YesNo, MessageBoxIcon.Question);
return msgBoxResponse == DialogResult.Yes;
}
}
}

View File

@@ -1,4 +1,5 @@
using System;
using System.ComponentModel;
using System.Linq;
using System.Windows.Forms;
using mRemoteNG.App;
@@ -6,13 +7,14 @@ using mRemoteNG.Connection;
using mRemoteNG.Connection.Protocol;
using mRemoteNG.Container;
using mRemoteNG.Tools;
using mRemoteNG.Tree;
using mRemoteNG.Tree.Root;
// ReSharper disable UnusedParameter.Local
namespace mRemoteNG.UI.Controls
{
internal sealed class ConnectionContextMenu : ContextMenuStrip
public sealed class ConnectionContextMenu : ContextMenuStrip
{
private ToolStripMenuItem _cMenTreeAddConnection;
private ToolStripMenuItem _cMenTreeAddFolder;
@@ -44,14 +46,23 @@ namespace mRemoteNG.UI.Controls
private ToolStripMenuItem _cMenTreeImportFile;
private ToolStripMenuItem _cMenTreeImportActiveDirectory;
private ToolStripMenuItem _cMenTreeImportPortScan;
private readonly ConnectionTree _connectionTree;
private readonly IConnectionInitiator _connectionInitiator;
public ConnectionContextMenu()
public ConnectionContextMenu(ConnectionTree connectionTree)
{
_connectionTree = connectionTree;
_connectionInitiator = new ConnectionInitiator();
InitializeComponent();
ApplyLanguage();
EnableShortcutKeys();
Opening += (sender, args) => AddExternalApps();
Opening += (sender, args) =>
{
AddExternalApps();
ShowHideMenuItems();
};
Closing += (sender, args) => EnableMenuItemsRecursive(Items);
}
private void InitializeComponent()
@@ -384,104 +395,129 @@ namespace mRemoteNG.UI.Controls
_cMenTreeMoveDown.Text = Language.strMoveDown;
}
internal void ShowHideTreeContextMenuItems(ConnectionInfo connectionInfo)
internal void ShowHideMenuItems()
{
if (connectionInfo == null)
if (_connectionTree.SelectedNode == null)
return;
try
{
Enabled = true;
EnableMenuItemsRecursive(Items);
if (connectionInfo is RootPuttySessionsNodeInfo)
if (_connectionTree.SelectedNode is RootPuttySessionsNodeInfo)
{
_cMenTreeAddConnection.Enabled = false;
_cMenTreeAddFolder.Enabled = false;
_cMenTreeConnect.Enabled = false;
_cMenTreeConnectWithOptions.Enabled = false;
_cMenTreeDisconnect.Enabled = false;
_cMenTreeToolsTransferFile.Enabled = false;
_cMenTreeConnectWithOptions.Enabled = false;
_cMenTreeToolsSort.Enabled = false;
_cMenTreeToolsExternalApps.Enabled = false;
_cMenTreeDuplicate.Enabled = false;
_cMenTreeRename.Enabled = true;
_cMenTreeDelete.Enabled = false;
_cMenTreeMoveUp.Enabled = false;
_cMenTreeMoveDown.Enabled = false;
ShowHideMenuItemsForRootPuttyNode();
}
else if (connectionInfo is RootNodeInfo)
else if (_connectionTree.SelectedNode is RootNodeInfo)
{
_cMenTreeConnect.Enabled = false;
_cMenTreeConnectWithOptions.Enabled = false;
_cMenTreeConnectWithOptionsConnectInFullscreen.Enabled = false;
_cMenTreeConnectWithOptionsConnectToConsoleSession.Enabled = false;
_cMenTreeConnectWithOptionsChoosePanelBeforeConnecting.Enabled = false;
_cMenTreeDisconnect.Enabled = false;
_cMenTreeToolsTransferFile.Enabled = false;
_cMenTreeToolsExternalApps.Enabled = false;
_cMenTreeDuplicate.Enabled = false;
_cMenTreeDelete.Enabled = false;
_cMenTreeMoveUp.Enabled = false;
_cMenTreeMoveDown.Enabled = false;
ShowHideMenuItemsForRootConnectionNode();
}
else if (connectionInfo is ContainerInfo)
else if (_connectionTree.SelectedNode is ContainerInfo)
{
_cMenTreeConnectWithOptionsConnectInFullscreen.Enabled = false;
_cMenTreeConnectWithOptionsConnectToConsoleSession.Enabled = false;
_cMenTreeDisconnect.Enabled = false;
var openConnections = ((ContainerInfo)connectionInfo).Children.Sum(child => child.OpenConnections.Count);
if (openConnections > 0)
_cMenTreeDisconnect.Enabled = true;
_cMenTreeToolsTransferFile.Enabled = false;
_cMenTreeToolsExternalApps.Enabled = false;
ShowHideMenuItemsForContainer(_connectionTree.SelectedNode);
}
else if (connectionInfo is PuttySessionInfo)
else if (_connectionTree.SelectedNode is PuttySessionInfo)
{
_cMenTreeAddConnection.Enabled = false;
_cMenTreeAddFolder.Enabled = false;
if (connectionInfo.OpenConnections.Count == 0)
_cMenTreeDisconnect.Enabled = false;
if (!(connectionInfo.Protocol == ProtocolType.SSH1 | connectionInfo.Protocol == ProtocolType.SSH2))
_cMenTreeToolsTransferFile.Enabled = false;
_cMenTreeConnectWithOptionsConnectInFullscreen.Enabled = false;
_cMenTreeConnectWithOptionsConnectToConsoleSession.Enabled = false;
_cMenTreeToolsSort.Enabled = false;
_cMenTreeDuplicate.Enabled = false;
_cMenTreeRename.Enabled = false;
_cMenTreeDelete.Enabled = false;
_cMenTreeMoveUp.Enabled = false;
_cMenTreeMoveDown.Enabled = false;
ShowHideMenuItemsForPuttyNode(_connectionTree.SelectedNode);
}
else
{
if (connectionInfo.OpenConnections.Count == 0)
_cMenTreeDisconnect.Enabled = false;
if (!(connectionInfo.Protocol == ProtocolType.SSH1 | connectionInfo.Protocol == ProtocolType.SSH2))
_cMenTreeToolsTransferFile.Enabled = false;
if (!(connectionInfo.Protocol == ProtocolType.RDP | connectionInfo.Protocol == ProtocolType.ICA))
{
_cMenTreeConnectWithOptionsConnectInFullscreen.Enabled = false;
_cMenTreeConnectWithOptionsConnectToConsoleSession.Enabled = false;
}
if (connectionInfo.Protocol == ProtocolType.IntApp)
_cMenTreeConnectWithOptionsNoCredentials.Enabled = false;
ShowHideMenuItemsForConnectionNode(_connectionTree.SelectedNode);
}
}
catch (Exception ex)
{
Runtime.MessageCollector.AddExceptionStackTrace("ShowHideTreeContextMenuItems (UI.Window.ConnectionTreeWindow) failed", ex);
Runtime.MessageCollector.AddExceptionStackTrace("ShowHideMenuItems (UI.Controls.ConnectionContextMenu) failed", ex);
}
}
internal void ShowHideMenuItemsForRootPuttyNode()
{
_cMenTreeAddConnection.Enabled = false;
_cMenTreeAddFolder.Enabled = false;
_cMenTreeConnect.Enabled = false;
_cMenTreeConnectWithOptions.Enabled = false;
_cMenTreeDisconnect.Enabled = false;
_cMenTreeToolsTransferFile.Enabled = false;
_cMenTreeConnectWithOptions.Enabled = false;
_cMenTreeToolsSort.Enabled = false;
_cMenTreeToolsExternalApps.Enabled = false;
_cMenTreeDuplicate.Enabled = false;
_cMenTreeRename.Enabled = true;
_cMenTreeDelete.Enabled = false;
_cMenTreeMoveUp.Enabled = false;
_cMenTreeMoveDown.Enabled = false;
}
internal void ShowHideMenuItemsForRootConnectionNode()
{
_cMenTreeConnect.Enabled = false;
_cMenTreeConnectWithOptions.Enabled = false;
_cMenTreeConnectWithOptionsConnectInFullscreen.Enabled = false;
_cMenTreeConnectWithOptionsConnectToConsoleSession.Enabled = false;
_cMenTreeConnectWithOptionsChoosePanelBeforeConnecting.Enabled = false;
_cMenTreeDisconnect.Enabled = false;
_cMenTreeToolsTransferFile.Enabled = false;
_cMenTreeToolsExternalApps.Enabled = false;
_cMenTreeDuplicate.Enabled = false;
_cMenTreeDelete.Enabled = false;
_cMenTreeMoveUp.Enabled = false;
_cMenTreeMoveDown.Enabled = false;
}
internal void ShowHideMenuItemsForContainer(ConnectionInfo connectionInfo)
{
_cMenTreeConnectWithOptionsConnectInFullscreen.Enabled = false;
_cMenTreeConnectWithOptionsConnectToConsoleSession.Enabled = false;
_cMenTreeDisconnect.Enabled = false;
var openConnections = ((ContainerInfo)connectionInfo).Children.Sum(child => child.OpenConnections.Count);
if (openConnections > 0)
_cMenTreeDisconnect.Enabled = true;
_cMenTreeToolsTransferFile.Enabled = false;
_cMenTreeToolsExternalApps.Enabled = false;
}
internal void ShowHideMenuItemsForPuttyNode(ConnectionInfo connectionInfo)
{
_cMenTreeAddConnection.Enabled = false;
_cMenTreeAddFolder.Enabled = false;
if (connectionInfo.OpenConnections.Count == 0)
_cMenTreeDisconnect.Enabled = false;
if (!(connectionInfo.Protocol == ProtocolType.SSH1 | connectionInfo.Protocol == ProtocolType.SSH2))
_cMenTreeToolsTransferFile.Enabled = false;
_cMenTreeConnectWithOptionsConnectInFullscreen.Enabled = false;
_cMenTreeConnectWithOptionsConnectToConsoleSession.Enabled = false;
_cMenTreeToolsSort.Enabled = false;
_cMenTreeDuplicate.Enabled = false;
_cMenTreeRename.Enabled = false;
_cMenTreeDelete.Enabled = false;
_cMenTreeMoveUp.Enabled = false;
_cMenTreeMoveDown.Enabled = false;
}
internal void ShowHideMenuItemsForConnectionNode(ConnectionInfo connectionInfo)
{
if (connectionInfo.OpenConnections.Count == 0)
_cMenTreeDisconnect.Enabled = false;
if (!(connectionInfo.Protocol == ProtocolType.SSH1 | connectionInfo.Protocol == ProtocolType.SSH2))
_cMenTreeToolsTransferFile.Enabled = false;
if (!(connectionInfo.Protocol == ProtocolType.RDP | connectionInfo.Protocol == ProtocolType.ICA))
{
_cMenTreeConnectWithOptionsConnectInFullscreen.Enabled = false;
_cMenTreeConnectWithOptionsConnectToConsoleSession.Enabled = false;
}
if (connectionInfo.Protocol == ProtocolType.IntApp)
_cMenTreeConnectWithOptionsNoCredentials.Enabled = false;
}
internal void DisableShortcutKeys()
{
_cMenTreeConnect.ShortcutKeys = Keys.None;
@@ -553,183 +589,218 @@ namespace mRemoteNG.UI.Controls
_cMenTreeToolsExternalApps.DropDownItems.Clear();
}
#region Events
public event EventHandler ConnectClicked;
#region Click handlers
private void OnConnectClicked(object sender, EventArgs e)
{
var handler = ConnectClicked;
handler?.Invoke(this, e);
var selectedNodeAsContainer = _connectionTree.SelectedNode as ContainerInfo;
if (selectedNodeAsContainer != null)
_connectionInitiator.OpenConnection(selectedNodeAsContainer, ConnectionInfo.Force.DoNotJump);
else
_connectionInitiator.OpenConnection(_connectionTree.SelectedNode, ConnectionInfo.Force.DoNotJump);
}
public event EventHandler ConnectToConsoleSessionClicked;
private void OnConnectToConsoleSessionClicked(object sender, EventArgs e)
{
var handler = ConnectToConsoleSessionClicked;
handler?.Invoke(this, e);
var selectedNodeAsContainer = _connectionTree.SelectedNode as ContainerInfo;
if (selectedNodeAsContainer != null)
_connectionInitiator.OpenConnection(selectedNodeAsContainer, ConnectionInfo.Force.UseConsoleSession | ConnectionInfo.Force.DoNotJump);
else
_connectionInitiator.OpenConnection(_connectionTree.SelectedNode, ConnectionInfo.Force.UseConsoleSession | ConnectionInfo.Force.DoNotJump);
}
public event EventHandler DontConnectToConsoleSessionClicked;
private void OnDontConnectToConsoleSessionClicked(object sender, EventArgs e)
{
var handler = DontConnectToConsoleSessionClicked;
handler?.Invoke(this, e);
var selectedNodeAsContainer = _connectionTree.SelectedNode as ContainerInfo;
if (selectedNodeAsContainer != null)
_connectionInitiator.OpenConnection(selectedNodeAsContainer, ConnectionInfo.Force.DontUseConsoleSession | ConnectionInfo.Force.DoNotJump);
else
_connectionInitiator.OpenConnection(_connectionTree.SelectedNode, ConnectionInfo.Force.DontUseConsoleSession | ConnectionInfo.Force.DoNotJump);
}
public event EventHandler ConnectInFullscreenClicked;
private void OnConnectInFullscreenClicked(object sender, EventArgs e)
{
var handler = ConnectInFullscreenClicked;
handler?.Invoke(this, e);
var selectedNodeAsContainer = _connectionTree.SelectedNode as ContainerInfo;
if (selectedNodeAsContainer != null)
_connectionInitiator.OpenConnection(selectedNodeAsContainer, ConnectionInfo.Force.Fullscreen | ConnectionInfo.Force.DoNotJump);
else
_connectionInitiator.OpenConnection(_connectionTree.SelectedNode, ConnectionInfo.Force.Fullscreen | ConnectionInfo.Force.DoNotJump);
}
public event EventHandler ConnectWithNoCredentialsClick;
private void OnConnectWithNoCredentialsClick(object sender, EventArgs e)
{
var handler = ConnectWithNoCredentialsClick;
handler?.Invoke(this, e);
var selectedNodeAsContainer = _connectionTree.SelectedNode as ContainerInfo;
if (selectedNodeAsContainer != null)
_connectionInitiator.OpenConnection(selectedNodeAsContainer, ConnectionInfo.Force.NoCredentials);
else
_connectionInitiator.OpenConnection(_connectionTree.SelectedNode, ConnectionInfo.Force.NoCredentials);
}
public event EventHandler ChoosePanelBeforeConnectingClicked;
private void OnChoosePanelBeforeConnectingClicked(object sender, EventArgs e)
{
var handler = ChoosePanelBeforeConnectingClicked;
handler?.Invoke(this, e);
var selectedNodeAsContainer = _connectionTree.SelectedNode as ContainerInfo;
if (selectedNodeAsContainer != null)
_connectionInitiator.OpenConnection(selectedNodeAsContainer, ConnectionInfo.Force.OverridePanel | ConnectionInfo.Force.DoNotJump);
else
_connectionInitiator.OpenConnection(_connectionTree.SelectedNode, ConnectionInfo.Force.OverridePanel | ConnectionInfo.Force.DoNotJump);
}
public event EventHandler DisconnectClicked;
private void OnDisconnectClicked(object sender, EventArgs e)
{
var handler = DisconnectClicked;
handler?.Invoke(this, e);
DisconnectConnection(_connectionTree.SelectedNode);
}
public event EventHandler TransferFileClicked;
public void DisconnectConnection(ConnectionInfo connectionInfo)
{
try
{
if (connectionInfo == null) return;
var nodeAsContainer = connectionInfo as ContainerInfo;
if (nodeAsContainer != null)
{
foreach (var child in nodeAsContainer.Children)
{
for (var i = 0; i <= child.OpenConnections.Count - 1; i++)
{
child.OpenConnections[i].Disconnect();
}
}
}
else
{
for (var i = 0; i <= connectionInfo.OpenConnections.Count - 1; i++)
{
connectionInfo.OpenConnections[i].Disconnect();
}
}
}
catch (Exception ex)
{
Runtime.MessageCollector.AddExceptionStackTrace("DisconnectConnection (UI.Window.ConnectionTreeWindow) failed", ex);
}
}
private void OnTransferFileClicked(object sender, EventArgs e)
{
var handler = TransferFileClicked;
handler?.Invoke(this, e);
SshTransferFile();
}
public event EventHandler DuplicateClicked;
public void SshTransferFile()
{
try
{
Windows.Show(WindowType.SSHTransfer);
Windows.SshtransferForm.Hostname = _connectionTree.SelectedNode.Hostname;
Windows.SshtransferForm.Username = _connectionTree.SelectedNode.Username;
Windows.SshtransferForm.Password = _connectionTree.SelectedNode.Password;
Windows.SshtransferForm.Port = Convert.ToString(_connectionTree.SelectedNode.Port);
}
catch (Exception ex)
{
Runtime.MessageCollector.AddExceptionStackTrace("SSHTransferFile (UI.Window.ConnectionTreeWindow) failed", ex);
}
}
private void OnDuplicateClicked(object sender, EventArgs e)
{
var handler = DuplicateClicked;
handler?.Invoke(this, e);
_connectionTree.DuplicateSelectedNode();
}
public event EventHandler RenameClicked;
private void OnRenameClicked(object sender, EventArgs e)
{
var handler = RenameClicked;
handler?.Invoke(this, e);
_connectionTree.RenameSelectedNode();
}
public event EventHandler DeleteClicked;
private void OnDeleteClicked(object sender, EventArgs e)
{
var handler = DeleteClicked;
handler?.Invoke(this, e);
_connectionTree.DeleteSelectedNode();
}
public event EventHandler ImportFileClicked;
private void OnImportFileClicked(object sender, EventArgs e)
{
var handler = ImportFileClicked;
handler?.Invoke(this, e);
var selectedNodeAsContainer = _connectionTree.SelectedNode as ContainerInfo ?? _connectionTree.SelectedNode.Parent;
Import.ImportFromFile(selectedNodeAsContainer);
}
public event EventHandler ImportActiveDirectoryClicked;
private void OnImportActiveDirectoryClicked(object sender, EventArgs e)
{
var handler = ImportActiveDirectoryClicked;
handler?.Invoke(this, e);
Windows.Show(WindowType.ActiveDirectoryImport);
}
public event EventHandler ImportPortScanClicked;
private void OnImportPortScanClicked(object sender, EventArgs e)
{
var handler = ImportPortScanClicked;
handler?.Invoke(this, e);
Windows.Show(WindowType.PortScan);
}
public event EventHandler ExportFileClicked;
private void OnExportFileClicked(object sender, EventArgs e)
{
var handler = ExportFileClicked;
handler?.Invoke(this, e);
Export.ExportToFile(_connectionTree.SelectedNode, Runtime.ConnectionTreeModel);
}
public event EventHandler AddConnectionClicked;
private void OnAddConnectionClicked(object sender, EventArgs e)
{
var handler = AddConnectionClicked;
handler?.Invoke(this, e);
_connectionTree.AddConnection();
Runtime.SaveConnectionsAsync();
}
public event EventHandler AddFolderClicked;
private void OnAddFolderClicked(object sender, EventArgs e)
{
var handler = AddFolderClicked;
handler?.Invoke(this, e);
_connectionTree.AddFolder();
Runtime.SaveConnectionsAsync();
}
public event EventHandler SortAscendingClicked;
private void OnSortAscendingClicked(object sender, EventArgs e)
{
var handler = SortAscendingClicked;
handler?.Invoke(this, e);
SortNodesRecursive(_connectionTree.SelectedNode, ListSortDirection.Ascending);
}
public event EventHandler SortDescendingClicked;
private void OnSortDescendingClicked(object sender, EventArgs e)
{
var handler = SortDescendingClicked;
handler?.Invoke(this, e);
SortNodesRecursive(_connectionTree.SelectedNode, ListSortDirection.Descending);
}
public event EventHandler MoveUpClicked;
private void OnMoveUpClicked(object sender, EventArgs e)
{
var handler = MoveUpClicked;
handler?.Invoke(this, e);
_connectionTree.SelectedNode.Parent.PromoteChild(_connectionTree.SelectedNode);
Runtime.SaveConnectionsAsync();
}
public event EventHandler MoveDownClicked;
private void OnMoveDownClicked(object sender, EventArgs e)
{
var handler = MoveDownClicked;
handler?.Invoke(this, e);
_connectionTree.SelectedNode.Parent.DemoteChild(_connectionTree.SelectedNode);
Runtime.SaveConnectionsAsync();
}
public event EventHandler ExternalToolClicked;
private void OnExternalToolClicked(object sender, EventArgs e)
{
var handler = ExternalToolClicked;
handler?.Invoke(sender, e);
StartExternalApp((ExternalTool)((ToolStripMenuItem)sender).Tag);
}
private void StartExternalApp(ExternalTool externalTool)
{
try
{
if (_connectionTree.SelectedNode.GetTreeNodeType() == TreeNodeType.Connection | _connectionTree.SelectedNode.GetTreeNodeType() == TreeNodeType.PuttySession)
externalTool.Start(_connectionTree.SelectedNode);
}
catch (Exception ex)
{
Runtime.MessageCollector.AddExceptionStackTrace("cMenTreeToolsExternalAppsEntry_Click failed (UI.Window.ConnectionTreeWindow)", ex);
}
}
#endregion
private void SortNodesRecursive(ConnectionInfo sortTarget, ListSortDirection sortDirection)
{
if (sortTarget == null)
sortTarget = _connectionTree.GetRootConnectionNode();
var sortTargetAsContainer = sortTarget as ContainerInfo;
if (sortTargetAsContainer != null)
sortTargetAsContainer.SortRecursive(sortDirection);
else
_connectionTree.SelectedNode.Parent.SortRecursive(sortDirection);
Runtime.SaveConnectionsAsync();
}
}
}

View File

@@ -0,0 +1,36 @@
namespace mRemoteNG.UI.Controls
{
partial class ConnectionTree
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
components = new System.ComponentModel.Container();
}
#endregion
}
}

View File

@@ -0,0 +1,280 @@
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using System.Windows.Forms;
using BrightIdeasSoftware;
using mRemoteNG.App;
using mRemoteNG.Config.Putty;
using mRemoteNG.Connection;
using mRemoteNG.Container;
using mRemoteNG.Tree;
using mRemoteNG.Tree.Root;
namespace mRemoteNG.UI.Controls
{
public partial class ConnectionTree : TreeListView, IConnectionTree
{
private ConnectionTreeModel _connectionTreeModel;
private readonly ConnectionTreeDragAndDropHandler _dragAndDropHandler = new ConnectionTreeDragAndDropHandler();
private readonly PuttySessionsManager _puttySessionsManager = PuttySessionsManager.Instance;
public ConnectionInfo SelectedNode => (ConnectionInfo) SelectedObject;
public NodeSearcher NodeSearcher { get; private set; }
public IConfirm NodeDeletionConfirmer { get; set; } = new AlwaysConfirmYes();
public IEnumerable<IConnectionTreeDelegate> PostSetupActions { get; set; } = new IConnectionTreeDelegate[0];
public ITreeNodeClickHandler DoubleClickHandler { get; set; } = new TreeNodeCompositeClickHandler();
public ITreeNodeClickHandler SingleClickHandler { get; set; } = new TreeNodeCompositeClickHandler();
public ConnectionTreeModel ConnectionTreeModel
{
get { return _connectionTreeModel; }
set
{
_connectionTreeModel = value;
PopulateTreeView();
}
}
public ConnectionTree()
{
InitializeComponent();
SetupConnectionTreeView();
}
#region ConnectionTree Setup
private void SetupConnectionTreeView()
{
var imageList = new StatusImageList();
SmallImageList = imageList.GetImageList();
AddColumns(imageList.ImageGetter);
LinkModelToView();
SetupDropSink();
SetEventHandlers();
}
private void AddColumns(ImageGetterDelegate imageGetterDelegate)
{
Columns.Add(new NameColumn(imageGetterDelegate));
}
private void LinkModelToView()
{
CanExpandGetter = item =>
{
var itemAsContainer = item as ContainerInfo;
return itemAsContainer?.Children.Count > 0;
};
ChildrenGetter = item => ((ContainerInfo)item).Children;
}
private void SetupDropSink()
{
DropSink = new SimpleDropSink
{
CanDropBetween = true
};
}
private void SetEventHandlers()
{
Collapsed += (sender, args) =>
{
var container = args.Model as ContainerInfo;
if (container != null)
container.IsExpanded = false;
};
Expanded += (sender, args) =>
{
var container = args.Model as ContainerInfo;
if (container != null)
container.IsExpanded = true;
};
SelectionChanged += tvConnections_AfterSelect;
CellClick += tvConnections_NodeMouseSingleClick;
CellClick += tvConnections_NodeMouseDoubleClick;
CellToolTipShowing += tvConnections_CellToolTipShowing;
ModelCanDrop += _dragAndDropHandler.HandleEvent_ModelCanDrop;
ModelDropped += _dragAndDropHandler.HandleEvent_ModelDropped;
}
private void PopulateTreeView()
{
UnregisterModelUpdateHandlers();
SetObjects(ConnectionTreeModel.RootNodes);
RegisterModelUpdateHandlers();
NodeSearcher = new NodeSearcher(ConnectionTreeModel);
ExecutePostSetupActions();
}
private void RegisterModelUpdateHandlers()
{
_puttySessionsManager.PuttySessionsCollectionChanged += OnPuttySessionsCollectionChanged;
ConnectionTreeModel.CollectionChanged += HandleCollectionChanged;
ConnectionTreeModel.PropertyChanged += HandleCollectionPropertyChanged;
}
private void UnregisterModelUpdateHandlers()
{
_puttySessionsManager.PuttySessionsCollectionChanged -= OnPuttySessionsCollectionChanged;
ConnectionTreeModel.CollectionChanged -= HandleCollectionChanged;
ConnectionTreeModel.PropertyChanged -= HandleCollectionPropertyChanged;
}
private void OnPuttySessionsCollectionChanged(object sender, NotifyCollectionChangedEventArgs args)
{
RefreshObjects(GetRootPuttyNodes().ToList());
}
private void HandleCollectionPropertyChanged(object sender, PropertyChangedEventArgs propertyChangedEventArgs)
{
//TODO for some reason property changed events are getting triggered twice for each changed property. should be just once. cant find source of duplication
var property = propertyChangedEventArgs.PropertyName;
if (property != "Name" && property != "OpenConnections") return;
var senderAsConnectionInfo = sender as ConnectionInfo;
if (senderAsConnectionInfo != null)
RefreshObject(senderAsConnectionInfo);
}
private void ExecutePostSetupActions()
{
foreach (var action in PostSetupActions)
{
action.Execute(this);
}
}
#endregion
#region ConnectionTree Behavior
public RootNodeInfo GetRootConnectionNode()
{
return (RootNodeInfo)Roots.Cast<ConnectionInfo>().First(item => item is RootNodeInfo);
}
public void InvokeExpand(object model)
{
Invoke((MethodInvoker)(() => Expand(model)));
}
public void InvokeRebuildAll(bool preserveState)
{
Invoke((MethodInvoker)(() => RebuildAll(preserveState)));
}
public IEnumerable<RootPuttySessionsNodeInfo> GetRootPuttyNodes()
{
return Objects.OfType<RootPuttySessionsNodeInfo>();
}
public void AddConnection()
{
try
{
AddNode(new ConnectionInfo());
}
catch (Exception ex)
{
Runtime.MessageCollector.AddExceptionStackTrace("UI.Window.Tree.AddConnection() failed.", ex);
}
}
public void AddFolder()
{
try
{
AddNode(new ContainerInfo());
}
catch (Exception ex)
{
Runtime.MessageCollector.AddExceptionStackTrace(Language.strErrorAddFolderFailed, ex);
}
}
private void AddNode(ConnectionInfo newNode)
{
if (SelectedNode == null) return;
DefaultConnectionInfo.Instance.SaveTo(newNode);
DefaultConnectionInheritance.Instance.SaveTo(newNode.Inheritance);
var selectedContainer = SelectedNode as ContainerInfo;
var parent = selectedContainer ?? SelectedNode?.Parent;
newNode.SetParent(parent);
Expand(parent);
SelectObject(newNode, true);
EnsureModelVisible(newNode);
}
public void DuplicateSelectedNode()
{
var newNode = SelectedNode.Clone();
newNode.Parent.SetChildBelow(newNode, SelectedNode);
Runtime.SaveConnectionsAsync();
}
public void RenameSelectedNode()
{
SelectedItem.BeginEdit();
Runtime.SaveConnectionsAsync();
}
public void DeleteSelectedNode()
{
if (SelectedNode is RootNodeInfo || SelectedNode is PuttySessionInfo) return;
if (!NodeDeletionConfirmer.Confirm()) return;
ConnectionTreeModel.DeleteNode(SelectedNode);
Runtime.SaveConnectionsAsync();
}
private void HandleCollectionChanged(object sender, NotifyCollectionChangedEventArgs args)
{
RefreshObject(sender);
}
private void tvConnections_AfterSelect(object sender, EventArgs e)
{
try
{
Windows.ConfigForm.SelectedTreeNode = SelectedNode;
}
catch (Exception ex)
{
Runtime.MessageCollector.AddExceptionStackTrace("tvConnections_AfterSelect (UI.Window.ConnectionTreeWindow) failed", ex);
}
}
private void tvConnections_NodeMouseSingleClick(object sender, CellClickEventArgs e)
{
if (e.ClickCount > 1) return;
var clickedNode = e.Model as ConnectionInfo;
SingleClickHandler.Execute(clickedNode);
}
private void tvConnections_NodeMouseDoubleClick(object sender, CellClickEventArgs e)
{
if (e.ClickCount < 2) return;
var clickedNode = e.Model as ConnectionInfo;
DoubleClickHandler.Execute(clickedNode);
}
private void tvConnections_CellToolTipShowing(object sender, ToolTipShowingEventArgs e)
{
try
{
var nodeProducingTooltip = (ConnectionInfo)e.Model;
e.Text = nodeProducingTooltip.Description;
}
catch (Exception ex)
{
Runtime.MessageCollector.AddExceptionStackTrace("tvConnections_MouseMove (UI.Window.ConnectionTreeWindow) failed", ex);
}
}
#endregion
}
}

View File

@@ -0,0 +1,25 @@
using System.Collections;
using mRemoteNG.Connection;
using mRemoteNG.Tree;
using mRemoteNG.Tree.Root;
namespace mRemoteNG.UI.Controls
{
public interface IConnectionTree
{
ConnectionTreeModel ConnectionTreeModel { get; set; }
ConnectionInfo SelectedNode { get; }
IEnumerable ExpandedObjects { get; set; }
RootNodeInfo GetRootConnectionNode();
void InvokeExpand(object model);
void InvokeRebuildAll(bool preserveState);
void ToggleExpansion(object model);
}
}

View File

@@ -0,0 +1,8 @@

namespace mRemoteNG.UI.Controls
{
public interface IConnectionTreeDelegate
{
void Execute(IConnectionTree connectionTree);
}
}

View File

@@ -0,0 +1,18 @@
using BrightIdeasSoftware;
using mRemoteNG.Connection;
namespace mRemoteNG.UI.Controls
{
public class NameColumn : OLVColumn
{
public NameColumn(ImageGetterDelegate imageGetterDelegate)
{
AspectName = "Name";
FillsFreeSpace = true;
IsButton = true;
AspectGetter = item => ((ConnectionInfo) item).Name;
ImageGetter = imageGetterDelegate;
}
}
}

View File

@@ -0,0 +1,57 @@
using System;
using System.Drawing;
using System.Windows.Forms;
using mRemoteNG.App;
using mRemoteNG.Connection;
using mRemoteNG.Container;
using mRemoteNG.Tree.Root;
namespace mRemoteNG.UI.Controls
{
public class StatusImageList
{
public ImageList GetImageList()
{
var imageList = new ImageList
{
ColorDepth = ColorDepth.Depth32Bit,
ImageSize = new Size(16, 16),
TransparentColor = Color.Transparent
};
FillImageList(imageList);
return imageList;
}
public object ImageGetter(object rowObject)
{
if (rowObject is RootPuttySessionsNodeInfo) return "PuttySessions";
if (rowObject is RootNodeInfo) return "Root";
if (rowObject is ContainerInfo) return "Folder";
var connection = rowObject as ConnectionInfo;
if (connection == null) return "";
return connection.OpenConnections.Count > 0 ? "Play" : "Pause";
}
private void FillImageList(ImageList imageList)
{
try
{
imageList.Images.Add(Resources.Root);
imageList.Images.SetKeyName(0, "Root");
imageList.Images.Add(Resources.Folder);
imageList.Images.SetKeyName(1, "Folder");
imageList.Images.Add(Resources.Play);
imageList.Images.SetKeyName(2, "Play");
imageList.Images.Add(Resources.Pause);
imageList.Images.SetKeyName(3, "Pause");
imageList.Images.Add(Resources.PuttySessions);
imageList.Images.SetKeyName(4, "PuttySessions");
}
catch (Exception ex)
{
Runtime.MessageCollector.AddExceptionStackTrace($"Unable to fill the image list of type {nameof(StatusImageList)}", ex);
}
}
}
}

View File

@@ -44,6 +44,8 @@ namespace mRemoteNG.UI.Forms
private ConnectionInfo _selectedConnection;
private SystemMenu _systemMenu;
private ConnectionTreeWindow ConnectionTreeWindow { get; set; }
private readonly IConnectionInitiator _connectionInitiator = new ConnectionInitiator();
@@ -495,13 +497,13 @@ namespace mRemoteNG.UI.Forms
private void mMenFileNewConnection_Click(object sender, EventArgs e)
{
ConnectionTreeWindow.AddConnection();
ConnectionTreeWindow.ConnectionTree.AddConnection();
Runtime.SaveConnectionsAsync();
}
private void mMenFileNewFolder_Click(object sender, EventArgs e)
{
ConnectionTreeWindow.AddFolder();
ConnectionTreeWindow.ConnectionTree.AddFolder();
Runtime.SaveConnectionsAsync();
}
@@ -547,19 +549,19 @@ namespace mRemoteNG.UI.Forms
private void mMenFileDelete_Click(object sender, EventArgs e)
{
ConnectionTreeWindow.DeleteSelectedNode();
ConnectionTreeWindow.ConnectionTree.DeleteSelectedNode();
Runtime.SaveConnectionsAsync();
}
private void mMenFileRename_Click(object sender, EventArgs e)
{
ConnectionTreeWindow.RenameSelectedNode();
ConnectionTreeWindow.ConnectionTree.RenameSelectedNode();
Runtime.SaveConnectionsAsync();
}
private void mMenFileDuplicate_Click(object sender, EventArgs e)
{
ConnectionTreeWindow.DuplicateSelectedNode();
ConnectionTreeWindow.ConnectionTree.DuplicateSelectedNode();
Runtime.SaveConnectionsAsync();
}
@@ -585,7 +587,7 @@ namespace mRemoteNG.UI.Forms
foreach (var i in ICList)
{
i.Protocol.Close();
ConnectionInitiator.OpenConnection(i.Info, ConnectionInfo.Force.DoNotJump);
_connectionInitiator.OpenConnection(i.Info, ConnectionInfo.Force.DoNotJump);
}
// throw it on the garbage collector
@@ -860,7 +862,7 @@ namespace mRemoteNG.UI.Forms
return;
}
cmbQuickConnect.Add(connectionInfo);
ConnectionInitiator.OpenConnection(connectionInfo, ConnectionInfo.Force.DoNotJump);
_connectionInitiator.OpenConnection(connectionInfo, ConnectionInfo.Force.DoNotJump);
}
catch (Exception ex)
{
@@ -936,14 +938,14 @@ namespace mRemoteNG.UI.Forms
btnConnections.DropDownItems.AddRange(rootMenuItems);
}
private static void ConnectionsMenuItem_MouseUp(object sender, MouseEventArgs e)
private void ConnectionsMenuItem_MouseUp(object sender, MouseEventArgs e)
{
if (e.Button != MouseButtons.Left) return;
if (((ToolStripMenuItem) sender).Tag is ContainerInfo) return;
var tag = ((ToolStripMenuItem)sender).Tag as ConnectionInfo;
if (tag != null)
{
ConnectionInitiator.OpenConnection(tag);
_connectionInitiator.OpenConnection(tag);
}
}
#endregion

View File

@@ -7,7 +7,6 @@ namespace mRemoteNG.UI.Window
#region Windows Form Designer generated code
internal System.Windows.Forms.TextBox txtSearch;
internal System.Windows.Forms.Panel pnlConnections;
internal System.Windows.Forms.ImageList imgListTree;
internal System.Windows.Forms.MenuStrip msMain;
internal System.Windows.Forms.ToolStripMenuItem mMenView;
internal System.Windows.Forms.ToolStripMenuItem mMenViewExpandAllFolders;
@@ -17,13 +16,10 @@ namespace mRemoteNG.UI.Window
internal System.Windows.Forms.ToolStripMenuItem mMenAddConnection;
internal System.Windows.Forms.ToolStripMenuItem mMenAddFolder;
public System.Windows.Forms.TreeView tvConnections;
public BrightIdeasSoftware.TreeListView olvConnections;
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
this.olvConnections = new BrightIdeasSoftware.TreeListView();
this.olvNameColumn = ((BrightIdeasSoftware.OLVColumn)(new BrightIdeasSoftware.OLVColumn()));
this.imgListTree = new System.Windows.Forms.ImageList(this.components);
this.olvConnections = new mRemoteNG.UI.Controls.ConnectionTree();
this.pnlConnections = new System.Windows.Forms.Panel();
this.PictureBox1 = new System.Windows.Forms.PictureBox();
this.txtSearch = new System.Windows.Forms.TextBox();
@@ -42,20 +38,16 @@ namespace mRemoteNG.UI.Window
//
// olvConnections
//
this.olvConnections.AllColumns.Add(this.olvNameColumn);
this.olvConnections.AllowDrop = true;
this.olvConnections.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.olvConnections.BorderStyle = System.Windows.Forms.BorderStyle.None;
this.olvConnections.CellEditUseWholeCell = false;
this.olvConnections.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
this.olvNameColumn});
this.olvConnections.Cursor = System.Windows.Forms.Cursors.Default;
this.olvConnections.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.None;
this.olvConnections.HideSelection = false;
this.olvConnections.IsSimpleDragSource = true;
this.olvConnections.IsSimpleDropSink = true;
this.olvConnections.LabelEdit = true;
this.olvConnections.Location = new System.Drawing.Point(0, 0);
this.olvConnections.MultiSelect = false;
@@ -64,7 +56,6 @@ namespace mRemoteNG.UI.Window
this.olvConnections.SelectedForeColor = System.Drawing.SystemColors.HighlightText;
this.olvConnections.ShowGroups = false;
this.olvConnections.Size = new System.Drawing.Size(192, 410);
this.olvConnections.SmallImageList = this.imgListTree;
this.olvConnections.TabIndex = 20;
this.olvConnections.UnfocusedSelectedBackColor = System.Drawing.SystemColors.Highlight;
this.olvConnections.UnfocusedSelectedForeColor = System.Drawing.SystemColors.HighlightText;
@@ -72,18 +63,6 @@ namespace mRemoteNG.UI.Window
this.olvConnections.View = System.Windows.Forms.View.Details;
this.olvConnections.VirtualMode = true;
//
// olvNameColumn
//
this.olvNameColumn.AspectName = "Name";
this.olvNameColumn.FillsFreeSpace = true;
this.olvNameColumn.IsButton = true;
//
// imgListTree
//
this.imgListTree.ColorDepth = System.Windows.Forms.ColorDepth.Depth32Bit;
this.imgListTree.ImageSize = new System.Drawing.Size(16, 16);
this.imgListTree.TransparentColor = System.Drawing.Color.Transparent;
//
// pnlConnections
//
this.pnlConnections.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
@@ -214,6 +193,6 @@ namespace mRemoteNG.UI.Window
#endregion
private System.ComponentModel.IContainer components;
private BrightIdeasSoftware.OLVColumn olvNameColumn;
private Controls.ConnectionTree olvConnections;
}
}

View File

@@ -3,17 +3,10 @@ using mRemoteNG.Connection;
using mRemoteNG.Container;
using mRemoteNG.Tree;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using BrightIdeasSoftware;
using mRemoteNG.Config.Putty;
using mRemoteNG.Tools;
using mRemoteNG.Tree.Root;
using mRemoteNG.UI.Controls;
using WeifenLuo.WinFormsUI.Docking;
@@ -22,22 +15,16 @@ namespace mRemoteNG.UI.Window
{
public partial class ConnectionTreeWindow
{
private ConnectionTreeModel _connectionTreeModel;
private readonly ConnectionTreeDragAndDropHandler _dragAndDropHandler = new ConnectionTreeDragAndDropHandler();
private NodeSearcher _nodeSearcher;
private readonly ConnectionContextMenu _contextMenu = new ConnectionContextMenu();
private readonly PuttySessionsManager _puttySessionsManager = PuttySessionsManager.Instance;
private readonly ConnectionContextMenu _contextMenu;
private readonly IConnectionInitiator _connectionInitiator = new ConnectionInitiator();
public ConnectionInfo SelectedNode => (ConnectionInfo) olvConnections.SelectedObject;
public ConnectionTreeModel ConnectionTreeModel
public ConnectionInfo SelectedNode => olvConnections.SelectedNode;
public ConnectionTree ConnectionTree
{
get { return _connectionTreeModel; }
set
{
_connectionTreeModel = value;
PopulateTreeView();
}
get { return olvConnections; }
set { olvConnections = value; }
}
public ConnectionTreeWindow(DockContent panel)
@@ -45,235 +32,12 @@ namespace mRemoteNG.UI.Window
WindowType = WindowType.Tree;
DockPnl = panel;
InitializeComponent();
FillImageList();
LinkModelToView();
SetupDropSink();
SetEventHandlers();
}
private void FillImageList()
{
try
{
imgListTree.Images.Add(Resources.Root);
imgListTree.Images.SetKeyName(0, "Root");
imgListTree.Images.Add(Resources.Folder);
imgListTree.Images.SetKeyName(1, "Folder");
imgListTree.Images.Add(Resources.Play);
imgListTree.Images.SetKeyName(2, "Play");
imgListTree.Images.Add(Resources.Pause);
imgListTree.Images.SetKeyName(3, "Pause");
imgListTree.Images.Add(Resources.PuttySessions);
imgListTree.Images.SetKeyName(4, "PuttySessions");
}
catch (Exception ex)
{
Runtime.MessageCollector.AddExceptionStackTrace("FillImageList (UI.Window.ConnectionTreeWindow) failed", ex);
}
}
private void LinkModelToView()
{
olvNameColumn.AspectGetter = item => ((ConnectionInfo)item).Name;
olvNameColumn.ImageGetter = ConnectionImageGetter;
olvConnections.CanExpandGetter = item =>
{
var itemAsContainer = item as ContainerInfo;
return itemAsContainer?.Children.Count > 0;
};
olvConnections.ChildrenGetter = item => ((ContainerInfo)item).Children;
_contextMenu = new ConnectionContextMenu(olvConnections);
olvConnections.ContextMenuStrip = _contextMenu;
}
private void SetupDropSink()
{
var dropSink = (SimpleDropSink)olvConnections.DropSink;
dropSink.CanDropBetween = true;
}
private static object ConnectionImageGetter(object rowObject)
{
if (rowObject is RootPuttySessionsNodeInfo) return "PuttySessions";
if (rowObject is RootNodeInfo) return "Root";
if (rowObject is ContainerInfo) return "Folder";
var connection = rowObject as ConnectionInfo;
if (connection == null) return "";
return connection.OpenConnections.Count > 0 ? "Play" : "Pause";
}
private void SetEventHandlers()
{
SetTreeEventHandlers();
SetContextMenuEventHandlers();
SetMenuEventHandlers();
}
private void SetTreeEventHandlers()
{
olvConnections.Collapsed += (sender, args) =>
{
var container = args.Model as ContainerInfo;
if (container != null)
container.IsExpanded = false;
};
olvConnections.Expanded += (sender, args) =>
{
var container = args.Model as ContainerInfo;
if (container != null)
container.IsExpanded = true;
};
olvConnections.BeforeLabelEdit += tvConnections_BeforeLabelEdit;
olvConnections.AfterLabelEdit += tvConnections_AfterLabelEdit;
olvConnections.SelectionChanged += tvConnections_AfterSelect;
olvConnections.CellClick += tvConnections_NodeMouseSingleClick;
olvConnections.CellClick += tvConnections_NodeMouseDoubleClick;
olvConnections.CellToolTipShowing += tvConnections_CellToolTipShowing;
olvConnections.ModelCanDrop += _dragAndDropHandler.HandleEvent_ModelCanDrop;
olvConnections.ModelDropped += _dragAndDropHandler.HandleEvent_ModelDropped;
olvConnections.KeyDown += tvConnections_KeyDown;
olvConnections.KeyPress += tvConnections_KeyPress;
}
private void SetContextMenuEventHandlers()
{
_contextMenu.Opening += (sender, args) => _contextMenu.ShowHideTreeContextMenuItems(SelectedNode);
_contextMenu.ConnectClicked += (sender, args) =>
{
var selectedNodeAsContainer = SelectedNode as ContainerInfo;
if (selectedNodeAsContainer != null)
ConnectionInitiator.OpenConnection(selectedNodeAsContainer, ConnectionInfo.Force.DoNotJump);
else
ConnectionInitiator.OpenConnection(SelectedNode, ConnectionInfo.Force.DoNotJump);
};
_contextMenu.ConnectToConsoleSessionClicked += (sender, args) =>
{
var selectedNodeAsContainer = SelectedNode as ContainerInfo;
if (selectedNodeAsContainer != null)
ConnectionInitiator.OpenConnection(selectedNodeAsContainer, ConnectionInfo.Force.UseConsoleSession | ConnectionInfo.Force.DoNotJump);
else
ConnectionInitiator.OpenConnection(SelectedNode, ConnectionInfo.Force.UseConsoleSession | ConnectionInfo.Force.DoNotJump);
};
_contextMenu.DontConnectToConsoleSessionClicked += (sender, args) =>
{
var selectedNodeAsContainer = SelectedNode as ContainerInfo;
if (selectedNodeAsContainer != null)
ConnectionInitiator.OpenConnection(selectedNodeAsContainer, ConnectionInfo.Force.DontUseConsoleSession | ConnectionInfo.Force.DoNotJump);
else
ConnectionInitiator.OpenConnection(SelectedNode, ConnectionInfo.Force.DontUseConsoleSession | ConnectionInfo.Force.DoNotJump);
};
_contextMenu.ConnectInFullscreenClicked += (sender, args) =>
{
var selectedNodeAsContainer = SelectedNode as ContainerInfo;
if (selectedNodeAsContainer != null)
ConnectionInitiator.OpenConnection(selectedNodeAsContainer, ConnectionInfo.Force.Fullscreen | ConnectionInfo.Force.DoNotJump);
else
ConnectionInitiator.OpenConnection(SelectedNode, ConnectionInfo.Force.Fullscreen | ConnectionInfo.Force.DoNotJump);
};
_contextMenu.ConnectWithNoCredentialsClick += (sender, args) =>
{
var selectedNodeAsContainer = SelectedNode as ContainerInfo;
if (selectedNodeAsContainer != null)
ConnectionInitiator.OpenConnection(selectedNodeAsContainer, ConnectionInfo.Force.NoCredentials);
else
ConnectionInitiator.OpenConnection(SelectedNode, ConnectionInfo.Force.NoCredentials);
};
_contextMenu.ChoosePanelBeforeConnectingClicked += (sender, args) =>
{
var selectedNodeAsContainer = SelectedNode as ContainerInfo;
if (selectedNodeAsContainer != null)
ConnectionInitiator.OpenConnection(selectedNodeAsContainer, ConnectionInfo.Force.OverridePanel | ConnectionInfo.Force.DoNotJump);
else
ConnectionInitiator.OpenConnection(SelectedNode, ConnectionInfo.Force.OverridePanel | ConnectionInfo.Force.DoNotJump);
};
_contextMenu.DisconnectClicked += (sender, args) => DisconnectConnection(SelectedNode);
_contextMenu.TransferFileClicked += (sender, args) => SshTransferFile();
_contextMenu.DuplicateClicked += (sender, args) => DuplicateSelectedNode();
_contextMenu.RenameClicked += (sender, args) => RenameSelectedNode();
_contextMenu.DeleteClicked += (sender, args) => DeleteSelectedNode();
_contextMenu.ImportFileClicked += (sender, args) =>
{
var selectedNodeAsContainer = SelectedNode as ContainerInfo ?? SelectedNode.Parent;
Import.ImportFromFile(selectedNodeAsContainer);
};
_contextMenu.ImportActiveDirectoryClicked += (sender, args) => Windows.Show(WindowType.ActiveDirectoryImport);
_contextMenu.ImportPortScanClicked += (sender, args) => Windows.Show(WindowType.PortScan);
_contextMenu.ExportFileClicked += (sender, args) => Export.ExportToFile(SelectedNode, Runtime.ConnectionTreeModel);
_contextMenu.AddConnectionClicked += cMenTreeAddConnection_Click;
_contextMenu.AddFolderClicked += cMenTreeAddFolder_Click;
_contextMenu.SortAscendingClicked += (sender, args) => SortNodesRecursive(SelectedNode, ListSortDirection.Ascending);
_contextMenu.SortDescendingClicked += (sender, args) => SortNodesRecursive(SelectedNode, ListSortDirection.Descending);
_contextMenu.MoveUpClicked += cMenTreeMoveUp_Click;
_contextMenu.MoveDownClicked += cMenTreeMoveDown_Click;
_contextMenu.ExternalToolClicked += (sender, args) => StartExternalApp((ExternalTool)((ToolStripMenuItem)sender).Tag);
}
private void SetMenuEventHandlers()
{
mMenViewExpandAllFolders.Click += (sender, args) => olvConnections.ExpandAll();
mMenViewCollapseAllFolders.Click += (sender, args) =>
{
olvConnections.CollapseAll();
olvConnections.Expand(GetRootConnectionNode());
};
mMenSortAscending.Click += (sender, args) => SortNodesRecursive(GetRootConnectionNode(), ListSortDirection.Ascending);
}
private void PopulateTreeView()
{
UnregisterModelUpdateHandlers();
olvConnections.SetObjects(ConnectionTreeModel.RootNodes);
RegisterModelUpdateHandlers();
_nodeSearcher = new NodeSearcher(ConnectionTreeModel);
ExpandPreviouslyOpenedFolders();
ExpandRootConnectionNode();
OpenConnectionsFromLastSession();
}
private void RegisterModelUpdateHandlers()
{
_puttySessionsManager.PuttySessionsCollectionChanged += OnPuttySessionsCollectionChanged;
ConnectionTreeModel.CollectionChanged += HandleCollectionChanged;
ConnectionTreeModel.PropertyChanged += HandleCollectionPropertyChanged;
}
private void UnregisterModelUpdateHandlers()
{
_puttySessionsManager.PuttySessionsCollectionChanged -= OnPuttySessionsCollectionChanged;
ConnectionTreeModel.CollectionChanged -= HandleCollectionChanged;
ConnectionTreeModel.PropertyChanged -= HandleCollectionPropertyChanged;
}
private void OnPuttySessionsCollectionChanged(object sender, NotifyCollectionChangedEventArgs args)
{
RefreshTreeObjects(GetRootPuttyNodes().ToList());
}
private void HandleCollectionPropertyChanged(object sender, PropertyChangedEventArgs propertyChangedEventArgs)
{
//TODO for some reason property changed events are getting triggered twice for each changed property. should be just once. cant find source of duplication
var property = propertyChangedEventArgs.PropertyName;
if (property != "Name" && property != "OpenConnections") return;
var senderAsConnectionInfo = sender as ConnectionInfo;
if (senderAsConnectionInfo != null)
RefreshTreeObject(senderAsConnectionInfo);
}
private void ExpandRootConnectionNode()
{
var rootConnectionNode = GetRootConnectionNode();
olvConnections.InvokeExpand(rootConnectionNode);
}
private RootNodeInfo GetRootConnectionNode()
{
return (RootNodeInfo)olvConnections.Roots.Cast<ConnectionInfo>().First(item => item is RootNodeInfo);
}
private IEnumerable<RootPuttySessionsNodeInfo> GetRootPuttyNodes()
{
return olvConnections.Objects.OfType<RootPuttySessionsNodeInfo>();
}
SetConnectionTreeEventHandlers();
Settings.Default.PropertyChanged += (sender, args) => SetConnectionTreeEventHandlers();
}
#region Form Stuff
private void Tree_Load(object sender, EventArgs e)
@@ -316,226 +80,88 @@ namespace mRemoteNG.UI.Window
}
#endregion
private void ExpandPreviouslyOpenedFolders()
{
var containerList = ConnectionTreeModel.GetRecursiveChildList(GetRootConnectionNode()).OfType<ContainerInfo>();
var previouslyExpandedNodes = containerList.Where(container => container.IsExpanded);
olvConnections.ExpandedObjects = previouslyExpandedNodes;
olvConnections.InvokeRebuildAll(true);
}
private void OpenConnectionsFromLastSession()
{
if (!Settings.Default.OpenConsFromLastSession || Settings.Default.NoReconnect) return;
var connectionInfoList = GetRootConnectionNode().GetRecursiveChildList().Where(node => !(node is ContainerInfo));
var previouslyOpenedConnections = connectionInfoList.Where(item => item.PleaseConnect);
foreach (var connectionInfo in previouslyOpenedConnections)
{
ConnectionInitiator.OpenConnection(connectionInfo);
}
}
public void EnsureRootNodeVisible()
#region ConnectionTree
private void SetConnectionTreeEventHandlers()
{
olvConnections.EnsureModelVisible(GetRootConnectionNode());
olvConnections.NodeDeletionConfirmer = new SelectedConnectionDeletionConfirmer(olvConnections, MessageBox.Show);
olvConnections.BeforeLabelEdit += tvConnections_BeforeLabelEdit;
olvConnections.AfterLabelEdit += tvConnections_AfterLabelEdit;
olvConnections.KeyDown += tvConnections_KeyDown;
olvConnections.KeyPress += tvConnections_KeyPress;
SetTreePostSetupActions();
SetConnectionTreeDoubleClickHandlers();
SetConnectionTreeSingleClickHandlers();
}
public void DuplicateSelectedNode()
{
var newNode = SelectedNode.Clone();
newNode.Parent.SetChildBelow(newNode, SelectedNode);
Runtime.SaveConnectionsAsync();
}
public void RenameSelectedNode()
{
olvConnections.SelectedItem.BeginEdit();
Runtime.SaveConnectionsAsync();
}
public void DeleteSelectedNode()
{
if (SelectedNode is RootNodeInfo || SelectedNode is PuttySessionInfo) return;
if (!UserConfirmsDeletion()) return;
ConnectionTreeModel.DeleteNode(SelectedNode);
Runtime.SaveConnectionsAsync();
}
private bool UserConfirmsDeletion()
private void SetTreePostSetupActions()
{
var selectedNodeAsContainer = SelectedNode as ContainerInfo;
if (selectedNodeAsContainer != null)
return selectedNodeAsContainer.HasChildren()
? UserConfirmsNonEmptyFolderDeletion()
: UserConfirmsEmptyFolderDeletion();
return UserConfirmsConnectionDeletion();
}
private bool UserConfirmsEmptyFolderDeletion()
{
var messagePrompt = string.Format(Language.strConfirmDeleteNodeFolder, SelectedNode.Name);
return PromptUser(messagePrompt);
}
private bool UserConfirmsNonEmptyFolderDeletion()
{
var messagePrompt = string.Format(Language.strConfirmDeleteNodeFolderNotEmpty, SelectedNode.Name);
return PromptUser(messagePrompt);
}
private bool UserConfirmsConnectionDeletion()
{
var messagePrompt = string.Format(Language.strConfirmDeleteNodeConnection, SelectedNode.Name);
return PromptUser(messagePrompt);
}
private static bool PromptUser(string promptMessage)
{
var msgBoxResponse = MessageBox.Show(promptMessage, Application.ProductName, MessageBoxButtons.YesNo, MessageBoxIcon.Question);
return (msgBoxResponse == DialogResult.Yes);
}
#region Private Methods
private void tvConnections_BeforeLabelEdit(object sender, LabelEditEventArgs e)
{
_contextMenu.DisableShortcutKeys();
}
private void tvConnections_AfterLabelEdit(object sender, LabelEditEventArgs e)
{
try
{
_contextMenu.EnableShortcutKeys();
ConnectionTreeModel.RenameNode(SelectedNode, e.Label);
Windows.ConfigForm.SelectedTreeNode = SelectedNode;
Runtime.SaveConnectionsAsync();
}
catch (Exception ex)
{
Runtime.MessageCollector.AddExceptionStackTrace("tvConnections_AfterLabelEdit (UI.Window.ConnectionTreeWindow) failed", ex);
}
}
private void tvConnections_AfterSelect(object sender, EventArgs e)
{
try
{
Windows.ConfigForm.SelectedTreeNode = SelectedNode;
}
catch (Exception ex)
{
Runtime.MessageCollector.AddExceptionStackTrace("tvConnections_AfterSelect (UI.Window.ConnectionTreeWindow) failed", ex);
}
}
private void tvConnections_NodeMouseSingleClick(object sender, CellClickEventArgs e)
{
try
{
if (e.ClickCount > 1) return;
var clickedNode = e.Model as ConnectionInfo;
if (clickedNode == null) return;
if (clickedNode.GetTreeNodeType() != TreeNodeType.Connection && clickedNode.GetTreeNodeType() != TreeNodeType.PuttySession) return;
if (Settings.Default.SingleClickOnConnectionOpensIt)
ConnectionInitiator.OpenConnection(SelectedNode);
if (Settings.Default.SingleClickSwitchesToOpenConnection)
ConnectionInitiator.SwitchToOpenConnection(SelectedNode);
}
catch (Exception ex)
{
Runtime.MessageCollector.AddExceptionStackTrace("tvConnections_NodeMouseClick (UI.Window.ConnectionTreeWindow) failed", ex);
}
}
private void tvConnections_NodeMouseDoubleClick(object sender, CellClickEventArgs e)
{
if (e.ClickCount < 2) return;
var clickedNodeAsContainer = e.Model as ContainerInfo;
if (clickedNodeAsContainer != null)
{
olvConnections.ToggleExpansion(clickedNodeAsContainer);
}
var clickedNode = e.Model as ConnectionInfo;
if (clickedNode?.GetTreeNodeType() == TreeNodeType.Connection |
clickedNode?.GetTreeNodeType() == TreeNodeType.PuttySession)
{
ConnectionInitiator.OpenConnection(SelectedNode);
}
}
private void tvConnections_CellToolTipShowing(object sender, ToolTipShowingEventArgs e)
{
try
{
var nodeProducingTooltip = (ConnectionInfo) e.Model;
e.Text = nodeProducingTooltip.Description;
}
catch (Exception ex)
{
Runtime.MessageCollector.AddExceptionStackTrace("tvConnections_MouseMove (UI.Window.ConnectionTreeWindow) failed", ex);
}
}
private void HandleCollectionChanged(object sender, NotifyCollectionChangedEventArgs args)
{
var senderAsContainerInfo = sender as ContainerInfo;
// ReSharper disable once SwitchStatementMissingSomeCases
switch (args?.Action)
var actions = new List<IConnectionTreeDelegate>
{
case NotifyCollectionChangedAction.Add:
var childList = senderAsContainerInfo?.Children;
ConnectionInfo otherChild = null;
if (childList?.Count > 1)
otherChild = childList.First(child => !args.NewItems.Contains(child));
RefreshTreeObject(otherChild ?? senderAsContainerInfo);
break;
case NotifyCollectionChangedAction.Remove:
RefreshTreeObjects(args.OldItems);
break;
case NotifyCollectionChangedAction.Move:
RefreshTreeObjects(args.OldItems);
break;
case NotifyCollectionChangedAction.Reset:
RefreshTreeObject(senderAsContainerInfo);
break;
case NotifyCollectionChangedAction.Replace:
break;
case null:
break;
}
new PreviouslyOpenedFolderExpander(),
new RootNodeExpander()
};
if (Settings.Default.OpenConsFromLastSession && !Settings.Default.NoReconnect)
actions.Add(new PreviousSessionOpener(_connectionInitiator));
olvConnections.PostSetupActions = actions;
}
private void RefreshTreeObject(ConnectionInfo modelObject)
private void SetConnectionTreeDoubleClickHandlers()
{
olvConnections.RefreshObject(modelObject);
var doubleClickHandler = new TreeNodeCompositeClickHandler
{
ClickHandlers = new ITreeNodeClickHandler[]
{
new ExpandNodeClickHandler(olvConnections),
new OpenConnectionClickHandler(_connectionInitiator)
}
};
olvConnections.DoubleClickHandler = doubleClickHandler;
}
private void RefreshTreeObjects(IList modelObjects)
{
olvConnections.RefreshObjects(modelObjects);
}
private void SetConnectionTreeSingleClickHandlers()
{
var handlers = new List<ITreeNodeClickHandler>();
if (Settings.Default.SingleClickOnConnectionOpensIt)
handlers.Add(new OpenConnectionClickHandler(_connectionInitiator));
if (Settings.Default.SingleClickSwitchesToOpenConnection)
handlers.Add(new SwitchToConnectionClickHandler(_connectionInitiator));
var singleClickHandler = new TreeNodeCompositeClickHandler {ClickHandlers = handlers};
olvConnections.SingleClickHandler = singleClickHandler;
}
#endregion
#region Top Menu
private void SetMenuEventHandlers()
{
mMenViewExpandAllFolders.Click += (sender, args) => olvConnections.ExpandAll();
mMenViewCollapseAllFolders.Click += (sender, args) =>
{
olvConnections.CollapseAll();
olvConnections.Expand(olvConnections.GetRootConnectionNode());
};
mMenSortAscending.Click += (sender, args) => SortNodesRecursive(olvConnections.GetRootConnectionNode(), ListSortDirection.Ascending);
}
#endregion
#region Tree Context Menu
private void cMenTreeAddConnection_Click(object sender, EventArgs e)
{
AddConnection();
olvConnections.AddConnection();
Runtime.SaveConnectionsAsync();
}
private void cMenTreeAddFolder_Click(object sender, EventArgs e)
{
AddFolder();
olvConnections.AddFolder();
Runtime.SaveConnectionsAsync();
}
private void SortNodesRecursive(ConnectionInfo sortTarget, ListSortDirection sortDirection)
{
if (sortTarget == null)
sortTarget = GetRootConnectionNode();
sortTarget = olvConnections.GetRootConnectionNode();
var sortTargetAsContainer = sortTarget as ContainerInfo;
if (sortTargetAsContainer != null)
@@ -546,115 +172,25 @@ namespace mRemoteNG.UI.Window
Runtime.SaveConnectionsAsync();
}
private void cMenTreeMoveUp_Click(object sender, EventArgs e)
{
SelectedNode.Parent.PromoteChild(SelectedNode);
Runtime.SaveConnectionsAsync();
}
private void cMenTreeMoveDown_Click(object sender, EventArgs e)
{
SelectedNode.Parent.DemoteChild(SelectedNode);
Runtime.SaveConnectionsAsync();
}
#endregion
#region Context Menu Actions
public void AddConnection()
{
try
{
AddNode(new ConnectionInfo());
}
catch (Exception ex)
{
Runtime.MessageCollector.AddExceptionStackTrace("UI.Window.Tree.AddConnection() failed.", ex);
}
}
public void AddFolder()
{
try
{
AddNode(new ContainerInfo());
}
catch (Exception ex)
{
Runtime.MessageCollector.AddExceptionStackTrace(Language.strErrorAddFolderFailed, ex);
}
}
private void AddNode(ConnectionInfo newNode)
{
if (SelectedNode == null) return;
DefaultConnectionInfo.Instance.SaveTo(newNode);
DefaultConnectionInheritance.Instance.SaveTo(newNode.Inheritance);
var selectedContainer = SelectedNode as ContainerInfo;
var parent = selectedContainer ?? SelectedNode?.Parent;
newNode.SetParent(parent);
olvConnections.Expand(parent);
olvConnections.SelectObject(newNode);
olvConnections.EnsureModelVisible(newNode);
private void tvConnections_BeforeLabelEdit(object sender, LabelEditEventArgs e)
{
_contextMenu.DisableShortcutKeys();
}
private void DisconnectConnection(ConnectionInfo connectionInfo)
{
try
{
if (connectionInfo == null) return;
var nodeAsContainer = connectionInfo as ContainerInfo;
if (nodeAsContainer != null)
{
foreach (var child in nodeAsContainer.Children)
{
for (var i = 0; i <= child.OpenConnections.Count - 1; i++)
{
child.OpenConnections[i].Disconnect();
}
}
}
else
{
for (var i = 0; i <= connectionInfo.OpenConnections.Count - 1; i++)
{
connectionInfo.OpenConnections[i].Disconnect();
}
}
}
catch (Exception ex)
{
Runtime.MessageCollector.AddExceptionStackTrace("DisconnectConnection (UI.Window.ConnectionTreeWindow) failed", ex);
}
}
private void SshTransferFile()
{
try
{
Windows.Show(WindowType.SSHTransfer);
Windows.SshtransferForm.Hostname = SelectedNode.Hostname;
Windows.SshtransferForm.Username = SelectedNode.Username;
Windows.SshtransferForm.Password = SelectedNode.Password;
Windows.SshtransferForm.Port = Convert.ToString(SelectedNode.Port);
}
catch (Exception ex)
{
Runtime.MessageCollector.AddExceptionStackTrace("SSHTransferFile (UI.Window.ConnectionTreeWindow) failed", ex);
}
}
private void StartExternalApp(ExternalTool externalTool)
{
try
{
if (SelectedNode.GetTreeNodeType() == TreeNodeType.Connection | SelectedNode.GetTreeNodeType() == TreeNodeType.PuttySession)
externalTool.Start(SelectedNode);
}
catch (Exception ex)
{
Runtime.MessageCollector.AddExceptionStackTrace("cMenTreeToolsExternalAppsEntry_Click failed (UI.Window.ConnectionTreeWindow)", ex);
}
}
private void tvConnections_AfterLabelEdit(object sender, LabelEditEventArgs e)
{
try
{
_contextMenu.EnableShortcutKeys();
ConnectionTree.ConnectionTreeModel.RenameNode(SelectedNode, e.Label);
Windows.ConfigForm.SelectedTreeNode = SelectedNode;
Runtime.SaveConnectionsAsync();
}
catch (Exception ex)
{
Runtime.MessageCollector.AddExceptionStackTrace("tvConnections_AfterLabelEdit (UI.Window.ConnectionTreeWindow) failed", ex);
}
}
#endregion
#region Search
@@ -683,13 +219,13 @@ namespace mRemoteNG.UI.Window
}
else if (e.KeyCode == Keys.Up)
{
var match = _nodeSearcher.PreviousMatch();
var match = olvConnections.NodeSearcher.PreviousMatch();
JumpToNode(match);
e.Handled = true;
}
else if (e.KeyCode == Keys.Down)
{
var match = _nodeSearcher.NextMatch();
var match = olvConnections.NodeSearcher.NextMatch();
JumpToNode(match);
e.Handled = true;
}
@@ -707,8 +243,8 @@ namespace mRemoteNG.UI.Window
private void txtSearch_TextChanged(object sender, EventArgs e)
{
if (txtSearch.Text == "") return;
_nodeSearcher?.SearchByName(txtSearch.Text);
JumpToNode(_nodeSearcher?.CurrentMatch);
olvConnections.NodeSearcher?.SearchByName(txtSearch.Text);
JumpToNode(olvConnections.NodeSearcher?.CurrentMatch);
}
private void JumpToNode(ConnectionInfo connectionInfo)
@@ -755,7 +291,7 @@ namespace mRemoteNG.UI.Window
if (e.KeyCode == Keys.Enter)
{
e.Handled = true;
ConnectionInitiator.OpenConnection(SelectedNode);
_connectionInitiator.OpenConnection(SelectedNode);
}
else if (e.Control && e.KeyCode == Keys.F)
{

View File

@@ -117,9 +117,6 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="imgListTree.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
<metadata name="msMain.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>119, 19</value>
</metadata>

View File

@@ -27,7 +27,8 @@ namespace mRemoteNG.UI.Window
public partial class ConnectionWindow : BaseWindow
{
public TabControl TabController;
private readonly IConnectionInitiator _connectionInitiator = new ConnectionInitiator();
#region Public Methods
public ConnectionWindow(DockContent panel, string formText = "")
@@ -333,9 +334,9 @@ namespace mRemoteNG.UI.Window
var modelAsContainer = model as ContainerInfo;
var modelAsConnection = model as ConnectionInfo;
if (modelAsContainer != null)
ConnectionInitiator.OpenConnection(modelAsContainer);
_connectionInitiator.OpenConnection(modelAsContainer);
else if (modelAsConnection != null)
ConnectionInitiator.OpenConnection(modelAsConnection);
_connectionInitiator.OpenConnection(modelAsConnection);
}
}
@@ -649,7 +650,7 @@ namespace mRemoteNG.UI.Window
{
var interfaceControl = TabController.SelectedTab?.Tag as InterfaceControl;
if (interfaceControl == null) return;
ConnectionInitiator.OpenConnection(interfaceControl.Info, ConnectionInfo.Force.DoNotJump);
_connectionInitiator.OpenConnection(interfaceControl.Info, ConnectionInfo.Force.DoNotJump);
_ignoreChangeSelectedTabClick = false;
}
catch (Exception ex)
@@ -665,7 +666,7 @@ namespace mRemoteNG.UI.Window
var interfaceControl = TabController.SelectedTab?.Tag as InterfaceControl;
if (interfaceControl == null) return;
interfaceControl.Protocol.Close();
ConnectionInitiator.OpenConnection(interfaceControl.Info, ConnectionInfo.Force.DoNotJump);
_connectionInitiator.OpenConnection(interfaceControl.Info, ConnectionInfo.Force.DoNotJump);
}
catch (Exception ex)
{

View File

@@ -187,6 +187,7 @@
<Compile Include="Connection\Converter.cs" />
<Compile Include="Connection\DefaultConnectionInfo.cs" />
<Compile Include="Connection\DefaultConnectionInheritance.cs" />
<Compile Include="Connection\IConnectionInitiator.cs" />
<Compile Include="Connection\IInheritable.cs" />
<Compile Include="Connection\IHasParent.cs" />
<Compile Include="Connection\Protocol\ProtocolFactory.cs" />
@@ -211,15 +212,31 @@
<Compile Include="Tools\ExternalToolsTypeConverter.cs" />
<Compile Include="Tools\ScanHost.cs" />
<Compile Include="Tools\SecureTransfer.cs" />
<Compile Include="Tree\AlwaysConfirmYes.cs" />
<Compile Include="Tree\SelectedConnectionDeletionConfirmer.cs" />
<Compile Include="Tree\ConnectionTreeDragAndDropHandler.cs" />
<Compile Include="Tree\ConnectionTreeModel.cs" />
<Compile Include="Tree\ClickHandlers\ExpandNodeClickHandler.cs" />
<Compile Include="Tree\ClickHandlers\ITreeNodeClickHandler.cs" />
<Compile Include="Tree\IConfirm.cs" />
<Compile Include="Tree\NodeSearcher.cs" />
<Compile Include="Tree\NodeType.cs" />
<Compile Include="Tree\ObjectListViewExtensions.cs" />
<Compile Include="Tree\ClickHandlers\OpenConnectionClickHandler.cs" />
<Compile Include="Tree\PreviouslyOpenedFolderExpander.cs" />
<Compile Include="Tree\PreviousSessionOpener.cs" />
<Compile Include="Tree\RootNodeExpander.cs" />
<Compile Include="Tree\Root\RootNodeTypeEnum.cs" />
<Compile Include="Tree\ClickHandlers\SwitchToConnectionClickHandler.cs" />
<Compile Include="Tree\ClickHandlers\TreeNodeCompositeClickHandler.cs" />
<Compile Include="UI\Controls\ConnectionContextMenu.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="UI\Controls\ConnectionTree\ConnectionTree.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="UI\Controls\ConnectionTree\ConnectionTree.Designer.cs">
<DependentUpon>ConnectionTree.cs</DependentUpon>
</Compile>
<Compile Include="UI\Controls\FilteredPropertyGrid\FilteredPropertyGrid.cs">
<SubType>Component</SubType>
</Compile>
@@ -227,12 +244,18 @@
<DependentUpon>FilteredPropertyGrid.cs</DependentUpon>
</Compile>
<Compile Include="UI\Controls\FilteredPropertyGrid\ObjectWrapper.cs" />
<Compile Include="UI\Controls\ConnectionTree\IConnectionTree.cs" />
<Compile Include="UI\Controls\IPTextBox.cs">
<SubType>UserControl</SubType>
</Compile>
<Compile Include="UI\Controls\ConnectionTree\IConnectionTreeDelegate.cs" />
<Compile Include="UI\Controls\ConnectionTree\NameColumn.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="UI\Controls\QuickConnectComboBox.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="UI\Controls\StatusImageList.cs" />
<Compile Include="UI\Controls\ToolStripSplitButton.cs">
<SubType>Component</SubType>
</Compile>

View File

@@ -0,0 +1,3 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=tree_005Cclickhandlers/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=ui_005Ccontrols_005Cconnectiontree/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>