Move opening image code from Eto to Avalonia.

This commit is contained in:
2020-04-11 21:04:20 +01:00
parent 95f30ce5a5
commit 4696645a01
8 changed files with 602 additions and 253 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,11 @@
using Aaru.CommonTypes.Interfaces;
namespace Aaru.Gui.Models
{
public class FileSystemModel : RootModel
{
public string VolumeName { get; set; }
public IFilesystem Filesystem { get; set; }
public IReadOnlyFilesystem ReadOnlyFilesystem { get; set; }
}
}

View File

@@ -1,4 +1,17 @@
using System.Collections.ObjectModel;
using Aaru.CommonTypes.Interfaces;
using Avalonia.Media.Imaging;
namespace Aaru.Gui.Models namespace Aaru.Gui.Models
{ {
public class ImageModel {} public class ImageModel
{
public ImageModel() => PartitionSchemesOrFileSystems = new ObservableCollection<RootModel>();
public string Path { get; set; }
public string FileName { get; set; }
public Bitmap Icon { get; set; }
public ObservableCollection<RootModel> PartitionSchemesOrFileSystems { get; }
public IMediaImage Image { get; set; }
}
} }

View File

@@ -0,0 +1,16 @@
using System.Collections.ObjectModel;
using Aaru.CommonTypes;
using Avalonia.Media.Imaging;
namespace Aaru.Gui.Models
{
public class PartitionModel
{
public PartitionModel() => FileSystems = new ObservableCollection<FileSystemModel>();
public string Name { get; set; }
public Bitmap Icon { get; set; }
public ObservableCollection<FileSystemModel> FileSystems { get; }
public Partition Partition { get; set; }
}
}

View File

@@ -0,0 +1,13 @@
using System.Collections.ObjectModel;
using Avalonia.Media.Imaging;
namespace Aaru.Gui.Models
{
public class PartitionSchemeModel : RootModel
{
public PartitionSchemeModel() => Partitions = new ObservableCollection<PartitionModel>();
public Bitmap Icon { get; set; }
public ObservableCollection<PartitionModel> Partitions { get; }
}
}

View File

