Files
wxWidgets/src/msw/progdlg.cpp
Vadim Zeitlin 76c4b1491e Always use native task dialog for wxProgressDialog under MSW if supported.
Use task dialogs even for the dialogs with wxPD_AUTO_HIDE style flag and
without wxPD_CAN_ABORT one. Generic fallback was used in this case as native
task dialog doesn't support dialogs without buttons but it is finally better
to create a dummy button and use the native dialog nevertheless. We already
have a mostly disabled "Close" button for the dialogs without wxPD_AUTO_HIDE
style so it seems logical to also have it (but just never enable it at all)
when this style is used.

Closes #12462.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@65574 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
2010-09-20 13:11:03 +00:00

904 lines
27 KiB
C++

/////////////////////////////////////////////////////////////////////////////
// Name: src/msw/progdlg.cpp
// Purpose: wxProgressDialog
// Author: Rickard Westerlund
// Created: 2010-07-22
// RCS-ID: $Id$
// Copyright: (c) 2010 wxWidgets team
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
// ============================================================================
// Declarations
// ============================================================================
// ----------------------------------------------------------------------------
// Headers
// ----------------------------------------------------------------------------
// For compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
#if wxUSE_PROGRESSDLG && wxUSE_THREADS
#include "wx/msw/private/msgdlg.h"
#include "wx/progdlg.h"
#include "wx/evtloop.h"
using namespace wxMSWMessageDialog;
#ifdef wxHAS_MSW_TASKDIALOG
// ----------------------------------------------------------------------------
// Constants
// ----------------------------------------------------------------------------
namespace
{
// Notification values of wxProgressDialogSharedData::m_notifications
const int wxSPDD_VALUE_CHANGED = 0x0001;
const int wxSPDD_RANGE_CHANGED = 0x0002;
const int wxSPDD_PBMARQUEE_CHANGED = 0x0004;
const int wxSPDD_TITLE_CHANGED = 0x0008;
const int wxSPDD_MESSAGE_CHANGED = 0x0010;
const int wxSPDD_EXPINFO_CHANGED = 0x0020;
const int wxSPDD_ENABLE_SKIP = 0x0040;
const int wxSPDD_ENABLE_ABORT = 0x0080;
const int wxSPDD_DISABLE_SKIP = 0x0100;
const int wxSPDD_DISABLE_ABORT = 0x0200;
const int wxSPDD_FINISHED = 0x0400;
const int wxSPDD_DESTROYED = 0x0800;
const int Id_SkipBtn = wxID_HIGHEST + 1;
} // anonymous namespace
// ============================================================================
// Helper classes
// ============================================================================
// Class used to share data between the main thread and the task dialog runner.
class wxProgressDialogSharedData
{
public:
wxProgressDialogSharedData()
{
m_hwnd = 0;
m_value = 0;
m_progressBarMarquee = false;
m_skipped = false;
m_notifications = 0;
}
wxCriticalSection m_cs;
HWND m_hwnd; // Task dialog handler
long m_style; // wxProgressDialog style
int m_value;
int m_range;
wxString m_title;
wxString m_message;
wxString m_expandedInformation;
wxString m_labelCancel; // Privately used by callback.
unsigned long m_timeStop;
wxProgressDialog::State m_state;
bool m_progressBarMarquee;
bool m_skipped;
// Bit field that indicates fields that have been modified by the
// main thread so the task dialog runner knows what to update.
int m_notifications;
};
// Runner thread that takes care of displaying and updating the
// task dialog.
class wxProgressDialogTaskRunner : public wxThread
{
public:
wxProgressDialogTaskRunner()
: wxThread(wxTHREAD_JOINABLE)
{ }
wxProgressDialogSharedData* GetSharedDataObject()
{ return &m_sharedData; }
private:
wxProgressDialogSharedData m_sharedData;
virtual void* Entry();
static HRESULT CALLBACK TaskDialogCallbackProc(HWND hwnd,
UINT uNotification,
WPARAM wParam,
LPARAM lParam,
LONG_PTR dwRefData);
};
namespace
{
// A custom event loop which runs until the state of the dialog becomes
// "Dismissed".
class wxProgressDialogModalLoop : public wxEventLoop
{
public:
wxProgressDialogModalLoop(wxProgressDialogSharedData& data)
: m_data(data)
{
}
protected:
virtual void OnNextIteration()
{
wxCriticalSectionLocker locker(m_data.m_cs);
if ( m_data.m_state == wxProgressDialog::Dismissed )
Exit();
}
wxProgressDialogSharedData& m_data;
wxDECLARE_NO_COPY_CLASS(wxProgressDialogModalLoop);
};
// ============================================================================
// Helper functions
// ============================================================================
BOOL CALLBACK DisplayCloseButton(HWND hwnd, LPARAM lParam)
{
wxProgressDialogSharedData *sharedData =
(wxProgressDialogSharedData *) lParam;
if ( wxGetWindowText( hwnd ) == sharedData->m_labelCancel )
{
sharedData->m_labelCancel = _("Close");
SendMessage( hwnd, WM_SETTEXT, 0,
(LPARAM) sharedData->m_labelCancel.wx_str() );
return FALSE;
}
return TRUE;
}
void PerformNotificationUpdates(HWND hwnd,
wxProgressDialogSharedData *sharedData)
{
// Update the appropriate dialog fields.
if ( sharedData->m_notifications & wxSPDD_RANGE_CHANGED )
{
::SendMessage( hwnd,
TDM_SET_PROGRESS_BAR_RANGE,
0,
MAKELPARAM(0, sharedData->m_range) );
}
if ( sharedData->m_notifications & wxSPDD_VALUE_CHANGED )
{
::SendMessage( hwnd,
TDM_SET_PROGRESS_BAR_POS,
sharedData->m_value,
0 );
}
if ( sharedData->m_notifications & wxSPDD_PBMARQUEE_CHANGED )
{
BOOL val = sharedData->m_progressBarMarquee ? TRUE : FALSE;
::SendMessage( hwnd,
TDM_SET_MARQUEE_PROGRESS_BAR,
val,
0 );
::SendMessage( hwnd,
TDM_SET_PROGRESS_BAR_MARQUEE,
val,
0 );
}
if ( sharedData->m_notifications & wxSPDD_TITLE_CHANGED )
::SetWindowText( hwnd, sharedData->m_title.wx_str() );
if ( sharedData->m_notifications & wxSPDD_MESSAGE_CHANGED )
{
// Split the message in the title string and the rest if it has
// multiple lines.
wxString
title = sharedData->m_message,
body;
const size_t posNL = title.find('\n');
if ( posNL != wxString::npos )
{
// There can an extra new line between the first and subsequent
// lines to separate them as it looks better with the generic
// version -- but in this one, they're already separated by the use
// of different dialog elements, so suppress the extra new line.
int numNLs = 1;
if ( posNL < title.length() - 1 && title[posNL + 1] == '\n' )
numNLs++;
body.assign(title, posNL + numNLs, wxString::npos);
title.erase(posNL);
}
::SendMessage( hwnd,
TDM_SET_ELEMENT_TEXT,
TDE_MAIN_INSTRUCTION,
(LPARAM) title.wx_str() );
::SendMessage( hwnd,
TDM_SET_ELEMENT_TEXT,
TDE_CONTENT,
(LPARAM) body.wx_str() );
}
if ( sharedData->m_notifications & wxSPDD_EXPINFO_CHANGED )
{
const wxString& expandedInformation =
sharedData->m_expandedInformation;
if ( !expandedInformation.empty() )
{
::SendMessage( hwnd,
TDM_SET_ELEMENT_TEXT,
TDE_EXPANDED_INFORMATION,
(LPARAM) expandedInformation.wx_str() );
}
}
if ( sharedData->m_notifications & wxSPDD_ENABLE_SKIP )
::SendMessage( hwnd, TDM_ENABLE_BUTTON, Id_SkipBtn, TRUE );
if ( sharedData->m_notifications & wxSPDD_ENABLE_ABORT )
::SendMessage( hwnd, TDM_ENABLE_BUTTON, IDCANCEL, TRUE );
if ( sharedData->m_notifications & wxSPDD_DISABLE_SKIP )
::SendMessage( hwnd, TDM_ENABLE_BUTTON, Id_SkipBtn, FALSE );
if ( sharedData->m_notifications & wxSPDD_DISABLE_ABORT )
::SendMessage( hwnd, TDM_ENABLE_BUTTON, IDCANCEL, FALSE );
// Is the progress finished?
if ( sharedData->m_notifications & wxSPDD_FINISHED )
{
sharedData->m_state = wxProgressDialog::Finished;
if ( !(sharedData->m_style & wxPD_AUTO_HIDE) )
{
// Change Cancel into Close and activate the button.
::SendMessage( hwnd, TDM_ENABLE_BUTTON, Id_SkipBtn, FALSE );
::SendMessage( hwnd, TDM_ENABLE_BUTTON, IDCANCEL, TRUE );
::EnumChildWindows( hwnd, DisplayCloseButton,
(LPARAM) sharedData );
}
}
}
} // anonymous namespace
#endif // wxHAS_MSW_TASKDIALOG
// ============================================================================
// wxProgressDialog implementation
// ============================================================================
wxProgressDialog::wxProgressDialog( const wxString& title,
const wxString& message,
int maximum,
wxWindow *parent,
int style )
: wxGenericProgressDialog(parent, style),
m_taskDialogRunner(NULL),
m_sharedData(NULL),
m_message(message),
m_title(title)
{
#ifdef wxHAS_MSW_TASKDIALOG
if ( HasNativeTaskDialog() )
{
SetMaximum(maximum);
Show();
DisableOtherWindows();
return;
}
#endif // wxHAS_MSW_TASKDIALOG
Create(title, message, maximum, parent, style);
}
wxProgressDialog::~wxProgressDialog()
{
#ifdef wxHAS_MSW_TASKDIALOG
if ( !m_taskDialogRunner )
return;
if ( m_sharedData )
{
wxCriticalSectionLocker locker(m_sharedData->m_cs);
m_sharedData->m_notifications |= wxSPDD_DESTROYED;
}
m_taskDialogRunner->Wait();
delete m_taskDialogRunner;
ReenableOtherWindows();
if ( GetTopParent() )
GetTopParent()->Raise();
#endif // wxHAS_MSW_TASKDIALOG
}
bool wxProgressDialog::Update(int value, const wxString& newmsg, bool *skip)
{
#ifdef wxHAS_MSW_TASKDIALOG
if ( HasNativeTaskDialog() )
{
{
wxCriticalSectionLocker locker(m_sharedData->m_cs);
// Do nothing in canceled state.
if ( !DoNativeBeforeUpdate(skip) )
return false;
value /= m_factor;
wxASSERT_MSG( value <= m_maximum, wxT("invalid progress value") );
m_sharedData->m_value = value;
m_sharedData->m_notifications |= wxSPDD_VALUE_CHANGED;
if ( !newmsg.empty() )
{
m_message = newmsg;
m_sharedData->m_message = newmsg;
m_sharedData->m_notifications |= wxSPDD_MESSAGE_CHANGED;
}
if ( m_sharedData->m_progressBarMarquee )
{
m_sharedData->m_progressBarMarquee = false;
m_sharedData->m_notifications |= wxSPDD_PBMARQUEE_CHANGED;
}
UpdateExpandedInformation( value );
// If we didn't just reach the finish, all we have to do is to
// return true if the dialog wasn't cancelled and false otherwise.
if ( value != m_maximum || m_state == Finished )
return m_sharedData->m_state != Canceled;
// On finishing, the dialog without wxPD_AUTO_HIDE style becomes a
// modal one meaning that we must block here until the user
// dismisses it.
m_state = Finished;
m_sharedData->m_state = Finished;
m_sharedData->m_notifications |= wxSPDD_FINISHED;
if ( HasPDFlag(wxPD_AUTO_HIDE) )
return true;
if ( newmsg.empty() )
{
// Provide the finishing message if the application didn't.
m_message = _("Done.");
m_sharedData->m_message = m_message;
m_sharedData->m_notifications |= wxSPDD_MESSAGE_CHANGED;
}
} // unlock m_sharedData->m_cs
// We only get here when we need to wait for the dialog to terminate so
// do just this by running a custom event loop until the dialog is
// dismissed.
wxProgressDialogModalLoop loop(*m_sharedData);
loop.Run();
return true;
}
#endif // wxHAS_MSW_TASKDIALOG
return wxGenericProgressDialog::Update( value, newmsg, skip );
}
bool wxProgressDialog::Pulse(const wxString& newmsg, bool *skip)
{
#ifdef wxHAS_MSW_TASKDIALOG
if ( HasNativeTaskDialog() )
{
wxCriticalSectionLocker locker(m_sharedData->m_cs);
// Do nothing in canceled state.
if ( !DoNativeBeforeUpdate(skip) )
return false;
if ( !m_sharedData->m_progressBarMarquee )
{
m_sharedData->m_progressBarMarquee = true;
m_sharedData->m_notifications |= wxSPDD_PBMARQUEE_CHANGED;
}
if ( !newmsg.empty() )
{
m_message = newmsg;
m_sharedData->m_message = newmsg;
m_sharedData->m_notifications |= wxSPDD_MESSAGE_CHANGED;
}
// The value passed here doesn't matter, only elapsed time makes sense
// in indeterminate mode anyhow.
UpdateExpandedInformation(0);
return m_sharedData->m_state != Canceled;
}
#endif // wxHAS_MSW_TASKDIALOG
return wxGenericProgressDialog::Pulse( newmsg, skip );
}
bool wxProgressDialog::DoNativeBeforeUpdate(bool *skip)
{
#ifdef wxHAS_MSW_TASKDIALOG
if ( HasNativeTaskDialog() )
{
if ( m_sharedData->m_skipped )
{
if ( skip && !*skip )
{
*skip = true;
m_sharedData->m_skipped = false;
m_sharedData->m_notifications |= wxSPDD_ENABLE_SKIP;
}
}
if ( m_sharedData->m_state == Canceled )
m_timeStop = m_sharedData->m_timeStop;
return m_sharedData->m_state != Canceled;
}
#endif // wxHAS_MSW_TASKDIALOG
wxUnusedVar(skip);
wxFAIL_MSG( "unreachable" );
return false;
}
void wxProgressDialog::Resume()
{
wxGenericProgressDialog::Resume();
#ifdef wxHAS_MSW_TASKDIALOG
if ( HasNativeTaskDialog() )
{
HWND hwnd;
{
wxCriticalSectionLocker locker(m_sharedData->m_cs);
m_sharedData->m_state = m_state;
// "Skip" was disabled when "Cancel" had been clicked, so re-enable
// it now.
m_sharedData->m_notifications |= wxSPDD_ENABLE_SKIP;
// Also re-enable "Cancel" itself
if ( HasPDFlag(wxPD_CAN_ABORT) )
m_sharedData->m_notifications |= wxSPDD_ENABLE_ABORT;
hwnd = m_sharedData->m_hwnd;
} // Unlock m_cs, we can't call any function operating on a dialog with
// it locked as it can result in a deadlock if the dialog callback is
// called by Windows.
// After resuming we need to bring the window on top of the Z-order as
// it could be hidden by another window shown from the main thread,
// e.g. a confirmation dialog asking whether the user really wants to
// abort.
//
// Notice that this must be done from the main thread as it owns the
// currently active window and attempts to do this from the task dialog
// thread would simply fail.
::BringWindowToTop(hwnd);
}
#endif // wxHAS_MSW_TASKDIALOG
}
int wxProgressDialog::GetValue() const
{
#ifdef wxHAS_MSW_TASKDIALOG
if ( HasNativeTaskDialog() )
{
wxCriticalSectionLocker locker(m_sharedData->m_cs);
return m_sharedData->m_value;
}
#endif // wxHAS_MSW_TASKDIALOG
return wxGenericProgressDialog::GetValue();
}
wxString wxProgressDialog::GetMessage() const
{
#ifdef wxHAS_MSW_TASKDIALOG
if ( HasNativeTaskDialog() )
return m_message;
#endif // wxHAS_MSW_TASKDIALOG
return wxGenericProgressDialog::GetMessage();
}
void wxProgressDialog::SetRange(int maximum)
{
#ifdef wxHAS_MSW_TASKDIALOG
if ( HasNativeTaskDialog() )
{
SetMaximum(maximum);
wxCriticalSectionLocker locker(m_sharedData->m_cs);
m_sharedData->m_range = maximum;
m_sharedData->m_notifications |= wxSPDD_RANGE_CHANGED;
return;
}
#endif // wxHAS_MSW_TASKDIALOG
wxGenericProgressDialog::SetRange( maximum );
}
bool wxProgressDialog::WasSkipped() const
{
#ifdef wxHAS_MSW_TASKDIALOG
if ( HasNativeTaskDialog() )
{
if ( !m_sharedData )
{
// Couldn't be skipped before being shown.
return false;
}
wxCriticalSectionLocker locker(m_sharedData->m_cs);
return m_sharedData->m_skipped;
}
#endif // wxHAS_MSW_TASKDIALOG
return wxGenericProgressDialog::WasSkipped();
}
bool wxProgressDialog::WasCancelled() const
{
#ifdef wxHAS_MSW_TASKDIALOG
if ( HasNativeTaskDialog() )
{
wxCriticalSectionLocker locker(m_sharedData->m_cs);
return m_sharedData->m_state == Canceled;
}
#endif // wxHAS_MSW_TASKDIALOG
return wxGenericProgressDialog::WasCancelled();
}
void wxProgressDialog::SetTitle(const wxString& title)
{
#ifdef wxHAS_MSW_TASKDIALOG
if ( HasNativeTaskDialog() )
{
m_title = title;
if ( m_sharedData )
{
wxCriticalSectionLocker locker(m_sharedData->m_cs);
m_sharedData->m_title = title;
m_sharedData->m_notifications = wxSPDD_TITLE_CHANGED;
}
}
#endif // wxHAS_MSW_TASKDIALOG
wxGenericProgressDialog::SetTitle(title);
}
wxString wxProgressDialog::GetTitle() const
{
#ifdef wxHAS_MSW_TASKDIALOG
if ( HasNativeTaskDialog() )
return m_title;
#endif // wxHAS_MSW_TASKDIALOG
return wxGenericProgressDialog::GetTitle();
}
bool wxProgressDialog::Show(bool show)
{
#ifdef wxHAS_MSW_TASKDIALOG
if ( HasNativeTaskDialog() )
{
// The dialog can't be hidden at all and showing it again after it had
// been shown before doesn't do anything.
if ( !show || m_taskDialogRunner )
return false;
// We're showing the dialog for the first time, create the thread that
// will manage it.
m_taskDialogRunner = new wxProgressDialogTaskRunner;
m_sharedData = m_taskDialogRunner->GetSharedDataObject();
// Initialize shared data.
m_sharedData->m_title = m_title;
m_sharedData->m_message = m_message;
m_sharedData->m_range = m_maximum;
m_sharedData->m_state = Uncancelable;
m_sharedData->m_style = GetPDStyle();
if ( HasPDFlag(wxPD_CAN_ABORT) )
{
m_sharedData->m_state = Continue;
m_sharedData->m_labelCancel = _("Cancel");
}
else // Dialog can't be cancelled.
{
// We still must have at least a single button in the dialog so
// just don't call it "Cancel" in this case.
m_sharedData->m_labelCancel = _("Close");
}
if ( HasPDFlag(wxPD_ELAPSED_TIME |
wxPD_ESTIMATED_TIME |
wxPD_REMAINING_TIME) )
{
// Use a non-empty string just to have the collapsible pane shown.
m_sharedData->m_expandedInformation = " ";
}
// Do launch the thread.
if ( m_taskDialogRunner->Create() != wxTHREAD_NO_ERROR )
{
wxLogError( "Unable to create thread!" );
return false;
}
if ( m_taskDialogRunner->Run() != wxTHREAD_NO_ERROR )
{
wxLogError( "Unable to start thread!" );
return false;
}
// Do not show the underlying dialog.
return false;
}
#endif // wxHAS_MSW_TASKDIALOG
return wxGenericProgressDialog::Show( show );
}
void wxProgressDialog::UpdateExpandedInformation(int value)
{
#ifdef wxHAS_MSW_TASKDIALOG
unsigned long elapsedTime;
unsigned long estimatedTime;
unsigned long remainingTime;
UpdateTimeEstimates(value, elapsedTime, estimatedTime, remainingTime);
int realEstimatedTime = estimatedTime,
realRemainingTime = remainingTime;
if ( m_sharedData->m_progressBarMarquee )
{
// In indeterminate mode we don't have any estimation neither for the
// remaining nor for estimated time.
realEstimatedTime =
realRemainingTime = -1;
}
wxString expandedInformation;
// Calculate the three different timing values.
if ( HasPDFlag(wxPD_ELAPSED_TIME) )
{
expandedInformation << GetElapsedLabel()
<< " "
<< GetFormattedTime(elapsedTime);
}
if ( HasPDFlag(wxPD_ESTIMATED_TIME) )
{
if ( !expandedInformation.empty() )
expandedInformation += "\n";
expandedInformation << GetEstimatedLabel()
<< " "
<< GetFormattedTime(realEstimatedTime);
}
if ( HasPDFlag(wxPD_REMAINING_TIME) )
{
if ( !expandedInformation.empty() )
expandedInformation += "\n";
expandedInformation << GetRemainingLabel()
<< " "
<< GetFormattedTime(realRemainingTime);
}
// Update with new timing information.
if ( expandedInformation != m_sharedData->m_expandedInformation )
{
m_sharedData->m_expandedInformation = expandedInformation;
m_sharedData->m_notifications |= wxSPDD_EXPINFO_CHANGED;
}
#else // !wxHAS_MSW_TASKDIALOG
wxUnusedVar(value);
#endif // wxHAS_MSW_TASKDIALOG/!wxHAS_MSW_TASKDIALOG
}
// ----------------------------------------------------------------------------
// wxProgressDialogTaskRunner and related methods
// ----------------------------------------------------------------------------
#ifdef wxHAS_MSW_TASKDIALOG
void* wxProgressDialogTaskRunner::Entry()
{
WinStruct<TASKDIALOGCONFIG> tdc;
wxMSWTaskDialogConfig wxTdc;
{
wxCriticalSectionLocker locker(m_sharedData.m_cs);
wxTdc.caption = m_sharedData.m_title.wx_str();
wxTdc.message = m_sharedData.m_message.wx_str();
wxTdc.MSWCommonTaskDialogInit( tdc );
tdc.pfCallback = TaskDialogCallbackProc;
tdc.lpCallbackData = (LONG_PTR) &m_sharedData;
// Undo some of the effects of MSWCommonTaskDialogInit().
tdc.dwFlags &= ~TDF_EXPAND_FOOTER_AREA; // Expand in content area.
tdc.dwCommonButtons = 0; // Don't use common buttons.
wxTdc.useCustomLabels = true;
if ( m_sharedData.m_style & wxPD_CAN_SKIP )
wxTdc.AddTaskDialogButton( tdc, Id_SkipBtn, 0, _("Skip") );
// Use a Cancel button when requested or use a Close button when
// the dialog does not automatically hide.
wxTdc.AddTaskDialogButton( tdc, IDCANCEL, 0,
m_sharedData.m_labelCancel );
tdc.dwFlags |= TDF_CALLBACK_TIMER | TDF_SHOW_PROGRESS_BAR;
if ( !m_sharedData.m_expandedInformation.empty() )
{
tdc.pszExpandedInformation =
m_sharedData.m_expandedInformation.wx_str();
}
}
TaskDialogIndirect_t taskDialogIndirect = GetTaskDialogIndirectFunc();
if ( !taskDialogIndirect )
return NULL;
int msAns;
HRESULT hr = taskDialogIndirect(&tdc, &msAns, NULL, NULL);
if ( FAILED(hr) )
wxLogApiError( "TaskDialogIndirect", hr );
// If the main thread is waiting for us to exit inside the event loop in
// Update(), wake it up so that it checks our status again.
wxWakeUpIdle();
return NULL;
}
// static
HRESULT CALLBACK
wxProgressDialogTaskRunner::TaskDialogCallbackProc
(
HWND hwnd,
UINT uNotification,
WPARAM wParam,
LPARAM WXUNUSED(lParam),
LONG_PTR dwRefData
)
{
wxProgressDialogSharedData * const sharedData =
(wxProgressDialogSharedData *) dwRefData;
wxCriticalSectionLocker locker(sharedData->m_cs);
switch ( uNotification )
{
case TDN_CREATED:
// Store the HWND for the main thread use.
sharedData->m_hwnd = hwnd;
// Set the maximum value and disable Close button.
::SendMessage( hwnd,
TDM_SET_PROGRESS_BAR_RANGE,
0,
MAKELPARAM(0, sharedData->m_range) );
// If we can't be aborted, the "Close" button will only be enabled
// when the progress ends (and not even then with wxPD_AUTO_HIDE).
if ( !(sharedData->m_style & wxPD_CAN_ABORT) )
::SendMessage( hwnd, TDM_ENABLE_BUTTON, IDCANCEL, FALSE );
break;
case TDN_BUTTON_CLICKED:
switch ( wParam )
{
case Id_SkipBtn:
::SendMessage(hwnd, TDM_ENABLE_BUTTON, Id_SkipBtn, FALSE);
sharedData->m_skipped = true;
return TRUE;
case IDCANCEL:
if ( sharedData->m_state == wxProgressDialog::Finished )
{
// If the main thread is waiting for us, tell it that
// we're gone (and if it doesn't wait, it's harmless).
sharedData->m_state = wxProgressDialog::Dismissed;
// Let Windows close the dialog.
return FALSE;
}
// Close button on the window triggers an IDCANCEL press,
// don't allow it when it should only be possible to close
// a finished dialog.
if ( sharedData->m_style & wxPD_CAN_ABORT )
{
wxCHECK_MSG
(
sharedData->m_state == wxProgressDialog::Continue,
TRUE,
"Dialog not in a cancelable state!"
);
::SendMessage(hwnd, TDM_ENABLE_BUTTON, Id_SkipBtn, FALSE);
::SendMessage(hwnd, TDM_ENABLE_BUTTON, IDCANCEL, FALSE);
sharedData->m_timeStop = wxGetCurrentTime();
sharedData->m_state = wxProgressDialog::Canceled;
}
return TRUE;
}
break;
case TDN_TIMER:
PerformNotificationUpdates(hwnd, sharedData);
/*
Decide whether we should end the dialog. This is done if either
the dialog object itself was destroyed or if the progress
finished and we were configured to hide automatically without
waiting for the user to dismiss us.
Notice that we do not close the dialog if it was cancelled
because it's up to the user code in the main thread to decide
whether it really wants to cancel the dialog.
*/
if ( (sharedData->m_notifications & wxSPDD_DESTROYED) ||
(sharedData->m_state == wxProgressDialog::Finished &&
sharedData->m_style & wxPD_AUTO_HIDE) )
{
::EndDialog( hwnd, IDCLOSE );
}
sharedData->m_notifications = 0;
return TRUE;
}
// Return anything.
return 0;
}
#endif // wxHAS_MSW_TASKDIALOG
#endif // wxUSE_PROGRESSDLG && wxUSE_THREADS