diff --git a/GUI/FileErrorDialog.xaml b/GUI/FileErrorDialog.xaml index c506f95f..89fd9dc9 100644 --- a/GUI/FileErrorDialog.xaml +++ b/GUI/FileErrorDialog.xaml @@ -3,7 +3,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:w="clr-namespace:Rnd.Windows" Title="{Binding Title}" ShowInTaskbar="False" WindowStartupLocation="CenterOwner" - ResizeMode="NoResize" SizeToContent="WidthAndHeight" + ResizeMode="NoResize" SizeToContent="WidthAndHeight" ShowActivated="True" Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"> diff --git a/GUI/GARbro.GUI.csproj b/GUI/GARbro.GUI.csproj index 8046765b..bf565e0e 100644 --- a/GUI/GARbro.GUI.csproj +++ b/GUI/GARbro.GUI.csproj @@ -95,9 +95,6 @@ ..\packages\NAudio.1.8.0\lib\net35\NAudio.dll True - - ..\packages\Ookii.Dialogs.1.0\lib\net35\Ookii.Dialogs.Wpf.dll - @@ -158,6 +155,10 @@ + + + Component + diff --git a/GUI/GarConvert.cs b/GUI/GarConvert.cs index 739bf79d..1d61da9d 100644 --- a/GUI/GarConvert.cs +++ b/GUI/GarConvert.cs @@ -34,7 +34,6 @@ using System.Diagnostics; using GameRes; using GARbro.GUI.Strings; using GARbro.GUI.Properties; -using Ookii.Dialogs.Wpf; using System.Runtime.InteropServices; namespace GARbro.GUI diff --git a/GUI/GarCreate.cs b/GUI/GarCreate.cs index 7388895a..8db66518 100644 --- a/GUI/GarCreate.cs +++ b/GUI/GarCreate.cs @@ -34,7 +34,6 @@ using System.Windows.Input; using GameRes; using GARbro.GUI.Strings; using GARbro.GUI.Properties; -using Ookii.Dialogs.Wpf; namespace GARbro.GUI { diff --git a/GUI/GarExtract.cs b/GUI/GarExtract.cs index aa8b31af..8d10048a 100644 --- a/GUI/GarExtract.cs +++ b/GUI/GarExtract.cs @@ -33,7 +33,6 @@ using System.Windows; using System.Windows.Input; using System.Windows.Interop; using System.Windows.Media.Imaging; -using Ookii.Dialogs.Wpf; using GameRes; using GARbro.GUI.Strings; using GARbro.GUI.Properties; @@ -114,6 +113,7 @@ namespace GARbro.GUI private bool m_skip_audio = false; private bool m_adjust_image_offset = false; private bool m_convert_audio; + private bool m_ignore_errors; private ImageFormat m_image_format; private int m_extract_count; private int m_skip_count; @@ -284,7 +284,7 @@ namespace GARbro.GUI var arc = m_fs.Source; int total = file_list.Count(); int progress_count = 0; - bool ignore_errors = false; + m_ignore_errors = false; foreach (var entry in file_list) { if (m_progress_dialog.CancellationPending) @@ -303,54 +303,18 @@ namespace GARbro.GUI } catch (Exception X) { - if (!ignore_errors) + if (!m_ignore_errors) { - IntPtr progress_handle = IntPtr.Zero; - try - { - var error_text = string.Format ("{0}\n{1}\n{2}", "Failed to extract file", - entry.Name, X.Message); - bool dialog_result = false; - m_main.Dispatcher.Invoke (() => { - progress_handle = HideProgressDialog(); - var dialog = new FileErrorDialog ("File extraction error", error_text); - dialog.Owner = m_main; - dialog_result = dialog.ShowDialog() ?? false; - ignore_errors = dialog.IgnoreErrors.IsChecked ?? false; - }); - if (!dialog_result) - break; - } - finally - { - if (progress_handle != IntPtr.Zero) - ShowWindow (progress_handle, SW_SHOW); - } + var error_text = string.Format ("{0}\n{1}\n{2}", "Failed to extract file", + entry.Name, X.Message); + if (!m_main.Dispatcher.Invoke (() => ShowErrorDialog (error_text))) + break; } ++m_skip_count; } } } - const int SW_HIDE = 0; - const int SW_SHOW = 5; - - [DllImport("user32.dll", SetLastError = true)] - static extern IntPtr FindWindowEx (IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow); - [DllImport("user32.dll")][return: MarshalAs(UnmanagedType.Bool)] - static extern bool ShowWindow (IntPtr hWnd, int nCmdShow); - - IntPtr HideProgressDialog () - { - // i just want to temporarily hide progress dialog when error window pops up, or at least force it - // to background so it can't be interacted with until error dialog is resolved. Unfortunately - // it's impossible with Ookii.Dialogs implementation aside from ugly hacks like this one. - var found = FindWindowEx (IntPtr.Zero, IntPtr.Zero, null, m_progress_dialog.WindowTitle); - if (IntPtr.Zero != found) - ShowWindow (found, SW_HIDE); - return found; - } - void ExtractImage (ArcFile arc, Entry entry, ImageFormat target_format) { using (var decoder = arc.OpenImage (entry)) @@ -450,6 +414,31 @@ namespace GARbro.GUI throw new IOException ("File aready exists"); } + bool ShowErrorDialog (string error_text) + { + var dialog = new FileErrorDialog ("File extraction error", error_text); + var progress_dialog_hwnd = m_progress_dialog.GetWindowHandle(); + if (progress_dialog_hwnd != IntPtr.Zero) + { + var native_dialog = new WindowInteropHelper (dialog); + native_dialog.Owner = progress_dialog_hwnd; + NativeMethods.EnableWindow (progress_dialog_hwnd, false); + EventHandler on_closed = null; + on_closed = (s, e) => { + NativeMethods.EnableWindow (progress_dialog_hwnd, true); + dialog.Closed -= on_closed; + }; + dialog.Closed += on_closed; + } + else + { + dialog.Owner = m_main; + } + bool dialog_result = dialog.ShowDialog() ?? false; + m_ignore_errors = dialog.IgnoreErrors.IsChecked ?? false; + return dialog_result; + } + void OnExtractComplete (object sender, RunWorkerCompletedEventArgs e) { m_extract_in_progress = false; diff --git a/GUI/ProgressDialog/IProgressDialog.cs b/GUI/ProgressDialog/IProgressDialog.cs new file mode 100644 index 00000000..16308e9a --- /dev/null +++ b/GUI/ProgressDialog/IProgressDialog.cs @@ -0,0 +1,116 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Runtime.InteropServices; + +namespace GARbro.GUI.Interop +{ + [ComImport, Guid ("F8383852-FCD3-11d1-A6B9-006097DF5BD4")] + internal class ProgressDialogRCW + { + } + + [ComImport, Guid ("EBBC7C04-315E-11d2-B62F-006097DF5BD4"), CoClass (typeof(ProgressDialogRCW))] + internal interface ProgressDialog : IProgressDialog + { + } + + [Flags] + internal enum ProgressDialogFlags : uint + { + Normal = 0x00000000, + Modal = 0x00000001, + AutoTime = 0x00000002, + NoTime = 0x00000004, + NoMinimize = 0x00000008, + NoProgressBar = 0x00000010, + MarqueeProgress = 0x00000020, + NoCancel = 0x00000040 + } + + [Flags] + internal enum ProgressTimerAction : uint + { + Reset = 0x00000001, + Pause = 0x00000002, + Resume = 0x00000003 + } + + [ComImport, Guid ("EBBC7C04-315E-11d2-B62F-006097DF5BD4"), InterfaceType (ComInterfaceType.InterfaceIsIUnknown)] + internal interface IProgressDialog + { + + [PreserveSig] + void StartProgressDialog( + IntPtr hwndParent, + [MarshalAs(UnmanagedType.IUnknown)] + object punkEnableModless, + ProgressDialogFlags dwFlags, + IntPtr pvResevered + ); + + [PreserveSig] + void StopProgressDialog(); + + [PreserveSig] + void SetTitle( + [MarshalAs(UnmanagedType.LPWStr)] + string pwzTitle + ); + + [PreserveSig] + void SetAnimation( + IntPtr hInstAnimation, + ushort idAnimation + ); + + [PreserveSig] + [return: MarshalAs(UnmanagedType.Bool)] + bool HasUserCancelled(); + + [PreserveSig] + void SetProgress( + uint dwCompleted, + uint dwTotal + ); + [PreserveSig] + void SetProgress64( + ulong ullCompleted, + ulong ullTotal + ); + + [PreserveSig] + void SetLine( + uint dwLineNum, + [MarshalAs(UnmanagedType.LPWStr)] + string pwzString, + [MarshalAs(UnmanagedType.VariantBool)] + bool fCompactPath, + IntPtr pvResevered + ); + + [PreserveSig] + void SetCancelMsg( + [MarshalAs(UnmanagedType.LPWStr)] + string pwzCancelMsg, + object pvResevered + ); + + [PreserveSig] + void Timer( + ProgressTimerAction dwTimerAction, + object pvResevered + ); + } + + [ComImport, Guid ("00000114-0000-0000-C000-000000000046"), InterfaceType (ComInterfaceType.InterfaceIsIUnknown)] + internal interface IOleWindow + { + [PreserveSig] + void GetWindow (out IntPtr phwnd); + + [PreserveSig] + void ContextSensitiveHelp ([MarshalAs(UnmanagedType.Bool)] bool fEnterMode); + } +} diff --git a/GUI/ProgressDialog/ProgressDialog.cs b/GUI/ProgressDialog/ProgressDialog.cs new file mode 100644 index 00000000..fa1f47a4 --- /dev/null +++ b/GUI/ProgressDialog/ProgressDialog.cs @@ -0,0 +1,713 @@ +// Modified ProgressDialog from Ookii.Dialogs +// +// Copyright © Sven Groot (Ookii.org) 2009 +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1) Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// 2) Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// 3) Neither the name of the ORGANIZATION nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +using System; +using System.ComponentModel; +using System.Runtime.InteropServices; +using System.Windows; +using System.Windows.Interop; + +namespace GARbro.GUI +{ + public class ProgressDialog : Component + { + private class ProgressChangedData + { + public string Text { get; set; } + public string Description { get; set; } + public object UserState { get; set; } + } + + private string _windowTitle; + private string _text; + private string _description; + private Interop.IProgressDialog _dialog; + private string _cancellationText; + private bool _useCompactPathsForText; + private bool _useCompactPathsForDescription; + private bool _cancellationPending; + private BackgroundWorker _backgroundWorker; + + /// + /// Event raised when the dialog is displayed. + /// + /// + /// Use this event to perform the operation that the dialog is showing the progress for. + /// This event will be raised on a different thread than the UI thread. + /// + public event DoWorkEventHandler DoWork; + + /// + /// Event raised when the operation completes. + /// + public event RunWorkerCompletedEventHandler RunWorkerCompleted; + + /// + /// Event raised when is called. + /// + public event ProgressChangedEventHandler ProgressChanged; + + /// + /// Initializes a new instance of the class. + /// + public ProgressDialog () + { + InitializeComponent(); + + ProgressBarStyle = ProgressBarStyle.ProgressBar; + ShowCancelButton = true; + MinimizeBox = true; + } + + /// + /// Gets or sets the text in the progress dialog's title bar. + /// + /// + /// The text in the progress dialog's title bar. The default value is an empty string. + /// + /// + /// + /// This property must be set before or is called. Changing property has + /// no effect while the dialog is being displayed. + /// + /// + [Localizable(true), Category("Appearance"), Description("The text in the progress dialog's title bar."), DefaultValue("")] + public string WindowTitle + { + get { return _windowTitle ?? string.Empty; } + set { _windowTitle = value; } + } + + /// + /// Gets or sets a short description of the operation being carried out. + /// + /// + /// A short description of the operation being carried. The default value is an empty string. + /// + /// + /// + /// This is the primary message to the user. + /// + /// + /// This property can be changed while the dialog is running, but may only be changed from the thread which + /// created the progress dialog. The recommended method to change this value while the dialog is running + /// is to use the method. + /// + /// + [Localizable(true), Category("Appearance"), Description("A short description of the operation being carried out.")] + public string Text + { + get { return _text ?? string.Empty; } + set + { + _text = value; + if (_dialog != null) + _dialog.SetLine (1, Text, UseCompactPathsForText, IntPtr.Zero); + } + } + + /// + /// Gets or sets a value that indicates whether path strings in the property should be compacted if + /// they are too large to fit on one line. + /// + /// + /// to compact path strings if they are too large to fit on one line; otherwise, + /// . The default value is . + /// + /// + /// + /// This property requires Windows Vista or later. On older versions of Windows, it has no effect. + /// + /// + /// This property can be changed while the dialog is running, but may only be changed from the thread which + /// created the progress dialog. + /// + /// + [Category("Behavior"), Description("Indicates whether path strings in the Text property should be compacted if they are too large to fit on one line."), DefaultValue(false)] + public bool UseCompactPathsForText + { + get { return _useCompactPathsForText; } + set + { + _useCompactPathsForText = value; + if (_dialog != null) + _dialog.SetLine (1, Text, _useCompactPathsForText, IntPtr.Zero); + } + } + + /// + /// Gets or sets additional details about the operation being carried out. + /// + /// + /// Additional details about the operation being carried out. The default value is an empty string. + /// + /// + /// This text is used to provide additional details beyond the property. + /// + /// + /// + /// This property can be changed while the dialog is running, but may only be changed from the thread which + /// created the progress dialog. The recommended method to change this value while the dialog is running + /// is to use the method. + /// + /// + [Localizable(true), Category("Appearance"), Description("Additional details about the operation being carried out."), DefaultValue("")] + public string Description + { + get { return _description ?? string.Empty; } + set + { + _description = value; + if (_dialog != null) + _dialog.SetLine (2, Description, UseCompactPathsForDescription, IntPtr.Zero); + } + } + + /// + /// Gets or sets a value that indicates whether path strings in the property should be compacted if + /// they are too large to fit on one line. + /// + /// + /// to compact path strings if they are too large to fit on one line; otherwise, + /// . The default value is . + /// + /// + /// + /// This property requires Windows Vista or later. On older versions of Windows, it has no effect. + /// + /// + /// This property can be changed while the dialog is running, but may only be changed from the thread which + /// created the progress dialog. + /// + /// + [Category("Behavior"), Description("Indicates whether path strings in the Description property should be compacted if they are too large to fit on one line."), DefaultValue(false)] + public bool UseCompactPathsForDescription + { + get { return _useCompactPathsForDescription; } + set + { + _useCompactPathsForDescription = value; + if( _dialog != null ) + _dialog.SetLine(2, Description, UseCompactPathsForDescription, IntPtr.Zero); + } + } + + /// + /// Gets or sets the text that will be shown after the Cancel button is pressed. + /// + /// + /// The text that will be shown after the Cancel button is pressed. + /// + /// + /// + /// This property must be set before or is called. Changing property has + /// no effect while the dialog is being displayed. + /// + /// + [Localizable(true), Category("Appearance"), Description("The text that will be shown after the Cancel button is pressed."), DefaultValue("")] + public string CancellationText + { + get { return _cancellationText ?? string.Empty; } + set { _cancellationText = value; } + } + + /// + /// Gets or sets a value that indicates whether an estimate of the remaining time will be shown. + /// + /// + /// if an estimate of remaining time will be shown; otherwise, . The + /// default value is . + /// + /// + /// + /// This property must be set before or is called. Changing property has + /// no effect while the dialog is being displayed. + /// + /// + [Category("Appearance"), Description("Indicates whether an estimate of the remaining time will be shown."), DefaultValue(false)] + public bool ShowTimeRemaining { get; set; } + + /// + /// Gets or sets a value that indicates whether the dialog has a cancel button. + /// + /// + /// if the dialog has a cancel button; otherwise, . The default + /// value is . + /// + /// + /// + /// This property requires Windows Vista or later; on older versions of Windows, the cancel button will always + /// be displayed. + /// + /// + /// The event handler for the event must periodically check the value of the + /// property to see if the operation has been cancelled if this + /// property is . + /// + /// + /// Setting this property to is not recommended unless absolutely necessary. + /// + /// + [Category("Appearance"), Description("Indicates whether the dialog has a cancel button. Do not set to false unless absolutely necessary."), DefaultValue(true)] + public bool ShowCancelButton { get; set; } + + /// + /// Gets or sets a value that indicates whether the progress dialog has a minimize button. + /// + /// + /// if the dialog has a minimize button; otherwise, . The default + /// value is . + /// + /// + /// + /// This property has no effect on modal dialogs (which do not have a minimize button). It only applies + /// to modeless dialogs shown by using the method. + /// + /// + /// This property must be set before is called. Changing property has + /// no effect while the dialog is being displayed. + /// + /// + [Category("Window Style"), Description("Indicates whether the progress dialog has a minimize button."), DefaultValue(true)] + public bool MinimizeBox { get; set; } + + /// + /// Gets a value indicating whether the user has requested cancellation of the operation. + /// + /// + /// if the user has cancelled the progress dialog; otherwise, . The default is . + /// + /// + /// The event handler for the event must periodically check this property and abort the operation + /// if it returns . + /// + [Browsable(false)] + public bool CancellationPending + { + get + { + _backgroundWorker.ReportProgress (-1); // Call with an out-of-range percentage will update the value of + // _cancellationPending but do nothing else. + return _cancellationPending; + } + } + + /// + /// Gets or sets a value that indicates whether a regular or marquee style progress bar should be used. + /// + /// + /// One of the values of . + /// The default value is . + /// + /// + /// + /// Operating systems older than Windows Vista do not support marquee progress bars on the progress dialog. On those operating systems, the + /// progress bar will be hidden completely if this property is . + /// + /// + /// When this property is set to , use the method to set + /// the value of the progress bar. When this property is set to + /// you can still use the method to update the text of the dialog, + /// but the percentage will be ignored. + /// + /// + /// This property must be set before or is called. Changing property has + /// no effect while the dialog is being displayed. + /// + /// + [Category("Appearance"), Description("Indicates the style of the progress bar."), DefaultValue(ProgressBarStyle.ProgressBar)] + public ProgressBarStyle ProgressBarStyle { get; set; } + + + /// + /// Gets a value that indicates whether the is running an asynchronous operation. + /// + /// + /// if the is running an asynchronous operation; + /// otherwise, . + /// + [Browsable(false)] + public bool IsBusy + { + get { return _backgroundWorker.IsBusy; } + } + + /// + /// Displays the progress dialog as a modeless dialog. + /// + /// + /// + /// This function will not block the parent window and will return immediately. + /// + /// + /// Although this function returns immediately, you cannot use the UI thread to do any processing. The dialog + /// will not function correctly unless the UI thread continues to handle window messages, so that thread may + /// not be blocked by some other activity. All processing related to the progress dialog must be done in + /// the event handler. + /// + /// + public void Show () + { + Show (null); + } + + /// + /// Displays the progress dialog as a modeless dialog. + /// + /// A parameter for use by the background operation to be executed in the event handler. + /// + /// + /// This function will not block the parent window and return immediately. + /// + /// + /// Although this function returns immediately, you cannot use the UI thread to do any processing. The dialog + /// will not function correctly unless the UI thread continues to handle window messages, so that thread may + /// not be blocked by some other activity. All processing related to the progress dialog must be done in + /// the event handler. + /// + /// + public void Show (object argument) + { + RunProgressDialog (IntPtr.Zero, argument); + } + + /// + /// Displays the progress dialog as a modal dialog. + /// + /// + /// + /// The ShowDialog function for most .Net dialogs will not return until the dialog is closed. However, + /// the function for the class will return immediately. + /// The parent window will be disabled as with all modal dialogs. + /// + /// + /// Although this function returns immediately, you cannot use the UI thread to do any processing. The dialog + /// will not function correctly unless the UI thread continues to handle window messages, so that thread may + /// not be blocked by some other activity. All processing related to the progress dialog must be done in + /// the event handler. + /// + /// + /// The progress dialog's window will appear in the taskbar. This behaviour is also contrary to most .Net dialogs, + /// but is part of the underlying native progress dialog API so cannot be avoided. + /// + /// + /// When possible, it is recommended that you use a modeless dialog using the function. + /// + /// + public void ShowDialog() + { + ShowDialog (null, null); + } + + /// + /// Displays the progress dialog as a modal dialog. + /// + /// The window that owns the dialog. + /// + /// + /// The ShowDialog function for most .Net dialogs will not return until the dialog is closed. However, + /// the function for the class will return immediately. + /// The parent window will be disabled as with all modal dialogs. + /// + /// + /// Although this function returns immediately, you cannot use the UI thread to do any processing. The dialog + /// will not function correctly unless the UI thread continues to handle window messages, so that thread may + /// not be blocked by some other activity. All processing related to the progress dialog must be done in + /// the event handler. + /// + /// + /// The progress dialog's window will appear in the taskbar. This behaviour is also contrary to most .Net dialogs, + /// but is part of the underlying native progress dialog API so cannot be avoided. + /// + /// + /// When possible, it is recommended that you use a modeless dialog using the function. + /// + /// + public void ShowDialog (Window owner) + { + ShowDialog (owner, null); + } + + /// + /// Displays the progress dialog as a modal dialog. + /// + /// The window that owns the dialog. + /// A parameter for use by the background operation to be executed in the event handler. + /// + /// + /// The ShowDialog function for most .Net dialogs will not return until the dialog is closed. However, + /// the function for the class will return immediately. + /// The parent window will be disabled as with all modal dialogs. + /// + /// + /// Although this function returns immediately, you cannot use the UI thread to do any processing. The dialog + /// will not function correctly unless the UI thread continues to handle window messages, so that thread may + /// not be blocked by some other activity. All processing related to the progress dialog must be done in + /// the event handler. + /// + /// + /// The progress dialog's window will appear in the taskbar. This behaviour is also contrary to most .Net dialogs, + /// but is part of the underlying native progress dialog API so cannot be avoided. + /// + /// + /// When possible, it is recommended that you use a modeless dialog using the function. + /// + /// + public void ShowDialog (Window owner, object argument) + { + RunProgressDialog (owner == null ? NativeMethods.GetActiveWindow() : new WindowInteropHelper(owner).Handle, argument); + } + + const int SW_HIDE = 0; + const int SW_SHOW = 5; + + public void Hide () + { + if (null == _dialog) + return; + var hwnd = GetWindowHandle(); + if (hwnd != IntPtr.Zero) + NativeMethods.ShowWindow (hwnd, SW_HIDE); + } + + public void Restore () + { + if (null == _dialog) + return; + var hwnd = GetWindowHandle(); + if (hwnd != IntPtr.Zero) + NativeMethods.ShowWindow (hwnd, SW_SHOW); + } + + /// + /// Get win32 handle of the progress dialog window. + /// + public IntPtr GetWindowHandle () + { + var ole = _dialog as Interop.IOleWindow; + if (null == ole) + return IntPtr.Zero; + IntPtr hwnd; + ole.GetWindow (out hwnd); + return hwnd; + } + + /// + /// Updates the dialog's progress bar. + /// + /// The percentage, from 0 to 100, of the operation that is complete. + /// + /// + /// Call this method from the event handler if you want to report progress. + /// + /// + /// This method has no effect is is + /// or . + /// + /// + /// is out of range. + /// The progress dialog is not currently being displayed. + public void ReportProgress (int percentProgress) + { + ReportProgress (percentProgress, null, null, null); + } + + /// + /// Updates the dialog's progress bar. + /// + /// The percentage, from 0 to 100, of the operation that is complete. + /// The new value of the progress dialog's primary text message, or to leave the value unchanged. + /// The new value of the progress dialog's additional description message, or to leave the value unchanged. + /// Call this method from the event handler if you want to report progress. + /// is out of range. + /// The progress dialog is not currently being displayed. + public void ReportProgress (int percentProgress, string text, string description) + { + ReportProgress (percentProgress, text, description, null); + } + + /// + /// Updates the dialog's progress bar. + /// + /// The percentage, from 0 to 100, of the operation that is complete. + /// The new value of the progress dialog's primary text message, or to leave the value unchanged. + /// The new value of the progress dialog's additional description message, or to leave the value unchanged. + /// A state object that will be passed to the event handler. + /// Call this method from the event handler if you want to report progress. + /// is out of range. + /// The progress dialog is not currently being displayed. + public void ReportProgress (int percentProgress, string text, string description, object userState) + { + if (percentProgress < 0 || percentProgress > 100) + throw new ArgumentOutOfRangeException ("percentProgress"); + if (_dialog == null) + throw new InvalidOperationException ("The progress dialog is not shown."); + _backgroundWorker.ReportProgress (percentProgress, new ProgressChangedData { Text = text, Description = description, UserState = userState }); + } + + /// + /// Raises the event. + /// + /// The containing data for the event. + protected virtual void OnDoWork (DoWorkEventArgs e) + { + var handler = DoWork; + if (handler != null) + handler (this, e); + } + + /// + /// Raises the event. + /// + /// The containing data for the event. + protected virtual void OnRunWorkerCompleted (RunWorkerCompletedEventArgs e) + { + var handler = RunWorkerCompleted; + if (handler != null) + handler (this, e); + } + + /// + /// Raises the event. + /// + /// The containing data for the event. + protected virtual void OnProgressChanged (ProgressChangedEventArgs e) + { + var handler = ProgressChanged; + if (handler != null) + handler (this, e); + } + + private void RunProgressDialog (IntPtr owner, object argument) + { + if (_backgroundWorker.IsBusy) + throw new InvalidOperationException ("The progress dialog is already running."); + + _cancellationPending = false; + _dialog = new Interop.ProgressDialog(); + _dialog.SetTitle (WindowTitle); + + if (CancellationText.Length > 0) + _dialog.SetCancelMsg (CancellationText, null); + _dialog.SetLine (1, Text, UseCompactPathsForText, IntPtr.Zero); + _dialog.SetLine (2, Description, UseCompactPathsForDescription, IntPtr.Zero); + + var flags = Interop.ProgressDialogFlags.Normal; + if (owner != IntPtr.Zero) + flags |= Interop.ProgressDialogFlags.Modal; + switch (ProgressBarStyle) + { + case ProgressBarStyle.None: + flags |= Interop.ProgressDialogFlags.NoProgressBar; + break; + case ProgressBarStyle.MarqueeProgressBar: + if (NativeMethods.IsWindowsVistaOrLater) + flags |= Interop.ProgressDialogFlags.MarqueeProgress; + else + flags |= Interop.ProgressDialogFlags.NoProgressBar; // Older than Vista doesn't support marquee. + break; + } + if( ShowTimeRemaining ) + flags |= Interop.ProgressDialogFlags.AutoTime; + if( !ShowCancelButton ) + flags |= Interop.ProgressDialogFlags.NoCancel; + if( !MinimizeBox ) + flags |= Interop.ProgressDialogFlags.NoMinimize; + + _dialog.StartProgressDialog (owner, null, flags, IntPtr.Zero); + _backgroundWorker.RunWorkerAsync (argument); + } + + private void InitializeComponent () + { + _backgroundWorker = new BackgroundWorker(); + _backgroundWorker.WorkerReportsProgress = true; + _backgroundWorker.WorkerSupportsCancellation = true; + _backgroundWorker.DoWork += new DoWorkEventHandler (_backgroundWorker_DoWork); + _backgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler (_backgroundWorker_RunWorkerCompleted); + _backgroundWorker.ProgressChanged += new ProgressChangedEventHandler (_backgroundWorker_ProgressChanged); + + } + + private void _backgroundWorker_DoWork (object sender, DoWorkEventArgs e) + { + OnDoWork (e); + } + + private void _backgroundWorker_RunWorkerCompleted (object sender, RunWorkerCompletedEventArgs e) + { + _dialog.StopProgressDialog(); + Marshal.ReleaseComObject (_dialog); + _dialog = null; + + OnRunWorkerCompleted (new RunWorkerCompletedEventArgs((!e.Cancelled && e.Error == null) ? e.Result : null, e.Error, e.Cancelled)); + } + + private void _backgroundWorker_ProgressChanged (object sender, ProgressChangedEventArgs e) + { + _cancellationPending = _dialog.HasUserCancelled(); + // ReportProgress doesn't allow values outside this range. However, CancellationPending will call + // BackgroundWorker.ReportProgress directly with a value that is outside this range to update the value of the property. + if (e.ProgressPercentage >= 0 && e.ProgressPercentage <= 100) + { + _dialog.SetProgress ((uint)e.ProgressPercentage, 100); + var data = e.UserState as ProgressChangedData; + if (data != null) + { + if (data.Text != null) + Text = data.Text; + if (data.Description != null) + Description = data.Description; + OnProgressChanged (new ProgressChangedEventArgs (e.ProgressPercentage, data.UserState)); + } + } + } + } + + /// + /// Indicates the type of progress on a task dialog. + /// + public enum ProgressBarStyle + { + /// + /// No progress bar is displayed on the dialog. + /// + None, + /// + /// A regular progress bar is displayed on the dialog. + /// + ProgressBar, + /// + /// A marquee progress bar is displayed on the dialog. Use this value for operations + /// that cannot report concrete progress information. + /// + MarqueeProgressBar + } +} diff --git a/GUI/Utility.cs b/GUI/Utility.cs index f289b480..d0ca2a61 100644 --- a/GUI/Utility.cs +++ b/GUI/Utility.cs @@ -35,6 +35,14 @@ namespace GARbro.GUI { internal class NativeMethods { + public static bool IsWindowsVistaOrLater + { + get + { + return Environment.OSVersion.Platform == PlatformID.Win32NT && Environment.OSVersion.Version >= new Version (6, 0, 6000); + } + } + [DllImport ("shlwapi.dll", CharSet = CharSet.Unicode)] internal static extern int StrCmpLogicalW (string psz1, string psz2); @@ -46,6 +54,15 @@ namespace GARbro.GUI [DllImport ("user32.dll")] internal static extern int ReleaseDC (IntPtr hWnd, IntPtr hDc); + + [DllImport ("user32.dll", CharSet = CharSet.Unicode, ExactSpelling = true)] + internal static extern IntPtr GetActiveWindow(); + + [DllImport ("user32.dll")][return: MarshalAs(UnmanagedType.Bool)] + internal static extern bool ShowWindow (IntPtr hWnd, int nCmdShow); + + [DllImport ("user32.dll")][return: MarshalAs(UnmanagedType.Bool)] + internal static extern bool EnableWindow (IntPtr hWnd, bool bEnable); } public static class Desktop