X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/d9698bd4acdee91faaca22c4246842ac9456f876..51ec29ea5d1d1093bafbae27a8483211a90d9659:/src/msw/window.cpp diff --git a/src/msw/window.cpp b/src/msw/window.cpp index 22913dd508..c7bdf169d2 100644 --- a/src/msw/window.cpp +++ b/src/msw/window.cpp @@ -79,6 +79,7 @@ #endif #include "wx/msw/private.h" +#include "wx/msw/private/keyboard.h" #include "wx/msw/dcclient.h" #if wxUSE_TOOLTIPS @@ -89,6 +90,10 @@ #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 @@ -108,10 +113,6 @@ #include #endif -#if !defined __WXWINCE__ && !defined NEED_PBT_H - #include -#endif - #if defined(__WXWINCE__) #include "wx/msw/wince/missing.h" #ifdef __POCKETPC__ @@ -151,16 +152,6 @@ #define HAVE_TRACKMOUSEEVENT #endif // everything needed for TrackMouseEvent() -// if this is set to 1, we use deferred window sizing to reduce flicker when -// resizing complicated window hierarchies, but this can in theory result in -// different behaviour than the old code so we keep the possibility to use it -// by setting this to 0 (in the future this should be removed completely) -#ifdef __WXWINCE__ -#define USE_DEFERRED_SIZING 0 -#else -#define USE_DEFERRED_SIZING 1 -#endif - // set this to 1 to filter out duplicate mouse events, e.g. mouse move events // when mouse position didnd't change #ifdef __WXWINCE__ @@ -175,6 +166,10 @@ #define wxHAS_XBUTTON #endif +#ifndef MAPVK_VK_TO_CHAR + #define MAPVK_VK_TO_CHAR 2 +#endif + // --------------------------------------------------------------------------- // global variables // --------------------------------------------------------------------------- @@ -183,13 +178,23 @@ 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; @@ -204,14 +209,38 @@ WX_DECLARE_HASH_MAP(int, wxWindow::MSWMessageHandler, 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 @@ -222,9 +251,9 @@ LRESULT WXDLLEXPORT APIENTRY _EXPORT wxWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); -#ifdef __WXDEBUG__ +#if wxDEBUG_LEVEL >= 2 const wxChar *wxGetMessageName(int message); -#endif //__WXDEBUG__ +#endif // wxDEBUG_LEVEL >= 2 void wxRemoveHandleAssociation(wxWindowMSW *win); extern void wxAssociateWinWithHandle(HWND hWnd, wxWindowMSW *win); @@ -251,7 +280,7 @@ static inline void wxBringWindowToTop(HWND hwnd) // raise top level parent to top of z order if (!::SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE)) { - wxLogLastError(_T("SetWindowPos")); + wxLogLastError(wxT("SetWindowPos")); } } @@ -288,20 +317,23 @@ static void EnsureParentHasControlParentStyle(wxWindow *parent) #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 @@ -311,119 +343,10 @@ bool GetCursorPosWinCE(POINT* pt) // 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(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( 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) - EVT_ERASE_BACKGROUND(wxWindowMSW::OnEraseBackground) #ifdef __WXWINCE__ EVT_INIT_DIALOG(wxWindowMSW::OnInitDialog) #endif @@ -518,19 +441,20 @@ bool wxWindowMSW::MSWCommand(WXUINT WXUNUSED(param), WXWORD WXUNUSED(id)) void wxWindowMSW::Init() { // MSW specific - m_isBeingDeleted = false; m_oldWndProc = NULL; m_mouseInWindow = false; m_lastKeydownProcessed = false; m_hWnd = 0; - m_hDWP = 0; m_xThumbSize = 0; m_yThumbSize = 0; +#if wxUSE_DEFERRED_SIZING + m_hDWP = 0; m_pendingPosition = wxDefaultPosition; m_pendingSize = wxDefaultSize; +#endif // wxUSE_DEFERRED_SIZING #ifdef __POCKETPC__ m_contextMenuEnabled = false; @@ -540,7 +464,7 @@ void wxWindowMSW::Init() // Destructor wxWindowMSW::~wxWindowMSW() { - m_isBeingDeleted = true; + SendDestroyEvent(); #ifndef __WXUNIVERSAL__ // VS: make sure there's no wxFrame with last focus set to us: @@ -574,7 +498,9 @@ wxWindowMSW::~wxWindowMSW() //if (::IsWindow(GetHwnd())) { if ( !::DestroyWindow(GetHwnd()) ) + { wxLogLastError(wxT("DestroyWindow")); + } } // remove hWnd <-> wxWindow association @@ -586,7 +512,7 @@ wxWindowMSW::~wxWindowMSW() /* static */ const wxChar *wxWindowMSW::MSWGetRegisteredClassName() { - return wxApp::GetRegisteredClassName(_T("wxWindow"), COLOR_BTNFACE); + return wxApp::GetRegisteredClassName(wxT("wxWindow"), COLOR_BTNFACE); } // real construction (Init() must have been called before!) @@ -637,15 +563,14 @@ bool wxWindowMSW::Create(wxWindow *parent, void wxWindowMSW::SetFocus() { HWND hWnd = GetHwnd(); - wxCHECK_RET( hWnd, _T("can't set focus to invalid window") ); + wxCHECK_RET( hWnd, wxT("can't set focus to invalid window") ); -#if !defined(__WXMICROWIN__) && !defined(__WXWINCE__) +#if !defined(__WXWINCE__) ::SetLastError(0); #endif if ( !::SetFocus(hWnd) ) { -#if defined(__WXDEBUG__) && !defined(__WXMICROWIN__) // was there really an error? DWORD dwRes = ::GetLastError(); if ( dwRes ) @@ -653,10 +578,9 @@ void wxWindowMSW::SetFocus() HWND hwndFocus = ::GetFocus(); if ( hwndFocus != hWnd ) { - wxLogApiError(_T("SetFocus"), dwRes); + wxLogApiError(wxT("SetFocus"), dwRes); } } -#endif // Debug } } @@ -691,9 +615,21 @@ wxWindow *wxWindowBase::DoFindFocus() 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) @@ -734,6 +670,9 @@ wxWindowMSW::MSWShowWithEffect(bool show, wxShowEffect effect, unsigned timeout) { + if ( effect == wxSHOW_EFFECT_NONE ) + return Show(show); + if ( !wxWindowBase::Show(show) ) return false; @@ -743,7 +682,7 @@ wxWindowMSW::MSWShowWithEffect(bool show, static bool s_initDone = false; if ( !s_initDone ) { - wxDynamicLibrary dllUser32(_T("user32.dll"), wxDL_VERBATIM | wxDL_QUIET); + wxDynamicLibrary dllUser32(wxT("user32.dll"), wxDL_VERBATIM | wxDL_QUIET); wxDL_INIT_FUNC(s_pfn, AnimateWindow, dllUser32); s_initDone = true; @@ -811,17 +750,17 @@ wxWindowMSW::MSWShowWithEffect(bool show, case wxSHOW_EFFECT_MAX: - wxFAIL_MSG( _T("invalid window show effect") ); + wxFAIL_MSG( wxT("invalid window show effect") ); return false; default: - wxFAIL_MSG( _T("unknown window show effect") ); + wxFAIL_MSG( wxT("unknown window show effect") ); return false; } if ( !(*s_pfnAnimateWindow)(GetHwnd(), timeout, dwFlags) ) { - wxLogLastError(_T("AnimateWindow")); + wxLogLastError(wxT("AnimateWindow")); return false; } @@ -855,7 +794,7 @@ void wxWindowMSW::DoReleaseMouse() { if ( !::ReleaseCapture() ) { - wxLogLastError(_T("ReleaseCapture")); + wxLogLastError(wxT("ReleaseCapture")); } } @@ -889,6 +828,7 @@ bool wxWindowMSW::SetFont(const wxFont& font) return true; } + bool wxWindowMSW::SetCursor(const wxCursor& cursor) { if ( !wxWindowBase::SetCursor(cursor) ) @@ -898,7 +838,10 @@ bool wxWindowMSW::SetCursor(const wxCursor& 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 @@ -908,11 +851,7 @@ bool wxWindowMSW::SetCursor(const wxCursor& cursor) HWND hWnd = GetHwnd(); POINT point; -#ifdef __WXWINCE__ - ::GetCursorPosWinCE(&point); -#else - ::GetCursorPos(&point); -#endif + ::wxGetCursorPosMSW(&point); RECT rect = wxGetWindowRect(hWnd); @@ -925,6 +864,22 @@ bool wxWindowMSW::SetCursor(const wxCursor& cursor) } //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; } @@ -935,7 +890,7 @@ void wxWindowMSW::WarpPointer(int x, int y) if ( !::SetCursorPos(x, y) ) { - wxLogLastError(_T("SetCursorPos")); + wxLogLastError(wxT("SetCursorPos")); } } @@ -963,6 +918,9 @@ void wxWindowMSW::MSWUpdateUIState(int action, int state) // scrolling stuff // --------------------------------------------------------------------------- +namespace +{ + inline int GetScrollPosition(HWND hWnd, int wOrient) { #ifdef __WXMICROWIN__ @@ -978,12 +936,19 @@ inline int GetScrollPosition(HWND hWnd, int wOrient) #endif } +inline UINT WXOrientToSB(int orient) +{ + return orient == wxHORIZONTAL ? SB_HORZ : SB_VERT; +} + +} // anonymous namespace + int wxWindowMSW::GetScrollPos(int orient) const { HWND hWnd = GetHwnd(); - wxCHECK_MSG( hWnd, 0, _T("no HWND in GetScrollPos") ); + wxCHECK_MSG( hWnd, 0, wxT("no HWND in GetScrollPos") ); - return GetScrollPosition(hWnd, orient == wxHORIZONTAL ? SB_HORZ : SB_VERT); + return GetScrollPosition(hWnd, WXOrientToSB(orient)); } // This now returns the whole range, not just the number @@ -994,19 +959,13 @@ int wxWindowMSW::GetScrollRange(int orient) const HWND hWnd = GetHwnd(); if ( !hWnd ) return 0; -#if 0 - ::GetScrollRange(hWnd, orient == wxHORIZONTAL ? SB_HORZ : SB_VERT, - &minPos, &maxPos); -#endif WinStruct scrollInfo; scrollInfo.fMask = SIF_RANGE; - if ( !::GetScrollInfo(hWnd, - orient == wxHORIZONTAL ? SB_HORZ : SB_VERT, - &scrollInfo) ) + if ( !::GetScrollInfo(hWnd, WXOrientToSB(orient), &scrollInfo) ) { // Most of the time this is not really an error, since the return // value can also be zero when there is no scrollbar yet. - // wxLogLastError(_T("GetScrollInfo")); + // wxLogLastError(wxT("GetScrollInfo")); } maxPos = scrollInfo.nMax; @@ -1022,7 +981,7 @@ int wxWindowMSW::GetScrollThumb(int orient) const void wxWindowMSW::SetScrollPos(int orient, int pos, bool refresh) { HWND hWnd = GetHwnd(); - wxCHECK_RET( hWnd, _T("SetScrollPos: no HWND") ); + wxCHECK_RET( hWnd, wxT("SetScrollPos: no HWND") ); WinStruct info; info.nPage = 0; @@ -1035,8 +994,7 @@ void wxWindowMSW::SetScrollPos(int orient, int pos, bool refresh) info.fMask |= SIF_DISABLENOSCROLL; } - ::SetScrollInfo(hWnd, orient == wxHORIZONTAL ? SB_HORZ : SB_VERT, - &info, refresh); + ::SetScrollInfo(hWnd, WXOrientToSB(orient), &info, refresh); } // New function that will replace some of the above. @@ -1046,28 +1004,41 @@ void wxWindowMSW::SetScrollbar(int orient, int range, bool refresh) { + // We have to set the variables here to make them valid in events + // triggered by ::SetScrollInfo() + *(orient == wxHORIZONTAL ? &m_xThumbSize : &m_yThumbSize) = pageSize; + + HWND hwnd = GetHwnd(); + if ( !hwnd ) + return; + WinStruct info; - info.nPage = pageSize; - info.nMin = 0; // range is nMax - nMin + 1 - info.nMax = range - 1; // as both nMax and nMax are inclusive - info.nPos = pos; + if ( range != -1 ) + { + info.nPage = pageSize; + info.nMin = 0; // range is nMax - nMin + 1 + info.nMax = range - 1; // as both nMax and nMax are inclusive + info.nPos = pos; + + // 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 + info.fMask = SIF_RANGE | SIF_PAGE | SIF_POS; - if ( HasFlag(wxALWAYS_SHOW_SB) ) + if ( HasFlag(wxALWAYS_SHOW_SB) || range == -1 ) { // disable scrollbar instead of removing it then info.fMask |= SIF_DISABLENOSCROLL; } - HWND hWnd = GetHwnd(); - if ( hWnd ) - { - // We have to set the variables here to make them valid in events - // triggered by ::SetScrollInfo() - *(orient == wxHORIZONTAL ? &m_xThumbSize : &m_yThumbSize) = pageSize; - - ::SetScrollInfo(hWnd, orient == wxHORIZONTAL ? SB_HORZ : SB_VERT, - &info, refresh); - } + ::SetScrollInfo(hwnd, WXOrientToSB(orient), &info, refresh); } void wxWindowMSW::ScrollWindow(int dx, int dy, const wxRect *prect) @@ -1143,7 +1114,7 @@ void wxWindowMSW::SetLayoutDirection(wxLayoutDirection dir) wxUnusedVar(dir); #else wxCHECK_RET( GetHwnd(), - _T("layout direction must be set after window creation") ); + wxT("layout direction must be set after window creation") ); LONG styleOld = wxGetWindowExStyle(this); @@ -1159,7 +1130,7 @@ void wxWindowMSW::SetLayoutDirection(wxLayoutDirection dir) break; default: - wxFAIL_MSG(_T("unsupported layout direction")); + wxFAIL_MSG(wxT("unsupported layout direction")); break; } @@ -1175,7 +1146,7 @@ wxLayoutDirection wxWindowMSW::GetLayoutDirection() const #ifdef __WXWINCE__ return wxLayout_Default; #else - wxCHECK_MSG( GetHwnd(), wxLayout_Default, _T("invalid window") ); + wxCHECK_MSG( GetHwnd(), wxLayout_Default, wxT("invalid window") ); return wxHasWindowExStyle(this, WS_EX_LAYOUTRTL) ? wxLayout_RightToLeft : wxLayout_LeftToRight; @@ -1258,7 +1229,9 @@ void wxWindowMSW::AssociateHandle(WXWidget handle) if ( m_hWnd ) { if ( !::DestroyWindow(GetHwnd()) ) + { wxLogLastError(wxT("DestroyWindow")); + } } WXHWND wxhwnd = (WXHWND)handle; @@ -1282,7 +1255,7 @@ bool wxCheckWindowWndProc(WXHWND hWnd, // TODO: get rid of wxTLWHiddenParent special case (currently it's not // registered by wxApp but using ad hoc code in msw/toplevel.cpp); // there is also a hidden window class used by sockets &c - return wxApp::IsRegisteredClassName(str) || str == _T("wxTLWHiddenParent"); + return wxApp::IsRegisteredClassName(str) || str == wxT("wxTLWHiddenParent"); } // ---------------------------------------------------------------------------- @@ -1387,9 +1360,10 @@ void wxWindowMSW::MSWUpdateStyle(long flagsOld, long exflagsOld) 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(_T("SetWindowPos")); + wxLogLastError(wxT("SetWindowPos")); } } } @@ -1483,7 +1457,7 @@ WXDWORD wxWindowMSW::MSWGetStyle(long flags, WXDWORD *exstyle) const { default: case wxBORDER_DEFAULT: - wxFAIL_MSG( _T("unknown border style") ); + wxFAIL_MSG( wxT("unknown border style") ); // fall through case wxBORDER_NONE: @@ -1534,11 +1508,7 @@ bool wxWindowMSW::IsMouseInWindow() const { // 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 @@ -1564,8 +1534,7 @@ void wxWindowMSW::OnInternalIdle() } #endif // !HAVE_TRACKMOUSEEVENT - if (wxUpdateUIEvent::CanUpdate(this) && IsShownOnScreen()) - UpdateWindowUI(wxUPDATE_UI_FROMIDLE); + wxWindowBase::OnInternalIdle(); } // Set this window to be the child of 'parent'. @@ -1650,7 +1619,7 @@ void wxWindowMSW::Update() { if ( !::UpdateWindow(GetHwnd()) ) { - wxLogLastError(_T("UpdateWindow")); + wxLogLastError(wxT("UpdateWindow")); } #if !defined(__WXMICROWIN__) && !defined(__WXWINCE__) @@ -1752,11 +1721,11 @@ void wxWindowMSW::DoSetToolTip(wxToolTip *tooltip) bool wxWindowMSW::IsSizeDeferred() const { -#if USE_DEFERRED_SIZING +#if wxUSE_DEFERRED_SIZING if ( m_pendingPosition != wxDefaultPosition || m_pendingSize != wxDefaultSize ) return true; -#endif // USE_DEFERRED_SIZING +#endif // wxUSE_DEFERRED_SIZING return false; } @@ -1764,7 +1733,7 @@ bool wxWindowMSW::IsSizeDeferred() const // Get total size void wxWindowMSW::DoGetSize(int *x, int *y) const { -#if USE_DEFERRED_SIZING +#if wxUSE_DEFERRED_SIZING // if SetSize() had been called at wx level but not realized at Windows // level yet (i.e. EndDeferWindowPos() not called), we still should return // the new and not the old position to the other wx code @@ -1776,7 +1745,7 @@ void wxWindowMSW::DoGetSize(int *x, int *y) const *y = m_pendingSize.y; } else // use current size -#endif // USE_DEFERRED_SIZING +#endif // wxUSE_DEFERRED_SIZING { RECT rect = wxGetWindowRect(GetHwnd()); @@ -1790,10 +1759,22 @@ void wxWindowMSW::DoGetSize(int *x, int *y) const // Get size *available for subwindows* i.e. excluding menu bar etc. void wxWindowMSW::DoGetClientSize(int *x, int *y) const { -#if USE_DEFERRED_SIZING +#if wxUSE_DEFERRED_SIZING 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; @@ -1808,7 +1789,7 @@ void wxWindowMSW::DoGetClientSize(int *x, int *y) const *y = rect.bottom - rect.top; } else -#endif // USE_DEFERRED_SIZING +#endif // wxUSE_DEFERRED_SIZING { RECT rect = wxGetClientRect(GetHwnd()); @@ -1817,6 +1798,15 @@ void wxWindowMSW::DoGetClientSize(int *x, int *y) const 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 @@ -1824,11 +1814,13 @@ 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()); @@ -1909,7 +1901,7 @@ void wxWindowMSW::DoClientToScreen(int *x, int *y) const bool wxWindowMSW::DoMoveSibling(WXHWND hwnd, int x, int y, int width, int height) { -#if USE_DEFERRED_SIZING +#if wxUSE_DEFERRED_SIZING // if our parent had prepared a defer window handle for us, use it (unless // we are a top level window) wxWindowMSW * const parent = IsTopLevel() ? NULL : GetParent(); @@ -1921,7 +1913,7 @@ wxWindowMSW::DoMoveSibling(WXHWND hwnd, int x, int y, int width, int height) SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOACTIVATE); if ( !hdwp ) { - wxLogLastError(_T("DeferWindowPos")); + wxLogLastError(wxT("DeferWindowPos")); } } @@ -1939,13 +1931,13 @@ wxWindowMSW::DoMoveSibling(WXHWND hwnd, int x, int y, int width, int height) } // otherwise (or if deferring failed) move the window in place immediately -#endif // USE_DEFERRED_SIZING +#endif // wxUSE_DEFERRED_SIZING if ( !::MoveWindow((HWND)hwnd, x, y, width, height, IsShown()) ) { wxLogLastError(wxT("MoveWindow")); } - // if USE_DEFERRED_SIZING, indicates that we didn't use deferred move, + // if wxUSE_DEFERRED_SIZING, indicates that we didn't use deferred move, // ignored otherwise return false; } @@ -1961,7 +1953,7 @@ void wxWindowMSW::DoMoveWindow(int x, int y, int width, int height) if ( DoMoveSibling(m_hWnd, x, y, width, height) ) { -#if USE_DEFERRED_SIZING +#if wxUSE_DEFERRED_SIZING m_pendingPosition = wxPoint(x, y); m_pendingSize = wxSize(width, height); } @@ -1969,7 +1961,7 @@ void wxWindowMSW::DoMoveWindow(int x, int y, int width, int height) { m_pendingPosition = wxDefaultPosition; m_pendingSize = wxDefaultSize; -#endif // USE_DEFERRED_SIZING +#endif // wxUSE_DEFERRED_SIZING } } @@ -1996,6 +1988,12 @@ void wxWindowMSW::DoSetSize(int x, int y, int width, int height, int sizeFlags) width == currentW && height == currentH && !(sizeFlags & wxSIZE_FORCE) ) { + if (sizeFlags & wxSIZE_FORCE_EVENT) + { + wxSizeEvent event( wxSize(width,height), GetId() ); + event.SetEventObject( this ); + HandleWindowEvent( event ); + } return; } @@ -2011,7 +2009,7 @@ void wxWindowMSW::DoSetSize(int x, int y, int width, int height, int sizeFlags) { if ( sizeFlags & wxSIZE_AUTO_WIDTH ) { - size = DoGetBestSize(); + size = GetBestSize(); width = size.x; } else @@ -2027,9 +2025,9 @@ void wxWindowMSW::DoSetSize(int x, int y, int width, int height, int sizeFlags) { if ( size.x == wxDefaultCoord ) { - size = DoGetBestSize(); + size = GetBestSize(); } - //else: already called DoGetBestSize() above + //else: already called GetBestSize() above height = size.y; } @@ -2101,11 +2099,41 @@ void wxWindowMSW::DoSetClientSize(int width, int height) height + heightWin - rectClient.bottom, TRUE) ) { - wxLogLastError(_T("MoveWindow")); + wxLogLastError(wxT("MoveWindow")); } } } +wxSize wxWindowMSW::DoGetBorderSize() const +{ + wxCoord border; + switch ( GetBorder() ) + { + case wxBORDER_STATIC: + case wxBORDER_SIMPLE: + border = 1; + break; + + case wxBORDER_SUNKEN: + border = 2; + break; + + case wxBORDER_RAISED: + case wxBORDER_DOUBLE: + border = 3; + break; + + default: + wxFAIL_MSG( wxT("unknown border style") ); + // fall through + + case wxBORDER_NONE: + border = 0; + } + + return 2*wxSize(border, border); +} + // --------------------------------------------------------------------------- // text metrics // --------------------------------------------------------------------------- @@ -2126,13 +2154,14 @@ int wxWindowMSW::GetCharWidth() const #endif } -void wxWindowMSW::GetTextExtent(const wxString& string, - int *x, int *y, - int *descent, int *externalLeading, - const wxFont *fontToUse) const +void wxWindowMSW::DoGetTextExtent(const wxString& string, + int *x, int *y, + int *descent, + int *externalLeading, + const wxFont *fontToUse) const { - wxASSERT_MSG( !fontToUse || fontToUse->Ok(), - _T("invalid font in GetTextExtent()") ); + wxASSERT_MSG( !fontToUse || fontToUse->IsOk(), + wxT("invalid font in GetTextExtent()") ); HFONT hfontToUse; if ( fontToUse ) @@ -2193,21 +2222,18 @@ static void wxYieldForCommandsOnly() 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__ @@ -2225,7 +2251,7 @@ bool wxWindowMSW::DoPopupMenu(wxMenu *menu, int x, int y) } #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() @@ -2236,8 +2262,6 @@ bool wxWindowMSW::DoPopupMenu(wxMenu *menu, int x, int y) // for example) and so we do need to process the event immediately wxYieldForCommandsOnly(); - menu->SetInvokingWindow(NULL); - return true; } @@ -2249,10 +2273,23 @@ bool wxWindowMSW::DoPopupMenu(wxMenu *menu, int x, int y) 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) @@ -2276,7 +2313,7 @@ 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 ) @@ -2353,8 +2390,6 @@ bool wxWindowMSW::MSWProcessMessage(WXMSG* pMsg) // emulate the button click btn = wxFindWinFromHandle(msg->hwnd); } - - bProcess = false; } else // not a button itself, do we have default button? { @@ -2414,6 +2449,13 @@ bool wxWindowMSW::MSWProcessMessage(WXMSG* pMsg) 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__ @@ -2638,12 +2680,18 @@ wxWindowCreationHook::~wxWindowCreationHook() // Main window proc LRESULT WXDLLEXPORT APIENTRY _EXPORT wxWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { - // trace all messages - useful for the debugging -#ifdef __WXDEBUG__ - wxLogTrace(wxTraceMessages, + // trace all messages: useful for the debugging but noticeably slows down + // the code so don't do it by default +#if wxDEBUG_LEVEL >= 2 + // 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); -#endif // __WXDEBUG__ + wxGetMessageName(message), hWnd, (long)wParam, (long)lParam); +#endif // wxDEBUG_LEVEL >= 2 wxWindowMSW *wnd = wxFindWinFromHandle(hWnd); @@ -2667,7 +2715,11 @@ LRESULT WXDLLEXPORT APIENTRY _EXPORT wxWndProc(HWND hWnd, UINT message, WPARAM w 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; @@ -2731,7 +2783,7 @@ WXLRESULT wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM l } } break; -#if 0 + case WM_ENTERSIZEMOVE: { processed = HandleEnterSizeMove(); @@ -2743,7 +2795,7 @@ WXLRESULT wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM l processed = HandleExitSizeMove(); } break; -#endif + case WM_SIZING: { LPRECT pRect = (LPRECT)lParam; @@ -2907,7 +2959,7 @@ WXLRESULT wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM l // this should never happen wxCHECK_MSG( win, 0, - _T("FindWindowForMouseEvent() returned NULL") ); + wxT("FindWindowForMouseEvent() returned NULL") ); } #ifdef __POCKETPC__ if (IsContextMenuEnabled() && message == WM_LBUTTONDOWN) @@ -3040,32 +3092,36 @@ WXLRESULT wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM l 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: @@ -3075,6 +3131,7 @@ WXLRESULT wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM l case VK_SUBTRACT: case VK_MULTIPLY: case VK_DIVIDE: + case VK_DECIMAL: case VK_NUMPAD0: case VK_NUMPAD1: case VK_NUMPAD2: @@ -3092,14 +3149,11 @@ WXLRESULT wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM l 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 @@ -3111,8 +3165,32 @@ WXLRESULT wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM l #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 @@ -3146,10 +3224,21 @@ WXLRESULT wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM l } 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); @@ -3223,7 +3312,17 @@ WXLRESULT wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM l 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 @@ -3403,9 +3502,10 @@ WXLRESULT wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM l // now alter the client size making room for drawing a // themed border RECT *rect; + NCCALCSIZE_PARAMS *csparam = NULL; if ( wParam ) { - NCCALCSIZE_PARAMS *csparam = (NCCALCSIZE_PARAMS *)lParam; + csparam = (NCCALCSIZE_PARAMS *)lParam; rect = &csparam->rgrc[0]; } else @@ -3428,8 +3528,14 @@ WXLRESULT wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM l &rcClient) == S_OK ) { InflateRect(&rcClient, -1, -1); - *rect = rcClient; - rc.result = WVR_REDRAW; + if (wParam) + csparam->rgrc[0] = rcClient; + else + *((RECT*)lParam) = rcClient; + + // WVR_REDRAW triggers a bug whereby child windows are moved up and left, + // so don't use. + // rc.result = WVR_REDRAW; } } } @@ -3494,15 +3600,26 @@ WXLRESULT wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM l } 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) ) { -#ifdef __WXDEBUG__ - wxLogTrace(wxTraceMessages, wxT("Forwarding %s to DefWindowProc."), +#if wxDEBUG_LEVEL >= 2 + wxLogTrace("winmsg", wxT("Forwarding %s to DefWindowProc."), wxGetMessageName(message)); -#endif // __WXDEBUG__ - rc.result = MSWDefWindowProc(message, wParam, lParam); +#endif // wxDEBUG_LEVEL >= 2 + result = MSWDefWindowProc(message, wParam, lParam); } - return rc.result; + return result; } // ---------------------------------------------------------------------------- @@ -3522,20 +3639,24 @@ void wxAssociateWinWithHandle(HWND hwnd, wxWindowMSW *win) wxCHECK_RET( hwnd != (HWND)NULL, wxT("attempt to add a NULL hwnd to window list ignored") ); -#ifdef __WXDEBUG__ +#if wxDEBUG_LEVEL WindowHandles::const_iterator i = gs_windowHandles.find(hwnd); if ( i != gs_windowHandles.end() ) { if ( i->second != win ) { - wxLogDebug(wxT("HWND %p already associated with another window (%s)"), - hwnd, win->GetClassInfo()->GetClassName()); + wxFAIL_MSG( + wxString::Format( + wxT("HWND %p already associated with another window (%s)"), + hwnd, win->GetClassInfo()->GetClassName() + ) + ); } //else: this actually happens currently because we associate the window // with its HWND during creation (if we create it) and also when // SubclassWin() is called later, this is ok } -#endif // __WXDEBUG__ +#endif // wxDEBUG_LEVEL gs_windowHandles[hwnd] = (wxWindow *)win; } @@ -3555,33 +3676,22 @@ void wxWindowMSW::MSWDestroyWindow() { } -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 @@ -3599,49 +3709,6 @@ bool wxWindowMSW::MSWGetCreateWindowCoords(const wxPoint& pos, 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 @@ -3819,7 +3886,7 @@ bool wxWindowMSW::HandleTooltipNotify(WXUINT code, if ( !len ) { - wxLogLastError(_T("MultiByteToWideChar()")); + wxLogLastError(wxT("MultiByteToWideChar()")); } buf[len] = L'\0'; @@ -3937,16 +4004,13 @@ bool wxWindowMSW::HandleCreate(WXLPCREATESTRUCT WXUNUSED_IN_WINCE(cs), bool wxWindowMSW::HandleDestroy() { - SendDestroyEvent(); - // delete our drop target if we've got one #if wxUSE_DRAG_AND_DROP if ( m_dropTarget != NULL ) { m_dropTarget->Revoke(m_hWnd); - delete m_dropTarget; - m_dropTarget = NULL; + wxDELETE(m_dropTarget); } #endif // wxUSE_DRAG_AND_DROP @@ -4088,7 +4152,6 @@ bool wxWindowMSW::HandleDropFiles(WXWPARAM wParam) ::DragQueryFile(hFilesInfo, wIndex, wxStringBuffer(files[wIndex], len), len); } - DragFinish (hFilesInfo); wxDropFilesEvent event(wxEVT_DROP_FILES, gwFilesDropped, files); event.SetEventObject(this); @@ -4098,6 +4161,8 @@ bool wxWindowMSW::HandleDropFiles(WXWPARAM wParam) event.m_pos.x = dropPoint.x; event.m_pos.y = dropPoint.y; + DragFinish(hFilesInfo); + return HandleWindowEvent(event); #endif } @@ -4127,19 +4192,14 @@ bool wxWindowMSW::HandleSetCursor(WXHWND WXUNUSED(hWnd), // 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() ) @@ -4153,7 +4213,7 @@ bool wxWindowMSW::HandleSetCursor(WXHWND WXUNUSED(hWnd), // 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); } @@ -4161,7 +4221,7 @@ bool wxWindowMSW::HandleSetCursor(WXHWND WXUNUSED(hWnd), if ( !hcursor && !GetParent() ) { const wxCursor *cursor = wxGetGlobalCursor(); - if ( cursor && cursor->Ok() ) + if ( cursor && cursor->IsOk() ) { hcursor = GetHcursorOf(*cursor); } @@ -4211,7 +4271,7 @@ bool wxWindowMSW::HandlePower(WXWPARAM WXUNUSED_IN_WINCE(wParam), break; default: - wxLogDebug(_T("Unknown WM_POWERBROADCAST(%d) event"), wParam); + wxLogDebug(wxT("Unknown WM_POWERBROADCAST(%d) event"), wParam); // fall through // these messages are currently not mapped to wx events @@ -4304,7 +4364,7 @@ wxWindowMSW::MSWOnDrawItem(int WXUNUSED_UNLESS_ODRAWN(id), return false; wxCHECK_MSG( wxDynamicCast(pMenuItem, wxMenuItem), - false, _T("MSWOnDrawItem: bad wxMenuItem pointer") ); + false, wxT("MSWOnDrawItem: bad wxMenuItem pointer") ); // prepare to call OnDrawItem(): notice using of wxDCTemp to prevent // the DC from being released @@ -4370,7 +4430,7 @@ wxWindowMSW::MSWOnMeasureItem(int id, WXMEASUREITEMSTRUCT *itemStruct) return false; wxCHECK_MSG( wxDynamicCast(pMenuItem, wxMenuItem), - false, _T("MSWOnMeasureItem: bad wxMenuItem pointer") ); + false, wxT("MSWOnMeasureItem: bad wxMenuItem pointer") ); size_t w, h; bool rc = pMenuItem->OnMeasureItem(&w, &h); @@ -4597,12 +4657,12 @@ extern wxCOLORMAP *wxGetStdColourMap() // reference bitmap which can tell us what the RGB values change // to. wxLogNull logNo; // suppress error if we couldn't load the bitmap - wxBitmap stdColourBitmap(_T("wxBITMAP_STD_COLOURS")); - if ( stdColourBitmap.Ok() ) + wxBitmap stdColourBitmap(wxT("wxBITMAP_STD_COLOURS")); + if ( stdColourBitmap.IsOk() ) { // the pixels in the bitmap must correspond to wxSTD_COL_XXX! wxASSERT_MSG( stdColourBitmap.GetWidth() == wxSTD_COL_MAX, - _T("forgot to update wxBITMAP_STD_COLOURS!") ); + wxT("forgot to update wxBITMAP_STD_COLOURS!") ); wxMemoryDC memDC; memDC.SelectObject(stdColourBitmap); @@ -4645,25 +4705,110 @@ extern wxCOLORMAP *wxGetStdColourMap() 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 if ( !hRegion ) + { wxLogLastError(wxT("CreateRectRgn")); + } if ( ::GetUpdateRgn(GetHwnd(), hRegion, FALSE) == ERROR ) + { wxLogLastError(wxT("GetUpdateRgn")); + } 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 @@ -4694,105 +4839,158 @@ void wxWindowMSW::OnPaint(wxPaintEvent& event) bool wxWindowMSW::HandleEraseBkgnd(WXHDC hdc) { - wxDCTemp dc(hdc, GetClientSize()); - wxDCTempImpl *impl = (wxDCTempImpl*) dc.GetImpl(); + switch ( GetBackgroundStyle() ) + { + case wxBG_STYLE_ERASE: + case wxBG_STYLE_COLOUR: + // we need to generate an erase background event + { + wxDCTemp dc(hdc, GetClientSize()); + wxDCTempImpl *impl = (wxDCTempImpl*) dc.GetImpl(); - impl->SetHDC(hdc); - impl->SetWindow((wxWindow *)this); + impl->SetHDC(hdc); + impl->SetWindow((wxWindow *)this); - wxEraseEvent event(m_windowId, &dc); - event.SetEventObject(this); - bool rc = HandleWindowEvent(event); + wxEraseEvent event(m_windowId, &dc); + event.SetEventObject(this); + bool rc = HandleWindowEvent(event); - // must be called manually as ~wxDC doesn't do anything for wxDCTemp - impl->SelectOldObjects(hdc); + // must be called manually as ~wxDC doesn't do anything for + // wxDCTemp + impl->SelectOldObjects(hdc); - return rc; + if ( rc ) + { + // background erased by the user-defined handler + return true; + } + } + // fall through + + case wxBG_STYLE_SYSTEM: + if ( !DoEraseBackground(hdc) ) + { + // let the default processing to take place if we didn't erase + // the background ourselves + return false; + } + 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; + + default: + wxFAIL_MSG( "unknown background style" ); + } + + return true; } -void wxWindowMSW::OnEraseBackground(wxEraseEvent& event) +#ifdef wxHAS_MSW_BACKGROUND_ERASE_HOOK + +bool wxWindowMSW::MSWHasEraseBgHook() const { - // 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() ) - { - event.Skip(); - return; - } + return gs_eraseBgHooks.find(const_cast(this)) + != gs_eraseBgHooks.end(); +} - if ( GetBackgroundStyle() == wxBG_STYLE_CUSTOM ) +void wxWindowMSW::MSWSetEraseBgHook(wxWindow *child) +{ + if ( child ) { - // don't skip the event here, custom background means that the app - // is drawing it itself in its OnPaint(), so don't draw it at all - // now to avoid flicker - return; + if ( !gs_eraseBgHooks.insert( + EraseBgHooks::value_type(this, child)).second ) + { + wxFAIL_MSG( wxT("Setting erase background hook twice?") ); + } } - - wxDC *dc = event.GetDC(); - if (!dc) return; - wxMSWDCImpl *impl = (wxMSWDCImpl*) dc->GetImpl(); - - // do default background painting - if ( !DoEraseBackground(GetHdcOf(*impl)) ) + else // reset the hook { - // let the system paint the background - event.Skip(); + 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); + if ( !::SetBrushOrgEx((HDC)hDC, -rc.left, -rc.top, 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; @@ -4895,7 +5093,7 @@ bool wxWindowMSW::HandleExitSizeMove() bool wxWindowMSW::HandleSize(int WXUNUSED(w), int WXUNUSED(h), WXUINT wParam) { -#if USE_DEFERRED_SIZING +#if wxUSE_DEFERRED_SIZING // when we resize this window, its children are probably going to be // repositioned as well, prepare to use DeferWindowPos() for them int numChildren = 0; @@ -4916,20 +5114,20 @@ bool wxWindowMSW::HandleSize(int WXUNUSED(w), int WXUNUSED(h), WXUINT wParam) m_hDWP = (WXHANDLE)::BeginDeferWindowPos(numChildren); if ( !m_hDWP ) { - wxLogLastError(_T("BeginDeferWindowPos")); + wxLogLastError(wxT("BeginDeferWindowPos")); } if (m_hDWP) useDefer = true; } } -#endif // USE_DEFERRED_SIZING +#endif // wxUSE_DEFERRED_SIZING // update this window size bool processed = false; switch ( wParam ) { default: - wxFAIL_MSG( _T("unexpected WM_SIZE parameter") ); + wxFAIL_MSG( wxT("unexpected WM_SIZE parameter") ); // fall through nevertheless case SIZE_MAXHIDE: @@ -4955,7 +5153,7 @@ bool wxWindowMSW::HandleSize(int WXUNUSED(w), int WXUNUSED(h), WXUINT wParam) processed = HandleWindowEvent(event); } -#if USE_DEFERRED_SIZING +#if wxUSE_DEFERRED_SIZING // and finally change the positions of all child windows at once if ( useDefer && m_hDWP ) { @@ -4969,7 +5167,7 @@ bool wxWindowMSW::HandleSize(int WXUNUSED(w), int WXUNUSED(h), WXUINT wParam) // do put all child controls in place at once if ( !::EndDeferWindowPos(hDWP) ) { - wxLogLastError(_T("EndDeferWindowPos")); + wxLogLastError(wxT("EndDeferWindowPos")); } // Reset our children's pending pos/size values. @@ -4977,12 +5175,11 @@ bool wxWindowMSW::HandleSize(int WXUNUSED(w), int WXUNUSED(h), WXUINT wParam) node; node = node->GetNext() ) { - wxWindowMSW *child = node->GetData(); - child->m_pendingPosition = wxDefaultPosition; - child->m_pendingSize = wxDefaultSize; + wxWindowMSW * const child = node->GetData(); + child->MSWEndDeferWindowPos(); } } -#endif // USE_DEFERRED_SIZING +#endif // wxUSE_DEFERRED_SIZING return processed; } @@ -5165,7 +5362,7 @@ void wxWindowMSW::InitMouseEvent(wxMouseEvent& event, // still don't get move, enter nor leave events. static wxWindowMSW *FindWindowForMouseEvent(wxWindowMSW *win, int *x, int *y) { - wxCHECK_MSG( x && y, win, _T("NULL pointer in FindWindowForMouseEvent") ); + wxCHECK_MSG( x && y, win, wxT("NULL pointer in FindWindowForMouseEvent") ); // first try to find a non transparent child: this allows us to send events // to a static text which is inside a static box, for example @@ -5288,22 +5485,15 @@ bool wxWindowMSW::HandleMouseMove(int x, int y, WXUINT flags) if ( !s_initDone ) { // see comment in wxApp::GetComCtl32Version() explaining the - // use of wxDL_GET_LOADED - wxDynamicLibrary dllComCtl32(_T("comctl32.dll"), - wxDL_VERBATIM | - wxDL_QUIET | - wxDL_GET_LOADED); + // use of wxLoadedDLL + wxLoadedDLL dllComCtl32(wxT("comctl32.dll")); if ( dllComCtl32.IsLoaded() ) { s_pfn_TrackMouseEvent = (_TrackMouseEvent_t) - dllComCtl32.RawGetSymbol(_T("_TrackMouseEvent")); + dllComCtl32.RawGetSymbol(wxT("_TrackMouseEvent")); } s_initDone = true; - - // we shouldn't unload comctl32.dll here as we didn't really - // load it above - dllComCtl32.Detach(); } if ( s_pfn_TrackMouseEvent ) @@ -5382,7 +5572,7 @@ bool wxWindowMSW::HandleMouseWheel(WXWPARAM wParam, WXLPARAM lParam) &s_linesPerRotation, 0)) { // this is not supposed to happen - wxLogLastError(_T("SystemParametersInfo(GETWHEELSCROLLLINES)")); + wxLogLastError(wxT("SystemParametersInfo(GETWHEELSCROLLLINES)")); // the default is 3, so use it if SystemParametersInfo() failed s_linesPerRotation = 3; @@ -5419,14 +5609,7 @@ void wxWindowMSW::GenerateMouseLeave() state |= MK_RBUTTON; POINT pt; -#ifdef __WXWINCE__ - if ( !::GetCursorPosWinCE(&pt) ) -#else - if ( !::GetCursorPos(&pt) ) -#endif - { - wxLogLastError(_T("GetCursorPos")); - } + wxGetCursorPosMSW(&pt); // we need to have client coordinates here for symmetry with // wxEVT_ENTER_WINDOW @@ -5444,73 +5627,128 @@ void wxWindowMSW::GenerateMouseLeave() // 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(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; + // Event coordinates must be in window client coordinates system which + // doesn't make sense if there is no window. + // + // We could use screen coordinates for such events but this would make the + // logic of the event handlers more complicated: you'd need to test for the + // event object and interpret the coordinates differently according to + // whether it's NULL or not so unless somebody really asks for this let's + // just avoid the issue. + if ( win ) + { + const wxPoint mousePos = win->ScreenToClient(wxGetMousePosition()); + event.m_x = mousePos.x; + event.m_y = mousePos.y; + } +} + +} // anonymous namespace - event.m_x = pt.x; - event.m_y = pt.y; +void +wxWindowMSW::InitAnyKeyEvent(wxKeyEvent& event, + WXWPARAM wParam, + WXLPARAM lParam) const +{ + MSWInitAnyKeyEvent(event, wParam, lParam, this); +} + +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(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 @@ -5518,40 +5756,32 @@ bool wxWindowMSW::HandleChar(WXWPARAM wParam, WXLPARAM lParam, bool isASCII) // 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); } @@ -5590,13 +5820,14 @@ int wxWindowMSW::HandleMenuChar(int WXUNUSED_IN_WINCE(chAccel), // menu creation code wxMenuItem *item = (wxMenuItem*)mii.dwItemData; - const wxChar *p = wxStrchr(item->GetItemLabel().wx_str(), _T('&')); + const wxString label(item->GetItemLabel()); + const wxChar *p = wxStrchr(label.wx_str(), wxT('&')); while ( p++ ) { - if ( *p == _T('&') ) + if ( *p == wxT('&') ) { // this is not the accel char, find the real one - p = wxStrchr(p + 1, _T('&')); + p = wxStrchr(p + 1, wxT('&')); } else // got the accel char { @@ -5619,7 +5850,7 @@ int wxWindowMSW::HandleMenuChar(int WXUNUSED_IN_WINCE(chAccel), else // failed to get the menu text? { // it's not fatal, so don't show error, but still log it - wxLogLastError(_T("GetMenuItemInfo")); + wxLogLastError(wxT("GetMenuItemInfo")); } } #endif @@ -5789,12 +6020,11 @@ bool wxWindowMSW::MSWOnScroll(int orientation, WXWORD wParam, scrollInfo.fMask = SIF_TRACKPOS; if ( !::GetScrollInfo(GetHwnd(), - orientation == wxHORIZONTAL ? SB_HORZ - : SB_VERT, + WXOrientToSB(orientation), &scrollInfo) ) { // Not necessarily an error, if there are no scrollbars yet. - // wxLogLastError(_T("GetScrollInfo")); + // wxLogLastError(wxT("GetScrollInfo")); } event.SetPosition(scrollInfo.nTrackPos); @@ -5820,7 +6050,7 @@ bool wxWindowMSW::MSWOnScroll(int orientation, WXWORD wParam, wxWindowMSW::MSWRegisterMessageHandler(int msg, MSWMessageHandler handler) { wxCHECK_MSG( gs_messageHandlers.find(msg) == gs_messageHandlers.end(), - false, _T("registering handler for the same message twice") ); + false, wxT("registering handler for the same message twice") ); gs_messageHandlers[msg] = handler; return true; @@ -5831,7 +6061,7 @@ wxWindowMSW::MSWUnregisterMessageHandler(int msg, MSWMessageHandler handler) { const MSWMessageHandlers::iterator i = gs_messageHandlers.find(msg); wxCHECK_RET( i != gs_messageHandlers.end() && i->second == handler, - _T("unregistering non-registered handler?") ); + wxT("unregistering non-registered handler?") ); gs_messageHandlers.erase(i); } @@ -5867,24 +6097,34 @@ void wxGetCharSize(WXHWND wnd, int *x, int *y, const wxFont& the_font) // 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; @@ -5958,34 +6198,76 @@ static const struct wxKeyMapping #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: @@ -6031,78 +6313,109 @@ int wxCharCodeMSWToWX(int vk, WXLPARAM lParam) 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; @@ -6120,15 +6433,18 @@ WXWORD wxCharCodeWXToMSW(int wxk, bool *isVirtual) 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) { @@ -6161,7 +6477,7 @@ bool wxGetKeyState(wxKeyCode key) 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 ) @@ -6183,7 +6499,7 @@ wxMouseState wxGetMouseState() { wxMouseState ms; POINT pt; - GetCursorPos( &pt ); + wxGetCursorPosMSW(&pt); ms.SetX(pt.x); ms.SetY(pt.y); @@ -6221,19 +6537,19 @@ extern wxWindow *wxGetWindowFromHWND(WXHWND hWnd) // For a radiobutton, we get the radiobox from GWL_USERDATA (which is set // by code in msw/radiobox.cpp), for all the others we just search up the // window hierarchy - wxWindow *win = (wxWindow *)NULL; + wxWindow *win = NULL; if ( hwnd ) { 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 @@ -6279,64 +6595,61 @@ extern wxWindow *wxGetWindowFromHWND(WXHWND hWnd) // Windows keyboard hook. Allows interception of e.g. F1, ESCAPE // in active frames and dialogs, regardless of where the focus is. static HHOOK wxTheKeyboardHook = 0; -static FARPROC wxTheKeyboardHookProc = 0; -int APIENTRY _EXPORT -wxKeyboardHook(int nCode, WORD wParam, DWORD lParam); - -void wxSetKeyboardHook(bool doIt) -{ - if ( doIt ) - { - wxTheKeyboardHookProc = MakeProcInstance((FARPROC) wxKeyboardHook, wxGetInstance()); - wxTheKeyboardHook = SetWindowsHookEx(WH_KEYBOARD, (HOOKPROC) wxTheKeyboardHookProc, wxGetInstance(), - - GetCurrentThreadId() - // (DWORD)GetCurrentProcess()); // This is another possibility. Which is right? - ); - } - else - { - UnhookWindowsHookEx(wxTheKeyboardHook); - } -} -int APIENTRY _EXPORT +int APIENTRY wxKeyboardHook(int nCode, WORD wParam, DWORD lParam) { DWORD hiWord = HIWORD(lParam); if ( nCode != HC_NOREMOVE && ((hiWord & KF_UP) == 0) ) { - int id = wxCharCodeMSWToWX(wParam, lParam); - if ( id != 0 ) + wchar_t uc; + 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(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; + } + } } } } @@ -6344,9 +6657,32 @@ wxKeyboardHook(int nCode, WORD wParam, DWORD lParam) return (int)CallNextHookEx(wxTheKeyboardHook, nCode, wParam, lParam); } +void wxSetKeyboardHook(bool doIt) +{ + if ( doIt ) + { + wxTheKeyboardHook = ::SetWindowsHookEx + ( + WH_KEYBOARD, + (HOOKPROC)wxKeyboardHook, + NULL, // must be NULL for process hook + ::GetCurrentThreadId() + ); + if ( !wxTheKeyboardHook ) + { + wxLogLastError(wxT("SetWindowsHookEx(wxKeyboardHook)")); + } + } + else // uninstall + { + if ( wxTheKeyboardHook ) + ::UnhookWindowsHookEx(wxTheKeyboardHook); + } +} + #endif // !__WXMICROWIN__ -#ifdef __WXDEBUG__ +#if wxDEBUG_LEVEL >= 2 const wxChar *wxGetMessageName(int message) { switch ( message ) @@ -6504,6 +6840,10 @@ const wxChar *wxGetMessageName(int message) case 0x0120: return wxT("WM_MENUCHAR"); case 0x0121: return wxT("WM_ENTERIDLE"); + case 0x0127: return wxT("WM_CHANGEUISTATE"); + case 0x0128: return wxT("WM_UPDATEUISTATE"); + case 0x0129: return wxT("WM_QUERYUISTATE"); + case 0x0132: return wxT("WM_CTLCOLORMSGBOX"); case 0x0133: return wxT("WM_CTLCOLOREDIT"); case 0x0134: return wxT("WM_CTLCOLORLISTBOX"); @@ -6815,7 +7155,7 @@ const wxChar *wxGetMessageName(int message) return s_szBuf.c_str(); } } -#endif //__WXDEBUG__ +#endif // wxDEBUG_LEVEL >= 2 static TEXTMETRIC wxGetTextMetrics(const wxWindowMSW *win) { @@ -6872,11 +7212,7 @@ wxWindow* wxFindWindowAtPoint(const wxPoint& pt) wxPoint wxGetMousePosition() { POINT pt; -#ifdef __WXWINCE__ - GetCursorPosWinCE(&pt); -#else - GetCursorPos( & pt ); -#endif + wxGetCursorPosMSW(&pt); return wxPoint(pt.x, pt.y); } @@ -6891,10 +7227,10 @@ static void WinCEUnregisterHotKey(int modifiers, int id) typedef BOOL (WINAPI *UnregisterFunc1Proc)(UINT, UINT); UnregisterFunc1Proc procUnregisterFunc; - hCoreDll = LoadLibrary(_T("coredll.dll")); + hCoreDll = LoadLibrary(wxT("coredll.dll")); if (hCoreDll) { - procUnregisterFunc = (UnregisterFunc1Proc)GetProcAddress(hCoreDll, _T("UnregisterFunc1")); + procUnregisterFunc = (UnregisterFunc1Proc)GetProcAddress(hCoreDll, wxT("UnregisterFunc1")); if (procUnregisterFunc) procUnregisterFunc(modifiers, id); FreeLibrary(hCoreDll); @@ -6922,7 +7258,7 @@ bool wxWindowMSW::RegisterHotKey(int hotkeyId, int modifiers, int keycode) if ( !::RegisterHotKey(GetHwnd(), hotkeyId, win_modifiers, keycode) ) { - wxLogLastError(_T("RegisterHotKey")); + wxLogLastError(wxT("RegisterHotKey")); return false; } @@ -6938,7 +7274,7 @@ bool wxWindowMSW::UnregisterHotKey(int hotkeyId) if ( !::UnregisterHotKey(GetHwnd(), hotkeyId) ) { - wxLogLastError(_T("UnregisterHotKey")); + wxLogLastError(wxT("UnregisterHotKey")); return false; } @@ -6950,12 +7286,10 @@ bool wxWindowMSW::UnregisterHotKey(int hotkeyId) 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; @@ -6991,7 +7325,7 @@ public: if ( !ms_hMsgHookProc ) { - wxLogLastError(_T("SetWindowsHookEx(WH_GETMESSAGE)")); + wxLogLastError(wxT("SetWindowsHookEx(WH_GETMESSAGE)")); return false; }