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