Winforms overhal in progress

This commit is contained in:
Xjph 2023-05-05 09:37:08 -02:30
commit 1d62a0ec1d
50 changed files with 5872 additions and 2681 deletions

View file

@ -0,0 +1,75 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<sectionGroup name="userSettings" type="System.Configuration.UserSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
<section name="Observatory.Properties.Core" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowExeDefinition="MachineToLocalUser" requirePermission="false" />
</sectionGroup>
</configSections>
<userSettings>
<Observatory.Properties.Core>
<setting name="JournalFolder" serializeAs="String">
<value />
</setting>
<setting name="AllowUnsigned" serializeAs="String">
<value>False</value>
</setting>
<setting name="NativeNotify" serializeAs="String">
<value>True</value>
</setting>
<setting name="NativeNotifyFont" serializeAs="String">
<value />
</setting>
<setting name="NativeNotifyColour" serializeAs="String">
<value>4294944000</value>
</setting>
<setting name="NativeNotifyCorner" serializeAs="String">
<value>0</value>
</setting>
<setting name="NativeNotifyScreen" serializeAs="String">
<value>-1</value>
</setting>
<setting name="TryPrimeSystemContextOnStartMonitor" serializeAs="String">
<value>False</value>
</setting>
<setting name="CoreVersion" serializeAs="String">
<value />
</setting>
<setting name="PluginSettings" serializeAs="String">
<value />
</setting>
<setting name="VoiceNotify" serializeAs="String">
<value>False</value>
</setting>
<setting name="VoiceSelected" serializeAs="String">
<value />
</setting>
<setting name="VoiceVolume" serializeAs="String">
<value>75</value>
</setting>
<setting name="VoiceRate" serializeAs="String">
<value>0</value>
</setting>
<setting name="MainWindowSize" serializeAs="String">
<value>800, 500</value>
</setting>
<setting name="MainWindowPosition" serializeAs="String">
<value>100, 100</value>
</setting>
<setting name="NativeNotifyScale" serializeAs="String">
<value>100</value>
</setting>
<setting name="NativeNotifyTimeout" serializeAs="String">
<value>8000</value>
</setting>
<setting name="StartMonitor" serializeAs="String">
<value>False</value>
</setting>
<setting name="ExportFolder" serializeAs="String">
<value />
</setting>
<setting name="StartReadAll" serializeAs="String">
<value>False</value>
</setting>
</Observatory.Properties.Core>
</userSettings>
</configuration>

View file

@ -1,14 +1,11 @@
using Observatory.Framework;
using System;
using System.Collections.Generic;
using Observatory.UI.Views;
using Observatory.UI.ViewModels;
using Observatory.UI;
namespace Observatory.NativeNotification
{
public class NativePopup
{
private Dictionary<Guid, NotificationView> notifications;
private Dictionary<Guid, NotificationForm> notifications;
public NativePopup()
{
@ -18,26 +15,21 @@ namespace Observatory.NativeNotification
public Guid InvokeNativeNotification(NotificationArgs notificationArgs)
{
var notificationGuid = Guid.NewGuid();
Avalonia.Threading.Dispatcher.UIThread.InvokeAsync(() =>
var notification = new NotificationForm()
{
var notifyWindow = new NotificationView(notificationGuid) { DataContext = new NotificationViewModel(notificationArgs) };
notifyWindow.Closed += NotifyWindow_Closed;
foreach (var notification in notifications)
{
notification.Value.AdjustOffset(true);
}
notifications.Add(notificationGuid, notifyWindow);
notifyWindow.Show();
});
Guid = notificationGuid
};
notification.Show();
notifications.Add(notificationGuid, notification);
//TODO: Implement winform notification
return notificationGuid;
}
private void NotifyWindow_Closed(object sender, EventArgs e)
{
var currentNotification = (NotificationView)sender;
var currentNotification = (NotificationForm)sender;
if (notifications.ContainsKey(currentNotification.Guid))
{
@ -49,10 +41,7 @@ namespace Observatory.NativeNotification
{
if (notifications.ContainsKey(guid))
{
Avalonia.Threading.Dispatcher.UIThread.InvokeAsync(() =>
{
notifications[guid].Close();
});
notifications[guid].Close();
}
}
@ -60,10 +49,8 @@ namespace Observatory.NativeNotification
{
if (notifications.ContainsKey(guid))
{
Avalonia.Threading.Dispatcher.UIThread.InvokeAsync(() =>
{
notifications[guid].DataContext = new NotificationViewModel(notificationArgs);
});
//TODO: Update notification content
// notifications[guid].DataContext = new NotificationViewModel(notificationArgs);
}
}

View file

@ -1,24 +1,26 @@
using System;
using Avalonia;
using Avalonia.ReactiveUI;
using Observatory.PluginManagement;
using System.Reflection.PortableExecutable;
namespace Observatory
{
class ObservatoryCore
internal static class ObservatoryCore
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
if (args.Length > 0 && System.IO.File.Exists(args[0]))
if (args.Length > 0 && File.Exists(args[0]))
{
var fileInfo = new System.IO.FileInfo(args[0]);
var fileInfo = new FileInfo(args[0]);
if (fileInfo.Extension == ".eop" || fileInfo.Extension == ".zip")
System.IO.File.Copy(
File.Copy(
fileInfo.FullName,
$"{AppDomain.CurrentDomain.BaseDirectory}{System.IO.Path.DirectorySeparatorChar}plugins{System.IO.Path.DirectorySeparatorChar}{fileInfo.Name}");
$"{AppDomain.CurrentDomain.BaseDirectory}{Path.DirectorySeparatorChar}plugins{Path.DirectorySeparatorChar}{fileInfo.Name}");
}
string version = System.Reflection.Assembly.GetEntryAssembly().GetName().Version.ToString();
string version = System.Reflection.Assembly.GetEntryAssembly()?.GetName().Version?.ToString() ?? "0";
try
{
if (Properties.Core.Default.CoreVersion != version)
@ -34,7 +36,14 @@ namespace Observatory
Properties.Core.Default.CoreVersion = version;
Properties.Core.Default.Save();
}
BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
// To customize application configuration such as set high DPI settings or default font,
// see https://aka.ms/applicationconfiguration.
ApplicationConfiguration.Initialize();
Application.Run(new UI.CoreForm());
PluginManagement.PluginManager.GetInstance.Shutdown();
}
catch (Exception ex)
{
@ -63,16 +72,7 @@ namespace Observatory
.AppendLine(ex.StackTrace);
if (ex.InnerException != null)
errorMessage.AppendLine(FormatExceptionMessage(ex.InnerException, true));
return errorMessage.ToString();
}
public static AppBuilder BuildAvaloniaApp()
{
return AppBuilder.Configure<UI.MainApplication>()
.UsePlatformDetect()
.LogToTrace()
.UseReactiveUI();
}
}
}
}

View file

@ -1,77 +1,68 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net6.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<UseWindowsForms>true</UseWindowsForms>
<ImplicitUsings>enable</ImplicitUsings>
<RootNamespace>Observatory</RootNamespace>
<SignAssembly>false</SignAssembly>
<DelaySign>false</DelaySign>
<AssemblyOriginatorKeyFile>ObservatoryKey.snk</AssemblyOriginatorKeyFile>
<!--<PublishTrimmed>true</PublishTrimmed>-->
<TrimMode>Link</TrimMode>
<IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract>
</PropertyGroup>
<RootNamespace>Observatory</RootNamespace>
</PropertyGroup>
<PropertyGroup>
<VersionSuffix>0.2.$([System.DateTime]::UtcNow.Year.ToString().Substring(2))$([System.DateTime]::UtcNow.DayOfYear.ToString().PadLeft(3, "0")).$([System.DateTime]::UtcNow.ToString(HHmm))</VersionSuffix>
<AssemblyVersion Condition=" '$(VersionSuffix)' == '' ">0.0.0.1</AssemblyVersion>
<AssemblyVersion Condition=" '$(VersionSuffix)' != '' ">$(VersionSuffix)</AssemblyVersion>
<Version Condition=" '$(VersionSuffix)' == '' ">0.0.1.0</Version>
<Version Condition=" '$(VersionSuffix)' != '' ">$(VersionSuffix)</Version>
<ApplicationIcon>Assets\EOCIcon-Presized.ico</ApplicationIcon>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Avalonia" Version="11.0.0-preview4" />
<PackageReference Include="Avalonia.Controls.DataGrid" Version="11.0.0-preview4" />
<PackageReference Include="Avalonia.Desktop" Version="11.0.0-preview4" />
<PackageReference Include="Avalonia.Diagnostics" Version="11.0.0-preview4" />
<PackageReference Include="Avalonia.ReactiveUI" Version="11.0.0-preview4" />
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.0.0-preview4" />
<PackageReference Include="Avalonia.Themes.Simple" Version="11.0.0-preview4" />
<PackageReference Include="Egorozh.ColorPicker.Avalonia.Dialog" Version="11.0.0-preview1" />
<PackageReference Include="MessageBox.Avalonia" Version="2.3.1-prev2" />
<PackageReference Include="System.Configuration.ConfigurationManager" Version="7.0.0" />
<PackageReference Include="System.Speech" Version="7.0.0" />
</ItemGroup>
<PropertyGroup>
<VersionSuffix>0.2.$([System.DateTime]::UtcNow.Year.ToString().Substring(2))$([System.DateTime]::UtcNow.DayOfYear.ToString().PadLeft(3, "0")).$([System.DateTime]::UtcNow.ToString(HHmm))</VersionSuffix>
<AssemblyVersion Condition=" '$(VersionSuffix)' == '' ">0.0.0.1</AssemblyVersion>
<AssemblyVersion Condition=" '$(VersionSuffix)' != '' ">$(VersionSuffix)</AssemblyVersion>
<Version Condition=" '$(VersionSuffix)' == '' ">0.0.1.0</Version>
<Version Condition=" '$(VersionSuffix)' != '' ">$(VersionSuffix)</Version>
<ApplicationIcon>Assets\EOCIcon-Presized.ico</ApplicationIcon>
<StartupObject>Observatory.ObservatoryCore</StartupObject>
<SignAssembly>False</SignAssembly>
</PropertyGroup>
<ItemGroup>
<AvaloniaResource Include="Assets\**" />
</ItemGroup>
<ItemGroup>
<Reference Include="ObservatoryFramework">
<HintPath>..\ObservatoryFramework\bin\Release\net6.0\ObservatoryFramework.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Security.Extensions" Version="1.2.0" />
<PackageReference Include="System.Speech" Version="7.0.0" />
</ItemGroup>
<ItemGroup>
<Reference Include="ObservatoryFramework">
<HintPath>..\ObservatoryFramework\bin\Release\net6.0\ObservatoryFramework.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Update="Properties\Core.Designer.cs">
<DesignTimeSharedInput>True</DesignTimeSharedInput>
<AutoGen>True</AutoGen>
<DependentUpon>Core.settings</DependentUpon>
</Compile>
<Compile Update="Properties\Resources.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<None Update="Properties\Core.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Core.Designer.cs</LastGenOutput>
</None>
</ItemGroup>
<ItemGroup>
<Compile Update="Properties\Core.Designer.cs">
<DesignTimeSharedInput>True</DesignTimeSharedInput>
<AutoGen>True</AutoGen>
<DependentUpon>Core.settings</DependentUpon>
</Compile>
<Compile Update="UI\Views\CoreView.axaml.cs">
<DependentUpon>CoreView.axaml</DependentUpon>
</Compile>
<Compile Update="UI\Views\NotificationView.axaml.cs">
<DependentUpon>NotificationView.axaml</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<None Update="Properties\Core.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Core.Designer.cs</LastGenOutput>
</None>
</ItemGroup>
<Target Name="PreBuild" BeforeTargets="PreBuildEvent">
<Exec Condition=" '$(OS)' == 'Windows_NT'" Command="if not exist &quot;$(ProjectDir)..\ObservatoryFramework\bin\Release\net5.0\ObservatoryFramework.dll&quot; dotnet build &quot;$(ProjectDir)..\ObservatoryFramework\ObservatoryFramework.csproj&quot; -c Release" />
<Exec Condition=" '$(OS)' == 'Windows_NT'" Command="if not exist &quot;$(OutDir)plugins\ObservatoryExplorer.dll&quot; dotnet build &quot;$(ProjectDir)..\ObservatoryExplorer\ObservatoryExplorer.csproj&quot; -c $(ConfigurationName)" />
<Exec Condition=" '$(OS)' != 'Windows_NT'" Command="[ ! -e &quot;$(ProjectDir)../ObservatoryFramework/bin/Release/net5.0/ObservatoryFramework.dll&quot; ] &amp;&amp; dotnet build &quot;$(ProjectDir)../ObservatoryFramework/ObservatoryFramework.csproj&quot; -c Release || echo No build necessary" />
<Exec Condition=" '$(OS)' != 'Windows_NT'" Command="[ ! -e &quot;$(ProjectDir)$(OutDir)plugins/ObservatoryExplorer.dll&quot; ] &amp;&amp; dotnet build &quot;$(ProjectDir)../ObservatoryExplorer/ObservatoryExplorer.csproj&quot; -c $(ConfigurationName) || echo No build necessary" />
</Target>
</Project>
<Target Name="PreBuild" BeforeTargets="PreBuildEvent">
<Exec Condition=" '$(OS)' == 'Windows_NT'" Command="if not exist &quot;$(ProjectDir)..\ObservatoryFramework\bin\Release\net5.0\ObservatoryFramework.dll&quot; dotnet build &quot;$(ProjectDir)..\ObservatoryFramework\ObservatoryFramework.csproj&quot; -c Release" />
<Exec Condition=" '$(OS)' == 'Windows_NT'" Command="if not exist &quot;$(OutDir)plugins\ObservatoryExplorer.dll&quot; dotnet build &quot;$(ProjectDir)..\ObservatoryExplorer\ObservatoryExplorer.csproj&quot; -c $(ConfigurationName)" />
<Exec Condition=" '$(OS)' != 'Windows_NT'" Command="[ ! -e &quot;$(ProjectDir)../ObservatoryFramework/bin/Release/net5.0/ObservatoryFramework.dll&quot; ] &amp;&amp; dotnet build &quot;$(ProjectDir)../ObservatoryFramework/ObservatoryFramework.csproj&quot; -c Release || echo No build necessary" />
<Exec Condition=" '$(OS)' != 'Windows_NT'" Command="[ ! -e &quot;$(ProjectDir)$(OutDir)plugins/ObservatoryExplorer.dll&quot; ] &amp;&amp; dotnet build &quot;$(ProjectDir)../ObservatoryExplorer/ObservatoryExplorer.csproj&quot; -c $(ConfigurationName) || echo No build necessary" />
</Target>
</Project>

View file

@ -1,9 +1,9 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30128.74
# Visual Studio Version 17
VisualStudioVersion = 17.3.32922.545
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ObservatoryCore", "ObservatoryCore.csproj", "{0E1C4F16-858E-4E53-948A-77D81A8F3395}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ObservatoryCore", "ObservatoryCore.csproj", "{036A9A33-8C38-4A0C-BE2E-AC64B1B22090}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -11,15 +11,15 @@ Global
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{0E1C4F16-858E-4E53-948A-77D81A8F3395}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0E1C4F16-858E-4E53-948A-77D81A8F3395}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0E1C4F16-858E-4E53-948A-77D81A8F3395}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0E1C4F16-858E-4E53-948A-77D81A8F3395}.Release|Any CPU.Build.0 = Release|Any CPU
{036A9A33-8C38-4A0C-BE2E-AC64B1B22090}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{036A9A33-8C38-4A0C-BE2E-AC64B1B22090}.Debug|Any CPU.Build.0 = Debug|Any CPU
{036A9A33-8C38-4A0C-BE2E-AC64B1B22090}.Release|Any CPU.ActiveCfg = Release|Any CPU
{036A9A33-8C38-4A0C-BE2E-AC64B1B22090}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {F41B8681-A5D9-4167-9938-56DE88024000}
SolutionGuid = {53E6C705-9815-47F7-8ABF-92A7FA4E2F4B}
EndGlobalSection
EndGlobal

