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