X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/90c1530adf4014047991d04edf1f097808a88027..8adb5d455797d5b656ef677c74359e5a578b539f:/src/msw/window.cpp diff --git a/src/msw/window.cpp b/src/msw/window.cpp index ea58c880b2..a3e8a83329 100644 --- a/src/msw/window.cpp +++ b/src/msw/window.cpp @@ -5,8 +5,8 @@ // Modified by: VZ on 13.05.99: no more Default(), MSWOnXXX() reorganisation // Created: 04/01/98 // RCS-ID: $Id$ -// Copyright: (c) Julian Smart and Markus Holzem -// Licence: wxWindows license +// Copyright: (c) Julian Smart +// Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// // =========================================================================== @@ -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" @@ -57,6 +58,17 @@ #include "wx/dnd.h" #endif +#if wxUSE_ACCESSIBILITY + #include "wx/access.h" + #include + #ifndef WM_GETOBJECT + #define WM_GETOBJECT 0x003D + #endif + #ifndef OBJID_CLIENT + #define OBJID_CLIENT 0xFFFFFFFC + #endif +#endif + #include "wx/menuitem.h" #include "wx/log.h" @@ -101,6 +113,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,6 +132,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 // --------------------------------------------------------------------------- @@ -140,15 +170,25 @@ static bool gs_hasStdCmap = FALSE; // --------------------------------------------------------------------------- // the window proc for all our windows +#ifdef __DIGITALMARS__ +extern "C" LRESULT WXDLLEXPORT APIENTRY _EXPORT wxWndProc(HWND hWnd, UINT message, + WPARAM wParam, LPARAM lParam); +#else LRESULT WXDLLEXPORT APIENTRY _EXPORT wxWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); +#endif + #ifdef __WXDEBUG__ const char *wxGetMessageName(int message); #endif //__WXDEBUG__ void wxRemoveHandleAssociation(wxWindowMSW *win); -void wxAssociateWinWithHandle(HWND hWnd, wxWindowMSW *win); +#ifdef __DIGITALMARS__ +extern "C" void wxAssociateWinWithHandle(HWND hWnd, wxWindowMSW *win); +#else +extern void wxAssociateWinWithHandle(HWND hWnd, wxWindowMSW *win); +#endif wxWindow *wxFindWinFromHandle(WXHWND hWnd); // this magical function is used to translate VK_APPS key presses to right @@ -159,6 +199,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) { @@ -290,21 +333,19 @@ 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; m_hWnd = 0; - // pass WM_GETDLGCODE to DefWindowProc() - m_lDlgCode = 0; - m_xThumbSize = 0; m_yThumbSize = 0; m_backgroundTransparent = FALSE; @@ -328,12 +369,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; } @@ -360,6 +401,8 @@ wxWindowMSW::~wxWindowMSW() // remove hWnd <-> wxWindow association wxRemoveHandleAssociation(this); } + + delete m_childrenDisabled; } // real construction (Init() must have been called before!) @@ -372,69 +415,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; - } - - // 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 &= ~(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); } // --------------------------------------------------------------------------- @@ -467,6 +477,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() { @@ -488,19 +512,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; } @@ -545,7 +608,7 @@ wxString wxWindowMSW::GetTitle() const return wxGetWindowText(GetHWND()); } -void wxWindowMSW::CaptureMouse() +void wxWindowMSW::DoCaptureMouse() { HWND hWnd = GetHwnd(); if ( hWnd ) @@ -554,7 +617,7 @@ void wxWindowMSW::CaptureMouse() } } -void wxWindowMSW::ReleaseMouse() +void wxWindowMSW::DoReleaseMouse() { if ( !::ReleaseCapture() ) { @@ -901,15 +964,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) @@ -966,7 +1035,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) ) @@ -975,7 +1044,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; } } @@ -1006,119 +1078,158 @@ 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 +// ---------------------------------------------------------------------------- -// Make a Windows extended style from the given wxWindows window style -WXDWORD wxWindowMSW::MakeExtendedStyle(long style, bool eliminateBorders) +void wxWindowMSW::SetWindowStyleFlag(long flags) { - WXDWORD exStyle = 0; - if ( style & wxTRANSPARENT_WINDOW ) - exStyle |= WS_EX_TRANSPARENT; + long flagsOld = GetWindowStyleFlag(); + if ( flags == flagsOld ) + return; + + // update the internal variable + wxWindowBase::SetWindowStyleFlag(flags); - if ( !eliminateBorders ) + // 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 ) { - if ( style & wxSUNKEN_BORDER ) - exStyle |= WS_EX_CLIENTEDGE; - if ( style & wxDOUBLE_BORDER ) - exStyle |= WS_EX_DLGMODALFRAME; -#if defined(__WIN95__) - if ( style & wxRAISED_BORDER ) - // It seems that WS_EX_WINDOWEDGE doesn't work, but WS_EX_DLGMODALFRAME does - exStyle |= WS_EX_DLGMODALFRAME; /* WS_EX_WINDOWEDGE */; - if ( style & wxSTATIC_BORDER ) - exStyle |= WS_EX_STATICEDGE; -#endif + // 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); } - return exStyle; + // 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")); + } + } } -// Determines whether native 3D effects or CTL3D should be used, -// applying a default border style if required, and returning an extended -// style to pass to CreateWindowEx. -WXDWORD wxWindowMSW::Determine3DEffects(WXDWORD defaultBorderStyle, - bool *want3D) const +WXDWORD wxWindowMSW::MSWGetStyle(long flags, WXDWORD *exstyle) const { - // If matches certain criteria, then assume no 3D effects - // unless specifically requested (dealt with in MakeExtendedStyle) - if ( !GetParent() -#if wxUSE_CONTROLS - || !IsKindOf(CLASSINFO(wxControl)) -#endif // wxUSE_CONTROLS - || (m_windowStyle & wxNO_BORDER) ) + // 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); + + // Check if we want to automatically give it a sunken style. + // Note than because 'sunken' actually maps to WS_EX_CLIENTEDGE, which + // is a more neutral term, we don't necessarily get a sunken effect in + // Windows XP. Instead we get the appropriate style for the theme. + + if (border == wxBORDER_DEFAULT && wxTheApp->GetAuto3D() && GetParent() && + ((GetParent()->GetWindowStyleFlag() & wxUSER_COLOURS) != wxUSER_COLOURS)) { - *want3D = FALSE; - return MakeExtendedStyle(m_windowStyle); - } + border = (wxBorder)((flags & wxBORDER_MASK) | wxBORDER_SUNKEN); + } + + // Only give it WS_BORDER for wxBORDER_SIMPLE + if (border & wxBORDER_SIMPLE) + style |= WS_BORDER; + + // now deal with ext style if the caller wants it + if ( exstyle ) + { + *exstyle = 0; - // Determine whether we should be using 3D effects or not. - bool nativeBorder = FALSE; // by default, we don't want a Win95 effect + if ( flags & wxTRANSPARENT_WINDOW ) + *exstyle |= WS_EX_TRANSPARENT; - // 1) App can specify global 3D effects - *want3D = wxTheApp->GetAuto3D(); + switch ( border ) + { + default: + wxFAIL_MSG( _T("unknown border style") ); + // fall through - // 2) If the parent is being drawn with user colours, or simple border specified, - // switch effects off. TODO: replace wxUSER_COLOURS with wxNO_3D - if ( GetParent() && (GetParent()->GetWindowStyleFlag() & wxUSER_COLOURS) || (m_windowStyle & wxSIMPLE_BORDER) ) - *want3D = FALSE; + case wxBORDER_NONE: + case wxBORDER_SIMPLE: + case wxBORDER_DEFAULT: + break; - // 3) Control can override this global setting by defining - // a border style, e.g. wxSUNKEN_BORDER - if ( m_windowStyle & wxSUNKEN_BORDER ) - *want3D = TRUE; + case wxBORDER_STATIC: + *exstyle |= WS_EX_STATICEDGE; + break; - // 4) If it's a special border, CTL3D can't cope so we want a native border - if ( (m_windowStyle & wxDOUBLE_BORDER) || (m_windowStyle & wxRAISED_BORDER) || - (m_windowStyle & wxSTATIC_BORDER) ) - { - *want3D = TRUE; - nativeBorder = TRUE; - } + case wxBORDER_RAISED: + *exstyle |= WS_EX_WINDOWEDGE; + break; - // 5) If this isn't a Win95 app, and we are using CTL3D, remove border - // effects from extended style -#if wxUSE_CTL3D - if ( *want3D ) - nativeBorder = FALSE; -#endif + case wxBORDER_SUNKEN: + *exstyle |= WS_EX_CLIENTEDGE; + style &= ~WS_BORDER; + break; - DWORD exStyle = MakeExtendedStyle(m_windowStyle, !nativeBorder); + case wxBORDER_DOUBLE: + *exstyle |= WS_EX_DLGMODALFRAME; + break; + } - // If we want 3D, but haven't specified a border here, - // apply the default border style specified. - // TODO what about non-Win95 WIN32? Does it have borders? -#if defined(__WIN95__) && !wxUSE_CTL3D - if ( defaultBorderStyle && (*want3D) && ! ((m_windowStyle & wxDOUBLE_BORDER) || (m_windowStyle & wxRAISED_BORDER ) || - (m_windowStyle & wxSTATIC_BORDER) || (m_windowStyle & wxSIMPLE_BORDER) )) - exStyle |= defaultBorderStyle; // WS_EX_CLIENTEDGE; -#endif + // 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 exStyle; + return style; } #if WXWIN_COMPATIBILITY @@ -1179,6 +1290,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 @@ -1302,10 +1415,12 @@ void wxWindowMSW::Update() // drag and drop // --------------------------------------------------------------------------- -#if wxUSE_DRAG_AND_DROP +//FIXME __DIGITALMARS__ does not honor drag drop in setup.h +#if wxUSE_DRAG_AND_DROP void wxWindowMSW::SetDropTarget(wxDropTarget *pDropTarget) { +#ifndef __DIGITALMARS__ if ( m_dropTarget != 0 ) { m_dropTarget->Revoke(m_hWnd); delete m_dropTarget; @@ -1314,17 +1429,19 @@ void wxWindowMSW::SetDropTarget(wxDropTarget *pDropTarget) m_dropTarget = pDropTarget; if ( m_dropTarget != 0 ) m_dropTarget->Register(m_hWnd); +#endif // __DIGITALMARS__ } - #endif // wxUSE_DRAG_AND_DROP // old style file-manager drag&drop support: we retain the old-style // DragAcceptFiles in parallel with SetDropTarget. void wxWindowMSW::DragAcceptFiles(bool accept) { +#ifndef __DIGITALMARS__ HWND hWnd = GetHwnd(); if ( hWnd ) ::DragAcceptFiles(hWnd, (BOOL)accept); +#endif } // ---------------------------------------------------------------------------- @@ -1338,7 +1455,7 @@ void wxWindowMSW::DoSetToolTip(wxToolTip *tooltip) wxWindowBase::DoSetToolTip(tooltip); if ( m_tooltip ) - m_tooltip->SetWindow(this); + m_tooltip->SetWindow((wxWindow *)this); } #endif // wxUSE_TOOLTIPS @@ -1542,20 +1659,12 @@ void wxWindowMSW::DoSetClientSize(int width, int height) ::GetClientRect(GetHwnd(), &rectClient); // if the size is already ok, stop here (rectClient.left = top = 0) - if ( rectClient.right == width && rectClient.bottom == height ) + if ( (rectClient.right == width || width == -1) && + (rectClient.bottom == height || height == -1) ) { 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; @@ -1718,6 +1827,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) @@ -1759,7 +1872,11 @@ bool wxWindowMSW::DoPopupMenu(wxMenu *menu, int x, int y) long wxWindowMSW::MSWDefWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) { if ( m_oldWndProc ) +#ifdef __DIGITALMARS__ + return ::CallWindowProc( (FARPROC) m_oldWndProc, GetHwnd(), (UINT) nMsg, (WPARAM) wParam, (LPARAM) lParam); +#else return ::CallWindowProc(CASTWNDPROC m_oldWndProc, GetHwnd(), (UINT) nMsg, (WPARAM) wParam, (LPARAM) lParam); +#endif else return ::DefWindowProc(GetHwnd(), nMsg, wParam, lParam); } @@ -1776,14 +1893,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(); @@ -1795,11 +1905,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: @@ -1870,12 +1990,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 } } } @@ -1928,10 +2063,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__ @@ -1954,11 +2125,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; @@ -2081,8 +2253,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); @@ -2207,10 +2381,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: @@ -2238,68 +2420,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: @@ -2361,9 +2538,19 @@ long wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam #endif // defined(WM_DRAWITEM) case WM_GETDLGCODE: - if ( m_lDlgCode ) + if ( !IsOfStandardClass() ) { - rc.result = m_lDlgCode; + // 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() @@ -2371,62 +2558,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) ) + // 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 ) + 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: @@ -2450,7 +2654,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: @@ -2502,10 +2717,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; @@ -2559,6 +2782,20 @@ long wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam } break; +#if wxUSE_ACCESSIBILITY + case WM_GETOBJECT: + { + //WPARAM dwFlags = (WPARAM) (DWORD) wParam; + LPARAM dwObjId = (LPARAM) (DWORD) lParam; + + if (dwObjId == OBJID_CLIENT && GetOrCreateAccessible()) + { + return LresultFromObject(IID_IAccessible, wParam, (IUnknown*) GetAccessible()->GetIAccessible()); + } + break; + } +#endif + #if defined(__WIN32__) && defined(WM_HELP) case WM_HELP: { @@ -2606,29 +2843,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); } @@ -2659,7 +2897,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__ @@ -2689,6 +2927,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 ) @@ -2700,8 +2941,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; } @@ -2724,14 +2968,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; } @@ -2739,6 +2984,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, @@ -2750,53 +3000,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 @@ -2811,17 +3017,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 ) { @@ -2846,7 +3052,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__ @@ -2854,12 +3060,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 ) @@ -2872,32 +3085,78 @@ 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 + MultiByteToWideChar(CP_ACP, 0, ttip, ttip.length()+1, + (wchar_t *)ttText->szText, + sizeof(ttText->szText) / sizeof(wchar_t)); +#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__ // --------------------------------------------------------------------------- @@ -2945,8 +3204,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); @@ -2958,11 +3242,11 @@ bool wxWindowMSW::HandleCreate(WXLPCREATESTRUCT WXUNUSED(cs), bool *mayCreate) bool wxWindowMSW::HandleDestroy() { - wxWindowDestroyEvent event((wxWindow *)this); - (void)GetEventHandler()->ProcessEvent(event); + SendDestroyEvent(); // delete our drop target if we've got one #if wxUSE_DRAG_AND_DROP +#ifndef __DIGITALMARS__ if ( m_dropTarget != NULL ) { m_dropTarget->Revoke(m_hWnd); @@ -2970,6 +3254,7 @@ bool wxWindowMSW::HandleDestroy() delete m_dropTarget; m_dropTarget = NULL; } +#endif //#ifndef __DIGITALMARS__ #endif // wxUSE_DRAG_AND_DROP // WM_DESTROY handled @@ -3045,6 +3330,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); @@ -3076,7 +3368,10 @@ bool wxWindowMSW::HandleInitDialog(WXHWND WXUNUSED(hWndFocus)) bool wxWindowMSW::HandleDropFiles(WXWPARAM wParam) { -#ifndef __WXMICROWIN__ +//FIX ME __DIGITALMARS__ +#if defined (__WXMICROWIN__) || defined (__DIGITALMARS__) + return FALSE; +#else // __WXMICROWIN__ HDROP hFilesInfo = (HDROP) wParam; // Get the total number of files dropped @@ -3111,11 +3406,13 @@ bool wxWindowMSW::HandleDropFiles(WXWPARAM wParam) event.m_pos.y = dropPoint.y; return GetEventHandler()->ProcessEvent(event); -#else // __WXMICROWIN__ - return FALSE; #endif } +#ifdef __DIGITALMARS__ +extern "C" HCURSOR wxGetCurrentBusyCursor(); +#endif + bool wxWindowMSW::HandleSetCursor(WXHWND WXUNUSED(hWnd), short nHitTest, int WXUNUSED(mouseMsg)) @@ -3192,6 +3489,8 @@ bool wxWindowMSW::HandleSetCursor(WXHWND WXUNUSED(hWnd), if ( hcursor ) { +// wxLogDebug("HandleSetCursor: Setting cursor %ld", (long) hcursor); + ::SetCursor(hcursor); // cursor set, stop here @@ -3290,6 +3589,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, @@ -3336,6 +3643,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)); @@ -3343,8 +3684,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); @@ -3352,7 +3724,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 @@ -3585,9 +3957,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); @@ -3599,27 +3975,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; } @@ -3731,6 +4112,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; @@ -3739,6 +4121,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 @@ -3854,7 +4294,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()); @@ -3864,6 +4305,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 @@ -3889,7 +4332,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) ) { @@ -3909,30 +4354,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) @@ -3947,7 +4399,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; @@ -3969,7 +4421,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; } @@ -3977,6 +4429,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 // --------------------------------------------------------------------------- @@ -4070,7 +4585,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 ) { @@ -4198,6 +4713,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; @@ -4255,6 +4771,25 @@ 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; + +#ifdef VK_APPS + case VK_LWIN: id = WXK_WINDOWS_LEFT; break; + case VK_RWIN: id = WXK_WINDOWS_RIGHT; break; + case VK_APPS: id = WXK_WINDOWS_MENU; break; +#endif // VK_APPS defined + default: id = 0; } @@ -4444,7 +4979,7 @@ void wxSetKeyboardHook(bool doIt) // avoids warning about statement with no effect (FreeProcInstance // doesn't do anything under Win32) -#if !defined(WIN32) && !defined(_WIN32) && !defined(__WIN32__) && !defined(__NT__) && !defined(__GNUWIN32__) +#if !defined(__WIN32__) && !defined(__NT__) FreeProcInstance(wxTheKeyboardHookProc); #endif } @@ -4969,9 +5504,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) @@ -4998,6 +5534,7 @@ wxPoint wxGetMousePosition() { POINT pt; GetCursorPos( & pt ); + return wxPoint(pt.x, pt.y); }