SHA256
Initial Fill
This commit is contained in:
commit
976b651e4b
15 files changed
+1128
No files matched your search
+76
@@ -0,0 +1,76 @@
|
|||||||
|
# .gitignore for Visual Studio / .NET projects
|
||||||
|
# Generated minimal .gitignore
|
||||||
|
|
||||||
|
## Visual Studio temporary files, build results, and
|
||||||
|
## files generated by popular Visual Studio add-ons.
|
||||||
|
.vs/
|
||||||
|
*.user
|
||||||
|
*.suo
|
||||||
|
*.userosscache
|
||||||
|
*.sln.docstates
|
||||||
|
|
||||||
|
# Build results
|
||||||
|
[Bb]in/
|
||||||
|
[Oo]bj/
|
||||||
|
project.lock.json
|
||||||
|
project.fragment.lock.json
|
||||||
|
artifacts/
|
||||||
|
|
||||||
|
# NuGet
|
||||||
|
*.nupkg
|
||||||
|
packages/
|
||||||
|
|
||||||
|
# Visual Studio code coverage
|
||||||
|
*.coverage
|
||||||
|
*.coveragexml
|
||||||
|
|
||||||
|
# MSTest test results
|
||||||
|
TestResults/
|
||||||
|
|
||||||
|
# Rider
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# Resharper
|
||||||
|
_ReSharper*/
|
||||||
|
*.[Rr]e[Ss]harper
|
||||||
|
|
||||||
|
# DotCover
|
||||||
|
*.dotCover
|
||||||
|
|
||||||
|
# Others
|
||||||
|
*.log
|
||||||
|
*.tmp
|
||||||
|
*.temp
|
||||||
|
*.cache
|
||||||
|
*.bak
|
||||||
|
*.swp
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# Windows image file caches
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# User-specific files
|
||||||
|
*.aps
|
||||||
|
*.ncb
|
||||||
|
*.opendb
|
||||||
|
*.opensdf
|
||||||
|
|
||||||
|
# IIS Express
|
||||||
|
/.vs/**/applicationhost.config
|
||||||
|
|
||||||
|
# Node
|
||||||
|
node_modules/
|
||||||
|
|
||||||
|
# git LFS
|
||||||
|
*.psd
|
||||||
|
|
||||||
|
# Ignore NuGet packages folder
|
||||||
|
**/packages/*
|
||||||
|
|
||||||
|
# Ignore published output
|
||||||
|
publish/
|
||||||
|
|
||||||
|
# Ignore JetBrains Rider settings
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# Keep the file simple; add more entries as needed
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<configuration>
|
||||||
|
<startup>
|
||||||
|
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8" />
|
||||||
|
</startup>
|
||||||
|
</configuration>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<Application x:Class="TapeCatalog.App"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:local="clr-namespace:TapeCatalog"
|
||||||
|
StartupUri="MainWindow.xaml">
|
||||||
|
<Application.Resources>
|
||||||
|
|
||||||
|
</Application.Resources>
|
||||||
|
</Application>
|
||||||
+17
@@ -0,0 +1,17 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Configuration;
|
||||||
|
using System.Data;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows;
|
||||||
|
|
||||||
|
namespace TapeCatalog
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Interaction logic for App.xaml
|
||||||
|
/// </summary>
|
||||||
|
public partial class App : Application
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
<Window x:Class="TapeCatalog.MainWindow"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:local="clr-namespace:TapeCatalog"
|
||||||
|
mc:Ignorable="d"
|
||||||
|
Title="MainWindow" Height="450" Width="800">
|
||||||
|
<Grid>
|
||||||
|
<TabControl>
|
||||||
|
<TabItem Header="SRC-ARC">
|
||||||
|
<Grid Background="#FFE5E5E5">
|
||||||
|
<TextBox x:Name="SourceTextBox" HorizontalAlignment="Left" Margin="0,19,0,0" TextWrapping="Wrap" Text="Source Directory" VerticalAlignment="Top" Width="507" Height="20"/>
|
||||||
|
<TextBox x:Name="ArchiveTextBox" HorizontalAlignment="Left" Height="20" Margin="0,56,0,0" TextWrapping="Wrap" Text="Archive Directory" VerticalAlignment="Top" Width="507"/>
|
||||||
|
<Button x:Name="SourceSelect" Content="Select Directory" HorizontalAlignment="Left" Margin="512,19,0,0" VerticalAlignment="Top" Height="20" Click="SET_RES_DIR"/>
|
||||||
|
<Button x:Name="ArchiveSelect" Content="Select Directory" HorizontalAlignment="Left" Margin="512,56,0,0" VerticalAlignment="Top" Height="20" Click="SET_ARC_DIR"/>
|
||||||
|
<Button Content="Scan Source" HorizontalAlignment="Left" Height="22" Margin="700,146,0,0" VerticalAlignment="Top" Width="76" Click="SCAN_RES_DIR" RenderTransformOrigin="2.316,5.185" Grid.Column="1"/>
|
||||||
|
<Button Content="Scan Archive" HorizontalAlignment="Left" Height="22" Margin="700,168,0,0" VerticalAlignment="Top" Width="76" Click="SCAN_ARC_DIR" Grid.Column="1"/>
|
||||||
|
<Button Content="Clear List" HorizontalAlignment="Left" Height="22" Margin="700,190,0,0" VerticalAlignment="Top" Width="76" Click="CLEAR_FILES" Grid.Column="1"/>
|
||||||
|
<Button Content="Verify Sizes" HorizontalAlignment="Left" Height="22" Margin="700,212,0,0" VerticalAlignment="Top" Width="76" Click="VERIFY_SIZES" Grid.Column="1"/>
|
||||||
|
<Button Content="Export XML" HorizontalAlignment="Left" Height="22" Margin="700,234,0,0" VerticalAlignment="Top" Width="76" Click="EXPORT_LIST" Grid.Column="1"/>
|
||||||
|
<Button Content="Import XML" HorizontalAlignment="Left" Height="22" Margin="700,256,0,0" VerticalAlignment="Top" Width="76" Click="IMPORT_LIST" Grid.Column="1"/>
|
||||||
|
<Button Content="-> TapeCat" HorizontalAlignment="Left" Height="22" Margin="700,278,0,0" VerticalAlignment="Top" Width="76" Click="COPY_TO_TAPECATALOG" Grid.Column="1"/>
|
||||||
|
<ListView x:Name="FilesListView" Margin="10,146,101,10" Grid.ColumnSpan="2">
|
||||||
|
<ListView.View>
|
||||||
|
<GridView>
|
||||||
|
<GridViewColumn Header="Filename" DisplayMemberBinding="{Binding Filename}" Width="300"/>
|
||||||
|
<GridViewColumn Header="File Dir" DisplayMemberBinding="{Binding FileDir}" Width="300"/>
|
||||||
|
<GridViewColumn Header="Size SRC (MB)" DisplayMemberBinding="{Binding SizeSrc}" Width="100"/>
|
||||||
|
<GridViewColumn Header="Size ARC (MB)" DisplayMemberBinding="{Binding SizeArc}" Width="100"/>
|
||||||
|
<GridViewColumn Header="Status" DisplayMemberBinding="{Binding Status}" Width="60"/>
|
||||||
|
<GridViewColumn Header="Archive Drive" DisplayMemberBinding="{Binding ArcDrive}" Width="140"/>
|
||||||
|
<GridViewColumn Header="Mod SRC" DisplayMemberBinding="{Binding ModSrc}" Width="140"/>
|
||||||
|
<GridViewColumn Header="Mod ARC" DisplayMemberBinding="{Binding ModArc}" Width="140"/>
|
||||||
|
</GridView>
|
||||||
|
</ListView.View>
|
||||||
|
</ListView>
|
||||||
|
<TextBlock TextWrapping="Wrap" Text="Source Directory:" Margin="0,0,697,387"/>
|
||||||
|
<TextBlock TextWrapping="Wrap" Text="Archive Directory:" Margin="0,37,697,350"/>
|
||||||
|
</Grid>
|
||||||
|
</TabItem>
|
||||||
|
<TabItem Header="Tapecatalog">
|
||||||
|
<Grid Background="#FFE5E5E5">
|
||||||
|
<ComboBox x:Name="MediumComboBox" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Width="200" Grid.ColumnSpan="2"/>
|
||||||
|
<Button Content="Refresh Mediums" HorizontalAlignment="Left" Height="22" Margin="218,10,0,0" VerticalAlignment="Top" Width="120" Click="REFRESH_MEDIUMS"/>
|
||||||
|
<Button Content="Copy from SRC-ARC" HorizontalAlignment="Left" Height="22" Margin="343,10,0,0" VerticalAlignment="Top" Width="120" Click="COPY_FROM_SRCARC"/>
|
||||||
|
<ListView x:Name="TapeCatalogListView" Margin="10,44,10,40" Grid.ColumnSpan="2">
|
||||||
|
<ListView.View>
|
||||||
|
<GridView>
|
||||||
|
<GridViewColumn Header="Medium" DisplayMemberBinding="{Binding Medium}" Width="150"/>
|
||||||
|
<GridViewColumn Header="Dateiname" DisplayMemberBinding="{Binding Filename}" Width="400"/>
|
||||||
|
<GridViewColumn Header="Änderungsdatum" DisplayMemberBinding="{Binding Modified}" Width="200"/>
|
||||||
|
</GridView>
|
||||||
|
</ListView.View>
|
||||||
|
</ListView>
|
||||||
|
<Button Content="Export Medium" HorizontalAlignment="Left" Height="22" Margin="468,0,0,374" VerticalAlignment="Bottom" Width="120" Click="EXPORT_MEDIUM"/>
|
||||||
|
</Grid>
|
||||||
|
</TabItem>
|
||||||
|
</TabControl>
|
||||||
|
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
||||||
@@ -0,0 +1,364 @@
|
|||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
using System.Reflection;
|
||||||
|
using System.Resources;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Windows;
|
||||||
|
|
||||||
|
// General Information about an assembly is controlled through the following
|
||||||
|
// set of attributes. Change these attribute values to modify the information
|
||||||
|
// associated with an assembly.
|
||||||
|
[assembly: AssemblyTitle("TapeCatalog")]
|
||||||
|
[assembly: AssemblyDescription("")]
|
||||||
|
[assembly: AssemblyConfiguration("")]
|
||||||
|
[assembly: AssemblyCompany("")]
|
||||||
|
[assembly: AssemblyProduct("TapeCatalog")]
|
||||||
|
[assembly: AssemblyCopyright("Copyright © 2026")]
|
||||||
|
[assembly: AssemblyTrademark("")]
|
||||||
|
[assembly: AssemblyCulture("")]
|
||||||
|
|
||||||
|
// Setting ComVisible to false makes the types in this assembly not visible
|
||||||
|
// to COM components. If you need to access a type in this assembly from
|
||||||
|
// COM, set the ComVisible attribute to true on that type.
|
||||||
|
[assembly: ComVisible(false)]
|
||||||
|
|
||||||
|
//In order to begin building localizable applications, set
|
||||||
|
//<UICulture>CultureYouAreCodingWith</UICulture> in your .csproj file
|
||||||
|
//inside a <PropertyGroup>. For example, if you are using US english
|
||||||
|
//in your source files, set the <UICulture> to en-US. Then uncomment
|
||||||
|
//the NeutralResourceLanguage attribute below. Update the "en-US" in
|
||||||
|
//the line below to match the UICulture setting in the project file.
|
||||||
|
|
||||||
|
//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
|
||||||
|
|
||||||
|
|
||||||
|
[assembly: ThemeInfo(
|
||||||
|
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
|
||||||
|
//(used if a resource is not found in the page,
|
||||||
|
// or application resource dictionaries)
|
||||||
|
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
|
||||||
|
//(used if a resource is not found in the page,
|
||||||
|
// app, or any theme specific resource dictionaries)
|
||||||
|
)]
|
||||||
|
|
||||||
|
|
||||||
|
// Version information for an assembly consists of the following four values:
|
||||||
|
//
|
||||||
|
// Major Version
|
||||||
|
// Minor Version
|
||||||
|
// Build Number
|
||||||
|
// Revision
|
||||||
|
//
|
||||||
|
[assembly: AssemblyVersion("1.0.0.0")]
|
||||||
|
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||||
Generated
+71
@@ -0,0 +1,71 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// <auto-generated>
|
||||||
|
// This code was generated by a tool.
|
||||||
|
// Runtime Version:4.0.30319.42000
|
||||||
|
//
|
||||||
|
// Changes to this file may cause incorrect behavior and will be lost if
|
||||||
|
// the code is regenerated.
|
||||||
|
// </auto-generated>
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
namespace TapeCatalog.Properties
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||||
|
/// </summary>
|
||||||
|
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||||
|
// class via a tool like ResGen or Visual Studio.
|
||||||
|
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||||
|
// with the /str option, or rebuild your VS project.
|
||||||
|
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
|
||||||
|
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||||
|
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||||
|
internal class Resources
|
||||||
|
{
|
||||||
|
|
||||||
|
private static global::System.Resources.ResourceManager resourceMan;
|
||||||
|
|
||||||
|
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||||
|
|
||||||
|
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||||
|
internal Resources()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the cached ResourceManager instance used by this class.
|
||||||
|
/// </summary>
|
||||||
|
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||||
|
internal static global::System.Resources.ResourceManager ResourceManager
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if ((resourceMan == null))
|
||||||
|
{
|
||||||
|
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("TapeCatalog.Properties.Resources", typeof(Resources).Assembly);
|
||||||
|
resourceMan = temp;
|
||||||
|
}
|
||||||
|
return resourceMan;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Overrides the current thread's CurrentUICulture property for all
|
||||||
|
/// resource lookups using this strongly typed resource class.
|
||||||
|
/// </summary>
|
||||||
|
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||||
|
internal static global::System.Globalization.CultureInfo Culture
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return resourceCulture;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
resourceCulture = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,117 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<root>
|
||||||
|
<!--
|
||||||
|
Microsoft ResX Schema
|
||||||
|
|
||||||
|
Version 2.0
|
||||||
|
|
||||||
|
The primary goals of this format is to allow a simple XML format
|
||||||
|
that is mostly human readable. The generation and parsing of the
|
||||||
|
various data types are done through the TypeConverter classes
|
||||||
|
associated with the data types.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
... ado.net/XML headers & schema ...
|
||||||
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
|
<resheader name="version">2.0</resheader>
|
||||||
|
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||||
|
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||||
|
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||||
|
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||||
|
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||||
|
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||||
|
</data>
|
||||||
|
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
|
<comment>This is a comment</comment>
|
||||||
|
</data>
|
||||||
|
|
||||||
|
There are any number of "resheader" rows that contain simple
|
||||||
|
name/value pairs.
|
||||||
|
|
||||||
|
Each data row contains a name, and value. The row also contains a
|
||||||
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
|
text/value conversion through the TypeConverter architecture.
|
||||||
|
Classes that don't support this are serialized and stored with the
|
||||||
|
mimetype set.
|
||||||
|
|
||||||
|
The mimetype is used for serialized objects, and tells the
|
||||||
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
|
read any of the formats listed below.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
|
value : The object must be serialized into a byte array
|
||||||
|
: using a System.ComponentModel.TypeConverter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
-->
|
||||||
|
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||||
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:choice maxOccurs="unbounded">
|
||||||
|
<xsd:element name="metadata">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="assembly">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:attribute name="alias" type="xsd:string" />
|
||||||
|
<xsd:attribute name="name" type="xsd:string" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="data">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="resheader">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:schema>
|
||||||
|
<resheader name="resmimetype">
|
||||||
|
<value>text/microsoft-resx</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="version">
|
||||||
|
<value>2.0</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="reader">
|
||||||
|
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="writer">
|
||||||
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
</root>
|
||||||
Generated
+30
@@ -0,0 +1,30 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// <auto-generated>
|
||||||
|
// This code was generated by a tool.
|
||||||
|
// Runtime Version:4.0.30319.42000
|
||||||
|
//
|
||||||
|
// Changes to this file may cause incorrect behavior and will be lost if
|
||||||
|
// the code is regenerated.
|
||||||
|
// </auto-generated>
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
namespace TapeCatalog.Properties
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||||
|
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
|
||||||
|
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
|
||||||
|
{
|
||||||
|
|
||||||
|
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
|
||||||
|
|
||||||
|
public static Settings Default
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return defaultInstance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
|
<SettingsFile xmlns="uri:settings" CurrentProfile="(Default)">
|
||||||
|
<Profiles>
|
||||||
|
<Profile Name="(Default)" />
|
||||||
|
</Profiles>
|
||||||
|
<Settings />
|
||||||
|
</SettingsFile>
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||||
|
<PropertyGroup>
|
||||||
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||||
|
<ProjectGuid>{2ED075BB-CE1E-4B8C-B81C-E0BEA77D1685}</ProjectGuid>
|
||||||
|
<OutputType>WinExe</OutputType>
|
||||||
|
<RootNamespace>TapeCatalog</RootNamespace>
|
||||||
|
<AssemblyName>TapeCatalog</AssemblyName>
|
||||||
|
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
|
||||||
|
<FileAlignment>512</FileAlignment>
|
||||||
|
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||||
|
<Deterministic>true</Deterministic>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
|
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||||
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
<DebugType>full</DebugType>
|
||||||
|
<Optimize>false</Optimize>
|
||||||
|
<OutputPath>bin\Debug\</OutputPath>
|
||||||
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||||
|
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||||
|
<DebugType>pdbonly</DebugType>
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<OutputPath>bin\Release\</OutputPath>
|
||||||
|
<DefineConstants>TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="System" />
|
||||||
|
<Reference Include="System.Data" />
|
||||||
|
<Reference Include="System.Windows.Forms" />
|
||||||
|
<Reference Include="System.Xml" />
|
||||||
|
<Reference Include="Microsoft.CSharp" />
|
||||||
|
<Reference Include="System.Core" />
|
||||||
|
<Reference Include="System.Xml.Linq" />
|
||||||
|
<Reference Include="System.Data.DataSetExtensions" />
|
||||||
|
<Reference Include="System.Net.Http" />
|
||||||
|
<Reference Include="System.Xaml">
|
||||||
|
<RequiredTargetFramework>4.0</RequiredTargetFramework>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="WindowsBase" />
|
||||||
|
<Reference Include="PresentationCore" />
|
||||||
|
<Reference Include="PresentationFramework" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ApplicationDefinition Include="App.xaml">
|
||||||
|
<Generator>MSBuild:Compile</Generator>
|
||||||
|
<SubType>Designer</SubType>
|
||||||
|
</ApplicationDefinition>
|
||||||
|
<Compile Include="XML_Methods.cs" />
|
||||||
|
<Page Include="MainWindow.xaml">
|
||||||
|
<Generator>MSBuild:Compile</Generator>
|
||||||
|
<SubType>Designer</SubType>
|
||||||
|
</Page>
|
||||||
|
<Compile Include="App.xaml.cs">
|
||||||
|
<DependentUpon>App.xaml</DependentUpon>
|
||||||
|
<SubType>Code</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="MainWindow.xaml.cs">
|
||||||
|
<DependentUpon>MainWindow.xaml</DependentUpon>
|
||||||
|
<SubType>Code</SubType>
|
||||||
|
</Compile>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="Properties\AssemblyInfo.cs">
|
||||||
|
<SubType>Code</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Properties\Resources.Designer.cs">
|
||||||
|
<AutoGen>True</AutoGen>
|
||||||
|
<DesignTime>True</DesignTime>
|
||||||
|
<DependentUpon>Resources.resx</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Properties\Settings.Designer.cs">
|
||||||
|
<AutoGen>True</AutoGen>
|
||||||
|
<DependentUpon>Settings.settings</DependentUpon>
|
||||||
|
<DesignTimeSharedInput>True</DesignTimeSharedInput>
|
||||||
|
</Compile>
|
||||||
|
<EmbeddedResource Include="Properties\Resources.resx">
|
||||||
|
<Generator>ResXFileCodeGenerator</Generator>
|
||||||
|
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||||
|
</EmbeddedResource>
|
||||||
|
<None Include="Properties\Settings.settings">
|
||||||
|
<Generator>SettingsSingleFileGenerator</Generator>
|
||||||
|
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="App.config" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include=".gitignore" />
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
<Solution>
|
||||||
|
<Project Path="TapeCatalog.csproj" Id="2ed075bb-ce1e-4b8c-b81c-e0bea77d1685" />
|
||||||
|
</Solution>
|
||||||
+203
@@ -0,0 +1,203 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Xml.Linq;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using System.Windows;
|
||||||
|
using Microsoft.Win32;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using TapeCatalog;
|
||||||
|
|
||||||
|
namespace XML
|
||||||
|
{
|
||||||
|
public static class Methods
|
||||||
|
{
|
||||||
|
public class TapeEntry
|
||||||
|
{
|
||||||
|
public string Medium { get; set; }
|
||||||
|
public string Filename { get; set; }
|
||||||
|
public string Modified { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ExportTapeEntries(Window owner, string medium, IEnumerable<TapeEntry> entries)
|
||||||
|
{
|
||||||
|
if (entries == null) return;
|
||||||
|
|
||||||
|
var dlg = new Microsoft.Win32.SaveFileDialog()
|
||||||
|
{
|
||||||
|
Filter = "XML Files (*.xml)|*.xml|All Files (*.*)|*.*",
|
||||||
|
DefaultExt = "xml",
|
||||||
|
AddExtension = true,
|
||||||
|
FileName = $"tape_{medium}.xml"
|
||||||
|
};
|
||||||
|
|
||||||
|
var res = dlg.ShowDialog(owner);
|
||||||
|
if (res != true) return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var doc = new XDocument(
|
||||||
|
new XElement("TapeEntries",
|
||||||
|
new XElement("Medium", medium ?? string.Empty),
|
||||||
|
entries.Select(e => new XElement("Entry",
|
||||||
|
new XElement("Medium", e.Medium ?? string.Empty),
|
||||||
|
new XElement("Filename", e.Filename ?? string.Empty),
|
||||||
|
new XElement("Modified", e.Modified ?? string.Empty)
|
||||||
|
))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
doc.Save(dlg.FileName);
|
||||||
|
MessageBox.Show(owner, $"Export erfolgreich nach {dlg.FileName}", "Export", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
MessageBox.Show(owner, $"Fehler beim Export: {ex.Message}", "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static void CopyToTapeCatalog(Window owner, IEnumerable<FileEntry> sourceFiles, ListView targetListView)
|
||||||
|
{
|
||||||
|
if (sourceFiles == null || targetListView == null) return;
|
||||||
|
|
||||||
|
// add entries: Medium = ArcDrive, Dateiname = Filename, Änderungsdatum = SizeArc (archive size)
|
||||||
|
foreach (var f in sourceFiles)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var te = new TapeEntry
|
||||||
|
{
|
||||||
|
Medium = f.ArcDrive ?? string.Empty,
|
||||||
|
Filename = f.Filename ?? string.Empty,
|
||||||
|
Modified = f.SizeArc ?? string.Empty
|
||||||
|
};
|
||||||
|
|
||||||
|
// add to ListView on UI thread
|
||||||
|
if (!targetListView.Dispatcher.CheckAccess())
|
||||||
|
{
|
||||||
|
targetListView.Dispatcher.Invoke(() => targetListView.Items.Add(te));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
targetListView.Items.Add(te);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// ignore malformed entries
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static void ImportList(Window owner, ObservableCollection<FileEntry> files, TextBox sourceTextBox, TextBox archiveTextBox)
|
||||||
|
{
|
||||||
|
if (files == null) return;
|
||||||
|
|
||||||
|
if (files.Count > 0)
|
||||||
|
{
|
||||||
|
var ask = MessageBox.Show(owner, "Aktuelle Liste speichern?\nJa = speichern, Nein = löschen, Abbrechen = abbrechen.", "Liste vorhanden", MessageBoxButton.YesNoCancel, MessageBoxImage.Question);
|
||||||
|
if (ask == MessageBoxResult.Cancel) return;
|
||||||
|
|
||||||
|
if (ask == MessageBoxResult.Yes)
|
||||||
|
{
|
||||||
|
// save current list
|
||||||
|
ExportList(owner, sourceTextBox?.Text ?? string.Empty, archiveTextBox?.Text ?? string.Empty, files);
|
||||||
|
files.Clear();
|
||||||
|
}
|
||||||
|
else if (ask == MessageBoxResult.No)
|
||||||
|
{
|
||||||
|
files.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var dlg = new Microsoft.Win32.OpenFileDialog()
|
||||||
|
{
|
||||||
|
Filter = "XML Files (*.xml)|*.xml|All Files (*.*)|*.*",
|
||||||
|
DefaultExt = "xml",
|
||||||
|
Multiselect = false
|
||||||
|
};
|
||||||
|
|
||||||
|
var res = dlg.ShowDialog(owner);
|
||||||
|
if (res != true) return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var doc = XDocument.Load(dlg.FileName);
|
||||||
|
var root = doc.Element("Files");
|
||||||
|
if (root == null) return;
|
||||||
|
|
||||||
|
var src = root.Element("SourceDir")?.Value ?? string.Empty;
|
||||||
|
var arc = root.Element("ArchiveDir")?.Value ?? string.Empty;
|
||||||
|
if (sourceTextBox != null) sourceTextBox.Text = src;
|
||||||
|
if (archiveTextBox != null) archiveTextBox.Text = arc;
|
||||||
|
|
||||||
|
foreach (var fe in root.Elements("File"))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var entry = new FileEntry
|
||||||
|
{
|
||||||
|
Filename = fe.Element("Filename")?.Value ?? string.Empty,
|
||||||
|
FileDir = fe.Element("FileDir")?.Value ?? string.Empty,
|
||||||
|
SizeSrc = fe.Element("SizeSrc")?.Value ?? string.Empty,
|
||||||
|
SizeArc = fe.Element("SizeArc")?.Value ?? string.Empty,
|
||||||
|
Status = fe.Element("Status")?.Value ?? string.Empty,
|
||||||
|
ArcDrive = fe.Element("ArcDrive")?.Value ?? string.Empty,
|
||||||
|
ModSrc = fe.Element("ModSrc")?.Value ?? string.Empty,
|
||||||
|
ModArc = fe.Element("ModArc")?.Value ?? string.Empty
|
||||||
|
};
|
||||||
|
files.Add(entry);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// ignore malformed entries
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MessageBox.Show(owner, $"Import erfolgreich von {dlg.FileName}", "Import", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
MessageBox.Show(owner, $"Fehler beim Import: {ex.Message}", "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static void ExportList(Window owner, string sourceDir, string archiveDir, IEnumerable<FileEntry> files)
|
||||||
|
{
|
||||||
|
var dlg = new SaveFileDialog()
|
||||||
|
{
|
||||||
|
Filter = "XML Files (*.xml)|*.xml|All Files (*.*)|*.*",
|
||||||
|
DefaultExt = "xml",
|
||||||
|
AddExtension = true,
|
||||||
|
FileName = "filelist.xml"
|
||||||
|
};
|
||||||
|
|
||||||
|
var res = dlg.ShowDialog(owner);
|
||||||
|
if (res != true)
|
||||||
|
return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var doc = new XDocument(
|
||||||
|
new XElement("Files",
|
||||||
|
new XElement("SourceDir", sourceDir ?? string.Empty),
|
||||||
|
new XElement("ArchiveDir", archiveDir ?? string.Empty),
|
||||||
|
files.Select(f => new XElement("File",
|
||||||
|
new XElement("Filename", f.Filename ?? string.Empty),
|
||||||
|
new XElement("FileDir", f.FileDir ?? string.Empty),
|
||||||
|
new XElement("SizeSrc", f.SizeSrc ?? string.Empty),
|
||||||
|
new XElement("SizeArc", f.SizeArc ?? string.Empty),
|
||||||
|
new XElement("Status", f.Status ?? string.Empty),
|
||||||
|
new XElement("ArcDrive", f.ArcDrive ?? string.Empty),
|
||||||
|
new XElement("ModSrc", f.ModSrc ?? string.Empty),
|
||||||
|
new XElement("ModArc", f.ModArc ?? string.Empty)
|
||||||
|
))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
doc.Save(dlg.FileName);
|
||||||
|
MessageBox.Show(owner, $"Export erfolgreich nach {dlg.FileName}", "Export", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
MessageBox.Show(owner, $"Fehler beim Export: {ex.Message}", "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user