mirror of
https://github.com/mRemoteNG/mRemoteNG.git
synced 2026-02-17 14:07:46 +08:00
Compare commits
46 Commits
20251007-v
...
20251007-v
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8f26d57f40 | ||
|
|
3bd2fe889a | ||
|
|
492a2629c2 | ||
|
|
b64ddf32ff | ||
|
|
e86a550985 | ||
|
|
fcccdacb99 | ||
|
|
487de4c29b | ||
|
|
d36c6cb067 | ||
|
|
a4b704252b | ||
|
|
93e8d26a75 | ||
|
|
0a3ecaac64 | ||
|
|
ea6b762021 | ||
|
|
265a43e31c | ||
|
|
6d156586ac | ||
|
|
68e3f607a3 | ||
|
|
0b240a3902 | ||
|
|
4082761606 | ||
|
|
e68c42ba64 | ||
|
|
347546ee0e | ||
|
|
ca717d6b80 | ||
|
|
5d623d80eb | ||
|
|
a2edbd9934 | ||
|
|
5830f39d50 | ||
|
|
0aa0b59635 | ||
|
|
3c6a485647 | ||
|
|
bbe1fa8416 | ||
|
|
ac4469bb4a | ||
|
|
9e61e8eafa | ||
|
|
b193199268 | ||
|
|
f8b7d37af1 | ||
|
|
b3e9202d72 | ||
|
|
0f819ade56 | ||
|
|
d682afcde2 | ||
|
|
e67754ee9f | ||
|
|
4897771fbf | ||
|
|
4128f3404a | ||
|
|
7bc25ceb38 | ||
|
|
f77f0f5e04 | ||
|
|
0e666efaad | ||
|
|
e2893b9516 | ||
|
|
fb86b13948 | ||
|
|
e22cc6921d | ||
|
|
42fdd91206 | ||
|
|
2329d95002 | ||
|
|
8dda6ba13f | ||
|
|
156e2b8056 |
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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; }
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
19
mRemoteNG/Connection/Protocol/ARD/ProtocolARD.cs
Normal file
19
mRemoteNG/Connection/Protocol/ARD/ProtocolARD.cs
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
49
mRemoteNG/Language/Language.Designer.cs
generated
49
mRemoteNG/Language/Language.Designer.cs
generated
@@ -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 && Panels.
|
||||
/// </summary>
|
||||
|
||||
@@ -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 & 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.
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -168,6 +168,7 @@ namespace mRemoteNG.UI.Controls.ConnectionInfoPropertyGrid
|
||||
strHide.AddRange(SpecialRdpExclusions());
|
||||
break;
|
||||
case ProtocolType.VNC:
|
||||
case ProtocolType.ARD:
|
||||
strHide.AddRange(SpecialVncExclusions());
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
------
|
||||
|
||||
|
||||
94
mRemoteNGTests/Connection/ColorPropertyAttributeTests.cs
Normal file
94
mRemoteNGTests/Connection/ColorPropertyAttributeTests.cs
Normal 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
166
mRemoteNGTests/Tools/TabColorConverterTests.cs
Normal file
166
mRemoteNGTests/Tools/TabColorConverterTests.cs
Normal 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"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user