diff --git a/GUI/App.xaml.cs b/GUI/App.xaml.cs index 04133bf8..4395960b 100644 --- a/GUI/App.xaml.cs +++ b/GUI/App.xaml.cs @@ -40,7 +40,8 @@ namespace GARbro.GUI { const StringComparison StringIgnoreCase = StringComparison.OrdinalIgnoreCase; - public static string Name { get { return "GARbro"; } } + public static string Name { get { return "GARbro"; } } + public static string FormatsDat { get { return "Formats.dat"; } } /// /// Initial browsing directory. @@ -80,12 +81,11 @@ namespace GARbro.GUI if (string.IsNullOrEmpty (InitPath)) InitPath = Directory.GetCurrentDirectory(); - string formats_dat = "Formats.dat"; - DeserializeScheme (Path.Combine (FormatCatalog.Instance.DataDirectory, formats_dat)); - DeserializeScheme (Path.Combine (GetLocalAppDataFolder(), formats_dat)); + DeserializeScheme (Path.Combine (FormatCatalog.Instance.DataDirectory, FormatsDat)); + DeserializeScheme (Path.Combine (GetLocalAppDataFolder(), FormatsDat)); } - string GetLocalAppDataFolder () + public string GetLocalAppDataFolder () { string local_app_data = Environment.GetFolderPath (Environment.SpecialFolder.LocalApplicationData); var attribs = Assembly.GetExecutingAssembly().GetCustomAttributes (typeof(AssemblyCompanyAttribute), false); @@ -93,7 +93,7 @@ namespace GARbro.GUI return Path.Combine (local_app_data, company, Name); } - void DeserializeScheme (string scheme_file) + public void DeserializeScheme (string scheme_file) { try { diff --git a/GUI/GarUpdate.cs b/GUI/GarUpdate.cs index 42930b6a..727e66dc 100644 --- a/GUI/GarUpdate.cs +++ b/GUI/GarUpdate.cs @@ -34,44 +34,93 @@ using System.Windows.Input; using System.Xml; using GameRes; using GARbro.GUI.Strings; +using System.IO; namespace GARbro.GUI { public partial class MainWindow : Window { - private readonly BackgroundWorker m_update_checker = new BackgroundWorker(); + GarUpdate m_updater; private void InitUpdatesChecker () { - m_update_checker.DoWork += StartUpdatesCheck; - m_update_checker.RunWorkerCompleted += UpdatesCheckComplete; + var update_url = App.Resources["UpdateUrl"] as Uri; + m_updater = new GarUpdate (this, update_url); + } + + public void CanExecuteUpdate (object sender, CanExecuteRoutedEventArgs e) + { + e.CanExecute = m_updater.CanExecute (e.Parameter); } /// /// Handle "Check for updates" command. /// private void CheckUpdatesExec (object sender, ExecutedRoutedEventArgs e) + { + m_updater.Execute (e.Parameter); + } + } + + public class GarUpdateInfo + { + public Version ReleaseVersion { get; set; } + public Uri ReleaseUrl { get; set; } + public string ReleaseNotes { get; set; } + public int FormatsVersion { get; set; } + public Uri FormatsUrl { get; set; } + public IDictionary Assemblies { get; set; } + } + + internal sealed class GarUpdate : ICommand, IDisposable + { + private readonly MainWindow m_main; + private readonly BackgroundWorker m_update_checker = new BackgroundWorker(); + private readonly Uri m_url; + + const int RequestTimeout = 20000; // milliseconds + + public GarUpdate (MainWindow main, Uri url) + { + m_main = main; + m_url = url; + m_update_checker.DoWork += StartUpdatesCheck; + m_update_checker.RunWorkerCompleted += UpdatesCheckComplete; + } + + public void Execute (object parameter) { if (!m_update_checker.IsBusy) m_update_checker.RunWorkerAsync(); } + public bool CanExecute (object parameter) + { + return !m_update_checker.IsBusy; + } + + public event EventHandler CanExecuteChanged; + + void OnCanExecuteChanged () + { + var handler = CanExecuteChanged; + if (handler != null) + handler (this, new EventArgs()); + } + private void StartUpdatesCheck (object sender, DoWorkEventArgs e) { - var url = m_app.Resources["UpdateUrl"] as Uri; - if (null == url) - return; - using (var updater = new GarUpdate (this)) - { - e.Result = updater.Check (url); - } + OnCanExecuteChanged(); + if (m_url != null) + e.Result = Check (m_url); } private void UpdatesCheckComplete (object sender, RunWorkerCompletedEventArgs e) { + OnCanExecuteChanged(); if (e.Error != null) { - SetStatusText (string.Format ("{0} {1}", guiStrings.MsgUpdateFailed, e.Error.Message)); + m_main.SetStatusText (string.Format ("{0} {1}", guiStrings.MsgUpdateFailed, e.Error.Message)); return; } else if (e.Cancelled) @@ -79,7 +128,7 @@ namespace GARbro.GUI var result = e.Result as GarUpdateInfo; if (null == result) { - SetStatusText (guiStrings.MsgNoUpdates); + m_main.SetStatusText (guiStrings.MsgNoUpdates); return; } var app_version = Assembly.GetExecutingAssembly().GetName().Version; @@ -88,17 +137,44 @@ namespace GARbro.GUI bool has_db_update = db_version < result.FormatsVersion && CheckAssemblies (result.Assemblies); if (!has_app_update && !has_db_update) { - SetStatusText (guiStrings.MsgUpToDate); + m_main.SetStatusText (guiStrings.MsgUpToDate); return; } var dialog = new UpdateDialog (result, has_app_update, has_db_update); - dialog.Owner = this; - dialog.FormatsDownload.Click += FormatsDownloadExec; + dialog.Owner = m_main; + dialog.FormatsDownload.Click += (s, a) => StartFormatsDownload (dialog, result); dialog.ShowDialog(); } - private void FormatsDownloadExec (object sender, RoutedEventArgs e) + private void StartFormatsDownload (UpdateDialog dialog, GarUpdateInfo info) { + try + { + dialog.FormatsDownload.IsEnabled = false; + var app_data_folder = m_main.App.GetLocalAppDataFolder(); + Directory.CreateDirectory (app_data_folder); + var local_formats_dat = Path.Combine (app_data_folder, App.FormatsDat); + using (var client = new WebClientEx()) + using (var tmp_file = new GARbro.Shell.TemporaryFile (app_data_folder, Path.GetRandomFileName())) + { + client.Timeout = RequestTimeout; + // FIXME download blocks GUI thread. + client.DownloadFile (info.FormatsUrl, tmp_file.Name); + + m_main.App.DeserializeScheme (tmp_file.Name); + if (!GARbro.Shell.File.Rename (tmp_file.Name, local_formats_dat)) + throw new Win32Exception (GARbro.Shell.File.GetLastError()); + } + dialog.FormatsUpdateText.Text = guiStrings.MsgUpdateComplete; + } + catch (Exception X) + { + dialog.FormatsUpdateText.Text = string.Format ("{0}\n{1}", guiStrings.MsgDownloadFailed, X.Message); + } + finally + { + dialog.FormatsDownload.Visibility = Visibility.Collapsed; + } } /// @@ -117,30 +193,8 @@ namespace GARbro.GUI } return true; } - } - public class GarUpdateInfo - { - public Version ReleaseVersion { get; set; } - public Uri ReleaseUrl { get; set; } - public string ReleaseNotes { get; set; } - public int FormatsVersion { get; set; } - public Uri FormatsUrl { get; set; } - public IDictionary Assemblies { get; set; } - } - - internal sealed class GarUpdate : IDisposable - { - Window m_main; - - const int RequestTimeout = 20000; // milliseconds - - public GarUpdate (Window main) - { - m_main = main; - } - - public GarUpdateInfo Check (Uri version_url) + GarUpdateInfo Check (Uri version_url) { var request = WebRequest.Create (version_url); request.Timeout = RequestTimeout; @@ -190,9 +244,33 @@ namespace GARbro.GUI { if (!m_disposed) { + m_update_checker.Dispose(); m_disposed = true; } GC.SuppressFinalize (this); } } + + /// + /// WebClient with timeout setting. + /// + internal class WebClientEx : WebClient + { + /// + /// Request timeout, in milliseconds. + /// + public int Timeout { get; set; } + + public WebClientEx () + { + Timeout = 60000; + } + + protected override WebRequest GetWebRequest (Uri uri) + { + var request = base.GetWebRequest (uri); + request.Timeout = Timeout; + return request; + } + } } diff --git a/GUI/MainWindow.xaml b/GUI/MainWindow.xaml index 5caa2549..daa8abcf 100644 --- a/GUI/MainWindow.xaml +++ b/GUI/MainWindow.xaml @@ -403,7 +403,7 @@ - + diff --git a/GUI/MainWindow.xaml.cs b/GUI/MainWindow.xaml.cs index 436a6622..4bca3861 100644 --- a/GUI/MainWindow.xaml.cs +++ b/GUI/MainWindow.xaml.cs @@ -54,6 +54,8 @@ namespace GARbro.GUI { private App m_app; + public App App { get { return m_app; } } + const StringComparison StringIgnoreCase = StringComparison.CurrentCultureIgnoreCase; public MainWindow() diff --git a/GUI/Strings/guiStrings.Designer.cs b/GUI/Strings/guiStrings.Designer.cs index 3829cae0..67aab972 100644 --- a/GUI/Strings/guiStrings.Designer.cs +++ b/GUI/Strings/guiStrings.Designer.cs @@ -663,6 +663,15 @@ namespace GARbro.GUI.Strings { } } + /// + /// Looks up a localized string similar to Update download failed.. + /// + public static string MsgDownloadFailed { + get { + return ResourceManager.GetString("MsgDownloadFailed", resourceCulture); + } + } + /// /// Looks up a localized string similar to archive is empty. /// @@ -882,6 +891,15 @@ namespace GARbro.GUI.Strings { } } + /// + /// Looks up a localized string similar to Formats database updated.. + /// + public static string MsgUpdateComplete { + get { + return ResourceManager.GetString("MsgUpdateComplete", resourceCulture); + } + } + /// /// Looks up a localized string similar to Update check failed.. /// @@ -1210,6 +1228,15 @@ namespace GARbro.GUI.Strings { } } + /// + /// Looks up a localized string similar to Application update. + /// + public static string TextUpdateTitle { + get { + return ResourceManager.GetString("TextUpdateTitle", resourceCulture); + } + } + /// /// Looks up a localized string similar to Visit download page. /// diff --git a/GUI/Strings/guiStrings.ko-KR.resx b/GUI/Strings/guiStrings.ko-KR.resx index 2e390101..52c5d398 100644 --- a/GUI/Strings/guiStrings.ko-KR.resx +++ b/GUI/Strings/guiStrings.ko-KR.resx @@ -505,6 +505,10 @@ Check for updates... translation pending + + Update download failed. + translation pending + No updates currently available. translation pending @@ -513,6 +517,10 @@ Formats database update available. translation pending + + Formats database updated. + translation pending + Update check failed. translation pending @@ -525,6 +533,10 @@ New version available: translation pending + + Application update + translation pending + Visit download page translation pending diff --git a/GUI/Strings/guiStrings.resx b/GUI/Strings/guiStrings.resx index d3ecaa5e..a4c3aebf 100644 --- a/GUI/Strings/guiStrings.resx +++ b/GUI/Strings/guiStrings.resx @@ -512,6 +512,9 @@ Overwrite? Formats database update available. + + Formats database updated. + Update check failed. @@ -521,7 +524,13 @@ Overwrite? New version available: + + Application update + Visit download page + + Update download failed. + \ No newline at end of file diff --git a/GUI/Strings/guiStrings.ru-RU.resx b/GUI/Strings/guiStrings.ru-RU.resx index 26b120b3..48e15388 100644 --- a/GUI/Strings/guiStrings.ru-RU.resx +++ b/GUI/Strings/guiStrings.ru-RU.resx @@ -527,12 +527,18 @@ Проверить обновления... + + Не удалось обновить базу форматов. + Обновления недоступны. Доступна обновлённая база форматов. + + База форматов успешно обновлена. + Сбой проверки обновлений. @@ -542,6 +548,9 @@ Доступна новая версия: + + Обновление программы + Перейти на страницу загрузки diff --git a/GUI/Strings/guiStrings.zh-Hans.resx b/GUI/Strings/guiStrings.zh-Hans.resx index 06845701..dbb2c1c5 100644 --- a/GUI/Strings/guiStrings.zh-Hans.resx +++ b/GUI/Strings/guiStrings.zh-Hans.resx @@ -517,6 +517,10 @@ Check for updates... translation pending + + Update download failed. + translation pending + No updates currently available. translation pending @@ -525,6 +529,10 @@ Formats database update available. translation pending + + Formats database updated. + translation pending + Update check failed. translation pending @@ -537,6 +545,10 @@ New version available: translation pending + + Application update + translation pending + Visit download page translation pending diff --git a/GUI/UpdateDialog.xaml b/GUI/UpdateDialog.xaml index de18c5ac..db2914d9 100644 --- a/GUI/UpdateDialog.xaml +++ b/GUI/UpdateDialog.xaml @@ -24,7 +24,7 @@ IN THE SOFTWARE. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:GARbro.GUI.Strings" - Title="Application update" ShowInTaskbar="False" WindowStartupLocation="CenterOwner" + Title="{x:Static s:guiStrings.TextUpdateTitle}" ShowInTaskbar="False" WindowStartupLocation="CenterOwner" Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" SizeToContent="WidthAndHeight" ResizeMode="NoResize"> @@ -34,7 +34,7 @@ IN THE SOFTWARE. Margin="10" Width="75" Height="25" Click="Button_Click" IsCancel="True"/> - @@ -53,8 +53,8 @@ IN THE SOFTWARE. - -