#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
-
// ---------------------------------------------------------------------------
// 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
// ---------------------------------------------------------------------------
{
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
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);
+ }
}
-
- // 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
+#endif // Debug
}
}
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.
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!!
+
+ static wxChar buffer[512];
+ WNDCLASS cls;
+
+ ::GetClassName((HWND)hWnd, buffer, 512);
+ ::GetClassInfo(wxGetInstance(), buffer, &cls);
+ return wndProc == (WXFARPROC)cls.lpfnWndProc;
+#else
+ return wndProc == (WXFARPROC)::GetWindowLong((HWND)hWnd, GWL_WNDPROC);
+#endif
+}
+
+
// Make a Windows extended style from the given wxWindows window style
WXDWORD wxWindowMSW::MakeExtendedStyle(long style, bool eliminateBorders)
{
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 && rectClient.bottom == height )
+ {
+ break;
+ }
- RECT rect2;
- GetWindowRect(hWnd, &rect2);
+ if ( i == 3 )
+ {
+ // how did it happen? maybe OnSize() handler does something really
+ // strange in this class?
+ wxFAIL_MSG( _T("logic error in DoSetClientSize") );
- // 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;
+ break;
+ }
- // If there's a parent, must subtract the parent's top left corner
- // since MoveWindow moves relative to the parent
+ int widthClient = width,
+ heightClient = height;
- POINT point;
- point.x = rect2.left;
- point.y = rect2.top;
- if ( parent )
- {
- ::ScreenToClient(hParentWnd, &point);
- }
+ // 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);
- DoMoveWindow(point.x, point.y, actual_width, actual_height);
+ widthClient += rectWin.right - rectWin.left - rectClient.right;
+ heightClient += rectWin.bottom - rectWin.top - rectClient.bottom;
- wxSizeEvent event(wxSize(width, height), m_windowId);
- event.SetEventObject(this);
- GetEventHandler()->ProcessEvent(event);
+ POINT point;
+ point.x = rectWin.left;
+ point.y = rectWin.top;
+
+ // MoveWindow positions the child windows relative to the parent, so
+ // adjust if necessary
+ if ( !IsTopLevel() )
+ {
+ wxWindow *parent = GetParent();
+ if ( parent )
+ {
+ ::ScreenToClient(GetHwndOf(parent), &point);
+ }
+ }
+
+ DoMoveWindow(point.x, point.y, widthClient, heightClient);
+ }
}
// For implementation purposes - sometimes decorations make the client area
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;
// wxWindow <-> HWND map
// ----------------------------------------------------------------------------
-wxList *wxWinHandleList = NULL;
+wxWinHashTable *wxWinHandleHash = NULL;
wxWindow *wxFindWinFromHandle(WXHWND hWnd)
{
- wxNode *node = wxWinHandleList->Find((long)hWnd);
- if ( !node )
- return NULL;
- return (wxWindow *)node->Data();
+ return wxWinHandleHash->Get((long)hWnd);
}
void wxAssociateWinWithHandle(HWND hWnd, wxWindowMSW *win)
#endif // __WXDEBUG__
if (!oldWin)
{
- wxWinHandleList->Append((long)hWnd, win);
+ wxWinHandleHash->Put((long)hWnd, (wxWindow *)win);
}
}
void wxRemoveHandleAssociation(wxWindowMSW *win)
{
- wxWinHandleList->DeleteObject(win);
+ wxWinHandleHash->Delete((long)win->GetHWND());
}
// ----------------------------------------------------------------------------
{
}
-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,
SubclassWin(m_hWnd);
- SetFont(wxSystemSettings::GetSystemFont(wxSYS_DEFAULT_GUI_FONT));
+ SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
return TRUE;
}
{
#ifndef __WXMICROWIN__
HDROP hFilesInfo = (HDROP) wParam;
- POINT dropPoint;
- DragQueryPoint(hFilesInfo, (LPPOINT) &dropPoint);
// Get the total number of files dropped
- WORD gwFilesDropped = (WORD)::DragQueryFile
+ UINT gwFilesDropped = ::DragQueryFile
(
(HDROP)hFilesInfo,
(UINT)-1,
);
wxString *files = new wxString[gwFilesDropped];
- int wIndex;
- for (wIndex=0; wIndex < (int)gwFilesDropped; wIndex++)
+ for ( UINT wIndex = 0; wIndex < gwFilesDropped; wIndex++ )
{
- DragQueryFile (hFilesInfo, wIndex, (LPTSTR) wxBuffer, 1000);
- files[wIndex] = wxBuffer;
+ // first get the needed buffer length (+1 for terminating NUL)
+ size_t len = ::DragQueryFile(hFilesInfo, wIndex, NULL, 0) + 1;
+
+ // and now get the file name
+ ::DragQueryFile(hFilesInfo, wIndex,
+ files[wIndex].GetWriteBuf(len), len);
+
+ files[wIndex].UngetWriteBuf();
}
DragFinish (hFilesInfo);
wxDropFilesEvent event(wxEVT_DROP_FILES, gwFilesDropped, files);
event.m_eventObject = this;
+
+ POINT dropPoint;
+ DragQueryPoint(hFilesInfo, (LPPOINT) &dropPoint);
event.m_pos.x = dropPoint.x;
event.m_pos.y = dropPoint.y;
- bool rc = GetEventHandler()->ProcessEvent(event);
-
- delete[] files;
-
- return rc;
+ return GetEventHandler()->ProcessEvent(event);
#else // __WXMICROWIN__
return FALSE;
#endif
wxSysColourChangedEvent event;
event.SetEventObject(this);
- return GetEventHandler()->ProcessEvent(event);
+ (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::HandleCtlColor(WXHBRUSH *brush,
// Responds to colour changes: passes event on to children.
void wxWindowMSW::OnSysColourChanged(wxSysColourChangedEvent& 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
// ---------------------------------------------------------------------------
bool wxWindowMSW::HandleCommand(WXWORD id, WXWORD cmd, WXHWND control)
{
#if wxUSE_MENUS_NATIVE
- if ( wxCurrentPopupMenu )
+ if ( !cmd && wxCurrentPopupMenu )
{
wxMenu *popupMenu = wxCurrentPopupMenu;
wxCurrentPopupMenu = NULL;
}
#endif // wxUSE_MENUS_NATIVE
- wxWindow *win = (wxWindow*) NULL;
- if ( cmd == 0 || cmd == 1 ) // menu or accel - use id
+ wxWindow *win = NULL;
+
+ // first try to find it from HWND - this works even with the broken
+ // programs using the same ids for different controls
+ if ( control )
{
- // must cast to a signed type before comparing with other ids!
- win = FindItem((signed short)id);
+ win = wxFindWinFromHandle(control);
}
- if (!win && control)
+ // try the id
+ if ( !win )
{
- // find it from HWND - this works even with the broken programs using
- // the same ids for different controls
- win = wxFindWinFromHandle(control);
+ // must cast to a signed type before comparing with other ids!
+ win = FindItem((signed short)id);
}
if ( win )
// 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);