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
);
241 TDM_SET_ELEMENT_TEXT
,
242 TDE_MAIN_INSTRUCTION
,
243 (LPARAM
) title
.wx_str() );
246 TDM_SET_ELEMENT_TEXT
,
248 (LPARAM
) body
.wx_str() );
251 if ( sharedData
->m_notifications
& wxSPDD_EXPINFO_CHANGED
)
253 const wxString
& expandedInformation
=
254 sharedData
->m_expandedInformation
;
255 if ( !expandedInformation
.empty() )
258 TDM_SET_ELEMENT_TEXT
,
259 TDE_EXPANDED_INFORMATION
,
260 (LPARAM
) expandedInformation
.wx_str() );
264 if ( sharedData
->m_notifications
& wxSPDD_ENABLE_SKIP
)
265 ::SendMessage( hwnd
, TDM_ENABLE_BUTTON
, Id_SkipBtn
, TRUE
);
267 if ( sharedData
->m_notifications
& wxSPDD_ENABLE_ABORT
)
268 ::SendMessage( hwnd
, TDM_ENABLE_BUTTON
, IDCANCEL
, TRUE
);
270 if ( sharedData
->m_notifications
& wxSPDD_DISABLE_SKIP
)
271 ::SendMessage( hwnd
, TDM_ENABLE_BUTTON
, Id_SkipBtn
, FALSE
);
273 if ( sharedData
->m_notifications
& wxSPDD_DISABLE_ABORT
)
274 ::SendMessage( hwnd
, TDM_ENABLE_BUTTON
, IDCANCEL
, FALSE
);
276 // Is the progress finished?
277 if ( sharedData
->m_notifications
& wxSPDD_FINISHED
)
279 sharedData
->m_state
= wxProgressDialog::Finished
;
281 if ( !(sharedData
->m_style
& wxPD_AUTO_HIDE
) )
283 // Change Cancel into Close and activate the button.
284 ::SendMessage( hwnd
, TDM_ENABLE_BUTTON
, Id_SkipBtn
, FALSE
);
285 ::SendMessage( hwnd
, TDM_ENABLE_BUTTON
, IDCANCEL
, TRUE
);
286 ::EnumChildWindows( hwnd
, DisplayCloseButton
,
287 (LPARAM
) sharedData
);
292 } // anonymous namespace
294 #endif // wxHAS_MSW_TASKDIALOG
296 // ============================================================================
297 // wxProgressDialog implementation
298 // ============================================================================
300 wxProgressDialog::wxProgressDialog( const wxString
& title
,
301 const wxString
& message
,
305 : wxGenericProgressDialog(parent
, style
),
306 m_taskDialogRunner(NULL
),
311 #ifdef wxHAS_MSW_TASKDIALOG
312 if ( HasNativeTaskDialog() )
317 DisableOtherWindows();
321 #endif // wxHAS_MSW_TASKDIALOG
323 Create(title
, message
, maximum
, parent
, style
);
326 wxProgressDialog::~wxProgressDialog()
328 #ifdef wxHAS_MSW_TASKDIALOG
329 if ( !m_taskDialogRunner
)
334 wxCriticalSectionLocker
locker(m_sharedData
->m_cs
);
335 m_sharedData
->m_notifications
|= wxSPDD_DESTROYED
;
338 m_taskDialogRunner
->Wait();
340 delete m_taskDialogRunner
;
342 ReenableOtherWindows();
344 if ( GetTopParent() )
345 GetTopParent()->Raise();
346 #endif // wxHAS_MSW_TASKDIALOG
349 bool wxProgressDialog::Update(int value
, const wxString
& newmsg
, bool *skip
)
351 #ifdef wxHAS_MSW_TASKDIALOG
352 if ( HasNativeTaskDialog() )
355 wxCriticalSectionLocker
locker(m_sharedData
->m_cs
);
357 // Do nothing in canceled state.
358 if ( !DoNativeBeforeUpdate(skip
) )
363 wxASSERT_MSG( value
<= m_maximum
, wxT("invalid progress value") );
365 m_sharedData
->m_value
= value
;
366 m_sharedData
->m_notifications
|= wxSPDD_VALUE_CHANGED
;
368 if ( !newmsg
.empty() )
371 m_sharedData
->m_message
= newmsg
;
372 m_sharedData
->m_notifications
|= wxSPDD_MESSAGE_CHANGED
;
375 if ( m_sharedData
->m_progressBarMarquee
)
377 m_sharedData
->m_progressBarMarquee
= false;
378 m_sharedData
->m_notifications
|= wxSPDD_PBMARQUEE_CHANGED
;
381 UpdateExpandedInformation( value
);
383 // If we didn't just reach the finish, all we have to do is to
384 // return true if the dialog wasn't cancelled and false otherwise.
385 if ( value
!= m_maximum
|| m_state
== Finished
)
386 return m_sharedData
->m_state
!= Canceled
;
389 // On finishing, the dialog without wxPD_AUTO_HIDE style becomes a
390 // modal one meaning that we must block here until the user
393 m_sharedData
->m_state
= Finished
;
394 m_sharedData
->m_notifications
|= wxSPDD_FINISHED
;
395 if ( HasPDFlag(wxPD_AUTO_HIDE
) )
398 if ( newmsg
.empty() )
400 // Provide the finishing message if the application didn't.
401 m_message
= _("Done.");
402 m_sharedData
->m_message
= m_message
;
403 m_sharedData
->m_notifications
|= wxSPDD_MESSAGE_CHANGED
;
405 } // unlock m_sharedData->m_cs
407 // We only get here when we need to wait for the dialog to terminate so
408 // do just this by running a custom event loop until the dialog is
410 wxProgressDialogModalLoop
loop(*m_sharedData
);
414 #endif // wxHAS_MSW_TASKDIALOG
416 return wxGenericProgressDialog::Update( value
, newmsg
, skip
);
419 bool wxProgressDialog::Pulse(const wxString
& newmsg
, bool *skip
)
421 #ifdef wxHAS_MSW_TASKDIALOG
422 if ( HasNativeTaskDialog() )
424 wxCriticalSectionLocker
locker(m_sharedData
->m_cs
);
426 // Do nothing in canceled state.
427 if ( !DoNativeBeforeUpdate(skip
) )
430 if ( !m_sharedData
->m_progressBarMarquee
)
432 m_sharedData
->m_progressBarMarquee
= true;
433 m_sharedData
->m_notifications
|= wxSPDD_PBMARQUEE_CHANGED
;
436 if ( !newmsg
.empty() )
439 m_sharedData
->m_message
= newmsg
;
440 m_sharedData
->m_notifications
|= wxSPDD_MESSAGE_CHANGED
;
443 // The value passed here doesn't matter, only elapsed time makes sense
444 // in indeterminate mode anyhow.
445 UpdateExpandedInformation(0);
447 return m_sharedData
->m_state
!= Canceled
;
449 #endif // wxHAS_MSW_TASKDIALOG
451 return wxGenericProgressDialog::Pulse( newmsg
, skip
);
454 bool wxProgressDialog::DoNativeBeforeUpdate(bool *skip
)
456 #ifdef wxHAS_MSW_TASKDIALOG
457 if ( HasNativeTaskDialog() )
459 if ( m_sharedData
->m_skipped
)
461 if ( skip
&& !*skip
)
464 m_sharedData
->m_skipped
= false;
465 m_sharedData
->m_notifications
|= wxSPDD_ENABLE_SKIP
;
469 if ( m_sharedData
->m_state
== Canceled
)
470 m_timeStop
= m_sharedData
->m_timeStop
;
472 return m_sharedData
->m_state
!= Canceled
;
474 #endif // wxHAS_MSW_TASKDIALOG
477 wxFAIL_MSG( "unreachable" );
482 void wxProgressDialog::Resume()
484 wxGenericProgressDialog::Resume();
486 #ifdef wxHAS_MSW_TASKDIALOG
487 if ( HasNativeTaskDialog() )
492 wxCriticalSectionLocker
locker(m_sharedData
->m_cs
);
493 m_sharedData
->m_state
= m_state
;
495 // "Skip" was disabled when "Cancel" had been clicked, so re-enable
497 m_sharedData
->m_notifications
|= wxSPDD_ENABLE_SKIP
;
499 // Also re-enable "Cancel" itself
500 if ( HasPDFlag(wxPD_CAN_ABORT
) )
501 m_sharedData
->m_notifications
|= wxSPDD_ENABLE_ABORT
;
503 hwnd
= m_sharedData
->m_hwnd
;
504 } // Unlock m_cs, we can't call any function operating on a dialog with
505 // it locked as it can result in a deadlock if the dialog callback is
506 // called by Windows.
508 // After resuming we need to bring the window on top of the Z-order as
509 // it could be hidden by another window shown from the main thread,
510 // e.g. a confirmation dialog asking whether the user really wants to
513 // Notice that this must be done from the main thread as it owns the
514 // currently active window and attempts to do this from the task dialog
515 // thread would simply fail.
516 ::BringWindowToTop(hwnd
);
518 #endif // wxHAS_MSW_TASKDIALOG
521 WXWidget
wxProgressDialog::GetHandle() const
523 #ifdef wxHAS_MSW_TASKDIALOG
524 if ( HasNativeTaskDialog() )
528 wxCriticalSectionLocker
locker(m_sharedData
->m_cs
);
529 m_sharedData
->m_state
= m_state
;
530 hwnd
= m_sharedData
->m_hwnd
;
535 return wxGenericProgressDialog::GetHandle();
538 int wxProgressDialog::GetValue() const
540 #ifdef wxHAS_MSW_TASKDIALOG
541 if ( HasNativeTaskDialog() )
543 wxCriticalSectionLocker
locker(m_sharedData
->m_cs
);
544 return m_sharedData
->m_value
;
546 #endif // wxHAS_MSW_TASKDIALOG
548 return wxGenericProgressDialog::GetValue();
551 wxString
wxProgressDialog::GetMessage() const
553 #ifdef wxHAS_MSW_TASKDIALOG
554 if ( HasNativeTaskDialog() )
556 #endif // wxHAS_MSW_TASKDIALOG
558 return wxGenericProgressDialog::GetMessage();
561 void wxProgressDialog::SetRange(int maximum
)
563 #ifdef wxHAS_MSW_TASKDIALOG
564 if ( HasNativeTaskDialog() )
568 wxCriticalSectionLocker
locker(m_sharedData
->m_cs
);
570 m_sharedData
->m_range
= maximum
;
571 m_sharedData
->m_notifications
|= wxSPDD_RANGE_CHANGED
;
575 #endif // wxHAS_MSW_TASKDIALOG
577 wxGenericProgressDialog::SetRange( maximum
);
580 bool wxProgressDialog::WasSkipped() const
582 #ifdef wxHAS_MSW_TASKDIALOG
583 if ( HasNativeTaskDialog() )
587 // Couldn't be skipped before being shown.
591 wxCriticalSectionLocker
locker(m_sharedData
->m_cs
);
592 return m_sharedData
->m_skipped
;
594 #endif // wxHAS_MSW_TASKDIALOG
596 return wxGenericProgressDialog::WasSkipped();
599 bool wxProgressDialog::WasCancelled() const
601 #ifdef wxHAS_MSW_TASKDIALOG
602 if ( HasNativeTaskDialog() )
604 wxCriticalSectionLocker
locker(m_sharedData
->m_cs
);
605 return m_sharedData
->m_state
== Canceled
;
607 #endif // wxHAS_MSW_TASKDIALOG
609 return wxGenericProgressDialog::WasCancelled();
612 void wxProgressDialog::SetTitle(const wxString
& title
)
614 #ifdef wxHAS_MSW_TASKDIALOG
615 if ( HasNativeTaskDialog() )
621 wxCriticalSectionLocker
locker(m_sharedData
->m_cs
);
622 m_sharedData
->m_title
= title
;
623 m_sharedData
->m_notifications
= wxSPDD_TITLE_CHANGED
;
626 #endif // wxHAS_MSW_TASKDIALOG
628 wxGenericProgressDialog::SetTitle(title
);
631 wxString
wxProgressDialog::GetTitle() const
633 #ifdef wxHAS_MSW_TASKDIALOG
634 if ( HasNativeTaskDialog() )
636 #endif // wxHAS_MSW_TASKDIALOG
638 return wxGenericProgressDialog::GetTitle();
641 bool wxProgressDialog::Show(bool show
)
643 #ifdef wxHAS_MSW_TASKDIALOG
644 if ( HasNativeTaskDialog() )
646 // The dialog can't be hidden at all and showing it again after it had
647 // been shown before doesn't do anything.
648 if ( !show
|| m_taskDialogRunner
)
651 // We're showing the dialog for the first time, create the thread that
653 m_taskDialogRunner
= new wxProgressDialogTaskRunner
;
654 m_sharedData
= m_taskDialogRunner
->GetSharedDataObject();
656 // Initialize shared data.
657 m_sharedData
->m_title
= m_title
;
658 m_sharedData
->m_message
= m_message
;
659 m_sharedData
->m_range
= m_maximum
;
660 m_sharedData
->m_state
= Uncancelable
;
661 m_sharedData
->m_style
= GetPDStyle();
662 m_sharedData
->m_parent
= GetTopParent();
664 if ( HasPDFlag(wxPD_CAN_ABORT
) )
666 m_sharedData
->m_state
= Continue
;
667 m_sharedData
->m_labelCancel
= _("Cancel");
669 else // Dialog can't be cancelled.
671 // We still must have at least a single button in the dialog so
672 // just don't call it "Cancel" in this case.
673 m_sharedData
->m_labelCancel
= _("Close");
676 if ( HasPDFlag(wxPD_ELAPSED_TIME
|
677 wxPD_ESTIMATED_TIME
|
678 wxPD_REMAINING_TIME
) )
680 // Use a non-empty string just to have the collapsible pane shown.
681 m_sharedData
->m_expandedInformation
= " ";
684 // Do launch the thread.
685 if ( m_taskDialogRunner
->Create() != wxTHREAD_NO_ERROR
)
687 wxLogError( "Unable to create thread!" );
691 if ( m_taskDialogRunner
->Run() != wxTHREAD_NO_ERROR
)
693 wxLogError( "Unable to start thread!" );
697 // Do not show the underlying dialog.
700 #endif // wxHAS_MSW_TASKDIALOG
702 return wxGenericProgressDialog::Show( show
);
705 void wxProgressDialog::UpdateExpandedInformation(int value
)
707 #ifdef wxHAS_MSW_TASKDIALOG
708 unsigned long elapsedTime
;
709 unsigned long estimatedTime
;
710 unsigned long remainingTime
;
711 UpdateTimeEstimates(value
, elapsedTime
, estimatedTime
, remainingTime
);
713 int realEstimatedTime
= estimatedTime
,
714 realRemainingTime
= remainingTime
;
715 if ( m_sharedData
->m_progressBarMarquee
)
717 // In indeterminate mode we don't have any estimation neither for the
718 // remaining nor for estimated time.
720 realRemainingTime
= -1;
723 wxString expandedInformation
;
725 // Calculate the three different timing values.
726 if ( HasPDFlag(wxPD_ELAPSED_TIME
) )
728 expandedInformation
<< GetElapsedLabel()
730 << GetFormattedTime(elapsedTime
);
733 if ( HasPDFlag(wxPD_ESTIMATED_TIME
) )
735 if ( !expandedInformation
.empty() )
736 expandedInformation
+= "\n";
738 expandedInformation
<< GetEstimatedLabel()
740 << GetFormattedTime(realEstimatedTime
);
743 if ( HasPDFlag(wxPD_REMAINING_TIME
) )
745 if ( !expandedInformation
.empty() )
746 expandedInformation
+= "\n";
748 expandedInformation
<< GetRemainingLabel()
750 << GetFormattedTime(realRemainingTime
);
753 // Update with new timing information.
754 if ( expandedInformation
!= m_sharedData
->m_expandedInformation
)
756 m_sharedData
->m_expandedInformation
= expandedInformation
;
757 m_sharedData
->m_notifications
|= wxSPDD_EXPINFO_CHANGED
;
759 #else // !wxHAS_MSW_TASKDIALOG
761 #endif // wxHAS_MSW_TASKDIALOG/!wxHAS_MSW_TASKDIALOG
764 // ----------------------------------------------------------------------------
765 // wxProgressDialogTaskRunner and related methods
766 // ----------------------------------------------------------------------------
768 #ifdef wxHAS_MSW_TASKDIALOG
770 void* wxProgressDialogTaskRunner::Entry()
772 WinStruct
<TASKDIALOGCONFIG
> tdc
;
773 wxMSWTaskDialogConfig wxTdc
;
776 wxCriticalSectionLocker
locker(m_sharedData
.m_cs
);
778 wxTdc
.caption
= m_sharedData
.m_title
.wx_str();
779 wxTdc
.message
= m_sharedData
.m_message
.wx_str();
781 // MSWCommonTaskDialogInit() will add an IDCANCEL button but we need to
782 // give it the correct label.
783 wxTdc
.btnOKLabel
= m_sharedData
.m_labelCancel
;
784 wxTdc
.useCustomLabels
= true;
786 wxTdc
.MSWCommonTaskDialogInit( tdc
);
787 tdc
.pfCallback
= TaskDialogCallbackProc
;
788 tdc
.lpCallbackData
= (LONG_PTR
) &m_sharedData
;
790 // Undo some of the effects of MSWCommonTaskDialogInit().
791 tdc
.dwFlags
&= ~TDF_EXPAND_FOOTER_AREA
; // Expand in content area.
792 tdc
.dwCommonButtons
= 0; // Don't use common buttons.
794 if ( m_sharedData
.m_style
& wxPD_CAN_SKIP
)
795 wxTdc
.AddTaskDialogButton( tdc
, Id_SkipBtn
, 0, _("Skip") );
797 tdc
.dwFlags
|= TDF_CALLBACK_TIMER
| TDF_SHOW_PROGRESS_BAR
;
799 if ( !m_sharedData
.m_expandedInformation
.empty() )
801 tdc
.pszExpandedInformation
=
802 m_sharedData
.m_expandedInformation
.wx_str();
806 TaskDialogIndirect_t taskDialogIndirect
= GetTaskDialogIndirectFunc();
807 if ( !taskDialogIndirect
)
811 HRESULT hr
= taskDialogIndirect(&tdc
, &msAns
, NULL
, NULL
);
813 wxLogApiError( "TaskDialogIndirect", hr
);
815 // If the main thread is waiting for us to exit inside the event loop in
816 // Update(), wake it up so that it checks our status again.
824 wxProgressDialogTaskRunner::TaskDialogCallbackProc
829 LPARAM
WXUNUSED(lParam
),
833 wxProgressDialogSharedData
* const sharedData
=
834 (wxProgressDialogSharedData
*) dwRefData
;
836 wxCriticalSectionLocker
locker(sharedData
->m_cs
);
838 switch ( uNotification
)
841 // Store the HWND for the main thread use.
842 sharedData
->m_hwnd
= hwnd
;
844 // Set the maximum value and disable Close button.
846 TDM_SET_PROGRESS_BAR_RANGE
,
848 MAKELPARAM(0, sharedData
->m_range
) );
850 // We always create this task dialog with NULL parent because our
851 // parent in wx sense is a window created from a different thread
852 // and so can't be used as our real parent. However we still center
853 // this window on the parent one as the task dialogs do with their
854 // real parent usually.
855 if ( sharedData
->m_parent
)
857 wxRect
rect(wxRectFromRECT(wxGetWindowRect(hwnd
)));
858 rect
= rect
.CentreIn(sharedData
->m_parent
->GetRect());
871 // If we can't be aborted, the "Close" button will only be enabled
872 // when the progress ends (and not even then with wxPD_AUTO_HIDE).
873 if ( !(sharedData
->m_style
& wxPD_CAN_ABORT
) )
874 ::SendMessage( hwnd
, TDM_ENABLE_BUTTON
, IDCANCEL
, FALSE
);
877 case TDN_BUTTON_CLICKED
:
881 ::SendMessage(hwnd
, TDM_ENABLE_BUTTON
, Id_SkipBtn
, FALSE
);
882 sharedData
->m_skipped
= true;
886 if ( sharedData
->m_state
== wxProgressDialog::Finished
)
888 // If the main thread is waiting for us, tell it that
889 // we're gone (and if it doesn't wait, it's harmless).
890 sharedData
->m_state
= wxProgressDialog::Dismissed
;
892 // Let Windows close the dialog.
896 // Close button on the window triggers an IDCANCEL press,
897 // don't allow it when it should only be possible to close
898 // a finished dialog.
899 if ( sharedData
->m_style
& wxPD_CAN_ABORT
)
903 sharedData
->m_state
== wxProgressDialog::Continue
,
905 "Dialog not in a cancelable state!"
908 ::SendMessage(hwnd
, TDM_ENABLE_BUTTON
, Id_SkipBtn
, FALSE
);
909 ::SendMessage(hwnd
, TDM_ENABLE_BUTTON
, IDCANCEL
, FALSE
);
911 sharedData
->m_timeStop
= wxGetCurrentTime();
912 sharedData
->m_state
= wxProgressDialog::Canceled
;
920 PerformNotificationUpdates(hwnd
, sharedData
);
923 Decide whether we should end the dialog. This is done if either
924 the dialog object itself was destroyed or if the progress
925 finished and we were configured to hide automatically without
926 waiting for the user to dismiss us.
928 Notice that we do not close the dialog if it was cancelled
929 because it's up to the user code in the main thread to decide
930 whether it really wants to cancel the dialog.
932 if ( (sharedData
->m_notifications
& wxSPDD_DESTROYED
) ||
933 (sharedData
->m_state
== wxProgressDialog::Finished
&&
934 sharedData
->m_style
& wxPD_AUTO_HIDE
) )
936 ::EndDialog( hwnd
, IDCLOSE
);
939 sharedData
->m_notifications
= 0;
948 #endif // wxHAS_MSW_TASKDIALOG
950 #endif // wxUSE_PROGRESSDLG && wxUSE_THREADS