diff --git a/mRemoteNGTests/Tree/ConnectionTreeTests.cs b/mRemoteNGTests/Tree/ConnectionTreeTests.cs
new file mode 100644
index 00000000..e2771009
--- /dev/null
+++ b/mRemoteNGTests/Tree/ConnectionTreeTests.cs
@@ -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()
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/mRemoteNGTests/mRemoteNGTests.csproj b/mRemoteNGTests/mRemoteNGTests.csproj
index a8010efe..63e3ce76 100644
--- a/mRemoteNGTests/mRemoteNGTests.csproj
+++ b/mRemoteNGTests/mRemoteNGTests.csproj
@@ -101,7 +101,9 @@
-
+
+ False
+
@@ -146,6 +148,7 @@
+
diff --git a/mRemoteV1/App/Runtime.cs b/mRemoteV1/App/Runtime.cs
index d99b8e5e..319f570b 100644
--- a/mRemoteV1/App/Runtime.cs
+++ b/mRemoteV1/App/Runtime.cs
@@ -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)
{
diff --git a/mRemoteV1/UI/Controls/ConnectionTree.cs b/mRemoteV1/UI/Controls/ConnectionTree.cs
index a94326f3..e3895eb7 100644
--- a/mRemoteV1/UI/Controls/ConnectionTree.cs
+++ b/mRemoteV1/UI/Controls/ConnectionTree.cs
@@ -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();
+ 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
}
}
\ No newline at end of file
diff --git a/mRemoteV1/UI/Forms/frmMain.cs b/mRemoteV1/UI/Forms/frmMain.cs
index 5d632e74..20a7f3f1 100644
--- a/mRemoteV1/UI/Forms/frmMain.cs
+++ b/mRemoteV1/UI/Forms/frmMain.cs
@@ -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();
}
diff --git a/mRemoteV1/UI/Window/ConnectionTreeWindow.Designer.cs b/mRemoteV1/UI/Window/ConnectionTreeWindow.Designer.cs
index 18666dda..abb971af 100644
--- a/mRemoteV1/UI/Window/ConnectionTreeWindow.Designer.cs
+++ b/mRemoteV1/UI/Window/ConnectionTreeWindow.Designer.cs
@@ -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;
}
}
diff --git a/mRemoteV1/UI/Window/ConnectionTreeWindow.cs b/mRemoteV1/UI/Window/ConnectionTreeWindow.cs
index 484e3742..0056b9bb 100644
--- a/mRemoteV1/UI/Window/ConnectionTreeWindow.cs
+++ b/mRemoteV1/UI/Window/ConnectionTreeWindow.cs
@@ -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();
- 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)
diff --git a/mRemoteV1/UI/Window/ConnectionTreeWindow.resx b/mRemoteV1/UI/Window/ConnectionTreeWindow.resx
index dfeabba7..584bd685 100644
--- a/mRemoteV1/UI/Window/ConnectionTreeWindow.resx
+++ b/mRemoteV1/UI/Window/ConnectionTreeWindow.resx
@@ -117,9 +117,6 @@
System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
- 17, 17
-
119, 19