Completed splitting the connection tree from the connection tree window

This commit is contained in:
David Sparer
2016-12-13 09:00:38 -07:00
parent a22758f5cd
commit d5360c6568
8 changed files with 515 additions and 467 deletions

View File

@@ -0,0 +1,29 @@
using mRemoteNG.UI.Controls;
using NUnit.Framework;
namespace mRemoteNGTests.Tree
{
public class ConnectionTreeTests
{
private ConnectionTree _connectionTree;
[SetUp]
public void Setup()
{
_connectionTree = new ConnectionTree();
}
[TearDown]
public void Teardown()
{
_connectionTree.Dispose();
}
[Test]
public void test()
{
}
}
}

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>
@@ -146,6 +148,7 @@
<Compile Include="Security\CryptographyProviderFactoryTests.cs" />
<Compile Include="Security\EncryptedSecureStringTests.cs" />
<Compile Include="Security\LegacyRijndaelCryptographyProviderTests.cs" />
<Compile Include="Tree\ConnectionTreeTests.cs" />
<Compile Include="Tree\NodeSearcherTests.cs" />
<Compile Include="Tree\RootNodeInfoTests.cs" />
<Compile Include="UI\Controls\TestForm.cs">

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)
{

View File

@@ -1,8 +1,17 @@
using System.Collections.Generic;
using System;
using System.Collections;
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.Tools;
using mRemoteNG.Tree;
using mRemoteNG.Tree.Root;
@@ -10,12 +19,451 @@ namespace mRemoteNG.UI.Controls
{
public partial class ConnectionTree : TreeListView
{
private ConnectionTreeModel _connectionTreeModel;
private readonly ConnectionTreeDragAndDropHandler _dragAndDropHandler = new ConnectionTreeDragAndDropHandler();
private readonly PuttySessionsManager _puttySessionsManager = PuttySessionsManager.Instance;
private OLVColumn _olvNameColumn;
private ImageList imgListTree;
public ConnectionInfo SelectedNode => (ConnectionInfo)SelectedObject;
public NodeSearcher NodeSearcher { get; private set; }
public ConnectionTreeModel ConnectionTreeModel
{
get { return _connectionTreeModel; }
set
{
_connectionTreeModel = value;
PopulateTreeView();
}
}
public ConnectionTree()
{
InitializeComponent();
SetupConnectionTreeView();
}
#region ConnectionTree Setup
private void SetupConnectionTreeView()
{
CreateNameColumn();
CreateImageList();
FillImageList();
LinkModelToView();
SetupDropSink();
SetEventHandlers();
}
private void CreateNameColumn()
{
_olvNameColumn = new OLVColumn
{
AspectName = "Name",
FillsFreeSpace = true,
IsButton = true,
AspectGetter = item => ((ConnectionInfo)item).Name,
ImageGetter = ConnectionImageGetter
};
Columns.Add(_olvNameColumn);
AllColumns.Add(_olvNameColumn);
}
private void CreateImageList()
{
imgListTree = new ImageList(this.components)
{
ColorDepth = ColorDepth.Depth32Bit,
ImageSize = new System.Drawing.Size(16, 16),
TransparentColor = System.Drawing.Color.Transparent
};
SmallImageList = imgListTree;
}
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()
{
CanExpandGetter = item =>
{
var itemAsContainer = item as ContainerInfo;
return itemAsContainer?.Children.Count > 0;
};
ChildrenGetter = item => ((ContainerInfo)item).Children;
//ContextMenuStrip = _contextMenu;
}
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 SetupDropSink()
{
DropSink = new SimpleDropSink();
var dropSink = (SimpleDropSink)DropSink;
dropSink.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);
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 ExpandPreviouslyOpenedFolders()
{
var containerList = ConnectionTreeModel.GetRecursiveChildList(GetRootConnectionNode()).OfType<ContainerInfo>();
var previouslyExpandedNodes = containerList.Where(container => container.IsExpanded);
ExpandedObjects = previouslyExpandedNodes;
this.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);
}
}
#endregion
#region ConnectionTree
public void DeleteSelectedNode()
{
if (SelectedNode is RootNodeInfo || SelectedNode is PuttySessionInfo) return;
if (!UserConfirmsDeletion()) return;
ConnectionTreeModel.DeleteNode(SelectedNode);
Runtime.SaveConnectionsAsync();
}
private bool UserConfirmsDeletion()
{
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);
}
private void HandleCollectionChanged(object sender, NotifyCollectionChangedEventArgs args)
{
var senderAsContainerInfo = sender as ContainerInfo;
// ReSharper disable once SwitchStatementMissingSomeCases
switch (args?.Action)
{
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;
}
}
private void RefreshTreeObject(ConnectionInfo modelObject)
{
RefreshObject(modelObject);
}
private void RefreshTreeObjects(IList modelObjects)
{
RefreshObjects(modelObjects);
}
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 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);
}
}
public 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);
}
}
public 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 ExpandRootConnectionNode()
{
var rootConnectionNode = GetRootConnectionNode();
this.InvokeExpand(rootConnectionNode);
}
public void EnsureRootNodeVisible()
{
EnsureModelVisible(GetRootConnectionNode());
}
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)
{
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);
}
}
public RootNodeInfo GetRootConnectionNode()
@@ -40,5 +488,6 @@ namespace mRemoteNG.UI.Controls
SelectedItem.BeginEdit();
Runtime.SaveConnectionsAsync();
}
#endregion
}
}

View File

