16 Commits

Author SHA1 Message Date
d041e7b1b3 [Refactor] Set explicit binding modes. 2025-07-08 18:14:37 +01:00
563ffcdd32 [Refactor] Move row and column Grid definitions to attributes 2025-07-08 17:55:44 +01:00
86420a4fd0 [Refactor] Use expression bodied lambda 2025-07-08 17:51:43 +01:00
01a2f916b9 [Refactor] Replace direct localization property bindings with static resource references 2025-07-08 13:17:36 +01:00
7e8e42d07c [Refactor] Simplify constructor parameters and initialization in multiple classes 2025-07-08 01:18:07 +01:00
63a16cb19e [Refactor] Use collection expression. 2025-07-08 01:08:45 +01:00
aa1ad9fd64 Typo. 2025-07-08 01:07:19 +01:00
d75381c4eb [Refactor] Simplify event handler parameters in MainWindowViewModel and FileImporter 2025-07-08 00:59:53 +01:00
7bc6da0780 [Refactor] Replace SaveFileDialog with StorageProvider for file saving 2025-07-08 00:58:21 +01:00
8c12694fd6 [Refactor] Replace OpenFileDialog with StorageProvider for file selection 2025-07-07 23:59:01 +01:00
ad27c2a9ab [Refactor] Replace OpenFolderDialog with StorageProvider for folder selection 2025-07-07 23:45:03 +01:00
a88c9000ed [Refactor] Update field names in checksum packets to use properties for consistency 2025-07-07 22:46:40 +01:00
44fb779034 [Refactor] Replace OrderBy with Order for file sorting in ImportDatFolderViewModel and FileImporter 2025-07-07 22:44:34 +01:00
66ecf4c432 [Refactor] Simplify SHA384 byte conversion by using _datHash directly 2025-07-07 22:42:57 +01:00
9818a8c416 [Refactor] Update commands to use asynchronous patterns with ReactiveCommand 2025-07-07 22:41:08 +01:00
ca1708a337 [Refactor] Update Task.Run usage to await and discard returned tasks.
Fixes some race conditions.
2025-07-07 22:17:27 +01:00
36 changed files with 1179 additions and 1436 deletions

View File

@@ -390,8 +390,8 @@ resharper_loop_can_be_converted_to_query_highlighting
resharper_loop_can_be_partly_converted_to_query_highlighting = warning
resharper_member_can_be_file_local_highlighting = warning
resharper_member_can_be_internal_highlighting = warning
resharper_member_can_be_made_static_global_highlighting = warning
resharper_member_can_be_made_static_local_highlighting = warning
resharper_member_can_be_made_static_global_highlighting = none
resharper_member_can_be_made_static_local_highlighting = none
resharper_member_can_be_private_global_highlighting = warning
resharper_member_can_be_private_local_highlighting = warning
resharper_member_can_be_protected_global_highlighting = warning

View File

