Skip to content

Commit

Permalink
Folder symlink feature added
Browse files Browse the repository at this point in the history
SymlinkCreator can now creates folder symlinks.
  • Loading branch information
arnobpl committed Apr 19, 2019
1 parent 2f09ae4 commit 06f4179
Show file tree
Hide file tree
Showing 7 changed files with 73 additions and 34 deletions.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,19 @@ SymlinkCreator is a GUI app for creating symbolic links (symlinks), and it is ba
## Use cases
- Suppose, you have a collection of several songs sorted by artists and albums in your PC. You might want a separate collection of your most favorite songs which you will store in your mobile devices. In this scenario, the traditional shortcut option through Windows Explorer right-click context menu is not enough, because you cannot copy the actual file contents by copying the traditional shortcut files (*\*.lnk*). You might consider duplicating the files which you will store in your mobile devices. But it will waste your storage space of your PC. In this case, SymlinkCreator will come in handy. You can easily create the separate collection of songs and transfer them to your mobile devices, without wasting your PC's storage space.

- Suppose, you have a special folder which is linked to your online storage like Google Drive. You might want some specific files to be backed up from other folders. A traditional shortcut file is not helpful here to back up those files. In this scenario, you can use SymlinkCreator for the backup purpose without duplicating those files in the special folder.
- Suppose, you have a special folder which is linked to your online storage like Google Drive. You might want some specific files/folders to be backed up from other folders. A traditional shortcut file is not helpful here to back up those files. In this scenario, you can use SymlinkCreator for the backup purpose without duplicating those files/folders in the special folder.

## What SymlinkCreator does
SymlinkCreator creates *symlinks* which is an NTFS feature. Unlink the traditional shortcut files (*\*.lnk*), symlinks do not have any *file size*. While symlinks may be called advanced shortcut files, but they appear to be real files. Unlike duplicated files, symlinks do not waste your storage space. Currently, SymlinkCreator only works for files, not folders. I might add support for folders in the future.
SymlinkCreator creates *symlinks* which is an NTFS feature. Unlink the traditional shortcut files (*\*.lnk*), symlinks do not have any *file size*. While symlinks may be called advanced shortcut files, but they appear to be real files. Unlike duplicated files, symlinks do not waste your storage space. SymlinkCreator works for both files and folders.

## How SymlinkCreator works
SymlinkCreator uses `mklink` command to create symlinks. SymlinkCreator first creates a script file which contains `mklink` command lines, and execute it. SymlinkCreator works in Windows Vista, Windows 7 and Windows 10. It does not work in Windows XP because of the lack of `mklink` command.

## How to use SymlinkCreator
![Screenshot](SymlinkCreator/_ReadMe/Screenshot.png "Screenshot of SymlinkCreator")
- At the `Source file list`, you can add files which will be copied in `Destination path` as symlinks.
- At the `Source file or folder list`, you can add files or folders which will be copied in `Destination path` as symlinks.
- Using SymlinkCreator's drag-n-drop feature, you can easily create multiple symlinks at a time.
- Tick `Use relative path if possible` option to use relative paths while creating symlinks. In this case, relative paths will be used if both source files and destination files are in the same drive.
- Tick `Use relative path if possible` option to use relative paths while creating symlinks. In this case, relative paths will be used if both source files/folders and destination files/folders are in the same drive.
- Tick `Retain script file after execution` option if you want to save the script file for later use like logging purpose.

## Why SymlinkCreator needs administrative rights
Expand Down
4 changes: 2 additions & 2 deletions SymlinkCreator/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,5 +52,5 @@
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]

