Compare commits

...

46 Commits

Author SHA1 Message Date
Dimitrij
8f26d57f40 NB release 2025-10-08 00:01:23 +01:00
Dimitrij
3bd2fe889a upd to for check x64 vc++ 2025-10-07 23:56:48 +01:00
Dimitrij
492a2629c2 Replace to x64 version 2025-10-07 23:32:21 +01:00
Dimitrij
b64ddf32ff fix 2025-10-07 23:31:28 +01:00
Dimitrij
e86a550985 Merge branch 'v1.78.2-dev' of https://github.com/mRemoteNG/mRemoteNG into v1.78.2-dev 2025-10-07 23:31:12 +01:00
Dimitrij
fcccdacb99 Remove to update 2025-10-07 23:31:05 +01:00
Dimitrij
487de4c29b Merge pull request #2871 from simonai1254/v1.78.2-dev
Update README.md featuring Icon Project
2025-10-07 22:36:13 +01:00
Dimitrij
d36c6cb067 Merge pull request #2869 from mRemoteNG/copilot/fix-color-selection-for-panel-tabs
Fix Color property converter and add missing Display category attributes for TabColor
2025-10-07 22:31:30 +01:00
Simon Monai
a4b704252b Update README.md
Include Link to Fancy Icon Collection of @bearlikelion
2025-10-07 23:29:12 +02:00
Dimitrij
93e8d26a75 Merge branch 'v1.78.2-dev' into copilot/fix-color-selection-for-panel-tabs 2025-10-07 22:26:04 +01:00
copilot-swe-agent[bot]
0a3ecaac64 Fix Color property converter and add missing category attributes
Co-authored-by: Kvarkas <3611964+Kvarkas@users.noreply.github.com>
2025-10-07 21:17:26 +00:00
Simon Monai
ea6b762021 Update README.md
Add Visual C++ Dependency back in
2025-10-07 23:16:12 +02:00
Dimitrij
265a43e31c fix 2025-10-07 22:12:15 +01:00
copilot-swe-agent[bot]
6d156586ac Initial plan 2025-10-07 21:11:51 +00:00
Simon Monai
68e3f607a3 Update README.md
Fix Formatting Issue with Dependency Link
2025-10-07 22:40:48 +02:00
Dimitrij
0b240a3902 Merge pull request #2864 from mRemoteNG/copilot/add-color-support-for-connection-folders
Add Color property to connections and folders with inheritance support
2025-10-07 21:25:20 +01:00
Dimitrij
4082761606 Merge branch 'v1.78.2-dev' into copilot/add-color-support-for-connection-folders 2025-10-07 21:24:50 +01:00
Dimitrij
e68c42ba64 Merge pull request #2867 from mRemoteNG/copilot/fix-tab-color-selection-error
[WIP] Fix tab color dropdown selection error
2025-10-07 21:17:04 +01:00
copilot-swe-agent[bot]
347546ee0e Add TabColorConverter to fix Color to String conversion issue
Co-authored-by: Kvarkas <3611964+Kvarkas@users.noreply.github.com>
2025-10-07 20:15:32 +00:00
Dimitrij
ca717d6b80 Merge pull request #2863 from mRemoteNG/copilot/add-ard-connection-support
Add ARD (Apple Remote Desktop) protocol support for macOS connections
2025-10-07 21:12:17 +01:00
copilot-swe-agent[bot]
5d623d80eb Initial plan 2025-10-07 20:10:54 +00:00
Dimitrij
a2edbd9934 Merge pull request #2865 from mRemoteNG/copilot/add-connection-tab-colors
Add configurable connection tab colors to distinguish environments
2025-10-07 21:02:05 +01:00
copilot-swe-agent[bot]
5830f39d50 Add documentation for Tab Color feature
Co-authored-by: Kvarkas <3611964+Kvarkas@users.noreply.github.com>
2025-10-07 19:51:26 +00:00
copilot-swe-agent[bot]
0aa0b59635 Add documentation for Color property in folders and inheritance
Co-authored-by: Kvarkas <3611964+Kvarkas@users.noreply.github.com>
2025-10-07 19:50:10 +00:00
copilot-swe-agent[bot]
3c6a485647 Add TabColor property to connection info and implement tab coloring
Co-authored-by: Kvarkas <3611964+Kvarkas@users.noreply.github.com>
2025-10-07 19:49:32 +00:00
copilot-swe-agent[bot]
bbe1fa8416 Add Color property to Language.Designer.cs and add inheritance tests
Co-authored-by: Kvarkas <3611964+Kvarkas@users.noreply.github.com>
2025-10-07 19:49:21 +00:00
copilot-swe-agent[bot]
ac4469bb4a Add ARD protocol support to UI, port scanning, and tests
Co-authored-by: Kvarkas <3611964+Kvarkas@users.noreply.github.com>
2025-10-07 19:48:57 +00:00
copilot-swe-agent[bot]
9e61e8eafa Add Color property to connections and folders with inheritance support
Co-authored-by: Kvarkas <3611964+Kvarkas@users.noreply.github.com>
2025-10-07 19:47:07 +00:00
copilot-swe-agent[bot]
b193199268 Add ARD protocol to VNC property attributes and default port handling
Co-authored-by: Kvarkas <3611964+Kvarkas@users.noreply.github.com>
2025-10-07 19:45:38 +00:00
copilot-swe-agent[bot]
f8b7d37af1 Add ARD (Apple Remote Desktop) protocol support
Co-authored-by: Kvarkas <3611964+Kvarkas@users.noreply.github.com>
2025-10-07 19:42:31 +00:00
Dimitrij
b3e9202d72 Merge pull request #2862 from mRemoteNG/copilot/fix-unhandled-exception-panel-closure
Fix unhandled exception when closing panel with active connections
2025-10-07 20:40:35 +01:00
copilot-swe-agent[bot]
0f819ade56 Initial plan 2025-10-07 19:39:14 +00:00
copilot-swe-agent[bot]
d682afcde2 Initial plan 2025-10-07 19:39:03 +00:00
copilot-swe-agent[bot]
e67754ee9f Fix unhandled exception when closing panel with connections
Co-authored-by: Kvarkas <3611964+Kvarkas@users.noreply.github.com>
2025-10-07 19:35:18 +00:00
copilot-swe-agent[bot]
4897771fbf Initial plan 2025-10-07 19:35:06 +00:00
copilot-swe-agent[bot]
4128f3404a Initial plan 2025-10-07 19:31:35 +00:00
Dimitrij
7bc25ceb38 Merge pull request #2861 from mRemoteNG/copilot/add-autofocus-to-password-field
[WIP] Add autofocus to password field on startup
2025-10-07 20:29:40 +01:00
Dimitrij
f77f0f5e04 Merge pull request #2859 from mRemoteNG/copilot/fix-username-field-visibility
Fix Username field visibility for External Tool protocol
2025-10-07 20:28:48 +01:00
copilot-swe-agent[bot]
0e666efaad Add autofocus to password field in FrmPassword form
Co-authored-by: Kvarkas <3611964+Kvarkas@users.noreply.github.com>
2025-10-07 19:26:14 +00:00
copilot-swe-agent[bot]
e2893b9516 Add IntApp to Username property supported protocols
Co-authored-by: Kvarkas <3611964+Kvarkas@users.noreply.github.com>
2025-10-07 19:24:01 +00:00
copilot-swe-agent[bot]
fb86b13948 Initial plan 2025-10-07 19:22:15 +00:00
copilot-swe-agent[bot]
e22cc6921d Initial plan 2025-10-07 19:19:17 +00:00
Dimitrij
42fdd91206 Merge pull request #2857 from mRemoteNG/copilot/fix-default-panel-opening
Fix Update panel auto-loading on startup
2025-10-07 20:14:21 +01:00
copilot-swe-agent[bot]
2329d95002 Remove automatic Update panel display at startup
Co-authored-by: Kvarkas <3611964+Kvarkas@users.noreply.github.com>
2025-10-07 16:49:36 +00:00
copilot-swe-agent[bot]
8dda6ba13f Fix Update panel auto-checking on load
Co-authored-by: Kvarkas <3611964+Kvarkas@users.noreply.github.com>
2025-10-07 16:47:29 +00:00
copilot-swe-agent[bot]
156e2b8056 Initial plan 2025-10-07 16:41:34 +00:00
34 changed files with 710 additions and 30 deletions

