X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/ed5bc8f1bfec69427d183ae49f5d4e744d088011..8d7490018a47f60149a35ced6f880ec834aba471:/src/msw/window.cpp diff --git a/src/msw/window.cpp b/src/msw/window.cpp index 7328f6539a..3189e52624 100644 --- a/src/msw/window.cpp +++ b/src/msw/window.cpp @@ -24,9 +24,12 @@ #pragma hdrstop #endif +#include "wx/window.h" + #ifndef WX_PRECOMP #include "wx/msw/wrapwin.h" - #include "wx/window.h" + #include "wx/msw/wrapcctl.h" // include "properly" + #include "wx/msw/missing.h" #include "wx/accel.h" #include "wx/menu.h" #include "wx/dc.h" @@ -43,14 +46,20 @@ #include "wx/settings.h" #include "wx/statbox.h" #include "wx/sizer.h" + #include "wx/intl.h" + #include "wx/log.h" + #include "wx/textctrl.h" + #include "wx/menuitem.h" + #include "wx/module.h" #endif #if wxUSE_OWNER_DRAWN && !defined(__WXUNIVERSAL__) #include "wx/ownerdrw.h" #endif +#include "wx/hashmap.h" #include "wx/evtloop.h" -#include "wx/module.h" +#include "wx/power.h" #include "wx/sysopt.h" #if wxUSE_DRAG_AND_DROP @@ -69,9 +78,6 @@ #endif #endif -#include "wx/menuitem.h" -#include "wx/log.h" - #include "wx/msw/private.h" #if wxUSE_TOOLTIPS @@ -86,12 +92,9 @@ #include "wx/spinctrl.h" #endif // wxUSE_SPINCTRL -#include "wx/intl.h" -#include "wx/log.h" - -#include "wx/textctrl.h" #include "wx/notebook.h" #include "wx/listctrl.h" +#include "wx/dynlib.h" #include @@ -104,15 +107,33 @@ #include #endif -#include - -#include "wx/msw/missing.h" +#if !defined __WXWINCE__ && !defined NEED_PBT_H + #include +#endif #if defined(__WXWINCE__) #include "wx/msw/wince/missing.h" +#ifdef __POCKETPC__ + #include + #include + #include + #include +#endif +#endif + +#if wxUSE_UXTHEME + #include "wx/msw/uxtheme.h" + #define EP_EDITTEXT 1 + #define ETS_NORMAL 1 + #define ETS_HOT 2 + #define ETS_SELECTED 3 + #define ETS_DISABLED 4 + #define ETS_FOCUSED 5 + #define ETS_READONLY 6 + #define ETS_ASSIST 7 #endif -#if defined(TME_LEAVE) && defined(WM_MOUSELEAVE) +#if defined(TME_LEAVE) && defined(WM_MOUSELEAVE) && wxUSE_DYNLIB_CLASS #define HAVE_TRACKMOUSEEVENT #endif // everything needed for TrackMouseEvent() @@ -134,6 +155,12 @@ #define wxUSE_MOUSEEVENT_HACK 1 #endif +// not all compilers/platforms have X button related declarations (notably +// Windows CE doesn't, and probably some old SDKs don't neither) +#ifdef WM_XBUTTONDOWN + #define wxHAS_XBUTTON +#endif + // --------------------------------------------------------------------------- // global variables // --------------------------------------------------------------------------- @@ -164,6 +191,13 @@ static struct MouseEventInfoDummy } gs_lastMouseEvent; #endif // wxUSE_MOUSEEVENT_HACK +// hash containing the registered handlers for the custom messages +WX_DECLARE_HASH_MAP(int, wxWindow::MSWMessageHandler, + wxIntegerHash, wxIntegerEqual, + MSWMessageHandlers); + +static MSWMessageHandlers gs_messageHandlers; + // --------------------------------------------------------------------------- // private functions // --------------------------------------------------------------------------- @@ -476,7 +510,6 @@ void wxWindowMSW::Init() m_mouseInWindow = false; m_lastKeydownProcessed = false; - m_childrenDisabled = NULL; m_frozenness = 0; m_hWnd = 0; @@ -487,6 +520,10 @@ void wxWindowMSW::Init() m_pendingPosition = wxDefaultPosition; m_pendingSize = wxDefaultSize; + +#ifdef __POCKETPC__ + m_contextMenuEnabled = false; +#endif } // Destructor @@ -533,8 +570,6 @@ wxWindowMSW::~wxWindowMSW() wxRemoveHandleAssociation(this); } - delete m_childrenDisabled; - } // real construction (Init() must have been called before!) @@ -636,69 +671,11 @@ wxWindow *wxWindowBase::DoFindFocus() return NULL; } -bool wxWindowMSW::Enable(bool enable) +void wxWindowMSW::DoEnable( bool enable ) { - if ( !wxWindowBase::Enable(enable) ) - return false; - HWND hWnd = GetHwnd(); if ( hWnd ) ::EnableWindow(hWnd, (BOOL)enable); - - // the logic below doesn't apply to the top level windows -- otherwise - // showing a modal dialog would result in total greying out (and ungreying - // out later) of everything which would be really ugly - if ( IsTopLevel() ) - return true; - - // when the parent is disabled, all of its children should be disabled as - // well but when it is enabled back, only those of the children which - // hadn't been already disabled in the beginning should be enabled again, - // so we have to keep the list of those children - for ( wxWindowList::compatibility_iterator node = GetChildren().GetFirst(); - node; - node = node->GetNext() ) - { - wxWindow *child = node->GetData(); - if ( child->IsTopLevel() ) - { - // the logic below doesn't apply to top level children - continue; - } - - if ( enable ) - { - // re-enable the child unless it had been disabled before us - if ( !m_childrenDisabled || !m_childrenDisabled->Find(child) ) - child->Enable(); - } - else // we're being disabled - { - if ( child->IsEnabled() ) - { - // disable it as children shouldn't stay enabled while the - // parent is not - child->Disable(); - } - else // child already disabled, remember it - { - // have we created the list of disabled children already? - if ( !m_childrenDisabled ) - m_childrenDisabled = new wxWindowList; - - m_childrenDisabled->Append(child); - } - } - } - - if ( enable && m_childrenDisabled ) - { - // we don't need this list any more, don't keep unused memory - delete m_childrenDisabled; - m_childrenDisabled = NULL; - } - - return true; } bool wxWindowMSW::Show(bool show) @@ -724,6 +701,110 @@ bool wxWindowMSW::Show(bool show) return true; } +bool +wxWindowMSW::MSWShowWithEffect(bool show, + wxShowEffect effect, + unsigned timeout, + wxDirection dir) +{ + typedef BOOL (WINAPI *AnimateWindow_t)(HWND, DWORD, DWORD); + + static AnimateWindow_t s_pfnAnimateWindow = NULL; + static bool s_initDone = false; + if ( !s_initDone ) + { + wxLogNull noLog; + + wxDynamicLibrary dllUser32(_T("user32.dll"), wxDL_VERBATIM); + wxDL_INIT_FUNC(s_pfn, AnimateWindow, dllUser32); + + s_initDone = true; + + // notice that it's ok to unload user32.dll here as it won't be really + // unloaded, being still in use because we link to it statically too + } + + if ( !s_pfnAnimateWindow ) + return Show(show); + + // prepare to use AnimateWindow() + + if ( !timeout ) + timeout = 200; // this is the default animation timeout, per MSDN + + DWORD dwFlags = show ? 0 : AW_HIDE; + bool needsDir = false; + switch ( effect ) + { + case wxSHOW_EFFECT_ROLL: + needsDir = true; + break; + + case wxSHOW_EFFECT_SLIDE: + needsDir = true; + dwFlags |= AW_SLIDE; + break; + + case wxSHOW_EFFECT_BLEND: + dwFlags |= AW_BLEND; + break; + + case wxSHOW_EFFECT_EXPAND: + dwFlags |= AW_CENTER; + break; + + + case wxSHOW_EFFECT_MAX: + wxFAIL_MSG( _T("invalid window show effect") ); + return false; + + default: + wxFAIL_MSG( _T("unknown window show effect") ); + return false; + } + + if ( needsDir ) + { + switch ( dir ) + { + case wxTOP: + dwFlags |= AW_VER_NEGATIVE; + break; + + case wxBOTTOM: + dwFlags |= AW_VER_POSITIVE; + break; + + case wxLEFT: + dwFlags |= AW_HOR_NEGATIVE; + break; + + case wxRIGHT: + dwFlags |= AW_HOR_POSITIVE; + break; + + default: + wxFAIL_MSG( _T("unknown window effect direction") ); + return false; + } + } + else // animation effect which doesn't need the direction + { + wxASSERT_MSG( dir == wxBOTTOM, + _T("non-default direction used unnecessarily") ); + } + + + if ( !(*s_pfnAnimateWindow)(GetHwnd(), timeout, dwFlags) ) + { + wxLogLastError(_T("AnimateWindow")); + + return false; + } + + return true; +} + // Raise the window to the top of the Z order void wxWindowMSW::Raise() { @@ -788,22 +869,33 @@ bool wxWindowMSW::SetCursor(const wxCursor& cursor) return false; } - if ( m_cursor.Ok() ) + // don't "overwrite" busy cursor + if ( m_cursor.Ok() && !wxIsBusy() ) { - HWND hWnd = GetHwnd(); + // 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 + bool set = HasCapture(); + if ( !set ) + { + HWND hWnd = GetHwnd(); - // Change the cursor NOW if we're within the correct window - POINT point; + POINT point; #ifdef __WXWINCE__ - ::GetCursorPosWinCE(&point); + ::GetCursorPosWinCE(&point); #else - ::GetCursorPos(&point); + ::GetCursorPos(&point); #endif - RECT rect = wxGetWindowRect(hWnd); + RECT rect = wxGetWindowRect(hWnd); - if ( ::PtInRect(&rect, point) && !wxIsBusy() ) + set = ::PtInRect(&rect, point) != 0; + } + + if ( set ) + { ::SetCursor(GetHcursorOf(m_cursor)); + } + //else: will be set later when the mouse enters this window } return true; @@ -827,7 +919,7 @@ void wxWindowMSW::MSWUpdateUIState(int action, int state) if ( s_needToUpdate == -1 ) { int verMaj, verMin; - s_needToUpdate = wxGetOsVersion(&verMaj, &verMin) == wxWINDOWS_NT && + s_needToUpdate = wxGetOsVersion(&verMaj, &verMin) == wxOS_WINDOWS_NT && verMaj >= 5; } @@ -956,10 +1048,7 @@ void wxWindowMSW::ScrollWindow(int dx, int dy, const wxRect *prect) RECT *pr; if ( prect ) { - rect.left = prect->x; - rect.top = prect->y; - rect.right = prect->x + prect->width; - rect.bottom = prect->y + prect->height; + wxCopyRectToRECT(*prect, rect); pr = ▭ } else @@ -1016,6 +1105,67 @@ bool wxWindowMSW::ScrollPages(int pages) down ? pages : -pages); } +// ---------------------------------------------------------------------------- +// RTL support +// ---------------------------------------------------------------------------- + +void wxWindowMSW::SetLayoutDirection(wxLayoutDirection dir) +{ +#ifdef __WXWINCE__ + wxUnusedVar(dir); +#else + const HWND hwnd = GetHwnd(); + wxCHECK_RET( hwnd, _T("layout direction must be set after window creation") ); + + LONG styleOld = ::GetWindowLong(hwnd, GWL_EXSTYLE); + + LONG styleNew = styleOld; + switch ( dir ) + { + case wxLayout_LeftToRight: + styleNew &= ~WS_EX_LAYOUTRTL; + break; + + case wxLayout_RightToLeft: + styleNew |= WS_EX_LAYOUTRTL; + break; + + default: + wxFAIL_MSG(_T("unsupported layout direction")); + break; + } + + if ( styleNew != styleOld ) + { + ::SetWindowLong(hwnd, GWL_EXSTYLE, styleNew); + } +#endif +} + +wxLayoutDirection wxWindowMSW::GetLayoutDirection() const +{ +#ifdef __WXWINCE__ + return wxLayout_Default; +#else + const HWND hwnd = GetHwnd(); + wxCHECK_MSG( hwnd, wxLayout_Default, _T("invalid window") ); + + return ::GetWindowLong(hwnd, GWL_EXSTYLE) & WS_EX_LAYOUTRTL + ? wxLayout_RightToLeft + : wxLayout_LeftToRight; +#endif +} + +wxCoord +wxWindowMSW::AdjustForLayoutDirection(wxCoord x, + wxCoord WXUNUSED(width), + wxCoord WXUNUSED(widthTotal)) const +{ + // Win32 mirrors the coordinates of RTL windows automatically, so don't + // redo it ourselves + return x; +} + // --------------------------------------------------------------------------- // subclassing // --------------------------------------------------------------------------- @@ -1045,6 +1195,10 @@ void wxWindowMSW::SubclassWin(WXHWND hWnd) // simply check m_oldWndProc m_oldWndProc = NULL; } + + // we're officially created now, send the event + wxWindowCreateEvent event((wxWindow *)this); + (void)GetEventHandler()->ProcessEvent(event); } void wxWindowMSW::UnsubclassWin() @@ -1140,14 +1294,48 @@ void wxWindowMSW::SetWindowStyleFlag(long flags) // update the internal variable wxWindowBase::SetWindowStyleFlag(flags); + // and the real window flags + MSWUpdateStyle(flagsOld, GetExtraStyle()); +} + +void wxWindowMSW::SetExtraStyle(long exflags) +{ + long exflagsOld = GetExtraStyle(); + if ( exflags == exflagsOld ) + return; + + // update the internal variable + wxWindowBase::SetExtraStyle(exflags); + + // and the real window flags + MSWUpdateStyle(GetWindowStyleFlag(), exflagsOld); +} + +void wxWindowMSW::MSWUpdateStyle(long flagsOld, long exflagsOld) +{ // now update the Windows style as well if needed - and if the window had // been already created if ( !GetHwnd() ) return; - WXDWORD exstyle, exstyleOld; - long style = MSWGetStyle(flags, &exstyle), - styleOld = MSWGetStyle(flagsOld, &exstyleOld); + // we may need to call SetWindowPos() when we change some styles + bool callSWP = false; + + WXDWORD exstyle; + long style = MSWGetStyle(GetWindowStyleFlag(), &exstyle); + + // this is quite a horrible hack but we need it because MSWGetStyle() + // doesn't take exflags as parameter but uses GetExtraStyle() internally + // and so we have to modify the window exflags temporarily to get the + // correct exstyleOld + long exflagsNew = GetExtraStyle(); + wxWindowBase::SetExtraStyle(exflagsOld); + + WXDWORD exstyleOld; + long styleOld = MSWGetStyle(flagsOld, &exstyleOld); + + wxWindowBase::SetExtraStyle(exflagsNew); + if ( style != styleOld ) { @@ -1160,17 +1348,34 @@ void wxWindowMSW::SetWindowStyleFlag(long flags) styleReal |= style; ::SetWindowLong(GetHwnd(), GWL_STYLE, styleReal); + + // we need to call SetWindowPos() if any of the styles affecting the + // frame appearance have changed + callSWP = ((styleOld ^ style ) & (WS_BORDER | + WS_THICKFRAME | + WS_CAPTION | + WS_DLGFRAME | + WS_MAXIMIZEBOX | + WS_MINIMIZEBOX | + WS_SYSMENU) ) != 0; } // and the extended style + long exstyleReal = ::GetWindowLong(GetHwnd(), GWL_EXSTYLE); + if ( exstyle != exstyleOld ) { - long exstyleReal = ::GetWindowLong(GetHwnd(), GWL_EXSTYLE); exstyleReal &= ~exstyleOld; exstyleReal |= exstyle; ::SetWindowLong(GetHwnd(), GWL_EXSTYLE, exstyleReal); + // ex style changes don't take effect without calling SetWindowPos + callSWP = true; + } + + if ( callSWP ) + { // we must call SetWindowPos() to flush the cached extended style and // also to make the change to wxSTAY_ON_TOP style take effect: just // setting the style simply doesn't work @@ -1178,13 +1383,49 @@ void wxWindowMSW::SetWindowStyleFlag(long flags) exstyleReal & WS_EX_TOPMOST ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, - SWP_NOMOVE | SWP_NOSIZE) ) + SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED) ) { wxLogLastError(_T("SetWindowPos")); } } } +wxBorder wxWindowMSW::GetDefaultBorderForControl() const +{ + return wxBORDER_THEME; +} + +wxBorder wxWindowMSW::GetDefaultBorder() const +{ + return wxWindowBase::GetDefaultBorder(); +} + +// Translate wxBORDER_THEME (and other border styles if necessary to the value +// that makes most sense for this Windows environment +wxBorder wxWindowMSW::TranslateBorder(wxBorder border) const +{ +#if defined(__POCKETPC__) || defined(__SMARTPHONE__) + if (border == wxBORDER_THEME || border == wxBORDER_SUNKEN || border == wxBORDER_SIMPLE) + return wxBORDER_SIMPLE; + else + return wxBORDER_NONE; +#else +#if wxUSE_UXTHEME + if (border == wxBORDER_THEME) + { + if (CanApplyThemeBorder()) + { + wxUxThemeEngine* theme = wxUxThemeEngine::GetIfActive(); + if (theme) + return wxBORDER_THEME; + } + } +#endif + return border; +#endif +} + + WXDWORD wxWindowMSW::MSWGetStyle(long flags, WXDWORD *exstyle) const { // translate common wxWidgets styles to Windows ones @@ -1214,7 +1455,10 @@ WXDWORD wxWindowMSW::MSWGetStyle(long flags, WXDWORD *exstyle) const if ( flags & wxHSCROLL ) style |= WS_HSCROLL; - const wxBorder border = GetBorder(flags); + const wxBorder border = TranslateBorder(GetBorder(flags)); + + // After translation, border is now optimized for the specific version of Windows + // and theme engine presence. // WS_BORDER is only required for wxBORDER_SIMPLE if ( border == wxBORDER_SIMPLE ) @@ -1239,6 +1483,7 @@ WXDWORD wxWindowMSW::MSWGetStyle(long flags, WXDWORD *exstyle) const case wxBORDER_NONE: case wxBORDER_SIMPLE: + case wxBORDER_THEME: break; case wxBORDER_STATIC: @@ -1254,9 +1499,9 @@ WXDWORD wxWindowMSW::MSWGetStyle(long flags, WXDWORD *exstyle) const style &= ~WS_BORDER; break; - case wxBORDER_DOUBLE: - *exstyle |= WS_EX_DLGMODALFRAME; - break; +// case wxBORDER_DOUBLE: +// *exstyle |= WS_EX_DLGMODALFRAME; +// break; } // wxUniv doesn't use Windows dialog navigation functions at all @@ -1314,7 +1559,7 @@ void wxWindowMSW::OnInternalIdle() } #endif // !HAVE_TRACKMOUSEEVENT - if (wxUpdateUIEvent::CanUpdate(this)) + if (wxUpdateUIEvent::CanUpdate(this) && IsShown()) UpdateWindowUI(wxUPDATE_UI_FROMIDLE); } @@ -1381,11 +1626,7 @@ void wxWindowMSW::Refresh(bool eraseBack, const wxRect *rect) const RECT *pRect; if ( rect ) { - mswRect.left = rect->x; - mswRect.top = rect->y; - mswRect.right = rect->x + rect->width; - mswRect.bottom = rect->y + rect->height; - + wxCopyRectToRECT(*rect, mswRect); pRect = &mswRect; } else @@ -1424,9 +1665,13 @@ void wxWindowMSW::Update() // drag and drop // --------------------------------------------------------------------------- +#if wxUSE_DRAG_AND_DROP || !defined(__WXWINCE__) + +#if wxUSE_STATBOX + // we need to lower the sibling static boxes so controls contained within can be // a drop target -static inline void AdjustStaticBoxZOrder(wxWindow *parent) +static void AdjustStaticBoxZOrder(wxWindow *parent) { // no sibling static boxes if we have no parent (ie TLW) if ( !parent ) @@ -1445,6 +1690,16 @@ static inline void AdjustStaticBoxZOrder(wxWindow *parent) } } +#else // !wxUSE_STATBOX + +static inline void AdjustStaticBoxZOrder(wxWindow * WXUNUSED(parent)) +{ +} + +#endif // wxUSE_STATBOX/!wxUSE_STATBOX + +#endif // drag and drop is used + #if wxUSE_DRAG_AND_DROP void wxWindowMSW::SetDropTarget(wxDropTarget *pDropTarget) { @@ -1510,6 +1765,7 @@ bool wxWindowMSW::IsSizeDeferred() const // Get total size void wxWindowMSW::DoGetSize(int *x, int *y) const { +#if USE_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 @@ -1521,6 +1777,7 @@ void wxWindowMSW::DoGetSize(int *x, int *y) const *y = m_pendingSize.y; } else // use current size +#endif // USE_DEFERRED_SIZING { RECT rect = wxGetWindowRect(GetHwnd()); @@ -1535,21 +1792,9 @@ void wxWindowMSW::DoGetSize(int *x, int *y) const void wxWindowMSW::DoGetClientSize(int *x, int *y) const { #if USE_DEFERRED_SIZING - if ( IsTopLevel() || m_pendingSize == wxDefaultSize ) -#endif - { // top level windows resizing is never deferred, so we can safely use - // the current size here - RECT rect = wxGetClientRect(GetHwnd()); - - if ( x ) - *x = rect.right; - if ( y ) - *y = rect.bottom; - } -#if USE_DEFERRED_SIZING - else // non top level and using deferred sizing + if ( m_pendingSize != wxDefaultSize ) { - // we need to calculate the *pending* client size here + // we need to calculate the client size corresponding to pending size RECT rect; rect.left = m_pendingPosition.x; rect.top = m_pendingPosition.y; @@ -1563,7 +1808,16 @@ void wxWindowMSW::DoGetClientSize(int *x, int *y) const if ( y ) *y = rect.bottom - rect.top; } -#endif + else +#endif // USE_DEFERRED_SIZING + { + RECT rect = wxGetClientRect(GetHwnd()); + + if ( x ) + *x = rect.right; + if ( y ) + *y = rect.bottom; + } } void wxWindowMSW::DoGetPosition(int *x, int *y) const @@ -1587,6 +1841,13 @@ void wxWindowMSW::DoGetPosition(int *x, int *y) const // children, not for the dialogs/frames if ( !IsTopLevel() ) { + if ( wxTheApp->GetLayoutDirection() == wxLayout_RightToLeft ) + { + // In RTL mode, we want the logical left x-coordinate, + // which would be the physical right x-coordinate. + point.x = rect.right; + } + // Since we now have the absolute screen coords, if there's a // parent we must subtract its top left corner if ( parent ) @@ -1880,7 +2141,7 @@ void wxWindowMSW::GetTextExtent(const wxString& string, SIZE sizeRect; TEXTMETRIC tm; - ::GetTextExtentPoint32(hdc, string, string.length(), &sizeRect); + ::GetTextExtentPoint32(hdc, string.wx_str(), string.length(), &sizeRect); GetTextMetrics(hdc, &tm); if ( x ) @@ -1945,10 +2206,22 @@ bool wxWindowMSW::DoPopupMenu(wxMenu *menu, int x, int y) ::ClientToScreen(hWnd, &point); wxCurrentPopupMenu = menu; #if defined(__WXWINCE__) - UINT flags = 0; -#else + static const UINT flags = 0; +#else // !__WXWINCE__ UINT flags = TPM_RIGHTBUTTON; -#endif + // NT4 doesn't support TPM_RECURSE and simply doesn't show the menu at all + // when it's use, I'm not sure about Win95/98 but prefer to err on the safe + // side and not to use it there neither -- modify the test if it does work + // on these systems + if ( wxGetWinVersion() >= wxWinVersion_5 ) + { + // using TPM_RECURSE allows us to show a popup menu while another menu + // is opened which can be useful and is supported by the other + // platforms, so allow it under Windows too + flags |= TPM_RECURSE; + } +#endif // __WXWINCE__/!__WXWINCE__ + ::TrackPopupMenu(hMenu, flags, point.x, point.y, 0, hWnd, NULL); // we need to do it right now as otherwise the events are never going to be @@ -2000,18 +2273,14 @@ bool wxWindowMSW::MSWProcessMessage(WXMSG* pMsg) // WM_GETDLGCODE: ask the control if it wants the key for itself, // don't process it if it's the case (except for Ctrl-Tab/Enter // combinations which are always processed) - LONG lDlgCode = 0; - if ( !bCtrlDown ) - { - lDlgCode = ::SendMessage(msg->hwnd, WM_GETDLGCODE, 0, 0); + LONG lDlgCode = ::SendMessage(msg->hwnd, WM_GETDLGCODE, 0, 0); - // surprizingly, 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 ) - { - lDlgCode |= DLGC_WANTTAB | DLGC_WANTARROWS; - } + // surprizingly, 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 ) + { + lDlgCode |= DLGC_WANTTAB | DLGC_WANTARROWS; } bool bForward = true, @@ -2023,10 +2292,13 @@ bool wxWindowMSW::MSWProcessMessage(WXMSG* pMsg) switch ( msg->wParam ) { case VK_TAB: - if ( lDlgCode & DLGC_WANTTAB ) { + if ( (lDlgCode & DLGC_WANTTAB) && !bCtrlDown ) + { + // let the control have the TAB bProcess = false; } - else { + else // use it for navigation + { // Ctrl-Tab cycles thru notebook pages bWindowChange = bCtrlDown; bForward = !bShiftDown; @@ -2048,18 +2320,27 @@ bool wxWindowMSW::MSWProcessMessage(WXMSG* pMsg) bProcess = false; break; + case VK_PRIOR: + bForward = false; + // fall through + + case VK_NEXT: + // we treat PageUp/Dn as arrows because chances are that + // a control which needs arrows also needs them for + // navigation (e.g. wxTextCtrl, wxListCtrl, ...) + if ( (lDlgCode & DLGC_WANTARROWS) && !bCtrlDown ) + bProcess = false; + else // OTOH Ctrl-PageUp/Dn works as [Shift-]Ctrl-Tab + bWindowChange = true; + break; + case VK_RETURN: { - if ( (lDlgCode & DLGC_WANTMESSAGE) && !bCtrlDown ) - { - // control wants to process Enter itself, don't - // call IsDialogMessage() which would interpret - // it - return false; - } - +#if wxUSE_BUTTON // currently active button should get enter press even - // if there is a default button elsewhere + // if there is a default button elsewhere so check if + // this window is a button first + wxWindow *btn = NULL; if ( lDlgCode & DLGC_DEFPUSHBUTTON ) { // let IsDialogMessage() handle this for all @@ -2069,56 +2350,78 @@ bool wxWindowMSW::MSWProcessMessage(WXMSG* pMsg) if ( (style & BS_OWNERDRAW) == BS_OWNERDRAW ) { // emulate the button click - wxWindow * - btn = wxFindWinFromHandle((WXHWND)msg->hwnd); - if ( btn ) - btn->MSWCommand(BN_CLICKED, 0 /* unused */); + btn = wxFindWinFromHandle((WXHWND)msg->hwnd); } bProcess = false; } - else // not a button itself + else // not a button itself, do we have default button? { -#if wxUSE_BUTTON - wxButton *btn = wxDynamicCast(GetDefaultItem(), - wxButton); - if ( btn && btn->IsEnabled() ) + // check if this window or any of its ancestors + // wants the message for itself (we always reserve + // Ctrl-Enter for dialog navigation though) + wxWindow *win = this; + if ( !bCtrlDown ) { - // if we do have a default button, do press it - btn->MSWCommand(BN_CLICKED, 0 /* unused */); + // this will contain the dialog code of this + // window and all of its parent windows in turn + LONG lDlgCode2 = lDlgCode; - return true; - } - else // no default button -#endif // wxUSE_BUTTON - { -#ifdef __WXWINCE__ - wxJoystickEvent event(wxEVT_JOY_BUTTON_DOWN); - event.SetEventObject(this); - if(GetEventHandler()->ProcessEvent(event)) - return true; -#endif - // this is a quick and dirty test for a text - // control - if ( !(lDlgCode & DLGC_HASSETSEL) ) - { - // don't process Enter, the control might - // need it for itself and don't let - // ::IsDialogMessage() have it as it can - // eat the Enter events sometimes - return false; - } - else if (!IsTopLevel()) + while ( win ) { - // if not a top level window, let parent - // handle it - return false; + if ( lDlgCode2 & DLGC_WANTMESSAGE ) + { + // as it wants to process Enter itself, + // don't call IsDialogMessage() which + // would consume it + return false; + } + + // don't propagate keyboard messages beyond + // the first top level window parent + if ( win->IsTopLevel() ) + break; + + win = win->GetParent(); + + lDlgCode2 = ::SendMessage + ( + GetHwndOf(win), + WM_GETDLGCODE, + 0, + 0 + ); } - //else: treat Enter as TAB: pass to the next - // control as this is the best thing to do - // if the text doesn't handle Enter itself + } + else // bCtrlDown + { + win = wxGetTopLevelParent(win); + } + + wxTopLevelWindow * const + tlw = wxDynamicCast(win, wxTopLevelWindow); + if ( tlw ) + { + btn = wxDynamicCast(tlw->GetDefaultItem(), + wxButton); } } + + if ( btn && btn->IsEnabled() ) + { + btn->MSWCommand(BN_CLICKED, 0 /* unused */); + return true; + } + +#endif // wxUSE_BUTTON + +#ifdef __WXWINCE__ + // map Enter presses into button presses on PDAs + wxJoystickEvent event(wxEVT_JOY_BUTTON_DOWN); + event.SetEventObject(this); + if ( GetEventHandler()->ProcessEvent(event) ) + return true; +#endif // __WXWINCE__ } break; @@ -2146,90 +2449,10 @@ bool wxWindowMSW::MSWProcessMessage(WXMSG* pMsg) } } - // don't let IsDialogMessage() get VK_ESCAPE as it _always_ eats the - // message even when there is no cancel button and when the message is - // needed by the control itself: in particular, it prevents the tree in - // place edit control from being closed with Escape in a dialog - if ( msg->message != WM_KEYDOWN || msg->wParam != VK_ESCAPE ) + if ( ::IsDialogMessage(GetHwnd(), msg) ) { - // ::IsDialogMessage() is broken and may sometimes hang the - // application by going into an infinite loop, so we try to detect - // [some of] the situations when this may happen and not call it - // then - - // assume we can call it by default - bool canSafelyCallIsDlgMsg = true; - - HWND hwndFocus = ::GetFocus(); - - // if the currently focused window itself has WS_EX_CONTROLPARENT style, ::IsDialogMessage() will also enter - // an infinite loop, because it will recursively check the child - // windows but not the window itself and so if none of the children - // accepts focus it loops forever (as it only stops when it gets - // back to the window it started from) - // - // while it is very unusual that a window with WS_EX_CONTROLPARENT - // style has the focus, it can happen. One such possibility is if - // all windows are either toplevel, wxDialog, wxPanel or static - // controls and no window can actually accept keyboard input. -#if !defined(__WXWINCE__) - if ( ::GetWindowLong(hwndFocus, GWL_EXSTYLE) & WS_EX_CONTROLPARENT ) - { - // pessimistic by default - canSafelyCallIsDlgMsg = false; - for ( wxWindowList::compatibility_iterator node = GetChildren().GetFirst(); - node; - node = node->GetNext() ) - { - wxWindow * const win = node->GetData(); - if ( win->AcceptsFocus() && - !(::GetWindowLong(GetHwndOf(win), GWL_EXSTYLE) & - WS_EX_CONTROLPARENT) ) - { - // it shouldn't hang... - canSafelyCallIsDlgMsg = true; - - break; - } - } - } -#endif // !__WXWINCE__ - - if ( canSafelyCallIsDlgMsg ) - { - // ::IsDialogMessage() can enter in an infinite loop when the - // currently focused window is disabled or hidden and its - // parent has WS_EX_CONTROLPARENT style, so don't call it in - // this case - while ( hwndFocus ) - { - if ( !::IsWindowEnabled(hwndFocus) || - !::IsWindowVisible(hwndFocus) ) - { - // it would enter an infinite loop if we do this! - canSafelyCallIsDlgMsg = false; - - break; - } - - if ( !(::GetWindowLong(hwndFocus, GWL_STYLE) & WS_CHILD) ) - { - // it's a top level window, don't go further -- e.g. even - // if the parent of a dialog is disabled, this doesn't - // break navigation inside the dialog - break; - } - - hwndFocus = ::GetParent(hwndFocus); - } - } - - // let IsDialogMessage() have the message if it's safe to call it - if ( canSafelyCallIsDlgMsg && ::IsDialogMessage(GetHwnd(), msg) ) - { - // IsDialogMessage() did something... - return true; - } + // IsDialogMessage() did something... + return true; } } #endif // __WXUNIVERSAL__ @@ -2240,7 +2463,7 @@ bool wxWindowMSW::MSWProcessMessage(WXMSG* pMsg) // relay mouse move events to the tooltip control MSG *msg = (MSG *)pMsg; if ( msg->message == WM_MOUSEMOVE ) - m_tooltip->RelayEvent(pMsg); + wxToolTip::RelayEvent(pMsg); } #endif // wxUSE_TOOLTIPS @@ -2257,10 +2480,96 @@ bool wxWindowMSW::MSWTranslateMessage(WXMSG* pMsg) #endif // wxUSE_ACCEL } -bool wxWindowMSW::MSWShouldPreProcessMessage(WXMSG* WXUNUSED(pMsg)) +bool wxWindowMSW::MSWShouldPreProcessMessage(WXMSG* msg) { - // preprocess all messages by default - return true; + // all tests below have to deal with various bugs/misfeatures of + // IsDialogMessage(): we have to prevent it from being called from our + // MSWProcessMessage() in some situations + + // don't let IsDialogMessage() get VK_ESCAPE as it _always_ eats the + // message even when there is no cancel button and when the message is + // needed by the control itself: in particular, it prevents the tree in + // place edit control from being closed with Escape in a dialog + if ( msg->message == WM_KEYDOWN && msg->wParam == VK_ESCAPE ) + { + return false; + } + + // ::IsDialogMessage() is broken and may sometimes hang the application by + // going into an infinite loop when it tries to find the control to give + // focus to when Alt- is pressed, so we try to detect [some of] the + // situations when this may happen and not call it then + if ( msg->message != WM_SYSCHAR ) + return true; + + // assume we can call it by default + bool canSafelyCallIsDlgMsg = true; + + HWND hwndFocus = ::GetFocus(); + + // if the currently focused window itself has WS_EX_CONTROLPARENT style, + // ::IsDialogMessage() will also enter an infinite loop, because it will + // recursively check the child windows but not the window itself and so if + // none of the children accepts focus it loops forever (as it only stops + // when it gets back to the window it started from) + // + // while it is very unusual that a window with WS_EX_CONTROLPARENT + // style has the focus, it can happen. One such possibility is if + // all windows are either toplevel, wxDialog, wxPanel or static + // controls and no window can actually accept keyboard input. +#if !defined(__WXWINCE__) + if ( ::GetWindowLong(hwndFocus, GWL_EXSTYLE) & WS_EX_CONTROLPARENT ) + { + // pessimistic by default + canSafelyCallIsDlgMsg = false; + for ( wxWindowList::compatibility_iterator node = GetChildren().GetFirst(); + node; + node = node->GetNext() ) + { + wxWindow * const win = node->GetData(); + if ( win->CanAcceptFocus() && + !(::GetWindowLong(GetHwndOf(win), GWL_EXSTYLE) & + WS_EX_CONTROLPARENT) ) + { + // it shouldn't hang... + canSafelyCallIsDlgMsg = true; + + break; + } + } + } +#endif // !__WXWINCE__ + + if ( canSafelyCallIsDlgMsg ) + { + // ::IsDialogMessage() can enter in an infinite loop when the + // currently focused window is disabled or hidden and its + // parent has WS_EX_CONTROLPARENT style, so don't call it in + // this case + while ( hwndFocus ) + { + if ( !::IsWindowEnabled(hwndFocus) || + !::IsWindowVisible(hwndFocus) ) + { + // it would enter an infinite loop if we do this! + canSafelyCallIsDlgMsg = false; + + break; + } + + if ( !(::GetWindowLong(hwndFocus, GWL_STYLE) & WS_CHILD) ) + { + // it's a top level window, don't go further -- e.g. even + // if the parent of a dialog is disabled, this doesn't + // break navigation inside the dialog + break; + } + + hwndFocus = ::GetParent(hwndFocus); + } + } + + return canSafelyCallIsDlgMsg; } // --------------------------------------------------------------------------- @@ -2350,7 +2659,7 @@ LRESULT WXDLLEXPORT APIENTRY _EXPORT wxWndProc(HWND hWnd, UINT message, WPARAM w LRESULT rc; - if ( wnd && wxEventLoop::AllowProcessing(wnd) ) + if ( wnd && wxGUIEventLoop::AllowProcessing(wnd) ) rc = wnd->MSWWindowProc(message, wParam, lParam); else rc = ::DefWindowProc(hWnd, message, wParam, lParam); @@ -2422,7 +2731,19 @@ WXLRESULT wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM l } } break; +#if 0 + case WM_ENTERSIZEMOVE: + { + processed = HandleEnterSizeMove(); + } + break; + case WM_EXITSIZEMOVE: + { + processed = HandleExitSizeMove(); + } + break; +#endif case WM_SIZING: { LPRECT pRect = (LPRECT)lParam; @@ -2509,7 +2830,8 @@ WXLRESULT wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM l #ifdef HAVE_TRACKMOUSEEVENT case WM_MOUSELEAVE: - // filter out excess WM_MOUSELEAVE events sent after PopupMenu() (on XP at least) + // filter out excess WM_MOUSELEAVE events sent after PopupMenu() + // (on XP at least) if ( m_mouseInWindow ) { GenerateMouseLeave(); @@ -2537,6 +2859,11 @@ WXLRESULT wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM l case WM_MBUTTONDOWN: case WM_MBUTTONUP: case WM_MBUTTONDBLCLK: +#ifdef wxHAS_XBUTTON + case WM_XBUTTONDOWN: + case WM_XBUTTONUP: + case WM_XBUTTONDBLCLK: +#endif // wxHAS_XBUTTON { #ifdef __WXMICROWIN__ // MicroWindows seems to ignore the fact that a window is @@ -2582,6 +2909,36 @@ WXLRESULT wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM l wxCHECK_MSG( win, 0, _T("FindWindowForMouseEvent() returned NULL") ); } +#ifdef __POCKETPC__ + if (IsContextMenuEnabled() && message == WM_LBUTTONDOWN) + { + SHRGINFO shrgi = {0}; + + shrgi.cbSize = sizeof(SHRGINFO); + shrgi.hwndClient = (HWND) GetHWND(); + shrgi.ptDown.x = x; + shrgi.ptDown.y = y; + + shrgi.dwFlags = SHRG_RETURNCMD; + // shrgi.dwFlags = SHRG_NOTIFYPARENT; + + if (GN_CONTEXTMENU == ::SHRecognizeGesture(&shrgi)) + { + wxPoint pt(x, y); + pt = ClientToScreen(pt); + + wxContextMenuEvent evtCtx(wxEVT_CONTEXT_MENU, GetId(), pt); + + evtCtx.SetEventObject(this); + if (GetEventHandler()->ProcessEvent(evtCtx)) + { + processed = true; + return true; + } + } + } +#endif + #else // !__WXWINCE__ wxWindowMSW *win = this; #endif // __WXWINCE__/!__WXWINCE__ @@ -2597,7 +2954,7 @@ WXLRESULT wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM l // problems, so don't do it for them (unnecessary anyhow) if ( !win->IsOfStandardClass() ) { - if ( message == WM_LBUTTONDOWN && win->AcceptsFocus() ) + if ( message == WM_LBUTTONDOWN && win->IsFocusable() ) win->SetFocus(); } } @@ -2671,12 +3028,12 @@ WXLRESULT wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM l #endif // defined(WM_DRAWITEM) case WM_GETDLGCODE: - if ( !IsOfStandardClass() ) + if ( !IsOfStandardClass() || HasFlag(wxWANTS_CHARS) ) { // we always want to get the char events rc.result = DLGC_WANTCHARS; - if ( GetWindowStyleFlag() & wxWANTS_CHARS ) + if ( HasFlag(wxWANTS_CHARS) ) { // in fact, we want everything rc.result |= DLGC_WANTARROWS | @@ -2726,6 +3083,16 @@ WXLRESULT wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM l case VK_SUBTRACT: case VK_MULTIPLY: case VK_DIVIDE: + case VK_NUMPAD0: + case VK_NUMPAD1: + case VK_NUMPAD2: + case VK_NUMPAD3: + case VK_NUMPAD4: + case VK_NUMPAD5: + case VK_NUMPAD6: + case VK_NUMPAD7: + case VK_NUMPAD8: + case VK_NUMPAD9: case VK_OEM_1: case VK_OEM_2: case VK_OEM_3: @@ -2849,6 +3216,10 @@ WXLRESULT wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM l processed = HandleCaptureChanged((WXHWND) (HWND) lParam); break; + case WM_SETTINGCHANGE: + processed = HandleSettingChange(wParam, lParam); + break; + case WM_QUERYNEWPALETTE: processed = HandleQueryNewPalette(); break; @@ -2923,50 +3294,48 @@ WXLRESULT wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM l #if defined(WM_HELP) case WM_HELP: { - // HELPINFO doesn't seem to be supported on WinCE. + // by default, WM_HELP is propagated by DefWindowProc() upwards + // to the window parent but as we do it ourselves already + // (wxHelpEvent is derived from wxCommandEvent), we don't want + // to get the other events if we process this message at all + processed = true; + + // WM_HELP doesn't use lParam under CE #ifndef __WXWINCE__ HELPINFO* info = (HELPINFO*) lParam; - // Don't yet process menu help events, just windows - if (info->iContextType == HELPINFO_WINDOW) + if ( info->iContextType == HELPINFO_WINDOW ) { -#endif - wxWindowMSW* subjectOfHelp = this; - bool eventProcessed = false; - while (subjectOfHelp && !eventProcessed) - { - wxHelpEvent helpEvent(wxEVT_HELP, - subjectOfHelp->GetId(), +#endif // !__WXWINCE__ + wxHelpEvent helpEvent + ( + wxEVT_HELP, + GetId(), #ifdef __WXWINCE__ - wxPoint(0,0) + wxGetMousePosition() // what else? #else - wxPoint(info->MousePos.x, info->MousePos.y) + wxPoint(info->MousePos.x, info->MousePos.y) #endif - ); - - helpEvent.SetEventObject(this); - eventProcessed = - GetEventHandler()->ProcessEvent(helpEvent); + ); - // Go up the window hierarchy until the event is - // handled (or not) - subjectOfHelp = subjectOfHelp->GetParent(); - } - - processed = eventProcessed; + helpEvent.SetEventObject(this); + GetEventHandler()->ProcessEvent(helpEvent); #ifndef __WXWINCE__ } - else if (info->iContextType == HELPINFO_MENUITEM) + else if ( info->iContextType == HELPINFO_MENUITEM ) { wxHelpEvent helpEvent(wxEVT_HELP, info->iCtrlId); helpEvent.SetEventObject(this); - processed = GetEventHandler()->ProcessEvent(helpEvent); + GetEventHandler()->ProcessEvent(helpEvent); } - //else: processed is already false -#endif + else // unknown help event? + { + processed = false; + } +#endif // !__WXWINCE__ } break; -#endif +#endif // WM_HELP #if !defined(__WXWINCE__) case WM_CONTEXTMENU: @@ -2994,6 +3363,7 @@ WXLRESULT wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM l break; #endif +#if wxUSE_MENUS case WM_MENUCHAR: // we're only interested in our own menus, not MF_SYSMENU if ( HIWORD(wParam) == MF_POPUP ) @@ -3002,11 +3372,121 @@ WXLRESULT wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM l int i = HandleMenuChar(toupper(LOWORD(wParam)), lParam); if ( i != wxNOT_FOUND ) { - rc.result = MAKELRESULT(i, MNC_EXECUTE); + rc.result = MAKELRESULT(i, MNC_EXECUTE); + processed = true; + } + } + break; +#endif // wxUSE_MENUS + +#ifndef __WXWINCE__ + case WM_POWERBROADCAST: + { + bool vetoed; + processed = HandlePower(wParam, lParam, &vetoed); + rc.result = processed && vetoed ? BROADCAST_QUERY_DENY : TRUE; + } + break; +#endif // __WXWINCE__ + +#if wxUSE_UXTHEME + // If we want the default themed border then we need to draw it ourselves + case WM_NCCALCSIZE: + { + wxUxThemeEngine* theme = wxUxThemeEngine::GetIfActive(); + const wxBorder border = TranslateBorder(GetBorder()); + if (theme && border == wxBORDER_THEME) + { + // first ask the widget to calculate the border size + rc.result = MSWDefWindowProc(message, wParam, lParam); + processed = true; + + // now alter the client size making room for drawing a themed border + NCCALCSIZE_PARAMS *csparam = NULL; + RECT rect; + if (wParam) + { + csparam = (NCCALCSIZE_PARAMS*)lParam; + rect = csparam->rgrc[0]; + } + else + { + rect = *((RECT*)lParam); + } + wxUxThemeHandle hTheme((wxWindow *)this, L"EDIT"); + RECT rcClient = { 0, 0, 0, 0 }; + wxClientDC dc((wxWindow *)this); + + if (theme->GetThemeBackgroundContentRect( + hTheme, GetHdcOf(dc), EP_EDITTEXT, ETS_NORMAL, + &rect, &rcClient) == S_OK) + { + InflateRect(&rcClient, -1, -1); + if (wParam) + csparam->rgrc[0] = rcClient; + else + *((RECT*)lParam) = rcClient; + rc.result = WVR_REDRAW; + } + } + } + break; + + case WM_NCPAINT: + { + wxUxThemeEngine* theme = wxUxThemeEngine::GetIfActive(); + const wxBorder border = TranslateBorder(GetBorder()); + if (theme && border == wxBORDER_THEME) + { + // first ask the widget to paint its non-client area, such as scrollbars, etc. + rc.result = MSWDefWindowProc(message, wParam, lParam); processed = true; + + wxUxThemeHandle hTheme((wxWindow *)this, L"EDIT"); + wxWindowDC dc((wxWindow *)this); + + // Clip the DC so that you only draw on the non-client area + RECT rcBorder; + wxCopyRectToRECT(GetSize(), rcBorder); + + RECT rcClient; + theme->GetThemeBackgroundContentRect( + hTheme, GetHdcOf(dc), EP_EDITTEXT, ETS_NORMAL, &rcBorder, &rcClient); + InflateRect(&rcClient, -1, -1); + + ::ExcludeClipRect(GetHdcOf(dc), rcClient.left, rcClient.top, + rcClient.right, rcClient.bottom); + + // Make sure the background is in a proper state + if (theme->IsThemeBackgroundPartiallyTransparent(hTheme, EP_EDITTEXT, ETS_NORMAL)) + { + theme->DrawThemeParentBackground(GetHwnd(), GetHdcOf(dc), &rcBorder); + } + + // Draw the border + int nState; + if ( !IsEnabled() ) + nState = ETS_DISABLED; + // should we check this? + //else if ( ::GetWindowLong(GetHwnd(), GWL_STYLE) & ES_READONLY) + // nState = ETS_READONLY; + else + nState = ETS_NORMAL; + theme->DrawThemeBackground(hTheme, GetHdcOf(dc), EP_EDITTEXT, nState, &rcBorder, NULL); } } break; + +#endif // wxUSE_UXTHEME + + default: + // try a custom message handler + const MSWMessageHandlers::const_iterator + i = gs_messageHandlers.find(message); + if ( i != gs_messageHandlers.end() ) + { + processed = (*i->second)(this, message, wParam, lParam); + } } if ( !processed ) @@ -3193,8 +3673,8 @@ bool wxWindowMSW::MSWCreate(const wxChar *wclass, m_hWnd = (WXHWND)::CreateWindowEx ( extendedStyle, - className, - title ? title : m_windowName.c_str(), + className.wx_str(), + title ? title : m_windowName.wx_str(), style, x, y, w, h, (HWND)MSWGetParent(), @@ -3306,7 +3786,7 @@ bool wxWindowMSW::HandleTooltipNotify(WXUINT code, // Truncate tooltip length if needed as otherwise we might not have // enough space for it in the buffer and MultiByteToWideChar() would // return an error - size_t tipLength = wxMin(ttip.Len(), WXSIZEOF(buf) - 1); + size_t tipLength = wxMin(ttip.length(), WXSIZEOF(buf) - 1); // Convert to WideChar without adding the NULL character. The NULL // character is added afterwards (this is more efficient). @@ -3314,7 +3794,7 @@ bool wxWindowMSW::HandleTooltipNotify(WXUINT code, ( CP_ACP, 0, // no flags - ttip, + ttip.wx_str(), tipLength, buf, WXSIZEOF(buf) - 1 @@ -3434,10 +3914,6 @@ bool wxWindowMSW::HandleCreate(WXLPCREATESTRUCT WXUNUSED_IN_WINCE(cs), EnsureParentHasControlParentStyle(GetParent()); #endif // !__WXWINCE__ - // TODO: should generate this event from WM_NCCREATE - wxWindowCreateEvent event((wxWindow *)this); - (void)GetEventHandler()->ProcessEvent(event); - *mayCreate = true; return true; @@ -3636,63 +4112,56 @@ bool wxWindowMSW::HandleSetCursor(WXHWND WXUNUSED(hWnd), { #ifndef __WXMICROWIN__ // the logic is as follows: - // -1. don't set cursor for non client area, including but not limited to - // the title bar, scrollbars, &c - // 0. allow the user to override default behaviour by using EVT_SET_CURSOR - // 1. if we have the cursor set it unless wxIsBusy() - // 2. if we're a top level window, set some cursor anyhow - // 3. if wxIsBusy(), set the busy cursor, otherwise the global one + // 0. if we're busy, set the busy cursor (even for non client elements) + // 1. don't set custom cursor for non client area of enabled windows + // 2. ask user EVT_SET_CURSOR handler for the cursor + // 3. if still no cursor but we're in a TLW, set the global cursor - if ( nHitTest != HTCLIENT ) + HCURSOR hcursor = 0; + if ( wxIsBusy() ) { - return false; + hcursor = wxGetCurrentBusyCursor(); } + else // not busy + { + if ( nHitTest != HTCLIENT ) + return false; - HCURSOR hcursor = 0; - - // 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; + // 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)) + if ( !::GetCursorPosWinCE(&pt)) #else - if ( !::GetCursorPos(&pt) ) + if ( !::GetCursorPos(&pt) ) #endif - { - wxLogLastError(wxT("GetCursorPos")); - } - - int x = pt.x, - y = pt.y; - ScreenToClient(&x, &y); - wxSetCursorEvent event(x, y); - - bool processedEvtSetCursor = GetEventHandler()->ProcessEvent(event); - if ( processedEvtSetCursor && event.HasCursor() ) - { - hcursor = GetHcursorOf(event.GetCursor()); - } + { + wxLogLastError(wxT("GetCursorPos")); + } - if ( !hcursor ) - { - bool isBusy = wxIsBusy(); + int x = pt.x, + y = pt.y; + ScreenToClient(&x, &y); + wxSetCursorEvent event(x, y); - // the test for processedEvtSetCursor is here to prevent using 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() ) + bool processedEvtSetCursor = GetEventHandler()->ProcessEvent(event); + if ( processedEvtSetCursor && event.HasCursor() ) { - hcursor = GetHcursorOf(m_cursor); + hcursor = GetHcursorOf(event.GetCursor()); } - if ( !GetParent() ) + if ( !hcursor ) { - if ( isBusy ) + // the test for processedEvtSetCursor is here to prevent using + // 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() ) { - hcursor = wxGetCurrentBusyCursor(); + hcursor = GetHcursorOf(m_cursor); } - else if ( !hcursor ) + + if ( !hcursor && !GetParent() ) { const wxCursor *cursor = wxGetGlobalCursor(); if ( cursor && cursor->Ok() ) @@ -3703,10 +4172,9 @@ bool wxWindowMSW::HandleSetCursor(WXHWND WXUNUSED(hWnd), } } + if ( hcursor ) { -// wxLogDebug("HandleSetCursor: Setting cursor %ld", (long) hcursor); - ::SetCursor(hcursor); // cursor set, stop here @@ -3718,6 +4186,82 @@ bool wxWindowMSW::HandleSetCursor(WXHWND WXUNUSED(hWnd), return false; } +bool wxWindowMSW::HandlePower(WXWPARAM WXUNUSED_IN_WINCE(wParam), + WXLPARAM WXUNUSED(lParam), + bool *WXUNUSED_IN_WINCE(vetoed)) +{ +#ifdef __WXWINCE__ + // FIXME + return false; +#else + wxEventType evtType; + switch ( wParam ) + { + case PBT_APMQUERYSUSPEND: + evtType = wxEVT_POWER_SUSPENDING; + break; + + case PBT_APMQUERYSUSPENDFAILED: + evtType = wxEVT_POWER_SUSPEND_CANCEL; + break; + + case PBT_APMSUSPEND: + evtType = wxEVT_POWER_SUSPENDED; + break; + + case PBT_APMRESUMESUSPEND: +#ifdef PBT_APMRESUMEAUTOMATIC + case PBT_APMRESUMEAUTOMATIC: +#endif + evtType = wxEVT_POWER_RESUME; + break; + + default: + wxLogDebug(_T("Unknown WM_POWERBROADCAST(%d) event"), wParam); + // fall through + + // these messages are currently not mapped to wx events + case PBT_APMQUERYSTANDBY: + case PBT_APMQUERYSTANDBYFAILED: + case PBT_APMSTANDBY: + case PBT_APMRESUMESTANDBY: + case PBT_APMBATTERYLOW: + case PBT_APMPOWERSTATUSCHANGE: + case PBT_APMOEMEVENT: + case PBT_APMRESUMECRITICAL: + evtType = wxEVT_NULL; + break; + } + + // don't handle unknown messages + if ( evtType == wxEVT_NULL ) + return false; + + // TODO: notify about PBTF_APMRESUMEFROMFAILURE in case of resume events? + + wxPowerEvent event(evtType); + if ( !GetEventHandler()->ProcessEvent(event) ) + return false; + + *vetoed = event.IsVetoed(); + + return true; +#endif +} + +bool wxWindowMSW::IsDoubleBuffered() const +{ + for ( const wxWindowMSW *wnd = this; + wnd && !wnd->IsTopLevel(); wnd = + wnd->GetParent() ) + { + if ( ::GetWindowLong(GetHwndOf(wnd), GWL_EXSTYLE) & WS_EX_COMPOSITED ) + return true; + } + + return false; +} + // --------------------------------------------------------------------------- // owner drawn stuff // --------------------------------------------------------------------------- @@ -3927,12 +4471,40 @@ bool wxWindowMSW::HandlePaletteChanged(WXHWND hWndPalChange) bool wxWindowMSW::HandleCaptureChanged(WXHWND hWndGainedCapture) { - wxMouseCaptureChangedEvent event(GetId(), wxFindWinFromHandle(hWndGainedCapture)); - event.SetEventObject(this); + // notify windows on the capture stack about lost capture + // (see http://sourceforge.net/tracker/index.php?func=detail&aid=1153662&group_id=9863&atid=109863): + wxWindowBase::NotifyCaptureLost(); + wxWindow *win = wxFindWinFromHandle(hWndGainedCapture); + wxMouseCaptureChangedEvent event(GetId(), win); + event.SetEventObject(this); return GetEventHandler()->ProcessEvent(event); } +bool wxWindowMSW::HandleSettingChange(WXWPARAM wParam, WXLPARAM lParam) +{ + // despite MSDN saying "(This message cannot be sent directly to a window.)" + // we need to send this to child windows (it is only sent to top-level + // windows) so {list,tree}ctrls can adjust their font size if necessary + // this is exactly how explorer does it to enable the font size changes + + wxWindowList::compatibility_iterator node = GetChildren().GetFirst(); + while ( node ) + { + // top-level windows already get this message from the system + wxWindow *win = node->GetData(); + if ( !win->IsTopLevel() ) + { + ::SendMessage(GetHwndOf(win), WM_SETTINGCHANGE, wParam, lParam); + } + + node = node->GetNext(); + } + + // let the system handle it + return false; +} + bool wxWindowMSW::HandleQueryNewPalette() { @@ -4086,6 +4658,10 @@ bool wxWindowMSW::HandlePaint() eventNc.SetEventObject(this); GetEventHandler()->ProcessEvent(eventNc); + // don't keep an HRGN we don't need any longer (GetUpdateRegion() can only + // be called from inside the event handlers called above) + m_updateRegion.Clear(); + return processed; } @@ -4105,7 +4681,7 @@ void wxWindowMSW::OnPaint(wxPaintEvent& event) bool wxWindowMSW::HandleEraseBkgnd(WXHDC hdc) { - wxDCTemp dc(hdc); + wxDCTemp dc(hdc, GetClientSize()); dc.SetHDC(hdc); dc.SetWindow((wxWindow *)this); @@ -4282,6 +4858,24 @@ bool wxWindowMSW::HandleMoving(wxRect& rect) return rc; } +bool wxWindowMSW::HandleEnterSizeMove() +{ + wxMoveEvent event(wxPoint(0,0), m_windowId); + event.SetEventType(wxEVT_MOVE_START); + event.SetEventObject(this); + + return GetEventHandler()->ProcessEvent(event); +} + +bool wxWindowMSW::HandleExitSizeMove() +{ + wxMoveEvent event(wxPoint(0,0), m_windowId); + event.SetEventType(wxEVT_MOVE_END); + event.SetEventObject(this); + + return GetEventHandler()->ProcessEvent(event); +} + bool wxWindowMSW::HandleSize(int WXUNUSED(w), int WXUNUSED(h), WXUINT wParam) { #if USE_DEFERRED_SIZING @@ -4433,8 +5027,11 @@ bool wxWindowMSW::HandleGetMinMaxInfo(void *WXUNUSED_IN_WINCE(mmInfo)) // command messages // --------------------------------------------------------------------------- -bool wxWindowMSW::HandleCommand(WXWORD id, WXWORD cmd, WXHWND control) +bool wxWindowMSW::HandleCommand(WXWORD id_, WXWORD cmd, WXHWND control) { + // sign extend to int from short before comparing with the other int ids + int id = (signed short)id_; + #if wxUSE_MENUS_NATIVE if ( !cmd && wxCurrentPopupMenu ) { @@ -4457,8 +5054,7 @@ bool wxWindowMSW::HandleCommand(WXWORD id, WXWORD cmd, WXHWND control) // try the id if ( !win ) { - // must cast to a signed type before comparing with other ids! - win = FindItem((signed short)id); + win = FindItem(id); } if ( win ) @@ -4524,7 +5120,11 @@ void wxWindowMSW::InitMouseEvent(wxMouseEvent& event, event.m_leftDown = (flags & MK_LBUTTON) != 0; event.m_middleDown = (flags & MK_MBUTTON) != 0; event.m_rightDown = (flags & MK_RBUTTON) != 0; - event.m_altDown = ::GetKeyState(VK_MENU) < 0; +#ifdef wxHAS_XBUTTON + event.m_aux1Down = (flags & MK_XBUTTON1) != 0; + event.m_aux2Down = (flags & MK_XBUTTON2) != 0; +#endif // wxHAS_XBUTTON + event.m_altDown = ::wxIsAltDown(); #ifndef __WXWINCE__ event.SetTimestamp(::GetMessageTime()); @@ -4622,9 +5222,29 @@ bool wxWindowMSW::HandleMouseEvent(WXUINT msg, int x, int y, WXUINT flags) wxEVT_RIGHT_DCLICK, wxEVT_MIDDLE_DOWN, wxEVT_MIDDLE_UP, - wxEVT_MIDDLE_DCLICK + wxEVT_MIDDLE_DCLICK, + 0, // this one is for wxEVT_MOTION which is not used here + wxEVT_AUX1_DOWN, + wxEVT_AUX1_UP, + wxEVT_AUX1_DCLICK, + wxEVT_AUX2_DOWN, + wxEVT_AUX2_UP, + wxEVT_AUX2_DCLICK }; +#ifdef wxHAS_XBUTTON + // the same messages are used for both auxillary mouse buttons so we need + // to adjust the index manually + switch ( msg ) + { + case WM_XBUTTONDOWN: + case WM_XBUTTONUP: + case WM_XBUTTONDBLCLK: + if ( flags & MK_XBUTTON2 ) + msg += wxEVT_AUX2_DOWN - wxEVT_AUX1_DOWN; + } +#endif // wxHAS_XBUTTON + wxMouseEvent event(eventsMouse[msg - WM_MOUSEMOVE]); InitMouseEvent(event, x, y, flags); @@ -4645,14 +5265,41 @@ bool wxWindowMSW::HandleMouseMove(int x, int y, WXUINT flags) m_mouseInWindow = true; #ifdef HAVE_TRACKMOUSEEVENT - WinStruct trackinfo; + typedef BOOL (WINAPI *_TrackMouseEvent_t)(LPTRACKMOUSEEVENT); +#ifdef __WXWINCE__ + static const _TrackMouseEvent_t + s_pfn_TrackMouseEvent = _TrackMouseEvent; +#else // !__WXWINCE__ + static _TrackMouseEvent_t s_pfn_TrackMouseEvent; + static bool s_initDone = false; + if ( !s_initDone ) + { + wxLogNull noLog; + + wxDynamicLibrary dllComCtl32(_T("comctl32.dll"), wxDL_VERBATIM); + if ( dllComCtl32.IsLoaded() ) + { + s_pfn_TrackMouseEvent = (_TrackMouseEvent_t) + dllComCtl32.GetSymbol(_T("_TrackMouseEvent")); + } + + s_initDone = true; - trackinfo.dwFlags = TME_LEAVE; - trackinfo.hwndTrack = GetHwnd(); + // notice that it's ok to unload comctl32.dll here as it won't + // be really unloaded, being still in use because we link to it + // statically too + } + + if ( s_pfn_TrackMouseEvent ) +#endif // __WXWINCE__/!__WXWINCE__ + { + WinStruct trackinfo; - // Use the commctrl.h _TrackMouseEvent(), which will call the real - // TrackMouseEvent() if available or emulate it - _TrackMouseEvent(&trackinfo); + trackinfo.dwFlags = TME_LEAVE; + trackinfo.hwndTrack = GetHwnd(); + + (*s_pfn_TrackMouseEvent)(&trackinfo); + } #endif // HAVE_TRACKMOUSEEVENT wxMouseEvent event(wxEVT_ENTER_WINDOW); @@ -4662,7 +5309,7 @@ bool wxWindowMSW::HandleMouseMove(int x, int y, WXUINT flags) } } #ifdef HAVE_TRACKMOUSEEVENT - else + else // mouse not in window { // Check if we need to send a LEAVE event // Windows doesn't send WM_MOUSELEAVE if the mouse has been captured so @@ -4830,35 +5477,14 @@ bool wxWindowMSW::HandleChar(WXWPARAM wParam, WXLPARAM lParam, bool isASCII) int id; if ( isASCII ) { - // If 1 -> 26, translate to either special keycode or just set - // ctrlDown. IOW, Ctrl-C should result in keycode == 3 and - // ControlDown() == true. id = wParam; - if ( (id > 0) && (id < 27) ) - { - switch (id) - { - case 13: - id = WXK_RETURN; - break; - - case 8: - id = WXK_BACK; - break; - - case 9: - id = WXK_TAB; - break; - - default: - //ctrlDown = true; - break; - } - } } else // we're called from WM_KEYDOWN { - id = wxCharCodeMSWToWX(wParam, lParam); + // 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 ) { // it's ASCII and will be processed here only when called from @@ -4895,16 +5521,8 @@ bool wxWindowMSW::HandleKeyDown(WXWPARAM wParam, WXLPARAM lParam) id = wParam; } - if ( id != -1 ) // VZ: does this ever happen (FIXME)? - { - wxKeyEvent event(CreateKeyEvent(wxEVT_KEY_DOWN, id, lParam, wParam)); - if ( GetEventHandler()->ProcessEvent(event) ) - { - return true; - } - } - - return false; + wxKeyEvent event(CreateKeyEvent(wxEVT_KEY_DOWN, id, lParam, wParam)); + return GetEventHandler()->ProcessEvent(event); } bool wxWindowMSW::HandleKeyUp(WXWPARAM wParam, WXLPARAM lParam) @@ -4917,16 +5535,11 @@ bool wxWindowMSW::HandleKeyUp(WXWPARAM wParam, WXLPARAM lParam) id = wParam; } - if ( id != -1 ) // VZ: does this ever happen (FIXME)? - { - wxKeyEvent event(CreateKeyEvent(wxEVT_KEY_UP, id, lParam, wParam)); - if ( GetEventHandler()->ProcessEvent(event) ) - return true; - } - - return false; + wxKeyEvent event(CreateKeyEvent(wxEVT_KEY_UP, id, lParam, wParam)); + return GetEventHandler()->ProcessEvent(event); } +#if wxUSE_MENUS int wxWindowMSW::HandleMenuChar(int WXUNUSED_IN_WINCE(chAccel), WXLPARAM WXUNUSED_IN_WINCE(lParam)) { @@ -4961,7 +5574,7 @@ int wxWindowMSW::HandleMenuChar(int WXUNUSED_IN_WINCE(chAccel), // menu creation code wxMenuItem *item = (wxMenuItem*)mii.dwItemData; - const wxChar *p = wxStrchr(item->GetText(), _T('&')); + const wxChar *p = wxStrchr(item->GetItemLabel().wx_str(), _T('&')); while ( p++ ) { if ( *p == _T('&') ) @@ -4997,6 +5610,19 @@ int wxWindowMSW::HandleMenuChar(int WXUNUSED_IN_WINCE(chAccel), return wxNOT_FOUND; } +bool wxWindowMSW::HandleClipboardEvent( WXUINT nMsg ) +{ + const wxEventType type = ( nMsg == WM_CUT ) ? wxEVT_COMMAND_TEXT_CUT : + ( nMsg == WM_COPY ) ? wxEVT_COMMAND_TEXT_COPY : + /*( nMsg == WM_PASTE ) ? */ wxEVT_COMMAND_TEXT_PASTE; + wxClipboardTextEvent evt(type, GetId()); + + evt.SetEventObject(this); + + return GetEventHandler()->ProcessEvent(evt); +} +#endif // wxUSE_MENUS + // --------------------------------------------------------------------------- // joystick // --------------------------------------------------------------------------- @@ -5169,6 +5795,30 @@ bool wxWindowMSW::MSWOnScroll(int orientation, WXWORD wParam, return GetEventHandler()->ProcessEvent(event); } +// ---------------------------------------------------------------------------- +// custom message handlers +// ---------------------------------------------------------------------------- + +/* static */ bool +wxWindowMSW::MSWRegisterMessageHandler(int msg, MSWMessageHandler handler) +{ + wxCHECK_MSG( gs_messageHandlers.find(msg) == gs_messageHandlers.end(), + false, _T("registering handler for the same message twice") ); + + gs_messageHandlers[msg] = handler; + return true; +} + +/* static */ void +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?") ); + + gs_messageHandlers.erase(i); +} + // =========================================================================== // global functions // =========================================================================== @@ -5212,244 +5862,290 @@ int ChooseNormalOrExtended(int lParam, int keyNormal, int keyExtended) return !lParam || (lParam & (1 << 24)) ? 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 +// +// 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 +{ + int vk; + wxKeyCode wxk; +} gs_specialKeys[] = +{ + { VK_CANCEL, WXK_CANCEL }, + { VK_BACK, WXK_BACK }, + { VK_TAB, WXK_TAB }, + { VK_CLEAR, WXK_CLEAR }, + { VK_SHIFT, WXK_SHIFT }, + { VK_CONTROL, WXK_CONTROL }, + { VK_MENU , WXK_ALT }, + { VK_PAUSE, WXK_PAUSE }, + { VK_CAPITAL, WXK_CAPITAL }, + { VK_SPACE, WXK_SPACE }, + { VK_ESCAPE, WXK_ESCAPE }, + { VK_SELECT, WXK_SELECT }, + { VK_PRINT, WXK_PRINT }, + { VK_EXECUTE, WXK_EXECUTE }, + { VK_SNAPSHOT, WXK_SNAPSHOT }, + { VK_HELP, WXK_HELP }, + + { VK_NUMPAD0, WXK_NUMPAD0 }, + { VK_NUMPAD1, WXK_NUMPAD1 }, + { VK_NUMPAD2, WXK_NUMPAD2 }, + { VK_NUMPAD3, WXK_NUMPAD3 }, + { VK_NUMPAD4, WXK_NUMPAD4 }, + { VK_NUMPAD5, WXK_NUMPAD5 }, + { VK_NUMPAD6, WXK_NUMPAD6 }, + { VK_NUMPAD7, WXK_NUMPAD7 }, + { VK_NUMPAD8, WXK_NUMPAD8 }, + { VK_NUMPAD9, WXK_NUMPAD9 }, + { VK_MULTIPLY, WXK_NUMPAD_MULTIPLY }, + { VK_ADD, WXK_NUMPAD_ADD }, + { VK_SUBTRACT, WXK_NUMPAD_SUBTRACT }, + { VK_DECIMAL, WXK_NUMPAD_DECIMAL }, + { VK_DIVIDE, WXK_NUMPAD_DIVIDE }, + + { VK_F1, WXK_F1 }, + { VK_F2, WXK_F2 }, + { VK_F3, WXK_F3 }, + { VK_F4, WXK_F4 }, + { VK_F5, WXK_F5 }, + { VK_F6, WXK_F6 }, + { VK_F7, WXK_F7 }, + { VK_F8, WXK_F8 }, + { VK_F9, WXK_F9 }, + { VK_F10, WXK_F10 }, + { VK_F11, WXK_F11 }, + { VK_F12, WXK_F12 }, + { VK_F13, WXK_F13 }, + { VK_F14, WXK_F14 }, + { VK_F15, WXK_F15 }, + { VK_F16, WXK_F16 }, + { VK_F17, WXK_F17 }, + { VK_F18, WXK_F18 }, + { VK_F19, WXK_F19 }, + { VK_F20, WXK_F20 }, + { VK_F21, WXK_F21 }, + { VK_F22, WXK_F22 }, + { VK_F23, WXK_F23 }, + { VK_F24, WXK_F24 }, + + { VK_NUMLOCK, WXK_NUMLOCK }, + { VK_SCROLL, WXK_SCROLL }, + +#ifdef VK_APPS + { VK_LWIN, WXK_WINDOWS_LEFT }, + { VK_RWIN, WXK_WINDOWS_RIGHT }, + { VK_APPS, WXK_WINDOWS_MENU }, +#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 keySym, WXLPARAM lParam) +int wxCharCodeMSWToWX(int vk, WXLPARAM lParam) { - int id; - switch (keySym) - { - case VK_CANCEL: id = WXK_CANCEL; break; - case VK_BACK: id = WXK_BACK; break; - case VK_TAB: id = WXK_TAB; break; - case VK_CLEAR: id = WXK_CLEAR; break; - case VK_SHIFT: id = WXK_SHIFT; break; - case VK_CONTROL: id = WXK_CONTROL; break; - case VK_MENU : id = WXK_ALT; break; - case VK_PAUSE: id = WXK_PAUSE; break; - case VK_CAPITAL: id = WXK_CAPITAL; break; - case VK_SPACE: id = WXK_SPACE; break; - case VK_ESCAPE: id = WXK_ESCAPE; break; - case VK_SELECT: id = WXK_SELECT; break; - case VK_PRINT: id = WXK_PRINT; break; - case VK_EXECUTE: id = WXK_EXECUTE; break; - case VK_HELP : id = WXK_HELP; break; - case VK_NUMPAD0: id = WXK_NUMPAD0; break; - case VK_NUMPAD1: id = WXK_NUMPAD1; break; - case VK_NUMPAD2: id = WXK_NUMPAD2; break; - case VK_NUMPAD3: id = WXK_NUMPAD3; break; - case VK_NUMPAD4: id = WXK_NUMPAD4; break; - case VK_NUMPAD5: id = WXK_NUMPAD5; break; - case VK_NUMPAD6: id = WXK_NUMPAD6; break; - case VK_NUMPAD7: id = WXK_NUMPAD7; break; - case VK_NUMPAD8: id = WXK_NUMPAD8; break; - case VK_NUMPAD9: id = WXK_NUMPAD9; break; - case VK_MULTIPLY: id = WXK_NUMPAD_MULTIPLY; break; - case VK_ADD: id = WXK_NUMPAD_ADD; break; - case VK_SUBTRACT: id = WXK_NUMPAD_SUBTRACT; break; - case VK_DECIMAL: id = WXK_NUMPAD_DECIMAL; break; - case VK_DIVIDE: id = WXK_NUMPAD_DIVIDE; break; - case VK_F1: id = WXK_F1; break; - case VK_F2: id = WXK_F2; break; - case VK_F3: id = WXK_F3; break; - case VK_F4: id = WXK_F4; break; - case VK_F5: id = WXK_F5; break; - case VK_F6: id = WXK_F6; break; - case VK_F7: id = WXK_F7; break; - case VK_F8: id = WXK_F8; break; - case VK_F9: id = WXK_F9; break; - case VK_F10: id = WXK_F10; break; - case VK_F11: id = WXK_F11; break; - case VK_F12: id = WXK_F12; break; - case VK_F13: id = WXK_F13; break; - case VK_F14: id = WXK_F14; break; - case VK_F15: id = WXK_F15; break; - case VK_F16: id = WXK_F16; break; - case VK_F17: id = WXK_F17; break; - case VK_F18: id = WXK_F18; break; - case VK_F19: id = WXK_F19; break; - case VK_F20: id = WXK_F20; break; - case VK_F21: id = WXK_F21; break; - case VK_F22: id = WXK_F22; break; - case VK_F23: id = WXK_F23; break; - case VK_F24: id = WXK_F24; break; - case VK_NUMLOCK: id = WXK_NUMLOCK; break; - case VK_SCROLL: id = WXK_SCROLL; break; + // 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; + } + // 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: id = ';'; break; - case VK_OEM_PLUS: id = '+'; break; - case VK_OEM_COMMA: id = ','; break; - case VK_OEM_MINUS: id = '-'; break; - case VK_OEM_PERIOD: id = '.'; break; - case VK_OEM_2: id = '/'; break; - case VK_OEM_3: id = '~'; break; - case VK_OEM_4: id = '['; break; - case VK_OEM_5: id = '\\'; break; - case VK_OEM_6: id = ']'; break; - case VK_OEM_7: id = '\''; break; - -#ifdef VK_APPS - case VK_LWIN: id = WXK_WINDOWS_LEFT; break; - case VK_RWIN: id = WXK_WINDOWS_RIGHT; break; - case VK_APPS: id = WXK_WINDOWS_MENU; break; -#endif // VK_APPS defined + 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; // handle extended keys case VK_PRIOR: - id = ChooseNormalOrExtended(lParam, WXK_NUMPAD_PRIOR, WXK_PRIOR); + wxk = ChooseNormalOrExtended(lParam, WXK_NUMPAD_PAGEUP, WXK_PAGEUP); break; + case VK_NEXT: - id = ChooseNormalOrExtended(lParam, WXK_NUMPAD_NEXT, WXK_NEXT); + wxk = ChooseNormalOrExtended(lParam, WXK_NUMPAD_PAGEDOWN, WXK_PAGEDOWN); break; + case VK_END: - id = ChooseNormalOrExtended(lParam, WXK_NUMPAD_END, WXK_END); + wxk = ChooseNormalOrExtended(lParam, WXK_NUMPAD_END, WXK_END); break; + case VK_HOME: - id = ChooseNormalOrExtended(lParam, WXK_NUMPAD_HOME, WXK_HOME); + wxk = ChooseNormalOrExtended(lParam, WXK_NUMPAD_HOME, WXK_HOME); break; + case VK_LEFT: - id = ChooseNormalOrExtended(lParam, WXK_NUMPAD_LEFT, WXK_LEFT); + wxk = ChooseNormalOrExtended(lParam, WXK_NUMPAD_LEFT, WXK_LEFT); break; + case VK_UP: - id = ChooseNormalOrExtended(lParam, WXK_NUMPAD_UP, WXK_UP); + wxk = ChooseNormalOrExtended(lParam, WXK_NUMPAD_UP, WXK_UP); break; + case VK_RIGHT: - id = ChooseNormalOrExtended(lParam, WXK_NUMPAD_RIGHT, WXK_RIGHT); + wxk = ChooseNormalOrExtended(lParam, WXK_NUMPAD_RIGHT, WXK_RIGHT); break; + case VK_DOWN: - id = ChooseNormalOrExtended(lParam, WXK_NUMPAD_DOWN, WXK_DOWN); + wxk = ChooseNormalOrExtended(lParam, WXK_NUMPAD_DOWN, WXK_DOWN); break; + case VK_INSERT: - id = ChooseNormalOrExtended(lParam, WXK_NUMPAD_INSERT, WXK_INSERT); + wxk = ChooseNormalOrExtended(lParam, WXK_NUMPAD_INSERT, WXK_INSERT); break; + case VK_DELETE: - id = ChooseNormalOrExtended(lParam, WXK_NUMPAD_DELETE, WXK_DELETE); + wxk = ChooseNormalOrExtended(lParam, WXK_NUMPAD_DELETE, WXK_DELETE); break; + case VK_RETURN: // don't use ChooseNormalOrExtended() here as the keys are reversed // here: numpad enter is the extended one - id = lParam && (lParam & (1 << 24)) ? WXK_NUMPAD_ENTER : WXK_RETURN; + wxk = lParam && (lParam & (1 << 24)) ? WXK_NUMPAD_ENTER : WXK_RETURN; break; default: - id = 0; - } - - return id; -} - -WXWORD wxCharCodeWXToMSW(int id, bool *isVirtual) -{ - *isVirtual = true; - WXWORD keySym; - switch (id) - { - case WXK_CANCEL: keySym = VK_CANCEL; break; - case WXK_CLEAR: keySym = VK_CLEAR; break; - case WXK_SHIFT: keySym = VK_SHIFT; break; - case WXK_CONTROL: keySym = VK_CONTROL; break; - case WXK_ALT: keySym = VK_MENU; break; - case WXK_PAUSE: keySym = VK_PAUSE; break; - case WXK_CAPITAL: keySym = VK_CAPITAL; break; - case WXK_PRIOR: keySym = VK_PRIOR; break; - case WXK_NEXT : keySym = VK_NEXT; break; - case WXK_END: keySym = VK_END; break; - case WXK_HOME : keySym = VK_HOME; break; - case WXK_LEFT : keySym = VK_LEFT; break; - case WXK_UP: keySym = VK_UP; break; - case WXK_RIGHT: keySym = VK_RIGHT; break; - case WXK_DOWN : keySym = VK_DOWN; break; - case WXK_SELECT: keySym = VK_SELECT; break; - case WXK_PRINT: keySym = VK_PRINT; break; - case WXK_EXECUTE: keySym = VK_EXECUTE; break; - case WXK_INSERT: keySym = VK_INSERT; break; - case WXK_DELETE: keySym = VK_DELETE; break; - case WXK_HELP : keySym = VK_HELP; break; - case WXK_NUMPAD0: keySym = VK_NUMPAD0; break; - case WXK_NUMPAD1: keySym = VK_NUMPAD1; break; - case WXK_NUMPAD2: keySym = VK_NUMPAD2; break; - case WXK_NUMPAD3: keySym = VK_NUMPAD3; break; - case WXK_NUMPAD4: keySym = VK_NUMPAD4; break; - case WXK_NUMPAD5: keySym = VK_NUMPAD5; break; - case WXK_NUMPAD6: keySym = VK_NUMPAD6; break; - case WXK_NUMPAD7: keySym = VK_NUMPAD7; break; - case WXK_NUMPAD8: keySym = VK_NUMPAD8; break; - case WXK_NUMPAD9: keySym = VK_NUMPAD9; break; - case WXK_NUMPAD_MULTIPLY: keySym = VK_MULTIPLY; break; - case WXK_NUMPAD_ADD: keySym = VK_ADD; break; - case WXK_NUMPAD_SUBTRACT: keySym = VK_SUBTRACT; break; - case WXK_NUMPAD_DECIMAL: keySym = VK_DECIMAL; break; - case WXK_NUMPAD_DIVIDE: keySym = VK_DIVIDE; break; - case WXK_F1: keySym = VK_F1; break; - case WXK_F2: keySym = VK_F2; break; - case WXK_F3: keySym = VK_F3; break; - case WXK_F4: keySym = VK_F4; break; - case WXK_F5: keySym = VK_F5; break; - case WXK_F6: keySym = VK_F6; break; - case WXK_F7: keySym = VK_F7; break; - case WXK_F8: keySym = VK_F8; break; - case WXK_F9: keySym = VK_F9; break; - case WXK_F10: keySym = VK_F10; break; - case WXK_F11: keySym = VK_F11; break; - case WXK_F12: keySym = VK_F12; break; - case WXK_F13: keySym = VK_F13; break; - case WXK_F14: keySym = VK_F14; break; - case WXK_F15: keySym = VK_F15; break; - case WXK_F16: keySym = VK_F16; break; - case WXK_F17: keySym = VK_F17; break; - case WXK_F18: keySym = VK_F18; break; - case WXK_F19: keySym = VK_F19; break; - case WXK_F20: keySym = VK_F20; break; - case WXK_F21: keySym = VK_F21; break; - case WXK_F22: keySym = VK_F22; break; - case WXK_F23: keySym = VK_F23; break; - case WXK_F24: keySym = VK_F24; break; - case WXK_NUMLOCK: keySym = VK_NUMLOCK; break; - case WXK_SCROLL: keySym = VK_SCROLL; break; - default: - { - *isVirtual = false; - keySym = (WORD)id; + wxk = 0; + } + + return wxk; +} + +WXWORD wxCharCodeWXToMSW(int wxk, bool *isVirtual) +{ + if ( isVirtual ) + *isVirtual = true; + + // check the table first + for ( size_t n = 0; n < WXSIZEOF(gs_specialKeys); n++ ) + { + if ( gs_specialKeys[n].wxk == wxk ) + return gs_specialKeys[n].vk; + } + + // and then check for special keys not included in the table + WXWORD vk; + switch ( wxk ) + { + case WXK_PAGEUP: + case WXK_NUMPAD_PAGEUP: + vk = VK_PRIOR; + break; + + case WXK_PAGEDOWN: + case WXK_NUMPAD_PAGEDOWN: + vk = VK_NEXT; + break; + + case WXK_END: + case WXK_NUMPAD_END: + vk = VK_END; + break; + + case WXK_HOME: + case WXK_NUMPAD_HOME: + vk = VK_HOME; + break; + + case WXK_LEFT: + case WXK_NUMPAD_LEFT: + vk = VK_LEFT; + break; + + case WXK_UP: + case WXK_NUMPAD_UP: + vk = VK_UP; + break; + + case WXK_RIGHT: + case WXK_NUMPAD_RIGHT: + vk = VK_RIGHT; + break; + + case WXK_DOWN: + case WXK_NUMPAD_DOWN: + vk = VK_DOWN; + break; + + case WXK_INSERT: + case WXK_NUMPAD_INSERT: + vk = VK_INSERT; + break; + + case WXK_DELETE: + case WXK_NUMPAD_DELETE: + vk = VK_DELETE; + break; + + default: + if ( isVirtual ) + *isVirtual = false; + vk = (WXWORD)wxk; + break; + } + + return vk; +} + +#ifndef SM_SWAPBUTTON + #define SM_SWAPBUTTON 23 +#endif + +// small helper for wxGetKeyState() and wxGetMouseState() +static inline bool wxIsKeyDown(WXWORD vk) +{ + switch (vk) + { + case VK_LBUTTON: + if (GetSystemMetrics(SM_SWAPBUTTON)) vk = VK_RBUTTON; + break; + case VK_RBUTTON: + if (GetSystemMetrics(SM_SWAPBUTTON)) vk = VK_LBUTTON; break; - } } - return keySym; + // the low order bit indicates whether the key was pressed since the last + // call and the high order one indicates whether it is down right now and + // we only want that one + return (GetAsyncKeyState(vk) & (1<<15)) != 0; } bool wxGetKeyState(wxKeyCode key) { - bool bVirtual; + // although this does work under Windows, it is not supported under other + // platforms so don't allow it, you must use wxGetMouseState() instead + wxASSERT_MSG( key != VK_LBUTTON && + key != VK_RBUTTON && + key != VK_MBUTTON, + wxT("can't use wxGetKeyState() for mouse buttons") ); - wxASSERT_MSG(key != WXK_LBUTTON && key != WXK_RBUTTON && key != - WXK_MBUTTON, wxT("can't use wxGetKeyState() for mouse buttons")); + const WXWORD vk = wxCharCodeWXToMSW(key); -//High order with GetAsyncKeyState only available on WIN32 -#ifdef __WIN32__ - //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) + // 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 ) { -#endif - //low order bit means LED is highlighted, - //high order means key is down - //Here, for compat with other ports we want both - return GetKeyState( wxCharCodeWXToMSW(key, &bVirtual) ) != 0; + // low order bit means LED is highlighted and high order one means the + // key is down; for compatibility with the other ports return true if + // either one is set + return GetKeyState(vk) != 0; -#ifdef __WIN32__ } - else + else // normal key { - //normal key - //low order bit means key pressed since last call - //high order means key is down - //We want only the high order bit - the key may not be down if only low order - return ( GetAsyncKeyState( wxCharCodeWXToMSW(key, &bVirtual) ) & (1<<15) ) != 0; + return wxIsKeyDown(vk); } -#endif } @@ -5461,13 +6157,17 @@ wxMouseState wxGetMouseState() ms.SetX(pt.x); ms.SetY(pt.y); - ms.SetLeftDown( (GetAsyncKeyState(VK_LBUTTON) & (1<<15)) != 0 ); - ms.SetMiddleDown( (GetAsyncKeyState(VK_MBUTTON) & (1<<15)) != 0 ); - ms.SetRightDown( (GetAsyncKeyState(VK_RBUTTON) & (1<<15)) != 0 ); - - ms.SetControlDown( (GetAsyncKeyState(VK_CONTROL) & (1<<15)) != 0 ); - ms.SetShiftDown( (GetAsyncKeyState(VK_SHIFT) & (1<<15)) != 0 ); - ms.SetAltDown( (GetAsyncKeyState(VK_MENU) & (1<<15)) != 0 ); + ms.SetLeftDown(wxIsKeyDown(VK_LBUTTON)); + ms.SetMiddleDown(wxIsKeyDown(VK_MBUTTON)); + ms.SetRightDown(wxIsKeyDown(VK_RBUTTON)); +#ifdef wxHAS_XBUTTON + ms.SetAux1Down(wxIsKeyDown(VK_XBUTTON1)); + ms.SetAux2Down(wxIsKeyDown(VK_XBUTTON2)); +#endif // wxHAS_XBUTTON + + ms.SetControlDown(wxIsCtrlDown ()); + ms.SetShiftDown (wxIsShiftDown()); + ms.SetAltDown (wxIsAltDown ()); // ms.SetMetaDown(); return ms; @@ -5708,6 +6408,46 @@ const wxChar *wxGetMessageName(int message) case 0x00A7: return wxT("WM_NCMBUTTONDOWN"); case 0x00A8: return wxT("WM_NCMBUTTONUP"); case 0x00A9: return wxT("WM_NCMBUTTONDBLCLK"); + + case 0x00B0: return wxT("EM_GETSEL"); + case 0x00B1: return wxT("EM_SETSEL"); + case 0x00B2: return wxT("EM_GETRECT"); + case 0x00B3: return wxT("EM_SETRECT"); + case 0x00B4: return wxT("EM_SETRECTNP"); + case 0x00B5: return wxT("EM_SCROLL"); + case 0x00B6: return wxT("EM_LINESCROLL"); + case 0x00B7: return wxT("EM_SCROLLCARET"); + case 0x00B8: return wxT("EM_GETMODIFY"); + case 0x00B9: return wxT("EM_SETMODIFY"); + case 0x00BA: return wxT("EM_GETLINECOUNT"); + case 0x00BB: return wxT("EM_LINEINDEX"); + case 0x00BC: return wxT("EM_SETHANDLE"); + case 0x00BD: return wxT("EM_GETHANDLE"); + case 0x00BE: return wxT("EM_GETTHUMB"); + case 0x00C1: return wxT("EM_LINELENGTH"); + case 0x00C2: return wxT("EM_REPLACESEL"); + case 0x00C4: return wxT("EM_GETLINE"); + case 0x00C5: return wxT("EM_LIMITTEXT/EM_SETLIMITTEXT"); /* ;win40 Name change */ + case 0x00C6: return wxT("EM_CANUNDO"); + case 0x00C7: return wxT("EM_UNDO"); + case 0x00C8: return wxT("EM_FMTLINES"); + case 0x00C9: return wxT("EM_LINEFROMCHAR"); + case 0x00CB: return wxT("EM_SETTABSTOPS"); + case 0x00CC: return wxT("EM_SETPASSWORDCHAR"); + case 0x00CD: return wxT("EM_EMPTYUNDOBUFFER"); + case 0x00CE: return wxT("EM_GETFIRSTVISIBLELINE"); + case 0x00CF: return wxT("EM_SETREADONLY"); + case 0x00D0: return wxT("EM_SETWORDBREAKPROC"); + case 0x00D1: return wxT("EM_GETWORDBREAKPROC"); + case 0x00D2: return wxT("EM_GETPASSWORDCHAR"); + case 0x00D3: return wxT("EM_SETMARGINS"); + case 0x00D4: return wxT("EM_GETMARGINS"); + case 0x00D5: return wxT("EM_GETLIMITTEXT"); + case 0x00D6: return wxT("EM_POSFROMCHAR"); + case 0x00D7: return wxT("EM_CHARFROMPOS"); + case 0x00D8: return wxT("EM_SETIMESTATUS"); + case 0x00D9: return wxT("EM_GETIMESTATUS"); + case 0x0100: return wxT("WM_KEYDOWN"); case 0x0101: return wxT("WM_KEYUP"); case 0x0102: return wxT("WM_CHAR"); @@ -5733,6 +6473,16 @@ const wxChar *wxGetMessageName(int message) case 0x011F: return wxT("WM_MENUSELECT"); case 0x0120: return wxT("WM_MENUCHAR"); case 0x0121: return wxT("WM_ENTERIDLE"); + + case 0x0132: return wxT("WM_CTLCOLORMSGBOX"); + case 0x0133: return wxT("WM_CTLCOLOREDIT"); + case 0x0134: return wxT("WM_CTLCOLORLISTBOX"); + case 0x0135: return wxT("WM_CTLCOLORBTN"); + case 0x0136: return wxT("WM_CTLCOLORDLG"); + case 0x0137: return wxT("WM_CTLCOLORSCROLLBAR"); + case 0x0138: return wxT("WM_CTLCOLORSTATIC"); + case 0x01E1: return wxT("MN_GETHMENU"); + case 0x0200: return wxT("WM_MOUSEMOVE"); case 0x0201: return wxT("WM_LBUTTONDOWN"); case 0x0202: return wxT("WM_LBUTTONUP"); @@ -5744,6 +6494,9 @@ const wxChar *wxGetMessageName(int message) case 0x0208: return wxT("WM_MBUTTONUP"); case 0x0209: return wxT("WM_MBUTTONDBLCLK"); case 0x020A: return wxT("WM_MOUSEWHEEL"); + case 0x020B: return wxT("WM_XBUTTONDOWN"); + case 0x020C: return wxT("WM_XBUTTONUP"); + case 0x020D: return wxT("WM_XBUTTONDBLCLK"); case 0x0210: return wxT("WM_PARENTNOTIFY"); case 0x0211: return wxT("WM_ENTERMENULOOP"); case 0x0212: return wxT("WM_EXITMENULOOP"); @@ -5777,6 +6530,11 @@ const wxChar *wxGetMessageName(int message) case 0x0290: return wxT("WM_IME_KEYDOWN"); case 0x0291: return wxT("WM_IME_KEYUP"); + case 0x02A0: return wxT("WM_NCMOUSEHOVER"); + case 0x02A1: return wxT("WM_MOUSEHOVER"); + case 0x02A2: return wxT("WM_NCMOUSELEAVE"); + case 0x02A3: return wxT("WM_MOUSELEAVE"); + case 0x0300: return wxT("WM_CUT"); case 0x0301: return wxT("WM_COPY"); case 0x0302: return wxT("WM_PASTE"); @@ -5795,9 +6553,10 @@ const wxChar *wxGetMessageName(int message) case 0x030F: return wxT("WM_QUERYNEWPALETTE"); case 0x0310: return wxT("WM_PALETTEISCHANGING"); case 0x0311: return wxT("WM_PALETTECHANGED"); -#if wxUSE_HOTKEY case 0x0312: return wxT("WM_HOTKEY"); -#endif + + case 0x0317: return wxT("WM_PRINT"); + case 0x0318: return wxT("WM_PRINTCLIENT"); // common controls messages - although they're not strictly speaking // standard, it's nice to decode them nevertheless @@ -6073,18 +6832,10 @@ wxWindow* wxFindWindowAtPoint(const wxPoint& pt) POINT pt2; pt2.x = pt.x; pt2.y = pt.y; - HWND hWndHit = ::WindowFromPoint(pt2); - wxWindow* win = wxFindWinFromHandle((WXHWND) hWndHit) ; - HWND hWnd = hWndHit; + HWND hWnd = ::WindowFromPoint(pt2); - // Try to find a window with a wxWindow associated with it - while (!win && (hWnd != 0)) - { - hWnd = ::GetParent(hWnd); - win = wxFindWinFromHandle((WXHWND) hWnd) ; - } - return win; + return wxGetWindowFromHWND((WXHWND)hWnd); } // Get the current mouse position. @@ -6236,7 +6987,7 @@ public: } return CallNextHookEx(ms_hMsgHookProc, nCode, wParam, lParam); - }; + } private: static HHOOK ms_hMsgHookProc;