X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/58331be29ab14a2067a9b20e49330bbccc1194ca..e28cc3ca0aa9bb1c7e9613c447ca94b13a8bc858:/src/msw/window.cpp diff --git a/src/msw/window.cpp b/src/msw/window.cpp index 64e7ce2c52..7535e4efeb 100644 --- a/src/msw/window.cpp +++ b/src/msw/window.cpp @@ -90,6 +90,10 @@ #include "wx/caret.h" #endif // wxUSE_CARET +#if wxUSE_RADIOBOX + #include "wx/radiobox.h" +#endif // wxUSE_RADIOBOX + #if wxUSE_SPINCTRL #include "wx/spinctrl.h" #endif // wxUSE_SPINCTRL @@ -178,6 +182,13 @@ extern wxMenu *wxCurrentPopupMenu; #endif +#if wxUSE_UXTHEME +// This is a hack used by the owner-drawn wxButton implementation to ensure +// that the brush used for erasing its background is correctly aligned with the +// control. +wxWindowMSW *wxWindowBeingErased = NULL; +#endif // wxUSE_UXTHEME + namespace { @@ -224,6 +235,15 @@ EraseBgHooks gs_eraseBgHooks; #endif // wxHAS_MSW_BACKGROUND_ERASE_HOOK +// If this variable is strictly positive, EVT_CHAR_HOOK is not generated for +// Escape key presses as it can't be intercepted because it's needed by some +// currently shown window, e.g. IME entry. +// +// This is currently global as we allow using UI from the main thread only +// anyhow but could be replaced with a thread-specific value in the future if +// needed. +int gs_modalEntryWindowCount = 0; + } // anonymous namespace // --------------------------------------------------------------------------- @@ -324,115 +344,7 @@ bool GetCursorPosWinCE(POINT* pt) // method #ifdef __WXUNIVERSAL__ IMPLEMENT_ABSTRACT_CLASS(wxWindowMSW, wxWindowBase) -#else // __WXMSW__ -#if wxUSE_EXTENDED_RTTI - -// windows that are created from a parent window during its Create method, eg. spin controls in a calendar controls -// must never been streamed out separately otherwise chaos occurs. Right now easiest is to test for negative ids, as -// windows with negative ids never can be recreated anyway - -bool wxWindowStreamingCallback( const wxObject *object, wxWriter * , wxPersister * , wxxVariantArray & ) -{ - const wxWindow * win = dynamic_cast(object) ; - if ( win && win->GetId() < 0 ) - return false ; - return true ; -} - -IMPLEMENT_DYNAMIC_CLASS_XTI_CALLBACK(wxWindow, wxWindowBase,"wx/window.h", wxWindowStreamingCallback) - -// make wxWindowList known before the property is used - -wxCOLLECTION_TYPE_INFO( wxWindow* , wxWindowList ) ; - -template<> void wxCollectionToVariantArray( wxWindowList const &theList, wxxVariantArray &value) -{ - wxListCollectionToVariantArray( theList , value ) ; -} - -WX_DEFINE_FLAGS( wxWindowStyle ) - -wxBEGIN_FLAGS( wxWindowStyle ) - // new style border flags, we put them first to - // use them for streaming out - - wxFLAGS_MEMBER(wxBORDER_SIMPLE) - wxFLAGS_MEMBER(wxBORDER_SUNKEN) - wxFLAGS_MEMBER(wxBORDER_DOUBLE) - wxFLAGS_MEMBER(wxBORDER_RAISED) - wxFLAGS_MEMBER(wxBORDER_STATIC) - wxFLAGS_MEMBER(wxBORDER_NONE) - - // old style border flags - wxFLAGS_MEMBER(wxSIMPLE_BORDER) - wxFLAGS_MEMBER(wxSUNKEN_BORDER) - wxFLAGS_MEMBER(wxDOUBLE_BORDER) - wxFLAGS_MEMBER(wxRAISED_BORDER) - wxFLAGS_MEMBER(wxSTATIC_BORDER) - wxFLAGS_MEMBER(wxBORDER) - - // standard window styles - wxFLAGS_MEMBER(wxTAB_TRAVERSAL) - wxFLAGS_MEMBER(wxCLIP_CHILDREN) - wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW) - wxFLAGS_MEMBER(wxWANTS_CHARS) - wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE) - wxFLAGS_MEMBER(wxALWAYS_SHOW_SB ) - wxFLAGS_MEMBER(wxVSCROLL) - wxFLAGS_MEMBER(wxHSCROLL) - -wxEND_FLAGS( wxWindowStyle ) - -wxBEGIN_PROPERTIES_TABLE(wxWindow) - wxEVENT_PROPERTY( Close , wxEVT_CLOSE_WINDOW , wxCloseEvent) - wxEVENT_PROPERTY( Create , wxEVT_CREATE , wxWindowCreateEvent ) - wxEVENT_PROPERTY( Destroy , wxEVT_DESTROY , wxWindowDestroyEvent ) - // Always constructor Properties first - - wxREADONLY_PROPERTY( Parent,wxWindow*, GetParent, EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) - wxPROPERTY( Id,wxWindowID, SetId, GetId, -1 /*wxID_ANY*/ , 0 /*flags*/ , wxT("Helpstring") , wxT("group") ) - wxPROPERTY( Position,wxPoint, SetPosition , GetPosition, wxDefaultPosition , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // pos - wxPROPERTY( Size,wxSize, SetSize, GetSize, wxDefaultSize , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // size - wxPROPERTY( WindowStyle , long , SetWindowStyleFlag , GetWindowStyleFlag , EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style - - // Then all relations of the object graph - - wxREADONLY_PROPERTY_COLLECTION( Children , wxWindowList , wxWindowBase* , GetWindowChildren , wxPROP_OBJECT_GRAPH /*flags*/ , wxT("Helpstring") , wxT("group")) - - // and finally all other properties - - wxPROPERTY( ExtraStyle , long , SetExtraStyle , GetExtraStyle , EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // extstyle - wxPROPERTY( BackgroundColour , wxColour , SetBackgroundColour , GetBackgroundColour , EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // bg - wxPROPERTY( ForegroundColour , wxColour , SetForegroundColour , GetForegroundColour , EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // fg - wxPROPERTY( Enabled , bool , Enable , IsEnabled , wxxVariant((bool)true) , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) - wxPROPERTY( Shown , bool , Show , IsShown , wxxVariant((bool)true) , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) -#if 0 - // possible property candidates (not in xrc) or not valid in all subclasses - wxPROPERTY( Title,wxString, SetTitle, GetTitle, wxEmptyString ) - wxPROPERTY( Font , wxFont , SetFont , GetWindowFont , ) - wxPROPERTY( Label,wxString, SetLabel, GetLabel, wxEmptyString ) - // MaxHeight, Width , MinHeight , Width - // TODO switch label to control and title to toplevels - - wxPROPERTY( ThemeEnabled , bool , SetThemeEnabled , GetThemeEnabled , ) - //wxPROPERTY( Cursor , wxCursor , SetCursor , GetCursor , ) - // wxPROPERTY( ToolTip , wxString , SetToolTip , GetToolTipText , ) - wxPROPERTY( AutoLayout , bool , SetAutoLayout , GetAutoLayout , ) - - - -#endif -wxEND_PROPERTIES_TABLE() - -wxBEGIN_HANDLERS_TABLE(wxWindow) -wxEND_HANDLERS_TABLE() - -wxCONSTRUCTOR_DUMMY(wxWindow) - -#else - IMPLEMENT_DYNAMIC_CLASS(wxWindow, wxWindowBase) -#endif -#endif // __WXUNIVERSAL__/__WXMSW__ +#endif // __WXUNIVERSAL__ BEGIN_EVENT_TABLE(wxWindowMSW, wxWindowBase) EVT_SYS_COLOUR_CHANGED(wxWindowMSW::OnSysColourChanged) @@ -917,6 +829,7 @@ bool wxWindowMSW::SetFont(const wxFont& font) return true; } + bool wxWindowMSW::SetCursor(const wxCursor& cursor) { if ( !wxWindowBase::SetCursor(cursor) ) @@ -926,7 +839,10 @@ bool wxWindowMSW::SetCursor(const wxCursor& cursor) } // don't "overwrite" busy cursor - if ( m_cursor.Ok() && !wxIsBusy() ) + if ( wxIsBusy() ) + return true; + + if ( m_cursor.IsOk() ) { // normally we should change the cursor only if it's over this window // but we should do it always if we capture the mouse currently @@ -953,6 +869,26 @@ bool wxWindowMSW::SetCursor(const wxCursor& cursor) } //else: will be set later when the mouse enters this window } + else // Invalid cursor: this means reset to the default one. + { + // To revert to the correct cursor we need to find the window currently + // under the cursor and ask it to set its cursor itself as only it + // knows what it is. + POINT pt; + if ( !::GetCursorPos(&pt) ) + { + wxLogLastError(wxT("GetCursorPos")); + return false; + } + + const wxWindow* win = wxFindWindowAtPoint(wxPoint(pt.x, pt.y)); + if ( !win ) + win = this; + + ::SendMessage(GetHwndOf(win), WM_SETCURSOR, + (WPARAM)GetHwndOf(win), + MAKELPARAM(HTCLIENT, WM_MOUSEMOVE)); + } return true; } @@ -1611,8 +1547,7 @@ void wxWindowMSW::OnInternalIdle() } #endif // !HAVE_TRACKMOUSEEVENT - if (wxUpdateUIEvent::CanUpdate(this) && IsShownOnScreen()) - UpdateWindowUI(wxUPDATE_UI_FROMIDLE); + wxWindowBase::OnInternalIdle(); } // Set this window to be the child of 'parent'. @@ -1876,6 +1811,15 @@ void wxWindowMSW::DoGetClientSize(int *x, int *y) const if ( y ) *y = rect.bottom; } + + // The size of the client window can't be negative but ::GetClientRect() + // can return negative size for an extremely small (1x1) window with + // borders so ensure that we correct it here as having negative sizes is + // completely unexpected. + if ( x && *x < 0 ) + *x = 0; + if ( y && *y < 0 ) + *y = 0; } void wxWindowMSW::DoGetPosition(int *x, int *y) const @@ -2229,7 +2173,7 @@ void wxWindowMSW::DoGetTextExtent(const wxString& string, int *externalLeading, const wxFont *fontToUse) const { - wxASSERT_MSG( !fontToUse || fontToUse->Ok(), + wxASSERT_MSG( !fontToUse || fontToUse->IsOk(), wxT("invalid font in GetTextExtent()") ); HFONT hfontToUse; @@ -2293,18 +2237,16 @@ bool wxWindowMSW::DoPopupMenu(wxMenu *menu, int x, int y) { menu->UpdateUI(); + wxPoint pt; if ( x == wxDefaultCoord && y == wxDefaultCoord ) { - wxPoint mouse = ScreenToClient(wxGetMousePosition()); - x = mouse.x; y = mouse.y; + pt = wxGetMousePosition(); + } + else + { + pt = ClientToScreen(wxPoint(x, y)); } - HWND hWnd = GetHwnd(); - HMENU hMenu = GetHmenuOf(menu); - POINT point; - point.x = x; - point.y = y; - ::ClientToScreen(hWnd, &point); #if defined(__WXWINCE__) static const UINT flags = 0; #else // !__WXWINCE__ @@ -2322,7 +2264,7 @@ bool wxWindowMSW::DoPopupMenu(wxMenu *menu, int x, int y) } #endif // __WXWINCE__/!__WXWINCE__ - ::TrackPopupMenu(hMenu, flags, point.x, point.y, 0, hWnd, NULL); + ::TrackPopupMenu(GetHmenuOf(menu), flags, pt.x, pt.y, 0, GetHwnd(), NULL); // we need to do it right now as otherwise the events are never going to be // sent to wxCurrentPopupMenu from HandleCommand() @@ -2344,10 +2286,23 @@ bool wxWindowMSW::DoPopupMenu(wxMenu *menu, int x, int y) WXLRESULT wxWindowMSW::MSWDefWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) { + WXLRESULT rc; if ( m_oldWndProc ) - return ::CallWindowProc(CASTWNDPROC m_oldWndProc, GetHwnd(), (UINT) nMsg, (WPARAM) wParam, (LPARAM) lParam); + rc = ::CallWindowProc(CASTWNDPROC m_oldWndProc, GetHwnd(), (UINT) nMsg, (WPARAM) wParam, (LPARAM) lParam); else - return ::DefWindowProc(GetHwnd(), nMsg, wParam, lParam); + rc = ::DefWindowProc(GetHwnd(), nMsg, wParam, lParam); + + // Special hack used by wxTextEntry auto-completion only: this event is + // sent after the normal keyboard processing so that its handler could use + // the updated contents of the text control, after taking the key that was + // pressed into account. + if ( nMsg == WM_CHAR ) + { + wxKeyEvent event(CreateCharEvent(wxEVT_AFTER_CHAR, wParam, lParam)); + HandleWindowEvent(event); + } + + return rc; } bool wxWindowMSW::MSWProcessMessage(WXMSG* pMsg) @@ -2448,8 +2403,6 @@ bool wxWindowMSW::MSWProcessMessage(WXMSG* pMsg) // emulate the button click btn = wxFindWinFromHandle(msg->hwnd); } - - bProcess = false; } else // not a button itself, do we have default button? { @@ -2509,6 +2462,13 @@ bool wxWindowMSW::MSWProcessMessage(WXMSG* pMsg) return true; } + // This "Return" key press won't be actually used for + // navigation so don't generate wxNavigationKeyEvent + // for it but still pass it to IsDialogMessage() as it + // may handle it in some other way (e.g. by playing the + // default error sound). + bProcess = false; + #endif // wxUSE_BUTTON #ifdef __WXWINCE__ @@ -2768,7 +2728,11 @@ LRESULT WXDLLEXPORT APIENTRY _EXPORT wxWndProc(HWND hWnd, UINT message, WPARAM w return rc; } -WXLRESULT wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam) +bool +wxWindowMSW::MSWHandleMessage(WXLRESULT *result, + WXUINT message, + WXWPARAM wParam, + WXLPARAM lParam) { // did we process the message? bool processed = false; @@ -2832,7 +2796,7 @@ WXLRESULT wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM l } } break; -#if 0 + case WM_ENTERSIZEMOVE: { processed = HandleEnterSizeMove(); @@ -2844,7 +2808,7 @@ WXLRESULT wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM l processed = HandleExitSizeMove(); } break; -#endif + case WM_SIZING: { LPRECT pRect = (LPRECT)lParam; @@ -3276,6 +3240,17 @@ WXLRESULT wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM l } break; + case WM_IME_STARTCOMPOSITION: + // IME popup needs Escape as it should undo the changes in its + // entry window instead of e.g. closing the dialog for which the + // IME is used (and losing all the changes in the IME window). + gs_modalEntryWindowCount++; + break; + + case WM_IME_ENDCOMPOSITION: + gs_modalEntryWindowCount--; + break; + #if wxUSE_HOTKEY case WM_HOTKEY: processed = HandleHotKey((WORD)wParam, lParam); @@ -3637,15 +3612,26 @@ WXLRESULT wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM l } if ( !processed ) + return false; + + *result = rc.result; + + return true; +} + +WXLRESULT wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam) +{ + WXLRESULT result; + if ( !MSWHandleMessage(&result, message, wParam, lParam) ) { #if wxDEBUG_LEVEL >= 2 wxLogTrace("winmsg", wxT("Forwarding %s to DefWindowProc."), wxGetMessageName(message)); #endif // wxDEBUG_LEVEL >= 2 - rc.result = MSWDefWindowProc(message, wParam, lParam); + result = MSWDefWindowProc(message, wParam, lParam); } - return rc.result; + return result; } // ---------------------------------------------------------------------------- @@ -4246,7 +4232,7 @@ bool wxWindowMSW::HandleSetCursor(WXHWND WXUNUSED(hWnd), // m_cursor if the user code caught EVT_SET_CURSOR() and returned // nothing from it - this is a way to say that our cursor shouldn't // be used for this point - if ( !processedEvtSetCursor && m_cursor.Ok() ) + if ( !processedEvtSetCursor && m_cursor.IsOk() ) { hcursor = GetHcursorOf(m_cursor); } @@ -4254,7 +4240,7 @@ bool wxWindowMSW::HandleSetCursor(WXHWND WXUNUSED(hWnd), if ( !hcursor && !GetParent() ) { const wxCursor *cursor = wxGetGlobalCursor(); - if ( cursor && cursor->Ok() ) + if ( cursor && cursor->IsOk() ) { hcursor = GetHcursorOf(*cursor); } @@ -4691,7 +4677,7 @@ extern wxCOLORMAP *wxGetStdColourMap() // to. wxLogNull logNo; // suppress error if we couldn't load the bitmap wxBitmap stdColourBitmap(wxT("wxBITMAP_STD_COLOURS")); - if ( stdColourBitmap.Ok() ) + if ( stdColourBitmap.IsOk() ) { // the pixels in the bitmap must correspond to wxSTD_COL_XXX! wxASSERT_MSG( stdColourBitmap.GetWidth() == wxSTD_COL_MAX, @@ -4969,9 +4955,30 @@ bool wxWindowMSW::DoEraseBackground(WXHDC hDC) } WXHBRUSH -wxWindowMSW::MSWGetBgBrushForChild(WXHDC WXUNUSED(hDC), - wxWindowMSW * WXUNUSED(child)) +wxWindowMSW::MSWGetBgBrushForChild(WXHDC hDC, wxWindowMSW *child) { + // Test for the custom background brush first. + WXHBRUSH hbrush = MSWGetCustomBgBrush(); + if ( hbrush ) + { + // We assume that this is either a stipple or hatched brush and not a + // solid one as otherwise it would have been enough to set the + // background colour and such brushes need to be positioned correctly + // in order to align when different windows are painted, so do it here. + RECT rc; + ::GetWindowRect(GetHwndOf(child), &rc); + + ::MapWindowPoints(NULL, GetHwnd(), (POINT *)&rc, 1); + + if ( !::SetBrushOrgEx((HDC)hDC, -rc.left, -rc.top, NULL) ) + { + wxLogLastError(wxT("SetBrushOrgEx(bg brush)")); + } + + return hbrush; + } + + // Otherwise see if we have a custom background colour. if ( m_hasBgCol ) { wxBrush * @@ -4985,9 +4992,17 @@ wxWindowMSW::MSWGetBgBrushForChild(WXHDC WXUNUSED(hDC), WXHBRUSH wxWindowMSW::MSWGetBgBrush(WXHDC hDC) { + // Use the special wxWindowBeingErased variable if it is set as the child + // being erased. + wxWindowMSW * const child = +#if wxUSE_UXTHEME + wxWindowBeingErased ? wxWindowBeingErased : +#endif + this; + for ( wxWindowMSW *win = this; win; win = win->GetParent() ) { - WXHBRUSH hBrush = win->MSWGetBgBrushForChild(hDC, this); + WXHBRUSH hBrush = win->MSWGetBgBrushForChild(hDC, child); if ( hBrush ) return hBrush; @@ -5724,11 +5739,12 @@ wxWindowMSW::CreateKeyEvent(wxEventType evType, return event; } -// isASCII is true only when we're called from WM_CHAR handler and not from -// WM_KEYDOWN one -bool wxWindowMSW::HandleChar(WXWPARAM wParam, WXLPARAM lParam) +wxKeyEvent +wxWindowMSW::CreateCharEvent(wxEventType evType, + WXWPARAM wParam, + WXLPARAM lParam) const { - wxKeyEvent event(wxEVT_CHAR); + wxKeyEvent event(evType); InitAnyKeyEvent(event, wParam, lParam); #if wxUSE_UNICODE @@ -5772,6 +5788,14 @@ bool wxWindowMSW::HandleChar(WXWPARAM wParam, WXLPARAM lParam) event.m_altDown = false; } + return event; +} + +// isASCII is true only when we're called from WM_CHAR handler and not from +// WM_KEYDOWN one +bool wxWindowMSW::HandleChar(WXWPARAM wParam, WXLPARAM lParam) +{ + wxKeyEvent event(CreateCharEvent(wxEVT_CHAR, wParam, lParam)); return HandleWindowEvent(event); } @@ -6541,13 +6565,13 @@ extern wxWindow *wxGetWindowFromHWND(WXHWND hWnd) win = wxFindWinFromHandle(hwnd); if ( !win ) { -#if wxUSE_RADIOBOX +#if wxUSE_RADIOBOX && !defined(__WXUNIVERSAL__) // native radiobuttons return DLGC_RADIOBUTTON here and for any // wxWindow class which overrides WM_GETDLGCODE processing to // do it as well, win would be already non NULL if ( ::SendMessage(hwnd, WM_GETDLGCODE, 0, 0) & DLGC_RADIOBUTTON ) { - win = (wxWindow *)wxGetWindowUserData(hwnd); + win = wxRadioBox::GetFromRadioButtonHWND(hwnd); } //else: it's a wxRadioButton, not a radiobutton from wxRadioBox #endif // wxUSE_RADIOBOX @@ -6602,29 +6626,42 @@ wxKeyboardHook(int nCode, WORD wParam, DWORD lParam) { wchar_t uc; int id = wxMSWKeyboard::VKToWX(wParam, lParam, &uc); - if ( id != WXK_NONE + + // Don't intercept keyboard entry (notably Escape) if a modal window + // (not managed by wx, e.g. IME one) is currently opened as more often + // than not it needs all the keys for itself. + // + // Also don't catch it if a window currently captures the mouse as + // Escape is normally used to release the mouse capture and if you + // really need to catch all the keys in the window that has mouse + // capture it can be easily done in its own EVT_CHAR handler as it is + // certain to have focus while it has the capture. + if ( !gs_modalEntryWindowCount && !::GetCapture() ) + { + if ( id != WXK_NONE #if wxUSE_UNICODE - || static_cast(uc) != WXK_NONE + || static_cast(uc) != WXK_NONE #endif // wxUSE_UNICODE - ) - { - const wxWindow * const win = wxGetActiveWindow(); + ) + { + const wxWindow * const win = wxGetActiveWindow(); - wxKeyEvent event(wxEVT_CHAR_HOOK); - MSWInitAnyKeyEvent(event, wParam, lParam, win); + wxKeyEvent event(wxEVT_CHAR_HOOK); + MSWInitAnyKeyEvent(event, wParam, lParam, win); - event.m_keyCode = id; + event.m_keyCode = id; #if wxUSE_UNICODE - event.m_uniChar = uc; + event.m_uniChar = uc; #endif // wxUSE_UNICODE - wxEvtHandler * const handler = win ? win->GetEventHandler() - : wxTheApp; + wxEvtHandler * const handler = win ? win->GetEventHandler() + : wxTheApp; - if ( handler && handler->ProcessEvent(event) ) - { - // processed - return 1; + if ( handler && handler->ProcessEvent(event) ) + { + // processed + return 1; + } } } }