]> git.saurik.com Git - wxWidgets.git/blame - src/msw/progdlg.cpp
Remove the unused "monolithic" MSW wxMediaCtrl file.
[wxWidgets.git] / src / msw / progdlg.cpp
CommitLineData
c31d9c7f
VZ
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
31using namespace wxMSWMessageDialog;
32
33#ifdef wxHAS_MSW_TASKDIALOG
34
35// ----------------------------------------------------------------------------
36// Constants
37// ----------------------------------------------------------------------------
38
39namespace
40{
41
42// Notification values of wxProgressDialogSharedData::m_notifications
43const int wxSPDD_VALUE_CHANGED = 0x0001;
44const int wxSPDD_RANGE_CHANGED = 0x0002;
45const int wxSPDD_PBMARQUEE_CHANGED = 0x0004;
46const int wxSPDD_TITLE_CHANGED = 0x0008;
47const int wxSPDD_MESSAGE_CHANGED = 0x0010;
48const int wxSPDD_EXPINFO_CHANGED = 0x0020;
49const int wxSPDD_ENABLE_SKIP = 0x0040;
50const int wxSPDD_ENABLE_ABORT = 0x0080;
51const int wxSPDD_DISABLE_SKIP = 0x0100;
52const int wxSPDD_DISABLE_ABORT = 0x0200;
53const int wxSPDD_FINISHED = 0x0400;
54const int wxSPDD_DESTROYED = 0x0800;
55
56const 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.
65class wxProgressDialogSharedData
66{
67public:
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.
100class wxProgressDialogTaskRunner : public wxThread
101{
102public:
8297e64b
VZ
103 wxProgressDialogTaskRunner()
104 : wxThread(wxTHREAD_JOINABLE)
c31d9c7f
VZ
105 { }
106
107 wxProgressDialogSharedData* GetSharedDataObject()
108 { return &m_sharedData; }
109
110private:
c31d9c7f
VZ
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
126namespace
127{
128
129bool UsesCloseButtonOnly(long style)
130{
131 return !((style & wxPD_CAN_ABORT) || (style & wxPD_AUTO_HIDE));
132}
133
134BOOL 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
151void 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
270wxProgressDialog::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
294wxProgressDialog::~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
317bool 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
375bool 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
410bool 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
438void 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
476int 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
489wxString 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
499void 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
514bool 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
533bool 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
546void 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
b00403b4 562 wxGenericProgressDialog::SetTitle(title);
c31d9c7f
VZ
563}
564
565wxString 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
575bool 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.
8297e64b 587 m_taskDialogRunner = new wxProgressDialogTaskRunner;
c31d9c7f
VZ
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) )
a5655d37
VZ
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 }
c31d9c7f
VZ
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
651bool 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
667void 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
732void* 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
789HRESULT CALLBACK
790wxProgressDialogTaskRunner::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