#endif
#include "wx/msw/private.h"
+#include "wx/msw/private/keyboard.h"
#include "wx/msw/dcclient.h"
+#include "wx/private/textmeasure.h"
#if wxUSE_TOOLTIPS
#include "wx/tooltip.h"
#include "wx/caret.h"
#endif // wxUSE_CARET
+#if wxUSE_RADIOBOX
+ #include "wx/radiobox.h"
+#endif // wxUSE_RADIOBOX
+
#if wxUSE_SPINCTRL
#include "wx/spinctrl.h"
#endif // wxUSE_SPINCTRL
#include <windowsx.h>
#endif
-#if !defined __WXWINCE__ && !defined NEED_PBT_H
- #include <pbt.h>
-#endif
-
#if defined(__WXWINCE__)
#include "wx/msw/wince/missing.h"
#ifdef __POCKETPC__
#define wxHAS_XBUTTON
#endif
+#ifndef MAPVK_VK_TO_CHAR
+ #define MAPVK_VK_TO_CHAR 2
+#endif
+
// ---------------------------------------------------------------------------
// global variables
// ---------------------------------------------------------------------------
extern wxMenu *wxCurrentPopupMenu;
#endif
+#if wxUSE_UXTHEME
+// This is a hack used by the owner-drawn wxButton implementation to ensure
+// that the brush used for erasing its background is correctly aligned with the
+// control.
+wxWindowMSW *wxWindowBeingErased = NULL;
+#endif // wxUSE_UXTHEME
+
+namespace
+{
+
// true if we had already created the std colour map, used by
// wxGetStdColourMap() and wxWindow::OnSysColourChanged() (FIXME-MT)
-static bool gs_hasStdCmap = false;
+bool gs_hasStdCmap = false;
// last mouse event information we need to filter out the duplicates
#if wxUSE_MOUSEEVENT_HACK
-static struct MouseEventInfoDummy
+struct MouseEventInfoDummy
{
// mouse position (in screen coordinates)
wxPoint pos;
wxIntegerHash, wxIntegerEqual,
MSWMessageHandlers);
-static MSWMessageHandlers gs_messageHandlers;
+MSWMessageHandlers gs_messageHandlers;
// hash containing all our windows, it uses HWND keys and wxWindow* values
WX_DECLARE_HASH_MAP(HWND, wxWindow *,
wxPointerHash, wxPointerEqual,
WindowHandles);
-static WindowHandles gs_windowHandles;
+WindowHandles gs_windowHandles;
+
+#ifdef wxHAS_MSW_BACKGROUND_ERASE_HOOK
+
+// temporary override for WM_ERASEBKGND processing: we don't store this in
+// wxWindow itself as we don't need it during most of the time so don't
+// increase the size of all window objects unnecessarily
+WX_DECLARE_HASH_MAP(wxWindow *, wxWindow *,
+ wxPointerHash, wxPointerEqual,
+ EraseBgHooks);
+
+EraseBgHooks gs_eraseBgHooks;
+
+#endif // wxHAS_MSW_BACKGROUND_ERASE_HOOK
+
+// If this variable is strictly positive, EVT_CHAR_HOOK is not generated for
+// Escape key presses as it can't be intercepted because it's needed by some
+// currently shown window, e.g. IME entry.
+//
+// This is currently global as we allow using UI from the main thread only
+// anyhow but could be replaced with a thread-specific value in the future if
+// needed.
+int gs_modalEntryWindowCount = 0;
+
+} // anonymous namespace
// ---------------------------------------------------------------------------
// private functions
#endif // !__WXWINCE__
-#ifdef __WXWINCE__
-// On Windows CE, GetCursorPos can return an error, so use this function
-// instead
-bool GetCursorPosWinCE(POINT* pt)
+// GetCursorPos can return an error, so use this function
+// instead.
+// Error originally observed with WinCE, but later using Remote Desktop
+// to connect to XP.
+void wxGetCursorPosMSW(POINT* pt)
{
if (!GetCursorPos(pt))
{
+#ifdef __WXWINCE__
+ wxLogLastError(wxT("GetCursorPos"));
+#endif
DWORD pos = GetMessagePos();
- pt->x = LOWORD(pos);
- pt->y = HIWORD(pos);
+ // the coordinates may be negative in multi-monitor systems
+ pt->x = GET_X_LPARAM(pos);
+ pt->y = GET_Y_LPARAM(pos);
}
- return true;
}
-#endif
// ---------------------------------------------------------------------------
// event tables
// method
#ifdef __WXUNIVERSAL__
IMPLEMENT_ABSTRACT_CLASS(wxWindowMSW, wxWindowBase)
-#else // __WXMSW__
-#if wxUSE_EXTENDED_RTTI
-
-// windows that are created from a parent window during its Create method, eg. spin controls in a calendar controls
-// must never been streamed out separately otherwise chaos occurs. Right now easiest is to test for negative ids, as
-// windows with negative ids never can be recreated anyway
-
-bool wxWindowStreamingCallback( const wxObject *object, wxWriter * , wxPersister * , wxxVariantArray & )
-{
- const wxWindow * win = dynamic_cast<const wxWindow*>(object) ;
- if ( win && win->GetId() < 0 )
- return false ;
- return true ;
-}
-
-IMPLEMENT_DYNAMIC_CLASS_XTI_CALLBACK(wxWindow, wxWindowBase,"wx/window.h", wxWindowStreamingCallback)
-
-// make wxWindowList known before the property is used
-
-wxCOLLECTION_TYPE_INFO( wxWindow* , wxWindowList ) ;
-
-template<> void wxCollectionToVariantArray( wxWindowList const &theList, wxxVariantArray &value)
-{
- wxListCollectionToVariantArray<wxWindowList::compatibility_iterator>( theList , value ) ;
-}
-
-WX_DEFINE_FLAGS( wxWindowStyle )
-
-wxBEGIN_FLAGS( wxWindowStyle )
- // new style border flags, we put them first to
- // use them for streaming out
-
- wxFLAGS_MEMBER(wxBORDER_SIMPLE)
- wxFLAGS_MEMBER(wxBORDER_SUNKEN)
- wxFLAGS_MEMBER(wxBORDER_DOUBLE)
- wxFLAGS_MEMBER(wxBORDER_RAISED)
- wxFLAGS_MEMBER(wxBORDER_STATIC)
- wxFLAGS_MEMBER(wxBORDER_NONE)
-
- // old style border flags
- wxFLAGS_MEMBER(wxSIMPLE_BORDER)
- wxFLAGS_MEMBER(wxSUNKEN_BORDER)
- wxFLAGS_MEMBER(wxDOUBLE_BORDER)
- wxFLAGS_MEMBER(wxRAISED_BORDER)
- wxFLAGS_MEMBER(wxSTATIC_BORDER)
- wxFLAGS_MEMBER(wxBORDER)
-
- // standard window styles
- wxFLAGS_MEMBER(wxTAB_TRAVERSAL)
- wxFLAGS_MEMBER(wxCLIP_CHILDREN)
- wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW)
- wxFLAGS_MEMBER(wxWANTS_CHARS)
- wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE)
- wxFLAGS_MEMBER(wxALWAYS_SHOW_SB )
- wxFLAGS_MEMBER(wxVSCROLL)
- wxFLAGS_MEMBER(wxHSCROLL)
-
-wxEND_FLAGS( wxWindowStyle )
-
-wxBEGIN_PROPERTIES_TABLE(wxWindow)
- wxEVENT_PROPERTY( Close , wxEVT_CLOSE_WINDOW , wxCloseEvent)
- wxEVENT_PROPERTY( Create , wxEVT_CREATE , wxWindowCreateEvent )
- wxEVENT_PROPERTY( Destroy , wxEVT_DESTROY , wxWindowDestroyEvent )
- // Always constructor Properties first
-
- wxREADONLY_PROPERTY( Parent,wxWindow*, GetParent, EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
- wxPROPERTY( Id,wxWindowID, SetId, GetId, -1 /*wxID_ANY*/ , 0 /*flags*/ , wxT("Helpstring") , wxT("group") )
- wxPROPERTY( Position,wxPoint, SetPosition , GetPosition, wxDefaultPosition , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // pos
- wxPROPERTY( Size,wxSize, SetSize, GetSize, wxDefaultSize , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // size
- wxPROPERTY( WindowStyle , long , SetWindowStyleFlag , GetWindowStyleFlag , EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style
-
- // Then all relations of the object graph
-
- wxREADONLY_PROPERTY_COLLECTION( Children , wxWindowList , wxWindowBase* , GetWindowChildren , wxPROP_OBJECT_GRAPH /*flags*/ , wxT("Helpstring") , wxT("group"))
-
- // and finally all other properties
-
- wxPROPERTY( ExtraStyle , long , SetExtraStyle , GetExtraStyle , EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // extstyle
- wxPROPERTY( BackgroundColour , wxColour , SetBackgroundColour , GetBackgroundColour , EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // bg
- wxPROPERTY( ForegroundColour , wxColour , SetForegroundColour , GetForegroundColour , EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // fg
- wxPROPERTY( Enabled , bool , Enable , IsEnabled , wxxVariant((bool)true) , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
- wxPROPERTY( Shown , bool , Show , IsShown , wxxVariant((bool)true) , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
-#if 0
- // possible property candidates (not in xrc) or not valid in all subclasses
- wxPROPERTY( Title,wxString, SetTitle, GetTitle, wxEmptyString )
- wxPROPERTY( Font , wxFont , SetFont , GetWindowFont , )
- wxPROPERTY( Label,wxString, SetLabel, GetLabel, wxEmptyString )
- // MaxHeight, Width , MinHeight , Width
- // TODO switch label to control and title to toplevels
-
- wxPROPERTY( ThemeEnabled , bool , SetThemeEnabled , GetThemeEnabled , )
- //wxPROPERTY( Cursor , wxCursor , SetCursor , GetCursor , )
- // wxPROPERTY( ToolTip , wxString , SetToolTip , GetToolTipText , )
- wxPROPERTY( AutoLayout , bool , SetAutoLayout , GetAutoLayout , )
-
-
-
-#endif
-wxEND_PROPERTIES_TABLE()
-
-wxBEGIN_HANDLERS_TABLE(wxWindow)
-wxEND_HANDLERS_TABLE()
-
-wxCONSTRUCTOR_DUMMY(wxWindow)
-
-#else
- IMPLEMENT_DYNAMIC_CLASS(wxWindow, wxWindowBase)
-#endif
-#endif // __WXUNIVERSAL__/__WXMSW__
+#endif // __WXUNIVERSAL__
BEGIN_EVENT_TABLE(wxWindowMSW, wxWindowBase)
EVT_SYS_COLOUR_CHANGED(wxWindowMSW::OnSysColourChanged)
if ( !controlOnly
#if wxUSE_CONTROLS
- || parent->IsKindOf(CLASSINFO(wxControl))
+ || wxDynamicCast(parent, wxControl)
#endif // wxUSE_CONTROLS
)
{
void wxWindowMSW::DoEnable( bool enable )
{
- HWND hWnd = GetHwnd();
- if ( hWnd )
- ::EnableWindow(hWnd, (BOOL)enable);
+ MSWEnableHWND(GetHwnd(), enable);
+}
+
+bool wxWindowMSW::MSWEnableHWND(WXHWND hWnd, bool enable)
+{
+ if ( !hWnd )
+ return false;
+
+ // If disabling focused control, we move focus to the next one, as if the
+ // user pressed Tab. That's because we can't keep focus on a disabled
+ // control, Tab-navigation would stop working then.
+ if ( !enable && ::GetFocus() == hWnd )
+ Navigate();
+
+ return ::EnableWindow(hWnd, (BOOL)enable) != 0;
}
bool wxWindowMSW::Show(bool show)
wxShowEffect effect,
unsigned timeout)
{
+#if wxUSE_DYNLIB_CLASS
+ if ( effect == wxSHOW_EFFECT_NONE )
+ return Show(show);
+
if ( !wxWindowBase::Show(show) )
return false;
}
return true;
+#else // wxUSE_DYNLIB_CLASS
+ return Show(show);
+#endif
}
// Raise the window to the top of the Z order
return true;
}
+
bool wxWindowMSW::SetCursor(const wxCursor& cursor)
{
if ( !wxWindowBase::SetCursor(cursor) )
}
// don't "overwrite" busy cursor
- if ( m_cursor.Ok() && !wxIsBusy() )
+ if ( wxIsBusy() )
+ return true;
+
+ if ( m_cursor.IsOk() )
{
// normally we should change the cursor only if it's over this window
// but we should do it always if we capture the mouse currently
HWND hWnd = GetHwnd();
POINT point;
-#ifdef __WXWINCE__
- ::GetCursorPosWinCE(&point);
-#else
- ::GetCursorPos(&point);
-#endif
+ ::wxGetCursorPosMSW(&point);
RECT rect = wxGetWindowRect(hWnd);
}
//else: will be set later when the mouse enters this window
}
+ else // Invalid cursor: this means reset to the default one.
+ {
+ // To revert to the correct cursor we need to find the window currently
+ // under the cursor and ask it to set its cursor itself as only it
+ // knows what it is.
+ POINT pt;
+ wxGetCursorPosMSW(&pt);
+
+ const wxWindowMSW* win = wxFindWindowAtPoint(wxPoint(pt.x, pt.y));
+ if ( !win )
+ win = this;
+
+ ::SendMessage(GetHwndOf(win), WM_SETCURSOR,
+ (WPARAM)GetHwndOf(win),
+ MAKELPARAM(HTCLIENT, WM_MOUSEMOVE));
+ }
return true;
}
info.nMax = range - 1; // as both nMax and nMax are inclusive
info.nPos = pos;
- // enable the scrollbar if it had been disabled before by specifying
- // SIF_DISABLENOSCROLL below: as we can't know whether this had been
- // done or not just do it always
- ::EnableScrollBar(hwnd, WXOrientToSB(orient), ESB_ENABLE_BOTH);
+ // We normally also reenable scrollbar in case it had been previously
+ // disabled by specifying SIF_DISABLENOSCROLL below but we should only
+ // do this if it has valid range, otherwise it would be enabled but not
+ // do anything.
+ if ( range >= pageSize )
+ {
+ ::EnableScrollBar(hwnd, WXOrientToSB(orient), ESB_ENABLE_BOTH);
+ }
}
//else: leave all the fields to be 0
exstyleReal & WS_EX_TOPMOST ? HWND_TOPMOST
: HWND_NOTOPMOST,
0, 0, 0, 0,
- SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED) )
+ SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE |
+ SWP_FRAMECHANGED) )
{
wxLogLastError(wxT("SetWindowPos"));
}
// it doesn't seem useful to use WS_CLIPSIBLINGS here as we officially
// don't support overlapping windows and it only makes sense for them and,
// presumably, gives the system some extra work (to manage more clipping
- // regions), so avoid it alltogether
+ // regions), so avoid it altogether
if ( flags & wxVSCROLL )
{
// get the mouse position
POINT pt;
-#ifdef __WXWINCE__
- ::GetCursorPosWinCE(&pt);
-#else
- ::GetCursorPos(&pt);
-#endif
+ wxGetCursorPosMSW(&pt);
// find the window which currently has the cursor and go up the window
// chain until we find this window - or exhaust it
}
#endif // !HAVE_TRACKMOUSEEVENT
- if (wxUpdateUIEvent::CanUpdate(this) && IsShownOnScreen())
- UpdateWindowUI(wxUPDATE_UI_FROMIDLE);
+ wxWindowBase::OnInternalIdle();
}
// Set this window to be the child of 'parent'.
if ( m_pendingSize != wxDefaultSize )
{
// we need to calculate the client size corresponding to pending size
+ //
+ // FIXME: Unfortunately this doesn't work correctly for the maximized
+ // top level windows, the returned values are too small (e.g.
+ // under Windows 7 on a 1600*1200 screen with task bar on the
+ // right the pending size for a maximized window is 1538*1200
+ // and WM_NCCALCSIZE returns 1528*1172 even though the correct
+ // client size of such window is 1538*1182). No idea how to fix
+ // it though, setting WS_MAXIMIZE in GWL_STYLE before calling
+ // WM_NCCALCSIZE doesn't help and AdjustWindowRectEx() doesn't
+ // work in this direction neither. So we just have to live with
+ // the slightly wrong results and relayout the window when it
+ // gets finally shown in its maximized state (see #11762).
RECT rect;
rect.left = m_pendingPosition.x;
rect.top = m_pendingPosition.y;
if ( y )
*y = rect.bottom;
}
+
+ // The size of the client window can't be negative but ::GetClientRect()
+ // can return negative size for an extremely small (1x1) window with
+ // borders so ensure that we correct it here as having negative sizes is
+ // completely unexpected.
+ if ( x && *x < 0 )
+ *x = 0;
+ if ( y && *y < 0 )
+ *y = 0;
}
void wxWindowMSW::DoGetPosition(int *x, int *y) const
wxWindow * const parent = GetParent();
wxPoint pos;
+#if wxUSE_DEFERRED_SIZING
if ( m_pendingPosition != wxDefaultPosition )
{
pos = m_pendingPosition;
}
else // use current position
+#endif // wxUSE_DEFERRED_SIZING
{
RECT rect = wxGetWindowRect(GetHwnd());
int *externalLeading,
const wxFont *fontToUse) const
{
- wxASSERT_MSG( !fontToUse || fontToUse->Ok(),
- wxT("invalid font in GetTextExtent()") );
-
- HFONT hfontToUse;
- if ( fontToUse )
- hfontToUse = GetHfontOf(*fontToUse);
+ // ensure we work with a valid font
+ wxFont font;
+ if ( !fontToUse || !fontToUse->IsOk() )
+ font = GetFont();
else
- hfontToUse = GetHfontOf(GetFont());
-
- WindowHDC hdc(GetHwnd());
- SelectInHDC selectFont(hdc, hfontToUse);
+ font = *fontToUse;
- SIZE sizeRect;
- TEXTMETRIC tm;
- ::GetTextExtentPoint32(hdc, string.wx_str(), string.length(), &sizeRect);
- GetTextMetrics(hdc, &tm);
+ wxCHECK_RET( font.IsOk(), wxT("invalid font in GetTextExtent()") );
- if ( x )
- *x = sizeRect.cx;
- if ( y )
- *y = sizeRect.cy;
- if ( descent )
- *descent = tm.tmDescent;
- if ( externalLeading )
- *externalLeading = tm.tmExternalLeading;
+ const wxWindow* win = static_cast<const wxWindow*>(this);
+ wxTextMeasure txm(win, &font);
+ txm.GetTextExtent(string, x, y, descent, externalLeading);
}
// ---------------------------------------------------------------------------
bool wxWindowMSW::DoPopupMenu(wxMenu *menu, int x, int y)
{
- menu->SetInvokingWindow(this);
menu->UpdateUI();
+ wxPoint pt;
if ( x == wxDefaultCoord && y == wxDefaultCoord )
{
- wxPoint mouse = ScreenToClient(wxGetMousePosition());
- x = mouse.x; y = mouse.y;
+ pt = wxGetMousePosition();
+ }
+ else
+ {
+ pt = ClientToScreen(wxPoint(x, y));
}
- HWND hWnd = GetHwnd();
- HMENU hMenu = GetHmenuOf(menu);
- POINT point;
- point.x = x;
- point.y = y;
- ::ClientToScreen(hWnd, &point);
#if defined(__WXWINCE__)
static const UINT flags = 0;
#else // !__WXWINCE__
}
#endif // __WXWINCE__/!__WXWINCE__
- ::TrackPopupMenu(hMenu, flags, point.x, point.y, 0, hWnd, NULL);
+ ::TrackPopupMenu(GetHmenuOf(menu), flags, pt.x, pt.y, 0, GetHwnd(), NULL);
// we need to do it right now as otherwise the events are never going to be
// sent to wxCurrentPopupMenu from HandleCommand()
// for example) and so we do need to process the event immediately
wxYieldForCommandsOnly();
- menu->SetInvokingWindow(NULL);
-
return true;
}
WXLRESULT wxWindowMSW::MSWDefWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
{
+ WXLRESULT rc;
if ( m_oldWndProc )
- return ::CallWindowProc(CASTWNDPROC m_oldWndProc, GetHwnd(), (UINT) nMsg, (WPARAM) wParam, (LPARAM) lParam);
+ rc = ::CallWindowProc(CASTWNDPROC m_oldWndProc, GetHwnd(), (UINT) nMsg, (WPARAM) wParam, (LPARAM) lParam);
else
- return ::DefWindowProc(GetHwnd(), nMsg, wParam, lParam);
+ rc = ::DefWindowProc(GetHwnd(), nMsg, wParam, lParam);
+
+ // Special hack used by wxTextEntry auto-completion only: this event is
+ // sent after the normal keyboard processing so that its handler could use
+ // the updated contents of the text control, after taking the key that was
+ // pressed into account.
+ if ( nMsg == WM_CHAR )
+ {
+ wxKeyEvent event(CreateCharEvent(wxEVT_AFTER_CHAR, wParam, lParam));
+ HandleWindowEvent(event);
+ }
+
+ return rc;
}
bool wxWindowMSW::MSWProcessMessage(WXMSG* pMsg)
// combinations which are always processed)
LONG lDlgCode = ::SendMessage(msg->hwnd, WM_GETDLGCODE, 0, 0);
- // surprizingly, DLGC_WANTALLKEYS bit mask doesn't contain the
+ // surprisingly, DLGC_WANTALLKEYS bit mask doesn't contain the
// DLGC_WANTTAB nor DLGC_WANTARROWS bits although, logically,
// it, of course, implies them
if ( lDlgCode & DLGC_WANTALLKEYS )
// emulate the button click
btn = wxFindWinFromHandle(msg->hwnd);
}
-
- bProcess = false;
}
else // not a button itself, do we have default button?
{
return true;
}
+ // This "Return" key press won't be actually used for
+ // navigation so don't generate wxNavigationKeyEvent
+ // for it but still pass it to IsDialogMessage() as it
+ // may handle it in some other way (e.g. by playing the
+ // default error sound).
+ bProcess = false;
+
#endif // wxUSE_BUTTON
#ifdef __WXWINCE__
// trace all messages: useful for the debugging but noticeably slows down
// the code so don't do it by default
#if wxDEBUG_LEVEL >= 2
- wxLogTrace(wxTraceMessages,
+ // notice that we cast wParam and lParam to long to avoid mismatch with
+ // format specifiers in 64 bit builds where they are both int64 quantities
+ //
+ // casting like this loses information, of course, but it shouldn't matter
+ // much for this diagnostic code and it keeps the code simple
+ wxLogTrace("winmsg",
wxT("Processing %s(hWnd=%p, wParam=%08lx, lParam=%08lx)"),
- wxGetMessageName(message), hWnd, (long)wParam, lParam);
+ wxGetMessageName(message), hWnd, (long)wParam, (long)lParam);
#endif // wxDEBUG_LEVEL >= 2
wxWindowMSW *wnd = wxFindWinFromHandle(hWnd);
return rc;
}
-WXLRESULT wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam)
+bool
+wxWindowMSW::MSWHandleMessage(WXLRESULT *result,
+ WXUINT message,
+ WXWPARAM wParam,
+ WXLPARAM lParam)
{
// did we process the message?
bool processed = false;
}
}
break;
-#if 0
+
case WM_ENTERSIZEMOVE:
{
processed = HandleEnterSizeMove();
processed = HandleExitSizeMove();
}
break;
-#endif
+
case WM_SIZING:
{
LPRECT pRect = (LPRECT)lParam;
#if wxUSE_MOUSEWHEEL
case WM_MOUSEWHEEL:
- processed = HandleMouseWheel(wParam, lParam);
+ processed = HandleMouseWheel(wxMOUSE_WHEEL_VERTICAL, wParam, lParam);
break;
-#endif
+
+ case WM_MOUSEHWHEEL:
+ processed = HandleMouseWheel(wxMOUSE_WHEEL_HORIZONTAL, wParam, lParam);
+ break;
+#endif // wxUSE_MOUSEWHEEL
case WM_LBUTTONDOWN:
case WM_LBUTTONUP:
case MM_JOY1BUTTONUP:
case MM_JOY2BUTTONUP:
processed = HandleJoystickEvent(message,
- GET_X_LPARAM(lParam),
- GET_Y_LPARAM(lParam),
+ LOWORD(lParam),
+ HIWORD(lParam),
wParam);
break;
#endif // __WXMICROWIN__
case WM_SYSKEYDOWN:
case WM_KEYDOWN:
- // If this has been processed by an event handler, return 0 now
- // (we've handled it).
+ // Generate the key down event in any case.
m_lastKeydownProcessed = HandleKeyDown((WORD) wParam, lParam);
if ( m_lastKeydownProcessed )
{
+ // If it was processed by an event handler, we stop here,
+ // notably we intentionally don't generate char event then.
processed = true;
}
-
- if ( !processed )
+ else // key down event not handled
{
+ // Examine the event to decide whether we need to generate a
+ // char event for it ourselves or let Windows do it. Window
+ // mostly only does it for the keys which produce printable
+ // characters (although there are exceptions, e.g. VK_ESCAPE or
+ // VK_BACK (but not VK_DELETE)) while we do it for all keys
+ // except the modifier ones (the wisdom of this is debatable
+ // but by now this decision is enshrined forever due to
+ // backwards compatibility).
switch ( wParam )
{
- // we consider these messages "not interesting" to OnChar, so
- // just don't do anything more with them
+ // No wxEVT_CHAR events are generated for these keys at all.
case VK_SHIFT:
case VK_CONTROL:
case VK_MENU:
case VK_CAPITAL:
case VK_NUMLOCK:
case VK_SCROLL:
- processed = true;
- break;
- // avoid duplicate messages to OnChar for these ASCII keys:
- // they will be translated by TranslateMessage() and received
- // in WM_CHAR
+ // Windows will send us WM_CHAR for these ones so we'll
+ // generate wxEVT_CHAR for them later when we get it.
case VK_ESCAPE:
case VK_SPACE:
case VK_RETURN:
case VK_OEM_5:
case VK_OEM_6:
case VK_OEM_7:
+ case VK_OEM_102:
case VK_OEM_PLUS:
case VK_OEM_COMMA:
case VK_OEM_MINUS:
case VK_OEM_PERIOD:
- // but set processed to false, not true to still pass them
- // to the control's default window proc - otherwise
- // built-in keyboard handling won't work
- processed = false;
break;
#ifdef VK_APPS
#endif // VK_APPS
default:
- // do generate a CHAR event
- processed = HandleChar((WORD)wParam, lParam);
+ if ( (wParam >= '0' && wParam <= '9') ||
+ (wParam >= 'A' && wParam <= 'Z') )
+ {
+ // We'll get WM_CHAR for those later too.
+ break;
+ }
+
+ // But for the rest we won't get WM_CHAR later so we do
+ // need to generate the event right now.
+ wxKeyEvent event(wxEVT_CHAR);
+ InitAnyKeyEvent(event, wParam, lParam);
+
+ // Set the "extended" bit in lParam because we want to
+ // generate CHAR events with WXK_HOME and not
+ // WXK_NUMPAD_HOME even if the "Home" key on numpad was
+ // pressed.
+ event.m_keyCode = wxMSWKeyboard::VKToWX
+ (
+ wParam,
+ lParam | (KF_EXTENDED << 16)
+ );
+
+ // Don't produce events without any valid character
+ // code (even if this shouldn't normally happen...).
+ if ( event.m_keyCode != WXK_NONE )
+ processed = HandleWindowEvent(event);
}
}
if (message == WM_SYSKEYDOWN) // Let Windows still handle the SYSKEYs
}
else
{
- processed = HandleChar((WORD)wParam, lParam, true);
+ processed = HandleChar((WORD)wParam, lParam);
}
break;
+ case WM_IME_STARTCOMPOSITION:
+ // IME popup needs Escape as it should undo the changes in its
+ // entry window instead of e.g. closing the dialog for which the
+ // IME is used (and losing all the changes in the IME window).
+ gs_modalEntryWindowCount++;
+ break;
+
+ case WM_IME_ENDCOMPOSITION:
+ gs_modalEntryWindowCount--;
+ break;
+
#if wxUSE_HOTKEY
case WM_HOTKEY:
processed = HandleHotKey((WORD)wParam, lParam);
break;
case WM_ERASEBKGND:
- processed = HandleEraseBkgnd((WXHDC)wParam);
+ {
+#ifdef wxHAS_MSW_BACKGROUND_ERASE_HOOK
+ // check if an override was configured for this window
+ EraseBgHooks::const_iterator it = gs_eraseBgHooks.find(this);
+ if ( it != gs_eraseBgHooks.end() )
+ processed = it->second->MSWEraseBgHook((WXHDC)wParam);
+ else
+#endif // wxHAS_MSW_BACKGROUND_ERASE_HOOK
+ processed = HandleEraseBkgnd((WXHDC)wParam);
+ }
+
if ( processed )
{
// we processed the message, i.e. erased the background
}
if ( !processed )
+ return false;
+
+ *result = rc.result;
+
+ return true;
+}
+
+WXLRESULT wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam)
+{
+ WXLRESULT result;
+ if ( !MSWHandleMessage(&result, message, wParam, lParam) )
{
#if wxDEBUG_LEVEL >= 2
- wxLogTrace(wxTraceMessages, wxT("Forwarding %s to DefWindowProc."),
+ wxLogTrace("winmsg", wxT("Forwarding %s to DefWindowProc."),
wxGetMessageName(message));
#endif // wxDEBUG_LEVEL >= 2
- rc.result = MSWDefWindowProc(message, wParam, lParam);
+ result = MSWDefWindowProc(message, wParam, lParam);
}
- return rc.result;
+ return result;
}
// ----------------------------------------------------------------------------
{
}
-bool wxWindowMSW::MSWGetCreateWindowCoords(const wxPoint& pos,
+void wxWindowMSW::MSWGetCreateWindowCoords(const wxPoint& pos,
const wxSize& size,
int& x, int& y,
int& w, int& h) const
{
- // yes, those are just some arbitrary hardcoded numbers
- static const int DEFAULT_Y = 200;
-
- bool nonDefault = false;
+ // CW_USEDEFAULT can't be used for child windows so just position them at
+ // the origin by default
+ x = pos.x == wxDefaultCoord ? 0 : pos.x;
+ y = pos.y == wxDefaultCoord ? 0 : pos.y;
- if ( pos.x == wxDefaultCoord )
- {
- // if x is set to CW_USEDEFAULT, y parameter is ignored anyhow so we
- // can just as well set it to CW_USEDEFAULT as well
- x =
- y = CW_USEDEFAULT;
- }
- else
- {
- // OTOH, if x is not set to CW_USEDEFAULT, y shouldn't be set to it
- // neither because it is not handled as a special value by Windows then
- // and so we have to choose some default value for it
- x = pos.x;
- y = pos.y == wxDefaultCoord ? DEFAULT_Y : pos.y;
+ AdjustForParentClientOrigin(x, y);
- nonDefault = true;
- }
+ // We don't have any clearly good choice for the size by default neither
+ // but we must use something non-zero.
+ w = WidthDefault(size.x);
+ h = HeightDefault(size.y);
/*
NB: there used to be some code here which set the initial size of the
this and ignore any attempts to change the window size to the size it
already has - so no WM_SIZE would be sent.
*/
-
-
- // we don't use CW_USEDEFAULT here for several reasons:
- //
- // 1. it results in huge frames on modern screens (1000*800 is not
- // uncommon on my 1280*1024 screen) which is way too big for a half
- // empty frame of most of wxWidgets samples for example)
- //
- // 2. it is buggy for frames with wxFRAME_TOOL_WINDOW style for which
- // the default is for whatever reason 8*8 which breaks client <->
- // window size calculations (it would be nice if it didn't, but it
- // does and the simplest way to fix it seemed to change the broken
- // default size anyhow)
- //
- // 3. there is just no advantage in doing it: with x and y it is
- // possible that [future versions of] Windows position the new top
- // level window in some smart way which we can't do, but we can
- // guess a reasonably good size for a new window just as well
- // ourselves
-
- // However, on PocketPC devices, we must use the default
- // size if possible.
-#ifdef _WIN32_WCE
- if (size.x == wxDefaultCoord)
- w = CW_USEDEFAULT;
- else
- w = size.x;
- if (size.y == wxDefaultCoord)
- h = CW_USEDEFAULT;
- else
- h = size.y;
-#else
- if ( size.x == wxDefaultCoord || size.y == wxDefaultCoord)
- {
- nonDefault = true;
- }
- w = WidthDefault(size.x);
- h = HeightDefault(size.y);
-#endif
-
- AdjustForParentClientOrigin(x, y);
-
- return nonDefault;
}
WXHWND wxWindowMSW::MSWGetParent() const
m_hWnd = (WXHWND)::CreateWindowEx
(
extendedStyle,
- className.wx_str(),
- title ? title : m_windowName.wx_str(),
+ className.t_str(),
+ title ? title : m_windowName.t_str(),
style,
x, y, w, h,
(HWND)MSWGetParent(),
(
CP_ACP,
0, // no flags
- ttip.wx_str(),
+ ttip.t_str(),
tipLength,
buf,
WXSIZEOF(buf) - 1
{
m_dropTarget->Revoke(m_hWnd);
- delete m_dropTarget;
- m_dropTarget = NULL;
+ wxDELETE(m_dropTarget);
}
#endif // wxUSE_DRAG_AND_DROP
// first ask the user code - it may wish to set the cursor in some very
// specific way (for example, depending on the current position)
POINT pt;
-#ifdef __WXWINCE__
- if ( !::GetCursorPosWinCE(&pt))
-#else
- if ( !::GetCursorPos(&pt) )
-#endif
- {
- wxLogLastError(wxT("GetCursorPos"));
- }
+ wxGetCursorPosMSW(&pt);
int x = pt.x,
y = pt.y;
ScreenToClient(&x, &y);
wxSetCursorEvent event(x, y);
+ event.SetId(GetId());
+ event.SetEventObject(this);
bool processedEvtSetCursor = HandleWindowEvent(event);
if ( processedEvtSetCursor && event.HasCursor() )
// m_cursor if the user code caught EVT_SET_CURSOR() and returned
// nothing from it - this is a way to say that our cursor shouldn't
// be used for this point
- if ( !processedEvtSetCursor && m_cursor.Ok() )
+ if ( !processedEvtSetCursor && m_cursor.IsOk() )
{
hcursor = GetHcursorOf(m_cursor);
}
if ( !hcursor && !GetParent() )
{
const wxCursor *cursor = wxGetGlobalCursor();
- if ( cursor && cursor->Ok() )
+ if ( cursor && cursor->IsOk() )
{
hcursor = GetHcursorOf(*cursor);
}
// to.
wxLogNull logNo; // suppress error if we couldn't load the bitmap
wxBitmap stdColourBitmap(wxT("wxBITMAP_STD_COLOURS"));
- if ( stdColourBitmap.Ok() )
+ if ( stdColourBitmap.IsOk() )
{
// the pixels in the bitmap must correspond to wxSTD_COL_XXX!
wxASSERT_MSG( stdColourBitmap.GetWidth() == wxSTD_COL_MAX,
return s_cmap;
}
+#if wxUSE_UXTHEME && !defined(TMT_FILLCOLOR)
+ #define TMT_FILLCOLOR 3802
+ #define TMT_TEXTCOLOR 3803
+ #define TMT_BORDERCOLOR 3801
+#endif
+
+wxColour wxWindowMSW::MSWGetThemeColour(const wchar_t *themeName,
+ int themePart,
+ int themeState,
+ MSWThemeColour themeColour,
+ wxSystemColour fallback) const
+{
+#if wxUSE_UXTHEME
+ const wxUxThemeEngine* theme = wxUxThemeEngine::GetIfActive();
+ if ( theme )
+ {
+ int themeProperty = 0;
+
+ // TODO: Convert this into a table? Sure would be faster.
+ switch ( themeColour )
+ {
+ case ThemeColourBackground:
+ themeProperty = TMT_FILLCOLOR;
+ break;
+ case ThemeColourText:
+ themeProperty = TMT_TEXTCOLOR;
+ break;
+ case ThemeColourBorder:
+ themeProperty = TMT_BORDERCOLOR;
+ break;
+ default:
+ wxFAIL_MSG(wxT("unsupported theme colour"));
+ };
+
+ wxUxThemeHandle hTheme((const wxWindow *)this, themeName);
+ COLORREF col;
+ HRESULT hr = theme->GetThemeColor
+ (
+ hTheme,
+ themePart,
+ themeState,
+ themeProperty,
+ &col
+ );
+
+ if ( SUCCEEDED(hr) )
+ return wxRGBToColour(col);
+
+ wxLogApiError(
+ wxString::Format(
+ "GetThemeColor(%s, %i, %i, %i)",
+ themeName, themePart, themeState, themeProperty),
+ hr);
+ }
+#else
+ wxUnusedVar(themeName);
+ wxUnusedVar(themePart);
+ wxUnusedVar(themeState);
+ wxUnusedVar(themeColour);
+#endif
+ return wxSystemSettings::GetColour(fallback);
+}
+
// ---------------------------------------------------------------------------
// painting
// ---------------------------------------------------------------------------
+// this variable is used to check that a paint event handler which processed
+// the event did create a wxPaintDC inside its code and called BeginPaint() to
+// validate the invalidated window area as otherwise we'd keep getting an
+// endless stream of WM_PAINT messages for this window resulting in a lot of
+// difficult to debug problems (e.g. impossibility to repaint other windows,
+// lack of timer and idle events and so on)
+extern bool wxDidCreatePaintDC;
+bool wxDidCreatePaintDC = false;
+
bool wxWindowMSW::HandlePaint()
{
HRGN hRegion = ::CreateRectRgn(0, 0, 0, 0); // Dummy call to get a handle
m_updateRegion = wxRegion((WXHRGN) hRegion);
+ wxDidCreatePaintDC = false;
+
wxPaintEvent event(m_windowId);
event.SetEventObject(this);
bool processed = HandleWindowEvent(event);
+ if ( processed && !wxDidCreatePaintDC )
+ {
+ // do call MSWDefWindowProc() to validate the update region to avoid
+ // the problems mentioned above
+ processed = false;
+ }
+
// note that we must generate NC event after the normal one as otherwise
// BeginPaint() will happily overwrite our decorations with the background
// colour
// be called from inside the event handlers called above)
m_updateRegion.Clear();
+ wxPaintDCImpl::EndPaint((wxWindow *)this);
+
return processed;
}
bool wxWindowMSW::HandleEraseBkgnd(WXHDC hdc)
{
- // standard non top level controls (i.e. except the dialogs) always erase
- // their background themselves in HandleCtlColor() or have some control-
- // specific ways to set the colours (common controls)
- if ( IsOfStandardClass() && !IsTopLevel() )
+ if ( IsBeingDeleted() )
+ {
+ // We can get WM_ERASEBKGND after starting the destruction of our top
+ // level parent. Handling it in this case is unnecessary and can be
+ // actually harmful as e.g. wxStaticBox::GetClientSize() doesn't work
+ // without a valid TLW parent (because it uses dialog units internally
+ // which use the dialog font), so just don't do anything then.
return false;
+ }
switch ( GetBackgroundStyle() )
{
case wxBG_STYLE_ERASE:
+ case wxBG_STYLE_COLOUR:
// we need to generate an erase background event
{
wxDCTemp dc(hdc, GetClientSize());
if ( rc )
{
- // background erase by the user-defined handler
+ // background erased by the user-defined handler
return true;
}
}
break;
case wxBG_STYLE_PAINT:
+ case wxBG_STYLE_TRANSPARENT:
// no need to do anything here at all, background will be entirely
// redrawn in WM_PAINT handler
break;
return true;
}
+#ifdef wxHAS_MSW_BACKGROUND_ERASE_HOOK
+
+bool wxWindowMSW::MSWHasEraseBgHook() const
+{
+ return gs_eraseBgHooks.find(const_cast<wxWindowMSW *>(this))
+ != gs_eraseBgHooks.end();
+}
+
+void wxWindowMSW::MSWSetEraseBgHook(wxWindow *child)
+{
+ if ( child )
+ {
+ if ( !gs_eraseBgHooks.insert(
+ EraseBgHooks::value_type(this, child)).second )
+ {
+ wxFAIL_MSG( wxT("Setting erase background hook twice?") );
+ }
+ }
+ else // reset the hook
+ {
+ if ( gs_eraseBgHooks.erase(this) != 1 )
+ {
+ wxFAIL_MSG( wxT("Resetting erase background which was not set?") );
+ }
+ }
+}
+
+#endif // wxHAS_MSW_BACKGROUND_ERASE_HOOK
+
bool wxWindowMSW::DoEraseBackground(WXHDC hDC)
{
HBRUSH hbr = (HBRUSH)MSWGetBgBrush(hDC);
if ( !hbr )
return false;
- wxFillRect(GetHwnd(), (HDC)hDC, hbr);
+ // erase just the client area of the window, this is important for the
+ // frames to avoid drawing over the toolbar part of the window (you might
+ // think using WS_CLIPCHILDREN would prevent this from happening, but it
+ // clearly doesn't)
+ RECT rc;
+ wxCopyRectToRECT(GetClientRect(), rc);
+ ::FillRect((HDC)hDC, &rc, hbr);
return true;
}
WXHBRUSH
-wxWindowMSW::MSWGetBgBrushForChild(WXHDC WXUNUSED(hDC), WXHWND hWnd)
+wxWindowMSW::MSWGetBgBrushForChild(WXHDC hDC, wxWindowMSW *child)
{
- if ( m_hasBgCol )
+ // Test for the custom background brush first.
+ WXHBRUSH hbrush = MSWGetCustomBgBrush();
+ if ( hbrush )
{
- // our background colour applies to:
- // 1. this window itself, always
- // 2. all children unless the colour is "not inheritable"
- // 3. even if it is not inheritable, our immediate transparent
- // children should still inherit it -- but not any transparent
- // children because it would look wrong if a child of non
- // transparent child would show our bg colour when the child itself
- // does not
- wxWindow *win = wxFindWinFromHandle(hWnd);
- if ( win == this ||
- m_inheritBgCol ||
- (win && win->HasTransparentBackground() &&
- win->GetParent() == this) )
- {
- // draw children with the same colour as the parent
- wxBrush *
- brush = wxTheBrushList->FindOrCreateBrush(GetBackgroundColour());
+ // We assume that this is either a stipple or hatched brush and not a
+ // solid one as otherwise it would have been enough to set the
+ // background colour and such brushes need to be positioned correctly
+ // in order to align when different windows are painted, so do it here.
+ RECT rc;
+ ::GetWindowRect(GetHwndOf(child), &rc);
+
+ ::MapWindowPoints(NULL, GetHwnd(), (POINT *)&rc, 1);
- return (WXHBRUSH)GetHbrushOf(*brush);
+ int x = rc.left,
+ y = rc.top;
+ MSWAdjustBrushOrg(&x, &y);
+
+ if ( !::SetBrushOrgEx((HDC)hDC, -x, -y, NULL) )
+ {
+ wxLogLastError(wxT("SetBrushOrgEx(bg brush)"));
}
+
+ return hbrush;
+ }
+
+ // Otherwise see if we have a custom background colour.
+ if ( m_hasBgCol )
+ {
+ wxBrush *
+ brush = wxTheBrushList->FindOrCreateBrush(GetBackgroundColour());
+
+ return (WXHBRUSH)GetHbrushOf(*brush);
}
return 0;
}
-WXHBRUSH wxWindowMSW::MSWGetBgBrush(WXHDC hDC, WXHWND hWndToPaint)
+WXHBRUSH wxWindowMSW::MSWGetBgBrush(WXHDC hDC)
{
- if ( !hWndToPaint )
- hWndToPaint = GetHWND();
+ // Use the special wxWindowBeingErased variable if it is set as the child
+ // being erased.
+ wxWindowMSW * const child =
+#if wxUSE_UXTHEME
+ wxWindowBeingErased ? wxWindowBeingErased :
+#endif
+ this;
for ( wxWindowMSW *win = this; win; win = win->GetParent() )
{
- WXHBRUSH hBrush = win->MSWGetBgBrushForChild(hDC, hWndToPaint);
+ WXHBRUSH hBrush = win->MSWGetBgBrushForChild(hDC, child);
if ( hBrush )
return hBrush;
+ // don't use the parent background if we're not transparent
+ if ( !win->HasTransparentBackground() )
+ break;
+
// background is not inherited beyond top level windows
if ( win->IsTopLevel() )
break;
};
#ifdef wxHAS_XBUTTON
- // the same messages are used for both auxillary mouse buttons so we need
+ // the same messages are used for both auxiliary mouse buttons so we need
// to adjust the index manually
switch ( msg )
{
}
-bool wxWindowMSW::HandleMouseWheel(WXWPARAM wParam, WXLPARAM lParam)
+bool
+wxWindowMSW::HandleMouseWheel(wxMouseWheelAxis axis,
+ WXWPARAM wParam, WXLPARAM lParam)
{
#if wxUSE_MOUSEWHEEL
// notice that WM_MOUSEWHEEL position is in screen coords (as it's
InitMouseEvent(event, pt.x, pt.y, LOWORD(wParam));
event.m_wheelRotation = (short)HIWORD(wParam);
event.m_wheelDelta = WHEEL_DELTA;
+ event.m_wheelAxis = axis;
static int s_linesPerRotation = -1;
if ( s_linesPerRotation == -1 )
state |= MK_RBUTTON;
POINT pt;
-#ifdef __WXWINCE__
- if ( !::GetCursorPosWinCE(&pt) )
-#else
- if ( !::GetCursorPos(&pt) )
-#endif
- {
- wxLogLastError(wxT("GetCursorPos"));
- }
+ wxGetCursorPosMSW(&pt);
// we need to have client coordinates here for symmetry with
// wxEVT_ENTER_WINDOW
// keyboard handling
// ---------------------------------------------------------------------------
-// create the key event of the given type for the given key - used by
-// HandleChar and HandleKeyDown/Up
-wxKeyEvent wxWindowMSW::CreateKeyEvent(wxEventType evType,
- int id,
- WXLPARAM lParam,
- WXWPARAM wParam) const
+namespace
{
- wxKeyEvent event(evType);
- event.SetId(GetId());
+
+// Implementation of InitAnyKeyEvent() which can also be used when there is no
+// associated window: this can happen for the wxEVT_CHAR_HOOK events created by
+// the global keyboard hook (e.g. the event might have happened in a non-wx
+// window).
+void
+MSWInitAnyKeyEvent(wxKeyEvent& event,
+ WXWPARAM wParam,
+ WXLPARAM lParam,
+ const wxWindowBase *win /* may be NULL */)
+{
+ if ( win )
+ {
+ event.SetId(win->GetId());
+ event.SetEventObject(const_cast<wxWindowBase *>(win));
+ }
+ else // No associated window.
+ {
+ // Use wxID_ANY for compatibility with the old code even if wxID_NONE
+ // would arguably make more sense.
+ event.SetId(wxID_ANY);
+ }
+
event.m_shiftDown = wxIsShiftDown();
event.m_controlDown = wxIsCtrlDown();
event.m_altDown = (HIWORD(lParam) & KF_ALTDOWN) == KF_ALTDOWN;
- event.SetEventObject((wxWindow *)this); // const_cast
- event.m_keyCode = id;
-#if wxUSE_UNICODE
- event.m_uniChar = (wxChar) wParam;
-#endif
event.m_rawCode = (wxUint32) wParam;
event.m_rawFlags = (wxUint32) lParam;
#ifndef __WXWINCE__
event.SetTimestamp(::GetMessageTime());
#endif
+}
- // translate the position to client coords
- POINT pt;
-#ifdef __WXWINCE__
- GetCursorPosWinCE(&pt);
-#else
- GetCursorPos(&pt);
-#endif
- RECT rect;
- GetWindowRect(GetHwnd(),&rect);
- pt.x -= rect.left;
- pt.y -= rect.top;
+} // anonymous namespace
+
+void
+wxWindowMSW::InitAnyKeyEvent(wxKeyEvent& event,
+ WXWPARAM wParam,
+ WXLPARAM lParam) const
+{
+ MSWInitAnyKeyEvent(event, wParam, lParam, this);
+}
- event.m_x = pt.x;
- event.m_y = pt.y;
+wxKeyEvent
+wxWindowMSW::CreateKeyEvent(wxEventType evType,
+ WXWPARAM wParam,
+ WXLPARAM lParam) const
+{
+ // Catch any attempts to use this with WM_CHAR, it wouldn't work because
+ // wParam is supposed to be a virtual key and not a character here.
+ wxASSERT_MSG( evType != wxEVT_CHAR && evType != wxEVT_CHAR_HOOK,
+ "CreateKeyEvent() can't be used for char events" );
+
+ wxKeyEvent event(evType);
+ InitAnyKeyEvent(event, wParam, lParam);
+
+ event.m_keyCode = wxMSWKeyboard::VKToWX
+ (
+ wParam,
+ lParam
+#if wxUSE_UNICODE
+ , &event.m_uniChar
+#endif // wxUSE_UNICODE
+ );
return event;
}
-// isASCII is true only when we're called from WM_CHAR handler and not from
-// WM_KEYDOWN one
-bool wxWindowMSW::HandleChar(WXWPARAM wParam, WXLPARAM lParam, bool isASCII)
+wxKeyEvent
+wxWindowMSW::CreateCharEvent(wxEventType evType,
+ WXWPARAM wParam,
+ WXLPARAM lParam) const
{
- int id;
- if ( isASCII )
+ wxKeyEvent event(evType);
+ InitAnyKeyEvent(event, wParam, lParam);
+
+#if wxUSE_UNICODE
+ // TODO: wParam uses UTF-16 so this is incorrect for characters outside of
+ // the BMP, we should use WM_UNICHAR to handle them.
+ event.m_uniChar = wParam;
+#endif // wxUSE_UNICODE
+
+ // Set non-Unicode key code too for compatibility if possible.
+ if ( wParam < 0x80 )
{
- id = wParam;
+ // It's an ASCII character, no need to translate it.
+ event.m_keyCode = wParam;
}
- else // we're called from WM_KEYDOWN
+ else
{
- // don't pass lParam to wxCharCodeMSWToWX() here because we don't want
- // to get numpad key codes: CHAR events should use the logical keys
- // such as WXK_HOME instead of WXK_NUMPAD_HOME which is for KEY events
- id = wxCharCodeMSWToWX(wParam);
- if ( id == 0 )
+ // Check if this key can be represented (as a single character) in the
+ // current locale.
+ const wchar_t wc = wParam;
+ char ch;
+ if ( wxConvLibc.FromWChar(&ch, 1, &wc, 1) != wxCONV_FAILED )
{
- // it's ASCII and will be processed here only when called from
- // WM_CHAR (i.e. when isASCII = true), don't process it now
- return false;
+ // For compatibility continue to provide the key code in this field
+ // even though using GetUnicodeKey() is recommended now.
+ event.m_keyCode = static_cast<unsigned char>(ch);
}
+ //else: Key can't be represented in the current locale, leave m_keyCode
+ // as WXK_NONE and use GetUnicodeKey() to access the character.
}
- wxKeyEvent event(CreateKeyEvent(wxEVT_CHAR, id, lParam, wParam));
-
// the alphanumeric keys produced by pressing AltGr+something on European
// keyboards have both Ctrl and Alt modifiers which may confuse the user
// code as, normally, keys with Ctrl and/or Alt don't result in anything
// KEY_DOWN event would still have the correct modifiers if they're really
// needed)
if ( event.m_controlDown && event.m_altDown &&
- (id >= 32 && id < 256) )
+ (event.m_keyCode >= 32 && event.m_keyCode < 256) )
{
event.m_controlDown =
event.m_altDown = false;
}
+ return event;
+}
+
+// isASCII is true only when we're called from WM_CHAR handler and not from
+// WM_KEYDOWN one
+bool wxWindowMSW::HandleChar(WXWPARAM wParam, WXLPARAM lParam)
+{
+ wxKeyEvent event(CreateCharEvent(wxEVT_CHAR, wParam, lParam));
return HandleWindowEvent(event);
}
bool wxWindowMSW::HandleKeyDown(WXWPARAM wParam, WXLPARAM lParam)
{
- int id = wxCharCodeMSWToWX(wParam, lParam);
-
- if ( !id )
- {
- // normal ASCII char
- id = wParam;
- }
-
- wxKeyEvent event(CreateKeyEvent(wxEVT_KEY_DOWN, id, lParam, wParam));
+ wxKeyEvent event(CreateKeyEvent(wxEVT_KEY_DOWN, wParam, lParam));
return HandleWindowEvent(event);
}
bool wxWindowMSW::HandleKeyUp(WXWPARAM wParam, WXLPARAM lParam)
{
- int id = wxCharCodeMSWToWX(wParam, lParam);
-
- if ( !id )
- {
- // normal ASCII char
- id = wParam;
- }
-
- wxKeyEvent event(CreateKeyEvent(wxEVT_KEY_UP, id, lParam, wParam));
+ wxKeyEvent event(CreateKeyEvent(wxEVT_KEY_UP, wParam, lParam));
return HandleWindowEvent(event);
}
#ifndef __WXWINCE__
const HMENU hmenu = (HMENU)lParam;
- MENUITEMINFO mii;
- wxZeroMemory(mii);
- mii.cbSize = sizeof(MENUITEMINFO);
+ WinStruct<MENUITEMINFO> mii;
// we could use MIIM_FTYPE here as we only need to know if the item is
// ownerdrawn or not and not dwTypeData which MIIM_TYPE also returns, but
// menu creation code
wxMenuItem *item = (wxMenuItem*)mii.dwItemData;
- const wxChar *p = wxStrchr(item->GetItemLabel().wx_str(), wxT('&'));
+ const wxString label(item->GetItemLabel());
+ const wxChar *p = wxStrchr(label.t_str(), wxT('&'));
while ( p++ )
{
if ( *p == wxT('&') )
}
wxJoystickEvent event(eventType, buttons, joystick, change);
- event.SetPosition(wxPoint(x, y));
+ if ( eventType == wxEVT_JOY_ZMOVE )
+ event.SetZPosition(x);
+ else
+ event.SetPosition(wxPoint(x, y));
event.SetEventObject(this);
return HandleWindowEvent(event);
// the_font.ReleaseResource();
}
-// use the "extended" bit (24) of lParam to distinguish extended keys
-// from normal keys as the same key is sent
-static inline
+// ----------------------------------------------------------------------------
+// keyboard codes
+// ----------------------------------------------------------------------------
+
+namespace wxMSWKeyboard
+{
+
+namespace
+{
+
+// use the "extended" bit of lParam to distinguish extended keys from normal
+// keys as the same virtual key code is sent for both by Windows
+inline
int ChooseNormalOrExtended(int lParam, int keyNormal, int keyExtended)
{
// except that if lParam is 0, it means we don't have real lParam from
// WM_KEYDOWN but are just translating just a VK constant (e.g. done from
// msw/treectrl.cpp when processing TVN_KEYDOWN) -- then assume this is a
// non-numpad (hence extended) key as this is a more common case
- return !lParam || (lParam & (1 << 24)) ? keyExtended : keyNormal;
+ return !lParam || (HIWORD(lParam) & KF_EXTENDED) ? keyExtended : keyNormal;
}
// this array contains the Windows virtual key codes which map one to one to
-// WXK_xxx constants and is used in wxCharCodeMSWToWX/WXToMSW() below
+// WXK_xxx constants and is used in wxMSWKeyboard::VKToWX/WXToVK() below
//
// note that keys having a normal and numpad version (e.g. WXK_HOME and
// WXK_NUMPAD_HOME) are not included in this table as the mapping is not 1-to-1
-static const struct wxKeyMapping
+const struct wxKeyMapping
{
int vk;
wxKeyCode wxk;
#endif // VK_APPS defined
};
-// Returns 0 if was a normal ASCII value, not a special key. This indicates that
-// the key should be ignored by WM_KEYDOWN and processed by WM_CHAR instead.
-int wxCharCodeMSWToWX(int vk, WXLPARAM lParam)
+} // anonymous namespace
+
+int VKToWX(WXWORD vk, WXLPARAM lParam, wchar_t *uc)
{
+ int wxk;
+
// check the table first
for ( size_t n = 0; n < WXSIZEOF(gs_specialKeys); n++ )
{
if ( gs_specialKeys[n].vk == vk )
- return gs_specialKeys[n].wxk;
+ {
+ wxk = gs_specialKeys[n].wxk;
+ if ( wxk < WXK_START )
+ {
+ // Unicode code for this key is the same as its ASCII code.
+ if ( uc )
+ *uc = wxk;
+ }
+
+ return wxk;
+ }
}
// keys requiring special handling
- int wxk;
switch ( vk )
{
- // the mapping for these keys may be incorrect on non-US keyboards so
- // maybe we shouldn't map them to ASCII values at all
- case VK_OEM_1: wxk = ';'; break;
- case VK_OEM_PLUS: wxk = '+'; break;
- case VK_OEM_COMMA: wxk = ','; break;
- case VK_OEM_MINUS: wxk = '-'; break;
- case VK_OEM_PERIOD: wxk = '.'; break;
- case VK_OEM_2: wxk = '/'; break;
- case VK_OEM_3: wxk = '~'; break;
- case VK_OEM_4: wxk = '['; break;
- case VK_OEM_5: wxk = '\\'; break;
- case VK_OEM_6: wxk = ']'; break;
- case VK_OEM_7: wxk = '\''; break;
+ case VK_OEM_1:
+ case VK_OEM_PLUS:
+ case VK_OEM_COMMA:
+ case VK_OEM_MINUS:
+ case VK_OEM_PERIOD:
+ case VK_OEM_2:
+ case VK_OEM_3:
+ case VK_OEM_4:
+ case VK_OEM_5:
+ case VK_OEM_6:
+ case VK_OEM_7:
+ case VK_OEM_102:
+ // MapVirtualKey() returns 0 if it fails to convert the virtual
+ // key which nicely corresponds to our WXK_NONE.
+ wxk = ::MapVirtualKey(vk, MAPVK_VK_TO_CHAR);
+
+ if ( HIWORD(wxk) & 0x8000 )
+ {
+ // It's a dead key and we don't return anything at all for them
+ // as we simply don't have any way to indicate the difference
+ // between e.g. a normal "'" and "'" as a dead key -- and
+ // generating the same events for them just doesn't seem like a
+ // good idea.
+ wxk = WXK_NONE;
+ }
+
+ // In any case return this as a Unicode character value.
+ if ( uc )
+ *uc = wxk;
+
+ // For compatibility with the old non-Unicode code we continue
+ // returning key codes for Latin-1 characters directly
+ // (normally it would really only make sense to do it for the
+ // ASCII characters, not Latin-1 ones).
+ if ( wxk > 255 )
+ {
+ // But for anything beyond this we can only return the key
+ // value as a real Unicode character, not a wxKeyCode
+ // because this enum values clash with Unicode characters
+ // (e.g. WXK_LBUTTON also happens to be U+012C a.k.a.
+ // "LATIN CAPITAL LETTER I WITH BREVE").
+ wxk = WXK_NONE;
+ }
+ break;
// handle extended keys
case VK_PRIOR:
case VK_DELETE:
wxk = ChooseNormalOrExtended(lParam, WXK_NUMPAD_DELETE, WXK_DELETE);
+
+ if ( uc )
+ *uc = WXK_DELETE;
break;
case VK_RETURN:
// don't use ChooseNormalOrExtended() here as the keys are reversed
// here: numpad enter is the extended one
- wxk = lParam && (lParam & (1 << 24)) ? WXK_NUMPAD_ENTER : WXK_RETURN;
+ wxk = HIWORD(lParam) & KF_EXTENDED ? WXK_NUMPAD_ENTER : WXK_RETURN;
+
+ if ( uc )
+ *uc = WXK_RETURN;
break;
default:
- wxk = 0;
+ if ( (vk >= '0' && vk <= '9') || (vk >= 'A' && vk <= 'Z') )
+ {
+ // A simple alphanumeric key and the values of them coincide in
+ // Windows and wx for both ASCII and Unicode codes.
+ wxk = vk;
+ }
+ else // Something we simply don't know about at all.
+ {
+ wxk = WXK_NONE;
+ }
+
+ if ( uc )
+ *uc = vk;
}
return wxk;
}
-WXWORD wxCharCodeWXToMSW(int wxk, bool *isVirtual)
+WXWORD WXToVK(int wxk, bool *isExtended)
{
- if ( isVirtual )
- *isVirtual = true;
-
// check the table first
for ( size_t n = 0; n < WXSIZEOF(gs_specialKeys); n++ )
{
if ( gs_specialKeys[n].wxk == wxk )
+ {
+ // All extended keys (i.e. non-numpad versions of the keys that
+ // exist both in the numpad and outside of it) are dealt with
+ // below.
+ if ( isExtended )
+ *isExtended = false;
+
return gs_specialKeys[n].vk;
+ }
}
// and then check for special keys not included in the table
+ bool extended = false;
WXWORD vk;
switch ( wxk )
{
case WXK_PAGEUP:
+ extended = true;
case WXK_NUMPAD_PAGEUP:
vk = VK_PRIOR;
break;
case WXK_PAGEDOWN:
+ extended = true;
case WXK_NUMPAD_PAGEDOWN:
vk = VK_NEXT;
break;
case WXK_END:
+ extended = true;
case WXK_NUMPAD_END:
vk = VK_END;
break;
case WXK_HOME:
+ extended = true;
case WXK_NUMPAD_HOME:
vk = VK_HOME;
break;
case WXK_LEFT:
+ extended = true;
case WXK_NUMPAD_LEFT:
vk = VK_LEFT;
break;
case WXK_UP:
+ extended = true;
case WXK_NUMPAD_UP:
vk = VK_UP;
break;
case WXK_RIGHT:
+ extended = true;
case WXK_NUMPAD_RIGHT:
vk = VK_RIGHT;
break;
case WXK_DOWN:
+ extended = true;
case WXK_NUMPAD_DOWN:
vk = VK_DOWN;
break;
case WXK_INSERT:
+ extended = true;
case WXK_NUMPAD_INSERT:
vk = VK_INSERT;
break;
case WXK_DELETE:
+ extended = true;
case WXK_NUMPAD_DELETE:
vk = VK_DELETE;
break;
else
#endif // !__WXWINCE__
{
- if ( isVirtual )
- *isVirtual = false;
vk = (WXWORD)wxk;
}
}
+ if ( isExtended )
+ *isExtended = extended;
+
return vk;
}
+} // namespace wxMSWKeyboard
+
// small helper for wxGetKeyState() and wxGetMouseState()
static inline bool wxIsKeyDown(WXWORD vk)
{
key != VK_MBUTTON,
wxT("can't use wxGetKeyState() for mouse buttons") );
- const WXWORD vk = wxCharCodeWXToMSW(key);
+ const WXWORD vk = wxMSWKeyboard::WXToVK(key);
// if the requested key is a LED key, return true if the led is pressed
if ( key == WXK_NUMLOCK || key == WXK_CAPITAL || key == WXK_SCROLL )
{
wxMouseState ms;
POINT pt;
- GetCursorPos( &pt );
+ wxGetCursorPosMSW(&pt);
ms.SetX(pt.x);
ms.SetY(pt.y);
win = wxFindWinFromHandle(hwnd);
if ( !win )
{
-#if wxUSE_RADIOBOX
+#if wxUSE_RADIOBOX && !defined(__WXUNIVERSAL__)
// native radiobuttons return DLGC_RADIOBUTTON here and for any
// wxWindow class which overrides WM_GETDLGCODE processing to
// do it as well, win would be already non NULL
if ( ::SendMessage(hwnd, WM_GETDLGCODE, 0, 0) & DLGC_RADIOBUTTON )
{
- win = (wxWindow *)wxGetWindowUserData(hwnd);
+ win = wxRadioBox::GetFromRadioButtonHWND(hwnd);
}
//else: it's a wxRadioButton, not a radiobutton from wxRadioBox
#endif // wxUSE_RADIOBOX
DWORD hiWord = HIWORD(lParam);
if ( nCode != HC_NOREMOVE && ((hiWord & KF_UP) == 0) )
{
- int id = wxCharCodeMSWToWX(wParam, lParam);
- if ( id != 0 )
+ wchar_t uc = 0;
+ int id = wxMSWKeyboard::VKToWX(wParam, lParam, &uc);
+
+ // Don't intercept keyboard entry (notably Escape) if a modal window
+ // (not managed by wx, e.g. IME one) is currently opened as more often
+ // than not it needs all the keys for itself.
+ //
+ // Also don't catch it if a window currently captures the mouse as
+ // Escape is normally used to release the mouse capture and if you
+ // really need to catch all the keys in the window that has mouse
+ // capture it can be easily done in its own EVT_CHAR handler as it is
+ // certain to have focus while it has the capture.
+ if ( !gs_modalEntryWindowCount && !::GetCapture() )
{
- wxKeyEvent event(wxEVT_CHAR_HOOK);
- if ( (HIWORD(lParam) & KF_ALTDOWN) == KF_ALTDOWN )
- event.m_altDown = true;
-
- event.SetEventObject(NULL);
- event.m_keyCode = id;
- event.m_shiftDown = wxIsShiftDown();
- event.m_controlDown = wxIsCtrlDown();
-#ifndef __WXWINCE__
- event.SetTimestamp(::GetMessageTime());
-#endif
- wxWindow *win = wxGetActiveWindow();
- wxEvtHandler *handler;
- if ( win )
- {
- handler = win->GetEventHandler();
- event.SetId(win->GetId());
- }
- else
+ if ( id != WXK_NONE
+#if wxUSE_UNICODE
+ || static_cast<int>(uc) != WXK_NONE
+#endif // wxUSE_UNICODE
+ )
{
- handler = wxTheApp;
- event.SetId(wxID_ANY);
- }
+ wxWindow const* win = wxWindow::DoFindFocus();
+ if ( !win )
+ {
+ // Even if the focus got lost somehow, still send the event
+ // to the top level parent to allow a wxDialog to always
+ // close on Escape.
+ win = wxGetActiveWindow();
+ }
- if ( handler && handler->ProcessEvent(event) )
- {
- // processed
- return 1;
+ wxKeyEvent event(wxEVT_CHAR_HOOK);
+ MSWInitAnyKeyEvent(event, wParam, lParam, win);
+
+ event.m_keyCode = id;
+#if wxUSE_UNICODE
+ event.m_uniChar = uc;
+#endif // wxUSE_UNICODE
+
+ wxEvtHandler * const handler = win ? win->GetEventHandler()
+ : wxTheApp;
+
+ if ( handler && handler->ProcessEvent(event) )
+ {
+ if ( !event.IsNextEventAllowed() )
+ {
+ // Stop processing of this event.
+ return 1;
+ }
+ }
}
}
}
pt2.y = pt.y;
HWND hWnd = ::WindowFromPoint(pt2);
+ if ( hWnd )
+ {
+ // WindowFromPoint() ignores the disabled children but we're supposed
+ // to take them into account, so check if we have a child at this
+ // coordinate using ChildWindowFromPointEx().
+ for ( ;; )
+ {
+ pt2.x = pt.x;
+ pt2.y = pt.y;
+ ::ScreenToClient(hWnd, &pt2);
+ HWND child = ::ChildWindowFromPointEx(hWnd, pt2, CWP_SKIPINVISIBLE);
+ if ( child == hWnd || !child )
+ break;
+
+ // ChildWindowFromPointEx() only examines the immediate children
+ // but we want to get the deepest (top in Z-order) one, so continue
+ // iterating for as long as it finds anything.
+ hWnd = child;
+ }
+ }
return wxGetWindowFromHWND((WXHWND)hWnd);
}
wxPoint wxGetMousePosition()
{
POINT pt;
-#ifdef __WXWINCE__
- GetCursorPosWinCE(&pt);
-#else
- GetCursorPos( & pt );
-#endif
+ wxGetCursorPosMSW(&pt);
return wxPoint(pt.x, pt.y);
}
bool wxWindowMSW::HandleHotKey(WXWPARAM wParam, WXLPARAM lParam)
{
- int hotkeyId = wParam;
- int virtualKey = HIWORD(lParam);
int win_modifiers = LOWORD(lParam);
- wxKeyEvent event(CreateKeyEvent(wxEVT_HOTKEY, virtualKey, wParam, lParam));
- event.SetId(hotkeyId);
+ wxKeyEvent event(CreateKeyEvent(wxEVT_HOTKEY, HIWORD(lParam)));
+ event.SetId(wParam);
event.m_shiftDown = (win_modifiers & MOD_SHIFT) != 0;
event.m_controlDown = (win_modifiers & MOD_CONTROL) != 0;
event.m_altDown = (win_modifiers & MOD_ALT) != 0;
#if wxUSE_STATBOX
static void wxAdjustZOrder(wxWindow* parent)
{
- if (parent->IsKindOf(CLASSINFO(wxStaticBox)))
+ if (wxDynamicCast(parent, wxStaticBox))
{
// Set the z-order correctly
SetWindowPos((HWND) parent->GetHWND(), HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);