diff --git a/.editorconfig b/.editorconfig
index 922679d..93670f4 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -18,3 +18,5 @@ dotnet_diagnostic.IDE0090.severity=silent
# Suppress 'use index operator (^)' and 'use substring operator (..)' because they make the code less readable / intuitive
dotnet_diagnostic.IDE0056.severity=silent
dotnet_diagnostic.IDE0057.severity=silent
+# Supress 'do not use implicit cast' because it makes the code much less readable
+dotnet_diagnostic.IDE0220.severity=silent
diff --git a/.gitattributes b/.gitattributes
index 1ff0c42..b72c2c6 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1,7 +1,7 @@
###############################################################################
# Set default behavior to automatically normalize line endings.
###############################################################################
-* text=auto
+* text eol=lf
###############################################################################
# Set default behavior for command prompt diff.
diff --git a/App.axaml b/App.axaml
new file mode 100644
index 0000000..dec45f8
--- /dev/null
+++ b/App.axaml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
diff --git a/App.axaml.cs b/App.axaml.cs
new file mode 100644
index 0000000..1422ab5
--- /dev/null
+++ b/App.axaml.cs
@@ -0,0 +1,23 @@
+using Avalonia;
+using Avalonia.Controls.ApplicationLifetimes;
+using Avalonia.Markup.Xaml;
+
+namespace robocopy_gui;
+
+public partial class App : Application
+{
+ public override void Initialize()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+
+ public override void OnFrameworkInitializationCompleted()
+ {
+ if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
+ {
+ desktop.MainWindow = new MainWindow();
+ }
+
+ base.OnFrameworkInitializationCompleted();
+ }
+}
\ No newline at end of file
diff --git a/App.config b/App.config
deleted file mode 100644
index 622859c..0000000
--- a/App.config
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
-
-
-
-
-
-
- 1280
-
-
- 600
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/App.xaml b/App.xaml
deleted file mode 100644
index 2057281..0000000
--- a/App.xaml
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
-
-
-
-
-
-
-
diff --git a/App.xaml.cs b/App.xaml.cs
deleted file mode 100644
index 292125a..0000000
--- a/App.xaml.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-using System.Windows;
-
-namespace robocopy_gui
-{
- ///
- /// Interaction logic for App.xaml
- ///
- public partial class App : Application
- {
- }
-}
diff --git a/AssemblyInfo.cs b/AssemblyInfo.cs
deleted file mode 100644
index 8b5504e..0000000
--- a/AssemblyInfo.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-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)
-)]
diff --git a/Assets/exclusions.png b/Assets/exclusions.png
index c9edf98..4cc7307 100644
Binary files a/Assets/exclusions.png and b/Assets/exclusions.png differ
diff --git a/Assets/icon.png b/Assets/icon.png
index 30ae757..9af0403 100644
Binary files a/Assets/icon.png and b/Assets/icon.png differ
diff --git a/Assets/main-interface.png b/Assets/main-interface.png
index 00e9106..5416454 100644
Binary files a/Assets/main-interface.png and b/Assets/main-interface.png differ
diff --git a/Classes/StartupTask.cs b/Classes/StartupTask.cs
index 1517606..f54d5e6 100644
--- a/Classes/StartupTask.cs
+++ b/Classes/StartupTask.cs
@@ -1,8 +1,11 @@
+using Avalonia;
+using Avalonia.Controls.ApplicationLifetimes;
using Microsoft.Win32;
+using robocopy_gui.UI;
using System;
-using System.Windows;
+using System.Threading.Tasks;
-namespace robocopy_gui.Classes
+namespace robocopy_gui
{
internal class StartupTask
{
@@ -35,7 +38,7 @@ public bool CheckRegistration()
}
return false; //Name is not present in key's values
}
- public void Register()
+ public async Task Register()
{
RegistryKey key = Registry.CurrentUser.OpenSubKey(startupKey, RegistryKeyPermissionCheck.ReadWriteSubTree) ?? throw new ArgumentException("HKEY_CURRENT_USER run key does not exist");
foreach (string valueName in key.GetValueNames())
@@ -44,21 +47,28 @@ public void Register()
{
if(key?.GetValue(Name)?.ToString() != Path)
{
- if(MessageBox.Show(
+ DialogMessage message = new DialogMessage(
Name + " is already set to startup with path '" + key?.GetValue(Name)?.ToString() + "'.\nOverwrite?",
- "Name conflict!",
- MessageBoxButton.YesNo) == MessageBoxResult.Yes )
+ "Name conflict!"
+ );
+ message.SetButtons(new string[] { "Yes", "No" });
+ if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
+ {
+ await message.ShowDialog(desktop.MainWindow);
+ }
+ if(message.ReturnValue == "Yes")
{
break;
} else
{
- return; //User does not want to overwrite existing startup key
+ return false; //User does not want to overwrite existing startup key
}
}
- return; //Name is already set with correct Path
+ return true; //Name is already set with correct Path
}
}
key?.SetValue(Name, Path, RegistryValueKind.String);
+ return true;
}
public void Unregister()
diff --git a/Classes/UIOperationArbitrary.cs b/Classes/UIOperationArbitrary.cs
deleted file mode 100644
index ce2dcdc..0000000
--- a/Classes/UIOperationArbitrary.cs
+++ /dev/null
@@ -1,34 +0,0 @@
-using System.Windows;
-using System.Windows.Controls;
-
-namespace robocopy_gui.Classes
-{
- internal class UIOperationArbitrary
- {
- public int Index { get; set; }
- public Label Label { get; set; }
- public TextBox Command { get; set; }
-
- public UIOperationArbitrary(int operationIndex)
- {
- Index = operationIndex;
-
- Label = new Label
- {
- Content = "Command:",
- HorizontalAlignment = HorizontalAlignment.Center,
- VerticalAlignment = VerticalAlignment.Center
- };
-
- Command = new TextBox
- {
- Name = "arbitraryCommand" + operationIndex,
- Text = MainWindow.OperationsList[operationIndex].Command,
- VerticalAlignment = VerticalAlignment.Top,
- Margin = new Thickness(10, 10, 0, 0),
- TextWrapping = TextWrapping.NoWrap,
- Tag = operationIndex
- };
- }
- }
-}
diff --git a/MainWindow.axaml b/MainWindow.axaml
new file mode 100644
index 0000000..f68367f
--- /dev/null
+++ b/MainWindow.axaml
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MainWindow.axaml.cs b/MainWindow.axaml.cs
new file mode 100644
index 0000000..3ccbbda
--- /dev/null
+++ b/MainWindow.axaml.cs
@@ -0,0 +1,534 @@
+using Avalonia.Controls;
+using Avalonia.Input;
+using Avalonia.Interactivity;
+using Avalonia.Markup.Xaml.MarkupExtensions;
+using Avalonia.Media;
+using Microsoft.Win32;
+using robocopy_gui.Classes;
+using robocopy_gui.UI;
+using System;
+using System.Collections.Generic;
+using System.IO;
+
+namespace robocopy_gui;
+
+public partial class MainWindow : Window {
+ public static List OperationsList { get; set; } = new List();
+ private static RegistryKey Regkey =
+#pragma warning disable CS8601 // Possible null reference assignment.
+ //Yes, the key is possibly null here. This is handled later, in initializations.
+ //So later on, this key is not null and all other checks should reflect that.
+ Registry.CurrentUser.OpenSubKey("Software\\Stormbase\\robocopy-ui", RegistryKeyPermissionCheck.ReadWriteSubTree);
+#pragma warning restore CS8601
+ private string currentFile = string.Empty;
+ private string scriptTitle = "Backup";
+
+ public MainWindow() {
+ InitializeComponent();
+ try {
+ var version = System.Reflection.Assembly.GetExecutingAssembly()?.GetName()?.Version?.ToString();
+ if (version is not null) {
+ TitleLabel.Content = "Robocopy GUI " + version;
+ }
+ string lastFile = "";
+ if (Regkey != null) {
+ string? lastFileValue = Regkey.GetValue("lastFile") as string;
+ if (lastFileValue is not null) {
+ lastFile = lastFileValue;
+ }
+ var lastWidth = Regkey.GetValue("Width");
+ var lastHeight = Regkey.GetValue("Height");
+ if (lastHeight is not null && lastWidth is not null) {
+ MainWindow1.Width = Convert.ToInt32(lastWidth);
+ MainWindow1.Height = Convert.ToInt32(lastHeight);
+ }
+ } else {
+ RegistryKey? key = Registry.CurrentUser.OpenSubKey("Software\\Stormbase", RegistryKeyPermissionCheck.ReadWriteSubTree);
+ if (key == null) {
+ Registry.CurrentUser.OpenSubKey("Software", RegistryKeyPermissionCheck.ReadWriteSubTree)?.CreateSubKey("Stormbase", true);
+ key = Registry.CurrentUser.OpenSubKey("Software\\Stormbase", RegistryKeyPermissionCheck.ReadWriteSubTree);
+ }
+ key = key?.OpenSubKey("robocopy-ui");
+ if (key == null) {
+ Registry.CurrentUser.OpenSubKey("Software\\Stormbase", RegistryKeyPermissionCheck.ReadWriteSubTree)?.CreateSubKey("robocopy-ui", true);
+ }
+ Regkey = Registry.CurrentUser.OpenSubKey("Software\\Stormbase\\robocopy-ui", RegistryKeyPermissionCheck.ReadWriteSubTree)
+ ?? throw new Exception("Couldn't open registry key after creation");
+ }
+ if (!string.IsNullOrWhiteSpace(lastFile)) {
+ if (File.Exists(lastFile)) {
+ InputFilePath.Text = lastFile;
+ currentFile = lastFile;
+ ButtonCommit.IsEnabled = true;
+ ReadFile();
+ }
+ }
+ } catch (Exception e) {
+ DialogMessage dialog = new DialogMessage(e.Message, "Error");
+ dialog.ShowDialog(MainWindow1);
+ throw;
+ }
+ }
+
+
+ /* UI ELEMENTS
+ * ===============================================================================
+ * ===============================================================================
+ */
+
+
+ private async void ButtonPickFile_Click(object sender, RoutedEventArgs e) {
+ OpenFileDialog openFileDialog = new OpenFileDialog {
+ Filters = new List { new FileDialogFilter { Extensions = new List { "bat" }, Name = "Batch Files" } },
+ AllowMultiple = false
+ };
+
+ var result = await openFileDialog.ShowAsync(MainWindow1);
+ if (result is not null && result.Length > 0) {
+ string fileName = result[0];
+ currentFile = fileName;
+ InputFilePath.Text = currentFile;
+ if (File.Exists(fileName)) {
+ ReadFile();
+ } else {
+ File.CreateText(fileName).Dispose();
+ //show operations group with empty list to allow adding of new operations
+ ClearOperationsList();
+ RenderList();
+ }
+ ButtonCommit.IsEnabled = true;
+ }
+ }
+ private async void ButtonReloadFile_Click(object sender, RoutedEventArgs e) {
+ if (File.Exists(currentFile)) {
+ ReadFile();
+ } else {
+ DialogMessage message = new DialogMessage(
+ "The file / path you've entered does not exist.\nDo you want to create it?",
+ "File doesn't exist"
+ );
+ message.SetButtons(new string[] { "Yes", "Cancel" });
+ await message.ShowDialog(MainWindow1);
+ if (message.ReturnValue == "Yes") {
+ File.CreateText(currentFile).Dispose();
+ //show operations group with empty list to allow adding of new operations
+ ClearOperationsList();
+ RenderList();
+ ButtonCommit.IsEnabled = true;
+ } else {
+ InputFilePath.Text = currentFile;
+ }
+ message.Close();
+ }
+ }
+ private async void InputFilePath_LostFocus(object? sender, RoutedEventArgs e) {
+ InputFilePath.LostFocus -= InputFilePath_LostFocus; //necessary, because on Enter this procedure would then fire again
+ if (currentFile != InputFilePath.Text) {
+ string fileName = InputFilePath.Text;
+ if (File.Exists(fileName)) {
+ currentFile = fileName;
+ ReadFile();
+ ButtonCommit.IsEnabled = true;
+ } else {
+ DialogMessage message = new DialogMessage(
+ "The file / path you've entered does not exist.\nDo you want to create it?",
+ "File doesn't exist"
+ );
+ message.SetButtons(new string[] { "Yes", "Cancel" });
+ await message.ShowDialog(MainWindow1);
+ if (message.ReturnValue == "Yes") {
+ File.CreateText(fileName).Dispose();
+ currentFile = fileName;
+ //show operations group with empty list to allow adding of new operations
+ ClearOperationsList();
+ RenderList();
+ ButtonCommit.IsEnabled = true;
+ } else {
+ InputFilePath.Text = currentFile;
+ }
+ message.Close();
+ }
+ }
+ InputFilePath.LostFocus += InputFilePath_LostFocus; //see above
+ }
+ private void InputFilePath_KeyDown(object sender, KeyEventArgs e) {
+ if (e.Key == Key.Enter) {
+ InputFilePath_LostFocus(sender, e);
+ }
+ }
+ private void InputScriptTitle_LostFocus(object sender, RoutedEventArgs e) {
+ scriptTitle = InputScriptTitle.Text;
+ }
+ private void InputScriptTitle_KeyDown(object sender, KeyEventArgs e) {
+ if (e.Key == Key.Enter) {
+ InputScriptTitle_LostFocus(sender, e);
+ }
+ }
+ private async void CheckStartup_Checked(object sender, RoutedEventArgs e) {
+ if (!await new StartupTask(currentFile, scriptTitle).Register()) {
+ CheckStartup.IsChecked = false;
+ }
+ }
+ private void CheckStartup_Unchecked(object sender, RoutedEventArgs e) {
+ new StartupTask(currentFile, scriptTitle).Unregister();
+ }
+
+
+ /* ROUTINES FOR GENERATED OPERATION ROW UI ELEMENTS
+ * ===============================================================================
+ * ===============================================================================
+ */
+
+ private async void OperationButtonSearchSource_Click(object? sender, RoutedEventArgs e) {
+ Button s = sender as Button ?? throw new Exception("Sender is null");
+ int index = Convert.ToInt32(s.Tag);
+ OpenFolderDialog dialog = new OpenFolderDialog {
+ Directory = Path.GetDirectoryName(OperationsList[index].SourceFolder)
+ };
+ string? result = await dialog.ShowAsync(MainWindow1);
+ if (result is not null) {
+ OperationsList[index].SourceFolder = result;
+ OperationsList[index].Name = OperationsList[index].CreateName();
+ foreach (Control control in GridOperations.Children) {
+ if (control.Name == "source" && Convert.ToInt32(control.Tag) == index) {
+ ((TextBox)control).Text = result;
+ }
+ }
+ }
+ }
+ private async void OperationButtonSearchDest_Click(object? sender, RoutedEventArgs args) {
+ Button s = sender as Button ?? throw new Exception("Sender is null");
+ int index = Convert.ToInt32(s.Tag);
+ OpenFolderDialog dialog = new OpenFolderDialog {
+ Directory = Path.GetDirectoryName(OperationsList[index].DestinationFolder)
+ };
+ string? result = await dialog.ShowAsync(MainWindow1);
+ if (result is not null) {
+ OperationsList[index].DestinationFolder = result;
+ OperationsList[index].Name = OperationsList[index].CreateName();
+ foreach (Control control in GridOperations.Children) {
+ if (control.Name == "dest" && Convert.ToInt32(control.Tag) == index) {
+ ((TextBox)control).Text = result;
+ }
+ }
+ }
+ }
+ private void OperationTextBoxSource_LostFocus(object? sender, RoutedEventArgs e) {
+ TextBox s = sender as TextBox ?? throw new Exception("Sender is null");
+ int index = Convert.ToInt32(s.Tag);
+ OperationsList[index].SourceFolder = s.Text;
+ OperationsList[index].Name = OperationsList[index].CreateName();
+ }
+ private void OperationTextBoxDest_LostFocus(object? sender, RoutedEventArgs e) {
+ TextBox s = sender as TextBox ?? throw new Exception("Sender is null");
+ int index = Convert.ToInt32(s.Tag);
+ OperationsList[index].DestinationFolder = s.Text;
+ OperationsList[index].Name = OperationsList[index].CreateName();
+ }
+ private void OperationTextBoxCommand_LostFocus(object? sender, RoutedEventArgs e) {
+ TextBox s = sender as TextBox ?? throw new Exception("Sender is null");
+ int index = Convert.ToInt32(s.Tag);
+ OperationsList[index].Command = s.Text;
+ }
+ private void OperationCheckMirror_enable(object? sender, RoutedEventArgs e) {
+ CheckBox s = sender as CheckBox ?? throw new Exception("Sender is null");
+ int index = Convert.ToInt32(s.Tag);
+ OperationsList[index].IsMirror = true;
+ foreach (Control control in GridOperations.Children) {
+ if (control.Name == "move" && Convert.ToInt32(control.Tag) == index) {
+ ((CheckBox)control).IsChecked = false;
+ }
+ }
+ }
+ private void OperationCheckMove_enable(object? sender, RoutedEventArgs e) {
+ CheckBox s = sender as CheckBox ?? throw new Exception("Sender is null");
+ int index = Convert.ToInt32(s.Tag);
+ OperationsList[index].IsMove = true;
+ foreach (Control control in GridOperations.Children) {
+ if (control.Name == "mirror" && Convert.ToInt32(control.Tag) == index) {
+ ((CheckBox)control).IsChecked = false;
+ }
+ }
+ }
+ private void OperationCheckOnlyNewer_enable(object? sender, RoutedEventArgs e) {
+ CheckBox s = sender as CheckBox ?? throw new Exception("Sender is null");
+ int index = Convert.ToInt32(s.Tag);
+ OperationsList[index].IsOnlyIfNewer = true;
+ foreach (Control control in GridOperations.Children) {
+ if (control.Name == "FATtime" && Convert.ToInt32(control.Tag) == index) {
+ ((CheckBox)control).IsChecked = true;
+ }
+ }
+ }
+
+
+
+ /* ROUTINES FOR READING AND PARSING FILE
+ * ===============================================================================
+ * ===============================================================================
+ */
+
+
+ private void ClearOperationsList() {
+ OperationsList.Clear();
+ foreach (Control control in GridOperations.Children) {
+ if (control is TextBox) // this prevents LostFocus events from firing for non-existing elements after the list is cleared or re-populated
+ {
+ control.LostFocus -= OperationTextBoxSource_LostFocus;
+ control.LostFocus -= OperationTextBoxDest_LostFocus;
+ }
+ }
+ GridOperations.Children.Clear();
+ GridOperations.RowDefinitions.Clear();
+ }
+
+
+ private void ReadFile() {
+ Regkey.SetValue("lastFile", currentFile);
+ ClearOperationsList();
+
+ //read lines in file
+ List operationStrings = new List();
+ using (StreamReader reader = File.OpenText(currentFile)) {
+ int skipFirstLines = 1; // 0 to disable - 1 is designed to skip "echo off" at the beginning of file
+ for (int i = 0; i < skipFirstLines; i++) {
+ reader.ReadLine();
+ }
+ while (!reader.EndOfStream) {
+ var readLine = reader.ReadLine();
+ if (readLine != null && !string.IsNullOrWhiteSpace(readLine)) {
+ operationStrings.Add(readLine);
+ }
+ }
+ reader.Close();
+ }
+
+ //interpret all read lines as operations
+ foreach (string operation in operationStrings) {
+ if (operation.ToLower().StartsWith("robocopy") || operation.ToLower().StartsWith("rem robocopy")) {
+ OperationsList.Add(new Operation(operation));
+ } else if (operation.ToLower().StartsWith("title ")) {
+ scriptTitle = operation.Substring(6);
+ } else if (operation.ToLower().StartsWith("rem ") && !operation.ToLower().StartsWith("rem echo")) {
+ OperationsList.Add(new Operation(true, false, operation.Substring(3)));
+ } else if (!operation.ToLower().StartsWith("echo")) {
+ OperationsList.Add(new Operation(true, true, operation));
+ }
+ }
+ InputScriptTitle.Text = scriptTitle;
+
+ if (new StartupTask(currentFile, scriptTitle).CheckRegistration()) {
+ CheckStartup.IsChecked = true;
+ } else {
+ CheckStartup.IsChecked = false;
+ }
+ RenderList();
+ }
+ private void AddOperationRow(Operation operation, int operationIndex) {
+ RowDefinition newRow = new RowDefinition {
+ Height = new GridLength(42)
+ };
+ GridOperations.RowDefinitions.Add(newRow);
+
+ if (!operation.IsArbitrary) {
+ RowOperationRobocopy row = new RowOperationRobocopy(operationIndex);
+ row.SearchSourceButton.Click += OperationButtonSearchSource_Click;
+ row.SearchDestButton.Click += OperationButtonSearchDest_Click;
+ row.SourceText.LostFocus += OperationTextBoxSource_LostFocus;
+ row.DestText.LostFocus += OperationTextBoxDest_LostFocus;
+ row.Mirror.Checked += OperationCheckMirror_enable;
+ row.Move.Checked += OperationCheckMove_enable;
+ row.OnlyNewer.Checked += OperationCheckOnlyNewer_enable;
+
+ Grid.SetColumn(row.SearchSourceButton, 1);
+ Grid.SetRow(row.SearchSourceButton, operationIndex);
+ Grid.SetColumn(row.SourceText, 2);
+ Grid.SetRow(row.SourceText, operationIndex);
+ Grid.SetColumn(row.SearchDestButton, 3);
+ Grid.SetRow(row.SearchDestButton, operationIndex);
+ Grid.SetColumn(row.DestText, 4);
+ Grid.SetRow(row.DestText, operationIndex);
+ Grid.SetColumn(row.ExclFilesButton, 5);
+ Grid.SetRow(row.ExclFilesButton, operationIndex);
+ Grid.SetColumn(row.ExclFoldersButton, 6);
+ Grid.SetRow(row.ExclFoldersButton, operationIndex);
+ Grid.SetColumn(row.Mirror, 7);
+ Grid.SetRow(row.Mirror, operationIndex);
+ Grid.SetColumn(row.Move, 8);
+ Grid.SetRow(row.Move, operationIndex);
+ Grid.SetColumn(row.OnlyNewer, 9);
+ Grid.SetRow(row.OnlyNewer, operationIndex);
+ Grid.SetColumn(row.FATFileTime, 10);
+ Grid.SetRow(row.FATFileTime, operationIndex);
+
+ GridOperations.Children.Add(row.SearchSourceButton);
+ GridOperations.Children.Add(row.SourceText);
+ GridOperations.Children.Add(row.SearchDestButton);
+ GridOperations.Children.Add(row.DestText);
+ GridOperations.Children.Add(row.ExclFilesButton);
+ GridOperations.Children.Add(row.ExclFoldersButton);
+ GridOperations.Children.Add(row.Mirror);
+ GridOperations.Children.Add(row.Move);
+ GridOperations.Children.Add(row.OnlyNewer);
+ GridOperations.Children.Add(row.FATFileTime);
+ } else {
+ RowOperationArbitrary row = new RowOperationArbitrary(operationIndex);
+ row.Command.LostFocus += OperationTextBoxCommand_LostFocus;
+ Grid.SetColumn(row.Command, 1);
+ Grid.SetColumnSpan(row.Command, 10);
+ Grid.SetRow(row.Command, operationIndex);
+ GridOperations.Children.Add(row.Command);
+ }
+
+ CheckBox enabled = new CheckBox {
+ Content = "Enabled",
+ IsChecked = operation.IsEnabled,
+ HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Center,
+ VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center,
+ Tag = operationIndex
+ };
+ enabled.Checked += (sender, e) => {
+ CheckBox s = sender as CheckBox ?? throw new Exception("Sender is null");
+ int index = Convert.ToInt32(s.Tag);
+ OperationsList[index].IsEnabled = true;
+ };
+ enabled.Unchecked += (sender, e) => {
+ CheckBox s = sender as CheckBox ?? throw new Exception("Sender is null");
+ int index = Convert.ToInt32(s.Tag);
+ OperationsList[index].IsEnabled = false;
+ };
+ Grid.SetColumn(enabled, 0);
+ Grid.SetRow(enabled, operationIndex);
+
+ PathIcon removeIcon = new PathIcon {
+ Data = Geometry.Parse(Icons.Delete)
+ };
+ Button remove = new Button {
+ Content = removeIcon,
+ HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Center,
+ VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center,
+ Width = 60,
+ Tag = operationIndex,
+ HorizontalContentAlignment = Avalonia.Layout.HorizontalAlignment.Center
+ };
+ remove.Click += (s, e) => {
+ Button sender = s as Button ?? throw new Exception("Sender is null");
+ int index = Convert.ToInt32(sender.Tag);
+ OperationsList.RemoveAt(index);
+ List toDelete = new List();
+ foreach (Control control in GridOperations.Children) {
+ if ((int)control.GetValue(Grid.RowProperty) == index) {
+ toDelete.Add(control); //the separate list is needed to not modify the collection that drives the foreach
+ }
+ if ((int)control.GetValue(Grid.RowProperty) > index) {
+ Grid.SetRow(control, (int)control.GetValue(Grid.RowProperty) - 1);
+ control.Tag = Convert.ToInt32(control.Tag) - 1;
+ }
+ }
+ foreach (Control control in toDelete) {
+ GridOperations.Children.Remove(control);
+ }
+ GridOperations.RowDefinitions.RemoveAt(index);
+ };
+ Grid.SetColumn(remove, 11);
+ Grid.SetRow(remove, operationIndex);
+
+ GridOperations.Children.Add(enabled);
+ GridOperations.Children.Add(remove);
+ }
+ public void RenderList() {
+ int operationIndex = 0;
+ foreach (Operation operation in OperationsList) {
+ AddOperationRow(operation, operationIndex);
+ operationIndex++;
+ }
+
+ RowDefinition addRow = new RowDefinition {
+ Height = new GridLength(42)
+ };
+ GridOperations.RowDefinitions.Add(addRow);
+
+ Button add = new Button {
+ Name = "ButtonAddRobocopy",
+ Content = "+ Operation",
+ HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Left,
+ VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center,
+ Width = 240
+ };
+ add.Click += (s, e) => {
+ Operation newOp = new Operation(string.Empty, string.Empty);
+ OperationsList.Add(newOp);
+ int currentAddRow = 0;
+ foreach (Control control in GridOperations.Children) {
+ if (control.Name == "ButtonAddArbitrary") {
+ currentAddRow = (int)control.GetValue(Grid.RowProperty);
+ Grid.SetRow(control, currentAddRow + 1);
+ Grid.SetRow(s as Control, currentAddRow + 1);
+ }
+ }
+ AddOperationRow(newOp, currentAddRow);
+ };
+ Button addArbitrary = new Button {
+ Name = "ButtonAddArbitrary",
+ Content = "+ Arbitrary Command",
+ HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Left,
+ VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center,
+ Width = 240
+ };
+ addArbitrary.Click += (s, e) => {
+ Operation newOp = new Operation(true, true, string.Empty);
+ OperationsList.Add(newOp);
+ int currentAddRow = 0;
+ foreach (Control control in GridOperations.Children) {
+ if (control.Name == "ButtonAddRobocopy") {
+ currentAddRow = (int)control.GetValue(Grid.RowProperty);
+ Grid.SetRow(control, currentAddRow + 1);
+ Grid.SetRow(s as Control, currentAddRow + 1);
+ }
+ }
+ AddOperationRow(newOp, currentAddRow);
+ };
+ Grid.SetColumn(add, 7);
+ Grid.SetColumnSpan(add, 4);
+ Grid.SetRow(add, operationIndex);
+ Grid.SetColumn(addArbitrary, 4);
+ Grid.SetColumnSpan(addArbitrary, 3);
+ Grid.SetRow(addArbitrary, operationIndex);
+
+ GridOperations.Children.Add(add);
+ GridOperations.Children.Add(addArbitrary);
+ }
+
+ private void ButtonCommit_Click(object sender, RoutedEventArgs e) {
+ // MessageBox.Show("Inspect!"); // set breakpoint here for convenient variable inspection
+
+ StreamWriter file;
+ if (!File.Exists(currentFile)) {
+ file = File.CreateText(currentFile);
+ } else {
+ file = new StreamWriter(currentFile);
+ }
+ file.WriteLine("echo off");
+ file.WriteLine("title " + scriptTitle);
+
+ foreach (Operation item in OperationsList) {
+ if ((item.IsArbitrary && !string.IsNullOrWhiteSpace(item.Command)) ||
+ (!string.IsNullOrWhiteSpace(item.SourceFolder) && !string.IsNullOrWhiteSpace(item.DestinationFolder))) {
+ file.WriteLine();
+ if (!item.IsEnabled) {
+ file.Write("REM ");
+ }
+ if (!item.IsArbitrary) {
+ file.WriteLine("echo " + item.Name);
+ }
+ file.WriteLine(item.GetCommand());
+ }
+ }
+ file.Close();
+ }
+
+ private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e) {
+ Regkey.SetValue("Width", MainWindow1.Width);
+ Regkey.SetValue("Height", MainWindow1.Height);
+ }
+}
diff --git a/MainWindow.xaml b/MainWindow.xaml
deleted file mode 100644
index 5196a2b..0000000
--- a/MainWindow.xaml
+++ /dev/null
@@ -1,67 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/MainWindow.xaml.cs b/MainWindow.xaml.cs
deleted file mode 100644
index 1077046..0000000
--- a/MainWindow.xaml.cs
+++ /dev/null
@@ -1,601 +0,0 @@
-using Microsoft.Win32;
-using Microsoft.WindowsAPICodePack.Dialogs;
-using robocopy_gui.Classes;
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Windows;
-using System.Windows.Controls;
-
-namespace robocopy_gui
-{
- ///
- /// Interaction logic for MainWindow.xaml
- ///
- public partial class MainWindow : Window
- {
- public static List OperationsList { get; set; } = new List();
- private readonly List registeredNames = new List();
- private string currentFile = "";
- private string scriptTitle = "Backup";
-
- public MainWindow()
- {
- InitializeComponent();
- try
- {
- MainWindow1.Width = Properties.Settings.Default.MainWindowWidth;
- MainWindow1.Height = Properties.Settings.Default.MainWindowHeight;
- string version = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString();
- this.Title = "Robocopy GUI " + version;
- string lastFile = Properties.Settings.Default.LastFile;
- if (!string.IsNullOrWhiteSpace(lastFile))
- {
- if (File.Exists(lastFile))
- {
- InputFilePath.Text = lastFile;
- currentFile = lastFile;
- ButtonCommit.IsEnabled = true;
- ReadFile();
- }
- }
- }
- catch (Exception e)
- {
- MessageBox.Show(e.Message + e.StackTrace);
- }
- }
-
- private void ButtonPickFile_Click(object sender, RoutedEventArgs e)
- {
- //pick batch file
- OpenFileDialog openFileDialog = new OpenFileDialog
- {
- CheckFileExists = false,
- Filter = "Batch Files (*.bat)|*.bat"
- };
- if (openFileDialog.ShowDialog() == true)
- {
- string fileName = openFileDialog.FileName;
- currentFile = fileName;
- InputFilePath.Text = currentFile;
- if (File.Exists(fileName))
- {
- ReadFile();
- }
- else
- {
- File.CreateText(fileName).Dispose();
- //show operations group with empty list to allow adding of new operations
- ClearOperationsList();
- RenderList();
- }
- ButtonCommit.IsEnabled = true;
- }
- }
-
- private void InputFilePath_LostFocus(object sender, RoutedEventArgs e)
- {
- if (currentFile != InputFilePath.Text)
- {
- string fileName = InputFilePath.Text;
- if (File.Exists(fileName))
- {
- currentFile = fileName;
- ReadFile();
- ButtonCommit.IsEnabled = true;
- }
- else
- {
- if (MessageBox.Show(
- "The file / path you've entered does not exist.\nDo you want to create it?",
- "File doesn't exist",
- MessageBoxButton.YesNo) == MessageBoxResult.Yes)
- {
- File.CreateText(fileName).Dispose();
- currentFile = fileName;
- //show operations group with empty list to allow adding of new operations
- ClearOperationsList();
- RenderList();
- ButtonCommit.IsEnabled = true;
- }
- else
- {
- InputFilePath.Text = currentFile;
- }
- }
- }
- }
- private void InputFilePath_KeyDown(object sender, System.Windows.Input.KeyEventArgs e)
- {
- if (e.Key == System.Windows.Input.Key.Enter)
- {
- InputFilePath_LostFocus(sender, e);
- }
- }
- private void InputFile_Drop(object sender, DragEventArgs e)
- {
- string fileName = ((string[])e.Data.GetData(DataFormats.FileDrop))[0];
- currentFile = fileName;
- InputFilePath.Text = currentFile;
- if (File.Exists(fileName))
- {
- ReadFile();
- ButtonCommit.IsEnabled = true;
- }
- }
- private void InputScriptTitle_LostFocus(object sender, RoutedEventArgs e)
- {
- scriptTitle = InputScriptTitle.Text;
- }
- private void InputScriptTitle_KeyDown(object sender, System.Windows.Input.KeyEventArgs e)
- {
- if (e.Key == System.Windows.Input.Key.Enter)
- {
- InputScriptTitle_LostFocus(sender, e);
- }
- }
- private void CheckStartup_Checked(object sender, RoutedEventArgs e)
- {
- new StartupTask(currentFile, scriptTitle).Register();
- }
-
- private void CheckStartup_Unchecked(object sender, RoutedEventArgs e)
- {
- new StartupTask(currentFile, scriptTitle).Unregister();
- }
-
- /* ROUTINES FOR GENERATED OPERATION ROW UI ELEMENTS
- * ===============================================================================
- * ===============================================================================
- */
-
- private void OperationButtonSearchSource_Click(object sender, RoutedEventArgs e)
- {
- Button s = sender as Button ?? throw new Exception("Sender is null");
- int index = Convert.ToInt32(s.Tag);
- CommonOpenFileDialog dialog = new CommonOpenFileDialog
- {
- InitialDirectory = Path.GetDirectoryName(MainWindow.OperationsList[index].SourceFolder),
- IsFolderPicker = true
- };
- if (dialog.ShowDialog() == CommonFileDialogResult.Ok)
- {
- OperationsList[index].SourceFolder = dialog.FileName;
- OperationsList[index].Name = OperationsList[index].CreateName();
- TextBox source = GridOperations.FindName("source" + index) as TextBox ?? throw new Exception("Couldn't find appropriate TextBox");
- source.Text = dialog.FileName;
- }
- }
- private void OperationButtonSearchDest_Click(object sender, RoutedEventArgs args)
- {
- Button s = sender as Button ?? throw new Exception("Sender is null");
- int index = Convert.ToInt32(s.Tag);
- CommonOpenFileDialog dialog = new CommonOpenFileDialog
- {
- InitialDirectory = Path.GetDirectoryName(OperationsList[index].DestinationFolder),
- IsFolderPicker = true
- };
- if (dialog.ShowDialog() == CommonFileDialogResult.Ok)
- {
- OperationsList[index].DestinationFolder = dialog.FileName;
- OperationsList[index].Name = OperationsList[index].CreateName();
- TextBox dest = GridOperations.FindName("dest" + index) as TextBox ?? throw new Exception("Couldn't find appropriate TextBox");
- dest.Text = dialog.FileName;
- }
- }
- private void OperationTextBoxSource_LostFocus(object sender, RoutedEventArgs e)
- {
- TextBox s = sender as TextBox ?? throw new Exception("Sender is null");
- int index = Convert.ToInt32(s.Tag);
- OperationsList[index].SourceFolder = s.Text;
- OperationsList[index].Name = OperationsList[index].CreateName();
- }
- private void OperationTextBoxDest_LostFocus(object sender, RoutedEventArgs e)
- {
- TextBox s = sender as TextBox ?? throw new Exception("Sender is null");
- int index = Convert.ToInt32(s.Tag);
- OperationsList[index].DestinationFolder = s.Text;
- OperationsList[index].Name = OperationsList[index].CreateName();
- }
- private void OperationTextBoxCommand_LostFocus(object sender, RoutedEventArgs e)
- {
- TextBox s = sender as TextBox ?? throw new Exception("Sender is null");
- int index = Convert.ToInt32(s.Tag);
- OperationsList[index].Command = s.Text;
- }
- private void OperationCheckMirror_enable(object sender, RoutedEventArgs e)
- {
- CheckBox s = sender as CheckBox ?? throw new Exception("Sender is null");
- int index = Convert.ToInt32(s.Tag);
- OperationsList[index].IsMirror = true;
- CheckBox move = GridOperations.FindName("move" + index) as CheckBox ?? throw new Exception("Couldn't find appropriate CheckBox");
- move.IsChecked = false;
- }
- private void OperationCheckMove_enable(object sender, RoutedEventArgs e)
- {
- CheckBox s = sender as CheckBox ?? throw new Exception("Sender is null");
- int index = Convert.ToInt32(s.Tag);
- OperationsList[index].IsMove = true;
- CheckBox mirror = GridOperations.FindName("mirror" + index) as CheckBox ?? throw new Exception("Couldn't find appropriate CheckBox");
- mirror.IsChecked = false;
- }
- private void OperationCheckOnlyNewer_enable(object sender, RoutedEventArgs e)
- {
- CheckBox s = sender as CheckBox ?? throw new Exception("Sender is null");
- int index = Convert.ToInt32(s.Tag);
- OperationsList[index].IsOnlyIfNewer = true;
- CheckBox fatFileTime = GridOperations.FindName("FATtime" + index) as CheckBox ?? throw new Exception("Couldn't find appropriate CheckBox");
- fatFileTime.IsChecked = true;
- }
-
-
- /* ROUTINES FOR READING AND PARSING FILE
- * ===============================================================================
- * ===============================================================================
- */
-
-
- private void ClearOperationsList()
- {
- OperationsList.Clear();
- foreach (string item in registeredNames)
- {
- GridOperations.UnregisterName(item);
- }
- registeredNames.Clear();
- foreach (UIElement control in GridOperations.Children)
- {
- if (control is TextBox) // this prevents LostFocus events from firing for non-existing elements after the list is cleared or re-populated
- {
- control.LostFocus -= OperationTextBoxSource_LostFocus;
- control.LostFocus -= OperationTextBoxDest_LostFocus;
- }
- }
- GridOperations.Children.Clear();
- GridOperations.RowDefinitions.Clear();
- }
-
- private void ReadFile()
- {
- ClearOperationsList();
-
- //read lines in file
- List operationStrings = new List();
- using (StreamReader reader = File.OpenText(currentFile))
- {
- int skipFirstLines = 1; // 0 to disable - 1 is designed to skip "echo off" at the beginning of file
- for (int i = 0; i < skipFirstLines; i++)
- {
- reader.ReadLine();
- }
- while (!reader.EndOfStream)
- {
- var readLine = reader.ReadLine();
- if (readLine != null && !string.IsNullOrWhiteSpace(readLine))
- {
- operationStrings.Add(readLine);
- }
- }
- reader.Close();
- }
-
- //interpret all read lines as operations
- foreach (string operation in operationStrings)
- {
- if (operation.ToLower().StartsWith("robocopy") || operation.ToLower().StartsWith("rem robocopy"))
- {
- OperationsList.Add(new Operation(operation));
- }
- else if (operation.ToLower().StartsWith("title "))
- {
- scriptTitle = operation.Substring(6);
- }
- else if (operation.ToLower().StartsWith("rem ") && !operation.ToLower().StartsWith("rem echo"))
- {
- OperationsList.Add(new Operation(true, false, operation.Substring(3)));
- }
- else if (!operation.ToLower().StartsWith("echo"))
- {
- OperationsList.Add(new Operation(true, true, operation));
- }
- }
- InputScriptTitle.Text = scriptTitle;
-
- if (new StartupTask(currentFile, scriptTitle).CheckRegistration())
- {
- CheckStartup.IsChecked = true;
- }
- else
- {
- CheckStartup.IsChecked = false;
- }
- RenderList();
- }
- private void AddOperationRow(Operation operation, int operationIndex)
- {
- RowDefinition newRow = new RowDefinition
- {
- Height = new GridLength(42)
- };
- GridOperations.RowDefinitions.Add(newRow);
-
- if (!operation.IsArbitrary)
- {
- UIOperationRobocopy row = new UIOperationRobocopy(operationIndex);
- row.SearchSourceButton.Click += OperationButtonSearchSource_Click;
- row.SearchDestButton.Click += OperationButtonSearchDest_Click;
- row.SourceText.LostFocus += OperationTextBoxSource_LostFocus;
- row.DestText.LostFocus += OperationTextBoxDest_LostFocus;
- row.Mirror.Checked += OperationCheckMirror_enable;
- row.Move.Checked += OperationCheckMove_enable;
- row.OnlyNewer.Checked += OperationCheckOnlyNewer_enable;
-
- Grid.SetColumn(row.SearchSourceButton, 1);
- Grid.SetRow(row.SearchSourceButton, operationIndex);
- Grid.SetColumn(row.SourceText, 2);
- Grid.SetRow(row.SourceText, operationIndex);
- Grid.SetColumn(row.SearchDestButton, 3);
- Grid.SetRow(row.SearchDestButton, operationIndex);
- Grid.SetColumn(row.DestText, 4);
- Grid.SetRow(row.DestText, operationIndex);
- Grid.SetColumn(row.ExclFilesButton, 5);
- Grid.SetRow(row.ExclFilesButton, operationIndex);
- Grid.SetColumn(row.ExclFoldersButton, 6);
- Grid.SetRow(row.ExclFoldersButton, operationIndex);
- Grid.SetColumn(row.Mirror, 7);
- Grid.SetRow(row.Mirror, operationIndex);
- Grid.SetColumn(row.Move, 8);
- Grid.SetRow(row.Move, operationIndex);
- Grid.SetColumn(row.OnlyNewer, 9);
- Grid.SetRow(row.OnlyNewer, operationIndex);
- Grid.SetColumn(row.FATFileTime, 10);
- Grid.SetRow(row.FATFileTime, operationIndex);
-
-
- GridOperations.Children.Add(row.SearchSourceButton);
- GridOperations.Children.Add(row.SourceText);
- GridOperations.Children.Add(row.SearchDestButton);
- GridOperations.Children.Add(row.DestText);
- GridOperations.Children.Add(row.ExclFilesButton);
- GridOperations.Children.Add(row.ExclFoldersButton);
- GridOperations.Children.Add(row.Mirror);
- GridOperations.Children.Add(row.Move);
- GridOperations.Children.Add(row.OnlyNewer);
- GridOperations.Children.Add(row.FATFileTime);
-
- GridOperations.RegisterName(row.SourceText.Name, row.SourceText);
- GridOperations.RegisterName(row.DestText.Name, row.DestText);
- GridOperations.RegisterName(row.Mirror.Name, row.Mirror);
- GridOperations.RegisterName(row.Move.Name, row.Move);
- GridOperations.RegisterName(row.FATFileTime.Name, row.FATFileTime);
- registeredNames.Add(row.SourceText.Name);
- registeredNames.Add(row.DestText.Name);
- registeredNames.Add(row.Mirror.Name);
- registeredNames.Add(row.Move.Name);
- registeredNames.Add(row.FATFileTime.Name);
- }
- else
- {
- UIOperationArbitrary row = new UIOperationArbitrary(operationIndex);
- row.Command.LostFocus += OperationTextBoxCommand_LostFocus;
- Grid.SetColumn(row.Label, 1);
- Grid.SetRow(row.Label, operationIndex);
- Grid.SetColumn(row.Command, 2);
- Grid.SetColumnSpan(row.Command, 9);
- Grid.SetRow(row.Command, operationIndex);
- GridOperations.Children.Add(row.Label);
- GridOperations.Children.Add(row.Command);
- GridOperations.RegisterName(row.Command.Name, row.Command);
- registeredNames.Add(row.Command.Name);
- }
-
- CheckBox enabled = new CheckBox
- {
- Content = "Enabled",
- IsChecked = operation.IsEnabled,
- HorizontalAlignment = HorizontalAlignment.Center,
- VerticalAlignment = VerticalAlignment.Center,
- Tag = operationIndex
- };
- enabled.Checked += (sender, e) =>
- {
- CheckBox s = sender as CheckBox ?? throw new Exception("Sender is null");
- int index = Convert.ToInt32(s.Tag);
- OperationsList[index].IsEnabled = true;
- };
- enabled.Unchecked += (sender, e) =>
- {
- CheckBox s = sender as CheckBox ?? throw new Exception("Sender is null");
- int index = Convert.ToInt32(s.Tag);
- OperationsList[index].IsEnabled = false;
- };
- Grid.SetColumn(enabled, 0);
- Grid.SetRow(enabled, operationIndex);
-
- Button remove = new Button
- {
- Content = "-",
- HorizontalAlignment = HorizontalAlignment.Center,
- VerticalAlignment = VerticalAlignment.Center,
- Width = 60,
- Tag = operationIndex
- };
- remove.Click += (s, e) =>
- {
-
- Button sender = s as Button ?? throw new Exception("Sender is null");
- int index = Convert.ToInt32(sender.Tag);
- OperationsList.RemoveAt(index);
- List toDelete = new List();
- foreach (Control control in GridOperations.Children)
- {
- if ((int)control.GetValue(Grid.RowProperty) == index)
- {
- toDelete.Add(control); //the separate list is needed for type conversion to UIElement
- if( !string.IsNullOrWhiteSpace(control.Name) )
- {
- GridOperations.UnregisterName(control.Name);
- registeredNames.Remove(control.Name);
- }
- }
- if ((int)control.GetValue(Grid.RowProperty) > index)
- {
- Grid.SetRow(control, (int)control.GetValue(Grid.RowProperty) - 1);
- if( !string.IsNullOrWhiteSpace(control.Name) )
- {
- GridOperations.UnregisterName(control.Name);
- registeredNames.Remove(control.Name);
- if(control.Name.StartsWith("source"))
- {
- control.Name = "source" + (Convert.ToInt32(control.Tag) - 1);
- } else if (control.Name.StartsWith("dest"))
- {
- control.Name = "dest" + (Convert.ToInt32(control.Tag) - 1);
- }
- else if (control.Name.StartsWith("mirror"))
- {
- control.Name = "mirror" + (Convert.ToInt32(control.Tag) - 1);
- }
- else if (control.Name.StartsWith("move"))
- {
- control.Name = "move" + (Convert.ToInt32(control.Tag) - 1);
- }
- else if (control.Name.StartsWith("FATtime"))
- {
- control.Name = "FATtime" + (Convert.ToInt32(control.Tag) - 1);
- }
- else if (control.Name.StartsWith("arbitraryCommand"))
- {
- control.Name = "arbitraryCommand" + (Convert.ToInt32(control.Tag) - 1);
- }
- GridOperations.RegisterName(control.Name, control);
- registeredNames.Add(control.Name);
- }
- control.Tag = Convert.ToInt32(control.Tag) - 1;
- }
- }
- foreach (UIElement control in toDelete)
- {
- GridOperations.Children.Remove(control);
- }
- GridOperations.RowDefinitions.RemoveAt(index);
- };
- Grid.SetColumn(remove, 11);
- Grid.SetRow(remove, operationIndex);
-
- GridOperations.Children.Add(enabled);
- GridOperations.Children.Add(remove);
- }
- public void RenderList()
- {
- int operationIndex = 0;
- foreach (Operation operation in OperationsList)
- {
- AddOperationRow(operation, operationIndex);
- operationIndex++;
- }
-
- RowDefinition addRow = new RowDefinition
- {
- Height = new GridLength(42)
- };
- GridOperations.RowDefinitions.Add(addRow);
-
- Button add = new Button
- {
- Name = "ButtonAddRobocopy",
- Content = "+ Operation",
- HorizontalAlignment = HorizontalAlignment.Left,
- VerticalAlignment = VerticalAlignment.Center,
- Width = 240
- };
- add.Click += (s, e) =>
- {
- Operation newOp = new Operation(string.Empty, string.Empty);
- OperationsList.Add(newOp);
- Button addArbitrary = GridOperations.FindName("ButtonAddArbitrary") as Button ?? throw new Exception("Couldn't find ButtonAddArbitrary");
- int currentAddRow = (int)addArbitrary.GetValue(Grid.RowProperty);
- Grid.SetRow(addArbitrary, currentAddRow + 1);
- Grid.SetRow(s as Control, currentAddRow + 1);
- AddOperationRow(newOp, currentAddRow);
- };
- Button addArbitrary = new Button
- {
- Name = "ButtonAddArbitrary",
- Content = "+ Arbitrary Command",
- HorizontalAlignment = HorizontalAlignment.Left,
- VerticalAlignment = VerticalAlignment.Center,
- Width = 240
- };
- addArbitrary.Click += (s, e) =>
- {
- Operation newOp = new Operation(true, true, string.Empty);
- OperationsList.Add(newOp);
- Button addRobocopy = GridOperations.FindName("ButtonAddRobocopy") as Button ?? throw new Exception("Couldn't find ButtonAddRobocopy");
- int currentAddRow = (int)addRobocopy.GetValue(Grid.RowProperty);
- Grid.SetRow(addRobocopy, currentAddRow + 1);
- Grid.SetRow(s as Control, currentAddRow + 1);
- AddOperationRow(newOp, currentAddRow);
- };
- Grid.SetColumn(add, 7);
- Grid.SetColumnSpan(add, 4);
- Grid.SetRow(add, operationIndex);
- Grid.SetColumn(addArbitrary, 4);
- Grid.SetColumnSpan(addArbitrary, 3);
- Grid.SetRow(addArbitrary, operationIndex);
-
- GridOperations.Children.Add(add);
- GridOperations.Children.Add(addArbitrary);
- GridOperations.RegisterName(add.Name, add);
- GridOperations.RegisterName(addArbitrary.Name, addArbitrary);
- registeredNames.Add(add.Name);
- registeredNames.Add(addArbitrary.Name);
- GroupOperations.Visibility = Visibility.Visible;
- }
-
- private void ButtonCommit_Click(object sender, RoutedEventArgs e)
- {
- // MessageBox.Show("Inspect!"); // set breakpoint here for convenient variable inspection
-
- StreamWriter file;
- if (!File.Exists(currentFile))
- {
- file = File.CreateText(currentFile);
- }
- else
- {
- file = new StreamWriter(currentFile);
- }
- file.WriteLine("echo off");
- file.WriteLine("title " + scriptTitle);
-
- foreach (Operation item in OperationsList)
- {
- if ((item.IsArbitrary && !string.IsNullOrWhiteSpace(item.Command)) ||
- (!string.IsNullOrWhiteSpace(item.SourceFolder) && !string.IsNullOrWhiteSpace(item.DestinationFolder)))
- {
- file.WriteLine();
- if (!item.IsEnabled)
- {
- file.Write("REM ");
- }
- if (!item.IsArbitrary)
- {
- file.WriteLine("echo " + item.Name);
- }
- file.WriteLine(item.GetCommand());
- }
- }
- file.Close();
- }
-
- private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
- {
- Properties.Settings.Default.MainWindowWidth = MainWindow1.Width;
- Properties.Settings.Default.MainWindowHeight = MainWindow1.Height;
- Properties.Settings.Default.LastFile = currentFile;
- Properties.Settings.Default.Save();
- }
- }
-}
diff --git a/Program.cs b/Program.cs
new file mode 100644
index 0000000..d1b6b20
--- /dev/null
+++ b/Program.cs
@@ -0,0 +1,19 @@
+using Avalonia;
+using System;
+
+namespace robocopy_gui;
+
+class Program {
+ // Initialization code. Don't use any Avalonia, third-party APIs or any
+ // SynchronizationContext-reliant code before AppMain is called: things aren't initialized
+ // yet and stuff might break.
+ [STAThread]
+ public static void Main(string[] args) => BuildAvaloniaApp()
+ .StartWithClassicDesktopLifetime(args);
+
+ // Avalonia configuration, don't remove; also used by visual designer.
+ public static AppBuilder BuildAvaloniaApp()
+ => AppBuilder.Configure()
+ .UsePlatformDetect()
+ .LogToTrace();
+}
diff --git a/Properties/Settings.Designer.cs b/Properties/Settings.Designer.cs
deleted file mode 100644
index d9712b6..0000000
--- a/Properties/Settings.Designer.cs
+++ /dev/null
@@ -1,62 +0,0 @@
-//------------------------------------------------------------------------------
-//
-// 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.
-//
-//------------------------------------------------------------------------------
-
-namespace robocopy_gui.Properties {
-
-
- [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.5.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;
- }
- }
-
- [global::System.Configuration.UserScopedSettingAttribute()]
- [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
- [global::System.Configuration.DefaultSettingValueAttribute("1280")]
- public double MainWindowWidth {
- get {
- return ((double)(this["MainWindowWidth"]));
- }
- set {
- this["MainWindowWidth"] = value;
- }
- }
-
- [global::System.Configuration.UserScopedSettingAttribute()]
- [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
- [global::System.Configuration.DefaultSettingValueAttribute("600")]
- public double MainWindowHeight {
- get {
- return ((double)(this["MainWindowHeight"]));
- }
- set {
- this["MainWindowHeight"] = value;
- }
- }
-
- [global::System.Configuration.UserScopedSettingAttribute()]
- [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
- [global::System.Configuration.DefaultSettingValueAttribute("")]
- public string LastFile {
- get {
- return ((string)(this["LastFile"]));
- }
- set {
- this["LastFile"] = value;
- }
- }
- }
-}
diff --git a/Properties/Settings.settings b/Properties/Settings.settings
deleted file mode 100644
index cec6945..0000000
--- a/Properties/Settings.settings
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
-
-
- 1280
-
-
- 600
-
-
-
-
-
-
\ No newline at end of file
diff --git a/README.md b/README.md
index 8defcb7..040aa6f 100644
--- a/README.md
+++ b/README.md
@@ -25,17 +25,21 @@
Grab your executable from the [release page](https://github.com/crowbait/robocopy-gui/releases).
-There are 2 versions published:
-- framework-dependent: requires .NET-Framework 7.0 to be installed, but is a much smaller executable
-- self-contained: does not have outside dependencies, but is much larger
-
-Both assembly types are build for x64 Windows.
+Binaries are built for x64 Windows.
+Important: the program is targeting Windows 10.0.18362 and *might or might not* work on older versions. Theoretically, it should work down to Windows 7.
## To-do
-- migrate to UWP or WinUI 3 to enable the following features:
- - drag-and-drop reordering of operations
- - visual grouping of operations for a less convoluted interface
- settings dialog per operation
- move less important checkboxes (only newer, FAT file time)
- options to set retry count and multithreading
+
+#### Delayed indefinitely
+These items seem either impossible or at least unfeasible. Further research might be required and ideas on tackling these problems have not been successful so far.
+- drag-and-drop reordering of operations
+- visual grouping of operations
+
+## Development
+
+- development using Visual Studio 2022 is recommended
+- installation of [AvaloniaUI extension](https://avaloniaui.net/GettingStarted#installation) is recommended
diff --git a/UI/DialogExclusions.axaml b/UI/DialogExclusions.axaml
new file mode 100644
index 0000000..320cebc
--- /dev/null
+++ b/UI/DialogExclusions.axaml
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/UI/DialogExclusions.axaml.cs b/UI/DialogExclusions.axaml.cs
new file mode 100644
index 0000000..3b36b2c
--- /dev/null
+++ b/UI/DialogExclusions.axaml.cs
@@ -0,0 +1,114 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Data.Core;
+using Avalonia.Data;
+using Avalonia.Media;
+using System;
+using System.Collections.Generic;
+using Avalonia.Interactivity;
+
+namespace robocopy_gui.UI;
+
+public partial class DialogExclusions : Window {
+ private class StringObject {
+ public string Value { get; set; } = string.Empty;
+ public StringObject(string init) {
+ Value = init;
+ }
+ }
+
+ private readonly List exclusions = new List();
+ public List ReturnExclusions = new List();
+ public bool IsCancel = false;
+
+ public DialogExclusions() {
+ InitializeComponent();
+ }
+
+ public DialogExclusions(List exclusionList) {
+ InitializeComponent();
+ foreach (string item in exclusionList) {
+ exclusions.Add(new StringObject(item));
+ }
+ RenderList();
+ }
+
+ private void RenderList() {
+ int index = 0;
+ GridExclusions.RowDefinitions.Clear();
+ GridExclusions.Children.Clear();
+ foreach (StringObject exclusion in exclusions) {
+ RowDefinition newRow = new RowDefinition {
+ Height = new GridLength(42)
+ };
+ GridExclusions.RowDefinitions.Add(newRow);
+
+ TextBox pattern = new TextBox {
+ [!TextBox.TextProperty] = new Binding("Value", BindingMode.TwoWay) {
+ Source = exclusions[index]
+ },
+ VerticalAlignment = Avalonia.Layout.VerticalAlignment.Top,
+ Margin = new Thickness(10, 10, 0, 0),
+ TextWrapping = TextWrapping.NoWrap
+ };
+ Grid.SetColumn(pattern, 0);
+ Grid.SetRow(pattern, index);
+
+ Button remove = new Button {
+ Content = "-",
+ HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Center,
+ VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center,
+ Width = 60,
+ Tag = index
+ };
+ remove.Click += (s, e) => {
+ Button sender = s as Button ?? throw new Exception("Sender is null");
+ int index = Convert.ToInt32(sender.Tag);
+ exclusions.RemoveAt(index);
+ RenderList();
+ };
+ Grid.SetColumn(remove, 1);
+ Grid.SetRow(remove, index);
+
+ GridExclusions.Children.Add(pattern);
+ GridExclusions.Children.Add(remove);
+ index++;
+ }
+
+ RowDefinition addRow = new RowDefinition {
+ Height = new GridLength(42)
+ };
+ GridExclusions.RowDefinitions.Add(addRow);
+
+ Button add = new Button {
+ Content = "+",
+ HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Center,
+ VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center,
+ Width = 60
+ };
+ add.Click += (s, e) => {
+ exclusions.Add(new StringObject(""));
+ RenderList();
+ };
+ Grid.SetColumn(add, 1);
+ Grid.SetRow(add, index);
+
+ GridExclusions.Children.Add(add);
+ }
+
+ private void ButtonExclusionsOK_Click(object sender, RoutedEventArgs e) {
+ ReturnExclusions.Clear();
+ foreach (StringObject exclusion in exclusions) {
+ if (!string.IsNullOrWhiteSpace(exclusion.Value)) {
+ ReturnExclusions.Add(exclusion.Value);
+ }
+ }
+ IsCancel = false;
+ ExclusionsWindow.Close();
+ }
+
+ private void ButtonExclusionsCancel_Click(object sender, RoutedEventArgs e) {
+ IsCancel = true;
+ ExclusionsWindow.Close();
+ }
+}
diff --git a/UI/DialogExclusions.xaml b/UI/DialogExclusions.xaml
deleted file mode 100644
index 6a52cc1..0000000
--- a/UI/DialogExclusions.xaml
+++ /dev/null
@@ -1,38 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/UI/DialogExclusions.xaml.cs b/UI/DialogExclusions.xaml.cs
deleted file mode 100644
index aff3054..0000000
--- a/UI/DialogExclusions.xaml.cs
+++ /dev/null
@@ -1,121 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Windows;
-using System.Windows.Controls;
-using System.Windows.Data;
-
-namespace robocopy_gui
-{
- ///
- /// Interaction logic for DialogExclusions.xaml
- ///
- public partial class DialogExclusions : Window
- {
- private List exclusions = new List();
- public List returnExclusions = new List();
- public DialogExclusions(List exclusionList)
- {
- InitializeComponent();
- foreach (string item in exclusionList)
- {
- exclusions.Add(new StringObject(item));
- }
- renderList();
- }
-
- private void renderList()
- {
- int index = 0;
- GridExclusions.RowDefinitions.Clear();
- GridExclusions.Children.Clear();
- foreach (StringObject exclusion in exclusions)
- {
- RowDefinition newRow = new RowDefinition();
- newRow.Height = new GridLength(42);
- GridExclusions.RowDefinitions.Add(newRow);
-
- TextBox pattern = new TextBox();
- //pattern.Text = exclusions[index];
- pattern.VerticalAlignment = VerticalAlignment.Top;
- pattern.Margin = new Thickness(10, 10, 0, 0);
- pattern.TextWrapping = TextWrapping.NoWrap;
- Binding binding = new Binding
- {
- Path = new PropertyPath("Value"),
- Source = exclusions[index],
- Mode = BindingMode.TwoWay,
- UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
- };
- pattern.SetBinding(TextBox.TextProperty, binding);
- Grid.SetColumn(pattern, 0);
- Grid.SetRow(pattern, index);
-
- Button remove = new Button();
- remove.Content = "-";
- remove.HorizontalAlignment = HorizontalAlignment.Center;
- remove.VerticalAlignment = VerticalAlignment.Center;
- remove.Width = 60;
- remove.Tag = index;
- remove.Click += (s, e) =>
- {
- Button sender = s as Button ?? throw new Exception("Sender is null");
- int index = Convert.ToInt32(sender.Tag);
- exclusions.RemoveAt(index);
- renderList();
- };
- Grid.SetColumn(remove, 1);
- Grid.SetRow(remove, index);
-
- GridExclusions.Children.Add(pattern);
- GridExclusions.Children.Add(remove);
- index++;
- }
-
- RowDefinition addRow = new RowDefinition();
- addRow.Height = new GridLength(42);
- GridExclusions.RowDefinitions.Add(addRow);
-
- Button add = new Button();
- add.Content = "+";
- add.HorizontalAlignment = HorizontalAlignment.Center;
- add.VerticalAlignment = VerticalAlignment.Center;
- add.Width = 60;
- add.Click += (s, e) =>
- {
- exclusions.Add(new StringObject(""));
- renderList();
- };
- Grid.SetColumn(add, 1);
- Grid.SetRow(add, index);
-
- GridExclusions.Children.Add(add);
- }
-
- private class StringObject
- {
- public string Value { get; set; } = string.Empty;
- public StringObject(string init)
- {
- Value = init;
- }
- }
-
- private void ButtonExclusionsOK_Click(object sender, RoutedEventArgs e)
- {
- returnExclusions.Clear();
- foreach (StringObject exclusion in exclusions)
- {
- if (!string.IsNullOrWhiteSpace(exclusion.Value))
- {
- returnExclusions.Add(exclusion.Value);
- }
- }
- DialogExclusionsWindow.DialogResult = true;
- }
-
- private void ButtonExclusionsCancel_Click(object sender, RoutedEventArgs e)
- {
- DialogExclusionsWindow.DialogResult = false;
- }
- }
-}
diff --git a/UI/DialogMessage.axaml b/UI/DialogMessage.axaml
new file mode 100644
index 0000000..55586f7
--- /dev/null
+++ b/UI/DialogMessage.axaml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/UI/DialogMessage.axaml.cs b/UI/DialogMessage.axaml.cs
new file mode 100644
index 0000000..b2c7cf9
--- /dev/null
+++ b/UI/DialogMessage.axaml.cs
@@ -0,0 +1,57 @@
+using Avalonia;
+using Avalonia.Controls;
+
+namespace robocopy_gui.UI;
+
+public partial class DialogMessage : Window {
+ private string _text;
+ public string Text {
+ get { return _text; }
+ set {
+ _text = value;
+ TextBlock.Text = _text;
+ }
+ }
+ private string _header;
+ public string Header {
+ get { return _header; }
+ set {
+ _header = value;
+ TitleLabel.Content = value;
+ }
+ }
+ public void SetButtons(string[] value) {
+ int index = 0;
+ foreach (string item in value) {
+ Button button = new Button {
+ Content = item,
+ Margin = new Thickness(5, 0, 0, 0)
+ };
+ if (index == 0) {
+ button.IsDefault = true;
+ }
+ button.Click += (s, e) => {
+ ReturnValue = item;
+ MessageWindow.Close();
+ };
+ GridButtons.Children.Add(button);
+ GridButtons.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
+ index++;
+ Grid.SetColumn(button, index);
+ }
+ }
+ public string ReturnValue = string.Empty;
+
+ public DialogMessage() {
+ InitializeComponent();
+ _text = string.Empty;
+ _header = string.Empty;
+ }
+ public DialogMessage(string text, string title) {
+ InitializeComponent();
+ _text = text;
+ Text = text;
+ _header = title;
+ Header = title;
+ }
+}
diff --git a/UI/Icons.axaml b/UI/Icons.axaml
new file mode 100644
index 0000000..03ab5eb
--- /dev/null
+++ b/UI/Icons.axaml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/UI/Icons.cs b/UI/Icons.cs
new file mode 100644
index 0000000..964e83c
--- /dev/null
+++ b/UI/Icons.cs
@@ -0,0 +1,6 @@
+namespace robocopy_gui.UI {
+ internal static class Icons {
+ public static string Delete = "M24,7.25 C27.1017853,7.25 29.629937,9.70601719 29.7458479,12.7794443 L29.75,13 L37,13 C37.6903559,13 38.25,13.5596441 38.25,14.25 C38.25,14.8972087 37.7581253,15.4295339 37.1278052,15.4935464 L37,15.5 L35.909,15.5 L34.2058308,38.0698451 C34.0385226,40.2866784 32.1910211,42 29.9678833,42 L18.0321167,42 C15.8089789,42 13.9614774,40.2866784 13.7941692,38.0698451 L12.09,15.5 L11,15.5 C10.3527913,15.5 9.8204661,15.0081253 9.75645361,14.3778052 L9.75,14.25 C9.75,13.6027913 10.2418747,13.0704661 10.8721948,13.0064536 L11,13 L18.25,13 C18.25,9.82436269 20.8243627,7.25 24,7.25 Z M33.4021054,15.5 L14.5978946,15.5 L16.2870795,37.8817009 C16.3559711,38.7945146 17.116707,39.5 18.0321167,39.5 L29.9678833,39.5 C30.883293,39.5 31.6440289,38.7945146 31.7129205,37.8817009 L33.4021054,15.5 Z M27.25,20.75 C27.8972087,20.75 28.4295339,21.2418747 28.4935464,21.8721948 L28.5,22 L28.5,33 C28.5,33.6903559 27.9403559,34.25 27.25,34.25 C26.6027913,34.25 26.0704661,33.7581253 26.0064536,33.1278052 L26,33 L26,22 C26,21.3096441 26.5596441,20.75 27.25,20.75 Z M20.75,20.75 C21.3972087,20.75 21.9295339,21.2418747 21.9935464,21.8721948 L22,22 L22,33 C22,33.6903559 21.4403559,34.25 20.75,34.25 C20.1027913,34.25 19.5704661,33.7581253 19.5064536,33.1278052 L19.5,33 L19.5,22 C19.5,21.3096441 20.0596441,20.75 20.75,20.75 Z M24,9.75 C22.2669685,9.75 20.8507541,11.1064548 20.7551448,12.8155761 L20.75,13 L27.25,13 C27.25,11.2050746 25.7949254,9.75 24,9.75 Z";
+ public static string Search = "M11.5,2.75 C16.3324916,2.75 20.25,6.66750844 20.25,11.5 C20.25,13.6461673 19.4773285,15.6118676 18.1949905,17.1340957 L25.0303301,23.9696699 C25.3232233,24.2625631 25.3232233,24.7374369 25.0303301,25.0303301 C24.7640635,25.2965966 24.3473998,25.3208027 24.0537883,25.1029482 L23.9696699,25.0303301 L17.1340957,18.1949905 C15.6118676,19.4773285 13.6461673,20.25 11.5,20.25 C6.66750844,20.25 2.75,16.3324916 2.75,11.5 C2.75,6.66750844 6.66750844,2.75 11.5,2.75 Z M11.5,4.25 C7.49593556,4.25 4.25,7.49593556 4.25,11.5 C4.25,15.5040644 7.49593556,18.75 11.5,18.75 C15.5040644,18.75 18.75,15.5040644 18.75,11.5 C18.75,7.49593556 15.5040644,4.25 11.5,4.25 Z";
+ }
+}
diff --git a/UI/RowOperationArbitrary.cs b/UI/RowOperationArbitrary.cs
new file mode 100644
index 0000000..8724859
--- /dev/null
+++ b/UI/RowOperationArbitrary.cs
@@ -0,0 +1,21 @@
+using Avalonia.Controls;
+
+namespace robocopy_gui.UI {
+ internal class RowOperationArbitrary {
+ public int Index { get; set; }
+ public TextBox Command { get; set; }
+
+ public RowOperationArbitrary(int operationIndex) {
+ Index = operationIndex;
+ Command = new TextBox {
+ Name = "arbitraryCommand",
+ Text = MainWindow.OperationsList[operationIndex].Command,
+ Watermark = "Command",
+ VerticalAlignment = Avalonia.Layout.VerticalAlignment.Top,
+ Margin = new Avalonia.Thickness(10, 5, 0, 0),
+ TextWrapping = Avalonia.Media.TextWrapping.NoWrap,
+ Tag = operationIndex
+ };
+ }
+ }
+}
diff --git a/Classes/UIOperationRobocopy.cs b/UI/RowOperationRobocopy.cs
similarity index 55%
rename from Classes/UIOperationRobocopy.cs
rename to UI/RowOperationRobocopy.cs
index aca9530..84366d9 100644
--- a/Classes/UIOperationRobocopy.cs
+++ b/UI/RowOperationRobocopy.cs
@@ -1,67 +1,71 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Controls.ApplicationLifetimes;
+using Avalonia.Layout;
+using Avalonia.Media;
using System;
-using System.Windows;
-using System.Windows.Controls;
+using System.Linq;
-namespace robocopy_gui.Classes
-{
- internal class UIOperationRobocopy
- {
- public int Index { get; set;}
- public Button SearchSourceButton { get; set;}
- public TextBox SourceText { get; set;}
- public Button SearchDestButton { get; set;}
- public TextBox DestText { get; set;}
- public Button ExclFilesButton { get; set;}
- public Button ExclFoldersButton { get; set;}
- public CheckBox Mirror { get; set;}
- public CheckBox Move { get; set;}
- public CheckBox OnlyNewer { get; set;}
- public CheckBox FATFileTime { get; set;}
+namespace robocopy_gui.UI {
+ internal class RowOperationRobocopy {
+ public int Index { get; set; }
+ public Button SearchSourceButton { get; set; }
+ public TextBox SourceText { get; set; }
+ public Button SearchDestButton { get; set; }
+ public TextBox DestText { get; set; }
+ public Button ExclFilesButton { get; set; }
+ public Button ExclFoldersButton { get; set; }
+ public CheckBox Mirror { get; set; }
+ public CheckBox Move { get; set; }
+ public CheckBox OnlyNewer { get; set; }
+ public CheckBox FATFileTime { get; set; }
- public UIOperationRobocopy(int operationIndex)
- {
+ public RowOperationRobocopy(int operationIndex) {
Index = operationIndex;
- SearchSourceButton = new Button
- {
- Content = "Search...",
+ PathIcon searchSourceIcon = new PathIcon{
+ Data = Geometry.Parse(Icons.Search)
+ };
+ SearchSourceButton = new Button {
+ Content = searchSourceIcon,
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center,
- Width = 100,
+ Width = 60,
Tag = operationIndex
};
- SourceText = new TextBox
- {
- Name = "source" + operationIndex,
+ SourceText = new TextBox {
+ Name = "source",
+ Watermark = "Source",
Text = MainWindow.OperationsList[operationIndex].SourceFolder,
VerticalAlignment = VerticalAlignment.Top,
- Margin = new Thickness(10, 10, 0, 0),
+ Margin = new Thickness(10, 5, 0, 0),
TextWrapping = TextWrapping.NoWrap,
Tag = operationIndex
};
- SearchDestButton = new Button
- {
- Content = "Search...",
+ PathIcon searchDestIcon = new PathIcon {
+ Data = Geometry.Parse(Icons.Search)
+ };
+ SearchDestButton = new Button {
+ Content = searchDestIcon,
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center,
- Width = 100,
+ Width = 60,
Tag = operationIndex
};
- DestText = new TextBox
- {
- Name = "dest" + operationIndex,
+ DestText = new TextBox {
+ Name = "dest",
+ Watermark = "Destination",
Text = MainWindow.OperationsList[operationIndex].DestinationFolder,
VerticalAlignment = VerticalAlignment.Top,
- Margin = new Thickness(10, 10, 0, 0),
+ Margin = new Thickness(10, 5, 0, 0),
TextWrapping = TextWrapping.NoWrap,
Tag = operationIndex
};
- ExclFilesButton = new Button
- {
+ ExclFilesButton = new Button {
Content = "Exclude\nFiles: " + MainWindow.OperationsList[operationIndex].ExcludeFiles.Count,
FontSize = 10,
HorizontalAlignment = HorizontalAlignment.Center,
@@ -69,23 +73,20 @@ public UIOperationRobocopy(int operationIndex)
Width = 100,
Tag = operationIndex
};
- ExclFilesButton.Click += (s, e) =>
- {
+ ExclFilesButton.Click += async (s, e) => {
Button sender = s as Button ?? throw new Exception("Sender is null");
int index = Convert.ToInt32(sender.Tag);
DialogExclusions dialog = new DialogExclusions(MainWindow.OperationsList[index].ExcludeFiles);
- dialog.ShowDialog();
-
- //handle return
- if (dialog.DialogResult.HasValue && dialog.DialogResult.Value)
- {
- MainWindow.OperationsList[index].ExcludeFiles = dialog.returnExclusions;
- sender.Content = "Exclude\nFiles: " + dialog.returnExclusions.Count;
+ if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) {
+ await dialog.ShowDialog(desktop.MainWindow);
+ }
+ if (!dialog.IsCancel) {
+ MainWindow.OperationsList[index].ExcludeFiles = dialog.ReturnExclusions;
+ sender.Content = "Exclude\nFiles: " + dialog.ReturnExclusions.Count;
}
};
- ExclFoldersButton = new Button
- {
+ ExclFoldersButton = new Button {
Content = "Exclude\nFolders: " + MainWindow.OperationsList[operationIndex].ExcludeFolders.Count,
FontSize = 10,
HorizontalAlignment = HorizontalAlignment.Center,
@@ -93,89 +94,82 @@ public UIOperationRobocopy(int operationIndex)
Width = 100,
Tag = operationIndex
};
- ExclFoldersButton.Click += (s, e) =>
- {
+ ExclFoldersButton.Click += async (s, e) => {
Button sender = s as Button ?? throw new Exception("Sender is null");
int index = Convert.ToInt32(sender.Tag);
DialogExclusions dialog = new DialogExclusions(MainWindow.OperationsList[index].ExcludeFolders);
- dialog.ShowDialog();
-
- //handle return
- if (dialog.DialogResult.HasValue && dialog.DialogResult.Value)
- {
- MainWindow.OperationsList[index].ExcludeFolders = dialog.returnExclusions;
- sender.Content = "Exclude\nFolders: " + dialog.returnExclusions.Count;
+ if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) {
+ await dialog.ShowDialog(desktop.MainWindow);
+ }
+ if (!dialog.IsCancel) {
+ MainWindow.OperationsList[index].ExcludeFolders = dialog.ReturnExclusions;
+ sender.Content = "Exclude\nFiles: " + dialog.ReturnExclusions.Count;
}
};
- Mirror = new CheckBox
- {
- Name = "mirror" + operationIndex,
+ Mirror = new CheckBox {
+ Name = "mirror",
Content = "Mirror",
IsChecked = MainWindow.OperationsList[operationIndex].IsMirror,
- ToolTip = "Copy files to destination folder, removing files from the destination that are not present in the source folder.",
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center,
Tag = operationIndex
};
- Mirror.Unchecked += (sender, e) =>
- {
+ Mirror.SetValue(ToolTip.TipProperty,
+ "Copy files to destination folder, removing files from the destination that are not present in the source folder.");
+ Mirror.Unchecked += (sender, e) => {
CheckBox s = sender as CheckBox ?? throw new Exception("Sender is null");
int index = Convert.ToInt32(s.Tag);
MainWindow.OperationsList[index].IsMirror = false;
};
- Move = new CheckBox
- {
- Name = "move" + operationIndex,
+ Move = new CheckBox {
+ Name = "move",
Content = "Move",
IsChecked = MainWindow.OperationsList[operationIndex].IsMove,
- ToolTip = "Move files to the destination folder rather than copying them, removing them from the source folder.",
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center,
Tag = operationIndex
};
- Move.Unchecked += (sender, e) =>
- {
+ Move.SetValue(ToolTip.TipProperty,
+ "Move files to the destination folder rather than copying them, removing them from the source folder.");
+ Move.Unchecked += (sender, e) => {
CheckBox s = sender as CheckBox ?? throw new Exception("Sender is null");
int index = Convert.ToInt32(s.Tag);
MainWindow.OperationsList[index].IsMove = false;
};
- OnlyNewer = new CheckBox
- {
+ OnlyNewer = new CheckBox {
Content = "Only newer",
IsChecked = MainWindow.OperationsList[operationIndex].IsOnlyIfNewer,
- ToolTip = "Copy or move files to the destination folder only if the source file is newer than the target file.",
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center,
Tag = operationIndex
};
- OnlyNewer.Unchecked += (sender, e) =>
- {
+ OnlyNewer.SetValue(ToolTip.TipProperty,
+ "Copy or move files to the destination folder only if the source file is newer than the target file.");
+ OnlyNewer.Unchecked += (sender, e) => {
CheckBox s = sender as CheckBox ?? throw new Exception("Sender is null");
int index = Convert.ToInt32(s.Tag);
MainWindow.OperationsList[index].IsOnlyIfNewer = false;
};
- FATFileTime = new CheckBox
- {
- Name = "FATtime" + operationIndex,
+ FATFileTime = new CheckBox {
+ Name = "FATtime",
Content = "FAT-Time",
IsChecked = MainWindow.OperationsList[operationIndex].IsOnlyIfNewer,
- ToolTip = "Use FAT-style time format when writing file. Useful when copying to another file system and when using \"only newer files\".",
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center,
Tag = operationIndex
};
- FATFileTime.Checked += (sender, e) =>
- {
+ FATFileTime.SetValue(ToolTip.TipProperty,
+ "Use FAT-style time format when writing file. Useful when copying to another file system and when using \"only newer files\".");
+ FATFileTime.Checked += (sender, e) => {
CheckBox s = sender as CheckBox ?? throw new Exception("Sender is null");
int index = Convert.ToInt32(s.Tag);
MainWindow.OperationsList[index].IsUseFATTime = true;
};
- FATFileTime.Unchecked += (sender, e) =>
- {
+ FATFileTime.Unchecked += (sender, e) => {
CheckBox s = sender as CheckBox ?? throw new Exception("Sender is null");
int index = Convert.ToInt32(s.Tag);
MainWindow.OperationsList[index].IsUseFATTime = false;
diff --git a/app.manifest b/app.manifest
new file mode 100644
index 0000000..670f345
--- /dev/null
+++ b/app.manifest
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/robocopy-gui.csproj b/robocopy-gui.csproj
index cde085a..cafcdc9 100644
--- a/robocopy-gui.csproj
+++ b/robocopy-gui.csproj
@@ -1,59 +1,43 @@
-
+ 2.0.0
+ 2.0.0
WinExe
- bin\
- net7.0-windows
- robocopy_gui
+ net6.0-windows10.0.18362.0
enable
- true
- true
+ true
+ app.manifest
+
true
- robocopy_gui.App
- 1.0.1
- 1.0.1.0
- True
- LICENSE.md
True
Assets\icon.ico
- Debug;Release
- AnyCPU;x64
+ true
+ AnyCPU
-
-
-
-
-
-
+
-
+
-
- True
- True
- Settings.settings
-
+
-
- True
- \
-
-
- SettingsSingleFileGenerator
- Settings.Designer.cs
-
+
+
+
+
+
-
+
+ DialogMessage.axaml
+
-
+
-
diff --git a/robocopy-gui.sln b/robocopy-gui.sln
index 8b5d2d4..2971cc6 100644
--- a/robocopy-gui.sln
+++ b/robocopy-gui.sln
@@ -1,30 +1,25 @@
+
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.5.33516.290
MinimumVisualStudioVersion = 10.0.40219.1
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "robocopy-gui", "robocopy-gui.csproj", "{A9E92172-C7C0-40B1-823C-02972BEE9053}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "robocopy-gui", "robocopy-gui.csproj", "{16648788-CDD9-4B3E-A2B9-90CCBBD4D977}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
- Debug|x64 = Debug|x64
Release|Any CPU = Release|Any CPU
- Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {A9E92172-C7C0-40B1-823C-02972BEE9053}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {A9E92172-C7C0-40B1-823C-02972BEE9053}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {A9E92172-C7C0-40B1-823C-02972BEE9053}.Debug|x64.ActiveCfg = Debug|x64
- {A9E92172-C7C0-40B1-823C-02972BEE9053}.Debug|x64.Build.0 = Debug|x64
- {A9E92172-C7C0-40B1-823C-02972BEE9053}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {A9E92172-C7C0-40B1-823C-02972BEE9053}.Release|Any CPU.Build.0 = Release|Any CPU
- {A9E92172-C7C0-40B1-823C-02972BEE9053}.Release|x64.ActiveCfg = Release|x64
- {A9E92172-C7C0-40B1-823C-02972BEE9053}.Release|x64.Build.0 = Release|x64
+ {16648788-CDD9-4B3E-A2B9-90CCBBD4D977}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {16648788-CDD9-4B3E-A2B9-90CCBBD4D977}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {16648788-CDD9-4B3E-A2B9-90CCBBD4D977}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {16648788-CDD9-4B3E-A2B9-90CCBBD4D977}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
- SolutionGuid = {2EC09D56-F89B-47B0-8162-FAE7CAC04727}
+ SolutionGuid = {53EDED3D-CFF1-447B-A542-FBFE771656F7}
EndGlobalSection
EndGlobal