@@ -28,7 +28,7 @@ public sealed class Fuse : FileSystem
_directoryCache = new ConcurrentDictionary<long, List<DirectoryEntry>>();
_lastHandle = 0;
_fileStatHandleCache = new ConcurrentDictionary<long, Stat>();
Name = "romrepombgrfs";
Name = "romrepomgrfs";
_vfs = vfs;
}

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.IO;
using System.Linq;
using System.Threading;
@@ -19,29 +20,17 @@ namespace RomRepoMgr.Core.Filesystem;
// TODO: Do not show machines or romsets with no ROMs in repo
public class Vfs : IDisposable
{
readonly ConcurrentDictionary<ulong, ConcurrentDictionary<string, CachedDisk>> _machineDisksCache;
readonly ConcurrentDictionary<ulong, ConcurrentDictionary<string, CachedFile>> _machineFilesCache;
readonly ConcurrentDictionary<ulong, ConcurrentDictionary<string, CachedMedia>> _machineMediasCache;
readonly ConcurrentDictionary<long, ConcurrentDictionary<string, CachedMachine>> _machinesStatCache;
readonly ConcurrentDictionary<long, RomSet> _romSetsCache;
readonly ConcurrentDictionary<long, Stream> _streamsCache;
readonly ConcurrentDictionary<ulong, ConcurrentDictionary<string, CachedDisk>> _machineDisksCache = [];
readonly ConcurrentDictionary<ulong, ConcurrentDictionary<string, CachedFile>> _machineFilesCache = [];
readonly ConcurrentDictionary<ulong, ConcurrentDictionary<string, CachedMedia>> _machineMediasCache = [];
readonly ConcurrentDictionary<long, ConcurrentDictionary<string, CachedMachine>> _machinesStatCache = [];
readonly ConcurrentDictionary<long, RomSet> _romSetsCache = [];
readonly ConcurrentDictionary<long, Stream> _streamsCache = [];
Fuse _fuse;
long _lastHandle;
ConcurrentDictionary<string, long> _rootDirectoryCache;
ConcurrentDictionary<string, long> _rootDirectoryCache = [];
Winfsp _winfsp;
public Vfs()
{
_rootDirectoryCache = new ConcurrentDictionary<string, long>();
_romSetsCache = new ConcurrentDictionary<long, RomSet>();
_machinesStatCache = new ConcurrentDictionary<long, ConcurrentDictionary<string, CachedMachine>>();
_machineFilesCache = new ConcurrentDictionary<ulong, ConcurrentDictionary<string, CachedFile>>();
_machineDisksCache = new ConcurrentDictionary<ulong, ConcurrentDictionary<string, CachedDisk>>();
_machineMediasCache = new ConcurrentDictionary<ulong, ConcurrentDictionary<string, CachedMedia>>();
_streamsCache = new ConcurrentDictionary<long, Stream>();
_lastHandle = 0;
}
public static bool IsAvailable => OperatingSystem.IsMacOS() || OperatingSystem.IsLinux()
? Fuse.IsAvailable
: OperatingSystem.IsWindows() && Winfsp.IsAvailable;
@@ -59,7 +48,7 @@ public class Vfs : IDisposable
MountPoint = mountPoint
};
Task.Run(() =>
_ = Task.Run(() =>
{
_fuse.Start();
@@ -667,6 +656,12 @@ public class Vfs : IDisposable
return handle;
}
[ContractInvariantMethod]
void ObjectInvariant()
{
Contract.Invariant(_machineMediasCache.All(pair => pair.Value != null));
}
}
internal sealed class CachedMachine

View File

@@ -14,13 +14,10 @@ using FileInfo = Fsp.Interop.FileInfo;
namespace RomRepoMgr.Core.Filesystem;
[SupportedOSPlatform("windows")]
public class Winfsp : FileSystemBase
public class Winfsp(Vfs vfs) : FileSystemBase
{
readonly Vfs _vfs;
FileSystemHost _host;
public Winfsp(Vfs vfs) => _vfs = vfs;
public static bool IsAvailable
{
get
@@ -128,7 +125,7 @@ public class Winfsp : FileSystemBase
{
volumeInfo = new VolumeInfo();
_vfs.GetInfo(out _, out ulong totalSize);
vfs.GetInfo(out _, out ulong totalSize);
volumeInfo.FreeSize = 0;
volumeInfo.TotalSize = totalSize;
@@ -144,7 +141,7 @@ public class Winfsp : FileSystemBase
fileInfo = default(FileInfo);
normalizedName = default(string);
string[] pieces = _vfs.SplitPath(fileName);
string[] pieces = vfs.SplitPath(fileName);
// Root directory
if(pieces.Length == 0)
@@ -169,11 +166,11 @@ public class Winfsp : FileSystemBase
return STATUS_SUCCESS;
}
long romSetId = _vfs.GetRomSetId(pieces[0]);
long romSetId = vfs.GetRomSetId(pieces[0]);
if(romSetId <= 0) return STATUS_OBJECT_NAME_NOT_FOUND;
RomSet romSet = _vfs.GetRomSet(romSetId);
RomSet romSet = vfs.GetRomSet(romSetId);
if(romSet == null) return STATUS_OBJECT_NAME_NOT_FOUND;
@@ -207,7 +204,7 @@ public class Winfsp : FileSystemBase
return STATUS_SUCCESS;
}
CachedMachine machine = _vfs.GetMachine(romSetId, pieces[1]);
CachedMachine machine = vfs.GetMachine(romSetId, pieces[1]);
if(machine == null) return STATUS_OBJECT_NAME_NOT_FOUND;
@@ -242,7 +239,7 @@ public class Winfsp : FileSystemBase
}
long handle = 0;
CachedFile file = _vfs.GetFile(machine.Id, pieces[2]);
CachedFile file = vfs.GetFile(machine.Id, pieces[2]);
if(file != null)
{
@@ -250,7 +247,7 @@ public class Winfsp : FileSystemBase
if(file.Sha384 == null) return STATUS_OBJECT_NAME_NOT_FOUND;
handle = _vfs.Open(file.Sha384, (long)file.Size);
handle = vfs.Open(file.Sha384, (long)file.Size);
if(handle <= 0) return STATUS_OBJECT_NAME_NOT_FOUND;
@@ -280,7 +277,7 @@ public class Winfsp : FileSystemBase
return STATUS_SUCCESS;
}
CachedDisk disk = _vfs.GetDisk(machine.Id, pieces[2]);
CachedDisk disk = vfs.GetDisk(machine.Id, pieces[2]);
if(disk != null)
{
@@ -288,7 +285,7 @@ public class Winfsp : FileSystemBase
if(disk.Sha1 == null && disk.Md5 == null) return STATUS_OBJECT_NAME_NOT_FOUND;
handle = _vfs.OpenDisk(disk.Sha1, disk.Md5);
handle = vfs.OpenDisk(disk.Sha1, disk.Md5);
if(handle <= 0) return STATUS_OBJECT_NAME_NOT_FOUND;
@@ -318,7 +315,7 @@ public class Winfsp : FileSystemBase
return STATUS_SUCCESS;
}
CachedMedia media = _vfs.GetMedia(machine.Id, pieces[2]);
CachedMedia media = vfs.GetMedia(machine.Id, pieces[2]);
if(media == null) return STATUS_OBJECT_NAME_NOT_FOUND;
@@ -326,7 +323,7 @@ public class Winfsp : FileSystemBase
if(media.Sha256 == null && media.Sha1 == null && media.Md5 == null) return STATUS_OBJECT_NAME_NOT_FOUND;
handle = _vfs.OpenMedia(media.Sha256, media.Sha1, media.Md5);
handle = vfs.OpenMedia(media.Sha256, media.Sha1, media.Md5);
if(handle <= 0) return STATUS_OBJECT_NAME_NOT_FOUND;
@@ -362,7 +359,7 @@ public class Winfsp : FileSystemBase
if(node.Handle <= 0) return;
_vfs.Close(node.Handle);
vfs.Close(node.Handle);
}
public override int Read(object fileNode, object fileDesc, IntPtr buffer, ulong offset, uint length,
@@ -374,7 +371,7 @@ public class Winfsp : FileSystemBase
var buf = new byte[length];
int ret = _vfs.Read(node.Handle, buf, (long)offset);
int ret = vfs.Read(node.Handle, buf, (long)offset);
if(ret < 0) return STATUS_INVALID_HANDLE;
@@ -408,12 +405,12 @@ public class Winfsp : FileSystemBase
{
if(node.MachineId > 0)
{
ConcurrentDictionary<string, CachedFile> cachedMachineFiles = _vfs.GetFilesFromMachine(node.MachineId);
ConcurrentDictionary<string, CachedFile> cachedMachineFiles = vfs.GetFilesFromMachine(node.MachineId);
ConcurrentDictionary<string, CachedDisk> cachedMachineDisks = _vfs.GetDisksFromMachine(node.MachineId);
ConcurrentDictionary<string, CachedDisk> cachedMachineDisks = vfs.GetDisksFromMachine(node.MachineId);
ConcurrentDictionary<string, CachedMedia> cachedMachineMedias =
_vfs.GetMediasFromMachine(node.MachineId);
vfs.GetMediasFromMachine(node.MachineId);
node.Children =
[
@@ -483,7 +480,7 @@ public class Winfsp : FileSystemBase
}
else if(node.RomSetId > 0)
{
ConcurrentDictionary<string, CachedMachine> machines = _vfs.GetMachinesFromRomSet(node.RomSetId);
ConcurrentDictionary<string, CachedMachine> machines = vfs.GetMachinesFromRomSet(node.RomSetId);
node.Children =
[
@@ -514,12 +511,12 @@ public class Winfsp : FileSystemBase
{
node.Children = [];
node.Children.AddRange(_vfs.GetRootEntries()
.Select(e => new FileEntry
{
FileName = e,
IsRomSet = true
}));
node.Children.AddRange(vfs.GetRootEntries()
.Select(e => new FileEntry
{
FileName = e,
IsRomSet = true
}));
}
if(marker != null)
@@ -540,11 +537,11 @@ public class Winfsp : FileSystemBase
if(entry.IsRomSet)
{
long romSetId = _vfs.GetRomSetId(entry.FileName);
long romSetId = vfs.GetRomSetId(entry.FileName);
if(romSetId <= 0) continue;
RomSet romSet = _vfs.GetRomSet(romSetId);
RomSet romSet = vfs.GetRomSet(romSetId);
if(romSet is null) continue;
@@ -569,7 +566,7 @@ public class Winfsp : FileSystemBase
{
fileAttributes = 0;
string[] pieces = _vfs.SplitPath(fileName);
string[] pieces = vfs.SplitPath(fileName);
// Root directory
if(pieces.Length == 0)
@@ -588,11 +585,11 @@ public class Winfsp : FileSystemBase
return STATUS_SUCCESS;
}
long romSetId = _vfs.GetRomSetId(pieces[0]);
long romSetId = vfs.GetRomSetId(pieces[0]);
if(romSetId <= 0) return STATUS_OBJECT_NAME_NOT_FOUND;
RomSet romSet = _vfs.GetRomSet(romSetId);
RomSet romSet = vfs.GetRomSet(romSetId);
if(romSet == null) return STATUS_OBJECT_NAME_NOT_FOUND;
@@ -604,7 +601,7 @@ public class Winfsp : FileSystemBase
return STATUS_SUCCESS;
}
CachedMachine machine = _vfs.GetMachine(romSetId, pieces[1]);
CachedMachine machine = vfs.GetMachine(romSetId, pieces[1]);
if(machine == null) return STATUS_OBJECT_NAME_NOT_FOUND;
@@ -617,7 +614,7 @@ public class Winfsp : FileSystemBase
}
long handle = 0;
CachedFile file = _vfs.GetFile(machine.Id, pieces[2]);
CachedFile file = vfs.GetFile(machine.Id, pieces[2]);
if(file != null)
{
@@ -625,7 +622,7 @@ public class Winfsp : FileSystemBase
if(file.Sha384 == null) return STATUS_OBJECT_NAME_NOT_FOUND;
handle = _vfs.Open(file.Sha384, (long)file.Size);
handle = vfs.Open(file.Sha384, (long)file.Size);
if(handle <= 0) return STATUS_OBJECT_NAME_NOT_FOUND;
@@ -634,7 +631,7 @@ public class Winfsp : FileSystemBase
return STATUS_SUCCESS;
}
CachedDisk disk = _vfs.GetDisk(machine.Id, pieces[2]);
CachedDisk disk = vfs.GetDisk(machine.Id, pieces[2]);
if(disk != null)
{
@@ -642,7 +639,7 @@ public class Winfsp : FileSystemBase
if(disk.Sha1 == null && disk.Md5 == null) return STATUS_OBJECT_NAME_NOT_FOUND;
handle = _vfs.OpenDisk(disk.Sha1, disk.Md5);
handle = vfs.OpenDisk(disk.Sha1, disk.Md5);
if(handle <= 0) return STATUS_OBJECT_NAME_NOT_FOUND;
@@ -651,7 +648,7 @@ public class Winfsp : FileSystemBase
return STATUS_SUCCESS;
}
CachedMedia media = _vfs.GetMedia(machine.Id, pieces[2]);
CachedMedia media = vfs.GetMedia(machine.Id, pieces[2]);
if(media == null) return STATUS_OBJECT_NAME_NOT_FOUND;
@@ -659,7 +656,7 @@ public class Winfsp : FileSystemBase
if(media.Sha256 == null && media.Sha1 == null && media.Md5 == null) return STATUS_OBJECT_NAME_NOT_FOUND;
handle = _vfs.OpenMedia(media.Sha256, media.Sha1, media.Md5);
handle = vfs.OpenMedia(media.Sha256, media.Sha1, media.Md5);
if(handle <= 0) return STATUS_OBJECT_NAME_NOT_FOUND;

View File

@@ -3,20 +3,12 @@ using System.IO;
namespace RomRepoMgr.Core;
internal sealed class StreamWithLength : Stream
internal sealed class StreamWithLength(Stream baseStream, long length) : Stream
{
readonly Stream _baseStream;
public StreamWithLength(Stream baseStream, long length)
{
_baseStream = baseStream;
Length = length;
}
public override bool CanRead => _baseStream.CanRead;
public override bool CanSeek => _baseStream.CanSeek;
public override bool CanWrite => _baseStream.CanWrite;
public override long Length { get; }
public override bool CanRead => baseStream.CanRead;
public override bool CanSeek => baseStream.CanSeek;
public override bool CanWrite => baseStream.CanWrite;
public override long Length { get; } = length;
public override long Position
{
@@ -24,9 +16,9 @@ internal sealed class StreamWithLength : Stream
set => throw new NotSupportedException();
}
public override void Flush() => _baseStream.Flush();
public override void Flush() => baseStream.Flush();
public override int Read(byte[] buffer, int offset, int count) => _baseStream.Read(buffer, offset, count);
public override int Read(byte[] buffer, int offset, int count) => baseStream.Read(buffer, offset, count);
public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException();
@@ -36,7 +28,7 @@ internal sealed class StreamWithLength : Stream
public override void Close()
{
_baseStream.Close();
baseStream.Close();
base.Close();
}
}

View File

@@ -90,27 +90,27 @@ internal sealed class Checksum
_sha384Pkt = new Sha384Packet();
_sha512Pkt = new Sha512Packet();
_crc32Pkt.context = _crc32Ctx;
_md5Pkt.context = _md5Ctx;
_sha1Pkt.context = _sha1Ctx;
_sha256Pkt.context = _sha256Ctx;
_sha384Pkt.context = _sha384Ctx;
_sha512Pkt.context = _sha512Ctx;
_crc32Pkt.Context = _crc32Ctx;
_md5Pkt.Context = _md5Ctx;
_sha1Pkt.Context = _sha1Ctx;
_sha256Pkt.Context = _sha256Ctx;
_sha384Pkt.Context = _sha384Ctx;
_sha512Pkt.Context = _sha512Ctx;
}
internal void Update(byte[] data)
{
_crc32Pkt.data = data;
_crc32Pkt.Data = data;
_crc32Thread.Start(_crc32Pkt);
_md5Pkt.data = data;
_md5Pkt.Data = data;
_md5Thread.Start(_md5Pkt);
_sha1Pkt.data = data;
_sha1Pkt.Data = data;
_sha1Thread.Start(_sha1Pkt);
_sha256Pkt.data = data;
_sha256Pkt.Data = data;
_sha256Thread.Start(_sha256Pkt);
_sha384Pkt.data = data;
_sha384Pkt.Data = data;
_sha384Thread.Start(_sha384Pkt);
_sha512Pkt.data = data;
_sha512Pkt.Data = data;
_sha512Thread.Start(_sha512Pkt);
while(_crc32Thread.IsAlive ||
@@ -142,51 +142,51 @@ internal sealed class Checksum
struct Crc32Packet
{
public Crc32Context context;
public byte[] data;
public Crc32Context Context;
public byte[] Data;
}
struct Md5Packet
{
public Md5Context context;
public byte[] data;
public Md5Context Context;
public byte[] Data;
}
struct Sha1Packet
{
public Sha1Context context;
public byte[] data;
public Sha1Context Context;
public byte[] Data;
}
struct Sha256Packet
{
public Sha256Context context;
public byte[] data;
public Sha256Context Context;
public byte[] Data;
}
struct Sha384Packet
{
public Sha384Context context;
public byte[] data;
public Sha384Context Context;
public byte[] Data;
}
struct Sha512Packet
{
public Sha512Context context;
public byte[] data;
public Sha512Context Context;
public byte[] Data;
}
static void UpdateCrc32(object packet) => ((Crc32Packet)packet).context.Update(((Crc32Packet)packet).data);
static void UpdateCrc32(object packet) => ((Crc32Packet)packet).Context.Update(((Crc32Packet)packet).Data);
static void UpdateMd5(object packet) => ((Md5Packet)packet).context.Update(((Md5Packet)packet).data);
static void UpdateMd5(object packet) => ((Md5Packet)packet).Context.Update(((Md5Packet)packet).Data);
static void UpdateSha1(object packet) => ((Sha1Packet)packet).context.Update(((Sha1Packet)packet).data);
static void UpdateSha1(object packet) => ((Sha1Packet)packet).Context.Update(((Sha1Packet)packet).Data);
static void UpdateSha256(object packet) => ((Sha256Packet)packet).context.Update(((Sha256Packet)packet).data);
static void UpdateSha256(object packet) => ((Sha256Packet)packet).Context.Update(((Sha256Packet)packet).Data);
static void UpdateSha384(object packet) => ((Sha384Packet)packet).context.Update(((Sha384Packet)packet).data);
static void UpdateSha384(object packet) => ((Sha384Packet)packet).Context.Update(((Sha384Packet)packet).Data);
static void UpdateSha512(object packet) => ((Sha512Packet)packet).context.Update(((Sha512Packet)packet).data);
static void UpdateSha512(object packet) => ((Sha512Packet)packet).Context.Update(((Sha512Packet)packet).Data);
#endregion Threading helpers
}

View File

@@ -15,23 +15,15 @@ using CompressionMode = SharpCompress.Compressors.CompressionMode;
namespace RomRepoMgr.Core.Workers;
public class FileExporter
public class FileExporter(long romSetId, string outPath)
{
const long BUFFER_SIZE = 131072;
readonly string _outPath;
readonly long _romSetId;
const long BUFFER_SIZE = 131072;
long _filePosition;
Dictionary<string, FileByMachine> _filesByMachine;
long _machinePosition;
Machine[] _machines;
string _zipCurrentEntryName;
public FileExporter(long romSetId, string outPath)
{
_romSetId = romSetId;
_outPath = outPath;
}
public event EventHandler WorkFinished;
public event EventHandler<ProgressBoundsEventArgs> SetProgressBounds;
public event EventHandler<ProgressEventArgs> SetProgress;
@@ -53,7 +45,7 @@ public class FileExporter
using var ctx = Context.Create(Settings.Settings.Current.DatabasePath);
RomSet romSet = ctx.RomSets.Find(_romSetId);
RomSet romSet = ctx.RomSets.Find(romSetId);
if(romSet == null)
{
@@ -74,7 +66,7 @@ public class FileExporter
Message = Localization.ExportingRoms
});
_machines = ctx.Machines.Where(m => m.RomSet.Id == _romSetId).ToArray();
_machines = ctx.Machines.Where(m => m.RomSet.Id == romSetId).ToArray();
SetProgressBounds?.Invoke(this,
new ProgressBoundsEventArgs
@@ -135,7 +127,7 @@ public class FileExporter
if(machineName.EndsWith(".zip", StringComparison.InvariantCultureIgnoreCase))
machineName = machineName[..^4];
string machinePath = Path.Combine(_outPath, machineName);
string machinePath = Path.Combine(outPath, machineName);
if(!Directory.Exists(machinePath)) Directory.CreateDirectory(machinePath);
@@ -340,7 +332,7 @@ public class FileExporter
if(machineName.EndsWith(".zip", StringComparison.InvariantCultureIgnoreCase))
machineName = machineName[..^4];
string machinePath = Path.Combine(_outPath, machineName);
string machinePath = Path.Combine(outPath, machineName);
if(!Directory.Exists(machinePath)) Directory.CreateDirectory(machinePath);
@@ -498,7 +490,7 @@ public class FileExporter
if(_filesByMachine.Count == 0)
{
_machinePosition++;
Task.Run(CompressNextMachine);
_ = Task.Run(CompressNextMachine);
return;
}
@@ -512,7 +504,7 @@ public class FileExporter
if(!machineName.EndsWith(".zip", StringComparison.InvariantCultureIgnoreCase)) machineName += ".zip";
var zf = new ZipFile(Path.Combine(_outPath, machineName), Encoding.UTF8)
var zf = new ZipFile(Path.Combine(outPath, machineName), Encoding.UTF8)
{
CompressionLevel = CompressionLevel.BestCompression,
CompressionMethod = CompressionMethod.Deflate,

View File

@@ -18,42 +18,23 @@ using SharpCompress.Compressors.LZMA;
namespace RomRepoMgr.Core.Workers;
public class FileImporter
public class FileImporter(bool onlyKnown, bool deleteAfterImport)
{
const long BUFFER_SIZE = 131072;
readonly Context _ctx;
readonly bool _deleteAfterImport;
readonly List<DbDisk> _newDisks;
readonly List<DbFile> _newFiles;
readonly List<DbMedia> _newMedias;
readonly bool _onlyKnown;
readonly Dictionary<string, DbDisk> _pendingDisksByMd5;
readonly Dictionary<string, DbDisk> _pendingDisksBySha1;
readonly Dictionary<string, DbFile> _pendingFiles;
readonly Dictionary<string, DbMedia> _pendingMediasByMd5;
readonly Dictionary<string, DbMedia> _pendingMediasBySha1;
readonly Dictionary<string, DbMedia> _pendingMediasBySha256;
readonly Context _ctx = Context.Create(Settings.Settings.Current.DatabasePath);
readonly List<DbDisk> _newDisks = [];
readonly List<DbFile> _newFiles = [];
readonly List<DbMedia> _newMedias = [];
readonly Dictionary<string, DbDisk> _pendingDisksByMd5 = [];
readonly Dictionary<string, DbDisk> _pendingDisksBySha1 = [];
readonly Dictionary<string, DbFile> _pendingFiles = [];
readonly Dictionary<string, DbMedia> _pendingMediasByMd5 = [];
readonly Dictionary<string, DbMedia> _pendingMediasBySha1 = [];
readonly Dictionary<string, DbMedia> _pendingMediasBySha256 = [];
string _lastMessage;
long _position;
long _totalFiles;
public FileImporter(bool onlyKnown, bool deleteAfterImport)
{
_pendingFiles = new Dictionary<string, DbFile>();
_pendingDisksByMd5 = new Dictionary<string, DbDisk>();
_pendingDisksBySha1 = new Dictionary<string, DbDisk>();
_pendingMediasBySha256 = new Dictionary<string, DbMedia>();
_pendingMediasBySha1 = new Dictionary<string, DbMedia>();
_pendingMediasByMd5 = new Dictionary<string, DbMedia>();
_newFiles = [];
_newDisks = [];
_newMedias = [];
_onlyKnown = onlyKnown;
_deleteAfterImport = deleteAfterImport;
_position = 0;
_ctx = Context.Create(Settings.Settings.Current.DatabasePath);
}
public event EventHandler SetIndeterminateProgress2;
public event EventHandler<ProgressBoundsEventArgs> SetProgressBounds2;
public event EventHandler<ProgressEventArgs> SetProgress2;
@@ -77,7 +58,7 @@ public class FileImporter
Message = Localization.EnumeratingFiles
});
string[] files = Directory.GetFiles(path, "*", SearchOption.AllDirectories).OrderBy(p => p).ToArray();
string[] files = Directory.GetFiles(path, "*", SearchOption.AllDirectories).Order().ToArray();
_totalFiles += files.LongLength;
SetProgressBounds?.Invoke(this,
@@ -388,7 +369,7 @@ public class FileImporter
if(dbFile == null)
{
if(_onlyKnown)
if(onlyKnown)
{
_lastMessage = Localization.UnknownFile;
@@ -493,7 +474,7 @@ public class FileImporter
inFs.Close();
if(_deleteAfterImport) File.Delete(path);
if(deleteAfterImport) File.Delete(path);
return true;
}
@@ -559,7 +540,7 @@ public class FileImporter
if(!fileInDb) _newFiles.Add(dbFile);
if(_deleteAfterImport) File.Delete(path);
if(deleteAfterImport) File.Delete(path);
return true;
}
@@ -655,7 +636,7 @@ public class FileImporter
if(dbDisk == null)
{
if(_onlyKnown)
if(onlyKnown)
{
_lastMessage = Localization.UnknownFile;
@@ -770,7 +751,7 @@ public class FileImporter
inFs.Close();
if(_deleteAfterImport) File.Delete(path);
if(deleteAfterImport) File.Delete(path);
return true;
}
@@ -833,7 +814,7 @@ public class FileImporter
if(!diskInDb) _newDisks.Add(dbDisk);
if(_deleteAfterImport) File.Delete(path);
if(deleteAfterImport) File.Delete(path);
if(knownDiskWasBigger) File.Delete(repoPath + ".bak");
@@ -954,7 +935,7 @@ public class FileImporter
if(dbMedia == null)
{
if(_onlyKnown)
if(onlyKnown)
{
_lastMessage = Localization.UnknownFile;
@@ -1107,7 +1088,7 @@ public class FileImporter
inFs.Close();
if(_deleteAfterImport) File.Delete(path);
if(deleteAfterImport) File.Delete(path);
return true;
}
@@ -1170,7 +1151,7 @@ public class FileImporter
if(!mediaInDb) _newMedias.Add(dbMedia);
if(_deleteAfterImport) File.Delete(path);
if(deleteAfterImport) File.Delete(path);
if(knownMediaWasBigger) File.Delete(repoPath + ".bak");
@@ -1341,7 +1322,7 @@ public class FileImporter
long counter = 0;
unarProcess.OutputDataReceived += (sender, e) =>
unarProcess.OutputDataReceived += (_, e) =>
{
counter++;

View File

@@ -29,10 +29,8 @@ using RomRepoMgr.Database.Models;
namespace RomRepoMgr.Database;
public sealed class Context : DbContext
public sealed class Context(DbContextOptions options) : DbContext(options)
{
public Context(DbContextOptions options) : base(options) {}
public DbSet<DbFile> Files { get; set; }
public DbSet<RomSet> RomSets { get; set; }
public DbSet<Machine> Machines { get; set; }
@@ -198,6 +196,6 @@ public sealed class Context : DbContext
entity.HasOne(e => e.Media).WithMany(e => e.Machines).OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity<RomSetStat>(entity => { entity.HasOne(e => e.RomSet).WithOne(e => e.Statistics); });
modelBuilder.Entity<RomSetStat>(entity => entity.HasOne(e => e.RomSet).WithOne(e => e.Statistics));
}
}

View File

@@ -117,6 +117,6 @@ public class App : Application
})
return;
mainWindowViewModel.ExecuteSettingsCommand();
_ = mainWindowViewModel.ExecuteSettingsCommandAsync();
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -6,15 +6,13 @@
<Compile Update="**\*.xaml.cs">
<DependentUpon>%(Filename)</DependentUpon>
</Compile>
<None Remove="**\*.xaml"/>
<AvaloniaResource Include="**\*.xaml">
<SubType>Designer</SubType>
</AvaloniaResource>
</ItemGroup>
<ItemGroup>
<AvaloniaResource Include="Assets\**"/>
<Compile Update="Resources\Localization.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Localization.resx</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Avalonia"/>
@@ -33,7 +31,7 @@
<PackageReference Include="Roslynator.Analyzers"/>
<PackageReference Include="Roslynator.CodeAnalysis.Analyzers"/>
<PackageReference Include="Roslynator.Formatting.Analyzers"/>
<PackageReference Include="SkiaSharp.NativeAssets.Linux" />
<PackageReference Include="SkiaSharp.NativeAssets.Linux"/>
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer"/>
<PackageReference Include="Text.Analyzers"/>
</ItemGroup>
@@ -44,7 +42,7 @@
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Resources\Localization.resx">
<Generator>ResXFileCodeGenerator</Generator>
<Generator>PublicResXFileCodeGenerator</Generator>
<LastGenOutput>Localization.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>

View File

@@ -34,7 +34,6 @@ using System.Threading.Tasks;
using Avalonia.Threading;
using ReactiveUI;
using RomRepoMgr.Core.Models;
using RomRepoMgr.Resources;
using RomRepoMgr.Views;
namespace RomRepoMgr.ViewModels;
@@ -58,8 +57,7 @@ public sealed class AboutViewModel : ViewModelBase
Assemblies = [];
// TODO: They do not load in time
Task.Run(() =>
_ = Task.Run(() =>
{
foreach(Assembly assembly in AppDomain.CurrentDomain.GetAssemblies().OrderBy(a => a.FullName))
{
@@ -83,23 +81,14 @@ public sealed class AboutViewModel : ViewModelBase
});
}
public string AboutLabel => Localization.AboutLabel;
public string LibrariesLabel => Localization.LibrariesLabel;
public string AuthorsLabel => Localization.AuthorsLabel;
public string Title => Localization.AboutTitle;
public string SoftwareName => "RomRepoMgr";
public string SuiteName => "ROM Repository Manager";
public string Copyright => "© 2020-2024 Natalia Portillo";
public string Website => "https://www.claunia.com";
public string License => Localization.LicenseLabel;
public string CloseLabel => Localization.CloseLabel;
public string AssembliesLibraryText => Localization.AssembliesLibraryText;
public string AssembliesVersionText => Localization.AssembliesVersionText;
public string Authors => Localization.AuthorsText;
public ReactiveCommand<Unit, Unit> WebsiteCommand { get; }
public ReactiveCommand<Unit, Unit> LicenseCommand { get; }
public ReactiveCommand<Unit, Unit> CloseCommand { get; }
public ObservableCollection<AssemblyModel> Assemblies { get; }
public string SoftwareName => "RomRepoMgr";
public string SuiteName => "ROM Repository Manager";
public string Copyright => "© 2020-2024 Natalia Portillo";
public string Website => "https://www.claunia.com";
public ReactiveCommand<Unit, Unit> WebsiteCommand { get; }
public ReactiveCommand<Unit, Unit> LicenseCommand { get; }
public ReactiveCommand<Unit, Unit> CloseCommand { get; }
public ObservableCollection<AssemblyModel> Assemblies { get; }
public string VersionText
{

View File

@@ -25,12 +25,12 @@
using System;
using System.Reactive;
using System.Threading.Tasks;
using ReactiveUI;
using RomRepoMgr.Core.EventArgs;
using RomRepoMgr.Core.Models;
using RomRepoMgr.Database;
using RomRepoMgr.Database.Models;
using RomRepoMgr.Resources;
using RomRepoMgr.Views;
namespace RomRepoMgr.ViewModels;
@@ -61,30 +61,11 @@ public class EditDatViewModel : ViewModelBase
_date = romSet.Date;
_description = romSet.Description;
_homepage = romSet.Homepage;
SaveCommand = ReactiveCommand.Create(ExecuteSaveCommand);
SaveCommand = ReactiveCommand.CreateFromTask(ExecuteSaveCommandAsync);
CancelCommand = ReactiveCommand.Create(ExecuteCloseCommand);
CloseCommand = ReactiveCommand.Create(ExecuteCloseCommand);
}
public string NameLabel => Localization.RomSetNameLabel;
public string VersionLabel => Localization.RomSetVersionLabel;
public string AuthorLabel => Localization.RomSetAuthorLabel;
public string CategoryLabel => Localization.RomSetCategoryLabel;
public string CommentLabel => Localization.RomSetCommentLabel;
public string DateLabel => Localization.RomSetDateLabel;
public string DescriptionLabel => Localization.RomSetDescriptionLabel;
public string HomepageLabel => Localization.HomepageLabel;
public string TotalMachinesLabel => Localization.TotalMachinesLabel;
public string CompleteMachinesLabel => Localization.CompleteMachinesLabel;
public string IncompleteMachinesLabel => Localization.IncompleteMachinesLabel;
public string TotalRomsLabel => Localization.TotalRomsLabel;
public string HaveRomsLabel => Localization.HaveRomsLabel;
public string MissRomsLabel => Localization.MissRomsLabel;
public string Title => Localization.EditDatTitle;
public string SaveLabel => Localization.SaveLabel;
public string CancelLabel => Localization.CancelLabel;
public string CloseLabel => Localization.CloseLabel;
public ReactiveCommand<Unit, Unit> SaveCommand { get; }
public ReactiveCommand<Unit, Unit> CancelCommand { get; }
public ReactiveCommand<Unit, Unit> CloseCommand { get; }
@@ -193,9 +174,9 @@ public class EditDatViewModel : ViewModelBase
void ExecuteCloseCommand() => _view.Close();
async void ExecuteSaveCommand()
async Task ExecuteSaveCommandAsync()
{
using var ctx = Context.Create(Settings.Settings.Current.DatabasePath);
await using var ctx = Context.Create(Settings.Settings.Current.DatabasePath);
RomSet romSetDb = await ctx.RomSets.FindAsync(_romSet.Id);

View File

@@ -62,8 +62,6 @@ public sealed class ExportDatViewModel : ViewModelBase
_worker.FailedWithText += OnWorkerOnFailedWithText;
}
public string Title => Localization.ExportDatTitle;
public string StatusMessage
{
get => _statusMessage;
@@ -94,7 +92,6 @@ public sealed class ExportDatViewModel : ViewModelBase
set => this.RaiseAndSetIfChanged(ref _canClose, value);
}
public string CloseLabel => Localization.CloseLabel;
public ReactiveCommand<Unit, Unit> CloseCommand { get; }
void OnWorkerOnFinishedWithText(object sender, MessageEventArgs args) => Dispatcher.UIThread.Post(() =>
@@ -119,24 +116,23 @@ public sealed class ExportDatViewModel : ViewModelBase
ProgressVisible = true;
StatusMessage = Localization.DecompressingDat;
var sha384Bytes = new byte[48];
string sha384 = _datHash;
var sha384Bytes = new byte[48];
for(var i = 0; i < 48; i++)
{
if(sha384[i * 2] >= 0x30 && sha384[i * 2] <= 0x39)
sha384Bytes[i] = (byte)((sha384[i * 2] - 0x30) * 0x10);
else if(sha384[i * 2] >= 0x41 && sha384[i * 2] <= 0x46)
sha384Bytes[i] = (byte)((sha384[i * 2] - 0x37) * 0x10);
else if(sha384[i * 2] >= 0x61 && sha384[i * 2] <= 0x66)
sha384Bytes[i] = (byte)((sha384[i * 2] - 0x57) * 0x10);
if(_datHash[i * 2] >= 0x30 && _datHash[i * 2] <= 0x39)
sha384Bytes[i] = (byte)((_datHash[i * 2] - 0x30) * 0x10);
else if(_datHash[i * 2] >= 0x41 && _datHash[i * 2] <= 0x46)
sha384Bytes[i] = (byte)((_datHash[i * 2] - 0x37) * 0x10);
else if(_datHash[i * 2] >= 0x61 && _datHash[i * 2] <= 0x66)
sha384Bytes[i] = (byte)((_datHash[i * 2] - 0x57) * 0x10);
if(sha384[i * 2 + 1] >= 0x30 && sha384[i * 2 + 1] <= 0x39)
sha384Bytes[i] += (byte)(sha384[i * 2 + 1] - 0x30);
else if(sha384[i * 2 + 1] >= 0x41 && sha384[i * 2 + 1] <= 0x46)
sha384Bytes[i] += (byte)(sha384[i * 2 + 1] - 0x37);
else if(sha384[i * 2 + 1] >= 0x61 && sha384[i * 2 + 1] <= 0x66)
sha384Bytes[i] += (byte)(sha384[i * 2 + 1] - 0x57);
if(_datHash[i * 2 + 1] >= 0x30 && _datHash[i * 2 + 1] <= 0x39)
sha384Bytes[i] += (byte)(_datHash[i * 2 + 1] - 0x30);
else if(_datHash[i * 2 + 1] >= 0x41 && _datHash[i * 2 + 1] <= 0x46)
sha384Bytes[i] += (byte)(_datHash[i * 2 + 1] - 0x37);
else if(_datHash[i * 2 + 1] >= 0x61 && _datHash[i * 2 + 1] <= 0x66)
sha384Bytes[i] += (byte)(_datHash[i * 2 + 1] - 0x57);
}
string datHash32 = Base32.ToBase32String(sha384Bytes);
@@ -145,6 +141,6 @@ public sealed class ExportDatViewModel : ViewModelBase
if(!File.Exists(compressedDatPath)) _view.Close();
Task.Run(() => _worker.DecompressFile(compressedDatPath, _outPath));
_ = Task.Run(() => _worker.DecompressFile(compressedDatPath, _outPath));
}
}

View File

@@ -30,7 +30,6 @@ using Avalonia.Threading;
using ReactiveUI;
using RomRepoMgr.Core.EventArgs;
using RomRepoMgr.Core.Workers;
using RomRepoMgr.Resources;
using RomRepoMgr.Views;
namespace RomRepoMgr.ViewModels;
@@ -68,7 +67,6 @@ public sealed class ExportRomsViewModel : ViewModelBase
CanClose = false;
}
public string PathLabel => Localization.PathLabel;
public string FolderPath { get; }
public bool ProgressVisible
@@ -179,9 +177,6 @@ public sealed class ExportRomsViewModel : ViewModelBase
set => this.RaiseAndSetIfChanged(ref _progress3IsIndeterminate, value);
}
public string Title => Localization.ExportRomsTitle;
public string CloseLabel => Localization.CloseLabel;
public bool CanClose
{
get => _canClose;
@@ -260,6 +255,6 @@ public sealed class ExportRomsViewModel : ViewModelBase
ProgressVisible = true;
Task.Run(worker.Export);
_ = Task.Run(worker.Export);
}
}

View File

@@ -75,11 +75,7 @@ public sealed class ImportDatFolderViewModel : ViewModelBase
StartCommand = ReactiveCommand.Create(ExecuteStartCommand);
}
public string PathLabel => Localization.PathLabel;
public string CategoryLabel => Localization.RomSetCategoryLabel;
public string FolderPath { get; }
public string AllFilesLabel => Localization.AllFilesLabel;
public string RecursiveLabel => Localization.RecursiveLabel;
public string FolderPath { get; }
public bool AllFilesChecked
{
@@ -191,13 +187,7 @@ public sealed class ImportDatFolderViewModel : ViewModelBase
set => this.RaiseAndSetIfChanged(ref _category, value);
}
public string Title => Localization.ImportDatFolderTitle;
public ObservableCollection<ImportDatFolderItem> ImportResults { get; }
public string ResultFilenameLabel => Localization.ResultFilenameLabel;
public string ResultStatusLabel => Localization.ResultStatusLabel;
public string CloseLabel => Localization.CloseLabel;
public string StartLabel => Localization.StartLabel;
public ObservableCollection<ImportDatFolderItem> ImportResults { get; }
public bool CanClose
{
@@ -216,52 +206,56 @@ public sealed class ImportDatFolderViewModel : ViewModelBase
internal void OnOpened() => RefreshFiles();
void RefreshFiles() => Task.Run(() =>
void RefreshFiles()
{
Dispatcher.UIThread.Post(() =>
_ = Task.Run(() =>
{
IsReady = false;
ProgressVisible = true;
Progress2Visible = false;
ProgressIsIndeterminate = true;
StatusMessage = Localization.SearchingForFiles;
});
Dispatcher.UIThread.Post(() =>
{
IsReady = false;
ProgressVisible = true;
Progress2Visible = false;
ProgressIsIndeterminate = true;
StatusMessage = Localization.SearchingForFiles;
});
if(_allFilesChecked)
{
_datFiles = Directory
.GetFiles(FolderPath,
"*.*",
_recursiveChecked ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly)
.OrderBy(f => f)
.ToArray();
}
else
{
string[] dats = Directory.GetFiles(FolderPath,
"*.dat",
if(_allFilesChecked)
{
_datFiles = Directory.GetFiles(FolderPath,
"*.*",
_recursiveChecked
? SearchOption.AllDirectories
: SearchOption.TopDirectoryOnly);
: SearchOption.TopDirectoryOnly)
.Order()
.ToArray();
}
else
{
string[] dats = Directory.GetFiles(FolderPath,
"*.dat",
_recursiveChecked
? SearchOption.AllDirectories
: SearchOption.TopDirectoryOnly);
string[] xmls = Directory.GetFiles(FolderPath,
"*.xml",
_recursiveChecked
? SearchOption.AllDirectories
: SearchOption.TopDirectoryOnly);
string[] xmls = Directory.GetFiles(FolderPath,
"*.xml",
_recursiveChecked
? SearchOption.AllDirectories
: SearchOption.TopDirectoryOnly);
_datFiles = dats.Concat(xmls).OrderBy(f => f).ToArray();
}
_datFiles = dats.Concat(xmls).Order().ToArray();
}
Dispatcher.UIThread.Post(() =>
{
IsReady = true;
ProgressVisible = false;
StatusMessage = string.Format(Localization.FoundFiles, _datFiles.Length);
CanClose = true;
CanStart = true;
Dispatcher.UIThread.Post(() =>
{
IsReady = true;
ProgressVisible = false;
StatusMessage = string.Format(Localization.FoundFiles, _datFiles.Length);
CanClose = true;
CanStart = true;
});
});
});
}
void ExecuteCloseCommand() => _view.Close();
@@ -299,15 +293,15 @@ public sealed class ImportDatFolderViewModel : ViewModelBase
StatusMessage = string.Format(Localization.ImportingItem, Path.GetFileName(_datFiles[_listPosition]));
ProgressValue = _listPosition;
var _worker = new DatImporter(_datFiles[_listPosition], Category);
_worker.ErrorOccurred += OnWorkerOnErrorOccurred;
_worker.SetIndeterminateProgress += OnWorkerOnSetIndeterminateProgress;
_worker.SetMessage += OnWorkerOnSetMessage;
_worker.SetProgress += OnWorkerOnSetProgress;
_worker.SetProgressBounds += OnWorkerOnSetProgressBounds;
_worker.WorkFinished += OnWorkerOnWorkFinished;
_worker.RomSetAdded += RomSetAdded;
Task.Run(_worker.Import);
var worker = new DatImporter(_datFiles[_listPosition], Category);
worker.ErrorOccurred += OnWorkerOnErrorOccurred;
worker.SetIndeterminateProgress += OnWorkerOnSetIndeterminateProgress;
worker.SetMessage += OnWorkerOnSetMessage;
worker.SetProgress += OnWorkerOnSetProgress;
worker.SetProgressBounds += OnWorkerOnSetProgressBounds;
worker.WorkFinished += OnWorkerOnWorkFinished;
worker.RomSetAdded += RomSetAdded;
_ = Task.Run(worker.Import);
}
void OnWorkerOnWorkFinished(object sender, MessageEventArgs args) => Dispatcher.UIThread.Post(() =>

View File

@@ -30,7 +30,6 @@ using Avalonia.Threading;
using ReactiveUI;
using RomRepoMgr.Core.EventArgs;
using RomRepoMgr.Core.Workers;
using RomRepoMgr.Resources;
using RomRepoMgr.Views;
namespace RomRepoMgr.ViewModels;
@@ -65,8 +64,6 @@ public sealed class ImportDatViewModel : ViewModelBase
_worker.WorkFinished += OnWorkerOnWorkFinished;
}
public string Title => Localization.ImportDatTitle;
public string StatusMessage
{
get => _statusMessage;
@@ -121,7 +118,6 @@ public sealed class ImportDatViewModel : ViewModelBase
set => this.RaiseAndSetIfChanged(ref _canClose, value);
}
public string CloseLabel => Localization.CloseLabel;
public ReactiveCommand<Unit, Unit> CloseCommand { get; }
void OnWorkerOnWorkFinished(object sender, MessageEventArgs args) => Dispatcher.UIThread.Post(() =>
@@ -161,7 +157,7 @@ public sealed class ImportDatViewModel : ViewModelBase
{
ProgressVisible = true;
_worker.RomSetAdded += RomSetAdded;
Task.Run(_worker.Import);
_ = Task.Run(_worker.Import);
}
public event EventHandler<RomSetEventArgs> RomSetAdded;

View File

@@ -77,11 +77,7 @@ public sealed class ImportRomFolderViewModel : ViewModelBase
_removeFilesEnabled = false;
}
public string PathLabel => Localization.PathLabel;
public string FolderPath { get; }
public string RemoveFilesLabel => Localization.RemoveFilesLabel;
public string KnownOnlyLabel => Localization.KnownOnlyLabel;
public string RecurseArchivesLabel => Localization.RecurseArchivesLabel;
public bool RecurseArchivesEnabled => Settings.Settings.UnArUsable;
public bool RemoveFilesChecked
@@ -198,13 +194,7 @@ public sealed class ImportRomFolderViewModel : ViewModelBase
set => this.RaiseAndSetIfChanged(ref _isImporting, value);
}
public string Title => Localization.ImportRomFolderTitle;
public ObservableCollection<ImportRomItem> ImportResults { get; }
public string ResultFilenameLabel => Localization.ResultFilenameLabel;
public string ResultStatusLabel => Localization.ResultStatusLabel;
public string CloseLabel => Localization.CloseLabel;
public string StartLabel => Localization.StartLabel;
public ObservableCollection<ImportRomItem> ImportResults { get; }
public bool CanClose
{
@@ -244,7 +234,7 @@ public sealed class ImportRomFolderViewModel : ViewModelBase
worker.Finished += OnWorkerOnFinished;
worker.ImportedRom += OnWorkerOnImportedRom;
Task.Run(() => worker.ProcessPath(FolderPath, true, RecurseArchivesChecked));
_ = Task.Run(() => worker.ProcessPath(FolderPath, true, RecurseArchivesChecked));
}
void OnWorkerOnImportedRom(object sender, ImportedRomItemEventArgs args) =>

View File

@@ -29,9 +29,11 @@ using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Reactive;
using System.Threading.Tasks;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Platform.Storage;
using Avalonia.Threading;
using MsBox.Avalonia;
using MsBox.Avalonia.Enums;
@@ -54,56 +56,23 @@ public class MainWindowViewModel : ViewModelBase
{
_view = view;
ExitCommand = ReactiveCommand.Create(ExecuteExitCommand);
SettingsCommand = ReactiveCommand.Create(ExecuteSettingsCommand);
SettingsCommand = ReactiveCommand.CreateFromTask(ExecuteSettingsCommandAsync);
AboutCommand = ReactiveCommand.Create(ExecuteAboutCommand);
ImportDatCommand = ReactiveCommand.Create(ExecuteImportDatCommand);
ImportDatFolderCommand = ReactiveCommand.Create(ExecuteImportDatFolderCommand);
ImportRomFolderCommand = ReactiveCommand.Create(ExecuteImportRomFolderCommand);
DeleteRomSetCommand = ReactiveCommand.Create(ExecuteDeleteRomSetCommand);
ImportDatCommand = ReactiveCommand.CreateFromTask(ExecuteImportDatCommandAsync);
ImportDatFolderCommand = ReactiveCommand.CreateFromTask(ExecuteImportDatFolderCommandAsync);
ImportRomFolderCommand = ReactiveCommand.CreateFromTask(ExecuteImportRomFolderCommandAsync);
DeleteRomSetCommand = ReactiveCommand.CreateFromTask(ExecuteDeleteRomSetCommandAsync);
EditRomSetCommand = ReactiveCommand.Create(ExecuteEditRomSetCommand);
ExportDatCommand = ReactiveCommand.Create(ExecuteExportDatCommand);
ExportRomsCommand = ReactiveCommand.Create(ExecuteExportRomsCommand);
MountCommand = ReactiveCommand.Create(ExecuteMountCommand);
ExportDatCommand = ReactiveCommand.CreateFromTask(ExecuteExportDatCommandAsync);
ExportRomsCommand = ReactiveCommand.CreateFromTask(ExecuteExportRomsCommandAsync);
MountCommand = ReactiveCommand.CreateFromTask(ExecuteMountCommandAsync);
UmountCommand = ReactiveCommand.Create(ExecuteUmountCommand);
UpdateStatsCommand = ReactiveCommand.Create(ExecuteUpdateStatsCommand);
UpdateStatsCommand = ReactiveCommand.CreateFromTask(ExecuteUpdateStatsCommandAsync);
RomSets = new ObservableCollection<RomSetModel>(romSets);
}
public ObservableCollection<RomSetModel> RomSets { get; }
public string RomSetLabel => Localization.RomSets;
public string RomSetNameLabel => Localization.RomSetNameLabel;
public string RomSetVersionLabel => Localization.RomSetVersionLabel;
public string RomSetAuthorLabel => Localization.RomSetAuthorLabel;
public string RomSetCategoryLabel => Localization.RomSetCategoryLabel;
public string RomSetDateLabel => Localization.RomSetDateLabel;
public string RomSetDescriptionLabel => Localization.RomSetDescriptionLabel;
public string RomSetCommentLabel => Localization.RomSetCommentLabel;
public string RomSetTotalMachinesLabel => Localization.RomSetTotalMachinesLabel;
public string RomSetCompleteMachinesLabel => Localization.RomSetCompleteMachinesLabel;
public string RomSetIncompleteMachinesLabel => Localization.RomSetIncompleteMachinesLabel;
public string RomSetTotalRomsLabel => Localization.RomSetTotalRomsLabel;
public string RomSetHaveRomsLabel => Localization.RomSetHaveRomsLabel;
public string RomSetMissRomsLabel => Localization.RomSetMissRomsLabel;
public bool IsVfsAvailable => Vfs.IsAvailable;
public string FileMenuText => Localization.FileMenuText;
public string FileMenuImportDatFileText => Localization.FileMenuImportDatFileText;
public string FileMenuImportDatFolderText => Localization.FileMenuImportDatFolderText;
public string FileMenuSettingsText => Localization.FileMenuSettingsText;
public string FileMenuExitText => Localization.FileMenuExitText;
public string FilesystemMenuText => Localization.FilesystemMenuText;
public string FilesystemMenuMountText => Localization.FilesystemMenuMountText;
public string RomsMenuText => Localization.RomsMenuText;
public string RomsMenuImportText => Localization.RomsMenuImportText;
public string RomSetsMenuText => Localization.RomSetsMenuText;
public string RomSetsMenuSaveRomsText => Localization.RomSetsMenuSaveRomsText;
public string RomSetsMenuSaveDatText => Localization.RomSetsMenuSaveDatText;
public string RomSetsMenuEditText => Localization.RomSetsMenuEditText;
public string RomSetsMenuDeleteText => Localization.RomSetsMenuDeleteText;
public string HelpMenuText => Localization.HelpMenuText;
public string HelpMenuAboutText => Localization.HelpMenuAboutText;
public string FilesystemMenuUmountText => Localization.FilesystemMenuUmountText;
public string DatabaseMenuText => Localization.DatabaseMenuText;
public string DatabaseMenuUpdateStatsText => Localization.DatabaseMenuUpdateStatsText;
public ObservableCollection<RomSetModel> RomSets { get; }
public bool IsVfsAvailable => Vfs.IsAvailable;
public bool NativeMenuSupported =>
NativeMenu.GetIsNativeMenuExported((Application.Current.ApplicationLifetime as
@@ -135,11 +104,12 @@ public class MainWindowViewModel : ViewModelBase
set => this.RaiseAndSetIfChanged(ref _selectedRomSet, value);
}
internal async void ExecuteSettingsCommand()
internal Task ExecuteSettingsCommandAsync()
{
var dialog = new SettingsDialog();
dialog.DataContext = new SettingsViewModel(dialog);
await dialog.ShowDialog(_view);
return dialog.ShowDialog(_view);
}
internal void ExecuteExitCommand() =>
@@ -149,79 +119,72 @@ public class MainWindowViewModel : ViewModelBase
{
var dialog = new About();
dialog.DataContext = new AboutViewModel(dialog);
dialog.ShowDialog(_view);
_ = dialog.ShowDialog(_view);
}
async void ExecuteImportDatCommand()
async Task ExecuteImportDatCommandAsync()
{
var dlgOpen = new OpenFileDialog
var datFileType = new FilePickerFileType(Localization.DatFilesDialogLabel)
{
AllowMultiple = false,
Title = Localization.ImportDatFileDialogTitle
Patterns = ["*.dat", "*.xml"],
AppleUniformTypeIdentifiers = ["public.xml", "public.json"],
MimeTypes = ["application/xml", "text/*"]
};
dlgOpen.Filters.Add(new FileDialogFilter
IReadOnlyList<IStorageFile> result = await _view.StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
{
Extensions = ["dat", "xml"],
Name = Localization.DatFilesDialogLabel
Title = Localization.ImportDatFileDialogTitle,
AllowMultiple = false,
SuggestedStartLocation = await _view.StorageProvider.TryGetWellKnownFolderAsync(WellKnownFolder.Documents),
FileTypeFilter = [datFileType, FilePickerFileTypes.All]
});
dlgOpen.Filters.Add(new FileDialogFilter
{
Extensions = ["*"],
Name = Localization.AllFilesDialogLabel
});
string[] result = await dlgOpen.ShowAsync(_view);
if(result?.Length != 1) return;
if(result.Count != 1) return;
var dialog = new ImportDat();
var importDatViewModel = new ImportDatViewModel(dialog, result[0]);
var importDatViewModel = new ImportDatViewModel(dialog, result[0].Path.LocalPath);
importDatViewModel.RomSetAdded += ImportDatViewModelOnRomSetAdded;
dialog.DataContext = importDatViewModel;
await dialog.ShowDialog(_view);
_ = dialog.ShowDialog(_view);
}
void ImportDatViewModelOnRomSetAdded(object sender, RomSetEventArgs e) =>
Dispatcher.UIThread.Post(() => RomSets.Add(e.RomSet));
async void ExecuteImportDatFolderCommand()
async Task ExecuteImportDatFolderCommandAsync()
{
var dlgOpen = new OpenFolderDialog
{
Title = Localization.ImportDatFolderDialogTitle
};
IReadOnlyList<IStorageFolder> result =
await _view.StorageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions
{
Title = Localization.ImportDatFolderDialogTitle
});
string result = await dlgOpen.ShowAsync(_view);
if(result == null) return;
if(result.Count < 1) return;
var dialog = new ImportDatFolder();
var importDatFolderViewModel = new ImportDatFolderViewModel(dialog, result);
var importDatFolderViewModel = new ImportDatFolderViewModel(dialog, result[0].Path.LocalPath);
importDatFolderViewModel.RomSetAdded += ImportDatViewModelOnRomSetAdded;
dialog.DataContext = importDatFolderViewModel;
await dialog.ShowDialog(_view);
_ = dialog.ShowDialog(_view);
}
async void ExecuteImportRomFolderCommand()
async Task ExecuteImportRomFolderCommandAsync()
{
var dlgOpen = new OpenFolderDialog
{
Title = Localization.ImportRomsFolderDialogTitle
};
IReadOnlyList<IStorageFolder> result =
await _view.StorageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions
{
Title = Localization.ImportRomsFolderDialogTitle
});
string result = await dlgOpen.ShowAsync(_view);
if(result == null) return;
if(result.Count < 1) return;
var dialog = new ImportRomFolder();
var importRomFolderViewModel = new ImportRomFolderViewModel(dialog, result);
var importRomFolderViewModel = new ImportRomFolderViewModel(dialog, result[0].Path.LocalPath);
dialog.DataContext = importRomFolderViewModel;
await dialog.ShowDialog(_view);
_ = dialog.ShowDialog(_view);
}
async void ExecuteDeleteRomSetCommand()
async Task ExecuteDeleteRomSetCommandAsync()
{
if(SelectedRomSet == null) return;
@@ -251,7 +214,7 @@ public class MainWindowViewModel : ViewModelBase
var window = new EditDat();
var viewModel = new EditDatViewModel(window, SelectedRomSet);
viewModel.RomSetModified += (sender, args) =>
viewModel.RomSetModified += (_, args) =>
{
RomSetModel old = RomSets.FirstOrDefault(r => r.Id == args.RomSet.Id);
@@ -266,60 +229,57 @@ public class MainWindowViewModel : ViewModelBase
window.Show();
}
async void ExecuteExportDatCommand()
async Task ExecuteExportDatCommandAsync()
{
if(SelectedRomSet == null) return;
var dlgSave = new SaveFileDialog
IStorageFile result = await _view.StorageProvider.SaveFilePickerAsync(new FilePickerSaveOptions
{
InitialFileName = SelectedRomSet.Filename
};
string result = await dlgSave.ShowAsync(_view);
SuggestedFileName = SelectedRomSet.Filename,
SuggestedStartLocation = await _view.StorageProvider.TryGetWellKnownFolderAsync(WellKnownFolder.Documents)
});
if(result == null) return;
var dialog = new ExportDat();
var viewModel = new ExportDatViewModel(dialog, SelectedRomSet.Sha384, result);
var viewModel = new ExportDatViewModel(dialog, SelectedRomSet.Sha384, result.Path.LocalPath);
dialog.DataContext = viewModel;
await dialog.ShowDialog(_view);
_ = dialog.ShowDialog(_view);
}
async void ExecuteExportRomsCommand()
async Task ExecuteExportRomsCommandAsync()
{
var dlgOpen = new OpenFolderDialog
{
Title = Localization.ExportRomsDialogTitle
};
IReadOnlyList<IStorageFolder> result =
await _view.StorageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions
{
Title = Localization.ExportRomsDialogTitle
});
string result = await dlgOpen.ShowAsync(_view);
if(result == null) return;
if(result.Count < 1) return;
var dialog = new ExportRoms();
var viewModel = new ExportRomsViewModel(dialog, result, SelectedRomSet.Id);
var viewModel = new ExportRomsViewModel(dialog, result[0].Path.LocalPath, SelectedRomSet.Id);
dialog.DataContext = viewModel;
await dialog.ShowDialog(_view);
_ = dialog.ShowDialog(_view);
}
async void ExecuteMountCommand()
async Task ExecuteMountCommandAsync()
{
if(Vfs != null) return;
var dlgOpen = new OpenFolderDialog
{
Title = Localization.SelectMountPointDialogTitle
};
IReadOnlyList<IStorageFolder> result =
await _view.StorageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions
{
Title = Localization.SelectMountPointDialogTitle
});
string result = await dlgOpen.ShowAsync(_view);
if(result == null) return;
if(result.Count < 1) return;
try
{
Vfs = new Vfs();
Vfs.Umounted += VfsOnUmounted;
Vfs.MountTo(result);
Vfs.MountTo(result[0].Path.LocalPath);
}
catch(Exception)
{
@@ -333,7 +293,7 @@ public class MainWindowViewModel : ViewModelBase
void ExecuteUmountCommand() => Vfs?.Umount();
async void ExecuteUpdateStatsCommand()
async Task ExecuteUpdateStatsCommandAsync()
{
ButtonResult result = await MessageBoxManager
.GetMessageBoxStandard(Localization.DatabaseMenuUpdateStatsText,
@@ -347,6 +307,6 @@ public class MainWindowViewModel : ViewModelBase
var view = new UpdateStats();
var viewModel = new UpdateStatsViewModel(view);
view.DataContext = viewModel;
await view.ShowDialog(_view);
_ = view.ShowDialog(_view);
}
}

View File

@@ -35,19 +35,9 @@ using RomRepoMgr.Views;
namespace RomRepoMgr.ViewModels;
public sealed class RemoveDatViewModel : ViewModelBase
public sealed class RemoveDatViewModel(RemoveDat view, long romSetId) : ViewModelBase
{
readonly long _romSetId;
readonly RemoveDat _view;
string _statusMessage;
public RemoveDatViewModel(RemoveDat view, long romSetId)
{
_view = view;
_romSetId = romSetId;
}
public string Title => Localization.RemoveDatTitle;
string _statusMessage;
public string StatusMessage
{
@@ -55,52 +45,55 @@ public sealed class RemoveDatViewModel : ViewModelBase
set => this.RaiseAndSetIfChanged(ref _statusMessage, value);
}
internal void OnOpened() => Task.Run(() =>
internal void OnOpened()
{
using var ctx = Context.Create(Settings.Settings.Current.DatabasePath);
Dispatcher.UIThread.Post(() => StatusMessage = Localization.RetrievingRomSetFromDatabase);
RomSet romSet = ctx.RomSets.Find(_romSetId);
if(romSet == null) return;
Dispatcher.UIThread.Post(() => StatusMessage = Localization.RemovingRomSetFromDatabase);
ctx.RomSets.Remove(romSet);
Dispatcher.UIThread.Post(() => StatusMessage = Localization.SavingChangesToDatabase);
ctx.SaveChanges();
Dispatcher.UIThread.Post(() => StatusMessage = Localization.RemovingDatFileFromRepo);
var sha384Bytes = new byte[48];
string sha384 = romSet.Sha384;
for(var i = 0; i < 48; i++)
_ = Task.Run(() =>
{
if(sha384[i * 2] >= 0x30 && sha384[i * 2] <= 0x39)
sha384Bytes[i] = (byte)((sha384[i * 2] - 0x30) * 0x10);
else if(sha384[i * 2] >= 0x41 && sha384[i * 2] <= 0x46)
sha384Bytes[i] = (byte)((sha384[i * 2] - 0x37) * 0x10);
else if(sha384[i * 2] >= 0x61 && sha384[i * 2] <= 0x66)
sha384Bytes[i] = (byte)((sha384[i * 2] - 0x57) * 0x10);
using var ctx = Context.Create(Settings.Settings.Current.DatabasePath);
if(sha384[i * 2 + 1] >= 0x30 && sha384[i * 2 + 1] <= 0x39)
sha384Bytes[i] += (byte)(sha384[i * 2 + 1] - 0x30);
else if(sha384[i * 2 + 1] >= 0x41 && sha384[i * 2 + 1] <= 0x46)
sha384Bytes[i] += (byte)(sha384[i * 2 + 1] - 0x37);
else if(sha384[i * 2 + 1] >= 0x61 && sha384[i * 2 + 1] <= 0x66)
sha384Bytes[i] += (byte)(sha384[i * 2 + 1] - 0x57);
}
Dispatcher.UIThread.Post(() => StatusMessage = Localization.RetrievingRomSetFromDatabase);
string datHash32 = Base32.ToBase32String(sha384Bytes);
string datFilesPath = Path.Combine(Settings.Settings.Current.RepositoryPath, "datfiles");
string compressedDatPath = Path.Combine(datFilesPath, datHash32 + ".lz");
RomSet romSet = ctx.RomSets.Find(romSetId);
if(File.Exists(compressedDatPath)) File.Delete(compressedDatPath);
if(romSet == null) return;
Dispatcher.UIThread.Post(_view.Close);
});
Dispatcher.UIThread.Post(() => StatusMessage = Localization.RemovingRomSetFromDatabase);
ctx.RomSets.Remove(romSet);
Dispatcher.UIThread.Post(() => StatusMessage = Localization.SavingChangesToDatabase);
ctx.SaveChanges();
Dispatcher.UIThread.Post(() => StatusMessage = Localization.RemovingDatFileFromRepo);
var sha384Bytes = new byte[48];
string sha384 = romSet.Sha384;
for(var i = 0; i < 48; i++)
{
if(sha384[i * 2] >= 0x30 && sha384[i * 2] <= 0x39)
sha384Bytes[i] = (byte)((sha384[i * 2] - 0x30) * 0x10);
else if(sha384[i * 2] >= 0x41 && sha384[i * 2] <= 0x46)
sha384Bytes[i] = (byte)((sha384[i * 2] - 0x37) * 0x10);
else if(sha384[i * 2] >= 0x61 && sha384[i * 2] <= 0x66)
sha384Bytes[i] = (byte)((sha384[i * 2] - 0x57) * 0x10);
if(sha384[i * 2 + 1] >= 0x30 && sha384[i * 2 + 1] <= 0x39)
sha384Bytes[i] += (byte)(sha384[i * 2 + 1] - 0x30);
else if(sha384[i * 2 + 1] >= 0x41 && sha384[i * 2 + 1] <= 0x46)
sha384Bytes[i] += (byte)(sha384[i * 2 + 1] - 0x37);
else if(sha384[i * 2 + 1] >= 0x61 && sha384[i * 2 + 1] <= 0x66)
sha384Bytes[i] += (byte)(sha384[i * 2 + 1] - 0x57);
}
string datHash32 = Base32.ToBase32String(sha384Bytes);
string datFilesPath = Path.Combine(Settings.Settings.Current.RepositoryPath, "datfiles");
string compressedDatPath = Path.Combine(datFilesPath, datHash32 + ".lz");
if(File.Exists(compressedDatPath)) File.Delete(compressedDatPath);
Dispatcher.UIThread.Post(view.Close);
});
}
}

