[GUI] Added a window to edit AaruFormat files basic metadata.

This commit is contained in:
2025-12-14 11:09:43 +00:00
parent 61eb194fa1
commit 0b373c33e1
8 changed files with 1030 additions and 16 deletions

View File

@@ -0,0 +1,359 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Input;
using Aaru.CommonTypes;
using Aaru.CommonTypes.Enums;
using Aaru.CommonTypes.Interfaces;
using Aaru.Core;
using Aaru.Gui.Views.Windows;
using Aaru.Images;
using Aaru.Localization;
using Aaru.Logging;
using Avalonia.Platform.Storage;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Humanizer;
using MsBox.Avalonia;
using MsBox.Avalonia.Base;
using MsBox.Avalonia.Enums;
using ImageInfo = Aaru.CommonTypes.Structs.ImageInfo;
namespace Aaru.Gui.ViewModels.Windows;
public sealed partial class ImageMetadataViewModel : ViewModelBase
{
readonly ImageMetadata _view;
[ObservableProperty]
string _comments;
[ObservableProperty]
bool _commentsNotSet = true;
[ObservableProperty]
string _creator;
[ObservableProperty]
bool _creatorNotSet = true;
[ObservableProperty]
string _driveFirmwareRevision;
[ObservableProperty]
bool _driveFirmwareRevisionNotSet = true;
[ObservableProperty]
string _driveManufacturer;
[ObservableProperty]
bool _driveManufacturerNotSet = true;
[ObservableProperty]
string _driveModel;
[ObservableProperty]
bool _driveModelNotSet = true;
[ObservableProperty]
string _driveSerialNumber;
[ObservableProperty]
bool _driveSerialNumberNotSet = true;
AaruFormat _imageFormat;
[ObservableProperty]
string _imagePath;
IFilter _inputFilter;
[ObservableProperty]
bool _isOpened;
[ObservableProperty]
int _mediaLastSequence;
[ObservableProperty]
string _mediaManufacturer;
[ObservableProperty]
bool _mediaManufacturerNotSet = true;
[ObservableProperty]
string _mediaModel;
[ObservableProperty]
bool _mediaModelNotSet = true;
[ObservableProperty]
string _mediaPartNumber;
[ObservableProperty]
bool _mediaPartNumberNotSet = true;
[ObservableProperty]
int _mediaSequence;
[ObservableProperty]
string _mediaSerialNumber;
[ObservableProperty]
bool _mediaSerialNumberNotSet = true;
[ObservableProperty]
string _mediaTitle;
[ObservableProperty]
bool _mediaTitleNotSet = true;
[ObservableProperty]
string _mediaType;
[ObservableProperty]
bool _sequenceNotSet = true;
[ObservableProperty]
string _size;
public ImageMetadataViewModel(ImageMetadata view)
{
_view = view;
OpenImageCommand = new AsyncRelayCommand(OpenImageAsync);
LoadMetadataCommand = new RelayCommand(LoadMetadata);
SaveMetadataCommand = new AsyncRelayCommand(SaveMetadataAsync);
CloseCommand = new RelayCommand(Close);
}
public ICommand OpenImageCommand { get; }
public ICommand LoadMetadataCommand { get; }
public ICommand SaveMetadataCommand { get; }
public ICommand CloseCommand { get; }
static FilePickerFileType AaruFormatFiles { get; } = new(UI.AaruFormat_files)
{
Patterns = new AaruFormat().KnownExtensions.Select(static s => $"*{s}").ToList(),
MimeTypes = ["application/octet-stream"]
};
void Close()
{
CloseImage();
_view.Close();
}
public void CloseImage()
{
if(!IsOpened) return;
_imageFormat.Close();
IsOpened = false;
}
async Task SaveMetadataAsync()
{
if(!IsOpened) return;
var info = new ImageInfo
{
MediaSequence = SequenceNotSet ? 0 : MediaSequence,
LastMediaSequence = SequenceNotSet ? 0 : MediaLastSequence,
Creator = CreatorNotSet ? null : Creator,
Comments = CommentsNotSet ? null : Comments,
MediaTitle = MediaTitleNotSet ? null : MediaTitle,
MediaManufacturer = MediaManufacturerNotSet ? null : MediaManufacturer,
MediaModel = MediaModelNotSet ? null : MediaModel,
MediaSerialNumber = MediaSerialNumberNotSet ? null : MediaSerialNumber,
MediaPartNumber = MediaPartNumberNotSet ? null : MediaPartNumber,
DriveManufacturer = DriveManufacturerNotSet ? null : DriveManufacturer,
DriveModel = DriveModelNotSet ? null : DriveModel,
DriveSerialNumber = DriveSerialNumberNotSet ? null : DriveSerialNumber,
DriveFirmwareRevision = DriveFirmwareRevisionNotSet ? null : DriveFirmwareRevision
};
ulong sectors = _imageFormat.Info.Sectors;
MediaType mediaType = _imageFormat.Info.MediaType;
uint negativeSectors = _imageFormat.Info.NegativeSectors;
uint overflowSectors = _imageFormat.Info.OverflowSectors;
uint sectorSize = _imageFormat.Info.SectorSize;
// We close the read-only context and reopen it in resume mode
_imageFormat.Close();
bool ret = _imageFormat.Create(ImagePath, mediaType, [], sectors, negativeSectors, overflowSectors, sectorSize);
IMsBox<ButtonResult> msbox;
if(!ret)
{
AaruLogging.Error(UI.Error_reopening_image_for_writing);
AaruLogging.Error(_imageFormat.ErrorMessage);
msbox = MessageBoxManager.GetMessageBoxStandard(UI.Title_Error,
UI.There_was_an_error_reopening_the_image_for_writing,
ButtonEnum.Ok,
Icon.Error);
await msbox.ShowAsync();
_view.Close();
return;
}
// Now we set the metadata
_imageFormat.SetImageInfo(info);
// We close the image
_imageFormat.Close();
// And we re-open it in read-only mode
ErrorNumber errno = _imageFormat.Open(_inputFilter);
if(errno != ErrorNumber.NoError)
{
AaruLogging.Error(UI.Error_reopening_image_in_read_only_mode_after_writing_metadata);
AaruLogging.Error(Localization.Core.Error_0, errno);
msbox = MessageBoxManager.GetMessageBoxStandard(UI.Title_Error,
UI
.There_was_an_error_reopening_the_image_in_read_only_mode_after_writing_metadata,
ButtonEnum.Ok,
Icon.Error);
await msbox.ShowAsync();
_view.Close();
return;
}
msbox = MessageBoxManager.GetMessageBoxStandard(Localization.Core.Success,
UI.Metadata_saved_successfully,
ButtonEnum.Ok,
Icon.Success);
await msbox.ShowAsync();
LoadMetadata();
}
void LoadMetadata()
{
if(!IsOpened) return;
MediaSequence = _imageFormat.Info.MediaSequence;
MediaLastSequence = _imageFormat.Info.LastMediaSequence;
Creator = _imageFormat.Info.Creator;
Comments = _imageFormat.Info.Comments;
MediaTitle = _imageFormat.Info.MediaTitle;
MediaManufacturer = _imageFormat.Info.MediaManufacturer;
MediaModel = _imageFormat.Info.MediaModel;
MediaSerialNumber = _imageFormat.Info.MediaSerialNumber;
MediaPartNumber = _imageFormat.Info.MediaPartNumber;
DriveManufacturer = _imageFormat.Info.DriveManufacturer;
DriveModel = _imageFormat.Info.DriveModel;
DriveSerialNumber = _imageFormat.Info.DriveSerialNumber;
DriveFirmwareRevision = _imageFormat.Info.DriveFirmwareRevision;
SequenceNotSet = MediaSequence == 0 || MediaLastSequence == 0;
CreatorNotSet = string.IsNullOrEmpty(Creator);
CommentsNotSet = string.IsNullOrEmpty(Comments);
MediaTitleNotSet = string.IsNullOrEmpty(MediaTitle);
MediaManufacturerNotSet = string.IsNullOrEmpty(MediaManufacturer);
MediaModelNotSet = string.IsNullOrEmpty(MediaModel);
MediaSerialNumberNotSet = string.IsNullOrEmpty(MediaSerialNumber);
MediaPartNumberNotSet = string.IsNullOrEmpty(MediaPartNumber);
DriveManufacturerNotSet = string.IsNullOrEmpty(DriveManufacturer);
DriveModelNotSet = string.IsNullOrEmpty(DriveModel);
DriveSerialNumberNotSet = string.IsNullOrEmpty(DriveSerialNumber);
DriveFirmwareRevisionNotSet = string.IsNullOrEmpty(DriveFirmwareRevision);
}
async Task OpenImageAsync()
{
IReadOnlyList<IStorageFile> result = await _view.StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
{
Title = UI.Dialog_Choose_image_to_open,
AllowMultiple = false,
FileTypeFilter = [AaruFormatFiles]
});
// Exit if user did not select exactly one file
if(result.Count != 1) return;
// Get the appropriate filter plugin for the selected file
IFilter inputFilter = PluginRegister.Singleton.GetFilter(result[0].Path.LocalPath);
// Show error if no suitable filter plugin is found
if(inputFilter == null)
{
IMsBox<ButtonResult> msbox = MessageBoxManager.GetMessageBoxStandard(UI.Title_Error,
UI.Cannot_open_specified_file,
ButtonEnum.Ok,
Icon.Error);
await msbox.ShowAsync();
return;
}
try
{
// Detect the image format of the selected file
if(ImageFormat.Detect(inputFilter) is not AaruFormat imageFormat)
{
IMsBox<ButtonResult> msbox = MessageBoxManager.GetMessageBoxStandard(UI.Title_Error,
UI.File_is_not_an_AaruFormat_image,
ButtonEnum.Ok,
Icon.Error);
await msbox.ShowAsync();
return;
}
try
{
// Open the image file
ErrorNumber opened = imageFormat.Open(inputFilter);
if(opened != ErrorNumber.NoError)
{
IMsBox<ButtonResult> msbox = MessageBoxManager.GetMessageBoxStandard(UI.Title_Error,
string.Format(UI.Error_0_opening_image_format, opened),
ButtonEnum.Ok,
Icon.Error);
await msbox.ShowAsync();
AaruLogging.Error(UI.Unable_to_open_image_format);
AaruLogging.Error(UI.No_error_given);
return;
}
if(imageFormat.Info.Version.StartsWith("1.", StringComparison.OrdinalIgnoreCase))
{
IMsBox<ButtonResult> msbox = MessageBoxManager.GetMessageBoxStandard(UI.Title_Warning,
UI.AaruFormat_images_version_1_x_are_read_only,
ButtonEnum.Ok,
Icon.Warning);
await msbox.ShowAsync();
return;
}
ImagePath = $"[lime]{result[0].Path.LocalPath}[/]";
MediaType = $"[orange]{imageFormat.Info.MediaType.Humanize()}[/]";
Size =
$"[teal]{ByteSize.FromBytes(imageFormat.Info.Sectors * imageFormat.Info.SectorSize).Humanize()}[/]";
_inputFilter = inputFilter;
_imageFormat = imageFormat;
IsOpened = true;
LoadMetadata();
}
catch(Exception ex)
{
IMsBox<ButtonResult> msbox = MessageBoxManager.GetMessageBoxStandard(UI.Title_Error,
UI.Unable_to_open_image_format,
ButtonEnum.Ok,
Icon.Error);
await msbox.ShowAsync();
AaruLogging.Error(UI.Unable_to_open_image_format);
AaruLogging.Error(Localization.Core.Error_0, ex.Message);
AaruLogging.Exception(ex, Localization.Core.Error_0, ex.Message);
}
}
catch(Exception ex)
{
IMsBox<ButtonResult> msbox = MessageBoxManager.GetMessageBoxStandard(UI.Title_Error,
UI.Exception_reading_file,
ButtonEnum.Ok,
Icon.Error);
await msbox.ShowAsync();
AaruLogging.Error(string.Format(UI.Error_reading_file_0, ex.Message));
AaruLogging.Exception(ex, UI.Error_reading_file_0, ex.Message);
}
}
}

