M src/PacketLogger/ViewModels/DockFactory.cs => src/PacketLogger/ViewModels/DockFactory.cs +52 -14
@@ 6,6 6,7 @@
using System;
using System.Collections.Generic;
+using System.Reactive;
using Dock.Avalonia.Controls;
using Dock.Model.Controls;
using Dock.Model.Core;
@@ 27,6 28,57 @@ public class DockFactory : Factory, IDisposable
private readonly NostaleProcesses _processes = new();
private readonly CommsInjector _injector;
+ private IRootDock? _rootDock;
+ private IDocumentDock? _documentDock;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DockFactory"/> class.
+ /// </summary>
+ /// <param name="injector">The communications injector.</param>
+ /// <param name="repository">The repository.</param>
+ public DockFactory(CommsInjector injector, StatefulRepository repository)
+ {
+ _repository = repository;
+ _injector = injector;
+ }
+
+ /// <summary>
+ /// Gets the document dock.
+ /// </summary>
+ public IDocumentDock DocumentDock
+ => _documentDock ?? throw new InvalidOperationException("Dock factory is not initialized");
+
+ /// <summary>
+ /// Creaate and load a document.
+ /// </summary>
+ /// <param name="load">The function to use to load the document.</param>
+ public void CreateLoadedDocument(Func<PacketLogDocumentViewModel, IObservable<Unit>> load)
+ {
+ if (_documentDock is null)
+ {
+ return;
+ }
+
+ var document = new PacketLogDocumentViewModel(_injector, _repository, _processes)
+ { Id = $"New tab", Title = $"New tab" };
+
+ var observable = load(document);
+ observable.Subscribe
+ (
+ x =>
+ {
+ if (!document.Loaded)
+ {
+ return;
+ }
+
+ AddDockable(_documentDock, document);
+ SetActiveDockable(document);
+ SetFocusedDockable(_documentDock, document);
+ }
+ );
+ }
+
/// <inheritdoc />
public override IDocumentDock CreateDocumentDock()
{
@@ 52,20 104,6 @@ public class DockFactory : Factory, IDisposable
return documentDock;
}
- private IRootDock? _rootDock;
- private IDocumentDock? _documentDock;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="DockFactory"/> class.
- /// </summary>
- /// <param name="injector">The communications injector.</param>
- /// <param name="repository">The repository.</param>
- public DockFactory(CommsInjector injector, StatefulRepository repository)
- {
- _repository = repository;
- _injector = injector;
- }
-
/// <inheritdoc />
public override IRootDock CreateLayout()
{
M src/PacketLogger/ViewModels/MainWindowViewModel.cs => src/PacketLogger/ViewModels/MainWindowViewModel.cs +123 -5
@@ 7,23 7,34 @@
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
+using System.IO;
+using System.Linq;
+using System.Reactive;
+using System.Reactive.Linq;
+using System.Reflection;
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Controls.ApplicationLifetimes;
using Dock.Model.Controls;
using Dock.Model.Core;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using NosSmooth.Comms.Local.Extensions;
using NosSmooth.Core.Extensions;
+using NosSmooth.PacketSerializer.Abstractions.Attributes;
using NosSmooth.PacketSerializer.Extensions;
using NosSmooth.PacketSerializer.Packets;
using PacketLogger.Models;
using PacketLogger.Models.Packets;
+using PacketLogger.Views;
+using ReactiveUI;
namespace PacketLogger.ViewModels;
/// <inheritdoc />
public class MainWindowViewModel : ViewModelBase, INotifyPropertyChanged
{
- private readonly IFactory? _factory;
+ private readonly DockFactory _factory;
/// <summary>
/// Initializes a new instance of the <see cref="MainWindowViewModel"/> class.
@@ 41,6 52,7 @@ public class MainWindowViewModel : ViewModelBase, INotifyPropertyChanged
.BuildServiceProvider();
var packetTypes = services.GetRequiredService<IPacketTypesRepository>();
+
var result = packetTypes.AddDefaultPackets();
if (!result.IsSuccess)
{
@@ 48,20 60,126 @@ public class MainWindowViewModel : ViewModelBase, INotifyPropertyChanged
}
_factory = services.GetRequiredService<DockFactory>();
-
- Layout = _factory?.CreateLayout();
+ Layout = _factory.CreateLayout();
if (Layout is { })
{
- _factory?.InitLayout(Layout);
+ _factory.InitLayout(Layout);
if (Layout is { } root)
{
root.Navigate.Execute("Home");
}
}
+
+ SaveAll = ReactiveCommand.CreateFromTask
+ (
+ async () =>
+ {
+ if (Layout?.FocusedDockable is PacketLogDocumentViewModel activeDocument && activeDocument.Loaded
+ && activeDocument.LogViewModel is not null)
+ {
+ var mainWindow = (App.Current!.ApplicationLifetime as IClassicDesktopStyleApplicationLifetime)
+ ?.MainWindow;
+ var result = await new SaveFileDialog()
+ {
+ DefaultExtension = ".log",
+ InitialFileName = Assembly.GetEntryAssembly()?.GetModules().FirstOrDefault()?.FullyQualifiedName
+ }.ShowAsync(mainWindow!);
+
+ if (result is null)
+ {
+ return;
+ }
+
+ using var file = File.OpenWrite(result);
+ using var streamWriter = new StreamWriter(file);
+ foreach (var packet in activeDocument.LogViewModel.Provider.Packets.Items)
+ {
+ await streamWriter.WriteLineAsync
+ (
+ $"[{packet.Date:HH:mm:ss}]\t[{(packet.Source == PacketSource.Server ? "Recv" : "Send")}]\t{packet.PacketString.Trim()}"
+ );
+ }
+ }
+ }
+ );
+
+ SaveFiltered = ReactiveCommand.CreateFromTask
+ (
+ async () =>
+ {
+ if (Layout?.FocusedDockable is PacketLogDocumentViewModel activeDocument && activeDocument.Loaded
+ && activeDocument.LogViewModel is not null)
+ {
+ var mainWindow = (App.Current!.ApplicationLifetime as IClassicDesktopStyleApplicationLifetime)
+ ?.MainWindow;
+ var result = await new SaveFileDialog()
+ {
+ DefaultExtension = ".log",
+ }.ShowAsync(mainWindow!);
+
+ if (result is null)
+ {
+ return;
+ }
+
+ using var file = File.OpenWrite(result);
+ using var streamWriter = new StreamWriter(file);
+ foreach (var packet in activeDocument.LogViewModel.FilteredPackets)
+ {
+ await streamWriter.WriteLineAsync
+ (
+ $"[{packet.Date:HH:mm:ss}]\t[{(packet.Source == PacketSource.Server ? "Recv" : "Send")}]\t{packet.PacketString.Trim()}"
+ );
+ }
+ }
+ }
+ );
+
+ OpenFile = ReactiveCommand.Create
+ (() => _factory.CreateLoadedDocument(doc => doc.OpenFile.Execute(Unit.Default)));
+
+ OpenEmpty = ReactiveCommand.Create
+ (() => _factory.CreateLoadedDocument(doc => doc.OpenDummy.Execute(Unit.Default)));
+
+ NewTab = ReactiveCommand.Create
+ (() => _factory.DocumentDock.CreateDocument?.Execute(null));
+
+ QuitApplication = ReactiveCommand.Create
+ (() => (Application.Current?.ApplicationLifetime as IControlledApplicationLifetime)?.Shutdown());
}
/// <summary>
/// Gets or sets the layout.
/// </summary>
public IRootDock? Layout { get; set; }
-}
+
+ /// <summary>
+ /// Gets a command that quits the application.
+ /// </summary>
+ public ReactiveCommand<Unit, Unit> QuitApplication { get; }
+
+ /// <summary>
+ /// Gets a command that saves all packets.
+ /// </summary>
+ public ReactiveCommand<Unit, Unit> SaveAll { get; }
+
+ /// <summary>
+ /// Gets a command that saves filtered packets.
+ /// </summary>
+ public ReactiveCommand<Unit, Unit> SaveFiltered { get; }
+
+ /// <summary>
+ /// Gets the comamnd that opens a file.
+ /// </summary>
+ public ReactiveCommand<Unit, Unit> OpenFile { get; }
+
+ /// <summary>
+ /// Gets the command that opens empty logger.
+ /// </summary>
+ public ReactiveCommand<Unit, Unit> OpenEmpty { get; }
+
+ /// <summary>
+ /// Gets the command that opens a new tab.
+ /// </summary>
+ public ReactiveCommand<Unit, Unit> NewTab { get; }
+}<
\ No newline at end of file
M src/PacketLogger/Views/MainWindow.axaml => src/PacketLogger/Views/MainWindow.axaml +8 -8
@@ 21,18 21,18 @@
<Grid RowDefinitions="Auto,*" ColumnDefinitions="Auto, *">
<Menu Grid.Row="0" Grid.Column="0">
<MenuItem Header="_File">
- <MenuItem Header="_Open File..." />
+ <MenuItem Header="_New Tab" Command="{Binding NewTab}"></MenuItem>
+ <MenuItem Header="_Open Empty" Command="{Binding OpenEmpty}" />
+ <MenuItem Header="_Open File..." Command="{Binding OpenFile}" />
<Separator />
- <MenuItem Header="_Save Filtered As..." />
- <MenuItem Header="Save All As..." />
+ <MenuItem Header="_Save Filtered As..." Command="{Binding SaveFiltered}" />
+ <MenuItem Header="Save All As..." Command="{Binding SaveAll}" />
<Separator />
- <MenuItem Header="Exit" />
+ <MenuItem Header="Exit" Command="{Binding QuitApplication}" />
</MenuItem>
<MenuItem Header="_Tools">
- <MenuItem Header="_Packet Sender">
- <MenuItem Header="_Test" />
- </MenuItem>
- <MenuItem Header="_Packet Analyzer"></MenuItem>
+ <MenuItem Header="_Packet Sender"/>
+ <MenuItem Header="_Packet Analyzer"/>
</MenuItem>
</Menu>