View file

@ -2,6 +2,7 @@
using Observatory.Framework.Files;
using Observatory.Framework.Interfaces;
using Observatory.NativeNotification;
using Observatory.Utils;
using System;
using System.Collections.ObjectModel;
using System.IO;
@ -20,7 +21,7 @@ namespace Observatory.PluginManagement
NativePopup = new();
}
public string Version => System.Reflection.Assembly.GetEntryAssembly().GetName().Version.ToString();
public string Version => System.Reflection.Assembly.GetEntryAssembly()?.GetName().Version?.ToString() ?? "0";
public Action<Exception, String> GetPluginErrorLogger(IObservatoryPlugin plugin)
{
@ -97,50 +98,29 @@ namespace Observatory.PluginManagement
/// </summary>
/// <param name="worker"></param>
/// <param name="item"></param>
public void AddGridItem(IObservatoryWorker worker, List<object> item)
public void AddGridItem(IObservatoryWorker worker, object item)
{
Avalonia.Threading.Dispatcher.UIThread.InvokeAsync(() =>
{
ObservableCollection<object> newRow = new(item);
worker.PluginUI.BasicGrid.Items.Add(newRow);
//Hacky removal of original empty object if one was used to populate columns
if (worker.PluginUI.BasicGrid.Items.Count == 2)
{
bool allNull = true;
foreach (var cell in worker.PluginUI.BasicGrid.Items[0])
{
if (cell != null)
{
allNull = false;
break;
}
}
if (allNull)
worker.PluginUI.BasicGrid.Items.RemoveAt(0);
}
});
worker.PluginUI.DataGrid.Add(item);
}
public void ClearGrid(IObservatoryWorker worker)
public void AddGridItems(IObservatoryWorker worker, IEnumerable<object> items)
{
Avalonia.Threading.Dispatcher.UIThread.InvokeAsync(() =>
{
worker.PluginUI.BasicGrid.Items.Clear();
});
//TODO: Add to winform list
}
public void ClearGrid(IObservatoryWorker worker, object templateItem)
{
//TODO: Clear winform list
}
public void ExecuteOnUIThread(Action action)
{
Avalonia.Threading.Dispatcher.UIThread.InvokeAsync(action);
//TODO: Execute action
}
public System.Net.Http.HttpClient HttpClient
{
get => Observatory.HttpClient.Client;
get => Utils.HttpClient.Client;
}
public LogMonitorState CurrentLogMonitorState
@ -162,7 +142,7 @@ namespace Observatory.PluginManagement
var context = new System.Diagnostics.StackFrame(1).GetMethod();
string folderLocation = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)
+ $"{Path.DirectorySeparatorChar}ObservatoryCore{Path.DirectorySeparatorChar}{context.DeclaringType.Assembly.GetName().Name}{Path.DirectorySeparatorChar}";
+ $"{Path.DirectorySeparatorChar}ObservatoryCore{Path.DirectorySeparatorChar}{context?.DeclaringType?.Assembly.GetName().Name}{Path.DirectorySeparatorChar}";
if (!Directory.Exists(folderLocation))
Directory.CreateDirectory(folderLocation);

View file