View File

@@ -118,6 +118,7 @@ public partial class MainWindowViewModel : ViewModelBase
OpenIbgLogCommand = new AsyncRelayCommand(OpenIbgLogAsync);
ConnectToRemoteCommand = new AsyncRelayCommand(ConnectToRemoteAsync);
OpenDeviceCommand = new RelayCommand(OpenDevice);
ImageMetadataCommand = new AsyncRelayCommand(ImageMetadataAsync);
_genericHddIcon =
new Bitmap(AssetLoader.Open(new Uri("avares://Aaru.Gui/Assets/Icons/oxygen/32x32/drive-harddisk.png")));
@@ -172,6 +173,7 @@ public partial class MainWindowViewModel : ViewModelBase
public ICommand OpenIbgLogCommand { get; }
public ICommand ConnectToRemoteCommand { get; }
public ICommand OpenDeviceCommand { get; }
public ICommand ImageMetadataCommand { get; }
public bool NativeMenuSupported
{
@@ -229,6 +231,14 @@ public partial class MainWindowViewModel : ViewModelBase
}
}
Task ImageMetadataAsync()
{
var dialog = new ImageMetadata();
dialog.DataContext = new ImageMetadataViewModel(dialog);
return dialog.ShowDialog(_view);
}
void OpenDevice()
{
var deviceListWindow = new DeviceList();

View File

@@ -0,0 +1,235 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:windows="clr-namespace:Aaru.Gui.ViewModels.Windows"
xmlns:controls="clr-namespace:Aaru.Gui.Controls"
xmlns:localization="clr-namespace:Aaru.Localization;assembly=Aaru.Localization"
mc:Ignorable="d"
d:DesignWidth="800"
d:DesignHeight="450"
Width="680"
Height="600"
x:Class="Aaru.Gui.Views.Windows.ImageMetadata"
x:DataType="windows:ImageMetadataViewModel"
Title="{x:Static localization:UI.Title_Edit_image_metadata}">
<Design.DataContext>
<windows:ImageMetadataViewModel />
</Design.DataContext>
<Grid RowDefinitions="*, Auto"
RowSpacing="8"
Margin="12">
<TabControl Grid.Row="0">
<TabItem>
<TabItem.Header>
<controls:SpectreTextBlock Text="{x:Static localization:UI.Title_Image}" />
</TabItem.Header>
<Grid Margin="8"
RowSpacing="8"
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,*">
<StackPanel Orientation="Horizontal"
Spacing="8"
Grid.Row="0">
<controls:SpectreTextBlock Text="{x:Static localization:UI.Title_Image_path}"
FontWeight="Bold" />
<controls:SpectreTextBlock Text="{Binding ImagePath, Mode=OneWay}" />
</StackPanel>
<StackPanel Orientation="Horizontal"
Spacing="8"
Grid.Row="1">
<controls:SpectreTextBlock Text="{x:Static localization:UI.Title_Media_type}"
FontWeight="Bold" />
<controls:SpectreTextBlock Text="{Binding MediaType, Mode=OneWay}" />
</StackPanel>
<StackPanel Orientation="Horizontal"
Spacing="8"
Grid.Row="2">
<controls:SpectreTextBlock Text="{x:Static localization:UI.Title_Size}"
FontWeight="Bold" />
<controls:SpectreTextBlock Text="{Binding Size, Mode=OneWay}" />
</StackPanel>
<StackPanel Orientation="Horizontal"
Spacing="8"
Grid.Row="3">
<controls:SpectreTextBlock Text="{x:Static localization:UI.Title_Creator}"
FontWeight="Bold"
VerticalAlignment="Center" />
<CheckBox Content="{x:Static localization:UI.Title_Not_set}"
IsChecked="{Binding CreatorNotSet, Mode=TwoWay}" />
</StackPanel>
<TextBox Text="{Binding Creator, Mode=TwoWay}"
Watermark="{x:Static localization:UI.Watermark_Who_created_the_image}"
Grid.Row="4"
IsEnabled="{Binding !CreatorNotSet, Mode=OneWay}" />
<StackPanel Orientation="Horizontal"
Spacing="8"
Grid.Row="5">
<controls:SpectreTextBlock Text="{x:Static localization:UI.Title_Comments}"
FontWeight="Bold"
VerticalAlignment="Center" />
<CheckBox Content="{x:Static localization:UI.Title_Not_set}"
IsChecked="{Binding CommentsNotSet, Mode=TwoWay}" />
</StackPanel>
<TextBox Text="{Binding Comments, Mode=TwoWay}"
AcceptsReturn="True"
MinHeight="200"
VerticalAlignment="Stretch"
Grid.Row="6"
IsEnabled="{Binding !CommentsNotSet, Mode=OneWay}" />
</Grid>
</TabItem>
<TabItem>
<TabItem.Header>
<controls:SpectreTextBlock Text="{x:Static localization:UI.Title_Media}" />
</TabItem.Header>
<StackPanel Orientation="Vertical"
Spacing="8">
<CheckBox Content="{x:Static localization:UI.Title_Sequence_is_not_set}"
IsChecked="{Binding SequenceNotSet, Mode=TwoWay}" />
<StackPanel Orientation="Horizontal"
Spacing="8">
<controls:SpectreTextBlock Text="{x:Static localization:UI.Title_Media_number_no}"
VerticalAlignment="Center" />
<NumericUpDown Minimum="1"
Increment="1"
Value="{Binding MediaSequence, Mode=TwoWay}"
IsEnabled="{Binding !SequenceNotSet, Mode=OneWay}"
Width="120" />
<controls:SpectreTextBlock Text="{x:Static localization:UI.Title_no_in_a_sequence_of_no}"
VerticalAlignment="Center" />
<NumericUpDown Minimum="1"
Increment="1"
Value="{Binding MediaLastSequence, Mode=TwoWay}"
IsEnabled="{Binding !SequenceNotSet, Mode=OneWay}"
Width="120" />
<controls:SpectreTextBlock Text="{x:Static localization:UI.Title_no_media}"
VerticalAlignment="Center" />
</StackPanel>
<StackPanel Orientation="Horizontal"
Spacing="8">
<controls:SpectreTextBlock Text="{x:Static localization:UI.Title_Title}"
FontWeight="Bold"
VerticalAlignment="Center" />
<CheckBox Content="{x:Static localization:UI.Title_Not_set}"
IsChecked="{Binding MediaTitleNotSet, Mode=TwoWay}" />
</StackPanel>
<TextBox Text="{Binding MediaTitle, Mode=TwoWay}"
IsEnabled="{Binding !MediaTitleNotSet, Mode=OneWay}"
Watermark="{x:Static localization:UI.Watermark_Media_title}" />
<StackPanel Orientation="Horizontal"
Spacing="8">
<controls:SpectreTextBlock Text="{x:Static localization:UI.Title_Manufacturer}"
FontWeight="Bold"
VerticalAlignment="Center" />
<CheckBox Content="{x:Static localization:UI.Title_Not_set}"
IsChecked="{Binding MediaManufacturerNotSet, Mode=TwoWay}" />
</StackPanel>
<TextBox Text="{Binding MediaManufacturer, Mode=TwoWay}"
IsEnabled="{Binding !MediaManufacturerNotSet, Mode=OneWay}"
Watermark="{x:Static localization:UI.Watermark_Media_manufacturer}" />
<StackPanel Orientation="Horizontal"
Spacing="8">
<controls:SpectreTextBlock Text="{x:Static localization:UI.Title_Model}"
FontWeight="Bold"
VerticalAlignment="Center" />
<CheckBox Content="{x:Static localization:UI.Title_Not_set}"
IsChecked="{Binding MediaModelNotSet, Mode=TwoWay}" />
</StackPanel>
<TextBox Text="{Binding MediaModel, Mode=TwoWay}"
IsEnabled="{Binding !MediaModelNotSet, Mode=OneWay}"
Watermark="{x:Static localization:UI.Watermark_Media_model}" />
<StackPanel Orientation="Horizontal"
Spacing="8">
<controls:SpectreTextBlock Text="{x:Static localization:UI.Title_Manufacturer}"
FontWeight="Bold"
VerticalAlignment="Center" />
<CheckBox Content="{x:Static localization:UI.Title_Not_set}"
IsChecked="{Binding MediaSerialNumberNotSet, Mode=TwoWay}" />
</StackPanel>
<TextBox Text="{Binding MediaSerialNumber, Mode=TwoWay}"
IsEnabled="{Binding !MediaSerialNumberNotSet, Mode=OneWay}"
Watermark="{x:Static localization:UI.Watermark_Media_serial_number}" />
<StackPanel Orientation="Horizontal"
Spacing="8">
<controls:SpectreTextBlock Text="{x:Static localization:UI.Title_Part_number}"
FontWeight="Bold"
VerticalAlignment="Center" />
<CheckBox Content="{x:Static localization:UI.Title_Not_set}"
IsChecked="{Binding MediaPartNumberNotSet, Mode=TwoWay}" />
</StackPanel>
<TextBox Text="{Binding MediaPartNumber, Mode=TwoWay}"
IsEnabled="{Binding !MediaPartNumberNotSet, Mode=OneWay}"
Watermark="{x:Static localization:UI.Watermark_Media_part_number}" />
</StackPanel>
</TabItem>
<TabItem>
<TabItem.Header>
<controls:SpectreTextBlock Text="{x:Static localization:UI.Title_Drive}" />
</TabItem.Header>
<StackPanel Orientation="Vertical"
Spacing="8">
<StackPanel Orientation="Horizontal"
Spacing="8">
<controls:SpectreTextBlock Text="{x:Static localization:UI.Title_Manufacturer}"
FontWeight="Bold"
VerticalAlignment="Center" />
<CheckBox Content="{x:Static localization:UI.Title_Not_set}"
IsChecked="{Binding DriveManufacturerNotSet, Mode=TwoWay}" />
</StackPanel>
<TextBox Text="{Binding DriveManufacturer, Mode=TwoWay}"
IsEnabled="{Binding !DriveManufacturerNotSet, Mode=OneWay}"
Watermark="{x:Static localization:UI.Watermark_Drive_manufacturer}" />
<StackPanel Orientation="Horizontal"
Spacing="8">
<controls:SpectreTextBlock Text="{x:Static localization:UI.Title_Model}"
FontWeight="Bold"
VerticalAlignment="Center" />
<CheckBox Content="{x:Static localization:UI.Title_Not_set}"
IsChecked="{Binding DriveModelNotSet, Mode=TwoWay}" />
</StackPanel>
<TextBox Text="{Binding DriveModel, Mode=TwoWay}"
IsEnabled="{Binding !DriveModelNotSet, Mode=OneWay}"
Watermark="{x:Static localization:UI.Watermark_Drive_model}" />
<StackPanel Orientation="Horizontal"
Spacing="8">
<controls:SpectreTextBlock Text="{x:Static localization:UI.Title_Serial_number}"
FontWeight="Bold"
VerticalAlignment="Center" />
<CheckBox Content="{x:Static localization:UI.Title_Not_set}"
IsChecked="{Binding DriveSerialNumberNotSet, Mode=TwoWay}" />
</StackPanel>
<TextBox Text="{Binding DriveSerialNumber, Mode=TwoWay}"
IsEnabled="{Binding !DriveSerialNumberNotSet, Mode=OneWay}"
Watermark="{x:Static localization:UI.Watermark_Drive_serial_number}" />
<StackPanel Orientation="Horizontal"
Spacing="8">
<controls:SpectreTextBlock Text="{x:Static localization:UI.Title_Firmware_revision}"
FontWeight="Bold"
VerticalAlignment="Center" />
<CheckBox Content="{x:Static localization:UI.Title_Not_set}"
IsChecked="{Binding DriveFirmwareRevisionNotSet, Mode=TwoWay}" />
</StackPanel>
<TextBox Text="{Binding DriveFirmwareRevision, Mode=TwoWay}"
IsEnabled="{Binding !DriveFirmwareRevisionNotSet, Mode=OneWay}"
Watermark="{x:Static localization:UI.Watermark_Drive_firmware_revision}" />
</StackPanel>
</TabItem>
</TabControl>
<StackPanel Grid.Row="1"
HorizontalAlignment="Right"
Orientation="Horizontal"
Spacing="8">
<Button Content="{x:Static localization:UI.ButtonLabel_Open}"
IsVisible="{Binding !IsOpened, Mode=OneWay}"
Command="{Binding OpenImageCommand, Mode=OneWay}" />
<Button Content="{x:Static localization:UI.ButtonLabel_Load}"
IsVisible="{Binding IsOpened, Mode=OneWay}"
Command="{Binding LoadMetadataCommand, Mode=OneWay}" />
<Button Content="{x:Static localization:UI.ButtonLabel_Save}"
IsVisible="{Binding IsOpened, Mode=OneWay}"
Command="{Binding SaveMetadataCommand, Mode=OneWay}" />
<Button Content="{x:Static localization:UI.ButtonLabel_Close}"
Command="{Binding CloseCommand, Mode=OneWay}" />
</StackPanel>
</Grid>
</Window>

View File

@@ -0,0 +1,19 @@
using Aaru.Gui.ViewModels.Windows;
using Avalonia.Controls;
namespace Aaru.Gui.Views.Windows;
public partial class ImageMetadata : Window
{
public ImageMetadata()
{
InitializeComponent();
}
/// <inheritdoc />
protected override void OnClosing(WindowClosingEventArgs e)
{
(DataContext as ImageMetadataViewModel)?.CloseImage();
base.OnClosing(e);
}
}

View File

@@ -26,7 +26,7 @@
Command="{Binding OpenIbgLogCommand, Mode=OneWay}" />
<MenuItem Header="{x:Static localization:UI.Menu_Connect_to_AaruRemote}"
Command="{Binding ConnectToRemoteCommand, Mode=OneWay}" />
<MenuItem Header="Open device..."
<MenuItem Header="{x:Static localization:UI.Menu_Open_device}"
Command="{Binding OpenDeviceCommand, Mode=OneWay}"
IsVisible="{Binding DevicesSupported, Mode=OneWay}" />
<Separator />
@@ -38,7 +38,7 @@
IsVisible="{Binding !NativeMenuSupported, Mode=OneWay}"
Command="{Binding ExitCommand, Mode=OneWay}" />
</MenuItem>
<MenuItem Header="Image"
<MenuItem Header="{x:Static localization:UI.Menu_Image}"
IsEnabled="{Binding ImageLoaded, Mode=OneWay}">
<MenuItem Command="{Binding CalculateEntropyCommand, Mode=OneWay}"
Header="{x:Static localization:UI.ButtonLabel_Calculate_entropy}" />
@@ -55,6 +55,10 @@
<MenuItem Command="{Binding DecodeImageMediaTagsCommand, Mode=OneWay}"
Header="{x:Static localization:UI.ButtonLabel_Decode_media_tags}" />
</MenuItem>
<MenuItem Header="{x:Static localization:UI.Menu_Metadata}">
<MenuItem Command="{Binding ImageMetadataCommand, Mode=OneWay}"
Header="{x:Static localization:UI.Menu_Edit_image_metadata}" />
</MenuItem>
<MenuItem Header="{x:Static localization:UI.Menu_Window}">
<MenuItem Header="{x:Static localization:UI.Menu_Console}"
Command="{Binding ConsoleCommand, Mode=OneWay}" />

View File

@@ -6699,5 +6699,197 @@ namespace Aaru.Localization {
return ResourceManager.GetString("Images_have_identical_contents", resourceCulture);
}
}
public static string Menu_Image {
get {
return ResourceManager.GetString("Menu_Image", resourceCulture);
}
}
public static string Menu_Metadata {
get {
return ResourceManager.GetString("Menu_Metadata", resourceCulture);
}
}
public static string Menu_Edit_image_metadata {
get {
return ResourceManager.GetString("Menu_Edit_image_metadata", resourceCulture);
}
}
public static string Title_Edit_image_metadata {
get {
return ResourceManager.GetString("Title_Edit_image_metadata", resourceCulture);
}
}
public static string Title_Image {
get {
return ResourceManager.GetString("Title_Image", resourceCulture);
}
}
public static string Title_Image_path {
get {
return ResourceManager.GetString("Title_Image_path", resourceCulture);
}
}
public static string Title_Creator {
get {
return ResourceManager.GetString("Title_Creator", resourceCulture);
}
}
public static string Title_Not_set {
get {
return ResourceManager.GetString("Title_Not_set", resourceCulture);
}
}
public static string Watermark_Who_created_the_image {
get {
return ResourceManager.GetString("Watermark_Who_created_the_image", resourceCulture);
}
}
public static string Title_Sequence_is_not_set {
get {
return ResourceManager.GetString("Title_Sequence_is_not_set", resourceCulture);
}
}
public static string Title_Media_number_no {
get {
return ResourceManager.GetString("Title_Media_number_no", resourceCulture);
}
}
public static string Title_no_in_a_sequence_of_no {
get {
return ResourceManager.GetString("Title_no_in_a_sequence_of_no", resourceCulture);
}
}
public static string Title_no_media {
get {
return ResourceManager.GetString("Title_no_media", resourceCulture);
}
}
public static string Watermark_Media_title {
get {
return ResourceManager.GetString("Watermark_Media_title", resourceCulture);
}
}
public static string Watermark_Media_manufacturer {
get {
return ResourceManager.GetString("Watermark_Media_manufacturer", resourceCulture);
}
}
public static string Watermark_Media_model {
get {
return ResourceManager.GetString("Watermark_Media_model", resourceCulture);
}
}
public static string Watermark_Media_serial_number {
get {
return ResourceManager.GetString("Watermark_Media_serial_number", resourceCulture);
}
}
public static string Watermark_Media_part_number {
get {
return ResourceManager.GetString("Watermark_Media_part_number", resourceCulture);
}
}
public static string Watermark_Drive_manufacturer {
get {
return ResourceManager.GetString("Watermark_Drive_manufacturer", resourceCulture);
}
}
public static string Watermark_Drive_model {
get {
return ResourceManager.GetString("Watermark_Drive_model", resourceCulture);
}
}
public static string Watermark_Drive_serial_number {
get {
return ResourceManager.GetString("Watermark_Drive_serial_number", resourceCulture);
}
}
public static string Watermark_Drive_firmware_revision {
get {
return ResourceManager.GetString("Watermark_Drive_firmware_revision", resourceCulture);
}
}
public static string ButtonLabel_Open {
get {
return ResourceManager.GetString("ButtonLabel_Open", resourceCulture);
}
}
public static string ButtonLabel_Load {
get {
return ResourceManager.GetString("ButtonLabel_Load", resourceCulture);
}
}
public static string AaruFormat_files {
get {
return ResourceManager.GetString("AaruFormat_files", resourceCulture);
}
}
public static string Error_reopening_image_for_writing {
get {
return ResourceManager.GetString("Error_reopening_image_for_writing", resourceCulture);
}
}
public static string There_was_an_error_reopening_the_image_for_writing {
get {
return ResourceManager.GetString("There_was_an_error_reopening_the_image_for_writing", resourceCulture);
}
}
public static string Error_reopening_image_in_read_only_mode_after_writing_metadata {
get {
return ResourceManager.GetString("Error_reopening_image_in_read_only_mode_after_writing_metadata", resourceCulture);
}
}
public static string There_was_an_error_reopening_the_image_in_read_only_mode_after_writing_metadata {
get {
return ResourceManager.GetString("There_was_an_error_reopening_the_image_in_read_only_mode_after_writing_metadata", resourceCulture);
}
}
public static string Metadata_saved_successfully {
get {
return ResourceManager.GetString("Metadata_saved_successfully", resourceCulture);
}
}
public static string File_is_not_an_AaruFormat_image {
get {
return ResourceManager.GetString("File_is_not_an_AaruFormat_image", resourceCulture);
}
}
public static string AaruFormat_images_version_1_x_are_read_only {
get {
return ResourceManager.GetString("AaruFormat_images_version_1_x_are_read_only", resourceCulture);
}
}
}
}

