X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/73927f8bac6a9392855eb20def7cf2a0049c05e4..656fc51cda91ea40154358a6adcd85ded506812a:/src/msw/window.cpp diff --git a/src/msw/window.cpp b/src/msw/window.cpp index 5f6bf803cf..f58179b4c3 100644 --- a/src/msw/window.cpp +++ b/src/msw/window.cpp @@ -50,13 +50,12 @@ #include #endif -#if wxUSE_OWNER_DRAWN +#if wxUSE_OWNER_DRAWN #include "wx/ownerdrw.h" #endif -#if wxUSE_DRAG_AND_DROP - #include "wx/dataobj.h" - #include "wx/msw/ole/droptgt.h" +#if wxUSE_DRAG_AND_DROP + #include "wx/dnd.h" #endif #include "wx/menuitem.h" @@ -72,15 +71,19 @@ #include "wx/caret.h" #endif // wxUSE_CARET +#if wxUSE_SPINCTRL + #include "wx/spinctrl.h" +#endif // wxUSE_SPINCTRL + #include "wx/intl.h" #include "wx/log.h" - #include "wx/textctrl.h" +#include "wx/notebook.h" #include -#ifndef __GNUWIN32__ +#ifndef __GNUWIN32_OLD__ #include #include #endif @@ -89,25 +92,20 @@ #include #endif -#if ( defined(__WIN95__) && !defined(__GNUWIN32__)) || defined(__TWIN32__ ) - #include -#endif - -#ifndef __TWIN32__ - #ifdef __GNUWIN32__ - #include +#if !defined(__GNUWIN32_OLD__) && !defined(__TWIN32__) + #ifdef __WIN95__ + #include + #endif +#else // broken compiler + #ifndef __TWIN32__ + #include "wx/msw/gnuwin32/extra.h" #endif #endif -// --------------------------------------------------------------------------- -// macros -// --------------------------------------------------------------------------- - -// standard macros missing from some compilers headers -#ifndef GET_X_LPARAM - #define GET_X_LPARAM(lp) ((int)(short)LOWORD(lp)) - #define GET_Y_LPARAM(lp) ((int)(short)HIWORD(lp)) -#endif // GET_X_LPARAM +// This didn't appear in mingw until 2.95.2 +#ifndef SIF_TRACKPOS +#define SIF_TRACKPOS 16 +#endif // --------------------------------------------------------------------------- // global variables @@ -118,14 +116,14 @@ extern MSG s_currentMsg; wxMenu *wxCurrentPopupMenu = NULL; extern wxList WXDLLEXPORT wxPendingDelete; -extern wxChar wxCanvasClassName[]; +extern const wxChar *wxCanvasClassName; // --------------------------------------------------------------------------- // private functions // --------------------------------------------------------------------------- // the window proc for all our windows -LRESULT APIENTRY _EXPORT wxWndProc(HWND hWnd, UINT message, +LRESULT WXDLLEXPORT APIENTRY _EXPORT wxWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); #ifdef __WXDEBUG__ @@ -136,19 +134,25 @@ void wxRemoveHandleAssociation(wxWindow *win); void wxAssociateWinWithHandle(HWND hWnd, wxWindow *win); wxWindow *wxFindWinFromHandle(WXHWND hWnd); +// this magical function is used to translate VK_APPS key presses to right +// mouse clicks +static void TranslateKbdEventToMouse(wxWindow *win, int *x, int *y, WPARAM *flags); + +// get the text metrics for the current font +static TEXTMETRIC wxGetTextMetrics(const wxWindow *win); + // --------------------------------------------------------------------------- // event tables // --------------------------------------------------------------------------- -#if !USE_SHARED_LIBRARY - IMPLEMENT_DYNAMIC_CLASS(wxWindow, wxWindowBase) -#endif +IMPLEMENT_DYNAMIC_CLASS(wxWindow, wxWindowBase) BEGIN_EVENT_TABLE(wxWindow, wxWindowBase) EVT_ERASE_BACKGROUND(wxWindow::OnEraseBackground) EVT_SYS_COLOUR_CHANGED(wxWindow::OnSysColourChanged) EVT_INIT_DIALOG(wxWindow::OnInitDialog) EVT_IDLE(wxWindow::OnIdle) + EVT_SET_FOCUS(wxWindow::OnSetFocus) END_EVENT_TABLE() // =========================================================================== @@ -160,8 +164,19 @@ END_EVENT_TABLE() // --------------------------------------------------------------------------- // Find an item given the MS Windows id -wxWindow *wxWindow::FindItem(int id) const +wxWindow *wxWindow::FindItem(long id) const { + wxControl *item = wxDynamicCast(this, wxControl); + if ( item ) + { + // i it we or one of our "internal" children? + if ( item->GetId() == id || + (item->GetSubcontrols().Index(id) != wxNOT_FOUND) ) + { + return item; + } + } + wxWindowList::Node *current = GetChildren().GetFirst(); while (current) { @@ -171,19 +186,6 @@ wxWindow *wxWindow::FindItem(int id) const if ( wnd ) return wnd; - if ( childWin->IsKindOf(CLASSINFO(wxControl)) ) - { - wxControl *item = (wxControl *)childWin; - if ( item->GetId() == id ) - return item; - else - { - // In case it's a 'virtual' control (e.g. radiobox) - if ( item->GetSubcontrols().Member((wxObject *)id) ) - return item; - } - } - current = current->GetNext(); } @@ -280,8 +282,12 @@ wxWindow::~wxWindow() if ( m_hWnd ) { - if ( !::DestroyWindow(GetHwnd()) ) - wxLogLastError("DestroyWindow"); + // VZ: test temp removed to understand what really happens here + //if (::IsWindow(GetHwnd())) + { + if ( !::DestroyWindow(GetHwnd()) ) + wxLogLastError(wxT("DestroyWindow")); + } // remove hWnd <-> wxWindow association wxRemoveHandleAssociation(this); @@ -295,21 +301,27 @@ bool wxWindow::Create(wxWindow *parent, wxWindowID id, long style, const wxString& name) { - wxCHECK_MSG( parent, FALSE, _T("can't create wxWindow without parent") ); + wxCHECK_MSG( parent, FALSE, wxT("can't create wxWindow without parent") ); - CreateBase(parent, id, pos, size, style, name); + if ( !CreateBase(parent, id, pos, size, style, wxDefaultValidator, name) ) + return FALSE; parent->AddChild(this); DWORD msflags = 0; if ( style & wxBORDER ) msflags |= WS_BORDER; +/* Not appropriate for non-frame/dialog windows, and + may clash with other window styles. if ( style & wxTHICK_FRAME ) msflags |= WS_THICKFRAME; - +*/ + //msflags |= WS_CHILD /* | WS_CLIPSIBLINGS */ | WS_VISIBLE; msflags |= WS_CHILD | WS_VISIBLE; if ( style & wxCLIP_CHILDREN ) msflags |= WS_CLIPCHILDREN; + if ( style & wxCLIP_SIBLINGS ) + msflags |= WS_CLIPSIBLINGS; bool want3D; WXDWORD exStyle = Determine3DEffects(WS_EX_CLIENTEDGE, &want3D); @@ -370,6 +382,20 @@ bool wxWindow::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 eb reenabled too when you reenable the dialog! +#if 0 + wxWindowList::Node *node = GetChildren().GetFirst(); + while ( node ) + { + wxWindow *child = node->GetData(); + child->Enable(enable); + + node = node->GetNext(); + } +#endif // 0 + return TRUE; } @@ -445,7 +471,7 @@ bool wxWindow::SetFont(const wxFont& font) { WXHANDLE hFont = m_font.GetResourceHandle(); - wxASSERT_MSG( hFont, _T("should have valid font") ); + wxASSERT_MSG( hFont, wxT("should have valid font") ); ::SendMessage(hWnd, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(TRUE, 0)); } @@ -460,20 +486,20 @@ bool wxWindow::SetCursor(const wxCursor& cursor) return FALSE; } - wxASSERT_MSG( m_cursor.Ok(), - _T("cursor must be valid after call to the base version")); - - HWND hWnd = GetHwnd(); + if ( m_cursor.Ok() ) + { + HWND hWnd = GetHwnd(); - // Change the cursor NOW if we're within the correct window - POINT point; - ::GetCursorPos(&point); + // Change the cursor NOW if we're within the correct window + POINT point; + ::GetCursorPos(&point); - RECT rect; - ::GetWindowRect(hWnd, &rect); + RECT rect; + ::GetWindowRect(hWnd, &rect); - if ( ::PtInRect(&rect, point) && !wxIsBusy() ) - ::SetCursor((HCURSOR)m_cursor.GetHCURSOR()); + if ( ::PtInRect(&rect, point) && !wxIsBusy() ) + ::SetCursor(GetHcursorOf(m_cursor)); + } return TRUE; } @@ -798,10 +824,10 @@ void wxWindow::ScrollWindow(int dx, int dy, const wxRect *rect) void wxWindow::SubclassWin(WXHWND hWnd) { - wxASSERT_MSG( !m_oldWndProc, _T("subclassing window twice?") ); + wxASSERT_MSG( !m_oldWndProc, wxT("subclassing window twice?") ); HWND hwnd = (HWND)hWnd; - wxCHECK_RET( ::IsWindow(hwnd), _T("invalid HWND in SubclassWin") ); + wxCHECK_RET( ::IsWindow(hwnd), wxT("invalid HWND in SubclassWin") ); wxAssociateWinWithHandle(hwnd, this); @@ -819,7 +845,7 @@ void wxWindow::UnsubclassWin() { m_hWnd = 0; - wxCHECK_RET( ::IsWindow(hwnd), _T("invalid HWND in SubclassWin") ); + wxCHECK_RET( ::IsWindow(hwnd), wxT("invalid HWND in UnsubclassWin") ); FARPROC farProc = (FARPROC) GetWindowLong(hwnd, GWL_WNDPROC); if ( (m_oldWndProc != 0) && (farProc != (FARPROC) m_oldWndProc) ) @@ -845,7 +871,8 @@ WXDWORD wxWindow::MakeExtendedStyle(long style, bool eliminateBorders) exStyle |= WS_EX_DLGMODALFRAME; #if defined(__WIN95__) if ( style & wxRAISED_BORDER ) - exStyle |= WS_EX_WINDOWEDGE; + // 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 @@ -856,7 +883,8 @@ WXDWORD wxWindow::MakeExtendedStyle(long style, bool eliminateBorders) // 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 wxWindow::Determine3DEffects(WXDWORD defaultBorderStyle, bool *want3D) +WXDWORD wxWindow::Determine3DEffects(WXDWORD defaultBorderStyle, + bool *want3D) const { // If matches certain criteria, then assume no 3D effects // unless specifically requested (dealt with in MakeExtendedStyle) @@ -965,11 +993,17 @@ void wxWindow::OnIdle(wxIdleEvent& event) // by the time the OnIdle function is called, so 'state' may be // meaningless. int state = 0; - if ( ::GetKeyState(VK_SHIFT) != 0 ) + if ( wxIsShiftDown() ) state |= MK_SHIFT; - if ( ::GetKeyState(VK_CONTROL) != 0 ) + if ( wxIsCtrlDown() ) state |= MK_CONTROL; - + if ( GetKeyState( VK_LBUTTON ) ) + state |= MK_LBUTTON; + if ( GetKeyState( VK_MBUTTON ) ) + state |= MK_MBUTTON; + if ( GetKeyState( VK_RBUTTON ) ) + state |= MK_RBUTTON; + wxMouseEvent event(wxEVT_LEAVE_WINDOW); InitMouseEvent(event, pt.x, pt.y, state); @@ -1076,8 +1110,14 @@ void wxWindow::DoGetSize(int *x, int *y) const { HWND hWnd = GetHwnd(); RECT rect; - GetWindowRect(hWnd, &rect); - +#ifdef __WIN16__ + ::GetWindowRect(hWnd, &rect); +#else + if ( !::GetWindowRect(hWnd, &rect) ) + { + wxLogLastError(_T("GetWindowRect")); + } +#endif if ( x ) *x = rect.right - rect.left; if ( y ) @@ -1111,10 +1151,10 @@ void wxWindow::DoGetPosition(int *x, int *y) const ::ScreenToClient(hParentWnd, &point); } - // We may be faking the client origin. So a window that's really at (0, - // 30) may appear (to wxWin apps) to be at (0, 0). if ( parent ) { + // We may be faking the client origin. So a window that's really at (0, + // 30) may appear (to wxWin apps) to be at (0, 0). wxPoint pt(parent->GetClientAreaOrigin()); point.x -= pt.x; point.y -= pt.y; @@ -1173,6 +1213,14 @@ void wxWindow::DoGetClientSize(int *x, int *y) const *y = rect.bottom; } +void wxWindow::DoMoveWindow(int x, int y, int width, int height) +{ + if ( !::MoveWindow(GetHwnd(), x, y, width, height, TRUE) ) + { + wxLogLastError(wxT("MoveWindow")); + } +} + // set the size of the window: if the dimensions are positive, just use them, // but if any of them is equal to -1, it means that we must find the value for // it ourselves (unless sizeFlags contains wxSIZE_ALLOW_MINUS_ONE flag, in @@ -1196,9 +1244,9 @@ void wxWindow::DoSetSize(int x, int y, int width, int height, int sizeFlags) return; } - if ( x == -1 || (sizeFlags & wxSIZE_ALLOW_MINUS_ONE) ) + if ( x == -1 && !(sizeFlags & wxSIZE_ALLOW_MINUS_ONE) ) x = currentX; - if ( y == -1 || (sizeFlags & wxSIZE_ALLOW_MINUS_ONE) ) + if ( y == -1 && !(sizeFlags & wxSIZE_ALLOW_MINUS_ONE) ) y = currentY; AdjustForParentClientOrigin(x, y, sizeFlags); @@ -1206,7 +1254,7 @@ void wxWindow::DoSetSize(int x, int y, int width, int height, int sizeFlags) wxSize size(-1, -1); if ( width == -1 ) { - if ( sizeFlags && wxSIZE_AUTO_WIDTH ) + if ( sizeFlags & wxSIZE_AUTO_WIDTH ) { size = DoGetBestSize(); width = size.x; @@ -1220,11 +1268,11 @@ void wxWindow::DoSetSize(int x, int y, int width, int height, int sizeFlags) if ( height == -1 ) { - if ( sizeFlags && wxSIZE_AUTO_HEIGHT ) + if ( sizeFlags & wxSIZE_AUTO_HEIGHT ) { if ( size.x == -1 ) { - size= DoGetBestSize(); + size = DoGetBestSize(); } //else: already called DoGetBestSize() above @@ -1237,16 +1285,7 @@ void wxWindow::DoSetSize(int x, int y, int width, int height, int sizeFlags) } } - if ( !::MoveWindow(GetHwnd(), x, y, width, height, TRUE) ) - { - wxLogLastError("MoveWindow"); - } -} - -// for a generic window there is no natural best size - just use the current one -wxSize wxWindow::DoGetBestSize() -{ - return GetSize(); + DoMoveWindow(x, y, width, height); } void wxWindow::DoSetClientSize(int width, int height) @@ -1280,7 +1319,7 @@ void wxWindow::DoSetClientSize(int width, int height) ::ScreenToClient(hParentWnd, &point); } - MoveWindow(hWnd, point.x, point.y, actual_width, actual_height, (BOOL)TRUE); + DoMoveWindow(point.x, point.y, actual_width, actual_height); wxSizeEvent event(wxSize(width, height), m_windowId); event.SetEventObject(this); @@ -1317,26 +1356,18 @@ void wxWindow::AdjustForParentClientOrigin(int& x, int& y, int sizeFlags) int wxWindow::GetCharHeight() const { - TEXTMETRIC lpTextMetric; - HWND hWnd = GetHwnd(); - HDC dc = ::GetDC(hWnd); - - GetTextMetrics(dc, &lpTextMetric); - ::ReleaseDC(hWnd, dc); - - return lpTextMetric.tmHeight; + return wxGetTextMetrics(this).tmHeight; } int wxWindow::GetCharWidth() const { - TEXTMETRIC lpTextMetric; - HWND hWnd = GetHwnd(); - HDC dc = ::GetDC(hWnd); - - GetTextMetrics(dc, &lpTextMetric); - ::ReleaseDC(hWnd, dc); - - return lpTextMetric.tmAveCharWidth; + // +1 is needed because Windows apparently adds it when calculating the + // dialog units size in pixels +#if wxDIALOG_UNIT_COMPATIBILITY + return wxGetTextMetrics(this).tmAveCharWidth ; +#else + return wxGetTextMetrics(this).tmAveCharWidth + 1; +#endif } void wxWindow::GetTextExtent(const wxString& string, @@ -1370,10 +1401,14 @@ void wxWindow::GetTextExtent(const wxString& string, ReleaseDC(hWnd, dc); - if ( x ) *x = sizeRect.cx; - if ( y ) *y = sizeRect.cy; - if ( descent ) *descent = tm.tmDescent; - if ( externalLeading ) *externalLeading = tm.tmExternalLeading; + if ( x ) + *x = sizeRect.cx; + if ( y ) + *y = sizeRect.cy; + if ( descent ) + *descent = tm.tmDescent; + if ( externalLeading ) + *externalLeading = tm.tmExternalLeading; } #if wxUSE_CARET && WXWIN_COMPATIBILITY @@ -1422,7 +1457,7 @@ void wxWindow::GetCaretPos(int *x, int *y) const // popup menu // --------------------------------------------------------------------------- -bool wxWindow::PopupMenu(wxMenu *menu, int x, int y) +bool wxWindow::DoPopupMenu(wxMenu *menu, int x, int y) { menu->SetInvokingWindow(this); menu->UpdateUI(); @@ -1435,7 +1470,7 @@ bool wxWindow::PopupMenu(wxMenu *menu, int x, int y) ::ClientToScreen(hWnd, &point); wxCurrentPopupMenu = menu; ::TrackPopupMenu(hMenu, TPM_RIGHTBUTTON, point.x, point.y, 0, hWnd, NULL); - wxYield(); + wxYieldIfNeeded(); wxCurrentPopupMenu = NULL; menu->SetInvokingWindow(NULL); @@ -1461,6 +1496,10 @@ bool wxWindow::MSWProcessMessage(WXMSG* pMsg) { // intercept dialog navigation keys MSG *msg = (MSG *)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; @@ -1470,8 +1509,8 @@ bool wxWindow::MSWProcessMessage(WXMSG* pMsg) if ( bProcess ) { - bool bCtrlDown = (::GetKeyState(VK_CONTROL) & 0x100) != 0; - bool bShiftDown = (::GetKeyState(VK_SHIFT) & 0x100) != 0; + bool bCtrlDown = wxIsCtrlDown(); + bool bShiftDown = wxIsShiftDown(); // WM_GETDLGCODE: ask the control if it wants the key for itself, // don't process it if it's the case (except for Ctrl-Tab/Enter @@ -1526,7 +1565,18 @@ bool wxWindow::MSWProcessMessage(WXMSG* pMsg) } else if ( lDlgCode & DLGC_BUTTON ) { - // buttons want process Enter themselevs + // let IsDialogMessage() handle this for all + // buttons except the owner-drawn ones which it + // just seems to ignore + long style = ::GetWindowLong(msg->hwnd, GWL_STYLE); + if ( (style & BS_OWNERDRAW) == BS_OWNERDRAW ) + { + // emulate the button click + wxWindow *btn = wxFindWinFromHandle((WXHWND)msg->hwnd); + if ( btn ) + btn->MSWCommand(BN_CLICKED, 0 /* unused */); + } + bProcess = FALSE; } else @@ -1578,9 +1628,41 @@ bool wxWindow::MSWProcessMessage(WXMSG* pMsg) } } } +#else + // let ::IsDialogMessage() do almost everything and handle just the + // things it doesn't here: Ctrl-TAB for switching notebook pages + if ( msg->message == WM_KEYDOWN ) + { + // don't process system keys here + if ( !(HIWORD(msg->lParam) & KF_ALTDOWN) ) + { + if ( (msg->wParam == VK_TAB) && wxIsCtrlDown() ) + { + // find the first notebook parent and change its page + wxWindow *win = this; + wxNotebook *nbook = NULL; + while ( win && !nbook ) + { + nbook = wxDynamicCast(win, wxNotebook); + win = win->GetParent(); + } + + if ( nbook ) + { + bool forward = !wxIsShiftDown(); + + nbook->AdvanceSelection(forward); + } + } + } + } +#endif // 0 if ( ::IsDialogMessage(GetHwnd(), msg) ) + { + // IsDialogMessage() did something... return TRUE; + } } #if wxUSE_TOOLTIPS @@ -1700,11 +1782,11 @@ void wxWindow::UnpackMenuSelect(WXWPARAM wParam, WXLPARAM lParam, wxWindow *wxWndHook = NULL; // Main window proc -LRESULT APIENTRY _EXPORT wxWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +LRESULT WXDLLEXPORT APIENTRY _EXPORT wxWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { // trace all messages - useful for the debugging #ifdef __WXDEBUG__ - wxLogTrace(wxTraceMessages, _T("Processing %s(wParam=%8lx, lParam=%8lx)"), + wxLogTrace(wxTraceMessages, wxT("Processing %s(wParam=%8lx, lParam=%8lx)"), wxGetMessageName(message), wParam, lParam); #endif // __WXDEBUG__ @@ -1714,6 +1796,12 @@ LRESULT APIENTRY _EXPORT wxWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARA // it with wxWindow stored in wxWndHook if ( !wnd && wxWndHook ) { +#if 0 // def __WXDEBUG__ + char buf[512]; + ::GetClassNameA((HWND) hWnd, buf, 512); + wxString className(buf); +#endif + wxAssociateWinWithHandle(hWnd, wxWndHook); wnd = wxWndHook; wxWndHook = NULL; @@ -1777,7 +1865,7 @@ long wxWindow::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam) break; case WM_MOVE: - processed = HandleMove(LOWORD(lParam), HIWORD(lParam)); + processed = HandleMove(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); break; case WM_SIZE: @@ -1818,7 +1906,18 @@ long wxWindow::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam) break; case WM_MOUSEMOVE: + processed = HandleMouseMove(GET_X_LPARAM(lParam), + GET_Y_LPARAM(lParam), + wParam); + break; + case WM_LBUTTONDOWN: + // set focus to this window + if (AcceptsFocus()) + SetFocus(); + + // fall through + case WM_LBUTTONUP: case WM_LBUTTONDBLCLK: case WM_RBUTTONDOWN: @@ -1827,12 +1926,10 @@ long wxWindow::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam) case WM_MBUTTONDOWN: case WM_MBUTTONUP: case WM_MBUTTONDBLCLK: - { - short x = LOWORD(lParam); - short y = HIWORD(lParam); - - processed = HandleMouseEvent(message, x, y, wParam); - } + processed = HandleMouseEvent(message, + GET_X_LPARAM(lParam), + GET_Y_LPARAM(lParam), + wParam); break; case MM_JOY1MOVE: @@ -1843,12 +1940,10 @@ long wxWindow::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam) case MM_JOY2BUTTONDOWN: case MM_JOY1BUTTONUP: case MM_JOY2BUTTONUP: - { - int x = LOWORD(lParam); - int y = HIWORD(lParam); - - processed = HandleJoystickEvent(message, x, y, wParam); - } + processed = HandleJoystickEvent(message, + GET_X_LPARAM(lParam), + GET_Y_LPARAM(lParam), + wParam); break; case WM_SYSCOMMAND: @@ -1901,6 +1996,7 @@ long wxWindow::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam) //else: get the dlg code from the DefWindowProc() break; + case WM_SYSKEYDOWN: case WM_KEYDOWN: // If this has been processed by an event handler, // return 0 now (we've handled it). @@ -1928,6 +2024,8 @@ long wxWindow::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam) 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 @@ -1940,20 +2038,11 @@ long wxWindow::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam) // click because both usually pop up a context menu case VK_APPS: { - // construct the key mask - WPARAM fwKeys = MK_RBUTTON; - if ( (::GetKeyState(VK_CONTROL) & 0x100) != 0 ) - fwKeys |= MK_CONTROL; - if ( (::GetKeyState(VK_SHIFT) & 0x100) != 0 ) - fwKeys |= MK_SHIFT; - - // simulate right mouse button click - DWORD dwPos = ::GetMessagePos(); - int x = GET_X_LPARAM(dwPos), - y = GET_Y_LPARAM(dwPos); - - ScreenToClient(&x, &y); - processed = HandleMouseEvent(WM_RBUTTONDOWN, x, y, fwKeys); + WPARAM flags; + int x, y; + + TranslateKbdEventToMouse(this, &x, &y, &flags); + processed = HandleMouseEvent(WM_RBUTTONDOWN, x, y, flags); } break; #endif // VK_APPS @@ -1967,10 +2056,26 @@ long wxWindow::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam) } break; + case WM_SYSKEYUP: case WM_KEYUP: - processed = HandleKeyUp((WORD) wParam, lParam); +#ifdef VK_APPS + // special case of VK_APPS: treat it the same as right mouse button + if ( wParam == VK_APPS ) + { + WPARAM flags; + int x, y; + + TranslateKbdEventToMouse(this, &x, &y, &flags); + processed = HandleMouseEvent(WM_RBUTTONUP, x, y, flags); + } + else +#endif // VK_APPS + { + processed = HandleKeyUp((WORD) wParam, lParam); + } break; + case WM_SYSCHAR: case WM_CHAR: // Always an ASCII character processed = HandleChar((WORD)wParam, lParam, TRUE); break; @@ -2078,12 +2183,42 @@ long wxWindow::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam) rc.result = TRUE; } break; +#ifdef __WIN32__ + case WM_HELP: + { + HELPINFO* info = (HELPINFO*) lParam; + // Don't yet process menu help events, just windows + if (info->iContextType == HELPINFO_WINDOW) + { + wxWindow* subjectOfHelp = this; + bool eventProcessed = FALSE; + while (subjectOfHelp && !eventProcessed) + { + wxHelpEvent helpEvent(wxEVT_HELP, subjectOfHelp->GetId(), wxPoint(info->MousePos.x, info->MousePos.y) ) ; // info->iCtrlId); + helpEvent.SetEventObject(this); + eventProcessed = GetEventHandler()->ProcessEvent(helpEvent); + + // Go up the window hierarchy until the event is handled (or not) + subjectOfHelp = subjectOfHelp->GetParent(); + } + processed = eventProcessed; + } + else if (info->iContextType == HELPINFO_MENUITEM) + { + wxHelpEvent helpEvent(wxEVT_HELP, info->iCtrlId) ; + helpEvent.SetEventObject(this); + processed = GetEventHandler()->ProcessEvent(helpEvent); + } + else processed = FALSE; + break; + } +#endif } if ( !processed ) { #ifdef __WXDEBUG__ - wxLogTrace(wxTraceMessages, _T("Forwarding %s to DefWindowProc."), + wxLogTrace(wxTraceMessages, wxT("Forwarding %s to DefWindowProc."), wxGetMessageName(message)); #endif // __WXDEBUG__ rc.result = MSWDefWindowProc(message, wParam, lParam); @@ -2119,19 +2254,44 @@ wxWindow *wxFindWinFromHandle(WXHWND hWnd) return (wxWindow *)node->Data(); } +#if 0 // def __WXDEBUG__ +static int gs_AssociationCount = 0; +#endif + void wxAssociateWinWithHandle(HWND hWnd, wxWindow *win) { // adding NULL hWnd is (first) surely a result of an error and // (secondly) breaks menu command processing wxCHECK_RET( hWnd != (HWND)NULL, - _T("attempt to add a NULL hWnd to window list ignored") ); + wxT("attempt to add a NULL hWnd to window list ignored") ); + + + wxWindow *oldWin = wxFindWinFromHandle((WXHWND) hWnd); + if ( oldWin && (oldWin != win) ) + { + wxString str(win->GetClassInfo()->GetClassName()); + wxLogError(wxT("Bug! Found existing HWND %X for new window of class %s"), (int) hWnd, (const wxChar*) str); + } + else if (!oldWin) + { +#if 0 // def __WXDEBUG__ + gs_AssociationCount ++; + wxLogDebug("+ Association %d", gs_AssociationCount); +#endif - if ( !wxWinHandleList->Find((long)hWnd) ) wxWinHandleList->Append((long)hWnd, win); + } } void wxRemoveHandleAssociation(wxWindow *win) { +#if 0 // def __WXDEBUG__ + if (wxWinHandleList->Member(win)) + { + wxLogDebug("- Association %d", gs_AssociationCount); + gs_AssociationCount --; + } +#endif wxWinHandleList->DeleteObject(win); } @@ -2145,24 +2305,25 @@ void wxWindow::MSWDetachWindowMenu() { if ( m_hMenu ) { + wxChar buf[1024]; HMENU hMenu = (HMENU)m_hMenu; int N = ::GetMenuItemCount(hMenu); - int i; - for (i = 0; i < N; i++) + for ( int i = 0; i < N; i++ ) { - wxChar buf[100]; - int chars = GetMenuString(hMenu, i, buf, 100, MF_BYPOSITION); - if ( !chars ) + if ( !::GetMenuString(hMenu, i, buf, WXSIZEOF(buf), MF_BYPOSITION) ) { - wxLogLastError(_T("GetMenuString")); + wxLogLastError(wxT("GetMenuString")); continue; } - if ( wxStrcmp(buf, _T("&Window")) == 0 ) + if ( wxStrcmp(buf, _("&Window")) == 0 ) { - RemoveMenu(hMenu, i, MF_BYPOSITION); + if ( !::RemoveMenu(hMenu, i, MF_BYPOSITION) ) + { + wxLogLastError(wxT("RemoveMenu")); + } break; } @@ -2199,23 +2360,45 @@ bool wxWindow::MSWCreate(int id, height1 = parent_rect.bottom - parent_rect.top; } - if ( x > -1 ) x1 = x; - if ( y > -1 ) y1 = y; - if ( width > -1 ) width1 = width; - if ( height > -1 ) height1 = height; + if ( x != -1 ) + x1 = x; + if ( y != -1 ) + y1 = y; + if ( width != -1 ) + width1 = width; + if ( height != -1 ) + height1 = height; + + // unfortunately, setting WS_EX_CONTROLPARENT only for some windows in the + // hierarchy with several embedded panels (and not all of them) causes the + // program to hang during the next call to IsDialogMessage() due to the bug + // in this function (at least in Windows NT 4.0, it seems to work ok in + // Win2K) +#if 0 + // if we have wxTAB_TRAVERSAL style, we want WS_EX_CONTROLPARENT or + // IsDialogMessage() won't work for us + if ( GetWindowStyleFlag() & wxTAB_TRAVERSAL ) + { + extendedStyle |= WS_EX_CONTROLPARENT; + } +#endif // 0 -#ifdef __WXWINE__ - HWND hParent = (HWND)NULL; -#else - HWND hParent = NULL; -#endif - if ( parent ) - hParent = (HWND) parent->GetHWND(); + HWND hParent = parent ? GetHwndOf(parent) : NULL; wxWndHook = this; if ( dialog_template ) { + // for the dialogs without wxDIALOG_NO_PARENT style, use the top level + // app window as parent - this avoids creating modal dialogs without + // parent + if ( !hParent && !(GetWindowStyleFlag() & wxDIALOG_NO_PARENT) ) + { + wxWindow *winTop = wxTheApp->GetTopWindow(); + if ( winTop ) + hParent = GetHwndOf(winTop); + } + m_hWnd = (WXHWND)::CreateDialog(wxGetInstance(), dialog_template, hParent, @@ -2223,12 +2406,44 @@ bool wxWindow::MSWCreate(int id, if ( m_hWnd == 0 ) { - wxLogError(_("Can't find dummy dialog template!\n" - "Check resource include path for finding wx.rc.")); + wxLogError(_("Can't find dialog template '%s'!\nCheck resource include path for finding wx.rc."), + dialog_template); return FALSE; } + if ( extendedStyle != 0 ) + { + ::SetWindowLong(GetHwnd(), GWL_EXSTYLE, extendedStyle); + ::SetWindowPos(GetHwnd(), NULL, 0, 0, 0, 0, + SWP_NOSIZE | + SWP_NOMOVE | + SWP_NOZORDER | + SWP_NOACTIVATE); + } + +#if defined(__WIN95__) + // For some reason, the system menu is activated when we use the + // WS_EX_CONTEXTHELP style, so let's set a reasonable icon + if (extendedStyle & WS_EX_CONTEXTHELP) + { + wxFrame *winTop = wxDynamicCast(wxTheApp->GetTopWindow(), wxFrame); + if ( winTop ) + { + wxIcon icon = winTop->GetIcon(); + if ( icon.Ok() ) + { + ::SendMessage(GetHwnd(), WM_SETICON, + (WPARAM)TRUE, + (LPARAM)GetHiconOf(icon)); + } + } + } +#endif // __WIN95__ + + + // JACS: is the following still necessary? The above seems to work. + // ::SetWindowLong(GWL_EXSTYLE) doesn't work for the dialogs, so try // to take care of (at least some) extended style flags ourselves if ( extendedStyle & WS_EX_TOPMOST ) @@ -2236,25 +2451,35 @@ bool wxWindow::MSWCreate(int id, if ( !::SetWindowPos(GetHwnd(), HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE) ) { - wxLogLastError(_T("SetWindowPos")); + wxLogLastError(wxT("SetWindowPos")); } } // move the dialog to its initial position without forcing repainting if ( !::MoveWindow(GetHwnd(), x1, y1, width1, height1, FALSE) ) { - wxLogLastError(_T("MoveWindow")); + wxLogLastError(wxT("MoveWindow")); } } - else + else // creating a normal window, not a dialog { int controlId = 0; if ( style & WS_CHILD ) + { controlId = id; + // all child windows should clip their siblings + // style |= /* WS_CLIPSIBLINGS */ ; + } + + wxString className(wclass); + if ( GetWindowStyleFlag() & wxNO_FULL_REPAINT_ON_RESIZE ) + { + className += wxT("NR"); + } m_hWnd = (WXHWND)CreateWindowEx(extendedStyle, - wclass, - title ? title : _T(""), + className, + title ? title : wxT(""), style, x1, y1, width1, height1, @@ -2264,8 +2489,7 @@ bool wxWindow::MSWCreate(int id, if ( !m_hWnd ) { - wxLogError(_("Can't create window of class %s!\n" - "Possible Windows 3.x compatibility problem?"), + wxLogError(_("Can't create window of class %s!\nPossible Windows 3.x compatibility problem?"), wclass); return FALSE; @@ -2273,7 +2497,20 @@ bool wxWindow::MSWCreate(int id, } wxWndHook = NULL; - wxWinHandleList->Append((long)m_hWnd, this); + +#ifdef __WXDEBUG__ + wxNode* node = wxWinHandleList->Member(this); + if (node) + { + HWND hWnd = (HWND) node->GetKeyInteger(); + if (hWnd != (HWND) m_hWnd) + { + wxLogError(wxT("A second HWND association is being added for the same window!")); + } + } +#endif // Debug + + wxAssociateWinWithHandle((HWND) m_hWnd, this); return TRUE; } @@ -2308,8 +2545,6 @@ bool wxWindow::HandleNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) if ( child->MSWOnNotify(idCtrl, lParam, result) ) { return TRUE; - - break; } node = node->GetNext(); @@ -2325,7 +2560,7 @@ bool wxWindow::MSWOnNotify(int WXUNUSED(idCtrl), { #if wxUSE_TOOLTIPS NMHDR* hdr = (NMHDR *)lParam; - if ( hdr->code == TTN_NEEDTEXT && m_tooltip ) + if ( (int)hdr->code == TTN_NEEDTEXT && m_tooltip ) { TOOLTIPTEXT *ttt = (TOOLTIPTEXT *)lParam; ttt->lpszText = (wxChar *)m_tooltip->GetTip().c_str(); @@ -2348,7 +2583,7 @@ bool wxWindow::HandleQueryEndSession(long logOff, bool *mayEnd) wxCloseEvent event(wxEVT_QUERY_END_SESSION, -1); event.SetEventObject(wxTheApp); event.SetCanVeto(TRUE); - event.SetLoggingOff(logOff == ENDSESSION_LOGOFF); + event.SetLoggingOff(logOff == (long)ENDSESSION_LOGOFF); bool rc = wxTheApp->ProcessEvent(event); @@ -2371,7 +2606,7 @@ bool wxWindow::HandleEndSession(bool endSession, long logOff) wxCloseEvent event(wxEVT_END_SESSION, -1); event.SetEventObject(wxTheApp); event.SetCanVeto(FALSE); - event.SetLoggingOff( (logOff == ENDSESSION_LOGOFF) ); + event.SetLoggingOff( (logOff == (long)ENDSESSION_LOGOFF) ); if ( (this == wxTheApp->GetTopWindow()) && // Only send once wxTheApp->ProcessEvent(event)) { @@ -2418,6 +2653,33 @@ bool wxWindow::HandleDestroy() // activation/focus // --------------------------------------------------------------------------- +void wxWindow::OnSetFocus(wxFocusEvent& event) +{ + // panel wants to track the window which was the last to have focus in it, + // so we want to set ourselves as the window which last had focus + // + // notice that it's also important to do it upwards the tree becaus + // otherwise when the top level panel gets focus, it won't set it back to + // us, but to some other sibling + wxWindow *win = this; + while ( win ) + { + wxWindow *parent = win->GetParent(); + wxPanel *panel = wxDynamicCast(parent, wxPanel); + if ( panel ) + { + panel->SetLastFocus(win); + } + + win = parent; + } + + wxLogTrace(_T("focus"), _T("%s (0x%08x) gets focus"), + GetClassInfo()->GetClassName(), GetHandle()); + + event.Skip(); +} + bool wxWindow::HandleActivate(int state, bool WXUNUSED(minimized), WXHWND WXUNUSED(activate)) @@ -2440,13 +2702,6 @@ bool wxWindow::HandleSetFocus(WXHWND WXUNUSED(hwnd)) } #endif // wxUSE_CARET - // panel wants to track the window which was the last to have focus in it - wxPanel *panel = wxDynamicCast(GetParent(), wxPanel); - if ( panel ) - { - panel->SetLastFocus(this); - } - wxFocusEvent event(wxEVT_SET_FOCUS, m_windowId); event.SetEventObject(this); @@ -2496,10 +2751,13 @@ bool wxWindow::HandleDropFiles(WXWPARAM wParam) DragQueryPoint(hFilesInfo, (LPPOINT) &dropPoint); // Get the total number of files dropped - WORD gwFilesDropped = (WORD)DragQueryFile ((HDROP)hFilesInfo, - (UINT)-1, - (LPSTR)0, - (UINT)0); + WORD gwFilesDropped = (WORD)::DragQueryFile + ( + (HDROP)hFilesInfo, + (UINT)-1, + (LPTSTR)0, + (UINT)0 + ); wxString *files = new wxString[gwFilesDropped]; int wIndex; @@ -2525,53 +2783,84 @@ bool wxWindow::HandleSetCursor(WXHWND hWnd, short nHitTest, int WXUNUSED(mouseMsg)) { - // don't set cursor for other windows, only for this one: this prevents - // children of this window from getting the same cursor as the parent has - // (don't forget that this message is propagated by default up the window - // parent-child hierarchy) - if ( GetHWND() == hWnd ) + // the logic is as follows: + // -1. don't set cursor for non client area, including but not limited to + // the title bar, scrollbars, &c + // 0. allow the user to override default behaviour by using EVT_SET_CURSOR + // 1. if we have the cursor set it unless wxIsBusy() + // 2. if we're a top level window, set some cursor anyhow + // 3. if wxIsBusy(), set the busy cursor, otherwise the global one + + if ( nHitTest != HTCLIENT ) + { + return FALSE; + } + + HCURSOR hcursor = 0; + + // first ask the user code - it may wish to set the cursor in some very + // specific way (for example, depending on the current position) + POINT pt; +#ifdef __WIN32__ + if ( !::GetCursorPos(&pt) ) { - // don't set cursor when the mouse is not in the client part - if ( nHitTest == HTCLIENT || nHitTest == HTERROR ) + wxLogLastError(wxT("GetCursorPos")); + } +#else + // In WIN16 it doesn't return a value. + ::GetCursorPos(&pt); +#endif + + int x = pt.x, + y = pt.y; + ScreenToClient(&x, &y); + wxSetCursorEvent event(x, y); + + bool processedEvtSetCursor = GetEventHandler()->ProcessEvent(event); + if ( processedEvtSetCursor && event.HasCursor() ) + { + hcursor = GetHcursorOf(event.GetCursor()); + } + + if ( !hcursor ) + { + bool isBusy = wxIsBusy(); + + // the test for processedEvtSetCursor is here to prevent using m_cursor + // if the user code caught EVT_SET_CURSOR() and returned nothing from + // it - this is a way to say that our cursor shouldn't be used for this + // point + if ( !processedEvtSetCursor && m_cursor.Ok() ) { - HCURSOR hcursor = 0; - if ( wxIsBusy() ) - { - // from msw\utils.cpp - extern HCURSOR gs_wxBusyCursor; + hcursor = GetHcursorOf(m_cursor); + } - hcursor = gs_wxBusyCursor; + if ( !GetParent() ) + { + if ( isBusy ) + { + hcursor = wxGetCurrentBusyCursor(); } - else + else if ( !hcursor ) { - wxCursor *cursor = NULL; - - if ( m_cursor.Ok() ) + const wxCursor *cursor = wxGetGlobalCursor(); + if ( cursor && cursor->Ok() ) { - cursor = &m_cursor; + hcursor = GetHcursorOf(*cursor); } - else - { - // from msw\data.cpp - extern wxCursor *g_globalCursor; - - if ( g_globalCursor && g_globalCursor->Ok() ) - cursor = g_globalCursor; - } - - if ( cursor ) - hcursor = (HCURSOR)cursor->GetHCURSOR(); } + } + } - if ( hcursor ) - { - ::SetCursor(hcursor); + if ( hcursor ) + { + ::SetCursor(hcursor); - return TRUE; - } - } + // cursor set, stop here + return TRUE; } + // pass up the window chain return FALSE; } @@ -2610,10 +2899,9 @@ bool wxWindow::MSWOnDrawItem(int id, WXDRAWITEMSTRUCT *itemStruct) { return ((wxControl *)item)->MSWOnDraw(itemStruct); } - else -#endif - return FALSE; +#endif // USE_OWNER_DRAWN + return FALSE; } bool wxWindow::MSWOnMeasureItem(int id, WXMEASUREITEMSTRUCT *itemStruct) @@ -2735,9 +3023,9 @@ bool wxWindow::HandlePaint() #ifdef __WIN32__ HRGN hRegion = ::CreateRectRgn(0, 0, 0, 0); // Dummy call to get a handle if ( !hRegion ) - wxLogLastError("CreateRectRgn"); + wxLogLastError(wxT("CreateRectRgn")); if ( ::GetUpdateRgn(GetHwnd(), hRegion, FALSE) == ERROR ) - wxLogLastError("GetUpdateRgn"); + wxLogLastError(wxT("GetUpdateRgn")); m_updateRegion = wxRegion((WXHRGN) hRegion); #else @@ -2755,6 +3043,16 @@ bool wxWindow::HandlePaint() return GetEventHandler()->ProcessEvent(event); } +// Can be called from an application's OnPaint handler +void wxWindow::OnPaint(wxPaintEvent& event) +{ + HDC hDC = (HDC) wxPaintDC::FindDCInCache((wxWindow*) event.GetEventObject()); + if (hDC != 0) + { + MSWDefWindowProc(WM_PAINT, (WPARAM) hDC, 0); + } +} + bool wxWindow::HandleEraseBkgnd(WXHDC hdc) { // Prevents flicker when dragging @@ -2788,7 +3086,7 @@ void wxWindow::OnEraseBackground(wxEraseEvent& event) m_backgroundColour.Blue()); HBRUSH hBrush = ::CreateSolidBrush(ref); if ( !hBrush ) - wxLogLastError("CreateSolidBrush"); + wxLogLastError(wxT("CreateSolidBrush")); HDC hdc = (HDC)event.GetDC()->GetHDC(); @@ -2868,6 +3166,25 @@ bool wxWindow::HandleGetMinMaxInfo(void *mmInfo) return rc; } +// generate an artificial resize event +/* FUNCTION IS NOW A MEMBER OF wxFrame - gt +void wxWindow::SendSizeEvent() +{ + RECT r; +#ifdef __WIN16__ + ::GetWindowRect(GetHwnd(), &r); +#else + if ( !::GetWindowRect(GetHwnd(), &r) ) + { + wxLogLastError(_T("GetWindowRect")); + } +#endif + + (void)::PostMessage(GetHwnd(), WM_SIZE, SIZE_RESTORED, + MAKELPARAM(r.right - r.left, r.bottom - r.top)); +} +*/ + // --------------------------------------------------------------------------- // command messages // --------------------------------------------------------------------------- @@ -2882,14 +3199,52 @@ bool wxWindow::HandleCommand(WXWORD id, WXWORD cmd, WXHWND control) return popupMenu->MSWCommand(cmd, id); } - wxWindow *win = FindItem(id); - if ( !win ) + wxWindow *win = (wxWindow*) NULL; + if ( cmd == 0 || cmd == 1 ) // menu or accel - use id + { + // must cast to a signed type before comparing with other ids! + win = FindItem((signed short)id); + } + + if (!win && control) { + // find it from HWND - this works even with the broken programs using + // the same ids for different controls win = wxFindWinFromHandle(control); } if ( win ) + { return win->MSWCommand(cmd, id); + } + + // the messages sent from the in-place edit control used by the treectrl + // for label editing have id == 0, but they should _not_ be treated as menu + // messages (they are EN_XXX ones, in fact) so don't translate anything + // coming from a control to wxEVT_COMMAND_MENU_SELECTED + if ( !control ) + { + // If no child window, it may be an accelerator, e.g. for a popup menu + // command + + wxCommandEvent event(wxEVT_COMMAND_MENU_SELECTED); + event.SetEventObject(this); + event.SetId(id); + event.SetInt(id); + + return GetEventHandler()->ProcessEvent(event); + } +#if wxUSE_SPINCTRL + else + { + // the text ctrl which is logically part of wxSpinCtrl sends WM_COMMAND + // notifications to its parent which we want to reflect back to + // wxSpinCtrl + wxSpinCtrl *spin = wxSpinCtrl::GetSpinForTextCtrl(control); + if ( spin && spin->ProcessTextCommand(cmd, id) ) + return TRUE; + } +#endif // wxUSE_SPINCTRL return FALSE; } @@ -2922,6 +3277,7 @@ void wxWindow::InitMouseEvent(wxMouseEvent& event, int x, int y, WXUINT flags) event.m_leftDown = ((flags & MK_LBUTTON) != 0); event.m_middleDown = ((flags & MK_MBUTTON) != 0); event.m_rightDown = ((flags & MK_RBUTTON) != 0); + event.m_altDown = (::GetKeyState(VK_MENU) & 0x80000000) != 0; event.SetTimestamp(s_currentMsg.time); event.m_eventObject = this; @@ -2994,12 +3350,43 @@ bool wxWindow::HandleMouseMove(int x, int y, WXUINT flags) // keyboard handling // --------------------------------------------------------------------------- +// create the key event of the given type for the given key - used by +// HandleChar and HandleKeyDown/Up +wxKeyEvent wxWindow::CreateKeyEvent(wxEventType evType, + int id, + WXLPARAM lParam) const +{ + wxKeyEvent event(evType); + event.SetId(GetId()); + event.m_shiftDown = wxIsShiftDown(); + event.m_controlDown = wxIsCtrlDown(); + event.m_altDown = (HIWORD(lParam) & KF_ALTDOWN) == KF_ALTDOWN; + + event.m_eventObject = (wxWindow *)this; // const_cast + event.m_keyCode = id; + event.SetTimestamp(s_currentMsg.time); + + // translate the position to client coords + POINT pt; + GetCursorPos(&pt); + RECT rect; + GetWindowRect(GetHwnd(),&rect); + pt.x -= rect.left; + pt.y -= rect.top; + + event.m_x = pt.x; + event.m_y = pt.y; + + return event; +} + // isASCII is TRUE only when we're called from WM_CHAR handler and not from // WM_KEYDOWN one bool wxWindow::HandleChar(WXWORD wParam, WXLPARAM lParam, bool isASCII) { + bool ctrlDown = FALSE; + int id; - bool tempControlDown = FALSE; if ( isASCII ) { // If 1 -> 26, translate to CTRL plus a letter. @@ -3008,142 +3395,86 @@ bool wxWindow::HandleChar(WXWORD wParam, WXLPARAM lParam, bool isASCII) { switch (id) { - case 13: - { + case 13: id = WXK_RETURN; break; - } - case 8: - { + + case 8: id = WXK_BACK; break; - } - case 9: - { + + case 9: id = WXK_TAB; break; - } - default: - { - tempControlDown = TRUE; + + default: + ctrlDown = TRUE; id = id + 96; - } } } } - else if ( (id = wxCharCodeMSWToWX(wParam)) == 0 ) { + 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) + // WM_CHAR (i.e. when isASCII = TRUE), don't process it now id = -1; } if ( id != -1 ) { - wxKeyEvent event(wxEVT_CHAR); - event.m_shiftDown = (::GetKeyState(VK_SHIFT)&0x100?TRUE:FALSE); - event.m_controlDown = (::GetKeyState(VK_CONTROL)&0x100?TRUE:FALSE); - if ( (HIWORD(lParam) & KF_ALTDOWN) == KF_ALTDOWN ) - event.m_altDown = TRUE; - - event.m_eventObject = this; - event.m_keyCode = id; - event.SetTimestamp(s_currentMsg.time); - - POINT pt; - GetCursorPos(&pt); - RECT rect; - GetWindowRect(GetHwnd(),&rect); - pt.x -= rect.left; - pt.y -= rect.top; - - event.m_x = pt.x; event.m_y = pt.y; + wxKeyEvent event(CreateKeyEvent(wxEVT_CHAR, id, lParam)); + if ( ctrlDown ) + { + event.m_controlDown = TRUE; + } if ( GetEventHandler()->ProcessEvent(event) ) return TRUE; - else - return FALSE; } - else - return FALSE; + + return FALSE; } bool wxWindow::HandleKeyDown(WXWORD wParam, WXLPARAM lParam) { - int id; + int id = wxCharCodeMSWToWX(wParam); - if ( (id = wxCharCodeMSWToWX(wParam)) == 0 ) { + if ( !id ) + { + // normal ASCII char id = wParam; } - if ( id != -1 ) + if ( id != -1 ) // VZ: does this ever happen (FIXME)? { - wxKeyEvent event(wxEVT_KEY_DOWN); - event.m_shiftDown = (::GetKeyState(VK_SHIFT)&0x100?TRUE:FALSE); - event.m_controlDown = (::GetKeyState(VK_CONTROL)&0x100?TRUE:FALSE); - if ( (HIWORD(lParam) & KF_ALTDOWN) == KF_ALTDOWN ) - event.m_altDown = TRUE; - - event.m_eventObject = this; - event.m_keyCode = id; - event.SetTimestamp(s_currentMsg.time); - - POINT pt; - GetCursorPos(&pt); - RECT rect; - GetWindowRect(GetHwnd(),&rect); - pt.x -= rect.left; - pt.y -= rect.top; - - event.m_x = pt.x; event.m_y = pt.y; - + wxKeyEvent event(CreateKeyEvent(wxEVT_KEY_DOWN, id, lParam)); if ( GetEventHandler()->ProcessEvent(event) ) { return TRUE; } - else return FALSE; - } - else - { - return FALSE; } + + return FALSE; } bool wxWindow::HandleKeyUp(WXWORD wParam, WXLPARAM lParam) { - int id; + int id = wxCharCodeMSWToWX(wParam); - if ( (id = wxCharCodeMSWToWX(wParam)) == 0 ) { + if ( !id ) + { + // normal ASCII char id = wParam; } - if ( id != -1 ) + if ( id != -1 ) // VZ: does this ever happen (FIXME)? { - wxKeyEvent event(wxEVT_KEY_UP); - event.m_shiftDown = (::GetKeyState(VK_SHIFT)&0x100?TRUE:FALSE); - event.m_controlDown = (::GetKeyState(VK_CONTROL)&0x100?TRUE:FALSE); - if ( (HIWORD(lParam) & KF_ALTDOWN) == KF_ALTDOWN ) - event.m_altDown = TRUE; - - event.m_eventObject = this; - event.m_keyCode = id; - event.SetTimestamp(s_currentMsg.time); - - POINT pt; - GetCursorPos(&pt); - RECT rect; - GetWindowRect(GetHwnd(),&rect); - pt.x -= rect.left; - pt.y -= rect.top; - - event.m_x = pt.x; event.m_y = pt.y; - + wxKeyEvent event(CreateKeyEvent(wxEVT_KEY_UP, id, lParam)); if ( GetEventHandler()->ProcessEvent(event) ) return TRUE; - else - return FALSE; } - else - return FALSE; + + return FALSE; } // --------------------------------------------------------------------------- @@ -3218,7 +3549,7 @@ bool wxWindow::HandleJoystickEvent(WXUINT msg, int x, int y, WXUINT flags) break; default: - wxFAIL_MSG(_T("no such joystick event")); + wxFAIL_MSG(wxT("no such joystick event")); return FALSE; } @@ -3275,9 +3606,35 @@ bool wxWindow::MSWOnScroll(int orientation, WXWORD wParam, event.m_eventType = wxEVT_SCROLLWIN_PAGEDOWN; break; - case SB_THUMBTRACK: case SB_THUMBPOSITION: - event.m_eventType = wxEVT_SCROLLWIN_THUMBTRACK; + case SB_THUMBTRACK: +#ifdef __WIN32__ + // under Win32, the scrollbar range and position are 32 bit integers, + // but WM_[HV]SCROLL only carry the low 16 bits of them, so we must + // explicitly query the scrollbar for the correct position (this must + // be done only for these two SB_ events as they are the only one + // carrying the scrollbar position) + { + SCROLLINFO scrollInfo; + wxZeroMemory(scrollInfo); + scrollInfo.cbSize = sizeof(SCROLLINFO); + scrollInfo.fMask = SIF_TRACKPOS; + + if ( !::GetScrollInfo(GetHwnd(), + orientation == wxHORIZONTAL ? SB_HORZ + : SB_VERT, + &scrollInfo) ) + { + wxLogLastError(_T("GetScrollInfo")); + } + + event.SetPosition(scrollInfo.nTrackPos); + } +#endif // Win32 + + event.m_eventType = wParam == SB_THUMBPOSITION + ? wxEVT_SCROLLWIN_THUMBRELEASE + : wxEVT_SCROLLWIN_THUMBTRACK; break; default: @@ -3291,7 +3648,7 @@ bool wxWindow::MSWOnScroll(int orientation, WXWORD wParam, // global functions // =========================================================================== -void wxGetCharSize(WXHWND wnd, int *x, int *y,wxFont *the_font) +void wxGetCharSize(WXHWND wnd, int *x, int *y, const wxFont *the_font) { TEXTMETRIC tm; HDC dc = ::GetDC((HWND) wnd); @@ -3301,7 +3658,7 @@ void wxGetCharSize(WXHWND wnd, int *x, int *y,wxFont *the_font) { // the_font->UseResource(); // the_font->RealizeResource(); - fnt = (HFONT)the_font->GetResourceHandle(); + fnt = (HFONT)((wxFont *)the_font)->GetResourceHandle(); // const_cast if ( fnt ) was = (HFONT) SelectObject(dc,fnt); } @@ -3311,8 +3668,11 @@ void wxGetCharSize(WXHWND wnd, int *x, int *y,wxFont *the_font) SelectObject(dc,was); } ReleaseDC((HWND)wnd, dc); - *x = tm.tmAveCharWidth; - *y = tm.tmHeight + tm.tmExternalLeading; + + if ( x ) + *x = tm.tmAveCharWidth; + if ( y ) + *y = tm.tmHeight + tm.tmExternalLeading; // if ( the_font ) // the_font->ReleaseResource(); @@ -3322,80 +3682,82 @@ void wxGetCharSize(WXHWND wnd, int *x, int *y,wxFont *the_font) // the key should be ignored by WM_KEYDOWN and processed by WM_CHAR instead. int wxCharCodeMSWToWX(int keySym) { - int id = 0; + int id; switch (keySym) { - case VK_CANCEL: id = WXK_CANCEL; break; - case VK_BACK: id = WXK_BACK; break; - case VK_TAB: id = WXK_TAB; break; - case VK_CLEAR: id = WXK_CLEAR; break; - case VK_RETURN: id = WXK_RETURN; break; - case VK_SHIFT: id = WXK_SHIFT; break; - case VK_CONTROL: id = WXK_CONTROL; break; - case VK_MENU : id = WXK_MENU; break; - case VK_PAUSE: id = WXK_PAUSE; break; - case VK_SPACE: id = WXK_SPACE; break; - case VK_ESCAPE: id = WXK_ESCAPE; break; - case VK_PRIOR: id = WXK_PRIOR; break; - case VK_NEXT : id = WXK_NEXT; break; - case VK_END: id = WXK_END; break; - case VK_HOME : id = WXK_HOME; break; - case VK_LEFT : id = WXK_LEFT; break; - case VK_UP: id = WXK_UP; break; - case VK_RIGHT: id = WXK_RIGHT; break; - case VK_DOWN : id = WXK_DOWN; break; - case VK_SELECT: id = WXK_SELECT; break; - case VK_PRINT: id = WXK_PRINT; break; - case VK_EXECUTE: id = WXK_EXECUTE; break; - case VK_INSERT: id = WXK_INSERT; break; - case VK_DELETE: id = WXK_DELETE; break; - case VK_HELP : id = WXK_HELP; break; - case VK_NUMPAD0: id = WXK_NUMPAD0; break; - case VK_NUMPAD1: id = WXK_NUMPAD1; break; - case VK_NUMPAD2: id = WXK_NUMPAD2; break; - case VK_NUMPAD3: id = WXK_NUMPAD3; break; - case VK_NUMPAD4: id = WXK_NUMPAD4; break; - case VK_NUMPAD5: id = WXK_NUMPAD5; break; - case VK_NUMPAD6: id = WXK_NUMPAD6; break; - case VK_NUMPAD7: id = WXK_NUMPAD7; break; - case VK_NUMPAD8: id = WXK_NUMPAD8; break; - case VK_NUMPAD9: id = WXK_NUMPAD9; break; - case VK_MULTIPLY: id = WXK_MULTIPLY; break; - case VK_ADD: id = WXK_ADD; break; - case VK_SUBTRACT: id = WXK_SUBTRACT; break; - case VK_DECIMAL: id = WXK_DECIMAL; break; - case VK_DIVIDE: id = WXK_DIVIDE; break; - case VK_F1: id = WXK_F1; break; - case VK_F2: id = WXK_F2; break; - case VK_F3: id = WXK_F3; break; - case VK_F4: id = WXK_F4; break; - case VK_F5: id = WXK_F5; break; - case VK_F6: id = WXK_F6; break; - case VK_F7: id = WXK_F7; break; - case VK_F8: id = WXK_F8; break; - case VK_F9: id = WXK_F9; break; - case VK_F10: id = WXK_F10; break; - case VK_F11: id = WXK_F11; break; - case VK_F12: id = WXK_F12; break; - case VK_F13: id = WXK_F13; break; - case VK_F14: id = WXK_F14; break; - case VK_F15: id = WXK_F15; break; - case VK_F16: id = WXK_F16; break; - case VK_F17: id = WXK_F17; break; - case VK_F18: id = WXK_F18; break; - case VK_F19: id = WXK_F19; break; - case VK_F20: id = WXK_F20; break; - case VK_F21: id = WXK_F21; break; - case VK_F22: id = WXK_F22; break; - case VK_F23: id = WXK_F23; break; - case VK_F24: id = WXK_F24; break; - case VK_NUMLOCK: id = WXK_NUMLOCK; break; - case VK_SCROLL: id = WXK_SCROLL; break; - default: - { - return 0; - } + case VK_CANCEL: id = WXK_CANCEL; break; + case VK_BACK: id = WXK_BACK; break; + case VK_TAB: id = WXK_TAB; break; + case VK_CLEAR: id = WXK_CLEAR; break; + case VK_RETURN: id = WXK_RETURN; break; + case VK_SHIFT: id = WXK_SHIFT; break; + case VK_CONTROL: id = WXK_CONTROL; break; + case VK_MENU : id = WXK_MENU; break; + case VK_PAUSE: id = WXK_PAUSE; break; + case VK_SPACE: id = WXK_SPACE; break; + case VK_ESCAPE: id = WXK_ESCAPE; break; + case VK_PRIOR: id = WXK_PRIOR; break; + case VK_NEXT : id = WXK_NEXT; break; + case VK_END: id = WXK_END; break; + case VK_HOME : id = WXK_HOME; break; + case VK_LEFT : id = WXK_LEFT; break; + case VK_UP: id = WXK_UP; break; + case VK_RIGHT: id = WXK_RIGHT; break; + case VK_DOWN : id = WXK_DOWN; break; + case VK_SELECT: id = WXK_SELECT; break; + case VK_PRINT: id = WXK_PRINT; break; + case VK_EXECUTE: id = WXK_EXECUTE; break; + case VK_INSERT: id = WXK_INSERT; break; + case VK_DELETE: id = WXK_DELETE; break; + case VK_HELP : id = WXK_HELP; break; + case VK_NUMPAD0: id = WXK_NUMPAD0; break; + case VK_NUMPAD1: id = WXK_NUMPAD1; break; + case VK_NUMPAD2: id = WXK_NUMPAD2; break; + case VK_NUMPAD3: id = WXK_NUMPAD3; break; + case VK_NUMPAD4: id = WXK_NUMPAD4; break; + case VK_NUMPAD5: id = WXK_NUMPAD5; break; + case VK_NUMPAD6: id = WXK_NUMPAD6; break; + case VK_NUMPAD7: id = WXK_NUMPAD7; break; + case VK_NUMPAD8: id = WXK_NUMPAD8; break; + case VK_NUMPAD9: id = WXK_NUMPAD9; break; + case VK_MULTIPLY: id = WXK_MULTIPLY; break; + case 0xBB: // VK_OEM_PLUS + case VK_ADD: id = WXK_ADD; break; + case 0xBD: // VK_OEM_MINUS + case VK_SUBTRACT: id = WXK_SUBTRACT; break; + case 0xBE: // VK_OEM_PERIOD + case VK_DECIMAL: id = WXK_DECIMAL; break; + case VK_DIVIDE: id = WXK_DIVIDE; break; + case VK_F1: id = WXK_F1; break; + case VK_F2: id = WXK_F2; break; + case VK_F3: id = WXK_F3; break; + case VK_F4: id = WXK_F4; break; + case VK_F5: id = WXK_F5; break; + case VK_F6: id = WXK_F6; break; + case VK_F7: id = WXK_F7; break; + case VK_F8: id = WXK_F8; break; + case VK_F9: id = WXK_F9; break; + case VK_F10: id = WXK_F10; break; + case VK_F11: id = WXK_F11; break; + case VK_F12: id = WXK_F12; break; + case VK_F13: id = WXK_F13; break; + case VK_F14: id = WXK_F14; break; + case VK_F15: id = WXK_F15; break; + case VK_F16: id = WXK_F16; break; + case VK_F17: id = WXK_F17; break; + case VK_F18: id = WXK_F18; break; + case VK_F19: id = WXK_F19; break; + case VK_F20: id = WXK_F20; break; + case VK_F21: id = WXK_F21; break; + case VK_F22: id = WXK_F22; break; + case VK_F23: id = WXK_F23; break; + case VK_F24: id = WXK_F24; break; + case VK_NUMLOCK: id = WXK_NUMLOCK; break; + case VK_SCROLL: id = WXK_SCROLL; break; + default: + id = 0; } + return id; } @@ -3486,6 +3848,48 @@ wxWindow *wxGetActiveWindow() return NULL; } +extern wxWindow *wxGetWindowFromHWND(WXHWND hWnd) +{ + HWND hwnd = (HWND)hWnd; + + // For a radiobutton, we get the radiobox from GWL_USERDATA (which is set + // by code in msw/radiobox.cpp), for all the others we just search up the + // window hierarchy + wxWindow *win = (wxWindow *)NULL; + if ( hwnd ) + { + win = wxFindWinFromHandle((WXHWND)hwnd); + if ( !win ) + { + // the radiobox pointer is stored in GWL_USERDATA only under Win32 +#ifdef __WIN32__ + // native radiobuttons return DLGC_RADIOBUTTON here and for any + // wxWindow class which overrides WM_GETDLGCODE processing to + // do it as well, win would be already non NULL + if ( ::SendMessage((HWND)hwnd, WM_GETDLGCODE, + 0, 0) & DLGC_RADIOBUTTON ) + { + win = (wxWindow *)::GetWindowLong(hwnd, GWL_USERDATA); + } + else +#endif // Win32 + { + // hwnd is not a wxWindow, try its parent next below + hwnd = ::GetParent(hwnd); + } + } + //else: it's a wxRadioButton, not a radiobutton from wxRadioBox + } + + while ( hwnd && !win ) + { + win = wxFindWinFromHandle((WXHWND)hwnd); + hwnd = ::GetParent(hwnd); + } + + return win; +} + // Windows keyboard hook. Allows interception of e.g. F1, ESCAPE // in active frames and dialogs, regardless of where the focus is. static HHOOK wxTheKeyboardHook = 0; @@ -3499,17 +3903,23 @@ void wxSetKeyboardHook(bool doIt) { wxTheKeyboardHookProc = MakeProcInstance((FARPROC) wxKeyboardHook, wxGetInstance()); wxTheKeyboardHook = SetWindowsHookEx(WH_KEYBOARD, (HOOKPROC) wxTheKeyboardHookProc, wxGetInstance(), + #if defined(__WIN32__) && !defined(__TWIN32__) - GetCurrentThreadId()); + GetCurrentThreadId() // (DWORD)GetCurrentProcess()); // This is another possibility. Which is right? #else - GetCurrentTask()); + GetCurrentTask() #endif + ); } else { UnhookWindowsHookEx(wxTheKeyboardHook); + // avoids mingw warning about statement with no effect (FreeProcInstance + // doesn't do anything under Win32) +#ifndef __GNUC__ FreeProcInstance(wxTheKeyboardHookProc); +#endif } } @@ -3519,8 +3929,8 @@ wxKeyboardHook(int nCode, WORD wParam, DWORD lParam) DWORD hiWord = HIWORD(lParam); if ( nCode != HC_NOREMOVE && ((hiWord & KF_UP) == 0) ) { - int id; - if ( (id = wxCharCodeMSWToWX(wParam)) != 0 ) + int id = wxCharCodeMSWToWX(wParam); + if ( id != 0 ) { wxKeyEvent event(wxEVT_CHAR_HOOK); if ( (HIWORD(lParam) & KF_ALTDOWN) == KF_ALTDOWN ) @@ -3528,25 +3938,31 @@ wxKeyboardHook(int nCode, WORD wParam, DWORD lParam) event.m_eventObject = NULL; event.m_keyCode = id; - /* begin Albert's fix for control and shift key 26.5 */ - event.m_shiftDown = (::GetKeyState(VK_SHIFT)&0x100?TRUE:FALSE); - event.m_controlDown = (::GetKeyState(VK_CONTROL)&0x100?TRUE:FALSE); - /* end Albert's fix for control and shift key 26.5 */ + event.m_shiftDown = wxIsShiftDown(); + event.m_controlDown = wxIsCtrlDown(); event.SetTimestamp(s_currentMsg.time); wxWindow *win = wxGetActiveWindow(); + wxEvtHandler *handler; if ( win ) { - if ( win->GetEventHandler()->ProcessEvent(event) ) - return 1; + handler = win->GetEventHandler(); + event.SetId(win->GetId()); } else { - if ( wxTheApp && wxTheApp->ProcessEvent(event) ) - return 1; + handler = wxTheApp; + event.SetId(-1); + } + + if ( handler && handler->ProcessEvent(event) ) + { + // processed + return 1; } } } + return (int)CallNextHookEx(wxTheKeyboardHook, nCode, wParam, lParam); } @@ -3968,3 +4384,89 @@ const char *wxGetMessageName(int message) } } #endif //__WXDEBUG__ + +static void TranslateKbdEventToMouse(wxWindow *win, int *x, int *y, WPARAM *flags) +{ + // construct the key mask + WPARAM& fwKeys = *flags; + + fwKeys = MK_RBUTTON; + if ( wxIsCtrlDown() ) + fwKeys |= MK_CONTROL; + if ( wxIsShiftDown() ) + fwKeys |= MK_SHIFT; + + // simulate right mouse button click + DWORD dwPos = ::GetMessagePos(); + *x = GET_X_LPARAM(dwPos); + *y = GET_Y_LPARAM(dwPos); + + win->ScreenToClient(x, y); +} + +static TEXTMETRIC wxGetTextMetrics(const wxWindow *win) +{ + // prepare the DC + TEXTMETRIC tm; + HWND hwnd = GetHwndOf(win); + HDC hdc = ::GetDC(hwnd); + +#if !wxDIALOG_UNIT_COMPATIBILITY + // and select the current font into it + HFONT hfont = GetHfontOf(win->GetFont()); + if ( hfont ) + { + hfont = (HFONT)::SelectObject(hdc, hfont); + } +#endif + + // finally retrieve the text metrics from it + GetTextMetrics(hdc, &tm); + +#if !wxDIALOG_UNIT_COMPATIBILITY + // and clean up + if ( hfont ) + { + (void)::SelectObject(hdc, hfont); + } +#endif + + ::ReleaseDC(hwnd, hdc); + + return tm; +} + +// Find the wxWindow at the current mouse position, returning the mouse +// position. +wxWindow* wxFindWindowAtPointer(wxPoint& pt) +{ + return wxFindWindowAtPoint(wxGetMousePosition()); +} + +wxWindow* wxFindWindowAtPoint(const wxPoint& pt) +{ + POINT pt2; + pt2.x = pt.x; + pt2.y = pt.y; + HWND hWndHit = ::WindowFromPoint(pt2); + + wxWindow* win = wxFindWinFromHandle((WXHWND) hWndHit) ; + HWND hWnd = hWndHit; + + // Try to find a window with a wxWindow associated with it + while (!win && (hWnd != 0)) + { + hWnd = ::GetParent(hWnd); + win = wxFindWinFromHandle((WXHWND) hWnd) ; + } + return win; +} + +// Get the current mouse position. +wxPoint wxGetMousePosition() +{ + POINT pt; + GetCursorPos( & pt ); + return wxPoint(pt.x, pt.y); +} +