@@ -1,21 +1,39 @@
using System.Collections.ObjectModel; using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq; using System.Linq;
using System.Reactive; using System.Reactive;
using Aaru.CommonTypes;
using Aaru.CommonTypes.Enums;
using Aaru.CommonTypes.Interfaces;
using Aaru.CommonTypes.Interop; using Aaru.CommonTypes.Interop;
using Aaru.CommonTypes.Structs;
using Aaru.Console;
using Aaru.Core;
using Aaru.Database; using Aaru.Database;
using Aaru.Gui.Models; using Aaru.Gui.Models;
using Aaru.Gui.Views; using Aaru.Gui.Views;
using Avalonia; using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Media.Imaging;
using Avalonia.Platform;
using MessageBox.Avalonia; using MessageBox.Avalonia;
using MessageBox.Avalonia.Enums;
using ReactiveUI; using ReactiveUI;
using PlatformID = Aaru.CommonTypes.Interop.PlatformID;
namespace Aaru.Gui.ViewModels namespace Aaru.Gui.ViewModels
{ {
public class MainWindowViewModel : ViewModelBase public class MainWindowViewModel : ViewModelBase
{ {
readonly IAssetLoader _assets;
readonly DevicesRootModel _devicesRoot; readonly DevicesRootModel _devicesRoot;
readonly Bitmap _genericFolderIcon;
readonly Bitmap _genericHddIcon;
readonly Bitmap _genericOpticalIcon;
readonly Bitmap _genericTapeIcon;
readonly ImagesRootModel _imagesRoot; readonly ImagesRootModel _imagesRoot;
readonly MainWindow _view; readonly MainWindow _view;
ConsoleWindow _consoleWindow; ConsoleWindow _consoleWindow;
@@ -30,8 +48,10 @@ namespace Aaru.Gui.ViewModels
ExitCommand = ReactiveCommand.Create(ExecuteExitCommand); ExitCommand = ReactiveCommand.Create(ExecuteExitCommand);
SettingsCommand = ReactiveCommand.Create(ExecuteSettingsCommand); SettingsCommand = ReactiveCommand.Create(ExecuteSettingsCommand);
ConsoleCommand = ReactiveCommand.Create(ExecuteConsoleCommand); ConsoleCommand = ReactiveCommand.Create(ExecuteConsoleCommand);
OpenCommand = ReactiveCommand.Create(ExecuteOpenCommand);
_view = view; _view = view;
TreeRoot = new ObservableCollection<RootModel>(); TreeRoot = new ObservableCollection<RootModel>();
_assets = AvaloniaLocator.Current.GetService<IAssetLoader>();
_imagesRoot = new ImagesRootModel _imagesRoot = new ImagesRootModel
{ {
@@ -55,6 +75,18 @@ namespace Aaru.Gui.ViewModels
break; break;
} }
_genericHddIcon =
new Bitmap(_assets.Open(new Uri("avares://Aaru.Gui/Assets/Icons/oxygen/32x32/drive-harddisk.png")));
_genericOpticalIcon =
new Bitmap(_assets.Open(new Uri("avares://Aaru.Gui/Assets/Icons/oxygen/32x32/drive-optical.png")));
_genericTapeIcon =
new Bitmap(_assets.Open(new Uri("avares://Aaru.Gui/Assets/Icons/oxygen/32x32/media-tape.png")));
_genericFolderIcon =
new Bitmap(_assets.Open(new Uri("avares://Aaru.Gui/Assets/Icons/oxygen/32x32/inode-directory.png")));
} }
public bool DevicesSupported public bool DevicesSupported
@@ -76,6 +108,7 @@ namespace Aaru.Gui.ViewModels
public ReactiveCommand<Unit, Unit> StatisticsCommand { get; } public ReactiveCommand<Unit, Unit> StatisticsCommand { get; }
public ReactiveCommand<Unit, Unit> ExitCommand { get; } public ReactiveCommand<Unit, Unit> ExitCommand { get; }
public ReactiveCommand<Unit, Unit> SettingsCommand { get; } public ReactiveCommand<Unit, Unit> SettingsCommand { get; }
public ReactiveCommand<Unit, Unit> OpenCommand { get; }
internal void ExecuteAboutCommand() internal void ExecuteAboutCommand()
{ {
@@ -140,5 +173,239 @@ namespace Aaru.Gui.ViewModels
_consoleWindow.Show(); _consoleWindow.Show();
} }
async void ExecuteOpenCommand()
{
// TODO: Extensions
var dlgOpenImage = new OpenFileDialog
{
Title = "Choose image to open", AllowMultiple = false
};
string[] result = await dlgOpenImage.ShowAsync(_view);
if(result?.Length != 1)
return;
var filtersList = new FiltersList();
IFilter inputFilter = filtersList.GetFilter(result[0]);
if(inputFilter == null)
{
MessageBoxManager.GetMessageBoxStandardWindow("Error", "Cannot open specified file.", ButtonEnum.Ok,
Icon.Error);
return;
}
try
{
IMediaImage imageFormat = ImageFormat.Detect(inputFilter);
if(imageFormat == null)
{
MessageBoxManager.GetMessageBoxStandardWindow("Error", "Image format not identified.",
ButtonEnum.Ok, Icon.Error);
return;
}
AaruConsole.WriteLine("Image format identified by {0} ({1}).", imageFormat.Name, imageFormat.Id);
try
{
if(!imageFormat.Open(inputFilter))
{
MessageBoxManager.GetMessageBoxStandardWindow("Error", "Unable to open image format.",
ButtonEnum.Ok, Icon.Error);
AaruConsole.ErrorWriteLine("Unable to open image format");
AaruConsole.ErrorWriteLine("No error given");
return;
}
var mediaResource =
new Uri($"avares://Aaru.Gui/Assets/Logos/Media/{imageFormat.Info.MediaType}.png");
var imageModel = new ImageModel
{
Path = result[0], Icon = _assets.Exists(mediaResource)
? new Bitmap(_assets.Open(mediaResource))
: imageFormat.Info.XmlMediaType == XmlMediaType.BlockMedia
? _genericHddIcon
: imageFormat.Info.XmlMediaType == XmlMediaType.OpticalDisc
? _genericOpticalIcon
: _genericFolderIcon,
FileName = Path.GetFileName(result[0]), Image = imageFormat
};
// TODO: pnlImageInfo
List<Partition> partitions = Core.Partitions.GetAll(imageFormat);
Core.Partitions.AddSchemesToStats(partitions);
bool checkraw = false;
List<string> idPlugins;
IFilesystem plugin;
PluginBase plugins = GetPluginBase.Instance;
if(partitions.Count == 0)
{
AaruConsole.DebugWriteLine("Analyze command", "No partitions found");
checkraw = true;
}
else
{
AaruConsole.WriteLine("{0} partitions found.", partitions.Count);
foreach(string scheme in partitions.Select(p => p.Scheme).Distinct().OrderBy(s => s))
{
// TODO: Add icons to partition schemes
var schemeModel = new PartitionSchemeModel
{
Name = scheme
};
foreach(Partition partition in partitions.
Where(p => p.Scheme == scheme).OrderBy(p => p.Start))
{
// TODO: pnlPartition
var partitionModel = new PartitionModel
{
// TODO: Add icons to partition types
Name = $"{partition.Name} ({partition.Type})", Partition = partition
};
AaruConsole.WriteLine("Identifying filesystem on partition");
Core.Filesystems.Identify(imageFormat, out idPlugins, partition);
if(idPlugins.Count == 0)
AaruConsole.WriteLine("Filesystem not identified");
else
{
AaruConsole.WriteLine($"Identified by {idPlugins.Count} plugins");
foreach(string pluginName in idPlugins)
if(plugins.PluginsList.TryGetValue(pluginName, out plugin))
{
plugin.GetInformation(imageFormat, partition, out string information, null);
var fsPlugin = plugin as IReadOnlyFilesystem;
if(fsPlugin != null)
{
Errno error =
fsPlugin.Mount(imageFormat, partition, null,
new Dictionary<string, string>(), null);
if(error != Errno.NoError)
fsPlugin = null;
}
var filesystemModel = new FileSystemModel
{
VolumeName =
plugin.XmlFsType.VolumeName is null ? $"{plugin.XmlFsType.Type}"
: $"{plugin.XmlFsType.VolumeName} ({plugin.XmlFsType.Type})",
Filesystem = plugin, ReadOnlyFilesystem = fsPlugin
};
/* TODO: Trap expanding item
if(fsPlugin != null)
Statistics.AddCommand("ls");
*/
Statistics.AddFilesystem(plugin.XmlFsType.Type);
partitionModel.FileSystems.Add(filesystemModel);
}
}
schemeModel.Partitions.Add(partitionModel);
}
imageModel.PartitionSchemesOrFileSystems.Add(schemeModel);
}
}
if(checkraw)
{
var wholePart = new Partition
{
Name = "Whole device", Length = imageFormat.Info.Sectors,
Size = imageFormat.Info.Sectors * imageFormat.Info.SectorSize
};
Core.Filesystems.Identify(imageFormat, out idPlugins, wholePart);
if(idPlugins.Count == 0)
AaruConsole.WriteLine("Filesystem not identified");
else
{
AaruConsole.WriteLine($"Identified by {idPlugins.Count} plugins");
foreach(string pluginName in idPlugins)
if(plugins.PluginsList.TryGetValue(pluginName, out plugin))
{
plugin.GetInformation(imageFormat, wholePart, out string information, null);
var fsPlugin = plugin as IReadOnlyFilesystem;
if(fsPlugin != null)
{
Errno error = fsPlugin.Mount(imageFormat, wholePart, null,
new Dictionary<string, string>(), null);
if(error != Errno.NoError)
fsPlugin = null;
}
var filesystemModel = new FileSystemModel
{
VolumeName = plugin.XmlFsType.VolumeName is null ? $"{plugin.XmlFsType.Type}"
: $"{plugin.XmlFsType.VolumeName} ({plugin.XmlFsType.Type})",
Filesystem = plugin, ReadOnlyFilesystem = fsPlugin
};
/* TODO: Trap expanding item
if(fsPlugin != null)
Statistics.AddCommand("ls");
*/
Statistics.AddFilesystem(plugin.XmlFsType.Type);
imageModel.PartitionSchemesOrFileSystems.Add(filesystemModel);
}
}
}
Statistics.AddMediaFormat(imageFormat.Format);
Statistics.AddMedia(imageFormat.Info.MediaType, false);
Statistics.AddFilter(inputFilter.Name);
_imagesRoot.Images.Add(imageModel);
}
catch(Exception ex)
{
MessageBoxManager.GetMessageBoxStandardWindow("Error", "Unable to open image format.",
ButtonEnum.Ok, Icon.Error);
AaruConsole.ErrorWriteLine("Unable to open image format");
AaruConsole.ErrorWriteLine("Error: {0}", ex.Message);
AaruConsole.DebugWriteLine("Image-info command", "Stack trace: {0}", ex.StackTrace);
}
}
catch(Exception ex)
{
MessageBoxManager.GetMessageBoxStandardWindow("Error", "Exception reading file.", ButtonEnum.Ok,
Icon.Error);
AaruConsole.ErrorWriteLine($"Error reading file: {ex.Message}");
AaruConsole.DebugWriteLine("Image-info command", ex.StackTrace);
}
Statistics.AddCommand("image-info");
}
} }
} }

