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
);
895 // End dialog in three different cases:
896 // 1. Progress finished and dialog should automatically hide.
897 // 2. The wxProgressDialog object was destructed and should
898 // automatically hide.
899 // 3. The dialog was canceled and wxProgressDialog object
902 sharedData
->m_state
== wxGenericProgressDialog
::Canceled
;
904 sharedData
->m_state
== wxGenericProgressDialog
::Finished
;
906 (sharedData
->m_notifications
& wxSPDD_DESTROYED
) != 0;
907 bool shouldAutoHide
= (sharedData
->m_style
& wxPD_AUTO_HIDE
) != 0;
909 if ( (shouldAutoHide
&& (isFinished
|| wasDestroyed
))
910 || (wasDestroyed
&& isCanceled
) )
912 ::EndDialog( hwnd
, IDCLOSE
);
915 sharedData
->m_notifications
= 0;
924 #endif // wxHAS_MSW_TASKDIALOG
926 #endif // wxUSE_PROGRESSDLG && wxUSE_THREADS