Compare commits

..

2 Commits

Author SHA1 Message Date
copilot-swe-agent[bot]
f882aaf4fc Implement dynamic tab naming for SSH/Telnet PuTTY connections
Co-authored-by: Kvarkas <3611964+Kvarkas@users.noreply.github.com>
2026-02-20 13:51:36 +00:00
copilot-swe-agent[bot]
d39f2b7f3c Initial plan 2026-02-20 13:42:49 +00:00
24 changed files with 296 additions and 556 deletions

View File

@@ -16,9 +16,9 @@ mRemoteNG is an open-source, multi-protocol, tabbed remote connections manager f
## Building the Project
### Prerequisites
- Visual Studio 2026 (version 18.4.0 or later)
- Visual Studio 2022 (version 17.14.12 or later)
- .NET 10.0 Desktop Runtime
- Windows 10/11 or Windows Server 2022+
- Windows 10/11 or Windows Server 2016+
### Build Commands
```powershell

View File

@@ -1,63 +0,0 @@
name: Remove other GitHub repo links
permissions:
contents: read
issues: write
pull-requests: write
on:
issue_comment:
types: [created, edited]
issues:
types: [opened, edited]
jobs:
filter:
runs-on: ubuntu-latest
steps:
- name: Sanitize links
uses: actions/github-script@v8
with:
script: |
const githubRepoRegex =
/\[[^\]]*\]\((?!https?:\/\/github\.com\/user-attachments\/assets\/)[^\)]*https?:\/\/github\.com\/(?!user-attachments\/assets\/)[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+[^\s)]*\)|https?:\/\/github\.com\/(?!user-attachments\/assets\/)[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+[^\s)]*/gi;
// CASE 1: Comment
if (context.payload.comment) {
const comment = context.payload.comment;
console.log("Processing comment:", comment.id);
const matches = comment.body.match(githubRepoRegex);
console.log("Matches:", matches);
if (!matches) return;
const sanitized = comment.body.replace(
githubRepoRegex,
"**[CENSORED!] Links to external GitHub repositories are not allowed**"
);
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: comment.id,
body: sanitized
});
console.log("Comment updated");
return;
}
// CASE 2: Issue body
const issue = context.payload.issue;
if (issue) {
console.log("Processing issue:", issue.number);
const matches = issue.body?.match(githubRepoRegex);
console.log("Matches:", matches);
if (!matches) return;
const sanitized = issue.body.replace(
githubRepoRegex,
"**[CENSORED!] Links to external GitHub repositories are not allowed**"
);
await github.rest.issues.update({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
body: sanitized
});
console.log("Issue body updated");
}

View File

@@ -62,7 +62,6 @@
<PackageVersion Include="System.Collections.Immutable" Version="10.0.3" />
<PackageVersion Include="System.Console" Version="4.3.1" />
<PackageVersion Include="System.Data.Common" Version="4.3.0" />
<PackageVersion Include="System.Data.Odbc" Version="10.0.3" />
<PackageVersion Include="System.Diagnostics.DiagnosticSource" Version="10.0.3" />
<PackageVersion Include="System.Drawing.Common" Version="10.0.3" />
<PackageVersion Include="System.Diagnostics.EventLog" Version="10.0.3" />

View File

@@ -6,6 +6,7 @@
<AssemblyName>ObjectListView</AssemblyName>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<UseWindowsForms>true</UseWindowsForms>
<EnableUnsafeBinaryFormatterSerialization>true</EnableUnsafeBinaryFormatterSerialization>
<NoWarn>$(NoWarn);WFO1000</NoWarn>
<EnableWinFormsAnalyzers>false</EnableWinFormsAnalyzers>
</PropertyGroup>

View File

@@ -1,38 +1,149 @@
using System;
using System;
using Microsoft.Data.SqlClient;
using System.Runtime.Versioning;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using LiteDB;
using mRemoteNG.Resources.Language;
namespace mRemoteNG.Config.DatabaseConnectors
{
//[SupportedOSPlatform("windows")]
/// <summary>
/// A helper class for testing database connectivity.
/// A helper class for testing database connectivity
/// </summary>
[SupportedOSPlatform("windows")]
///
using System;
using System.Data.SqlClient;
public class DatabaseConnectionTester
{
public async Task<ConnectionTestResult> TestConnectivity(string type, string server, string database, string username, string password)
{
try
{
using IDatabaseConnector dbConnector = DatabaseConnectorFactory.DatabaseConnector(type, server, database, username, password);
await dbConnector.ConnectAsync();
// Build the connection string based on the provided parameters
string connectionString = $"Data Source={server};Initial Catalog={database};User ID={username};Password={password}";
// Attempt to open a connection to the database
using (SqlConnection connection = new SqlConnection(connectionString))
{
await connection.OpenAsync();
}
return ConnectionTestResult.ConnectionSucceded;
}
catch (Exception ex)
catch (SqlException ex)
{
string message = ex.Message;
if (message.Contains("server was not found", StringComparison.OrdinalIgnoreCase)
|| message.Contains("network-related", StringComparison.OrdinalIgnoreCase)
|| message.Contains("instance-specific", StringComparison.OrdinalIgnoreCase))
return ConnectionTestResult.ServerNotAccessible;
if (message.Contains("Cannot open database", StringComparison.OrdinalIgnoreCase)
|| message.Contains("Unknown database", StringComparison.OrdinalIgnoreCase))
return ConnectionTestResult.UnknownDatabase;
if (message.Contains("Login failed", StringComparison.OrdinalIgnoreCase)
|| message.Contains("Access denied", StringComparison.OrdinalIgnoreCase))
return ConnectionTestResult.CredentialsRejected;
// Handle specific SQL exceptions
switch (ex.Number)
{
case 4060: // Invalid Database
return ConnectionTestResult.UnknownDatabase;
case 18456: // Login Failed
return ConnectionTestResult.CredentialsRejected;
case -1: // Server not accessible
return ConnectionTestResult.ServerNotAccessible;
default:
return ConnectionTestResult.UnknownError;
}
}
catch
{
// Handle any other exceptions
return ConnectionTestResult.UnknownError;
}
}
}
}
//public class DatabaseConnectionTester
//{
//public async Task<ConnectionTestResult> TestConnectivity(string type, string server, string database, string username, string password)
//{
//using IDatabaseConnector dbConnector = DatabaseConnectorFactory.DatabaseConnector(type, server, database, username, password);
//try
//{
// Validate architecture compatibility
//if (!Environment.Is64BitProcess)
//{
// throw new PlatformNotSupportedException("The application must run in a 64-bit process to use this database connector.");
// }
// Attempt to connect
//using (SqlConnection connection = new SqlConnection("Data Source=172.22.155.100,1433;Initial Catalog=Demo;Integrated Security=False;User ID=sa;Password=London123;Multiple Active Result Sets=True;Connect Timeout=30;Encrypt=True;Trust Server Certificate=True;Application Name=mRemoteNG;Application Intent=ReadOnly"))
//{
// connection.Open();
// Console.WriteLine("Connection successful!");
//}
//Console.WriteLine($"{RuntimeInformation.OSArchitecture}");
//Console.WriteLine($"{RuntimeInformation.ProcessArchitecture}");
//try
//{
// using (SqlConnection connection = new SqlConnection("Data Source=172.22.155.100,1433;Initial Catalog=Demo;Integrated Security=False;User ID=sa;Password=London123;Multiple Active Result Sets=True;Connect Timeout=30;Encrypt=True;Trust Server Certificate=True;Application Name=mRemoteNG;Application Intent=ReadOnly"))
// {
// connection.Open();
// Console.WriteLine("Connection successful!");
// }
//}
//catch (Exception ex)
//{
// Console.WriteLine($"Connection failed: {ex.Message}");
//}
//}
/*
try
{
using (SqlConnection connection = new SqlConnection("Data Source=172.22.155.100,1433;Initial Catalog=Demo;Integrated Security=False;User ID=sa;Password=London123;Multiple Active Result Sets=True;Connect Timeout=30;Encrypt=True;Trust Server Certificate=True;Application Name=mRemoteNG;Application Intent=ReadOnly"))
{
connection.Open();
}
}
catch (TypeInitializationException ex)
{
Console.WriteLine($"Type initialization error: {ex.InnerException?.Message}");
}
//await dbConnector.ConnectAsync();
return ConnectionTestResult.ConnectionSucceded;
}
catch (PlatformNotSupportedException ex)
{
// Log or handle architecture mismatch
Console.WriteLine(string.Format(Language.ErrorPlatformNotSupported, ex.Message));
return ConnectionTestResult.UnknownError;
}
catch (DllNotFoundException ex)
{
// Handle missing native dependencies
Console.WriteLine(string.Format(Language.ErrorMissingDependency, ex.Message));
return ConnectionTestResult.UnknownError;
}
catch (BadImageFormatException ex)
{
// Handle architecture mismatch in native libraries
Console.WriteLine(string.Format(Language.ErrorArchitectureMismatch, ex.Message));
return ConnectionTestResult.UnknownError;
}
catch (SqlException sqlException)
{
if (sqlException.Message.Contains("The server was not found"))
return ConnectionTestResult.ServerNotAccessible;
if (sqlException.Message.Contains("Cannot open database"))
return ConnectionTestResult.UnknownDatabase;
if (sqlException.Message.Contains("Login failed for user"))
return ConnectionTestResult.CredentialsRejected;
return ConnectionTestResult.UnknownError;
}
catch (Exception ex)
{
// Log unexpected errors
Console.WriteLine($"Unexpected error: {ex.Message}");
return ConnectionTestResult.UnknownError;
}
*/
// }
// }
}

View File

@@ -1,6 +1,5 @@
using mRemoteNG.App;
using mRemoteNG.Security.SymmetricEncryption;
using System;
using System.Runtime.Versioning;
namespace mRemoteNG.Config.DatabaseConnectors
@@ -23,13 +22,14 @@ namespace mRemoteNG.Config.DatabaseConnectors
public static IDatabaseConnector DatabaseConnector(string type, string server, string database, string username, string password)
{
return type switch
switch (type)
{
"mysql" => new MySqlDatabaseConnector(server, database, username, password),
"odbc" => throw new NotSupportedException("ODBC database connections are not supported for schema initialization. Please use a supported database backend."),
"mssql" => new MSSqlDatabaseConnector(server, database, username, password),
_ => new MSSqlDatabaseConnector(server, database, username, password)
};
case "mysql":
return new MySqlDatabaseConnector(server, database, username, password);
case "mssql":
default:
return new MSSqlDatabaseConnector(server, database, username, password);
}
}
}
}

View File

@@ -1,106 +0,0 @@
using System.Data;
using System.Data.Common;
using System.Data.Odbc;
using System.Threading.Tasks;
// ReSharper disable ArrangeAccessorOwnerBody
namespace mRemoteNG.Config.DatabaseConnectors
{
public class OdbcDatabaseConnector : IDatabaseConnector
{
private DbConnection _dbConnection { get; set; } = default(OdbcConnection);
private string _dbConnectionString = "";
private readonly string _connectionString;
private readonly string _dbUsername;
private readonly string _dbPassword;
public DbConnection DbConnection()
{
return _dbConnection;
}
public DbCommand DbCommand(string dbCommand)
{
return new OdbcCommand(dbCommand, (OdbcConnection) _dbConnection);
}
public bool IsConnected => (_dbConnection.State == ConnectionState.Open);
/// <summary>
/// Creates an ODBC database connector.
/// </summary>
/// <param name="connectionString">
/// An ODBC connection string or DSN name. If a plain DSN name is provided (no '=' character),
/// it is automatically wrapped as "DSN=&lt;name&gt;".
/// </param>
/// <param name="username">Optional user name appended to the connection string as UID.</param>
/// <param name="password">Optional password appended to the connection string as PWD.</param>
public OdbcDatabaseConnector(string connectionString, string username, string password)
{
_connectionString = connectionString;
_dbUsername = username;
_dbPassword = password;
Initialize();
}
private void Initialize()
{
BuildConnectionString();
_dbConnection = new OdbcConnection(_dbConnectionString);
}
private void BuildConnectionString()
{
// If no '=' present in the provided string it is a plain DSN name.
string baseString = _connectionString.Contains('=')
? _connectionString
: $"DSN={_connectionString}";
OdbcConnectionStringBuilder builder = new()
{
ConnectionString = baseString
};
if (!string.IsNullOrEmpty(_dbUsername))
builder["UID"] = _dbUsername;
if (!string.IsNullOrEmpty(_dbPassword))
builder["PWD"] = _dbPassword;
_dbConnectionString = builder.ConnectionString;
}
public void Connect()
{
_dbConnection.Open();
}
public async Task ConnectAsync()
{
await _dbConnection.OpenAsync();
}
public void Disconnect()
{
_dbConnection.Close();
}
public void AssociateItemToThisConnector(DbCommand dbCommand)
{
dbCommand.Connection = (OdbcConnection) _dbConnection;
}
public void Dispose()
{
Dispose(true);
}
private void Dispose(bool itIsSafeToFreeManagedObjects)
{
if (!itIsSafeToFreeManagedObjects) return;
_dbConnection.Close();
_dbConnection.Dispose();
}
}
}

View File

@@ -348,6 +348,7 @@ namespace mRemoteNG.Connection
private static void SetConnectionFormEventHandlers(ProtocolBase newProtocol, Form connectionForm)
{
newProtocol.Closed += ((ConnectionWindow)connectionForm).Prot_Event_Closed;
newProtocol.TitleChanged += ((ConnectionWindow)connectionForm).Prot_Event_TitleChanged;
}
private void SetConnectionEventHandlers(ProtocolBase newProtocol)

View File

@@ -305,6 +305,15 @@ namespace mRemoteNG.Connection.Protocol
remove => ClosedEvent = (ClosedEventHandler)Delegate.Remove(ClosedEvent, value);
}
public delegate void TitleChangedEventHandler(object sender, string newTitle);
private TitleChangedEventHandler TitleChangedEvent;
public event TitleChangedEventHandler TitleChanged
{
add => TitleChangedEvent = (TitleChangedEventHandler)Delegate.Combine(TitleChangedEvent, value);
remove => TitleChangedEvent = (TitleChangedEventHandler)Delegate.Remove(TitleChangedEvent, value);
}
public void Event_Closing(object sender)
{
@@ -336,6 +345,11 @@ namespace mRemoteNG.Connection.Protocol
ErrorOccuredEvent?.Invoke(sender, errorMsg, errorCode);
}
protected void Event_TitleChanged(object sender, string newTitle)
{
TitleChangedEvent?.Invoke(sender, newTitle);
}
protected void Event_ReconnectGroupCloseClicked()
{
Close();

View File

@@ -28,8 +28,11 @@ namespace mRemoteNG.Connection.Protocol
public class PuttyBase : ProtocolBase
{
private const int IDM_RECONF = 0x50; // PuTTY Settings Menu ID
private const int TitleMonitorIntervalMs = 500;
private bool _isPuttyNg;
private readonly DisplayProperties _display = new();
private System.Threading.Timer _titleMonitorTimer;
private string _lastWindowTitle;
#region Public Properties
@@ -39,9 +42,9 @@ namespace mRemoteNG.Connection.Protocol
public IntPtr PuttyHandle { get; set; }
private Process? PuttyProcess { get; set; }
private Process PuttyProcess { get; set; }
public static string? PuttyPath { get; set; }
public static string PuttyPath { get; set; }
public bool Focused => NativeMethods.GetForegroundWindow() == PuttyHandle;
@@ -60,7 +63,7 @@ namespace mRemoteNG.Connection.Protocol
public bool isRunning()
{
return PuttyProcess?.HasExited == false;
return !PuttyProcess.HasExited;
}
public void CreatePipe(object oData)
@@ -298,9 +301,6 @@ namespace mRemoteNG.Connection.Protocol
while (PuttyHandle.ToInt32() == 0 &
Environment.TickCount < startTicks + Properties.OptionsAdvancedPage.Default.MaxPuttyWaitTime * 1000)
{
if (PuttyProcess.HasExited)
break;
if (_isPuttyNg)
{
PuttyHandle = NativeMethods.FindWindowEx(InterfaceControl.Handle, new IntPtr(0), null, null);
@@ -308,28 +308,12 @@ namespace mRemoteNG.Connection.Protocol
else
{
PuttyProcess.Refresh();
IntPtr candidateHandle = PuttyProcess.MainWindowHandle;
if (candidateHandle != IntPtr.Zero)
{
// Check the window class name to distinguish the actual PuTTY
// terminal window ("PuTTY") from popup dialogs like the host key
// verification alert (class "#32770"). Dialogs must remain as
// top-level windows so the user can interact with them.
StringBuilder className = new(256);
NativeMethods.GetClassName(candidateHandle, className, className.Capacity);
string cls = className.ToString();
if (cls.Equals("PuTTY", StringComparison.OrdinalIgnoreCase))
{
PuttyHandle = candidateHandle;
}
}
PuttyHandle = PuttyProcess.MainWindowHandle;
}
if (PuttyHandle.ToInt32() == 0)
{
Thread.Sleep(100);
Thread.Sleep(0);
}
}
@@ -352,6 +336,11 @@ namespace mRemoteNG.Connection.Protocol
Resize(this, new EventArgs());
base.Connect();
// Start monitoring PuTTY window title for dynamic tab naming
_lastWindowTitle = PuttyProcess.MainWindowTitle;
_titleMonitorTimer = new System.Threading.Timer(MonitorPuttyTitle, null, TitleMonitorIntervalMs, TitleMonitorIntervalMs);
return true;
}
catch (Exception ex)
@@ -382,6 +371,32 @@ namespace mRemoteNG.Connection.Protocol
}
}
private void MonitorPuttyTitle(object state)
{
try
{
if (PuttyProcess == null || PuttyProcess.HasExited)
{
_titleMonitorTimer?.Dispose();
return;
}
PuttyProcess.Refresh();
string currentTitle = PuttyProcess.MainWindowTitle;
if (currentTitle != _lastWindowTitle)
{
_lastWindowTitle = currentTitle;
Event_TitleChanged(this, currentTitle);
}
}
catch (Exception ex)
{
_titleMonitorTimer?.Dispose();
Runtime.MessageCollector.AddMessage(MessageClass.InformationMsg,
"PuTTY title monitoring stopped: " + ex.Message, true);
}
}
protected override void Resize(object sender, EventArgs e)
{
try
@@ -422,25 +437,32 @@ namespace mRemoteNG.Connection.Protocol
public override void Close()
{
_titleMonitorTimer?.Dispose();
_titleMonitorTimer = null;
try
{
if (PuttyProcess?.HasExited == false)
if (PuttyProcess.HasExited == false)
{
PuttyProcess.Kill();
}
}
catch (Exception ex)
{
Runtime.MessageCollector.AddMessage(MessageClass.ErrorMsg, Language.PuttyKillFailed + Environment.NewLine + ex.Message, true);
Runtime.MessageCollector.AddMessage(MessageClass.ErrorMsg,
Language.PuttyKillFailed + Environment.NewLine + ex.Message,
true);
}
try
{
PuttyProcess?.Dispose();
PuttyProcess.Dispose();
}
catch (Exception ex)
{
Runtime.MessageCollector.AddMessage(MessageClass.ErrorMsg, Language.PuttyDisposeFailed + Environment.NewLine + ex.Message, true);
Runtime.MessageCollector.AddMessage(MessageClass.ErrorMsg,
Language.PuttyDisposeFailed + Environment.NewLine + ex.Message,
true);
}
base.Close();
@@ -455,7 +477,9 @@ namespace mRemoteNG.Connection.Protocol
}
catch (Exception ex)
{
Runtime.MessageCollector.AddMessage(MessageClass.ErrorMsg, Language.PuttyShowSettingsDialogFailed + Environment.NewLine + ex.Message, true);
Runtime.MessageCollector.AddMessage(MessageClass.ErrorMsg,
Language.PuttyShowSettingsDialogFailed + Environment.NewLine +
ex.Message, true);
}
}

View File

@@ -19,7 +19,7 @@ namespace mRemoteNG.Resources.Language {
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "18.0.0.0")]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Language {
@@ -177,15 +177,6 @@ namespace mRemoteNG.Resources.Language {
}
}
/// <summary>
/// Looks up a localized string similar to &amp;Sessions.
/// </summary>
internal static string _Sessions {
get {
return ResourceManager.GetString("_Sessions", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to &amp;Stop.
/// </summary>
@@ -2918,15 +2909,6 @@ namespace mRemoteNG.Resources.Language {
}
}
/// <summary>
/// Looks up a localized string similar to Jump to Session {0}.
/// </summary>
internal static string JumpToSession {
get {
return ResourceManager.GetString("JumpToSession", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to (Automatically Detect).
/// </summary>
@@ -3512,15 +3494,6 @@ namespace mRemoteNG.Resources.Language {
}
}
/// <summary>
/// Looks up a localized string similar to Next Session.
/// </summary>
internal static string NextSession {
get {
return ResourceManager.GetString("NextSession", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to No.
/// </summary>
@@ -3990,15 +3963,6 @@ namespace mRemoteNG.Resources.Language {
}
}
/// <summary>
/// Looks up a localized string similar to Previous Session.
/// </summary>
internal static string PreviousSession {
get {
return ResourceManager.GetString("PreviousSession", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Printers.
/// </summary>
@@ -6153,6 +6117,15 @@ namespace mRemoteNG.Resources.Language {
}
}
/// <summary>
/// Looks up a localized string similar to Use terminal title for tab names (SSH/Telnet).
/// </summary>
internal static string UseTerminalTitleForTabs {
get {
return ResourceManager.GetString("UseTerminalTitleForTabs", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Show Text.
/// </summary>

View File

@@ -120,18 +120,6 @@
<data name="MenuItem_About" xml:space="preserve">
<value>About</value>
</data>
<data name="_Sessions" xml:space="preserve">
<value>&amp;Sessions</value>
</data>
<data name="NextSession" xml:space="preserve">
<value>Next Session</value>
</data>
<data name="PreviousSession" xml:space="preserve">
<value>Previous Session</value>
</data>
<data name="JumpToSession" xml:space="preserve">
<value>Jump to Session {0}</value>
</data>
<data name="ActiveDirectory" xml:space="preserve">
<value>Active Directory</value>
</data>
@@ -1612,6 +1600,9 @@ If you run into such an error, please create a new connection file!</value>
<data name="ShowProtocolOnTabs" xml:space="preserve">
<value>Show protocols on tab names</value>
</data>
<data name="UseTerminalTitleForTabs" xml:space="preserve">
<value>Use terminal title for tab names (SSH/Telnet)</value>
</data>
<data name="SingleClickOnConnectionOpensIt" xml:space="preserve">
<value>Single click on connection opens it</value>
</data>

View File

@@ -1,4 +1,5 @@
//Generated for platform: x64
@@ -10,7 +11,7 @@ using System.Resources;
// Compute version values
//Build nr: 3405
//Build nr: 3225
// General Information
[assembly: AssemblyTitle("mRemoteNG")]
@@ -18,12 +19,12 @@ using System.Resources;
[assembly: AssemblyConfiguration("x64")]
[assembly: AssemblyCompany("Profi-KOM Ltd.")]
[assembly: AssemblyProduct("mRemoteNG Connection Manager")]
[assembly: AssemblyCopyright("(c) 2026 mRemoteNG")]
[assembly: AssemblyCopyright("(c) 2025 mRemoteNG")]
[assembly: AssemblyTrademark("Profi-KOM LTd.")]
[assembly: AssemblyCulture("")]
// Version information
[assembly: AssemblyVersion("1.78.2.3405")]
[assembly: AssemblyFileVersion("1.78.2.3405")]
[assembly: AssemblyVersion("1.78.2.3225")]
[assembly: AssemblyFileVersion("1.78.2.3225")]
[assembly: NeutralResourcesLanguageAttribute("en-US")]
[assembly: AssemblyInformationalVersion("1.78.2 (Nightly Build 3405) x64")]
[assembly: AssemblyInformationalVersion("1.78.2 (Nightly Build 3225) x64")]

View File

@@ -11,30 +11,14 @@
if (File.Exists(dllPath)) {
Assembly.LoadFrom(dllPath);
var sp = Host as IServiceProvider;
if (sp != null) {
// Use reflection to avoid compile-time dependency on EnvDTE
var envDteAsm = AppDomain.CurrentDomain.GetAssemblies();
Type dteType = null;
foreach (var asm in envDteAsm) {
dteType = asm.GetType("EnvDTE.DTE");
if (dteType != null) break;
}
if (dteType != null) {
var dte = sp.GetService(dteType);
if (dte != null) {
var solution = dteType.GetProperty("Solution")?.GetValue(dte);
var findItem = solution?.GetType().GetMethod("FindProjectItem", new[] { typeof(string) });
var projectItem = findItem?.Invoke(solution, new object[] { Host.TemplateFile });
var project = projectItem?.GetType().GetProperty("ContainingProject")?.GetValue(projectItem);
var cfgMgr = project?.GetType().GetProperty("ConfigurationManager")?.GetValue(project);
var activeCfg = cfgMgr?.GetType().GetProperty("ActiveConfiguration")?.GetValue(cfgMgr);
platformFromDte = activeCfg?.GetType().GetProperty("PlatformName")?.GetValue(activeCfg) as string;
}
}
}
var dte = sp?.GetService(typeof(EnvDTE.DTE)) as EnvDTE.DTE;
var projectItem = dte?.Solution?.FindProjectItem(Host.TemplateFile);
var project = projectItem?.ContainingProject;
var cfg = project?.ConfigurationManager?.ActiveConfiguration;
platformFromDte = cfg?.PlatformName;
}
} catch {
// Fallback to environment variables below
// Log the exception if necessary
}
platformValue = platformFromDte

View File

@@ -166,5 +166,17 @@ namespace mRemoteNG.Properties {
this["BindConnectionsAndConfigPanels"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("False")]
public bool UseTerminalTitleForTabs {
get {
return ((bool)(this["UseTerminalTitleForTabs"]));
}
set {
this["UseTerminalTitleForTabs"] = value;
}
}
}
}

View File

@@ -38,5 +38,8 @@
<Setting Name="BindConnectionsAndConfigPanels" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
<Setting Name="UseTerminalTitleForTabs" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
</Settings>
</SettingsFile>

View File

@@ -41,6 +41,7 @@ namespace mRemoteNG.UI.Forms.OptionsPages
chkShowLogonInfoOnTabs = new MrngCheckBox();
chkDoubleClickClosesTab = new MrngCheckBox();
chkShowProtocolOnTabs = new MrngCheckBox();
chkUseTerminalTitleForTabs = new MrngCheckBox();
chkCreateEmptyPanelOnStart = new MrngCheckBox();
chkBindConnectionsAndConfigPanels = new MrngCheckBox();
txtBoxPanelName = new MrngTextBox();
@@ -80,7 +81,7 @@ namespace mRemoteNG.UI.Forms.OptionsPages
chkIdentifyQuickConnectTabs._mice = MrngCheckBox.MouseState.OUT;
chkIdentifyQuickConnectTabs.AutoSize = true;
chkIdentifyQuickConnectTabs.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
chkIdentifyQuickConnectTabs.Location = new System.Drawing.Point(3, 72);
chkIdentifyQuickConnectTabs.Location = new System.Drawing.Point(3, 95);
chkIdentifyQuickConnectTabs.Name = "chkIdentifyQuickConnectTabs";
chkIdentifyQuickConnectTabs.Size = new System.Drawing.Size(315, 17);
chkIdentifyQuickConnectTabs.TabIndex = 4;
@@ -104,7 +105,7 @@ namespace mRemoteNG.UI.Forms.OptionsPages
chkAlwaysShowPanelSelectionDlg._mice = MrngCheckBox.MouseState.OUT;
chkAlwaysShowPanelSelectionDlg.AutoSize = true;
chkAlwaysShowPanelSelectionDlg.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
chkAlwaysShowPanelSelectionDlg.Location = new System.Drawing.Point(3, 118);
chkAlwaysShowPanelSelectionDlg.Location = new System.Drawing.Point(3, 141);
chkAlwaysShowPanelSelectionDlg.Name = "chkAlwaysShowPanelSelectionDlg";
chkAlwaysShowPanelSelectionDlg.Size = new System.Drawing.Size(347, 17);
chkAlwaysShowPanelSelectionDlg.TabIndex = 6;
@@ -128,7 +129,7 @@ namespace mRemoteNG.UI.Forms.OptionsPages
chkDoubleClickClosesTab._mice = MrngCheckBox.MouseState.OUT;
chkDoubleClickClosesTab.AutoSize = true;
chkDoubleClickClosesTab.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
chkDoubleClickClosesTab.Location = new System.Drawing.Point(3, 95);
chkDoubleClickClosesTab.Location = new System.Drawing.Point(3, 118);
chkDoubleClickClosesTab.Name = "chkDoubleClickClosesTab";
chkDoubleClickClosesTab.Size = new System.Drawing.Size(170, 17);
chkDoubleClickClosesTab.TabIndex = 5;
@@ -147,12 +148,24 @@ namespace mRemoteNG.UI.Forms.OptionsPages
chkShowProtocolOnTabs.Text = "Show protocols on tab names";
chkShowProtocolOnTabs.UseVisualStyleBackColor = true;
//
// chkUseTerminalTitleForTabs
//
chkUseTerminalTitleForTabs._mice = MrngCheckBox.MouseState.OUT;
chkUseTerminalTitleForTabs.AutoSize = true;
chkUseTerminalTitleForTabs.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
chkUseTerminalTitleForTabs.Location = new System.Drawing.Point(3, 72);
chkUseTerminalTitleForTabs.Name = "chkUseTerminalTitleForTabs";
chkUseTerminalTitleForTabs.Size = new System.Drawing.Size(270, 17);
chkUseTerminalTitleForTabs.TabIndex = 10;
chkUseTerminalTitleForTabs.Text = "Use terminal title for tab names (SSH/Telnet)";
chkUseTerminalTitleForTabs.UseVisualStyleBackColor = true;
//
// chkCreateEmptyPanelOnStart
//
chkCreateEmptyPanelOnStart._mice = MrngCheckBox.MouseState.OUT;
chkCreateEmptyPanelOnStart.AutoSize = true;
chkCreateEmptyPanelOnStart.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
chkCreateEmptyPanelOnStart.Location = new System.Drawing.Point(3, 141);
chkCreateEmptyPanelOnStart.Location = new System.Drawing.Point(3, 164);
chkCreateEmptyPanelOnStart.Name = "chkCreateEmptyPanelOnStart";
chkCreateEmptyPanelOnStart.Size = new System.Drawing.Size(271, 17);
chkCreateEmptyPanelOnStart.TabIndex = 7;
@@ -165,7 +178,7 @@ namespace mRemoteNG.UI.Forms.OptionsPages
chkBindConnectionsAndConfigPanels._mice = MrngCheckBox.MouseState.OUT;
chkBindConnectionsAndConfigPanels.AutoSize = true;
chkBindConnectionsAndConfigPanels.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
chkBindConnectionsAndConfigPanels.Location = new System.Drawing.Point(3, 210);
chkBindConnectionsAndConfigPanels.Location = new System.Drawing.Point(3, 233);
chkBindConnectionsAndConfigPanels.Name = "chkBindConnectionsAndConfigPanels";
chkBindConnectionsAndConfigPanels.Size = new System.Drawing.Size(350, 17);
chkBindConnectionsAndConfigPanels.TabIndex = 9;
@@ -175,7 +188,7 @@ namespace mRemoteNG.UI.Forms.OptionsPages
// txtBoxPanelName
//
txtBoxPanelName.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
txtBoxPanelName.Location = new System.Drawing.Point(35, 177);
txtBoxPanelName.Location = new System.Drawing.Point(35, 200);
txtBoxPanelName.Name = "txtBoxPanelName";
txtBoxPanelName.Size = new System.Drawing.Size(213, 22);
txtBoxPanelName.TabIndex = 8;
@@ -183,7 +196,7 @@ namespace mRemoteNG.UI.Forms.OptionsPages
// lblPanelName
//
lblPanelName.AutoSize = true;
lblPanelName.Location = new System.Drawing.Point(32, 161);
lblPanelName.Location = new System.Drawing.Point(32, 184);
lblPanelName.Name = "lblPanelName";
lblPanelName.Size = new System.Drawing.Size(69, 13);
lblPanelName.TabIndex = 9;
@@ -194,6 +207,7 @@ namespace mRemoteNG.UI.Forms.OptionsPages
pnlOptions.Controls.Add(chkAlwaysShowPanelTabs);
pnlOptions.Controls.Add(lblPanelName);
pnlOptions.Controls.Add(chkShowProtocolOnTabs);
pnlOptions.Controls.Add(chkUseTerminalTitleForTabs);
pnlOptions.Controls.Add(txtBoxPanelName);
pnlOptions.Controls.Add(chkDoubleClickClosesTab);
pnlOptions.Controls.Add(chkCreateEmptyPanelOnStart);
@@ -206,7 +220,7 @@ namespace mRemoteNG.UI.Forms.OptionsPages
pnlOptions.Dock = System.Windows.Forms.DockStyle.Top;
pnlOptions.Location = new System.Drawing.Point(0, 30);
pnlOptions.Name = "pnlOptions";
pnlOptions.Size = new System.Drawing.Size(610, 240);
pnlOptions.Size = new System.Drawing.Size(610, 263);
pnlOptions.TabIndex = 10;
//
// lblRegistrySettingsUsedInfo
@@ -243,6 +257,7 @@ namespace mRemoteNG.UI.Forms.OptionsPages
internal MrngCheckBox chkShowLogonInfoOnTabs;
internal MrngCheckBox chkDoubleClickClosesTab;
internal MrngCheckBox chkShowProtocolOnTabs;
internal MrngCheckBox chkUseTerminalTitleForTabs;
private MrngCheckBox chkCreateEmptyPanelOnStart;
private MrngCheckBox chkBindConnectionsAndConfigPanels;
private Controls.MrngTextBox txtBoxPanelName;

View File

@@ -42,6 +42,7 @@ namespace mRemoteNG.UI.Forms.OptionsPages
chkOpenNewTabRightOfSelected.Text = Language.OpenNewTabRight;
chkShowLogonInfoOnTabs.Text = Language.ShowLogonInfoOnTabs;
chkShowProtocolOnTabs.Text = Language.ShowProtocolOnTabs;
chkUseTerminalTitleForTabs.Text = Language.UseTerminalTitleForTabs;
chkIdentifyQuickConnectTabs.Text = Language.IdentifyQuickConnectTabs;
chkDoubleClickClosesTab.Text = Language.DoubleClickTabClosesIt;
chkAlwaysShowPanelSelectionDlg.Text = Language.AlwaysShowPanelSelection;
@@ -73,6 +74,7 @@ namespace mRemoteNG.UI.Forms.OptionsPages
chkShowLogonInfoOnTabs.Checked = Properties.OptionsTabsPanelsPage.Default.ShowLogonInfoOnTabs;
chkShowProtocolOnTabs.Checked = Properties.OptionsTabsPanelsPage.Default.ShowProtocolOnTabs;
chkUseTerminalTitleForTabs.Checked = Properties.OptionsTabsPanelsPage.Default.UseTerminalTitleForTabs;
chkIdentifyQuickConnectTabs.Checked = Properties.OptionsTabsPanelsPage.Default.IdentifyQuickConnectTabs;
chkDoubleClickClosesTab.Checked = Properties.OptionsTabsPanelsPage.Default.DoubleClickOnTabClosesIt;
chkAlwaysShowPanelSelectionDlg.Checked = Properties.OptionsTabsPanelsPage.Default.AlwaysShowPanelSelectionDlg;
@@ -105,6 +107,7 @@ namespace mRemoteNG.UI.Forms.OptionsPages
Properties.OptionsTabsPanelsPage.Default.ShowLogonInfoOnTabs = chkShowLogonInfoOnTabs.Checked;
Properties.OptionsTabsPanelsPage.Default.ShowProtocolOnTabs = chkShowProtocolOnTabs.Checked;
Properties.OptionsTabsPanelsPage.Default.UseTerminalTitleForTabs = chkUseTerminalTitleForTabs.Checked;
Properties.OptionsTabsPanelsPage.Default.IdentifyQuickConnectTabs = chkIdentifyQuickConnectTabs.Checked;
Properties.OptionsTabsPanelsPage.Default.DoubleClickOnTabClosesIt = chkDoubleClickClosesTab.Checked;
Properties.OptionsTabsPanelsPage.Default.AlwaysShowPanelSelectionDlg = chkAlwaysShowPanelSelectionDlg.Checked;

View File

@@ -38,7 +38,6 @@ 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();
@@ -76,14 +75,13 @@ 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(212, 25);
this.msMain.Size = new System.Drawing.Size(151, 25);
this.msMain.Stretch = false;
this.msMain.TabIndex = 0;
this.msMain.Text = "Main Toolbar";
@@ -96,13 +94,6 @@ 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;
@@ -262,7 +253,6 @@ 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;

View File

@@ -294,7 +294,6 @@ namespace mRemoteNG.UI.Forms
private void ApplyLanguage()
{
fileMenu.ApplyLanguage();
sessionsMenu.ApplyLanguage();
viewMenu.ApplyLanguage();
toolsMenu.ApplyLanguage();
helpMenu.ApplyLanguage();
@@ -700,7 +699,6 @@ namespace mRemoteNG.UI.Forms
private void PnlDock_ActiveDocumentChanged(object sender, EventArgs e)
{
ActivateConnection();
sessionsMenu.UpdateMenuState();
}
internal void UpdateWindowTitle()

View File

@@ -1,161 +0,0 @@
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)((int)Keys.D1 + i);
_sessionNumberItems[i].Size = new System.Drawing.Size(230, 22);
_sessionNumberItems[i].Text = string.Format(Language.JumpToSession.ToString(), sessionNumber);
_sessionNumberItems[i].Enabled = false; // Initialize as disabled
int capturedIndex = i; // Capture the index for the lambda
_sessionNumberItems[i].Click += (s, e) => JumpToSessionNumber(capturedIndex);
}
// Initialize navigation items as disabled
_mMenSessionsNextSession.Enabled = false;
_mMenSessionsPreviousSession.Enabled = false;
// 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.ToString(), i + 1);
}
}
public void UpdateMenuState()
{
// 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 SessionsMenu_DropDownOpening(object sender, EventArgs e)
{
// Update state when menu is opened (for visual feedback)
UpdateMenuState();
}
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;
}
}
}

View File

@@ -387,34 +387,6 @@ 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<IDockContent>();
}
}
#endregion
#region Events
@@ -860,6 +832,26 @@ namespace mRemoteNG.UI.Window
Invoke(new Action(() => tabPage.Close()));
}
public void Prot_Event_TitleChanged(object sender, string newTitle)
{
if (!Properties.OptionsTabsPanelsPage.Default.UseTerminalTitleForTabs) return;
ProtocolBase protocolBase = sender as ProtocolBase;
if (!(protocolBase?.InterfaceControl?.Parent is ConnectionTab tabPage)) return;
if (tabPage.Disposing || tabPage.IsDisposed) return;
if (IsDisposed || Disposing) return;
string connectionName = protocolBase.InterfaceControl.Info?.Name ?? "";
string tabText = string.IsNullOrEmpty(newTitle)
? connectionName
: $"{newTitle} ({connectionName})";
tabText = tabText.Replace("&", "&&");
if (tabPage.InvokeRequired)
tabPage.Invoke(new Action(() => tabPage.TabText = tabText));
else
tabPage.TabText = tabText;
}
#endregion
}
}

View File

@@ -9,9 +9,6 @@
<OutputType>WinExe</OutputType>
<TargetFramework>net10.0-windows10.0.26100.0</TargetFramework>
<UseWindowsForms>true</UseWindowsForms>
<TransformOnBuild>true</TransformOnBuild>
<TransformOutOfDateOnly>false</TransformOutOfDateOnly>
<OverwriteReadOnlyOutputFiles>true</OverwriteReadOnlyOutputFiles>
<ApplicationIcon>Icons\mRemoteNG.ico</ApplicationIcon>
<Version>1.78.2-dev</Version>
<Description>Multi-protocol remote connections manager</Description>
@@ -141,7 +138,6 @@
<PackageReference Include="Microsoft.Web.WebView2" />
<PackageReference Include="Microsoft.Xaml.Behaviors.Wpf" />
<PackageReference Include="MySql.Data" />
<PackageReference Include="System.Data.Odbc" />
<PackageReference Include="Newtonsoft.Json" />
<PackageReference Include="Newtonsoft.Json.Schema" />
<PackageReference Include="OpenCover" />
@@ -281,11 +277,6 @@
<SubType>UserControl</SubType>
</Compile>
</ItemGroup>
<ItemGroup>
<T4ParameterValues Include="platformType">
<Value>$(Platform)</Value>
</T4ParameterValues>
</ItemGroup>
<ItemGroup>
<None Update="Properties\AssemblyInfo.tt">
<LastGenOutput>AssemblyInfo.cs</LastGenOutput>
@@ -553,10 +544,10 @@
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<PackageReference Update="chromiumembeddedframework.runtime.win-x64" Version="145.0.26" />
<PackageReference Update="chromiumembeddedframework.runtime.win-x64" Version="144.0.12" />
</ItemGroup>
<ItemGroup Condition="'$(Platform)'=='arm64'">
<PackageReference Update="chromiumembeddedframework.runtime.win-arm64" Version="145.0.26" />
<PackageReference Update="chromiumembeddedframework.runtime.win-arm64" Version="144.0.12" />
</ItemGroup>
<ItemGroup>
<Service Include="{508349b6-6b84-4df5-91f0-309beebad82d}" />
@@ -596,5 +587,4 @@
<RemoveDir Directories="$(TempBuildDir)" />
<RemoveDir Directories="$(ProjectDir)bin\x64\Release Self-Contained" />
</Target>
<Import Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v18.0\TextTemplating\Microsoft.TextTemplating.targets" />
</Project>

View File

@@ -73,35 +73,3 @@ Connections
- Move Up
* - Ctrl+Down
- Move Down
Sessions
========
.. list-table::
:widths: 30 70
:header-rows: 1
* - Keybinding
- Action
* - Ctrl+Right
- Next Session/Tab
* - Ctrl+Left
- Previous Session/Tab
* - Ctrl+1
- Jump to Session 1
* - Ctrl+2
- Jump to Session 2
* - Ctrl+3
- Jump to Session 3
* - Ctrl+4
- Jump to Session 4
* - Ctrl+5
- Jump to Session 5
* - Ctrl+6
- Jump to Session 6
* - Ctrl+7
- Jump to Session 7
* - Ctrl+8
- Jump to Session 8
* - Ctrl+9
- Jump to Session 9