View File

@@ -24,10 +24,11 @@
*******************************************************************************/
using System;
using System.Collections.Generic;
using System.IO;
using System.Reactive;
using System.Threading.Tasks;
using Avalonia.Controls;
using Avalonia.Platform.Storage;
using Avalonia.Threading;
using Microsoft.EntityFrameworkCore;
using MsBox.Avalonia;
@@ -64,10 +65,10 @@ public sealed class SettingsViewModel : ViewModelBase
_unArChanged = false;
CloseCommand = ReactiveCommand.Create(ExecuteCloseCommand);
UnArCommand = ReactiveCommand.Create(ExecuteUnArCommand);
TemporaryCommand = ReactiveCommand.Create(ExecuteTemporaryCommand);
RepositoryCommand = ReactiveCommand.Create(ExecuteRepositoryCommand);
DatabaseCommand = ReactiveCommand.Create(ExecuteDatabaseCommand);
UnArCommand = ReactiveCommand.CreateFromTask(ExecuteUnArCommandAsync);
TemporaryCommand = ReactiveCommand.CreateFromTask(ExecuteTemporaryCommandAsync);
RepositoryCommand = ReactiveCommand.CreateFromTask(ExecuteRepositoryCommandAsync);
DatabaseCommand = ReactiveCommand.CreateFromTask(ExecuteDatabaseCommandAsync);
SaveCommand = ReactiveCommand.Create(ExecuteSaveCommand);
DatabasePath = Settings.Settings.Current.DatabasePath;
@@ -78,14 +79,6 @@ public sealed class SettingsViewModel : ViewModelBase
if(!string.IsNullOrWhiteSpace(UnArPath)) CheckUnAr();
}
public string ChooseLabel => Localization.ChooseLabel;
public string Title => Localization.SettingsTitle;
public string CloseLabel => Localization.CloseLabel;
public string DatabaseLabel => Localization.DatabaseFileLabel;
public string RepositoryLabel => Localization.RepositoryFolderLabel;
public string TemporaryLabel => Localization.TemporaryFolderLabel;
public string UnArPathLabel => Localization.UnArPathLabel;
public ReactiveCommand<Unit, Unit> UnArCommand { get; }
public ReactiveCommand<Unit, Unit> TemporaryCommand { get; }
public ReactiveCommand<Unit, Unit> RepositoryCommand { get; }
@@ -137,8 +130,6 @@ public sealed class SettingsViewModel : ViewModelBase
set => this.RaiseAndSetIfChanged(ref _unArVersion, value);
}
public string SaveLabel => Localization.SaveLabel;
void CheckUnAr()
{
var worker = new Compression();
@@ -146,16 +137,16 @@ public sealed class SettingsViewModel : ViewModelBase
worker.FinishedWithText += CheckUnArFinished;
worker.FailedWithText += CheckUnArFailed;
Task.Run(() => worker.CheckUnAr(UnArPath));
_ = Task.Run(() => worker.CheckUnAr(UnArPath));
}
async void CheckUnArFailed(object sender, ErrorEventArgs args)
void CheckUnArFailed(object sender, ErrorEventArgs args)
{
UnArVersion = "";
UnArPath = "";
await MessageBoxManager.GetMessageBoxStandard(Localization.Error, $"{args.Message}", ButtonEnum.Ok, Icon.Error)
.ShowWindowDialogAsync(_view);
_ = MessageBoxManager.GetMessageBoxStandard(Localization.Error, args.Message, ButtonEnum.Ok, Icon.Error)
.ShowWindowDialogAsync(_view);
}
void CheckUnArFinished(object sender, MessageEventArgs args) => Dispatcher.UIThread.Post(() =>
@@ -166,65 +157,65 @@ public sealed class SettingsViewModel : ViewModelBase
void ExecuteCloseCommand() => _view.Close();
async void ExecuteUnArCommand()
async Task ExecuteUnArCommandAsync()
{
var dlgFile = new OpenFileDialog
IReadOnlyList<IStorageFile> result = await _view.StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
{
Title = Localization.ChooseUnArExecutable,
AllowMultiple = false
};
AllowMultiple = false,
SuggestedStartLocation =
!string.IsNullOrWhiteSpace(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles))
? await _view.StorageProvider.TryGetFolderFromPathAsync(Environment.GetFolderPath(Environment
.SpecialFolder.ProgramFiles))
: await _view.StorageProvider.TryGetWellKnownFolderAsync(WellKnownFolder.Desktop)
});
if(!string.IsNullOrWhiteSpace(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles)))
dlgFile.Directory = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
if(result.Count != 1) return;
string[] result = await dlgFile.ShowAsync(_view);
if(result?.Length != 1) return;
UnArPath = result[0];
UnArPath = result[0].Path.LocalPath;
CheckUnAr();
}
async void ExecuteTemporaryCommand()
async Task ExecuteTemporaryCommandAsync()
{
var dlgFolder = new OpenFolderDialog
{
Title = Localization.ChooseTemporaryFolder
};
IReadOnlyList<IStorageFolder> result =
await _view.StorageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions
{
Title = Localization.ChooseTemporaryFolder
});
string result = await dlgFolder.ShowAsync(_view);
if(result.Count < 1) return;
if(result == null) return;
TemporaryPath = result;
TemporaryPath = result[0].Path.LocalPath;
}
async void ExecuteRepositoryCommand()
async Task ExecuteRepositoryCommandAsync()
{
var dlgFolder = new OpenFolderDialog
{
Title = Localization.ChooseRepositoryFolder
};
IReadOnlyList<IStorageFolder> result =
await _view.StorageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions
{
Title = Localization.ChooseRepositoryFolder,
AllowMultiple = false
});
string result = await dlgFolder.ShowAsync(_view);
if(result.Count < 1) return;
if(result == null) return;
RepositoryPath = result;
RepositoryPath = result[0].Path.LocalPath;
}
async void ExecuteDatabaseCommand()
async Task ExecuteDatabaseCommandAsync()
{
var dlgFile = new SaveFileDialog
IStorageFile resultFile = await _view.StorageProvider.SaveFilePickerAsync(new FilePickerSaveOptions
{
InitialFileName = "romrepo.db",
Directory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
Title = Localization.ChooseDatabaseFile
};
SuggestedFileName = "romrepo.db",
SuggestedStartLocation = await _view.StorageProvider.TryGetWellKnownFolderAsync(WellKnownFolder.Documents),
Title = Localization.ChooseDatabaseFile,
ShowOverwritePrompt = true
});
string result = await dlgFile.ShowAsync(_view);
if(resultFile == null) return;
if(result == null) return;
string result = resultFile.Path.LocalPath;
if(File.Exists(result))
{

View File

@@ -37,7 +37,6 @@ using RomRepoMgr.Core.EventArgs;
using RomRepoMgr.Core.Models;
using RomRepoMgr.Core.Workers;
using RomRepoMgr.Database;
using RomRepoMgr.Resources;
namespace RomRepoMgr.ViewModels;
@@ -180,33 +179,30 @@ public sealed class SplashWindowViewModel : ViewModelBase
set => this.RaiseAndSetIfChanged(ref _loadingRomSetsUnknown, value);
}
public string LoadingText => "ROM Repository Manager";
public string LoadingSettingsText => Localization.LoadingSettingsText;
public string CheckingUnArText => Localization.CheckingUnArText;
public string LoadingDatabaseText => Localization.LoadingDatabaseText;
public string MigratingDatabaseText => Localization.MigratingDatabaseText;
public string LoadingRomSetsText => Localization.LoadingRomSetsText;
public string ExitButtonText => Localization.ExitButtonText;
public string LoadingText => "ROM Repository Manager";
void ExecuteExitCommand() =>
(Application.Current.ApplicationLifetime as ClassicDesktopStyleApplicationLifetime)?.Shutdown();
internal void OnOpened() => Dispatcher.UIThread.Post(LoadSettings);
void LoadSettings() => Task.Run(() =>
void LoadSettings()
{
try
_ = Task.Run(() =>
{
Settings.Settings.LoadSettings();
try
{
Settings.Settings.LoadSettings();
Dispatcher.UIThread.Post(CheckUnAr);
}
catch(Exception e)
{
// TODO: Log error
Dispatcher.UIThread.Post(FailedLoadingSettings);
}
});
Dispatcher.UIThread.Post(CheckUnAr);
}
catch(Exception e)
{
// TODO: Log error
Dispatcher.UIThread.Post(FailedLoadingSettings);
}
});
}
void FailedLoadingSettings()
{
@@ -215,24 +211,27 @@ public sealed class SplashWindowViewModel : ViewModelBase
ExitVisible = true;
}
void CheckUnAr() => Task.Run(() =>
void CheckUnAr()
{
LoadingSettingsUnknown = false;
LoadingSettingsOk = true;
try
_ = Task.Run(() =>
{
var worker = new Compression();
Settings.Settings.UnArUsable = worker.CheckUnAr(Settings.Settings.Current.UnArchiverPath);
LoadingSettingsUnknown = false;
LoadingSettingsOk = true;
Dispatcher.UIThread.Post(LoadDatabase);
}
catch(Exception e)
{
// TODO: Log error
Dispatcher.UIThread.Post(FailedCheckUnAr);
}
});
try
{
var worker = new Compression();
Settings.Settings.UnArUsable = worker.CheckUnAr(Settings.Settings.Current.UnArchiverPath);
Dispatcher.UIThread.Post(LoadDatabase);
}
catch(Exception e)
{
// TODO: Log error
Dispatcher.UIThread.Post(FailedCheckUnAr);
}
});
}
void FailedCheckUnAr()
{
@@ -246,7 +245,7 @@ public sealed class SplashWindowViewModel : ViewModelBase
CheckingUnArUnknown = false;
CheckingUnArOk = true;
Task.Run(() =>
_ = Task.Run(() =>
{
try
{
@@ -278,7 +277,7 @@ public sealed class SplashWindowViewModel : ViewModelBase
LoadingDatabaseUnknown = false;
LoadingDatabaseOk = true;
Task.Run(() =>
_ = Task.Run(() =>
{
try
{
@@ -308,7 +307,7 @@ public sealed class SplashWindowViewModel : ViewModelBase
MigratingDatabaseUnknown = false;
MigratingDatabaseOk = true;
Task.Run(() =>
_ = Task.Run(() =>
{
try
{

View File

@@ -60,21 +60,6 @@ public sealed class UpdateStatsViewModel : ViewModelBase
RomSets = [];
}
public string Title => Localization.UpdateStatsTitle;
public string RomSetNameLabel => Localization.RomSetNameLabel;
public string RomSetVersionLabel => Localization.RomSetVersionLabel;
public string RomSetAuthorLabel => Localization.RomSetAuthorLabel;
public string RomSetCategoryLabel => Localization.RomSetCategoryLabel;
public string RomSetDateLabel => Localization.RomSetDateLabel;
public string RomSetDescriptionLabel => Localization.RomSetDescriptionLabel;
public string RomSetCommentLabel => Localization.RomSetCommentLabel;
public string RomSetTotalMachinesLabel => Localization.RomSetTotalMachinesLabel;
public string RomSetCompleteMachinesLabel => Localization.RomSetCompleteMachinesLabel;
public string RomSetIncompleteMachinesLabel => Localization.RomSetIncompleteMachinesLabel;
public string RomSetTotalRomsLabel => Localization.RomSetTotalRomsLabel;
public string RomSetHaveRomsLabel => Localization.RomSetHaveRomsLabel;
public string RomSetMissRomsLabel => Localization.RomSetMissRomsLabel;
public string StatusMessage
{
get => _statusMessage;
@@ -125,142 +110,144 @@ public sealed class UpdateStatsViewModel : ViewModelBase
public ObservableCollection<RomSetModel> RomSets { get; }
public string CloseLabel => Localization.CloseLabel;
public ReactiveCommand<Unit, Unit> CloseCommand { get; }
internal void OnOpened() => Task.Run(() =>
internal void OnOpened()
{
using var ctx = Context.Create(Settings.Settings.Current.DatabasePath);
Dispatcher.UIThread.Post(() =>
_ = Task.Run(() =>
{
StatusMessage = Localization.RetrievingRomSetsFromDatabase;
ProgressVisible = true;
IndeterminateProgress = true;
});
long romSetCount = ctx.RomSets.LongCount();
Dispatcher.UIThread.Post(() => { StatusMessage = Localization.RemovingOldStatistics; });
ctx.Database.ExecuteSql($"DELETE FROM \"RomSetStats\"");
Dispatcher.UIThread.Post(() =>
{
IndeterminateProgress = false;
MinimumValue = 0;
MaximumValue = romSetCount;
CurrentValue = 0;
});
long pos = 0;
foreach(RomSet romSet in ctx.RomSets)
{
long currentPos = pos;
using var ctx = Context.Create(Settings.Settings.Current.DatabasePath);
Dispatcher.UIThread.Post(() =>
{
StatusMessage = string.Format(Localization.CalculatingStatisticsForRomSet,
romSet.Name,
romSet.Version,
romSet.Description);
CurrentValue = currentPos;
StatusMessage = Localization.RetrievingRomSetsFromDatabase;
ProgressVisible = true;
IndeterminateProgress = true;
});
try
{
RomSetStat stats = ctx.RomSets.Where(r => r.Id == romSet.Id)
.Select(r => new RomSetStat
{
RomSetId = r.Id,
TotalMachines = r.Machines.Count,
CompleteMachines =
r.Machines.Count(m => m.Files.Count > 0 &&
m.Disks.Count == 0 &&
m.Files.All(f => f.File.IsInRepo)) +
r.Machines.Count(m => m.Disks.Count > 0 &&
m.Files.Count == 0 &&
m.Disks.All(f => f.Disk.IsInRepo)) +
r.Machines.Count(m => m.Files.Count > 0 &&
m.Disks.Count > 0 &&
m.Files.All(f => f.File.IsInRepo) &&
m.Disks.All(f => f.Disk.IsInRepo)),
IncompleteMachines =
r.Machines.Count(m => m.Files.Count > 0 &&
m.Disks.Count == 0 &&
m.Files.Any(f => !f.File.IsInRepo)) +
r.Machines.Count(m => m.Disks.Count > 0 &&
m.Files.Count == 0 &&
m.Disks.Any(f => !f.Disk.IsInRepo)) +
r.Machines.Count(m => m.Files.Count > 0 &&
m.Disks.Count > 0 &&
(m.Files.Any(f => !f.File.IsInRepo) ||
m.Disks.Any(f => !f.Disk.IsInRepo))),
TotalRoms =
r.Machines.Sum(m => m.Files.Count) +
r.Machines.Sum(m => m.Disks.Count) +
r.Machines.Sum(m => m.Medias.Count),
HaveRoms = r.Machines.Sum(m => m.Files.Count(f => f.File.IsInRepo)) +
r.Machines.Sum(m => m.Disks.Count(f => f.Disk.IsInRepo)) +
r.Machines.Sum(m => m.Medias.Count(f => f.Media.IsInRepo)),
MissRoms = r.Machines.Sum(m => m.Files.Count(f => !f.File.IsInRepo)) +
r.Machines.Sum(m => m.Disks.Count(f => !f.Disk.IsInRepo)) +
r.Machines.Sum(m => m.Medias.Count(f => !f.Media.IsInRepo))
})
.FirstOrDefault();
long romSetCount = ctx.RomSets.LongCount();
ctx.RomSetStats.Add(stats);
Dispatcher.UIThread.Post(() => StatusMessage = Localization.RemovingOldStatistics);
ctx.Database.ExecuteSql($"DELETE FROM \"RomSetStats\"");
Dispatcher.UIThread.Post(() =>
{
IndeterminateProgress = false;
MinimumValue = 0;
MaximumValue = romSetCount;
CurrentValue = 0;
});
long pos = 0;
foreach(RomSet romSet in ctx.RomSets)
{
long currentPos = pos;
Dispatcher.UIThread.Post(() =>
{
RomSets.Add(new RomSetModel
{
Id = romSet.Id,
Author = romSet.Author,
Comment = romSet.Comment,
Date = romSet.Date,
Description = romSet.Description,
Filename = romSet.Filename,
Homepage = romSet.Homepage,
Name = romSet.Name,
Sha384 = romSet.Sha384,
Version = romSet.Version,
TotalMachines = stats.TotalMachines,
CompleteMachines = stats.CompleteMachines,
IncompleteMachines = stats.IncompleteMachines,
TotalRoms = stats.TotalRoms,
HaveRoms = stats.HaveRoms,
MissRoms = stats.MissRoms,
Category = romSet.Category
});
StatusMessage = string.Format(Localization.CalculatingStatisticsForRomSet,
romSet.Name,
romSet.Version,
romSet.Description);
CurrentValue = currentPos;
});
try
{
RomSetStat stats = ctx.RomSets.Where(r => r.Id == romSet.Id)
.Select(r => new RomSetStat
{
RomSetId = r.Id,
TotalMachines = r.Machines.Count,
CompleteMachines =
r.Machines.Count(m => m.Files.Count > 0 &&
m.Disks.Count == 0 &&
m.Files.All(f => f.File.IsInRepo)) +
r.Machines.Count(m => m.Disks.Count > 0 &&
m.Files.Count == 0 &&
m.Disks.All(f => f.Disk.IsInRepo)) +
r.Machines.Count(m => m.Files.Count > 0 &&
m.Disks.Count > 0 &&
m.Files.All(f => f.File.IsInRepo) &&
m.Disks.All(f => f.Disk.IsInRepo)),
IncompleteMachines =
r.Machines.Count(m => m.Files.Count > 0 &&
m.Disks.Count == 0 &&
m.Files.Any(f => !f.File.IsInRepo)) +
r.Machines.Count(m => m.Disks.Count > 0 &&
m.Files.Count == 0 &&
m.Disks.Any(f => !f.Disk.IsInRepo)) +
r.Machines.Count(m => m.Files.Count > 0 &&
m.Disks.Count > 0 &&
(m.Files.Any(f => !f.File.IsInRepo) ||
m.Disks.Any(f => !f.Disk.IsInRepo))),
TotalRoms =
r.Machines.Sum(m => m.Files.Count) +
r.Machines.Sum(m => m.Disks.Count) +
r.Machines.Sum(m => m.Medias.Count),
HaveRoms = r.Machines.Sum(m => m.Files.Count(f => f.File.IsInRepo)) +
r.Machines.Sum(m => m.Disks.Count(f => f.Disk.IsInRepo)) +
r.Machines.Sum(m => m.Medias.Count(f => f.Media.IsInRepo)),
MissRoms = r.Machines.Sum(m => m.Files.Count(f => !f.File.IsInRepo)) +
r.Machines.Sum(m => m.Disks.Count(f => !f.Disk.IsInRepo)) +
r.Machines.Sum(m => m.Medias.Count(f => !f.Media.IsInRepo))
})
.FirstOrDefault();
ctx.RomSetStats.Add(stats);
Dispatcher.UIThread.Post(() =>
{
RomSets.Add(new RomSetModel
{
Id = romSet.Id,
Author = romSet.Author,
Comment = romSet.Comment,
Date = romSet.Date,
Description = romSet.Description,
Filename = romSet.Filename,
Homepage = romSet.Homepage,
Name = romSet.Name,
Sha384 = romSet.Sha384,
Version = romSet.Version,
TotalMachines = stats.TotalMachines,
CompleteMachines = stats.CompleteMachines,
IncompleteMachines = stats.IncompleteMachines,
TotalRoms = stats.TotalRoms,
HaveRoms = stats.HaveRoms,
MissRoms = stats.MissRoms,
Category = romSet.Category
});
});
}
catch(Exception)
{
// Ignored
}
pos++;
}
catch(Exception)
Dispatcher.UIThread.Post(() =>
{
// Ignored
}
StatusMessage = Localization.SavingChangesToDatabase;
ProgressVisible = true;
IndeterminateProgress = true;
});
pos++;
}
ctx.SaveChanges();
Dispatcher.UIThread.Post(() =>
{
StatusMessage = Localization.SavingChangesToDatabase;
ProgressVisible = true;
IndeterminateProgress = true;
Dispatcher.UIThread.Post(() =>
{
StatusMessage = Localization.Finished;
ProgressVisible = false;
CanClose = true;
});
});
ctx.SaveChanges();
Dispatcher.UIThread.Post(() =>
{
StatusMessage = Localization.Finished;
ProgressVisible = false;
CanClose = true;
});
});
}
void ExecuteCloseCommand() => _view.Close();
}

View File

@@ -29,6 +29,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:RomRepoMgr.ViewModels;assembly=RomRepoMgr"
xmlns:resources="clr-namespace:RomRepoMgr.Resources"
mc:Ignorable="d"
d:DesignWidth="800"
d:DesignHeight="450"
@@ -37,19 +38,14 @@
x:Class="RomRepoMgr.Views.About"
Icon="/Assets/avalonia-logo.ico"
CanResize="False"
Title="{Binding Title}">
Title="{x:Static resources:Localization.AboutTitle}">
<Design.DataContext>
<vm:AboutViewModel />
</Design.DataContext>
<Border Padding="15">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="*" /> <RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid RowDefinitions="Auto,*,Auto">
<Grid Grid.Row="0"
ColumnDefinitions="Auto,*">
<Border Grid.Column="0"
BorderThickness="5">
<Image Source="/Assets/avalonia-logo.ico"
@@ -58,18 +54,16 @@
</Border>
<Grid Grid.Column="1"
HorizontalAlignment="Left"
VerticalAlignment="Center">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
</Grid.RowDefinitions>
VerticalAlignment="Center"
RowDefinitions="Auto,Auto">
<TextBlock Grid.Row="0"
Text="{Binding SoftwareName}"
Text="{Binding SoftwareName, Mode=OneWay}"
HorizontalAlignment="Left"
VerticalAlignment="Center"
FontSize="16"
FontWeight="Bold" />
<TextBlock Grid.Row="1"
Text="{Binding VersionText}"
Text="{Binding VersionText, Mode=OneWay}"
HorizontalAlignment="Left"
VerticalAlignment="Center" />
</Grid>
@@ -79,28 +73,22 @@
VerticalAlignment="Stretch">
<TabItem>
<TabItem.Header>
<TextBlock Text="{Binding AboutLabel}" />
<TextBlock Text="{x:Static resources:Localization.AboutLabel}" />
</TabItem.Header>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="12" />
<RowDefinition Height="Auto" /> <RowDefinition Height="12" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid RowDefinitions="Auto,12,Auto,12,Auto,Auto,*">
<TextBlock Grid.Row="0"
Text="{Binding SuiteName}" />
Text="{Binding SuiteName, Mode=OneWay}" />
<TextBlock Grid.Row="2"
Text="{Binding Copyright}" />
Text="{Binding Copyright, Mode=OneWay}" />
<Button Grid.Row="4"
BorderThickness="0"
Background="Transparent"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Padding="0"
Command="{Binding WebsiteCommand}">
Command="{Binding WebsiteCommand, Mode=OneWay}">
<!-- TODO: TextDecorations="Underline" in next Avalonia UI version -->
<TextBlock Text="{Binding Website}"
<TextBlock Text="{Binding Website, Mode=OneWay}"
Foreground="Blue" />
</Button>
<Button Grid.Row="5"
@@ -109,32 +97,32 @@
HorizontalAlignment="Left"
VerticalAlignment="Center"
Padding="0"
Command="{Binding LicenseCommand}">
Command="{Binding LicenseCommand, Mode=OneWay}">
<!-- TODO: TextDecorations="Underline" in next Avalonia UI version -->
<TextBlock Text="{Binding License}"
<TextBlock Text="{x:Static resources:Localization.LicenseLabel}"
Foreground="Blue" />
</Button>
</Grid>
</TabItem>
<TabItem>
<TabItem.Header>
<TextBlock Text="{Binding LibrariesLabel}" />
<TextBlock Text="{x:Static resources:Localization.LibrariesLabel}" />
</TabItem.Header>
<DataGrid ItemsSource="{Binding Assemblies}"
<DataGrid ItemsSource="{Binding Assemblies, Mode=OneWay}"
HorizontalScrollBarVisibility="Visible">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Name}"
<DataGridTextColumn Binding="{Binding Name, Mode=OneWay}"
Width="Auto"
IsReadOnly="True">
<DataGridTextColumn.Header>
<TextBlock Text="{Binding AssembliesLibraryText}" />
<TextBlock Text="{x:Static resources:Localization.AssembliesLibraryText}" />
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn Binding="{Binding Version}"
<DataGridTextColumn Binding="{Binding Version, Mode=OneWay}"
Width="Auto"
IsReadOnly="True">
<DataGridTextColumn.Header>
<TextBlock Text="{Binding AssembliesVersionText}" />
<TextBlock Text="{x:Static resources:Localization.AssembliesVersionText}" />
</DataGridTextColumn.Header>
</DataGridTextColumn>
</DataGrid.Columns>
@@ -142,17 +130,17 @@
</TabItem>
<TabItem>
<TabItem.Header>
<TextBlock Text="{Binding AuthorsLabel}" />
<TextBlock Text="{x:Static resources:Localization.AuthorsLabel}" />
</TabItem.Header>
<TextBox IsReadOnly="True"
Text="{Binding Authors}" />
Text="{x:Static resources:Localization.AuthorsText}" />
</TabItem>
</TabControl>
<Button Grid.Row="2"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Command="{Binding CloseCommand}">
<TextBlock Text="{Binding CloseLabel}" />
Command="{Binding CloseCommand, Mode=OneWay}">
<TextBlock Text="{x:Static resources:Localization.CloseLabel}" />
</Button>
</Grid>
</Border>

View File

@@ -29,6 +29,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:RomRepoMgr.ViewModels;assembly=RomRepoMgr"
xmlns:resources="clr-namespace:RomRepoMgr.Resources"
mc:Ignorable="d"
d:DesignWidth="800"
d:DesignHeight="450"
@@ -37,242 +38,207 @@
x:Class="RomRepoMgr.Views.EditDat"
Icon="/Assets/avalonia-logo.ico"
CanResize="False"
Title="{Binding Title}"
Title="{x:Static resources:Localization.EditDatTitle}"
WindowStartupLocation="CenterScreen">
<Design.DataContext>
<vm:EditDatViewModel />
</Design.DataContext>
<Border Padding="15">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="140" /> <ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto">
<Grid Grid.Row="0"
ColumnDefinitions="140,*">
<TextBlock Grid.Column="0"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Text="{Binding NameLabel}"
Text="{x:Static resources:Localization.RomSetNameLabel}"
FontWeight="Bold"
Padding="5" />
<TextBox Grid.Column="1"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
Text="{Binding Name}"
Text="{Binding Name, Mode=TwoWay}"
Padding="5" />
</Grid>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="140" /> <ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid Grid.Row="1"
ColumnDefinitions="140,*">
<TextBlock Grid.Column="0"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Text="{Binding VersionLabel}"
Text="{x:Static resources:Localization.RomSetVersionLabel}"
FontWeight="Bold"
Padding="5" />
<TextBox Grid.Column="1"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
Text="{Binding Version}"
Text="{Binding Version, Mode=TwoWay}"
Padding="5" />
</Grid>
<Grid Grid.Row="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="140" /> <ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid Grid.Row="2"
ColumnDefinitions="140,*">
<TextBlock Grid.Column="0"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Text="{Binding AuthorLabel}"
Text="{x:Static resources:Localization.RomSetAuthorLabel}"
FontWeight="Bold"
Padding="5" />
<TextBox Grid.Column="1"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
Text="{Binding Author}"
Text="{Binding Author, Mode=TwoWay}"
Padding="5" />
</Grid>
<Grid Grid.Row="3">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="140" /> <ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid Grid.Row="3"
ColumnDefinitions="140,*">
<TextBlock Grid.Column="0"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Text="{Binding CategoryLabel}"
Text="{x:Static resources:Localization.RomSetCategoryLabel}"
FontWeight="Bold"
Padding="5" />
<TextBox Grid.Column="1"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
Text="{Binding Category}"
Text="{Binding Category, Mode=TwoWay}"
Padding="5" />
</Grid>
<Grid Grid.Row="4">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="140" /> <ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid Grid.Row="4"
ColumnDefinitions="140,*">
<TextBlock Grid.Column="0"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Text="{Binding CommentLabel}"
Text="{x:Static resources:Localization.RomSetCommentLabel}"
FontWeight="Bold"
Padding="5" />
<TextBox Grid.Column="1"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
Text="{Binding Comment}"
Text="{Binding Comment, Mode=TwoWay}"
Padding="5" />
</Grid>
<Grid Grid.Row="5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="140" /> <ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid Grid.Row="5"
ColumnDefinitions="140,*">
<TextBlock Grid.Column="0"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Text="{Binding DateLabel}"
Text="{x:Static resources:Localization.RomSetDateLabel}"
FontWeight="Bold"
Padding="5" />
<TextBox Grid.Column="1"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
Text="{Binding Date}"
Text="{Binding Date, Mode=TwoWay}"
Padding="5" />
</Grid>
<Grid Grid.Row="6">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="140" /> <ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid Grid.Row="6"
ColumnDefinitions="140,*">
<TextBlock Grid.Column="0"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Text="{Binding DescriptionLabel}"
Text="{x:Static resources:Localization.RomSetDescriptionLabel}"
FontWeight="Bold"
Padding="5" />
<TextBox Grid.Column="1"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
Text="{Binding Description}"
Text="{Binding Description, Mode=TwoWay}"
Padding="5" />
</Grid>
<Grid Grid.Row="7">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="140" /> <ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid Grid.Row="7"
ColumnDefinitions="140,*">
<TextBlock Grid.Column="0"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Text="{Binding HomepageLabel}"
Text="{x:Static resources:Localization.HomepageLabel}"
FontWeight="Bold"
Padding="5" />
<TextBox Grid.Column="1"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
Text="{Binding Homepage}"
Text="{Binding Homepage, Mode=TwoWay}"
Padding="5" />
</Grid>
<Grid Grid.Row="8">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="140" /> <ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid Grid.Row="8"
ColumnDefinitions="140,*">
<TextBlock Grid.Column="0"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Text="{Binding TotalMachinesLabel}"
Text="{x:Static resources:Localization.TotalMachinesLabel}"
FontWeight="Bold"
Padding="5" />
<TextBlock Grid.Column="1"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Text="{Binding TotalMachines}"
Text="{Binding TotalMachines, Mode=OneWay}"
Padding="5" />
</Grid>
<Grid Grid.Row="9">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="140" /> <ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid Grid.Row="9"
ColumnDefinitions="140,*">
<TextBlock Grid.Column="0"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Text="{Binding CompleteMachinesLabel}"
Text="{x:Static resources:Localization.CompleteMachinesLabel}"
FontWeight="Bold"
Padding="5" />
<TextBlock Grid.Column="1"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Text="{Binding CompleteMachines}"
Text="{Binding CompleteMachines, Mode=OneWay}"
Padding="5" />
</Grid>
<Grid Grid.Row="10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="140" /> <ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid Grid.Row="10"
ColumnDefinitions="140,*">
<TextBlock Grid.Column="0"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Text="{Binding IncompleteMachinesLabel}"
Text="{x:Static resources:Localization.IncompleteMachinesLabel}"
FontWeight="Bold"
Padding="5" />
<TextBlock Grid.Column="1"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Text="{Binding IncompleteMachines}"
Text="{Binding IncompleteMachines, Mode=OneWay}"
Padding="5" />
</Grid>
<Grid Grid.Row="11">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="140" /> <ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid Grid.Row="11"
ColumnDefinitions="140,*">
<TextBlock Grid.Column="0"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Text="{Binding TotalRomsLabel}"
Text="{x:Static resources:Localization.TotalRomsLabel}"
FontWeight="Bold"
Padding="5" />
<TextBlock Grid.Column="1"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Text="{Binding TotalRoms}"
Text="{Binding TotalRoms, Mode=OneWay}"
Padding="5" />
</Grid>
<Grid Grid.Row="12">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="140" /> <ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid Grid.Row="12"
ColumnDefinitions="140,*">
<TextBlock Grid.Column="0"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Text="{Binding HaveRomsLabel}"
Text="{x:Static resources:Localization.HaveRomsLabel}"
FontWeight="Bold"
Padding="5" />
<TextBlock Grid.Column="1"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Text="{Binding HaveRoms}"
Text="{Binding HaveRoms, Mode=OneWay}"
Padding="5" />
</Grid>
<Grid Grid.Row="13">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="140" /> <ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid Grid.Row="13"
ColumnDefinitions="140,*">
<TextBlock Grid.Column="0"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Text="{Binding MissRomsLabel}"
Text="{x:Static resources:Localization.MissRomsLabel}"
FontWeight="Bold"
Padding="5" />
<TextBlock Grid.Column="1"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Text="{Binding MissRoms}"
Text="{Binding MissRoms, Mode=OneWay}"
Padding="5" />
</Grid>
<StackPanel Grid.Row="14"
@@ -280,21 +246,21 @@
HorizontalAlignment="Right">
<Button HorizontalAlignment="Right"
VerticalAlignment="Center"
Command="{Binding SaveCommand}"
IsVisible="{Binding Modified}">
<TextBlock Text="{Binding SaveLabel}" />
Command="{Binding SaveCommand, Mode=OneWay}"
IsVisible="{Binding Modified, Mode=OneWay}">
<TextBlock Text="{x:Static resources:Localization.SaveLabel}" />
</Button>
<Button HorizontalAlignment="Right"
VerticalAlignment="Center"
Command="{Binding CancelCommand}"
IsVisible="{Binding Modified}">
<TextBlock Text="{Binding CancelLabel}" />
Command="{Binding CancelCommand, Mode=OneWay}"
IsVisible="{Binding Modified, Mode=OneWay}">
<TextBlock Text="{x:Static resources:Localization.CancelLabel}" />
</Button>
<Button HorizontalAlignment="Right"
VerticalAlignment="Center"
Command="{Binding CloseCommand}"
IsVisible="{Binding !Modified}">
<TextBlock Text="{Binding CloseLabel}" />
Command="{Binding CloseCommand, Mode=OneWay}"
IsVisible="{Binding !Modified, Mode=OneWay}">
<TextBlock Text="{x:Static resources:Localization.CloseLabel}" />
</Button>
</StackPanel>
</Grid>

View File

@@ -29,6 +29,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:RomRepoMgr.ViewModels;assembly=RomRepoMgr"
xmlns:resources="clr-namespace:RomRepoMgr.Resources"
mc:Ignorable="d"
d:DesignWidth="800"
d:DesignHeight="450"
@@ -37,35 +38,31 @@
x:Class="RomRepoMgr.Views.ExportDat"
Icon="/Assets/avalonia-logo.ico"
CanResize="False"
Title="{Binding Title}"
Title="{x:Static resources:Localization.ExportDatTitle}"
WindowStartupLocation="CenterOwner">
<Design.DataContext>
<vm:ExportDatViewModel />
</Design.DataContext>
<Border Padding="15">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid RowDefinitions="Auto,auto,Auto,Auto">
<TextBlock Grid.Row="0"
Text="{Binding StatusMessage}"
Text="{Binding StatusMessage, Mode=OneWay}"
HorizontalAlignment="Center" />
<ProgressBar Grid.Row="1"
IsIndeterminate="True"
HorizontalAlignment="Stretch"
IsVisible="{Binding ProgressVisible}" />
IsVisible="{Binding ProgressVisible, Mode=OneWay}" />
<TextBlock Grid.Row="2"
Text="{Binding ErrorMessage}"
Text="{Binding ErrorMessage, Mode=OneWay}"
HorizontalAlignment="Center"
Foreground="Red"
IsVisible="{Binding ErrorVisible}" />
IsVisible="{Binding ErrorVisible, Mode=OneWay}" />
<Button Grid.Row="3"
HorizontalAlignment="Right"
VerticalAlignment="Center"
IsEnabled="{Binding CanClose}"
Command="{Binding CloseCommand}">
<TextBlock Text="{Binding CloseLabel}" />
IsEnabled="{Binding CanClose, Mode=OneWay}"
Command="{Binding CloseCommand, Mode=OneWay}">
<TextBlock Text="{x:Static resources:Localization.CloseLabel}" />
</Button>
</Grid>
</Border>

View File

@@ -29,6 +29,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:RomRepoMgr.ViewModels;assembly=RomRepoMgr"
xmlns:resources="clr-namespace:RomRepoMgr.Resources"
mc:Ignorable="d"
d:DesignWidth="800"
d:DesignHeight="450"
@@ -37,57 +38,52 @@
x:Class="RomRepoMgr.Views.ExportRoms"
Icon="/Assets/avalonia-logo.ico"
CanResize="False"
Title="{Binding Title}"
Title="{x:Static resources:Localization.ExportRomsTitle}"
WindowStartupLocation="CenterOwner">
<Design.DataContext>
<vm:ExportRomsViewModel />
</Design.DataContext>
<Border Padding="15">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="*" /> <RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,*,Auto">
<StackPanel Grid.Row="0"
Orientation="Horizontal"
HorizontalAlignment="Stretch">
<TextBlock Text="{Binding PathLabel}"
<TextBlock Text="{x:Static resources:Localization.PathLabel}"
FontWeight="Bold" />
<TextBlock Text="{Binding FolderPath}" />
<TextBlock Text="{Binding FolderPath, Mode=OneWay}" />
</StackPanel>
<TextBlock Grid.Row="1"
Text="{Binding StatusMessage}"
Text="{Binding StatusMessage, Mode=OneWay}"
FontWeight="Bold"
HorizontalAlignment="Center" />
<ProgressBar Grid.Row="2"
Minimum="{Binding ProgressMinimum}"
Maximum="{Binding ProgressMaximum}"
Value="{Binding ProgressValue}"
IsIndeterminate="{Binding ProgressIsIndeterminate}"
IsVisible="{Binding ProgressVisible}" />
Minimum="{Binding ProgressMinimum, Mode=OneWay}"
Maximum="{Binding ProgressMaximum, Mode=OneWay}"
Value="{Binding ProgressValue, Mode=OneWay}"
IsIndeterminate="{Binding ProgressIsIndeterminate, Mode=OneWay}"
IsVisible="{Binding ProgressVisible, Mode=OneWay}" />
<StackPanel Grid.Row="3"
IsVisible="{Binding Progress2Visible}">
<TextBlock Text="{Binding Status2Message}" />
<ProgressBar Minimum="{Binding Progress2Minimum}"
Maximum="{Binding Progress2Maximum}"
Value="{Binding Progress2Value}"
IsIndeterminate="{Binding Progress2IsIndeterminate}" />
IsVisible="{Binding Progress2Visible, Mode=OneWay}">
<TextBlock Text="{Binding Status2Message, Mode=OneWay}" />
<ProgressBar Minimum="{Binding Progress2Minimum, Mode=OneWay}"
Maximum="{Binding Progress2Maximum, Mode=OneWay}"
Value="{Binding Progress2Value, Mode=OneWay}"
IsIndeterminate="{Binding Progress2IsIndeterminate, Mode=OneWay}" />
</StackPanel>
<StackPanel Grid.Row="4"
IsVisible="{Binding Progress3Visible}">
<TextBlock Text="{Binding Status3Message}" />
<ProgressBar Minimum="{Binding Progress3Minimum}"
Maximum="{Binding Progress3Maximum}"
Value="{Binding Progress3Value}"
IsIndeterminate="{Binding Progress3IsIndeterminate}" />
IsVisible="{Binding Progress3Visible, Mode=OneWay}">
<TextBlock Text="{Binding Status3Message, Mode=OneWay}" />
<ProgressBar Minimum="{Binding Progress3Minimum, Mode=OneWay}"
Maximum="{Binding Progress3Maximum, Mode=OneWay}"
Value="{Binding Progress3Value, Mode=OneWay}"
IsIndeterminate="{Binding Progress3IsIndeterminate, Mode=OneWay}" />
</StackPanel>
<Button Grid.Row="5"
HorizontalAlignment="Right"
VerticalAlignment="Center"
IsEnabled="{Binding CanClose}"
Command="{Binding CloseCommand}">
<TextBlock Text="{Binding CloseLabel}" />
IsEnabled="{Binding CanClose, Mode=OneWay}"
Command="{Binding CloseCommand, Mode=OneWay}">
<TextBlock Text="{x:Static resources:Localization.CloseLabel}" />
</Button>
</Grid>
</Border>

View File

@@ -29,6 +29,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:RomRepoMgr.ViewModels;assembly=RomRepoMgr"
xmlns:resources="clr-namespace:RomRepoMgr.Resources"
mc:Ignorable="d"
d:DesignWidth="800"
d:DesignHeight="450"
@@ -37,38 +38,34 @@
x:Class="RomRepoMgr.Views.ImportDat"
Icon="/Assets/avalonia-logo.ico"
CanResize="False"
Title="{Binding Title}"
Title="{x:Static resources:Localization.ImportDatTitle}"
WindowStartupLocation="CenterOwner">
<Design.DataContext>
<vm:ImportDatViewModel />
</Design.DataContext>
<Border Padding="15">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid RowDefinitions="Auto,auto,Auto,Auto">
<TextBlock Grid.Row="0"
Text="{Binding StatusMessage}"
Text="{Binding StatusMessage, Mode=OneWay}"
HorizontalAlignment="Center" />
<ProgressBar Grid.Row="1"
IsIndeterminate="{Binding IndeterminateProgress}"
Maximum="{Binding MaximumValue}"
Minimum="{Binding MinimumValue}"
Value="{Binding CurrentValue}"
IsIndeterminate="{Binding IndeterminateProgress, Mode=OneWay}"
Maximum="{Binding MaximumValue, Mode=OneWay}"
Minimum="{Binding MinimumValue, Mode=OneWay}"
Value="{Binding CurrentValue, Mode=OneWay}"
HorizontalAlignment="Stretch"
IsVisible="{Binding ProgressVisible}" />
IsVisible="{Binding ProgressVisible, Mode=OneWay}" />
<TextBlock Grid.Row="2"
Text="{Binding ErrorMessage}"
Text="{Binding ErrorMessage, Mode=OneWay}"
HorizontalAlignment="Center"
Foreground="Red"
IsVisible="{Binding ErrorVisible}" />
IsVisible="{Binding ErrorVisible, Mode=OneWay}" />
<Button Grid.Row="3"
HorizontalAlignment="Right"
VerticalAlignment="Center"
IsEnabled="{Binding CanClose}"
Command="{Binding CloseCommand}">
<TextBlock Text="{Binding CloseLabel}" />
IsEnabled="{Binding CanClose, Mode=OneWay}"
Command="{Binding CloseCommand, Mode=OneWay}">
<TextBlock Text="{x:Static resources:Localization.CloseLabel}" />
</Button>
</Grid>
</Border>

View File

@@ -29,6 +29,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:RomRepoMgr.ViewModels;assembly=RomRepoMgr"
xmlns:resources="clr-namespace:RomRepoMgr.Resources"
mc:Ignorable="d"
d:DesignWidth="800"
d:DesignHeight="450"
@@ -37,109 +38,102 @@
x:Class="RomRepoMgr.Views.ImportDatFolder"
Icon="/Assets/avalonia-logo.ico"
CanResize="False"
Title="{Binding Title}"
Title="{x:Static resources:Localization.ImportDatFolderTitle}"
WindowStartupLocation="CenterOwner">
<Design.DataContext>
<vm:ImportDatFolderViewModel />
</Design.DataContext>
<Border Padding="15">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="*" /> <RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,*,Auto">
<StackPanel Grid.Row="0"
Orientation="Horizontal"
HorizontalAlignment="Stretch">
<TextBlock Text="{Binding PathLabel}"
<TextBlock Text="{x:Static resources:Localization.PathLabel}"
FontWeight="Bold" />
<TextBlock Text="{Binding FolderPath}" />
<TextBlock Text="{Binding FolderPath, Mode=OneWay}" />
</StackPanel>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid Grid.Row="1"
ColumnDefinitions="Auto,*">
<TextBlock Grid.Column="0"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Text="{Binding CategoryLabel}"
Text="{x:Static resources:Localization.RomSetCategoryLabel}"
FontWeight="Bold"
Padding="5" />
<TextBox Grid.Column="1"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
Text="{Binding Category}"
Text="{Binding Category, Mode=OneWay}"
Padding="5" />
</Grid>
<CheckBox Grid.Row="2"
IsChecked="{Binding AllFilesChecked}"
IsEnabled="{Binding IsReady}">
IsChecked="{Binding AllFilesChecked, Mode=TwoWay}"
IsEnabled="{Binding IsReady, Mode=OneWay}">
<CheckBox.Content>
<TextBlock Text="{Binding AllFilesLabel}" />
<TextBlock Text="{x:Static resources:Localization.AllFilesLabel}" />
</CheckBox.Content>
</CheckBox>
<CheckBox Grid.Row="3"
IsChecked="{Binding RecursiveChecked}"
IsEnabled="{Binding IsReady}">
IsChecked="{Binding RecursiveChecked, Mode=TwoWay}"
IsEnabled="{Binding IsReady, Mode=OneWay}">
<CheckBox.Content>
<TextBlock Text="{Binding RecursiveLabel}" />
<TextBlock Text="{x:Static resources:Localization.RecursiveLabel}" />
</CheckBox.Content>
</CheckBox>
<TextBlock Grid.Row="4"
Text="{Binding StatusMessage}"
Text="{Binding StatusMessage, Mode=OneWay}"
FontWeight="Bold"
HorizontalAlignment="Center" />
<ProgressBar Grid.Row="5"
Minimum="{Binding ProgressMinimum}"
Maximum="{Binding ProgressMaximum}"
Value="{Binding ProgressValue}"
IsIndeterminate="{Binding ProgressIsIndeterminate}"
IsVisible="{Binding ProgressVisible}" />
Minimum="{Binding ProgressMinimum, Mode=OneWay}"
Maximum="{Binding ProgressMaximum, Mode=OneWay}"
Value="{Binding ProgressValue, Mode=OneWay}"
IsIndeterminate="{Binding ProgressIsIndeterminate, Mode=OneWay}"
IsVisible="{Binding ProgressVisible, Mode=OneWay}" />
<StackPanel Grid.Row="6"
IsVisible="{Binding Progress2Visible}">
<TextBlock Text="{Binding Status2Message}" />
<ProgressBar Minimum="{Binding Progress2Minimum}"
Maximum="{Binding Progress2Maximum}"
Value="{Binding Progress2Value}"
IsIndeterminate="{Binding Progress2IsIndeterminate}" />
IsVisible="{Binding Progress2Visible, Mode=OneWay}">
<TextBlock Text="{Binding Status2Message, Mode=OneWay}" />
<ProgressBar Minimum="{Binding Progress2Minimum, Mode=OneWay}"
Maximum="{Binding Progress2Maximum, Mode=OneWay}"
Value="{Binding Progress2Value, Mode=OneWay}"
IsIndeterminate="{Binding Progress2IsIndeterminate, Mode=OneWay}" />
</StackPanel>
<DataGrid Grid.Row="7"
ItemsSource="{Binding ImportResults}"
ItemsSource="{Binding ImportResults, Mode=OneWay}"
HorizontalScrollBarVisibility="Visible"
IsVisible="{Binding IsImporting}">
IsVisible="{Binding IsImporting, Mode=OneWay}">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Filename}"
<DataGridTextColumn Binding="{Binding Filename, Mode=OneWay}"
Width="Auto"
IsReadOnly="True">
<DataGridTextColumn.Header>
<TextBlock Text="{Binding ResultFilenameLabel}" />
<TextBlock Text="{x:Static resources:Localization.ResultFilenameLabel}" />
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn Binding="{Binding Status}"
<DataGridTextColumn Binding="{Binding Status, Mode=OneWay}"
Width="Auto"
IsReadOnly="True">
<DataGridTextColumn.Header>
<TextBlock Text="{Binding ResultStatusLabel}" />
<TextBlock Text="{x:Static resources:Localization.ResultStatusLabel}" />
</DataGridTextColumn.Header>
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
<StackPanel Grid.Row="8"
Orientation="Horizontal"
IsVisible="{Binding IsReady}"
IsVisible="{Binding IsReady, Mode=OneWay}"
HorizontalAlignment="Right">
<Button HorizontalAlignment="Right"
VerticalAlignment="Center"
IsEnabled="{Binding CanClose}"
Command="{Binding CloseCommand}">
<TextBlock Text="{Binding CloseLabel}" />
IsEnabled="{Binding CanClose, Mode=OneWay}"
Command="{Binding CloseCommand, Mode=OneWay}">
<TextBlock Text="{x:Static resources:Localization.CloseLabel}" />
</Button>
<Button HorizontalAlignment="Right"
VerticalAlignment="Center"
IsEnabled="{Binding CanStart}"
Command="{Binding StartCommand}">
<TextBlock Text="{Binding StartLabel}" />
IsEnabled="{Binding CanStart, Mode=OneWay}"
Command="{Binding StartCommand, Mode=OneWay}">
<TextBlock Text="{x:Static resources:Localization.StartLabel}" />
</Button>
</StackPanel>
</Grid>

View File

@@ -29,6 +29,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:RomRepoMgr.ViewModels;assembly=RomRepoMgr"
xmlns:resources="clr-namespace:RomRepoMgr.Resources"
mc:Ignorable="d"
d:DesignWidth="800"
d:DesignHeight="450"
@@ -37,83 +38,78 @@
x:Class="RomRepoMgr.Views.ImportRomFolder"
Icon="/Assets/avalonia-logo.ico"
CanResize="False"
Title="{Binding Title}"
Title="{x:Static resources:Localization.ImportRomFolderTitle}"
WindowStartupLocation="CenterOwner">
<Design.DataContext>
<vm:ImportRomFolderViewModel />
</Design.DataContext>
<Border Padding="15">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="*" /> <RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,*,Auto">
<StackPanel Grid.Row="0"
Orientation="Horizontal"
HorizontalAlignment="Stretch">
<TextBlock Text="{Binding PathLabel}"
<TextBlock Text="{x:Static resources:Localization.PathLabel}"
FontWeight="Bold" />
<TextBlock Text="{Binding FolderPath}" />
<TextBlock Text="{Binding FolderPath, Mode=OneWay}" />
</StackPanel>
<CheckBox Grid.Row="1"
IsChecked="{Binding RemoveFilesChecked}"
IsEnabled="{Binding RemoveFilesEnabled}"
IsVisible="{Binding IsReady}">
IsChecked="{Binding RemoveFilesChecked, Mode=TwoWay}"
IsEnabled="{Binding RemoveFilesEnabled, Mode=OneWay}"
IsVisible="{Binding IsReady, Mode=OneWay}">
<CheckBox.Content>
<TextBlock Text="{Binding RemoveFilesLabel}" />
<TextBlock Text="{x:Static resources:Localization.RemoveFilesLabel}" />
</CheckBox.Content>
</CheckBox>
<CheckBox Grid.Row="2"
IsChecked="{Binding KnownOnlyChecked}"
IsVisible="{Binding IsReady}">
IsChecked="{Binding KnownOnlyChecked, Mode=TwoWay}"
IsVisible="{Binding IsReady, Mode=OneWay}">
<CheckBox.Content>
<TextBlock Text="{Binding KnownOnlyLabel}" />
<TextBlock Text="{x:Static resources:Localization.KnownOnlyLabel}" />
</CheckBox.Content>
</CheckBox>
<CheckBox Grid.Row="3"
IsChecked="{Binding RecurseArchivesChecked}"
IsEnabled="{Binding RecurseArchivesEnabled}"
IsVisible="{Binding IsReady}">
IsChecked="{Binding RecurseArchivesChecked, Mode=TwoWay}"
IsEnabled="{Binding RecurseArchivesEnabled, Mode=OneWay}"
IsVisible="{Binding IsReady, Mode=OneWay}">
<CheckBox.Content>
<TextBlock Text="{Binding RecurseArchivesLabel}" />
<TextBlock Text="{x:Static resources:Localization.RecurseArchivesLabel}" />
</CheckBox.Content>
</CheckBox>
<TextBlock Grid.Row="4"
Text="{Binding StatusMessage}"
Text="{Binding StatusMessage, Mode=OneWay}"
FontWeight="Bold"
HorizontalAlignment="Center" />
<ProgressBar Grid.Row="5"
Minimum="{Binding ProgressMinimum}"
Maximum="{Binding ProgressMaximum}"
Value="{Binding ProgressValue}"
IsIndeterminate="{Binding ProgressIsIndeterminate}"
IsVisible="{Binding ProgressVisible}" />
Minimum="{Binding ProgressMinimum, Mode=OneWay}"
Maximum="{Binding ProgressMaximum, Mode=OneWay}"
Value="{Binding ProgressValue, Mode=OneWay}"
IsIndeterminate="{Binding ProgressIsIndeterminate, Mode=OneWay}"
IsVisible="{Binding ProgressVisible, Mode=OneWay}" />
<StackPanel Grid.Row="6"
IsVisible="{Binding Progress2Visible}">
<TextBlock Text="{Binding Status2Message}" />
<ProgressBar Minimum="{Binding Progress2Minimum}"
Maximum="{Binding Progress2Maximum}"
Value="{Binding Progress2Value}"
IsIndeterminate="{Binding Progress2IsIndeterminate}" />
IsVisible="{Binding Progress2Visible, Mode=OneWay}">
<TextBlock Text="{Binding Status2Message, Mode=OneWay}" />
<ProgressBar Minimum="{Binding Progress2Minimum, Mode=OneWay}"
Maximum="{Binding Progress2Maximum, Mode=OneWay}"
Value="{Binding Progress2Value, Mode=OneWay}"
IsIndeterminate="{Binding Progress2IsIndeterminate, Mode=OneWay}" />
</StackPanel>
<DataGrid Grid.Row="7"
ItemsSource="{Binding ImportResults}"
ItemsSource="{Binding ImportResults, Mode=OneWay}"
HorizontalScrollBarVisibility="Visible"
IsVisible="{Binding IsImporting}">
IsVisible="{Binding IsImporting, Mode=OneWay}">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Filename}"
<DataGridTextColumn Binding="{Binding Filename, Mode=OneWay}"
Width="Auto"
IsReadOnly="True">
<DataGridTextColumn.Header>
<TextBlock Text="{Binding ResultFilenameLabel}" />
<TextBlock Text="{x:Static resources:Localization.ResultFilenameLabel}" />
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn Binding="{Binding Status}"
<DataGridTextColumn Binding="{Binding Status, Mode=OneWay}"
Width="Auto"
IsReadOnly="True">
<DataGridTextColumn.Header>
<TextBlock Text="{Binding ResultStatusLabel}" />
<TextBlock Text="{x:Static resources:Localization.ResultStatusLabel}" />
</DataGridTextColumn.Header>
</DataGridTextColumn>
</DataGrid.Columns>
@@ -123,15 +119,15 @@
HorizontalAlignment="Right">
<Button HorizontalAlignment="Right"
VerticalAlignment="Center"
IsEnabled="{Binding CanClose}"
Command="{Binding CloseCommand}">
<TextBlock Text="{Binding CloseLabel}" />
IsEnabled="{Binding CanClose, Mode=OneWay}"
Command="{Binding CloseCommand, Mode=OneWay}">
<TextBlock Text="{x:Static resources:Localization.CloseLabel}" />
</Button>
<Button HorizontalAlignment="Right"
VerticalAlignment="Center"
IsEnabled="{Binding CanStart}"
Command="{Binding StartCommand}">
<TextBlock Text="{Binding StartLabel}" />
IsEnabled="{Binding CanStart, Mode=OneWay}"
Command="{Binding StartCommand, Mode=OneWay}">
<TextBlock Text="{x:Static resources:Localization.StartLabel}" />
</Button>
</StackPanel>
</Grid>

View File

@@ -3,6 +3,7 @@
xmlns:vm="clr-namespace:RomRepoMgr.ViewModels;assembly=RomRepoMgr"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:resources="clr-namespace:RomRepoMgr.Resources"
mc:Ignorable="d"
d:DesignWidth="800"
d:DesignHeight="450"
@@ -14,172 +15,171 @@
</Design.DataContext>
<DockPanel>
<Menu DockPanel.Dock="Top">
<MenuItem Header="{Binding FileMenuText}">
<MenuItem Header="{Binding FileMenuImportDatFileText}"
Command="{Binding ImportDatCommand}" />
<MenuItem Header="{x:Static resources:Localization.FileMenuText}">
<MenuItem Header="{x:Static resources:Localization.FileMenuImportDatFileText}"
Command="{Binding ImportDatCommand, Mode=OneWay}" />
<Separator />
<MenuItem Header="{Binding FileMenuImportDatFolderText}"
Command="{Binding ImportDatFolderCommand}" />
<MenuItem Header="{x:Static resources:Localization.FileMenuImportDatFolderText}"
Command="{Binding ImportDatFolderCommand, Mode=OneWay}" />
<Separator />
<MenuItem Header="{Binding FileMenuSettingsText}"
IsVisible="{Binding !NativeMenuSupported}"
Command="{Binding SettingsCommand}" />
<MenuItem Header="{x:Static resources:Localization.FileMenuSettingsText}"
IsVisible="{Binding !NativeMenuSupported, Mode=OneWay}"
Command="{Binding SettingsCommand, Mode=OneWay}" />
<Separator />
<MenuItem Header="{Binding FileMenuExitText}"
IsVisible="{Binding !NativeMenuSupported}"
Command="{Binding ExitCommand}" />
<MenuItem Header="{x:Static resources:Localization.FileMenuExitText}"
IsVisible="{Binding !NativeMenuSupported, Mode=OneWay}"
Command="{Binding ExitCommand, Mode=OneWay}" />
</MenuItem>
<MenuItem Header="{Binding FilesystemMenuText}"
IsEnabled="{Binding IsVfsAvailable}">
<MenuItem Header="{Binding FilesystemMenuMountText}"
Command="{Binding MountCommand}"
IsEnabled="{Binding Vfs, Converter={x:Static ObjectConverters.IsNull}}" />
<MenuItem Header="{x:Static resources:Localization.FilesystemMenuText}">
<MenuItem Header="{x:Static resources:Localization.FilesystemMenuMountText}"
Command="{Binding MountCommand, Mode=OneWay}"
IsEnabled="{Binding Vfs, Converter={x:Static ObjectConverters.IsNull}, Mode=OneWay}" />
<Separator />
<MenuItem Header="{Binding FilesystemMenuUmountText}"
Command="{Binding UmountCommand}"
IsEnabled="{Binding Vfs, Converter={x:Static ObjectConverters.IsNotNull}}" />
<MenuItem Header="{x:Static resources:Localization.FilesystemMenuUmountText}"
Command="{Binding UmountCommand, Mode=OneWay}"
IsEnabled="{Binding Vfs, Converter={x:Static ObjectConverters.IsNotNull}, Mode=OneWay}" />
<Separator />
</MenuItem>
<MenuItem Header="{Binding RomsMenuText}">
<MenuItem Header="{Binding RomsMenuImportText}"
Command="{Binding ImportRomFolderCommand}" />
<MenuItem Header="{x:Static resources:Localization.RomsMenuText}">
<MenuItem Header="{x:Static resources:Localization.RomsMenuImportText}"
Command="{Binding ImportRomFolderCommand, Mode=OneWay}" />
<Separator />
</MenuItem>
<MenuItem Header="{Binding RomSetsMenuText}"
IsEnabled="{Binding SelectedRomSet, Converter={x:Static ObjectConverters.IsNotNull}}">
<MenuItem Header="{Binding RomSetsMenuSaveRomsText}"
Command="{Binding ExportRomsCommand}" />
<MenuItem Header="{x:Static resources:Localization.RomSetsMenuText}"
IsEnabled="{Binding SelectedRomSet, Converter={x:Static ObjectConverters.IsNotNull}, Mode=OneWay}">
<MenuItem Header="{x:Static resources:Localization.RomSetsMenuSaveRomsText}"
Command="{Binding ExportRomsCommand, Mode=OneWay}" />
<Separator />
<MenuItem Header="{Binding RomSetsMenuSaveDatText}"
Command="{Binding ExportDatCommand}" />
<MenuItem Header="{x:Static resources:Localization.RomSetsMenuSaveDatText}"
Command="{Binding ExportDatCommand, Mode=OneWay}" />
<Separator />
<MenuItem Header="{Binding RomSetsMenuEditText}"
Command="{Binding EditRomSetCommand}" />
<MenuItem Header="{x:Static resources:Localization.RomSetsMenuEditText}"
Command="{Binding EditRomSetCommand, Mode=OneWay}" />
<Separator />
<MenuItem Header="{Binding RomSetsMenuDeleteText}"
Command="{Binding DeleteRomSetCommand}" />
<MenuItem Header="{x:Static resources:Localization.RomSetsMenuDeleteText}"
Command="{Binding DeleteRomSetCommand, Mode=OneWay}" />
<Separator />
</MenuItem>
<MenuItem Header="{Binding DatabaseMenuText}">
<MenuItem Header="{Binding DatabaseMenuUpdateStatsText}"
Command="{Binding UpdateStatsCommand}" />
<MenuItem Header="{x:Static resources:Localization.DatabaseMenuText}">
<MenuItem Header="{x:Static resources:Localization.DatabaseMenuUpdateStatsText}"
Command="{Binding UpdateStatsCommand, Mode=OneWay}" />
<Separator />
</MenuItem>
<MenuItem Header="{Binding HelpMenuText}">
<MenuItem Header="{Binding HelpMenuAboutText}"
<MenuItem Header="{x:Static resources:Localization.HelpMenuText}">
<MenuItem Header="{x:Static resources:Localization.HelpMenuAboutText}"
Name="AboutMenuItem"
IsVisible="{Binding !NativeMenuSupported}"
Command="{Binding AboutCommand}" />
IsVisible="{Binding !NativeMenuSupported, Mode=OneWay}"
Command="{Binding AboutCommand, Mode=OneWay}" />
</MenuItem>
</Menu>
<TabControl HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<TabItem>
<TabItem.Header>
<TextBlock Text="{Binding RomSetLabel}" />
<TextBlock Text="{x:Static resources:Localization.RomSets}" />
</TabItem.Header>
<DataGrid ItemsSource="{Binding RomSets}"
<DataGrid ItemsSource="{Binding RomSets, Mode=OneWay}"
HorizontalScrollBarVisibility="Visible"
SelectedItem="{Binding SelectedRomSet, Mode=TwoWay}"
CanUserSortColumns="True"
CanUserResizeColumns="True">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Name}"
<DataGridTextColumn Binding="{Binding Name, Mode=OneWay}"
Width="Auto"
IsReadOnly="True">
<DataGridTextColumn.Header>
<TextBlock Text="{Binding RomSetNameLabel}" />
<TextBlock Text="{x:Static resources:Localization.RomSetNameLabel}" />
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn Binding="{Binding Version}"
<DataGridTextColumn Binding="{Binding Version, Mode=OneWay}"
Width="Auto"
IsReadOnly="True">
<DataGridTextColumn.Header>
<TextBlock Text="{Binding RomSetVersionLabel}" />
<TextBlock Text="{x:Static resources:Localization.RomSetVersionLabel}" />
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn Binding="{Binding Author}"
<DataGridTextColumn Binding="{Binding Author, Mode=OneWay}"
Width="Auto"
IsReadOnly="True">
<DataGridTextColumn.Header>
<TextBlock Text="{Binding RomSetAuthorLabel}" />
<TextBlock Text="{x:Static resources:Localization.RomSetAuthorLabel}" />
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn Binding="{Binding Category}"
<DataGridTextColumn Binding="{Binding Category, Mode=OneWay}"
Width="Auto"
IsReadOnly="True">
<DataGridTextColumn.Header>
<TextBlock Text="{Binding RomSetCategoryLabel}" />
<TextBlock Text="{x:Static resources:Localization.RomSetCategoryLabel}" />
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn Binding="{Binding Date}"
<DataGridTextColumn Binding="{Binding Date, Mode=OneWay}"
Width="Auto"
IsReadOnly="True">
<DataGridTextColumn.Header>
<TextBlock Text="{Binding RomSetDateLabel}" />
<TextBlock Text="{x:Static resources:Localization.RomSetDateLabel}" />
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn Binding="{Binding Description}"
<DataGridTextColumn Binding="{Binding Description, Mode=OneWay}"
Width="Auto"
IsReadOnly="True">
<DataGridTextColumn.Header>
<TextBlock Text="{Binding RomSetDescriptionLabel}" />
<TextBlock Text="{x:Static resources:Localization.RomSetDescriptionLabel}" />
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn Binding="{Binding Comment}"
<DataGridTextColumn Binding="{Binding Comment, Mode=OneWay}"
Width="Auto"
IsReadOnly="True">
<DataGridTextColumn.Header>
<TextBlock Text="{Binding RomSetCommentLabel}" />
<TextBlock Text="{x:Static resources:Localization.RomSetCommentLabel}" />
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn Binding="{Binding Homepage}"
<DataGridTextColumn Binding="{Binding Homepage, Mode=OneWay}"
Width="Auto"
IsReadOnly="True">
<DataGridTextColumn.Header>
<TextBlock Text="{Binding RomSetHomepageLabel}" />
<TextBlock Text="{x:Static resources:Localization.HomepageLabel}" />
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn Binding="{Binding TotalMachines}"
<DataGridTextColumn Binding="{Binding TotalMachines, Mode=OneWay}"
Width="Auto"
IsReadOnly="True">
<DataGridTextColumn.Header>
<TextBlock Text="{Binding RomSetTotalMachinesLabel}" />
<TextBlock Text="{x:Static resources:Localization.RomSetTotalMachinesLabel}" />
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn Binding="{Binding CompleteMachines}"
<DataGridTextColumn Binding="{Binding CompleteMachines, Mode=OneWay}"
Width="Auto"
IsReadOnly="True">
<DataGridTextColumn.Header>
<TextBlock Text="{Binding RomSetCompleteMachinesLabel}" />
<TextBlock Text="{x:Static resources:Localization.RomSetCompleteMachinesLabel}" />
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn Binding="{Binding IncompleteMachines}"
<DataGridTextColumn Binding="{Binding IncompleteMachines, Mode=OneWay}"
Width="Auto"
IsReadOnly="True">
<DataGridTextColumn.Header>
<TextBlock Text="{Binding RomSetIncompleteMachinesLabel}" />
<TextBlock Text="{x:Static resources:Localization.RomSetIncompleteMachinesLabel}" />
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn Binding="{Binding TotalRoms}"
<DataGridTextColumn Binding="{Binding TotalRoms, Mode=OneWay}"
Width="Auto"
IsReadOnly="True">
<DataGridTextColumn.Header>
<TextBlock Text="{Binding RomSetTotalRomsLabel}" />
<TextBlock Text="{x:Static resources:Localization.RomSetTotalRomsLabel}" />
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn Binding="{Binding HaveRoms}"
<DataGridTextColumn Binding="{Binding HaveRoms, Mode=OneWay}"
Width="Auto"
IsReadOnly="True">
<DataGridTextColumn.Header>
<TextBlock Text="{Binding RomSetHaveRomsLabel}" />
<TextBlock Text="{x:Static resources:Localization.RomSetHaveRomsLabel}" />
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn Binding="{Binding MissRoms}"
<DataGridTextColumn Binding="{Binding MissRoms, Mode=OneWay}"
Width="Auto"
IsReadOnly="True">
<DataGridTextColumn.Header>
<TextBlock Text="{Binding RomSetMissRomsLabel}" />
<TextBlock Text="{x:Static resources:Localization.RomSetMissRomsLabel}" />
</DataGridTextColumn.Header>
</DataGridTextColumn>
</DataGrid.Columns>

View File

@@ -29,6 +29,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:RomRepoMgr.ViewModels;assembly=RomRepoMgr"
xmlns:resources="clr-namespace:RomRepoMgr.Resources"
mc:Ignorable="d"
d:DesignWidth="800"
d:DesignHeight="450"
@@ -37,18 +38,15 @@
x:Class="RomRepoMgr.Views.RemoveDat"
Icon="/Assets/avalonia-logo.ico"
CanResize="False"
Title="{Binding Title}"
Title="{x:Static resources:Localization.RemoveDatTitle}"
WindowStartupLocation="CenterOwner">
<Design.DataContext>
<vm:RemoveDatViewModel />
</Design.DataContext>
<Border Padding="15">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="auto" />
</Grid.RowDefinitions>
<Grid RowDefinitions="Auto,auto">
<TextBlock Grid.Row="0"
Text="{Binding StatusMessage}"
Text="{Binding StatusMessage, Mode=OneWay}"
HorizontalAlignment="Center" />
<ProgressBar Grid.Row="1"
IsIndeterminate="True"

View File

@@ -29,6 +29,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:RomRepoMgr.ViewModels;assembly=RomRepoMgr"
xmlns:resources="clr-namespace:RomRepoMgr.Resources"
mc:Ignorable="d"
d:DesignWidth="800"
d:DesignHeight="450"
@@ -37,129 +38,117 @@
x:Class="RomRepoMgr.Views.SettingsDialog"
Icon="/Assets/avalonia-logo.ico"
CanResize="False"
Title="{Binding Title}">
Title="{x:Static resources:Localization.SettingsTitle}">
<Design.DataContext>
<vm:SettingsViewModel />
</Design.DataContext>
<Border Padding="15">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" /> <ColumnDefinition Width="250" /> <ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto">
<Grid Grid.Row="0"
ColumnDefinitions="*,250,Auto">
<TextBlock Grid.Column="0"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Text="{Binding DatabaseLabel}"
Text="{x:Static resources:Localization.DatabaseFileLabel}"
FontWeight="Bold"
Padding="5" />
<TextBox Grid.Column="1"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
Text="{Binding DatabasePath}"
Text="{Binding DatabasePath, Mode=TwoWay}"
IsReadOnly="True"
Padding="5" />
<Button Grid.Column="2"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Command="{Binding DatabaseCommand}"
Command="{Binding DatabaseCommand, Mode=OneWay}"
Padding="5">
<TextBlock Text="{Binding ChooseLabel}" />
<TextBlock Text="{x:Static resources:Localization.ChooseLabel}" />
</Button>
</Grid>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" /> <ColumnDefinition Width="250" /> <ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid Grid.Row="1"
ColumnDefinitions="*,250,Auto">
<TextBlock Grid.Column="0"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Text="{Binding RepositoryLabel}"
Text="{x:Static resources:Localization.RepositoryFolderLabel}"
FontWeight="Bold"
Padding="5" />
<TextBox Grid.Column="1"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
Text="{Binding RepositoryPath}"
Text="{Binding RepositoryPath, Mode=TwoWay}"
IsReadOnly="True"
Padding="5" />
<Button Grid.Column="2"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Command="{Binding RepositoryCommand}"
Command="{Binding RepositoryCommand, Mode=OneWay}"
Padding="5">
<TextBlock Text="{Binding ChooseLabel}" />
<TextBlock Text="{x:Static resources:Localization.ChooseLabel}" />
</Button>
</Grid>
<Grid Grid.Row="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" /> <ColumnDefinition Width="250" /> <ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid Grid.Row="2"
ColumnDefinitions="*,250,Auto">
<TextBlock Grid.Column="0"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Text="{Binding TemporaryLabel}"
Text="{x:Static resources:Localization.TemporaryFolderLabel}"
FontWeight="Bold"
Padding="5" />
<TextBox Grid.Column="1"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
Text="{Binding TemporaryPath}"
Text="{Binding TemporaryPath, Mode=TwoWay}"
IsReadOnly="True"
Padding="5" />
<Button Grid.Column="2"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Command="{Binding TemporaryCommand}"
Command="{Binding TemporaryCommand, Mode=OneWay}"
Padding="5">
<TextBlock Text="{Binding ChooseLabel}" />
<TextBlock Text="{x:Static resources:Localization.ChooseLabel}" />
</Button>
</Grid>
<Grid Grid.Row="3">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" /> <ColumnDefinition Width="250" /> <ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid Grid.Row="3"
ColumnDefinitions="*,250,Auto">
<TextBlock Grid.Column="0"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Text="{Binding UnArPathLabel}"
Text="{x:Static resources:Localization.UnArPathLabel}"
FontWeight="Bold"
Padding="5" />
<TextBox Grid.Column="1"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
Text="{Binding UnArPath}"
Text="{Binding UnArPath, Mode=TwoWay}"
IsReadOnly="True"
Padding="5" />
<Button Grid.Column="2"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Command="{Binding UnArCommand}"
Command="{Binding UnArCommand, Mode=OneWay}"
Padding="5">
<TextBlock Text="{Binding ChooseLabel}" />
<TextBlock Text="{x:Static resources:Localization.ChooseLabel}" />
</Button>
</Grid>
<TextBlock Grid.Row="4"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Text="{Binding UnArVersion}"
Text="{Binding UnArVersion, Mode=OneWay}"
FontWeight="Bold" />
<StackPanel Grid.Row="5"
Orientation="Horizontal"
HorizontalAlignment="Right">
<Button HorizontalAlignment="Right"
VerticalAlignment="Center"
Command="{Binding SaveCommand}">
<TextBlock Text="{Binding SaveLabel}" />
Command="{Binding SaveCommand, Mode=OneWay}">
<TextBlock Text="{x:Static resources:Localization.SaveLabel}" />
</Button>
<Button HorizontalAlignment="Right"
VerticalAlignment="Center"
Command="{Binding CloseCommand}">
<TextBlock Text="{Binding CloseLabel}" />
Command="{Binding CloseCommand, Mode=OneWay}">
<TextBlock Text="{x:Static resources:Localization.CloseLabel}" />
</Button>
</StackPanel>
</Grid>

View File

@@ -4,6 +4,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:svg="clr-namespace:Avalonia.Svg.Skia;assembly=Avalonia.Svg.Skia"
xmlns:resources="clr-namespace:RomRepoMgr.Resources"
mc:Ignorable="d"
d:DesignWidth="450"
d:DesignHeight="250"
@@ -22,7 +23,7 @@
VerticalAlignment="Top"
Orientation="Vertical"
Margin="5">
<TextBlock Text="{Binding LoadingText}"
<TextBlock Text="{Binding LoadingText, Mode=OneWay}"
FontWeight="Bold"
HorizontalAlignment="Center"
VerticalAlignment="Top" />
@@ -30,136 +31,136 @@
Orientation="Horizontal">
<Image MaxWidth="24"
MaxHeight="24"
IsVisible="{Binding LoadingSettingsOk}">
IsVisible="{Binding LoadingSettingsOk, Mode=OneWay}">
<Image.Source>
<svg:SvgImage Source="/Assets/emblem-checked.svg" />
</Image.Source>
</Image>
<Image MaxWidth="24"
MaxHeight="24"
IsVisible="{Binding LoadingSettingsError}">
IsVisible="{Binding LoadingSettingsError, Mode=OneWay}">
<Image.Source>
<svg:SvgImage Source="/Assets/emblem-error.svg" />
</Image.Source>
</Image>
<Image MaxWidth="24"
MaxHeight="24"
IsVisible="{Binding LoadingSettingsUnknown}">
IsVisible="{Binding LoadingSettingsUnknown, Mode=OneWay}">
<Image.Source>
<svg:SvgImage Source="/Assets/emblem-question.svg" />
</Image.Source>
</Image>
<TextBlock Text="{Binding LoadingSettingsText}"
<TextBlock Text="{x:Static resources:Localization.LoadingSettingsText}"
VerticalAlignment="Center" />
</StackPanel>
<StackPanel HorizontalAlignment="Left"
Orientation="Horizontal">
<Image MaxWidth="24"
MaxHeight="24"
IsVisible="{Binding CheckingUnArOk}">
IsVisible="{Binding CheckingUnArOk, Mode=OneWay}">
<Image.Source>
<svg:SvgImage Source="/Assets/emblem-checked.svg" />
</Image.Source>
</Image>
<Image MaxWidth="24"
MaxHeight="24"
IsVisible="{Binding CheckingUnArError}">
IsVisible="{Binding CheckingUnArError, Mode=OneWay}">
<Image.Source>
<svg:SvgImage Source="/Assets/emblem-error.svg" />
</Image.Source>
</Image>
<Image MaxWidth="24"
MaxHeight="24"
IsVisible="{Binding CheckingUnArUnknown}">
IsVisible="{Binding CheckingUnArUnknown, Mode=OneWay}">
<Image.Source>
<svg:SvgImage Source="/Assets/emblem-question.svg" />
</Image.Source>
</Image>
<TextBlock Text="{Binding CheckingUnArText}"
<TextBlock Text="{x:Static resources:Localization.CheckingUnArText}"
VerticalAlignment="Center" />
</StackPanel>
<StackPanel HorizontalAlignment="Left"
Orientation="Horizontal">
<Image MaxWidth="24"
MaxHeight="24"
IsVisible="{Binding LoadingDatabaseOk}">
IsVisible="{Binding LoadingDatabaseOk, Mode=OneWay}">
<Image.Source>
<svg:SvgImage Source="/Assets/emblem-checked.svg" />
</Image.Source>
</Image>
<Image MaxWidth="24"
MaxHeight="24"
IsVisible="{Binding LoadingDatabaseError}">
IsVisible="{Binding LoadingDatabaseError, Mode=OneWay}">
<Image.Source>
<svg:SvgImage Source="/Assets/emblem-error.svg" />
</Image.Source>
</Image>
<Image MaxWidth="24"
MaxHeight="24"
IsVisible="{Binding LoadingDatabaseUnknown}">
IsVisible="{Binding LoadingDatabaseUnknown, Mode=OneWay}">
<Image.Source>
<svg:SvgImage Source="/Assets/emblem-question.svg" />
</Image.Source>
</Image>
<TextBlock Text="{Binding LoadingDatabaseText}"
<TextBlock Text="{x:Static resources:Localization.LoadingDatabaseText}"
VerticalAlignment="Center" />
</StackPanel>
<StackPanel HorizontalAlignment="Left"
Orientation="Horizontal">
<Image MaxWidth="24"
MaxHeight="24"
IsVisible="{Binding MigratingDatabaseOk}">
IsVisible="{Binding MigratingDatabaseOk, Mode=OneWay}">
<Image.Source>
<svg:SvgImage Source="/Assets/emblem-checked.svg" />
</Image.Source>
</Image>
<Image MaxWidth="24"
MaxHeight="24"
IsVisible="{Binding MigratingDatabaseError}">
IsVisible="{Binding MigratingDatabaseError, Mode=OneWay}">
<Image.Source>
<svg:SvgImage Source="/Assets/emblem-error.svg" />
</Image.Source>
</Image>
<Image MaxWidth="24"
MaxHeight="24"
IsVisible="{Binding MigratingDatabaseUnknown}">
IsVisible="{Binding MigratingDatabaseUnknown, Mode=OneWay}">
<Image.Source>
<svg:SvgImage Source="/Assets/emblem-question.svg" />
</Image.Source>
</Image>
<TextBlock Text="{Binding MigratingDatabaseText}"
<TextBlock Text="{x:Static resources:Localization.MigratingDatabaseText}"
VerticalAlignment="Center" />
</StackPanel>
<StackPanel HorizontalAlignment="Left"
Orientation="Horizontal">
<Image MaxWidth="24"
MaxHeight="24"
IsVisible="{Binding LoadingRomSetsOk}">
IsVisible="{Binding LoadingRomSetsOk, Mode=OneWay}">
<Image.Source>
<svg:SvgImage Source="/Assets/emblem-checked.svg" />
</Image.Source>
</Image>
<Image MaxWidth="24"
MaxHeight="24"
IsVisible="{Binding LoadingRomSetsError}">
IsVisible="{Binding LoadingRomSetsError, Mode=OneWay}">
<Image.Source>
<svg:SvgImage Source="/Assets/emblem-error.svg" />
</Image.Source>
</Image>
<Image MaxWidth="24"
MaxHeight="24"
IsVisible="{Binding LoadingRomSetsUnknown}">
IsVisible="{Binding LoadingRomSetsUnknown, Mode=OneWay}">
<Image.Source>
<svg:SvgImage Source="/Assets/emblem-question.svg" />
</Image.Source>
</Image>
<TextBlock Text="{Binding LoadingRomSetsText}"
<TextBlock Text="{x:Static resources:Localization.LoadingRomSetsText}"
VerticalAlignment="Center" />
</StackPanel>
<Button Command="{Binding ExitCommand}"
IsVisible="{Binding ExitVisible}"
HorizontalAlignment="Right">
<TextBlock Text="{Binding ExitButtonText}" />
<TextBlock Text="{x:Static resources:Localization.ExitButtonText}" />
</Button>
</StackPanel>
</Window>

View File

@@ -29,136 +29,133 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:RomRepoMgr.ViewModels;assembly=RomRepoMgr"
xmlns:resources="clr-namespace:RomRepoMgr.Resources"
mc:Ignorable="d"
d:DesignWidth="800"
d:DesignHeight="450"
x:Class="RomRepoMgr.Views.UpdateStats"
Icon="/Assets/avalonia-logo.ico"
CanResize="False"
Title="{Binding Title}"
Title="{x:Static resources:Localization.UpdateStatsTitle}"
WindowStartupLocation="CenterOwner">
<Design.DataContext>
<vm:UpdateStatsViewModel />
</Design.DataContext>
<Border Padding="15">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> <RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid RowDefinitions="Auto,Auto,*,Auto">
<TextBlock Grid.Row="0"
Text="{Binding StatusMessage}"
Text="{Binding StatusMessage, Mode=OneWay}"
HorizontalAlignment="Center" />
<ProgressBar Grid.Row="1"
IsIndeterminate="{Binding IndeterminateProgress}"
Maximum="{Binding MaximumValue}"
Minimum="{Binding MinimumValue}"
Value="{Binding CurrentValue}"
IsIndeterminate="{Binding IndeterminateProgress, Mode=OneWay}"
Maximum="{Binding MaximumValue, Mode=OneWay}"
Minimum="{Binding MinimumValue, Mode=OneWay}"
Value="{Binding CurrentValue, Mode=OneWay}"
HorizontalAlignment="Stretch"
IsVisible="{Binding ProgressVisible}" />
IsVisible="{Binding ProgressVisible, Mode=OneWay}" />
<DataGrid Grid.Row="2"
ItemsSource="{Binding RomSets}"
ItemsSource="{Binding RomSets, Mode=OneWay}"
HorizontalScrollBarVisibility="Visible"
SelectedItem="{Binding SelectedRomSet, Mode=TwoWay}"
CanUserSortColumns="True"
CanUserResizeColumns="True">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Name}"
<DataGridTextColumn Binding="{Binding Name, Mode=OneWay}"
Width="Auto"
IsReadOnly="True">
<DataGridTextColumn.Header>
<TextBlock Text="{Binding RomSetNameLabel}" />
<TextBlock Text="{x:Static resources:Localization.RomSetNameLabel}" />
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn Binding="{Binding Version}"
<DataGridTextColumn Binding="{Binding Version, Mode=OneWay}"
Width="Auto"
IsReadOnly="True">
<DataGridTextColumn.Header>
<TextBlock Text="{Binding RomSetVersionLabel}" />
<TextBlock Text="{x:Static resources:Localization.RomSetVersionLabel}" />
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn Binding="{Binding Author}"
<DataGridTextColumn Binding="{Binding Author, Mode=OneWay}"
Width="Auto"
IsReadOnly="True">
<DataGridTextColumn.Header>
<TextBlock Text="{Binding RomSetAuthorLabel}" />
<TextBlock Text="{x:Static resources:Localization.RomSetAuthorLabel}" />
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn Binding="{Binding Category}"
<DataGridTextColumn Binding="{Binding Category, Mode=OneWay}"
Width="Auto"
IsReadOnly="True">
<DataGridTextColumn.Header>
<TextBlock Text="{Binding RomSetCategoryLabel}" />
<TextBlock Text="{x:Static resources:Localization.RomSetCategoryLabel}" />
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn Binding="{Binding Date}"
<DataGridTextColumn Binding="{Binding Date, Mode=OneWay}"
Width="Auto"
IsReadOnly="True">
<DataGridTextColumn.Header>
<TextBlock Text="{Binding RomSetDateLabel}" />
<TextBlock Text="{x:Static resources:Localization.RomSetDateLabel}" />
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn Binding="{Binding Description}"
<DataGridTextColumn Binding="{Binding Description, Mode=OneWay}"
Width="Auto"
IsReadOnly="True">
<DataGridTextColumn.Header>
<TextBlock Text="{Binding RomSetDescriptionLabel}" />
<TextBlock Text="{x:Static resources:Localization.RomSetDescriptionLabel}" />
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn Binding="{Binding Comment}"
<DataGridTextColumn Binding="{Binding Comment, Mode=OneWay}"
Width="Auto"
IsReadOnly="True">
<DataGridTextColumn.Header>
<TextBlock Text="{Binding RomSetCommentLabel}" />
<TextBlock Text="{x:Static resources:Localization.RomSetCommentLabel}" />
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn Binding="{Binding Homepage}"
<DataGridTextColumn Binding="{Binding Homepage, Mode=OneWay}"
Width="Auto"
IsReadOnly="True">
<DataGridTextColumn.Header>
<TextBlock Text="{Binding RomSetHomepageLabel}" />
<TextBlock Text="{x:Static resources:Localization.HomepageLabel}" />
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn Binding="{Binding TotalMachines}"
<DataGridTextColumn Binding="{Binding TotalMachines, Mode=OneWay}"
Width="Auto"
IsReadOnly="True">
<DataGridTextColumn.Header>
<TextBlock Text="{Binding RomSetTotalMachinesLabel}" />
<TextBlock Text="{x:Static resources:Localization.RomSetTotalMachinesLabel}" />
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn Binding="{Binding CompleteMachines}"
<DataGridTextColumn Binding="{Binding CompleteMachines, Mode=OneWay}"
Width="Auto"
IsReadOnly="True">
<DataGridTextColumn.Header>
<TextBlock Text="{Binding RomSetCompleteMachinesLabel}" />
<TextBlock Text="{x:Static resources:Localization.RomSetCompleteMachinesLabel}" />
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn Binding="{Binding IncompleteMachines}"
<DataGridTextColumn Binding="{Binding IncompleteMachines, Mode=OneWay}"
Width="Auto"
IsReadOnly="True">
<DataGridTextColumn.Header>
<TextBlock Text="{Binding RomSetIncompleteMachinesLabel}" />
<TextBlock Text="{x:Static resources:Localization.RomSetIncompleteMachinesLabel}" />
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn Binding="{Binding TotalRoms}"
<DataGridTextColumn Binding="{Binding TotalRoms, Mode=OneWay}"
Width="Auto"
IsReadOnly="True">
<DataGridTextColumn.Header>
<TextBlock Text="{Binding RomSetTotalRomsLabel}" />
<TextBlock Text="{x:Static resources:Localization.RomSetTotalRomsLabel}" />
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn Binding="{Binding HaveRoms}"
<DataGridTextColumn Binding="{Binding HaveRoms, Mode=OneWay}"
Width="Auto"
IsReadOnly="True">
<DataGridTextColumn.Header>
<TextBlock Text="{Binding RomSetHaveRomsLabel}" />
<TextBlock Text="{x:Static resources:Localization.RomSetHaveRomsLabel}" />
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn Binding="{Binding MissRoms}"
<DataGridTextColumn Binding="{Binding MissRoms, Mode=OneWay}"
Width="Auto"
IsReadOnly="True">
<DataGridTextColumn.Header>
<TextBlock Text="{Binding RomSetMissRomsLabel}" />
<TextBlock Text="{x:Static resources:Localization.RomSetMissRomsLabel}" />
</DataGridTextColumn.Header>
</DataGridTextColumn>
</DataGrid.Columns>
@@ -166,9 +163,9 @@
<Button Grid.Row="3"
HorizontalAlignment="Right"
VerticalAlignment="Center"
IsEnabled="{Binding CanClose}"
Command="{Binding CloseCommand}">
<TextBlock Text="{Binding CloseLabel}" />
IsEnabled="{Binding CanClose, Mode=OneWay}"
Command="{Binding CloseCommand, Mode=OneWay}">
<TextBlock Text="{x:Static resources:Localization.CloseLabel}" />
</Button>
</Grid>
</Border>