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
) )
396 if ( newmsg
.empty() )
398 // Provide the finishing message if the application didn't.
399 m_message
= _("Done.");
400 m_sharedData
->m_message
= m_message
;
401 m_sharedData
->m_notifications
|= wxSPDD_MESSAGE_CHANGED
;
403 } // unlock m_sharedData->m_cs
405 // We only get here when we need to wait for the dialog to terminate so
406 // do just this by running a custom event loop until the dialog is
408 wxProgressDialogModalLoop
loop(*m_sharedData
);
412 #endif // wxHAS_MSW_TASKDIALOG
414 return wxGenericProgressDialog
::Update( value
, newmsg
, skip
);
417 bool wxProgressDialog
::Pulse(const wxString
& newmsg
, bool *skip
)
419 #ifdef wxHAS_MSW_TASKDIALOG
420 if ( HasNativeProgressDialog() )
422 wxCriticalSectionLocker
locker(m_sharedData
->m_cs
);
424 // Do nothing in canceled state.
425 if ( !DoNativeBeforeUpdate(skip
) )
428 if ( !m_sharedData
->m_progressBarMarquee
)
430 m_sharedData
->m_progressBarMarquee
= true;
431 m_sharedData
->m_notifications
|= wxSPDD_PBMARQUEE_CHANGED
;
434 if ( !newmsg
.empty() )
437 m_sharedData
->m_message
= newmsg
;
438 m_sharedData
->m_notifications
|= wxSPDD_MESSAGE_CHANGED
;
441 // The value passed here doesn't matter, only elapsed time makes sense
442 // in indeterminate mode anyhow.
443 UpdateExpandedInformation(0);
445 return m_sharedData
->m_state
!= Canceled
;
447 #endif // wxHAS_MSW_TASKDIALOG
449 return wxGenericProgressDialog
::Pulse( newmsg
, skip
);
452 bool wxProgressDialog
::DoNativeBeforeUpdate(bool *skip
)
454 #ifdef wxHAS_MSW_TASKDIALOG
455 if ( HasNativeProgressDialog() )
457 if ( m_sharedData
->m_skipped
)
459 if ( skip
&& !*skip
)
462 m_sharedData
->m_skipped
= false;
463 m_sharedData
->m_notifications
|= wxSPDD_ENABLE_SKIP
;
467 if ( m_sharedData
->m_state
== Canceled
)
468 m_timeStop
= m_sharedData
->m_timeStop
;
470 return m_sharedData
->m_state
!= Canceled
;
472 #endif // wxHAS_MSW_TASKDIALOG
475 wxFAIL_MSG( "unreachable" );
480 void wxProgressDialog
::Resume()
482 wxGenericProgressDialog
::Resume();
484 #ifdef wxHAS_MSW_TASKDIALOG
485 if ( HasNativeProgressDialog() )
490 wxCriticalSectionLocker
locker(m_sharedData
->m_cs
);
491 m_sharedData
->m_state
= m_state
;
493 // "Skip" was disabled when "Cancel" had been clicked, so re-enable
495 m_sharedData
->m_notifications
|= wxSPDD_ENABLE_SKIP
;
497 if ( !UsesCloseButtonOnly(GetPDStyle()) )
498 m_sharedData
->m_notifications
|= wxSPDD_ENABLE_ABORT
;
500 hwnd
= m_sharedData
->m_hwnd
;
501 } // Unlock m_cs, we can't call any function operating on a dialog with
502 // it locked as it can result in a deadlock if the dialog callback is
503 // called by Windows.
505 // After resuming we need to bring the window on top of the Z-order as
506 // it could be hidden by another window shown from the main thread,
507 // e.g. a confirmation dialog asking whether the user really wants to
510 // Notice that this must be done from the main thread as it owns the
511 // currently active window and attempts to do this from the task dialog
512 // thread would simply fail.
513 ::BringWindowToTop(hwnd
);
515 #endif // wxHAS_MSW_TASKDIALOG
518 int wxProgressDialog
::GetValue() const
520 #ifdef wxHAS_MSW_TASKDIALOG
521 if ( HasNativeProgressDialog() )
523 wxCriticalSectionLocker
locker(m_sharedData
->m_cs
);
524 return m_sharedData
->m_value
;
526 #endif // wxHAS_MSW_TASKDIALOG
528 return wxGenericProgressDialog
::GetValue();
531 wxString wxProgressDialog
::GetMessage() const
533 #ifdef wxHAS_MSW_TASKDIALOG
534 if ( HasNativeProgressDialog() )
536 #endif // wxHAS_MSW_TASKDIALOG
538 return wxGenericProgressDialog
::GetMessage();
541 void wxProgressDialog
::SetRange(int maximum
)
543 #ifdef wxHAS_MSW_TASKDIALOG
544 if ( HasNativeProgressDialog() )
548 wxCriticalSectionLocker
locker(m_sharedData
->m_cs
);
550 m_sharedData
->m_range
= maximum
;
551 m_sharedData
->m_notifications
|= wxSPDD_RANGE_CHANGED
;
555 #endif // wxHAS_MSW_TASKDIALOG
557 wxGenericProgressDialog
::SetRange( maximum
);
560 bool wxProgressDialog
::WasSkipped() const
562 #ifdef wxHAS_MSW_TASKDIALOG
563 if ( HasNativeProgressDialog() )
567 // Couldn't be skipped before being shown.
571 wxCriticalSectionLocker
locker(m_sharedData
->m_cs
);
572 return m_sharedData
->m_skipped
;
574 #endif // wxHAS_MSW_TASKDIALOG
576 return wxGenericProgressDialog
::WasSkipped();
579 bool wxProgressDialog
::WasCancelled() const
581 #ifdef wxHAS_MSW_TASKDIALOG
582 if ( HasNativeProgressDialog() )
584 wxCriticalSectionLocker
locker(m_sharedData
->m_cs
);
585 return m_sharedData
->m_state
== Canceled
;
587 #endif // wxHAS_MSW_TASKDIALOG
589 return wxGenericProgressDialog
::WasCancelled();
592 void wxProgressDialog
::SetTitle(const wxString
& title
)
594 #ifdef wxHAS_MSW_TASKDIALOG
595 if ( HasNativeProgressDialog() )
601 wxCriticalSectionLocker
locker(m_sharedData
->m_cs
);
602 m_sharedData
->m_title
= title
;
603 m_sharedData
->m_notifications
= wxSPDD_TITLE_CHANGED
;
606 #endif // wxHAS_MSW_TASKDIALOG
608 wxGenericProgressDialog
::SetTitle(title
);
611 wxString wxProgressDialog
::GetTitle() const
613 #ifdef wxHAS_MSW_TASKDIALOG
614 if ( HasNativeProgressDialog() )
616 #endif // wxHAS_MSW_TASKDIALOG
618 return wxGenericProgressDialog
::GetTitle();
621 bool wxProgressDialog
::Show(bool show
)
623 #ifdef wxHAS_MSW_TASKDIALOG
624 if ( HasNativeProgressDialog() )
626 // The dialog can't be hidden at all and showing it again after it had
627 // been shown before doesn't do anything.
628 if ( !show
|| m_taskDialogRunner
)
631 // We're showing the dialog for the first time, create the thread that
633 m_taskDialogRunner
= new wxProgressDialogTaskRunner
;
634 m_sharedData
= m_taskDialogRunner
->GetSharedDataObject();
636 // Initialize shared data.
637 m_sharedData
->m_title
= m_title
;
638 m_sharedData
->m_message
= m_message
;
639 m_sharedData
->m_range
= m_maximum
;
640 m_sharedData
->m_state
= Uncancelable
;
641 m_sharedData
->m_style
= GetPDStyle();
643 if ( HasPDFlag(wxPD_CAN_ABORT
) )
645 m_sharedData
->m_state
= Continue
;
646 m_sharedData
->m_labelCancel
= _("Cancel");
648 else if ( !HasPDFlag(wxPD_AUTO_HIDE
) )
650 m_sharedData
->m_labelCancel
= _("Close");
653 if ( HasPDFlag(wxPD_ELAPSED_TIME
|
654 wxPD_ESTIMATED_TIME
|
655 wxPD_REMAINING_TIME
) )
657 // Use a non-empty string just to have the collapsible pane shown.
658 m_sharedData
->m_expandedInformation
= " ";
661 // Do launch the thread.
662 if ( m_taskDialogRunner
->Create() != wxTHREAD_NO_ERROR
)
664 wxLogError( "Unable to create thread!" );
668 if ( m_taskDialogRunner
->Run() != wxTHREAD_NO_ERROR
)
670 wxLogError( "Unable to start thread!" );
674 // Do not show the underlying dialog.
677 #endif // wxHAS_MSW_TASKDIALOG
679 return wxGenericProgressDialog
::Show( show
);
682 bool wxProgressDialog
::HasNativeProgressDialog() const
684 #ifdef wxHAS_MSW_TASKDIALOG
685 // Native task dialog, if available, can't be used without any buttons so
686 // we fall back to the generic one if none of "Skip", "Cancel" and "Close"
688 return HasNativeTaskDialog()
689 && (HasPDFlag(wxPD_CAN_SKIP
| wxPD_CAN_ABORT
) ||
690 !HasPDFlag(wxPD_AUTO_HIDE
));
691 #else // !wxHAS_MSW_TASKDIALOG
692 // This shouldn't be even called in !wxHAS_MSW_TASKDIALOG case but as we
693 // still must define the function as returning something, return false.
695 #endif // wxHAS_MSW_TASKDIALOG/!wxHAS_MSW_TASKDIALOG
698 void wxProgressDialog
::UpdateExpandedInformation(int value
)
700 #ifdef wxHAS_MSW_TASKDIALOG
701 unsigned long elapsedTime
;
702 unsigned long estimatedTime
;
703 unsigned long remainingTime
;
704 UpdateTimeEstimates(value
, elapsedTime
, estimatedTime
, remainingTime
);
706 int realEstimatedTime
= estimatedTime
,
707 realRemainingTime
= remainingTime
;
708 if ( m_sharedData
->m_progressBarMarquee
)
710 // In indeterminate mode we don't have any estimation neither for the
711 // remaining nor for estimated time.
713 realRemainingTime
= -1;
716 wxString expandedInformation
;
718 // Calculate the three different timing values.
719 if ( HasPDFlag(wxPD_ELAPSED_TIME
) )
721 expandedInformation
<< GetElapsedLabel()
723 << GetFormattedTime(elapsedTime
);
726 if ( HasPDFlag(wxPD_ESTIMATED_TIME
) )
728 if ( !expandedInformation
.empty() )
729 expandedInformation
+= "\n";
731 expandedInformation
<< GetEstimatedLabel()
733 << GetFormattedTime(realEstimatedTime
);
736 if ( HasPDFlag(wxPD_REMAINING_TIME
) )
738 if ( !expandedInformation
.empty() )
739 expandedInformation
+= "\n";
741 expandedInformation
<< GetRemainingLabel()
743 << GetFormattedTime(realRemainingTime
);
746 // Update with new timing information.
747 if ( expandedInformation
!= m_sharedData
->m_expandedInformation
)
749 m_sharedData
->m_expandedInformation
= expandedInformation
;
750 m_sharedData
->m_notifications
|= wxSPDD_EXPINFO_CHANGED
;
752 #else // !wxHAS_MSW_TASKDIALOG
754 #endif // wxHAS_MSW_TASKDIALOG/!wxHAS_MSW_TASKDIALOG
757 // ----------------------------------------------------------------------------
758 // wxProgressDialogTaskRunner and related methods
759 // ----------------------------------------------------------------------------
761 #ifdef wxHAS_MSW_TASKDIALOG
763 void* wxProgressDialogTaskRunner
::Entry()
765 WinStruct
<TASKDIALOGCONFIG
> tdc
;
766 wxMSWTaskDialogConfig wxTdc
;
769 wxCriticalSectionLocker
locker(m_sharedData
.m_cs
);
771 wxTdc
.caption
= m_sharedData
.m_title
.wx_str();
772 wxTdc
.message
= m_sharedData
.m_message
.wx_str();
774 wxTdc
.MSWCommonTaskDialogInit( tdc
);
775 tdc
.pfCallback
= TaskDialogCallbackProc
;
776 tdc
.lpCallbackData
= (LONG_PTR
) &m_sharedData
;
778 // Undo some of the effects of MSWCommonTaskDialogInit().
779 tdc
.dwFlags
&= ~TDF_EXPAND_FOOTER_AREA
; // Expand in content area.
780 tdc
.dwCommonButtons
= 0; // Don't use common buttons.
782 wxTdc
.useCustomLabels
= true;
784 if ( m_sharedData
.m_style
& wxPD_CAN_SKIP
)
785 wxTdc
.AddTaskDialogButton( tdc
, Id_SkipBtn
, 0, _("Skip") );
787 // Use a Cancel button when requested or use a Close button when
788 // the dialog does not automatically hide.
789 if ( (m_sharedData
.m_style
& wxPD_CAN_ABORT
)
790 || !(m_sharedData
.m_style
& wxPD_AUTO_HIDE
) )
792 wxTdc
.AddTaskDialogButton( tdc
, IDCANCEL
, 0,
793 m_sharedData
.m_labelCancel
);
796 tdc
.dwFlags
|= TDF_CALLBACK_TIMER
| TDF_SHOW_PROGRESS_BAR
;
798 if ( !m_sharedData
.m_expandedInformation
.empty() )
800 tdc
.pszExpandedInformation
=
801 m_sharedData
.m_expandedInformation
.wx_str();
805 TaskDialogIndirect_t taskDialogIndirect
= GetTaskDialogIndirectFunc();
806 if ( !taskDialogIndirect
)
810 HRESULT hr
= taskDialogIndirect(&tdc
, &msAns
, NULL
, NULL
);
812 wxLogApiError( "TaskDialogIndirect", hr
);
814 // If the main thread is waiting for us to exit inside the event loop in
815 // Update(), wake it up so that it checks our status again.
823 wxProgressDialogTaskRunner
::TaskDialogCallbackProc
828 LPARAM
WXUNUSED(lParam
),
832 wxProgressDialogSharedData
* const sharedData
=
833 (wxProgressDialogSharedData
*) dwRefData
;
835 wxCriticalSectionLocker
locker(sharedData
->m_cs
);
837 switch ( uNotification
)
840 // Store the HWND for the main thread use.
841 sharedData
->m_hwnd
= hwnd
;
843 // Set the maximum value and disable Close button.
845 TDM_SET_PROGRESS_BAR_RANGE
,
847 MAKELPARAM(0, sharedData
->m_range
) );
849 if ( UsesCloseButtonOnly(sharedData
->m_style
) )
850 ::SendMessage( hwnd
, TDM_ENABLE_BUTTON
, IDCANCEL
, FALSE
);
853 case TDN_BUTTON_CLICKED
:
857 ::SendMessage(hwnd
, TDM_ENABLE_BUTTON
, Id_SkipBtn
, FALSE
);
858 sharedData
->m_skipped
= true;
862 if ( sharedData
->m_state
== wxProgressDialog
::Finished
)
864 // If the main thread is waiting for us, tell it that
865 // we're gone (and if it doesn't wait, it's harmless).
866 sharedData
->m_state
= wxProgressDialog
::Dismissed
;
868 // Let Windows close the dialog.
872 // Close button on the window triggers an IDCANCEL press,
873 // don't allow it when it should only be possible to close
874 // a finished dialog.
875 if ( !UsesCloseButtonOnly(sharedData
->m_style
) )
879 sharedData
->m_state
== wxProgressDialog
::Continue
,
881 "Dialog not in a cancelable state!"
884 ::SendMessage(hwnd
, TDM_ENABLE_BUTTON
, Id_SkipBtn
, FALSE
);
885 ::SendMessage(hwnd
, TDM_ENABLE_BUTTON
, IDCANCEL
, FALSE
);
887 sharedData
->m_timeStop
= wxGetCurrentTime();
888 sharedData
->m_state
= wxProgressDialog
::Canceled
;
896 PerformNotificationUpdates(hwnd
, sharedData
);
899 Decide whether we should end the dialog. This is done if either
900 the dialog object itself was destroyed or if the progress
901 finished and we were configured to hide automatically without
902 waiting for the user to dismiss us.
904 Notice that we do not close the dialog if it was cancelled
905 because it's up to the user code in the main thread to decide
906 whether it really wants to cancel the dialog.
908 if ( (sharedData
->m_notifications
& wxSPDD_DESTROYED
) ||
909 (sharedData
->m_state
== wxProgressDialog
::Finished
&&
910 sharedData
->m_style
& wxPD_AUTO_HIDE
) )
912 ::EndDialog( hwnd
, IDCLOSE
);
915 sharedData
->m_notifications
= 0;
924 #endif // wxHAS_MSW_TASKDIALOG
926 #endif // wxUSE_PROGRESSDLG && wxUSE_THREADS