1. Iteration of the Copy Function

2. Settings Tap
3. XML TAB
This commit is contained in:
Black_Betty committed 2026-10-04 20:29:33 +02:00
1 parent 33153f9448
commit abab3612f2
5 files changed
+499 -10

No files matched your search

+49 -10
View File
@@ -7,15 +7,13 @@
mc:Ignorable="d" mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800"> Title="MainWindow" Height="450" Width="800">
<Grid> <Grid>
<TabControl> <TabControl x:Name="MainTabControl">
<TabItem Header="SRC-ARC"> <TabItem Header="SRC-ARC">
<Grid Background="#FFE5E5E5"> <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"/> <Grid.ColumnDefinitions>
<TextBox x:Name="ArchiveTextBox" HorizontalAlignment="Left" Height="20" Margin="0,56,0,0" TextWrapping="Wrap" Text="Archive Directory" VerticalAlignment="Top" Width="507"/> <ColumnDefinition Width="99*"/>
<Button x:Name="SourceSelect" Content="Select Directory" HorizontalAlignment="Left" Margin="512,19,0,0" VerticalAlignment="Top" Height="20" Click="SET_RES_DIR"/> <ColumnDefinition Width="298*"/>
<Button x:Name="ArchiveSelect" Content="Select Directory" HorizontalAlignment="Left" Margin="512,56,0,0" VerticalAlignment="Top" Height="20" Click="SET_ARC_DIR"/> </Grid.ColumnDefinitions>
<Button Content="Scan Source" HorizontalAlignment="Right" Height="22" Margin="0,146,10,0" VerticalAlignment="Top" Width="76" Click="SCAN_RES_DIR" RenderTransformOrigin="2.316,5.185" Grid.Column="1"/>
<Button Content="Scan Archive" HorizontalAlignment="Right" Height="22" Margin="0,168,10,0" VerticalAlignment="Top" Width="76" Click="SCAN_ARC_DIR" Grid.Column="1"/>
<Button Content="Clear List" HorizontalAlignment="Right" Height="22" Margin="0,190,10,0" VerticalAlignment="Top" Width="76" Click="CLEAR_FILES" Grid.Column="1"/> <Button Content="Clear List" HorizontalAlignment="Right" Height="22" Margin="0,190,10,0" VerticalAlignment="Top" Width="76" Click="CLEAR_FILES" Grid.Column="1"/>
<Button Content="Verify Sizes" HorizontalAlignment="Right" Height="22" Margin="0,212,10,0" VerticalAlignment="Top" Width="76" Click="VERIFY_SIZES" Grid.Column="1"/> <Button Content="Verify Sizes" HorizontalAlignment="Right" Height="22" Margin="0,212,10,0" VerticalAlignment="Top" Width="76" Click="VERIFY_SIZES" Grid.Column="1"/>
<Button Content="Export XML" HorizontalAlignment="Right" Height="22" Margin="0,234,10,0" VerticalAlignment="Top" Width="76" Click="EXPORT_LIST" Grid.Column="1"/> <Button Content="Export XML" HorizontalAlignment="Right" Height="22" Margin="0,234,10,0" VerticalAlignment="Top" Width="76" Click="EXPORT_LIST" Grid.Column="1"/>
@@ -35,8 +33,7 @@
</GridView> </GridView>
</ListView.View> </ListView.View>
</ListView> </ListView>
<TextBlock TextWrapping="Wrap" Text="Source Directory:" Margin="0,0,697,387"/>
<TextBlock TextWrapping="Wrap" Text="Archive Directory:" Margin="0,37,697,350"/>
</Grid> </Grid>
</TabItem> </TabItem>
<TabItem Header="Tapecatalog"> <TabItem Header="Tapecatalog">
@@ -56,7 +53,49 @@
<Button Content="Export Medium" HorizontalAlignment="Left" Height="22" Margin="468,0,0,374" VerticalAlignment="Top" Width="120" Click="EXPORT_MEDIUM"/> <Button Content="Export Medium" HorizontalAlignment="Left" Height="22" Margin="468,0,0,374" VerticalAlignment="Top" Width="120" Click="EXPORT_MEDIUM"/>
</Grid> </Grid>
</TabItem> </TabItem>
<TabItem Header="Copy">
<Grid Background="#FFE5E5E5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="102*"/>
<ColumnDefinition Width="295*"/>
</Grid.ColumnDefinitions>
<Button Content="Refresh Copy Tree" HorizontalAlignment="Left" Height="22" Margin="10,10,0,0" VerticalAlignment="Top" Width="140" Click="POPULATE_COPY_TREE"/>
<Button Content="Copy Selected to Archive" HorizontalAlignment="Left" Height="22" Margin="160,10,0,0" VerticalAlignment="Top" Width="180" Click="COPY_SELECTED_TO_ARCHIVE" Grid.ColumnSpan="2"/>
<TreeView x:Name="CopyTreeView" Margin="10,40,10,10" Grid.ColumnSpan="2" />
</Grid>
</TabItem>
<TabItem Header="XML">
<Grid Background="#FFE5E5E5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="250"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<ListBox x:Name="XmlFilesListBox" Margin="4" SelectionChanged="XmlFilesListBox_SelectionChanged" />
<TextBox x:Name="XmlViewerTextBox" Margin="4" Grid.Column="1" IsReadOnly="True" TextWrapping="Wrap" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto" />
</Grid>
</TabItem>
<TabItem Header="Settings" Height="20" VerticalAlignment="Center" RenderTransformOrigin="0.104,0.652" HorizontalAlignment="Left" Width="55">
<Grid Background="#FFE5E5E5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0*"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Text="Application Settings" FontWeight="Bold" Margin="0,0,0,10" Grid.ColumnSpan="2" />
<TextBox x:Name="SourceTextBox" HorizontalAlignment="Left" Height="20" Margin="0,40,0,0" TextWrapping="Wrap" Text="Source Directory" VerticalAlignment="Top" Width="510" Grid.ColumnSpan="2"/>
<TextBox x:Name="ArchiveTextBox" HorizontalAlignment="Left" Height="20" Margin="0,80,0,0" TextWrapping="Wrap" Text="Archive Directory" VerticalAlignment="Top" Width="510" Grid.ColumnSpan="2"/>
<TextBlock Text="XML Directory:" Height="20" Margin="0,100,101,0" VerticalAlignment="Top" Grid.ColumnSpan="2"/>
<TextBox x:Name="XmlDirectoryTextBox" HorizontalAlignment="Left" Height="20" Margin="0,120,0,0" TextWrapping="Wrap" Text="XML Directory" VerticalAlignment="Top" Width="510" Grid.ColumnSpan="2"/>
<Button x:Name="XmlDirectorySelect" Content="Select Directory" HorizontalAlignment="Left" Margin="520,120,0,0" VerticalAlignment="Top" Height="20" Click="SET_XML_DIR" Grid.Column="1"/>
<Button x:Name="SourceSelect" Content="Select Directory" HorizontalAlignment="Left" Margin="520,40,0,0" VerticalAlignment="Top" Height="20" Click="SET_RES_DIR" Grid.Column="1" Width="87"/>
<Button x:Name="ArchiveSelect" Content="Select Directory" HorizontalAlignment="Left" Margin="520,80,0,0" VerticalAlignment="Top" Height="20" Click="SET_ARC_DIR" Grid.Column="1" Width="87"/>
<Button Content="Scan Source" HorizontalAlignment="Left" Height="22" Margin="10,160,0,0" VerticalAlignment="Top" Width="120" Click="SCAN_RES_DIR" Grid.Column="1"/>
<Button Content="Scan Archive" HorizontalAlignment="Left" Height="22" Margin="10,182,0,0" VerticalAlignment="Top" Width="120" Click="SCAN_ARC_DIR" Grid.Column="1"/>
<Button Content="Load XML Files" HorizontalAlignment="Left" Height="22" Margin="10,204,0,0" VerticalAlignment="Top" Width="120" Click="LOAD_XML_FILES" Grid.Column="1"/>
<TextBlock TextWrapping="Wrap" Text="Source Directory:" Height="20" Margin="0,20,101,0" VerticalAlignment="Top"/>
<TextBlock TextWrapping="Wrap" Text="Archive Directory:" Height="20" Margin="0,60,101,0" VerticalAlignment="Top"/>
</Grid>
</TabItem>
</TabControl> </TabControl>
</Grid> </Grid>
</Window> </Window>
+213
View File
@@ -34,6 +34,25 @@ namespace TapeCatalog
return mb.ToString("F2"); return mb.ToString("F2");
} }
private void XmlFilesListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (XmlFilesListBox.SelectedItem is string path)
{
try
{
XmlViewerTextBox.Text = File.ReadAllText(path);
}
catch (Exception ex)
{
XmlViewerTextBox.Text = $"Error reading file: {ex.Message}";
}
}
else
{
XmlViewerTextBox.Clear();
}
}
public MainWindow() public MainWindow()
{ {
InitializeComponent(); InitializeComponent();
@@ -153,6 +172,62 @@ namespace TapeCatalog
} }
} }
#endregion (SET_ARC_DIR) #endregion (SET_ARC_DIR)
#region (SET_XML_DIR)
private void SET_XML_DIR(object sender, RoutedEventArgs e)
{
// Use the WinForms FolderBrowserDialog to select a folder for XML files
using (var dlg = new System.Windows.Forms.FolderBrowserDialog())
{
dlg.Description = "Select XML folder";
dlg.ShowNewFolderButton = true;
var result = dlg.ShowDialog();
if (result == System.Windows.Forms.DialogResult.OK && !string.IsNullOrWhiteSpace(dlg.SelectedPath))
{
XmlDirectoryTextBox.Text = dlg.SelectedPath;
}
}
}
#endregion (SET_XML_DIR)
#region (LOAD_XML_FILES)
private void LOAD_XML_FILES(object sender, RoutedEventArgs e)
{
var xmlPath = XmlDirectoryTextBox.Text?.Trim();
if (string.IsNullOrWhiteSpace(xmlPath) || !Directory.Exists(xmlPath))
{
MessageBox.Show(this, "Please select a valid XML directory first.", "Invalid XML Directory", MessageBoxButton.OK, MessageBoxImage.Warning);
return;
}
string[] files;
try
{
files = Directory.GetFiles(xmlPath, "*.xml", SearchOption.TopDirectoryOnly);
}
catch (Exception ex)
{
MessageBox.Show(this, $"Error enumerating XML files: {ex.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
return;
}
XmlFilesListBox.Items.Clear();
XmlViewerTextBox.Clear();
foreach (var f in files)
{
XmlFilesListBox.Items.Add(f);
}
// switch to XML tab
foreach (var it in MainTabControl.Items)
{
if (it is TabItem ti && (ti.Header as string) == "XML")
{
MainTabControl.SelectedItem = ti;
break;
}
}
}
#endregion (LOAD_XML_FILES)
#region (SCAN_RES_DIR) #region (SCAN_RES_DIR)
private void SCAN_RES_DIR(object sender, RoutedEventArgs e) private void SCAN_RES_DIR(object sender, RoutedEventArgs e)
{ {
@@ -341,6 +416,144 @@ namespace TapeCatalog
} }
} }
} }
// Populate CopyTreeView with directory structure from SourceTextBox
private void POPULATE_COPY_TREE(object sender, RoutedEventArgs e)
{
BuildCopyTree();
}
private void BuildCopyTree()
{
CopyTreeView.Items.Clear();
var path = SourceTextBox.Text?.Trim();
if (string.IsNullOrWhiteSpace(path) || !Directory.Exists(path))
{
MessageBox.Show(this, "Please select a valid Source directory first.", "Invalid Source", MessageBoxButton.OK, MessageBoxImage.Warning);
return;
}
try
{
var rootDir = new DirectoryInfo(path);
var rootItem = CreateDirectoryNode(rootDir);
rootItem.IsExpanded = true;
CopyTreeView.Items.Add(rootItem);
}
catch (Exception ex)
{
MessageBox.Show(this, $"Error building copy tree: {ex.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private TreeViewItem CreateDirectoryNode(DirectoryInfo dir)
{
var item = new TreeViewItem { Header = dir.Name, Tag = dir.FullName };
try
{
foreach (var sub in dir.GetDirectories())
{
item.Items.Add(CreateDirectoryNode(sub));
}
foreach (var f in dir.GetFiles())
{
var fileItem = new TreeViewItem { Tag = f.FullName };
var sp = new StackPanel { Orientation = Orientation.Horizontal };
var cb = new CheckBox { VerticalAlignment = VerticalAlignment.Center, Margin = new Thickness(0, 0, 4, 0) };
var tb = new TextBlock { Text = f.Name, VerticalAlignment = VerticalAlignment.Center };
sp.Children.Add(cb);
sp.Children.Add(tb);
fileItem.Header = sp;
item.Items.Add(fileItem);
}
}
catch
{
// ignore access exceptions
}
return item;
}
private void COPY_SELECTED_TO_ARCHIVE(object sender, RoutedEventArgs e)
{
var archivePath = ArchiveTextBox.Text?.Trim();
if (string.IsNullOrWhiteSpace(archivePath) || !Directory.Exists(archivePath))
{
MessageBox.Show(this, "Please select a valid Archive directory first.", "Invalid Archive", MessageBoxButton.OK, MessageBoxImage.Warning);
return;
}
var sourceRoot = SourceTextBox.Text?.Trim();
if (string.IsNullOrWhiteSpace(sourceRoot) || !Directory.Exists(sourceRoot))
{
MessageBox.Show(this, "Please select a valid Source directory first.", "Invalid Source", MessageBoxButton.OK, MessageBoxImage.Warning);
return;
}
var files = new List<string>();
foreach (var it in CopyTreeView.Items)
{
if (it is TreeViewItem tvi)
CollectCheckedFiles(tvi, files);
}
if (files.Count == 0)
{
MessageBox.Show(this, "No files selected.", "Nothing to copy", MessageBoxButton.OK, MessageBoxImage.Information);
return;
}
int copied = 0, failed = 0;
foreach (var src in files)
{
try
{
var fileDir = System.IO.Path.GetDirectoryName(src) ?? string.Empty;
string relative;
if (fileDir.StartsWith(sourceRoot, StringComparison.OrdinalIgnoreCase))
relative = fileDir.Substring(sourceRoot.Length).TrimStart(System.IO.Path.DirectorySeparatorChar, System.IO.Path.AltDirectorySeparatorChar);
else
relative = System.IO.Path.GetFileName(fileDir);
var destDir = System.IO.Path.Combine(archivePath, relative);
Directory.CreateDirectory(destDir);
var destFile = System.IO.Path.Combine(destDir, System.IO.Path.GetFileName(src));
File.Copy(src, destFile, true);
copied++;
}
catch
{
failed++;
}
}
MessageBox.Show(this, $"Copy finished. Success: {copied}, Failed: {failed}", "Copy completed", MessageBoxButton.OK, MessageBoxImage.Information);
// no-op edit to preserve patch continuity
}
private void CollectCheckedFiles(TreeViewItem node, List<string> list)
{
if (node.Header is StackPanel sp)
{
foreach (var child in sp.Children)
{
if (child is CheckBox cb && cb.IsChecked == true)
{
if (node.Tag is string path && File.Exists(path))
{
list.Add(path);
}
break;
}
}
}
foreach (var it in node.Items)
{
if (it is TreeViewItem t)
CollectCheckedFiles(t, list);
}
}
} }
#endregion (VERIFY_SIZES) #endregion (VERIFY_SIZES)
public class FileEntry : System.ComponentModel.INotifyPropertyChanged public class FileEntry : System.ComponentModel.INotifyPropertyChanged
+51
View File
@@ -0,0 +1,51 @@
using System;
using System.IO;
using System.Windows.Controls;
using System.Windows;
namespace TapeCatalog.Methods
{
// Reusable helper methods for copy-related UI functionality
public static class CopyMethods
{
public static void PopulateTree(TreeView tree, string path)
{
if (tree == null) return;
tree.Items.Clear();
if (string.IsNullOrWhiteSpace(path) || !Directory.Exists(path)) return;
var rootDir = new DirectoryInfo(path);
var rootItem = CreateDirectoryNode(rootDir);
rootItem.IsExpanded = true;
tree.Items.Add(rootItem);
}
public static TreeViewItem CreateDirectoryNode(DirectoryInfo dir)
{
var item = new TreeViewItem { Header = dir.Name, Tag = dir.FullName };
try
{
foreach (var sub in dir.GetDirectories())
{
item.Items.Add(CreateDirectoryNode(sub));
}
foreach (var f in dir.GetFiles())
{
var fileItem = new TreeViewItem { Tag = f.FullName };
var sp = new StackPanel { Orientation = Orientation.Horizontal };
var cb = new CheckBox { VerticalAlignment = VerticalAlignment.Center, Margin = new Thickness(0, 0, 4, 0) };
var tb = new TextBlock { Text = f.Name, VerticalAlignment = VerticalAlignment.Center };
sp.Children.Add(cb);
sp.Children.Add(tb);
fileItem.Header = sp;
item.Items.Add(fileItem);
}
}
catch
{
// ignore access exceptions
}
return item;
}
}
}
+184
View File
@@ -0,0 +1,184 @@
using System;
using System.IO;
using System.Xml.Serialization;
using System.Linq;
using System.Collections.ObjectModel;
namespace TapeCatalog.Methods
{
/// <summary>
/// Simple settings container and load/save helpers.
/// </summary>
[Serializable]
public class SettingsData
{
public string SourceDirectory { get; set; }
public string ArchiveDirectory { get; set; }
public string XmlDirectory { get; set; }
public static SettingsData Default => new SettingsData
{
SourceDirectory = string.Empty,
ArchiveDirectory = string.Empty,
XmlDirectory = string.Empty
};
}
public static class SettingsManager
{
private const string DefaultFileName = "appsettings.xml";
public static void Save(SettingsData data, string filePath = null)
{
if (data == null) throw new ArgumentNullException(nameof(data));
var path = string.IsNullOrWhiteSpace(filePath) ? DefaultFileName : filePath;
try
{
using (var fs = File.Create(path))
{
var ser = new XmlSerializer(typeof(SettingsData));
ser.Serialize(fs, data);
}
}
catch
{
// swallow errors; callers can handle existence/permission issues
}
}
public static SettingsData Load(string filePath = null)
{
var path = string.IsNullOrWhiteSpace(filePath) ? DefaultFileName : filePath;
if (!File.Exists(path)) return SettingsData.Default;
try
{
using (var fs = File.OpenRead(path))
{
var ser = new XmlSerializer(typeof(SettingsData));
return ser.Deserialize(fs) as SettingsData ?? SettingsData.Default;
}
}
catch
{
return SettingsData.Default;
}
}
}
// UI-related helpers used by the Settings tab. These methods avoid direct UI manipulation
// and return data so the MainWindow can update controls.
public static class SettingsUi
{
public static string SelectDirectory(string description)
{
using (var dlg = new System.Windows.Forms.FolderBrowserDialog())
{
dlg.Description = description ?? "Select a folder";
dlg.ShowNewFolderButton = true;
var result = dlg.ShowDialog();
if (result == System.Windows.Forms.DialogResult.OK && !string.IsNullOrWhiteSpace(dlg.SelectedPath))
return dlg.SelectedPath;
}
return null;
}
public static int ScanSource(string path, ObservableCollection<TapeCatalog.FileEntry> files)
{
if (string.IsNullOrWhiteSpace(path)) throw new ArgumentException("path");
if (!Directory.Exists(path)) throw new DirectoryNotFoundException(path);
foreach (var file in Directory.EnumerateFiles(path, "*.*", SearchOption.AllDirectories))
{
try
{
var fi = new FileInfo(file);
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)
{
existing.SizeSrc = newSizeMb;
existing.ModSrc = fi.LastWriteTime.ToString();
existing.Status = string.Empty;
existing.ArcDrive = string.Empty;
}
}
else
{
files.Add(new TapeCatalog.FileEntry
{
Filename = fi.Name,
FileDir = fi.DirectoryName,
SizeSrc = FormatBytesToMb(fi.Length),
ModSrc = fi.LastWriteTime.ToString(),
});
}
}
catch
{
// ignore inaccessible files
}
}
return files.Count;
}
public static int ScanArchive(string path, ObservableCollection<TapeCatalog.FileEntry> files)
{
if (string.IsNullOrWhiteSpace(path)) throw new ArgumentException("path");
if (!Directory.Exists(path)) throw new DirectoryNotFoundException(path);
foreach (var file in Directory.EnumerateFiles(path, "*.*", SearchOption.AllDirectories))
{
try
{
var fi = new FileInfo(file);
var existing = files.FirstOrDefault(f => string.Equals(f.Filename, fi.Name, StringComparison.Ordinal));
if (existing != null)
{
existing.SizeArc = FormatBytesToMb(fi.Length);
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 TapeCatalog.FileEntry
{
Filename = fi.Name,
FileDir = fi.DirectoryName,
SizeArc = FormatBytesToMb(fi.Length),
ModArc = fi.LastWriteTime.ToString()
});
}
}
catch
{
// ignore inaccessible files
}
}
return files.Count;
}
public static string[] GetXmlFiles(string xmlPath)
{
if (string.IsNullOrWhiteSpace(xmlPath) || !Directory.Exists(xmlPath)) return new string[0];
try
{
return Directory.GetFiles(xmlPath, "*.xml", SearchOption.TopDirectoryOnly);
}
catch
{
return new string[0];
}
}
private static string FormatBytesToMb(long bytes)
{
double mb = bytes / 1024.0 / 1024.0;
return mb.ToString("F2");
}
}
}
+2
View File
@@ -56,6 +56,8 @@
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType> <SubType>Designer</SubType>
</ApplicationDefinition> </ApplicationDefinition>
<Compile Include="Methods\CopyMethods.cs" />
<Compile Include="Methods\Settings.cs" />
<Compile Include="XML_Methods.cs" /> <Compile Include="XML_Methods.cs" />
<Page Include="MainWindow.xaml"> <Page Include="MainWindow.xaml">
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>