moved all click-actions for the tree to separate classes to make them easily composable

this has the nice effect of also pushing any calls to the settings class outside the classes that are actualy doing work, making them much easier to test in isolation
This commit is contained in:
David Sparer
2017-01-12 12:47:23 -07:00
parent 978d94a2cd
commit 4c792308bb
9 changed files with 207 additions and 88 deletions

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 ConnectionTree _connectionTree;
public ExpandNodeClickHandler(ConnectionTree 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,15 @@
using mRemoteNG.Connection;
namespace mRemoteNG.Tree
{
public class OpenConnectionClickHandler : ITreeNodeClickHandler
{
public void Execute(ConnectionInfo clickedNode)
{
if (clickedNode == null) return;
if (clickedNode.GetTreeNodeType() != TreeNodeType.Connection && clickedNode.GetTreeNodeType() != TreeNodeType.PuttySession) return;
ConnectionInitiator.OpenConnection(clickedNode);
}
}
}

View File

@@ -0,0 +1,15 @@
using mRemoteNG.Connection;
namespace mRemoteNG.Tree
{
public class SwitchToConnectionClickHandler : ITreeNodeClickHandler
{
public void Execute(ConnectionInfo clickedNode)
{
if (clickedNode == null) return;
if (clickedNode.GetTreeNodeType() != TreeNodeType.Connection && clickedNode.GetTreeNodeType() != TreeNodeType.PuttySession) return;
ConnectionInitiator.SwitchToOpenConnection(clickedNode);
}
}
}

View File

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

View File

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

View File

@@ -28,6 +28,11 @@ namespace mRemoteNG.UI.Controls
public IEnumerable<IConnectionTreeDelegate> PostSetupActions { get; set; } = new IConnectionTreeDelegate[0];
public TreeNodeDoubleClickHandler DoubleClickHandler { get; set; } = new TreeNodeDoubleClickHandler();
public TreeNodeSingleClickHandler SingleClickHandler { get; set; } = new TreeNodeSingleClickHandler();
public ConnectionTreeModel ConnectionTreeModel
{
get { return _connectionTreeModel; }
@@ -149,6 +154,66 @@ namespace mRemoteNG.UI.Controls
#endregion
#region ConnectionTree Behavior
public RootNodeInfo GetRootConnectionNode()
{
return (RootNodeInfo)Roots.Cast<ConnectionInfo>().First(item => item is RootNodeInfo);
}
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);
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;
@@ -189,43 +254,6 @@ namespace mRemoteNG.UI.Controls
}
}
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);
EnsureModelVisible(newNode);
}
private void tvConnections_AfterSelect(object sender, EventArgs e)
{
try
@@ -240,40 +268,16 @@ namespace mRemoteNG.UI.Controls
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);
}
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 clickedNodeAsContainer = e.Model as ContainerInfo;
if (clickedNodeAsContainer != null)
{
ToggleExpansion(clickedNodeAsContainer);
}
var clickedNode = e.Model as ConnectionInfo;
if (clickedNode?.GetTreeNodeType() == TreeNodeType.Connection |
clickedNode?.GetTreeNodeType() == TreeNodeType.PuttySession)
{
ConnectionInitiator.OpenConnection(SelectedNode);
}
DoubleClickHandler.Execute(clickedNode);
}
private void tvConnections_CellToolTipShowing(object sender, ToolTipShowingEventArgs e)
@@ -288,29 +292,6 @@ namespace mRemoteNG.UI.Controls
Runtime.MessageCollector.AddExceptionStackTrace("tvConnections_MouseMove (UI.Window.ConnectionTreeWindow) failed", ex);
}
}
public RootNodeInfo GetRootConnectionNode()
{
return (RootNodeInfo)Roots.Cast<ConnectionInfo>().First(item => item is RootNodeInfo);
}
public IEnumerable<RootPuttySessionsNodeInfo> GetRootPuttyNodes()
{
return Objects.OfType<RootPuttySessionsNodeInfo>();
}
public void DuplicateSelectedNode()
{
var newNode = SelectedNode.Clone();
newNode.Parent.SetChildBelow(newNode, SelectedNode);
Runtime.SaveConnectionsAsync();
}
public void RenameSelectedNode()
{
SelectedItem.BeginEdit();
Runtime.SaveConnectionsAsync();
}
#endregion
}
}

View File

@@ -3,6 +3,7 @@ using mRemoteNG.Connection;
using mRemoteNG.Container;
using mRemoteNG.Tree;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
@@ -33,6 +34,7 @@ namespace mRemoteNG.UI.Window
olvConnections.ContextMenuStrip = _contextMenu;
SetMenuEventHandlers();
SetConnectionTreeEventHandlers();
Settings.Default.PropertyChanged += (sender, args) => SetConnectionTreeEventHandlers();
}
#region Form Stuff
@@ -90,6 +92,28 @@ namespace mRemoteNG.UI.Window
new RootNodeExpander(),
new PreviousSessionOpener()
};
SetConnectionTreeDoubleClickHandlers();
SetConnectionTreeSingleClickHandlers();
}
private void SetConnectionTreeDoubleClickHandlers()
{
olvConnections.DoubleClickHandler.ClickHandlers = new ITreeNodeClickHandler[]
{
new ExpandNodeClickHandler(olvConnections),
new OpenConnectionClickHandler()
};
}
private void SetConnectionTreeSingleClickHandlers()
{
var singleClickHandlers = new List<ITreeNodeClickHandler>();
if (Settings.Default.SingleClickOnConnectionOpensIt)
singleClickHandlers.Add(new OpenConnectionClickHandler());
if (Settings.Default.SingleClickSwitchesToOpenConnection)
singleClickHandlers.Add(new SwitchToConnectionClickHandler());
olvConnections.SingleClickHandler.ClickHandlers = singleClickHandlers;
}
#endregion

View File

@@ -214,13 +214,19 @@
<Compile Include="Tree\ConnectionDeletionConfirmer.cs" />
<Compile Include="Tree\ConnectionTreeDragAndDropHandler.cs" />
<Compile Include="Tree\ConnectionTreeModel.cs" />
<Compile Include="Tree\ExpandNodeClickHandler.cs" />
<Compile Include="Tree\ITreeNodeClickHandler.cs" />
<Compile Include="Tree\NodeSearcher.cs" />
<Compile Include="Tree\NodeType.cs" />
<Compile Include="Tree\ObjectListViewExtensions.cs" />
<Compile Include="Tree\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\SwitchToConnectionClickHandler.cs" />
<Compile Include="Tree\TreeNodeDoubleClickHandler.cs" />
<Compile Include="Tree\TreeNodeSingleClickHandler.cs" />
<Compile Include="UI\Controls\ConnectionContextMenu.cs">
<SubType>Component</SubType>
</Compile>