X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/8e35ab96f64a0ad21470043492e0153980b95382..440ddb750f4b9d9a4bb968a4f2ee07f3b307cf17:/src/msw/window.cpp diff --git a/src/msw/window.cpp b/src/msw/window.cpp index 598b077b1a..6bb0616f0b 100644 --- a/src/msw/window.cpp +++ b/src/msw/window.cpp @@ -1,6 +1,6 @@ ///////////////////////////////////////////////////////////////////////////// -// Name: src/msw/windows.cpp -// Purpose: wxWindow +// Name: src/msw/window.cpp +// Purpose: wxWindowMSW // Author: Julian Smart // Modified by: VZ on 13.05.99: no more Default(), MSWOnXXX() reorganisation // Created: 04/01/98 @@ -17,10 +17,6 @@ // headers // --------------------------------------------------------------------------- -#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA) - #pragma implementation "window.h" -#endif - // For compilers that support precompilation, includes "wx.h". #include "wx/wxprec.h" @@ -28,11 +24,13 @@ #pragma hdrstop #endif +#include "wx/window.h" + #ifndef WX_PRECOMP #include "wx/msw/wrapwin.h" - #include "wx/window.h" + #include "wx/msw/wrapcctl.h" // include "properly" + #include "wx/msw/missing.h" #include "wx/accel.h" - #include "wx/setup.h" #include "wx/menu.h" #include "wx/dc.h" #include "wx/dcclient.h" @@ -47,12 +45,23 @@ #include "wx/msgdlg.h" #include "wx/settings.h" #include "wx/statbox.h" + #include "wx/sizer.h" + #include "wx/intl.h" + #include "wx/log.h" + #include "wx/textctrl.h" + #include "wx/menuitem.h" + #include "wx/module.h" #endif #if wxUSE_OWNER_DRAWN && !defined(__WXUNIVERSAL__) #include "wx/ownerdrw.h" #endif +#include "wx/hashmap.h" +#include "wx/evtloop.h" +#include "wx/power.h" +#include "wx/sysopt.h" + #if wxUSE_DRAG_AND_DROP #include "wx/dnd.h" #endif @@ -69,10 +78,9 @@ #endif #endif -#include "wx/menuitem.h" -#include "wx/log.h" - #include "wx/msw/private.h" +#include "wx/msw/private/keyboard.h" +#include "wx/msw/dcclient.h" #if wxUSE_TOOLTIPS #include "wx/tooltip.h" @@ -82,20 +90,21 @@ #include "wx/caret.h" #endif // wxUSE_CARET +#if wxUSE_RADIOBOX + #include "wx/radiobox.h" +#endif // wxUSE_RADIOBOX + #if wxUSE_SPINCTRL #include "wx/spinctrl.h" #endif // wxUSE_SPINCTRL -#include "wx/intl.h" -#include "wx/log.h" - -#include "wx/textctrl.h" #include "wx/notebook.h" #include "wx/listctrl.h" +#include "wx/dynlib.h" #include -#if (!defined(__GNUWIN32_OLD__) && !defined(__WXMICROWIN__) && !defined(__WXWINCE__)) || defined(__CYGWIN10__) +#if (!defined(__GNUWIN32_OLD__) && !defined(__WXMICROWIN__) /* && !defined(__WXWINCE__) */ ) || defined(__CYGWIN10__) #include #include #endif @@ -104,76 +113,134 @@ #include #endif -#if (!defined(__GNUWIN32_OLD__) && !defined(__WXMICROWIN__) && !defined(__WXWINCE__)) || defined(__CYGWIN10__) - #ifdef __WIN95__ - #include - #endif -#elif !defined(__WXMICROWIN__) && !defined(__WXWINCE__) // broken compiler - #include "wx/msw/gnuwin32/extra.h" +#if defined(__WXWINCE__) + #include "wx/msw/wince/missing.h" +#ifdef __POCKETPC__ + #include + #include + #include + #include +#endif #endif -#if defined(__GNUG__) -#include "wx/msw/missing.h" +#if wxUSE_UXTHEME + #include "wx/msw/uxtheme.h" + #define EP_EDITTEXT 1 + #define ETS_NORMAL 1 + #define ETS_HOT 2 + #define ETS_SELECTED 3 + #define ETS_DISABLED 4 + #define ETS_FOCUSED 5 + #define ETS_READONLY 6 + #define ETS_ASSIST 7 #endif -#if defined(__WXWINCE__) -#include "wx/msw/wince/missing.h" +// define the constants used by AnimateWindow() if our SDK doesn't have them +#ifndef AW_CENTER + #define AW_HOR_POSITIVE 0x00000001 + #define AW_HOR_NEGATIVE 0x00000002 + #define AW_VER_POSITIVE 0x00000004 + #define AW_VER_NEGATIVE 0x00000008 + #define AW_CENTER 0x00000010 + #define AW_HIDE 0x00010000 + #define AW_ACTIVATE 0x00020000 + #define AW_SLIDE 0x00040000 + #define AW_BLEND 0x00080000 #endif -// ---------------------------------------------------------------------------- -// standard constants not available with all compilers/headers -// ---------------------------------------------------------------------------- +#if defined(TME_LEAVE) && defined(WM_MOUSELEAVE) && wxUSE_DYNLIB_CLASS + #define HAVE_TRACKMOUSEEVENT +#endif // everything needed for TrackMouseEvent() -// This didn't appear in mingw until 2.95.2 -#ifndef SIF_TRACKPOS -#define SIF_TRACKPOS 16 +// set this to 1 to filter out duplicate mouse events, e.g. mouse move events +// when mouse position didnd't change +#ifdef __WXWINCE__ + #define wxUSE_MOUSEEVENT_HACK 0 +#else + #define wxUSE_MOUSEEVENT_HACK 1 #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 +// not all compilers/platforms have X button related declarations (notably +// Windows CE doesn't, and probably some old SDKs don't neither) +#ifdef WM_XBUTTONDOWN + #define wxHAS_XBUTTON #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 +#ifndef MAPVK_VK_TO_CHAR + #define MAPVK_VK_TO_CHAR 2 #endif // --------------------------------------------------------------------------- // global variables // --------------------------------------------------------------------------- -// the last Windows message we got (FIXME-MT) -extern MSG s_currentMsg; - #if wxUSE_MENUS_NATIVE -wxMenu *wxCurrentPopupMenu = NULL; -#endif // wxUSE_MENUS_NATIVE +extern wxMenu *wxCurrentPopupMenu; +#endif -extern const wxChar *wxCanvasClassName; +#if wxUSE_UXTHEME +// This is a hack used by the owner-drawn wxButton implementation to ensure +// that the brush used for erasing its background is correctly aligned with the +// control. +wxWindowMSW *wxWindowBeingErased = NULL; +#endif // wxUSE_UXTHEME + +namespace +{ // true if we had already created the std colour map, used by // wxGetStdColourMap() and wxWindow::OnSysColourChanged() (FIXME-MT) -static bool gs_hasStdCmap = FALSE; +bool gs_hasStdCmap = false; + +// last mouse event information we need to filter out the duplicates +#if wxUSE_MOUSEEVENT_HACK +struct MouseEventInfoDummy +{ + // mouse position (in screen coordinates) + wxPoint pos; + + // last mouse event type + wxEventType type; +} gs_lastMouseEvent; +#endif // wxUSE_MOUSEEVENT_HACK + +// hash containing the registered handlers for the custom messages +WX_DECLARE_HASH_MAP(int, wxWindow::MSWMessageHandler, + wxIntegerHash, wxIntegerEqual, + MSWMessageHandlers); + +MSWMessageHandlers gs_messageHandlers; + +// hash containing all our windows, it uses HWND keys and wxWindow* values +WX_DECLARE_HASH_MAP(HWND, wxWindow *, + wxPointerHash, wxPointerEqual, + WindowHandles); + +WindowHandles gs_windowHandles; + +#ifdef wxHAS_MSW_BACKGROUND_ERASE_HOOK + +// temporary override for WM_ERASEBKGND processing: we don't store this in +// wxWindow itself as we don't need it during most of the time so don't +// increase the size of all window objects unnecessarily +WX_DECLARE_HASH_MAP(wxWindow *, wxWindow *, + wxPointerHash, wxPointerEqual, + EraseBgHooks); + +EraseBgHooks gs_eraseBgHooks; + +#endif // wxHAS_MSW_BACKGROUND_ERASE_HOOK + +// If this variable is strictly positive, EVT_CHAR_HOOK is not generated for +// Escape key presses as it can't be intercepted because it's needed by some +// currently shown window, e.g. IME entry. +// +// This is currently global as we allow using UI from the main thread only +// anyhow but could be replaced with a thread-specific value in the future if +// needed. +int gs_modalEntryWindowCount = 0; + +} // anonymous namespace // --------------------------------------------------------------------------- // private functions @@ -184,24 +251,20 @@ LRESULT WXDLLEXPORT APIENTRY _EXPORT wxWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); -#ifdef __WXDEBUG__ - const char *wxGetMessageName(int message); -#endif //__WXDEBUG__ +#if wxDEBUG_LEVEL >= 2 + const wxChar *wxGetMessageName(int message); +#endif // wxDEBUG_LEVEL >= 2 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(wxWindowMSW *win, - int *x, int *y, WPARAM *flags); // get the text metrics for the current font static TEXTMETRIC wxGetTextMetrics(const wxWindowMSW *win); +#ifdef __WXWINCE__ // find the window for the mouse event at the specified position -static wxWindowMSW *FindWindowForMouseEvent(wxWindowMSW *win, int *x, int *y); //TW:REQ:Univ +static wxWindowMSW *FindWindowForMouseEvent(wxWindowMSW *win, int *x, int *y); +#endif // __WXWINCE__ // wrapper around BringWindowToTop() API static inline void wxBringWindowToTop(HWND hwnd) @@ -217,10 +280,12 @@ static inline void wxBringWindowToTop(HWND hwnd) // 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")); + wxLogLastError(wxT("SetWindowPos")); } } +#ifndef __WXWINCE__ + // ensure that all our parent windows have WS_EX_CONTROLPARENT style static void EnsureParentHasControlParentStyle(wxWindow *parent) { @@ -235,22 +300,39 @@ static void EnsureParentHasControlParentStyle(wxWindow *parent) get back to the initial (focused) window: as we do have this style, GetNextDlgTabItem() will leave this window and continue in its parent, but if the parent doesn't have it, it wouldn't recurse inside it later - on and so wouldn't have a chance of getting back to this window neither. + on and so wouldn't have a chance of getting back to this window either. */ -#ifndef __WXWINCE__ while ( parent && !parent->IsTopLevel() ) { - LONG exStyle = ::GetWindowLong(GetHwndOf(parent), GWL_EXSTYLE); + LONG exStyle = wxGetWindowExStyle(parent); if ( !(exStyle & WS_EX_CONTROLPARENT) ) { // force the parent to have this style - ::SetWindowLong(GetHwndOf(parent), GWL_EXSTYLE, - exStyle | WS_EX_CONTROLPARENT); + wxSetWindowExStyle(parent, exStyle | WS_EX_CONTROLPARENT); } parent = parent->GetParent(); } +} + #endif // !__WXWINCE__ + +// GetCursorPos can return an error, so use this function +// instead. +// Error originally observed with WinCE, but later using Remote Desktop +// to connect to XP. +void wxGetCursorPosMSW(POINT* pt) +{ + if (!GetCursorPos(pt)) + { +#ifdef __WXWINCE__ + wxLogLastError(wxT("GetCursorPos")); +#endif + DWORD pos = GetMessagePos(); + // the coordinates may be negative in multi-monitor systems + pt->x = GET_X_LPARAM(pos); + pt->y = GET_Y_LPARAM(pos); + } } // --------------------------------------------------------------------------- @@ -261,120 +343,13 @@ static void EnsureParentHasControlParentStyle(wxWindow *parent) // method #ifdef __WXUNIVERSAL__ IMPLEMENT_ABSTRACT_CLASS(wxWindowMSW, wxWindowBase) -#else // __WXMSW__ -#if wxUSE_EXTENDED_RTTI - -// windows that are created from a parent window during its Create method, eg. spin controls in a calendar controls -// must never been streamed out separately otherwise chaos occurs. Right now easiest is to test for negative ids, as -// windows with negative ids never can be recreated anyway - -bool wxWindowStreamingCallback( const wxObject *object, wxWriter * , wxPersister * , wxxVariantArray & ) -{ - const wxWindow * win = dynamic_cast(object) ; - if ( win && win->GetId() < 0 ) - return false ; - return true ; -} - -IMPLEMENT_DYNAMIC_CLASS_XTI_CALLBACK(wxWindow, wxWindowBase,"wx/window.h", wxWindowStreamingCallback) - -// make wxWindowList known before the property is used - -wxCOLLECTION_TYPE_INFO( wxWindow* , wxWindowList ) ; - -template<> void wxCollectionToVariantArray( wxWindowList const &theList, wxxVariantArray &value) -{ - wxListCollectionToVariantArray( theList , value ) ; -} - -WX_DEFINE_FLAGS( wxWindowStyle ) - -wxBEGIN_FLAGS( wxWindowStyle ) - // new style border flags, we put them first to - // use them for streaming out - - wxFLAGS_MEMBER(wxBORDER_SIMPLE) - wxFLAGS_MEMBER(wxBORDER_SUNKEN) - wxFLAGS_MEMBER(wxBORDER_DOUBLE) - wxFLAGS_MEMBER(wxBORDER_RAISED) - wxFLAGS_MEMBER(wxBORDER_STATIC) - wxFLAGS_MEMBER(wxBORDER_NONE) - - // old style border flags - wxFLAGS_MEMBER(wxSIMPLE_BORDER) - wxFLAGS_MEMBER(wxSUNKEN_BORDER) - wxFLAGS_MEMBER(wxDOUBLE_BORDER) - wxFLAGS_MEMBER(wxRAISED_BORDER) - wxFLAGS_MEMBER(wxSTATIC_BORDER) - wxFLAGS_MEMBER(wxNO_BORDER) - - // standard window styles - wxFLAGS_MEMBER(wxTAB_TRAVERSAL) - wxFLAGS_MEMBER(wxCLIP_CHILDREN) - wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW) - wxFLAGS_MEMBER(wxWANTS_CHARS) - wxFLAGS_MEMBER(wxNO_FULL_REPAINT_ON_RESIZE) - wxFLAGS_MEMBER(wxALWAYS_SHOW_SB ) - wxFLAGS_MEMBER(wxVSCROLL) - wxFLAGS_MEMBER(wxHSCROLL) - -wxEND_FLAGS( wxWindowStyle ) - -wxBEGIN_PROPERTIES_TABLE(wxWindow) - wxEVENT_PROPERTY( Close , wxEVT_CLOSE_WINDOW , wxCloseEvent) - wxEVENT_PROPERTY( Create , wxEVT_CREATE , wxWindowCreateEvent ) - wxEVENT_PROPERTY( Destroy , wxEVT_DESTROY , wxWindowDestroyEvent ) - // Always constructor Properties first - - wxREADONLY_PROPERTY( Parent,wxWindow*, GetParent, , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) - wxPROPERTY( Id,wxWindowID, SetId, GetId, -1, 0 /*flags*/ , wxT("Helpstring") , wxT("group") ) - wxPROPERTY( Position,wxPoint, SetPosition , GetPosition, wxPoint(-1,-1) , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // pos - wxPROPERTY( Size,wxSize, SetSize, GetSize, wxSize(-1,-1) , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // size - wxPROPERTY( WindowStyle , long , SetWindowStyleFlag , GetWindowStyleFlag , , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style - - // Then all relations of the object graph - - wxREADONLY_PROPERTY_COLLECTION( Children , wxWindowList , wxWindowBase* , GetWindowChildren , wxPROP_OBJECT_GRAPH /*flags*/ , wxT("Helpstring") , wxT("group")) - - // and finally all other properties - - wxPROPERTY( ExtraStyle , long , SetExtraStyle , GetExtraStyle , , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // extstyle - wxPROPERTY( BackgroundColour , wxColour , SetBackgroundColour , GetBackgroundColour , , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // bg - wxPROPERTY( ForegroundColour , wxColour , SetForegroundColour , GetForegroundColour , , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // fg - wxPROPERTY( Enabled , bool , Enable , IsEnabled , wxxVariant((bool)true) , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) - wxPROPERTY( Shown , bool , Show , IsShown , wxxVariant((bool)true) , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) -#if 0 - // possible property candidates (not in xrc) or not valid in all subclasses - wxPROPERTY( Title,wxString, SetTitle, GetTitle, wxT("") ) - wxPROPERTY( Font , wxFont , SetFont , GetWindowFont , ) - wxPROPERTY( Label,wxString, SetLabel, GetLabel, wxT("") ) - // MaxHeight, Width , MinHeight , Width - // TODO switch label to control and title to toplevels - - wxPROPERTY( ThemeEnabled , bool , SetThemeEnabled , GetThemeEnabled , ) - //wxPROPERTY( Cursor , wxCursor , SetCursor , GetCursor , ) - // wxPROPERTY( ToolTip , wxString , SetToolTip , GetToolTipText , ) - wxPROPERTY( AutoLayout , bool , SetAutoLayout , GetAutoLayout , ) - - - -#endif -wxEND_PROPERTIES_TABLE() - -wxBEGIN_HANDLERS_TABLE(wxWindow) -wxEND_HANDLERS_TABLE() - -wxCONSTRUCTOR_DUMMY(wxWindow) - -#else - IMPLEMENT_DYNAMIC_CLASS(wxWindow, wxWindowBase) -#endif -#endif // __WXUNIVERSAL__/__WXMSW__ +#endif // __WXUNIVERSAL__ BEGIN_EVENT_TABLE(wxWindowMSW, wxWindowBase) - EVT_ERASE_BACKGROUND(wxWindowMSW::OnEraseBackground) EVT_SYS_COLOUR_CHANGED(wxWindowMSW::OnSysColourChanged) +#ifdef __WXWINCE__ EVT_INIT_DIALOG(wxWindowMSW::OnInitDialog) +#endif END_EVENT_TABLE() // =========================================================================== @@ -392,7 +367,7 @@ wxWindow *wxWindowMSW::FindItem(long id) const wxControl *item = wxDynamicCastThis(wxControl); if ( item ) { - // is it we or one of our "internal" children? + // is it us or one of our "internal" children? if ( item->GetId() == id #ifndef __WXUNIVERSAL__ || (item->GetSubcontrols().Index(id) != wxNOT_FOUND) @@ -434,7 +409,7 @@ wxWindow *wxWindowMSW::FindItemByHWND(WXHWND hWnd, bool controlOnly) const if ( !controlOnly #if wxUSE_CONTROLS - || parent->IsKindOf(CLASSINFO(wxControl)) + || wxDynamicCast(parent, wxControl) #endif // wxUSE_CONTROLS ) { @@ -456,7 +431,7 @@ wxWindow *wxWindowMSW::FindItemByHWND(WXHWND hWnd, bool controlOnly) const // Default command handler bool wxWindowMSW::MSWCommand(WXUINT WXUNUSED(param), WXWORD WXUNUSED(id)) { - return FALSE; + return false; } // ---------------------------------------------------------------------------- @@ -465,39 +440,31 @@ bool wxWindowMSW::MSWCommand(WXUINT WXUNUSED(param), WXWORD WXUNUSED(id)) void wxWindowMSW::Init() { - // generic - InitBase(); - // MSW specific - m_isBeingDeleted = FALSE; m_oldWndProc = NULL; - m_mouseInWindow = FALSE; - m_lastKeydownProcessed = FALSE; - - m_childrenDisabled = NULL; - - // wxWnd - m_hMenu = 0; + m_mouseInWindow = false; + m_lastKeydownProcessed = false; m_hWnd = 0; m_xThumbSize = 0; m_yThumbSize = 0; - // as all windows are created with WS_VISIBLE style... - m_isShown = TRUE; +#if wxUSE_DEFERRED_SIZING + m_hDWP = 0; + m_pendingPosition = wxDefaultPosition; + m_pendingSize = wxDefaultSize; +#endif // wxUSE_DEFERRED_SIZING -#if wxUSE_MOUSEEVENT_HACK - m_lastMouseX = - m_lastMouseY = -1; - m_lastMouseEvent = -1; -#endif // wxUSE_MOUSEEVENT_HACK +#ifdef __POCKETPC__ + m_contextMenuEnabled = false; +#endif } // Destructor wxWindowMSW::~wxWindowMSW() { - m_isBeingDeleted = TRUE; + SendDestroyEvent(); #ifndef __WXUNIVERSAL__ // VS: make sure there's no wxFrame with last focus set to us: @@ -510,13 +477,18 @@ wxWindowMSW::~wxWindowMSW() { frame->SetLastFocus(NULL); } - break; + + // apparently sometimes we can end up with our grand parent + // pointing to us as well: this is surely a bug in focus handling + // code but it's not clear where it happens so for now just try to + // fix it here by not breaking out of the loop + //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 + // If we did it the other way around, children wouldn't be able // find their parent frame (see above). DestroyChildren(); @@ -526,14 +498,21 @@ wxWindowMSW::~wxWindowMSW() //if (::IsWindow(GetHwnd())) { if ( !::DestroyWindow(GetHwnd()) ) + { wxLogLastError(wxT("DestroyWindow")); + } } // remove hWnd <-> wxWindow association wxRemoveHandleAssociation(this); } - delete m_childrenDisabled; +} + +/* static */ +const wxChar *wxWindowMSW::MSWGetRegisteredClassName() +{ + return wxApp::GetRegisteredClassName(wxT("wxWindow"), COLOR_BTNFACE); } // real construction (Init() must have been called before!) @@ -544,10 +523,10 @@ bool wxWindowMSW::Create(wxWindow *parent, long style, const wxString& name) { - wxCHECK_MSG( parent, FALSE, wxT("can't create wxWindow without parent") ); + wxCHECK_MSG( parent, false, wxT("can't create wxWindow without parent") ); if ( !CreateBase(parent, id, pos, size, style, wxDefaultValidator, name) ) - return FALSE; + return false; parent->AddChild(this); @@ -563,19 +542,18 @@ bool wxWindowMSW::Create(wxWindow *parent, 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_VISIBLE; - m_isShown = FALSE; - } - else + if ( IsShown() ) { msflags |= WS_VISIBLE; } - return MSWCreate(wxCanvasClassName, NULL, pos, size, msflags, exstyle); + if ( !MSWCreate(MSWGetRegisteredClassName(), + NULL, pos, size, msflags, exstyle) ) + return false; + + InheritAttributes(); + + return true; } // --------------------------------------------------------------------------- @@ -585,15 +563,14 @@ bool wxWindowMSW::Create(wxWindow *parent, void wxWindowMSW::SetFocus() { HWND hWnd = GetHwnd(); - wxCHECK_RET( hWnd, _T("can't set focus to invalid window") ); + wxCHECK_RET( hWnd, wxT("can't set focus to invalid window") ); -#if !defined(__WXMICROWIN__) && !defined(__WXWINCE__) +#if !defined(__WXWINCE__) ::SetLastError(0); #endif if ( !::SetFocus(hWnd) ) { -#if defined(__WXDEBUG__) && !defined(__WXMICROWIN__) // was there really an error? DWORD dwRes = ::GetLastError(); if ( dwRes ) @@ -601,10 +578,9 @@ void wxWindowMSW::SetFocus() HWND hwndFocus = ::GetFocus(); if ( hwndFocus != hWnd ) { - wxLogApiError(_T("SetFocus"), dwRes); + wxLogApiError(wxT("SetFocus"), dwRes); } } -#endif // Debug } } @@ -626,7 +602,7 @@ void wxWindowMSW::SetFocusFromKbd() } // Get the window with the focus -wxWindow *wxWindowBase::FindFocus() +wxWindow *wxWindowBase::DoFindFocus() { HWND hWnd = ::GetFocus(); if ( hWnd ) @@ -637,86 +613,163 @@ wxWindow *wxWindowBase::FindFocus() return NULL; } -bool wxWindowMSW::Enable(bool enable) +void wxWindowMSW::DoEnable( bool enable ) { - if ( !wxWindowBase::Enable(enable) ) - return FALSE; + MSWEnableHWND(GetHwnd(), enable); +} - HWND hWnd = GetHwnd(); - if ( hWnd ) - ::EnableWindow(hWnd, (BOOL)enable); +bool wxWindowMSW::MSWEnableHWND(WXHWND hWnd, bool enable) +{ + if ( !hWnd ) + return false; - // 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; + // If disabling focused control, we move focus to the next one, as if the + // user pressed Tab. That's because we can't keep focus on a disabled + // control, Tab-navigation would stop working then. + if ( !enable && ::GetFocus() == hWnd ) + Navigate(); - // 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(); - if ( child->IsTopLevel() ) - { - // the logic below doesn't apply to top level children - continue; - } + return ::EnableWindow(hWnd, (BOOL)enable) != 0; +} - 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; +bool wxWindowMSW::Show(bool show) +{ + if ( !wxWindowBase::Show(show) ) + return false; - m_childrenDisabled->Append(child); - } - } + HWND hWnd = GetHwnd(); + + // we could be called before the underlying window is created (this is + // actually useful to prevent it from being initially shown), e.g. + // + // wxFoo *foo = new wxFoo; + // foo->Hide(); + // foo->Create(parent, ...); + // + // should work without errors + if ( hWnd ) + { + ::ShowWindow(hWnd, show ? SW_SHOW : SW_HIDE); } - if ( enable && m_childrenDisabled ) + if ( IsFrozen() ) { - // we don't need this list any more, don't keep unused memory - delete m_childrenDisabled; - m_childrenDisabled = NULL; + // DoFreeze/DoThaw don't do anything if the window is not shown, so + // we have to call them from here now + if ( show ) + DoFreeze(); + else + DoThaw(); } - return TRUE; + return true; } -bool wxWindowMSW::Show(bool show) +bool +wxWindowMSW::MSWShowWithEffect(bool show, + wxShowEffect effect, + unsigned timeout) { +#if wxUSE_DYNLIB_CLASS + if ( effect == wxSHOW_EFFECT_NONE ) + return Show(show); + if ( !wxWindowBase::Show(show) ) - return FALSE; + return false; - HWND hWnd = GetHwnd(); - int cshow = show ? SW_SHOW : SW_HIDE; - ::ShowWindow(hWnd, cshow); + typedef BOOL (WINAPI *AnimateWindow_t)(HWND, DWORD, DWORD); + + static AnimateWindow_t s_pfnAnimateWindow = NULL; + static bool s_initDone = false; + if ( !s_initDone ) + { + wxDynamicLibrary dllUser32(wxT("user32.dll"), wxDL_VERBATIM | wxDL_QUIET); + wxDL_INIT_FUNC(s_pfn, AnimateWindow, dllUser32); + + s_initDone = true; + + // notice that it's ok to unload user32.dll here as it won't be really + // unloaded, being still in use because we link to it statically too + } + + if ( !s_pfnAnimateWindow ) + return Show(show); + + // Show() has a side effect of sending a WM_SIZE to the window, which helps + // ensuring that it's laid out correctly, but AnimateWindow() doesn't do + // this so send the event ourselves + SendSizeEvent(); + + // prepare to use AnimateWindow() + + if ( !timeout ) + timeout = 200; // this is the default animation timeout, per MSDN - if ( show && IsTopLevel() ) + DWORD dwFlags = show ? 0 : AW_HIDE; + + switch ( effect ) + { + case wxSHOW_EFFECT_ROLL_TO_LEFT: + dwFlags |= AW_HOR_NEGATIVE; + break; + + case wxSHOW_EFFECT_ROLL_TO_RIGHT: + dwFlags |= AW_HOR_POSITIVE; + break; + + case wxSHOW_EFFECT_ROLL_TO_TOP: + dwFlags |= AW_VER_NEGATIVE; + break; + + case wxSHOW_EFFECT_ROLL_TO_BOTTOM: + dwFlags |= AW_VER_POSITIVE; + break; + + case wxSHOW_EFFECT_SLIDE_TO_LEFT: + dwFlags |= AW_SLIDE | AW_HOR_NEGATIVE; + break; + + case wxSHOW_EFFECT_SLIDE_TO_RIGHT: + dwFlags |= AW_SLIDE | AW_HOR_POSITIVE; + break; + + case wxSHOW_EFFECT_SLIDE_TO_TOP: + dwFlags |= AW_SLIDE | AW_VER_NEGATIVE; + break; + + case wxSHOW_EFFECT_SLIDE_TO_BOTTOM: + dwFlags |= AW_SLIDE | AW_VER_POSITIVE; + break; + + case wxSHOW_EFFECT_BLEND: + dwFlags |= AW_BLEND; + break; + + case wxSHOW_EFFECT_EXPAND: + dwFlags |= AW_CENTER; + break; + + + case wxSHOW_EFFECT_MAX: + wxFAIL_MSG( wxT("invalid window show effect") ); + return false; + + default: + wxFAIL_MSG( wxT("unknown window show effect") ); + return false; + } + + if ( !(*s_pfnAnimateWindow)(GetHwnd(), timeout, dwFlags) ) { - wxBringWindowToTop(hWnd); + wxLogLastError(wxT("AnimateWindow")); + + return false; } - return TRUE; + return true; +#else // wxUSE_DYNLIB_CLASS + return Show(show); +#endif } // Raise the window to the top of the Z order @@ -732,16 +785,6 @@ void wxWindowMSW::Lower() SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); } -void wxWindowMSW::SetTitle( const wxString& title) -{ - SetWindowText(GetHwnd(), title.c_str()); -} - -wxString wxWindowMSW::GetTitle() const -{ - return wxGetWindowText(GetHWND()); -} - void wxWindowMSW::DoCaptureMouse() { HWND hWnd = GetHwnd(); @@ -755,14 +798,14 @@ void wxWindowMSW::DoReleaseMouse() { if ( !::ReleaseCapture() ) { - wxLogLastError(_T("ReleaseCapture")); + wxLogLastError(wxT("ReleaseCapture")); } } /* static */ wxWindow *wxWindowBase::GetCapture() { HWND hwnd = ::GetCapture(); - return hwnd ? wxFindWinFromHandle((WXHWND)hwnd) : (wxWindow *)NULL; + return hwnd ? wxFindWinFromHandle(hwnd) : NULL; } bool wxWindowMSW::SetFont(const wxFont& font) @@ -770,53 +813,108 @@ bool wxWindowMSW::SetFont(const wxFont& font) if ( !wxWindowBase::SetFont(font) ) { // nothing to do - return FALSE; + return false; } HWND hWnd = GetHwnd(); if ( hWnd != 0 ) { - WXHANDLE hFont = m_font.GetResourceHandle(); + // note the use of GetFont() instead of m_font: our own font could have + // just been reset and in this case we need to change the font used by + // the native window to the default for this class, i.e. exactly what + // GetFont() returns + WXHANDLE hFont = GetFont().GetResourceHandle(); wxASSERT_MSG( hFont, wxT("should have valid font") ); ::SendMessage(hWnd, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(TRUE, 0)); } - return TRUE; + return true; } + bool wxWindowMSW::SetCursor(const wxCursor& cursor) { if ( !wxWindowBase::SetCursor(cursor) ) { // no change - return FALSE; + return false; } - if ( m_cursor.Ok() ) + // don't "overwrite" busy cursor + if ( wxIsBusy() ) + return true; + + if ( m_cursor.IsOk() ) { - HWND hWnd = GetHwnd(); + // normally we should change the cursor only if it's over this window + // but we should do it always if we capture the mouse currently + bool set = HasCapture(); + if ( !set ) + { + HWND hWnd = GetHwnd(); - // Change the cursor NOW if we're within the correct window - POINT point; - ::GetCursorPos(&point); + POINT point; + ::wxGetCursorPosMSW(&point); - RECT rect = wxGetWindowRect(hWnd); + RECT rect = wxGetWindowRect(hWnd); - if ( ::PtInRect(&rect, point) && !wxIsBusy() ) + set = ::PtInRect(&rect, point) != 0; + } + + if ( set ) + { ::SetCursor(GetHcursorOf(m_cursor)); + } + //else: will be set later when the mouse enters this window + } + else // Invalid cursor: this means reset to the default one. + { + // To revert to the correct cursor we need to find the window currently + // under the cursor and ask it to set its cursor itself as only it + // knows what it is. + POINT pt; + wxGetCursorPosMSW(&pt); + + const wxWindowMSW* win = wxFindWindowAtPoint(wxPoint(pt.x, pt.y)); + if ( !win ) + win = this; + + ::SendMessage(GetHwndOf(win), WM_SETCURSOR, + (WPARAM)GetHwndOf(win), + MAKELPARAM(HTCLIENT, WM_MOUSEMOVE)); } - return TRUE; + return true; } -void wxWindowMSW::WarpPointer (int x, int y) +void wxWindowMSW::WarpPointer(int x, int y) { ClientToScreen(&x, &y); if ( !::SetCursorPos(x, y) ) { - wxLogLastError(_T("SetCursorPos")); + wxLogLastError(wxT("SetCursorPos")); + } +} + +void wxWindowMSW::MSWUpdateUIState(int action, int state) +{ + // WM_CHANGEUISTATE only appeared in Windows 2000 so it can do us no good + // to use it on older systems -- and could possibly do some harm + static int s_needToUpdate = -1; + if ( s_needToUpdate == -1 ) + { + int verMaj, verMin; + s_needToUpdate = wxGetOsVersion(&verMaj, &verMin) == wxOS_WINDOWS_NT && + verMaj >= 5; + } + + if ( s_needToUpdate ) + { + // we send WM_CHANGEUISTATE so if nothing needs changing then the system + // won't send WM_UPDATEUISTATE + ::SendMessage(GetHwnd(), WM_CHANGEUISTATE, MAKEWPARAM(action, state), 0); } } @@ -824,11 +922,8 @@ void wxWindowMSW::WarpPointer (int x, int y) // scrolling stuff // --------------------------------------------------------------------------- -// convert wxHORIZONTAL/wxVERTICAL to SB_HORZ/SB_VERT -static inline int wxDirToWinStyle(int orient) +namespace { - return orient == wxHORIZONTAL ? SB_HORZ : SB_VERT; -} inline int GetScrollPosition(HWND hWnd, int wOrient) { @@ -838,24 +933,26 @@ inline int GetScrollPosition(HWND hWnd, int wOrient) WinStruct scrollInfo; scrollInfo.cbSize = sizeof(SCROLLINFO); scrollInfo.fMask = SIF_POS; - if ( !::GetScrollInfo(hWnd, - wOrient, - &scrollInfo) ) - { - // Not neccessarily an error, if there are no scrollbars yet. - // wxLogLastError(_T("GetScrollInfo")); - } + ::GetScrollInfo(hWnd, wOrient, &scrollInfo ); + return scrollInfo.nPos; -// return ::GetScrollPos(hWnd, wOrient); + #endif } +inline UINT WXOrientToSB(int orient) +{ + return orient == wxHORIZONTAL ? SB_HORZ : SB_VERT; +} + +} // anonymous namespace + int wxWindowMSW::GetScrollPos(int orient) const { HWND hWnd = GetHwnd(); - wxCHECK_MSG( hWnd, 0, _T("no HWND in GetScrollPos") ); + wxCHECK_MSG( hWnd, 0, wxT("no HWND in GetScrollPos") ); - return GetScrollPosition(hWnd, orient == wxHORIZONTAL ? SB_HORZ : SB_VERT); + return GetScrollPosition(hWnd, WXOrientToSB(orient)); } // This now returns the whole range, not just the number @@ -866,19 +963,13 @@ int wxWindowMSW::GetScrollRange(int orient) const HWND hWnd = GetHwnd(); if ( !hWnd ) return 0; -#if 0 - ::GetScrollRange(hWnd, orient == wxHORIZONTAL ? SB_HORZ : SB_VERT, - &minPos, &maxPos); -#endif WinStruct scrollInfo; scrollInfo.fMask = SIF_RANGE; - if ( !::GetScrollInfo(hWnd, - orient == wxHORIZONTAL ? SB_HORZ : SB_VERT, - &scrollInfo) ) + if ( !::GetScrollInfo(hWnd, WXOrientToSB(orient), &scrollInfo) ) { // Most of the time this is not really an error, since the return // value can also be zero when there is no scrollbar yet. - // wxLogLastError(_T("GetScrollInfo")); + // wxLogLastError(wxT("GetScrollInfo")); } maxPos = scrollInfo.nMax; @@ -894,7 +985,7 @@ int wxWindowMSW::GetScrollThumb(int orient) const void wxWindowMSW::SetScrollPos(int orient, int pos, bool refresh) { HWND hWnd = GetHwnd(); - wxCHECK_RET( hWnd, _T("SetScrollPos: no HWND") ); + wxCHECK_RET( hWnd, wxT("SetScrollPos: no HWND") ); WinStruct info; info.nPage = 0; @@ -907,8 +998,7 @@ void wxWindowMSW::SetScrollPos(int orient, int pos, bool refresh) info.fMask |= SIF_DISABLENOSCROLL; } - ::SetScrollInfo(hWnd, orient == wxHORIZONTAL ? SB_HORZ : SB_VERT, - &info, refresh); + ::SetScrollInfo(hWnd, WXOrientToSB(orient), &info, refresh); } // New function that will replace some of the above. @@ -918,26 +1008,41 @@ void wxWindowMSW::SetScrollbar(int orient, int range, bool refresh) { + // We have to set the variables here to make them valid in events + // triggered by ::SetScrollInfo() + *(orient == wxHORIZONTAL ? &m_xThumbSize : &m_yThumbSize) = pageSize; + + HWND hwnd = GetHwnd(); + if ( !hwnd ) + return; + 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; + if ( range != -1 ) + { + 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; + + // We normally also reenable scrollbar in case it had been previously + // disabled by specifying SIF_DISABLENOSCROLL below but we should only + // do this if it has valid range, otherwise it would be enabled but not + // do anything. + if ( range >= pageSize ) + { + ::EnableScrollBar(hwnd, WXOrientToSB(orient), ESB_ENABLE_BOTH); + } + } + //else: leave all the fields to be 0 + info.fMask = SIF_RANGE | SIF_PAGE | SIF_POS; - if ( HasFlag(wxALWAYS_SHOW_SB) ) + if ( HasFlag(wxALWAYS_SHOW_SB) || range == -1 ) { // disable scrollbar instead of removing it then info.fMask |= SIF_DISABLENOSCROLL; } - HWND hWnd = GetHwnd(); - if ( hWnd ) - { - ::SetScrollInfo(hWnd, orient == wxHORIZONTAL ? SB_HORZ : SB_VERT, - &info, refresh); - } - - *(orient == wxHORIZONTAL ? &m_xThumbSize : &m_yThumbSize) = pageSize; + ::SetScrollInfo(hwnd, WXOrientToSB(orient), &info, refresh); } void wxWindowMSW::ScrollWindow(int dx, int dy, const wxRect *prect) @@ -946,20 +1051,18 @@ void wxWindowMSW::ScrollWindow(int dx, int dy, const wxRect *prect) RECT *pr; if ( prect ) { - rect.left = prect->x; - rect.top = prect->y; - rect.right = prect->x + prect->width; - rect.bottom = prect->y + prect->height; + wxCopyRectToRECT(*prect, rect); pr = ▭ } else { pr = NULL; + } #ifdef __WXWINCE__ // FIXME: is this the exact equivalent of the line below? - ::ScrollWindowEx(GetHwnd(), dx, dy, pr, pr, 0, 0, SW_ERASE|SW_INVALIDATE); + ::ScrollWindowEx(GetHwnd(), dx, dy, pr, pr, 0, 0, SW_SCROLLCHILDREN|SW_ERASE|SW_INVALIDATE); #else ::ScrollWindow(GetHwnd(), dx, dy, pr, pr); #endif @@ -1005,35 +1108,100 @@ bool wxWindowMSW::ScrollPages(int pages) down ? pages : -pages); } -// --------------------------------------------------------------------------- -// subclassing -// --------------------------------------------------------------------------- +// ---------------------------------------------------------------------------- +// RTL support +// ---------------------------------------------------------------------------- -void wxWindowMSW::SubclassWin(WXHWND hWnd) +void wxWindowMSW::SetLayoutDirection(wxLayoutDirection dir) { - wxASSERT_MSG( !m_oldWndProc, wxT("subclassing window twice?") ); - +#ifdef __WXWINCE__ + wxUnusedVar(dir); +#else + wxCHECK_RET( GetHwnd(), + wxT("layout direction must be set after window creation") ); + + LONG styleOld = wxGetWindowExStyle(this); + + LONG styleNew = styleOld; + switch ( dir ) + { + case wxLayout_LeftToRight: + styleNew &= ~WS_EX_LAYOUTRTL; + break; + + case wxLayout_RightToLeft: + styleNew |= WS_EX_LAYOUTRTL; + break; + + default: + wxFAIL_MSG(wxT("unsupported layout direction")); + break; + } + + if ( styleNew != styleOld ) + { + wxSetWindowExStyle(this, styleNew); + } +#endif +} + +wxLayoutDirection wxWindowMSW::GetLayoutDirection() const +{ +#ifdef __WXWINCE__ + return wxLayout_Default; +#else + wxCHECK_MSG( GetHwnd(), wxLayout_Default, wxT("invalid window") ); + + return wxHasWindowExStyle(this, WS_EX_LAYOUTRTL) ? wxLayout_RightToLeft + : wxLayout_LeftToRight; +#endif +} + +wxCoord +wxWindowMSW::AdjustForLayoutDirection(wxCoord x, + wxCoord WXUNUSED(width), + wxCoord WXUNUSED(widthTotal)) const +{ + // Win32 mirrors the coordinates of RTL windows automatically, so don't + // redo it ourselves + return x; +} + +// --------------------------------------------------------------------------- +// subclassing +// --------------------------------------------------------------------------- + +void wxWindowMSW::SubclassWin(WXHWND hWnd) +{ + wxASSERT_MSG( !m_oldWndProc, wxT("subclassing window twice?") ); + HWND hwnd = (HWND)hWnd; wxCHECK_RET( ::IsWindow(hwnd), wxT("invalid HWND in SubclassWin") ); + SetHWND(hWnd); + wxAssociateWinWithHandle(hwnd, this); - m_oldWndProc = (WXFARPROC)::GetWindowLong((HWND)hWnd, GWL_WNDPROC); + m_oldWndProc = (WXFARPROC)wxGetWindowProc((HWND)hWnd); // we don't need to subclass the window of our own class (in the Windows // sense of the word) - if ( !wxCheckWindowWndProc(hWnd, (WXFARPROC)wxWndProc) ) + if ( !wxCheckWindowWndProc(hWnd, (WXFARPROC)wxWndProc) ) { - ::SetWindowLong(hwnd, GWL_WNDPROC, (LONG) wxWndProc); + wxSetWindowProc(hwnd, 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 + // don't bother restoring it either: this also makes it easy to + // implement IsOfStandardClass() method which returns true for the + // standard controls and false for the wxWidgets own windows as it can // simply check m_oldWndProc m_oldWndProc = NULL; } + + // we're officially created now, send the event + wxWindowCreateEvent event((wxWindow *)this); + (void)HandleWindowEvent(event); } void wxWindowMSW::UnsubclassWin() @@ -1044,7 +1212,7 @@ void wxWindowMSW::UnsubclassWin() HWND hwnd = GetHwnd(); if ( hwnd ) { - m_hWnd = 0; + SetHWND(0); wxCHECK_RET( ::IsWindow(hwnd), wxT("invalid HWND in UnsubclassWin") ); @@ -1052,7 +1220,7 @@ void wxWindowMSW::UnsubclassWin() { if ( !wxCheckWindowWndProc((WXHWND)hwnd, m_oldWndProc) ) { - ::SetWindowLong(hwnd, GWL_WNDPROC, (LONG) m_oldWndProc); + wxSetWindowProc(hwnd, (WNDPROC)m_oldWndProc); } m_oldWndProc = NULL; @@ -1060,46 +1228,38 @@ void wxWindowMSW::UnsubclassWin() } } -bool wxCheckWindowWndProc(WXHWND hWnd, WXFARPROC wndProc) +void wxWindowMSW::AssociateHandle(WXWidget handle) { - // 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 ( m_hWnd ) { - wxLogLastError(_T("GetClassInfo")); - - return FALSE; + if ( !::DestroyWindow(GetHwnd()) ) + { + wxLogLastError(wxT("DestroyWindow")); + } } - return wndProc == (WXFARPROC)cls.lpfnWndProc; -#endif + WXHWND wxhwnd = (WXHWND)handle; + + // this also calls SetHWND(wxhwnd) + SubclassWin(wxhwnd); +} + +void wxWindowMSW::DissociateHandle() +{ + // this also calls SetHWND(0) for us + UnsubclassWin(); +} + + +bool wxCheckWindowWndProc(WXHWND hWnd, + WXFARPROC WXUNUSED(wndProc)) +{ + const wxString str(wxGetWindowClass(hWnd)); + + // TODO: get rid of wxTLWHiddenParent special case (currently it's not + // registered by wxApp but using ad hoc code in msw/toplevel.cpp); + // there is also a hidden window class used by sockets &c + return wxApp::IsRegisteredClassName(str) || str == wxT("wxTLWHiddenParent"); } // ---------------------------------------------------------------------------- @@ -1115,14 +1275,48 @@ void wxWindowMSW::SetWindowStyleFlag(long flags) // update the internal variable wxWindowBase::SetWindowStyleFlag(flags); + // and the real window flags + MSWUpdateStyle(flagsOld, GetExtraStyle()); +} + +void wxWindowMSW::SetExtraStyle(long exflags) +{ + long exflagsOld = GetExtraStyle(); + if ( exflags == exflagsOld ) + return; + + // update the internal variable + wxWindowBase::SetExtraStyle(exflags); + + // and the real window flags + MSWUpdateStyle(GetWindowStyleFlag(), exflagsOld); +} + +void wxWindowMSW::MSWUpdateStyle(long flagsOld, long exflagsOld) +{ // 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); + // we may need to call SetWindowPos() when we change some styles + bool callSWP = false; + + WXDWORD exstyle; + long style = MSWGetStyle(GetWindowStyleFlag(), &exstyle); + + // this is quite a horrible hack but we need it because MSWGetStyle() + // doesn't take exflags as parameter but uses GetExtraStyle() internally + // and so we have to modify the window exflags temporarily to get the + // correct exstyleOld + long exflagsNew = GetExtraStyle(); + wxWindowBase::SetExtraStyle(exflagsOld); + + WXDWORD exstyleOld; + long styleOld = MSWGetStyle(flagsOld, &exstyleOld); + + wxWindowBase::SetExtraStyle(exflagsNew); + if ( style != styleOld ) { @@ -1135,41 +1329,108 @@ void wxWindowMSW::SetWindowStyleFlag(long flags) styleReal |= style; ::SetWindowLong(GetHwnd(), GWL_STYLE, styleReal); + + // we need to call SetWindowPos() if any of the styles affecting the + // frame appearance have changed + callSWP = ((styleOld ^ style ) & (WS_BORDER | + WS_THICKFRAME | + WS_CAPTION | + WS_DLGFRAME | + WS_MAXIMIZEBOX | + WS_MINIMIZEBOX | + WS_SYSMENU) ) != 0; } // and the extended style + long exstyleReal = wxGetWindowExStyle(this); + if ( exstyle != exstyleOld ) { - long exstyleReal = ::GetWindowLong(GetHwnd(), GWL_EXSTYLE); exstyleReal &= ~exstyleOld; exstyleReal |= exstyle; - ::SetWindowLong(GetHwnd(), GWL_EXSTYLE, exstyleReal); + wxSetWindowExStyle(this, exstyleReal); - // we must call SetWindowPos() to flash the cached extended style and + // ex style changes don't take effect without calling SetWindowPos + callSWP = true; + } + + if ( callSWP ) + { + // we must call SetWindowPos() to flush 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) ) + SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | + SWP_FRAMECHANGED) ) + { + wxLogLastError(wxT("SetWindowPos")); + } + } +} + +wxBorder wxWindowMSW::GetDefaultBorderForControl() const +{ + return wxBORDER_THEME; +} + +wxBorder wxWindowMSW::GetDefaultBorder() const +{ + return wxWindowBase::GetDefaultBorder(); +} + +// Translate wxBORDER_THEME (and other border styles if necessary) to the value +// that makes most sense for this Windows environment +wxBorder wxWindowMSW::TranslateBorder(wxBorder border) const +{ +#if defined(__POCKETPC__) || defined(__SMARTPHONE__) + if (border == wxBORDER_THEME || border == wxBORDER_SUNKEN || border == wxBORDER_SIMPLE) + return wxBORDER_SIMPLE; + else + return wxBORDER_NONE; +#else +#if wxUSE_UXTHEME + if (border == wxBORDER_THEME) + { + if (CanApplyThemeBorder()) { - wxLogLastError(_T("SetWindowPos")); + wxUxThemeEngine* theme = wxUxThemeEngine::GetIfActive(); + if (theme) + return wxBORDER_THEME; } + return wxBORDER_SUNKEN; } +#endif + return border; +#endif } + WXDWORD wxWindowMSW::MSWGetStyle(long flags, WXDWORD *exstyle) const { - // translate the style - WXDWORD style = WS_CHILD | WS_VISIBLE; + // translate common wxWidgets styles to Windows ones + + // most of windows are child ones, those which are not (such as + // wxTopLevelWindow) should remove WS_CHILD in their MSWGetStyle() + WXDWORD style = WS_CHILD; - if ( flags & wxCLIP_CHILDREN ) + // using this flag results in very significant reduction in flicker, + // especially with controls inside the static boxes (as the interior of the + // box is not redrawn twice), but sometimes results in redraw problems, so + // optionally allow the old code to continue to use it provided a special + // system option is turned on + if ( !wxSystemOptions::GetOptionInt(wxT("msw.window.no-clip-children")) + || (flags & wxCLIP_CHILDREN) ) style |= WS_CLIPCHILDREN; - if ( flags & wxCLIP_SIBLINGS ) - style |= WS_CLIPSIBLINGS; + // it doesn't seem useful to use WS_CLIPSIBLINGS here as we officially + // don't support overlapping windows and it only makes sense for them and, + // presumably, gives the system some extra work (to manage more clipping + // regions), so avoid it alltogether + if ( flags & wxVSCROLL ) style |= WS_VSCROLL; @@ -1177,7 +1438,10 @@ WXDWORD wxWindowMSW::MSWGetStyle(long flags, WXDWORD *exstyle) const if ( flags & wxHSCROLL ) style |= WS_HSCROLL; - const wxBorder border = GetBorder(flags); + const wxBorder border = TranslateBorder(GetBorder(flags)); + + // After translation, border is now optimized for the specific version of Windows + // and theme engine presence. // WS_BORDER is only required for wxBORDER_SIMPLE if ( border == wxBORDER_SIMPLE ) @@ -1197,11 +1461,12 @@ WXDWORD wxWindowMSW::MSWGetStyle(long flags, WXDWORD *exstyle) const { default: case wxBORDER_DEFAULT: - wxFAIL_MSG( _T("unknown border style") ); + wxFAIL_MSG( wxT("unknown border style") ); // fall through case wxBORDER_NONE: case wxBORDER_SIMPLE: + case wxBORDER_THEME: break; case wxBORDER_STATIC: @@ -1217,9 +1482,9 @@ WXDWORD wxWindowMSW::MSWGetStyle(long flags, WXDWORD *exstyle) const style &= ~WS_BORDER; break; - case wxBORDER_DOUBLE: - *exstyle |= WS_EX_DLGMODALFRAME; - break; +// case wxBORDER_DOUBLE: +// *exstyle |= WS_EX_DLGMODALFRAME; +// break; } // wxUniv doesn't use Windows dialog navigation functions at all @@ -1247,7 +1512,7 @@ bool wxWindowMSW::IsMouseInWindow() const { // get the mouse position POINT pt; - ::GetCursorPos(&pt); + wxGetCursorPosMSW(&pt); // find the window which currently has the cursor and go up the window // chain until we find this window - or exhaust it @@ -1260,6 +1525,7 @@ bool wxWindowMSW::IsMouseInWindow() const void wxWindowMSW::OnInternalIdle() { +#ifndef HAVE_TRACKMOUSEEVENT // Check if we need to send a LEAVE event if ( m_mouseInWindow ) { @@ -1267,52 +1533,19 @@ void wxWindowMSW::OnInternalIdle() // 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 OnInternalIdle function is called, so 'state' - // may be meaningless. - int state = 0; - if ( wxIsShiftDown() ) - state |= MK_SHIFT; - if ( wxIsCtrlDown() ) - state |= MK_CONTROL; - if ( GetKeyState( VK_LBUTTON ) ) - state |= MK_LBUTTON; - if ( GetKeyState( VK_MBUTTON ) ) - state |= MK_MBUTTON; - if ( GetKeyState( VK_RBUTTON ) ) - state |= MK_RBUTTON; - - 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); + GenerateMouseLeave(); } } +#endif // !HAVE_TRACKMOUSEEVENT - if (wxUpdateUIEvent::CanUpdate(this)) - UpdateWindowUI(wxUPDATE_UI_FROMIDLE); + wxWindowBase::OnInternalIdle(); } // Set this window to be the child of 'parent'. bool wxWindowMSW::Reparent(wxWindowBase *parent) { if ( !wxWindowBase::Reparent(parent) ) - return FALSE; + return false; HWND hWndChild = GetHwnd(); HWND hWndParent = GetParent() ? GetWinHwnd(GetParent()) : (HWND)0; @@ -1320,13 +1553,13 @@ bool wxWindowMSW::Reparent(wxWindowBase *parent) ::SetParent(hWndChild, hWndParent); #ifndef __WXWINCE__ - if ( ::GetWindowLong(hWndChild, GWL_EXSTYLE) & WS_EX_CONTROLPARENT ) + if ( wxHasWindowExStyle(this, WS_EX_CONTROLPARENT) ) { EnsureParentHasControlParentStyle(GetParent()); } #endif // !__WXWINCE__ - return TRUE; + return true; } static inline void SendSetRedraw(HWND hwnd, bool on) @@ -1336,17 +1569,23 @@ static inline void SendSetRedraw(HWND hwnd, bool on) #endif } -void wxWindowMSW::Freeze() +void wxWindowMSW::DoFreeze() { - SendSetRedraw(GetHwnd(), FALSE); + if ( !IsShown() ) + return; // no point in freezing hidden window + + SendSetRedraw(GetHwnd(), false); } -void wxWindowMSW::Thaw() +void wxWindowMSW::DoThaw() { - SendSetRedraw(GetHwnd(), TRUE); + if ( !IsShown() ) + return; // hidden windows aren't frozen by DoFreeze - // we need to refresh everything or otherwise he invalidated area is not - // repainted + SendSetRedraw(GetHwnd(), true); + + // we need to refresh everything or otherwise the invalidated area + // is not going to be repainted Refresh(); } @@ -1355,18 +1594,28 @@ void wxWindowMSW::Refresh(bool eraseBack, const wxRect *rect) HWND hWnd = GetHwnd(); if ( hWnd ) { + RECT mswRect; + const RECT *pRect; if ( rect ) { - RECT mswRect; - mswRect.left = rect->x; - mswRect.top = rect->y; - mswRect.right = rect->x + rect->width; - mswRect.bottom = rect->y + rect->height; - - ::InvalidateRect(hWnd, &mswRect, eraseBack); + wxCopyRectToRECT(*rect, mswRect); + pRect = &mswRect; } else - ::InvalidateRect(hWnd, NULL, eraseBack); + { + pRect = NULL; + } + + // RedrawWindow not available on SmartPhone or eVC++ 3 +#if !defined(__SMARTPHONE__) && !(defined(_WIN32_WCE) && _WIN32_WCE < 400) + UINT flags = RDW_INVALIDATE | RDW_ALLCHILDREN; + if ( eraseBack ) + flags |= RDW_ERASE; + + ::RedrawWindow(hWnd, pRect, NULL, flags); +#else + ::InvalidateRect(hWnd, pRect, eraseBack); +#endif } } @@ -1374,7 +1623,7 @@ void wxWindowMSW::Update() { if ( !::UpdateWindow(GetHwnd()) ) { - wxLogLastError(_T("UpdateWindow")); + wxLogLastError(wxT("UpdateWindow")); } #if !defined(__WXMICROWIN__) && !defined(__WXWINCE__) @@ -1388,6 +1637,40 @@ void wxWindowMSW::Update() // drag and drop // --------------------------------------------------------------------------- +#if wxUSE_DRAG_AND_DROP || !defined(__WXWINCE__) + +#if wxUSE_STATBOX + +// we need to lower the sibling static boxes so controls contained within can be +// a drop target +static void AdjustStaticBoxZOrder(wxWindow *parent) +{ + // no sibling static boxes if we have no parent (ie TLW) + if ( !parent ) + return; + + for ( wxWindowList::compatibility_iterator node = parent->GetChildren().GetFirst(); + node; + node = node->GetNext() ) + { + wxStaticBox *statbox = wxDynamicCast(node->GetData(), wxStaticBox); + if ( statbox ) + { + ::SetWindowPos(GetHwndOf(statbox), HWND_BOTTOM, 0, 0, 0, 0, + SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); + } + } +} + +#else // !wxUSE_STATBOX + +static inline void AdjustStaticBoxZOrder(wxWindow * WXUNUSED(parent)) +{ +} + +#endif // wxUSE_STATBOX/!wxUSE_STATBOX + +#endif // drag and drop is used #if wxUSE_DRAG_AND_DROP void wxWindowMSW::SetDropTarget(wxDropTarget *pDropTarget) @@ -1399,18 +1682,24 @@ void wxWindowMSW::SetDropTarget(wxDropTarget *pDropTarget) m_dropTarget = pDropTarget; if ( m_dropTarget != 0 ) + { + AdjustStaticBoxZOrder(GetParent()); m_dropTarget->Register(m_hWnd); + } } #endif // wxUSE_DRAG_AND_DROP -// old style file-manager drag&drop support: we retain the old-style +// old-style file manager drag&drop support: we retain the old-style // DragAcceptFiles in parallel with SetDropTarget. -void wxWindowMSW::DragAcceptFiles(bool accept) +void wxWindowMSW::DragAcceptFiles(bool WXUNUSED_IN_WINCE(accept)) { -#if !defined(__WXWINCE__) +#ifndef __WXWINCE__ HWND hWnd = GetHwnd(); if ( hWnd ) + { + AdjustStaticBoxZOrder(GetParent()); ::DragAcceptFiles(hWnd, (BOOL)accept); + } #endif } @@ -1434,66 +1723,151 @@ void wxWindowMSW::DoSetToolTip(wxToolTip *tooltip) // moving and resizing // --------------------------------------------------------------------------- +bool wxWindowMSW::IsSizeDeferred() const +{ +#if wxUSE_DEFERRED_SIZING + if ( m_pendingPosition != wxDefaultPosition || + m_pendingSize != wxDefaultSize ) + return true; +#endif // wxUSE_DEFERRED_SIZING + + return false; +} + // Get total size void wxWindowMSW::DoGetSize(int *x, int *y) const { - RECT rect = wxGetWindowRect(GetHwnd()); +#if wxUSE_DEFERRED_SIZING + // if SetSize() had been called at wx level but not realized at Windows + // level yet (i.e. EndDeferWindowPos() not called), we still should return + // the new and not the old position to the other wx code + if ( m_pendingSize != wxDefaultSize ) + { + if ( x ) + *x = m_pendingSize.x; + if ( y ) + *y = m_pendingSize.y; + } + else // use current size +#endif // wxUSE_DEFERRED_SIZING + { + RECT rect = wxGetWindowRect(GetHwnd()); - if ( x ) - *x = rect.right - rect.left; - if ( y ) - *y = rect.bottom - rect.top; + if ( x ) + *x = rect.right - rect.left; + if ( y ) + *y = rect.bottom - rect.top; + } } // Get size *available for subwindows* i.e. excluding menu bar etc. void wxWindowMSW::DoGetClientSize(int *x, int *y) const { - RECT rect = wxGetClientRect(GetHwnd()); +#if wxUSE_DEFERRED_SIZING + if ( m_pendingSize != wxDefaultSize ) + { + // we need to calculate the client size corresponding to pending size + // + // FIXME: Unfortunately this doesn't work correctly for the maximized + // top level windows, the returned values are too small (e.g. + // under Windows 7 on a 1600*1200 screen with task bar on the + // right the pending size for a maximized window is 1538*1200 + // and WM_NCCALCSIZE returns 1528*1172 even though the correct + // client size of such window is 1538*1182). No idea how to fix + // it though, setting WS_MAXIMIZE in GWL_STYLE before calling + // WM_NCCALCSIZE doesn't help and AdjustWindowRectEx() doesn't + // work in this direction neither. So we just have to live with + // the slightly wrong results and relayout the window when it + // gets finally shown in its maximized state (see #11762). + RECT rect; + rect.left = m_pendingPosition.x; + rect.top = m_pendingPosition.y; + rect.right = rect.left + m_pendingSize.x; + rect.bottom = rect.top + m_pendingSize.y; + + ::SendMessage(GetHwnd(), WM_NCCALCSIZE, FALSE, (LPARAM)&rect); + + if ( x ) + *x = rect.right - rect.left; + if ( y ) + *y = rect.bottom - rect.top; + } + else +#endif // wxUSE_DEFERRED_SIZING + { + RECT rect = wxGetClientRect(GetHwnd()); - if ( x ) - *x = rect.right; - if ( y ) - *y = rect.bottom; + if ( x ) + *x = rect.right; + if ( y ) + *y = rect.bottom; + } + + // The size of the client window can't be negative but ::GetClientRect() + // can return negative size for an extremely small (1x1) window with + // borders so ensure that we correct it here as having negative sizes is + // completely unexpected. + if ( x && *x < 0 ) + *x = 0; + if ( y && *y < 0 ) + *y = 0; } void wxWindowMSW::DoGetPosition(int *x, int *y) const { - RECT rect = wxGetWindowRect(GetHwnd()); - - POINT point; - point.x = rect.left; - point.y = rect.top; + wxWindow * const parent = GetParent(); - // we do the adjustments with respect to the parent only for the "real" - // children, not for the dialogs/frames - if ( !IsTopLevel() ) + wxPoint pos; +#if wxUSE_DEFERRED_SIZING + if ( m_pendingPosition != wxDefaultPosition ) + { + pos = m_pendingPosition; + } + else // use current position +#endif // wxUSE_DEFERRED_SIZING { - HWND hParentWnd = 0; - wxWindow *parent = GetParent(); - if ( parent ) - hParentWnd = GetWinHwnd(parent); + RECT rect = wxGetWindowRect(GetHwnd()); - // Since we now have the absolute screen coords, if there's a parent we - // must subtract its top left corner - if ( hParentWnd ) - { - ::ScreenToClient(hParentWnd, &point); - } + POINT point; + point.x = rect.left; + point.y = rect.top; - if ( parent ) + // we do the adjustments with respect to the parent only for the "real" + // children, not for the dialogs/frames + if ( !IsTopLevel() ) { - // 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 ( wxTheApp->GetLayoutDirection() == wxLayout_RightToLeft ) + { + // In RTL mode, we want the logical left x-coordinate, + // which would be the physical right x-coordinate. + point.x = rect.right; + } + + // Since we now have the absolute screen coords, if there's a + // parent we must subtract its top left corner + if ( parent ) + { + ::ScreenToClient(GetHwndOf(parent), &point); + } } + + pos.x = point.x; + pos.y = point.y; + } + + // we also must adjust by the client area offset: a control which is just + // under a toolbar could be at (0, 30) in Windows but at (0, 0) in wx + if ( parent && !IsTopLevel() ) + { + const wxPoint pt(parent->GetClientAreaOrigin()); + pos.x -= pt.x; + pos.y -= pt.y; } if ( x ) - *x = point.x; + *x = pos.x; if ( y ) - *y = point.y; + *y = pos.y; } void wxWindowMSW::DoScreenToClient(int *x, int *y) const @@ -1528,6 +1902,50 @@ void wxWindowMSW::DoClientToScreen(int *x, int *y) const *y = pt.y; } +bool +wxWindowMSW::DoMoveSibling(WXHWND hwnd, int x, int y, int width, int height) +{ +#if wxUSE_DEFERRED_SIZING + // if our parent had prepared a defer window handle for us, use it (unless + // we are a top level window) + wxWindowMSW * const parent = IsTopLevel() ? NULL : GetParent(); + + HDWP hdwp = parent ? (HDWP)parent->m_hDWP : NULL; + if ( hdwp ) + { + hdwp = ::DeferWindowPos(hdwp, (HWND)hwnd, NULL, x, y, width, height, + SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOACTIVATE); + if ( !hdwp ) + { + wxLogLastError(wxT("DeferWindowPos")); + } + } + + if ( parent ) + { + // hdwp must be updated as it may have been changed + parent->m_hDWP = (WXHANDLE)hdwp; + } + + if ( hdwp ) + { + // did deferred move, remember new coordinates of the window as they're + // different from what Windows would return for it + return true; + } + + // otherwise (or if deferring failed) move the window in place immediately +#endif // wxUSE_DEFERRED_SIZING + if ( !::MoveWindow((HWND)hwnd, x, y, width, height, IsShown()) ) + { + wxLogLastError(wxT("MoveWindow")); + } + + // if wxUSE_DEFERRED_SIZING, indicates that we didn't use deferred move, + // ignored otherwise + return false; +} + void wxWindowMSW::DoMoveWindow(int x, int y, int width, int height) { // TODO: is this consistent with other platforms? @@ -1536,9 +1954,18 @@ void wxWindowMSW::DoMoveWindow(int x, int y, int width, int height) width = 0; if (height < 0) height = 0; - if ( !::MoveWindow(GetHwnd(), x, y, width, height, TRUE) ) + + if ( DoMoveSibling(m_hWnd, x, y, width, height) ) { - wxLogLastError(wxT("MoveWindow")); +#if wxUSE_DEFERRED_SIZING + m_pendingPosition = wxPoint(x, y); + m_pendingSize = wxSize(width, height); + } + else // window was moved immediately, without deferring it + { + m_pendingPosition = wxDefaultPosition; + m_pendingSize = wxDefaultSize; +#endif // wxUSE_DEFERRED_SIZING } } @@ -1554,30 +1981,39 @@ void wxWindowMSW::DoSetSize(int x, int y, int width, int height, int sizeFlags) { // get the current size and position... int currentX, currentY; + int currentW, currentH; + GetPosition(¤tX, ¤tY); - int currentW,currentH; GetSize(¤tW, ¤tH); - // ... and don't do anything (avoiding flicker) if it's already ok + // ... and don't do anything (avoiding flicker) if it's already ok unless + // we're forced to resize the window if ( x == currentX && y == currentY && - width == currentW && height == currentH ) + width == currentW && height == currentH && + !(sizeFlags & wxSIZE_FORCE) ) { + if (sizeFlags & wxSIZE_FORCE_EVENT) + { + wxSizeEvent event( wxSize(width,height), GetId() ); + event.SetEventObject( this ); + HandleWindowEvent( event ); + } return; } - if ( x == -1 && !(sizeFlags & wxSIZE_ALLOW_MINUS_ONE) ) + if ( x == wxDefaultCoord && !(sizeFlags & wxSIZE_ALLOW_MINUS_ONE) ) x = currentX; - if ( y == -1 && !(sizeFlags & wxSIZE_ALLOW_MINUS_ONE) ) + if ( y == wxDefaultCoord && !(sizeFlags & wxSIZE_ALLOW_MINUS_ONE) ) y = currentY; AdjustForParentClientOrigin(x, y, sizeFlags); - wxSize size(-1, -1); - if ( width == -1 ) + wxSize size = wxDefaultSize; + if ( width == wxDefaultCoord ) { if ( sizeFlags & wxSIZE_AUTO_WIDTH ) { - size = DoGetBestSize(); + size = GetBestSize(); width = size.x; } else @@ -1587,15 +2023,15 @@ void wxWindowMSW::DoSetSize(int x, int y, int width, int height, int sizeFlags) } } - if ( height == -1 ) + if ( height == wxDefaultCoord ) { if ( sizeFlags & wxSIZE_AUTO_HEIGHT ) { - if ( size.x == -1 ) + if ( size.x == wxDefaultCoord ) { - size = DoGetBestSize(); + size = GetBestSize(); } - //else: already called DoGetBestSize() above + //else: already called GetBestSize() above height = size.y; } @@ -1611,11 +2047,12 @@ void wxWindowMSW::DoSetSize(int x, int y, int width, int height, int sizeFlags) void wxWindowMSW::DoSetClientSize(int width, int height) { - // setting the client size is less obvious than it it could have been + // setting the client size is less obvious than 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 + // may [dis]appear and/or its menubar may [un]wrap (and AdjustWindowRect() + // doesn't take neither into account) 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 @@ -1628,28 +2065,21 @@ void wxWindowMSW::DoSetClientSize(int width, int height) RECT rectClient; ::GetClientRect(GetHwnd(), &rectClient); - // if the size is already ok, stop here (rectClient.left = top = 0) - if ( (rectClient.right == width || width == -1) && - (rectClient.bottom == height || height == -1) ) + // if the size is already ok, stop here (NB: rectClient.left = top = 0) + if ( (rectClient.right == width || width == wxDefaultCoord) && + (rectClient.bottom == height || height == wxDefaultCoord) ) { break; } - 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 RECT rectWin; ::GetWindowRect(GetHwnd(), &rectWin); - widthClient += rectWin.right - rectWin.left - rectClient.right; - heightClient += rectWin.bottom - rectWin.top - rectClient.bottom; - - POINT point; - point.x = rectWin.left; - point.y = rectWin.top; + const int widthWin = rectWin.right - rectWin.left, + heightWin = rectWin.bottom - rectWin.top; // MoveWindow positions the child windows relative to the parent, so // adjust if necessary @@ -1658,19 +2088,54 @@ void wxWindowMSW::DoSetClientSize(int width, int height) wxWindow *parent = GetParent(); if ( parent ) { - ::ScreenToClient(GetHwndOf(parent), &point); + ::ScreenToClient(GetHwndOf(parent), (POINT *)&rectWin); } } - DoMoveWindow(point.x, point.y, widthClient, heightClient); + // don't call DoMoveWindow() because we want to move window immediately + // and not defer it here as otherwise the value returned by + // GetClient/WindowRect() wouldn't change as the window wouldn't be + // really resized + if ( !::MoveWindow(GetHwnd(), + rectWin.left, + rectWin.top, + width + widthWin - rectClient.right, + height + heightWin - rectClient.bottom, + TRUE) ) + { + wxLogLastError(wxT("MoveWindow")); + } } } -// For implementation purposes - sometimes decorations make the client area -// smaller -wxPoint wxWindowMSW::GetClientAreaOrigin() const +wxSize wxWindowMSW::DoGetBorderSize() const { - return wxPoint(0, 0); + wxCoord border; + switch ( GetBorder() ) + { + case wxBORDER_STATIC: + case wxBORDER_SIMPLE: + border = 1; + break; + + case wxBORDER_SUNKEN: + border = 2; + break; + + case wxBORDER_RAISED: + case wxBORDER_DOUBLE: + border = 3; + break; + + default: + wxFAIL_MSG( wxT("unknown border style") ); + // fall through + + case wxBORDER_NONE: + border = 0; + } + + return 2*wxSize(border, border); } // --------------------------------------------------------------------------- @@ -1693,36 +2158,28 @@ int wxWindowMSW::GetCharWidth() const #endif } -void wxWindowMSW::GetTextExtent(const wxString& string, - int *x, int *y, - int *descent, int *externalLeading, - const wxFont *theFont) const +void wxWindowMSW::DoGetTextExtent(const wxString& string, + int *x, int *y, + int *descent, + int *externalLeading, + const wxFont *fontToUse) const { - const wxFont *fontToUse = theFont; - if ( !fontToUse ) - fontToUse = &m_font; + wxASSERT_MSG( !fontToUse || fontToUse->IsOk(), + wxT("invalid font in GetTextExtent()") ); - HWND hWnd = GetHwnd(); - HDC dc = ::GetDC(hWnd); + HFONT hfontToUse; + if ( fontToUse ) + hfontToUse = GetHfontOf(*fontToUse); + else + hfontToUse = GetHfontOf(GetFont()); - HFONT fnt = 0; - HFONT hfontOld = 0; - if ( fontToUse && fontToUse->Ok() ) - { - fnt = (HFONT)((wxFont *)fontToUse)->GetResourceHandle(); // const_cast - if ( fnt ) - hfontOld = (HFONT)SelectObject(dc,fnt); - } + WindowHDC hdc(GetHwnd()); + SelectInHDC selectFont(hdc, hfontToUse); SIZE sizeRect; TEXTMETRIC tm; - GetTextExtentPoint(dc, string, (int)string.Length(), &sizeRect); - GetTextMetrics(dc, &tm); - - if ( fontToUse && fnt && hfontOld ) - SelectObject(dc, hfontOld); - - ReleaseDC(hWnd, dc); + ::GetTextExtentPoint32(hdc, string.t_str(), string.length(), &sizeRect); + GetTextMetrics(hdc, &tm); if ( x ) *x = sizeRect.cx; @@ -1750,36 +2207,57 @@ 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 ) + while ( ::PeekMessage(&msg, (HWND)0, WM_COMMAND, WM_COMMAND, PM_REMOVE) ) { - wxTheApp->DoMessage((WXMSG *)&msg); - } + if ( msg.message == WM_QUIT ) + { + // if we retrieved a WM_QUIT, insert back into the message queue. + ::PostQuitMessage(0); + break; + } - // If we retrieved a WM_QUIT, insert back into the message queue. - if (msg.message == WM_QUIT) - ::PostQuitMessage(0); + // luckily (as we don't have access to wxEventLoopImpl method from here + // anyhow...) we don't need to pre process WM_COMMANDs so dispatch it + // immediately + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + } } bool wxWindowMSW::DoPopupMenu(wxMenu *menu, int x, int y) { - menu->SetInvokingWindow(this); menu->UpdateUI(); - HWND hWnd = GetHwnd(); - HMENU hMenu = GetHmenuOf(menu); - POINT point; - point.x = x; - point.y = y; - ::ClientToScreen(hWnd, &point); - wxCurrentPopupMenu = menu; - UINT flags = 0; -#if !defined(__WXWINCE__) - flags = TPM_RIGHTBUTTON; -#endif - ::TrackPopupMenu(hMenu, flags, point.x, point.y, 0, hWnd, NULL); + wxPoint pt; + if ( x == wxDefaultCoord && y == wxDefaultCoord ) + { + pt = wxGetMousePosition(); + } + else + { + pt = ClientToScreen(wxPoint(x, y)); + } - // we need to do it righ now as otherwise the events are never going to be +#if defined(__WXWINCE__) + static const UINT flags = 0; +#else // !__WXWINCE__ + UINT flags = TPM_RIGHTBUTTON; + // NT4 doesn't support TPM_RECURSE and simply doesn't show the menu at all + // when it's use, I'm not sure about Win95/98 but prefer to err on the safe + // side and not to use it there neither -- modify the test if it does work + // on these systems + if ( wxGetWinVersion() >= wxWinVersion_5 ) + { + // using TPM_RECURSE allows us to show a popup menu while another menu + // is opened which can be useful and is supported by the other + // platforms, so allow it under Windows too + flags |= TPM_RECURSE; + } +#endif // __WXWINCE__/!__WXWINCE__ + + ::TrackPopupMenu(GetHmenuOf(menu), flags, pt.x, pt.y, 0, GetHwnd(), NULL); + + // we need to do it right now as otherwise the events are never going to be // sent to wxCurrentPopupMenu from HandleCommand() // // note that even eliminating (ugly) wxCurrentPopupMenu global wouldn't @@ -1788,11 +2266,7 @@ bool wxWindowMSW::DoPopupMenu(wxMenu *menu, int x, int y) // for example) and so we do need to process the event immediately wxYieldForCommandsOnly(); - wxCurrentPopupMenu = NULL; - - menu->SetInvokingWindow(NULL); - - return TRUE; + return true; } #endif // wxUSE_MENUS_NATIVE @@ -1801,12 +2275,25 @@ bool wxWindowMSW::DoPopupMenu(wxMenu *menu, int x, int y) // pre/post message processing // =========================================================================== -long wxWindowMSW::MSWDefWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) +WXLRESULT wxWindowMSW::MSWDefWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) { + WXLRESULT rc; if ( m_oldWndProc ) - return ::CallWindowProc(CASTWNDPROC m_oldWndProc, GetHwnd(), (UINT) nMsg, (WPARAM) wParam, (LPARAM) lParam); + rc = ::CallWindowProc(CASTWNDPROC m_oldWndProc, GetHwnd(), (UINT) nMsg, (WPARAM) wParam, (LPARAM) lParam); else - return ::DefWindowProc(GetHwnd(), nMsg, wParam, lParam); + rc = ::DefWindowProc(GetHwnd(), nMsg, wParam, lParam); + + // Special hack used by wxTextEntry auto-completion only: this event is + // sent after the normal keyboard processing so that its handler could use + // the updated contents of the text control, after taking the key that was + // pressed into account. + if ( nMsg == WM_CHAR ) + { + wxKeyEvent event(CreateCharEvent(wxEVT_AFTER_CHAR, wParam, lParam)); + HandleWindowEvent(event); + } + + return rc; } bool wxWindowMSW::MSWProcessMessage(WXMSG* pMsg) @@ -1820,7 +2307,6 @@ bool wxWindowMSW::MSWProcessMessage(WXMSG* pMsg) // here we try to do all the job which ::IsDialogMessage() usually does // internally -#if 1 if ( msg->message == WM_KEYDOWN ) { bool bCtrlDown = wxIsCtrlDown(); @@ -1829,65 +2315,75 @@ bool wxWindowMSW::MSWProcessMessage(WXMSG* pMsg) // WM_GETDLGCODE: ask the control if it wants the key for itself, // don't process it if it's the case (except for Ctrl-Tab/Enter // combinations which are always processed) - LONG lDlgCode = 0; - if ( !bCtrlDown ) - { - lDlgCode = ::SendMessage(msg->hwnd, WM_GETDLGCODE, 0, 0); + LONG 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; - } + // surprisingly, 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; + bool bForward = true, + bWindowChange = false, + bFromTab = false; // should we process this message specially? - bool bProcess = TRUE; + bool bProcess = true; switch ( msg->wParam ) { case VK_TAB: - // assume that nobody wants Shift-TAB for himself - if we - // don't do it there is no easy way for a control to grab - // TABs but still let Shift-TAB work as navugation key - if ( (lDlgCode & DLGC_WANTTAB) && !bShiftDown ) { - bProcess = FALSE; + if ( (lDlgCode & DLGC_WANTTAB) && !bCtrlDown ) + { + // let the control have the TAB + bProcess = false; } - else { + else // use it for navigation + { // Ctrl-Tab cycles thru notebook pages bWindowChange = bCtrlDown; bForward = !bShiftDown; + bFromTab = true; } break; case VK_UP: case VK_LEFT: if ( (lDlgCode & DLGC_WANTARROWS) || bCtrlDown ) - bProcess = FALSE; + bProcess = false; else - bForward = FALSE; + bForward = false; break; case VK_DOWN: case VK_RIGHT: if ( (lDlgCode & DLGC_WANTARROWS) || bCtrlDown ) - bProcess = FALSE; + bProcess = false; + break; + + case VK_PRIOR: + bForward = false; + // fall through + + case VK_NEXT: + // we treat PageUp/Dn as arrows because chances are that + // a control which needs arrows also needs them for + // navigation (e.g. wxTextCtrl, wxListCtrl, ...) + if ( (lDlgCode & DLGC_WANTARROWS) && !bCtrlDown ) + bProcess = false; + else // OTOH Ctrl-PageUp/Dn works as [Shift-]Ctrl-Tab + bWindowChange = true; break; case VK_RETURN: { - if ( (lDlgCode & DLGC_WANTMESSAGE) && !bCtrlDown ) - { - // control wants to process Enter itself, don't - // call IsDialogMessage() which would interpret - // it - return FALSE; - } - else if ( lDlgCode & DLGC_BUTTON ) +#if wxUSE_BUTTON + // currently active button should get enter press even + // if there is a default button elsewhere so check if + // this window is a button first + wxWindow *btn = NULL; + if ( lDlgCode & DLGC_DEFPUSHBUTTON ) { // let IsDialogMessage() handle this for all // buttons except the owner-drawn ones which it @@ -1896,56 +2392,88 @@ bool wxWindowMSW::MSWProcessMessage(WXMSG* pMsg) if ( (style & BS_OWNERDRAW) == BS_OWNERDRAW ) { // emulate the button click - wxWindow *btn = wxFindWinFromHandle((WXHWND)msg->hwnd); - if ( btn ) - btn->MSWCommand(BN_CLICKED, 0 /* unused */); + btn = wxFindWinFromHandle(msg->hwnd); } - - bProcess = FALSE; } - // FIXME: this should be handled by - // wxNavigationKeyEvent handler and not here!! - else + else // not a button itself, do we have default button? { -#if wxUSE_BUTTON - wxButton *btn = wxDynamicCast(GetDefaultItem(), - wxButton); - if ( btn && btn->IsEnabled() ) + // check if this window or any of its ancestors + // wants the message for itself (we always reserve + // Ctrl-Enter for dialog navigation though) + wxWindow *win = this; + if ( !bCtrlDown ) { - // if we do have a default button, do press it - btn->MSWCommand(BN_CLICKED, 0 /* unused */); + // this will contain the dialog code of this + // window and all of its parent windows in turn + LONG lDlgCode2 = lDlgCode; - return TRUE; - } - 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()) + while ( win ) { - // if not a top level window, let parent - // handle it - return FALSE; + if ( lDlgCode2 & DLGC_WANTMESSAGE ) + { + // as it wants to process Enter itself, + // don't call IsDialogMessage() which + // would consume it + return false; + } + + // don't propagate keyboard messages beyond + // the first top level window parent + if ( win->IsTopLevel() ) + break; + + win = win->GetParent(); + + lDlgCode2 = ::SendMessage + ( + GetHwndOf(win), + WM_GETDLGCODE, + 0, + 0 + ); } - //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 + } + else // bCtrlDown + { + win = wxGetTopLevelParent(win); + } + + wxTopLevelWindow * const + tlw = wxDynamicCast(win, wxTopLevelWindow); + if ( tlw ) + { + btn = wxDynamicCast(tlw->GetDefaultItem(), + wxButton); } } + + if ( btn && btn->IsEnabled() ) + { + btn->MSWCommand(BN_CLICKED, 0 /* unused */); + return true; + } + + // This "Return" key press won't be actually used for + // navigation so don't generate wxNavigationKeyEvent + // for it but still pass it to IsDialogMessage() as it + // may handle it in some other way (e.g. by playing the + // default error sound). + bProcess = false; + +#endif // wxUSE_BUTTON + +#ifdef __WXWINCE__ + // map Enter presses into button presses on PDAs + wxJoystickEvent event(wxEVT_JOY_BUTTON_DOWN); + event.SetEventObject(this); + if ( HandleWindowEvent(event) ) + return true; +#endif // __WXWINCE__ } break; default: - bProcess = FALSE; + bProcess = false; } if ( bProcess ) @@ -1953,129 +2481,25 @@ bool wxWindowMSW::MSWProcessMessage(WXMSG* pMsg) wxNavigationKeyEvent event; event.SetDirection(bForward); event.SetWindowChange(bWindowChange); + event.SetFromTab(bFromTab); event.SetEventObject(this); - if ( GetEventHandler()->ProcessEvent(event) ) - { - return TRUE; - } - } - } -#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 ) - { - // don't process system keys here - if ( !(HIWORD(msg->lParam) & KF_ALTDOWN) ) - { - if ( (msg->wParam == VK_TAB) && wxIsCtrlDown() ) + if ( HandleWindowEvent(event) ) { - // find the first notebook parent and change its page - wxWindow *win = this; - wxNotebook *nbook = NULL; - while ( win && !nbook ) - { - nbook = wxDynamicCast(win, wxNotebook); - win = win->GetParent(); - } - - if ( nbook ) - { - bool forward = !wxIsShiftDown(); + // as we don't call IsDialogMessage(), which would take of + // this by default, we need to manually send this message + // so that controls can change their UI state if needed + MSWUpdateUIState(UIS_CLEAR, UISF_HIDEFOCUS); - nbook->AdvanceSelection(forward); - } + return true; } } } -#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() 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; - } + if ( ::IsDialogMessage(GetHwnd(), msg) ) + { + // IsDialogMessage() did something... + return true; } } #endif // __WXUNIVERSAL__ @@ -2086,11 +2510,11 @@ bool wxWindowMSW::MSWProcessMessage(WXMSG* pMsg) // relay mouse move events to the tooltip control MSG *msg = (MSG *)pMsg; if ( msg->message == WM_MOUSEMOVE ) - m_tooltip->RelayEvent(pMsg); + wxToolTip::RelayEvent(pMsg); } #endif // wxUSE_TOOLTIPS - return FALSE; + return false; } bool wxWindowMSW::MSWTranslateMessage(WXMSG* pMsg) @@ -2099,14 +2523,99 @@ bool wxWindowMSW::MSWTranslateMessage(WXMSG* pMsg) return m_acceleratorTable.Translate(this, pMsg); #else (void) pMsg; - return FALSE; + return false; #endif // wxUSE_ACCEL } -bool wxWindowMSW::MSWShouldPreProcessMessage(WXMSG* WXUNUSED(pMsg)) +bool wxWindowMSW::MSWShouldPreProcessMessage(WXMSG* msg) { - // preprocess all messages by default - return TRUE; + // all tests below have to deal with various bugs/misfeatures of + // IsDialogMessage(): we have to prevent it from being called from our + // MSWProcessMessage() in some situations + + // don't let IsDialogMessage() get VK_ESCAPE 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 ) + { + return false; + } + + // ::IsDialogMessage() is broken and may sometimes hang the application by + // going into an infinite loop when it tries to find the control to give + // focus to when Alt- is pressed, so we try to detect [some of] the + // situations when this may happen and not call it then + if ( msg->message != WM_SYSCHAR ) + return true; + + // 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 ) + { + // pessimistic by default + canSafelyCallIsDlgMsg = false; + for ( wxWindowList::compatibility_iterator node = GetChildren().GetFirst(); + node; + node = node->GetNext() ) + { + wxWindow * const win = node->GetData(); + if ( win->CanAcceptFocus() && + !wxHasWindowExStyle(win, 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); + } + } + + return canSafelyCallIsDlgMsg; } // --------------------------------------------------------------------------- @@ -2138,13 +2647,10 @@ void wxWindowMSW::UnpackScroll(WXWPARAM wParam, WXLPARAM lParam, } void wxWindowMSW::UnpackCtlColor(WXWPARAM wParam, WXLPARAM lParam, - WXWORD *nCtlColor, WXHDC *hdc, WXHWND *hwnd) + WXHDC *hdc, WXHWND *hwnd) { -#ifndef __WXMICROWIN__ - *nCtlColor = CTLCOLOR_BTN; *hwnd = (WXHWND)lParam; *hdc = (WXHDC)wParam; -#endif } void wxWindowMSW::UnpackMenuSelect(WXWPARAM wParam, WXLPARAM lParam, @@ -2156,7 +2662,7 @@ void wxWindowMSW::UnpackMenuSelect(WXWPARAM wParam, WXLPARAM lParam, } // --------------------------------------------------------------------------- -// Main wxWindows window proc and the window proc for wxWindow +// Main wxWidgets window proc and the window proc for wxWindow // --------------------------------------------------------------------------- // Hook for new window just as it's being created, when the window isn't yet @@ -2178,14 +2684,20 @@ wxWindowCreationHook::~wxWindowCreationHook() // 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(hWnd=%08lx, wParam=%8lx, lParam=%8lx)"), - wxGetMessageName(message), (long)hWnd, (long)wParam, lParam); -#endif // __WXDEBUG__ + // trace all messages: useful for the debugging but noticeably slows down + // the code so don't do it by default +#if wxDEBUG_LEVEL >= 2 + // notice that we cast wParam and lParam to long to avoid mismatch with + // format specifiers in 64 bit builds where they are both int64 quantities + // + // casting like this loses information, of course, but it shouldn't matter + // much for this diagnostic code and it keeps the code simple + wxLogTrace("winmsg", + wxT("Processing %s(hWnd=%p, wParam=%08lx, lParam=%08lx)"), + wxGetMessageName(message), hWnd, (long)wParam, (long)lParam); +#endif // wxDEBUG_LEVEL >= 2 - wxWindowMSW *wnd = wxFindWinFromHandle((WXHWND) hWnd); + wxWindowMSW *wnd = wxFindWinFromHandle(hWnd); // when we get the first message for the HWND we just created, we associate // it with wxWindow stored in gs_winBeingCreated @@ -2199,7 +2711,7 @@ LRESULT WXDLLEXPORT APIENTRY _EXPORT wxWndProc(HWND hWnd, UINT message, WPARAM w LRESULT rc; - if ( wnd ) + if ( wnd && wxGUIEventLoop::AllowProcessing(wnd) ) rc = wnd->MSWWindowProc(message, wParam, lParam); else rc = ::DefWindowProc(hWnd, message, wParam, lParam); @@ -2207,17 +2719,20 @@ LRESULT WXDLLEXPORT APIENTRY _EXPORT wxWndProc(HWND hWnd, UINT message, WPARAM w return rc; } -long wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam) +bool +wxWindowMSW::MSWHandleMessage(WXLRESULT *result, + WXUINT message, + WXWPARAM wParam, + WXLPARAM lParam) { // did we process the message? - bool processed = FALSE; + bool processed = false; // the return value union { bool allow; - long result; - WXHICON hIcon; + WXLRESULT result; WXHBRUSH hBrush; } rc; @@ -2239,13 +2754,17 @@ long wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam break; case WM_DESTROY: - // never set processed to TRUE and *always* pass WM_DESTROY to + // 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_SIZE: + processed = HandleSize(LOWORD(lParam), HIWORD(lParam), wParam); + break; + case WM_MOVE: processed = HandleMove(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); break; @@ -2268,37 +2787,19 @@ long wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam } } break; -#endif - case WM_SIZE: - switch ( wParam ) + case WM_ENTERSIZEMOVE: { - 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 + processed = HandleEnterSizeMove(); + } + break; - case SIZE_MAXIMIZED: - case SIZE_RESTORED: - processed = HandleSize(LOWORD(lParam), HIWORD(lParam), - wParam); + case WM_EXITSIZEMOVE: + { + processed = HandleExitSizeMove(); } break; -#if !defined(__WXWINCE__) case WM_SIZING: { LPRECT pRect = (LPRECT)lParam; @@ -2316,10 +2817,11 @@ long wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam } } break; -#endif +#endif // !__WXWINCE__ #if !defined(__WXMICROWIN__) && !defined(__WXWINCE__) case WM_ACTIVATEAPP: + // This implicitly sends a wxEVT_ACTIVATE_APP event wxTheApp->SetActive(wParam != 0, FindFocus()); break; #endif @@ -2335,57 +2837,41 @@ long wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam break; case WM_SETFOCUS: - processed = HandleSetFocus((WXHWND)(HWND)wParam); + processed = HandleSetFocus((WXHWND)wParam); break; case WM_KILLFOCUS: - processed = HandleKillFocus((WXHWND)(HWND)wParam); + processed = HandleKillFocus((WXHWND)wParam); + break; + + case WM_PRINTCLIENT: + processed = HandlePrintClient((WXHDC)wParam); break; case WM_PAINT: + if ( wParam ) { - if ( wParam ) - { - // cast to wxWindow is needed for wxUniv - wxPaintDCEx dc((wxWindow *)this, (WXHDC)wParam); - processed = HandlePaint(); - } - else - { - processed = HandlePaint(); - } - break; - } + wxPaintDCEx dc((wxWindow *)this, (WXHDC)wParam); -#ifndef __WXWINCE__ - case WM_PRINT: + processed = HandlePaint(); + } + else // no DC given { - // Don't call the wx handlers in this case - if ( wxIsKindOf(this, wxListCtrl) ) - break; - - if ( lParam & PRF_ERASEBKGND ) - HandleEraseBkgnd((WXHDC)(HDC)wParam); - - wxPaintDCEx dc((wxWindow *)this, (WXHDC)wParam); processed = HandlePaint(); } break; -#endif 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 +#endif // __WXUNIVERSAL__ + // don't let the DefWindowProc() destroy our window - we'll do it // ourselves in ~wxWindow - processed = TRUE; + processed = true; rc.result = TRUE; -#endif break; case WM_SHOWWINDOW: @@ -2398,11 +2884,31 @@ long wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam wParam); break; +#ifdef HAVE_TRACKMOUSEEVENT + case WM_MOUSELEAVE: + // filter out excess WM_MOUSELEAVE events sent after PopupMenu() + // (on XP at least) + if ( m_mouseInWindow ) + { + GenerateMouseLeave(); + } + + // always pass processed back as false, this allows the window + // manager to process the message too. This is needed to + // ensure windows XP themes work properly as the mouse moves + // over widgets like buttons. So don't set processed to true here. + break; +#endif // HAVE_TRACKMOUSEEVENT + #if wxUSE_MOUSEWHEEL case WM_MOUSEWHEEL: - processed = HandleMouseWheel(wParam, lParam); + processed = HandleMouseWheel(wxMOUSE_WHEEL_VERTICAL, wParam, lParam); break; -#endif + + case WM_MOUSEHWHEEL: + processed = HandleMouseWheel(wxMOUSE_WHEEL_HORIZONTAL, wParam, lParam); + break; +#endif // wxUSE_MOUSEWHEEL case WM_LBUTTONDOWN: case WM_LBUTTONUP: @@ -2413,6 +2919,11 @@ long wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam case WM_MBUTTONDOWN: case WM_MBUTTONUP: case WM_MBUTTONDBLCLK: +#ifdef wxHAS_XBUTTON + case WM_XBUTTONDOWN: + case WM_XBUTTONUP: + case WM_XBUTTONDBLCLK: +#endif // wxHAS_XBUTTON { #ifdef __WXMICROWIN__ // MicroWindows seems to ignore the fact that a window is @@ -2423,7 +2934,7 @@ long wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam { if (!win->IsEnabled()) { - processed = TRUE; + processed = true; break; } @@ -2439,8 +2950,10 @@ long wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam int x = GET_X_LPARAM(lParam), y = GET_Y_LPARAM(lParam); +#ifdef __WXWINCE__ // redirect the event to a static control if necessary by - // finding one under mouse + // finding one under mouse because under CE the static controls + // don't generate mouse events (even with SS_NOTIFY) wxWindowMSW *win; if ( GetCapture() == this ) { @@ -2454,20 +2967,57 @@ long wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam // this should never happen wxCHECK_MSG( win, 0, - _T("FindWindowForMouseEvent() returned NULL") ); + wxT("FindWindowForMouseEvent() returned NULL") ); + } +#ifdef __POCKETPC__ + if (IsContextMenuEnabled() && message == WM_LBUTTONDOWN) + { + SHRGINFO shrgi = {0}; + + shrgi.cbSize = sizeof(SHRGINFO); + shrgi.hwndClient = (HWND) GetHWND(); + shrgi.ptDown.x = x; + shrgi.ptDown.y = y; + + shrgi.dwFlags = SHRG_RETURNCMD; + // shrgi.dwFlags = SHRG_NOTIFYPARENT; + + if (GN_CONTEXTMENU == ::SHRecognizeGesture(&shrgi)) + { + wxPoint pt(x, y); + pt = ClientToScreen(pt); + + wxContextMenuEvent evtCtx(wxEVT_CONTEXT_MENU, GetId(), pt); + + evtCtx.SetEventObject(this); + if (HandleWindowEvent(evtCtx)) + { + processed = true; + return true; + } + } + } +#endif + +#else // !__WXWINCE__ + wxWindowMSW *win = this; +#endif // __WXWINCE__/!__WXWINCE__ + + processed = win->HandleMouseEvent(message, x, y, wParam); + // if the app didn't eat the event, handle it in the default + // way, that is by giving this window the focus + if ( !processed ) + { // 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 + // problems, so don't do it for them (unnecessary anyhow) if ( !win->IsOfStandardClass() ) { - if ( message == WM_LBUTTONDOWN && win->AcceptsFocus() ) + if ( message == WM_LBUTTONDOWN && win->IsFocusable() ) win->SetFocus(); } } - - processed = win->HandleMouseEvent(message, x, y, wParam); } break; @@ -2481,16 +3031,12 @@ long wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam case MM_JOY1BUTTONUP: case MM_JOY2BUTTONUP: processed = HandleJoystickEvent(message, - GET_X_LPARAM(lParam), - GET_Y_LPARAM(lParam), + LOWORD(lParam), + HIWORD(lParam), wParam); break; #endif // __WXMICROWIN__ - case WM_SYSCOMMAND: - processed = HandleSysCommand(wParam, lParam); - break; - case WM_COMMAND: { WORD id, cmd; @@ -2505,36 +3051,41 @@ long wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam processed = HandleNotify((int)wParam, lParam, &rc.result); break; - // for these messages we must return TRUE if process the message + // we only need to reply to WM_NOTIFYFORMAT manually when using MSLU, + // otherwise DefWindowProc() does it perfectly fine for us, but MSLU + // apparently doesn't always behave properly and needs some help +#if wxUSE_UNICODE_MSLU && defined(NF_QUERY) + case WM_NOTIFYFORMAT: + if ( lParam == NF_QUERY ) + { + processed = true; + rc.result = NFR_UNICODE; + } + break; +#endif // wxUSE_UNICODE_MSLU + + // for these messages we must return true if process the message #ifdef WM_DRAWITEM case WM_DRAWITEM: - case WM_MEASUREITEM: - { - int idCtrl = (UINT)wParam; - if ( message == WM_DRAWITEM ) - { - processed = MSWOnDrawItem(idCtrl, - (WXDRAWITEMSTRUCT *)lParam); - } - else - { - processed = MSWOnMeasureItem(idCtrl, - (WXMEASUREITEMSTRUCT *)lParam); - } + processed = MSWOnDrawItem(wParam, (WXDRAWITEMSTRUCT *)lParam); + if ( processed ) + rc.result = TRUE; + break; - if ( processed ) - rc.result = TRUE; - } + case WM_MEASUREITEM: + processed = MSWOnMeasureItem(wParam, (WXMEASUREITEMSTRUCT *)lParam); + if ( processed ) + rc.result = TRUE; break; #endif // defined(WM_DRAWITEM) case WM_GETDLGCODE: - if ( !IsOfStandardClass() ) + if ( !IsOfStandardClass() || HasFlag(wxWANTS_CHARS) ) { // we always want to get the char events rc.result = DLGC_WANTCHARS; - if ( GetWindowStyleFlag() & wxWANTS_CHARS ) + if ( HasFlag(wxWANTS_CHARS) ) { // in fact, we want everything rc.result |= DLGC_WANTARROWS | @@ -2542,39 +3093,43 @@ long wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam DLGC_WANTALLKEYS; } - processed = TRUE; + processed = true; } //else: get the dlg code from the DefWindowProc() break; case WM_SYSKEYDOWN: case WM_KEYDOWN: - // If this has been processed by an event handler, return 0 now - // (we've handled it). + // Generate the key down event in any case. m_lastKeydownProcessed = HandleKeyDown((WORD) wParam, lParam); if ( m_lastKeydownProcessed ) { - processed = TRUE; + // If it was processed by an event handler, we stop here, + // notably we intentionally don't generate char event then. + processed = true; } - - if ( !processed ) + else // key down event not handled { + // Examine the event to decide whether we need to generate a + // char event for it ourselves or let Windows do it. Window + // mostly only does it for the keys which produce printable + // characters (although there are exceptions, e.g. VK_ESCAPE or + // VK_BACK (but not VK_DELETE)) while we do it for all keys + // except the modifier ones (the wisdom of this is debatable + // but by now this decision is enshrined forever due to + // backwards compatibility). switch ( wParam ) { - // we consider these message "not interesting" to OnChar, so - // just don't do anything more with them + // No wxEVT_CHAR events are generated for these keys at all. 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 + // Windows will send us WM_CHAR for these ones so we'll + // generate wxEVT_CHAR for them later when we get it. case VK_ESCAPE: case VK_SPACE: case VK_RETURN: @@ -2584,6 +3139,17 @@ long wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam case VK_SUBTRACT: case VK_MULTIPLY: case VK_DIVIDE: + case VK_DECIMAL: + case VK_NUMPAD0: + case VK_NUMPAD1: + case VK_NUMPAD2: + case VK_NUMPAD3: + case VK_NUMPAD4: + case VK_NUMPAD5: + case VK_NUMPAD6: + case VK_NUMPAD7: + case VK_NUMPAD8: + case VK_NUMPAD9: case VK_OEM_1: case VK_OEM_2: case VK_OEM_3: @@ -2591,37 +3157,52 @@ long wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam case VK_OEM_5: case VK_OEM_6: case VK_OEM_7: + case VK_OEM_102: 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; - - TranslateKbdEventToMouse(this, &x, &y, &flags); - processed = HandleMouseEvent(WM_RBUTTONDOWN, x, y, flags); - } + processed = HandleMouseEvent(WM_RBUTTONDOWN, -1, -1, 0); break; #endif // VK_APPS default: - // do generate a CHAR event - processed = HandleChar((WORD)wParam, lParam); + if ( (wParam >= '0' && wParam <= '9') || + (wParam >= 'A' && wParam <= 'Z') ) + { + // We'll get WM_CHAR for those later too. + break; + } + + // But for the rest we won't get WM_CHAR later so we do + // need to generate the event right now. + wxKeyEvent event(wxEVT_CHAR); + InitAnyKeyEvent(event, wParam, lParam); + + // Set the "extended" bit in lParam because we want to + // generate CHAR events with WXK_HOME and not + // WXK_NUMPAD_HOME even if the "Home" key on numpad was + // pressed. + event.m_keyCode = wxMSWKeyboard::VKToWX + ( + wParam, + lParam | (KF_EXTENDED << 16) + ); + + // Don't produce events without any valid character + // code (even if this shouldn't normally happen...). + if ( event.m_keyCode != WXK_NONE ) + processed = HandleWindowEvent(event); } } if (message == WM_SYSKEYDOWN) // Let Windows still handle the SYSKEYs - processed = FALSE; + processed = false; break; case WM_SYSKEYUP: @@ -2630,11 +3211,7 @@ long wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam // special case of VK_APPS: treat it the same as right mouse button if ( wParam == VK_APPS ) { - WPARAM flags; - int x, y; - - TranslateKbdEventToMouse(this, &x, &y, &flags); - processed = HandleMouseEvent(WM_RBUTTONUP, x, y, flags); + processed = HandleMouseEvent(WM_RBUTTONUP, -1, -1, 0); } else #endif // VK_APPS @@ -2650,21 +3227,38 @@ long wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam // 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; + m_lastKeydownProcessed = false; + processed = true; } else { - processed = HandleChar((WORD)wParam, lParam, TRUE); + processed = HandleChar((WORD)wParam, lParam); } break; + case WM_IME_STARTCOMPOSITION: + // IME popup needs Escape as it should undo the changes in its + // entry window instead of e.g. closing the dialog for which the + // IME is used (and losing all the changes in the IME window). + gs_modalEntryWindowCount++; + break; + + case WM_IME_ENDCOMPOSITION: + gs_modalEntryWindowCount--; + break; + #if wxUSE_HOTKEY case WM_HOTKEY: processed = HandleHotKey((WORD)wParam, lParam); break; #endif // wxUSE_HOTKEY + case WM_CUT: + case WM_COPY: + case WM_PASTE: + processed = HandleClipboardEvent(message); + break; + case WM_HSCROLL: case WM_VSCROLL: { @@ -2679,7 +3273,7 @@ long wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam break; // CTLCOLOR messages are sent by children to query the parent for their - // colors#ifndef __WXMICROWIN__ + // colors #ifndef __WXMICROWIN__ case WM_CTLCOLORMSGBOX: case WM_CTLCOLOREDIT: @@ -2689,18 +3283,11 @@ long wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam case WM_CTLCOLORSCROLLBAR: case WM_CTLCOLORSTATIC: { - WXWORD nCtlColor; WXHDC hdc; WXHWND hwnd; - UnpackCtlColor(wParam, lParam, &nCtlColor, &hdc, &hwnd); - - processed = HandleCtlColor(&rc.hBrush, - (WXHDC)hdc, - (WXHWND)hwnd, - nCtlColor, - message, - wParam, - lParam); + UnpackCtlColor(wParam, lParam, &hdc, &hwnd); + + processed = HandleCtlColor(&rc.hBrush, (WXHDC)hdc, (WXHWND)hwnd); } break; #endif // !__WXMICROWIN__ @@ -2717,11 +3304,15 @@ long wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam #endif case WM_PALETTECHANGED: - processed = HandlePaletteChanged((WXHWND) (HWND) wParam); + processed = HandlePaletteChanged((WXHWND)wParam); break; case WM_CAPTURECHANGED: - processed = HandleCaptureChanged((WXHWND) (HWND) lParam); + processed = HandleCaptureChanged((WXHWND)lParam); + break; + + case WM_SETTINGCHANGE: + processed = HandleSettingChange(wParam, lParam); break; case WM_QUERYNEWPALETTE: @@ -2729,7 +3320,17 @@ long wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam break; case WM_ERASEBKGND: - processed = HandleEraseBkgnd((WXHDC)(HDC)wParam); + { +#ifdef wxHAS_MSW_BACKGROUND_ERASE_HOOK + // check if an override was configured for this window + EraseBgHooks::const_iterator it = gs_eraseBgHooks.find(this); + if ( it != gs_eraseBgHooks.end() ) + processed = it->second->MSWEraseBgHook((WXHDC)wParam); + else +#endif // wxHAS_MSW_BACKGROUND_ERASE_HOOK + processed = HandleEraseBkgnd((WXHDC)wParam); + } + if ( processed ) { // we processed the message, i.e. erased the background @@ -2744,7 +3345,7 @@ long wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam #endif case WM_INITDIALOG: - processed = HandleInitDialog((WXHWND)(HWND)wParam); + processed = HandleInitDialog((WXHWND)wParam); if ( processed ) { @@ -2768,7 +3369,7 @@ long wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam #endif case WM_SETCURSOR: - processed = HandleSetCursor((WXHWND)(HWND)wParam, + processed = HandleSetCursor((WXHWND)wParam, LOWORD(lParam), // hit test HIWORD(lParam)); // mouse msg @@ -2787,7 +3388,7 @@ long wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam //WPARAM dwFlags = (WPARAM) (DWORD) wParam; LPARAM dwObjId = (LPARAM) (DWORD) lParam; - if (dwObjId == OBJID_CLIENT && GetOrCreateAccessible()) + if (dwObjId == (LPARAM)OBJID_CLIENT && GetOrCreateAccessible()) { return LresultFromObject(IID_IAccessible, wParam, (IUnknown*) GetAccessible()->GetIAccessible()); } @@ -2798,50 +3399,48 @@ long wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam #if defined(WM_HELP) case WM_HELP: { - // HELPINFO doesn't seem to be supported on WinCE. + // by default, WM_HELP is propagated by DefWindowProc() upwards + // to the window parent but as we do it ourselves already + // (wxHelpEvent is derived from wxCommandEvent), we don't want + // to get the other events if we process this message at all + processed = true; + + // WM_HELP doesn't use lParam under CE #ifndef __WXWINCE__ HELPINFO* info = (HELPINFO*) lParam; - // Don't yet process menu help events, just windows - if (info->iContextType == HELPINFO_WINDOW) + if ( info->iContextType == HELPINFO_WINDOW ) { -#endif - wxWindowMSW* subjectOfHelp = this; - bool eventProcessed = FALSE; - while (subjectOfHelp && !eventProcessed) - { - wxHelpEvent helpEvent(wxEVT_HELP, - subjectOfHelp->GetId(), +#endif // !__WXWINCE__ + wxHelpEvent helpEvent + ( + wxEVT_HELP, + GetId(), #ifdef __WXWINCE__ - wxPoint(0, 0) + wxGetMousePosition() // what else? #else - wxPoint(info->MousePos.x, info->MousePos.y) + 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; + helpEvent.SetEventObject(this); + HandleWindowEvent(helpEvent); #ifndef __WXWINCE__ } - else if (info->iContextType == HELPINFO_MENUITEM) + else if ( info->iContextType == HELPINFO_MENUITEM ) { wxHelpEvent helpEvent(wxEVT_HELP, info->iCtrlId); helpEvent.SetEventObject(this); - processed = GetEventHandler()->ProcessEvent(helpEvent); + HandleWindowEvent(helpEvent); } - //else: processed is already FALSE -#endif + else // unknown help event? + { + processed = false; + } +#endif // !__WXWINCE__ } break; -#endif +#endif // WM_HELP #if !defined(__WXWINCE__) case WM_CONTEXTMENU: @@ -2851,11 +3450,26 @@ long wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam wxPoint pt(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); wxContextMenuEvent evtCtx(wxEVT_CONTEXT_MENU, GetId(), pt); - processed = GetEventHandler()->ProcessEvent(evtCtx); + + // we could have got an event from our child, reflect it back + // to it if this is the case + wxWindowMSW *win = NULL; + WXHWND hWnd = (WXHWND)wParam; + if ( hWnd != m_hWnd ) + { + win = FindItemByHWND(hWnd); + } + + if ( !win ) + win = this; + + evtCtx.SetEventObject(win); + processed = win->HandleWindowEvent(evtCtx); } break; #endif +#if wxUSE_MENUS case WM_MENUCHAR: // we're only interested in our own menus, not MF_SYSMENU if ( HIWORD(wParam) == MF_POPUP ) @@ -2865,60 +3479,199 @@ long wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam if ( i != wxNOT_FOUND ) { rc.result = MAKELRESULT(i, MNC_EXECUTE); - processed = TRUE; + processed = true; } } break; - } - - if ( !processed ) - { -#ifdef __WXDEBUG__ - wxLogTrace(wxTraceMessages, wxT("Forwarding %s to DefWindowProc."), - wxGetMessageName(message)); -#endif // __WXDEBUG__ - rc.result = MSWDefWindowProc(message, wParam, lParam); - } +#endif // wxUSE_MENUS - return rc.result; -} +#ifndef __WXWINCE__ + case WM_POWERBROADCAST: + { + bool vetoed; + processed = HandlePower(wParam, lParam, &vetoed); + rc.result = processed && vetoed ? BROADCAST_QUERY_DENY : TRUE; + } + break; +#endif // __WXWINCE__ -// ---------------------------------------------------------------------------- -// wxWindow <-> HWND map -// ---------------------------------------------------------------------------- +#if wxUSE_UXTHEME + // If we want the default themed border then we need to draw it ourselves + case WM_NCCALCSIZE: + { + wxUxThemeEngine* theme = wxUxThemeEngine::GetIfActive(); + const wxBorder border = TranslateBorder(GetBorder()); + if (theme && border == wxBORDER_THEME) + { + // first ask the widget to calculate the border size + rc.result = MSWDefWindowProc(message, wParam, lParam); + processed = true; + + // now alter the client size making room for drawing a + // themed border + RECT *rect; + NCCALCSIZE_PARAMS *csparam = NULL; + if ( wParam ) + { + csparam = (NCCALCSIZE_PARAMS *)lParam; + rect = &csparam->rgrc[0]; + } + else + { + rect = (RECT *)lParam; + } + + wxUxThemeHandle hTheme((const wxWindow *)this, L"EDIT"); + RECT rcClient = { 0, 0, 0, 0 }; + wxClientDC dc((wxWindow *)this); + wxMSWDCImpl *impl = (wxMSWDCImpl*) dc.GetImpl(); + + if ( theme->GetThemeBackgroundContentRect + ( + hTheme, + GetHdcOf(*impl), + EP_EDITTEXT, + ETS_NORMAL, + rect, + &rcClient) == S_OK ) + { + InflateRect(&rcClient, -1, -1); + if (wParam) + csparam->rgrc[0] = rcClient; + else + *((RECT*)lParam) = rcClient; + + // WVR_REDRAW triggers a bug whereby child windows are moved up and left, + // so don't use. + // rc.result = WVR_REDRAW; + } + } + } + break; + + case WM_NCPAINT: + { + wxUxThemeEngine* theme = wxUxThemeEngine::GetIfActive(); + const wxBorder border = TranslateBorder(GetBorder()); + if (theme && border == wxBORDER_THEME) + { + // first ask the widget to paint its non-client area, such as scrollbars, etc. + rc.result = MSWDefWindowProc(message, wParam, lParam); + processed = true; + + wxUxThemeHandle hTheme((const wxWindow *)this, L"EDIT"); + wxWindowDC dc((wxWindow *)this); + wxMSWDCImpl *impl = (wxMSWDCImpl*) dc.GetImpl(); + + // Clip the DC so that you only draw on the non-client area + RECT rcBorder; + wxCopyRectToRECT(GetSize(), rcBorder); + + RECT rcClient; + theme->GetThemeBackgroundContentRect( + hTheme, GetHdcOf(*impl), EP_EDITTEXT, ETS_NORMAL, &rcBorder, &rcClient); + InflateRect(&rcClient, -1, -1); + + ::ExcludeClipRect(GetHdcOf(*impl), rcClient.left, rcClient.top, + rcClient.right, rcClient.bottom); + + // Make sure the background is in a proper state + if (theme->IsThemeBackgroundPartiallyTransparent(hTheme, EP_EDITTEXT, ETS_NORMAL)) + { + theme->DrawThemeParentBackground(GetHwnd(), GetHdcOf(*impl), &rcBorder); + } + + // Draw the border + int nState; + if ( !IsEnabled() ) + nState = ETS_DISABLED; + // should we check this? + //else if ( ::GetWindowLong(GetHwnd(), GWL_STYLE) & ES_READONLY) + // nState = ETS_READONLY; + else + nState = ETS_NORMAL; + theme->DrawThemeBackground(hTheme, GetHdcOf(*impl), EP_EDITTEXT, nState, &rcBorder, NULL); + } + } + break; + +#endif // wxUSE_UXTHEME -wxWinHashTable *wxWinHandleHash = NULL; + default: + // try a custom message handler + const MSWMessageHandlers::const_iterator + i = gs_messageHandlers.find(message); + if ( i != gs_messageHandlers.end() ) + { + processed = (*i->second)(this, message, wParam, lParam); + } + } + + if ( !processed ) + return false; + + *result = rc.result; + + return true; +} + +WXLRESULT wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam) +{ + WXLRESULT result; + if ( !MSWHandleMessage(&result, message, wParam, lParam) ) + { +#if wxDEBUG_LEVEL >= 2 + wxLogTrace("winmsg", wxT("Forwarding %s to DefWindowProc."), + wxGetMessageName(message)); +#endif // wxDEBUG_LEVEL >= 2 + result = MSWDefWindowProc(message, wParam, lParam); + } + + return result; +} -wxWindow *wxFindWinFromHandle(WXHWND hWnd) +// ---------------------------------------------------------------------------- +// wxWindow <-> HWND map +// ---------------------------------------------------------------------------- + +wxWindow *wxFindWinFromHandle(HWND hwnd) { - return wxWinHandleHash->Get((long)hWnd); + WindowHandles::const_iterator i = gs_windowHandles.find(hwnd); + return i == gs_windowHandles.end() ? NULL : i->second; } -void wxAssociateWinWithHandle(HWND hWnd, wxWindowMSW *win) +void wxAssociateWinWithHandle(HWND hwnd, wxWindowMSW *win) { - // adding NULL hWnd is (first) surely a result of an error and + // 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") ); + 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) ) +#if wxDEBUG_LEVEL + WindowHandles::const_iterator i = gs_windowHandles.find(hwnd); + if ( i != gs_windowHandles.end() ) { - wxLogDebug(wxT("HWND %X already associated with another window (%s)"), - (int) hWnd, win->GetClassInfo()->GetClassName()); - } - else -#endif // __WXDEBUG__ - if (!oldWin) - { - wxWinHandleHash->Put((long)hWnd, (wxWindow *)win); + if ( i->second != win ) + { + wxFAIL_MSG( + wxString::Format( + wxT("HWND %p already associated with another window (%s)"), + hwnd, win->GetClassInfo()->GetClassName() + ) + ); + } + //else: this actually happens currently because we associate the window + // with its HWND during creation (if we create it) and also when + // SubclassWin() is called later, this is ok } +#endif // wxDEBUG_LEVEL + + gs_windowHandles[hwnd] = (wxWindow *)win; } void wxRemoveHandleAssociation(wxWindowMSW *win) { - wxWinHandleHash->Delete((long)win->GetHWND()); + gs_windowHandles.erase(GetHwndOf(win)); } // ---------------------------------------------------------------------------- @@ -2931,38 +3684,27 @@ void wxWindowMSW::MSWDestroyWindow() { } -bool wxWindowMSW::MSWGetCreateWindowCoords(const wxPoint& pos, +void wxWindowMSW::MSWGetCreateWindowCoords(const wxPoint& pos, const wxSize& size, int& x, int& y, int& w, int& h) const { - static const int DEFAULT_Y = 200; - static const int DEFAULT_H = 250; - - bool nonDefault = FALSE; + // CW_USEDEFAULT can't be used for child windows so just position them at + // the origin by default + x = pos.x == wxDefaultCoord ? 0 : pos.x; + y = pos.y == wxDefaultCoord ? 0 : pos.y; - 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; - } - else - { - // OTOH, if x is not set to CW_USEDEFAULT, y shouldn't be set to it - // neither because it is not handled as a special value by Windows then - // and so we have to choose some default value for it - x = pos.x; - y = pos.y == -1 ? DEFAULT_Y : pos.y; + AdjustForParentClientOrigin(x, y); - nonDefault = TRUE; - } + // We don't have any clearly good choice for the size by default neither + // but we must use something non-zero. + w = WidthDefault(size.x); + h = HeightDefault(size.y); /* 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 + specified. This was wrong because wxWidgets 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 @@ -2971,26 +3713,10 @@ bool wxWindowMSW::MSWGetCreateWindowCoords(const wxPoint& pos, 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 + size, nothing will happen as both wxWidgets 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 ) - { - // as above, h is not used at all in this case anyhow - w = - h = CW_USEDEFAULT; - } - else - { - // and, again as above, we can't set the height to CW_USEDEFAULT here - w = size.x; - h = size.y == -1 ? DEFAULT_H : size.y; - - nonDefault = TRUE; - } - - return nonDefault; } WXHWND wxWindowMSW::MSWGetParent() const @@ -3005,6 +3731,17 @@ bool wxWindowMSW::MSWCreate(const wxChar *wclass, WXDWORD style, WXDWORD extendedStyle) { + // check a common bug in the user code: if the window is created with a + // non-default ctor and Create() is called too, we'd create 2 HWND for a + // single wxWindow object and this results in all sorts of trouble, + // especially for wxTLWs + wxCHECK_MSG( !m_hWnd, true, "window can't be recreated" ); + + // this can happen if this function is called using the return value of + // wxApp::GetRegisteredClassName() which failed + wxCHECK_MSG( wclass, false, "failed to register window class?" ); + + // choose the position/size for the new window int x, y, w, h; (void)MSWGetCreateWindowCoords(pos, size, x, y, w, h); @@ -3017,58 +3754,37 @@ bool wxWindowMSW::MSWCreate(const wxChar *wclass, // 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 ) + if ( !HasFlag(wxFULL_REPAINT_ON_RESIZE) ) { - className += wxT("NR"); + className += wxApp::GetNoRedrawClassSuffix(); } // do create the window wxWindowCreationHook hook(this); -#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 - { - m_hWnd = (WXHWND)::CreateWindowEx - ( - extendedStyle, - className, - title ? title : wxEmptyString, - style, - x, y, w, h, - (HWND)MSWGetParent(), - (HMENU)controlId, - wxGetInstance(), - NULL // no extra data - ); - } + m_hWnd = (WXHWND)::CreateWindowEx + ( + extendedStyle, + className.t_str(), + title ? title : m_windowName.t_str(), + style, + x, y, w, h, + (HWND)MSWGetParent(), + (HMENU)wxUIntToPtr(controlId), + wxGetInstance(), + NULL // no extra data + ); if ( !m_hWnd ) { - wxLogSysError(_("Can't create window of class %s"), wclass); + wxLogSysError(_("Can't create window of class %s"), className.c_str()); - return FALSE; + return false; } SubclassWin(m_hWnd); - SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT)); - - return TRUE; + return true; } // =========================================================================== @@ -3079,14 +3795,12 @@ bool wxWindowMSW::MSWCreate(const wxChar *wclass, // WM_NOTIFY // --------------------------------------------------------------------------- -#ifdef __WIN95__ - bool wxWindowMSW::HandleNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) { #ifndef __WXMICROWIN__ LPNMHDR hdr = (LPNMHDR)lParam; HWND hWnd = hdr->hwndFrom; - wxWindow *win = wxFindWinFromHandle((WXHWND)hWnd); + wxWindow *win = wxFindWinFromHandle(hWnd); // if the control is one of our windows, let it handle the message itself if ( win ) @@ -3108,7 +3822,7 @@ bool wxWindowMSW::HandleNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) wxWindow *child = node->GetData(); if ( child->MSWOnNotify(idCtrl, lParam, result) ) { - return TRUE; + return true; } node = node->GetNext(); @@ -3118,7 +3832,7 @@ bool wxWindowMSW::HandleNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) // by default, handle it ourselves return MSWOnNotify(idCtrl, lParam, result); #else // __WXMICROWIN__ - return FALSE; + return false; #endif } @@ -3134,59 +3848,70 @@ bool wxWindowMSW::HandleTooltipNotify(WXUINT code, // 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() ) + if ( !(code == (WXUINT) TTN_NEEDTEXTA || code == (WXUINT) TTN_NEEDTEXTW) + || ttip.empty() ) { // not a tooltip message or no tooltip to show anyhow - return FALSE; + return false; } #endif LPTOOLTIPTEXT ttText = (LPTOOLTIPTEXT)lParam; + // We don't want to use the szText buffer because it has a limit of 80 + // bytes and this is not enough, especially for Unicode build where it + // limits the tooltip string length to only 40 characters + // + // The best would be, of course, to not impose any length limitations at + // all but then the buffer would have to be dynamic and someone would have + // to free it and we don't have the tooltip owner object here any more, so + // for now use our own static buffer with a higher fixed max length. + // + // Note that using a static buffer should not be a problem as only a single + // tooltip can be shown at the same time anyhow. #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 ( code == (WXUINT) TTN_NEEDTEXTW ) { -#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)); -*/ - // Fix by dimitrishortcut: see patch 771772 - - // FIXME: szText has a max of 80 bytes, so limit the tooltip string - // length accordingly. Ideally lpszText should be used, but who - // would be responsible for freeing the buffer? - - // Maximum length of a tip is 39 characters. 39 is 80/2 minus 1 byte - // needed for NULL character. - size_t tipLength = wxMin(ttip.Len(), 39); + // We need to convert tooltip from multi byte to Unicode on the fly. + static wchar_t buf[513]; - // Convert to WideChar without adding the NULL character. The NULL - // character is added afterwards (Could have used ttip.Left(tipLength) - // and a cchMultiByte parameter of tipLength+1, but this is more - //efficient. - ::MultiByteToWideChar(CP_ACP, 0, ttip, tipLength, - (wchar_t *)ttText->szText, - sizeof(ttText->szText) / sizeof(wchar_t)); + // Truncate tooltip length if needed as otherwise we might not have + // enough space for it in the buffer and MultiByteToWideChar() would + // return an error + size_t tipLength = wxMin(ttip.length(), WXSIZEOF(buf) - 1); - // Add the NULL character. - ttText->szText[tipLength*2+0] = '\0'; - ttText->szText[tipLength*2+1] = '\0'; + // Convert to WideChar without adding the NULL character. The NULL + // character is added afterwards (this is more efficient). + int len = ::MultiByteToWideChar + ( + CP_ACP, + 0, // no flags + ttip.t_str(), + tipLength, + buf, + WXSIZEOF(buf) - 1 + ); + + if ( !len ) + { + wxLogLastError(wxT("MultiByteToWideChar()")); + } -#endif // Unicode/!Unicode + buf[len] = L'\0'; + ttText->lpszText = (LPSTR) buf; + } + else // TTN_NEEDTEXTA +#endif // !wxUSE_UNICODE + { + // we get here if we got TTN_NEEDTEXTA (only happens in ANSI build) or + // if we got TTN_NEEDTEXTW in Unicode build: in this case we just have + // to copy the string we have into the buffer + static wxChar buf[513]; + wxStrlcpy(buf, ttip.c_str(), WXSIZEOF(buf)); + ttText->lpszText = buf; } - return TRUE; + return true; } #endif // wxUSE_TOOLTIPS @@ -3202,26 +3927,26 @@ bool wxWindowMSW::MSWOnNotify(int WXUNUSED(idCtrl), if ( HandleTooltipNotify(hdr->code, lParam, m_tooltip->GetTip())) { // processed - return TRUE; + return true; } } +#else + wxUnusedVar(lParam); #endif // wxUSE_TOOLTIPS - return FALSE; + return false; } -#endif // __WIN95__ - // --------------------------------------------------------------------------- // end session messages // --------------------------------------------------------------------------- bool wxWindowMSW::HandleQueryEndSession(long logOff, bool *mayEnd) { -#ifndef __WXWINCE__ - wxCloseEvent event(wxEVT_QUERY_END_SESSION, -1); +#ifdef ENDSESSION_LOGOFF + wxCloseEvent event(wxEVT_QUERY_END_SESSION, wxID_ANY); event.SetEventObject(wxTheApp); - event.SetCanVeto(TRUE); + event.SetCanVeto(true); event.SetLoggingOff(logOff == (long)ENDSESSION_LOGOFF); bool rc = wxTheApp->ProcessEvent(event); @@ -3235,29 +3960,33 @@ bool wxWindowMSW::HandleQueryEndSession(long logOff, bool *mayEnd) return rc; #else - return FALSE; + wxUnusedVar(logOff); + wxUnusedVar(mayEnd); + return false; #endif } bool wxWindowMSW::HandleEndSession(bool endSession, long logOff) { -#ifndef __WXWINCE__ +#ifdef ENDSESSION_LOGOFF // do nothing if the session isn't ending if ( !endSession ) - return FALSE; + return false; // only send once if ( (this != wxTheApp->GetTopWindow()) ) - return FALSE; + return false; - wxCloseEvent event(wxEVT_END_SESSION, -1); + wxCloseEvent event(wxEVT_END_SESSION, wxID_ANY); event.SetEventObject(wxTheApp); - event.SetCanVeto(FALSE); - event.SetLoggingOff( (logOff == (long)ENDSESSION_LOGOFF) ); + event.SetCanVeto(false); + event.SetLoggingOff((logOff & ENDSESSION_LOGOFF) != 0); return wxTheApp->ProcessEvent(event); #else - return FALSE; + wxUnusedVar(endSession); + wxUnusedVar(logOff); + return false; #endif } @@ -3265,7 +3994,8 @@ bool wxWindowMSW::HandleEndSession(bool endSession, long logOff) // window creation/destruction // --------------------------------------------------------------------------- -bool wxWindowMSW::HandleCreate(WXLPCREATESTRUCT cs, bool *mayCreate) +bool wxWindowMSW::HandleCreate(WXLPCREATESTRUCT WXUNUSED_IN_WINCE(cs), + bool *mayCreate) { // VZ: why is this commented out for WinCE? If it doesn't support // WS_EX_CONTROLPARENT at all it should be somehow handled globally, @@ -3275,32 +4005,25 @@ bool wxWindowMSW::HandleCreate(WXLPCREATESTRUCT cs, bool *mayCreate) EnsureParentHasControlParentStyle(GetParent()); #endif // !__WXWINCE__ - // TODO: should generate this event from WM_NCCREATE - wxWindowCreateEvent event((wxWindow *)this); - (void)GetEventHandler()->ProcessEvent(event); - - *mayCreate = TRUE; + *mayCreate = true; - return TRUE; + return true; } bool wxWindowMSW::HandleDestroy() { - SendDestroyEvent(); - // delete our drop target if we've got one #if wxUSE_DRAG_AND_DROP if ( m_dropTarget != NULL ) { m_dropTarget->Revoke(m_hWnd); - delete m_dropTarget; - m_dropTarget = NULL; + wxDELETE(m_dropTarget); } #endif // wxUSE_DRAG_AND_DROP // WM_DESTROY handled - return TRUE; + return true; } // --------------------------------------------------------------------------- @@ -3316,15 +4039,22 @@ bool wxWindowMSW::HandleActivate(int state, m_windowId); event.SetEventObject(this); - return GetEventHandler()->ProcessEvent(event); + return HandleWindowEvent(event); } bool wxWindowMSW::HandleSetFocus(WXHWND hwnd) { + // Strangly enough, some controls get set focus events when they are being + // deleted, even if they already had focus before. + if ( m_isBeingDeleted ) + { + return false; + } + // notify the parent keeping track of focus for the kbd navigation // purposes that we got it wxChildFocusEvent eventFocus((wxWindow *)this); - (void)GetEventHandler()->ProcessEvent(eventFocus); + (void)HandleWindowEvent(eventFocus); #if wxUSE_CARET // Deal with caret @@ -3334,22 +4064,13 @@ bool wxWindowMSW::HandleSetFocus(WXHWND 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); + return HandleWindowEvent(event); } bool wxWindowMSW::HandleKillFocus(WXHWND hwnd) @@ -3362,21 +4083,11 @@ bool wxWindowMSW::HandleKillFocus(WXHWND 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; + return false; } wxFocusEvent event(wxEVT_KILL_FOCUS, m_windowId); @@ -3385,7 +4096,21 @@ bool wxWindowMSW::HandleKillFocus(WXHWND hwnd) // wxFindWinFromHandle() may return NULL, it is ok event.SetWindow(wxFindWinFromHandle(hwnd)); - return GetEventHandler()->ProcessEvent(event); + return HandleWindowEvent(event); +} + +// --------------------------------------------------------------------------- +// labels +// --------------------------------------------------------------------------- + +void wxWindowMSW::SetLabel( const wxString& label) +{ + SetWindowText(GetHwnd(), label.c_str()); +} + +wxString wxWindowMSW::GetLabel() const +{ + return wxGetWindowText(GetHWND()); } // --------------------------------------------------------------------------- @@ -3395,23 +4120,24 @@ bool wxWindowMSW::HandleKillFocus(WXHWND hwnd) bool wxWindowMSW::HandleShow(bool show, int WXUNUSED(status)) { wxShowEvent event(GetId(), show); - event.m_eventObject = this; + event.SetEventObject(this); - return GetEventHandler()->ProcessEvent(event); + return HandleWindowEvent(event); } bool wxWindowMSW::HandleInitDialog(WXHWND WXUNUSED(hWndFocus)) { wxInitDialogEvent event(GetId()); - event.m_eventObject = this; + event.SetEventObject(this); - return GetEventHandler()->ProcessEvent(event); + return HandleWindowEvent(event); } bool wxWindowMSW::HandleDropFiles(WXWPARAM wParam) { #if defined (__WXMICROWIN__) || defined(__WXWINCE__) - return FALSE; + wxUnusedVar(wParam); + return false; #else // __WXMICROWIN__ HDROP hFilesInfo = (HDROP) wParam; @@ -3434,17 +4160,18 @@ bool wxWindowMSW::HandleDropFiles(WXWPARAM wParam) ::DragQueryFile(hFilesInfo, wIndex, wxStringBuffer(files[wIndex], len), len); } - DragFinish (hFilesInfo); wxDropFilesEvent event(wxEVT_DROP_FILES, gwFilesDropped, files); - event.m_eventObject = this; + event.SetEventObject(this); POINT dropPoint; DragQueryPoint(hFilesInfo, (LPPOINT) &dropPoint); event.m_pos.x = dropPoint.x; event.m_pos.y = dropPoint.y; - return GetEventHandler()->ProcessEvent(event); + DragFinish(hFilesInfo); + + return HandleWindowEvent(event); #endif } @@ -3455,62 +4182,54 @@ bool wxWindowMSW::HandleSetCursor(WXHWND WXUNUSED(hWnd), { #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 - // 0. allow the user to override default behaviour by using EVT_SET_CURSOR - // 1. if we have the cursor set it unless wxIsBusy() - // 2. if we're a top level window, set some cursor anyhow - // 3. if wxIsBusy(), set the busy cursor, otherwise the global one - - if ( nHitTest != HTCLIENT ) - { - return FALSE; - } + // 0. if we're busy, set the busy cursor (even for non client elements) + // 1. don't set custom cursor for non client area of enabled windows + // 2. ask user EVT_SET_CURSOR handler for the cursor + // 3. if still no cursor but we're in a TLW, set the global cursor HCURSOR hcursor = 0; - - // first ask the user code - it may wish to set the cursor in some very - // specific way (for example, depending on the current position) - POINT pt; - if ( !::GetCursorPos(&pt) ) + if ( wxIsBusy() ) { - wxLogLastError(wxT("GetCursorPos")); + hcursor = wxGetCurrentBusyCursor(); } - - int x = pt.x, - y = pt.y; - ScreenToClient(&x, &y); - wxSetCursorEvent event(x, y); - - bool processedEvtSetCursor = GetEventHandler()->ProcessEvent(event); - if ( processedEvtSetCursor && event.HasCursor() ) + else // not busy { - hcursor = GetHcursorOf(event.GetCursor()); - } + if ( nHitTest != HTCLIENT ) + return false; - if ( !hcursor ) - { - bool isBusy = wxIsBusy(); + // 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; + wxGetCursorPosMSW(&pt); + + int x = pt.x, + y = pt.y; + ScreenToClient(&x, &y); + wxSetCursorEvent event(x, y); + event.SetId(GetId()); + event.SetEventObject(this); - // the test for processedEvtSetCursor is here to prevent using m_cursor - // if the user code caught EVT_SET_CURSOR() and returned nothing from - // it - this is a way to say that our cursor shouldn't be used for this - // point - if ( !processedEvtSetCursor && m_cursor.Ok() ) + bool processedEvtSetCursor = HandleWindowEvent(event); + if ( processedEvtSetCursor && event.HasCursor() ) { - hcursor = GetHcursorOf(m_cursor); + hcursor = GetHcursorOf(event.GetCursor()); } - if ( !GetParent() ) + if ( !hcursor ) { - if ( isBusy ) + // the test for processedEvtSetCursor is here to prevent using + // m_cursor if the user code caught EVT_SET_CURSOR() and returned + // nothing from it - this is a way to say that our cursor shouldn't + // be used for this point + if ( !processedEvtSetCursor && m_cursor.IsOk() ) { - hcursor = wxGetCurrentBusyCursor(); + hcursor = GetHcursorOf(m_cursor); } - else if ( !hcursor ) + + if ( !hcursor && !GetParent() ) { const wxCursor *cursor = wxGetGlobalCursor(); - if ( cursor && cursor->Ok() ) + if ( cursor && cursor->IsOk() ) { hcursor = GetHcursorOf(*cursor); } @@ -3518,19 +4237,110 @@ bool wxWindowMSW::HandleSetCursor(WXHWND WXUNUSED(hWnd), } } + if ( hcursor ) { -// wxLogDebug("HandleSetCursor: Setting cursor %ld", (long) hcursor); - ::SetCursor(hcursor); // cursor set, stop here - return TRUE; + return true; } #endif // __WXMICROWIN__ // pass up the window chain - return FALSE; + return false; +} + +bool wxWindowMSW::HandlePower(WXWPARAM WXUNUSED_IN_WINCE(wParam), + WXLPARAM WXUNUSED(lParam), + bool *WXUNUSED_IN_WINCE(vetoed)) +{ +#ifdef __WXWINCE__ + // FIXME + return false; +#else + wxEventType evtType; + switch ( wParam ) + { + case PBT_APMQUERYSUSPEND: + evtType = wxEVT_POWER_SUSPENDING; + break; + + case PBT_APMQUERYSUSPENDFAILED: + evtType = wxEVT_POWER_SUSPEND_CANCEL; + break; + + case PBT_APMSUSPEND: + evtType = wxEVT_POWER_SUSPENDED; + break; + + case PBT_APMRESUMESUSPEND: + evtType = wxEVT_POWER_RESUME; + break; + + default: + wxLogDebug(wxT("Unknown WM_POWERBROADCAST(%d) event"), wParam); + // fall through + + // these messages are currently not mapped to wx events + case PBT_APMQUERYSTANDBY: + case PBT_APMQUERYSTANDBYFAILED: + case PBT_APMSTANDBY: + case PBT_APMRESUMESTANDBY: + case PBT_APMBATTERYLOW: + case PBT_APMPOWERSTATUSCHANGE: + case PBT_APMOEMEVENT: + case PBT_APMRESUMECRITICAL: +#ifdef PBT_APMRESUMEAUTOMATIC + case PBT_APMRESUMEAUTOMATIC: +#endif + evtType = wxEVT_NULL; + break; + } + + // don't handle unknown messages + if ( evtType == wxEVT_NULL ) + return false; + + // TODO: notify about PBTF_APMRESUMEFROMFAILURE in case of resume events? + + wxPowerEvent event(evtType); + if ( !HandleWindowEvent(event) ) + return false; + + *vetoed = event.IsVetoed(); + + return true; +#endif +} + +bool wxWindowMSW::IsDoubleBuffered() const +{ + for ( const wxWindowMSW *win = this; win; win = win->GetParent() ) + { + if ( wxHasWindowExStyle(win, WS_EX_COMPOSITED) ) + return true; + + if ( win->IsTopLevel() ) + break; + } + + return false; +} + +void wxWindowMSW::SetDoubleBuffered(bool on) +{ + // Get the current extended style bits + long exstyle = wxGetWindowExStyle(this); + + // Twiddle the bit as needed + if ( on ) + exstyle |= WS_EX_COMPOSITED; + else + exstyle &= ~WS_EX_COMPOSITED; + + // put it back + wxSetWindowExStyle(this, exstyle); } // --------------------------------------------------------------------------- @@ -3557,7 +4367,12 @@ wxWindowMSW::MSWOnDrawItem(int WXUNUSED_UNLESS_ODRAWN(id), { wxMenuItem *pMenuItem = (wxMenuItem *)(pDrawStruct->itemData); - wxCHECK( pMenuItem->IsKindOf(CLASSINFO(wxMenuItem)), FALSE ); + // see comment before the same test in MSWOnMeasureItem() below + if ( !pMenuItem ) + return false; + + wxCHECK_MSG( wxDynamicCast(pMenuItem, wxMenuItem), + false, wxT("MSWOnDrawItem: bad wxMenuItem pointer") ); // prepare to call OnDrawItem(): notice using of wxDCTemp to prevent // the DC from being released @@ -3585,7 +4400,13 @@ wxWindowMSW::MSWOnDrawItem(int WXUNUSED_UNLESS_ODRAWN(id), #else // !wxUSE_OWNER_DRAWN // we may still have owner-drawn buttons internally because we have to make // them owner-drawn to support colour change - wxControl *item = wxDynamicCast(FindItem(id), wxButton); + wxControl *item = +# if wxUSE_BUTTON + wxDynamicCast(FindItem(id), wxButton) +# else + NULL +# endif + ; #endif // USE_OWNER_DRAWN if ( item ) @@ -3595,13 +4416,11 @@ wxWindowMSW::MSWOnDrawItem(int WXUNUSED_UNLESS_ODRAWN(id), #endif // wxUSE_CONTROLS - return FALSE; + return false; } bool -wxWindowMSW::MSWOnMeasureItem(int WXUNUSED_UNLESS_ODRAWN(id), - WXMEASUREITEMSTRUCT * - WXUNUSED_UNLESS_ODRAWN(itemStruct)) +wxWindowMSW::MSWOnMeasureItem(int id, WXMEASUREITEMSTRUCT *itemStruct) { #if wxUSE_OWNER_DRAWN && wxUSE_MENUS_NATIVE // is it a menu item? @@ -3610,10 +4429,24 @@ wxWindowMSW::MSWOnMeasureItem(int WXUNUSED_UNLESS_ODRAWN(id), { wxMenuItem *pMenuItem = (wxMenuItem *)(pMeasureStruct->itemData); - wxCHECK( pMenuItem->IsKindOf(CLASSINFO(wxMenuItem)), FALSE ); + // according to Carsten Fuchs the pointer may be NULL under XP if an + // MDI child frame is initially maximized, see this for more info: + // http://article.gmane.org/gmane.comp.lib.wxwidgets.general/27745 + // + // so silently ignore it instead of asserting + if ( !pMenuItem ) + return false; + + wxCHECK_MSG( wxDynamicCast(pMenuItem, wxMenuItem), + false, wxT("MSWOnMeasureItem: bad wxMenuItem pointer") ); + + size_t w, h; + bool rc = pMenuItem->OnMeasureItem(&w, &h); - return pMenuItem->OnMeasureItem(&pMeasureStruct->itemWidth, - &pMeasureStruct->itemHeight); + pMeasureStruct->itemWidth = w; + pMeasureStruct->itemHeight = h; + + return rc; } wxControl *item = wxDynamicCast(FindItem(id), wxControl); @@ -3621,9 +4454,12 @@ wxWindowMSW::MSWOnMeasureItem(int WXUNUSED_UNLESS_ODRAWN(id), { return item->MSWOnMeasure(itemStruct); } -#endif // wxUSE_OWNER_DRAWN +#else + wxUnusedVar(id); + wxUnusedVar(itemStruct); +#endif // wxUSE_OWNER_DRAWN && wxUSE_MENUS_NATIVE - return FALSE; + return false; } // --------------------------------------------------------------------------- @@ -3635,11 +4471,11 @@ bool wxWindowMSW::HandleSysColorChange() wxSysColourChangedEvent event; event.SetEventObject(this); - (void)GetEventHandler()->ProcessEvent(event); + (void)HandleWindowEvent(event); // always let the system carry on the default processing to allow the // native controls to react to the colours update - return FALSE; + return false; } bool wxWindowMSW::HandleDisplayChange() @@ -3647,56 +4483,29 @@ bool wxWindowMSW::HandleDisplayChange() wxDisplayChangedEvent event; event.SetEventObject(this); - return GetEventHandler()->ProcessEvent(event); + return HandleWindowEvent(event); } -bool wxWindowMSW::HandleCtlColor(WXHBRUSH *brush, - WXHDC pDC, - WXHWND pWnd, - WXUINT nCtlColor, - WXUINT message, - WXWPARAM wParam, - WXLPARAM lParam) -{ #ifndef __WXMICROWIN__ - WXHBRUSH hBrush = 0; -#ifdef __WXWINCE__ - if (FALSE) +bool wxWindowMSW::HandleCtlColor(WXHBRUSH *brush, WXHDC hDC, WXHWND hWnd) +{ +#if !wxUSE_CONTROLS || defined(__WXUNIVERSAL__) + wxUnusedVar(hDC); + wxUnusedVar(hWnd); #else - if ( nCtlColor == CTLCOLOR_DLG ) -#endif - { - hBrush = OnCtlColor(pDC, pWnd, nCtlColor, message, wParam, lParam); - } -#if wxUSE_CONTROLS + wxControl *item = wxDynamicCast(FindItemByHWND(hWnd, true), wxControl); + + if ( item ) + *brush = item->MSWControlColor(hDC, hWnd); else - { - wxControl *item = (wxControl *)FindItemByHWND(pWnd, TRUE); - if ( item ) - hBrush = item->OnCtlColor(pDC, pWnd, nCtlColor, message, wParam, lParam); - } #endif // wxUSE_CONTROLS + *brush = NULL; - if ( hBrush ) - *brush = hBrush; - - return hBrush != 0; -#else // __WXMICROWIN__ - return FALSE; -#endif + return *brush != NULL; } -// Define for each class of dialog and control -WXHBRUSH wxWindowMSW::OnCtlColor(WXHDC WXUNUSED(hDC), - WXHWND WXUNUSED(hWnd), - WXUINT WXUNUSED(nCtlColor), - WXUINT WXUNUSED(message), - WXWPARAM WXUNUSED(wParam), - WXLPARAM WXUNUSED(lParam)) -{ - return (WXHBRUSH)0; -} +#endif // __WXMICROWIN__ bool wxWindowMSW::HandlePaletteChanged(WXHWND hWndPalChange) { @@ -3728,7 +4537,7 @@ bool wxWindowMSW::HandlePaletteChanged(WXHWND hWndPalChange) // now check for the need to redraw if (result > 0) - InvalidateRect((HWND) hWndPalChange, NULL, TRUE); + ::InvalidateRect((HWND) hWndPalChange, NULL, TRUE); } } @@ -3738,15 +4547,43 @@ bool wxWindowMSW::HandlePaletteChanged(WXHWND hWndPalChange) event.SetEventObject(this); event.SetChangedWindow(wxFindWinFromHandle(hWndPalChange)); - return GetEventHandler()->ProcessEvent(event); + return HandleWindowEvent(event); } bool wxWindowMSW::HandleCaptureChanged(WXHWND hWndGainedCapture) { - wxMouseCaptureChangedEvent event(GetId(), wxFindWinFromHandle(hWndGainedCapture)); + // notify windows on the capture stack about lost capture + // (see http://sourceforge.net/tracker/index.php?func=detail&aid=1153662&group_id=9863&atid=109863): + wxWindowBase::NotifyCaptureLost(); + + wxWindow *win = wxFindWinFromHandle(hWndGainedCapture); + wxMouseCaptureChangedEvent event(GetId(), win); event.SetEventObject(this); + return HandleWindowEvent(event); +} + +bool wxWindowMSW::HandleSettingChange(WXWPARAM wParam, WXLPARAM lParam) +{ + // despite MSDN saying "(This message cannot be sent directly to a window.)" + // we need to send this to child windows (it is only sent to top-level + // windows) so {list,tree}ctrls can adjust their font size if necessary + // this is exactly how explorer does it to enable the font size changes + + wxWindowList::compatibility_iterator node = GetChildren().GetFirst(); + while ( node ) + { + // top-level windows already get this message from the system + wxWindow *win = node->GetData(); + if ( !win->IsTopLevel() ) + { + ::SendMessage(GetHwndOf(win), WM_SETTINGCHANGE, wParam, lParam); + } - return GetEventHandler()->ProcessEvent(event); + node = node->GetNext(); + } + + // let the system handle it + return false; } bool wxWindowMSW::HandleQueryNewPalette() @@ -3758,7 +4595,7 @@ bool wxWindowMSW::HandleQueryNewPalette() 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()); + HDC hdc = ::GetDC((HWND) GetHWND()); win->m_palette.SetHPALETTE( (WXHPALETTE) ::SelectPalette(hdc, (HPALETTE) win->m_palette.GetHPALETTE(), FALSE) ); @@ -3777,7 +4614,7 @@ bool wxWindowMSW::HandleQueryNewPalette() wxQueryNewPaletteEvent event(GetId()); event.SetEventObject(this); - return GetEventHandler()->ProcessEvent(event) && event.GetPaletteRealized(); + return HandleWindowEvent(event) && event.GetPaletteRealized(); } // Responds to colour changes: passes event on to children. @@ -3789,7 +4626,7 @@ void wxWindowMSW::OnSysColourChanged(wxSysColourChangedEvent& WXUNUSED(event)) if ( IsTopLevel() ) { // FIXME-MT - gs_hasStdCmap = FALSE; + gs_hasStdCmap = false; } wxWindowList::compatibility_iterator node = GetChildren().GetFirst(); while ( node ) @@ -3807,20 +4644,6 @@ void wxWindowMSW::OnSysColourChanged(wxSysColourChangedEvent& WXUNUSED(event)) 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() @@ -3830,7 +4653,7 @@ extern wxCOLORMAP *wxGetStdColourMap() if ( !gs_hasStdCmap ) { - static bool s_coloursInit = FALSE; + static bool s_coloursInit = false; if ( !s_coloursInit ) { @@ -3841,12 +4664,13 @@ extern wxCOLORMAP *wxGetStdColourMap() // 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() ) + wxLogNull logNo; // suppress error if we couldn't load the bitmap + wxBitmap stdColourBitmap(wxT("wxBITMAP_STD_COLOURS")); + if ( stdColourBitmap.IsOk() ) { // 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!") ); + wxT("forgot to update wxBITMAP_STD_COLOURS!") ); wxMemoryDC memDC; memDC.SelectObject(stdColourBitmap); @@ -3868,10 +4692,10 @@ extern wxCOLORMAP *wxGetStdColourMap() //s_stdColours[5] = RGB(255,000,255); // magenta } - s_coloursInit = TRUE; + s_coloursInit = true; } - gs_hasStdCmap = TRUE; + gs_hasStdCmap = true; // create the colour map #define INIT_CMAP_ENTRY(col) \ @@ -3889,34 +4713,120 @@ extern wxCOLORMAP *wxGetStdColourMap() return s_cmap; } +#if wxUSE_UXTHEME && !defined(TMT_FILLCOLOR) + #define TMT_FILLCOLOR 3802 + #define TMT_TEXTCOLOR 3803 + #define TMT_BORDERCOLOR 3801 +#endif + +wxColour wxWindowMSW::MSWGetThemeColour(const wchar_t *themeName, + int themePart, + int themeState, + MSWThemeColour themeColour, + wxSystemColour fallback) const +{ +#if wxUSE_UXTHEME + const wxUxThemeEngine* theme = wxUxThemeEngine::GetIfActive(); + if ( theme ) + { + int themeProperty = 0; + + // TODO: Convert this into a table? Sure would be faster. + switch ( themeColour ) + { + case ThemeColourBackground: + themeProperty = TMT_FILLCOLOR; + break; + case ThemeColourText: + themeProperty = TMT_TEXTCOLOR; + break; + case ThemeColourBorder: + themeProperty = TMT_BORDERCOLOR; + break; + default: + wxFAIL_MSG(wxT("unsupported theme colour")); + }; + + wxUxThemeHandle hTheme((const wxWindow *)this, themeName); + COLORREF col; + HRESULT hr = theme->GetThemeColor + ( + hTheme, + themePart, + themeState, + themeProperty, + &col + ); + + if ( SUCCEEDED(hr) ) + return wxRGBToColour(col); + + wxLogApiError( + wxString::Format( + "GetThemeColor(%s, %i, %i, %i)", + themeName, themePart, themeState, themeProperty), + hr); + } +#else + wxUnusedVar(themeName); + wxUnusedVar(themePart); + wxUnusedVar(themeState); + wxUnusedVar(themeColour); +#endif + return wxSystemSettings::GetColour(fallback); +} + // --------------------------------------------------------------------------- // painting // --------------------------------------------------------------------------- +// this variable is used to check that a paint event handler which processed +// the event did create a wxPaintDC inside its code and called BeginPaint() to +// validate the invalidated window area as otherwise we'd keep getting an +// endless stream of WM_PAINT messages for this window resulting in a lot of +// difficult to debug problems (e.g. impossibility to repaint other windows, +// lack of timer and idle events and so on) +extern bool wxDidCreatePaintDC; +bool wxDidCreatePaintDC = false; + bool wxWindowMSW::HandlePaint() { -// 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")); + } if ( ::GetUpdateRgn(GetHwnd(), hRegion, FALSE) == ERROR ) + { wxLogLastError(wxT("GetUpdateRgn")); + } m_updateRegion = wxRegion((WXHRGN) hRegion); + wxDidCreatePaintDC = false; + wxPaintEvent event(m_windowId); event.SetEventObject(this); - bool processed = GetEventHandler()->ProcessEvent(event); + bool processed = HandleWindowEvent(event); + + if ( processed && !wxDidCreatePaintDC ) + { + // do call MSWDefWindowProc() to validate the update region to avoid + // the problems mentioned above + processed = false; + } // 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); + HandleWindowEvent(eventNc); + + // don't keep an HRGN we don't need any longer (GetUpdateRegion() can only + // be called from inside the event handlers called above) + m_updateRegion.Clear(); return processed; } @@ -3927,7 +4837,7 @@ void wxWindowMSW::OnPaint(wxPaintEvent& event) #ifdef __WXUNIVERSAL__ event.Skip(); #else - HDC hDC = (HDC) wxPaintDC::FindDCInCache((wxWindow*) event.GetEventObject()); + HDC hDC = (HDC) wxPaintDCImpl::FindDCInCache((wxWindow*) event.GetEventObject()); if (hDC != 0) { MSWDefWindowProc(WM_PAINT, (WPARAM) hDC, 0); @@ -3937,75 +4847,208 @@ void wxWindowMSW::OnPaint(wxPaintEvent& event) bool wxWindowMSW::HandleEraseBkgnd(WXHDC hdc) { - // Prevents flicker when dragging - if ( ::IsIconic(GetHwnd()) ) - return TRUE; - -#if 0 - if (GetParent() && GetParent()->GetExtraStyle() & wxWS_EX_THEMED_BACKGROUND) + if ( IsBeingDeleted() ) { - return FALSE; + // We can get WM_ERASEBKGND after starting the destruction of our top + // level parent. Handling it in this case is unnecessary and can be + // actually harmful as e.g. wxStaticBox::GetClientSize() doesn't work + // without a valid TLW parent (because it uses dialog units internally + // which use the dialog font), so just don't do anything then. + return false; } - if (GetExtraStyle() & wxWS_EX_THEMED_BACKGROUND) + switch ( GetBackgroundStyle() ) { - if (wxUxThemeEngine::Get()) - { - WXHTHEME hTheme = wxUxThemeEngine::Get()->m_pfnOpenThemeData(GetHWND(), L"TAB"); - if (hTheme) + case wxBG_STYLE_ERASE: + case wxBG_STYLE_COLOUR: + // we need to generate an erase background event + { + wxDCTemp dc(hdc, GetClientSize()); + wxDCTempImpl *impl = (wxDCTempImpl*) dc.GetImpl(); + + impl->SetHDC(hdc); + impl->SetWindow((wxWindow *)this); + + wxEraseEvent event(m_windowId, &dc); + event.SetEventObject(this); + bool rc = HandleWindowEvent(event); + + // must be called manually as ~wxDC doesn't do anything for + // wxDCTemp + impl->SelectOldObjects(hdc); + + if ( rc ) + { + // background erased by the user-defined handler + return true; + } + } + // fall through + + case wxBG_STYLE_SYSTEM: + if ( !DoEraseBackground(hdc) ) { - 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; + // let the default processing to take place if we didn't erase + // the background ourselves + return false; } - } + break; + + case wxBG_STYLE_PAINT: + case wxBG_STYLE_TRANSPARENT: + // no need to do anything here at all, background will be entirely + // redrawn in WM_PAINT handler + break; + + default: + wxFAIL_MSG( "unknown background style" ); } -#endif - wxDCTemp dc(hdc); + return true; +} - dc.SetHDC(hdc); - dc.SetWindow((wxWindow *)this); - dc.BeginDrawing(); +#ifdef wxHAS_MSW_BACKGROUND_ERASE_HOOK - wxEraseEvent event(m_windowId, &dc); - event.SetEventObject(this); - bool rc = GetEventHandler()->ProcessEvent(event); +bool wxWindowMSW::MSWHasEraseBgHook() const +{ + return gs_eraseBgHooks.find(const_cast(this)) + != gs_eraseBgHooks.end(); +} - dc.EndDrawing(); +void wxWindowMSW::MSWSetEraseBgHook(wxWindow *child) +{ + if ( child ) + { + if ( !gs_eraseBgHooks.insert( + EraseBgHooks::value_type(this, child)).second ) + { + wxFAIL_MSG( wxT("Setting erase background hook twice?") ); + } + } + else // reset the hook + { + if ( gs_eraseBgHooks.erase(this) != 1 ) + { + wxFAIL_MSG( wxT("Resetting erase background which was not set?") ); + } + } +} - // must be called manually as ~wxDC doesn't do anything for wxDCTemp - dc.SelectOldObjects(hdc); +#endif // wxHAS_MSW_BACKGROUND_ERASE_HOOK - return rc; +bool wxWindowMSW::DoEraseBackground(WXHDC hDC) +{ + HBRUSH hbr = (HBRUSH)MSWGetBgBrush(hDC); + if ( !hbr ) + return false; + + // erase just the client area of the window, this is important for the + // frames to avoid drawing over the toolbar part of the window (you might + // think using WS_CLIPCHILDREN would prevent this from happening, but it + // clearly doesn't) + RECT rc; + wxCopyRectToRECT(GetClientRect(), rc); + ::FillRect((HDC)hDC, &rc, hbr); + + return true; } -void wxWindowMSW::OnEraseBackground(wxEraseEvent& event) +WXHBRUSH +wxWindowMSW::MSWGetBgBrushForChild(WXHDC hDC, wxWindowMSW *child) { - RECT rect; - ::GetClientRect(GetHwnd(), &rect); + // Test for the custom background brush first. + WXHBRUSH hbrush = MSWGetCustomBgBrush(); + if ( hbrush ) + { + // We assume that this is either a stipple or hatched brush and not a + // solid one as otherwise it would have been enough to set the + // background colour and such brushes need to be positioned correctly + // in order to align when different windows are painted, so do it here. + RECT rc; + ::GetWindowRect(GetHwndOf(child), &rc); - COLORREF ref = PALETTERGB(m_backgroundColour.Red(), - m_backgroundColour.Green(), - m_backgroundColour.Blue()); - HBRUSH hBrush = ::CreateSolidBrush(ref); - if ( !hBrush ) - wxLogLastError(wxT("CreateSolidBrush")); + ::MapWindowPoints(NULL, GetHwnd(), (POINT *)&rc, 1); - HDC hdc = (HDC)event.GetDC()->GetHDC(); + if ( !::SetBrushOrgEx((HDC)hDC, -rc.left, -rc.top, NULL) ) + { + wxLogLastError(wxT("SetBrushOrgEx(bg brush)")); + } -#ifndef __WXWINCE__ - int mode = ::SetMapMode(hdc, MM_TEXT); -#endif + return hbrush; + } - ::FillRect(hdc, &rect, hBrush); - ::DeleteObject(hBrush); + // Otherwise see if we have a custom background colour. + if ( m_hasBgCol ) + { + wxBrush * + brush = wxTheBrushList->FindOrCreateBrush(GetBackgroundColour()); -#ifndef __WXWINCE__ - ::SetMapMode(hdc, mode); + return (WXHBRUSH)GetHbrushOf(*brush); + } + + return 0; +} + +WXHBRUSH wxWindowMSW::MSWGetBgBrush(WXHDC hDC) +{ + // Use the special wxWindowBeingErased variable if it is set as the child + // being erased. + wxWindowMSW * const child = +#if wxUSE_UXTHEME + wxWindowBeingErased ? wxWindowBeingErased : #endif + this; + + for ( wxWindowMSW *win = this; win; win = win->GetParent() ) + { + WXHBRUSH hBrush = win->MSWGetBgBrushForChild(hDC, child); + if ( hBrush ) + return hBrush; + + // don't use the parent background if we're not transparent + if ( !win->HasTransparentBackground() ) + break; + + // background is not inherited beyond top level windows + if ( win->IsTopLevel() ) + break; + } + + return 0; +} + +bool wxWindowMSW::HandlePrintClient(WXHDC hDC) +{ + // we receive this message when DrawThemeParentBackground() is + // called from def window proc of several controls under XP and we + // must draw properly themed background here + // + // note that naively I'd expect filling the client rect with the + // brush returned by MSWGetBgBrush() work -- but for some reason it + // doesn't and we have to call parents MSWPrintChild() which is + // supposed to call DrawThemeBackground() with appropriate params + // + // also note that in this case lParam == PRF_CLIENT but we're + // clearly expected to paint the background and nothing else! + + if ( IsTopLevel() || InheritsBackgroundColour() ) + return false; + + // sometimes we don't want the parent to handle it at all, instead + // return whatever value this window wants + if ( !MSWShouldPropagatePrintChild() ) + return MSWPrintChild(hDC, (wxWindow *)this); + + for ( wxWindow *win = GetParent(); win; win = win->GetParent() ) + { + if ( win->MSWPrintChild(hDC, (wxWindow *)this) ) + return true; + + if ( win->IsTopLevel() || win->InheritsBackgroundColour() ) + break; + } + + return false; } // --------------------------------------------------------------------------- @@ -4017,7 +5060,7 @@ bool wxWindowMSW::HandleMinimize() wxIconizeEvent event(m_windowId); event.SetEventObject(this); - return GetEventHandler()->ProcessEvent(event); + return HandleWindowEvent(event); } bool wxWindowMSW::HandleMaximize() @@ -4025,15 +5068,16 @@ bool wxWindowMSW::HandleMaximize() wxMaximizeEvent event(m_windowId); event.SetEventObject(this); - return GetEventHandler()->ProcessEvent(event); + return HandleWindowEvent(event); } bool wxWindowMSW::HandleMove(int x, int y) { - wxMoveEvent event(wxPoint(x, y), m_windowId); + wxPoint point(x,y); + wxMoveEvent event(point, m_windowId); event.SetEventObject(this); - return GetEventHandler()->ProcessEvent(event); + return HandleWindowEvent(event); } bool wxWindowMSW::HandleMoving(wxRect& rect) @@ -4041,22 +5085,121 @@ bool wxWindowMSW::HandleMoving(wxRect& rect) wxMoveEvent event(rect, m_windowId); event.SetEventObject(this); - bool rc = GetEventHandler()->ProcessEvent(event); + bool rc = HandleWindowEvent(event); if (rc) rect = event.GetRect(); return rc; } -bool wxWindowMSW::HandleSize(int WXUNUSED(w), int WXUNUSED(h), - WXUINT WXUNUSED(flag)) +bool wxWindowMSW::HandleEnterSizeMove() +{ + wxMoveEvent event(wxPoint(0,0), m_windowId); + event.SetEventType(wxEVT_MOVE_START); + event.SetEventObject(this); + + return HandleWindowEvent(event); +} + +bool wxWindowMSW::HandleExitSizeMove() { - // 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); + wxMoveEvent event(wxPoint(0,0), m_windowId); + event.SetEventType(wxEVT_MOVE_END); event.SetEventObject(this); - return GetEventHandler()->ProcessEvent(event); + return HandleWindowEvent(event); +} + +bool wxWindowMSW::HandleSize(int WXUNUSED(w), int WXUNUSED(h), WXUINT wParam) +{ +#if wxUSE_DEFERRED_SIZING + // when we resize this window, its children are probably going to be + // repositioned as well, prepare to use DeferWindowPos() for them + int numChildren = 0; + for ( HWND child = ::GetWindow(GetHwndOf(this), GW_CHILD); + child; + child = ::GetWindow(child, GW_HWNDNEXT) ) + { + numChildren ++; + } + + // Protect against valid m_hDWP being overwritten + bool useDefer = false; + + if ( numChildren > 1 ) + { + if (!m_hDWP) + { + m_hDWP = (WXHANDLE)::BeginDeferWindowPos(numChildren); + if ( !m_hDWP ) + { + wxLogLastError(wxT("BeginDeferWindowPos")); + } + if (m_hDWP) + useDefer = true; + } + } +#endif // wxUSE_DEFERRED_SIZING + + // update this window size + bool processed = false; + switch ( wParam ) + { + default: + wxFAIL_MSG( wxT("unexpected WM_SIZE parameter") ); + // fall through nevertheless + + case SIZE_MAXHIDE: + case SIZE_MAXSHOW: + // we're not interested in these messages at all + break; + + case SIZE_MINIMIZED: + processed = HandleMinimize(); + break; + + case SIZE_MAXIMIZED: + /* processed = */ HandleMaximize(); + // fall through to send a normal size event as well + + case SIZE_RESTORED: + // 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); + + processed = HandleWindowEvent(event); + } + +#if wxUSE_DEFERRED_SIZING + // and finally change the positions of all child windows at once + if ( useDefer && m_hDWP ) + { + // reset m_hDWP to NULL so that child windows don't try to use our + // m_hDWP after we call EndDeferWindowPos() on it (this shouldn't + // happen anyhow normally but who knows what weird flow of control we + // may have depending on what the users EVT_SIZE handler does...) + HDWP hDWP = (HDWP)m_hDWP; + m_hDWP = NULL; + + // do put all child controls in place at once + if ( !::EndDeferWindowPos(hDWP) ) + { + wxLogLastError(wxT("EndDeferWindowPos")); + } + + // Reset our children's pending pos/size values. + for ( wxWindowList::compatibility_iterator node = GetChildren().GetFirst(); + node; + node = node->GetNext() ) + { + wxWindowMSW * const child = node->GetData(); + child->MSWEndDeferWindowPos(); + } + } +#endif // wxUSE_DEFERRED_SIZING + + return processed; } bool wxWindowMSW::HandleSizing(wxRect& rect) @@ -4064,48 +5207,48 @@ bool wxWindowMSW::HandleSizing(wxRect& rect) wxSizeEvent event(rect, m_windowId); event.SetEventObject(this); - bool rc = GetEventHandler()->ProcessEvent(event); + bool rc = HandleWindowEvent(event); if (rc) rect = event.GetRect(); return rc; } -bool wxWindowMSW::HandleGetMinMaxInfo(void *mmInfo) +bool wxWindowMSW::HandleGetMinMaxInfo(void *WXUNUSED_IN_WINCE(mmInfo)) { #ifdef __WXWINCE__ - return FALSE; + return false; #else MINMAXINFO *info = (MINMAXINFO *)mmInfo; - bool rc = FALSE; + bool rc = false; int minWidth = GetMinWidth(), minHeight = GetMinHeight(), maxWidth = GetMaxWidth(), maxHeight = GetMaxHeight(); - if ( minWidth != -1 ) + if ( minWidth != wxDefaultCoord ) { info->ptMinTrackSize.x = minWidth; - rc = TRUE; + rc = true; } - if ( minHeight != -1 ) + if ( minHeight != wxDefaultCoord ) { info->ptMinTrackSize.y = minHeight; - rc = TRUE; + rc = true; } - if ( maxWidth != -1 ) + if ( maxWidth != wxDefaultCoord ) { info->ptMaxTrackSize.x = maxWidth; - rc = TRUE; + rc = true; } - if ( maxHeight != -1 ) + if ( maxHeight != wxDefaultCoord ) { info->ptMaxTrackSize.y = maxHeight; - rc = TRUE; + rc = true; } return rc; @@ -4116,8 +5259,11 @@ bool wxWindowMSW::HandleGetMinMaxInfo(void *mmInfo) // command messages // --------------------------------------------------------------------------- -bool wxWindowMSW::HandleCommand(WXWORD id, WXWORD cmd, WXHWND control) +bool wxWindowMSW::HandleCommand(WXWORD id_, WXWORD cmd, WXHWND control) { + // sign extend to int from short before comparing with the other int ids + int id = (signed short)id_; + #if wxUSE_MENUS_NATIVE if ( !cmd && wxCurrentPopupMenu ) { @@ -4140,8 +5286,7 @@ bool wxWindowMSW::HandleCommand(WXWORD id, WXWORD cmd, WXHWND control) // try the id if ( !win ) { - // must cast to a signed type before comparing with other ids! - win = FindItem((signed short)id); + win = FindItem(id); } if ( win ) @@ -4155,46 +5300,34 @@ bool wxWindowMSW::HandleCommand(WXWORD id, WXWORD cmd, WXHWND control) // coming from a control to wxEVT_COMMAND_MENU_SELECTED if ( !control ) { - // If no child window, it may be an accelerator, e.g. for a popup menu - // command - - wxCommandEvent event(wxEVT_COMMAND_MENU_SELECTED); + wxCommandEvent event(wxEVT_COMMAND_MENU_SELECTED, id); event.SetEventObject(this); - event.SetId(id); event.SetInt(id); - return GetEventHandler()->ProcessEvent(event); + return HandleWindowEvent(event); } -#if wxUSE_SPINCTRL && !defined(__WXUNIVERSAL__) else { +#if wxUSE_SPINCTRL && !defined(__WXUNIVERSAL__) // 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; - } + return true; #endif // wxUSE_SPINCTRL - return FALSE; -} - -bool wxWindowMSW::HandleSysCommand(WXWPARAM wParam, WXLPARAM WXUNUSED(lParam)) -{ -#ifndef __WXWINCE__ - // 4 bits are reserved - switch ( wParam & 0xFFFFFFF0 ) - { - case SC_MAXIMIZE: - return HandleMaximize(); - - case SC_MINIMIZE: - return HandleMinimize(); - } +#if wxUSE_CHOICE && defined(__SMARTPHONE__) + // the listbox ctrl which is logically part of wxChoice sends WM_COMMAND + // notifications to its parent which we want to reflect back to + // wxChoice + wxChoice *choice = wxChoice::GetChoiceForListBox(control); + if ( choice && choice->MSWCommand(cmd, id) ) + return true; #endif + } - return FALSE; + return false; } // --------------------------------------------------------------------------- @@ -4215,22 +5348,26 @@ void wxWindowMSW::InitMouseEvent(wxMouseEvent& event, 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; +#ifdef wxHAS_XBUTTON + event.m_aux1Down = (flags & MK_XBUTTON1) != 0; + event.m_aux2Down = (flags & MK_XBUTTON2) != 0; +#endif // wxHAS_XBUTTON + event.m_altDown = ::wxIsAltDown(); + +#ifndef __WXWINCE__ + event.SetTimestamp(::GetMessageTime()); +#endif - event.SetTimestamp(s_currentMsg.time); - event.m_eventObject = this; + event.SetEventObject(this); event.SetId(GetId()); #if wxUSE_MOUSEEVENT_HACK - m_lastMouseX = x; - m_lastMouseY = y; - m_lastMouseEvent = event.GetEventType(); + gs_lastMouseEvent.pos = ClientToScreen(wxPoint(x, y)); + gs_lastMouseEvent.type = event.GetEventType(); #endif // wxUSE_MOUSEEVENT_HACK } +#ifdef __WXWINCE__ // 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 @@ -4241,9 +5378,9 @@ void wxWindowMSW::InitMouseEvent(wxMouseEvent& event, // 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 +static wxWindowMSW *FindWindowForMouseEvent(wxWindowMSW *win, int *x, int *y) { - wxCHECK_MSG( x && y, win, _T("NULL pointer in FindWindowForMouseEvent") ); + wxCHECK_MSG( x && y, win, wxT("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 @@ -4281,7 +5418,7 @@ static wxWindowMSW *FindWindowForMouseEvent(wxWindowMSW *win, int *x, int *y) // ::IsWindowVisible(hwndUnderMouse) && ::IsWindowEnabled(hwndUnderMouse) ) { - wxWindow *winUnderMouse = wxFindWinFromHandle((WXHWND)hwndUnderMouse); + wxWindow *winUnderMouse = wxFindWinFromHandle(hwndUnderMouse); if ( winUnderMouse ) { // translate the mouse coords to the other window coords @@ -4294,11 +5431,12 @@ static wxWindowMSW *FindWindowForMouseEvent(wxWindowMSW *win, int *x, int *y) // return win; } +#endif // __WXWINCE__ 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 + // WM_MOUSELAST, so it's enough to subtract WM_MOUSEMOVE == WM_MOUSEFIRST // from the message id and take the value in the table to get wxWin event // id static const wxEventType eventsMouse[] = @@ -4312,13 +5450,33 @@ bool wxWindowMSW::HandleMouseEvent(WXUINT msg, int x, int y, WXUINT flags) wxEVT_RIGHT_DCLICK, wxEVT_MIDDLE_DOWN, wxEVT_MIDDLE_UP, - wxEVT_MIDDLE_DCLICK + wxEVT_MIDDLE_DCLICK, + 0, // this one is for wxEVT_MOTION which is not used here + wxEVT_AUX1_DOWN, + wxEVT_AUX1_UP, + wxEVT_AUX1_DCLICK, + wxEVT_AUX2_DOWN, + wxEVT_AUX2_UP, + wxEVT_AUX2_DCLICK }; +#ifdef wxHAS_XBUTTON + // the same messages are used for both auxillary mouse buttons so we need + // to adjust the index manually + switch ( msg ) + { + case WM_XBUTTONDOWN: + case WM_XBUTTONUP: + case WM_XBUTTONDBLCLK: + if ( flags & MK_XBUTTON2 ) + msg += wxEVT_AUX2_DOWN - wxEVT_AUX1_DOWN; + } +#endif // wxHAS_XBUTTON + wxMouseEvent event(eventsMouse[msg - WM_MOUSEMOVE]); InitMouseEvent(event, x, y, flags); - return GetEventHandler()->ProcessEvent(event); + return HandleWindowEvent(event); } bool wxWindowMSW::HandleMouseMove(int x, int y, WXUINT flags) @@ -4332,27 +5490,78 @@ bool wxWindowMSW::HandleMouseMove(int x, int y, WXUINT flags) if ( !HasCapture() || IsMouseInWindow() ) { // Generate an ENTER event - m_mouseInWindow = TRUE; + m_mouseInWindow = true; + +#ifdef HAVE_TRACKMOUSEEVENT + typedef BOOL (WINAPI *_TrackMouseEvent_t)(LPTRACKMOUSEEVENT); +#ifdef __WXWINCE__ + static const _TrackMouseEvent_t + s_pfn_TrackMouseEvent = _TrackMouseEvent; +#else // !__WXWINCE__ + static _TrackMouseEvent_t s_pfn_TrackMouseEvent; + static bool s_initDone = false; + if ( !s_initDone ) + { + // see comment in wxApp::GetComCtl32Version() explaining the + // use of wxLoadedDLL + wxLoadedDLL dllComCtl32(wxT("comctl32.dll")); + if ( dllComCtl32.IsLoaded() ) + { + s_pfn_TrackMouseEvent = (_TrackMouseEvent_t) + dllComCtl32.RawGetSymbol(wxT("_TrackMouseEvent")); + } + + s_initDone = true; + } + + if ( s_pfn_TrackMouseEvent ) +#endif // __WXWINCE__/!__WXWINCE__ + { + WinStruct trackinfo; + + trackinfo.dwFlags = TME_LEAVE; + trackinfo.hwndTrack = GetHwnd(); + + (*s_pfn_TrackMouseEvent)(&trackinfo); + } +#endif // HAVE_TRACKMOUSEEVENT wxMouseEvent event(wxEVT_ENTER_WINDOW); InitMouseEvent(event, x, y, flags); - (void)GetEventHandler()->ProcessEvent(event); + (void)HandleWindowEvent(event); + } + } +#ifdef HAVE_TRACKMOUSEEVENT + else // mouse not in window + { + // Check if we need to send a LEAVE event + // Windows doesn't send WM_MOUSELEAVE if the mouse has been captured so + // send it here if we are using native mouse leave tracking + if ( HasCapture() && !IsMouseInWindow() ) + { + GenerateMouseLeave(); } } +#endif // HAVE_TRACKMOUSEEVENT #if wxUSE_MOUSEEVENT_HACK - // Window gets a click down message followed by a mouse move message even - // if position isn't changed! We want to discard the trailing move event - // if x and y are the same. - if ( (m_lastMouseEvent == wxEVT_RIGHT_DOWN || - m_lastMouseEvent == wxEVT_LEFT_DOWN || - m_lastMouseEvent == wxEVT_MIDDLE_DOWN) && - (m_lastMouseX == x && m_lastMouseY == y) ) - { - m_lastMouseEvent = wxEVT_MOTION; + // Windows often generates mouse events even if mouse position hasn't + // changed (http://article.gmane.org/gmane.comp.lib.wxwidgets.devel/66576) + // + // Filter this out as it can result in unexpected behaviour compared to + // other platforms + if ( gs_lastMouseEvent.type == wxEVT_RIGHT_DOWN || + gs_lastMouseEvent.type == wxEVT_LEFT_DOWN || + gs_lastMouseEvent.type == wxEVT_MIDDLE_DOWN || + gs_lastMouseEvent.type == wxEVT_MOTION ) + { + if ( ClientToScreen(wxPoint(x, y)) == gs_lastMouseEvent.pos ) + { + gs_lastMouseEvent.type = wxEVT_MOTION; - return FALSE; + return false; + } } #endif // wxUSE_MOUSEEVENT_HACK @@ -4360,16 +5569,22 @@ bool wxWindowMSW::HandleMouseMove(int x, int y, WXUINT flags) } -bool wxWindowMSW::HandleMouseWheel(WXWPARAM wParam, WXLPARAM lParam) +bool +wxWindowMSW::HandleMouseWheel(wxMouseWheelAxis axis, + WXWPARAM wParam, WXLPARAM lParam) { #if wxUSE_MOUSEWHEEL + // notice that WM_MOUSEWHEEL position is in screen coords (as it's + // forwarded up to the parent by DefWindowProc()) and not in the client + // ones as all the other messages, translate them to the client coords for + // consistency + const wxPoint + pt = ScreenToClient(wxPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); wxMouseEvent event(wxEVT_MOUSEWHEEL); - InitMouseEvent(event, - GET_X_LPARAM(lParam), - GET_Y_LPARAM(lParam), - LOWORD(wParam)); + InitMouseEvent(event, pt.x, pt.y, LOWORD(wParam)); event.m_wheelRotation = (short)HIWORD(wParam); event.m_wheelDelta = WHEEL_DELTA; + event.m_wheelAxis = axis; static int s_linesPerRotation = -1; if ( s_linesPerRotation == -1 ) @@ -4378,7 +5593,7 @@ bool wxWindowMSW::HandleMouseWheel(WXWPARAM wParam, WXLPARAM lParam) &s_linesPerRotation, 0)) { // this is not supposed to happen - wxLogLastError(_T("SystemParametersInfo(GETWHEELSCROLLLINES)")); + wxLogLastError(wxT("SystemParametersInfo(GETWHEELSCROLLLINES)")); // the default is 3, so use it if SystemParametersInfo() failed s_linesPerRotation = 3; @@ -4386,100 +5601,160 @@ bool wxWindowMSW::HandleMouseWheel(WXWPARAM wParam, WXLPARAM lParam) } event.m_linesPerAction = s_linesPerRotation; - return GetEventHandler()->ProcessEvent(event); + return HandleWindowEvent(event); -#else - (void) wParam; - (void) lParam; +#else // !wxUSE_MOUSEWHEEL + wxUnusedVar(wParam); + wxUnusedVar(lParam); - return FALSE; -#endif + return false; +#endif // wxUSE_MOUSEWHEEL/!wxUSE_MOUSEWHEEL } +void wxWindowMSW::GenerateMouseLeave() +{ + m_mouseInWindow = false; + + int state = 0; + if ( wxIsShiftDown() ) + state |= MK_SHIFT; + if ( wxIsCtrlDown() ) + state |= MK_CONTROL; + + // Only the high-order bit should be tested + if ( GetKeyState( VK_LBUTTON ) & (1<<15) ) + state |= MK_LBUTTON; + if ( GetKeyState( VK_MBUTTON ) & (1<<15) ) + state |= MK_MBUTTON; + if ( GetKeyState( VK_RBUTTON ) & (1<<15) ) + state |= MK_RBUTTON; + + POINT pt; + wxGetCursorPosMSW(&pt); + + // 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 event(wxEVT_LEAVE_WINDOW); + InitMouseEvent(event, pt.x, pt.y, state); + + (void)HandleWindowEvent(event); +} // --------------------------------------------------------------------------- // keyboard handling // --------------------------------------------------------------------------- -// create the key event of the given type for the given key - used by -// HandleChar and HandleKeyDown/Up -wxKeyEvent wxWindowMSW::CreateKeyEvent(wxEventType evType, - int id, - WXLPARAM lParam, - WXWPARAM wParam) const +namespace { - wxKeyEvent event(evType); - event.SetId(GetId()); + +// Implementation of InitAnyKeyEvent() which can also be used when there is no +// associated window: this can happen for the wxEVT_CHAR_HOOK events created by +// the global keyboard hook (e.g. the event might have happened in a non-wx +// window). +void +MSWInitAnyKeyEvent(wxKeyEvent& event, + WXWPARAM wParam, + WXLPARAM lParam, + const wxWindowBase *win /* may be NULL */) +{ + if ( win ) + { + event.SetId(win->GetId()); + event.SetEventObject(const_cast(win)); + } + else // No associated window. + { + // Use wxID_ANY for compatibility with the old code even if wxID_NONE + // would arguably make more sense. + event.SetId(wxID_ANY); + } + event.m_shiftDown = wxIsShiftDown(); event.m_controlDown = wxIsCtrlDown(); event.m_altDown = (HIWORD(lParam) & KF_ALTDOWN) == KF_ALTDOWN; - event.m_eventObject = (wxWindow *)this; // const_cast - event.m_keyCode = id; event.m_rawCode = (wxUint32) wParam; event.m_rawFlags = (wxUint32) lParam; - event.SetTimestamp(s_currentMsg.time); +#ifndef __WXWINCE__ + event.SetTimestamp(::GetMessageTime()); +#endif +} - // translate the position to client coords - POINT pt; - GetCursorPos(&pt); - RECT rect; - GetWindowRect(GetHwnd(),&rect); - pt.x -= rect.left; - pt.y -= rect.top; +} // anonymous namespace - event.m_x = pt.x; - event.m_y = pt.y; +void +wxWindowMSW::InitAnyKeyEvent(wxKeyEvent& event, + WXWPARAM wParam, + WXLPARAM lParam) const +{ + MSWInitAnyKeyEvent(event, wParam, lParam, this); +} + +wxKeyEvent +wxWindowMSW::CreateKeyEvent(wxEventType evType, + WXWPARAM wParam, + WXLPARAM lParam) const +{ + // Catch any attempts to use this with WM_CHAR, it wouldn't work because + // wParam is supposed to be a virtual key and not a character here. + wxASSERT_MSG( evType != wxEVT_CHAR && evType != wxEVT_CHAR_HOOK, + "CreateKeyEvent() can't be used for char events" ); + + wxKeyEvent event(evType); + InitAnyKeyEvent(event, wParam, lParam); + + event.m_keyCode = wxMSWKeyboard::VKToWX + ( + wParam, + lParam +#if wxUSE_UNICODE + , &event.m_uniChar +#endif // wxUSE_UNICODE + ); return event; } -// isASCII is TRUE only when we're called from WM_CHAR handler and not from -// WM_KEYDOWN one -bool wxWindowMSW::HandleChar(WXWPARAM wParam, WXLPARAM lParam, bool isASCII) +wxKeyEvent +wxWindowMSW::CreateCharEvent(wxEventType evType, + WXWPARAM wParam, + WXLPARAM lParam) const { - int id; - if ( isASCII ) - { - // 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) ) - { - switch (id) - { - case 13: - id = WXK_RETURN; - break; - - case 8: - id = WXK_BACK; - break; + wxKeyEvent event(evType); + InitAnyKeyEvent(event, wParam, lParam); - case 9: - id = WXK_TAB; - break; +#if wxUSE_UNICODE + // TODO: wParam uses UTF-16 so this is incorrect for characters outside of + // the BMP, we should use WM_UNICHAR to handle them. + event.m_uniChar = wParam; +#endif // wxUSE_UNICODE - default: - //ctrlDown = TRUE; - break; - } - } + // Set non-Unicode key code too for compatibility if possible. + if ( wParam < 0x80 ) + { + // It's an ASCII character, no need to translate it. + event.m_keyCode = wParam; } - else // we're called from WM_KEYDOWN + else { - id = wxCharCodeMSWToWX(wParam); - if ( id == 0 ) + // Check if this key can be represented (as a single character) in the + // current locale. + const wchar_t wc = wParam; + char ch; + if ( wxConvLibc.FromWChar(&ch, 1, &wc, 1) != wxCONV_FAILED ) { - // 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; + // For compatibility continue to provide the key code in this field + // even though using GetUnicodeKey() is recommended now. + event.m_keyCode = static_cast(ch); } + //else: Key can't be represented in the current locale, leave m_keyCode + // as WXK_NONE and use GetUnicodeKey() to access the character. } - 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 @@ -4487,73 +5762,59 @@ bool wxWindowMSW::HandleChar(WXWPARAM wParam, WXLPARAM lParam, bool isASCII) // 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_keyCode >= 32 && event.m_keyCode < 256) ) { event.m_controlDown = - event.m_altDown = FALSE; + event.m_altDown = false; } - return GetEventHandler()->ProcessEvent(event); + return event; } -bool wxWindowMSW::HandleKeyDown(WXWPARAM wParam, WXLPARAM lParam) +// isASCII is true only when we're called from WM_CHAR handler and not from +// WM_KEYDOWN one +bool wxWindowMSW::HandleChar(WXWPARAM wParam, WXLPARAM lParam) { - int id = wxCharCodeMSWToWX(wParam); - - if ( !id ) - { - // normal ASCII char - id = wParam; - } - - if ( id != -1 ) // VZ: does this ever happen (FIXME)? - { - wxKeyEvent event(CreateKeyEvent(wxEVT_KEY_DOWN, id, lParam, wParam)); - if ( GetEventHandler()->ProcessEvent(event) ) - { - return TRUE; - } - } + wxKeyEvent event(CreateCharEvent(wxEVT_CHAR, wParam, lParam)); + return HandleWindowEvent(event); +} - return FALSE; +bool wxWindowMSW::HandleKeyDown(WXWPARAM wParam, WXLPARAM lParam) +{ + wxKeyEvent event(CreateKeyEvent(wxEVT_KEY_DOWN, wParam, lParam)); + return HandleWindowEvent(event); } bool wxWindowMSW::HandleKeyUp(WXWPARAM wParam, WXLPARAM lParam) { - int id = wxCharCodeMSWToWX(wParam); - - if ( !id ) - { - // normal ASCII char - id = wParam; - } - - if ( id != -1 ) // VZ: does this ever happen (FIXME)? - { - wxKeyEvent event(CreateKeyEvent(wxEVT_KEY_UP, id, lParam, wParam)); - if ( GetEventHandler()->ProcessEvent(event) ) - return TRUE; - } - - return FALSE; + wxKeyEvent event(CreateKeyEvent(wxEVT_KEY_UP, wParam, lParam)); + return HandleWindowEvent(event); } -int wxWindowMSW::HandleMenuChar(int chAccel, WXLPARAM lParam) +#if wxUSE_MENUS +int wxWindowMSW::HandleMenuChar(int WXUNUSED_IN_WINCE(chAccel), + WXLPARAM WXUNUSED_IN_WINCE(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); + WinStruct mii; + + // we could use MIIM_FTYPE here as we only need to know if the item is + // ownerdrawn or not and not dwTypeData which MIIM_TYPE also returns, but + // MIIM_FTYPE is not supported under Win95 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++ ) { + // previous loop iteration could modify it, reset it back before + // calling GetMenuItemInfo() to prevent it from overflowing dwTypeData + mii.cch = 0; + if ( ::GetMenuItemInfo(hmenu, i, TRUE, &mii) ) { if ( mii.fType == MFT_OWNERDRAW ) @@ -4563,20 +5824,21 @@ int wxWindowMSW::HandleMenuChar(int chAccel, WXLPARAM lParam) // menu creation code wxMenuItem *item = (wxMenuItem*)mii.dwItemData; - const wxChar *p = wxStrchr(item->GetText(), _T('&')); + const wxString label(item->GetItemLabel()); + const wxChar *p = wxStrchr(label.t_str(), wxT('&')); while ( p++ ) { - if ( *p == _T('&') ) + if ( *p == wxT('&') ) { // this is not the accel char, find the real one - p = wxStrchr(p + 1, _T('&')); + p = wxStrchr(p + 1, wxT('&')); } 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 ) + if ( (wchar_t)wxToupper(*p) == (wchar_t)chAccel ) { return i; } @@ -4591,15 +5853,28 @@ int wxWindowMSW::HandleMenuChar(int chAccel, WXLPARAM lParam) } else // failed to get the menu text? { - // it's not fatal, so don't show error, but still log - // it - wxLogLastError(_T("GetMenuItemInfo")); + // it's not fatal, so don't show error, but still log it + wxLogLastError(wxT("GetMenuItemInfo")); } } #endif return wxNOT_FOUND; } +#endif // wxUSE_MENUS + +bool wxWindowMSW::HandleClipboardEvent(WXUINT nMsg) +{ + const wxEventType type = nMsg == WM_CUT ? wxEVT_COMMAND_TEXT_CUT + : nMsg == WM_COPY ? wxEVT_COMMAND_TEXT_COPY + : /* nMsg == WM_PASTE */ wxEVT_COMMAND_TEXT_PASTE; + wxClipboardTextEvent evt(type, GetId()); + + evt.SetEventObject(this); + + return HandleWindowEvent(evt); +} + // --------------------------------------------------------------------------- // joystick // --------------------------------------------------------------------------- @@ -4675,16 +5950,23 @@ bool wxWindowMSW::HandleJoystickEvent(WXUINT msg, int x, int y, WXUINT flags) default: wxFAIL_MSG(wxT("no such joystick event")); - return FALSE; + return false; } wxJoystickEvent event(eventType, buttons, joystick, change); - event.SetPosition(wxPoint(x, y)); + if ( eventType == wxEVT_JOY_ZMOVE ) + event.SetZPosition(x); + else + event.SetPosition(wxPoint(x, y)); event.SetEventObject(this); - return GetEventHandler()->ProcessEvent(event); + return HandleWindowEvent(event); #else - return FALSE; + wxUnusedVar(msg); + wxUnusedVar(x); + wxUnusedVar(y); + wxUnusedVar(flags); + return false; #endif } @@ -4695,7 +5977,7 @@ bool wxWindowMSW::HandleJoystickEvent(WXUINT msg, int x, int y, WXUINT flags) bool wxWindowMSW::MSWOnScroll(int orientation, WXWORD wParam, WXWORD pos, WXHWND control) { - if ( control ) + if ( control && control != m_hWnd ) // Prevent infinite recursion { wxWindow *child = wxFindWinFromHandle(control); if ( child ) @@ -4705,32 +5987,32 @@ bool wxWindowMSW::MSWOnScroll(int orientation, WXWORD wParam, wxScrollWinEvent event; event.SetPosition(pos); event.SetOrientation(orientation); - event.m_eventObject = this; + event.SetEventObject(this); switch ( wParam ) { case SB_TOP: - event.m_eventType = wxEVT_SCROLLWIN_TOP; + event.SetEventType(wxEVT_SCROLLWIN_TOP); break; case SB_BOTTOM: - event.m_eventType = wxEVT_SCROLLWIN_BOTTOM; + event.SetEventType(wxEVT_SCROLLWIN_BOTTOM); break; case SB_LINEUP: - event.m_eventType = wxEVT_SCROLLWIN_LINEUP; + event.SetEventType(wxEVT_SCROLLWIN_LINEUP); break; case SB_LINEDOWN: - event.m_eventType = wxEVT_SCROLLWIN_LINEDOWN; + event.SetEventType(wxEVT_SCROLLWIN_LINEDOWN); break; case SB_PAGEUP: - event.m_eventType = wxEVT_SCROLLWIN_PAGEUP; + event.SetEventType(wxEVT_SCROLLWIN_PAGEUP); break; case SB_PAGEDOWN: - event.m_eventType = wxEVT_SCROLLWIN_PAGEDOWN; + event.SetEventType(wxEVT_SCROLLWIN_PAGEDOWN); break; case SB_THUMBPOSITION: @@ -4745,49 +6027,70 @@ bool wxWindowMSW::MSWOnScroll(int orientation, WXWORD wParam, scrollInfo.fMask = SIF_TRACKPOS; if ( !::GetScrollInfo(GetHwnd(), - orientation == wxHORIZONTAL ? SB_HORZ - : SB_VERT, + WXOrientToSB(orientation), &scrollInfo) ) { - // Not neccessarily an error, if there are no scrollbars yet. - // wxLogLastError(_T("GetScrollInfo")); + // Not necessarily an error, if there are no scrollbars yet. + // wxLogLastError(wxT("GetScrollInfo")); } event.SetPosition(scrollInfo.nTrackPos); } - event.m_eventType = wParam == SB_THUMBPOSITION + event.SetEventType( wParam == SB_THUMBPOSITION ? wxEVT_SCROLLWIN_THUMBRELEASE - : wxEVT_SCROLLWIN_THUMBTRACK; + : wxEVT_SCROLLWIN_THUMBTRACK ); break; default: - return FALSE; + return false; } - return GetEventHandler()->ProcessEvent(event); + return HandleWindowEvent(event); +} + +// ---------------------------------------------------------------------------- +// custom message handlers +// ---------------------------------------------------------------------------- + +/* static */ bool +wxWindowMSW::MSWRegisterMessageHandler(int msg, MSWMessageHandler handler) +{ + wxCHECK_MSG( gs_messageHandlers.find(msg) == gs_messageHandlers.end(), + false, wxT("registering handler for the same message twice") ); + + gs_messageHandlers[msg] = handler; + return true; +} + +/* static */ void +wxWindowMSW::MSWUnregisterMessageHandler(int msg, MSWMessageHandler handler) +{ + const MSWMessageHandlers::iterator i = gs_messageHandlers.find(msg); + wxCHECK_RET( i != gs_messageHandlers.end() && i->second == handler, + wxT("unregistering non-registered handler?") ); + + gs_messageHandlers.erase(i); } // =========================================================================== // global functions // =========================================================================== -void wxGetCharSize(WXHWND wnd, int *x, int *y, const wxFont *the_font) +void wxGetCharSize(WXHWND wnd, int *x, int *y, const wxFont& the_font) { TEXTMETRIC tm; HDC dc = ::GetDC((HWND) wnd); - HFONT fnt =0; HFONT was = 0; - if ( the_font ) - { - // the_font->UseResource(); - // the_font->RealizeResource(); - fnt = (HFONT)((wxFont *)the_font)->GetResourceHandle(); // const_cast - if ( fnt ) - was = (HFONT) SelectObject(dc,fnt); - } + + // the_font.UseResource(); + // the_font.RealizeResource(); + HFONT fnt = (HFONT)the_font.GetResourceHandle(); // const_cast + if ( fnt ) + was = (HFONT) SelectObject(dc,fnt); + GetTextMetrics(dc, &tm); - if ( the_font && fnt && was ) + if ( fnt && was ) { SelectObject(dc,was); } @@ -4798,193 +6101,438 @@ void wxGetCharSize(WXHWND wnd, int *x, int *y, const wxFont *the_font) if ( y ) *y = tm.tmHeight + tm.tmExternalLeading; - // if ( the_font ) - // the_font->ReleaseResource(); -} - -// Returns 0 if was a normal ASCII value, not a special key. This indicates that -// the key should be ignored by WM_KEYDOWN and processed by WM_CHAR instead. -int wxCharCodeMSWToWX(int keySym) -{ - int id; - switch (keySym) - { - case VK_CANCEL: id = WXK_CANCEL; break; - case VK_BACK: id = WXK_BACK; break; - case VK_TAB: id = WXK_TAB; break; - case VK_CLEAR: id = WXK_CLEAR; break; - case VK_RETURN: id = WXK_RETURN; break; - case VK_SHIFT: id = WXK_SHIFT; break; - case VK_CONTROL: id = WXK_CONTROL; break; - case VK_MENU : id = WXK_MENU; break; - case VK_PAUSE: id = WXK_PAUSE; break; - case VK_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; - case VK_NEXT : id = WXK_NEXT; break; - case VK_END: id = WXK_END; break; - case VK_HOME : id = WXK_HOME; break; - case VK_LEFT : id = WXK_LEFT; break; - case VK_UP: id = WXK_UP; break; - case VK_RIGHT: id = WXK_RIGHT; break; - case VK_DOWN : id = WXK_DOWN; break; - case VK_SELECT: id = WXK_SELECT; break; - case VK_PRINT: id = WXK_PRINT; break; - case VK_EXECUTE: id = WXK_EXECUTE; break; - case VK_INSERT: id = WXK_INSERT; break; - case VK_DELETE: id = WXK_DELETE; break; - case VK_HELP : id = WXK_HELP; break; - case VK_NUMPAD0: id = WXK_NUMPAD0; break; - case VK_NUMPAD1: id = WXK_NUMPAD1; break; - case VK_NUMPAD2: id = WXK_NUMPAD2; break; - case VK_NUMPAD3: id = WXK_NUMPAD3; break; - case VK_NUMPAD4: id = WXK_NUMPAD4; break; - case VK_NUMPAD5: id = WXK_NUMPAD5; break; - case VK_NUMPAD6: id = WXK_NUMPAD6; break; - case VK_NUMPAD7: id = WXK_NUMPAD7; break; - case VK_NUMPAD8: id = WXK_NUMPAD8; break; - case VK_NUMPAD9: id = WXK_NUMPAD9; break; - case VK_MULTIPLY: id = WXK_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; - case VK_F4: id = WXK_F4; break; - case VK_F5: id = WXK_F5; break; - case VK_F6: id = WXK_F6; break; - case VK_F7: id = WXK_F7; break; - case VK_F8: id = WXK_F8; break; - case VK_F9: id = WXK_F9; break; - case VK_F10: id = WXK_F10; break; - case VK_F11: id = WXK_F11; break; - case VK_F12: id = WXK_F12; break; - case VK_F13: id = WXK_F13; break; - case VK_F14: id = WXK_F14; break; - case VK_F15: id = WXK_F15; break; - case VK_F16: id = WXK_F16; break; - case VK_F17: id = WXK_F17; break; - case VK_F18: id = WXK_F18; break; - case VK_F19: id = WXK_F19; break; - case VK_F20: id = WXK_F20; break; - case VK_F21: id = WXK_F21; break; - case VK_F22: id = WXK_F22; break; - case VK_F23: id = WXK_F23; break; - case VK_F24: id = WXK_F24; break; - case VK_NUMLOCK: id = WXK_NUMLOCK; break; - case VK_SCROLL: id = WXK_SCROLL; break; - - 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; + // the_font.ReleaseResource(); +} + +// ---------------------------------------------------------------------------- +// keyboard codes +// ---------------------------------------------------------------------------- + +namespace wxMSWKeyboard +{ + +namespace +{ + +// use the "extended" bit of lParam to distinguish extended keys from normal +// keys as the same virtual key code is sent for both by Windows +inline +int ChooseNormalOrExtended(int lParam, int keyNormal, int keyExtended) +{ + // except that if lParam is 0, it means we don't have real lParam from + // WM_KEYDOWN but are just translating just a VK constant (e.g. done from + // msw/treectrl.cpp when processing TVN_KEYDOWN) -- then assume this is a + // non-numpad (hence extended) key as this is a more common case + return !lParam || (HIWORD(lParam) & KF_EXTENDED) ? keyExtended : keyNormal; +} + +// this array contains the Windows virtual key codes which map one to one to +// WXK_xxx constants and is used in wxMSWKeyboard::VKToWX/WXToVK() below +// +// note that keys having a normal and numpad version (e.g. WXK_HOME and +// WXK_NUMPAD_HOME) are not included in this table as the mapping is not 1-to-1 +const struct wxKeyMapping +{ + int vk; + wxKeyCode wxk; +} gs_specialKeys[] = +{ + { VK_CANCEL, WXK_CANCEL }, + { VK_BACK, WXK_BACK }, + { VK_TAB, WXK_TAB }, + { VK_CLEAR, WXK_CLEAR }, + { VK_SHIFT, WXK_SHIFT }, + { VK_CONTROL, WXK_CONTROL }, + { VK_MENU , WXK_ALT }, + { VK_PAUSE, WXK_PAUSE }, + { VK_CAPITAL, WXK_CAPITAL }, + { VK_SPACE, WXK_SPACE }, + { VK_ESCAPE, WXK_ESCAPE }, + { VK_SELECT, WXK_SELECT }, + { VK_PRINT, WXK_PRINT }, + { VK_EXECUTE, WXK_EXECUTE }, + { VK_SNAPSHOT, WXK_SNAPSHOT }, + { VK_HELP, WXK_HELP }, + + { VK_NUMPAD0, WXK_NUMPAD0 }, + { VK_NUMPAD1, WXK_NUMPAD1 }, + { VK_NUMPAD2, WXK_NUMPAD2 }, + { VK_NUMPAD3, WXK_NUMPAD3 }, + { VK_NUMPAD4, WXK_NUMPAD4 }, + { VK_NUMPAD5, WXK_NUMPAD5 }, + { VK_NUMPAD6, WXK_NUMPAD6 }, + { VK_NUMPAD7, WXK_NUMPAD7 }, + { VK_NUMPAD8, WXK_NUMPAD8 }, + { VK_NUMPAD9, WXK_NUMPAD9 }, + { VK_MULTIPLY, WXK_NUMPAD_MULTIPLY }, + { VK_ADD, WXK_NUMPAD_ADD }, + { VK_SUBTRACT, WXK_NUMPAD_SUBTRACT }, + { VK_DECIMAL, WXK_NUMPAD_DECIMAL }, + { VK_DIVIDE, WXK_NUMPAD_DIVIDE }, + + { VK_F1, WXK_F1 }, + { VK_F2, WXK_F2 }, + { VK_F3, WXK_F3 }, + { VK_F4, WXK_F4 }, + { VK_F5, WXK_F5 }, + { VK_F6, WXK_F6 }, + { VK_F7, WXK_F7 }, + { VK_F8, WXK_F8 }, + { VK_F9, WXK_F9 }, + { VK_F10, WXK_F10 }, + { VK_F11, WXK_F11 }, + { VK_F12, WXK_F12 }, + { VK_F13, WXK_F13 }, + { VK_F14, WXK_F14 }, + { VK_F15, WXK_F15 }, + { VK_F16, WXK_F16 }, + { VK_F17, WXK_F17 }, + { VK_F18, WXK_F18 }, + { VK_F19, WXK_F19 }, + { VK_F20, WXK_F20 }, + { VK_F21, WXK_F21 }, + { VK_F22, WXK_F22 }, + { VK_F23, WXK_F23 }, + { VK_F24, WXK_F24 }, + + { VK_NUMLOCK, WXK_NUMLOCK }, + { VK_SCROLL, WXK_SCROLL }, #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; + { VK_LWIN, WXK_WINDOWS_LEFT }, + { VK_RWIN, WXK_WINDOWS_RIGHT }, + { VK_APPS, WXK_WINDOWS_MENU }, #endif // VK_APPS defined +}; + +} // anonymous namespace + +int VKToWX(WXWORD vk, WXLPARAM lParam, wchar_t *uc) +{ + int wxk; + + // check the table first + for ( size_t n = 0; n < WXSIZEOF(gs_specialKeys); n++ ) + { + if ( gs_specialKeys[n].vk == vk ) + { + wxk = gs_specialKeys[n].wxk; + if ( wxk < WXK_START ) + { + // Unicode code for this key is the same as its ASCII code. + if ( uc ) + *uc = wxk; + } + + return wxk; + } + } + + // keys requiring special handling + switch ( vk ) + { + case VK_OEM_1: + case VK_OEM_PLUS: + case VK_OEM_COMMA: + case VK_OEM_MINUS: + case VK_OEM_PERIOD: + 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_102: + // MapVirtualKey() returns 0 if it fails to convert the virtual + // key which nicely corresponds to our WXK_NONE. + wxk = ::MapVirtualKey(vk, MAPVK_VK_TO_CHAR); + + if ( HIWORD(wxk) & 0x8000 ) + { + // It's a dead key and we don't return anything at all for them + // as we simply don't have any way to indicate the difference + // between e.g. a normal "'" and "'" as a dead key -- and + // generating the same events for them just doesn't seem like a + // good idea. + wxk = WXK_NONE; + } + + // In any case return this as a Unicode character value. + if ( uc ) + *uc = wxk; + + // For compatibility with the old non-Unicode code we continue + // returning key codes for Latin-1 characters directly + // (normally it would really only make sense to do it for the + // ASCII characters, not Latin-1 ones). + if ( wxk > 255 ) + { + // But for anything beyond this we can only return the key + // value as a real Unicode character, not a wxKeyCode + // because this enum values clash with Unicode characters + // (e.g. WXK_LBUTTON also happens to be U+012C a.k.a. + // "LATIN CAPITAL LETTER I WITH BREVE"). + wxk = WXK_NONE; + } + break; + + // handle extended keys + case VK_PRIOR: + wxk = ChooseNormalOrExtended(lParam, WXK_NUMPAD_PAGEUP, WXK_PAGEUP); + break; + + case VK_NEXT: + wxk = ChooseNormalOrExtended(lParam, WXK_NUMPAD_PAGEDOWN, WXK_PAGEDOWN); + break; + + case VK_END: + wxk = ChooseNormalOrExtended(lParam, WXK_NUMPAD_END, WXK_END); + break; + + case VK_HOME: + wxk = ChooseNormalOrExtended(lParam, WXK_NUMPAD_HOME, WXK_HOME); + break; + + case VK_LEFT: + wxk = ChooseNormalOrExtended(lParam, WXK_NUMPAD_LEFT, WXK_LEFT); + break; + + case VK_UP: + wxk = ChooseNormalOrExtended(lParam, WXK_NUMPAD_UP, WXK_UP); + break; + + case VK_RIGHT: + wxk = ChooseNormalOrExtended(lParam, WXK_NUMPAD_RIGHT, WXK_RIGHT); + break; + + case VK_DOWN: + wxk = ChooseNormalOrExtended(lParam, WXK_NUMPAD_DOWN, WXK_DOWN); + break; + + case VK_INSERT: + wxk = ChooseNormalOrExtended(lParam, WXK_NUMPAD_INSERT, WXK_INSERT); + break; + + case VK_DELETE: + wxk = ChooseNormalOrExtended(lParam, WXK_NUMPAD_DELETE, WXK_DELETE); + break; + + case VK_RETURN: + // don't use ChooseNormalOrExtended() here as the keys are reversed + // here: numpad enter is the extended one + wxk = HIWORD(lParam) & KF_EXTENDED ? WXK_NUMPAD_ENTER : WXK_RETURN; + + if ( uc ) + *uc = WXK_RETURN; + break; default: - id = 0; - } - - return id; -} - -int wxCharCodeWXToMSW(int id, bool *isVirtual) -{ - *isVirtual = TRUE; - int keySym = 0; - switch (id) - { - case WXK_CANCEL: keySym = VK_CANCEL; break; - case WXK_CLEAR: keySym = VK_CLEAR; break; - case WXK_SHIFT: keySym = VK_SHIFT; break; - case WXK_CONTROL: keySym = VK_CONTROL; break; - case WXK_MENU : keySym = VK_MENU; break; - case WXK_PAUSE: keySym = VK_PAUSE; break; - case WXK_PRIOR: keySym = VK_PRIOR; break; - case WXK_NEXT : keySym = VK_NEXT; break; - case WXK_END: keySym = VK_END; break; - case WXK_HOME : keySym = VK_HOME; break; - case WXK_LEFT : keySym = VK_LEFT; break; - case WXK_UP: keySym = VK_UP; break; - case WXK_RIGHT: keySym = VK_RIGHT; break; - case WXK_DOWN : keySym = VK_DOWN; break; - case WXK_SELECT: keySym = VK_SELECT; break; - case WXK_PRINT: keySym = VK_PRINT; break; - case WXK_EXECUTE: keySym = VK_EXECUTE; break; - case WXK_INSERT: keySym = VK_INSERT; break; - case WXK_DELETE: keySym = VK_DELETE; break; - case WXK_HELP : keySym = VK_HELP; break; - case WXK_NUMPAD0: keySym = VK_NUMPAD0; break; - case WXK_NUMPAD1: keySym = VK_NUMPAD1; break; - case WXK_NUMPAD2: keySym = VK_NUMPAD2; break; - case WXK_NUMPAD3: keySym = VK_NUMPAD3; break; - case WXK_NUMPAD4: keySym = VK_NUMPAD4; break; - case WXK_NUMPAD5: keySym = VK_NUMPAD5; break; - case WXK_NUMPAD6: keySym = VK_NUMPAD6; break; - case WXK_NUMPAD7: keySym = VK_NUMPAD7; break; - case WXK_NUMPAD8: keySym = VK_NUMPAD8; break; - case WXK_NUMPAD9: keySym = VK_NUMPAD9; 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; - case WXK_F4: keySym = VK_F4; break; - case WXK_F5: keySym = VK_F5; break; - case WXK_F6: keySym = VK_F6; break; - case WXK_F7: keySym = VK_F7; break; - case WXK_F8: keySym = VK_F8; break; - case WXK_F9: keySym = VK_F9; break; - case WXK_F10: keySym = VK_F10; break; - case WXK_F11: keySym = VK_F11; break; - case WXK_F12: keySym = VK_F12; break; - case WXK_F13: keySym = VK_F13; break; - case WXK_F14: keySym = VK_F14; break; - case WXK_F15: keySym = VK_F15; break; - case WXK_F16: keySym = VK_F16; break; - case WXK_F17: keySym = VK_F17; break; - case WXK_F18: keySym = VK_F18; break; - case WXK_F19: keySym = VK_F19; break; - case WXK_F20: keySym = VK_F20; break; - case WXK_F21: keySym = VK_F21; break; - case WXK_F22: keySym = VK_F22; break; - case WXK_F23: keySym = VK_F23; break; - case WXK_F24: keySym = VK_F24; break; - case WXK_NUMLOCK: keySym = VK_NUMLOCK; break; - case WXK_SCROLL: keySym = VK_SCROLL; break; - default: + if ( (vk >= '0' && vk <= '9') || (vk >= 'A' && vk <= 'Z') ) + { + // A simple alphanumeric key and the values of them coincide in + // Windows and wx for both ASCII and Unicode codes. + wxk = vk; + } + else // Something we simply don't know about at all. + { + wxk = WXK_NONE; + } + + if ( uc ) + *uc = vk; + } + + return wxk; +} + +WXWORD WXToVK(int wxk, bool *isExtended) +{ + // check the table first + for ( size_t n = 0; n < WXSIZEOF(gs_specialKeys); n++ ) + { + if ( gs_specialKeys[n].wxk == wxk ) { - *isVirtual = FALSE; - keySym = id; + // All extended keys (i.e. non-numpad versions of the keys that + // exist both in the numpad and outside of it) are dealt with + // below. + if ( isExtended ) + *isExtended = false; + + return gs_specialKeys[n].vk; + } + } + + // and then check for special keys not included in the table + bool extended = false; + WXWORD vk; + switch ( wxk ) + { + case WXK_PAGEUP: + extended = true; + case WXK_NUMPAD_PAGEUP: + vk = VK_PRIOR; + break; + + case WXK_PAGEDOWN: + extended = true; + case WXK_NUMPAD_PAGEDOWN: + vk = VK_NEXT; + break; + + case WXK_END: + extended = true; + case WXK_NUMPAD_END: + vk = VK_END; + break; + + case WXK_HOME: + extended = true; + case WXK_NUMPAD_HOME: + vk = VK_HOME; + break; + + case WXK_LEFT: + extended = true; + case WXK_NUMPAD_LEFT: + vk = VK_LEFT; + break; + + case WXK_UP: + extended = true; + case WXK_NUMPAD_UP: + vk = VK_UP; + break; + + case WXK_RIGHT: + extended = true; + case WXK_NUMPAD_RIGHT: + vk = VK_RIGHT; + break; + + case WXK_DOWN: + extended = true; + case WXK_NUMPAD_DOWN: + vk = VK_DOWN; + break; + + case WXK_INSERT: + extended = true; + case WXK_NUMPAD_INSERT: + vk = VK_INSERT; break; + + case WXK_DELETE: + extended = true; + case WXK_NUMPAD_DELETE: + vk = VK_DELETE; + break; + + default: + // no VkKeyScan() under CE unfortunately, we need to test how does + // it handle OEM keys +#ifndef __WXWINCE__ + // check to see if its one of the OEM key codes. + BYTE vks = LOBYTE(VkKeyScan(wxk)); + if ( vks != 0xff ) + { + vk = vks; + } + else +#endif // !__WXWINCE__ + { + vk = (WXWORD)wxk; + } + } + + if ( isExtended ) + *isExtended = extended; + + return vk; +} + +} // namespace wxMSWKeyboard + +// small helper for wxGetKeyState() and wxGetMouseState() +static inline bool wxIsKeyDown(WXWORD vk) +{ + // SM_SWAPBUTTON is not available under CE, so don't swap buttons there +#ifdef SM_SWAPBUTTON + if ( vk == VK_LBUTTON || vk == VK_RBUTTON ) + { + if ( ::GetSystemMetrics(SM_SWAPBUTTON) ) + { + if ( vk == VK_LBUTTON ) + vk = VK_RBUTTON; + else // vk == VK_RBUTTON + vk = VK_LBUTTON; } } - return keySym; +#endif // SM_SWAPBUTTON + + // the low order bit indicates whether the key was pressed since the last + // call and the high order one indicates whether it is down right now and + // we only want that one + return (GetAsyncKeyState(vk) & (1<<15)) != 0; } +bool wxGetKeyState(wxKeyCode key) +{ + // although this does work under Windows, it is not supported under other + // platforms so don't allow it, you must use wxGetMouseState() instead + wxASSERT_MSG( key != VK_LBUTTON && + key != VK_RBUTTON && + key != VK_MBUTTON, + wxT("can't use wxGetKeyState() for mouse buttons") ); + + const WXWORD vk = wxMSWKeyboard::WXToVK(key); + + // if the requested key is a LED key, return true if the led is pressed + if ( key == WXK_NUMLOCK || key == WXK_CAPITAL || key == WXK_SCROLL ) + { + // low order bit means LED is highlighted and high order one means the + // key is down; for compatibility with the other ports return true if + // either one is set + return GetKeyState(vk) != 0; + + } + else // normal key + { + return wxIsKeyDown(vk); + } +} + + +wxMouseState wxGetMouseState() +{ + wxMouseState ms; + POINT pt; + wxGetCursorPosMSW(&pt); + + ms.SetX(pt.x); + ms.SetY(pt.y); + ms.SetLeftDown(wxIsKeyDown(VK_LBUTTON)); + ms.SetMiddleDown(wxIsKeyDown(VK_MBUTTON)); + ms.SetRightDown(wxIsKeyDown(VK_RBUTTON)); +#ifdef wxHAS_XBUTTON + ms.SetAux1Down(wxIsKeyDown(VK_XBUTTON1)); + ms.SetAux2Down(wxIsKeyDown(VK_XBUTTON2)); +#endif // wxHAS_XBUTTON + + ms.SetControlDown(wxIsCtrlDown ()); + ms.SetShiftDown (wxIsShiftDown()); + ms.SetAltDown (wxIsAltDown ()); +// ms.SetMetaDown(); + + return ms; +} + + wxWindow *wxGetActiveWindow() { HWND hWnd = GetActiveWindow(); if ( hWnd != 0 ) { - return wxFindWinFromHandle((WXHWND) hWnd); + return wxFindWinFromHandle(hWnd); } return NULL; } @@ -4996,19 +6544,19 @@ extern wxWindow *wxGetWindowFromHWND(WXHWND hWnd) // For a radiobutton, we get the radiobox from GWL_USERDATA (which is set // by code in msw/radiobox.cpp), for all the others we just search up the // window hierarchy - wxWindow *win = (wxWindow *)NULL; + wxWindow *win = NULL; if ( hwnd ) { - win = wxFindWinFromHandle((WXHWND)hwnd); + win = wxFindWinFromHandle(hwnd); if ( !win ) { -#if wxUSE_RADIOBOX +#if wxUSE_RADIOBOX && !defined(__WXUNIVERSAL__) // 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, WM_GETDLGCODE, 0, 0) & DLGC_RADIOBUTTON ) { - win = (wxWindow *)::GetWindowLong(hwnd, GWL_USERDATA); + win = wxRadioBox::GetFromRadioButtonHWND(hwnd); } //else: it's a wxRadioButton, not a radiobutton from wxRadioBox #endif // wxUSE_RADIOBOX @@ -5043,7 +6591,7 @@ extern wxWindow *wxGetWindowFromHWND(WXHWND hWnd) #endif hwnd = ::GetParent(hwnd); - win = wxFindWinFromHandle((WXHWND)hwnd); + win = wxFindWinFromHandle(hwnd); } return win; @@ -5054,63 +6602,61 @@ extern wxWindow *wxGetWindowFromHWND(WXHWND hWnd) // 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; -static FARPROC wxTheKeyboardHookProc = 0; -int APIENTRY _EXPORT -wxKeyboardHook(int nCode, WORD wParam, DWORD lParam); -void wxSetKeyboardHook(bool doIt) -{ - if ( doIt ) - { - wxTheKeyboardHookProc = MakeProcInstance((FARPROC) wxKeyboardHook, wxGetInstance()); - wxTheKeyboardHook = SetWindowsHookEx(WH_KEYBOARD, (HOOKPROC) wxTheKeyboardHookProc, wxGetInstance(), - - GetCurrentThreadId() - // (DWORD)GetCurrentProcess()); // This is another possibility. Which is right? - ); - } - else - { - UnhookWindowsHookEx(wxTheKeyboardHook); - } -} - -int APIENTRY _EXPORT +int APIENTRY wxKeyboardHook(int nCode, WORD wParam, DWORD lParam) { DWORD hiWord = HIWORD(lParam); if ( nCode != HC_NOREMOVE && ((hiWord & KF_UP) == 0) ) { - int id = wxCharCodeMSWToWX(wParam); - if ( id != 0 ) + wchar_t uc; + int id = wxMSWKeyboard::VKToWX(wParam, lParam, &uc); + + // Don't intercept keyboard entry (notably Escape) if a modal window + // (not managed by wx, e.g. IME one) is currently opened as more often + // than not it needs all the keys for itself. + // + // Also don't catch it if a window currently captures the mouse as + // Escape is normally used to release the mouse capture and if you + // really need to catch all the keys in the window that has mouse + // capture it can be easily done in its own EVT_CHAR handler as it is + // certain to have focus while it has the capture. + if ( !gs_modalEntryWindowCount && !::GetCapture() ) { - wxKeyEvent event(wxEVT_CHAR_HOOK); - if ( (HIWORD(lParam) & KF_ALTDOWN) == KF_ALTDOWN ) - event.m_altDown = TRUE; - - event.m_eventObject = NULL; - event.m_keyCode = id; - event.m_shiftDown = wxIsShiftDown(); - event.m_controlDown = wxIsCtrlDown(); - event.SetTimestamp(s_currentMsg.time); - - wxWindow *win = wxGetActiveWindow(); - wxEvtHandler *handler; - if ( win ) - { - handler = win->GetEventHandler(); - event.SetId(win->GetId()); - } - else + if ( id != WXK_NONE +#if wxUSE_UNICODE + || static_cast(uc) != WXK_NONE +#endif // wxUSE_UNICODE + ) { - handler = wxTheApp; - event.SetId(-1); - } + wxWindow const* win = wxWindow::DoFindFocus(); + if ( !win ) + { + // Even if the focus got lost somehow, still send the event + // to the top level parent to allow a wxDialog to always + // close on Escape. + win = wxGetActiveWindow(); + } - if ( handler && handler->ProcessEvent(event) ) - { - // processed - return 1; + wxKeyEvent event(wxEVT_CHAR_HOOK); + MSWInitAnyKeyEvent(event, wParam, lParam, win); + + event.m_keyCode = id; +#if wxUSE_UNICODE + event.m_uniChar = uc; +#endif // wxUSE_UNICODE + + wxEvtHandler * const handler = win ? win->GetEventHandler() + : wxTheApp; + + if ( handler && handler->ProcessEvent(event) ) + { + if ( !event.IsNextEventAllowed() ) + { + // Stop processing of this event. + return 1; + } + } } } } @@ -5118,439 +6664,505 @@ wxKeyboardHook(int nCode, WORD wParam, DWORD lParam) return (int)CallNextHookEx(wxTheKeyboardHook, nCode, wParam, lParam); } +void wxSetKeyboardHook(bool doIt) +{ + if ( doIt ) + { + wxTheKeyboardHook = ::SetWindowsHookEx + ( + WH_KEYBOARD, + (HOOKPROC)wxKeyboardHook, + NULL, // must be NULL for process hook + ::GetCurrentThreadId() + ); + if ( !wxTheKeyboardHook ) + { + wxLogLastError(wxT("SetWindowsHookEx(wxKeyboardHook)")); + } + } + else // uninstall + { + if ( wxTheKeyboardHook ) + ::UnhookWindowsHookEx(wxTheKeyboardHook); + } +} + #endif // !__WXMICROWIN__ -#ifdef __WXDEBUG__ -const char *wxGetMessageName(int message) +#if wxDEBUG_LEVEL >= 2 +const wxChar *wxGetMessageName(int message) { switch ( message ) { - case 0x0000: return "WM_NULL"; - case 0x0001: return "WM_CREATE"; - case 0x0002: return "WM_DESTROY"; - case 0x0003: return "WM_MOVE"; - case 0x0005: return "WM_SIZE"; - case 0x0006: return "WM_ACTIVATE"; - case 0x0007: return "WM_SETFOCUS"; - case 0x0008: return "WM_KILLFOCUS"; - case 0x000A: return "WM_ENABLE"; - case 0x000B: return "WM_SETREDRAW"; - case 0x000C: return "WM_SETTEXT"; - case 0x000D: return "WM_GETTEXT"; - case 0x000E: return "WM_GETTEXTLENGTH"; - case 0x000F: return "WM_PAINT"; - case 0x0010: return "WM_CLOSE"; - case 0x0011: return "WM_QUERYENDSESSION"; - case 0x0012: return "WM_QUIT"; - case 0x0013: return "WM_QUERYOPEN"; - case 0x0014: return "WM_ERASEBKGND"; - case 0x0015: return "WM_SYSCOLORCHANGE"; - case 0x0016: return "WM_ENDSESSION"; - case 0x0017: return "WM_SYSTEMERROR"; - case 0x0018: return "WM_SHOWWINDOW"; - case 0x0019: return "WM_CTLCOLOR"; - case 0x001A: return "WM_WININICHANGE"; - case 0x001B: return "WM_DEVMODECHANGE"; - case 0x001C: return "WM_ACTIVATEAPP"; - case 0x001D: return "WM_FONTCHANGE"; - case 0x001E: return "WM_TIMECHANGE"; - case 0x001F: return "WM_CANCELMODE"; - case 0x0020: return "WM_SETCURSOR"; - case 0x0021: return "WM_MOUSEACTIVATE"; - case 0x0022: return "WM_CHILDACTIVATE"; - case 0x0023: return "WM_QUEUESYNC"; - case 0x0024: return "WM_GETMINMAXINFO"; - case 0x0026: return "WM_PAINTICON"; - case 0x0027: return "WM_ICONERASEBKGND"; - case 0x0028: return "WM_NEXTDLGCTL"; - case 0x002A: return "WM_SPOOLERSTATUS"; - case 0x002B: return "WM_DRAWITEM"; - case 0x002C: return "WM_MEASUREITEM"; - case 0x002D: return "WM_DELETEITEM"; - case 0x002E: return "WM_VKEYTOITEM"; - case 0x002F: return "WM_CHARTOITEM"; - case 0x0030: return "WM_SETFONT"; - case 0x0031: return "WM_GETFONT"; - case 0x0037: return "WM_QUERYDRAGICON"; - case 0x0039: return "WM_COMPAREITEM"; - case 0x0041: return "WM_COMPACTING"; - case 0x0044: return "WM_COMMNOTIFY"; - case 0x0046: return "WM_WINDOWPOSCHANGING"; - case 0x0047: return "WM_WINDOWPOSCHANGED"; - case 0x0048: return "WM_POWER"; - - case 0x004A: return "WM_COPYDATA"; - case 0x004B: return "WM_CANCELJOURNAL"; - case 0x004E: return "WM_NOTIFY"; - case 0x0050: return "WM_INPUTLANGCHANGEREQUEST"; - case 0x0051: return "WM_INPUTLANGCHANGE"; - case 0x0052: return "WM_TCARD"; - case 0x0053: return "WM_HELP"; - case 0x0054: return "WM_USERCHANGED"; - case 0x0055: return "WM_NOTIFYFORMAT"; - case 0x007B: return "WM_CONTEXTMENU"; - case 0x007C: return "WM_STYLECHANGING"; - case 0x007D: return "WM_STYLECHANGED"; - case 0x007E: return "WM_DISPLAYCHANGE"; - case 0x007F: return "WM_GETICON"; - case 0x0080: return "WM_SETICON"; - - case 0x0081: return "WM_NCCREATE"; - case 0x0082: return "WM_NCDESTROY"; - case 0x0083: return "WM_NCCALCSIZE"; - case 0x0084: return "WM_NCHITTEST"; - case 0x0085: return "WM_NCPAINT"; - case 0x0086: return "WM_NCACTIVATE"; - case 0x0087: return "WM_GETDLGCODE"; - case 0x00A0: return "WM_NCMOUSEMOVE"; - case 0x00A1: return "WM_NCLBUTTONDOWN"; - case 0x00A2: return "WM_NCLBUTTONUP"; - case 0x00A3: return "WM_NCLBUTTONDBLCLK"; - case 0x00A4: return "WM_NCRBUTTONDOWN"; - case 0x00A5: return "WM_NCRBUTTONUP"; - case 0x00A6: return "WM_NCRBUTTONDBLCLK"; - case 0x00A7: return "WM_NCMBUTTONDOWN"; - case 0x00A8: return "WM_NCMBUTTONUP"; - case 0x00A9: return "WM_NCMBUTTONDBLCLK"; - case 0x0100: return "WM_KEYDOWN"; - case 0x0101: return "WM_KEYUP"; - case 0x0102: return "WM_CHAR"; - case 0x0103: return "WM_DEADCHAR"; - case 0x0104: return "WM_SYSKEYDOWN"; - case 0x0105: return "WM_SYSKEYUP"; - case 0x0106: return "WM_SYSCHAR"; - case 0x0107: return "WM_SYSDEADCHAR"; - case 0x0108: return "WM_KEYLAST"; - - case 0x010D: return "WM_IME_STARTCOMPOSITION"; - case 0x010E: return "WM_IME_ENDCOMPOSITION"; - case 0x010F: return "WM_IME_COMPOSITION"; - - case 0x0110: return "WM_INITDIALOG"; - case 0x0111: return "WM_COMMAND"; - case 0x0112: return "WM_SYSCOMMAND"; - case 0x0113: return "WM_TIMER"; - case 0x0114: return "WM_HSCROLL"; - case 0x0115: return "WM_VSCROLL"; - case 0x0116: return "WM_INITMENU"; - case 0x0117: return "WM_INITMENUPOPUP"; - case 0x011F: return "WM_MENUSELECT"; - case 0x0120: return "WM_MENUCHAR"; - case 0x0121: return "WM_ENTERIDLE"; - case 0x0200: return "WM_MOUSEMOVE"; - case 0x0201: return "WM_LBUTTONDOWN"; - case 0x0202: return "WM_LBUTTONUP"; - case 0x0203: return "WM_LBUTTONDBLCLK"; - case 0x0204: return "WM_RBUTTONDOWN"; - case 0x0205: return "WM_RBUTTONUP"; - case 0x0206: return "WM_RBUTTONDBLCLK"; - 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"; - - 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"; - - case 0x0220: return "WM_MDICREATE"; - case 0x0221: return "WM_MDIDESTROY"; - case 0x0222: return "WM_MDIACTIVATE"; - case 0x0223: return "WM_MDIRESTORE"; - case 0x0224: return "WM_MDINEXT"; - case 0x0225: return "WM_MDIMAXIMIZE"; - case 0x0226: return "WM_MDITILE"; - case 0x0227: return "WM_MDICASCADE"; - case 0x0228: return "WM_MDIICONARRANGE"; - case 0x0229: return "WM_MDIGETACTIVE"; - case 0x0230: return "WM_MDISETMENU"; - case 0x0233: return "WM_DROPFILES"; - - case 0x0281: return "WM_IME_SETCONTEXT"; - case 0x0282: return "WM_IME_NOTIFY"; - case 0x0283: return "WM_IME_CONTROL"; - case 0x0284: return "WM_IME_COMPOSITIONFULL"; - case 0x0285: return "WM_IME_SELECT"; - case 0x0286: return "WM_IME_CHAR"; - case 0x0290: return "WM_IME_KEYDOWN"; - case 0x0291: return "WM_IME_KEYUP"; - - case 0x0300: return "WM_CUT"; - case 0x0301: return "WM_COPY"; - case 0x0302: return "WM_PASTE"; - case 0x0303: return "WM_CLEAR"; - case 0x0304: return "WM_UNDO"; - case 0x0305: return "WM_RENDERFORMAT"; - case 0x0306: return "WM_RENDERALLFORMATS"; - case 0x0307: return "WM_DESTROYCLIPBOARD"; - case 0x0308: return "WM_DRAWCLIPBOARD"; - case 0x0309: return "WM_PAINTCLIPBOARD"; - case 0x030A: return "WM_VSCROLLCLIPBOARD"; - case 0x030B: return "WM_SIZECLIPBOARD"; - case 0x030C: return "WM_ASKCBFORMATNAME"; - case 0x030D: return "WM_CHANGECBCHAIN"; - case 0x030E: return "WM_HSCROLLCLIPBOARD"; - case 0x030F: return "WM_QUERYNEWPALETTE"; - case 0x0310: return "WM_PALETTEISCHANGING"; - case 0x0311: return "WM_PALETTECHANGED"; -#if wxUSE_HOTKEY - case 0x0312: return "WM_HOTKEY"; -#endif + case 0x0000: return wxT("WM_NULL"); + case 0x0001: return wxT("WM_CREATE"); + case 0x0002: return wxT("WM_DESTROY"); + case 0x0003: return wxT("WM_MOVE"); + case 0x0005: return wxT("WM_SIZE"); + case 0x0006: return wxT("WM_ACTIVATE"); + case 0x0007: return wxT("WM_SETFOCUS"); + case 0x0008: return wxT("WM_KILLFOCUS"); + case 0x000A: return wxT("WM_ENABLE"); + case 0x000B: return wxT("WM_SETREDRAW"); + case 0x000C: return wxT("WM_SETTEXT"); + case 0x000D: return wxT("WM_GETTEXT"); + case 0x000E: return wxT("WM_GETTEXTLENGTH"); + case 0x000F: return wxT("WM_PAINT"); + case 0x0010: return wxT("WM_CLOSE"); + case 0x0011: return wxT("WM_QUERYENDSESSION"); + case 0x0012: return wxT("WM_QUIT"); + case 0x0013: return wxT("WM_QUERYOPEN"); + case 0x0014: return wxT("WM_ERASEBKGND"); + case 0x0015: return wxT("WM_SYSCOLORCHANGE"); + case 0x0016: return wxT("WM_ENDSESSION"); + case 0x0017: return wxT("WM_SYSTEMERROR"); + case 0x0018: return wxT("WM_SHOWWINDOW"); + case 0x0019: return wxT("WM_CTLCOLOR"); + case 0x001A: return wxT("WM_WININICHANGE"); + case 0x001B: return wxT("WM_DEVMODECHANGE"); + case 0x001C: return wxT("WM_ACTIVATEAPP"); + case 0x001D: return wxT("WM_FONTCHANGE"); + case 0x001E: return wxT("WM_TIMECHANGE"); + case 0x001F: return wxT("WM_CANCELMODE"); + case 0x0020: return wxT("WM_SETCURSOR"); + case 0x0021: return wxT("WM_MOUSEACTIVATE"); + case 0x0022: return wxT("WM_CHILDACTIVATE"); + case 0x0023: return wxT("WM_QUEUESYNC"); + case 0x0024: return wxT("WM_GETMINMAXINFO"); + case 0x0026: return wxT("WM_PAINTICON"); + case 0x0027: return wxT("WM_ICONERASEBKGND"); + case 0x0028: return wxT("WM_NEXTDLGCTL"); + case 0x002A: return wxT("WM_SPOOLERSTATUS"); + case 0x002B: return wxT("WM_DRAWITEM"); + case 0x002C: return wxT("WM_MEASUREITEM"); + case 0x002D: return wxT("WM_DELETEITEM"); + case 0x002E: return wxT("WM_VKEYTOITEM"); + case 0x002F: return wxT("WM_CHARTOITEM"); + case 0x0030: return wxT("WM_SETFONT"); + case 0x0031: return wxT("WM_GETFONT"); + case 0x0037: return wxT("WM_QUERYDRAGICON"); + case 0x0039: return wxT("WM_COMPAREITEM"); + case 0x0041: return wxT("WM_COMPACTING"); + case 0x0044: return wxT("WM_COMMNOTIFY"); + case 0x0046: return wxT("WM_WINDOWPOSCHANGING"); + case 0x0047: return wxT("WM_WINDOWPOSCHANGED"); + case 0x0048: return wxT("WM_POWER"); + + case 0x004A: return wxT("WM_COPYDATA"); + case 0x004B: return wxT("WM_CANCELJOURNAL"); + case 0x004E: return wxT("WM_NOTIFY"); + case 0x0050: return wxT("WM_INPUTLANGCHANGEREQUEST"); + case 0x0051: return wxT("WM_INPUTLANGCHANGE"); + case 0x0052: return wxT("WM_TCARD"); + case 0x0053: return wxT("WM_HELP"); + case 0x0054: return wxT("WM_USERCHANGED"); + case 0x0055: return wxT("WM_NOTIFYFORMAT"); + case 0x007B: return wxT("WM_CONTEXTMENU"); + case 0x007C: return wxT("WM_STYLECHANGING"); + case 0x007D: return wxT("WM_STYLECHANGED"); + case 0x007E: return wxT("WM_DISPLAYCHANGE"); + case 0x007F: return wxT("WM_GETICON"); + case 0x0080: return wxT("WM_SETICON"); + + case 0x0081: return wxT("WM_NCCREATE"); + case 0x0082: return wxT("WM_NCDESTROY"); + case 0x0083: return wxT("WM_NCCALCSIZE"); + case 0x0084: return wxT("WM_NCHITTEST"); + case 0x0085: return wxT("WM_NCPAINT"); + case 0x0086: return wxT("WM_NCACTIVATE"); + case 0x0087: return wxT("WM_GETDLGCODE"); + case 0x00A0: return wxT("WM_NCMOUSEMOVE"); + case 0x00A1: return wxT("WM_NCLBUTTONDOWN"); + case 0x00A2: return wxT("WM_NCLBUTTONUP"); + case 0x00A3: return wxT("WM_NCLBUTTONDBLCLK"); + case 0x00A4: return wxT("WM_NCRBUTTONDOWN"); + case 0x00A5: return wxT("WM_NCRBUTTONUP"); + case 0x00A6: return wxT("WM_NCRBUTTONDBLCLK"); + case 0x00A7: return wxT("WM_NCMBUTTONDOWN"); + case 0x00A8: return wxT("WM_NCMBUTTONUP"); + case 0x00A9: return wxT("WM_NCMBUTTONDBLCLK"); + + case 0x00B0: return wxT("EM_GETSEL"); + case 0x00B1: return wxT("EM_SETSEL"); + case 0x00B2: return wxT("EM_GETRECT"); + case 0x00B3: return wxT("EM_SETRECT"); + case 0x00B4: return wxT("EM_SETRECTNP"); + case 0x00B5: return wxT("EM_SCROLL"); + case 0x00B6: return wxT("EM_LINESCROLL"); + case 0x00B7: return wxT("EM_SCROLLCARET"); + case 0x00B8: return wxT("EM_GETMODIFY"); + case 0x00B9: return wxT("EM_SETMODIFY"); + case 0x00BA: return wxT("EM_GETLINECOUNT"); + case 0x00BB: return wxT("EM_LINEINDEX"); + case 0x00BC: return wxT("EM_SETHANDLE"); + case 0x00BD: return wxT("EM_GETHANDLE"); + case 0x00BE: return wxT("EM_GETTHUMB"); + case 0x00C1: return wxT("EM_LINELENGTH"); + case 0x00C2: return wxT("EM_REPLACESEL"); + case 0x00C4: return wxT("EM_GETLINE"); + case 0x00C5: return wxT("EM_LIMITTEXT/EM_SETLIMITTEXT"); /* ;win40 Name change */ + case 0x00C6: return wxT("EM_CANUNDO"); + case 0x00C7: return wxT("EM_UNDO"); + case 0x00C8: return wxT("EM_FMTLINES"); + case 0x00C9: return wxT("EM_LINEFROMCHAR"); + case 0x00CB: return wxT("EM_SETTABSTOPS"); + case 0x00CC: return wxT("EM_SETPASSWORDCHAR"); + case 0x00CD: return wxT("EM_EMPTYUNDOBUFFER"); + case 0x00CE: return wxT("EM_GETFIRSTVISIBLELINE"); + case 0x00CF: return wxT("EM_SETREADONLY"); + case 0x00D0: return wxT("EM_SETWORDBREAKPROC"); + case 0x00D1: return wxT("EM_GETWORDBREAKPROC"); + case 0x00D2: return wxT("EM_GETPASSWORDCHAR"); + case 0x00D3: return wxT("EM_SETMARGINS"); + case 0x00D4: return wxT("EM_GETMARGINS"); + case 0x00D5: return wxT("EM_GETLIMITTEXT"); + case 0x00D6: return wxT("EM_POSFROMCHAR"); + case 0x00D7: return wxT("EM_CHARFROMPOS"); + case 0x00D8: return wxT("EM_SETIMESTATUS"); + case 0x00D9: return wxT("EM_GETIMESTATUS"); + + case 0x0100: return wxT("WM_KEYDOWN"); + case 0x0101: return wxT("WM_KEYUP"); + case 0x0102: return wxT("WM_CHAR"); + case 0x0103: return wxT("WM_DEADCHAR"); + case 0x0104: return wxT("WM_SYSKEYDOWN"); + case 0x0105: return wxT("WM_SYSKEYUP"); + case 0x0106: return wxT("WM_SYSCHAR"); + case 0x0107: return wxT("WM_SYSDEADCHAR"); + case 0x0108: return wxT("WM_KEYLAST"); + + case 0x010D: return wxT("WM_IME_STARTCOMPOSITION"); + case 0x010E: return wxT("WM_IME_ENDCOMPOSITION"); + case 0x010F: return wxT("WM_IME_COMPOSITION"); + + case 0x0110: return wxT("WM_INITDIALOG"); + case 0x0111: return wxT("WM_COMMAND"); + case 0x0112: return wxT("WM_SYSCOMMAND"); + case 0x0113: return wxT("WM_TIMER"); + case 0x0114: return wxT("WM_HSCROLL"); + case 0x0115: return wxT("WM_VSCROLL"); + case 0x0116: return wxT("WM_INITMENU"); + case 0x0117: return wxT("WM_INITMENUPOPUP"); + case 0x011F: return wxT("WM_MENUSELECT"); + case 0x0120: return wxT("WM_MENUCHAR"); + case 0x0121: return wxT("WM_ENTERIDLE"); + + case 0x0127: return wxT("WM_CHANGEUISTATE"); + case 0x0128: return wxT("WM_UPDATEUISTATE"); + case 0x0129: return wxT("WM_QUERYUISTATE"); + + case 0x0132: return wxT("WM_CTLCOLORMSGBOX"); + case 0x0133: return wxT("WM_CTLCOLOREDIT"); + case 0x0134: return wxT("WM_CTLCOLORLISTBOX"); + case 0x0135: return wxT("WM_CTLCOLORBTN"); + case 0x0136: return wxT("WM_CTLCOLORDLG"); + case 0x0137: return wxT("WM_CTLCOLORSCROLLBAR"); + case 0x0138: return wxT("WM_CTLCOLORSTATIC"); + case 0x01E1: return wxT("MN_GETHMENU"); + + case 0x0200: return wxT("WM_MOUSEMOVE"); + case 0x0201: return wxT("WM_LBUTTONDOWN"); + case 0x0202: return wxT("WM_LBUTTONUP"); + case 0x0203: return wxT("WM_LBUTTONDBLCLK"); + case 0x0204: return wxT("WM_RBUTTONDOWN"); + case 0x0205: return wxT("WM_RBUTTONUP"); + case 0x0206: return wxT("WM_RBUTTONDBLCLK"); + case 0x0207: return wxT("WM_MBUTTONDOWN"); + case 0x0208: return wxT("WM_MBUTTONUP"); + case 0x0209: return wxT("WM_MBUTTONDBLCLK"); + case 0x020A: return wxT("WM_MOUSEWHEEL"); + case 0x020B: return wxT("WM_XBUTTONDOWN"); + case 0x020C: return wxT("WM_XBUTTONUP"); + case 0x020D: return wxT("WM_XBUTTONDBLCLK"); + case 0x0210: return wxT("WM_PARENTNOTIFY"); + case 0x0211: return wxT("WM_ENTERMENULOOP"); + case 0x0212: return wxT("WM_EXITMENULOOP"); + + case 0x0213: return wxT("WM_NEXTMENU"); + case 0x0214: return wxT("WM_SIZING"); + case 0x0215: return wxT("WM_CAPTURECHANGED"); + case 0x0216: return wxT("WM_MOVING"); + case 0x0218: return wxT("WM_POWERBROADCAST"); + case 0x0219: return wxT("WM_DEVICECHANGE"); + + case 0x0220: return wxT("WM_MDICREATE"); + case 0x0221: return wxT("WM_MDIDESTROY"); + case 0x0222: return wxT("WM_MDIACTIVATE"); + case 0x0223: return wxT("WM_MDIRESTORE"); + case 0x0224: return wxT("WM_MDINEXT"); + case 0x0225: return wxT("WM_MDIMAXIMIZE"); + case 0x0226: return wxT("WM_MDITILE"); + case 0x0227: return wxT("WM_MDICASCADE"); + case 0x0228: return wxT("WM_MDIICONARRANGE"); + case 0x0229: return wxT("WM_MDIGETACTIVE"); + case 0x0230: return wxT("WM_MDISETMENU"); + case 0x0233: return wxT("WM_DROPFILES"); + + case 0x0281: return wxT("WM_IME_SETCONTEXT"); + case 0x0282: return wxT("WM_IME_NOTIFY"); + case 0x0283: return wxT("WM_IME_CONTROL"); + case 0x0284: return wxT("WM_IME_COMPOSITIONFULL"); + case 0x0285: return wxT("WM_IME_SELECT"); + case 0x0286: return wxT("WM_IME_CHAR"); + case 0x0290: return wxT("WM_IME_KEYDOWN"); + case 0x0291: return wxT("WM_IME_KEYUP"); + + case 0x02A0: return wxT("WM_NCMOUSEHOVER"); + case 0x02A1: return wxT("WM_MOUSEHOVER"); + case 0x02A2: return wxT("WM_NCMOUSELEAVE"); + case 0x02A3: return wxT("WM_MOUSELEAVE"); + + case 0x0300: return wxT("WM_CUT"); + case 0x0301: return wxT("WM_COPY"); + case 0x0302: return wxT("WM_PASTE"); + case 0x0303: return wxT("WM_CLEAR"); + case 0x0304: return wxT("WM_UNDO"); + case 0x0305: return wxT("WM_RENDERFORMAT"); + case 0x0306: return wxT("WM_RENDERALLFORMATS"); + case 0x0307: return wxT("WM_DESTROYCLIPBOARD"); + case 0x0308: return wxT("WM_DRAWCLIPBOARD"); + case 0x0309: return wxT("WM_PAINTCLIPBOARD"); + case 0x030A: return wxT("WM_VSCROLLCLIPBOARD"); + case 0x030B: return wxT("WM_SIZECLIPBOARD"); + case 0x030C: return wxT("WM_ASKCBFORMATNAME"); + case 0x030D: return wxT("WM_CHANGECBCHAIN"); + case 0x030E: return wxT("WM_HSCROLLCLIPBOARD"); + case 0x030F: return wxT("WM_QUERYNEWPALETTE"); + case 0x0310: return wxT("WM_PALETTEISCHANGING"); + case 0x0311: return wxT("WM_PALETTECHANGED"); + case 0x0312: return wxT("WM_HOTKEY"); + + case 0x0317: return wxT("WM_PRINT"); + case 0x0318: return wxT("WM_PRINTCLIENT"); // common controls messages - although they're not strictly speaking // standard, it's nice to decode them nevertheless // listview - case 0x1000 + 0: return "LVM_GETBKCOLOR"; - case 0x1000 + 1: return "LVM_SETBKCOLOR"; - case 0x1000 + 2: return "LVM_GETIMAGELIST"; - case 0x1000 + 3: return "LVM_SETIMAGELIST"; - case 0x1000 + 4: return "LVM_GETITEMCOUNT"; - case 0x1000 + 5: return "LVM_GETITEMA"; - case 0x1000 + 75: return "LVM_GETITEMW"; - case 0x1000 + 6: return "LVM_SETITEMA"; - case 0x1000 + 76: return "LVM_SETITEMW"; - case 0x1000 + 7: return "LVM_INSERTITEMA"; - case 0x1000 + 77: return "LVM_INSERTITEMW"; - case 0x1000 + 8: return "LVM_DELETEITEM"; - case 0x1000 + 9: return "LVM_DELETEALLITEMS"; - case 0x1000 + 10: return "LVM_GETCALLBACKMASK"; - case 0x1000 + 11: return "LVM_SETCALLBACKMASK"; - case 0x1000 + 12: return "LVM_GETNEXTITEM"; - case 0x1000 + 13: return "LVM_FINDITEMA"; - case 0x1000 + 83: return "LVM_FINDITEMW"; - case 0x1000 + 14: return "LVM_GETITEMRECT"; - case 0x1000 + 15: return "LVM_SETITEMPOSITION"; - case 0x1000 + 16: return "LVM_GETITEMPOSITION"; - case 0x1000 + 17: return "LVM_GETSTRINGWIDTHA"; - case 0x1000 + 87: return "LVM_GETSTRINGWIDTHW"; - case 0x1000 + 18: return "LVM_HITTEST"; - case 0x1000 + 19: return "LVM_ENSUREVISIBLE"; - case 0x1000 + 20: return "LVM_SCROLL"; - case 0x1000 + 21: return "LVM_REDRAWITEMS"; - case 0x1000 + 22: return "LVM_ARRANGE"; - case 0x1000 + 23: return "LVM_EDITLABELA"; - case 0x1000 + 118: return "LVM_EDITLABELW"; - case 0x1000 + 24: return "LVM_GETEDITCONTROL"; - case 0x1000 + 25: return "LVM_GETCOLUMNA"; - case 0x1000 + 95: return "LVM_GETCOLUMNW"; - case 0x1000 + 26: return "LVM_SETCOLUMNA"; - case 0x1000 + 96: return "LVM_SETCOLUMNW"; - case 0x1000 + 27: return "LVM_INSERTCOLUMNA"; - case 0x1000 + 97: return "LVM_INSERTCOLUMNW"; - case 0x1000 + 28: return "LVM_DELETECOLUMN"; - case 0x1000 + 29: return "LVM_GETCOLUMNWIDTH"; - case 0x1000 + 30: return "LVM_SETCOLUMNWIDTH"; - case 0x1000 + 31: return "LVM_GETHEADER"; - case 0x1000 + 33: return "LVM_CREATEDRAGIMAGE"; - case 0x1000 + 34: return "LVM_GETVIEWRECT"; - case 0x1000 + 35: return "LVM_GETTEXTCOLOR"; - case 0x1000 + 36: return "LVM_SETTEXTCOLOR"; - case 0x1000 + 37: return "LVM_GETTEXTBKCOLOR"; - case 0x1000 + 38: return "LVM_SETTEXTBKCOLOR"; - case 0x1000 + 39: return "LVM_GETTOPINDEX"; - case 0x1000 + 40: return "LVM_GETCOUNTPERPAGE"; - case 0x1000 + 41: return "LVM_GETORIGIN"; - case 0x1000 + 42: return "LVM_UPDATE"; - case 0x1000 + 43: return "LVM_SETITEMSTATE"; - case 0x1000 + 44: return "LVM_GETITEMSTATE"; - case 0x1000 + 45: return "LVM_GETITEMTEXTA"; - case 0x1000 + 115: return "LVM_GETITEMTEXTW"; - case 0x1000 + 46: return "LVM_SETITEMTEXTA"; - case 0x1000 + 116: return "LVM_SETITEMTEXTW"; - case 0x1000 + 47: return "LVM_SETITEMCOUNT"; - case 0x1000 + 48: return "LVM_SORTITEMS"; - case 0x1000 + 49: return "LVM_SETITEMPOSITION32"; - case 0x1000 + 50: return "LVM_GETSELECTEDCOUNT"; - case 0x1000 + 51: return "LVM_GETITEMSPACING"; - case 0x1000 + 52: return "LVM_GETISEARCHSTRINGA"; - case 0x1000 + 117: return "LVM_GETISEARCHSTRINGW"; - case 0x1000 + 53: return "LVM_SETICONSPACING"; - case 0x1000 + 54: return "LVM_SETEXTENDEDLISTVIEWSTYLE"; - case 0x1000 + 55: return "LVM_GETEXTENDEDLISTVIEWSTYLE"; - case 0x1000 + 56: return "LVM_GETSUBITEMRECT"; - case 0x1000 + 57: return "LVM_SUBITEMHITTEST"; - case 0x1000 + 58: return "LVM_SETCOLUMNORDERARRAY"; - case 0x1000 + 59: return "LVM_GETCOLUMNORDERARRAY"; - case 0x1000 + 60: return "LVM_SETHOTITEM"; - case 0x1000 + 61: return "LVM_GETHOTITEM"; - case 0x1000 + 62: return "LVM_SETHOTCURSOR"; - case 0x1000 + 63: return "LVM_GETHOTCURSOR"; - case 0x1000 + 64: return "LVM_APPROXIMATEVIEWRECT"; - case 0x1000 + 65: return "LVM_SETWORKAREA"; + case 0x1000 + 0: return wxT("LVM_GETBKCOLOR"); + case 0x1000 + 1: return wxT("LVM_SETBKCOLOR"); + case 0x1000 + 2: return wxT("LVM_GETIMAGELIST"); + case 0x1000 + 3: return wxT("LVM_SETIMAGELIST"); + case 0x1000 + 4: return wxT("LVM_GETITEMCOUNT"); + case 0x1000 + 5: return wxT("LVM_GETITEMA"); + case 0x1000 + 75: return wxT("LVM_GETITEMW"); + case 0x1000 + 6: return wxT("LVM_SETITEMA"); + case 0x1000 + 76: return wxT("LVM_SETITEMW"); + case 0x1000 + 7: return wxT("LVM_INSERTITEMA"); + case 0x1000 + 77: return wxT("LVM_INSERTITEMW"); + case 0x1000 + 8: return wxT("LVM_DELETEITEM"); + case 0x1000 + 9: return wxT("LVM_DELETEALLITEMS"); + case 0x1000 + 10: return wxT("LVM_GETCALLBACKMASK"); + case 0x1000 + 11: return wxT("LVM_SETCALLBACKMASK"); + case 0x1000 + 12: return wxT("LVM_GETNEXTITEM"); + case 0x1000 + 13: return wxT("LVM_FINDITEMA"); + case 0x1000 + 83: return wxT("LVM_FINDITEMW"); + case 0x1000 + 14: return wxT("LVM_GETITEMRECT"); + case 0x1000 + 15: return wxT("LVM_SETITEMPOSITION"); + case 0x1000 + 16: return wxT("LVM_GETITEMPOSITION"); + case 0x1000 + 17: return wxT("LVM_GETSTRINGWIDTHA"); + case 0x1000 + 87: return wxT("LVM_GETSTRINGWIDTHW"); + case 0x1000 + 18: return wxT("LVM_HITTEST"); + case 0x1000 + 19: return wxT("LVM_ENSUREVISIBLE"); + case 0x1000 + 20: return wxT("LVM_SCROLL"); + case 0x1000 + 21: return wxT("LVM_REDRAWITEMS"); + case 0x1000 + 22: return wxT("LVM_ARRANGE"); + case 0x1000 + 23: return wxT("LVM_EDITLABELA"); + case 0x1000 + 118: return wxT("LVM_EDITLABELW"); + case 0x1000 + 24: return wxT("LVM_GETEDITCONTROL"); + case 0x1000 + 25: return wxT("LVM_GETCOLUMNA"); + case 0x1000 + 95: return wxT("LVM_GETCOLUMNW"); + case 0x1000 + 26: return wxT("LVM_SETCOLUMNA"); + case 0x1000 + 96: return wxT("LVM_SETCOLUMNW"); + case 0x1000 + 27: return wxT("LVM_INSERTCOLUMNA"); + case 0x1000 + 97: return wxT("LVM_INSERTCOLUMNW"); + case 0x1000 + 28: return wxT("LVM_DELETECOLUMN"); + case 0x1000 + 29: return wxT("LVM_GETCOLUMNWIDTH"); + case 0x1000 + 30: return wxT("LVM_SETCOLUMNWIDTH"); + case 0x1000 + 31: return wxT("LVM_GETHEADER"); + case 0x1000 + 33: return wxT("LVM_CREATEDRAGIMAGE"); + case 0x1000 + 34: return wxT("LVM_GETVIEWRECT"); + case 0x1000 + 35: return wxT("LVM_GETTEXTCOLOR"); + case 0x1000 + 36: return wxT("LVM_SETTEXTCOLOR"); + case 0x1000 + 37: return wxT("LVM_GETTEXTBKCOLOR"); + case 0x1000 + 38: return wxT("LVM_SETTEXTBKCOLOR"); + case 0x1000 + 39: return wxT("LVM_GETTOPINDEX"); + case 0x1000 + 40: return wxT("LVM_GETCOUNTPERPAGE"); + case 0x1000 + 41: return wxT("LVM_GETORIGIN"); + case 0x1000 + 42: return wxT("LVM_UPDATE"); + case 0x1000 + 43: return wxT("LVM_SETITEMSTATE"); + case 0x1000 + 44: return wxT("LVM_GETITEMSTATE"); + case 0x1000 + 45: return wxT("LVM_GETITEMTEXTA"); + case 0x1000 + 115: return wxT("LVM_GETITEMTEXTW"); + case 0x1000 + 46: return wxT("LVM_SETITEMTEXTA"); + case 0x1000 + 116: return wxT("LVM_SETITEMTEXTW"); + case 0x1000 + 47: return wxT("LVM_SETITEMCOUNT"); + case 0x1000 + 48: return wxT("LVM_SORTITEMS"); + case 0x1000 + 49: return wxT("LVM_SETITEMPOSITION32"); + case 0x1000 + 50: return wxT("LVM_GETSELECTEDCOUNT"); + case 0x1000 + 51: return wxT("LVM_GETITEMSPACING"); + case 0x1000 + 52: return wxT("LVM_GETISEARCHSTRINGA"); + case 0x1000 + 117: return wxT("LVM_GETISEARCHSTRINGW"); + case 0x1000 + 53: return wxT("LVM_SETICONSPACING"); + case 0x1000 + 54: return wxT("LVM_SETEXTENDEDLISTVIEWSTYLE"); + case 0x1000 + 55: return wxT("LVM_GETEXTENDEDLISTVIEWSTYLE"); + case 0x1000 + 56: return wxT("LVM_GETSUBITEMRECT"); + case 0x1000 + 57: return wxT("LVM_SUBITEMHITTEST"); + case 0x1000 + 58: return wxT("LVM_SETCOLUMNORDERARRAY"); + case 0x1000 + 59: return wxT("LVM_GETCOLUMNORDERARRAY"); + case 0x1000 + 60: return wxT("LVM_SETHOTITEM"); + case 0x1000 + 61: return wxT("LVM_GETHOTITEM"); + case 0x1000 + 62: return wxT("LVM_SETHOTCURSOR"); + case 0x1000 + 63: return wxT("LVM_GETHOTCURSOR"); + case 0x1000 + 64: return wxT("LVM_APPROXIMATEVIEWRECT"); + case 0x1000 + 65: return wxT("LVM_SETWORKAREA"); // tree view - case 0x1100 + 0: return "TVM_INSERTITEMA"; - case 0x1100 + 50: return "TVM_INSERTITEMW"; - case 0x1100 + 1: return "TVM_DELETEITEM"; - case 0x1100 + 2: return "TVM_EXPAND"; - case 0x1100 + 4: return "TVM_GETITEMRECT"; - case 0x1100 + 5: return "TVM_GETCOUNT"; - case 0x1100 + 6: return "TVM_GETINDENT"; - case 0x1100 + 7: return "TVM_SETINDENT"; - case 0x1100 + 8: return "TVM_GETIMAGELIST"; - case 0x1100 + 9: return "TVM_SETIMAGELIST"; - case 0x1100 + 10: return "TVM_GETNEXTITEM"; - case 0x1100 + 11: return "TVM_SELECTITEM"; - case 0x1100 + 12: return "TVM_GETITEMA"; - case 0x1100 + 62: return "TVM_GETITEMW"; - case 0x1100 + 13: return "TVM_SETITEMA"; - case 0x1100 + 63: return "TVM_SETITEMW"; - case 0x1100 + 14: return "TVM_EDITLABELA"; - case 0x1100 + 65: return "TVM_EDITLABELW"; - case 0x1100 + 15: return "TVM_GETEDITCONTROL"; - case 0x1100 + 16: return "TVM_GETVISIBLECOUNT"; - case 0x1100 + 17: return "TVM_HITTEST"; - case 0x1100 + 18: return "TVM_CREATEDRAGIMAGE"; - case 0x1100 + 19: return "TVM_SORTCHILDREN"; - case 0x1100 + 20: return "TVM_ENSUREVISIBLE"; - case 0x1100 + 21: return "TVM_SORTCHILDRENCB"; - case 0x1100 + 22: return "TVM_ENDEDITLABELNOW"; - case 0x1100 + 23: return "TVM_GETISEARCHSTRINGA"; - case 0x1100 + 64: return "TVM_GETISEARCHSTRINGW"; - case 0x1100 + 24: return "TVM_SETTOOLTIPS"; - case 0x1100 + 25: return "TVM_GETTOOLTIPS"; + case 0x1100 + 0: return wxT("TVM_INSERTITEMA"); + case 0x1100 + 50: return wxT("TVM_INSERTITEMW"); + case 0x1100 + 1: return wxT("TVM_DELETEITEM"); + case 0x1100 + 2: return wxT("TVM_EXPAND"); + case 0x1100 + 4: return wxT("TVM_GETITEMRECT"); + case 0x1100 + 5: return wxT("TVM_GETCOUNT"); + case 0x1100 + 6: return wxT("TVM_GETINDENT"); + case 0x1100 + 7: return wxT("TVM_SETINDENT"); + case 0x1100 + 8: return wxT("TVM_GETIMAGELIST"); + case 0x1100 + 9: return wxT("TVM_SETIMAGELIST"); + case 0x1100 + 10: return wxT("TVM_GETNEXTITEM"); + case 0x1100 + 11: return wxT("TVM_SELECTITEM"); + case 0x1100 + 12: return wxT("TVM_GETITEMA"); + case 0x1100 + 62: return wxT("TVM_GETITEMW"); + case 0x1100 + 13: return wxT("TVM_SETITEMA"); + case 0x1100 + 63: return wxT("TVM_SETITEMW"); + case 0x1100 + 14: return wxT("TVM_EDITLABELA"); + case 0x1100 + 65: return wxT("TVM_EDITLABELW"); + case 0x1100 + 15: return wxT("TVM_GETEDITCONTROL"); + case 0x1100 + 16: return wxT("TVM_GETVISIBLECOUNT"); + case 0x1100 + 17: return wxT("TVM_HITTEST"); + case 0x1100 + 18: return wxT("TVM_CREATEDRAGIMAGE"); + case 0x1100 + 19: return wxT("TVM_SORTCHILDREN"); + case 0x1100 + 20: return wxT("TVM_ENSUREVISIBLE"); + case 0x1100 + 21: return wxT("TVM_SORTCHILDRENCB"); + case 0x1100 + 22: return wxT("TVM_ENDEDITLABELNOW"); + case 0x1100 + 23: return wxT("TVM_GETISEARCHSTRINGA"); + case 0x1100 + 64: return wxT("TVM_GETISEARCHSTRINGW"); + case 0x1100 + 24: return wxT("TVM_SETTOOLTIPS"); + case 0x1100 + 25: return wxT("TVM_GETTOOLTIPS"); // header - case 0x1200 + 0: return "HDM_GETITEMCOUNT"; - case 0x1200 + 1: return "HDM_INSERTITEMA"; - case 0x1200 + 10: return "HDM_INSERTITEMW"; - case 0x1200 + 2: return "HDM_DELETEITEM"; - case 0x1200 + 3: return "HDM_GETITEMA"; - case 0x1200 + 11: return "HDM_GETITEMW"; - case 0x1200 + 4: return "HDM_SETITEMA"; - case 0x1200 + 12: return "HDM_SETITEMW"; - case 0x1200 + 5: return "HDM_LAYOUT"; - case 0x1200 + 6: return "HDM_HITTEST"; - case 0x1200 + 7: return "HDM_GETITEMRECT"; - case 0x1200 + 8: return "HDM_SETIMAGELIST"; - case 0x1200 + 9: return "HDM_GETIMAGELIST"; - case 0x1200 + 15: return "HDM_ORDERTOINDEX"; - case 0x1200 + 16: return "HDM_CREATEDRAGIMAGE"; - case 0x1200 + 17: return "HDM_GETORDERARRAY"; - case 0x1200 + 18: return "HDM_SETORDERARRAY"; - case 0x1200 + 19: return "HDM_SETHOTDIVIDER"; + case 0x1200 + 0: return wxT("HDM_GETITEMCOUNT"); + case 0x1200 + 1: return wxT("HDM_INSERTITEMA"); + case 0x1200 + 10: return wxT("HDM_INSERTITEMW"); + case 0x1200 + 2: return wxT("HDM_DELETEITEM"); + case 0x1200 + 3: return wxT("HDM_GETITEMA"); + case 0x1200 + 11: return wxT("HDM_GETITEMW"); + case 0x1200 + 4: return wxT("HDM_SETITEMA"); + case 0x1200 + 12: return wxT("HDM_SETITEMW"); + case 0x1200 + 5: return wxT("HDM_LAYOUT"); + case 0x1200 + 6: return wxT("HDM_HITTEST"); + case 0x1200 + 7: return wxT("HDM_GETITEMRECT"); + case 0x1200 + 8: return wxT("HDM_SETIMAGELIST"); + case 0x1200 + 9: return wxT("HDM_GETIMAGELIST"); + case 0x1200 + 15: return wxT("HDM_ORDERTOINDEX"); + case 0x1200 + 16: return wxT("HDM_CREATEDRAGIMAGE"); + case 0x1200 + 17: return wxT("HDM_GETORDERARRAY"); + case 0x1200 + 18: return wxT("HDM_SETORDERARRAY"); + case 0x1200 + 19: return wxT("HDM_SETHOTDIVIDER"); // tab control - case 0x1300 + 2: return "TCM_GETIMAGELIST"; - case 0x1300 + 3: return "TCM_SETIMAGELIST"; - case 0x1300 + 4: return "TCM_GETITEMCOUNT"; - case 0x1300 + 5: return "TCM_GETITEMA"; - case 0x1300 + 60: return "TCM_GETITEMW"; - case 0x1300 + 6: return "TCM_SETITEMA"; - case 0x1300 + 61: return "TCM_SETITEMW"; - case 0x1300 + 7: return "TCM_INSERTITEMA"; - case 0x1300 + 62: return "TCM_INSERTITEMW"; - case 0x1300 + 8: return "TCM_DELETEITEM"; - case 0x1300 + 9: return "TCM_DELETEALLITEMS"; - case 0x1300 + 10: return "TCM_GETITEMRECT"; - case 0x1300 + 11: return "TCM_GETCURSEL"; - case 0x1300 + 12: return "TCM_SETCURSEL"; - case 0x1300 + 13: return "TCM_HITTEST"; - case 0x1300 + 14: return "TCM_SETITEMEXTRA"; - case 0x1300 + 40: return "TCM_ADJUSTRECT"; - case 0x1300 + 41: return "TCM_SETITEMSIZE"; - case 0x1300 + 42: return "TCM_REMOVEIMAGE"; - case 0x1300 + 43: return "TCM_SETPADDING"; - case 0x1300 + 44: return "TCM_GETROWCOUNT"; - case 0x1300 + 45: return "TCM_GETTOOLTIPS"; - case 0x1300 + 46: return "TCM_SETTOOLTIPS"; - case 0x1300 + 47: return "TCM_GETCURFOCUS"; - case 0x1300 + 48: return "TCM_SETCURFOCUS"; - case 0x1300 + 49: return "TCM_SETMINTABWIDTH"; - case 0x1300 + 50: return "TCM_DESELECTALL"; + case 0x1300 + 2: return wxT("TCM_GETIMAGELIST"); + case 0x1300 + 3: return wxT("TCM_SETIMAGELIST"); + case 0x1300 + 4: return wxT("TCM_GETITEMCOUNT"); + case 0x1300 + 5: return wxT("TCM_GETITEMA"); + case 0x1300 + 60: return wxT("TCM_GETITEMW"); + case 0x1300 + 6: return wxT("TCM_SETITEMA"); + case 0x1300 + 61: return wxT("TCM_SETITEMW"); + case 0x1300 + 7: return wxT("TCM_INSERTITEMA"); + case 0x1300 + 62: return wxT("TCM_INSERTITEMW"); + case 0x1300 + 8: return wxT("TCM_DELETEITEM"); + case 0x1300 + 9: return wxT("TCM_DELETEALLITEMS"); + case 0x1300 + 10: return wxT("TCM_GETITEMRECT"); + case 0x1300 + 11: return wxT("TCM_GETCURSEL"); + case 0x1300 + 12: return wxT("TCM_SETCURSEL"); + case 0x1300 + 13: return wxT("TCM_HITTEST"); + case 0x1300 + 14: return wxT("TCM_SETITEMEXTRA"); + case 0x1300 + 40: return wxT("TCM_ADJUSTRECT"); + case 0x1300 + 41: return wxT("TCM_SETITEMSIZE"); + case 0x1300 + 42: return wxT("TCM_REMOVEIMAGE"); + case 0x1300 + 43: return wxT("TCM_SETPADDING"); + case 0x1300 + 44: return wxT("TCM_GETROWCOUNT"); + case 0x1300 + 45: return wxT("TCM_GETTOOLTIPS"); + case 0x1300 + 46: return wxT("TCM_SETTOOLTIPS"); + case 0x1300 + 47: return wxT("TCM_GETCURFOCUS"); + case 0x1300 + 48: return wxT("TCM_SETCURFOCUS"); + case 0x1300 + 49: return wxT("TCM_SETMINTABWIDTH"); + case 0x1300 + 50: return wxT("TCM_DESELECTALL"); // toolbar - case WM_USER+1: return "TB_ENABLEBUTTON"; - case WM_USER+2: return "TB_CHECKBUTTON"; - case WM_USER+3: return "TB_PRESSBUTTON"; - case WM_USER+4: return "TB_HIDEBUTTON"; - case WM_USER+5: return "TB_INDETERMINATE"; - case WM_USER+9: return "TB_ISBUTTONENABLED"; - case WM_USER+10: return "TB_ISBUTTONCHECKED"; - case WM_USER+11: return "TB_ISBUTTONPRESSED"; - case WM_USER+12: return "TB_ISBUTTONHIDDEN"; - case WM_USER+13: return "TB_ISBUTTONINDETERMINATE"; - case WM_USER+17: return "TB_SETSTATE"; - case WM_USER+18: return "TB_GETSTATE"; - case WM_USER+19: return "TB_ADDBITMAP"; - case WM_USER+20: return "TB_ADDBUTTONS"; - case WM_USER+21: return "TB_INSERTBUTTON"; - case WM_USER+22: return "TB_DELETEBUTTON"; - case WM_USER+23: return "TB_GETBUTTON"; - case WM_USER+24: return "TB_BUTTONCOUNT"; - case WM_USER+25: return "TB_COMMANDTOINDEX"; - case WM_USER+26: return "TB_SAVERESTOREA"; - case WM_USER+76: return "TB_SAVERESTOREW"; - case WM_USER+27: return "TB_CUSTOMIZE"; - case WM_USER+28: return "TB_ADDSTRINGA"; - case WM_USER+77: return "TB_ADDSTRINGW"; - case WM_USER+29: return "TB_GETITEMRECT"; - case WM_USER+30: return "TB_BUTTONSTRUCTSIZE"; - case WM_USER+31: return "TB_SETBUTTONSIZE"; - case WM_USER+32: return "TB_SETBITMAPSIZE"; - case WM_USER+33: return "TB_AUTOSIZE"; - case WM_USER+35: return "TB_GETTOOLTIPS"; - case WM_USER+36: return "TB_SETTOOLTIPS"; - case WM_USER+37: return "TB_SETPARENT"; - case WM_USER+39: return "TB_SETROWS"; - case WM_USER+40: return "TB_GETROWS"; - case WM_USER+42: return "TB_SETCMDID"; - case WM_USER+43: return "TB_CHANGEBITMAP"; - case WM_USER+44: return "TB_GETBITMAP"; - case WM_USER+45: return "TB_GETBUTTONTEXTA"; - case WM_USER+75: return "TB_GETBUTTONTEXTW"; - case WM_USER+46: return "TB_REPLACEBITMAP"; - case WM_USER+47: return "TB_SETINDENT"; - case WM_USER+48: return "TB_SETIMAGELIST"; - case WM_USER+49: return "TB_GETIMAGELIST"; - case WM_USER+50: return "TB_LOADIMAGES"; - case WM_USER+51: return "TB_GETRECT"; - case WM_USER+52: return "TB_SETHOTIMAGELIST"; - case WM_USER+53: return "TB_GETHOTIMAGELIST"; - case WM_USER+54: return "TB_SETDISABLEDIMAGELIST"; - case WM_USER+55: return "TB_GETDISABLEDIMAGELIST"; - case WM_USER+56: return "TB_SETSTYLE"; - case WM_USER+57: return "TB_GETSTYLE"; - case WM_USER+58: return "TB_GETBUTTONSIZE"; - case WM_USER+59: return "TB_SETBUTTONWIDTH"; - case WM_USER+60: return "TB_SETMAXTEXTROWS"; - case WM_USER+61: return "TB_GETTEXTROWS"; - case WM_USER+41: return "TB_GETBITMAPFLAGS"; + case WM_USER+1: return wxT("TB_ENABLEBUTTON"); + case WM_USER+2: return wxT("TB_CHECKBUTTON"); + case WM_USER+3: return wxT("TB_PRESSBUTTON"); + case WM_USER+4: return wxT("TB_HIDEBUTTON"); + case WM_USER+5: return wxT("TB_INDETERMINATE"); + case WM_USER+9: return wxT("TB_ISBUTTONENABLED"); + case WM_USER+10: return wxT("TB_ISBUTTONCHECKED"); + case WM_USER+11: return wxT("TB_ISBUTTONPRESSED"); + case WM_USER+12: return wxT("TB_ISBUTTONHIDDEN"); + case WM_USER+13: return wxT("TB_ISBUTTONINDETERMINATE"); + case WM_USER+17: return wxT("TB_SETSTATE"); + case WM_USER+18: return wxT("TB_GETSTATE"); + case WM_USER+19: return wxT("TB_ADDBITMAP"); + case WM_USER+20: return wxT("TB_ADDBUTTONS"); + case WM_USER+21: return wxT("TB_INSERTBUTTON"); + case WM_USER+22: return wxT("TB_DELETEBUTTON"); + case WM_USER+23: return wxT("TB_GETBUTTON"); + case WM_USER+24: return wxT("TB_BUTTONCOUNT"); + case WM_USER+25: return wxT("TB_COMMANDTOINDEX"); + case WM_USER+26: return wxT("TB_SAVERESTOREA"); + case WM_USER+76: return wxT("TB_SAVERESTOREW"); + case WM_USER+27: return wxT("TB_CUSTOMIZE"); + case WM_USER+28: return wxT("TB_ADDSTRINGA"); + case WM_USER+77: return wxT("TB_ADDSTRINGW"); + case WM_USER+29: return wxT("TB_GETITEMRECT"); + case WM_USER+30: return wxT("TB_BUTTONSTRUCTSIZE"); + case WM_USER+31: return wxT("TB_SETBUTTONSIZE"); + case WM_USER+32: return wxT("TB_SETBITMAPSIZE"); + case WM_USER+33: return wxT("TB_AUTOSIZE"); + case WM_USER+35: return wxT("TB_GETTOOLTIPS"); + case WM_USER+36: return wxT("TB_SETTOOLTIPS"); + case WM_USER+37: return wxT("TB_SETPARENT"); + case WM_USER+39: return wxT("TB_SETROWS"); + case WM_USER+40: return wxT("TB_GETROWS"); + case WM_USER+42: return wxT("TB_SETCMDID"); + case WM_USER+43: return wxT("TB_CHANGEBITMAP"); + case WM_USER+44: return wxT("TB_GETBITMAP"); + case WM_USER+45: return wxT("TB_GETBUTTONTEXTA"); + case WM_USER+75: return wxT("TB_GETBUTTONTEXTW"); + case WM_USER+46: return wxT("TB_REPLACEBITMAP"); + case WM_USER+47: return wxT("TB_SETINDENT"); + case WM_USER+48: return wxT("TB_SETIMAGELIST"); + case WM_USER+49: return wxT("TB_GETIMAGELIST"); + case WM_USER+50: return wxT("TB_LOADIMAGES"); + case WM_USER+51: return wxT("TB_GETRECT"); + case WM_USER+52: return wxT("TB_SETHOTIMAGELIST"); + case WM_USER+53: return wxT("TB_GETHOTIMAGELIST"); + case WM_USER+54: return wxT("TB_SETDISABLEDIMAGELIST"); + case WM_USER+55: return wxT("TB_GETDISABLEDIMAGELIST"); + case WM_USER+56: return wxT("TB_SETSTYLE"); + case WM_USER+57: return wxT("TB_GETSTYLE"); + case WM_USER+58: return wxT("TB_GETBUTTONSIZE"); + case WM_USER+59: return wxT("TB_SETBUTTONWIDTH"); + case WM_USER+60: return wxT("TB_SETMAXTEXTROWS"); + case WM_USER+61: return wxT("TB_GETTEXTROWS"); + case WM_USER+41: return wxT("TB_GETBITMAPFLAGS"); default: - static char s_szBuf[128]; - sprintf(s_szBuf, "", message); - return s_szBuf; + static wxString s_szBuf; + s_szBuf.Printf(wxT(""), message); + return s_szBuf.c_str(); } } -#endif //__WXDEBUG__ - -static void TranslateKbdEventToMouse(wxWindowMSW *win, - int *x, int *y, WPARAM *flags) -{ - // construct the key mask - WPARAM& fwKeys = *flags; - - fwKeys = MK_RBUTTON; - if ( wxIsCtrlDown() ) - fwKeys |= MK_CONTROL; - if ( wxIsShiftDown() ) - fwKeys |= MK_SHIFT; - - // simulate right mouse button click - DWORD dwPos = ::GetMessagePos(); - *x = GET_X_LPARAM(dwPos); - *y = GET_Y_LPARAM(dwPos); - - win->ScreenToClient(x, y); -} +#endif // wxDEBUG_LEVEL >= 2 static TEXTMETRIC wxGetTextMetrics(const wxWindowMSW *win) { @@ -5597,31 +7209,50 @@ wxWindow* wxFindWindowAtPoint(const wxPoint& pt) POINT pt2; pt2.x = pt.x; pt2.y = pt.y; - HWND hWndHit = ::WindowFromPoint(pt2); - - wxWindow* win = wxFindWinFromHandle((WXHWND) hWndHit) ; - HWND hWnd = hWndHit; - // Try to find a window with a wxWindow associated with it - while (!win && (hWnd != 0)) + HWND hWnd = ::WindowFromPoint(pt2); + if ( hWnd ) { - hWnd = ::GetParent(hWnd); - win = wxFindWinFromHandle((WXHWND) hWnd) ; + // WindowFromPoint() ignores the disabled children but we're supposed + // to take them into account, so check if we have a child at this + // coordinate. + ::ScreenToClient(hWnd, &pt2); + hWnd = ::ChildWindowFromPointEx(hWnd, pt2, CWP_SKIPINVISIBLE); } - return win; + + return wxGetWindowFromHWND((WXHWND)hWnd); } // Get the current mouse position. wxPoint wxGetMousePosition() { POINT pt; - GetCursorPos( & pt ); + wxGetCursorPosMSW(&pt); return wxPoint(pt.x, pt.y); } #if wxUSE_HOTKEY +#if defined(__SMARTPHONE__) || defined(__POCKETPC__) +static void WinCEUnregisterHotKey(int modifiers, int id) +{ + // Register hotkeys for the hardware buttons + HINSTANCE hCoreDll; + typedef BOOL (WINAPI *UnregisterFunc1Proc)(UINT, UINT); + + UnregisterFunc1Proc procUnregisterFunc; + hCoreDll = LoadLibrary(wxT("coredll.dll")); + if (hCoreDll) + { + procUnregisterFunc = (UnregisterFunc1Proc)GetProcAddress(hCoreDll, wxT("UnregisterFunc1")); + if (procUnregisterFunc) + procUnregisterFunc(modifiers, id); + FreeLibrary(hCoreDll); + } +} +#endif + bool wxWindowMSW::RegisterHotKey(int hotkeyId, int modifiers, int keycode) { UINT win_modifiers=0; @@ -5634,43 +7265,150 @@ bool wxWindowMSW::RegisterHotKey(int hotkeyId, int modifiers, int keycode) if ( modifiers & wxMOD_WIN ) win_modifiers |= MOD_WIN; +#if defined(__SMARTPHONE__) || defined(__POCKETPC__) + // Required for PPC and Smartphone hardware buttons + if (keycode >= WXK_SPECIAL1 && keycode <= WXK_SPECIAL20) + WinCEUnregisterHotKey(win_modifiers, hotkeyId); +#endif + if ( !::RegisterHotKey(GetHwnd(), hotkeyId, win_modifiers, keycode) ) { - wxLogLastError(_T("RegisterHotKey")); + wxLogLastError(wxT("RegisterHotKey")); - return FALSE; + return false; } - return TRUE; + return true; } bool wxWindowMSW::UnregisterHotKey(int hotkeyId) { +#if defined(__SMARTPHONE__) || defined(__POCKETPC__) + WinCEUnregisterHotKey(MOD_WIN, hotkeyId); +#endif + if ( !::UnregisterHotKey(GetHwnd(), hotkeyId) ) { - wxLogLastError(_T("UnregisterHotKey")); + wxLogLastError(wxT("UnregisterHotKey")); - return FALSE; + return false; } - return TRUE; + return true; } +#if wxUSE_ACCEL + 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); + wxKeyEvent event(CreateKeyEvent(wxEVT_HOTKEY, HIWORD(lParam))); + event.SetId(wParam); 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); + return HandleWindowEvent(event); } +#endif // wxUSE_ACCEL + #endif // wxUSE_HOTKEY +// Not tested under WinCE +#ifndef __WXWINCE__ + +// this class installs a message hook which really wakes up our idle processing +// each time a WM_NULL is received (wxWakeUpIdle does this), even if we're +// sitting inside a local modal loop (e.g. a menu is opened or scrollbar is +// being dragged or even inside ::MessageBox()) and so don't control message +// dispatching otherwise +class wxIdleWakeUpModule : public wxModule +{ +public: + virtual bool OnInit() + { + ms_hMsgHookProc = ::SetWindowsHookEx + ( + WH_GETMESSAGE, + &wxIdleWakeUpModule::MsgHookProc, + NULL, + GetCurrentThreadId() + ); + + if ( !ms_hMsgHookProc ) + { + wxLogLastError(wxT("SetWindowsHookEx(WH_GETMESSAGE)")); + + return false; + } + + return true; + } + + virtual void OnExit() + { + ::UnhookWindowsHookEx(wxIdleWakeUpModule::ms_hMsgHookProc); + } + + static LRESULT CALLBACK MsgHookProc(int nCode, WPARAM wParam, LPARAM lParam) + { + MSG *msg = (MSG*)lParam; + + // only process the message if it is actually going to be removed from + // the message queue, this prevents that the same event from being + // processed multiple times if now someone just called PeekMessage() + if ( msg->message == WM_NULL && wParam == PM_REMOVE ) + { + wxTheApp->ProcessPendingEvents(); + } + + return CallNextHookEx(ms_hMsgHookProc, nCode, wParam, lParam); + } + +private: + static HHOOK ms_hMsgHookProc; + + DECLARE_DYNAMIC_CLASS(wxIdleWakeUpModule) +}; + +HHOOK wxIdleWakeUpModule::ms_hMsgHookProc = 0; + +IMPLEMENT_DYNAMIC_CLASS(wxIdleWakeUpModule, wxModule) + +#endif // __WXWINCE__ + +#ifdef __WXWINCE__ + +#if wxUSE_STATBOX +static void wxAdjustZOrder(wxWindow* parent) +{ + if (wxDynamicCast(parent, wxStaticBox)) + { + // Set the z-order correctly + SetWindowPos((HWND) parent->GetHWND(), HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE); + } + + wxWindowList::compatibility_iterator current = parent->GetChildren().GetFirst(); + while (current) + { + wxWindow *childWin = current->GetData(); + wxAdjustZOrder(childWin); + current = current->GetNext(); + } +} +#endif + +// We need to adjust the z-order of static boxes in WinCE, to +// make 'contained' controls visible +void wxWindowMSW::OnInitDialog( wxInitDialogEvent& event ) +{ +#if wxUSE_STATBOX + wxAdjustZOrder(this); +#endif + + event.Skip(); +} +#endif