mirror of
https://github.com/aaru-dps/Aaru.git
synced 2026-07-08 17:56:18 +00:00
[GUI] Add archive information and subdirectory panels with data models
This commit is contained in:
@@ -38,6 +38,7 @@ using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Aaru.CommonTypes.Enums;
|
||||
using Aaru.CommonTypes.Structs;
|
||||
using Aaru.Localization;
|
||||
|
||||
namespace Aaru.CommonTypes.Interfaces;
|
||||
|
||||
@@ -46,28 +47,35 @@ namespace Aaru.CommonTypes.Interfaces;
|
||||
public enum ArchiveSupportedFeature : uint
|
||||
{
|
||||
/// <summary>The archive supports filenames for its entries. If this flag is not set, files can only be accessed by number.</summary>
|
||||
[LocalizedDescription(nameof(UI.Archive_supports_filenames))]
|
||||
SupportsFilenames = 1 << 0,
|
||||
/// <summary>
|
||||
/// The archive supports compression. If this flag is not set, compressed and uncompressed lengths are always the
|
||||
/// same.
|
||||
/// </summary>
|
||||
[LocalizedDescription(nameof(UI.Archive_supports_compression))]
|
||||
SupportsCompression = 1 << 1,
|
||||
/// <summary>
|
||||
/// The archive supports subdirectories. If this flag is not set, all filenames are guaranteed to not contain any
|
||||
/// "/" character.
|
||||
/// </summary>
|
||||
[LocalizedDescription(nameof(UI.Archive_supports_subdirectories))]
|
||||
SupportsSubdirectories = 1 << 2,
|
||||
/// <summary>
|
||||
/// The archive supports explicit entries for directories (like Zip, for example). If this flag is not set,
|
||||
/// directories are implicit by the relative name of the files.
|
||||
/// </summary>
|
||||
[LocalizedDescription(nameof(UI.Archive_has_explicit_directories))]
|
||||
HasExplicitDirectories = 1 << 3,
|
||||
/// <summary>The archive stores a timestamp with each entry if this flag is set.</summary>
|
||||
[LocalizedDescription(nameof(UI.Archive_has_entry_timestamp))]
|
||||
HasEntryTimestamp = 1 << 4,
|
||||
/// <summary>If this flag is set, individual files or the whole archive might be encrypted or password-protected.</summary>
|
||||
[LocalizedDescription(nameof(UI.Archive_supports_protection))]
|
||||
SupportsProtection = 1 << 5, // TODO: not implemented yet
|
||||
|
||||
/// <summary>If this flag is set, the archive supports returning extended attributes (Xattrs) for each entry.</summary>
|
||||
[LocalizedDescription(nameof(UI.Archive_supports_xattrs))]
|
||||
SupportsXAttrs = 1 << 6
|
||||
}
|
||||
|
||||
|
||||
75
Aaru.Gui/Models/ArchiveFileModel.cs
Normal file
75
Aaru.Gui/Models/ArchiveFileModel.cs
Normal file
@@ -0,0 +1,75 @@
|
||||
// /***************************************************************************
|
||||
// Aaru Data Preservation Suite
|
||||
// ----------------------------------------------------------------------------
|
||||
//
|
||||
// Filename : ArchiveFileModel.cs
|
||||
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
||||
//
|
||||
// Component : GUI data models.
|
||||
//
|
||||
// --[ Description ] ----------------------------------------------------------
|
||||
//
|
||||
// Contains information about a file entry inside an archive.
|
||||
//
|
||||
// --[ 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 <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// Copyright © 2011-2026 Natalia Portillo
|
||||
// ****************************************************************************/
|
||||
|
||||
using System;
|
||||
using Aaru.CommonTypes.Structs;
|
||||
using Avalonia.Media;
|
||||
|
||||
namespace Aaru.Gui.Models;
|
||||
|
||||
public sealed class ArchiveFileModel
|
||||
{
|
||||
public int EntryNumber { get; set; }
|
||||
public string Name { get; set; }
|
||||
|
||||
public string Size => $"{Stat?.Length ?? 0}";
|
||||
|
||||
public string CreationTime =>
|
||||
Stat is null || Stat.CreationTime == default(DateTime) ? "" : $"{Stat.CreationTime:G}";
|
||||
|
||||
public string LastAccessTime => Stat is null || Stat.AccessTime == default(DateTime) ? "" : $"{Stat.AccessTime:G}";
|
||||
|
||||
public string ChangedTime => Stat is null || Stat.StatusChangeTime == default(DateTime)
|
||||
? ""
|
||||
: $"{Stat.StatusChangeTime:G}";
|
||||
|
||||
public string LastBackupTime => Stat is null || Stat.BackupTime == default(DateTime) ? "" : $"{Stat.BackupTime:G}";
|
||||
|
||||
public string LastWriteTime => Stat is null || Stat.LastWriteTime == default(DateTime)
|
||||
? ""
|
||||
: $"{Stat.LastWriteTime:G}";
|
||||
|
||||
public string Attributes => Stat is null ? "" : $"{Stat.Attributes}";
|
||||
|
||||
public string Gid => Stat is null ? "" : $"{Stat.GID}";
|
||||
|
||||
public string Uid => Stat is null ? "" : $"{Stat.UID}";
|
||||
|
||||
public string Inode => Stat is null ? "" : $"{Stat.Inode}";
|
||||
|
||||
public string Links => Stat is null ? "" : $"{Stat.Links}";
|
||||
|
||||
public string Mode => Stat is null ? "" : $"{Stat.Mode}";
|
||||
|
||||
public FileEntryInfo Stat { get; set; }
|
||||
public IBrush Color { get; set; }
|
||||
}
|
||||
49
Aaru.Gui/Models/ArchiveModel.cs
Normal file
49
Aaru.Gui/Models/ArchiveModel.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
// /***************************************************************************
|
||||
// Aaru Data Preservation Suite
|
||||
// ----------------------------------------------------------------------------
|
||||
//
|
||||
// Filename : ArchiveModel.cs
|
||||
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
||||
//
|
||||
// Component : GUI data models.
|
||||
//
|
||||
// --[ Description ] ----------------------------------------------------------
|
||||
//
|
||||
// Contains information about an opened archive.
|
||||
//
|
||||
// --[ 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 <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// Copyright © 2011-2026 Natalia Portillo
|
||||
// ****************************************************************************/
|
||||
|
||||
using System.Collections.ObjectModel;
|
||||
using Aaru.CommonTypes.Interfaces;
|
||||
using Aaru.Gui.ViewModels.Panels;
|
||||
using Avalonia.Media;
|
||||
|
||||
namespace Aaru.Gui.Models;
|
||||
|
||||
public sealed class ArchiveModel : RootModel
|
||||
{
|
||||
public string Path { get; set; }
|
||||
public string FileName { get; set; }
|
||||
public IImage Icon { get; set; }
|
||||
public IArchive Archive { get; set; }
|
||||
public IFilter Filter { get; set; }
|
||||
public ArchiveInfoViewModel ViewModel { get; set; }
|
||||
public ObservableCollection<ArchiveSubdirectoryModel> Roots { get; set; } = [];
|
||||
}
|
||||
47
Aaru.Gui/Models/ArchiveSubdirectoryModel.cs
Normal file
47
Aaru.Gui/Models/ArchiveSubdirectoryModel.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
// /***************************************************************************
|
||||
// Aaru Data Preservation Suite
|
||||
// ----------------------------------------------------------------------------
|
||||
//
|
||||
// Filename : ArchiveSubdirectoryModel.cs
|
||||
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
||||
//
|
||||
// Component : GUI data models.
|
||||
//
|
||||
// --[ Description ] ----------------------------------------------------------
|
||||
//
|
||||
// Contains information about a virtual subdirectory inside an archive.
|
||||
//
|
||||
// --[ 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 <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// Copyright © 2011-2026 Natalia Portillo
|
||||
// ****************************************************************************/
|
||||
|
||||
using System.Collections.ObjectModel;
|
||||
using Aaru.CommonTypes.Interfaces;
|
||||
using Avalonia.Media.Imaging;
|
||||
|
||||
namespace Aaru.Gui.Models;
|
||||
|
||||
public sealed class ArchiveSubdirectoryModel
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string Path { get; set; }
|
||||
public IArchive Archive { get; set; }
|
||||
public ObservableCollection<ArchiveSubdirectoryModel> Subdirectories { get; set; } = [];
|
||||
public ObservableCollection<ArchiveFileModel> Entries { get; set; } = [];
|
||||
public Bitmap Icon { get; set; }
|
||||
}
|
||||
61
Aaru.Gui/ViewModels/Panels/ArchiveInfoViewModel.cs
Normal file
61
Aaru.Gui/ViewModels/Panels/ArchiveInfoViewModel.cs
Normal file
@@ -0,0 +1,61 @@
|
||||
// /***************************************************************************
|
||||
// Aaru Data Preservation Suite
|
||||
// ----------------------------------------------------------------------------
|
||||
//
|
||||
// Filename : ArchiveInfoViewModel.cs
|
||||
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
||||
//
|
||||
// Component : GUI view models.
|
||||
//
|
||||
// --[ Description ] ----------------------------------------------------------
|
||||
//
|
||||
// View model and code for the opened archive information panel.
|
||||
//
|
||||
// --[ 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 <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// Copyright © 2011-2026 Natalia Portillo
|
||||
// ****************************************************************************/
|
||||
|
||||
using Aaru.CommonTypes.Interfaces;
|
||||
using Aaru.Localization;
|
||||
using Humanizer;
|
||||
|
||||
namespace Aaru.Gui.ViewModels.Panels;
|
||||
|
||||
public sealed class ArchiveInfoViewModel
|
||||
{
|
||||
public ArchiveInfoViewModel(string path, IFilter filter, IArchive archive)
|
||||
{
|
||||
ArchivePathText = string.Format($"[blue]{path}[/]");
|
||||
FilterText = string.Format(UI.Filter_0, filter.Name);
|
||||
PluginText = string.Format(UI.Archive_plugin_0, archive.Name);
|
||||
PluginIdText = string.Format(UI.Archive_plugin_id_0, archive.Id);
|
||||
NumberOfEntriesText = string.Format(UI.Archive_number_of_entries_0, archive.NumberOfEntries);
|
||||
FeaturesText = string.Format(UI.Archive_features_0, archive.ArchiveFeatures.Humanize());
|
||||
|
||||
archive.GetInformation(filter, null, out string information);
|
||||
InformationText = information ?? "";
|
||||
}
|
||||
|
||||
public string ArchivePathText { get; }
|
||||
public string FilterText { get; }
|
||||
public string PluginText { get; }
|
||||
public string PluginIdText { get; }
|
||||
public string NumberOfEntriesText { get; }
|
||||
public string FeaturesText { get; }
|
||||
public string InformationText { get; }
|
||||
}
|
||||
310
Aaru.Gui/ViewModels/Panels/ArchiveSubdirectoryViewModel.cs
Normal file
310
Aaru.Gui/ViewModels/Panels/ArchiveSubdirectoryViewModel.cs
Normal file
@@ -0,0 +1,310 @@
|
||||
// /***************************************************************************
|
||||
// Aaru Data Preservation Suite
|
||||
// ----------------------------------------------------------------------------
|
||||
//
|
||||
// Filename : ArchiveSubdirectoryViewModel.cs
|
||||
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
||||
//
|
||||
// Component : GUI view models.
|
||||
//
|
||||
// --[ Description ] ----------------------------------------------------------
|
||||
//
|
||||
// View model and code for the archive subdirectory contents panel.
|
||||
//
|
||||
// --[ 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 <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// Copyright © 2011-2026 Natalia Portillo
|
||||
// ****************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using Aaru.CommonTypes.Enums;
|
||||
using Aaru.CommonTypes.Interfaces;
|
||||
using Aaru.CommonTypes.Interop;
|
||||
using Aaru.Core;
|
||||
using Aaru.Gui.Models;
|
||||
using Aaru.Helpers;
|
||||
using Aaru.Localization;
|
||||
using Aaru.Logging;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Media;
|
||||
using Avalonia.Platform.Storage;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using MsBox.Avalonia;
|
||||
using MsBox.Avalonia.Enums;
|
||||
using Sentry;
|
||||
|
||||
namespace Aaru.Gui.ViewModels.Panels;
|
||||
|
||||
public sealed class ArchiveSubdirectoryViewModel
|
||||
{
|
||||
const string MODULE_NAME = "Archive Subdirectory ViewModel";
|
||||
readonly ArchiveSubdirectoryModel _model;
|
||||
readonly Window _view;
|
||||
|
||||
public ArchiveSubdirectoryViewModel(ArchiveSubdirectoryModel model, Window view)
|
||||
{
|
||||
Entries = [];
|
||||
SelectedEntries = [];
|
||||
ExtractFilesCommand = new AsyncRelayCommand(ExtractFiles);
|
||||
_model = model;
|
||||
_view = view;
|
||||
|
||||
foreach(ArchiveFileModel entry in model.Entries)
|
||||
{
|
||||
entry.Color =
|
||||
new SolidColorBrush(Color.Parse(DirColorsParser.Instance.ExtensionColors
|
||||
.TryGetValue(Path.GetExtension(entry.Name),
|
||||
out string hex)
|
||||
? hex
|
||||
: DirColorsParser.Instance.NormalColor));
|
||||
|
||||
Entries.Add(entry);
|
||||
}
|
||||
}
|
||||
|
||||
public ObservableCollection<ArchiveFileModel> Entries { get; }
|
||||
public List<ArchiveFileModel> SelectedEntries { get; }
|
||||
public ICommand ExtractFilesCommand { get; }
|
||||
|
||||
async Task ExtractFiles()
|
||||
{
|
||||
if(SelectedEntries.Count == 0) return;
|
||||
|
||||
IReadOnlyList<IStorageFolder> result =
|
||||
await _view.StorageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions
|
||||
{
|
||||
Title = UI.Dialog_Choose_destination_folder,
|
||||
AllowMultiple = false
|
||||
});
|
||||
|
||||
if(result.Count != 1) return;
|
||||
|
||||
Statistics.AddCommand("extract-files");
|
||||
|
||||
string folder = result[0].Path.AbsolutePath;
|
||||
|
||||
foreach(ArchiveFileModel file in SelectedEntries)
|
||||
{
|
||||
string filename = file.Name;
|
||||
|
||||
ButtonResult mboxResult;
|
||||
|
||||
if(DetectOS.IsWindows)
|
||||
{
|
||||
if(filename.Contains('<') ||
|
||||
filename.Contains('>') ||
|
||||
filename.Contains(':') ||
|
||||
filename.Contains('\\') ||
|
||||
filename.Contains('/') ||
|
||||
filename.Contains('|') ||
|
||||
filename.Contains('?') ||
|
||||
filename.Contains('*') ||
|
||||
filename.Any(static c => c < 32) ||
|
||||
filename.Equals("CON", StringComparison.InvariantCultureIgnoreCase) ||
|
||||
filename.Equals("PRN", StringComparison.InvariantCultureIgnoreCase) ||
|
||||
filename.Equals("AUX", StringComparison.InvariantCultureIgnoreCase) ||
|
||||
filename.Equals("COM1", StringComparison.InvariantCultureIgnoreCase) ||
|
||||
filename.Equals("COM2", StringComparison.InvariantCultureIgnoreCase) ||
|
||||
filename.Equals("COM3", StringComparison.InvariantCultureIgnoreCase) ||
|
||||
filename.Equals("COM4", StringComparison.InvariantCultureIgnoreCase) ||
|
||||
filename.Equals("COM5", StringComparison.InvariantCultureIgnoreCase) ||
|
||||
filename.Equals("COM6", StringComparison.InvariantCultureIgnoreCase) ||
|
||||
filename.Equals("COM7", StringComparison.InvariantCultureIgnoreCase) ||
|
||||
filename.Equals("COM8", StringComparison.InvariantCultureIgnoreCase) ||
|
||||
filename.Equals("COM9", StringComparison.InvariantCultureIgnoreCase) ||
|
||||
filename.Equals("LPT1", StringComparison.InvariantCultureIgnoreCase) ||
|
||||
filename.Equals("LPT2", StringComparison.InvariantCultureIgnoreCase) ||
|
||||
filename.Equals("LPT3", StringComparison.InvariantCultureIgnoreCase) ||
|
||||
filename.Equals("LPT4", StringComparison.InvariantCultureIgnoreCase) ||
|
||||
filename.Equals("LPT5", StringComparison.InvariantCultureIgnoreCase) ||
|
||||
filename.Equals("LPT6", StringComparison.InvariantCultureIgnoreCase) ||
|
||||
filename.Equals("LPT7", StringComparison.InvariantCultureIgnoreCase) ||
|
||||
filename.Equals("LPT8", StringComparison.InvariantCultureIgnoreCase) ||
|
||||
filename.Equals("LPT9", StringComparison.InvariantCultureIgnoreCase) ||
|
||||
filename[^1] == '.' ||
|
||||
filename[^1] == ' ')
|
||||
{
|
||||
char[] chars;
|
||||
|
||||
if(filename[^1] == '.' || filename[^1] == ' ')
|
||||
chars = new char[filename.Length - 1];
|
||||
else
|
||||
chars = new char[filename.Length];
|
||||
|
||||
for(var ci = 0; ci < chars.Length; ci++)
|
||||
{
|
||||
chars[ci] = filename[ci] switch
|
||||
{
|
||||
'<'
|
||||
or '>'
|
||||
or ':'
|
||||
or '\\'
|
||||
or '/'
|
||||
or '|'
|
||||
or '?'
|
||||
or '*'
|
||||
or >= '\u0000' and <= '\u001F' => '_',
|
||||
_ => filename[ci]
|
||||
};
|
||||
}
|
||||
|
||||
if(filename.StartsWith("CON", StringComparison.InvariantCultureIgnoreCase) ||
|
||||
filename.StartsWith("PRN", StringComparison.InvariantCultureIgnoreCase) ||
|
||||
filename.StartsWith("AUX", StringComparison.InvariantCultureIgnoreCase) ||
|
||||
filename.StartsWith("COM", StringComparison.InvariantCultureIgnoreCase) ||
|
||||
filename.StartsWith("LPT", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
chars[0] = '_';
|
||||
chars[1] = '_';
|
||||
chars[2] = '_';
|
||||
}
|
||||
|
||||
string corrected = new(chars);
|
||||
|
||||
mboxResult = await MessageBoxManager.GetMessageBoxStandard(UI.Unsupported_filename,
|
||||
string
|
||||
.Format(UI
|
||||
.Filename_0_not_supported_want_to_rename_to_1,
|
||||
filename,
|
||||
corrected),
|
||||
ButtonEnum.YesNoCancel,
|
||||
Icon.Warning)
|
||||
.ShowWindowDialogAsync(_view);
|
||||
|
||||
switch(mboxResult)
|
||||
{
|
||||
case ButtonResult.Cancel:
|
||||
return;
|
||||
case ButtonResult.No:
|
||||
continue;
|
||||
default:
|
||||
filename = corrected;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string outputPath = Path.Combine(folder, filename);
|
||||
|
||||
if(File.Exists(outputPath))
|
||||
{
|
||||
mboxResult = await MessageBoxManager.GetMessageBoxStandard(UI.Existing_file,
|
||||
string
|
||||
.Format(UI.File_named_0_exists_overwrite_Q,
|
||||
filename),
|
||||
ButtonEnum.YesNoCancel,
|
||||
Icon.Warning)
|
||||
.ShowWindowDialogAsync(_view);
|
||||
|
||||
switch(mboxResult)
|
||||
{
|
||||
case ButtonResult.Cancel:
|
||||
return;
|
||||
case ButtonResult.No:
|
||||
continue;
|
||||
default:
|
||||
try
|
||||
{
|
||||
File.Delete(outputPath);
|
||||
}
|
||||
catch(IOException)
|
||||
{
|
||||
mboxResult = await MessageBoxManager.GetMessageBoxStandard(UI.Cannot_delete,
|
||||
UI.Could_note_delete_existe_file_continue_Q,
|
||||
ButtonEnum.YesNo,
|
||||
Icon.Error)
|
||||
.ShowWindowDialogAsync(_view);
|
||||
|
||||
if(mboxResult == ButtonResult.No) return;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
ErrorNumber error = _model.Archive.GetEntry(file.EntryNumber, out IFilter entryFilter);
|
||||
|
||||
if(error != ErrorNumber.NoError || entryFilter is null)
|
||||
{
|
||||
AaruLogging.Debug(MODULE_NAME, string.Format(UI.Skipped_non_regular_archive_entry_0, file.Name));
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
using(Stream inStream = entryFilter.GetDataForkStream())
|
||||
{
|
||||
await using(var fs = new FileStream(outputPath,
|
||||
FileMode.CreateNew,
|
||||
FileAccess.ReadWrite,
|
||||
FileShare.None))
|
||||
{
|
||||
await inStream.CopyToAsync(fs);
|
||||
}
|
||||
}
|
||||
|
||||
var fi = new FileInfo(outputPath);
|
||||
|
||||
try
|
||||
{
|
||||
if(file.Stat?.CreationTimeUtc is not null) fi.CreationTimeUtc = file.Stat.CreationTimeUtc.Value;
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
SentrySdk.CaptureException(ex);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if(file.Stat?.LastWriteTimeUtc is not null) fi.LastWriteTimeUtc = file.Stat.LastWriteTimeUtc.Value;
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
SentrySdk.CaptureException(ex);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if(file.Stat?.AccessTimeUtc is not null) fi.LastAccessTimeUtc = file.Stat.AccessTimeUtc.Value;
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
SentrySdk.CaptureException(ex);
|
||||
}
|
||||
}
|
||||
catch(IOException)
|
||||
{
|
||||
mboxResult = await MessageBoxManager.GetMessageBoxStandard(UI.Cannot_create_file,
|
||||
UI
|
||||
.Could_not_create_destination_file_continue_Q,
|
||||
ButtonEnum.YesNo,
|
||||
Icon.Error)
|
||||
.ShowWindowDialogAsync(_view);
|
||||
|
||||
if(mboxResult == ButtonResult.No) return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -41,6 +41,7 @@ using Aaru.CommonTypes;
|
||||
using Aaru.CommonTypes.Enums;
|
||||
using Aaru.CommonTypes.Interfaces;
|
||||
using Aaru.CommonTypes.Interop;
|
||||
using Aaru.CommonTypes.Structs;
|
||||
using Aaru.Core;
|
||||
using Aaru.Database;
|
||||
using Aaru.Gui.Models;
|
||||
@@ -67,6 +68,7 @@ using MsBox.Avalonia.Dto;
|
||||
using MsBox.Avalonia.Enums;
|
||||
using MsBox.Avalonia.Models;
|
||||
using Spectre.Console;
|
||||
using ArchiveInfo = Aaru.Gui.Views.Panels.ArchiveInfo;
|
||||
using Console = Aaru.Gui.Views.Dialogs.Console;
|
||||
using FileSystem = Aaru.CommonTypes.AaruMetadata.FileSystem;
|
||||
using ImageInfo = Aaru.Gui.Views.Panels.ImageInfo;
|
||||
@@ -83,7 +85,10 @@ public partial class MainWindowViewModel : ViewModelBase
|
||||
readonly SvgImage _genericOpticalIcon;
|
||||
readonly SvgImage _genericTapeIcon;
|
||||
readonly Window _view;
|
||||
Console _console;
|
||||
ArchiveModel _archive;
|
||||
[ObservableProperty]
|
||||
bool _archiveLoaded;
|
||||
Console _console;
|
||||
[ObservableProperty]
|
||||
[CanBeNull]
|
||||
object _contentPanel;
|
||||
@@ -107,13 +112,13 @@ public partial class MainWindowViewModel : ViewModelBase
|
||||
SettingsCommand = new AsyncRelayCommand(SettingsAsync);
|
||||
ConsoleCommand = new RelayCommand(Console);
|
||||
OpenCommand = new AsyncRelayCommand(OpenAsync);
|
||||
CalculateEntropyCommand = new RelayCommand(CalculateEntropy);
|
||||
VerifyImageCommand = new RelayCommand(VerifyImage);
|
||||
ChecksumImageCommand = new RelayCommand(ChecksumImage);
|
||||
ConvertImageCommand = new RelayCommand(ConvertImage);
|
||||
CreateSidecarCommand = new RelayCommand(CreateSidecar);
|
||||
ViewImageSectorsCommand = new RelayCommand(ViewImageSectors);
|
||||
DecodeImageMediaTagsCommand = new RelayCommand(DecodeImageMediaTags);
|
||||
CalculateEntropyCommand = new RelayCommand(CalculateEntropy, () => ImageLoaded);
|
||||
VerifyImageCommand = new RelayCommand(VerifyImage, () => ImageLoaded);
|
||||
ChecksumImageCommand = new RelayCommand(ChecksumImage, () => ImageLoaded);
|
||||
ConvertImageCommand = new RelayCommand(ConvertImage, () => ImageLoaded);
|
||||
CreateSidecarCommand = new RelayCommand(CreateSidecar, () => ImageLoaded);
|
||||
ViewImageSectorsCommand = new RelayCommand(ViewImageSectors, () => ImageLoaded);
|
||||
DecodeImageMediaTagsCommand = new RelayCommand(DecodeImageMediaTags, () => ImageLoaded);
|
||||
OpenMhddLogCommand = new AsyncRelayCommand(OpenMhddLogAsync);
|
||||
OpenIbgLogCommand = new AsyncRelayCommand(OpenIbgLogAsync);
|
||||
ConnectToRemoteCommand = new AsyncRelayCommand(ConnectToRemoteAsync);
|
||||
@@ -240,6 +245,20 @@ public partial class MainWindowViewModel : ViewModelBase
|
||||
DataContext = new SubdirectoryViewModel(subdirectoryModel, _view)
|
||||
};
|
||||
|
||||
break;
|
||||
case ArchiveModel archiveModel:
|
||||
ContentPanel = new ArchiveInfo
|
||||
{
|
||||
DataContext = archiveModel.ViewModel
|
||||
};
|
||||
|
||||
break;
|
||||
case ArchiveSubdirectoryModel archiveSubdirectoryModel:
|
||||
ContentPanel = new ArchiveSubdirectory
|
||||
{
|
||||
DataContext = new ArchiveSubdirectoryViewModel(archiveSubdirectoryModel, _view)
|
||||
};
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -388,7 +407,7 @@ public partial class MainWindowViewModel : ViewModelBase
|
||||
// TODO: Extensions
|
||||
IReadOnlyList<IStorageFile> result = await _view.StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
|
||||
{
|
||||
Title = UI.Dialog_Choose_image_to_open,
|
||||
Title = UI.Dialog_Choose_file_to_open,
|
||||
AllowMultiple = false,
|
||||
FileTypeFilter = [FilePickerFileTypes.All]
|
||||
});
|
||||
@@ -417,12 +436,22 @@ public partial class MainWindowViewModel : ViewModelBase
|
||||
// Detect the image format of the selected file
|
||||
if(ImageFormat.Detect(inputFilter) is not IMediaImage imageFormat)
|
||||
{
|
||||
IMsBox<ButtonResult> msbox = MessageBoxManager.GetMessageBoxStandard(UI.Title_Error,
|
||||
UI.Image_format_not_identified,
|
||||
ButtonEnum.Ok,
|
||||
Icon.Error);
|
||||
// Try archive format as fallback
|
||||
IArchive archiveFormat = ArchiveFormat.Detect(inputFilter);
|
||||
|
||||
await msbox.ShowAsync();
|
||||
if(archiveFormat is null)
|
||||
{
|
||||
IMsBox<ButtonResult> msbox = MessageBoxManager.GetMessageBoxStandard(UI.Title_Error,
|
||||
UI.Image_format_not_identified,
|
||||
ButtonEnum.Ok,
|
||||
Icon.Error);
|
||||
|
||||
await msbox.ShowAsync();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
await OpenArchiveAsync(archiveFormat, inputFilter, result[0].Path.LocalPath);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -666,6 +695,11 @@ public partial class MainWindowViewModel : ViewModelBase
|
||||
Statistics.AddMedia(imageFormat.Info.MediaType, false);
|
||||
Statistics.AddFilter(inputFilter.Name);
|
||||
|
||||
// Close any previously opened archive before replacing the tree
|
||||
_archive?.Archive?.Close();
|
||||
_archive = null;
|
||||
ArchiveLoaded = false;
|
||||
|
||||
TreeRoot.Clear();
|
||||
TreeRoot.Add(imageModel);
|
||||
Title = $"Aaru - {imageModel.FileName}";
|
||||
@@ -702,6 +736,144 @@ public partial class MainWindowViewModel : ViewModelBase
|
||||
Statistics.AddCommand("image-info");
|
||||
}
|
||||
|
||||
async Task OpenArchiveAsync(IArchive archiveFormat, IFilter inputFilter, string path)
|
||||
{
|
||||
AaruLogging.WriteLine(UI.Archive_format_identified_by_0_1, Markup.Escape(archiveFormat.Name), archiveFormat.Id);
|
||||
|
||||
try
|
||||
{
|
||||
ErrorNumber opened = archiveFormat.Open(inputFilter, null);
|
||||
|
||||
if(opened != ErrorNumber.NoError)
|
||||
{
|
||||
IMsBox<ButtonResult> msbox = MessageBoxManager.GetMessageBoxStandard(UI.Title_Error,
|
||||
string.Format(UI.Error_0_opening_archive_format, opened),
|
||||
ButtonEnum.Ok,
|
||||
Icon.Error);
|
||||
|
||||
await msbox.ShowAsync();
|
||||
|
||||
AaruLogging.Error(UI.Unable_to_open_archive_format);
|
||||
AaruLogging.Error(UI.No_error_given);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var archiveModel = new ArchiveModel
|
||||
{
|
||||
Path = path,
|
||||
FileName = Path.GetFileName(path),
|
||||
Icon = _genericFolderIcon,
|
||||
Archive = archiveFormat,
|
||||
Filter = inputFilter,
|
||||
ViewModel = new ArchiveInfoViewModel(path, inputFilter, archiveFormat)
|
||||
};
|
||||
|
||||
var rootDir = new ArchiveSubdirectoryModel
|
||||
{
|
||||
Name = "/",
|
||||
Path = "",
|
||||
Archive = archiveFormat
|
||||
};
|
||||
|
||||
bool supportsSubdirs =
|
||||
archiveFormat.ArchiveFeatures.HasFlag(ArchiveSupportedFeature.SupportsSubdirectories);
|
||||
|
||||
for(var entryNumber = 0; entryNumber < archiveFormat.NumberOfEntries; entryNumber++)
|
||||
{
|
||||
ErrorNumber nameErr = archiveFormat.GetFilename(entryNumber, out string entryName);
|
||||
|
||||
if(nameErr != ErrorNumber.NoError || string.IsNullOrEmpty(entryName)) continue;
|
||||
|
||||
archiveFormat.Stat(entryNumber, out FileEntryInfo stat);
|
||||
|
||||
if(!supportsSubdirs || !entryName.Contains('/'))
|
||||
{
|
||||
rootDir.Entries.Add(new ArchiveFileModel
|
||||
{
|
||||
EntryNumber = entryNumber,
|
||||
Name = entryName,
|
||||
Stat = stat
|
||||
});
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
string[] parts = entryName.Split('/');
|
||||
ArchiveSubdirectoryModel current = rootDir;
|
||||
|
||||
for(var i = 0; i < parts.Length - 1; i++)
|
||||
{
|
||||
string part = parts[i];
|
||||
|
||||
if(string.IsNullOrEmpty(part)) continue;
|
||||
|
||||
ArchiveSubdirectoryModel child = current.Subdirectories.FirstOrDefault(s => s.Name == part);
|
||||
|
||||
if(child == null)
|
||||
{
|
||||
child = new ArchiveSubdirectoryModel
|
||||
{
|
||||
Name = part,
|
||||
Path = string.Join('/', parts, 0, i + 1),
|
||||
Archive = archiveFormat
|
||||
};
|
||||
|
||||
current.Subdirectories.Add(child);
|
||||
}
|
||||
|
||||
current = child;
|
||||
}
|
||||
|
||||
// If the entry itself is a directory (trailing slash) skip adding as a file
|
||||
string leaf = parts[^1];
|
||||
|
||||
if(string.IsNullOrEmpty(leaf))
|
||||
{
|
||||
// Entry represents a directory; ensure it's present as subdirectory
|
||||
continue;
|
||||
}
|
||||
|
||||
current.Entries.Add(new ArchiveFileModel
|
||||
{
|
||||
EntryNumber = entryNumber,
|
||||
Name = leaf,
|
||||
Stat = stat
|
||||
});
|
||||
}
|
||||
|
||||
archiveModel.Roots.Add(rootDir);
|
||||
|
||||
Statistics.AddFilter(inputFilter.Name);
|
||||
|
||||
// Close any previously opened archive before replacing the tree
|
||||
_archive?.Archive?.Close();
|
||||
|
||||
TreeRoot.Clear();
|
||||
TreeRoot.Add(archiveModel);
|
||||
Title = $"Aaru - {archiveModel.FileName}";
|
||||
_image = null;
|
||||
ImageLoaded = false;
|
||||
_archive = archiveModel;
|
||||
ArchiveLoaded = true;
|
||||
|
||||
Statistics.AddCommand("archive-info");
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
IMsBox<ButtonResult> msbox = MessageBoxManager.GetMessageBoxStandard(UI.Title_Error,
|
||||
UI.Unable_to_open_archive_format,
|
||||
ButtonEnum.Ok,
|
||||
Icon.Error);
|
||||
|
||||
await msbox.ShowAsync();
|
||||
|
||||
AaruLogging.Error(UI.Unable_to_open_archive_format);
|
||||
AaruLogging.Error(Aaru.Localization.Core.Error_0, ex.Message);
|
||||
AaruLogging.Exception(ex, Aaru.Localization.Core.Error_0, ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
Task AboutAsync()
|
||||
{
|
||||
var dialog = new About();
|
||||
@@ -757,8 +929,24 @@ public partial class MainWindowViewModel : ViewModelBase
|
||||
return dialog.ShowDialog(_view);
|
||||
}
|
||||
|
||||
internal void Exit() =>
|
||||
internal void Exit()
|
||||
{
|
||||
_archive?.Archive?.Close();
|
||||
_archive = null;
|
||||
|
||||
(Application.Current?.ApplicationLifetime as ClassicDesktopStyleApplicationLifetime)?.Shutdown();
|
||||
}
|
||||
|
||||
partial void OnImageLoadedChanged(bool value)
|
||||
{
|
||||
(CalculateEntropyCommand as IRelayCommand)?.NotifyCanExecuteChanged();
|
||||
(VerifyImageCommand as IRelayCommand)?.NotifyCanExecuteChanged();
|
||||
(ChecksumImageCommand as IRelayCommand)?.NotifyCanExecuteChanged();
|
||||
(ConvertImageCommand as IRelayCommand)?.NotifyCanExecuteChanged();
|
||||
(CreateSidecarCommand as IRelayCommand)?.NotifyCanExecuteChanged();
|
||||
(ViewImageSectorsCommand as IRelayCommand)?.NotifyCanExecuteChanged();
|
||||
(DecodeImageMediaTagsCommand as IRelayCommand)?.NotifyCanExecuteChanged();
|
||||
}
|
||||
|
||||
void Console()
|
||||
{
|
||||
|
||||
82
Aaru.Gui/Views/Panels/ArchiveInfo.xaml
Normal file
82
Aaru.Gui/Views/Panels/ArchiveInfo.xaml
Normal file
@@ -0,0 +1,82 @@
|
||||
<!--
|
||||
// /***************************************************************************
|
||||
// Aaru Data Preservation Suite
|
||||
// ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐
|
||||
//
|
||||
// Filename : ArchiveInfo.xaml
|
||||
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
||||
//
|
||||
// Component : GUI panel.
|
||||
//
|
||||
// ‐‐[ Description ] ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐
|
||||
//
|
||||
// Archive information panel.
|
||||
//
|
||||
// ‐‐[ 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 <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐
|
||||
// Copyright © 2011-2026 Natalia Portillo
|
||||
// ****************************************************************************/
|
||||
-->
|
||||
<UserControl 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:panels="clr-namespace:Aaru.Gui.ViewModels.Panels"
|
||||
xmlns:localization="clr-namespace:Aaru.Localization;assembly=Aaru.Localization"
|
||||
xmlns:controls="clr-namespace:Aaru.Gui.Controls"
|
||||
mc:Ignorable="d"
|
||||
d:DesignWidth="800"
|
||||
d:DesignHeight="450"
|
||||
x:DataType="panels:ArchiveInfoViewModel"
|
||||
x:Class="Aaru.Gui.Views.Panels.ArchiveInfo">
|
||||
<Grid RowDefinitions="Auto,*"
|
||||
Margin="12"
|
||||
RowSpacing="8">
|
||||
<StackPanel Grid.Row="0"
|
||||
Spacing="8">
|
||||
<controls:SpectreTextBlock FontWeight="Bold"
|
||||
Text="{x:Static localization:UI.Title_Archive_information}" />
|
||||
<controls:SpectreTextBlock Text="{Binding ArchivePathText, Mode=OneWay}"
|
||||
TextWrapping="Wrap" />
|
||||
<controls:SpectreTextBlock Text="{Binding FilterText, Mode=OneWay}"
|
||||
TextWrapping="Wrap" />
|
||||
<controls:SpectreTextBlock Text="{Binding PluginText, Mode=OneWay}"
|
||||
TextWrapping="Wrap" />
|
||||
<controls:SpectreTextBlock Text="{Binding PluginIdText, Mode=OneWay}"
|
||||
TextWrapping="Wrap" />
|
||||
<controls:SpectreTextBlock Text="{Binding NumberOfEntriesText, Mode=OneWay}"
|
||||
TextWrapping="Wrap" />
|
||||
<controls:SpectreTextBlock Text="{Binding FeaturesText, Mode=OneWay}"
|
||||
TextWrapping="Wrap" />
|
||||
</StackPanel>
|
||||
<Border Grid.Row="1"
|
||||
BorderThickness="1"
|
||||
BorderBrush="{DynamicResource SystemControlForegroundBaseMediumLowBrush}"
|
||||
CornerRadius="8"
|
||||
IsVisible="{Binding !!InformationText, Mode=OneWay}">
|
||||
<StackPanel Spacing="8"
|
||||
Margin="12">
|
||||
<controls:SpectreTextBlock FontWeight="Bold"
|
||||
Text="{x:Static localization:UI.Title_Details}" />
|
||||
<ScrollViewer>
|
||||
<controls:SpectreTextBlock Text="{Binding InformationText, Mode=OneWay}"
|
||||
TextWrapping="Wrap" />
|
||||
</ScrollViewer>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
43
Aaru.Gui/Views/Panels/ArchiveInfo.xaml.cs
Normal file
43
Aaru.Gui/Views/Panels/ArchiveInfo.xaml.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
// /***************************************************************************
|
||||
// Aaru Data Preservation Suite
|
||||
// ----------------------------------------------------------------------------
|
||||
//
|
||||
// Filename : ArchiveInfo.xaml.cs
|
||||
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
||||
//
|
||||
// Component : GUI panels.
|
||||
//
|
||||
// --[ Description ] ----------------------------------------------------------
|
||||
//
|
||||
// Archive information panel.
|
||||
//
|
||||
// --[ 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 <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// Copyright © 2011-2026 Natalia Portillo
|
||||
// ****************************************************************************/
|
||||
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
|
||||
namespace Aaru.Gui.Views.Panels;
|
||||
|
||||
public sealed class ArchiveInfo : UserControl
|
||||
{
|
||||
public ArchiveInfo() => InitializeComponent();
|
||||
|
||||
void InitializeComponent() => AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
108
Aaru.Gui/Views/Panels/ArchiveSubdirectory.xaml
Normal file
108
Aaru.Gui/Views/Panels/ArchiveSubdirectory.xaml
Normal file
@@ -0,0 +1,108 @@
|
||||
<!--
|
||||
// /***************************************************************************
|
||||
// Aaru Data Preservation Suite
|
||||
// ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐
|
||||
//
|
||||
// Filename : ArchiveSubdirectory.xaml
|
||||
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
||||
//
|
||||
// Component : GUI panel.
|
||||
//
|
||||
// ‐‐[ Description ] ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐
|
||||
//
|
||||
// Archive subdirectory contents panel.
|
||||
//
|
||||
// ‐‐[ 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 <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐
|
||||
// Copyright © 2011-2026 Natalia Portillo
|
||||
// ****************************************************************************/
|
||||
-->
|
||||
<UserControl 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:panels="clr-namespace:Aaru.Gui.ViewModels.Panels"
|
||||
xmlns:localization="clr-namespace:Aaru.Localization;assembly=Aaru.Localization"
|
||||
xmlns:controls="clr-namespace:Aaru.Gui.Controls"
|
||||
mc:Ignorable="d"
|
||||
d:DesignWidth="800"
|
||||
d:DesignHeight="450"
|
||||
x:DataType="panels:ArchiveSubdirectoryViewModel"
|
||||
x:Class="Aaru.Gui.Views.Panels.ArchiveSubdirectory">
|
||||
<DataGrid Margin="12"
|
||||
ItemsSource="{Binding Entries, Mode=OneWay}"
|
||||
IsReadOnly="True"
|
||||
SelectionMode="Extended">
|
||||
<DataGrid.Styles>
|
||||
<Style Selector="DataGridRow">
|
||||
<Setter Property="Foreground"
|
||||
Value="{ReflectionBinding Color, Mode=OneWay}" />
|
||||
</Style>
|
||||
</DataGrid.Styles>
|
||||
<DataGrid.ContextMenu>
|
||||
<ContextMenu>
|
||||
<Button Command="{Binding ExtractFilesCommand, Mode=OneWay}">
|
||||
<TextBlock Text="{x:Static localization:UI.ButtonLabel_Extract_to}" />
|
||||
</Button>
|
||||
</ContextMenu>
|
||||
</DataGrid.ContextMenu>
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Binding="{Binding Name, Mode=OneWay}"
|
||||
Width="Auto"
|
||||
IsReadOnly="True">
|
||||
<DataGridTextColumn.Header>
|
||||
<controls:SpectreTextBlock Text="{x:Static localization:UI.Title_Name}" />
|
||||
</DataGridTextColumn.Header>
|
||||
</DataGridTextColumn>
|
||||
<DataGridTextColumn Binding="{Binding Size, Mode=OneWay}"
|
||||
Width="Auto"
|
||||
IsReadOnly="True">
|
||||
<DataGridTextColumn.Header>
|
||||
<controls:SpectreTextBlock Text="{x:Static localization:UI.Title_Length}" />
|
||||
</DataGridTextColumn.Header>
|
||||
</DataGridTextColumn>
|
||||
<DataGridTextColumn Binding="{Binding CreationTime, Mode=OneWay}"
|
||||
Width="Auto"
|
||||
IsReadOnly="True">
|
||||
<DataGridTextColumn.Header>
|
||||
<controls:SpectreTextBlock Text="{x:Static localization:UI.Title_Creation}" />
|
||||
</DataGridTextColumn.Header>
|
||||
</DataGridTextColumn>
|
||||
<DataGridTextColumn Binding="{Binding LastAccessTime, Mode=OneWay}"
|
||||
Width="Auto"
|
||||
IsReadOnly="True">
|
||||
<DataGridTextColumn.Header>
|
||||
<controls:SpectreTextBlock Text="{x:Static localization:UI.Title_Last_access}" />
|
||||
</DataGridTextColumn.Header>
|
||||
</DataGridTextColumn>
|
||||
<DataGridTextColumn Binding="{Binding LastWriteTime, Mode=OneWay}"
|
||||
Width="Auto"
|
||||
IsReadOnly="True">
|
||||
<DataGridTextColumn.Header>
|
||||
<controls:SpectreTextBlock Text="{x:Static localization:UI.Title_Last_write}" />
|
||||
</DataGridTextColumn.Header>
|
||||
</DataGridTextColumn>
|
||||
<DataGridTextColumn Binding="{Binding Attributes, Mode=OneWay}"
|
||||
Width="Auto"
|
||||
IsReadOnly="True">
|
||||
<DataGridTextColumn.Header>
|
||||
<controls:SpectreTextBlock Text="{x:Static localization:UI.Title_Attributes}" />
|
||||
</DataGridTextColumn.Header>
|
||||
</DataGridTextColumn>
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
</UserControl>
|
||||
43
Aaru.Gui/Views/Panels/ArchiveSubdirectory.xaml.cs
Normal file
43
Aaru.Gui/Views/Panels/ArchiveSubdirectory.xaml.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
// /***************************************************************************
|
||||
// Aaru Data Preservation Suite
|
||||
// ----------------------------------------------------------------------------
|
||||
//
|
||||
// Filename : ArchiveSubdirectory.xaml.cs
|
||||
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
||||
//
|
||||
// Component : GUI panels.
|
||||
//
|
||||
// --[ Description ] ----------------------------------------------------------
|
||||
//
|
||||
// Archive subdirectory contents panel.
|
||||
//
|
||||
// --[ 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 <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// Copyright © 2011-2026 Natalia Portillo
|
||||
// ****************************************************************************/
|
||||
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
|
||||
namespace Aaru.Gui.Views.Panels;
|
||||
|
||||
public sealed class ArchiveSubdirectory : UserControl
|
||||
{
|
||||
public ArchiveSubdirectory() => InitializeComponent();
|
||||
|
||||
void InitializeComponent() => AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
@@ -325,6 +325,28 @@
|
||||
VerticalAlignment="Center" />
|
||||
</StackPanel>
|
||||
</TreeDataTemplate>
|
||||
<TreeDataTemplate DataType="models:ArchiveModel"
|
||||
ItemsSource="{Binding Roots, Mode=OneWay}">
|
||||
<StackPanel Orientation="Horizontal"
|
||||
Spacing="4">
|
||||
<Image Width="24"
|
||||
Height="24"
|
||||
Source="{Binding Icon, Mode=OneWay}" />
|
||||
<TextBlock Text="{Binding FileName, Mode=OneWay}"
|
||||
VerticalAlignment="Center" />
|
||||
</StackPanel>
|
||||
</TreeDataTemplate>
|
||||
<TreeDataTemplate DataType="models:ArchiveSubdirectoryModel"
|
||||
ItemsSource="{Binding Subdirectories, Mode=OneWay}">
|
||||
<StackPanel Orientation="Horizontal"
|
||||
Spacing="4">
|
||||
<Svg Width="24"
|
||||
Height="24"
|
||||
Path="/Assets/Icons/oxygen/inode-directory.svg" />
|
||||
<TextBlock Text="{Binding Name, Mode=OneWay}"
|
||||
VerticalAlignment="Center" />
|
||||
</StackPanel>
|
||||
</TreeDataTemplate>
|
||||
<DataTemplate DataType="models:RootModel">
|
||||
<TextBlock Text="{Binding Name, Mode=OneWay}" />
|
||||
</DataTemplate>
|
||||
|
||||
3840
Aaru.Localization/UI.Designer.cs
generated
3840
Aaru.Localization/UI.Designer.cs
generated
File diff suppressed because it is too large
Load Diff
@@ -5623,4 +5623,49 @@ Probadores:
|
||||
<data name="Maximum_error_recovery_is_100" xml:space="preserve">
|
||||
<value>La recuperación de errores máxima es 100%</value>
|
||||
</data>
|
||||
<data name="Dialog_Choose_file_to_open" xml:space="preserve">
|
||||
<value>Elegir archivo a abrir</value>
|
||||
</data>
|
||||
<data name="Error_0_opening_archive_format" xml:space="preserve">
|
||||
<value>Error {0} al abrir el formato de archivo.</value>
|
||||
</data>
|
||||
<data name="Title_Archive_information" xml:space="preserve">
|
||||
<value>[bold][slateblue1]Información del archivo[/][/]</value>
|
||||
</data>
|
||||
<data name="Archive_plugin_0" xml:space="preserve">
|
||||
<value>[slateblue1]Plugin:[/] [red]{0}[/]</value>
|
||||
</data>
|
||||
<data name="Archive_plugin_id_0" xml:space="preserve">
|
||||
<value>[slateblue1]ID del plugin:[/] [purple]{0}[/]</value>
|
||||
</data>
|
||||
<data name="Archive_number_of_entries_0" xml:space="preserve">
|
||||
<value>[slateblue1]Número de entradas:[/] [aqua]{0}[/]</value>
|
||||
</data>
|
||||
<data name="Archive_features_0" xml:space="preserve">
|
||||
<value>[slateblue1]Características:[/] [orange3]{0}[/]</value>
|
||||
</data>
|
||||
<data name="Skipped_non_regular_archive_entry_0" xml:space="preserve">
|
||||
<value>Se omitió la entrada de archivo no regular {0}.</value>
|
||||
</data>
|
||||
<data name="Archive_supports_filenames" xml:space="preserve">
|
||||
<value>Soporta nombres de archivos</value>
|
||||
</data>
|
||||
<data name="Archive_supports_compression" xml:space="preserve">
|
||||
<value>Soporta compresión</value>
|
||||
</data>
|
||||
<data name="Archive_supports_subdirectories" xml:space="preserve">
|
||||
<value>Soporta subdirectorios</value>
|
||||
</data>
|
||||
<data name="Archive_has_explicit_directories" xml:space="preserve">
|
||||
<value>Tiene subdirectories explícitos</value>
|
||||
</data>
|
||||
<data name="Archive_has_entry_timestamp" xml:space="preserve">
|
||||
<value>Tiene marcas de tiempo</value>
|
||||
</data>
|
||||
<data name="Archive_supports_protection" xml:space="preserve">
|
||||
<value>Soporta protección</value>
|
||||
</data>
|
||||
<data name="Archive_supports_xattrs" xml:space="preserve">
|
||||
<value>Soporta atributos extendidos</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -5707,4 +5707,49 @@ Do you want to continue?</value>
|
||||
<data name="Maximum_error_recovery_is_100" xml:space="preserve">
|
||||
<value>Maximum error recovery is 100%</value>
|
||||
</data>
|
||||
<data name="Dialog_Choose_file_to_open" xml:space="preserve">
|
||||
<value>Choose file to open</value>
|
||||
</data>
|
||||
<data name="Error_0_opening_archive_format" xml:space="preserve">
|
||||
<value>Error {0} opening archive format.</value>
|
||||
</data>
|
||||
<data name="Title_Archive_information" xml:space="preserve">
|
||||
<value>[bold][slateblue1]Archive information[/][/]</value>
|
||||
</data>
|
||||
<data name="Archive_plugin_0" xml:space="preserve">
|
||||
<value>[slateblue1]Plugin:[/] [red]{0}[/]</value>
|
||||
</data>
|
||||
<data name="Archive_plugin_id_0" xml:space="preserve">
|
||||
<value>[slateblue1]Plugin ID:[/] [purple]{0}[/]</value>
|
||||
</data>
|
||||
<data name="Archive_number_of_entries_0" xml:space="preserve">
|
||||
<value>[slateblue1]Number of entries:[/] [aqua]{0}[/]</value>
|
||||
</data>
|
||||
<data name="Archive_features_0" xml:space="preserve">
|
||||
<value>[slateblue1]Features:[/] [orange3]{0}[/]</value>
|
||||
</data>
|
||||
<data name="Skipped_non_regular_archive_entry_0" xml:space="preserve">
|
||||
<value>Skipped non-regular archive entry {0}.</value>
|
||||
</data>
|
||||
<data name="Archive_supports_filenames" xml:space="preserve">
|
||||
<value>Supports filenames</value>
|
||||
</data>
|
||||
<data name="Archive_supports_compression" xml:space="preserve">
|
||||
<value>Supports compression</value>
|
||||
</data>
|
||||
<data name="Archive_supports_subdirectories" xml:space="preserve">
|
||||
<value>Supports subdirectories</value>
|
||||
</data>
|
||||
<data name="Archive_has_explicit_directories" xml:space="preserve">
|
||||
<value>Has explicit subdirectories</value>
|
||||
</data>
|
||||
<data name="Archive_has_entry_timestamp" xml:space="preserve">
|
||||
<value>Has entry timestamp</value>
|
||||
</data>
|
||||
<data name="Archive_supports_protection" xml:space="preserve">
|
||||
<value>Supports protection</value>
|
||||
</data>
|
||||
<data name="Archive_supports_xattrs" xml:space="preserve">
|
||||
<value>Supports extended attributes</value>
|
||||
</data>
|
||||
</root>
|
||||
Reference in New Issue
Block a user