Browse Source

Allow users to customise commands to launch explorer

Apparently some people use their own file browsers.

Fixes #348
Antony Male 8 years ago
parent
commit
ff062d60f7

+ 3 - 1
src/ProcessRunner/ProcessRunner.csproj

@@ -63,10 +63,12 @@
     <Reference Include="System.Xml" />
   </ItemGroup>
   <ItemGroup>
+    <Compile Include="..\SyncTrayzor\Utils\StringExtensions.cs">
+      <Link>StringExtensions.cs</Link>
+    </Compile>
     <Compile Include="Options.cs" />
     <Compile Include="Program.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
-    <Compile Include="StringExtensions.cs" />
   </ItemGroup>
   <ItemGroup>
     <None Include="App.config" />

+ 1 - 0
src/ProcessRunner/Program.cs

@@ -1,4 +1,5 @@
 using Mono.Options;
+using SyncTrayzor.Utils;
 using System;
 using System.ComponentModel;
 using System.Diagnostics;

+ 0 - 55
src/ProcessRunner/StringExtensions.cs

@@ -1,55 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-
-namespace ProcessRunner
-{
-    // Stolen from http://stackoverflow.com/a/298990/1086121
-    public static class StringExtensions
-    {
-        public static IEnumerable<string> SplitCommandLine(string commandLine)
-        {
-            bool inQuotes = false;
-
-            return commandLine.Split(c =>
-            {
-                if (c == '\"')
-                    inQuotes = !inQuotes;
-
-                return !inQuotes && c == ' ';
-            })
-                              .Select(arg => arg.Trim().TrimMatchingQuotes('\"'))
-                              .Where(arg => !string.IsNullOrEmpty(arg));
-        }
-
-        public static IEnumerable<string> Split(this string str, Func<char, bool> controller)
-        {
-            int nextPiece = 0;
-
-            for (int c = 0; c < str.Length; c++)
-            {
-                if (controller(str[c]))
-                {
-                    yield return str.Substring(nextPiece, c - nextPiece);
-                    nextPiece = c + 1;
-                }
-            }
-
-            yield return str.Substring(nextPiece);
-        }
-
-        public static string TrimMatchingQuotes(this string input, char quote)
-        {
-            if ((input.Length >= 2) &&
-                (input[0] == quote) && (input[input.Length - 1] == quote))
-                return input.Substring(1, input.Length - 2);
-
-            return input;
-        }
-
-        public static string JoinCommandLine(IEnumerable<string> input)
-        {
-            return String.Join(" ", input.Select(x => x.Contains(' ') ? $"\"{x.Replace("\"", "\\\"")}\"" : x));
-        }
-    }
-}

+ 2 - 0
src/SyncTrayzor/App.config

@@ -61,6 +61,8 @@
         <PauseDevicesOnMeteredNetworks>true</PauseDevicesOnMeteredNetworks>
         <HaveDonated>false</HaveDonated>
         <IconAnimationMode>DataTransferring</IconAnimationMode>
+        <OpenFolderCommand>explorer.exe "{0}"</OpenFolderCommand>
+        <ShowFileInFolderCommand>explorer.exe /select, "{0}"</ShowFileInFolderCommand>
       </DefaultUserConfiguration>
     </settings>
 

+ 1 - 1
src/SyncTrayzor/NotifyIcon/NotifyIconViewModel.cs

@@ -201,7 +201,7 @@ namespace SyncTrayzor.NotifyIcon
 
         public void Execute(object parameter)
         {
-            this.processStartProvider.StartDetached("explorer.exe", this.folder.Path);
+            this.processStartProvider.ShowFolderInExplorer(this.folder.Path);
         }
     }
 }

+ 1 - 1
src/SyncTrayzor/Pages/ConflictResolution/ConflictResolutionViewModel.cs

@@ -217,7 +217,7 @@ namespace SyncTrayzor.Pages.ConflictResolution
 
         public void ShowFileInFolder(ConflictViewModel conflict)
         {
-            this.processStartProvider.ShowInExplorer(conflict.FilePath);
+            this.processStartProvider.ShowFileInExplorer(conflict.FilePath);
         }
 
         public void Close()

+ 2 - 2
src/SyncTrayzor/Pages/FileTransfersTrayViewModel.cs