@@ -494,13 +494,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();
}
@@ -546,19 +546,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();
}

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 mRemoteNG.UI.Controls.ConnectionTree olvConnections;
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
this.olvConnections = new mRemoteNG.UI.Controls.ConnectionTree();
this.olvNameColumn = ((BrightIdeasSoftware.OLVColumn)(new BrightIdeasSoftware.OLVColumn()));
this.imgListTree = new System.Windows.Forms.ImageList(this.components);
this.pnlConnections = new System.Windows.Forms.Panel();
this.PictureBox1 = new System.Windows.Forms.PictureBox();
this.txtSearch = new System.Windows.Forms.TextBox();
@@ -42,15 +38,12 @@ 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;
@@ -64,7 +57,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 +64,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 +194,6 @@ namespace mRemoteNG.UI.Window
#endregion
private System.ComponentModel.IContainer components;
private BrightIdeasSoftware.OLVColumn olvNameColumn;
private Controls.ConnectionTree olvConnections;
}
}

View File

@@ -3,16 +3,10 @@ using mRemoteNG.Connection;
using mRemoteNG.Container;
using mRemoteNG.Tree;
using System;
using System.Collections;
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;
@@ -21,22 +15,14 @@ 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;
public ConnectionInfo SelectedNode => olvConnections.SelectedNode;
public ConnectionTreeModel ConnectionTreeModel
public ConnectionTree ConnectionTree
{
get { return _connectionTreeModel; }
set
{
_connectionTreeModel = value;
PopulateTreeView();
}
get { return olvConnections; }
set { olvConnections = value; }
}
public ConnectionTreeWindow(DockContent panel)
@@ -44,7 +30,10 @@ namespace mRemoteNG.UI.Window
WindowType = WindowType.Tree;
DockPnl = panel;
InitializeComponent();
SetupConnectionTreeView();
olvConnections.ContextMenuStrip = _contextMenu;
SetMenuEventHandlers();
SetContextMenuEventHandlers();
SetConnectionTreeEventHandlers();
}
#region Form Stuff
@@ -88,413 +77,14 @@ namespace mRemoteNG.UI.Window
}
#endregion
#region ConnectionTree Setup
private void SetupConnectionTreeView()
#region ConnectionTree
private void SetConnectionTreeEventHandlers()
{
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;
olvConnections.ContextMenuStrip = _contextMenu;
}
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 SetupDropSink()
{
var dropSink = (SimpleDropSink)olvConnections.DropSink;
dropSink.CanDropBetween = true;
}
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 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(olvConnections.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 ExpandPreviouslyOpenedFolders()
{
var containerList = ConnectionTreeModel.GetRecursiveChildList(olvConnections.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 = olvConnections.GetRootConnectionNode().GetRecursiveChildList().Where(node => !(node is ContainerInfo));
var previouslyOpenedConnections = connectionInfoList.Where(item => item.PleaseConnect);
foreach (var connectionInfo in previouslyOpenedConnections)
{
ConnectionInitiator.OpenConnection(connectionInfo);
}
}
#endregion
#region ConnectionTree
public void DuplicateSelectedNode() => olvConnections.DuplicateSelectedNode();
public void RenameSelectedNode() => olvConnections.RenameSelectedNode();
public void DeleteSelectedNode()
{
if (SelectedNode is RootNodeInfo || SelectedNode is PuttySessionInfo) return;
if (!UserConfirmsDeletion()) return;
ConnectionTreeModel.DeleteNode(SelectedNode);
Runtime.SaveConnectionsAsync();
}
private bool UserConfirmsDeletion()
{
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);
}
private void HandleCollectionChanged(object sender, NotifyCollectionChangedEventArgs args)
{
var senderAsContainerInfo = sender as ContainerInfo;
// ReSharper disable once SwitchStatementMissingSomeCases
switch (args?.Action)
{
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;
}
}
private void RefreshTreeObject(ConnectionInfo modelObject)
{
olvConnections.RefreshObject(modelObject);
}
private void RefreshTreeObjects(IList modelObjects)
{
olvConnections.RefreshObjects(modelObjects);
}
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 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 ExpandRootConnectionNode()
{
var rootConnectionNode = olvConnections.GetRootConnectionNode();
olvConnections.InvokeExpand(rootConnectionNode);
}
public void EnsureRootNodeVisible()
{
olvConnections.EnsureModelVisible(olvConnections.GetRootConnectionNode());
}
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);
}
}
#endregion
#region Top Menu
@@ -562,11 +152,11 @@ namespace mRemoteNG.UI.Window
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.DisconnectClicked += (sender, args) => olvConnections.DisconnectConnection(SelectedNode);
_contextMenu.TransferFileClicked += (sender, args) => olvConnections.SshTransferFile();
_contextMenu.DuplicateClicked += (sender, args) => olvConnections.DuplicateSelectedNode();
_contextMenu.RenameClicked += (sender, args) => olvConnections.RenameSelectedNode();
_contextMenu.DeleteClicked += (sender, args) => olvConnections.DeleteSelectedNode();
_contextMenu.ImportFileClicked += (sender, args) =>
{
var selectedNodeAsContainer = SelectedNode as ContainerInfo ?? SelectedNode.Parent;
@@ -581,18 +171,18 @@ namespace mRemoteNG.UI.Window
_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);
_contextMenu.ExternalToolClicked += (sender, args) => olvConnections.StartExternalApp((ExternalTool)((ToolStripMenuItem)sender).Tag);
}
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();
}
@@ -632,7 +222,7 @@ namespace mRemoteNG.UI.Window
try
{
_contextMenu.EnableShortcutKeys();
ConnectionTreeModel.RenameNode(SelectedNode, e.Label);
ConnectionTree.ConnectionTreeModel.RenameNode(SelectedNode, e.Label);
Windows.ConfigForm.SelectedTreeNode = SelectedNode;
Runtime.SaveConnectionsAsync();
}
@@ -669,13 +259,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;
}
@@ -693,8 +283,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)

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>