#include "wx/menu.h"
#include "wx/dc.h"
#include "wx/dcclient.h"
+ #include "wx/dcmemory.h"
#include "wx/utils.h"
#include "wx/app.h"
#include "wx/layout.h"
#endif
#endif
+// ----------------------------------------------------------------------------
+// standard constants not available with all compilers/headers
+// ----------------------------------------------------------------------------
+
// This didn't appear in mingw until 2.95.2
#ifndef SIF_TRACKPOS
#define SIF_TRACKPOS 16
#ifndef SPI_GETWHEELSCROLLLINES
#define SPI_GETWHEELSCROLLLINES 104
#endif
+#endif // wxUSE_MOUSEWHEEL
+
+#ifndef VK_OEM_1
+ #define VK_OEM_1 0xBA
+ #define VK_OEM_PLUS 0xBB
+ #define VK_OEM_COMMA 0xBC
+ #define VK_OEM_MINUS 0xBD
+ #define VK_OEM_PERIOD 0xBE
+ #define VK_OEM_2 0xBF
+ #define VK_OEM_3 0xC0
+ #define VK_OEM_4 0xDB
+ #define VK_OEM_5 0xDC
+ #define VK_OEM_6 0xDD
+ #define VK_OEM_7 0xDE
#endif
// ---------------------------------------------------------------------------
// global variables
// ---------------------------------------------------------------------------
-// the last Windows message we got (MT-UNSAFE)
+// the last Windows message we got (FIXME-MT)
extern MSG s_currentMsg;
#if wxUSE_MENUS_NATIVE
extern const wxChar *wxCanvasClassName;
+// true if we had already created the std colour map, used by
+// wxGetStdColourMap() and wxWindow::OnSysColourChanged() (FIXME-MT)
+static bool gs_hasStdCmap = FALSE;
+
// ---------------------------------------------------------------------------
// private functions
// ---------------------------------------------------------------------------
// get the text metrics for the current font
static TEXTMETRIC wxGetTextMetrics(const wxWindowMSW *win);
+// find the window for the mouse event at the specified position
+static wxWindowMSW *FindWindowForMouseEvent(wxWindow *win, int *x, int *y);
+
// wrapper around BringWindowToTop() API
static inline void wxBringWindowToTop(HWND hwnd)
{
m_oldWndProc = 0;
m_useCtl3D = FALSE;
m_mouseInWindow = FALSE;
+ m_lastKeydownProcessed = FALSE;
// wxWnd
m_hMenu = 0;
m_hWnd = 0;
- // pass WM_GETDLGCODE to DefWindowProc()
- m_lDlgCode = 0;
-
m_xThumbSize = 0;
m_yThumbSize = 0;
m_backgroundTransparent = FALSE;
{
m_isBeingDeleted = TRUE;
- MSWDetachWindowMenu();
-
#ifndef __WXUNIVERSAL__
// VS: make sure there's no wxFrame with last focus set to us:
for ( wxWindow *win = GetParent(); win; win = win->GetParent() )
//
// the correct solution is to create the controls as siblings of the
// static box
- wxASSERT_MSG( !wxDynamicCastThis(wxStaticBox),
+ wxASSERT_MSG( !wxDynamicCast(parent, wxStaticBox),
_T("wxStaticBox can't be used as a window parent!") );
#endif // wxUSE_STATBOX
parent->AddChild(this);
- // all windows are created visible
- DWORD msflags = WS_CHILD | WS_VISIBLE;
+ WXDWORD exstyle;
+ DWORD msflags = MSWGetCreateWindowFlags(&exstyle);
#ifdef __WXUNIVERSAL__
- // no 3d effects, we draw them ourselves
- WXDWORD exStyle = 0;
-#else // !wxUniversal
- if ( style & wxCLIP_CHILDREN )
- msflags |= WS_CLIPCHILDREN;
- if ( style & wxCLIP_SIBLINGS )
- msflags |= WS_CLIPSIBLINGS;
-
- bool want3D;
- WXDWORD exStyle = Determine3DEffects(WS_EX_CLIENTEDGE, &want3D);
-
- // Even with extended styles, need to combine with WS_BORDER
- // for them to look right.
- if ( want3D ||
- (m_windowStyle & (wxBORDER |
- wxSIMPLE_BORDER |
- wxRAISED_BORDER |
- wxSUNKEN_BORDER |
- wxDOUBLE_BORDER)) )
- {
- msflags |= WS_BORDER;
- }
-
- // calculate the value to return from WM_GETDLGCODE handler
- if ( GetWindowStyleFlag() & wxWANTS_CHARS )
- {
- // want everything: i.e. all keys and WM_CHAR message
- m_lDlgCode = DLGC_WANTARROWS | DLGC_WANTCHARS |
- DLGC_WANTTAB | DLGC_WANTMESSAGE;
- }
-#endif // wxUniversal/!wxUniversal
+ // no borders, we draw them ourselves
+ exstyle = 0;
+ msflags &= ~WS_BORDER;
+#endif // wxUniversal
+ // all windows are created visible by default except popup ones (which are
+ // like the wxTopLevelWindows in this aspect)
if ( style & wxPOPUP_WINDOW )
{
- // a popup window floats on top of everything
- exStyle |= WS_EX_TOPMOST | WS_EX_TOOLWINDOW;
-
- // it is also created hidden as other top level windows
msflags &= ~WS_VISIBLE;
m_isShown = FALSE;
}
+ else
+ {
+ msflags |= WS_VISIBLE;
+ }
- return MSWCreate(wxCanvasClassName, NULL, pos, size, msflags, exStyle);
+ return MSWCreate(wxCanvasClassName, NULL, pos, size, msflags, exstyle);
}
// ---------------------------------------------------------------------------
if ( !::SetFocus(hWnd) )
{
+#if defined(__WXDEBUG__) && !defined(__WXMICROWIN__)
// was there really an error?
-#ifndef __WXMICROWIN__
DWORD dwRes = ::GetLastError();
-#else
-
- DWORD dwRes = 0;
-#endif
if ( dwRes )
{
- wxLogApiError(_T("SetFocus"), dwRes);
+ HWND hwndFocus = ::GetFocus();
+ if ( hwndFocus != hWnd )
+ {
+ wxLogApiError(_T("SetFocus"), dwRes);
+ }
}
+#endif // Debug
+ }
+}
- // VZ: just why does this happen sometimes?? any idea?
-#if 0
- HWND hwndFocus = ::GetFocus();
- wxASSERT_MSG( hwndFocus == hWnd, _T("SetFocus() didn't work?") );
-#endif // 0
+void wxWindowMSW::SetFocusFromKbd()
+{
+ wxWindowBase::SetFocusFromKbd();
+
+ // when the focus is given to the control with DLGC_HASSETSEL style from
+ // keyboard its contents should be entirely selected: this is what
+ // ::IsDialogMessage() does and so we should do it as well to provide the
+ // same LNF as the native programs
+ if ( ::SendMessage(GetHwnd(), WM_GETDLGCODE, 0, 0) & DLGC_HASSETSEL )
+ {
+ ::SendMessage(GetHwnd(), EM_SETSEL, 0, -1);
}
}
return wxGetWindowText(GetHWND());
}
-void wxWindowMSW::CaptureMouse()
+void wxWindowMSW::DoCaptureMouse()
{
HWND hWnd = GetHwnd();
if ( hWnd )
}
}
-void wxWindowMSW::ReleaseMouse()
+void wxWindowMSW::DoReleaseMouse()
{
if ( !::ReleaseCapture() )
{
void wxWindowMSW::SetScrollPos(int orient, int pos, bool refresh)
{
-#if defined(__WIN95__)
- SCROLLINFO info;
- int dir;
+ HWND hWnd = GetHwnd();
+ wxCHECK_RET( hWnd, _T("SetScrollPos: no HWND") );
- if ( orient == wxHORIZONTAL ) {
- dir = SB_HORZ;
- } else {
- dir = SB_VERT;
- }
+ int dir = orient == wxHORIZONTAL ? SB_HORZ : SB_VERT;
+#if defined(__WIN95__)
+ SCROLLINFO info;
info.cbSize = sizeof(SCROLLINFO);
info.nPage = 0;
info.nMin = 0;
info.nPos = pos;
info.fMask = SIF_POS;
- HWND hWnd = GetHwnd();
- if ( hWnd )
- ::SetScrollInfo(hWnd, dir, &info, refresh);
-#else
- int wOrient;
- if ( orient == wxHORIZONTAL )
- wOrient = SB_HORZ;
- else
- wOrient = SB_VERT;
-
- HWND hWnd = GetHwnd();
- if ( hWnd )
- ::SetScrollPos(hWnd, wOrient, pos, refresh);
-#endif
+ ::SetScrollInfo(hWnd, dir, &info, refresh);
+#else // !__WIN95__
+ ::SetScrollPos(hWnd, dir, pos, refresh);
+#endif // __WIN95__/!__WIN95__
}
// New function that will replace some of the above.
void wxWindowMSW::ScrollWindow(int dx, int dy, const wxRect *prect)
{
RECT rect;
+ RECT *pr;
if ( prect )
{
rect.left = prect->x;
rect.top = prect->y;
rect.right = prect->x + prect->width;
rect.bottom = prect->y + prect->height;
+ pr = ▭
+ }
+ else
+ {
+ pr = NULL;
}
- ::ScrollWindow(GetHwnd(), dx, dy, prect ? &rect : NULL, NULL);
+ ::ScrollWindow(GetHwnd(), dx, dy, pr, pr);
}
static bool ScrollVertically(HWND hwnd, int kind, int count)
wxAssociateWinWithHandle(hwnd, this);
- m_oldWndProc = (WXFARPROC)::GetWindowLong(hwnd, GWL_WNDPROC);
+ m_oldWndProc = (WXFARPROC)::GetWindowLong((HWND)hWnd, GWL_WNDPROC);
// we don't need to subclass the window of our own class (in the Windows
// sense of the word)
- if ( (WXFARPROC) m_oldWndProc != (WXFARPROC) wxWndProc )
+ if ( !wxCheckWindowWndProc(hWnd, (WXFARPROC)wxWndProc) )
{
::SetWindowLong(hwnd, GWL_WNDPROC, (LONG) wxWndProc);
}
if ( m_oldWndProc )
{
- FARPROC wndProc = (FARPROC)::GetWindowLong(hwnd, GWL_WNDPROC);
- if ( wndProc != (FARPROC) m_oldWndProc )
+ if ( !wxCheckWindowWndProc((WXHWND)hwnd, m_oldWndProc) )
{
::SetWindowLong(hwnd, GWL_WNDPROC, (LONG) m_oldWndProc);
}
}
}
+bool wxCheckWindowWndProc(WXHWND hWnd, WXFARPROC wndProc)
+{
+#if wxUSE_UNICODE_MSLU
+ // VS: We can't use GetWindowLong(hwnd, GWL_WNDPROC) together with unicows.dll
+ // because it doesn't return pointer to the real wnd proc but rather a handle
+ // of a fake proc that does Unicode<->ANSI translation.
+ //
+ // The hack bellow works, because WNDCLASS contains original window handler
+ // rather that the unicows fake one. This may not be on purpose, though; if
+ // it stops working with future versions of unicows.dll, we can override
+ // unicows hooks by setting Unicows_{Set,Get}WindowLong and
+ // Unicows_RegisterClass to our own versions that keep track of
+ // fake<->real wnd proc mapping.
+ //
+ // FIXME: Doesn't handle wnd procs set by SetWindowLong, only these set
+ // with RegisterClass!!
+
+ if ( wxUsingUnicowsDll() )
+ {
+ static wxChar buffer[512];
+ WNDCLASS cls;
+
+ ::GetClassName((HWND)hWnd, buffer, 512);
+ ::GetClassInfo(wxGetInstance(), buffer, &cls);
+ return wndProc == (WXFARPROC)cls.lpfnWndProc;
+ }
+ else
+#endif
+ {
+ return wndProc == (WXFARPROC)::GetWindowLong((HWND)hWnd, GWL_WNDPROC);
+ }
+}
+
+// ----------------------------------------------------------------------------
+// Style handling
+// ----------------------------------------------------------------------------
+
+void wxWindowMSW::SetWindowStyleFlag(long flags)
+{
+ long flagsOld = GetWindowStyleFlag();
+ if ( flags == flagsOld )
+ return;
+
+ // update the internal variable
+ wxWindowBase::SetWindowStyleFlag(flags);
+
+ // now update the Windows style as well if needed - and if the window had
+ // been already created
+ if ( !GetHwnd() )
+ return;
+
+ WXDWORD exstyle, exstyleOld;
+ long style = MSWGetStyle(flags, &exstyle),
+ styleOld = MSWGetStyle(flagsOld, &exstyleOld);
+
+ if ( style != styleOld )
+ {
+ // some flags (e.g. WS_VISIBLE or WS_DISABLED) should not be changed by
+ // this function so instead of simply setting the style to the new
+ // value we clear the bits which were set in styleOld but are set in
+ // the new one and set the ones which were not set before
+ long styleReal = ::GetWindowLong(GetHwnd(), GWL_STYLE);
+ styleReal &= ~styleOld;
+ styleReal |= style;
+
+ ::SetWindowLong(GetHwnd(), GWL_STYLE, styleReal);
+ }
+
+ // and the extended style
+ if ( exstyle != exstyleOld )
+ {
+ long exstyleReal = ::GetWindowLong(GetHwnd(), GWL_EXSTYLE);
+ exstyleReal &= ~exstyleOld;
+ exstyleReal |= exstyle;
+
+ ::SetWindowLong(GetHwnd(), GWL_EXSTYLE, exstyleReal);
+
+ // we must call SetWindowPos() to flash the cached extended style and
+ // also to make the change to wxSTAY_ON_TOP style take effect: just
+ // setting the style simply doesn't work
+ if ( !::SetWindowPos(GetHwnd(),
+ exstyleReal & WS_EX_TOPMOST ? HWND_TOPMOST
+ : HWND_NOTOPMOST,
+ 0, 0, 0, 0,
+ SWP_NOMOVE | SWP_NOSIZE) )
+ {
+ wxLogLastError(_T("SetWindowPos"));
+ }
+ }
+}
+
+WXDWORD wxWindowMSW::MSWGetStyle(long flags, WXDWORD *exstyle) const
+{
+ // translate the style
+ WXDWORD style = WS_CHILD;
+
+ if ( flags & wxCLIP_CHILDREN )
+ style |= WS_CLIPCHILDREN;
+
+ if ( flags & wxCLIP_SIBLINGS )
+ style |= WS_CLIPSIBLINGS;
+
+ wxBorder border = (wxBorder)(flags & wxBORDER_MASK);
+ if ( border != wxBORDER_NONE && border != wxBORDER_DEFAULT )
+ style |= WS_BORDER;
+
+ // now deal with ext style if the caller wants it
+ if ( exstyle )
+ {
+ *exstyle = 0;
+
+ if ( flags & wxTRANSPARENT_WINDOW )
+ *exstyle |= WS_EX_TRANSPARENT;
+
+ switch ( flags & wxBORDER_MASK )
+ {
+ default:
+ wxFAIL_MSG( _T("unknown border style") );
+ // fall through
+
+ case wxBORDER_NONE:
+ case wxBORDER_SIMPLE:
+ case wxBORDER_DEFAULT:
+ break;
+
+ case wxBORDER_STATIC:
+ *exstyle |= WS_EX_STATICEDGE;
+ break;
+
+ case wxBORDER_RAISED:
+ *exstyle |= WS_EX_WINDOWEDGE;
+ break;
+
+ case wxBORDER_SUNKEN:
+ *exstyle |= WS_EX_CLIENTEDGE;
+ break;
+
+ case wxBORDER_DOUBLE:
+ *exstyle |= WS_EX_DLGMODALFRAME;
+ break;
+ }
+ }
+
+ return style;
+}
+
// Make a Windows extended style from the given wxWindows window style
WXDWORD wxWindowMSW::MakeExtendedStyle(long style, bool eliminateBorders)
{
// Check if we need to send a LEAVE event
if ( m_mouseInWindow )
{
+ // note that we should generate the leave event whether the window has
+ // or doesn't have mouse capture
if ( !IsMouseInWindow() )
{
// Generate a LEAVE event
static inline void SendSetRedraw(HWND hwnd, bool on)
{
+#ifndef __WXMICROWIN__
::SendMessage(hwnd, WM_SETREDRAW, (WPARAM)on, 0);
+#endif
}
void wxWindowMSW::Freeze()
void wxWindowMSW::DoMoveWindow(int x, int y, int width, int height)
{
+ // TODO: is this consistent with other platforms?
+ // Still, negative width or height shouldn't be allowed
+ if (width < 0)
+ width = 0;
+ if (height < 0)
+ height = 0;
if ( !::MoveWindow(GetHwnd(), x, y, width, height, TRUE) )
{
wxLogLastError(wxT("MoveWindow"));
void wxWindowMSW::DoSetClientSize(int width, int height)
{
- wxWindow *parent = GetParent();
- HWND hWnd = GetHwnd();
- HWND hParentWnd = (HWND) 0;
- if ( parent )
- hParentWnd = (HWND) parent->GetHWND();
+ // setting the client size is less obvious than it it could have been
+ // because in the result of changing the total size the window scrollbar
+ // may [dis]appear and/or its menubar may [un]wrap and so the client size
+ // will not be correct as the difference between the total and client size
+ // changes - so we keep changing it until we get it right
+ //
+ // normally this loop shouldn't take more than 3 iterations (usually 1 but
+ // if scrollbars [dis]appear as the result of the first call, then 2 and it
+ // may become 3 if the window had 0 size originally and so we didn't
+ // calculate the scrollbar correction correctly during the first iteration)
+ // but just to be on the safe side we check for it instead of making it an
+ // "infinite" loop (i.e. leaving break inside as the only way to get out)
+ for ( int i = 0; i < 4; i++ )
+ {
+ RECT rectClient;
+ ::GetClientRect(GetHwnd(), &rectClient);
- RECT rect;
- ::GetClientRect(hWnd, &rect);
+ // if the size is already ok, stop here (rectClient.left = top = 0)
+ if ( (rectClient.right == width || width == -1) &&
+ (rectClient.bottom == height || height == -1) )
+ {
+ break;
+ }
- RECT rect2;
- GetWindowRect(hWnd, &rect2);
+ int widthClient = width,
+ heightClient = height;
- // Find the difference between the entire window (title bar and all)
- // and the client area; add this to the new client size to move the
- // window
- int actual_width = rect2.right - rect2.left - rect.right + width;
- int actual_height = rect2.bottom - rect2.top - rect.bottom + height;
+ // Find the difference between the entire window (title bar and all)
+ // and the client area; add this to the new client size to move the
+ // window
+ RECT rectWin;
+ ::GetWindowRect(GetHwnd(), &rectWin);
- // If there's a parent, must subtract the parent's top left corner
- // since MoveWindow moves relative to the parent
+ widthClient += rectWin.right - rectWin.left - rectClient.right;
+ heightClient += rectWin.bottom - rectWin.top - rectClient.bottom;
- POINT point;
- point.x = rect2.left;
- point.y = rect2.top;
- if ( parent )
- {
- ::ScreenToClient(hParentWnd, &point);
- }
+ POINT point;
+ point.x = rectWin.left;
+ point.y = rectWin.top;
- DoMoveWindow(point.x, point.y, actual_width, actual_height);
+ // MoveWindow positions the child windows relative to the parent, so
+ // adjust if necessary
+ if ( !IsTopLevel() )
+ {
+ wxWindow *parent = GetParent();
+ if ( parent )
+ {
+ ::ScreenToClient(GetHwndOf(parent), &point);
+ }
+ }
- wxSizeEvent event(wxSize(width, height), m_windowId);
- event.SetEventObject(this);
- GetEventHandler()->ProcessEvent(event);
+ DoMoveWindow(point.x, point.y, widthClient, heightClient);
+ }
}
// For implementation purposes - sometimes decorations make the client area
return TRUE;
}
else // no default button
-#endif // wxUSE_BUTTON
{
- // no special function for enter and don't even
- // let IsDialogMessage() have it: it seems to
- // do something really strange with it
- return FALSE;
+#endif // wxUSE_BUTTON
+ // this is a quick and dirty test for a text
+ // control
+ if ( !(lDlgCode & DLGC_HASSETSEL) )
+ {
+ // don't process Enter, the control might
+ // need it for itself and don't let
+ // ::IsDialogMessage() have it as it can
+ // eat the Enter events sometimes
+ return FALSE;
+ }
+ //else: 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
}
}
}
}
#endif // 1/0
- if ( ::IsDialogMessage(GetHwnd(), msg) )
+ // we handle VK_ESCAPE ourselves in wxDialog::OnCharHook() and we
+ // shouldn't let IsDialogMessage() get it as it _always_ eats the
+ // message even when there is no cancel button and when the message is
+ // needed by the control itself: in particular, it prevents the tree in
+ // place edit control from being closed with Escape in a dialog
+ if ( msg->message != WM_KEYDOWN || msg->wParam != VK_ESCAPE )
{
- // IsDialogMessage() did something...
- return TRUE;
+ if ( ::IsDialogMessage(GetHwnd(), msg) )
+ {
+ // IsDialogMessage() did something...
+ return TRUE;
+ }
}
}
#endif // __WXUNIVERSAL__
#if wxUSE_ACCEL && !defined(__WXUNIVERSAL__)
return m_acceleratorTable.Translate(this, pMsg);
#else
+ (void) pMsg;
return FALSE;
#endif // wxUSE_ACCEL
}
-bool wxWindowMSW::MSWShouldPreProcessMessage(WXMSG* pMsg)
+bool wxWindowMSW::MSWShouldPreProcessMessage(WXMSG* WXUNUSED(pMsg))
{
// preprocess all messages by default
return TRUE;
break;
case WM_CLOSE:
+#ifdef __WXUNIVERSAL__
+ // Universal uses its own wxFrame/wxDialog, so we don't receive
+ // close events unless we have this.
+ Close();
+ processed = TRUE;
+ rc.result = TRUE;
+#else
// don't let the DefWindowProc() destroy our window - we'll do it
// ourselves in ~wxWindow
processed = TRUE;
rc.result = TRUE;
+#endif
break;
case WM_SHOWWINDOW:
case WM_MBUTTONDOWN:
case WM_MBUTTONUP:
case WM_MBUTTONDBLCLK:
- {
- processed = FALSE;
+ {
#ifdef __WXMICROWIN__
// MicroWindows seems to ignore the fact that a window is
// disabled. So catch mouse events and throw them away if
// necessary.
wxWindowMSW* win = this;
- while (win)
+ for ( ;; )
{
if (!win->IsEnabled())
{
processed = TRUE;
break;
}
+
win = win->GetParent();
- if (win && win->IsTopLevel())
+ if ( !win || win->IsTopLevel() )
break;
}
-#endif // __WXMICROWIN__
+
if (!processed)
+#endif // __WXMICROWIN__
{
- if (message == WM_LBUTTONDOWN && AcceptsFocus())
+ // VZ: why do we need it here? DefWindowProc() is supposed
+ // to do this for us anyhow
+ if ( message == WM_LBUTTONDOWN && AcceptsFocus() )
SetFocus();
- processed = HandleMouseEvent(message,
- GET_X_LPARAM(lParam),
- GET_Y_LPARAM(lParam),
- wParam);
- }
- break;
- }
-#ifdef __WXMICROWIN__
- case WM_NCLBUTTONDOWN:
- case WM_NCLBUTTONUP:
- case WM_NCLBUTTONDBLCLK:
- case WM_NCRBUTTONDOWN:
- case WM_NCRBUTTONUP:
- case WM_NCRBUTTONDBLCLK:
-#if 0
- case WM_NCMBUTTONDOWN:
- case WM_NCMBUTTONUP:
- case WM_NCMBUTTONDBLCLK:
-#endif
- {
- // MicroWindows seems to ignore the fact that a window
- // is disabled. So catch mouse events and throw them away if necessary.
- processed = FALSE;
- wxWindowMSW* win = this;
- while (win)
- {
- if (!win->IsEnabled())
- {
- processed = TRUE;
- break;
- }
- win = win->GetParent();
- if (win && win->IsTopLevel())
- break;
+ int x = GET_X_LPARAM(lParam),
+ y = GET_Y_LPARAM(lParam);
+
+ // redirect the event to a static control if necessary
+ wxWindow *win = FindWindowForMouseEvent(this, &x, &y);
+
+ processed = win->HandleMouseEvent(message, x, y, wParam);
}
- break;
}
-#endif // __WXMICROWIN__
+ break;
#ifdef MM_JOY1MOVE
case MM_JOY1MOVE:
#endif // defined(WM_DRAWITEM)
case WM_GETDLGCODE:
- if ( m_lDlgCode )
+ if ( GetWindowStyleFlag() & wxWANTS_CHARS )
{
- rc.result = m_lDlgCode;
+ // want everything: i.e. all keys and WM_CHAR message
+ rc.result = DLGC_WANTARROWS | DLGC_WANTCHARS |
+ DLGC_WANTTAB | DLGC_WANTMESSAGE;
processed = TRUE;
}
//else: get the dlg code from the DefWindowProc()
case WM_SYSKEYDOWN:
case WM_KEYDOWN:
- // If this has been processed by an event handler,
- // return 0 now (we've handled it).
- if ( HandleKeyDown((WORD) wParam, lParam) )
+ // If this has been processed by an event handler, return 0 now
+ // (we've handled it).
+ m_lastKeydownProcessed = HandleKeyDown((WORD) wParam, lParam);
+ if ( m_lastKeydownProcessed )
{
processed = TRUE;
-
- break;
- }
-
- // we consider these message "not interesting" to OnChar
- if ( wParam == VK_SHIFT || wParam == VK_CONTROL )
- {
- processed = TRUE;
-
break;
}
switch ( wParam )
{
- // avoid duplicate messages to OnChar for these ASCII keys: they
- // will be translated by TranslateMessage() and received in WM_CHAR
+ // we consider these message "not interesting" to OnChar, so
+ // just don't do anything more with them
+ case VK_SHIFT:
+ case VK_CONTROL:
+ case VK_MENU:
+ case VK_CAPITAL:
+ case VK_NUMLOCK:
+ case VK_SCROLL:
+ processed = TRUE;
+ break;
+
+ // avoid duplicate messages to OnChar for these ASCII keys:
+ // they will be translated by TranslateMessage() and received
+ // in WM_CHAR
case VK_ESCAPE:
case VK_SPACE:
case VK_RETURN:
case VK_TAB:
case VK_ADD:
case VK_SUBTRACT:
- // but set processed to FALSE, not TRUE to still pass them to
- // the control's default window proc - otherwise built-in
- // keyboard handling won't work
+ case VK_MULTIPLY:
+ case VK_DIVIDE:
+ case VK_OEM_1:
+ case VK_OEM_2:
+ case VK_OEM_3:
+ case VK_OEM_4:
+ case VK_OEM_5:
+ case VK_OEM_6:
+ case VK_OEM_7:
+ case VK_OEM_PLUS:
+ case VK_OEM_COMMA:
+ case VK_OEM_MINUS:
+ case VK_OEM_PERIOD:
+ // but set processed to FALSE, not TRUE to still pass them
+ // to the control's default window proc - otherwise
+ // built-in keyboard handling won't work
processed = FALSE;
break;
break;
#endif // VK_APPS
- case VK_LEFT:
- case VK_RIGHT:
- case VK_DOWN:
- case VK_UP:
default:
+ // do generate a CHAR event
processed = HandleChar((WORD)wParam, lParam);
+
}
break;
case WM_SYSCHAR:
case WM_CHAR: // Always an ASCII character
- processed = HandleChar((WORD)wParam, lParam, TRUE);
+ if ( m_lastKeydownProcessed )
+ {
+ // The key was handled in the EVT_KEY_DOWN and handling
+ // a key in an EVT_KEY_DOWN handler is meant, by
+ // design, to prevent EVT_CHARs from happening
+ m_lastKeydownProcessed = FALSE;
+ processed = TRUE;
+ }
+ else
+ {
+ processed = HandleChar((WORD)wParam, lParam, TRUE);
+ }
break;
case WM_HSCROLL:
break;
#endif // !__WXMICROWIN__
- // the return value for this message is ignored
case WM_SYSCOLORCHANGE:
+ // the return value for this message is ignored
processed = HandleSysColorChange();
break;
+ case WM_DISPLAYCHANGE:
+ processed = HandleDisplayChange();
+ break;
+
case WM_PALETTECHANGED:
processed = HandlePaletteChanged((WXHWND) (HWND) wParam);
break;
+ case WM_CAPTURECHANGED:
+ processed = HandleCaptureChanged((WXHWND) (HWND) lParam);
+ break;
+
case WM_QUERYNEWPALETTE:
processed = HandleQueryNewPalette();
break;
}
break;
#endif // __WIN32__
-
- // unfortunately this doesn't really work as then window which
- // doesn't accept focus doesn't get any mouse events neither which
- // means it can't get any input at all
-#if 0 //def __WXUNIVERSAL__
- case WM_NCHITTEST:
- // we shouldn't allow the windows which don't want to get focus to
- // get it
- if ( !AcceptsFocus() )
- {
- rc.result = HTTRANSPARENT;
- processed = TRUE;
- }
- break;
-#endif // __WXUNIVERSAL__
}
if ( !processed )
#endif // __WXDEBUG__
if (!oldWin)
{
- wxWinHandleHash->Put((long)hWnd, win);
+ wxWinHandleHash->Put((long)hWnd, (wxWindow *)win);
}
}
{
}
-void wxWindowMSW::MSWDetachWindowMenu()
-{
-#ifndef __WXUNIVERSAL__
- if ( m_hMenu )
- {
- wxChar buf[1024];
- HMENU hMenu = (HMENU)m_hMenu;
-
- int N = ::GetMenuItemCount(hMenu);
- for ( int i = 0; i < N; i++ )
- {
- if ( !::GetMenuString(hMenu, i, buf, WXSIZEOF(buf), MF_BYPOSITION) )
- {
- wxLogLastError(wxT("GetMenuString"));
-
- continue;
- }
-
- if ( wxStrcmp(buf, _("&Window")) == 0 )
- {
- if ( !::RemoveMenu(hMenu, i, MF_BYPOSITION) )
- {
- wxLogLastError(wxT("RemoveMenu"));
- }
-
- break;
- }
- }
- }
-#endif // __WXUNIVERSAL__
-}
-
bool 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;
if ( pos.x == -1 )
}
else
{
+ // OTOH, if x is not set to CW_USEDEFAULT, y shouldn't be set to it
+ // neither because it is not handled as a special value by Windows then
+ // and so we have to choose some default value for it
x = pos.x;
- y = pos.y == -1 ? CW_USEDEFAULT : pos.y;
+ y = pos.y == -1 ? DEFAULT_Y : pos.y;
nonDefault = TRUE;
}
*/
if ( size.x == -1 )
{
- // as abobe, h is not used at all in this case anyhow
+ // as above, h is not used at all in this case anyhow
w =
h = CW_USEDEFAULT;
}
else
{
+ // and, again as above, we can't set the height to CW_USEDEFAULT here
w = size.x;
- h = size.y == -1 ? CW_USEDEFAULT : size.y;
+ h = size.y == -1 ? DEFAULT_H : size.y;
nonDefault = TRUE;
}
SubclassWin(m_hWnd);
- SetFont(wxSystemSettings::GetSystemFont(wxSYS_DEFAULT_GUI_FONT));
+ SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
return TRUE;
}
wxSysColourChangedEvent event;
event.SetEventObject(this);
+ (void)GetEventHandler()->ProcessEvent(event);
+
+ // always let the system carry on the default processing to allow the
+ // native controls to react to the colours update
+ return FALSE;
+}
+
+bool wxWindowMSW::HandleDisplayChange()
+{
+ wxDisplayChangedEvent event;
+ event.SetEventObject(this);
+
return GetEventHandler()->ProcessEvent(event);
}
bool wxWindowMSW::HandlePaletteChanged(WXHWND hWndPalChange)
{
+#if wxUSE_PALETTE
+ // same as below except we don't respond to our own messages
+ if ( hWndPalChange != GetHWND() )
+ {
+ // check to see if we our our parents have a custom palette
+ wxWindow *win = this;
+ while ( win && !win->HasCustomPalette() )
+ {
+ win = win->GetParent();
+ }
+
+ if ( win && win->HasCustomPalette() )
+ {
+ // realize the palette to see whether redrawing is needed
+ HDC hdc = ::GetDC((HWND) hWndPalChange);
+ win->m_palette.SetHPALETTE((WXHPALETTE)
+ ::SelectPalette(hdc, GetHpaletteOf(win->m_palette), FALSE));
+
+ int result = ::RealizePalette(hdc);
+
+ // restore the palette (before releasing the DC)
+ win->m_palette.SetHPALETTE((WXHPALETTE)
+ ::SelectPalette(hdc, GetHpaletteOf(win->m_palette), FALSE));
+ ::RealizePalette(hdc);
+ ::ReleaseDC((HWND) hWndPalChange, hdc);
+
+ // now check for the need to redraw
+ if (result > 0)
+ InvalidateRect((HWND) hWndPalChange, NULL, TRUE);
+ }
+
+ }
+#endif // wxUSE_PALETTE
+
wxPaletteChangedEvent event(GetId());
event.SetEventObject(this);
event.SetChangedWindow(wxFindWinFromHandle(hWndPalChange));
return GetEventHandler()->ProcessEvent(event);
}
+bool wxWindowMSW::HandleCaptureChanged(WXHWND hWndGainedCapture)
+{
+ wxMouseCaptureChangedEvent event(GetId(), wxFindWinFromHandle(hWndGainedCapture));
+ event.SetEventObject(this);
+
+ return GetEventHandler()->ProcessEvent(event);
+}
+
bool wxWindowMSW::HandleQueryNewPalette()
{
+
+#if wxUSE_PALETTE
+ // check to see if we our our parents have a custom palette
+ wxWindow *win = this;
+ while (!win->HasCustomPalette() && win->GetParent()) win = win->GetParent();
+ if (win->HasCustomPalette()) {
+ /* realize the palette to see whether redrawing is needed */
+ HDC hdc = GetDC((HWND) GetHWND());
+ win->m_palette.SetHPALETTE( (WXHPALETTE)
+ ::SelectPalette(hdc, (HPALETTE) win->m_palette.GetHPALETTE(), FALSE) );
+
+ int result = ::RealizePalette(hdc);
+ /* restore the palette (before releasing the DC) */
+ win->m_palette.SetHPALETTE( (WXHPALETTE)
+ ::SelectPalette(hdc, (HPALETTE) win->m_palette.GetHPALETTE(), TRUE) );
+ ::RealizePalette(hdc);
+ ::ReleaseDC((HWND) GetHWND(), hdc);
+ /* now check for the need to redraw */
+ if (result > 0)
+ ::InvalidateRect((HWND) GetHWND(), NULL, TRUE);
+ }
+#endif // wxUSE_PALETTE
+
wxQueryNewPaletteEvent event(GetId());
event.SetEventObject(this);
}
// Responds to colour changes: passes event on to children.
-void wxWindowMSW::OnSysColourChanged(wxSysColourChangedEvent& event)
+void wxWindowMSW::OnSysColourChanged(wxSysColourChangedEvent& WXUNUSED(event))
{
- wxNode *node = GetChildren().First();
+ // the top level window also reset the standard colour map as it might have
+ // changed (there is no need to do it for the non top level windows as we
+ // only have to do it once)
+ if ( IsTopLevel() )
+ {
+ // FIXME-MT
+ gs_hasStdCmap = FALSE;
+ }
+ wxWindowList::Node *node = GetChildren().GetFirst();
while ( node )
{
- // Only propagate to non-top-level windows
- wxWindow *win = (wxWindow *)node->Data();
- if ( win->GetParent() )
+ // Only propagate to non-top-level windows because Windows already
+ // sends this event to all top-level ones
+ wxWindow *win = node->GetData();
+ if ( !win->IsTopLevel() )
{
- wxSysColourChangedEvent event2;
- event.m_eventObject = win;
- win->GetEventHandler()->ProcessEvent(event2);
+ // we need to send the real WM_SYSCOLORCHANGE and not just trigger
+ // EVT_SYS_COLOUR_CHANGED call because the latter wouldn't work for
+ // the standard controls
+ ::SendMessage(GetHwndOf(win), WM_SYSCOLORCHANGE, 0, 0);
}
- node = node->Next();
+ node = node->GetNext();
+ }
+
+ // update the colours we use if they were not set explicitly by the user:
+ // this must be done or OnCtlColor() would continue to use the old colours
+ if ( !m_hasFgCol )
+ {
+ m_foregroundColour = wxSystemSettings::
+ GetSystemColour(wxSYS_COLOUR_WINDOWTEXT);
+ }
+
+ if ( !m_hasBgCol )
+ {
+ m_backgroundColour = wxSystemSettings::
+ GetSystemColour(wxSYS_COLOUR_BTNFACE);
}
}
+extern wxCOLORMAP *wxGetStdColourMap()
+{
+ static COLORREF s_stdColours[wxSTD_COL_MAX];
+ static wxCOLORMAP s_cmap[wxSTD_COL_MAX];
+
+ if ( !gs_hasStdCmap )
+ {
+ static bool s_coloursInit = FALSE;
+
+ if ( !s_coloursInit )
+ {
+ // When a bitmap is loaded, the RGB values can change (apparently
+ // because Windows adjusts them to care for the old programs always
+ // using 0xc0c0c0 while the transparent colour for the new Windows
+ // versions is different). But we do this adjustment ourselves so
+ // we want to avoid Windows' "help" and for this we need to have a
+ // reference bitmap which can tell us what the RGB values change
+ // to.
+ wxBitmap stdColourBitmap(_T("wxBITMAP_STD_COLOURS"));
+ if ( stdColourBitmap.Ok() )
+ {
+ // the pixels in the bitmap must correspond to wxSTD_COL_XXX!
+ wxASSERT_MSG( stdColourBitmap.GetWidth() == wxSTD_COL_MAX,
+ _T("forgot to update wxBITMAP_STD_COLOURS!") );
+
+ wxMemoryDC memDC;
+ memDC.SelectObject(stdColourBitmap);
+
+ wxColour colour;
+ for ( size_t i = 0; i < WXSIZEOF(s_stdColours); i++ )
+ {
+ memDC.GetPixel(i, 0, &colour);
+ s_stdColours[i] = wxColourToRGB(colour);
+ }
+ }
+ else // wxBITMAP_STD_COLOURS couldn't be loaded
+ {
+ s_stdColours[0] = RGB(000,000,000); // black
+ s_stdColours[1] = RGB(128,128,128); // dark grey
+ s_stdColours[2] = RGB(192,192,192); // light grey
+ s_stdColours[3] = RGB(255,255,255); // white
+ //s_stdColours[4] = RGB(000,000,255); // blue
+ //s_stdColours[5] = RGB(255,000,255); // magenta
+ }
+
+ s_coloursInit = TRUE;
+ }
+
+ gs_hasStdCmap = TRUE;
+
+ // create the colour map
+#define INIT_CMAP_ENTRY(col) \
+ s_cmap[wxSTD_COL_##col].from = s_stdColours[wxSTD_COL_##col]; \
+ s_cmap[wxSTD_COL_##col].to = ::GetSysColor(COLOR_##col)
+
+ INIT_CMAP_ENTRY(BTNTEXT);
+ INIT_CMAP_ENTRY(BTNSHADOW);
+ INIT_CMAP_ENTRY(BTNFACE);
+ INIT_CMAP_ENTRY(BTNHIGHLIGHT);
+
+#undef INIT_CMAP_ENTRY
+ }
+
+ return s_cmap;
+}
+
// ---------------------------------------------------------------------------
// painting
// ---------------------------------------------------------------------------
return GetEventHandler()->ProcessEvent(event);
}
-bool wxWindowMSW::HandleSize(int w, int h, WXUINT WXUNUSED(flag))
+bool wxWindowMSW::HandleSize(int WXUNUSED(w), int WXUNUSED(h),
+ WXUINT WXUNUSED(flag))
{
- wxSizeEvent event(wxSize(w, h), m_windowId);
+ // don't use w and h parameters as they specify the client size while
+ // according to the docs EVT_SIZE handler is supposed to receive the total
+ // size
+ wxSizeEvent event(GetSize(), m_windowId);
event.SetEventObject(this);
return GetEventHandler()->ProcessEvent(event);
bool rc = FALSE;
- if ( m_minWidth != -1 )
+ int minWidth = GetMinWidth(),
+ minHeight = GetMinHeight(),
+ maxWidth = GetMaxWidth(),
+ maxHeight = GetMaxHeight();
+
+ if ( minWidth != -1 )
{
- info->ptMinTrackSize.x = m_minWidth;
+ info->ptMinTrackSize.x = minWidth;
rc = TRUE;
}
- if ( m_minHeight != -1 )
+ if ( minHeight != -1 )
{
- info->ptMinTrackSize.y = m_minHeight;
+ info->ptMinTrackSize.y = minHeight;
rc = TRUE;
}
- if ( m_maxWidth != -1 )
+ if ( maxWidth != -1 )
{
- info->ptMaxTrackSize.x = m_maxWidth;
+ info->ptMaxTrackSize.x = maxWidth;
rc = TRUE;
}
- if ( m_maxHeight != -1 )
+ if ( maxHeight != -1 )
{
- info->ptMaxTrackSize.y = m_maxHeight;
+ info->ptMaxTrackSize.y = maxHeight;
rc = TRUE;
}
#endif // wxUSE_MENUS_NATIVE
wxWindow *win = NULL;
- if ( cmd == 0 || cmd == 1 ) // menu or accel - use id
+
+ // first try to find it from HWND - this works even with the broken
+ // programs using the same ids for different controls
+ if ( control )
{
- // must cast to a signed type before comparing with other ids!
- win = FindItem((signed short)id);
+ win = wxFindWinFromHandle(control);
}
- if ( !win && control )
+ // try the id
+ if ( !win )
{
- // find it from HWND - this works even with the broken programs using
- // the same ids for different controls
- win = wxFindWinFromHandle(control);
+ // must cast to a signed type before comparing with other ids!
+ win = FindItem((signed short)id);
}
if ( win )
#endif // wxUSE_MOUSEEVENT_HACK
}
+// Windows doesn't send the mouse events to the static controls (which are
+// transparent in the sense that their WM_NCHITTEST handler returns
+// HTTRANSPARENT) at all but we want all controls to receive the mouse events
+// and so we manually check if we don't have a child window under mouse and if
+// we do, send the event to it instead of the window Windows had sent WM_XXX
+// to.
+//
+// Notice that this is not done for the mouse move events because this could
+// (would?) be too slow, but only for clicks which means that the static texts
+// still don't get move, enter nor leave events.
+static wxWindowMSW *FindWindowForMouseEvent(wxWindow *win, int *x, int *y)
+{
+ wxCHECK_MSG( x && y, win, _T("NULL pointer in FindWindowForMouseEvent") );
+
+ // first try to find a non transparent child: this allows us to send events
+ // to a static text which is inside a static box, for example
+ POINT pt = { *x, *y };
+ HWND hwnd = GetHwndOf(win),
+ hwndUnderMouse;
+
+#ifdef __WIN32__
+ hwndUnderMouse = ::ChildWindowFromPointEx
+ (
+ hwnd,
+ pt,
+ CWP_SKIPINVISIBLE |
+ CWP_SKIPDISABLED |
+ CWP_SKIPTRANSPARENT
+ );
+
+ if ( !hwndUnderMouse || hwndUnderMouse == hwnd )
+#endif // __WIN32__
+ {
+ // now try any child window at all
+ hwndUnderMouse = ::ChildWindowFromPoint(hwnd, pt);
+ }
+
+ // check that we have a child window which is susceptible to receive mouse
+ // events: for this it must be shown and enabled
+ if ( hwndUnderMouse &&
+ hwndUnderMouse != hwnd &&
+ ::IsWindowVisible(hwndUnderMouse) &&
+ ::IsWindowEnabled(hwndUnderMouse) )
+ {
+ wxWindow *winUnderMouse = wxFindWinFromHandle((WXHWND)hwndUnderMouse);
+ if ( winUnderMouse )
+ {
+ // translate the mouse coords to the other window coords
+ win->ClientToScreen(x, y);
+ winUnderMouse->ScreenToClient(x, y);
+
+ win = winUnderMouse;
+ }
+ }
+
+ return win;
+}
+
bool wxWindowMSW::HandleMouseEvent(WXUINT msg, int x, int y, WXUINT flags)
{
// the mouse events take consecutive IDs from WM_MOUSEFIRST to
// HandleChar and HandleKeyDown/Up
wxKeyEvent wxWindowMSW::CreateKeyEvent(wxEventType evType,
int id,
- WXLPARAM lParam) const
+ WXLPARAM lParam,
+ WXWPARAM wParam) const
{
wxKeyEvent event(evType);
event.SetId(GetId());
event.m_eventObject = (wxWindow *)this; // const_cast
event.m_keyCode = id;
+ event.m_rawCode = (wxUint32) wParam;
+ event.m_rawFlags = (wxUint32) lParam;
event.SetTimestamp(s_currentMsg.time);
// translate the position to client coords
default:
ctrlDown = TRUE;
- id = id + 96;
+ id = id + 'a' - 1;
}
}
}
- else if ( (id = wxCharCodeMSWToWX(wParam)) == 0 )
+ else // we're called from WM_KEYDOWN
{
- // it's ASCII and will be processed here only when called from
- // WM_CHAR (i.e. when isASCII = TRUE), don't process it now
- id = -1;
- }
-
- if ( id != -1 )
- {
- wxKeyEvent event(CreateKeyEvent(wxEVT_CHAR, id, lParam));
- if ( ctrlDown )
+ id = wxCharCodeMSWToWX(wParam);
+ if ( id == 0 )
{
- event.m_controlDown = TRUE;
+ // it's ASCII and will be processed here only when called from
+ // WM_CHAR (i.e. when isASCII = TRUE), don't process it now
+ return FALSE;
}
+ }
- if ( GetEventHandler()->ProcessEvent(event) )
- return TRUE;
+ wxKeyEvent event(CreateKeyEvent(wxEVT_CHAR, id, lParam, wParam));
+ if ( ctrlDown )
+ {
+ event.m_controlDown = TRUE;
}
- return FALSE;
+ return GetEventHandler()->ProcessEvent(event);
}
bool wxWindowMSW::HandleKeyDown(WXWPARAM wParam, WXLPARAM lParam)
if ( id != -1 ) // VZ: does this ever happen (FIXME)?
{
- wxKeyEvent event(CreateKeyEvent(wxEVT_KEY_DOWN, id, lParam));
+ wxKeyEvent event(CreateKeyEvent(wxEVT_KEY_DOWN, id, lParam, wParam));
if ( GetEventHandler()->ProcessEvent(event) )
{
return TRUE;
if ( id != -1 ) // VZ: does this ever happen (FIXME)?
{
- wxKeyEvent event(CreateKeyEvent(wxEVT_KEY_UP, id, lParam));
+ wxKeyEvent event(CreateKeyEvent(wxEVT_KEY_UP, id, lParam, wParam));
if ( GetEventHandler()->ProcessEvent(event) )
return TRUE;
}
// ---------------------------------------------------------------------------
bool wxWindowMSW::MSWOnScroll(int orientation, WXWORD wParam,
- WXWORD pos, WXHWND control)
+ WXWORD pos, WXHWND control)
{
if ( control )
{
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_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;
+
default:
id = 0;
}
// FIXME: this is clearly not the best way to do it but I think we'll
// need to change HWND <-> wxWindow code more heavily than I can
// do it now to fix it
+#ifndef __WXMICROWIN__
if ( ::GetWindow(hwnd, GW_OWNER) )
{
// it's a dialog box, don't go upwards
break;
}
+#endif
hwnd = ::GetParent(hwnd);
win = wxFindWinFromHandle((WXHWND)hwnd);
{
POINT pt;
GetCursorPos( & pt );
+
return wxPoint(pt.x, pt.y);
}