diff --git a/YMouseButtonControl.Core.Tests/YMouseButtonControl.Core.Tests.csproj b/YMouseButtonControl.Core.Tests/YMouseButtonControl.Core.Tests.csproj
deleted file mode 100644
index b0b948b..0000000
--- a/YMouseButtonControl.Core.Tests/YMouseButtonControl.Core.Tests.csproj
+++ /dev/null
@@ -1,44 +0,0 @@
-
-
-
- net8.0
- enable
- enable
-
- false
- true
-
-
-
-
-
- all
- runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
-
-
-
- all
- runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
-
- all
- runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/YMouseButtonControl.Core/Mappings/ButtonMappingMapper.cs b/YMouseButtonControl.Core/Mappings/ButtonMappingMapper.cs
index 46fd134..3643509 100644
--- a/YMouseButtonControl.Core/Mappings/ButtonMappingMapper.cs
+++ b/YMouseButtonControl.Core/Mappings/ButtonMappingMapper.cs
@@ -1,33 +1,168 @@
using System;
-using Riok.Mapperly.Abstractions;
using YMouseButtonControl.Core.ViewModels.Models;
-using YMouseButtonControl.DataAccess.Models;
+using YMouseButtonControl.Domain.Models;
namespace YMouseButtonControl.Core.Mappings;
-[Mapper]
-public static partial class ButtonMappingMapper
+public static class ButtonMappingMapper
{
- [MapDerivedType]
- [MapDerivedType]
- [MapDerivedType]
- [MapDerivedType]
- public static partial BaseButtonMappingVm Map(ButtonMapping? buttonMapping);
-
- [MapDerivedType]
- [MapDerivedType]
- [MapDerivedType]
- [MapDerivedType]
- public static partial ButtonMapping Map(BaseButtonMappingVm buttonMapping);
-
- [MapDerivedType]
- [MapDerivedType]
- [MapDerivedType]
- [MapDerivedType]
- public static partial void Map(BaseButtonMappingVm src, ButtonMapping dst);
+ public static BaseButtonMappingVm MapToViewModel(ButtonMapping buttonMapping)
+ {
+ return buttonMapping switch
+ {
+ DisabledMapping disabledMapping => MapDisabledMapping(disabledMapping),
+ NothingMapping nothingMapping => MapNothingMapping(nothingMapping),
+ SimulatedKeystroke simulatedKeystroke => MapSimulatedKeystroke(simulatedKeystroke),
+ RightClick rightClick => MapRightClick(rightClick),
+ _ => throw new InvalidOperationException("Unknown button mapping type"),
+ };
+ }
+
+ private static DisabledMappingVm MapDisabledMapping(DisabledMapping disabledMapping) =>
+ new()
+ {
+ Id = disabledMapping.Id,
+ ProfileId = disabledMapping.ProfileId,
+ MouseButton = disabledMapping.MouseButton,
+ Keys = disabledMapping.Keys,
+ AutoRepeatDelay = disabledMapping.AutoRepeatDelay,
+ AutoRepeatRandomizeDelayEnabled = disabledMapping.AutoRepeatRandomizeDelayEnabled,
+ BlockOriginalMouseInput = disabledMapping.BlockOriginalMouseInput,
+ Selected = disabledMapping.Selected,
+ };
+
+ private static NothingMappingVm MapNothingMapping(NothingMapping nothingMapping) =>
+ new()
+ {
+ Id = nothingMapping.Id,
+ ProfileId = nothingMapping.ProfileId,
+ MouseButton = nothingMapping.MouseButton,
+ Keys = nothingMapping.Keys,
+ AutoRepeatDelay = nothingMapping.AutoRepeatDelay,
+ AutoRepeatRandomizeDelayEnabled = nothingMapping.AutoRepeatRandomizeDelayEnabled,
+ BlockOriginalMouseInput = nothingMapping.BlockOriginalMouseInput,
+ Selected = nothingMapping.Selected,
+ };
+
+ private static SimulatedKeystrokeVm MapSimulatedKeystroke(SimulatedKeystroke simulatedKeystroke)
+ {
+ var viewModel = new SimulatedKeystrokeVm
+ {
+ Id = simulatedKeystroke.Id,
+ ProfileId = simulatedKeystroke.ProfileId,
+ MouseButton = simulatedKeystroke.MouseButton,
+ Keys = simulatedKeystroke.Keys,
+ AutoRepeatDelay = simulatedKeystroke.AutoRepeatDelay,
+ AutoRepeatRandomizeDelayEnabled = simulatedKeystroke.AutoRepeatRandomizeDelayEnabled,
+ BlockOriginalMouseInput = simulatedKeystroke.BlockOriginalMouseInput,
+ Selected = simulatedKeystroke.Selected,
+ };
+
+ if (simulatedKeystroke.SimulatedKeystrokeType != null)
+ {
+ viewModel.SimulatedKeystrokeType = MapSimulatedKeystrokeType(
+ simulatedKeystroke.SimulatedKeystrokeType
+ );
+ }
+
+ return viewModel;
+ }
+
+ private static RightClickVm MapRightClick(RightClick rightClick) =>
+ new()
+ {
+ Id = rightClick.Id,
+ ProfileId = rightClick.ProfileId,
+ MouseButton = rightClick.MouseButton,
+ Keys = rightClick.Keys,
+ AutoRepeatDelay = rightClick.AutoRepeatDelay,
+ AutoRepeatRandomizeDelayEnabled = rightClick.AutoRepeatRandomizeDelayEnabled,
+ BlockOriginalMouseInput = rightClick.BlockOriginalMouseInput,
+ Selected = rightClick.Selected,
+ };
+
+ public static ButtonMapping MapToEntity(BaseButtonMappingVm buttonMappingVm)
+ {
+ return buttonMappingVm switch
+ {
+ DisabledMappingVm disabledMappingVm => MapDisabledMappingVm(disabledMappingVm),
+ NothingMappingVm nothingMappingVm => MapNothingMappingVm(nothingMappingVm),
+ SimulatedKeystrokeVm simulatedKeystrokeVm => MapSimulatedKeystrokeVm(
+ simulatedKeystrokeVm
+ ),
+ RightClickVm rightClickVm => MapRightClickVm(rightClickVm),
+ _ => throw new InvalidOperationException("Unknown button mapping VM type"),
+ };
+ }
+
+ private static DisabledMapping MapDisabledMappingVm(DisabledMappingVm disabledMappingVm) =>
+ new()
+ {
+ Id = disabledMappingVm.Id,
+ ProfileId = disabledMappingVm.ProfileId,
+ MouseButton = disabledMappingVm.MouseButton,
+ Keys = disabledMappingVm.Keys,
+ AutoRepeatDelay = disabledMappingVm.AutoRepeatDelay,
+ AutoRepeatRandomizeDelayEnabled = disabledMappingVm.AutoRepeatRandomizeDelayEnabled,
+ BlockOriginalMouseInput = disabledMappingVm.BlockOriginalMouseInput,
+ Selected = disabledMappingVm.Selected,
+ };
+
+ private static NothingMapping MapNothingMappingVm(NothingMappingVm nothingMappingVm) =>
+ new()
+ {
+ Id = nothingMappingVm.Id,
+ ProfileId = nothingMappingVm.ProfileId,
+ MouseButton = nothingMappingVm.MouseButton,
+ Keys = nothingMappingVm.Keys,
+ AutoRepeatDelay = nothingMappingVm.AutoRepeatDelay,
+ AutoRepeatRandomizeDelayEnabled = nothingMappingVm.AutoRepeatRandomizeDelayEnabled,
+ BlockOriginalMouseInput = nothingMappingVm.BlockOriginalMouseInput,
+ Selected = nothingMappingVm.Selected,
+ };
+
+ private static SimulatedKeystroke MapSimulatedKeystrokeVm(
+ SimulatedKeystrokeVm simulatedKeystrokeVm
+ )
+ {
+ var entity = new SimulatedKeystroke
+ {
+ Id = simulatedKeystrokeVm.Id,
+ ProfileId = simulatedKeystrokeVm.ProfileId,
+ MouseButton = simulatedKeystrokeVm.MouseButton,
+ Keys = simulatedKeystrokeVm.Keys,
+ AutoRepeatDelay = simulatedKeystrokeVm.AutoRepeatDelay,
+ AutoRepeatRandomizeDelayEnabled = simulatedKeystrokeVm.AutoRepeatRandomizeDelayEnabled,
+ BlockOriginalMouseInput = simulatedKeystrokeVm.BlockOriginalMouseInput,
+ Selected = simulatedKeystrokeVm.Selected,
+ };
+
+ // Assuming BaseSimulatedKeystrokeTypeVm has a method or constructor that can map SimulatedKeystrokeType to it
+ if (simulatedKeystrokeVm.SimulatedKeystrokeType != null)
+ {
+ entity.SimulatedKeystrokeType = MapSimulatedKeystrokeTypeVm(
+ simulatedKeystrokeVm.SimulatedKeystrokeType
+ );
+ }
+
+ return entity;
+ }
+
+ private static RightClick MapRightClickVm(RightClickVm rightClickVm) =>
+ new()
+ {
+ Id = rightClickVm.Id,
+ ProfileId = rightClickVm.ProfileId,
+ MouseButton = rightClickVm.MouseButton,
+ Keys = rightClickVm.Keys,
+ AutoRepeatDelay = rightClickVm.AutoRepeatDelay,
+ AutoRepeatRandomizeDelayEnabled = rightClickVm.AutoRepeatRandomizeDelayEnabled,
+ BlockOriginalMouseInput = rightClickVm.BlockOriginalMouseInput,
+ Selected = rightClickVm.Selected,
+ };
public static BaseSimulatedKeystrokeTypeVm MapSimulatedKeystrokeType(
- SimulatedKeystrokeType simulatedKeystrokeType
+ SimulatedKeystrokeType? simulatedKeystrokeType
) =>
simulatedKeystrokeType switch
{
diff --git a/YMouseButtonControl.Core/Mappings/ProfileMapper.cs b/YMouseButtonControl.Core/Mappings/ProfileMapper.cs
index 681b35d..fdf8f2f 100644
--- a/YMouseButtonControl.Core/Mappings/ProfileMapper.cs
+++ b/YMouseButtonControl.Core/Mappings/ProfileMapper.cs
@@ -1,25 +1,50 @@
-using System.Collections.Generic;
-using System.Linq;
-using Riok.Mapperly.Abstractions;
+using System.Linq;
using YMouseButtonControl.Core.ViewModels.Models;
-using YMouseButtonControl.DataAccess.Models;
+using YMouseButtonControl.Domain.Models;
namespace YMouseButtonControl.Core.Mappings;
-[Mapper]
-public static partial class ProfileMapper
+public static class ProfileMapper
{
- public static partial ProfileVm Map(Profile? profile);
+ public static ProfileVm MapToViewModel(Profile profile)
+ {
+ var buttonMappings = profile
+ .ButtonMappings.Select(ButtonMappingMapper.MapToViewModel)
+ .ToList();
+ var viewModel = new ProfileVm(buttonMappings)
+ {
+ Id = profile.Id,
+ IsDefault = profile.IsDefault,
+ Checked = profile.Checked,
+ Name = profile.Name,
+ Description = profile.Description,
+ WindowCaption = profile.WindowCaption,
+ Process = profile.Process,
+ WindowClass = profile.WindowClass,
+ ParentClass = profile.ParentClass,
+ MatchType = profile.MatchType,
+ DisplayPriority = profile.DisplayPriority,
+ };
- public static partial Profile Map(ProfileVm vm);
+ return viewModel;
+ }
- public static partial void Map(ProfileVm src, Profile dst);
-
- private static List MapButtonMapping(
- ICollection buttonMappings
- ) => buttonMappings.Select(ButtonMappingMapper.Map).ToList();
-
- private static ICollection MapButtonMappingVms(
- List buttonMappings
- ) => buttonMappings.Select(ButtonMappingMapper.Map).ToList();
+ public static Profile MapToEntity(ProfileVm profileVm) =>
+ new()
+ {
+ Id = profileVm.Id,
+ IsDefault = profileVm.IsDefault,
+ Checked = profileVm.Checked,
+ Name = profileVm.Name,
+ Description = profileVm.Description,
+ WindowCaption = profileVm.WindowCaption,
+ Process = profileVm.Process,
+ WindowClass = profileVm.WindowClass,
+ ParentClass = profileVm.ParentClass,
+ MatchType = profileVm.MatchType,
+ DisplayPriority = profileVm.DisplayPriority,
+ ButtonMappings = profileVm
+ .ButtonMappings.Select(ButtonMappingMapper.MapToEntity)
+ .ToList(),
+ };
}
diff --git a/YMouseButtonControl.Core/Mappings/SettingMapper.cs b/YMouseButtonControl.Core/Mappings/SettingMapper.cs
deleted file mode 100644
index 4173c2e..0000000
--- a/YMouseButtonControl.Core/Mappings/SettingMapper.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-using Riok.Mapperly.Abstractions;
-using YMouseButtonControl.Core.ViewModels.Models;
-using YMouseButtonControl.DataAccess.Models;
-
-namespace YMouseButtonControl.Core.Mappings;
-
-[Mapper]
-public static partial class SettingMapper
-{
- [MapDerivedType]
- [MapDerivedType]
- [MapDerivedType]
- public static partial BaseSettingVm Map(Setting? setting);
-
- [MapDerivedType]
- [MapDerivedType]
- [MapDerivedType]
- public static partial Setting Map(BaseSettingVm baseSettingVm);
-
- [MapDerivedType]
- [MapDerivedType]
- [MapDerivedType]
- public static partial void Map(BaseSettingVm src, Setting dst);
-}
diff --git a/YMouseButtonControl.Core/Mappings/ThemeMapper.cs b/YMouseButtonControl.Core/Mappings/ThemeMapper.cs
deleted file mode 100644
index 97be0c3..0000000
--- a/YMouseButtonControl.Core/Mappings/ThemeMapper.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-using Riok.Mapperly.Abstractions;
-using YMouseButtonControl.Core.ViewModels.Models;
-using YMouseButtonControl.DataAccess.Models;
-
-namespace YMouseButtonControl.Core.Mappings;
-
-[Mapper]
-public static partial class ThemeMapper
-{
- public static partial ThemeVm Map(Theme? theme);
-
- public static partial Theme Map(ThemeVm? vm);
-}
diff --git a/YMouseButtonControl.Windows/NativeMethods.json b/YMouseButtonControl.Core/NativeMethods.json
similarity index 100%
rename from YMouseButtonControl.Windows/NativeMethods.json
rename to YMouseButtonControl.Core/NativeMethods.json
diff --git a/YMouseButtonControl.Windows/NativeMethods.txt b/YMouseButtonControl.Core/NativeMethods.txt
similarity index 100%
rename from YMouseButtonControl.Windows/NativeMethods.txt
rename to YMouseButtonControl.Core/NativeMethods.txt
diff --git a/YMouseButtonControl.Core/Repositories/ButtonMappingRepository.cs b/YMouseButtonControl.Core/Repositories/ButtonMappingRepository.cs
deleted file mode 100644
index f9db682..0000000
--- a/YMouseButtonControl.Core/Repositories/ButtonMappingRepository.cs
+++ /dev/null
@@ -1,99 +0,0 @@
-using System.Collections.Generic;
-using System.Data;
-using System.Linq;
-using System.Threading.Tasks;
-using Dapper;
-using YMouseButtonControl.Core.Mappings;
-using YMouseButtonControl.Core.ViewModels.Models;
-using YMouseButtonControl.DataAccess.Context;
-using YMouseButtonControl.DataAccess.Models;
-using YMouseButtonControl.DataAccess.Queries;
-
-namespace YMouseButtonControl.Core.Repositories;
-
-public class ButtonMappingRepository(YMouseButtonControlDbContext ctx, ButtonMappingQueries queries)
- : IRepository
-{
- private readonly YMouseButtonControlDbContext _ctx = ctx;
- private const string TblName = "ButtonMappings";
-
- public int Add(BaseButtonMappingVm vm)
- {
- using var conn = _ctx.CreateConnection();
- var ent = ButtonMappingMapper.Map(vm);
- ent.ButtonMappingType = ent switch
- {
- DisabledMapping => ButtonMappingType.Disabled,
- NothingMapping => ButtonMappingType.Nothing,
- SimulatedKeystroke => ButtonMappingType.SimulatedKeystroke,
- RightClick => ButtonMappingType.RightClick,
- _ => throw new System.NotImplementedException(),
- };
- return conn.Execute(queries.Add(), ent);
- }
-
- public async Task AddAsync(BaseButtonMappingVm vm)
- {
- using var conn = _ctx.CreateConnection();
- return await conn.ExecuteAsync(queries.Add(), vm);
- }
-
- public BaseButtonMappingVm? GetById(int id)
- {
- using var conn = _ctx.CreateConnection();
- return ButtonMappingMapper.Map(
- conn.QueryFirstOrDefault(queries.GetById(TblName), id)
- );
- }
-
- public async Task GetByIdAsync(int id)
- {
- using var conn = _ctx.CreateConnection();
- return ButtonMappingMapper.Map(
- await conn.QueryFirstOrDefaultAsync(queries.GetById(TblName), id)
- );
- }
-
- public IEnumerable GetAll()
- {
- using var conn = _ctx.CreateConnection();
- return conn.Query(queries.GetAll(TblName)).Select(ButtonMappingMapper.Map);
- }
-
- public async Task> GetAllAsync()
- {
- using var conn = _ctx.CreateConnection();
- return (await conn.QueryAsync(queries.GetAll(TblName))).Select(
- ButtonMappingMapper.Map
- );
- }
-
- public int Update(BaseButtonMappingVm vm)
- {
- using var conn = _ctx.CreateConnection();
- return conn.Execute(queries.Update(), vm);
- }
-
- public async Task UpdateAsync(BaseButtonMappingVm vm)
- {
- using var conn = _ctx.CreateConnection();
- return await conn.ExecuteAsync(queries.Update(), vm);
- }
-
- public int Delete(BaseButtonMappingVm vm)
- {
- using var conn = _ctx.CreateConnection();
- return conn.Execute(queries.DeleteById(TblName), vm.Id);
- }
-
- public Task DeleteAsync(BaseButtonMappingVm vm)
- {
- using var conn = _ctx.CreateConnection();
- return conn.ExecuteAsync(queries.DeleteById(TblName), vm.Id);
- }
-
- public BaseButtonMappingVm? GetByName(string name)
- {
- throw new System.NotImplementedException();
- }
-}
diff --git a/YMouseButtonControl.Core/Repositories/IRepository.cs b/YMouseButtonControl.Core/Repositories/IRepository.cs
deleted file mode 100644
index 4a12ded..0000000
--- a/YMouseButtonControl.Core/Repositories/IRepository.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-using System.Collections.Generic;
-using System.Threading.Tasks;
-
-namespace YMouseButtonControl.Core.Repositories;
-
-public interface IRepository
-{
- int Add(TVm vm);
- Task AddAsync(TVm vm);
- TVm? GetByName(string name);
- TVm? GetById(int id);
- Task GetByIdAsync(int id);
- IEnumerable GetAll();
- Task> GetAllAsync();
- int Update(TVm vm);
- Task UpdateAsync(TVm vm);
- int Delete(TVm vm);
- Task DeleteAsync(TVm vm);
-}
diff --git a/YMouseButtonControl.Core/Repositories/ProfileRepository.cs b/YMouseButtonControl.Core/Repositories/ProfileRepository.cs
deleted file mode 100644
index b1c04ac..0000000
--- a/YMouseButtonControl.Core/Repositories/ProfileRepository.cs
+++ /dev/null
@@ -1,140 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
-using Dapper;
-using YMouseButtonControl.Core.Mappings;
-using YMouseButtonControl.Core.ViewModels.Models;
-using YMouseButtonControl.DataAccess.Context;
-using YMouseButtonControl.DataAccess.Models;
-using YMouseButtonControl.DataAccess.Queries;
-
-namespace YMouseButtonControl.Core.Repositories;
-
-public class ProfileRepository(
- YMouseButtonControlDbContext ctx,
- ProfileQueries queries,
- ButtonMappingQueries btnMappingQueries
-) : IRepository
-{
- private readonly YMouseButtonControlDbContext _ctx = ctx;
- private const string TblName = "Profiles";
-
- public int Add(ProfileVm vm)
- {
- using var conn = _ctx.CreateConnection();
- return conn.Query(queries.Add(), vm).Single();
- }
-
- public async Task AddAsync(ProfileVm vm)
- {
- using var conn = _ctx.CreateConnection();
- return await conn.ExecuteAsync(queries.Add(), vm);
- }
-
- public ProfileVm? GetById(int id)
- {
- using var conn = _ctx.CreateConnection();
- var profile = conn.QueryFirstOrDefault(queries.GetById(TblName), new { Id = id });
- if (profile == null)
- {
- return null;
- }
- profile.ButtonMappings = GetButtonMappingsForProfileId(id);
- return ProfileMapper.Map(profile);
- }
-
- public async Task GetByIdAsync(int id)
- {
- using var conn = _ctx.CreateConnection();
- return ProfileMapper.Map(
- await conn.QueryFirstOrDefaultAsync(queries.GetById(TblName), id)
- );
- }
-
- public IEnumerable GetAll()
- {
- using var conn = _ctx.CreateConnection();
- return conn.Query(queries.GetAll(TblName))
- .Select(p =>
- {
- p.ButtonMappings = GetButtonMappingsForProfileId(p.Id);
- return ProfileMapper.Map(p);
- });
- }
-
- public async Task> GetAllAsync()
- {
- using var conn = _ctx.CreateConnection();
- return (await conn.QueryAsync(queries.GetAll(TblName))).Select(ProfileMapper.Map);
- }
-
- public int Update(ProfileVm vm)
- {
- using var conn = _ctx.CreateConnection();
- return conn.Execute(queries.Update(), vm);
- }
-
- public async Task UpdateAsync(ProfileVm vm)
- {
- using var conn = _ctx.CreateConnection();
- return await conn.ExecuteAsync(queries.Update(), vm);
- }
-
- public int Delete(ProfileVm vm)
- {
- using var conn = _ctx.CreateConnection();
- return conn.Execute(queries.DeleteById(TblName), vm);
- }
-
- public Task DeleteAsync(ProfileVm vm)
- {
- using var conn = _ctx.CreateConnection();
- return conn.ExecuteAsync(queries.DeleteById(TblName), vm);
- }
-
- private List GetButtonMappingsForProfileId(int id)
- {
- using var conn = _ctx.CreateConnection();
- using var reader = conn.ExecuteReader(btnMappingQueries.GetByProfileId(), new { Id = id });
- var nothingParser = reader.GetRowParser();
- var disabledParser = reader.GetRowParser();
- var simulatedKeystrokeParser = reader.GetRowParser();
- var rightClickParser = reader.GetRowParser();
-
- var buttonMappings = new List();
-
- while (reader.Read())
- {
- var discriminator = (ButtonMappingType)
- reader.GetInt32(reader.GetOrdinal("ButtonMappingType"));
- switch (discriminator)
- {
- case ButtonMappingType.Disabled:
- buttonMappings.Add(disabledParser(reader));
- break;
- case ButtonMappingType.Nothing:
- buttonMappings.Add(nothingParser(reader));
- break;
- case ButtonMappingType.SimulatedKeystroke:
- buttonMappings.Add(simulatedKeystrokeParser(reader));
- break;
- case ButtonMappingType.RightClick:
- buttonMappings.Add(rightClickParser(reader));
- break;
- default:
- throw new ArgumentOutOfRangeException();
- }
- }
-
- return buttonMappings;
- }
-
- public ProfileVm? GetByName(string name)
- {
- using var conn = _ctx.CreateConnection();
- return ProfileMapper.Map(
- conn.QuerySingleOrDefault(queries.GetByName(TblName), name)
- );
- }
-}
diff --git a/YMouseButtonControl.Core/Repositories/SettingRepository.cs b/YMouseButtonControl.Core/Repositories/SettingRepository.cs
deleted file mode 100644
index 7c42ed2..0000000
--- a/YMouseButtonControl.Core/Repositories/SettingRepository.cs
+++ /dev/null
@@ -1,162 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Data;
-using System.Linq;
-using System.Threading.Tasks;
-using Dapper;
-using YMouseButtonControl.Core.Mappings;
-using YMouseButtonControl.Core.ViewModels.Models;
-using YMouseButtonControl.DataAccess.Context;
-using YMouseButtonControl.DataAccess.Models;
-using YMouseButtonControl.DataAccess.Queries;
-using static Dapper.SqlMapper;
-
-namespace YMouseButtonControl.Core.Repositories;
-
-public class SettingRepository(YMouseButtonControlDbContext ctx, SettingQueries queries)
- : IRepository
-{
- private readonly YMouseButtonControlDbContext _ctx = ctx;
- private const string TblName = "Settings";
-
- public int Add(BaseSettingVm vm)
- {
- using var conn = _ctx.CreateConnection();
- return conn.Execute(queries.Add(), vm);
- }
-
- public async Task AddAsync(BaseSettingVm vm)
- {
- using var conn = _ctx.CreateConnection();
- return await conn.ExecuteAsync(queries.Add(), vm);
- }
-
- public BaseSettingVm? GetById(int id)
- {
- using var conn = _ctx.CreateConnection();
- return SettingMapper.Map(conn.QueryFirstOrDefault(queries.GetById(TblName), id));
- }
-
- public async Task GetByIdAsync(int id)
- {
- using var conn = _ctx.CreateConnection();
- return SettingMapper.Map(
- await conn.QueryFirstOrDefaultAsync(queries.GetById(TblName), id)
- );
- }
-
- public IEnumerable GetAll()
- {
- using var conn = _ctx.CreateConnection();
- using var reader = conn.ExecuteReader(queries.GetAll(TblName));
- var settings = new List();
- var settingBoolParser = reader.GetRowParser();
- var settingIntParser = reader.GetRowParser();
- var settingStringParser = reader.GetRowParser();
- while (reader.Read())
- {
- var discriminator = (SettingType)
- reader.GetInt32(reader.GetOrdinal(nameof(SettingType)));
- switch (discriminator)
- {
- case SettingType.SettingBool:
- settings.Add(settingBoolParser(reader));
- break;
- case SettingType.SettingString:
- settings.Add(settingStringParser(reader));
- break;
- case SettingType.SettingInt:
- settings.Add(settingIntParser(reader));
- break;
- default:
- throw new ArgumentOutOfRangeException();
- }
- }
- return settings.Select(SettingMapper.Map);
- }
-
- public BaseSettingVm? GetByName(string name)
- {
- using var conn = _ctx.CreateConnection();
- using var reader = conn.ExecuteReader(queries.GetByName(TblName), new { Name = name });
- var settingBoolParser = reader.GetRowParser();
- var settingIntParser = reader.GetRowParser();
- var settingStringParser = reader.GetRowParser();
- reader.Read();
- var discriminator = (SettingType)reader.GetInt32(reader.GetOrdinal(nameof(SettingType)));
- return discriminator switch
- {
- SettingType.SettingBool => SettingMapper.Map(settingBoolParser(reader)),
- SettingType.SettingString => SettingMapper.Map(settingStringParser(reader)),
- SettingType.SettingInt => SettingMapper.Map(settingIntParser(reader)),
- _ => throw new ArgumentOutOfRangeException(),
- };
- }
-
- public async Task> GetAllAsync()
- {
- using var conn = _ctx.CreateConnection();
- return (await conn.QueryAsync(queries.GetAll(TblName))).Select(SettingMapper.Map);
- }
-
- public int Update(BaseSettingVm vm)
- {
- using var conn = _ctx.CreateConnection();
- var ent = SettingMapper.Map(vm);
- return ent switch
- {
- SettingBool t => conn.Execute(
- queries.Update(),
- new
- {
- ent.Id,
- ent.Name,
- t.BoolValue,
- StringValue = (string?)null,
- IntValue = (int?)null,
- }
- ),
- SettingString t => conn.Execute(
- queries.Update(),
- new
- {
- ent.Id,
- ent.Name,
- BoolValue = (bool?)null,
- t.StringValue,
- IntValue = (int?)null,
- }
- ),
- SettingInt t => conn.Execute(
- queries.Update(),
- new
- {
- ent.Id,
- ent.Name,
- BoolValue = (bool?)null,
- StringValue = (string?)null,
- t.IntValue,
- }
- ),
- _ => throw new NotImplementedException(),
- };
- }
-
- public async Task UpdateAsync(BaseSettingVm vm)
- {
- using var conn = _ctx.CreateConnection();
- return await conn.ExecuteAsync(queries.Update(), vm);
- }
-
- public int Delete(BaseSettingVm vm)
- {
- using var conn = _ctx.CreateConnection();
- return conn.Execute(queries.DeleteById(TblName), vm.Id);
- }
-
- public Task DeleteAsync(BaseSettingVm vm)
- {
- using var conn = _ctx.CreateConnection();
- return conn.ExecuteAsync(queries.DeleteById(TblName), vm.Id);
- }
-}
diff --git a/YMouseButtonControl.Core/Repositories/ThemeRepository.cs b/YMouseButtonControl.Core/Repositories/ThemeRepository.cs
deleted file mode 100644
index e34083a..0000000
--- a/YMouseButtonControl.Core/Repositories/ThemeRepository.cs
+++ /dev/null
@@ -1,84 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Data;
-using System.Linq;
-using System.Threading.Tasks;
-using Dapper;
-using YMouseButtonControl.Core.Mappings;
-using YMouseButtonControl.Core.ViewModels.Models;
-using YMouseButtonControl.DataAccess.Context;
-using YMouseButtonControl.DataAccess.Models;
-using YMouseButtonControl.DataAccess.Queries;
-
-namespace YMouseButtonControl.Core.Repositories;
-
-public class ThemeRepository(YMouseButtonControlDbContext ctx, ThemeQueries queries)
- : IRepository
-{
- private const string TblName = "Themes";
-
- public int Add(ThemeVm vm)
- {
- using var conn = ctx.CreateConnection();
- return conn.Query(queries.Add(), ThemeMapper.Map(vm)).Single();
- }
-
- public Task AddAsync(ThemeVm vm)
- {
- throw new NotImplementedException();
- }
-
- public int Delete(ThemeVm vm)
- {
- using var conn = ctx.CreateConnection();
- return conn.Execute(queries.DeleteById(TblName), new { ThemeMapper.Map(vm).Id });
- }
-
- public Task DeleteAsync(ThemeVm vm)
- {
- throw new NotImplementedException();
- }
-
- public IEnumerable GetAll()
- {
- using var conn = ctx.CreateConnection();
- return conn.Query(queries.GetAll(TblName)).Select(ThemeMapper.Map);
- }
-
- public Task> GetAllAsync()
- {
- throw new NotImplementedException();
- }
-
- public ThemeVm? GetById(int id)
- {
- using var conn = ctx.CreateConnection();
- return ThemeMapper.Map(
- conn.QueryFirstOrDefault(queries.GetById(TblName), new { Id = id })
- );
- }
-
- public Task GetByIdAsync(int id)
- {
- throw new NotImplementedException();
- }
-
- public ThemeVm? GetByName(string name)
- {
- using var conn = ctx.CreateConnection();
- return ThemeMapper.Map(
- conn.QuerySingleOrDefault(queries.GetByName(TblName), new { Name = name })
- );
- }
-
- public int Update(ThemeVm vm)
- {
- using var conn = ctx.CreateConnection();
- return conn.Execute(queries.Update(), ThemeMapper.Map(vm));
- }
-
- public Task UpdateAsync(ThemeVm vm)
- {
- throw new NotImplementedException();
- }
-}
diff --git a/YMouseButtonControl.Core/Services/BackgroundTasks/IBackgroundTasksRunner.cs b/YMouseButtonControl.Core/Services/BackgroundTasks/IBackgroundTasksRunner.cs
deleted file mode 100644
index db543d0..0000000
--- a/YMouseButtonControl.Core/Services/BackgroundTasks/IBackgroundTasksRunner.cs
+++ /dev/null
@@ -1,5 +0,0 @@
-using System;
-
-namespace YMouseButtonControl.Core.Services.BackgroundTasks;
-
-public interface IBackgroundTasksRunner : IDisposable { }
diff --git a/YMouseButtonControl.Core/Services/KeyboardAndMouse/Implementations/MouseListenerHandlerRegistrations.cs b/YMouseButtonControl.Core/Services/KeyboardAndMouse/Implementations/MouseListenerHandlerRegistrations.cs
new file mode 100644
index 0000000..f05cf8a
--- /dev/null
+++ b/YMouseButtonControl.Core/Services/KeyboardAndMouse/Implementations/MouseListenerHandlerRegistrations.cs
@@ -0,0 +1,10 @@
+using Microsoft.Extensions.DependencyInjection;
+using YMouseButtonControl.Core.Services.KeyboardAndMouse.Implementations.Queries.Profiles;
+
+namespace YMouseButtonControl.Core.Services.KeyboardAndMouse.Implementations;
+
+public static class MouseListenerHandlerRegistrations
+{
+ public static void RegisterCommon(IServiceCollection services) =>
+ services.AddScoped();
+}
diff --git a/YMouseButtonControl.Core/Services/KeyboardAndMouse/Implementations/MouseListenerService.cs b/YMouseButtonControl.Core/Services/KeyboardAndMouse/Implementations/MouseListenerService.cs
index be93ae6..5289edc 100644
--- a/YMouseButtonControl.Core/Services/KeyboardAndMouse/Implementations/MouseListenerService.cs
+++ b/YMouseButtonControl.Core/Services/KeyboardAndMouse/Implementations/MouseListenerService.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.ObjectModel;
using System.Linq;
using System.Reactive.Linq;
using System.Reactive.Subjects;
@@ -9,8 +10,9 @@
using SharpHook.Reactive;
using YMouseButtonControl.Core.Services.KeyboardAndMouse.Enums;
using YMouseButtonControl.Core.Services.KeyboardAndMouse.EventArgs;
-using YMouseButtonControl.Core.Services.Processes;
-using YMouseButtonControl.Core.Services.Profiles;
+using YMouseButtonControl.Core.Services.KeyboardAndMouse.Implementations.Queries.CurrentWindow;
+using YMouseButtonControl.Core.Services.KeyboardAndMouse.Implementations.Queries.Profiles;
+using static YMouseButtonControl.Core.Services.KeyboardAndMouse.Implementations.Queries.Profiles.ListProfiles;
using LogLevel = Microsoft.Extensions.Logging.LogLevel;
namespace YMouseButtonControl.Core.Services.KeyboardAndMouse.Implementations;
@@ -28,12 +30,11 @@ public interface IMouseListener : IDisposable
/// Wrapper around sharphook for listening to mouse events
/// Converts mouse events to NewMouseHookEventArgs
///
-public partial class MouseListener : IMouseListener
+public partial class MouseListenerService : IMouseListener
{
- private readonly ILogger _logger;
+ private readonly ILogger _logger;
private readonly IReactiveGlobalHook _hook;
- private readonly IProfilesService _profilesService;
- private readonly ICurrentWindowService _currentWindowService;
+ private readonly IGetCurrentWindow _currentWindowService;
private Thread? _thread;
private readonly IDisposable? _mouseMovedDisposable;
private readonly IDisposable? _mousePressedDisposable;
@@ -43,22 +44,23 @@ public partial class MouseListener : IMouseListener
private readonly Subject _mouseReleasedSubject;
private readonly Subject _mouseMovedSubject;
private readonly Subject _mouseWheelSubject;
+ private readonly ReadOnlyCollection _profiles;
- public MouseListener(
- ILogger logger,
+ public MouseListenerService(
+ ILogger logger,
IReactiveGlobalHook hook,
- IProfilesService profilesService,
- ICurrentWindowService currentWindowService
+ ListProfiles.Handler listProfilesHandler,
+ IGetCurrentWindow currentWindowService
)
{
_logger = logger;
_hook = hook;
- _profilesService = profilesService;
_currentWindowService = currentWindowService;
_mousePressedSubject = new Subject();
_mouseReleasedSubject = new Subject();
_mouseMovedSubject = new Subject();
_mouseWheelSubject = new Subject();
+ _profiles = listProfilesHandler.Execute();
_mouseMovedDisposable = _hook
.MouseMoved.Sample(TimeSpan.FromMilliseconds(100))
@@ -103,39 +105,39 @@ public void Run()
private bool ShouldSuppressEvent(NewMouseHookEventArgs args) =>
args.Button switch
{
- YMouseButton.MouseButton1 => _profilesService.Profiles.Any(p =>
+ YMouseButton.MouseButton1 => _profiles.Any(p =>
p is { Checked: true, MouseButton1.BlockOriginalMouseInput: true }
&& (p.Process == "*" || (args.ActiveWindow?.Contains(p.Process) ?? false))
),
- YMouseButton.MouseButton2 => _profilesService.Profiles.Any(p =>
+ YMouseButton.MouseButton2 => _profiles.Any(p =>
p is { Checked: true, MouseButton2.BlockOriginalMouseInput: true }
&& (p.Process == "*" || (args.ActiveWindow?.Contains(p.Process) ?? false))
),
- YMouseButton.MouseButton3 => _profilesService.Profiles.Any(p =>
+ YMouseButton.MouseButton3 => _profiles.Any(p =>
p is { Checked: true, MouseButton3.BlockOriginalMouseInput: true }
&& (p.Process == "*" || (args.ActiveWindow?.Contains(p.Process) ?? false))
),
- YMouseButton.MouseButton4 => _profilesService.Profiles.Any(p =>
+ YMouseButton.MouseButton4 => _profiles.Any(p =>
p is { Checked: true, MouseButton4.BlockOriginalMouseInput: true }
&& (p.Process == "*" || (args.ActiveWindow?.Contains(p.Process) ?? false))
),
- YMouseButton.MouseButton5 => _profilesService.Profiles.Any(p =>
+ YMouseButton.MouseButton5 => _profiles.Any(p =>
p is { Checked: true, MouseButton5.BlockOriginalMouseInput: true }
&& (p.Process == "*" || (args.ActiveWindow?.Contains(p.Process) ?? false))
),
- YMouseButton.MouseWheelUp => _profilesService.Profiles.Any(p =>
+ YMouseButton.MouseWheelUp => _profiles.Any(p =>
p is { Checked: true, MouseWheelUp.BlockOriginalMouseInput: true }
&& (p.Process == "*" || (args.ActiveWindow?.Contains(p.Process) ?? false))
),
- YMouseButton.MouseWheelDown => _profilesService.Profiles.Any(p =>
+ YMouseButton.MouseWheelDown => _profiles.Any(p =>
p is { Checked: true, MouseWheelDown.BlockOriginalMouseInput: true }
&& (p.Process == "*" || (args.ActiveWindow?.Contains(p.Process) ?? false))
),
- YMouseButton.MouseWheelLeft => _profilesService.Profiles.Any(p =>
+ YMouseButton.MouseWheelLeft => _profiles.Any(p =>
p is { Checked: true, MouseWheelLeft.BlockOriginalMouseInput: true }
&& (p.Process == "*" || (args.ActiveWindow?.Contains(p.Process) ?? false))
),
- YMouseButton.MouseWheelRight => _profilesService.Profiles.Any(p =>
+ YMouseButton.MouseWheelRight => _profiles.Any(p =>
p is { Checked: true, MouseWheelRight.BlockOriginalMouseInput: true }
&& (p.Process == "*" || (args.ActiveWindow?.Contains(p.Process) ?? false))
),
diff --git a/YMouseButtonControl.Core/Services/KeyboardAndMouse/Implementations/Queries/CurrentWindow/GetCurrentWindowLinux.cs b/YMouseButtonControl.Core/Services/KeyboardAndMouse/Implementations/Queries/CurrentWindow/GetCurrentWindowLinux.cs
new file mode 100644
index 0000000..505dbee
--- /dev/null
+++ b/YMouseButtonControl.Core/Services/KeyboardAndMouse/Implementations/Queries/CurrentWindow/GetCurrentWindowLinux.cs
@@ -0,0 +1,6 @@
+namespace YMouseButtonControl.Core.Services.KeyboardAndMouse.Implementations.Queries.CurrentWindow;
+
+public class GetCurrentWindowLinux : IGetCurrentWindow
+{
+ public string ForegroundWindow => "*";
+}
diff --git a/YMouseButtonControl.Linux/Services/CurrentWindowServiceX11.cs b/YMouseButtonControl.Core/Services/KeyboardAndMouse/Implementations/Queries/CurrentWindow/GetCurrentWindowLinuxX11.cs
similarity index 91%
rename from YMouseButtonControl.Linux/Services/CurrentWindowServiceX11.cs
rename to YMouseButtonControl.Core/Services/KeyboardAndMouse/Implementations/Queries/CurrentWindow/GetCurrentWindowLinuxX11.cs
index 6cf362e..24bf8fe 100644
--- a/YMouseButtonControl.Linux/Services/CurrentWindowServiceX11.cs
+++ b/YMouseButtonControl.Core/Services/KeyboardAndMouse/Implementations/Queries/CurrentWindow/GetCurrentWindowLinuxX11.cs
@@ -1,9 +1,11 @@
+using System;
+using System.IO;
using System.Runtime.InteropServices;
-using YMouseButtonControl.Core.Services.Processes;
+using YMouseButtonControl.Core.Services.KeyboardAndMouse.Implementations.Queries.CurrentWindow;
-namespace YMouseButtonControl.Linux.Services;
+namespace YMouseButtonControl.Core.Services.KeyboardAndMouse.Implementations.MouseListener.Queries.CurrentWindow;
-public class CurrentWindowServiceX11 : ICurrentWindowService
+public class GetCurrentWindowLinuxX11 : IGetCurrentWindow
{
public string ForegroundWindow => GetForegroundWindow();
diff --git a/YMouseButtonControl.MacOS/Services/CurrentWindowService.cs b/YMouseButtonControl.Core/Services/KeyboardAndMouse/Implementations/Queries/CurrentWindow/GetCurrentWindowOsx.cs
similarity index 77%
rename from YMouseButtonControl.MacOS/Services/CurrentWindowService.cs
rename to YMouseButtonControl.Core/Services/KeyboardAndMouse/Implementations/Queries/CurrentWindow/GetCurrentWindowOsx.cs
index d0dfe8e..5989ee8 100644
--- a/YMouseButtonControl.MacOS/Services/CurrentWindowService.cs
+++ b/YMouseButtonControl.Core/Services/KeyboardAndMouse/Implementations/Queries/CurrentWindow/GetCurrentWindowOsx.cs
@@ -1,13 +1,12 @@
using System;
using System.Runtime.InteropServices;
using System.Text;
-using YMouseButtonControl.Core.Services.Processes;
using CFIndex = long;
-namespace YMouseButtonControl.MacOS.Services;
+namespace YMouseButtonControl.Core.Services.KeyboardAndMouse.Implementations.Queries.CurrentWindow;
// a lot of this code was copied from https://stackoverflow.com/a/44669560 and https://github.com/isnowrain/CoreFoundation/blob/master/Project/CFType.cs
-public class CurrentWindowService : ICurrentWindowService
+public class GetCurrentWindowOsx : IGetCurrentWindow
{
public string ForegroundWindow => GetForegroundWindow();
@@ -33,15 +32,15 @@ private string GetForegroundWindow()
"/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation";
[DllImport(AppKitFramework, CharSet = CharSet.Ansi)]
- public static extern IntPtr objc_getClass(string name);
+ public static extern nint objc_getClass(string name);
[DllImport(FoundationFramework, EntryPoint = "objc_msgSend")]
- public static extern IntPtr objc_msgSend_retIntPtr(IntPtr target, IntPtr selector);
+ public static extern nint objc_msgSend_retIntPtr(nint target, nint selector);
- public static IntPtr GetSelector(string name)
+ public static nint GetSelector(string name)
{
- IntPtr cfstrSelector = CreateCfString(name);
- IntPtr selector = NSSelectorFromString(cfstrSelector);
+ nint cfstrSelector = CreateCfString(name);
+ nint selector = NSSelectorFromString(cfstrSelector);
CFRelease(cfstrSelector);
return selector;
}
@@ -56,8 +55,8 @@ private static unsafe string GetStrFromCfString(nint ptr)
{
var length = CFStringGetLength(ptr);
var u = CFStringGetCharactersPtr(ptr);
- var buffer = IntPtr.Zero;
- if (u == IntPtr.Zero)
+ var buffer = nint.Zero;
+ if (u == nint.Zero)
{
var range = new CfRange(0, length);
buffer = Marshal.AllocCoTaskMem((int)(length * 2));
@@ -66,28 +65,28 @@ private static unsafe string GetStrFromCfString(nint ptr)
}
var str = new string((char*)u, 0, (int)length);
- if (buffer != IntPtr.Zero)
+ if (buffer != nint.Zero)
Marshal.FreeCoTaskMem(buffer);
return str;
}
[DllImport(FoundationFramework)]
- public static extern void CFRelease(IntPtr handle);
+ public static extern void CFRelease(nint handle);
[DllImport(CoreFoundation)]
static extern nint CFStringGetCharactersPtr(nint theString);
[DllImport(AppKitFramework)]
- public static extern IntPtr NSSelectorFromString(IntPtr cfstr);
+ public static extern nint NSSelectorFromString(nint cfstr);
- public static unsafe IntPtr CreateCfString(string aString)
+ public static unsafe nint CreateCfString(string aString)
{
var bytes = Encoding.Unicode.GetBytes(aString);
fixed (byte* b = bytes)
{
var cfStr = CFStringCreateWithBytes(
- IntPtr.Zero,
- (IntPtr)b,
+ nint.Zero,
+ (nint)b,
bytes.Length,
CfStringEncoding.Utf16,
false
@@ -97,9 +96,9 @@ public static unsafe IntPtr CreateCfString(string aString)
}
[DllImport(FoundationFramework)]
- public static extern IntPtr CFStringCreateWithBytes(
- IntPtr allocator,
- IntPtr buffer,
+ public static extern nint CFStringCreateWithBytes(
+ nint allocator,
+ nint buffer,
long bufferLength,
CfStringEncoding encoding,
bool isExternalRepresentation
diff --git a/YMouseButtonControl.Windows/Services/CurrentWindowService.cs b/YMouseButtonControl.Core/Services/KeyboardAndMouse/Implementations/Queries/CurrentWindow/GetCurrentWindowWindows.cs
similarity index 90%
rename from YMouseButtonControl.Windows/Services/CurrentWindowService.cs
rename to YMouseButtonControl.Core/Services/KeyboardAndMouse/Implementations/Queries/CurrentWindow/GetCurrentWindowWindows.cs
index 817d373..d8152a2 100644
--- a/YMouseButtonControl.Windows/Services/CurrentWindowService.cs
+++ b/YMouseButtonControl.Core/Services/KeyboardAndMouse/Implementations/Queries/CurrentWindow/GetCurrentWindowWindows.cs
@@ -4,13 +4,12 @@
using Microsoft.Extensions.Logging;
using Windows.Win32;
using Windows.Win32.System.Threading;
-using YMouseButtonControl.Core.Services.Processes;
-namespace YMouseButtonControl.Windows.Services;
+namespace YMouseButtonControl.Core.Services.KeyboardAndMouse.Implementations.Queries.CurrentWindow;
[SupportedOSPlatform("windows5.1.2600")]
-public partial class CurrentWindowService(ILogger logger)
- : ICurrentWindowService
+public partial class GetCurrentWindowWindows(ILogger logger)
+ : IGetCurrentWindow
{
public string ForegroundWindow => GetWindowTitleFromHWnd();
diff --git a/YMouseButtonControl.Core/Services/KeyboardAndMouse/Implementations/Queries/CurrentWindow/IGetCurrentWindow.cs b/YMouseButtonControl.Core/Services/KeyboardAndMouse/Implementations/Queries/CurrentWindow/IGetCurrentWindow.cs
new file mode 100644
index 0000000..f198c20
--- /dev/null
+++ b/YMouseButtonControl.Core/Services/KeyboardAndMouse/Implementations/Queries/CurrentWindow/IGetCurrentWindow.cs
@@ -0,0 +1,6 @@
+namespace YMouseButtonControl.Core.Services.KeyboardAndMouse.Implementations.Queries.CurrentWindow;
+
+public interface IGetCurrentWindow
+{
+ string ForegroundWindow { get; }
+}
diff --git a/YMouseButtonControl.Core/Services/KeyboardAndMouse/Implementations/Queries/Profiles/ListProfiles.cs b/YMouseButtonControl.Core/Services/KeyboardAndMouse/Implementations/Queries/Profiles/ListProfiles.cs
new file mode 100644
index 0000000..461ce9b
--- /dev/null
+++ b/YMouseButtonControl.Core/Services/KeyboardAndMouse/Implementations/Queries/Profiles/ListProfiles.cs
@@ -0,0 +1,58 @@
+using System;
+using DynamicData;
+using DynamicData.Binding;
+using YMouseButtonControl.Core.Services.Profiles;
+using YMouseButtonControl.Core.ViewModels.Models;
+
+namespace YMouseButtonControl.Core.Services.KeyboardAndMouse.Implementations.Queries.Profiles;
+
+public static class ListProfiles
+{
+ public sealed class ButtonMappingForMouseListener(BaseButtonMappingVm vm)
+ {
+ public bool? BlockOriginalMouseInput = vm.BlockOriginalMouseInput;
+ }
+
+ public sealed class ProfileForMouseListener(ProfileVm vm)
+ {
+ public bool Checked { get; } = vm.Checked;
+ public string Process { get; } = vm.Process;
+ public int DisplayPriority = vm.DisplayPriority;
+ public ButtonMappingForMouseListener MouseButton1 { get; } = new(vm.MouseButton1);
+ public ButtonMappingForMouseListener MouseButton2 { get; } = new(vm.MouseButton2);
+ public ButtonMappingForMouseListener MouseButton3 { get; } = new(vm.MouseButton3);
+ public ButtonMappingForMouseListener MouseButton4 { get; } = new(vm.MouseButton4);
+ public ButtonMappingForMouseListener MouseButton5 { get; } = new(vm.MouseButton5);
+ public ButtonMappingForMouseListener MouseWheelUp { get; } = new(vm.MouseWheelUp);
+ public ButtonMappingForMouseListener MouseWheelDown { get; } = new(vm.MouseWheelDown);
+ public ButtonMappingForMouseListener MouseWheelLeft { get; } = new(vm.MouseWheelLeft);
+ public ButtonMappingForMouseListener MouseWheelRight { get; } = new(vm.MouseWheelRight);
+ }
+
+ public sealed class Handler(IProfilesCache profilesCache) : IDisposable
+ {
+ private IDisposable? _disposable;
+
+ public void Dispose()
+ {
+ _disposable?.Dispose();
+ }
+
+ public System.Collections.ObjectModel.ReadOnlyObservableCollection Execute()
+ {
+ _disposable = profilesCache
+ .ProfilesSc.Connect()
+ .AutoRefresh()
+ .Transform(x => new ProfileForMouseListener(x))
+ .SortAndBind(
+ out var profiles,
+ SortExpressionComparer.Ascending(x =>
+ x.DisplayPriority
+ )
+ )
+ .DisposeMany()
+ .Subscribe();
+ return profiles;
+ }
+ }
+}
diff --git a/YMouseButtonControl.Core/Services/KeyboardAndMouse/Interfaces/ISkipProfileService.cs b/YMouseButtonControl.Core/Services/KeyboardAndMouse/Implementations/Queries/SkipProfile/ISkipProfile.cs
similarity index 60%
rename from YMouseButtonControl.Core/Services/KeyboardAndMouse/Interfaces/ISkipProfileService.cs
rename to YMouseButtonControl.Core/Services/KeyboardAndMouse/Implementations/Queries/SkipProfile/ISkipProfile.cs
index b2c928c..6812d66 100644
--- a/YMouseButtonControl.Core/Services/KeyboardAndMouse/Interfaces/ISkipProfileService.cs
+++ b/YMouseButtonControl.Core/Services/KeyboardAndMouse/Implementations/Queries/SkipProfile/ISkipProfile.cs
@@ -1,9 +1,9 @@
using YMouseButtonControl.Core.Services.KeyboardAndMouse.EventArgs;
using YMouseButtonControl.Core.ViewModels.Models;
-namespace YMouseButtonControl.Core.Services.KeyboardAndMouse.Interfaces;
+namespace YMouseButtonControl.Core.Services.KeyboardAndMouse.Implementations.Queries.SkipProfile;
-public interface ISkipProfileService
+public interface ISkipProfile
{
bool ShouldSkipProfile(ProfileVm p, NewMouseHookEventArgs e);
}
diff --git a/YMouseButtonControl.MacOS/Services/SkipProfileService.cs b/YMouseButtonControl.Core/Services/KeyboardAndMouse/Implementations/Queries/SkipProfile/SkipProfileOsx.cs
similarity index 71%
rename from YMouseButtonControl.MacOS/Services/SkipProfileService.cs
rename to YMouseButtonControl.Core/Services/KeyboardAndMouse/Implementations/Queries/SkipProfile/SkipProfileOsx.cs
index 4240d45..c57e452 100644
--- a/YMouseButtonControl.MacOS/Services/SkipProfileService.cs
+++ b/YMouseButtonControl.Core/Services/KeyboardAndMouse/Implementations/Queries/SkipProfile/SkipProfileOsx.cs
@@ -1,15 +1,12 @@
using Microsoft.Extensions.Logging;
using YMouseButtonControl.Core.Services.KeyboardAndMouse.EventArgs;
-using YMouseButtonControl.Core.Services.KeyboardAndMouse.Interfaces;
-using YMouseButtonControl.Core.Services.Processes;
+using YMouseButtonControl.Core.Services.KeyboardAndMouse.Implementations.Queries.CurrentWindow;
using YMouseButtonControl.Core.ViewModels.Models;
-namespace YMouseButtonControl.MacOS.Services;
+namespace YMouseButtonControl.Core.Services.KeyboardAndMouse.Implementations.Queries.SkipProfile;
-public partial class SkipProfileService(
- ILogger logger,
- ICurrentWindowService currentWindowService
-) : ISkipProfileService
+public partial class SkipProfileOsx(ILogger logger, IGetCurrentWindow currentWindow)
+ : ISkipProfile
{
// Returns whether this profile should be skipped on mouse events
public bool ShouldSkipProfile(ProfileVm p, NewMouseHookEventArgs e)
@@ -26,12 +23,12 @@ public bool ShouldSkipProfile(ProfileVm p, NewMouseHookEventArgs e)
return false;
}
- if (currentWindowService.ForegroundWindow.Contains(p.Process))
+ if (currentWindow.ForegroundWindow.Contains(p.Process))
{
return false;
}
- LogForegroundWindow(logger, currentWindowService.ForegroundWindow);
+ LogForegroundWindow(logger, currentWindow.ForegroundWindow);
LogCouldNotFindForegroundWindow(logger, p.Process);
return true;
}
diff --git a/YMouseButtonControl.Linux/Services/SkipProfileService.cs b/YMouseButtonControl.Core/Services/KeyboardAndMouse/Implementations/Queries/SkipProfile/SkipProfileService.cs
similarity index 81%
rename from YMouseButtonControl.Linux/Services/SkipProfileService.cs
rename to YMouseButtonControl.Core/Services/KeyboardAndMouse/Implementations/Queries/SkipProfile/SkipProfileService.cs
index 30dee4d..7726dd0 100644
--- a/YMouseButtonControl.Linux/Services/SkipProfileService.cs
+++ b/YMouseButtonControl.Core/Services/KeyboardAndMouse/Implementations/Queries/SkipProfile/SkipProfileService.cs
@@ -1,15 +1,14 @@
using Microsoft.Extensions.Logging;
using YMouseButtonControl.Core.Services.KeyboardAndMouse.EventArgs;
-using YMouseButtonControl.Core.Services.KeyboardAndMouse.Interfaces;
-using YMouseButtonControl.Core.Services.Processes;
+using YMouseButtonControl.Core.Services.KeyboardAndMouse.Implementations.Queries.CurrentWindow;
using YMouseButtonControl.Core.ViewModels.Models;
-namespace YMouseButtonControl.Linux.Services;
+namespace YMouseButtonControl.Core.Services.KeyboardAndMouse.Implementations.Queries.SkipProfile;
-public partial class SkipProfileService(
- ILogger logger,
- ICurrentWindowService currentWindowService
-) : ISkipProfileService
+public partial class SkipProfileLinux(
+ ILogger logger,
+ IGetCurrentWindow currentWindowService
+) : ISkipProfile
{
// Returns whether this profile should be skipped on mouse events
public bool ShouldSkipProfile(ProfileVm p, NewMouseHookEventArgs e)
diff --git a/YMouseButtonControl.Windows/Services/SkipProfileService.cs b/YMouseButtonControl.Core/Services/KeyboardAndMouse/Implementations/Queries/SkipProfile/SkipProfileWindows.cs
similarity index 77%
rename from YMouseButtonControl.Windows/Services/SkipProfileService.cs
rename to YMouseButtonControl.Core/Services/KeyboardAndMouse/Implementations/Queries/SkipProfile/SkipProfileWindows.cs
index c7572e7..63f72c8 100644
--- a/YMouseButtonControl.Windows/Services/SkipProfileService.cs
+++ b/YMouseButtonControl.Core/Services/KeyboardAndMouse/Implementations/Queries/SkipProfile/SkipProfileWindows.cs
@@ -1,13 +1,12 @@
using System.Runtime.Versioning;
using Microsoft.Extensions.Logging;
using YMouseButtonControl.Core.Services.KeyboardAndMouse.EventArgs;
-using YMouseButtonControl.Core.Services.KeyboardAndMouse.Interfaces;
using YMouseButtonControl.Core.ViewModels.Models;
-namespace YMouseButtonControl.Windows.Services;
+namespace YMouseButtonControl.Core.Services.KeyboardAndMouse.Implementations.Queries.SkipProfile;
[SupportedOSPlatform("windows5.1.2600")]
-public partial class SkipProfileService(ILogger logger) : ISkipProfileService
+public partial class SkipProfileWindows(ILogger logger) : ISkipProfile
{
public bool ShouldSkipProfile(ProfileVm p, NewMouseHookEventArgs e)
{
diff --git a/YMouseButtonControl.Core/Services/KeyboardAndMouse/Implementations/SimulatedKeystrokesTypes/AsMouseButtonReleasedService.cs b/YMouseButtonControl.Core/Services/KeyboardAndMouse/Implementations/SimulatedKeystrokesTypes/AsMouseButtonReleasedService.cs
index c090230..e06449e 100644
--- a/YMouseButtonControl.Core/Services/KeyboardAndMouse/Implementations/SimulatedKeystrokesTypes/AsMouseButtonReleasedService.cs
+++ b/YMouseButtonControl.Core/Services/KeyboardAndMouse/Implementations/SimulatedKeystrokesTypes/AsMouseButtonReleasedService.cs
@@ -1,5 +1,4 @@
using YMouseButtonControl.Core.Services.KeyboardAndMouse.Enums;
-using YMouseButtonControl.Core.Services.KeyboardAndMouse.Interfaces;
using YMouseButtonControl.Core.ViewModels.Models;
namespace YMouseButtonControl.Core.Services.KeyboardAndMouse.Implementations.SimulatedKeystrokesTypes;
diff --git a/YMouseButtonControl.Core/Services/KeyboardAndMouse/Implementations/SimulatedKeystrokesTypes/DuringMousePressAndReleaseService.cs b/YMouseButtonControl.Core/Services/KeyboardAndMouse/Implementations/SimulatedKeystrokesTypes/DuringMousePressAndReleaseService.cs
index 9350b58..697a002 100644
--- a/YMouseButtonControl.Core/Services/KeyboardAndMouse/Implementations/SimulatedKeystrokesTypes/DuringMousePressAndReleaseService.cs
+++ b/YMouseButtonControl.Core/Services/KeyboardAndMouse/Implementations/SimulatedKeystrokesTypes/DuringMousePressAndReleaseService.cs
@@ -1,6 +1,5 @@
using System;
using YMouseButtonControl.Core.Services.KeyboardAndMouse.Enums;
-using YMouseButtonControl.Core.Services.KeyboardAndMouse.Interfaces;
using YMouseButtonControl.Core.ViewModels.Models;
namespace YMouseButtonControl.Core.Services.KeyboardAndMouse.Implementations.SimulatedKeystrokesTypes;
diff --git a/YMouseButtonControl.Core/Services/KeyboardAndMouse/Implementations/SimulatedKeystrokesTypes/StickyHoldService.cs b/YMouseButtonControl.Core/Services/KeyboardAndMouse/Implementations/SimulatedKeystrokesTypes/StickyHoldService.cs
index 99e037e..1919d82 100644
--- a/YMouseButtonControl.Core/Services/KeyboardAndMouse/Implementations/SimulatedKeystrokesTypes/StickyHoldService.cs
+++ b/YMouseButtonControl.Core/Services/KeyboardAndMouse/Implementations/SimulatedKeystrokesTypes/StickyHoldService.cs
@@ -1,5 +1,4 @@
using YMouseButtonControl.Core.Services.KeyboardAndMouse.Enums;
-using YMouseButtonControl.Core.Services.KeyboardAndMouse.Interfaces;
using YMouseButtonControl.Core.ViewModels.Models;
namespace YMouseButtonControl.Core.Services.KeyboardAndMouse.Implementations.SimulatedKeystrokesTypes;
diff --git a/YMouseButtonControl.Core/Services/KeyboardAndMouse/Implementations/SimulatedMousePressTypes/RightClick.cs b/YMouseButtonControl.Core/Services/KeyboardAndMouse/Implementations/SimulatedMousePressTypes/RightClick.cs
index 57a731b..42dfc72 100644
--- a/YMouseButtonControl.Core/Services/KeyboardAndMouse/Implementations/SimulatedMousePressTypes/RightClick.cs
+++ b/YMouseButtonControl.Core/Services/KeyboardAndMouse/Implementations/SimulatedMousePressTypes/RightClick.cs
@@ -2,7 +2,6 @@
using System.Threading;
using SharpHook.Native;
using YMouseButtonControl.Core.Services.KeyboardAndMouse.Enums;
-using YMouseButtonControl.Core.Services.KeyboardAndMouse.Interfaces;
namespace YMouseButtonControl.Core.Services.KeyboardAndMouse.Implementations.SimulatedMousePressTypes;
diff --git a/YMouseButtonControl.Core/Services/KeyboardAndMouse/KeyboardSimulatorWorker.cs b/YMouseButtonControl.Core/Services/KeyboardAndMouse/KeyboardSimulatorWorker.cs
index e383383..88772aa 100644
--- a/YMouseButtonControl.Core/Services/KeyboardAndMouse/KeyboardSimulatorWorker.cs
+++ b/YMouseButtonControl.Core/Services/KeyboardAndMouse/KeyboardSimulatorWorker.cs
@@ -3,9 +3,9 @@
using YMouseButtonControl.Core.Services.KeyboardAndMouse.Enums;
using YMouseButtonControl.Core.Services.KeyboardAndMouse.EventArgs;
using YMouseButtonControl.Core.Services.KeyboardAndMouse.Implementations;
+using YMouseButtonControl.Core.Services.KeyboardAndMouse.Implementations.Queries.SkipProfile;
using YMouseButtonControl.Core.Services.KeyboardAndMouse.Implementations.SimulatedKeystrokesTypes;
using YMouseButtonControl.Core.Services.KeyboardAndMouse.Implementations.SimulatedMousePressTypes;
-using YMouseButtonControl.Core.Services.KeyboardAndMouse.Interfaces;
using YMouseButtonControl.Core.Services.Profiles;
using YMouseButtonControl.Core.ViewModels.Models;
@@ -13,9 +13,9 @@ namespace YMouseButtonControl.Core.Services.KeyboardAndMouse;
public partial class KeyboardSimulatorWorker(
ILogger logger,
- IProfilesService profilesService,
+ IProfilesCache profilesService,
IMouseListener mouseListener,
- ISkipProfileService skipProfileService,
+ ISkipProfile skipProfileService,
IAsMouseButtonPressedService asMouseButtonPressedService,
IAsMouseButtonReleasedService asMouseButtonReleasedService,
IDuringMousePressAndReleaseService duringMousePressAndReleaseService,
diff --git a/YMouseButtonControl.Core/Services/Logging/EnableLoggingService.cs b/YMouseButtonControl.Core/Services/Logging/EnableLoggingService.cs
deleted file mode 100644
index 46d2b0b..0000000
--- a/YMouseButtonControl.Core/Services/Logging/EnableLoggingService.cs
+++ /dev/null
@@ -1,102 +0,0 @@
-using System;
-using System.IO;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
-
-namespace YMouseButtonControl.Core.Services.Logging;
-
-public interface IEnableLoggingService
-{
- void EnableLogging();
- void DisableLogging();
-
- ///
- /// True = enabled
- /// False = disabled
- ///
- ///
- ///
- bool GetLoggingState();
-}
-
-public class EnableLoggingService : IEnableLoggingService
-{
- private readonly string _pathToAppSettings = Path.Join(
- AppContext.BaseDirectory,
- "appsettings.json"
- );
-
- ///
- /// True = enabled
- /// False = disabled
- ///
- ///
- ///
- public bool GetLoggingState()
- {
- if (!File.Exists(_pathToAppSettings))
- {
- throw new Exception($"Appsettings file not found: {_pathToAppSettings}");
- }
- var json =
- JsonConvert.DeserializeObject(File.ReadAllText(_pathToAppSettings))
- ?? throw new Exception($"Error deserializing {_pathToAppSettings}");
- return json.GetValue("Logging") != null;
- }
-
- public void EnableLogging()
- {
- if (!File.Exists(_pathToAppSettings))
- {
- throw new Exception($"Appsettings file not found: {_pathToAppSettings}");
- }
- var json =
- JsonConvert.DeserializeObject(File.ReadAllText(_pathToAppSettings))
- ?? throw new Exception($"Error deserializing {_pathToAppSettings}");
- var toAdd = new JProperty(
- "Logging",
- new JObject(
- new JProperty(
- "LogLevel",
- new JObject(
- new JProperty("Default", "Debug"),
- new JProperty("System", "Information"),
- new JProperty("Microsoft", "Error")
- )
- ),
- new JProperty(
- "File",
- new JObject(
- new JProperty("Path", "YMouseButtonControl.log"),
- new JProperty("Append", true),
- new JProperty("MinLevel", "Information"),
- new JProperty("FileSizeLimitBytes", 0),
- new JProperty("MaxRollingFiles", 0)
- )
- )
- )
- );
- json.Add(toAdd);
- File.WriteAllText(
- _pathToAppSettings,
- JsonConvert.SerializeObject(json, Formatting.Indented)
- );
- }
-
- public void DisableLogging()
- {
- if (!File.Exists(_pathToAppSettings))
- {
- throw new Exception($"Appsettings file not found: {_pathToAppSettings}");
- }
-
- var json =
- JsonConvert.DeserializeObject(File.ReadAllText(_pathToAppSettings))
- ?? throw new Exception($"Error deserializing {_pathToAppSettings}");
- json.Remove("Logging");
- File.WriteAllText(
- _pathToAppSettings,
- JsonConvert.SerializeObject(json, Formatting.Indented)
- );
- }
-}
diff --git a/YMouseButtonControl.Core/Services/Processes/ICurrentWindowService.cs b/YMouseButtonControl.Core/Services/Processes/ICurrentWindowService.cs
deleted file mode 100644
index bd0d5fb..0000000
--- a/YMouseButtonControl.Core/Services/Processes/ICurrentWindowService.cs
+++ /dev/null
@@ -1,6 +0,0 @@
-namespace YMouseButtonControl.Core.Services.Processes;
-
-public interface ICurrentWindowService
-{
- string ForegroundWindow { get; }
-}
diff --git a/YMouseButtonControl.Core/Services/Processes/IProcessMonitorService.cs b/YMouseButtonControl.Core/Services/Processes/IProcessMonitorService.cs
deleted file mode 100644
index 89ffa37..0000000
--- a/YMouseButtonControl.Core/Services/Processes/IProcessMonitorService.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-using System.Collections.Generic;
-
-namespace YMouseButtonControl.Core.Services.Processes;
-
-public interface IProcessMonitorService
-{
- IEnumerable GetProcesses();
-}
diff --git a/YMouseButtonControl.Core/Services/Processes/ProcessModel.cs b/YMouseButtonControl.Core/Services/Processes/ProcessModel.cs
deleted file mode 100644
index ee6ff13..0000000
--- a/YMouseButtonControl.Core/Services/Processes/ProcessModel.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-using System;
-using System.Diagnostics;
-using System.IO;
-using ReactiveUI;
-
-namespace YMouseButtonControl.Core.Services.Processes;
-
-public class ProcessModel(Process process) : ReactiveObject, IDisposable
-{
- public Process Process { get; } = process;
- public Stream? Bitmap { get; set; }
-
- public void Dispose()
- {
- Process.Dispose();
- Bitmap?.Dispose();
- }
-}
diff --git a/YMouseButtonControl.Core/Services/Profiles/Exceptions/InvalidMoveException.cs b/YMouseButtonControl.Core/Services/Profiles/Exceptions/InvalidMoveException.cs
deleted file mode 100644
index 732c5f1..0000000
--- a/YMouseButtonControl.Core/Services/Profiles/Exceptions/InvalidMoveException.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-using System;
-
-namespace YMouseButtonControl.Core.Services.Profiles.Exceptions;
-
-public class InvalidMoveException : Exception
-{
- public InvalidMoveException() { }
-
- public InvalidMoveException(string message)
- : base(message) { }
-
- public InvalidMoveException(string message, Exception innerException)
- : base(message, innerException) { }
-}
diff --git a/YMouseButtonControl.Core/Services/Profiles/Exceptions/InvalidReplaceException.cs b/YMouseButtonControl.Core/Services/Profiles/Exceptions/InvalidReplaceException.cs
deleted file mode 100644
index f7ea706..0000000
--- a/YMouseButtonControl.Core/Services/Profiles/Exceptions/InvalidReplaceException.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-using System;
-
-namespace YMouseButtonControl.Core.Services.Profiles.Exceptions;
-
-public class InvalidReplaceException : Exception
-{
- public InvalidReplaceException() { }
-
- public InvalidReplaceException(string message)
- : base(message) { }
-
- public InvalidReplaceException(string message, Exception innerException)
- : base(message, innerException) { }
-}
diff --git a/YMouseButtonControl.Core/Services/Profiles/ProfilesCache.cs b/YMouseButtonControl.Core/Services/Profiles/ProfilesCache.cs
new file mode 100644
index 0000000..59374ca
--- /dev/null
+++ b/YMouseButtonControl.Core/Services/Profiles/ProfilesCache.cs
@@ -0,0 +1,52 @@
+using System;
+using System.Collections.ObjectModel;
+using System.Linq;
+using DynamicData;
+using ReactiveUI;
+using YMouseButtonControl.Core.Services.Profiles.Queries.Profiles;
+using YMouseButtonControl.Core.ViewModels.Models;
+
+namespace YMouseButtonControl.Core.Services.Profiles;
+
+public interface IProfilesCache
+{
+ SourceCache ProfilesSc { get; }
+ ProfileVm? CurrentProfile { get; set; }
+ ReadOnlyObservableCollection Profiles { get; }
+ IObservable> Connect();
+}
+
+public class ProfilesCache : ReactiveObject, IProfilesCache, IDisposable
+{
+ private ProfileVm? _currentProfile;
+ private readonly SourceCache _profilesSc;
+ private readonly ReadOnlyObservableCollection _profilesObsCol;
+
+ public ProfilesCache(ListDbProfiles.Handler listDbProfilesHandler)
+ {
+ _profilesSc = new SourceCache(x => x.Id);
+ _profilesSc.Connect().AutoRefresh().Bind(out _profilesObsCol).Subscribe();
+ _profilesSc.AddOrUpdate(listDbProfilesHandler.Execute());
+ CurrentProfile ??= Profiles.FirstOrDefault();
+ }
+
+ ///
+ /// Read only collection of profiles
+ ///
+ public ReadOnlyObservableCollection Profiles => _profilesObsCol;
+
+ public IObservable> Connect() => _profilesSc.Connect();
+
+ public ProfileVm? CurrentProfile
+ {
+ get => _currentProfile;
+ set => this.RaiseAndSetIfChanged(ref _currentProfile, value);
+ }
+
+ public SourceCache ProfilesSc => _profilesSc;
+
+ public void Dispose()
+ {
+ _profilesSc.Dispose();
+ }
+}
diff --git a/YMouseButtonControl.Core/Services/Profiles/ProfilesService.cs b/YMouseButtonControl.Core/Services/Profiles/ProfilesService.cs
deleted file mode 100644
index c8e364c..0000000
--- a/YMouseButtonControl.Core/Services/Profiles/ProfilesService.cs
+++ /dev/null
@@ -1,207 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Collections.ObjectModel;
-using System.IO;
-using System.Linq;
-using System.Reactive.Linq;
-using Avalonia.Media.TextFormatting.Unicode;
-using DynamicData;
-using DynamicData.Binding;
-using Newtonsoft.Json;
-using ReactiveUI;
-using YMouseButtonControl.Core.Repositories;
-using YMouseButtonControl.Core.Services.Profiles.Exceptions;
-using YMouseButtonControl.Core.ViewModels.Models;
-using YMouseButtonControl.DataAccess.Models;
-
-namespace YMouseButtonControl.Core.Services.Profiles;
-
-public interface IProfilesService
-{
- ProfileVm? CurrentProfile { get; set; }
- ReadOnlyObservableCollection Profiles { get; }
- IObservable> Connect();
- ProfileVm CopyProfile(ProfileVm p);
- void WriteProfileToFile(ProfileVm p, string path);
- void ImportProfileFromPath(string path);
- void AddProfile(ProfileVm profileVm);
- void ReplaceProfile(ProfileVm oldProfileVm, ProfileVm newProfileVm);
- void MoveProfileUp(ProfileVm p);
- void MoveProfileDown(ProfileVm p);
- void RemoveProfile(ProfileVm profileVm);
- void AddOrUpdate(ProfileVm profileVm);
-}
-
-public class ProfilesService : ReactiveObject, IProfilesService, IDisposable
-{
- private readonly IRepository _profileRepository;
- private ProfileVm? _currentProfile;
- private readonly SourceCache _profiles;
- private readonly ReadOnlyObservableCollection _profilesObsCol;
-
- public ProfilesService(IRepository profileRepository)
- {
- _profileRepository = profileRepository;
- _profiles = new SourceCache(x => x.Id);
- _profiles
- .Connect()
- .AutoRefresh()
- .SortBy(x => x.DisplayPriority)
- .Bind(out _profilesObsCol)
- .Subscribe();
- _profiles.AddOrUpdate(profileRepository.GetAll().ToList());
- }
-
- ///
- /// Read only collection of profiles
- ///
- public ReadOnlyObservableCollection Profiles => _profilesObsCol;
-
- public void AddOrUpdate(ProfileVm profile) => _profiles.AddOrUpdate(profile);
-
- public IObservable> Connect() => _profiles.Connect();
-
- public ProfileVm? CurrentProfile
- {
- get => _currentProfile;
- set => this.RaiseAndSetIfChanged(ref _currentProfile, value);
- }
-
- public ProfileVm CopyProfile(ProfileVm p)
- {
- var jsonString = JsonConvert.SerializeObject(
- p,
- new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto }
- );
- return JsonConvert.DeserializeObject(
- jsonString,
- new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto }
- ) ?? throw new JsonSerializationException("Error deserializing profile");
- }
-
- public bool IsUnsavedChanges()
- {
- var dbProfiles = _profileRepository.GetAll().ToList();
- return !dbProfiles.SequenceEqual(_profiles.Items);
- }
-
- public void WriteProfileToFile(ProfileVm p, string path)
- {
- var jsonString = JsonConvert.SerializeObject(
- p,
- new JsonSerializerSettings
- {
- TypeNameHandling = TypeNameHandling.Auto,
- Formatting = Formatting.Indented,
- }
- );
- File.WriteAllText(path, jsonString);
- }
-
- public void ImportProfileFromPath(string path)
- {
- var f = File.ReadAllText(path);
- var deserializedProfile =
- JsonConvert.DeserializeObject(
- f,
- new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto }
- ) ?? throw new JsonSerializationException("Error deserializing profile");
- AddProfile(deserializedProfile);
- }
-
- public void AddProfile(ProfileVm profile)
- {
- _profiles.AddOrUpdate(profile);
- }
-
- public void ReplaceProfile(ProfileVm oldProfile, ProfileVm newProfile)
- {
- if (oldProfile.IsDefault || newProfile.IsDefault)
- {
- throw new InvalidReplaceException("Cannot replace the default profile");
- }
-
- newProfile.Id = oldProfile.Id;
- _profiles.AddOrUpdate(newProfile);
- }
-
- public void MoveProfileUp(ProfileVm p)
- {
- if (p.IsDefault)
- {
- throw new InvalidMoveException("Cannot move the default profile");
- }
-
- _profiles.Edit(updater =>
- {
- var nextSmaller = _profiles
- .Items.Where(x => x.DisplayPriority < p.DisplayPriority)
- .MaxBy(x => x.DisplayPriority);
- if (nextSmaller is null)
- {
- throw new InvalidMoveException(
- "Unable to retrieve max display index from profiles cache"
- );
- }
-
- if (nextSmaller.IsDefault)
- {
- throw new InvalidMoveException("Cannot move profile above default profile");
- }
-
- // swap priorities
- (nextSmaller.DisplayPriority, p.DisplayPriority) = (
- p.DisplayPriority,
- nextSmaller.DisplayPriority
- );
- });
- }
-
- public void MoveProfileDown(ProfileVm p)
- {
- if (p.IsDefault)
- {
- throw new InvalidMoveException("Cannot move the default profile");
- }
-
- var nextLargerPriority = _profiles
- .Items.Where(x => x.DisplayPriority > p.DisplayPriority)
- .MinBy(x => x.DisplayPriority);
- if (nextLargerPriority is null)
- {
- throw new InvalidMoveException(
- "Unable to retrieve max display index from profiles cache"
- );
- }
-
- // swap priorities
- (nextLargerPriority.DisplayPriority, p.DisplayPriority) = (
- p.DisplayPriority,
- nextLargerPriority.DisplayPriority
- );
- }
-
- public void RemoveProfile(ProfileVm profile)
- {
- if (profile.IsDefault)
- {
- throw new Exception("Attempted to remove default profile");
- }
-
- var nextSmallerPriority = _profiles
- .Items.Where(x => x.DisplayPriority < profile.DisplayPriority)
- .MaxBy(x => x.DisplayPriority);
- if (nextSmallerPriority is null)
- {
- throw new Exception("Unable to find next profile to set as current profile");
- }
-
- _profiles.Remove(profile);
- CurrentProfile = nextSmallerPriority;
- }
-
- public void Dispose()
- {
- _profiles.Dispose();
- }
-}
diff --git a/YMouseButtonControl.Core/Services/Profiles/Queries/Profiles/ListDbProfiles.cs b/YMouseButtonControl.Core/Services/Profiles/Queries/Profiles/ListDbProfiles.cs
new file mode 100644
index 0000000..864f72c
--- /dev/null
+++ b/YMouseButtonControl.Core/Services/Profiles/Queries/Profiles/ListDbProfiles.cs
@@ -0,0 +1,22 @@
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.EntityFrameworkCore;
+using YMouseButtonControl.Core.Mappings;
+using YMouseButtonControl.Core.ViewModels.Models;
+using YMouseButtonControl.Infrastructure.Context;
+
+namespace YMouseButtonControl.Core.Services.Profiles.Queries.Profiles;
+
+public static class ListDbProfiles
+{
+ public sealed class Handler(YMouseButtonControlDbContext db)
+ {
+ public List Execute() =>
+ db
+ .Profiles.AsNoTracking()
+ .Include(x => x.ButtonMappings)
+ .ToList()
+ .Select(ProfileMapper.MapToViewModel)
+ .ToList();
+ }
+}
diff --git a/YMouseButtonControl.Core/Services/Settings/SettingsService.cs b/YMouseButtonControl.Core/Services/Settings/SettingsService.cs
deleted file mode 100644
index cadb738..0000000
--- a/YMouseButtonControl.Core/Services/Settings/SettingsService.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using ReactiveUI;
-using YMouseButtonControl.Core.Repositories;
-using YMouseButtonControl.Core.ViewModels.Models;
-using YMouseButtonControl.DataAccess.Models;
-
-namespace YMouseButtonControl.Core.Services.Settings;
-
-public interface ISettingsService
-{
- BaseSettingVm? GetSetting(string name);
- int UpdateSetting(BaseSettingVm vm);
-}
-
-public class SettingsService(IRepository settingRepository)
- : ReactiveObject,
- ISettingsService
-{
- public BaseSettingVm? GetSetting(string name) => settingRepository.GetByName(name);
-
- public int UpdateSetting(BaseSettingVm vm) => settingRepository.Update(vm);
-}
diff --git a/YMouseButtonControl.Core/Services/StartMenuInstaller/IStartMenuInstallerService.cs b/YMouseButtonControl.Core/Services/StartMenuInstaller/IStartMenuInstallerService.cs
deleted file mode 100644
index ff2ad4a..0000000
--- a/YMouseButtonControl.Core/Services/StartMenuInstaller/IStartMenuInstallerService.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-namespace YMouseButtonControl.Core.Services.StartMenuInstaller;
-
-public interface IStartMenuInstallerService
-{
- bool InstallStatus();
- void Install();
- void Uninstall();
-}
diff --git a/YMouseButtonControl.Core/Services/StartupInstaller/IStartupInstallerService.cs b/YMouseButtonControl.Core/Services/StartupInstaller/IStartupInstallerService.cs
deleted file mode 100644
index c45a998..0000000
--- a/YMouseButtonControl.Core/Services/StartupInstaller/IStartupInstallerService.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-namespace YMouseButtonControl.Core.Services.StartupInstaller;
-
-public interface IStartupInstallerService
-{
- public bool ButtonEnabled();
- public bool InstallStatus();
- public void Install();
- public void Uninstall();
-}
diff --git a/YMouseButtonControl.Core/Services/Theme/ThemeService.cs b/YMouseButtonControl.Core/Services/Theme/ThemeService.cs
deleted file mode 100644
index 60ab85b..0000000
--- a/YMouseButtonControl.Core/Services/Theme/ThemeService.cs
+++ /dev/null
@@ -1,146 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using Avalonia;
-using Avalonia.Media;
-using Avalonia.Styling;
-using ReactiveUI;
-using YMouseButtonControl.Core.Repositories;
-using YMouseButtonControl.Core.Services.Settings;
-using YMouseButtonControl.Core.ViewModels.Models;
-
-namespace YMouseButtonControl.Core.Services.Theme;
-
-public interface IThemeService
-{
- public IBrush Background { get; }
- public IBrush Highlight { get; }
- ThemeVariant ThemeVariant { get; }
- List Themes { get; }
-}
-
-public class ThemeService : ReactiveObject, IThemeService
-{
- private readonly IRepository _themeRepo;
- private readonly SettingIntVm _themeSetting;
- private readonly ThemeVm _themeVm;
- private IBrush _background;
- private IBrush _highlight;
- private readonly ThemeVariant _themeVariant;
-
- public ThemeService(
- IRepository themeRepo,
- ISettingsService settingsService
- )
- {
- _themeRepo = themeRepo;
- _themeSetting =
- settingsService.GetSetting("Theme") as SettingIntVm
- ?? throw new Exception("Error retrieving theme setting");
- _themeVm =
- _themeRepo.GetById(_themeSetting.IntValue)
- ?? throw new Exception($"Error retrieving theme with id: {_themeSetting.IntValue}");
- _themeVariant = GetThemeVariant();
- _background = GetBackground();
- _highlight = GetHighlight();
- }
-
- public List Themes => [.. _themeRepo.GetAll().OrderBy(x => x.Id)];
-
- public ThemeVariant ThemeVariant => _themeVariant;
-
- public IBrush Background
- {
- get => _background;
- set => this.RaiseAndSetIfChanged(ref _background, value);
- }
-
- public IBrush Highlight
- {
- get => _highlight;
- set => this.RaiseAndSetIfChanged(ref _highlight, value);
- }
-
- private IBrush GetBackground()
- {
- // Background is of the form #aarrggbb
- if (_themeVm.Background.StartsWith('#'))
- {
- return Brush.Parse(_themeVm.Background);
- }
-
- // Background is an avalonia resource like SystemAltHighColor
- if (
- Application.Current!.TryGetResource(
- _themeVm.Background,
- Application.Current.ActualThemeVariant,
- out var backgroundBrush
- )
- )
- {
- if (backgroundBrush is null)
- {
- throw new Exception("Error retrieving background brush");
- }
- var bbStr = backgroundBrush.ToString();
- if (string.IsNullOrWhiteSpace(bbStr))
- {
- throw new Exception("Error retrieving background brush");
- }
- var brush = Brush.Parse(bbStr);
- return brush;
- }
-
- // Background may be a color like White, Black, etc.
- return Brush.Parse(_themeVm.Background);
- }
-
- private IBrush GetHighlight()
- {
- // Highlight is of the form #aarrggbb
- if (_themeVm.Highlight.StartsWith('#'))
- {
- return Brush.Parse(_themeVm.Highlight);
- }
-
- // Highlight is an avalonia resource like SystemAltHighColor
- if (
- Application.Current!.TryGetResource(
- _themeVm.Highlight,
- Application.Current.ActualThemeVariant,
- out var highlightBrush
- )
- )
- {
- if (highlightBrush is null)
- {
- throw new Exception("Error retrieving highlight brush");
- }
- var bbStr = highlightBrush.ToString();
- if (string.IsNullOrWhiteSpace(bbStr))
- {
- throw new Exception("Error retrieving highlight brush");
- }
- var brush = Brush.Parse(bbStr);
- return brush;
- }
-
- // Highlight may be a color like White, Black, etc.
- return Brush.Parse(_themeVm.Highlight);
- }
-
- private ThemeVariant GetThemeVariant()
- {
- return _themeSetting.IntValue switch
- {
- 1 => Application.Current!.ActualThemeVariant == ThemeVariant.Light
- ? ThemeVariant.Light
- : ThemeVariant.Dark,
- 2 => ThemeVariant.Light,
- 3 => ThemeVariant.Dark,
- _ => throw new ArgumentOutOfRangeException(
- $"Invalid theme id: {_themeSetting.IntValue}"
- ),
- };
- }
-}
diff --git a/YMouseButtonControl.Core/ViewModels/App/AppHandlerRegistrations.cs b/YMouseButtonControl.Core/ViewModels/App/AppHandlerRegistrations.cs
new file mode 100644
index 0000000..f680ba4
--- /dev/null
+++ b/YMouseButtonControl.Core/ViewModels/App/AppHandlerRegistrations.cs
@@ -0,0 +1,41 @@
+using System.Runtime.Versioning;
+using Microsoft.Extensions.DependencyInjection;
+using YMouseButtonControl.Core.ViewModels.App.Commands.StartupInstaller.Install;
+using YMouseButtonControl.Core.ViewModels.App.Commands.StartupInstaller.Uninstall;
+using YMouseButtonControl.Core.ViewModels.App.Queries.StartupInstaller.CanBeInstalled;
+using YMouseButtonControl.Core.ViewModels.App.Queries.StartupInstaller.IsInstalled;
+
+namespace YMouseButtonControl.Core.ViewModels.App;
+
+public static class AppHandlerRegistrations
+{
+ public static void RegisterCommon(IServiceCollection services) { }
+
+ public static void RegisterLinux(IServiceCollection services)
+ {
+ services
+ .AddScoped()
+ .AddScoped()
+ .AddScoped()
+ .AddScoped();
+ }
+
+ public static void RegisterOsx(IServiceCollection services)
+ {
+ services
+ .AddScoped()
+ .AddScoped()
+ .AddScoped()
+ .AddScoped();
+ }
+
+ [SupportedOSPlatform("windows5.1.2600")]
+ public static void RegisterWindows(IServiceCollection services)
+ {
+ services
+ .AddScoped()
+ .AddScoped()
+ .AddScoped()
+ .AddScoped();
+ }
+}
diff --git a/YMouseButtonControl.Core/ViewModels/AppViewModel/AppViewModel.cs b/YMouseButtonControl.Core/ViewModels/App/AppViewModel.cs
similarity index 79%
rename from YMouseButtonControl.Core/ViewModels/AppViewModel/AppViewModel.cs
rename to YMouseButtonControl.Core/ViewModels/App/AppViewModel.cs
index 0fb38a9..d1e8a3c 100644
--- a/YMouseButtonControl.Core/ViewModels/AppViewModel/AppViewModel.cs
+++ b/YMouseButtonControl.Core/ViewModels/App/AppViewModel.cs
@@ -3,11 +3,14 @@
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using ReactiveUI;
-using YMouseButtonControl.Core.Services.StartupInstaller;
+using YMouseButtonControl.Core.ViewModels.App.Commands.StartupInstaller.Install;
+using YMouseButtonControl.Core.ViewModels.App.Commands.StartupInstaller.Uninstall;
+using YMouseButtonControl.Core.ViewModels.App.Queries.StartupInstaller.CanBeInstalled;
+using YMouseButtonControl.Core.ViewModels.App.Queries.StartupInstaller.IsInstalled;
using YMouseButtonControl.Core.ViewModels.MainWindow;
using YMouseButtonControl.Core.Views;
-namespace YMouseButtonControl.Core.ViewModels.AppViewModel;
+namespace YMouseButtonControl.Core.ViewModels.App;
public interface IAppViewModel;
@@ -21,13 +24,16 @@ public class AppViewModel : ViewModelBase, IAppViewModel
private string _runAtStartupHeader = "";
public AppViewModel(
- IStartupInstallerService startupInstallerService,
+ ICanBeInstalledHandler canBeInstalledHandler,
+ IIsInstalledHandler isInstalledHandler,
+ IInstallHandler installHandler,
+ IUninstallHandler uninstallHandler,
IMainWindow mainWindow,
IMainWindowViewModel mainWindowViewModel
)
{
- RunAtStartupIsEnabled = startupInstallerService.ButtonEnabled();
- RunAtStartupIsChecked = startupInstallerService.InstallStatus();
+ RunAtStartupIsEnabled = canBeInstalledHandler.Execute();
+ RunAtStartupIsChecked = isInstalledHandler.Execute();
RunAtStartupHeader = RunAtStartupIsChecked
? string.Format(RunAtStartupHeaderFmt, RunAtStartupChecked)
: string.Format(RunAtStartupHeaderFmt, RunAtStartupNotChecked);
@@ -61,10 +67,10 @@ is IClassicDesktopStyleApplicationLifetime lifetime
RunAtStartupCommand = ReactiveCommand.Create(
() =>
{
- if (startupInstallerService.InstallStatus())
+ if (isInstalledHandler.Execute())
{
// uninstall
- startupInstallerService.Uninstall();
+ uninstallHandler.Execute();
RunAtStartupIsChecked = false;
RunAtStartupHeader = string.Format(
RunAtStartupHeaderFmt,
@@ -74,7 +80,7 @@ is IClassicDesktopStyleApplicationLifetime lifetime
else
{
// install
- startupInstallerService.Install();
+ installHandler.Execute();
RunAtStartupIsChecked = true;
RunAtStartupHeader = string.Format(RunAtStartupHeaderFmt, RunAtStartupChecked);
}
diff --git a/YMouseButtonControl.Core/ViewModels/App/Commands/StartupInstaller/Install/IInstallHandler.cs b/YMouseButtonControl.Core/ViewModels/App/Commands/StartupInstaller/Install/IInstallHandler.cs
new file mode 100644
index 0000000..86ba661
--- /dev/null
+++ b/YMouseButtonControl.Core/ViewModels/App/Commands/StartupInstaller/Install/IInstallHandler.cs
@@ -0,0 +1,7 @@
+namespace YMouseButtonControl.Core.ViewModels.App.Commands.StartupInstaller.Install
+{
+ public interface IInstallHandler
+ {
+ void Execute();
+ }
+}
diff --git a/YMouseButtonControl.Core/ViewModels/App/Commands/StartupInstaller/Install/InstallLinux.cs b/YMouseButtonControl.Core/ViewModels/App/Commands/StartupInstaller/Install/InstallLinux.cs
new file mode 100644
index 0000000..dfddaef
--- /dev/null
+++ b/YMouseButtonControl.Core/ViewModels/App/Commands/StartupInstaller/Install/InstallLinux.cs
@@ -0,0 +1,46 @@
+using System;
+using System.IO;
+
+namespace YMouseButtonControl.Core.ViewModels.App.Commands.StartupInstaller.Install;
+
+public static class InstallLinux
+{
+ public sealed class Handler : IInstallHandler
+ {
+ private const string DesktopFile = """
+ [Desktop Entry]
+ Type=Application
+ Exec={0}
+ Path={1}
+ Hidden=false
+ NoDisplay=false
+ X-GNOME-Autostart-enabled=true
+ Name=YMouseButtonControl
+ Comment=YMouseButtonControl
+ """;
+ private readonly string _configDir = Environment.GetFolderPath(
+ Environment.SpecialFolder.ApplicationData
+ );
+ private readonly string _autostartDir;
+ private readonly string _desktopFilePath;
+
+ public Handler()
+ {
+ _autostartDir = Path.Combine(_configDir, "autostart");
+ _desktopFilePath = Path.Combine(_autostartDir, "YMouseButtonControl.desktop");
+ }
+
+ public void Execute() =>
+ File.WriteAllText(
+ _desktopFilePath,
+ string.Format(DesktopFile, GetCurExePath(), GetCurExeParentPath())
+ );
+
+ private static string GetCurExeParentPath() =>
+ Path.GetDirectoryName(GetCurExePath())
+ ?? throw new Exception("Error retrieving parent of process path");
+
+ private static string GetCurExePath() =>
+ Environment.ProcessPath ?? throw new Exception("Error retrieving process path");
+ }
+}
diff --git a/YMouseButtonControl.Core/ViewModels/App/Commands/StartupInstaller/Install/InstallOsx.cs b/YMouseButtonControl.Core/ViewModels/App/Commands/StartupInstaller/Install/InstallOsx.cs
new file mode 100644
index 0000000..9fd0825
--- /dev/null
+++ b/YMouseButtonControl.Core/ViewModels/App/Commands/StartupInstaller/Install/InstallOsx.cs
@@ -0,0 +1,51 @@
+using System;
+using System.IO;
+
+namespace YMouseButtonControl.Core.ViewModels.App.Commands.StartupInstaller.Install;
+
+public static class InstallOsx
+{
+ public sealed class Handler : IInstallHandler
+ {
+ private const string PlistData = """
+
+
+
+
+ Label
+ com.github.ymousebuttoncontrol
+
+ ProgramArguments
+
+ {0}
+
+
+ RunAtLoad
+
+
+ KeepAlive
+
+
+
+ """;
+
+ private readonly string _usrLaunchAgentsDir;
+ private readonly string _plistPath;
+
+ public Handler()
+ {
+ _usrLaunchAgentsDir = Path.Join(
+ Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
+ "Library",
+ "LaunchAgents"
+ );
+ _plistPath = Path.Join(_usrLaunchAgentsDir, "com.github.ymousebuttoncontrol.plist");
+ }
+
+ public void Execute() =>
+ File.WriteAllText(_plistPath, string.Format(PlistData, GetCurExePath()));
+
+ private static string GetCurExePath() =>
+ Environment.ProcessPath ?? throw new Exception("Error retrieving process path");
+ }
+}
diff --git a/YMouseButtonControl.Core/ViewModels/App/Commands/StartupInstaller/Install/InstallWindows.cs b/YMouseButtonControl.Core/ViewModels/App/Commands/StartupInstaller/Install/InstallWindows.cs
new file mode 100644
index 0000000..dc4109b
--- /dev/null
+++ b/YMouseButtonControl.Core/ViewModels/App/Commands/StartupInstaller/Install/InstallWindows.cs
@@ -0,0 +1,32 @@
+using System;
+using System.IO;
+using System.Runtime.Versioning;
+using System.Security.AccessControl;
+using Microsoft.Win32;
+
+namespace YMouseButtonControl.Core.ViewModels.App.Commands.StartupInstaller.Install;
+
+[SupportedOSPlatform("windows")]
+public static class InstallWindows
+{
+ public sealed class Handler : IInstallHandler
+ {
+ private const string BaseKeyPath = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Run\";
+ private const string ValName = "YMouseButtonControl";
+
+ public void Execute()
+ {
+ using var key =
+ Registry.CurrentUser.OpenSubKey(
+ BaseKeyPath,
+ RegistryKeyPermissionCheck.ReadWriteSubTree,
+ RegistryRights.FullControl
+ ) ?? throw new Exception($"Error opening key {BaseKeyPath}");
+ var newKeyVal = $"\"{Path.Join(GetCurExePath())}\"";
+ key.SetValue(ValName, newKeyVal);
+ }
+
+ private static string GetCurExePath() =>
+ Environment.ProcessPath ?? throw new Exception("Error retrieving process path");
+ }
+}
diff --git a/YMouseButtonControl.Core/ViewModels/App/Commands/StartupInstaller/Uninstall/IUninstallHandler.cs b/YMouseButtonControl.Core/ViewModels/App/Commands/StartupInstaller/Uninstall/IUninstallHandler.cs
new file mode 100644
index 0000000..5cd282a
--- /dev/null
+++ b/YMouseButtonControl.Core/ViewModels/App/Commands/StartupInstaller/Uninstall/IUninstallHandler.cs
@@ -0,0 +1,7 @@
+namespace YMouseButtonControl.Core.ViewModels.App.Commands.StartupInstaller.Uninstall
+{
+ public interface IUninstallHandler
+ {
+ void Execute();
+ }
+}
diff --git a/YMouseButtonControl.Core/ViewModels/App/Commands/StartupInstaller/Uninstall/UninstallLinux.cs b/YMouseButtonControl.Core/ViewModels/App/Commands/StartupInstaller/Uninstall/UninstallLinux.cs
new file mode 100644
index 0000000..6508440
--- /dev/null
+++ b/YMouseButtonControl.Core/ViewModels/App/Commands/StartupInstaller/Uninstall/UninstallLinux.cs
@@ -0,0 +1,24 @@
+using System;
+using System.IO;
+
+namespace YMouseButtonControl.Core.ViewModels.App.Commands.StartupInstaller.Uninstall;
+
+public static class UninstallLinux
+{
+ public sealed class Handler : IUninstallHandler
+ {
+ private readonly string _configDir = Environment.GetFolderPath(
+ Environment.SpecialFolder.ApplicationData
+ );
+ private readonly string _autostartDir;
+ private readonly string _desktopFilePath;
+
+ public Handler()
+ {
+ _autostartDir = Path.Combine(_configDir, "autostart");
+ _desktopFilePath = Path.Combine(_autostartDir, "YMouseButtonControl.desktop");
+ }
+
+ public void Execute() => File.Delete(_desktopFilePath);
+ }
+}
diff --git a/YMouseButtonControl.Core/ViewModels/App/Commands/StartupInstaller/Uninstall/UninstallOsx.cs b/YMouseButtonControl.Core/ViewModels/App/Commands/StartupInstaller/Uninstall/UninstallOsx.cs
new file mode 100644
index 0000000..7081e9f
--- /dev/null
+++ b/YMouseButtonControl.Core/ViewModels/App/Commands/StartupInstaller/Uninstall/UninstallOsx.cs
@@ -0,0 +1,25 @@
+using System;
+using System.IO;
+
+namespace YMouseButtonControl.Core.ViewModels.App.Commands.StartupInstaller.Uninstall;
+
+public static class UninstallOsx
+{
+ public sealed class Handler : IUninstallHandler
+ {
+ private readonly string _usrLaunchAgentsDir;
+ private readonly string _plistPath;
+
+ public Handler()
+ {
+ _usrLaunchAgentsDir = Path.Join(
+ Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
+ "Library",
+ "LaunchAgents"
+ );
+ _plistPath = Path.Join(_usrLaunchAgentsDir, "com.github.ymousebuttoncontrol.plist");
+ }
+
+ public void Execute() => File.Delete(_plistPath);
+ }
+}
diff --git a/YMouseButtonControl.Core/ViewModels/App/Commands/StartupInstaller/Uninstall/UninstallWindows.cs b/YMouseButtonControl.Core/ViewModels/App/Commands/StartupInstaller/Uninstall/UninstallWindows.cs
new file mode 100644
index 0000000..8057f2f
--- /dev/null
+++ b/YMouseButtonControl.Core/ViewModels/App/Commands/StartupInstaller/Uninstall/UninstallWindows.cs
@@ -0,0 +1,27 @@
+using System;
+using System.Runtime.Versioning;
+using System.Security.AccessControl;
+using Microsoft.Win32;
+
+namespace YMouseButtonControl.Core.ViewModels.App.Commands.StartupInstaller.Uninstall;
+
+[SupportedOSPlatform("windows")]
+public static class UninstallWindows
+{
+ public sealed class Handler : IUninstallHandler
+ {
+ private const string BaseKeyPath = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Run\";
+ private const string ValName = "YMouseButtonControl";
+
+ public void Execute()
+ {
+ using var key =
+ Registry.CurrentUser.OpenSubKey(
+ BaseKeyPath,
+ RegistryKeyPermissionCheck.ReadWriteSubTree,
+ RegistryRights.FullControl
+ ) ?? throw new Exception($"Error opening key {BaseKeyPath}");
+ key.DeleteValue(ValName);
+ }
+ }
+}
diff --git a/YMouseButtonControl.Core/ViewModels/App/Queries/StartupInstaller/CanBeInstalled/CanBeInstalledLinux.cs b/YMouseButtonControl.Core/ViewModels/App/Queries/StartupInstaller/CanBeInstalled/CanBeInstalledLinux.cs
new file mode 100644
index 0000000..fccd88b
--- /dev/null
+++ b/YMouseButtonControl.Core/ViewModels/App/Queries/StartupInstaller/CanBeInstalled/CanBeInstalledLinux.cs
@@ -0,0 +1,22 @@
+using System;
+using System.IO;
+
+namespace YMouseButtonControl.Core.ViewModels.App.Queries.StartupInstaller.CanBeInstalled;
+
+public static class CanBeInstalledLinux
+{
+ public sealed class Handler : ICanBeInstalledHandler
+ {
+ private readonly string _configDir = Environment.GetFolderPath(
+ Environment.SpecialFolder.ApplicationData
+ );
+ private readonly string _autostartDir;
+
+ public Handler()
+ {
+ _autostartDir = Path.Combine(_configDir, "autostart");
+ }
+
+ public bool Execute() => Directory.Exists(_autostartDir);
+ }
+}
diff --git a/YMouseButtonControl.Core/ViewModels/App/Queries/StartupInstaller/CanBeInstalled/CanBeInstalledOsx.cs b/YMouseButtonControl.Core/ViewModels/App/Queries/StartupInstaller/CanBeInstalled/CanBeInstalledOsx.cs
new file mode 100644
index 0000000..55ec7fa
--- /dev/null
+++ b/YMouseButtonControl.Core/ViewModels/App/Queries/StartupInstaller/CanBeInstalled/CanBeInstalledOsx.cs
@@ -0,0 +1,23 @@
+using System;
+using System.IO;
+
+namespace YMouseButtonControl.Core.ViewModels.App.Queries.StartupInstaller.CanBeInstalled;
+
+public static class CanBeInstalledOsx
+{
+ public sealed class Handler : ICanBeInstalledHandler
+ {
+ private readonly string _usrLaunchAgentsDir;
+
+ public Handler()
+ {
+ _usrLaunchAgentsDir = Path.Join(
+ Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
+ "Library",
+ "LaunchAgents"
+ );
+ }
+
+ public bool Execute() => Directory.Exists(_usrLaunchAgentsDir);
+ }
+}
diff --git a/YMouseButtonControl.Core/ViewModels/App/Queries/StartupInstaller/CanBeInstalled/CanBeInstalledWindows.cs b/YMouseButtonControl.Core/ViewModels/App/Queries/StartupInstaller/CanBeInstalled/CanBeInstalledWindows.cs
new file mode 100644
index 0000000..4a793c2
--- /dev/null
+++ b/YMouseButtonControl.Core/ViewModels/App/Queries/StartupInstaller/CanBeInstalled/CanBeInstalledWindows.cs
@@ -0,0 +1,9 @@
+namespace YMouseButtonControl.Core.ViewModels.App.Queries.StartupInstaller.CanBeInstalled;
+
+public static class CanBeInstalledWindows
+{
+ public sealed class Handler : ICanBeInstalledHandler
+ {
+ public bool Execute() => true;
+ }
+}
diff --git a/YMouseButtonControl.Core/ViewModels/App/Queries/StartupInstaller/CanBeInstalled/ICanBeInstalledHandler.cs b/YMouseButtonControl.Core/ViewModels/App/Queries/StartupInstaller/CanBeInstalled/ICanBeInstalledHandler.cs
new file mode 100644
index 0000000..057b305
--- /dev/null
+++ b/YMouseButtonControl.Core/ViewModels/App/Queries/StartupInstaller/CanBeInstalled/ICanBeInstalledHandler.cs
@@ -0,0 +1,7 @@
+namespace YMouseButtonControl.Core.ViewModels.App.Queries.StartupInstaller.CanBeInstalled
+{
+ public interface ICanBeInstalledHandler
+ {
+ bool Execute();
+ }
+}
diff --git a/YMouseButtonControl.Core/ViewModels/App/Queries/StartupInstaller/IsInstalled/IIsInstalledHandler.cs b/YMouseButtonControl.Core/ViewModels/App/Queries/StartupInstaller/IsInstalled/IIsInstalledHandler.cs
new file mode 100644
index 0000000..1eeaf77
--- /dev/null
+++ b/YMouseButtonControl.Core/ViewModels/App/Queries/StartupInstaller/IsInstalled/IIsInstalledHandler.cs
@@ -0,0 +1,7 @@
+namespace YMouseButtonControl.Core.ViewModels.App.Queries.StartupInstaller.IsInstalled
+{
+ public interface IIsInstalledHandler
+ {
+ bool Execute();
+ }
+}
diff --git a/YMouseButtonControl.Core/ViewModels/App/Queries/StartupInstaller/IsInstalled/IsInstalledLinux.cs b/YMouseButtonControl.Core/ViewModels/App/Queries/StartupInstaller/IsInstalled/IsInstalledLinux.cs
new file mode 100644
index 0000000..491b5fc
--- /dev/null
+++ b/YMouseButtonControl.Core/ViewModels/App/Queries/StartupInstaller/IsInstalled/IsInstalledLinux.cs
@@ -0,0 +1,41 @@
+using System;
+using System.IO;
+
+namespace YMouseButtonControl.Core.ViewModels.App.Queries.StartupInstaller.IsInstalled;
+
+public static class IsInstalledLinux
+{
+ public sealed class Handler : IIsInstalledHandler
+ {
+ private readonly string _configDir = Environment.GetFolderPath(
+ Environment.SpecialFolder.ApplicationData
+ );
+ private readonly string _autostartDir;
+ private readonly string _desktopFilePath;
+
+ public Handler()
+ {
+ _autostartDir = Path.Combine(_configDir, "autostart");
+ _desktopFilePath = Path.Combine(_autostartDir, "YMouseButtonControl.desktop");
+ }
+
+ public bool Execute()
+ {
+ if (!Directory.Exists(_autostartDir))
+ {
+ return false;
+ }
+
+ if (!File.Exists(_desktopFilePath))
+ {
+ return false;
+ }
+
+ var expectedExecLine = $"Exec={GetCurExePath()}";
+ return File.ReadAllText(_desktopFilePath).Contains(expectedExecLine);
+ }
+
+ private static string GetCurExePath() =>
+ Environment.ProcessPath ?? throw new Exception("Error retrieving process path");
+ }
+}
diff --git a/YMouseButtonControl.Core/ViewModels/App/Queries/StartupInstaller/IsInstalled/IsInstalledOsx.cs b/YMouseButtonControl.Core/ViewModels/App/Queries/StartupInstaller/IsInstalled/IsInstalledOsx.cs
new file mode 100644
index 0000000..3c88c0c
--- /dev/null
+++ b/YMouseButtonControl.Core/ViewModels/App/Queries/StartupInstaller/IsInstalled/IsInstalledOsx.cs
@@ -0,0 +1,30 @@
+using System;
+using System.IO;
+
+namespace YMouseButtonControl.Core.ViewModels.App.Queries.StartupInstaller.IsInstalled;
+
+public static class IsInstalledOsx
+{
+ public sealed class Handler : IIsInstalledHandler
+ {
+ private readonly string _usrLaunchAgentsDir;
+ private readonly string _plistPath;
+
+ public Handler()
+ {
+ _usrLaunchAgentsDir = Path.Join(
+ Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
+ "Library",
+ "LaunchAgents"
+ );
+ _plistPath = Path.Join(_usrLaunchAgentsDir, "com.github.ymousebuttoncontrol.plist");
+ }
+
+ public bool Execute() =>
+ File.Exists(_plistPath)
+ && File.ReadAllText(_plistPath).Contains($"{GetCurExePath()}");
+
+ private static string GetCurExePath() =>
+ Environment.ProcessPath ?? throw new Exception("Error retrieving process path");
+ }
+}
diff --git a/YMouseButtonControl.Core/ViewModels/App/Queries/StartupInstaller/IsInstalled/IsInstalledWindows.cs b/YMouseButtonControl.Core/ViewModels/App/Queries/StartupInstaller/IsInstalled/IsInstalledWindows.cs
new file mode 100644
index 0000000..4f2c884
--- /dev/null
+++ b/YMouseButtonControl.Core/ViewModels/App/Queries/StartupInstaller/IsInstalled/IsInstalledWindows.cs
@@ -0,0 +1,31 @@
+using System;
+using System.Runtime.Versioning;
+using System.Security.AccessControl;
+using Microsoft.Win32;
+
+namespace YMouseButtonControl.Core.ViewModels.App.Queries.StartupInstaller.IsInstalled;
+
+public static class IsInstalledWindows
+{
+ [SupportedOSPlatform("windows")]
+ public sealed class Handler : IIsInstalledHandler
+ {
+ private const string BaseKeyPath = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Run\";
+ private const string ValName = "YMouseButtonControl";
+
+ public bool Execute()
+ {
+ using var key = Registry.CurrentUser.OpenSubKey(BaseKeyPath, RegistryRights.ReadKey);
+ var keyVal = key?.GetValue(ValName)?.ToString();
+ if (string.IsNullOrWhiteSpace(keyVal))
+ {
+ return false;
+ }
+
+ return keyVal.Trim('"') == GetCurExePath();
+ }
+
+ private static string GetCurExePath() =>
+ Environment.ProcessPath ?? throw new Exception("Error retrieving process path");
+ }
+}
diff --git a/YMouseButtonControl.Core/ViewModels/DialogBase.cs b/YMouseButtonControl.Core/ViewModels/DialogBase.cs
index e545229..992e367 100644
--- a/YMouseButtonControl.Core/ViewModels/DialogBase.cs
+++ b/YMouseButtonControl.Core/ViewModels/DialogBase.cs
@@ -1,9 +1,5 @@
using ReactiveUI;
-using YMouseButtonControl.Core.Services.Theme;
namespace YMouseButtonControl.Core.ViewModels;
-public class DialogBase(IThemeService themeService) : ReactiveObject
-{
- public IThemeService ThemeService { get; } = themeService;
-}
+public class DialogBase : ReactiveObject { }
diff --git a/YMouseButtonControl.Core/ViewModels/Dialogs/GlobalSettingsDialog/Commands/Logging/DisableLogging.cs b/YMouseButtonControl.Core/ViewModels/Dialogs/GlobalSettingsDialog/Commands/Logging/DisableLogging.cs
new file mode 100644
index 0000000..c62d38b
--- /dev/null
+++ b/YMouseButtonControl.Core/ViewModels/Dialogs/GlobalSettingsDialog/Commands/Logging/DisableLogging.cs
@@ -0,0 +1,34 @@
+using System;
+using System.IO;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+
+namespace YMouseButtonControl.Core.ViewModels.Dialogs.GlobalSettingsDialog.Commands.Logging;
+
+public static class DisableLogging
+{
+ public sealed class Handler
+ {
+ private readonly string _pathToAppSettings = Path.Join(
+ AppContext.BaseDirectory,
+ "appsettings.json"
+ );
+
+ public void Execute()
+ {
+ if (!File.Exists(_pathToAppSettings))
+ {
+ throw new Exception($"Appsettings file not found: {_pathToAppSettings}");
+ }
+
+ var json =
+ JsonConvert.DeserializeObject(File.ReadAllText(_pathToAppSettings))
+ ?? throw new Exception($"Error deserializing {_pathToAppSettings}");
+ json.Remove("Logging");
+ File.WriteAllText(
+ _pathToAppSettings,
+ JsonConvert.SerializeObject(json, Formatting.Indented)
+ );
+ }
+ }
+}
diff --git a/YMouseButtonControl.Core/ViewModels/Dialogs/GlobalSettingsDialog/Commands/Logging/EnableLogging.cs b/YMouseButtonControl.Core/ViewModels/Dialogs/GlobalSettingsDialog/Commands/Logging/EnableLogging.cs
new file mode 100644
index 0000000..cc2429c
--- /dev/null
+++ b/YMouseButtonControl.Core/ViewModels/Dialogs/GlobalSettingsDialog/Commands/Logging/EnableLogging.cs
@@ -0,0 +1,56 @@
+using System;
+using System.IO;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+
+namespace YMouseButtonControl.Core.ViewModels.Dialogs.GlobalSettingsDialog.Commands.Logging;
+
+public static class EnableLogging
+{
+ public sealed class Handler
+ {
+ private readonly string _pathToAppSettings = Path.Join(
+ AppContext.BaseDirectory,
+ "appsettings.json"
+ );
+
+ public void Execute()
+ {
+ if (!File.Exists(_pathToAppSettings))
+ {
+ throw new Exception($"Appsettings file not found: {_pathToAppSettings}");
+ }
+ var json =
+ JsonConvert.DeserializeObject(File.ReadAllText(_pathToAppSettings))
+ ?? throw new Exception($"Error deserializing {_pathToAppSettings}");
+ var toAdd = new JProperty(
+ "Logging",
+ new JObject(
+ new JProperty(
+ "LogLevel",
+ new JObject(
+ new JProperty("Default", "Debug"),
+ new JProperty("System", "Information"),
+ new JProperty("Microsoft", "Error")
+ )
+ ),
+ new JProperty(
+ "File",
+ new JObject(
+ new JProperty("Path", "YMouseButtonControl.log"),
+ new JProperty("Append", true),
+ new JProperty("MinLevel", "Information"),
+ new JProperty("FileSizeLimitBytes", 0),
+ new JProperty("MaxRollingFiles", 0)
+ )
+ )
+ )
+ );
+ json.Add(toAdd);
+ File.WriteAllText(
+ _pathToAppSettings,
+ JsonConvert.SerializeObject(json, Formatting.Indented)
+ );
+ }
+ }
+}
diff --git a/YMouseButtonControl.Core/ViewModels/Dialogs/GlobalSettingsDialog/Commands/Settings/UpdateSetting.cs b/YMouseButtonControl.Core/ViewModels/Dialogs/GlobalSettingsDialog/Commands/Settings/UpdateSetting.cs
new file mode 100644
index 0000000..6935f44
--- /dev/null
+++ b/YMouseButtonControl.Core/ViewModels/Dialogs/GlobalSettingsDialog/Commands/Settings/UpdateSetting.cs
@@ -0,0 +1,36 @@
+using System;
+using System.Threading.Tasks;
+using Microsoft.EntityFrameworkCore;
+using YMouseButtonControl.Infrastructure.Context;
+
+namespace YMouseButtonControl.Core.ViewModels.Dialogs.GlobalSettingsDialog.Commands.Settings;
+
+public static class UpdateSetting
+{
+ public sealed record Command(string Name, T Value);
+
+ public sealed class Handler(YMouseButtonControlDbContext db)
+ {
+ public async Task ExecuteAsync(Command c)
+ {
+ switch (c.Value)
+ {
+ case string:
+ var entStr = await db.SettingStrings.FirstAsync(x => x.Name == c.Name);
+ entStr.StringValue = c.Value as string;
+ break;
+ case int:
+ var entInt = await db.SettingInts.FirstAsync(x => x.Name == c.Name);
+ entInt.IntValue = Convert.ToInt32(c.Value);
+ break;
+ case bool:
+ var entBool = await db.SettingBools.FirstAsync(x => x.Name == c.Name);
+ entBool.BoolValue = Convert.ToBoolean(c.Value);
+ break;
+ default:
+ throw new ArgumentOutOfRangeException(nameof(c.Value));
+ }
+ await db.SaveChangesAsync();
+ }
+ }
+}
diff --git a/YMouseButtonControl.Core/ViewModels/Dialogs/GlobalSettingsDialog/Commands/StartMenuInstall/IStartMenuInstallHandler.cs b/YMouseButtonControl.Core/ViewModels/Dialogs/GlobalSettingsDialog/Commands/StartMenuInstall/IStartMenuInstallHandler.cs
new file mode 100644
index 0000000..1153dfb
--- /dev/null
+++ b/YMouseButtonControl.Core/ViewModels/Dialogs/GlobalSettingsDialog/Commands/StartMenuInstall/IStartMenuInstallHandler.cs
@@ -0,0 +1,7 @@
+namespace YMouseButtonControl.Core.ViewModels.Dialogs.GlobalSettingsDialog.Commands.StartMenuInstall
+{
+ public interface IStartMenuInstallHandler
+ {
+ void Execute();
+ }
+}
diff --git a/YMouseButtonControl.Core/ViewModels/Dialogs/GlobalSettingsDialog/Commands/StartMenuInstall/StartMenuInstallLinux.cs b/YMouseButtonControl.Core/ViewModels/Dialogs/GlobalSettingsDialog/Commands/StartMenuInstall/StartMenuInstallLinux.cs
new file mode 100644
index 0000000..ac57abc
--- /dev/null
+++ b/YMouseButtonControl.Core/ViewModels/Dialogs/GlobalSettingsDialog/Commands/StartMenuInstall/StartMenuInstallLinux.cs
@@ -0,0 +1,47 @@
+using System;
+using System.IO;
+
+namespace YMouseButtonControl.Core.ViewModels.Dialogs.GlobalSettingsDialog.Commands.StartMenuInstall;
+
+public static class StartMenuInstallLinux
+{
+ public sealed class Handler : IStartMenuInstallHandler
+ {
+ private const string DesktopFile = """
+ [Desktop Entry]
+ Type=Application
+ Exec={0}
+ Path={1}
+ Hidden=false
+ NoDisplay=false
+ X-GNOME-Autostart-enabled=true
+ Name=YMouseButtonControl
+ Comment=YMouseButtonControl
+ """;
+
+ private readonly string _localShare = Environment.GetFolderPath(
+ Environment.SpecialFolder.LocalApplicationData
+ );
+
+ private readonly string _desktopFilePath;
+
+ public Handler()
+ {
+ var applicationsDir = Path.Combine(_localShare, "applications");
+ _desktopFilePath = Path.Combine(applicationsDir, "YMouseButtonControl.desktop");
+ }
+
+ public void Execute() =>
+ File.WriteAllText(
+ _desktopFilePath,
+ string.Format(DesktopFile, GetCurExePath(), GetCurExeParentPath())
+ );
+
+ private static string GetCurExeParentPath() =>
+ Path.GetDirectoryName(GetCurExePath())
+ ?? throw new Exception("Error retrieving parent of process path");
+
+ private static string GetCurExePath() =>
+ Environment.ProcessPath ?? throw new Exception("Error retrieving process path");
+ }
+}
diff --git a/YMouseButtonControl.Core/ViewModels/Dialogs/GlobalSettingsDialog/Commands/StartMenuInstall/StartMenuInstallOsx.cs b/YMouseButtonControl.Core/ViewModels/Dialogs/GlobalSettingsDialog/Commands/StartMenuInstall/StartMenuInstallOsx.cs
new file mode 100644
index 0000000..35f328c
--- /dev/null
+++ b/YMouseButtonControl.Core/ViewModels/Dialogs/GlobalSettingsDialog/Commands/StartMenuInstall/StartMenuInstallOsx.cs
@@ -0,0 +1,14 @@
+using System;
+
+namespace YMouseButtonControl.Core.ViewModels.Dialogs.GlobalSettingsDialog.Commands.StartMenuInstall;
+
+public static class StartMenuInstallOsx
+{
+ public sealed class Handler : IStartMenuInstallHandler
+ {
+ public void Execute()
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/YMouseButtonControl.Core/ViewModels/Dialogs/GlobalSettingsDialog/Commands/StartMenuInstall/StartMenuInstallWindows.cs b/YMouseButtonControl.Core/ViewModels/Dialogs/GlobalSettingsDialog/Commands/StartMenuInstall/StartMenuInstallWindows.cs
new file mode 100644
index 0000000..32881fa
--- /dev/null
+++ b/YMouseButtonControl.Core/ViewModels/Dialogs/GlobalSettingsDialog/Commands/StartMenuInstall/StartMenuInstallWindows.cs
@@ -0,0 +1,60 @@
+using System;
+using System.IO;
+using WindowsShortcutFactory;
+
+namespace YMouseButtonControl.Core.ViewModels.Dialogs.GlobalSettingsDialog.Commands.StartMenuInstall;
+
+public static class StartMenuInstallWindows
+{
+ public sealed class Handler : IStartMenuInstallHandler
+ {
+ private readonly string _roamingAppDataFolder = Environment.GetFolderPath(
+ Environment.SpecialFolder.ApplicationData
+ );
+
+ private readonly string _roamingYMouseButtonsFolder;
+
+ private readonly string _roamingYmouseButtonsShortcutPath;
+
+ public Handler()
+ {
+ _roamingYMouseButtonsFolder = Path.Combine(
+ _roamingAppDataFolder,
+ "Microsoft",
+ "Windows",
+ "Start Menu",
+ "Programs",
+ "YMouseButtonControl"
+ );
+ _roamingYmouseButtonsShortcutPath = Path.Combine(
+ _roamingYMouseButtonsFolder,
+ "YMouseButtonControl.lnk"
+ );
+ }
+
+ public void Execute()
+ {
+ if (File.Exists(_roamingYmouseButtonsShortcutPath))
+ {
+ File.Delete(_roamingYmouseButtonsShortcutPath);
+ }
+
+ if (!Directory.Exists(_roamingYMouseButtonsFolder))
+ {
+ Directory.CreateDirectory(_roamingYMouseButtonsFolder);
+ }
+
+ using var shortcut = new WindowsShortcut();
+ shortcut.Path = GetCurExePath();
+ shortcut.WorkingDirectory = GetCurExeParentPath();
+ shortcut.Save(_roamingYmouseButtonsShortcutPath);
+ }
+
+ private static string GetCurExeParentPath() =>
+ Path.GetDirectoryName(GetCurExePath())
+ ?? throw new Exception("Error retrieving parent of process path");
+
+ private static string GetCurExePath() =>
+ Environment.ProcessPath ?? throw new Exception("Error retrieving process path");
+ }
+}
diff --git a/YMouseButtonControl.Core/ViewModels/Dialogs/GlobalSettingsDialog/Commands/StartMenuUninstall/IStartMenuUninstallHandler.cs b/YMouseButtonControl.Core/ViewModels/Dialogs/GlobalSettingsDialog/Commands/StartMenuUninstall/IStartMenuUninstallHandler.cs
new file mode 100644
index 0000000..51450fe
--- /dev/null
+++ b/YMouseButtonControl.Core/ViewModels/Dialogs/GlobalSettingsDialog/Commands/StartMenuUninstall/IStartMenuUninstallHandler.cs
@@ -0,0 +1,7 @@
+namespace YMouseButtonControl.Core.ViewModels.Dialogs.GlobalSettingsDialog.Commands.StartMenuUninstall
+{
+ public interface IStartMenuUninstallHandler
+ {
+ void Execute();
+ }
+}
diff --git a/YMouseButtonControl.Core/ViewModels/Dialogs/GlobalSettingsDialog/Commands/StartMenuUninstall/StartMenuUninstallLinux.cs b/YMouseButtonControl.Core/ViewModels/Dialogs/GlobalSettingsDialog/Commands/StartMenuUninstall/StartMenuUninstallLinux.cs
new file mode 100644
index 0000000..012a362
--- /dev/null
+++ b/YMouseButtonControl.Core/ViewModels/Dialogs/GlobalSettingsDialog/Commands/StartMenuUninstall/StartMenuUninstallLinux.cs
@@ -0,0 +1,24 @@
+using System;
+using System.IO;
+
+namespace YMouseButtonControl.Core.ViewModels.Dialogs.GlobalSettingsDialog.Commands.StartMenuUninstall;
+
+public static class StartMenuUninstallLinux
+{
+ public sealed class Handler : IStartMenuUninstallHandler
+ {
+ private readonly string _localShare = Environment.GetFolderPath(
+ Environment.SpecialFolder.LocalApplicationData
+ );
+
+ private readonly string _desktopFilePath;
+
+ public Handler()
+ {
+ var applicationsDir = Path.Combine(_localShare, "applications");
+ _desktopFilePath = Path.Combine(applicationsDir, "YMouseButtonControl.desktop");
+ }
+
+ public void Execute() => File.Delete(_desktopFilePath);
+ }
+}
diff --git a/YMouseButtonControl.Core/ViewModels/Dialogs/GlobalSettingsDialog/Commands/StartMenuUninstall/StartMenuUninstallOsx.cs b/YMouseButtonControl.Core/ViewModels/Dialogs/GlobalSettingsDialog/Commands/StartMenuUninstall/StartMenuUninstallOsx.cs
new file mode 100644
index 0000000..729c0ea
--- /dev/null
+++ b/YMouseButtonControl.Core/ViewModels/Dialogs/GlobalSettingsDialog/Commands/StartMenuUninstall/StartMenuUninstallOsx.cs
@@ -0,0 +1,14 @@
+using System;
+
+namespace YMouseButtonControl.Core.ViewModels.Dialogs.GlobalSettingsDialog.Commands.StartMenuUninstall;
+
+public static class StartMenuUninstallOsx
+{
+ public sealed class Handler : IStartMenuUninstallHandler
+ {
+ public void Execute()
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/YMouseButtonControl.Core/ViewModels/Dialogs/GlobalSettingsDialog/Commands/StartMenuUninstall/StartMenuUninstallWindows.cs b/YMouseButtonControl.Core/ViewModels/Dialogs/GlobalSettingsDialog/Commands/StartMenuUninstall/StartMenuUninstallWindows.cs
new file mode 100644
index 0000000..1974c72
--- /dev/null
+++ b/YMouseButtonControl.Core/ViewModels/Dialogs/GlobalSettingsDialog/Commands/StartMenuUninstall/StartMenuUninstallWindows.cs
@@ -0,0 +1,37 @@
+using System;
+using System.IO;
+
+namespace YMouseButtonControl.Core.ViewModels.Dialogs.GlobalSettingsDialog.Commands.StartMenuUninstall;
+
+public static class StartMenuUninstallWindows
+{
+ public sealed class Handler : IStartMenuUninstallHandler
+ {
+ private readonly string _roamingAppDataFolder = Environment.GetFolderPath(
+ Environment.SpecialFolder.ApplicationData
+ );
+ private readonly string _roamingYMouseButtonsFolder;
+ private readonly string _roamingYmouseButtonsShortcutPath;
+
+ public Handler()
+ {
+ _roamingYMouseButtonsFolder = Path.Combine(
+ _roamingAppDataFolder,
+ "Microsoft",
+ "Windows",
+ "Start Menu",
+ "Programs",
+ "YMouseButtonControl"
+ );
+ _roamingYmouseButtonsShortcutPath = Path.Combine(
+ _roamingYMouseButtonsFolder,
+ "YMouseButtonControl.lnk"
+ );
+ }
+
+ public void Execute()
+ {
+ File.Delete(_roamingYmouseButtonsShortcutPath);
+ }
+ }
+}
diff --git a/YMouseButtonControl.Core/ViewModels/Dialogs/GlobalSettingsDialog/GlobalSettingsDialogHandlerRegistrations.cs b/YMouseButtonControl.Core/ViewModels/Dialogs/GlobalSettingsDialog/GlobalSettingsDialogHandlerRegistrations.cs
new file mode 100644
index 0000000..52a0104
--- /dev/null
+++ b/YMouseButtonControl.Core/ViewModels/Dialogs/GlobalSettingsDialog/GlobalSettingsDialogHandlerRegistrations.cs
@@ -0,0 +1,47 @@
+using Microsoft.Extensions.DependencyInjection;
+using YMouseButtonControl.Core.ViewModels.Dialogs.GlobalSettingsDialog.Commands.Logging;
+using YMouseButtonControl.Core.ViewModels.Dialogs.GlobalSettingsDialog.Commands.Settings;
+using YMouseButtonControl.Core.ViewModels.Dialogs.GlobalSettingsDialog.Commands.StartMenuInstall;
+using YMouseButtonControl.Core.ViewModels.Dialogs.GlobalSettingsDialog.Commands.StartMenuUninstall;
+using YMouseButtonControl.Core.ViewModels.Dialogs.GlobalSettingsDialog.Queries.Logging;
+using YMouseButtonControl.Core.ViewModels.Dialogs.GlobalSettingsDialog.Queries.Settings;
+using YMouseButtonControl.Core.ViewModels.Dialogs.GlobalSettingsDialog.Queries.StartMenuInstallerStatus;
+using YMouseButtonControl.Core.ViewModels.Dialogs.GlobalSettingsDialog.Queries.Themes;
+
+namespace YMouseButtonControl.Core.ViewModels.Dialogs.GlobalSettingsDialog;
+
+public static class GlobalSettingsDialogHandlerRegistrations
+{
+ public static void RegisterCommon(IServiceCollection services)
+ {
+ services
+ .AddScoped()
+ .AddScoped()
+ .AddScoped()
+ .AddScoped()
+ .AddScoped()
+ .AddScoped()
+ .AddScoped()
+ .AddScoped.Handler>()
+ .AddScoped.Handler>()
+ .AddScoped.Handler>();
+ }
+
+ public static void RegisterWindows(IServiceCollection services) =>
+ services
+ .AddScoped()
+ .AddScoped()
+ .AddScoped();
+
+ public static void RegisterLinux(IServiceCollection services) =>
+ services
+ .AddScoped()
+ .AddScoped()
+ .AddScoped();
+
+ public static void RegisterOsx(IServiceCollection services) =>
+ services
+ .AddScoped()
+ .AddScoped()
+ .AddScoped();
+}
diff --git a/YMouseButtonControl.Core/ViewModels/Dialogs/GlobalSettingsDialog/GlobalSettingsDialogViewModel.cs b/YMouseButtonControl.Core/ViewModels/Dialogs/GlobalSettingsDialog/GlobalSettingsDialogViewModel.cs
new file mode 100644
index 0000000..5b83bc2
--- /dev/null
+++ b/YMouseButtonControl.Core/ViewModels/Dialogs/GlobalSettingsDialog/GlobalSettingsDialogViewModel.cs
@@ -0,0 +1,173 @@
+using System;
+using System.Collections.ObjectModel;
+using System.Linq;
+using System.Reactive;
+using System.Reactive.Linq;
+using Avalonia.Styling;
+using ReactiveUI;
+using YMouseButtonControl.Core.ViewModels.Dialogs.GlobalSettingsDialog.Commands.Logging;
+using YMouseButtonControl.Core.ViewModels.Dialogs.GlobalSettingsDialog.Commands.Settings;
+using YMouseButtonControl.Core.ViewModels.Dialogs.GlobalSettingsDialog.Commands.StartMenuInstall;
+using YMouseButtonControl.Core.ViewModels.Dialogs.GlobalSettingsDialog.Commands.StartMenuUninstall;
+using YMouseButtonControl.Core.ViewModels.Dialogs.GlobalSettingsDialog.Queries.Logging;
+using YMouseButtonControl.Core.ViewModels.Dialogs.GlobalSettingsDialog.Queries.Settings;
+using YMouseButtonControl.Core.ViewModels.Dialogs.GlobalSettingsDialog.Queries.StartMenuInstallerStatus;
+using YMouseButtonControl.Core.ViewModels.Dialogs.GlobalSettingsDialog.Queries.Themes;
+
+namespace YMouseButtonControl.Core.ViewModels.Dialogs.GlobalSettingsDialog;
+
+public interface IGlobalSettingsDialogViewModel;
+
+public class GlobalSettingsDialogViewModel : DialogBase, IGlobalSettingsDialogViewModel
+{
+ private GetBoolSetting.BoolSettingVm _startMinimizedSetting;
+ private bool _loggingEnabled;
+ private bool _startMenuChecked;
+ private GetIntSetting.IntSettingVm _themeSetting;
+ private ObservableCollection _themeCollection;
+ private ListThemes.ThemeVm _selectedTheme;
+ private readonly ObservableAsPropertyHelper? _applyIsExec;
+ private readonly ThemeVariant _themeVariant;
+
+ public GlobalSettingsDialogViewModel(
+ GetBoolSetting.Handler getBoolSettingHandler,
+ IStartMenuInstallerStatusHandler startupMenuInstallerStatusHandler,
+ IStartMenuInstallHandler startMenuInstallHandler,
+ IStartMenuUninstallHandler startMenuUninstallHandler,
+ EnableLogging.Handler enableLoggingHandler,
+ DisableLogging.Handler disableLoggingHandler,
+ GetLoggingState.Handler loggingStateHandler,
+ GetIntSetting.Handler getIntSettingHandler,
+ UpdateSetting.Handler updateSettingIntHandler,
+ UpdateSetting.Handler updateSettingBoolHandler,
+ GetThemeVariant.Handler getThemeVariantHandler,
+ ListThemes.Handler listThemesHandler
+ )
+ {
+ _themeVariant = getThemeVariantHandler.Execute();
+ StartMenuEnabled = !OperatingSystem.IsMacOS();
+ _startMenuChecked = StartMenuEnabled && startupMenuInstallerStatusHandler.Execute();
+ _startMinimizedSetting = getBoolSettingHandler.Execute(
+ new Queries.Settings.Models.Query("StartMinimized")
+ );
+ _loggingEnabled = loggingStateHandler.Execute();
+ _themeSetting = getIntSettingHandler.Execute(new Queries.Settings.Models.Query("Theme"));
+ _themeCollection = [.. listThemesHandler.Execute()];
+ _selectedTheme = _themeCollection.First(x => x.Id == _themeSetting.Value);
+
+ // Update the theme setting selected theme value
+ this.WhenAnyValue(x => x.SelectedTheme).Subscribe(x => ThemeSetting.Value = x.Id);
+
+ var startMinimizedChanged = this.WhenAnyValue(
+ x => x.StartMinimized.Value,
+ selector: val =>
+ getBoolSettingHandler
+ .Execute(new Queries.Settings.Models.Query("StartMinimized"))
+ .Value != val
+ );
+ var loggingChanged = this.WhenAnyValue(
+ x => x.LoggingEnabled,
+ selector: val => val != loggingStateHandler.Execute()
+ );
+ var startMenuChanged = this.WhenAnyValue(
+ x => x.StartMenuChecked,
+ selector: val => StartMenuEnabled && val != startupMenuInstallerStatusHandler.Execute()
+ );
+ var themeChanged = this.WhenAnyValue(
+ x => x.ThemeSetting.Value,
+ selector: val =>
+ getIntSettingHandler.Execute(new Queries.Settings.Models.Query("Theme")).Value
+ != val
+ );
+ var applyIsExecObs = this.WhenAnyValue(x => x.AppIsExec);
+ var canSave = startMinimizedChanged
+ .Merge(loggingChanged)
+ .Merge(startMenuChanged)
+ .Merge(applyIsExecObs)
+ .Merge(themeChanged);
+ ApplyCommand = ReactiveCommand.CreateFromTask(
+ async () =>
+ {
+ if (LoggingEnabled != loggingStateHandler.Execute())
+ {
+ if (LoggingEnabled)
+ {
+ enableLoggingHandler.Execute();
+ }
+ else
+ {
+ disableLoggingHandler.Execute();
+ }
+ }
+
+ if (
+ StartMenuEnabled
+ && StartMenuChecked != startupMenuInstallerStatusHandler.Execute()
+ )
+ {
+ if (StartMenuChecked)
+ {
+ startMenuInstallHandler.Execute();
+ }
+ else
+ {
+ startMenuUninstallHandler.Execute();
+ }
+ }
+
+ await updateSettingBoolHandler.ExecuteAsync(
+ new UpdateSetting.Command("StartMinimized", StartMinimized.Value)
+ );
+ await updateSettingIntHandler.ExecuteAsync(
+ new UpdateSetting.Command("Theme", ThemeSetting.Value)
+ );
+ },
+ canSave
+ );
+ _applyIsExec = ApplyCommand.IsExecuting.ToProperty(this, x => x.AppIsExec);
+ }
+
+ private bool AppIsExec => _applyIsExec?.Value ?? false;
+
+ public bool StartMenuChecked
+ {
+ get => _startMenuChecked;
+ set => this.RaiseAndSetIfChanged(ref _startMenuChecked, value);
+ }
+
+ public bool StartMenuEnabled { get; init; }
+
+ public GetBoolSetting.BoolSettingVm StartMinimized
+ {
+ get => _startMinimizedSetting;
+ set => this.RaiseAndSetIfChanged(ref _startMinimizedSetting, value);
+ }
+
+ public bool LoggingEnabled
+ {
+ get => _loggingEnabled;
+ set => this.RaiseAndSetIfChanged(ref _loggingEnabled, value);
+ }
+
+ public ListThemes.ThemeVm SelectedTheme
+ {
+ get => _selectedTheme;
+ set => this.RaiseAndSetIfChanged(ref _selectedTheme, value);
+ }
+
+ public GetIntSetting.IntSettingVm ThemeSetting
+ {
+ get => _themeSetting;
+ set => this.RaiseAndSetIfChanged(ref _themeSetting, value);
+ }
+
+ public ObservableCollection ThemeCollection
+ {
+ get => _themeCollection;
+ set => this.RaiseAndSetIfChanged(ref _themeCollection, value);
+ }
+
+ public ReactiveCommand ApplyCommand { get; init; }
+
+ public ThemeVariant ThemeVariant => _themeVariant;
+}
diff --git a/YMouseButtonControl.Core/ViewModels/Dialogs/GlobalSettingsDialog/Queries/Logging/GetLoggingState.cs b/YMouseButtonControl.Core/ViewModels/Dialogs/GlobalSettingsDialog/Queries/Logging/GetLoggingState.cs
new file mode 100644
index 0000000..a87d852
--- /dev/null
+++ b/YMouseButtonControl.Core/ViewModels/Dialogs/GlobalSettingsDialog/Queries/Logging/GetLoggingState.cs
@@ -0,0 +1,29 @@
+using System;
+using System.IO;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+
+namespace YMouseButtonControl.Core.ViewModels.Dialogs.GlobalSettingsDialog.Queries.Logging;
+
+public static class GetLoggingState
+{
+ public sealed class Handler
+ {
+ private readonly string _pathToAppSettings = Path.Join(
+ AppContext.BaseDirectory,
+ "appsettings.json"
+ );
+
+ public bool Execute()
+ {
+ if (!File.Exists(_pathToAppSettings))
+ {
+ throw new Exception($"Appsettings file not found: {_pathToAppSettings}");
+ }
+ var json =
+ JsonConvert.DeserializeObject(File.ReadAllText(_pathToAppSettings))
+ ?? throw new Exception($"Error deserializing {_pathToAppSettings}");
+ return json.GetValue("Logging") != null;
+ }
+ }
+}
diff --git a/YMouseButtonControl.Core/ViewModels/Dialogs/GlobalSettingsDialog/Queries/Settings/GetBoolSetting.cs b/YMouseButtonControl.Core/ViewModels/Dialogs/GlobalSettingsDialog/Queries/Settings/GetBoolSetting.cs
new file mode 100644
index 0000000..7b7dd84
--- /dev/null
+++ b/YMouseButtonControl.Core/ViewModels/Dialogs/GlobalSettingsDialog/Queries/Settings/GetBoolSetting.cs
@@ -0,0 +1,21 @@
+using System.Linq;
+using ReactiveUI;
+using YMouseButtonControl.Core.ViewModels.Dialogs.GlobalSettingsDialog.Queries.Settings.Models;
+using YMouseButtonControl.Domain.Models;
+using YMouseButtonControl.Infrastructure.Context;
+
+namespace YMouseButtonControl.Core.ViewModels.Dialogs.GlobalSettingsDialog.Queries.Settings;
+
+public static class GetBoolSetting
+{
+ public sealed class BoolSettingVm(SettingBool setting) : ReactiveObject
+ {
+ public string Name { get; } = setting.Name;
+ public bool Value { get; } = setting.BoolValue;
+ }
+
+ public sealed class Handler(YMouseButtonControlDbContext db)
+ {
+ public BoolSettingVm Execute(Query q) => new(db.SettingBools.First(x => x.Name == q.Name));
+ }
+}
diff --git a/YMouseButtonControl.Core/ViewModels/Dialogs/GlobalSettingsDialog/Queries/Settings/GetIntSetting.cs b/YMouseButtonControl.Core/ViewModels/Dialogs/GlobalSettingsDialog/Queries/Settings/GetIntSetting.cs
new file mode 100644
index 0000000..39a6799
--- /dev/null
+++ b/YMouseButtonControl.Core/ViewModels/Dialogs/GlobalSettingsDialog/Queries/Settings/GetIntSetting.cs
@@ -0,0 +1,26 @@
+using System.Linq;
+using ReactiveUI;
+using YMouseButtonControl.Core.ViewModels.Dialogs.GlobalSettingsDialog.Queries.Settings.Models;
+using YMouseButtonControl.Domain.Models;
+using YMouseButtonControl.Infrastructure.Context;
+
+namespace YMouseButtonControl.Core.ViewModels.Dialogs.GlobalSettingsDialog.Queries.Settings;
+
+public static class GetIntSetting
+{
+ public sealed class IntSettingVm(SettingInt setting) : ReactiveObject
+ {
+ private int _value = setting.IntValue;
+ public string Name { get; } = setting.Name;
+ public int Value
+ {
+ get => _value;
+ set => this.RaiseAndSetIfChanged(ref _value, value);
+ }
+ }
+
+ public sealed class Handler(YMouseButtonControlDbContext db)
+ {
+ public IntSettingVm Execute(Query q) => new(db.SettingInts.First(x => x.Name == q.Name));
+ }
+}
diff --git a/YMouseButtonControl.Core/ViewModels/Dialogs/GlobalSettingsDialog/Queries/Settings/Models/Query.cs b/YMouseButtonControl.Core/ViewModels/Dialogs/GlobalSettingsDialog/Queries/Settings/Models/Query.cs
new file mode 100644
index 0000000..e194220
--- /dev/null
+++ b/YMouseButtonControl.Core/ViewModels/Dialogs/GlobalSettingsDialog/Queries/Settings/Models/Query.cs
@@ -0,0 +1,3 @@
+namespace YMouseButtonControl.Core.ViewModels.Dialogs.GlobalSettingsDialog.Queries.Settings.Models;
+
+public sealed record Query(string Name);
diff --git a/YMouseButtonControl.Core/ViewModels/Dialogs/GlobalSettingsDialog/Queries/StartMenuInstallerStatus/IStartMenuInstallerStatusHandler.cs b/YMouseButtonControl.Core/ViewModels/Dialogs/GlobalSettingsDialog/Queries/StartMenuInstallerStatus/IStartMenuInstallerStatusHandler.cs
new file mode 100644
index 0000000..cc9684c
--- /dev/null
+++ b/YMouseButtonControl.Core/ViewModels/Dialogs/GlobalSettingsDialog/Queries/StartMenuInstallerStatus/IStartMenuInstallerStatusHandler.cs
@@ -0,0 +1,7 @@
+namespace YMouseButtonControl.Core.ViewModels.Dialogs.GlobalSettingsDialog.Queries.StartMenuInstallerStatus
+{
+ public interface IStartMenuInstallerStatusHandler
+ {
+ bool Execute();
+ }
+}
diff --git a/YMouseButtonControl.Core/ViewModels/Dialogs/GlobalSettingsDialog/Queries/StartMenuInstallerStatus/StartMenuInstallerStatusLinux.cs b/YMouseButtonControl.Core/ViewModels/Dialogs/GlobalSettingsDialog/Queries/StartMenuInstallerStatus/StartMenuInstallerStatusLinux.cs
new file mode 100644
index 0000000..394da61
--- /dev/null
+++ b/YMouseButtonControl.Core/ViewModels/Dialogs/GlobalSettingsDialog/Queries/StartMenuInstallerStatus/StartMenuInstallerStatusLinux.cs
@@ -0,0 +1,25 @@
+using System;
+using System.IO;
+
+namespace YMouseButtonControl.Core.ViewModels.Dialogs.GlobalSettingsDialog.Queries.StartMenuInstallerStatus;
+
+public static class StartMenuInstallerStatusLinux
+{
+ public sealed class Handler : IStartMenuInstallerStatusHandler
+ {
+ private readonly string _localShare = Environment.GetFolderPath(
+ Environment.SpecialFolder.LocalApplicationData
+ );
+
+ public bool Execute()
+ {
+ var applicationsDir = Path.Combine(_localShare, "applications");
+ var desktopFilePath = Path.Combine(applicationsDir, "YMouseButtonControl.desktop");
+ return File.Exists(desktopFilePath)
+ && File.ReadAllText(desktopFilePath).Contains($"Exec={GetCurExePath()}");
+ }
+
+ private static string GetCurExePath() =>
+ Environment.ProcessPath ?? throw new Exception("Error retrieving process path");
+ }
+}
diff --git a/YMouseButtonControl.Core/ViewModels/Dialogs/GlobalSettingsDialog/Queries/StartMenuInstallerStatus/StartMenuInstallerStatusOsx.cs b/YMouseButtonControl.Core/ViewModels/Dialogs/GlobalSettingsDialog/Queries/StartMenuInstallerStatus/StartMenuInstallerStatusOsx.cs
new file mode 100644
index 0000000..4bd3dff
--- /dev/null
+++ b/YMouseButtonControl.Core/ViewModels/Dialogs/GlobalSettingsDialog/Queries/StartMenuInstallerStatus/StartMenuInstallerStatusOsx.cs
@@ -0,0 +1,12 @@
+namespace YMouseButtonControl.Core.ViewModels.Dialogs.GlobalSettingsDialog.Queries.StartMenuInstallerStatus;
+
+public static class StartMenuInstallerStatusOsx
+{
+ public sealed class Handler : IStartMenuInstallerStatusHandler
+ {
+ public bool Execute()
+ {
+ throw new System.NotImplementedException();
+ }
+ }
+}
diff --git a/YMouseButtonControl.Core/ViewModels/Dialogs/GlobalSettingsDialog/Queries/StartMenuInstallerStatus/StartMenuInstallerStatusWindows.cs b/YMouseButtonControl.Core/ViewModels/Dialogs/GlobalSettingsDialog/Queries/StartMenuInstallerStatus/StartMenuInstallerStatusWindows.cs
new file mode 100644
index 0000000..cfa7113
--- /dev/null
+++ b/YMouseButtonControl.Core/ViewModels/Dialogs/GlobalSettingsDialog/Queries/StartMenuInstallerStatus/StartMenuInstallerStatusWindows.cs
@@ -0,0 +1,39 @@
+using System;
+using System.IO;
+using WindowsShortcutFactory;
+
+namespace YMouseButtonControl.Core.ViewModels.Dialogs.GlobalSettingsDialog.Queries.StartMenuInstallerStatus;
+
+public static class StartMenuInstallerStatusWindows
+{
+ public sealed class Handler : IStartMenuInstallerStatusHandler
+ {
+ public bool Execute()
+ {
+ var roamingAppDataFolder = Environment.GetFolderPath(
+ Environment.SpecialFolder.ApplicationData
+ );
+ var roamingYMouseButtonsFolder = Path.Combine(
+ roamingAppDataFolder,
+ "Microsoft",
+ "Windows",
+ "Start Menu",
+ "Programs",
+ "YMouseButtonControl"
+ );
+ var roamingYmouseButtonsShortcutPath = Path.Combine(
+ roamingYMouseButtonsFolder,
+ "YMouseButtonControl.lnk"
+ );
+ if (!File.Exists(roamingYmouseButtonsShortcutPath))
+ {
+ return false;
+ }
+ using var shortcut = WindowsShortcut.Load(roamingYmouseButtonsShortcutPath);
+ return shortcut.Path == GetCurExePath();
+ }
+ }
+
+ private static string GetCurExePath() =>
+ Environment.ProcessPath ?? throw new Exception("Error retrieving process path");
+}
diff --git a/YMouseButtonControl.Core/ViewModels/Dialogs/GlobalSettingsDialog/Queries/Themes/GetThemeVariant.cs b/YMouseButtonControl.Core/ViewModels/Dialogs/GlobalSettingsDialog/Queries/Themes/GetThemeVariant.cs
new file mode 100644
index 0000000..6e8628e
--- /dev/null
+++ b/YMouseButtonControl.Core/ViewModels/Dialogs/GlobalSettingsDialog/Queries/Themes/GetThemeVariant.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Linq;
+using Avalonia;
+using Avalonia.Styling;
+using YMouseButtonControl.Infrastructure.Context;
+
+namespace YMouseButtonControl.Core.ViewModels.Dialogs.GlobalSettingsDialog.Queries.Themes;
+
+public static class GetThemeVariant
+{
+ public sealed class Handler(YMouseButtonControlDbContext db)
+ {
+ public ThemeVariant Execute() =>
+ db.SettingInts.First(x => x.Name == "Theme").IntValue switch
+ {
+ 1 => Application.Current!.ActualThemeVariant == ThemeVariant.Light
+ ? ThemeVariant.Light
+ : ThemeVariant.Dark,
+ 2 => ThemeVariant.Light,
+ 3 => ThemeVariant.Dark,
+ _ => throw new ArgumentOutOfRangeException($"Invalid theme id"),
+ };
+ }
+}
diff --git a/YMouseButtonControl.Core/ViewModels/Dialogs/GlobalSettingsDialog/Queries/Themes/ListThemes.cs b/YMouseButtonControl.Core/ViewModels/Dialogs/GlobalSettingsDialog/Queries/Themes/ListThemes.cs
new file mode 100644
index 0000000..e9bda7a
--- /dev/null
+++ b/YMouseButtonControl.Core/ViewModels/Dialogs/GlobalSettingsDialog/Queries/Themes/ListThemes.cs
@@ -0,0 +1,20 @@
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.EntityFrameworkCore;
+using YMouseButtonControl.Infrastructure.Context;
+
+namespace YMouseButtonControl.Core.ViewModels.Dialogs.GlobalSettingsDialog.Queries.Themes;
+
+public class ListThemes
+{
+ public sealed class ThemeVm(Domain.Models.Theme theme)
+ {
+ public int Id { get; } = theme.Id;
+ public string Name { get; } = theme.Name;
+ }
+
+ public sealed class Handler(YMouseButtonControlDbContext db)
+ {
+ public List Execute() => [.. db.Themes.AsNoTracking().Select(x => new ThemeVm(x))];
+ }
+}
diff --git a/YMouseButtonControl.Core/ViewModels/Dialogs/ProcessSelectorDialog/ProcessSelectorDialogHandlerRegistrations.cs b/YMouseButtonControl.Core/ViewModels/Dialogs/ProcessSelectorDialog/ProcessSelectorDialogHandlerRegistrations.cs
new file mode 100644
index 0000000..e6ca2d7
--- /dev/null
+++ b/YMouseButtonControl.Core/ViewModels/Dialogs/ProcessSelectorDialog/ProcessSelectorDialogHandlerRegistrations.cs
@@ -0,0 +1,23 @@
+using System.Runtime.Versioning;
+using Microsoft.Extensions.DependencyInjection;
+using YMouseButtonControl.Core.ViewModels.Dialogs.ProcessSelectorDialog.Queries.Processes;
+using YMouseButtonControl.Core.ViewModels.Dialogs.ProcessSelectorDialog.Queries.Profiles;
+using YMouseButtonControl.Core.ViewModels.Dialogs.ProcessSelectorDialog.Queries.Themes;
+
+namespace YMouseButtonControl.Core.ViewModels.Dialogs.ProcessSelectorDialog;
+
+public static class ProcessSelectorDialogHandlerRegistrations
+{
+ public static void RegisterCommon(IServiceCollection services) =>
+ services.AddScoped().AddScoped();
+
+ public static void RegisterLinux(IServiceCollection services) =>
+ services.AddScoped();
+
+ public static void RegisterOsx(IServiceCollection services) =>
+ services.AddScoped();
+
+ [SupportedOSPlatform("windows5.1.2600")]
+ public static void RegisterWindows(IServiceCollection services) =>
+ services.AddScoped();
+}
diff --git a/YMouseButtonControl.Core/ViewModels/Dialogs/ProcessSelectorDialog/ProcessSelectorDialogViewModel.cs b/YMouseButtonControl.Core/ViewModels/Dialogs/ProcessSelectorDialog/ProcessSelectorDialogViewModel.cs
new file mode 100644
index 0000000..5048688
--- /dev/null
+++ b/YMouseButtonControl.Core/ViewModels/Dialogs/ProcessSelectorDialog/ProcessSelectorDialogViewModel.cs
@@ -0,0 +1,184 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+using System.Reactive;
+using System.Reactive.Disposables;
+using System.Reactive.Linq;
+using Avalonia.Styling;
+using DynamicData;
+using DynamicData.Binding;
+using ReactiveUI;
+using YMouseButtonControl.Core.ViewModels.Dialogs.ProcessSelectorDialog.Queries.Processes;
+using YMouseButtonControl.Core.ViewModels.Dialogs.ProcessSelectorDialog.Queries.Profiles;
+using YMouseButtonControl.Core.ViewModels.Dialogs.ProcessSelectorDialog.Queries.Themes;
+using YMouseButtonControl.Core.ViewModels.Models;
+using YMouseButtonControl.Domain.Models;
+
+namespace YMouseButtonControl.Core.ViewModels.Dialogs.ProcessSelectorDialog;
+
+public interface IProcessSelectorDialogViewModel
+{
+ ReactiveCommand RefreshButtonCommand { get; }
+}
+
+public class ProcessSelectorDialogViewModel
+ : DialogBase,
+ IProcessSelectorDialogViewModel,
+ IActivatableViewModel
+{
+ private readonly SourceList _sourceProcessModels;
+ private string? _processFilter;
+ private readonly ReadOnlyObservableCollection _filtered;
+ private Queries.Processes.Models.ProcessModel? _processModel;
+ private string? _selectedProcessModuleName;
+ private string? _selectedProcessMainWindowTitle;
+
+ public ProcessSelectorDialogViewModel(
+ IListProcessesHandler listProcessesHandler,
+ GetMaxProfileId.Handler getMaxProfileIdHandler,
+ GetThemeVariant.Handler getThemeVariantHandler,
+ string? selectedProcessModuleName
+ )
+ {
+ Activator = new ViewModelActivator();
+
+ ThemeVariant = getThemeVariantHandler.Execute();
+ _sourceProcessModels = new SourceList();
+ var dynamicFilter = this.WhenValueChanged(x => x.ProcessFilter)
+ .Select(CreateProcessFilterPredicate);
+ var filteredDisposable = _sourceProcessModels
+ .Connect()
+ .Filter(dynamicFilter)
+ .Sort(
+ SortExpressionComparer.Ascending(x =>
+ x.Process.ProcessName
+ )
+ )
+ .Bind(out _filtered)
+ .Subscribe();
+ RefreshButtonCommand = ReactiveCommand.Create(
+ () => _sourceProcessModels.EditDiff(listProcessesHandler.Execute())
+ );
+ var canExecuteOkCommand = this.WhenAnyValue(
+ x => x.SelectedProcessModel,
+ selector: model => model is not null
+ );
+ OkCommand = ReactiveCommand.Create(
+ () =>
+ {
+ var maxBmId = getMaxProfileIdHandler.Execute();
+ var buttonMappings = new List
+ {
+ CreateButtonMappings(maxBmId, MouseButton.Mb1),
+ CreateButtonMappings(maxBmId += 4, MouseButton.Mb2),
+ CreateButtonMappings(maxBmId += 4, MouseButton.Mb3),
+ CreateButtonMappings(maxBmId += 4, MouseButton.Mb4),
+ CreateButtonMappings(maxBmId += 4, MouseButton.Mb5),
+ CreateButtonMappings(maxBmId += 4, MouseButton.Mwu),
+ CreateButtonMappings(maxBmId += 4, MouseButton.Mwd),
+ CreateButtonMappings(maxBmId += 4, MouseButton.Mwl),
+ CreateButtonMappings(maxBmId + 4, MouseButton.Mwr),
+ };
+ return new ProfileVm(buttonMappings)
+ {
+ Name = SelectedProcessModuleName ?? string.Empty,
+ Description = SelectedProcessMainWindowTitle ?? string.Empty,
+ Process = SelectedProcessModuleName ?? string.Empty,
+ WindowCaption = "N/A",
+ WindowClass = "N/A",
+ ParentClass = "N/A",
+ MatchType = "N/A",
+ };
+ },
+ canExecuteOkCommand
+ );
+ this.WhenAnyValue(x => x.SelectedProcessModel)
+ .Subscribe(x =>
+ {
+ SelectedProcessModuleName = x?.Process.MainModule?.ModuleName;
+ SelectedProcessMainWindowTitle = x?.Process.MainWindowTitle;
+ });
+ if (!string.IsNullOrWhiteSpace(selectedProcessModuleName))
+ {
+ var foundProc = _filtered.FirstOrDefault(x =>
+ x.Process.MainModule?.ModuleName == selectedProcessModuleName
+ );
+ if (foundProc is not null)
+ {
+ SelectedProcessModel = foundProc;
+ }
+ }
+
+ this.WhenActivated(disposables =>
+ {
+ _sourceProcessModels.EditDiff(listProcessesHandler.Execute());
+ Disposable.Create(() => { }).DisposeWith(disposables);
+ });
+ }
+
+ private static List CreateButtonMappings(
+ int id,
+ MouseButton mouseButton
+ ) =>
+ [
+ new NothingMappingVm
+ {
+ Id = ++id,
+ MouseButton = mouseButton,
+ Selected = true,
+ },
+ new DisabledMappingVm { Id = ++id, MouseButton = mouseButton },
+ new SimulatedKeystrokeVm { Id = ++id, MouseButton = mouseButton },
+ new RightClickVm { Id = ++id, MouseButton = mouseButton },
+ ];
+
+ private Func CreateProcessFilterPredicate(
+ string? txt
+ )
+ {
+ if (string.IsNullOrWhiteSpace(txt))
+ {
+ return _ => true;
+ }
+
+ return model =>
+ !string.IsNullOrWhiteSpace(model.Process.ProcessName)
+ && model.Process.ProcessName.Contains(txt, StringComparison.OrdinalIgnoreCase);
+ }
+
+ public string? ProcessFilter
+ {
+ get => _processFilter;
+ set => this.RaiseAndSetIfChanged(ref _processFilter, value);
+ }
+
+ public ReactiveCommand RefreshButtonCommand { get; }
+
+ public ReactiveCommand OkCommand { get; }
+
+ public ReadOnlyObservableCollection Filtered =>
+ _filtered;
+
+ public Queries.Processes.Models.ProcessModel? SelectedProcessModel
+ {
+ get => _processModel;
+ set => this.RaiseAndSetIfChanged(ref _processModel, value);
+ }
+
+ public string? SelectedProcessModuleName
+ {
+ get => _selectedProcessModuleName;
+ set => this.RaiseAndSetIfChanged(ref _selectedProcessModuleName, value);
+ }
+
+ public string? SelectedProcessMainWindowTitle
+ {
+ get => _selectedProcessMainWindowTitle;
+ set => this.RaiseAndSetIfChanged(ref _selectedProcessMainWindowTitle, value);
+ }
+
+ public ThemeVariant ThemeVariant { get; }
+
+ public ViewModelActivator Activator { get; }
+}
diff --git a/YMouseButtonControl.Core/ViewModels/Dialogs/ProcessSelectorDialog/ProcessSelectorDialogVmFactory.cs b/YMouseButtonControl.Core/ViewModels/Dialogs/ProcessSelectorDialog/ProcessSelectorDialogVmFactory.cs
new file mode 100644
index 0000000..013d7ac
--- /dev/null
+++ b/YMouseButtonControl.Core/ViewModels/Dialogs/ProcessSelectorDialog/ProcessSelectorDialogVmFactory.cs
@@ -0,0 +1,20 @@
+using YMouseButtonControl.Core.ViewModels.Dialogs.ProcessSelectorDialog.Queries.Processes;
+using YMouseButtonControl.Core.ViewModels.Dialogs.ProcessSelectorDialog.Queries.Profiles;
+using YMouseButtonControl.Core.ViewModels.Dialogs.ProcessSelectorDialog.Queries.Themes;
+
+namespace YMouseButtonControl.Core.ViewModels.Dialogs.ProcessSelectorDialog;
+
+public interface IProcessSelectorDialogVmFactory
+{
+ ProcessSelectorDialogViewModel Create(string? moduleName = null);
+}
+
+public class ProcessSelectorDialogVmFactory(
+ IListProcessesHandler listProcessesHandler,
+ GetMaxProfileId.Handler getMaxProfileIdHandler,
+ GetThemeVariant.Handler getThemeVariantHandler
+) : IProcessSelectorDialogVmFactory
+{
+ public ProcessSelectorDialogViewModel Create(string? moduleName = null) =>
+ new(listProcessesHandler, getMaxProfileIdHandler, getThemeVariantHandler, moduleName);
+}
diff --git a/YMouseButtonControl.Core/ViewModels/Dialogs/ProcessSelectorDialog/Queries/Processes/IListProcessesHandler.cs b/YMouseButtonControl.Core/ViewModels/Dialogs/ProcessSelectorDialog/Queries/Processes/IListProcessesHandler.cs
new file mode 100644
index 0000000..ec0c4ae
--- /dev/null
+++ b/YMouseButtonControl.Core/ViewModels/Dialogs/ProcessSelectorDialog/Queries/Processes/IListProcessesHandler.cs
@@ -0,0 +1,10 @@
+using System.Collections.Generic;
+using YMouseButtonControl.Core.ViewModels.Dialogs.ProcessSelectorDialog.Queries.Processes.Models;
+
+namespace YMouseButtonControl.Core.ViewModels.Dialogs.ProcessSelectorDialog.Queries.Processes
+{
+ public interface IListProcessesHandler
+ {
+ IEnumerable Execute();
+ }
+}
diff --git a/YMouseButtonControl.Core/ViewModels/Dialogs/ProcessSelectorDialog/Queries/Processes/ListProcessesLinux.cs b/YMouseButtonControl.Core/ViewModels/Dialogs/ProcessSelectorDialog/Queries/Processes/ListProcessesLinux.cs
new file mode 100644
index 0000000..290ac62
--- /dev/null
+++ b/YMouseButtonControl.Core/ViewModels/Dialogs/ProcessSelectorDialog/Queries/Processes/ListProcessesLinux.cs
@@ -0,0 +1,20 @@
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+
+namespace YMouseButtonControl.Core.ViewModels.Dialogs.ProcessSelectorDialog.Queries.Processes;
+
+public static class ListProcessesLinux
+{
+ public sealed class Handler : IListProcessesHandler
+ {
+ public IEnumerable Execute() =>
+ Process
+ .GetProcesses()
+ .Select(x => new Models.ProcessModel(x))
+ .Where(x =>
+ !string.IsNullOrWhiteSpace(x.Process.MainModule?.ModuleName)
+ && !string.IsNullOrWhiteSpace(x.Process.ProcessName)
+ );
+ }
+}
diff --git a/YMouseButtonControl.Core/ViewModels/Dialogs/ProcessSelectorDialog/Queries/Processes/ListProcessesOsx.cs b/YMouseButtonControl.Core/ViewModels/Dialogs/ProcessSelectorDialog/Queries/Processes/ListProcessesOsx.cs
new file mode 100644
index 0000000..8a50eaa
--- /dev/null
+++ b/YMouseButtonControl.Core/ViewModels/Dialogs/ProcessSelectorDialog/Queries/Processes/ListProcessesOsx.cs
@@ -0,0 +1,18 @@
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using YMouseButtonControl.Core.ViewModels.Dialogs.ProcessSelectorDialog.Queries.Processes.Models;
+
+namespace YMouseButtonControl.Core.ViewModels.Dialogs.ProcessSelectorDialog.Queries.Processes;
+
+public static class ListProcessesOsx
+{
+ public sealed class Handler : IListProcessesHandler
+ {
+ public IEnumerable Execute() =>
+ Process
+ .GetProcesses()
+ .Select(x => new ProcessModel(x))
+ .Where(x => !string.IsNullOrWhiteSpace(x.Process.ProcessName));
+ }
+}
diff --git a/YMouseButtonControl.Core/ViewModels/Dialogs/ProcessSelectorDialog/Queries/Processes/ListProcessesWindows.cs b/YMouseButtonControl.Core/ViewModels/Dialogs/ProcessSelectorDialog/Queries/Processes/ListProcessesWindows.cs
new file mode 100644
index 0000000..25f3189
--- /dev/null
+++ b/YMouseButtonControl.Core/ViewModels/Dialogs/ProcessSelectorDialog/Queries/Processes/ListProcessesWindows.cs
@@ -0,0 +1,64 @@
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.Drawing;
+using System.Drawing.Imaging;
+using System.IO;
+using System.Linq;
+using System.Runtime.Versioning;
+using System.Threading.Tasks;
+using YMouseButtonControl.Core.ViewModels.Dialogs.ProcessSelectorDialog.Queries.Processes.Models;
+
+namespace YMouseButtonControl.Core.ViewModels.Dialogs.ProcessSelectorDialog.Queries.Processes;
+
+[SupportedOSPlatform("windows5.1.2600")]
+public static class ListProcessesWindows
+{
+ public sealed class Handler : IListProcessesHandler
+ {
+ public IEnumerable Execute()
+ {
+ var cb = new ConcurrentBag();
+ Parallel.ForEach(
+ Process.GetProcesses().DistinctBy(x => x.ProcessName),
+ p =>
+ {
+ try
+ {
+ if (p.MainModule != null)
+ {
+ cb.Add(p);
+ }
+ }
+ catch (Exception ex) when (ex is Win32Exception or AggregateException) { }
+ }
+ );
+ return cb.DistinctBy(x => x.MainModule!.FileName)
+ .Select(x => new ProcessModel(x)
+ {
+ Bitmap = GetBitmapStreamFromPath(x.MainModule!.FileName),
+ });
+ }
+
+ private static MemoryStream? GetBitmapStreamFromPath(string path)
+ {
+ if (path is null or "/")
+ {
+ return null;
+ }
+
+ var icon = Icon.ExtractAssociatedIcon(path);
+ var bmp = icon?.ToBitmap();
+ if (bmp is null)
+ {
+ return null;
+ }
+ var stream = new MemoryStream();
+ bmp.Save(stream, ImageFormat.Bmp);
+ stream.Position = 0;
+ return stream;
+ }
+ }
+}
diff --git a/YMouseButtonControl.Core/ViewModels/Dialogs/ProcessSelectorDialog/Queries/Processes/Models/ProcessModel.cs b/YMouseButtonControl.Core/ViewModels/Dialogs/ProcessSelectorDialog/Queries/Processes/Models/ProcessModel.cs
new file mode 100644
index 0000000..0ac760d
--- /dev/null
+++ b/YMouseButtonControl.Core/ViewModels/Dialogs/ProcessSelectorDialog/Queries/Processes/Models/ProcessModel.cs
@@ -0,0 +1,16 @@
+using System.Diagnostics;
+using System.IO;
+
+namespace YMouseButtonControl.Core.ViewModels.Dialogs.ProcessSelectorDialog.Queries.Processes.Models;
+
+public record ProcessModel(Process P)
+{
+ public Process Process { get; } = P;
+ public Stream? Bitmap { get; set; }
+
+ public void Dispose()
+ {
+ Process.Dispose();
+ Bitmap?.Dispose();
+ }
+}
diff --git a/YMouseButtonControl.Core/ViewModels/Dialogs/ProcessSelectorDialog/Queries/Profiles/GetMaxProfileId.cs b/YMouseButtonControl.Core/ViewModels/Dialogs/ProcessSelectorDialog/Queries/Profiles/GetMaxProfileId.cs
new file mode 100644
index 0000000..b9e9a4c
--- /dev/null
+++ b/YMouseButtonControl.Core/ViewModels/Dialogs/ProcessSelectorDialog/Queries/Profiles/GetMaxProfileId.cs
@@ -0,0 +1,13 @@
+using System.Linq;
+using YMouseButtonControl.Core.Services.Profiles;
+
+namespace YMouseButtonControl.Core.ViewModels.Dialogs.ProcessSelectorDialog.Queries.Profiles;
+
+public static class GetMaxProfileId
+{
+ public sealed class Handler(IProfilesCache profilesService)
+ {
+ public int Execute() =>
+ profilesService.Profiles.SelectMany(x => x.ButtonMappings).Max(x => x.Id);
+ }
+}
diff --git a/YMouseButtonControl.Core/ViewModels/Dialogs/ProcessSelectorDialog/Queries/Themes/GetThemeVariant.cs b/YMouseButtonControl.Core/ViewModels/Dialogs/ProcessSelectorDialog/Queries/Themes/GetThemeVariant.cs
new file mode 100644
index 0000000..051e197
--- /dev/null
+++ b/YMouseButtonControl.Core/ViewModels/Dialogs/ProcessSelectorDialog/Queries/Themes/GetThemeVariant.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Linq;
+using Avalonia;
+using Avalonia.Styling;
+using YMouseButtonControl.Infrastructure.Context;
+
+namespace YMouseButtonControl.Core.ViewModels.Dialogs.ProcessSelectorDialog.Queries.Themes;
+
+public static class GetThemeVariant
+{
+ public sealed class Handler(YMouseButtonControlDbContext db)
+ {
+ public ThemeVariant Execute() =>
+ db.SettingInts.First(x => x.Name == "Theme").IntValue switch
+ {
+ 1 => Application.Current!.ActualThemeVariant == ThemeVariant.Light
+ ? ThemeVariant.Light
+ : ThemeVariant.Dark,
+ 2 => ThemeVariant.Light,
+ 3 => ThemeVariant.Dark,
+ _ => throw new ArgumentOutOfRangeException($"Invalid theme id"),
+ };
+ }
+}
diff --git a/YMouseButtonControl.Core/ViewModels/Dialogs/SimulatedKeystrokesDialog/Queries/Theme/GetThemeVariant.cs b/YMouseButtonControl.Core/ViewModels/Dialogs/SimulatedKeystrokesDialog/Queries/Theme/GetThemeVariant.cs
new file mode 100644
index 0000000..7337bd1
--- /dev/null
+++ b/YMouseButtonControl.Core/ViewModels/Dialogs/SimulatedKeystrokesDialog/Queries/Theme/GetThemeVariant.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Linq;
+using Avalonia;
+using Avalonia.Styling;
+using YMouseButtonControl.Infrastructure.Context;
+
+namespace YMouseButtonControl.Core.ViewModels.Dialogs.SimulatedKeystrokesDialog.Queries.Theme;
+
+public static class GetThemeVariant
+{
+ public sealed class Handler(YMouseButtonControlDbContext db)
+ {
+ public ThemeVariant Execute() =>
+ db.SettingInts.First(x => x.Name == "Theme").IntValue switch
+ {
+ 1 => Application.Current!.ActualThemeVariant == ThemeVariant.Light
+ ? ThemeVariant.Light
+ : ThemeVariant.Dark,
+ 2 => ThemeVariant.Light,
+ 3 => ThemeVariant.Dark,
+ _ => throw new ArgumentOutOfRangeException($"Invalid theme id"),
+ };
+ }
+}
diff --git a/YMouseButtonControl.Core/ViewModels/Dialogs/SimulatedKeystrokesDialog/SimulatedKeystrokesDialogHandlerRegistrations.cs b/YMouseButtonControl.Core/ViewModels/Dialogs/SimulatedKeystrokesDialog/SimulatedKeystrokesDialogHandlerRegistrations.cs
new file mode 100644
index 0000000..595944e
--- /dev/null
+++ b/YMouseButtonControl.Core/ViewModels/Dialogs/SimulatedKeystrokesDialog/SimulatedKeystrokesDialogHandlerRegistrations.cs
@@ -0,0 +1,10 @@
+using Microsoft.Extensions.DependencyInjection;
+using YMouseButtonControl.Core.ViewModels.Dialogs.SimulatedKeystrokesDialog.Queries.Theme;
+
+namespace YMouseButtonControl.Core.ViewModels.Dialogs.SimulatedKeystrokesDialog;
+
+public static class SimulatedKeystrokesDialogHandlerRegistrations
+{
+ public static void RegisterCommon(IServiceCollection services) =>
+ services.AddScoped();
+}
diff --git a/YMouseButtonControl.Core/ViewModels/LayerViewModel/SimulatedKeystrokesDialogViewModel.cs b/YMouseButtonControl.Core/ViewModels/Dialogs/SimulatedKeystrokesDialog/SimulatedKeystrokesDialogViewModel.cs
similarity index 95%
rename from YMouseButtonControl.Core/ViewModels/LayerViewModel/SimulatedKeystrokesDialogViewModel.cs
rename to YMouseButtonControl.Core/ViewModels/Dialogs/SimulatedKeystrokesDialog/SimulatedKeystrokesDialogViewModel.cs
index 014b494..dd17302 100644
--- a/YMouseButtonControl.Core/ViewModels/LayerViewModel/SimulatedKeystrokesDialogViewModel.cs
+++ b/YMouseButtonControl.Core/ViewModels/Dialogs/SimulatedKeystrokesDialog/SimulatedKeystrokesDialogViewModel.cs
@@ -2,15 +2,15 @@
using System.Collections.Generic;
using System.Linq;
using System.Reactive;
-using Avalonia.Collections;
+using Avalonia.Styling;
using ReactiveUI;
using YMouseButtonControl.Core.Services.KeyboardAndMouse.EventArgs;
using YMouseButtonControl.Core.Services.KeyboardAndMouse.Implementations;
-using YMouseButtonControl.Core.Services.Theme;
+using YMouseButtonControl.Core.ViewModels.Dialogs.SimulatedKeystrokesDialog.Queries.Theme;
using YMouseButtonControl.Core.ViewModels.Models;
-using YMouseButtonControl.DataAccess.Models;
+using YMouseButtonControl.Domain.Models;
-namespace YMouseButtonControl.Core.ViewModels.LayerViewModel;
+namespace YMouseButtonControl.Core.ViewModels.Dialogs.SimulatedKeystrokesDialog;
public class SimulatedKeystrokesDialogViewModel : DialogBase, IDisposable
{
@@ -43,13 +43,13 @@ public class SimulatedKeystrokesDialogViewModel : DialogBase, IDisposable
public SimulatedKeystrokesDialogViewModel(
IMouseListener mouseListener,
- IThemeService themeService,
string buttonName,
MouseButton mouseButton,
+ GetThemeVariant.Handler getThemeVariantHandler,
SimulatedKeystrokeVm? currentMapping = null
)
- : base(themeService)
{
+ ThemeVariant = getThemeVariantHandler.Execute();
_title = $"SimulatedKeystrokes - {buttonName}";
_mouseListener = mouseListener;
currentMapping ??= new SimulatedKeystrokeVm();
@@ -124,6 +124,8 @@ is RepeatedlyWhileButtonDownActionTypeVm
});
}
+ public ThemeVariant ThemeVariant { get; }
+
private void MouseListenerOnOnMouseMovedEventHandler(NewMouseHookMoveEventArgs e)
{
X = e.X;
@@ -205,8 +207,8 @@ public bool AutoRepeatEnabled
set => this.RaiseAndSetIfChanged(ref _autoRepeatEnabled, value);
}
- public AvaloniaList SimulatedKeystrokesTypes { get; set; } =
- new(GetSimulatedKeystrokesTypes());
+ public List SimulatedKeystrokesTypes { get; set; } =
+ new(SimulatedKeystrokeTypesList.Select(x => x()));
public ReactiveCommand OkCommand { get; }
@@ -376,7 +378,9 @@ public BaseButtonMappingVm? CurrentMapping
set => this.RaiseAndSetIfChanged(ref _currentMapping, value);
}
- private static readonly List> SimulatedKeystrokeTypesList =
+ private static readonly IReadOnlyList<
+ Func
+ > SimulatedKeystrokeTypesList =
[
() => new MouseButtonPressedActionTypeVm(),
() => new MouseButtonReleasedActionTypeVm(),
@@ -391,9 +395,6 @@ public BaseButtonMappingVm? CurrentMapping
private string? _computedXy;
- private static IEnumerable GetSimulatedKeystrokesTypes() =>
- SimulatedKeystrokeTypesList.Select(x => x());
-
public void Dispose()
{
_mouseListener.Dispose();
diff --git a/YMouseButtonControl.Core/ViewModels/Dialogs/SimulatedKeystrokesDialog/SimulatedKeystrokesDialogVmFactory.cs b/YMouseButtonControl.Core/ViewModels/Dialogs/SimulatedKeystrokesDialog/SimulatedKeystrokesDialogVmFactory.cs
new file mode 100644
index 0000000..bae3b85
--- /dev/null
+++ b/YMouseButtonControl.Core/ViewModels/Dialogs/SimulatedKeystrokesDialog/SimulatedKeystrokesDialogVmFactory.cs
@@ -0,0 +1,34 @@
+using YMouseButtonControl.Core.Services.KeyboardAndMouse.Implementations;
+using YMouseButtonControl.Core.ViewModels.Dialogs.SimulatedKeystrokesDialog.Queries.Theme;
+using YMouseButtonControl.Core.ViewModels.Models;
+using YMouseButtonControl.Domain.Models;
+
+namespace YMouseButtonControl.Core.ViewModels.Dialogs.SimulatedKeystrokesDialog;
+
+public interface ISimulatedKeystrokesDialogVmFactory
+{
+ SimulatedKeystrokesDialogViewModel Create(
+ string buttonName,
+ MouseButton mouseButton,
+ BaseButtonMappingVm? mapping
+ );
+}
+
+public class SimulatedKeystrokesDialogVmFactory(
+ IMouseListener mouseListener,
+ GetThemeVariant.Handler getThemeVariant
+) : ISimulatedKeystrokesDialogVmFactory
+{
+ public SimulatedKeystrokesDialogViewModel Create(
+ string buttonName,
+ MouseButton mouseButton,
+ BaseButtonMappingVm? mapping
+ ) =>
+ new(
+ mouseListener,
+ buttonName,
+ mouseButton,
+ getThemeVariant,
+ mapping as SimulatedKeystrokeVm
+ );
+}
diff --git a/YMouseButtonControl.Core/ViewModels/Layer/LayerViewModel.cs b/YMouseButtonControl.Core/ViewModels/Layer/LayerViewModel.cs
new file mode 100644
index 0000000..aa155a7
--- /dev/null
+++ b/YMouseButtonControl.Core/ViewModels/Layer/LayerViewModel.cs
@@ -0,0 +1,163 @@
+using System;
+using System.Reactive.Linq;
+using ReactiveUI;
+using YMouseButtonControl.Core.Services.Profiles;
+using YMouseButtonControl.Core.ViewModels.Dialogs.SimulatedKeystrokesDialog;
+using YMouseButtonControl.Core.ViewModels.Models;
+using YMouseButtonControl.Core.ViewModels.MouseCombo;
+using YMouseButtonControl.Domain.Models;
+
+namespace YMouseButtonControl.Core.ViewModels.Layer;
+
+public interface ILayerViewModel;
+
+public class LayerViewModel : ViewModelBase, ILayerViewModel
+{
+ private IMouseComboViewModel? _mb1ComboVm;
+ private IMouseComboViewModel? _mb2ComboVm;
+ private IMouseComboViewModel? _mb3ComboVm;
+ private IMouseComboViewModel? _mb4ComboVm;
+ private IMouseComboViewModel? _mb5ComboVm;
+ private IMouseComboViewModel? _mwrComboVm;
+ private IMouseComboViewModel? _mwlComboVm;
+ private IMouseComboViewModel? _mwdComboVm;
+ private IMouseComboViewModel? _mwuComboVm;
+
+ public LayerViewModel(
+ IMouseComboViewModelFactory mbComboViewModelFactory,
+ IProfilesCache profilesService
+ )
+ {
+ ShowSimulatedKeystrokesPickerInteraction =
+ new Interaction();
+
+ profilesService
+ .WhenAnyValue(x => x.CurrentProfile)
+ .WhereNotNull()
+ .Subscribe(profileVm =>
+ {
+ Mb1ComboVm = mbComboViewModelFactory.CreateWithMouseButton(
+ profileVm.BtnSc,
+ MouseButton.Mb1,
+ "Left Button",
+ profileVm.Mb1Mappings,
+ ShowSimulatedKeystrokesPickerInteraction
+ );
+ Mb2ComboVm = mbComboViewModelFactory.CreateWithMouseButton(
+ profileVm.BtnSc,
+ MouseButton.Mb2,
+ "Right Button",
+ profileVm.Mb2Mappings,
+ ShowSimulatedKeystrokesPickerInteraction
+ );
+ Mb3ComboVm = mbComboViewModelFactory.CreateWithMouseButton(
+ profileVm.BtnSc,
+ MouseButton.Mb3,
+ "Middle Button",
+ profileVm.Mb3Mappings,
+ ShowSimulatedKeystrokesPickerInteraction
+ );
+ Mb4ComboVm = mbComboViewModelFactory.CreateWithMouseButton(
+ profileVm.BtnSc,
+ MouseButton.Mb4,
+ "Mouse Button 4",
+ profileVm.Mb4Mappings,
+ ShowSimulatedKeystrokesPickerInteraction
+ );
+ Mb5ComboVm = mbComboViewModelFactory.CreateWithMouseButton(
+ profileVm.BtnSc,
+ MouseButton.Mb5,
+ "Mouse Button 5",
+ profileVm.Mb5Mappings,
+ ShowSimulatedKeystrokesPickerInteraction
+ );
+ MwuComboVm = mbComboViewModelFactory.CreateWithMouseButton(
+ profileVm.BtnSc,
+ MouseButton.Mwu,
+ "Wheel Up",
+ profileVm.MwuMappings,
+ ShowSimulatedKeystrokesPickerInteraction
+ );
+ MwdComboVm = mbComboViewModelFactory.CreateWithMouseButton(
+ profileVm.BtnSc,
+ MouseButton.Mwd,
+ "Wheel Down",
+ profileVm.MwdMappings,
+ ShowSimulatedKeystrokesPickerInteraction
+ );
+ MwlComboVm = mbComboViewModelFactory.CreateWithMouseButton(
+ profileVm.BtnSc,
+ MouseButton.Mwl,
+ "Wheel Left",
+ profileVm.MwlMappings,
+ ShowSimulatedKeystrokesPickerInteraction
+ );
+ MwrComboVm = mbComboViewModelFactory.CreateWithMouseButton(
+ profileVm.BtnSc,
+ MouseButton.Mwr,
+ "Wheel Right",
+ profileVm.MwrMappings,
+ ShowSimulatedKeystrokesPickerInteraction
+ );
+ });
+ }
+
+ public IMouseComboViewModel? Mb1ComboVm
+ {
+ get => _mb1ComboVm;
+ set => this.RaiseAndSetIfChanged(ref _mb1ComboVm, value);
+ }
+
+ public IMouseComboViewModel? Mb2ComboVm
+ {
+ get => _mb2ComboVm;
+ set => this.RaiseAndSetIfChanged(ref _mb2ComboVm, value);
+ }
+
+ public IMouseComboViewModel? Mb3ComboVm
+ {
+ get => _mb3ComboVm;
+ set => this.RaiseAndSetIfChanged(ref _mb3ComboVm, value);
+ }
+
+ public IMouseComboViewModel? Mb4ComboVm
+ {
+ get => _mb4ComboVm;
+ set => this.RaiseAndSetIfChanged(ref _mb4ComboVm, value);
+ }
+
+ public IMouseComboViewModel? Mb5ComboVm
+ {
+ get => _mb5ComboVm;
+ set => this.RaiseAndSetIfChanged(ref _mb5ComboVm, value);
+ }
+
+ public IMouseComboViewModel? MwrComboVm
+ {
+ get => _mwrComboVm;
+ set => this.RaiseAndSetIfChanged(ref _mwrComboVm, value);
+ }
+
+ public IMouseComboViewModel? MwlComboVm
+ {
+ get => _mwlComboVm;
+ set => this.RaiseAndSetIfChanged(ref _mwlComboVm, value);
+ }
+
+ public IMouseComboViewModel? MwdComboVm
+ {
+ get => _mwdComboVm;
+ set => this.RaiseAndSetIfChanged(ref _mwdComboVm, value);
+ }
+
+ public IMouseComboViewModel? MwuComboVm
+ {
+ get => _mwuComboVm;
+ set => this.RaiseAndSetIfChanged(ref _mwuComboVm, value);
+ }
+
+ public Interaction<
+ SimulatedKeystrokesDialogViewModel,
+ SimulatedKeystrokeVm?
+ > ShowSimulatedKeystrokesPickerInteraction { get; }
+}
diff --git a/YMouseButtonControl.Core/ViewModels/LayerViewModel/LayerViewModel.cs b/YMouseButtonControl.Core/ViewModels/LayerViewModel/LayerViewModel.cs
deleted file mode 100644
index e8ef2de..0000000
--- a/YMouseButtonControl.Core/ViewModels/LayerViewModel/LayerViewModel.cs
+++ /dev/null
@@ -1,135 +0,0 @@
-using System;
-using ReactiveUI;
-using YMouseButtonControl.Core.Services.Profiles;
-using YMouseButtonControl.DataAccess.Models;
-
-namespace YMouseButtonControl.Core.ViewModels.LayerViewModel;
-
-public interface ILayerViewModel;
-
-public class LayerViewModel : ViewModelBase, ILayerViewModel
-{
- public LayerViewModel(
- IMouseComboViewModelFactory mbComboViewModelFactory,
- IProfilesService profilesService
- )
- {
- Mb1ComboVm = mbComboViewModelFactory.CreateWithMouseButton(MouseButton.Mb1, "Left Button");
- Mb2ComboVm = mbComboViewModelFactory.CreateWithMouseButton(MouseButton.Mb2, "Right Button");
- Mb3ComboVm = mbComboViewModelFactory.CreateWithMouseButton(
- MouseButton.Mb3,
- "Middle Button"
- );
- Mb4ComboVm = mbComboViewModelFactory.CreateWithMouseButton(
- MouseButton.Mb4,
- "Mouse Button 4"
- );
- Mb5ComboVm = mbComboViewModelFactory.CreateWithMouseButton(
- MouseButton.Mb5,
- "Mouse Button 5"
- );
- MwuComboVm = mbComboViewModelFactory.CreateWithMouseButton(MouseButton.Mwu, "Wheel Up");
- MwdComboVm = mbComboViewModelFactory.CreateWithMouseButton(MouseButton.Mwd, "Wheel Down");
- MwlComboVm = mbComboViewModelFactory.CreateWithMouseButton(MouseButton.Mwl, "Wheel Left");
- MwrComboVm = mbComboViewModelFactory.CreateWithMouseButton(MouseButton.Mwr, "Wheel Right");
-
- this.WhenAnyValue(x => x.Mb1ComboVm.SelectedBtnMap)
- .WhereNotNull()
- .Subscribe(x =>
- {
- if (profilesService.CurrentProfile is not null)
- {
- profilesService.CurrentProfile.MouseButton1 = x;
- }
- });
- this.WhenAnyValue(x => x.Mb2ComboVm.SelectedBtnMap)
- .WhereNotNull()
- .Subscribe(x =>
- {
- if (profilesService.CurrentProfile is not null)
- {
- profilesService.CurrentProfile.MouseButton2 = x;
- }
- });
- this.WhenAnyValue(x => x.Mb3ComboVm.SelectedBtnMap)
- .WhereNotNull()
- .Subscribe(x =>
- {
- if (profilesService.CurrentProfile is not null)
- {
- profilesService.CurrentProfile.MouseButton3 = x;
- }
- });
- this.WhenAnyValue(x => x.Mb4ComboVm.SelectedBtnMap)
- .WhereNotNull()
- .Subscribe(x =>
- {
- if (profilesService.CurrentProfile is not null)
- {
- profilesService.CurrentProfile.MouseButton4 = x;
- }
- });
- this.WhenAnyValue(x => x.Mb5ComboVm.SelectedBtnMap)
- .WhereNotNull()
- .Subscribe(x =>
- {
- if (profilesService.CurrentProfile is not null)
- {
- profilesService.CurrentProfile.MouseButton5 = x;
- }
- });
- this.WhenAnyValue(x => x.MwuComboVm.SelectedBtnMap)
- .WhereNotNull()
- .Subscribe(x =>
- {
- if (profilesService.CurrentProfile is not null)
- {
- profilesService.CurrentProfile.MouseWheelUp = x;
- }
- });
- this.WhenAnyValue(x => x.MwdComboVm.SelectedBtnMap)
- .WhereNotNull()
- .Subscribe(x =>
- {
- if (profilesService.CurrentProfile is not null)
- {
- profilesService.CurrentProfile.MouseWheelDown = x;
- }
- });
- this.WhenAnyValue(x => x.MwlComboVm.SelectedBtnMap)
- .WhereNotNull()
- .Subscribe(x =>
- {
- if (profilesService.CurrentProfile is not null)
- {
- profilesService.CurrentProfile.MouseWheelLeft = x;
- }
- });
- this.WhenAnyValue(x => x.MwrComboVm.SelectedBtnMap)
- .WhereNotNull()
- .Subscribe(x =>
- {
- if (profilesService.CurrentProfile is not null)
- {
- profilesService.CurrentProfile.MouseWheelRight = x;
- }
- });
- }
-
- public IMouseComboViewModel Mb1ComboVm { get; }
-
- public IMouseComboViewModel Mb2ComboVm { get; }
-
- public IMouseComboViewModel Mb3ComboVm { get; }
-
- public IMouseComboViewModel Mb4ComboVm { get; }
-
- public IMouseComboViewModel Mb5ComboVm { get; }
- public IMouseComboViewModel MwrComboVm { get; }
-
- public IMouseComboViewModel MwlComboVm { get; }
-
- public IMouseComboViewModel MwdComboVm { get; }
-
- public IMouseComboViewModel MwuComboVm { get; }
-}
diff --git a/YMouseButtonControl.Core/ViewModels/LayerViewModel/MouseComboViewModelFactory.cs b/YMouseButtonControl.Core/ViewModels/LayerViewModel/MouseComboViewModelFactory.cs
deleted file mode 100644
index 746aeac..0000000
--- a/YMouseButtonControl.Core/ViewModels/LayerViewModel/MouseComboViewModelFactory.cs
+++ /dev/null
@@ -1,31 +0,0 @@
-using YMouseButtonControl.Core.Services.KeyboardAndMouse.Implementations;
-using YMouseButtonControl.Core.Services.Profiles;
-using YMouseButtonControl.Core.Services.Theme;
-using YMouseButtonControl.DataAccess.Models;
-
-namespace YMouseButtonControl.Core.ViewModels.LayerViewModel;
-
-public interface IMouseComboViewModelFactory
-{
- IMouseComboViewModel CreateWithMouseButton(MouseButton mouseButton, string labelTxt);
-}
-
-public class MouseComboViewModelFactory(
- IMouseListener mouseListener,
- IThemeService themeService,
- IProfilesService profilesService,
- IShowSimulatedKeystrokesDialogService showSimulatedKeystrokesDialogService
-) : IMouseComboViewModelFactory
-{
- public IMouseComboViewModel CreateWithMouseButton(MouseButton mouseButton, string labelTxt) =>
- new MouseComboViewModel(
- profilesService,
- mouseListener,
- themeService,
- mouseButton,
- showSimulatedKeystrokesDialogService
- )
- {
- LabelTxt = labelTxt,
- };
-}
diff --git a/YMouseButtonControl.Core/ViewModels/LayerViewModel/ShowSimulatedKeystrokesDialogService.cs b/YMouseButtonControl.Core/ViewModels/LayerViewModel/ShowSimulatedKeystrokesDialogService.cs
deleted file mode 100644
index 37e5fa9..0000000
--- a/YMouseButtonControl.Core/ViewModels/LayerViewModel/ShowSimulatedKeystrokesDialogService.cs
+++ /dev/null
@@ -1,51 +0,0 @@
-using System.Reactive.Linq;
-using System.Threading.Tasks;
-using ReactiveUI;
-using YMouseButtonControl.Core.Services.KeyboardAndMouse.Implementations;
-using YMouseButtonControl.Core.Services.Theme;
-using YMouseButtonControl.Core.ViewModels.Models;
-using YMouseButtonControl.DataAccess.Models;
-
-namespace YMouseButtonControl.Core.ViewModels.LayerViewModel;
-
-public interface IShowSimulatedKeystrokesDialogService
-{
- Interaction<
- SimulatedKeystrokesDialogViewModel,
- SimulatedKeystrokeVm?
- > ShowSimulatedKeystrokesPickerInteraction { get; }
-
- Task ShowSimulatedKeystrokesDialog(
- string buttonName,
- MouseButton mouseButton,
- BaseButtonMappingVm mapping
- );
-}
-
-public class ShowSimulatedKeystrokesDialogService(
- IMouseListener mouseListener,
- IThemeService themeService
-) : IShowSimulatedKeystrokesDialogService
-{
- public Interaction<
- SimulatedKeystrokesDialogViewModel,
- SimulatedKeystrokeVm?
- > ShowSimulatedKeystrokesPickerInteraction { get; } = new();
-
- public async Task ShowSimulatedKeystrokesDialog(
- string buttonName,
- MouseButton mouseButton,
- BaseButtonMappingVm? mapping
- )
- {
- return await ShowSimulatedKeystrokesPickerInteraction.Handle(
- new SimulatedKeystrokesDialogViewModel(
- mouseListener,
- themeService,
- buttonName,
- mouseButton,
- mapping as SimulatedKeystrokeVm
- )
- );
- }
-}
diff --git a/YMouseButtonControl.Core/ViewModels/MainWindow/Commands/Profiles/ApplyProfiles.cs b/YMouseButtonControl.Core/ViewModels/MainWindow/Commands/Profiles/ApplyProfiles.cs
new file mode 100644
index 0000000..1f62e14
--- /dev/null
+++ b/YMouseButtonControl.Core/ViewModels/MainWindow/Commands/Profiles/ApplyProfiles.cs
@@ -0,0 +1,121 @@
+using System;
+using System.Linq;
+using System.Threading.Tasks;
+using Microsoft.EntityFrameworkCore;
+using YMouseButtonControl.Core.Mappings;
+using YMouseButtonControl.Core.Services.Profiles;
+using YMouseButtonControl.Core.ViewModels.Models;
+using YMouseButtonControl.Domain.Models;
+using YMouseButtonControl.Infrastructure.Context;
+
+namespace YMouseButtonControl.Core.ViewModels.MainWindow.Commands.Profiles;
+
+public static class ApplyProfiles
+{
+ public sealed class Handler(YMouseButtonControlDbContext db, IProfilesCache profilesCache)
+ {
+ public async Task ExecuteAsync()
+ {
+ var dbProfiles = await db.Profiles.AsNoTracking().ToListAsync();
+
+ // delete profiles that exist in the db but not in the profiles service. User had to remove a profile for this to occur
+ dbProfiles
+ .Where(x => profilesCache.Profiles.All(y => y.Id != x.Id))
+ .ToList()
+ .ForEach(async x =>
+ {
+ var ent = await db.Profiles.FindAsync(x.Id);
+ if (ent is not null)
+ {
+ db.Profiles.Remove(ent);
+ }
+ });
+
+ // update profiles that exist in both profiles service and db
+ profilesCache
+ .Profiles.Where(x => dbProfiles.Any(y => y.Id == x.Id))
+ .ToList()
+ .ForEach(async profilesServicePvm =>
+ {
+ var ent =
+ await db.Profiles.FindAsync(profilesServicePvm.Id)
+ ?? throw new Exception("Profile not found");
+ ent.Checked = profilesServicePvm.Checked;
+ ent.Description = profilesServicePvm.Description;
+ ent.DisplayPriority = profilesServicePvm.DisplayPriority;
+ ent.IsDefault = profilesServicePvm.IsDefault;
+ ent.MatchType = profilesServicePvm.MatchType;
+ ent.Name = profilesServicePvm.Name;
+ ent.ParentClass = profilesServicePvm.ParentClass;
+ ent.Process = profilesServicePvm.Process;
+ ent.WindowCaption = profilesServicePvm.WindowCaption;
+ ent.WindowClass = profilesServicePvm.WindowClass;
+
+ foreach (var profilesServicePvmBtnMapVm in profilesServicePvm.ButtonMappings)
+ {
+ var dbBm = db.ButtonMappings.Find(profilesServicePvmBtnMapVm.Id);
+
+ // if button mapping doesn't exist in the db, add it
+ // else if button mapping exists in the db and does not equal the button mapping in the profiles service profile, update button mapping
+ if (dbBm is null)
+ {
+ var newBm = ButtonMappingMapper.MapToEntity(profilesServicePvmBtnMapVm);
+ db.ButtonMappings.Add(newBm);
+ }
+ else
+ {
+ var dbBmMapped = ButtonMappingMapper.MapToViewModel(dbBm);
+ if (profilesServicePvmBtnMapVm.Equals(dbBmMapped))
+ {
+ continue;
+ }
+
+ dbBm.AutoRepeatDelay = profilesServicePvmBtnMapVm.AutoRepeatDelay;
+ dbBm.AutoRepeatRandomizeDelayEnabled =
+ profilesServicePvmBtnMapVm.AutoRepeatRandomizeDelayEnabled;
+ dbBm.BlockOriginalMouseInput =
+ profilesServicePvmBtnMapVm.BlockOriginalMouseInput;
+ dbBm.Keys = profilesServicePvmBtnMapVm.Keys;
+ dbBm.MouseButton = profilesServicePvmBtnMapVm.MouseButton;
+ dbBm.Selected = profilesServicePvmBtnMapVm.Selected;
+ if (profilesServicePvmBtnMapVm.SimulatedKeystrokeType is not null)
+ {
+ dbBm.SimulatedKeystrokeType =
+ profilesServicePvmBtnMapVm.SimulatedKeystrokeType switch
+ {
+ AsMousePressedAndReleasedActionTypeVm =>
+ SimulatedKeystrokeType.AsMousePressedAndReleasedActionType,
+ DuringMouseActionTypeVm =>
+ SimulatedKeystrokeType.DuringMouseActionType,
+ InAnotherThreadPressedActionTypeVm =>
+ SimulatedKeystrokeType.InAnotherThreadPressedActionType,
+ InAnotherThreadReleasedActionTypeVm =>
+ SimulatedKeystrokeType.InAnotherThreadReleasedActionType,
+ MouseButtonPressedActionTypeVm =>
+ SimulatedKeystrokeType.MouseButtonPressedActionType,
+ MouseButtonReleasedActionTypeVm =>
+ SimulatedKeystrokeType.MouseButtonReleasedActionType,
+ RepeatedlyWhileButtonDownActionTypeVm =>
+ SimulatedKeystrokeType.RepeatedlyWhileButtonDownActionType,
+ StickyHoldActionTypeVm =>
+ SimulatedKeystrokeType.StickyHoldActionType,
+ StickyRepeatActionTypeVm =>
+ SimulatedKeystrokeType.StickyRepeatActionType,
+ _ => throw new NotImplementedException(),
+ };
+ }
+ }
+ }
+ });
+
+ // add profiles that exist in the profile service but not the db. this occurs when a user adds a profile
+ await db.Profiles.AddRangeAsync(
+ profilesCache
+ .Profiles.Where(x => dbProfiles.All(y => y.Id != x.Id))
+ .Select(ProfileMapper.MapToEntity)
+ );
+
+ await db.SaveChangesAsync();
+ }
+ }
+}
diff --git a/YMouseButtonControl.Core/ViewModels/MainWindow/Features/Apply/ApplyProfiles.cs b/YMouseButtonControl.Core/ViewModels/MainWindow/Features/Apply/ApplyProfiles.cs
deleted file mode 100644
index 11a6a80..0000000
--- a/YMouseButtonControl.Core/ViewModels/MainWindow/Features/Apply/ApplyProfiles.cs
+++ /dev/null
@@ -1,41 +0,0 @@
-using System.Transactions;
-using YMouseButtonControl.Core.Repositories;
-using YMouseButtonControl.Core.Services.Profiles;
-using YMouseButtonControl.Core.ViewModels.Models;
-using YMouseButtonControl.DataAccess.Models;
-
-namespace YMouseButtonControl.Core.ViewModels.MainWindow.Features.Apply;
-
-public interface IApply
-{
- void ApplyProfiles();
-}
-
-public class Apply(
- IRepository profileRepository,
- IRepository buttonMappingRepository,
- IProfilesService profilesService
-) : IApply
-{
- public void ApplyProfiles()
- {
- using var trn = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled);
- foreach (var dbVm in profileRepository.GetAll())
- {
- profileRepository.Delete(dbVm);
- }
- foreach (var vm in profilesService.Profiles)
- {
- var profileId = profileRepository.Add(vm);
- foreach (var bm in vm.ButtonMappings)
- {
- if (bm.ProfileId <= 0)
- {
- bm.ProfileId = profileId;
- }
- buttonMappingRepository.Add(bm);
- }
- }
- trn.Complete();
- }
-}
diff --git a/YMouseButtonControl.Core/ViewModels/MainWindow/GlobalSettingsDialogViewModel.cs b/YMouseButtonControl.Core/ViewModels/MainWindow/GlobalSettingsDialogViewModel.cs
deleted file mode 100644
index 0653252..0000000
--- a/YMouseButtonControl.Core/ViewModels/MainWindow/GlobalSettingsDialogViewModel.cs
+++ /dev/null
@@ -1,159 +0,0 @@
-using System;
-using System.Collections.ObjectModel;
-using System.Linq;
-using System.Reactive;
-using System.Reactive.Linq;
-using System.Transactions;
-using ReactiveUI;
-using YMouseButtonControl.Core.Services.Logging;
-using YMouseButtonControl.Core.Services.Settings;
-using YMouseButtonControl.Core.Services.StartMenuInstaller;
-using YMouseButtonControl.Core.Services.Theme;
-using YMouseButtonControl.Core.ViewModels.Models;
-using YMouseButtonControl.DataAccess.Models;
-
-namespace YMouseButtonControl.Core.ViewModels.MainWindow;
-
-public interface IGlobalSettingsDialogViewModel;
-
-public class GlobalSettingsDialogViewModel : DialogBase, IGlobalSettingsDialogViewModel
-{
- private SettingBoolVm _startMinimizedSetting;
- private bool _loggingEnabled;
- private bool _startMenuChecked;
- private SettingIntVm _themeSetting;
- private ObservableCollection _themeCollection;
- private ThemeVm _selectedTheme;
- private readonly ObservableAsPropertyHelper? _applyIsExec;
-
- public GlobalSettingsDialogViewModel(
- IStartMenuInstallerService startMenuInstallerService,
- IEnableLoggingService enableLoggingService,
- ISettingsService settingsService,
- IThemeService themeService
- )
- : base(themeService)
- {
- StartMenuEnabled = !OperatingSystem.IsMacOS();
- _startMenuChecked = StartMenuEnabled && startMenuInstallerService.InstallStatus();
- _startMinimizedSetting =
- settingsService.GetSetting("StartMinimized") as SettingBoolVm
- ?? throw new Exception("Error retrieving StartMinimized setting");
- _loggingEnabled = enableLoggingService.GetLoggingState();
- _themeSetting =
- settingsService.GetSetting("Theme") as SettingIntVm
- ?? throw new Exception("Error retrieving Theme setting");
- _themeCollection = [.. themeService.Themes];
- _selectedTheme = _themeCollection.First(x => x.Id == _themeSetting.IntValue);
-
- // Update the theme setting selected theme value
- this.WhenAnyValue(x => x.SelectedTheme).Subscribe(x => ThemeSetting.IntValue = x.Id);
-
- var startMinimizedChanged = this.WhenAnyValue(
- x => x.StartMinimized.BoolValue,
- selector: val =>
- settingsService.GetSetting("StartMinimized") is not SettingBoolVm curVal
- || curVal.BoolValue != val
- );
- var loggingChanged = this.WhenAnyValue(
- x => x.LoggingEnabled,
- selector: val => val != enableLoggingService.GetLoggingState()
- );
- var startMenuChanged = this.WhenAnyValue(
- x => x.StartMenuChecked,
- selector: val => StartMenuEnabled && val != startMenuInstallerService.InstallStatus()
- );
- var themeChanged = this.WhenAnyValue(
- x => x.ThemeSetting.IntValue,
- selector: val =>
- settingsService.GetSetting("Theme") is not SettingIntVm curVal
- || curVal.IntValue != val
- );
- var applyIsExecObs = this.WhenAnyValue(x => x.AppIsExec);
- var canSave = startMinimizedChanged
- .Merge(loggingChanged)
- .Merge(startMenuChanged)
- .Merge(applyIsExecObs)
- .Merge(themeChanged);
- ApplyCommand = ReactiveCommand.Create(
- () =>
- {
- if (LoggingEnabled != enableLoggingService.GetLoggingState())
- {
- if (LoggingEnabled)
- {
- enableLoggingService.EnableLogging();
- }
- else
- {
- enableLoggingService.DisableLogging();
- }
- }
-
- if (
- StartMenuEnabled
- && StartMenuChecked != startMenuInstallerService.InstallStatus()
- )
- {
- if (StartMenuChecked)
- {
- startMenuInstallerService.Install();
- }
- else
- {
- startMenuInstallerService.Uninstall();
- }
- }
-
- using var trn = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled);
- settingsService.UpdateSetting(StartMinimized);
- settingsService.UpdateSetting(ThemeSetting);
- trn.Complete();
- },
- canSave
- );
- _applyIsExec = ApplyCommand.IsExecuting.ToProperty(this, x => x.AppIsExec);
- }
-
- private bool AppIsExec => _applyIsExec?.Value ?? false;
-
- public bool StartMenuChecked
- {
- get => _startMenuChecked;
- set => this.RaiseAndSetIfChanged(ref _startMenuChecked, value);
- }
-
- public bool StartMenuEnabled { get; init; }
-
- public SettingBoolVm StartMinimized
- {
- get => _startMinimizedSetting;
- set => this.RaiseAndSetIfChanged(ref _startMinimizedSetting, value);
- }
-
- public bool LoggingEnabled
- {
- get => _loggingEnabled;
- set => this.RaiseAndSetIfChanged(ref _loggingEnabled, value);
- }
-
- public ThemeVm SelectedTheme
- {
- get => _selectedTheme;
- set => this.RaiseAndSetIfChanged(ref _selectedTheme, value);
- }
-
- public SettingIntVm ThemeSetting
- {
- get => _themeSetting;
- set => this.RaiseAndSetIfChanged(ref _themeSetting, value);
- }
-
- public ObservableCollection ThemeCollection
- {
- get => _themeCollection;
- set => this.RaiseAndSetIfChanged(ref _themeCollection, value);
- }
-
- public ReactiveCommand ApplyCommand { get; init; }
-}
diff --git a/YMouseButtonControl.Core/ViewModels/MainWindow/MainWindowHandlerRegistrations.cs b/YMouseButtonControl.Core/ViewModels/MainWindow/MainWindowHandlerRegistrations.cs
new file mode 100644
index 0000000..3223911
--- /dev/null
+++ b/YMouseButtonControl.Core/ViewModels/MainWindow/MainWindowHandlerRegistrations.cs
@@ -0,0 +1,15 @@
+using Microsoft.Extensions.DependencyInjection;
+using YMouseButtonControl.Core.ViewModels.MainWindow.Commands.Profiles;
+using YMouseButtonControl.Core.ViewModels.MainWindow.Queries.Profiles;
+using YMouseButtonControl.Core.ViewModels.MainWindow.Queries.Theme;
+
+namespace YMouseButtonControl.Core.ViewModels.MainWindow;
+
+public static class MainWindowHandlerRegistrations
+{
+ public static void RegisterCommon(IServiceCollection services) =>
+ services
+ .AddScoped()
+ .AddScoped()
+ .AddScoped();
+}
diff --git a/YMouseButtonControl.Core/ViewModels/MainWindow/MainWindowViewModel.cs b/YMouseButtonControl.Core/ViewModels/MainWindow/MainWindowViewModel.cs
index 3ac7004..010c371 100644
--- a/YMouseButtonControl.Core/ViewModels/MainWindow/MainWindowViewModel.cs
+++ b/YMouseButtonControl.Core/ViewModels/MainWindow/MainWindowViewModel.cs
@@ -1,25 +1,20 @@
using System;
-using System.Collections.ObjectModel;
-using System.Diagnostics;
-using System.Linq;
using System.Reactive;
using System.Reactive.Linq;
using System.Threading.Tasks;
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
+using Avalonia.Styling;
using DynamicData;
-using DynamicData.Binding;
using ReactiveUI;
-using YMouseButtonControl.Core.Repositories;
using YMouseButtonControl.Core.Services.Profiles;
-using YMouseButtonControl.Core.Services.Settings;
-using YMouseButtonControl.Core.Services.Theme;
-using YMouseButtonControl.Core.ViewModels.LayerViewModel;
-using YMouseButtonControl.Core.ViewModels.MainWindow.Features.Apply;
-using YMouseButtonControl.Core.ViewModels.Models;
-using YMouseButtonControl.Core.ViewModels.ProfilesInformationViewModel;
+using YMouseButtonControl.Core.ViewModels.Dialogs.GlobalSettingsDialog;
+using YMouseButtonControl.Core.ViewModels.Layer;
+using YMouseButtonControl.Core.ViewModels.MainWindow.Commands.Profiles;
+using YMouseButtonControl.Core.ViewModels.MainWindow.Queries.Profiles;
+using YMouseButtonControl.Core.ViewModels.MainWindow.Queries.Theme;
+using YMouseButtonControl.Core.ViewModels.ProfilesInformation;
using YMouseButtonControl.Core.ViewModels.ProfilesList;
-using YMouseButtonControl.DataAccess.Models;
namespace YMouseButtonControl.Core.ViewModels.MainWindow;
@@ -32,43 +27,39 @@ public interface IMainWindowViewModel
ReactiveCommand CloseCommand { get; }
ReactiveCommand SettingsCommand { get; }
Interaction ShowSettingsDialogInteraction { get; }
- ProfileVm? CurrentProfile { get; }
- IThemeService ThemeService { get; }
}
public class MainWindowViewModel : ViewModelBase, IMainWindowViewModel
{
#region Fields
- private readonly IRepository _profileRepository;
- private readonly IProfilesService _ps;
- private readonly IThemeService _themeService;
private readonly IProfilesListViewModel _profilesListViewModel;
private readonly IGlobalSettingsDialogViewModel _globalSettingsDialogViewModel;
- private readonly ObservableAsPropertyHelper? _isExecutingSave;
- private readonly ReadOnlyObservableCollection _profileVms;
- private bool _canSave;
+ private bool _dirty;
+ private bool Dirty
+ {
+ get => _dirty;
+ set => this.RaiseAndSetIfChanged(ref _dirty, value);
+ }
#endregion
#region Constructor
public MainWindowViewModel(
- IProfilesService ps,
- IThemeService themeService,
+ IProfilesCache pc,
+ GetThemeVariant.Handler getThemeVariantHandler,
ILayerViewModel layerViewModel,
IProfilesListViewModel profilesListViewModel,
IProfilesInformationViewModel profilesInformationViewModel,
IGlobalSettingsDialogViewModel globalSettingsDialogViewModel,
- IApply apply,
- IRepository profileRepository
+ ApplyProfiles.Handler applyProfilesHandler,
+ IsCacheDirty.Handler isCacheDirtyHandler
)
{
- _profileRepository = profileRepository;
_profilesListViewModel = profilesListViewModel;
_globalSettingsDialogViewModel = globalSettingsDialogViewModel;
- _ps = ps;
- _themeService = themeService;
+ ThemeVariant = getThemeVariantHandler.Execute();
LayerViewModel = layerViewModel;
ProfilesInformationViewModel = profilesInformationViewModel;
SettingsCommand = ReactiveCommand.CreateFromTask(ShowSettingsDialogAsync);
@@ -83,55 +74,20 @@ is IClassicDesktopStyleApplicationLifetime lifetime
lifetime.MainWindow?.Hide();
}
});
- var myOp = _ps.Connect()
+ var myOp = pc.Connect()
.AutoRefresh()
- .RefCount()
- .Bind(out _profileVms)
.DisposeMany()
- .Subscribe(CanSaveHelper);
- var isExecutingObservable = this.WhenAnyValue(x => x.IsExecutingSave)
- .Subscribe(_ => CanSave = false);
- var canSaveCmd = this.WhenAnyValue(x => x.CanSave);
- ApplyCommand = ReactiveCommand.Create(apply.ApplyProfiles, canSaveCmd);
- _isExecutingSave = ApplyCommand.IsExecuting.ToProperty(this, x => x.IsExecutingSave);
+ .Subscribe((_) => Dirty = isCacheDirtyHandler.Execute());
+ var canExecuteApply = this.WhenAnyValue(x => x.Dirty);
+ ApplyCommand = ReactiveCommand.CreateFromTask(
+ applyProfilesHandler.ExecuteAsync,
+ canExecuteApply
+ );
+ var isExecutingObservable = this.WhenAnyObservable(x => x.ApplyCommand.IsExecuting);
+ canExecuteApply = canExecuteApply.Merge(isExecutingObservable);
+ isExecutingObservable.Skip(1).Where(x => !x).Subscribe(x => Dirty = false);
}
- private void CanSaveHelper(IChangeSet changeSet)
- {
- foreach (var cs in changeSet)
- {
- var entity = _profileRepository.GetById(cs.Current.Id);
- switch (cs.Reason)
- {
- case ChangeReason.Add:
- CanSave = entity is null;
- break;
- case ChangeReason.Update:
- CanSave = !entity?.Equals(cs.Current) ?? true;
- break;
- case ChangeReason.Remove:
- CanSave = entity is not null;
- break;
- case ChangeReason.Refresh:
- CanSave = !entity?.Equals(cs.Current) ?? true;
- break;
- case ChangeReason.Moved:
- break;
- default:
- throw new ArgumentOutOfRangeException();
- }
- }
- }
-
- public ProfileVm? CurrentProfile => _ps.CurrentProfile;
-
- public bool CanSave
- {
- get => _canSave;
- set => this.RaiseAndSetIfChanged(ref _canSave, value);
- }
- public bool IsExecutingSave => _isExecutingSave?.Value ?? false;
-
#endregion
#region Properties
@@ -148,8 +104,7 @@ public bool CanSave
public Interaction ShowSettingsDialogInteraction { get; }
- public IThemeService ThemeService => _themeService;
-
+ public ThemeVariant ThemeVariant { get; }
#endregion
private async Task ShowSettingsDialogAsync()
diff --git a/YMouseButtonControl.Core/ViewModels/MainWindow/Queries/Profiles/IsCacheDirty.cs b/YMouseButtonControl.Core/ViewModels/MainWindow/Queries/Profiles/IsCacheDirty.cs
new file mode 100644
index 0000000..9202692
--- /dev/null
+++ b/YMouseButtonControl.Core/ViewModels/MainWindow/Queries/Profiles/IsCacheDirty.cs
@@ -0,0 +1,16 @@
+using System;
+using System.Linq;
+using YMouseButtonControl.Core.Mappings;
+using YMouseButtonControl.Core.Services.Profiles;
+using YMouseButtonControl.Infrastructure.Context;
+
+namespace YMouseButtonControl.Core.ViewModels.MainWindow.Queries.Profiles;
+
+public static class IsCacheDirty
+{
+ public sealed class Handler(IProfilesCache profilesCache, YMouseButtonControlDbContext db)
+ {
+ public bool Execute() =>
+ !profilesCache.Profiles.SequenceEqual(db.Profiles.Select(ProfileMapper.MapToViewModel));
+ }
+}
diff --git a/YMouseButtonControl.Core/ViewModels/MainWindow/Queries/Theme/GetThemeVariant.cs b/YMouseButtonControl.Core/ViewModels/MainWindow/Queries/Theme/GetThemeVariant.cs
new file mode 100644
index 0000000..f1c315b
--- /dev/null
+++ b/YMouseButtonControl.Core/ViewModels/MainWindow/Queries/Theme/GetThemeVariant.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Linq;
+using Avalonia;
+using Avalonia.Styling;
+using YMouseButtonControl.Infrastructure.Context;
+
+namespace YMouseButtonControl.Core.ViewModels.MainWindow.Queries.Theme;
+
+public static class GetThemeVariant
+{
+ public sealed class Handler(YMouseButtonControlDbContext db)
+ {
+ public ThemeVariant Execute() =>
+ db.SettingInts.First(x => x.Name == "Theme").IntValue switch
+ {
+ 1 => Application.Current!.ActualThemeVariant == ThemeVariant.Light
+ ? ThemeVariant.Light
+ : ThemeVariant.Dark,
+ 2 => ThemeVariant.Light,
+ 3 => ThemeVariant.Dark,
+ _ => throw new ArgumentOutOfRangeException($"Invalid theme id"),
+ };
+ }
+}
diff --git a/YMouseButtonControl.Core/ViewModels/Models/BaseButtonMappingVm.cs b/YMouseButtonControl.Core/ViewModels/Models/BaseButtonMappingVm.cs
index f631ebe..49c0019 100644
--- a/YMouseButtonControl.Core/ViewModels/Models/BaseButtonMappingVm.cs
+++ b/YMouseButtonControl.Core/ViewModels/Models/BaseButtonMappingVm.cs
@@ -1,7 +1,7 @@
using System;
using Newtonsoft.Json;
using ReactiveUI;
-using YMouseButtonControl.DataAccess.Models;
+using YMouseButtonControl.Domain.Models;
namespace YMouseButtonControl.Core.ViewModels.Models;
@@ -24,6 +24,7 @@ public abstract class BaseButtonMappingVm : ReactiveObject, IEquatable this.RaiseAndSetIfChanged(ref _blockOriginalMouseInput, value);
}
+ protected BaseButtonMappingVm CreateClone(BaseButtonMappingVm clone)
+ {
+ clone.Id = Id;
+ clone.ProfileId = ProfileId;
+ clone.MouseButton = MouseButton;
+ clone.Index = Index;
+ clone.Selected = Selected;
+ clone.HasSettingsPopped = HasSettingsPopped;
+ clone.Enabled = Enabled;
+ clone.Description = Description;
+ clone.PriorityDescription = PriorityDescription;
+ clone.AutoRepeatDelay = AutoRepeatDelay;
+ clone.AutoRepeatRandomizeDelayEnabled = AutoRepeatRandomizeDelayEnabled;
+ clone.Keys = Keys;
+ clone.State = State;
+ clone.CanRaiseDialog = CanRaiseDialog;
+ clone.SimulatedKeystrokeType = SimulatedKeystrokeType?.Clone();
+ clone.BlockOriginalMouseInput = BlockOriginalMouseInput;
+
+ return clone;
+ }
+
+ public abstract BaseButtonMappingVm Clone();
+
public override string? ToString()
{
return Description;
@@ -124,7 +149,8 @@ public bool Equals(BaseButtonMappingVm? other)
&& AutoRepeatDelay == other.AutoRepeatDelay
&& AutoRepeatRandomizeDelayEnabled == other.AutoRepeatRandomizeDelayEnabled
&& Equals(SimulatedKeystrokeType, other.SimulatedKeystrokeType)
- && BlockOriginalMouseInput == other.BlockOriginalMouseInput;
+ && BlockOriginalMouseInput == other.BlockOriginalMouseInput
+ && Selected == other.Selected;
}
public override bool Equals(object? obj)
@@ -162,6 +188,8 @@ public DisabledMappingVm()
Index = 1;
Description = "Disabled";
}
+
+ public override BaseButtonMappingVm Clone() => CreateClone(new DisabledMappingVm());
}
public class NothingMappingVm : BaseButtonMappingVm
@@ -171,6 +199,8 @@ public NothingMappingVm()
Index = 0;
Description = "** No Change (Don't Intercept) **";
}
+
+ public override BaseButtonMappingVm Clone() => CreateClone(new NothingMappingVm());
}
public class SimulatedKeystrokeVm : BaseButtonMappingVm
@@ -183,6 +213,8 @@ public SimulatedKeystrokeVm()
BlockOriginalMouseInput = true;
}
+ public override BaseButtonMappingVm Clone() => CreateClone(new SimulatedKeystrokeVm());
+
public override string? ToString()
{
var myStr = SimulatedKeystrokeType is not null
@@ -210,4 +242,6 @@ public RightClickVm()
Index = 3;
Description = "Right Click";
}
+
+ public override BaseButtonMappingVm Clone() => CreateClone(new RightClickVm());
}
diff --git a/YMouseButtonControl.Core/ViewModels/Models/BaseSimulatedKeystrokeTypeVm.cs b/YMouseButtonControl.Core/ViewModels/Models/BaseSimulatedKeystrokeTypeVm.cs
index 6eb13bd..35ac957 100644
--- a/YMouseButtonControl.Core/ViewModels/Models/BaseSimulatedKeystrokeTypeVm.cs
+++ b/YMouseButtonControl.Core/ViewModels/Models/BaseSimulatedKeystrokeTypeVm.cs
@@ -34,6 +34,17 @@ public bool Enabled
set => this.RaiseAndSetIfChanged(ref _enabled, value);
}
+ protected BaseSimulatedKeystrokeTypeVm CreateClone(BaseSimulatedKeystrokeTypeVm clone)
+ {
+ clone.Index = Index;
+ clone.Description = Description;
+ clone.ShortDescription = ShortDescription;
+ clone.Enabled = Enabled;
+ return clone;
+ }
+
+ public abstract BaseSimulatedKeystrokeTypeVm Clone();
+
public override string ToString() => $"{Index + 1} {Description}";
public bool Equals(BaseSimulatedKeystrokeTypeVm? other)
@@ -54,7 +65,7 @@ public override bool Equals(object? obj)
return false;
if (ReferenceEquals(this, obj))
return true;
- if (obj.GetType() != this.GetType())
+ if (obj.GetType() != GetType())
return false;
return Equals((BaseSimulatedKeystrokeTypeVm)obj);
}
@@ -74,6 +85,9 @@ public AsMousePressedAndReleasedActionTypeVm()
ShortDescription = "pressed & released";
Enabled = true;
}
+
+ public override BaseSimulatedKeystrokeTypeVm Clone() =>
+ CreateClone(new AsMousePressedAndReleasedActionTypeVm());
}
public class DuringMouseActionTypeVm : BaseSimulatedKeystrokeTypeVm
@@ -85,6 +99,9 @@ public DuringMouseActionTypeVm()
ShortDescription = "during";
Enabled = true;
}
+
+ public override BaseSimulatedKeystrokeTypeVm Clone() =>
+ CreateClone(new DuringMouseActionTypeVm());
}
public class InAnotherThreadPressedActionTypeVm : BaseSimulatedKeystrokeTypeVm
@@ -96,6 +113,9 @@ public InAnotherThreadPressedActionTypeVm()
ShortDescription = "thread-down";
Enabled = false;
}
+
+ public override BaseSimulatedKeystrokeTypeVm Clone() =>
+ CreateClone(new InAnotherThreadPressedActionTypeVm());
}
public class InAnotherThreadReleasedActionTypeVm : BaseSimulatedKeystrokeTypeVm
@@ -107,6 +127,9 @@ public InAnotherThreadReleasedActionTypeVm()
ShortDescription = "thread-up";
Enabled = false;
}
+
+ public override BaseSimulatedKeystrokeTypeVm Clone() =>
+ CreateClone(new InAnotherThreadReleasedActionTypeVm());
}
public class MouseButtonPressedActionTypeVm : BaseSimulatedKeystrokeTypeVm
@@ -118,6 +141,9 @@ public MouseButtonPressedActionTypeVm()
ShortDescription = "pressed";
Enabled = true;
}
+
+ public override BaseSimulatedKeystrokeTypeVm Clone() =>
+ CreateClone(new MouseButtonPressedActionTypeVm());
}
public class MouseButtonReleasedActionTypeVm : BaseSimulatedKeystrokeTypeVm
@@ -129,6 +155,9 @@ public MouseButtonReleasedActionTypeVm()
ShortDescription = "released";
Enabled = true;
}
+
+ public override BaseSimulatedKeystrokeTypeVm Clone() =>
+ CreateClone(new MouseButtonReleasedActionTypeVm());
}
public class RepeatedlyWhileButtonDownActionTypeVm : BaseSimulatedKeystrokeTypeVm
@@ -140,6 +169,9 @@ public RepeatedlyWhileButtonDownActionTypeVm()
ShortDescription = "repeat";
Enabled = true;
}
+
+ public override BaseSimulatedKeystrokeTypeVm Clone() =>
+ CreateClone(new RepeatedlyWhileButtonDownActionTypeVm());
}
public class StickyHoldActionTypeVm : BaseSimulatedKeystrokeTypeVm
@@ -151,6 +183,9 @@ public StickyHoldActionTypeVm()
ShortDescription = "sticky hold";
Enabled = true;
}
+
+ public override BaseSimulatedKeystrokeTypeVm Clone() =>
+ CreateClone(new StickyHoldActionTypeVm());
}
public class StickyRepeatActionTypeVm : BaseSimulatedKeystrokeTypeVm
@@ -162,4 +197,7 @@ public StickyRepeatActionTypeVm()
ShortDescription = "sticky repeat";
Enabled = true;
}
+
+ public override BaseSimulatedKeystrokeTypeVm Clone() =>
+ CreateClone(new StickyRepeatActionTypeVm());
}
diff --git a/YMouseButtonControl.Core/ViewModels/Models/ProfileVm.cs b/YMouseButtonControl.Core/ViewModels/Models/ProfileVm.cs
index 60571de..5d679df 100644
--- a/YMouseButtonControl.Core/ViewModels/Models/ProfileVm.cs
+++ b/YMouseButtonControl.Core/ViewModels/Models/ProfileVm.cs
@@ -1,10 +1,12 @@
using System;
using System.Collections.Generic;
+using System.Collections.ObjectModel;
using System.Linq;
+using System.Reactive.Linq;
using DynamicData;
using Newtonsoft.Json;
using ReactiveUI;
-using YMouseButtonControl.DataAccess.Models;
+using YMouseButtonControl.Domain.Models;
namespace YMouseButtonControl.Core.ViewModels.Models;
@@ -21,14 +23,156 @@ public class ProfileVm : ReactiveObject, IEquatable
private string _matchType = "N/A";
private int _displayPriority;
- private readonly SourceCache _btnSc;
+ private readonly SourceCache _btnSc;
+ private readonly ReadOnlyObservableCollection _btnsMappings;
+ private readonly ReadOnlyObservableCollection _mb1Mappings;
+ private readonly ReadOnlyObservableCollection _mb2Mappings;
+ private readonly ReadOnlyObservableCollection _mb3Mappings;
+ private readonly ReadOnlyObservableCollection _mb4Mappings;
+ private readonly ReadOnlyObservableCollection _mb5Mappings;
+ private readonly ReadOnlyObservableCollection _mwuMappings;
+ private readonly ReadOnlyObservableCollection _mwdMappings;
+ private readonly ReadOnlyObservableCollection _mwlMappings;
+ private readonly ReadOnlyObservableCollection _mwrMappings;
+
+ private readonly ObservableAsPropertyHelper _mb1;
+ private readonly ObservableAsPropertyHelper _mb2;
+ private readonly ObservableAsPropertyHelper _mb3;
+ private readonly ObservableAsPropertyHelper