Merge branch 'progress-dialog-fixes'
Make native MSW wxProgressDialog much more usable and some minor improvements to the generic version.
This commit is contained in:
@@ -223,6 +223,7 @@ wxMSW:
|
||||
- Fix updating radio groups when non-radio item is inserted to wxMenu.
|
||||
- Fix autoselecting the contents of wxTextCtrl with wxWANTS_CHARS style.
|
||||
- Implement SetIcon(), SetPosition(), GetPosition() for native wxProgressDialog.
|
||||
- Fix focus-related problems when using native wxProgressDialog.
|
||||
- Fix crash when reparenting the currently focused window to another TLW.
|
||||
|
||||
wxOSX:
|
||||
|
@@ -12,6 +12,7 @@
|
||||
#define __PROGDLGH_G__
|
||||
|
||||
#include "wx/dialog.h"
|
||||
#include "wx/weakref.h"
|
||||
|
||||
class WXDLLIMPEXP_FWD_CORE wxButton;
|
||||
class WXDLLIMPEXP_FWD_CORE wxEventLoop;
|
||||
@@ -43,18 +44,18 @@ public:
|
||||
virtual bool Update(int value, const wxString& newmsg = wxEmptyString, bool *skip = NULL);
|
||||
virtual bool Pulse(const wxString& newmsg = wxEmptyString, bool *skip = NULL);
|
||||
|
||||
void Resume();
|
||||
virtual void Resume();
|
||||
|
||||
int GetValue() const;
|
||||
int GetRange() const;
|
||||
wxString GetMessage() const;
|
||||
virtual int GetValue() const;
|
||||
virtual int GetRange() const;
|
||||
virtual wxString GetMessage() const;
|
||||
|
||||
void SetRange(int maximum);
|
||||
virtual void SetRange(int maximum);
|
||||
|
||||
// Return whether "Cancel" or "Skip" button was pressed, always return
|
||||
// false if the corresponding button is not shown.
|
||||
bool WasCancelled() const;
|
||||
bool WasSkipped() const;
|
||||
virtual bool WasCancelled() const;
|
||||
virtual bool WasSkipped() const;
|
||||
|
||||
// Must provide overload to avoid hiding it (and warnings about it)
|
||||
virtual void Update() wxOVERRIDE { wxDialog::Update(); }
|
||||
@@ -104,6 +105,9 @@ protected:
|
||||
// Converts seconds to HH:mm:ss format.
|
||||
static wxString GetFormattedTime(unsigned long timeInSec);
|
||||
|
||||
// Create a new event loop if there is no currently running one.
|
||||
void EnsureActiveEventLoopExists();
|
||||
|
||||
// callback for optional abort button
|
||||
void OnCancel(wxCommandEvent&);
|
||||
|
||||
@@ -120,8 +124,8 @@ protected:
|
||||
// the dialog was shown
|
||||
void ReenableOtherWindows();
|
||||
|
||||
// Set the top level parent we store from the parent window provided when
|
||||
// creating the dialog.
|
||||
// Store the parent window as wxWindow::m_parent and also set the top level
|
||||
// parent reference we store in this class itself.
|
||||
void SetTopParent(wxWindow* parent);
|
||||
|
||||
// return the top level parent window of this dialog (may be NULL)
|
||||
@@ -183,8 +187,9 @@ private:
|
||||
*m_estimated,
|
||||
*m_remaining;
|
||||
|
||||
// parent top level window (may be NULL)
|
||||
wxWindow *m_parentTop;
|
||||
// Reference to the parent top level window, automatically becomes NULL if
|
||||
// it it is destroyed and could be always NULL if it's not given at all.
|
||||
wxWindowRef m_parentTop;
|
||||
|
||||
// Progress dialog styles: this is not the same as m_windowStyle because
|
||||
// wxPD_XXX constants clash with the existing TLW styles so to be sure we
|
||||
|
@@ -26,17 +26,17 @@ public:
|
||||
virtual bool Update(int value, const wxString& newmsg = wxEmptyString, bool *skip = NULL) wxOVERRIDE;
|
||||
virtual bool Pulse(const wxString& newmsg = wxEmptyString, bool *skip = NULL) wxOVERRIDE;
|
||||
|
||||
void Resume();
|
||||
virtual void Resume() wxOVERRIDE;
|
||||
|
||||
int GetValue() const;
|
||||
wxString GetMessage() const;
|
||||
virtual int GetValue() const wxOVERRIDE;
|
||||
virtual wxString GetMessage() const wxOVERRIDE;
|
||||
|
||||
void SetRange(int maximum);
|
||||
virtual void SetRange(int maximum) wxOVERRIDE;
|
||||
|
||||
// Return whether "Cancel" or "Skip" button was pressed, always return
|
||||
// false if the corresponding button is not shown.
|
||||
bool WasSkipped() const;
|
||||
bool WasCancelled() const;
|
||||
virtual bool WasSkipped() const wxOVERRIDE;
|
||||
virtual bool WasCancelled() const wxOVERRIDE;
|
||||
|
||||
virtual void SetTitle(const wxString& title) wxOVERRIDE;
|
||||
virtual wxString GetTitle() const wxOVERRIDE;
|
||||
@@ -44,6 +44,8 @@ public:
|
||||
virtual void SetIcons(const wxIconBundle& icons) wxOVERRIDE;
|
||||
virtual void DoMoveWindow(int x, int y, int width, int height) wxOVERRIDE;
|
||||
virtual void DoGetPosition(int *x, int *y) const wxOVERRIDE;
|
||||
virtual void DoGetSize(int *width, int *height) const wxOVERRIDE;
|
||||
virtual void Fit() wxOVERRIDE;
|
||||
|
||||
virtual bool Show( bool show = true ) wxOVERRIDE;
|
||||
|
||||
@@ -53,15 +55,24 @@ public:
|
||||
virtual WXWidget GetHandle() const wxOVERRIDE;
|
||||
|
||||
private:
|
||||
// Performs common routines to Update() and Pulse(). Requires the
|
||||
// shared object to have been entered.
|
||||
// Common part of Update() and Pulse().
|
||||
//
|
||||
// Returns false if the user requested cancelling the dialog.
|
||||
bool DoNativeBeforeUpdate(bool *skip);
|
||||
|
||||
// Dispatch the pending events to let the windows to update, just as the
|
||||
// generic version does. This is done as part of DoNativeBeforeUpdate().
|
||||
void DispatchEvents();
|
||||
|
||||
// Updates the various timing informations for both determinate
|
||||
// and indeterminate modes. Requires the shared object to have
|
||||
// been entered.
|
||||
void UpdateExpandedInformation(int value);
|
||||
|
||||
// Get the task dialog geometry when using the native dialog.
|
||||
wxRect GetTaskDialogRect() const;
|
||||
|
||||
|
||||
wxProgressDialogTaskRunner *m_taskDialogRunner;
|
||||
|
||||
wxProgressDialogSharedData *m_sharedData;
|
||||
|
@@ -90,6 +90,9 @@ public:
|
||||
// NULL if getting the system menu failed.
|
||||
wxMenu *MSWGetSystemMenu() const;
|
||||
|
||||
// Enable or disable the close button of the specified window.
|
||||
static bool MSWEnableCloseButton(WXHWND hwnd, bool enable = true);
|
||||
|
||||
|
||||
// implementation from now on
|
||||
// --------------------------
|
||||
|
@@ -33,6 +33,26 @@
|
||||
wxProgressDialog in a multi-threaded application you should be sure to use
|
||||
wxThreadEvent for your inter-threads communications).
|
||||
|
||||
Although wxProgressDialog is not really modal, it should be created on the
|
||||
stack, and not the heap, as other modal dialogs, e.g. use it like this:
|
||||
@code
|
||||
void MyFrame::SomeFunc()
|
||||
{
|
||||
wxProgressDialog dialog(...);
|
||||
for ( int i = 0; i < 100; ++i ) {
|
||||
if ( !dialog.Update(i)) {
|
||||
// Cancelled by user.
|
||||
break;
|
||||
}
|
||||
|
||||
... do something time-consuming (but not too much) ...
|
||||
}
|
||||
}
|
||||
@endcode
|
||||
Note that this becomes even more important if the dialog is instantiated
|
||||
during the program initialization, e.g. from wxApp::OnInit(): the dialog
|
||||
must be destroyed before the main event loop is started in this case.
|
||||
|
||||
@beginStyleTable
|
||||
@style{wxPD_APP_MODAL}
|
||||
Make the progress dialog modal. If this flag is not given, it is
|
||||
@@ -192,9 +212,19 @@ public:
|
||||
for the user to dismiss it, meaning that this function does not return
|
||||
until this happens.
|
||||
|
||||
Notice that you may want to call Fit() to change the dialog size to
|
||||
conform to the length of the new message if desired. The dialog does
|
||||
not do this automatically.
|
||||
Notice that if @a newmsg is longer than the currently shown message,
|
||||
the dialog will be automatically made wider to account for it. However
|
||||
if the new message is shorter than the previous one, the dialog doesn't
|
||||
shrink back to avoid constant resizes if the message is changed often.
|
||||
To do this and fit the dialog to its current contents you may call
|
||||
Fit() explicitly. However the native MSW implementation of this class
|
||||
does make the dialog shorter if the new text has fewer lines of text
|
||||
than the old one, so it is recommended to keep the number of lines of
|
||||
text constant in order to avoid jarring dialog size changes. You may
|
||||
also want to make the initial message, specified when creating the
|
||||
dialog, wide enough to avoid having to resize the dialog later, e.g. by
|
||||
appending a long string of unbreakable spaces (@c wxString(L'\u00a0',
|
||||
100)) to it.
|
||||
|
||||
@param value
|
||||
The new value of the progress meter. It should be less than or equal to
|
||||
|
@@ -233,6 +233,9 @@ wxBEGIN_EVENT_TABLE(MyFrame, wxFrame)
|
||||
|
||||
#if wxUSE_PROGRESSDLG
|
||||
EVT_MENU(DIALOGS_PROGRESS, MyFrame::ShowProgress)
|
||||
#ifdef wxHAS_NATIVE_PROGRESSDIALOG
|
||||
EVT_MENU(DIALOGS_PROGRESS_GENERIC, MyFrame::ShowProgressGeneric)
|
||||
#endif // wxHAS_NATIVE_PROGRESSDIALOG
|
||||
#endif // wxUSE_PROGRESSDLG
|
||||
|
||||
EVT_MENU(DIALOGS_APP_PROGRESS, MyFrame::ShowAppProgress)
|
||||
@@ -349,7 +352,25 @@ bool MyApp::OnInit()
|
||||
);
|
||||
for ( int i = 0; i <= PROGRESS_COUNT; i++ )
|
||||
{
|
||||
if ( !dlg.Update(i) )
|
||||
wxString msg;
|
||||
switch ( i )
|
||||
{
|
||||
case 15:
|
||||
msg = "And the same dialog but with a very, very, very long"
|
||||
" message, just to test how it appears in this case.";
|
||||
break;
|
||||
|
||||
case 30:
|
||||
msg = "Back to brevity";
|
||||
break;
|
||||
|
||||
case 80:
|
||||
msg = "Back and adjusted";
|
||||
dlg.Fit();
|
||||
break;
|
||||
}
|
||||
|
||||
if ( !dlg.Update(i, msg) )
|
||||
break;
|
||||
|
||||
wxMilliSleep(50);
|
||||
@@ -490,6 +511,10 @@ bool MyApp::OnInit()
|
||||
|
||||
#if wxUSE_PROGRESSDLG
|
||||
info_menu->Append(DIALOGS_PROGRESS, wxT("Pro&gress dialog\tCtrl-G"));
|
||||
#ifdef wxHAS_NATIVE_PROGRESSDIALOG
|
||||
info_menu->Append(DIALOGS_PROGRESS_GENERIC,
|
||||
wxT("Generic progress dialog\tCtrl-Alt-G"));
|
||||
#endif // wxHAS_NATIVE_PROGRESSDIALOG
|
||||
#endif // wxUSE_PROGRESSDLG
|
||||
|
||||
info_menu->Append(DIALOGS_APP_PROGRESS, wxT("&App progress\tShift-Ctrl-G"));
|
||||
@@ -2652,10 +2677,10 @@ void MyFrame::OnExit(wxCommandEvent& WXUNUSED(event) )
|
||||
|
||||
#if wxUSE_PROGRESSDLG
|
||||
|
||||
static const int max = 100;
|
||||
|
||||
void MyFrame::ShowProgress( wxCommandEvent& WXUNUSED(event) )
|
||||
{
|
||||
static const int max = 100;
|
||||
|
||||
wxProgressDialog dialog("Progress dialog example",
|
||||
// "Reserve" enough space for the multiline
|
||||
// messages below, we'll change it anyhow
|
||||
@@ -2673,6 +2698,30 @@ void MyFrame::ShowProgress( wxCommandEvent& WXUNUSED(event) )
|
||||
wxPD_SMOOTH // - makes indeterminate mode bar on WinXP very small
|
||||
);
|
||||
|
||||
DoShowProgress(dialog);
|
||||
}
|
||||
|
||||
#ifdef wxHAS_NATIVE_PROGRESSDIALOG
|
||||
void MyFrame::ShowProgressGeneric( wxCommandEvent& WXUNUSED(event) )
|
||||
{
|
||||
wxGenericProgressDialog dialog("Generic progress dialog example",
|
||||
wxString(' ', 100) + "\n\n\n\n",
|
||||
max,
|
||||
this,
|
||||
wxPD_CAN_ABORT |
|
||||
wxPD_CAN_SKIP |
|
||||
wxPD_APP_MODAL |
|
||||
wxPD_ELAPSED_TIME |
|
||||
wxPD_ESTIMATED_TIME |
|
||||
wxPD_REMAINING_TIME |
|
||||
wxPD_SMOOTH);
|
||||
|
||||
DoShowProgress(dialog);
|
||||
}
|
||||
#endif // wxHAS_NATIVE_PROGRESSDIALOG
|
||||
|
||||
void MyFrame::DoShowProgress(wxGenericProgressDialog& dialog)
|
||||
{
|
||||
bool cont = true;
|
||||
for ( int i = 0; i <= max; i++ )
|
||||
{
|
||||
@@ -2721,8 +2770,8 @@ void MyFrame::ShowProgress( wxCommandEvent& WXUNUSED(event) )
|
||||
{
|
||||
i += max/4;
|
||||
|
||||
if ( i >= 100 )
|
||||
i = 99;
|
||||
if ( i >= max )
|
||||
i = max - 1;
|
||||
}
|
||||
|
||||
if ( !cont )
|
||||
@@ -2736,7 +2785,7 @@ void MyFrame::ShowProgress( wxCommandEvent& WXUNUSED(event) )
|
||||
dialog.Resume();
|
||||
}
|
||||
|
||||
wxMilliSleep(200);
|
||||
wxMilliSleep(100);
|
||||
}
|
||||
|
||||
if ( !cont )
|
||||
@@ -2768,7 +2817,7 @@ void MyFrame::ShowAppProgress( wxCommandEvent& WXUNUSED(event) )
|
||||
{
|
||||
progress.SetValue(i);
|
||||
|
||||
wxMilliSleep(500);
|
||||
wxMilliSleep(200);
|
||||
}
|
||||
|
||||
wxLogStatus("Progress finished");
|
||||
|
@@ -452,6 +452,10 @@ public:
|
||||
|
||||
#if wxUSE_PROGRESSDLG
|
||||
void ShowProgress(wxCommandEvent& event);
|
||||
#ifdef wxHAS_NATIVE_PROGRESSDIALOG
|
||||
void ShowProgressGeneric(wxCommandEvent& event);
|
||||
#endif // wxHAS_NATIVE_PROGRESSDIALOG
|
||||
void DoShowProgress(wxGenericProgressDialog& dialog);
|
||||
#endif // wxUSE_PROGRESSDLG
|
||||
void ShowAppProgress(wxCommandEvent& event);
|
||||
|
||||
@@ -596,6 +600,7 @@ enum
|
||||
DIALOGS_ONTOP,
|
||||
DIALOGS_MODELESS_BTN,
|
||||
DIALOGS_PROGRESS,
|
||||
DIALOGS_PROGRESS_GENERIC,
|
||||
DIALOGS_APP_PROGRESS,
|
||||
DIALOGS_ABOUTDLG_SIMPLE,
|
||||
DIALOGS_ABOUTDLG_FANCY,
|
||||
|
@@ -133,6 +133,7 @@ wxGenericProgressDialog::wxGenericProgressDialog(const wxString& title,
|
||||
|
||||
void wxGenericProgressDialog::SetTopParent(wxWindow* parent)
|
||||
{
|
||||
m_parent = parent;
|
||||
m_parentTop = GetParentForModalDialog(parent, GetWindowStyle());
|
||||
}
|
||||
|
||||
@@ -144,13 +145,9 @@ bool wxGenericProgressDialog::Create( const wxString& title,
|
||||
{
|
||||
SetTopParent(parent);
|
||||
|
||||
m_parentTop = wxGetTopLevelParent(parent);
|
||||
m_pdStyle = style;
|
||||
|
||||
wxWindow* const
|
||||
realParent = GetParentForModalDialog(parent, GetWindowStyle());
|
||||
|
||||
if (!wxDialog::Create(realParent, wxID_ANY, title, wxDefaultPosition, wxDefaultSize, GetWindowStyle()))
|
||||
if (!wxDialog::Create(m_parentTop, wxID_ANY, title, wxDefaultPosition, wxDefaultSize, GetWindowStyle()))
|
||||
return false;
|
||||
|
||||
SetMaximum(maximum);
|
||||
@@ -160,11 +157,7 @@ bool wxGenericProgressDialog::Create( const wxString& title,
|
||||
// even if this means we have to start it ourselves (this happens most
|
||||
// commonly during the program initialization, e.g. for the progress
|
||||
// dialogs shown from overridden wxApp::OnInit()).
|
||||
if ( !wxEventLoopBase::GetActive() )
|
||||
{
|
||||
m_tempEventLoop = new wxEventLoop;
|
||||
wxEventLoop::SetActive(m_tempEventLoop);
|
||||
}
|
||||
EnsureActiveEventLoopExists();
|
||||
|
||||
#if defined(__WXMSW__) && !defined(__WXUNIVERSAL__)
|
||||
// we have to remove the "Close" button from the title bar then as it is
|
||||
@@ -363,6 +356,15 @@ wxString wxGenericProgressDialog::GetFormattedTime(unsigned long timeInSec)
|
||||
return timeAsHMS;
|
||||
}
|
||||
|
||||
void wxGenericProgressDialog::EnsureActiveEventLoopExists()
|
||||
{
|
||||
if ( !wxEventLoopBase::GetActive() )
|
||||
{
|
||||
m_tempEventLoop = new wxEventLoop;
|
||||
wxEventLoop::SetActive(m_tempEventLoop);
|
||||
}
|
||||
}
|
||||
|
||||
wxStaticText *
|
||||
wxGenericProgressDialog::CreateLabel(const wxString& text, wxSizer *sizer)
|
||||
{
|
||||
@@ -695,6 +697,21 @@ wxGenericProgressDialog::~wxGenericProgressDialog()
|
||||
|
||||
if ( m_tempEventLoop )
|
||||
{
|
||||
// If another event loop has been installed as active during the life
|
||||
// time of this object, we shouldn't deactivate it, but we also can't
|
||||
// delete our m_tempEventLoop in this case because it risks leaving the
|
||||
// new event loop with a dangling pointer, which it will set back as
|
||||
// the active loop when it exits, resulting in a crash. So we have no
|
||||
// choice but to just leak this pointer then, which is, of course, bad
|
||||
// and usually easily avoidable by just destroying the progress dialog
|
||||
// sooner, so warn the programmer about it.
|
||||
wxCHECK_RET
|
||||
(
|
||||
wxEventLoopBase::GetActive() == m_tempEventLoop,
|
||||
"current event loop must not be changed during "
|
||||
"wxGenericProgressDialog lifetime"
|
||||
);
|
||||
|
||||
wxEventLoopBase::SetActive(NULL);
|
||||
delete m_tempEventLoop;
|
||||
}
|
||||
@@ -765,8 +782,17 @@ void wxGenericProgressDialog::UpdateMessage(const wxString &newmsg)
|
||||
{
|
||||
if ( !newmsg.empty() && newmsg != m_msg->GetLabel() )
|
||||
{
|
||||
const wxSize sizeOld = m_msg->GetSize();
|
||||
|
||||
m_msg->SetLabel(newmsg);
|
||||
|
||||
if ( m_msg->GetSize().x > sizeOld.x )
|
||||
{
|
||||
// Resize the dialog to fit its new, longer contents instead of
|
||||
// just truncating it.
|
||||
Fit();
|
||||
}
|
||||
|
||||
// allow the window to repaint:
|
||||
// NOTE: since we yield only for UI events with this call, there
|
||||
// should be no side-effects
|
||||
|
@@ -57,7 +57,6 @@ 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 wxSPDD_ICON_CHANGED = 0x1000;
|
||||
@@ -81,6 +80,7 @@ public:
|
||||
m_value = 0;
|
||||
m_progressBarMarquee = false;
|
||||
m_skipped = false;
|
||||
m_msgChangeElementText = TDM_UPDATE_ELEMENT_TEXT;
|
||||
m_notifications = 0;
|
||||
m_parent = NULL;
|
||||
}
|
||||
@@ -105,9 +105,48 @@ public:
|
||||
bool m_progressBarMarquee;
|
||||
bool m_skipped;
|
||||
|
||||
// The task dialog message to use for changing the text of its elements:
|
||||
// it is set to TDM_SET_ELEMENT_TEXT by Fit() to let the dialog adjust
|
||||
// itself to the size of its elements during the next update, but otherwise
|
||||
// TDM_UPDATE_ELEMENT_TEXT is used in order to prevent the dialog from
|
||||
// performing a layout on each update, which is annoying as it can result
|
||||
// in its size constantly changing.
|
||||
int m_msgChangeElementText;
|
||||
|
||||
// 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;
|
||||
|
||||
|
||||
// Helper function to split a single message, passed via our public API,
|
||||
// into the title and the main content body used by the native dialog.
|
||||
//
|
||||
// Note that it uses m_message and so must be called with m_cs locked.
|
||||
void SplitMessageIntoTitleAndBody(wxString& title, wxString& body) const
|
||||
{
|
||||
title = m_message;
|
||||
|
||||
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);
|
||||
}
|
||||
else // A single line
|
||||
{
|
||||
// Don't use title without the body, this doesn't make sense.
|
||||
body.clear();
|
||||
title.swap(body);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Runner thread that takes care of displaying and updating the
|
||||
@@ -182,6 +221,16 @@ BOOL CALLBACK DisplayCloseButton(HWND hwnd, LPARAM lParam)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// This function enables or disables both the cancel button in the task dialog
|
||||
// and the close button in its title bar, as they perform the same function and
|
||||
// so should be kept in the same state.
|
||||
void EnableCloseButtons(HWND hwnd, bool enable)
|
||||
{
|
||||
::SendMessage(hwnd, TDM_ENABLE_BUTTON, IDCANCEL, enable ? TRUE : FALSE);
|
||||
|
||||
wxTopLevelWindow::MSWEnableCloseButton(hwnd, enable);
|
||||
}
|
||||
|
||||
void PerformNotificationUpdates(HWND hwnd,
|
||||
wxProgressDialogSharedData *sharedData)
|
||||
{
|
||||
@@ -196,6 +245,33 @@ void PerformNotificationUpdates(HWND hwnd,
|
||||
|
||||
if ( sharedData->m_notifications & wxSPDD_VALUE_CHANGED )
|
||||
{
|
||||
// Use a hack to avoid progress bar animation: we can't afford to use
|
||||
// it because animating the gauge movement smoothly requires a
|
||||
// constantly running message loop and while it does run in this (task
|
||||
// dialog) thread, it is often blocked from proceeding by some lock
|
||||
// held by the main thread which is busy doing something and may not
|
||||
// dispatch events frequently enough. So, in practice, the animation
|
||||
// can lag far behind the real value and results in showing a wrong
|
||||
// value in the progress bar.
|
||||
//
|
||||
// To prevent this from happening, set the progress bar value to
|
||||
// something greater than its maximal value and then move it back: in
|
||||
// the current implementations of the progress bar control, moving its
|
||||
// position backwards does it directly, without using the animation,
|
||||
// which is exactly what we want here.
|
||||
//
|
||||
// Finally notice that this hack doesn't really work for the last
|
||||
// value, but while we could use a nested hack and temporarily increase
|
||||
// the progress bar range when the value is equal to it, it isn't
|
||||
// actually necessary in practice because when we reach the end of the
|
||||
// bar the dialog is either hidden immediately anyhow or the main
|
||||
// thread enters a modal event loop which does dispatch events and so
|
||||
// it's not a problem to have an animated transition in this particular
|
||||
// case.
|
||||
::SendMessage( hwnd,
|
||||
TDM_SET_PROGRESS_BAR_POS,
|
||||
sharedData->m_value + 1,
|
||||
0 );
|
||||
::SendMessage( hwnd,
|
||||
TDM_SET_PROGRESS_BAR_POS,
|
||||
sharedData->m_value,
|
||||
@@ -235,39 +311,31 @@ void PerformNotificationUpdates(HWND hwnd,
|
||||
{
|
||||
// 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);
|
||||
}
|
||||
else // A single line
|
||||
{
|
||||
// Don't use title without the body, this doesn't make sense.
|
||||
title.swap(body);
|
||||
}
|
||||
wxString title, body;
|
||||
sharedData->SplitMessageIntoTitleAndBody(title, body);
|
||||
|
||||
::SendMessage( hwnd,
|
||||
TDM_SET_ELEMENT_TEXT,
|
||||
sharedData->m_msgChangeElementText,
|
||||
TDE_MAIN_INSTRUCTION,
|
||||
wxMSW_CONV_LPARAM(title) );
|
||||
|
||||
::SendMessage( hwnd,
|
||||
TDM_SET_ELEMENT_TEXT,
|
||||
sharedData->m_msgChangeElementText,
|
||||
TDE_CONTENT,
|
||||
wxMSW_CONV_LPARAM(body) );
|
||||
|
||||
// After using TDM_SET_ELEMENT_TEXT once, we don't want to use it for
|
||||
// the subsequent updates as it could result in dialog size changing
|
||||
// unexpectedly, so reset it (which does nothing if we had already done
|
||||
// it, of course, but it's not a problem).
|
||||
//
|
||||
// Notice that, contrary to its documentation, even using this message
|
||||
// still increases the dialog size if the new text is longer (at least
|
||||
// under Windows 7), but it doesn't shrink back if the text becomes
|
||||
// shorter later and stays at the bigger size which is still a big gain
|
||||
// as it prevents jumping back and forth between the smaller and larger
|
||||
// sizes.
|
||||
sharedData->m_msgChangeElementText = TDM_UPDATE_ELEMENT_TEXT;
|
||||
}
|
||||
|
||||
if ( sharedData->m_notifications & wxSPDD_EXPINFO_CHANGED )
|
||||
@@ -276,8 +344,15 @@ void PerformNotificationUpdates(HWND hwnd,
|
||||
sharedData->m_expandedInformation;
|
||||
if ( !expandedInformation.empty() )
|
||||
{
|
||||
// Here we never need to use TDM_SET_ELEMENT_TEXT as the size of
|
||||
// the expanded information doesn't change drastically.
|
||||
//
|
||||
// Notice that TDM_UPDATE_ELEMENT_TEXT for this element only works
|
||||
// when using TDF_EXPAND_FOOTER_AREA, as we do. Without this flag,
|
||||
// only TDM_SET_ELEMENT_TEXT could be used as otherwise the dialog
|
||||
// layout becomes completely mangled (at least under Windows 7).
|
||||
::SendMessage( hwnd,
|
||||
TDM_SET_ELEMENT_TEXT,
|
||||
TDM_UPDATE_ELEMENT_TEXT,
|
||||
TDE_EXPANDED_INFORMATION,
|
||||
wxMSW_CONV_LPARAM(expandedInformation) );
|
||||
}
|
||||
@@ -287,14 +362,11 @@ void PerformNotificationUpdates(HWND hwnd,
|
||||
::SendMessage( hwnd, TDM_ENABLE_BUTTON, Id_SkipBtn, TRUE );
|
||||
|
||||
if ( sharedData->m_notifications & wxSPDD_ENABLE_ABORT )
|
||||
::SendMessage( hwnd, TDM_ENABLE_BUTTON, IDCANCEL, TRUE );
|
||||
EnableCloseButtons(hwnd, 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 )
|
||||
{
|
||||
@@ -304,7 +376,7 @@ void PerformNotificationUpdates(HWND hwnd,
|
||||
{
|
||||
// Change Cancel into Close and activate the button.
|
||||
::SendMessage( hwnd, TDM_ENABLE_BUTTON, Id_SkipBtn, FALSE );
|
||||
::SendMessage( hwnd, TDM_ENABLE_BUTTON, IDCANCEL, TRUE );
|
||||
EnableCloseButtons(hwnd, true);
|
||||
::EnumChildWindows( hwnd, DisplayCloseButton,
|
||||
(LPARAM) sharedData );
|
||||
}
|
||||
@@ -337,6 +409,7 @@ wxProgressDialog::wxProgressDialog( const wxString& title,
|
||||
SetPDStyle(style);
|
||||
SetMaximum(maximum);
|
||||
|
||||
EnsureActiveEventLoopExists();
|
||||
Show();
|
||||
DisableOtherWindows();
|
||||
|
||||
@@ -359,14 +432,49 @@ wxProgressDialog::~wxProgressDialog()
|
||||
m_sharedData->m_notifications |= wxSPDD_DESTROYED;
|
||||
}
|
||||
|
||||
m_taskDialogRunner->Wait();
|
||||
// We can't use simple wxThread::Wait() here as we would deadlock because
|
||||
// the task dialog thread expects this thread to process some messages
|
||||
// (presumably those the task dialog sends to its parent during its
|
||||
// destruction).
|
||||
const WXHANDLE hThread = m_taskDialogRunner->MSWGetHandle();
|
||||
for ( bool cont = true; cont; )
|
||||
{
|
||||
DWORD rc = ::MsgWaitForMultipleObjects
|
||||
(
|
||||
1, // number of objects to wait for
|
||||
(HANDLE *)&hThread, // the objects
|
||||
false, // wait for any objects, not all
|
||||
INFINITE, // no timeout
|
||||
QS_ALLINPUT | // return as soon as there are any events
|
||||
QS_ALLPOSTMESSAGE
|
||||
);
|
||||
|
||||
delete m_taskDialogRunner;
|
||||
switch ( rc )
|
||||
{
|
||||
case 0xFFFFFFFF:
|
||||
// This is unexpected, but we can't do anything about it and
|
||||
// probably shouldn't continue waiting as we risk doing it
|
||||
// forever.
|
||||
wxLogLastError("MsgWaitForMultipleObjectsEx");
|
||||
cont = false;
|
||||
break;
|
||||
|
||||
case WAIT_OBJECT_0:
|
||||
// Thread has terminated.
|
||||
cont = false;
|
||||
break;
|
||||
|
||||
default:
|
||||
// An event has arrive, so dispatch it.
|
||||
wxEventLoop::GetActive()->Dispatch();
|
||||
}
|
||||
}
|
||||
|
||||
// Enable the windows before deleting the task dialog to ensure that we
|
||||
// can regain the activation.
|
||||
ReenableOtherWindows();
|
||||
|
||||
if ( GetTopParent() )
|
||||
GetTopParent()->Raise();
|
||||
delete m_taskDialogRunner;
|
||||
#endif // wxHAS_MSW_TASKDIALOG
|
||||
}
|
||||
|
||||
@@ -375,21 +483,26 @@ bool wxProgressDialog::Update(int value, const wxString& newmsg, bool *skip)
|
||||
#ifdef wxHAS_MSW_TASKDIALOG
|
||||
if ( HasNativeTaskDialog() )
|
||||
{
|
||||
if ( !DoNativeBeforeUpdate(skip) )
|
||||
{
|
||||
// Dialog was cancelled.
|
||||
return false;
|
||||
}
|
||||
|
||||
value /= m_factor;
|
||||
|
||||
wxASSERT_MSG( value <= m_maximum, wxT("invalid progress value") );
|
||||
|
||||
{
|
||||
wxCriticalSectionLocker locker(m_sharedData->m_cs);
|
||||
|
||||
// Do nothing in canceled state.
|
||||
if ( !DoNativeBeforeUpdate(skip) )
|
||||
return false;
|
||||
if ( value != m_sharedData->m_value )
|
||||
{
|
||||
m_sharedData->m_value = value;
|
||||
m_sharedData->m_notifications |= wxSPDD_VALUE_CHANGED;
|
||||
}
|
||||
|
||||
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() )
|
||||
if ( !newmsg.empty() && newmsg != m_message )
|
||||
{
|
||||
m_message = newmsg;
|
||||
m_sharedData->m_message = newmsg;
|
||||
@@ -445,11 +558,13 @@ 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) )
|
||||
{
|
||||
// Dialog was cancelled.
|
||||
return false;
|
||||
}
|
||||
|
||||
wxCriticalSectionLocker locker(m_sharedData->m_cs);
|
||||
|
||||
if ( !m_sharedData->m_progressBarMarquee )
|
||||
{
|
||||
@@ -457,15 +572,15 @@ bool wxProgressDialog::Pulse(const wxString& newmsg, bool *skip)
|
||||
m_sharedData->m_notifications |= wxSPDD_PBMARQUEE_CHANGED;
|
||||
}
|
||||
|
||||
if ( !newmsg.empty() )
|
||||
if ( !newmsg.empty() && newmsg != m_message )
|
||||
{
|
||||
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.
|
||||
// Value of 0 is special and is used when we can't estimate the
|
||||
// remaining and total times, which is exactly what we need here.
|
||||
UpdateExpandedInformation(0);
|
||||
|
||||
return m_sharedData->m_state != Canceled;
|
||||
@@ -475,32 +590,49 @@ bool wxProgressDialog::Pulse(const wxString& newmsg, bool *skip)
|
||||
return wxGenericProgressDialog::Pulse( newmsg, skip );
|
||||
}
|
||||
|
||||
void wxProgressDialog::DispatchEvents()
|
||||
{
|
||||
#ifdef wxHAS_MSW_TASKDIALOG
|
||||
// No need for HasNativeTaskDialog() check, we're only called when this is
|
||||
// the case.
|
||||
|
||||
// We don't need to dispatch the user input events as the task dialog
|
||||
// handles its own ones in its thread and we shouldn't react to any
|
||||
// other user actions while the dialog is shown.
|
||||
wxEventLoop::GetActive()->
|
||||
YieldFor(wxEVT_CATEGORY_ALL & ~wxEVT_CATEGORY_USER_INPUT);
|
||||
#else // !wxHAS_MSW_TASKDIALOG
|
||||
wxFAIL_MSG( "unreachable" );
|
||||
#endif // wxHAS_MSW_TASKDIALOG/!wxHAS_MSW_TASKDIALOG
|
||||
}
|
||||
|
||||
bool wxProgressDialog::DoNativeBeforeUpdate(bool *skip)
|
||||
{
|
||||
#ifdef wxHAS_MSW_TASKDIALOG
|
||||
if ( HasNativeTaskDialog() )
|
||||
DispatchEvents();
|
||||
|
||||
wxCriticalSectionLocker locker(m_sharedData->m_cs);
|
||||
|
||||
if ( m_sharedData->m_skipped )
|
||||
{
|
||||
if ( m_sharedData->m_skipped )
|
||||
if ( skip && !*skip )
|
||||
{
|
||||
if ( skip && !*skip )
|
||||
{
|
||||
*skip = true;
|
||||
m_sharedData->m_skipped = false;
|
||||
m_sharedData->m_notifications |= wxSPDD_ENABLE_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
|
||||
|
||||
if ( m_sharedData->m_state == Canceled )
|
||||
m_timeStop = m_sharedData->m_timeStop;
|
||||
|
||||
return m_sharedData->m_state != Canceled;
|
||||
#else // !wxHAS_MSW_TASKDIALOG
|
||||
wxUnusedVar(skip);
|
||||
wxFAIL_MSG( "unreachable" );
|
||||
|
||||
return false;
|
||||
#endif // wxHAS_MSW_TASKDIALOG/!wxHAS_MSW_TASKDIALOG
|
||||
}
|
||||
|
||||
void wxProgressDialog::Resume()
|
||||
@@ -514,7 +646,7 @@ void wxProgressDialog::Resume()
|
||||
|
||||
{
|
||||
wxCriticalSectionLocker locker(m_sharedData->m_cs);
|
||||
m_sharedData->m_state = m_state;
|
||||
m_sharedData->m_state = Continue;
|
||||
|
||||
// "Skip" was disabled when "Cancel" had been clicked, so re-enable
|
||||
// it now.
|
||||
@@ -542,20 +674,16 @@ void wxProgressDialog::Resume()
|
||||
#endif // wxHAS_MSW_TASKDIALOG
|
||||
}
|
||||
|
||||
WXWidget wxProgressDialog::GetHandle() const
|
||||
{
|
||||
WXWidget wxProgressDialog::GetHandle() const
|
||||
{
|
||||
#ifdef wxHAS_MSW_TASKDIALOG
|
||||
if ( HasNativeTaskDialog() )
|
||||
{
|
||||
HWND hwnd;
|
||||
{
|
||||
wxCriticalSectionLocker locker(m_sharedData->m_cs);
|
||||
m_sharedData->m_state = m_state;
|
||||
hwnd = m_sharedData->m_hwnd;
|
||||
}
|
||||
return hwnd;
|
||||
wxCriticalSectionLocker locker(m_sharedData->m_cs);
|
||||
return m_sharedData->m_hwnd;
|
||||
}
|
||||
#endif
|
||||
#endif // wxHAS_MSW_TASKDIALOG
|
||||
|
||||
return wxGenericProgressDialog::GetHandle();
|
||||
}
|
||||
|
||||
@@ -715,21 +843,33 @@ void wxProgressDialog::DoMoveWindow(int x, int y, int width, int height)
|
||||
wxGenericProgressDialog::DoMoveWindow(x, y, width, height);
|
||||
}
|
||||
|
||||
wxRect wxProgressDialog::GetTaskDialogRect() const
|
||||
{
|
||||
wxRect r;
|
||||
|
||||
#ifdef wxHAS_MSW_TASKDIALOG
|
||||
if ( m_sharedData )
|
||||
{
|
||||
wxCriticalSectionLocker locker(m_sharedData->m_cs);
|
||||
r = wxRectFromRECT(wxGetWindowRect(m_sharedData->m_hwnd));
|
||||
}
|
||||
#else // !wxHAS_MSW_TASKDIALOG
|
||||
wxFAIL_MSG( "unreachable" );
|
||||
#endif // wxHAS_MSW_TASKDIALOG/!wxHAS_MSW_TASKDIALOG
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
void wxProgressDialog::DoGetPosition(int *x, int *y) const
|
||||
{
|
||||
#ifdef wxHAS_MSW_TASKDIALOG
|
||||
if ( HasNativeTaskDialog() )
|
||||
{
|
||||
wxPoint pos;
|
||||
{
|
||||
wxCriticalSectionLocker locker(m_sharedData->m_cs);
|
||||
m_sharedData->m_state = m_state;
|
||||
pos = m_sharedData->m_winPosition;
|
||||
}
|
||||
const wxRect r = GetTaskDialogRect();
|
||||
if (x)
|
||||
*x = pos.x;
|
||||
*x = r.x;
|
||||
if (y)
|
||||
*y = pos.y;
|
||||
*y = r.y;
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -738,6 +878,42 @@ void wxProgressDialog::DoGetPosition(int *x, int *y) const
|
||||
wxGenericProgressDialog::DoGetPosition(x, y);
|
||||
}
|
||||
|
||||
void wxProgressDialog::DoGetSize(int *width, int *height) const
|
||||
{
|
||||
#ifdef wxHAS_MSW_TASKDIALOG
|
||||
if ( HasNativeTaskDialog() )
|
||||
{
|
||||
const wxRect r = GetTaskDialogRect();
|
||||
if ( width )
|
||||
*width = r.width;
|
||||
if ( height )
|
||||
*height = r.height;
|
||||
|
||||
return;
|
||||
}
|
||||
#endif // wxHAS_MSW_TASKDIALOG
|
||||
|
||||
wxGenericProgressDialog::DoGetSize(width, height);
|
||||
}
|
||||
|
||||
void wxProgressDialog::Fit()
|
||||
{
|
||||
#ifdef wxHAS_MSW_TASKDIALOG
|
||||
if ( HasNativeTaskDialog() )
|
||||
{
|
||||
wxCriticalSectionLocker locker(m_sharedData->m_cs);
|
||||
|
||||
// Force the task dialog to use this message to adjust it layout.
|
||||
m_sharedData->m_msgChangeElementText = TDM_SET_ELEMENT_TEXT;
|
||||
|
||||
// Don't change the message, but pretend that it did change.
|
||||
m_sharedData->m_notifications |= wxSPDD_MESSAGE_CHANGED;
|
||||
}
|
||||
#endif // wxHAS_MSW_TASKDIALOG
|
||||
|
||||
wxGenericProgressDialog::Fit();
|
||||
}
|
||||
|
||||
bool wxProgressDialog::Show(bool show)
|
||||
{
|
||||
#ifdef wxHAS_MSW_TASKDIALOG
|
||||
@@ -777,8 +953,9 @@ bool wxProgressDialog::Show(bool show)
|
||||
wxPD_ESTIMATED_TIME |
|
||||
wxPD_REMAINING_TIME) )
|
||||
{
|
||||
// Use a non-empty string just to have the collapsible pane shown.
|
||||
m_sharedData->m_expandedInformation = " ";
|
||||
// Set the expanded information field from the beginning to avoid
|
||||
// having to re-layout the dialog later when it changes.
|
||||
UpdateExpandedInformation(0);
|
||||
}
|
||||
|
||||
// Do launch the thread.
|
||||
@@ -794,6 +971,16 @@ bool wxProgressDialog::Show(bool show)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Wait until the dialog is shown as the program may need some time
|
||||
// before it calls Update() and we want to show something to the user
|
||||
// in the meanwhile.
|
||||
while ( wxEventLoop::GetActive()->Dispatch() )
|
||||
{
|
||||
wxCriticalSectionLocker locker(m_sharedData->m_cs);
|
||||
if ( m_sharedData->m_hwnd )
|
||||
break;
|
||||
}
|
||||
|
||||
// Do not show the underlying dialog.
|
||||
return false;
|
||||
}
|
||||
@@ -810,14 +997,15 @@ void wxProgressDialog::UpdateExpandedInformation(int value)
|
||||
unsigned long remainingTime;
|
||||
UpdateTimeEstimates(value, elapsedTime, estimatedTime, remainingTime);
|
||||
|
||||
int realEstimatedTime = estimatedTime,
|
||||
realRemainingTime = remainingTime;
|
||||
if ( m_sharedData->m_progressBarMarquee )
|
||||
// The value of 0 is special, we can't estimate anything before we have at
|
||||
// least one update, so leave the times dependent on it indeterminate.
|
||||
//
|
||||
// This value is also used by Pulse(), as in the indeterminate mode we can
|
||||
// never estimate anything.
|
||||
if ( !value )
|
||||
{
|
||||
// In indeterminate mode we don't have any estimation neither for the
|
||||
// remaining nor for estimated time.
|
||||
realEstimatedTime =
|
||||
realRemainingTime = -1;
|
||||
estimatedTime =
|
||||
remainingTime = static_cast<unsigned long>(-1);
|
||||
}
|
||||
|
||||
wxString expandedInformation;
|
||||
@@ -837,7 +1025,7 @@ void wxProgressDialog::UpdateExpandedInformation(int value)
|
||||
|
||||
expandedInformation << GetEstimatedLabel()
|
||||
<< " "
|
||||
<< GetFormattedTime(realEstimatedTime);
|
||||
<< GetFormattedTime(estimatedTime);
|
||||
}
|
||||
|
||||
if ( HasPDFlag(wxPD_REMAINING_TIME) )
|
||||
@@ -847,7 +1035,7 @@ void wxProgressDialog::UpdateExpandedInformation(int value)
|
||||
|
||||
expandedInformation << GetRemainingLabel()
|
||||
<< " "
|
||||
<< GetFormattedTime(realRemainingTime);
|
||||
<< GetFormattedTime(remainingTime);
|
||||
}
|
||||
|
||||
// Update with new timing information.
|
||||
@@ -875,8 +1063,20 @@ void* wxProgressDialogTaskRunner::Entry()
|
||||
{
|
||||
wxCriticalSectionLocker locker(m_sharedData.m_cs);
|
||||
|
||||
// If we have a parent, we must use it to have correct Z-order and
|
||||
// icon, even if this comes at the price of attaching this thread input
|
||||
// to the thread that created the parent window, i.e. the main thread.
|
||||
wxTdc.parent = m_sharedData.m_parent;
|
||||
wxTdc.caption = m_sharedData.m_title.wx_str();
|
||||
wxTdc.message = m_sharedData.m_message.wx_str();
|
||||
|
||||
// Split the message into the title and main body text in the same way
|
||||
// as it's done later in PerformNotificationUpdates() when the message
|
||||
// is changed by Update() or Pulse().
|
||||
m_sharedData.SplitMessageIntoTitleAndBody
|
||||
(
|
||||
wxTdc.message,
|
||||
wxTdc.extendedMessage
|
||||
);
|
||||
|
||||
// MSWCommonTaskDialogInit() will add an IDCANCEL button but we need to
|
||||
// give it the correct label.
|
||||
@@ -887,10 +1087,6 @@ void* wxProgressDialogTaskRunner::Entry()
|
||||
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.
|
||||
|
||||
if ( m_sharedData.m_style & wxPD_CAN_SKIP )
|
||||
wxTdc.AddTaskDialogButton( tdc, Id_SkipBtn, 0, _("Skip") );
|
||||
|
||||
@@ -900,6 +1096,13 @@ void* wxProgressDialogTaskRunner::Entry()
|
||||
{
|
||||
tdc.pszExpandedInformation =
|
||||
m_sharedData.m_expandedInformation.t_str();
|
||||
|
||||
// If we have elapsed/estimated/... times to show, show them from
|
||||
// the beginning for consistency with the generic version and also
|
||||
// because showing them later may be very sluggish if the main
|
||||
// thread doesn't update the dialog sufficiently frequently, while
|
||||
// hiding them still works reasonably well.
|
||||
tdc.dwFlags |= TDF_EXPANDED_BY_DEFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -930,6 +1133,10 @@ wxProgressDialogTaskRunner::TaskDialogCallbackProc
|
||||
LONG_PTR dwRefData
|
||||
)
|
||||
{
|
||||
bool endDialog = false;
|
||||
|
||||
// Block for shared data critical section.
|
||||
{
|
||||
wxProgressDialogSharedData * const sharedData =
|
||||
(wxProgressDialogSharedData *) dwRefData;
|
||||
|
||||
@@ -941,45 +1148,20 @@ wxProgressDialogTaskRunner::TaskDialogCallbackProc
|
||||
// Store the HWND for the main thread use.
|
||||
sharedData->m_hwnd = hwnd;
|
||||
|
||||
// The main thread is sitting in an event dispatching loop waiting
|
||||
// for this dialog to be shown, so make sure it does get an event.
|
||||
wxWakeUpIdle();
|
||||
|
||||
// Set the maximum value and disable Close button.
|
||||
::SendMessage( hwnd,
|
||||
TDM_SET_PROGRESS_BAR_RANGE,
|
||||
0,
|
||||
MAKELPARAM(0, sharedData->m_range) );
|
||||
|
||||
// We always create this task dialog with NULL parent because our
|
||||
// parent in wx sense is a window created from a different thread
|
||||
// and so can't be used as our real parent. However we still center
|
||||
// this window on the parent one as the task dialogs do with their
|
||||
// real parent usually.
|
||||
if ( sharedData->m_parent )
|
||||
{
|
||||
wxRect rect(wxRectFromRECT(wxGetWindowRect(hwnd)));
|
||||
rect = rect.CentreIn(sharedData->m_parent->GetRect());
|
||||
::SetWindowPos(hwnd,
|
||||
NULL,
|
||||
rect.x,
|
||||
rect.y,
|
||||
-1,
|
||||
-1,
|
||||
SWP_NOACTIVATE |
|
||||
SWP_NOOWNERZORDER |
|
||||
SWP_NOSIZE |
|
||||
SWP_NOZORDER);
|
||||
}
|
||||
|
||||
// Store current position for the main thread use
|
||||
// if no position update is pending.
|
||||
if ( !(sharedData->m_notifications & wxSPDD_WINDOW_MOVED) )
|
||||
{
|
||||
RECT r = wxGetWindowRect(hwnd);
|
||||
sharedData->m_winPosition = wxPoint(r.left, r.top);
|
||||
}
|
||||
|
||||
// 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 );
|
||||
EnableCloseButtons(hwnd, false);
|
||||
break;
|
||||
|
||||
case TDN_BUTTON_CLICKED:
|
||||
@@ -988,7 +1170,7 @@ wxProgressDialogTaskRunner::TaskDialogCallbackProc
|
||||
case Id_SkipBtn:
|
||||
::SendMessage(hwnd, TDM_ENABLE_BUTTON, Id_SkipBtn, FALSE);
|
||||
sharedData->m_skipped = true;
|
||||
return TRUE;
|
||||
return S_FALSE;
|
||||
|
||||
case IDCANCEL:
|
||||
if ( sharedData->m_state == wxProgressDialog::Finished )
|
||||
@@ -998,7 +1180,7 @@ wxProgressDialogTaskRunner::TaskDialogCallbackProc
|
||||
sharedData->m_state = wxProgressDialog::Dismissed;
|
||||
|
||||
// Let Windows close the dialog.
|
||||
return FALSE;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// Close button on the window triggers an IDCANCEL press,
|
||||
@@ -1006,26 +1188,48 @@ wxProgressDialogTaskRunner::TaskDialogCallbackProc
|
||||
// a finished dialog.
|
||||
if ( sharedData->m_style & wxPD_CAN_ABORT )
|
||||
{
|
||||
wxCHECK_MSG
|
||||
(
|
||||
sharedData->m_state == wxProgressDialog::Continue,
|
||||
TRUE,
|
||||
"Dialog not in a cancelable state!"
|
||||
);
|
||||
switch ( sharedData->m_state )
|
||||
{
|
||||
case wxProgressDialog::Canceled:
|
||||
// It can happen that we receive a second
|
||||
// cancel request before we had time to process
|
||||
// the first one, in which case simply do
|
||||
// nothing for the subsequent one.
|
||||
break;
|
||||
|
||||
::SendMessage(hwnd, TDM_ENABLE_BUTTON, Id_SkipBtn, FALSE);
|
||||
::SendMessage(hwnd, TDM_ENABLE_BUTTON, IDCANCEL, FALSE);
|
||||
case wxProgressDialog::Continue:
|
||||
::SendMessage(hwnd, TDM_ENABLE_BUTTON, Id_SkipBtn, FALSE);
|
||||
EnableCloseButtons(hwnd, false);
|
||||
|
||||
sharedData->m_timeStop = wxGetCurrentTime();
|
||||
sharedData->m_state = wxProgressDialog::Canceled;
|
||||
sharedData->m_timeStop = wxGetCurrentTime();
|
||||
sharedData->m_state = wxProgressDialog::Canceled;
|
||||
break;
|
||||
|
||||
// States which shouldn't be possible here:
|
||||
|
||||
// We shouldn't have an (enabled) cancel button at
|
||||
// all then.
|
||||
case wxProgressDialog::Uncancelable:
|
||||
|
||||
// This one was already dealt with above.
|
||||
case wxProgressDialog::Finished:
|
||||
|
||||
// Normally it shouldn't be possible to get any
|
||||
// notifications after switching to this state.
|
||||
case wxProgressDialog::Dismissed:
|
||||
wxFAIL_MSG( "unreachable" );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
return S_FALSE;
|
||||
}
|
||||
break;
|
||||
|
||||
case TDN_TIMER:
|
||||
PerformNotificationUpdates(hwnd, sharedData);
|
||||
// Don't perform updates if nothing needs to be done.
|
||||
if ( sharedData->m_notifications )
|
||||
PerformNotificationUpdates(hwnd, sharedData);
|
||||
|
||||
/*
|
||||
Decide whether we should end the dialog. This is done if either
|
||||
@@ -1041,21 +1245,30 @@ wxProgressDialogTaskRunner::TaskDialogCallbackProc
|
||||
(sharedData->m_state == wxProgressDialog::Finished &&
|
||||
sharedData->m_style & wxPD_AUTO_HIDE) )
|
||||
{
|
||||
::EndDialog( hwnd, IDCLOSE );
|
||||
// Don't call EndDialog() from here as it could deadlock
|
||||
// because we are inside the shared data critical section, do
|
||||
// it below after leaving it instead.
|
||||
endDialog = true;
|
||||
}
|
||||
|
||||
sharedData->m_notifications = 0;
|
||||
{
|
||||
// Update current position for the main thread use.
|
||||
RECT r = wxGetWindowRect(hwnd);
|
||||
sharedData->m_winPosition = wxPoint(r.left, r.top);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
if ( endDialog )
|
||||
break;
|
||||
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
} // Leave shared data critical section.
|
||||
|
||||
if ( endDialog )
|
||||
{
|
||||
::EndDialog( hwnd, IDCLOSE );
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
// Return anything.
|
||||
return 0;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
#endif // wxHAS_MSW_TASKDIALOG
|
||||
|
@@ -986,10 +986,11 @@ void wxTopLevelWindowMSW::SetIcons(const wxIconBundle& icons)
|
||||
DoSelectAndSetIcon(icons, SM_CXICON, SM_CYICON, ICON_BIG);
|
||||
}
|
||||
|
||||
bool wxTopLevelWindowMSW::EnableCloseButton(bool enable)
|
||||
// static
|
||||
bool wxTopLevelWindowMSW::MSWEnableCloseButton(WXHWND hwnd, bool enable)
|
||||
{
|
||||
// get system (a.k.a. window) menu
|
||||
HMENU hmenu = GetSystemMenu(GetHwnd(), FALSE /* get it */);
|
||||
HMENU hmenu = GetSystemMenu(hwnd, FALSE /* get it */);
|
||||
if ( !hmenu )
|
||||
{
|
||||
// no system menu at all -- ok if we want to remove the close button
|
||||
@@ -1008,7 +1009,7 @@ bool wxTopLevelWindowMSW::EnableCloseButton(bool enable)
|
||||
return false;
|
||||
}
|
||||
// update appearance immediately
|
||||
if ( !::DrawMenuBar(GetHwnd()) )
|
||||
if ( !::DrawMenuBar(hwnd) )
|
||||
{
|
||||
wxLogLastError(wxT("DrawMenuBar"));
|
||||
}
|
||||
@@ -1016,6 +1017,11 @@ bool wxTopLevelWindowMSW::EnableCloseButton(bool enable)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool wxTopLevelWindowMSW::EnableCloseButton(bool enable)
|
||||
{
|
||||
return MSWEnableCloseButton(GetHwnd(), enable);
|
||||
}
|
||||
|
||||
// Window must have wxCAPTION and either wxCLOSE_BOX or wxSYSTEM_MENU for the
|
||||
// button to be visible. Also check for wxMAXIMIZE_BOX because we don't want
|
||||
// to enable a button that is excluded from the current style.
|
||||
|
Reference in New Issue
Block a user