]> git.saurik.com Git - wxWidgets.git/blobdiff - src/msw/textctrl.cpp
fixed stub code compilation (don't use PSYMBOL_INFO)
[wxWidgets.git] / src / msw / textctrl.cpp
index c1975d4c5192b5518a3a82a8fa7ae0c3fb027974..9899b1434815926a36a2f2336a34a59e9e5035d0 100644 (file)
@@ -39,6 +39,7 @@
     #include "wx/log.h"
     #include "wx/app.h"
     #include "wx/menu.h"
     #include "wx/log.h"
     #include "wx/app.h"
     #include "wx/menu.h"
+    #include "wx/math.h"
 #endif
 
 #include "wx/module.h"
 #endif
 
 #include "wx/module.h"
@@ -102,6 +103,36 @@ IMPLEMENT_DYNAMIC_CLASS(wxRichEditModule, wxModule)
 
 #endif // wxUSE_RICHEDIT
 
 
 #endif // wxUSE_RICHEDIT
 
+// a small class used to set m_updatesCount to 0 (to filter duplicate events if
+// necessary) and to reset it back to -1 afterwards
+class UpdatesCountFilter
+{
+public:
+    UpdatesCountFilter(int& count)
+        : m_count(count)
+    {
+        wxASSERT_MSG( m_count == -1, _T("wrong initial m_updatesCount value") );
+
+        m_count = 0;
+    }
+
+    ~UpdatesCountFilter()
+    {
+        m_count = -1;
+    }
+
+    // return true if an event has been received
+    bool GotUpdate() const
+    {
+        return m_count == 1;
+    }
+
+private:
+    int& m_count;
+
+    DECLARE_NO_COPY_CLASS(UpdatesCountFilter)
+};
+
 // ----------------------------------------------------------------------------
 // event tables and other macros
 // ----------------------------------------------------------------------------
 // ----------------------------------------------------------------------------
 // event tables and other macros
 // ----------------------------------------------------------------------------
@@ -218,17 +249,14 @@ void wxTextCtrl::Init()
 #endif // wxUSE_RICHEDIT
 
     m_privateContextMenu = NULL;
 #endif // wxUSE_RICHEDIT
 
     m_privateContextMenu = NULL;
-    m_suppressNextUpdate = false;
+    m_updatesCount = -1;
     m_isNativeCaretShown = true;
     m_isNativeCaretShown = true;
+    m_isCaretAtEnd = true;
 }
 
 wxTextCtrl::~wxTextCtrl()
 {
 }
 
 wxTextCtrl::~wxTextCtrl()
 {
-    if (m_privateContextMenu)
-    {
-        delete m_privateContextMenu;
-        m_privateContextMenu = NULL;
-    }
+    delete m_privateContextMenu;
 }
 
 bool wxTextCtrl::Create(wxWindow *parent, wxWindowID id,
 }
 
 bool wxTextCtrl::Create(wxWindow *parent, wxWindowID id,
@@ -346,8 +374,6 @@ bool wxTextCtrl::Create(wxWindow *parent, wxWindowID id,
     if ( !MSWCreateControl(windowClass, msStyle, pos, size, valueWin) )
         return false;
 
     if ( !MSWCreateControl(windowClass, msStyle, pos, size, valueWin) )
         return false;
 
-    SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
-
 #if wxUSE_RICHEDIT
     if ( IsRich() )
     {
 #if wxUSE_RICHEDIT
     if ( IsRich() )
     {
@@ -756,13 +782,8 @@ wxTextCtrl::StreamIn(const wxString& value,
     // the cast below is needed for broken (very) old mingw32 headers
     eds.pfnCallback = (EDITSTREAMCALLBACK)wxRichEditStreamIn;
 
     // the cast below is needed for broken (very) old mingw32 headers
     eds.pfnCallback = (EDITSTREAMCALLBACK)wxRichEditStreamIn;
 
-    // we're going to receive 2 EN_CHANGE notifications if we got any selection
-    // (same problem as in DoWriteText())
-    if ( selectionOnly && HasSelection() )
-    {
-        // so suppress one of them
-        m_suppressNextUpdate = true;
-    }
+    // same problem as in DoWriteText(): we can get multiple events here
+    UpdatesCountFilter ucf(m_updatesCount);
 
     ::SendMessage(GetHwnd(), EM_STREAMIN,
                   SF_TEXT |
 
     ::SendMessage(GetHwnd(), EM_STREAMIN,
                   SF_TEXT |
@@ -770,6 +791,8 @@ wxTextCtrl::StreamIn(const wxString& value,
                   (selectionOnly ? SFF_SELECTION : 0),
                   (LPARAM)&eds);
 
                   (selectionOnly ? SFF_SELECTION : 0),
                   (LPARAM)&eds);
 
+    wxASSERT_MSG( ucf.GotUpdate(), _T("EM_STREAMIN didn't send EN_UPDATE?") );
+
     if ( eds.dwError )
     {
         wxLogLastError(_T("EM_STREAMIN"));
     if ( eds.dwError )
     {
         wxLogLastError(_T("EM_STREAMIN"));
@@ -911,40 +934,22 @@ void wxTextCtrl::DoWriteText(const wxString& value, bool selectionOnly)
 #endif // wxUSE_RICHEDIT
     {
         // in some cases we get 2 EN_CHANGE notifications after the SendMessage
 #endif // wxUSE_RICHEDIT
     {
         // in some cases we get 2 EN_CHANGE notifications after the SendMessage
-        // call below which is confusing for the client code and so should be
-        // avoided
-        //
-        // these cases are: (a) plain EDIT controls if EM_REPLACESEL is used
-        // and there is a non empty selection currently and (b) rich text
-        // controls in any case
-        if (
-#if wxUSE_RICHEDIT
-            IsRich() ||
-#endif // wxUSE_RICHEDIT
-            (selectionOnly && HasSelection()) )
-        {
-            m_suppressNextUpdate = true;
-        }
+        // call (this happens for plain EDITs with EM_REPLACESEL and under some
+        // -- undetermined -- conditions with rich edit) and sometimes we don't
+        // get any events at all (plain EDIT with WM_SETTEXT), so ensure that
+        // we generate exactly one of them by ignoring all but the first one in
+        // SendUpdateEvent() and generating one ourselves if we hadn't got any
+        // notifications from Windows
+        UpdatesCountFilter ucf(m_updatesCount);
 
         ::SendMessage(GetHwnd(), selectionOnly ? EM_REPLACESEL : WM_SETTEXT,
                       0, (LPARAM)valueDos.c_str());
 
 
         ::SendMessage(GetHwnd(), selectionOnly ? EM_REPLACESEL : WM_SETTEXT,
                       0, (LPARAM)valueDos.c_str());
 
-        // OTOH, non rich text controls don't generate any events at all when
-        // we use WM_SETTEXT -- have to emulate them here
-        if ( !selectionOnly
-#if wxUSE_RICHEDIT
-                && !IsRich()
-#endif // wxUSE_RICHEDIT
-           )
+        if ( !ucf.GotUpdate() )
         {
         {
-            // Windows already sends an update event for single-line
-            // controls.
-            if ( m_windowStyle & wxTE_MULTILINE )
-                SendUpdateEvent();
+            SendUpdateEvent();
         }
     }
         }
     }
-
-    AdjustSpaceLimit();
 }
 
 void wxTextCtrl::AppendText(const wxString& text)
 }
 
 void wxTextCtrl::AppendText(const wxString& text)
@@ -954,7 +959,8 @@ void wxTextCtrl::AppendText(const wxString& text)
     WriteText(text);
 
 #if wxUSE_RICHEDIT
     WriteText(text);
 
 #if wxUSE_RICHEDIT
-    if ( IsMultiLine() && GetRichVersion() > 1 )
+    // don't do this if we're frozen, saves some time
+    if ( !IsFrozen() && IsMultiLine() && GetRichVersion() > 1 )
     {
         // setting the caret to the end and showing it simply doesn't work for
         // RichEdit 2.0 -- force it to still do what we want
     {
         // setting the caret to the end and showing it simply doesn't work for
         // RichEdit 2.0 -- force it to still do what we want
@@ -1084,6 +1090,8 @@ void wxTextCtrl::SetEditable(bool editable)
 void wxTextCtrl::SetInsertionPoint(long pos)
 {
     DoSetSelection(pos, pos);
 void wxTextCtrl::SetInsertionPoint(long pos)
 {
     DoSetSelection(pos, pos);
+
+    m_isCaretAtEnd = pos == GetLastPosition();
 }
 
 void wxTextCtrl::SetInsertionPointEnd()
 }
 
 void wxTextCtrl::SetInsertionPointEnd()
@@ -1093,8 +1101,11 @@ void wxTextCtrl::SetInsertionPointEnd()
     // if it doesn't actually move the caret anywhere and so the simple fact of
     // doing it results in horrible flicker when appending big amounts of text
     // to the control in a few chunks (see DoAddText() test in the text sample)
     // if it doesn't actually move the caret anywhere and so the simple fact of
     // doing it results in horrible flicker when appending big amounts of text
     // to the control in a few chunks (see DoAddText() test in the text sample)
-    if ( GetInsertionPoint() == GetLastPosition() )
+    if ( m_isCaretAtEnd || GetInsertionPoint() == GetLastPosition() )
+    {
+        m_isCaretAtEnd = true;
         return;
         return;
+    }
 
     long pos;
 
 
     long pos;
 
@@ -1130,7 +1141,7 @@ long wxTextCtrl::GetInsertionPoint() const
     return Pos & 0xFFFF;
 }
 
     return Pos & 0xFFFF;
 }
 
-long wxTextCtrl::GetLastPosition() const
+wxTextPos wxTextCtrl::GetLastPosition() const
 {
     int numLines = GetNumberOfLines();
     long posStartLastLine = XYToPosition(0, numLines - 1);
 {
     int numLines = GetNumberOfLines();
     long posStartLastLine = XYToPosition(0, numLines - 1);
@@ -1212,7 +1223,7 @@ void wxTextCtrl::DoSetSelection(long from, long to, bool scrollCaret)
         ::SendMessage(hWnd, EM_SETSEL, (WPARAM)from, (LPARAM)to);
     }
 
         ::SendMessage(hWnd, EM_SETSEL, (WPARAM)from, (LPARAM)to);
     }
 
-    if ( scrollCaret )
+    if ( scrollCaret && !IsFrozen() )
     {
 #if wxUSE_RICHEDIT
         // richedit 3.0 (i.e. the version living in riched20.dll distributed
     {
 #if wxUSE_RICHEDIT
         // richedit 3.0 (i.e. the version living in riched20.dll distributed
@@ -1473,6 +1484,9 @@ void wxTextCtrl::ShowPosition(long pos)
 
     if (linesToScroll != 0)
       (void)::SendMessage(hWnd, EM_LINESCROLL, (WPARAM)0, (LPARAM)linesToScroll);
 
     if (linesToScroll != 0)
       (void)::SendMessage(hWnd, EM_LINESCROLL, (WPARAM)0, (LPARAM)linesToScroll);
+
+    // be pessimistic
+    m_isCaretAtEnd = false;
 }
 
 long wxTextCtrl::GetLengthOfLineContainingPos(long pos) const
 }
 
 long wxTextCtrl::GetLengthOfLineContainingPos(long pos) const
@@ -1549,6 +1563,9 @@ void wxTextCtrl::Undo()
     if (CanUndo())
     {
         ::SendMessage(GetHwnd(), EM_UNDO, 0, 0);
     if (CanUndo())
     {
         ::SendMessage(GetHwnd(), EM_UNDO, 0, 0);
+
+        // it's not necessarily at the end any more
+        m_isCaretAtEnd = false;
     }
 }
 
     }
 }
 
@@ -1563,6 +1580,9 @@ void wxTextCtrl::Redo()
 #endif
         // Same as Undo, since Undo undoes the undo, i.e. a redo.
         ::SendMessage(GetHwnd(), EM_UNDO, 0, 0);
 #endif
         // Same as Undo, since Undo undoes the undo, i.e. a redo.
         ::SendMessage(GetHwnd(), EM_UNDO, 0, 0);
+
+        // it's not necessarily at the end any more
+        m_isCaretAtEnd = false;
     }
 }
 
     }
 }
 
@@ -1793,18 +1813,29 @@ WXLRESULT wxTextCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lPara
 
 bool wxTextCtrl::SendUpdateEvent()
 {
 
 bool wxTextCtrl::SendUpdateEvent()
 {
-    // is event reporting suspended?
-    if ( m_suppressNextUpdate )
+    switch ( m_updatesCount )
     {
     {
-        // do process the next one
-        m_suppressNextUpdate = false;
+        case 0:
+            // remember that we've got an update
+            m_updatesCount++;
+            break;
 
 
-        return false;
+        case 1:
+            // we had already sent one event since the last control modification
+            return false;
+
+        default:
+            wxFAIL_MSG( _T("unexpected wxTextCtrl::m_updatesCount value") );
+            // fall through
+
+        case -1:
+            // we hadn't updated the control ourselves, this event comes from
+            // the user, don't need to ignore it nor update the count
+            break;
     }
 
     wxCommandEvent event(wxEVT_COMMAND_TEXT_UPDATED, GetId());
     InitCommandEvent(event);
     }
 
     wxCommandEvent event(wxEVT_COMMAND_TEXT_UPDATED, GetId());
     InitCommandEvent(event);
-    event.SetString(GetValue());
 
     return ProcessCommand(event);
 }
 
     return ProcessCommand(event);
 }
@@ -1848,38 +1879,12 @@ bool wxTextCtrl::MSWCommand(WXUINT param, WXWORD WXUNUSED(id))
     return true;
 }
 
     return true;
 }
 
-WXHBRUSH wxTextCtrl::OnCtlColor(WXHDC pDC, WXHWND WXUNUSED(pWnd), WXUINT WXUNUSED(nCtlColor),
-#if wxUSE_CTL3D
-                               WXUINT message,
-                               WXWPARAM wParam,
-                               WXLPARAM lParam
-#else
-                               WXUINT WXUNUSED(message),
-                               WXWPARAM WXUNUSED(wParam),
-                               WXLPARAM WXUNUSED(lParam)
-#endif
-    )
+WXHBRUSH wxTextCtrl::MSWControlColor(WXHDC hDC)
 {
 {
-#if wxUSE_CTL3D
-    if ( m_useCtl3D )
-    {
-        HBRUSH hbrush = Ctl3dCtlColorEx(message, wParam, lParam);
-        return (WXHBRUSH) hbrush;
-    }
-#endif // wxUSE_CTL3D
-
-    HDC hdc = (HDC)pDC;
-    wxColour colBack = GetBackgroundColour();
-
-    if (!IsEnabled() && (GetWindowStyle() & wxTE_MULTILINE) == 0)
-        colBack = wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE);
+    if ( !IsEnabled() && !HasFlag(wxTE_MULTILINE) )
+        return MSWControlColorDisabled(hDC);
 
 
-    ::SetBkColor(hdc, wxColourToRGB(colBack));
-    ::SetTextColor(hdc, wxColourToRGB(GetForegroundColour()));
-
-    wxBrush *brush = wxTheBrushList->FindOrCreateBrush(colBack, wxSOLID);
-
-    return (WXHBRUSH)brush->GetResourceHandle();
+    return wxTextCtrlBase::MSWControlColorSolid(hDC);
 }
 
 bool wxTextCtrl::AdjustSpaceLimit()
 }
 
 bool wxTextCtrl::AdjustSpaceLimit()
@@ -2073,6 +2078,23 @@ void wxTextCtrl::OnSetFocus(wxFocusEvent& WXUNUSED(event))
     }
 }
 
     }
 }
 
+// ----------------------------------------------------------------------------
+// Default colors for MSW text control
+//
+// Set default background color to the native white instead of
+// the default wxSYS_COLOUR_BTNFACE (is triggered with wxNullColour).
+// ----------------------------------------------------------------------------
+
+wxVisualAttributes wxTextCtrl::GetDefaultAttributes() const
+{
+    wxVisualAttributes attrs;
+    attrs.font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
+    attrs.colFg = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
+    attrs.colBg = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); //white
+
+    return attrs;
+}
+
 // the rest of the file only deals with the rich edit controls
 #if wxUSE_RICHEDIT
 
 // the rest of the file only deals with the rich edit controls
 #if wxUSE_RICHEDIT
 
@@ -2176,23 +2198,6 @@ bool wxTextCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
     return wxTextCtrlBase::MSWOnNotify(idCtrl, lParam, result);
 }
 
     return wxTextCtrlBase::MSWOnNotify(idCtrl, lParam, result);
 }
 
-// ----------------------------------------------------------------------------
-// Default colors for MSW text control
-//
-// Set default background color to the native white instead of
-// the default wxSYS_COLOUR_BTNFACE (is triggered with wxNullColour). 
-// ----------------------------------------------------------------------------
-
-wxVisualAttributes wxTextCtrl::GetDefaultAttributes() const
-{
-    wxVisualAttributes attrs;
-    attrs.font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
-    attrs.colFg = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
-    attrs.colBg = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); //white
-
-    return attrs;
-}
-
 // ----------------------------------------------------------------------------
 // colour setting for the rich edit controls
 // ----------------------------------------------------------------------------
 // ----------------------------------------------------------------------------
 // colour setting for the rich edit controls
 // ----------------------------------------------------------------------------
@@ -2457,10 +2462,13 @@ bool wxTextCtrl::SetDefaultStyle(const wxTextAttr& style)
     if ( !wxTextCtrlBase::SetDefaultStyle(style) )
         return false;
 
     if ( !wxTextCtrlBase::SetDefaultStyle(style) )
         return false;
 
-    // we have to do this or the style wouldn't apply for the text typed by the
-    // user
-    long posLast = GetLastPosition();
-    SetStyle(posLast, posLast, m_defaultStyle);
+    if ( IsEditable() )
+    {
+        // we have to do this or the style wouldn't apply for the text typed by
+        // the user
+        wxTextPos posLast = GetLastPosition();
+        SetStyle(posLast, posLast, m_defaultStyle);
+    }
 
     return true;
 }
 
     return true;
 }
@@ -2523,7 +2531,7 @@ bool wxTextCtrl::GetStyle(long position, wxTextAttr& style)
     wxStrcpy(lf.lfFaceName, cf.szFaceName);
 
     //NOTE:  we _MUST_ set each of these values to _something_ since we
     wxStrcpy(lf.lfFaceName, cf.szFaceName);
 
     //NOTE:  we _MUST_ set each of these values to _something_ since we
-    //do not call wxZeroMemory on the LOGFONT lf 
+    //do not call wxZeroMemory on the LOGFONT lf
     if (cf.dwEffects & CFE_ITALIC)
         lf.lfItalic = TRUE;
     else
     if (cf.dwEffects & CFE_ITALIC)
         lf.lfItalic = TRUE;
     else