]> git.saurik.com Git - wxWidgets.git/blobdiff - src/msw/window.cpp
fix order of initializiton of rootless treectrl
[wxWidgets.git] / src / msw / window.cpp
index 5630b738c63db6c8c1940fe0413a149938fd9ff2..5f4c38e7fe760dec7befbba0420d16ad358b0970 100644 (file)
     #endif
 #endif
 
-
 // ---------------------------------------------------------------------------
 // global variables
 // ---------------------------------------------------------------------------
@@ -378,7 +377,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
 
@@ -451,23 +450,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
     }
 }
 
@@ -826,36 +820,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.
@@ -982,11 +963,11 @@ void wxWindowMSW::SubclassWin(WXHWND hWnd)
 
     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);
     }
@@ -1011,8 +992,7 @@ void wxWindowMSW::UnsubclassWin()
 
         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);
             }
@@ -1022,6 +1002,35 @@ void wxWindowMSW::UnsubclassWin()
     }
 }
 
+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)
 {
@@ -1234,7 +1243,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()
@@ -1430,6 +1441,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"));
@@ -1505,40 +1522,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
@@ -2562,7 +2606,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
@@ -2591,14 +2638,11 @@ long wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam
 // 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)
@@ -2619,13 +2663,13 @@ 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());
 }
 
 // ----------------------------------------------------------------------------
@@ -2692,29 +2736,32 @@ bool wxWindowMSW::MSWGetCreateWindowCoords(const wxPoint& pos,
         nonDefault = TRUE;
     }
 
-    if ( size.x == -1 || size.y == -1 )
+    /*
+      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 )
     {
-        // Find parent's size, if it exists, to set up a possible default panel
-        // size the size of the parent window
-        wxWindow *parent = GetParent();
-        if ( parent )
-        {
-            RECT rectParent;
-            ::GetClientRect(GetHwndOf(parent), &rectParent);
-
-            w = size.x == -1 ? rectParent.right - rectParent.left : size.x;
-            h = size.y == -1 ? rectParent.bottom - rectParent.top : size.y;
-        }
-        else
-        {
-            w =
-            h = CW_USEDEFAULT;
-        }
+        // as abobe, h is not used at all in this case anyhow
+        w =
+        h = CW_USEDEFAULT;
     }
     else
     {
         w = size.x;
-        h = size.y;
+        h = size.y == -1 ? CW_USEDEFAULT : size.y;
 
         nonDefault = TRUE;
     }
@@ -2745,7 +2792,7 @@ bool wxWindowMSW::MSWCreate(const wxChar *wclass,
     }
     else // !popup
     {
-        if ( (isChild || (style & WS_POPUPWINDOW)) && parent )
+        if ( (isChild || HasFlag(wxFRAME_TOOL_WINDOW)) && parent )
         {
             // this is either a normal child window or a top level window with
             // wxFRAME_TOOL_WINDOW style (see below)
@@ -2758,8 +2805,7 @@ bool wxWindowMSW::MSWCreate(const wxChar *wclass,
             // 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 style translated into WS_POPUPWINDOW we test
-            // against above)
+            // wxFRAME_TOOL_WINDOW as above)
             hParent = NULL;
         }
 
@@ -3062,11 +3108,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,
@@ -3075,24 +3119,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
@@ -3265,7 +3313,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,
@@ -3332,19 +3384,35 @@ bool wxWindowMSW::HandleQueryNewPalette()
 // Responds to colour changes: passes event on to children.
 void wxWindowMSW::OnSysColourChanged(wxSysColourChangedEvent& event)
 {
-    wxNode *node = GetChildren().First();
+    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);
     }
 }
 
@@ -3521,7 +3589,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;
@@ -3530,18 +3598,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 )
@@ -4285,11 +4355,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);