mirror of
https://github.com/Stone-Red-Code/ClipCMD.git
synced 2026-09-06 15:56:08 +02:00
Move source files to src directory
This commit is contained in:
@@ -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
|
||||
@@ -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>
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
)]
|
||||
@@ -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>
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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 |
Reference in New Issue
Block a user