X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/3723b7b1d8cbeebccef4f8ad7fe3884dfca9eca6..b5435dccd098ae153c3b7b3b8fca8dd0d763986c:/src/msw/window.cpp?ds=sidebyside diff --git a/src/msw/window.cpp b/src/msw/window.cpp index 2680a55bcd..3d22361214 100644 --- a/src/msw/window.cpp +++ b/src/msw/window.cpp @@ -1,12 +1,12 @@ ///////////////////////////////////////////////////////////////////////////// -// Name: windows.cpp +// Name: src/msw/windows.cpp // Purpose: wxWindow // Author: Julian Smart // 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 ///////////////////////////////////////////////////////////////////////////// // =========================================================================== @@ -29,25 +29,24 @@ #endif #ifndef WX_PRECOMP - #include - #include "wx/msw/winundef.h" + #include "wx/msw/wrapwin.h" #include "wx/window.h" #include "wx/accel.h" #include "wx/setup.h" #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/panel.h" #include "wx/layout.h" #include "wx/dialog.h" #include "wx/frame.h" #include "wx/listbox.h" #include "wx/button.h" #include "wx/msgdlg.h" - - #include + #include "wx/settings.h" + #include "wx/statbox.h" #endif #if wxUSE_OWNER_DRAWN @@ -58,6 +57,18 @@ #include "wx/dnd.h" #endif +#if wxUSE_ACCESSIBILITY + #include "wx/access.h" + #include + #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" @@ -71,6 +82,10 @@ #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" @@ -79,7 +94,7 @@ #include -#ifndef __GNUWIN32_OLD__ +#if (!defined(__GNUWIN32_OLD__) && !defined(__WXMICROWIN__) && !defined(__WXWINCE__)) || defined(__CYGWIN10__) #include #include #endif @@ -88,32 +103,77 @@ #include #endif -#if !defined(__GNUWIN32_OLD__) && !defined(__TWIN32__) +#if (!defined(__GNUWIN32_OLD__) && !defined(__WXMICROWIN__) && !defined(__WXWINCE__)) || defined(__CYGWIN10__) #ifdef __WIN95__ #include #endif -#else // broken compiler - #ifndef __TWIN32__ - #include "wx/msw/gnuwin32/extra.h" - #endif +#elif !defined(__WXMICROWIN__) && !defined(__WXWINCE__) // broken compiler + #include "wx/msw/gnuwin32/extra.h" +#endif + +#if defined(__GNUG__) +#include "wx/msw/missing.h" +#endif + +#if defined(__WXWINCE__) +#include "wx/msw/wince/missing.h" #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 #endif +#if wxUSE_MOUSEWHEEL + #ifndef WM_MOUSEWHEEL + #define WM_MOUSEWHEEL 0x020A + #endif + #ifndef WHEEL_DELTA + #define WHEEL_DELTA 120 + #endif + #ifndef SPI_GETWHEELSCROLLLINES + #define SPI_GETWHEELSCROLLLINES 104 + #endif +#endif // wxUSE_MOUSEWHEEL + +#ifndef VK_OEM_1 + #define VK_OEM_1 0xBA + #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 + +#ifndef VK_OEM_COMMA + #define VK_OEM_PLUS 0xBB + #define VK_OEM_COMMA 0xBC + #define VK_OEM_MINUS 0xBD + #define VK_OEM_PERIOD 0xBE +#endif + // --------------------------------------------------------------------------- // global variables // --------------------------------------------------------------------------- -// the last Windows message we got (MT-UNSAFE) +// the last Windows message we got (FIXME-MT) extern MSG s_currentMsg; +#if wxUSE_MENUS_NATIVE wxMenu *wxCurrentPopupMenu = NULL; -extern wxList WXDLLEXPORT wxPendingDelete; +#endif // wxUSE_MENUS_NATIVE + extern const wxChar *wxCanvasClassName; +// true if we had already created the std colour map, used by +// wxGetStdColourMap() and wxWindow::OnSysColourChanged() (FIXME-MT) +static bool gs_hasStdCmap = FALSE; + // --------------------------------------------------------------------------- // private functions // --------------------------------------------------------------------------- @@ -122,33 +182,60 @@ extern const wxChar *wxCanvasClassName; LRESULT WXDLLEXPORT APIENTRY _EXPORT wxWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); + #ifdef __WXDEBUG__ const char *wxGetMessageName(int message); #endif //__WXDEBUG__ -void wxRemoveHandleAssociation(wxWindow *win); -void wxAssociateWinWithHandle(HWND hWnd, wxWindow *win); +void wxRemoveHandleAssociation(wxWindowMSW *win); +extern void wxAssociateWinWithHandle(HWND hWnd, wxWindowMSW *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); +static void TranslateKbdEventToMouse(wxWindowMSW *win, + int *x, int *y, WPARAM *flags); // get the text metrics for the current font -static TEXTMETRIC wxGetTextMetrics(const wxWindow *win); +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) +{ +#ifdef __WXMICROWIN__ + // It seems that MicroWindows brings the _parent_ of the window to the top, + // which can be the wrong one. + + // activate (set focus to) specified window + ::SetFocus(hwnd); +#endif + + // raise top level parent to top of z order + if (!::SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE)) + { + wxLogLastError(_T("SetWindowPos")); + } +} // --------------------------------------------------------------------------- // event tables // --------------------------------------------------------------------------- -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) +// in wxUniv/MSW this class is abstract because it doesn't have DoPopupMenu() +// method +#ifdef __WXUNIVERSAL__ + IMPLEMENT_ABSTRACT_CLASS(wxWindowMSW, wxWindowBase) +#else // __WXMSW__ + IMPLEMENT_DYNAMIC_CLASS(wxWindow, wxWindowBase) +#endif // __WXUNIVERSAL__/__WXMSW__ + +BEGIN_EVENT_TABLE(wxWindowMSW, wxWindowBase) + EVT_ERASE_BACKGROUND(wxWindowMSW::OnEraseBackground) + EVT_SYS_COLOUR_CHANGED(wxWindowMSW::OnSysColourChanged) + EVT_INIT_DIALOG(wxWindowMSW::OnInitDialog) END_EVENT_TABLE() // =========================================================================== @@ -160,20 +247,25 @@ END_EVENT_TABLE() // --------------------------------------------------------------------------- // Find an item given the MS Windows id -wxWindow *wxWindow::FindItem(long id) const +wxWindow *wxWindowMSW::FindItem(long id) const { - wxControl *item = wxDynamicCast(this, wxControl); +#if wxUSE_CONTROLS + wxControl *item = wxDynamicCastThis(wxControl); if ( item ) { - // i it we or one of our "internal" children? - if ( item->GetId() == id || - (item->GetSubcontrols().Index(id) != wxNOT_FOUND) ) + // is it we or one of our "internal" children? + if ( item->GetId() == id +#ifndef __WXUNIVERSAL__ + || (item->GetSubcontrols().Index(id) != wxNOT_FOUND) +#endif // __WXUNIVERSAL__ + ) { return item; } } +#endif // wxUSE_CONTROLS - wxWindowList::Node *current = GetChildren().GetFirst(); + wxWindowList::compatibility_iterator current = GetChildren().GetFirst(); while (current) { wxWindow *childWin = current->GetData(); @@ -189,9 +281,9 @@ wxWindow *wxWindow::FindItem(long id) const } // Find an item given the MS Windows handle -wxWindow *wxWindow::FindItemByHWND(WXHWND hWnd, bool controlOnly) const +wxWindow *wxWindowMSW::FindItemByHWND(WXHWND hWnd, bool controlOnly) const { - wxWindowList::Node *current = GetChildren().GetFirst(); + wxWindowList::compatibility_iterator current = GetChildren().GetFirst(); while (current) { wxWindow *parent = current->GetData(); @@ -201,7 +293,11 @@ wxWindow *wxWindow::FindItemByHWND(WXHWND hWnd, bool controlOnly) const if ( wnd ) return wnd; - if ( !controlOnly || parent->IsKindOf(CLASSINFO(wxControl)) ) + if ( !controlOnly +#if wxUSE_CONTROLS + || parent->IsKindOf(CLASSINFO(wxControl)) +#endif // wxUSE_CONTROLS + ) { wxWindow *item = current->GetData(); if ( item->GetHWND() == hWnd ) @@ -219,7 +315,7 @@ wxWindow *wxWindow::FindItemByHWND(WXHWND hWnd, bool controlOnly) const } // Default command handler -bool wxWindow::MSWCommand(WXUINT WXUNUSED(param), WXWORD WXUNUSED(id)) +bool wxWindowMSW::MSWCommand(WXUINT WXUNUSED(param), WXWORD WXUNUSED(id)) { return FALSE; } @@ -228,31 +324,26 @@ bool wxWindow::MSWCommand(WXUINT WXUNUSED(param), WXWORD WXUNUSED(id)) // constructors and such // ---------------------------------------------------------------------------- -void wxWindow::Init() +void wxWindowMSW::Init() { // generic InitBase(); // MSW specific - m_doubleClickAllowed = 0; - m_winCaptured = FALSE; - m_isBeingDeleted = FALSE; - m_oldWndProc = 0; - m_useCtl3D = FALSE; + m_oldWndProc = NULL; 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; // as all windows are created with WS_VISIBLE style... m_isShown = TRUE; @@ -265,17 +356,34 @@ void wxWindow::Init() } // Destructor -wxWindow::~wxWindow() +wxWindowMSW::~wxWindowMSW() { m_isBeingDeleted = TRUE; - MSWDetachWindowMenu(); +#ifndef __WXUNIVERSAL__ + // VS: make sure there's no wxFrame with last focus set to us: + for ( wxWindow *win = GetParent(); win; win = win->GetParent() ) + { + wxTopLevelWindow *frame = wxDynamicCast(win, wxTopLevelWindow); + if ( frame ) + { + if ( frame->GetLastFocus() == this ) + { + frame->SetLastFocus(NULL); + } + break; + } + } +#endif // __WXUNIVERSAL__ + + // VS: destroy children first and _then_ detach *this from its parent. + // If we'd do it the other way around, children wouldn't be able + // find their parent frame (see above). + DestroyChildren(); if ( m_parent ) m_parent->RemoveChild(this); - DestroyChildren(); - if ( m_hWnd ) { // VZ: test temp removed to understand what really happens here @@ -288,14 +396,17 @@ wxWindow::~wxWindow() // remove hWnd <-> wxWindow association wxRemoveHandleAssociation(this); } + + delete m_childrenDisabled; } // real construction (Init() must have been called before!) -bool wxWindow::Create(wxWindow *parent, wxWindowID id, - const wxPoint& pos, - const wxSize& size, - long style, - const wxString& name) +bool wxWindowMSW::Create(wxWindow *parent, + wxWindowID id, + const wxPoint& pos, + const wxSize& size, + long style, + const wxString& name) { wxCHECK_MSG( parent, FALSE, wxT("can't create wxWindow without parent") ); @@ -304,57 +415,78 @@ bool wxWindow::Create(wxWindow *parent, wxWindowID id, 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); - - // Even with extended styles, need to combine with WS_BORDER - // for them to look right. - if ( want3D || (m_windowStyle & wxSIMPLE_BORDER) || (m_windowStyle & wxRAISED_BORDER ) || - (m_windowStyle & wxSUNKEN_BORDER) || (m_windowStyle & wxDOUBLE_BORDER)) + WXDWORD exstyle; + DWORD msflags = MSWGetCreateWindowFlags(&exstyle); + +#ifdef __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 ) { - msflags |= WS_BORDER; + msflags &= ~WS_VISIBLE; + m_isShown = FALSE; } - - // calculate the value to return from WM_GETDLGCODE handler - if ( GetWindowStyleFlag() & wxWANTS_CHARS ) + else { - // want everything: i.e. all keys and WM_CHAR message - m_lDlgCode = DLGC_WANTARROWS | DLGC_WANTCHARS | - DLGC_WANTTAB | DLGC_WANTMESSAGE; + msflags |= WS_VISIBLE; } - MSWCreate(m_windowId, parent, wxCanvasClassName, this, NULL, - pos.x, pos.y, - WidthDefault(size.x), HeightDefault(size.y), - msflags, NULL, exStyle); - - return TRUE; + return MSWCreate(wxCanvasClassName, NULL, pos, size, msflags, exstyle); } // --------------------------------------------------------------------------- // basic operations // --------------------------------------------------------------------------- -void wxWindow::SetFocus() +void wxWindowMSW::SetFocus() { HWND hWnd = GetHwnd(); - if ( hWnd ) - ::SetFocus(hWnd); + wxCHECK_RET( hWnd, _T("can't set focus to invalid window") ); + +#if !defined(__WXMICROWIN__) && !defined(__WXWINCE__) + ::SetLastError(0); +#endif + + if ( !::SetFocus(hWnd) ) + { +#if defined(__WXDEBUG__) && !defined(__WXMICROWIN__) + // was there really an error? + DWORD dwRes = ::GetLastError(); + if ( dwRes ) + { + HWND hwndFocus = ::GetFocus(); + if ( hwndFocus != hWnd ) + { + wxLogApiError(_T("SetFocus"), dwRes); + } + } +#endif // Debug + } +} + +void wxWindowMSW::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); + } + + // do this after (maybe) setting the selection as like this when + // wxEVT_SET_FOCUS handler is called, the selection would have been already + // set correctly -- this may be important + wxWindowBase::SetFocusFromKbd(); } // Get the window with the focus @@ -363,13 +495,13 @@ wxWindow *wxWindowBase::FindFocus() HWND hWnd = ::GetFocus(); if ( hWnd ) { - return wxFindWinFromHandle((WXHWND) hWnd); + return wxGetWindowFromHWND((WXHWND)hWnd); } return NULL; } -bool wxWindow::Enable(bool enable) +bool wxWindowMSW::Enable(bool enable) { if ( !wxWindowBase::Enable(enable) ) return FALSE; @@ -378,24 +510,63 @@ 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 ) + // 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::compatibility_iterator 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; } -bool wxWindow::Show(bool show) +bool wxWindowMSW::Show(bool show) { if ( !wxWindowBase::Show(show) ) return FALSE; @@ -404,57 +575,61 @@ bool wxWindow::Show(bool show) int cshow = show ? SW_SHOW : SW_HIDE; ::ShowWindow(hWnd, cshow); - if ( show ) + if ( show && IsTopLevel() ) { - BringWindowToTop(hWnd); + wxBringWindowToTop(hWnd); } return TRUE; } // Raise the window to the top of the Z order -void wxWindow::Raise() +void wxWindowMSW::Raise() { - ::BringWindowToTop(GetHwnd()); + wxBringWindowToTop(GetHwnd()); } // Lower the window to the bottom of the Z order -void wxWindow::Lower() +void wxWindowMSW::Lower() { ::SetWindowPos(GetHwnd(), HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); } -void wxWindow::SetTitle( const wxString& title) +void wxWindowMSW::SetTitle( const wxString& title) { SetWindowText(GetHwnd(), title.c_str()); } -wxString wxWindow::GetTitle() const +wxString wxWindowMSW::GetTitle() const { return wxGetWindowText(GetHWND()); } -void wxWindow::CaptureMouse() +void wxWindowMSW::DoCaptureMouse() { HWND hWnd = GetHwnd(); - if ( hWnd && !m_winCaptured ) + if ( hWnd ) { - SetCapture(hWnd); - m_winCaptured = TRUE; + ::SetCapture(hWnd); } } -void wxWindow::ReleaseMouse() +void wxWindowMSW::DoReleaseMouse() { - if ( m_winCaptured ) + if ( !::ReleaseCapture() ) { - ReleaseCapture(); - m_winCaptured = FALSE; + wxLogLastError(_T("ReleaseCapture")); } } -bool wxWindow::SetFont(const wxFont& font) +/* static */ wxWindow *wxWindowBase::GetCapture() +{ + HWND hwnd = ::GetCapture(); + return hwnd ? wxFindWinFromHandle((WXHWND)hwnd) : (wxWindow *)NULL; +} + +bool wxWindowMSW::SetFont(const wxFont& font) { if ( !wxWindowBase::SetFont(font) ) { @@ -474,7 +649,7 @@ bool wxWindow::SetFont(const wxFont& font) return TRUE; } -bool wxWindow::SetCursor(const wxCursor& cursor) +bool wxWindowMSW::SetCursor(const wxCursor& cursor) { if ( !wxWindowBase::SetCursor(cursor) ) { @@ -490,8 +665,7 @@ bool wxWindow::SetCursor(const wxCursor& cursor) POINT point; ::GetCursorPos(&point); - RECT rect; - ::GetWindowRect(hWnd, &rect); + RECT rect = wxGetWindowRect(hWnd); if ( ::PtInRect(&rect, point) && !wxIsBusy() ) ::SetCursor(GetHcursorOf(m_cursor)); @@ -500,24 +674,18 @@ bool wxWindow::SetCursor(const wxCursor& cursor) return TRUE; } -void wxWindow::WarpPointer (int x_pos, int y_pos) +void wxWindowMSW::WarpPointer (int x, int y) { - // Move the pointer to (x_pos,y_pos) coordinates. They are expressed in - // pixel coordinates, relatives to the canvas -- So, we first need to - // substract origin of the window, then convert to screen position - - int x = x_pos; int y = y_pos; - RECT rect; - GetWindowRect (GetHwnd(), &rect); + ClientToScreen(&x, &y); - x += rect.left; - y += rect.top; - - SetCursorPos (x, y); + if ( !::SetCursorPos(x, y) ) + { + wxLogLastError(_T("SetCursorPos")); + } } #if WXWIN_COMPATIBILITY -void wxWindow::MSWDeviceToLogical (float *x, float *y) const +void wxWindowMSW::MSWDeviceToLogical (float *x, float *y) const { } #endif // WXWIN_COMPATIBILITY @@ -526,11 +694,15 @@ void wxWindow::MSWDeviceToLogical (float *x, float *y) const // scrolling stuff // --------------------------------------------------------------------------- -#if WXWIN_COMPATIBILITY -void wxWindow::SetScrollRange(int orient, int range, bool refresh) +// convert wxHORIZONTAL/wxVERTICAL to SB_HORZ/SB_VERT +static inline int wxDirToWinStyle(int orient) { -#if defined(__WIN95__) + return orient == wxHORIZONTAL ? SB_HORZ : SB_VERT; +} +#if WXWIN_COMPATIBILITY +void wxWindowMSW::SetScrollRange(int orient, int range, bool refresh) +{ int range1 = range; // Try to adjust the range to cope with page size > 1 @@ -541,284 +713,212 @@ void wxWindow::SetScrollRange(int orient, int range, bool refresh) range1 += (pageSize - 1); } - SCROLLINFO info; - int dir; - - if ( orient == wxHORIZONTAL ) { - dir = SB_HORZ; - } else { - dir = SB_VERT; - } - - info.cbSize = sizeof(SCROLLINFO); + WinStruct info; info.nPage = pageSize; // Have to set this, or scrollbar goes awry info.nMin = 0; info.nMax = range1; - info.nPos = 0; info.fMask = SIF_RANGE | SIF_PAGE; HWND hWnd = GetHwnd(); if ( hWnd ) - ::SetScrollInfo(hWnd, dir, &info, refresh); -#else - int wOrient; - if ( orient == wxHORIZONTAL ) - wOrient = SB_HORZ; - else - wOrient = SB_VERT; - - HWND hWnd = GetHwnd(); - if ( hWnd ) - ::SetScrollRange(hWnd, wOrient, 0, range, refresh); -#endif + ::SetScrollInfo(hWnd, wxDirToWinStyle(orient), &info, refresh); } -void wxWindow::SetScrollPage(int orient, int page, bool refresh) +void wxWindowMSW::SetScrollPage(int orient, int page, bool refresh) { -#if defined(__WIN95__) - SCROLLINFO info; - int dir; - - if ( orient == wxHORIZONTAL ) { - dir = SB_HORZ; - m_xThumbSize = page; - } else { - dir = SB_VERT; - m_yThumbSize = page; - } - - info.cbSize = sizeof(SCROLLINFO); + WinStruct info; info.nPage = page; - info.nMin = 0; info.fMask = SIF_PAGE; HWND hWnd = GetHwnd(); if ( hWnd ) - ::SetScrollInfo(hWnd, dir, &info, refresh); -#else - if ( orient == wxHORIZONTAL ) - m_xThumbSize = page; - else - m_yThumbSize = page; -#endif + ::SetScrollInfo(hWnd, wxDirToWinStyle(orient), &info, refresh); } -int wxWindow::OldGetScrollRange(int orient) const +int wxWindowMSW::GetScrollPage(int orient) const { - int wOrient; - if ( orient == wxHORIZONTAL ) - wOrient = SB_HORZ; - else - wOrient = SB_VERT; + return orient == wxHORIZONTAL ? m_xThumbSize : m_yThumbSize; +} + +#endif // WXWIN_COMPATIBILITY -#if __WATCOMC__ && defined(__WINDOWS_386__) - short minPos, maxPos; +inline int GetScrollPosition(HWND hWnd, int wOrient) +{ +#ifdef __WXMICROWIN__ + return ::GetScrollPosWX(hWnd, wOrient); #else - int minPos, maxPos; -#endif - HWND hWnd = GetHwnd(); - if ( hWnd ) + SCROLLINFO scrollInfo; + scrollInfo.cbSize = sizeof(SCROLLINFO); + scrollInfo.fMask = SIF_POS; + if ( !::GetScrollInfo(hWnd, + wOrient, + &scrollInfo) ) { - ::GetScrollRange(hWnd, wOrient, &minPos, &maxPos); -#if defined(__WIN95__) - // Try to adjust the range to cope with page size > 1 - // - a Windows API quirk - int pageSize = GetScrollPage(orient); - if ( pageSize > 1 ) - { - maxPos -= (pageSize - 1); - } -#endif - return maxPos; + wxLogLastError(_T("GetScrollInfo")); } - else - return 0; -} - -int wxWindow::GetScrollPage(int orient) const -{ - if ( orient == wxHORIZONTAL ) - return m_xThumbSize; - else - return m_yThumbSize; + return scrollInfo.nPos; +// return ::GetScrollPos(hWnd, wOrient); +#endif } -#endif // WXWIN_COMPATIBILITY - -int wxWindow::GetScrollPos(int orient) const +int wxWindowMSW::GetScrollPos(int orient) const { - int wOrient; - if ( orient == wxHORIZONTAL ) - wOrient = SB_HORZ; - else - wOrient = SB_VERT; HWND hWnd = GetHwnd(); - if ( hWnd ) - { - return ::GetScrollPos(hWnd, wOrient); - } - else - return 0; + wxCHECK_MSG( hWnd, 0, _T("no HWND in GetScrollPos") ); + + return GetScrollPosition(hWnd, orient == wxHORIZONTAL ? SB_HORZ : SB_VERT); } // This now returns the whole range, not just the number // of positions that we can scroll. -int wxWindow::GetScrollRange(int orient) const +int wxWindowMSW::GetScrollRange(int orient) const { - int wOrient; - if ( orient == wxHORIZONTAL ) - wOrient = SB_HORZ; - else - wOrient = SB_VERT; - -#if __WATCOMC__ && defined(__WINDOWS_386__) - short minPos, maxPos; -#else - int minPos, maxPos; -#endif + int maxPos; HWND hWnd = GetHwnd(); - if ( hWnd ) - { - ::GetScrollRange(hWnd, wOrient, &minPos, &maxPos); -#if defined(__WIN95__) - // Try to adjust the range to cope with page size > 1 - // - a Windows API quirk - int pageSize = GetScrollThumb(orient); - if ( pageSize > 1 ) - { - maxPos -= (pageSize - 1); - } - // October 10th: new range concept. - maxPos += pageSize; + if ( !hWnd ) + return 0; +#if 0 + ::GetScrollRange(hWnd, orient == wxHORIZONTAL ? SB_HORZ : SB_VERT, + &minPos, &maxPos); #endif - - return maxPos; + SCROLLINFO scrollInfo; + scrollInfo.fMask = SIF_RANGE; + if ( !::GetScrollInfo(hWnd, + orient == wxHORIZONTAL ? SB_HORZ : SB_VERT, + &scrollInfo) ) + { + wxLogLastError(_T("GetScrollInfo")); } - else - return 0; + maxPos = scrollInfo.nMax; + + // undo "range - 1" done in SetScrollbar() + return maxPos + 1; } -int wxWindow::GetScrollThumb(int orient) const +int wxWindowMSW::GetScrollThumb(int orient) const { - if ( orient == wxHORIZONTAL ) - return m_xThumbSize; - else - return m_yThumbSize; + return orient == wxHORIZONTAL ? m_xThumbSize : m_yThumbSize; } -void wxWindow::SetScrollPos(int orient, int pos, bool refresh) +void wxWindowMSW::SetScrollPos(int orient, int pos, bool refresh) { -#if defined(__WIN95__) - SCROLLINFO info; - int dir; - - if ( orient == wxHORIZONTAL ) { - dir = SB_HORZ; - } else { - dir = SB_VERT; - } + HWND hWnd = GetHwnd(); + wxCHECK_RET( hWnd, _T("SetScrollPos: no HWND") ); - info.cbSize = sizeof(SCROLLINFO); + WinStruct info; info.nPage = 0; info.nMin = 0; info.nPos = pos; info.fMask = SIF_POS; + if ( HasFlag(wxALWAYS_SHOW_SB) ) + { + // disable scrollbar instead of removing it then + info.fMask |= SIF_DISABLENOSCROLL; + } + + ::SetScrollInfo(hWnd, orient == wxHORIZONTAL ? SB_HORZ : SB_VERT, + &info, refresh); +} + +// New function that will replace some of the above. +void wxWindowMSW::SetScrollbar(int orient, + int pos, + int pageSize, + int range, + bool refresh) +{ + WinStruct info; + info.nPage = pageSize; + info.nMin = 0; // range is nMax - nMin + 1 + info.nMax = range - 1; // as both nMax and nMax are inclusive + info.nPos = pos; + info.fMask = SIF_RANGE | SIF_PAGE | SIF_POS; + if ( HasFlag(wxALWAYS_SHOW_SB) ) + { + // disable scrollbar instead of removing it then + info.fMask |= SIF_DISABLENOSCROLL; + } HWND hWnd = GetHwnd(); if ( hWnd ) - ::SetScrollInfo(hWnd, dir, &info, refresh); -#else - int wOrient; - if ( orient == wxHORIZONTAL ) - wOrient = SB_HORZ; + { + ::SetScrollInfo(hWnd, orient == wxHORIZONTAL ? SB_HORZ : SB_VERT, + &info, refresh); + } + + *(orient == wxHORIZONTAL ? &m_xThumbSize : &m_yThumbSize) = pageSize; +} + +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 - wOrient = SB_VERT; + { + pr = NULL; + } - HWND hWnd = GetHwnd(); - if ( hWnd ) - ::SetScrollPos(hWnd, wOrient, pos, refresh); +#ifdef __WXWINCE__ + // FIXME: is this the exact equivalent of the line below? + ::ScrollWindowEx(GetHwnd(), dx, dy, pr, pr, 0, 0, SW_ERASE|SW_INVALIDATE); +#else + ::ScrollWindow(GetHwnd(), dx, dy, pr, pr); #endif } -// New function that will replace some of the above. -void wxWindow::SetScrollbar(int orient, int pos, int thumbVisible, - int range, bool refresh) +static bool ScrollVertically(HWND hwnd, int kind, int count) { -#if defined(__WIN95__) - int oldRange = range - thumbVisible; + int posStart = GetScrollPosition(hwnd, SB_VERT); - int range1 = oldRange; - - // Try to adjust the range to cope with page size > 1 - // - a Windows API quirk - int pageSize = thumbVisible; - if ( pageSize > 1 && range > 0) + int pos = posStart; + for ( int n = 0; n < count; n++ ) { - range1 += (pageSize - 1); - } + ::SendMessage(hwnd, WM_VSCROLL, kind, 0); - SCROLLINFO info; - int dir; + int posNew = GetScrollPosition(hwnd, SB_VERT); + if ( posNew == pos ) + { + // don't bother to continue, we're already at top/bottom + break; + } - if ( orient == wxHORIZONTAL ) { - dir = SB_HORZ; - } else { - dir = SB_VERT; + pos = posNew; } - info.cbSize = sizeof(SCROLLINFO); - info.nPage = pageSize; // Have to set this, or scrollbar goes awry - info.nMin = 0; - info.nMax = range1; - info.nPos = pos; - info.fMask = SIF_RANGE | SIF_PAGE | SIF_POS; + return pos != posStart; +} - HWND hWnd = GetHwnd(); - if ( hWnd ) - ::SetScrollInfo(hWnd, dir, &info, refresh); -#else - int wOrient; - if ( orient == wxHORIZONTAL ) - wOrient = SB_HORZ; - else - wOrient = SB_VERT; +bool wxWindowMSW::ScrollLines(int lines) +{ + bool down = lines > 0; - HWND hWnd = GetHwnd(); - if ( hWnd ) - { - ::SetScrollRange(hWnd, wOrient, 0, range, FALSE); - ::SetScrollPos(hWnd, wOrient, pos, refresh); - } -#endif - if ( orient == wxHORIZONTAL ) { - m_xThumbSize = thumbVisible; - } else { - m_yThumbSize = thumbVisible; - } + return ScrollVertically(GetHwnd(), + down ? SB_LINEDOWN : SB_LINEUP, + down ? lines : -lines); } -void wxWindow::ScrollWindow(int dx, int dy, const wxRect *rect) +bool wxWindowMSW::ScrollPages(int pages) { - RECT rect2; - if ( rect ) - { - rect2.left = rect->x; - rect2.top = rect->y; - rect2.right = rect->x + rect->width; - rect2.bottom = rect->y + rect->height; - } + bool down = pages > 0; - if ( rect ) - ::ScrollWindow(GetHwnd(), dx, dy, &rect2, NULL); - else - ::ScrollWindow(GetHwnd(), dx, dy, NULL, NULL); + return ScrollVertically(GetHwnd(), + down ? SB_PAGEDOWN : SB_PAGEUP, + down ? pages : -pages); } // --------------------------------------------------------------------------- // subclassing // --------------------------------------------------------------------------- -void wxWindow::SubclassWin(WXHWND hWnd) +void wxWindowMSW::SubclassWin(WXHWND hWnd) { wxASSERT_MSG( !m_oldWndProc, wxT("subclassing window twice?") ); @@ -827,11 +927,25 @@ void wxWindow::SubclassWin(WXHWND hWnd) wxAssociateWinWithHandle(hwnd, this); - m_oldWndProc = (WXFARPROC) GetWindowLong(hwnd, GWL_WNDPROC); - SetWindowLong(hwnd, GWL_WNDPROC, (LONG) wxWndProc); + 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) ) + { + ::SetWindowLong(hwnd, GWL_WNDPROC, (LONG) wxWndProc); + } + else + { + // 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; + } } -void wxWindow::UnsubclassWin() +void wxWindowMSW::UnsubclassWin() { wxRemoveHandleAssociation(this); @@ -843,102 +957,199 @@ void wxWindow::UnsubclassWin() wxCHECK_RET( ::IsWindow(hwnd), wxT("invalid HWND in UnsubclassWin") ); - FARPROC farProc = (FARPROC) GetWindowLong(hwnd, GWL_WNDPROC); - if ( (m_oldWndProc != 0) && (farProc != (FARPROC) m_oldWndProc) ) + if ( m_oldWndProc ) { - SetWindowLong(hwnd, GWL_WNDPROC, (LONG) m_oldWndProc); - m_oldWndProc = 0; + if ( !wxCheckWindowWndProc((WXHWND)hwnd, m_oldWndProc) ) + { + ::SetWindowLong(hwnd, GWL_WNDPROC, (LONG) m_oldWndProc); + } + + m_oldWndProc = NULL; } } } -// Make a Windows extended style from the given wxWindows window style -WXDWORD wxWindow::MakeExtendedStyle(long style, bool eliminateBorders) -{ - WXDWORD exStyle = 0; - if ( style & wxTRANSPARENT_WINDOW ) - exStyle |= WS_EX_TRANSPARENT; - - if ( !eliminateBorders ) +bool wxCheckWindowWndProc(WXHWND hWnd, WXFARPROC wndProc) +{ + // 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. + + // On WinCE (at least), the wndproc comparison doesn't work, + // so have to use something like this. +#ifdef __WXWINCE__ + extern const wxChar *wxCanvasClassName; + extern const wxChar *wxCanvasClassNameNR; + extern const wxChar *wxMDIFrameClassName; + extern const wxChar *wxMDIFrameClassNameNoRedraw; + extern const wxChar *wxMDIChildFrameClassName; + extern const wxChar *wxMDIChildFrameClassNameNoRedraw; + wxString str(wxGetWindowClass(hWnd)); + if (str == wxCanvasClassName || + str == wxCanvasClassNameNR || + str == wxMDIFrameClassName || + str == wxMDIFrameClassNameNoRedraw || + str == wxMDIChildFrameClassName || + str == wxMDIChildFrameClassNameNoRedraw || + str == _T("wxTLWHiddenParent")) + return TRUE; // Effectively means don't subclass + else + return FALSE; +#else + WNDCLASS cls; + if ( !::GetClassInfo(wxGetInstance(), wxGetWindowClass(hWnd), &cls) ) { - if ( style & wxSUNKEN_BORDER ) - exStyle |= WS_EX_CLIENTEDGE; - if ( style & wxDOUBLE_BORDER ) - exStyle |= WS_EX_DLGMODALFRAME; -#if defined(__WIN95__) - if ( style & wxRAISED_BORDER ) - exStyle |= WS_EX_WINDOWEDGE; - if ( style & wxSTATIC_BORDER ) - exStyle |= WS_EX_STATICEDGE; -#endif + wxLogLastError(_T("GetClassInfo")); + + return FALSE; } - return exStyle; + + return wndProc == (WXFARPROC)cls.lpfnWndProc; +#endif } -// 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) const +// ---------------------------------------------------------------------------- +// Style handling +// ---------------------------------------------------------------------------- + +void wxWindowMSW::SetWindowStyleFlag(long flags) { - // If matches certain criteria, then assume no 3D effects - // unless specifically requested (dealt with in MakeExtendedStyle) - if ( !GetParent() || !IsKindOf(CLASSINFO(wxControl)) || (m_windowStyle & wxNO_BORDER) ) + long flagsOld = GetWindowStyleFlag(); + if ( flags == flagsOld ) + return; + + // update the internal variable + wxWindowBase::SetWindowStyleFlag(flags); + + // now update the Windows style as well if needed - and if the window had + // been already created + if ( !GetHwnd() ) + return; + + WXDWORD exstyle, exstyleOld; + long style = MSWGetStyle(flags, &exstyle), + styleOld = MSWGetStyle(flagsOld, &exstyleOld); + + if ( style != styleOld ) { - *want3D = FALSE; - return MakeExtendedStyle(m_windowStyle, FALSE); + // 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); } - // Determine whether we should be using 3D effects or not. - bool nativeBorder = FALSE; // by default, we don't want a Win95 effect + // and the extended style + if ( exstyle != exstyleOld ) + { + long exstyleReal = ::GetWindowLong(GetHwnd(), GWL_EXSTYLE); + exstyleReal &= ~exstyleOld; + exstyleReal |= exstyle; + + ::SetWindowLong(GetHwnd(), GWL_EXSTYLE, exstyleReal); + + // we must call SetWindowPos() to flash the cached extended style and + // also to make the change to wxSTAY_ON_TOP style take effect: just + // setting the style simply doesn't work + if ( !::SetWindowPos(GetHwnd(), + exstyleReal & WS_EX_TOPMOST ? HWND_TOPMOST + : HWND_NOTOPMOST, + 0, 0, 0, 0, + SWP_NOMOVE | SWP_NOSIZE) ) + { + wxLogLastError(_T("SetWindowPos")); + } + } +} + +WXDWORD wxWindowMSW::MSWGetStyle(long flags, WXDWORD *exstyle) const +{ + // translate the style + WXDWORD style = WS_CHILD | WS_VISIBLE; + + if ( flags & wxCLIP_CHILDREN ) + style |= WS_CLIPCHILDREN; + + if ( flags & wxCLIP_SIBLINGS ) + style |= WS_CLIPSIBLINGS; - // 1) App can specify global 3D effects - *want3D = wxTheApp->GetAuto3D(); + if ( flags & wxVSCROLL ) + style |= WS_VSCROLL; - // 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; + if ( flags & wxHSCROLL ) + style |= WS_HSCROLL; - // 3) Control can override this global setting by defining - // a border style, e.g. wxSUNKEN_BORDER - if ( m_windowStyle & wxSUNKEN_BORDER ) - *want3D = TRUE; + const wxBorder border = GetBorder(flags); - // 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) ) + // WS_BORDER is only required for wxBORDER_SIMPLE + if ( border == wxBORDER_SIMPLE ) + style |= WS_BORDER; + + // now deal with ext style if the caller wants it + if ( exstyle ) { - *want3D = TRUE; - nativeBorder = TRUE; - } + *exstyle = 0; - // 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; +#ifndef __WXWINCE__ + if ( flags & wxTRANSPARENT_WINDOW ) + *exstyle |= WS_EX_TRANSPARENT; #endif - DWORD exStyle = MakeExtendedStyle(m_windowStyle, !nativeBorder); + switch ( border ) + { + default: + case wxBORDER_DEFAULT: + wxFAIL_MSG( _T("unknown border style") ); + // fall through - // 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 + case wxBORDER_NONE: + case wxBORDER_SIMPLE: + break; + + case wxBORDER_STATIC: + *exstyle |= WS_EX_STATICEDGE; + break; + + case wxBORDER_RAISED: + *exstyle |= WS_EX_DLGMODALFRAME; + break; + + case wxBORDER_SUNKEN: + *exstyle |= WS_EX_CLIENTEDGE; + style &= ~WS_BORDER; + break; + + case wxBORDER_DOUBLE: + *exstyle |= WS_EX_DLGMODALFRAME; + break; + } - return exStyle; + // wxUniv doesn't use Windows dialog navigation functions at all +#if !defined(__WXUNIVERSAL__) && !defined(__WXWINCE__) + // to make the dialog navigation work with the nested panels we must + // use this style (top level windows such as dialogs don't need it) + if ( (flags & wxTAB_TRAVERSAL) && !IsTopLevel() ) + { + *exstyle |= WS_EX_CONTROLPARENT; + } +#endif // __WXUNIVERSAL__ + } + + return style; } #if WXWIN_COMPATIBILITY // If nothing defined for this, try the parent. // E.g. we may be a button loaded from a resource, with no callback function // defined. -void wxWindow::OnCommand(wxWindow& win, wxCommandEvent& event) +void wxWindowMSW::OnCommand(wxWindow& win, wxCommandEvent& event) { if ( GetEventHandler()->ProcessEvent(event) ) return; @@ -948,7 +1159,7 @@ void wxWindow::OnCommand(wxWindow& win, wxCommandEvent& event) #endif // WXWIN_COMPATIBILITY_2 #if WXWIN_COMPATIBILITY -wxObject* wxWindow::GetChild(int number) const +wxObject* wxWindowMSW::GetChild(int number) const { // Return a pointer to the Nth object in the Panel wxNode *node = GetChildren().First(); @@ -966,27 +1177,42 @@ wxObject* wxWindow::GetChild(int number) const #endif // WXWIN_COMPATIBILITY // Setup background and foreground colours correctly -void wxWindow::SetupColours() +void wxWindowMSW::SetupColours() { if ( GetParent() ) SetBackgroundColour(GetParent()->GetBackgroundColour()); } -void wxWindow::OnIdle(wxIdleEvent& event) +bool wxWindowMSW::IsMouseInWindow() const +{ + // get the mouse position + POINT pt; + ::GetCursorPos(&pt); + + // find the window which currently has the cursor and go up the window + // chain until we find this window - or exhaust it + HWND hwnd = ::WindowFromPoint(pt); + while ( hwnd && (hwnd != GetHwnd()) ) + hwnd = ::GetParent(hwnd); + + return hwnd != NULL; +} + +void wxWindowMSW::OnInternalIdle() { // Check if we need to send a LEAVE event if ( m_mouseInWindow ) { - POINT pt; - ::GetCursorPos(&pt); - if ( ::WindowFromPoint(pt) != GetHwnd() ) + // note that we should generate the leave event whether the window has + // or doesn't have mouse capture + if ( !IsMouseInWindow() ) { // Generate a LEAVE event m_mouseInWindow = FALSE; - // Unfortunately the mouse button and keyboard state may have changed - // by the time the OnIdle function is called, so 'state' may be - // meaningless. + // Unfortunately the mouse button and keyboard state may have + // changed by the time the OnInternalIdle function is called, so 'state' + // may be meaningless. int state = 0; if ( wxIsShiftDown() ) state |= MK_SHIFT; @@ -998,19 +1224,32 @@ void wxWindow::OnIdle(wxIdleEvent& event) state |= MK_MBUTTON; if ( GetKeyState( VK_RBUTTON ) ) state |= MK_RBUTTON; - - wxMouseEvent event(wxEVT_LEAVE_WINDOW); - InitMouseEvent(event, pt.x, pt.y, state); - (void)GetEventHandler()->ProcessEvent(event); + POINT pt; + if ( !::GetCursorPos(&pt) ) + { + wxLogLastError(_T("GetCursorPos")); + } + + // we need to have client coordinates here for symmetry with + // wxEVT_ENTER_WINDOW + RECT rect = wxGetWindowRect(GetHwnd()); + pt.x -= rect.left; + pt.y -= rect.top; + + wxMouseEvent event2(wxEVT_LEAVE_WINDOW); + InitMouseEvent(event2, pt.x, pt.y, state); + + (void)GetEventHandler()->ProcessEvent(event2); } } - UpdateWindowUI(); + if (wxUpdateUIEvent::CanUpdate(this)) + UpdateWindowUI(wxUPDATE_UI_FROMIDLE); } // Set this window to be the child of 'parent'. -bool wxWindow::Reparent(wxWindow *parent) +bool wxWindowMSW::Reparent(wxWindowBase *parent) { if ( !wxWindowBase::Reparent(parent) ) return FALSE; @@ -1023,15 +1262,36 @@ bool wxWindow::Reparent(wxWindow *parent) return TRUE; } -void wxWindow::Clear() +void wxWindowMSW::Clear() { - wxClientDC dc(this); + wxClientDC dc((wxWindow *)this); wxBrush brush(GetBackgroundColour(), wxSOLID); dc.SetBackground(brush); dc.Clear(); } -void wxWindow::Refresh(bool eraseBack, const wxRect *rect) +static inline void SendSetRedraw(HWND hwnd, bool on) +{ +#ifndef __WXMICROWIN__ + ::SendMessage(hwnd, WM_SETREDRAW, (WPARAM)on, 0); +#endif +} + +void wxWindowMSW::Freeze() +{ + SendSetRedraw(GetHwnd(), FALSE); +} + +void wxWindowMSW::Thaw() +{ + SendSetRedraw(GetHwnd(), TRUE); + + // we need to refresh everything or otherwise he invalidated area is not + // repainted + Refresh(); +} + +void wxWindowMSW::Refresh(bool eraseBack, const wxRect *rect) { HWND hWnd = GetHwnd(); if ( hWnd ) @@ -1051,13 +1311,27 @@ void wxWindow::Refresh(bool eraseBack, const wxRect *rect) } } +void wxWindowMSW::Update() +{ + if ( !::UpdateWindow(GetHwnd()) ) + { + wxLogLastError(_T("UpdateWindow")); + } + +#if !defined(__WXMICROWIN__) && !defined(__WXWINCE__) + // just calling UpdateWindow() is not enough, what we did in our WM_PAINT + // handler needs to be really drawn right now + (void)::GdiFlush(); +#endif // __WIN32__ +} + // --------------------------------------------------------------------------- // drag and drop // --------------------------------------------------------------------------- -#if wxUSE_DRAG_AND_DROP -void wxWindow::SetDropTarget(wxDropTarget *pDropTarget) +#if wxUSE_DRAG_AND_DROP +void wxWindowMSW::SetDropTarget(wxDropTarget *pDropTarget) { if ( m_dropTarget != 0 ) { m_dropTarget->Revoke(m_hWnd); @@ -1068,16 +1342,17 @@ void wxWindow::SetDropTarget(wxDropTarget *pDropTarget) if ( m_dropTarget != 0 ) m_dropTarget->Register(m_hWnd); } - #endif // wxUSE_DRAG_AND_DROP // old style file-manager drag&drop support: we retain the old-style // DragAcceptFiles in parallel with SetDropTarget. -void wxWindow::DragAcceptFiles(bool accept) +void wxWindowMSW::DragAcceptFiles(bool accept) { +#if !defined(__WXWINCE__) HWND hWnd = GetHwnd(); if ( hWnd ) ::DragAcceptFiles(hWnd, (BOOL)accept); +#endif } // ---------------------------------------------------------------------------- @@ -1086,12 +1361,12 @@ void wxWindow::DragAcceptFiles(bool accept) #if wxUSE_TOOLTIPS -void wxWindow::DoSetToolTip(wxToolTip *tooltip) +void wxWindowMSW::DoSetToolTip(wxToolTip *tooltip) { wxWindowBase::DoSetToolTip(tooltip); if ( m_tooltip ) - m_tooltip->SetWindow(this); + m_tooltip->SetWindow((wxWindow *)this); } #endif // wxUSE_TOOLTIPS @@ -1101,30 +1376,30 @@ void wxWindow::DoSetToolTip(wxToolTip *tooltip) // --------------------------------------------------------------------------- // Get total size -void wxWindow::DoGetSize(int *x, int *y) const +void wxWindowMSW::DoGetSize(int *x, int *y) const { - HWND hWnd = GetHwnd(); - RECT rect; -#ifdef __WIN16__ - ::GetWindowRect(hWnd, &rect); -#else - if ( !::GetWindowRect(hWnd, &rect) ) - { - wxLogLastError(_T("GetWindowRect")); - } -#endif + RECT rect = wxGetWindowRect(GetHwnd()); + if ( x ) *x = rect.right - rect.left; if ( y ) *y = rect.bottom - rect.top; } -void wxWindow::DoGetPosition(int *x, int *y) const +// Get size *available for subwindows* i.e. excluding menu bar etc. +void wxWindowMSW::DoGetClientSize(int *x, int *y) const { - HWND hWnd = GetHwnd(); + RECT rect = wxGetClientRect(GetHwnd()); - RECT rect; - GetWindowRect(hWnd, &rect); + if ( x ) + *x = rect.right; + if ( y ) + *y = rect.bottom; +} + +void wxWindowMSW::DoGetPosition(int *x, int *y) const +{ + RECT rect = wxGetWindowRect(GetHwnd()); POINT point; point.x = rect.left; @@ -1146,11 +1421,14 @@ 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). - wxPoint pt(parent->GetClientAreaOrigin()); - point.x -= pt.x; - point.y -= pt.y; + 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; + } } if ( x ) @@ -1159,7 +1437,7 @@ void wxWindow::DoGetPosition(int *x, int *y) const *y = point.y; } -void wxWindow::DoScreenToClient(int *x, int *y) const +void wxWindowMSW::DoScreenToClient(int *x, int *y) const { POINT pt; if ( x ) @@ -1167,8 +1445,7 @@ void wxWindow::DoScreenToClient(int *x, int *y) const if ( y ) pt.y = *y; - HWND hWnd = GetHwnd(); - ::ScreenToClient(hWnd, &pt); + ::ScreenToClient(GetHwnd(), &pt); if ( x ) *x = pt.x; @@ -1176,7 +1453,7 @@ void wxWindow::DoScreenToClient(int *x, int *y) const *y = pt.y; } -void wxWindow::DoClientToScreen(int *x, int *y) const +void wxWindowMSW::DoClientToScreen(int *x, int *y) const { POINT pt; if ( x ) @@ -1184,8 +1461,7 @@ void wxWindow::DoClientToScreen(int *x, int *y) const if ( y ) pt.y = *y; - HWND hWnd = GetHwnd(); - ::ClientToScreen(hWnd, &pt); + ::ClientToScreen(GetHwnd(), &pt); if ( x ) *x = pt.x; @@ -1193,20 +1469,14 @@ void wxWindow::DoClientToScreen(int *x, int *y) const *y = pt.y; } -// Get size *available for subwindows* i.e. excluding menu bar etc. -void wxWindow::DoGetClientSize(int *x, int *y) const -{ - HWND hWnd = GetHwnd(); - RECT rect; - ::GetClientRect(hWnd, &rect); - if ( x ) - *x = rect.right; - if ( y ) - *y = rect.bottom; -} - -void wxWindow::DoMoveWindow(int x, int y, int width, int height) +void wxWindowMSW::DoMoveWindow(int x, int y, int width, int height) { + // TODO: is this consistent with other platforms? + // Still, negative width or height shouldn't be allowed + if (width < 0) + width = 0; + if (height < 0) + height = 0; if ( !::MoveWindow(GetHwnd(), x, y, width, height, TRUE) ) { wxLogLastError(wxT("MoveWindow")); @@ -1221,7 +1491,7 @@ void wxWindow::DoMoveWindow(int x, int y, int width, int height) // If sizeFlags contains wxSIZE_AUTO_WIDTH/HEIGHT flags (default), we calculate // the width/height to best suit our contents, otherwise we reuse the current // width/height -void wxWindow::DoSetSize(int x, int y, int width, int height, int sizeFlags) +void wxWindowMSW::DoSetSize(int x, int y, int width, int height, int sizeFlags) { // get the current size and position... int currentX, currentY; @@ -1280,89 +1550,91 @@ void wxWindow::DoSetSize(int x, int y, int width, int height, int sizeFlags) DoMoveWindow(x, y, width, height); } -void wxWindow::DoSetClientSize(int width, int height) +void wxWindowMSW::DoSetClientSize(int width, int height) { - wxWindow *parent = GetParent(); - HWND hWnd = GetHwnd(); - HWND hParentWnd = (HWND) 0; - if ( parent ) - hParentWnd = (HWND) parent->GetHWND(); + // setting the client size is less obvious than it it could have been + // because in the result of changing the total size the window scrollbar + // may [dis]appear and/or its menubar may [un]wrap and so the client size + // will not be correct as the difference between the total and client size + // changes - so we keep changing it until we get it right + // + // normally this loop shouldn't take more than 3 iterations (usually 1 but + // if scrollbars [dis]appear as the result of the first call, then 2 and it + // may become 3 if the window had 0 size originally and so we didn't + // calculate the scrollbar correction correctly during the first iteration) + // but just to be on the safe side we check for it instead of making it an + // "infinite" loop (i.e. leaving break inside as the only way to get out) + for ( int i = 0; i < 4; i++ ) + { + RECT rectClient; + ::GetClientRect(GetHwnd(), &rectClient); - RECT rect; - ::GetClientRect(hWnd, &rect); + // if the size is already ok, stop here (rectClient.left = top = 0) + if ( (rectClient.right == width || width == -1) && + (rectClient.bottom == height || height == -1) ) + { + break; + } - RECT rect2; - GetWindowRect(hWnd, &rect2); + int widthClient = width, + heightClient = height; - // Find the difference between the entire window (title bar and all) - // and the client area; add this to the new client size to move the - // window - int actual_width = rect2.right - rect2.left - rect.right + width; - int actual_height = rect2.bottom - rect2.top - rect.bottom + height; + // Find the difference between the entire window (title bar and all) + // and the client area; add this to the new client size to move the + // window + RECT rectWin; + ::GetWindowRect(GetHwnd(), &rectWin); - // If there's a parent, must subtract the parent's top left corner - // since MoveWindow moves relative to the parent + widthClient += rectWin.right - rectWin.left - rectClient.right; + heightClient += rectWin.bottom - rectWin.top - rectClient.bottom; - POINT point; - point.x = rect2.left; - point.y = rect2.top; - if ( parent ) - { - ::ScreenToClient(hParentWnd, &point); - } + POINT point; + point.x = rectWin.left; + point.y = rectWin.top; - DoMoveWindow(point.x, point.y, actual_width, actual_height); + // MoveWindow positions the child windows relative to the parent, so + // adjust if necessary + if ( !IsTopLevel() ) + { + wxWindow *parent = GetParent(); + if ( parent ) + { + ::ScreenToClient(GetHwndOf(parent), &point); + } + } - wxSizeEvent event(wxSize(width, height), m_windowId); - event.SetEventObject(this); - GetEventHandler()->ProcessEvent(event); + DoMoveWindow(point.x, point.y, widthClient, heightClient); + } } // For implementation purposes - sometimes decorations make the client area // smaller -wxPoint wxWindow::GetClientAreaOrigin() const +wxPoint wxWindowMSW::GetClientAreaOrigin() const { return wxPoint(0, 0); } -// Makes an adjustment to the window position (for example, a frame that has -// a toolbar that it manages itself). -void wxWindow::AdjustForParentClientOrigin(int& x, int& y, int sizeFlags) -{ - // don't do it for the dialogs/frames - they float independently of their - // parent - if ( !IsTopLevel() ) - { - wxWindow *parent = GetParent(); - if ( !(sizeFlags & wxSIZE_NO_ADJUSTMENTS) && parent ) - { - wxPoint pt(parent->GetClientAreaOrigin()); - x += pt.x; y += pt.y; - } - } -} - // --------------------------------------------------------------------------- // text metrics // --------------------------------------------------------------------------- -int wxWindow::GetCharHeight() const +int wxWindowMSW::GetCharHeight() const { return wxGetTextMetrics(this).tmHeight; } -int wxWindow::GetCharWidth() const +int wxWindowMSW::GetCharWidth() const { // +1 is needed because Windows apparently adds it when calculating the // dialog units size in pixels #if wxDIALOG_UNIT_COMPATIBILITY - return wxGetTextMetrics(this).tmAveCharWidth ; + return wxGetTextMetrics(this).tmAveCharWidth; #else return wxGetTextMetrics(this).tmAveCharWidth + 1; #endif } -void wxWindow::GetTextExtent(const wxString& string, +void wxWindowMSW::GetTextExtent(const wxString& string, int *x, int *y, int *descent, int *externalLeading, const wxFont *theFont) const @@ -1408,36 +1680,36 @@ void wxWindow::GetTextExtent(const wxString& string, // Caret manipulation // --------------------------------------------------------------------------- -void wxWindow::CreateCaret(int w, int h) +void wxWindowMSW::CreateCaret(int w, int h) { SetCaret(new wxCaret(this, w, h)); } -void wxWindow::CreateCaret(const wxBitmap *WXUNUSED(bitmap)) +void wxWindowMSW::CreateCaret(const wxBitmap *WXUNUSED(bitmap)) { wxFAIL_MSG("not implemented"); } -void wxWindow::ShowCaret(bool show) +void wxWindowMSW::ShowCaret(bool show) { wxCHECK_RET( m_caret, "no caret to show" ); m_caret->Show(show); } -void wxWindow::DestroyCaret() +void wxWindowMSW::DestroyCaret() { SetCaret(NULL); } -void wxWindow::SetCaretPos(int x, int y) +void wxWindowMSW::SetCaretPos(int x, int y) { wxCHECK_RET( m_caret, "no caret to move" ); m_caret->Move(x, y); } -void wxWindow::GetCaretPos(int *x, int *y) const +void wxWindowMSW::GetCaretPos(int *x, int *y) const { wxCHECK_RET( m_caret, "no caret to get position of" ); @@ -1449,7 +1721,30 @@ void wxWindow::GetCaretPos(int *x, int *y) const // popup menu // --------------------------------------------------------------------------- -bool wxWindow::DoPopupMenu(wxMenu *menu, int x, int y) +#if wxUSE_MENUS_NATIVE + +// yield for WM_COMMAND events only, i.e. process all WM_COMMANDs in the queue +// immediately, without waiting for the next event loop iteration +// +// NB: this function should probably be made public later as it can almost +// surely replace wxYield() elsewhere as well +static void wxYieldForCommandsOnly() +{ + // peek all WM_COMMANDs (it will always return WM_QUIT too but we don't + // want to process it here) + MSG msg; + while ( ::PeekMessage(&msg, (HWND)0, WM_COMMAND, WM_COMMAND, PM_REMOVE) + && msg.message != WM_QUIT ) + { + 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) { menu->SetInvokingWindow(this); menu->UpdateUI(); @@ -1461,8 +1756,21 @@ bool wxWindow::DoPopupMenu(wxMenu *menu, int x, int y) point.y = y; ::ClientToScreen(hWnd, &point); wxCurrentPopupMenu = menu; - ::TrackPopupMenu(hMenu, TPM_RIGHTBUTTON, point.x, point.y, 0, hWnd, NULL); - wxYield(); + UINT flags = 0; +#if !defined(__WXWINCE__) + flags = TPM_RIGHTBUTTON; +#endif + ::TrackPopupMenu(hMenu, flags, point.x, point.y, 0, hWnd, NULL); + + // we need to do it righ now as otherwise the events are never going to be + // sent to wxCurrentPopupMenu from HandleCommand() + // + // note that even eliminating (ugly) wxCurrentPopupMenu global wouldn't + // help and we'd still need wxYieldForCommandsOnly() as the menu may be + // destroyed as soon as we return (it can be a local variable in the caller + // for example) and so we do need to process the event immediately + wxYieldForCommandsOnly(); + wxCurrentPopupMenu = NULL; menu->SetInvokingWindow(NULL); @@ -1470,11 +1778,13 @@ bool wxWindow::DoPopupMenu(wxMenu *menu, int x, int y) return TRUE; } +#endif // wxUSE_MENUS_NATIVE + // =========================================================================== // pre/post message processing // =========================================================================== -long wxWindow::MSWDefWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) +long wxWindowMSW::MSWDefWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) { if ( m_oldWndProc ) return ::CallWindowProc(CASTWNDPROC m_oldWndProc, GetHwnd(), (UINT) nMsg, (WPARAM) wParam, (LPARAM) lParam); @@ -1482,8 +1792,10 @@ long wxWindow::MSWDefWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) return ::DefWindowProc(GetHwnd(), nMsg, wParam, lParam); } -bool wxWindow::MSWProcessMessage(WXMSG* pMsg) +bool wxWindowMSW::MSWProcessMessage(WXMSG* pMsg) { + // wxUniversal implements tab traversal itself +#ifndef __WXUNIVERSAL__ if ( m_hWnd != 0 && (GetWindowStyleFlag() & wxTAB_TRAVERSAL) ) { // intercept dialog navigation keys @@ -1492,14 +1804,7 @@ bool wxWindow::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(); @@ -1511,11 +1816,21 @@ bool wxWindow::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: @@ -1557,20 +1872,27 @@ 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; } + // FIXME: this should be handled by + // wxNavigationKeyEvent handler and not here!! else { - wxPanel *panel = wxDynamicCast(this, wxPanel); - wxButton *btn = NULL; - if ( panel ) - { - // panel may have a default button which should - // be activated by Enter - btn = panel->GetDefaultItem(); - } - +#if wxUSE_BUTTON + wxButton *btn = wxDynamicCast(GetDefaultItem(), + wxButton); if ( btn && btn->IsEnabled() ) { // if we do have a default button, do press it @@ -1578,9 +1900,29 @@ bool wxWindow::MSWProcessMessage(WXMSG* pMsg) return TRUE; } - // else: but if it does not it makes sense to make - // it work like a TAB - and that's what we do. - // Note that Ctrl-Enter always works this way. + else // no default button + { +#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 + } } } break; @@ -1598,18 +1940,11 @@ bool wxWindow::MSWProcessMessage(WXMSG* pMsg) if ( GetEventHandler()->ProcessEvent(event) ) { - wxButton *btn = wxDynamicCast(FindFocus(), wxButton); - if ( btn ) - { - // the button which has focus should be default - btn->SetDefault(); - } - return TRUE; } } } -#else +#else // 0 // 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 ) @@ -1637,14 +1972,96 @@ bool wxWindow::MSWProcessMessage(WXMSG* pMsg) } } } -#endif // 0 - - if ( ::IsDialogMessage(GetHwnd(), msg) ) +#endif // 1/0 + + // 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() is broken and may sometimes hang the + // application by going into an infinite loop, so we try to detect + // [some of] the situatations when this may happen and not call it + // then + + // assume we can call it by default + bool canSafelyCallIsDlgMsg = TRUE; + + HWND hwndFocus = ::GetFocus(); + + // if the currently focused window itself has WS_EX_CONTROLPARENT style, ::IsDialogMessage() will also enter + // an infinite loop, because it will recursively check the child + // windows but not the window itself and so if none of the children + // accepts focus it loops forever (as it only stops when it gets + // back to the window it started from) + // + // while it is very unusual that a window with WS_EX_CONTROLPARENT + // style has the focus, it can happen. One such possibility is if + // all windows are either toplevel, wxDialog, wxPanel or static + // controls and no window can actually accept keyboard input. +#if !defined(__WXWINCE__) + if ( ::GetWindowLong(hwndFocus, GWL_EXSTYLE) & WS_EX_CONTROLPARENT ) + { + // passimistic by default + canSafelyCallIsDlgMsg = FALSE; + for ( wxWindowList::compatibility_iterator node = GetChildren().GetFirst(); + node; + node = node->GetNext() ) + { + wxWindow * const win = node->GetData(); + if ( win->AcceptsFocus() && + !(::GetWindowLong(GetHwndOf(win), GWL_EXSTYLE) & + WS_EX_CONTROLPARENT) ) + { + // it shouldn't hang... + canSafelyCallIsDlgMsg = TRUE; + + break; + } + } + } +#endif // !__WXWINCE__ + + if ( canSafelyCallIsDlgMsg ) + { + // ::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 + 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); + } + } + + // let IsDialogMessage() have the message if it's safe to call it + if ( canSafelyCallIsDlgMsg && ::IsDialogMessage(GetHwnd(), msg) ) + { + // IsDialogMessage() did something... + return TRUE; + } } } +#endif // __WXUNIVERSAL__ #if wxUSE_TOOLTIPS if ( m_tooltip ) @@ -1659,18 +2076,27 @@ bool wxWindow::MSWProcessMessage(WXMSG* pMsg) return FALSE; } -bool wxWindow::MSWTranslateMessage(WXMSG* pMsg) +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* WXUNUSED(pMsg)) +{ + // preprocess all messages by default + return TRUE; } // --------------------------------------------------------------------------- -// message params unpackers (different for Win16 and Win32) +// message params unpackers // --------------------------------------------------------------------------- -#ifdef __WIN32__ - -void wxWindow::UnpackCommand(WXWPARAM wParam, WXLPARAM lParam, +void wxWindowMSW::UnpackCommand(WXWPARAM wParam, WXLPARAM lParam, WORD *id, WXHWND *hwnd, WORD *cmd) { *id = LOWORD(wParam); @@ -1678,7 +2104,7 @@ void wxWindow::UnpackCommand(WXWPARAM wParam, WXLPARAM lParam, *cmd = HIWORD(wParam); } -void wxWindow::UnpackActivate(WXWPARAM wParam, WXLPARAM lParam, +void wxWindowMSW::UnpackActivate(WXWPARAM wParam, WXLPARAM lParam, WXWORD *state, WXWORD *minimized, WXHWND *hwnd) { *state = LOWORD(wParam); @@ -1686,7 +2112,7 @@ void wxWindow::UnpackActivate(WXWPARAM wParam, WXLPARAM lParam, *hwnd = (WXHWND)lParam; } -void wxWindow::UnpackScroll(WXWPARAM wParam, WXLPARAM lParam, +void wxWindowMSW::UnpackScroll(WXWPARAM wParam, WXLPARAM lParam, WXWORD *code, WXWORD *pos, WXHWND *hwnd) { *code = LOWORD(wParam); @@ -1694,15 +2120,17 @@ void wxWindow::UnpackScroll(WXWPARAM wParam, WXLPARAM lParam, *hwnd = (WXHWND)lParam; } -void wxWindow::UnpackCtlColor(WXWPARAM wParam, WXLPARAM lParam, +void wxWindowMSW::UnpackCtlColor(WXWPARAM wParam, WXLPARAM lParam, WXWORD *nCtlColor, WXHDC *hdc, WXHWND *hwnd) { +#ifndef __WXMICROWIN__ *nCtlColor = CTLCOLOR_BTN; *hwnd = (WXHWND)lParam; *hdc = (WXHDC)wParam; +#endif } -void wxWindow::UnpackMenuSelect(WXWPARAM wParam, WXLPARAM lParam, +void wxWindowMSW::UnpackMenuSelect(WXWPARAM wParam, WXLPARAM lParam, WXWORD *item, WXWORD *flags, WXHMENU *hmenu) { *item = (WXWORD)wParam; @@ -1710,107 +2138,59 @@ void wxWindow::UnpackMenuSelect(WXWPARAM wParam, WXLPARAM lParam, *hmenu = (WXHMENU)lParam; } -#else // Win16 - -void wxWindow::UnpackCommand(WXWPARAM wParam, WXLPARAM lParam, - WXWORD *id, WXHWND *hwnd, WXWORD *cmd) -{ - *id = (WXWORD)wParam; - *hwnd = (WXHWND)LOWORD(lParam); - *cmd = HIWORD(lParam); -} - -void wxWindow::UnpackActivate(WXWPARAM wParam, WXLPARAM lParam, - WXWORD *state, WXWORD *minimized, WXHWND *hwnd) -{ - *state = (WXWORD)wParam; - *minimized = LOWORD(lParam); - *hwnd = (WXHWND)HIWORD(lParam); -} +// --------------------------------------------------------------------------- +// Main wxWindows window proc and the window proc for wxWindow +// --------------------------------------------------------------------------- -void wxWindow::UnpackScroll(WXWPARAM wParam, WXLPARAM lParam, - WXWORD *code, WXWORD *pos, WXHWND *hwnd) -{ - *code = (WXWORD)wParam; - *pos = LOWORD(lParam); - *hwnd = (WXHWND)HIWORD(lParam); -} +// Hook for new window just as it's being created, when the window isn't yet +// associated with the handle +static wxWindowMSW *gs_winBeingCreated = NULL; -void wxWindow::UnpackCtlColor(WXWPARAM wParam, WXLPARAM lParam, - WXWORD *nCtlColor, WXHDC *hdc, WXHWND *hwnd) +// implementation of wxWindowCreationHook class: it just sets gs_winBeingCreated to the +// window being created and insures that it's always unset back later +wxWindowCreationHook::wxWindowCreationHook(wxWindowMSW *winBeingCreated) { - *hwnd = (WXHWND)LOWORD(lParam); - *nCtlColor = (int)HIWORD(lParam); - *hdc = (WXHDC)wParam; + gs_winBeingCreated = winBeingCreated; } -void wxWindow::UnpackMenuSelect(WXWPARAM wParam, WXLPARAM lParam, - WXWORD *item, WXWORD *flags, WXHMENU *hmenu) +wxWindowCreationHook::~wxWindowCreationHook() { - *item = (WXWORD)wParam; - *flags = LOWORD(lParam); - *hmenu = (WXHMENU)HIWORD(lParam); + gs_winBeingCreated = NULL; } -#endif // Win32/16 - -// --------------------------------------------------------------------------- -// Main wxWindows window proc and the window proc for wxWindow -// --------------------------------------------------------------------------- - -// Hook for new window just as it's being created, when the window isn't yet -// associated with the handle -wxWindow *wxWndHook = NULL; - // Main window proc LRESULT WXDLLEXPORT APIENTRY _EXPORT wxWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { // trace all messages - useful for the debugging #ifdef __WXDEBUG__ - wxLogTrace(wxTraceMessages, wxT("Processing %s(wParam=%8lx, lParam=%8lx)"), - wxGetMessageName(message), wParam, lParam); + wxLogTrace(wxTraceMessages, + wxT("Processing %s(hWnd=%08lx, wParam=%8lx, lParam=%8lx)"), + wxGetMessageName(message), (long)hWnd, (long)wParam, lParam); #endif // __WXDEBUG__ - wxWindow *wnd = wxFindWinFromHandle((WXHWND) hWnd); + wxWindowMSW *wnd = wxFindWinFromHandle((WXHWND) hWnd); // when we get the first message for the HWND we just created, we associate - // it with wxWindow stored in wxWndHook - if ( !wnd && wxWndHook ) + // it with wxWindow stored in gs_winBeingCreated + if ( !wnd && gs_winBeingCreated ) { -#if 0 // def __WXDEBUG__ - char buf[512]; - ::GetClassNameA((HWND) hWnd, buf, 512); - wxString className(buf); -#endif - - wxAssociateWinWithHandle(hWnd, wxWndHook); - wnd = wxWndHook; - wxWndHook = NULL; + wxAssociateWinWithHandle(hWnd, gs_winBeingCreated); + wnd = gs_winBeingCreated; + gs_winBeingCreated = NULL; wnd->SetHWND((WXHWND)hWnd); } LRESULT rc; - // Stop right here if we don't have a valid handle in our wxWindow object. - if ( wnd && !wnd->GetHWND() ) - { - // FIXME: why do we do this? - wnd->SetHWND((WXHWND) hWnd); - rc = wnd->MSWDefWindowProc(message, wParam, lParam ); - wnd->SetHWND(0); - } + if ( wnd ) + rc = wnd->MSWWindowProc(message, wParam, lParam); else - { - if ( wnd ) - rc = wnd->MSWWindowProc(message, wParam, lParam); - else - rc = DefWindowProc( hWnd, message, wParam, lParam ); - } + rc = ::DefWindowProc(hWnd, message, wParam, lParam); return rc; } -long wxWindow::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam) +long wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam) { // did we process the message? bool processed = FALSE; @@ -1842,16 +2222,90 @@ long wxWindow::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam) break; case WM_DESTROY: - processed = HandleDestroy(); + // never set processed to TRUE and *always* pass WM_DESTROY to + // DefWindowProc() as Windows may do some internal cleanup when + // processing it and failing to pass the message along may cause + // memory and resource leaks! + (void)HandleDestroy(); break; case WM_MOVE: - processed = HandleMove(LOWORD(lParam), HIWORD(lParam)); + processed = HandleMove(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); + break; + +#if !defined(__WXWINCE__) + case WM_MOVING: + { + LPRECT pRect = (LPRECT)lParam; + wxRect rc; + rc.SetLeft(pRect->left); + rc.SetTop(pRect->top); + rc.SetRight(pRect->right); + rc.SetBottom(pRect->bottom); + processed = HandleMoving(rc); + if (processed) { + pRect->left = rc.GetLeft(); + pRect->top = rc.GetTop(); + pRect->right = rc.GetRight(); + pRect->bottom = rc.GetBottom(); + } + } break; +#endif case WM_SIZE: - processed = HandleSize(LOWORD(lParam), HIWORD(lParam), wParam); + switch ( wParam ) + { + case SIZE_MAXHIDE: + case SIZE_MAXSHOW: + // we're not interested in these messages at all + break; + + case SIZE_MINIMIZED: + // we shouldn't send sizev events for these messages as the + // client size may be negative which breaks existing code + // + // OTOH we might send another (wxMinimizedEvent?) one or + // add an additional parameter to wxSizeEvent if this is + // useful to anybody + break; + + default: + wxFAIL_MSG( _T("unexpected WM_SIZE parameter") ); + // fall through nevertheless + + case SIZE_MAXIMIZED: + case SIZE_RESTORED: + processed = HandleSize(LOWORD(lParam), HIWORD(lParam), + wParam); + } + break; + +#if !defined(__WXWINCE__) + case WM_SIZING: + { + LPRECT pRect = (LPRECT)lParam; + wxRect rc; + rc.SetLeft(pRect->left); + rc.SetTop(pRect->top); + rc.SetRight(pRect->right); + rc.SetBottom(pRect->bottom); + processed = HandleSizing(rc); + if (processed) { + pRect->left = rc.GetLeft(); + pRect->top = rc.GetTop(); + pRect->right = rc.GetRight(); + pRect->bottom = rc.GetBottom(); + } + } + break; +#endif + +#if !defined(__WXMICROWIN__) && !defined(__WXWINCE__) + case WM_ACTIVATEAPP: + wxTheApp->SetActive(wParam != 0, FindFocus()); break; +#endif case WM_ACTIVATE: { @@ -1876,10 +2330,18 @@ long wxWindow::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: @@ -1887,20 +2349,18 @@ long wxWindow::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam) break; case WM_MOUSEMOVE: - { - short x = LOWORD(lParam); - short y = HIWORD(lParam); + processed = HandleMouseMove(GET_X_LPARAM(lParam), + GET_Y_LPARAM(lParam), + wParam); + break; - processed = HandleMouseMove(x, y, wParam); - } - break; +#if wxUSE_MOUSEWHEEL + case WM_MOUSEWHEEL: + processed = HandleMouseWheel(wParam, lParam); + break; +#endif case WM_LBUTTONDOWN: - // set focus to this window - SetFocus(); - - // fall through - case WM_LBUTTONUP: case WM_LBUTTONDBLCLK: case WM_RBUTTONDOWN: @@ -1910,13 +2370,64 @@ long wxWindow::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam) case WM_MBUTTONUP: case WM_MBUTTONDBLCLK: { - short x = LOWORD(lParam); - short y = HIWORD(lParam); +#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; + for ( ;; ) + { + if (!win->IsEnabled()) + { + processed = TRUE; + break; + } + + win = win->GetParent(); + if ( !win || win->IsTopLevel() ) + break; + } + + if ( processed ) + break; + +#endif // __WXMICROWIN__ + 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 ) + { + // but don't do it if the mouse is captured by this window + // because then it should really get this event itself + win = this; + } + else + { + 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() ) + { + if ( message == WM_LBUTTONDOWN && win->AcceptsFocus() ) + win->SetFocus(); + } + } - processed = HandleMouseEvent(message, x, y, wParam); + processed = win->HandleMouseEvent(message, x, y, wParam); } break; +#ifdef MM_JOY1MOVE case MM_JOY1MOVE: case MM_JOY2MOVE: case MM_JOY1ZMOVE: @@ -1925,13 +2436,12 @@ 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; +#endif // __WXMICROWIN__ case WM_SYSCOMMAND: processed = HandleSysCommand(wParam, lParam); @@ -1947,13 +2457,12 @@ long wxWindow::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam) } break; -#ifdef __WIN95__ case WM_NOTIFY: processed = HandleNotify((int)wParam, lParam, &rc.result); break; -#endif // Win95 // for these messages we must return TRUE if process the message +#ifdef WM_DRAWITEM case WM_DRAWITEM: case WM_MEASUREITEM: { @@ -1973,11 +2482,22 @@ long wxWindow::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam) rc.result = TRUE; } break; +#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() @@ -1985,62 +2505,79 @@ long wxWindow::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam) case WM_SYSKEYDOWN: case WM_KEYDOWN: - // If this has been processed by an event handler, - // return 0 now (we've handled it). - if ( HandleKeyDown((WORD) wParam, lParam) ) - { - processed = TRUE; - - break; - } - - // we consider these message "not interesting" to OnChar - if ( wParam == VK_SHIFT || wParam == VK_CONTROL ) + // If this has been processed by an event handler, return 0 now + // (we've handled it). + m_lastKeydownProcessed = HandleKeyDown((WORD) wParam, lParam); + if ( m_lastKeydownProcessed ) { processed = TRUE; - - break; } - switch ( wParam ) + if ( !processed ) { - // avoid duplicate messages to OnChar for these ASCII keys: they - // will be translated by TranslateMessage() and received in WM_CHAR - case VK_ESCAPE: - case VK_SPACE: - case VK_RETURN: - case VK_BACK: - case VK_TAB: - case VK_ADD: - case VK_SUBTRACT: - // but set processed to FALSE, not TRUE to still pass them to - // the control's default window proc - otherwise built-in - // keyboard handling won't work - processed = FALSE; - - break; + 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; + + // 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: @@ -2056,16 +2593,33 @@ long wxWindow::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam) processed = HandleMouseEvent(WM_RBUTTONUP, x, y, flags); } else -#endif // VK_APPS +#endif // VK_APPS + { + processed = HandleKeyUp((WORD) wParam, lParam); + } + break; + + case WM_SYSCHAR: + case WM_CHAR: // Always an ASCII character + 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 = HandleKeyUp((WORD) wParam, lParam); + processed = HandleChar((WORD)wParam, lParam, TRUE); } break; - case WM_SYSCHAR: - case WM_CHAR: // Always an ASCII character - processed = HandleChar((WORD)wParam, lParam, TRUE); +#if wxUSE_HOTKEY + case WM_HOTKEY: + processed = HandleHotKey((WORD)wParam, lParam); break; +#endif // wxUSE_HOTKEY case WM_HSCROLL: case WM_VSCROLL: @@ -2081,8 +2635,8 @@ long wxWindow::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam) break; // CTLCOLOR messages are sent by children to query the parent for their - // colors -#ifdef __WIN32__ + // colors#ifndef __WXMICROWIN__ +#ifndef __WXMICROWIN__ case WM_CTLCOLORMSGBOX: case WM_CTLCOLOREDIT: case WM_CTLCOLORLISTBOX: @@ -2090,9 +2644,6 @@ long wxWindow::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam) case WM_CTLCOLORDLG: case WM_CTLCOLORSCROLLBAR: case WM_CTLCOLORSTATIC: -#else // Win16 - case WM_CTLCOLOR: -#endif // Win32/16 { WXWORD nCtlColor; WXHDC hdc; @@ -2108,16 +2659,27 @@ long wxWindow::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam) lParam); } break; +#endif // !__WXMICROWIN__ - // the return value for this message is ignored case WM_SYSCOLORCHANGE: + // the return value for this message is ignored processed = HandleSysColorChange(); break; +#if !defined(__WXWINCE__) + case WM_DISPLAYCHANGE: + processed = HandleDisplayChange(); + break; +#endif + case WM_PALETTECHANGED: processed = HandlePaletteChanged((WXHWND) (HWND) wParam); break; + case WM_CAPTURECHANGED: + processed = HandleCaptureChanged((WXHWND) (HWND) lParam); + break; + case WM_QUERYNEWPALETTE: processed = HandleQueryNewPalette(); break; @@ -2131,9 +2693,11 @@ long wxWindow::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam) } break; +#if !defined(__WXWINCE__) case WM_DROPFILES: processed = HandleDropFiles(wParam); break; +#endif case WM_INITDIALOG: processed = HandleInitDialog((WXHWND)(HWND)wParam); @@ -2145,6 +2709,7 @@ long wxWindow::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam) } break; +#if !defined(__WXWINCE__) case WM_QUERYENDSESSION: processed = HandleQueryEndSession(lParam, &rc.allow); break; @@ -2156,6 +2721,7 @@ long wxWindow::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam) case WM_GETMINMAXINFO: processed = HandleGetMinMaxInfo((MINMAXINFO*)lParam); break; +#endif case WM_SETCURSOR: processed = HandleSetCursor((WXHWND)(HWND)wParam, @@ -2170,36 +2736,95 @@ long wxWindow::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam) rc.result = TRUE; } break; -#ifdef __WIN32__ + +#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(WM_HELP) 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) + // HELPINFO doesn't seem to be supported on WinCE. +#ifndef __WXWINCE__ + HELPINFO* info = (HELPINFO*) lParam; + // Don't yet process menu help events, just windows + if (info->iContextType == HELPINFO_WINDOW) + { +#endif + wxWindowMSW* subjectOfHelp = this; + bool eventProcessed = FALSE; + while (subjectOfHelp && !eventProcessed) + { + wxHelpEvent helpEvent(wxEVT_HELP, + subjectOfHelp->GetId(), +#ifdef __WXWINCE__ + wxPoint(0, 0) +#else + wxPoint(info->MousePos.x, info->MousePos.y) +#endif + ); + + helpEvent.SetEventObject(this); + eventProcessed = + GetEventHandler()->ProcessEvent(helpEvent); + + // Go up the window hierarchy until the event is + // handled (or not) + subjectOfHelp = subjectOfHelp->GetParent(); + } + + processed = eventProcessed; +#ifndef __WXWINCE__ + } + else if (info->iContextType == HELPINFO_MENUITEM) { - wxHelpEvent helpEvent(wxEVT_HELP, subjectOfHelp->GetId(), wxPoint(info->MousePos.x, info->MousePos.y) ) ; // info->iCtrlId); + wxHelpEvent helpEvent(wxEVT_HELP, info->iCtrlId); helpEvent.SetEventObject(this); - eventProcessed = GetEventHandler()->ProcessEvent(helpEvent); + processed = GetEventHandler()->ProcessEvent(helpEvent); - // Go up the window hierarchy until the event is handled (or not) - subjectOfHelp = subjectOfHelp->GetParent(); } - processed = eventProcessed; + //else: processed is already FALSE +#endif } - else if (info->iContextType == HELPINFO_MENUITEM) + break; +#endif + +#if !defined(__WXWINCE__) + case WM_CONTEXTMENU: { - wxHelpEvent helpEvent(wxEVT_HELP, info->iCtrlId) ; - helpEvent.SetEventObject(this); - processed = GetEventHandler()->ProcessEvent(helpEvent); + // we don't convert from screen to client coordinates as + // the event may be handled by a parent window + wxPoint pt(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); + + wxContextMenuEvent evtCtx(wxEVT_CONTEXT_MENU, GetId(), pt); + processed = GetEventHandler()->ProcessEvent(evtCtx); } - else processed = FALSE; break; - } #endif + + case WM_MENUCHAR: + // we're only interested in our own menus, not MF_SYSMENU + if ( HIWORD(wParam) == MF_POPUP ) + { + // 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; } if ( !processed ) @@ -2214,265 +2839,190 @@ long wxWindow::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam) return rc.result; } -// Dialog window proc -LONG APIENTRY _EXPORT -wxDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) -{ - if ( message == WM_INITDIALOG ) - { - // for this message, returning TRUE tells system to set focus to the - // first control in the dialog box - return TRUE; - } - else - { - // for all the other ones, FALSE means that we didn't process the - // message - return 0; - } -} +// ---------------------------------------------------------------------------- +// wxWindow <-> HWND map +// ---------------------------------------------------------------------------- + +wxWinHashTable *wxWinHandleHash = NULL; -wxList *wxWinHandleList = NULL; wxWindow *wxFindWinFromHandle(WXHWND hWnd) { - wxNode *node = wxWinHandleList->Find((long)hWnd); - if ( !node ) - return NULL; - return (wxWindow *)node->Data(); + return wxWinHandleHash->Get((long)hWnd); } -#if 0 // def __WXDEBUG__ -static int gs_AssociationCount = 0; -#endif - -void wxAssociateWinWithHandle(HWND hWnd, wxWindow *win) +void wxAssociateWinWithHandle(HWND hWnd, wxWindowMSW *win) { // adding NULL hWnd is (first) surely a result of an error and // (secondly) breaks menu command processing wxCHECK_RET( hWnd != (HWND)NULL, wxT("attempt to add a NULL hWnd to window list ignored") ); - wxWindow *oldWin = wxFindWinFromHandle((WXHWND) hWnd); +#ifdef __WXDEBUG__ 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); + wxLogDebug(wxT("HWND %X already associated with another window (%s)"), + (int) hWnd, win->GetClassInfo()->GetClassName()); } - else if (!oldWin) + else +#endif // __WXDEBUG__ + if (!oldWin) { -#if 0 // def __WXDEBUG__ - gs_AssociationCount ++; - wxLogDebug("+ Association %d", gs_AssociationCount); -#endif - - wxWinHandleList->Append((long)hWnd, win); + wxWinHandleHash->Put((long)hWnd, (wxWindow *)win); } } -void wxRemoveHandleAssociation(wxWindow *win) +void wxRemoveHandleAssociation(wxWindowMSW *win) { -#if 0 // def __WXDEBUG__ - if (wxWinHandleList->Member(win)) - { - wxLogDebug("- Association %d", gs_AssociationCount); - gs_AssociationCount --; - } -#endif - wxWinHandleList->DeleteObject(win); + wxWinHandleHash->Delete((long)win->GetHWND()); } +// ---------------------------------------------------------------------------- +// various MSW speciic class dependent functions +// ---------------------------------------------------------------------------- + // Default destroyer - override if you destroy it in some other way // (e.g. with MDI child windows) -void wxWindow::MSWDestroyWindow() +void wxWindowMSW::MSWDestroyWindow() { } -void wxWindow::MSWDetachWindowMenu() +bool wxWindowMSW::MSWGetCreateWindowCoords(const wxPoint& pos, + const wxSize& size, + int& x, int& y, + int& w, int& h) const { - if ( m_hMenu ) - { - HMENU hMenu = (HMENU)m_hMenu; - - int N = ::GetMenuItemCount(hMenu); - int i; - for (i = 0; i < N; i++) - { - wxChar buf[100]; - int chars = GetMenuString(hMenu, i, buf, 100, MF_BYPOSITION); - if ( !chars ) - { - wxLogLastError(wxT("GetMenuString")); - - continue; - } + static const int DEFAULT_Y = 200; + static const int DEFAULT_H = 250; - if ( wxStrcmp(buf, wxT("&Window")) == 0 ) - { - RemoveMenu(hMenu, i, MF_BYPOSITION); + bool nonDefault = FALSE; - break; - } - } + if ( pos.x == -1 ) + { + // if set x to CW_USEDEFAULT, y parameter is ignored anyhow so we can + // just as well set it to CW_USEDEFAULT as well + x = + y = CW_USEDEFAULT; } -} - -bool wxWindow::MSWCreate(int id, - wxWindow *parent, - const wxChar *wclass, - wxWindow *wx_win, - const wxChar *title, - int x, - int y, - int width, - int height, - WXDWORD style, - const wxChar *dialog_template, - WXDWORD extendedStyle) -{ - int x1 = CW_USEDEFAULT; - int y1 = 0; - int width1 = CW_USEDEFAULT; - int height1 = 100; - - // Find parent's size, if it exists, to set up a possible default - // panel size the size of the parent window - RECT parent_rect; - if ( parent ) + else { - ::GetClientRect((HWND) parent->GetHWND(), &parent_rect); + // 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 ? DEFAULT_Y : pos.y; - width1 = parent_rect.right - parent_rect.left; - height1 = parent_rect.bottom - parent_rect.top; + nonDefault = TRUE; } - 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 ) + /* + NB: there used to be some code here which set the initial size of the + window to the client size of the parent if no explicit size was + specified. This was wrong because wxWindows programs often assume + that they get a WM_SIZE (EVT_SIZE) upon creation, however this broke + it. To see why, you should understand that Windows sends WM_SIZE from + inside ::CreateWindow() anyhow. However, ::CreateWindow() is called + from some base class ctor and so this WM_SIZE is not processed in the + real class' OnSize() (because it's not fully constructed yet and the + event goes to some base class OnSize() instead). So the WM_SIZE we + rely on is the one sent when the parent frame resizes its children + but here is the problem: if the child already has just the right + size, nothing will happen as both wxWindows and Windows check for + this and ignore any attempts to change the window size to the size it + already has - so no WM_SIZE would be sent. + */ + if ( size.x == -1 ) { - extendedStyle |= WS_EX_CONTROLPARENT; + // as above, h is not used at all in this case anyhow + w = + h = CW_USEDEFAULT; } -#endif // 0 - - HWND hParent = (HWND)NULL; - if ( parent ) - hParent = (HWND) parent->GetHWND(); + else + { + // and, again as above, we can't set the height to CW_USEDEFAULT here + w = size.x; + h = size.y == -1 ? DEFAULT_H : size.y; - wxWndHook = this; + nonDefault = TRUE; + } - if ( dialog_template ) - { - m_hWnd = (WXHWND)::CreateDialog(wxGetInstance(), - dialog_template, - hParent, - (DLGPROC)wxDlgProc); + return nonDefault; +} - if ( m_hWnd == 0 ) - { - wxLogError(_("Can't find dummy dialog template!\nCheck resource include path for finding wx.rc.")); +WXHWND wxWindowMSW::MSWGetParent() const +{ + return m_parent ? m_parent->GetHWND() : WXHWND(NULL); +} - 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) - { - if (wxTheApp->GetTopWindow() && (wxTheApp->GetTopWindow()->IsKindOf(CLASSINFO(wxFrame)))) - { - wxIcon icon = ((wxFrame*)wxTheApp->GetTopWindow())->GetIcon(); - if (icon.Ok()) - SendMessage(GetHwnd(), WM_SETICON, - (WPARAM)TRUE, (LPARAM)(HICON) icon.GetHICON()); - } - } -#endif // __WIN95__ +bool wxWindowMSW::MSWCreate(const wxChar *wclass, + const wxChar *title, + const wxPoint& pos, + const wxSize& size, + WXDWORD style, + WXDWORD extendedStyle) +{ + // choose the position/size for the new window + int x, y, w, h; + (void)MSWGetCreateWindowCoords(pos, size, x, y, w, h); + // controlId is menu handle for the top level windows, so set it to 0 + // unless we're creating a child window + int controlId = style & WS_CHILD ? GetId() : 0; - // JACS: is the following still necessary? The above seems to work. + // 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 + // ensures that the window is not fully repainted on each resize + wxString className(wclass); + if ( GetWindowStyleFlag() & wxNO_FULL_REPAINT_ON_RESIZE ) + { + className += wxT("NR"); + } - // ::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 ) - { - if ( !::SetWindowPos(GetHwnd(), HWND_TOPMOST, 0, 0, 0, 0, - SWP_NOSIZE | SWP_NOMOVE) ) - { - wxLogLastError(wxT("SetWindowPos")); - } - } + // do create the window + wxWindowCreationHook hook(this); - // move the dialog to its initial position without forcing repainting - if ( !::MoveWindow(GetHwnd(), x1, y1, width1, height1, FALSE) ) - { - wxLogLastError(wxT("MoveWindow")); - } +#ifdef __WXWINCE__ + if (extendedStyle == 0) + { + m_hWnd = (WXHWND)::CreateWindow + ( + className, + title ? title : wxEmptyString, + style, + x, y, w, h, + (HWND)MSWGetParent(), + (HMENU)controlId, + wxGetInstance(), + NULL // no extra data + ); } else +#endif { - 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, - className, - title ? title : wxT(""), - style, - x1, y1, - width1, height1, - hParent, (HMENU)controlId, - wxGetInstance(), - NULL); - - if ( !m_hWnd ) - { - wxLogError(_("Can't create window of class %s!\nPossible Windows 3.x compatibility problem?"), - wclass); - - return FALSE; - } + m_hWnd = (WXHWND)::CreateWindowEx + ( + extendedStyle, + className, + title ? title : wxEmptyString, + style, + x, y, w, h, + (HWND)MSWGetParent(), + (HMENU)controlId, + wxGetInstance(), + NULL // no extra data + ); } - wxWndHook = NULL; -#ifdef __WXDEBUG__ - wxNode* node = wxWinHandleList->Member(this); - if (node) + if ( !m_hWnd ) { - HWND hWnd = (HWND) node->GetKeyInteger(); - if (hWnd != (HWND) m_hWnd) - { - wxLogError(wxT("A second HWND association is being added for the same window!")); - } + wxLogSysError(_("Can't create window of class %s"), wclass); + + return FALSE; } -#endif - wxAssociateWinWithHandle((HWND) m_hWnd, this); + + SubclassWin(m_hWnd); + + SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT)); return TRUE; } @@ -2486,21 +3036,29 @@ bool wxWindow::MSWCreate(int id, // --------------------------------------------------------------------------- #ifdef __WIN95__ -// FIXME: VZ: I'm not sure at all that the order of processing is correct -bool wxWindow::HandleNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) + +bool wxWindowMSW::HandleNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) { +#ifndef __WXMICROWIN__ LPNMHDR hdr = (LPNMHDR)lParam; 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(); + wxWindowList::compatibility_iterator node = GetChildren().GetFirst(); while ( node ) { wxWindow *child = node->GetData(); @@ -2511,37 +3069,88 @@ bool wxWindow::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 } -bool wxWindow::MSWOnNotify(int WXUNUSED(idCtrl), - WXLPARAM lParam, - WXLPARAM* WXUNUSED(result)) -{ #if wxUSE_TOOLTIPS - NMHDR* hdr = (NMHDR *)lParam; - if ( (int)hdr->code == TTN_NEEDTEXT && m_tooltip ) + +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 +#ifndef __WXWINCE__ + if ( !(code == (WXUINT) TTN_NEEDTEXTA || code == (WXUINT) TTN_NEEDTEXTW) || ttip.empty() ) { - TOOLTIPTEXT *ttt = (TOOLTIPTEXT *)lParam; - ttt->lpszText = (wxChar *)m_tooltip->GetTip().c_str(); + // not a tooltip message or no tooltip to show anyhow + return FALSE; + } +#endif - // processed - return TRUE; + 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)) +{ +#if wxUSE_TOOLTIPS + if ( m_tooltip ) + { + NMHDR* hdr = (NMHDR *)lParam; + if ( HandleTooltipNotify(hdr->code, lParam, m_tooltip->GetTip())) + { + // processed + return TRUE; + } } #endif // wxUSE_TOOLTIPS return FALSE; } + #endif // __WIN95__ // --------------------------------------------------------------------------- // end session messages // --------------------------------------------------------------------------- -bool wxWindow::HandleQueryEndSession(long logOff, bool *mayEnd) +bool wxWindowMSW::HandleQueryEndSession(long logOff, bool *mayEnd) { +#ifndef __WXWINCE__ wxCloseEvent event(wxEVT_QUERY_END_SESSION, -1); event.SetEventObject(wxTheApp); event.SetCanVeto(TRUE); @@ -2557,33 +3166,68 @@ bool wxWindow::HandleQueryEndSession(long logOff, bool *mayEnd) } return rc; +#else + return FALSE; +#endif } -bool wxWindow::HandleEndSession(bool endSession, long logOff) +bool wxWindowMSW::HandleEndSession(bool endSession, long logOff) { +#ifndef __WXWINCE__ // do nothing if the session isn't ending if ( !endSession ) return FALSE; + // only send once + if ( (this != wxTheApp->GetTopWindow()) ) + return FALSE; + wxCloseEvent event(wxEVT_END_SESSION, -1); event.SetEventObject(wxTheApp); event.SetCanVeto(FALSE); event.SetLoggingOff( (logOff == (long)ENDSESSION_LOGOFF) ); - if ( (this == wxTheApp->GetTopWindow()) && // Only send once - wxTheApp->ProcessEvent(event)) - { - } - return TRUE; + + return wxTheApp->ProcessEvent(event); +#else + return FALSE; +#endif } // --------------------------------------------------------------------------- // window creation/destruction // --------------------------------------------------------------------------- -bool wxWindow::HandleCreate(WXLPCREATESTRUCT 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 +#ifndef __WXWINCE__ + 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(); + } + } +#endif + // TODO: should generate this event from WM_NCCREATE - wxWindowCreateEvent event(this); + wxWindowCreateEvent event((wxWindow *)this); (void)GetEventHandler()->ProcessEvent(event); *mayCreate = TRUE; @@ -2591,10 +3235,9 @@ bool wxWindow::HandleCreate(WXLPCREATESTRUCT cs, bool *mayCreate) return TRUE; } -bool wxWindow::HandleDestroy() +bool wxWindowMSW::HandleDestroy() { - wxWindowDestroyEvent event(this); - (void)GetEventHandler()->ProcessEvent(event); + SendDestroyEvent(); // delete our drop target if we've got one #if wxUSE_DRAG_AND_DROP @@ -2615,34 +3258,7 @@ 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 wxWindowMSW::HandleActivate(int state, bool WXUNUSED(minimized), WXHWND WXUNUSED(activate)) { @@ -2654,8 +3270,13 @@ bool wxWindow::HandleActivate(int state, return GetEventHandler()->ProcessEvent(event); } -bool wxWindow::HandleSetFocus(WXHWND WXUNUSED(hwnd)) +bool wxWindowMSW::HandleSetFocus(WXHWND hwnd) { + // notify the parent keeping track of focus for the kbd navigation + // purposes that we got it + wxChildFocusEvent eventFocus((wxWindow *)this); + (void)GetEventHandler()->ProcessEvent(eventFocus); + #if wxUSE_CARET // Deal with caret if ( m_caret ) @@ -2664,13 +3285,25 @@ bool wxWindow::HandleSetFocus(WXHWND WXUNUSED(hwnd)) } #endif // wxUSE_CARET +#if wxUSE_TEXTCTRL + // If it's a wxTextCtrl don't send the event as it will be done + // after the control gets to process it from EN_FOCUS handler + if ( wxDynamicCastThis(wxTextCtrl) ) + { + return FALSE; + } +#endif // wxUSE_TEXTCTRL + wxFocusEvent event(wxEVT_SET_FOCUS, m_windowId); event.SetEventObject(this); + // wxFindWinFromHandle() may return NULL, it is ok + event.SetWindow(wxFindWinFromHandle(hwnd)); + return GetEventHandler()->ProcessEvent(event); } -bool wxWindow::HandleKillFocus(WXHWND WXUNUSED(hwnd)) +bool wxWindowMSW::HandleKillFocus(WXHWND hwnd) { #if wxUSE_CARET // Deal with caret @@ -2680,9 +3313,29 @@ bool wxWindow::HandleKillFocus(WXHWND WXUNUSED(hwnd)) } #endif // wxUSE_CARET +#if wxUSE_TEXTCTRL + // If it's a wxTextCtrl don't send the event as it will be done + // after the control gets to process it. + wxTextCtrl *ctrl = wxDynamicCastThis(wxTextCtrl); + if ( ctrl ) + { + return FALSE; + } +#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); + // wxFindWinFromHandle() may return NULL, it is ok + event.SetWindow(wxFindWinFromHandle(hwnd)); + return GetEventHandler()->ProcessEvent(event); } @@ -2690,7 +3343,7 @@ bool wxWindow::HandleKillFocus(WXHWND WXUNUSED(hwnd)) // miscellaneous // --------------------------------------------------------------------------- -bool wxWindow::HandleShow(bool show, int status) +bool wxWindowMSW::HandleShow(bool show, int WXUNUSED(status)) { wxShowEvent event(GetId(), show); event.m_eventObject = this; @@ -2698,7 +3351,7 @@ bool wxWindow::HandleShow(bool show, int status) return GetEventHandler()->ProcessEvent(event); } -bool wxWindow::HandleInitDialog(WXHWND WXUNUSED(hWndFocus)) +bool wxWindowMSW::HandleInitDialog(WXHWND WXUNUSED(hWndFocus)) { wxInitDialogEvent event(GetId()); event.m_eventObject = this; @@ -2706,14 +3359,15 @@ bool wxWindow::HandleInitDialog(WXHWND WXUNUSED(hWndFocus)) return GetEventHandler()->ProcessEvent(event); } -bool wxWindow::HandleDropFiles(WXWPARAM wParam) +bool wxWindowMSW::HandleDropFiles(WXWPARAM wParam) { +#if defined (__WXMICROWIN__) || defined(__WXWINCE__) + return FALSE; +#else // __WXMICROWIN__ HDROP hFilesInfo = (HDROP) wParam; - POINT dropPoint; - DragQueryPoint(hFilesInfo, (LPPOINT) &dropPoint); // Get the total number of files dropped - WORD gwFilesDropped = (WORD)::DragQueryFile + UINT gwFilesDropped = ::DragQueryFile ( (HDROP)hFilesInfo, (UINT)-1, @@ -2722,29 +3376,35 @@ bool wxWindow::HandleDropFiles(WXWPARAM wParam) ); wxString *files = new wxString[gwFilesDropped]; - int wIndex; - for (wIndex=0; wIndex < (int)gwFilesDropped; wIndex++) + for ( UINT wIndex = 0; wIndex < gwFilesDropped; wIndex++ ) { - DragQueryFile (hFilesInfo, wIndex, (LPTSTR) wxBuffer, 1000); - files[wIndex] = wxBuffer; + // first get the needed buffer length (+1 for terminating NUL) + size_t len = ::DragQueryFile(hFilesInfo, wIndex, NULL, 0) + 1; + + // and now get the file name + ::DragQueryFile(hFilesInfo, wIndex, + wxStringBuffer(files[wIndex], len), len); } DragFinish (hFilesInfo); wxDropFilesEvent event(wxEVT_DROP_FILES, gwFilesDropped, files); event.m_eventObject = this; - event.m_pos.x = dropPoint.x; event.m_pos.x = dropPoint.y; - bool rc = GetEventHandler()->ProcessEvent(event); - - delete[] files; + POINT dropPoint; + DragQueryPoint(hFilesInfo, (LPPOINT) &dropPoint); + event.m_pos.x = dropPoint.x; + event.m_pos.y = dropPoint.y; - return rc; + return GetEventHandler()->ProcessEvent(event); +#endif } -bool wxWindow::HandleSetCursor(WXHWND hWnd, - short nHitTest, - int WXUNUSED(mouseMsg)) + +bool wxWindowMSW::HandleSetCursor(WXHWND WXUNUSED(hWnd), + short nHitTest, + int WXUNUSED(mouseMsg)) { +#ifndef __WXMICROWIN__ // the logic is as follows: // -1. don't set cursor for non client area, including but not limited to // the title bar, scrollbars, &c @@ -2763,15 +3423,10 @@ bool wxWindow::HandleSetCursor(WXHWND hWnd, // 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) ) { wxLogLastError(wxT("GetCursorPos")); } -#else - // In WIN16 it doesn't return a value. - ::GetCursorPos(&pt); -#endif int x = pt.x, y = pt.y; @@ -2816,11 +3471,14 @@ bool wxWindow::HandleSetCursor(WXHWND hWnd, if ( hcursor ) { +// wxLogDebug("HandleSetCursor: Setting cursor %ld", (long) hcursor); + ::SetCursor(hcursor); // cursor set, stop here return TRUE; } +#endif // __WXMICROWIN__ // pass up the window chain return FALSE; @@ -2830,49 +3488,64 @@ bool wxWindow::HandleSetCursor(WXHWND hWnd, // owner drawn stuff // --------------------------------------------------------------------------- -bool wxWindow::MSWOnDrawItem(int id, WXDRAWITEMSTRUCT *itemStruct) +bool wxWindowMSW::MSWOnDrawItem(int id, WXDRAWITEMSTRUCT *itemStruct) { #if wxUSE_OWNER_DRAWN + +#if wxUSE_MENUS_NATIVE // is it a menu item? - if ( id == 0 ) + DRAWITEMSTRUCT *pDrawStruct = (DRAWITEMSTRUCT *)itemStruct; + if ( id == 0 && pDrawStruct->CtlType == ODT_MENU ) { - DRAWITEMSTRUCT *pDrawStruct = (DRAWITEMSTRUCT *)itemStruct; wxMenuItem *pMenuItem = (wxMenuItem *)(pDrawStruct->itemData); wxCHECK( pMenuItem->IsKindOf(CLASSINFO(wxMenuItem)), FALSE ); - // prepare to call OnDrawItem() - wxDC dc; - dc.SetHDC((WXHDC)pDrawStruct->hDC, FALSE); + // prepare to call OnDrawItem(): notice using of wxDCTemp to prevent + // the DC from being released + wxDCTemp dc((WXHDC)pDrawStruct->hDC); wxRect rect(pDrawStruct->rcItem.left, pDrawStruct->rcItem.top, pDrawStruct->rcItem.right - pDrawStruct->rcItem.left, pDrawStruct->rcItem.bottom - pDrawStruct->rcItem.top); return pMenuItem->OnDrawItem - ( - dc, rect, - (wxOwnerDrawn::wxODAction)pDrawStruct->itemAction, - (wxOwnerDrawn::wxODStatus)pDrawStruct->itemState - ); + ( + dc, + rect, + (wxOwnerDrawn::wxODAction)pDrawStruct->itemAction, + (wxOwnerDrawn::wxODStatus)pDrawStruct->itemState + ); } +#endif // wxUSE_MENUS_NATIVE + +#endif // USE_OWNER_DRAWN +#if wxUSE_CONTROLS + +#if wxUSE_OWNER_DRAWN wxWindow *item = FindItem(id); if ( item && item->IsKindOf(CLASSINFO(wxControl)) ) - { return ((wxControl *)item)->MSWOnDraw(itemStruct); - } +#elif !defined(__WXUNIVERSAL__) + // we may still have owner-drawn buttons internally because we have to make + // them owner-drawn to support colour change + wxWindow *item = FindItem(id); + if ( item && item->IsKindOf(CLASSINFO(wxButton)) ) + return ((wxButton *)item)->MSWOnDraw(itemStruct); #endif // USE_OWNER_DRAWN +#endif // wxUSE_CONTROLS + return FALSE; } -bool wxWindow::MSWOnMeasureItem(int id, WXMEASUREITEMSTRUCT *itemStruct) +bool wxWindowMSW::MSWOnMeasureItem(int id, WXMEASUREITEMSTRUCT *itemStruct) { #if wxUSE_OWNER_DRAWN // is it a menu item? - if ( id == 0 ) + MEASUREITEMSTRUCT *pMeasureStruct = (MEASUREITEMSTRUCT *)itemStruct; + if ( id == 0 && pMeasureStruct->CtlType == ODT_MENU ) { - MEASUREITEMSTRUCT *pMeasureStruct = (MEASUREITEMSTRUCT *)itemStruct; wxMenuItem *pMenuItem = (wxMenuItem *)(pMeasureStruct->itemData); wxCHECK( pMenuItem->IsKindOf(CLASSINFO(wxMenuItem)), FALSE ); @@ -2894,15 +3567,27 @@ bool wxWindow::MSWOnMeasureItem(int id, WXMEASUREITEMSTRUCT *itemStruct) // colours and palettes // --------------------------------------------------------------------------- -bool wxWindow::HandleSysColorChange() +bool wxWindowMSW::HandleSysColorChange() { wxSysColourChangedEvent event; event.SetEventObject(this); + (void)GetEventHandler()->ProcessEvent(event); + + // always let the system carry on the default processing to allow the + // native controls to react to the colours update + return FALSE; +} + +bool wxWindowMSW::HandleDisplayChange() +{ + wxDisplayChangedEvent event; + event.SetEventObject(this); + return GetEventHandler()->ProcessEvent(event); } -bool wxWindow::HandleCtlColor(WXHBRUSH *brush, +bool wxWindowMSW::HandleCtlColor(WXHBRUSH *brush, WXHDC pDC, WXHWND pWnd, WXUINT nCtlColor, @@ -2910,38 +3595,82 @@ bool wxWindow::HandleCtlColor(WXHBRUSH *brush, WXWPARAM wParam, WXLPARAM lParam) { +#ifndef __WXMICROWIN__ WXHBRUSH hBrush = 0; +#ifdef __WXWINCE__ + if (FALSE) +#else if ( nCtlColor == CTLCOLOR_DLG ) +#endif { hBrush = OnCtlColor(pDC, pWnd, nCtlColor, message, wParam, lParam); } +#if wxUSE_CONTROLS else { wxControl *item = (wxControl *)FindItemByHWND(pWnd, TRUE); if ( item ) hBrush = item->OnCtlColor(pDC, pWnd, nCtlColor, message, wParam, lParam); } +#endif // wxUSE_CONTROLS if ( hBrush ) *brush = hBrush; return hBrush != 0; +#else // __WXMICROWIN__ + return FALSE; +#endif } // Define for each class of dialog and control -WXHBRUSH wxWindow::OnCtlColor(WXHDC hDC, - WXHWND hWnd, - WXUINT nCtlColor, - WXUINT message, - WXWPARAM wParam, - WXLPARAM lParam) +WXHBRUSH wxWindowMSW::OnCtlColor(WXHDC WXUNUSED(hDC), + WXHWND WXUNUSED(hWnd), + WXUINT WXUNUSED(nCtlColor), + WXUINT WXUNUSED(message), + WXWPARAM WXUNUSED(wParam), + WXLPARAM WXUNUSED(lParam)) { return (WXHBRUSH)0; } -bool wxWindow::HandlePaletteChanged(WXHWND hWndPalChange) +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)); @@ -2949,8 +3678,39 @@ bool wxWindow::HandlePaletteChanged(WXHWND hWndPalChange) return GetEventHandler()->ProcessEvent(event); } -bool wxWindow::HandleQueryNewPalette() +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); @@ -2958,31 +3718,123 @@ bool wxWindow::HandleQueryNewPalette() } // Responds to colour changes: passes event on to children. -void wxWindow::OnSysColourChanged(wxSysColourChangedEvent& event) +void wxWindowMSW::OnSysColourChanged(wxSysColourChangedEvent& WXUNUSED(event)) { - wxNode *node = GetChildren().First(); + // the top level window also reset the standard colour map as it might have + // changed (there is no need to do it for the non top level windows as we + // only have to do it once) + if ( IsTopLevel() ) + { + // FIXME-MT + gs_hasStdCmap = FALSE; + } + wxWindowList::compatibility_iterator node = GetChildren().GetFirst(); while ( node ) { - // Only propagate to non-top-level windows - wxWindow *win = (wxWindow *)node->Data(); - if ( win->GetParent() ) + // Only propagate to non-top-level windows because Windows already + // sends this event to all top-level ones + wxWindow *win = node->GetData(); + if ( !win->IsTopLevel() ) { - wxSysColourChangedEvent event2; - event.m_eventObject = win; - win->GetEventHandler()->ProcessEvent(event2); + // we need to send the real WM_SYSCOLORCHANGE and not just trigger + // EVT_SYS_COLOUR_CHANGED call because the latter wouldn't work for + // the standard controls + ::SendMessage(GetHwndOf(win), WM_SYSCOLORCHANGE, 0, 0); } - node = node->Next(); + node = node->GetNext(); + } + + // update the colours we use if they were not set explicitly by the user: + // this must be done or OnCtlColor() would continue to use the old colours + if ( !m_hasFgCol ) + { + m_foregroundColour = wxSystemSettings:: + GetSystemColour(wxSYS_COLOUR_WINDOWTEXT); + } + + if ( !m_hasBgCol ) + { + m_backgroundColour = wxSystemSettings:: + GetSystemColour(wxSYS_COLOUR_BTNFACE); + } +} + +extern wxCOLORMAP *wxGetStdColourMap() +{ + static COLORREF s_stdColours[wxSTD_COL_MAX]; + static wxCOLORMAP s_cmap[wxSTD_COL_MAX]; + + if ( !gs_hasStdCmap ) + { + static bool s_coloursInit = FALSE; + + if ( !s_coloursInit ) + { + // When a bitmap is loaded, the RGB values can change (apparently + // because Windows adjusts them to care for the old programs always + // using 0xc0c0c0 while the transparent colour for the new Windows + // versions is different). But we do this adjustment ourselves so + // we want to avoid Windows' "help" and for this we need to have a + // reference bitmap which can tell us what the RGB values change + // to. + wxBitmap stdColourBitmap(_T("wxBITMAP_STD_COLOURS")); + if ( stdColourBitmap.Ok() ) + { + // the pixels in the bitmap must correspond to wxSTD_COL_XXX! + wxASSERT_MSG( stdColourBitmap.GetWidth() == wxSTD_COL_MAX, + _T("forgot to update wxBITMAP_STD_COLOURS!") ); + + wxMemoryDC memDC; + memDC.SelectObject(stdColourBitmap); + + wxColour colour; + for ( size_t i = 0; i < WXSIZEOF(s_stdColours); i++ ) + { + memDC.GetPixel(i, 0, &colour); + s_stdColours[i] = wxColourToRGB(colour); + } + } + else // wxBITMAP_STD_COLOURS couldn't be loaded + { + s_stdColours[0] = RGB(000,000,000); // black + s_stdColours[1] = RGB(128,128,128); // dark grey + s_stdColours[2] = RGB(192,192,192); // light grey + s_stdColours[3] = RGB(255,255,255); // white + //s_stdColours[4] = RGB(000,000,255); // blue + //s_stdColours[5] = RGB(255,000,255); // magenta + } + + s_coloursInit = TRUE; + } + + gs_hasStdCmap = TRUE; + + // create the colour map +#define INIT_CMAP_ENTRY(col) \ + s_cmap[wxSTD_COL_##col].from = s_stdColours[wxSTD_COL_##col]; \ + s_cmap[wxSTD_COL_##col].to = ::GetSysColor(COLOR_##col) + + INIT_CMAP_ENTRY(BTNTEXT); + INIT_CMAP_ENTRY(BTNSHADOW); + INIT_CMAP_ENTRY(BTNFACE); + INIT_CMAP_ENTRY(BTNHIGHLIGHT); + +#undef INIT_CMAP_ENTRY } + + return s_cmap; } // --------------------------------------------------------------------------- // painting // --------------------------------------------------------------------------- -bool wxWindow::HandlePaint() +bool wxWindowMSW::HandlePaint() { -#ifdef __WIN32__ +// if (GetExtraStyle() & wxWS_EX_THEMED_BACKGROUND) +// return FALSE; + HRGN hRegion = ::CreateRectRgn(0, 0, 0, 0); // Dummy call to get a handle if ( !hRegion ) wxLogLastError(wxT("CreateRectRgn")); @@ -2990,41 +3842,69 @@ bool wxWindow::HandlePaint() wxLogLastError(wxT("GetUpdateRgn")); m_updateRegion = wxRegion((WXHRGN) hRegion); -#else - RECT updateRect; - ::GetUpdateRect(GetHwnd(), & updateRect, FALSE); - - m_updateRegion = wxRegion(updateRect.left, updateRect.top, - updateRect.right - updateRect.left, - updateRect.bottom - updateRect.top); -#endif wxPaintEvent event(m_windowId); event.SetEventObject(this); - return GetEventHandler()->ProcessEvent(event); + bool processed = GetEventHandler()->ProcessEvent(event); + + // note that we must generate NC event after the normal one as otherwise + // BeginPaint() will happily overwrite our decorations with the background + // colour + wxNcPaintEvent eventNc(m_windowId); + eventNc.SetEventObject(this); + GetEventHandler()->ProcessEvent(eventNc); + + return processed; } // Can be called from an application's OnPaint handler -void wxWindow::OnPaint(wxPaintEvent& event) +void wxWindowMSW::OnPaint(wxPaintEvent& event) { +#ifdef __WXUNIVERSAL__ + event.Skip(); +#else HDC hDC = (HDC) wxPaintDC::FindDCInCache((wxWindow*) event.GetEventObject()); if (hDC != 0) { MSWDefWindowProc(WM_PAINT, (WPARAM) hDC, 0); } +#endif } -bool wxWindow::HandleEraseBkgnd(WXHDC hdc) +bool wxWindowMSW::HandleEraseBkgnd(WXHDC hdc) { // Prevents flicker when dragging if ( ::IsIconic(GetHwnd()) ) return TRUE; - wxDC dc; +#if 0 + if (GetParent() && GetParent()->GetExtraStyle() & wxWS_EX_THEMED_BACKGROUND) + { + return FALSE; + } + + if (GetExtraStyle() & wxWS_EX_THEMED_BACKGROUND) + { + if (wxUxThemeEngine::Get()) + { + WXHTHEME hTheme = wxUxThemeEngine::Get()->m_pfnOpenThemeData(GetHWND(), L"TAB"); + if (hTheme) + { + WXURECT rect; + ::GetClientRect((HWND) GetHWND(), (RECT*) & rect); + wxUxThemeEngine::Get()->m_pfnDrawThemeBackground(hTheme, hdc, 10 /* TABP_BODY */, 0, &rect, &rect); + wxUxThemeEngine::Get()->m_pfnCloseThemeData(hTheme); + return TRUE; + } + } + } +#endif + + wxDCTemp dc(hdc); dc.SetHDC(hdc); - dc.SetWindow(this); + dc.SetWindow((wxWindow *)this); dc.BeginDrawing(); wxEraseEvent event(m_windowId, &dc); @@ -3032,13 +3912,14 @@ bool wxWindow::HandleEraseBkgnd(WXHDC hdc) bool rc = GetEventHandler()->ProcessEvent(event); dc.EndDrawing(); + + // must be called manually as ~wxDC doesn't do anything for wxDCTemp dc.SelectOldObjects(hdc); - dc.SetHDC((WXHDC) NULL); return rc; } -void wxWindow::OnEraseBackground(wxEraseEvent& event) +void wxWindowMSW::OnEraseBackground(wxEraseEvent& event) { RECT rect; ::GetClientRect(GetHwnd(), &rect); @@ -3052,18 +3933,23 @@ void wxWindow::OnEraseBackground(wxEraseEvent& event) HDC hdc = (HDC)event.GetDC()->GetHDC(); +#ifndef __WXWINCE__ int mode = ::SetMapMode(hdc, MM_TEXT); +#endif ::FillRect(hdc, &rect, hBrush); ::DeleteObject(hBrush); + +#ifndef __WXWINCE__ ::SetMapMode(hdc, mode); +#endif } // --------------------------------------------------------------------------- // moving and resizing // --------------------------------------------------------------------------- -bool wxWindow::HandleMinimize() +bool wxWindowMSW::HandleMinimize() { wxIconizeEvent event(m_windowId); event.SetEventObject(this); @@ -3071,7 +3957,7 @@ bool wxWindow::HandleMinimize() return GetEventHandler()->ProcessEvent(event); } -bool wxWindow::HandleMaximize() +bool wxWindowMSW::HandleMaximize() { wxMaximizeEvent event(m_windowId); event.SetEventObject(this); @@ -3079,7 +3965,7 @@ bool wxWindow::HandleMaximize() return GetEventHandler()->ProcessEvent(event); } -bool wxWindow::HandleMove(int x, int y) +bool wxWindowMSW::HandleMove(int x, int y) { wxMoveEvent event(wxPoint(x, y), m_windowId); event.SetEventObject(this); @@ -3087,90 +3973,112 @@ bool wxWindow::HandleMove(int x, int y) return GetEventHandler()->ProcessEvent(event); } -bool wxWindow::HandleSize(int w, int h, WXUINT WXUNUSED(flag)) +bool wxWindowMSW::HandleMoving(wxRect& rect) +{ + wxMoveEvent event(rect, m_windowId); + event.SetEventObject(this); + + bool rc = GetEventHandler()->ProcessEvent(event); + if (rc) + rect = event.GetRect(); + return rc; +} + +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); } -bool wxWindow::HandleGetMinMaxInfo(void *mmInfo) +bool wxWindowMSW::HandleSizing(wxRect& rect) +{ + wxSizeEvent event(rect, m_windowId); + event.SetEventObject(this); + + bool rc = GetEventHandler()->ProcessEvent(event); + if (rc) + rect = event.GetRect(); + return rc; +} + +bool wxWindowMSW::HandleGetMinMaxInfo(void *mmInfo) { +#ifdef __WXWINCE__ + return FALSE; +#else MINMAXINFO *info = (MINMAXINFO *)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; } return rc; -} - -// generate an artificial resize event -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 // --------------------------------------------------------------------------- -bool wxWindow::HandleCommand(WXWORD id, WXWORD cmd, WXHWND control) +bool wxWindowMSW::HandleCommand(WXWORD id, WXWORD cmd, WXHWND control) { - if ( wxCurrentPopupMenu ) +#if wxUSE_MENUS_NATIVE + if ( !cmd && wxCurrentPopupMenu ) { wxMenu *popupMenu = wxCurrentPopupMenu; wxCurrentPopupMenu = NULL; return popupMenu->MSWCommand(cmd, id); } +#endif // wxUSE_MENUS_NATIVE - wxWindow *win = (wxWindow*) NULL; - if ( cmd == 0 || cmd == 1 ) // menu or accel - use id + wxWindow *win = NULL; + + // first try to find it from HWND - this works even with the broken + // programs using the same ids for different controls + if ( control ) { - // must cast to a signed type before comparing with other ids! - win = FindItem((signed short)id); + win = wxFindWinFromHandle(control); } - if (!win && control) + // try the id + if ( !win ) { - // find it from HWND - this works even with the broken programs using - // the same ids for different controls - win = wxFindWinFromHandle(control); + // must cast to a signed type before comparing with other ids! + win = FindItem((signed short)id); } if ( win ) @@ -3194,12 +4102,24 @@ bool wxWindow::HandleCommand(WXWORD id, WXWORD cmd, WXHWND control) return GetEventHandler()->ProcessEvent(event); } +#if wxUSE_SPINCTRL && !defined(__WXUNIVERSAL__) + 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; } -bool wxWindow::HandleSysCommand(WXWPARAM wParam, WXLPARAM lParam) +bool wxWindowMSW::HandleSysCommand(WXWPARAM wParam, WXLPARAM WXUNUSED(lParam)) { +#ifndef __WXWINCE__ // 4 bits are reserved switch ( wParam & 0xFFFFFFF0 ) { @@ -3209,6 +4129,7 @@ bool wxWindow::HandleSysCommand(WXWPARAM wParam, WXLPARAM lParam) case SC_MINIMIZE: return HandleMinimize(); } +#endif return FALSE; } @@ -3217,28 +4138,101 @@ bool wxWindow::HandleSysCommand(WXWPARAM wParam, WXLPARAM lParam) // mouse events // --------------------------------------------------------------------------- -void wxWindow::InitMouseEvent(wxMouseEvent& event, int x, int y, WXUINT flags) -{ - event.m_x = x; - event.m_y = y; - event.m_shiftDown = ((flags & MK_SHIFT) != 0); - event.m_controlDown = ((flags & MK_CONTROL) != 0); - 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; +void wxWindowMSW::InitMouseEvent(wxMouseEvent& event, + int x, int y, + WXUINT flags) +{ + // our client coords are not quite the same as Windows ones + wxPoint pt = GetClientAreaOrigin(); + event.m_x = x - pt.x; + event.m_y = y - pt.y; + + event.m_shiftDown = (flags & MK_SHIFT) != 0; + event.m_controlDown = (flags & MK_CONTROL) != 0; + 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; + // Returns different negative values on WinME and WinNT, + // so simply test for negative value. + event.m_altDown = ::GetKeyState(VK_MENU) < 0; + event.SetTimestamp(s_currentMsg.time); event.m_eventObject = this; + event.SetId(GetId()); #if wxUSE_MOUSEEVENT_HACK m_lastMouseX = x; m_lastMouseY = y; m_lastMouseEvent = event.GetEventType(); #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 __WXWINCE__ + hwndUnderMouse = ::ChildWindowFromPoint + ( + hwnd, + pt + ); +#else + hwndUnderMouse = ::ChildWindowFromPointEx + ( + hwnd, + pt, + CWP_SKIPINVISIBLE | + CWP_SKIPDISABLED | + CWP_SKIPTRANSPARENT + ); +#endif + + if ( !hwndUnderMouse || hwndUnderMouse == hwnd ) + { + // 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 wxWindow::HandleMouseEvent(WXUINT msg, int x, int y, WXUINT flags) +bool wxWindowMSW::HandleMouseEvent(WXUINT msg, int x, int y, WXUINT flags) { // the mouse events take consecutive IDs from WM_MOUSEFIRST to // WM_MOUSELAST, so it's enough to substract WM_MOUSEMOVE == WM_MOUSEFIRST @@ -3264,17 +4258,24 @@ bool wxWindow::HandleMouseEvent(WXUINT msg, int x, int y, WXUINT flags) return GetEventHandler()->ProcessEvent(event); } -bool wxWindow::HandleMouseMove(int x, int y, WXUINT flags) +bool wxWindowMSW::HandleMouseMove(int x, int y, WXUINT flags) { if ( !m_mouseInWindow ) { - // Generate an ENTER event - m_mouseInWindow = TRUE; + // it would be wrong to assume that just because we get a mouse move + // event that the mouse is inside the window: although this is usually + // true, it is not if we had captured the mouse, so we need to check + // the mouse coordinates here + if ( !HasCapture() || IsMouseInWindow() ) + { + // Generate an ENTER event + m_mouseInWindow = TRUE; - wxMouseEvent event(wxEVT_ENTER_WINDOW); - InitMouseEvent(event, x, y, flags); + wxMouseEvent event(wxEVT_ENTER_WINDOW); + InitMouseEvent(event, x, y, flags); - (void)GetEventHandler()->ProcessEvent(event); + (void)GetEventHandler()->ProcessEvent(event); + } } #if wxUSE_MOUSEEVENT_HACK @@ -3284,7 +4285,7 @@ bool wxWindow::HandleMouseMove(int x, int y, WXUINT flags) if ( (m_lastMouseEvent == wxEVT_RIGHT_DOWN || m_lastMouseEvent == wxEVT_LEFT_DOWN || m_lastMouseEvent == wxEVT_MIDDLE_DOWN) && - (m_lastMouseX == event.m_x && m_lastMouseY == event.m_y) ) + (m_lastMouseX == x && m_lastMouseY == y) ) { m_lastMouseEvent = wxEVT_MOTION; @@ -3295,15 +4296,54 @@ bool wxWindow::HandleMouseMove(int x, int y, WXUINT flags) return HandleMouseEvent(WM_MOUSEMOVE, x, y, flags); } + +bool wxWindowMSW::HandleMouseWheel(WXWPARAM wParam, WXLPARAM lParam) +{ +#if wxUSE_MOUSEWHEEL + wxMouseEvent event(wxEVT_MOUSEWHEEL); + InitMouseEvent(event, + GET_X_LPARAM(lParam), + GET_Y_LPARAM(lParam), + LOWORD(wParam)); + event.m_wheelRotation = (short)HIWORD(wParam); + event.m_wheelDelta = WHEEL_DELTA; + + static int s_linesPerRotation = -1; + if ( s_linesPerRotation == -1 ) + { + if ( !::SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, + &s_linesPerRotation, 0)) + { + // this is not supposed to happen + wxLogLastError(_T("SystemParametersInfo(GETWHEELSCROLLLINES)")); + + // the default is 3, so use it if SystemParametersInfo() failed + s_linesPerRotation = 3; + } + } + + event.m_linesPerAction = s_linesPerRotation; + return GetEventHandler()->ProcessEvent(event); + +#else + (void) wParam; + (void) lParam; + + return FALSE; +#endif +} + + // --------------------------------------------------------------------------- // 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 wxWindowMSW::CreateKeyEvent(wxEventType evType, + int id, + WXLPARAM lParam, + WXWPARAM wParam) const { wxKeyEvent event(evType); event.SetId(GetId()); @@ -3313,6 +4353,8 @@ wxKeyEvent wxWindow::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 @@ -3331,14 +4373,16 @@ wxKeyEvent wxWindow::CreateKeyEvent(wxEventType evType, // 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 wxWindowMSW::HandleChar(WXWPARAM wParam, WXLPARAM lParam, bool isASCII) { bool ctrlDown = FALSE; 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) ) { @@ -3358,33 +4402,40 @@ bool wxWindow::HandleChar(WXWORD 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 wxWindow::HandleKeyDown(WXWORD wParam, WXLPARAM lParam) +bool wxWindowMSW::HandleKeyDown(WXWPARAM wParam, WXLPARAM lParam) { int id = wxCharCodeMSWToWX(wParam); @@ -3396,7 +4447,7 @@ bool wxWindow::HandleKeyDown(WXWORD 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; @@ -3406,7 +4457,7 @@ bool wxWindow::HandleKeyDown(WXWORD wParam, WXLPARAM lParam) return FALSE; } -bool wxWindow::HandleKeyUp(WXWORD wParam, WXLPARAM lParam) +bool wxWindowMSW::HandleKeyUp(WXWPARAM wParam, WXLPARAM lParam) { int id = wxCharCodeMSWToWX(wParam); @@ -3418,7 +4469,7 @@ bool wxWindow::HandleKeyUp(WXWORD 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; } @@ -3426,12 +4477,75 @@ bool wxWindow::HandleKeyUp(WXWORD wParam, WXLPARAM lParam) return FALSE; } +int wxWindowMSW::HandleMenuChar(int chAccel, WXLPARAM lParam) +{ + // FIXME: implement GetMenuItemCount for WinCE, possibly + // in terms of GetMenuItemInfo +#ifndef __WXWINCE__ + 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 to get the menu text? + { + // it's not fatal, so don't show error, but still log + // it + wxLogLastError(_T("GetMenuItemInfo")); + } + } +#endif + return wxNOT_FOUND; +} + // --------------------------------------------------------------------------- // joystick // --------------------------------------------------------------------------- -bool wxWindow::HandleJoystickEvent(WXUINT msg, int x, int y, WXUINT flags) +bool wxWindowMSW::HandleJoystickEvent(WXUINT msg, int x, int y, WXUINT flags) { +#ifdef JOY_BUTTON1 int change = 0; if ( flags & JOY_BUTTON1CHG ) change = wxJOY_BUTTON1; @@ -3508,14 +4622,17 @@ bool wxWindow::HandleJoystickEvent(WXUINT msg, int x, int y, WXUINT flags) event.SetEventObject(this); return GetEventHandler()->ProcessEvent(event); +#else + return FALSE; +#endif } // --------------------------------------------------------------------------- // scrolling // --------------------------------------------------------------------------- -bool wxWindow::MSWOnScroll(int orientation, WXWORD wParam, - WXWORD pos, WXHWND control) +bool wxWindowMSW::MSWOnScroll(int orientation, WXWORD wParam, + WXWORD pos, WXHWND control) { if ( control ) { @@ -3557,16 +4674,13 @@ bool wxWindow::MSWOnScroll(int orientation, WXWORD wParam, case SB_THUMBPOSITION: 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); + WinStruct scrollInfo; scrollInfo.fMask = SIF_TRACKPOS; if ( !::GetScrollInfo(GetHwnd(), @@ -3579,7 +4693,6 @@ bool wxWindow::MSWOnScroll(int orientation, WXWORD wParam, event.SetPosition(scrollInfo.nTrackPos); } -#endif // Win32 event.m_eventType = wParam == SB_THUMBPOSITION ? wxEVT_SCROLLWIN_THUMBRELEASE @@ -3643,6 +4756,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; @@ -3669,14 +4783,11 @@ int wxCharCodeMSWToWX(int keySym) 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_MULTIPLY: id = WXK_NUMPAD_MULTIPLY; break; + case VK_ADD: id = WXK_NUMPAD_ADD; break; + case VK_SUBTRACT: id = WXK_NUMPAD_SUBTRACT; break; + case VK_DECIMAL: id = WXK_NUMPAD_DECIMAL; break; + case VK_DIVIDE: id = WXK_NUMPAD_DIVIDE; break; case VK_F1: id = WXK_F1; break; case VK_F2: id = WXK_F2; break; case VK_F3: id = WXK_F3; break; @@ -3703,6 +4814,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; } @@ -3746,11 +4876,11 @@ int wxCharCodeWXToMSW(int id, bool *isVirtual) case WXK_NUMPAD7: keySym = VK_NUMPAD7; break; case WXK_NUMPAD8: keySym = VK_NUMPAD8; break; case WXK_NUMPAD9: keySym = VK_NUMPAD9; break; - case WXK_MULTIPLY: keySym = VK_MULTIPLY; break; - case WXK_ADD: keySym = VK_ADD; break; - case WXK_SUBTRACT: keySym = VK_SUBTRACT; break; - case WXK_DECIMAL: keySym = VK_DECIMAL; break; - case WXK_DIVIDE: keySym = VK_DIVIDE; break; + case WXK_NUMPAD_MULTIPLY: keySym = VK_MULTIPLY; break; + case WXK_NUMPAD_ADD: keySym = VK_ADD; break; + case WXK_NUMPAD_SUBTRACT: keySym = VK_SUBTRACT; break; + case WXK_NUMPAD_DECIMAL: keySym = VK_DECIMAL; break; + case WXK_NUMPAD_DIVIDE: keySym = VK_DIVIDE; break; case WXK_F1: keySym = VK_F1; break; case WXK_F2: keySym = VK_F2; break; case WXK_F3: keySym = VK_F3; break; @@ -3810,35 +4940,55 @@ extern wxWindow *wxGetWindowFromHWND(WXHWND hWnd) win = wxFindWinFromHandle((WXHWND)hwnd); if ( !win ) { - // the radiobox pointer is stored in GWL_USERDATA only under Win32 -#ifdef __WIN32__ +#if wxUSE_RADIOBOX // 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 ) + if ( ::SendMessage(hwnd, WM_GETDLGCODE, 0, 0) & DLGC_RADIOBUTTON ) { win = (wxWindow *)::GetWindowLong(hwnd, GWL_USERDATA); } - else -#endif // Win32 + //else: it's a wxRadioButton, not a radiobutton from wxRadioBox +#endif // wxUSE_RADIOBOX + + // spin control text buddy window should be mapped to spin ctrl + // itself so try it too +#if wxUSE_SPINCTRL && !defined(__WXUNIVERSAL__) + if ( !win ) { - // hwnd is not a wxWindow, try its parent next below - hwnd = ::GetParent(hwnd); + win = wxSpinCtrl::GetSpinForTextCtrl((WXHWND)hwnd); } +#endif // wxUSE_SPINCTRL } - //else: it's a wxRadioButton, not a radiobutton from wxRadioBox } while ( hwnd && !win ) { - win = wxFindWinFromHandle((WXHWND)hwnd); + // this is a really ugly hack needed to avoid mistakenly returning the + // parent frame wxWindow for the find/replace modeless dialog HWND - + // this, in turn, is needed to call IsDialogMessage() from + // wxApp::ProcessMessage() as for this we must return NULL from here + // + // FIXME: this is clearly not the best way to do it but I think we'll + // need to change HWND <-> wxWindow code more heavily than I can + // do it now to fix it +#ifndef __WXMICROWIN__ + if ( ::GetWindow(hwnd, GW_OWNER) ) + { + // it's a dialog box, don't go upwards + break; + } +#endif + hwnd = ::GetParent(hwnd); + win = wxFindWinFromHandle((WXHWND)hwnd); } return win; } +#if !defined(__WXMICROWIN__) && !defined(__WXWINCE__) + // 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; @@ -3853,22 +5003,13 @@ void wxSetKeyboardHook(bool doIt) wxTheKeyboardHookProc = MakeProcInstance((FARPROC) wxKeyboardHook, wxGetInstance()); wxTheKeyboardHook = SetWindowsHookEx(WH_KEYBOARD, (HOOKPROC) wxTheKeyboardHookProc, wxGetInstance(), -#if defined(__WIN32__) && !defined(__TWIN32__) GetCurrentThreadId() // (DWORD)GetCurrentProcess()); // This is another possibility. Which is right? -#else - 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 } } @@ -3915,6 +5056,8 @@ wxKeyboardHook(int nCode, WORD wParam, DWORD lParam) return (int)CallNextHookEx(wxTheKeyboardHook, nCode, wParam, lParam); } +#endif // !__WXMICROWIN__ + #ifdef __WXDEBUG__ const char *wxGetMessageName(int message) { @@ -3974,7 +5117,6 @@ const char *wxGetMessageName(int message) case 0x0047: return "WM_WINDOWPOSCHANGED"; case 0x0048: return "WM_POWER"; -#ifdef __WIN32__ case 0x004A: return "WM_COPYDATA"; case 0x004B: return "WM_CANCELJOURNAL"; case 0x004E: return "WM_NOTIFY"; @@ -3990,7 +5132,6 @@ const char *wxGetMessageName(int message) case 0x007E: return "WM_DISPLAYCHANGE"; case 0x007F: return "WM_GETICON"; case 0x0080: return "WM_SETICON"; -#endif //WIN32 case 0x0081: return "WM_NCCREATE"; case 0x0082: return "WM_NCDESTROY"; @@ -4019,11 +5160,9 @@ const char *wxGetMessageName(int message) case 0x0107: return "WM_SYSDEADCHAR"; case 0x0108: return "WM_KEYLAST"; -#ifdef __WIN32__ case 0x010D: return "WM_IME_STARTCOMPOSITION"; case 0x010E: return "WM_IME_ENDCOMPOSITION"; case 0x010F: return "WM_IME_COMPOSITION"; -#endif //WIN32 case 0x0110: return "WM_INITDIALOG"; case 0x0111: return "WM_COMMAND"; @@ -4046,18 +5185,17 @@ const char *wxGetMessageName(int message) case 0x0207: return "WM_MBUTTONDOWN"; case 0x0208: return "WM_MBUTTONUP"; case 0x0209: return "WM_MBUTTONDBLCLK"; + case 0x020A: return "WM_MOUSEWHEEL"; case 0x0210: return "WM_PARENTNOTIFY"; case 0x0211: return "WM_ENTERMENULOOP"; case 0x0212: return "WM_EXITMENULOOP"; -#ifdef __WIN32__ case 0x0213: return "WM_NEXTMENU"; case 0x0214: return "WM_SIZING"; case 0x0215: return "WM_CAPTURECHANGED"; case 0x0216: return "WM_MOVING"; case 0x0218: return "WM_POWERBROADCAST"; case 0x0219: return "WM_DEVICECHANGE"; -#endif //WIN32 case 0x0220: return "WM_MDICREATE"; case 0x0221: return "WM_MDIDESTROY"; @@ -4072,7 +5210,6 @@ const char *wxGetMessageName(int message) case 0x0230: return "WM_MDISETMENU"; case 0x0233: return "WM_DROPFILES"; -#ifdef __WIN32__ case 0x0281: return "WM_IME_SETCONTEXT"; case 0x0282: return "WM_IME_NOTIFY"; case 0x0283: return "WM_IME_CONTROL"; @@ -4081,7 +5218,6 @@ const char *wxGetMessageName(int message) case 0x0286: return "WM_IME_CHAR"; case 0x0290: return "WM_IME_KEYDOWN"; case 0x0291: return "WM_IME_KEYUP"; -#endif //WIN32 case 0x0300: return "WM_CUT"; case 0x0301: return "WM_COPY"; @@ -4101,8 +5237,10 @@ const char *wxGetMessageName(int message) case 0x030F: return "WM_QUERYNEWPALETTE"; case 0x0310: return "WM_PALETTEISCHANGING"; case 0x0311: return "WM_PALETTECHANGED"; +#if wxUSE_HOTKEY + case 0x0312: return "WM_HOTKEY"; +#endif -#ifdef __WIN32__ // common controls messages - although they're not strictly speaking // standard, it's nice to decode them nevertheless @@ -4324,8 +5462,6 @@ const char *wxGetMessageName(int message) case WM_USER+61: return "TB_GETTEXTROWS"; case WM_USER+41: return "TB_GETBITMAPFLAGS"; -#endif //WIN32 - default: static char s_szBuf[128]; sprintf(s_szBuf, "", message); @@ -4334,7 +5470,8 @@ const char *wxGetMessageName(int message) } #endif //__WXDEBUG__ -static void TranslateKbdEventToMouse(wxWindow *win, int *x, int *y, WPARAM *flags) +static void TranslateKbdEventToMouse(wxWindowMSW *win, + int *x, int *y, WPARAM *flags) { // construct the key mask WPARAM& fwKeys = *flags; @@ -4353,7 +5490,7 @@ static void TranslateKbdEventToMouse(wxWindow *win, int *x, int *y, WPARAM *flag win->ScreenToClient(x, y); } -static TEXTMETRIC wxGetTextMetrics(const wxWindow *win) +static TEXTMETRIC wxGetTextMetrics(const wxWindowMSW *win) { // prepare the DC TEXTMETRIC tm; @@ -4389,9 +5526,16 @@ static TEXTMETRIC wxGetTextMetrics(const wxWindow *win) // position. wxWindow* wxFindWindowAtPointer(wxPoint& pt) { - // Use current message to find last mouse position - extern MSG s_currentMsg; - HWND hWndHit = ::WindowFromPoint(s_currentMsg.pt); + pt = wxGetMousePosition(); + return wxFindWindowAtPoint(pt); +} + +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; @@ -4408,7 +5552,63 @@ wxWindow* wxFindWindowAtPointer(wxPoint& pt) // Get the current mouse position. wxPoint wxGetMousePosition() { - extern MSG s_currentMsg; - return wxPoint(s_currentMsg.pt.x, s_currentMsg.pt.y); + POINT pt; + GetCursorPos( & pt ); + + return wxPoint(pt.x, pt.y); +} + +#if wxUSE_HOTKEY + +bool wxWindowMSW::RegisterHotKey(int hotkeyId, int modifiers, int keycode) +{ + UINT win_modifiers=0; + if ( modifiers & wxMOD_ALT ) + win_modifiers |= MOD_ALT; + if ( modifiers & wxMOD_SHIFT ) + win_modifiers |= MOD_SHIFT; + if ( modifiers & wxMOD_CONTROL ) + win_modifiers |= MOD_CONTROL; + if ( modifiers & wxMOD_WIN ) + win_modifiers |= MOD_WIN; + + if ( !::RegisterHotKey(GetHwnd(), hotkeyId, win_modifiers, keycode) ) + { + wxLogLastError(_T("RegisterHotKey")); + + return FALSE; + } + + return TRUE; +} + +bool wxWindowMSW::UnregisterHotKey(int hotkeyId) +{ + if ( !::UnregisterHotKey(GetHwnd(), hotkeyId) ) + { + wxLogLastError(_T("UnregisterHotKey")); + + return FALSE; + } + + return TRUE; +} + +bool wxWindowMSW::HandleHotKey(WXWPARAM wParam, WXLPARAM lParam) +{ + int hotkeyId = wParam; + int virtualKey = HIWORD(lParam); + int win_modifiers = LOWORD(lParam); + + wxKeyEvent event(CreateKeyEvent(wxEVT_HOTKEY, virtualKey, wParam, lParam)); + event.SetId(hotkeyId); + event.m_shiftDown = (win_modifiers & MOD_SHIFT) != 0; + event.m_controlDown = (win_modifiers & MOD_CONTROL) != 0; + event.m_altDown = (win_modifiers & MOD_ALT) != 0; + event.m_metaDown = (win_modifiers & MOD_WIN) != 0; + + return GetEventHandler()->ProcessEvent(event); } +#endif // wxUSE_HOTKEY +