X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/1bffa9138f96098a82ec635b3e8612b34f414796..e3dbf5934e997729c6f4f5154b1b7f29d5d22149:/src/msw/window.cpp?ds=sidebyside diff --git a/src/msw/window.cpp b/src/msw/window.cpp index a73e4bad73..77589902e5 100644 --- a/src/msw/window.cpp +++ b/src/msw/window.cpp @@ -37,6 +37,7 @@ #include "wx/menu.h" #include "wx/dc.h" #include "wx/dcclient.h" + #include "wx/dcmemory.h" #include "wx/utils.h" #include "wx/app.h" #include "wx/layout.h" @@ -101,6 +102,10 @@ #endif #endif +// ---------------------------------------------------------------------------- +// standard constants not available with all compilers/headers +// ---------------------------------------------------------------------------- + // This didn't appear in mingw until 2.95.2 #ifndef SIF_TRACKPOS #define SIF_TRACKPOS 16 @@ -116,13 +121,27 @@ #ifndef SPI_GETWHEELSCROLLLINES #define SPI_GETWHEELSCROLLLINES 104 #endif +#endif // wxUSE_MOUSEWHEEL + +#ifndef VK_OEM_1 + #define VK_OEM_1 0xBA + #define VK_OEM_PLUS 0xBB + #define VK_OEM_COMMA 0xBC + #define VK_OEM_MINUS 0xBD + #define VK_OEM_PERIOD 0xBE + #define VK_OEM_2 0xBF + #define VK_OEM_3 0xC0 + #define VK_OEM_4 0xDB + #define VK_OEM_5 0xDC + #define VK_OEM_6 0xDD + #define VK_OEM_7 0xDE #endif // --------------------------------------------------------------------------- // global variables // --------------------------------------------------------------------------- -// the last Windows message we got (MT-UNSAFE) +// the last Windows message we got (FIXME-MT) extern MSG s_currentMsg; #if wxUSE_MENUS_NATIVE @@ -131,6 +150,10 @@ wxMenu *wxCurrentPopupMenu = NULL; extern const wxChar *wxCanvasClassName; +// true if we had already created the std colour map, used by +// wxGetStdColourMap() and wxWindow::OnSysColourChanged() (FIXME-MT) +static bool gs_hasStdCmap = FALSE; + // --------------------------------------------------------------------------- // private functions // --------------------------------------------------------------------------- @@ -292,15 +315,13 @@ void wxWindowMSW::Init() m_oldWndProc = 0; m_useCtl3D = FALSE; m_mouseInWindow = FALSE; + m_lastKeydownProcessed = FALSE; // wxWnd m_hMenu = 0; m_hWnd = 0; - // pass WM_GETDLGCODE to DefWindowProc() - m_lDlgCode = 0; - m_xThumbSize = 0; m_yThumbSize = 0; m_backgroundTransparent = FALSE; @@ -320,8 +341,6 @@ wxWindowMSW::~wxWindowMSW() { m_isBeingDeleted = TRUE; - MSWDetachWindowMenu(); - #ifndef __WXUNIVERSAL__ // VS: make sure there's no wxFrame with last focus set to us: for ( wxWindow *win = GetParent(); win; win = win->GetParent() ) @@ -377,7 +396,7 @@ bool wxWindowMSW::Create(wxWindow *parent, // // the correct solution is to create the controls as siblings of the // static box - wxASSERT_MSG( !wxDynamicCastThis(wxStaticBox), + wxASSERT_MSG( !wxDynamicCast(parent, wxStaticBox), _T("wxStaticBox can't be used as a window parent!") ); #endif // wxUSE_STATBOX @@ -386,53 +405,27 @@ bool wxWindowMSW::Create(wxWindow *parent, parent->AddChild(this); - // all windows are created visible - DWORD msflags = WS_CHILD | WS_VISIBLE; + // note that all windows are created visible by default + WXDWORD exstyle; + DWORD msflags = WS_VISIBLE | MSWGetCreateWindowFlags(&exstyle); #ifdef __WXUNIVERSAL__ - // no 3d effects, we draw them ourselves - WXDWORD exStyle = 0; -#else // !wxUniversal - if ( style & wxCLIP_CHILDREN ) - msflags |= WS_CLIPCHILDREN; - if ( style & wxCLIP_SIBLINGS ) - msflags |= WS_CLIPSIBLINGS; - - bool want3D; - WXDWORD exStyle = Determine3DEffects(WS_EX_CLIENTEDGE, &want3D); - - // Even with extended styles, need to combine with WS_BORDER - // for them to look right. - if ( want3D || - (m_windowStyle & (wxBORDER | - wxSIMPLE_BORDER | - wxRAISED_BORDER | - wxSUNKEN_BORDER | - wxDOUBLE_BORDER)) ) - { - msflags |= WS_BORDER; - } - - // calculate the value to return from WM_GETDLGCODE handler - if ( GetWindowStyleFlag() & wxWANTS_CHARS ) - { - // want everything: i.e. all keys and WM_CHAR message - m_lDlgCode = DLGC_WANTARROWS | DLGC_WANTCHARS | - DLGC_WANTTAB | DLGC_WANTMESSAGE; - } -#endif // wxUniversal/!wxUniversal + // no borders, we draw them ourselves + exstyle = 0; + msflags &= ~WS_BORDER; +#endif // wxUniversal if ( style & wxPOPUP_WINDOW ) { // a popup window floats on top of everything - exStyle |= WS_EX_TOPMOST | WS_EX_TOOLWINDOW; + exstyle |= WS_EX_TOPMOST | WS_EX_TOOLWINDOW; // it is also created hidden as other top level windows msflags &= ~WS_VISIBLE; m_isShown = FALSE; } - return MSWCreate(wxCanvasClassName, NULL, pos, size, msflags, exStyle); + return MSWCreate(wxCanvasClassName, NULL, pos, size, msflags, exstyle); } // --------------------------------------------------------------------------- @@ -450,23 +443,18 @@ void wxWindowMSW::SetFocus() if ( !::SetFocus(hWnd) ) { +#if defined(__WXDEBUG__) && !defined(__WXMICROWIN__) // was there really an error? -#ifndef __WXMICROWIN__ DWORD dwRes = ::GetLastError(); -#else - - DWORD dwRes = 0; -#endif if ( dwRes ) { - wxLogApiError(_T("SetFocus"), dwRes); + HWND hwndFocus = ::GetFocus(); + if ( hwndFocus != hWnd ) + { + wxLogApiError(_T("SetFocus"), dwRes); + } } - - // VZ: just why does this happen sometimes?? any idea? -#if 0 - HWND hwndFocus = ::GetFocus(); - wxASSERT_MSG( hwndFocus == hWnd, _T("SetFocus() didn't work?") ); -#endif // 0 +#endif // Debug } } @@ -548,7 +536,7 @@ wxString wxWindowMSW::GetTitle() const return wxGetWindowText(GetHWND()); } -void wxWindowMSW::CaptureMouse() +void wxWindowMSW::DoCaptureMouse() { HWND hWnd = GetHwnd(); if ( hWnd ) @@ -557,7 +545,7 @@ void wxWindowMSW::CaptureMouse() } } -void wxWindowMSW::ReleaseMouse() +void wxWindowMSW::DoReleaseMouse() { if ( !::ReleaseCapture() ) { @@ -825,36 +813,23 @@ int wxWindowMSW::GetScrollThumb(int orient) const void wxWindowMSW::SetScrollPos(int orient, int pos, bool refresh) { -#if defined(__WIN95__) - SCROLLINFO info; - int dir; + HWND hWnd = GetHwnd(); + wxCHECK_RET( hWnd, _T("SetScrollPos: no HWND") ); - if ( orient == wxHORIZONTAL ) { - dir = SB_HORZ; - } else { - dir = SB_VERT; - } + int dir = orient == wxHORIZONTAL ? SB_HORZ : SB_VERT; +#if defined(__WIN95__) + SCROLLINFO info; info.cbSize = sizeof(SCROLLINFO); info.nPage = 0; info.nMin = 0; info.nPos = pos; info.fMask = SIF_POS; - HWND hWnd = GetHwnd(); - if ( hWnd ) - ::SetScrollInfo(hWnd, dir, &info, refresh); -#else - int wOrient; - if ( orient == wxHORIZONTAL ) - wOrient = SB_HORZ; - else - wOrient = SB_VERT; - - HWND hWnd = GetHwnd(); - if ( hWnd ) - ::SetScrollPos(hWnd, wOrient, pos, refresh); -#endif + ::SetScrollInfo(hWnd, dir, &info, refresh); +#else // !__WIN95__ + ::SetScrollPos(hWnd, dir, pos, refresh); +#endif // __WIN95__/!__WIN95__ } // New function that will replace some of the above. @@ -917,15 +892,21 @@ void wxWindowMSW::SetScrollbar(int orient, int pos, int thumbVisible, void wxWindowMSW::ScrollWindow(int dx, int dy, const wxRect *prect) { RECT rect; + RECT *pr; if ( prect ) { rect.left = prect->x; rect.top = prect->y; rect.right = prect->x + prect->width; rect.bottom = prect->y + prect->height; + pr = ▭ + } + else + { + pr = NULL; } - ::ScrollWindow(GetHwnd(), dx, dy, prect ? &rect : NULL, NULL); + ::ScrollWindow(GetHwnd(), dx, dy, pr, pr); } static bool ScrollVertically(HWND hwnd, int kind, int count) @@ -981,11 +962,11 @@ void wxWindowMSW::SubclassWin(WXHWND hWnd) wxAssociateWinWithHandle(hwnd, this); - m_oldWndProc = (WXFARPROC)::GetWindowLong(hwnd, GWL_WNDPROC); + m_oldWndProc = (WXFARPROC)::GetWindowLong((HWND)hWnd, GWL_WNDPROC); // we don't need to subclass the window of our own class (in the Windows // sense of the word) - if ( (WXFARPROC) m_oldWndProc != (WXFARPROC) wxWndProc ) + if ( !wxCheckWindowWndProc(hWnd, (WXFARPROC)wxWndProc) ) { ::SetWindowLong(hwnd, GWL_WNDPROC, (LONG) wxWndProc); } @@ -1010,8 +991,7 @@ void wxWindowMSW::UnsubclassWin() if ( m_oldWndProc ) { - FARPROC wndProc = (FARPROC)::GetWindowLong(hwnd, GWL_WNDPROC); - if ( wndProc != (FARPROC) m_oldWndProc ) + if ( !wxCheckWindowWndProc((WXHWND)hwnd, m_oldWndProc) ) { ::SetWindowLong(hwnd, GWL_WNDPROC, (LONG) m_oldWndProc); } @@ -1021,6 +1001,152 @@ void wxWindowMSW::UnsubclassWin() } } +bool wxCheckWindowWndProc(WXHWND hWnd, WXFARPROC wndProc) +{ +#if wxUSE_UNICODE_MSLU + // VS: We can't use GetWindowLong(hwnd, GWL_WNDPROC) together with unicows.dll + // because it doesn't return pointer to the real wnd proc but rather a handle + // of a fake proc that does Unicode<->ANSI translation. + // + // The hack bellow works, because WNDCLASS contains original window handler + // rather that the unicows fake one. This may not be on purpose, though; if + // it stops working with future versions of unicows.dll, we can override + // unicows hooks by setting Unicows_{Set,Get}WindowLong and + // Unicows_RegisterClass to our own versions that keep track of + // fake<->real wnd proc mapping. + // + // FIXME: Doesn't handle wnd procs set by SetWindowLong, only these set + // with RegisterClass!! + + if ( wxUsingUnicowsDll() ) + { + static wxChar buffer[512]; + WNDCLASS cls; + + ::GetClassName((HWND)hWnd, buffer, 512); + ::GetClassInfo(wxGetInstance(), buffer, &cls); + return wndProc == (WXFARPROC)cls.lpfnWndProc; + } + else +#endif + { + return wndProc == (WXFARPROC)::GetWindowLong((HWND)hWnd, GWL_WNDPROC); + } +} + +// ---------------------------------------------------------------------------- +// Style handling +// ---------------------------------------------------------------------------- + +void wxWindowMSW::SetWindowStyleFlag(long flags) +{ + long flagsOld = GetWindowStyleFlag(); + if ( flags == flagsOld ) + return; + + // update the internal variable + wxWindowBase::SetWindowStyleFlag(flags); + + // now update the Windows style as well if needed - and if the window had + // been already created + if ( !GetHwnd() ) + return; + + WXDWORD exstyle, exstyleOld; + long style = MSWGetStyle(flags, &exstyle), + styleOld = MSWGetStyle(flagsOld, &exstyleOld); + + if ( style != styleOld ) + { + // some flags (e.g. WS_VISIBLE or WS_DISABLED) should not be changed by + // this function so instead of simply setting the style to the new + // value we clear the bits which were set in styleOld but are set in + // the new one and set the ones which were not set before + long styleReal = ::GetWindowLong(GetHwnd(), GWL_STYLE); + styleReal &= ~styleOld; + styleReal |= style; + + ::SetWindowLong(GetHwnd(), GWL_STYLE, styleReal); + } + + // and the extended style + if ( exstyle != exstyleOld ) + { + long exstyleReal = ::GetWindowLong(GetHwnd(), GWL_EXSTYLE); + exstyleReal &= ~exstyleOld; + exstyleReal |= exstyle; + + ::SetWindowLong(GetHwnd(), GWL_EXSTYLE, exstyleReal); + + // we must call SetWindowPos() to flash the cached extended style and + // also to make the change to wxSTAY_ON_TOP style take effect: just + // setting the style simply doesn't work + if ( !::SetWindowPos(GetHwnd(), + exstyleReal & WS_EX_TOPMOST ? HWND_TOPMOST + : HWND_NOTOPMOST, + 0, 0, 0, 0, + SWP_NOMOVE | SWP_NOSIZE) ) + { + wxLogLastError(_T("SetWindowPos")); + } + } +} + +WXDWORD wxWindowMSW::MSWGetStyle(long flags, WXDWORD *exstyle) const +{ + // translate the style + WXDWORD style = WS_CHILD; + + if ( flags & wxCLIP_CHILDREN ) + style |= WS_CLIPCHILDREN; + + if ( flags & wxCLIP_SIBLINGS ) + style |= WS_CLIPSIBLINGS; + + wxBorder border = (wxBorder)(flags & wxBORDER_MASK); + if ( border != wxBORDER_NONE && border != wxBORDER_DEFAULT ) + style |= WS_BORDER; + + // now deal with ext style if the caller wants it + if ( exstyle ) + { + *exstyle = 0; + + if ( flags & wxTRANSPARENT_WINDOW ) + *exstyle |= WS_EX_TRANSPARENT; + + switch ( flags & wxBORDER_MASK ) + { + default: + wxFAIL_MSG( _T("unknown border style") ); + // fall through + + case wxBORDER_NONE: + case wxBORDER_SIMPLE: + case wxBORDER_DEFAULT: + break; + + case wxBORDER_STATIC: + *exstyle |= WS_EX_STATICEDGE; + break; + + case wxBORDER_RAISED: + *exstyle |= WS_EX_WINDOWEDGE; + break; + + case wxBORDER_SUNKEN: + *exstyle |= WS_EX_CLIENTEDGE; + break; + + case wxBORDER_DOUBLE: + *exstyle |= WS_EX_DLGMODALFRAME; + break; + } + } + + return style; +} + // Make a Windows extended style from the given wxWindows window style WXDWORD wxWindowMSW::MakeExtendedStyle(long style, bool eliminateBorders) { @@ -1167,6 +1293,8 @@ void wxWindowMSW::OnIdle(wxIdleEvent& WXUNUSED(event)) // Check if we need to send a LEAVE event if ( m_mouseInWindow ) { + // note that we should generate the leave event whether the window has + // or doesn't have mouse capture if ( !IsMouseInWindow() ) { // Generate a LEAVE event @@ -1233,7 +1361,9 @@ void wxWindowMSW::Clear() static inline void SendSetRedraw(HWND hwnd, bool on) { +#ifndef __WXMICROWIN__ ::SendMessage(hwnd, WM_SETREDRAW, (WPARAM)on, 0); +#endif } void wxWindowMSW::Freeze() @@ -1429,6 +1559,12 @@ void wxWindowMSW::DoClientToScreen(int *x, int *y) const void wxWindowMSW::DoMoveWindow(int x, int y, int width, int height) { + // TODO: is this consistent with other platforms? + // Still, negative width or height shouldn't be allowed + if (width < 0) + width = 0; + if (height < 0) + height = 0; if ( !::MoveWindow(GetHwnd(), x, y, width, height, TRUE) ) { wxLogLastError(wxT("MoveWindow")); @@ -1504,40 +1640,59 @@ void wxWindowMSW::DoSetSize(int x, int y, int width, int height, int sizeFlags) void wxWindowMSW::DoSetClientSize(int width, int height) { - wxWindow *parent = GetParent(); - HWND hWnd = GetHwnd(); - HWND hParentWnd = (HWND) 0; - if ( parent ) - hParentWnd = (HWND) parent->GetHWND(); + // setting the client size is less obvious than it it could have been + // because in the result of changing the total size the window scrollbar + // may [dis]appear and/or its menubar may [un]wrap and so the client size + // will not be correct as the difference between the total and client size + // changes - so we keep changing it until we get it right + // + // normally this loop shouldn't take more than 3 iterations (usually 1 but + // if scrollbars [dis]appear as the result of the first call, then 2 and it + // may become 3 if the window had 0 size originally and so we didn't + // calculate the scrollbar correction correctly during the first iteration) + // but just to be on the safe side we check for it instead of making it an + // "infinite" loop (i.e. leaving break inside as the only way to get out) + for ( int i = 0; i < 4; i++ ) + { + RECT rectClient; + ::GetClientRect(GetHwnd(), &rectClient); - RECT rect; - ::GetClientRect(hWnd, &rect); + // if the size is already ok, stop here (rectClient.left = top = 0) + if ( (rectClient.right == width || width == -1) && + (rectClient.bottom == height || height == -1) ) + { + break; + } - RECT rect2; - GetWindowRect(hWnd, &rect2); + int widthClient = width, + heightClient = height; - // Find the difference between the entire window (title bar and all) - // and the client area; add this to the new client size to move the - // window - int actual_width = rect2.right - rect2.left - rect.right + width; - int actual_height = rect2.bottom - rect2.top - rect.bottom + height; + // Find the difference between the entire window (title bar and all) + // and the client area; add this to the new client size to move the + // window + RECT rectWin; + ::GetWindowRect(GetHwnd(), &rectWin); - // If there's a parent, must subtract the parent's top left corner - // since MoveWindow moves relative to the parent + widthClient += rectWin.right - rectWin.left - rectClient.right; + heightClient += rectWin.bottom - rectWin.top - rectClient.bottom; - POINT point; - point.x = rect2.left; - point.y = rect2.top; - if ( parent ) - { - ::ScreenToClient(hParentWnd, &point); - } + POINT point; + point.x = rectWin.left; + point.y = rectWin.top; - DoMoveWindow(point.x, point.y, actual_width, actual_height); + // MoveWindow positions the child windows relative to the parent, so + // adjust if necessary + if ( !IsTopLevel() ) + { + wxWindow *parent = GetParent(); + if ( parent ) + { + ::ScreenToClient(GetHwndOf(parent), &point); + } + } - wxSizeEvent event(wxSize(width, height), m_windowId); - event.SetEventObject(this); - GetEventHandler()->ProcessEvent(event); + DoMoveWindow(point.x, point.y, widthClient, heightClient); + } } // For implementation purposes - sometimes decorations make the client area @@ -1881,10 +2036,18 @@ bool wxWindowMSW::MSWProcessMessage(WXMSG* pMsg) } #endif // 1/0 - if ( ::IsDialogMessage(GetHwnd(), msg) ) + // we handle VK_ESCAPE ourselves in wxDialog::OnCharHook() and we + // shouldn't let IsDialogMessage() get it 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 ) { - // IsDialogMessage() did something... - return TRUE; + if ( ::IsDialogMessage(GetHwnd(), msg) ) + { + // IsDialogMessage() did something... + return TRUE; + } } } #endif // __WXUNIVERSAL__ @@ -1907,11 +2070,12 @@ bool wxWindowMSW::MSWTranslateMessage(WXMSG* pMsg) #if wxUSE_ACCEL && !defined(__WXUNIVERSAL__) return m_acceleratorTable.Translate(this, pMsg); #else + (void) pMsg; return FALSE; #endif // wxUSE_ACCEL } -bool wxWindowMSW::MSWShouldPreProcessMessage(WXMSG* pMsg) +bool wxWindowMSW::MSWShouldPreProcessMessage(WXMSG* WXUNUSED(pMsg)) { // preprocess all messages by default return TRUE; @@ -2314,9 +2478,11 @@ long wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam #endif // defined(WM_DRAWITEM) case WM_GETDLGCODE: - if ( m_lDlgCode ) + if ( GetWindowStyleFlag() & wxWANTS_CHARS ) { - rc.result = m_lDlgCode; + // want everything: i.e. all keys and WM_CHAR message + rc.result = DLGC_WANTARROWS | DLGC_WANTCHARS | + DLGC_WANTTAB | DLGC_WANTMESSAGE; processed = TRUE; } //else: get the dlg code from the DefWindowProc() @@ -2324,27 +2490,31 @@ long wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam case WM_SYSKEYDOWN: case WM_KEYDOWN: - // If this has been processed by an event handler, - // return 0 now (we've handled it). - if ( HandleKeyDown((WORD) wParam, lParam) ) + // If this has been processed by an event handler, return 0 now + // (we've handled it). + m_lastKeydownProcessed = HandleKeyDown((WORD) wParam, lParam); + if ( m_lastKeydownProcessed ) { processed = TRUE; - - break; - } - - // we consider these message "not interesting" to OnChar - if ( wParam == VK_SHIFT || wParam == VK_CONTROL ) - { - processed = TRUE; - break; } switch ( wParam ) { - // avoid duplicate messages to OnChar for these ASCII keys: they - // will be translated by TranslateMessage() and received in WM_CHAR + // we consider these message "not interesting" to OnChar, so + // just don't do anything more with them + case VK_SHIFT: + case VK_CONTROL: + case VK_MENU: + case VK_CAPITAL: + case VK_NUMLOCK: + case VK_SCROLL: + processed = TRUE; + break; + + // avoid duplicate messages to OnChar for these ASCII keys: + // they will be translated by TranslateMessage() and received + // in WM_CHAR case VK_ESCAPE: case VK_SPACE: case VK_RETURN: @@ -2352,9 +2522,22 @@ long wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam case VK_TAB: case VK_ADD: case VK_SUBTRACT: - // but set processed to FALSE, not TRUE to still pass them to - // the control's default window proc - otherwise built-in - // keyboard handling won't work + case VK_MULTIPLY: + case VK_DIVIDE: + case VK_OEM_1: + case VK_OEM_2: + case VK_OEM_3: + case VK_OEM_4: + case VK_OEM_5: + case VK_OEM_6: + case VK_OEM_7: + case VK_OEM_PLUS: + case VK_OEM_COMMA: + case VK_OEM_MINUS: + case VK_OEM_PERIOD: + // but set processed to FALSE, not TRUE to still pass them + // to the control's default window proc - otherwise + // built-in keyboard handling won't work processed = FALSE; break; @@ -2373,12 +2556,10 @@ long wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam break; #endif // VK_APPS - case VK_LEFT: - case VK_RIGHT: - case VK_DOWN: - case VK_UP: default: + // do generate a CHAR event processed = HandleChar((WORD)wParam, lParam); + } break; @@ -2403,7 +2584,18 @@ long wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam case WM_SYSCHAR: case WM_CHAR: // Always an ASCII character - processed = HandleChar((WORD)wParam, lParam, TRUE); + if ( m_lastKeydownProcessed ) + { + // The key was handled in the EVT_KEY_DOWN and handling + // a key in an EVT_KEY_DOWN handler is meant, by + // design, to prevent EVT_CHARs from happening + m_lastKeydownProcessed = FALSE; + processed = TRUE; + } + else + { + processed = HandleChar((WORD)wParam, lParam, TRUE); + } break; case WM_HSCROLL: @@ -2450,15 +2642,23 @@ long wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam break; #endif // !__WXMICROWIN__ - // the return value for this message is ignored case WM_SYSCOLORCHANGE: + // the return value for this message is ignored processed = HandleSysColorChange(); break; + case WM_DISPLAYCHANGE: + processed = HandleDisplayChange(); + break; + case WM_PALETTECHANGED: processed = HandlePaletteChanged((WXHWND) (HWND) wParam); break; + case WM_CAPTURECHANGED: + processed = HandleCaptureChanged((WXHWND) (HWND) lParam); + break; + case WM_QUERYNEWPALETTE: processed = HandleQueryNewPalette(); break; @@ -2560,21 +2760,6 @@ long wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam } break; #endif // __WIN32__ - - // unfortunately this doesn't really work as then window which - // doesn't accept focus doesn't get any mouse events neither which - // means it can't get any input at all -#if 0 //def __WXUNIVERSAL__ - case WM_NCHITTEST: - // we shouldn't allow the windows which don't want to get focus to - // get it - if ( !AcceptsFocus() ) - { - rc.result = HTTRANSPARENT; - processed = TRUE; - } - break; -#endif // __WXUNIVERSAL__ } if ( !processed ) @@ -2618,7 +2803,7 @@ void wxAssociateWinWithHandle(HWND hWnd, wxWindowMSW *win) #endif // __WXDEBUG__ if (!oldWin) { - wxWinHandleHash->Put((long)hWnd, win); + wxWinHandleHash->Put((long)hWnd, (wxWindow *)win); } } @@ -2637,38 +2822,6 @@ void wxWindowMSW::MSWDestroyWindow() { } -void wxWindowMSW::MSWDetachWindowMenu() -{ -#ifndef __WXUNIVERSAL__ - if ( m_hMenu ) - { - wxChar buf[1024]; - HMENU hMenu = (HMENU)m_hMenu; - - int N = ::GetMenuItemCount(hMenu); - for ( int i = 0; i < N; i++ ) - { - if ( !::GetMenuString(hMenu, i, buf, WXSIZEOF(buf), MF_BYPOSITION) ) - { - wxLogLastError(wxT("GetMenuString")); - - continue; - } - - if ( wxStrcmp(buf, _("&Window")) == 0 ) - { - if ( !::RemoveMenu(hMenu, i, MF_BYPOSITION) ) - { - wxLogLastError(wxT("RemoveMenu")); - } - - break; - } - } - } -#endif // __WXUNIVERSAL__ -} - bool wxWindowMSW::MSWGetCreateWindowCoords(const wxPoint& pos, const wxSize& size, int& x, int& y, @@ -2817,7 +2970,7 @@ bool wxWindowMSW::MSWCreate(const wxChar *wclass, SubclassWin(m_hWnd); - SetFont(wxSystemSettings::GetSystemFont(wxSYS_DEFAULT_GUI_FONT)); + SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT)); return TRUE; } @@ -3063,11 +3216,9 @@ bool wxWindowMSW::HandleDropFiles(WXWPARAM wParam) { #ifndef __WXMICROWIN__ HDROP hFilesInfo = (HDROP) wParam; - POINT dropPoint; - DragQueryPoint(hFilesInfo, (LPPOINT) &dropPoint); // Get the total number of files dropped - WORD gwFilesDropped = (WORD)::DragQueryFile + UINT gwFilesDropped = ::DragQueryFile ( (HDROP)hFilesInfo, (UINT)-1, @@ -3076,24 +3227,28 @@ bool wxWindowMSW::HandleDropFiles(WXWPARAM wParam) ); wxString *files = new wxString[gwFilesDropped]; - int wIndex; - for (wIndex=0; wIndex < (int)gwFilesDropped; wIndex++) + for ( UINT wIndex = 0; wIndex < gwFilesDropped; wIndex++ ) { - DragQueryFile (hFilesInfo, wIndex, (LPTSTR) wxBuffer, 1000); - files[wIndex] = wxBuffer; + // first get the needed buffer length (+1 for terminating NUL) + size_t len = ::DragQueryFile(hFilesInfo, wIndex, NULL, 0) + 1; + + // and now get the file name + ::DragQueryFile(hFilesInfo, wIndex, + files[wIndex].GetWriteBuf(len), len); + + files[wIndex].UngetWriteBuf(); } DragFinish (hFilesInfo); wxDropFilesEvent event(wxEVT_DROP_FILES, gwFilesDropped, files); event.m_eventObject = this; + + POINT dropPoint; + DragQueryPoint(hFilesInfo, (LPPOINT) &dropPoint); event.m_pos.x = dropPoint.x; event.m_pos.y = dropPoint.y; - bool rc = GetEventHandler()->ProcessEvent(event); - - delete[] files; - - return rc; + return GetEventHandler()->ProcessEvent(event); #else // __WXMICROWIN__ return FALSE; #endif @@ -3266,6 +3421,18 @@ bool wxWindowMSW::HandleSysColorChange() wxSysColourChangedEvent event; event.SetEventObject(this); + (void)GetEventHandler()->ProcessEvent(event); + + // always let the system carry on the default processing to allow the + // native controls to react to the colours update + return FALSE; +} + +bool wxWindowMSW::HandleDisplayChange() +{ + wxDisplayChangedEvent event; + event.SetEventObject(this); + return GetEventHandler()->ProcessEvent(event); } @@ -3315,6 +3482,40 @@ WXHBRUSH wxWindowMSW::OnCtlColor(WXHDC WXUNUSED(hDC), bool wxWindowMSW::HandlePaletteChanged(WXHWND hWndPalChange) { +#if wxUSE_PALETTE + // same as below except we don't respond to our own messages + if ( hWndPalChange != GetHWND() ) + { + // check to see if we our our parents have a custom palette + wxWindow *win = this; + while ( win && !win->HasCustomPalette() ) + { + win = win->GetParent(); + } + + if ( win && win->HasCustomPalette() ) + { + // realize the palette to see whether redrawing is needed + HDC hdc = ::GetDC((HWND) hWndPalChange); + win->m_palette.SetHPALETTE((WXHPALETTE) + ::SelectPalette(hdc, GetHpaletteOf(win->m_palette), FALSE)); + + int result = ::RealizePalette(hdc); + + // restore the palette (before releasing the DC) + win->m_palette.SetHPALETTE((WXHPALETTE) + ::SelectPalette(hdc, GetHpaletteOf(win->m_palette), FALSE)); + ::RealizePalette(hdc); + ::ReleaseDC((HWND) hWndPalChange, hdc); + + // now check for the need to redraw + if (result > 0) + InvalidateRect((HWND) hWndPalChange, NULL, TRUE); + } + + } +#endif // wxUSE_PALETTE + wxPaletteChangedEvent event(GetId()); event.SetEventObject(this); event.SetChangedWindow(wxFindWinFromHandle(hWndPalChange)); @@ -3322,8 +3523,39 @@ bool wxWindowMSW::HandlePaletteChanged(WXHWND hWndPalChange) return GetEventHandler()->ProcessEvent(event); } +bool wxWindowMSW::HandleCaptureChanged(WXHWND hWndGainedCapture) +{ + wxMouseCaptureChangedEvent event(GetId(), wxFindWinFromHandle(hWndGainedCapture)); + event.SetEventObject(this); + + return GetEventHandler()->ProcessEvent(event); +} + bool wxWindowMSW::HandleQueryNewPalette() { + +#if wxUSE_PALETTE + // check to see if we our our parents have a custom palette + wxWindow *win = this; + while (!win->HasCustomPalette() && win->GetParent()) win = win->GetParent(); + if (win->HasCustomPalette()) { + /* realize the palette to see whether redrawing is needed */ + HDC hdc = GetDC((HWND) GetHWND()); + win->m_palette.SetHPALETTE( (WXHPALETTE) + ::SelectPalette(hdc, (HPALETTE) win->m_palette.GetHPALETTE(), FALSE) ); + + int result = ::RealizePalette(hdc); + /* restore the palette (before releasing the DC) */ + win->m_palette.SetHPALETTE( (WXHPALETTE) + ::SelectPalette(hdc, (HPALETTE) win->m_palette.GetHPALETTE(), TRUE) ); + ::RealizePalette(hdc); + ::ReleaseDC((HWND) GetHWND(), hdc); + /* now check for the need to redraw */ + if (result > 0) + ::InvalidateRect((HWND) GetHWND(), NULL, TRUE); + } +#endif // wxUSE_PALETTE + wxQueryNewPaletteEvent event(GetId()); event.SetEventObject(this); @@ -3331,22 +3563,112 @@ bool wxWindowMSW::HandleQueryNewPalette() } // Responds to colour changes: passes event on to children. -void wxWindowMSW::OnSysColourChanged(wxSysColourChangedEvent& event) +void wxWindowMSW::OnSysColourChanged(wxSysColourChangedEvent& WXUNUSED(event)) { - wxNode *node = GetChildren().First(); + // the top level window also reset the standard colour map as it might have + // changed (there is no need to do it for the non top level windows as we + // only have to do it once) + if ( IsTopLevel() ) + { + // FIXME-MT + gs_hasStdCmap = FALSE; + } + wxWindowList::Node *node = GetChildren().GetFirst(); while ( node ) { - // Only propagate to non-top-level windows - wxWindow *win = (wxWindow *)node->Data(); - if ( win->GetParent() ) + // Only propagate to non-top-level windows because Windows already + // sends this event to all top-level ones + wxWindow *win = node->GetData(); + if ( !win->IsTopLevel() ) { - wxSysColourChangedEvent event2; - event.m_eventObject = win; - win->GetEventHandler()->ProcessEvent(event2); + // we need to send the real WM_SYSCOLORCHANGE and not just trigger + // EVT_SYS_COLOUR_CHANGED call because the latter wouldn't work for + // the standard controls + ::SendMessage(GetHwndOf(win), WM_SYSCOLORCHANGE, 0, 0); } - node = node->Next(); + node = node->GetNext(); + } + + // update the colours we use if they were not set explicitly by the user: + // this must be done or OnCtlColor() would continue to use the old colours + if ( !m_hasFgCol ) + { + m_foregroundColour = wxSystemSettings:: + GetSystemColour(wxSYS_COLOUR_WINDOWTEXT); + } + + if ( !m_hasBgCol ) + { + m_backgroundColour = wxSystemSettings:: + GetSystemColour(wxSYS_COLOUR_BTNFACE); + } +} + +extern wxCOLORMAP *wxGetStdColourMap() +{ + static COLORREF s_stdColours[wxSTD_COL_MAX]; + static wxCOLORMAP s_cmap[wxSTD_COL_MAX]; + + if ( !gs_hasStdCmap ) + { + static bool s_coloursInit = FALSE; + + if ( !s_coloursInit ) + { + // When a bitmap is loaded, the RGB values can change (apparently + // because Windows adjusts them to care for the old programs always + // using 0xc0c0c0 while the transparent colour for the new Windows + // versions is different). But we do this adjustment ourselves so + // we want to avoid Windows' "help" and for this we need to have a + // reference bitmap which can tell us what the RGB values change + // to. + wxBitmap stdColourBitmap(_T("wxBITMAP_STD_COLOURS")); + if ( stdColourBitmap.Ok() ) + { + // the pixels in the bitmap must correspond to wxSTD_COL_XXX! + wxASSERT_MSG( stdColourBitmap.GetWidth() == wxSTD_COL_MAX, + _T("forgot to update wxBITMAP_STD_COLOURS!") ); + + wxMemoryDC memDC; + memDC.SelectObject(stdColourBitmap); + + wxColour colour; + for ( size_t i = 0; i < WXSIZEOF(s_stdColours); i++ ) + { + memDC.GetPixel(i, 0, &colour); + s_stdColours[i] = wxColourToRGB(colour); + } + } + else // wxBITMAP_STD_COLOURS couldn't be loaded + { + s_stdColours[0] = RGB(000,000,000); // black + s_stdColours[1] = RGB(128,128,128); // dark grey + s_stdColours[2] = RGB(192,192,192); // light grey + s_stdColours[3] = RGB(255,255,255); // white + //s_stdColours[4] = RGB(000,000,255); // blue + //s_stdColours[5] = RGB(255,000,255); // magenta + } + + s_coloursInit = TRUE; + } + + gs_hasStdCmap = TRUE; + + // create the colour map +#define INIT_CMAP_ENTRY(col) \ + s_cmap[wxSTD_COL_##col].from = s_stdColours[wxSTD_COL_##col]; \ + s_cmap[wxSTD_COL_##col].to = ::GetSysColor(COLOR_##col) + + INIT_CMAP_ENTRY(BTNTEXT); + INIT_CMAP_ENTRY(BTNSHADOW); + INIT_CMAP_ENTRY(BTNFACE); + INIT_CMAP_ENTRY(BTNHIGHLIGHT); + +#undef INIT_CMAP_ENTRY } + + return s_cmap; } // --------------------------------------------------------------------------- @@ -3474,9 +3796,13 @@ bool wxWindowMSW::HandleMove(int x, int y) return GetEventHandler()->ProcessEvent(event); } -bool wxWindowMSW::HandleSize(int w, int h, WXUINT WXUNUSED(flag)) +bool wxWindowMSW::HandleSize(int WXUNUSED(w), int WXUNUSED(h), + WXUINT WXUNUSED(flag)) { - wxSizeEvent event(wxSize(w, h), m_windowId); + // don't use w and h parameters as they specify the client size while + // according to the docs EVT_SIZE handler is supposed to receive the total + // size + wxSizeEvent event(GetSize(), m_windowId); event.SetEventObject(this); return GetEventHandler()->ProcessEvent(event); @@ -3488,27 +3814,32 @@ bool wxWindowMSW::HandleGetMinMaxInfo(void *mmInfo) bool rc = FALSE; - if ( m_minWidth != -1 ) + int minWidth = GetMinWidth(), + minHeight = GetMinHeight(), + maxWidth = GetMaxWidth(), + maxHeight = GetMaxHeight(); + + if ( minWidth != -1 ) { - info->ptMinTrackSize.x = m_minWidth; + info->ptMinTrackSize.x = minWidth; rc = TRUE; } - if ( m_minHeight != -1 ) + if ( minHeight != -1 ) { - info->ptMinTrackSize.y = m_minHeight; + info->ptMinTrackSize.y = minHeight; rc = TRUE; } - if ( m_maxWidth != -1 ) + if ( maxWidth != -1 ) { - info->ptMaxTrackSize.x = m_maxWidth; + info->ptMaxTrackSize.x = maxWidth; rc = TRUE; } - if ( m_maxHeight != -1 ) + if ( maxHeight != -1 ) { - info->ptMaxTrackSize.y = m_maxHeight; + info->ptMaxTrackSize.y = maxHeight; rc = TRUE; } @@ -3522,7 +3853,7 @@ bool wxWindowMSW::HandleGetMinMaxInfo(void *mmInfo) bool wxWindowMSW::HandleCommand(WXWORD id, WXWORD cmd, WXHWND control) { #if wxUSE_MENUS_NATIVE - if ( wxCurrentPopupMenu ) + if ( !cmd && wxCurrentPopupMenu ) { wxMenu *popupMenu = wxCurrentPopupMenu; wxCurrentPopupMenu = NULL; @@ -3531,18 +3862,20 @@ bool wxWindowMSW::HandleCommand(WXWORD id, WXWORD cmd, WXHWND control) } #endif // wxUSE_MENUS_NATIVE - wxWindow *win = (wxWindow*) NULL; - if ( cmd == 0 || cmd == 1 ) // menu or accel - use id + wxWindow *win = NULL; + + // first try to find it from HWND - this works even with the broken + // programs using the same ids for different controls + if ( control ) { - // must cast to a signed type before comparing with other ids! - win = FindItem((signed short)id); + win = wxFindWinFromHandle(control); } - if (!win && control) + // try the id + if ( !win ) { - // find it from HWND - this works even with the broken programs using - // the same ids for different controls - win = wxFindWinFromHandle(control); + // must cast to a signed type before comparing with other ids! + win = FindItem((signed short)id); } if ( win ) @@ -3741,7 +4074,8 @@ bool wxWindowMSW::HandleMouseWheel(WXWPARAM wParam, WXLPARAM lParam) // HandleChar and HandleKeyDown/Up wxKeyEvent wxWindowMSW::CreateKeyEvent(wxEventType evType, int id, - WXLPARAM lParam) const + WXLPARAM lParam, + WXWPARAM wParam) const { wxKeyEvent event(evType); event.SetId(GetId()); @@ -3751,6 +4085,8 @@ wxKeyEvent wxWindowMSW::CreateKeyEvent(wxEventType evType, event.m_eventObject = (wxWindow *)this; // const_cast event.m_keyCode = id; + event.m_rawCode = (wxUint32) wParam; + event.m_rawFlags = (wxUint32) lParam; event.SetTimestamp(s_currentMsg.time); // translate the position to client coords @@ -3796,30 +4132,28 @@ bool wxWindowMSW::HandleChar(WXWPARAM wParam, WXLPARAM lParam, bool isASCII) default: ctrlDown = TRUE; - id = id + 96; + id = id + 'a' - 1; } } } - else if ( (id = wxCharCodeMSWToWX(wParam)) == 0 ) - { - // it's ASCII and will be processed here only when called from - // WM_CHAR (i.e. when isASCII = TRUE), don't process it now - id = -1; - } - - if ( id != -1 ) + else // we're called from WM_KEYDOWN { - wxKeyEvent event(CreateKeyEvent(wxEVT_CHAR, id, lParam)); - if ( ctrlDown ) + id = wxCharCodeMSWToWX(wParam); + if ( id == 0 ) { - event.m_controlDown = TRUE; + // it's ASCII and will be processed here only when called from + // WM_CHAR (i.e. when isASCII = TRUE), don't process it now + return FALSE; } + } - if ( GetEventHandler()->ProcessEvent(event) ) - return TRUE; + wxKeyEvent event(CreateKeyEvent(wxEVT_CHAR, id, lParam, wParam)); + if ( ctrlDown ) + { + event.m_controlDown = TRUE; } - return FALSE; + return GetEventHandler()->ProcessEvent(event); } bool wxWindowMSW::HandleKeyDown(WXWPARAM wParam, WXLPARAM lParam) @@ -3834,7 +4168,7 @@ bool wxWindowMSW::HandleKeyDown(WXWPARAM wParam, WXLPARAM lParam) if ( id != -1 ) // VZ: does this ever happen (FIXME)? { - wxKeyEvent event(CreateKeyEvent(wxEVT_KEY_DOWN, id, lParam)); + wxKeyEvent event(CreateKeyEvent(wxEVT_KEY_DOWN, id, lParam, wParam)); if ( GetEventHandler()->ProcessEvent(event) ) { return TRUE; @@ -3856,7 +4190,7 @@ bool wxWindowMSW::HandleKeyUp(WXWPARAM wParam, WXLPARAM lParam) if ( id != -1 ) // VZ: does this ever happen (FIXME)? { - wxKeyEvent event(CreateKeyEvent(wxEVT_KEY_UP, id, lParam)); + wxKeyEvent event(CreateKeyEvent(wxEVT_KEY_UP, id, lParam, wParam)); if ( GetEventHandler()->ProcessEvent(event) ) return TRUE; } @@ -3957,7 +4291,7 @@ bool wxWindowMSW::HandleJoystickEvent(WXUINT msg, int x, int y, WXUINT flags) // --------------------------------------------------------------------------- bool wxWindowMSW::MSWOnScroll(int orientation, WXWORD wParam, - WXWORD pos, WXHWND control) + WXWORD pos, WXHWND control) { if ( control ) { @@ -4085,6 +4419,7 @@ int wxCharCodeMSWToWX(int keySym) case VK_CONTROL: id = WXK_CONTROL; break; case VK_MENU : id = WXK_MENU; 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_PRIOR: id = WXK_PRIOR; break; @@ -4142,6 +4477,19 @@ int wxCharCodeMSWToWX(int keySym) case VK_F24: id = WXK_F24; break; case VK_NUMLOCK: id = WXK_NUMLOCK; break; case VK_SCROLL: id = WXK_SCROLL; break; + + 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; + default: id = 0; } @@ -4286,11 +4634,13 @@ extern wxWindow *wxGetWindowFromHWND(WXHWND hWnd) // FIXME: this is clearly not the best way to do it but I think we'll // need to change HWND <-> wxWindow code more heavily than I can // do it now to fix it +#ifndef __WXMICROWIN__ if ( ::GetWindow(hwnd, GW_OWNER) ) { // it's a dialog box, don't go upwards break; } +#endif hwnd = ::GetParent(hwnd); win = wxFindWinFromHandle((WXHWND)hwnd); @@ -4883,6 +5233,7 @@ wxPoint wxGetMousePosition() { POINT pt; GetCursorPos( & pt ); + return wxPoint(pt.x, pt.y); }