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 _mb4; + private readonly ObservableAsPropertyHelper _mb5; + private readonly ObservableAsPropertyHelper _mwu; + private readonly ObservableAsPropertyHelper _mwd; + private readonly ObservableAsPropertyHelper _mwl; + private readonly ObservableAsPropertyHelper _mwr; public ProfileVm(List buttonMappings) { - _btnSc = new SourceCache(x => x.MouseButton); + _btnSc = new SourceCache(x => x.Id); _btnSc.Edit(x => x.AddOrUpdate(buttonMappings)); + _btnSc.Connect().AutoRefresh().Bind(out _btnsMappings).Subscribe(); + _btnSc + .Connect() + .Filter(x => x.MouseButton == MouseButton.Mb1) + .AutoRefresh() + .Bind(out _mb1Mappings) + .Subscribe(); + _btnSc + .Connect() + .Filter(x => x.MouseButton == MouseButton.Mb2) + .AutoRefresh() + .Bind(out _mb2Mappings) + .Subscribe(); + _btnSc + .Connect() + .Filter(x => x.MouseButton == MouseButton.Mb3) + .AutoRefresh() + .Bind(out _mb3Mappings) + .Subscribe(); + _btnSc + .Connect() + .Filter(x => x.MouseButton == MouseButton.Mb4) + .AutoRefresh() + .Bind(out _mb4Mappings) + .Subscribe(); + _btnSc + .Connect() + .Filter(x => x.MouseButton == MouseButton.Mb5) + .AutoRefresh() + .Bind(out _mb5Mappings) + .Subscribe(); + _btnSc + .Connect() + .Filter(x => x.MouseButton == MouseButton.Mwu) + .AutoRefresh() + .Bind(out _mwuMappings) + .Subscribe(); + _btnSc + .Connect() + .Filter(x => x.MouseButton == MouseButton.Mwd) + .AutoRefresh() + .Bind(out _mwdMappings) + .Subscribe(); + _btnSc + .Connect() + .Filter(x => x.MouseButton == MouseButton.Mwl) + .AutoRefresh() + .Bind(out _mwlMappings) + .Subscribe(); + _btnSc + .Connect() + .Filter(x => x.MouseButton == MouseButton.Mwr) + .AutoRefresh() + .Bind(out _mwrMappings) + .Subscribe(); + + _mb1 = _btnSc + .Connect() + .Filter(x => x.MouseButton == MouseButton.Mb1) + .WhenPropertyChanged(x => x.Selected) + .Where(x => x.Value) + .Select(x => x.Sender) + .ToProperty(this, x => x.MouseButton1); + _mb2 = _btnSc + .Connect() + .Filter(x => x.MouseButton == MouseButton.Mb2) + .WhenPropertyChanged(x => x.Selected) + .Where(x => x.Value) + .Select(x => x.Sender) + .ToProperty(this, x => x.MouseButton2); + _mb3 = _btnSc + .Connect() + .Filter(x => x.MouseButton == MouseButton.Mb3) + .WhenPropertyChanged(x => x.Selected) + .Where(x => x.Value) + .Select(x => x.Sender) + .ToProperty(this, x => x.MouseButton3); + _mb4 = _btnSc + .Connect() + .Filter(x => x.MouseButton == MouseButton.Mb4) + .WhenPropertyChanged(x => x.Selected) + .Where(x => x.Value) + .Select(x => x.Sender) + .ToProperty(this, x => x.MouseButton4); + _mb5 = _btnSc + .Connect() + .Filter(x => x.MouseButton == MouseButton.Mb5) + .WhenPropertyChanged(x => x.Selected) + .Where(x => x.Value) + .Select(x => x.Sender) + .ToProperty(this, x => x.MouseButton5); + _mwu = _btnSc + .Connect() + .Filter(x => x.MouseButton == MouseButton.Mwu) + .WhenPropertyChanged(x => x.Selected) + .Where(x => x.Value) + .Select(x => x.Sender) + .ToProperty(this, x => x.MouseWheelUp); + _mwd = _btnSc + .Connect() + .Filter(x => x.MouseButton == MouseButton.Mwd) + .WhenPropertyChanged(x => x.Selected) + .Where(x => x.Value) + .Select(x => x.Sender) + .ToProperty(this, x => x.MouseWheelDown); + _mwl = _btnSc + .Connect() + .Filter(x => x.MouseButton == MouseButton.Mwl) + .WhenPropertyChanged(x => x.Selected) + .Where(x => x.Value) + .Select(x => x.Sender) + .ToProperty(this, x => x.MouseWheelLeft); + _mwr = _btnSc + .Connect() + .Filter(x => x.MouseButton == MouseButton.Mwr) + .WhenPropertyChanged(x => x.Selected) + .Where(x => x.Value) + .Select(x => x.Sender) + .ToProperty(this, x => x.MouseWheelRight); } + [JsonIgnore] + public SourceCache BtnSc => _btnSc; + [JsonIgnore] public int Id { get; set; } public bool IsDefault { get; set; } @@ -84,106 +228,61 @@ public required string MatchType set => this.RaiseAndSetIfChanged(ref _matchType, value); } - public List ButtonMappings => _btnSc.Items.ToList(); + public ReadOnlyObservableCollection ButtonMappings => _btnsMappings; [JsonIgnore] - public BaseButtonMappingVm MouseButton1 - { - get => _btnSc.Items.First(x => x.MouseButton == MouseButton.Mb1); - set - { - _btnSc.Edit(updater => updater.AddOrUpdate(value)); - this.RaisePropertyChanged(); - } - } + public ReadOnlyObservableCollection Mb1Mappings => _mb1Mappings; [JsonIgnore] - public BaseButtonMappingVm MouseButton2 - { - get => _btnSc.Items.First(x => x.MouseButton == MouseButton.Mb2); - set - { - _btnSc.Edit(updater => updater.AddOrUpdate(value)); - this.RaisePropertyChanged(); - } - } + public ReadOnlyObservableCollection Mb2Mappings => _mb2Mappings; [JsonIgnore] - public BaseButtonMappingVm MouseButton3 - { - get => _btnSc.Items.First(x => x.MouseButton == MouseButton.Mb3); - set - { - _btnSc.Edit(updater => updater.AddOrUpdate(value)); - this.RaisePropertyChanged(); - } - } + public ReadOnlyObservableCollection Mb3Mappings => _mb3Mappings; [JsonIgnore] - public BaseButtonMappingVm MouseButton4 - { - get => _btnSc.Items.First(x => x.MouseButton == MouseButton.Mb4); - set - { - _btnSc.Edit(updater => updater.AddOrUpdate(value)); - this.RaisePropertyChanged(); - } - } + public ReadOnlyObservableCollection Mb4Mappings => _mb4Mappings; [JsonIgnore] - public BaseButtonMappingVm MouseButton5 - { - get => _btnSc.Items.First(x => x.MouseButton == MouseButton.Mb5); - set - { - _btnSc.Edit(updater => updater.AddOrUpdate(value)); - this.RaisePropertyChanged(); - } - } + public ReadOnlyObservableCollection Mb5Mappings => _mb5Mappings; [JsonIgnore] - public BaseButtonMappingVm MouseWheelUp - { - get => _btnSc.Items.First(x => x.MouseButton == MouseButton.Mwu); - set - { - _btnSc.Edit(updater => updater.AddOrUpdate(value)); - this.RaisePropertyChanged(); - } - } + public ReadOnlyObservableCollection MwuMappings => _mwuMappings; [JsonIgnore] - public BaseButtonMappingVm MouseWheelDown - { - get => _btnSc.Items.First(x => x.MouseButton == MouseButton.Mwd); - set - { - _btnSc.Edit(updater => updater.AddOrUpdate(value)); - this.RaisePropertyChanged(); - } - } + public ReadOnlyObservableCollection MwdMappings => _mwdMappings; [JsonIgnore] - public BaseButtonMappingVm MouseWheelLeft - { - get => _btnSc.Items.First(x => x.MouseButton == MouseButton.Mwl); - set - { - _btnSc.Edit(updater => updater.AddOrUpdate(value)); - this.RaisePropertyChanged(); - } - } + public ReadOnlyObservableCollection MwlMappings => _mwlMappings; [JsonIgnore] - public BaseButtonMappingVm MouseWheelRight - { - get => _btnSc.Items.First(x => x.MouseButton == MouseButton.Mwr); - set - { - _btnSc.Edit(updater => updater.AddOrUpdate(value)); - this.RaisePropertyChanged(); - } - } + public ReadOnlyObservableCollection MwrMappings => _mwrMappings; + + [JsonIgnore] + public BaseButtonMappingVm MouseButton1 => _mb1.Value; + + [JsonIgnore] + public BaseButtonMappingVm MouseButton2 => _mb2.Value; + + [JsonIgnore] + public BaseButtonMappingVm MouseButton3 => _mb3.Value; + + [JsonIgnore] + public BaseButtonMappingVm MouseButton4 => _mb4.Value; + + [JsonIgnore] + public BaseButtonMappingVm MouseButton5 => _mb5.Value; + + [JsonIgnore] + public BaseButtonMappingVm MouseWheelUp => _mwu.Value; + + [JsonIgnore] + public BaseButtonMappingVm MouseWheelDown => _mwd.Value; + + [JsonIgnore] + public BaseButtonMappingVm MouseWheelLeft => _mwl.Value; + + [JsonIgnore] + public BaseButtonMappingVm MouseWheelRight => _mwr.Value; public int DisplayPriority { @@ -191,6 +290,22 @@ public int DisplayPriority set => this.RaiseAndSetIfChanged(ref _displayPriority, value); } + public ProfileVm Clone() => + new(ButtonMappings.Select(x => x.Clone()).ToList()) + { + Id = Id, + Checked = Checked, + DisplayPriority = DisplayPriority, + IsDefault = IsDefault, + Name = Name, + Description = Description, + MatchType = MatchType, + ParentClass = ParentClass, + Process = Process, + WindowClass = WindowClass, + WindowCaption = WindowCaption, + }; + public bool Equals(ProfileVm? other) { if (ReferenceEquals(null, other)) diff --git a/YMouseButtonControl.Core/ViewModels/Models/ThemeVm.cs b/YMouseButtonControl.Core/ViewModels/Models/ThemeVm.cs deleted file mode 100644 index e2eeb55..0000000 --- a/YMouseButtonControl.Core/ViewModels/Models/ThemeVm.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace YMouseButtonControl.Core.ViewModels.Models; - -public class ThemeVm -{ - public int Id { get; set; } - public required string Name { get; set; } - public required string Background { get; set; } - public required string Highlight { get; set; } -} diff --git a/YMouseButtonControl.Core/ViewModels/MouseCombo/MouseComboHandlerRegistrations.cs b/YMouseButtonControl.Core/ViewModels/MouseCombo/MouseComboHandlerRegistrations.cs new file mode 100644 index 0000000..95646b7 --- /dev/null +++ b/YMouseButtonControl.Core/ViewModels/MouseCombo/MouseComboHandlerRegistrations.cs @@ -0,0 +1,10 @@ +using Microsoft.Extensions.DependencyInjection; +using YMouseButtonControl.Core.ViewModels.MouseCombo.Queries.Theme; + +namespace YMouseButtonControl.Core.ViewModels.MouseCombo; + +public static class MouseComboHandlerRegistrations +{ + public static void RegisterCommon(IServiceCollection services) => + services.AddScoped().AddScoped(); +} diff --git a/YMouseButtonControl.Core/ViewModels/LayerViewModel/MouseComboViewModel.cs b/YMouseButtonControl.Core/ViewModels/MouseCombo/MouseComboViewModel.cs similarity index 57% rename from YMouseButtonControl.Core/ViewModels/LayerViewModel/MouseComboViewModel.cs rename to YMouseButtonControl.Core/ViewModels/MouseCombo/MouseComboViewModel.cs index 933d26b..6276c0b 100644 --- a/YMouseButtonControl.Core/ViewModels/LayerViewModel/MouseComboViewModel.cs +++ b/YMouseButtonControl.Core/ViewModels/MouseCombo/MouseComboViewModel.cs @@ -1,32 +1,30 @@ using System; -using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Reactive; using System.Reactive.Linq; using System.Timers; using Avalonia.Media; +using Avalonia.Threading; using DynamicData; using ReactiveUI; using YMouseButtonControl.Core.Services.KeyboardAndMouse.Enums; -using YMouseButtonControl.Core.Services.KeyboardAndMouse.EventArgs; using YMouseButtonControl.Core.Services.KeyboardAndMouse.Implementations; -using YMouseButtonControl.Core.Services.Profiles; -using YMouseButtonControl.Core.Services.Theme; +using YMouseButtonControl.Core.ViewModels.Dialogs.SimulatedKeystrokesDialog; using YMouseButtonControl.Core.ViewModels.Models; -using YMouseButtonControl.DataAccess.Models; +using YMouseButtonControl.Core.ViewModels.MouseCombo.Queries.Theme; +using YMouseButtonControl.Domain.Models; -namespace YMouseButtonControl.Core.ViewModels.LayerViewModel; +namespace YMouseButtonControl.Core.ViewModels.MouseCombo; public interface IMouseComboViewModel { BaseButtonMappingVm? SelectedBtnMap { get; } - IShowSimulatedKeystrokesDialogService ShowSimulatedKeystrokesDialogService { get; } + ReadOnlyObservableCollection BtnMappings { get; } } public class MouseComboViewModel : ReactiveObject, IMouseComboViewModel, IDisposable { - private readonly IProfilesService _profilesService; private readonly ReadOnlyObservableCollection _btnMappings; private BaseButtonMappingVm? _selectedBtnMap; private readonly IDisposable? _mbDownDisposable; @@ -37,14 +35,22 @@ public class MouseComboViewModel : ReactiveObject, IMouseComboViewModel, IDispos private string? _labelTxt; public MouseComboViewModel( - IProfilesService profilesService, + SourceCache BtnSc, IMouseListener mouseListener, - IThemeService themeService, + ISimulatedKeystrokesDialogVmFactory simulatedKeystrokesDialogVmFactory, + GetThemeBackground.Handler getThemeBackgroundHandler, + GetThemeHighlight.Handler getThemeHighlightHandler, MouseButton mouseButton, - IShowSimulatedKeystrokesDialogService showSimulatedKeystrokesDialogService + ReadOnlyObservableCollection btnMappings, + Interaction< + SimulatedKeystrokesDialogViewModel, + SimulatedKeystrokeVm? + > showSimulatedKeystrokesPickerInteraction ) { - _backgroundColor = themeService.Background; + _btnMappings = btnMappings; + _backgroundColor = getThemeBackgroundHandler.Execute(); + switch (mouseButton) { case MouseButton.Mb1: @@ -56,14 +62,18 @@ IShowSimulatedKeystrokesDialogService showSimulatedKeystrokesDialogService { if (next.Button == (YMouseButton)(mouseButton + 1)) { - BackgroundColor = themeService.Highlight; + Dispatcher.UIThread.Post( + () => BackgroundColor = getThemeHighlightHandler.Execute() + ); } }); _mbUpDisposable = mouseListener.OnMouseReleasedChanged.Subscribe(next => { if (next.Button == (YMouseButton)(mouseButton + 1)) { - BackgroundColor = themeService.Background; + Dispatcher.UIThread.Post( + () => BackgroundColor = getThemeBackgroundHandler.Execute() + ); } }); break; @@ -73,7 +83,9 @@ IShowSimulatedKeystrokesDialogService showSimulatedKeystrokesDialogService case MouseButton.Mwr: _wheelTimer.Elapsed += delegate { - BackgroundColor = themeService.Background; + Dispatcher.UIThread.Post( + () => BackgroundColor = getThemeBackgroundHandler.Execute() + ); }; _mWheelDisposable = mouseListener.OnMouseWheelChanged.Subscribe(next => { @@ -85,7 +97,11 @@ IShowSimulatedKeystrokesDialogService showSimulatedKeystrokesDialogService when mouseButton == MouseButton.Mwr: case WheelScrollDirection.HorizontalLeft when mouseButton == MouseButton.Mwl: - MouseWheelDoHighlight(); + BackgroundColor = getThemeHighlightHandler.Execute(); + if (!_wheelTimer.Enabled) + { + _wheelTimer.Start(); + } break; } }); @@ -94,30 +110,25 @@ IShowSimulatedKeystrokesDialogService showSimulatedKeystrokesDialogService throw new ArgumentOutOfRangeException(nameof(mouseButton), mouseButton, null); } - _profilesService = profilesService; - SourceCache sourceButtonMappings = new(x => x.Index); - var myOp = sourceButtonMappings.Connect().AutoRefresh().Bind(out _btnMappings).Subscribe(); - sourceButtonMappings.AddOrUpdate(GetButtonMappings(mouseButton)); - this.WhenAnyValue(x => x._profilesService.CurrentProfile) - .WhereNotNull() + SelectedBtnMap = BtnMappings.First(x => x.Selected); + this.WhenAnyValue(x => x.SelectedBtnMap) .DistinctUntilChanged() - .Subscribe(newProfile => + .WhereNotNull() + .Subscribe(current => { - sourceButtonMappings.Edit(updater => + BtnSc.Edit(inner => { - updater.Clear(); - updater.AddOrUpdate(GetButtonMappings(mouseButton)); - - var src = - newProfile.ButtonMappings.First(x => x.MouseButton == mouseButton) - ?? throw new Exception("Error retrieving button mapping"); - updater.AddOrUpdate(src); + var previous = inner.Items.FirstOrDefault(x => + x.Selected && x.MouseButton == current.MouseButton && !x.Equals(current) + ); + if (previous != null) + { + previous.Selected = false; + } + var target = inner.Items.First(x => x.Id == current.Id); + target.Selected = true; }); - - var found = _btnMappings.FirstOrDefault(x => x.Selected); - SelectedBtnMap = found ?? _btnMappings.MinBy(x => x.Index); }); - ShowSimulatedKeystrokesDialogService = showSimulatedKeystrokesDialogService; var canClickUserClickedSettingsBtn = this.WhenAnyValue( x => x.SelectedBtnMap, selector: btnMap => btnMap is SimulatedKeystrokeVm @@ -130,31 +141,36 @@ IShowSimulatedKeystrokesDialogService showSimulatedKeystrokesDialogService return; } - var newMapping = - await ShowSimulatedKeystrokesDialogService.ShowSimulatedKeystrokesDialog( + var newMapping = await showSimulatedKeystrokesPickerInteraction.Handle( + simulatedKeystrokesDialogVmFactory.Create( "Something", mouseButton, SelectedBtnMap - ); + ) + ); if (newMapping is not null) { newMapping.Selected = true; - sourceButtonMappings.AddOrUpdate(newMapping); + var previouslySelectedBtnMap = BtnMappings.FirstOrDefault(x => + x.Selected && x.Id != newMapping.Id + ); + if ( + previouslySelectedBtnMap is not null + && !previouslySelectedBtnMap.Equals(newMapping) + ) + { + previouslySelectedBtnMap.Selected = false; + BtnSc.AddOrUpdate([previouslySelectedBtnMap, newMapping]); + } + else + { + BtnSc.AddOrUpdate([newMapping]); + } SelectedBtnMap = newMapping; } }, canClickUserClickedSettingsBtn ); - return; - - void MouseWheelDoHighlight() - { - BackgroundColor = themeService.Highlight; - if (!_wheelTimer.Enabled) - { - _wheelTimer.Start(); - } - } } public IBrush BackgroundColor @@ -163,8 +179,6 @@ public IBrush BackgroundColor set => this.RaiseAndSetIfChanged(ref _backgroundColor, value); } - public IShowSimulatedKeystrokesDialogService ShowSimulatedKeystrokesDialogService { get; } - public BaseButtonMappingVm? SelectedBtnMap { get => _selectedBtnMap; @@ -181,41 +195,6 @@ public string? LabelTxt public ReactiveCommand UserClickedEditSettingButton { get; set; } - private static IEnumerable GetButtonMappings(MouseButton mouseButton) => - ButtonMappingDictionary.Select(x => x.Value(mouseButton)); - - private static readonly Dictionary< - ButtonMappings, - Func - > ButtonMappingDictionary = - new() - { - { - ButtonMappings.Nothing, - mb => new NothingMappingVm { MouseButton = mb } - }, - { - ButtonMappings.Disabled, - mb => new DisabledMappingVm { MouseButton = mb } - }, - { - ButtonMappings.SimulatedKeystrokes, - mb => new SimulatedKeystrokeVm { MouseButton = mb } - }, - { - ButtonMappings.RightClick, - mb => new RightClickVm { MouseButton = mb } - }, - }; - - private enum ButtonMappings - { - Nothing, - Disabled, - SimulatedKeystrokes, - RightClick, - } - public void Dispose() { _mbDownDisposable?.Dispose(); diff --git a/YMouseButtonControl.Core/ViewModels/MouseCombo/MouseComboViewModelFactory.cs b/YMouseButtonControl.Core/ViewModels/MouseCombo/MouseComboViewModelFactory.cs new file mode 100644 index 0000000..f20d024 --- /dev/null +++ b/YMouseButtonControl.Core/ViewModels/MouseCombo/MouseComboViewModelFactory.cs @@ -0,0 +1,55 @@ +using System.Collections.ObjectModel; +using DynamicData; +using YMouseButtonControl.Core.Services.KeyboardAndMouse.Implementations; +using YMouseButtonControl.Core.ViewModels.Dialogs.SimulatedKeystrokesDialog; +using YMouseButtonControl.Core.ViewModels.Models; +using YMouseButtonControl.Core.ViewModels.MouseCombo.Queries.Theme; +using YMouseButtonControl.Domain.Models; + +namespace YMouseButtonControl.Core.ViewModels.MouseCombo; + +public interface IMouseComboViewModelFactory +{ + IMouseComboViewModel? CreateWithMouseButton( + SourceCache BtnSc, + MouseButton mouseButton, + string labelTxt, + ReadOnlyObservableCollection buttonMappings, + ReactiveUI.Interaction< + SimulatedKeystrokesDialogViewModel, + SimulatedKeystrokeVm? + >? showSimulatedKeystrokesPickerInteraction + ); +} + +public class MouseComboViewModelFactory( + IMouseListener mouseListener, + ISimulatedKeystrokesDialogVmFactory simulatedKeystrokesDialogVmFactory, + GetThemeBackground.Handler getThemeBackgroundHandler, + GetThemeHighlight.Handler getThemeHighlightHandler +) : IMouseComboViewModelFactory +{ + public IMouseComboViewModel CreateWithMouseButton( + SourceCache BtnSc, + MouseButton mouseButton, + string labelTxt, + ReadOnlyObservableCollection buttonMappings, + ReactiveUI.Interaction< + SimulatedKeystrokesDialogViewModel, + SimulatedKeystrokeVm? + >? showSimulatedKeystrokesPickerInteraction + ) => + new MouseComboViewModel( + BtnSc, + mouseListener, + simulatedKeystrokesDialogVmFactory, + getThemeBackgroundHandler, + getThemeHighlightHandler, + mouseButton, + buttonMappings, + showSimulatedKeystrokesPickerInteraction! + ) + { + LabelTxt = labelTxt, + }; +} diff --git a/YMouseButtonControl.Core/ViewModels/MouseCombo/Queries/Theme/GetThemeBackground.cs b/YMouseButtonControl.Core/ViewModels/MouseCombo/Queries/Theme/GetThemeBackground.cs new file mode 100644 index 0000000..0638a34 --- /dev/null +++ b/YMouseButtonControl.Core/ViewModels/MouseCombo/Queries/Theme/GetThemeBackground.cs @@ -0,0 +1,63 @@ +using System; +using System.Linq; +using Avalonia; +using Avalonia.Media; +using YMouseButtonControl.Infrastructure.Context; + +namespace YMouseButtonControl.Core.ViewModels.MouseCombo.Queries.Theme; + +public static class GetThemeBackground +{ + public sealed class Handler(YMouseButtonControlDbContext db) + { + private static IBrush? Background; + + public IBrush Execute() + { + if (Background is not null) + { + return Background; + } + var themeId = db.SettingInts.First(x => x.Name == "Theme").IntValue; + var themeBackground = + db.Themes.Find(themeId)?.Background + ?? throw new Exception($"Error retrieving theme background for id {themeId}"); + Background = GetBackground(themeBackground); + return Background; + } + + private static IBrush GetBackground(string background) + { + // Background is of the form #aarrggbb + if (background.StartsWith('#')) + { + return Brush.Parse(background); + } + + // Background is an avalonia resource like SystemAltHighColor + if ( + Application.Current!.TryGetResource( + 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(background); + } + } +} diff --git a/YMouseButtonControl.Core/ViewModels/MouseCombo/Queries/Theme/GetThemeHighlight.cs b/YMouseButtonControl.Core/ViewModels/MouseCombo/Queries/Theme/GetThemeHighlight.cs new file mode 100644 index 0000000..ffb0356 --- /dev/null +++ b/YMouseButtonControl.Core/ViewModels/MouseCombo/Queries/Theme/GetThemeHighlight.cs @@ -0,0 +1,63 @@ +using System; +using System.Linq; +using Avalonia; +using Avalonia.Media; +using YMouseButtonControl.Infrastructure.Context; + +namespace YMouseButtonControl.Core.ViewModels.MouseCombo.Queries.Theme; + +public static class GetThemeHighlight +{ + public sealed class Handler(YMouseButtonControlDbContext db) + { + private static IBrush? Highlight; + + public IBrush Execute() + { + if (Highlight is not null) + { + return Highlight; + } + var themeId = db.SettingInts.First(x => x.Name == "Theme").IntValue; + var themeBackground = + db.Themes.Find(themeId)?.Highlight + ?? throw new Exception($"Error retrieving theme highlight for id {themeId}"); + Highlight = GetHighlight(themeBackground); + return Highlight; + } + + private static IBrush GetHighlight(string highlight) + { + // Highlight is of the form #aarrggbb + if (highlight.StartsWith('#')) + { + return Brush.Parse(highlight); + } + + // Highlight is an avalonia resource like SystemAltHighColor + if ( + Application.Current!.TryGetResource( + 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(highlight); + } + } +} diff --git a/YMouseButtonControl.Core/ViewModels/ProfilesInformation/ProfilesInformationViewModel.cs b/YMouseButtonControl.Core/ViewModels/ProfilesInformation/ProfilesInformationViewModel.cs new file mode 100644 index 0000000..64ba598 --- /dev/null +++ b/YMouseButtonControl.Core/ViewModels/ProfilesInformation/ProfilesInformationViewModel.cs @@ -0,0 +1,33 @@ +using ReactiveUI; +using YMouseButtonControl.Core.Services.Profiles; + +namespace YMouseButtonControl.Core.ViewModels.ProfilesInformation; + +public interface IProfilesInformationViewModel { } + +public class ProfilesInformationViewModel : ViewModelBase, IProfilesInformationViewModel +{ + private IProfilesCache ProfilesCache { get; } + private readonly ObservableAsPropertyHelper _profileInfo; + public ProfilesInformationModel ProfileInfo => _profileInfo.Value; + + public ProfilesInformationViewModel(IProfilesCache profilesCache) + { + ProfilesCache = profilesCache; + _profileInfo = this.WhenAnyValue( + x => x.ProfilesCache.CurrentProfile, + selector: vm => new ProfilesInformationModel(vm) + ) + .ToProperty(this, x => x.ProfileInfo); + } +} + +public class ProfilesInformationModel(Models.ProfileVm? vm) +{ + public string Description { get; } = vm?.Description ?? "N/A"; + public string WindowCaption { get; } = vm?.WindowCaption ?? "N/A"; + public string Process { get; } = vm?.Process ?? "N/A"; + public string WindowClass { get; } = vm?.WindowClass ?? "N/A"; + public string ParentClass { get; } = vm?.ParentClass ?? "N/A"; + public string MatchType { get; } = vm?.MatchType ?? "N/A"; +} diff --git a/YMouseButtonControl.Core/ViewModels/ProfilesInformationViewModel/ProfilesInformationViewModel.cs b/YMouseButtonControl.Core/ViewModels/ProfilesInformationViewModel/ProfilesInformationViewModel.cs deleted file mode 100644 index 9b27dad..0000000 --- a/YMouseButtonControl.Core/ViewModels/ProfilesInformationViewModel/ProfilesInformationViewModel.cs +++ /dev/null @@ -1,15 +0,0 @@ -using YMouseButtonControl.Core.Services.Profiles; - -namespace YMouseButtonControl.Core.ViewModels.ProfilesInformationViewModel; - -public interface IProfilesInformationViewModel -{ - public IProfilesService ProfilesService { get; } -} - -public class ProfilesInformationViewModel(IProfilesService profilesService) - : ViewModelBase, - IProfilesInformationViewModel -{ - public IProfilesService ProfilesService { get; } = profilesService; -} diff --git a/YMouseButtonControl.Core/ViewModels/ProfilesList/Commands/Profiles/AddProfile.cs b/YMouseButtonControl.Core/ViewModels/ProfilesList/Commands/Profiles/AddProfile.cs new file mode 100644 index 0000000..2c9048d --- /dev/null +++ b/YMouseButtonControl.Core/ViewModels/ProfilesList/Commands/Profiles/AddProfile.cs @@ -0,0 +1,23 @@ +using System.Linq; +using YMouseButtonControl.Core.Services.Profiles; +using YMouseButtonControl.Core.ViewModels.Models; + +namespace YMouseButtonControl.Core.ViewModels.ProfilesList.Commands.Profiles; + +public static class AddProfile +{ + public sealed record Command(ProfileVm Profile); + + public sealed class Handler(IProfilesCache profilesService) + { + public void Execute(Command c) + { + profilesService.ProfilesSc.Edit(inner => + { + c.Profile.Id = inner.Items.Max(x => x.Id) + 1; + c.Profile.DisplayPriority = inner.Items.Max(x => x.DisplayPriority) + 1; + inner.AddOrUpdate(c.Profile); + }); + } + } +} diff --git a/YMouseButtonControl.Core/ViewModels/ProfilesList/Commands/Profiles/CopyProfile.cs b/YMouseButtonControl.Core/ViewModels/ProfilesList/Commands/Profiles/CopyProfile.cs new file mode 100644 index 0000000..5ee0ff3 --- /dev/null +++ b/YMouseButtonControl.Core/ViewModels/ProfilesList/Commands/Profiles/CopyProfile.cs @@ -0,0 +1,61 @@ +using System; +using System.Linq; +using YMouseButtonControl.Core.Services.Profiles; +using YMouseButtonControl.Core.ViewModels.Models; +using YMouseButtonControl.Core.ViewModels.ProfilesList.Models; + +namespace YMouseButtonControl.Core.ViewModels.ProfilesList.Commands.Profiles; + +public static class CopyProfile +{ + public sealed record Command( + int ProfileIdToCopyBtnMappingsFrom, + string Name, + string Description, + bool Checked, + string Process, + string MatchType, + string ParentClass, + string WindowClass + ); + + public sealed class Handler(IProfilesCache profilesService) + { + public ProfilesListProfileModel Execute(Command c) + { + ProfilesListProfileModel? result = default; + profilesService.ProfilesSc.Edit(inner => + { + var clonedBtnMappings = inner + .Items.First(x => x.Id == c.ProfileIdToCopyBtnMappingsFrom) + .Clone() + .ButtonMappings.ToList(); + var newProfileVm = new ProfileVm(clonedBtnMappings) + { + Id = inner.Items.Max(x => x.Id) + 1, + DisplayPriority = inner.Items.Max(x => x.DisplayPriority) + 1, + Description = c.Description, + MatchType = c.MatchType, + ParentClass = c.ParentClass, + Process = c.Process, + WindowClass = c.WindowClass, + Name = c.Name, + }; + + foreach (var bm in newProfileVm.ButtonMappings) + { + bm.ProfileId = newProfileVm.Id; + } + var maxBtnMappingId = inner.Items.SelectMany(x => x.ButtonMappings).Max(x => x.Id); + foreach (var bm in newProfileVm.ButtonMappings) + { + bm.Id = ++maxBtnMappingId; + } + inner.AddOrUpdate(newProfileVm); + result = new ProfilesListProfileModel(newProfileVm, profilesService); + }); + + return result ?? throw new Exception("Result null exception"); + } + } +} diff --git a/YMouseButtonControl.Core/ViewModels/ProfilesList/Commands/Profiles/ExportProfile.cs b/YMouseButtonControl.Core/ViewModels/ProfilesList/Commands/Profiles/ExportProfile.cs new file mode 100644 index 0000000..f127fa2 --- /dev/null +++ b/YMouseButtonControl.Core/ViewModels/ProfilesList/Commands/Profiles/ExportProfile.cs @@ -0,0 +1,27 @@ +using System.IO; +using System.Linq; +using Newtonsoft.Json; +using YMouseButtonControl.Core.Services.Profiles; + +namespace YMouseButtonControl.Core.ViewModels.ProfilesList.Commands.Profiles; + +public static class ExportProfile +{ + public sealed record Command(int Id, string Path); + + public sealed class Handler(IProfilesCache profilesCache, ProfileVmConverter profileVmConverter) + { + public void Execute(Command c) + { + var profileToExport = profilesCache.Profiles.First(x => x.Id == c.Id); + var settings = new JsonSerializerSettings + { + TypeNameHandling = TypeNameHandling.Auto, + Formatting = Formatting.Indented, + }; + settings.Converters.Add(profileVmConverter); + var jsonString = JsonConvert.SerializeObject(profileToExport, settings); + File.WriteAllText(c.Path, jsonString); + } + } +} diff --git a/YMouseButtonControl.Core/ViewModels/ProfilesList/Commands/Profiles/ImportProfile.cs b/YMouseButtonControl.Core/ViewModels/ProfilesList/Commands/Profiles/ImportProfile.cs new file mode 100644 index 0000000..b4abe08 --- /dev/null +++ b/YMouseButtonControl.Core/ViewModels/ProfilesList/Commands/Profiles/ImportProfile.cs @@ -0,0 +1,26 @@ +using System.IO; +using DynamicData; +using Newtonsoft.Json; +using YMouseButtonControl.Core.Services.Profiles; +using YMouseButtonControl.Core.ViewModels.Models; + +namespace YMouseButtonControl.Core.ViewModels.ProfilesList.Commands.Profiles; + +public static class ImportProfile +{ + public sealed record Command(string Path); + + public sealed class Handler(IProfilesCache profilesCache, ProfileVmConverter profileVmConverter) + { + public void Execute(Command c) + { + var f = File.ReadAllText(c.Path); + var settings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto }; + settings.Converters.Add(profileVmConverter); + var deserializedProfile = + JsonConvert.DeserializeObject(f, settings) + ?? throw new JsonSerializationException("Error deserializing profile"); + profilesCache.ProfilesSc.AddOrUpdate(deserializedProfile); + } + } +} diff --git a/YMouseButtonControl.Core/ViewModels/ProfilesList/Commands/Profiles/ProfileVmConverter.cs b/YMouseButtonControl.Core/ViewModels/ProfilesList/Commands/Profiles/ProfileVmConverter.cs new file mode 100644 index 0000000..fa01e0b --- /dev/null +++ b/YMouseButtonControl.Core/ViewModels/ProfilesList/Commands/Profiles/ProfileVmConverter.cs @@ -0,0 +1,101 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using YMouseButtonControl.Core.Services.Profiles; +using YMouseButtonControl.Core.ViewModels.Models; + +namespace YMouseButtonControl.Core.ViewModels.ProfilesList.Commands.Profiles; + +public class ProfileVmConverter(IProfilesCache profilesCache) : JsonConverter +{ + public override void WriteJson(JsonWriter writer, ProfileVm? value, JsonSerializer serializer) + { + JObject obj = + new() + { + // Serialize the properties directly. + ["IsDefault"] = value!.IsDefault, + ["Checked"] = value.Checked, + ["Name"] = value.Name, + ["Description"] = value.Description, + ["WindowCaption"] = value.WindowCaption, + ["Process"] = value.Process, + ["WindowClass"] = value.WindowClass, + ["ParentClass"] = value.ParentClass, + ["MatchType"] = value.MatchType, + ["DisplayPriority"] = value.DisplayPriority, + }; + + if (value.ButtonMappings != null) + { + obj["ButtonMappings"] = JToken.FromObject(value.ButtonMappings, serializer); + } + + obj.WriteTo(writer); + } + + public override ProfileVm ReadJson( + JsonReader reader, + Type objectType, + ProfileVm? existingValue, + bool hasExistingValue, + JsonSerializer serializer + ) + { + JObject obj = JObject.Load(reader); + + // Deserialize the properties directly. + bool isDefault = obj["IsDefault"]!.ToObject(serializer); + bool @checked = obj["Checked"]!.ToObject(serializer); + string name = obj["Name"]!.ToObject(serializer)!; + string description = obj["Description"]?.ToObject(serializer) ?? string.Empty; + string windowCaption = obj["WindowCaption"]?.ToObject(serializer) ?? "N/A"; + string process = obj["Process"]?.ToObject(serializer) ?? "N/A"; + string windowClass = obj["WindowClass"]?.ToObject(serializer) ?? "N/A"; + string parentClass = obj["ParentClass"]?.ToObject(serializer) ?? "N/A"; + string matchType = obj["MatchType"]?.ToObject(serializer) ?? "N/A"; + int displayPriority = obj["DisplayPriority"]!.ToObject(serializer); + + // Deserialize the button mappings. + List buttonMappings = []; + if (obj["ButtonMappings"] != null) + { + buttonMappings = obj["ButtonMappings"]!.ToObject>( + serializer + )!; + } + var maxBtnMappingIdInCache = profilesCache + .Profiles.SelectMany(x => x.ButtonMappings) + .Max(x => x.Id); + foreach (var bm in buttonMappings) + { + bm.Id = ++maxBtnMappingIdInCache; + } + + var idForProfile = profilesCache.Profiles.Max(x => x.Id) + 1; + foreach (var bm in buttonMappings) + { + bm.ProfileId = idForProfile; + } + + ProfileVm profileVm = + new(buttonMappings) + { + Id = idForProfile, + IsDefault = isDefault, + Checked = @checked, + Name = name!, + Description = description, + WindowCaption = windowCaption, + Process = process, + WindowClass = windowClass, + ParentClass = parentClass, + MatchType = matchType, + DisplayPriority = displayPriority, + }; + + return profileVm; + } +} diff --git a/YMouseButtonControl.Core/ViewModels/ProfilesList/Commands/Profiles/RemoveProfile.cs b/YMouseButtonControl.Core/ViewModels/ProfilesList/Commands/Profiles/RemoveProfile.cs new file mode 100644 index 0000000..b76e300 --- /dev/null +++ b/YMouseButtonControl.Core/ViewModels/ProfilesList/Commands/Profiles/RemoveProfile.cs @@ -0,0 +1,14 @@ +using YMouseButtonControl.Core.Services.Profiles; + +namespace YMouseButtonControl.Core.ViewModels.ProfilesList.Commands.Profiles; + +public static class RemoveProfile +{ + public sealed record Command(int Id); + + public sealed class Handler(IProfilesCache profilesService) + { + public void Execute(Command c) => + profilesService.ProfilesSc.Edit(inner => inner.RemoveKey(c.Id)); + } +} diff --git a/YMouseButtonControl.Core/ViewModels/ProfilesList/Commands/Profiles/SetCurrentProfile.cs b/YMouseButtonControl.Core/ViewModels/ProfilesList/Commands/Profiles/SetCurrentProfile.cs new file mode 100644 index 0000000..1243fbe --- /dev/null +++ b/YMouseButtonControl.Core/ViewModels/ProfilesList/Commands/Profiles/SetCurrentProfile.cs @@ -0,0 +1,29 @@ +using System.Linq; +using YMouseButtonControl.Core.Services.Profiles; +using YMouseButtonControl.Core.ViewModels.ProfilesList.Models; + +namespace YMouseButtonControl.Core.ViewModels.ProfilesList.Commands.Profiles; + +/// +/// Assign ProfilesService CurrentProfile to a new profile +/// +public static class SetCurrentProfile +{ + public sealed record Command(ProfilesListProfileModel? Profile); + + public sealed class Handler(IProfilesCache profilesService) + { + public void Execute(Command c) + { + if (c.Profile is null) + { + profilesService.CurrentProfile = null; + } + else + { + var profile = profilesService.Profiles.First(x => x.Id == c.Profile.Id); + profilesService.CurrentProfile = profile; + } + } + } +} diff --git a/YMouseButtonControl.Core/ViewModels/ProfilesList/Features/Add/AddProfile.cs b/YMouseButtonControl.Core/ViewModels/ProfilesList/Features/Add/AddProfile.cs deleted file mode 100644 index ae3ca17..0000000 --- a/YMouseButtonControl.Core/ViewModels/ProfilesList/Features/Add/AddProfile.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Linq; -using YMouseButtonControl.Core.Services.Profiles; -using YMouseButtonControl.Core.ViewModels.Models; - -namespace YMouseButtonControl.Core.ViewModels.ProfilesList.Features.Add; - -public interface IAddProfile -{ - void Add(ProfileVm profile); -} - -public class AddProfile(IProfilesService profilesService) : IAddProfile -{ - public void Add(ProfileVm profile) - { - profile.Id = GetNextProfileId(); - profile.DisplayPriority = GetNextProfileDisplayPriority(); - profilesService.AddOrUpdate(profile); - } - - private int GetNextProfileDisplayPriority() => - profilesService.Profiles.Max(x => x.DisplayPriority) + 1; - - private int GetNextProfileId() => profilesService.Profiles.Max(x => x.Id) + 1; -} diff --git a/YMouseButtonControl.Core/ViewModels/ProfilesList/Models/ProfilesListProfileModel.cs b/YMouseButtonControl.Core/ViewModels/ProfilesList/Models/ProfilesListProfileModel.cs new file mode 100644 index 0000000..9f8ecc7 --- /dev/null +++ b/YMouseButtonControl.Core/ViewModels/ProfilesList/Models/ProfilesListProfileModel.cs @@ -0,0 +1,154 @@ +using System; +using System.Linq; +using System.Reactive.Linq; +using ReactiveUI; +using YMouseButtonControl.Core.Services.Profiles; +using YMouseButtonControl.Core.ViewModels.Models; + +namespace YMouseButtonControl.Core.ViewModels.ProfilesList.Models; + +public sealed class ProfilesListProfileModel : ReactiveObject +{ + private int _id; + private string _description; + private bool _checked; + private readonly bool _isDefault; + private int _displayPriority; + private string? _process; + private string? _name; + + public ProfilesListProfileModel(ProfileVm profileVm, IProfilesCache profilesService) + { + _id = profileVm.Id; + _description = profileVm.Description; + _checked = profileVm.Checked; + _isDefault = profileVm.IsDefault; + _displayPriority = profileVm.DisplayPriority; + _process = profileVm.Process; + _name = profileVm.Name; + + this.WhenAnyValue(x => x.Id) + .ObserveOn(RxApp.MainThreadScheduler) + .Subscribe(id => + { + profilesService.ProfilesSc.Edit(inner => + { + var tgt = inner.Items.FirstOrDefault(x => x.Id == Id); + if (tgt is not null) + { + tgt.Id = id; + } + }); + }); + this.WhenAnyValue(x => x.Description) + .ObserveOn(RxApp.MainThreadScheduler) + .Subscribe(description => + { + profilesService.ProfilesSc.Edit(inner => + { + var tgt = inner.Items.FirstOrDefault(x => x.Id == Id); + if (tgt is not null) + { + tgt.Description = description; + } + }); + }); + this.WhenAnyValue(x => x.DisplayPriority) + .ObserveOn(RxApp.MainThreadScheduler) + .Subscribe(displayPriority => + { + profilesService.ProfilesSc.Edit(inner => + { + var tgt = inner.Items.FirstOrDefault(x => x.Id == Id); + if (tgt is not null) + { + tgt.DisplayPriority = displayPriority; + } + }); + }); + this.WhenAnyValue(x => x.Checked) + .ObserveOn(RxApp.MainThreadScheduler) + .Subscribe(@checked => + { + profilesService.ProfilesSc.Edit(inner => + { + var tgt = inner.Items.FirstOrDefault(x => x.Id == Id); + if (tgt is not null) + { + tgt.Checked = @checked; + } + }); + }); + this.WhenAnyValue(x => x.DisplayPriority) + .ObserveOn(RxApp.MainThreadScheduler) + .Subscribe(displayPriority => + { + profilesService.ProfilesSc.Edit(inner => + { + var tgt = inner.Items.FirstOrDefault(x => x.Id == Id); + if (tgt is not null) + { + tgt.DisplayPriority = displayPriority; + } + }); + }); + this.WhenAnyValue(x => x.Process) + .ObserveOn(RxApp.MainThreadScheduler) + .Subscribe(Process => + { + profilesService.ProfilesSc.Edit(inner => + { + var tgt = inner.Items.FirstOrDefault(x => x.Id == Id); + if (tgt is not null) + { + tgt.Process = Process!; + } + }); + }); + this.WhenAnyValue(x => x.Name) + .ObserveOn(RxApp.MainThreadScheduler) + .Subscribe(Name => + { + profilesService.ProfilesSc.Edit(inner => + { + var tgt = inner.Items.FirstOrDefault(x => x.Id == Id); + if (tgt is not null) + { + tgt.Name = Name!; + } + }); + }); + } + + public int Id + { + get => _id; + set => this.RaiseAndSetIfChanged(ref _id, value); + } + public string Description + { + get => _description; + set => this.RaiseAndSetIfChanged(ref _description, value); + } + public bool Checked + { + get => _checked; + set => this.RaiseAndSetIfChanged(ref _checked, value); + } + public bool IsDefault => _isDefault; + public int DisplayPriority + { + get => _displayPriority; + set => this.RaiseAndSetIfChanged(ref _displayPriority, value); + } + public string? Process + { + get => _process; + set => this.RaiseAndSetIfChanged(ref _process, value); + } + public string? Name + { + get => _name; + set => this.RaiseAndSetIfChanged(ref _name, value); + } +} diff --git a/YMouseButtonControl.Core/ViewModels/ProfilesList/ProcessSelectorDialogViewModel.cs b/YMouseButtonControl.Core/ViewModels/ProfilesList/ProcessSelectorDialogViewModel.cs deleted file mode 100644 index 61c30b6..0000000 --- a/YMouseButtonControl.Core/ViewModels/ProfilesList/ProcessSelectorDialogViewModel.cs +++ /dev/null @@ -1,124 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Reactive; -using System.Reactive.Linq; -using System.Windows.Input; -using DynamicData; -using DynamicData.Binding; -using ReactiveUI; -using YMouseButtonControl.Core.Services.Processes; -using YMouseButtonControl.Core.Services.Theme; -using YMouseButtonControl.Core.ViewModels.Models; -using YMouseButtonControl.DataAccess.Models; - -namespace YMouseButtonControl.Core.ViewModels.ProfilesList; - -public interface IProcessSelectorDialogViewModel -{ - ICommand RefreshButtonCommand { get; } -} - -public class ProcessSelectorDialogViewModel : DialogBase, IProcessSelectorDialogViewModel -{ - private readonly IProcessMonitorService _processMonitorService; - private readonly SourceList _sourceProcessModels; - private string? _processFilter; - private readonly ReadOnlyObservableCollection _filtered; - private ProcessModel? _processModel; - - public ProcessSelectorDialogViewModel( - IProcessMonitorService processMonitorService, - IThemeService themeService - ) - : base(themeService) - { - _sourceProcessModels = new SourceList(); - _processMonitorService = processMonitorService; - var dynamicFilter = this.WhenValueChanged(x => x.ProcessFilter) - .Select(CreateProcessFilterPredicate); - var filteredDisposable = _sourceProcessModels - .Connect() - .Filter(dynamicFilter) - .Bind(out _filtered) - .Subscribe(); - RefreshProcessList(); - RefreshButtonCommand = ReactiveCommand.Create(RefreshProcessList); - var canExecuteOkCommand = this.WhenAnyValue( - x => x.SelectedProcessModel, - selector: model => model is not null - ); - OkCommand = ReactiveCommand.Create( - () => - { - var mb1 = new NothingMappingVm { MouseButton = MouseButton.Mb1 }; - var mb2 = new NothingMappingVm { MouseButton = MouseButton.Mb2 }; - var mb3 = new NothingMappingVm { MouseButton = MouseButton.Mb3 }; - var mb4 = new NothingMappingVm { MouseButton = MouseButton.Mb4 }; - var mb5 = new NothingMappingVm { MouseButton = MouseButton.Mb5 }; - var mwu = new NothingMappingVm { MouseButton = MouseButton.Mwu }; - var mwd = new NothingMappingVm { MouseButton = MouseButton.Mwd }; - var mwl = new NothingMappingVm { MouseButton = MouseButton.Mwl }; - var mwr = new NothingMappingVm { MouseButton = MouseButton.Mwr }; - - var buttonMappings = new List() - { - mb1, - mb2, - mb3, - mb4, - mb5, - mwu, - mwd, - mwl, - mwr, - }; - - return new ProfileVm(buttonMappings) - { - Name = SelectedProcessModel!.Process.MainModule!.ModuleName, - Description = SelectedProcessModel.Process.MainWindowTitle, - Process = SelectedProcessModel.Process.MainModule.ModuleName, - WindowCaption = "N/A", - WindowClass = "N/A", - ParentClass = "N/A", - MatchType = "N/A", - }; - }, - canExecuteOkCommand - ); - } - - 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 ICommand RefreshButtonCommand { get; } - - public ReactiveCommand OkCommand { get; } - - public ReadOnlyObservableCollection Filtered => _filtered; - - public ProcessModel? SelectedProcessModel - { - get => _processModel; - set => this.RaiseAndSetIfChanged(ref _processModel, value); - } - - private void RefreshProcessList() => - _sourceProcessModels.EditDiff(_processMonitorService.GetProcesses()); -} diff --git a/YMouseButtonControl.Core/ViewModels/ProfilesList/ProfilesListHandlerRegistrations.cs b/YMouseButtonControl.Core/ViewModels/ProfilesList/ProfilesListHandlerRegistrations.cs new file mode 100644 index 0000000..0bd77a7 --- /dev/null +++ b/YMouseButtonControl.Core/ViewModels/ProfilesList/ProfilesListHandlerRegistrations.cs @@ -0,0 +1,21 @@ +using Microsoft.Extensions.DependencyInjection; +using YMouseButtonControl.Core.ViewModels.ProfilesList.Commands.Profiles; +using YMouseButtonControl.Core.ViewModels.ProfilesList.Queries.Profiles; + +namespace YMouseButtonControl.Core.ViewModels.ProfilesList; + +public static class ProfilesListHandlerRegistrations +{ + public static void RegisterCommon(IServiceCollection services) + { + services + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped(); + } +} diff --git a/YMouseButtonControl.Core/ViewModels/ProfilesList/ProfilesListViewModel.cs b/YMouseButtonControl.Core/ViewModels/ProfilesList/ProfilesListViewModel.cs index feffdfc..fd404f4 100644 --- a/YMouseButtonControl.Core/ViewModels/ProfilesList/ProfilesListViewModel.cs +++ b/YMouseButtonControl.Core/ViewModels/ProfilesList/ProfilesListViewModel.cs @@ -1,14 +1,15 @@ using System; -using System.Diagnostics; +using System.Collections.ObjectModel; using System.Linq; using System.Reactive; using System.Reactive.Linq; -using System.Threading.Tasks; using System.Windows.Input; using ReactiveUI; -using YMouseButtonControl.Core.Services.Profiles; +using YMouseButtonControl.Core.ViewModels.Dialogs.ProcessSelectorDialog; using YMouseButtonControl.Core.ViewModels.Models; -using YMouseButtonControl.Core.ViewModels.ProfilesList.Features.Add; +using YMouseButtonControl.Core.ViewModels.ProfilesList.Commands.Profiles; +using YMouseButtonControl.Core.ViewModels.ProfilesList.Models; +using YMouseButtonControl.Core.ViewModels.ProfilesList.Queries.Profiles; namespace YMouseButtonControl.Core.ViewModels.ProfilesList; @@ -16,10 +17,7 @@ public interface IProfilesListViewModel; public class ProfilesListViewModel : ViewModelBase, IProfilesListViewModel { - private IProfilesService _profilesService; - private readonly IProcessSelectorDialogViewModel _processSelectorDialogViewModel; - private readonly IAddProfile _addProfile; - private int _selectedIndex; + private ProfilesListProfileModel? _currentProfile; public ICommand AddButtonCommand { get; } public ReactiveCommand EditButtonCommand { get; } @@ -35,209 +33,209 @@ public Interaction< > ShowProcessSelectorInteraction { get; } public ProfilesListViewModel( - IProfilesService profilesService, - IProcessSelectorDialogViewModel processSelectorDialogViewModel, - IAddProfile addProfile + ListProfiles.Handler listCacheProfilesHandler, + GetCurrentProfile.Handler getCurrentProfileHandler, + SetCurrentProfile.Handler setCurrentProfileHandler, + RemoveProfile.Handler removeProfileHandler, + IProcessSelectorDialogVmFactory processSelectorDialogVmFactory, + AddProfile.Handler addProfileHandler, + ExportProfile.Handler exportProfileHandler, + CopyProfile.Handler copyProfileHandler, + ImportProfile.Handler importProfileHandler ) { - _profilesService = profilesService; - AddButtonCommand = ReactiveCommand.CreateFromTask(ShowProcessPickerDialogAsync); + CurrentProfile = getCurrentProfileHandler.Execute(); + Profiles = listCacheProfilesHandler.Execute(); + this.WhenAnyValue(x => x.CurrentProfile) + .WhereNotNull() + .Select(cur => new SetCurrentProfile.Command(cur)) + .Subscribe(setCurrentProfileHandler.Execute); + ShowProcessSelectorInteraction = + new Interaction(); + AddButtonCommand = ReactiveCommand.CreateFromTask(async () => + { + var result = await ShowProcessSelectorInteraction.Handle( + processSelectorDialogVmFactory.Create() + ); + if (result is null) + { + return; + } + addProfileHandler.Execute(new AddProfile.Command(result)); + }); ShowExportFileDialog = new Interaction(); ShowImportFileDialog = new Interaction(); - ImportCommand = ReactiveCommand.CreateFromTask(OnImportClickedAsync); + ImportCommand = ReactiveCommand.CreateFromTask(async () => + { + var result = await ShowImportFileDialog.Handle(Unit.Default); + if (string.IsNullOrWhiteSpace(result)) + { + return; + } + + importProfileHandler.Execute(new ImportProfile.Command(result)); + }); var exportCanExecute = this.WhenAnyValue( - x => x.ProfilesService.CurrentProfile, + x => x.CurrentProfile, curProf => !curProf?.IsDefault ?? false ); - ExportCommand = ReactiveCommand.CreateFromTask(OnExportClickedAsync, exportCanExecute); + ExportCommand = ReactiveCommand.CreateFromTask( + async () => + { + var result = await ShowExportFileDialog.Handle(CurrentProfile!.Name!); + if (string.IsNullOrWhiteSpace(result)) + { + return; + } + exportProfileHandler.Execute(new ExportProfile.Command(CurrentProfile.Id, result)); + }, + exportCanExecute + ); + var removeCanExecute = this.WhenAnyValue( - x => x.ProfilesService.CurrentProfile, + x => x.CurrentProfile, curProf => !curProf?.IsDefault ?? false ); - RemoveButtonCommand = ReactiveCommand.Create(OnRemoveButtonClicked, removeCanExecute); + RemoveButtonCommand = ReactiveCommand.Create( + () => + { + if (CurrentProfile is not null) + { + var curProfileDisplayPriority = CurrentProfile.DisplayPriority; + removeProfileHandler.Execute(new RemoveProfile.Command(CurrentProfile.Id)); + CurrentProfile = Profiles + .Where(x => x.DisplayPriority < curProfileDisplayPriority) + .MinBy(x => x.DisplayPriority); + } + }, + removeCanExecute + ); + var upCommandCanExecute = this.WhenAnyValue( - x => x.ProfilesService.CurrentProfile, - x => x.ProfilesService.CurrentProfile!.DisplayPriority, - selector: (curProf, _) => + x => x.CurrentProfile, + x => x.CurrentProfile!.DisplayPriority, + selector: (curProf, curProfDisplayPriority) => curProf is not null && !curProf.IsDefault - && _profilesService.Profiles.Any(p => - !p.IsDefault && p.DisplayPriority < curProf.DisplayPriority - ) + && Profiles.Any(p => !p.IsDefault && p.DisplayPriority < curProfDisplayPriority) + ); + UpCommand = ReactiveCommand.Create( + () => + { + var profileOfNextSmallerDisplayPriority = + Profiles + .Where(x => x.DisplayPriority < CurrentProfile!.DisplayPriority) + .MaxBy(x => x.DisplayPriority) + ?? throw new Exception("Unable to find profile to move down the profiles list"); + if (profileOfNextSmallerDisplayPriority.IsDefault) + { + throw new InvalidOperationException( + "You cannot move the Default profile in the profiles list" + ); + } + + // swap display priorities + ( + profileOfNextSmallerDisplayPriority.DisplayPriority, + CurrentProfile!.DisplayPriority + ) = ( + CurrentProfile.DisplayPriority, + profileOfNextSmallerDisplayPriority.DisplayPriority + ); + }, + upCommandCanExecute ); - UpCommand = ReactiveCommand.Create(UpButtonClicked, upCommandCanExecute); var downCommandCanExecute = this.WhenAnyValue( - x => x.ProfilesService.CurrentProfile, - x => x.ProfilesService.CurrentProfile!.DisplayPriority, - selector: (curProf, _) => + x => x.CurrentProfile, + x => x.CurrentProfile!.DisplayPriority, + selector: (curProf, curProfDisplayPriority) => curProf is not null && !curProf.IsDefault - && _profilesService.Profiles.Any(p => p.DisplayPriority > curProf.DisplayPriority) - ); - DownCommand = ReactiveCommand.Create(DownButtonClicked, downCommandCanExecute); - _processSelectorDialogViewModel = processSelectorDialogViewModel; - _addProfile = addProfile; - ShowProcessSelectorInteraction = - new Interaction(); - var editCanExecute = this.WhenAnyValue( - x => x.ProfilesService.CurrentProfile, - curProf => !curProf?.IsDefault ?? false + && Profiles.Any(p => p.DisplayPriority > curProfDisplayPriority) ); - EditButtonCommand = ReactiveCommand.CreateFromTask(EditButtonClickedAsync, editCanExecute); - CopyCommand = ReactiveCommand.CreateFromTask(OnCopyClickedAsync); - - SelectedIndex = _profilesService.Profiles.First(x => x.IsDefault).DisplayPriority; - this.WhenAnyValue(x => x.SelectedIndex) - .Subscribe(x => + DownCommand = ReactiveCommand.Create( + () => { - if (_profilesService.CurrentProfile is null) - { - _profilesService.CurrentProfile = _profilesService.Profiles[x]; - } - else + var profileOfNextLargerDisplayPriority = + Profiles + .Where(x => x.DisplayPriority > CurrentProfile!.DisplayPriority) + .MaxBy(x => x.DisplayPriority) + ?? throw new Exception("Unable to find profile to move up the profiles list"); + if (profileOfNextLargerDisplayPriority.IsDefault) { - if (SelectedIndex < 0) - { - SelectedIndex = _profilesService.CurrentProfile!.DisplayPriority; - } - else - { - if (_profilesService.Profiles.Count <= x) - { - SelectedIndex = _profilesService - .Profiles.First(y => y.IsDefault) - .DisplayPriority; - } - else - { - _profilesService.CurrentProfile = _profilesService.Profiles[x]; - } - } + throw new InvalidOperationException( + "You cannot move the Default profile in the profiles list" + ); } - }); - } - - public int SelectedIndex - { - get => _selectedIndex; - set => this.RaiseAndSetIfChanged(ref _selectedIndex, value); - } - public Interaction ShowExportFileDialog { get; } - public Interaction ShowImportFileDialog { get; } - - public IProfilesService ProfilesService - { - get => _profilesService; - set => _profilesService = value; - } - - private async Task OnImportClickedAsync() - { - var result = await ShowImportFileDialog.Handle(Unit.Default); - if (string.IsNullOrWhiteSpace(result)) - { - return; - } - - _profilesService.ImportProfileFromPath(result); - } - - private async Task OnExportClickedAsync() - { - Debug.Assert( - _profilesService.CurrentProfile != null, - "_profilesService.CurrentProfile != null" - ); - var result = await ShowExportFileDialog.Handle(_profilesService.CurrentProfile.Name); - if (string.IsNullOrWhiteSpace(result)) - { - return; - } - - _profilesService.WriteProfileToFile(_profilesService.CurrentProfile, result); - } - - private async Task OnCopyClickedAsync() - { - var newProfile = await ShowProcessSelectorInteraction.Handle( - _processSelectorDialogViewModel - ); - if (newProfile is null) - { - return; - } - - Debug.Assert( - _profilesService.CurrentProfile != null, - "_profilesService.CurrentProfile != null" + // swap display priorities + ( + profileOfNextLargerDisplayPriority.DisplayPriority, + CurrentProfile!.DisplayPriority + ) = ( + CurrentProfile.DisplayPriority, + profileOfNextLargerDisplayPriority.DisplayPriority + ); + }, + downCommandCanExecute ); - var copiedProfile = _profilesService.CopyProfile(_profilesService.CurrentProfile); - copiedProfile.Name = newProfile.Name; - copiedProfile.Description = newProfile.Description; - copiedProfile.Checked = newProfile.Checked; - copiedProfile.Process = newProfile.Process; - copiedProfile.MatchType = newProfile.MatchType; - copiedProfile.ParentClass = newProfile.ParentClass; - copiedProfile.WindowCaption = newProfile.WindowCaption; - copiedProfile.WindowClass = newProfile.WindowClass; - _profilesService.AddProfile(copiedProfile); - } - private void OnRemoveButtonClicked() - { - Debug.Assert( - _profilesService.CurrentProfile != null, - "_profilesService.CurrentProfile != null" + var editCanExecute = this.WhenAnyValue( + x => x.CurrentProfile, + curProf => !curProf?.IsDefault ?? false ); - _profilesService.RemoveProfile(_profilesService.CurrentProfile); - } + EditButtonCommand = ReactiveCommand.CreateFromTask( + async () => + { + var result = await ShowProcessSelectorInteraction.Handle( + processSelectorDialogVmFactory.Create(CurrentProfile?.Process) + ); + if (result is null) + { + return; + } - private void UpButtonClicked() - { - Debug.Assert( - _profilesService.CurrentProfile != null, - "_profilesService.CurrentProfile != null" + CurrentProfile!.Process = result.Process; + CurrentProfile.Name = result.Name; + CurrentProfile.Description = result.Description; + }, + editCanExecute ); - _profilesService.MoveProfileUp(_profilesService.CurrentProfile); + CopyCommand = ReactiveCommand.CreateFromTask(async () => + { + var newProfile = await ShowProcessSelectorInteraction.Handle( + processSelectorDialogVmFactory.Create() + ); + if (newProfile is null) + { + return; + } + + var copiedProfile = copyProfileHandler.Execute( + new CopyProfile.Command( + CurrentProfile!.Id, + newProfile.Name, + newProfile.Description, + newProfile.Checked, + newProfile.Process, + newProfile.MatchType, + newProfile.ParentClass, + newProfile.WindowClass + ) + ); + setCurrentProfileHandler.Execute(new SetCurrentProfile.Command(copiedProfile)); + CurrentProfile = getCurrentProfileHandler.Execute(); + }); } - private void DownButtonClicked() - { - Debug.Assert( - _profilesService.CurrentProfile != null, - "_profilesService.CurrentProfile != null" - ); - _profilesService.MoveProfileDown(_profilesService.CurrentProfile); - // SelectedIndex++; - } + public Interaction ShowExportFileDialog { get; } + public Interaction ShowImportFileDialog { get; } - private async Task EditButtonClickedAsync() + public ProfilesListProfileModel? CurrentProfile { - var result = await ShowProcessSelectorInteraction.Handle(_processSelectorDialogViewModel); - if (result is null) - { - return; - } - - Debug.Assert( - _profilesService.CurrentProfile != null, - "_profilesService.CurrentProfile != null" - ); - var newProfile = _profilesService.CopyProfile(_profilesService.CurrentProfile); - newProfile.Process = result.Process; - newProfile.Name = result.Name; - newProfile.Description = result.Description; - _profilesService.ReplaceProfile(_profilesService.CurrentProfile, newProfile); + get => _currentProfile; + set => this.RaiseAndSetIfChanged(ref _currentProfile, value); } - private async Task ShowProcessPickerDialogAsync() - { - var result = await ShowProcessSelectorInteraction.Handle(_processSelectorDialogViewModel); - if (result is null) - { - return; - } - _addProfile.Add(result); - } + public ReadOnlyObservableCollection Profiles { get; } } diff --git a/YMouseButtonControl.Core/ViewModels/ProfilesList/Queries/Profiles/GetCurrentProfile.cs b/YMouseButtonControl.Core/ViewModels/ProfilesList/Queries/Profiles/GetCurrentProfile.cs new file mode 100644 index 0000000..8224d56 --- /dev/null +++ b/YMouseButtonControl.Core/ViewModels/ProfilesList/Queries/Profiles/GetCurrentProfile.cs @@ -0,0 +1,17 @@ +using YMouseButtonControl.Core.Services.Profiles; +using YMouseButtonControl.Core.ViewModels.Models; +using YMouseButtonControl.Core.ViewModels.ProfilesList.Models; + +namespace YMouseButtonControl.Core.ViewModels.ProfilesList.Queries.Profiles; + +public static class GetCurrentProfile +{ + public sealed class Handler(IProfilesCache profilesService) + { + public ProfilesListProfileModel? Execute() + { + var vm = profilesService.CurrentProfile; + return vm is null ? null : new(vm, profilesService); + } + } +} diff --git a/YMouseButtonControl.Core/ViewModels/ProfilesList/Queries/Profiles/ListProfiles.cs b/YMouseButtonControl.Core/ViewModels/ProfilesList/Queries/Profiles/ListProfiles.cs new file mode 100644 index 0000000..31c588d --- /dev/null +++ b/YMouseButtonControl.Core/ViewModels/ProfilesList/Queries/Profiles/ListProfiles.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.ObjectModel; +using System.Reactive.Linq; +using DynamicData; +using DynamicData.Binding; +using ReactiveUI; +using YMouseButtonControl.Core.Services.Profiles; +using YMouseButtonControl.Core.ViewModels.ProfilesList.Models; + +namespace YMouseButtonControl.Core.ViewModels.ProfilesList.Queries.Profiles; + +public static class ListProfiles +{ + public sealed class Handler(IProfilesCache profilesService) : IDisposable + { + private IDisposable? _disposable; + + public ReadOnlyObservableCollection Execute() + { + _disposable = profilesService + .ProfilesSc.Connect() + .AutoRefresh() + .Transform(x => new ProfilesListProfileModel(x, profilesService)) + .ObserveOn(RxApp.MainThreadScheduler) + .SortAndBind( + out var profiles, + SortExpressionComparer.Ascending(x => + x.DisplayPriority + ) + ) + .DisposeMany() + .Subscribe(); + return profiles; + } + + public void Dispose() + { + _disposable?.Dispose(); + } + } +} diff --git a/YMouseButtonControl.Core/YMouseButtonControl.Core.csproj b/YMouseButtonControl.Core/YMouseButtonControl.Core.csproj index 77da661..8227576 100644 --- a/YMouseButtonControl.Core/YMouseButtonControl.Core.csproj +++ b/YMouseButtonControl.Core/YMouseButtonControl.Core.csproj @@ -1,4 +1,4 @@ - + net8.0 @@ -7,6 +7,7 @@ 0.1.0 0.1.0 0.1.0 + true @@ -18,22 +19,28 @@ - - - - - - - - + + - + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + - + diff --git a/YMouseButtonControl.DataAccess/Context/YMouseButtonControlDbContext.cs b/YMouseButtonControl.DataAccess/Context/YMouseButtonControlDbContext.cs index da048ff..6cb0438 100644 --- a/YMouseButtonControl.DataAccess/Context/YMouseButtonControlDbContext.cs +++ b/YMouseButtonControl.DataAccess/Context/YMouseButtonControlDbContext.cs @@ -1,117 +1,385 @@ using System.Data; -using Dapper; using Microsoft.Data.Sqlite; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Design; using Microsoft.Extensions.Configuration; +using YMouseButtonControl.Domain.Models; -namespace YMouseButtonControl.DataAccess.Context; +namespace YMouseButtonControl.Infrastructure.Context; -public class YMouseButtonControlDbContext(IConfigurationRoot? configuration) +public class YMouseButtonControlDbContext : DbContext { - public IConfigurationRoot? Configuration { get; } = configuration; + public DbSet Profiles { get; set; } = null!; + public DbSet Settings { get; set; } = null!; + public DbSet SettingBools { get; set; } = null!; + public DbSet SettingStrings { get; set; } = null!; + public DbSet SettingInts { get; set; } = null!; + public DbSet Themes { get; set; } = null!; + public DbSet ButtonMappings { get; set; } = null!; + public DbSet DisabledMappings { get; set; } = null!; + public DbSet NothingMappings { get; set; } = null!; + public DbSet SimulatedKeystrokeMappings { get; set; } = null!; + public DbSet RightClickMappings { get; set; } = null!; - public IDbConnection CreateConnection() + public YMouseButtonControlDbContext(DbContextOptions opts) + : base(opts) { - return new SqliteConnection( - Configuration?.GetConnectionString("YMouseButtonControlContext") - ); + Database.EnsureCreated(); } - public void Init() + protected override void OnModelCreating(ModelBuilder modelBuilder) { - using var conn = CreateConnection(); - InitProfiles(); - InitSettings(); - InitThemes(); - InitButtonMappings(); - return; + modelBuilder + .Entity() + .HasData( + new Profile + { + Id = 1, + IsDefault = true, + Checked = true, + DisplayPriority = 0, + Name = "Default", + Description = "Default description", + WindowCaption = "N/A", + Process = "*", + MatchType = "N/A", + ParentClass = "N/A", + WindowClass = "N/A", + } + ); + + modelBuilder + .Entity() + .HasData( + new NothingMapping + { + Id = 1, + MouseButton = MouseButton.Mb1, + ProfileId = 1, + Selected = true, + }, + new NothingMapping + { + Id = 5, + MouseButton = MouseButton.Mb2, + ProfileId = 1, + Selected = true, + }, + new NothingMapping + { + Id = 9, + MouseButton = MouseButton.Mb3, + ProfileId = 1, + Selected = true, + }, + new NothingMapping + { + Id = 13, + MouseButton = MouseButton.Mb4, + ProfileId = 1, + Selected = true, + }, + new NothingMapping + { + Id = 17, + MouseButton = MouseButton.Mb5, + ProfileId = 1, + Selected = true, + }, + new NothingMapping + { + Id = 21, + MouseButton = MouseButton.Mwu, + ProfileId = 1, + Selected = true, + }, + new NothingMapping + { + Id = 25, + MouseButton = MouseButton.Mwd, + ProfileId = 1, + Selected = true, + }, + new NothingMapping + { + Id = 29, + MouseButton = MouseButton.Mwl, + ProfileId = 1, + Selected = true, + }, + new NothingMapping + { + Id = 33, + MouseButton = MouseButton.Mwr, + ProfileId = 1, + Selected = true, + } + ); + + modelBuilder + .Entity() + .HasData( + new DisabledMapping + { + Id = 2, + MouseButton = MouseButton.Mb1, + Selected = false, + ProfileId = 1, + }, + new DisabledMapping + { + Id = 6, + MouseButton = MouseButton.Mb2, + Selected = false, + ProfileId = 1, + }, + new DisabledMapping + { + Id = 10, + MouseButton = MouseButton.Mb3, + Selected = false, + ProfileId = 1, + }, + new DisabledMapping + { + Id = 14, + MouseButton = MouseButton.Mb4, + Selected = false, + ProfileId = 1, + }, + new DisabledMapping + { + Id = 18, + MouseButton = MouseButton.Mb5, + Selected = false, + ProfileId = 1, + }, + new DisabledMapping + { + Id = 22, + MouseButton = MouseButton.Mwu, + Selected = false, + ProfileId = 1, + }, + new DisabledMapping + { + Id = 26, + MouseButton = MouseButton.Mwd, + Selected = false, + ProfileId = 1, + }, + new DisabledMapping + { + Id = 30, + MouseButton = MouseButton.Mwl, + Selected = false, + ProfileId = 1, + }, + new DisabledMapping + { + Id = 34, + MouseButton = MouseButton.Mwr, + Selected = false, + ProfileId = 1, + } + ); + + modelBuilder + .Entity() + .HasData( + new SimulatedKeystroke + { + Id = 3, + MouseButton = MouseButton.Mb1, + Selected = false, + ProfileId = 1, + }, + new SimulatedKeystroke + { + Id = 7, + MouseButton = MouseButton.Mb2, + Selected = false, + ProfileId = 1, + }, + new SimulatedKeystroke + { + Id = 11, + MouseButton = MouseButton.Mb3, + Selected = false, + ProfileId = 1, + }, + new SimulatedKeystroke + { + Id = 15, + MouseButton = MouseButton.Mb4, + Selected = false, + ProfileId = 1, + }, + new SimulatedKeystroke + { + Id = 19, + MouseButton = MouseButton.Mb5, + Selected = false, + ProfileId = 1, + }, + new SimulatedKeystroke + { + Id = 23, + MouseButton = MouseButton.Mwu, + Selected = false, + ProfileId = 1, + }, + new SimulatedKeystroke + { + Id = 27, + MouseButton = MouseButton.Mwd, + Selected = false, + ProfileId = 1, + }, + new SimulatedKeystroke + { + Id = 31, + MouseButton = MouseButton.Mwl, + Selected = false, + ProfileId = 1, + }, + new SimulatedKeystroke + { + Id = 35, + MouseButton = MouseButton.Mwr, + Selected = false, + ProfileId = 1, + } + ); + + modelBuilder + .Entity() + .HasData( + new RightClick + { + Id = 4, + MouseButton = MouseButton.Mb1, + Selected = false, + ProfileId = 1, + }, + new RightClick + { + Id = 8, + MouseButton = MouseButton.Mb2, + Selected = false, + ProfileId = 1, + }, + new RightClick + { + Id = 12, + MouseButton = MouseButton.Mb3, + Selected = false, + ProfileId = 1, + }, + new RightClick + { + Id = 16, + MouseButton = MouseButton.Mb4, + Selected = false, + ProfileId = 1, + }, + new RightClick + { + Id = 20, + MouseButton = MouseButton.Mb5, + Selected = false, + ProfileId = 1, + }, + new RightClick + { + Id = 24, + MouseButton = MouseButton.Mwu, + Selected = false, + ProfileId = 1, + }, + new RightClick + { + Id = 28, + MouseButton = MouseButton.Mwd, + Selected = false, + ProfileId = 1, + }, + new RightClick + { + Id = 32, + MouseButton = MouseButton.Mwl, + Selected = false, + ProfileId = 1, + }, + new RightClick + { + Id = 36, + MouseButton = MouseButton.Mwr, + Selected = false, + ProfileId = 1, + } + ); - void InitProfiles() - { - const string sql = """ - CREATE TABLE IF NOT EXISTS - Profiles ( - Id INTEGER PRIMARY KEY AUTOINCREMENT, - IsDefault BOOLEAN, - Checked BOOLEAN, - DisplayPriority INTEGER, - Name TEXT NOT NULL, - Description TEXT NOT NULL, - WindowCaption TEXT NOT NULL, - Process TEXT NOT NULL, - WindowClass TEXT NOT NULL, - ParentClass TEXT NOT NULL, - MatchType TEXT NOT NULL - ); - INSERT OR IGNORE INTO Profiles ( - Id, IsDefault, Checked, Name, Description, WindowCaption, Process, WindowClass, ParentClass, MatchType) - VALUES ('1', '1', '1', 'Default', 'Default description', 'N/A', '*', 'N/A', 'N/A', 'N/A'); - """; - conn.Execute(sql); - } + modelBuilder + .Entity() + .HasData( + new Theme + { + Id = 1, + Name = "Default", + Background = "SystemAltHighColor", + Highlight = "SystemAccentColor", + }, + new Theme + { + Id = 2, + Name = "Light", + Background = "White", + Highlight = "Yellow", + }, + new Theme + { + Id = 3, + Name = "Dark", + Background = "Black", + Highlight = "#3700b3", + } + ); - void InitSettings() - { - const string sql = """ - CREATE TABLE IF NOT EXISTS - Settings ( - Id INTEGER PRIMARY KEY AUTOINCREMENT, - Name TEXT NOT NULL, - BoolValue BOOLEAN, - StringValue TEXT, - IntValue INTEGER, - SettingType INTEGER NOT NULL - ); - INSERT OR IGNORE INTO Settings (Id, Name, BoolValue, SettingType) VALUES ('1', 'StartMinimized', 0, '1'); - INSERT OR IGNORE INTO Settings (Id, Name, IntValue, SettingType) VALUES ('2', 'Theme', '1', '3'); - """; - conn.Execute(sql); - } + modelBuilder + .Entity() + .HasData( + new SettingBool + { + Id = 1, + Name = "StartMinimized", + BoolValue = false, + } + ); + modelBuilder + .Entity() + .HasData( + new SettingInt + { + Id = 2, + Name = "Theme", + IntValue = 3, + } + ); + } +} - void InitThemes() - { - const string sql = """ - CREATE TABLE IF NOT EXISTS - Themes ( - Id INTEGER PRIMARY KEY AUTOINCREMENT, - Name TEXT NOT NULL, - Background TEXT NOT NULL, - Highlight TEXT NOT NULL - ); - INSERT OR IGNORE INTO Themes (Id, Name, Background, Highlight) VALUES ('1', 'Default', 'SystemAltHighColor','SystemAccentColor'); - INSERT OR IGNORE INTO Themes (Id, Name, Background, Highlight) VALUES ('2', 'Light', 'White', 'Yellow'); - INSERT OR IGNORE INTO Themes (Id, Name, Background, Highlight) VALUES ('3', 'Dark', 'Black', '#3700b3'); - """; - conn.Execute(sql); - } +public class YMouseButtonControlDbContextFactory + : IDesignTimeDbContextFactory +{ + public YMouseButtonControlDbContext CreateDbContext(string[] args) + { + var optionsBuilder = new DbContextOptionsBuilder(); + optionsBuilder.UseSqlite( + "Data Source=YMouseButtonControl.db", + sqliteOptionsAction: so => so.MigrationsAssembly("YMouseButtonControl.Infrastructure") + ); - void InitButtonMappings() - { - const string sql = """ - CREATE TABLE IF NOT EXISTS - ButtonMappings ( - Id INTEGER PRIMARY KEY AUTOINCREMENT, - Keys TEXT, - MouseButton INTEGER NOT NULL, - ProfileId INTEGER NOT NULL, - SimulatedKeystrokeType INTEGER, - Selected BOOLEAN, - BlockOriginalMouseInput BOOLEAN, - AutoRepeatDelay INTEGER, - AutoRepeatRandomizeDelayEnabled BOOLEAN, - ButtonMappingType INTEGER NOT NULL, - FOREIGN KEY (ProfileId) REFERENCES Profiles (Id) ON DELETE CASCADE - ); - INSERT OR IGNORE INTO ButtonMappings (Id, MouseButton, ProfileId, ButtonMappingType) VALUES ('1', '0', '1', '1'); - INSERT OR IGNORE INTO ButtonMappings (Id, MouseButton, ProfileId, ButtonMappingType) VALUES ('2', '1', '1', '1'); - INSERT OR IGNORE INTO ButtonMappings (Id, MouseButton, ProfileId, ButtonMappingType) VALUES ('3', '2', '1', '1'); - INSERT OR IGNORE INTO ButtonMappings (Id, MouseButton, ProfileId, ButtonMappingType) VALUES ('4', '3', '1', '1'); - INSERT OR IGNORE INTO ButtonMappings (Id, MouseButton, ProfileId, ButtonMappingType) VALUES ('5', '4', '1', '1'); - INSERT OR IGNORE INTO ButtonMappings (Id, MouseButton, ProfileId, ButtonMappingType) VALUES ('6', '5', '1', '1'); - INSERT OR IGNORE INTO ButtonMappings (Id, MouseButton, ProfileId, ButtonMappingType) VALUES ('7', '6', '1', '1'); - INSERT OR IGNORE INTO ButtonMappings (Id, MouseButton, ProfileId, ButtonMappingType) VALUES ('8', '7', '1', '1'); - INSERT OR IGNORE INTO ButtonMappings (Id, MouseButton, ProfileId, ButtonMappingType) VALUES ('9', '8', '1', '1'); - """; - conn.Execute(sql); - } + return new YMouseButtonControlDbContext(optionsBuilder.Options); } } diff --git a/YMouseButtonControl.DataAccess/Migrations/20241221144135_Initial.Designer.cs b/YMouseButtonControl.DataAccess/Migrations/20241221144135_Initial.Designer.cs new file mode 100644 index 0000000..fef0dcd --- /dev/null +++ b/YMouseButtonControl.DataAccess/Migrations/20241221144135_Initial.Designer.cs @@ -0,0 +1,564 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using YMouseButtonControl.Infrastructure.Context; + +#nullable disable + +namespace YMouseButtonControl.Infrastructure.Migrations +{ + [DbContext(typeof(YMouseButtonControlDbContext))] + [Migration("20241221144135_Initial")] + partial class Initial + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "8.0.11"); + + modelBuilder.Entity("YMouseButtonControl.Domain.Models.ButtonMapping", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AutoRepeatDelay") + .HasColumnType("INTEGER"); + + b.Property("AutoRepeatRandomizeDelayEnabled") + .HasColumnType("INTEGER"); + + b.Property("BlockOriginalMouseInput") + .HasColumnType("INTEGER"); + + b.Property("Discriminator") + .IsRequired() + .HasMaxLength(21) + .HasColumnType("TEXT"); + + b.Property("Keys") + .HasColumnType("TEXT"); + + b.Property("MouseButton") + .HasColumnType("INTEGER"); + + b.Property("ProfileId") + .HasColumnType("INTEGER"); + + b.Property("Selected") + .HasColumnType("INTEGER"); + + b.Property("SimulatedKeystrokeType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ProfileId"); + + b.ToTable("ButtonMappings"); + + b.HasDiscriminator().HasValue("ButtonMapping"); + + b.UseTphMappingStrategy(); + }); + + modelBuilder.Entity("YMouseButtonControl.Domain.Models.Profile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Checked") + .HasColumnType("INTEGER"); + + b.Property("Description") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DisplayPriority") + .HasColumnType("INTEGER"); + + b.Property("IsDefault") + .HasColumnType("INTEGER"); + + b.Property("MatchType") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ParentClass") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Process") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("WindowCaption") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("WindowClass") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Profiles"); + + b.HasData( + new + { + Id = 1, + Checked = true, + Description = "Default description", + DisplayPriority = 0, + IsDefault = true, + MatchType = "N/A", + Name = "Default", + ParentClass = "N/A", + Process = "*", + WindowCaption = "N/A", + WindowClass = "N/A" + }); + }); + + modelBuilder.Entity("YMouseButtonControl.Domain.Models.Setting", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Discriminator") + .IsRequired() + .HasMaxLength(13) + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Settings"); + + b.HasDiscriminator().HasValue("Setting"); + + b.UseTphMappingStrategy(); + }); + + modelBuilder.Entity("YMouseButtonControl.Domain.Models.Theme", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Background") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Highlight") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Themes"); + + b.HasData( + new + { + Id = 1, + Background = "SystemAltHighColor", + Highlight = "SystemAccentColor", + Name = "Default" + }, + new + { + Id = 2, + Background = "White", + Highlight = "Yellow", + Name = "Light" + }, + new + { + Id = 3, + Background = "Black", + Highlight = "#3700b3", + Name = "Dark" + }); + }); + + modelBuilder.Entity("YMouseButtonControl.Domain.Models.DisabledMapping", b => + { + b.HasBaseType("YMouseButtonControl.Domain.Models.ButtonMapping"); + + b.HasDiscriminator().HasValue("DisabledMapping"); + + b.HasData( + new + { + Id = 2, + MouseButton = 0, + ProfileId = 1, + Selected = false + }, + new + { + Id = 6, + MouseButton = 1, + ProfileId = 1, + Selected = false + }, + new + { + Id = 10, + MouseButton = 2, + ProfileId = 1, + Selected = false + }, + new + { + Id = 14, + MouseButton = 3, + ProfileId = 1, + Selected = false + }, + new + { + Id = 18, + MouseButton = 4, + ProfileId = 1, + Selected = false + }, + new + { + Id = 22, + MouseButton = 5, + ProfileId = 1, + Selected = false + }, + new + { + Id = 26, + MouseButton = 6, + ProfileId = 1, + Selected = false + }, + new + { + Id = 30, + MouseButton = 7, + ProfileId = 1, + Selected = false + }, + new + { + Id = 34, + MouseButton = 8, + ProfileId = 1, + Selected = false + }); + }); + + modelBuilder.Entity("YMouseButtonControl.Domain.Models.NothingMapping", b => + { + b.HasBaseType("YMouseButtonControl.Domain.Models.ButtonMapping"); + + b.HasDiscriminator().HasValue("NothingMapping"); + + b.HasData( + new + { + Id = 1, + MouseButton = 0, + ProfileId = 1, + Selected = true + }, + new + { + Id = 5, + MouseButton = 1, + ProfileId = 1, + Selected = true + }, + new + { + Id = 9, + MouseButton = 2, + ProfileId = 1, + Selected = true + }, + new + { + Id = 13, + MouseButton = 3, + ProfileId = 1, + Selected = true + }, + new + { + Id = 17, + MouseButton = 4, + ProfileId = 1, + Selected = true + }, + new + { + Id = 21, + MouseButton = 5, + ProfileId = 1, + Selected = true + }, + new + { + Id = 25, + MouseButton = 6, + ProfileId = 1, + Selected = true + }, + new + { + Id = 29, + MouseButton = 7, + ProfileId = 1, + Selected = true + }, + new + { + Id = 33, + MouseButton = 8, + ProfileId = 1, + Selected = true + }); + }); + + modelBuilder.Entity("YMouseButtonControl.Domain.Models.RightClick", b => + { + b.HasBaseType("YMouseButtonControl.Domain.Models.ButtonMapping"); + + b.HasDiscriminator().HasValue("RightClick"); + + b.HasData( + new + { + Id = 4, + MouseButton = 0, + ProfileId = 1, + Selected = false + }, + new + { + Id = 8, + MouseButton = 1, + ProfileId = 1, + Selected = false + }, + new + { + Id = 12, + MouseButton = 2, + ProfileId = 1, + Selected = false + }, + new + { + Id = 16, + MouseButton = 3, + ProfileId = 1, + Selected = false + }, + new + { + Id = 20, + MouseButton = 4, + ProfileId = 1, + Selected = false + }, + new + { + Id = 24, + MouseButton = 5, + ProfileId = 1, + Selected = false + }, + new + { + Id = 28, + MouseButton = 6, + ProfileId = 1, + Selected = false + }, + new + { + Id = 32, + MouseButton = 7, + ProfileId = 1, + Selected = false + }, + new + { + Id = 36, + MouseButton = 8, + ProfileId = 1, + Selected = false + }); + }); + + modelBuilder.Entity("YMouseButtonControl.Domain.Models.SimulatedKeystroke", b => + { + b.HasBaseType("YMouseButtonControl.Domain.Models.ButtonMapping"); + + b.HasDiscriminator().HasValue("SimulatedKeystroke"); + + b.HasData( + new + { + Id = 3, + BlockOriginalMouseInput = true, + MouseButton = 0, + ProfileId = 1, + Selected = false + }, + new + { + Id = 7, + BlockOriginalMouseInput = true, + MouseButton = 1, + ProfileId = 1, + Selected = false + }, + new + { + Id = 11, + BlockOriginalMouseInput = true, + MouseButton = 2, + ProfileId = 1, + Selected = false + }, + new + { + Id = 15, + BlockOriginalMouseInput = true, + MouseButton = 3, + ProfileId = 1, + Selected = false + }, + new + { + Id = 19, + BlockOriginalMouseInput = true, + MouseButton = 4, + ProfileId = 1, + Selected = false + }, + new + { + Id = 23, + BlockOriginalMouseInput = true, + MouseButton = 5, + ProfileId = 1, + Selected = false + }, + new + { + Id = 27, + BlockOriginalMouseInput = true, + MouseButton = 6, + ProfileId = 1, + Selected = false + }, + new + { + Id = 31, + BlockOriginalMouseInput = true, + MouseButton = 7, + ProfileId = 1, + Selected = false + }, + new + { + Id = 35, + BlockOriginalMouseInput = true, + MouseButton = 8, + ProfileId = 1, + Selected = false + }); + }); + + modelBuilder.Entity("YMouseButtonControl.Domain.Models.SettingBool", b => + { + b.HasBaseType("YMouseButtonControl.Domain.Models.Setting"); + + b.Property("BoolValue") + .HasColumnType("INTEGER"); + + b.HasDiscriminator().HasValue("SettingBool"); + + b.HasData( + new + { + Id = 1, + Name = "StartMinimized", + BoolValue = false + }); + }); + + modelBuilder.Entity("YMouseButtonControl.Domain.Models.SettingInt", b => + { + b.HasBaseType("YMouseButtonControl.Domain.Models.Setting"); + + b.Property("IntValue") + .HasColumnType("INTEGER"); + + b.HasDiscriminator().HasValue("SettingInt"); + + b.HasData( + new + { + Id = 2, + Name = "Theme", + IntValue = 3 + }); + }); + + modelBuilder.Entity("YMouseButtonControl.Domain.Models.SettingString", b => + { + b.HasBaseType("YMouseButtonControl.Domain.Models.Setting"); + + b.Property("StringValue") + .HasColumnType("TEXT"); + + b.HasDiscriminator().HasValue("SettingString"); + }); + + modelBuilder.Entity("YMouseButtonControl.Domain.Models.ButtonMapping", b => + { + b.HasOne("YMouseButtonControl.Domain.Models.Profile", "Profile") + .WithMany("ButtonMappings") + .HasForeignKey("ProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Profile"); + }); + + modelBuilder.Entity("YMouseButtonControl.Domain.Models.Profile", b => + { + b.Navigation("ButtonMappings"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/YMouseButtonControl.DataAccess/Migrations/20241221144135_Initial.cs b/YMouseButtonControl.DataAccess/Migrations/20241221144135_Initial.cs new file mode 100644 index 0000000..d5bf062 --- /dev/null +++ b/YMouseButtonControl.DataAccess/Migrations/20241221144135_Initial.cs @@ -0,0 +1,186 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +#pragma warning disable CA1814 // Prefer jagged arrays over multidimensional + +namespace YMouseButtonControl.Infrastructure.Migrations +{ + /// + public partial class Initial : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Profiles", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + IsDefault = table.Column(type: "INTEGER", nullable: false), + Checked = table.Column(type: "INTEGER", nullable: false), + DisplayPriority = table.Column(type: "INTEGER", nullable: false), + Name = table.Column(type: "TEXT", nullable: false), + Description = table.Column(type: "TEXT", nullable: false), + WindowCaption = table.Column(type: "TEXT", nullable: false), + Process = table.Column(type: "TEXT", nullable: false), + WindowClass = table.Column(type: "TEXT", nullable: false), + ParentClass = table.Column(type: "TEXT", nullable: false), + MatchType = table.Column(type: "TEXT", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Profiles", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Settings", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Name = table.Column(type: "TEXT", nullable: false), + Discriminator = table.Column(type: "TEXT", maxLength: 13, nullable: false), + BoolValue = table.Column(type: "INTEGER", nullable: true), + IntValue = table.Column(type: "INTEGER", nullable: true), + StringValue = table.Column(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Settings", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Themes", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Name = table.Column(type: "TEXT", nullable: false), + Background = table.Column(type: "TEXT", nullable: false), + Highlight = table.Column(type: "TEXT", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Themes", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "ButtonMappings", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Keys = table.Column(type: "TEXT", nullable: true), + MouseButton = table.Column(type: "INTEGER", nullable: false), + ProfileId = table.Column(type: "INTEGER", nullable: false), + SimulatedKeystrokeType = table.Column(type: "INTEGER", nullable: true), + AutoRepeatDelay = table.Column(type: "INTEGER", nullable: true), + AutoRepeatRandomizeDelayEnabled = table.Column(type: "INTEGER", nullable: true), + Selected = table.Column(type: "INTEGER", nullable: false), + BlockOriginalMouseInput = table.Column(type: "INTEGER", nullable: true), + Discriminator = table.Column(type: "TEXT", maxLength: 21, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_ButtonMappings", x => x.Id); + table.ForeignKey( + name: "FK_ButtonMappings_Profiles_ProfileId", + column: x => x.ProfileId, + principalTable: "Profiles", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.InsertData( + table: "Profiles", + columns: new[] { "Id", "Checked", "Description", "DisplayPriority", "IsDefault", "MatchType", "Name", "ParentClass", "Process", "WindowCaption", "WindowClass" }, + values: new object[] { 1, true, "Default description", 0, true, "N/A", "Default", "N/A", "*", "N/A", "N/A" }); + + migrationBuilder.InsertData( + table: "Settings", + columns: new[] { "Id", "BoolValue", "Discriminator", "Name" }, + values: new object[] { 1, false, "SettingBool", "StartMinimized" }); + + migrationBuilder.InsertData( + table: "Settings", + columns: new[] { "Id", "Discriminator", "IntValue", "Name" }, + values: new object[] { 2, "SettingInt", 3, "Theme" }); + + migrationBuilder.InsertData( + table: "Themes", + columns: new[] { "Id", "Background", "Highlight", "Name" }, + values: new object[,] + { + { 1, "SystemAltHighColor", "SystemAccentColor", "Default" }, + { 2, "White", "Yellow", "Light" }, + { 3, "Black", "#3700b3", "Dark" } + }); + + migrationBuilder.InsertData( + table: "ButtonMappings", + columns: new[] { "Id", "AutoRepeatDelay", "AutoRepeatRandomizeDelayEnabled", "BlockOriginalMouseInput", "Discriminator", "Keys", "MouseButton", "ProfileId", "Selected", "SimulatedKeystrokeType" }, + values: new object[,] + { + { 1, null, null, null, "NothingMapping", null, 0, 1, true, null }, + { 2, null, null, null, "DisabledMapping", null, 0, 1, false, null }, + { 3, null, null, true, "SimulatedKeystroke", null, 0, 1, false, null }, + { 4, null, null, null, "RightClick", null, 0, 1, false, null }, + { 5, null, null, null, "NothingMapping", null, 1, 1, true, null }, + { 6, null, null, null, "DisabledMapping", null, 1, 1, false, null }, + { 7, null, null, true, "SimulatedKeystroke", null, 1, 1, false, null }, + { 8, null, null, null, "RightClick", null, 1, 1, false, null }, + { 9, null, null, null, "NothingMapping", null, 2, 1, true, null }, + { 10, null, null, null, "DisabledMapping", null, 2, 1, false, null }, + { 11, null, null, true, "SimulatedKeystroke", null, 2, 1, false, null }, + { 12, null, null, null, "RightClick", null, 2, 1, false, null }, + { 13, null, null, null, "NothingMapping", null, 3, 1, true, null }, + { 14, null, null, null, "DisabledMapping", null, 3, 1, false, null }, + { 15, null, null, true, "SimulatedKeystroke", null, 3, 1, false, null }, + { 16, null, null, null, "RightClick", null, 3, 1, false, null }, + { 17, null, null, null, "NothingMapping", null, 4, 1, true, null }, + { 18, null, null, null, "DisabledMapping", null, 4, 1, false, null }, + { 19, null, null, true, "SimulatedKeystroke", null, 4, 1, false, null }, + { 20, null, null, null, "RightClick", null, 4, 1, false, null }, + { 21, null, null, null, "NothingMapping", null, 5, 1, true, null }, + { 22, null, null, null, "DisabledMapping", null, 5, 1, false, null }, + { 23, null, null, true, "SimulatedKeystroke", null, 5, 1, false, null }, + { 24, null, null, null, "RightClick", null, 5, 1, false, null }, + { 25, null, null, null, "NothingMapping", null, 6, 1, true, null }, + { 26, null, null, null, "DisabledMapping", null, 6, 1, false, null }, + { 27, null, null, true, "SimulatedKeystroke", null, 6, 1, false, null }, + { 28, null, null, null, "RightClick", null, 6, 1, false, null }, + { 29, null, null, null, "NothingMapping", null, 7, 1, true, null }, + { 30, null, null, null, "DisabledMapping", null, 7, 1, false, null }, + { 31, null, null, true, "SimulatedKeystroke", null, 7, 1, false, null }, + { 32, null, null, null, "RightClick", null, 7, 1, false, null }, + { 33, null, null, null, "NothingMapping", null, 8, 1, true, null }, + { 34, null, null, null, "DisabledMapping", null, 8, 1, false, null }, + { 35, null, null, true, "SimulatedKeystroke", null, 8, 1, false, null }, + { 36, null, null, null, "RightClick", null, 8, 1, false, null } + }); + + migrationBuilder.CreateIndex( + name: "IX_ButtonMappings_ProfileId", + table: "ButtonMappings", + column: "ProfileId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "ButtonMappings"); + + migrationBuilder.DropTable( + name: "Settings"); + + migrationBuilder.DropTable( + name: "Themes"); + + migrationBuilder.DropTable( + name: "Profiles"); + } + } +} diff --git a/YMouseButtonControl.DataAccess/Migrations/YMouseButtonControlDbContextModelSnapshot.cs b/YMouseButtonControl.DataAccess/Migrations/YMouseButtonControlDbContextModelSnapshot.cs new file mode 100644 index 0000000..881cfc5 --- /dev/null +++ b/YMouseButtonControl.DataAccess/Migrations/YMouseButtonControlDbContextModelSnapshot.cs @@ -0,0 +1,561 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using YMouseButtonControl.Infrastructure.Context; + +#nullable disable + +namespace YMouseButtonControl.Infrastructure.Migrations +{ + [DbContext(typeof(YMouseButtonControlDbContext))] + partial class YMouseButtonControlDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "8.0.11"); + + modelBuilder.Entity("YMouseButtonControl.Domain.Models.ButtonMapping", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AutoRepeatDelay") + .HasColumnType("INTEGER"); + + b.Property("AutoRepeatRandomizeDelayEnabled") + .HasColumnType("INTEGER"); + + b.Property("BlockOriginalMouseInput") + .HasColumnType("INTEGER"); + + b.Property("Discriminator") + .IsRequired() + .HasMaxLength(21) + .HasColumnType("TEXT"); + + b.Property("Keys") + .HasColumnType("TEXT"); + + b.Property("MouseButton") + .HasColumnType("INTEGER"); + + b.Property("ProfileId") + .HasColumnType("INTEGER"); + + b.Property("Selected") + .HasColumnType("INTEGER"); + + b.Property("SimulatedKeystrokeType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ProfileId"); + + b.ToTable("ButtonMappings"); + + b.HasDiscriminator().HasValue("ButtonMapping"); + + b.UseTphMappingStrategy(); + }); + + modelBuilder.Entity("YMouseButtonControl.Domain.Models.Profile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Checked") + .HasColumnType("INTEGER"); + + b.Property("Description") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DisplayPriority") + .HasColumnType("INTEGER"); + + b.Property("IsDefault") + .HasColumnType("INTEGER"); + + b.Property("MatchType") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ParentClass") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Process") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("WindowCaption") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("WindowClass") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Profiles"); + + b.HasData( + new + { + Id = 1, + Checked = true, + Description = "Default description", + DisplayPriority = 0, + IsDefault = true, + MatchType = "N/A", + Name = "Default", + ParentClass = "N/A", + Process = "*", + WindowCaption = "N/A", + WindowClass = "N/A" + }); + }); + + modelBuilder.Entity("YMouseButtonControl.Domain.Models.Setting", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Discriminator") + .IsRequired() + .HasMaxLength(13) + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Settings"); + + b.HasDiscriminator().HasValue("Setting"); + + b.UseTphMappingStrategy(); + }); + + modelBuilder.Entity("YMouseButtonControl.Domain.Models.Theme", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Background") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Highlight") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Themes"); + + b.HasData( + new + { + Id = 1, + Background = "SystemAltHighColor", + Highlight = "SystemAccentColor", + Name = "Default" + }, + new + { + Id = 2, + Background = "White", + Highlight = "Yellow", + Name = "Light" + }, + new + { + Id = 3, + Background = "Black", + Highlight = "#3700b3", + Name = "Dark" + }); + }); + + modelBuilder.Entity("YMouseButtonControl.Domain.Models.DisabledMapping", b => + { + b.HasBaseType("YMouseButtonControl.Domain.Models.ButtonMapping"); + + b.HasDiscriminator().HasValue("DisabledMapping"); + + b.HasData( + new + { + Id = 2, + MouseButton = 0, + ProfileId = 1, + Selected = false + }, + new + { + Id = 6, + MouseButton = 1, + ProfileId = 1, + Selected = false + }, + new + { + Id = 10, + MouseButton = 2, + ProfileId = 1, + Selected = false + }, + new + { + Id = 14, + MouseButton = 3, + ProfileId = 1, + Selected = false + }, + new + { + Id = 18, + MouseButton = 4, + ProfileId = 1, + Selected = false + }, + new + { + Id = 22, + MouseButton = 5, + ProfileId = 1, + Selected = false + }, + new + { + Id = 26, + MouseButton = 6, + ProfileId = 1, + Selected = false + }, + new + { + Id = 30, + MouseButton = 7, + ProfileId = 1, + Selected = false + }, + new + { + Id = 34, + MouseButton = 8, + ProfileId = 1, + Selected = false + }); + }); + + modelBuilder.Entity("YMouseButtonControl.Domain.Models.NothingMapping", b => + { + b.HasBaseType("YMouseButtonControl.Domain.Models.ButtonMapping"); + + b.HasDiscriminator().HasValue("NothingMapping"); + + b.HasData( + new + { + Id = 1, + MouseButton = 0, + ProfileId = 1, + Selected = true + }, + new + { + Id = 5, + MouseButton = 1, + ProfileId = 1, + Selected = true + }, + new + { + Id = 9, + MouseButton = 2, + ProfileId = 1, + Selected = true + }, + new + { + Id = 13, + MouseButton = 3, + ProfileId = 1, + Selected = true + }, + new + { + Id = 17, + MouseButton = 4, + ProfileId = 1, + Selected = true + }, + new + { + Id = 21, + MouseButton = 5, + ProfileId = 1, + Selected = true + }, + new + { + Id = 25, + MouseButton = 6, + ProfileId = 1, + Selected = true + }, + new + { + Id = 29, + MouseButton = 7, + ProfileId = 1, + Selected = true + }, + new + { + Id = 33, + MouseButton = 8, + ProfileId = 1, + Selected = true + }); + }); + + modelBuilder.Entity("YMouseButtonControl.Domain.Models.RightClick", b => + { + b.HasBaseType("YMouseButtonControl.Domain.Models.ButtonMapping"); + + b.HasDiscriminator().HasValue("RightClick"); + + b.HasData( + new + { + Id = 4, + MouseButton = 0, + ProfileId = 1, + Selected = false + }, + new + { + Id = 8, + MouseButton = 1, + ProfileId = 1, + Selected = false + }, + new + { + Id = 12, + MouseButton = 2, + ProfileId = 1, + Selected = false + }, + new + { + Id = 16, + MouseButton = 3, + ProfileId = 1, + Selected = false + }, + new + { + Id = 20, + MouseButton = 4, + ProfileId = 1, + Selected = false + }, + new + { + Id = 24, + MouseButton = 5, + ProfileId = 1, + Selected = false + }, + new + { + Id = 28, + MouseButton = 6, + ProfileId = 1, + Selected = false + }, + new + { + Id = 32, + MouseButton = 7, + ProfileId = 1, + Selected = false + }, + new + { + Id = 36, + MouseButton = 8, + ProfileId = 1, + Selected = false + }); + }); + + modelBuilder.Entity("YMouseButtonControl.Domain.Models.SimulatedKeystroke", b => + { + b.HasBaseType("YMouseButtonControl.Domain.Models.ButtonMapping"); + + b.HasDiscriminator().HasValue("SimulatedKeystroke"); + + b.HasData( + new + { + Id = 3, + BlockOriginalMouseInput = true, + MouseButton = 0, + ProfileId = 1, + Selected = false + }, + new + { + Id = 7, + BlockOriginalMouseInput = true, + MouseButton = 1, + ProfileId = 1, + Selected = false + }, + new + { + Id = 11, + BlockOriginalMouseInput = true, + MouseButton = 2, + ProfileId = 1, + Selected = false + }, + new + { + Id = 15, + BlockOriginalMouseInput = true, + MouseButton = 3, + ProfileId = 1, + Selected = false + }, + new + { + Id = 19, + BlockOriginalMouseInput = true, + MouseButton = 4, + ProfileId = 1, + Selected = false + }, + new + { + Id = 23, + BlockOriginalMouseInput = true, + MouseButton = 5, + ProfileId = 1, + Selected = false + }, + new + { + Id = 27, + BlockOriginalMouseInput = true, + MouseButton = 6, + ProfileId = 1, + Selected = false + }, + new + { + Id = 31, + BlockOriginalMouseInput = true, + MouseButton = 7, + ProfileId = 1, + Selected = false + }, + new + { + Id = 35, + BlockOriginalMouseInput = true, + MouseButton = 8, + ProfileId = 1, + Selected = false + }); + }); + + modelBuilder.Entity("YMouseButtonControl.Domain.Models.SettingBool", b => + { + b.HasBaseType("YMouseButtonControl.Domain.Models.Setting"); + + b.Property("BoolValue") + .HasColumnType("INTEGER"); + + b.HasDiscriminator().HasValue("SettingBool"); + + b.HasData( + new + { + Id = 1, + Name = "StartMinimized", + BoolValue = false + }); + }); + + modelBuilder.Entity("YMouseButtonControl.Domain.Models.SettingInt", b => + { + b.HasBaseType("YMouseButtonControl.Domain.Models.Setting"); + + b.Property("IntValue") + .HasColumnType("INTEGER"); + + b.HasDiscriminator().HasValue("SettingInt"); + + b.HasData( + new + { + Id = 2, + Name = "Theme", + IntValue = 3 + }); + }); + + modelBuilder.Entity("YMouseButtonControl.Domain.Models.SettingString", b => + { + b.HasBaseType("YMouseButtonControl.Domain.Models.Setting"); + + b.Property("StringValue") + .HasColumnType("TEXT"); + + b.HasDiscriminator().HasValue("SettingString"); + }); + + modelBuilder.Entity("YMouseButtonControl.Domain.Models.ButtonMapping", b => + { + b.HasOne("YMouseButtonControl.Domain.Models.Profile", "Profile") + .WithMany("ButtonMappings") + .HasForeignKey("ProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Profile"); + }); + + modelBuilder.Entity("YMouseButtonControl.Domain.Models.Profile", b => + { + b.Navigation("ButtonMappings"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/YMouseButtonControl.DataAccess/Queries/BaseQueries.cs b/YMouseButtonControl.DataAccess/Queries/BaseQueries.cs deleted file mode 100644 index 37f6cfa..0000000 --- a/YMouseButtonControl.DataAccess/Queries/BaseQueries.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace YMouseButtonControl.DataAccess.Queries; - -public abstract class BaseQueries -{ - public abstract string Add(); - - public virtual string GetByName(string tblName) => - $""" - SELECT * FROM {tblName} - WHERE Name = @Name - LIMIT 1; - """; - - public virtual string GetAll(string tblName) => $"SELECT * FROM {tblName};"; - - public virtual string GetById(string tblName) => $"SELECT * FROM {tblName} WHERE Id = @Id;"; - - public abstract string Update(); - - public virtual string DeleteById(string tblName) => $"DELETE FROM {tblName} WHERE Id = @Id;"; -} diff --git a/YMouseButtonControl.DataAccess/Queries/ButtonMappingQueries.cs b/YMouseButtonControl.DataAccess/Queries/ButtonMappingQueries.cs deleted file mode 100644 index f7f56c6..0000000 --- a/YMouseButtonControl.DataAccess/Queries/ButtonMappingQueries.cs +++ /dev/null @@ -1,26 +0,0 @@ -namespace YMouseButtonControl.DataAccess.Queries; - -public class ButtonMappingQueries : BaseQueries -{ - public string GetByProfileId() => "SELECT * FROM ButtonMappings WHERE ProfileId = @Id;"; - - public override string Add() => - """ - INSERT INTO ButtonMappings - (Keys, MouseButton, ProfileId, SimulatedKeystrokeType, Selected, BlockOriginalMouseInput, ButtonMappingType, AutoRepeatDelay, AutoRepeatRandomizeDelayEnabled) - VALUES (@Keys, @MouseButton, @ProfileId, @SimulatedKeystrokeType, @Selected, @BlockOriginalMouseInput, @ButtonMappingType, @AutoRepeatDelay, @AutoRepeatRandomizeDelayEnabled); - """; - - public override string Update() => - """ - UPDATE ButtonMappings - SET Keys = @Keys, - MouseButton = @MouseButton, - ProfileId = @ProfileId, - SimulatedKeystrokeType = @SimulatedKeystrokeType, - Selected = @Selected, - AutoRepeatDelay = @AutoRepeatDelay, - AutoRepeatRandomizeDelayEnabled = @AutoRepeatRandomizeDelayEnabled, - BlockOriginalMouseInput = @BlockOriginalMouseInput; - """; -} diff --git a/YMouseButtonControl.DataAccess/Queries/ProfileQueries.cs b/YMouseButtonControl.DataAccess/Queries/ProfileQueries.cs deleted file mode 100644 index 2f5fe9c..0000000 --- a/YMouseButtonControl.DataAccess/Queries/ProfileQueries.cs +++ /dev/null @@ -1,27 +0,0 @@ -namespace YMouseButtonControl.DataAccess.Queries; - -public class ProfileQueries : BaseQueries -{ - public override string Add() => - """ - INSERT INTO Profiles - (Id, IsDefault, Checked, DisplayPriority, Name, Description, WindowCaption, Process, WindowClass, ParentClass, MatchType) - VALUES (@Id, @IsDefault, @Checked, @DisplayPriority, @Name, @Description, @WindowCaption, @Process, @WindowClass, @ParentClass, @MatchType); - SELECT SEQ from sqlite_sequence WHERE name='Profiles'; - """; - - public override string Update() => - """ - UPDATE Profiles - SET IsDefault = @IsDefault, - Checked = @Checked, - DisplayPriority = @DisplayPriority, - Name = @Name, - Description = @Description, - WindowCaption = @WindowCaption, - Process = @Process, - WindowClass = @WindowClass, - ParentClass = @ParentClass, - MatchType = @MatchType; - """; -} diff --git a/YMouseButtonControl.DataAccess/Queries/SettingQueries.cs b/YMouseButtonControl.DataAccess/Queries/SettingQueries.cs deleted file mode 100644 index 02e6532..0000000 --- a/YMouseButtonControl.DataAccess/Queries/SettingQueries.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace YMouseButtonControl.DataAccess.Queries; - -public class SettingQueries : BaseQueries -{ - public override string Add() => - """ - INSERT INTO Settings (Id, Name, BoolValue, StringValue, IntValue) - VALUES (@Id, @Name, @BoolValue, @StringValue, @IntValue); - """; - - public override string Update() => - """ - UPDATE Settings - SET Name = @Name, - BoolValue = @BoolValue, - StringValue = @StringValue, - IntValue = @IntValue - WHERE Id = @Id; - """; -} diff --git a/YMouseButtonControl.DataAccess/Queries/ThemeQueries.cs b/YMouseButtonControl.DataAccess/Queries/ThemeQueries.cs deleted file mode 100644 index 45e9cff..0000000 --- a/YMouseButtonControl.DataAccess/Queries/ThemeQueries.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace YMouseButtonControl.DataAccess.Queries; - -public class ThemeQueries : BaseQueries -{ - public override string Add() => - """ - INSERT INTO Themes (Id, Name, Background, Highlight) - VALUES (@Id, @Name, @Background, @Highlight); - """; - - public override string Update() => - """ - UPDATE Themes - SET Name = @Name, - Background = @Background, - Highlight = @Highlight - Where Id = @Id, - """; -} diff --git a/YMouseButtonControl.DataAccess/YMouseButtonControl.DataAccess.csproj b/YMouseButtonControl.DataAccess/YMouseButtonControl.Infrastructure.csproj similarity index 65% rename from YMouseButtonControl.DataAccess/YMouseButtonControl.DataAccess.csproj rename to YMouseButtonControl.DataAccess/YMouseButtonControl.Infrastructure.csproj index bc46183..c4ca276 100644 --- a/YMouseButtonControl.DataAccess/YMouseButtonControl.DataAccess.csproj +++ b/YMouseButtonControl.DataAccess/YMouseButtonControl.Infrastructure.csproj @@ -7,9 +7,13 @@ - + + + + + diff --git a/YMouseButtonControl.DataAccess/Models/ButtonMapping.cs b/YMouseButtonControl.Domain/Models/ButtonMapping.cs similarity index 80% rename from YMouseButtonControl.DataAccess/Models/ButtonMapping.cs rename to YMouseButtonControl.Domain/Models/ButtonMapping.cs index 16cefea..d4eb995 100644 --- a/YMouseButtonControl.DataAccess/Models/ButtonMapping.cs +++ b/YMouseButtonControl.Domain/Models/ButtonMapping.cs @@ -1,4 +1,4 @@ -namespace YMouseButtonControl.DataAccess.Models; +namespace YMouseButtonControl.Domain.Models; public enum MouseButton { @@ -25,7 +25,6 @@ public abstract class ButtonMapping public bool? AutoRepeatRandomizeDelayEnabled { get; set; } public bool Selected { get; set; } public bool? BlockOriginalMouseInput { get; set; } - public ButtonMappingType ButtonMappingType { get; set; } } public class DisabledMapping : ButtonMapping; @@ -41,11 +40,3 @@ public SimulatedKeystroke() } public class RightClick : ButtonMapping; - -public enum ButtonMappingType -{ - Disabled, - Nothing, - SimulatedKeystroke, - RightClick, -} diff --git a/YMouseButtonControl.DataAccess/Models/Profile.cs b/YMouseButtonControl.Domain/Models/Profile.cs similarity index 80% rename from YMouseButtonControl.DataAccess/Models/Profile.cs rename to YMouseButtonControl.Domain/Models/Profile.cs index 43e06c8..aa465ea 100644 --- a/YMouseButtonControl.DataAccess/Models/Profile.cs +++ b/YMouseButtonControl.Domain/Models/Profile.cs @@ -1,4 +1,4 @@ -namespace YMouseButtonControl.DataAccess.Models; +namespace YMouseButtonControl.Domain.Models; public class Profile { @@ -14,5 +14,5 @@ public class Profile public required string ParentClass { get; set; } public required string MatchType { get; set; } - public virtual ICollection? ButtonMappings { get; set; } + public virtual ICollection ButtonMappings { get; set; } = []; } diff --git a/YMouseButtonControl.DataAccess/Models/Setting.cs b/YMouseButtonControl.Domain/Models/Setting.cs similarity index 89% rename from YMouseButtonControl.DataAccess/Models/Setting.cs rename to YMouseButtonControl.Domain/Models/Setting.cs index 943e649..329638a 100644 --- a/YMouseButtonControl.DataAccess/Models/Setting.cs +++ b/YMouseButtonControl.Domain/Models/Setting.cs @@ -1,4 +1,4 @@ -namespace YMouseButtonControl.DataAccess.Models; +namespace YMouseButtonControl.Domain.Models; public abstract class Setting { diff --git a/YMouseButtonControl.DataAccess/Models/SimulatedKeystrokeType.cs b/YMouseButtonControl.Domain/Models/SimulatedKeystrokeType.cs similarity index 87% rename from YMouseButtonControl.DataAccess/Models/SimulatedKeystrokeType.cs rename to YMouseButtonControl.Domain/Models/SimulatedKeystrokeType.cs index 6d32881..332b730 100644 --- a/YMouseButtonControl.DataAccess/Models/SimulatedKeystrokeType.cs +++ b/YMouseButtonControl.Domain/Models/SimulatedKeystrokeType.cs @@ -1,4 +1,4 @@ -namespace YMouseButtonControl.DataAccess.Models; +namespace YMouseButtonControl.Domain.Models; public enum SimulatedKeystrokeType { diff --git a/YMouseButtonControl.DataAccess/Models/Theme.cs b/YMouseButtonControl.Domain/Models/Theme.cs similarity index 79% rename from YMouseButtonControl.DataAccess/Models/Theme.cs rename to YMouseButtonControl.Domain/Models/Theme.cs index c2090a1..b91c248 100644 --- a/YMouseButtonControl.DataAccess/Models/Theme.cs +++ b/YMouseButtonControl.Domain/Models/Theme.cs @@ -1,4 +1,4 @@ -namespace YMouseButtonControl.DataAccess.Models; +namespace YMouseButtonControl.Domain.Models; public class Theme { diff --git a/YMouseButtonControl.Domain/YMouseButtonControl.Domain.csproj b/YMouseButtonControl.Domain/YMouseButtonControl.Domain.csproj new file mode 100644 index 0000000..3a63532 --- /dev/null +++ b/YMouseButtonControl.Domain/YMouseButtonControl.Domain.csproj @@ -0,0 +1,9 @@ + + + + net8.0 + enable + enable + + + diff --git a/YMouseButtonControl.Linux.Tests/GlobalUsings.cs b/YMouseButtonControl.Linux.Tests/GlobalUsings.cs deleted file mode 100644 index 3244567..0000000 --- a/YMouseButtonControl.Linux.Tests/GlobalUsings.cs +++ /dev/null @@ -1 +0,0 @@ -global using NUnit.Framework; diff --git a/YMouseButtonControl.Linux.Tests/StartupInstallerTests.cs b/YMouseButtonControl.Linux.Tests/StartupInstallerTests.cs deleted file mode 100644 index e7000ca..0000000 --- a/YMouseButtonControl.Linux.Tests/StartupInstallerTests.cs +++ /dev/null @@ -1,44 +0,0 @@ -using YMouseButtonControl.Linux.Services; - -namespace YMouseButtonControl.Linux.Tests; - -[Explicit] -public class StartupInstallerTests -{ - [Test] - public void I_can_get_startup_install_status() - { - var sut = new StartupInstallerService(); - - Assert.DoesNotThrow(() => - { - sut.InstallStatus(); - }); - } - - [Test] - public void I_can_install() - { - var sut = new StartupInstallerService(); - - Assert.DoesNotThrow(() => - { - sut.Install(); - - Assert.That(sut.InstallStatus(), Is.True); - }); - } - - [Test] - public void I_can_uninstall() - { - var sut = new StartupInstallerService(); - - Assert.DoesNotThrow(() => - { - sut.Uninstall(); - - Assert.That(sut.InstallStatus(), Is.False); - }); - } -} diff --git a/YMouseButtonControl.Linux.Tests/YMouseButtonControl.Linux.Tests.csproj b/YMouseButtonControl.Linux.Tests/YMouseButtonControl.Linux.Tests.csproj deleted file mode 100644 index 56ef86c..0000000 --- a/YMouseButtonControl.Linux.Tests/YMouseButtonControl.Linux.Tests.csproj +++ /dev/null @@ -1,30 +0,0 @@ - - - - net8.0 - enable - enable - - false - true - - - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - - diff --git a/YMouseButtonControl.Linux/Services/BackgroundTasksRunner.cs b/YMouseButtonControl.Linux/Services/BackgroundTasksRunner.cs deleted file mode 100644 index c905057..0000000 --- a/YMouseButtonControl.Linux/Services/BackgroundTasksRunner.cs +++ /dev/null @@ -1,28 +0,0 @@ -using YMouseButtonControl.Core.Services.BackgroundTasks; -using YMouseButtonControl.Core.Services.KeyboardAndMouse; -using YMouseButtonControl.Core.Services.KeyboardAndMouse.Implementations; - -namespace YMouseButtonControl.Linux.Services; - -public class BackgroundTasksRunner : IBackgroundTasksRunner -{ - private readonly IMouseListener _mouseListener; - private readonly KeyboardSimulatorWorker _keyboardSimulatorWorker; - - public BackgroundTasksRunner( - IMouseListener mouseListener, - KeyboardSimulatorWorker keyboardSimulatorWorker - ) - { - _mouseListener = mouseListener; - _keyboardSimulatorWorker = keyboardSimulatorWorker; - _mouseListener.Run(); - _keyboardSimulatorWorker.Run(); - } - - public void Dispose() - { - _mouseListener.Dispose(); - _keyboardSimulatorWorker.Dispose(); - } -} diff --git a/YMouseButtonControl.Linux/Services/CurrentWindowService.cs b/YMouseButtonControl.Linux/Services/CurrentWindowService.cs deleted file mode 100644 index 0606c86..0000000 --- a/YMouseButtonControl.Linux/Services/CurrentWindowService.cs +++ /dev/null @@ -1,8 +0,0 @@ -using YMouseButtonControl.Core.Services.Processes; - -namespace YMouseButtonControl.Linux.Services; - -public class CurrentWindowService : ICurrentWindowService -{ - public string ForegroundWindow => "*"; -} diff --git a/YMouseButtonControl.Linux/Services/ProcessMonitorService.cs b/YMouseButtonControl.Linux/Services/ProcessMonitorService.cs deleted file mode 100644 index c983a6a..0000000 --- a/YMouseButtonControl.Linux/Services/ProcessMonitorService.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Diagnostics; -using YMouseButtonControl.Core.Services.Processes; - -namespace YMouseButtonControl.Linux.Services; - -public class ProcessMonitorService : IProcessMonitorService -{ - public IEnumerable GetProcesses() => - Process - .GetProcesses() - .Select(x => new ProcessModel(x)) - .Where(x => - !string.IsNullOrWhiteSpace(x.Process.MainModule?.ModuleName) - && !string.IsNullOrWhiteSpace(x.Process.ProcessName) - ); -} diff --git a/YMouseButtonControl.Linux/Services/StartMenuInstallerService.cs b/YMouseButtonControl.Linux/Services/StartMenuInstallerService.cs deleted file mode 100644 index c2c57ad..0000000 --- a/YMouseButtonControl.Linux/Services/StartMenuInstallerService.cs +++ /dev/null @@ -1,49 +0,0 @@ -using YMouseButtonControl.Core.Services.StartMenuInstaller; - -namespace YMouseButtonControl.Linux.Services; - -public class StartMenuInstallerService : IStartMenuInstallerService -{ - 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 StartMenuInstallerService() - { - var applicationsDir = Path.Combine(_localShare, "applications"); - _desktopFilePath = Path.Combine(applicationsDir, "YMouseButtonControl.desktop"); - } - - public bool InstallStatus() => - File.Exists(_desktopFilePath) - && File.ReadAllText(_desktopFilePath).Contains($"Exec={GetCurExePath()}"); - - public void Install() => - File.WriteAllText( - _desktopFilePath, - string.Format(DesktopFile, GetCurExePath(), GetCurExeParentPath()) - ); - - public void Uninstall() => File.Delete(_desktopFilePath); - - 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.Linux/Services/StartupInstallerService.cs b/YMouseButtonControl.Linux/Services/StartupInstallerService.cs deleted file mode 100644 index 47b5a68..0000000 --- a/YMouseButtonControl.Linux/Services/StartupInstallerService.cs +++ /dev/null @@ -1,62 +0,0 @@ -using YMouseButtonControl.Core.Services.StartupInstaller; - -namespace YMouseButtonControl.Linux.Services; - -public class StartupInstallerService : IStartupInstallerService -{ - 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 StartupInstallerService() - { - _autostartDir = Path.Combine(_configDir, "autostart"); - _desktopFilePath = Path.Combine(_autostartDir, "YMouseButtonControl.desktop"); - } - - public bool ButtonEnabled() => Directory.Exists(_autostartDir); - - public bool InstallStatus() - { - if (!Directory.Exists(_autostartDir)) - { - return false; - } - - if (!File.Exists(_desktopFilePath)) - { - return false; - } - - var expectedExecLine = $"Exec={GetCurExePath()}"; - return File.ReadAllText(_desktopFilePath).Contains(expectedExecLine); - } - - public void Install() => - File.WriteAllText( - _desktopFilePath, - string.Format(DesktopFile, GetCurExePath(), GetCurExeParentPath()) - ); - - public void Uninstall() => File.Delete(_desktopFilePath); - - 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.Linux/YMouseButtonControl.Linux.csproj b/YMouseButtonControl.Linux/YMouseButtonControl.Linux.csproj deleted file mode 100644 index fa29fe5..0000000 --- a/YMouseButtonControl.Linux/YMouseButtonControl.Linux.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net8.0 - enable - enable - default - true - - - - true - - - - true - - - - - - - diff --git a/YMouseButtonControl.MacOS/Services/BackgroundTasksRunner.cs b/YMouseButtonControl.MacOS/Services/BackgroundTasksRunner.cs deleted file mode 100644 index 315fe8e..0000000 --- a/YMouseButtonControl.MacOS/Services/BackgroundTasksRunner.cs +++ /dev/null @@ -1,28 +0,0 @@ -using YMouseButtonControl.Core.Services.BackgroundTasks; -using YMouseButtonControl.Core.Services.KeyboardAndMouse; -using YMouseButtonControl.Core.Services.KeyboardAndMouse.Implementations; - -namespace YMouseButtonControl.MacOS.Services; - -public class BackgroundTasksRunner : IBackgroundTasksRunner -{ - private readonly IMouseListener _mouseListener; - private readonly KeyboardSimulatorWorker _keyboardSimulatorWorker; - - public BackgroundTasksRunner( - IMouseListener mouseListener, - KeyboardSimulatorWorker keyboardSimulatorWorker - ) - { - _mouseListener = mouseListener; - _keyboardSimulatorWorker = keyboardSimulatorWorker; - _mouseListener.Run(); - _keyboardSimulatorWorker.Run(); - } - - public void Dispose() - { - _mouseListener.Dispose(); - _keyboardSimulatorWorker.Dispose(); - } -} diff --git a/YMouseButtonControl.MacOS/Services/ProcessMonitorService.cs b/YMouseButtonControl.MacOS/Services/ProcessMonitorService.cs deleted file mode 100644 index 305a767..0000000 --- a/YMouseButtonControl.MacOS/Services/ProcessMonitorService.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using YMouseButtonControl.Core.Services.Processes; - -namespace YMouseButtonControl.MacOS.Services; - -public class ProcessMonitorService : IProcessMonitorService -{ - public IEnumerable GetProcesses() => - Process - .GetProcesses() - .Select(x => new ProcessModel(x)) - .Where(x => !string.IsNullOrWhiteSpace(x.Process.ProcessName)); -} diff --git a/YMouseButtonControl.MacOS/Services/StartMenuInstaller.cs b/YMouseButtonControl.MacOS/Services/StartMenuInstaller.cs deleted file mode 100644 index 26ddb0c..0000000 --- a/YMouseButtonControl.MacOS/Services/StartMenuInstaller.cs +++ /dev/null @@ -1,21 +0,0 @@ -using YMouseButtonControl.Core.Services.StartMenuInstaller; - -namespace YMouseButtonControl.MacOS.Services; - -public class StartMenuInstaller : IStartMenuInstallerService -{ - public bool InstallStatus() - { - throw new System.NotImplementedException(); - } - - public void Install() - { - throw new System.NotImplementedException(); - } - - public void Uninstall() - { - throw new System.NotImplementedException(); - } -} diff --git a/YMouseButtonControl.MacOS/Services/StartupInstallerService.cs b/YMouseButtonControl.MacOS/Services/StartupInstallerService.cs deleted file mode 100644 index 51c8893..0000000 --- a/YMouseButtonControl.MacOS/Services/StartupInstallerService.cs +++ /dev/null @@ -1,57 +0,0 @@ -using System; -using System.IO; -using YMouseButtonControl.Core.Services.StartupInstaller; - -namespace YMouseButtonControl.MacOS.Services; - -public class StartupInstallerService : IStartupInstallerService -{ - private const string PlistData = """ - - - - - Label - com.github.ymousebuttoncontrol - - ProgramArguments - - {0} - - - RunAtLoad - - - KeepAlive - - - - """; - - private readonly string _usrLaunchAgentsDir; - private readonly string _plistPath; - - public StartupInstallerService() - { - _usrLaunchAgentsDir = Path.Join( - Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), - "Library", - "LaunchAgents" - ); - _plistPath = Path.Join(_usrLaunchAgentsDir, "com.github.ymousebuttoncontrol.plist"); - } - - public bool ButtonEnabled() => Directory.Exists(_usrLaunchAgentsDir); - - public bool InstallStatus() => - File.Exists(_plistPath) - && File.ReadAllText(_plistPath).Contains($"{GetCurExePath()}"); - - public void Install() => - File.WriteAllText(_plistPath, string.Format(PlistData, GetCurExePath())); - - public void Uninstall() => File.Delete(_plistPath); - - private static string GetCurExePath() => - Environment.ProcessPath ?? throw new Exception("Error retrieving process path"); -} diff --git a/YMouseButtonControl.MacOS/YMouseButtonControl.MacOS.csproj b/YMouseButtonControl.MacOS/YMouseButtonControl.MacOS.csproj deleted file mode 100644 index b8ed9dc..0000000 --- a/YMouseButtonControl.MacOS/YMouseButtonControl.MacOS.csproj +++ /dev/null @@ -1,22 +0,0 @@ - - - - net8.0 - latest - enable - true - - - - true - - - - true - - - - - - - diff --git a/YMouseButtonControl.Windows.Tests/StartupInstallerTests.cs b/YMouseButtonControl.Windows.Tests/StartupInstallerTests.cs deleted file mode 100644 index 857b77c..0000000 --- a/YMouseButtonControl.Windows.Tests/StartupInstallerTests.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System.Runtime.Versioning; -using YMouseButtonControl.Windows; -using YMouseButtonControl.Windows.Services; - -namespace YMouseButtonControl.Windows.Tests; - -[SupportedOSPlatform("windows")] -[Explicit] -public class StartupInstallerTests -{ - [Test] - public void I_can_get_startup_install_status() - { - var sut = new StartupInstallerService(); - - Assert.DoesNotThrow(() => - { - sut.InstallStatus(); - }); - } - - [Test] - public void I_can_install() - { - var sut = new StartupInstallerService(); - - Assert.DoesNotThrow(() => - { - sut.Install(); - - Assert.That(sut.InstallStatus(), Is.True); - }); - } - - [Test] - public void I_can_uninstall() - { - var sut = new StartupInstallerService(); - - Assert.DoesNotThrow(() => - { - sut.Uninstall(); - - Assert.That(sut.InstallStatus(), Is.False); - }); - } -} diff --git a/YMouseButtonControl.Windows.Tests/YMouseButtonControl.Windows.Tests.csproj b/YMouseButtonControl.Windows.Tests/YMouseButtonControl.Windows.Tests.csproj deleted file mode 100644 index 4eb2aaa..0000000 --- a/YMouseButtonControl.Windows.Tests/YMouseButtonControl.Windows.Tests.csproj +++ /dev/null @@ -1,34 +0,0 @@ - - - - net8.0 - enable - enable - - false - true - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - - - - - - - diff --git a/YMouseButtonControl.Windows/Services/ProcessMonitorService.cs b/YMouseButtonControl.Windows/Services/ProcessMonitorService.cs deleted file mode 100644 index c2aba8f..0000000 --- a/YMouseButtonControl.Windows/Services/ProcessMonitorService.cs +++ /dev/null @@ -1,61 +0,0 @@ -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.Services.Processes; - -namespace YMouseButtonControl.Windows.Services; - -[SupportedOSPlatform("windows5.1.2600")] -public class ProcessMonitorService : IProcessMonitorService -{ - public IEnumerable GetProcesses() - { - var cb = new ConcurrentBag(); - Parallel.ForEach( - Process.GetProcesses().DistinctBy(x => x.ProcessName), - p => - { - try - { - if (p.MainModule != null) - { - cb.Add(p); - } - } - catch (Win32Exception) { } - } - ); - return cb.DistinctBy(x => x.MainModule!.FileName) - .Select(x => new ProcessModel(x) - { - Bitmap = GetBitmapStreamFromPath(x.MainModule!.FileName), - }) - .ToList(); - } - - 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.Windows/Services/StartMenuInstallerService.cs b/YMouseButtonControl.Windows/Services/StartMenuInstallerService.cs deleted file mode 100644 index 2371ef2..0000000 --- a/YMouseButtonControl.Windows/Services/StartMenuInstallerService.cs +++ /dev/null @@ -1,70 +0,0 @@ -using System; -using System.IO; -using WindowsShortcutFactory; -using YMouseButtonControl.Core.Services.StartMenuInstaller; - -namespace YMouseButtonControl.Windows.Services; - -public class StartMenuInstallerService : IStartMenuInstallerService -{ - private readonly string _roamingAppDataFolder = Environment.GetFolderPath( - Environment.SpecialFolder.ApplicationData - ); - - private readonly string _roamingYMouseButtonsFolder; - - private readonly string _roamingYmouseButtonsShortcutPath; - - public StartMenuInstallerService() - { - _roamingYMouseButtonsFolder = Path.Combine( - _roamingAppDataFolder, - "Microsoft", - "Windows", - "Start Menu", - "Programs", - "YMouseButtonControl" - ); - _roamingYmouseButtonsShortcutPath = Path.Combine( - _roamingYMouseButtonsFolder, - "YMouseButtonControl.lnk" - ); - } - - public bool InstallStatus() - { - if (!File.Exists(_roamingYmouseButtonsShortcutPath)) - { - return false; - } - using var shortcut = WindowsShortcut.Load(_roamingYmouseButtonsShortcutPath); - return shortcut.Path == GetCurExePath(); - } - - public void Install() - { - 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); - } - - public void Uninstall() => File.Delete(_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.Windows/Services/StartupInstallerService.cs b/YMouseButtonControl.Windows/Services/StartupInstallerService.cs deleted file mode 100644 index b3d40bf..0000000 --- a/YMouseButtonControl.Windows/Services/StartupInstallerService.cs +++ /dev/null @@ -1,59 +0,0 @@ -using System; -using System.IO; -using System.Runtime.Versioning; -using System.Security.AccessControl; -using Microsoft.Win32; -using YMouseButtonControl.Core.Services.StartupInstaller; - -namespace YMouseButtonControl.Windows.Services; - -[SupportedOSPlatform("windows")] -public class StartupInstallerService : IStartupInstallerService -{ - private const string BaseKeyPath = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Run\"; - private const string ValName = "YMouseButtonControl"; - - public bool ButtonEnabled() => true; - - /// - /// Gets the start-up install status of the program - /// - /// True = installed, False = not installed - public bool InstallStatus() - { - 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(); - } - - public void Install() - { - 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); - } - - public void Uninstall() - { - using var key = - Registry.CurrentUser.OpenSubKey( - BaseKeyPath, - RegistryKeyPermissionCheck.ReadWriteSubTree, - RegistryRights.FullControl - ) ?? throw new Exception($"Error opening key {BaseKeyPath}"); - key.DeleteValue(ValName); - } - - private static string GetCurExePath() => - Environment.ProcessPath ?? throw new Exception("Error retrieving process path"); -} diff --git a/YMouseButtonControl.Windows/YMouseButtonControl.Windows.csproj b/YMouseButtonControl.Windows/YMouseButtonControl.Windows.csproj deleted file mode 100644 index 1cbe3f8..0000000 --- a/YMouseButtonControl.Windows/YMouseButtonControl.Windows.csproj +++ /dev/null @@ -1,30 +0,0 @@ - - - - net8.0 - latest - enable - - - - true - - - - true - - - - - - - - - all - - - - - - - diff --git a/YMouseButtonControl.db-shm b/YMouseButtonControl.db-shm new file mode 100644 index 0000000..fe9ac28 Binary files /dev/null and b/YMouseButtonControl.db-shm differ diff --git a/YMouseButtonControl.db-wal b/YMouseButtonControl.db-wal new file mode 100644 index 0000000..e69de29 diff --git a/YMouseButtonControl.sln b/YMouseButtonControl.sln index 996cabb..ff98def 100644 --- a/YMouseButtonControl.sln +++ b/YMouseButtonControl.sln @@ -1,33 +1,22 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "YMouseButtonControl.Core.Tests", "YMouseButtonControl.Core.Tests\YMouseButtonControl.Core.Tests.csproj", "{0F770391-A4A1-611D-4FBA-125A6EB42834}" -EndProject +# Visual Studio Version 17 +VisualStudioVersion = 17.12.35527.113 +MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "YMouseButtonControl.Core", "YMouseButtonControl.Core\YMouseButtonControl.Core.csproj", "{CEB84FCC-DB49-6456-7173-1F508E465D31}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "YMouseButtonControl.DataAccess", "YMouseButtonControl.DataAccess\YMouseButtonControl.DataAccess.csproj", "{A4C2C9B9-43FA-ACCF-4C5B-E79ECC6F61BA}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "YMouseButtonControl.Linux.Tests", "YMouseButtonControl.Linux.Tests\YMouseButtonControl.Linux.Tests.csproj", "{BD4EFA4F-A2CD-2BDE-BAA5-C55B4B220A84}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "YMouseButtonControl.Linux", "YMouseButtonControl.Linux\YMouseButtonControl.Linux.csproj", "{A5CC65AD-9267-EF18-07FE-77C6AD6D454A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "YMouseButtonControl.MacOS", "YMouseButtonControl.MacOS\YMouseButtonControl.MacOS.csproj", "{3B6B60EF-52F8-75F2-B82F-818C79904027}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "YMouseButtonControl.Windows.Tests", "YMouseButtonControl.Windows.Tests\YMouseButtonControl.Windows.Tests.csproj", "{30EBA756-11F4-DFCE-0D4B-05691CA902A7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "YMouseButtonControl.Windows", "YMouseButtonControl.Windows\YMouseButtonControl.Windows.csproj", "{71DBAE95-A199-1248-A4B2-AA579ED0354B}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "YMouseButtonControl.Infrastructure", "YMouseButtonControl.DataAccess\YMouseButtonControl.Infrastructure.csproj", "{A4C2C9B9-43FA-ACCF-4C5B-E79ECC6F61BA}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "YMouseButtonControl", "YMouseButtonControl\YMouseButtonControl.csproj", "{9E275480-14AB-A39F-C3FB-ED2FDB97361F}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "YMouseButtonControl.Domain", "YMouseButtonControl.Domain\YMouseButtonControl.Domain.csproj", "{4DE55456-2875-4979-BB42-8D57B0FDE353}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {0F770391-A4A1-611D-4FBA-125A6EB42834}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0F770391-A4A1-611D-4FBA-125A6EB42834}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0F770391-A4A1-611D-4FBA-125A6EB42834}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0F770391-A4A1-611D-4FBA-125A6EB42834}.Release|Any CPU.Build.0 = Release|Any CPU {CEB84FCC-DB49-6456-7173-1F508E465D31}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {CEB84FCC-DB49-6456-7173-1F508E465D31}.Debug|Any CPU.Build.0 = Debug|Any CPU {CEB84FCC-DB49-6456-7173-1F508E465D31}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -36,29 +25,16 @@ Global {A4C2C9B9-43FA-ACCF-4C5B-E79ECC6F61BA}.Debug|Any CPU.Build.0 = Debug|Any CPU {A4C2C9B9-43FA-ACCF-4C5B-E79ECC6F61BA}.Release|Any CPU.ActiveCfg = Release|Any CPU {A4C2C9B9-43FA-ACCF-4C5B-E79ECC6F61BA}.Release|Any CPU.Build.0 = Release|Any CPU - {BD4EFA4F-A2CD-2BDE-BAA5-C55B4B220A84}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {BD4EFA4F-A2CD-2BDE-BAA5-C55B4B220A84}.Debug|Any CPU.Build.0 = Debug|Any CPU - {BD4EFA4F-A2CD-2BDE-BAA5-C55B4B220A84}.Release|Any CPU.ActiveCfg = Release|Any CPU - {BD4EFA4F-A2CD-2BDE-BAA5-C55B4B220A84}.Release|Any CPU.Build.0 = Release|Any CPU - {A5CC65AD-9267-EF18-07FE-77C6AD6D454A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A5CC65AD-9267-EF18-07FE-77C6AD6D454A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A5CC65AD-9267-EF18-07FE-77C6AD6D454A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A5CC65AD-9267-EF18-07FE-77C6AD6D454A}.Release|Any CPU.Build.0 = Release|Any CPU - {3B6B60EF-52F8-75F2-B82F-818C79904027}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3B6B60EF-52F8-75F2-B82F-818C79904027}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3B6B60EF-52F8-75F2-B82F-818C79904027}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3B6B60EF-52F8-75F2-B82F-818C79904027}.Release|Any CPU.Build.0 = Release|Any CPU - {30EBA756-11F4-DFCE-0D4B-05691CA902A7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {30EBA756-11F4-DFCE-0D4B-05691CA902A7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {30EBA756-11F4-DFCE-0D4B-05691CA902A7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {30EBA756-11F4-DFCE-0D4B-05691CA902A7}.Release|Any CPU.Build.0 = Release|Any CPU - {71DBAE95-A199-1248-A4B2-AA579ED0354B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {71DBAE95-A199-1248-A4B2-AA579ED0354B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {71DBAE95-A199-1248-A4B2-AA579ED0354B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {71DBAE95-A199-1248-A4B2-AA579ED0354B}.Release|Any CPU.Build.0 = Release|Any CPU {9E275480-14AB-A39F-C3FB-ED2FDB97361F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {9E275480-14AB-A39F-C3FB-ED2FDB97361F}.Debug|Any CPU.Build.0 = Debug|Any CPU {9E275480-14AB-A39F-C3FB-ED2FDB97361F}.Release|Any CPU.ActiveCfg = Release|Any CPU {9E275480-14AB-A39F-C3FB-ED2FDB97361F}.Release|Any CPU.Build.0 = Release|Any CPU + {4DE55456-2875-4979-BB42-8D57B0FDE353}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4DE55456-2875-4979-BB42-8D57B0FDE353}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4DE55456-2875-4979-BB42-8D57B0FDE353}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4DE55456-2875-4979-BB42-8D57B0FDE353}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE EndGlobalSection EndGlobal diff --git a/YMouseButtonControl/App.axaml b/YMouseButtonControl/App.axaml index e31ad54..42dbb3e 100644 --- a/YMouseButtonControl/App.axaml +++ b/YMouseButtonControl/App.axaml @@ -1,6 +1,8 @@ @@ -9,7 +11,7 @@ - + diff --git a/YMouseButtonControl/App.axaml.cs b/YMouseButtonControl/App.axaml.cs index 946bfe7..c4337c8 100644 --- a/YMouseButtonControl/App.axaml.cs +++ b/YMouseButtonControl/App.axaml.cs @@ -4,6 +4,7 @@ using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Markup.Xaml; using Avalonia.ReactiveUI; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -12,14 +13,14 @@ using ReactiveUI; using Splat; using Splat.Microsoft.Extensions.DependencyInjection; -using YMouseButtonControl.Core.Services.BackgroundTasks; -using YMouseButtonControl.Core.Services.Settings; -using YMouseButtonControl.Core.ViewModels.AppViewModel; +using YMouseButtonControl.BackgroundTaskRunner; +using YMouseButtonControl.Core.ViewModels.App; using YMouseButtonControl.Core.ViewModels.MainWindow; using YMouseButtonControl.Core.ViewModels.Models; using YMouseButtonControl.Core.Views; -using YMouseButtonControl.DataAccess.Context; using YMouseButtonControl.DependencyInjection; +using YMouseButtonControl.Infrastructure.Context; +using YMouseButtonControl.Queries.Settings; using ILogger = Microsoft.Extensions.Logging.ILogger; using LogLevel = Microsoft.Extensions.Logging.LogLevel; @@ -45,8 +46,11 @@ private void Init() .ConfigureServices(services => { services.UseMicrosoftDependencyResolver(); + services.AddDbContext(opts => + opts.UseSqlite(configuration.GetConnectionString("YMouseButtonControlContext")) + ); services.AddScoped(_ => configuration); - services.AddScoped(); + //services.AddScoped(); var resolver = Locator.CurrentMutable; resolver.InitializeSplat(); @@ -75,7 +79,7 @@ private void Init() Container = host.Services; Container.UseMicrosoftDependencyResolver(); - Container.GetRequiredService().Init(); + //Container.GetRequiredService().Init(); RxApp.MainThreadScheduler = AvaloniaScheduler.Instance; } @@ -91,16 +95,16 @@ public override void OnFrameworkInitializationCompleted() { DataContext = Container?.GetRequiredService(); _backgroundTasksRunner = Container?.GetRequiredService(); - var settingsService = - Container?.GetRequiredService() - ?? throw new Exception($"Error retrieving {nameof(ISettingsService)}"); - var startMinimized = - settingsService.GetSetting("StartMinimized") as SettingBoolVm - ?? throw new Exception("Error retrieving setting StartMinimized"); + var getBoolSettingHandler = + Container?.GetRequiredService() + ?? throw new Exception($"Error retrieving {nameof(GetBoolSetting.Handler)}"); + var startMinimized = getBoolSettingHandler.Execute( + new GetBoolSetting.Query("StartMinimized") + ); if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { - if (!startMinimized.BoolValue) + if (!startMinimized) { var mainWindow = (Window)Container.GetRequiredService(); mainWindow.DataContext = Container.GetRequiredService(); diff --git a/YMouseButtonControl.Windows/Services/BackgroundTasksRunner.cs b/YMouseButtonControl/BackgroundTaskRunner/BackgroundTasksRunner.cs similarity index 85% rename from YMouseButtonControl.Windows/Services/BackgroundTasksRunner.cs rename to YMouseButtonControl/BackgroundTaskRunner/BackgroundTasksRunner.cs index 335c4db..4a4885f 100644 --- a/YMouseButtonControl.Windows/Services/BackgroundTasksRunner.cs +++ b/YMouseButtonControl/BackgroundTaskRunner/BackgroundTasksRunner.cs @@ -1,8 +1,10 @@ -using YMouseButtonControl.Core.Services.BackgroundTasks; +using System; using YMouseButtonControl.Core.Services.KeyboardAndMouse; using YMouseButtonControl.Core.Services.KeyboardAndMouse.Implementations; -namespace YMouseButtonControl.Windows.Services; +namespace YMouseButtonControl.BackgroundTaskRunner; + +public interface IBackgroundTasksRunner : IDisposable { } public class BackgroundTasksRunner : IBackgroundTasksRunner { diff --git a/YMouseButtonControl/DependencyInjection/Bootstrapper.cs b/YMouseButtonControl/DependencyInjection/Bootstrapper.cs index ed2ac15..7570998 100644 --- a/YMouseButtonControl/DependencyInjection/Bootstrapper.cs +++ b/YMouseButtonControl/DependencyInjection/Bootstrapper.cs @@ -8,9 +8,7 @@ public static void Register(IServiceCollection services) { ServicesBootstrapper.RegisterServices(services); FactoriesBootstrapper.RegisterFactories(services); - DataAccessBootstrapper.RegisterDataAccess(services); KeyboardAndMouseBootstrapper.RegisterKeyboardAndMouse(services); - FeaturesBootstrapper.RegisterFeatures(services); ViewModelsBootstrapper.RegisterViewModels(services); ViewBootstrapper.RegisterViews(services); } diff --git a/YMouseButtonControl/DependencyInjection/DataAccessBootstrapper.cs b/YMouseButtonControl/DependencyInjection/DataAccessBootstrapper.cs deleted file mode 100644 index 7af089d..0000000 --- a/YMouseButtonControl/DependencyInjection/DataAccessBootstrapper.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Microsoft.Extensions.DependencyInjection; -using YMouseButtonControl.Core.Repositories; -using YMouseButtonControl.Core.ViewModels.Models; -using YMouseButtonControl.DataAccess.Models; -using YMouseButtonControl.DataAccess.Queries; - -namespace YMouseButtonControl.DependencyInjection; - -public static class DataAccessBootstrapper -{ - public static void RegisterDataAccess(IServiceCollection services) - { - services - .AddScoped() - .AddScoped() - .AddScoped() - .AddScoped() - .AddScoped, ProfileRepository>() - .AddScoped, ButtonMappingRepository>() - .AddScoped, SettingRepository>() - .AddScoped, ThemeRepository>(); - } -} diff --git a/YMouseButtonControl/DependencyInjection/FactoriesBootstrapper.cs b/YMouseButtonControl/DependencyInjection/FactoriesBootstrapper.cs index 9f28da7..9ef71ab 100644 --- a/YMouseButtonControl/DependencyInjection/FactoriesBootstrapper.cs +++ b/YMouseButtonControl/DependencyInjection/FactoriesBootstrapper.cs @@ -1,5 +1,7 @@ using Microsoft.Extensions.DependencyInjection; -using YMouseButtonControl.Core.ViewModels.LayerViewModel; +using YMouseButtonControl.Core.ViewModels.Dialogs.ProcessSelectorDialog; +using YMouseButtonControl.Core.ViewModels.Dialogs.SimulatedKeystrokesDialog; +using YMouseButtonControl.Core.ViewModels.MouseCombo; namespace YMouseButtonControl.DependencyInjection; @@ -7,6 +9,9 @@ public static class FactoriesBootstrapper { public static void RegisterFactories(IServiceCollection services) { - services.AddScoped(); + services + .AddScoped() + .AddScoped() + .AddScoped(); } } diff --git a/YMouseButtonControl/DependencyInjection/FeaturesBootstrapper.cs b/YMouseButtonControl/DependencyInjection/FeaturesBootstrapper.cs deleted file mode 100644 index 1d85be5..0000000 --- a/YMouseButtonControl/DependencyInjection/FeaturesBootstrapper.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Microsoft.Extensions.DependencyInjection; -using YMouseButtonControl.Core.ViewModels.MainWindow.Features.Apply; -using YMouseButtonControl.Core.ViewModels.ProfilesList.Features.Add; - -namespace YMouseButtonControl.DependencyInjection; - -public static class FeaturesBootstrapper -{ - public static void RegisterFeatures(IServiceCollection services) - { - services.AddTransient().AddTransient(); - } -} diff --git a/YMouseButtonControl/DependencyInjection/KeyboardAndMouseBootstrapper.cs b/YMouseButtonControl/DependencyInjection/KeyboardAndMouseBootstrapper.cs index 3403707..b5af865 100644 --- a/YMouseButtonControl/DependencyInjection/KeyboardAndMouseBootstrapper.cs +++ b/YMouseButtonControl/DependencyInjection/KeyboardAndMouseBootstrapper.cs @@ -2,12 +2,12 @@ using Microsoft.Extensions.DependencyInjection; using SharpHook; using SharpHook.Reactive; +using SharpHook.Testing; using YMouseButtonControl.Core.Services.KeyboardAndMouse; 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.Linux.Services; namespace YMouseButtonControl.DependencyInjection; @@ -23,15 +23,15 @@ private static void RegisterOsSpecificKeyboardAndMouseServices(IServiceCollectio { if (OperatingSystem.IsWindowsVersionAtLeast(5, 1, 2600)) { - services.AddScoped(); + services.AddScoped(); } else if (OperatingSystem.IsMacOS()) { - services.AddScoped(); + services.AddScoped(); } else if (OperatingSystem.IsLinux()) { - services.AddScoped(); + services.AddScoped(); } else { @@ -43,8 +43,14 @@ private static void RegisterCommonKeyboardAndMouseServices(IServiceCollection se { services .AddScoped() +#if DEBUG + .AddScoped( + (_) => new SimpleReactiveGlobalHook(globalHookProvider: new TestProvider()) + ) +#else .AddScoped() - .AddScoped() +#endif + .AddScoped() .AddScoped() .AddScoped() .AddScoped() diff --git a/YMouseButtonControl/DependencyInjection/ServicesBootstrapper.cs b/YMouseButtonControl/DependencyInjection/ServicesBootstrapper.cs index 797e398..cb9349b 100644 --- a/YMouseButtonControl/DependencyInjection/ServicesBootstrapper.cs +++ b/YMouseButtonControl/DependencyInjection/ServicesBootstrapper.cs @@ -1,14 +1,20 @@ using System; using System.Runtime.Versioning; using Microsoft.Extensions.DependencyInjection; -using YMouseButtonControl.Core.Services.BackgroundTasks; -using YMouseButtonControl.Core.Services.Logging; -using YMouseButtonControl.Core.Services.Processes; +using YMouseButtonControl.BackgroundTaskRunner; +using YMouseButtonControl.Core.Services.KeyboardAndMouse.Implementations; +using YMouseButtonControl.Core.Services.KeyboardAndMouse.Implementations.Queries.CurrentWindow; using YMouseButtonControl.Core.Services.Profiles; -using YMouseButtonControl.Core.Services.Settings; -using YMouseButtonControl.Core.Services.StartMenuInstaller; -using YMouseButtonControl.Core.Services.StartupInstaller; -using YMouseButtonControl.Core.Services.Theme; +using YMouseButtonControl.Core.Services.Profiles.Queries.Profiles; +using YMouseButtonControl.Core.ViewModels.App; +using YMouseButtonControl.Core.ViewModels.Dialogs.GlobalSettingsDialog; +using YMouseButtonControl.Core.ViewModels.Dialogs.ProcessSelectorDialog; +using YMouseButtonControl.Core.ViewModels.Dialogs.SimulatedKeystrokesDialog; +using YMouseButtonControl.Core.ViewModels.MainWindow; +using YMouseButtonControl.Core.ViewModels.MouseCombo; +using YMouseButtonControl.Core.ViewModels.ProfilesList; +using YMouseButtonControl.Core.ViewModels.ProfilesList.Commands.Profiles; +using YMouseButtonControl.Queries.Settings; namespace YMouseButtonControl.DependencyInjection; @@ -22,11 +28,20 @@ public static void RegisterServices(IServiceCollection services) private static void RegisterCommonServices(IServiceCollection services) { + AppHandlerRegistrations.RegisterCommon(services); + ProcessSelectorDialogHandlerRegistrations.RegisterCommon(services); + ProfilesListHandlerRegistrations.RegisterCommon(services); + GlobalSettingsDialogHandlerRegistrations.RegisterCommon(services); + MainWindowHandlerRegistrations.RegisterCommon(services); + MouseComboHandlerRegistrations.RegisterCommon(services); + MouseListenerHandlerRegistrations.RegisterCommon(services); + SimulatedKeystrokesDialogHandlerRegistrations.RegisterCommon(services); services - .AddScoped() - .AddScoped() - .AddScoped() - .AddScoped(); + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped(); } private static void RegisterPlatformSpecificServices(IServiceCollection services) @@ -51,39 +66,36 @@ private static void RegisterPlatformSpecificServices(IServiceCollection services private static void RegisterLinuxServices(IServiceCollection services) { - services - .AddScoped() - .AddScoped() - .AddScoped() - .AddScoped(); + AppHandlerRegistrations.RegisterLinux(services); + GlobalSettingsDialogHandlerRegistrations.RegisterLinux(services); + ProcessSelectorDialogHandlerRegistrations.RegisterLinux(services); if (Environment.GetEnvironmentVariable("XDG_SESSION_TYPE") == "x11") { - services.AddScoped(); + services.AddScoped< + IGetCurrentWindow, + Core.Services.KeyboardAndMouse.Implementations.MouseListener.Queries.CurrentWindow.GetCurrentWindowLinuxX11 + >(); } else { - services.AddScoped(); + services.AddScoped(); } } [SupportedOSPlatform("windows5.1.2600")] private static void RegisterWindowsServices(IServiceCollection services) { - services - .AddScoped() - .AddScoped() - .AddScoped() - .AddScoped() - .AddScoped(); + AppHandlerRegistrations.RegisterWindows(services); + ProcessSelectorDialogHandlerRegistrations.RegisterWindows(services); + GlobalSettingsDialogHandlerRegistrations.RegisterWindows(services); + services.AddScoped(); } private static void RegisterMacOsServices(IServiceCollection services) { - services - .AddScoped() - .AddScoped() - .AddScoped() - .AddScoped() - .AddScoped(); + AppHandlerRegistrations.RegisterOsx(services); + ProcessSelectorDialogHandlerRegistrations.RegisterOsx(services); + GlobalSettingsDialogHandlerRegistrations.RegisterOsx(services); + services.AddScoped(); } } diff --git a/YMouseButtonControl/DependencyInjection/ViewModelsBootstrapper.cs b/YMouseButtonControl/DependencyInjection/ViewModelsBootstrapper.cs index b724716..f8e4f59 100644 --- a/YMouseButtonControl/DependencyInjection/ViewModelsBootstrapper.cs +++ b/YMouseButtonControl/DependencyInjection/ViewModelsBootstrapper.cs @@ -1,8 +1,10 @@ using Microsoft.Extensions.DependencyInjection; -using YMouseButtonControl.Core.ViewModels.AppViewModel; -using YMouseButtonControl.Core.ViewModels.LayerViewModel; +using YMouseButtonControl.Core.ViewModels.App; +using YMouseButtonControl.Core.ViewModels.Dialogs.GlobalSettingsDialog; +using YMouseButtonControl.Core.ViewModels.Dialogs.ProcessSelectorDialog; +using YMouseButtonControl.Core.ViewModels.Layer; using YMouseButtonControl.Core.ViewModels.MainWindow; -using YMouseButtonControl.Core.ViewModels.ProfilesInformationViewModel; +using YMouseButtonControl.Core.ViewModels.ProfilesInformation; using YMouseButtonControl.Core.ViewModels.ProfilesList; namespace YMouseButtonControl.DependencyInjection; @@ -17,14 +19,9 @@ public static void RegisterViewModels(IServiceCollection services) private static void RegisterCommonViewModels(IServiceCollection services) { services - .AddScoped() .AddScoped() .AddScoped() .AddScoped() - .AddScoped< - IShowSimulatedKeystrokesDialogService, - ShowSimulatedKeystrokesDialogService - >() .AddScoped() .AddScoped() .AddScoped() diff --git a/YMouseButtonControl/Queries/Settings/GetBoolSetting.cs b/YMouseButtonControl/Queries/Settings/GetBoolSetting.cs new file mode 100644 index 0000000..bc4cc8d --- /dev/null +++ b/YMouseButtonControl/Queries/Settings/GetBoolSetting.cs @@ -0,0 +1,14 @@ +using System.Linq; +using YMouseButtonControl.Infrastructure.Context; + +namespace YMouseButtonControl.Queries.Settings; + +internal static class GetBoolSetting +{ + internal sealed record Query(string Name); + + internal sealed class Handler(YMouseButtonControlDbContext db) + { + internal bool Execute(Query q) => db.SettingBools.First(x => x.Name == q.Name).BoolValue; + } +} diff --git a/YMouseButtonControl/Views/Dialogs/GlobalSettingsDialog.axaml b/YMouseButtonControl/Views/Dialogs/GlobalSettingsDialog.axaml index 6364560..77f22c4 100644 --- a/YMouseButtonControl/Views/Dialogs/GlobalSettingsDialog.axaml +++ b/YMouseButtonControl/Views/Dialogs/GlobalSettingsDialog.axaml @@ -2,22 +2,24 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - xmlns:mainWindow="clr-namespace:YMouseButtonControl.Core.ViewModels.MainWindow;assembly=YMouseButtonControl.Core" + xmlns:globalSettingsDialog="clr-namespace:YMouseButtonControl.Core.ViewModels.Dialogs.GlobalSettingsDialog;assembly=YMouseButtonControl.Core" + x:DataType="globalSettingsDialog:GlobalSettingsDialogViewModel" + x:CompileBindings="True" WindowStartupLocation="CenterOwner" Width="400" SizeToContent="Height" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="YMouseButtonControl.Views.Dialogs.GlobalSettingsDialog" Title="Global Settings" - RequestedThemeVariant="{Binding ThemeService.ThemeVariant}"> + RequestedThemeVariant="{Binding ThemeVariant}"> - + + IsChecked="{Binding StartMinimized.Value}" /> + RequestedThemeVariant="{Binding ThemeVariant}"> - + @@ -37,7 +39,7 @@ - + @@ -68,14 +70,14 @@