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