View File

@@ -2193,7 +2193,7 @@ Probadores:
<value>Comandos</value>
</data>
<data name="Title_Comments" xml:space="preserve">
<value>Comentarios</value>
<value>[bold][slateblue1]Comentarios[/][/]</value>
</data>
<data name="Title_Connected_by_USB" xml:space="preserve">
<value>[bold][slateblue1]Conectado por USB[/][/]</value>
@@ -2244,7 +2244,7 @@ Probadores:
<value>Información del fabricante del disco</value>
</data>
<data name="Title_Drive" xml:space="preserve">
<value>Unidad</value>
<value>[bold][red]Unidad[/][/]</value>
</data>
<data name="Title_Drive_firmware_revision" xml:space="preserve">
<value>[slateblue1]Revisión del firmware de la unidad[/]</value>
@@ -2310,7 +2310,7 @@ Probadores:
<value>[bold][blue]Dispositivo FireWire[/][/]</value>
</data>
<data name="Title_Firmware_revision" xml:space="preserve">
<value>Revisión de firmware</value>
<value>[bold][slateblue1]Revisión de firmware[/][/]</value>
</data>
<data name="Title_First_Media_image" xml:space="preserve">
<value>[green]1ª imagen[/]</value>
@@ -2478,8 +2478,8 @@ Probadores:
<value>[bold][blue]Mensaje[/][/]</value>
</data>
<data name="Title_Metadata" xml:space="preserve">
<value>Metadato</value>
</data>
<value>Metadatos</value>
</data>
<data name="Title_Miscellaneous" xml:space="preserve">
<value>[bold][darkorange3]Miscelánea[/][/]</value>
</data>
@@ -2529,7 +2529,7 @@ Probadores:
<value>Particiones</value>
</data>
<data name="Title_Part_number" xml:space="preserve">
<value>Número de parte</value>
<value>[bold][slateblue1]Número de parte[/][/]</value>
</data>
<data name="Title_PCMCIA" xml:space="preserve">
<value>PCMCIA</value>
@@ -2631,7 +2631,7 @@ Probadores:
<value>[bold][teal]Veces usado[/][/]</value>
</data>
<data name="Title_Title" xml:space="preserve">
<value>Título</value>
<value>[bold][slateblue1]Título[/][/]</value>
</data>
<data name="Title_TOC" xml:space="preserve">
<value>[bold][blue]Tabla de contenidos[/][/]</value>
@@ -2988,7 +2988,7 @@ Probadores:
<value>[bold][blue]Fecha[/][/]</value>
</data>
<data name="Title_Size" xml:space="preserve">
<value>[bold][lime]Tamaño[/][/]</value>
<value>[bold][slateblue1]Tamaño[/][/]</value>
</data>
<data name="Unable_to_get_information_about_archive" xml:space="preserve">
<value>[red]No se pudo obtener información sobre el archivo...[/]</value>
@@ -3350,4 +3350,100 @@ Probadores:
<data name="Images_have_identical_contents" xml:space="preserve">
<value>[green]Las imágenes tienen idéntico contenido.[/]</value>
</data>
<data name="Menu_Image" xml:space="preserve">
<value>_Imagen</value>
</data>
<data name="Menu_Metadata" xml:space="preserve">
<value>_Metadatos</value>
</data>
<data name="Menu_Edit_image_metadata" xml:space="preserve">
<value>Editar metadatos de _imagen</value>
</data>
<data name="Title_Edit_image_metadata" xml:space="preserve">
<value>Editar metadatos de imagen</value>
</data>
<data name="Title_Image" xml:space="preserve">
<value>[bold][purple_2]Imagen[/][/]</value>
</data>
<data name="Title_Image_path" xml:space="preserve">
<value>[bold][slateblue1]Ruta a la imagen[/][/]</value>
</data>
<data name="Title_Creator" xml:space="preserve">
<value>[bold][slateblue1]Creador[/][/]</value>
</data>
<data name="Title_Not_set" xml:space="preserve">
<value>No establecido</value>
</data>
<data name="Watermark_Who_created_the_image" xml:space="preserve">
<value>Quién creó la imagen</value>
</data>
<data name="Title_Sequence_is_not_set" xml:space="preserve">
<value>Secuencia no establecida</value>
</data>
<data name="Title_Media_number_no" xml:space="preserve">
<value>[bold][slateblue1]Medio número[/][/]</value>
</data>
<data name="Title_no_in_a_sequence_of_no" xml:space="preserve">
<value>[bold][slateblue1]en una secuencia de[/][/]</value>
</data>
<data name="Title_no_media" xml:space="preserve">
<value>[bold][slateblue1]medios.[/][/]</value>
</data>
<data name="Watermark_Media_title" xml:space="preserve">
<value>Título del medio</value>
</data>
<data name="Watermark_Media_manufacturer" xml:space="preserve">
<value>Fabricante del medio</value>
</data>
<data name="Watermark_Media_model" xml:space="preserve">
<value>Modelo del medio</value>
</data>
<data name="Watermark_Media_serial_number" xml:space="preserve">
<value>Número de serie del medio</value>
</data>
<data name="Watermark_Media_part_number" xml:space="preserve">
<value>Número de parte del medio</value>
</data>
<data name="Watermark_Drive_manufacturer" xml:space="preserve">
<value>Fabricante de la unidad</value>
</data>
<data name="Watermark_Drive_model" xml:space="preserve">
<value>Modelo de la unidad</value>
</data>
<data name="Watermark_Drive_serial_number" xml:space="preserve">
<value>Número de serie de la unidad</value>
</data>
<data name="Watermark_Drive_firmware_revision" xml:space="preserve">
<value>Revisión del firmware de la unidad</value>
</data>
<data name="ButtonLabel_Open" xml:space="preserve">
<value>Abrir</value>
</data>
<data name="ButtonLabel_Load" xml:space="preserve">
<value>Cargar</value>
</data>
<data name="AaruFormat_files" xml:space="preserve">
<value>Archivos AaruFormat</value>
</data>
<data name="Error_reopening_image_for_writing" xml:space="preserve">
<value>Error reabriendo la imagen para escritura.</value>
</data>
<data name="There_was_an_error_reopening_the_image_for_writing" xml:space="preserve">
<value>Ocurrió un error al reabrir la imagen para escritura. Consulte el registro para más información.</value>
</data>
<data name="Error_reopening_image_in_read_only_mode_after_writing_metadata" xml:space="preserve">
<value>Error reabriendo la imagen en sólo lectura después de escribir los metadatos.</value>
</data>
<data name="There_was_an_error_reopening_the_image_in_read_only_mode_after_writing_metadata" xml:space="preserve">
<value>Ocurrió un error al reabrir la imagen en sólo lectura después de escribir los metadatos. Consulte el registro para más información.</value>
</data>
<data name="Metadata_saved_successfully" xml:space="preserve">
<value>Metadatos guardados con éxito.</value>
</data>
<data name="File_is_not_an_AaruFormat_image" xml:space="preserve">
<value>El archivo no es una imagen AaruFormat.</value>
</data>
<data name="AaruFormat_images_version_1_x_are_read_only" xml:space="preserve">
<value>Las imágenes AaruFormat versión 1.x son de sólo lectura, conviértala primero a una versión más moderna.</value>
</data>
</root>

