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;
79 wxCriticalSection m_cs
;
81 wxWindow
*m_parent
; // Parent window only used to center us over it.
82 HWND m_hwnd
; // Task dialog handler
83 long m_style
; // wxProgressDialog style
88 wxString m_expandedInformation
;
89 wxString m_labelCancel
; // Privately used by callback.
90 unsigned long m_timeStop
;
92 wxProgressDialog::State m_state
;
93 bool m_progressBarMarquee
;
96 // Bit field that indicates fields that have been modified by the
97 // main thread so the task dialog runner knows what to update.
101 // Runner thread that takes care of displaying and updating the
103 class wxProgressDialogTaskRunner
: public wxThread
106 wxProgressDialogTaskRunner()
107 : wxThread(wxTHREAD_JOINABLE
)
110 wxProgressDialogSharedData
* GetSharedDataObject()
111 { return &m_sharedData
; }
114 wxProgressDialogSharedData m_sharedData
;
116 virtual void* Entry();
118 static HRESULT CALLBACK
TaskDialogCallbackProc(HWND hwnd
,
128 // A custom event loop which runs until the state of the dialog becomes
130 class wxProgressDialogModalLoop
: public wxEventLoop
133 wxProgressDialogModalLoop(wxProgressDialogSharedData
& data
)
139 virtual void OnNextIteration()
141 wxCriticalSectionLocker
locker(m_data
.m_cs
);
143 if ( m_data
.m_state
== wxProgressDialog::Dismissed
)
147 wxProgressDialogSharedData
& m_data
;
149 wxDECLARE_NO_COPY_CLASS(wxProgressDialogModalLoop
);
152 // ============================================================================
154 // ============================================================================
156 BOOL CALLBACK
DisplayCloseButton(HWND hwnd
, LPARAM lParam
)
158 wxProgressDialogSharedData
*sharedData
=
159 (wxProgressDialogSharedData
*) lParam
;
161 if ( wxGetWindowText( hwnd
) == sharedData
->m_labelCancel
)
163 sharedData
->m_labelCancel
= _("Close");
164 SendMessage( hwnd
, WM_SETTEXT
, 0,
165 (LPARAM
) sharedData
->m_labelCancel
.wx_str() );
173 void PerformNotificationUpdates(HWND hwnd
,
174 wxProgressDialogSharedData
*sharedData
)
176 // Update the appropriate dialog fields.
177 if ( sharedData
->m_notifications
& wxSPDD_RANGE_CHANGED
)
180 TDM_SET_PROGRESS_BAR_RANGE
,
182 MAKELPARAM(0, sharedData
->m_range
) );
185 if ( sharedData
->m_notifications
& wxSPDD_VALUE_CHANGED
)
188 TDM_SET_PROGRESS_BAR_POS
,
193 if ( sharedData
->m_notifications
& wxSPDD_PBMARQUEE_CHANGED
)
195 BOOL val
= sharedData
->m_progressBarMarquee
? TRUE
: FALSE
;
197 TDM_SET_MARQUEE_PROGRESS_BAR
,
201 TDM_SET_PROGRESS_BAR_MARQUEE
,
206 if ( sharedData
->m_notifications
& wxSPDD_TITLE_CHANGED
)
207 ::SetWindowText( hwnd
, sharedData
->m_title
.wx_str() );
209 if ( sharedData
->m_notifications
& wxSPDD_MESSAGE_CHANGED
)
211 // Split the message in the title string and the rest if it has
214 title
= sharedData
->m_message
,
217 const size_t posNL
= title
.find('\n');
218 if ( posNL
!= wxString::npos
)
220 // There can an extra new line between the first and subsequent
221 // lines to separate them as it looks better with the generic
222 // version -- but in this one, they're already separated by the use
223 // of different dialog elements, so suppress the extra new line.
225 if ( posNL
< title
.length() - 1 && title
[posNL
+ 1] == '\n' )
228 body
.assign(title
, posNL
+ numNLs
, wxString::npos
);
233 TDM_SET_ELEMENT_TEXT
,
234 TDE_MAIN_INSTRUCTION
,
235 (LPARAM
) title
.wx_str() );
238 TDM_SET_ELEMENT_TEXT
,
240 (LPARAM
) body
.wx_str() );
243 if ( sharedData
->m_notifications
& wxSPDD_EXPINFO_CHANGED
)
245 const wxString
& expandedInformation
=
246 sharedData
->m_expandedInformation
;
247 if ( !expandedInformation
.empty() )
250 TDM_SET_ELEMENT_TEXT
,
251 TDE_EXPANDED_INFORMATION
,
252 (LPARAM
) expandedInformation
.wx_str() );
256 if ( sharedData
->m_notifications
& wxSPDD_ENABLE_SKIP
)
257 ::SendMessage( hwnd
, TDM_ENABLE_BUTTON
, Id_SkipBtn
, TRUE
);
259 if ( sharedData
->m_notifications
& wxSPDD_ENABLE_ABORT
)
260 ::SendMessage( hwnd
, TDM_ENABLE_BUTTON
, IDCANCEL
, TRUE
);
262 if ( sharedData
->m_notifications
& wxSPDD_DISABLE_SKIP
)
263 ::SendMessage( hwnd
, TDM_ENABLE_BUTTON
, Id_SkipBtn
, FALSE
);
265 if ( sharedData
->m_notifications
& wxSPDD_DISABLE_ABORT
)
266 ::SendMessage( hwnd
, TDM_ENABLE_BUTTON
, IDCANCEL
, FALSE
);
268 // Is the progress finished?
269 if ( sharedData
->m_notifications
& wxSPDD_FINISHED
)
271 sharedData
->m_state
= wxProgressDialog::Finished
;
273 if ( !(sharedData
->m_style
& wxPD_AUTO_HIDE
) )
275 // Change Cancel into Close and activate the button.
276 ::SendMessage( hwnd
, TDM_ENABLE_BUTTON
, Id_SkipBtn
, FALSE
);
277 ::SendMessage( hwnd
, TDM_ENABLE_BUTTON
, IDCANCEL
, TRUE
);
278 ::EnumChildWindows( hwnd
, DisplayCloseButton
,
279 (LPARAM
) sharedData
);
284 } // anonymous namespace
286 #endif // wxHAS_MSW_TASKDIALOG
288 // ============================================================================
289 // wxProgressDialog implementation
290 // ============================================================================
292 wxProgressDialog::wxProgressDialog( const wxString
& title
,
293 const wxString
& message
,
297 : wxGenericProgressDialog(parent
, style
),
298 m_taskDialogRunner(NULL
),
303 #ifdef wxHAS_MSW_TASKDIALOG
304 if ( HasNativeTaskDialog() )
309 DisableOtherWindows();
313 #endif // wxHAS_MSW_TASKDIALOG
315 Create(title
, message
, maximum
, parent
, style
);
318 wxProgressDialog::~wxProgressDialog()
320 #ifdef wxHAS_MSW_TASKDIALOG
321 if ( !m_taskDialogRunner
)
326 wxCriticalSectionLocker
locker(m_sharedData
->m_cs
);
327 m_sharedData
->m_notifications
|= wxSPDD_DESTROYED
;
330 m_taskDialogRunner
->Wait();
332 delete m_taskDialogRunner
;
334 ReenableOtherWindows();
336 if ( GetTopParent() )
337 GetTopParent()->Raise();
338 #endif // wxHAS_MSW_TASKDIALOG
341 bool wxProgressDialog::Update(int value
, const wxString
& newmsg
, bool *skip
)
343 #ifdef wxHAS_MSW_TASKDIALOG
344 if ( HasNativeTaskDialog() )
347 wxCriticalSectionLocker
locker(m_sharedData
->m_cs
);
349 // Do nothing in canceled state.
350 if ( !DoNativeBeforeUpdate(skip
) )
355 wxASSERT_MSG( value
<= m_maximum
, wxT("invalid progress value") );
357 m_sharedData
->m_value
= value
;
358 m_sharedData
->m_notifications
|= wxSPDD_VALUE_CHANGED
;
360 if ( !newmsg
.empty() )
363 m_sharedData
->m_message
= newmsg
;
364 m_sharedData
->m_notifications
|= wxSPDD_MESSAGE_CHANGED
;
367 if ( m_sharedData
->m_progressBarMarquee
)
369 m_sharedData
->m_progressBarMarquee
= false;
370 m_sharedData
->m_notifications
|= wxSPDD_PBMARQUEE_CHANGED
;
373 UpdateExpandedInformation( value
);
375 // If we didn't just reach the finish, all we have to do is to
376 // return true if the dialog wasn't cancelled and false otherwise.
377 if ( value
!= m_maximum
|| m_state
== Finished
)
378 return m_sharedData
->m_state
!= Canceled
;
381 // On finishing, the dialog without wxPD_AUTO_HIDE style becomes a
382 // modal one meaning that we must block here until the user
385 m_sharedData
->m_state
= Finished
;
386 m_sharedData
->m_notifications
|= wxSPDD_FINISHED
;
387 if ( HasPDFlag(wxPD_AUTO_HIDE
) )
390 if ( newmsg
.empty() )
392 // Provide the finishing message if the application didn't.
393 m_message
= _("Done.");
394 m_sharedData
->m_message
= m_message
;
395 m_sharedData
->m_notifications
|= wxSPDD_MESSAGE_CHANGED
;
397 } // unlock m_sharedData->m_cs
399 // We only get here when we need to wait for the dialog to terminate so
400 // do just this by running a custom event loop until the dialog is
402 wxProgressDialogModalLoop
loop(*m_sharedData
);
406 #endif // wxHAS_MSW_TASKDIALOG
408 return wxGenericProgressDialog::Update( value
, newmsg
, skip
);
411 bool wxProgressDialog::Pulse(const wxString
& newmsg
, bool *skip
)
413 #ifdef wxHAS_MSW_TASKDIALOG
414 if ( HasNativeTaskDialog() )
416 wxCriticalSectionLocker
locker(m_sharedData
->m_cs
);
418 // Do nothing in canceled state.
419 if ( !DoNativeBeforeUpdate(skip
) )
422 if ( !m_sharedData
->m_progressBarMarquee
)
424 m_sharedData
->m_progressBarMarquee
= true;
425 m_sharedData
->m_notifications
|= wxSPDD_PBMARQUEE_CHANGED
;
428 if ( !newmsg
.empty() )
431 m_sharedData
->m_message
= newmsg
;
432 m_sharedData
->m_notifications
|= wxSPDD_MESSAGE_CHANGED
;
435 // The value passed here doesn't matter, only elapsed time makes sense
436 // in indeterminate mode anyhow.
437 UpdateExpandedInformation(0);
439 return m_sharedData
->m_state
!= Canceled
;
441 #endif // wxHAS_MSW_TASKDIALOG
443 return wxGenericProgressDialog::Pulse( newmsg
, skip
);
446 bool wxProgressDialog::DoNativeBeforeUpdate(bool *skip
)
448 #ifdef wxHAS_MSW_TASKDIALOG
449 if ( HasNativeTaskDialog() )
451 if ( m_sharedData
->m_skipped
)
453 if ( skip
&& !*skip
)
456 m_sharedData
->m_skipped
= false;
457 m_sharedData
->m_notifications
|= wxSPDD_ENABLE_SKIP
;
461 if ( m_sharedData
->m_state
== Canceled
)
462 m_timeStop
= m_sharedData
->m_timeStop
;
464 return m_sharedData
->m_state
!= Canceled
;
466 #endif // wxHAS_MSW_TASKDIALOG
469 wxFAIL_MSG( "unreachable" );
474 void wxProgressDialog::Resume()
476 wxGenericProgressDialog::Resume();
478 #ifdef wxHAS_MSW_TASKDIALOG
479 if ( HasNativeTaskDialog() )
484 wxCriticalSectionLocker
locker(m_sharedData
->m_cs
);
485 m_sharedData
->m_state
= m_state
;
487 // "Skip" was disabled when "Cancel" had been clicked, so re-enable
489 m_sharedData
->m_notifications
|= wxSPDD_ENABLE_SKIP
;
491 // Also re-enable "Cancel" itself
492 if ( HasPDFlag(wxPD_CAN_ABORT
) )
493 m_sharedData
->m_notifications
|= wxSPDD_ENABLE_ABORT
;
495 hwnd
= m_sharedData
->m_hwnd
;
496 } // Unlock m_cs, we can't call any function operating on a dialog with
497 // it locked as it can result in a deadlock if the dialog callback is
498 // called by Windows.
500 // After resuming we need to bring the window on top of the Z-order as
501 // it could be hidden by another window shown from the main thread,
502 // e.g. a confirmation dialog asking whether the user really wants to
505 // Notice that this must be done from the main thread as it owns the
506 // currently active window and attempts to do this from the task dialog
507 // thread would simply fail.
508 ::BringWindowToTop(hwnd
);
510 #endif // wxHAS_MSW_TASKDIALOG
513 int wxProgressDialog::GetValue() const
515 #ifdef wxHAS_MSW_TASKDIALOG
516 if ( HasNativeTaskDialog() )
518 wxCriticalSectionLocker
locker(m_sharedData
->m_cs
);
519 return m_sharedData
->m_value
;
521 #endif // wxHAS_MSW_TASKDIALOG
523 return wxGenericProgressDialog::GetValue();
526 wxString
wxProgressDialog::GetMessage() const
528 #ifdef wxHAS_MSW_TASKDIALOG
529 if ( HasNativeTaskDialog() )
531 #endif // wxHAS_MSW_TASKDIALOG
533 return wxGenericProgressDialog::GetMessage();
536 void wxProgressDialog::SetRange(int maximum
)
538 #ifdef wxHAS_MSW_TASKDIALOG
539 if ( HasNativeTaskDialog() )
543 wxCriticalSectionLocker
locker(m_sharedData
->m_cs
);
545 m_sharedData
->m_range
= maximum
;
546 m_sharedData
->m_notifications
|= wxSPDD_RANGE_CHANGED
;
550 #endif // wxHAS_MSW_TASKDIALOG
552 wxGenericProgressDialog::SetRange( maximum
);
555 bool wxProgressDialog::WasSkipped() const
557 #ifdef wxHAS_MSW_TASKDIALOG
558 if ( HasNativeTaskDialog() )
562 // Couldn't be skipped before being shown.
566 wxCriticalSectionLocker
locker(m_sharedData
->m_cs
);
567 return m_sharedData
->m_skipped
;
569 #endif // wxHAS_MSW_TASKDIALOG
571 return wxGenericProgressDialog::WasSkipped();
574 bool wxProgressDialog::WasCancelled() const
576 #ifdef wxHAS_MSW_TASKDIALOG
577 if ( HasNativeTaskDialog() )
579 wxCriticalSectionLocker
locker(m_sharedData
->m_cs
);
580 return m_sharedData
->m_state
== Canceled
;
582 #endif // wxHAS_MSW_TASKDIALOG
584 return wxGenericProgressDialog::WasCancelled();
587 void wxProgressDialog::SetTitle(const wxString
& title
)
589 #ifdef wxHAS_MSW_TASKDIALOG
590 if ( HasNativeTaskDialog() )
596 wxCriticalSectionLocker
locker(m_sharedData
->m_cs
);
597 m_sharedData
->m_title
= title
;
598 m_sharedData
->m_notifications
= wxSPDD_TITLE_CHANGED
;
601 #endif // wxHAS_MSW_TASKDIALOG
603 wxGenericProgressDialog::SetTitle(title
);
606 wxString
wxProgressDialog::GetTitle() const
608 #ifdef wxHAS_MSW_TASKDIALOG
609 if ( HasNativeTaskDialog() )
611 #endif // wxHAS_MSW_TASKDIALOG
613 return wxGenericProgressDialog::GetTitle();
616 bool wxProgressDialog::Show(bool show
)
618 #ifdef wxHAS_MSW_TASKDIALOG
619 if ( HasNativeTaskDialog() )
621 // The dialog can't be hidden at all and showing it again after it had
622 // been shown before doesn't do anything.
623 if ( !show
|| m_taskDialogRunner
)
626 // We're showing the dialog for the first time, create the thread that
628 m_taskDialogRunner
= new wxProgressDialogTaskRunner
;
629 m_sharedData
= m_taskDialogRunner
->GetSharedDataObject();
631 // Initialize shared data.
632 m_sharedData
->m_title
= m_title
;
633 m_sharedData
->m_message
= m_message
;
634 m_sharedData
->m_range
= m_maximum
;
635 m_sharedData
->m_state
= Uncancelable
;
636 m_sharedData
->m_style
= GetPDStyle();
637 m_sharedData
->m_parent
= GetTopParent();
639 if ( HasPDFlag(wxPD_CAN_ABORT
) )
641 m_sharedData
->m_state
= Continue
;
642 m_sharedData
->m_labelCancel
= _("Cancel");
644 else // Dialog can't be cancelled.
646 // We still must have at least a single button in the dialog so
647 // just don't call it "Cancel" in this case.
648 m_sharedData
->m_labelCancel
= _("Close");
651 if ( HasPDFlag(wxPD_ELAPSED_TIME
|
652 wxPD_ESTIMATED_TIME
|
653 wxPD_REMAINING_TIME
) )
655 // Use a non-empty string just to have the collapsible pane shown.
656 m_sharedData
->m_expandedInformation
= " ";
659 // Do launch the thread.
660 if ( m_taskDialogRunner
->Create() != wxTHREAD_NO_ERROR
)
662 wxLogError( "Unable to create thread!" );
666 if ( m_taskDialogRunner
->Run() != wxTHREAD_NO_ERROR
)
668 wxLogError( "Unable to start thread!" );
672 // Do not show the underlying dialog.
675 #endif // wxHAS_MSW_TASKDIALOG
677 return wxGenericProgressDialog::Show( show
);
680 void wxProgressDialog::UpdateExpandedInformation(int value
)
682 #ifdef wxHAS_MSW_TASKDIALOG
683 unsigned long elapsedTime
;
684 unsigned long estimatedTime
;
685 unsigned long remainingTime
;
686 UpdateTimeEstimates(value
, elapsedTime
, estimatedTime
, remainingTime
);
688 int realEstimatedTime
= estimatedTime
,
689 realRemainingTime
= remainingTime
;
690 if ( m_sharedData
->m_progressBarMarquee
)
692 // In indeterminate mode we don't have any estimation neither for the
693 // remaining nor for estimated time.
695 realRemainingTime
= -1;
698 wxString expandedInformation
;
700 // Calculate the three different timing values.
701 if ( HasPDFlag(wxPD_ELAPSED_TIME
) )
703 expandedInformation
<< GetElapsedLabel()
705 << GetFormattedTime(elapsedTime
);
708 if ( HasPDFlag(wxPD_ESTIMATED_TIME
) )
710 if ( !expandedInformation
.empty() )
711 expandedInformation
+= "\n";
713 expandedInformation
<< GetEstimatedLabel()
715 << GetFormattedTime(realEstimatedTime
);
718 if ( HasPDFlag(wxPD_REMAINING_TIME
) )
720 if ( !expandedInformation
.empty() )
721 expandedInformation
+= "\n";
723 expandedInformation
<< GetRemainingLabel()
725 << GetFormattedTime(realRemainingTime
);
728 // Update with new timing information.
729 if ( expandedInformation
!= m_sharedData
->m_expandedInformation
)
731 m_sharedData
->m_expandedInformation
= expandedInformation
;
732 m_sharedData
->m_notifications
|= wxSPDD_EXPINFO_CHANGED
;
734 #else // !wxHAS_MSW_TASKDIALOG
736 #endif // wxHAS_MSW_TASKDIALOG/!wxHAS_MSW_TASKDIALOG
739 // ----------------------------------------------------------------------------
740 // wxProgressDialogTaskRunner and related methods
741 // ----------------------------------------------------------------------------
743 #ifdef wxHAS_MSW_TASKDIALOG
745 void* wxProgressDialogTaskRunner::Entry()
747 WinStruct
<TASKDIALOGCONFIG
> tdc
;
748 wxMSWTaskDialogConfig wxTdc
;
751 wxCriticalSectionLocker
locker(m_sharedData
.m_cs
);
753 wxTdc
.caption
= m_sharedData
.m_title
.wx_str();
754 wxTdc
.message
= m_sharedData
.m_message
.wx_str();
756 wxTdc
.MSWCommonTaskDialogInit( tdc
);
757 tdc
.pfCallback
= TaskDialogCallbackProc
;
758 tdc
.lpCallbackData
= (LONG_PTR
) &m_sharedData
;
760 // Undo some of the effects of MSWCommonTaskDialogInit().
761 tdc
.dwFlags
&= ~TDF_EXPAND_FOOTER_AREA
; // Expand in content area.
762 tdc
.dwCommonButtons
= 0; // Don't use common buttons.
764 wxTdc
.useCustomLabels
= true;
766 if ( m_sharedData
.m_style
& wxPD_CAN_SKIP
)
767 wxTdc
.AddTaskDialogButton( tdc
, Id_SkipBtn
, 0, _("Skip") );
769 // Use a Cancel button when requested or use a Close button when
770 // the dialog does not automatically hide.
771 wxTdc
.AddTaskDialogButton( tdc
, IDCANCEL
, 0,
772 m_sharedData
.m_labelCancel
);
774 tdc
.dwFlags
|= TDF_CALLBACK_TIMER
| TDF_SHOW_PROGRESS_BAR
;
776 if ( !m_sharedData
.m_expandedInformation
.empty() )
778 tdc
.pszExpandedInformation
=
779 m_sharedData
.m_expandedInformation
.wx_str();
783 TaskDialogIndirect_t taskDialogIndirect
= GetTaskDialogIndirectFunc();
784 if ( !taskDialogIndirect
)
788 HRESULT hr
= taskDialogIndirect(&tdc
, &msAns
, NULL
, NULL
);
790 wxLogApiError( "TaskDialogIndirect", hr
);
792 // If the main thread is waiting for us to exit inside the event loop in
793 // Update(), wake it up so that it checks our status again.
801 wxProgressDialogTaskRunner::TaskDialogCallbackProc
806 LPARAM
WXUNUSED(lParam
),
810 wxProgressDialogSharedData
* const sharedData
=
811 (wxProgressDialogSharedData
*) dwRefData
;
813 wxCriticalSectionLocker
locker(sharedData
->m_cs
);
815 switch ( uNotification
)
818 // Store the HWND for the main thread use.
819 sharedData
->m_hwnd
= hwnd
;
821 // Set the maximum value and disable Close button.
823 TDM_SET_PROGRESS_BAR_RANGE
,
825 MAKELPARAM(0, sharedData
->m_range
) );
827 // We always create this task dialog with NULL parent because our
828 // parent in wx sense is a window created from a different thread
829 // and so can't be used as our real parent. However we still center
830 // this window on the parent one as the task dialogs do with their
831 // real parent usually.
832 if ( sharedData
->m_parent
)
834 wxRect
rect(wxRectFromRECT(wxGetWindowRect(hwnd
)));
835 rect
= rect
.CentreIn(sharedData
->m_parent
->GetRect());
848 // If we can't be aborted, the "Close" button will only be enabled
849 // when the progress ends (and not even then with wxPD_AUTO_HIDE).
850 if ( !(sharedData
->m_style
& wxPD_CAN_ABORT
) )
851 ::SendMessage( hwnd
, TDM_ENABLE_BUTTON
, IDCANCEL
, FALSE
);
854 case TDN_BUTTON_CLICKED
:
858 ::SendMessage(hwnd
, TDM_ENABLE_BUTTON
, Id_SkipBtn
, FALSE
);
859 sharedData
->m_skipped
= true;
863 if ( sharedData
->m_state
== wxProgressDialog::Finished
)
865 // If the main thread is waiting for us, tell it that
866 // we're gone (and if it doesn't wait, it's harmless).
867 sharedData
->m_state
= wxProgressDialog::Dismissed
;
869 // Let Windows close the dialog.
873 // Close button on the window triggers an IDCANCEL press,
874 // don't allow it when it should only be possible to close
875 // a finished dialog.
876 if ( sharedData
->m_style
& wxPD_CAN_ABORT
)
880 sharedData
->m_state
== wxProgressDialog::Continue
,
882 "Dialog not in a cancelable state!"
885 ::SendMessage(hwnd
, TDM_ENABLE_BUTTON
, Id_SkipBtn
, FALSE
);
886 ::SendMessage(hwnd
, TDM_ENABLE_BUTTON
, IDCANCEL
, FALSE
);
888 sharedData
->m_timeStop
= wxGetCurrentTime();
889 sharedData
->m_state
= wxProgressDialog::Canceled
;
897 PerformNotificationUpdates(hwnd
, sharedData
);
900 Decide whether we should end the dialog. This is done if either
901 the dialog object itself was destroyed or if the progress
902 finished and we were configured to hide automatically without
903 waiting for the user to dismiss us.
905 Notice that we do not close the dialog if it was cancelled
906 because it's up to the user code in the main thread to decide
907 whether it really wants to cancel the dialog.
909 if ( (sharedData
->m_notifications
& wxSPDD_DESTROYED
) ||
910 (sharedData
->m_state
== wxProgressDialog::Finished
&&
911 sharedData
->m_style
& wxPD_AUTO_HIDE
) )
913 ::EndDialog( hwnd
, IDCLOSE
);
916 sharedData
->m_notifications
= 0;
925 #endif // wxHAS_MSW_TASKDIALOG
927 #endif // wxUSE_PROGRESSDLG && wxUSE_THREADS