diff --git a/mRemoteNG/Language/Language.resx b/mRemoteNG/Language/Language.resx
index 8b08a641..0b013dfd 100644
--- a/mRemoteNG/Language/Language.resx
+++ b/mRemoteNG/Language/Language.resx
@@ -2559,4 +2559,16 @@ Nightly Channel includes Alphas, Betas & Release Candidates.
SSH engine OTP mode
+
+ &Sessions
+
+
+ Next Session
+
+
+ Previous Session
+
+
+ Jump to Session {0}
+
\ No newline at end of file
diff --git a/mRemoteNG/UI/Forms/frmMain.Designer.cs b/mRemoteNG/UI/Forms/frmMain.Designer.cs
index 58fa2c28..1803fb90 100644
--- a/mRemoteNG/UI/Forms/frmMain.Designer.cs
+++ b/mRemoteNG/UI/Forms/frmMain.Designer.cs
@@ -38,6 +38,7 @@ namespace mRemoteNG.UI.Forms
this.pnlDock = new WeifenLuo.WinFormsUI.Docking.DockPanel();
this.msMain = new System.Windows.Forms.MenuStrip();
this.fileMenu = new mRemoteNG.UI.Menu.FileMenu();
+ this.sessionsMenu = new mRemoteNG.UI.Menu.SessionsMenu();
this.viewMenu = new mRemoteNG.UI.Menu.ViewMenu();
this.toolsMenu = new mRemoteNG.UI.Menu.ToolsMenu();
this.helpMenu = new mRemoteNG.UI.Menu.HelpMenu();
@@ -75,13 +76,14 @@ namespace mRemoteNG.UI.Forms
this.msMain.GripStyle = System.Windows.Forms.ToolStripGripStyle.Visible;
this.msMain.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.fileMenu,
+ this.sessionsMenu,
this.viewMenu,
this.toolsMenu,
this.helpMenu});
this.msMain.Location = new System.Drawing.Point(3, 0);
this.msMain.Name = "msMain";
this.msMain.Padding = new System.Windows.Forms.Padding(0, 0, 1, 0);
- this.msMain.Size = new System.Drawing.Size(151, 25);
+ this.msMain.Size = new System.Drawing.Size(212, 25);
this.msMain.Stretch = false;
this.msMain.TabIndex = 0;
this.msMain.Text = "Main Toolbar";
@@ -94,6 +96,13 @@ namespace mRemoteNG.UI.Forms
this.fileMenu.Text = "&File";
this.fileMenu.TreeWindow = null;
//
+ // sessionsMenu
+ //
+ this.sessionsMenu.Margin = new System.Windows.Forms.Padding(0, 3, 0, 3);
+ this.sessionsMenu.Name = "mMenSessions";
+ this.sessionsMenu.Size = new System.Drawing.Size(61, 19);
+ this.sessionsMenu.Text = "&Sessions";
+ //
// viewMenu
//
this.viewMenu.FullscreenHandler = null;
@@ -253,6 +262,7 @@ namespace mRemoteNG.UI.Forms
internal System.Windows.Forms.ToolStripSeparator mMenSep3;
private System.ComponentModel.IContainer components;
private Menu.FileMenu fileMenu;
+ private Menu.SessionsMenu sessionsMenu;
private Menu.ViewMenu viewMenu;
private Menu.ToolsMenu toolsMenu;
private Menu.HelpMenu helpMenu;
diff --git a/mRemoteNG/UI/Forms/frmMain.cs b/mRemoteNG/UI/Forms/frmMain.cs
index e4a4af11..08399165 100644
--- a/mRemoteNG/UI/Forms/frmMain.cs
+++ b/mRemoteNG/UI/Forms/frmMain.cs
@@ -292,6 +292,7 @@ namespace mRemoteNG.UI.Forms
private void ApplyLanguage()
{
fileMenu.ApplyLanguage();
+ sessionsMenu.ApplyLanguage();
viewMenu.ApplyLanguage();
toolsMenu.ApplyLanguage();
helpMenu.ApplyLanguage();
diff --git a/mRemoteNG/UI/Menu/msMain/SessionsMenu.cs b/mRemoteNG/UI/Menu/msMain/SessionsMenu.cs
new file mode 100644
index 00000000..570a4717
--- /dev/null
+++ b/mRemoteNG/UI/Menu/msMain/SessionsMenu.cs
@@ -0,0 +1,150 @@
+using System;
+using System.Windows.Forms;
+using mRemoteNG.UI.Window;
+using mRemoteNG.Resources.Language;
+using System.Runtime.Versioning;
+using mRemoteNG.UI.Forms;
+
+namespace mRemoteNG.UI.Menu
+{
+ [SupportedOSPlatform("windows")]
+ public class SessionsMenu : ToolStripMenuItem
+ {
+ private ToolStripMenuItem _mMenSessionsNextSession;
+ private ToolStripMenuItem _mMenSessionsPreviousSession;
+ private ToolStripSeparator _mMenSessionsSep1;
+ private readonly ToolStripMenuItem[] _sessionNumberItems = new ToolStripMenuItem[9];
+
+ public SessionsMenu()
+ {
+ Initialize();
+ }
+
+ private void Initialize()
+ {
+ _mMenSessionsNextSession = new ToolStripMenuItem();
+ _mMenSessionsPreviousSession = new ToolStripMenuItem();
+ _mMenSessionsSep1 = new ToolStripSeparator();
+
+ // Initialize session number menu items (Ctrl+1 through Ctrl+9)
+ for (int i = 0; i < 9; i++)
+ {
+ _sessionNumberItems[i] = new ToolStripMenuItem();
+ }
+
+ //
+ // mMenSessions
+ //
+ DropDownItems.Add(_mMenSessionsNextSession);
+ DropDownItems.Add(_mMenSessionsPreviousSession);
+ DropDownItems.Add(_mMenSessionsSep1);
+
+ for (int i = 0; i < 9; i++)
+ {
+ DropDownItems.Add(_sessionNumberItems[i]);
+ }
+
+ Name = "mMenSessions";
+ Size = new System.Drawing.Size(61, 20);
+ Text = Language._Sessions;
+
+ //
+ // mMenSessionsNextSession
+ //
+ _mMenSessionsNextSession.Name = "mMenSessionsNextSession";
+ _mMenSessionsNextSession.ShortcutKeys = Keys.Control | Keys.Right;
+ _mMenSessionsNextSession.Size = new System.Drawing.Size(230, 22);
+ _mMenSessionsNextSession.Text = Language.NextSession;
+ _mMenSessionsNextSession.Click += mMenSessionsNextSession_Click;
+
+ //
+ // mMenSessionsPreviousSession
+ //
+ _mMenSessionsPreviousSession.Name = "mMenSessionsPreviousSession";
+ _mMenSessionsPreviousSession.ShortcutKeys = Keys.Control | Keys.Left;
+ _mMenSessionsPreviousSession.Size = new System.Drawing.Size(230, 22);
+ _mMenSessionsPreviousSession.Text = Language.PreviousSession;
+ _mMenSessionsPreviousSession.Click += mMenSessionsPreviousSession_Click;
+
+ //
+ // mMenSessionsSep1
+ //
+ _mMenSessionsSep1.Name = "mMenSessionsSep1";
+ _mMenSessionsSep1.Size = new System.Drawing.Size(227, 6);
+
+ // Initialize session number items (Ctrl+1 through Ctrl+9)
+ for (int i = 0; i < 9; i++)
+ {
+ int sessionNumber = i + 1;
+ _sessionNumberItems[i].Name = $"mMenSessionsSession{sessionNumber}";
+ _sessionNumberItems[i].ShortcutKeys = Keys.Control | (Keys.D1 + i);
+ _sessionNumberItems[i].Size = new System.Drawing.Size(230, 22);
+ _sessionNumberItems[i].Text = string.Format(Language.JumpToSession, sessionNumber);
+ int capturedIndex = i; // Capture the index for the lambda
+ _sessionNumberItems[i].Click += (s, e) => JumpToSessionNumber(capturedIndex);
+ }
+
+ // Hook up the dropdown opening event to update enabled state
+ DropDownOpening += SessionsMenu_DropDownOpening;
+ }
+
+ public void ApplyLanguage()
+ {
+ Text = Language._Sessions;
+ _mMenSessionsNextSession.Text = Language.NextSession;
+ _mMenSessionsPreviousSession.Text = Language.PreviousSession;
+
+ for (int i = 0; i < 9; i++)
+ {
+ _sessionNumberItems[i].Text = string.Format(Language.JumpToSession, i + 1);
+ }
+ }
+
+ private void SessionsMenu_DropDownOpening(object sender, EventArgs e)
+ {
+ // Update enabled state of menu items based on active sessions
+ var connectionWindow = GetActiveConnectionWindow();
+ bool hasMultipleSessions = false;
+ int sessionCount = 0;
+
+ if (connectionWindow != null)
+ {
+ var documents = connectionWindow.GetDocuments();
+ sessionCount = documents.Length;
+ hasMultipleSessions = sessionCount > 1;
+ }
+
+ _mMenSessionsNextSession.Enabled = hasMultipleSessions;
+ _mMenSessionsPreviousSession.Enabled = hasMultipleSessions;
+
+ // Enable/disable session number items based on session count
+ for (int i = 0; i < 9; i++)
+ {
+ _sessionNumberItems[i].Enabled = (i < sessionCount);
+ }
+ }
+
+ private void mMenSessionsNextSession_Click(object sender, EventArgs e)
+ {
+ var connectionWindow = GetActiveConnectionWindow();
+ connectionWindow?.NavigateToNextTab();
+ }
+
+ private void mMenSessionsPreviousSession_Click(object sender, EventArgs e)
+ {
+ var connectionWindow = GetActiveConnectionWindow();
+ connectionWindow?.NavigateToPreviousTab();
+ }
+
+ private void JumpToSessionNumber(int index)
+ {
+ var connectionWindow = GetActiveConnectionWindow();
+ connectionWindow?.NavigateToTab(index);
+ }
+
+ private ConnectionWindow GetActiveConnectionWindow()
+ {
+ return FrmMain.Default.pnlDock?.ActiveDocument as ConnectionWindow;
+ }
+ }
+}
diff --git a/mRemoteNG/UI/Window/ConnectionWindow.cs b/mRemoteNG/UI/Window/ConnectionWindow.cs
index 4fc122e3..d6d078c4 100644
--- a/mRemoteNG/UI/Window/ConnectionWindow.cs
+++ b/mRemoteNG/UI/Window/ConnectionWindow.cs
@@ -387,6 +387,34 @@ namespace mRemoteNG.UI.Window
}
}
+ internal void NavigateToTab(int index)
+ {
+ try
+ {
+ var documents = connDock.DocumentsToArray();
+ if (index < 0 || index >= documents.Length) return;
+
+ documents[index].DockHandler.Activate();
+ }
+ catch (Exception ex)
+ {
+ Runtime.MessageCollector.AddExceptionMessage("NavigateToTab (UI.Window.ConnectionWindow) failed", ex);
+ }
+ }
+
+ internal IDockContent[] GetDocuments()
+ {
+ try
+ {
+ return connDock.DocumentsToArray();
+ }
+ catch (Exception ex)
+ {
+ Runtime.MessageCollector.AddExceptionMessage("GetDocuments (UI.Window.ConnectionWindow) failed", ex);
+ return Array.Empty();
+ }
+ }
+
#endregion
#region Events