View File

@@ -17,7 +17,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
- #2734: fix native build for Windows-x64
### Added
- #2728 Add support for building mRemoteNG on Windows ARM64
- #2865: Add configurable connection tab colors to distinguish between different environments
- #2864: Add Color property to connections and folders with inheritance support
- #2863: Add ARD (Apple Remote Desktop) protocol support for macOS connections
- #2728: Add support for building mRemoteNG on Windows ARM64
- #2723: Read keyboardhook, gatewayaccesstoken and gatewaycredentialssource from RDP File
- #2690: தமிழ் (ta) Translation update
- #2643: Registry Settings: enhancements and new settings implementation
@@ -29,6 +33,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
- #2502: Updated Polish translation
### Dependency update
- #3bd2fe8: puttyng updated to x64 version (and signed)
## [1.77.3.1784]
### Fixed

View File

@@ -84,7 +84,8 @@ You will need to compile it yourself using Visual Studio.
### Minimum Requirements
* [[Microsoft .NET Desktop Runtime 9.0](https://dotnet.microsoft.com/download/dotnet/6.0](https://dotnet.microsoft.com/en-us/download/dotnet/9.0))
* [Microsoft .NET Desktop Runtime 9.0](https://dotnet.microsoft.com/download/dotnet/6.0)
* [Microsoft Visual C++ Redistributable x86 (note: use 32-bit, required until #2870 is resolved)](https://learn.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist?view=msvc-170#latest-supported-redistributable-version)
* Microsoft Terminal Service Client 6.0 or later (needed if you use RDP with mstscax.dll and/or msrdp.ocx to be registered)
### Download
@@ -129,6 +130,7 @@ _If you are using the Portable version, simply deleting the folder that contains
* [PSmRemoteNG](https://github.com/realslacker/PSmRemoteNG) A module to create mRemoteNG connection files from PowerShell.
* [mRemoteNGOpenVPN](https://github.com/T3los/mRemoteNGOpenVPN) A script that can be embedded as an external tool to control OpenVPN.
* [mRemoteNG-Icons](https://github.com/bearlikelion/mRemoteNG-Icons) A collection of fancy icons to customize the connections
## Contribute

View File

@@ -24,7 +24,7 @@ namespace mRemoteNG.App.Update
string version = $"{major}.{minor}";
foreach (var baseKey in baseKeys)
{
string path = $@"{baseKey}\{version}\VC\Runtimes\x86";
string path = $@"{baseKey}\{version}\VC\Runtimes\x64";
using (RegistryKey? key = Registry.LocalMachine.OpenSubKey(path))
{
if (key?.GetValue("Installed") is int installed && installed == 1)

View File

@@ -79,7 +79,7 @@ namespace mRemoteNG.App
// Checking Visual C++ Redistributable version
if (VCppRuntimeCheck.GetInstalledVcRedistVersions() == null || VCppRuntimeCheck.GetInstalledVcRedistVersions().Count == 0)
{
var downloadUrl2 = "https://aka.ms/vs/17/release/vc_redist.x86.exe";
var downloadUrl2 = "https://aka.ms/vs/17/release/vc_redist.x64.exe";
try
{
var result = MessageBox.Show(

View File

@@ -84,10 +84,8 @@ namespace mRemoteNG.App
try
{
await _appUpdate.GetUpdateInfoAsync();
if (_appUpdate.IsUpdateAvailable())
{
Windows.Show(WindowType.Update);
}
// Update is available, but don't show the panel automatically at startup
// User can check for updates manually via Help > Check for Updates menu
}
catch (Exception ex)
{

View File

@@ -26,7 +26,7 @@ namespace mRemoteNG.App
internal static ConfigWindow ConfigForm { get; set; } = new ConfigWindow();
internal static ErrorAndInfoWindow ErrorsForm { get; set; } = new ErrorAndInfoWindow();
private static UpdateWindow UpdateForm { get; set; } = new UpdateWindow();
internal static UpdateWindow UpdateForm { get; set; } = new UpdateWindow();
internal static SSHTransferWindow SshtransferForm { get; private set; } = new SSHTransferWindow();
internal static OptionsWindow OptionsFormWindow { get; private set; }

View File

@@ -42,6 +42,7 @@ namespace mRemoteNG.Config.Serializers.ConnectionSerializers.Xml
element.Add(new XAttribute("Descr", connectionInfo.Description));
element.Add(new XAttribute("Icon", connectionInfo.Icon));
element.Add(new XAttribute("Panel", connectionInfo.Panel));
element.Add(new XAttribute("TabColor", connectionInfo.TabColor));
element.Add(new XAttribute("Id", connectionInfo.ConstantID));
if (!Runtime.UseCredentialManager)
@@ -187,6 +188,8 @@ namespace mRemoteNG.Config.Serializers.ConnectionSerializers.Xml
element.Add(new XAttribute("InheritIcon", inheritance.Icon.ToString().ToLowerInvariant()));
if (inheritance.Panel)
element.Add(new XAttribute("InheritPanel", inheritance.Panel.ToString().ToLowerInvariant()));
if (inheritance.TabColor)
element.Add(new XAttribute("InheritTabColor", inheritance.TabColor.ToString().ToLowerInvariant()));
if (inheritance.Password)
element.Add(new XAttribute("InheritPassword", inheritance.Password.ToString().ToLowerInvariant()));
if (inheritance.Port)

View File

@@ -328,6 +328,7 @@ namespace mRemoteNG.Config.Serializers.ConnectionSerializers.Xml
connectionInfo.Inheritance.DisplayWallpaper = xmlnode.GetAttributeAsBool("InheritDisplayWallpaper");
connectionInfo.Inheritance.Icon = xmlnode.GetAttributeAsBool("InheritIcon");
connectionInfo.Inheritance.Panel = xmlnode.GetAttributeAsBool("InheritPanel");
connectionInfo.Inheritance.TabColor = xmlnode.GetAttributeAsBool("InheritTabColor");
connectionInfo.Inheritance.Port = xmlnode.GetAttributeAsBool("InheritPort");
connectionInfo.Inheritance.Protocol = xmlnode.GetAttributeAsBool("InheritProtocol");
connectionInfo.Inheritance.PuttySession = xmlnode.GetAttributeAsBool("InheritPuttySession");
@@ -350,6 +351,7 @@ namespace mRemoteNG.Config.Serializers.ConnectionSerializers.Xml
connectionInfo.Icon = xmlnode.GetAttributeAsString("Icon");
connectionInfo.Panel = xmlnode.GetAttributeAsString("Panel");
connectionInfo.TabColor = xmlnode.GetAttributeAsString("TabColor");
}
else
{

View File

@@ -61,6 +61,10 @@ namespace mRemoteNG.Config.Serializers.MiscSerializers
if (host.Vnc)
finalProtocol = ProtocolType.VNC;
break;
case ProtocolType.ARD:
if (host.Vnc)
finalProtocol = ProtocolType.ARD;
break;
default:
protocolValid = false;
break;

View File

@@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using mRemoteNG.Connection.Protocol;
using mRemoteNG.Connection.Protocol.Http;
using mRemoteNG.Connection.Protocol.RDP;
@@ -22,6 +23,8 @@ namespace mRemoteNG.Connection
private string _description;
private string _icon;
private string _panel;
private string _color;
private string _tabColor;
private string _hostname;
private ExternalAddressProvider _externalAddressProvider;
@@ -153,6 +156,28 @@ namespace mRemoteNG.Connection
set => SetField(ref _panel, value, "Panel");
}
[LocalizedAttributes.LocalizedCategory(nameof(Language.Display)),
LocalizedAttributes.LocalizedDisplayName(nameof(Language.Color)),
LocalizedAttributes.LocalizedDescription(nameof(Language.PropertyDescriptionColor)),
Editor(typeof(System.Drawing.Design.ColorEditor), typeof(System.Drawing.Design.UITypeEditor)),
TypeConverter(typeof(MiscTools.TabColorConverter))]
public virtual string Color
{
get => GetPropertyValue("Color", _color);
set => SetField(ref _color, value, "Color");
}
[LocalizedAttributes.LocalizedCategory(nameof(Language.Display)),
LocalizedAttributes.LocalizedDisplayName(nameof(Language.TabColor)),
LocalizedAttributes.LocalizedDescription(nameof(Language.PropertyDescriptionTabColor)),
Editor(typeof(System.Drawing.Design.ColorEditor), typeof(System.Drawing.Design.UITypeEditor)),
TypeConverter(typeof(MiscTools.TabColorConverter))]
public virtual string TabColor
{
get => GetPropertyValue("TabColor", _tabColor);
set => SetField(ref _tabColor, value, "TabColor");
}
#endregion
#region Connection
@@ -203,7 +228,7 @@ namespace mRemoteNG.Connection
[LocalizedAttributes.LocalizedCategory(nameof(Language.Connection), 2),
LocalizedAttributes.LocalizedDisplayName(nameof(Language.Username)),
LocalizedAttributes.LocalizedDescription(nameof(Language.PropertyDescriptionUsername)),
AttributeUsedInProtocol(ProtocolType.RDP, ProtocolType.SSH1, ProtocolType.SSH2, ProtocolType.HTTP, ProtocolType.HTTPS)]
AttributeUsedInProtocol(ProtocolType.RDP, ProtocolType.SSH1, ProtocolType.SSH2, ProtocolType.HTTP, ProtocolType.HTTPS, ProtocolType.IntApp)]
public virtual string Username
{
get => GetPropertyValue("Username", _username);
@@ -908,7 +933,7 @@ namespace mRemoteNG.Connection
LocalizedAttributes.LocalizedDisplayName(nameof(Language.Compression)),
LocalizedAttributes.LocalizedDescription(nameof(Language.PropertyDescriptionCompression)),
TypeConverter(typeof(MiscTools.EnumTypeConverter)),
AttributeUsedInProtocol(ProtocolType.VNC),
AttributeUsedInProtocol(ProtocolType.VNC, ProtocolType.ARD),
Browsable(false)]
public ProtocolVNC.Compression VNCCompression
{
@@ -920,7 +945,7 @@ namespace mRemoteNG.Connection
LocalizedAttributes.LocalizedDisplayName(nameof(Language.Encoding)),
LocalizedAttributes.LocalizedDescription(nameof(Language.PropertyDescriptionEncoding)),
TypeConverter(typeof(MiscTools.EnumTypeConverter)),
AttributeUsedInProtocol(ProtocolType.VNC),
AttributeUsedInProtocol(ProtocolType.VNC, ProtocolType.ARD),
Browsable(false)]
public ProtocolVNC.Encoding VNCEncoding
{
@@ -932,7 +957,7 @@ namespace mRemoteNG.Connection
LocalizedAttributes.LocalizedDisplayName(nameof(Language.AuthenticationMode)),
LocalizedAttributes.LocalizedDescription(nameof(Language.PropertyDescriptionAuthenticationMode)),
TypeConverter(typeof(MiscTools.EnumTypeConverter)),
AttributeUsedInProtocol(ProtocolType.VNC),
AttributeUsedInProtocol(ProtocolType.VNC, ProtocolType.ARD),
Browsable(false)]
public ProtocolVNC.AuthMode VNCAuthMode
{
@@ -944,7 +969,7 @@ namespace mRemoteNG.Connection
LocalizedAttributes.LocalizedDisplayName(nameof(Language.ProxyType)),
LocalizedAttributes.LocalizedDescription(nameof(Language.PropertyDescriptionVNCProxyType)),
TypeConverter(typeof(MiscTools.EnumTypeConverter)),
AttributeUsedInProtocol(ProtocolType.VNC),
AttributeUsedInProtocol(ProtocolType.VNC, ProtocolType.ARD),
Browsable(false)]
public ProtocolVNC.ProxyType VNCProxyType
{
@@ -955,7 +980,7 @@ namespace mRemoteNG.Connection
[LocalizedAttributes.LocalizedCategory(nameof(Language.Proxy), 7),
LocalizedAttributes.LocalizedDisplayName(nameof(Language.ProxyAddress)),
LocalizedAttributes.LocalizedDescription(nameof(Language.PropertyDescriptionVNCProxyAddress)),
AttributeUsedInProtocol(ProtocolType.VNC),
AttributeUsedInProtocol(ProtocolType.VNC, ProtocolType.ARD),
Browsable(false)]
public string VNCProxyIP
{
@@ -966,7 +991,7 @@ namespace mRemoteNG.Connection
[LocalizedAttributes.LocalizedCategory(nameof(Language.Proxy), 7),
LocalizedAttributes.LocalizedDisplayName(nameof(Language.ProxyPort)),
LocalizedAttributes.LocalizedDescription(nameof(Language.PropertyDescriptionVNCProxyPort)),
AttributeUsedInProtocol(ProtocolType.VNC),
AttributeUsedInProtocol(ProtocolType.VNC, ProtocolType.ARD),
Browsable(false)]
public int VNCProxyPort
{
@@ -977,7 +1002,7 @@ namespace mRemoteNG.Connection
[LocalizedAttributes.LocalizedCategory(nameof(Language.Proxy), 7),
LocalizedAttributes.LocalizedDisplayName(nameof(Language.ProxyUsername)),
LocalizedAttributes.LocalizedDescription(nameof(Language.PropertyDescriptionVNCProxyUsername)),
AttributeUsedInProtocol(ProtocolType.VNC),
AttributeUsedInProtocol(ProtocolType.VNC, ProtocolType.ARD),
Browsable(false)]
public string VNCProxyUsername
{
@@ -989,7 +1014,7 @@ namespace mRemoteNG.Connection
LocalizedAttributes.LocalizedDisplayName(nameof(Language.ProxyPassword)),
LocalizedAttributes.LocalizedDescription(nameof(Language.PropertyDescriptionVNCProxyPassword)),
PasswordPropertyText(true),
AttributeUsedInProtocol(ProtocolType.VNC),
AttributeUsedInProtocol(ProtocolType.VNC, ProtocolType.ARD),
Browsable(false)]
public string VNCProxyPassword
{
@@ -1001,7 +1026,7 @@ namespace mRemoteNG.Connection
LocalizedAttributes.LocalizedDisplayName(nameof(Language.Colors)),
LocalizedAttributes.LocalizedDescription(nameof(Language.PropertyDescriptionColors)),
TypeConverter(typeof(MiscTools.EnumTypeConverter)),
AttributeUsedInProtocol(ProtocolType.VNC),
AttributeUsedInProtocol(ProtocolType.VNC, ProtocolType.ARD),
Browsable(false)]
public ProtocolVNC.Colors VNCColors
{
@@ -1013,7 +1038,7 @@ namespace mRemoteNG.Connection
LocalizedAttributes.LocalizedDisplayName(nameof(Language.SmartSizeMode)),
LocalizedAttributes.LocalizedDescription(nameof(Language.PropertyDescriptionSmartSizeMode)),
TypeConverter(typeof(MiscTools.EnumTypeConverter)),
AttributeUsedInProtocol(ProtocolType.VNC)]
AttributeUsedInProtocol(ProtocolType.VNC, ProtocolType.ARD)]
public ProtocolVNC.SmartSizeMode VNCSmartSizeMode
{
get => GetPropertyValue("VNCSmartSizeMode", _vncSmartSizeMode);
@@ -1024,7 +1049,7 @@ namespace mRemoteNG.Connection
LocalizedAttributes.LocalizedDisplayName(nameof(Language.ViewOnly)),
LocalizedAttributes.LocalizedDescription(nameof(Language.PropertyDescriptionViewOnly)),
TypeConverter(typeof(MiscTools.YesNoTypeConverter)),
AttributeUsedInProtocol(ProtocolType.VNC)]
AttributeUsedInProtocol(ProtocolType.VNC, ProtocolType.ARD)]
public bool VNCViewOnly
{
get => GetPropertyValue("VNCViewOnly", _vncViewOnly);

View File

@@ -5,6 +5,7 @@ using System.Linq;
using System.Reflection;
using mRemoteNG.App;
using mRemoteNG.Connection.Protocol;
using mRemoteNG.Connection.Protocol.ARD;
using mRemoteNG.Connection.Protocol.Http;
using mRemoteNG.Connection.Protocol.PowerShell;
using mRemoteNG.Connection.Protocol.RAW;
@@ -254,6 +255,8 @@ namespace mRemoteNG.Connection
return (int)RdpProtocol.Defaults.Port;
case ProtocolType.VNC:
return (int)ProtocolVNC.Defaults.Port;
case ProtocolType.ARD:
return (int)ProtocolARD.Defaults.Port;
case ProtocolType.SSH1:
return (int)ProtocolSSH1.Defaults.Port;
case ProtocolType.SSH2:
@@ -289,6 +292,8 @@ namespace mRemoteNG.Connection
Description = Settings.Default.ConDefaultDescription;
Icon = Settings.Default.ConDefaultIcon;
Panel = Language.General;
Color = string.Empty;
TabColor = string.Empty;
}
private void SetConnectionDefaults()

View File

@@ -50,6 +50,18 @@ namespace mRemoteNG.Connection
TypeConverter(typeof(MiscTools.YesNoTypeConverter))]
public bool Panel { get; set; }
[LocalizedAttributes.LocalizedCategory(nameof(Language.Display), 2),
LocalizedAttributes.LocalizedDisplayNameInherit(nameof(Language.Color)),
LocalizedAttributes.LocalizedDescriptionInherit(nameof(Language.PropertyDescriptionColor)),
TypeConverter(typeof(MiscTools.YesNoTypeConverter))]
public bool Color { get; set; }
[LocalizedAttributes.LocalizedCategory(nameof(Language.Display), 2),
LocalizedAttributes.LocalizedDisplayNameInherit(nameof(Language.TabColor)),
LocalizedAttributes.LocalizedDescriptionInherit(nameof(Language.PropertyDescriptionTabColor)),
TypeConverter(typeof(MiscTools.YesNoTypeConverter))]
public bool TabColor { get; set; }
#endregion
#region Connection

View File

@@ -0,0 +1,19 @@
using System;
using System.Runtime.Versioning;
using mRemoteNG.Connection.Protocol.VNC;
namespace mRemoteNG.Connection.Protocol.ARD
{
[SupportedOSPlatform("windows")]
public class ProtocolARD : ProtocolVNC
{
public ProtocolARD()
{
}
public new enum Defaults
{
Port = 5900
}
}
}

View File

@@ -5,6 +5,7 @@ using mRemoteNG.Connection.Protocol.Rlogin;
using mRemoteNG.Connection.Protocol.SSH;
using mRemoteNG.Connection.Protocol.Telnet;
using mRemoteNG.Connection.Protocol.VNC;
using mRemoteNG.Connection.Protocol.ARD;
using System;
using mRemoteNG.Connection.Protocol.PowerShell;
using mRemoteNG.Resources.Language;
@@ -28,6 +29,8 @@ namespace mRemoteNG.Connection.Protocol
return rdp;
case ProtocolType.VNC:
return new ProtocolVNC();
case ProtocolType.ARD:
return new ProtocolARD();
case ProtocolType.SSH1:
return new ProtocolSSH1();
case ProtocolType.SSH2:

View File

@@ -35,6 +35,9 @@ namespace mRemoteNG.Connection.Protocol
[LocalizedAttributes.LocalizedDescription(nameof(Language.PowerShell))]
PowerShell = 10,
[LocalizedAttributes.LocalizedDescription(nameof(Language.Ard))]
ARD = 11,
[LocalizedAttributes.LocalizedDescription(nameof(Language.ExternalTool))]
IntApp = 20
}

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", "17.0.0.0")]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "18.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Language {
@@ -420,6 +420,15 @@ namespace mRemoteNG.Resources.Language {
}
}
/// <summary>
/// Looks up a localized string similar to ARD (Apple Remote Desktop).
/// </summary>
internal static string Ard {
get {
return ResourceManager.GetString("Ard", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Arguments.
/// </summary>
@@ -843,6 +852,15 @@ namespace mRemoteNG.Resources.Language {
}
}
/// <summary>
/// Looks up a localized string similar to Color.
/// </summary>
internal static string Color {
get {
return ResourceManager.GetString("Color", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Colors.
/// </summary>
@@ -3388,7 +3406,7 @@ namespace mRemoteNG.Resources.Language {
}
/// <summary>
/// Looks up a localized string similar to OpeningCommand TODO.
/// Looks up a localized string similar to Opening Command .
/// </summary>
internal static string OpeningCommand {
get {
@@ -3774,6 +3792,15 @@ namespace mRemoteNG.Resources.Language {
}
}
/// <summary>
/// Looks up a localized string similar to Sets the color for the connection or folder in the connections tree. Connections inherit this color from their parent folder..
/// </summary>
internal static string PropertyDescriptionColor {
get {
return ResourceManager.GetString("PropertyDescriptionColor", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Select the color quality to be used..
/// </summary>
@@ -4314,6 +4341,15 @@ namespace mRemoteNG.Resources.Language {
}
}
/// <summary>
/// Looks up a localized string similar to Sets the color of the connection tab. Leave empty for default theme color..
/// </summary>
internal static string PropertyDescriptionTabColor {
get {
return ResourceManager.GetString("PropertyDescriptionTabColor", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Connect to the console session of the remote host..
/// </summary>
@@ -6189,6 +6225,15 @@ namespace mRemoteNG.Resources.Language {
}
}
/// <summary>
/// Looks up a localized string similar to Tab Color.
/// </summary>
internal static string TabColor {
get {
return ResourceManager.GetString("TabColor", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Tabs &amp;&amp; Panels.
/// </summary>

View File

@@ -159,6 +159,9 @@
<data name="AskUpdatesMainInstruction" xml:space="preserve">
<value>Automatic update settings</value>
</data>
<data name="Ard" xml:space="preserve">
<value>ARD (Apple Remote Desktop)</value>
</data>
<data name="Aspect" xml:space="preserve">
<value>Aspect</value>
</data>
@@ -1020,6 +1023,12 @@ If you run into such an error, please create a new connection file!</value>
<data name="PropertyDescriptionPanel" xml:space="preserve">
<value>Sets the panel in which the connection will open.</value>
</data>
<data name="PropertyDescriptionColor" xml:space="preserve">
<value>Sets the color for the connection or folder in the connections tree. Connections inherit this color from their parent folder.</value>
</data>
<data name="PropertyDescriptionTabColor" xml:space="preserve">
<value>Sets the color of the connection tab. Leave empty for default theme color.</value>
</data>
<data name="PropertyDescriptionPassword" xml:space="preserve">
<value>Enter your password.</value>
</data>
@@ -1137,6 +1146,9 @@ If you run into such an error, please create a new connection file!</value>
<data name="CacheBitmaps" xml:space="preserve">
<value>Cache Bitmaps</value>
</data>
<data name="Color" xml:space="preserve">
<value>Color</value>
</data>
<data name="Colors" xml:space="preserve">
<value>Colors</value>
</data>
@@ -1182,6 +1194,9 @@ If you run into such an error, please create a new connection file!</value>
<data name="Panel" xml:space="preserve">
<value>Panel</value>
</data>
<data name="TabColor" xml:space="preserve">
<value>Tab Color</value>
</data>
<data name="Password" xml:space="preserve">
<value>Password</value>
</data>
@@ -2200,7 +2215,8 @@ Nightly Channel includes Alphas, Betas &amp; Release Candidates.</value>
<comment>https://docs.microsoft.com/en-us/windows/win32/termserv/imstscsecuredsettings-workdir</comment>
</data>
<data name="OpeningCommand" xml:space="preserve">
<value>OpeningCommand TODO</value>
<value>Opening Command </value>
<comment>Command what will be run after connection</comment>
</data>
<data name="PropertyDescriptionOpeningCommand" xml:space="preserve">
<value>Description of OpeningCommand TODO</value>

Binary file not shown.

View File

@@ -268,5 +268,131 @@ namespace mRemoteNG.Tools
return svc;
}
}
public class TabColorConverter : TypeConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType)
{
return sourceType == typeof(string) || sourceType == typeof(Color) || base.CanConvertFrom(context, sourceType);
}
public override bool CanConvertTo(ITypeDescriptorContext? context, Type? destinationType)
{
return destinationType == typeof(string) || destinationType == typeof(Color) || base.CanConvertTo(context, destinationType);
}
public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object? value)
{
if (value == null || (value is string str && string.IsNullOrWhiteSpace(str)))
{
return string.Empty;
}
if (value is string stringValue)
{
return stringValue;
}
if (value is Color colorValue)
{
// Convert Color to string representation
// Use named color if it's a known color, otherwise use hex format
if (colorValue.IsNamedColor)
{
return colorValue.Name;
}
else
{
// Return hex format without alpha if fully opaque, otherwise include alpha
if (colorValue.A == 255)
{
return $"#{colorValue.R:X2}{colorValue.G:X2}{colorValue.B:X2}";
}
else
{
return $"#{colorValue.A:X2}{colorValue.R:X2}{colorValue.G:X2}{colorValue.B:X2}";
}
}
}
return base.ConvertFrom(context, culture, value);
}
public override object ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType)
{
if (destinationType == typeof(string))
{
if (value == null || (value is string str && string.IsNullOrWhiteSpace(str)))
{
return string.Empty;
}
return value.ToString() ?? string.Empty;
}
if (destinationType == typeof(Color))
{
if (value == null || (value is string str && string.IsNullOrWhiteSpace(str)))
{
return Color.Empty;
}
if (value is string stringValue)
{
try
{
ColorConverter converter = new ColorConverter();
return converter.ConvertFromString(stringValue) ?? Color.Empty;
}
catch
{
return Color.Empty;
}
}
}
return base.ConvertTo(context, culture, value, destinationType) ?? throw new InvalidOperationException("Base conversion returned null.");
}
public override bool GetStandardValuesSupported(ITypeDescriptorContext? context)
{
return true;
}
public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext? context)
{
// Provide a list of common colors for the dropdown
Color[] colors =
[
Color.Red,
Color.Orange,
Color.Yellow,
Color.Green,
Color.Blue,
Color.Purple,
Color.Pink,
Color.Brown,
Color.Black,
Color.White,
Color.Gray,
Color.LightGray,
Color.DarkGray,
Color.Cyan,
Color.Magenta,
Color.Lime,
Color.Navy,
Color.Teal,
Color.Maroon,
Color.Olive
];
return new StandardValuesCollection(colors);
}
public override bool GetStandardValuesExclusive(ITypeDescriptorContext? context)
{
// Return false to allow custom values (hex codes or other color names)
return false;
}
}
}
}

View File

@@ -168,6 +168,7 @@ namespace mRemoteNG.UI.Controls.ConnectionInfoPropertyGrid
strHide.AddRange(SpecialRdpExclusions());
break;
case ProtocolType.VNC:
case ProtocolType.ARD:
strHide.AddRange(SpecialVncExclusions());
break;
}

View File

@@ -166,6 +166,7 @@ namespace mRemoteNG.UI.Controls.ConnectionTree
ModelDropped += _dragAndDropHandler.HandleEvent_ModelDropped;
BeforeLabelEdit += OnBeforeLabelEdit;
AfterLabelEdit += OnAfterLabelEdit;
FormatCell += ConnectionTree_FormatCell;
}
/// <summary>
@@ -512,6 +513,27 @@ namespace mRemoteNG.UI.Controls.ConnectionTree
_contextMenu.DisableShortcutKeys();
}
private void ConnectionTree_FormatCell(object sender, FormatCellEventArgs e)
{
if (e.Model is not ConnectionInfo connectionInfo)
return;
string colorString = connectionInfo.Color;
if (string.IsNullOrEmpty(colorString))
return;
try
{
System.Drawing.ColorConverter converter = new();
System.Drawing.Color color = (System.Drawing.Color)converter.ConvertFromString(colorString);
e.SubItem.ForeColor = color;
}
catch
{
// If color parsing fails, just ignore and use default color
}
}
private void OnAfterLabelEdit(object sender, LabelEditEventArgs e)
{
if (!_nodeInEditMode)

View File

@@ -61,9 +61,14 @@ namespace mRemoteNG.UI.Forms
pbLock.Image = display.ScaleImage(pbLock.Image);
Height = tableLayoutPanel1.Height;
if (NewPasswordMode) return;
if (NewPasswordMode)
{
txtPassword.Focus();
return;
}
lblVerify.Visible = false;
txtVerify.Visible = false;
txtPassword.Focus();
}
private void PasswordForm_FormClosed(object sender, FormClosedEventArgs e)

View File

@@ -215,9 +215,14 @@ namespace mRemoteNG.UI.Forms.OptionsPages
InitialiseCheckForUpdatesOnStartupComboBox();
}
private void btnUpdateCheckNow_Click(object sender, EventArgs e)
private async void btnUpdateCheckNow_Click(object sender, EventArgs e)
{
App.Windows.Show(WindowType.Update);
var updateWindow = App.Windows.UpdateForm;
if (updateWindow != null && !updateWindow.IsDisposed)
{
await updateWindow.PerformUpdateCheckAsync();
}
}
private void chkUseProxyForAutomaticUpdates_CheckedChanged(object sender, EventArgs e)

View File

@@ -145,7 +145,15 @@ namespace mRemoteNG.UI.Menu
#region Info
private void mMenToolsUpdate_Click(object sender, EventArgs e) => App.Windows.Show(WindowType.Update);
private async void mMenToolsUpdate_Click(object sender, EventArgs e)
{
App.Windows.Show(WindowType.Update);
var updateWindow = App.Windows.UpdateForm;
if (updateWindow != null && !updateWindow.IsDisposed)
{
await updateWindow.PerformUpdateCheckAsync();
}
}
private void mMenInfoHelp_Click(object sender, EventArgs e) => Process.Start("explorer.exe", GeneralAppInfo.UrlDocumentation);

View File

@@ -991,8 +991,11 @@ namespace mRemoteNG.UI.Tabs
rectText = DrawHelper.RtlTransform(this, rectText);
rectIcon = DrawHelper.RtlTransform(this, rectIcon);
Color activeColor = DockPane.DockPanel.Theme.ColorPalette.TabSelectedActive.Background;
Color lostFocusColor = DockPane.DockPanel.Theme.ColorPalette.TabSelectedInactive.Background;
// Get custom tab color if available
Color? customTabColor = GetCustomTabColor(tab.Content);
Color activeColor = customTabColor ?? DockPane.DockPanel.Theme.ColorPalette.TabSelectedActive.Background;
Color lostFocusColor = customTabColor ?? DockPane.DockPanel.Theme.ColorPalette.TabSelectedInactive.Background;
Color inactiveColor = DockPane.DockPanel.Theme.ColorPalette.MainWindowActive.Background;
Color mouseHoverColor = DockPane.DockPanel.Theme.ColorPalette.TabUnselectedHovered.Background;
@@ -1056,6 +1059,31 @@ namespace mRemoteNG.UI.Tabs
g.DrawIcon(tab.Content.DockHandler.Icon, rectIcon);
}
private Color? GetCustomTabColor(IDockContent content)
{
try
{
if (content is ConnectionTab connectionTab)
{
InterfaceControl interfaceControl = InterfaceControl.FindInterfaceControl(connectionTab);
if (interfaceControl?.Info != null)
{
string tabColorStr = interfaceControl.Info.TabColor;
if (!string.IsNullOrEmpty(tabColorStr))
{
ColorConverter converter = new ColorConverter();
return (Color)converter.ConvertFromString(tabColorStr);
}
}
}
}
catch
{
// If there's any error parsing the color, just return null to use default
}
return null;
}
private bool m_isMouseDown;
protected bool IsMouseDown

View File

@@ -776,6 +776,7 @@ namespace mRemoteNG.UI.Window
ProtocolBase protocolBase = sender as ProtocolBase;
if (!(protocolBase?.InterfaceControl.Parent is ConnectionTab tabPage)) return;
if (tabPage.Disposing || tabPage.IsDisposed) return;
if (IsDisposed || Disposing) return;
tabPage.protocolClose = true;
Invoke(new Action(() => tabPage.Close()));
}

View File

@@ -35,6 +35,15 @@ namespace mRemoteNG.UI.Window
FontOverrider.FontOverride(this);
}
/// <summary>
/// Checks for updates and displays the results in the window.
/// Call this method when you want to trigger an update check.
/// </summary>
public async Task PerformUpdateCheckAsync()
{
await CheckForUpdateAsync();
}
#endregion
#region Form Stuff
@@ -44,7 +53,6 @@ namespace mRemoteNG.UI.Window
ApplyTheme();
ThemeManager.getInstance().ThemeChanged += ApplyTheme;
ApplyLanguage();
await CheckForUpdateAsync();
}
private new void ApplyTheme()

View File

@@ -57,3 +57,22 @@ Only the Name and Hostname/IP properties are left over,
everything else will be inherited from the parent folder.
Of course you can also only let some of the properties be inherited.
Just play around with this a bit and you'll get the hang of it.
Color Property
==============
You can set a color for each connection or folder in the connections list.
This makes things clearer when you have many connections.
To set a color:
1. Select a connection or folder in the connections tree
2. In the properties panel, find the **Color** property under the Display category
3. Click on the color value and select a color from the color picker
When you set a color on a folder, all connections under that folder can inherit the same color
if their Color inheritance is enabled. This provides a visual way to group and identify
related connections in the tree view.
.. note::
The Color property can be inherited just like other properties. Enable inheritance
in the inheritance view to have connections automatically use their parent folder's color.

View File

@@ -87,6 +87,26 @@ Icon
The icon indicates the visual identifier for the connection.
Clicking the icon will let you set a different icon for the connection.
Tab Color
---------
.. note::
The Tab Color property is available in the Display category of the connection properties.
You can set a custom color for connection tabs to help distinguish between different environments (e.g., Development, Testing, Production).
This can be especially useful when working with critical systems like Live servers, where you want a clear visual reminder.
To set a tab color:
1. Select your connection in the Connections panel
2. In the Config panel, expand the **Display** category
3. Find the **Tab Color** property
4. Enter a color name (e.g., "Red", "Green", "Blue") or a hex color code (e.g., "#FF0000", "#00FF00")
5. Leave empty to use the default theme color
The tab color will be applied when you open the connection. You can use inheritance to set the same color for multiple connections in a folder.
Status
------

View File

@@ -0,0 +1,94 @@
using System;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using mRemoteNG.Connection;
using mRemoteNG.Tools;
using NUnit.Framework;
namespace mRemoteNGTests.Connection
{
[TestFixture]
public class ColorPropertyAttributeTests
{
[Test]
public void ColorPropertyHasTabColorConverter()
{
// Get the Color property
var propertyInfo = typeof(ConnectionInfo).GetProperty("Color");
Assert.That(propertyInfo, Is.Not.Null, "Color property should exist");
// Get the TypeConverter attribute
var typeConverterAttr = propertyInfo.GetCustomAttributes(typeof(TypeConverterAttribute), true)
.FirstOrDefault() as TypeConverterAttribute;
Assert.That(typeConverterAttr, Is.Not.Null, "Color property should have TypeConverter attribute");
Assert.That(typeConverterAttr.ConverterTypeName, Does.Contain("TabColorConverter"),
"Color property should use TabColorConverter");
}
[Test]
public void TabColorPropertyHasTabColorConverter()
{
// Get the TabColor property
var propertyInfo = typeof(ConnectionInfo).GetProperty("TabColor");
Assert.That(propertyInfo, Is.Not.Null, "TabColor property should exist");
// Get the TypeConverter attribute
var typeConverterAttr = propertyInfo.GetCustomAttributes(typeof(TypeConverterAttribute), true)
.FirstOrDefault() as TypeConverterAttribute;
Assert.That(typeConverterAttr, Is.Not.Null, "TabColor property should have TypeConverter attribute");
Assert.That(typeConverterAttr.ConverterTypeName, Does.Contain("TabColorConverter"),
"TabColor property should use TabColorConverter");
}
[Test]
public void ColorPropertyHasCategoryAttribute()
{
var propertyInfo = typeof(ConnectionInfo).GetProperty("Color");
Assert.That(propertyInfo, Is.Not.Null);
var categoryAttr = propertyInfo.GetCustomAttributes(typeof(CategoryAttribute), true)
.FirstOrDefault() as CategoryAttribute;
Assert.That(categoryAttr, Is.Not.Null, "Color property should have Category attribute");
}
[Test]
public void TabColorPropertyHasCategoryAttribute()
{
var propertyInfo = typeof(ConnectionInfo).GetProperty("TabColor");
Assert.That(propertyInfo, Is.Not.Null);
var categoryAttr = propertyInfo.GetCustomAttributes(typeof(CategoryAttribute), true)
.FirstOrDefault() as CategoryAttribute;
Assert.That(categoryAttr, Is.Not.Null, "TabColor property should have Category attribute");
}
[Test]
public void ColorInheritancePropertyHasCategoryAttribute()
{
var propertyInfo = typeof(ConnectionInfoInheritance).GetProperty("Color");
Assert.That(propertyInfo, Is.Not.Null);
var categoryAttr = propertyInfo.GetCustomAttributes(typeof(CategoryAttribute), true)
.FirstOrDefault() as CategoryAttribute;
Assert.That(categoryAttr, Is.Not.Null, "Color inheritance property should have Category attribute");
}
[Test]
public void TabColorInheritancePropertyHasCategoryAttribute()
{
var propertyInfo = typeof(ConnectionInfoInheritance).GetProperty("TabColor");
Assert.That(propertyInfo, Is.Not.Null);
var categoryAttr = propertyInfo.GetCustomAttributes(typeof(CategoryAttribute), true)
.FirstOrDefault() as CategoryAttribute;
Assert.That(categoryAttr, Is.Not.Null, "TabColor inheritance property should have Category attribute");
}
}
}

View File

@@ -115,6 +115,7 @@ namespace mRemoteNGTests.Connection
[TestCase(ProtocolType.SSH2, ExpectedResult = 22)]
[TestCase(ProtocolType.Telnet, ExpectedResult = 23)]
[TestCase(ProtocolType.VNC, ExpectedResult = 5900)]
[TestCase(ProtocolType.ARD, ExpectedResult = 5900)]
public int GetDefaultPortReturnsCorrectPortForProtocol(ProtocolType protocolType)
{
_connectionInfo.Protocol = protocolType;

View File

@@ -68,5 +68,29 @@ namespace mRemoteNGTests.IntegrationTests
folder3.AddChild(connection);
Assert.That(connection.Icon, Is.EqualTo(folder1.Icon));
}
[Test]
public void ConnectionInheritsColorFromFolder()
{
var folder = new ContainerInfo { Color = "Red" };
var connection = new ConnectionInfo { Inheritance = { Color = true } };
_rootNode.AddChild(folder);
folder.AddChild(connection);
Assert.That(connection.Color, Is.EqualTo(folder.Color));
}
[Test]
public void CanInheritColorThroughMultipleFolderLevels()
{
var folder1 = new ContainerInfo { Color = "Blue" };
var folder2 = new ContainerInfo { Inheritance = { Color = true } };
var folder3 = new ContainerInfo { Inheritance = { Color = true } };
var connection = new ConnectionInfo { Inheritance = { Color = true } };
_rootNode.AddChild(folder1);
folder1.AddChild(folder2);
folder2.AddChild(folder3);
folder3.AddChild(connection);
Assert.That(connection.Color, Is.EqualTo(folder1.Color));
}
}
}

View File

@@ -0,0 +1,166 @@
using System.Drawing;
using mRemoteNG.Tools;
using NUnit.Framework;
namespace mRemoteNGTests.Tools
{
public class TabColorConverterTests
{
private MiscTools.TabColorConverter _converter;
[SetUp]
public void Setup()
{
_converter = new MiscTools.TabColorConverter();
}
[TestCase(typeof(string), true)]
[TestCase(typeof(Color), true)]
public void CanConvertFrom(Type typeToConvertFrom, bool expectedOutcome)
{
var actualOutcome = _converter.CanConvertFrom(typeToConvertFrom);
Assert.That(actualOutcome, Is.EqualTo(expectedOutcome));
}
[TestCase(typeof(string), true)]
[TestCase(typeof(Color), true)]
public void CanConvertTo(Type typeToConvertTo, bool expectedOutcome)
{
var actualOutcome = _converter.CanConvertTo(typeToConvertTo);
Assert.That(actualOutcome, Is.EqualTo(expectedOutcome));
}
[Test]
public void ConvertFromColorToStringNamedColor()
{
var color = Color.Red;
var result = _converter.ConvertFrom(color);
Assert.That(result, Is.EqualTo("Red"));
}
[Test]
public void ConvertFromColorToStringCustomColor()
{
var color = Color.FromArgb(255, 128, 64, 32);
var result = _converter.ConvertFrom(color);
Assert.That(result, Is.EqualTo("#80401F"));
}
[Test]
public void ConvertFromColorToStringCustomColorWithAlpha()
{
var color = Color.FromArgb(128, 255, 0, 0);
var result = _converter.ConvertFrom(color);
Assert.That(result, Is.EqualTo("#80FF0000"));
}
[Test]
public void ConvertFromStringReturnsString()
{
var colorString = "Blue";
var result = _converter.ConvertFrom(colorString);
Assert.That(result, Is.EqualTo("Blue"));
}
[Test]
public void ConvertFromHexStringReturnsString()
{
var colorString = "#FF0000";
var result = _converter.ConvertFrom(colorString);
Assert.That(result, Is.EqualTo("#FF0000"));
}
[Test]
public void ConvertFromNullReturnsEmptyString()
{
var result = _converter.ConvertFrom(null);
Assert.That(result, Is.EqualTo(string.Empty));
}
[Test]
public void ConvertFromEmptyStringReturnsEmptyString()
{
var result = _converter.ConvertFrom("");
Assert.That(result, Is.EqualTo(string.Empty));
}
[Test]
public void ConvertToStringFromString()
{
var colorString = "Green";
var result = _converter.ConvertTo(colorString, typeof(string));
Assert.That(result, Is.EqualTo("Green"));
}
[Test]
public void ConvertToColorFromNamedString()
{
var colorString = "Red";
var result = _converter.ConvertTo(colorString, typeof(Color));
Assert.That(result, Is.EqualTo(Color.Red));
}
[Test]
public void ConvertToColorFromHexString()
{
var colorString = "#FF0000";
var result = _converter.ConvertTo(colorString, typeof(Color));
var expectedColor = Color.FromArgb(255, 255, 0, 0);
Assert.That(result, Is.EqualTo(expectedColor));
}
[Test]
public void ConvertToColorFromEmptyStringReturnsEmpty()
{
var result = _converter.ConvertTo("", typeof(Color));
Assert.That(result, Is.EqualTo(Color.Empty));
}
[Test]
public void ConvertToColorFromNullReturnsEmpty()
{
var result = _converter.ConvertTo(null, typeof(Color));
Assert.That(result, Is.EqualTo(Color.Empty));
}
[Test]
public void GetStandardValuesSupportedReturnsTrue()
{
var result = _converter.GetStandardValuesSupported(null);
Assert.That(result, Is.True);
}
[Test]
public void GetStandardValuesReturnsColorList()
{
var result = _converter.GetStandardValues(null);
Assert.That(result, Is.Not.Null);
Assert.That(result.Count, Is.GreaterThan(0));
}
[Test]
public void GetStandardValuesExclusiveReturnsFalse()
{
var result = _converter.GetStandardValuesExclusive(null);
Assert.That(result, Is.False);
}
[Test]
public void ConvertFromColorObjectDoesNotThrowException()
{
// This test verifies the fix for the "Object of type 'System.Drawing.Color' cannot be converted to type 'System.String'" error
var color = Color.FromArgb(255, 100, 150, 200);
Assert.DoesNotThrow(() => _converter.ConvertFrom(color));
}
[Test]
public void ColorPropertyUsesTabColorConverter()
{
// This test verifies that the Color property can properly handle Color objects
// by using TabColorConverter instead of System.Drawing.ColorConverter
var color = Color.Blue;
var result = _converter.ConvertFrom(color);
Assert.That(result, Is.EqualTo("Blue"));
}
}
}

View File

@@ -45,5 +45,15 @@ namespace mRemoteNGTests.UI.Forms
cancelButton.PerformClick();
Assert.That(eventFired, Is.True);
}
[Test]
public void PasswordFieldHasAutofocus()
{
// Find the password textbox control
TextBox passwordTextBox = _passwordForm.FindControl<TextBox>("txtPassword");
// Verify that the password field has focus when the form is loaded
Assert.That(passwordTextBox.Focused, Is.True, "Password field should have autofocus when form loads");
}
}
}