Add project files.
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.31702.278
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WPFLinkTool", "WPFLinkTool\WPFLinkTool.csproj", "{F7A5BA83-57A5-40C8-87AB-14DC84AE7529}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{F7A5BA83-57A5-40C8-87AB-14DC84AE7529}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{F7A5BA83-57A5-40C8-87AB-14DC84AE7529}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F7A5BA83-57A5-40C8-87AB-14DC84AE7529}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F7A5BA83-57A5-40C8-87AB-14DC84AE7529}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {C28B0299-C112-4902-83AA-89612681ECEC}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -0,0 +1,36 @@
|
||||
<Window x:Class="WPFLinkTool.AddInstanceDialog"
|
||||
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:WPFLinkTool"
|
||||
mc:Ignorable="d"
|
||||
Title="AddInstanceDialog" Height="250" Width="500"
|
||||
WindowStartupLocation="CenterOwner">
|
||||
<Border Margin="10">
|
||||
<StackPanel>
|
||||
<TextBlock IsEnabled="{Binding ElementName=CreateButton, Path=IsEnabled}" Text="Instance name"></TextBlock>
|
||||
<TextBox Text="{Binding InstanceName}"></TextBox>
|
||||
<TextBlock IsEnabled="{Binding ElementName=CreateButton, Path=IsEnabled}" Text="Data source path"></TextBlock>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition></ColumnDefinition>
|
||||
<ColumnDefinition Width="100"></ColumnDefinition>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBox Text="{Binding DataSource , Mode=TwoWay}"></TextBox>
|
||||
<Button IsEnabled="{Binding ElementName=CreateButton, Path=IsEnabled}" Command="{Binding OpenBrowseDialog}" Grid.Column="1" Content="Browse..."></Button>
|
||||
</Grid>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition></ColumnDefinition>
|
||||
<ColumnDefinition></ColumnDefinition>
|
||||
</Grid.ColumnDefinitions>
|
||||
<RadioButton IsEnabled="{Binding ElementName=CreateButton, Path=IsEnabled}" Content="Move files" Grid.Column="0"></RadioButton>
|
||||
<RadioButton IsEnabled="{Binding ElementName=CreateButton, Path=IsEnabled}" Content="Copy files" Grid.Column="1" IsChecked="{Binding IsCopyChecked}"></RadioButton>
|
||||
</Grid>
|
||||
<Button x:Name="CreateButton" Command="{Binding AddInstanceCommand}">Create instance</Button>
|
||||
<TextBlock Text="{Binding StatusText}"></TextBlock>
|
||||
<ProgressBar Height="30" Value="{Binding Progress}"></ProgressBar>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</Window>
|
||||
@@ -0,0 +1,28 @@
|
||||
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.Shapes;
|
||||
|
||||
namespace WPFLinkTool
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for AddInstanceDialog.xaml
|
||||
/// </summary>
|
||||
public partial class AddInstanceDialog : Window
|
||||
{
|
||||
public AddInstanceDialog(SharedViewModel shared)
|
||||
{
|
||||
InitializeComponent();
|
||||
DataContext = new AddInstanceViewModel(shared);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<Application x:Class="WPFLinkTool.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="clr-namespace:WPFLinkTool"
|
||||
StartupUri="MainWindow.xaml">
|
||||
<Application.Resources>
|
||||
|
||||
</Application.Resources>
|
||||
</Application>
|
||||
@@ -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 WPFLinkTool
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for App.xaml
|
||||
/// </summary>
|
||||
public partial class App : Application
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
using System.Windows;
|
||||
|
||||
[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)
|
||||
)]
|
||||
@@ -0,0 +1,65 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.Serialization.Formatters.Binary;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Xml;
|
||||
using System.Xml.Schema;
|
||||
using System.Xml.Serialization;
|
||||
using static WPFLinkTool.Global;
|
||||
|
||||
namespace WPFLinkTool
|
||||
{
|
||||
public class DBInfo
|
||||
{
|
||||
public List<DataInstance> InstanceList { get; set; }
|
||||
public int InstanceCount => InstanceList.Count;
|
||||
public long DBSize
|
||||
{
|
||||
get
|
||||
{
|
||||
long size = 0;
|
||||
DirectoryInfo dir = new(dbDir);
|
||||
foreach (var file in dir.GetFiles())
|
||||
{
|
||||
size += file.Length;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
}
|
||||
|
||||
public long IndividualSize
|
||||
{
|
||||
get
|
||||
{
|
||||
long size = 0;
|
||||
foreach (DataInstance instance in InstanceList)
|
||||
{
|
||||
size += instance.Size;
|
||||
}
|
||||
return size;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public DBInfo()
|
||||
{
|
||||
if (InstanceList == null)
|
||||
InstanceList = new();
|
||||
}
|
||||
|
||||
public void SaveToXml()
|
||||
{
|
||||
XmlSerializer serializer = new(typeof(DBInfo));
|
||||
TextWriter writer = new StreamWriter(DBInfoPath);
|
||||
serializer.Serialize(writer, this);
|
||||
}
|
||||
public void UpdateDBSize()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using static WPFLinkTool.Global;
|
||||
|
||||
namespace WPFLinkTool
|
||||
{
|
||||
public class DataInstance
|
||||
{
|
||||
public string InstanceName { get; set; }
|
||||
public List<InstanceFileInfo> InstanceFiles { get; set; }
|
||||
public int Entries { get; set; }
|
||||
public long Size { get; set; }
|
||||
public string PrettySize => ReadableSize(Size);
|
||||
|
||||
public DataInstance(string name, List<InstanceFileInfo> fileInfos, int entries, long size)
|
||||
{
|
||||
InstanceName = name;
|
||||
InstanceFiles = fileInfos;
|
||||
Entries = entries;
|
||||
Size = size;
|
||||
}
|
||||
|
||||
public DataInstance()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using static WPFLinkTool.Global;
|
||||
|
||||
namespace WPFLinkTool
|
||||
{
|
||||
public class InstanceFileInfo
|
||||
{
|
||||
public string OriginalFileName { get; set; }
|
||||
public string HashedFileName { get; set; }
|
||||
public string Location { get; set; }
|
||||
public long SizeBytes { get; set; }
|
||||
public string PrettySize => ReadableSize(SizeBytes);
|
||||
|
||||
public InstanceFileInfo(string orgFName, string hashFName, string loc, long size)
|
||||
{
|
||||
OriginalFileName = orgFName;
|
||||
HashedFileName = hashFName;
|
||||
Location = loc;
|
||||
SizeBytes = size;
|
||||
}
|
||||
public InstanceFileInfo()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace WPFLinkTool
|
||||
{
|
||||
public class Global
|
||||
{
|
||||
//public static string curDir = @"F:\Torrents\bemaniso.ws\HardLinkUI";
|
||||
//public static string curDir = @"C:\Tools and stuff\WPFLinkTool";
|
||||
public static string curDir = Directory.GetCurrentDirectory();
|
||||
public static string dbDir = curDir + @"\DB";
|
||||
public static string xmlDir = curDir + @"\XML";
|
||||
public static string DBInfoPath = curDir + @"\DBInfo.xml";
|
||||
|
||||
public static string ReadableSize(long size)
|
||||
{
|
||||
if (size < 1024f)
|
||||
{
|
||||
return $"{size} B";
|
||||
}
|
||||
if (size > 1024f && size < 1048576f)
|
||||
{
|
||||
return $"{Math.Round(size / 1024f, 2)} KB";
|
||||
}
|
||||
if (size > 1048576f && size < 1073741824f)
|
||||
{
|
||||
return $"{Math.Round(size / 1048576f, 2)} MB";
|
||||
}
|
||||
if (size > 1073741824f)
|
||||
{
|
||||
return $"{Math.Round(size / 1073741824f, 2)} GB";
|
||||
}
|
||||
else return "what?";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
<Window x:Class="WPFLinkTool.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:WPFLinkTool"
|
||||
mc:Ignorable="d"
|
||||
Title="MainWindow" Height="450" Width="500">
|
||||
<Border Padding="10">
|
||||
<StackPanel>
|
||||
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition></ColumnDefinition>
|
||||
<ColumnDefinition></ColumnDefinition>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Column="0" Text="{Binding DBSize, StringFormat=DB size: {0}}"></TextBlock>
|
||||
<TextBlock Grid.Column="1" Text="{Binding SumSize, StringFormat=Instance size sum: {0}}"></TextBlock>
|
||||
</Grid>
|
||||
|
||||
<Grid Margin="0 2 0 2">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition></ColumnDefinition>
|
||||
<ColumnDefinition></ColumnDefinition>
|
||||
<ColumnDefinition></ColumnDefinition>
|
||||
<ColumnDefinition></ColumnDefinition>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Button Command="{Binding OpenAddInstanceWindow}" Grid.Column="0">Add instance</Button>
|
||||
<Button IsEnabled="{Binding LinkButtonEnabled}" Command="{Binding MakeHardLinksCommand}" Grid.Column="1">Make hard links</Button>
|
||||
<Button IsEnabled="{Binding UIEnabled}" Command="{Binding DeleteUnusedCommand}" Grid.Column="2">Delete unused files</Button>
|
||||
<Button IsEnabled="{Binding LinkButtonEnabled}" Command="{Binding DeleteInstanceCommand}" Grid.Column="3">Delete instance</Button>
|
||||
</Grid>
|
||||
|
||||
<DockPanel MaxHeight="300" Margin="0 2 0 2">
|
||||
<ScrollViewer VerticalScrollBarVisibility="Visible">
|
||||
<ItemsControl ItemsSource="{Binding InstanceList}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="16"></ColumnDefinition>
|
||||
<ColumnDefinition></ColumnDefinition>
|
||||
<ColumnDefinition></ColumnDefinition>
|
||||
<ColumnDefinition></ColumnDefinition>
|
||||
|
||||
</Grid.ColumnDefinitions>
|
||||
<RadioButton x:Name="RdioBtn" Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=Window}, Path=DataContext.SelectedInstanceCommand}" CommandParameter="{Binding ElementName=InstName, Path=Text}" Grid.Column="0" GroupName="InstanceGroup"></RadioButton>
|
||||
<TextBlock HorizontalAlignment="Center" x:Name="InstName" Grid.Column="1" Text="{Binding InstanceName}"></TextBlock>
|
||||
<TextBlock HorizontalAlignment="Center" Grid.Column="2" Text="{Binding Entries, StringFormat={}{0} files}"></TextBlock>
|
||||
<TextBlock HorizontalAlignment="Center" Grid.Column="3" Text="{Binding PrettySize}"></TextBlock>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</ScrollViewer>
|
||||
</DockPanel>
|
||||
|
||||
<ProgressBar Margin="0 2 0 2" Value="{Binding Progress}" MinHeight="20"></ProgressBar>
|
||||
|
||||
<TextBlock Text="{Binding HeaderText}"></TextBlock>
|
||||
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</Window>
|
||||
@@ -0,0 +1,32 @@
|
||||
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.Forms;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
using WPFLinkTool;
|
||||
|
||||
namespace WPFLinkTool
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for MainWindow.xaml
|
||||
/// </summary>
|
||||
public partial class MainWindow : Window
|
||||
{
|
||||
public MainWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
SharedViewModel shared = new();
|
||||
DataContext = new MainWindowViewModel(shared);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace WPFLinkTool
|
||||
{
|
||||
public class AddInstanceViewModel : ViewModelBase
|
||||
{
|
||||
public readonly SharedViewModel Shared;
|
||||
public bool IsCopyChecked { get; set; } = true;
|
||||
private double _progress;
|
||||
public double Progress
|
||||
{
|
||||
get
|
||||
{
|
||||
return _progress;
|
||||
}
|
||||
set
|
||||
{
|
||||
_progress = value;
|
||||
OnPropertyChanged(nameof(Progress));
|
||||
}
|
||||
}
|
||||
public string InstanceName { get; set; }
|
||||
public string DataSource { get; set; }
|
||||
private string _statusText;
|
||||
public string StatusText
|
||||
{
|
||||
get
|
||||
{
|
||||
return _statusText;
|
||||
}
|
||||
set
|
||||
{
|
||||
_statusText = value;
|
||||
OnPropertyChanged(nameof(StatusText));
|
||||
}
|
||||
}
|
||||
|
||||
public RelayCommand CalculateFilesCommand { get; private set; }
|
||||
public RelayCommand OpenBrowseDialog { get; private set; }
|
||||
public ICommand AddInstanceCommand { get; }
|
||||
|
||||
public AddInstanceViewModel(SharedViewModel shared)
|
||||
{
|
||||
Shared = shared;
|
||||
InstanceName = "";
|
||||
DataSource = "";
|
||||
OpenBrowseDialog = new(OpenFolderBrowser);
|
||||
AddInstanceCommand = new AddInstanceCommand(this, (ex) => StatusText = ex.Message);
|
||||
}
|
||||
|
||||
public void OpenFolderBrowser(object i)
|
||||
{
|
||||
FolderBrowserDialog dialog = new();
|
||||
dialog.ShowDialog();
|
||||
DataSource = dialog.SelectedPath;
|
||||
OnPropertyChanged("DataSource");
|
||||
dialog.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using static WPFLinkTool.Global;
|
||||
|
||||
namespace WPFLinkTool
|
||||
{
|
||||
public class AddInstanceCommand : AsyncCommandBase
|
||||
{
|
||||
private readonly AddInstanceViewModel vm;
|
||||
|
||||
public AddInstanceCommand(AddInstanceViewModel addInstanceViewModel, Action<Exception> onException) : base(onException)
|
||||
{
|
||||
vm = addInstanceViewModel;
|
||||
}
|
||||
|
||||
protected override async Task ExecuteAsync(object parameter)
|
||||
{
|
||||
if (vm.InstanceName != string.Empty && vm.DataSource != string.Empty && IsUniqueName(vm.InstanceName))
|
||||
{
|
||||
IsExecuting = true;
|
||||
vm.Progress = 0;
|
||||
|
||||
// Create a list of InstanceFileInfos
|
||||
List<InstanceFileInfo> fileInfos = new();
|
||||
long instanceSize = 0;
|
||||
|
||||
// Enumerate files
|
||||
vm.StatusText = "Enumerating files...";
|
||||
DirectoryInfo dir = new(vm.DataSource);
|
||||
var files = dir.EnumerateFiles("", SearchOption.AllDirectories);
|
||||
int already = 0;
|
||||
|
||||
// Calculate progress step
|
||||
double progressStep = 100D / files.Count();
|
||||
|
||||
foreach (var file in files)
|
||||
{
|
||||
// Hash and make file infos
|
||||
vm.StatusText = $"Hashing {file.FullName}";
|
||||
string hash = await Task.Run(() => GetMD5Checksum(file.FullName));
|
||||
InstanceFileInfo inFile = new(file.Name, hash + file.Extension,
|
||||
file.DirectoryName.Replace(vm.DataSource, ""), file.Length);
|
||||
instanceSize += file.Length;
|
||||
|
||||
// Update the progress bar
|
||||
vm.Progress += progressStep;
|
||||
|
||||
// Add file info to the list
|
||||
fileInfos.Add(inFile);
|
||||
|
||||
// Check if file exists and move/copy
|
||||
if (!File.Exists(dbDir + @"\" + hash + file.Extension))
|
||||
{
|
||||
if (vm.IsCopyChecked)
|
||||
{
|
||||
vm.StatusText = $"Copying {file.Name}";
|
||||
await Task.Run(() => File.Copy(file.FullName, dbDir + @"\" + hash + file.Extension));
|
||||
}
|
||||
else
|
||||
{
|
||||
vm.StatusText = $"Moving {file.Name}";
|
||||
await Task.Run(() => File.Move(file.FullName, dbDir + @"\" + hash + file.Extension));
|
||||
}
|
||||
}
|
||||
else already++;
|
||||
}
|
||||
|
||||
// Create a DataInstance and add to DBInfo
|
||||
DataInstance instance = new(vm.InstanceName, fileInfos, fileInfos.Count, instanceSize);
|
||||
vm.Shared.Info.InstanceList.Add(instance);
|
||||
vm.Shared.Info.SaveToXml();
|
||||
|
||||
vm.StatusText = $"Done. {instance.InstanceName} added with {instance.Entries} files and a size of {instance.PrettySize} \n" +
|
||||
$"{already} files already exist.";
|
||||
IsExecuting = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBox.Show("Empty fields or duplicate instance name.", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
public string GetMD5Checksum(string filename)
|
||||
{
|
||||
using var md5 = MD5.Create();
|
||||
using var stream = File.OpenRead(filename);
|
||||
var hash = md5.ComputeHash(stream);
|
||||
return BitConverter.ToString(hash).Replace("-", "");
|
||||
}
|
||||
public bool IsUniqueName(string instancename)
|
||||
{
|
||||
List<string> names = new();
|
||||
List<string> namesAdd = new();
|
||||
foreach (var instance in vm.Shared.Info.InstanceList)
|
||||
{
|
||||
names.Add(instance.InstanceName);
|
||||
}
|
||||
namesAdd = names;
|
||||
namesAdd.Add(instancename);
|
||||
if (names.Count == namesAdd.Distinct().Count())
|
||||
return true;
|
||||
else return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace WPFLinkTool
|
||||
{
|
||||
public abstract class AsyncCommandBase : ICommand
|
||||
{
|
||||
private readonly Action<Exception> _onException;
|
||||
|
||||
private bool _isExecuting;
|
||||
public bool IsExecuting
|
||||
{
|
||||
get
|
||||
{
|
||||
return _isExecuting;
|
||||
}
|
||||
set
|
||||
{
|
||||
_isExecuting = value;
|
||||
CanExecuteChanged?.Invoke(this, new EventArgs());
|
||||
}
|
||||
}
|
||||
|
||||
public event EventHandler CanExecuteChanged;
|
||||
|
||||
public AsyncCommandBase(Action<Exception> onException)
|
||||
{
|
||||
_onException = onException;
|
||||
}
|
||||
|
||||
public bool CanExecute(object parameter)
|
||||
{
|
||||
return !IsExecuting;
|
||||
}
|
||||
|
||||
public async void Execute(object parameter)
|
||||
{
|
||||
IsExecuting = true;
|
||||
|
||||
try
|
||||
{
|
||||
await ExecuteAsync(parameter);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_onException?.Invoke(ex);
|
||||
}
|
||||
|
||||
IsExecuting = false;
|
||||
}
|
||||
|
||||
protected abstract Task ExecuteAsync(object parameter);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using static WPFLinkTool.Global;
|
||||
|
||||
namespace WPFLinkTool
|
||||
{
|
||||
class DeleteUnusedCommand : AsyncCommandBase
|
||||
{
|
||||
private readonly MainWindowViewModel vm;
|
||||
|
||||
public DeleteUnusedCommand(MainWindowViewModel viewModel, Action<Exception> onException) : base(onException)
|
||||
{
|
||||
vm = viewModel;
|
||||
}
|
||||
|
||||
protected override async Task ExecuteAsync(object parameter)
|
||||
{
|
||||
IsExecuting = true;
|
||||
vm.UIEnabled = false;
|
||||
|
||||
HashSet<string> dBFiles = new();
|
||||
DirectoryInfo dir = new(dbDir);
|
||||
foreach (var file in dir.GetFiles())
|
||||
{
|
||||
dBFiles.Add(file.Name);
|
||||
}
|
||||
|
||||
foreach (var instance in vm.Shared.Info.InstanceList)
|
||||
{
|
||||
foreach (var file in instance.InstanceFiles)
|
||||
{
|
||||
dBFiles.Remove(file.HashedFileName);
|
||||
}
|
||||
}
|
||||
|
||||
double progressStep = 100D / dBFiles.Count;
|
||||
|
||||
var prompt = MessageBox.Show($"Found {dBFiles.Count} unused files. Delete?", "Delete unused files",
|
||||
MessageBoxButtons.YesNo, MessageBoxIcon.Question);
|
||||
if (prompt == DialogResult.Yes)
|
||||
{
|
||||
foreach (var file in dBFiles)
|
||||
{
|
||||
await Task.Run(() => File.Delete(dbDir + @"\" + file));
|
||||
vm.Progress += progressStep;
|
||||
}
|
||||
}
|
||||
|
||||
await Task.Run(() => vm.UpdateDBSize());
|
||||
|
||||
vm.Progress = 0;
|
||||
vm.UIEnabled = true;
|
||||
IsExecuting = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using static WPFLinkTool.Global;
|
||||
|
||||
namespace WPFLinkTool
|
||||
{
|
||||
public class MakeHardLinksCommand : AsyncCommandBase
|
||||
{
|
||||
private readonly MainWindowViewModel vm;
|
||||
|
||||
public MakeHardLinksCommand(MainWindowViewModel viewmodel, Action<Exception> onException) : base(onException)
|
||||
{
|
||||
vm = viewmodel;
|
||||
}
|
||||
|
||||
protected override async Task ExecuteAsync(object parameter)
|
||||
{
|
||||
IsExecuting = true;
|
||||
vm.UIEnabled = false;
|
||||
string selInst = vm.SelectedInstance;
|
||||
var selectedInstance = (from inst in vm.Shared.Info.InstanceList
|
||||
where inst.InstanceName == selInst
|
||||
select inst).Single();
|
||||
|
||||
|
||||
|
||||
var prompt = MessageBox.Show($"Creating hard links from {selectedInstance.InstanceName}.\nProceed to link target selection?", "Make hard links", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
|
||||
if (prompt == DialogResult.Yes)
|
||||
{
|
||||
FolderBrowserDialog dialog = new();
|
||||
if (dialog.ShowDialog() == DialogResult.OK)
|
||||
{
|
||||
string target = dialog.SelectedPath;
|
||||
|
||||
var uniqueFolders = (from folders in selectedInstance.InstanceFiles
|
||||
where folders.Location != string.Empty
|
||||
select folders.Location).Distinct();
|
||||
|
||||
double progressStep = 100D / (selectedInstance.InstanceFiles.Count + uniqueFolders.Count());
|
||||
|
||||
foreach (var folder in uniqueFolders)
|
||||
{
|
||||
if (!Directory.Exists(target + folder))
|
||||
{
|
||||
await Task.Run(() => Directory.CreateDirectory(target + folder));
|
||||
}
|
||||
vm.Progress += progressStep;
|
||||
}
|
||||
|
||||
foreach (var file in selectedInstance.InstanceFiles)
|
||||
{
|
||||
if (!File.Exists(target + file.Location + @"\" + file.OriginalFileName))
|
||||
{
|
||||
await Task.Run(() => CreateHardLink(target + file.Location + @"\" + file.OriginalFileName,
|
||||
dbDir + @"\" + file.HashedFileName, IntPtr.Zero));
|
||||
}
|
||||
vm.Progress += progressStep;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vm.Progress = 0;
|
||||
|
||||
|
||||
|
||||
vm.UIEnabled = true;
|
||||
IsExecuting = false;
|
||||
}
|
||||
|
||||
[DllImport("Kernel32.dll", CharSet = CharSet.Unicode)]
|
||||
static extern bool CreateHardLink(
|
||||
string lpFileName,
|
||||
string lpExistingFileName,
|
||||
IntPtr lpSecurityAttributes
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace WPFLinkTool
|
||||
{
|
||||
public class RelayCommand : ICommand
|
||||
{
|
||||
readonly Action<object> _execute;
|
||||
readonly Predicate<object> _canExecute;
|
||||
|
||||
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
|
||||
{
|
||||
if (execute == null)
|
||||
throw new NullReferenceException("execute");
|
||||
_execute = execute;
|
||||
_canExecute = canExecute;
|
||||
}
|
||||
|
||||
public RelayCommand(Action<object> execute) : this(execute, null)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public event EventHandler CanExecuteChanged
|
||||
{
|
||||
add { CommandManager.RequerySuggested += value; }
|
||||
remove { CommandManager.RequerySuggested -= value; }
|
||||
}
|
||||
|
||||
public bool CanExecute(object parameter)
|
||||
{
|
||||
return _canExecute == null ? true : _canExecute(parameter);
|
||||
}
|
||||
|
||||
public void Execute(object parameter)
|
||||
{
|
||||
_execute.Invoke(parameter);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,212 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Data;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using System.Windows.Input;
|
||||
using System.Xml;
|
||||
using System.Xml.Serialization;
|
||||
using static WPFLinkTool.Global;
|
||||
|
||||
namespace WPFLinkTool
|
||||
{
|
||||
public class MainWindowViewModel : ViewModelBase
|
||||
{
|
||||
public readonly SharedViewModel Shared;
|
||||
private string _dBSize;
|
||||
public string DBSize
|
||||
{
|
||||
get
|
||||
{
|
||||
return _dBSize;
|
||||
}
|
||||
set
|
||||
{
|
||||
_dBSize = value;
|
||||
OnPropertyChanged(nameof(DBSize));
|
||||
}
|
||||
}
|
||||
private string _sumSize;
|
||||
public string SumSize
|
||||
{
|
||||
get
|
||||
{
|
||||
return _sumSize;
|
||||
}
|
||||
set
|
||||
{
|
||||
_sumSize = value;
|
||||
OnPropertyChanged(nameof(SumSize));
|
||||
}
|
||||
}
|
||||
private bool _linkButtonEnabled = false;
|
||||
public bool LinkButtonEnabled
|
||||
{
|
||||
get
|
||||
{
|
||||
return _linkButtonEnabled;
|
||||
}
|
||||
set
|
||||
{
|
||||
_linkButtonEnabled = value;
|
||||
OnPropertyChanged(nameof(LinkButtonEnabled));
|
||||
}
|
||||
}
|
||||
private bool _uIEnabled = true;
|
||||
public bool UIEnabled
|
||||
{
|
||||
get
|
||||
{
|
||||
return _uIEnabled;
|
||||
}
|
||||
set
|
||||
{
|
||||
_uIEnabled = value;
|
||||
OnPropertyChanged(nameof(UIEnabled));
|
||||
}
|
||||
}
|
||||
private double _progress;
|
||||
public double Progress
|
||||
{
|
||||
get
|
||||
{
|
||||
return _progress;
|
||||
}
|
||||
set
|
||||
{
|
||||
_progress = value;
|
||||
OnPropertyChanged(nameof(Progress));
|
||||
}
|
||||
}
|
||||
private string _headerText;
|
||||
public string HeaderText
|
||||
{
|
||||
get
|
||||
{
|
||||
return _headerText;
|
||||
}
|
||||
set
|
||||
{
|
||||
_headerText = value;
|
||||
OnPropertyChanged(nameof(HeaderText));
|
||||
}
|
||||
}
|
||||
private string _selectedInstance;
|
||||
public string SelectedInstance
|
||||
{
|
||||
get
|
||||
{
|
||||
return _selectedInstance;
|
||||
}
|
||||
set
|
||||
{
|
||||
_selectedInstance = value;
|
||||
OnPropertyChanged(nameof(SelectedInstance));
|
||||
}
|
||||
}
|
||||
|
||||
public ObservableCollection<DataInstance> InstanceList
|
||||
{
|
||||
get
|
||||
{
|
||||
ObservableCollection<DataInstance> list = new();
|
||||
foreach (var instance in Shared.Info.InstanceList)
|
||||
{
|
||||
list.Add(instance);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
}
|
||||
public RelayCommand OpenAddInstanceWindow { get; private set; }
|
||||
public RelayCommand SelectedInstanceCommand { get; private set; }
|
||||
public RelayCommand DeleteInstanceCommand { get; private set; }
|
||||
public ICommand MakeHardLinksCommand { get; private set; }
|
||||
public ICommand DeleteUnusedCommand { get; private set; }
|
||||
|
||||
public MainWindowViewModel(SharedViewModel shared)
|
||||
{
|
||||
if (!Directory.Exists(dbDir))
|
||||
Directory.CreateDirectory(dbDir);
|
||||
Shared = shared;
|
||||
OpenAddInstanceWindow = new(OpenInstanceWindow);
|
||||
SelectedInstanceCommand = new(SetSelectedRadioButton);
|
||||
DeleteInstanceCommand = new(DeleteInstance);
|
||||
MakeHardLinksCommand = new MakeHardLinksCommand(this, (ex) => HeaderText = ex.Message);
|
||||
DeleteUnusedCommand = new DeleteUnusedCommand(this, (ex) => HeaderText = ex.Message);
|
||||
LoadDBInfo();
|
||||
Task.Run(() => UpdateDBSize());
|
||||
Task.Run(() => UpdateSumSize());
|
||||
}
|
||||
|
||||
private void LoadDBInfo()
|
||||
{
|
||||
if (!File.Exists(DBInfoPath))
|
||||
{
|
||||
Shared.Info = new();
|
||||
}
|
||||
else
|
||||
{
|
||||
XmlSerializer serializer = new(typeof(DBInfo));
|
||||
FileStream fs = new(DBInfoPath, FileMode.Open);
|
||||
XmlReader reader = XmlReader.Create(fs);
|
||||
Shared.Info = (DBInfo)serializer.Deserialize(reader);
|
||||
}
|
||||
}
|
||||
|
||||
public void OpenInstanceWindow(object o)
|
||||
{
|
||||
AddInstanceDialog dialog = new(Shared);
|
||||
dialog.ShowDialog();
|
||||
OnPropertyChanged(nameof(InstanceList));
|
||||
LinkButtonEnabled = false;
|
||||
Task.Run(() => UpdateDBSize());
|
||||
Task.Run(() => UpdateSumSize());
|
||||
}
|
||||
|
||||
public void SetSelectedRadioButton(object o)
|
||||
{
|
||||
SelectedInstance = (string)o;
|
||||
LinkButtonEnabled = true;
|
||||
}
|
||||
|
||||
public void DeleteInstance(object o)
|
||||
{
|
||||
var prompt = MessageBox.Show($"Delete {SelectedInstance}?", "Delete instance", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
|
||||
if (prompt == DialogResult.Yes)
|
||||
{
|
||||
DataInstance selected = (from inst in Shared.Info.InstanceList
|
||||
where inst.InstanceName == SelectedInstance
|
||||
select inst).Single();
|
||||
|
||||
Shared.Info.InstanceList.Remove(selected);
|
||||
Shared.Info.SaveToXml();
|
||||
LinkButtonEnabled = false;
|
||||
UpdateSumSize();
|
||||
OnPropertyChanged(nameof(InstanceList));
|
||||
}
|
||||
}
|
||||
public void UpdateDBSize()
|
||||
{
|
||||
DirectoryInfo dir = new(dbDir);
|
||||
long size = 0;
|
||||
foreach (var file in dir.GetFiles())
|
||||
{
|
||||
size += file.Length;
|
||||
}
|
||||
DBSize = ReadableSize(size);
|
||||
}
|
||||
public void UpdateSumSize()
|
||||
{
|
||||
long size = 0;
|
||||
foreach (var instance in Shared.Info.InstanceList)
|
||||
{
|
||||
size += instance.Size;
|
||||
}
|
||||
SumSize = ReadableSize(size);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace WPFLinkTool
|
||||
{
|
||||
public class SharedViewModel : ViewModelBase
|
||||
{
|
||||
private DBInfo _info;
|
||||
public DBInfo Info
|
||||
{
|
||||
get
|
||||
{
|
||||
return _info;
|
||||
}
|
||||
set
|
||||
{
|
||||
_info = value;
|
||||
OnPropertyChanged(nameof(Info));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Text;
|
||||
|
||||
namespace WPFLinkTool
|
||||
{
|
||||
public class ViewModelBase : INotifyPropertyChanged
|
||||
{
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
protected void OnPropertyChanged(string propertyName = null)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net5.0-windows</TargetFramework>
|
||||
<UseWPF>true</UseWPF>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
Reference in New Issue
Block a user