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