X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/4f04a4fd782128f16136aabe9eb6c654b2a103a8..17d98558b35b75e3cad68d96841b4fa5a0c7e6ee:/src/msw/window.cpp diff --git a/src/msw/window.cpp b/src/msw/window.cpp index be4d7d01d2..117141a4ea 100644 --- a/src/msw/window.cpp +++ b/src/msw/window.cpp @@ -173,13 +173,16 @@ extern wxMenu *wxCurrentPopupMenu; #endif +namespace +{ + // true if we had already created the std colour map, used by // wxGetStdColourMap() and wxWindow::OnSysColourChanged() (FIXME-MT) -static bool gs_hasStdCmap = false; +bool gs_hasStdCmap = false; // last mouse event information we need to filter out the duplicates #if wxUSE_MOUSEEVENT_HACK -static struct MouseEventInfoDummy +struct MouseEventInfoDummy { // mouse position (in screen coordinates) wxPoint pos; @@ -194,14 +197,29 @@ WX_DECLARE_HASH_MAP(int, wxWindow::MSWMessageHandler, wxIntegerHash, wxIntegerEqual, MSWMessageHandlers); -static MSWMessageHandlers gs_messageHandlers; +MSWMessageHandlers gs_messageHandlers; // hash containing all our windows, it uses HWND keys and wxWindow* values WX_DECLARE_HASH_MAP(HWND, wxWindow *, wxPointerHash, wxPointerEqual, WindowHandles); -static WindowHandles gs_windowHandles; +WindowHandles gs_windowHandles; + +#ifdef wxHAS_MSW_BACKGROUND_ERASE_HOOK + +// temporary override for WM_ERASEBKGND processing: we don't store this in +// wxWindow itself as we don't need it during most of the time so don't +// increase the size of all window objects unnecessarily +WX_DECLARE_HASH_MAP(wxWindow *, wxWindow *, + wxPointerHash, wxPointerEqual, + EraseBgHooks); + +EraseBgHooks gs_eraseBgHooks; + +#endif // wxHAS_MSW_BACKGROUND_ERASE_HOOK + +} // anonymous namespace // --------------------------------------------------------------------------- // private functions @@ -681,9 +699,21 @@ wxWindow *wxWindowBase::DoFindFocus() void wxWindowMSW::DoEnable( bool enable ) { - HWND hWnd = GetHwnd(); - if ( hWnd ) - ::EnableWindow(hWnd, (BOOL)enable); + MSWEnableHWND(GetHwnd(), enable); +} + +bool wxWindowMSW::MSWEnableHWND(WXHWND hWnd, bool enable) +{ + if ( !hWnd ) + return false; + + // If disabling focused control, we move focus to the next one, as if the + // user pressed Tab. That's because we can't keep focus on a disabled + // control, Tab-navigation would stop working then. + if ( !enable && ::GetFocus() == hWnd ) + Navigate(); + + return ::EnableWindow(hWnd, (BOOL)enable) != 0; } bool wxWindowMSW::Show(bool show) @@ -1058,10 +1088,14 @@ void wxWindowMSW::SetScrollbar(int orient, info.nMax = range - 1; // as both nMax and nMax are inclusive info.nPos = pos; - // enable the scrollbar if it had been disabled before by specifying - // SIF_DISABLENOSCROLL below: as we can't know whether this had been - // done or not just do it always - ::EnableScrollBar(hwnd, WXOrientToSB(orient), ESB_ENABLE_BOTH); + // We normally also reenable scrollbar in case it had been previously + // disabled by specifying SIF_DISABLENOSCROLL below but we should only + // do this if it has valid range, otherwise it would be enabled but not + // do anything. + if ( range >= pageSize ) + { + ::EnableScrollBar(hwnd, WXOrientToSB(orient), ESB_ENABLE_BOTH); + } } //else: leave all the fields to be 0 @@ -1801,6 +1835,18 @@ void wxWindowMSW::DoGetClientSize(int *x, int *y) const if ( m_pendingSize != wxDefaultSize ) { // we need to calculate the client size corresponding to pending size + // + // FIXME: Unfortunately this doesn't work correctly for the maximized + // top level windows, the returned values are too small (e.g. + // under Windows 7 on a 1600*1200 screen with task bar on the + // right the pending size for a maximized window is 1538*1200 + // and WM_NCCALCSIZE returns 1528*1172 even though the correct + // client size of such window is 1538*1182). No idea how to fix + // it though, setting WS_MAXIMIZE in GWL_STYLE before calling + // WM_NCCALCSIZE doesn't help and AdjustWindowRectEx() doesn't + // work in this direction neither. So we just have to live with + // the slightly wrong results and relayout the window when it + // gets finally shown in its maximized state (see #11762). RECT rect; rect.left = m_pendingPosition.x; rect.top = m_pendingPosition.y; @@ -1831,11 +1877,13 @@ void wxWindowMSW::DoGetPosition(int *x, int *y) const wxWindow * const parent = GetParent(); wxPoint pos; +#if wxUSE_DEFERRED_SIZING if ( m_pendingPosition != wxDefaultPosition ) { pos = m_pendingPosition; } else // use current position +#endif // wxUSE_DEFERRED_SIZING { RECT rect = wxGetWindowRect(GetHwnd()); @@ -2237,7 +2285,6 @@ static void wxYieldForCommandsOnly() bool wxWindowMSW::DoPopupMenu(wxMenu *menu, int x, int y) { - menu->SetInvokingWindow(this); menu->UpdateUI(); if ( x == wxDefaultCoord && y == wxDefaultCoord ) @@ -2280,8 +2327,6 @@ bool wxWindowMSW::DoPopupMenu(wxMenu *menu, int x, int y) // for example) and so we do need to process the event immediately wxYieldForCommandsOnly(); - menu->SetInvokingWindow(NULL); - return true; } @@ -2320,7 +2365,7 @@ bool wxWindowMSW::MSWProcessMessage(WXMSG* pMsg) // combinations which are always processed) LONG lDlgCode = ::SendMessage(msg->hwnd, WM_GETDLGCODE, 0, 0); - // surprizingly, DLGC_WANTALLKEYS bit mask doesn't contain the + // surprisingly, DLGC_WANTALLKEYS bit mask doesn't contain the // DLGC_WANTTAB nor DLGC_WANTARROWS bits although, logically, // it, of course, implies them if ( lDlgCode & DLGC_WANTALLKEYS ) @@ -2685,9 +2730,14 @@ LRESULT WXDLLEXPORT APIENTRY _EXPORT wxWndProc(HWND hWnd, UINT message, WPARAM w // trace all messages: useful for the debugging but noticeably slows down // the code so don't do it by default #if wxDEBUG_LEVEL >= 2 + // notice that we cast wParam and lParam to long to avoid mismatch with + // format specifiers in 64 bit builds where they are both int64 quantities + // + // casting like this loses information, of course, but it shouldn't matter + // much for this diagnostic code and it keeps the code simple wxLogTrace("winmsg", wxT("Processing %s(hWnd=%p, wParam=%08lx, lParam=%08lx)"), - wxGetMessageName(message), hWnd, (long)wParam, lParam); + wxGetMessageName(message), hWnd, (long)wParam, (long)lParam); #endif // wxDEBUG_LEVEL >= 2 wxWindowMSW *wnd = wxFindWinFromHandle(hWnd); @@ -3269,7 +3319,17 @@ WXLRESULT wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM l break; case WM_ERASEBKGND: - processed = HandleEraseBkgnd((WXHDC)wParam); + { +#ifdef wxHAS_MSW_BACKGROUND_ERASE_HOOK + // check if an override was configured for this window + EraseBgHooks::const_iterator it = gs_eraseBgHooks.find(this); + if ( it != gs_eraseBgHooks.end() ) + processed = it->second->MSWEraseBgHook((WXHDC)wParam); + else +#endif // wxHAS_MSW_BACKGROUND_ERASE_HOOK + processed = HandleEraseBkgnd((WXHDC)wParam); + } + if ( processed ) { // we processed the message, i.e. erased the background @@ -3612,33 +3672,22 @@ void wxWindowMSW::MSWDestroyWindow() { } -bool wxWindowMSW::MSWGetCreateWindowCoords(const wxPoint& pos, +void wxWindowMSW::MSWGetCreateWindowCoords(const wxPoint& pos, const wxSize& size, int& x, int& y, int& w, int& h) const { - // yes, those are just some arbitrary hardcoded numbers - static const int DEFAULT_Y = 200; - - bool nonDefault = false; + // CW_USEDEFAULT can't be used for child windows so just position them at + // the origin by default + x = pos.x == wxDefaultCoord ? 0 : pos.x; + y = pos.y == wxDefaultCoord ? 0 : pos.y; - if ( pos.x == wxDefaultCoord ) - { - // if x is set to CW_USEDEFAULT, y parameter is ignored anyhow so we - // can just as well set it to CW_USEDEFAULT as well - x = - y = CW_USEDEFAULT; - } - else - { - // OTOH, if x is not set to CW_USEDEFAULT, y shouldn't be set to it - // neither because it is not handled as a special value by Windows then - // and so we have to choose some default value for it - x = pos.x; - y = pos.y == wxDefaultCoord ? DEFAULT_Y : pos.y; + AdjustForParentClientOrigin(x, y); - nonDefault = true; - } + // We don't have any clearly good choice for the size by default neither + // but we must use something non-zero. + w = WidthDefault(size.x); + h = HeightDefault(size.y); /* NB: there used to be some code here which set the initial size of the @@ -3656,49 +3705,6 @@ bool wxWindowMSW::MSWGetCreateWindowCoords(const wxPoint& pos, this and ignore any attempts to change the window size to the size it already has - so no WM_SIZE would be sent. */ - - - // we don't use CW_USEDEFAULT here for several reasons: - // - // 1. it results in huge frames on modern screens (1000*800 is not - // uncommon on my 1280*1024 screen) which is way too big for a half - // empty frame of most of wxWidgets samples for example) - // - // 2. it is buggy for frames with wxFRAME_TOOL_WINDOW style for which - // the default is for whatever reason 8*8 which breaks client <-> - // window size calculations (it would be nice if it didn't, but it - // does and the simplest way to fix it seemed to change the broken - // default size anyhow) - // - // 3. there is just no advantage in doing it: with x and y it is - // possible that [future versions of] Windows position the new top - // level window in some smart way which we can't do, but we can - // guess a reasonably good size for a new window just as well - // ourselves - - // However, on PocketPC devices, we must use the default - // size if possible. -#ifdef _WIN32_WCE - if (size.x == wxDefaultCoord) - w = CW_USEDEFAULT; - else - w = size.x; - if (size.y == wxDefaultCoord) - h = CW_USEDEFAULT; - else - h = size.y; -#else - if ( size.x == wxDefaultCoord || size.y == wxDefaultCoord) - { - nonDefault = true; - } - w = WidthDefault(size.x); - h = HeightDefault(size.y); -#endif - - AdjustForParentClientOrigin(x, y); - - return nonDefault; } WXHWND wxWindowMSW::MSWGetParent() const @@ -4000,8 +4006,7 @@ bool wxWindowMSW::HandleDestroy() { m_dropTarget->Revoke(m_hWnd); - delete m_dropTarget; - m_dropTarget = NULL; + wxDELETE(m_dropTarget); } #endif // wxUSE_DRAG_AND_DROP @@ -4196,6 +4201,8 @@ bool wxWindowMSW::HandleSetCursor(WXHWND WXUNUSED(hWnd), y = pt.y; ScreenToClient(&x, &y); wxSetCursorEvent event(x, y); + event.SetId(GetId()); + event.SetEventObject(this); bool processedEvtSetCursor = HandleWindowEvent(event); if ( processedEvtSetCursor && event.HasCursor() ) @@ -4701,10 +4708,82 @@ extern wxCOLORMAP *wxGetStdColourMap() return s_cmap; } +#if wxUSE_UXTHEME && !defined(TMT_FILLCOLOR) + #define TMT_FILLCOLOR 3802 + #define TMT_TEXTCOLOR 3803 + #define TMT_BORDERCOLOR 3801 +#endif + +wxColour wxWindowMSW::MSWGetThemeColour(const wchar_t *themeName, + int themePart, + int themeState, + MSWThemeColour themeColour, + wxSystemColour fallback) const +{ +#if wxUSE_UXTHEME + const wxUxThemeEngine* theme = wxUxThemeEngine::GetIfActive(); + if ( theme ) + { + int themeProperty = 0; + + // TODO: Convert this into a table? Sure would be faster. + switch ( themeColour ) + { + case ThemeColourBackground: + themeProperty = TMT_FILLCOLOR; + break; + case ThemeColourText: + themeProperty = TMT_TEXTCOLOR; + break; + case ThemeColourBorder: + themeProperty = TMT_BORDERCOLOR; + break; + default: + wxFAIL_MSG(wxT("unsupported theme colour")); + }; + + wxUxThemeHandle hTheme((const wxWindow *)this, themeName); + COLORREF col; + HRESULT hr = theme->GetThemeColor + ( + hTheme, + themePart, + themeState, + themeProperty, + &col + ); + + if ( SUCCEEDED(hr) ) + return wxRGBToColour(col); + + wxLogApiError( + wxString::Format( + "GetThemeColor(%s, %i, %i, %i)", + themeName, themePart, themeState, themeProperty), + hr); + } +#else + wxUnusedVar(themeName); + wxUnusedVar(themePart); + wxUnusedVar(themeState); + wxUnusedVar(themeColour); +#endif + return wxSystemSettings::GetColour(fallback); +} + // --------------------------------------------------------------------------- // painting // --------------------------------------------------------------------------- +// this variable is used to check that a paint event handler which processed +// the event did create a wxPaintDC inside its code and called BeginPaint() to +// validate the invalidated window area as otherwise we'd keep getting an +// endless stream of WM_PAINT messages for this window resulting in a lot of +// difficult to debug problems (e.g. impossibility to repaint other windows, +// lack of timer and idle events and so on) +extern bool wxDidCreatePaintDC; +bool wxDidCreatePaintDC = false; + bool wxWindowMSW::HandlePaint() { HRGN hRegion = ::CreateRectRgn(0, 0, 0, 0); // Dummy call to get a handle @@ -4719,11 +4798,20 @@ bool wxWindowMSW::HandlePaint() m_updateRegion = wxRegion((WXHRGN) hRegion); + wxDidCreatePaintDC = false; + wxPaintEvent event(m_windowId); event.SetEventObject(this); bool processed = HandleWindowEvent(event); + if ( processed && !wxDidCreatePaintDC ) + { + // do call MSWDefWindowProc() to validate the update region to avoid + // the problems mentioned above + processed = false; + } + // note that we must generate NC event after the normal one as otherwise // BeginPaint() will happily overwrite our decorations with the background // colour @@ -4804,58 +4892,78 @@ bool wxWindowMSW::HandleEraseBkgnd(WXHDC hdc) return true; } +#ifdef wxHAS_MSW_BACKGROUND_ERASE_HOOK + +bool wxWindowMSW::MSWHasEraseBgHook() const +{ + return gs_eraseBgHooks.find(this) != gs_eraseBgHooks.end(); +} + +void wxWindowMSW::MSWSetEraseBgHook(wxWindow *child) +{ + if ( child ) + { + if ( !gs_eraseBgHooks.insert( + EraseBgHooks::value_type(this, child)).second ) + { + wxFAIL_MSG( wxT("Setting erase background hook twice?") ); + } + } + else // reset the hook + { + if ( gs_eraseBgHooks.erase(this) != 1 ) + { + wxFAIL_MSG( wxT("Resetting erase background which was not set?") ); + } + } +} + +#endif // wxHAS_MSW_BACKGROUND_ERASE_HOOK + bool wxWindowMSW::DoEraseBackground(WXHDC hDC) { HBRUSH hbr = (HBRUSH)MSWGetBgBrush(hDC); if ( !hbr ) return false; - wxFillRect(GetHwnd(), (HDC)hDC, hbr); + // erase just the client area of the window, this is important for the + // frames to avoid drawing over the toolbar part of the window (you might + // think using WS_CLIPCHILDREN would prevent this from happening, but it + // clearly doesn't) + RECT rc; + wxCopyRectToRECT(GetClientRect(), rc); + ::FillRect((HDC)hDC, &rc, hbr); return true; } WXHBRUSH -wxWindowMSW::MSWGetBgBrushForChild(WXHDC WXUNUSED(hDC), WXHWND hWnd) +wxWindowMSW::MSWGetBgBrushForChild(WXHDC WXUNUSED(hDC), + wxWindowMSW * WXUNUSED(child)) { if ( m_hasBgCol ) { - // our background colour applies to: - // 1. this window itself, always - // 2. all children unless the colour is "not inheritable" - // 3. even if it is not inheritable, our immediate transparent - // children should still inherit it -- but not any transparent - // children because it would look wrong if a child of non - // transparent child would show our bg colour when the child itself - // does not - wxWindow *win = wxFindWinFromHandle(hWnd); - if ( win == this || - m_inheritBgCol || - (win && win->HasTransparentBackground() && - win->GetParent() == this) ) - { - // draw children with the same colour as the parent - wxBrush * - brush = wxTheBrushList->FindOrCreateBrush(GetBackgroundColour()); + wxBrush * + brush = wxTheBrushList->FindOrCreateBrush(GetBackgroundColour()); - return (WXHBRUSH)GetHbrushOf(*brush); - } + return (WXHBRUSH)GetHbrushOf(*brush); } return 0; } -WXHBRUSH wxWindowMSW::MSWGetBgBrush(WXHDC hDC, WXHWND hWndToPaint) +WXHBRUSH wxWindowMSW::MSWGetBgBrush(WXHDC hDC) { - if ( !hWndToPaint ) - hWndToPaint = GetHWND(); - for ( wxWindowMSW *win = this; win; win = win->GetParent() ) { - WXHBRUSH hBrush = win->MSWGetBgBrushForChild(hDC, hWndToPaint); + WXHBRUSH hBrush = win->MSWGetBgBrushForChild(hDC, this); if ( hBrush ) return hBrush; + // don't use the parent background if we're not transparent + if ( !win->HasTransparentBackground() ) + break; + // background is not inherited beyond top level windows if ( win->IsTopLevel() ) break; @@ -5523,20 +5631,10 @@ wxKeyEvent wxWindowMSW::CreateKeyEvent(wxEventType evType, event.SetTimestamp(::GetMessageTime()); #endif - // translate the position to client coords - POINT pt; -#ifdef __WXWINCE__ - GetCursorPosWinCE(&pt); -#else - GetCursorPos(&pt); -#endif - RECT rect; - GetWindowRect(GetHwnd(),&rect); - pt.x -= rect.left; - pt.y -= rect.top; - - event.m_x = pt.x; - event.m_y = pt.y; + // translate the position to client coordinates + const wxPoint mousePos = ScreenToClient(wxGetMousePosition()); + event.m_x = mousePos.x; + event.m_y = mousePos.y; return event; } @@ -5645,7 +5743,8 @@ int wxWindowMSW::HandleMenuChar(int WXUNUSED_IN_WINCE(chAccel), // menu creation code wxMenuItem *item = (wxMenuItem*)mii.dwItemData; - const wxChar *p = wxStrchr(item->GetItemLabel().wx_str(), wxT('&')); + const wxString label(item->GetItemLabel()); + const wxChar *p = wxStrchr(label.wx_str(), wxT('&')); while ( p++ ) { if ( *p == wxT('&') )