]> git.saurik.com Git - wxWidgets.git/blame - src/msw/progdlg.cpp
Simplify and rationalize wxGDIPlusContext class constructors.
[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 }
239
240 ::SendMessage( hwnd,
241 TDM_SET_ELEMENT_TEXT,
242 TDE_MAIN_INSTRUCTION,
243 (LPARAM) title.wx_str() );
244
245 ::SendMessage( hwnd,
246 TDM_SET_ELEMENT_TEXT,
247 TDE_CONTENT,
248 (LPARAM) body.wx_str() );
249 }
250
251 if ( sharedData->m_notifications & wxSPDD_EXPINFO_CHANGED )
252 {
253 const wxString& expandedInformation =
254 sharedData->m_expandedInformation;
255 if ( !expandedInformation.empty() )
256 {
257 ::SendMessage( hwnd,
258 TDM_SET_ELEMENT_TEXT,
259 TDE_EXPANDED_INFORMATION,
260 (LPARAM) expandedInformation.wx_str() );
261 }
262 }
263
264 if ( sharedData->m_notifications & wxSPDD_ENABLE_SKIP )
265 ::SendMessage( hwnd, TDM_ENABLE_BUTTON, Id_SkipBtn, TRUE );
266
267 if ( sharedData->m_notifications & wxSPDD_ENABLE_ABORT )
268 ::SendMessage( hwnd, TDM_ENABLE_BUTTON, IDCANCEL, TRUE );
269
270 if ( sharedData->m_notifications & wxSPDD_DISABLE_SKIP )
271 ::SendMessage( hwnd, TDM_ENABLE_BUTTON, Id_SkipBtn, FALSE );
272
273 if ( sharedData->m_notifications & wxSPDD_DISABLE_ABORT )
274 ::SendMessage( hwnd, TDM_ENABLE_BUTTON, IDCANCEL, FALSE );
275
276 // Is the progress finished?
277 if ( sharedData->m_notifications & wxSPDD_FINISHED )
278 {
979901c8 279 sharedData->m_state = wxProgressDialog::Finished;
c31d9c7f
VZ
280
281 if ( !(sharedData->m_style & wxPD_AUTO_HIDE) )
282 {
283 // Change Cancel into Close and activate the button.
284 ::SendMessage( hwnd, TDM_ENABLE_BUTTON, Id_SkipBtn, FALSE );
285 ::SendMessage( hwnd, TDM_ENABLE_BUTTON, IDCANCEL, TRUE );
286 ::EnumChildWindows( hwnd, DisplayCloseButton,
287 (LPARAM) sharedData );
288 }
289 }
290}
291
292} // anonymous namespace
293
294#endif // wxHAS_MSW_TASKDIALOG
295
296// ============================================================================
297// wxProgressDialog implementation
298// ============================================================================
299
300wxProgressDialog::wxProgressDialog( const wxString& title,
301 const wxString& message,
302 int maximum,
303 wxWindow *parent,
304 int style )
2de77c6a 305 : wxGenericProgressDialog(parent, style),
c31d9c7f
VZ
306 m_taskDialogRunner(NULL),
307 m_sharedData(NULL),
308 m_message(message),
309 m_title(title)
310{
311#ifdef wxHAS_MSW_TASKDIALOG
76c4b149 312 if ( HasNativeTaskDialog() )
c31d9c7f 313 {
2de77c6a
VZ
314 SetMaximum(maximum);
315
c31d9c7f
VZ
316 Show();
317 DisableOtherWindows();
318
319 return;
320 }
321#endif // wxHAS_MSW_TASKDIALOG
322
323 Create(title, message, maximum, parent, style);
324}
325
326wxProgressDialog::~wxProgressDialog()
327{
328#ifdef wxHAS_MSW_TASKDIALOG
329 if ( !m_taskDialogRunner )
330 return;
331
332 if ( m_sharedData )
333 {
334 wxCriticalSectionLocker locker(m_sharedData->m_cs);
335 m_sharedData->m_notifications |= wxSPDD_DESTROYED;
336 }
337
338 m_taskDialogRunner->Wait();
339
340 delete m_taskDialogRunner;
341
342 ReenableOtherWindows();
343
344 if ( GetTopParent() )
345 GetTopParent()->Raise();
346#endif // wxHAS_MSW_TASKDIALOG
347}
348
349bool wxProgressDialog::Update(int value, const wxString& newmsg, bool *skip)
350{
351#ifdef wxHAS_MSW_TASKDIALOG
76c4b149 352 if ( HasNativeTaskDialog() )
c31d9c7f 353 {
cc31a982
VZ
354 {
355 wxCriticalSectionLocker locker(m_sharedData->m_cs);
c31d9c7f 356
cc31a982
VZ
357 // Do nothing in canceled state.
358 if ( !DoNativeBeforeUpdate(skip) )
359 return false;
c31d9c7f 360
cc31a982 361 value /= m_factor;
c31d9c7f 362
cc31a982 363 wxASSERT_MSG( value <= m_maximum, wxT("invalid progress value") );
c31d9c7f 364
cc31a982
VZ
365 m_sharedData->m_value = value;
366 m_sharedData->m_notifications |= wxSPDD_VALUE_CHANGED;
c31d9c7f 367
cc31a982
VZ
368 if ( !newmsg.empty() )
369 {
370 m_message = newmsg;
371 m_sharedData->m_message = newmsg;
372 m_sharedData->m_notifications |= wxSPDD_MESSAGE_CHANGED;
373 }
c31d9c7f 374
cc31a982
VZ
375 if ( m_sharedData->m_progressBarMarquee )
376 {
377 m_sharedData->m_progressBarMarquee = false;
378 m_sharedData->m_notifications |= wxSPDD_PBMARQUEE_CHANGED;
379 }
c31d9c7f 380
cc31a982
VZ
381 UpdateExpandedInformation( value );
382
383 // If we didn't just reach the finish, all we have to do is to
384 // return true if the dialog wasn't cancelled and false otherwise.
385 if ( value != m_maximum || m_state == Finished )
386 return m_sharedData->m_state != Canceled;
c31d9c7f 387
c31d9c7f 388
cc31a982
VZ
389 // On finishing, the dialog without wxPD_AUTO_HIDE style becomes a
390 // modal one meaning that we must block here until the user
391 // dismisses it.
c31d9c7f
VZ
392 m_state = Finished;
393 m_sharedData->m_state = Finished;
394 m_sharedData->m_notifications |= wxSPDD_FINISHED;
4be95bef
VZ
395 if ( HasPDFlag(wxPD_AUTO_HIDE) )
396 return true;
397
398 if ( newmsg.empty() )
c31d9c7f
VZ
399 {
400 // Provide the finishing message if the application didn't.
401 m_message = _("Done.");
402 m_sharedData->m_message = m_message;
403 m_sharedData->m_notifications |= wxSPDD_MESSAGE_CHANGED;
404 }
cc31a982
VZ
405 } // unlock m_sharedData->m_cs
406
407 // We only get here when we need to wait for the dialog to terminate so
408 // do just this by running a custom event loop until the dialog is
409 // dismissed.
410 wxProgressDialogModalLoop loop(*m_sharedData);
411 loop.Run();
412 return true;
c31d9c7f
VZ
413 }
414#endif // wxHAS_MSW_TASKDIALOG
415
416 return wxGenericProgressDialog::Update( value, newmsg, skip );
417}
418
419bool wxProgressDialog::Pulse(const wxString& newmsg, bool *skip)
420{
421#ifdef wxHAS_MSW_TASKDIALOG
76c4b149 422 if ( HasNativeTaskDialog() )
c31d9c7f
VZ
423 {
424 wxCriticalSectionLocker locker(m_sharedData->m_cs);
425
426 // Do nothing in canceled state.
427 if ( !DoNativeBeforeUpdate(skip) )
428 return false;
429
430 if ( !m_sharedData->m_progressBarMarquee )
431 {
432 m_sharedData->m_progressBarMarquee = true;
433 m_sharedData->m_notifications |= wxSPDD_PBMARQUEE_CHANGED;
434 }
435
436 if ( !newmsg.empty() )
437 {
438 m_message = newmsg;
439 m_sharedData->m_message = newmsg;
440 m_sharedData->m_notifications |= wxSPDD_MESSAGE_CHANGED;
441 }
442
443 // The value passed here doesn't matter, only elapsed time makes sense
444 // in indeterminate mode anyhow.
445 UpdateExpandedInformation(0);
446
447 return m_sharedData->m_state != Canceled;
448 }
449#endif // wxHAS_MSW_TASKDIALOG
450
451 return wxGenericProgressDialog::Pulse( newmsg, skip );
452}
453
454bool wxProgressDialog::DoNativeBeforeUpdate(bool *skip)
455{
456#ifdef wxHAS_MSW_TASKDIALOG
76c4b149 457 if ( HasNativeTaskDialog() )
c31d9c7f
VZ
458 {
459 if ( m_sharedData->m_skipped )
460 {
461 if ( skip && !*skip )
462 {
463 *skip = true;
464 m_sharedData->m_skipped = false;
465 m_sharedData->m_notifications |= wxSPDD_ENABLE_SKIP;
466 }
467 }
468
469 if ( m_sharedData->m_state == Canceled )
470 m_timeStop = m_sharedData->m_timeStop;
471
472 return m_sharedData->m_state != Canceled;
473 }
474#endif // wxHAS_MSW_TASKDIALOG
475
476 wxUnusedVar(skip);
477 wxFAIL_MSG( "unreachable" );
478
479 return false;
480}
481
482void wxProgressDialog::Resume()
483{
484 wxGenericProgressDialog::Resume();
485
486#ifdef wxHAS_MSW_TASKDIALOG
76c4b149 487 if ( HasNativeTaskDialog() )
c31d9c7f
VZ
488 {
489 HWND hwnd;
490
491 {
492 wxCriticalSectionLocker locker(m_sharedData->m_cs);
493 m_sharedData->m_state = m_state;
494
495 // "Skip" was disabled when "Cancel" had been clicked, so re-enable
496 // it now.
497 m_sharedData->m_notifications |= wxSPDD_ENABLE_SKIP;
498
76c4b149
VZ
499 // Also re-enable "Cancel" itself
500 if ( HasPDFlag(wxPD_CAN_ABORT) )
c31d9c7f
VZ
501 m_sharedData->m_notifications |= wxSPDD_ENABLE_ABORT;
502
503 hwnd = m_sharedData->m_hwnd;
504 } // Unlock m_cs, we can't call any function operating on a dialog with
505 // it locked as it can result in a deadlock if the dialog callback is
506 // called by Windows.
507
508 // After resuming we need to bring the window on top of the Z-order as
509 // it could be hidden by another window shown from the main thread,
510 // e.g. a confirmation dialog asking whether the user really wants to
511 // abort.
512 //
513 // Notice that this must be done from the main thread as it owns the
514 // currently active window and attempts to do this from the task dialog
515 // thread would simply fail.
516 ::BringWindowToTop(hwnd);
517 }
518#endif // wxHAS_MSW_TASKDIALOG
519}
520
01bd848e
RD
521WXWidget wxProgressDialog::GetHandle() const
522{
523#ifdef wxHAS_MSW_TASKDIALOG
524 if ( HasNativeTaskDialog() )
525 {
526 HWND hwnd;
527 {
528 wxCriticalSectionLocker locker(m_sharedData->m_cs);
529 m_sharedData->m_state = m_state;
530 hwnd = m_sharedData->m_hwnd;
531 }
532 return hwnd;
533 }
534#endif
535 return wxGenericProgressDialog::GetHandle();
536}
537
c31d9c7f
VZ
538int wxProgressDialog::GetValue() const
539{
540#ifdef wxHAS_MSW_TASKDIALOG
76c4b149 541 if ( HasNativeTaskDialog() )
c31d9c7f
VZ
542 {
543 wxCriticalSectionLocker locker(m_sharedData->m_cs);
544 return m_sharedData->m_value;
545 }
546#endif // wxHAS_MSW_TASKDIALOG
547
548 return wxGenericProgressDialog::GetValue();
549}
550
551wxString wxProgressDialog::GetMessage() const
552{
553#ifdef wxHAS_MSW_TASKDIALOG
76c4b149 554 if ( HasNativeTaskDialog() )
c31d9c7f
VZ
555 return m_message;
556#endif // wxHAS_MSW_TASKDIALOG
557
558 return wxGenericProgressDialog::GetMessage();
559}
560
561void wxProgressDialog::SetRange(int maximum)
562{
c31d9c7f 563#ifdef wxHAS_MSW_TASKDIALOG
76c4b149 564 if ( HasNativeTaskDialog() )
c31d9c7f 565 {
2de77c6a
VZ
566 SetMaximum(maximum);
567
c31d9c7f
VZ
568 wxCriticalSectionLocker locker(m_sharedData->m_cs);
569
570 m_sharedData->m_range = maximum;
571 m_sharedData->m_notifications |= wxSPDD_RANGE_CHANGED;
2de77c6a
VZ
572
573 return;
c31d9c7f
VZ
574 }
575#endif // wxHAS_MSW_TASKDIALOG
2de77c6a
VZ
576
577 wxGenericProgressDialog::SetRange( maximum );
c31d9c7f
VZ
578}
579
580bool wxProgressDialog::WasSkipped() const
581{
582#ifdef wxHAS_MSW_TASKDIALOG
76c4b149 583 if ( HasNativeTaskDialog() )
c31d9c7f
VZ
584 {
585 if ( !m_sharedData )
586 {
587 // Couldn't be skipped before being shown.
588 return false;
589 }
590
591 wxCriticalSectionLocker locker(m_sharedData->m_cs);
592 return m_sharedData->m_skipped;
593 }
594#endif // wxHAS_MSW_TASKDIALOG
595
596 return wxGenericProgressDialog::WasSkipped();
597}
598
599bool wxProgressDialog::WasCancelled() const
600{
601#ifdef wxHAS_MSW_TASKDIALOG
76c4b149 602 if ( HasNativeTaskDialog() )
c31d9c7f
VZ
603 {
604 wxCriticalSectionLocker locker(m_sharedData->m_cs);
605 return m_sharedData->m_state == Canceled;
606 }
607#endif // wxHAS_MSW_TASKDIALOG
608
609 return wxGenericProgressDialog::WasCancelled();
610}
611
612void wxProgressDialog::SetTitle(const wxString& title)
613{
614#ifdef wxHAS_MSW_TASKDIALOG
76c4b149 615 if ( HasNativeTaskDialog() )
c31d9c7f
VZ
616 {
617 m_title = title;
618
619 if ( m_sharedData )
620 {
621 wxCriticalSectionLocker locker(m_sharedData->m_cs);
622 m_sharedData->m_title = title;
623 m_sharedData->m_notifications = wxSPDD_TITLE_CHANGED;
624 }
625 }
626#endif // wxHAS_MSW_TASKDIALOG
627
b00403b4 628 wxGenericProgressDialog::SetTitle(title);
c31d9c7f
VZ
629}
630
631wxString wxProgressDialog::GetTitle() const
632{
633#ifdef wxHAS_MSW_TASKDIALOG
76c4b149 634 if ( HasNativeTaskDialog() )
c31d9c7f
VZ
635 return m_title;
636#endif // wxHAS_MSW_TASKDIALOG
637
638 return wxGenericProgressDialog::GetTitle();
639}
640
641bool wxProgressDialog::Show(bool show)
642{
643#ifdef wxHAS_MSW_TASKDIALOG
76c4b149 644 if ( HasNativeTaskDialog() )
c31d9c7f
VZ
645 {
646 // The dialog can't be hidden at all and showing it again after it had
647 // been shown before doesn't do anything.
648 if ( !show || m_taskDialogRunner )
649 return false;
650
651 // We're showing the dialog for the first time, create the thread that
652 // will manage it.
8297e64b 653 m_taskDialogRunner = new wxProgressDialogTaskRunner;
c31d9c7f
VZ
654 m_sharedData = m_taskDialogRunner->GetSharedDataObject();
655
656 // Initialize shared data.
657 m_sharedData->m_title = m_title;
658 m_sharedData->m_message = m_message;
659 m_sharedData->m_range = m_maximum;
660 m_sharedData->m_state = Uncancelable;
e77570de 661 m_sharedData->m_style = GetPDStyle();
a4984245 662 m_sharedData->m_parent = GetTopParent();
c31d9c7f 663
e77570de 664 if ( HasPDFlag(wxPD_CAN_ABORT) )
c31d9c7f
VZ
665 {
666 m_sharedData->m_state = Continue;
667 m_sharedData->m_labelCancel = _("Cancel");
668 }
76c4b149 669 else // Dialog can't be cancelled.
c31d9c7f 670 {
76c4b149
VZ
671 // We still must have at least a single button in the dialog so
672 // just don't call it "Cancel" in this case.
c31d9c7f
VZ
673 m_sharedData->m_labelCancel = _("Close");
674 }
675
e77570de
VZ
676 if ( HasPDFlag(wxPD_ELAPSED_TIME |
677 wxPD_ESTIMATED_TIME |
678 wxPD_REMAINING_TIME) )
c31d9c7f
VZ
679 {
680 // Use a non-empty string just to have the collapsible pane shown.
681 m_sharedData->m_expandedInformation = " ";
682 }
683
684 // Do launch the thread.
685 if ( m_taskDialogRunner->Create() != wxTHREAD_NO_ERROR )
686 {
687 wxLogError( "Unable to create thread!" );
688 return false;
689 }
690
691 if ( m_taskDialogRunner->Run() != wxTHREAD_NO_ERROR )
692 {
693 wxLogError( "Unable to start thread!" );
694 return false;
695 }
696
c31d9c7f
VZ
697 // Do not show the underlying dialog.
698 return false;
699 }
700#endif // wxHAS_MSW_TASKDIALOG
701
702 return wxGenericProgressDialog::Show( show );
703}
704
c31d9c7f
VZ
705void wxProgressDialog::UpdateExpandedInformation(int value)
706{
707#ifdef wxHAS_MSW_TASKDIALOG
708 unsigned long elapsedTime;
709 unsigned long estimatedTime;
710 unsigned long remainingTime;
711 UpdateTimeEstimates(value, elapsedTime, estimatedTime, remainingTime);
712
713 int realEstimatedTime = estimatedTime,
714 realRemainingTime = remainingTime;
715 if ( m_sharedData->m_progressBarMarquee )
716 {
717 // In indeterminate mode we don't have any estimation neither for the
718 // remaining nor for estimated time.
719 realEstimatedTime =
720 realRemainingTime = -1;
721 }
722
723 wxString expandedInformation;
724
725 // Calculate the three different timing values.
e77570de 726 if ( HasPDFlag(wxPD_ELAPSED_TIME) )
c31d9c7f
VZ
727 {
728 expandedInformation << GetElapsedLabel()
729 << " "
730 << GetFormattedTime(elapsedTime);
731 }
732
e77570de 733 if ( HasPDFlag(wxPD_ESTIMATED_TIME) )
c31d9c7f
VZ
734 {
735 if ( !expandedInformation.empty() )
736 expandedInformation += "\n";
737
738 expandedInformation << GetEstimatedLabel()
739 << " "
740 << GetFormattedTime(realEstimatedTime);
741 }
742
e77570de 743 if ( HasPDFlag(wxPD_REMAINING_TIME) )
c31d9c7f
VZ
744 {
745 if ( !expandedInformation.empty() )
746 expandedInformation += "\n";
747
748 expandedInformation << GetRemainingLabel()
749 << " "
750 << GetFormattedTime(realRemainingTime);
751 }
752
753 // Update with new timing information.
754 if ( expandedInformation != m_sharedData->m_expandedInformation )
755 {
756 m_sharedData->m_expandedInformation = expandedInformation;
757 m_sharedData->m_notifications |= wxSPDD_EXPINFO_CHANGED;
758 }
759#else // !wxHAS_MSW_TASKDIALOG
760 wxUnusedVar(value);
761#endif // wxHAS_MSW_TASKDIALOG/!wxHAS_MSW_TASKDIALOG
762}
763
764// ----------------------------------------------------------------------------
765// wxProgressDialogTaskRunner and related methods
766// ----------------------------------------------------------------------------
767
768#ifdef wxHAS_MSW_TASKDIALOG
769
770void* wxProgressDialogTaskRunner::Entry()
771{
772 WinStruct<TASKDIALOGCONFIG> tdc;
773 wxMSWTaskDialogConfig wxTdc;
774
775 {
776 wxCriticalSectionLocker locker(m_sharedData.m_cs);
777
778 wxTdc.caption = m_sharedData.m_title.wx_str();
779 wxTdc.message = m_sharedData.m_message.wx_str();
780
04a00346
VZ
781 // MSWCommonTaskDialogInit() will add an IDCANCEL button but we need to
782 // give it the correct label.
783 wxTdc.btnOKLabel = m_sharedData.m_labelCancel;
784 wxTdc.useCustomLabels = true;
785
c31d9c7f
VZ
786 wxTdc.MSWCommonTaskDialogInit( tdc );
787 tdc.pfCallback = TaskDialogCallbackProc;
788 tdc.lpCallbackData = (LONG_PTR) &m_sharedData;
789
790 // Undo some of the effects of MSWCommonTaskDialogInit().
791 tdc.dwFlags &= ~TDF_EXPAND_FOOTER_AREA; // Expand in content area.
792 tdc.dwCommonButtons = 0; // Don't use common buttons.
793
c31d9c7f
VZ
794 if ( m_sharedData.m_style & wxPD_CAN_SKIP )
795 wxTdc.AddTaskDialogButton( tdc, Id_SkipBtn, 0, _("Skip") );
796
928da15b 797 tdc.dwFlags |= TDF_CALLBACK_TIMER | TDF_SHOW_PROGRESS_BAR;
c31d9c7f
VZ
798
799 if ( !m_sharedData.m_expandedInformation.empty() )
800 {
801 tdc.pszExpandedInformation =
802 m_sharedData.m_expandedInformation.wx_str();
803 }
804 }
805
806 TaskDialogIndirect_t taskDialogIndirect = GetTaskDialogIndirectFunc();
807 if ( !taskDialogIndirect )
808 return NULL;
809
810 int msAns;
811 HRESULT hr = taskDialogIndirect(&tdc, &msAns, NULL, NULL);
812 if ( FAILED(hr) )
813 wxLogApiError( "TaskDialogIndirect", hr );
814
cc31a982
VZ
815 // If the main thread is waiting for us to exit inside the event loop in
816 // Update(), wake it up so that it checks our status again.
817 wxWakeUpIdle();
818
c31d9c7f
VZ
819 return NULL;
820}
821
822// static
823HRESULT CALLBACK
824wxProgressDialogTaskRunner::TaskDialogCallbackProc
825 (
826 HWND hwnd,
827 UINT uNotification,
828 WPARAM wParam,
829 LPARAM WXUNUSED(lParam),
830 LONG_PTR dwRefData
831 )
832{
833 wxProgressDialogSharedData * const sharedData =
834 (wxProgressDialogSharedData *) dwRefData;
835
836 wxCriticalSectionLocker locker(sharedData->m_cs);
837
838 switch ( uNotification )
839 {
840 case TDN_CREATED:
841 // Store the HWND for the main thread use.
842 sharedData->m_hwnd = hwnd;
843
844 // Set the maximum value and disable Close button.
845 ::SendMessage( hwnd,
846 TDM_SET_PROGRESS_BAR_RANGE,
847 0,
848 MAKELPARAM(0, sharedData->m_range) );
849
a4984245
VZ
850 // We always create this task dialog with NULL parent because our
851 // parent in wx sense is a window created from a different thread
852 // and so can't be used as our real parent. However we still center
853 // this window on the parent one as the task dialogs do with their
854 // real parent usually.
855 if ( sharedData->m_parent )
856 {
857 wxRect rect(wxRectFromRECT(wxGetWindowRect(hwnd)));
858 rect = rect.CentreIn(sharedData->m_parent->GetRect());
859 ::SetWindowPos(hwnd,
860 NULL,
861 rect.x,
862 rect.y,
863 -1,
864 -1,
865 SWP_NOACTIVATE |
866 SWP_NOOWNERZORDER |
867 SWP_NOSIZE |
868 SWP_NOZORDER);
869 }
870
76c4b149
VZ
871 // If we can't be aborted, the "Close" button will only be enabled
872 // when the progress ends (and not even then with wxPD_AUTO_HIDE).
873 if ( !(sharedData->m_style & wxPD_CAN_ABORT) )
c31d9c7f
VZ
874 ::SendMessage( hwnd, TDM_ENABLE_BUTTON, IDCANCEL, FALSE );
875 break;
876
877 case TDN_BUTTON_CLICKED:
878 switch ( wParam )
879 {
880 case Id_SkipBtn:
881 ::SendMessage(hwnd, TDM_ENABLE_BUTTON, Id_SkipBtn, FALSE);
882 sharedData->m_skipped = true;
883 return TRUE;
884
885 case IDCANCEL:
979901c8 886 if ( sharedData->m_state == wxProgressDialog::Finished )
c31d9c7f 887 {
cc31a982
VZ
888 // If the main thread is waiting for us, tell it that
889 // we're gone (and if it doesn't wait, it's harmless).
890 sharedData->m_state = wxProgressDialog::Dismissed;
891
892 // Let Windows close the dialog.
c31d9c7f
VZ
893 return FALSE;
894 }
895
896 // Close button on the window triggers an IDCANCEL press,
897 // don't allow it when it should only be possible to close
898 // a finished dialog.
76c4b149 899 if ( sharedData->m_style & wxPD_CAN_ABORT )
c31d9c7f 900 {
979901c8
VZ
901 wxCHECK_MSG
902 (
903 sharedData->m_state == wxProgressDialog::Continue,
904 TRUE,
905 "Dialog not in a cancelable state!"
906 );
c31d9c7f
VZ
907
908 ::SendMessage(hwnd, TDM_ENABLE_BUTTON, Id_SkipBtn, FALSE);
909 ::SendMessage(hwnd, TDM_ENABLE_BUTTON, IDCANCEL, FALSE);
910
911 sharedData->m_timeStop = wxGetCurrentTime();
979901c8 912 sharedData->m_state = wxProgressDialog::Canceled;
c31d9c7f
VZ
913 }
914
915 return TRUE;
916 }
917 break;
918
919 case TDN_TIMER:
920 PerformNotificationUpdates(hwnd, sharedData);
921
21a5e7e0
VZ
922 /*
923 Decide whether we should end the dialog. This is done if either
924 the dialog object itself was destroyed or if the progress
925 finished and we were configured to hide automatically without
926 waiting for the user to dismiss us.
927
928 Notice that we do not close the dialog if it was cancelled
929 because it's up to the user code in the main thread to decide
930 whether it really wants to cancel the dialog.
931 */
932 if ( (sharedData->m_notifications & wxSPDD_DESTROYED) ||
933 (sharedData->m_state == wxProgressDialog::Finished &&
934 sharedData->m_style & wxPD_AUTO_HIDE) )
c31d9c7f
VZ
935 {
936 ::EndDialog( hwnd, IDCLOSE );
937 }
938
939 sharedData->m_notifications = 0;
940
941 return TRUE;
942 }
943
944 // Return anything.
945 return 0;
946}
947
948#endif // wxHAS_MSW_TASKDIALOG
949
950#endif // wxUSE_PROGRESSDLG && wxUSE_THREADS