@@ -223,9 +223,9 @@ namespace SyncTrayzor.Pages
             if (fileTransfer.ActionType == ItemChangedActionType.Update)
             {
                 if (fileTransfer.ItemType == ItemChangedItemType.File)
-                    this.processStartProvider.ShowInExplorer(Path.Combine(folder.Path, fileTransfer.Path));
+                    this.processStartProvider.ShowFileInExplorer(Path.Combine(folder.Path, fileTransfer.Path));
                 else if (fileTransfer.ItemType == ItemChangedItemType.Dir)
-                    this.processStartProvider.StartDetached("explorer.exe", Path.Combine(folder.Path, fileTransfer.Path));
+                    this.processStartProvider.ShowFolderInExplorer(Path.Combine(folder.Path, fileTransfer.Path));
             }
         }
     }

+ 2 - 2
src/SyncTrayzor/Pages/Settings/SettingsViewModel.cs

@@ -409,12 +409,12 @@ namespace SyncTrayzor.Pages.Settings
 
         public void ShowSyncthingLogFile()
         {
-            this.processStartProvider.ShowInExplorer(Path.Combine(this.applicationPathsProvider.LogFilePath, "syncthing.log"));
+            this.processStartProvider.ShowFileInExplorer(Path.Combine(this.applicationPathsProvider.LogFilePath, "syncthing.log"));
         }
 
         public void ShowSyncTrayzorLogFile()
         {
-            this.processStartProvider.ShowInExplorer(Path.Combine(this.applicationPathsProvider.LogFilePath, "SyncTrayzor.log"));
+            this.processStartProvider.ShowFileInExplorer(Path.Combine(this.applicationPathsProvider.LogFilePath, "SyncTrayzor.log"));
         }
 
         public void SelectLoggingTab()

+ 1 - 1
src/SyncTrayzor/Pages/UnhandledExceptionViewModel.cs

@@ -57,7 +57,7 @@ namespace SyncTrayzor.Pages
 
         public void OpenLogFilePath()
         {
-            this.processStartProvider.ShowInExplorer(Path.Combine(this.applicationPathsProvider.LogFilePath, "SyncTrayzor.log"));
+            this.processStartProvider.ShowFileInExplorer(Path.Combine(this.applicationPathsProvider.LogFilePath, "SyncTrayzor.log"));
         }
 
         public void Close()

+ 1 - 1
src/SyncTrayzor/Pages/ViewerViewModel.cs

@@ -263,7 +263,7 @@ namespace SyncTrayzor.Pages
             if (!this.syncthingManager.Folders.TryFetchById(folderId, out folder))
                 return;
 
-            this.processStartProvider.StartDetached("explorer.exe", folder.Path);
+            this.processStartProvider.ShowFolderInExplorer(folder.Path);
         }
 
         private void BrowseFolderPath()

+ 8 - 1
src/SyncTrayzor/Services/Config/Configuration.cs

@@ -72,6 +72,8 @@ namespace SyncTrayzor.Services.Config
         public bool PauseDevicesOnMeteredNetworks { get; set; }
         public bool HaveDonated { get; set; }
         public IconAnimationMode IconAnimationMode { get; set; }
+        public string OpenFolderCommand { get; set; }
+        public string ShowFileInFolderCommand { get; set; }
 
         public Configuration()
         {
@@ -109,6 +111,8 @@ namespace SyncTrayzor.Services.Config
             this.PauseDevicesOnMeteredNetworks = true;
             this.HaveDonated = false;
             this.IconAnimationMode = IconAnimationMode.DataTransferring;
+            this.OpenFolderCommand = "explorer.exe \"{0}\"";
+            this.ShowFileInFolderCommand = "explorer.exe /select, \"{0}\"";
         }
 
         public Configuration(Configuration other)
@@ -145,6 +149,8 @@ namespace SyncTrayzor.Services.Config
             this.PauseDevicesOnMeteredNetworks = other.PauseDevicesOnMeteredNetworks;
             this.HaveDonated = other.HaveDonated;
             this.IconAnimationMode = other.IconAnimationMode;
+            this.OpenFolderCommand = other.OpenFolderCommand;
+            this.ShowFileInFolderCommand = other.ShowFileInFolderCommand;
         }
 
         public override string ToString()
@@ -162,7 +168,8 @@ namespace SyncTrayzor.Services.Config
                 $"DisableHardwareRendering={this.DisableHardwareRendering} EnableFailedTransferAlerts={this.EnableFailedTransferAlerts} " +
                 $"EnableConflictFileMonitoring={this.EnableConflictFileMonitoring} SyncthingDebugFacilities=[{String.Join(",", this.SyncthingDebugFacilities)}] "+
                 $"ConflictResolverDeletesToRecycleBin={this.ConflictResolverDeletesToRecycleBin} PauseDevicesOnMeteredNetworks={this.PauseDevicesOnMeteredNetworks} "+
