mirror of
https://github.com/Stone-Red-Code/ClipCMD.git
synced 2026-09-04 00:46:10 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2139938496 | ||
|
|
6439c9c4bb | ||
|
|
21e50c4f0e | ||
|
|
923b00cfc8 | ||
|
|
64c693e81b | ||
|
|
1afa6cef02 | ||
|
|
bca0c378c1 | ||
|
|
6048a8330c | ||
|
|
4f75699850 | ||
|
|
6a8414c0a9 | ||
|
|
ae580599cb | ||
|
|
cee54c9364 | ||
|
|
e23ae0a47a | ||
|
|
71451f22b2 | ||
|
|
48af2275a7 |
@@ -0,0 +1,32 @@
|
|||||||
|
---
|
||||||
|
name: Bug report
|
||||||
|
about: Create a report to help us improve
|
||||||
|
title: ''
|
||||||
|
labels: bug
|
||||||
|
assignees: Stone-Red-Code
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Describe the bug**
|
||||||
|
A clear and concise description of what the bug is.
|
||||||
|
|
||||||
|
**To Reproduce**
|
||||||
|
Steps to reproduce the behavior:
|
||||||
|
1. Go to '...'
|
||||||
|
2. Click on '....'
|
||||||
|
3. Scroll down to '....'
|
||||||
|
4. See error
|
||||||
|
|
||||||
|
**Expected behavior**
|
||||||
|
A clear and concise description of what you expected to happen.
|
||||||
|
|
||||||
|
**Screenshots**
|
||||||
|
If applicable, add screenshots to help explain your problem.
|
||||||
|
|
||||||
|
**Desktop (please complete the following information):**
|
||||||
|
- OS: [e.g. iOS]
|
||||||
|
- Browser [e.g. chrome, safari]
|
||||||
|
- Version [e.g. 22]
|
||||||
|
|
||||||
|
**Additional context**
|
||||||
|
Add any other context about the problem here.
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
name: Feature request
|
||||||
|
about: Suggest an idea for this project
|
||||||
|
title: ''
|
||||||
|
labels: enhancement
|
||||||
|
assignees: Stone-Red-Code
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Is your feature request related to a problem? Please describe.**
|
||||||
|
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||||
|
|
||||||
|
**Describe the solution you'd like**
|
||||||
|
A clear and concise description of what you want to happen.
|
||||||
|
|
||||||
|
**Describe alternatives you've considered**
|
||||||
|
A clear and concise description of any alternative solutions or features you've considered.
|
||||||
|
|
||||||
|
**Additional context**
|
||||||
|
Add any other context or screenshots about the feature request here.
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Configuration;
|
|
||||||
using System.Data;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Windows;
|
|
||||||
|
|
||||||
namespace ClipCMD;
|
|
||||||
/// <summary>
|
|
||||||
/// Interaction logic for App.xaml
|
|
||||||
/// </summary>
|
|
||||||
public partial class App : Application
|
|
||||||
{
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<OutputType>WinExe</OutputType>
|
|
||||||
<TargetFramework>net6.0-windows</TargetFramework>
|
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
<UseWPF>true</UseWPF>
|
|
||||||
<AssemblyVersion>0.0.0.2</AssemblyVersion>
|
|
||||||
<FileVersion>0.0.0.2</FileVersion>
|
|
||||||
<Version>0.0.0.2</Version>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="InputSimulator" Version="1.0.4" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
||||||
@@ -1,36 +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">
|
|
||||||
<DockPanel>
|
|
||||||
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal">
|
|
||||||
<Label Content="Prefix:" Width="45" />
|
|
||||||
<TextBox x:Name="prefixTextBox" KeyUp="FixTextBox_TextChanged" Text="01" />
|
|
||||||
</StackPanel>
|
|
||||||
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal">
|
|
||||||
<Label Content="Suffix:" Width="45" />
|
|
||||||
<TextBox x:Name="suffixTextBox" KeyUp="FixTextBox_TextChanged" Text="01" />
|
|
||||||
</StackPanel>
|
|
||||||
|
|
||||||
<Grid DockPanel.Dock="Top" VerticalAlignment="Stretch">
|
|
||||||
<Grid.ColumnDefinitions>
|
|
||||||
<ColumnDefinition />
|
|
||||||
<ColumnDefinition />
|
|
||||||
</Grid.ColumnDefinitions>
|
|
||||||
|
|
||||||
<DockPanel Grid.Column="0">
|
|
||||||
<Label DockPanel.Dock="Top" Content="Static Commands:" />
|
|
||||||
<TextBox DockPanel.Dock="Top" x:Name="staticCommandsTextBox" TextChanged="StaticCommandsTextBox_TextChanged" TextWrapping="Wrap" AcceptsReturn="True" VerticalAlignment="Stretch" />
|
|
||||||
</DockPanel>
|
|
||||||
|
|
||||||
<DockPanel Grid.Column="1">
|
|
||||||
<Label DockPanel.Dock="Top" Content="History:" />
|
|
||||||
<ListBox DockPanel.Dock="Top" x:Name="logListBox" VerticalAlignment="Stretch" />
|
|
||||||
</DockPanel>
|
|
||||||
</Grid>
|
|
||||||
</DockPanel>
|
|
||||||
</Window>
|
|
||||||
@@ -1,167 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Data;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Windows;
|
|
||||||
using System.Windows.Media;
|
|
||||||
|
|
||||||
using WindowsInput;
|
|
||||||
using WindowsInput.Native;
|
|
||||||
|
|
||||||
namespace ClipCMD;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Interaction logic for MainWindow.xaml
|
|
||||||
/// </summary>
|
|
||||||
public partial class MainWindow : Window
|
|
||||||
{
|
|
||||||
private readonly Dictionary<string, Func<string, string>> stuffToReplace = new()
|
|
||||||
{
|
|
||||||
{ "time", (_) => DateTime.Now.ToShortTimeString() },
|
|
||||||
{ "date", (_) => DateTime.Now.ToShortDateString() },
|
|
||||||
{ "calc", (math) => new DataTable().Compute(math.Trim()[4..], null)?.ToString() ?? string.Empty },
|
|
||||||
};
|
|
||||||
|
|
||||||
public MainWindow()
|
|
||||||
{
|
|
||||||
InitializeComponent();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnSourceInitialized(EventArgs e)
|
|
||||||
{
|
|
||||||
base.OnSourceInitialized(e);
|
|
||||||
|
|
||||||
// Initialize the clipboard now that we have a window soruce to use
|
|
||||||
ClipboardManager windowClipboardManager = new ClipboardManager(this);
|
|
||||||
windowClipboardManager.ClipboardChanged += ClipboardManager_ClipboardChanged;
|
|
||||||
|
|
||||||
if (File.Exists("cmd.txt"))
|
|
||||||
{
|
|
||||||
staticCommandsTextBox.Text = File.ReadAllText("cmd.txt");
|
|
||||||
}
|
|
||||||
if (File.Exists("fix.txt"))
|
|
||||||
{
|
|
||||||
string[] lines = File.ReadAllLines("fix.txt");
|
|
||||||
if (lines.Length == 2)
|
|
||||||
{
|
|
||||||
prefixTextBox.Text = lines[0];
|
|
||||||
suffixTextBox.Text = lines[1];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
File.Delete("fix.txt");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private IDataObject? oldData;
|
|
||||||
|
|
||||||
private 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? text);
|
|
||||||
|
|
||||||
if (!result || text is null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!text.StartsWith(prefixTextBox.Text) || !text.EndsWith(suffixTextBox.Text))
|
|
||||||
{
|
|
||||||
oldData = Clipboard.GetDataObject();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
text = text[prefixTextBox.Text.Length..^suffixTextBox.Text.Length].Trim();
|
|
||||||
|
|
||||||
Func<string, string>? func = stuffToReplace.FirstOrDefault(v => text.StartsWith(v.Key)).Value;
|
|
||||||
|
|
||||||
string logText = text;
|
|
||||||
string? outText;
|
|
||||||
|
|
||||||
if (func is null)
|
|
||||||
{
|
|
||||||
string[]? parts = staticCommandsTextBox.Text.Split('\n').FirstOrDefault(l =>
|
|
||||||
{
|
|
||||||
if (string.IsNullOrWhiteSpace(l))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
string? startText = l.Trim().Split('>').FirstOrDefault()?.Trim();
|
|
||||||
return startText is not null && text == startText;
|
|
||||||
})?.Split('>');
|
|
||||||
|
|
||||||
if (parts?.Length == 2)
|
|
||||||
{
|
|
||||||
outText = parts[1];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
oldData = Clipboard.GetDataObject();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
outText = func(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
outText = outText.Trim();
|
|
||||||
logListBox.Items.Insert(0, $"{logText} > {outText}");
|
|
||||||
|
|
||||||
Clipboard.SetDataObject(outText);
|
|
||||||
|
|
||||||
oldData = Clipboard.GetDataObject();
|
|
||||||
|
|
||||||
InputSimulator inputSimulator = new InputSimulator();
|
|
||||||
inputSimulator.Keyboard.KeyPress(new[] { VirtualKeyCode.CONTROL, VirtualKeyCode.VK_V });
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
logListBox.Items.Insert(0, $"Error: {ex.Message}");
|
|
||||||
Debug.WriteLine(ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void StaticCommandsTextBox_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
|
|
||||||
{
|
|
||||||
bool isError = false;
|
|
||||||
foreach (string line in staticCommandsTextBox.Text.Split('\n'))
|
|
||||||
{
|
|
||||||
if (!string.IsNullOrWhiteSpace(line) && line.Count(c => c == '>') != 1)
|
|
||||||
{
|
|
||||||
isError = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isError)
|
|
||||||
{
|
|
||||||
staticCommandsTextBox.Foreground = Brushes.Red;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
staticCommandsTextBox.Foreground = Brushes.Black;
|
|
||||||
stuffToReplace["list"] = (_) => string.Join(", ", staticCommandsTextBox.Text.Split('\n').Where(x => !string.IsNullOrWhiteSpace(x)).Select(x => x.Split('>').FirstOrDefault()?.Trim()));
|
|
||||||
File.WriteAllText("cmd.txt", staticCommandsTextBox.Text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void FixTextBox_TextChanged(object sender, System.Windows.Input.KeyEventArgs e)
|
|
||||||
{
|
|
||||||
if (prefixTextBox is null || suffixTextBox is null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
File.WriteAllLines("fix.txt", new[] { prefixTextBox.Text, suffixTextBox.Text });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -4,15 +4,17 @@
|
|||||||
|
|
||||||
## What is it?
|
## What is it?
|
||||||
|
|
||||||
ClipCMD is a windows app that allows you to create macros and run them by copying them to the clipboard.
|
ClipCMD is a Windows app that allows you to create macros and run them by copying them to the clipboard.\
|
||||||
You write a macro anywhere, copy it to your clipboard and it will be automatically replaced by the specified macro text.
|
You write a macro anywhere, copy it to your clipboard, and it will be automatically replaced by the output of the specified macro script.\
|
||||||
|
ClipCMD uses [PowerShell](https://learn.microsoft.com/en-us/powershell/scripting) as scripting language.
|
||||||
|
|
||||||
### Example
|
### Example
|
||||||
|
|
||||||
> Prefix: `_`\
|
> Prefix: `_`\
|
||||||
> Suffix: `_`\
|
> Suffix: `_`\
|
||||||
> Macro name: `email`\
|
> Macro name: `email`\
|
||||||
> Macro text: `[email protected]`
|
> Macro script: `Write-Output [email protected]`\
|
||||||
|
> Macro script (alternative): `"[email protected]"`
|
||||||
|
|
||||||
> Input: `_email_`\
|
> Input: `_email_`\
|
||||||
> Output: `[email protected]`
|
> Output: `[email protected]`
|
||||||
@@ -20,13 +22,31 @@ You write a macro anywhere, copy it to your clipboard and it will be automatical
|
|||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
1. Download one of the [releases](https://github.com/Stone-Red-Code/ClipCMD/releases)
|
1. Download one of the [releases](https://github.com/Stone-Red-Code/ClipCMD/releases)
|
||||||
1. Start `ClipCMD.exe`
|
1. Start `setup.exe`
|
||||||
1. (Optional) Set the prefix and suffix for your commands
|
1. (Optional) Set the prefix and suffix for your commands
|
||||||
1. Specify some commands in the built in text field
|
1. Specify some commands in the built-in text field
|
||||||
- Format: `[macroName]>[macroText]`
|
- Format:
|
||||||
- Each line is a new macro
|
```
|
||||||
1. There are some built in macros like `time` and `date` which will output the current local time and date
|
[<command name(s) (separated by comma)>]
|
||||||
1. Write `[suffix][macroName][preffix]` somewhere, and then select it and press ``CTRL+C``
|
<scriptText>
|
||||||
|
<scriptText>
|
||||||
|
<scriptText>
|
||||||
|
...
|
||||||
|
[<command name(s) (separated by comma)>]
|
||||||
|
<scriptText>
|
||||||
|
<scriptText>
|
||||||
|
<scriptText>
|
||||||
|
...
|
||||||
|
```
|
||||||
|
- Strings between square brackets define macros.
|
||||||
|
- You can define macro aliases by separating them by a comma `[quit, exit, q]`
|
||||||
|
- Lines between a macro definition and the next macro (or the end of the file) is PowerShell code.
|
||||||
|
- Everything you write to the output stream replaces the input macro.
|
||||||
|
- You can pass parameters to the scripts by separating the parameters by space after the macro: `_combine hi nope_`
|
||||||
|
- You can access the parameters by accessing the `$args` array.
|
||||||
|
1. There is one prebuilt macro named `list`. This lists all of your macros.
|
||||||
|
1. Write `[suffix][macroName][preffix]` somewhere, and then select it and press `CTRL+C`
|
||||||
|
|
||||||
## Preview
|
## Preview
|
||||||
<img width="589" alt="Screenshot 2022-05-03 221232" src="https://user-images.githubusercontent.com/56473591/166560142-9e89c57f-1af0-4340-9317-dacf0064d6c8.png">
|
|
||||||
|
<img width="591" alt="image" src="https://user-images.githubusercontent.com/56473591/226146460-d9d2e8fc-3754-44c7-bc43-2e32f1d07847.png">
|
||||||
|
|||||||
Binary file not shown.
@@ -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
|
||||||
|
|
||||||
@@ -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());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>WinExe</OutputType>
|
||||||
|
<TargetFramework>net8.0-windows7.0</TargetFramework>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<UseWPF>true</UseWPF>
|
||||||
|
<UseWindowsForms>true</UseWindowsForms>
|
||||||
|
<AssemblyVersion>0.0.0.3</AssemblyVersion>
|
||||||
|
<FileVersion>0.0.0.3</FileVersion>
|
||||||
|
<Version>0.0.0.3</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,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,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,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