X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/e7c652bc7aecc963aeb85022deeb810970d0dbcd..666e33ab46ed911cd94513c43fcc2bb191704000:/src/msw/window.cpp diff --git a/src/msw/window.cpp b/src/msw/window.cpp index a4e435e411..cc71fb2735 100644 --- a/src/msw/window.cpp +++ b/src/msw/window.cpp @@ -28,6 +28,8 @@ #ifndef WX_PRECOMP #include "wx/msw/wrapwin.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" @@ -46,14 +48,18 @@ #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 @@ -72,8 +78,6 @@ #endif #endif -#include "wx/menuitem.h" - #include "wx/msw/private.h" #if wxUSE_TOOLTIPS @@ -88,9 +92,9 @@ #include "wx/spinctrl.h" #endif // wxUSE_SPINCTRL -#include "wx/textctrl.h" #include "wx/notebook.h" #include "wx/listctrl.h" +#include "wx/dynlib.h" #include @@ -103,9 +107,9 @@ #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" @@ -117,7 +121,7 @@ #endif #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() @@ -169,6 +173,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 // --------------------------------------------------------------------------- @@ -481,7 +492,6 @@ void wxWindowMSW::Init() m_mouseInWindow = false; m_lastKeydownProcessed = false; - m_childrenDisabled = NULL; m_frozenness = 0; m_hWnd = 0; @@ -542,8 +552,6 @@ wxWindowMSW::~wxWindowMSW() wxRemoveHandleAssociation(this); } - delete m_childrenDisabled; - } // real construction (Init() must have been called before!) @@ -645,69 +653,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) @@ -800,7 +750,30 @@ bool wxWindowMSW::SetCursor(const wxCursor& cursor) // don't "overwrite" busy cursor if ( m_cursor.Ok() && !wxIsBusy() ) { - ::SetCursor(GetHcursorOf(m_cursor)); + // 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(); + + POINT point; +#ifdef __WXWINCE__ + ::GetCursorPosWinCE(&point); +#else + ::GetCursorPos(&point); +#endif + + RECT rect = wxGetWindowRect(hWnd); + + set = ::PtInRect(&rect, point) != 0; + } + + if ( set ) + { + ::SetCursor(GetHcursorOf(m_cursor)); + } + //else: will be set later when the mouse enters this window } return true; @@ -824,7 +797,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; } @@ -953,10 +926,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 @@ -1013,6 +983,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 // --------------------------------------------------------------------------- @@ -1141,6 +1172,25 @@ 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() ) @@ -1149,9 +1199,21 @@ void wxWindowMSW::SetWindowStyleFlag(long flags) // we may need to call SetWindowPos() when we change some styles bool callSWP = false; - WXDWORD exstyle, exstyleOld; - long style = MSWGetStyle(flags, &exstyle), - styleOld = MSWGetStyle(flagsOld, &exstyleOld); + 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 ) { @@ -1165,14 +1227,8 @@ void wxWindowMSW::SetWindowStyleFlag(long flags) ::SetWindowLong(GetHwnd(), GWL_STYLE, styleReal); - // If any of the style changes changed any of the frame styles: - // MSDN: SetWindowLong: - // Certain window data is cached, so changes you make using - // SetWindowLong will not take effect until you call the - // SetWindowPos function. Specifically, if you change any of - // the frame styles, you must call SetWindowPos with the - // SWP_FRAMECHANGED flag for the cache to be updated properly. - + // 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 | @@ -1408,11 +1464,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 @@ -1451,9 +1503,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 ) @@ -1472,6 +1528,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) { @@ -1537,6 +1603,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 @@ -1548,6 +1615,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()); @@ -1562,21 +1630,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; @@ -1590,7 +1646,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 @@ -1614,6 +1679,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 ) @@ -1972,10 +2044,22 @@ bool wxWindowMSW::DoPopupMenu(wxMenu *menu, int x, int y) ::ClientToScreen(hWnd, &point); wxCurrentPopupMenu = menu; #if defined(__WXWINCE__) - UINT flags = 0; -#else - UINT flags = TPM_RIGHTBUTTON | TPM_RECURSE; -#endif + static const UINT flags = 0; +#else // !__WXWINCE__ + UINT flags = TPM_RIGHTBUTTON; + // 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 @@ -2027,18 +2111,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, @@ -2050,10 +2130,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; @@ -2075,18 +2158,34 @@ 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 + // call IsDialogMessage() which would consume 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 @@ -2096,56 +2195,38 @@ 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() ) + wxTopLevelWindow * + tlw = wxDynamicCast(wxGetTopLevelParent(this), + wxTopLevelWindow); + if ( tlw ) { - // if we do have a default button, do press it - btn->MSWCommand(BN_CLICKED, 0 /* unused */); - - return true; + btn = wxDynamicCast(tlw->GetDefaultItem(), + wxButton); } - else // no default button + } + + if ( btn && btn->IsEnabled() ) + { + btn->MSWCommand(BN_CLICKED, 0 /* unused */); + return true; + } + #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()) - { - // if not a top level window, let parent - // handle it - return false; - } - //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 - } - } + // 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; @@ -2173,90 +2254,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__ @@ -2267,7 +2268,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 @@ -2284,10 +2285,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; } // --------------------------------------------------------------------------- @@ -2377,7 +2464,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); @@ -2536,7 +2623,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(); @@ -2654,7 +2742,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->CanAcceptFocus() ) win->SetFocus(); } } @@ -2728,12 +2816,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 | @@ -2994,50 +3082,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: @@ -3078,6 +3164,25 @@ WXLRESULT wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM l } } break; + +#ifndef __WXWINCE__ + case WM_POWERBROADCAST: + { + bool vetoed; + processed = HandlePower(wParam, lParam, &vetoed); + rc.result = processed && vetoed ? BROADCAST_QUERY_DENY : TRUE; + } + break; +#endif // __WXWINCE__ + + 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 ) @@ -3265,7 +3370,7 @@ bool wxWindowMSW::MSWCreate(const wxChar *wclass, ( extendedStyle, className, - title ? title : m_windowName.c_str(), + title ? title : (const wxChar*)m_windowName.c_str(), style, x, y, w, h, (HWND)MSWGetParent(), @@ -3377,7 +3482,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). @@ -3785,6 +3890,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 // --------------------------------------------------------------------------- @@ -3994,9 +4175,13 @@ 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); } @@ -4196,7 +4381,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); @@ -4736,14 +4921,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")); + } - trackinfo.dwFlags = TME_LEAVE; - trackinfo.hwndTrack = GetHwnd(); + s_initDone = true; - // Use the commctrl.h _TrackMouseEvent(), which will call the real - // TrackMouseEvent() if available or emulate it - _TrackMouseEvent(&trackinfo); + // 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; + + trackinfo.dwFlags = TME_LEAVE; + trackinfo.hwndTrack = GetHwnd(); + + (*s_pfn_TrackMouseEvent)(&trackinfo); + } #endif // HAVE_TRACKMOUSEEVENT wxMouseEvent event(wxEVT_ENTER_WINDOW); @@ -4753,7 +4965,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 @@ -4921,35 +5133,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 @@ -4986,16 +5177,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) @@ -5008,14 +5191,8 @@ 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); } int wxWindowMSW::HandleMenuChar(int WXUNUSED_IN_WINCE(chAccel), @@ -5088,6 +5265,18 @@ 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); +} + // --------------------------------------------------------------------------- // joystick // --------------------------------------------------------------------------- @@ -5260,6 +5449,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 // =========================================================================== @@ -5303,244 +5516,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_PAGEUP, WXK_PAGEUP); + wxk = ChooseNormalOrExtended(lParam, WXK_NUMPAD_PAGEUP, WXK_PAGEUP); break; + case VK_NEXT: - id = ChooseNormalOrExtended(lParam, WXK_NUMPAD_PAGEDOWN, WXK_PAGEDOWN); + 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_PAGEUP: keySym = VK_PRIOR; break; - case WXK_PAGEDOWN: 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 } @@ -5552,13 +5811,13 @@ 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.SetLeftDown(wxIsKeyDown(VK_LBUTTON)); + ms.SetMiddleDown(wxIsKeyDown(VK_MBUTTON)); + ms.SetRightDown(wxIsKeyDown(VK_RBUTTON)); - 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.SetControlDown(wxIsKeyDown(VK_CONTROL)); + ms.SetShiftDown(wxIsKeyDown(VK_SHIFT)); + ms.SetAltDown(wxIsKeyDown(VK_MENU)); // ms.SetMetaDown(); return ms; @@ -5799,6 +6058,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"); @@ -5824,6 +6123,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"); @@ -5868,6 +6177,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"); @@ -5886,9 +6200,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 @@ -6164,18 +6479,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. @@ -6327,7 +6634,7 @@ public: } return CallNextHookEx(ms_hMsgHookProc, nCode, wParam, lParam); - }; + } private: static HHOOK ms_hMsgHookProc;