1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/msw/progdlg.cpp
3 // Purpose: wxProgressDialog
4 // Author: Rickard Westerlund
6 // Copyright: (c) 2010 wxWidgets team
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
10 // ============================================================================
12 // ============================================================================
14 // ----------------------------------------------------------------------------
16 // ----------------------------------------------------------------------------
18 // For compilers that support precompilation, includes "wx.h".
19 #include "wx/wxprec.h"
25 #if wxUSE_PROGRESSDLG && wxUSE_THREADS
27 #include "wx/progdlg.h"
31 #include "wx/msgdlg.h"
32 #include "wx/stopwatch.h"
33 #include "wx/msw/private.h"
36 #include "wx/msw/private/msgdlg.h"
37 #include "wx/evtloop.h"
39 using namespace wxMSWMessageDialog
;
41 #ifdef wxHAS_MSW_TASKDIALOG
43 // ----------------------------------------------------------------------------
45 // ----------------------------------------------------------------------------
50 // Notification values of wxProgressDialogSharedData::m_notifications
51 const int wxSPDD_VALUE_CHANGED
= 0x0001;
52 const int wxSPDD_RANGE_CHANGED
= 0x0002;
53 const int wxSPDD_PBMARQUEE_CHANGED
= 0x0004;
54 const int wxSPDD_TITLE_CHANGED
= 0x0008;
55 const int wxSPDD_MESSAGE_CHANGED
= 0x0010;
56 const int wxSPDD_EXPINFO_CHANGED
= 0x0020;
57 const int wxSPDD_ENABLE_SKIP
= 0x0040;
58 const int wxSPDD_ENABLE_ABORT
= 0x0080;
59 const int wxSPDD_DISABLE_SKIP
= 0x0100;
60 const int wxSPDD_DISABLE_ABORT
= 0x0200;
61 const int wxSPDD_FINISHED
= 0x0400;
62 const int wxSPDD_DESTROYED
= 0x0800;
64 const int Id_SkipBtn
= wxID_HIGHEST
+ 1;
66 } // anonymous namespace
68 // ============================================================================
70 // ============================================================================
72 // Class used to share data between the main thread and the task dialog runner.
73 class wxProgressDialogSharedData
76 wxProgressDialogSharedData()
80 m_progressBarMarquee
= false;
86 wxCriticalSection m_cs
;
88 wxWindow
*m_parent
; // Parent window only used to center us over it.
89 HWND m_hwnd
; // Task dialog handler
90 long m_style
; // wxProgressDialog style
95 wxString m_expandedInformation
;
96 wxString m_labelCancel
; // Privately used by callback.
97 unsigned long m_timeStop
;
99 wxProgressDialog::State m_state
;
100 bool m_progressBarMarquee
;
103 // Bit field that indicates fields that have been modified by the
104 // main thread so the task dialog runner knows what to update.
108 // Runner thread that takes care of displaying and updating the
110 class wxProgressDialogTaskRunner
: public wxThread
113 wxProgressDialogTaskRunner()
114 : wxThread(wxTHREAD_JOINABLE
)
117 wxProgressDialogSharedData
* GetSharedDataObject()
118 { return &m_sharedData
; }
121 wxProgressDialogSharedData m_sharedData
;
123 virtual void* Entry();
125 static HRESULT CALLBACK
TaskDialogCallbackProc(HWND hwnd
,
135 // A custom event loop which runs until the state of the dialog becomes
137 class wxProgressDialogModalLoop
: public wxEventLoop
140 wxProgressDialogModalLoop(wxProgressDialogSharedData
& data
)
146 virtual void OnNextIteration()
148 wxCriticalSectionLocker
locker(m_data
.m_cs
);
150 if ( m_data
.m_state
== wxProgressDialog::Dismissed
)
154 wxProgressDialogSharedData
& m_data
;
156 wxDECLARE_NO_COPY_CLASS(wxProgressDialogModalLoop
);
159 // ============================================================================
161 // ============================================================================
163 BOOL CALLBACK
DisplayCloseButton(HWND hwnd
, LPARAM lParam
)
165 wxProgressDialogSharedData
*sharedData
=
166 (wxProgressDialogSharedData
*) lParam
;
168 if ( wxGetWindowText( hwnd
) == sharedData
->m_labelCancel
)
170 sharedData
->m_labelCancel
= _("Close");
171 SendMessage( hwnd
, WM_SETTEXT
, 0,
172 wxMSW_CONV_LPARAM(sharedData
->m_labelCancel
) );
180 void PerformNotificationUpdates(HWND hwnd
,
181 wxProgressDialogSharedData
*sharedData
)
183 // Update the appropriate dialog fields.
184 if ( sharedData
->m_notifications
& wxSPDD_RANGE_CHANGED
)
187 TDM_SET_PROGRESS_BAR_RANGE
,
189 MAKELPARAM(0, sharedData
->m_range
) );
192 if ( sharedData
->m_notifications
& wxSPDD_VALUE_CHANGED
)
195 TDM_SET_PROGRESS_BAR_POS
,
200 if ( sharedData
->m_notifications
& wxSPDD_PBMARQUEE_CHANGED
)
202 BOOL val
= sharedData
->m_progressBarMarquee
? TRUE
: FALSE
;
204 TDM_SET_MARQUEE_PROGRESS_BAR
,
208 TDM_SET_PROGRESS_BAR_MARQUEE
,
213 if ( sharedData
->m_notifications
& wxSPDD_TITLE_CHANGED
)
214 ::SetWindowText( hwnd
, sharedData
->m_title
.t_str() );
216 if ( sharedData
->m_notifications
& wxSPDD_MESSAGE_CHANGED
)
218 // Split the message in the title string and the rest if it has
221 title
= sharedData
->m_message
,
224 const size_t posNL
= title
.find('\n');
225 if ( posNL
!= wxString::npos
)
227 // There can an extra new line between the first and subsequent
228 // lines to separate them as it looks better with the generic
229 // version -- but in this one, they're already separated by the use
230 // of different dialog elements, so suppress the extra new line.
232 if ( posNL
< title
.length() - 1 && title
[posNL
+ 1] == '\n' )
235 body
.assign(title
, posNL
+ numNLs
, wxString::npos
);
238 else // A single line
240 // Don't use title without the body, this doesn't make sense.
245 TDM_SET_ELEMENT_TEXT
,
246 TDE_MAIN_INSTRUCTION
,
247 wxMSW_CONV_LPARAM(title
) );
250 TDM_SET_ELEMENT_TEXT
,
252 wxMSW_CONV_LPARAM(body
) );
255 if ( sharedData
->m_notifications
& wxSPDD_EXPINFO_CHANGED
)
257 const wxString
& expandedInformation
=
258 sharedData
->m_expandedInformation
;
259 if ( !expandedInformation
.empty() )
262 TDM_SET_ELEMENT_TEXT
,
263 TDE_EXPANDED_INFORMATION
,
264 wxMSW_CONV_LPARAM(expandedInformation
) );
268 if ( sharedData
->m_notifications
& wxSPDD_ENABLE_SKIP
)
269 ::SendMessage( hwnd
, TDM_ENABLE_BUTTON
, Id_SkipBtn
, TRUE
);
271 if ( sharedData
->m_notifications
& wxSPDD_ENABLE_ABORT
)
272 ::SendMessage( hwnd
, TDM_ENABLE_BUTTON
, IDCANCEL
, TRUE
);
274 if ( sharedData
->m_notifications
& wxSPDD_DISABLE_SKIP
)
275 ::SendMessage( hwnd
, TDM_ENABLE_BUTTON
, Id_SkipBtn
, FALSE
);
277 if ( sharedData
->m_notifications
& wxSPDD_DISABLE_ABORT
)
278 ::SendMessage( hwnd
, TDM_ENABLE_BUTTON
, IDCANCEL
, FALSE
);
280 // Is the progress finished?
281 if ( sharedData
->m_notifications
& wxSPDD_FINISHED
)
283 sharedData
->m_state
= wxProgressDialog::Finished
;
285 if ( !(sharedData
->m_style
& wxPD_AUTO_HIDE
) )
287 // Change Cancel into Close and activate the button.
288 ::SendMessage( hwnd
, TDM_ENABLE_BUTTON
, Id_SkipBtn
, FALSE
);
289 ::SendMessage( hwnd
, TDM_ENABLE_BUTTON
, IDCANCEL
, TRUE
);
290 ::EnumChildWindows( hwnd
, DisplayCloseButton
,
291 (LPARAM
) sharedData
);
296 } // anonymous namespace
298 #endif // wxHAS_MSW_TASKDIALOG
300 // ============================================================================
301 // wxProgressDialog implementation
302 // ============================================================================
304 wxProgressDialog::wxProgressDialog( const wxString
& title
,
305 const wxString
& message
,
309 : wxGenericProgressDialog(),
310 m_taskDialogRunner(NULL
),
315 #ifdef wxHAS_MSW_TASKDIALOG
316 if ( HasNativeTaskDialog() )
318 SetTopParent(parent
);
323 DisableOtherWindows();
327 #endif // wxHAS_MSW_TASKDIALOG
329 Create(title
, message
, maximum
, parent
, style
);
332 wxProgressDialog::~wxProgressDialog()
334 #ifdef wxHAS_MSW_TASKDIALOG
335 if ( !m_taskDialogRunner
)
340 wxCriticalSectionLocker
locker(m_sharedData
->m_cs
);
341 m_sharedData
->m_notifications
|= wxSPDD_DESTROYED
;
344 m_taskDialogRunner
->Wait();
346 delete m_taskDialogRunner
;
348 ReenableOtherWindows();
350 if ( GetTopParent() )
351 GetTopParent()->Raise();
352 #endif // wxHAS_MSW_TASKDIALOG
355 bool wxProgressDialog::Update(int value
, const wxString
& newmsg
, bool *skip
)
357 #ifdef wxHAS_MSW_TASKDIALOG
358 if ( HasNativeTaskDialog() )
361 wxCriticalSectionLocker
locker(m_sharedData
->m_cs
);
363 // Do nothing in canceled state.
364 if ( !DoNativeBeforeUpdate(skip
) )
369 wxASSERT_MSG( value
<= m_maximum
, wxT("invalid progress value") );
371 m_sharedData
->m_value
= value
;
372 m_sharedData
->m_notifications
|= wxSPDD_VALUE_CHANGED
;
374 if ( !newmsg
.empty() )
377 m_sharedData
->m_message
= newmsg
;
378 m_sharedData
->m_notifications
|= wxSPDD_MESSAGE_CHANGED
;
381 if ( m_sharedData
->m_progressBarMarquee
)
383 m_sharedData
->m_progressBarMarquee
= false;
384 m_sharedData
->m_notifications
|= wxSPDD_PBMARQUEE_CHANGED
;
387 UpdateExpandedInformation( value
);
389 // If we didn't just reach the finish, all we have to do is to
390 // return true if the dialog wasn't cancelled and false otherwise.
391 if ( value
!= m_maximum
|| m_state
== Finished
)
392 return m_sharedData
->m_state
!= Canceled
;
395 // On finishing, the dialog without wxPD_AUTO_HIDE style becomes a
396 // modal one meaning that we must block here until the user
399 m_sharedData
->m_state
= Finished
;
400 m_sharedData
->m_notifications
|= wxSPDD_FINISHED
;
401 if ( HasPDFlag(wxPD_AUTO_HIDE
) )
404 if ( newmsg
.empty() )
406 // Provide the finishing message if the application didn't.
407 m_message
= _("Done.");
408 m_sharedData
->m_message
= m_message
;
409 m_sharedData
->m_notifications
|= wxSPDD_MESSAGE_CHANGED
;
411 } // unlock m_sharedData->m_cs
413 // We only get here when we need to wait for the dialog to terminate so
414 // do just this by running a custom event loop until the dialog is
416 wxProgressDialogModalLoop
loop(*m_sharedData
);
420 #endif // wxHAS_MSW_TASKDIALOG
422 return wxGenericProgressDialog::Update( value
, newmsg
, skip
);
425 bool wxProgressDialog::Pulse(const wxString
& newmsg
, bool *skip
)
427 #ifdef wxHAS_MSW_TASKDIALOG
428 if ( HasNativeTaskDialog() )
430 wxCriticalSectionLocker
locker(m_sharedData
->m_cs
);
432 // Do nothing in canceled state.
433 if ( !DoNativeBeforeUpdate(skip
) )
436 if ( !m_sharedData
->m_progressBarMarquee
)
438 m_sharedData
->m_progressBarMarquee
= true;
439 m_sharedData
->m_notifications
|= wxSPDD_PBMARQUEE_CHANGED
;
442 if ( !newmsg
.empty() )
445 m_sharedData
->m_message
= newmsg
;
446 m_sharedData
->m_notifications
|= wxSPDD_MESSAGE_CHANGED
;
449 // The value passed here doesn't matter, only elapsed time makes sense
450 // in indeterminate mode anyhow.
451 UpdateExpandedInformation(0);
453 return m_sharedData
->m_state
!= Canceled
;
455 #endif // wxHAS_MSW_TASKDIALOG
457 return wxGenericProgressDialog::Pulse( newmsg
, skip
);
460 bool wxProgressDialog::DoNativeBeforeUpdate(bool *skip
)
462 #ifdef wxHAS_MSW_TASKDIALOG
463 if ( HasNativeTaskDialog() )
465 if ( m_sharedData
->m_skipped
)
467 if ( skip
&& !*skip
)
470 m_sharedData
->m_skipped
= false;
471 m_sharedData
->m_notifications
|= wxSPDD_ENABLE_SKIP
;
475 if ( m_sharedData
->m_state
== Canceled
)
476 m_timeStop
= m_sharedData
->m_timeStop
;
478 return m_sharedData
->m_state
!= Canceled
;
480 #endif // wxHAS_MSW_TASKDIALOG
483 wxFAIL_MSG( "unreachable" );
488 void wxProgressDialog::Resume()
490 wxGenericProgressDialog::Resume();
492 #ifdef wxHAS_MSW_TASKDIALOG
493 if ( HasNativeTaskDialog() )
498 wxCriticalSectionLocker
locker(m_sharedData
->m_cs
);
499 m_sharedData
->m_state
= m_state
;
501 // "Skip" was disabled when "Cancel" had been clicked, so re-enable
503 m_sharedData
->m_notifications
|= wxSPDD_ENABLE_SKIP
;
505 // Also re-enable "Cancel" itself
506 if ( HasPDFlag(wxPD_CAN_ABORT
) )
507 m_sharedData
->m_notifications
|= wxSPDD_ENABLE_ABORT
;
509 hwnd
= m_sharedData
->m_hwnd
;
510 } // Unlock m_cs, we can't call any function operating on a dialog with
511 // it locked as it can result in a deadlock if the dialog callback is
512 // called by Windows.
514 // After resuming we need to bring the window on top of the Z-order as
515 // it could be hidden by another window shown from the main thread,
516 // e.g. a confirmation dialog asking whether the user really wants to
519 // Notice that this must be done from the main thread as it owns the
520 // currently active window and attempts to do this from the task dialog
521 // thread would simply fail.
522 ::BringWindowToTop(hwnd
);
524 #endif // wxHAS_MSW_TASKDIALOG
527 WXWidget
wxProgressDialog::GetHandle() const
529 #ifdef wxHAS_MSW_TASKDIALOG
530 if ( HasNativeTaskDialog() )
534 wxCriticalSectionLocker
locker(m_sharedData
->m_cs
);
535 m_sharedData
->m_state
= m_state
;
536 hwnd
= m_sharedData
->m_hwnd
;
541 return wxGenericProgressDialog::GetHandle();
544 int wxProgressDialog::GetValue() const
546 #ifdef wxHAS_MSW_TASKDIALOG
547 if ( HasNativeTaskDialog() )
549 wxCriticalSectionLocker
locker(m_sharedData
->m_cs
);
550 return m_sharedData
->m_value
;
552 #endif // wxHAS_MSW_TASKDIALOG
554 return wxGenericProgressDialog::GetValue();
557 wxString
wxProgressDialog::GetMessage() const
559 #ifdef wxHAS_MSW_TASKDIALOG
560 if ( HasNativeTaskDialog() )
562 #endif // wxHAS_MSW_TASKDIALOG
564 return wxGenericProgressDialog::GetMessage();
567 void wxProgressDialog::SetRange(int maximum
)
569 #ifdef wxHAS_MSW_TASKDIALOG
570 if ( HasNativeTaskDialog() )
574 wxCriticalSectionLocker
locker(m_sharedData
->m_cs
);
576 m_sharedData
->m_range
= maximum
;
577 m_sharedData
->m_notifications
|= wxSPDD_RANGE_CHANGED
;
581 #endif // wxHAS_MSW_TASKDIALOG
583 wxGenericProgressDialog::SetRange( maximum
);
586 bool wxProgressDialog::WasSkipped() const
588 #ifdef wxHAS_MSW_TASKDIALOG
589 if ( HasNativeTaskDialog() )
593 // Couldn't be skipped before being shown.
597 wxCriticalSectionLocker
locker(m_sharedData
->m_cs
);
598 return m_sharedData
->m_skipped
;
600 #endif // wxHAS_MSW_TASKDIALOG
602 return wxGenericProgressDialog::WasSkipped();
605 bool wxProgressDialog::WasCancelled() const
607 #ifdef wxHAS_MSW_TASKDIALOG
608 if ( HasNativeTaskDialog() )
610 wxCriticalSectionLocker
locker(m_sharedData
->m_cs
);
611 return m_sharedData
->m_state
== Canceled
;
613 #endif // wxHAS_MSW_TASKDIALOG
615 return wxGenericProgressDialog::WasCancelled();
618 void wxProgressDialog::SetTitle(const wxString
& title
)
620 #ifdef wxHAS_MSW_TASKDIALOG
621 if ( HasNativeTaskDialog() )
627 wxCriticalSectionLocker
locker(m_sharedData
->m_cs
);
628 m_sharedData
->m_title
= title
;
629 m_sharedData
->m_notifications
= wxSPDD_TITLE_CHANGED
;
632 #endif // wxHAS_MSW_TASKDIALOG
634 wxGenericProgressDialog::SetTitle(title
);
637 wxString
wxProgressDialog::GetTitle() const
639 #ifdef wxHAS_MSW_TASKDIALOG
640 if ( HasNativeTaskDialog() )
642 #endif // wxHAS_MSW_TASKDIALOG
644 return wxGenericProgressDialog::GetTitle();
647 bool wxProgressDialog::Show(bool show
)
649 #ifdef wxHAS_MSW_TASKDIALOG
650 if ( HasNativeTaskDialog() )
652 // The dialog can't be hidden at all and showing it again after it had
653 // been shown before doesn't do anything.
654 if ( !show
|| m_taskDialogRunner
)
657 // We're showing the dialog for the first time, create the thread that
659 m_taskDialogRunner
= new wxProgressDialogTaskRunner
;
660 m_sharedData
= m_taskDialogRunner
->GetSharedDataObject();
662 // Initialize shared data.
663 m_sharedData
->m_title
= m_title
;
664 m_sharedData
->m_message
= m_message
;
665 m_sharedData
->m_range
= m_maximum
;
666 m_sharedData
->m_state
= Uncancelable
;
667 m_sharedData
->m_style
= GetPDStyle();
668 m_sharedData
->m_parent
= GetTopParent();
670 if ( HasPDFlag(wxPD_CAN_ABORT
) )
672 m_sharedData
->m_state
= Continue
;
673 m_sharedData
->m_labelCancel
= _("Cancel");
675 else // Dialog can't be cancelled.
677 // We still must have at least a single button in the dialog so
678 // just don't call it "Cancel" in this case.
679 m_sharedData
->m_labelCancel
= _("Close");
682 if ( HasPDFlag(wxPD_ELAPSED_TIME
|
683 wxPD_ESTIMATED_TIME
|
684 wxPD_REMAINING_TIME
) )
686 // Use a non-empty string just to have the collapsible pane shown.
687 m_sharedData
->m_expandedInformation
= " ";
690 // Do launch the thread.
691 if ( m_taskDialogRunner
->Create() != wxTHREAD_NO_ERROR
)
693 wxLogError( "Unable to create thread!" );
697 if ( m_taskDialogRunner
->Run() != wxTHREAD_NO_ERROR
)
699 wxLogError( "Unable to start thread!" );
703 // Do not show the underlying dialog.
706 #endif // wxHAS_MSW_TASKDIALOG
708 return wxGenericProgressDialog::Show( show
);
711 void wxProgressDialog::UpdateExpandedInformation(int value
)
713 #ifdef wxHAS_MSW_TASKDIALOG
714 unsigned long elapsedTime
;
715 unsigned long estimatedTime
;
716 unsigned long remainingTime
;
717 UpdateTimeEstimates(value
, elapsedTime
, estimatedTime
, remainingTime
);
719 int realEstimatedTime
= estimatedTime
,
720 realRemainingTime
= remainingTime
;
721 if ( m_sharedData
->m_progressBarMarquee
)
723 // In indeterminate mode we don't have any estimation neither for the
724 // remaining nor for estimated time.
726 realRemainingTime
= -1;
729 wxString expandedInformation
;
731 // Calculate the three different timing values.
732 if ( HasPDFlag(wxPD_ELAPSED_TIME
) )
734 expandedInformation
<< GetElapsedLabel()
736 << GetFormattedTime(elapsedTime
);
739 if ( HasPDFlag(wxPD_ESTIMATED_TIME
) )
741 if ( !expandedInformation
.empty() )
742 expandedInformation
+= "\n";
744 expandedInformation
<< GetEstimatedLabel()
746 << GetFormattedTime(realEstimatedTime
);
749 if ( HasPDFlag(wxPD_REMAINING_TIME
) )
751 if ( !expandedInformation
.empty() )
752 expandedInformation
+= "\n";
754 expandedInformation
<< GetRemainingLabel()
756 << GetFormattedTime(realRemainingTime
);
759 // Update with new timing information.
760 if ( expandedInformation
!= m_sharedData
->m_expandedInformation
)
762 m_sharedData
->m_expandedInformation
= expandedInformation
;
763 m_sharedData
->m_notifications
|= wxSPDD_EXPINFO_CHANGED
;
765 #else // !wxHAS_MSW_TASKDIALOG
767 #endif // wxHAS_MSW_TASKDIALOG/!wxHAS_MSW_TASKDIALOG
770 // ----------------------------------------------------------------------------
771 // wxProgressDialogTaskRunner and related methods
772 // ----------------------------------------------------------------------------
774 #ifdef wxHAS_MSW_TASKDIALOG
776 void* wxProgressDialogTaskRunner::Entry()
778 WinStruct
<TASKDIALOGCONFIG
> tdc
;
779 wxMSWTaskDialogConfig wxTdc
;
782 wxCriticalSectionLocker
locker(m_sharedData
.m_cs
);
784 wxTdc
.caption
= m_sharedData
.m_title
.wx_str();
785 wxTdc
.message
= m_sharedData
.m_message
.wx_str();
787 // MSWCommonTaskDialogInit() will add an IDCANCEL button but we need to
788 // give it the correct label.
789 wxTdc
.btnOKLabel
= m_sharedData
.m_labelCancel
;
790 wxTdc
.useCustomLabels
= true;
792 wxTdc
.MSWCommonTaskDialogInit( tdc
);
793 tdc
.pfCallback
= TaskDialogCallbackProc
;
794 tdc
.lpCallbackData
= (LONG_PTR
) &m_sharedData
;
796 // Undo some of the effects of MSWCommonTaskDialogInit().
797 tdc
.dwFlags
&= ~TDF_EXPAND_FOOTER_AREA
; // Expand in content area.
798 tdc
.dwCommonButtons
= 0; // Don't use common buttons.
800 if ( m_sharedData
.m_style
& wxPD_CAN_SKIP
)
801 wxTdc
.AddTaskDialogButton( tdc
, Id_SkipBtn
, 0, _("Skip") );
803 tdc
.dwFlags
|= TDF_CALLBACK_TIMER
| TDF_SHOW_PROGRESS_BAR
;
805 if ( !m_sharedData
.m_expandedInformation
.empty() )
807 tdc
.pszExpandedInformation
=
808 m_sharedData
.m_expandedInformation
.t_str();
812 TaskDialogIndirect_t taskDialogIndirect
= GetTaskDialogIndirectFunc();
813 if ( !taskDialogIndirect
)
817 HRESULT hr
= taskDialogIndirect(&tdc
, &msAns
, NULL
, NULL
);
819 wxLogApiError( "TaskDialogIndirect", hr
);
821 // If the main thread is waiting for us to exit inside the event loop in
822 // Update(), wake it up so that it checks our status again.
830 wxProgressDialogTaskRunner::TaskDialogCallbackProc
835 LPARAM
WXUNUSED(lParam
),
839 wxProgressDialogSharedData
* const sharedData
=
840 (wxProgressDialogSharedData
*) dwRefData
;
842 wxCriticalSectionLocker
locker(sharedData
->m_cs
);
844 switch ( uNotification
)
847 // Store the HWND for the main thread use.
848 sharedData
->m_hwnd
= hwnd
;
850 // Set the maximum value and disable Close button.
852 TDM_SET_PROGRESS_BAR_RANGE
,
854 MAKELPARAM(0, sharedData
->m_range
) );
856 // We always create this task dialog with NULL parent because our
857 // parent in wx sense is a window created from a different thread
858 // and so can't be used as our real parent. However we still center
859 // this window on the parent one as the task dialogs do with their
860 // real parent usually.
861 if ( sharedData
->m_parent
)
863 wxRect
rect(wxRectFromRECT(wxGetWindowRect(hwnd
)));
864 rect
= rect
.CentreIn(sharedData
->m_parent
->GetRect());
877 // If we can't be aborted, the "Close" button will only be enabled
878 // when the progress ends (and not even then with wxPD_AUTO_HIDE).
879 if ( !(sharedData
->m_style
& wxPD_CAN_ABORT
) )
880 ::SendMessage( hwnd
, TDM_ENABLE_BUTTON
, IDCANCEL
, FALSE
);
883 case TDN_BUTTON_CLICKED
:
887 ::SendMessage(hwnd
, TDM_ENABLE_BUTTON
, Id_SkipBtn
, FALSE
);
888 sharedData
->m_skipped
= true;
892 if ( sharedData
->m_state
== wxProgressDialog::Finished
)
894 // If the main thread is waiting for us, tell it that
895 // we're gone (and if it doesn't wait, it's harmless).
896 sharedData
->m_state
= wxProgressDialog::Dismissed
;
898 // Let Windows close the dialog.
902 // Close button on the window triggers an IDCANCEL press,
903 // don't allow it when it should only be possible to close
904 // a finished dialog.
905 if ( sharedData
->m_style
& wxPD_CAN_ABORT
)
909 sharedData
->m_state
== wxProgressDialog::Continue
,
911 "Dialog not in a cancelable state!"
914 ::SendMessage(hwnd
, TDM_ENABLE_BUTTON
, Id_SkipBtn
, FALSE
);
915 ::SendMessage(hwnd
, TDM_ENABLE_BUTTON
, IDCANCEL
, FALSE
);
917 sharedData
->m_timeStop
= wxGetCurrentTime();
918 sharedData
->m_state
= wxProgressDialog::Canceled
;
926 PerformNotificationUpdates(hwnd
, sharedData
);
929 Decide whether we should end the dialog. This is done if either
930 the dialog object itself was destroyed or if the progress
931 finished and we were configured to hide automatically without
932 waiting for the user to dismiss us.
934 Notice that we do not close the dialog if it was cancelled
935 because it's up to the user code in the main thread to decide
936 whether it really wants to cancel the dialog.
938 if ( (sharedData
->m_notifications
& wxSPDD_DESTROYED
) ||
939 (sharedData
->m_state
== wxProgressDialog::Finished
&&
940 sharedData
->m_style
& wxPD_AUTO_HIDE
) )
942 ::EndDialog( hwnd
, IDCLOSE
);
945 sharedData
->m_notifications
= 0;
954 #endif // wxHAS_MSW_TASKDIALOG
956 #endif // wxUSE_PROGRESSDLG && wxUSE_THREADS