Extract wxModalDialogHook from wx/testing.h into its own wx/modalhook.h, extend it to allow to be notified not only about showing modal dialogs but also about dismissing them and document it and show its use in the dialogs sample. Also replace all the WX_TESTING_SHOW_MODAL_HOOK macros occurrences with WX_HOOK_MODAL_DIALOG. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@74037 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
469 lines
14 KiB
C++
469 lines
14 KiB
C++
/////////////////////////////////////////////////////////////////////////////
|
|
// Name: src/msw/dialog.cpp
|
|
// Purpose: wxDialog class
|
|
// Author: Julian Smart
|
|
// Modified by:
|
|
// Created: 01/02/97
|
|
// RCS-ID: $Id$
|
|
// Copyright: (c) Julian Smart
|
|
// Licence: wxWindows licence
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
// ============================================================================
|
|
// declarations
|
|
// ============================================================================
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// headers
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// For compilers that support precompilation, includes "wx.h".
|
|
#include "wx/wxprec.h"
|
|
|
|
#ifdef __BORLANDC__
|
|
#pragma hdrstop
|
|
#endif
|
|
|
|
#include "wx/dialog.h"
|
|
#include "wx/modalhook.h"
|
|
|
|
#ifndef WX_PRECOMP
|
|
#include "wx/msw/wrapcdlg.h"
|
|
#include "wx/utils.h"
|
|
#include "wx/frame.h"
|
|
#include "wx/app.h"
|
|
#include "wx/button.h"
|
|
#include "wx/settings.h"
|
|
#include "wx/intl.h"
|
|
#include "wx/log.h"
|
|
#include "wx/toolbar.h"
|
|
#endif
|
|
|
|
#include "wx/msw/private.h"
|
|
#include "wx/evtloop.h"
|
|
#include "wx/scopedptr.h"
|
|
|
|
#if defined(__SMARTPHONE__) && defined(__WXWINCE__)
|
|
#include "wx/msw/wince/resources.h"
|
|
#endif // __SMARTPHONE__ && __WXWINCE__
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// wxWin macros
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// wxDialogModalData
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// this is simply a container for any data we need to implement modality which
|
|
// allows us to avoid changing wxDialog each time the implementation changes
|
|
class wxDialogModalData
|
|
{
|
|
public:
|
|
wxDialogModalData(wxDialog *dialog) : m_evtLoop(dialog) { }
|
|
|
|
void RunLoop()
|
|
{
|
|
m_evtLoop.Run();
|
|
}
|
|
|
|
void ExitLoop()
|
|
{
|
|
m_evtLoop.Exit();
|
|
}
|
|
|
|
private:
|
|
wxModalEventLoop m_evtLoop;
|
|
};
|
|
|
|
wxDEFINE_TIED_SCOPED_PTR_TYPE(wxDialogModalData)
|
|
|
|
// ============================================================================
|
|
// implementation
|
|
// ============================================================================
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// wxDialog construction
|
|
// ----------------------------------------------------------------------------
|
|
|
|
void wxDialog::Init()
|
|
{
|
|
m_isShown = false;
|
|
m_modalData = NULL;
|
|
#if wxUSE_TOOLBAR && defined(__POCKETPC__)
|
|
m_dialogToolBar = NULL;
|
|
#endif
|
|
#if wxUSE_DIALOG_SIZEGRIP
|
|
m_hGripper = 0;
|
|
#endif // wxUSE_DIALOG_SIZEGRIP
|
|
}
|
|
|
|
bool wxDialog::Create(wxWindow *parent,
|
|
wxWindowID id,
|
|
const wxString& title,
|
|
const wxPoint& pos,
|
|
const wxSize& size,
|
|
long style,
|
|
const wxString& name)
|
|
{
|
|
SetExtraStyle(GetExtraStyle() | wxTOPLEVEL_EX_DIALOG);
|
|
|
|
// All dialogs should really have this style
|
|
style |= wxTAB_TRAVERSAL;
|
|
|
|
if ( !wxTopLevelWindow::Create(parent, id, title, pos, size, style, name) )
|
|
return false;
|
|
|
|
if ( !m_hasFont )
|
|
SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
|
|
|
|
#if defined(__SMARTPHONE__) && defined(__WXWINCE__)
|
|
SetLeftMenu(wxID_OK, _("OK"));
|
|
#endif
|
|
#if wxUSE_TOOLBAR && defined(__POCKETPC__)
|
|
CreateToolBar();
|
|
#endif
|
|
|
|
#if wxUSE_DIALOG_SIZEGRIP
|
|
if ( HasFlag(wxRESIZE_BORDER) )
|
|
{
|
|
CreateGripper();
|
|
|
|
Connect(wxEVT_CREATE,
|
|
wxWindowCreateEventHandler(wxDialog::OnWindowCreate));
|
|
}
|
|
#endif // wxUSE_DIALOG_SIZEGRIP
|
|
|
|
return true;
|
|
}
|
|
|
|
wxDialog::~wxDialog()
|
|
{
|
|
// this will also reenable all the other windows for a modal dialog
|
|
Show(false);
|
|
|
|
#if wxUSE_DIALOG_SIZEGRIP
|
|
DestroyGripper();
|
|
#endif // wxUSE_DIALOG_SIZEGRIP
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// showing the dialogs
|
|
// ----------------------------------------------------------------------------
|
|
|
|
bool wxDialog::Show(bool show)
|
|
{
|
|
if ( show == IsShown() )
|
|
return false;
|
|
|
|
if ( !show && m_modalData )
|
|
{
|
|
// we need to do this before calling wxDialogBase version because if we
|
|
// had disabled other app windows, they must be reenabled right now as
|
|
// if they stay disabled Windows will activate another window (one
|
|
// which is enabled, anyhow) when we're hidden in the base class Show()
|
|
// and we will lose activation
|
|
m_modalData->ExitLoop();
|
|
}
|
|
|
|
if ( show )
|
|
{
|
|
if (CanDoLayoutAdaptation())
|
|
DoLayoutAdaptation();
|
|
|
|
// this usually will result in TransferDataToWindow() being called
|
|
// which will change the controls values so do it before showing as
|
|
// otherwise we could have some flicker
|
|
InitDialog();
|
|
}
|
|
|
|
wxDialogBase::Show(show);
|
|
|
|
if ( show )
|
|
{
|
|
// dialogs don't get WM_SIZE message from ::ShowWindow() for some
|
|
// reason so generate it ourselves for consistency with frames and
|
|
// dialogs in other ports
|
|
//
|
|
// NB: normally we should call it just the first time but doing it
|
|
// every time is simpler than keeping a flag
|
|
const wxSize size = GetClientSize();
|
|
::SendMessage(GetHwnd(), WM_SIZE,
|
|
SIZE_RESTORED, MAKELPARAM(size.x, size.y));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// show dialog modally
|
|
int wxDialog::ShowModal()
|
|
{
|
|
WX_HOOK_MODAL_DIALOG();
|
|
|
|
wxASSERT_MSG( !IsModal(), wxT("ShowModal() can't be called twice") );
|
|
|
|
Show();
|
|
|
|
// EndModal may have been called from InitDialog handler (called from
|
|
// inside Show()) and hidden the dialog back again
|
|
if ( IsShown() )
|
|
{
|
|
// enter and run the modal loop
|
|
wxDialogModalDataTiedPtr modalData(&m_modalData,
|
|
new wxDialogModalData(this));
|
|
modalData->RunLoop();
|
|
}
|
|
|
|
return GetReturnCode();
|
|
}
|
|
|
|
void wxDialog::EndModal(int retCode)
|
|
{
|
|
wxASSERT_MSG( IsModal(), wxT("EndModal() called for non modal dialog") );
|
|
|
|
SetReturnCode(retCode);
|
|
|
|
Hide();
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// wxDialog gripper handling
|
|
// ----------------------------------------------------------------------------
|
|
|
|
#if wxUSE_DIALOG_SIZEGRIP
|
|
|
|
void wxDialog::SetWindowStyleFlag(long style)
|
|
{
|
|
wxDialogBase::SetWindowStyleFlag(style);
|
|
|
|
if ( HasFlag(wxRESIZE_BORDER) )
|
|
CreateGripper();
|
|
else
|
|
DestroyGripper();
|
|
}
|
|
|
|
void wxDialog::CreateGripper()
|
|
{
|
|
if ( !m_hGripper )
|
|
{
|
|
// just create it here, it will be positioned and shown later
|
|
m_hGripper = (WXHWND)::CreateWindow
|
|
(
|
|
wxT("SCROLLBAR"),
|
|
wxT(""),
|
|
WS_CHILD |
|
|
WS_CLIPSIBLINGS |
|
|
SBS_SIZEGRIP |
|
|
SBS_SIZEBOX |
|
|
SBS_SIZEBOXBOTTOMRIGHTALIGN,
|
|
0, 0, 0, 0,
|
|
GetHwnd(),
|
|
0,
|
|
wxGetInstance(),
|
|
NULL
|
|
);
|
|
}
|
|
}
|
|
|
|
void wxDialog::DestroyGripper()
|
|
{
|
|
if ( m_hGripper )
|
|
{
|
|
// we used to have trouble with gripper appearing on top (and hence
|
|
// overdrawing) the other, real, dialog children -- check that this
|
|
// isn't the case automatically (but notice that this could be false if
|
|
// we're not shown at all as in this case ResizeGripper() might not
|
|
// have been called yet)
|
|
wxASSERT_MSG( !IsShown() ||
|
|
::GetWindow((HWND)m_hGripper, GW_HWNDNEXT) == 0,
|
|
wxT("Bug in wxWidgets: gripper should be at the bottom of Z-order") );
|
|
::DestroyWindow((HWND) m_hGripper);
|
|
m_hGripper = 0;
|
|
}
|
|
}
|
|
|
|
void wxDialog::ShowGripper(bool show)
|
|
{
|
|
wxASSERT_MSG( m_hGripper, wxT("shouldn't be called if we have no gripper") );
|
|
|
|
if ( show )
|
|
ResizeGripper();
|
|
|
|
::ShowWindow((HWND)m_hGripper, show ? SW_SHOW : SW_HIDE);
|
|
}
|
|
|
|
void wxDialog::ResizeGripper()
|
|
{
|
|
wxASSERT_MSG( m_hGripper, wxT("shouldn't be called if we have no gripper") );
|
|
|
|
HWND hwndGripper = (HWND)m_hGripper;
|
|
|
|
const wxRect rectGripper = wxRectFromRECT(wxGetWindowRect(hwndGripper));
|
|
const wxSize size = GetClientSize() - rectGripper.GetSize();
|
|
|
|
::SetWindowPos(hwndGripper, HWND_BOTTOM,
|
|
size.x, size.y,
|
|
rectGripper.width, rectGripper.height,
|
|
SWP_NOACTIVATE);
|
|
}
|
|
|
|
void wxDialog::OnWindowCreate(wxWindowCreateEvent& event)
|
|
{
|
|
if ( m_hGripper && IsShown() &&
|
|
event.GetWindow() && event.GetWindow()->GetParent() == this )
|
|
{
|
|
// Put gripper below the newly created child window
|
|
::SetWindowPos((HWND)m_hGripper, HWND_BOTTOM, 0, 0, 0, 0,
|
|
SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
|
|
}
|
|
|
|
event.Skip();
|
|
}
|
|
|
|
#endif // wxUSE_DIALOG_SIZEGRIP
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// wxWin event handlers
|
|
// ----------------------------------------------------------------------------
|
|
|
|
#ifdef __POCKETPC__
|
|
// Responds to the OK button in a PocketPC titlebar. This
|
|
// can be overridden, or you can change the id used for
|
|
// sending the event, by calling SetAffirmativeId.
|
|
bool wxDialog::DoOK()
|
|
{
|
|
const int idOk = GetAffirmativeId();
|
|
if ( EmulateButtonClickIfPresent(idOk) )
|
|
return true;
|
|
|
|
wxCommandEvent event(wxEVT_BUTTON, GetAffirmativeId());
|
|
event.SetEventObject(this);
|
|
|
|
return HandleWindowEvent(event);
|
|
}
|
|
#endif // __POCKETPC__
|
|
|
|
#if wxUSE_TOOLBAR && defined(__POCKETPC__)
|
|
// create main toolbar by calling OnCreateToolBar()
|
|
wxToolBar* wxDialog::CreateToolBar(long style, wxWindowID winid, const wxString& name)
|
|
{
|
|
m_dialogToolBar = OnCreateToolBar(style, winid, name);
|
|
|
|
return m_dialogToolBar;
|
|
}
|
|
|
|
// return a new toolbar
|
|
wxToolBar *wxDialog::OnCreateToolBar(long style,
|
|
wxWindowID winid,
|
|
const wxString& name)
|
|
{
|
|
return new wxToolMenuBar(this, winid,
|
|
wxDefaultPosition, wxDefaultSize,
|
|
style, name);
|
|
}
|
|
#endif
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// dialog Windows messages processing
|
|
// ---------------------------------------------------------------------------
|
|
|
|
WXLRESULT wxDialog::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam)
|
|
{
|
|
WXLRESULT rc = 0;
|
|
bool processed = false;
|
|
|
|
switch ( message )
|
|
{
|
|
#ifdef __WXWINCE__
|
|
// react to pressing the OK button in the title
|
|
case WM_COMMAND:
|
|
{
|
|
switch ( LOWORD(wParam) )
|
|
{
|
|
#ifdef __POCKETPC__
|
|
case IDOK:
|
|
processed = DoOK();
|
|
if (!processed)
|
|
processed = !Close();
|
|
#endif
|
|
#ifdef __SMARTPHONE__
|
|
case IDM_LEFT:
|
|
case IDM_RIGHT:
|
|
processed = HandleCommand( LOWORD(wParam) , 0 , NULL );
|
|
break;
|
|
#endif // __SMARTPHONE__
|
|
}
|
|
break;
|
|
}
|
|
#endif
|
|
case WM_CLOSE:
|
|
// if we can't close, tell the system that we processed the
|
|
// message - otherwise it would close us
|
|
processed = !Close();
|
|
break;
|
|
|
|
case WM_SIZE:
|
|
#if wxUSE_DIALOG_SIZEGRIP
|
|
if ( m_hGripper )
|
|
{
|
|
switch ( wParam )
|
|
{
|
|
case SIZE_MAXIMIZED:
|
|
ShowGripper(false);
|
|
break;
|
|
|
|
case SIZE_RESTORED:
|
|
ShowGripper(true);
|
|
}
|
|
}
|
|
#endif // wxUSE_DIALOG_SIZEGRIP
|
|
|
|
// the Windows dialogs unfortunately are not meant to be resizable
|
|
// at all and their standard class doesn't include CS_[VH]REDRAW
|
|
// styles which means that the window is not refreshed properly
|
|
// after the resize and no amount of WS_CLIPCHILDREN/SIBLINGS can
|
|
// help with it - so we have to refresh it manually which certainly
|
|
// creates flicker but at least doesn't show garbage on the screen
|
|
rc = wxWindow::MSWWindowProc(message, wParam, lParam);
|
|
processed = true;
|
|
if ( HasFlag(wxFULL_REPAINT_ON_RESIZE) )
|
|
{
|
|
::InvalidateRect(GetHwnd(), NULL, false /* erase bg */);
|
|
}
|
|
break;
|
|
|
|
#ifndef __WXMICROWIN__
|
|
case WM_SETCURSOR:
|
|
// we want to override the busy cursor for modal dialogs:
|
|
// typically, wxBeginBusyCursor() is called and then a modal dialog
|
|
// is shown, but the modal dialog shouldn't have hourglass cursor
|
|
if ( IsModal() && wxIsBusy() )
|
|
{
|
|
// set our cursor for all windows (but see below)
|
|
wxCursor cursor = m_cursor;
|
|
if ( !cursor.IsOk() )
|
|
cursor = wxCURSOR_ARROW;
|
|
|
|
::SetCursor(GetHcursorOf(cursor));
|
|
|
|
// in any case, stop here and don't let wxWindow process this
|
|
// message (it would set the busy cursor)
|
|
processed = true;
|
|
|
|
// but return false to tell the child window (if the event
|
|
// comes from one of them and not from ourselves) that it can
|
|
// set its own cursor if it has one: thus, standard controls
|
|
// (e.g. text ctrl) still have correct cursors in a dialog
|
|
// invoked while wxIsBusy()
|
|
rc = false;
|
|
}
|
|
break;
|
|
#endif // __WXMICROWIN__
|
|
}
|
|
|
|
if ( !processed )
|
|
rc = wxDialogBase::MSWWindowProc(message, wParam, lParam);
|
|
|
|
return rc;
|
|
}
|