View File

@@ -2137,7 +2137,7 @@ Testers:
<value>Image information</value>
</data>
<data name="Title_Comments" xml:space="preserve">
<value>Comments</value>
<value>[bold][slateblue1]Comments[/][/]</value>
</data>
<data name="Title_Media_information" xml:space="preserve">
<value>Media information</value>
@@ -2670,13 +2670,13 @@ Do you want to continue?</value>
<value>Metadata</value>
</data>
<data name="Title_Title" xml:space="preserve">
<value>Title</value>
<value>[bold][slateblue1]Title[/][/]</value>
</data>
<data name="Title_Barcode" xml:space="preserve">
<value>Barcode</value>
</data>
<data name="Title_Part_number" xml:space="preserve">
<value>Part number</value>
<value>[bold][slateblue1]Part number[/][/]</value>
</data>
<data name="Title_Number_in_sequence" xml:space="preserve">
<value>Number in sequence</value>
@@ -2685,10 +2685,10 @@ Do you want to continue?</value>
<value>Last media of the sequence</value>
</data>
<data name="Title_Drive" xml:space="preserve">
<value>Drive</value>
<value>[bold][red]Drive[/][/]</value>
</data>
<data name="Title_Firmware_revision" xml:space="preserve">
<value>Firmware revision</value>
<value>[bold][slateblue1]Firmware revision[/][/]</value>
</data>
<data name="Title_Existing_Aaru_Metadata_sidecar" xml:space="preserve">
<value>Existing Aaru Metadata sidecar</value>
@@ -3063,7 +3063,7 @@ Do you want to continue?</value>
<comment>"Attributes" abbreviated to the smallest possible size, if allowed by language rules.</comment>
</data>
<data name="Title_Size" xml:space="preserve">
<value>[bold][lime]Size[/][/]</value>
<value>[bold][slateblue1]Size[/][/]</value>
</data>
<data name="Title_Compressed" xml:space="preserve">
<value>[bold][teal]Compressed[/][/]</value>
@@ -3425,4 +3425,103 @@ Do you want to continue?</value>
<data name="Images_have_identical_contents" xml:space="preserve">
<value>[green]Images have identical contents.[/]</value>
</data>
<data name="Menu_Image" xml:space="preserve">
<value>_Image</value>
</data>
<data name="Menu_Metadata" xml:space="preserve">
<value>_Metadata</value>
</data>
<data name="Menu_Edit_image_metadata" xml:space="preserve">
<value>Edit _image metadata</value>
</data>
<data name="Title_Edit_image_metadata" xml:space="preserve">
<value>Edit image metadata</value>
</data>
<data name="Title_Image" xml:space="preserve">
<value>[bold][purple_2]Image[/][/]</value>
</data>
<data name="Title_Image_path" xml:space="preserve">
<value>[bold][slateblue1]Image path[/][/]</value>
</data>
<data name="Title_Creator" xml:space="preserve">
<value>[bold][slateblue1]Creator[/][/]</value>
</data>
<data name="Title_Not_set" xml:space="preserve">
<value>Not set</value>
</data>
<data name="Watermark_Who_created_the_image" xml:space="preserve">
<value>Who created the image</value>
</data>
<data name="Title_Sequence_is_not_set" xml:space="preserve">
<value>Sequence is not set</value>
</data>
<data name="Title_Media_number_no" xml:space="preserve">
<value>[bold][slateblue1]Media number[/][/]</value>
<comment>Followed by media number</comment>
</data>
<data name="Title_no_in_a_sequence_of_no" xml:space="preserve">
<value>[bold][slateblue1]in a sequence of[/][/]</value>
<comment>Follows a media number and is followed by the number of total media</comment>
</data>
<data name="Title_no_media" xml:space="preserve">
<value>[bold][slateblue1]media.[/][/]</value>
<comment>Follows the number of total media in the sequence.</comment>
</data>
<data name="Watermark_Media_title" xml:space="preserve">
<value>Media title</value>
</data>
<data name="Watermark_Media_manufacturer" xml:space="preserve">
<value>Media manufacturer</value>
</data>
<data name="Watermark_Media_model" xml:space="preserve">
<value>Media model</value>
</data>
<data name="Watermark_Media_serial_number" xml:space="preserve">
<value>Media serial number</value>
</data>
<data name="Watermark_Media_part_number" xml:space="preserve">
<value>Media part number</value>
</data>
<data name="Watermark_Drive_manufacturer" xml:space="preserve">
<value>Drive manufacturer</value>
</data>
<data name="Watermark_Drive_model" xml:space="preserve">
<value>Drive model</value>
</data>
<data name="Watermark_Drive_serial_number" xml:space="preserve">
<value>Drive serial number</value>
</data>
<data name="Watermark_Drive_firmware_revision" xml:space="preserve">
<value>Drive firmware revision</value>
</data>
<data name="ButtonLabel_Open" xml:space="preserve">
<value>Open</value>
</data>
<data name="ButtonLabel_Load" xml:space="preserve">
<value>Load</value>
</data>
<data name="AaruFormat_files" xml:space="preserve">
<value>AaruFormat files</value>
</data>
<data name="Error_reopening_image_for_writing" xml:space="preserve">
<value>Error reopening image for writing.</value>
</data>
<data name="There_was_an_error_reopening_the_image_for_writing" xml:space="preserve">
<value>There was an error reopening the image for writing. Check the logs for details.</value>
</data>
<data name="Error_reopening_image_in_read_only_mode_after_writing_metadata" xml:space="preserve">
<value>Error reopening image in read-only mode after writing metadata.</value>
</data>
<data name="There_was_an_error_reopening_the_image_in_read_only_mode_after_writing_metadata" xml:space="preserve">
<value>There was an error reopening the image in read-only mode after writing metadata. Check the logs for details.</value>
</data>
<data name="Metadata_saved_successfully" xml:space="preserve">
<value>Metadata saved successfully.</value>
</data>
<data name="File_is_not_an_AaruFormat_image" xml:space="preserve">
<value>File is not an AaruFormat image.</value>
</data>
<data name="AaruFormat_images_version_1_x_are_read_only" xml:space="preserve">
<value>AaruFormat images version 1.x are read-only, convert it to a newer version first.</value>
</data>
</root>