@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.Linq;
using Observatory.Framework.Files.Journal;
using System.Timers;
using Observatory.Utils;
namespace Observatory.PluginManagement
{

View file

@ -1,5 +1,4 @@
using Avalonia.Controls;
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
@ -8,6 +7,8 @@ using Observatory.Framework.Interfaces;
using System.IO;
using Observatory.Framework;
using System.Text.Json;
using Observatory.Utils;
using Microsoft.Security.Extensions;
namespace Observatory.PluginManagement
{
@ -191,54 +192,95 @@ namespace Observatory.PluginManagement
string pluginPath = $"{AppDomain.CurrentDomain.BaseDirectory}{Path.DirectorySeparatorChar}plugins";
string ownExe = System.Reflection.Assembly.GetExecutingAssembly().Location;
FileSignatureInfo ownSig;
using (var stream = File.OpenRead(ownExe))
ownSig = FileSignatureInfo.GetFromFileStream(stream);
if (Directory.Exists(pluginPath))
{
ExtractPlugins(pluginPath);
//Temporarily skipping signature checks. Need to do this the right way later.
var pluginLibraries = Directory.GetFiles($"{AppDomain.CurrentDomain.BaseDirectory}{Path.DirectorySeparatorChar}plugins", "*.dll");
//var coreToken = Assembly.GetExecutingAssembly().GetName().GetPublicKeyToken();
foreach (var dll in pluginLibraries)
{
try
{
//var pluginToken = AssemblyName.GetAssemblyName(dll).GetPublicKeyToken();
//PluginStatus signed;
PluginStatus pluginStatus = PluginStatus.SigCheckDisabled;
bool loadOkay = true;
//if (pluginToken.Length == 0)
//{
// errorList.Add($"Warning: {dll} not signed.");
// signed = PluginStatus.Unsigned;
//}
//else if (!coreToken.SequenceEqual(pluginToken))
//{
// errorList.Add($"Warning: {dll} signature does not match.");
// signed = PluginStatus.InvalidSignature;
//}
//else
//{
// errorList.Add($"OK: {dll} signed.");
// signed = PluginStatus.Signed;
//}
if (!Properties.Core.Default.AllowUnsigned)
{
if (ownSig.Kind == SignatureKind.Embedded)
{
FileSignatureInfo pluginSig;
using (var stream = File.OpenRead(dll))
pluginSig = FileSignatureInfo.GetFromFileStream(stream);
//if (signed == PluginStatus.Signed || Properties.Core.Default.AllowUnsigned)
//{
string error = LoadPluginAssembly(dll, observatoryWorkers, observatoryNotifiers);
if (pluginSig.Kind == SignatureKind.Embedded)
{
if (pluginSig.SigningCertificate.Thumbprint == ownSig.SigningCertificate.Thumbprint)
{
pluginStatus = PluginStatus.Signed;
}
else
{
pluginStatus = PluginStatus.InvalidSignature;
}
}
else
{
pluginStatus = PluginStatus.Unsigned;
}
}
else
{
pluginStatus = PluginStatus.NoCert;
}
if (pluginStatus != PluginStatus.Signed && pluginStatus != PluginStatus.NoCert)
{
string pluginHash = ComputeSha512Hash(dll);
if (Properties.Core.Default.UnsignedAllowed == null)
Properties.Core.Default.UnsignedAllowed = new();
if (!Properties.Core.Default.UnsignedAllowed.Contains(pluginHash))
{
string warning;
warning = $"Unable to confirm signature of plugin library {dll}.\r\n\r\n";
warning += "Please ensure that you trust the source of this plugin before loading it.\r\n\r\n";
warning += "Do you wish to continue loading the plugin? If you load this plugin you will not be asked again for this file.";
var response = MessageBox.Show(warning, "Plugin Signature Warning", MessageBoxButtons.OKCancel, MessageBoxIcon.Warning);
if (response == DialogResult.OK)
{
Properties.Core.Default.UnsignedAllowed.Add(pluginHash);
Properties.Core.Default.Save();
}
else
{
loadOkay = false;
}
}
}
}
if (loadOkay)
{
string error = LoadPluginAssembly(dll, observatoryWorkers, observatoryNotifiers, pluginStatus);
if (!string.IsNullOrWhiteSpace(error))
{
errorList.Add((error, string.Empty));
}
//}
//else
//{
// LoadPlaceholderPlugin(dll, signed, observatoryNotifiers);
//}
}
}
catch (Exception ex)
{
errorList.Add(($"ERROR: {new FileInfo(dll).Name}, {ex.Message}", ex.StackTrace));
errorList.Add(($"ERROR: {new FileInfo(dll).Name}, {ex.Message}", ex.StackTrace ?? String.Empty));
LoadPlaceholderPlugin(dll, PluginStatus.InvalidLibrary, observatoryNotifiers);
}
}
@ -246,6 +288,15 @@ namespace Observatory.PluginManagement
return errorList;
}
private static string ComputeSha512Hash(string filePath)
{
using (var SHA512 = System.Security.Cryptography.SHA512.Create())
{
using (FileStream fileStream = File.OpenRead(filePath))
return BitConverter.ToString(SHA512.ComputeHash(fileStream)).Replace("-", "").ToLowerInvariant();
}
}
private static void ExtractPlugins(string pluginFolder)
{
var files = Directory.GetFiles(pluginFolder, "*.zip")
@ -265,9 +316,8 @@ namespace Observatory.PluginManagement
}
}
private static string LoadPluginAssembly(string dllPath, List<(IObservatoryWorker plugin, PluginStatus signed)> workers, List<(IObservatoryNotifier plugin, PluginStatus signed)> notifiers)
private static string LoadPluginAssembly(string dllPath, List<(IObservatoryWorker plugin, PluginStatus signed)> workers, List<(IObservatoryNotifier plugin, PluginStatus signed)> notifiers, PluginStatus pluginStatus)
{
string recursionGuard = string.Empty;
System.Runtime.Loader.AssemblyLoadContext.Default.Resolving += (context, name) => {
@ -293,14 +343,12 @@ namespace Observatory.PluginManagement
if (name.Name != recursionGuard)
{
recursionGuard = name.Name;
return context.LoadFromAssemblyName(name);
}
else
{
throw new Exception("Unable to load assembly " + name.Name);
}
};
var pluginAssembly = System.Runtime.Loader.AssemblyLoadContext.Default.LoadFromAssemblyPath(new FileInfo(dllPath).FullName);
@ -325,12 +373,12 @@ namespace Observatory.PluginManagement
{
ConstructorInfo constructor = worker.GetConstructor(Array.Empty<Type>());
object instance = constructor.Invoke(Array.Empty<object>());
workers.Add((instance as IObservatoryWorker, PluginStatus.Signed));
workers.Add((instance as IObservatoryWorker, pluginStatus));
if (instance is IObservatoryNotifier)
{
// This is also a notifier; add to the notifier list as well, so the work and notifier are
// the same instance and can share state.
notifiers.Add((instance as IObservatoryNotifier, PluginStatus.Signed));
notifiers.Add((instance as IObservatoryNotifier, pluginStatus));
}
pluginCount++;
}
@ -366,13 +414,39 @@ namespace Observatory.PluginManagement
notifiers.Add((placeholder, pluginStatus));
}
/// <summary>
/// Possible plugin load results and signature statuses.
/// </summary>
public enum PluginStatus
{
/// <summary>
/// Plugin valid and signed with matching certificate.
/// </summary>
Signed,
/// <summary>
/// Plugin valid but not signed with any certificate.
/// </summary>
Unsigned,
/// <summary>
/// Plugin valid but not signed with valid certificate.
/// </summary>
InvalidSignature,
/// <summary>
/// Plugin invalid and cannot be loaded. Possible version mismatch.
/// </summary>
InvalidPlugin,
InvalidLibrary
/// <summary>
/// Plugin not a CLR library.
/// </summary>
InvalidLibrary,
/// <summary>
/// Plugin valid but executing assembly has no certificate to match against.
/// </summary>
NoCert,
/// <summary>
/// Plugin signature checks disabled.
/// </summary>
SigCheckDisabled
}
}
}

View file

@ -12,7 +12,7 @@ namespace Observatory.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.2.0.0")]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.3.0.0")]
internal sealed partial class Core : global::System.Configuration.ApplicationSettingsBase {
private static Core defaultInstance = ((Core)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Core())));
@ -277,13 +277,12 @@ namespace Observatory.Properties {
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("Fixed width")]
public string ExportStyle {
public global::System.Collections.Specialized.StringCollection UnsignedAllowed {
get {
return ((string)(this["ExportStyle"]));
return ((global::System.Collections.Specialized.StringCollection)(this["UnsignedAllowed"]));
}
set {
this["ExportStyle"] = value;
this["UnsignedAllowed"] = value;
}
}
}

View file

@ -65,8 +65,8 @@
<Setting Name="StartReadAll" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
<Setting Name="ExportStyle" Type="System.String" Scope="User">
<Value Profile="(Default)">Fixed width</Value>
<Setting Name="UnsignedAllowed" Type="System.Collections.Specialized.StringCollection" Scope="User">
<Value Profile="(Default)" />
</Setting>
</Settings>
</SettingsFile>

View file

@ -0,0 +1,63 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Observatory.Properties {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Observatory.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
}
}

View file

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

705
ObservatoryCore/UI/CoreForm.Designer.cs generated Normal file
View file

@ -0,0 +1,705 @@
namespace Observatory.UI
{
partial class CoreForm
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(CoreForm));
this.CoreMenu = new System.Windows.Forms.MenuStrip();
this.coreToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem();
this.CorePanel = new System.Windows.Forms.Panel();
this.VoiceSettingsPanel = new System.Windows.Forms.Panel();
this.VoiceSpeedSlider = new System.Windows.Forms.TrackBar();
this.VoiceVolumeSlider = new System.Windows.Forms.TrackBar();
this.VoiceTestButton = new System.Windows.Forms.Button();
this.VoiceCheckbox = new System.Windows.Forms.CheckBox();
this.VoiceDropdown = new System.Windows.Forms.ComboBox();
this.VoiceLabel = new System.Windows.Forms.Label();
this.VoiceSpeedLabel = new System.Windows.Forms.Label();
this.VoiceVolumeLabel = new System.Windows.Forms.Label();
this.VoiceNotificationLabel = new System.Windows.Forms.Label();
this.PopupSettingsPanel = new System.Windows.Forms.Panel();
this.DurationSpinner = new System.Windows.Forms.NumericUpDown();
this.ScaleSpinner = new System.Windows.Forms.NumericUpDown();
this.LabelColour = new System.Windows.Forms.Label();
this.TestButton = new System.Windows.Forms.Button();
this.ColourButton = new System.Windows.Forms.Button();
this.PopupCheckbox = new System.Windows.Forms.CheckBox();
this.LabelDuration = new System.Windows.Forms.Label();
this.LabelScale = new System.Windows.Forms.Label();
this.FontDropdown = new System.Windows.Forms.ComboBox();
this.LabelFont = new System.Windows.Forms.Label();
this.CornerDropdown = new System.Windows.Forms.ComboBox();
this.DisplayDropdown = new System.Windows.Forms.ComboBox();
this.CornerLabel = new System.Windows.Forms.Label();
this.DisplayLabel = new System.Windows.Forms.Label();
this.PopupNotificationLabel = new System.Windows.Forms.Label();
this.PluginFolderButton = new System.Windows.Forms.Button();
this.PluginList = new System.Windows.Forms.ListView();
this.NameColumn = new System.Windows.Forms.ColumnHeader();
this.TypeColumn = new System.Windows.Forms.ColumnHeader();
this.VersionColumn = new System.Windows.Forms.ColumnHeader();
this.StatusColumn = new System.Windows.Forms.ColumnHeader();
this.ReadAllButton = new System.Windows.Forms.Button();
this.ToggleMonitorButton = new System.Windows.Forms.Button();
this.ClearButton = new System.Windows.Forms.Button();
this.ExportButton = new System.Windows.Forms.Button();
this.GithubLink = new System.Windows.Forms.LinkLabel();
this.DonateLink = new System.Windows.Forms.LinkLabel();
this.PopupColour = new System.Windows.Forms.ColorDialog();
this.CoreMenu.SuspendLayout();
this.CorePanel.SuspendLayout();
this.VoiceSettingsPanel.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.VoiceSpeedSlider)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.VoiceVolumeSlider)).BeginInit();
this.PopupSettingsPanel.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.DurationSpinner)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.ScaleSpinner)).BeginInit();
this.SuspendLayout();
//
// CoreMenu
//
this.CoreMenu.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)));
this.CoreMenu.AutoSize = false;
this.CoreMenu.BackColor = System.Drawing.Color.Black;
this.CoreMenu.Dock = System.Windows.Forms.DockStyle.None;
this.CoreMenu.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.coreToolStripMenuItem,
this.toolStripMenuItem1});
this.CoreMenu.LayoutStyle = System.Windows.Forms.ToolStripLayoutStyle.VerticalStackWithOverflow;
this.CoreMenu.Location = new System.Drawing.Point(0, 0);
this.CoreMenu.Name = "CoreMenu";
this.CoreMenu.Size = new System.Drawing.Size(120, 691);
this.CoreMenu.TabIndex = 0;
//
// coreToolStripMenuItem
//
this.coreToolStripMenuItem.Font = new System.Drawing.Font("Segoe UI", 18F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
this.coreToolStripMenuItem.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(224)))), ((int)(((byte)(224)))), ((int)(((byte)(224)))));
this.coreToolStripMenuItem.Name = "coreToolStripMenuItem";
this.coreToolStripMenuItem.Size = new System.Drawing.Size(113, 36);
this.coreToolStripMenuItem.Text = "Core";
this.coreToolStripMenuItem.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
//
// toolStripMenuItem1
//
this.toolStripMenuItem1.Alignment = System.Windows.Forms.ToolStripItemAlignment.Right;
this.toolStripMenuItem1.Font = new System.Drawing.Font("Segoe UI", 18F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
this.toolStripMenuItem1.ForeColor = System.Drawing.Color.Gainsboro;
this.toolStripMenuItem1.Name = "toolStripMenuItem1";
this.toolStripMenuItem1.Size = new System.Drawing.Size(113, 36);
this.toolStripMenuItem1.Text = "<";
this.toolStripMenuItem1.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
//
// CorePanel
//
this.CorePanel.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.CorePanel.AutoScroll = true;
this.CorePanel.Controls.Add(this.VoiceSettingsPanel);
this.CorePanel.Controls.Add(this.VoiceNotificationLabel);
this.CorePanel.Controls.Add(this.PopupSettingsPanel);
this.CorePanel.Controls.Add(this.PopupNotificationLabel);
this.CorePanel.Controls.Add(this.PluginFolderButton);
this.CorePanel.Controls.Add(this.PluginList);
this.CorePanel.Location = new System.Drawing.Point(123, 12);
this.CorePanel.Name = "CorePanel";
this.CorePanel.Size = new System.Drawing.Size(665, 679);
this.CorePanel.TabIndex = 1;
//
// VoiceSettingsPanel
//
this.VoiceSettingsPanel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.VoiceSettingsPanel.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.VoiceSettingsPanel.Controls.Add(this.VoiceSpeedSlider);
this.VoiceSettingsPanel.Controls.Add(this.VoiceVolumeSlider);
this.VoiceSettingsPanel.Controls.Add(this.VoiceTestButton);
this.VoiceSettingsPanel.Controls.Add(this.VoiceCheckbox);
this.VoiceSettingsPanel.Controls.Add(this.VoiceDropdown);
this.VoiceSettingsPanel.Controls.Add(this.VoiceLabel);
this.VoiceSettingsPanel.Controls.Add(this.VoiceSpeedLabel);
this.VoiceSettingsPanel.Controls.Add(this.VoiceVolumeLabel);
this.VoiceSettingsPanel.Location = new System.Drawing.Point(3, 426);
this.VoiceSettingsPanel.Name = "VoiceSettingsPanel";
this.VoiceSettingsPanel.Size = new System.Drawing.Size(659, 177);
this.VoiceSettingsPanel.TabIndex = 5;
this.VoiceSettingsPanel.Visible = false;
//
// VoiceSpeedSlider
//
this.VoiceSpeedSlider.Location = new System.Drawing.Point(121, 51);
this.VoiceSpeedSlider.Maximum = 100;
this.VoiceSpeedSlider.Name = "VoiceSpeedSlider";
this.VoiceSpeedSlider.Size = new System.Drawing.Size(120, 45);
this.VoiceSpeedSlider.TabIndex = 15;
this.VoiceSpeedSlider.TickFrequency = 10;
this.VoiceSpeedSlider.TickStyle = System.Windows.Forms.TickStyle.Both;
this.VoiceSpeedSlider.Value = 50;
this.VoiceSpeedSlider.Scroll += new System.EventHandler(this.VoiceSpeedSlider_Scroll);
//
// VoiceVolumeSlider
//
this.VoiceVolumeSlider.LargeChange = 10;
this.VoiceVolumeSlider.Location = new System.Drawing.Point(120, 0);
this.VoiceVolumeSlider.Maximum = 100;
this.VoiceVolumeSlider.Name = "VoiceVolumeSlider";
this.VoiceVolumeSlider.Size = new System.Drawing.Size(121, 45);
this.VoiceVolumeSlider.TabIndex = 14;
this.VoiceVolumeSlider.TickFrequency = 10;
this.VoiceVolumeSlider.TickStyle = System.Windows.Forms.TickStyle.Both;
this.VoiceVolumeSlider.Value = 100;
this.VoiceVolumeSlider.Scroll += new System.EventHandler(this.VoiceVolumeSlider_Scroll);
//
// VoiceTestButton
//
this.VoiceTestButton.BackColor = System.Drawing.Color.DimGray;
this.VoiceTestButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.VoiceTestButton.ForeColor = System.Drawing.Color.WhiteSmoke;
this.VoiceTestButton.Location = new System.Drawing.Point(190, 131);
this.VoiceTestButton.Name = "VoiceTestButton";
this.VoiceTestButton.Size = new System.Drawing.Size(51, 23);
this.VoiceTestButton.TabIndex = 13;
this.VoiceTestButton.Text = "Test";
this.VoiceTestButton.UseVisualStyleBackColor = false;
//
// VoiceCheckbox
//
this.VoiceCheckbox.AutoSize = true;
this.VoiceCheckbox.ForeColor = System.Drawing.Color.Gainsboro;
this.VoiceCheckbox.Location = new System.Drawing.Point(120, 134);
this.VoiceCheckbox.Name = "VoiceCheckbox";
this.VoiceCheckbox.Size = new System.Drawing.Size(68, 19);
this.VoiceCheckbox.TabIndex = 11;
this.VoiceCheckbox.Text = "Enabled";
this.VoiceCheckbox.UseVisualStyleBackColor = true;
this.VoiceCheckbox.CheckedChanged += new System.EventHandler(this.VoiceCheckbox_CheckedChanged);
//
// VoiceDropdown
//
this.VoiceDropdown.FormattingEnabled = true;
this.VoiceDropdown.Location = new System.Drawing.Point(121, 102);
this.VoiceDropdown.Name = "VoiceDropdown";
this.VoiceDropdown.Size = new System.Drawing.Size(121, 23);
this.VoiceDropdown.TabIndex = 5;
this.VoiceDropdown.SelectedIndexChanged += new System.EventHandler(this.VoiceDropdown_SelectedIndexChanged);
//
// VoiceLabel
//
this.VoiceLabel.AutoSize = true;
this.VoiceLabel.ForeColor = System.Drawing.Color.Gainsboro;
this.VoiceLabel.Location = new System.Drawing.Point(77, 105);
this.VoiceLabel.Name = "VoiceLabel";
this.VoiceLabel.Size = new System.Drawing.Size(38, 15);
this.VoiceLabel.TabIndex = 4;
this.VoiceLabel.Text = "Voice:";
this.VoiceLabel.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
//
// VoiceSpeedLabel
//
this.VoiceSpeedLabel.AutoSize = true;
this.VoiceSpeedLabel.ForeColor = System.Drawing.Color.Gainsboro;
this.VoiceSpeedLabel.Location = new System.Drawing.Point(73, 63);
this.VoiceSpeedLabel.Name = "VoiceSpeedLabel";
this.VoiceSpeedLabel.Size = new System.Drawing.Size(42, 15);
this.VoiceSpeedLabel.TabIndex = 1;
this.VoiceSpeedLabel.Text = "Speed:";
this.VoiceSpeedLabel.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
//
// VoiceVolumeLabel
//
this.VoiceVolumeLabel.AutoSize = true;
this.VoiceVolumeLabel.ForeColor = System.Drawing.Color.Gainsboro;
this.VoiceVolumeLabel.Location = new System.Drawing.Point(64, 12);
this.VoiceVolumeLabel.Name = "VoiceVolumeLabel";
this.VoiceVolumeLabel.Size = new System.Drawing.Size(50, 15);
this.VoiceVolumeLabel.TabIndex = 0;
this.VoiceVolumeLabel.Text = "Volume:";
this.VoiceVolumeLabel.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
//
// VoiceNotificationLabel
//
this.VoiceNotificationLabel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.VoiceNotificationLabel.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.VoiceNotificationLabel.ForeColor = System.Drawing.Color.LightGray;
this.VoiceNotificationLabel.Location = new System.Drawing.Point(3, 403);
this.VoiceNotificationLabel.Name = "VoiceNotificationLabel";
this.VoiceNotificationLabel.Size = new System.Drawing.Size(659, 23);
this.VoiceNotificationLabel.TabIndex = 4;
this.VoiceNotificationLabel.Text = "❯ Voice Notifications";
this.VoiceNotificationLabel.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
this.VoiceNotificationLabel.Click += new System.EventHandler(this.VoiceNotificationLabel_Click);
//
// PopupSettingsPanel
//
this.PopupSettingsPanel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.PopupSettingsPanel.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.PopupSettingsPanel.Controls.Add(this.DurationSpinner);
this.PopupSettingsPanel.Controls.Add(this.ScaleSpinner);
this.PopupSettingsPanel.Controls.Add(this.LabelColour);
this.PopupSettingsPanel.Controls.Add(this.TestButton);
this.PopupSettingsPanel.Controls.Add(this.ColourButton);
this.PopupSettingsPanel.Controls.Add(this.PopupCheckbox);
this.PopupSettingsPanel.Controls.Add(this.LabelDuration);
this.PopupSettingsPanel.Controls.Add(this.LabelScale);
this.PopupSettingsPanel.Controls.Add(this.FontDropdown);
this.PopupSettingsPanel.Controls.Add(this.LabelFont);
this.PopupSettingsPanel.Controls.Add(this.CornerDropdown);
this.PopupSettingsPanel.Controls.Add(this.DisplayDropdown);
this.PopupSettingsPanel.Controls.Add(this.CornerLabel);
this.PopupSettingsPanel.Controls.Add(this.DisplayLabel);
this.PopupSettingsPanel.Location = new System.Drawing.Point(3, 195);
this.PopupSettingsPanel.Name = "PopupSettingsPanel";
this.PopupSettingsPanel.Size = new System.Drawing.Size(659, 208);
this.PopupSettingsPanel.TabIndex = 3;
this.PopupSettingsPanel.Visible = false;
//
// DurationSpinner
//
this.DurationSpinner.BackColor = System.Drawing.Color.DimGray;
this.DurationSpinner.ForeColor = System.Drawing.Color.Gainsboro;
this.DurationSpinner.Increment = new decimal(new int[] {
25,
0,
0,
0});
this.DurationSpinner.Location = new System.Drawing.Point(121, 119);
this.DurationSpinner.Maximum = new decimal(new int[] {
60000,
0,
0,
0});
this.DurationSpinner.Minimum = new decimal(new int[] {
100,
0,
0,
0});
this.DurationSpinner.Name = "DurationSpinner";
this.DurationSpinner.Size = new System.Drawing.Size(120, 23);
this.DurationSpinner.TabIndex = 15;
this.DurationSpinner.Value = new decimal(new int[] {
8000,
0,
0,
0});
this.DurationSpinner.ValueChanged += new System.EventHandler(this.DurationSpinner_ValueChanged);
//
// ScaleSpinner
//
this.ScaleSpinner.BackColor = System.Drawing.Color.DimGray;
this.ScaleSpinner.ForeColor = System.Drawing.Color.Gainsboro;
this.ScaleSpinner.Location = new System.Drawing.Point(121, 90);
this.ScaleSpinner.Maximum = new decimal(new int[] {
500,
0,
0,
0});
this.ScaleSpinner.Minimum = new decimal(new int[] {
1,
0,
0,
0});
this.ScaleSpinner.Name = "ScaleSpinner";
this.ScaleSpinner.Size = new System.Drawing.Size(120, 23);
this.ScaleSpinner.TabIndex = 14;
this.ScaleSpinner.Value = new decimal(new int[] {
100,
0,
0,
0});
this.ScaleSpinner.ValueChanged += new System.EventHandler(this.ScaleSpinner_ValueChanged);
//
// LabelColour
//
this.LabelColour.AutoSize = true;
this.LabelColour.ForeColor = System.Drawing.Color.Gainsboro;
this.LabelColour.Location = new System.Drawing.Point(68, 152);
this.LabelColour.Name = "LabelColour";
this.LabelColour.Size = new System.Drawing.Size(46, 15);
this.LabelColour.TabIndex = 13;
this.LabelColour.Text = "Colour:";
this.LabelColour.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
//
// TestButton
//
this.TestButton.BackColor = System.Drawing.Color.DimGray;
this.TestButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.TestButton.ForeColor = System.Drawing.Color.WhiteSmoke;
this.TestButton.Location = new System.Drawing.Point(190, 148);
this.TestButton.Name = "TestButton";
this.TestButton.Size = new System.Drawing.Size(51, 23);
this.TestButton.TabIndex = 12;
this.TestButton.Text = "Test";
this.TestButton.UseVisualStyleBackColor = false;
//
// ColourButton
//
this.ColourButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.ColourButton.Location = new System.Drawing.Point(121, 148);
this.ColourButton.Name = "ColourButton";
this.ColourButton.Size = new System.Drawing.Size(51, 23);
this.ColourButton.TabIndex = 11;
this.ColourButton.UseVisualStyleBackColor = true;
this.ColourButton.Click += new System.EventHandler(this.ColourButton_Click);
//
// PopupCheckbox
//
this.PopupCheckbox.AutoSize = true;
this.PopupCheckbox.ForeColor = System.Drawing.Color.Gainsboro;
this.PopupCheckbox.Location = new System.Drawing.Point(120, 177);
this.PopupCheckbox.Name = "PopupCheckbox";
this.PopupCheckbox.Size = new System.Drawing.Size(68, 19);
this.PopupCheckbox.TabIndex = 10;
this.PopupCheckbox.Text = "Enabled";
this.PopupCheckbox.UseVisualStyleBackColor = true;
this.PopupCheckbox.CheckedChanged += new System.EventHandler(this.PopupCheckbox_CheckedChanged);
//
// LabelDuration
//
this.LabelDuration.AutoSize = true;
this.LabelDuration.ForeColor = System.Drawing.Color.Gainsboro;
this.LabelDuration.Location = new System.Drawing.Point(32, 121);
this.LabelDuration.Name = "LabelDuration";
this.LabelDuration.Size = new System.Drawing.Size(83, 15);
this.LabelDuration.TabIndex = 9;
this.LabelDuration.Text = "Duration (ms):";
this.LabelDuration.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
//
// LabelScale
//
this.LabelScale.AutoSize = true;
this.LabelScale.ForeColor = System.Drawing.Color.Gainsboro;
this.LabelScale.Location = new System.Drawing.Point(57, 92);
this.LabelScale.Name = "LabelScale";
this.LabelScale.Size = new System.Drawing.Size(58, 15);
this.LabelScale.TabIndex = 7;
this.LabelScale.Text = "Scale (%):";
this.LabelScale.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
//
// FontDropdown
//
this.FontDropdown.FormattingEnabled = true;
this.FontDropdown.Location = new System.Drawing.Point(120, 61);
this.FontDropdown.Name = "FontDropdown";
this.FontDropdown.Size = new System.Drawing.Size(121, 23);
this.FontDropdown.TabIndex = 5;
this.FontDropdown.SelectedIndexChanged += new System.EventHandler(this.FontDropdown_SelectedIndexChanged);
//
// LabelFont
//
this.LabelFont.AutoSize = true;
this.LabelFont.ForeColor = System.Drawing.Color.Gainsboro;
this.LabelFont.Location = new System.Drawing.Point(80, 64);
this.LabelFont.Name = "LabelFont";
this.LabelFont.Size = new System.Drawing.Size(34, 15);
this.LabelFont.TabIndex = 4;
this.LabelFont.Text = "Font:";
this.LabelFont.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
//
// CornerDropdown
//
this.CornerDropdown.FormattingEnabled = true;
this.CornerDropdown.Items.AddRange(new object[] {
"Bottom-Right",
"Bottom-Left",
"Top-Right",
"Top-Left"});
this.CornerDropdown.Location = new System.Drawing.Point(120, 32);
this.CornerDropdown.Name = "CornerDropdown";
this.CornerDropdown.Size = new System.Drawing.Size(121, 23);
this.CornerDropdown.TabIndex = 3;
this.CornerDropdown.SelectedIndexChanged += new System.EventHandler(this.CornerDropdown_SelectedIndexChanged);
//
// DisplayDropdown
//
this.DisplayDropdown.FormattingEnabled = true;
this.DisplayDropdown.Location = new System.Drawing.Point(120, 3);
this.DisplayDropdown.Name = "DisplayDropdown";
this.DisplayDropdown.Size = new System.Drawing.Size(121, 23);
this.DisplayDropdown.TabIndex = 2;
this.DisplayDropdown.SelectedIndexChanged += new System.EventHandler(this.DisplayDropdown_SelectedIndexChanged);
//
// CornerLabel
//
this.CornerLabel.AutoSize = true;
this.CornerLabel.ForeColor = System.Drawing.Color.Gainsboro;
this.CornerLabel.Location = new System.Drawing.Point(68, 35);
this.CornerLabel.Name = "CornerLabel";
this.CornerLabel.Size = new System.Drawing.Size(46, 15);
this.CornerLabel.TabIndex = 1;
this.CornerLabel.Text = "Corner:";
this.CornerLabel.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
//
// DisplayLabel
//
this.DisplayLabel.AutoSize = true;
this.DisplayLabel.ForeColor = System.Drawing.Color.Gainsboro;
this.DisplayLabel.Location = new System.Drawing.Point(66, 6);
this.DisplayLabel.Name = "DisplayLabel";
this.DisplayLabel.Size = new System.Drawing.Size(48, 15);
this.DisplayLabel.TabIndex = 0;
this.DisplayLabel.Text = "Display:";
this.DisplayLabel.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
//
// PopupNotificationLabel
//
this.PopupNotificationLabel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.PopupNotificationLabel.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.PopupNotificationLabel.ForeColor = System.Drawing.Color.LightGray;
this.PopupNotificationLabel.Location = new System.Drawing.Point(3, 172);
this.PopupNotificationLabel.Name = "PopupNotificationLabel";
this.PopupNotificationLabel.Size = new System.Drawing.Size(659, 23);
this.PopupNotificationLabel.TabIndex = 2;
this.PopupNotificationLabel.Text = "❯ Popup Notifications";
this.PopupNotificationLabel.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
this.PopupNotificationLabel.Click += new System.EventHandler(this.PopupNotificationLabel_Click);
//
// PluginFolderButton
//
this.PluginFolderButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.PluginFolderButton.BackColor = System.Drawing.Color.DimGray;
this.PluginFolderButton.FlatAppearance.BorderSize = 0;
this.PluginFolderButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.PluginFolderButton.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(224)))), ((int)(((byte)(224)))), ((int)(((byte)(224)))));
this.PluginFolderButton.Location = new System.Drawing.Point(532, 140);
this.PluginFolderButton.Name = "PluginFolderButton";
this.PluginFolderButton.Size = new System.Drawing.Size(130, 23);
this.PluginFolderButton.TabIndex = 1;
this.PluginFolderButton.Text = "Open Plugin Folder";
this.PluginFolderButton.UseVisualStyleBackColor = false;
//
// PluginList
//
this.PluginList.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.PluginList.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.PluginList.BorderStyle = System.Windows.Forms.BorderStyle.None;
this.PluginList.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
this.NameColumn,
this.TypeColumn,
this.VersionColumn,
this.StatusColumn});
this.PluginList.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(224)))), ((int)(((byte)(224)))), ((int)(((byte)(224)))));
this.PluginList.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.Nonclickable;
this.PluginList.Location = new System.Drawing.Point(3, 3);
this.PluginList.MultiSelect = false;
this.PluginList.Name = "PluginList";
this.PluginList.OwnerDraw = true;
this.PluginList.Scrollable = false;
this.PluginList.Size = new System.Drawing.Size(659, 137);
this.PluginList.TabIndex = 0;
this.PluginList.UseCompatibleStateImageBehavior = false;
this.PluginList.View = System.Windows.Forms.View.Details;
this.PluginList.Resize += new System.EventHandler(this.PluginList_Resize);
//
// NameColumn
//
this.NameColumn.Text = "Plugin";
this.NameColumn.Width = 180;
//
// TypeColumn
//
this.TypeColumn.Text = "Type";
this.TypeColumn.Width = 120;
//
// VersionColumn
//
this.VersionColumn.Text = "Version";
this.VersionColumn.Width = 120;
//
// StatusColumn
//
this.StatusColumn.Text = "Status";
//
// ReadAllButton
//
this.ReadAllButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.ReadAllButton.BackColor = System.Drawing.Color.DimGray;
this.ReadAllButton.FlatAppearance.BorderSize = 0;
this.ReadAllButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.ReadAllButton.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(224)))), ((int)(((byte)(224)))), ((int)(((byte)(224)))));
this.ReadAllButton.Location = new System.Drawing.Point(713, 698);
this.ReadAllButton.Name = "ReadAllButton";
this.ReadAllButton.Size = new System.Drawing.Size(75, 23);
this.ReadAllButton.TabIndex = 2;
this.ReadAllButton.Text = "Read All";
this.ReadAllButton.UseVisualStyleBackColor = false;
this.ReadAllButton.Click += new System.EventHandler(this.ReadAllButton_Click);
//
// ToggleMonitorButton
//
this.ToggleMonitorButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.ToggleMonitorButton.BackColor = System.Drawing.Color.DimGray;
this.ToggleMonitorButton.FlatAppearance.BorderSize = 0;
this.ToggleMonitorButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.ToggleMonitorButton.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(224)))), ((int)(((byte)(224)))), ((int)(((byte)(224)))));
this.ToggleMonitorButton.Location = new System.Drawing.Point(610, 698);
this.ToggleMonitorButton.Name = "ToggleMonitorButton";
this.ToggleMonitorButton.Size = new System.Drawing.Size(97, 23);
this.ToggleMonitorButton.TabIndex = 3;
this.ToggleMonitorButton.Text = "Start Monitor";
this.ToggleMonitorButton.UseVisualStyleBackColor = false;
//
// ClearButton
//
this.ClearButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.ClearButton.BackColor = System.Drawing.Color.DimGray;
this.ClearButton.FlatAppearance.BorderSize = 0;
this.ClearButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.ClearButton.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(224)))), ((int)(((byte)(224)))), ((int)(((byte)(224)))));
this.ClearButton.Location = new System.Drawing.Point(529, 698);
this.ClearButton.Name = "ClearButton";
this.ClearButton.Size = new System.Drawing.Size(75, 23);
this.ClearButton.TabIndex = 4;
this.ClearButton.Text = "Clear";
this.ClearButton.UseVisualStyleBackColor = false;
//
// ExportButton
//
this.ExportButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.ExportButton.BackColor = System.Drawing.Color.DimGray;
this.ExportButton.FlatAppearance.BorderSize = 0;
this.ExportButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.ExportButton.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(224)))), ((int)(((byte)(224)))), ((int)(((byte)(224)))));
this.ExportButton.Location = new System.Drawing.Point(448, 698);
this.ExportButton.Name = "ExportButton";
this.ExportButton.Size = new System.Drawing.Size(75, 23);
this.ExportButton.TabIndex = 5;
this.ExportButton.Text = "Export";
this.ExportButton.UseVisualStyleBackColor = false;
//
// GithubLink
//
this.GithubLink.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.GithubLink.AutoSize = true;
this.GithubLink.LinkColor = System.Drawing.Color.White;
this.GithubLink.Location = new System.Drawing.Point(12, 694);
this.GithubLink.Name = "GithubLink";
this.GithubLink.Size = new System.Drawing.Size(42, 15);
this.GithubLink.TabIndex = 6;
this.GithubLink.TabStop = true;
this.GithubLink.Text = "github";
//
// DonateLink
//
this.DonateLink.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.DonateLink.AutoSize = true;
this.DonateLink.LinkColor = System.Drawing.Color.White;
this.DonateLink.Location = new System.Drawing.Point(12, 709);
this.DonateLink.Name = "DonateLink";
this.DonateLink.Size = new System.Drawing.Size(45, 15);
this.DonateLink.TabIndex = 7;
this.DonateLink.TabStop = true;
this.DonateLink.Text = "Donate";
//
// CoreForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.BackColor = System.Drawing.Color.Black;
this.ClientSize = new System.Drawing.Size(800, 733);
this.Controls.Add(this.DonateLink);
this.Controls.Add(this.GithubLink);
this.Controls.Add(this.ExportButton);
this.Controls.Add(this.ClearButton);
this.Controls.Add(this.ToggleMonitorButton);
this.Controls.Add(this.ReadAllButton);
this.Controls.Add(this.CorePanel);
this.Controls.Add(this.CoreMenu);
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.MainMenuStrip = this.CoreMenu;
this.Name = "CoreForm";
this.Text = "Elite Observatory Core";
this.CoreMenu.ResumeLayout(false);
this.CoreMenu.PerformLayout();
this.CorePanel.ResumeLayout(false);
this.VoiceSettingsPanel.ResumeLayout(false);
this.VoiceSettingsPanel.PerformLayout();
((System.ComponentModel.ISupportInitialize)(this.VoiceSpeedSlider)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.VoiceVolumeSlider)).EndInit();
this.PopupSettingsPanel.ResumeLayout(false);
this.PopupSettingsPanel.PerformLayout();
((System.ComponentModel.ISupportInitialize)(this.DurationSpinner)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.ScaleSpinner)).EndInit();
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private MenuStrip CoreMenu;
private ToolStripMenuItem coreToolStripMenuItem;
private Panel CorePanel;
private Button ReadAllButton;
private Button ToggleMonitorButton;
private Button ClearButton;
private Button ExportButton;
private LinkLabel GithubLink;
private LinkLabel DonateLink;
private ListView PluginList;
private ColumnHeader NameColumn;
private ColumnHeader TypeColumn;
private ColumnHeader VersionColumn;
private ColumnHeader StatusColumn;
private Button PluginFolderButton;
private Panel PopupSettingsPanel;
private ComboBox CornerDropdown;
private ComboBox DisplayDropdown;
private Label CornerLabel;
private Label DisplayLabel;
private Label PopupNotificationLabel;
private NumericUpDown DurationSpinner;
private NumericUpDown ScaleSpinner;
private Label LabelColour;
private Button TestButton;
private Button ColourButton;
private CheckBox PopupCheckbox;
private Label LabelDuration;
private Label LabelScale;
private ComboBox FontDropdown;
private Label LabelFont;
private ColorDialog PopupColour;
private ToolStripMenuItem toolStripMenuItem1;
private Panel VoiceSettingsPanel;
private TrackBar VoiceSpeedSlider;
private TrackBar VoiceVolumeSlider;
private Button VoiceTestButton;
private CheckBox VoiceCheckbox;
private ComboBox VoiceDropdown;
private Label VoiceLabel;
private Label VoiceSpeedLabel;
private Label VoiceVolumeLabel;
private Label VoiceNotificationLabel;
}
}

View file

@ -0,0 +1,418 @@
using Observatory.Framework.Interfaces;
using Observatory.PluginManagement;
using Observatory.Utils;
namespace Observatory.UI
{
public partial class CoreForm : Form
{
private Dictionary<object, Panel> uiPanels;
public CoreForm()
{
InitializeComponent();
PopulateDropdownOptions();
PopulateNativeSettings();
ColourListHeader(ref PluginList, Color.DarkSlateGray, Color.LightGray);
PopulatePluginList();
FitColumns();
string version = System.Reflection.Assembly.GetEntryAssembly()?.GetName().Version?.ToString() ?? "0";
Text += $" - v{version}";
CoreMenu.SizeChanged += CoreMenu_SizeChanged;
uiPanels = new();
uiPanels.Add(coreToolStripMenuItem, CorePanel);
pluginList = new Dictionary<string, ToolStripMenuItem>();
CreatePluginTabs();
CreatePluginSettings();
CoreMenu.ItemClicked += CoreMenu_ItemClicked;
PreCollapsePanels();
}
private void PreCollapsePanels()
{
AdjustPanelsBelow(VoiceSettingsPanel, AdjustmentDirection.Up);
AdjustPanelsBelow(PopupSettingsPanel, AdjustmentDirection.Up);
}
private void PopulateDropdownOptions()
{
var fonts = new System.Drawing.Text.InstalledFontCollection().Families;
FontDropdown.Items.AddRange(fonts.Select(f => f.Name).ToArray());
DisplayDropdown.Items.Add("Primary");
if (Screen.AllScreens.Length > 1)
for (int i = 0; i < Screen.AllScreens.Length; i++)
DisplayDropdown.Items.Add((i + 1).ToString());
var voices = new System.Speech.Synthesis.SpeechSynthesizer().GetInstalledVoices();
foreach (var voice in voices.Select(v => v.VoiceInfo.Name))
VoiceDropdown.Items.Add(voice);
}
private void PopulateNativeSettings()
{
var settings = Properties.Core.Default;
DisplayDropdown.SelectedIndex = settings.NativeNotifyScreen + 1;
CornerDropdown.SelectedIndex = settings.NativeNotifyCorner;
FontDropdown.SelectedItem = settings.NativeNotifyFont;
ScaleSpinner.Value = settings.NativeNotifyScale;
DurationSpinner.Value = settings.NativeNotifyTimeout;
ColourButton.BackColor = Color.FromArgb((int)settings.NativeNotifyColour);
PopupCheckbox.Checked = settings.NativeNotify;
VoiceVolumeSlider.Value = settings.VoiceVolume;
VoiceSpeedSlider.Value = settings.VoiceRate;
VoiceDropdown.SelectedItem = settings.VoiceSelected;
VoiceCheckbox.Checked = settings.VoiceNotify;
}
private void CoreMenu_SizeChanged(object? sender, EventArgs e)
{
CorePanel.Location = new Point(12 + CoreMenu.Width, 12);
CorePanel.Width = Width - CoreMenu.Width - 40;
}
private Dictionary<string, ToolStripMenuItem> pluginList;
private void CreatePluginTabs()
{
var uiPlugins = PluginManager.GetInstance.workerPlugins.Where(p => p.plugin.PluginUI.PluginUIType != Framework.PluginUI.UIType.None);
PluginHelper.CreatePluginTabs(CoreMenu, uiPlugins, uiPanels);
foreach(ToolStripMenuItem item in CoreMenu.Items)
{
pluginList.Add(item.Text, item);
}
}
private void CreatePluginSettings()
{
foreach (var plugin in PluginManager.GetInstance.workerPlugins)
{
var pluginSettingsPanel = new SettingsPanel(plugin.plugin, AdjustPanelsBelow);
AddSettingsPanel(pluginSettingsPanel);
}
foreach (var plugin in PluginManager.GetInstance.notifyPlugins)
{
var pluginSettingsPanel = new SettingsPanel(plugin.plugin, AdjustPanelsBelow);
AddSettingsPanel(pluginSettingsPanel);
}
}
private void AddSettingsPanel(SettingsPanel panel)
{
int lowestPoint = 0;
foreach (Control control in CorePanel.Controls)
{
if (control.Location.Y + control.Height > lowestPoint)
lowestPoint = control.Location.Y + control.Height;
}
panel.Header.Location = new Point(PopupNotificationLabel.Location.X, lowestPoint);
panel.Header.Width = PopupNotificationLabel.Width;
panel.Header.Font = PopupNotificationLabel.Font;
panel.Header.ForeColor = PopupNotificationLabel.ForeColor;
panel.Header.BackColor = PopupNotificationLabel.BackColor;
panel.Header.TextAlign = PopupNotificationLabel.TextAlign;
panel.Location = new Point(PopupNotificationLabel.Location.X, lowestPoint + panel.Header.Height);
panel.Width = PopupSettingsPanel.Width;
CorePanel.Controls.Add(panel.Header);
CorePanel.Controls.Add(panel);
}
private void PopulatePluginList()
{
List<IObservatoryPlugin> uniquePlugins = new();
foreach (var (plugin, signed) in PluginManager.GetInstance.workerPlugins)
{
if (!uniquePlugins.Contains(plugin))
{
uniquePlugins.Add(plugin);
ListViewItem item = new ListViewItem(new[] { plugin.Name, "Worker", plugin.Version, PluginStatusString(signed) });
PluginList.Items.Add(item);
}
}
}
private static string PluginStatusString(PluginManager.PluginStatus status)
{
switch (status)
{
case PluginManager.PluginStatus.Signed:
return "Signed";
case PluginManager.PluginStatus.Unsigned:
return "Unsigned";
case PluginManager.PluginStatus.InvalidSignature:
return "Invalid Signature";
case PluginManager.PluginStatus.InvalidPlugin:
return "Invalid Plugin";
case PluginManager.PluginStatus.InvalidLibrary:
return "Invalid File";
case PluginManager.PluginStatus.NoCert:
return "Unsigned Observatory (Debug build)";
case PluginManager.PluginStatus.SigCheckDisabled:
return "Signature Checks Disabled";
default:
return string.Empty;
}
}
private void CoreMenu_ItemClicked(object? _, ToolStripItemClickedEventArgs e)
{
if (e.ClickedItem.Text == "<")
{
foreach (KeyValuePair<string, ToolStripMenuItem> menuItem in pluginList)
{
if (menuItem.Value.Text == "<")
menuItem.Value.Text = ">";
else
menuItem.Value.Text = menuItem.Key[..1];
}
CoreMenu.Width = 40;
CorePanel.Location = new Point(43, 12);
// CorePanel.Width += 40;
}
else if (e.ClickedItem.Text == ">")
{
foreach (KeyValuePair<string, ToolStripMenuItem> menuItem in pluginList)
{
if (menuItem.Value.Text == ">")
menuItem.Value.Text = "<";
else
menuItem.Value.Text = menuItem.Key;
}
CoreMenu.Width = 120;
CorePanel.Location = new Point(123, 12);
// CorePanel.Width -= 40;
}
else
{
foreach (var panel in uiPanels.Where(p => p.Key != e.ClickedItem))
{
panel.Value.Visible = false;
}
if (!Controls.Contains(uiPanels[e.ClickedItem]))
{
uiPanels[e.ClickedItem].Location = CorePanel.Location;
uiPanels[e.ClickedItem].Size = CorePanel.Size;
uiPanels[e.ClickedItem].BackColor = CorePanel.BackColor;
Controls.Add(uiPanels[e.ClickedItem]);
}
uiPanels[e.ClickedItem].Visible = true;
}
}
private static void ColourListHeader(ref ListView list, Color backColor, Color foreColor)
{
list.OwnerDraw = true;
list.DrawColumnHeader +=
new DrawListViewColumnHeaderEventHandler
(
(sender, e) => headerDraw(sender, e, backColor, foreColor)
);
list.DrawItem += new DrawListViewItemEventHandler(bodyDraw);
}
private static void headerDraw(object? _, DrawListViewColumnHeaderEventArgs e, Color backColor, Color foreColor)
{
using (SolidBrush backBrush = new(backColor))
{
e.Graphics.FillRectangle(backBrush, e.Bounds);
}
using (Pen borderBrush = new(Color.Black))
{
e.Graphics.DrawLine(borderBrush, e.Bounds.Left, e.Bounds.Top, e.Bounds.Left, e.Bounds.Bottom);
e.Graphics.DrawLine(borderBrush, e.Bounds.Right, e.Bounds.Top, e.Bounds.Right, e.Bounds.Bottom);
}
if (e.Font != null && e.Header != null)
using (SolidBrush foreBrush = new(foreColor))
{
var format = new StringFormat();
format.Alignment = (StringAlignment)e.Header.TextAlign;
format.LineAlignment = StringAlignment.Center;
var paddedBounds = new Rectangle(e.Bounds.X + 2, e.Bounds.Y + 2, e.Bounds.Width - 4, e.Bounds.Height - 4);
e.Graphics.DrawString(e.Header?.Text, e.Font, foreBrush, paddedBounds, format);
}
}
private static void bodyDraw(object? _, DrawListViewItemEventArgs e)
{
e.DrawDefault = true;
}
private void PluginList_Resize(object sender, EventArgs e)
{
FitColumns();
}
private void FitColumns()
{
int totalWidth = 0;
foreach (ColumnHeader col in PluginList.Columns)
totalWidth += col.Width;
PluginList.Columns[3].Width += PluginList.Width - totalWidth;
}
private void ReadAllButton_Click(object sender, EventArgs e)
{
LogMonitor.GetInstance.ReadAllJournals();
}
private void PopupNotificationLabel_Click(object _, EventArgs e)
{
CorePanel.SuspendLayout();
if (PopupNotificationLabel.Text[0] == '❯')
{
PopupNotificationLabel.Text = PopupNotificationLabel.Text.Replace('❯', '⌵');
PopupSettingsPanel.Visible = true;
AdjustPanelsBelow(PopupSettingsPanel, AdjustmentDirection.Down);
}
else
{
PopupNotificationLabel.Text = PopupNotificationLabel.Text.Replace('⌵', '❯');
PopupSettingsPanel.Visible = false;
AdjustPanelsBelow(PopupSettingsPanel, AdjustmentDirection.Up);
}
CorePanel.ResumeLayout();
}
private void VoiceNotificationLabel_Click(object sender, EventArgs e)
{
CorePanel.SuspendLayout();
if (VoiceNotificationLabel.Text[0] == '❯')
{
VoiceNotificationLabel.Text = VoiceNotificationLabel.Text.Replace('❯', '⌵');
VoiceSettingsPanel.Visible = true;
AdjustPanelsBelow(VoiceSettingsPanel, AdjustmentDirection.Down);
}
else
{
VoiceNotificationLabel.Text = VoiceNotificationLabel.Text.Replace('⌵', '❯');
VoiceSettingsPanel.Visible = false;
AdjustPanelsBelow(VoiceSettingsPanel, AdjustmentDirection.Up);
}
CorePanel.ResumeLayout();
}
private void AdjustPanelsBelow(Control toggledControl, AdjustmentDirection adjustmentDirection)
{
var distance = adjustmentDirection == AdjustmentDirection.Down ? toggledControl.Height : -toggledControl.Height;
foreach (Control control in CorePanel.Controls)
{
var loc = control.Location;
if (loc.Y >= toggledControl.Location.Y && control != toggledControl)
{
loc.Y = control.Location.Y + distance;
control.Location = loc;
}
}
}
internal enum AdjustmentDirection
{
Up, Down
}
#region Settings Changes
private void ColourButton_Click(object _, EventArgs e)
{
var selectionResult = PopupColour.ShowDialog();
if (selectionResult == DialogResult.OK)
{
ColourButton.BackColor = PopupColour.Color;
Properties.Core.Default.NativeNotifyColour = (uint)PopupColour.Color.ToArgb();
Properties.Core.Default.Save();
}
}
private void PopupCheckbox_CheckedChanged(object _, EventArgs e)
{
Properties.Core.Default.NativeNotify = PopupCheckbox.Checked;
Properties.Core.Default.Save();
}
private void DurationSpinner_ValueChanged(object _, EventArgs e)
{
Properties.Core.Default.NativeNotifyTimeout = (int)DurationSpinner.Value;
Properties.Core.Default.Save();
}
private void ScaleSpinner_ValueChanged(object _, EventArgs e)
{
Properties.Core.Default.NativeNotifyScale = (int)ScaleSpinner.Value;
Properties.Core.Default.Save();
}
private void FontDropdown_SelectedIndexChanged(object _, EventArgs e)
{
Properties.Core.Default.NativeNotifyFont = FontDropdown.SelectedItem.ToString();
Properties.Core.Default.Save();
}
private void CornerDropdown_SelectedIndexChanged(object _, EventArgs e)
{
Properties.Core.Default.NativeNotifyCorner = CornerDropdown.SelectedIndex;
Properties.Core.Default.Save();
}
private void DisplayDropdown_SelectedIndexChanged(object _, EventArgs e)
{
Properties.Core.Default.NativeNotifyScreen = DisplayDropdown.SelectedIndex - 1;
Properties.Core.Default.Save();
}
private void VoiceVolumeSlider_Scroll(object _, EventArgs e)
{
Properties.Core.Default.VoiceVolume = VoiceVolumeSlider.Value;
Properties.Core.Default.Save();
}
private void VoiceSpeedSlider_Scroll(object _, EventArgs e)
{
Properties.Core.Default.VoiceRate = VoiceSpeedSlider.Value;
Properties.Core.Default.Save();
}
private void VoiceCheckbox_CheckedChanged(object _, EventArgs e)
{
Properties.Core.Default.VoiceNotify = VoiceCheckbox.Checked;
Properties.Core.Default.Save();
}
private void VoiceDropdown_SelectedIndexChanged(object _, EventArgs e)
{
Properties.Core.Default.VoiceSelected = VoiceDropdown.SelectedItem.ToString();
Properties.Core.Default.Save();
}
#endregion
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,103 @@
using System;
using System.Collections;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Observatory.UI
{
internal class DefaultSorter : IComparer
{
/// <summary>
/// Specifies the column to be sorted
/// </summary>
private int ColumnToSort;
/// <summary>
/// Specifies the order in which to sort (i.e. 'Ascending').
/// </summary>
private SortOrder OrderOfSort;
/// <summary>
/// Case insensitive comparer object
/// </summary>
private CaseInsensitiveComparer ObjectCompare;
/// <summary>
/// Class constructor. Initializes various elements
/// </summary>
public DefaultSorter()
{
// Initialize the column to '0'
ColumnToSort = 0;
// Initialize the sort order to 'none'
OrderOfSort = SortOrder.None;
// Initialize the CaseInsensitiveComparer object
ObjectCompare = new CaseInsensitiveComparer();
}
/// <summary>
/// This method is inherited from the IComparer interface. It compares the two objects passed using a case insensitive comparison.
/// </summary>
/// <param name="x">First object to be compared</param>
/// <param name="y">Second object to be compared</param>
/// <returns>The result of the comparison. "0" if equal, negative if 'x' is less than 'y' and positive if 'x' is greater than 'y'</returns>
public int Compare(object? x, object? y)
{
int compareResult;
ListViewItem? listviewX = (ListViewItem?)x;
ListViewItem? listviewY = (ListViewItem?)y;
// Compare the two items
compareResult = ObjectCompare.Compare(listviewX?.SubItems[ColumnToSort].Text, listviewY?.SubItems[ColumnToSort].Text);
// Calculate correct return value based on object comparison
if (OrderOfSort == SortOrder.Ascending)
{
// Ascending sort is selected, return normal result of compare operation
return compareResult;
}
else if (OrderOfSort == SortOrder.Descending)
{
// Descending sort is selected, return negative result of compare operation
return (-compareResult);
}
else
{
// Return '0' to indicate they are equal
return 0;
}
}
/// <summary>
/// Gets or sets the number of the column to which to apply the sorting operation (Defaults to '0').
/// </summary>
public int SortColumn
{
set
{
ColumnToSort = value;
}
get
{
return ColumnToSort;
}
}
/// <summary>
/// Gets or sets the order of sorting to apply (for example, 'Ascending' or 'Descending').
/// </summary>
public SortOrder Order
{
set
{
OrderOfSort = value;
}
get
{
return OrderOfSort;
}
}
}
}

View file

@ -1,15 +0,0 @@
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:dialog="clr-namespace:Egorozh.ColorPicker.Dialog;assembly=Egorozh.ColorPicker.Avalonia.Dialog"
xmlns:local="clr-namespace:Observatory.UI"
x:Class="Observatory.UI.MainApplication">
<Application.DataTemplates>
<local:ViewLocator/>
</Application.DataTemplates>
<Application.Styles>
<FluentTheme Mode="Dark"/>
<StyleInclude Source="avares://Avalonia.Controls.DataGrid/Themes/Fluent.xaml"/>
<StyleInclude Source="avares://Egorozh.ColorPicker.Avalonia.Dialog/Themes/Default.axaml" />
<dialog:FluentColorPickerTheme Mode="Dark" />
</Application.Styles>
</Application>

View file

@ -1,34 +0,0 @@
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
using Observatory.UI.ViewModels;
namespace Observatory.UI
{
public class MainApplication : Application
{
public override void Initialize()
{
AvaloniaXamlLoader.Load(this);
}
public override void OnFrameworkInitializationCompleted()
{
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
var pluginManager = PluginManagement.PluginManager.GetInstance;
desktop.MainWindow = new Views.MainWindow()
{
DataContext = new MainWindowViewModel(pluginManager)
};
desktop.MainWindow.Closing += (o, e) =>
{
pluginManager.Shutdown();
};
}
base.OnFrameworkInitializationCompleted();
}
}
}

View file

@ -1,14 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Observatory.UI.Models
{
public class BasicUIModel
{
public string Time { get; set; }
public string Description { get; set; }
}
}

View file

@ -1,17 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Avalonia.Controls;
using Observatory.UI.ViewModels;
namespace Observatory.UI.Models
{
public class CoreModel
{
public string Name { get; set; }
public ViewModelBase UI { get; set; }
}
}

Some files were not shown because too many files have changed in this diff Show more