From c6ddfeca9a2c5bb6dc8dc8048a80182226efa649 Mon Sep 17 00:00:00 2001 From: Rutherther Date: Fri, 10 Feb 2023 22:15:25 +0100 Subject: [PATCH] feat: allow editing only no profile in profile choose --- .../Models/Filters/FilterProfiles.cs | 7 +- .../Filters/FilterChooseViewModel.cs | 74 +++++- .../Filters/FilterConfigViewModel.cs | 4 +- .../Filters/FilterEntryViewModel.cs | 36 ++- .../ViewModels/Log/PacketLogViewModel.cs | 212 ++++++++++++++++++ .../Views/Log/PacketLogView.axaml | 100 +++++++++ .../Views/Log/PacketLogView.axaml.cs | 37 +++ 7 files changed, 458 insertions(+), 12 deletions(-) create mode 100644 src/PacketLogger/ViewModels/Log/PacketLogViewModel.cs create mode 100644 src/PacketLogger/Views/Log/PacketLogView.axaml create mode 100644 src/PacketLogger/Views/Log/PacketLogView.axaml.cs diff --git a/src/PacketLogger/Models/Filters/FilterProfiles.cs b/src/PacketLogger/Models/Filters/FilterProfiles.cs index fa8d298..67d1b5a 100644 --- a/src/PacketLogger/Models/Filters/FilterProfiles.cs +++ b/src/PacketLogger/Models/Filters/FilterProfiles.cs @@ -29,6 +29,11 @@ public class FilterProfiles AllProfiles = new ObservableCollection(); SelectableProfiles = new ObservableCollection(); + SelectableProfiles.Add(new FilterProfile(false) + { + Name = "No profile" + }); + AllProfiles.Add(DefaultProfile); } @@ -74,7 +79,7 @@ public class FilterProfiles /// The profile to add. public void AddProfile(FilterProfile profile) { - SelectableProfiles.Add(profile); + SelectableProfiles.Insert(SelectableProfiles.Count - 1, profile); AllProfiles.Add(profile); } diff --git a/src/PacketLogger/ViewModels/Filters/FilterChooseViewModel.cs b/src/PacketLogger/ViewModels/Filters/FilterChooseViewModel.cs index 710bdcc..79014f6 100644 --- a/src/PacketLogger/ViewModels/Filters/FilterChooseViewModel.cs +++ b/src/PacketLogger/ViewModels/Filters/FilterChooseViewModel.cs @@ -7,6 +7,7 @@ using System; using System.Collections.Generic; using System.Reactive.Disposables; +using DynamicData; using DynamicData.Binding; using PacketLogger.Models.Filters; using ReactiveUI; @@ -20,6 +21,7 @@ public class FilterChooseViewModel : ViewModelBase, IDisposable { private FilterProfile _currentProfile = null!; private IDisposable? _cleanUp; + private FilterProfile _noProfile; /// /// Initializes a new instance of the class. @@ -30,6 +32,10 @@ public class FilterChooseViewModel : ViewModelBase, IDisposable RecvFilterSelected = true; CurrentProfile = currentProfile; CurrentFilter = CreateSendRecvFilter(); + _noProfile = new FilterProfile(false) + { + Name = "No profile" + }; } /// @@ -40,9 +46,59 @@ public class FilterChooseViewModel : ViewModelBase, IDisposable get => _currentProfile; set { + if (value is null) + { + return; + } + + if (value.Name == "No profile" && value != _noProfile) + { + CurrentProfile = _noProfile; + return; + } + + var lastProfile = value; + if (value != _noProfile) + { + RecvEntryViewModel = new FilterEntryViewModel + ( + value.RecvFilterEntry, + (data) => + { + CopyCurrentToNoProfile(lastProfile); + _noProfile.RecvFilterEntry.Filters.Add(data); + CurrentProfile = _noProfile; + }, + (data) => + { + CopyCurrentToNoProfile(lastProfile); + _noProfile.RecvFilterEntry.Filters.Remove(data); + CurrentProfile = _noProfile; + } + ); + SendEntryViewModel = new FilterEntryViewModel + ( + value.SendFilterEntry, + (data) => + { + CopyCurrentToNoProfile(lastProfile); + _noProfile.SendFilterEntry.Filters.Add(data); + CurrentProfile = _noProfile; + }, + (data) => + { + CopyCurrentToNoProfile(lastProfile); + _noProfile.SendFilterEntry.Filters.Remove(data); + CurrentProfile = _noProfile; + } + ); + } + else + { + RecvEntryViewModel = new FilterEntryViewModel(value.RecvFilterEntry, null, null); + SendEntryViewModel = new FilterEntryViewModel(value.SendFilterEntry, null, null); + } _currentProfile = value; - RecvEntryViewModel = new FilterEntryViewModel(_currentProfile.RecvFilterEntry); - SendEntryViewModel = new FilterEntryViewModel(_currentProfile.SendFilterEntry); var recvWhenAny = _currentProfile.RecvFilterEntry.WhenAnyPropertyChanged("Active", "Whitelist") .Subscribe((e) => OnChange()); @@ -62,6 +118,20 @@ public class FilterChooseViewModel : ViewModelBase, IDisposable } } + private void CopyCurrentToNoProfile(FilterProfile lastProfile) + { + _noProfile.RecvFilterEntry.Filters.Clear(); + _noProfile.RecvFilterEntry.Filters.AddRange(lastProfile.RecvFilterEntry.Filters); + + _noProfile.SendFilterEntry.Filters.Clear(); + _noProfile.SendFilterEntry.Filters.AddRange(lastProfile.SendFilterEntry.Filters); + + _noProfile.RecvFilterEntry.Active = lastProfile.RecvFilterEntry.Active; + _noProfile.RecvFilterEntry.Whitelist = lastProfile.RecvFilterEntry.Whitelist; + _noProfile.SendFilterEntry.Active = lastProfile.SendFilterEntry.Active; + _noProfile.SendFilterEntry.Whitelist = lastProfile.SendFilterEntry.Whitelist; + } + /// /// Gets the current recv entry view model. /// diff --git a/src/PacketLogger/ViewModels/Filters/FilterConfigViewModel.cs b/src/PacketLogger/ViewModels/Filters/FilterConfigViewModel.cs index 089e506..4c25ad6 100644 --- a/src/PacketLogger/ViewModels/Filters/FilterConfigViewModel.cs +++ b/src/PacketLogger/ViewModels/Filters/FilterConfigViewModel.cs @@ -22,8 +22,8 @@ public class FilterConfigViewModel : ViewModelBase public FilterConfigViewModel(FilterProfile filterProfile) { _filterProfile = filterProfile; - RecvEntryViewModel = new FilterEntryViewModel(filterProfile.RecvFilterEntry); - SendEntryViewModel = new FilterEntryViewModel(filterProfile.SendFilterEntry); + RecvEntryViewModel = new FilterEntryViewModel(filterProfile.RecvFilterEntry, null, null); + SendEntryViewModel = new FilterEntryViewModel(filterProfile.SendFilterEntry, null, null); } /// diff --git a/src/PacketLogger/ViewModels/Filters/FilterEntryViewModel.cs b/src/PacketLogger/ViewModels/Filters/FilterEntryViewModel.cs index 2a1d915..391f7ea 100644 --- a/src/PacketLogger/ViewModels/Filters/FilterEntryViewModel.cs +++ b/src/PacketLogger/ViewModels/Filters/FilterEntryViewModel.cs @@ -4,6 +4,7 @@ // Copyright (c) František Boháček. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using System; using System.Reactive; using PacketLogger.Models.Filters; using ReactiveUI; @@ -19,7 +20,10 @@ public class FilterEntryViewModel : ViewModelBase /// Initializes a new instance of the class. /// /// The profile entry. - public FilterEntryViewModel(FilterProfileEntry entry) + /// The action called upon adding new. + /// The action called upon removing the given filter data. + public FilterEntryViewModel + (FilterProfileEntry entry, Action? addNew, Action? remove) { NewFilterType = FilterCreator.FilterType.PacketHeader; Entry = entry; @@ -30,14 +34,24 @@ public class FilterEntryViewModel : ViewModelBase var selected = SelectedFilter; if (selected is not null) { - var selectedIndex = Entry.Filters.IndexOf(selected); - SelectedFilter = Entry.Filters.Count > selectedIndex + 1 ? Entry.Filters[selectedIndex + 1] : null; - if (SelectedFilter is null && selectedIndex > 0) + if (remove is not null) { - SelectedFilter = Entry.Filters[selectedIndex - 1]; + SelectedFilter = null; + remove(selected); } + else + { + var selectedIndex = Entry.Filters.IndexOf(selected); + SelectedFilter = Entry.Filters.Count > selectedIndex + 1 + ? Entry.Filters[selectedIndex + 1] + : null; + if (SelectedFilter is null && selectedIndex > 0) + { + SelectedFilter = Entry.Filters[selectedIndex - 1]; + } - Entry.Filters.Remove(selected); + Entry.Filters.Remove(selected); + } } } ); @@ -49,7 +63,15 @@ public class FilterEntryViewModel : ViewModelBase if (!string.IsNullOrEmpty(NewFilter)) { var newFilter = new FilterCreator.FilterData(NewFilterType, NewFilter); - Entry.Filters.Add(newFilter); + if (addNew is not null) + { + addNew(newFilter); + } + else + { + Entry.Filters.Add(newFilter); + } + NewFilter = string.Empty; SelectedFilter = newFilter; } diff --git a/src/PacketLogger/ViewModels/Log/PacketLogViewModel.cs b/src/PacketLogger/ViewModels/Log/PacketLogViewModel.cs new file mode 100644 index 0000000..626d32f --- /dev/null +++ b/src/PacketLogger/ViewModels/Log/PacketLogViewModel.cs @@ -0,0 +1,212 @@ +// +// PacketLogViewModel.cs +// +// Copyright (c) František Boháček. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Reactive; +using System.Reactive.Concurrency; +using System.Reactive.Linq; +using Avalonia; +using DynamicData; +using DynamicData.Binding; +using PacketLogger.Models; +using PacketLogger.Models.Filters; +using PacketLogger.Models.Packets; +using PacketLogger.ViewModels.Filters; +using ReactiveUI; + +namespace PacketLogger.ViewModels.Log; + +/// +public class PacketLogViewModel : ViewModelBase, IDisposable +{ + private readonly FilterProfiles _filterProfiles; + private readonly ReadOnlyObservableCollection _packets; + private readonly IDisposable _cleanUp; + private bool _logReceived = true; + private bool _logSent = true; + + /// + /// Initializes a new instance of the class. + /// + /// The packet provider. + /// The filter profiles. + public PacketLogViewModel(IPacketProvider packetProvider, FilterProfiles filterProfiles) + { + _filterProfiles = filterProfiles; + FilterChoose = new FilterChooseViewModel(new FilterProfile(false)); + FilterChoose.CurrentProfile = filterProfiles.DefaultFilterEnabled + ? filterProfiles.DefaultProfile + : new FilterProfile(false) + { + Name = "No profile" + }; + Provider = packetProvider; + + var dynamicFilter = FilterChoose.WhenValueChanged(x => x.CurrentFilter) + .Select + ( + filter => + { + return (Func)((pi) => + { + if (filter is null) + { + return true; + } + + return filter.Match(pi); + }); + } + ); + + var packetsSubscription = Provider.Packets.Connect() + .Filter(dynamicFilter) + .Sort(new PacketComparer()) + .Bind(out _packets) + .ObserveOn(RxApp.MainThreadScheduler) + .DisposeMany() + .Subscribe + ( + _ => + { + if (Scroll) + { + RxApp.MainThreadScheduler.Schedule + ( + DateTimeOffset.Now.AddMilliseconds(100), + () => + { + if (FilteredPackets.Count > 0) + { + SelectedPacket = FilteredPackets[^1]; + } + } + ); + } + } + ); + + _cleanUp = packetsSubscription; + CopyPackets = ReactiveCommand.CreateFromObservable + ( + list => Observable.StartAsync + ( + async () => + { + var clipboardString = string.Join + ('\n', list.OfType().Select(x => x.PacketString)); + await Application.Current!.Clipboard!.SetTextAsync(clipboardString); + } + ) + ); + + TogglePane = ReactiveCommand.Create + ( + _ => PaneOpen = !PaneOpen + ); + + Clear = ReactiveCommand.Create + ( + () => Provider.Clear() + ); + } + + /// + /// Gets filter profiles. + /// + public FilterProfiles Profiles => _filterProfiles; + + /// + /// Gets the filtered packets. + /// + public ReadOnlyObservableCollection FilteredPackets => _packets; + + /// + /// Gets packet provider. + /// + public IPacketProvider Provider { get; } + + /// + /// Gets whether the pane is open. + /// + public bool PaneOpen { get; private set; } = true; + + /// + /// Gets the toggle pane command. + /// + public ReactiveCommand TogglePane { get; } + + /// + /// Gets command to copy packets. + /// + public ReactiveCommand CopyPackets { get; } + + /// + /// Gets the command for clearing. + /// + public ReactiveCommand Clear { get; } + + /// + /// Gets or sets whether to log received packets. + /// + public bool LogReceived + { + get => _logReceived; + set + { + Provider.LogReceived = value; + _logReceived = value; + } + } + + /// + /// Gets or sets whether to log sent packets. + /// + public bool LogSent + { + get => _logSent; + set + { + Provider.LogSent = value; + _logSent = value; + } + } + + /// + /// Gets or sets whether to scroll to teh bottom of the grid. + /// + public bool Scroll { get; set; } = true; + + /// + /// Gets or sets the currently selected packet. + /// + public object? SelectedPacket { get; set; } + + /// + /// Gets empty string. + /// + public string Empty { get; } = string.Empty; + + /// + /// Gets the filter choose view model. + /// + public FilterChooseViewModel FilterChoose { get; } + + /// + public void Dispose() + { + TogglePane.Dispose(); + CopyPackets.Dispose(); + Clear.Dispose(); + Provider.Dispose(); + (Provider as CommsPacketProvider)?.CustomDispose(); + _cleanUp.Dispose(); + } +} \ No newline at end of file diff --git a/src/PacketLogger/Views/Log/PacketLogView.axaml b/src/PacketLogger/Views/Log/PacketLogView.axaml new file mode 100644 index 0000000..eddb79a --- /dev/null +++ b/src/PacketLogger/Views/Log/PacketLogView.axaml @@ -0,0 +1,100 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +