]> git.saurik.com Git - wxWidgets.git/blobdiff - src/msw/window.cpp
a better fix for notebook page not being refreshed after Delete()
[wxWidgets.git] / src / msw / window.cpp
index ebd05bfd52bd386c847d68b3d697b0c41a336cd0..b1b75973f65c2d903f240480d693e945744871d5 100644 (file)
@@ -312,10 +312,8 @@ void wxWindowMSW::Init()
     InitBase();
 
     // MSW specific
-    m_doubleClickAllowed = 0;
-
     m_isBeingDeleted = FALSE;
-    m_oldWndProc = 0;
+    m_oldWndProc = NULL;
     m_useCtl3D = FALSE;
     m_mouseInWindow = FALSE;
     m_lastKeydownProcessed = FALSE;
@@ -348,12 +346,12 @@ wxWindowMSW::~wxWindowMSW()
     // VS: make sure there's no wxFrame with last focus set to us:
     for ( wxWindow *win = GetParent(); win; win = win->GetParent() )
     {
-        wxFrame *frame = wxDynamicCast(win, wxFrame);
+        wxTopLevelWindow *frame = wxDynamicCast(win, wxTopLevelWindow);
         if ( frame )
         {
             if ( frame->GetLastFocus() == this )
             {
-                frame->SetLastFocus((wxWindow*)NULL);
+                frame->SetLastFocus(NULL);
             }
             break;
         }
@@ -993,7 +991,10 @@ void wxWindowMSW::SubclassWin(WXHWND hWnd)
     }
     else
     {
-        // don't bother restoring it neither
+        // don't bother restoring it neither: this also makes it easy to
+        // implement IsOfStandardClass() method which returns TRUE for the
+        // standard controls and FALSE for the wxWindows own windows as it can
+        // simply check m_oldWndProc
         m_oldWndProc = NULL;
     }
 }
@@ -1024,35 +1025,21 @@ 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!!
-
-    if ( wxUsingUnicowsDll() )
+    // Unicows note: the code below works, but only 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.
+    WNDCLASS cls;
+    if ( !::GetClassInfo(wxGetInstance(), wxGetWindowClass(hWnd), &cls) )
     {
-        static wxChar buffer[512];
-        WNDCLASS cls;
+        wxLogLastError(_T("GetClassInfo"));
 
-        ::GetClassName((HWND)hWnd, buffer, 512);
-        ::GetClassInfo(wxGetInstance(), buffer, &cls);
-        return wndProc == (WXFARPROC)cls.lpfnWndProc;
-    }
-    else
-#endif
-    {
-        return wndProc == (WXFARPROC)::GetWindowLong((HWND)hWnd, GWL_WNDPROC);
+        return FALSE;
     }
+
+    return wndProc == (WXFARPROC)cls.lpfnWndProc;
 }
 
 // ----------------------------------------------------------------------------
@@ -1163,6 +1150,16 @@ WXDWORD wxWindowMSW::MSWGetStyle(long flags, WXDWORD *exstyle) const
                 *exstyle |= WS_EX_DLGMODALFRAME;
                 break;
         }
+
+        // wxUniv doesn't use Windows dialog navigation functions at all
+#ifndef __WXUNIVERSAL__
+        // to make the dialog navigation work with the nested panels we must
+        // use this style (top level windows such as dialogs don't need it)
+        if ( (flags & wxTAB_TRAVERSAL) && !IsTopLevel() )
+        {
+            *exstyle |= WS_EX_CONTROLPARENT;
+        }
+#endif // __WXUNIVERSAL__
     }
 
     return style;
@@ -1905,14 +1902,7 @@ bool wxWindowMSW::MSWProcessMessage(WXMSG* pMsg)
         // here we try to do all the job which ::IsDialogMessage() usually does
         // internally
 #if 1
-        bool bProcess = TRUE;
-        if ( msg->message != WM_KEYDOWN )
-            bProcess = FALSE;
-
-        if ( bProcess && (HIWORD(msg->lParam) & KF_ALTDOWN) == KF_ALTDOWN )
-            bProcess = FALSE;
-
-        if ( bProcess )
+        if ( msg->message == WM_KEYDOWN )
         {
             bool bCtrlDown = wxIsCtrlDown();
             bool bShiftDown = wxIsShiftDown();
@@ -1929,6 +1919,8 @@ bool wxWindowMSW::MSWProcessMessage(WXMSG* pMsg)
             bool bForward = TRUE,
                  bWindowChange = FALSE;
 
+            // should we process this message specially?
+            bool bProcess = TRUE;
             switch ( msg->wParam )
             {
                 case VK_TAB:
@@ -2073,7 +2065,35 @@ bool wxWindowMSW::MSWProcessMessage(WXMSG* pMsg)
         // place edit control from being closed with Escape in a dialog
         if ( msg->message != WM_KEYDOWN || msg->wParam != VK_ESCAPE )
         {
-            if ( ::IsDialogMessage(GetHwnd(), msg) )
+            // ::IsDialogMessage() can enter in an infinite loop when the
+            // currently focused window is disabled or hidden and its parent
+            // has WS_EX_CONTROLPARENT style, so don't call it in this case
+            bool canSafelyCallIsDlgMsg = TRUE;
+
+            HWND hwndFocus = ::GetFocus();
+            while ( hwndFocus )
+            {
+                if ( !::IsWindowEnabled(hwndFocus) ||
+                        !::IsWindowVisible(hwndFocus) )
+                {
+                    // it would enter an infinite loop if we do this!
+                    canSafelyCallIsDlgMsg = FALSE;
+
+                    break;
+                }
+
+                if ( !(::GetWindowLong(hwndFocus, GWL_STYLE) & WS_CHILD) )
+                {
+                    // it's a top level window, don't go further -- e.g. even
+                    // if the parent of a dialog is disabled, this doesn't
+                    // break navigation inside the dialog
+                    break;
+                }
+
+                hwndFocus = ::GetParent(hwndFocus);
+            }
+
+            if ( canSafelyCallIsDlgMsg && ::IsDialogMessage(GetHwnd(), msg) )
             {
                 // IsDialogMessage() did something...
                 return TRUE;
@@ -2412,28 +2432,36 @@ long wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam
                         break;
                 }
 
-                if (!processed)
+                if ( processed )
+                    break;
+
 #endif // __WXMICROWIN__
-                {
-                    // VZ: why do we need it here? DefWindowProc() is supposed
-                    //     to do this for us anyhow
-                    if ( message == WM_LBUTTONDOWN && AcceptsFocus() )
-                        SetFocus();
+                // VZ: if you find a situation when this is needed, tell
+                //     me about it, do *not* uncomment this code as it
+                //     causes other strange problems
+#if 0
+                if ( message == WM_LBUTTONDOWN && AcceptsFocus() )
+                    SetFocus();
+#endif // 0
 
-                    int x = GET_X_LPARAM(lParam),
-                        y = GET_Y_LPARAM(lParam);
+                int x = GET_X_LPARAM(lParam),
+                    y = GET_Y_LPARAM(lParam);
 
-                    // redirect the event to a static control if necessary
-                    if (this == GetCapture())
-                    {
-                        processed = HandleMouseEvent(message, x, y, wParam);
-                    }
-                    else
-                    {
-                        wxWindowMSW *win = FindWindowForMouseEvent(this, &x, &y); //TW:REQ:Univ
-                        processed = win->HandleMouseEvent(message, x, y, wParam);
-                    }
+                // redirect the event to a static control if necessary by
+                // finding one under mouse
+                wxWindowMSW *win;
+                if ( GetCapture() == this )
+                {
+                    // but don't do it if the mouse is captured by this window
+                    // because then it should really get this event itself
+                    win = this;
                 }
+                else
+                {
+                    win = FindWindowForMouseEvent(this, &x, &y);
+                }
+
+                processed = win->HandleMouseEvent(message, x, y, wParam);
             }
             break;
 
@@ -2497,11 +2525,19 @@ long wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam
 #endif // defined(WM_DRAWITEM)
 
         case WM_GETDLGCODE:
-            if ( GetWindowStyleFlag() & wxWANTS_CHARS )
+            if ( !IsOfStandardClass() )
             {
-                // want everything: i.e. all keys and WM_CHAR message
-                rc.result = DLGC_WANTARROWS | DLGC_WANTCHARS |
-                            DLGC_WANTTAB | DLGC_WANTMESSAGE;
+                // we always want to get the char events
+                rc.result = DLGC_WANTCHARS;
+
+                if ( GetWindowStyleFlag() & wxWANTS_CHARS )
+                {
+                    // in fact, we want everything
+                    rc.result |= DLGC_WANTARROWS |
+                                 DLGC_WANTTAB |
+                                 DLGC_WANTALLKEYS;
+                }
+
                 processed = TRUE;
             }
             //else: get the dlg code from the DefWindowProc()
@@ -2903,6 +2939,11 @@ bool wxWindowMSW::MSWGetCreateWindowCoords(const wxPoint& pos,
     return nonDefault;
 }
 
+WXHWND wxWindowMSW::MSWGetParent() const
+{
+    return m_parent ? m_parent->GetHWND() : WXHWND(NULL);
+}
+
 bool wxWindowMSW::MSWCreate(const wxChar *wclass,
                             const wxChar *title,
                             const wxPoint& pos,
@@ -2914,41 +2955,9 @@ bool wxWindowMSW::MSWCreate(const wxChar *wclass,
     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 ( (isChild || HasFlag(wxPOPUP_WINDOW) || HasFlag(wxFRAME_TOOL_WINDOW)) )
-    {
-        // this is either a normal child window, a popup window or a top level
-        // window with wxFRAME_TOOL_WINDOW style (see below)
-        hParent = parent ? GetHwndOf(parent) : NULL;
-    }
-    else
-    {
-        // this is 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;
-    }
-
     // 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();
-
-        if ( GetWindowStyleFlag() & wxCLIP_SIBLINGS )
-        {
-            style |= WS_CLIPSIBLINGS;
-        }
-    }
-    else // !child
-    {
-        controlId = 0;
-    }
+    int controlId = style & WS_CHILD ? GetId() : 0;
 
     // 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
@@ -2963,17 +2972,17 @@ bool wxWindowMSW::MSWCreate(const wxChar *wclass,
     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
-             );
+                       (
+                            extendedStyle,
+                            className,
+                            title ? title : wxT(""),
+                            style,
+                            x, y, w, h,
+                            (HWND)MSWGetParent(),
+                            (HMENU)controlId,
+                            wxGetInstance(),
+                            NULL                        // no extra data
+                       );
 
     if ( !m_hWnd )
     {
@@ -2998,7 +3007,7 @@ bool wxWindowMSW::MSWCreate(const wxChar *wclass,
 // ---------------------------------------------------------------------------
 
 #ifdef __WIN95__
-// FIXME: VZ: I'm not sure at all that the order of processing is correct
+
 bool wxWindowMSW::HandleNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
 {
 #ifndef __WXMICROWIN__
@@ -3006,12 +3015,19 @@ bool wxWindowMSW::HandleNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
     HWND hWnd = hdr->hwndFrom;
     wxWindow *win = wxFindWinFromHandle((WXHWND)hWnd);
 
-    // is this one of our windows?
+    // if the control is one of our windows, let it handle the message itself
     if ( win )
     {
         return win->MSWOnNotify(idCtrl, lParam, result);
     }
 
+    // VZ: why did we do it? normally this is unnecessary and, besides, it
+    //     breaks the message processing for the toolbars because the tooltip
+    //     notifications were being forwarded to the toolbar child controls
+    //     (if it had any) before being passed to the toolbar itself, so in my
+    //     example the tooltip for the combobox was always shown instead of the
+    //     correct button tooltips
+#if 0
     // try all our children
     wxWindowList::Node *node = GetChildren().GetFirst();
     while ( node )
@@ -3024,32 +3040,92 @@ bool wxWindowMSW::HandleNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
 
         node = node->GetNext();
     }
+#endif // 0
 
-    // finally try this window too (catches toolbar case)
+    // by default, handle it ourselves
     return MSWOnNotify(idCtrl, lParam, result);
 #else // __WXMICROWIN__
     return FALSE;
 #endif
 }
 
+#if wxUSE_TOOLTIPS
+
+bool wxWindowMSW::HandleTooltipNotify(WXUINT code,
+                                      WXLPARAM lParam,
+                                      const wxString& ttip)
+{
+    // I don't know why it happens, but the versions of comctl32.dll starting
+    // from 4.70 sometimes send TTN_NEEDTEXTW even to ANSI programs (normally,
+    // this message is supposed to be sent to Unicode programs only) -- hence
+    // we need to handle it as well, otherwise no tooltips will be shown in
+    // this case
+
+    if ( !(code == (WXUINT) TTN_NEEDTEXTA || code == (WXUINT) TTN_NEEDTEXTW) || ttip.empty() )
+    {
+        // not a tooltip message or no tooltip to show anyhow
+        return FALSE;
+    }
+
+    LPTOOLTIPTEXT ttText = (LPTOOLTIPTEXT)lParam;
+
+    if ( code == (WXUINT) TTN_NEEDTEXTA )
+    {
+        ttText->lpszText = (wxChar *)ttip.c_str();
+    }
+    else
+    {
+#if wxUSE_UNICODE
+        ttText->lpszText = (wxChar *)ttip.c_str();
+#else // !Unicode
+        size_t lenAnsi = ttip.length();
+
+        // some compilers (MetroWerks and Cygwin) don't like calling mbstowcs
+        // with NULL argument
+        #if defined( __MWERKS__ ) || defined( __CYGWIN__ )
+            size_t lenUnicode = 2*lenAnsi;
+        #else
+            size_t lenUnicode = mbstowcs(NULL, ttip, lenAnsi);
+        #endif
+
+        // using the pointer of right type avoids us doing all sorts of
+        // pointer arithmetics ourselves
+        wchar_t *dst = (wchar_t *)ttText->szText,
+                *pwz = new wchar_t[lenUnicode + 1];
+        mbstowcs(pwz, ttip, lenAnsi + 1);
+        memcpy(dst, pwz, lenUnicode*sizeof(wchar_t));
+
+        // put the terminating wide NUL
+        dst[lenUnicode] = L'\0';
+
+        delete [] pwz;
+#endif // Unicode/!Unicode
+    }
+
+    return TRUE;
+}
+
+#endif // wxUSE_TOOLTIPS
+
 bool wxWindowMSW::MSWOnNotify(int WXUNUSED(idCtrl),
-                           WXLPARAM lParam,
-                           WXLPARAM* WXUNUSED(result))
+                              WXLPARAM lParam,
+                              WXLPARAM* WXUNUSED(result))
 {
 #if wxUSE_TOOLTIPS
-    NMHDR* hdr = (NMHDR *)lParam;
-    if ( (int)hdr->code == TTN_NEEDTEXT && m_tooltip )
+    if ( m_tooltip )
     {
-        TOOLTIPTEXT *ttt = (TOOLTIPTEXT *)lParam;
-        ttt->lpszText = (wxChar *)m_tooltip->GetTip().c_str();
-
-        // processed
-        return TRUE;
+        NMHDR* hdr = (NMHDR *)lParam;
+        if ( HandleTooltipNotify(hdr->code, lParam, m_tooltip->GetTip()))
+        {
+            // processed
+            return TRUE;
+        }
     }
 #endif // wxUSE_TOOLTIPS
 
     return FALSE;
 }
+
 #endif // __WIN95__
 
 // ---------------------------------------------------------------------------
@@ -3097,8 +3173,33 @@ bool wxWindowMSW::HandleEndSession(bool endSession, long logOff)
 // window creation/destruction
 // ---------------------------------------------------------------------------
 
-bool wxWindowMSW::HandleCreate(WXLPCREATESTRUCT WXUNUSED(cs), bool *mayCreate)
+bool wxWindowMSW::HandleCreate(WXLPCREATESTRUCT cs, bool *mayCreate)
 {
+    // if we have WS_EX_CONTROLPARENT flag we absolutely *must* set it for our
+    // parent as well as otherwise several Win32 functions using
+    // GetNextDlgTabItem() to iterate over all controls such as
+    // IsDialogMessage() or DefDlgProc() would enter an infinite loop: indeed,
+    // all of them iterate over all the controls starting from the focus and
+    // stop iterating when they get back to the focus but unless all parents
+    // have WS_EX_CONTROLPARENT bit set, they would never get back to focus
+    if ( ((CREATESTRUCT *)cs)->dwExStyle & WS_EX_CONTROLPARENT )
+    {
+        // there is no need to do anything for the top level windows
+        const wxWindow *parent = GetParent();
+        while ( parent && !parent->IsTopLevel() )
+        {
+            LONG exStyle = ::GetWindowLong(GetHwndOf(parent), GWL_EXSTYLE);
+            if ( !(exStyle & WS_EX_CONTROLPARENT) )
+            {
+                // force the parent to have this style
+                ::SetWindowLong(GetHwndOf(parent), GWL_EXSTYLE,
+                                exStyle | WS_EX_CONTROLPARENT);
+            }
+
+            parent = parent->GetParent();
+        }
+    }
+
     // TODO: should generate this event from WM_NCCREATE
     wxWindowCreateEvent event((wxWindow *)this);
     (void)GetEventHandler()->ProcessEvent(event);
@@ -3197,6 +3298,13 @@ bool wxWindowMSW::HandleKillFocus(WXHWND hwnd)
     }
 #endif
 
+    // Don't send the event when in the process of being deleted.  This can
+    // only cause problems if the event handler tries to access the object.
+    if ( m_isBeingDeleted )
+    {
+        return FALSE;
+    }
+
     wxFocusEvent event(wxEVT_KILL_FOCUS, m_windowId);
     event.SetEventObject(this);
 
@@ -3501,7 +3609,7 @@ bool wxWindowMSW::HandlePaletteChanged(WXHWND hWndPalChange)
     if ( hWndPalChange != GetHWND() )
     {
         // check to see if we our our parents have a custom palette
-        wxWindow *win = this;
+        wxWindowMSW *win = this;
         while ( win && !win->HasCustomPalette() )
         {
             win = win->GetParent();
@@ -3550,7 +3658,7 @@ bool wxWindowMSW::HandleQueryNewPalette()
 
 #if wxUSE_PALETTE
     // check to see if we our our parents have a custom palette
-    wxWindow *win = this;
+    wxWindowMSW *win = this;
     while (!win->HasCustomPalette() && win->GetParent()) win = win->GetParent();
     if (win->HasCustomPalette()) {
         /* realize the palette to see whether redrawing is needed */
@@ -3965,6 +4073,7 @@ void wxWindowMSW::InitMouseEvent(wxMouseEvent& event,
 
     event.SetTimestamp(s_currentMsg.time);
     event.m_eventObject = this;
+    event.SetId(GetId());
 
 #if wxUSE_MOUSEEVENT_HACK
     m_lastMouseX = x;
@@ -4184,7 +4293,9 @@ bool wxWindowMSW::HandleChar(WXWPARAM wParam, WXLPARAM lParam, bool isASCII)
     int id;
     if ( isASCII )
     {
-        // If 1 -> 26, translate to CTRL plus a letter.
+        // If 1 -> 26, translate to either special keycode or just set
+        // ctrlDown.  IOW, Ctrl-C should result in keycode == 3 and
+        // ControlDown() == TRUE.
         id = wParam;
         if ( (id > 0) && (id < 27) )
         {
@@ -4204,7 +4315,7 @@ bool wxWindowMSW::HandleChar(WXWPARAM wParam, WXLPARAM lParam, bool isASCII)
 
                 default:
                     ctrlDown = TRUE;
-                    id = id + 'a' - 1;
+                    break;
             }
         }
     }