Implement listing devices in main window.

This commit is contained in:
2020-04-14 22:32:47 +01:00
parent 60a4ddee64
commit a4953807eb
5 changed files with 178 additions and 263 deletions

View File

@@ -31,14 +31,8 @@
// ****************************************************************************/
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using Aaru.CommonTypes.Enums;
using Aaru.CommonTypes.Interfaces;
using Aaru.CommonTypes.Structs;
using Aaru.CommonTypes.Structs.Devices.SCSI;
using Aaru.Console;
using Aaru.Core;
using Aaru.Core.Media.Info;
@@ -47,7 +41,7 @@ using Aaru.Gui.Panels;
using Eto.Drawing;
using Eto.Forms;
using Eto.Serialization.Xaml;
using FileAttributes = Aaru.CommonTypes.Structs.FileAttributes;
using DeviceInfo = Aaru.Core.Devices.Info.DeviceInfo;
namespace Aaru.Gui.Forms
{
@@ -180,7 +174,7 @@ namespace Aaru.Gui.Forms
Text = "Refresh devices"
};
menuItem.Click += OnDeviceRefresh;
// menuItem.Click += OnDeviceRefresh;
treeImagesMenu.Items.Add(menuItem);
if(!(treeImages.SelectedItem is TreeGridItem selectedItem))
@@ -203,106 +197,6 @@ namespace Aaru.Gui.Forms
Application.Instance.Quit();
}
protected void OnDeviceRefresh(object sender, EventArgs e) => RefreshDevices();
protected override void OnLoadComplete(EventArgs e)
{
base.OnLoadComplete(e);
RefreshDevices();
}
void RefreshDevices()
{
try
{
AaruConsole.WriteLine("Refreshing devices");
devicesRoot.Children.Clear();
foreach(DeviceInfo device in Device.ListDevices().Where(d => d.Supported).OrderBy(d => d.Vendor).
ThenBy(d => d.Model))
{
AaruConsole.DebugWriteLine("Main window",
"Found supported device model {0} by manufacturer {1} on bus {2} and path {3}",
device.Model, device.Vendor, device.Bus, device.Path);
var devItem = new TreeGridItem
{
Values = new object[]
{
hardDiskIcon, $"{device.Vendor} {device.Model} ({device.Bus})", device.Path, null
}
};
try
{
var dev = new Device(device.Path);
if(dev.IsRemote)
Statistics.AddRemote(dev.RemoteApplication, dev.RemoteVersion, dev.RemoteOperatingSystem,
dev.RemoteOperatingSystemVersion, dev.RemoteArchitecture);
switch(dev.Type)
{
case DeviceType.ATAPI:
case DeviceType.SCSI:
switch(dev.ScsiType)
{
case PeripheralDeviceTypes.DirectAccess:
case PeripheralDeviceTypes.SCSIZonedBlockDevice:
case PeripheralDeviceTypes.SimplifiedDevice:
devItem.Values[0] = dev.IsRemovable ? dev.IsUsb
? usbIcon
: removableIcon : hardDiskIcon;
break;
case PeripheralDeviceTypes.SequentialAccess:
devItem.Values[0] = tapeIcon;
break;
case PeripheralDeviceTypes.OpticalDevice:
case PeripheralDeviceTypes.WriteOnceDevice:
case PeripheralDeviceTypes.OCRWDevice:
devItem.Values[0] = removableIcon;
break;
case PeripheralDeviceTypes.MultiMediaDevice:
devItem.Values[0] = opticalIcon;
break;
}
break;
case DeviceType.SecureDigital:
case DeviceType.MMC:
devItem.Values[0] = sdIcon;
break;
case DeviceType.NVMe:
devItem.Values[0] = nullImage;
break;
}
dev.Close();
}
catch
{
// ignored
}
devItem.Children.Add(placeholderItem);
devicesRoot.Children.Add(devItem);
}
treeImages.ReloadData();
}
catch(InvalidOperationException ex)
{
AaruConsole.ErrorWriteLine(ex.Message);
}
}
protected void OnTreeImagesSelectedItemChanged(object sender, EventArgs e)
{
if(!(sender is TreeGridView tree))
@@ -342,7 +236,7 @@ namespace Aaru.Gui.Forms
return;
}
var devInfo = new Core.Devices.Info.DeviceInfo(dev);
var devInfo = new DeviceInfo(dev);
selectedItem.Values[3] = new pnlDeviceInfo(devInfo);
splMain.Panel2 = (Panel)selectedItem.Values[3];
@@ -449,116 +343,6 @@ namespace Aaru.Gui.Forms
dev.Close();
}
else if(((TreeGridItem)e.Item).Values[2] is IReadOnlyFilesystem fsPlugin)
{
var fsItem = (TreeGridItem)e.Item;
fsItem.Children.Clear();
if(fsItem.Values.Length == 5 &&
fsItem.Values[4] is string dirPath)
{
Errno errno = fsPlugin.ReadDir(dirPath, out List<string> dirents);
if(errno != Errno.NoError)
{
Eto.Forms.MessageBox.Show($"Error {errno} trying to read \"{dirPath}\" of chosen filesystem",
MessageBoxType.Error);
return;
}
List<string> directories = new List<string>();
foreach(string dirent in dirents)
{
errno = fsPlugin.Stat(dirPath + "/" + dirent, out FileEntryInfo stat);
if(errno != Errno.NoError)
{
AaruConsole.
ErrorWriteLine($"Error {errno} trying to get information about filesystem entry named {dirent}");
continue;
}
if(stat.Attributes.HasFlag(FileAttributes.Directory))
directories.Add(dirent);
}
foreach(string directory in directories)
{
var dirItem = new TreeGridItem
{
Values = new object[]
{
imagesIcon, directory, fsPlugin, null, dirPath + "/" + directory
}
};
dirItem.Children.Add(placeholderItem);
fsItem.Children.Add(dirItem);
}
}
else
{
Errno errno = fsPlugin.ReadDir("/", out List<string> dirents);
if(errno != Errno.NoError)
{
Eto.Forms.MessageBox.Show($"Error {errno} trying to read root directory of chosen filesystem",
MessageBoxType.Error);
return;
}
Dictionary<string, FileEntryInfo> files = new Dictionary<string, FileEntryInfo>();
List<string> directories = new List<string>();
foreach(string dirent in dirents)
{
errno = fsPlugin.Stat("/" + dirent, out FileEntryInfo stat);
if(errno != Errno.NoError)
{
AaruConsole.
ErrorWriteLine($"Error {errno} trying to get information about filesystem entry named {dirent}");
continue;
}
if(stat.Attributes.HasFlag(FileAttributes.Directory))
directories.Add(dirent);
else
files.Add(dirent, stat);
}
var rootDirectoryItem = new TreeGridItem
{
Values = new object[]
{
nullImage, // TODO: Get icon from volume
"/", fsPlugin, files
}
};
foreach(string directory in directories)
{
var dirItem = new TreeGridItem
{
Values = new object[]
{
imagesIcon, directory, fsPlugin, null, "/" + directory
}
};
dirItem.Children.Add(placeholderItem);
rootDirectoryItem.Children.Add(dirItem);
}
fsItem.Children.Add(rootDirectoryItem);
}
}
}
#region XAML IDs