-                $"HaveDonated={this.HaveDonated} IconAnimationMode={this.IconAnimationMode}>";
+                $"HaveDonated={this.HaveDonated} IconAnimationMode={this.IconAnimationMode} OpenFolderCommand={this.OpenFolderCommand} ShowFileInFolderCommand={this.ShowFileInFolderCommand}" +
+                $">";
         }
     }
 }

+ 33 - 3
src/SyncTrayzor/Services/ProcessStartProvider.cs

@@ -1,7 +1,10 @@
 using NLog;
+using SyncTrayzor.Services.Config;
+using SyncTrayzor.Utils;
 using System;
 using System.Diagnostics;
 using System.IO;
+using System.Linq;
 
 namespace SyncTrayzor.Services
 {
@@ -12,7 +15,8 @@ namespace SyncTrayzor.Services
         void StartDetached(string filename);
         void StartDetached(string filename, string arguments, string launchAfterFinished = null);
         void StartElevatedDetached(string filename, string arguments, string launchAfterFinished = null);
-        void ShowInExplorer(string filePath);
+        void ShowFolderInExplorer(string path);
+        void ShowFileInExplorer(string filePath);
     }
 
     public class ProcessStartProvider : IProcessStartProvider
@@ -21,9 +25,12 @@ namespace SyncTrayzor.Services
         private static readonly Logger logger = LogManager.GetCurrentClassLogger();
         private readonly string processRunnerPath;
 
-        public ProcessStartProvider(IAssemblyProvider assemblyProvider)
+        private readonly IConfigurationProvider configurationProvider;
+
+        public ProcessStartProvider(IAssemblyProvider assemblyProvider, IConfigurationProvider configurationProvider)
         {
             this.processRunnerPath = Path.Combine(Path.GetDirectoryName(assemblyProvider.Location), processRunner);
+            this.configurationProvider = configurationProvider;
         }
 
         public void Start(string filename)
@@ -84,6 +91,29 @@ namespace SyncTrayzor.Services
             Process.Start(startInfo);
         }
 
-        public void ShowInExplorer(string filePath) => this.StartDetached("explorer.exe", $"/select, \"{filePath}\"");
+        public void ShowFolderInExplorer(string path)
+        {
+            var command = this.configurationProvider.Load().OpenFolderCommand;
+            this.StartExplorerHelper(command, path);
+        }
+
+        public void ShowFileInExplorer(string filePath)
+        {
+            var command = this.configurationProvider.Load().ShowFileInFolderCommand;
+            this.StartExplorerHelper(command, filePath);
+        }
+
+        private void StartExplorerHelper(string commandFromConfig, string path)
+        {
+            var parts = StringExtensions.SplitCommandLine(String.Format(commandFromConfig, path));
+            var executable = parts.FirstOrDefault();
+            if (executable == null)
+            {
+                logger.Error($"Command {commandFromConfig} is badly formed, and does not contain an executable");
+                return;
+            }
+
+            this.StartDetached(executable, StringExtensions.JoinCommandLine(parts.Skip(1)));
+        }
     }
 }

+ 50 - 0
src/SyncTrayzor/Utils/StringExtensions.cs

@@ -14,5 +14,55 @@ namespace SyncTrayzor.Utils
                 return input.Substring(prefix.Length);
             return input;
         }
+
+        // Stolen from http://stackoverflow.com/a/298990/1086121
+        public static IEnumerable<string> SplitCommandLine(string commandLine)
+        {
+            bool inQuotes = false;
+
+            var split = commandLine.Split(c =>
+            {
+                if (c == '\"')
+                    inQuotes = !inQuotes;
+
+                return !inQuotes && c == ' ';
+            });
+
+            var result = split
+                .Select(arg => arg.Trim().TrimMatchingQuotes('\"'))
+                .Where(arg => !string.IsNullOrEmpty(arg));
+
+            return result;
+        }
+
+        public static IEnumerable<string> Split(this string str, Func<char, bool> controller)
+        {
+            int nextPiece = 0;
+
+            for (int c = 0; c < str.Length; c++)
+            {
+                if (controller(str[c]))
+                {
+                    yield return str.Substring(nextPiece, c - nextPiece);
+                    nextPiece = c + 1;
+                }
+            }
+
+            yield return str.Substring(nextPiece);
+        }
+
+        public static string TrimMatchingQuotes(this string input, char quote)
+        {
+            if ((input.Length >= 2) &&
+                (input[0] == quote) && (input[input.Length - 1] == quote))
+                return input.Substring(1, input.Length - 2);
+
+            return input;
+        }
+
+        public static string JoinCommandLine(IEnumerable<string> input)
+        {
+            return String.Join(" ", input.Select(x => x.Contains(' ') ? $"\"{x.Replace("\"", "\\\"")}\"" : x));
+        }
     }
 }