#include "wx/msgdlg.h"
#include "wx/settings.h"
#include "wx/statbox.h"
+ #include "wx/sizer.h"
#endif
#if wxUSE_OWNER_DRAWN && !defined(__WXUNIVERSAL__)
#endif
#include "wx/module.h"
+#include "wx/sysopt.h"
#if wxUSE_DRAG_AND_DROP
#include "wx/dnd.h"
#include <windowsx.h>
#endif
-#if (!defined(__GNUWIN32_OLD__) && !defined(__WXMICROWIN__) /* && !defined(__WXWINCE__) */ ) || defined(__CYGWIN10__)
- #ifdef __WIN95__
- #include <commctrl.h>
- #endif
-#elif !defined(__WXMICROWIN__) && !defined(__WXWINCE__) // broken compiler
- #include "wx/msw/gnuwin32/extra.h"
-#endif
+#include <commctrl.h>
#include "wx/msw/missing.h"
#if defined(__WXWINCE__)
-#include "wx/msw/wince/missing.h"
+ #include "wx/msw/wince/missing.h"
#endif
+#if defined(TME_LEAVE) && defined(WM_MOUSELEAVE)
+ #define HAVE_TRACKMOUSEEVENT
+#endif // everything needed for TrackMouseEvent()
+
+#define USE_DEFERRED_SIZING 1
+#define USE_DEFER_BUG_WORKAROUND 1
+
// ---------------------------------------------------------------------------
// global variables
// ---------------------------------------------------------------------------
#endif // !__WXWINCE__
+#ifdef __WXWINCE__
+// On Windows CE, GetCursorPos can return an error, so use this function
+// instead
+bool GetCursorPosWinCE(POINT* pt)
+{
+ if (!GetCursorPos(pt))
+ {
+ DWORD pos = GetMessagePos();
+ pt->x = LOWORD(pos);
+ pt->y = HIWORD(pos);
+ }
+ return true;
+}
+#endif
+
// ---------------------------------------------------------------------------
// event tables
// ---------------------------------------------------------------------------
// Always constructor Properties first
wxREADONLY_PROPERTY( Parent,wxWindow*, GetParent, EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
- wxPROPERTY( Id,wxWindowID, SetId, GetId, wxID_ANY, 0 /*flags*/ , wxT("Helpstring") , wxT("group") )
+ wxPROPERTY( Id,wxWindowID, SetId, GetId, -1 /*wxID_ANY*/ , 0 /*flags*/ , wxT("Helpstring") , wxT("group") )
wxPROPERTY( Position,wxPoint, SetPosition , GetPosition, wxDefaultPosition , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // pos
wxPROPERTY( Size,wxSize, SetSize, GetSize, wxDefaultSize , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // size
wxPROPERTY( WindowStyle , long , SetWindowStyleFlag , GetWindowStyleFlag , EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style
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( Title,wxString, SetTitle, GetTitle, wxEmptyString )
wxPROPERTY( Font , wxFont , SetFont , GetWindowFont , )
- wxPROPERTY( Label,wxString, SetLabel, GetLabel, wxT("") )
+ wxPROPERTY( Label,wxString, SetLabel, GetLabel, wxEmptyString )
// MaxHeight, Width , MinHeight , Width
// TODO switch label to control and title to toplevels
m_childrenDisabled = NULL;
m_frozenness = 0;
- // wxWnd
- m_hMenu = 0;
-
m_hWnd = 0;
+ m_hDWP = 0;
m_xThumbSize = 0;
m_yThumbSize = 0;
m_lastMouseY = -1;
m_lastMouseEvent = -1;
#endif // wxUSE_MOUSEEVENT_HACK
+
+ m_pendingPosition = wxDefaultPosition;
+ m_pendingSize = wxDefaultSize;
}
// Destructor
{
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__
}
delete m_childrenDisabled;
+
}
// real construction (Init() must have been called before!)
// Change the cursor NOW if we're within the correct window
POINT point;
+#ifdef __WXWINCE__
+ ::GetCursorPosWinCE(&point);
+#else
::GetCursorPos(&point);
+#endif
RECT rect = wxGetWindowRect(hWnd);
// scrolling stuff
// ---------------------------------------------------------------------------
-// convert wxHORIZONTAL/wxVERTICAL to SB_HORZ/SB_VERT
-static inline int wxDirToWinStyle(int orient)
-{
- return orient == wxHORIZONTAL ? SB_HORZ : SB_VERT;
-}
-
inline int GetScrollPosition(HWND hWnd, int wOrient)
{
#ifdef __WXMICROWIN__
wOrient,
&scrollInfo) )
{
- // Not neccessarily an error, if there are no scrollbars yet.
+ // Not necessarily an error, if there are no scrollbars yet.
// wxLogLastError(_T("GetScrollInfo"));
}
return scrollInfo.nPos;
// On WinCE (at least), the wndproc comparison doesn't work,
// so have to use something like this.
#ifdef __WXWINCE__
+ wxUnusedVar(wndProc);
+
extern wxChar *wxCanvasClassName;
extern wxChar *wxCanvasClassNameNR;
extern const wxChar *wxMDIFrameClassName;
::SetWindowLong(GetHwnd(), GWL_EXSTYLE, exstyleReal);
- // we must call SetWindowPos() to flash the cached extended style and
+ // 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(),
// 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;
{
// get the mouse position
POINT pt;
+#ifdef __WXWINCE__
+ ::GetCursorPosWinCE(&pt);
+#else
::GetCursorPos(&pt);
+#endif
// find the window which currently has the cursor and go up the window
// chain until we find this window - or exhaust it
void wxWindowMSW::OnInternalIdle()
{
+#ifndef HAVE_TRACKMOUSEEVENT
// Check if we need to send a LEAVE event
if ( m_mouseInWindow )
{
// 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);
{
if ( !m_frozenness++ )
{
- SendSetRedraw(GetHwnd(), false);
+ if ( IsShown() )
+ SendSetRedraw(GetHwnd(), false);
}
}
if ( !--m_frozenness )
{
- SendSetRedraw(GetHwnd(), true);
+ if ( IsShown() )
+ {
+ SendSetRedraw(GetHwnd(), true);
- // we need to refresh everything or otherwise he invalidated area is not
- // repainted
- Refresh();
+ // we need to refresh everything or otherwise the invalidated area
+ // is not going to be repainted
+ Refresh();
+ }
}
}
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);
+ 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
}
}
width = 0;
if (height < 0)
height = 0;
- if ( !::MoveWindow(GetHwnd(), x, y, width, height, IsShown() /*Repaint?*/) )
- {
- wxLogLastError(wxT("MoveWindow"));
- }
+
+ // if our parent had prepared a defer window handle for us, use it (unless
+ // we are a top level window)
+ wxWindowMSW *parent = GetParent();
+
+#if USE_DEFERRED_SIZING
+ HDWP hdwp = parent && !IsTopLevel() ? (HDWP)parent->m_hDWP : NULL;
+#else
+ HDWP hdwp = 0;
+#endif
+
+ wxMoveWindowDeferred(hdwp, this, GetHwnd(), x, y, width, height);
+
+#if USE_DEFERRED_SIZING
+ if ( parent )
+ // hdwp must be updated as it may have been changed
+ parent->m_hDWP = (WXHANDLE)hdwp;
+#endif
}
// set the size of the window: if the dimensions are positive, just use them,
{
// get the current size and position...
int currentX, currentY;
+ int currentW, currentH;
+
+#if USE_DEFER_BUG_WORKAROUND
+ currentX = m_pendingPosition.x;
+ if (currentX == wxDefaultCoord)
+ GetPosition(¤tX, NULL);
+
+ currentY = m_pendingPosition.y;
+ if (currentY == wxDefaultCoord)
+ GetPosition(NULL, ¤tY);
+
+ currentW = m_pendingSize.x;
+ if (currentW == wxDefaultCoord)
+ GetSize(¤tW, NULL);
+
+ currentH = m_pendingSize.y;
+ if (currentH == wxDefaultCoord)
+ GetSize(NULL, ¤tH);
+#else
GetPosition(¤tX, ¤tY);
- int currentW,currentH;
GetSize(¤tW, ¤tH);
+#endif
// ... and don't do anything (avoiding flicker) if it's already ok
if ( x == currentX && y == currentY &&
}
}
+#if USE_DEFER_BUG_WORKAROUND
+ // We don't actually use the hdwp here, but we need to know whether to
+ // save the pending dimensions or not. This isn't done in DoMoveWindow
+ // (where the hdwp is used) because some controls have thier own
+ // DoMoveWindow so it is easier to catch it here.
+ wxWindowMSW *parent = GetParent();
+ HDWP hdwp = parent && !IsTopLevel() ? (HDWP)parent->m_hDWP : NULL;
+ if (hdwp)
+ {
+ m_pendingPosition = wxPoint(x, y);
+ m_pendingSize = wxSize(width, height);
+ }
+ else
+ {
+ m_pendingPosition = wxDefaultPosition;
+ m_pendingSize = wxDefaultSize;
+ }
+#endif
+
DoMoveWindow(x, y, width, height);
}
}
}
-// For implementation purposes - sometimes decorations make the client area
-// smaller
-wxPoint wxWindowMSW::GetClientAreaOrigin() const
-{
- return wxPoint(0, 0);
-}
-
// ---------------------------------------------------------------------------
// text metrics
// ---------------------------------------------------------------------------
SIZE sizeRect;
TEXTMETRIC tm;
- GetTextExtentPoint(hdc, string, string.length(), &sizeRect);
+ ::GetTextExtentPoint32(hdc, string, string.length(), &sizeRect);
GetTextMetrics(hdc, &tm);
if ( x )
}
bool bForward = true,
- bWindowChange = false;
+ bWindowChange = false,
+ bFromTab = false;
// should we process this message specially?
bool bProcess = true;
// Ctrl-Tab cycles thru notebook pages
bWindowChange = bCtrlDown;
bForward = !bShiftDown;
+ bFromTab = true;
}
break;
case VK_ESCAPE:
{
#if wxUSE_BUTTON
- wxButton *btn = wxDynamicCast(FindWindow(wxID_CANCEL),
- wxButton);
+ wxButton *btn = wxDynamicCast(FindWindow(wxID_CANCEL),wxButton);
+
+ // our own wxLogDialog should react to Esc
+ // without Cancel button but this is a private class
+ // so let's try recognize it by content
+ #if wxUSE_LOG_DIALOG
+ if ( !btn &&
+ wxDynamicCast(this,wxDialog) &&
+ FindWindow(wxID_MORE) &&
+ FindWindow(wxID_OK) &&
+ !FindWindow(wxID_CANCEL) &&
+ GetTitle().MakeLower().StartsWith(wxTheApp->GetAppName().c_str())
+ )
+ btn = wxDynamicCast(FindWindow(wxID_OK),wxButton);
+ #endif // wxUSE_LOG_DIALOG
if ( btn && btn->IsEnabled() )
{
// if we do have a cancel button, do press it
bProcess = false;
}
// FIXME: this should be handled by
- // wxNavigationKeyEvent handler and not here!!
+ // wxNavigationKeyEvent handler and not here!
else
{
#if wxUSE_BUTTON
wxNavigationKeyEvent event;
event.SetDirection(bForward);
event.SetWindowChange(bWindowChange);
+ event.SetFromTab(bFromTab);
event.SetEventObject(this);
if ( GetEventHandler()->ProcessEvent(event) )
{
bool allow;
WXLRESULT result;
- WXHICON hIcon;
WXHBRUSH hBrush;
} rc;
(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;
}
}
break;
-#endif
-
- case WM_SIZE:
- processed = HandleSize(LOWORD(lParam), HIWORD(lParam), wParam);
- break;
-#if !defined(__WXWINCE__)
case WM_SIZING:
{
LPRECT pRect = (LPRECT)lParam;
}
}
break;
-#endif
+#endif // !__WXWINCE__
+
+ case WM_WINDOWPOSCHANGED:
+ {
+ WINDOWPOS *lpPos = (WINDOWPOS *)lParam;
+
+ if ( !(lpPos->flags & SWP_NOSIZE) )
+ {
+ RECT rc;
+ ::GetClientRect(GetHwnd(), &rc);
+
+ AutoHRGN hrgnClient(::CreateRectRgnIndirect(&rc));
+ AutoHRGN hrgnNew(::CreateRectRgn(lpPos->x, lpPos->y,
+ lpPos->cx, lpPos->cy));
+ AutoHRGN hrgn(::CreateRectRgn(0, 0, 0, 0));
+
+ // we need to invalidate any new exposed areas here
+ // to force them to repaint
+ if ( ::CombineRgn(hrgn, hrgnNew, hrgnClient, RGN_DIFF) != NULLREGION )
+ ::InvalidateRgn(GetHwnd(), hrgn, TRUE);
+ if ( ::CombineRgn(hrgn, hrgnClient, hrgnNew, RGN_DIFF) != NULLREGION )
+ ::InvalidateRgn(GetHwnd(), hrgn, TRUE);
+
+ }
+ }
+ break;
#if !defined(__WXMICROWIN__) && !defined(__WXWINCE__)
case WM_ACTIVATEAPP:
+ // This implicitly sends a wxEVT_ACTIVATE_APP event
wxTheApp->SetActive(wParam != 0, FindFocus());
break;
#endif
processed = HandleKillFocus((WXHWND)(HWND)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
{
-#if wxUSE_LISTCTRL
- // Don't call the wx handlers in this case
- if ( wxIsKindOf(this, wxListCtrl) )
- break;
-#endif
-
- 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;
rc.result = TRUE;
-#endif
break;
case WM_SHOWWINDOW:
wParam);
break;
- // Seems to be broken currently
-#if 0 // ndef __WXWINCE__
+#ifdef HAVE_TRACKMOUSEEVENT
case WM_MOUSELEAVE:
- {
- wxASSERT_MSG( !m_mouseInWindow, wxT("the mouse should be in a window to generate this event!") );
-
- // only process this message if the mouse is not in the window,
- // This can also check for children in composite windows.
- // however, this may mean the the wxEVT_LEAVE_WINDOW is never sent
- // if the mouse does not enter the window from it's child before
- // leaving the scope of the window. ( perhaps this can be picked
- // up in the OnIdle code as before, for this special case )
- if ( /*IsComposite() && */ !IsMouseInWindow() )
+ // filter out excess WM_MOUSELEAVE events sent after PopupMenu() (on XP at least)
+ if ( m_mouseInWindow )
{
- m_mouseInWindow = false;
-
- // Unfortunately no mouse state is passed with a WM_MOUSE_LEAVE
- 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();
}
+
// 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.
- processed = false;
- }
- break;
-#endif
- // __WXWINCE__
+ // 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:
wxHelpEvent helpEvent(wxEVT_HELP,
subjectOfHelp->GetId(),
#ifdef __WXWINCE__
- wxPoint(0, 0)
+ wxPoint(0,0)
#else
wxPoint(info->MousePos.x, info->MousePos.y)
#endif
// do create the window
wxWindowCreationHook hook(this);
- // VZ: anyone care to explain why this is done for CE?
-#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 // __WXWINCE__
- {
- 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,
+ title ? title : m_windowName.c_str(),
+ style,
+ x, y, w, h,
+ (HWND)MSWGetParent(),
+ (HMENU)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;
}
bool wxWindowMSW::HandleQueryEndSession(long logOff, bool *mayEnd)
{
-#ifndef __WXWINCE__
+#ifdef ENDSESSION_LOGOFF
wxCloseEvent event(wxEVT_QUERY_END_SESSION, wxID_ANY);
event.SetEventObject(wxTheApp);
event.SetCanVeto(true);
return rc;
#else
+ 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 wxTheApp->ProcessEvent(event);
#else
+ wxUnusedVar(endSession);
+ wxUnusedVar(logOff);
return false;
#endif
}
bool wxWindowMSW::HandleShow(bool show, int WXUNUSED(status))
{
wxShowEvent event(GetId(), show);
- event.m_eventObject = this;
+ event.SetEventObject(this);
return GetEventHandler()->ProcessEvent(event);
}
bool wxWindowMSW::HandleInitDialog(WXHWND WXUNUSED(hWndFocus))
{
wxInitDialogEvent event(GetId());
- event.m_eventObject = this;
+ event.SetEventObject(this);
return GetEventHandler()->ProcessEvent(event);
}
DragFinish (hFilesInfo);
wxDropFilesEvent event(wxEVT_DROP_FILES, gwFilesDropped, files);
- event.m_eventObject = this;
+ event.SetEventObject(this);
POINT dropPoint;
DragQueryPoint(hFilesInfo, (LPPOINT) &dropPoint);
// first ask the user code - it may wish to set the cursor in some very
// specific way (for example, depending on the current position)
POINT pt;
+#ifdef __WXWINCE__
+ if ( !::GetCursorPosWinCE(&pt))
+#else
if ( !::GetCursorPos(&pt) )
+#endif
{
wxLogLastError(wxT("GetCursorPos"));
}
{
wxMenuItem *pMenuItem = (wxMenuItem *)(pDrawStruct->itemData);
- wxCHECK_MSG( pMenuItem && pMenuItem->IsKindOf(CLASSINFO(wxMenuItem)),
+ // see comment before the same test in MSWOnMeasureItem() below
+ if ( !pMenuItem )
+ return false;
+
+ wxCHECK_MSG( wxDynamicCast(pMenuItem, wxMenuItem),
false, _T("MSWOnDrawItem: bad wxMenuItem pointer") );
// prepare to call OnDrawItem(): notice using of wxDCTemp to prevent
{
wxMenuItem *pMenuItem = (wxMenuItem *)(pMeasureStruct->itemData);
- wxCHECK_MSG( pMenuItem && pMenuItem->IsKindOf(CLASSINFO(wxMenuItem)),
+ // 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, _T("MSWOnMeasureItem: bad wxMenuItem pointer") );
size_t w, h;
#ifndef __WXMICROWIN__
-bool wxWindowMSW::HandleCtlColor(WXHBRUSH *brush, WXHDC pDC, WXHWND pWnd)
+bool wxWindowMSW::HandleCtlColor(WXHBRUSH *brush, WXHDC hDC, WXHWND hWnd)
{
-#if wxUSE_CONTROLS
- wxWindow *item = FindItemByHWND(pWnd, true);
+#if !wxUSE_CONTROLS || defined(__WXUNIVERSAL__)
+ wxUnusedVar(hDC);
+ wxUnusedVar(hWnd);
+#else
+ wxControl *item = wxDynamicCast(FindItemByHWND(hWnd, true), wxControl);
+
if ( item )
- *brush = item->MSWControlColor(pDC);
+ *brush = item->MSWControlColor(hDC, hWnd);
else
#endif // wxUSE_CONTROLS
*brush = NULL;
#endif // __WXMICROWIN__
-WXHBRUSH wxWindowMSW::MSWControlColor(WXHDC WXUNUSED(hDC))
-{
- return (WXHBRUSH)0;
-}
-
bool wxWindowMSW::HandlePaletteChanged(WXHWND hWndPalChange)
{
#if wxUSE_PALETTE
// 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.
+ wxLogNull logNo; // suppress error if we couldn't load the bitmap
wxBitmap stdColourBitmap(_T("wxBITMAP_STD_COLOURS"));
if ( stdColourBitmap.Ok() )
{
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"));
bool wxWindowMSW::HandleEraseBkgnd(WXHDC hdc)
{
- // Prevents flicker when dragging
- if ( ::IsIconic(GetHwnd()) )
- return true;
-
wxDCTemp dc(hdc);
dc.SetHDC(hdc);
void wxWindowMSW::OnEraseBackground(wxEraseEvent& event)
{
- switch ( GetBackgroundStyle() )
+ // standard non top level controls (i.e. except the dialogs) always erase
+ // their background themselves in HandleCtlColor() or have some control-
+ // specific ways to set the colours (common controls)
+ if ( IsOfStandardClass() && !IsTopLevel() )
{
- default:
- wxFAIL_MSG( _T("unexpected background style") );
- // fall through
+ event.Skip();
+ return;
+ }
- case wxBG_STYLE_CUSTOM:
- // don't skip the event here, custom background means that the app
- // is drawing it itself in its OnPaint()
- break;
+ if ( GetBackgroundStyle() == wxBG_STYLE_CUSTOM )
+ {
+ // don't skip the event here, custom background means that the app
+ // is drawing it itself in its OnPaint(), so don't draw it at all
+ // now to avoid flicker
+ return;
+ }
- case wxBG_STYLE_SYSTEM:
-#if wxUSE_NOTEBOOK && wxUSE_UXTHEME
- // automatically apply the tab control theme background to any
- // child panels to have the same look as the native property sheet
- // dialogs
- {
- for ( wxWindow *win = this; win; win = win->GetParent() )
- {
- wxNotebook *nbook = wxDynamicCast(win, wxNotebook);
- if ( nbook )
- {
- nbook->DoEraseBackground(event);
- return;
- }
- }
- }
-#endif // wxUSE_NOTEBOOK
- event.Skip();
+
+ // do default background painting
+ if ( !DoEraseBackground(GetHdcOf(*event.GetDC())) )
+ {
+ // let the system paint the background
+ event.Skip();
+ }
+}
+
+bool wxWindowMSW::DoEraseBackground(WXHDC hDC)
+{
+ HBRUSH hbr = (HBRUSH)MSWGetBgBrush(hDC);
+ if ( !hbr )
+ return false;
+
+ wxFillRect(GetHwnd(), (HDC)hDC, hbr);
+
+ return true;
+}
+
+WXHBRUSH
+wxWindowMSW::MSWGetBgBrushForChild(WXHDC WXUNUSED(hDC), WXHWND hWnd)
+{
+ if ( m_hasBgCol )
+ {
+ // our background colour applies to:
+ // 1. this window itself, always
+ // 2. all children unless the colour is "not inheritable"
+ // 3. even if it is not inheritable, our immediate transparent
+ // children should still inherit it -- but not any transparent
+ // children because it would look wrong if a child of non
+ // transparent child would show our bg colour when the child itself
+ // does not
+ wxWindow *win = wxFindWinFromHandle(hWnd);
+ if ( win == this ||
+ m_inheritBgCol ||
+ (win && win->HasTransparentBackground() &&
+ win->GetParent() == this) )
+ {
+ // draw children with the same colour as the parent
+ wxBrush *
+ brush = wxTheBrushList->FindOrCreateBrush(GetBackgroundColour());
+
+ return (WXHBRUSH)GetHbrushOf(*brush);
+ }
+ }
+
+ return 0;
+}
+
+WXHBRUSH wxWindowMSW::MSWGetBgBrush(WXHDC hDC, WXHWND hWndToPaint)
+{
+ if ( !hWndToPaint )
+ hWndToPaint = GetHWND();
+
+ for ( wxWindowMSW *win = this; win; win = win->GetParent() )
+ {
+ WXHBRUSH hBrush = win->MSWGetBgBrushForChild(hDC, hWndToPaint);
+ if ( hBrush )
+ return hBrush;
+
+ // background is not inherited beyond top level windows
+ if ( win->IsTopLevel() )
break;
+ }
- case wxBG_STYLE_COLOUR:
- // we have a fixed solid background colour, do use it
- RECT rect;
- ::GetClientRect(GetHwnd(), &rect);
+ return 0;
+}
- HBRUSH hBrush = ::CreateSolidBrush(
- wxColourToPalRGB(GetBackgroundColour()));
- if ( !hBrush )
- wxLogLastError(wxT("CreateSolidBrush"));
+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!
- HDC hdc = GetHdcOf((*event.GetDC()));
+ if ( IsTopLevel() || InheritsBackgroundColour() )
+ return false;
- ::FillRect(hdc, &rect, hBrush);
- ::DeleteObject(hBrush);
+ for ( wxWindow *win = GetParent(); win; win = win->GetParent() )
+ {
+ if ( win->MSWPrintChild(hDC, (wxWindow *)this) )
+ return true;
+
+ if ( win->IsTopLevel() || win->InheritsBackgroundColour() )
+ break;
}
+
+ return false;
}
// ---------------------------------------------------------------------------
bool wxWindowMSW::HandleSize(int WXUNUSED(w), int WXUNUSED(h), WXUINT wParam)
{
- bool processed = false;
+#if USE_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(_T("BeginDeferWindowPos"));
+ }
+ if (m_hDWP)
+ useDefer = true;
+ }
+ }
+#endif
+
+ // update this window size
+ bool processed = false;
switch ( wParam )
{
default:
processed = GetEventHandler()->ProcessEvent(event);
}
+#if USE_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(_T("EndDeferWindowPos"));
+ }
+
+#if USE_DEFER_BUG_WORKAROUND
+ // Reset our children's pending pos/size values.
+ for ( wxWindowList::compatibility_iterator node = GetChildren().GetFirst();
+ node;
+ node = node->GetNext() )
+ {
+ wxWindowMSW *child = node->GetData();
+ child->m_pendingPosition = wxDefaultPosition;
+ child->m_pendingSize = wxDefaultSize;
+ }
+#endif
+ }
+#endif
+
return processed;
}
return GetEventHandler()->ProcessEvent(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;
- }
#endif // wxUSE_SPINCTRL
+#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;
}
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;
#ifndef __WXWINCE__
event.SetTimestamp(::GetMessageTime());
#endif
- event.m_eventObject = this;
+ event.SetEventObject(this);
event.SetId(GetId());
#if wxUSE_MOUSEEVENT_HACK
{
// Generate an ENTER event
m_mouseInWindow = true;
-#if _WIN32_WINNT >= 0x0400
-#ifndef __WXWINCE__
- TRACKMOUSEEVENT trackinfo;
- trackinfo.cbSize = sizeof(trackinfo);
+#ifdef HAVE_TRACKMOUSEEVENT
+ WinStruct<TRACKMOUSEEVENT> trackinfo;
+
trackinfo.dwFlags = TME_LEAVE;
trackinfo.hwndTrack = GetHwnd();
- //Use the commctrl.h _TrackMouseEvent, which will call the
- // appropriate TrackMouseEvent or emulate it ( win95 )
- // else we need _WIN32_WINNT >= 0x0400
+
+ // Use the commctrl.h _TrackMouseEvent(), which will call the real
+ // TrackMouseEvent() if available or emulate it
_TrackMouseEvent(&trackinfo);
-#endif
-#endif
+#endif // HAVE_TRACKMOUSEEVENT
+
wxMouseEvent event(wxEVT_ENTER_WINDOW);
InitMouseEvent(event, x, y, flags);
event.m_linesPerAction = s_linesPerRotation;
return GetEventHandler()->ProcessEvent(event);
-#else
- (void) wParam;
- (void) lParam;
+#else // !wxUSE_MOUSEWHEEL
+ wxUnusedVar(wParam);
+ wxUnusedVar(lParam);
return false;
-#endif
+#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;
+#ifdef __WXWINCE__
+ if ( !::GetCursorPosWinCE(&pt) )
+#else
+ if ( !::GetCursorPos(&pt) )
+#endif
+ {
+ 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 event(wxEVT_LEAVE_WINDOW);
+ InitMouseEvent(event, pt.x, pt.y, state);
+
+ (void)GetEventHandler()->ProcessEvent(event);
+}
// ---------------------------------------------------------------------------
// keyboard handling
event.m_controlDown = wxIsCtrlDown();
event.m_altDown = (HIWORD(lParam) & KF_ALTDOWN) == KF_ALTDOWN;
- event.m_eventObject = (wxWindow *)this; // const_cast
+ event.SetEventObject((wxWindow *)this); // const_cast
event.m_keyCode = id;
#if wxUSE_UNICODE
event.m_uniChar = (wxChar) wParam;
// translate the position to client coords
POINT pt;
+#ifdef __WXWINCE__
+ GetCursorPosWinCE(&pt);
+#else
GetCursorPos(&pt);
+#endif
RECT rect;
GetWindowRect(GetHwnd(),&rect);
pt.x -= rect.left;
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:
: SB_VERT,
&scrollInfo) )
{
- // Not neccessarily an error, if there are no scrollbars yet.
+ // Not necessarily an error, if there are no scrollbars yet.
// wxLogLastError(_T("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:
case VK_CLEAR: id = WXK_CLEAR; break;
case VK_SHIFT: id = WXK_SHIFT; break;
case VK_CONTROL: id = WXK_CONTROL; break;
- case VK_MENU : id = WXK_MENU; break;
+ case VK_MENU : id = WXK_ALT; break;
case VK_PAUSE: id = WXK_PAUSE; break;
case VK_CAPITAL: id = WXK_CAPITAL; break;
case VK_SPACE: id = WXK_SPACE; break;
case VK_NUMLOCK: id = WXK_NUMLOCK; break;
case VK_SCROLL: id = WXK_SCROLL; break;
+ // the mapping for these keys may be incorrect on non-US keyboards so
+ // maybe we shouldn't map them to ASCII values at all
case VK_OEM_1: id = ';'; break;
case VK_OEM_PLUS: id = '+'; break;
case VK_OEM_COMMA: id = ','; break;
case WXK_SHIFT: keySym = VK_SHIFT; break;
case WXK_CONTROL: keySym = VK_CONTROL; break;
case WXK_ALT: keySym = VK_MENU; break;
- case WXK_MENU : keySym = VK_MENU; break;
case WXK_PAUSE: keySym = VK_PAUSE; break;
case WXK_CAPITAL: keySym = VK_CAPITAL; break;
case WXK_PRIOR: keySym = VK_PRIOR; break;
{
bool bVirtual;
+ wxASSERT_MSG(key != WXK_LBUTTON && key != WXK_RBUTTON && key !=
+ WXK_MBUTTON, wxT("can't use wxGetKeyState() for mouse buttons"));
+
//High order with GetAsyncKeyState only available on WIN32
-#ifdef __WIN32__
+#ifdef __WIN32__
//If the requested key is a LED key, return
//true if the led is pressed
if (key == WXK_NUMLOCK ||
key == WXK_SCROLL)
{
#endif
- //low order bit means LED is highlighted,
+ //low order bit means LED is highlighted,
//high order means key is down
//Here, for compat with other ports we want both
return GetKeyState( wxCharCodeWXToMSW(key, &bVirtual) ) != 0;
-#ifdef __WIN32__
+#ifdef __WIN32__
}
else
{
if ( (HIWORD(lParam) & KF_ALTDOWN) == KF_ALTDOWN )
event.m_altDown = true;
- event.m_eventObject = NULL;
+ event.SetEventObject(NULL);
event.m_keyCode = id;
event.m_shiftDown = wxIsShiftDown();
event.m_controlDown = wxIsCtrlDown();
wxPoint wxGetMousePosition()
{
POINT pt;
+#ifdef __WXWINCE__
+ GetCursorPosWinCE(&pt);
+#else
GetCursorPos( & pt );
+#endif
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(_T("coredll.dll"));
+ if (hCoreDll)
+ {
+ procUnregisterFunc = (UnregisterFunc1Proc)GetProcAddress(hCoreDll, _T("UnregisterFunc1"));
+ if (procUnregisterFunc)
+ procUnregisterFunc(modifiers, id);
+ FreeLibrary(hCoreDll);
+ }
+}
+#endif
+
bool wxWindowMSW::RegisterHotKey(int hotkeyId, int modifiers, int keycode)
{
UINT win_modifiers=0;
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"));
bool wxWindowMSW::UnregisterHotKey(int hotkeyId)
{
+#if defined(__SMARTPHONE__) || defined(__POCKETPC__)
+ WinCEUnregisterHotKey(MOD_WIN, hotkeyId);
+#endif
+
if ( !::UnregisterHotKey(GetHwnd(), hotkeyId) )
{
wxLogLastError(_T("UnregisterHotKey"));
#endif // wxUSE_HOTKEY
+// Moves a window by deferred method or normal method
+bool wxMoveWindowDeferred(HDWP& hdwp, wxWindowBase* win, HWND hWnd, int x, int y, int width, int height)
+{
+ if ( hdwp )
+ {
+ hdwp = ::DeferWindowPos(hdwp, hWnd, NULL,
+ x, y, width, height,
+ SWP_NOZORDER);
+ if ( !hdwp )
+ {
+ wxLogLastError(_T("DeferWindowPos"));
+ }
+ }
+
+ // otherwise (or if deferring failed) move the window in place immediately
+ if ( !hdwp )
+ {
+ if ( !::MoveWindow(hWnd, x, y, width, height, win->IsShown()) )
+ {
+ wxLogLastError(wxT("MoveWindow"));
+ }
+ }
+ return hdwp != NULL;
+}
+
// Not tested under WinCE
#ifndef __WXWINCE__
static LRESULT CALLBACK MsgHookProc(int nCode, WPARAM wParam, LPARAM lParam)
{
MSG *msg = (MSG*)lParam;
- if ( msg->message == WM_NULL )
+
+ // 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();
}