1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/msw/progdlg.cpp
3 // Purpose: wxProgressDialog
4 // Author: Rickard Westerlund
7 // Copyright: (c) 2010 wxWidgets team
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
11 // ============================================================================
13 // ============================================================================
15 // ----------------------------------------------------------------------------
17 // ----------------------------------------------------------------------------
19 // For compilers that support precompilation, includes "wx.h".
20 #include "wx/wxprec.h"
26 #if wxUSE_PROGRESSDLG && wxUSE_THREADS
28 #include "wx/msw/private/msgdlg.h"
29 #include "wx/progdlg.h"
30 #include "wx/evtloop.h"
32 using namespace wxMSWMessageDialog
;
34 #ifdef wxHAS_MSW_TASKDIALOG
36 // ----------------------------------------------------------------------------
38 // ----------------------------------------------------------------------------
43 // Notification values of wxProgressDialogSharedData::m_notifications
44 const int wxSPDD_VALUE_CHANGED
= 0x0001;
45 const int wxSPDD_RANGE_CHANGED
= 0x0002;
46 const int wxSPDD_PBMARQUEE_CHANGED
= 0x0004;
47 const int wxSPDD_TITLE_CHANGED
= 0x0008;
48 const int wxSPDD_MESSAGE_CHANGED
= 0x0010;
49 const int wxSPDD_EXPINFO_CHANGED
= 0x0020;
50 const int wxSPDD_ENABLE_SKIP
= 0x0040;
51 const int wxSPDD_ENABLE_ABORT
= 0x0080;
52 const int wxSPDD_DISABLE_SKIP
= 0x0100;
53 const int wxSPDD_DISABLE_ABORT
= 0x0200;
54 const int wxSPDD_FINISHED
= 0x0400;
55 const int wxSPDD_DESTROYED
= 0x0800;
57 const int Id_SkipBtn
= wxID_HIGHEST
+ 1;
59 } // anonymous namespace
61 // ============================================================================
63 // ============================================================================
65 // Class used to share data between the main thread and the task dialog runner.
66 class wxProgressDialogSharedData
69 wxProgressDialogSharedData()
73 m_progressBarMarquee
= false;
78 wxCriticalSection m_cs
;
80 HWND m_hwnd
; // Task dialog handler
81 long m_style
; // wxProgressDialog style
86 wxString m_expandedInformation
;
87 wxString m_labelCancel
; // Privately used by callback.
88 unsigned long m_timeStop
;
90 wxProgressDialog::State m_state
;
91 bool m_progressBarMarquee
;
94 // Bit field that indicates fields that have been modified by the
95 // main thread so the task dialog runner knows what to update.
99 // Runner thread that takes care of displaying and updating the
101 class wxProgressDialogTaskRunner
: public wxThread
104 wxProgressDialogTaskRunner()
105 : wxThread(wxTHREAD_JOINABLE
)
108 wxProgressDialogSharedData
* GetSharedDataObject()
109 { return &m_sharedData
; }
112 wxProgressDialogSharedData m_sharedData
;
114 virtual void* Entry();
116 static HRESULT CALLBACK
TaskDialogCallbackProc(HWND hwnd
,
126 // A custom event loop which runs until the state of the dialog becomes
128 class wxProgressDialogModalLoop
: public wxEventLoop
131 wxProgressDialogModalLoop(wxProgressDialogSharedData
& data
)
137 virtual void OnNextIteration()
139 wxCriticalSectionLocker
locker(m_data
.m_cs
);
141 if ( m_data
.m_state
== wxProgressDialog::Dismissed
)
145 wxProgressDialogSharedData
& m_data
;
147 wxDECLARE_NO_COPY_CLASS(wxProgressDialogModalLoop
);
150 // ============================================================================
152 // ============================================================================
154 // This function returns true if the progress dialog with the given style
155 // (combination of wxPD_XXX constants) needs the "Close" button and this button
156 // only, i.e. not a "Cancel" one.
157 bool UsesCloseButtonOnly(long style
)
159 return !(style
& (wxPD_CAN_ABORT
| wxPD_AUTO_HIDE
));
162 BOOL CALLBACK
DisplayCloseButton(HWND hwnd
, LPARAM lParam
)
164 wxProgressDialogSharedData
*sharedData
=
165 (wxProgressDialogSharedData
*) lParam
;
167 if ( wxGetWindowText( hwnd
) == sharedData
->m_labelCancel
)
169 sharedData
->m_labelCancel
= _("Close");
170 SendMessage( hwnd
, WM_SETTEXT
, 0,
171 (LPARAM
) sharedData
->m_labelCancel
.wx_str() );
179 void PerformNotificationUpdates(HWND hwnd
,
180 wxProgressDialogSharedData
*sharedData
)
182 // Update the appropriate dialog fields.
183 if ( sharedData
->m_notifications
& wxSPDD_RANGE_CHANGED
)
186 TDM_SET_PROGRESS_BAR_RANGE
,
188 MAKELPARAM(0, sharedData
->m_range
) );
191 if ( sharedData
->m_notifications
& wxSPDD_VALUE_CHANGED
)
194 TDM_SET_PROGRESS_BAR_POS
,
199 if ( sharedData
->m_notifications
& wxSPDD_PBMARQUEE_CHANGED
)
201 BOOL val
= sharedData
->m_progressBarMarquee
? TRUE
: FALSE
;
203 TDM_SET_MARQUEE_PROGRESS_BAR
,
207 TDM_SET_PROGRESS_BAR_MARQUEE
,
212 if ( sharedData
->m_notifications
& wxSPDD_TITLE_CHANGED
)
213 ::SetWindowText( hwnd
, sharedData
->m_title
.wx_str() );
215 if ( sharedData
->m_notifications
& wxSPDD_MESSAGE_CHANGED
)
217 // Split the message in the title string and the rest if it has
220 title
= sharedData
->m_message
,
223 const size_t posNL
= title
.find('\n');
224 if ( posNL
!= wxString::npos
)
226 // There can an extra new line between the first and subsequent
227 // lines to separate them as it looks better with the generic
228 // version -- but in this one, they're already separated by the use
229 // of different dialog elements, so suppress the extra new line.
231 if ( posNL
< title
.length() - 1 && title
[posNL
+ 1] == '\n' )
234 body
.assign(title
, posNL
+ numNLs
, wxString::npos
);
239 TDM_SET_ELEMENT_TEXT
,
240 TDE_MAIN_INSTRUCTION
,
241 (LPARAM
) title
.wx_str() );
244 TDM_SET_ELEMENT_TEXT
,
246 (LPARAM
) body
.wx_str() );
249 if ( sharedData
->m_notifications
& wxSPDD_EXPINFO_CHANGED
)
251 const wxString
& expandedInformation
=
252 sharedData
->m_expandedInformation
;
253 if ( !expandedInformation
.empty() )
256 TDM_SET_ELEMENT_TEXT
,
257 TDE_EXPANDED_INFORMATION
,
258 (LPARAM
) expandedInformation
.wx_str() );
262 if ( sharedData
->m_notifications
& wxSPDD_ENABLE_SKIP
)
263 ::SendMessage( hwnd
, TDM_ENABLE_BUTTON
, Id_SkipBtn
, TRUE
);
265 if ( sharedData
->m_notifications
& wxSPDD_ENABLE_ABORT
)
266 ::SendMessage( hwnd
, TDM_ENABLE_BUTTON
, IDCANCEL
, TRUE
);
268 if ( sharedData
->m_notifications
& wxSPDD_DISABLE_SKIP
)
269 ::SendMessage( hwnd
, TDM_ENABLE_BUTTON
, Id_SkipBtn
, FALSE
);
271 if ( sharedData
->m_notifications
& wxSPDD_DISABLE_ABORT
)
272 ::SendMessage( hwnd
, TDM_ENABLE_BUTTON
, IDCANCEL
, FALSE
);
274 // Is the progress finished?
275 if ( sharedData
->m_notifications
& wxSPDD_FINISHED
)
277 sharedData
->m_state
= wxProgressDialog::Finished
;
279 if ( !(sharedData
->m_style
& wxPD_AUTO_HIDE
) )
281 // Change Cancel into Close and activate the button.
282 ::SendMessage( hwnd
, TDM_ENABLE_BUTTON
, Id_SkipBtn
, FALSE
);
283 ::SendMessage( hwnd
, TDM_ENABLE_BUTTON
, IDCANCEL
, TRUE
);
284 ::EnumChildWindows( hwnd
, DisplayCloseButton
,
285 (LPARAM
) sharedData
);
290 } // anonymous namespace
292 #endif // wxHAS_MSW_TASKDIALOG
294 // ============================================================================
295 // wxProgressDialog implementation
296 // ============================================================================
298 wxProgressDialog::wxProgressDialog( const wxString
& title
,
299 const wxString
& message
,
303 : wxGenericProgressDialog(parent
, style
),
304 m_taskDialogRunner(NULL
),
309 #ifdef wxHAS_MSW_TASKDIALOG
310 if ( HasNativeProgressDialog() )
315 DisableOtherWindows();
319 #endif // wxHAS_MSW_TASKDIALOG
321 Create(title
, message
, maximum
, parent
, style
);
324 wxProgressDialog::~wxProgressDialog()
326 #ifdef wxHAS_MSW_TASKDIALOG
327 if ( !m_taskDialogRunner
)
332 wxCriticalSectionLocker
locker(m_sharedData
->m_cs
);
333 m_sharedData
->m_notifications
|= wxSPDD_DESTROYED
;
336 m_taskDialogRunner
->Wait();
338 delete m_taskDialogRunner
;
340 ReenableOtherWindows();
342 if ( GetTopParent() )
343 GetTopParent()->Raise();
344 #endif // wxHAS_MSW_TASKDIALOG
347 bool wxProgressDialog::Update(int value
, const wxString
& newmsg
, bool *skip
)
349 #ifdef wxHAS_MSW_TASKDIALOG
350 if ( HasNativeProgressDialog() )
353 wxCriticalSectionLocker
locker(m_sharedData
->m_cs
);
355 // Do nothing in canceled state.
356 if ( !DoNativeBeforeUpdate(skip
) )
361 wxASSERT_MSG( value
<= m_maximum
, wxT("invalid progress value") );
363 m_sharedData
->m_value
= value
;
364 m_sharedData
->m_notifications
|= wxSPDD_VALUE_CHANGED
;
366 if ( !newmsg
.empty() )
369 m_sharedData
->m_message
= newmsg
;
370 m_sharedData
->m_notifications
|= wxSPDD_MESSAGE_CHANGED
;
373 if ( m_sharedData
->m_progressBarMarquee
)
375 m_sharedData
->m_progressBarMarquee
= false;
376 m_sharedData
->m_notifications
|= wxSPDD_PBMARQUEE_CHANGED
;
379 UpdateExpandedInformation( value
);
381 // If we didn't just reach the finish, all we have to do is to
382 // return true if the dialog wasn't cancelled and false otherwise.
383 if ( value
!= m_maximum
|| m_state
== Finished
)
384 return m_sharedData
->m_state
!= Canceled
;
387 // On finishing, the dialog without wxPD_AUTO_HIDE style becomes a
388 // modal one meaning that we must block here until the user
391 m_sharedData
->m_state
= Finished
;
392 m_sharedData
->m_notifications
|= wxSPDD_FINISHED
;
393 if ( !HasPDFlag(wxPD_AUTO_HIDE
) && newmsg
.empty() )
395 // Provide the finishing message if the application didn't.
396 m_message
= _("Done.");
397 m_sharedData
->m_message
= m_message
;
398 m_sharedData
->m_notifications
|= wxSPDD_MESSAGE_CHANGED
;
400 } // unlock m_sharedData->m_cs
402 // We only get here when we need to wait for the dialog to terminate so
403 // do just this by running a custom event loop until the dialog is
405 wxProgressDialogModalLoop
loop(*m_sharedData
);
409 #endif // wxHAS_MSW_TASKDIALOG
411 return wxGenericProgressDialog::Update( value
, newmsg
, skip
);
414 bool wxProgressDialog::Pulse(const wxString
& newmsg
, bool *skip
)
416 #ifdef wxHAS_MSW_TASKDIALOG
417 if ( HasNativeProgressDialog() )
419 wxCriticalSectionLocker
locker(m_sharedData
->m_cs
);
421 // Do nothing in canceled state.
422 if ( !DoNativeBeforeUpdate(skip
) )
425 if ( !m_sharedData
->m_progressBarMarquee
)
427 m_sharedData
->m_progressBarMarquee
= true;
428 m_sharedData
->m_notifications
|= wxSPDD_PBMARQUEE_CHANGED
;
431 if ( !newmsg
.empty() )
434 m_sharedData
->m_message
= newmsg
;
435 m_sharedData
->m_notifications
|= wxSPDD_MESSAGE_CHANGED
;
438 // The value passed here doesn't matter, only elapsed time makes sense
439 // in indeterminate mode anyhow.
440 UpdateExpandedInformation(0);
442 return m_sharedData
->m_state
!= Canceled
;
444 #endif // wxHAS_MSW_TASKDIALOG
446 return wxGenericProgressDialog::Pulse( newmsg
, skip
);
449 bool wxProgressDialog::DoNativeBeforeUpdate(bool *skip
)
451 #ifdef wxHAS_MSW_TASKDIALOG
452 if ( HasNativeProgressDialog() )
454 if ( m_sharedData
->m_skipped
)
456 if ( skip
&& !*skip
)
459 m_sharedData
->m_skipped
= false;
460 m_sharedData
->m_notifications
|= wxSPDD_ENABLE_SKIP
;
464 if ( m_sharedData
->m_state
== Canceled
)
465 m_timeStop
= m_sharedData
->m_timeStop
;
467 return m_sharedData
->m_state
!= Canceled
;
469 #endif // wxHAS_MSW_TASKDIALOG
472 wxFAIL_MSG( "unreachable" );
477 void wxProgressDialog::Resume()
479 wxGenericProgressDialog::Resume();
481 #ifdef wxHAS_MSW_TASKDIALOG
482 if ( HasNativeProgressDialog() )
487 wxCriticalSectionLocker
locker(m_sharedData
->m_cs
);
488 m_sharedData
->m_state
= m_state
;
490 // "Skip" was disabled when "Cancel" had been clicked, so re-enable
492 m_sharedData
->m_notifications
|= wxSPDD_ENABLE_SKIP
;
494 if ( !UsesCloseButtonOnly(GetPDStyle()) )
495 m_sharedData
->m_notifications
|= wxSPDD_ENABLE_ABORT
;
497 hwnd
= m_sharedData
->m_hwnd
;
498 } // Unlock m_cs, we can't call any function operating on a dialog with
499 // it locked as it can result in a deadlock if the dialog callback is
500 // called by Windows.
502 // After resuming we need to bring the window on top of the Z-order as
503 // it could be hidden by another window shown from the main thread,
504 // e.g. a confirmation dialog asking whether the user really wants to
507 // Notice that this must be done from the main thread as it owns the
508 // currently active window and attempts to do this from the task dialog
509 // thread would simply fail.
510 ::BringWindowToTop(hwnd
);
512 #endif // wxHAS_MSW_TASKDIALOG
515 int wxProgressDialog::GetValue() const
517 #ifdef wxHAS_MSW_TASKDIALOG
518 if ( HasNativeProgressDialog() )
520 wxCriticalSectionLocker
locker(m_sharedData
->m_cs
);
521 return m_sharedData
->m_value
;
523 #endif // wxHAS_MSW_TASKDIALOG
525 return wxGenericProgressDialog::GetValue();
528 wxString
wxProgressDialog::GetMessage() const
530 #ifdef wxHAS_MSW_TASKDIALOG
531 if ( HasNativeProgressDialog() )
533 #endif // wxHAS_MSW_TASKDIALOG
535 return wxGenericProgressDialog::GetMessage();
538 void wxProgressDialog::SetRange(int maximum
)
540 #ifdef wxHAS_MSW_TASKDIALOG
541 if ( HasNativeProgressDialog() )
545 wxCriticalSectionLocker
locker(m_sharedData
->m_cs
);
547 m_sharedData
->m_range
= maximum
;
548 m_sharedData
->m_notifications
|= wxSPDD_RANGE_CHANGED
;
552 #endif // wxHAS_MSW_TASKDIALOG
554 wxGenericProgressDialog::SetRange( maximum
);
557 bool wxProgressDialog::WasSkipped() const
559 #ifdef wxHAS_MSW_TASKDIALOG
560 if ( HasNativeProgressDialog() )
564 // Couldn't be skipped before being shown.
568 wxCriticalSectionLocker
locker(m_sharedData
->m_cs
);
569 return m_sharedData
->m_skipped
;
571 #endif // wxHAS_MSW_TASKDIALOG
573 return wxGenericProgressDialog::WasSkipped();
576 bool wxProgressDialog::WasCancelled() const
578 #ifdef wxHAS_MSW_TASKDIALOG
579 if ( HasNativeProgressDialog() )
581 wxCriticalSectionLocker
locker(m_sharedData
->m_cs
);
582 return m_sharedData
->m_state
== Canceled
;
584 #endif // wxHAS_MSW_TASKDIALOG
586 return wxGenericProgressDialog::WasCancelled();
589 void wxProgressDialog::SetTitle(const wxString
& title
)
591 #ifdef wxHAS_MSW_TASKDIALOG
592 if ( HasNativeProgressDialog() )
598 wxCriticalSectionLocker
locker(m_sharedData
->m_cs
);
599 m_sharedData
->m_title
= title
;
600 m_sharedData
->m_notifications
= wxSPDD_TITLE_CHANGED
;
603 #endif // wxHAS_MSW_TASKDIALOG
605 wxGenericProgressDialog::SetTitle(title
);
608 wxString
wxProgressDialog::GetTitle() const
610 #ifdef wxHAS_MSW_TASKDIALOG
611 if ( HasNativeProgressDialog() )
613 #endif // wxHAS_MSW_TASKDIALOG
615 return wxGenericProgressDialog::GetTitle();
618 bool wxProgressDialog::Show(bool show
)
620 #ifdef wxHAS_MSW_TASKDIALOG
621 if ( HasNativeProgressDialog() )
623 // The dialog can't be hidden at all and showing it again after it had
624 // been shown before doesn't do anything.
625 if ( !show
|| m_taskDialogRunner
)
628 // We're showing the dialog for the first time, create the thread that
630 m_taskDialogRunner
= new wxProgressDialogTaskRunner
;
631 m_sharedData
= m_taskDialogRunner
->GetSharedDataObject();
633 // Initialize shared data.
634 m_sharedData
->m_title
= m_title
;
635 m_sharedData
->m_message
= m_message
;
636 m_sharedData
->m_range
= m_maximum
;
637 m_sharedData
->m_state
= Uncancelable
;
638 m_sharedData
->m_style
= GetPDStyle();
640 if ( HasPDFlag(wxPD_CAN_ABORT
) )
642 m_sharedData
->m_state
= Continue
;
643 m_sharedData
->m_labelCancel
= _("Cancel");
645 else if ( !HasPDFlag(wxPD_AUTO_HIDE
) )
647 m_sharedData
->m_labelCancel
= _("Close");
650 if ( HasPDFlag(wxPD_ELAPSED_TIME
|
651 wxPD_ESTIMATED_TIME
|
652 wxPD_REMAINING_TIME
) )
654 // Use a non-empty string just to have the collapsible pane shown.
655 m_sharedData
->m_expandedInformation
= " ";
658 // Do launch the thread.
659 if ( m_taskDialogRunner
->Create() != wxTHREAD_NO_ERROR
)
661 wxLogError( "Unable to create thread!" );
665 if ( m_taskDialogRunner
->Run() != wxTHREAD_NO_ERROR
)
667 wxLogError( "Unable to start thread!" );
671 // Do not show the underlying dialog.
674 #endif // wxHAS_MSW_TASKDIALOG
676 return wxGenericProgressDialog::Show( show
);
679 bool wxProgressDialog::HasNativeProgressDialog() const
681 #ifdef wxHAS_MSW_TASKDIALOG
682 // Native task dialog, if available, can't be used without any buttons so
683 // we fall back to the generic one if none of "Skip", "Cancel" and "Close"
685 return HasNativeTaskDialog()
686 && (HasPDFlag(wxPD_CAN_SKIP
| wxPD_CAN_ABORT
) ||
687 !HasPDFlag(wxPD_AUTO_HIDE
));
688 #else // !wxHAS_MSW_TASKDIALOG
689 // This shouldn't be even called in !wxHAS_MSW_TASKDIALOG case but as we
690 // still must define the function as returning something, return false.
692 #endif // wxHAS_MSW_TASKDIALOG/!wxHAS_MSW_TASKDIALOG
695 void wxProgressDialog::UpdateExpandedInformation(int value
)
697 #ifdef wxHAS_MSW_TASKDIALOG
698 unsigned long elapsedTime
;
699 unsigned long estimatedTime
;
700 unsigned long remainingTime
;
701 UpdateTimeEstimates(value
, elapsedTime
, estimatedTime
, remainingTime
);
703 int realEstimatedTime
= estimatedTime
,
704 realRemainingTime
= remainingTime
;
705 if ( m_sharedData
->m_progressBarMarquee
)
707 // In indeterminate mode we don't have any estimation neither for the
708 // remaining nor for estimated time.
710 realRemainingTime
= -1;
713 wxString expandedInformation
;
715 // Calculate the three different timing values.
716 if ( HasPDFlag(wxPD_ELAPSED_TIME
) )
718 expandedInformation
<< GetElapsedLabel()
720 << GetFormattedTime(elapsedTime
);
723 if ( HasPDFlag(wxPD_ESTIMATED_TIME
) )
725 if ( !expandedInformation
.empty() )
726 expandedInformation
+= "\n";
728 expandedInformation
<< GetEstimatedLabel()
730 << GetFormattedTime(realEstimatedTime
);
733 if ( HasPDFlag(wxPD_REMAINING_TIME
) )
735 if ( !expandedInformation
.empty() )
736 expandedInformation
+= "\n";
738 expandedInformation
<< GetRemainingLabel()
740 << GetFormattedTime(realRemainingTime
);
743 // Update with new timing information.
744 if ( expandedInformation
!= m_sharedData
->m_expandedInformation
)
746 m_sharedData
->m_expandedInformation
= expandedInformation
;
747 m_sharedData
->m_notifications
|= wxSPDD_EXPINFO_CHANGED
;
749 #else // !wxHAS_MSW_TASKDIALOG
751 #endif // wxHAS_MSW_TASKDIALOG/!wxHAS_MSW_TASKDIALOG
754 // ----------------------------------------------------------------------------
755 // wxProgressDialogTaskRunner and related methods
756 // ----------------------------------------------------------------------------
758 #ifdef wxHAS_MSW_TASKDIALOG
760 void* wxProgressDialogTaskRunner::Entry()
762 WinStruct
<TASKDIALOGCONFIG
> tdc
;
763 wxMSWTaskDialogConfig wxTdc
;
766 wxCriticalSectionLocker
locker(m_sharedData
.m_cs
);
768 wxTdc
.caption
= m_sharedData
.m_title
.wx_str();
769 wxTdc
.message
= m_sharedData
.m_message
.wx_str();
771 wxTdc
.MSWCommonTaskDialogInit( tdc
);
772 tdc
.pfCallback
= TaskDialogCallbackProc
;
773 tdc
.lpCallbackData
= (LONG_PTR
) &m_sharedData
;
775 // Undo some of the effects of MSWCommonTaskDialogInit().
776 tdc
.dwFlags
&= ~TDF_EXPAND_FOOTER_AREA
; // Expand in content area.
777 tdc
.dwCommonButtons
= 0; // Don't use common buttons.
779 wxTdc
.useCustomLabels
= true;
781 if ( m_sharedData
.m_style
& wxPD_CAN_SKIP
)
782 wxTdc
.AddTaskDialogButton( tdc
, Id_SkipBtn
, 0, _("Skip") );
784 // Use a Cancel button when requested or use a Close button when
785 // the dialog does not automatically hide.
786 if ( (m_sharedData
.m_style
& wxPD_CAN_ABORT
)
787 || !(m_sharedData
.m_style
& wxPD_AUTO_HIDE
) )
789 wxTdc
.AddTaskDialogButton( tdc
, IDCANCEL
, 0,
790 m_sharedData
.m_labelCancel
);
793 tdc
.dwFlags
|= TDF_CALLBACK_TIMER
| TDF_SHOW_PROGRESS_BAR
;
795 if ( !m_sharedData
.m_expandedInformation
.empty() )
797 tdc
.pszExpandedInformation
=
798 m_sharedData
.m_expandedInformation
.wx_str();
802 TaskDialogIndirect_t taskDialogIndirect
= GetTaskDialogIndirectFunc();
803 if ( !taskDialogIndirect
)
807 HRESULT hr
= taskDialogIndirect(&tdc
, &msAns
, NULL
, NULL
);
809 wxLogApiError( "TaskDialogIndirect", hr
);
811 // If the main thread is waiting for us to exit inside the event loop in
812 // Update(), wake it up so that it checks our status again.
820 wxProgressDialogTaskRunner::TaskDialogCallbackProc
825 LPARAM
WXUNUSED(lParam
),
829 wxProgressDialogSharedData
* const sharedData
=
830 (wxProgressDialogSharedData
*) dwRefData
;
832 wxCriticalSectionLocker
locker(sharedData
->m_cs
);
834 switch ( uNotification
)
837 // Store the HWND for the main thread use.
838 sharedData
->m_hwnd
= hwnd
;
840 // Set the maximum value and disable Close button.
842 TDM_SET_PROGRESS_BAR_RANGE
,
844 MAKELPARAM(0, sharedData
->m_range
) );
846 if ( UsesCloseButtonOnly(sharedData
->m_style
) )
847 ::SendMessage( hwnd
, TDM_ENABLE_BUTTON
, IDCANCEL
, FALSE
);
850 case TDN_BUTTON_CLICKED
:
854 ::SendMessage(hwnd
, TDM_ENABLE_BUTTON
, Id_SkipBtn
, FALSE
);
855 sharedData
->m_skipped
= true;
859 if ( sharedData
->m_state
== wxProgressDialog::Finished
)
861 // If the main thread is waiting for us, tell it that
862 // we're gone (and if it doesn't wait, it's harmless).
863 sharedData
->m_state
= wxProgressDialog::Dismissed
;
865 // Let Windows close the dialog.
869 // Close button on the window triggers an IDCANCEL press,
870 // don't allow it when it should only be possible to close
871 // a finished dialog.
872 if ( !UsesCloseButtonOnly(sharedData
->m_style
) )
876 sharedData
->m_state
== wxProgressDialog::Continue
,
878 "Dialog not in a cancelable state!"
881 ::SendMessage(hwnd
, TDM_ENABLE_BUTTON
, Id_SkipBtn
, FALSE
);
882 ::SendMessage(hwnd
, TDM_ENABLE_BUTTON
, IDCANCEL
, FALSE
);
884 sharedData
->m_timeStop
= wxGetCurrentTime();
885 sharedData
->m_state
= wxProgressDialog::Canceled
;
893 PerformNotificationUpdates(hwnd
, sharedData
);
896 Decide whether we should end the dialog. This is done if either
897 the dialog object itself was destroyed or if the progress
898 finished and we were configured to hide automatically without
899 waiting for the user to dismiss us.
901 Notice that we do not close the dialog if it was cancelled
902 because it's up to the user code in the main thread to decide
903 whether it really wants to cancel the dialog.
905 if ( (sharedData
->m_notifications
& wxSPDD_DESTROYED
) ||
906 (sharedData
->m_state
== wxProgressDialog::Finished
&&
907 sharedData
->m_style
& wxPD_AUTO_HIDE
) )
909 ::EndDialog( hwnd
, IDCLOSE
);
912 sharedData
->m_notifications
= 0;
921 #endif // wxHAS_MSW_TASKDIALOG
923 #endif // wxUSE_PROGRESSDLG && wxUSE_THREADS