9 Commits
Author SHA1 Message Date
Stone_Red 2139938496 Upgrade project to .NET 8 2023-11-22 17:38:53 +01:00
Stone_Red 6439c9c4bb Add inno setup installer script 2023-11-22 17:14:45 +01:00
Stone_Red 21e50c4f0e Move source files to src directory 2023-11-22 16:55:03 +01:00
Stone_Red 923b00cfc8 Use guid instead of app name to create the mutex 2023-11-22 16:53:31 +01:00
Stone_Red 64c693e81b Merge pull request #4 from yassinMi/main
fixed mutex lifetime issue
2023-11-22 16:41:07 +01:00
yassinMi 1afa6cef02 fixed mutex lifetime issue 2023-11-22 09:06:10 +00:00
Stone_Red bca0c378c1 Fixed a whoopsie 2023-06-06 23:21:53 +02:00
Stone_Red 6048a8330c Small UI improvments 2023-06-06 23:20:52 +02:00
Stone_Red 4f75699850 Add AutoType option and rework setting storeage 2023-06-06 19:08:34 +02:00
18 changed files with 496 additions and 156 deletions
-26
View File
@@ -1,26 +0,0 @@
using System.Threading;
using System.Windows;
namespace ClipCMD;
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
const string appName = "ClipCMD";
_ = new Mutex(true, appName, 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();
}
base.OnStartup(e);
}
}
-42
View File
@@ -1,42 +0,0 @@
<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"
Title="ClipCMD" Height="450" Width="800"
Closing="OnClose" StateChanged="OnStateChanged" Icon="logo.ico">
<DockPanel>
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal">
<Label Content="Prefix:" Width="45" />
<TextBox x:Name="prefixTextBox" KeyUp="FixTextBox_TextChanged" Text="_" />
</StackPanel>
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal">
<Label Content="Suffix:" Width="45" />
<TextBox x:Name="suffixTextBox" KeyUp="FixTextBox_TextChanged" Text="_" />
</StackPanel>
<Grid DockPanel.Dock="Top" VerticalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<DockPanel Grid.Column="0">
<Label DockPanel.Dock="Top" Content="Commands:" />
<TextBox DockPanel.Dock="Top" x:Name="commandsTextBox" HorizontalScrollBarVisibility="Visible" TextChanged="StaticCommandsTextBox_TextChanged" AcceptsReturn="True" VerticalAlignment="Stretch" />
</DockPanel>
<DockPanel x:Name="logPanel" Grid.Column="1">
<Label DockPanel.Dock="Top" Content="History:" />
<ListBox DockPanel.Dock="Top" x:Name="logListBox" VerticalAlignment="Stretch" />
</DockPanel>
<DockPanel Visibility="Collapsed" x:Name="errorPanel" Grid.Column="1">
<Label DockPanel.Dock="Top" Content="Errors:" />
<ListBox DockPanel.Dock="Top" x:Name="errorListBox" Foreground="Red" VerticalAlignment="Stretch" />
</DockPanel>
</Grid>
</DockPanel>
</Window>
Binary file not shown.
+51
View File
@@ -0,0 +1,51 @@
; Script generated by the Inno Setup Script Wizard.
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
#expr Exec('cmd.exe /C', 'dotnet build -o "' + SourcePath + '\publish" -c Release ' + SourcePath + '..\src\ClipCMD\')
#define MyAppName "ClipCMD"
#define MyAppVersion GetVersionNumbersString("/publish/ClipCMD.exe")
#define MyAppPublisher "Stone_Red"
#define MyAppExeName "ClipCMD.exe"
[Setup]
; NOTE: The value of AppId uniquely identifies this application. Do not use the same AppId value in installers for other applications.
; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
AppId=68d780d4-b946-4154-8815-e4632e5ec5d8
AppName={#MyAppName}
AppVersion={#MyAppVersion}
;AppVerName={#MyAppName} {#MyAppVersion}
AppPublisher={#MyAppPublisher}
DefaultDirName={autopf}\{#MyAppName}
DisableProgramGroupPage=yes
; The [Icons] "quicklaunchicon" entry uses {userappdata} but its [Tasks] entry has a proper IsAdminInstallMode Check.
UsedUserAreasWarning=no
; Remove the following line to run in administrative install mode (install for all users.)
PrivilegesRequired=lowest
PrivilegesRequiredOverridesAllowed=dialog
OutputBaseFilename={#MyAppName}-Installer
OutputDir=.
Compression=lzma
SolidCompression=yes
WizardStyle=modern
[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"
Name: "german"; MessagesFile: "compiler:Languages\German.isl"
[Tasks]
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
Name: "quicklaunchicon"; Description: "{cm:CreateQuickLaunchIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked; OnlyBelowVersion: 6.1; Check: not IsAdminInstallMode
[Files]
Source: "publish\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs
; NOTE: Don't use "Flags: ignoreversion" on any shared system files
[Icons]
Name: "{autoprograms}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon
Name: "{userappdata}\Microsoft\Internet Explorer\Quick Launch\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: quicklaunchicon
[Run]
Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent
View File
+44
View File
@@ -0,0 +1,44 @@
using System;
using System.IO;
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();
}
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
Exit += CloseHandler;
}
protected void CloseHandler(object sender, EventArgs e)
{
mutex?.ReleaseMutex();
}
private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
File.WriteAllText("error.log", e.ExceptionObject.ToString());
}
}
@@ -2,13 +2,13 @@
<PropertyGroup> <PropertyGroup>
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>
<TargetFramework>net7.0-windows</TargetFramework> <TargetFramework>net8.0-windows7.0</TargetFramework>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<UseWPF>true</UseWPF> <UseWPF>true</UseWPF>
<UseWindowsForms>true</UseWindowsForms> <UseWindowsForms>true</UseWindowsForms>
<AssemblyVersion>0.0.0.2</AssemblyVersion> <AssemblyVersion>0.0.0.3</AssemblyVersion>
<FileVersion>0.0.0.2</FileVersion> <FileVersion>0.0.0.3</FileVersion>
<Version>0.0.0.2</Version> <Version>0.0.0.3</Version>
<ApplicationIcon>logo.ico</ApplicationIcon> <ApplicationIcon>logo.ico</ApplicationIcon>
</PropertyGroup> </PropertyGroup>
+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>
@@ -8,7 +8,10 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Management.Automation; using System.Management.Automation;
using System.Management.Automation.Language; using System.Management.Automation.Language;
using System.Reflection;
using System.Text; using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using System.Windows; using System.Windows;
using WindowsInput; using WindowsInput;
@@ -22,12 +25,13 @@ namespace ClipCMD;
public partial class MainWindow : Window public partial class MainWindow : Window
{ {
private readonly Dictionary<string, string> commands = new Dictionary<string, string>(); private readonly Dictionary<string, string> commands = new Dictionary<string, string>();
private readonly string commandsPath;
private readonly System.Windows.Forms.NotifyIcon notifyIcon; private readonly System.Windows.Forms.NotifyIcon notifyIcon;
private readonly string settingsPath;
private IDataObject? oldData;
private WindowState storedWindowState = WindowState.Normal; private WindowState storedWindowState = WindowState.Normal;
public RuntimeData RuntimeData { get; set; } = new();
private readonly string cmdPath; public Settings Settings { get; set; } = new();
private readonly string fixPath;
public MainWindow() public MainWindow()
{ {
@@ -42,9 +46,7 @@ public partial class MainWindow : Window
notifyIcon.Click += NotifyIcon_Click; notifyIcon.Click += NotifyIcon_Click;
InitializeComponent(); string applicationDataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "StoneRed");
string applicationDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
string folderPath = Path.Combine(applicationDataPath, "ClipCMD"); string folderPath = Path.Combine(applicationDataPath, "ClipCMD");
if (!Directory.Exists(folderPath)) if (!Directory.Exists(folderPath))
@@ -52,65 +54,48 @@ public partial class MainWindow : Window
_ = Directory.CreateDirectory(folderPath); _ = Directory.CreateDirectory(folderPath);
} }
cmdPath = Path.Combine(folderPath, "cmd.txt"); commandsPath = Path.Combine(folderPath, "cmd.txt");
fixPath = Path.Combine(folderPath, "fix.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) protected override void OnSourceInitialized(EventArgs e)
{ {
base.OnSourceInitialized(e);
// Initialize the clipboard now that we have a window source to use // Initialize the clipboard now that we have a window source to use
ClipboardManager windowClipboardManager = new ClipboardManager(this); ClipboardManager windowClipboardManager = new ClipboardManager(this);
windowClipboardManager.ClipboardChanged += ClipboardManager_ClipboardChanged; windowClipboardManager.ClipboardChanged += ClipboardManager_ClipboardChanged;
if (File.Exists(cmdPath))
{
commandsTextBox.Text = File.ReadAllText(cmdPath);
} }
if (File.Exists(fixPath)) private void AddError(string commandNames, string error)
{ {
string[] lines = File.ReadAllLines(fixPath); RuntimeData.Errors.Add($"[{commandNames}]\n{error}");
if (lines.Length == 2)
{
prefixTextBox.Text = lines[0];
suffixTextBox.Text = lines[1];
}
else
{
File.Delete(fixPath);
}
}
} }
private void OnClose(object? sender, CancelEventArgs args) private void CancelAutoTypeButton_Click(object sender, RoutedEventArgs e)
{ {
notifyIcon.Dispose(); RuntimeData.AutoTypeRunning = false;
} }
private void OnStateChanged(object? sender, EventArgs args) private async void ClipboardManager_ClipboardChanged(object? sender, EventArgs e)
{
if (WindowState == WindowState.Minimized)
{
Hide();
notifyIcon?.ShowBalloonTip(2000);
}
else
{
storedWindowState = WindowState;
}
}
private void NotifyIcon_Click(object? sender, EventArgs e)
{
Show();
WindowState = storedWindowState;
}
private IDataObject? oldData;
private void ClipboardManager_ClipboardChanged(object? sender, EventArgs e)
{ {
if (!Clipboard.ContainsText() || (oldData is not null && Clipboard.IsCurrent(oldData))) if (!Clipboard.ContainsText() || (oldData is not null && Clipboard.IsCurrent(oldData)))
{ {
@@ -126,13 +111,13 @@ public partial class MainWindow : Window
return; return;
} }
if (!clipboardText.StartsWith(prefixTextBox.Text) || !clipboardText.EndsWith(suffixTextBox.Text)) if (!clipboardText.StartsWith(Settings.Prefix) || !clipboardText.EndsWith(Settings.Suffix))
{ {
oldData = Clipboard.GetDataObject(); oldData = Clipboard.GetDataObject();
return; return;
} }
clipboardText = clipboardText[prefixTextBox.Text.Length..^suffixTextBox.Text.Length].Trim().Replace("\"\"", "\0"); clipboardText = clipboardText[Settings.Prefix.Length..^Settings.Suffix.Length].Trim().Replace("\"\"", "\0");
TextFieldParser parser = new TextFieldParser(new StringReader(clipboardText)) TextFieldParser parser = new TextFieldParser(new StringReader(clipboardText))
{ {
@@ -170,34 +155,87 @@ public partial class MainWindow : Window
_ = outText.AppendLine(commandResult?.ToString() ?? string.Empty); _ = outText.AppendLine(commandResult?.ToString() ?? string.Empty);
} }
logListBox.Items.Insert(0, $"{command} > {outText.ToString().TrimEnd()}"); RuntimeData.Logs.Insert(0, $"{command} > {outText.ToString().TrimEnd()}");
Clipboard.SetText(outText.ToString().TrimEnd()); InputSimulator inputSimulator = new InputSimulator();
oldData = Clipboard.GetDataObject(); oldData = Clipboard.GetDataObject();
InputSimulator inputSimulator = new InputSimulator(); if (Settings.Mode == ClipCMDMode.ClipBoard)
{
Clipboard.SetText(outText.ToString().TrimEnd());
if (Settings.AutoPaste)
{
_ = inputSimulator.Keyboard.ModifiedKeyStroke(new[] { VirtualKeyCode.CONTROL }, new[] { VirtualKeyCode.VK_V }); _ = 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) catch (Exception ex)
{ {
logListBox.Items.Insert(0, $"Error: {ex.Message}"); RuntimeData.Logs.Insert(0, $"Error: {ex.Message}");
Debug.WriteLine(ex); 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) private void StaticCommandsTextBox_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
{ {
string[] lines = commandsTextBox.Text.Split('\n').Append("[#END#]").ToArray(); string[] lines = RuntimeData.CommandsText.Split('\n').Append("[#END#]").ToArray();
string commandNames = string.Empty; string commandNames = string.Empty;
StringBuilder script = new StringBuilder(); StringBuilder script = new StringBuilder();
commandsTextBox.Foreground = System.Windows.Media.Brushes.Black; RuntimeData.Errors.Clear();
errorPanel.Visibility = Visibility.Collapsed;
logPanel.Visibility = Visibility.Visible;
errorListBox.Items.Clear();
commands.Clear(); commands.Clear();
RuntimeData.CommandsInfo = $"Lines: {lines.Length} Commands: {commands.Count} Errors: {RuntimeData.Errors.Count}";
foreach (string l in lines) foreach (string l in lines)
{ {
string line = l.Trim('\n', '\r'); string line = l.Trim('\n', '\r');
@@ -220,11 +258,15 @@ public partial class MainWindow : Window
{ {
if (string.IsNullOrWhiteSpace(commandName)) if (string.IsNullOrWhiteSpace(commandName))
{ {
AddError(commandNames, "Command \"{commandName}\" is empty!"); 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())) else if (!commands.TryAdd(commandName.Trim(), script.ToString()))
{ {
AddError(commandNames, "Command \"{commandName}\" already exists!"); AddError(commandNames, $"Command \"{commandName}\" already exists!");
} }
} }
} }
@@ -246,26 +288,10 @@ public partial class MainWindow : Window
} }
} }
RuntimeData.CommandsInfo = $"Lines: {lines.Length} Commands: {commands.Count} Errors: {RuntimeData.Errors.Count}";
_ = commands.TryAdd("list", $"\"{string.Join(", ", commands.Keys)}\""); _ = commands.TryAdd("list", $"\"{string.Join(", ", commands.Keys)}\"");
File.WriteAllText(cmdPath, commandsTextBox.Text); File.WriteAllText(commandsPath, RuntimeData.CommandsText);
}
private void AddError(string commandNames, string error)
{
errorPanel.Visibility = Visibility.Visible;
logPanel.Visibility = Visibility.Collapsed;
commandsTextBox.Foreground = System.Windows.Media.Brushes.Red;
_ = errorListBox.Items.Add($"[{commandNames}]\n{error}");
}
private void FixTextBox_TextChanged(object sender, System.Windows.Input.KeyEventArgs e)
{
if (prefixTextBox is null || suffixTextBox is null)
{
return;
}
File.WriteAllLines(fixPath, new[] { prefixTextBox.Text, suffixTextBox.Text });
} }
} }
+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));
}
}
+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
}

Before

Width:  |  Height:  |  Size: 101 KiB

After

Width:  |  Height:  |  Size: 101 KiB