Remove unused wxProgressDialogTaskRunner::m_parent field.
[wxWidgets.git] / src / msw / progdlg.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/msw/progdlg.cpp
3 // Purpose: wxProgressDialog
4 // Author: Rickard Westerlund
5 // Created: 2010-07-22
6 // RCS-ID: $Id$
7 // Copyright: (c) 2010 wxWidgets team
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10
11 // ============================================================================
12 // Declarations
13 // ============================================================================
14
15 // ----------------------------------------------------------------------------
16 // Headers
17 // ----------------------------------------------------------------------------
18
19 // For compilers that support precompilation, includes "wx.h".
20 #include "wx/wxprec.h"
21
22 #ifdef __BORLANDC__
23 #pragma hdrstop
24 #endif
25
26 #if wxUSE_PROGRESSDLG && wxUSE_THREADS
27
28 #include "wx/msw/private/msgdlg.h"
29 #include "wx/progdlg.h"
30
31 using namespace wxMSWMessageDialog;
32
33 #ifdef wxHAS_MSW_TASKDIALOG
34
35 // ----------------------------------------------------------------------------
36 // Constants
37 // ----------------------------------------------------------------------------
38
39 namespace
40 {
41
42 // Notification values of wxProgressDialogSharedData::m_notifications
43 const int wxSPDD_VALUE_CHANGED = 0x0001;
44 const int wxSPDD_RANGE_CHANGED = 0x0002;
45 const int wxSPDD_PBMARQUEE_CHANGED = 0x0004;
46 const int wxSPDD_TITLE_CHANGED = 0x0008;
47 const int wxSPDD_MESSAGE_CHANGED = 0x0010;
48 const int wxSPDD_EXPINFO_CHANGED = 0x0020;
49 const int wxSPDD_ENABLE_SKIP = 0x0040;
50 const int wxSPDD_ENABLE_ABORT = 0x0080;
51 const int wxSPDD_DISABLE_SKIP = 0x0100;
52 const int wxSPDD_DISABLE_ABORT = 0x0200;
53 const int wxSPDD_FINISHED = 0x0400;
54 const int wxSPDD_DESTROYED = 0x0800;
55
56 const int Id_SkipBtn = wxID_HIGHEST + 1;
57
58 } // anonymous namespace
59
60 // ============================================================================
61 // Helper classes
62 // ============================================================================
63
64 // Class used to share data between the main thread and the task dialog runner.
65 class wxProgressDialogSharedData
66 {
67 public:
68 wxProgressDialogSharedData()
69 {
70 m_hwnd = 0;
71 m_value = 0;
72 m_progressBarMarquee = false;
73 m_skipped = false;
74 m_notifications = 0;
75 }
76
77 wxCriticalSection m_cs;
78
79 HWND m_hwnd; // Task dialog handler
80 long m_style; // wxProgressDialog style
81 int m_value;
82 int m_range;
83 wxString m_title;
84 wxString m_message;
85 wxString m_expandedInformation;
86 wxString m_labelCancel; // Privately used by callback.
87 unsigned long m_timeStop;
88
89 wxGenericProgressDialog::ProgressDialogState m_state;
90 bool m_progressBarMarquee;
91 bool m_skipped;
92
93 // Bit field that indicates fields that have been modified by the
94 // main thread so the task dialog runner knows what to update.
95 int m_notifications;
96 };
97
98 // Runner thread that takes care of displaying and updating the
99 // task dialog.
100 class wxProgressDialogTaskRunner : public wxThread
101 {
102 public:
103 wxProgressDialogTaskRunner()
104 : wxThread(wxTHREAD_JOINABLE)
105 { }
106
107 wxProgressDialogSharedData* GetSharedDataObject()
108 { return &m_sharedData; }
109
110 private:
111 wxProgressDialogSharedData m_sharedData;
112
113 virtual void* Entry();
114
115 static HRESULT CALLBACK TaskDialogCallbackProc(HWND hwnd,
116 UINT uNotification,
117 WPARAM wParam,
118 LPARAM lParam,
119 LONG_PTR dwRefData);
120 };
121
122 // ============================================================================
123 // Helper functions
124 // ============================================================================
125
126 namespace
127 {
128
129 bool UsesCloseButtonOnly(long style)
130 {
131 return !((style & wxPD_CAN_ABORT) || (style & wxPD_AUTO_HIDE));
132 }
133
134 BOOL CALLBACK DisplayCloseButton(HWND hwnd, LPARAM lParam)
135 {
136 wxProgressDialogSharedData *sharedData =
137 (wxProgressDialogSharedData *) lParam;
138
139 if ( wxGetWindowText( hwnd ) == sharedData->m_labelCancel )
140 {
141 sharedData->m_labelCancel = _("Close");
142 SendMessage( hwnd, WM_SETTEXT, 0,
143 (LPARAM) sharedData->m_labelCancel.wx_str() );
144
145 return FALSE;
146 }
147
148 return TRUE;
149 }
150
151 void PerformNotificationUpdates(HWND hwnd,
152 wxProgressDialogSharedData *sharedData)
153 {
154 // Update the appropriate dialog fields.
155 if ( sharedData->m_notifications & wxSPDD_RANGE_CHANGED )
156 {
157 ::SendMessage( hwnd,
158 TDM_SET_PROGRESS_BAR_RANGE,
159 0,
160 MAKELPARAM(0, sharedData->m_range) );
161 }
162
163 if ( sharedData->m_notifications & wxSPDD_VALUE_CHANGED )
164 {
165 ::SendMessage( hwnd,
166 TDM_SET_PROGRESS_BAR_POS,
167 sharedData->m_value,
168 0 );
169 }
170
171 if ( sharedData->m_notifications & wxSPDD_PBMARQUEE_CHANGED )
172 {
173 BOOL val = sharedData->m_progressBarMarquee ? TRUE : FALSE;
174 ::SendMessage( hwnd,
175 TDM_SET_MARQUEE_PROGRESS_BAR,
176 val,
177 0 );
178 ::SendMessage( hwnd,
179 TDM_SET_PROGRESS_BAR_MARQUEE,
180 val,
181 0 );
182 }
183
184 if ( sharedData->m_notifications & wxSPDD_TITLE_CHANGED )
185 ::SetWindowText( hwnd, sharedData->m_title.wx_str() );
186
187 if ( sharedData->m_notifications & wxSPDD_MESSAGE_CHANGED )
188 {
189 // Split the message in the title string and the rest if it has
190 // multiple lines.
191 wxString
192 title = sharedData->m_message,
193 body;
194
195 const size_t posNL = title.find('\n');
196 if ( posNL != wxString::npos )
197 {
198 // There can an extra new line between the first and subsequent
199 // lines to separate them as it looks better with the generic
200 // version -- but in this one, they're already separated by the use
201 // of different dialog elements, so suppress the extra new line.
202 int numNLs = 1;
203 if ( posNL < title.length() - 1 && title[posNL + 1] == '\n' )
204 numNLs++;
205
206 body.assign(title, posNL + numNLs, wxString::npos);
207 title.erase(posNL);
208 }
209
210 ::SendMessage( hwnd,
211 TDM_SET_ELEMENT_TEXT,
212 TDE_MAIN_INSTRUCTION,
213 (LPARAM) title.wx_str() );
214
215 ::SendMessage( hwnd,
216 TDM_SET_ELEMENT_TEXT,
217 TDE_CONTENT,
218 (LPARAM) body.wx_str() );
219 }
220
221 if ( sharedData->m_notifications & wxSPDD_EXPINFO_CHANGED )
222 {
223 const wxString& expandedInformation =
224 sharedData->m_expandedInformation;
225 if ( !expandedInformation.empty() )
226 {
227 ::SendMessage( hwnd,
228 TDM_SET_ELEMENT_TEXT,
229 TDE_EXPANDED_INFORMATION,
230 (LPARAM) expandedInformation.wx_str() );
231 }
232 }
233
234 if ( sharedData->m_notifications & wxSPDD_ENABLE_SKIP )
235 ::SendMessage( hwnd, TDM_ENABLE_BUTTON, Id_SkipBtn, TRUE );
236
237 if ( sharedData->m_notifications & wxSPDD_ENABLE_ABORT )
238 ::SendMessage( hwnd, TDM_ENABLE_BUTTON, IDCANCEL, TRUE );
239
240 if ( sharedData->m_notifications & wxSPDD_DISABLE_SKIP )
241 ::SendMessage( hwnd, TDM_ENABLE_BUTTON, Id_SkipBtn, FALSE );
242
243 if ( sharedData->m_notifications & wxSPDD_DISABLE_ABORT )
244 ::SendMessage( hwnd, TDM_ENABLE_BUTTON, IDCANCEL, FALSE );
245
246 // Is the progress finished?
247 if ( sharedData->m_notifications & wxSPDD_FINISHED )
248 {
249 sharedData->m_state = wxGenericProgressDialog::Finished;
250
251 if ( !(sharedData->m_style & wxPD_AUTO_HIDE) )
252 {
253 // Change Cancel into Close and activate the button.
254 ::SendMessage( hwnd, TDM_ENABLE_BUTTON, Id_SkipBtn, FALSE );
255 ::SendMessage( hwnd, TDM_ENABLE_BUTTON, IDCANCEL, TRUE );
256 ::EnumChildWindows( hwnd, DisplayCloseButton,
257 (LPARAM) sharedData );
258 }
259 }
260 }
261
262 } // anonymous namespace
263
264 #endif // wxHAS_MSW_TASKDIALOG
265
266 // ============================================================================
267 // wxProgressDialog implementation
268 // ============================================================================
269
270 wxProgressDialog::wxProgressDialog( const wxString& title,
271 const wxString& message,
272 int maximum,
273 wxWindow *parent,
274 int style )
275 : wxGenericProgressDialog(parent, maximum, style),
276 m_taskDialogRunner(NULL),
277 m_sharedData(NULL),
278 m_message(message),
279 m_title(title)
280 {
281 #ifdef wxHAS_MSW_TASKDIALOG
282 if ( HasNativeProgressDialog() )
283 {
284 Show();
285 DisableOtherWindows();
286
287 return;
288 }
289 #endif // wxHAS_MSW_TASKDIALOG
290
291 Create(title, message, maximum, parent, style);
292 }
293
294 wxProgressDialog::~wxProgressDialog()
295 {
296 #ifdef wxHAS_MSW_TASKDIALOG
297 if ( !m_taskDialogRunner )
298 return;
299
300 if ( m_sharedData )
301 {
302 wxCriticalSectionLocker locker(m_sharedData->m_cs);
303 m_sharedData->m_notifications |= wxSPDD_DESTROYED;
304 }
305
306 m_taskDialogRunner->Wait();
307
308 delete m_taskDialogRunner;
309
310 ReenableOtherWindows();
311
312 if ( GetTopParent() )
313 GetTopParent()->Raise();
314 #endif // wxHAS_MSW_TASKDIALOG
315 }
316
317 bool wxProgressDialog::Update(int value, const wxString& newmsg, bool *skip)
318 {
319 #ifdef wxHAS_MSW_TASKDIALOG
320 if ( HasNativeProgressDialog() )
321 {
322 wxCriticalSectionLocker locker(m_sharedData->m_cs);
323
324 // Do nothing in canceled state.
325 if ( !DoNativeBeforeUpdate(skip) )
326 return false;
327
328 value /= m_factor;
329
330 wxASSERT_MSG( value <= m_maximum, wxT("invalid progress value") );
331
332 m_sharedData->m_value = value;
333 m_sharedData->m_notifications |= wxSPDD_VALUE_CHANGED;
334
335 if ( !newmsg.empty() )
336 {
337 m_message = newmsg;
338 m_sharedData->m_message = newmsg;
339 m_sharedData->m_notifications |= wxSPDD_MESSAGE_CHANGED;
340 }
341
342 if ( m_sharedData->m_progressBarMarquee )
343 {
344 m_sharedData->m_progressBarMarquee = false;
345 m_sharedData->m_notifications |= wxSPDD_PBMARQUEE_CHANGED;
346 }
347
348 UpdateExpandedInformation( value );
349
350 // Has the progress bar finished?
351 if ( value == m_maximum )
352 {
353 if ( m_state == Finished )
354 return true;
355
356 m_state = Finished;
357 m_sharedData->m_state = Finished;
358 m_sharedData->m_notifications |= wxSPDD_FINISHED;
359 if( !HasFlag(wxPD_AUTO_HIDE) && newmsg.empty() )
360 {
361 // Provide the finishing message if the application didn't.
362 m_message = _("Done.");
363 m_sharedData->m_message = m_message;
364 m_sharedData->m_notifications |= wxSPDD_MESSAGE_CHANGED;
365 }
366 }
367
368 return m_sharedData->m_state != Canceled;
369 }
370 #endif // wxHAS_MSW_TASKDIALOG
371
372 return wxGenericProgressDialog::Update( value, newmsg, skip );
373 }
374
375 bool wxProgressDialog::Pulse(const wxString& newmsg, bool *skip)
376 {
377 #ifdef wxHAS_MSW_TASKDIALOG
378 if ( HasNativeProgressDialog() )
379 {
380 wxCriticalSectionLocker locker(m_sharedData->m_cs);
381
382 // Do nothing in canceled state.
383 if ( !DoNativeBeforeUpdate(skip) )
384 return false;
385
386 if ( !m_sharedData->m_progressBarMarquee )
387 {
388 m_sharedData->m_progressBarMarquee = true;
389 m_sharedData->m_notifications |= wxSPDD_PBMARQUEE_CHANGED;
390 }
391
392 if ( !newmsg.empty() )
393 {
394 m_message = newmsg;
395 m_sharedData->m_message = newmsg;
396 m_sharedData->m_notifications |= wxSPDD_MESSAGE_CHANGED;
397 }
398
399 // The value passed here doesn't matter, only elapsed time makes sense
400 // in indeterminate mode anyhow.
401 UpdateExpandedInformation(0);
402
403 return m_sharedData->m_state != Canceled;
404 }
405 #endif // wxHAS_MSW_TASKDIALOG
406
407 return wxGenericProgressDialog::Pulse( newmsg, skip );
408 }
409
410 bool wxProgressDialog::DoNativeBeforeUpdate(bool *skip)
411 {
412 #ifdef wxHAS_MSW_TASKDIALOG
413 if ( HasNativeProgressDialog() )
414 {
415 if ( m_sharedData->m_skipped )
416 {
417 if ( skip && !*skip )
418 {
419 *skip = true;
420 m_sharedData->m_skipped = false;
421 m_sharedData->m_notifications |= wxSPDD_ENABLE_SKIP;
422 }
423 }
424
425 if ( m_sharedData->m_state == Canceled )
426 m_timeStop = m_sharedData->m_timeStop;
427
428 return m_sharedData->m_state != Canceled;
429 }
430 #endif // wxHAS_MSW_TASKDIALOG
431
432 wxUnusedVar(skip);
433 wxFAIL_MSG( "unreachable" );
434
435 return false;
436 }
437
438 void wxProgressDialog::Resume()
439 {
440 wxGenericProgressDialog::Resume();
441
442 #ifdef wxHAS_MSW_TASKDIALOG
443 if ( HasNativeProgressDialog() )
444 {
445 HWND hwnd;
446
447 {
448 wxCriticalSectionLocker locker(m_sharedData->m_cs);
449 m_sharedData->m_state = m_state;
450
451 // "Skip" was disabled when "Cancel" had been clicked, so re-enable
452 // it now.
453 m_sharedData->m_notifications |= wxSPDD_ENABLE_SKIP;
454
455 if ( !UsesCloseButtonOnly(m_windowStyle) )
456 m_sharedData->m_notifications |= wxSPDD_ENABLE_ABORT;
457
458 hwnd = m_sharedData->m_hwnd;
459 } // Unlock m_cs, we can't call any function operating on a dialog with
460 // it locked as it can result in a deadlock if the dialog callback is
461 // called by Windows.
462
463 // After resuming we need to bring the window on top of the Z-order as
464 // it could be hidden by another window shown from the main thread,
465 // e.g. a confirmation dialog asking whether the user really wants to
466 // abort.
467 //
468 // Notice that this must be done from the main thread as it owns the
469 // currently active window and attempts to do this from the task dialog
470 // thread would simply fail.
471 ::BringWindowToTop(hwnd);
472 }
473 #endif // wxHAS_MSW_TASKDIALOG
474 }
475
476 int wxProgressDialog::GetValue() const
477 {
478 #ifdef wxHAS_MSW_TASKDIALOG
479 if ( HasNativeProgressDialog() )
480 {
481 wxCriticalSectionLocker locker(m_sharedData->m_cs);
482 return m_sharedData->m_value;
483 }
484 #endif // wxHAS_MSW_TASKDIALOG
485
486 return wxGenericProgressDialog::GetValue();
487 }
488
489 wxString wxProgressDialog::GetMessage() const
490 {
491 #ifdef wxHAS_MSW_TASKDIALOG
492 if ( HasNativeProgressDialog() )
493 return m_message;
494 #endif // wxHAS_MSW_TASKDIALOG
495
496 return wxGenericProgressDialog::GetMessage();
497 }
498
499 void wxProgressDialog::SetRange(int maximum)
500 {
501 wxGenericProgressDialog::SetRange( maximum );
502
503 #ifdef wxHAS_MSW_TASKDIALOG
504 if ( HasNativeProgressDialog() )
505 {
506 wxCriticalSectionLocker locker(m_sharedData->m_cs);
507
508 m_sharedData->m_range = maximum;
509 m_sharedData->m_notifications |= wxSPDD_RANGE_CHANGED;
510 }
511 #endif // wxHAS_MSW_TASKDIALOG
512 }
513
514 bool wxProgressDialog::WasSkipped() const
515 {
516 #ifdef wxHAS_MSW_TASKDIALOG
517 if ( HasNativeProgressDialog() )
518 {
519 if ( !m_sharedData )
520 {
521 // Couldn't be skipped before being shown.
522 return false;
523 }
524
525 wxCriticalSectionLocker locker(m_sharedData->m_cs);
526 return m_sharedData->m_skipped;
527 }
528 #endif // wxHAS_MSW_TASKDIALOG
529
530 return wxGenericProgressDialog::WasSkipped();
531 }
532
533 bool wxProgressDialog::WasCancelled() const
534 {
535 #ifdef wxHAS_MSW_TASKDIALOG
536 if ( HasNativeProgressDialog() )
537 {
538 wxCriticalSectionLocker locker(m_sharedData->m_cs);
539 return m_sharedData->m_state == Canceled;
540 }
541 #endif // wxHAS_MSW_TASKDIALOG
542
543 return wxGenericProgressDialog::WasCancelled();
544 }
545
546 void wxProgressDialog::SetTitle(const wxString& title)
547 {
548 #ifdef wxHAS_MSW_TASKDIALOG
549 if ( HasNativeProgressDialog() )
550 {
551 m_title = title;
552
553 if ( m_sharedData )
554 {
555 wxCriticalSectionLocker locker(m_sharedData->m_cs);
556 m_sharedData->m_title = title;
557 m_sharedData->m_notifications = wxSPDD_TITLE_CHANGED;
558 }
559 }
560 #endif // wxHAS_MSW_TASKDIALOG
561
562 wxGenericProgressDialog::SetTitle(title);
563 }
564
565 wxString wxProgressDialog::GetTitle() const
566 {
567 #ifdef wxHAS_MSW_TASKDIALOG
568 if ( HasNativeProgressDialog() )
569 return m_title;
570 #endif // wxHAS_MSW_TASKDIALOG
571
572 return wxGenericProgressDialog::GetTitle();
573 }
574
575 bool wxProgressDialog::Show(bool show)
576 {
577 #ifdef wxHAS_MSW_TASKDIALOG
578 if ( HasNativeProgressDialog() )
579 {
580 // The dialog can't be hidden at all and showing it again after it had
581 // been shown before doesn't do anything.
582 if ( !show || m_taskDialogRunner )
583 return false;
584
585 // We're showing the dialog for the first time, create the thread that
586 // will manage it.
587 m_taskDialogRunner = new wxProgressDialogTaskRunner;
588 m_sharedData = m_taskDialogRunner->GetSharedDataObject();
589
590 // Initialize shared data.
591 m_sharedData->m_title = m_title;
592 m_sharedData->m_message = m_message;
593 m_sharedData->m_range = m_maximum;
594 m_sharedData->m_state = Uncancelable;
595 m_sharedData->m_style = m_windowStyle;
596
597 if ( HasFlag(wxPD_CAN_ABORT) )
598 {
599 m_sharedData->m_state = Continue;
600 m_sharedData->m_labelCancel = _("Cancel");
601 }
602 else if ( !HasFlag(wxPD_AUTO_HIDE) )
603 {
604 m_sharedData->m_labelCancel = _("Close");
605 }
606
607 if ( m_windowStyle & (wxPD_ELAPSED_TIME
608 | wxPD_ESTIMATED_TIME
609 | wxPD_REMAINING_TIME) )
610 {
611 // Use a non-empty string just to have the collapsible pane shown.
612 m_sharedData->m_expandedInformation = " ";
613 }
614
615 // Do launch the thread.
616 if ( m_taskDialogRunner->Create() != wxTHREAD_NO_ERROR )
617 {
618 wxLogError( "Unable to create thread!" );
619 return false;
620 }
621
622 if ( m_taskDialogRunner->Run() != wxTHREAD_NO_ERROR )
623 {
624 wxLogError( "Unable to start thread!" );
625 return false;
626 }
627
628 if ( !HasFlag(wxPD_APP_MODAL) )
629 {
630 wxWindow * const parent = GetTopParent();
631 if ( parent )
632 {
633 parent->Disable();
634 }
635 else
636 {
637 wxFAIL_MSG( "Progress dialog must have a valid parent if "
638 "wxPD_APP_MODAL is not used." );
639 }
640 }
641 //else: otherwise all windows will be disabled by m_taskDialogRunner
642
643 // Do not show the underlying dialog.
644 return false;
645 }
646 #endif // wxHAS_MSW_TASKDIALOG
647
648 return wxGenericProgressDialog::Show( show );
649 }
650
651 bool wxProgressDialog::HasNativeProgressDialog() const
652 {
653 #ifdef wxHAS_MSW_TASKDIALOG
654 // For a native implementation task dialogs are required, which
655 // also require at least one button to be present so the flags needs
656 // to be checked as well to see if this is the case.
657 return HasNativeTaskDialog()
658 && ((m_windowStyle & (wxPD_CAN_SKIP | wxPD_CAN_ABORT))
659 || !(m_windowStyle & wxPD_AUTO_HIDE));
660 #else // !wxHAS_MSW_TASKDIALOG
661 // This shouldn't be even called in !wxHAS_MSW_TASKDIALOG case but as we
662 // still must define the function as returning something, return false.
663 return false;
664 #endif // wxHAS_MSW_TASKDIALOG/!wxHAS_MSW_TASKDIALOG
665 }
666
667 void wxProgressDialog::UpdateExpandedInformation(int value)
668 {
669 #ifdef wxHAS_MSW_TASKDIALOG
670 unsigned long elapsedTime;
671 unsigned long estimatedTime;
672 unsigned long remainingTime;
673 UpdateTimeEstimates(value, elapsedTime, estimatedTime, remainingTime);
674
675 int realEstimatedTime = estimatedTime,
676 realRemainingTime = remainingTime;
677 if ( m_sharedData->m_progressBarMarquee )
678 {
679 // In indeterminate mode we don't have any estimation neither for the
680 // remaining nor for estimated time.
681 realEstimatedTime =
682 realRemainingTime = -1;
683 }
684
685 wxString expandedInformation;
686
687 // Calculate the three different timing values.
688 if ( m_windowStyle & wxPD_ELAPSED_TIME )
689 {
690 expandedInformation << GetElapsedLabel()
691 << " "
692 << GetFormattedTime(elapsedTime);
693 }
694
695 if ( m_windowStyle & wxPD_ESTIMATED_TIME )
696 {
697 if ( !expandedInformation.empty() )
698 expandedInformation += "\n";
699
700 expandedInformation << GetEstimatedLabel()
701 << " "
702 << GetFormattedTime(realEstimatedTime);
703 }
704
705 if ( m_windowStyle & wxPD_REMAINING_TIME )
706 {
707 if ( !expandedInformation.empty() )
708 expandedInformation += "\n";
709
710 expandedInformation << GetRemainingLabel()
711 << " "
712 << GetFormattedTime(realRemainingTime);
713 }
714
715 // Update with new timing information.
716 if ( expandedInformation != m_sharedData->m_expandedInformation )
717 {
718 m_sharedData->m_expandedInformation = expandedInformation;
719 m_sharedData->m_notifications |= wxSPDD_EXPINFO_CHANGED;
720 }
721 #else // !wxHAS_MSW_TASKDIALOG
722 wxUnusedVar(value);
723 #endif // wxHAS_MSW_TASKDIALOG/!wxHAS_MSW_TASKDIALOG
724 }
725
726 // ----------------------------------------------------------------------------
727 // wxProgressDialogTaskRunner and related methods
728 // ----------------------------------------------------------------------------
729
730 #ifdef wxHAS_MSW_TASKDIALOG
731
732 void* wxProgressDialogTaskRunner::Entry()
733 {
734 WinStruct<TASKDIALOGCONFIG> tdc;
735 wxMSWTaskDialogConfig wxTdc;
736
737 {
738 wxCriticalSectionLocker locker(m_sharedData.m_cs);
739
740 wxTdc.caption = m_sharedData.m_title.wx_str();
741 wxTdc.message = m_sharedData.m_message.wx_str();
742
743 wxTdc.MSWCommonTaskDialogInit( tdc );
744 tdc.pfCallback = TaskDialogCallbackProc;
745 tdc.lpCallbackData = (LONG_PTR) &m_sharedData;
746
747 // Undo some of the effects of MSWCommonTaskDialogInit().
748 tdc.dwFlags &= ~TDF_EXPAND_FOOTER_AREA; // Expand in content area.
749 tdc.dwCommonButtons = 0; // Don't use common buttons.
750
751 wxTdc.useCustomLabels = true;
752
753 if ( m_sharedData.m_style & wxPD_CAN_SKIP )
754 wxTdc.AddTaskDialogButton( tdc, Id_SkipBtn, 0, _("Skip") );
755
756 // Use a Cancel button when requested or use a Close button when
757 // the dialog does not automatically hide.
758 if ( (m_sharedData.m_style & wxPD_CAN_ABORT)
759 || !(m_sharedData.m_style & wxPD_AUTO_HIDE) )
760 {
761 wxTdc.AddTaskDialogButton( tdc, IDCANCEL, 0,
762 m_sharedData.m_labelCancel );
763 }
764
765 tdc.dwFlags |= TDF_CALLBACK_TIMER
766 | TDF_SHOW_PROGRESS_BAR
767 | TDF_SHOW_MARQUEE_PROGRESS_BAR;
768
769 if ( !m_sharedData.m_expandedInformation.empty() )
770 {
771 tdc.pszExpandedInformation =
772 m_sharedData.m_expandedInformation.wx_str();
773 }
774 }
775
776 TaskDialogIndirect_t taskDialogIndirect = GetTaskDialogIndirectFunc();
777 if ( !taskDialogIndirect )
778 return NULL;
779
780 int msAns;
781 HRESULT hr = taskDialogIndirect(&tdc, &msAns, NULL, NULL);
782 if ( FAILED(hr) )
783 wxLogApiError( "TaskDialogIndirect", hr );
784
785 return NULL;
786 }
787
788 // static
789 HRESULT CALLBACK
790 wxProgressDialogTaskRunner::TaskDialogCallbackProc
791 (
792 HWND hwnd,
793 UINT uNotification,
794 WPARAM wParam,
795 LPARAM WXUNUSED(lParam),
796 LONG_PTR dwRefData
797 )
798 {
799 wxProgressDialogSharedData * const sharedData =
800 (wxProgressDialogSharedData *) dwRefData;
801
802 wxCriticalSectionLocker locker(sharedData->m_cs);
803
804 switch ( uNotification )
805 {
806 case TDN_CREATED:
807 // Store the HWND for the main thread use.
808 sharedData->m_hwnd = hwnd;
809
810 // Set the maximum value and disable Close button.
811 ::SendMessage( hwnd,
812 TDM_SET_PROGRESS_BAR_RANGE,
813 0,
814 MAKELPARAM(0, sharedData->m_range) );
815
816 if ( UsesCloseButtonOnly(sharedData->m_style) )
817 ::SendMessage( hwnd, TDM_ENABLE_BUTTON, IDCANCEL, FALSE );
818 break;
819
820 case TDN_BUTTON_CLICKED:
821 switch ( wParam )
822 {
823 case Id_SkipBtn:
824 ::SendMessage(hwnd, TDM_ENABLE_BUTTON, Id_SkipBtn, FALSE);
825 sharedData->m_skipped = true;
826 return TRUE;
827
828 case IDCANCEL:
829 if ( sharedData->m_state
830 == wxGenericProgressDialog::Finished )
831 {
832 return FALSE;
833 }
834
835 // Close button on the window triggers an IDCANCEL press,
836 // don't allow it when it should only be possible to close
837 // a finished dialog.
838 if ( !UsesCloseButtonOnly(sharedData->m_style) )
839 {
840 wxCHECK_MSG( sharedData->m_state ==
841 wxGenericProgressDialog::Continue,
842 TRUE,
843 "Dialog not in a cancelable state!");
844
845 ::SendMessage(hwnd, TDM_ENABLE_BUTTON, Id_SkipBtn, FALSE);
846 ::SendMessage(hwnd, TDM_ENABLE_BUTTON, IDCANCEL, FALSE);
847
848 sharedData->m_timeStop = wxGetCurrentTime();
849 sharedData->m_state = wxGenericProgressDialog::Canceled;
850 }
851
852 return TRUE;
853 }
854 break;
855
856 case TDN_TIMER:
857 PerformNotificationUpdates(hwnd, sharedData);
858
859 // End dialog in three different cases:
860 // 1. Progress finished and dialog should automatically hide.
861 // 2. The wxProgressDialog object was destructed and should
862 // automatically hide.
863 // 3. The dialog was canceled and wxProgressDialog object
864 // was destroyed.
865 bool isCanceled =
866 sharedData->m_state == wxGenericProgressDialog::Canceled;
867 bool isFinished =
868 sharedData->m_state == wxGenericProgressDialog::Finished;
869 bool wasDestroyed =
870 (sharedData->m_notifications & wxSPDD_DESTROYED) != 0;
871 bool shouldAutoHide = (sharedData->m_style & wxPD_AUTO_HIDE) != 0;
872
873 if ( (shouldAutoHide && (isFinished || wasDestroyed))
874 || (wasDestroyed && isCanceled) )
875 {
876 ::EndDialog( hwnd, IDCLOSE );
877 }
878
879 sharedData->m_notifications = 0;
880
881 return TRUE;
882 }
883
884 // Return anything.
885 return 0;
886 }
887
888 #endif // wxHAS_MSW_TASKDIALOG
889
890 #endif // wxUSE_PROGRESSDLG && wxUSE_THREADS