Move source files to src directory

This commit is contained in:
Stone_Red
2023-11-22 16:55:03 +01:00
parent 923b00cfc8
commit 21e50c4f0e
14 changed files with 0 additions and 0 deletions
+25
View File
@@ -0,0 +1,25 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.1.32421.90
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ClipCMD", "ClipCMD\ClipCMD.csproj", "{D5BA1B2C-E995-45C8-A952-8F5A809F2566}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{D5BA1B2C-E995-45C8-A952-8F5A809F2566}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D5BA1B2C-E995-45C8-A952-8F5A809F2566}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D5BA1B2C-E995-45C8-A952-8F5A809F2566}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D5BA1B2C-E995-45C8-A952-8F5A809F2566}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {E8688A15-37F6-4D71-A342-449BA8244CFA}
EndGlobalSection
EndGlobal
+9
View File
@@ -0,0 +1,9 @@
<Application x:Class="ClipCMD.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ClipCMD"
StartupUri="MainWindow.xaml">
<Application.Resources>
</Application.Resources>
</Application>
+36
View File
@@ -0,0 +1,36 @@
using System;
using System.Threading;
using System.Windows;
namespace ClipCMD;
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
public const string AppName = "ClipCMD";
public const string AppGuid = "68d780d4-b946-4154-8815-e4632e5ec5d8";
private Mutex? mutex;
protected override void OnStartup(StartupEventArgs e)
{
mutex = new Mutex(true, AppGuid, out bool createdNew);
if (!createdNew)
{
//App is already running! Exiting the application
_ = MessageBox.Show($"Another instance of {AppName} already running!", $"{AppName} is already running!", MessageBoxButton.OK, MessageBoxImage.Exclamation);
Current.Shutdown();
}
Exit += CloseHandler;
}
protected void CloseHandler(object sender, EventArgs e)
{
mutex?.ReleaseMutex();
}
}
+10
View File
@@ -0,0 +1,10 @@
using System.Windows;
[assembly: ThemeInfo(
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
//(used if a resource is not found in the page,
// or application resource dictionaries)
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
//(used if a resource is not found in the page,
// app, or any theme specific resource dictionaries)
)]
+33
View File
@@ -0,0 +1,33 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net7.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<UseWPF>true</UseWPF>
<UseWindowsForms>true</UseWindowsForms>
<AssemblyVersion>0.0.0.2</AssemblyVersion>
<FileVersion>0.0.0.2</FileVersion>
<Version>0.0.0.2</Version>
<ApplicationIcon>logo.ico</ApplicationIcon>
</PropertyGroup>
<ItemGroup>
<Content Include="logo.ico">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<PackageReference Include="InputSimulator" Version="1.0.4" />
<PackageReference Include="Microsoft.PowerShell.SDK" Version="7.0.0" />
<PackageReference Include="System.Management.Automation" Version="7.0.0" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.0.0" />
</ItemGroup>
<ItemGroup>
<None Update="logo.ico">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
+46
View File
@@ -0,0 +1,46 @@
using System;
using System.Windows;
using System.Windows.Interop;
namespace ClipCMD;
public class ClipboardManager
{
public event EventHandler? ClipboardChanged;
public ClipboardManager(Window windowSource)
{
if (PresentationSource.FromVisual(windowSource) is not HwndSource source)
{
throw new ArgumentException(
"Window source MUST be initialized first, such as in the Window's OnSourceInitialized handler."
, nameof(windowSource));
}
source.AddHook(WndProc);
// get window handle for interop
IntPtr windowHandle = new WindowInteropHelper(windowSource).Handle;
// register for clipboard events
NativeMethods.AddClipboardFormatListener(windowHandle);
}
private void OnClipboardChanged()
{
ClipboardChanged?.Invoke(this, EventArgs.Empty);
}
private static readonly IntPtr WndProcSuccess = IntPtr.Zero;
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
if (msg == NativeMethods.WM_CLIPBOARDUPDATE)
{
OnClipboardChanged();
handled = true;
}
return WndProcSuccess;
}
}
+35
View File
@@ -0,0 +1,35 @@
using System;
using System.Windows;
using System.Windows.Data;
namespace ClipCMD;
public class EnumBooleanConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (parameter is not string parameterString)
{
return DependencyProperty.UnsetValue;
}
if (!Enum.IsDefined(value.GetType(), value))
{
return DependencyProperty.UnsetValue;
}
object parameterValue = Enum.Parse(value.GetType(), parameterString);
return parameterValue.Equals(value);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (parameter is not string parameterString)
{
return DependencyProperty.UnsetValue;
}
return Enum.Parse(targetType, parameterString);
}
}
+85
View File
@@ -0,0 +1,85 @@
<Window x:Class="ClipCMD.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:ClipCMD"
mc:Ignorable="d"
d:DataContext ="{d:DesignInstance {x:Type local:MainWindow}, IsDesignTimeCreatable=True}"
Title="ClipCMD" Height="450" Width="800"
Closing="OnClose" StateChanged="OnStateChanged" Icon="logo.ico">
<Window.Resources>
<local:EnumBooleanConverter x:Key="EnumBooleanConverter" />
</Window.Resources>
<DockPanel>
<Grid DockPanel.Dock="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0">
<StackPanel Orientation="Horizontal">
<Label Content="Prefix:" Width="45" />
<TextBox Text="{Binding Settings.Prefix, UpdateSourceTrigger=PropertyChanged}" MinWidth="50" Height="20" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<Label Content="Suffix:" Width="45" />
<TextBox Text="{Binding Settings.Suffix,UpdateSourceTrigger=PropertyChanged}" MinWidth="50" Height="20" />
</StackPanel>
</StackPanel>
<StackPanel Grid.Column="1" Background="LightBlue" HorizontalAlignment="Right" Margin="10">
<Label Margin="0" Padding="0">Mode:</Label>
<StackPanel Orientation="Horizontal">
<RadioButton Content="ClipBoard" GroupName="Mode" IsChecked="{Binding Settings.Mode, Converter={StaticResource EnumBooleanConverter}, ConverterParameter=ClipBoard}" Margin="0,0,10,0" />
<StackPanel Orientation="Horizontal" IsEnabled="{Binding Settings.Mode, Converter={StaticResource EnumBooleanConverter}, ConverterParameter=ClipBoard}">
<CheckBox Content="AutoPaste" IsChecked="{Binding Settings.AutoPaste}" />
</StackPanel>
</StackPanel>
<Separator />
<StackPanel Orientation="Horizontal">
<RadioButton Content="AutoType" GroupName="Mode" IsChecked="{Binding Settings.Mode, Converter={StaticResource EnumBooleanConverter}, ConverterParameter=AutoType}" Margin="0,0,10,0" />
<StackPanel Orientation="Horizontal" IsEnabled="{Binding Settings.Mode, Converter={StaticResource EnumBooleanConverter}, ConverterParameter=AutoType}">
<Label Padding="0,0,10,0">Delay:</Label>
<TextBox Text="{Binding Settings.AutoTypeDelay, UpdateSourceTrigger=PropertyChanged}" Width="50" Height="20" />
</StackPanel>
</StackPanel>
<Button Content="Cancel AutoType" Click="CancelAutoTypeButton_Click" IsEnabled="{Binding RuntimeData.AutoTypeRunning}" />
</StackPanel>
</Grid>
<Grid DockPanel.Dock="Top" VerticalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<DockPanel Grid.Column="0">
<Label DockPanel.Dock="Top" Content="Commands:" />
<Grid DockPanel.Dock="Bottom">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="100" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Margin="4,0,0,3" Text="{Binding RuntimeData.CommandsInfo}" />
<Button Grid.Column="1" Background="AliceBlue" Content="Copy to clipboard" Click="CopyToClipboardButton_Click" />
</Grid>
<TextBox DockPanel.Dock="Top" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" Text="{Binding RuntimeData.CommandsText, UpdateSourceTrigger=PropertyChanged}" Foreground="{Binding RuntimeData.CommandsTextColor}" TextChanged="StaticCommandsTextBox_TextChanged" AcceptsReturn="True" VerticalAlignment="Stretch" />
</DockPanel>
<DockPanel Visibility="{Binding RuntimeData.LogPanelVisible}" Grid.Column="1">
<Label DockPanel.Dock="Top" Content="History:" />
<ListBox DockPanel.Dock="Top" VerticalAlignment="Stretch" ItemsSource="{Binding RuntimeData.Logs}" />
</DockPanel>
<DockPanel Visibility="{Binding RuntimeData.ErrorPanelVisible}" Grid.Column="1">
<Label DockPanel.Dock="Top" Content="Errors:" />
<ListBox DockPanel.Dock="Top" Foreground="Red" VerticalAlignment="Stretch" ItemsSource="{Binding RuntimeData.Errors}" />
</DockPanel>
</Grid>
</DockPanel>
</Window>
+297
View File
@@ -0,0 +1,297 @@
using Microsoft.VisualBasic.FileIO;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Management.Automation;
using System.Management.Automation.Language;
using System.Reflection;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using System.Windows;
using WindowsInput;
using WindowsInput.Native;
namespace ClipCMD;
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private readonly Dictionary<string, string> commands = new Dictionary<string, string>();
private readonly string commandsPath;
private readonly System.Windows.Forms.NotifyIcon notifyIcon;
private readonly string settingsPath;
private IDataObject? oldData;
private WindowState storedWindowState = WindowState.Normal;
public RuntimeData RuntimeData { get; set; } = new();
public Settings Settings { get; set; } = new();
public MainWindow()
{
notifyIcon = new System.Windows.Forms.NotifyIcon
{
BalloonTipText = "ClipCMD has been minimized. Click the tray icon to show.",
BalloonTipTitle = "ClipCMD",
Text = "ClipCMD",
Icon = new System.Drawing.Icon("logo.ico"),
Visible = true
};
notifyIcon.Click += NotifyIcon_Click;
string applicationDataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "StoneRed");
string folderPath = Path.Combine(applicationDataPath, "ClipCMD");
if (!Directory.Exists(folderPath))
{
_ = Directory.CreateDirectory(folderPath);
}
commandsPath = Path.Combine(folderPath, "cmd.txt");
settingsPath = Path.Combine(folderPath, "settings.txt");
if (File.Exists(commandsPath))
{
RuntimeData.CommandsText = File.ReadAllText(commandsPath);
}
if (File.Exists(settingsPath))
{
Settings = JsonSerializer.Deserialize<Settings>(File.ReadAllText(settingsPath)) ?? new Settings();
}
DataContext = this;
InitializeComponent();
#if DEBUG
Title = $"{App.AppName} - Dev {Assembly.GetExecutingAssembly().GetName().Version}";
#else
Title = $"{App.AppName} - {Assembly.GetExecutingAssembly().GetName().Version}";
#endif
}
protected override void OnSourceInitialized(EventArgs e)
{
// Initialize the clipboard now that we have a window source to use
ClipboardManager windowClipboardManager = new ClipboardManager(this);
windowClipboardManager.ClipboardChanged += ClipboardManager_ClipboardChanged;
}
private void AddError(string commandNames, string error)
{
RuntimeData.Errors.Add($"[{commandNames}]\n{error}");
}
private void CancelAutoTypeButton_Click(object sender, RoutedEventArgs e)
{
RuntimeData.AutoTypeRunning = false;
}
private async void ClipboardManager_ClipboardChanged(object? sender, EventArgs e)
{
if (!Clipboard.ContainsText() || (oldData is not null && Clipboard.IsCurrent(oldData)))
{
return;
}
try
{
bool result = SafeRepeat.Start(() => Clipboard.GetText().Trim(), 100, out string? clipboardText);
if (!result || clipboardText is null)
{
return;
}
if (!clipboardText.StartsWith(Settings.Prefix) || !clipboardText.EndsWith(Settings.Suffix))
{
oldData = Clipboard.GetDataObject();
return;
}
clipboardText = clipboardText[Settings.Prefix.Length..^Settings.Suffix.Length].Trim().Replace("\"\"", "\0");
TextFieldParser parser = new TextFieldParser(new StringReader(clipboardText))
{
HasFieldsEnclosedInQuotes = true
};
parser.SetDelimiters(" ");
string[] sections = parser.ReadFields() ?? Array.Empty<string>();
sections = sections.Select(s => s.Replace('\0', '\"')).ToArray();
if (sections.Length == 0)
{
return;
}
string command = sections[0].Trim();
if (!commands.TryGetValue(command, out string? script))
{
oldData = Clipboard.GetDataObject();
return;
}
PowerShell ps = PowerShell
.Create()
.AddScript(script)
.AddParameters(sections.Skip(1).ToList());
StringBuilder outText = new StringBuilder();
foreach (PSObject commandResult in ps.Invoke())
{
_ = outText.AppendLine(commandResult?.ToString() ?? string.Empty);
}
RuntimeData.Logs.Insert(0, $"{command} > {outText.ToString().TrimEnd()}");
InputSimulator inputSimulator = new InputSimulator();
oldData = Clipboard.GetDataObject();
if (Settings.Mode == ClipCMDMode.ClipBoard)
{
Clipboard.SetText(outText.ToString().TrimEnd());
if (Settings.AutoPaste)
{
_ = inputSimulator.Keyboard.ModifiedKeyStroke(new[] { VirtualKeyCode.CONTROL }, new[] { VirtualKeyCode.VK_V });
}
}
else if (Settings.Mode == ClipCMDMode.AutoType)
{
RuntimeData.AutoTypeRunning = true;
foreach (char c in outText.ToString())
{
_ = inputSimulator.Keyboard.TextEntry(c);
await Task.Delay(Settings.AutoTypeDelay);
if (!RuntimeData.AutoTypeRunning)
{
break;
}
}
RuntimeData.AutoTypeRunning = false;
}
}
catch (Exception ex)
{
RuntimeData.Logs.Insert(0, $"Error: {ex.Message}");
Debug.WriteLine(ex);
}
}
private void CopyToClipboardButton_Click(object sender, RoutedEventArgs e)
{
Clipboard.SetText(RuntimeData.CommandsText);
_ = MessageBox.Show($"Successfully copied {commands.Count - 1} command(s) to clipboard!", "Success!", MessageBoxButton.OK, MessageBoxImage.Information);
}
private void NotifyIcon_Click(object? sender, EventArgs e)
{
Show();
WindowState = storedWindowState;
}
private void OnClose(object? sender, CancelEventArgs args)
{
notifyIcon.Dispose();
File.WriteAllText(settingsPath, JsonSerializer.Serialize(Settings));
}
private void OnStateChanged(object? sender, EventArgs args)
{
if (WindowState == WindowState.Minimized)
{
Hide();
notifyIcon?.ShowBalloonTip(2000);
}
else
{
storedWindowState = WindowState;
}
}
private void StaticCommandsTextBox_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
{
string[] lines = RuntimeData.CommandsText.Split('\n').Append("[#END#]").ToArray();
string commandNames = string.Empty;
StringBuilder script = new StringBuilder();
RuntimeData.Errors.Clear();
commands.Clear();
RuntimeData.CommandsInfo = $"Lines: {lines.Length} Commands: {commands.Count} Errors: {RuntimeData.Errors.Count}";
foreach (string l in lines)
{
string line = l.Trim('\n', '\r');
if (line.StartsWith('[') && line.EndsWith(']'))
{
if (!string.IsNullOrEmpty(commandNames))
{
_ = Parser.ParseInput(script.ToString(), out _, out ParseError[] errors);
if (errors.Length > 0)
{
foreach (ParseError error in errors)
{
AddError(commandNames, error.ToString());
}
}
foreach (string commandName in commandNames.Split(','))
{
if (string.IsNullOrWhiteSpace(commandName))
{
AddError(commandNames, $"Command \"{commandName}\" is empty!");
}
else if (commandName.Trim().Any(char.IsWhiteSpace))
{
AddError(commandNames, $"Command \"{commandName}\" can't contain white spaces!");
}
else if (!commands.TryAdd(commandName.Trim(), script.ToString()))
{
AddError(commandNames, $"Command \"{commandName}\" already exists!");
}
}
}
commandNames = line.Trim()[1..^1].Trim();
script = new StringBuilder();
}
else if (!string.IsNullOrEmpty(commandNames))
{
if (!string.IsNullOrWhiteSpace(line))
{
_ = script.AppendLine(line);
}
}
else
{
AddError(commandNames, "Input has to start with [<Command Name>]!");
return;
}
}
RuntimeData.CommandsInfo = $"Lines: {lines.Length} Commands: {commands.Count} Errors: {RuntimeData.Errors.Count}";
_ = commands.TryAdd("list", $"\"{string.Join(", ", commands.Keys)}\"");
File.WriteAllText(commandsPath, RuntimeData.CommandsText);
}
}
+17
View File
@@ -0,0 +1,17 @@
using System;
using System.Runtime.InteropServices;
namespace ClipCMD;
internal static class NativeMethods
{
// See http://msdn.microsoft.com/en-us/library/ms649021%28v=vs.85%29.aspx
public const int WM_CLIPBOARDUPDATE = 0x031D;
public static readonly IntPtr HWND_MESSAGE = new IntPtr(-3);
// See http://msdn.microsoft.com/en-us/library/ms632599%28VS.85%29.aspx#message_only
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool AddClipboardFormatListener(IntPtr hwnd);
}
+90
View File
@@ -0,0 +1,90 @@
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Media;
namespace ClipCMD;
public class RuntimeData : INotifyPropertyChanged
{
public event PropertyChangedEventHandler? PropertyChanged;
private bool autoTypeRunning = false;
private string commandsInfo = string.Empty;
private string commandsText = string.Empty;
private Visibility errorPanelVisible = Visibility.Collapsed;
private Visibility logPanelVisible = Visibility.Visible;
public bool AutoTypeRunning
{
get => autoTypeRunning;
set
{
autoTypeRunning = value;
OnPropertyChanged();
}
}
public string CommandsInfo
{
get => commandsInfo;
set
{
commandsInfo = value;
OnPropertyChanged();
}
}
public string CommandsText
{
get => commandsText;
set
{
commandsText = value;
OnPropertyChanged();
}
}
public Brush CommandsTextColor => Errors.Count > 0 ? Brushes.Red : Brushes.Black;
public Visibility ErrorPanelVisible
{
get => errorPanelVisible;
set
{
errorPanelVisible = value;
OnPropertyChanged();
}
}
public ObservableCollection<string> Errors { get; } = new();
public Visibility LogPanelVisible
{
get => logPanelVisible;
set
{
logPanelVisible = value;
OnPropertyChanged();
}
}
public ObservableCollection<string> Logs { get; } = new();
public RuntimeData()
{
Errors.CollectionChanged += (sender, args) =>
{
ErrorPanelVisible = Errors.Any() ? Visibility.Visible : Visibility.Collapsed;
LogPanelVisible = Errors.Any() ? Visibility.Collapsed : Visibility.Visible;
OnPropertyChanged(nameof(CommandsTextColor));
};
}
private void OnPropertyChanged([CallerMemberName] string? propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
+44
View File
@@ -0,0 +1,44 @@
using System;
using System.Diagnostics;
namespace ClipCMD;
internal static class SafeRepeat
{
public static bool Start(Action action, int repeatCount)
{
for (int i = 0; i < repeatCount; i++)
{
try
{
action.Invoke();
return true;
}
catch (Exception ex)
{
Debug.WriteLine(ex);
}
}
return false;
}
public static bool Start<T>(Func<T> action, int repeatCount, out T? result)
{
for (int i = 0; i < repeatCount; i++)
{
try
{
result = action.Invoke();
return true;
}
catch (Exception ex)
{
Debug.WriteLine(ex);
}
}
result = default;
return false;
}
}
+77
View File
@@ -0,0 +1,77 @@
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace ClipCMD;
public class Settings : INotifyPropertyChanged
{
public event PropertyChangedEventHandler? PropertyChanged;
private bool autoPaste = true;
private int autoTypeDelay = 100;
private ClipCMDMode mode = ClipCMDMode.ClipBoard;
private string prefix = "_";
private string suffix = "_";
public bool AutoPaste
{
get => autoPaste;
set
{
autoPaste = value;
OnPropertyChanged();
}
}
public int AutoTypeDelay
{
get => autoTypeDelay;
set
{
autoTypeDelay = Math.Clamp(value, 0, 1000);
OnPropertyChanged();
}
}
public ClipCMDMode Mode
{
get => mode;
set
{
mode = value;
OnPropertyChanged();
}
}
public string Prefix
{
get => prefix;
set
{
prefix = value;
OnPropertyChanged();
}
}
public string Suffix
{
get => suffix;
set
{
suffix = value;
OnPropertyChanged();
}
}
protected void OnPropertyChanged([CallerMemberName] string? name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
public enum ClipCMDMode
{
ClipBoard,
AutoType
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB