3 Commits
master ... cyka

Author SHA1 Message Date
db019639da Update 2026-01-17 02:10:04 +01:00
3c0ff7f9dd Add profiles from other branches 2026-01-17 02:09:58 +01:00
aa4e7a443f Implement configuration profiles 2026-01-17 02:09:53 +01:00
26 changed files with 334 additions and 1345 deletions

View File

@@ -1,100 +0,0 @@
{
"runtimeTarget": {
"name": ".NETCoreApp,Version=v6.0",
"signature": ""
},
"compilationOptions": {},
"targets": {
".NETCoreApp,Version=v6.0": {
"EVE-O-Preview/1.0.0": {
"dependencies": {
"LightInject": "7.0.1",
"MediatR": "9.0.0",
"Microsoft.CSharp": "4.7.0",
"Microsoft.NET.ILLink.Analyzers": "7.0.100-1.23211.1",
"Microsoft.NET.ILLink.Tasks": "7.0.100-1.23211.1",
"Newtonsoft.Json": "13.0.3"
},
"runtime": {
"EVE-O-Preview.dll": {}
}
},
"LightInject/7.0.1": {
"runtime": {
"lib/netstandard2.0/LightInject.dll": {
"assemblyVersion": "7.0.1.0",
"fileVersion": "7.0.1.0"
}
}
},
"MediatR/9.0.0": {
"runtime": {
"lib/netstandard2.1/MediatR.dll": {
"assemblyVersion": "9.0.0.0",
"fileVersion": "9.0.0.0"
}
}
},
"Microsoft.CSharp/4.7.0": {},
"Microsoft.NET.ILLink.Analyzers/7.0.100-1.23211.1": {},
"Microsoft.NET.ILLink.Tasks/7.0.100-1.23211.1": {},
"Newtonsoft.Json/13.0.3": {
"runtime": {
"lib/net6.0/Newtonsoft.Json.dll": {
"assemblyVersion": "13.0.0.0",
"fileVersion": "13.0.3.27908"
}
}
}
}
},
"libraries": {
"EVE-O-Preview/1.0.0": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"LightInject/7.0.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-aw4ayG2Pe68i+85ws8zYk7MPCKjEd4DeBoTqVvmjA2cfpYYNnw+v0E5T3PKdriKaxdKF+eUzlnxWWZnYK/gx4w==",
"path": "lightinject/7.0.1",
"hashPath": "lightinject.7.0.1.nupkg.sha512"
},
"MediatR/9.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-8b3UYNxegHVYcJMG2zH8wn+YqxLvXG+eMfj0cMCq/jTW72p6O3PCKMkrIv0mqyxdW7bA4gblsocw7n+/9Akg5g==",
"path": "mediatr/9.0.0",
"hashPath": "mediatr.9.0.0.nupkg.sha512"
},
"Microsoft.CSharp/4.7.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA==",
"path": "microsoft.csharp/4.7.0",
"hashPath": "microsoft.csharp.4.7.0.nupkg.sha512"
},
"Microsoft.NET.ILLink.Analyzers/7.0.100-1.23211.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-0GvbEgDGcUQA9KuWcQU1WwYHXt1tBzNr1Nls/M57rM7NA/AndFwCaCEoJpJkmxRY7xLlPDBnmGp8h5+FNqUngg==",
"path": "microsoft.net.illink.analyzers/7.0.100-1.23211.1",
"hashPath": "microsoft.net.illink.analyzers.7.0.100-1.23211.1.nupkg.sha512"
},
"Microsoft.NET.ILLink.Tasks/7.0.100-1.23211.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-tvG8XZYLjT0o3WicCyKBZysVWo1jC9HdCFmNRmddx3WbAz0UCsd0qKZqpiEo99VLA8Re+FzWK51OcRldQPbt2Q==",
"path": "microsoft.net.illink.tasks/7.0.100-1.23211.1",
"hashPath": "microsoft.net.illink.tasks.7.0.100-1.23211.1.nupkg.sha512"
},
"Newtonsoft.Json/13.0.3": {
"type": "package",
"serviceable": true,
"sha512": "sha512-HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==",
"path": "newtonsoft.json/13.0.3",
"hashPath": "newtonsoft.json.13.0.3.nupkg.sha512"
}
}
}

View File

@@ -1,128 +0,0 @@
{
"ConfigVersion": 1,
"CycleGroup1ForwardHotkeys": [
"F14",
"Control+F14"
],
"CycleGroup1BackwardHotkeys": [
"F13",
"Control+F13"
],
"CycleGroup1ClientsOrder": {
"EVE - Example DPS Toon 1": 1,
"EVE - Example DPS Toon 2": 2,
"EVE - Example DPS Toon 3": 3
},
"CycleGroup2ForwardHotkeys": [
"F16",
"Control+F16"
],
"CycleGroup2BackwardHotkeys": [
"F15",
"Control+F15"
],
"CycleGroup2ClientsOrder": {
"EVE - Example Logi Toon 1": 1,
"EVE - Example Scout Toon 2": 2,
"EVE - Example Tackle Toon 3": 3
},
"CycleGroup3ForwardHotkeys": [
""
],
"CycleGroup3BackwardHotkeys": [
""
],
"CycleGroup3ClientsOrder": {
"EVE - cycle group 3": 1
},
"CycleGroup4ForwardHotkeys": [
""
],
"CycleGroup4BackwardHotkeys": [
""
],
"CycleGroup4ClientsOrder": {
"EVE - cycle group 4": 1
},
"CycleGroup5ForwardHotkeys": [
""
],
"CycleGroup5BackwardHotkeys": [
""
],
"CycleGroup5ClientsOrder": {
"EVE - cycle group 5": 1
},
"PerClientActiveClientHighlightColor": {
"EVE - Example Toon 1": "Red",
"EVE - Example Toon 2": "Green"
},
"PerClientThumbnailSize": {
"EVE - Example Toon 1": "200, 200",
"EVE - Example Toon 2": "200, 200"
},
"PerClientThumbnailRegion": {},
"PerClientZoomAnchor": {
"EVE - Example Toon 1": 1,
"EVE - Example Toon 2": 7
},
"MinimizeToTray": false,
"ThumbnailRefreshPeriod": 500,
"ThumbnailResizeTimeoutPeriod": 500,
"WineCompatibilityMode": false,
"ThumbnailsOpacity": 0.5,
"EnableClientLayoutTracking": false,
"HideActiveClientThumbnail": false,
"HideLoginClientThumbnail": false,
"MinimizeInactiveClients": false,
"WindowsAnimationStyle": 1,
"ShowThumbnailsAlwaysOnTop": true,
"EnablePerClientThumbnailLayouts": false,
"HideThumbnailsOnLostFocus": false,
"HideThumbnailsDelay": 2,
"ThumbnailSize": "408, 177",
"ThumbnailMaximumSize": "960, 540",
"ThumbnailMinimumSize": "192, 108",
"EnableThumbnailSnap": true,
"ThumbnailSnapRange": 20,
"EnableThumbnailZoom": false,
"ThumbnailZoomFactor": 2,
"ThumbnailZoomAnchor": 0,
"OverlayLabelAnchor": 0,
"ShowThumbnailOverlays": true,
"ShowThumbnailFrames": false,
"LockThumbnailLocation": false,
"ThumbnailSnapToGrid": true,
"ThumbnailSnapToGridSizeX": 102,
"ThumbnailSnapToGridSizeY": 44,
"EnableActiveClientHighlight": false,
"ActiveClientHighlightColor": "GreenYellow",
"OverlayLabelColor": "Orange",
"OverlayLabelSize": 10,
"EnableThumbnailRegionSnipping": true,
"DefaultThumbnailRegion": "1187, 1252, 398, 174",
"CurrentProfile": "Default",
"AvailableProfiles": [
"Default"
],
"IconName": "IconOriginal",
"ActiveClientHighlightThickness": 3,
"LoginThumbnailLocation": "5, 5",
"ToggleTrackingHotkey": "Alt+F16",
"ToggleSingleProcessHotkey": "Control+F16",
"ToggleAllThumbnailsHotkey": "Alt+F12",
"ThumbnailsManuallyHidden": false,
"PerClientLayout": {},
"FlatLayout": {
"EVE - Sn v1 cosunoo (2)": "1938, 1100",
"EVE - Primorium (1)": "1530, 1100",
"EVE - PhatPhuckDave (3)": "1122, 1100"
},
"ClientLayout": {},
"ClientHotkey": {},
"DisableThumbnail": {},
"PriorityClients": [],
"ExecutablesToPreview": [
"exefile"
]
}

View File

@@ -1,18 +0,0 @@
{
"runtimeOptions": {
"tfm": "net6.0",
"frameworks": [
{
"name": "Microsoft.NETCore.App",
"version": "6.0.0"
},
{
"name": "Microsoft.WindowsDesktop.App",
"version": "6.0.0"
}
],
"configProperties": {
"System.Reflection.Metadata.MetadataUpdater.IsSupported": false
}
}
}

View File

@@ -1,10 +0,0 @@
{
"ActiveProfile": "capacitor",
"Profiles": [
"Default",
"capacitor",
"drones",
"target-1row",
"target-3row"
]
}

View File

@@ -80,11 +80,11 @@
"EnablePerClientThumbnailLayouts": false,
"HideThumbnailsOnLostFocus": false,
"HideThumbnailsDelay": 2,
"ThumbnailSize": "512, 288",
"ThumbnailSize": "451, 238",
"ThumbnailMaximumSize": "960, 540",
"ThumbnailMinimumSize": "192, 108",
"EnableThumbnailSnap": true,
"ThumbnailSnapRange": 0,
"ThumbnailSnapRange": 150,
"EnableThumbnailZoom": false,
"ThumbnailZoomFactor": 2,
"ThumbnailZoomAnchor": 0,
@@ -93,13 +93,13 @@
"ShowThumbnailFrames": false,
"LockThumbnailLocation": false,
"ThumbnailSnapToGrid": true,
"ThumbnailSnapToGridSizeX": 64,
"ThumbnailSnapToGridSizeY": 36,
"ThumbnailSnapToGridSizeX": 112,
"ThumbnailSnapToGridSizeY": 59,
"EnableActiveClientHighlight": true,
"ActiveClientHighlightColor": "GreenYellow",
"OverlayLabelColor": "Orange",
"OverlayLabelSize": 10,
"EnableThumbnailRegionSnipping": false,
"EnableThumbnailRegionSnipping": true,
"DefaultThumbnailRegion": "1664, 188, 451, 238",
"CurrentProfile": "Default",
"AvailableProfiles": [
@@ -110,21 +110,20 @@
"LoginThumbnailLocation": "5, 5",
"ToggleTrackingHotkey": "Alt+F16",
"ToggleSingleProcessHotkey": "Control+F16",
"ToggleAllThumbnailsHotkey": "Alt+Oem3",
"ToggleAllThumbnailsHotkey": "Shift+Alt+Oem3",
"ThumbnailsManuallyHidden": false,
"PerClientLayout": {},
"FlatLayout": {
"EVE - Quartio": "-896, 1620",
"EVE - Tertiale": "-640, 1332",
"EVE - PhatPhuckDave": "-1152, 1044",
"EVE - Primorium": "-640, 1044",
"EVE - Secundamen": "-1152, 1332",
"EVE - Quartomo": "-512, 1152",
"EVE - Tertiashi": "-512, 864",
"EVE - Secundaru": "-512, 576",
"EVE - Primosaki": "-512, 288",
"EVE - PhuckingWeebDave": "-512, 0",
"EVE": "0, 0"
"EVE - Sn v1 cosunoo (2)": "1995, 1058",
"EVE - Primorium (1)": "240, 645",
"EVE - PhatPhuckDave (3)": "224, -2",
"EVE - Primorium (3)": "146, 285",
"EVE - PhatPhuckDave (2)": "146, 57",
"EVE - Sn v1 cosunoo (1)": "154, 472",
"EVE - Sn v1 cosunoo (3)": "292, 517",
"EVE - Primorium (2)": "224, 236",
"EVE - PhatPhuckDave (1)": "292, 57",
"EVE - Secundamen (1)": "224, 474"
},
"ClientLayout": {},
"ClientHotkey": {},

View File

@@ -118,12 +118,7 @@
"EVE - Tertiale": "0, 1155",
"EVE - Secundamen": "0, 1015",
"EVE - Primorium": "0, 875",
"EVE - PhatPhuckDave": "0, 735",
"EVE - Quartomo": "0, 1295",
"EVE - Tertiashi": "0, 1155",
"EVE - Secundaru": "0, 1015",
"EVE - Primosaki": "0, 875",
"EVE - PhuckingWeebDave": "0, 735"
"EVE - PhatPhuckDave": "0, 735"
},
"ClientLayout": {},
"ClientHotkey": {},
@@ -132,4 +127,4 @@
"ExecutablesToPreview": [
"exefile"
]
}
}

View File

@@ -1,130 +0,0 @@
{
"ConfigVersion": 1,
"CycleGroup1ForwardHotkeys": [
"F14",
"Control+F14"
],
"CycleGroup1BackwardHotkeys": [
"F13",
"Control+F13"
],
"CycleGroup1ClientsOrder": {
"EVE - Example DPS Toon 1": 1,
"EVE - Example DPS Toon 2": 2,
"EVE - Example DPS Toon 3": 3
},
"CycleGroup2ForwardHotkeys": [
"F16",
"Control+F16"
],
"CycleGroup2BackwardHotkeys": [
"F15",
"Control+F15"
],
"CycleGroup2ClientsOrder": {
"EVE - Example Logi Toon 1": 1,
"EVE - Example Scout Toon 2": 2,
"EVE - Example Tackle Toon 3": 3
},
"CycleGroup3ForwardHotkeys": [
""
],
"CycleGroup3BackwardHotkeys": [
""
],
"CycleGroup3ClientsOrder": {
"EVE - cycle group 3": 1
},
"CycleGroup4ForwardHotkeys": [
""
],
"CycleGroup4BackwardHotkeys": [
""
],
"CycleGroup4ClientsOrder": {
"EVE - cycle group 4": 1
},
"CycleGroup5ForwardHotkeys": [
""
],
"CycleGroup5BackwardHotkeys": [
""
],
"CycleGroup5ClientsOrder": {
"EVE - cycle group 5": 1
},
"PerClientActiveClientHighlightColor": {
"EVE - Example Toon 1": "Red",
"EVE - Example Toon 2": "Green"
},
"PerClientThumbnailSize": {
"EVE - Example Toon 1": "200, 200",
"EVE - Example Toon 2": "200, 200"
},
"PerClientThumbnailRegion": {},
"PerClientZoomAnchor": {
"EVE - Example Toon 1": 1,
"EVE - Example Toon 2": 7
},
"MinimizeToTray": true,
"ThumbnailRefreshPeriod": 500,
"ThumbnailResizeTimeoutPeriod": 500,
"WineCompatibilityMode": false,
"ThumbnailsOpacity": 1.0,
"EnableClientLayoutTracking": false,
"HideActiveClientThumbnail": false,
"HideLoginClientThumbnail": false,
"MinimizeInactiveClients": false,
"WindowsAnimationStyle": 1,
"ShowThumbnailsAlwaysOnTop": true,
"EnablePerClientThumbnailLayouts": false,
"HideThumbnailsOnLostFocus": false,
"HideThumbnailsDelay": 2,
"ThumbnailSize": "351, 148",
"ThumbnailMaximumSize": "960, 540",
"ThumbnailMinimumSize": "192, 108",
"EnableThumbnailSnap": true,
"ThumbnailSnapRange": 0,
"EnableThumbnailZoom": false,
"ThumbnailZoomFactor": 2,
"ThumbnailZoomAnchor": 0,
"OverlayLabelAnchor": 0,
"ShowThumbnailOverlays": true,
"ShowThumbnailFrames": false,
"LockThumbnailLocation": false,
"ThumbnailSnapToGrid": true,
"ThumbnailSnapToGridSizeX": 87,
"ThumbnailSnapToGridSizeY": 37,
"EnableActiveClientHighlight": true,
"ActiveClientHighlightColor": "GreenYellow",
"OverlayLabelColor": "Orange",
"OverlayLabelSize": 10,
"EnableThumbnailRegionSnipping": true,
"DefaultThumbnailRegion": "1764, 988, 351, 148",
"CurrentProfile": "Default",
"AvailableProfiles": [
"Default"
],
"IconName": "IconOriginal",
"ActiveClientHighlightThickness": 3,
"LoginThumbnailLocation": "5, 5",
"ToggleTrackingHotkey": "Alt+F16",
"ToggleSingleProcessHotkey": "Control+F16",
"ToggleAllThumbnailsHotkey": "Alt+Oem3",
"ThumbnailsManuallyHidden": false,
"PerClientLayout": {},
"FlatLayout": {
"EVE - Quartio": "-348, 814",
"EVE - Tertiale": "-348, 666",
"EVE - Secundamen": "-348, 518",
"EVE - Primorium": "-348, 370",
"EVE - PhatPhuckDave": "-348, 222"
},
"ClientLayout": {},
"ClientHotkey": {},
"DisableThumbnail": {},
"PriorityClients": [],
"ExecutablesToPreview": [
"exefile"
]
}

View File

@@ -0,0 +1,4 @@
{
"CurrentProfile": "capacitor",
"ProfileReadOnly": true
}

View File

@@ -84,7 +84,7 @@
"ThumbnailMaximumSize": "960, 540",
"ThumbnailMinimumSize": "192, 108",
"EnableThumbnailSnap": true,
"ThumbnailSnapRange": 0,
"ThumbnailSnapRange": 150,
"EnableThumbnailZoom": false,
"ThumbnailZoomFactor": 2,
"ThumbnailZoomAnchor": 0,
@@ -110,15 +110,15 @@
"LoginThumbnailLocation": "5, 5",
"ToggleTrackingHotkey": "Alt+F16",
"ToggleSingleProcessHotkey": "Control+F16",
"ToggleAllThumbnailsHotkey": "Alt+Oem3",
"ToggleAllThumbnailsHotkey": "Shift+Alt+Oem3",
"ThumbnailsManuallyHidden": false,
"PerClientLayout": {},
"FlatLayout": {
"EVE - Quartio": "0, 1295",
"EVE - Tertiale": "0, 1147",
"EVE - Secundamen": "0, 999",
"EVE - Primorium": "0, 851",
"EVE - PhatPhuckDave": "0, 703"
"EVE - Quartio": "0, 1239",
"EVE - Tertiale": "0, 1003",
"EVE - Secundamen": "0, 767",
"EVE - Primorium": "448, 1121",
"EVE - PhatPhuckDave": "448, 885"
},
"ClientLayout": {},
"ClientHotkey": {},
@@ -127,4 +127,4 @@
"ExecutablesToPreview": [
"exefile"
]
}
}

View File

@@ -110,15 +110,15 @@
"LoginThumbnailLocation": "5, 5",
"ToggleTrackingHotkey": "Alt+F16",
"ToggleSingleProcessHotkey": "Control+F16",
"ToggleAllThumbnailsHotkey": "Alt+Oem3",
"ToggleAllThumbnailsHotkey": "Shift+Alt+Oem3",
"ThumbnailsManuallyHidden": true,
"PerClientLayout": {},
"FlatLayout": {
"EVE - Quartio": "-1120, 1067",
"EVE - Tertiale": "-1568, 1067",
"EVE - Secundamen": "-2016, 1067",
"EVE - Primorium": "-2464, 679",
"EVE - PhatPhuckDave": "-2464, 291"
"EVE - Quartio": "-1229, 1067",
"EVE - Tertiale": "-1680, 1067",
"EVE - Secundamen": "-2131, 1067",
"EVE - Primorium": "-2576, 679",
"EVE - PhatPhuckDave": "-2576, 291"
},
"ClientLayout": {},
"ClientHotkey": {},
@@ -127,4 +127,4 @@
"ExecutablesToPreview": [
"exefile"
]
}
}

View File

@@ -1,43 +0,0 @@
{
"runtimeTarget": {
"name": ".NETCoreApp,Version=v6.0",
"signature": ""
},
"compilationOptions": {},
"targets": {
".NETCoreApp,Version=v6.0": {
"ExeFile/1.0.0": {
"dependencies": {
"Microsoft.CSharp": "4.7.0",
"System.Data.DataSetExtensions": "4.5.0"
},
"runtime": {
"ExeFile.dll": {}
}
},
"Microsoft.CSharp/4.7.0": {},
"System.Data.DataSetExtensions/4.5.0": {}
}
},
"libraries": {
"ExeFile/1.0.0": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Microsoft.CSharp/4.7.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA==",
"path": "microsoft.csharp/4.7.0",
"hashPath": "microsoft.csharp.4.7.0.nupkg.sha512"
},
"System.Data.DataSetExtensions/4.5.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-221clPs1445HkTBZPL+K9sDBdJRB8UN8rgjO3ztB0CQ26z//fmJXtlsr6whGatscsKGBrhJl5bwJuKSA8mwFOw==",
"path": "system.data.datasetextensions/4.5.0",
"hashPath": "system.data.datasetextensions.4.5.0.nupkg.sha512"
}
}
}

View File

@@ -1,3 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
</configuration>

View File

@@ -1,18 +0,0 @@
{
"runtimeOptions": {
"tfm": "net6.0",
"frameworks": [
{
"name": "Microsoft.NETCore.App",
"version": "6.0.0"
},
{
"name": "Microsoft.WindowsDesktop.App",
"version": "6.0.0"
}
],
"configProperties": {
"System.Reflection.Metadata.MetadataUpdater.IsSupported": false
}
}
}

View File

@@ -1,85 +1,203 @@
using System;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Newtonsoft.Json;
namespace EveOPreview.Configuration.Implementation {
class ConfigurationStorage : IConfigurationStorage {
private const string CONFIGURATION_FILE_NAME = "EVE-O-Preview.json";
private const string PROFILES_FOLDER_NAME = "Profiles";
private const string PROFILE_CONFIG_FILE_EXTENSION = ".json";
private const string PROFILES_DIRECTORY = "Profiles";
private const string MASTER_CONFIG_FILE = "master.json";
private readonly IAppConfig _appConfig;
private readonly IThumbnailConfiguration _thumbnailConfiguration;
private readonly string _baseDirectory;
private string _currentProfile;
public ConfigurationStorage(IAppConfig appConfig, IThumbnailConfiguration thumbnailConfiguration) {
this._appConfig = appConfig;
this._thumbnailConfiguration = thumbnailConfiguration;
this._baseDirectory = AppDomain.CurrentDomain.BaseDirectory;
this._currentProfile = "Default";
}
public string CurrentProfile {
get => this._currentProfile;
set {
if (this._currentProfile != value) {
this._currentProfile = value ?? "Default";
}
}
}
public void Load() {
string filename = this.GetConfigFileName();
string masterConfigPath = this.GetMasterConfigPath();
if (!File.Exists(filename)) {
return;
// First, load the master configuration which contains ProfileReadOnly and CurrentProfile
string currentProfile = "Default";
bool profileReadOnly = false;
if (File.Exists(masterConfigPath)) {
string masterRawData = File.ReadAllText(masterConfigPath);
var masterConfig = JsonConvert.DeserializeObject<dynamic>(masterRawData);
if (masterConfig.CurrentProfile != null) {
currentProfile = masterConfig.CurrentProfile.ToString();
}
if (masterConfig.ProfileReadOnly != null) {
profileReadOnly = (bool)masterConfig.ProfileReadOnly;
}
}
string rawData = File.ReadAllText(filename);
this._thumbnailConfiguration.CurrentProfile = currentProfile;
this._thumbnailConfiguration.ProfileReadOnly = profileReadOnly;
JsonSerializerSettings jsonSerializerSettings =
new JsonSerializerSettings() { ObjectCreationHandling = ObjectCreationHandling.Replace };
// Scan the profiles directory for available profiles AFTER loading master config
List<string> scannedProfiles = this.ScanAvailableProfiles();
// StageHotkeyArraysToAvoidDuplicates(rawData);
// Replace AvailableProfiles with scanned profiles (they are the source of truth)
this._thumbnailConfiguration.AvailableProfiles.Clear();
foreach (string profile in scannedProfiles) {
this._thumbnailConfiguration.AvailableProfiles.Add(profile);
}
JsonConvert.PopulateObject(rawData, this._thumbnailConfiguration, jsonSerializerSettings);
// Ensure CurrentProfile is in the list
if (!string.IsNullOrEmpty(this._thumbnailConfiguration.CurrentProfile) &&
!this._thumbnailConfiguration.AvailableProfiles.Contains(this._thumbnailConfiguration.CurrentProfile)) {
this._thumbnailConfiguration.AvailableProfiles.Add(this._thumbnailConfiguration.CurrentProfile);
}
// Then, load the profile-specific configuration
// IMPORTANT: Reset the configuration to clean state before loading
// to avoid merging with existing default values
string profileConfigPath = this.GetProfileConfigPath(currentProfile);
if (File.Exists(profileConfigPath)) {
string profileRawData = File.ReadAllText(profileConfigPath);
JsonSerializerSettings jsonSerializerSettings =
new JsonSerializerSettings() { ObjectCreationHandling = ObjectCreationHandling.Replace };
JsonConvert.PopulateObject(profileRawData, this._thumbnailConfiguration, jsonSerializerSettings);
} else {
// Try to load from legacy config file for backwards compatibility
string legacyConfigPath = this.GetConfigFileName();
if (File.Exists(legacyConfigPath)) {
string legacyRawData = File.ReadAllText(legacyConfigPath);
JsonSerializerSettings jsonSerializerSettings =
new JsonSerializerSettings() { ObjectCreationHandling = ObjectCreationHandling.Replace };
JsonConvert.PopulateObject(legacyRawData, this._thumbnailConfiguration, jsonSerializerSettings);
}
}
// Restore the values we just set (they might have been overwritten by profile load)
this._thumbnailConfiguration.CurrentProfile = currentProfile;
this._thumbnailConfiguration.ProfileReadOnly = profileReadOnly;
// Re-scan profiles after loading in case the profile overwrote AvailableProfiles
this._thumbnailConfiguration.AvailableProfiles.Clear();
foreach (string profile in scannedProfiles) {
this._thumbnailConfiguration.AvailableProfiles.Add(profile);
}
// Validate data after loading it
this._thumbnailConfiguration.ApplyRestrictions();
}
public void Save() {
string rawData = JsonConvert.SerializeObject(this._thumbnailConfiguration, Formatting.Indented);
string filename = this.GetConfigFileName();
// Save master configuration (contains ProfileReadOnly and CurrentProfile)
this.SaveMasterConfig();
// If profile is read-only, don't save to the profile file
if (!this._thumbnailConfiguration.ProfileReadOnly) {
this.SaveProfileConfig();
}
}
private void SaveMasterConfig() {
string masterConfigPath = this.GetMasterConfigPath();
string directory = Path.GetDirectoryName(masterConfigPath);
if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory)) {
Directory.CreateDirectory(directory);
}
// Create a minimal master config with only ProfileReadOnly and CurrentProfile
// AvailableProfiles is determined by scanning the folder, not saved here
var masterConfig = new {
CurrentProfile = this._thumbnailConfiguration.CurrentProfile ?? "Default",
ProfileReadOnly = this._thumbnailConfiguration.ProfileReadOnly
};
string rawData = JsonConvert.SerializeObject(masterConfig, Formatting.Indented);
try {
// Ensure the directory exists before saving
string directory = Path.GetDirectoryName(filename);
if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory)) {
Directory.CreateDirectory(directory);
}
File.WriteAllText(filename, rawData);
File.WriteAllText(masterConfigPath, rawData);
} catch (IOException) {
// Ignore error if for some reason the updated config cannot be written down
}
}
private void SaveProfileConfig() {
string profileConfigPath = this.GetProfileConfigPath();
string directory = Path.GetDirectoryName(profileConfigPath);
if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory)) {
Directory.CreateDirectory(directory);
}
// Serialize the full configuration to the profile file
string rawData = JsonConvert.SerializeObject(this._thumbnailConfiguration, Formatting.Indented);
try {
File.WriteAllText(profileConfigPath, rawData);
} catch (IOException) {
// Ignore error if for some reason the updated config cannot be written down
}
}
private string GetMasterConfigPath() {
return Path.Combine(PROFILES_DIRECTORY, MASTER_CONFIG_FILE);
}
private string GetProfileConfigPath() {
return this.GetProfileConfigPath(this._thumbnailConfiguration.CurrentProfile ?? "Default");
}
private string GetProfileConfigPath(string profileName) {
string sanitizedProfileName = this.SanitizeFileName(profileName);
return Path.Combine(PROFILES_DIRECTORY, $"{sanitizedProfileName}.json");
}
private string GetConfigFileName() {
// If a custom config file is specified via command line, use it
if (!string.IsNullOrEmpty(this._appConfig.ConfigFileName)) {
return this._appConfig.ConfigFileName;
return string.IsNullOrEmpty(this._appConfig.ConfigFileName) ? ConfigurationStorage.CONFIGURATION_FILE_NAME
: this._appConfig.ConfigFileName;
}
private string SanitizeFileName(string fileName) {
string invalidChars = new string(Path.GetInvalidFileNameChars()) + new string(Path.GetInvalidPathChars());
foreach (char c in invalidChars) {
fileName = fileName.Replace(c, '_');
}
return fileName;
}
private List<string> ScanAvailableProfiles() {
List<string> profiles = new List<string>();
if (!Directory.Exists(PROFILES_DIRECTORY)) {
profiles.Add("Default");
return profiles;
}
// If using the Default profile or no profile is set, use the main config file
if (string.IsNullOrEmpty(this._currentProfile) || this._currentProfile == "Default") {
return Path.Combine(this._baseDirectory, CONFIGURATION_FILE_NAME);
try {
foreach (string filePath in Directory.GetFiles(PROFILES_DIRECTORY, "*.json")) {
string fileName = Path.GetFileNameWithoutExtension(filePath);
// Skip the master config file
if (fileName.Equals(MASTER_CONFIG_FILE.Replace(".json", ""), StringComparison.OrdinalIgnoreCase)) {
continue;
}
// Add the profile name
if (!string.IsNullOrEmpty(fileName) && !profiles.Contains(fileName)) {
profiles.Add(fileName);
}
}
} catch (IOException) {
// If there's an error scanning the directory, just return what we have
}
// For named profiles, use the profile-specific config file in the Profiles folder
string profilesFolder = Path.Combine(this._baseDirectory, PROFILES_FOLDER_NAME);
return Path.Combine(profilesFolder, this._currentProfile + PROFILE_CONFIG_FILE_EXTENSION);
// Ensure Default is always in the list
if (!profiles.Contains("Default")) {
profiles.Add("Default");
}
return profiles;
}
}
}
}

View File

@@ -1,285 +0,0 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Security.AccessControl;
using System.Security.Principal;
using System.Threading;
using System.Windows.Forms;
using Newtonsoft.Json;
namespace EveOPreview.Configuration.Implementation {
class ProfileManager : IProfileManager {
private const string PROFILES_FOLDER_NAME = "Profiles";
private const string MASTER_CONFIG_FILE_NAME = "EVE-O-Preview-Master.json";
private const string PROFILE_CONFIG_FILE_EXTENSION = ".json";
private const string PROFILE_LOCK_MUTEX_PREFIX = "EVE-O-Preview_ProfileLock_";
private readonly IAppConfig _appConfig;
private readonly IConfigurationStorage _configurationStorage;
private readonly string _baseDirectory;
private readonly Dictionary<string, Mutex> _acquiredLocks;
private Mutex _currentProfileLock;
public ProfileManager(IAppConfig appConfig, IConfigurationStorage configurationStorage) {
this._appConfig = appConfig;
this._configurationStorage = configurationStorage;
// Get the base directory (where the main config file is located)
// We'll use the application base directory
this._baseDirectory = AppDomain.CurrentDomain.BaseDirectory;
// Ensure Profiles folder exists
this.EnsureProfilesFolderExists();
// Ensure master config exists
this.EnsureMasterConfigExists();
this._acquiredLocks = new Dictionary<string, Mutex>();
this._currentProfileLock = null;
}
public System.Collections.Generic.List<string> GetAvailableProfiles() {
string profilesPath = this.GetProfilesFolderPath();
var profiles = new System.Collections.Generic.List<string> { "Default" };
if (Directory.Exists(profilesPath)) {
var profileFiles = Directory.GetFiles(profilesPath, "*" + PROFILE_CONFIG_FILE_EXTENSION);
foreach (var file in profileFiles) {
string profileName = Path.GetFileNameWithoutExtension(file);
if (!string.IsNullOrEmpty(profileName) && !profiles.Contains(profileName)) {
profiles.Add(profileName);
}
}
}
return profiles;
}
public string GetActiveProfile() {
string masterConfigPath = this.GetMasterConfigPath();
if (!File.Exists(masterConfigPath)) {
return "Default";
}
try {
string rawData = File.ReadAllText(masterConfigPath);
var masterConfig = JsonConvert.DeserializeObject<MasterConfig>(rawData);
return masterConfig?.ActiveProfile ?? "Default";
} catch {
return "Default";
}
}
public void SwitchProfile(string profileName) {
if (string.IsNullOrEmpty(profileName)) {
profileName = "Default";
}
// Check if the profile is locked by another instance
if (this.IsProfileLocked(profileName)) {
MessageBox.Show(
$"Cannot switch to profile '{profileName}' because it is already in use by another instance.",
"Profile In Use",
MessageBoxButtons.OK,
MessageBoxIcon.Warning
);
return;
}
// Get the current instance ID
int instanceId = global::EveOPreview.Program.InstanceId;
// Get the current profile for this instance
var profiles = this.GetProfilesList();
string currentProfile = instanceId >= 0 && instanceId < profiles.Count
? profiles[instanceId]
: "Default";
// Save current configuration first
this._configurationStorage.Save();
// Release current profile lock
this.ReleaseProfileLock(currentProfile);
// Acquire new profile lock
this.AcquireProfileLock(profileName);
// Update the Profiles array for this instance
this.UpdateProfileForInstance(instanceId, profileName);
// Restart the application
this.RestartApplication();
}
public string GetProfileConfigPath(string profileName) {
if (string.IsNullOrEmpty(profileName) || profileName == "Default") {
return null;
}
return Path.Combine(this.GetProfilesFolderPath(), profileName + PROFILE_CONFIG_FILE_EXTENSION);
}
private string GetProfilesFolderPath() {
return Path.Combine(this._baseDirectory, PROFILES_FOLDER_NAME);
}
private string GetMasterConfigPath() {
return Path.Combine(this._baseDirectory, MASTER_CONFIG_FILE_NAME);
}
private void EnsureProfilesFolderExists() {
string profilesPath = this.GetProfilesFolderPath();
if (!Directory.Exists(profilesPath)) {
Directory.CreateDirectory(profilesPath);
}
}
private void EnsureMasterConfigExists() {
string masterConfigPath = this.GetMasterConfigPath();
if (!File.Exists(masterConfigPath)) {
var availableProfiles = this.GetAvailableProfiles();
var defaultConfig = new MasterConfig {
ActiveProfile = "Default",
Profiles = availableProfiles
};
string rawData = JsonConvert.SerializeObject(defaultConfig, Formatting.Indented);
File.WriteAllText(masterConfigPath, rawData);
}
}
private void SaveActiveProfile(string profileName) {
// Read existing config to preserve Profiles array
var existingConfig = this.LoadMasterConfig();
if (existingConfig == null) {
existingConfig = new MasterConfig { Profiles = this.GetAvailableProfiles() };
}
existingConfig.ActiveProfile = profileName;
string rawData = JsonConvert.SerializeObject(existingConfig, Formatting.Indented);
string masterConfigPath = this.GetMasterConfigPath();
try {
File.WriteAllText(masterConfigPath, rawData);
} catch (IOException) {
// Ignore error if config cannot be written
}
}
private MasterConfig LoadMasterConfig() {
string masterConfigPath = this.GetMasterConfigPath();
if (!File.Exists(masterConfigPath)) {
return null;
}
try {
string rawData = File.ReadAllText(masterConfigPath);
return JsonConvert.DeserializeObject<MasterConfig>(rawData);
} catch {
return null;
}
}
private void RestartApplication() {
// Call the static method in Program which properly handles the mutex
Program.RestartApplication();
}
public List<string> GetProfilesList() {
var masterConfig = this.LoadMasterConfig();
if (masterConfig?.Profiles != null && masterConfig.Profiles.Count > 0) {
return masterConfig.Profiles;
}
return this.GetAvailableProfiles();
}
public string GetProfileForInstance(int instanceId) {
var profiles = this.GetProfilesList();
if (instanceId >= 0 && instanceId < profiles.Count) {
return profiles[instanceId];
}
return "Default";
}
public bool IsProfileLocked(string profileName) {
if (string.IsNullOrEmpty(profileName) || profileName == "Default") {
return false;
}
string mutexName = PROFILE_LOCK_MUTEX_PREFIX + profileName;
try {
Mutex.OpenExisting(mutexName);
return true;
} catch (UnauthorizedAccessException) {
return true;
} catch {
return false;
}
}
public bool AcquireProfileLock(string profileName) {
if (string.IsNullOrEmpty(profileName) || profileName == "Default") {
return true;
}
string mutexName = PROFILE_LOCK_MUTEX_PREFIX + profileName;
try {
var mutex = new Mutex(true, mutexName);
this._currentProfileLock = mutex;
return true;
} catch {
return false;
}
}
public void ReleaseProfileLock(string profileName) {
if (this._currentProfileLock != null) {
this._currentProfileLock.ReleaseMutex();
this._currentProfileLock.Dispose();
this._currentProfileLock = null;
}
}
private void UpdateProfileForInstance(int instanceId, string profileName) {
var masterConfig = this.LoadMasterConfig();
if (masterConfig == null) {
masterConfig = new MasterConfig { Profiles = this.GetAvailableProfiles() };
}
// Ensure the Profiles array is large enough
if (masterConfig.Profiles == null) {
masterConfig.Profiles = this.GetAvailableProfiles();
}
// Expand the array if necessary
while (masterConfig.Profiles.Count <= instanceId) {
masterConfig.Profiles.Add("Default");
}
// Update the profile for this instance
masterConfig.Profiles[instanceId] = profileName;
// Also update ActiveProfile for backward compatibility
masterConfig.ActiveProfile = profileName;
// Save the updated config
string rawData = JsonConvert.SerializeObject(masterConfig, Formatting.Indented);
string masterConfigPath = this.GetMasterConfigPath();
try {
File.WriteAllText(masterConfigPath, rawData);
} catch (IOException) {
// Ignore error if config cannot be written
}
}
private class MasterConfig {
public string ActiveProfile { get; set; }
public List<string> Profiles { get; set; }
}
}
}

View File

@@ -120,6 +120,7 @@ namespace EveOPreview.Configuration.Implementation {
this.CurrentProfile = "Default";
this.AvailableProfiles = new List<string> { "Default" };
this.ProfileReadOnly = false;
this.IconName = "";
@@ -304,7 +305,10 @@ namespace EveOPreview.Configuration.Implementation {
public Rectangle DefaultThumbnailRegion { get; set; }
public string CurrentProfile { get; set; }
[JsonIgnore]
public List<string> AvailableProfiles { get; set; }
[JsonProperty("ProfileReadOnly")]
public bool ProfileReadOnly { get; set; }
[JsonProperty("IconName")]
public string IconName {
get; set;

View File

@@ -1,50 +0,0 @@
using System.Collections.Generic;
namespace EveOPreview.Configuration {
public interface IProfileManager {
/// <summary>
/// Gets the list of available profile names from the Profiles folder
/// </summary>
List<string> GetAvailableProfiles();
/// <summary>
/// Gets the currently active profile name from the master config
/// </summary>
string GetActiveProfile();
/// <summary>
/// Switches to the specified profile, saves current config, updates master config, and restarts the application
/// </summary>
void SwitchProfile(string profileName);
/// <summary>
/// Gets the full path to a profile's config file
/// </summary>
string GetProfileConfigPath(string profileName);
/// <summary>
/// Gets the list of profiles from the master config array
/// </summary>
List<string> GetProfilesList();
/// <summary>
/// Gets the profile name for the specified instance ID
/// </summary>
string GetProfileForInstance(int instanceId);
/// <summary>
/// Checks if a profile is currently locked by another instance
/// </summary>
bool IsProfileLocked(string profileName);
/// <summary>
/// Acquires a lock on the specified profile for the current instance
/// </summary>
bool AcquireProfileLock(string profileName);
/// <summary>
/// Releases the lock on the specified profile
/// </summary>
void ReleaseProfileLock(string profileName);
}
}

View File

@@ -81,6 +81,7 @@ namespace EveOPreview.Configuration {
string CurrentProfile { get; set; }
List<string> AvailableProfiles { get; set; }
bool ProfileReadOnly { get; set; }
string IconName { get; set; }

View File

@@ -18,7 +18,6 @@ namespace EveOPreview.Presenters {
private readonly IMediator _mediator;
private readonly IThumbnailConfiguration _configuration;
private readonly IConfigurationStorage _configurationStorage;
private readonly IProfileManager _profileManager;
private readonly IDictionary<string, IThumbnailDescription> _descriptionsCache;
private bool _suppressSizeNotifications;
@@ -26,13 +25,11 @@ namespace EveOPreview.Presenters {
#endregion
public MainFormPresenter(IApplicationController controller, IMainFormView view, IMediator mediator,
IThumbnailConfiguration configuration, IConfigurationStorage configurationStorage,
IProfileManager profileManager)
IThumbnailConfiguration configuration, IConfigurationStorage configurationStorage)
: base(controller, view) {
this._mediator = mediator;
this._configuration = configuration;
this._configurationStorage = configurationStorage;
this._profileManager = profileManager;
this._descriptionsCache = new Dictionary<string, IThumbnailDescription>();
@@ -47,7 +44,6 @@ namespace EveOPreview.Presenters {
this.View.ThumbnailStateChanged = this.UpdateThumbnailState;
this.View.DocumentationLinkActivated = this.OpenDocumentationLink;
this.View.ApplicationExitRequested = this.ExitApplication;
this.View.ProfileSwitchRequested = this.SwitchProfile;
this.View.IconName = this._configuration.IconName;
}
@@ -61,23 +57,10 @@ namespace EveOPreview.Presenters {
this.View.Minimize();
}
// Initialize the tray profile menu
this.UpdateTrayProfileMenu();
this._mediator.Send(new StartService());
this._suppressSizeNotifications = false;
}
private void UpdateTrayProfileMenu() {
var profiles = this._profileManager.GetAvailableProfiles();
var currentProfile = this._profileManager.GetActiveProfile();
this.View.UpdateTrayProfileMenu(profiles, currentProfile);
}
private void SwitchProfile(string profileName) {
this._profileManager.SwitchProfile(profileName);
}
private void Minimize() {
if (!this._configuration.MinimizeToTray) {
return;
@@ -149,6 +132,7 @@ namespace EveOPreview.Presenters {
this.View.AvailableProfiles = this._configuration.AvailableProfiles;
this.View.CurrentProfile = this._configuration.CurrentProfile;
this.View.ProfileReadOnly = this._configuration.ProfileReadOnly;
this.View.ToggleTrackingHotkey = this._configuration.ToggleTrackingHotkey;
this.View.ToggleSingleProcessHotkey = this._configuration.ToggleSingleProcessHotkey;
@@ -201,6 +185,7 @@ namespace EveOPreview.Presenters {
this._configuration.AvailableProfiles = this.View.AvailableProfiles;
this._configuration.CurrentProfile = this.View.CurrentProfile;
this._configuration.ProfileReadOnly = this.View.ProfileReadOnly;
this._configuration.ToggleTrackingHotkey = this.View.ToggleTrackingHotkey;
this._configuration.ToggleSingleProcessHotkey = this.View.ToggleSingleProcessHotkey;

View File

@@ -1,9 +1,7 @@
using System;
using System.Linq;
using System.Threading;
using System.Windows.Forms;
using EveOPreview.Configuration;
using EveOPreview.Configuration.Implementation;
using EveOPreview.Presenters;
using EveOPreview.Services;
using EveOPreview.View;
@@ -11,27 +9,17 @@ using MediatR;
namespace EveOPreview {
static class Program {
private const string MUTEX_PREFIX = "EVE-O-Preview_Instance_";
private const string INSTANCE_ID_ARG = "--instance-id=";
private static string MUTEX_NAME = "EVE-O-Preview Single Instance Mutex";
private static Mutex _singleInstanceMutex;
private static int _instanceId = 0;
/// <summary>The main entry point for the application.</summary>
[STAThread]
static void Main(string[] args) {
// Parse instance ID from command line arguments, or auto-detect next available
Program._instanceId = ParseInstanceId(args);
// If no instance ID was provided, find the next available one
if (Program._instanceId == 0 && !HasInstanceIdArgument(args)) {
Program._instanceId = FindNextAvailableInstanceId();
}
static void Main() {
// The very usual Mutex-based single-instance screening
// 'token' variable is used to store reference to the instance Mutex
// during the app lifetime
Program._singleInstanceMutex = Program.GetInstanceToken(Program._instanceId);
Program._singleInstanceMutex = Program.GetInstanceToken();
// If it was not possible to acquire the app token then another app instance is already running
// Nothing to do here
@@ -48,57 +36,20 @@ namespace EveOPreview {
controller.Run<MainFormPresenter>();
}
private static bool HasInstanceIdArgument(string[] args) {
if (args == null || args.Length == 0) {
return false;
}
return args.Any(a => a.StartsWith(INSTANCE_ID_ARG));
}
private static int ParseInstanceId(string[] args) {
if (args == null || args.Length == 0) {
return 0;
}
var arg = args.FirstOrDefault(a => a.StartsWith(INSTANCE_ID_ARG));
if (arg != null && int.TryParse(arg.Substring(INSTANCE_ID_ARG.Length), out int id)) {
return id;
}
return 0;
}
private static int FindNextAvailableInstanceId() {
// Try instance IDs starting from 0, find the first one that's not in use
for (int id = 0; id < 10; id++) {
string mutexName = MUTEX_PREFIX + id;
try {
Mutex.OpenExisting(mutexName);
// This instance ID is taken, try next
} catch {
// This instance ID is available
return id;
}
}
return 0; // Fallback to 0 if all are taken
}
private static Mutex GetInstanceToken(int instanceId) {
string mutexName = MUTEX_PREFIX + instanceId;
private static Mutex GetInstanceToken() {
// The code might look overcomplicated here for a single Mutex operation
// Yet we had already experienced a Windows-level issue
// where .NET finalizer thread was literally paralyzed by
// a failed Mutex operation. That did lead to weird OutOfMemory
// exceptions later
try {
Mutex.OpenExisting(mutexName);
Mutex.OpenExisting(Program.MUTEX_NAME);
// if that didn't fail then another instance is already running
return null;
} catch (UnauthorizedAccessException) {
return null;
} catch (Exception) {
Mutex token = new Mutex(true, mutexName, out var result);
Mutex token = new Mutex(true, Program.MUTEX_NAME, out var result);
return result ? token : null;
}
}
@@ -127,13 +78,9 @@ namespace EveOPreview {
container.Register(typeof(IRequestHandler<, >), typeof(Program).Assembly);
// Configuration services
container.Register<IConfigurationStorage>();
container.Register<IAppConfig>();
container.Register<IThumbnailConfiguration>();
container.Register<IConfigurationStorage>();
container.Register<IProfileManager>();
// Initialize profile system
Program.InitializeProfileSystem(container);
// Application services
container.Register<IThumbnailManager>();
@@ -151,58 +98,5 @@ namespace EveOPreview {
return controller;
}
private static void InitializeProfileSystem(IIocContainer container) {
var profileManager = container.Resolve<IProfileManager>();
var configurationStorage = container.Resolve<IConfigurationStorage>();
var thumbnailConfiguration = container.Resolve<IThumbnailConfiguration>();
// Get the active profile for this instance based on instance ID
string activeProfile = profileManager.GetProfileForInstance(Program._instanceId);
// Acquire the profile lock for this instance
profileManager.AcquireProfileLock(activeProfile);
// Set the current profile in the configuration storage
if (configurationStorage is ConfigurationStorage configStorage) {
configStorage.CurrentProfile = activeProfile;
}
// Update the thumbnail configuration with the active profile
thumbnailConfiguration.CurrentProfile = activeProfile;
thumbnailConfiguration.AvailableProfiles = profileManager.GetAvailableProfiles();
}
public static void RestartApplication() {
// Get the current executable path
string executablePath = System.Diagnostics.Process.GetCurrentProcess().MainModule?.FileName;
if (string.IsNullOrEmpty(executablePath)) {
return;
}
// Release the mutex first so the new instance can start
Program._singleInstanceMutex?.Dispose();
Program._singleInstanceMutex = null;
// Small delay to ensure the mutex is fully released
System.Threading.Thread.Sleep(100);
// Start a new instance of the application with the same instance ID
try {
System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo {
FileName = executablePath,
Arguments = INSTANCE_ID_ARG + Program._instanceId,
UseShellExecute = true
});
} catch (Exception) {
// If starting the new process fails, we're in a bad state
// Just exit anyway
}
// Exit the current application immediately
Environment.Exit(0);
}
public static int InstanceId => Program._instanceId;
}
}

View File

@@ -4,7 +4,6 @@ using System.ComponentModel;
using System.Drawing;
using System.Linq;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Windows.Threading;
@@ -55,11 +54,6 @@ namespace EveOPreview.Services {
private HotkeyHandler _toggleTrackingHotkey;
private HotkeyHandler _toggleSingleProcessHotkey;
private HotkeyHandler _toggleAllThumbnailsHotkey;
private EventWaitHandle _toggleEvent;
private Thread _toggleEventThread;
private SynchronizationContext _syncContext;
private const string TOGGLE_EVENT_NAME = "EVE-O-Preview_ToggleEvent";
private bool _isToggleInitiator;
#endregion
public ThumbnailManager(IMediator mediator, IThumbnailConfiguration configuration,
@@ -82,9 +76,6 @@ namespace EveOPreview.Services {
this._thumbnailViews = new Dictionary<IntPtr, IThumbnailView>();
// Capture the current synchronization context (UI thread)
this._syncContext = SynchronizationContext.Current;
// DispatcherTimer setup
this._thumbnailUpdateTimer = new DispatcherTimer();
this._thumbnailUpdateTimer.Tick += ThumbnailUpdateTimerTick;
@@ -185,34 +176,15 @@ namespace EveOPreview.Services {
var toggleAllThumbnailsKey = this._configuration.StringToKey(this._configuration.ToggleAllThumbnailsHotkey);
this._toggleAllThumbnailsHotkey = new HotkeyHandler(mainHandle, toggleAllThumbnailsKey);
this._toggleAllThumbnailsHotkey.Pressed += (object s, HandledEventArgs e) => {
// Mark this instance as the initiator so we don't process our own event
this._isToggleInitiator = true;
this._configuration.ThumbnailsManuallyHidden = !this._configuration.ThumbnailsManuallyHidden;
this._hideThumbnailsDelay = 0;
this.RefreshThumbnails();
// Signal all other instances to toggle as well
this._toggleEvent?.Set();
// Reset the event after a short delay so other instances can process it
// Then clear the initiator flag
System.Threading.Timer resetTimer = null;
resetTimer = new System.Threading.Timer(_ => {
this._toggleEvent?.Reset();
this._isToggleInitiator = false;
resetTimer?.Dispose();
}, null, 50, System.Threading.Timeout.Infinite);
System.Diagnostics.Debug.WriteLine(
$"Toggled all thumbnails: {(this._configuration.ThumbnailsManuallyHidden ? "Hidden" : "Visible")}");
e.Handled = true;
};
registered = this._toggleAllThumbnailsHotkey.Register();
System.Diagnostics.Debug.WriteLine($"Toggle all thumbnails hotkey registration result: {registered}");
// Initialize the toggle event for multi-instance synchronization
this.InitializeToggleEvent();
}
public void ReRegisterHotkeys() {
@@ -285,25 +257,9 @@ namespace EveOPreview.Services {
var toggleAllThumbnailsKey = this._configuration.StringToKey(this._configuration.ToggleAllThumbnailsHotkey);
this._toggleAllThumbnailsHotkey = new HotkeyHandler(mainHandle, toggleAllThumbnailsKey);
this._toggleAllThumbnailsHotkey.Pressed += (object s, HandledEventArgs e) => {
// Mark this instance as the initiator so we don't process our own event
this._isToggleInitiator = true;
this._configuration.ThumbnailsManuallyHidden = !this._configuration.ThumbnailsManuallyHidden;
this._hideThumbnailsDelay = 0;
this.RefreshThumbnails();
// Signal all other instances to toggle as well
this._toggleEvent?.Set();
// Reset the event after a short delay so other instances can process it
// Then clear the initiator flag
System.Threading.Timer resetTimer = null;
resetTimer = new System.Threading.Timer(_ => {
this._toggleEvent?.Reset();
this._isToggleInitiator = false;
resetTimer?.Dispose();
}, null, 50, System.Threading.Timeout.Infinite);
System.Diagnostics.Debug.WriteLine(
$"Toggled all thumbnails: {(this._configuration.ThumbnailsManuallyHidden ? "Hidden" : "Visible")}");
e.Handled = true;
@@ -311,62 +267,6 @@ namespace EveOPreview.Services {
this._toggleAllThumbnailsHotkey.Register();
}
private void InitializeToggleEvent() {
bool created;
try {
// Create or open the named event - use ManualReset to wake all instances
this._toggleEvent = new EventWaitHandle(false, EventResetMode.ManualReset, TOGGLE_EVENT_NAME, out created);
if (!created) {
// Event already exists, use it
this._toggleEvent = EventWaitHandle.OpenExisting(TOGGLE_EVENT_NAME);
}
} catch {
// Fallback: create a new one
this._toggleEvent = new EventWaitHandle(false, EventResetMode.ManualReset, TOGGLE_EVENT_NAME);
}
// Start a thread to listen for toggle events from other instances
this._toggleEventThread = new Thread(this.ToggleEventListener) {
IsBackground = true,
Name = "ToggleEventListener"
};
this._toggleEventThread.Start();
}
private void ToggleEventListener() {
while (true) {
try {
// Wait for the toggle event
this._toggleEvent?.WaitOne();
// Reset the event so we can wait again
this._toggleEvent?.Reset();
// Skip processing if we initiated this toggle
// Check inside the UI thread callback to avoid race conditions
this._syncContext?.Post(_ => {
// Double-check the flag on the UI thread after a small delay
// to ensure the initiator's flag is still set
System.Threading.Thread.Sleep(10);
if (this._isToggleInitiator) {
return; // Skip - we initiated this toggle
}
this._configuration.ThumbnailsManuallyHidden = !this._configuration.ThumbnailsManuallyHidden;
this._hideThumbnailsDelay = 0;
this.RefreshThumbnails();
System.Diagnostics.Debug.WriteLine(
$"Received toggle event: {(this._configuration.ThumbnailsManuallyHidden ? "Hidden" : "Visible")}");
}, null);
} catch (ThreadAbortException) {
// Thread is being aborted, exit gracefully
break;
} catch {
// Ignore other errors and continue listening
}
}
}
public IThumbnailView GetClientByTitle(string title) {
return _thumbnailViews.FirstOrDefault(x => x.Value.Title == title).Value;
}

View File

@@ -27,10 +27,13 @@ namespace EveOPreview.View {
/// </summary>
private void InitializeComponent() {
components = new System.ComponentModel.Container();
ToolStripMenuItem RestoreWindowMenuItem;
ToolStripMenuItem ExitMenuItem;
ToolStripMenuItem TitleMenuItem;
ToolStripSeparator SeparatorMenuItem;
RestoreWindowMenuItem = new ToolStripMenuItem();
ExitMenuItem = new ToolStripMenuItem();
TitleMenuItem = new ToolStripMenuItem();
SeparatorMenuItem = new ToolStripSeparator();
ProfileSeparatorMenuItem = new ToolStripSeparator();
ProfilesMenuItem = new ToolStripMenuItem();
ProfileReadOnlyMenuItem = new ToolStripMenuItem();
TabControl ContentTabControl;
TabPage GeneralTabPage;
Panel GeneralSettingsPanel;
@@ -82,8 +85,7 @@ namespace EveOPreview.View {
PickRegionButton = new Button();
ProfileLabel = new Label();
ProfileComboBox = new ComboBox();
SaveProfileButton = new Button();
DeleteProfileButton = new Button();
ProfileReadOnlyCheckBox = new CheckBox();
ThumbnailSnapRangeLabel = new Label();
ThumbnailSnapRangeNumericEdit = new NumericUpDown();
ThumbnailSnapToGridCheckBox = new CheckBox();
@@ -133,10 +135,6 @@ namespace EveOPreview.View {
DocumentationLink = new LinkLabel();
NotifyIcon = new NotifyIcon(components);
TrayMenu = new ContextMenuStrip(components);
RestoreWindowMenuItem = new ToolStripMenuItem();
ExitMenuItem = new ToolStripMenuItem();
TitleMenuItem = new ToolStripMenuItem();
SeparatorMenuItem = new ToolStripSeparator();
ContentTabControl = new TabControl();
GeneralTabPage = new TabPage();
GeneralSettingsPanel = new Panel();
@@ -216,6 +214,26 @@ namespace EveOPreview.View {
SeparatorMenuItem.Name = "SeparatorMenuItem";
SeparatorMenuItem.Size = new Size(150, 6);
//
// ProfileSeparatorMenuItem
//
ProfileSeparatorMenuItem.Name = "ProfileSeparatorMenuItem";
ProfileSeparatorMenuItem.Size = new Size(150, 6);
//
// ProfilesMenuItem
//
ProfilesMenuItem.DropDownItems.Clear();
ProfilesMenuItem.Name = "ProfilesMenuItem";
ProfilesMenuItem.Size = new Size(153, 22);
ProfilesMenuItem.Text = "Profiles";
//
// ProfileReadOnlyMenuItem
//
ProfileReadOnlyMenuItem.CheckOnClick = true;
ProfileReadOnlyMenuItem.Name = "ProfileReadOnlyMenuItem";
ProfileReadOnlyMenuItem.Size = new Size(153, 22);
ProfileReadOnlyMenuItem.Text = "Read Only";
ProfileReadOnlyMenuItem.Click += ProfileReadOnlyMenuItem_Click;
//
// ContentTabControl
//
ContentTabControl.Alignment = TabAlignment.Left;
@@ -474,8 +492,7 @@ namespace EveOPreview.View {
ThumbnailSettingsPanel.Controls.Add(PickRegionButton);
ThumbnailSettingsPanel.Controls.Add(ProfileLabel);
ThumbnailSettingsPanel.Controls.Add(ProfileComboBox);
ThumbnailSettingsPanel.Controls.Add(SaveProfileButton);
ThumbnailSettingsPanel.Controls.Add(DeleteProfileButton);
ThumbnailSettingsPanel.Controls.Add(ProfileReadOnlyCheckBox);
ThumbnailSettingsPanel.Controls.Add(ThumbnailSnapRangeLabel);
ThumbnailSettingsPanel.Controls.Add(ThumbnailSnapRangeNumericEdit);
ThumbnailSettingsPanel.Controls.Add(ThumbnailSnapToGridCheckBox);
@@ -636,27 +653,16 @@ namespace EveOPreview.View {
ProfileComboBox.TabIndex = 51;
ProfileComboBox.SelectedIndexChanged += ProfileComboBox_SelectedIndexChanged;
//
// SaveProfileButton
// ProfileReadOnlyCheckBox
//
SaveProfileButton.Location = new Point(179, 277);
SaveProfileButton.Margin = new Padding(4);
SaveProfileButton.Name = "SaveProfileButton";
SaveProfileButton.Size = new Size(56, 21);
SaveProfileButton.TabIndex = 52;
SaveProfileButton.Text = "Save";
SaveProfileButton.UseVisualStyleBackColor = true;
SaveProfileButton.Click += SaveProfileButton_Click;
//
// DeleteProfileButton
//
DeleteProfileButton.Location = new Point(243, 277);
DeleteProfileButton.Margin = new Padding(4);
DeleteProfileButton.Name = "DeleteProfileButton";
DeleteProfileButton.Size = new Size(56, 21);
DeleteProfileButton.TabIndex = 53;
DeleteProfileButton.Text = "Delete";
DeleteProfileButton.UseVisualStyleBackColor = true;
DeleteProfileButton.Click += DeleteProfileButton_Click;
ProfileReadOnlyCheckBox.AutoSize = true;
ProfileReadOnlyCheckBox.Location = new Point(310, 278);
ProfileReadOnlyCheckBox.Margin = new Padding(4);
ProfileReadOnlyCheckBox.Name = "ProfileReadOnlyCheckBox";
ProfileReadOnlyCheckBox.Size = new Size(107, 19);
ProfileReadOnlyCheckBox.TabIndex = 54;
ProfileReadOnlyCheckBox.Text = "Read Only";
ProfileReadOnlyCheckBox.UseVisualStyleBackColor = true;
//
// ThumbnailSnapRangeLabel
//
@@ -1467,7 +1473,7 @@ namespace EveOPreview.View {
//
TrayMenu.ImageScalingSize = new Size(24, 24);
TrayMenu.Items.AddRange(
new ToolStripItem[] { TitleMenuItem, RestoreWindowMenuItem, SeparatorMenuItem, ExitMenuItem });
new ToolStripItem[] { TitleMenuItem, RestoreWindowMenuItem, SeparatorMenuItem, ProfileSeparatorMenuItem, ProfilesMenuItem, ProfileReadOnlyMenuItem, ExitMenuItem });
TrayMenu.Name = "contextMenuStrip1";
TrayMenu.Size = new Size(154, 76);
//
@@ -1530,6 +1536,13 @@ namespace EveOPreview.View {
#endregion
private NotifyIcon NotifyIcon;
private ContextMenuStrip TrayMenu;
private ToolStripMenuItem TitleMenuItem;
private ToolStripMenuItem RestoreWindowMenuItem;
private ToolStripMenuItem ExitMenuItem;
private ToolStripMenuItem ProfilesMenuItem;
private ToolStripMenuItem ProfileReadOnlyMenuItem;
private ToolStripSeparator SeparatorMenuItem;
private ToolStripSeparator ProfileSeparatorMenuItem;
private TabPage ZoomTabPage;
private CheckBox EnableClientLayoutTrackingCheckBox;
private CheckBox HideActiveClientThumbnailCheckBox;
@@ -1578,8 +1591,7 @@ namespace EveOPreview.View {
private Label RegionHeightLabel;
private Button PickRegionButton;
private ComboBox ProfileComboBox;
private Button SaveProfileButton;
private Button DeleteProfileButton;
private CheckBox ProfileReadOnlyCheckBox;
private Label ProfileLabel;
private Label ThumbnailSnapRangeLabel;
private NumericUpDown ThumbnailSnapRangeNumericEdit;

View File

@@ -21,6 +21,7 @@ namespace EveOPreview.View {
private Size _minimumSize;
private Size _maximumSize;
private string _iconName;
private string _lastSetProfile;
#endregion
public MainForm(ApplicationContext context) {
@@ -45,9 +46,6 @@ namespace EveOPreview.View {
// Add mouse wheel event handlers for aspect ratio maintenance
this.ThumbnailsWidthNumericEdit.MouseWheel += ThumbnailSizeNumeric_MouseWheel;
this.ThumbnailsHeightNumericEdit.MouseWheel += ThumbnailSizeNumeric_MouseWheel;
// Set tray icon text with instance ID
this.SetTrayIconText();
}
public bool MinimizeToTray {
@@ -305,15 +303,6 @@ namespace EveOPreview.View {
}
}
public string CurrentProfile {
get => this.ProfileComboBox.Text;
set {
if (this.ProfileComboBox.Items.Contains(value)) {
this.ProfileComboBox.SelectedItem = value;
}
}
}
public List<string> AvailableProfiles {
get => this.ProfileComboBox.Items.Cast<string>().ToList();
set {
@@ -321,36 +310,85 @@ namespace EveOPreview.View {
foreach (var profile in value) {
this.ProfileComboBox.Items.Add(profile);
}
this.UpdateTrayProfilesMenu(value);
}
}
public bool ProfileReadOnly {
get => this.ProfileReadOnlyCheckBox.Checked;
set {
this.ProfileReadOnlyCheckBox.Checked = value;
this.ProfileReadOnlyMenuItem.Checked = value;
}
}
public string CurrentProfile {
get => this.ProfileComboBox.Text;
set {
this._lastSetProfile = value;
if (this.ProfileComboBox.Items.Contains(value)) {
this.ProfileComboBox.SelectedItem = value;
}
this.UpdateTrayProfilesMenuCheckedState();
}
}
private void ProfileComboBox_SelectedIndexChanged(object sender, EventArgs e) {
if (!this._suppressEvents) {
this.ApplicationSettingsChanged?.Invoke();
if (!this._suppressEvents && this.ProfileComboBox.SelectedItem != null) {
string newProfile = this.ProfileComboBox.SelectedItem.ToString();
// Only restart if this is a user-initiated change (not during initial load)
if (newProfile != this._lastSetProfile) {
this._lastSetProfile = newProfile;
// Save ONLY the master config, not the profile file
// This prevents corrupting the target profile with current settings
SaveMasterOnly();
Application.Restart();
Environment.Exit(0);
}
}
}
private void SaveProfileButton_Click(object sender, EventArgs e) {
using (var inputDialog = new InputDialog("Save Profile", "Enter profile name:")) {
if (inputDialog.ShowDialog() == DialogResult.OK) {
string profileName = inputDialog.InputText;
if (!string.IsNullOrWhiteSpace(profileName)) {
this.ProfileComboBox.Items.Add(profileName);
this.ProfileComboBox.SelectedItem = profileName;
private void SaveMasterOnly() {
// Set to read-only temporarily so Save() only saves master config
bool originalReadOnly = this.ProfileReadOnlyCheckBox.Checked;
this.ProfileReadOnlyCheckBox.Checked = true;
this.ApplicationSettingsChanged?.Invoke();
this.ProfileReadOnlyCheckBox.Checked = originalReadOnly;
}
private void UpdateTrayProfilesMenu(List<string> profiles) {
this._suppressEvents = true;
this.ProfilesMenuItem.DropDownItems.Clear();
foreach (string profile in profiles) {
var menuItem = new ToolStripMenuItem(profile);
if (profile == this.CurrentProfile) {
menuItem.Checked = true;
}
menuItem.Click += (sender, e) => {
if (!this._suppressEvents) {
this.CurrentProfile = profile;
// Save ONLY the master config, not the profile file
bool originalReadOnly = this.ProfileReadOnlyCheckBox.Checked;
this.ProfileReadOnlyCheckBox.Checked = true;
this.ApplicationSettingsChanged?.Invoke();
this.ProfileReadOnlyCheckBox.Checked = originalReadOnly;
Application.Restart();
Environment.Exit(0);
}
}
};
this.ProfilesMenuItem.DropDownItems.Add(menuItem);
}
this._suppressEvents = false;
}
private void DeleteProfileButton_Click(object sender, EventArgs e) {
if (this.ProfileComboBox.SelectedItem != null) {
string selectedProfile = this.ProfileComboBox.SelectedItem.ToString();
if (selectedProfile != "Default") {
this.ProfileComboBox.Items.Remove(selectedProfile);
this.ApplicationSettingsChanged?.Invoke();
}
private void UpdateTrayProfilesMenuCheckedState() {
this._suppressEvents = true;
foreach (ToolStripMenuItem item in this.ProfilesMenuItem.DropDownItems) {
item.Checked = (item.Text == this.CurrentProfile);
}
this._suppressEvents = false;
}
public new void Show() {// Registers the current instance as the application's Main Form
@@ -422,71 +460,6 @@ namespace EveOPreview.View {
public Action DocumentationLinkActivated { get; set; }
public Action<string> ProfileSwitchRequested { get; set; }
public void UpdateTrayProfileMenu(List<string> profiles, string currentProfile) {
// Clear existing items from the tray menu
this.TrayMenu.Items.Clear();
// Add title
ToolStripMenuItem titleMenuItem = new ToolStripMenuItem();
titleMenuItem.Enabled = false;
titleMenuItem.Name = "TitleMenuItem";
titleMenuItem.Size = new System.Drawing.Size(200, 22);
titleMenuItem.Text = "EVE-O-Preview";
this.TrayMenu.Items.Add(titleMenuItem);
// Add restore option
ToolStripMenuItem restoreMenuItem = new ToolStripMenuItem();
restoreMenuItem.Name = "RestoreWindowMenuItem";
restoreMenuItem.Size = new System.Drawing.Size(200, 22);
restoreMenuItem.Text = "Restore";
restoreMenuItem.Click += this.RestoreMainForm_Handler;
this.TrayMenu.Items.Add(restoreMenuItem);
// Add separator
ToolStripSeparator separatorMenuItem = new ToolStripSeparator();
separatorMenuItem.Name = "SeparatorMenuItem";
separatorMenuItem.Size = new System.Drawing.Size(200, 6);
this.TrayMenu.Items.Add(separatorMenuItem);
// Add profile submenu
ToolStripMenuItem profileMenuItem = new ToolStripMenuItem();
profileMenuItem.Name = "ProfileMenuItem";
profileMenuItem.Size = new System.Drawing.Size(200, 22);
profileMenuItem.Text = "Profiles";
foreach (var profile in profiles) {
ToolStripMenuItem profileItem = new ToolStripMenuItem();
profileItem.Name = "Profile_" + profile;
profileItem.Size = new System.Drawing.Size(180, 22);
profileItem.Text = profile;
profileItem.Checked = (profile == currentProfile);
profileItem.Click += (sender, e) => {
this.ProfileSwitchRequested?.Invoke(profile);
};
profileMenuItem.DropDownItems.Add(profileItem);
}
this.TrayMenu.Items.Add(profileMenuItem);
// Add final separator
ToolStripSeparator finalSeparatorMenuItem = new ToolStripSeparator();
finalSeparatorMenuItem.Name = "FinalSeparatorMenuItem";
finalSeparatorMenuItem.Size = new System.Drawing.Size(200, 6);
this.TrayMenu.Items.Add(finalSeparatorMenuItem);
// Add exit option
ToolStripMenuItem exitMenuItem = new ToolStripMenuItem();
exitMenuItem.Name = "ExitMenuItem";
exitMenuItem.Size = new System.Drawing.Size(200, 22);
exitMenuItem.Text = "Exit";
exitMenuItem.Click += this.ExitMenuItemClick_Handler;
this.TrayMenu.Items.Add(exitMenuItem);
}
#region UI events
private void ContentTabControl_DrawItem(object sender, DrawItemEventArgs e) {
TabControl control = (TabControl)sender;
@@ -700,6 +673,12 @@ namespace EveOPreview.View {
private void ExitMenuItemClick_Handler(object sender, EventArgs e) {
this.ApplicationExitRequested?.Invoke();
}
private void ProfileReadOnlyMenuItem_Click(object sender, EventArgs e) {
if (!this._suppressEvents) {
this.ApplicationSettingsChanged?.Invoke();
}
}
#endregion
private void InitZoomAnchorMap() {
@@ -738,12 +717,6 @@ namespace EveOPreview.View {
}
}
private void SetTrayIconText() {
// Get the instance ID from Program
int instanceId = EveOPreview.Program.InstanceId;
this.NotifyIcon.Text = $"EVE-O-Preview [{instanceId}]";
}
private void AnimationStyleCombo_SelectedIndexChanged(object sender, EventArgs e) {}
private void GeneralSettingsPanel_Paint(object sender, PaintEventArgs e) {}

View File

@@ -46,6 +46,7 @@ namespace EveOPreview.View {
string CurrentProfile { get; set; }
List<string> AvailableProfiles { get; set; }
bool ProfileReadOnly { get; set; }
string ToggleTrackingHotkey { get; set; }
string ToggleSingleProcessHotkey { get; set; }
@@ -63,9 +64,6 @@ namespace EveOPreview.View {
void RemoveThumbnails(IList<IThumbnailDescription> thumbnails);
void RefreshZoomSettings();
void UpdateTrayProfileMenu(List<string> profiles, string currentProfile);
Action<string> ProfileSwitchRequested { get; set; }
Action ApplicationExitRequested { get; set; }
Action FormActivated { get; set; }
Action FormMinimized { get; set; }

View File

@@ -1,91 +0,0 @@
{
"runtimeTarget": {
"name": ".NETCoreApp,Version=v8.0",
"signature": ""
},
"compilationOptions": {},
"targets": {
".NETCoreApp,Version=v8.0": {
"EVE-O-Preview/1.0.0": {
"dependencies": {
"LightInject": "7.0.1",
"MediatR": "9.0.0",
"Microsoft.CSharp": "4.7.0",
"Microsoft.NET.ILLink.Tasks": "8.0.8",
"Newtonsoft.Json": "13.0.3"
},
"runtime": {
"EVE-O-Preview.dll": {}
}
},
"LightInject/7.0.1": {
"runtime": {
"lib/net8.0/LightInject.dll": {
"assemblyVersion": "7.0.1.0",
"fileVersion": "7.0.1.0"
}
}
},
"MediatR/9.0.0": {
"runtime": {
"lib/netstandard2.1/MediatR.dll": {
"assemblyVersion": "9.0.0.0",
"fileVersion": "9.0.0.0"
}
}
},
"Microsoft.CSharp/4.7.0": {},
"Microsoft.NET.ILLink.Tasks/8.0.8": {},
"Newtonsoft.Json/13.0.3": {
"runtime": {
"lib/net6.0/Newtonsoft.Json.dll": {
"assemblyVersion": "13.0.0.0",
"fileVersion": "13.0.3.27908"
}
}
}
}
},
"libraries": {
"EVE-O-Preview/1.0.0": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"LightInject/7.0.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-aw4ayG2Pe68i+85ws8zYk7MPCKjEd4DeBoTqVvmjA2cfpYYNnw+v0E5T3PKdriKaxdKF+eUzlnxWWZnYK/gx4w==",
"path": "lightinject/7.0.1",
"hashPath": "lightinject.7.0.1.nupkg.sha512"
},
"MediatR/9.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-8b3UYNxegHVYcJMG2zH8wn+YqxLvXG+eMfj0cMCq/jTW72p6O3PCKMkrIv0mqyxdW7bA4gblsocw7n+/9Akg5g==",
"path": "mediatr/9.0.0",
"hashPath": "mediatr.9.0.0.nupkg.sha512"
},
"Microsoft.CSharp/4.7.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA==",
"path": "microsoft.csharp/4.7.0",
"hashPath": "microsoft.csharp.4.7.0.nupkg.sha512"
},
"Microsoft.NET.ILLink.Tasks/8.0.8": {
"type": "package",
"serviceable": true,
"sha512": "sha512-P8wR6MUWwYXIjPJuBaZgo5zlI/GWI6QEAo6NyVIbPefa9CCkohYu7dP2rD/mrqnjEqfRHyl+h9VZrDoGpELqYg==",
"path": "microsoft.net.illink.tasks/8.0.8",
"hashPath": "microsoft.net.illink.tasks.8.0.8.nupkg.sha512"
},
"Newtonsoft.Json/13.0.3": {
"type": "package",
"serviceable": true,
"sha512": "sha512-HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==",
"path": "newtonsoft.json/13.0.3",
"hashPath": "newtonsoft.json.13.0.3.nupkg.sha512"
}
}
}

View File

@@ -1,18 +0,0 @@
{
"runtimeOptions": {
"tfm": "net8.0",
"frameworks": [
{
"name": "Microsoft.NETCore.App",
"version": "8.0.0"
},
{
"name": "Microsoft.WindowsDesktop.App",
"version": "8.0.0"
}
],
"configProperties": {
"System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization": true
}
}
}