X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/0cf5b09998e7d4606387975a56b6a954a98fd7b7..a0be434e78d628764aa143b82b3c57e9603b7357:/src/msw/window.cpp diff --git a/src/msw/window.cpp b/src/msw/window.cpp index 0d91882294..a0398ffae2 100644 --- a/src/msw/window.cpp +++ b/src/msw/window.cpp @@ -102,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 @@ -117,6 +121,20 @@ #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 // --------------------------------------------------------------------------- @@ -160,6 +178,9 @@ static void TranslateKbdEventToMouse(wxWindowMSW *win, // get the text metrics for the current font static TEXTMETRIC wxGetTextMetrics(const wxWindowMSW *win); +// find the window for the mouse event at the specified position +static wxWindowMSW *FindWindowForMouseEvent(wxWindowMSW *win, int *x, int *y); //TW:REQ:Univ + // wrapper around BringWindowToTop() API static inline void wxBringWindowToTop(HWND hwnd) { @@ -291,12 +312,13 @@ void wxWindowMSW::Init() InitBase(); // MSW specific - m_doubleClickAllowed = 0; - m_isBeingDeleted = FALSE; - m_oldWndProc = 0; + m_oldWndProc = NULL; m_useCtl3D = FALSE; m_mouseInWindow = FALSE; + m_lastKeydownProcessed = FALSE; + + m_childrenDisabled = NULL; // wxWnd m_hMenu = 0; @@ -326,12 +348,12 @@ wxWindowMSW::~wxWindowMSW() // VS: make sure there's no wxFrame with last focus set to us: for ( wxWindow *win = GetParent(); win; win = win->GetParent() ) { - wxFrame *frame = wxDynamicCast(win, wxFrame); + wxTopLevelWindow *frame = wxDynamicCast(win, wxTopLevelWindow); if ( frame ) { if ( frame->GetLastFocus() == this ) { - frame->SetLastFocus((wxWindow*)NULL); + frame->SetLastFocus(NULL); } break; } @@ -358,6 +380,8 @@ wxWindowMSW::~wxWindowMSW() // remove hWnd <-> wxWindow association wxRemoveHandleAssociation(this); } + + delete m_childrenDisabled; } // real construction (Init() must have been called before!) @@ -370,61 +394,36 @@ bool wxWindowMSW::Create(wxWindow *parent, { wxCHECK_MSG( parent, FALSE, wxT("can't create wxWindow without parent") ); -#if wxUSE_STATBOX - // wxGTK doesn't allow to create controls with static box as the parent so - // this will result in a crash when the program is ported to wxGTK - warn - // about it - // - // the correct solution is to create the controls as siblings of the - // static box - wxASSERT_MSG( !wxDynamicCast(parent, wxStaticBox), - _T("wxStaticBox can't be used as a window parent!") ); -#endif // wxUSE_STATBOX - if ( !CreateBase(parent, id, pos, size, style, wxDefaultValidator, name) ) return FALSE; parent->AddChild(this); - // all windows are created visible - DWORD msflags = WS_CHILD | WS_VISIBLE; + WXDWORD exstyle; + DWORD msflags = 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; - } -#endif // wxUniversal/!wxUniversal - + // no borders, we draw them ourselves + exstyle &= ~(WS_EX_DLGMODALFRAME | + WS_EX_STATICEDGE | + WS_EX_CLIENTEDGE | + WS_EX_WINDOWEDGE); + msflags &= ~WS_BORDER; +#endif // wxUniversal + + // all windows are created visible by default except popup ones (which are + // like the wxTopLevelWindows in this aspect) if ( style & wxPOPUP_WINDOW ) { - // a popup window floats on top of everything - exStyle |= WS_EX_TOPMOST | WS_EX_TOOLWINDOW; - - // it is also created hidden as other top level windows msflags &= ~WS_VISIBLE; m_isShown = FALSE; } + else + { + msflags |= WS_VISIBLE; + } - return MSWCreate(wxCanvasClassName, NULL, pos, size, msflags, exStyle); + return MSWCreate(wxCanvasClassName, NULL, pos, size, msflags, exstyle); } // --------------------------------------------------------------------------- @@ -457,6 +456,20 @@ void wxWindowMSW::SetFocus() } } +void wxWindowMSW::SetFocusFromKbd() +{ + wxWindowBase::SetFocusFromKbd(); + + // when the focus is given to the control with DLGC_HASSETSEL style from + // keyboard its contents should be entirely selected: this is what + // ::IsDialogMessage() does and so we should do it as well to provide the + // same LNF as the native programs + if ( ::SendMessage(GetHwnd(), WM_GETDLGCODE, 0, 0) & DLGC_HASSETSEL ) + { + ::SendMessage(GetHwnd(), EM_SETSEL, 0, -1); + } +} + // Get the window with the focus wxWindow *wxWindowBase::FindFocus() { @@ -478,19 +491,58 @@ bool wxWindowMSW::Enable(bool enable) if ( hWnd ) ::EnableWindow(hWnd, (BOOL)enable); - // VZ: no, this is a bad idea: imagine that you have a dialog with some - // disabled controls and disable it - you really wouldn't like the - // disabled controls be reenabled too when you reenable the dialog! -#if 0 - wxWindowList::Node *node = GetChildren().GetFirst(); - while ( node ) + // 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::Node *node = GetChildren().GetFirst(); + node; + node = node->GetNext() ) { wxWindow *child = node->GetData(); - child->Enable(enable); + if ( child->IsTopLevel() ) + { + // the logic below doesn't apply to top level children + continue; + } - node = node->GetNext(); + if ( enable ) + { + // enable the child back 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; } -#endif // 0 return TRUE; } @@ -890,14 +942,14 @@ void wxWindowMSW::SetScrollbar(int orient, int pos, int thumbVisible, void wxWindowMSW::ScrollWindow(int dx, int dy, const wxRect *prect) { - RECT rect, *pr; + 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 @@ -962,7 +1014,7 @@ void wxWindowMSW::SubclassWin(WXHWND hWnd) wxAssociateWinWithHandle(hwnd, this); 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 ( !wxCheckWindowWndProc(hWnd, (WXFARPROC)wxWndProc) ) @@ -971,7 +1023,10 @@ void wxWindowMSW::SubclassWin(WXHWND hWnd) } else { - // don't bother restoring it neither + // don't bother restoring it neither: this also makes it easy to + // implement IsOfStandardClass() method which returns TRUE for the + // standard controls and FALSE for the wxWindows own windows as it can + // simply check m_oldWndProc m_oldWndProc = NULL; } } @@ -1002,32 +1057,145 @@ 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!! - - static wxChar buffer[512]; + // Unicows note: the code below works, but only 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. WNDCLASS cls; + if ( !::GetClassInfo(wxGetInstance(), wxGetWindowClass(hWnd), &cls) ) + { + wxLogLastError(_T("GetClassInfo")); + + return FALSE; + } - ::GetClassName((HWND)hWnd, buffer, 512); - ::GetClassInfo(wxGetInstance(), buffer, &cls); return wndProc == (WXFARPROC)cls.lpfnWndProc; -#else - return wndProc == (WXFARPROC)::GetWindowLong((HWND)hWnd, GWL_WNDPROC); -#endif } +// ---------------------------------------------------------------------------- +// 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; + } + + // wxUniv doesn't use Windows dialog navigation functions at all +#ifndef __WXUNIVERSAL__ + // to make the dialog navigation work with the nested panels we must + // use this style (top level windows such as dialogs don't need it) + if ( (flags & wxTAB_TRAVERSAL) && !IsTopLevel() ) + { + *exstyle |= WS_EX_CONTROLPARENT; + } +#endif // __WXUNIVERSAL__ + } + + return style; +} // Make a Windows extended style from the given wxWindows window style WXDWORD wxWindowMSW::MakeExtendedStyle(long style, bool eliminateBorders) @@ -1175,7 +1343,9 @@ void wxWindowMSW::OnIdle(wxIdleEvent& WXUNUSED(event)) // Check if we need to send a LEAVE event if ( m_mouseInWindow ) { - if ( !IsMouseInWindow() && !HasCapture()) + // note that we should generate the leave event whether the window has + // or doesn't have mouse capture + if ( !IsMouseInWindow() ) { // Generate a LEAVE event m_mouseInWindow = FALSE; @@ -1334,7 +1504,7 @@ void wxWindowMSW::DoSetToolTip(wxToolTip *tooltip) wxWindowBase::DoSetToolTip(tooltip); if ( m_tooltip ) - m_tooltip->SetWindow(this); + m_tooltip->SetWindow((wxWindow *)this); } #endif // wxUSE_TOOLTIPS @@ -1544,15 +1714,6 @@ void wxWindowMSW::DoSetClientSize(int width, int height) break; } - if ( i == 3 ) - { - // how did it happen? maybe OnSize() handler does something really - // strange in this class? - wxFAIL_MSG( _T("logic error in DoSetClientSize") ); - - break; - } - int widthClient = width, heightClient = height; @@ -1715,6 +1876,10 @@ static void wxYieldForCommandsOnly() { wxTheApp->DoMessage((WXMSG *)&msg); } + + // If we retrieved a WM_QUIT, insert back into the message queue. + if (msg.message == WM_QUIT) + ::PostQuitMessage(0); } bool wxWindowMSW::DoPopupMenu(wxMenu *menu, int x, int y) @@ -1773,14 +1938,7 @@ bool wxWindowMSW::MSWProcessMessage(WXMSG* pMsg) // here we try to do all the job which ::IsDialogMessage() usually does // internally #if 1 - bool bProcess = TRUE; - if ( msg->message != WM_KEYDOWN ) - bProcess = FALSE; - - if ( bProcess && (HIWORD(msg->lParam) & KF_ALTDOWN) == KF_ALTDOWN ) - bProcess = FALSE; - - if ( bProcess ) + if ( msg->message == WM_KEYDOWN ) { bool bCtrlDown = wxIsCtrlDown(); bool bShiftDown = wxIsShiftDown(); @@ -1792,11 +1950,21 @@ bool wxWindowMSW::MSWProcessMessage(WXMSG* pMsg) if ( !bCtrlDown ) { 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; + } } bool bForward = TRUE, bWindowChange = FALSE; + // should we process this message specially? + bool bProcess = TRUE; switch ( msg->wParam ) { case VK_TAB: @@ -1867,12 +2035,27 @@ bool wxWindowMSW::MSWProcessMessage(WXMSG* pMsg) return TRUE; } else // no default button -#endif // wxUSE_BUTTON { - // no special function for enter and don't even - // let IsDialogMessage() have it: it seems to - // do something really strange with it - return FALSE; +#endif // wxUSE_BUTTON + // 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 } } } @@ -1925,10 +2108,46 @@ 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; + // ::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 + bool canSafelyCallIsDlgMsg = TRUE; + + HWND hwndFocus = ::GetFocus(); + 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); + } + + if ( canSafelyCallIsDlgMsg && ::IsDialogMessage(GetHwnd(), msg) ) + { + // IsDialogMessage() did something... + return TRUE; + } } } #endif // __WXUNIVERSAL__ @@ -1951,11 +2170,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; @@ -2078,8 +2298,10 @@ LRESULT WXDLLEXPORT APIENTRY _EXPORT wxWndProc(HWND hWnd, UINT message, WPARAM w { // trace all messages - useful for the debugging #ifdef __WXDEBUG__ +#if wxUSE_LOG wxLogTrace(wxTraceMessages, wxT("Processing %s(wParam=%8lx, lParam=%8lx)"), - wxGetMessageName(message), wParam, lParam); + wxGetMessageName(message), (long) wParam, lParam); +#endif // wxUSE_LOG #endif // __WXDEBUG__ wxWindowMSW *wnd = wxFindWinFromHandle((WXHWND) hWnd); @@ -2204,10 +2426,18 @@ long wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam break; case WM_CLOSE: +#ifdef __WXUNIVERSAL__ + // Universal uses its own wxFrame/wxDialog, so we don't receive + // close events unless we have this. + Close(); + processed = TRUE; + rc.result = TRUE; +#else // don't let the DefWindowProc() destroy our window - we'll do it // ourselves in ~wxWindow processed = TRUE; rc.result = TRUE; +#endif break; case WM_SHOWWINDOW: @@ -2235,68 +2465,63 @@ long wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam case WM_MBUTTONDOWN: case WM_MBUTTONUP: case WM_MBUTTONDBLCLK: - { - processed = FALSE; + { #ifdef __WXMICROWIN__ // MicroWindows seems to ignore the fact that a window is // disabled. So catch mouse events and throw them away if // necessary. wxWindowMSW* win = this; - while (win) + for ( ;; ) { if (!win->IsEnabled()) { processed = TRUE; break; } + win = win->GetParent(); - if (win && win->IsTopLevel()) + if ( !win || win->IsTopLevel() ) break; } + + if ( processed ) + break; + #endif // __WXMICROWIN__ - if (!processed) + int x = GET_X_LPARAM(lParam), + y = GET_Y_LPARAM(lParam); + + // redirect the event to a static control if necessary by + // finding one under mouse + wxWindowMSW *win; + if ( GetCapture() == this ) { - if (message == WM_LBUTTONDOWN && AcceptsFocus()) - SetFocus(); - processed = HandleMouseEvent(message, - GET_X_LPARAM(lParam), - GET_Y_LPARAM(lParam), - wParam); + // but don't do it if the mouse is captured by this window + // because then it should really get this event itself + win = this; } - break; - } - -#ifdef __WXMICROWIN__ - case WM_NCLBUTTONDOWN: - case WM_NCLBUTTONUP: - case WM_NCLBUTTONDBLCLK: - case WM_NCRBUTTONDOWN: - case WM_NCRBUTTONUP: - case WM_NCRBUTTONDBLCLK: -#if 0 - case WM_NCMBUTTONDOWN: - case WM_NCMBUTTONUP: - case WM_NCMBUTTONDBLCLK: -#endif - { - // MicroWindows seems to ignore the fact that a window - // is disabled. So catch mouse events and throw them away if necessary. - processed = FALSE; - wxWindowMSW* win = this; - while (win) + else { - if (!win->IsEnabled()) + win = FindWindowForMouseEvent(this, &x, &y); + + // this should never happen + wxCHECK_MSG( win, 0, + _T("FindWindowForMouseEvent() returned NULL") ); + + // for the standard classes their WndProc sets the focus to + // them anyhow and doing it from here results in some weird + // problems, but for our windows we want them to acquire + // focus when clicked + if ( !win->IsOfStandardClass() ) { - processed = TRUE; - break; + if ( message == WM_LBUTTONDOWN && win->AcceptsFocus() ) + win->SetFocus(); } - win = win->GetParent(); - if (win && win->IsTopLevel()) - break; } - break; + + processed = win->HandleMouseEvent(message, x, y, wParam); } -#endif // __WXMICROWIN__ + break; #ifdef MM_JOY1MOVE case MM_JOY1MOVE: @@ -2358,11 +2583,19 @@ long wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam #endif // defined(WM_DRAWITEM) case WM_GETDLGCODE: - if ( GetWindowStyleFlag() & wxWANTS_CHARS ) + if ( !IsOfStandardClass() ) { - // want everything: i.e. all keys and WM_CHAR message - rc.result = DLGC_WANTARROWS | DLGC_WANTCHARS | - DLGC_WANTTAB | DLGC_WANTMESSAGE; + // we always want to get the char events + rc.result = DLGC_WANTCHARS; + + if ( GetWindowStyleFlag() & wxWANTS_CHARS ) + { + // in fact, we want everything + rc.result |= DLGC_WANTARROWS | + DLGC_WANTTAB | + DLGC_WANTALLKEYS; + } + processed = TRUE; } //else: get the dlg code from the DefWindowProc() @@ -2370,62 +2603,79 @@ 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) ) - { - processed = TRUE; - - break; - } - - // we consider these message "not interesting" to OnChar - if ( wParam == VK_SHIFT || wParam == VK_CONTROL ) + // 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; } - switch ( wParam ) + if ( !processed ) { - // 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: - case VK_BACK: - 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 - processed = FALSE; + switch ( wParam ) + { + // 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; - 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: + case VK_BACK: + case VK_TAB: + case VK_ADD: + case VK_SUBTRACT: + 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; #ifdef VK_APPS - // special case of VK_APPS: treat it the same as right mouse - // click because both usually pop up a context menu - case VK_APPS: - { - WPARAM flags; - int x, y; + // special case of VK_APPS: treat it the same as right mouse + // click because both usually pop up a context menu + case VK_APPS: + { + WPARAM flags; + int x, y; - TranslateKbdEventToMouse(this, &x, &y, &flags); - processed = HandleMouseEvent(WM_RBUTTONDOWN, x, y, flags); - } - break; + TranslateKbdEventToMouse(this, &x, &y, &flags); + processed = HandleMouseEvent(WM_RBUTTONDOWN, x, y, flags); + } + break; #endif // VK_APPS - case VK_LEFT: - case VK_RIGHT: - case VK_DOWN: - case VK_UP: - default: - processed = HandleChar((WORD)wParam, lParam); + default: + // do generate a CHAR event + processed = HandleChar((WORD)wParam, lParam); + } } + if (message == WM_SYSKEYDOWN) // Let Windows still handle the SYSKEYs + processed = FALSE; break; case WM_SYSKEYUP: @@ -2449,7 +2699,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: @@ -2501,10 +2762,18 @@ long wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam 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; @@ -2605,29 +2874,30 @@ long wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam processed = GetEventHandler()->ProcessEvent(evtCtx); } 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() ) + case WM_MENUCHAR: + // we're only interested in our own menus, not MF_SYSMENU + if ( HIWORD(wParam) == MF_POPUP ) { - rc.result = HTTRANSPARENT; - processed = TRUE; + // handle menu chars for ownerdrawn menu items + int i = HandleMenuChar(toupper(LOWORD(wParam)), lParam); + if ( i != wxNOT_FOUND ) + { + rc.result = MAKELRESULT(i, MNC_EXECUTE); + processed = TRUE; + } } break; -#endif // __WXUNIVERSAL__ +#endif // __WIN32__ } if ( !processed ) { #ifdef __WXDEBUG__ +#if wxUSE_LOG wxLogTrace(wxTraceMessages, wxT("Forwarding %s to DefWindowProc."), wxGetMessageName(message)); +#endif // wxUSE_LOG #endif // __WXDEBUG__ rc.result = MSWDefWindowProc(message, wParam, lParam); } @@ -2658,7 +2928,7 @@ void wxAssociateWinWithHandle(HWND hWnd, wxWindowMSW *win) if ( oldWin && (oldWin != win) ) { wxLogDebug(wxT("HWND %X already associated with another window (%s)"), - hWnd, win->GetClassInfo()->GetClassName()); + (int) hWnd, win->GetClassInfo()->GetClassName()); } else #endif // __WXDEBUG__ @@ -2688,6 +2958,9 @@ bool wxWindowMSW::MSWGetCreateWindowCoords(const wxPoint& pos, int& x, int& y, int& w, int& h) const { + static const int DEFAULT_Y = 200; + static const int DEFAULT_H = 250; + bool nonDefault = FALSE; if ( pos.x == -1 ) @@ -2699,8 +2972,11 @@ bool wxWindowMSW::MSWGetCreateWindowCoords(const wxPoint& pos, } 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 == -1 ? CW_USEDEFAULT : pos.y; + y = pos.y == -1 ? DEFAULT_Y : pos.y; nonDefault = TRUE; } @@ -2723,14 +2999,15 @@ bool wxWindowMSW::MSWGetCreateWindowCoords(const wxPoint& pos, */ if ( size.x == -1 ) { - // as abobe, h is not used at all in this case anyhow + // as above, h is not used at all in this case anyhow w = h = CW_USEDEFAULT; } else { + // and, again as above, we can't set the height to CW_USEDEFAULT here w = size.x; - h = size.y == -1 ? CW_USEDEFAULT : size.y; + h = size.y == -1 ? DEFAULT_H : size.y; nonDefault = TRUE; } @@ -2738,6 +3015,11 @@ bool wxWindowMSW::MSWGetCreateWindowCoords(const wxPoint& pos, return nonDefault; } +WXHWND wxWindowMSW::MSWGetParent() const +{ + return m_parent ? m_parent->GetHWND() : WXHWND(NULL); +} + bool wxWindowMSW::MSWCreate(const wxChar *wclass, const wxChar *title, const wxPoint& pos, @@ -2749,53 +3031,9 @@ bool wxWindowMSW::MSWCreate(const wxChar *wclass, int x, y, w, h; (void)MSWGetCreateWindowCoords(pos, size, x, y, w, h); - // find the correct parent HWND - wxWindow *parent = GetParent(); - bool isChild = (style & WS_CHILD) != 0; - HWND hParent; - if ( GetWindowStyleFlag() & wxPOPUP_WINDOW ) - { - // popup windows should have desktop as parent because they shouldn't - // be limited to the parents client area as child windows usually are - hParent = ::GetDesktopWindow(); - } - else // !popup - { - if ( (isChild || HasFlag(wxFRAME_TOOL_WINDOW)) && parent ) - { - // this is either a normal child window or a top level window with - // wxFRAME_TOOL_WINDOW style (see below) - hParent = GetHwndOf(parent); - } - else - { - // this is either a window for which no parent was specified (not - // much we can do then) or a frame without wxFRAME_TOOL_WINDOW - // style: we should use NULL parent HWND for it or it would be - // always on top of its parent which is not what we usually want - // (in fact, we only want it for frames with the special - // wxFRAME_TOOL_WINDOW as above) - hParent = NULL; - } - - } - // controlId is menu handle for the top level windows, so set it to 0 // unless we're creating a child window - int controlId; - if ( isChild ) - { - controlId = GetId(); - - if ( GetWindowStyleFlag() & wxCLIP_SIBLINGS ) - { - style |= WS_CLIPSIBLINGS; - } - } - else // !child - { - controlId = 0; - } + int controlId = style & WS_CHILD ? GetId() : 0; // for each class "Foo" we have we also have "FooNR" ("no repaint") class // which is the same but without CS_[HV]REDRAW class styles so using it @@ -2810,17 +3048,17 @@ bool wxWindowMSW::MSWCreate(const wxChar *wclass, wxWindowCreationHook hook(this); m_hWnd = (WXHWND)::CreateWindowEx - ( - extendedStyle, - className, - title ? title : wxT(""), - style, - x, y, w, h, - hParent, - (HMENU)controlId, - wxGetInstance(), - NULL // no extra data - ); + ( + extendedStyle, + className, + title ? title : wxT(""), + style, + x, y, w, h, + (HWND)MSWGetParent(), + (HMENU)controlId, + wxGetInstance(), + NULL // no extra data + ); if ( !m_hWnd ) { @@ -2845,7 +3083,7 @@ bool wxWindowMSW::MSWCreate(const wxChar *wclass, // --------------------------------------------------------------------------- #ifdef __WIN95__ -// FIXME: VZ: I'm not sure at all that the order of processing is correct + bool wxWindowMSW::HandleNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) { #ifndef __WXMICROWIN__ @@ -2853,12 +3091,19 @@ bool wxWindowMSW::HandleNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) HWND hWnd = hdr->hwndFrom; wxWindow *win = wxFindWinFromHandle((WXHWND)hWnd); - // is this one of our windows? + // if the control is one of our windows, let it handle the message itself if ( win ) { return win->MSWOnNotify(idCtrl, lParam, result); } + // VZ: why did we do it? normally this is unnecessary and, besides, it + // breaks the message processing for the toolbars because the tooltip + // notifications were being forwarded to the toolbar child controls + // (if it had any) before being passed to the toolbar itself, so in my + // example the tooltip for the combobox was always shown instead of the + // correct button tooltips +#if 0 // try all our children wxWindowList::Node *node = GetChildren().GetFirst(); while ( node ) @@ -2871,32 +3116,105 @@ bool wxWindowMSW::HandleNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) node = node->GetNext(); } +#endif // 0 - // finally try this window too (catches toolbar case) + // by default, handle it ourselves return MSWOnNotify(idCtrl, lParam, result); #else // __WXMICROWIN__ return FALSE; #endif } +#if wxUSE_TOOLTIPS + +bool wxWindowMSW::HandleTooltipNotify(WXUINT code, + WXLPARAM lParam, + const wxString& ttip) +{ + // I don't know why it happens, but the versions of comctl32.dll starting + // from 4.70 sometimes send TTN_NEEDTEXTW even to ANSI programs (normally, + // this message is supposed to be sent to Unicode programs only) -- hence + // we need to handle it as well, otherwise no tooltips will be shown in + // this case + + if ( !(code == (WXUINT) TTN_NEEDTEXTA || code == (WXUINT) TTN_NEEDTEXTW) || ttip.empty() ) + { + // not a tooltip message or no tooltip to show anyhow + return FALSE; + } + + LPTOOLTIPTEXT ttText = (LPTOOLTIPTEXT)lParam; + +#if !wxUSE_UNICODE + if ( code == (WXUINT) TTN_NEEDTEXTA ) + { + // we pass just the pointer as we store the string internally anyhow + ttText->lpszText = (char *)ttip.c_str(); + } + else // TTN_NEEDTEXTW +#endif // !Unicode + { +#if wxUSE_UNICODE + // in Unicode mode this is just what we need + ttText->lpszText = (wxChar *)ttip.c_str(); +#else // !Unicode + // in ANSI mode we have to convert the string and put it into the + // provided buffer: be careful not to overrun it + const size_t lenAnsi = ttip.length(); + + // some compilers (MetroWerks and Cygwin) don't like calling mbstowcs + // with NULL argument + #if defined( __MWERKS__ ) || defined( __CYGWIN__ ) + size_t lenUnicode = 2*lenAnsi; + #else + size_t lenUnicode = mbstowcs(NULL, ttip, lenAnsi); + #endif + + // using the pointer of right type avoids us doing all sorts of + // pointer arithmetics ourselves + wchar_t *dst = (wchar_t *)ttText->szText, + *pwz = new wchar_t[lenUnicode + 1]; + mbstowcs(pwz, ttip, lenAnsi + 1); + + // stay inside the buffer (-1 because it must be NUL-terminated) + if ( lenUnicode > WXSIZEOF(ttText->szText) - 1 ) + { + lenUnicode = WXSIZEOF(ttText->szText) - 1; + } + + memcpy(dst, pwz, lenUnicode*sizeof(wchar_t)); + + // put the terminating wide NUL + dst[lenUnicode] = L'\0'; + + delete [] pwz; +#endif // Unicode/!Unicode + } + + return TRUE; +} + +#endif // wxUSE_TOOLTIPS + bool wxWindowMSW::MSWOnNotify(int WXUNUSED(idCtrl), - WXLPARAM lParam, - WXLPARAM* WXUNUSED(result)) + WXLPARAM lParam, + WXLPARAM* WXUNUSED(result)) { #if wxUSE_TOOLTIPS - NMHDR* hdr = (NMHDR *)lParam; - if ( (int)hdr->code == TTN_NEEDTEXT && m_tooltip ) + if ( m_tooltip ) { - TOOLTIPTEXT *ttt = (TOOLTIPTEXT *)lParam; - ttt->lpszText = (wxChar *)m_tooltip->GetTip().c_str(); - - // processed - return TRUE; + NMHDR* hdr = (NMHDR *)lParam; + if ( HandleTooltipNotify(hdr->code, lParam, m_tooltip->GetTip())) + { + // processed + return TRUE; + } } #endif // wxUSE_TOOLTIPS return FALSE; } + #endif // __WIN95__ // --------------------------------------------------------------------------- @@ -2944,8 +3262,33 @@ bool wxWindowMSW::HandleEndSession(bool endSession, long logOff) // window creation/destruction // --------------------------------------------------------------------------- -bool wxWindowMSW::HandleCreate(WXLPCREATESTRUCT WXUNUSED(cs), bool *mayCreate) +bool wxWindowMSW::HandleCreate(WXLPCREATESTRUCT cs, bool *mayCreate) { + // if we have WS_EX_CONTROLPARENT flag we absolutely *must* set it for our + // parent as well as otherwise several Win32 functions using + // GetNextDlgTabItem() to iterate over all controls such as + // IsDialogMessage() or DefDlgProc() would enter an infinite loop: indeed, + // all of them iterate over all the controls starting from the focus and + // stop iterating when they get back to the focus but unless all parents + // have WS_EX_CONTROLPARENT bit set, they would never get back to focus + if ( ((CREATESTRUCT *)cs)->dwExStyle & WS_EX_CONTROLPARENT ) + { + // there is no need to do anything for the top level windows + const wxWindow *parent = GetParent(); + while ( parent && !parent->IsTopLevel() ) + { + LONG exStyle = ::GetWindowLong(GetHwndOf(parent), GWL_EXSTYLE); + if ( !(exStyle & WS_EX_CONTROLPARENT) ) + { + // force the parent to have this style + ::SetWindowLong(GetHwndOf(parent), GWL_EXSTYLE, + exStyle | WS_EX_CONTROLPARENT); + } + + parent = parent->GetParent(); + } + } + // TODO: should generate this event from WM_NCCREATE wxWindowCreateEvent event((wxWindow *)this); (void)GetEventHandler()->ProcessEvent(event); @@ -3044,6 +3387,13 @@ bool wxWindowMSW::HandleKillFocus(WXHWND hwnd) } #endif + // Don't send the event when in the process of being deleted. This can + // only cause problems if the event handler tries to access the object. + if ( m_isBeingDeleted ) + { + return FALSE; + } + wxFocusEvent event(wxEVT_KILL_FOCUS, m_windowId); event.SetEventObject(this); @@ -3191,6 +3541,8 @@ bool wxWindowMSW::HandleSetCursor(WXHWND WXUNUSED(hWnd), if ( hcursor ) { +// wxLogDebug("HandleSetCursor: Setting cursor %ld", (long) hcursor); + ::SetCursor(hcursor); // cursor set, stop here @@ -3289,6 +3641,14 @@ bool wxWindowMSW::HandleSysColorChange() return FALSE; } +bool wxWindowMSW::HandleDisplayChange() +{ + wxDisplayChangedEvent event; + event.SetEventObject(this); + + return GetEventHandler()->ProcessEvent(event); +} + bool wxWindowMSW::HandleCtlColor(WXHBRUSH *brush, WXHDC pDC, WXHWND pWnd, @@ -3335,6 +3695,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 + wxWindowMSW *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)); @@ -3342,8 +3736,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 + wxWindowMSW *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); @@ -3351,7 +3776,7 @@ bool wxWindowMSW::HandleQueryNewPalette() } // Responds to colour changes: passes event on to children. -void wxWindowMSW::OnSysColourChanged(wxSysColourChangedEvent& event) +void wxWindowMSW::OnSysColourChanged(wxSysColourChangedEvent& WXUNUSED(event)) { // 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 @@ -3584,9 +4009,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); @@ -3598,27 +4027,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; } @@ -3730,6 +4164,7 @@ void wxWindowMSW::InitMouseEvent(wxMouseEvent& event, event.SetTimestamp(s_currentMsg.time); event.m_eventObject = this; + event.SetId(GetId()); #if wxUSE_MOUSEEVENT_HACK m_lastMouseX = x; @@ -3738,6 +4173,64 @@ void wxWindowMSW::InitMouseEvent(wxMouseEvent& event, #endif // wxUSE_MOUSEEVENT_HACK } +// Windows doesn't send the mouse events to the static controls (which are +// transparent in the sense that their WM_NCHITTEST handler returns +// HTTRANSPARENT) at all but we want all controls to receive the mouse events +// and so we manually check if we don't have a child window under mouse and if +// we do, send the event to it instead of the window Windows had sent WM_XXX +// to. +// +// Notice that this is not done for the mouse move events because this could +// (would?) be too slow, but only for clicks which means that the static texts +// still don't get move, enter nor leave events. +static wxWindowMSW *FindWindowForMouseEvent(wxWindowMSW *win, int *x, int *y) //TW:REQ:Univ +{ + wxCHECK_MSG( x && y, win, _T("NULL pointer in FindWindowForMouseEvent") ); + + // first try to find a non transparent child: this allows us to send events + // to a static text which is inside a static box, for example + POINT pt = { *x, *y }; + HWND hwnd = GetHwndOf(win), + hwndUnderMouse; + +#ifdef __WIN32__ + hwndUnderMouse = ::ChildWindowFromPointEx + ( + hwnd, + pt, + CWP_SKIPINVISIBLE | + CWP_SKIPDISABLED | + CWP_SKIPTRANSPARENT + ); + + if ( !hwndUnderMouse || hwndUnderMouse == hwnd ) +#endif // __WIN32__ + { + // now try any child window at all + hwndUnderMouse = ::ChildWindowFromPoint(hwnd, pt); + } + + // check that we have a child window which is susceptible to receive mouse + // events: for this it must be shown and enabled + if ( hwndUnderMouse && + hwndUnderMouse != hwnd && + ::IsWindowVisible(hwndUnderMouse) && + ::IsWindowEnabled(hwndUnderMouse) ) + { + wxWindow *winUnderMouse = wxFindWinFromHandle((WXHWND)hwndUnderMouse); + if ( winUnderMouse ) + { + // translate the mouse coords to the other window coords + win->ClientToScreen(x, y); + winUnderMouse->ScreenToClient(x, y); + + win = winUnderMouse; + } + } + + return win; +} + bool wxWindowMSW::HandleMouseEvent(WXUINT msg, int x, int y, WXUINT flags) { // the mouse events take consecutive IDs from WM_MOUSEFIRST to @@ -3853,7 +4346,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()); @@ -3863,6 +4357,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 @@ -3888,7 +4384,9 @@ bool wxWindowMSW::HandleChar(WXWPARAM wParam, WXLPARAM lParam, bool isASCII) int id; if ( isASCII ) { - // If 1 -> 26, translate to CTRL plus a letter. + // 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) ) { @@ -3908,30 +4406,37 @@ bool wxWindowMSW::HandleChar(WXWPARAM wParam, WXLPARAM lParam, bool isASCII) default: ctrlDown = TRUE; - id = id + 96; + break; } } } - 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)); + + // the alphanumeric keys produced by pressing AltGr+something on European + // keyboards have both Ctrl and Alt modifiers which may confuse the user + // code as, normally, keys with Ctrl and/or Alt don't result in anything + // alphanumeric, so pretend that there are no modifiers at all (the + // KEY_DOWN event would still have the correct modifiers if they're really + // needed) + if ( event.m_controlDown && event.m_altDown && + (id >= 32 && id < 256) ) + { + event.m_controlDown = + event.m_altDown = FALSE; } - return FALSE; + return GetEventHandler()->ProcessEvent(event); } bool wxWindowMSW::HandleKeyDown(WXWPARAM wParam, WXLPARAM lParam) @@ -3946,7 +4451,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; @@ -3968,7 +4473,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; } @@ -3976,6 +4481,69 @@ bool wxWindowMSW::HandleKeyUp(WXWPARAM wParam, WXLPARAM lParam) return FALSE; } +#ifdef __WIN32__ + +int wxWindowMSW::HandleMenuChar(int chAccel, WXLPARAM lParam) +{ + const HMENU hmenu = (HMENU)lParam; + + MENUITEMINFO mii; + wxZeroMemory(mii); + mii.cbSize = sizeof(MENUITEMINFO); + mii.fMask = MIIM_TYPE | MIIM_DATA; + + // find if we have this letter in any owner drawn item + const int count = ::GetMenuItemCount(hmenu); + for ( int i = 0; i < count; i++ ) + { + if ( ::GetMenuItemInfo(hmenu, i, TRUE, &mii) ) + { + if ( mii.fType == MFT_OWNERDRAW ) + { + // dwItemData member of the MENUITEMINFO is a + // pointer to the associated wxMenuItem -- see the + // menu creation code + wxMenuItem *item = (wxMenuItem*)mii.dwItemData; + + const wxChar *p = wxStrchr(item->GetText(), _T('&')); + while ( p++ ) + { + if ( *p == _T('&') ) + { + // this is not the accel char, find the real one + p = wxStrchr(p + 1, _T('&')); + } + else // got the accel char + { + // FIXME-UNICODE: this comparison doesn't risk to work + // for non ASCII accelerator characters I'm afraid, but + // what can we do? + if ( wxToupper(*p) == chAccel ) + { + return i; + } + else + { + // this one doesn't match + break; + } + } + } + } + } + else // failed ot get the menu text? + { + // it's not fatal, so don't show error, but still log + // it + wxLogLastError(_T("GetMenuItemInfo")); + } + } + + return wxNOT_FOUND; +} + +#endif // __WIN32__ + // --------------------------------------------------------------------------- // joystick // --------------------------------------------------------------------------- @@ -4069,7 +4637,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 ) { @@ -4197,6 +4765,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; @@ -4254,6 +4823,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; } @@ -4968,9 +5550,10 @@ static TEXTMETRIC wxGetTextMetrics(const wxWindowMSW *win) // Find the wxWindow at the current mouse position, returning the mouse // position. -wxWindow* wxFindWindowAtPointer(wxPoint& WXUNUSED(pt)) +wxWindow* wxFindWindowAtPointer(wxPoint& pt) { - return wxFindWindowAtPoint(wxGetMousePosition()); + pt = wxGetMousePosition(); + return wxFindWindowAtPoint(pt); } wxWindow* wxFindWindowAtPoint(const wxPoint& pt) @@ -4997,6 +5580,7 @@ wxPoint wxGetMousePosition() { POINT pt; GetCursorPos( & pt ); + return wxPoint(pt.x, pt.y); }