├── btc qr.png ├── quansheng.ico ├── WiringMod2.png ├── WiringOverview2.png ├── WiringSchematic.png ├── QuanshengDock ├── smeter.png ├── quansheng.ico ├── UI │ ├── ButtonBorder.cs │ ├── VFOPreset.xaml │ ├── DcsMenu.cs │ ├── CtcssMenu.cs │ ├── Indicator.cs │ ├── SMeter.cs │ ├── TextPrompt.xaml.cs │ ├── TextPrompt.xaml │ ├── Preset.cs │ ├── LCD.cs │ ├── RotaryEncoder.cs │ ├── MenuActions.cs │ └── Potentiometer.cs ├── App.xaml ├── AssemblyInfo.cs ├── General │ ├── SavedList.cs │ ├── SavedDictionary.cs │ ├── Radio.cs │ └── ColorBrush.cs ├── QuanshengDock.csproj ├── User │ └── UserFolder.cs ├── App.xaml.cs ├── Serial │ └── Packet.cs ├── ClassExtensions.cs ├── RepeaterBook │ ├── BookContext.cs │ ├── RepeaterBookBrowser.xaml.cs │ ├── Repeater.cs │ └── RepeaterBookBrowser.xaml ├── Audio │ ├── AudioDevices.cs │ ├── VOX.cs │ ├── QDNH.cs │ └── Sound.cs ├── ExtendedVFO │ ├── GPIO.cs │ ├── Messenger.xaml │ ├── FixAM.cs │ ├── Defines.cs │ ├── Scanner.xaml.cs │ ├── Messenger.xaml.cs │ └── ScanList.cs ├── Channels │ ├── CopiedChannel.cs │ ├── Conversions.cs │ ├── ChannelEditor.xaml.cs │ └── ChannelEditor.xaml ├── Settings │ └── SettingsWindow.xaml.cs └── View │ └── ViewModel.cs ├── QuanshengDock.sln ├── .gitattributes ├── README.md └── .gitignore /btc qr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicsure/QuanshengDock/HEAD/btc qr.png -------------------------------------------------------------------------------- /quansheng.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicsure/QuanshengDock/HEAD/quansheng.ico -------------------------------------------------------------------------------- /WiringMod2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicsure/QuanshengDock/HEAD/WiringMod2.png -------------------------------------------------------------------------------- /WiringOverview2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicsure/QuanshengDock/HEAD/WiringOverview2.png -------------------------------------------------------------------------------- /WiringSchematic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicsure/QuanshengDock/HEAD/WiringSchematic.png -------------------------------------------------------------------------------- /QuanshengDock/smeter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicsure/QuanshengDock/HEAD/QuanshengDock/smeter.png -------------------------------------------------------------------------------- /QuanshengDock/quansheng.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicsure/QuanshengDock/HEAD/QuanshengDock/quansheng.ico -------------------------------------------------------------------------------- /QuanshengDock/UI/ButtonBorder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows.Controls; 7 | 8 | namespace QuanshengDock.UI 9 | { 10 | // code by -nicsure- 2024 11 | // https://www.youtube.com/nicsure 12 | 13 | public class ButtonBorder : Border 14 | { 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /QuanshengDock/App.xaml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /QuanshengDock/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | 3 | [assembly: ThemeInfo( 4 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located 5 | //(used if a resource is not found in the page, 6 | // or application resource dictionaries) 7 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located 8 | //(used if a resource is not found in the page, 9 | // app, or any theme specific resource dictionaries) 10 | )] 11 | -------------------------------------------------------------------------------- /QuanshengDock/General/SavedList.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Text.RegularExpressions; 7 | using System.Threading.Tasks; 8 | 9 | namespace QuanshengDock.General 10 | { 11 | public class SavedList : List 12 | { 13 | public string BackingFile { get; set; } 14 | 15 | public SavedList(string backingFile) 16 | { 17 | BackingFile = backingFile; 18 | try 19 | { 20 | string[] lines = File.ReadAllLines(backingFile); 21 | foreach(string line in lines) 22 | Add(Regex.Unescape(line)); 23 | } 24 | catch { } 25 | } 26 | 27 | public void Save() 28 | { 29 | try 30 | { 31 | File.WriteAllLines(BackingFile, this.Select(l => $"{Regex.Escape(l)}").ToArray()); 32 | } 33 | catch { } 34 | } 35 | 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /QuanshengDock/QuanshengDock.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | WinExe 5 | net6.0-windows 6 | enable 7 | true 8 | True 9 | quansheng.ico 10 | true 11 | AnyCPU;x64 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /QuanshengDock/User/UserFolder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace QuanshengDock.User 9 | { 10 | // code by -nicsure- 2024 11 | // https://www.youtube.com/nicsure 12 | 13 | public static class UserFolder 14 | { 15 | private const string scanLogDir = "scanlogs"; 16 | 17 | static UserFolder() 18 | { 19 | string logdir = Path.Combine(Instance.Name, scanLogDir); 20 | if (!Directory.Exists(Instance.Name)) 21 | Directory.CreateDirectory(Instance.Name); 22 | if (!Directory.Exists(logdir)) 23 | Directory.CreateDirectory(logdir); 24 | } 25 | 26 | public static string File(string file) 27 | { 28 | return Path.Combine(Instance.Name, file); 29 | } 30 | 31 | public static string LogFile(string file) 32 | { 33 | return Path.Combine(Instance.Name, scanLogDir, file); 34 | } 35 | 36 | public static string Dir => Instance.Name; 37 | } 38 | 39 | public static class Instance 40 | { 41 | private static string name = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "QuanshengDock"); 42 | public static string Name 43 | { 44 | get => name; 45 | set => name = value; 46 | } 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /QuanshengDock/General/SavedDictionary.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Text.RegularExpressions; 7 | using System.Threading.Tasks; 8 | using System.Windows.Input; 9 | 10 | namespace QuanshengDock.General 11 | { 12 | // code by -nicsure- 2024 13 | // https://www.youtube.com/nicsure 14 | 15 | public class SavedDictionary : Dictionary 16 | { 17 | public string BackingFile { get; set; } 18 | public SavedDictionary(string backingFile) 19 | { 20 | BackingFile = backingFile; 21 | try 22 | { 23 | string[] lines = File.ReadAllLines(backingFile); 24 | for (int i = 0; i < (lines.Length & 0x7ffffffe); i += 2) 25 | { 26 | string key = Regex.Unescape(lines[i]); 27 | string value = Regex.Unescape(lines[i + 1]); 28 | this[key] = value; 29 | } 30 | } 31 | catch { } 32 | } 33 | 34 | public void Save() 35 | { 36 | try 37 | { 38 | File.WriteAllLines(BackingFile, this.Select(kp => $"{Regex.Escape(kp.Key)}\r\n{Regex.Escape(kp.Value)}").ToArray()); 39 | } 40 | catch { } 41 | } 42 | 43 | public string GetValue(string key, string? def = null) => ContainsKey(key) ? this[key] : (def ?? string.Empty); 44 | 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /QuanshengDock/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using QuanshengDock.User; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Configuration; 5 | using System.Data; 6 | using System.Globalization; 7 | using System.Linq; 8 | using System.Threading.Tasks; 9 | using System.Windows; 10 | 11 | namespace QuanshengDock 12 | { 13 | /// 14 | /// Interaction logic for App.xaml 15 | /// 16 | public partial class App : Application 17 | { 18 | protected override void OnStartup(StartupEventArgs e) 19 | { 20 | if (e.Args.Length > 0) 21 | Instance.Name = e.Args[0]; 22 | CultureInfo customCulture = new("en-US"); 23 | CultureInfo.DefaultThreadCurrentCulture = customCulture; 24 | CultureInfo.DefaultThreadCurrentUICulture = customCulture; 25 | System.Threading.Thread.CurrentThread.CurrentCulture = customCulture; 26 | base.OnStartup(e); 27 | AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; 28 | } 29 | 30 | private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) 31 | { 32 | Exception exception = e.ExceptionObject as Exception ?? new("Unknown Exception Event"); 33 | if (exception != null) 34 | { 35 | MessageBox.Show($"An unhandled exception {exception} occurred:\n\n{exception.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Error); 36 | } 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /QuanshengDock/Serial/Packet.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace QuanshengDock.Serial 8 | { 9 | // code by -nicsure- 2024 10 | // https://www.youtube.com/nicsure 11 | 12 | public static class Packet 13 | { 14 | public const ushort Hello = 0x514; 15 | public const ushort GetRssi = 0x527; 16 | public const ushort KeyPress = 0x801; 17 | public const ushort GetScreen = 0x803; 18 | public const ushort Scan = 0x808; 19 | public const ushort ScanAdjust = 0x809; 20 | public const ushort ScanReply = 0x908; 21 | public const ushort WriteRegisters = 0x850; 22 | public const ushort ReadRegisters = 0x851; 23 | public const ushort RegisterInfo = 0x951; 24 | public const ushort WriteGPIO = 0x860; 25 | public const ushort ReadGPIO = 0x861; 26 | public const ushort GPIOInfo = 0x961; 27 | public const ushort GPIOPulse = 0x862; 28 | public const ushort EnterHardwareMode = 0x0870; 29 | public const ushort ExitHardwareMode = 0x0871; 30 | public const ushort SetReportReg = 0x0872; 31 | public const ushort ImHere = 0x515; 32 | public const ushort RssiInfo = 0x528; 33 | public const ushort WriteEeprom = 0x51D; 34 | public const ushort WriteEepromReply = 0x51E; 35 | public const ushort ReadEeprom = 0x51B; 36 | public const ushort ReadEepromReply = 0x51C; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /QuanshengDock.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.5.33627.172 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "QuanshengDock", "QuanshengDock\QuanshengDock.csproj", "{EAEC23C4-D9E8-4FBD-8330-2F6A2130A4D0}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Debug|x64 = Debug|x64 12 | Release|Any CPU = Release|Any CPU 13 | Release|x64 = Release|x64 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {EAEC23C4-D9E8-4FBD-8330-2F6A2130A4D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {EAEC23C4-D9E8-4FBD-8330-2F6A2130A4D0}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {EAEC23C4-D9E8-4FBD-8330-2F6A2130A4D0}.Debug|x64.ActiveCfg = Debug|x64 19 | {EAEC23C4-D9E8-4FBD-8330-2F6A2130A4D0}.Debug|x64.Build.0 = Debug|x64 20 | {EAEC23C4-D9E8-4FBD-8330-2F6A2130A4D0}.Release|Any CPU.ActiveCfg = Release|Any CPU 21 | {EAEC23C4-D9E8-4FBD-8330-2F6A2130A4D0}.Release|Any CPU.Build.0 = Release|Any CPU 22 | {EAEC23C4-D9E8-4FBD-8330-2F6A2130A4D0}.Release|x64.ActiveCfg = Release|x64 23 | {EAEC23C4-D9E8-4FBD-8330-2F6A2130A4D0}.Release|x64.Build.0 = Release|x64 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {230BE291-8530-4324-96E7-00F2B8E64BE9} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /QuanshengDock/ClassExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace QuanshengDock 8 | { 9 | public static class ClassExtensions 10 | { 11 | public static byte Byte(this ushort s, int byteIndex) => (byte)((s >> (byteIndex * 8)) & 0xff); 12 | public static byte Byte(this short s, int byteIndex) => (byte)((s >> (byteIndex * 8)) & 0xff); 13 | public static byte Byte(this int s, int byteIndex) => (byte)((s >> (byteIndex * 8)) & 0xff); 14 | public static byte Byte(this uint s, int byteIndex) => (byte)((s >> (byteIndex * 8)) & 0xff); 15 | public static double ToDouble(this string s) => double.TryParse(s, out double d) ? d : 0; 16 | public static float Clamp(this float value, float min, float max) => Math.Max(min, Math.Min(value, max)); 17 | public static double Clamp(this double value, double min, double max) => Math.Max(min, Math.Min(value, max)); 18 | public static int Clamp(this int value, int min, int max) => value < min ? min : value > max ? max : value; 19 | 20 | 21 | public static bool DoubleParse(this string s, out double d) 22 | { 23 | if(!double.TryParse(s, out d)) 24 | { 25 | if(!double.TryParse(s.Replace(",", "."), out d)) 26 | { 27 | if (!double.TryParse(s.Replace(".", ","), out d)) 28 | { 29 | return false; 30 | } 31 | } 32 | } 33 | return true; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /QuanshengDock/RepeaterBook/BookContext.cs: -------------------------------------------------------------------------------- 1 | using QuanshengDock.View; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Collections.ObjectModel; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace QuanshengDock.RepeaterBook 10 | { 11 | public class BookContext 12 | { 13 | public static BookContext Instance { get; } = new(); 14 | 15 | public ViewModel BookActions { get; } = new(nameof(BookActions)); 16 | public ViewModel BookCallsign { get; } = new(string.Empty, nameof(BookCallsign)); 17 | public ViewModel BookCity { get; } = new(string.Empty, nameof(BookCity)); 18 | public ViewModel BookCountry { get; } = new(string.Empty, nameof(BookCountry)); 19 | public ViewModel BookState { get; } = new(string.Empty, nameof(BookState)); 20 | public ViewModel BookRegion { get; } = new(string.Empty, nameof(BookRegion)); 21 | public ViewModel BookFrequency { get; } = new(string.Empty, nameof(BookFrequency)); 22 | public ViewModel BookMode { get; } = new(string.Empty, nameof(BookMode)); 23 | public ViewModel BookIdle { get; } = new(true, nameof(BookIdle)); 24 | public ViewModel> BookResults { get; } = new(new(), nameof(BookResults)); 25 | public ViewModel BookMessage { get; } = new(string.Empty, nameof(BookMessage)); 26 | public ViewModel> BookSelected { get; } = new(new(), nameof(BookSelected)); 27 | 28 | public BookContext() 29 | { 30 | Repeater.Activator++; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /QuanshengDock/UI/VFOPreset.xaml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 16 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /QuanshengDock/UI/DcsMenu.cs: -------------------------------------------------------------------------------- 1 | using QuanshengDock.Channels; 2 | using QuanshengDock.ExtendedVFO; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using System.Windows.Controls; 9 | 10 | namespace QuanshengDock.UI 11 | { 12 | internal class DcsMenu : ContextMenu 13 | { 14 | public DcsMenu(bool tx) : base() 15 | { 16 | Grid grid = new(); 17 | for (int i = 0; i < 8; i++) 18 | grid.ColumnDefinitions.Add(new ColumnDefinition()); 19 | for (int i = 0; i < 13; i++) 20 | grid.RowDefinitions.Add(new RowDefinition()); 21 | int cnt = 0; 22 | for (int y = 0; y < 13; y++) 23 | { 24 | for (int x = 0; x < 8; x++) 25 | { 26 | string s = Beautifiers.DcsStrings[cnt]; 27 | MenuItem mi = new() 28 | { 29 | Header = s, 30 | Tag = cnt 31 | }; 32 | mi.Click += (sender, e) => 33 | { 34 | if(sender is MenuItem cmi) 35 | { 36 | if(cmi.Tag is int i) 37 | { 38 | XVFO.SetDcs(i, tx); 39 | } 40 | } 41 | }; 42 | Grid.SetRow(mi, y); 43 | Grid.SetColumn(mi, x); 44 | grid.Children.Add(mi); 45 | cnt++; 46 | } 47 | } 48 | this.Items.Add(grid); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /QuanshengDock/UI/CtcssMenu.cs: -------------------------------------------------------------------------------- 1 | using QuanshengDock.Channels; 2 | using QuanshengDock.ExtendedVFO; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using System.Windows; 9 | using System.Windows.Controls; 10 | 11 | namespace QuanshengDock.UI 12 | { 13 | internal class CtcssMenu : ContextMenu 14 | { 15 | public CtcssMenu(bool tx) : base() 16 | { 17 | Grid grid = new(); 18 | for (int i = 0; i < 5; i++) 19 | grid.ColumnDefinitions.Add(new ColumnDefinition()); 20 | for (int i = 0; i < 10; i++) 21 | grid.RowDefinitions.Add(new RowDefinition()); 22 | int cnt = 0; 23 | for (int y = 0; y < 10; y++) 24 | { 25 | for (int x = 0; x < 5; x++) 26 | { 27 | string s = Beautifiers.CtcssStrings[cnt]; 28 | MenuItem mi = new() 29 | { 30 | Header = s, 31 | Tag = cnt 32 | }; 33 | mi.Click += (sender, e) => 34 | { 35 | if (sender is MenuItem cmi) 36 | { 37 | if (cmi.Tag is int i) 38 | { 39 | XVFO.SetCtcss(i, tx); 40 | } 41 | } 42 | }; 43 | Grid.SetRow(mi, y); 44 | Grid.SetColumn(mi, x); 45 | grid.Children.Add(mi); 46 | cnt++; 47 | } 48 | } 49 | this.Items.Add(grid); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /QuanshengDock/Audio/AudioDevices.cs: -------------------------------------------------------------------------------- 1 | using NAudio.CoreAudioApi; 2 | using NAudio.Wave; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace QuanshengDock.Audio 10 | { 11 | // code by -nicsure- 2024 12 | // https://www.youtube.com/nicsure 13 | 14 | public static class AudioDevices 15 | { 16 | private static readonly Dictionary fullDeviceNames = new(); 17 | 18 | static AudioDevices() 19 | { 20 | UpdateFullDeviceNames(); 21 | } 22 | 23 | private static void UpdateFullDeviceNames() 24 | { 25 | fullDeviceNames.Clear(); 26 | using MMDeviceEnumerator enumerator = new(); 27 | foreach (var device in enumerator.EnumerateAudioEndPoints(DataFlow.All, DeviceState.Active)) 28 | fullDeviceNames[device.FriendlyName.Length > 31 ? device.FriendlyName[..31] : device.FriendlyName] = device.FriendlyName; 29 | } 30 | 31 | public static string[] GetAudioInDevices() 32 | { 33 | string[] audioInDevices = new string[WaveIn.DeviceCount+1]; 34 | int i = 0; 35 | for (; i < WaveIn.DeviceCount; i++) 36 | audioInDevices[i] = fullDeviceNames.TryGetValue(WaveIn.GetCapabilities(i).ProductName, out string? s) ? s : WaveIn.GetCapabilities(i).ProductName; 37 | audioInDevices[i] = "No Device"; 38 | return audioInDevices; 39 | } 40 | 41 | public static string[] GetAudioOutDevices() 42 | { 43 | string[] audioOutDevices = new string[WaveOut.DeviceCount+1]; 44 | int i = 0; 45 | for (; i < WaveOut.DeviceCount; i++) 46 | audioOutDevices[i] = fullDeviceNames.TryGetValue(WaveOut.GetCapabilities(i).ProductName, out string? s) ? s : WaveOut.GetCapabilities(i).ProductName; 47 | audioOutDevices[i] = "No Device"; 48 | return audioOutDevices; 49 | } 50 | 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /QuanshengDock/ExtendedVFO/GPIO.cs: -------------------------------------------------------------------------------- 1 | using QuanshengDock.Serial; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Runtime.InteropServices; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace QuanshengDock.ExtendedVFO 10 | { 11 | public static class GPIO 12 | { 13 | public static void ReadA(GPIOA_PINS bit) 14 | { 15 | Comms.SendCommand(Packet.ReadGPIO, (ushort)1, (byte)0, (byte)bit); 16 | } 17 | public static void ReadB(GPIOA_PINS bit) 18 | { 19 | Comms.SendCommand(Packet.ReadGPIO, (ushort)1, (byte)1, (byte)bit); 20 | } 21 | public static void ReadC(GPIOA_PINS bit) 22 | { 23 | Comms.SendCommand(Packet.ReadGPIO, (ushort)1, (byte)2, (byte)bit); 24 | } 25 | public static void SetA(GPIOA_PINS bit) 26 | { 27 | Comms.SendCommand(Packet.WriteGPIO, (ushort)1, (byte)0, (byte)bit); 28 | } 29 | public static void ClearA(GPIOA_PINS bit) 30 | { 31 | Comms.SendCommand(Packet.WriteGPIO, (ushort)1, (byte)3, (byte)bit); 32 | } 33 | public static void SetB(GPIOB_PINS bit) 34 | { 35 | Comms.SendCommand(Packet.WriteGPIO, (ushort)1, (byte)1, (byte)bit); 36 | } 37 | public static void ClearB(GPIOB_PINS bit) 38 | { 39 | Comms.SendCommand(Packet.WriteGPIO, (ushort)1, (byte)4, (byte)bit); 40 | } 41 | public static void SetC(GPIOC_PINS bit) 42 | { 43 | Comms.SendCommand(Packet.WriteGPIO, (ushort)1, (byte)2, (byte)bit); 44 | } 45 | public static void ClearC(GPIOC_PINS bit) 46 | { 47 | Comms.SendCommand(Packet.WriteGPIO, (ushort)1, (byte)5, (byte)bit); 48 | } 49 | 50 | public static void EnableAudio(bool enabled) 51 | { 52 | if (enabled) 53 | SetC(GPIOC_PINS.GPIOC_PIN_AUDIO_PATH); 54 | else 55 | ClearC(GPIOC_PINS.GPIOC_PIN_AUDIO_PATH); 56 | } 57 | 58 | 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /QuanshengDock/UI/Indicator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows; 7 | using System.Windows.Controls; 8 | using System.Windows.Media; 9 | 10 | namespace QuanshengDock.UI 11 | { 12 | // code by -nicsure- 2024 13 | // https://www.youtube.com/nicsure 14 | 15 | public class Indicator : Border 16 | { 17 | private Frame innerFrame = new(); 18 | 19 | public Indicator() 20 | { 21 | Child = innerFrame; 22 | } 23 | 24 | public Brush OnBrush 25 | { 26 | get { return (Brush)GetValue(OnBrushProperty); } 27 | set { SetValue(OnBrushProperty, value); } 28 | } 29 | public static readonly DependencyProperty OnBrushProperty = 30 | DependencyProperty.Register("OnBrush", typeof(Brush), typeof(Indicator), new PropertyMetadata(new SolidColorBrush(Colors.Red))); 31 | 32 | public Brush OffBrush 33 | { 34 | get { return (Brush)GetValue(OffBrushProperty); } 35 | set { SetValue(OffBrushProperty, value); } 36 | } 37 | public static readonly DependencyProperty OffBrushProperty = 38 | DependencyProperty.Register("OffBrush", typeof(Brush), typeof(Indicator), new PropertyMetadata(new SolidColorBrush(Colors.Black))); 39 | 40 | 41 | public bool IsOn 42 | { 43 | get { return (bool)GetValue(IsOnProperty); } 44 | set { SetValue(IsOnProperty, value); } 45 | } 46 | public static readonly DependencyProperty IsOnProperty = 47 | DependencyProperty.Register("IsOn", typeof(bool), typeof(Indicator), new PropertyMetadata(IsOnChanged)); 48 | 49 | private static void IsOnChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 50 | { 51 | if(d is Indicator indicator) 52 | { 53 | indicator.innerFrame.Background = (bool)e.NewValue ? indicator.OnBrush : indicator.OffBrush; 54 | } 55 | } 56 | 57 | 58 | 59 | 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /QuanshengDock/RepeaterBook/RepeaterBookBrowser.xaml.cs: -------------------------------------------------------------------------------- 1 | using QuanshengDock.View; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Collections.ObjectModel; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using System.Windows; 9 | using System.Windows.Controls; 10 | using System.Windows.Data; 11 | using System.Windows.Documents; 12 | using System.Windows.Input; 13 | using System.Windows.Media; 14 | using System.Windows.Media.Imaging; 15 | using System.Windows.Shapes; 16 | 17 | namespace QuanshengDock.RepeaterBook 18 | { 19 | /// 20 | /// Interaction logic for RepeaterBookBrowser.xaml 21 | /// 22 | public partial class RepeaterBookBrowser : Window 23 | { 24 | private static readonly ViewModel> selected = VM.Get>("BookSelected"); 25 | 26 | public RepeaterBookBrowser() 27 | { 28 | DataContext = BookContext.Instance; 29 | InitializeComponent(); 30 | } 31 | 32 | protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e) 33 | { 34 | base.OnMouseLeftButtonDown(e); 35 | if (Mouse.DirectlyOver is Border) 36 | this.DragMove(); 37 | } 38 | 39 | private void Frame_MouseDown(object sender, MouseButtonEventArgs e) 40 | { 41 | Close(); 42 | } 43 | 44 | private void DataGrid_LoadingRow(object sender, DataGridRowEventArgs e) 45 | { 46 | if(e.Row.Item is Repeater repeater) 47 | { 48 | e.Row.Opacity = repeater.Status.Equals("On-air") ? 1.0 : 0.6; 49 | } 50 | } 51 | 52 | private void DataGrid_SelectedCellsChanged(object sender, SelectedCellsChangedEventArgs e) 53 | { 54 | selected.Value.Clear(); 55 | foreach(var v in RepeaterGrid.SelectedItems) 56 | { 57 | if(v is Repeater repeater) 58 | { 59 | selected.Value.Add(repeater); 60 | } 61 | } 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /QuanshengDock/General/Radio.cs: -------------------------------------------------------------------------------- 1 | using QuanshengDock.UI; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.ComponentModel; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using System.Windows; 9 | 10 | namespace QuanshengDock.General 11 | { 12 | // code by -nicsure- 2024 13 | // https://www.youtube.com/nicsure 14 | 15 | public enum RState { TX, RX, None } 16 | 17 | public static class Radio 18 | { 19 | public static bool Closing { get; set; } = false; 20 | public static bool SpectrumVisible { get; set; } = false; 21 | public static bool AnalyzerMode { get; set; } = false; 22 | public static bool SpectrumMode { get; set; } = false; 23 | public static bool WaterfallMode => !SpectrumMode; 24 | public static uint MonitoredFreq { get; set; } = 0; 25 | public static bool Monitoring { get; set; } = false; 26 | public static RState State { get; set; } = RState.None; 27 | public static bool DesignMode { get; } = DesignerProperties.GetIsInDesignMode(new DependencyObject()); 28 | public static bool PulseTX { get; set; } = false; 29 | public static bool IsXVFO { get; set; } = false; 30 | public static bool UsedXVFO { get; set; } = false; 31 | public static bool UseCommas { get; } = (0.5).ToString()[1] == ','; 32 | public static int AudioOutID { get; set; } = -1; 33 | public static int AudioInID { get; set; } = -1; 34 | public static int RadioOutID { get; set; } = -1; 35 | public static int RadioInID { get; set; } = -1; 36 | public static string NowFF => DateTime.Now.ToString("ddMMMyyyy-HH_mm_ss"); 37 | public static string Now => DateTime.Now.ToString("dd/MMM/yyyy,HH:mm.ss"); 38 | 39 | public static void Invoke(Action action) 40 | { 41 | if (!Closing) 42 | (_ = Application.Current)?.Dispatcher.Invoke(action); 43 | } 44 | 45 | public static string? Prompt(string prompt, bool canbeempty, string initial = "") 46 | { 47 | var window = new TextPrompt(prompt, canbeempty, initial); 48 | return (window.ShowDialog() ?? false) ? window.InputText : null; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /QuanshengDock/Channels/CopiedChannel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace QuanshengDock.Channels 8 | { 9 | public class CopiedChannel 10 | { 11 | public static CopiedChannel[] Clipboard { get; set; } = Array.Empty(); 12 | 13 | private readonly byte[] data = new byte[16]; 14 | private readonly byte[] name = new byte[16]; 15 | private readonly byte attr; 16 | public CopiedChannel(int num) 17 | { 18 | Array.Copy(Channel.DataBytes, num * 16, data, 0, 16); 19 | Array.Copy(Channel.NameBytes, num * 16, name, 0, 16); 20 | attr = Channel.AttrBytes[num]; 21 | } 22 | 23 | public CopiedChannel(string bookname, string tx, string rx, string code) 24 | { 25 | byte[] b = Encoding.ASCII.GetBytes(bookname); 26 | Array.Copy(b, 0, name, 0, b.Length > 10 ? 10 : b.Length); 27 | double rxf = rx.DoubleParse(out double f) ? f : 0; 28 | double txf = tx.DoubleParse(out f) ? f : 0; 29 | uint rxi = (uint)Math.Round(rxf * 100000.0); 30 | uint txi = (uint)Math.Round(txf * 100000.0); 31 | int txd = txi > rxi ? 1 : txi < rxi ? 2 : 0; 32 | txi = txi > rxi ? txi - rxi : rxi - txi; 33 | int codeindex = Array.IndexOf(Beautifiers.CtcssStrings, code); 34 | int codetype = 0; 35 | if (codeindex == -1) 36 | { 37 | codeindex = Array.IndexOf(Beautifiers.DcsStrings, code); 38 | if (codeindex > -1) 39 | codetype = 2; 40 | } 41 | else 42 | codetype = 1; 43 | if (codeindex == -1) codeindex = 0; 44 | Array.Copy(BitConverter.GetBytes(rxi), 0, data, 0, 4); 45 | Array.Copy(BitConverter.GetBytes(txi), 0, data, 4, 4); 46 | data[9] = (byte)codeindex; 47 | data[10] = (byte)(codetype << 4); 48 | data[11] = (byte)txd; 49 | } 50 | 51 | public void Paste(int num) 52 | { 53 | Array.Copy(data, 0, Channel.DataBytes, num * 16, 16); 54 | Array.Copy(name, 0, Channel.NameBytes, num * 16, 16); 55 | Channel.AttrBytes[num] = attr; 56 | } 57 | 58 | 59 | 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /QuanshengDock/UI/SMeter.cs: -------------------------------------------------------------------------------- 1 | using QuanshengDock.View; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Windows; 8 | using System.Windows.Media; 9 | 10 | namespace QuanshengDock.UI 11 | { 12 | public class SMeter : FrameworkElement 13 | { 14 | 15 | protected override void OnRender(DrawingContext context) 16 | { 17 | context.DrawRectangle(MeterColor, null, new(0, 0, ActualWidth, ActualHeight)); 18 | double w = ActualWidth / 100; 19 | double wc = ActualWidth / 90; 20 | double h = ActualWidth * 0.7; 21 | for (int i = 0; i < 100; i++) 22 | { 23 | if ((i & 1) != 0 || i > Value) 24 | { 25 | Rect r = new(i * w, 0, wc, ActualHeight); 26 | context.DrawRectangle(MaskColor, null, r); 27 | } 28 | else 29 | { 30 | Rect r = new(i * w, 0, wc, h); 31 | h -= w * 1.5; 32 | if (h < 0) h = 0; 33 | context.DrawRectangle(MaskColor, null, r); 34 | } 35 | } 36 | } 37 | 38 | public Brush MeterColor 39 | { 40 | get { return (Brush)GetValue(MeterColorProperty); } 41 | set { SetValue(MeterColorProperty, value); } 42 | } 43 | public static readonly DependencyProperty MeterColorProperty = 44 | DependencyProperty.Register("MeterColor", typeof(Brush), typeof(SMeter), new PropertyMetadata(new SolidColorBrush(Colors.LimeGreen))); 45 | 46 | public Brush MaskColor 47 | { 48 | get { return (Brush)GetValue(MaskColorProperty); } 49 | set { SetValue(MaskColorProperty, value); } 50 | } 51 | public static readonly DependencyProperty MaskColorProperty = 52 | DependencyProperty.Register("MaskColor", typeof(Brush), typeof(SMeter), new PropertyMetadata(new SolidColorBrush(Colors.Black))); 53 | 54 | public double Value 55 | { 56 | get { return (double)GetValue(ValueProperty); } 57 | set { SetValue(ValueProperty, value); } 58 | } 59 | public static readonly DependencyProperty ValueProperty = 60 | DependencyProperty.Register("Value", typeof(double), typeof(SMeter), new PropertyMetadata(0.0, ValueChanged)); 61 | 62 | private static void ValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 63 | { 64 | if (d is SMeter s) 65 | s.InvalidateVisual(); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /QuanshengDock/Settings/SettingsWindow.xaml.cs: -------------------------------------------------------------------------------- 1 | using QuanshengDock.Data; 2 | using QuanshengDock.Serial; 3 | using QuanshengDock.UI; 4 | using QuanshengDock.View; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.ComponentModel; 8 | using System.Diagnostics; 9 | using System.Linq; 10 | using System.Text; 11 | using System.Threading.Tasks; 12 | using System.Windows; 13 | using System.Windows.Controls; 14 | using System.Windows.Data; 15 | using System.Windows.Documents; 16 | using System.Windows.Input; 17 | using System.Windows.Media; 18 | using System.Windows.Media.Imaging; 19 | using System.Windows.Navigation; 20 | using System.Windows.Shapes; 21 | 22 | namespace QuanshengDock.Settings 23 | { 24 | /// 25 | /// Interaction logic for Settings.xaml 26 | /// 27 | public partial class SettingsWindow : Window 28 | { 29 | private static readonly ViewModel pass = VM.Get("NPass"); 30 | public SettingsWindow() 31 | { 32 | GC.Collect(); 33 | DataContext = Context.Instance; 34 | InitializeComponent(); 35 | NPass.Password = pass.Value; 36 | } 37 | 38 | protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e) 39 | { 40 | base.OnMouseLeftButtonDown(e); 41 | if (Mouse.DirectlyOver is Border) 42 | this.DragMove(); 43 | } 44 | 45 | protected override void OnClosing(CancelEventArgs e) 46 | { 47 | base.OnClosing(e); 48 | pass.Value = NPass.Password; 49 | } 50 | 51 | private void TextBox_KeyDown(object sender, KeyEventArgs e) 52 | { 53 | if (e.Key == Key.Enter) Defocusser.Focus(); 54 | if(sender is PasswordBox pb) 55 | pass.Value = pb.Password; 56 | } 57 | 58 | private void Register_KeyDown(object sender, KeyEventArgs e) 59 | { 60 | if (e.Key == Key.Enter) 61 | { 62 | Defocusser.Focus(); 63 | if (RegBox.Text.Length > 0) 64 | { 65 | string s = RegBox.Text; 66 | RegBox.Text = string.Empty; 67 | if (int.TryParse(s, System.Globalization.NumberStyles.HexNumber, null, out int i)) 68 | { 69 | Comms.SendCommand(Packet.ReadRegisters, (ushort)1, (ushort)i); 70 | } 71 | } 72 | } 73 | } 74 | 75 | private void Hyperlink_RequestNavigate(object sender, RequestNavigateEventArgs e) 76 | { 77 | Clipboard.SetText(e.Uri.AbsoluteUri); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /QuanshengDock/General/ColorBrush.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Windows; 8 | using System.Windows.Media; 9 | using Xceed.Wpf.Toolkit.PropertyGrid.Editors; 10 | 11 | namespace QuanshengDock.General 12 | { 13 | // code by -nicsure- 2024 14 | // https://www.youtube.com/nicsure 15 | 16 | public class ColorBrushPen : INotifyPropertyChanged 17 | { 18 | private SolidColorBrush brush; 19 | private Pen pen; 20 | private Color color; 21 | private double thickness; 22 | 23 | public event PropertyChangedEventHandler? PropertyChanged; 24 | 25 | public SolidColorBrush Brush => brush; 26 | public Pen Pen => pen; 27 | public Color Color 28 | { 29 | get => color; 30 | set 31 | { 32 | Radio.Invoke(() => 33 | { 34 | color = value; 35 | brush = new SolidColorBrush(color); 36 | pen = new(brush, thickness) { EndLineCap = PenLineCap.Round }; 37 | OnPropertyChanged(nameof(Color)); 38 | OnPropertyChanged(nameof(Brush)); 39 | OnPropertyChanged(nameof(Pen)); 40 | }); 41 | } 42 | } 43 | public double Thickness 44 | { 45 | get => thickness; 46 | set 47 | { 48 | Radio.Invoke(() => 49 | { 50 | thickness = value; 51 | pen = new(brush, thickness) { EndLineCap = PenLineCap.Round}; 52 | OnPropertyChanged(nameof(Thickness)); 53 | OnPropertyChanged(nameof(Pen)); 54 | }); 55 | } 56 | } 57 | 58 | public ColorBrushPen(Color col, double lineThickness) 59 | { 60 | color = col; 61 | brush = new(col); 62 | pen = new(brush, lineThickness) { EndLineCap = PenLineCap.Round }; 63 | thickness = lineThickness; 64 | } 65 | 66 | public override string ToString() 67 | { 68 | return thickness.ToString() + color.ToString(); 69 | } 70 | 71 | public static ColorBrushPen FromString(string s) 72 | { 73 | string[] p = s.Split('#'); 74 | if (p.Length == 2 && p[0].DoubleParse(out double t)) 75 | return new((Color)ColorConverter.ConvertFromString("#" + p[1]), t); 76 | return new(Colors.Gray, 1); 77 | } 78 | 79 | protected virtual void OnPropertyChanged(string propertyName) => (_ = PropertyChanged)?.Invoke(this, new(propertyName)); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /QuanshengDock/UI/TextPrompt.xaml.cs: -------------------------------------------------------------------------------- 1 | using QuanshengDock.Data; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Media; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using System.Windows; 9 | using System.Windows.Controls; 10 | using System.Windows.Data; 11 | using System.Windows.Documents; 12 | using System.Windows.Input; 13 | using System.Windows.Media; 14 | using System.Windows.Media.Imaging; 15 | using System.Windows.Shapes; 16 | 17 | namespace QuanshengDock.UI 18 | { 19 | /// 20 | /// Interaction logic for TextPrompt.xaml 21 | /// 22 | public partial class TextPrompt : Window 23 | { 24 | private readonly bool canBeEmpty; 25 | public TextPrompt(string prompt, bool canBeEmpty, string initial = "") 26 | { 27 | DataContext = Context.Instance; 28 | InitializeComponent(); 29 | Prompt = prompt; 30 | InputText = initial; 31 | this.canBeEmpty = canBeEmpty; 32 | InputBox.Focus(); 33 | InputBox.SelectAll(); 34 | } 35 | 36 | protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e) 37 | { 38 | base.OnMouseLeftButtonDown(e); 39 | if (Mouse.DirectlyOver is Border) 40 | this.DragMove(); 41 | } 42 | 43 | public string Prompt 44 | { 45 | get { return (string)GetValue(PromptProperty); } 46 | set { SetValue(PromptProperty, value); } 47 | } 48 | public static readonly DependencyProperty PromptProperty = 49 | DependencyProperty.Register("Prompt", typeof(string), typeof(TextPrompt), new PropertyMetadata(string.Empty)); 50 | 51 | public string InputText 52 | { 53 | get { return (string)GetValue(InputTextProperty); } 54 | set { SetValue(InputTextProperty, value); } 55 | } 56 | public static readonly DependencyProperty InputTextProperty = 57 | DependencyProperty.Register("InputText", typeof(string), typeof(TextPrompt), new PropertyMetadata(string.Empty)); 58 | 59 | private void Okay_Click(object sender, RoutedEventArgs e) 60 | { 61 | if (InputText.Length > 0 || canBeEmpty) 62 | DialogResult = true; 63 | else 64 | { 65 | SystemSounds.Exclamation.Play(); 66 | InputBox.Focus(); 67 | } 68 | 69 | } 70 | 71 | private void InputBox_KeyDown(object sender, KeyEventArgs e) 72 | { 73 | if(e.Key == Key.Enter) 74 | { 75 | Okay.Focus(); 76 | e.Handled = true; 77 | Okay_Click(sender, e); 78 | } 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /QuanshengDock/UI/TextPrompt.xaml: -------------------------------------------------------------------------------- 1 | 13 | 14 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 |