View File

@@ -1,4 +1,11 @@
using Avalonia.Media.Imaging;
namespace Aaru.Gui.Models
{
public class DeviceModel {}
public class DeviceModel
{
public Bitmap Icon { get; set; }
public string Name { get; set; }
public string Path { get; set; }
}
}

View File

@@ -9,9 +9,11 @@ using Aaru.CommonTypes.Enums;
using Aaru.CommonTypes.Interfaces;
using Aaru.CommonTypes.Interop;
using Aaru.CommonTypes.Structs;
using Aaru.CommonTypes.Structs.Devices.SCSI;
using Aaru.Console;
using Aaru.Core;
using Aaru.Database;
using Aaru.Devices;
using Aaru.Gui.Models;
using Aaru.Gui.Panels;
using Aaru.Gui.Views;
@@ -31,16 +33,21 @@ namespace Aaru.Gui.ViewModels
{
readonly IAssetLoader _assets;
readonly DevicesRootModel _devicesRoot;
readonly Bitmap _ejectIcon;
readonly Bitmap _genericFolderIcon;
readonly Bitmap _genericHddIcon;
readonly Bitmap _genericOpticalIcon;
readonly Bitmap _genericTapeIcon;
readonly ImagesRootModel _imagesRoot;
readonly MainWindow _view;
ConsoleWindow _consoleWindow;
public object _contentPanel;
bool _devicesSupported;
public object _treeViewSelectedItem;
readonly Bitmap _removableIcon;
readonly Bitmap _sdIcon;
readonly Bitmap _usbIcon;
readonly MainWindow _view;
ConsoleWindow _consoleWindow;
object _contentPanel;
bool _devicesSupported;
object _treeViewSelectedItem;
public MainWindowViewModel(MainWindow view)
{
@@ -59,6 +66,7 @@ namespace Aaru.Gui.ViewModels
CreateSidecarCommand = ReactiveCommand.Create(ExecuteCreateSidecarCommand);
ViewImageSectorsCommand = ReactiveCommand.Create(ExecuteViewImageSectorsCommand);
DecodeImageMediaTagsCommand = ReactiveCommand.Create(ExecuteDecodeImageMediaTagsCommand);
RefreshDevicesCommand = ReactiveCommand.Create(ExecuteRefreshDevicesCommand);
_view = view;
TreeRoot = new ObservableCollection<RootModel>();
_assets = AvaloniaLocator.Current.GetService<IAssetLoader>();
@@ -98,6 +106,21 @@ namespace Aaru.Gui.ViewModels
_genericFolderIcon =
new Bitmap(_assets.Open(new Uri("avares://Aaru.Gui/Assets/Icons/oxygen/32x32/inode-directory.png")));
_usbIcon =
new
Bitmap(_assets.Open(new
Uri("avares://Aaru.Gui/Assets/Icons/oxygen/32x32/drive-removable-media-usb.png")));
_removableIcon =
new
Bitmap(_assets.Open(new Uri("avares://Aaru.Gui/Assets/Icons/oxygen/32x32/drive-removable-media.png")));
_sdIcon =
new Bitmap(_assets.Open(new Uri("avares://Aaru.Gui/Assets/Icons/oxygen/32x32/media-flash-sd-mmc.png")));
_ejectIcon =
new Bitmap(_assets.Open(new Uri("avares://Aaru.Gui/Assets/Icons/oxygen/32x32/media-eject.png")));
}
public bool DevicesSupported
@@ -127,6 +150,7 @@ namespace Aaru.Gui.ViewModels
public ReactiveCommand<Unit, Unit> CreateSidecarCommand { get; }
public ReactiveCommand<Unit, Unit> ViewImageSectorsCommand { get; }
public ReactiveCommand<Unit, Unit> DecodeImageMediaTagsCommand { get; }
public ReactiveCommand<Unit, Unit> RefreshDevicesCommand { get; }
public object ContentPanel
{
@@ -282,21 +306,21 @@ namespace Aaru.Gui.ViewModels
dialog.ShowDialog(_view);
}
internal void ExecuteEncodingsCommand()
void ExecuteEncodingsCommand()
{
var dialog = new EncodingsDialog();
dialog.DataContext = new EncodingsDialogViewModel(dialog);
dialog.ShowDialog(_view);
}
internal void ExecutePluginsCommand()
void ExecutePluginsCommand()
{
var dialog = new PluginsDialog();
dialog.DataContext = new PluginsDialogViewModel(dialog);
dialog.ShowDialog(_view);
}
internal void ExecuteStatisticsCommand()
void ExecuteStatisticsCommand()
{
using var ctx = AaruContext.Create(Settings.Settings.LocalDbPath);
@@ -328,7 +352,7 @@ namespace Aaru.Gui.ViewModels
internal void ExecuteExitCommand() =>
(Application.Current.ApplicationLifetime as ClassicDesktopStyleApplicationLifetime)?.Shutdown();
internal void ExecuteConsoleCommand()
void ExecuteConsoleCommand()
{
if(_consoleWindow is null)
{
@@ -407,12 +431,10 @@ namespace Aaru.Gui.ViewModels
Filter = inputFilter
};
// TODO: pnlImageInfo
List<Partition> partitions = Core.Partitions.GetAll(imageFormat);
Core.Partitions.AddSchemesToStats(partitions);
bool checkraw = false;
bool checkRaw = false;
List<string> idPlugins;
IFilesystem plugin;
PluginBase plugins = GetPluginBase.Instance;
@@ -421,7 +443,7 @@ namespace Aaru.Gui.ViewModels
{
AaruConsole.DebugWriteLine("Analyze command", "No partitions found");
checkraw = true;
checkRaw = true;
}
else
{
@@ -438,7 +460,6 @@ namespace Aaru.Gui.ViewModels
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
@@ -505,7 +526,7 @@ namespace Aaru.Gui.ViewModels
}
}
if(checkraw)
if(checkRaw)
{
var wholePart = new Partition
{
@@ -589,5 +610,98 @@ namespace Aaru.Gui.ViewModels
Statistics.AddCommand("image-info");
}
internal void LoadComplete() => RefreshDevices();
void ExecuteRefreshDevicesCommand() => RefreshDevices();
void RefreshDevices()
{
if(!DevicesSupported)
return;
try
{
AaruConsole.WriteLine("Refreshing devices");
_devicesRoot.Devices.Clear();
foreach(DeviceInfo device in Device.ListDevices().Where(d => d.Supported).OrderBy(d => d.Vendor).
ThenBy(d => d.Model))
{
AaruConsole.DebugWriteLine("Main window",
"Found supported device model {0} by manufacturer {1} on bus {2} and path {3}",
device.Model, device.Vendor, device.Bus, device.Path);
var deviceModel = new DeviceModel
{
Icon = _genericHddIcon, Name = $"{device.Vendor} {device.Model} ({device.Bus})",
Path = device.Path
};
try
{
var dev = new Device(device.Path);
if(dev.IsRemote)
Statistics.AddRemote(dev.RemoteApplication, dev.RemoteVersion, dev.RemoteOperatingSystem,
dev.RemoteOperatingSystemVersion, dev.RemoteArchitecture);
switch(dev.Type)
{
case DeviceType.ATAPI:
case DeviceType.SCSI:
switch(dev.ScsiType)
{
case PeripheralDeviceTypes.DirectAccess:
case PeripheralDeviceTypes.SCSIZonedBlockDevice:
case PeripheralDeviceTypes.SimplifiedDevice:
deviceModel.Icon = dev.IsRemovable ? dev.IsUsb
? _usbIcon
: _removableIcon : _genericHddIcon;
break;
case PeripheralDeviceTypes.SequentialAccess:
deviceModel.Icon = _genericTapeIcon;
break;
case PeripheralDeviceTypes.OpticalDevice:
case PeripheralDeviceTypes.WriteOnceDevice:
case PeripheralDeviceTypes.OCRWDevice:
deviceModel.Icon = _removableIcon;
break;
case PeripheralDeviceTypes.MultiMediaDevice:
deviceModel.Icon = _genericOpticalIcon;
break;
}
break;
case DeviceType.SecureDigital:
case DeviceType.MMC:
deviceModel.Icon = _sdIcon;
break;
case DeviceType.NVMe:
deviceModel.Icon = null;
break;
}
dev.Close();
}
catch
{
// ignored
}
_devicesRoot.Devices.Add(deviceModel);
}
}
catch(InvalidOperationException ex)
{
AaruConsole.ErrorWriteLine(ex.Message);
}
}
}
}

View File

@@ -17,7 +17,7 @@
<MenuItem Header="E_xit" IsVisible="{Binding !NativeMenuSupported}" Command="{Binding ExitCommand}" />
</MenuItem>
<MenuItem Header="_Devices" IsVisible="{Binding DevicesSupported}">
<MenuItem Header="_Refresh" />
<MenuItem Header="_Refresh" Command="{Binding RefreshDevicesCommand}" />
</MenuItem>
<MenuItem Header="_Window">
<MenuItem Header="_Console" Command="{Binding ConsoleCommand}" />
@@ -39,12 +39,22 @@
<TreeView.DataTemplates>
<TreeDataTemplate DataType="models:DevicesRootModel" ItemsSource="{Binding Devices}">
<StackPanel Orientation="Horizontal">
<StackPanel.ContextMenu>
<ContextMenu>
<MenuItem Header="_Refresh devices" Command="{Binding RefreshDevicesCommand}" />
</ContextMenu>
</StackPanel.ContextMenu>
<Image Width="24" Height="24" Source="/Assets/Icons/oxygen/32x32/computer.png" />
<TextBlock Text="{Binding Name}" />
</StackPanel>
</TreeDataTemplate>
<TreeDataTemplate DataType="models:ImagesRootModel" ItemsSource="{Binding Images}">
<StackPanel Orientation="Horizontal">
<StackPanel.ContextMenu>
<ContextMenu>
<MenuItem Header="_Close all images" Command="{Binding CloseAllImages}" />
</ContextMenu>
</StackPanel.ContextMenu>
<Image Width="24" Height="24" Source="/Assets/Icons/oxygen/32x32/inode-directory.png" />
<TextBlock Text="{Binding Name}" />
</StackPanel>
@@ -54,27 +64,15 @@
<StackPanel Orientation="Horizontal">
<StackPanel.ContextMenu>
<ContextMenu>
<Button Command="{Binding CalculateEntropyCommand}">
<TextBlock Text="Calculate entropy" />
</Button>
<Button Command="{Binding VerifyImageCommand}">
<TextBlock Text="Verify" />
</Button>
<Button Command="{Binding ChecksumImageCommand}">
<TextBlock Text="Checksum" />
</Button>
<Button Command="{Binding ConvertImageCommand}">
<TextBlock Text="Convert to..." />
</Button>
<Button Command="{Binding CreateSidecarCommand}">
<TextBlock Text="Create CICM XML sidecar..." />
</Button>
<Button Command="{Binding ViewImageSectorsCommand}">
<TextBlock Text="View sectors" />
</Button>
<Button Command="{Binding DecodeImageMediaTagsCommand}">
<TextBlock Text="Decode media tags" />
</Button>
<MenuItem Command="{Binding CalculateEntropyCommand}" Header="Calculate entropy" />
<MenuItem Command="{Binding VerifyImageCommand}" Header="Verify" />
<MenuItem Command="{Binding ChecksumImageCommand}" Header="Checksum" />
<MenuItem Command="{Binding ConvertImageCommand}" Header="Convert to..." />
<MenuItem Command="{Binding CreateSidecarCommand}"
Header="Create CICM XML sidecar..." />
<MenuItem Command="{Binding ViewImageSectorsCommand}" Header="View sectors" />
<MenuItem Command="{Binding DecodeImageMediaTagsCommand}"
Header="Decode media tags" />
</ContextMenu>
</StackPanel.ContextMenu>
<Image Width="24" Height="24" Source="{Binding Icon}" />
@@ -105,6 +103,12 @@
<TextBlock Text="{Binding Name}" />
</StackPanel>
</TreeDataTemplate>
<TreeDataTemplate DataType="models:DeviceModel">
<StackPanel Orientation="Horizontal">
<Image Width="24" Height="24" Source="{Binding Icon}" />
<TextBlock Text="{Binding Name}" />
</StackPanel>
</TreeDataTemplate>
</TreeView.DataTemplates>
</TreeView>
<GridSplitter Grid.Column="1" Width="5" HorizontalAlignment="Stretch" />

View File

@@ -1,4 +1,6 @@
using Avalonia;
using System;
using Aaru.Gui.ViewModels;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
@@ -9,14 +11,18 @@ namespace Aaru.Gui.Views
public MainWindow()
{
InitializeComponent();
#if DEBUG
#if DEBUG
this.AttachDevTools();
#endif
#endif
}
private void InitializeComponent()
void InitializeComponent() => AvaloniaXamlLoader.Load(this);
protected override void OnOpened(EventArgs e)
{
AvaloniaXamlLoader.Load(this);
base.OnOpened(e);
(DataContext as MainWindowViewModel)?.LoadComplete();
}
}
}
}