View File

@@ -10,7 +10,7 @@
<DockPanel> <DockPanel>
<Menu DockPanel.Dock="Top"> <Menu DockPanel.Dock="Top">
<MenuItem Header="_File"> <MenuItem Header="_File">
<MenuItem Header="_Open" /> <Separator /> <MenuItem Header="_Open" Command="{Binding OpenCommand}" /> <Separator />
<MenuItem Header="_Settings" IsVisible="{Binding !NativeMenuSupported}" <MenuItem Header="_Settings" IsVisible="{Binding !NativeMenuSupported}"
Command="{Binding SettingsCommand}" /> Command="{Binding SettingsCommand}" />
<Separator /> <Separator />
@@ -49,6 +49,31 @@
<TextBlock Text="{Binding Name}" /> <TextBlock Text="{Binding Name}" />
</StackPanel> </StackPanel>
</TreeDataTemplate> </TreeDataTemplate>
<TreeDataTemplate DataType="models:ImageModel"
ItemsSource="{Binding PartitionSchemesOrFileSystems}">
<StackPanel Orientation="Horizontal">
<Image Width="24" Height="24" Source="{Binding Icon}" />
<TextBlock Text="{Binding FileName}" />
</StackPanel>
</TreeDataTemplate>
<TreeDataTemplate DataType="models:PartitionSchemeModel" ItemsSource="{Binding Partitions}">
<StackPanel Orientation="Horizontal">
<Image Width="24" Height="24" Source="{Binding Icon}" />
<TextBlock Text="{Binding Name}" />
</StackPanel>
</TreeDataTemplate>
<TreeDataTemplate DataType="models:PartitionModel" ItemsSource="{Binding FileSystems}">
<StackPanel Orientation="Horizontal">
<Image Width="24" Height="24" Source="{Binding Icon}" />
<TextBlock Text="{Binding Name}" />
</StackPanel>
</TreeDataTemplate>
<TreeDataTemplate DataType="models:FileSystemModel" ItemsSource="{Binding Subdirectories}">
<StackPanel Orientation="Horizontal">
<Image Width="24" Height="24" Source="{Binding Icon}" />
<TextBlock Text="{Binding VolumeName}" />
</StackPanel>
</TreeDataTemplate>
</TreeView.DataTemplates> </TreeView.DataTemplates>
</TreeView> </TreeView>
<GridSplitter Grid.Column="1" Width="5" HorizontalAlignment="Stretch" /> <GridSplitter Grid.Column="1" Width="5" HorizontalAlignment="Stretch" />

