Provide a task-dialog based wxMSW wxMessageDialog implementation.

Use the task dialog instead of the legacy message box for wxMessageDialog
implementation under wxMSW on recent (Vista and later) Windows versions.

As part of this change, remove wxMessageDialogWithCustomLabels and integrate
its functionality in wxMessageDialogBase itself as it's now used by all
platforms.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@65348 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Vadim Zeitlin 2010-08-18 22:48:28 +00:00
parent 5a607f8b23
commit ede7b01760
13 changed files with 364 additions and 118 deletions

View File

@ -24,13 +24,19 @@ public:
virtual int ShowModal();
protected:
// Creates a message dialog taking any options that have been set after
// object creation into account such as custom labels.
void DoCreateMsgdialog();
void OnYes(wxCommandEvent& event);
void OnNo(wxCommandEvent& event);
void OnCancel(wxCommandEvent& event);
private:
void DoCreateMsgdialog();
// can be overridden to provide more contents to the dialog
virtual void AddMessageDialogCheckBox(wxSizer *WXUNUSED(sizer)) { }
virtual void AddMessageDialogDetails(wxSizer *WXUNUSED(sizer)) { }
private:
wxPoint m_pos;
bool m_created;

View File

@ -12,7 +12,7 @@
#ifndef _WX_GTK_MSGDLG_H_
#define _WX_GTK_MSGDLG_H_
class WXDLLIMPEXP_CORE wxMessageDialog : public wxMessageDialogWithCustomLabels
class WXDLLIMPEXP_CORE wxMessageDialog : public wxMessageDialogBase
{
public:
wxMessageDialog(wxWindow *parent, const wxString& message,

View File

@ -84,7 +84,6 @@ public:
const int m_stockId;
};
// ctors
wxMessageDialogBase() { m_dialogStyle = 0; }
wxMessageDialogBase(wxWindow *parent,
@ -101,43 +100,22 @@ public:
// virtual dtor for the base class
virtual ~wxMessageDialogBase() { }
// methods for setting up more custom message dialogs -- all functions
// return false if they're not implemented
virtual bool SetYesNoLabels(const ButtonLabel& WXUNUSED(yes),
const ButtonLabel& WXUNUSED(no))
{
return false;
}
virtual bool SetYesNoCancelLabels(const ButtonLabel& WXUNUSED(yes),
const ButtonLabel& WXUNUSED(no),
const ButtonLabel& WXUNUSED(cancel))
{
return false;
}
virtual bool SetOKLabel(const ButtonLabel& WXUNUSED(ok))
{
return false;
}
virtual bool SetOKCancelLabels(const ButtonLabel& WXUNUSED(ok),
const ButtonLabel& WXUNUSED(cancel))
{
return false;
}
wxString GetCaption() const { return m_caption; }
virtual void SetMessage(const wxString& message)
{
m_message = message;
}
virtual void SetExtendedMessage(const wxString& extendedMessage)
wxString GetMessage() const { return m_message; }
void SetExtendedMessage(const wxString& extendedMessage)
{
m_extendedMessage = extendedMessage;
}
wxString GetExtendedMessage() const { return m_extendedMessage; }
// change the dialog style flag
void SetMessageDialogStyle(long style)
{
@ -165,74 +143,8 @@ public:
m_dialogStyle = style;
}
protected:
long GetMessageDialogStyle() const { return m_dialogStyle; }
// based on message dialog style, returns exactly one of: wxICON_NONE,
// wxICON_ERROR, wxICON_WARNING, wxICON_QUESTION, wxICON_INFORMATION
long GetEffectiveIcon() const
{
if ( m_dialogStyle & wxICON_NONE )
return wxICON_NONE;
else if ( m_dialogStyle & wxICON_ERROR )
return wxICON_ERROR;
else if ( m_dialogStyle & wxICON_WARNING )
return wxICON_WARNING;
else if ( m_dialogStyle & wxICON_QUESTION )
return wxICON_QUESTION;
else if ( m_dialogStyle & wxICON_INFORMATION )
return wxICON_INFORMATION;
else if ( m_dialogStyle & wxYES )
return wxICON_QUESTION;
else
return wxICON_INFORMATION;
}
// for the platforms not supporting separate main and extended messages
// this function should be used to combine both of them in a single string
wxString GetFullMessage() const
{
wxString msg = m_message;
if ( !m_extendedMessage.empty() )
msg << "\n\n" << m_extendedMessage;
return msg;
}
wxString m_message,
m_extendedMessage,
m_caption;
long m_dialogStyle;
wxDECLARE_NO_COPY_CLASS(wxMessageDialogBase);
};
// this is a helper class for native wxMessageDialog implementations which need
// to store the custom button labels as member variables and then use them in
// ShowModal() (there could conceivably be a port which would have some native
// functions for setting these labels immediately and we also don't need to
// store them at all if custom labels are not supported, which is why we do
// this in a separate class and not wxMessageDialogBase itself)
#if defined(__WXCOCOA__) || \
defined(__WXGTK20__) || \
defined(__WXMAC__) || \
defined(__WXMSW__)
class WXDLLIMPEXP_CORE wxMessageDialogWithCustomLabels
: public wxMessageDialogBase
{
public:
// ctors
wxMessageDialogWithCustomLabels() { }
wxMessageDialogWithCustomLabels(wxWindow *parent,
const wxString& message,
const wxString& caption,
long style)
: wxMessageDialogBase(parent, message, caption, style)
{
}
// customization of the message box buttons
virtual bool SetYesNoLabels(const ButtonLabel& yes,const ButtonLabel& no)
{
@ -265,7 +177,6 @@ public:
return true;
}
protected:
// test if any custom labels were set
bool HasCustomLabels() const
{
@ -285,6 +196,43 @@ protected:
wxString GetCancelLabel() const
{ return m_cancel.empty() ? GetDefaultCancelLabel() : m_cancel; }
// based on message dialog style, returns exactly one of: wxICON_NONE,
// wxICON_ERROR, wxICON_WARNING, wxICON_QUESTION, wxICON_INFORMATION
long GetEffectiveIcon() const
{
if ( m_dialogStyle & wxICON_NONE )
return wxICON_NONE;
else if ( m_dialogStyle & wxICON_ERROR )
return wxICON_ERROR;
else if ( m_dialogStyle & wxICON_WARNING )
return wxICON_WARNING;
else if ( m_dialogStyle & wxICON_QUESTION )
return wxICON_QUESTION;
else if ( m_dialogStyle & wxICON_INFORMATION )
return wxICON_INFORMATION;
else if ( m_dialogStyle & wxYES )
return wxICON_QUESTION;
else
return wxICON_INFORMATION;
}
protected:
// for the platforms not supporting separate main and extended messages
// this function should be used to combine both of them in a single string
wxString GetFullMessage() const
{
wxString msg = m_message;
if ( !m_extendedMessage.empty() )
msg << "\n\n" << m_extendedMessage;
return msg;
}
wxString m_message,
m_extendedMessage,
m_caption;
long m_dialogStyle;
// this function is called by our public SetXXXLabels() and should assign
// the value to var with possibly some transformation (e.g. Cocoa version
// currently uses this to remove any accelerators from the button strings
@ -309,15 +257,14 @@ private:
m_ok,
m_cancel;
wxDECLARE_NO_COPY_CLASS(wxMessageDialogWithCustomLabels);
wxDECLARE_NO_COPY_CLASS(wxMessageDialogBase);
};
#endif // ports needing wxMessageDialogWithCustomLabels
#include "wx/generic/msgdlgg.h"
#if defined(__WX_COMPILING_MSGDLGG_CPP__) || \
defined(__WXUNIVERSAL__) || defined(__WXGPE__) || \
(defined(__WXGTK__) && !defined(__WXGTK20__))
#include "wx/generic/msgdlgg.h"
#define wxMessageDialog wxGenericMessageDialog
#elif defined(__WXCOCOA__)

View File

@ -12,7 +12,7 @@
#ifndef _WX_MSGBOXDLG_H_
#define _WX_MSGBOXDLG_H_
class WXDLLIMPEXP_CORE wxMessageDialog : public wxMessageDialogWithCustomLabels
class WXDLLIMPEXP_CORE wxMessageDialog : public wxMessageDialogBase
{
public:
wxMessageDialog(wxWindow *parent,
@ -20,7 +20,7 @@ public:
const wxString& caption = wxMessageBoxCaptionStr,
long style = wxOK|wxCENTRE,
const wxPoint& WXUNUSED(pos) = wxDefaultPosition)
: wxMessageDialogWithCustomLabels(parent, message, caption, style)
: wxMessageDialogBase(parent, message, caption, style)
{
m_hook = NULL;
}
@ -55,11 +55,16 @@ private:
// offset all buttons starting from the first one given by dx to the right
void OffsetButtonsStartingFrom(int first, int dx);
// used by ShowModal() to display a message box when task dialogs
// aren't available.
int ShowMessageBox();
// used by ShowModal() to display a task dialog.
int ShowTaskDialog();
WXHANDLE m_hook; // HHOOK used to position the message box
DECLARE_DYNAMIC_CLASS(wxMessageDialog)
wxDECLARE_NO_COPY_CLASS(wxMessageDialog);
wxDECLARE_DYNAMIC_CLASS_NO_COPY(wxMessageDialog);
};

View File

@ -0,0 +1,91 @@
///////////////////////////////////////////////////////////////////////////////
// Name: wx/msw/private/msgdlg.h
// Purpose: helper functions used with native message dialog
// Author: Rickard Westerlund
// Created: 2010-07-12
// RCS-ID: $Id$
// Copyright: (c) 2010 wxWidgets team
// Licence: wxWindows licence
///////////////////////////////////////////////////////////////////////////////
#ifndef _WX_MSW_PRIVATE_MSGDLG_H_
#define _WX_MSW_PRIVATE_MSGDLG_H_
#include "wx/msw/wrapcctl.h"
#include "wx/scopedarray.h"
// Macro to help identify if task dialogs are available: we rely on
// TD_WARNING_ICON being defined in the headers for this as this symbol is used
// by the task dialogs only. Also notice that task dialogs are available for
// Unicode applications only.
#if defined(TD_WARNING_ICON) && wxUSE_UNICODE
#define wxHAS_MSW_TASKDIALOG
#endif
// Provides methods for creating a task dialog.
namespace wxMSWMessageDialog
{
#ifdef wxHAS_MSW_TASKDIALOG
class wxMSWTaskDialogConfig
{
public:
wxMSWTaskDialogConfig()
: buttons(new TASKDIALOG_BUTTON[3]),
parent(NULL),
iconId(0),
style(0),
useCustomLabels(false)
{ }
// initializes the object from a message dialog.
wxMSWTaskDialogConfig(const wxMessageDialogBase& dlg);
wxScopedArray<TASKDIALOG_BUTTON> buttons;
wxWindow *parent;
wxString caption;
wxString message;
wxString extendedMessage;
long iconId;
long style;
bool useCustomLabels;
wxString btnYesLabel;
wxString btnNoLabel;
wxString btnOKLabel;
wxString btnCancelLabel;
// Will create a task dialog with it's paremeters for it's creation
// stored in the provided TASKDIALOGCONFIG parameter.
// NOTE: The wxMSWTaskDialogConfig object needs to remain accessible
// during the subsequent call to TaskDialogIndirect().
void MSWCommonTaskDialogInit(TASKDIALOGCONFIG &tdc);
// Used by MSWCommonTaskDialogInit() to add a regular button or a
// button with a custom label if used.
void AddTaskDialogButton(TASKDIALOGCONFIG &tdc,
int btnCustomId,
int btnCommonId,
const wxString& customLabel);
}; // class wxMSWTaskDialogConfig
typedef HRESULT (WINAPI *TaskDialogIndirect_t)(const TASKDIALOGCONFIG *,
int *, int *, BOOL *);
// Return the pointer to TaskDialogIndirect(). This should only be called
// if HasNativeTaskDialog() returned true and is normally guaranteed to
// succeed in this case.
TaskDialogIndirect_t GetTaskDialogIndirectFunc();
#endif // wxHAS_MSW_TASKDIALOG
// Check if the task dialog is available: this simply checks the OS version
// as we know that it's only present in Vista and later.
bool HasNativeTaskDialog();
// Translates standard MSW button IDs like IDCANCEL into an equivalent
// wx constant such as wxCANCEL.
int MSWTranslateReturnCode(int msAns);
}; // namespace wxMSWMessageDialog
#endif // _WX_MSW_PRIVATE_MSGDLG_H_

View File

@ -13,7 +13,7 @@
#ifndef _WX_MSGBOXDLG_H_
#define _WX_MSGBOXDLG_H_
class WXDLLIMPEXP_CORE wxMessageDialog : public wxMessageDialogWithCustomLabels
class WXDLLIMPEXP_CORE wxMessageDialog : public wxMessageDialogBase
{
public:
wxMessageDialog(wxWindow *parent,

View File

@ -47,6 +47,8 @@
@style{wxICON_QUESTION}
Displays a question mark symbol. This icon is automatically used
with @c wxYES_NO so it's usually unnecessary to specify it explicitly.
This style is not supported for MSW task dialogs, as question icons do
not follow the guidelines. No icon will be displayed in this case.
@style{wxICON_INFORMATION}
Displays an information symbol. This icon is used by default if
@c wxYES_NO is not given so it is usually unnecessary to specify it
@ -56,7 +58,9 @@
just its parent (currently implemented only under MSW and GTK).
@style{wxCENTRE}
Centre the message box on its parent or on the screen if parent is not
specified (currently only implemented under MSW).
specified.
Setting this style under MSW makes no differences as the dialog is
always centered on the parent.
@endStyleTable
@library{wxcore}

View File

@ -16,7 +16,7 @@
#pragma hdrstop
#endif
#if wxUSE_MSGDLG && (!defined(__WXGTK20__) || defined(__WXUNIVERSAL__) || defined(__WXGPE__))
#if wxUSE_MSGDLG
#ifndef WX_PRECOMP
#include "wx/utils.h"
@ -103,7 +103,11 @@ void wxGenericMessageDialog::DoCreateMsgdialog()
topsizer->Add( icon_text, 1, wxCENTER | wxLEFT|wxRIGHT|wxTOP, 10 );
#endif // wxUSE_STATTEXT
// 3) buttons
// 3) optional checkbox and detailed text
AddMessageDialogCheckBox( topsizer );
AddMessageDialogDetails( topsizer );
// 4) buttons
int center_flag = wxEXPAND;
if (m_dialogStyle & wxYES_NO)
center_flag = wxALIGN_CENTRE;
@ -162,4 +166,4 @@ int wxGenericMessageDialog::ShowModal()
return wxMessageDialogBase::ShowModal();
}
#endif // wxUSE_MSGDLG && !defined(__WXGTK20__)
#endif // wxUSE_MSGDLG

View File

@ -44,7 +44,7 @@ wxMessageDialog::wxMessageDialog(wxWindow *parent,
const wxString& caption,
long style,
const wxPoint& WXUNUSED(pos))
: wxMessageDialogWithCustomLabels
: wxMessageDialogBase
(
GetParentForModalDialog(parent, style),
message,
@ -79,7 +79,7 @@ void wxMessageDialog::DoSetCustomLabel(wxString& var, const ButtonLabel& label)
int stockId = label.GetStockId();
if ( stockId == wxID_NONE )
{
wxMessageDialogWithCustomLabels::DoSetCustomLabel(var, label);
wxMessageDialogBase::DoSetCustomLabel(var, label);
var = wxConvertMnemonicsToGTK(var);
}
else // stock label

View File

@ -18,7 +18,7 @@
#if wxUSE_MSGDLG
#include "wx/msgdlg.h"
#include "wx/ptr_scpd.h"
// there is no hook support under CE so we can't use the code for message box
// positioning there
@ -38,9 +38,12 @@
#endif
#endif
#include "wx/dynlib.h"
#include "wx/msw/private.h"
#include "wx/msw/private/button.h"
#include "wx/msw/private/metrics.h"
#include "wx/msw/private/msgdlg.h"
#include "wx/msgdlg.h"
#if wxUSE_MSGBOX_HOOK
#include "wx/fontutil.h"
@ -53,6 +56,8 @@
#include "wx/msw/wince/missing.h"
#endif
using namespace wxMSWMessageDialog;
IMPLEMENT_CLASS(wxMessageDialog, wxDialog)
#if wxUSE_MSGBOX_HOOK
@ -430,7 +435,7 @@ wxFont wxMessageDialog::GetMessageFont()
return wxNativeFontInfo(ncm.lfMessageFont);
}
int wxMessageDialog::ShowModal()
int wxMessageDialog::ShowMessageBox()
{
if ( !wxTheApp->GetTopWindow() )
{
@ -564,11 +569,194 @@ int wxMessageDialog::ShowModal()
// do show the dialog
int msAns = MessageBox(hWnd, message.wx_str(), m_caption.wx_str(), msStyle);
return MSWTranslateReturnCode(msAns);
}
int wxMessageDialog::ShowTaskDialog()
{
#ifdef wxHAS_MSW_TASKDIALOG
TaskDialogIndirect_t taskDialogIndirect = GetTaskDialogIndirectFunc();
if ( !taskDialogIndirect )
return wxID_CANCEL;
WinStruct<TASKDIALOGCONFIG> tdc;
wxMSWTaskDialogConfig wxTdc( *this );
wxTdc.MSWCommonTaskDialogInit( tdc );
int msAns;
HRESULT hr = taskDialogIndirect( &tdc, &msAns, NULL, NULL );
if ( FAILED(hr) )
{
wxLogApiError( "TaskDialogIndirect", hr );
return wxID_CANCEL;
}
return MSWTranslateReturnCode( msAns );
#else
wxFAIL_MSG( "Task dialogs are unavailable." );
return wxID_CANCEL;
#endif // wxHAS_MSW_TASKDIALOG
}
int wxMessageDialog::ShowModal()
{
if ( HasNativeTaskDialog() )
return ShowTaskDialog();
return ShowMessageBox();
}
// ----------------------------------------------------------------------------
// Helpers of the wxMSWMessageDialog namespace
// ----------------------------------------------------------------------------
#ifdef wxHAS_MSW_TASKDIALOG
wxMSWTaskDialogConfig::wxMSWTaskDialogConfig(const wxMessageDialogBase& dlg)
: buttons(new TASKDIALOG_BUTTON[3])
{
parent = dlg.GetParentForModalDialog();
caption = dlg.GetCaption();
message = dlg.GetMessage();
extendedMessage = dlg.GetExtendedMessage();
iconId = dlg.GetEffectiveIcon();
style = dlg.GetMessageDialogStyle();
useCustomLabels = dlg.HasCustomLabels();
btnYesLabel = dlg.GetYesLabel();
btnNoLabel = dlg.GetNoLabel();
btnOKLabel = dlg.GetOKLabel();
btnCancelLabel = dlg.GetCancelLabel();
}
void wxMSWTaskDialogConfig::MSWCommonTaskDialogInit(TASKDIALOGCONFIG &tdc)
{
tdc.dwFlags = TDF_EXPAND_FOOTER_AREA | TDF_POSITION_RELATIVE_TO_WINDOW;
tdc.hInstance = wxGetInstance();
tdc.pszWindowTitle = caption.wx_str();
// use the top level window as parent if none specified
tdc.hwndParent = parent ? GetHwndOf(parent) : NULL;
if ( wxTheApp->GetLayoutDirection() == wxLayout_RightToLeft )
tdc.dwFlags |= TDF_RTL_LAYOUT;
tdc.pszMainInstruction = message.wx_str();
tdc.pszContent = extendedMessage.wx_str();
// set an icon to be used, if possible
switch ( iconId )
{
case wxICON_ERROR:
tdc.pszMainIcon = TD_ERROR_ICON;
break;
case wxICON_WARNING:
tdc.pszMainIcon = TD_WARNING_ICON;
break;
case wxICON_INFORMATION:
tdc.pszMainIcon = TD_INFORMATION_ICON;
break;
}
// custom label button array that can hold all buttons in use
tdc.pButtons = buttons.get();
if ( style & wxYES_NO )
{
AddTaskDialogButton(tdc, IDYES, TDCBF_YES_BUTTON, btnYesLabel);
AddTaskDialogButton(tdc, IDNO, TDCBF_NO_BUTTON, btnNoLabel);
if (style & wxCANCEL)
AddTaskDialogButton(tdc, IDCANCEL,
TDCBF_CANCEL_BUTTON, btnCancelLabel);
if ( style & wxNO_DEFAULT )
tdc.nDefaultButton = IDNO;
else if ( style & wxCANCEL_DEFAULT )
tdc.nDefaultButton = IDCANCEL;
}
else // without Yes/No we're going to have an OK button
{
AddTaskDialogButton(tdc, IDOK, TDCBF_OK_BUTTON, btnOKLabel);
if ( style & wxCANCEL )
{
AddTaskDialogButton(tdc, IDCANCEL,
TDCBF_CANCEL_BUTTON, btnCancelLabel);
if ( style & wxCANCEL_DEFAULT )
tdc.nDefaultButton = IDCANCEL;
}
}
}
void wxMSWTaskDialogConfig::AddTaskDialogButton(TASKDIALOGCONFIG &tdc,
int btnCustomId,
int btnCommonId,
const wxString& customLabel)
{
if ( useCustomLabels )
{
// use custom buttons to implement custom labels
TASKDIALOG_BUTTON &tdBtn = buttons[tdc.cButtons];
tdBtn.nButtonID = btnCustomId;
tdBtn.pszButtonText = customLabel.wx_str();
tdc.cButtons++;
}
else
{
tdc.dwCommonButtons |= btnCommonId;
}
}
// Task dialog can be used from different threads (and wxProgressDialog always
// uses it from another thread in fact) so protect access to the static
// variable below with a critical section.
wxCRIT_SECT_DECLARE(gs_csTaskDialogIndirect);
TaskDialogIndirect_t wxMSWMessageDialog::GetTaskDialogIndirectFunc()
{
static TaskDialogIndirect_t s_TaskDialogIndirect = NULL;
wxCRIT_SECT_LOCKER(lock, gs_csTaskDialogIndirect);
if ( !s_TaskDialogIndirect )
{
wxLoadedDLL dllComCtl32("comctl32.dll");
wxDL_INIT_FUNC(s_, TaskDialogIndirect, dllComCtl32);
// We must always succeed as this code is only executed under Vista and
// later which must have task dialog support.
wxASSERT_MSG( s_TaskDialogIndirect,
"Task dialog support unexpectedly not available" );
}
return s_TaskDialogIndirect;
}
#endif // wxHAS_MSW_TASKDIALOG
bool wxMSWMessageDialog::HasNativeTaskDialog()
{
#ifdef wxHAS_MSW_TASKDIALOG
return wxGetWinVersion() >= wxWinVersion_6;
#else
return false;
#endif
}
int wxMSWMessageDialog::MSWTranslateReturnCode(int msAns)
{
int ans;
switch (msAns)
{
default:
wxFAIL_MSG(wxT("unexpected ::MessageBox() return code"));
wxFAIL_MSG(wxT("unexpected return code"));
// fall through
case IDCANCEL:
@ -584,6 +772,7 @@ int wxMessageDialog::ShowModal()
ans = wxID_NO;
break;
}
return ans;
}

View File

@ -30,7 +30,7 @@ wxMessageDialog::wxMessageDialog(wxWindow *parent,
const wxString& caption,
long style,
const wxPoint& WXUNUSED(pos))
: wxMessageDialogWithCustomLabels(parent, message, caption, style)
: wxMessageDialogBase(parent, message, caption, style)
{
}

View File

@ -47,7 +47,7 @@ wxMessageDialog::wxMessageDialog(wxWindow *parent,
const wxString& caption,
long style,
const wxPoint& WXUNUSED(pos))
: wxMessageDialogWithCustomLabels(parent, message, caption, style)
: wxMessageDialogBase(parent, message, caption, style)
{
}

View File

@ -30,7 +30,7 @@ wxMessageDialog::wxMessageDialog(wxWindow *parent,
const wxString& caption,
long style,
const wxPoint& WXUNUSED(pos))
: wxMessageDialogWithCustomLabels(parent, message, caption, style)
: wxMessageDialogBase(parent, message, caption, style)
{
}