SHA256
365 lines
15 KiB
C#
365 lines
15 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using System.Windows;
|
|
using System.Windows.Controls;
|
|
using System.Windows.Data;
|
|
using System.Windows.Documents;
|
|
using System.Windows.Input;
|
|
using System.Windows.Media;
|
|
using System.Windows.Media.Imaging;
|
|
using System.Windows.Navigation;
|
|
using System.Windows.Shapes;
|
|
using System.Collections.ObjectModel;
|
|
using System.IO;
|
|
using XML;
|
|
|
|
|
|
|
|
namespace TapeCatalog
|
|
{
|
|
/// <summary>
|
|
/// Interaction logic for MainWindow.xaml
|
|
/// </summary>
|
|
public partial class MainWindow : Window
|
|
{
|
|
private readonly ObservableCollection<FileEntry> _files = new ObservableCollection<FileEntry>();
|
|
|
|
private static string FormatBytesToMb(long bytes)
|
|
{
|
|
// convert to MB with two decimal places
|
|
double mb = bytes / 1024.0 / 1024.0;
|
|
return mb.ToString("F2");
|
|
}
|
|
|
|
public MainWindow()
|
|
{
|
|
InitializeComponent();
|
|
FilesListView.ItemsSource = _files;
|
|
}
|
|
#region (export Medium)
|
|
private void EXPORT_MEDIUM(object sender, RoutedEventArgs e)
|
|
{
|
|
var selected = MediumComboBox.SelectedItem as string;
|
|
if (string.IsNullOrWhiteSpace(selected))
|
|
{
|
|
MessageBox.Show(this, "Bitte ein Medium im Dropdown auswählen.", "Keine Auswahl", MessageBoxButton.OK, MessageBoxImage.Warning);
|
|
return;
|
|
}
|
|
|
|
// collect matching entries from TapeCatalogListView
|
|
var matches = new List<XML.Methods.TapeEntry>();
|
|
foreach (var item in TapeCatalogListView.Items)
|
|
{
|
|
var te = item as dynamic;
|
|
if (te == null) continue;
|
|
string m = te.Medium as string;
|
|
if (string.Equals(m, selected, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
matches.Add(new XML.Methods.TapeEntry { Medium = te.Medium, Filename = te.Filename, Modified = te.Modified });
|
|
}
|
|
}
|
|
|
|
if (matches.Count == 0)
|
|
{
|
|
MessageBox.Show(this, "Keine Einträge für das gewählte Medium gefunden.", "Keine Einträge", MessageBoxButton.OK, MessageBoxImage.Information);
|
|
return;
|
|
}
|
|
|
|
// delegate export to XML helper
|
|
XML.Methods.ExportTapeEntries(this, selected, matches);
|
|
}
|
|
#endregion
|
|
#region (Import List)
|
|
private void IMPORT_LIST(object sender, RoutedEventArgs e)
|
|
{
|
|
// delegate to XML import helper
|
|
XML.Methods.ImportList(this, _files, SourceTextBox, ArchiveTextBox);
|
|
}
|
|
#endregion (Import List)
|
|
#region (COPY_TO_TAPECATALOG)
|
|
private void COPY_TO_TAPECATALOG(object sender, RoutedEventArgs e)
|
|
{
|
|
// delegate to XML helper to copy entries to TapeCatalog list view
|
|
XML.Methods.CopyToTapeCatalog(this, _files, TapeCatalogListView);
|
|
}
|
|
#endregion (COPY_TO_TAPECATALOG)
|
|
#region (COPY_FROM_SRCARC)
|
|
private void COPY_FROM_SRCARC(object sender, RoutedEventArgs e)
|
|
{
|
|
XML.Methods.CopyToTapeCatalog(this, _files, TapeCatalogListView);
|
|
}
|
|
#endregion (COPY_FROM_SRCARC)
|
|
#region (REFRESH_MEDIUMS)
|
|
private void REFRESH_MEDIUMS(object sender, RoutedEventArgs e)
|
|
{
|
|
// collect distinct mediums from TapeCatalogListView items
|
|
try
|
|
{
|
|
var mediums = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
|
foreach (var item in TapeCatalogListView.Items)
|
|
{
|
|
var te = item as dynamic;
|
|
if (te == null) continue;
|
|
string m = te.Medium as string;
|
|
if (!string.IsNullOrWhiteSpace(m)) mediums.Add(m);
|
|
}
|
|
|
|
MediumComboBox.ItemsSource = mediums.OrderBy(s => s).ToList();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
MessageBox.Show(this, $"Fehler beim Aktualisieren der Medium-Liste: {ex.Message}", "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
|
}
|
|
}
|
|
#endregion (REFRESH_MEDIUMS)
|
|
#region (EXPORT_LIST)
|
|
private void EXPORT_LIST(object sender, RoutedEventArgs e)
|
|
{
|
|
// Delegate export to XML helper
|
|
XML.Methods.ExportList(this, SourceTextBox.Text, ArchiveTextBox.Text, _files);
|
|
}
|
|
|
|
private void SET_RES_DIR(object sender, RoutedEventArgs e)
|
|
{
|
|
// Use the WinForms FolderBrowserDialog to select a folder
|
|
using (var dlg = new System.Windows.Forms.FolderBrowserDialog())
|
|
{
|
|
dlg.Description = "Select a folder";
|
|
dlg.ShowNewFolderButton = true;
|
|
var result = dlg.ShowDialog();
|
|
if (result == System.Windows.Forms.DialogResult.OK && !string.IsNullOrWhiteSpace(dlg.SelectedPath))
|
|
{
|
|
SourceTextBox.Text = dlg.SelectedPath;
|
|
}
|
|
}
|
|
}
|
|
#endregion (EXPORT_LIST)
|
|
#region (SET_ARC_DIR)
|
|
private void SET_ARC_DIR(object sender, RoutedEventArgs e)
|
|
{
|
|
// Use the WinForms FolderBrowserDialog to select a folder
|
|
using (var dlg = new System.Windows.Forms.FolderBrowserDialog())
|
|
{
|
|
dlg.Description = "Select a folder";
|
|
dlg.ShowNewFolderButton = true;
|
|
var result = dlg.ShowDialog();
|
|
if (result == System.Windows.Forms.DialogResult.OK && !string.IsNullOrWhiteSpace(dlg.SelectedPath))
|
|
{
|
|
ArchiveTextBox.Text = dlg.SelectedPath;
|
|
}
|
|
}
|
|
}
|
|
#endregion (SET_ARC_DIR)
|
|
#region (SCAN_RES_DIR)
|
|
private void SCAN_RES_DIR(object sender, RoutedEventArgs e)
|
|
{
|
|
var path = SourceTextBox.Text?.Trim();
|
|
if (string.IsNullOrWhiteSpace(path))
|
|
{
|
|
MessageBox.Show("Please select a directory first.", "No directory", MessageBoxButton.OK, MessageBoxImage.Warning);
|
|
return;
|
|
}
|
|
|
|
if (!Directory.Exists(path))
|
|
{
|
|
MessageBox.Show("The specified directory does not exist.", "Directory not found", MessageBoxButton.OK, MessageBoxImage.Error);
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
foreach (var file in Directory.EnumerateFiles(path, "*.*", SearchOption.AllDirectories))
|
|
{
|
|
try
|
|
{
|
|
var fi = new FileInfo(file);
|
|
// check if an entry with same filename and same source directory already exists
|
|
var existing = _files.FirstOrDefault(f => string.Equals(f.Filename, fi.Name, StringComparison.OrdinalIgnoreCase)
|
|
&& string.Equals(f.FileDir, fi.DirectoryName, StringComparison.OrdinalIgnoreCase));
|
|
if (existing != null)
|
|
{
|
|
var newSizeMb = FormatBytesToMb(fi.Length);
|
|
if (existing.SizeSrc != newSizeMb)
|
|
{
|
|
// update size and modification date, do not add a new entry
|
|
existing.SizeSrc = newSizeMb;
|
|
existing.ModSrc = fi.LastWriteTime.ToString();
|
|
// reset verification status and archive drive info
|
|
existing.Status = string.Empty;
|
|
existing.ArcDrive = string.Empty;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_files.Add(new FileEntry
|
|
{
|
|
Filename = fi.Name,
|
|
FileDir = fi.DirectoryName,
|
|
SizeSrc = FormatBytesToMb(fi.Length),
|
|
//SizeArc = string.Empty,
|
|
ModSrc = fi.LastWriteTime.ToString(),
|
|
//ModArc = string.Empty
|
|
});
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
// ignore files we can't access
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
MessageBox.Show($"Error scanning directory: {ex.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
|
|
}
|
|
// inform user that scan finished
|
|
MessageBox.Show($"Scan abgeschlossen. Einträge: {_files.Count}", "Scan beendet", MessageBoxButton.OK, MessageBoxImage.Information);
|
|
}
|
|
#endregion (SCAN_RES_DIR)
|
|
#region(SCAN_ARC_DIR)
|
|
private void SCAN_ARC_DIR(object sender, RoutedEventArgs e)
|
|
{
|
|
var path = ArchiveTextBox.Text?.Trim();
|
|
if (string.IsNullOrWhiteSpace(path))
|
|
{
|
|
MessageBox.Show("Please select an archive directory first.", "No directory", MessageBoxButton.OK, MessageBoxImage.Warning);
|
|
return;
|
|
}
|
|
|
|
if (!Directory.Exists(path))
|
|
{
|
|
MessageBox.Show("The specified archive directory does not exist.", "Directory not found", MessageBoxButton.OK, MessageBoxImage.Error);
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
foreach (var file in Directory.EnumerateFiles(path, "*.*", SearchOption.AllDirectories))
|
|
{
|
|
try
|
|
{
|
|
var fi = new FileInfo(file);
|
|
|
|
// look for existing entry by filename (case-insensitive)
|
|
var existing = _files.FirstOrDefault(f => string.Equals(f.Filename, fi.Name, StringComparison.Ordinal));
|
|
if (existing != null)
|
|
{
|
|
// update SizeArc
|
|
existing.SizeArc = FormatBytesToMb(fi.Length);
|
|
|
|
// update ModArc to the most recent modified date
|
|
DateTime prevMod;
|
|
if (!DateTime.TryParse(existing.ModArc, out prevMod))
|
|
prevMod = DateTime.MinValue;
|
|
|
|
var mod = fi.LastWriteTime > prevMod ? fi.LastWriteTime : prevMod;
|
|
existing.ModArc = mod.ToString();
|
|
}
|
|
else
|
|
{
|
|
_files.Add(new FileEntry
|
|
{
|
|
Filename = fi.Name,
|
|
FileDir = fi.DirectoryName,
|
|
//SizeSrc = string.Empty,
|
|
SizeArc = FormatBytesToMb(fi.Length),
|
|
//ModSrc = string.Empty,
|
|
ModArc = fi.LastWriteTime.ToString()
|
|
});
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
// ignore inaccessible files
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
MessageBox.Show($"Error scanning archive directory: {ex.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
|
|
}
|
|
// inform user that archive scan finished
|
|
MessageBox.Show($"Scan abgeschlossen. Einträge: {_files.Count}", "Scan beendet", MessageBoxButton.OK, MessageBoxImage.Information);
|
|
}
|
|
#endregion(SCAN_ARC_DIR)
|
|
#region (CLEAR_FILES)
|
|
private void CLEAR_FILES(object sender, RoutedEventArgs e)
|
|
{
|
|
_files.Clear();
|
|
}
|
|
#endregion (CLEAR_FILES)
|
|
#region (VERIFY_SIZES)
|
|
private void VERIFY_SIZES(object sender, RoutedEventArgs e)
|
|
{
|
|
// determine archive drive name (volume label or drive root)
|
|
string archivePath = ArchiveTextBox.Text?.Trim();
|
|
string archiveDriveName = string.Empty;
|
|
if (!string.IsNullOrWhiteSpace(archivePath))
|
|
{
|
|
try
|
|
{
|
|
var root = System.IO.Path.GetPathRoot(archivePath);
|
|
if (!string.IsNullOrWhiteSpace(root))
|
|
{
|
|
try
|
|
{
|
|
var di = new DriveInfo(root);
|
|
archiveDriveName = string.IsNullOrWhiteSpace(di.VolumeLabel) ? root.TrimEnd('\\') : di.VolumeLabel;
|
|
}
|
|
catch
|
|
{
|
|
archiveDriveName = root.TrimEnd('\\');
|
|
}
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
archiveDriveName = string.Empty;
|
|
}
|
|
}
|
|
|
|
foreach (var entry in _files)
|
|
{
|
|
// Sizes are stored in MB with two decimals; compare as doubles
|
|
double src = 0;
|
|
double arc = 0;
|
|
double.TryParse(entry.SizeSrc, out src);
|
|
double.TryParse(entry.SizeArc, out arc);
|
|
|
|
if (src > 0 && arc > 0 && Math.Abs(src - arc) < 0.01)
|
|
{
|
|
entry.Status = "✔";
|
|
entry.ArcDrive = archiveDriveName;
|
|
}
|
|
else
|
|
{
|
|
entry.Status = "✖";
|
|
entry.ArcDrive = string.Empty;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endregion (VERIFY_SIZES)
|
|
public class FileEntry : System.ComponentModel.INotifyPropertyChanged
|
|
{
|
|
public string Filename { get; set; }
|
|
public string FileDir { get; set; }
|
|
private string _sizeSrc;
|
|
public string SizeSrc { get => _sizeSrc; set { _sizeSrc = value; OnPropertyChanged(nameof(SizeSrc)); } }
|
|
private string _sizeArc;
|
|
public string SizeArc { get => _sizeArc; set { _sizeArc = value; OnPropertyChanged(nameof(SizeArc)); } }
|
|
public string ModSrc { get; set; }
|
|
public string ModArc { get; set; }
|
|
private string _status;
|
|
public string Status { get => _status; set { _status = value; OnPropertyChanged(nameof(Status)); } }
|
|
private string _arcDrive;
|
|
public string ArcDrive { get => _arcDrive; set { _arcDrive = value; OnPropertyChanged(nameof(ArcDrive)); } }
|
|
|
|
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
|
|
private void OnPropertyChanged(string propName) => PropertyChanged?.Invoke(this, new System.ComponentModel.PropertyChangedEventArgs(propName));
|
|
}
|
|
}
|