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