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(),
311 m_taskDialogRunner(NULL
),
316 #ifdef wxHAS_MSW_TASKDIALOG
317 if ( HasNativeTaskDialog() )
319 SetTopParent(parent
);
324 DisableOtherWindows();
328 #endif // wxHAS_MSW_TASKDIALOG
330 Create(title
, message
, maximum
, parent
, style
);
333 wxProgressDialog::~wxProgressDialog()
335 #ifdef wxHAS_MSW_TASKDIALOG
336 if ( !m_taskDialogRunner
)
341 wxCriticalSectionLocker
locker(m_sharedData
->m_cs
);
342 m_sharedData
->m_notifications
|= wxSPDD_DESTROYED
;
345 m_taskDialogRunner
->Wait();
347 delete m_taskDialogRunner
;
349 ReenableOtherWindows();
351 if ( GetTopParent() )
352 GetTopParent()->Raise();
353 #endif // wxHAS_MSW_TASKDIALOG
356 bool wxProgressDialog::Update(int value
, const wxString
& newmsg
, bool *skip
)
358 #ifdef wxHAS_MSW_TASKDIALOG
359 if ( HasNativeTaskDialog() )
362 wxCriticalSectionLocker
locker(m_sharedData
->m_cs
);
364 // Do nothing in canceled state.
365 if ( !DoNativeBeforeUpdate(skip
) )
370 wxASSERT_MSG( value
<= m_maximum
, wxT("invalid progress value") );
372 m_sharedData
->m_value
= value
;
373 m_sharedData
->m_notifications
|= wxSPDD_VALUE_CHANGED
;
375 if ( !newmsg
.empty() )
378 m_sharedData
->m_message
= newmsg
;
379 m_sharedData
->m_notifications
|= wxSPDD_MESSAGE_CHANGED
;
382 if ( m_sharedData
->m_progressBarMarquee
)
384 m_sharedData
->m_progressBarMarquee
= false;
385 m_sharedData
->m_notifications
|= wxSPDD_PBMARQUEE_CHANGED
;
388 UpdateExpandedInformation( value
);
390 // If we didn't just reach the finish, all we have to do is to
391 // return true if the dialog wasn't cancelled and false otherwise.
392 if ( value
!= m_maximum
|| m_state
== Finished
)
393 return m_sharedData
->m_state
!= Canceled
;
396 // On finishing, the dialog without wxPD_AUTO_HIDE style becomes a
397 // modal one meaning that we must block here until the user
400 m_sharedData
->m_state
= Finished
;
401 m_sharedData
->m_notifications
|= wxSPDD_FINISHED
;
402 if ( HasPDFlag(wxPD_AUTO_HIDE
) )
405 if ( newmsg
.empty() )
407 // Provide the finishing message if the application didn't.
408 m_message
= _("Done.");
409 m_sharedData
->m_message
= m_message
;
410 m_sharedData
->m_notifications
|= wxSPDD_MESSAGE_CHANGED
;
412 } // unlock m_sharedData->m_cs
414 // We only get here when we need to wait for the dialog to terminate so
415 // do just this by running a custom event loop until the dialog is
417 wxProgressDialogModalLoop
loop(*m_sharedData
);
421 #endif // wxHAS_MSW_TASKDIALOG
423 return wxGenericProgressDialog::Update( value
, newmsg
, skip
);
426 bool wxProgressDialog::Pulse(const wxString
& newmsg
, bool *skip
)
428 #ifdef wxHAS_MSW_TASKDIALOG
429 if ( HasNativeTaskDialog() )
431 wxCriticalSectionLocker
locker(m_sharedData
->m_cs
);
433 // Do nothing in canceled state.
434 if ( !DoNativeBeforeUpdate(skip
) )
437 if ( !m_sharedData
->m_progressBarMarquee
)
439 m_sharedData
->m_progressBarMarquee
= true;
440 m_sharedData
->m_notifications
|= wxSPDD_PBMARQUEE_CHANGED
;
443 if ( !newmsg
.empty() )
446 m_sharedData
->m_message
= newmsg
;
447 m_sharedData
->m_notifications
|= wxSPDD_MESSAGE_CHANGED
;
450 // The value passed here doesn't matter, only elapsed time makes sense
451 // in indeterminate mode anyhow.
452 UpdateExpandedInformation(0);
454 return m_sharedData
->m_state
!= Canceled
;
456 #endif // wxHAS_MSW_TASKDIALOG
458 return wxGenericProgressDialog::Pulse( newmsg
, skip
);
461 bool wxProgressDialog::DoNativeBeforeUpdate(bool *skip
)
463 #ifdef wxHAS_MSW_TASKDIALOG
464 if ( HasNativeTaskDialog() )
466 if ( m_sharedData
->m_skipped
)
468 if ( skip
&& !*skip
)
471 m_sharedData
->m_skipped
= false;
472 m_sharedData
->m_notifications
|= wxSPDD_ENABLE_SKIP
;
476 if ( m_sharedData
->m_state
== Canceled
)
477 m_timeStop
= m_sharedData
->m_timeStop
;
479 return m_sharedData
->m_state
!= Canceled
;
481 #endif // wxHAS_MSW_TASKDIALOG
484 wxFAIL_MSG( "unreachable" );
489 void wxProgressDialog::Resume()
491 wxGenericProgressDialog::Resume();
493 #ifdef wxHAS_MSW_TASKDIALOG
494 if ( HasNativeTaskDialog() )
499 wxCriticalSectionLocker
locker(m_sharedData
->m_cs
);
500 m_sharedData
->m_state
= m_state
;
502 // "Skip" was disabled when "Cancel" had been clicked, so re-enable
504 m_sharedData
->m_notifications
|= wxSPDD_ENABLE_SKIP
;
506 // Also re-enable "Cancel" itself
507 if ( HasPDFlag(wxPD_CAN_ABORT
) )
508 m_sharedData
->m_notifications
|= wxSPDD_ENABLE_ABORT
;
510 hwnd
= m_sharedData
->m_hwnd
;
511 } // Unlock m_cs, we can't call any function operating on a dialog with
512 // it locked as it can result in a deadlock if the dialog callback is
513 // called by Windows.
515 // After resuming we need to bring the window on top of the Z-order as
516 // it could be hidden by another window shown from the main thread,
517 // e.g. a confirmation dialog asking whether the user really wants to
520 // Notice that this must be done from the main thread as it owns the
521 // currently active window and attempts to do this from the task dialog
522 // thread would simply fail.
523 ::BringWindowToTop(hwnd
);
525 #endif // wxHAS_MSW_TASKDIALOG
528 WXWidget
wxProgressDialog::GetHandle() const
530 #ifdef wxHAS_MSW_TASKDIALOG
531 if ( HasNativeTaskDialog() )
535 wxCriticalSectionLocker
locker(m_sharedData
->m_cs
);
536 m_sharedData
->m_state
= m_state
;
537 hwnd
= m_sharedData
->m_hwnd
;
542 return wxGenericProgressDialog::GetHandle();
545 int wxProgressDialog::GetValue() const
547 #ifdef wxHAS_MSW_TASKDIALOG
548 if ( HasNativeTaskDialog() )
550 wxCriticalSectionLocker
locker(m_sharedData
->m_cs
);
551 return m_sharedData
->m_value
;
553 #endif // wxHAS_MSW_TASKDIALOG
555 return wxGenericProgressDialog::GetValue();
558 wxString
wxProgressDialog::GetMessage() const
560 #ifdef wxHAS_MSW_TASKDIALOG
561 if ( HasNativeTaskDialog() )
563 #endif // wxHAS_MSW_TASKDIALOG
565 return wxGenericProgressDialog::GetMessage();
568 void wxProgressDialog::SetRange(int maximum
)
570 #ifdef wxHAS_MSW_TASKDIALOG
571 if ( HasNativeTaskDialog() )
575 wxCriticalSectionLocker
locker(m_sharedData
->m_cs
);
577 m_sharedData
->m_range
= maximum
;
578 m_sharedData
->m_notifications
|= wxSPDD_RANGE_CHANGED
;
582 #endif // wxHAS_MSW_TASKDIALOG
584 wxGenericProgressDialog::SetRange( maximum
);
587 bool wxProgressDialog::WasSkipped() const
589 #ifdef wxHAS_MSW_TASKDIALOG
590 if ( HasNativeTaskDialog() )
594 // Couldn't be skipped before being shown.
598 wxCriticalSectionLocker
locker(m_sharedData
->m_cs
);
599 return m_sharedData
->m_skipped
;
601 #endif // wxHAS_MSW_TASKDIALOG
603 return wxGenericProgressDialog::WasSkipped();
606 bool wxProgressDialog::WasCancelled() const
608 #ifdef wxHAS_MSW_TASKDIALOG
609 if ( HasNativeTaskDialog() )
611 wxCriticalSectionLocker
locker(m_sharedData
->m_cs
);
612 return m_sharedData
->m_state
== Canceled
;
614 #endif // wxHAS_MSW_TASKDIALOG
616 return wxGenericProgressDialog::WasCancelled();
619 void wxProgressDialog::SetTitle(const wxString
& title
)
621 #ifdef wxHAS_MSW_TASKDIALOG
622 if ( HasNativeTaskDialog() )
628 wxCriticalSectionLocker
locker(m_sharedData
->m_cs
);
629 m_sharedData
->m_title
= title
;
630 m_sharedData
->m_notifications
= wxSPDD_TITLE_CHANGED
;
633 #endif // wxHAS_MSW_TASKDIALOG
635 wxGenericProgressDialog::SetTitle(title
);
638 wxString
wxProgressDialog::GetTitle() const
640 #ifdef wxHAS_MSW_TASKDIALOG
641 if ( HasNativeTaskDialog() )
643 #endif // wxHAS_MSW_TASKDIALOG
645 return wxGenericProgressDialog::GetTitle();
648 bool wxProgressDialog::Show(bool show
)
650 #ifdef wxHAS_MSW_TASKDIALOG
651 if ( HasNativeTaskDialog() )
653 // The dialog can't be hidden at all and showing it again after it had
654 // been shown before doesn't do anything.
655 if ( !show
|| m_taskDialogRunner
)
658 // We're showing the dialog for the first time, create the thread that
660 m_taskDialogRunner
= new wxProgressDialogTaskRunner
;
661 m_sharedData
= m_taskDialogRunner
->GetSharedDataObject();
663 // Initialize shared data.
664 m_sharedData
->m_title
= m_title
;
665 m_sharedData
->m_message
= m_message
;
666 m_sharedData
->m_range
= m_maximum
;
667 m_sharedData
->m_state
= Uncancelable
;
668 m_sharedData
->m_style
= GetPDStyle();
669 m_sharedData
->m_parent
= GetTopParent();
671 if ( HasPDFlag(wxPD_CAN_ABORT
) )
673 m_sharedData
->m_state
= Continue
;
674 m_sharedData
->m_labelCancel
= _("Cancel");
676 else // Dialog can't be cancelled.
678 // We still must have at least a single button in the dialog so
679 // just don't call it "Cancel" in this case.
680 m_sharedData
->m_labelCancel
= _("Close");
683 if ( HasPDFlag(wxPD_ELAPSED_TIME
|
684 wxPD_ESTIMATED_TIME
|
685 wxPD_REMAINING_TIME
) )
687 // Use a non-empty string just to have the collapsible pane shown.
688 m_sharedData
->m_expandedInformation
= " ";
691 // Do launch the thread.
692 if ( m_taskDialogRunner
->Create() != wxTHREAD_NO_ERROR
)
694 wxLogError( "Unable to create thread!" );
698 if ( m_taskDialogRunner
->Run() != wxTHREAD_NO_ERROR
)
700 wxLogError( "Unable to start thread!" );
704 // Do not show the underlying dialog.
707 #endif // wxHAS_MSW_TASKDIALOG
709 return wxGenericProgressDialog::Show( show
);
712 void wxProgressDialog::UpdateExpandedInformation(int value
)
714 #ifdef wxHAS_MSW_TASKDIALOG
715 unsigned long elapsedTime
;
716 unsigned long estimatedTime
;
717 unsigned long remainingTime
;
718 UpdateTimeEstimates(value
, elapsedTime
, estimatedTime
, remainingTime
);
720 int realEstimatedTime
= estimatedTime
,
721 realRemainingTime
= remainingTime
;
722 if ( m_sharedData
->m_progressBarMarquee
)
724 // In indeterminate mode we don't have any estimation neither for the
725 // remaining nor for estimated time.
727 realRemainingTime
= -1;
730 wxString expandedInformation
;
732 // Calculate the three different timing values.
733 if ( HasPDFlag(wxPD_ELAPSED_TIME
) )
735 expandedInformation
<< GetElapsedLabel()
737 << GetFormattedTime(elapsedTime
);
740 if ( HasPDFlag(wxPD_ESTIMATED_TIME
) )
742 if ( !expandedInformation
.empty() )
743 expandedInformation
+= "\n";
745 expandedInformation
<< GetEstimatedLabel()
747 << GetFormattedTime(realEstimatedTime
);
750 if ( HasPDFlag(wxPD_REMAINING_TIME
) )
752 if ( !expandedInformation
.empty() )
753 expandedInformation
+= "\n";
755 expandedInformation
<< GetRemainingLabel()
757 << GetFormattedTime(realRemainingTime
);
760 // Update with new timing information.
761 if ( expandedInformation
!= m_sharedData
->m_expandedInformation
)
763 m_sharedData
->m_expandedInformation
= expandedInformation
;
764 m_sharedData
->m_notifications
|= wxSPDD_EXPINFO_CHANGED
;
766 #else // !wxHAS_MSW_TASKDIALOG
768 #endif // wxHAS_MSW_TASKDIALOG/!wxHAS_MSW_TASKDIALOG
771 // ----------------------------------------------------------------------------
772 // wxProgressDialogTaskRunner and related methods
773 // ----------------------------------------------------------------------------
775 #ifdef wxHAS_MSW_TASKDIALOG
777 void* wxProgressDialogTaskRunner::Entry()
779 WinStruct
<TASKDIALOGCONFIG
> tdc
;
780 wxMSWTaskDialogConfig wxTdc
;
783 wxCriticalSectionLocker
locker(m_sharedData
.m_cs
);
785 wxTdc
.caption
= m_sharedData
.m_title
.wx_str();
786 wxTdc
.message
= m_sharedData
.m_message
.wx_str();
788 // MSWCommonTaskDialogInit() will add an IDCANCEL button but we need to
789 // give it the correct label.
790 wxTdc
.btnOKLabel
= m_sharedData
.m_labelCancel
;
791 wxTdc
.useCustomLabels
= true;
793 wxTdc
.MSWCommonTaskDialogInit( tdc
);
794 tdc
.pfCallback
= TaskDialogCallbackProc
;
795 tdc
.lpCallbackData
= (LONG_PTR
) &m_sharedData
;
797 // Undo some of the effects of MSWCommonTaskDialogInit().
798 tdc
.dwFlags
&= ~TDF_EXPAND_FOOTER_AREA
; // Expand in content area.
799 tdc
.dwCommonButtons
= 0; // Don't use common buttons.
801 if ( m_sharedData
.m_style
& wxPD_CAN_SKIP
)
802 wxTdc
.AddTaskDialogButton( tdc
, Id_SkipBtn
, 0, _("Skip") );
804 tdc
.dwFlags
|= TDF_CALLBACK_TIMER
| TDF_SHOW_PROGRESS_BAR
;
806 if ( !m_sharedData
.m_expandedInformation
.empty() )
808 tdc
.pszExpandedInformation
=
809 m_sharedData
.m_expandedInformation
.wx_str();
813 TaskDialogIndirect_t taskDialogIndirect
= GetTaskDialogIndirectFunc();
814 if ( !taskDialogIndirect
)
818 HRESULT hr
= taskDialogIndirect(&tdc
, &msAns
, NULL
, NULL
);
820 wxLogApiError( "TaskDialogIndirect", hr
);
822 // If the main thread is waiting for us to exit inside the event loop in
823 // Update(), wake it up so that it checks our status again.
831 wxProgressDialogTaskRunner::TaskDialogCallbackProc
836 LPARAM
WXUNUSED(lParam
),
840 wxProgressDialogSharedData
* const sharedData
=
841 (wxProgressDialogSharedData
*) dwRefData
;
843 wxCriticalSectionLocker
locker(sharedData
->m_cs
);
845 switch ( uNotification
)
848 // Store the HWND for the main thread use.
849 sharedData
->m_hwnd
= hwnd
;
851 // Set the maximum value and disable Close button.
853 TDM_SET_PROGRESS_BAR_RANGE
,
855 MAKELPARAM(0, sharedData
->m_range
) );
857 // We always create this task dialog with NULL parent because our
858 // parent in wx sense is a window created from a different thread
859 // and so can't be used as our real parent. However we still center
860 // this window on the parent one as the task dialogs do with their
861 // real parent usually.
862 if ( sharedData
->m_parent
)
864 wxRect
rect(wxRectFromRECT(wxGetWindowRect(hwnd
)));
865 rect
= rect
.CentreIn(sharedData
->m_parent
->GetRect());
878 // If we can't be aborted, the "Close" button will only be enabled
879 // when the progress ends (and not even then with wxPD_AUTO_HIDE).
880 if ( !(sharedData
->m_style
& wxPD_CAN_ABORT
) )
881 ::SendMessage( hwnd
, TDM_ENABLE_BUTTON
, IDCANCEL
, FALSE
);
884 case TDN_BUTTON_CLICKED
:
888 ::SendMessage(hwnd
, TDM_ENABLE_BUTTON
, Id_SkipBtn
, FALSE
);
889 sharedData
->m_skipped
= true;
893 if ( sharedData
->m_state
== wxProgressDialog::Finished
)
895 // If the main thread is waiting for us, tell it that
896 // we're gone (and if it doesn't wait, it's harmless).
897 sharedData
->m_state
= wxProgressDialog::Dismissed
;
899 // Let Windows close the dialog.
903 // Close button on the window triggers an IDCANCEL press,
904 // don't allow it when it should only be possible to close
905 // a finished dialog.
906 if ( sharedData
->m_style
& wxPD_CAN_ABORT
)
910 sharedData
->m_state
== wxProgressDialog::Continue
,
912 "Dialog not in a cancelable state!"
915 ::SendMessage(hwnd
, TDM_ENABLE_BUTTON
, Id_SkipBtn
, FALSE
);
916 ::SendMessage(hwnd
, TDM_ENABLE_BUTTON
, IDCANCEL
, FALSE
);
918 sharedData
->m_timeStop
= wxGetCurrentTime();
919 sharedData
->m_state
= wxProgressDialog::Canceled
;
927 PerformNotificationUpdates(hwnd
, sharedData
);
930 Decide whether we should end the dialog. This is done if either
931 the dialog object itself was destroyed or if the progress
932 finished and we were configured to hide automatically without
933 waiting for the user to dismiss us.
935 Notice that we do not close the dialog if it was cancelled
936 because it's up to the user code in the main thread to decide
937 whether it really wants to cancel the dialog.
939 if ( (sharedData
->m_notifications
& wxSPDD_DESTROYED
) ||
940 (sharedData
->m_state
== wxProgressDialog::Finished
&&
941 sharedData
->m_style
& wxPD_AUTO_HIDE
) )
943 ::EndDialog( hwnd
, IDCLOSE
);
946 sharedData
->m_notifications
= 0;
955 #endif // wxHAS_MSW_TASKDIALOG
957 #endif // wxUSE_PROGRESSDLG && wxUSE_THREADS