X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/7f9510b24e556ef75b4c0b387544bf551fbe07bc..19f1a09ac95f3698eca674a1daf65f5fc8a7b791:/src/msw/window.cpp diff --git a/src/msw/window.cpp b/src/msw/window.cpp index aa47fded24..45e349fff9 100644 --- a/src/msw/window.cpp +++ b/src/msw/window.cpp @@ -37,6 +37,7 @@ #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" @@ -118,12 +119,11 @@ #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 @@ -132,6 +132,10 @@ wxMenu *wxCurrentPopupMenu = NULL; 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 // --------------------------------------------------------------------------- @@ -321,8 +325,6 @@ wxWindowMSW::~wxWindowMSW() { m_isBeingDeleted = TRUE; - MSWDetachWindowMenu(); - #ifndef __WXUNIVERSAL__ // VS: make sure there's no wxFrame with last focus set to us: for ( wxWindow *win = GetParent(); win; win = win->GetParent() ) @@ -378,7 +380,7 @@ bool wxWindowMSW::Create(wxWindow *parent, // // 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 @@ -433,11 +435,7 @@ bool wxWindowMSW::Create(wxWindow *parent, m_isShown = FALSE; } - return MSWCreate(m_windowId, parent, wxCanvasClassName, - (wxWindow *)this, NULL, - pos.x, pos.y, - WidthDefault(size.x), HeightDefault(size.y), - msflags, NULL, exStyle); + return MSWCreate(wxCanvasClassName, NULL, pos, size, msflags, exStyle); } // --------------------------------------------------------------------------- @@ -455,23 +453,18 @@ void wxWindowMSW::SetFocus() 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 } } @@ -830,36 +823,23 @@ int wxWindowMSW::GetScrollThumb(int orient) const 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. @@ -986,11 +966,19 @@ void wxWindowMSW::SubclassWin(WXHWND hWnd) wxAssociateWinWithHandle(hwnd, this); - m_oldWndProc = (WXFARPROC) GetWindowLong(hwnd, GWL_WNDPROC); - - wxASSERT( (WXFARPROC) m_oldWndProc != (WXFARPROC) wxWndProc ); - - SetWindowLong(hwnd, GWL_WNDPROC, (LONG) wxWndProc); + m_oldWndProc = (WXFARPROC)::GetWindowLong((HWND)hWnd, GWL_WNDPROC); + + // we don't need to subclass the window of our own class (in the Windows + // sense of the word) + if ( !wxCheckWindowWndProc(hWnd, (WXFARPROC)wxWndProc) ) + { + ::SetWindowLong(hwnd, GWL_WNDPROC, (LONG) wxWndProc); + } + else + { + // don't bother restoring it neither + m_oldWndProc = NULL; + } } void wxWindowMSW::UnsubclassWin() @@ -1005,15 +993,47 @@ void wxWindowMSW::UnsubclassWin() wxCHECK_RET( ::IsWindow(hwnd), wxT("invalid HWND in UnsubclassWin") ); - FARPROC farProc = (FARPROC) GetWindowLong(hwnd, GWL_WNDPROC); - if ( (m_oldWndProc != 0) && (farProc != (FARPROC) m_oldWndProc) ) + if ( m_oldWndProc ) { - SetWindowLong(hwnd, GWL_WNDPROC, (LONG) m_oldWndProc); - m_oldWndProc = 0; + if ( !wxCheckWindowWndProc((WXHWND)hwnd, m_oldWndProc) ) + { + ::SetWindowLong(hwnd, GWL_WNDPROC, (LONG) m_oldWndProc); + } + + m_oldWndProc = NULL; } } } +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) { @@ -1035,6 +1055,7 @@ WXDWORD wxWindowMSW::MakeExtendedStyle(long style, bool eliminateBorders) exStyle |= WS_EX_STATICEDGE; #endif } + return exStyle; } @@ -1042,7 +1063,7 @@ WXDWORD wxWindowMSW::MakeExtendedStyle(long style, bool eliminateBorders) // applying a default border style if required, and returning an extended // style to pass to CreateWindowEx. WXDWORD wxWindowMSW::Determine3DEffects(WXDWORD defaultBorderStyle, - bool *want3D) const + bool *want3D) const { // If matches certain criteria, then assume no 3D effects // unless specifically requested (dealt with in MakeExtendedStyle) @@ -1053,7 +1074,7 @@ WXDWORD wxWindowMSW::Determine3DEffects(WXDWORD defaultBorderStyle, || (m_windowStyle & wxNO_BORDER) ) { *want3D = FALSE; - return MakeExtendedStyle(m_windowStyle, FALSE); + return MakeExtendedStyle(m_windowStyle); } // Determine whether we should be using 3D effects or not. @@ -1225,7 +1246,9 @@ void wxWindowMSW::Clear() static inline void SendSetRedraw(HWND hwnd, bool on) { +#ifndef __WXMICROWIN__ ::SendMessage(hwnd, WM_SETREDRAW, (WPARAM)on, 0); +#endif } void wxWindowMSW::Freeze() @@ -1421,6 +1444,12 @@ void wxWindowMSW::DoClientToScreen(int *x, int *y) const 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")); @@ -1496,40 +1525,67 @@ void wxWindowMSW::DoSetSize(int x, int y, int width, int height, int sizeFlags) 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 @@ -2007,7 +2063,19 @@ void wxWindowMSW::UnpackMenuSelect(WXWPARAM wParam, WXLPARAM lParam, // Hook for new window just as it's being created, when the window isn't yet // associated with the handle -wxWindowMSW *wxWndHook = NULL; +static wxWindowMSW *gs_winBeingCreated = NULL; + +// implementation of wxWindowCreationHook class: it just sets gs_winBeingCreated to the +// window being created and insures that it's always unset back later +wxWindowCreationHook::wxWindowCreationHook(wxWindowMSW *winBeingCreated) +{ + gs_winBeingCreated = winBeingCreated; +} + +wxWindowCreationHook::~wxWindowCreationHook() +{ + gs_winBeingCreated = NULL; +} // Main window proc LRESULT WXDLLEXPORT APIENTRY _EXPORT wxWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) @@ -2021,38 +2089,21 @@ LRESULT WXDLLEXPORT APIENTRY _EXPORT wxWndProc(HWND hWnd, UINT message, WPARAM w wxWindowMSW *wnd = wxFindWinFromHandle((WXHWND) hWnd); // when we get the first message for the HWND we just created, we associate - // it with wxWindow stored in wxWndHook - if ( !wnd && wxWndHook ) + // it with wxWindow stored in gs_winBeingCreated + if ( !wnd && gs_winBeingCreated ) { -#if 0 // def __WXDEBUG__ - char buf[512]; - ::GetClassNameA((HWND) hWnd, buf, 512); - wxString className(buf); -#endif - - wxAssociateWinWithHandle(hWnd, wxWndHook); - wnd = wxWndHook; - wxWndHook = NULL; + wxAssociateWinWithHandle(hWnd, gs_winBeingCreated); + wnd = gs_winBeingCreated; + gs_winBeingCreated = NULL; wnd->SetHWND((WXHWND)hWnd); } LRESULT rc; - // Stop right here if we don't have a valid handle in our wxWindow object. - if ( wnd && !wnd->GetHWND() ) - { - // FIXME: why do we do this? - wnd->SetHWND((WXHWND) hWnd); - rc = wnd->MSWDefWindowProc(message, wParam, lParam ); - wnd->SetHWND(0); - } + if ( wnd ) + rc = wnd->MSWWindowProc(message, wParam, lParam); else - { - if ( wnd ) - rc = wnd->MSWWindowProc(message, wParam, lParam); - else - rc = DefWindowProc( hWnd, message, wParam, lParam ); - } + rc = ::DefWindowProc(hWnd, message, wParam, lParam); return rc; } @@ -2447,8 +2498,8 @@ long wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam break; #endif // !__WXMICROWIN__ - // the return value for this message is ignored case WM_SYSCOLORCHANGE: + // the return value for this message is ignored processed = HandleSysColorChange(); break; @@ -2558,7 +2609,10 @@ long wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam break; #endif // __WIN32__ -#ifdef __WXUNIVERSAL__ + // 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 @@ -2583,37 +2637,17 @@ long wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam return rc.result; } -// Dialog window proc -LONG APIENTRY _EXPORT -wxDlgProc(HWND WXUNUSED(hWnd), UINT message, WPARAM WXUNUSED(wParam), LPARAM WXUNUSED(lParam)) -{ - if ( message == WM_INITDIALOG ) - { - // for this message, returning TRUE tells system to set focus to the - // first control in the dialog box - return TRUE; - } - else - { - // for all the other ones, FALSE means that we didn't process the - // message - return 0; - } -} +// ---------------------------------------------------------------------------- +// wxWindow <-> HWND map +// ---------------------------------------------------------------------------- + +wxWinHashTable *wxWinHandleHash = NULL; -wxList *wxWinHandleList = NULL; wxWindow *wxFindWinFromHandle(WXHWND hWnd) { - wxNode *node = wxWinHandleList->Find((long)hWnd); - if ( !node ) - return NULL; - return (wxWindow *)node->Data(); + return wxWinHandleHash->Get((long)hWnd); } -#if 0 // def __WXDEBUG__ -static int gs_AssociationCount = 0; -#endif - void wxAssociateWinWithHandle(HWND hWnd, wxWindowMSW *win) { // adding NULL hWnd is (first) surely a result of an error and @@ -2621,126 +2655,105 @@ void wxAssociateWinWithHandle(HWND hWnd, wxWindowMSW *win) wxCHECK_RET( hWnd != (HWND)NULL, wxT("attempt to add a NULL hWnd to window list ignored") ); - wxWindow *oldWin = wxFindWinFromHandle((WXHWND) hWnd); +#ifdef __WXDEBUG__ if ( oldWin && (oldWin != win) ) { - wxString str(win->GetClassInfo()->GetClassName()); - wxLogError(wxT("Bug! Found existing HWND %X for new window of class %s"), (int) hWnd, (const wxChar*) str); + wxLogDebug(wxT("HWND %X already associated with another window (%s)"), + hWnd, win->GetClassInfo()->GetClassName()); } - else if (!oldWin) + else +#endif // __WXDEBUG__ + if (!oldWin) { -#if 0 // def __WXDEBUG__ - gs_AssociationCount ++; - wxLogDebug("+ Association %d", gs_AssociationCount); -#endif - - wxWinHandleList->Append((long)hWnd, win); + wxWinHandleHash->Put((long)hWnd, (wxWindow *)win); } } void wxRemoveHandleAssociation(wxWindowMSW *win) { -#if 0 // def __WXDEBUG__ - if (wxWinHandleList->Member(win)) - { - wxLogDebug("- Association %d", gs_AssociationCount); - gs_AssociationCount --; - } -#endif - wxWinHandleList->DeleteObject(win); + wxWinHandleHash->Delete((long)win->GetHWND()); } +// ---------------------------------------------------------------------------- +// various MSW speciic class dependent functions +// ---------------------------------------------------------------------------- + // Default destroyer - override if you destroy it in some other way // (e.g. with MDI child windows) void wxWindowMSW::MSWDestroyWindow() { } -void wxWindowMSW::MSWDetachWindowMenu() +bool wxWindowMSW::MSWGetCreateWindowCoords(const wxPoint& pos, + const wxSize& size, + int& x, int& y, + int& w, int& h) const { -#ifndef __WXUNIVERSAL__ - if ( m_hMenu ) - { - wxChar buf[1024]; - HMENU hMenu = (HMENU)m_hMenu; + bool nonDefault = FALSE; - 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")); - } + if ( pos.x == -1 ) + { + // if set x to CW_USEDEFAULT, y parameter is ignored anyhow so we can + // just as well set it to CW_USEDEFAULT as well + x = + y = CW_USEDEFAULT; + } + else + { + x = pos.x; + y = pos.y == -1 ? CW_USEDEFAULT : pos.y; + + nonDefault = TRUE; + } + + /* + NB: there used to be some code here which set the initial size of the + window to the client size of the parent if no explicit size was + specified. This was wrong because wxWindows programs often assume + that they get a WM_SIZE (EVT_SIZE) upon creation, however this broke + it. To see why, you should understand that Windows sends WM_SIZE from + inside ::CreateWindow() anyhow. However, ::CreateWindow() is called + from some base class ctor and so this WM_SIZE is not processed in the + real class' OnSize() (because it's not fully constructed yet and the + event goes to some base class OnSize() instead). So the WM_SIZE we + rely on is the one sent when the parent frame resizes its children + but here is the problem: if the child already has just the right + size, nothing will happen as both wxWindows and Windows check for + this and ignore any attempts to change the window size to the size it + already has - so no WM_SIZE would be sent. + */ + if ( size.x == -1 ) + { + // as abobe, h is not used at all in this case anyhow + w = + h = CW_USEDEFAULT; + } + else + { + w = size.x; + h = size.y == -1 ? CW_USEDEFAULT : size.y; - break; - } - } + nonDefault = TRUE; } -#endif + + return nonDefault; } -bool wxWindowMSW::MSWCreate(int id, - wxWindow *parent, - const wxChar *wclass, - wxWindow * WXUNUSED(wx_win), +bool wxWindowMSW::MSWCreate(const wxChar *wclass, const wxChar *title, - int x, - int y, - int width, - int height, + const wxPoint& pos, + const wxSize& size, WXDWORD style, - const wxChar *dialog_template, WXDWORD extendedStyle) { - int x1 = CW_USEDEFAULT; - int y1 = 0; - int width1 = CW_USEDEFAULT; - int height1 = 100; - - // Find parent's size, if it exists, to set up a possible default - // panel size the size of the parent window - RECT rectParent; - if ( parent ) - { - ::GetClientRect(GetHwndOf(parent), &rectParent); - - width1 = rectParent.right - rectParent.left; - height1 = rectParent.bottom - rectParent.top; - } - - if ( x != -1 ) - x1 = x; - if ( y != -1 ) - y1 = y; - if ( width != -1 ) - width1 = width; - if ( height != -1 ) - height1 = height; - - // unfortunately, setting WS_EX_CONTROLPARENT only for some windows in the - // hierarchy with several embedded panels (and not all of them) causes the - // program to hang during the next call to IsDialogMessage() due to the bug - // in this function (at least in Windows NT 4.0, it seems to work ok in - // Win2K) -#if 0 - // if we have wxTAB_TRAVERSAL style, we want WS_EX_CONTROLPARENT or - // IsDialogMessage() won't work for us - if ( GetWindowStyleFlag() & wxTAB_TRAVERSAL ) - { - extendedStyle |= WS_EX_CONTROLPARENT; - } -#endif // 0 + // choose the position/size for the new window + int x, y, w, h; + (void)MSWGetCreateWindowCoords(pos, size, x, y, w, h); + // find the correct parent HWND + wxWindow *parent = GetParent(); + bool isChild = (style & WS_CHILD) != 0; HWND hParent; if ( GetWindowStyleFlag() & wxPOPUP_WINDOW ) { @@ -2748,149 +2761,79 @@ bool wxWindowMSW::MSWCreate(int id, // be limited to the parents client area as child windows usually are hParent = ::GetDesktopWindow(); } - else if ( parent ) + else // !popup { - hParent = GetHwndOf(parent); - } - else - { - // top level window - hParent = NULL; - } - - wxWndHook = this; - -#ifndef __WXMICROWIN__ - if ( dialog_template ) - { - // for the dialogs without wxDIALOG_NO_PARENT style, use the top level - // app window as parent - this avoids creating modal dialogs without - // parent - if ( !hParent && !(GetWindowStyleFlag() & wxDIALOG_NO_PARENT) ) - { - wxWindow *winTop = wxTheApp->GetTopWindow(); - if ( winTop ) - hParent = GetHwndOf(winTop); - } - - m_hWnd = (WXHWND)::CreateDialog(wxGetInstance(), - dialog_template, - hParent, - (DLGPROC)wxDlgProc); - - if ( m_hWnd == 0 ) + if ( (isChild || HasFlag(wxFRAME_TOOL_WINDOW)) && parent ) { - wxLogError(_("Can't find dialog template '%s'!\nCheck resource include path for finding wx.rc."), - dialog_template); - - return FALSE; - } - - if ( extendedStyle != 0 ) - { - ::SetWindowLong(GetHwnd(), GWL_EXSTYLE, extendedStyle); - ::SetWindowPos(GetHwnd(), NULL, 0, 0, 0, 0, - SWP_NOSIZE | - SWP_NOMOVE | - SWP_NOZORDER | - SWP_NOACTIVATE); + // this is either a normal child window or a top level window with + // wxFRAME_TOOL_WINDOW style (see below) + hParent = GetHwndOf(parent); } - -#if defined(__WIN95__) - // For some reason, the system menu is activated when we use the - // WS_EX_CONTEXTHELP style, so let's set a reasonable icon - if (extendedStyle & WS_EX_CONTEXTHELP) + else { - wxFrame *winTop = wxDynamicCast(wxTheApp->GetTopWindow(), wxFrame); - if ( winTop ) - { - wxIcon icon = winTop->GetIcon(); - if ( icon.Ok() ) - { - ::SendMessage(GetHwnd(), WM_SETICON, - (WPARAM)TRUE, - (LPARAM)GetHiconOf(icon)); - } - } + // this is either a window for which no parent was specified (not + // much we can do then) or a frame without wxFRAME_TOOL_WINDOW + // style: we should use NULL parent HWND for it or it would be + // always on top of its parent which is not what we usually want + // (in fact, we only want it for frames with the special + // wxFRAME_TOOL_WINDOW as above) + hParent = NULL; } -#endif // __WIN95__ - - // JACS: is the following still necessary? The above seems to work. + } - // ::SetWindowLong(GWL_EXSTYLE) doesn't work for the dialogs, so try - // to take care of (at least some) extended style flags ourselves - if ( extendedStyle & WS_EX_TOPMOST ) - { - if ( !::SetWindowPos(GetHwnd(), HWND_TOPMOST, 0, 0, 0, 0, - SWP_NOSIZE | SWP_NOMOVE) ) - { - wxLogLastError(wxT("SetWindowPos")); - } - } + // controlId is menu handle for the top level windows, so set it to 0 + // unless we're creating a child window + int controlId; + if ( isChild ) + { + controlId = GetId(); - // move the dialog to its initial position without forcing repainting - if ( !::MoveWindow(GetHwnd(), x1, y1, width1, height1, FALSE) ) + if ( GetWindowStyleFlag() & wxCLIP_SIBLINGS ) { - wxLogLastError(wxT("MoveWindow")); + style |= WS_CLIPSIBLINGS; } - } - else // creating a normal window, not a dialog -#endif // !__WXMICROWIN__ + else // !child { - int controlId = 0; - if ( style & WS_CHILD ) - { - controlId = id; - - if ( GetWindowStyleFlag() & wxCLIP_SIBLINGS ) - { - style |= WS_CLIPSIBLINGS; - } - } - - wxString className(wclass); - if ( GetWindowStyleFlag() & wxNO_FULL_REPAINT_ON_RESIZE ) - { - className += wxT("NR"); - } - - m_hWnd = (WXHWND)CreateWindowEx(extendedStyle, - className, - title ? title : wxT(""), - style, - x1, y1, - width1, height1, - hParent, (HMENU)controlId, - wxGetInstance(), - NULL); - - if ( !m_hWnd ) - { - wxLogSysError(_("Can't create window of class %s"), wclass); - - return FALSE; - } + controlId = 0; } - wxWndHook = NULL; - -#ifdef __WXDEBUG__ - wxNode* node = wxWinHandleList->Member(this); - if (node) + // for each class "Foo" we have we also have "FooNR" ("no repaint") class + // which is the same but without CS_[HV]REDRAW class styles so using it + // ensures that the window is not fully repainted on each resize + wxString className(wclass); + if ( GetWindowStyleFlag() & wxNO_FULL_REPAINT_ON_RESIZE ) { - HWND hWnd = (HWND) node->GetKeyInteger(); - if (hWnd != (HWND) m_hWnd) - { - wxLogError(wxT("A second HWND association is being added for the same window!")); - } + className += wxT("NR"); + } + + // do create the window + wxWindowCreationHook hook(this); + + m_hWnd = (WXHWND)::CreateWindowEx + ( + extendedStyle, + className, + title ? title : wxT(""), + style, + x, y, w, h, + hParent, + (HMENU)controlId, + wxGetInstance(), + NULL // no extra data + ); + + if ( !m_hWnd ) + { + wxLogSysError(_("Can't create window of class %s"), wclass); + + return FALSE; } -#endif // Debug - wxAssociateWinWithHandle((HWND) m_hWnd, this); + SubclassWin(m_hWnd); - SetFont(wxSystemSettings::GetSystemFont(wxSYS_DEFAULT_GUI_FONT)); + SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT)); return TRUE; } @@ -3136,11 +3079,9 @@ bool wxWindowMSW::HandleDropFiles(WXWPARAM wParam) { #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, @@ -3149,24 +3090,28 @@ bool wxWindowMSW::HandleDropFiles(WXWPARAM wParam) ); wxString *files = new wxString[gwFilesDropped]; - int wIndex; - for (wIndex=0; wIndex < (int)gwFilesDropped; wIndex++) + for ( UINT wIndex = 0; wIndex < gwFilesDropped; wIndex++ ) { - DragQueryFile (hFilesInfo, wIndex, (LPTSTR) wxBuffer, 1000); - files[wIndex] = wxBuffer; + // first get the needed buffer length (+1 for terminating NUL) + size_t len = ::DragQueryFile(hFilesInfo, wIndex, NULL, 0) + 1; + + // and now get the file name + ::DragQueryFile(hFilesInfo, wIndex, + 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 @@ -3339,7 +3284,11 @@ bool wxWindowMSW::HandleSysColorChange() 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, @@ -3406,20 +3355,110 @@ bool wxWindowMSW::HandleQueryNewPalette() // 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; } // --------------------------------------------------------------------------- @@ -3595,7 +3634,7 @@ bool wxWindowMSW::HandleGetMinMaxInfo(void *mmInfo) bool wxWindowMSW::HandleCommand(WXWORD id, WXWORD cmd, WXHWND control) { #if wxUSE_MENUS_NATIVE - if ( wxCurrentPopupMenu ) + if ( !cmd && wxCurrentPopupMenu ) { wxMenu *popupMenu = wxCurrentPopupMenu; wxCurrentPopupMenu = NULL; @@ -3604,18 +3643,20 @@ bool wxWindowMSW::HandleCommand(WXWORD id, WXWORD cmd, WXHWND control) } #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 ) @@ -4359,11 +4400,13 @@ extern wxWindow *wxGetWindowFromHWND(WXHWND hWnd) // 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);