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/progdlg.h"
32 #include "wx/msgdlg.h"
33 #include "wx/stopwatch.h"
34 #include "wx/msw/private.h"
37 #include "wx/msw/private/msgdlg.h"
38 #include "wx/evtloop.h"
40 using namespace wxMSWMessageDialog
;
42 #ifdef wxHAS_MSW_TASKDIALOG
44 // ----------------------------------------------------------------------------
46 // ----------------------------------------------------------------------------
51 // Notification values of wxProgressDialogSharedData::m_notifications
52 const int wxSPDD_VALUE_CHANGED
= 0x0001;
53 const int wxSPDD_RANGE_CHANGED
= 0x0002;
54 const int wxSPDD_PBMARQUEE_CHANGED
= 0x0004;
55 const int wxSPDD_TITLE_CHANGED
= 0x0008;
56 const int wxSPDD_MESSAGE_CHANGED
= 0x0010;
57 const int wxSPDD_EXPINFO_CHANGED
= 0x0020;
58 const int wxSPDD_ENABLE_SKIP
= 0x0040;
59 const int wxSPDD_ENABLE_ABORT
= 0x0080;
60 const int wxSPDD_DISABLE_SKIP
= 0x0100;
61 const int wxSPDD_DISABLE_ABORT
= 0x0200;
62 const int wxSPDD_FINISHED
= 0x0400;
63 const int wxSPDD_DESTROYED
= 0x0800;
65 const int Id_SkipBtn
= wxID_HIGHEST
+ 1;
67 } // anonymous namespace
69 // ============================================================================
71 // ============================================================================
73 // Class used to share data between the main thread and the task dialog runner.
74 class wxProgressDialogSharedData
77 wxProgressDialogSharedData()
81 m_progressBarMarquee
= false;
87 wxCriticalSection m_cs
;
89 wxWindow
*m_parent
; // Parent window only used to center us over it.
90 HWND m_hwnd
; // Task dialog handler
91 long m_style
; // wxProgressDialog style
96 wxString m_expandedInformation
;
97 wxString m_labelCancel
; // Privately used by callback.
98 unsigned long m_timeStop
;
100 wxProgressDialog::State m_state
;
101 bool m_progressBarMarquee
;
104 // Bit field that indicates fields that have been modified by the
105 // main thread so the task dialog runner knows what to update.
109 // Runner thread that takes care of displaying and updating the
111 class wxProgressDialogTaskRunner
: public wxThread
114 wxProgressDialogTaskRunner()
115 : wxThread(wxTHREAD_JOINABLE
)
118 wxProgressDialogSharedData
* GetSharedDataObject()
119 { return &m_sharedData
; }
122 wxProgressDialogSharedData m_sharedData
;
124 virtual void* Entry();
126 static HRESULT CALLBACK
TaskDialogCallbackProc(HWND hwnd
,
136 // A custom event loop which runs until the state of the dialog becomes
138 class wxProgressDialogModalLoop
: public wxEventLoop
141 wxProgressDialogModalLoop(wxProgressDialogSharedData
& data
)
147 virtual void OnNextIteration()
149 wxCriticalSectionLocker
locker(m_data
.m_cs
);
151 if ( m_data
.m_state
== wxProgressDialog::Dismissed
)
155 wxProgressDialogSharedData
& m_data
;
157 wxDECLARE_NO_COPY_CLASS(wxProgressDialogModalLoop
);
160 // ============================================================================
162 // ============================================================================
164 BOOL CALLBACK
DisplayCloseButton(HWND hwnd
, LPARAM lParam
)
166 wxProgressDialogSharedData
*sharedData
=
167 (wxProgressDialogSharedData
*) lParam
;
169 if ( wxGetWindowText( hwnd
) == sharedData
->m_labelCancel
)
171 sharedData
->m_labelCancel
= _("Close");
172 SendMessage( hwnd
, WM_SETTEXT
, 0,
173 (LPARAM
) sharedData
->m_labelCancel
.wx_str() );
181 void PerformNotificationUpdates(HWND hwnd
,
182 wxProgressDialogSharedData
*sharedData
)
184 // Update the appropriate dialog fields.
185 if ( sharedData
->m_notifications
& wxSPDD_RANGE_CHANGED
)
188 TDM_SET_PROGRESS_BAR_RANGE
,
190 MAKELPARAM(0, sharedData
->m_range
) );
193 if ( sharedData
->m_notifications
& wxSPDD_VALUE_CHANGED
)
196 TDM_SET_PROGRESS_BAR_POS
,
201 if ( sharedData
->m_notifications
& wxSPDD_PBMARQUEE_CHANGED
)
203 BOOL val
= sharedData
->m_progressBarMarquee
? TRUE
: FALSE
;
205 TDM_SET_MARQUEE_PROGRESS_BAR
,
209 TDM_SET_PROGRESS_BAR_MARQUEE
,
214 if ( sharedData
->m_notifications
& wxSPDD_TITLE_CHANGED
)
215 ::SetWindowText( hwnd
, sharedData
->m_title
.wx_str() );
217 if ( sharedData
->m_notifications
& wxSPDD_MESSAGE_CHANGED
)
219 // Split the message in the title string and the rest if it has
222 title
= sharedData
->m_message
,
225 const size_t posNL
= title
.find('\n');
226 if ( posNL
!= wxString::npos
)
228 // There can an extra new line between the first and subsequent
229 // lines to separate them as it looks better with the generic
230 // version -- but in this one, they're already separated by the use
231 // of different dialog elements, so suppress the extra new line.
233 if ( posNL
< title
.length() - 1 && title
[posNL
+ 1] == '\n' )
236 body
.assign(title
, posNL
+ numNLs
, wxString::npos
);
239 else // A single line
241 // Don't use title without the body, this doesn't make sense.
246 TDM_SET_ELEMENT_TEXT
,
247 TDE_MAIN_INSTRUCTION
,
248 (LPARAM
) title
.wx_str() );
251 TDM_SET_ELEMENT_TEXT
,
253 (LPARAM
) body
.wx_str() );
256 if ( sharedData
->m_notifications
& wxSPDD_EXPINFO_CHANGED
)
258 const wxString
& expandedInformation
=
259 sharedData
->m_expandedInformation
;
260 if ( !expandedInformation
.empty() )
263 TDM_SET_ELEMENT_TEXT
,
264 TDE_EXPANDED_INFORMATION
,
265 (LPARAM
) expandedInformation
.wx_str() );
269 if ( sharedData
->m_notifications
& wxSPDD_ENABLE_SKIP
)
270 ::SendMessage( hwnd
, TDM_ENABLE_BUTTON
, Id_SkipBtn
, TRUE
);
272 if ( sharedData
->m_notifications
& wxSPDD_ENABLE_ABORT
)
273 ::SendMessage( hwnd
, TDM_ENABLE_BUTTON
, IDCANCEL
, TRUE
);
275 if ( sharedData
->m_notifications
& wxSPDD_DISABLE_SKIP
)
276 ::SendMessage( hwnd
, TDM_ENABLE_BUTTON
, Id_SkipBtn
, FALSE
);
278 if ( sharedData
->m_notifications
& wxSPDD_DISABLE_ABORT
)
279 ::SendMessage( hwnd
, TDM_ENABLE_BUTTON
, IDCANCEL
, FALSE
);
281 // Is the progress finished?
282 if ( sharedData
->m_notifications
& wxSPDD_FINISHED
)
284 sharedData
->m_state
= wxProgressDialog::Finished
;
286 if ( !(sharedData
->m_style
& wxPD_AUTO_HIDE
) )
288 // Change Cancel into Close and activate the button.
289 ::SendMessage( hwnd
, TDM_ENABLE_BUTTON
, Id_SkipBtn
, FALSE
);
290 ::SendMessage( hwnd
, TDM_ENABLE_BUTTON
, IDCANCEL
, TRUE
);
291 ::EnumChildWindows( hwnd
, DisplayCloseButton
,
292 (LPARAM
) sharedData
);
297 } // anonymous namespace
299 #endif // wxHAS_MSW_TASKDIALOG
301 // ============================================================================
302 // wxProgressDialog implementation
303 // ============================================================================
305 wxProgressDialog::wxProgressDialog( const wxString
& title
,
306 const wxString
& message
,
310 : wxGenericProgressDialog(parent
, style
),
311 m_taskDialogRunner(NULL
),
316 #ifdef wxHAS_MSW_TASKDIALOG
317 if ( HasNativeTaskDialog() )
322 DisableOtherWindows();
326 #endif // wxHAS_MSW_TASKDIALOG
328 Create(title
, message
, maximum
, parent
, style
);
331 wxProgressDialog::~wxProgressDialog()
333 #ifdef wxHAS_MSW_TASKDIALOG
334 if ( !m_taskDialogRunner
)
339 wxCriticalSectionLocker
locker(m_sharedData
->m_cs
);
340 m_sharedData
->m_notifications
|= wxSPDD_DESTROYED
;
343 m_taskDialogRunner
->Wait();
345 delete m_taskDialogRunner
;
347 ReenableOtherWindows();
349 if ( GetTopParent() )
350 GetTopParent()->Raise();
351 #endif // wxHAS_MSW_TASKDIALOG
354 bool wxProgressDialog::Update(int value
, const wxString
& newmsg
, bool *skip
)
356 #ifdef wxHAS_MSW_TASKDIALOG
357 if ( HasNativeTaskDialog() )
360 wxCriticalSectionLocker
locker(m_sharedData
->m_cs
);
362 // Do nothing in canceled state.
363 if ( !DoNativeBeforeUpdate(skip
) )
368 wxASSERT_MSG( value
<= m_maximum
, wxT("invalid progress value") );
370 m_sharedData
->m_value
= value
;
371 m_sharedData
->m_notifications
|= wxSPDD_VALUE_CHANGED
;
373 if ( !newmsg
.empty() )
376 m_sharedData
->m_message
= newmsg
;
377 m_sharedData
->m_notifications
|= wxSPDD_MESSAGE_CHANGED
;
380 if ( m_sharedData
->m_progressBarMarquee
)
382 m_sharedData
->m_progressBarMarquee
= false;
383 m_sharedData
->m_notifications
|= wxSPDD_PBMARQUEE_CHANGED
;
386 UpdateExpandedInformation( value
);
388 // If we didn't just reach the finish, all we have to do is to
389 // return true if the dialog wasn't cancelled and false otherwise.
390 if ( value
!= m_maximum
|| m_state
== Finished
)
391 return m_sharedData
->m_state
!= Canceled
;
394 // On finishing, the dialog without wxPD_AUTO_HIDE style becomes a
395 // modal one meaning that we must block here until the user
398 m_sharedData
->m_state
= Finished
;
399 m_sharedData
->m_notifications
|= wxSPDD_FINISHED
;
400 if ( HasPDFlag(wxPD_AUTO_HIDE
) )
403 if ( newmsg
.empty() )
405 // Provide the finishing message if the application didn't.
406 m_message
= _("Done.");
407 m_sharedData
->m_message
= m_message
;
408 m_sharedData
->m_notifications
|= wxSPDD_MESSAGE_CHANGED
;
410 } // unlock m_sharedData->m_cs
412 // We only get here when we need to wait for the dialog to terminate so
413 // do just this by running a custom event loop until the dialog is
415 wxProgressDialogModalLoop
loop(*m_sharedData
);
419 #endif // wxHAS_MSW_TASKDIALOG
421 return wxGenericProgressDialog::Update( value
, newmsg
, skip
);
424 bool wxProgressDialog::Pulse(const wxString
& newmsg
, bool *skip
)
426 #ifdef wxHAS_MSW_TASKDIALOG
427 if ( HasNativeTaskDialog() )
429 wxCriticalSectionLocker
locker(m_sharedData
->m_cs
);
431 // Do nothing in canceled state.
432 if ( !DoNativeBeforeUpdate(skip
) )
435 if ( !m_sharedData
->m_progressBarMarquee
)
437 m_sharedData
->m_progressBarMarquee
= true;
438 m_sharedData
->m_notifications
|= wxSPDD_PBMARQUEE_CHANGED
;
441 if ( !newmsg
.empty() )
444 m_sharedData
->m_message
= newmsg
;
445 m_sharedData
->m_notifications
|= wxSPDD_MESSAGE_CHANGED
;
448 // The value passed here doesn't matter, only elapsed time makes sense
449 // in indeterminate mode anyhow.
450 UpdateExpandedInformation(0);
452 return m_sharedData
->m_state
!= Canceled
;
454 #endif // wxHAS_MSW_TASKDIALOG
456 return wxGenericProgressDialog::Pulse( newmsg
, skip
);
459 bool wxProgressDialog::DoNativeBeforeUpdate(bool *skip
)
461 #ifdef wxHAS_MSW_TASKDIALOG
462 if ( HasNativeTaskDialog() )
464 if ( m_sharedData
->m_skipped
)
466 if ( skip
&& !*skip
)
469 m_sharedData
->m_skipped
= false;
470 m_sharedData
->m_notifications
|= wxSPDD_ENABLE_SKIP
;
474 if ( m_sharedData
->m_state
== Canceled
)
475 m_timeStop
= m_sharedData
->m_timeStop
;
477 return m_sharedData
->m_state
!= Canceled
;
479 #endif // wxHAS_MSW_TASKDIALOG
482 wxFAIL_MSG( "unreachable" );
487 void wxProgressDialog::Resume()
489 wxGenericProgressDialog::Resume();
491 #ifdef wxHAS_MSW_TASKDIALOG
492 if ( HasNativeTaskDialog() )
497 wxCriticalSectionLocker
locker(m_sharedData
->m_cs
);
498 m_sharedData
->m_state
= m_state
;
500 // "Skip" was disabled when "Cancel" had been clicked, so re-enable
502 m_sharedData
->m_notifications
|= wxSPDD_ENABLE_SKIP
;
504 // Also re-enable "Cancel" itself
505 if ( HasPDFlag(wxPD_CAN_ABORT
) )
506 m_sharedData
->m_notifications
|= wxSPDD_ENABLE_ABORT
;
508 hwnd
= m_sharedData
->m_hwnd
;
509 } // Unlock m_cs, we can't call any function operating on a dialog with
510 // it locked as it can result in a deadlock if the dialog callback is
511 // called by Windows.
513 // After resuming we need to bring the window on top of the Z-order as
514 // it could be hidden by another window shown from the main thread,
515 // e.g. a confirmation dialog asking whether the user really wants to
518 // Notice that this must be done from the main thread as it owns the
519 // currently active window and attempts to do this from the task dialog
520 // thread would simply fail.
521 ::BringWindowToTop(hwnd
);
523 #endif // wxHAS_MSW_TASKDIALOG
526 WXWidget
wxProgressDialog::GetHandle() const
528 #ifdef wxHAS_MSW_TASKDIALOG
529 if ( HasNativeTaskDialog() )
533 wxCriticalSectionLocker
locker(m_sharedData
->m_cs
);
534 m_sharedData
->m_state
= m_state
;
535 hwnd
= m_sharedData
->m_hwnd
;
540 return wxGenericProgressDialog::GetHandle();
543 int wxProgressDialog::GetValue() const
545 #ifdef wxHAS_MSW_TASKDIALOG
546 if ( HasNativeTaskDialog() )
548 wxCriticalSectionLocker
locker(m_sharedData
->m_cs
);
549 return m_sharedData
->m_value
;
551 #endif // wxHAS_MSW_TASKDIALOG
553 return wxGenericProgressDialog::GetValue();
556 wxString
wxProgressDialog::GetMessage() const
558 #ifdef wxHAS_MSW_TASKDIALOG
559 if ( HasNativeTaskDialog() )
561 #endif // wxHAS_MSW_TASKDIALOG
563 return wxGenericProgressDialog::GetMessage();
566 void wxProgressDialog::SetRange(int maximum
)
568 #ifdef wxHAS_MSW_TASKDIALOG
569 if ( HasNativeTaskDialog() )
573 wxCriticalSectionLocker
locker(m_sharedData
->m_cs
);
575 m_sharedData
->m_range
= maximum
;
576 m_sharedData
->m_notifications
|= wxSPDD_RANGE_CHANGED
;
580 #endif // wxHAS_MSW_TASKDIALOG
582 wxGenericProgressDialog::SetRange( maximum
);
585 bool wxProgressDialog::WasSkipped() const
587 #ifdef wxHAS_MSW_TASKDIALOG
588 if ( HasNativeTaskDialog() )
592 // Couldn't be skipped before being shown.
596 wxCriticalSectionLocker
locker(m_sharedData
->m_cs
);
597 return m_sharedData
->m_skipped
;
599 #endif // wxHAS_MSW_TASKDIALOG
601 return wxGenericProgressDialog::WasSkipped();
604 bool wxProgressDialog::WasCancelled() const
606 #ifdef wxHAS_MSW_TASKDIALOG
607 if ( HasNativeTaskDialog() )
609 wxCriticalSectionLocker
locker(m_sharedData
->m_cs
);
610 return m_sharedData
->m_state
== Canceled
;
612 #endif // wxHAS_MSW_TASKDIALOG
614 return wxGenericProgressDialog::WasCancelled();
617 void wxProgressDialog::SetTitle(const wxString
& title
)
619 #ifdef wxHAS_MSW_TASKDIALOG
620 if ( HasNativeTaskDialog() )
626 wxCriticalSectionLocker
locker(m_sharedData
->m_cs
);
627 m_sharedData
->m_title
= title
;
628 m_sharedData
->m_notifications
= wxSPDD_TITLE_CHANGED
;
631 #endif // wxHAS_MSW_TASKDIALOG
633 wxGenericProgressDialog::SetTitle(title
);
636 wxString
wxProgressDialog::GetTitle() const
638 #ifdef wxHAS_MSW_TASKDIALOG
639 if ( HasNativeTaskDialog() )
641 #endif // wxHAS_MSW_TASKDIALOG
643 return wxGenericProgressDialog::GetTitle();
646 bool wxProgressDialog::Show(bool show
)
648 #ifdef wxHAS_MSW_TASKDIALOG
649 if ( HasNativeTaskDialog() )
651 // The dialog can't be hidden at all and showing it again after it had
652 // been shown before doesn't do anything.
653 if ( !show
|| m_taskDialogRunner
)
656 // We're showing the dialog for the first time, create the thread that
658 m_taskDialogRunner
= new wxProgressDialogTaskRunner
;
659 m_sharedData
= m_taskDialogRunner
->GetSharedDataObject();
661 // Initialize shared data.
662 m_sharedData
->m_title
= m_title
;
663 m_sharedData
->m_message
= m_message
;
664 m_sharedData
->m_range
= m_maximum
;
665 m_sharedData
->m_state
= Uncancelable
;
666 m_sharedData
->m_style
= GetPDStyle();
667 m_sharedData
->m_parent
= GetTopParent();
669 if ( HasPDFlag(wxPD_CAN_ABORT
) )
671 m_sharedData
->m_state
= Continue
;
672 m_sharedData
->m_labelCancel
= _("Cancel");
674 else // Dialog can't be cancelled.
676 // We still must have at least a single button in the dialog so
677 // just don't call it "Cancel" in this case.
678 m_sharedData
->m_labelCancel
= _("Close");
681 if ( HasPDFlag(wxPD_ELAPSED_TIME
|
682 wxPD_ESTIMATED_TIME
|
683 wxPD_REMAINING_TIME
) )
685 // Use a non-empty string just to have the collapsible pane shown.
686 m_sharedData
->m_expandedInformation
= " ";
689 // Do launch the thread.
690 if ( m_taskDialogRunner
->Create() != wxTHREAD_NO_ERROR
)
692 wxLogError( "Unable to create thread!" );
696 if ( m_taskDialogRunner
->Run() != wxTHREAD_NO_ERROR
)
698 wxLogError( "Unable to start thread!" );
702 // Do not show the underlying dialog.
705 #endif // wxHAS_MSW_TASKDIALOG
707 return wxGenericProgressDialog::Show( show
);
710 void wxProgressDialog::UpdateExpandedInformation(int value
)
712 #ifdef wxHAS_MSW_TASKDIALOG
713 unsigned long elapsedTime
;
714 unsigned long estimatedTime
;
715 unsigned long remainingTime
;
716 UpdateTimeEstimates(value
, elapsedTime
, estimatedTime
, remainingTime
);
718 int realEstimatedTime
= estimatedTime
,
719 realRemainingTime
= remainingTime
;
720 if ( m_sharedData
->m_progressBarMarquee
)
722 // In indeterminate mode we don't have any estimation neither for the
723 // remaining nor for estimated time.
725 realRemainingTime
= -1;
728 wxString expandedInformation
;
730 // Calculate the three different timing values.
731 if ( HasPDFlag(wxPD_ELAPSED_TIME
) )
733 expandedInformation
<< GetElapsedLabel()
735 << GetFormattedTime(elapsedTime
);
738 if ( HasPDFlag(wxPD_ESTIMATED_TIME
) )
740 if ( !expandedInformation
.empty() )
741 expandedInformation
+= "\n";
743 expandedInformation
<< GetEstimatedLabel()
745 << GetFormattedTime(realEstimatedTime
);
748 if ( HasPDFlag(wxPD_REMAINING_TIME
) )
750 if ( !expandedInformation
.empty() )
751 expandedInformation
+= "\n";
753 expandedInformation
<< GetRemainingLabel()
755 << GetFormattedTime(realRemainingTime
);
758 // Update with new timing information.
759 if ( expandedInformation
!= m_sharedData
->m_expandedInformation
)
761 m_sharedData
->m_expandedInformation
= expandedInformation
;
762 m_sharedData
->m_notifications
|= wxSPDD_EXPINFO_CHANGED
;
764 #else // !wxHAS_MSW_TASKDIALOG
766 #endif // wxHAS_MSW_TASKDIALOG/!wxHAS_MSW_TASKDIALOG
769 // ----------------------------------------------------------------------------
770 // wxProgressDialogTaskRunner and related methods
771 // ----------------------------------------------------------------------------
773 #ifdef wxHAS_MSW_TASKDIALOG
775 void* wxProgressDialogTaskRunner::Entry()
777 WinStruct
<TASKDIALOGCONFIG
> tdc
;
778 wxMSWTaskDialogConfig wxTdc
;
781 wxCriticalSectionLocker
locker(m_sharedData
.m_cs
);
783 wxTdc
.caption
= m_sharedData
.m_title
.wx_str();
784 wxTdc
.message
= m_sharedData
.m_message
.wx_str();
786 // MSWCommonTaskDialogInit() will add an IDCANCEL button but we need to
787 // give it the correct label.
788 wxTdc
.btnOKLabel
= m_sharedData
.m_labelCancel
;
789 wxTdc
.useCustomLabels
= true;
791 wxTdc
.MSWCommonTaskDialogInit( tdc
);
792 tdc
.pfCallback
= TaskDialogCallbackProc
;
793 tdc
.lpCallbackData
= (LONG_PTR
) &m_sharedData
;
795 // Undo some of the effects of MSWCommonTaskDialogInit().
796 tdc
.dwFlags
&= ~TDF_EXPAND_FOOTER_AREA
; // Expand in content area.
797 tdc
.dwCommonButtons
= 0; // Don't use common buttons.
799 if ( m_sharedData
.m_style
& wxPD_CAN_SKIP
)
800 wxTdc
.AddTaskDialogButton( tdc
, Id_SkipBtn
, 0, _("Skip") );
802 tdc
.dwFlags
|= TDF_CALLBACK_TIMER
| TDF_SHOW_PROGRESS_BAR
;
804 if ( !m_sharedData
.m_expandedInformation
.empty() )
806 tdc
.pszExpandedInformation
=
807 m_sharedData
.m_expandedInformation
.wx_str();
811 TaskDialogIndirect_t taskDialogIndirect
= GetTaskDialogIndirectFunc();
812 if ( !taskDialogIndirect
)
816 HRESULT hr
= taskDialogIndirect(&tdc
, &msAns
, NULL
, NULL
);
818 wxLogApiError( "TaskDialogIndirect", hr
);
820 // If the main thread is waiting for us to exit inside the event loop in
821 // Update(), wake it up so that it checks our status again.
829 wxProgressDialogTaskRunner::TaskDialogCallbackProc
834 LPARAM
WXUNUSED(lParam
),
838 wxProgressDialogSharedData
* const sharedData
=
839 (wxProgressDialogSharedData
*) dwRefData
;
841 wxCriticalSectionLocker
locker(sharedData
->m_cs
);
843 switch ( uNotification
)
846 // Store the HWND for the main thread use.
847 sharedData
->m_hwnd
= hwnd
;
849 // Set the maximum value and disable Close button.
851 TDM_SET_PROGRESS_BAR_RANGE
,
853 MAKELPARAM(0, sharedData
->m_range
) );
855 // We always create this task dialog with NULL parent because our
856 // parent in wx sense is a window created from a different thread
857 // and so can't be used as our real parent. However we still center
858 // this window on the parent one as the task dialogs do with their
859 // real parent usually.
860 if ( sharedData
->m_parent
)
862 wxRect
rect(wxRectFromRECT(wxGetWindowRect(hwnd
)));
863 rect
= rect
.CentreIn(sharedData
->m_parent
->GetRect());
876 // If we can't be aborted, the "Close" button will only be enabled
877 // when the progress ends (and not even then with wxPD_AUTO_HIDE).
878 if ( !(sharedData
->m_style
& wxPD_CAN_ABORT
) )
879 ::SendMessage( hwnd
, TDM_ENABLE_BUTTON
, IDCANCEL
, FALSE
);
882 case TDN_BUTTON_CLICKED
:
886 ::SendMessage(hwnd
, TDM_ENABLE_BUTTON
, Id_SkipBtn
, FALSE
);
887 sharedData
->m_skipped
= true;
891 if ( sharedData
->m_state
== wxProgressDialog::Finished
)
893 // If the main thread is waiting for us, tell it that
894 // we're gone (and if it doesn't wait, it's harmless).
895 sharedData
->m_state
= wxProgressDialog::Dismissed
;
897 // Let Windows close the dialog.
901 // Close button on the window triggers an IDCANCEL press,
902 // don't allow it when it should only be possible to close
903 // a finished dialog.
904 if ( sharedData
->m_style
& wxPD_CAN_ABORT
)
908 sharedData
->m_state
== wxProgressDialog::Continue
,
910 "Dialog not in a cancelable state!"
913 ::SendMessage(hwnd
, TDM_ENABLE_BUTTON
, Id_SkipBtn
, FALSE
);
914 ::SendMessage(hwnd
, TDM_ENABLE_BUTTON
, IDCANCEL
, FALSE
);
916 sharedData
->m_timeStop
= wxGetCurrentTime();
917 sharedData
->m_state
= wxProgressDialog::Canceled
;
925 PerformNotificationUpdates(hwnd
, sharedData
);
928 Decide whether we should end the dialog. This is done if either
929 the dialog object itself was destroyed or if the progress
930 finished and we were configured to hide automatically without
931 waiting for the user to dismiss us.
933 Notice that we do not close the dialog if it was cancelled
934 because it's up to the user code in the main thread to decide
935 whether it really wants to cancel the dialog.
937 if ( (sharedData
->m_notifications
& wxSPDD_DESTROYED
) ||
938 (sharedData
->m_state
== wxProgressDialog::Finished
&&
939 sharedData
->m_style
& wxPD_AUTO_HIDE
) )
941 ::EndDialog( hwnd
, IDCLOSE
);
944 sharedData
->m_notifications
= 0;
953 #endif // wxHAS_MSW_TASKDIALOG
955 #endif // wxUSE_PROGRESSDLG && wxUSE_THREADS