[assembly: AssemblyVersion("1.0.1")]
[assembly: AssemblyFileVersion("1.0.1")]
[assembly: AssemblyVersion("1.1.0")]
[assembly: AssemblyFileVersion("1.1.0")]
Binary file modified SymlinkCreator/_ReadMe/Screenshot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
16 changes: 10 additions & 6 deletions SymlinkCreator/core/SymlinkAgent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public class SymlinkAgent
{
#region members

private readonly List<string> _sourceFileList;
private readonly List<string> _sourceFileOrFolderList;
private string _destinationPath;
private readonly bool _shouldUseRelativePath;
private readonly bool _shouldRetainScriptFile;
Expand All @@ -23,10 +23,10 @@ public class SymlinkAgent

#region constructor

public SymlinkAgent(IEnumerable<string> sourceFileList, string destinationPath,
public SymlinkAgent(IEnumerable<string> sourceFileOrFolderList, string destinationPath,
bool shouldUseRelativePath = true, bool shouldRetainScriptFile = false)
{
this._sourceFileList = sourceFileList.ToList();
this._sourceFileOrFolderList = sourceFileOrFolderList.ToList();
this._destinationPath = destinationPath;
this._shouldUseRelativePath = shouldUseRelativePath;
this._shouldRetainScriptFile = shouldRetainScriptFile;
Expand Down Expand Up @@ -59,7 +59,7 @@ public void CreateSymlinks()
scriptExecutor.WriteLine(_splittedDestinationPath[0]);
scriptExecutor.WriteLine("cd \"" + _destinationPath + "\"");

foreach (string sourceFilePath in _sourceFileList)
foreach (string sourceFilePath in _sourceFileOrFolderList)
{
string[] splittedSourceFilePath = GetSplittedPath(sourceFilePath);

Expand All @@ -73,8 +73,12 @@ public void CreateSymlinks()
}
}

scriptExecutor.WriteLine("mklink \"" + splittedSourceFilePath.Last() + "\" \"" +
commandLineTargetPath + "\"");
scriptExecutor.Write("mklink ");
if (Directory.Exists(sourceFilePath))
scriptExecutor.Write("/d ");

scriptExecutor.WriteLine("\"" + splittedSourceFilePath.Last() + "\" " +
"\"" + commandLineTargetPath + "\"");
}

scriptExecutor.ExecuteAsAdmin();
Expand Down
24 changes: 18 additions & 6 deletions SymlinkCreator/ui/mainWindow/MainWindow.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,18 @@
d:DataContext="{d:DesignData Type=local:MainWindowViewModel, IsDesignTimeCreatable=True}"
Title="{x:Static core:ApplicationConfiguration.ApplicationName}"
MinWidth="600"
MinHeight="250"
MinHeight="300"
Width="600"
Height="300">
<Window.Resources>
<Thickness x:Key="DefaultMargin">5</Thickness>
<system:Double x:Key="DefaultMinButtonWidth">80</system:Double>
<system:Double x:Key="DefaultMinButtonHeight">20</system:Double>

<Style TargetType="Button">
<Setter Property="Margin" Value="{StaticResource DefaultMargin}" />
<Setter Property="MinWidth" Value="{StaticResource DefaultMinButtonWidth}" />
<Setter Property="MinHeight" Value="{StaticResource DefaultMinButtonHeight}" />
</Style>

<Style TargetType="TextBlock">
Expand Down Expand Up @@ -53,10 +55,10 @@
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>

<TextBlock Grid.Row="0" Text="Source file list:" />
<TextBlock Grid.Row="0" Text="Source file or folder list:" />

<ListView Grid.Row="1" x:Name="SourceFileListView" ItemsSource="{Binding FileList}"
AllowDrop="True" Drop="SourceFileListView_OnDrop">
<ListView Grid.Row="1" x:Name="SourceFileOrFolderListView" ItemsSource="{Binding FileOrFolderList}"
AllowDrop="True" Drop="SourceFileOrFolderListView_OnDrop">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" ToolTip="{Binding}" />
Expand All @@ -65,7 +67,16 @@
</ListView>

<DockPanel Grid.Row="2" LastChildFill="False">
<Button DockPanel.Dock="Left" Content="Add files" Click="AddButton_OnClick" />
<DockPanel.Resources>
<Style TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="MaxHeight" Value="{StaticResource DefaultMinButtonHeight}" />
</Style>
</DockPanel.Resources>

<StackPanel DockPanel.Dock="Left">
<Button Content="Add files" Click="AddFilesButton_OnClick" />
<Button Content="Add folder" Click="AddFolderButton_OnClick" />
</StackPanel>

<StackPanel DockPanel.Dock="Right" Orientation="Horizontal">
<Button Content="Delete selected" Click="DeleteSelectedButton_OnClick" />
Expand Down Expand Up @@ -122,7 +133,8 @@
</StackPanel>
</Button>

<Button Content="About" Click="AboutButton_OnClick" />
<Button Content="About" MaxHeight="{StaticResource DefaultMinButtonHeight}"
Click="AboutButton_OnClick" />
</DockPanel>
</Grid>
</Grid>
Expand Down
53 changes: 38 additions & 15 deletions SymlinkCreator/ui/mainWindow/MainWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,15 +49,28 @@ protected override void OnSourceInitialized(EventArgs eventArgs)

#region control event handles

private void AddButton_OnClick(object sender, RoutedEventArgs e)
private void AddFilesButton_OnClick(object sender, RoutedEventArgs e)
{
OpenFileDialog fileDialog = new OpenFileDialog();
fileDialog.Multiselect = true;
bool? result = fileDialog.ShowDialog();

if (result == true)
{
AddToSourceFileList(fileDialog.FileNames);
AddToSourceFileOrFolderList(fileDialog.FileNames);
}
}

private void AddFolderButton_OnClick(object sender, RoutedEventArgs e)
{
using (FolderBrowserDialog folderBrowserDialog = new FolderBrowserDialog())
{
DialogResult result = folderBrowserDialog.ShowDialog();

if (result == System.Windows.Forms.DialogResult.OK)
{
AddToSourceFileOrFolderList(folderBrowserDialog.SelectedPath);
}
}
}

Expand All @@ -82,26 +95,26 @@ private void DeleteSelectedButton_OnClick(object sender, RoutedEventArgs e)
MainWindowViewModel mainWindowViewModel = this.DataContext as MainWindowViewModel;
if (mainWindowViewModel == null) return;

List<string> selectedFileList = SourceFileListView.SelectedItems.Cast<string>().ToList();
foreach (var selectedItem in selectedFileList)
List<string> selectedFileOrFolderList = SourceFileOrFolderListView.SelectedItems.Cast<string>().ToList();
foreach (var selectedItem in selectedFileOrFolderList)
{
mainWindowViewModel.FileList.Remove(selectedItem);
mainWindowViewModel.FileOrFolderList.Remove(selectedItem);
}
}

private void ClearListButton_OnClick(object sender, RoutedEventArgs e)
{
MainWindowViewModel mainWindowViewModel = this.DataContext as MainWindowViewModel;

mainWindowViewModel?.FileList.Clear();
mainWindowViewModel?.FileOrFolderList.Clear();
}

private void SourceFileListView_OnDrop(object sender, DragEventArgs e)
private void SourceFileOrFolderListView_OnDrop(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.FileDrop))
{
string[] droppedFileList = (string[]) e.Data.GetData(DataFormats.FileDrop);
AddToSourceFileList(droppedFileList);
string[] droppedFileOrFolderList = (string[]) e.Data.GetData(DataFormats.FileDrop);
AddToSourceFileOrFolderList(droppedFileOrFolderList);
}
}

Expand Down Expand Up @@ -129,7 +142,7 @@ private void CreateSymlinksButton_OnClick(object sender, RoutedEventArgs e)
if (mainWindowViewModel == null) return;

SymlinkAgent symlinkAgent = new SymlinkAgent(
mainWindowViewModel.FileList,
mainWindowViewModel.FileOrFolderList,
mainWindowViewModel.DestinationPath,
mainWindowViewModel.ShouldUseRelativePath,
mainWindowViewModel.ShouldRetainScriptFile);
Expand Down Expand Up @@ -157,21 +170,31 @@ private void AboutButton_OnClick(object sender, RoutedEventArgs e)

#region helper methods

private void AddToSourceFileList(IEnumerable<string> fileList)
private void AddToSourceFileOrFolderList(IEnumerable<string> fileOrFolderList)
{
MainWindowViewModel mainWindowViewModel = this.DataContext as MainWindowViewModel;
if (mainWindowViewModel == null) return;

foreach (string file in fileList)
foreach (string fileOrFolder in fileOrFolderList)
{
if (!mainWindowViewModel.FileList.Contains(file))
if (!mainWindowViewModel.FileOrFolderList.Contains(fileOrFolder))
{
if (File.Exists(file))
mainWindowViewModel.FileList.Add(file);
mainWindowViewModel.FileOrFolderList.Add(fileOrFolder);
}
}
}

private void AddToSourceFileOrFolderList(string fileOrFolderPath)
{
MainWindowViewModel mainWindowViewModel = this.DataContext as MainWindowViewModel;
if (mainWindowViewModel == null) return;

if (!mainWindowViewModel.FileOrFolderList.Contains(fileOrFolderPath))
{
mainWindowViewModel.FileOrFolderList.Add(fileOrFolderPath);
}
}

private void AssignDestinationPath(string destinationPath)
{
MainWindowViewModel mainWindowViewModel = this.DataContext as MainWindowViewModel;
Expand Down
2 changes: 1 addition & 1 deletion SymlinkCreator/ui/mainWindow/MainWindowViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ internal class MainWindowViewModel : INotifyPropertyChanged
{
#region properties

public ObservableCollection<string> FileList { get; set; } = new ObservableCollection<string>();
public ObservableCollection<string> FileOrFolderList { get; set; } = new ObservableCollection<string>();

private string _destinationPath;
public string DestinationPath
Expand Down

0 comments on commit 06f4179

Please sign in to comment.