X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/e77570de2ecd420abf6c5642eb7fbdde616494c7..d5947fad64ffb5bd695620fc82e5d4f20d51fb47:/src/msw/progdlg.cpp diff --git a/src/msw/progdlg.cpp b/src/msw/progdlg.cpp index 0167b40762..3f051ac95e 100644 --- a/src/msw/progdlg.cpp +++ b/src/msw/progdlg.cpp @@ -27,6 +27,7 @@ #include "wx/msw/private/msgdlg.h" #include "wx/progdlg.h" +#include "wx/evtloop.h" using namespace wxMSWMessageDialog; @@ -86,7 +87,7 @@ public: wxString m_labelCancel; // Privately used by callback. unsigned long m_timeStop; - wxGenericProgressDialog::ProgressDialogState m_state; + wxProgressDialog::State m_state; bool m_progressBarMarquee; bool m_skipped; @@ -119,13 +120,37 @@ private: LONG_PTR dwRefData); }; +namespace +{ + +// A custom event loop which runs until the state of the dialog becomes +// "Dismissed". +class wxProgressDialogModalLoop : public wxEventLoop +{ +public: + wxProgressDialogModalLoop(wxProgressDialogSharedData& data) + : m_data(data) + { + } + +protected: + virtual void OnNextIteration() + { + wxCriticalSectionLocker locker(m_data.m_cs); + + if ( m_data.m_state == wxProgressDialog::Dismissed ) + Exit(); + } + + wxProgressDialogSharedData& m_data; + + wxDECLARE_NO_COPY_CLASS(wxProgressDialogModalLoop); +}; + // ============================================================================ // Helper functions // ============================================================================ -namespace -{ - // This function returns true if the progress dialog with the given style // (combination of wxPD_XXX constants) needs the "Close" button and this button // only, i.e. not a "Cancel" one. @@ -249,7 +274,7 @@ void PerformNotificationUpdates(HWND hwnd, // Is the progress finished? if ( sharedData->m_notifications & wxSPDD_FINISHED ) { - sharedData->m_state = wxGenericProgressDialog::Finished; + sharedData->m_state = wxProgressDialog::Finished; if ( !(sharedData->m_style & wxPD_AUTO_HIDE) ) { @@ -275,7 +300,7 @@ wxProgressDialog::wxProgressDialog( const wxString& title, int maximum, wxWindow *parent, int style ) - : wxGenericProgressDialog(parent, maximum, style), + : wxGenericProgressDialog(parent, style), m_taskDialogRunner(NULL), m_sharedData(NULL), m_message(message), @@ -284,6 +309,8 @@ wxProgressDialog::wxProgressDialog( const wxString& title, #ifdef wxHAS_MSW_TASKDIALOG if ( HasNativeProgressDialog() ) { + SetMaximum(maximum); + Show(); DisableOtherWindows(); @@ -322,53 +349,65 @@ bool wxProgressDialog::Update(int value, const wxString& newmsg, bool *skip) #ifdef wxHAS_MSW_TASKDIALOG if ( HasNativeProgressDialog() ) { - wxCriticalSectionLocker locker(m_sharedData->m_cs); + { + wxCriticalSectionLocker locker(m_sharedData->m_cs); - // Do nothing in canceled state. - if ( !DoNativeBeforeUpdate(skip) ) - return false; + // Do nothing in canceled state. + if ( !DoNativeBeforeUpdate(skip) ) + return false; - value /= m_factor; + value /= m_factor; - wxASSERT_MSG( value <= m_maximum, wxT("invalid progress value") ); + wxASSERT_MSG( value <= m_maximum, wxT("invalid progress value") ); - m_sharedData->m_value = value; - m_sharedData->m_notifications |= wxSPDD_VALUE_CHANGED; + m_sharedData->m_value = value; + m_sharedData->m_notifications |= wxSPDD_VALUE_CHANGED; - if ( !newmsg.empty() ) - { - m_message = newmsg; - m_sharedData->m_message = newmsg; - m_sharedData->m_notifications |= wxSPDD_MESSAGE_CHANGED; - } + if ( !newmsg.empty() ) + { + m_message = newmsg; + m_sharedData->m_message = newmsg; + m_sharedData->m_notifications |= wxSPDD_MESSAGE_CHANGED; + } - if ( m_sharedData->m_progressBarMarquee ) - { - m_sharedData->m_progressBarMarquee = false; - m_sharedData->m_notifications |= wxSPDD_PBMARQUEE_CHANGED; - } + if ( m_sharedData->m_progressBarMarquee ) + { + m_sharedData->m_progressBarMarquee = false; + m_sharedData->m_notifications |= wxSPDD_PBMARQUEE_CHANGED; + } - UpdateExpandedInformation( value ); + UpdateExpandedInformation( value ); + + // If we didn't just reach the finish, all we have to do is to + // return true if the dialog wasn't cancelled and false otherwise. + if ( value != m_maximum || m_state == Finished ) + return m_sharedData->m_state != Canceled; - // Has the progress bar finished? - if ( value == m_maximum ) - { - if ( m_state == Finished ) - return true; + // On finishing, the dialog without wxPD_AUTO_HIDE style becomes a + // modal one meaning that we must block here until the user + // dismisses it. m_state = Finished; m_sharedData->m_state = Finished; m_sharedData->m_notifications |= wxSPDD_FINISHED; - if( !HasPDFlag(wxPD_AUTO_HIDE) && newmsg.empty() ) + if ( HasPDFlag(wxPD_AUTO_HIDE) ) + return true; + + if ( newmsg.empty() ) { // Provide the finishing message if the application didn't. m_message = _("Done."); m_sharedData->m_message = m_message; m_sharedData->m_notifications |= wxSPDD_MESSAGE_CHANGED; } - } - - return m_sharedData->m_state != Canceled; + } // unlock m_sharedData->m_cs + + // We only get here when we need to wait for the dialog to terminate so + // do just this by running a custom event loop until the dialog is + // dismissed. + wxProgressDialogModalLoop loop(*m_sharedData); + loop.Run(); + return true; } #endif // wxHAS_MSW_TASKDIALOG @@ -501,17 +540,21 @@ wxString wxProgressDialog::GetMessage() const void wxProgressDialog::SetRange(int maximum) { - wxGenericProgressDialog::SetRange( maximum ); - #ifdef wxHAS_MSW_TASKDIALOG if ( HasNativeProgressDialog() ) { + SetMaximum(maximum); + wxCriticalSectionLocker locker(m_sharedData->m_cs); m_sharedData->m_range = maximum; m_sharedData->m_notifications |= wxSPDD_RANGE_CHANGED; + + return; } #endif // wxHAS_MSW_TASKDIALOG + + wxGenericProgressDialog::SetRange( maximum ); } bool wxProgressDialog::WasSkipped() const @@ -750,9 +793,7 @@ void* wxProgressDialogTaskRunner::Entry() m_sharedData.m_labelCancel ); } - tdc.dwFlags |= TDF_CALLBACK_TIMER - | TDF_SHOW_PROGRESS_BAR - | TDF_SHOW_MARQUEE_PROGRESS_BAR; + tdc.dwFlags |= TDF_CALLBACK_TIMER | TDF_SHOW_PROGRESS_BAR; if ( !m_sharedData.m_expandedInformation.empty() ) { @@ -770,6 +811,10 @@ void* wxProgressDialogTaskRunner::Entry() if ( FAILED(hr) ) wxLogApiError( "TaskDialogIndirect", hr ); + // If the main thread is waiting for us to exit inside the event loop in + // Update(), wake it up so that it checks our status again. + wxWakeUpIdle(); + return NULL; } @@ -814,9 +859,13 @@ wxProgressDialogTaskRunner::TaskDialogCallbackProc return TRUE; case IDCANCEL: - if ( sharedData->m_state - == wxGenericProgressDialog::Finished ) + if ( sharedData->m_state == wxProgressDialog::Finished ) { + // If the main thread is waiting for us, tell it that + // we're gone (and if it doesn't wait, it's harmless). + sharedData->m_state = wxProgressDialog::Dismissed; + + // Let Windows close the dialog. return FALSE; } @@ -825,16 +874,18 @@ wxProgressDialogTaskRunner::TaskDialogCallbackProc // a finished dialog. if ( !UsesCloseButtonOnly(sharedData->m_style) ) { - wxCHECK_MSG( sharedData->m_state == - wxGenericProgressDialog::Continue, - TRUE, - "Dialog not in a cancelable state!"); + wxCHECK_MSG + ( + sharedData->m_state == wxProgressDialog::Continue, + TRUE, + "Dialog not in a cancelable state!" + ); ::SendMessage(hwnd, TDM_ENABLE_BUTTON, Id_SkipBtn, FALSE); ::SendMessage(hwnd, TDM_ENABLE_BUTTON, IDCANCEL, FALSE); sharedData->m_timeStop = wxGetCurrentTime(); - sharedData->m_state = wxGenericProgressDialog::Canceled; + sharedData->m_state = wxProgressDialog::Canceled; } return TRUE; @@ -844,22 +895,19 @@ wxProgressDialogTaskRunner::TaskDialogCallbackProc case TDN_TIMER: PerformNotificationUpdates(hwnd, sharedData); - // End dialog in three different cases: - // 1. Progress finished and dialog should automatically hide. - // 2. The wxProgressDialog object was destructed and should - // automatically hide. - // 3. The dialog was canceled and wxProgressDialog object - // was destroyed. - bool isCanceled = - sharedData->m_state == wxGenericProgressDialog::Canceled; - bool isFinished = - sharedData->m_state == wxGenericProgressDialog::Finished; - bool wasDestroyed = - (sharedData->m_notifications & wxSPDD_DESTROYED) != 0; - bool shouldAutoHide = (sharedData->m_style & wxPD_AUTO_HIDE) != 0; - - if ( (shouldAutoHide && (isFinished || wasDestroyed)) - || (wasDestroyed && isCanceled) ) + /* + Decide whether we should end the dialog. This is done if either + the dialog object itself was destroyed or if the progress + finished and we were configured to hide automatically without + waiting for the user to dismiss us. + + Notice that we do not close the dialog if it was cancelled + because it's up to the user code in the main thread to decide + whether it really wants to cancel the dialog. + */ + if ( (sharedData->m_notifications & wxSPDD_DESTROYED) || + (sharedData->m_state == wxProgressDialog::Finished && + sharedData->m_style & wxPD_AUTO_HIDE) ) { ::EndDialog( hwnd, IDCLOSE ); }