From c67197f9776d1341c109e8e6d7fa42074c28058e Mon Sep 17 00:00:00 2001 From: Natalia Portillo Date: Wed, 19 Nov 2025 15:15:58 +0000 Subject: [PATCH] [GUI] Add device view. --- .../ViewModels/Windows/DeviceListViewModel.cs | 41 ++++- .../ViewModels/Windows/DeviceViewModel.cs | 149 ++++++++++++++++++ Aaru.Gui/Views/Windows/DeviceList.axaml | 11 +- Aaru.Gui/Views/Windows/DeviceView.axaml | 92 +++++++++++ Aaru.Gui/Views/Windows/DeviceView.axaml.cs | 61 +++++++ Aaru.Localization/UI.Designer.cs | 6 + Aaru.Localization/UI.es.resx | 19 ++- Aaru.Localization/UI.resx | 17 +- 8 files changed, 375 insertions(+), 21 deletions(-) create mode 100644 Aaru.Gui/ViewModels/Windows/DeviceViewModel.cs create mode 100644 Aaru.Gui/Views/Windows/DeviceView.axaml create mode 100644 Aaru.Gui/Views/Windows/DeviceView.axaml.cs diff --git a/Aaru.Gui/ViewModels/Windows/DeviceListViewModel.cs b/Aaru.Gui/ViewModels/Windows/DeviceListViewModel.cs index 47d6e7469..bc93176ce 100644 --- a/Aaru.Gui/ViewModels/Windows/DeviceListViewModel.cs +++ b/Aaru.Gui/ViewModels/Windows/DeviceListViewModel.cs @@ -32,9 +32,11 @@ using System; using System.Collections.ObjectModel; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Net.Sockets; using System.Threading.Tasks; +using System.Windows.Input; using Aaru.Devices; using Aaru.Devices.Remote; using Aaru.Devices.Windows; @@ -44,6 +46,7 @@ using Aaru.Localization; using Aaru.Logging; using Avalonia.Threading; using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; using JetBrains.Annotations; using MsBox.Avalonia; using MsBox.Avalonia.Base; @@ -60,13 +63,41 @@ public partial class DeviceListViewModel : ViewModelBase ObservableCollection _devices; [ObservableProperty] string _remotePath; + [ObservableProperty] + [CanBeNull] + DeviceModel _selectedDevice; - public DeviceListViewModel(DeviceList window) => _window = window; - - public DeviceListViewModel(DeviceList window, [NotNull] string remotePath) + public DeviceListViewModel(DeviceList window) { - _window = window; - RemotePath = remotePath; + _window = window; + OpenDeviceCommand = new RelayCommand(OpenDevice); + } + + public DeviceListViewModel(DeviceList window, [JetBrains.Annotations.NotNull] string remotePath) + { + _window = window; + RemotePath = remotePath; + OpenDeviceCommand = new RelayCommand(OpenDevice); + } + + public ICommand OpenDeviceCommand { get; } + + [SuppressMessage("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", + "MVVMTK0034:Direct field reference to [ObservableProperty] backing field", + Justification = "False positive, says it's always null, but it's not.")] + void OpenDevice() + { + if(_selectedDevice == null) return; + + var deviceView = new DeviceView + { + Topmost = true + }; + + var vm = new DeviceViewModel(deviceView, _selectedDevice.Path); + + deviceView.DataContext = vm; + deviceView.Show(); } public void LoadData() diff --git a/Aaru.Gui/ViewModels/Windows/DeviceViewModel.cs b/Aaru.Gui/ViewModels/Windows/DeviceViewModel.cs new file mode 100644 index 000000000..bb2c35e15 --- /dev/null +++ b/Aaru.Gui/ViewModels/Windows/DeviceViewModel.cs @@ -0,0 +1,149 @@ +// /*************************************************************************** +// Aaru Data Preservation Suite +// ---------------------------------------------------------------------------- +// +// Filename : DeviceViewModel.cs +// Author(s) : Natalia Portillo +// +// Component : GUI view models. +// +// --[ Description ] ---------------------------------------------------------- +// +// View model for device. +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General public License for more details. +// +// You should have received a copy of the GNU General public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2011-2025 Natalia Portillo +// ****************************************************************************/ + +using System.Threading.Tasks; +using Aaru.CommonTypes.Enums; +using Aaru.Core; +using Aaru.Devices; +using Aaru.Gui.Views.Windows; +using Aaru.Localization; +using Aaru.Logging; +using Avalonia.Threading; +using CommunityToolkit.Mvvm.ComponentModel; +using Humanizer; +using MsBox.Avalonia; +using MsBox.Avalonia.Base; +using MsBox.Avalonia.Enums; + +namespace Aaru.Gui.ViewModels.Windows; + +public partial class DeviceViewModel : ViewModelBase +{ + readonly DeviceView _window; + Device _dev; + [ObservableProperty] + string _devicePath; + [ObservableProperty] + string _deviceType; + [ObservableProperty] + string _manufacturer; + [ObservableProperty] + string _model; + [ObservableProperty] + bool _removableChecked; + [ObservableProperty] + string _revision; + [ObservableProperty] + string _scsiType; + [ObservableProperty] + string _serial; + [ObservableProperty] + string _statusMessage; + [ObservableProperty] + bool _usbConnected; + + public DeviceViewModel(DeviceView window, string devicePath) + { + _window = window; + DevicePath = devicePath; + } + + public void LoadData() + { + _ = Task.Run(Worker); + } + + void Worker() + { + Dispatcher.UIThread.Invoke(() => StatusMessage = "Opening device..."); + + var dev = Device.Create(DevicePath, out ErrorNumber devErrno); + + if(dev is null) + { + AaruLogging.Error(string.Format(UI.Could_not_open_device_error_0, devErrno)); + + Dispatcher.UIThread.Invoke(() => + { + IMsBox msbox = MessageBoxManager.GetMessageBoxStandard(UI.Title_Error, + string.Format(UI.Could_not_open_device_error_0, devErrno), + ButtonEnum.Ok, + Icon.Error); + + _ = msbox.ShowAsync(); + + _window.Close(); + }); + + return; + } + + if(dev.Error) + { + AaruLogging.Error(Error.Print(dev.LastError)); + + Dispatcher.UIThread.Invoke(() => + { + IMsBox msbox = MessageBoxManager.GetMessageBoxStandard(UI.Title_Error, + Error.Print(dev.LastError), + ButtonEnum.Ok, + Icon.Error); + + _ = msbox.ShowAsync(); + + _window.Close(); + }); + + return; + } + + Dispatcher.UIThread.Invoke(() => + { + DeviceType = $"[rosybrown]{dev.Type.Humanize()}[/]"; + Manufacturer = (dev.Manufacturer != null ? $"[blue]{dev.Manufacturer}[/]" : null)!; + Model = (dev.Model != null ? $"[purple]{dev.Model}[/]" : null)!; + Revision = (dev.FirmwareRevision != null ? $"[teal]{dev.FirmwareRevision}[/]" : null)!; + Serial = (dev.Serial != null ? $"[fuchsia]{dev.Serial}[/]" : null)!; + ScsiType = $"[orange]{dev.ScsiType.Humanize()}[/]"; + RemovableChecked = dev.IsRemovable; + UsbConnected = dev.IsUsb; + StatusMessage = "Device opened successfully."; + }); + + _dev = dev; + } + + public void Closed() + { + _dev?.Close(); + } +} \ No newline at end of file diff --git a/Aaru.Gui/Views/Windows/DeviceList.axaml b/Aaru.Gui/Views/Windows/DeviceList.axaml index 1d8ce39c4..c14cae615 100644 --- a/Aaru.Gui/Views/Windows/DeviceList.axaml +++ b/Aaru.Gui/Views/Windows/DeviceList.axaml @@ -16,7 +16,7 @@ -