Migrate project to avalonia (basic code structure)

This commit is contained in:
Stone_Red
2024-05-05 20:04:20 +02:00
parent 7ce3ce5b3b
commit 7b32b3397f
37 changed files with 1159 additions and 810 deletions
+13 -7
View File
@@ -1,9 +1,11 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.1.32421.90
VisualStudioVersion = 17.9.34728.123
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ClipCMD", "ClipCMD\ClipCMD.csproj", "{D5BA1B2C-E995-45C8-A952-8F5A809F2566}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ClipCmd", "ClipCmd\ClipCmd\ClipCmd.csproj", "{27564C22-06E6-4BE7-ADF9-75F8AE48B58B}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ClipCmd.Desktop", "ClipCmd\ClipCmd.Desktop\ClipCmd.Desktop.csproj", "{E676843F-19A2-456C-AD4F-520438460739}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -11,15 +13,19 @@ Global
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
{27564C22-06E6-4BE7-ADF9-75F8AE48B58B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{27564C22-06E6-4BE7-ADF9-75F8AE48B58B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{27564C22-06E6-4BE7-ADF9-75F8AE48B58B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{27564C22-06E6-4BE7-ADF9-75F8AE48B58B}.Release|Any CPU.Build.0 = Release|Any CPU
{E676843F-19A2-456C-AD4F-520438460739}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E676843F-19A2-456C-AD4F-520438460739}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E676843F-19A2-456C-AD4F-520438460739}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E676843F-19A2-456C-AD4F-520438460739}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {E8688A15-37F6-4D71-A342-449BA8244CFA}
SolutionGuid = {09386B0D-E2E0-426B-AB6B-D6F9849CAC6F}
EndGlobalSection
EndGlobal
+454
View File
@@ -0,0 +1,454 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Mono auto generated files
mono_crash.*
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
[Ll]ogs/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# Tye
.tye/
# ASP.NET Scaffolding
ScaffoldingReadMe.txt
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Coverlet is a free, cross platform Code Coverage Tool
coverage*.json
coverage*.xml
coverage*.info
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
*.appxbundle
*.appxupload
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# BeatPulse healthcheck temp database
healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
.ionide/
# Fody - auto-generated XML schema
FodyWeavers.xsd
##
## Visual studio for Mac
##
# globs
Makefile.in
*.userprefs
*.usertasks
config.make
config.status
aclocal.m4
install-sh
autom4te.cache/
*.tar.gz
tarballs/
test-results/
# Mac bundle stuff
*.dmg
*.app
# content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore
# General
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
# content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore
# Windows thumbnail cache files
Thumbs.db
ehthumbs.db
ehthumbs_vista.db
# Dump file
*.stackdump
# Folder config file
[Dd]esktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msix
*.msm
*.msp
# Windows shortcuts
*.lnk
# JetBrains Rider
.idea/
*.sln.iml
##
## Visual Studio Code
##
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
-9
View File
@@ -1,9 +0,0 @@
<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>
-44
View File
@@ -1,44 +0,0 @@
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());
}
}
-10
View File
@@ -1,10 +0,0 @@
using System.Windows;
[assembly: ThemeInfo(
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
//(used if a resource is not found in the page,
// or application resource dictionaries)
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
//(used if a resource is not found in the page,
// app, or any theme specific resource dictionaries)
)]
-33
View File
@@ -1,33 +0,0 @@
<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.4.0" />
<PackageReference Include="System.Management.Automation" Version="7.4.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,27 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<!--If you are willing to use Windows/MacOS native APIs you will need to create 3 projects.
One for Windows with net8.0-windows TFM, one for MacOS with net8.0-macos and one with net8.0 TFM for Linux.-->
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
</PropertyGroup>
<PropertyGroup>
<ApplicationManifest>app.manifest</ApplicationManifest>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Avalonia.Desktop" Version="$(AvaloniaVersion)" />
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="$(AvaloniaVersion)" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.8.0" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="4.8.0" />
<PackageReference Include="Microsoft.CodeAnalysis.Scripting.Common" Version="4.8.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ClipCmd\ClipCmd.csproj" />
</ItemGroup>
</Project>
+29
View File
@@ -0,0 +1,29 @@
using Avalonia;
using Avalonia.ReactiveUI;
using System;
namespace ClipCmd.Desktop;
internal static class Program
{
// Initialization code. Don't use any Avalonia, third-party APIs or any
// SynchronizationContext-reliant code before AppMain is called: things aren't initialized
// yet and stuff might break.
[STAThread]
public static void Main(string[] args)
{
_ = BuildAvaloniaApp()
.StartWithClassicDesktopLifetime(args);
}
// Avalonia configuration, don't remove; also used by visual designer.
public static AppBuilder BuildAvaloniaApp()
{
return AppBuilder.Configure<App>()
.UsePlatformDetect()
.WithInterFont()
.LogToTrace()
.UseReactiveUI();
}
}
+18
View File
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<!-- This manifest is used on Windows only.
Don't remove it as it might cause problems with window transparency and embedded controls.
For more details visit https://learn.microsoft.com/en-us/windows/win32/sbscs/application-manifests -->
<assemblyIdentity version="1.0.0.0" name="ClipCmd.Desktop"/>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- A list of the Windows versions that this application has been tested on
and is designed to work with. Uncomment the appropriate elements
and Windows will automatically select the most compatible environment. -->
<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
</application>
</compatibility>
</assembly>
+54
View File
@@ -0,0 +1,54 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.3.32811.315
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ClipCmd", "ClipCmd\ClipCmd.csproj", "{EBFA8512-1EA5-4D8C-B4AC-AB5B48A6D568}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ClipCmd.Desktop", "ClipCmd.Desktop\ClipCmd.Desktop.csproj", "{ABC31E74-02FF-46EB-B3B2-4E6AE43B456C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ClipCmd.Browser", "ClipCmd.Browser\ClipCmd.Browser.csproj", "{1C1A049E-235C-4CD0-B6FA-D53AC418F4DA}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ClipCmd.iOS", "ClipCmd.iOS\ClipCmd.iOS.csproj", "{EBD9022F-BC83-4846-9A11-6F7F3772DC64}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ClipCmd.Android", "ClipCmd.Android\ClipCmd.Android.csproj", "{7AD1DAC8-7FBE-49D5-8614-7321233DB82E}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{3DA99C4E-89E3-4049-9C22-0A7EC60D83D8}"
ProjectSection(SolutionItems) = preProject
Directory.Build.props = Directory.Build.props
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{EBFA8512-1EA5-4D8C-B4AC-AB5B48A6D568}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EBFA8512-1EA5-4D8C-B4AC-AB5B48A6D568}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EBFA8512-1EA5-4D8C-B4AC-AB5B48A6D568}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EBFA8512-1EA5-4D8C-B4AC-AB5B48A6D568}.Release|Any CPU.Build.0 = Release|Any CPU
{ABC31E74-02FF-46EB-B3B2-4E6AE43B456C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{ABC31E74-02FF-46EB-B3B2-4E6AE43B456C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{ABC31E74-02FF-46EB-B3B2-4E6AE43B456C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{ABC31E74-02FF-46EB-B3B2-4E6AE43B456C}.Release|Any CPU.Build.0 = Release|Any CPU
{1C1A049E-235C-4CD0-B6FA-D53AC418F4DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1C1A049E-235C-4CD0-B6FA-D53AC418F4DA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1C1A049E-235C-4CD0-B6FA-D53AC418F4DA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1C1A049E-235C-4CD0-B6FA-D53AC418F4DA}.Release|Any CPU.Build.0 = Release|Any CPU
{EBD9022F-BC83-4846-9A11-6F7F3772DC64}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EBD9022F-BC83-4846-9A11-6F7F3772DC64}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EBD9022F-BC83-4846-9A11-6F7F3772DC64}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EBD9022F-BC83-4846-9A11-6F7F3772DC64}.Release|Any CPU.Build.0 = Release|Any CPU
{7AD1DAC8-7FBE-49D5-8614-7321233DB82E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7AD1DAC8-7FBE-49D5-8614-7321233DB82E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7AD1DAC8-7FBE-49D5-8614-7321233DB82E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7AD1DAC8-7FBE-49D5-8614-7321233DB82E}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {83CB65B8-011F-4ED7-BCD3-A6CFA935EF7E}
EndGlobalSection
EndGlobal
+21
View File
@@ -0,0 +1,21 @@
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ClipCmd"
x:Class="ClipCmd.App"
RequestedThemeVariant="Default">
<!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. -->
<Application.DataTemplates>
<local:ViewLocator />
</Application.DataTemplates>
<Application.Resources>
<x:Double x:Key="ControlContentThemeFontSize">12</x:Double>
<FontFamily x:Key="ContentControlThemeFontFamily">Microsoft YaHei</FontFamily>
</Application.Resources>
<Application.Styles>
<FluentTheme />
<StyleInclude Source="avares://AvaloniaEdit/Themes/Fluent/AvaloniaEdit.xaml" />
</Application.Styles>
</Application>
+58
View File
@@ -0,0 +1,58 @@
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
using ClipCmd.Models;
using ClipCmd.Utilities;
using ClipCmd.ViewModels;
using ClipCmd.Views;
using Projektanker.Icons.Avalonia;
using Projektanker.Icons.Avalonia.FontAwesome;
using Projektanker.Icons.Avalonia.MaterialDesign;
using System;
namespace ClipCmd;
public partial class App : Application
{
public override void Initialize()
{
_ = IconProvider.Current
.Register<FontAwesomeIconProvider>()
.Register<MaterialDesignIconProvider>();
AvaloniaXamlLoader.Load(this);
}
public override void OnFrameworkInitializationCompleted()
{
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
desktop.MainWindow = new MainWindow();
ClipCmdCommandHandler clipCmdCommandHandler = new ClipCmdCommandHandler(desktop.MainWindow);
desktop.MainWindow.DataContext = new MainViewModel(clipCmdCommandHandler);
desktop.MainWindow.Closing += (sender, e) =>
{
Settings.SaveSettings();
};
clipCmdCommandHandler.Start();
}
else if (ApplicationLifetime is ISingleViewApplicationLifetime singleViewPlatform)
{
/*
singleViewPlatform.MainView = new MainView()
{
DataContext = new MainViewModel()
}
*/
throw new NotImplementedException();
}
base.OnFrameworkInitializationCompleted();
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 172 KiB

+33
View File
@@ -0,0 +1,33 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<LangVersion>latest</LangVersion>
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
</PropertyGroup>
<ItemGroup>
<AvaloniaResource Include="Assets\**" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Avalonia" Version="$(AvaloniaVersion)" />
<PackageReference Include="Avalonia.AvaloniaEdit" Version="11.0.6" />
<PackageReference Include="Avalonia.Themes.Fluent" Version="$(AvaloniaVersion)" />
<PackageReference Include="Avalonia.Fonts.Inter" Version="$(AvaloniaVersion)" />
<PackageReference Include="Avalonia.ReactiveUI" Version="$(AvaloniaVersion)" />
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="$(AvaloniaVersion)" />
<PackageReference Include="AvaloniaEdit.TextMate" Version="11.0.6" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.8.0" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="4.8.0" />
<PackageReference Include="Microsoft.CodeAnalysis.Scripting.Common" Version="4.8.0" />
<PackageReference Include="Microsoft.PowerShell.SDK" Version="7.4.2" />
<PackageReference Include="Projektanker.Icons.Avalonia" Version="9.3.0" />
<PackageReference Include="Projektanker.Icons.Avalonia.FontAwesome" Version="9.3.0" />
<PackageReference Include="Projektanker.Icons.Avalonia.MaterialDesign" Version="9.3.0" />
<PackageReference Include="Semi.Avalonia" Version="11.0.7.2" />
<PackageReference Include="SharpHook" Version="5.3.3" />
<PackageReference Include="Speckle.Material.Icons.Avalonia" Version="1.2.0" />
</ItemGroup>
</Project>
@@ -0,0 +1,4 @@
// This file is used by Code Analysis to maintain SuppressMessage
// attributes that are applied to this project.
// Project-level suppressions either have no target or are given
// a specific target and scoped to a namespace, type, member, etc.
@@ -0,0 +1,6 @@
namespace ClipCmd.Models;
internal class ClipCmdCommand
{
public string Script { get; set; } = string.Empty;
}
@@ -0,0 +1,7 @@
namespace ClipCmd.Models;
public enum ClipCmdMode
{
Clipboard,
AutoType
}
+53
View File
@@ -0,0 +1,53 @@
using ClipCmd.Utilities;
using System;
using System.Diagnostics;
using System.IO;
using System.Text.Json;
namespace ClipCmd.Models;
public class Settings
{
public static Settings Current { get; } = LoadSettings();
public bool AutoPaste { get; set; } = true;
public int AutoTypeDelay { get; set; }
public ClipCmdMode Mode { get; set; }
public string Prefix { get; set; } = "#";
public string Suffix { get; set; } = "";
public static void SaveSettings()
{
if (!Directory.Exists(Configuration.ApplicationDataPath))
{
_ = Directory.CreateDirectory(Configuration.ApplicationDataPath);
}
File.WriteAllText(Configuration.SettingsFilePath, JsonSerializer.Serialize(Current));
}
private static Settings LoadSettings()
{
if (File.Exists(Configuration.SettingsFilePath))
{
try
{
return JsonSerializer.Deserialize<Settings>(File.ReadAllText(Configuration.SettingsFilePath)) ?? new Settings();
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
return new Settings();
}
}
else
{
return new Settings();
}
}
}
@@ -0,0 +1,100 @@
using Avalonia.Controls;
using Avalonia.Input.Platform;
using ClipCmd.Models;
using SharpHook;
using SharpHook.Native;
using System.Collections.Concurrent;
using System.Management.Automation;
using System.Text;
using System.Threading.Tasks;
namespace ClipCmd.Utilities;
public class ClipCmdCommandHandler(Window window)
{
private readonly IClipboard clipboard = window.Clipboard!;
private readonly EventSimulator simulator = new EventSimulator();
private readonly ConcurrentDictionary<string, ClipCmdCommand> commands = new();
private string lastText = string.Empty;
public void Start()
{
_ = new TaskFactory().StartNew(async () =>
{
while (true)
{
await Run();
await Task.Delay(100);
}
}, TaskCreationOptions.LongRunning);
}
private async Task Run()
{
string? text = await clipboard.GetTextAsync();
if (text is null || text == lastText)
{
return;
}
lastText = text;
if (!text.StartsWith(Settings.Current.Prefix) || !text.EndsWith(Settings.Current.Suffix))
{
return;
}
string commandName = text[Settings.Current.Prefix.Length..^Settings.Current.Suffix.Length];
StringBuilder outText = new StringBuilder();
if (commands.TryGetValue(commandName, out ClipCmdCommand? command))
{
PowerShell powerShell = PowerShell.Create();
_ = powerShell.AddScript(command.Script);
foreach (PSObject commandResult in await powerShell.InvokeAsync())
{
_ = outText.AppendLine(commandResult?.ToString() ?? string.Empty);
}
}
else if (string.IsNullOrWhiteSpace(commandName) || string.IsNullOrEmpty(Settings.Current.Suffix + Settings.Current.Prefix))
{
return;
}
else
{
_ = outText.AppendLine("Command not found!");
}
await clipboard.ClearAsync();
if (Settings.Current.Mode == ClipCmdMode.Clipboard)
{
await clipboard.SetTextAsync(outText.ToString().TrimEnd());
if (Settings.Current.AutoPaste)
{
_ = simulator.SimulateKeyPress(KeyCode.VcLeftControl);
_ = simulator.SimulateKeyPress(KeyCode.VcV);
_ = simulator.SimulateKeyRelease(KeyCode.VcLeftControl);
_ = simulator.SimulateKeyRelease(KeyCode.VcV);
}
}
else
{
foreach (char c in outText.ToString().TrimEnd())
{
_ = simulator.SimulateTextEntry(c.ToString());
await Task.Delay(Settings.Current.AutoTypeDelay);
}
}
lastText = string.Empty;
}
}
@@ -0,0 +1,24 @@
using System;
using System.IO;
using System.Runtime.InteropServices;
namespace ClipCmd.Utilities;
internal static class Configuration
{
public static string ApplicationDataPath
{
get
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "StoneRed", "ClipCMD");
}
return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "StoneRed", "ClipCMD");
}
}
public static string SettingsFilePath => Path.Combine(ApplicationDataPath, "settings.json");
public static string LogFilePath => Path.Combine(ApplicationDataPath, $"ClipCMD.log");
}
+31
View File
@@ -0,0 +1,31 @@
using Avalonia.Controls;
using Avalonia.Controls.Templates;
using ClipCmd.ViewModels;
using System;
namespace ClipCmd;
public class ViewLocator : IDataTemplate
{
public Control? Build(object? data)
{
if (data is null)
return null;
var name = data.GetType().FullName!.Replace("ViewModel", "View", StringComparison.Ordinal);
var type = Type.GetType(name);
if (type != null)
{
return (Control)Activator.CreateInstance(type)!;
}
return new TextBlock { Text = "Not Found: " + name };
}
public bool Match(object? data)
{
return data is ViewModelBase;
}
}
@@ -0,0 +1,81 @@
using ClipCmd.Models;
using ClipCmd.Utilities;
using ReactiveUI;
namespace ClipCmd.ViewModels;
public class MainViewModel : ViewModelBase
{
public bool AutoTypeModeEnabled
{
get => Settings.Current.Mode == ClipCmdMode.AutoType;
set
{
Settings.Current.Mode = value ? ClipCmdMode.AutoType : ClipCmdMode.Clipboard;
this.RaisePropertyChanged();
this.RaisePropertyChanged(nameof(ClipboardModeEnabled));
}
}
public bool ClipboardModeEnabled
{
get => Settings.Current.Mode == ClipCmdMode.Clipboard;
set
{
Settings.Current.Mode = value ? ClipCmdMode.Clipboard : ClipCmdMode.AutoType;
this.RaisePropertyChanged();
this.RaisePropertyChanged(nameof(AutoTypeModeEnabled));
}
}
public int AutoTypeDelay
{
get => Settings.Current.AutoTypeDelay;
set
{
Settings.Current.AutoTypeDelay = value;
this.RaisePropertyChanged();
}
}
public bool AutoPaste
{
get => Settings.Current.AutoPaste;
set
{
Settings.Current.AutoPaste = value;
this.RaisePropertyChanged();
}
}
public string Prefix
{
get => Settings.Current.Prefix;
set
{
Settings.Current.Prefix = value;
this.RaisePropertyChanged();
}
}
public string Suffix
{
get => Settings.Current.Suffix;
set
{
Settings.Current.Suffix = value;
this.RaisePropertyChanged();
}
}
public MainViewModel(ClipCmdCommandHandler clipCmdCommandHandler)
{
}
public MainViewModel()
{
}
}
@@ -0,0 +1,7 @@
using ReactiveUI;
namespace ClipCmd.ViewModels;
public class ViewModelBase : ReactiveObject
{
}
+64
View File
@@ -0,0 +1,64 @@
<UserControl xmlns="https://github.com/avaloniaui"
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:vm="clr-namespace:ClipCmd.ViewModels"
xmlns:i="https://github.com/projektanker/icons.avalonia"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="ClipCmd.Views.MainView"
x:DataType="vm:MainViewModel"
Loaded="UserControl_Loaded" Background="{DynamicResource SemiColorBackground0}">
<Design.DataContext>
<!-- This only sets the DataContext for the previewer in an IDE,
to set the actual DataContext for runtime, set the DataContext property in code (look at App.axaml.cs) -->
<vm:MainViewModel />
</Design.DataContext>
<UserControl.Styles>
<StyleInclude Source="avares://Semi.Avalonia/Themes/Index.axaml" />
</UserControl.Styles>
<Grid RowDefinitions="Auto Auto Auto Auto Auto Auto" ColumnDefinitions="Auto * Auto" Margin="10">
<Grid RowDefinitions="Auto * Auto" VerticalAlignment="Stretch">
<TextBox Grid.Row="0" MaxLength="5" InnerLeftContent="Prefix:" Watermark="none" Text="{Binding Prefix}" />
<TextBox Grid.Row="2" MaxLength="5" InnerLeftContent="Suffix:" Watermark="none" Text="{Binding Suffix}" />
</Grid>
<Label Grid.Column="1" Content="ClipCMD" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="30" />
<Grid RowDefinitions="Auto Auto Auto Auto Auto" ColumnDefinitions="100 20 100" Grid.Column="2">
<Label Content="Mode" />
<RadioButton Grid.Row="1" VerticalAlignment="Center" Content="Clipboard" IsChecked="{Binding ClipboardModeEnabled}" />
<CheckBox Grid.Row="1" Grid.Column="2" VerticalAlignment="Center" Content="Auto Paste" IsEnabled="{Binding ClipboardModeEnabled}" IsChecked="{Binding AutoPaste}" />
<Separator Grid.Row="2" Grid.ColumnSpan="3" VerticalAlignment="Center" />
<RadioButton Grid.Row="3" VerticalAlignment="Center" Content="Auto Type" IsChecked="{Binding AutoTypeModeEnabled}" />
<NumericUpDown Grid.Row="3" Grid.Column="2" Increment="1" Minimum="0" Maximum="1000" VerticalAlignment="Center" IsEnabled="{Binding AutoTypeModeEnabled}" Value="{Binding AutoTypeDelay}" />
</Grid>
<Separator Grid.Row="1" Grid.ColumnSpan="3" VerticalAlignment="Center" Margin="0 10" />
<Button Grid.Column="0" Grid.Row="2">
<StackPanel Spacing="4" Orientation="Horizontal">
<i:Icon Value="fa-plus" />
<TextBlock>Add Command</TextBlock>
</StackPanel>
</Button>
<Grid ColumnDefinitions="* 20 *" Grid.Column="2" Grid.Row="2">
<Button>
<StackPanel Spacing="4" Orientation="Horizontal">
<i:Icon Value="fa-file-upload" />
<TextBlock>Import</TextBlock>
</StackPanel>
</Button>
<Button Grid.Column="2">
<StackPanel Orientation="Horizontal">
<i:Icon Value="fa-file-download" />
<TextBlock>Export</TextBlock>
</StackPanel>
</Button>
</Grid>
</Grid>
</UserControl>
@@ -0,0 +1,47 @@
using Avalonia.Controls;
using AvaloniaEdit;
using AvaloniaEdit.TextMate;
using System.Diagnostics;
using System.Management.Automation.Language;
using TextMateSharp.Grammars;
namespace ClipCmd.Views;
public partial class MainView : UserControl
{
public MainView()
{
InitializeComponent();
}
private void UserControl_Loaded(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
{
return;
//First of all you need to have a reference for your TextEditor for it to be used inside AvaloniaEdit.TextMate project.
TextEditor? textEditor = this.FindControl<TextEditor>("TextEditor");
textEditor.TextChanged += (s, e) =>
{
_ = Parser.ParseInput(textEditor.Text, out _, out ParseError[] parseErrors);
if (parseErrors.Length > 0)
{
foreach (ParseError parseError in parseErrors)
{
Debug.WriteLine(parseError.Message + " at line " + parseError.Extent.StartLineNumber + " and column " + parseError.Extent.StartColumnNumber);
}
}
};
RegistryOptions registryOptions = new RegistryOptions(ThemeName.DarkPlus);
//Initial setup of TextMate.
TextMate.Installation _textMateInstallation = textEditor.InstallTextMate(registryOptions);
//Here we are getting the language by the extension and right after that we are initializing grammar with this language.
//And that's all 😀, you are ready to use AvaloniaEdit with syntax highlighting!
_textMateInstallation.SetGrammar(registryOptions.GetScopeByLanguageId(registryOptions.GetLanguageByExtension(".ps1").Id));
}
}
@@ -0,0 +1,12 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="using:ClipCmd.ViewModels"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:views="clr-namespace:ClipCmd.Views"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="ClipCmd.Views.MainWindow"
Icon="/Assets/avalonia-logo.ico"
Title="ClipCmd" Width="800" Height="450">
<views:MainView />
</Window>
@@ -0,0 +1,10 @@
using Avalonia.Controls;
namespace ClipCmd.Views;
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
-46
View File
@@ -1,46 +0,0 @@
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;
}
}
+6
View File
@@ -0,0 +1,6 @@
<Project>
<PropertyGroup>
<Nullable>enable</Nullable>
<AvaloniaVersion>11.0.10</AvaloniaVersion>
</PropertyGroup>
</Project>
-35
View File
@@ -1,35 +0,0 @@
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
@@ -1,85 +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"
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>
-313
View File
@@ -1,313 +0,0 @@
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 = [];
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() ?? [];
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();
List<string> outputLines = [];
foreach (PSObject commandResult in ps.Invoke())
{
_ = outText.AppendLine(commandResult?.ToString() ?? string.Empty);
outputLines.Add(commandResult?.ToString().Trim() ?? string.Empty);
}
RuntimeData.Logs.Insert(0, $"{command} > {outText.ToString().TrimEnd()}");
InputSimulator inputSimulator = new InputSimulator();
oldData = Clipboard.GetDataObject();
if (Settings.Mode == ClipCMDMode.ClipBoard)
{
if (outputLines.TrueForAll(File.Exists))
{
Clipboard.SetFileDropList([.. outputLines.ToArray()]);
}
else
{
Clipboard.SetText(outText.ToString().TrimEnd());
}
if (Settings.AutoPaste)
{
_ = inputSimulator.Keyboard.ModifiedKeyStroke([VirtualKeyCode.CONTROL], [VirtualKeyCode.VK_V]);
}
}
else if (Settings.Mode == ClipCMDMode.AutoType)
{
RuntimeData.AutoTypeRunning = true;
while (inputSimulator.InputDeviceState.IsKeyDown(VirtualKeyCode.CONTROL))
{
// Wait for CTRL key to be released
await Task.Delay(100);
}
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'), "[#END#]"];
string commandNames = string.Empty;
StringBuilder script = new StringBuilder();
RuntimeData.Errors.Clear();
commands.Clear();
RuntimeData.CommandsInfo = $"Lines: {lines.Length} Commands: {commands.Count} Errors: {RuntimeData.Errors.Count}";
foreach (string l in lines)
{
string line = l.Trim('\n', '\r');
if (line.StartsWith('[') && line.EndsWith(']'))
{
if (!string.IsNullOrEmpty(commandNames))
{
_ = Parser.ParseInput(script.ToString(), out _, out ParseError[] errors);
if (errors.Length > 0)
{
foreach (ParseError error in errors)
{
AddError(commandNames, error.ToString());
}
}
foreach (string commandName in commandNames.Split(','))
{
if (string.IsNullOrWhiteSpace(commandName))
{
AddError(commandNames, $"Command \"{commandName}\" is empty!");
}
else if (commandName.Trim().Any(char.IsWhiteSpace))
{
AddError(commandNames, $"Command \"{commandName}\" can't contain white spaces!");
}
else if (!commands.TryAdd(commandName.Trim(), script.ToString()))
{
AddError(commandNames, $"Command \"{commandName}\" already exists!");
}
}
}
commandNames = line.Trim()[1..^1].Trim();
script = new StringBuilder();
}
else if (!string.IsNullOrEmpty(commandNames))
{
if (!string.IsNullOrWhiteSpace(line))
{
_ = script.AppendLine(line);
}
}
else
{
AddError(commandNames, "Input has to start with [<Command Name>]!");
return;
}
}
RuntimeData.CommandsInfo = $"Lines: {lines.Length} Commands: {commands.Count} Errors: {RuntimeData.Errors.Count}";
_ = commands.TryAdd("list", $"\"{string.Join(", ", commands.Keys)}\"");
File.WriteAllText(commandsPath, RuntimeData.CommandsText);
}
}
-17
View File
@@ -1,17 +0,0 @@
using System;
using System.Runtime.InteropServices;
namespace ClipCMD;
internal static class NativeMethods
{
// See http://msdn.microsoft.com/en-us/library/ms649021%28v=vs.85%29.aspx
public const int WM_CLIPBOARDUPDATE = 0x031D;
public static readonly IntPtr HWND_MESSAGE = new IntPtr(-3);
// See http://msdn.microsoft.com/en-us/library/ms632599%28VS.85%29.aspx#message_only
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool AddClipboardFormatListener(IntPtr hwnd);
}
-90
View File
@@ -1,90 +0,0 @@
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; } = [];
public Visibility LogPanelVisible
{
get => logPanelVisible;
set
{
logPanelVisible = value;
OnPropertyChanged();
}
}
public ObservableCollection<string> Logs { get; } = [];
public RuntimeData()
{
Errors.CollectionChanged += (sender, args) =>
{
ErrorPanelVisible = Errors.Any() ? Visibility.Visible : Visibility.Collapsed;
LogPanelVisible = Errors.Any() ? Visibility.Collapsed : Visibility.Visible;
OnPropertyChanged(nameof(CommandsTextColor));
};
}
private void OnPropertyChanged([CallerMemberName] string? propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
-44
View File
@@ -1,44 +0,0 @@
using System;
using System.Diagnostics;
namespace ClipCMD;
internal static class SafeRepeat
{
public static bool Start(Action action, int repeatCount)
{
for (int i = 0; i < repeatCount; i++)
{
try
{
action.Invoke();
return true;
}
catch (Exception ex)
{
Debug.WriteLine(ex);
}
}
return false;
}
public static bool Start<T>(Func<T> action, int repeatCount, out T? result)
{
for (int i = 0; i < repeatCount; i++)
{
try
{
result = action.Invoke();
return true;
}
catch (Exception ex)
{
Debug.WriteLine(ex);
}
}
result = default;
return false;
}
}
-77
View File
@@ -1,77 +0,0 @@
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.

Before

Width:  |  Height:  |  Size: 101 KiB