-Progress Bar implemented

- Copy Speed Limit implemented
This commit is contained in:
Black_Betty committed 2026-10-06 22:56:17 +02:00
1 parent 7550cab2c5
commit c8211fa7ae
2 files changed
+164 -54

No files matched your search

+56 -51
View File
@@ -10,16 +10,12 @@
<TabControl x:Name="MainTabControl">
<TabItem Header="SRC-ARC">
<Grid Background="#FFE5E5E5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="99*"/>
<ColumnDefinition Width="298*"/>
</Grid.ColumnDefinitions>
<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="Export XML" HorizontalAlignment="Right" Height="22" Margin="0,234,10,0" VerticalAlignment="Top" Width="76" Click="EXPORT_LIST" Grid.Column="1"/>
<Button Content="Import XML" HorizontalAlignment="Right" Height="22" Margin="0,256,10,0" VerticalAlignment="Top" Width="76" Click="IMPORT_LIST" Grid.Column="1"/>
<Button Content="-> TapeCat" HorizontalAlignment="Right" Height="22" Margin="0,278,10,0" VerticalAlignment="Top" Width="76" Click="COPY_TO_TAPECATALOG" Grid.Column="1"/>
<ListView x:Name="FilesListView" Margin="10,10,101,10" Grid.ColumnSpan="2">
<Button Content="Clear List" HorizontalAlignment="Right" Height="22" Margin="0,10,10,0" VerticalAlignment="Top" Width="76" Click="CLEAR_FILES" />
<Button Content="Verify Sizes" HorizontalAlignment="Right" Height="22" Margin="0,40,10,0" VerticalAlignment="Top" Width="76" Click="VERIFY_SIZES" />
<Button Content="Export XML" HorizontalAlignment="Right" Height="22" Margin="0,70,10,0" VerticalAlignment="Top" Width="76" Click="EXPORT_LIST" />
<Button Content="Import XML" HorizontalAlignment="Right" Height="22" Margin="0,100,10,0" VerticalAlignment="Top" Width="76" Click="IMPORT_LIST" />
<Button Content="-> TapeCat" HorizontalAlignment="Right" Height="22" Margin="0,130,10,0" VerticalAlignment="Top" Width="76" Click="COPY_TO_TAPECATALOG" />
<ListView x:Name="FilesListView" Margin="10,10,101,10" >
<ListView.View>
<GridView>
<GridViewColumn Header="Filename" DisplayMemberBinding="{Binding Filename}" Width="300"/>
@@ -33,18 +29,14 @@
</GridView>
</ListView.View>
</ListView>
</Grid>
</TabItem>
<TabItem Header="Tapecatalog">
<Grid Background="#FFE5E5E5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="19*"/>
<ColumnDefinition Width="775*"/>
</Grid.ColumnDefinitions>
<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="199,10,0,0" VerticalAlignment="Top" Width="120" Click="REFRESH_MEDIUMS" Grid.Column="1"/>
<Button Content="Copy from SRC-ARC" HorizontalAlignment="Left" Height="22" Margin="324,10,0,0" VerticalAlignment="Top" Width="120" Click="COPY_FROM_SRCARC" Grid.Column="1"/>
<ComboBox x:Name="MediumComboBox" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Width="200" />
<Button Content="Refresh Mediums" HorizontalAlignment="Left" Height="22" Margin="220,10,0,0" VerticalAlignment="Top" Width="120" Click="REFRESH_MEDIUMS" />
<Button Content="Copy from SRC-ARC" HorizontalAlignment="Left" Height="22" Margin="350,10,0,0" VerticalAlignment="Top" Width="120" Click="COPY_FROM_SRCARC" />
<Button Content="Export Medium" HorizontalAlignment="Left" Height="22" Margin="480,10,0,0" VerticalAlignment="Top" Width="120" Click="EXPORT_MEDIUM" />
<ListView x:Name="TapeCatalogListView" Margin="10,44,10,40" Grid.ColumnSpan="2">
<ListView.View>
<GridView>
@@ -54,66 +46,79 @@
</GridView>
</ListView.View>
</ListView>
<Button Content="Export Medium" HorizontalAlignment="Left" Height="22" Margin="449,10,0,0" VerticalAlignment="Top" Width="120" Click="EXPORT_MEDIUM" Grid.Column="1"/>
</Grid>
</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"/>
<StackPanel Orientation="Vertical" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="146,10,0,0" Grid.Column="1" Width="149">
<Button Content="Copy Selected to Archive" HorizontalAlignment="Left" Height="22" Margin="160,10,0,0" VerticalAlignment="Top" Width="180" Click="COPY_SELECTED_TO_ARCHIVE" />
<StackPanel Orientation="Vertical" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="370,10,0,0" Width="210">
<TextBlock Text="Selected files:"/>
<TextBlock x:Name="CopySelectedCountText" Text="0" Margin="0,2,0,6"/>
<TextBlock Text="Selected size (MB):"/>
<TextBlock x:Name="CopySelectedSizeText" Text="0.00" Margin="0,2,0,6"/>
<TextBlock Text="Archive free:"/>
<TextBlock x:Name="ArchiveFreeText" Text="-" Margin="0,2,0,6"/>
<ProgressBar x:Name="CopyProgressBar" Width="200" Height="16" Minimum="0" Maximum="100" Value="0" Margin="0,6,0,0" />
<TextBlock x:Name="CopyProgressText" Text="" Margin="0,2,0,0" />
</StackPanel>
<TreeView x:Name="CopyTreeView" Margin="10,170,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" />
<ListView x:Name="XmlSummaryListView" Margin="4" Grid.Column="1">
<ListBox x:Name="XmlFilesListBox" SelectionChanged="XmlFilesListBox_SelectionChanged" Width="200" Margin="10,10,590,10"/>
<ListView x:Name="XmlSummaryListView" Width="570" Margin="220,10,4,0">
<ListView.View>
<GridView>
<GridViewColumn Header="Filename" DisplayMemberBinding="{Binding Filename}" Width="250" />
<GridViewColumn Header="FileDir" DisplayMemberBinding="{Binding FileDir}" Width="350" />
<GridViewColumn Header="Filename" DisplayMemberBinding="{Binding Filename}" Width="200" />
<GridViewColumn Header="FileDir" DisplayMemberBinding="{Binding FileDir}" Width="300" />
<GridViewColumn Header="Status" DisplayMemberBinding="{Binding Status}" Width="100" />
</GridView>
</ListView.View>
</ListView>
</Grid>
</TabItem>
<TabItem Header="Settings" Height="20" VerticalAlignment="Center" RenderTransformOrigin="0.104,0.652" HorizontalAlignment="Left" Width="55">
<Grid Background="#FFE5E5E5">
<TabItem Header="Settings">
<Grid Background="#FFE5E5E5" Margin="10,0,0,0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0*"/>
<ColumnDefinition Width="96*"/>
<ColumnDefinition Width="301*"/>
<ColumnDefinition Width="150" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Text="Application Settings" FontWeight="Bold" Margin="0,0,0,10" Grid.ColumnSpan="3" />
<TextBox x:Name="SourceTextBox" HorizontalAlignment="Left" Height="20" Margin="0,40,0,0" TextWrapping="Wrap" Text="Source Directory" VerticalAlignment="Top" Width="510" Grid.ColumnSpan="3"/>
<TextBox x:Name="ArchiveTextBox" HorizontalAlignment="Left" Height="20" Margin="0,80,0,0" TextWrapping="Wrap" Text="Archive Directory" VerticalAlignment="Top" Width="510" Grid.ColumnSpan="3"/>
<TextBlock Text="XML Directory:" Height="20" Margin="0,100,101,0" VerticalAlignment="Top" Grid.ColumnSpan="3"/>
<TextBlock Text="Source Directory:" Height="20" Margin="0,20,192,0" VerticalAlignment="Top" Grid.ColumnSpan="3"/>
<TextBlock Text="Archive Directory:" Height="20" Margin="0,60,192,0" VerticalAlignment="Top" Grid.ColumnSpan="3"/>
<TextBox x:Name="XmlDirectoryTextBox" HorizontalAlignment="Left" Height="20" Margin="0,120,0,0" TextWrapping="Wrap" Text="XML Directory" VerticalAlignment="Top" Width="510" Grid.ColumnSpan="3"/>
<Button x:Name="XmlDirectorySelect" Content="Select Directory" HorizontalAlignment="Left" Margin="328,120,0,0" VerticalAlignment="Top" Height="20" Click="SET_XML_DIR" Grid.Column="2"/>
<Button x:Name="SourceSelect" Content="Select Directory" HorizontalAlignment="Left" Margin="328,40,0,0" VerticalAlignment="Top" Height="20" Click="SET_RES_DIR" Grid.Column="2" Width="87"/>
<Button x:Name="ArchiveSelect" Content="Select Directory" HorizontalAlignment="Left" Margin="328,80,0,0" VerticalAlignment="Top" Height="20" Click="SET_ARC_DIR" Grid.Column="2" 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 Text="Application Settings" FontWeight="Bold" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="3" Margin="6" />
<TextBlock Text="Source Directory:" Grid.Row="1" Grid.Column="0" VerticalAlignment="Center" Margin="6" />
<TextBox x:Name="SourceTextBox" Grid.Row="1" Grid.Column="1" Height="22" Margin="6" Text="Source Directory" />
<Button x:Name="SourceSelect" Content="Select Directory" Grid.Row="1" Grid.Column="2" Click="SET_RES_DIR" Margin="6" Height="22" />
<TextBlock Text="Archive Directory:" Grid.Row="2" Grid.Column="0" VerticalAlignment="Center" Margin="6" />
<TextBox x:Name="ArchiveTextBox" Grid.Row="2" Grid.Column="1" Height="22" Margin="6" Text="Archive Directory" />
<Button x:Name="ArchiveSelect" Content="Select Directory" Grid.Row="2" Grid.Column="2" Click="SET_ARC_DIR" Margin="6" Height="22" />
<TextBlock Text="XML Directory:" Grid.Row="3" Grid.Column="0" VerticalAlignment="Center" Margin="6" />
<TextBox x:Name="XmlDirectoryTextBox" Grid.Row="3" Grid.Column="1" Height="22" Margin="6" Text="XML Directory" />
<Button x:Name="XmlDirectorySelect" Content="Select Directory" Grid.Row="3" Grid.Column="2" Click="SET_XML_DIR" Margin="6" Height="22" />
<TextBlock Text="Copy speed (MB/s):" Grid.Row="4" Grid.Column="0" VerticalAlignment="Center" Margin="6" />
<TextBox x:Name="CopySpeedTextBox" Grid.Row="4" Grid.Column="1" Height="22" Margin="6" Text="0" />
<StackPanel Orientation="Horizontal" Grid.Row="5" Grid.Column="1" Margin="6">
<Button Content="Scan Source" Click="SCAN_RES_DIR" Margin="0,0,6,0" Height="24" />
<Button Content="Scan Archive" Click="SCAN_ARC_DIR" Margin="0,0,6,0" Height="24" />
<Button Content="Load XML Files" Click="LOAD_XML_FILES" Height="24" />
</StackPanel>
</Grid>
</TabItem>
+108 -3
View File
@@ -17,6 +17,9 @@ using System.IO;
using XML;
using TapeCatalog.Methods;
using System.Windows.Threading;
using System.Diagnostics;
using System.Threading;
using System.Globalization;
@@ -360,7 +363,7 @@ namespace TapeCatalog
Copy.PopulateTree(CopyTreeView, SourceTextBox.Text);
}
private void COPY_SELECTED_TO_ARCHIVE(object sender, RoutedEventArgs e)
private async void COPY_SELECTED_TO_ARCHIVE(object sender, RoutedEventArgs e)
{
var archivePath = ArchiveTextBox.Text?.Trim();
if (string.IsNullOrWhiteSpace(archivePath) || !Directory.Exists(archivePath))
@@ -376,8 +379,104 @@ namespace TapeCatalog
return;
}
var result = Copy.CopySelectedToArchive(CopyTreeView, sourceRoot, archivePath);
MessageBox.Show(this, $"Copy finished. Success: {result.Item1}, Failed: {result.Item2}", "Copy completed", MessageBoxButton.OK, MessageBoxImage.Information);
var files = Copy.GetCheckedFiles(CopyTreeView);
if (files.Count == 0)
{
MessageBox.Show(this, "No files selected.", "Nothing to copy", MessageBoxButton.OK, MessageBoxImage.Information);
return;
}
// prepare progress UI based on bytes
long totalBytes = 0;
foreach (var f in files)
{
try { totalBytes += new FileInfo(f).Length; } catch { }
}
if (totalBytes <= 0) totalBytes = 1; // avoid zero maximum
CopyProgressBar.Minimum = 0;
CopyProgressBar.Maximum = (double)totalBytes;
CopyProgressBar.Value = 0;
CopyProgressText.Text = "Starting...";
long bytesCopied = 0;
int copied = 0, failed = 0;
// parse speed limit (MB/s) from settings; 0 or invalid = unlimited
double speedMb = 0;
var speedText = CopySpeedTextBox?.Text?.Trim();
if (!string.IsNullOrWhiteSpace(speedText))
double.TryParse(speedText, NumberStyles.Float, CultureInfo.InvariantCulture, out speedMb);
double speedBytesPerSec = speedMb > 0 ? speedMb * 1024.0 * 1024.0 : 0;
await Task.Run(() =>
{
const int bufferSize = 64 * 1024; // 64KB
var buffer = new byte[bufferSize];
for (int i = 0; i < files.Count; i++)
{
var src = files[i];
try
{
var fileDir = System.IO.Path.GetDirectoryName(src) ?? string.Empty;
string relative;
if (!string.IsNullOrWhiteSpace(sourceRoot) && 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));
using (var inStream = new FileStream(src, FileMode.Open, FileAccess.Read, FileShare.Read))
using (var outStream = new FileStream(destFile, FileMode.Create, FileAccess.Write, FileShare.None))
{
long fileBytesCopied = 0;
var sw = Stopwatch.StartNew();
int read;
while ((read = inStream.Read(buffer, 0, buffer.Length)) > 0)
{
outStream.Write(buffer, 0, read);
fileBytesCopied += read;
Interlocked.Add(ref bytesCopied, read);
// throttling per file
if (speedBytesPerSec > 0)
{
double desiredMs = (fileBytesCopied / speedBytesPerSec) * 1000.0;
double actualMs = sw.Elapsed.TotalMilliseconds;
if (desiredMs > actualMs)
{
int sleep = (int)Math.Ceiling(desiredMs - actualMs);
if (sleep > 0) Thread.Sleep(sleep);
}
}
// update UI progress
Dispatcher.Invoke(() =>
{
CopyProgressBar.Value = Math.Min((double)bytesCopied, CopyProgressBar.Maximum);
double pct = (CopyProgressBar.Value / CopyProgressBar.Maximum) * 100.0;
CopyProgressText.Text = $"{FormatBytes(bytesCopied)} / {FormatBytes(totalBytes)} ({pct:F1}%)";
});
}
}
copied++;
}
catch
{
failed++;
}
}
});
CopyProgressText.Text = $"Done. Success: {copied}, Failed: {failed}. {FormatBytes(bytesCopied)} copied.";
MessageBox.Show(this, $"Copy finished. Success: {copied}, Failed: {failed}", "Copy completed", MessageBoxButton.OK, MessageBoxImage.Information);
}
private void UpdateCopySummary()
@@ -435,6 +534,12 @@ namespace TapeCatalog
// swallow periodic update errors; avoid UI disruptions
}
}
private static string FormatBytes(long bytes)
{
double mb = bytes / 1024.0 / 1024.0;
return mb.ToString("F2") + " MB";
}
}
#endregion (VERIFY_SIZES)
public class FileEntry : System.ComponentModel.INotifyPropertyChanged