A src/PacketLogger/Converters/EnumToBooleanConverter.cs => src/PacketLogger/Converters/EnumToBooleanConverter.cs +28 -0
@@ 0,0 1,28 @@
+//
+// EnumToBooleanConverter.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.Globalization;
+using Avalonia.Data;
+using Avalonia.Data.Converters;
+
+namespace PacketLogger.Converters;
+
+/// <inheritdoc />
+public class EnumToBooleanConverter : IValueConverter
+{
+ /// <inheritdoc />
+ public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
+ {
+ return value?.Equals(parameter);
+ }
+
+ /// <inheritdoc />
+ public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
+ {
+ return value?.Equals(true) == true ? parameter : BindingOperations.DoNothing;
+ }
+}<
\ No newline at end of file
M src/PacketLogger/Models/Filters/FilterProfiles.cs => src/PacketLogger/Models/Filters/FilterProfiles.cs +6 -1
@@ 29,7 29,7 @@ public class FilterProfiles
AllProfiles = new ObservableCollection<FilterProfile>();
SelectableProfiles = new ObservableCollection<FilterProfile>();
- SelectableProfiles.Add(new FilterProfile(false)
+ SelectableProfiles.Add(NoProfile = new FilterProfile(false)
{
Name = "No profile"
});
@@ 74,6 74,11 @@ public class FilterProfiles
public ObservableCollection<FilterProfile> AllProfiles { get; }
/// <summary>
+ /// Gets or sets the profile used as no profile.
+ /// </summary>
+ public FilterProfile NoProfile { get; set; }
+
+ /// <summary>
/// Add the given profile.
/// </summary>
/// <param name="profile">The profile to add.</param>
M src/PacketLogger/ViewModels/Filters/FilterChooseViewModel.cs => src/PacketLogger/ViewModels/Filters/FilterChooseViewModel.cs +60 -24
@@ 7,6 7,8 @@
using System;
using System.Collections.Generic;
using System.Reactive.Disposables;
+using System.Reflection.Metadata.Ecma335;
+using Avalonia.Controls.Notifications;
using DynamicData;
using DynamicData.Binding;
using PacketLogger.Models.Filters;
@@ 20,22 22,26 @@ namespace PacketLogger.ViewModels.Filters;
public class FilterChooseViewModel : ViewModelBase, IDisposable
{
private FilterProfile _currentProfile = null!;
+ private FilterProfile _currentRealProfile = null!;
private IDisposable? _cleanUp;
private FilterProfile _noProfile;
+ private FilterProfile _noRealProfile;
/// <summary>
/// Initializes a new instance of the <see cref="FilterChooseViewModel"/> class.
/// </summary>
/// <param name="currentProfile">The current filter profile.</param>
- public FilterChooseViewModel(FilterProfile currentProfile)
+ /// <param name="noProfile">The real no profile.</param>
+ public FilterChooseViewModel(FilterProfile currentProfile, FilterProfile noProfile)
{
- RecvFilterSelected = true;
- CurrentProfile = currentProfile;
- CurrentFilter = CreateSendRecvFilter();
_noProfile = new FilterProfile(false)
{
Name = "No profile"
};
+ _noRealProfile = noProfile;
+ RecvFilterSelected = true;
+ CurrentProfile = currentProfile;
+ CurrentFilter = CreateSendRecvFilter();
}
/// <summary>
@@ 51,10 57,25 @@ public class FilterChooseViewModel : ViewModelBase, IDisposable
return;
}
- if (value.Name == "No profile" && value != _noProfile)
+ var setCurrentProfile = value;
+ if (value.Name == "No profile")
{
- CurrentProfile = _noProfile;
- return;
+ if (value != _noProfile)
+ {
+ value = _noProfile;
+ }
+ else
+ {
+ setCurrentProfile = _noRealProfile;
+ }
+
+ CopyCurrentToNoProfile(_currentProfile);
+ }
+
+ var cleanUp = new CompositeDisposable();
+ if (RecvEntryViewModel is not null)
+ {
+ cleanUp = new CompositeDisposable(RecvEntryViewModel, SendEntryViewModel);
}
var lastProfile = value;
@@ 65,15 86,19 @@ public class FilterChooseViewModel : ViewModelBase, IDisposable
value.RecvFilterEntry,
(data) =>
{
- CopyCurrentToNoProfile(lastProfile);
- _noProfile.RecvFilterEntry.Filters.Add(data);
CurrentProfile = _noProfile;
+ _noProfile.RecvFilterEntry.Filters.Add(data);
},
(data) =>
{
- CopyCurrentToNoProfile(lastProfile);
+ CurrentProfile = _noProfile;
_noProfile.RecvFilterEntry.Filters.Remove(data);
+ },
+ (data) =>
+ {
CurrentProfile = _noProfile;
+ _noProfile.RecvFilterEntry.Active = data.Active;
+ _noProfile.RecvFilterEntry.Whitelist = data.Whitelist;
}
);
SendEntryViewModel = new FilterEntryViewModel
@@ 81,45 106,56 @@ public class FilterChooseViewModel : ViewModelBase, IDisposable
value.SendFilterEntry,
(data) =>
{
- CopyCurrentToNoProfile(lastProfile);
- _noProfile.SendFilterEntry.Filters.Add(data);
CurrentProfile = _noProfile;
+ _noProfile.SendFilterEntry.Filters.Add(data);
},
(data) =>
{
- CopyCurrentToNoProfile(lastProfile);
+ CurrentProfile = _noProfile;
_noProfile.SendFilterEntry.Filters.Remove(data);
+ },
+ (data) =>
+ {
CurrentProfile = _noProfile;
+ _noProfile.SendFilterEntry.Active = data.Active;
+ _noProfile.SendFilterEntry.Whitelist = data.Whitelist;
}
);
}
else
{
- RecvEntryViewModel = new FilterEntryViewModel(value.RecvFilterEntry, null, null);
- SendEntryViewModel = new FilterEntryViewModel(value.SendFilterEntry, null, null);
+ RecvEntryViewModel = new FilterEntryViewModel(value.RecvFilterEntry);
+ SendEntryViewModel = new FilterEntryViewModel(value.SendFilterEntry);
}
- _currentProfile = value;
- var recvWhenAny = _currentProfile.RecvFilterEntry.WhenAnyPropertyChanged("Active", "Whitelist")
+ _currentRealProfile = value;
+ _currentProfile = setCurrentProfile;
+
+ var recvWhenAny = _currentRealProfile.RecvFilterEntry.WhenAnyPropertyChanged("Active", "Whitelist")
.Subscribe((e) => OnChange());
- var sendWhenAny = _currentProfile.SendFilterEntry.WhenAnyPropertyChanged("Active", "Whitelist")
+ var sendWhenAny = _currentRealProfile.SendFilterEntry.WhenAnyPropertyChanged("Active", "Whitelist")
.Subscribe((e) => OnChange());
- var recvFilters = _currentProfile.RecvFilterEntry.Filters.ObserveCollectionChanges()
+ var recvFilters = _currentRealProfile.RecvFilterEntry.Filters.ObserveCollectionChanges()
.Subscribe((e) => OnChange());
- var sendFilters = _currentProfile.SendFilterEntry.Filters.ObserveCollectionChanges()
+ var sendFilters = _currentRealProfile.SendFilterEntry.Filters.ObserveCollectionChanges()
.Subscribe((e) => OnChange());
_cleanUp?.Dispose();
- _cleanUp = new CompositeDisposable(recvWhenAny, sendWhenAny, recvFilters, sendFilters);
+ _cleanUp = new CompositeDisposable(recvWhenAny, sendWhenAny, recvFilters, sendFilters, cleanUp);
OnChange();
}
}
- private void CopyCurrentToNoProfile(FilterProfile lastProfile)
+ private void CopyCurrentToNoProfile(FilterProfile? lastProfile)
{
+ if (lastProfile is null)
+ {
+ return;
+ }
+
_noProfile.RecvFilterEntry.Filters.Clear();
_noProfile.RecvFilterEntry.Filters.AddRange(lastProfile.RecvFilterEntry.Filters);
@@ 168,8 204,8 @@ public class FilterChooseViewModel : ViewModelBase, IDisposable
/// <returns>The created filter.</returns>
public IFilter CreateSendRecvFilter()
{
- IFilter recvFilter = CreateCompound(RecvEntryViewModel.Entry);
- IFilter sendFilter = CreateCompound(SendEntryViewModel.Entry);
+ IFilter recvFilter = CreateCompound(_currentRealProfile.RecvFilterEntry);
+ IFilter sendFilter = CreateCompound(_currentRealProfile.SendFilterEntry);
return new SendRecvFilter(sendFilter, recvFilter);
}
M src/PacketLogger/ViewModels/Filters/FilterConfigViewModel.cs => src/PacketLogger/ViewModels/Filters/FilterConfigViewModel.cs +2 -2
@@ 22,8 22,8 @@ public class FilterConfigViewModel : ViewModelBase
public FilterConfigViewModel(FilterProfile filterProfile)
{
_filterProfile = filterProfile;
- RecvEntryViewModel = new FilterEntryViewModel(filterProfile.RecvFilterEntry, null, null);
- SendEntryViewModel = new FilterEntryViewModel(filterProfile.SendFilterEntry, null, null);
+ RecvEntryViewModel = new FilterEntryViewModel(filterProfile.RecvFilterEntry);
+ SendEntryViewModel = new FilterEntryViewModel(filterProfile.SendFilterEntry);
}
/// <summary>
M src/PacketLogger/ViewModels/Filters/FilterEntryViewModel.cs => src/PacketLogger/ViewModels/Filters/FilterEntryViewModel.cs +72 -2
@@ 6,6 6,7 @@
using System;
using System.Reactive;
+using System.Reactive.Disposables;
using PacketLogger.Models.Filters;
using ReactiveUI;
@@ 14,17 15,27 @@ namespace PacketLogger.ViewModels.Filters;
/// <summary>
/// A view model for FilterEntryView.
/// </summary>
-public class FilterEntryViewModel : ViewModelBase
+public class FilterEntryViewModel : ViewModelBase, IDisposable
{
+ private readonly Action<(bool Active, bool Whitelist)>? _whitelistActiveChanged;
+ private readonly IDisposable _cleanUp;
+
/// <summary>
/// Initializes a new instance of the <see cref="FilterEntryViewModel"/> class.
/// </summary>
/// <param name="entry">The profile entry.</param>
/// <param name="addNew">The action called upon adding new.</param>
/// <param name="remove">The action called upon removing the given filter data.</param>
+ /// <param name="whitelistActiveChanged">Called when whitelist or active has been changed.</param>
public FilterEntryViewModel
- (FilterProfileEntry entry, Action<FilterCreator.FilterData>? addNew, Action<FilterCreator.FilterData>? remove)
+ (
+ FilterProfileEntry entry,
+ Action<FilterCreator.FilterData>? addNew = default,
+ Action<FilterCreator.FilterData>? remove = default,
+ Action<(bool Active, bool Whitelist)>? whitelistActiveChanged = default
+ )
{
+ _whitelistActiveChanged = whitelistActiveChanged;
NewFilterType = FilterCreator.FilterType.PacketHeader;
Entry = entry;
RemoveCurrent = ReactiveCommand.Create
@@ 77,6 88,52 @@ public class FilterEntryViewModel : ViewModelBase
}
}
);
+
+ var whitelistChanged = Entry.WhenAnyValue(x => x.Whitelist)
+ .Subscribe(_ => this.RaisePropertyChanged("Whitelist"));
+
+ var activeChanged = Entry.WhenAnyValue(x => x.Active)
+ .Subscribe(_ => this.RaisePropertyChanged("Active"));
+
+ _cleanUp = new CompositeDisposable(whitelistChanged, activeChanged);
+ }
+
+ /// <summary>
+ /// Gets or sets whether whitelist or blacklist.
+ /// </summary>
+ public bool Whitelist
+ {
+ get => Entry.Whitelist;
+ set
+ {
+ if (_whitelistActiveChanged is not null)
+ {
+ _whitelistActiveChanged((Entry.Active, value));
+ }
+ else
+ {
+ Entry.Whitelist = value;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets whether the filter entry is active.
+ /// </summary>
+ public bool Active
+ {
+ get => Entry.Active;
+ set
+ {
+ if (_whitelistActiveChanged is not null)
+ {
+ _whitelistActiveChanged((value, Entry.Whitelist));
+ }
+ else
+ {
+ Entry.Active = value;
+ }
+ }
}
/// <summary>
@@ 108,4 165,17 @@ public class FilterEntryViewModel : ViewModelBase
/// Gets the command to add new filter.
/// </summary>
public ReactiveCommand<Unit, Unit> AddNew { get; }
+
+ /// <summary>
+ /// Gets radio group.
+ /// </summary>
+ public string RadioGroup { get; } = Guid.NewGuid().ToString();
+
+ /// <inheritdoc />
+ public void Dispose()
+ {
+ _cleanUp.Dispose();
+ RemoveCurrent.Dispose();
+ AddNew.Dispose();
+ }
}=
\ No newline at end of file
M src/PacketLogger/ViewModels/Log/PacketLogViewModel.cs => src/PacketLogger/ViewModels/Log/PacketLogViewModel.cs +7 -7
@@ 40,13 40,13 @@ public class PacketLogViewModel : ViewModelBase, IDisposable
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"
- };
+ FilterChoose = new FilterChooseViewModel
+ (
+ filterProfiles.DefaultFilterEnabled
+ ? filterProfiles.DefaultProfile
+ : filterProfiles.NoProfile,
+ filterProfiles.SelectableProfiles.Last()
+ );
Provider = packetProvider;
var dynamicFilter = FilterChoose.WhenValueChanged(x => x.CurrentFilter)
M src/PacketLogger/ViewModels/MainWindowViewModel.cs => src/PacketLogger/ViewModels/MainWindowViewModel.cs +1 -1
@@ 35,7 35,7 @@ using ReactiveUI;
namespace PacketLogger.ViewModels;
/// <inheritdoc />
-public class MainWindowViewModel : ViewModelBase, INotifyPropertyChanged
+public class MainWindowViewModel : ViewModelBase
{
private readonly DockFactory _factory;
private readonly NostaleProcesses _processes;
M src/PacketLogger/Views/Filters/FilterEntryView.axaml => src/PacketLogger/Views/Filters/FilterEntryView.axaml +21 -6
@@ 3,16 3,26 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:filters="clr-namespace:PacketLogger.ViewModels.Filters"
+ xmlns:converters="clr-namespace:PacketLogger.Converters"
+ xmlns:system="clr-namespace:System;assembly=System.Runtime"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="PacketLogger.Views.Filters.FilterEntryView">
<Design.DataContext>
<filters:FilterEntryViewModel />
</Design.DataContext>
+ <UserControl.Resources>
+ <converters:EnumToBooleanConverter x:Key="EnumToBooleanConverter" />
+ <system:Boolean x:Key="True">True</system:Boolean>
+ <system:Boolean x:Key="False">False</system:Boolean>
+ </UserControl.Resources>
<Grid ColumnDefinitions="*,*" RowDefinitions="*, 40, 40, 40">
<DataGrid Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" Items="{Binding Entry.Filters}" IsReadOnly="True"
SelectedItem="{Binding SelectedFilter}"
CanUserReorderColumns="True" CanUserSortColumns="True" CanUserResizeColumns="True">
+ <DataGrid.KeyBindings>
+ <KeyBinding Gesture="Delete" Command="{Binding RemoveCurrent}" />
+ </DataGrid.KeyBindings>
<DataGrid.Styles>
<Style Selector="DataGridColumnHeader">
<Setter Property="MinHeight" Value="24" />
@@ 23,16 33,21 @@
<DataGridTextColumn Header="Value" Binding="{Binding Value}" Width="*" MinWidth="100" />
</DataGrid.Columns>
</DataGrid>
-
- <CheckBox Grid.Row="1" Grid.Column="0" Content="Filter active" IsChecked="{Binding Entry.Active}" />
+
+ <CheckBox Grid.Row="1" Grid.Column="0" Content="Filter active" IsChecked="{Binding Active}" />
<Grid Grid.Row="1" Grid.Column="1" ColumnDefinitions="*,*">
- <RadioButton Grid.Column="0" Content="Wl" GroupName="WlBl"></RadioButton>
- <RadioButton Grid.Column="1" Content="Bl" GroupName="WlBl" IsChecked="{Binding !Entry.Whitelist}"></RadioButton>
+ <RadioButton Grid.Column="0" Content="Wl" GroupName="{Binding RadioGroup}"
+ IsChecked="{Binding Whitelist, Converter={StaticResource EnumToBooleanConverter}, ConverterParameter={StaticResource True}}" />
+ <RadioButton Grid.Column="1" Content="Bl" GroupName="{Binding RadioGroup}"
+ IsChecked="{Binding Whitelist, Converter={StaticResource EnumToBooleanConverter}, ConverterParameter={StaticResource False}}" />
</Grid>
<TextBox VerticalAlignment="Center" Margin="0, 0, 5, 0" Height="30" Grid.Row="2" Grid.Column="0"
Text="{Binding NewFilter}">
+ <TextBox.KeyBindings>
+ <KeyBinding Gesture="Enter" Command="{Binding AddNew}" />
+ </TextBox.KeyBindings>
</TextBox>
<ComboBox x:Name="FilterType" VerticalAlignment="Center" HorizontalAlignment="Stretch" Margin="5, 0, 0, 0"
Height="32" Grid.Row="2"
@@ 41,6 56,6 @@
<Button HorizontalAlignment="Stretch" HorizontalContentAlignment="Center" Margin="0, 0, 5, 0" Grid.Row="3"
Grid.Column="0" Content="Remove" Command="{Binding RemoveCurrent}" />
<Button HorizontalAlignment="Stretch" HorizontalContentAlignment="Center" Margin="5, 0, 0,0" Grid.Row="3"
- Grid.Column="1" Content="Add" Command="{Binding AddNew}" IsDefault="True" />
+ Grid.Column="1" Content="Add" Command="{Binding AddNew}" />
</Grid>
-</UserControl>
+</UserControl><
\ No newline at end of file