wx Dialogs
==========

TODO: This page needs to made more clear.

This page discusses how to build dialog windows with wxWidgets so that they
behave like normal dialogs.

Links to relevant documentation:

  * <http://docs.wxwidgets.org/stable/wx_wxdialog.html>
  * <http://docs.wxwidgets.org/stable/wx_wxdialog.html#wxdialogsetaffirmativeid>
  * <http://docs.wxwidgets.org/stable/wx_wxcloseevent.html#wxcloseevent>
  * <http://docs.wxwidgets.org/stable/wx_wxbutton.html#wxbuttonsetdefault>

Buttons
-------

Usually:

  * 1 close

or

  * 1 accepting (affirmative id (wx.ID_OK)) - activated by ?
  * 1 discarding (escape id (wx.ID_CANCEL)) - activated by Esc press

(X) on windows
--------------

Raises close event.

C++ implementations
-------------------

    wxStdDialogButtonSizer *wxDialogBase::CreateStdDialogButtonSizer( long flags )
    {
        wxStdDialogButtonSizer *sizer = new wxStdDialogButtonSizer();

        wxButton *ok = NULL;
        wxButton *yes = NULL;
        wxButton *no = NULL;

        if (flags & wxOK)
        {
            ok = new wxButton(this, wxID_OK);
            sizer->AddButton(ok);
        }

        if (flags & wxCANCEL)
        {
            wxButton *cancel = new wxButton(this, wxID_CANCEL);
            sizer->AddButton(cancel);
        }

        if (flags & wxYES)
        {
            yes = new wxButton(this, wxID_YES);
            sizer->AddButton(yes);
        }

        if (flags & wxNO)
        {
            no = new wxButton(this, wxID_NO);
            sizer->AddButton(no);
        }

        if (flags & wxHELP)
        {
            wxButton *help = new wxButton(this, wxID_HELP);
            sizer->AddButton(help);
        }

        if (flags & wxNO_DEFAULT)
        {
            if (no)
            {
                no->SetDefault();
                no->SetFocus();
            }
        }
        else
        {
            if (ok)
            {
                ok->SetDefault();
                ok->SetFocus();
            }
            else if (yes)
            {
                yes->SetDefault();
                yes->SetFocus();
            }
        }

        if (flags & wxOK)
            SetAffirmativeId(wxID_OK);
        else if (flags & wxYES)
            SetAffirmativeId(wxID_YES);

        sizer->Realize();

        return sizer;
    }

and

    void wxStdDialogButtonSizer::AddButton(wxButton *mybutton)
    {
        switch (mybutton->GetId())
        {
            case wxID_OK:
            case wxID_YES:
            case wxID_SAVE:
                m_buttonAffirmative = mybutton;
                break;
            case wxID_APPLY:
                m_buttonApply = mybutton;
                break;
            case wxID_NO:
                m_buttonNegative = mybutton;
                break;
            case wxID_CANCEL:
                m_buttonCancel = mybutton;
                break;
            case wxID_HELP:
            case wxID_CONTEXT_HELP:
                m_buttonHelp = mybutton;
                break;
            default:
                break;
        }
    }

Solution
--------

The StdDialogButtonSizer only handles the layout of buttons in a platform
specific way. It has nothing to do with default or escape buttons.

In order to get correct accept and discard behaviour, the SetAffirmativeId and
SetEscapeId on the dialog has to be called. Default behaviour can be found
here:

  * <http://docs.wxwidgets.org/stable/wx_wxdialog.html#wxdialogsetaffirmativeid>

Interesting to note is that escape sometimes defaults to affirmative.

In order to make a button default, the SetDefault followed by SetFocus should
be called on the button. CreateStdDialogButtonSizer does this for you. If you
don't use it, you have to call those methods yourself.

Pressing (X) in dialog title bar causes the close event to be raised.
The default implementation simulates a button click with id = wx.ID_CANCEL:

  * <http://docs.wxwidgets.org/stable/wx_wxwindow.html#wxwindowclose>

Default implementation of close event looks like this:

    void wxDialogBase::OnButton(wxCommandEvent& event)
    {
        const int id = event.GetId();
        if ( id == GetAffirmativeId() )
        {
            AcceptAndClose();
        }
        else if ( id == wxID_APPLY )
        {
            if ( Validate() )
                TransferDataFromWindow();

            // TODO: disable the Apply button until things change again
        }
        else if ( id == GetEscapeId() ||
                    (id == wxID_CANCEL && GetEscapeId() == wxID_ANY) )
        {
            EndDialog(wxID_CANCEL);
        }
        else // not a standard button
        {
            event.Skip();
        }
    }

So if EscapeId is not cancel, the cancel event has to be handled anyway to
close the dialog. (Example solution in r67)