View File

@@ -36,7 +36,6 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Text; using System.Text;
using Aaru.Gui;
using Aaru.Commands; using Aaru.Commands;
using Aaru.Commands.Device; using Aaru.Commands.Device;
using Aaru.Commands.Filesystem; using Aaru.Commands.Filesystem;
@@ -45,11 +44,13 @@ using Aaru.Commands.Media;
using Aaru.Console; using Aaru.Console;
using Aaru.Core; using Aaru.Core;
using Aaru.Database; using Aaru.Database;
using Aaru.Gui;
using Aaru.Settings; using Aaru.Settings;
using Microsoft.EntityFrameworkCore;
using Avalonia; using Avalonia;
using Avalonia.Dialogs;
using Avalonia.Logging.Serilog; using Avalonia.Logging.Serilog;
using Avalonia.ReactiveUI; using Avalonia.ReactiveUI;
using Microsoft.EntityFrameworkCore;
namespace Aaru namespace Aaru
{ {
@@ -74,8 +75,7 @@ namespace Aaru
if(args.Length == 1 && if(args.Length == 1 &&
args[0].ToLowerInvariant() == "gui") args[0].ToLowerInvariant() == "gui")
{ {
return BuildAvaloniaApp() return BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
.StartWithClassicDesktopLifetime(args);
} }
AaruConsole.WriteLineEvent += System.Console.WriteLine; AaruConsole.WriteLineEvent += System.Console.WriteLine;
@@ -169,10 +169,8 @@ namespace Aaru
} }
// Avalonia configuration, don't remove; also used by visual designer. // Avalonia configuration, don't remove; also used by visual designer.
public static AppBuilder BuildAvaloniaApp() public static AppBuilder BuildAvaloniaApp() => AppBuilder.
=> AppBuilder.Configure<App>() Configure<App>().UsePlatformDetect().LogToDebug().
.UsePlatformDetect() UseReactiveUI().UseManagedSystemDialogs();
.LogToDebug()
.UseReactiveUI();
} }
} }