diff --git a/CHANGELOG.txt b/CHANGELOG.txt index d59b8bd7..f2323521 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,13 +1,26 @@ Changelog ========= +v1.0.16 +------- + + - Installer recommends Syncthing 0.11 (#64) + - Fix bad browser zoom after restart (#57) + - Fix display of folders which contain an underscore (#58) + - Handle duplicate devices/folders in Syncthing config (#61) + - Fix bad character encoding in Syncthing console (#62) + - Fix installer's handling of Syncthing version changes (#63) + - Clarify some UI wording/typos (#60, others) + - Remember size of Syncthing console (#56) + - Updated translations + v1.0.15 ------- - Fix crash on startup if Syncthing is slow to start (#55) - - Remember window size-position (#51) - - Zoom built-in borwser (#52) - - Add support for aribtrary environmental variables for Syncthing + - Remember window size/position (#51) + - Zoom built-in browser (#52) + - Add support for arbitrary environmental variables for Syncthing v1.0.14 ------- diff --git a/Rakefile b/Rakefile index f6d20bfb..763838a3 100644 --- a/Rakefile +++ b/Rakefile @@ -43,6 +43,8 @@ class ArchDirConfig end end +SYNCTHING_VERSIONS_TO_UPDATE = ['0.11'] + ARCH_CONFIG = [ArchDirConfig.new('x64'), ArchDirConfig.new('x86')] ASSEMBLY_INFOS = FileList['**/AssemblyInfo.cs'] @@ -166,7 +168,7 @@ namespace :"update-syncthing" do ARCH_CONFIG.each do |arch_config| desc "Update syncthing binaries (#{arch_config.arch}" task arch_config.arch do - arch_config.syncthing_binaries.values.each do |bin| + arch_config.syncthing_binaries.values_at(*SYNCTHING_VERSIONS_TO_UPDATE).each do |bin| path = File.join(arch_config.installer_dir, bin) raise "Could not find #{path}" unless File.exist?(path) sh path, '-upgrade' do; end diff --git a/installer/x64/installer-x64.iss b/installer/x64/installer-x64.iss index dbb63a3c..627eabc8 100644 --- a/installer/x64/installer-x64.iss +++ b/installer/x64/installer-x64.iss @@ -43,12 +43,13 @@ TouchDate=current Name: "english"; MessagesFile: "compiler:Default.isl" [CustomMessages] -SyncthingVersion=%nPlease select the Syncthing version.%n%n!! IMPORTANT !!%n%nv0.10 and v0.11 (beta) are incompatible. All of your devices must either use v0.10 or v0.11 (beta). +SyncthingVersion=%nPlease select the Syncthing version.%n%nv0.11 and v0.10 are incompatible. All of your devices must either use v0.11 or v0.10.%n [Tasks] Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked -Name: "syncthing0p10"; Description: "Syncthing v0.10 (recommended)"; GroupDescription: "{cm:SyncthingVersion}"; Flags: exclusive -Name: "syncthing0p11"; Description: "Syncthing v0.11 (beta)"; GroupDescription: "{cm:SyncthingVersion}"; Flags: exclusive unchecked +Name: "syncthing0p11"; Description: "Syncthing v0.11 (recommended)"; GroupDescription: "{cm:SyncthingVersion}"; Flags: exclusive +Name: "syncthing0p10"; Description: "Syncthing v0.10"; GroupDescription: "{cm:SyncthingVersion}"; Flags: exclusive unchecked + [Dirs] Name: "{userappdata}\{#AppDataFolder}" @@ -86,12 +87,32 @@ begin result := not exists or (release < 378758); end; +procedure BumpInstallCount; +var + fileContents: AnsiString; + installCount: integer; +begin + { Increment the install count in InstallCount.txt if it exists, or create it with the contents '1' if it doesn't } + if LoadStringFromFile(ExpandConstant('{app}\InstallCount.txt'), fileContents) then + begin + installCount := StrTointDef(Trim(string(fileContents)), 0) + 1; + end + else + begin + installCount := 1; + end; + + SaveStringToFile(ExpandConstant('{app}\InstallCount.txt'), IntToStr(installCount), False); +end; + procedure CurStepChanged(CurStep: TSetupStep); var ResultCode: integer; begin if CurStep = ssInstall then begin + BumpInstallCount(); + { We might be being run from ProcessRunner.exe, *and* we might be trying to update it. Funsies. Let's rename it (which Windows lets us do) } DeleteFile(ExpandConstant('{app}\ProcessRunner.exe.old')); RenameFile(ExpandConstant('{app}\ProcessRunner.exe'), ExpandConstant('{app}\ProcessRunner.exe.old')); @@ -114,5 +135,6 @@ end; [UninstallDelete] Type: files; Name: "{app}\ProcessRunner.exe.old" +Type: files; Name: "{app}\InstallCount.txt" Type: filesandordirs; Name: "{userappdata}\{#AppDataFolder}" Type: filesandordirs; Name: "{userappdata}\{#AppDataFolder}" \ No newline at end of file diff --git a/installer/x86/installer-x86.iss b/installer/x86/installer-x86.iss index f1859c78..99755da6 100644 --- a/installer/x86/installer-x86.iss +++ b/installer/x86/installer-x86.iss @@ -41,12 +41,12 @@ TouchDate=current Name: "english"; MessagesFile: "compiler:Default.isl" [CustomMessages] -SyncthingVersion=%nPlease select the Syncthing version.%n%n!! IMPORTANT !!%n%nv0.10 and v0.11 (beta) are incompatible. All of your devices must either use v0.10 or v0.11 (beta). +SyncthingVersion=%nPlease select the Syncthing version.%n%nv0.11 and v0.10 are incompatible. All of your devices must either use v0.11 or v0.10.%n [Tasks] Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked -Name: "syncthing0p10"; Description: "Syncthing v0.10 (recommended)"; GroupDescription: "{cm:SyncthingVersion}"; Flags: exclusive -Name: "syncthing0p11"; Description: "Syncthing v0.11 (beta)"; GroupDescription: "{cm:SyncthingVersion}"; Flags: exclusive unchecked +Name: "syncthing0p11"; Description: "Syncthing v0.11 (recommended)"; GroupDescription: "{cm:SyncthingVersion}"; Flags: exclusive +Name: "syncthing0p10"; Description: "Syncthing v0.10"; GroupDescription: "{cm:SyncthingVersion}"; Flags: exclusive unchecked [Dirs] Name: "{userappdata}\{#AppDataFolder}" @@ -84,12 +84,32 @@ begin result := not exists or (release < 378758); end; +procedure BumpInstallCount; +var + fileContents: AnsiString; + installCount: integer; +begin + { Increment the install count in InstallCount.txt if it exists, or create it with the contents '1' if it doesn't } + if LoadStringFromFile(ExpandConstant('{app}\InstallCount.txt'), fileContents) then + begin + installCount := StrTointDef(Trim(string(fileContents)), 0) + 1; + end + else + begin + installCount := 1; + end; + + SaveStringToFile(ExpandConstant('{app}\InstallCount.txt'), IntToStr(installCount), False); +end; + procedure CurStepChanged(CurStep: TSetupStep); var ResultCode: integer; begin if CurStep = ssInstall then begin + BumpInstallCount(); + { We might be being run from ProcessRunner.exe, *and* we might be trying to update it. Funsies. Let's rename it (which Windows lets us do) } DeleteFile(ExpandConstant('{app}\ProcessRunner.exe.old')); RenameFile(ExpandConstant('{app}\ProcessRunner.exe'), ExpandConstant('{app}\ProcessRunner.exe.old')); @@ -112,5 +132,6 @@ end; [UninstallDelete] Type: files; Name: "{app}\ProcessRunner.exe.old" +Type: files; Name: "{app}\InstallCount.txt" Type: filesandordirs; Name: "{userappdata}\{#AppDataFolder}" Type: filesandordirs; Name: "{userappdata}\{#AppDataFolder}" \ No newline at end of file diff --git a/server/version_check.php b/server/version_check.php index 3c433519..73da4fdf 100644 --- a/server/version_check.php +++ b/server/version_check.php @@ -65,6 +65,16 @@ function get_with_wildcard($src, $value, $default = null) } $versions = [ + '1.0.16' => [ + 'installed' => [ + 'direct_download_url' => [ + 'x64' => 'https://github.com/canton7/SyncTrayzor/releases/download/v1.0.16/SyncTrayzorSetup-x64.exe', + 'x86' => 'https://github.com/canton7/SyncTrayzor/releases/download/v1.0.16/SyncTrayzorSetup-x86.exe', + ], + ], + 'release_page_url' => 'https://github.com/canton7/SyncTrayzor/releases/tag/v1.0.16', + 'release_notes' => "- Installer recommends Syncthing 0.11 (#64)\n- Fix bad browser zoom after restart (#57)\n- Fix display of folders which contain an underscore (#58)\n- Handle duplicate devices/folders in Syncthing config (#61)\n- Fix bad character encoding in Syncthing console (#62)\n- Fix installer's handling of Syncthing version changes (#63)\n- Clarify some UI wording/typos (#60, others)\n- Remember size of Syncthing console (#56)\n- Updated translations", + ], '1.0.15' => [ 'installed' => [ 'direct_download_url' => [ @@ -73,14 +83,14 @@ function get_with_wildcard($src, $value, $default = null) ], ], 'release_page_url' => 'https://github.com/canton7/SyncTrayzor/releases/tag/v1.0.15', - 'release_notes' => "- Fix crash on startup if Syncthing is slow to start (#55)\n- Remember window size-position (#51)\n- Zoom built-in borwser (#52)\n- Add support for aribtrary environmental variables for Syncthing\n", + 'release_notes' => "- Fix crash on startup if Syncthing is slow to start (#55)\n- Remember window size/position (#51)\n- Zoom built-in browser (#52)\n- Add support for arbitrary environmental variables for Syncthing", ], '1.0.14' => [ 'installed' => [ 'direct_download_url' => [] ], 'release_page_url' => 'https://github.com/canton7/SyncTrayzor/releases/tag/v1.0.14', - 'release_notes' => "- Give Syncthing more than 10 seconds to start, fixing crash (#47, #48, #50)\n- Better Syncthing API management in general\n- Add support for 150% and 200% DPI to tray icon\n- Slightly improve UI\n", + 'release_notes' => "- Give Syncthing more than 10 seconds to start, fixing crash (#47, #48, #50)\n- Better Syncthing API management in general\n- Add support for 150% and 200% DPI to tray icon\n- Slightly improve UI", ], '1.0.13' => [ 'installed' => [ @@ -92,6 +102,7 @@ function get_with_wildcard($src, $value, $default = null) ]; $upgrades = [ + '1.0.15' => ['to' => 'latest', 'formatter' => '2'], '1.0.14' => ['to' => 'latest', 'formatter' => '2'], '1.0.13' => ['to' => 'latest', 'formatter' => '1'], '1.0.12' => ['to' => 'latest', 'formatter' => '1'], diff --git a/src/ProcessRunner/Properties/AssemblyInfo.cs b/src/ProcessRunner/Properties/AssemblyInfo.cs index acc6002f..5d39ad3e 100644 --- a/src/ProcessRunner/Properties/AssemblyInfo.cs +++ b/src/ProcessRunner/Properties/AssemblyInfo.cs @@ -32,5 +32,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.15.0")] -[assembly: AssemblyFileVersion("1.0.15.0")] +[assembly: AssemblyVersion("1.0.16.0")] +[assembly: AssemblyFileVersion("1.0.16.0")] diff --git a/src/SyncTrayzor/App.config b/src/SyncTrayzor/App.config index 16a10987..a5adc2eb 100644 --- a/src/SyncTrayzor/App.config +++ b/src/SyncTrayzor/App.config @@ -46,7 +46,7 @@ + xmlns:xsd="http://www.w3.org/2001/XMLSchema" Version="3"> false false true @@ -62,8 +62,9 @@ true true true - true + 100 0 + 0 diff --git a/src/SyncTrayzor/NotifyIcon/TaskbarIconResources.xaml b/src/SyncTrayzor/NotifyIcon/TaskbarIconResources.xaml index 82e8ed8d..fe27e759 100644 --- a/src/SyncTrayzor/NotifyIcon/TaskbarIconResources.xaml +++ b/src/SyncTrayzor/NotifyIcon/TaskbarIconResources.xaml @@ -3,7 +3,8 @@ xmlns:s="https://github.com/canton7/Stylet" xmlns:l="clr-namespace:SyncTrayzor.Localization" xmlns:ni="clr-namespace:SyncTrayzor.NotifyIcon" - xmlns:tb="http://www.hardcodet.net/taskbar"> + xmlns:tb="http://www.hardcodet.net/taskbar" + xmlns:xaml="clr-namespace:SyncTrayzor.Xaml"> @@ -79,7 +80,7 @@ Visibility="{Binding Folders, Converter={x:Static s:BoolToVisibilityConverter.Instance}}"> diff --git a/src/SyncTrayzor/Pages/SettingsView.xaml b/src/SyncTrayzor/Pages/SettingsView.xaml index 68695d4b..74cea72d 100644 --- a/src/SyncTrayzor/Pages/SettingsView.xaml +++ b/src/SyncTrayzor/Pages/SettingsView.xaml @@ -119,7 +119,10 @@ - + + + + diff --git a/src/SyncTrayzor/Pages/ShellView.xaml b/src/SyncTrayzor/Pages/ShellView.xaml index 96fd9ce2..1b318d3a 100644 --- a/src/SyncTrayzor/Pages/ShellView.xaml +++ b/src/SyncTrayzor/Pages/ShellView.xaml @@ -53,22 +53,23 @@ - + - + - + - + diff --git a/src/SyncTrayzor/Pages/ShellViewModel.cs b/src/SyncTrayzor/Pages/ShellViewModel.cs index bec55f82..5db48bee 100644 --- a/src/SyncTrayzor/Pages/ShellViewModel.cs +++ b/src/SyncTrayzor/Pages/ShellViewModel.cs @@ -27,6 +27,7 @@ public class ShellViewModel : Screen public bool WindowActivated { get; set; } public bool ShowConsole { get; set; } + public double ConsoleHeight { get; set; } public WindowPlacement Placement { get; set; } public ConsoleViewModel Console { get; private set; } public ViewerViewModel Viewer { get; private set; } @@ -62,8 +63,14 @@ public ShellViewModel( this.syncThingManager.StateChanged += (o, e) => this.SyncThingState = e.NewState; this.syncThingManager.ProcessExitedWithError += (o, e) => this.ShowExitedWithError(); - this.ShowConsole = configuration.ShowSyncthingConsole; - this.Bind(s => s.ShowConsole, (o, e) => this.configurationProvider.AtomicLoadAndSave(c => c.ShowSyncthingConsole = e.NewValue)); + this.ConsoleHeight = configuration.SyncthingConsoleHeight; + this.Bind(s => s.ConsoleHeight, (o, e) => this.configurationProvider.AtomicLoadAndSave(c => c.SyncthingConsoleHeight = e.NewValue)); + + this.ShowConsole = configuration.SyncthingConsoleHeight > 0; + this.Bind(s => s.ShowConsole, (o, e) => + { + this.ConsoleHeight = e.NewValue ? Configuration.DefaultSyncthingConsoleHeight : 0.0; + }); this.Placement = configuration.WindowPlacement; this.Bind(s => s.Placement, (o, e) => this.configurationProvider.AtomicLoadAndSave(c => c.WindowPlacement = e.NewValue)); diff --git a/src/SyncTrayzor/Pages/ViewerViewModel.cs b/src/SyncTrayzor/Pages/ViewerViewModel.cs index 4d0aac60..39e49711 100644 --- a/src/SyncTrayzor/Pages/ViewerViewModel.cs +++ b/src/SyncTrayzor/Pages/ViewerViewModel.cs @@ -29,6 +29,7 @@ public class ViewerViewModel : Screen, IRequestHandler, ILifeSpanHandler private readonly object cultureLock = new object(); // This can be read from many threads private CultureInfo culture; + private double zoomLevel; public string Location { get; private set; } @@ -36,7 +37,7 @@ public class ViewerViewModel : Screen, IRequestHandler, ILifeSpanHandler public bool ShowSyncThingStarting { get { return this.syncThingState == SyncThingState.Starting; } } public bool ShowSyncThingStopped { get { return this.syncThingState == SyncThingState.Stopped; ; } } - public IWpfWebBrowser WebBrowser { get; set; } + public ChromiumWebBrowser WebBrowser { get; set; } private JavascriptCallbackObject callback; @@ -51,6 +52,9 @@ public ViewerViewModel( this.processStartProvider = processStartProvider; this.configurationProvider = configurationProvider; + var configuration = this.configurationProvider.Load(); + this.zoomLevel = configuration.SyncthingWebBrowserZoomLevel; + this.syncThingManager.StateChanged += (o, e) => { this.syncThingState = e.NewState; @@ -65,7 +69,7 @@ public ViewerViewModel( this.InitializeBrowser(e.NewValue); }); - this.SetCulture(configurationProvider.Load()); + this.SetCulture(configuration); configurationProvider.ConfigurationChanged += (o, e) => this.SetCulture(e.NewConfiguration); } @@ -85,11 +89,25 @@ protected override void OnInitialActivate() }); } - private void InitializeBrowser(IWpfWebBrowser webBrowser) + private void InitializeBrowser(ChromiumWebBrowser webBrowser) { webBrowser.RequestHandler = this; webBrowser.LifeSpanHandler = this; webBrowser.RegisterJsObject("callbackObject", this.callback); + + // So. Fun story. From https://github.com/cefsharp/CefSharp/issues/738#issuecomment-91099199, we need to set the zoom level + // in the FrameLoadStart event. However, the IWpfWebBrowser's ZoomLevel is a DependencyProperty, and it wraps + // the SetZoomLevel method on the unmanaged browser (which is exposed directly by ChromiumWebBrowser, but not by IWpfWebBrowser). + // Now, FrameLoadState and FrameLoadEnd are called on a background thread, and since ZoomLevel is a DP, it can only be changed + // from the UI thread (it's "helpful" and does a dispatcher check for us). But, if we dispatch back to the UI thread to call + // ZoomLevel = xxx, then CEF seems to hit threading issues, and can sometimes render things entirely badly (massive icons, no + // localization, bad spacing, no JavaScript at all, etc). + // So, in this case, we need to call SetZoomLevel directly, as we can do that from the thread on which FrameLoadStart is called, + // and everything's happy. + // However, this means that the DP value isn't updated... Which means we can't use the DP at all. We have to call SetZoomLevel + // *everywhere*, and that means keeping a local field zoomLevel to track the current zoom level. Such is life + + webBrowser.FrameLoadStart += (o, e) => webBrowser.SetZoomLevel(this.zoomLevel); webBrowser.FrameLoadEnd += (o, e) => { if (e.IsMainFrame && e.Url != "about:blank") @@ -103,7 +121,6 @@ private void InitializeBrowser(IWpfWebBrowser webBrowser) webBrowser.ExecuteScriptAsync(script); } }; - WebBrowser.ZoomLevel = this.configurationProvider.Load().SyncthingWebBrowserZoomLevel; } public void RefreshBrowser() @@ -115,30 +132,27 @@ public void RefreshBrowser() public void ZoomIn() { - this.ZoomBy(0.2); + this.ZoomTo(this.zoomLevel + 0.2); } public void ZoomOut() { - this.ZoomBy(-0.2); + this.ZoomTo(this.zoomLevel - 0.2); } - private void ZoomBy(double amount) + public void ZoomReset() { - if (this.WebBrowser == null || this.syncThingState != SyncThingState.Running) - return; - - this.WebBrowser.ZoomLevel += amount; - this.configurationProvider.AtomicLoadAndSave(c => c.SyncthingWebBrowserZoomLevel = this.WebBrowser.ZoomLevel); + this.ZoomTo(0.0); } - public void ZoomReset() + private void ZoomTo(double zoomLevel) { if (this.WebBrowser == null || this.syncThingState != SyncThingState.Running) return; - this.WebBrowser.ZoomLevel = 0; - this.configurationProvider.AtomicLoadAndSave(c => c.SyncthingWebBrowserZoomLevel = this.WebBrowser.ZoomLevel); + this.zoomLevel = zoomLevel; + this.WebBrowser.SetZoomLevel(zoomLevel); + this.configurationProvider.AtomicLoadAndSave(c => c.SyncthingWebBrowserZoomLevel = zoomLevel); } private void OpenFolder(string folderId) diff --git a/src/SyncTrayzor/Properties/AssemblyInfo.cs b/src/SyncTrayzor/Properties/AssemblyInfo.cs index e47065ae..28a54390 100644 --- a/src/SyncTrayzor/Properties/AssemblyInfo.cs +++ b/src/SyncTrayzor/Properties/AssemblyInfo.cs @@ -51,5 +51,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.15.0")] -[assembly: AssemblyFileVersion("1.0.15.0")] +[assembly: AssemblyVersion("1.0.16.0")] +[assembly: AssemblyFileVersion("1.0.16.0")] diff --git a/src/SyncTrayzor/Properties/Settings.Designer.cs b/src/SyncTrayzor/Properties/Settings.Designer.cs index 972455ff..0bf7d81e 100644 --- a/src/SyncTrayzor/Properties/Settings.Designer.cs +++ b/src/SyncTrayzor/Properties/Settings.Designer.cs @@ -116,7 +116,7 @@ public int SyncthingConnectTimeoutSeconds { [global::System.Configuration.ApplicationScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute(@" - + false false true @@ -132,8 +132,9 @@ public int SyncthingConnectTimeoutSeconds { true true true - true + 100 0 + 0 ")] public global::SyncTrayzor.Services.Config.Configuration DefaultUserConfiguration { get { diff --git a/src/SyncTrayzor/Properties/Settings.settings b/src/SyncTrayzor/Properties/Settings.settings index c90e592e..bacafcd3 100644 --- a/src/SyncTrayzor/Properties/Settings.settings +++ b/src/SyncTrayzor/Properties/Settings.settings @@ -34,7 +34,7 @@ <?xml version="1.0" encoding="utf-16"?> -<Configuration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" Version="2"> +<Configuration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" Version="3"> <ShowTrayIconOnlyOnClose>false</ShowTrayIconOnlyOnClose> <MinimizeToTray>false</MinimizeToTray> <CloseToTray>true</CloseToTray> @@ -50,8 +50,9 @@ <NotifyOfNewVersions>true</NotifyOfNewVersions> <ObfuscateDeviceIDs>true</ObfuscateDeviceIDs> <UseComputerCulture>true</UseComputerCulture> - <ShowSyncthingConsole>true</ShowSyncthingConsole> + <SyncthingConsoleHeight>100</SyncthingConsoleHeight> <SyncthingWebBrowserZoomLevel>0</SyncthingWebBrowserZoomLevel> + <LastSeenInstallCount>0</LastSeenInstallCount> </Configuration> diff --git a/src/SyncTrayzor/Properties/Strings/Resources.Designer.cs b/src/SyncTrayzor/Properties/Strings/Resources.Designer.cs index 68cd8f6f..67e85a9f 100644 --- a/src/SyncTrayzor/Properties/Strings/Resources.Designer.cs +++ b/src/SyncTrayzor/Properties/Strings/Resources.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.18444 +// Runtime Version:4.0.30319.0 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. @@ -636,7 +636,7 @@ public static string SettingsView_Validation_NotShouldBeEmpty { } /// - /// Looks up a localized string similar to Must be in the format 'VAR=value VAR2="val1 vla2"'. + /// Looks up a localized string similar to Must be in the format 'VAR=value VAR2="val1 val2"'. /// public static string SettingsView_Validation_SyncthingEnvironmentalVariablesMustHaveFormat { get { @@ -645,7 +645,7 @@ public static string SettingsView_Validation_SyncthingEnvironmentalVariablesMust } /// - /// Looks up a localized string similar to The following folders will be watched for changes, which avoids polling.. + /// Looks up a localized string similar to The following folders will be watched for changes, which means you can set a longer polling interval.. /// public static string SettingsView_WatchedFolders_Explanation { get { diff --git a/src/SyncTrayzor/Properties/Strings/Resources.cs.resx b/src/SyncTrayzor/Properties/Strings/Resources.cs.resx index 8b3a016b..e9357053 100644 --- a/src/SyncTrayzor/Properties/Strings/Resources.cs.resx +++ b/src/SyncTrayzor/Properties/Strings/Resources.cs.resx @@ -167,7 +167,7 @@ _Nastavení - P_omoc + _Pomoc _O programu @@ -305,11 +305,11 @@ Nastavení Title of the 'settings' dialog - - Proměnné sledování Syncthing: + + Proměnné prostředí Syncthing: - V následujících adresářích budou sledovány změny, což zabrání opakovanému skenování. + V následujících adresářích budou sledovány změny, tudíž pro ně můžete nastavit delší interval opakování skenování. Adresáře nejsou dostupné. Spusťte prosím Syncthing @@ -557,4 +557,7 @@ Je nám líto, ale SyncTrayzor se musí ukončit. _Resetovat + + Musí být ve formátu 'PROM=hodnota PROM2="hodnota1 hodnota2"' + diff --git a/src/SyncTrayzor/Properties/Strings/Resources.de.resx b/src/SyncTrayzor/Properties/Strings/Resources.de.resx index 0df903ea..2fac1059 100644 --- a/src/SyncTrayzor/Properties/Strings/Resources.de.resx +++ b/src/SyncTrayzor/Properties/Strings/Resources.de.resx @@ -121,7 +121,7 @@ Erstellt von Antony Male. - Herunterladen + Herunterladen Seite öffnen Clickable link allowing the user to download the latest version of SyncTrayzor @@ -198,7 +198,7 @@ When a new version is available, the user receives a prompt. This button allows the user to say they want want to be prompted to upgrade to this particular version again - Jetzt herunterladen + Herunterladen Seite öffnen When a new version is available, the user receives a prompt. This button allows them to download the new version @@ -305,11 +305,11 @@ Einstellungen Title of the 'settings' dialog - - Syncthing Trace-Variablen: + + Syncthing Umgebungsvariablen: - Die folgenden Ordner werden auf Änderungen überwacht. Dies wiederum verhindert unnötige Abfragen. + The following folders will be watched for changes, which means you can set a longer polling interval. Ordner nicht verfügbar, bitte Syncthing starten. @@ -545,4 +545,19 @@ SyncTrayzor wird nun geschlossen. Änderungshistorie anzeigen + + _Zoom Browser + + + Hineinzoomen + + + Herauszoomen + + + Zurücksetzen + + + Muss im folgenden Format sein 'VAR=value VAR2="val1 val2"' + diff --git a/src/SyncTrayzor/Properties/Strings/Resources.el.resx b/src/SyncTrayzor/Properties/Strings/Resources.el.resx index d709a8ad..2f7c1c91 100644 --- a/src/SyncTrayzor/Properties/Strings/Resources.el.resx +++ b/src/SyncTrayzor/Properties/Strings/Resources.el.resx @@ -121,7 +121,7 @@ Δημιουργήθηκε από τον Antony Male. - Λήψη + Άνοιγμα σελίδας λήψης Clickable link allowing the user to download the latest version of SyncTrayzor @@ -198,7 +198,7 @@ When a new version is available, the user receives a prompt. This button allows the user to say they want want to be prompted to upgrade to this particular version again - Λήψη τώρα + Άνοιγμα σελίδας λήψης When a new version is available, the user receives a prompt. This button allows them to download the new version @@ -305,11 +305,11 @@ Ρυθμίσεις Title of the 'settings' dialog - - Syncthing ανίχνευση μεταβλητών: + + Syncthing περιβαλλοντικές μεταβλητές: - Οι ακόλουθοι φάκελοι θα πρέπει να παρακολουθούνται για αλλαγές, πράγμα που αποτρέπει την ψηφοφορία. + The following folders will be watched for changes, which means you can set a longer polling interval. Φάκελοι μη διαθέσιμη. Παρακαλούμε ξεκινήστε το Syncthing @@ -545,4 +545,19 @@ The menu option "Syncthing ->Kill all Syncthing processes" corresponds to She Εμφάνιση αρχείου καταγραφής αλλαγών + + _Zoom Browser + + + Μεγέθυνση + + + Σμίκρυνση + + + Eπαναφορά + + + Πρέπει να είναι στη μορφή 'VAR=value VAR2="val1 val2"' + diff --git a/src/SyncTrayzor/Properties/Strings/Resources.es.resx b/src/SyncTrayzor/Properties/Strings/Resources.es.resx index a0992408..1ec58bb7 100644 --- a/src/SyncTrayzor/Properties/Strings/Resources.es.resx +++ b/src/SyncTrayzor/Properties/Strings/Resources.es.resx @@ -121,7 +121,7 @@ Creado por Antony Male. - Open Download Page + Abrir la página de descargas Clickable link allowing the user to download the latest version of SyncTrayzor @@ -185,7 +185,7 @@ _Abrir en navegador externo - _Refrescar navegador + Re_frescar navegador _Comenzar @@ -198,7 +198,7 @@ When a new version is available, the user receives a prompt. This button allows the user to say they want want to be prompted to upgrade to this particular version again - Open Download Page + Abrir la página de descargas When a new version is available, the user receives a prompt. This button allows them to download the new version @@ -213,7 +213,7 @@ When a new version is available, the user receives a prompt. This message tells the user how to permanently disable new version prompts. 'The Settings page' corresponds to ShellView_Menu_File. 'Help ->About' corresponds to ShellView_Menu_Help_About. - SyncTrayzor version {0} is available! + ¡La versión {0} de SyncTrayzor está disponible! When a new version is available, the user receives a prompt. This text appears at the top of that prompt @@ -239,7 +239,7 @@ Setting allowing the user to indicate that the 'minimize' button on the top right of the window should put Syncthing down into the tray, rather than minimizing the application - Debes reiniciar Syncthing para que los nuevos cambios surtan efecto. + Debes reiniciar Syncthing para que los nuevos cambios surtan efecto Ofuscar el ID de dispositivo en la ventana de log @@ -252,7 +252,7 @@ Section header for advanced settings - iniciar al hacer login + Iniciar al hacer login Section header for settings to do with automatically starting SyncTrayzor when the user logs in @@ -264,7 +264,7 @@ Section header for settings to do with configuring SyncTrayzor - Carpetas Vigiladas + Carpetas Supervisadas Section header for settings to do with configuring SyncTrayzor's ability to watch folders for changes @@ -295,7 +295,7 @@ ToolTip shown when a user hovers over the SettingsView_SyncthingAddress text input. This shows more information on the input. - Usar el directorio principal para Syncthing. + Usar el directorio principal para Syncthing Esto significa que SyncTrayzor verá un conjunto diferente de carpetas configuradas con respecto a cualquier otra instancia de Syncthing en tu ordenador. @@ -305,11 +305,11 @@ Preferencias Title of the 'settings' dialog - - Variables de trazado de Syncthing: + + Variables de Entorno de Syncthing: - Las siguiente carpetas estarán vigiladas durante los cambios, lo cual evita el muestreo. + Las siguientes carpetas estarán supervisadas por si cambian, lo que significa que puedes configurar un intervalo de consulta más largo. Carpetas no disponibles. Por favor inicia Syncthing @@ -531,30 +531,33 @@ SyncTrayzor se va a cerrar. Lo sentimos. Link show in the centre of the screen when Syncthing is stopped, allowing the user to start syncthing - Usar el idioma de mi ordenador, si está disponible. Es necesario reiniciar SyncTrayzor para que el cambio surta efecto. + Usar el idioma de mi ordenador si está disponible. Es necesario reiniciar SyncTrayzor para que el cambio surta efecto - _View + _Ver - Syncthing Console + _Mostrar la consola de Syncthing - Install Now + Instalar ahora - View Changelog + Ver lista de cambios - _Zoom Browser + _Zoom del navegador - Zoom _In + _Acercar - Zoom _Out + Al_ejar - _Reset + _Restaurar + + + Debe estar en el formato 'VAR=valor VAR2="val1 val2"' diff --git a/src/SyncTrayzor/Properties/Strings/Resources.fr.resx b/src/SyncTrayzor/Properties/Strings/Resources.fr.resx index 6dbcab3b..61468788 100644 --- a/src/SyncTrayzor/Properties/Strings/Resources.fr.resx +++ b/src/SyncTrayzor/Properties/Strings/Resources.fr.resx @@ -179,7 +179,7 @@ _Arrêter - Arrêter _tous les processus Syncthing + Arrêter _tous les processus Syncthing _Ouvrir dans le navigateur externe @@ -305,11 +305,11 @@ Paramètres Title of the 'settings' dialog - - Syncthing Trace Variables: + + Variables d’environnement de Syncthing : - The following folders will be watched for changes, which avoids polling. + The following folders will be watched for changes, which means you can set a longer polling interval. Dossiers indisponibles. Veuillez démarrer Syncthing @@ -537,7 +537,7 @@ SyncTrayzor doit se fermer. Veuillez nous excuser. _Vue - Syncthing Console + Afficher la console de Syncthing Installer maintenant @@ -546,15 +546,18 @@ SyncTrayzor doit se fermer. Veuillez nous excuser. View Changelog - _Zoom Browser + Zoom - Zoom _In + Zoom _avant - Zoom _Out + Zoom _arrière - _Reset + _Normal + + + Doit être dans le format suivant : 'VAR=value VAR2="val1 val2"' diff --git a/src/SyncTrayzor/Properties/Strings/Resources.nl.resx b/src/SyncTrayzor/Properties/Strings/Resources.nl.resx index 0e67914d..f017e9aa 100644 --- a/src/SyncTrayzor/Properties/Strings/Resources.nl.resx +++ b/src/SyncTrayzor/Properties/Strings/Resources.nl.resx @@ -305,11 +305,11 @@ Instellingen Title of the 'settings' dialog - - Syncthing-opsporingsvariabelen: + + Syncthing Environmental Variables: - De volgende mappen zullen in de gaten worden gehouden op wijzigingen: + The following folders will be watched for changes, which means you can set a longer polling interval. Geen mappen beschikbaar. Start Syncthing. @@ -557,4 +557,7 @@ SyncTrayzor gaat nu afsluiten. Onze excuses. _Reset + + Must be in the format 'VAR=value VAR2="val1 val2"' + diff --git a/src/SyncTrayzor/Properties/Strings/Resources.resx b/src/SyncTrayzor/Properties/Strings/Resources.resx index d619aa7f..29ead54e 100644 --- a/src/SyncTrayzor/Properties/Strings/Resources.resx +++ b/src/SyncTrayzor/Properties/Strings/Resources.resx @@ -309,7 +309,7 @@ Syncthing Environmental Variables: - The following folders will be watched for changes, which avoids polling. + The following folders will be watched for changes, which means you can set a longer polling interval. Folders unavailable. Please start Syncthing @@ -558,6 +558,6 @@ SyncTrayzor is going to have to close. Sorry about that _Reset - Must be in the format 'VAR=value VAR2="val1 vla2"' + Must be in the format 'VAR=value VAR2="val1 val2"' \ No newline at end of file diff --git a/src/SyncTrayzor/Properties/Strings/Resources.ru.resx b/src/SyncTrayzor/Properties/Strings/Resources.ru.resx index 03f7e27c..46ccd167 100644 --- a/src/SyncTrayzor/Properties/Strings/Resources.ru.resx +++ b/src/SyncTrayzor/Properties/Strings/Resources.ru.resx @@ -121,7 +121,7 @@ Создано Antony Male. - Загрузить + Open Download Page Clickable link allowing the user to download the latest version of SyncTrayzor @@ -198,7 +198,7 @@ When a new version is available, the user receives a prompt. This button allows the user to say they want want to be prompted to upgrade to this particular version again - Загрузить сейчас + Open Download Page When a new version is available, the user receives a prompt. This button allows them to download the new version @@ -305,11 +305,11 @@ Настройки Title of the 'settings' dialog - - Отслеживаемые переменные Syncthing: + + Syncthing Environmental Variables: - Изменения будут отслеживаться в следующих папках. + The following folders will be watched for changes, which means you can set a longer polling interval. Папки недоступны. Запустите Syncthing @@ -545,4 +545,19 @@ SyncTrayzor закрывается. Извините. Посмотреть список изменений + + _Zoom Browser + + + Zoom _In + + + Zoom _Out + + + _Reset + + + Must be in the format 'VAR=value VAR2="val1 val2"' + diff --git a/src/SyncTrayzor/Properties/Strings/Resources.sk.resx b/src/SyncTrayzor/Properties/Strings/Resources.sk.resx index 3876063e..e70e3e7c 100644 --- a/src/SyncTrayzor/Properties/Strings/Resources.sk.resx +++ b/src/SyncTrayzor/Properties/Strings/Resources.sk.resx @@ -121,7 +121,7 @@ Vytvoril Anthony Male. - Open Download Page + Otvoriť stránku na stiahnutie Clickable link allowing the user to download the latest version of SyncTrayzor @@ -198,7 +198,7 @@ When a new version is available, the user receives a prompt. This button allows the user to say they want want to be prompted to upgrade to this particular version again - Open Download Page + Otvoriť stránku na stiahnutie When a new version is available, the user receives a prompt. This button allows them to download the new version @@ -305,11 +305,11 @@ Nastavenia Title of the 'settings' dialog - - Stopové premenné Syncthing: + + Premenné prostredia Syncthing: - Nasledovné priečinky budú sledované kvôli zmenám, čo predchádza dopytovaniu. + The following folders will be watched for changes, which means you can set a longer polling interval. Priečinky nie sú dostupné. Prosím zapnite Syncthing @@ -546,15 +546,18 @@ SyncTrayzor sa musí zavrieť. Ospravedlňujeme sa Zobraziť zoznam zmien - _Zoom Browser + _Priblížiť prehliadač - Zoom _In + Pr_iblížiť - Zoom _Out + _Oddialiť _Reset + + Musí byť vo formáte 'VAR=hodnota VAR2="premenná1 premenná2"' + diff --git a/src/SyncTrayzor/Services/Config/ApplicationPathsProvider.cs b/src/SyncTrayzor/Services/Config/ApplicationPathsProvider.cs index a5c30a89..02501ed8 100644 --- a/src/SyncTrayzor/Services/Config/ApplicationPathsProvider.cs +++ b/src/SyncTrayzor/Services/Config/ApplicationPathsProvider.cs @@ -17,6 +17,7 @@ public interface IApplicationPathsProvider string ConfigurationFilePath { get; } string ConfigurationFileBackupPath { get; } string UpdatesDownloadPath { get; } + string InstallCountFilePath { get; } void Initialize(PathConfiguration pathConfiguration); } @@ -84,5 +85,10 @@ public string UpdatesDownloadPath { get { return Path.Combine(Path.GetTempPath(), "SyncTrayzor"); } } + + public string InstallCountFilePath + { + get { return Path.Combine(this.ExePath, "InstallCount.txt"); } + } } } diff --git a/src/SyncTrayzor/Services/Config/Configuration.cs b/src/SyncTrayzor/Services/Config/Configuration.cs index a750b870..1815a999 100644 --- a/src/SyncTrayzor/Services/Config/Configuration.cs +++ b/src/SyncTrayzor/Services/Config/Configuration.cs @@ -74,7 +74,8 @@ public void WriteXml(XmlWriter writer) [XmlRoot("Configuration")] public class Configuration { - public const int CurrentVersion = 2; + public const int CurrentVersion = 3; + public const double DefaultSyncthingConsoleHeight = 100; [XmlAttribute("Version")] public int Version @@ -116,9 +117,10 @@ public string LatestNotifiedVersionRaw } public bool UseComputerCulture { get; set; } - public bool ShowSyncthingConsole { get; set; } + public double SyncthingConsoleHeight { get; set; } public WindowPlacement WindowPlacement { get; set; } public double SyncthingWebBrowserZoomLevel { get; set; } + public int LastSeenInstallCount { get; set; } public Configuration() { @@ -141,9 +143,10 @@ public Configuration() this.ObfuscateDeviceIDs = true; this.LatestNotifiedVersion = null; this.UseComputerCulture = true; - this.ShowSyncthingConsole = true; + this.SyncthingConsoleHeight = Configuration.DefaultSyncthingConsoleHeight; this.WindowPlacement = null; this.SyncthingWebBrowserZoomLevel = 0; + this.LastSeenInstallCount = 0; } public Configuration(Configuration other) @@ -165,9 +168,10 @@ public Configuration(Configuration other) this.ObfuscateDeviceIDs = other.ObfuscateDeviceIDs; this.LatestNotifiedVersion = other.LatestNotifiedVersion; this.UseComputerCulture = other.UseComputerCulture; - this.ShowSyncthingConsole = other.ShowSyncthingConsole; + this.SyncthingConsoleHeight = other.SyncthingConsoleHeight; this.WindowPlacement = other.WindowPlacement; this.SyncthingWebBrowserZoomLevel = other.SyncthingWebBrowserZoomLevel; + this.LastSeenInstallCount = other.LastSeenInstallCount; } public override string ToString() @@ -175,13 +179,13 @@ public override string ToString() return String.Format("", + "LastNotifiedVersion={14} ObfuscateDeviceIDs={15} UseComputerCulture={16} SyncthingConsoleHeight={17} WindowPlacement={18} " + + "SyncthingWebBrowserZoomLevel={19} LastSeenInstallCount={20}>", this.ShowTrayIconOnlyOnClose, this.MinimizeToTray, this.CloseToTray, this.ShowSynchronizedBalloon, this.ShowDeviceConnectivityBalloons, this.SyncthingAddress, this.StartSyncthingAutomatically, this.SyncthingApiKey, String.Join(" ", this.SyncthingEnvironmentalVariables.Select(x => String.Format("{0}={1}", x.Key, x.Value))), this.SyncthingUseCustomHome, this.SyncthingDenyUpgrade, this.SyncthingRunLowPriority, String.Join(", ", this.Folders), this.NotifyOfNewVersions, - this.LatestNotifiedVersion, this.ObfuscateDeviceIDs, this.UseComputerCulture, this.ShowSyncthingConsole, this.WindowPlacement, - this.SyncthingWebBrowserZoomLevel); + this.LatestNotifiedVersion, this.ObfuscateDeviceIDs, this.UseComputerCulture, this.SyncthingConsoleHeight, this.WindowPlacement, + this.SyncthingWebBrowserZoomLevel, this.LastSeenInstallCount); } } } diff --git a/src/SyncTrayzor/Services/Config/ConfigurationProvider.cs b/src/SyncTrayzor/Services/Config/ConfigurationProvider.cs index 8efaa234..144e8c0b 100644 --- a/src/SyncTrayzor/Services/Config/ConfigurationProvider.cs +++ b/src/SyncTrayzor/Services/Config/ConfigurationProvider.cs @@ -67,7 +67,8 @@ public ConfigurationProvider(IApplicationPathsProvider paths) this.migrations = new Func[] { - this.MigrateV1ToV2 + this.MigrateV1ToV2, + this.MigrateV2ToV3, }; } @@ -79,25 +80,60 @@ public void Initialize(Configuration defaultConfiguration) if (!File.Exists(Path.GetDirectoryName(this.paths.ConfigurationFilePath))) Directory.CreateDirectory(Path.GetDirectoryName(this.paths.ConfigurationFilePath)); - if (!File.Exists(this.paths.SyncthingPath)) + this.currentConfig = this.LoadFromDisk(defaultConfiguration); + + bool installCountChanged = false; + bool updateConfigInstallCount = false; + int latestInstallCount = 0; + // Might be portable, in which case this file won't exist + if (File.Exists(this.paths.InstallCountFilePath)) { - if (File.Exists(this.paths.SyncthingBackupPath)) + latestInstallCount = Int32.Parse(File.ReadAllText(this.paths.InstallCountFilePath).Trim()); + if (latestInstallCount != this.currentConfig.LastSeenInstallCount) { - logger.Info("Syncthing doesn't exist at {0}, so copying from {1}", this.paths.SyncthingPath, this.paths.SyncthingBackupPath); - File.Copy(this.paths.SyncthingBackupPath, this.paths.SyncthingPath); + logger.Debug("InstallCount changed from {0} to {1}", this.currentConfig.LastSeenInstallCount, latestInstallCount); + installCountChanged = true; + updateConfigInstallCount = true; } - else - throw new Exception(String.Format("Unable to find Syncthing at {0} or {1}", this.paths.SyncthingPath, this.paths.SyncthingBackupPath)); } - else if (this.paths.SyncthingPath != this.paths.SyncthingBackupPath && File.Exists(this.paths.SyncthingBackupPath) && - File.GetLastWriteTimeUtc(this.paths.SyncthingPath) < File.GetLastWriteTimeUtc(this.paths.SyncthingBackupPath)) + + // They're the same if we're portable, in which case, nothing to do + if (this.paths.SyncthingPath != this.paths.SyncthingBackupPath) { - logger.Info("Syncthing at {0} is older ({1}) than at {2} ({3}, so overwriting from backup", - this.paths.SyncthingPath, File.GetLastWriteTimeUtc(this.paths.SyncthingPath), this.paths.SyncthingBackupPath, File.GetLastWriteTimeUtc(this.paths.SyncthingBackupPath)); - File.Copy(this.paths.SyncthingBackupPath, this.paths.SyncthingPath, true); + if (!File.Exists(this.paths.SyncthingPath)) + { + if (File.Exists(this.paths.SyncthingBackupPath)) + { + logger.Info("Syncthing doesn't exist at {0}, so copying from {1}", this.paths.SyncthingPath, this.paths.SyncthingBackupPath); + File.Copy(this.paths.SyncthingBackupPath, this.paths.SyncthingPath); + } + else + { + throw new Exception(String.Format("Unable to find Syncthing at {0} or {1}", this.paths.SyncthingPath, this.paths.SyncthingBackupPath)); + } + } + else if (installCountChanged) + { + // If we hit this, then latestInstallCount is set to a real value + logger.Info("Install Count changed, so updating Syncthing at {0} from {1}", this.paths.SyncthingPath, this.paths.SyncthingBackupPath); + try + { + File.Copy(this.paths.SyncthingBackupPath, this.paths.SyncthingPath, true); + } + catch (IOException e) + { + // Syncthing.exe was probably running. We'll try again next time + updateConfigInstallCount = false; + logger.Error(String.Format("Failed to copy Syncthing from {0} to {1}", this.paths.SyncthingBackupPath, this.paths.SyncthingPath), e); + } + } } - this.currentConfig = this.LoadFromDisk(defaultConfiguration); + if (updateConfigInstallCount) + { + this.currentConfig.LastSeenInstallCount = latestInstallCount; + this.SaveToFile(this.currentConfig); + } } private Configuration LoadFromDisk(Configuration defaultConfiguration) @@ -189,6 +225,13 @@ private XDocument MigrateV1ToV2(XDocument configuration) return configuration; } + private XDocument MigrateV2ToV3(XDocument configuration) + { + bool? visible = (bool?)configuration.Root.Element("ShowSyncthingConsole"); + configuration.Root.Add(new XElement("SyncthingConsoleHeight", visible == true ? Configuration.DefaultSyncthingConsoleHeight : 0.0)); + return configuration; + } + private XDocument LegacyMigrationConfiguration(XDocument configuration) { var address = configuration.Root.Element("SyncthingAddress").Value; diff --git a/src/SyncTrayzor/SyncThing/ApiClient/Config.cs b/src/SyncTrayzor/SyncThing/ApiClient/Config.cs index 5ec1b791..9568e4b6 100644 --- a/src/SyncTrayzor/SyncThing/ApiClient/Config.cs +++ b/src/SyncTrayzor/SyncThing/ApiClient/Config.cs @@ -41,9 +41,13 @@ public TimeSpan RescanInterval set { this.RescanIntervalSeconds = (long)value.TotalSeconds; } } + [JsonProperty("invalid")] + public string Invalid { get; set; } + public override string ToString() { - return String.Format("", this.ID, this.Path, String.Join(", ", this.Devices), this.ReadOnly, this.RescanInterval); + return String.Format("", + this.ID, this.Path, String.Join(", ", this.Devices), this.ReadOnly, this.RescanInterval, this.Invalid); } } diff --git a/src/SyncTrayzor/SyncThing/ApiClient/ISyncThingApiClient.cs b/src/SyncTrayzor/SyncThing/ApiClient/ISyncThingApiClient.cs index 1ae5bd0f..bd9e1f65 100644 --- a/src/SyncTrayzor/SyncThing/ApiClient/ISyncThingApiClient.cs +++ b/src/SyncTrayzor/SyncThing/ApiClient/ISyncThingApiClient.cs @@ -10,8 +10,8 @@ namespace SyncTrayzor.SyncThing.ApiClient public interface ISyncThingApiClient { Task ShutdownAsync(); - Task> FetchEventsAsync(int since, int limit, CancellationToken cancellationToken); - Task> FetchEventsAsync(int since, CancellationToken cancellationToken); + Task> FetchEventsAsync(int since, int limit); + Task> FetchEventsAsync(int since); Task FetchConfigAsync(); Task ScanAsync(string folderId, string subPath); Task FetchSystemInfoAsync(); diff --git a/src/SyncTrayzor/SyncThing/ApiClient/ISyncThingApiV0p10.cs b/src/SyncTrayzor/SyncThing/ApiClient/ISyncThingApiV0p10.cs index b3ffe5c4..5f0e5955 100644 --- a/src/SyncTrayzor/SyncThing/ApiClient/ISyncThingApiV0p10.cs +++ b/src/SyncTrayzor/SyncThing/ApiClient/ISyncThingApiV0p10.cs @@ -11,10 +11,10 @@ namespace SyncTrayzor.SyncThing.ApiClient public interface ISyncThingApiV0p10 { [Get("/rest/events")] - Task> FetchEventsAsync(int since, CancellationToken cancellationToken); + Task> FetchEventsAsync(int since); [Get("/rest/events")] - Task> FetchEventsLimitAsync(int since, int limit, CancellationToken cancellationToken); + Task> FetchEventsLimitAsync(int since, int limit); [Get("/rest/config")] Task FetchConfigAsync(); diff --git a/src/SyncTrayzor/SyncThing/ApiClient/ISyncThingApiV0p11.cs b/src/SyncTrayzor/SyncThing/ApiClient/ISyncThingApiV0p11.cs index 01cbffaa..2a432148 100644 --- a/src/SyncTrayzor/SyncThing/ApiClient/ISyncThingApiV0p11.cs +++ b/src/SyncTrayzor/SyncThing/ApiClient/ISyncThingApiV0p11.cs @@ -11,10 +11,10 @@ namespace SyncTrayzor.SyncThing.ApiClient public interface ISyncThingApiV0p11 { [Get("/rest/events")] - Task> FetchEventsAsync(int since, CancellationToken cancellationToken); + Task> FetchEventsAsync(int since); [Get("/rest/events")] - Task> FetchEventsLimitAsync(int since, int limit, CancellationToken cancellationToken); + Task> FetchEventsLimitAsync(int since, int limit); [Get("/rest/system/config")] Task FetchConfigAsync(); diff --git a/src/SyncTrayzor/SyncThing/ApiClient/SyncThingApiClientV0p10.cs b/src/SyncTrayzor/SyncThing/ApiClient/SyncThingApiClientV0p10.cs index c84255e8..bd200986 100644 --- a/src/SyncTrayzor/SyncThing/ApiClient/SyncThingApiClientV0p10.cs +++ b/src/SyncTrayzor/SyncThing/ApiClient/SyncThingApiClientV0p10.cs @@ -40,14 +40,14 @@ public Task ShutdownAsync() return this.api.ShutdownAsync(); } - public Task> FetchEventsAsync(int since, int limit, CancellationToken cancellationToken) + public Task> FetchEventsAsync(int since, int limit) { - return this.api.FetchEventsLimitAsync(since, limit, cancellationToken); + return this.api.FetchEventsLimitAsync(since, limit); } - public Task> FetchEventsAsync(int since, CancellationToken cancellationToken) + public Task> FetchEventsAsync(int since) { - return this.api.FetchEventsAsync(since, cancellationToken); + return this.api.FetchEventsAsync(since); } public async Task FetchConfigAsync() diff --git a/src/SyncTrayzor/SyncThing/ApiClient/SyncThingApiClientV0p11.cs b/src/SyncTrayzor/SyncThing/ApiClient/SyncThingApiClientV0p11.cs index b420b1a2..f7dd6669 100644 --- a/src/SyncTrayzor/SyncThing/ApiClient/SyncThingApiClientV0p11.cs +++ b/src/SyncTrayzor/SyncThing/ApiClient/SyncThingApiClientV0p11.cs @@ -40,14 +40,14 @@ public Task ShutdownAsync() return this.api.ShutdownAsync(); } - public Task> FetchEventsAsync(int since, int limit, CancellationToken cancellationToken) + public Task> FetchEventsAsync(int since, int limit) { - return this.api.FetchEventsLimitAsync(since, limit, cancellationToken); + return this.api.FetchEventsLimitAsync(since, limit); } - public Task> FetchEventsAsync(int since, CancellationToken cancellationToken) + public Task> FetchEventsAsync(int since) { - return this.api.FetchEventsAsync(since, cancellationToken); + return this.api.FetchEventsAsync(since); } public async Task FetchConfigAsync() diff --git a/src/SyncTrayzor/SyncThing/EventWatcher/SyncThingEventWatcher.cs b/src/SyncTrayzor/SyncThing/EventWatcher/SyncThingEventWatcher.cs index d0647893..fe18c94f 100644 --- a/src/SyncTrayzor/SyncThing/EventWatcher/SyncThingEventWatcher.cs +++ b/src/SyncTrayzor/SyncThing/EventWatcher/SyncThingEventWatcher.cs @@ -52,9 +52,9 @@ protected override async Task PollAsync(CancellationToken cancellationToken) List events; // If this is the first poll, don't fetch the history if (this.lastEventId == 0) - events = await this.apiClient.FetchEventsAsync(0, 1, cancellationToken); + events = await this.apiClient.FetchEventsAsync(0, 1); else - events = await this.apiClient.FetchEventsAsync(this.lastEventId, cancellationToken); + events = await this.apiClient.FetchEventsAsync(this.lastEventId); // We can be aborted in the time it takes to fetch the events cancellationToken.ThrowIfCancellationRequested(); diff --git a/src/SyncTrayzor/SyncThing/SyncThingManager.cs b/src/SyncTrayzor/SyncThing/SyncThingManager.cs index 56082d17..9506b1bd 100644 --- a/src/SyncTrayzor/SyncThing/SyncThingManager.cs +++ b/src/SyncTrayzor/SyncThing/SyncThingManager.cs @@ -423,27 +423,36 @@ private async Task LoadStartupDataAsync(CancellationToken cancellationToken) cancellationToken.ThrowIfCancellationRequested(); await Task.WhenAll(configTask, systemTask, versionTask, connectionsTask); - this.devices = new ConcurrentDictionary(configTask.Result.Devices.Select(device => + // We can potentially see duplicate devices (if the user set their config file that way). Ignore them. + var devices = configTask.Result.Devices.DistinctBy(x => x.DeviceID).Select(device => { var deviceObj = new Device(device.DeviceID, device.Name); ItemConnectionData connectionData; if (connectionsTask.Result.DeviceConnections.TryGetValue(device.DeviceID, out connectionData)) deviceObj.SetConnected(connectionData.Address); - return new KeyValuePair(device.DeviceID, deviceObj); - })); + return deviceObj; + }); + this.devices = new ConcurrentDictionary(devices.Select(x => new KeyValuePair(x.DeviceId, x))); var tilde = systemTask.Result.Tilde; - var folderConstructionTasks = configTask.Result.Folders.Select(async folder => - { - var ignores = await this.FetchFolderIgnoresAsync(folder.ID, cancellationToken); - var path = folder.Path; - if (path.StartsWith("~")) - path = Path.Combine(tilde, path.Substring(1).TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)); - return new Folder(folder.ID, path, new FolderIgnores(ignores.IgnorePatterns, ignores.RegexPatterns)); - }); + // If the folder is invalid for any reason, we'll ignore it. + // Again, there's the potential for duplicate folder IDs (if the user's been fiddling their config). + // In this case, there's nothing really sensible we can do. Just pick one of them :) + var folderConstructionTasks = configTask.Result.Folders + .Where(x => String.IsNullOrWhiteSpace(x.Invalid)) + .DistinctBy(x => x.ID) + .Select(async folder => + { + var ignores = await this.FetchFolderIgnoresAsync(folder.ID, cancellationToken); + var path = folder.Path; + if (path.StartsWith("~")) + path = Path.Combine(tilde, path.Substring(1).TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)); + return new Folder(folder.ID, path, new FolderIgnores(ignores.IgnorePatterns, ignores.RegexPatterns)); + }); cancellationToken.ThrowIfCancellationRequested(); + var folders = await Task.WhenAll(folderConstructionTasks); this.folders = new ConcurrentDictionary(folders.Select(x => new KeyValuePair(x.FolderId, x))); diff --git a/src/SyncTrayzor/SyncThing/SyncThingProcessRunner.cs b/src/SyncTrayzor/SyncThing/SyncThingProcessRunner.cs index b371dd0d..4f0686f0 100644 --- a/src/SyncTrayzor/SyncThing/SyncThingProcessRunner.cs +++ b/src/SyncTrayzor/SyncThing/SyncThingProcessRunner.cs @@ -102,6 +102,8 @@ public void Start() RedirectStandardInput = true, RedirectStandardError = true, RedirectStandardOutput = true, + StandardOutputEncoding = Encoding.UTF8, + StandardErrorEncoding = Encoding.UTF8, }; foreach (var kvp in this.EnvironmentalVariables) diff --git a/src/SyncTrayzor/SyncTrayzor.csproj b/src/SyncTrayzor/SyncTrayzor.csproj index 2a9d8148..f83b72d4 100644 --- a/src/SyncTrayzor/SyncTrayzor.csproj +++ b/src/SyncTrayzor/SyncTrayzor.csproj @@ -265,6 +265,8 @@ + + @@ -273,7 +275,9 @@ + + diff --git a/src/SyncTrayzor/Utils/ChromiumWebBrowserExtensions.cs b/src/SyncTrayzor/Utils/ChromiumWebBrowserExtensions.cs new file mode 100644 index 00000000..b6d6192b --- /dev/null +++ b/src/SyncTrayzor/Utils/ChromiumWebBrowserExtensions.cs @@ -0,0 +1,22 @@ +using CefSharp.Wpf; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +namespace SyncTrayzor.Utils +{ + public static class ChromiumWebBrowserExtensions + { + private static MethodInfo setZoomLevelMethod = typeof(ChromiumWebBrowser).GetMethod("OnZoomLevelChanged", BindingFlags.Instance | BindingFlags.NonPublic, Type.DefaultBinder, new[] { typeof(double), typeof(double) }, null); + + public static void SetZoomLevel(this ChromiumWebBrowser browser, double zoomLevel) + { + // Yuck yuck yuck. This is fixed in CefSharp 39, but that breaks other things (I'm not entirely sure what, but things like + // the device ID become broken). + setZoomLevelMethod.Invoke(browser, new object[] { 0.0, zoomLevel }); + } + } +} diff --git a/src/SyncTrayzor/Utils/EnumerableExtensions.cs b/src/SyncTrayzor/Utils/EnumerableExtensions.cs new file mode 100644 index 00000000..6b31c960 --- /dev/null +++ b/src/SyncTrayzor/Utils/EnumerableExtensions.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SyncTrayzor.Utils +{ + public static class EnumerableExtensions + { + public static IEnumerable DistinctBy(this IEnumerable source, Func keySelector) + { + var knownKeys = new HashSet(); + foreach (var element in source) + { + if (knownKeys.Add(keySelector(element))) + yield return element; + } + } + } +} diff --git a/src/SyncTrayzor/Utils/SafeSyncthingExtensions.cs b/src/SyncTrayzor/Utils/SafeSyncthingExtensions.cs index a921fcee..55e31e0e 100644 --- a/src/SyncTrayzor/Utils/SafeSyncthingExtensions.cs +++ b/src/SyncTrayzor/Utils/SafeSyncthingExtensions.cs @@ -35,8 +35,8 @@ public static async Task StartWithErrorDialogAsync(this ISyncThingManager syncTh { // Haven't translated yet - still debugging windowManager.ShowMessageBox( - "Syncthing didn't start correctly", "Syncthing started running, but we were enable to connect to it. Please report this as a bug", + "Syncthing didn't start correctly", MessageBoxButton.OK, icon: MessageBoxImage.Error); } } diff --git a/src/SyncTrayzor/Xaml/CollapsingRowDefinitionBehaviour.cs b/src/SyncTrayzor/Xaml/CollapsingRowDefinitionBehaviour.cs index 1b865114..e6a4ff0e 100644 --- a/src/SyncTrayzor/Xaml/CollapsingRowDefinitionBehaviour.cs +++ b/src/SyncTrayzor/Xaml/CollapsingRowDefinitionBehaviour.cs @@ -5,6 +5,7 @@ using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; +using System.Windows.Data; using System.Windows.Interactivity; namespace SyncTrayzor.Xaml @@ -43,8 +44,15 @@ protected override void OnAttached() this.Refresh(); } + protected override void OnDetaching() + { + BindingOperations.ClearBinding(this.AssociatedObject, RowDefinition.HeightProperty); + } + private void Refresh() { + BindingOperations.ClearBinding(this.AssociatedObject, RowDefinition.HeightProperty); + if (this.RowVisibility == Visibility.Collapsed) { this.AssociatedObject.Height = new GridLength(0); @@ -52,7 +60,13 @@ private void Refresh() } else { - this.AssociatedObject.Height = this.Height; + var heightBinding = new Binding("Height") + { + Source = this, + Mode = BindingMode.TwoWay, + }; + BindingOperations.SetBinding(this.AssociatedObject, RowDefinition.HeightProperty, heightBinding); + this.AssociatedObject.MinHeight = this.MinHeight; } } diff --git a/src/SyncTrayzor/Xaml/GridLengthToAbsoluteConverter.cs b/src/SyncTrayzor/Xaml/GridLengthToAbsoluteConverter.cs new file mode 100644 index 00000000..1fdafecf --- /dev/null +++ b/src/SyncTrayzor/Xaml/GridLengthToAbsoluteConverter.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Data; + +namespace SyncTrayzor.Xaml +{ + public class GridLengthToAbsoluteConverter : IValueConverter + { + public static readonly GridLengthToAbsoluteConverter Instance = new GridLengthToAbsoluteConverter(); + + public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + if (value == null) + return null; + + try + { + return new GridLength(System.Convert.ToDouble(value)); + } + catch + { + return null; + } + } + + public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + if (!(value is GridLength)) + return null; + + return ((GridLength)value).Value; + } + } +} diff --git a/src/SyncTrayzor/Xaml/RemoveMnemonicsConverter.cs b/src/SyncTrayzor/Xaml/RemoveMnemonicsConverter.cs new file mode 100644 index 00000000..2cd388d8 --- /dev/null +++ b/src/SyncTrayzor/Xaml/RemoveMnemonicsConverter.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Data; + +namespace SyncTrayzor.Xaml +{ + public class RemoveMnemonicsConverter : IValueConverter + { + public static RemoveMnemonicsConverter Instance = new RemoveMnemonicsConverter(); + + public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + var val = value as string; + if (val == null) + return null; + + return val.Replace("_", "__"); + } + + public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + var val = value as string; + if (val == null) + return null; + + return val.Replace("__", "_"); + } + } +}