#endif
#endif
-#include "wx/generic/msgdlgg.h"
-
#if wxUSE_COCOA_NATIVE_MSGDLG
#define wxMessageDialog wxCocoaMessageDialog
#else
+ #include "wx/generic/msgdlgg.h"
+
#define wxMessageDialog wxGenericMessageDialog
#endif
-//-------------------------------------------------------------------------
-// wxMsgDialog
-//-------------------------------------------------------------------------
-
+// ----------------------------------------------------------------------------
+// wxCocoaMessageDialog
+// ----------------------------------------------------------------------------
-class WXDLLIMPEXP_CORE wxCocoaMessageDialog: public wxMessageDialogBase
+class WXDLLIMPEXP_CORE wxCocoaMessageDialog
+ : public wxMessageDialogWithCustomLabels
{
- DECLARE_DYNAMIC_CLASS(wxCocoaMessageDialog)
- DECLARE_NO_COPY_CLASS(wxCocoaMessageDialog)
-
-
public:
wxCocoaMessageDialog(wxWindow *parent,
const wxString& message,
long style = wxOK|wxCENTRE,
const wxPoint& pos = wxDefaultPosition);
- virtual ~wxCocoaMessageDialog();
-
virtual int ShowModal();
- // customization of the message box
- virtual bool SetYesNoLabels(const wxString& yes,const wxString& no);
- virtual bool SetYesNoCancelLabels(const wxString& yes, const wxString& no, const wxString& cancel);
- virtual bool SetOKLabel(const wxString& ok);
- virtual bool SetOKCancelLabels(const wxString& ok, const wxString& cancel);
-
protected:
// not supported for message dialog
virtual void DoSetSize(int WXUNUSED(x), int WXUNUSED(y),
int WXUNUSED(width), int WXUNUSED(height),
int WXUNUSED(sizeFlags) = wxSIZE_AUTO) {}
-private:
- wxString m_yes,
- m_no,
- m_ok,
- m_cancel;
+ // override wxMessageDialogWithCustomLabels method to get rid of
+ // accelerators in the custom label strings
+ //
+ // VZ: I have no idea _why_ do we do this but the old version did and
+ // I didn't want to change the existing behaviour
+ virtual void DoSetCustomLabel(wxString& var, const wxString& value);
+ DECLARE_DYNAMIC_CLASS(wxCocoaMessageDialog)
+ DECLARE_NO_COPY_CLASS(wxCocoaMessageDialog)
};
#endif // _WX_MSGDLG_H_
m_extendedMessage,
m_caption;
long m_dialogStyle;
+
+ DECLARE_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(__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 wxString& yes,const wxString& no)
+ {
+ DoSetCustomLabel(m_yes, yes);
+ DoSetCustomLabel(m_no, no);
+ return true;
+ }
+
+ virtual bool SetYesNoCancelLabels(const wxString& yes,
+ const wxString& no,
+ const wxString& cancel)
+ {
+ DoSetCustomLabel(m_yes, yes);
+ DoSetCustomLabel(m_no, no);
+ DoSetCustomLabel(m_cancel, cancel);
+ return true;
+ }
+
+ virtual bool SetOKLabel(const wxString& ok)
+ {
+ DoSetCustomLabel(m_ok, ok);
+ return true;
+ }
+
+ virtual bool SetOKCancelLabels(const wxString& ok, const wxString& cancel)
+ {
+ DoSetCustomLabel(m_ok, ok);
+ DoSetCustomLabel(m_cancel, cancel);
+ return true;
+ }
+
+protected:
+ // test if any custom labels were set
+ bool HasCustomLabels() const
+ {
+ return !(m_ok.empty() && m_cancel.empty() &&
+ m_yes.empty() && m_no.empty());
+ }
+
+ // these functions return the label to be used for the button which is
+ // either a custom label explicitly set by the user or the default label,
+ // i.e. they always return a valid string
+ wxString GetYesLabel() const { return m_yes.empty() ? _("Yes") : m_yes; }
+ wxString GetNoLabel() const { return m_no.empty() ? _("No") : m_no; }
+ wxString GetOKLabel() const { return m_ok.empty() ? _("OK") : m_ok; }
+ wxString GetCancelLabel() const
+ { return m_cancel.empty() ? _("Cancel") : m_cancel; }
+
+private:
+ // 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)
+ virtual void DoSetCustomLabel(wxString& var, const wxString& value)
+ {
+ var = value;
+ }
+
+ // labels for the buttons, initially empty meaning that the defaults should
+ // be used, use GetYes/No/OK/CancelLabel() to access them
+ wxString m_yes,
+ m_no,
+ m_ok,
+ m_cancel;
+
+ DECLARE_NO_COPY_CLASS(wxMessageDialogWithCustomLabels)
+};
+
+#endif // ports needing wxMessageDialogWithCustomLabels
+
#if defined(__WX_COMPILING_MSGDLGG_CPP__) || \
defined(__WXUNIVERSAL__) || defined(__WXGPE__) || \
(defined(__WXGTK__) && !defined(__WXGTK20__))
#ifndef _WX_MSGBOXDLG_H_
#define _WX_MSGBOXDLG_H_
-class WXDLLIMPEXP_CORE wxMessageDialog : public wxMessageDialogBase
+class WXDLLIMPEXP_CORE wxMessageDialog : public wxMessageDialogWithCustomLabels
{
public:
wxMessageDialog(wxWindow *parent,
const wxString& caption = wxMessageBoxCaptionStr,
long style = wxOK|wxCENTRE,
const wxPoint& WXUNUSED(pos) = wxDefaultPosition)
- : wxMessageDialogBase(parent, message, caption, style)
+ : wxMessageDialogWithCustomLabels(parent, message, caption, style)
{
m_hook = NULL;
}
-
virtual int ShowModal();
private:
+ // hook procedure used to adjust the message box beyond what the standard
+ // MessageBox() function can do for us
static WXLRESULT wxCALLBACK HookFunction(int code, WXWPARAM, WXLPARAM);
+ // adjust the button labels
+ //
+ // this is called from HookFunction() and our HWND is valid at this moment
+ void AdjustButtonLabels();
+
+
WXHANDLE m_hook; // HHOOK used to position the message box
DECLARE_DYNAMIC_CLASS(wxMessageDialog)
virtual int ShowModal();
- // customization of the message box
- virtual bool SetYesNoLabels(const wxString& yes,const wxString& no);
- virtual bool SetYesNoCancelLabels(const wxString& yes, const wxString& no, const wxString& cancel);
- virtual bool SetOKLabel(const wxString& ok);
- virtual bool SetOKCancelLabels(const wxString& ok, const wxString& cancel);
-
protected:
// not supported for message dialog
virtual void DoSetSize(int WXUNUSED(x), int WXUNUSED(y),
int WXUNUSED(width), int WXUNUSED(height),
int WXUNUSED(sizeFlags) = wxSIZE_AUTO) {}
- // labels for the buttons
- wxString m_yes,
- m_no,
- m_ok,
- m_cancel;
-
DECLARE_DYNAMIC_CLASS(wxMessageDialog)
};
// ----------------------------------------------------------------------------
wxCocoaMessageDialog::wxCocoaMessageDialog(wxWindow *parent,
- const wxString& message,
- const wxString& caption,
- long style,
- const wxPoint& pos) : wxMessageDialogBase(parent,message,caption,style)
+ const wxString& message,
+ const wxString& caption,
+ long style,
+ const wxPoint& pos)
+ : wxMessageDialogWithCustomLabels(parent, message, caption, style)
{
- //m_caption = caption;
- //m_message = message;
-
- //wxTopLevelWindows.Append((wxWindowBase*)this);
wxTopLevelWindows.Append(this);
wxASSERT(CreateBase(parent,wxID_ANY,wxDefaultPosition,wxDefaultSize,style,wxDefaultValidator,wxDialogNameStr));
m_cocoaNSWindow = nil;
m_cocoaNSView = nil;
-
- m_yes = _("Yes");
- m_no = _("No");
- m_ok = _("OK");
- m_cancel = _("Cancel");
-
}
-wxCocoaMessageDialog::~wxCocoaMessageDialog()
+void wxCocoaMessageDialog::DoSetCustomLabel(wxString& var, const wxString& value)
{
+ wxMessageDialogWithCustomLabels::DoSetCustomLabel(var, value);
+
+ var.Replace("&", "");
}
int wxCocoaMessageDialog::ShowModal()
{
if ( style & wxNO_DEFAULT )
{
- [alert addButtonWithTitle:wxNSStringWithWxString(m_no)];
- [alert addButtonWithTitle:wxNSStringWithWxString(m_yes)];
+ [alert addButtonWithTitle:wxNSStringWithWxString(GetNoLabel())];
+ [alert addButtonWithTitle:wxNSStringWithWxString(GetYesLabel())];
buttonId[0] = wxID_NO;
buttonId[1] = wxID_YES;
}
else
{
- [alert addButtonWithTitle:wxNSStringWithWxString(m_yes)];
- [alert addButtonWithTitle:wxNSStringWithWxString(m_no)];
+ [alert addButtonWithTitle:wxNSStringWithWxString(GetYesLabel())];
+ [alert addButtonWithTitle:wxNSStringWithWxString(GetNoLabel())];
buttonId[0] = wxID_YES;
buttonId[1] = wxID_NO;
}
if (style & wxCANCEL)
{
- [alert addButtonWithTitle:wxNSStringWithWxString(m_cancel)];
+ [alert addButtonWithTitle:wxNSStringWithWxString(GetCancelLabel())];
buttonId[2] = wxID_CANCEL;
}
}
// the MSW implementation even shows an OK button if it is not specified, we'll do the same
buttonId[0] = wxID_OK;
// using null as default title does not work on earlier systems
- [alert addButtonWithTitle:wxNSStringWithWxString(m_ok)];
+ [alert addButtonWithTitle:wxNSStringWithWxString(GetOKLabel())];
if (style & wxCANCEL)
{
- [alert addButtonWithTitle:wxNSStringWithWxString(m_cancel)];
+ [alert addButtonWithTitle:wxNSStringWithWxString(GetCancelLabel())];
buttonId[1] = wxID_CANCEL;
}
}
return buttonId[ret-NSAlertFirstButtonReturn];
}
-bool wxCocoaMessageDialog::SetYesNoLabels(const wxString& yes,const wxString& no)
-{
- m_yes = yes;
- m_yes.Replace(_("&"),_(""));
- m_no = no;
- m_no.Replace(_("&"),_(""));
- return true;
-}
-bool wxCocoaMessageDialog::SetYesNoCancelLabels(const wxString& yes, const wxString& no, const wxString& cancel)
-{
- m_yes = yes;
- m_yes.Replace(_("&"),_(""));
- m_no = no;
- m_no.Replace(_("&"),_(""));
- m_cancel = cancel;
- m_cancel.Replace(_("&"),_(""));
- return true;
-}
-bool wxCocoaMessageDialog::SetOKLabel(const wxString& ok)
-{
- m_ok = ok;
- m_ok.Replace(_("&"),_(""));
- return true;
-}
-bool wxCocoaMessageDialog::SetOKCancelLabels(const wxString& ok, const wxString& cancel)
-{
- m_ok = ok;
- m_ok.Replace(_("&"),_(""));
- m_cancel = cancel;
- m_cancel.Replace(_("&"),_(""));
- return true;
-}
-
#endif // wxUSE_DIRDLG
#endif
#include "wx/msw/private.h"
+#include "wx/msw/private/button.h"
+#include "wx/msw/private/metrics.h"
+
+#if wxUSE_MSGBOX_HOOK
+ #include "wx/fontutil.h"
+#endif
// For MB_TASKMODAL
#ifdef __WXWINCE__
-#include "wx/msw/wince/missing.h"
+ #include "wx/msw/wince/missing.h"
#endif
IMPLEMENT_CLASS(wxMessageDialog, wxDialog)
const HHOOK hhook = (HHOOK)wnd->m_hook;
const LRESULT rc = ::CallNextHookEx(hhook, code, wParam, lParam);
- if ( code == HC_ACTION && lParam )
+ if ( code == HCBT_ACTIVATE )
+ {
+ // we won't need this hook any longer
+ ::UnhookWindowsHookEx(hhook);
+ wnd->m_hook = NULL;
+ HookMap().erase(tid);
+
+ wnd->SetHWND((HWND)wParam);
+
+ // centre the message box on its parent if requested
+ if ( wnd->GetMessageDialogStyle() & wxCENTER )
+ wnd->Center(); // center on parent
+ //else: default behaviour, center on screen
+
+ // also update the labels if necessary
+ if ( wnd->HasCustomLabels() )
+ wnd->AdjustButtonLabels();
+
+ // there seems to be no reason to leave it set
+ wnd->SetHWND(NULL);
+ }
+
+ return rc;
+}
+
+namespace
+{
+
+// helper of AdjustButtonLabels(): set window position expressed in screen
+// coordinates, whether the window is child or top level
+void MoveWindowToScreenRect(HWND hwnd, RECT rc)
+{
+ if ( const HWND hwndParent = ::GetAncestor(hwnd, GA_PARENT) )
+ {
+ // map to parent window coordinates (notice that a RECT is laid out as
+ // 2 consecutive POINTs)
+ ::MapWindowPoints(HWND_DESKTOP, hwndParent,
+ reinterpret_cast<POINT *>(&rc), 2);
+ }
+
+ ::MoveWindow(hwnd,
+ rc.left, rc.top,
+ rc.right - rc.left, rc.bottom - rc.top,
+ FALSE);
+}
+
+// helper of AdjustButtonLabels(): move the given window by dx
+//
+// works for both child and top level windows
+void OffsetWindow(HWND hwnd, int dx)
+{
+ RECT rc = wxGetWindowRect(hwnd);
+
+ rc.left += dx;
+ rc.right += dx;
+
+ MoveWindowToScreenRect(hwnd, rc);
+}
+
+} // anonymous namespace
+
+void wxMessageDialog::AdjustButtonLabels()
+{
+ // changing the button labels is the easy part but we also need to ensure
+ // that the buttons are big enough for the label strings and increase their
+ // size (and hence the size of the message box itself) if they are not
+
+ // TODO-RTL: check whether this works correctly in RTL
+
+ // the order in this array is the one in which buttons appear in the
+ // message box
+ const static struct ButtonAccessors
{
- const CWPRETSTRUCT * const s = (CWPRETSTRUCT *)lParam;
+ int id;
+ wxString (wxMessageDialog::*getter)() const;
+ }
+ buttons[] =
+ {
+ { IDYES, &wxMessageDialog::GetYesLabel },
+ { IDNO, &wxMessageDialog::GetNoLabel },
+ { IDOK, &wxMessageDialog::GetOKLabel },
+ { IDCANCEL, &wxMessageDialog::GetCancelLabel },
+ };
+
+ // this contains the amount by which we increased the message box width
+ int dx = 0;
- if ( s->message == HCBT_ACTIVATE )
+ const NONCLIENTMETRICS& ncm = wxMSWImpl::GetNonClientMetrics();
+ const wxFont fontMsgBox(wxNativeFontInfo(ncm.lfMessageFont));
+
+ // we want to use this font in GetTextExtent() calls below but we don't
+ // want to send WM_SETFONT to the message box, who knows how is it going to
+ // react to it (right now it doesn't seem to do anything but what if this
+ // changes)
+ wxWindowBase::SetFont(fontMsgBox);
+
+ for ( unsigned n = 0; n < WXSIZEOF(buttons); n++ )
+ {
+ const HWND hwndBtn = ::GetDlgItem(GetHwnd(), buttons[n].id);
+ if ( !hwndBtn )
+ continue; // it's ok, not all buttons are always present
+
+ const wxString label = (this->*buttons[n].getter)();
+ const wxSize sizeLabel = wxWindowBase::GetTextExtent(label);
+
+ // check if the button is big enough for this label
+ RECT rc = wxGetWindowRect(hwndBtn);
+ const int widthOld = rc.right - rc.left;
+ const int widthNew = wxMSWButton::GetFittingSize(this, sizeLabel).x;
+ const int dw = widthNew - widthOld;
+ if ( dw > 0 )
{
- // we won't need this hook any longer
- ::UnhookWindowsHookEx(hhook);
- wnd->m_hook = NULL;
- HookMap().erase(tid);
+ // we need to resize the button
+ rc.right += dw;
+ MoveWindowToScreenRect(hwndBtn, rc);
- if ( wnd->GetMessageDialogStyle() & wxCENTER )
+ // and also move all the other buttons
+ for ( unsigned m = n + 1; m < WXSIZEOF(buttons); m++ )
{
- wnd->SetHWND(s->hwnd);
- wnd->Center(); // center on parent
- wnd->SetHWND(NULL);
+ const HWND hwndBtnNext = ::GetDlgItem(GetHwnd(), buttons[m].id);
+ if ( hwndBtnNext )
+ OffsetWindow(hwndBtnNext, dw);
}
- //else: default behaviour, center on screen
+
+ dx += dw;
}
+
+ ::SetWindowText(hwndBtn, label.wx_str());
}
- return rc;
+
+ // resize the message box itself if needed
+ if ( dx )
+ OffsetWindow(GetHwnd(), dx);
+
+ // surprisingly, we don't need to resize the static text control, it seems
+ // to adjust itself to the new size, at least under Windows 2003
+ // (TODO: test if this happens on older Windows versions)
}
#endif // wxUSE_MSGBOX_HOOK
#if wxUSE_MSGBOX_HOOK
// install the hook if we need to position the dialog in a non-default way
- if ( wxStyle & wxCENTER )
+ // or change the labels
+ if ( (wxStyle & wxCENTER) || HasCustomLabels() )
{
const DWORD tid = ::GetCurrentThreadId();
- m_hook = ::SetWindowsHookEx(WH_CALLWNDPROCRET,
+ m_hook = ::SetWindowsHookEx(WH_CBT,
&wxMessageDialog::HookFunction, NULL, tid);
HookMap()[tid] = this;
}
const wxString& caption,
long style,
const wxPoint& WXUNUSED(pos))
- : wxMessageDialogBase(parent, message, caption, style)
+ : wxMessageDialogWithCustomLabels(parent, message, caption, style)
{
- m_yes = _("Yes");
- m_no = _("No");
- m_ok = _("OK");
- m_cancel = _("Cancel");
-}
-
-bool wxMessageDialog::SetYesNoLabels(const wxString& yes,const wxString& no)
-{
- m_yes = yes;
- m_no = no;
- return true;
-}
-
-bool wxMessageDialog::SetYesNoCancelLabels(const wxString& yes, const wxString& no, const wxString& cancel)
-{
- m_yes = yes;
- m_no = no;
- m_cancel = cancel;
- return true;
-}
-
-bool wxMessageDialog::SetOKLabel(const wxString& ok)
-{
- m_ok = ok;
- return true;
-}
-
-bool wxMessageDialog::SetOKCancelLabels(const wxString& ok, const wxString& cancel)
-{
- m_ok = ok;
- m_cancel = cancel;
- return true;
}
int wxMessageDialog::ShowModal()
wxCFStringRef cfTitle( msgtitle, GetFont().GetEncoding() );
wxCFStringRef cfText( msgtext, GetFont().GetEncoding() );
- wxCFStringRef cfNoString( m_no.c_str(), GetFont().GetEncoding() );
- wxCFStringRef cfYesString( m_yes.c_str(), GetFont().GetEncoding() );
- wxCFStringRef cfOKString( m_ok.c_str() , GetFont().GetEncoding()) ;
- wxCFStringRef cfCancelString( m_cancel.c_str(), GetFont().GetEncoding() );
+ wxCFStringRef cfNoString( GetNoLabel().c_str(), GetFont().GetEncoding() );
+ wxCFStringRef cfYesString( GetYesLabel().c_str(), GetFont().GetEncoding() );
+ wxCFStringRef cfOKString( GetOKLabel().c_str() , GetFont().GetEncoding()) ;
+ wxCFStringRef cfCancelString( GetCancelLabel().c_str(), GetFont().GetEncoding() );
int buttonId[4] = { 0, 0, 0, wxID_CANCEL /* time-out */ };
short result;
AlertStdCFStringAlertParamRec param;
- wxCFStringRef cfNoString( m_no.c_str(), GetFont().GetEncoding() );
- wxCFStringRef cfYesString( m_yes.c_str(), GetFont().GetEncoding() );
- wxCFStringRef cfOKString( m_ok.c_str(), GetFont().GetEncoding() );
- wxCFStringRef cfCancelString( m_cancel.c_str(), GetFont().GetEncoding() );
+ wxCFStringRef cfNoString( GetNoLabel().c_str(), GetFont().GetEncoding() );
+ wxCFStringRef cfYesString( GetYesLabel().c_str(), GetFont().GetEncoding() );
+ wxCFStringRef cfOKString( GetOKLabel().c_str(), GetFont().GetEncoding() );
+ wxCFStringRef cfCancelString( GetCancelLabel().c_str(), GetFont().GetEncoding() );
wxCFStringRef cfTitle( msgtitle, GetFont().GetEncoding() );
wxCFStringRef cfText( msgtext, GetFont().GetEncoding() );