]> git.saurik.com Git - wxWidgets.git/blobdiff - src/msw/textctrl.cpp
don't write the strings to the stream one char at a time, it's *horribly* slow
[wxWidgets.git] / src / msw / textctrl.cpp
index 6f16032cc9f290dfc25cf3158cc43303e411fc7d..3824a797f4c310e5275900de3a40ba85556efe40 100644 (file)
@@ -195,44 +195,8 @@ bool wxTextCtrl::Create(wxWindow *parent, wxWindowID id,
     if ( parent )
         parent->AddChild(this);
 
-    // translate wxWin style flags to MSW ones, checking for consistency while
-    // doing it
-    long msStyle = ES_LEFT | WS_TABSTOP;
-
-    if ( m_windowStyle & wxCLIP_SIBLINGS )
-        msStyle |= WS_CLIPSIBLINGS;
-
-    if ( m_windowStyle & wxTE_MULTILINE )
-    {
-        wxASSERT_MSG( !(m_windowStyle & wxTE_PROCESS_ENTER),
-                      wxT("wxTE_PROCESS_ENTER style is ignored for multiline text controls (they always process it)") );
-
-        msStyle |= ES_MULTILINE | ES_WANTRETURN;
-        if ((m_windowStyle & wxTE_NO_VSCROLL) == 0)
-            msStyle |= WS_VSCROLL;
-        m_windowStyle |= wxTE_PROCESS_ENTER;
-    }
-    else // !multiline
-    {
-        // there is really no reason to not have this style for single line
-        // text controls
-        msStyle |= ES_AUTOHSCROLL;
-    }
-
-    if ( m_windowStyle & wxHSCROLL )
-        msStyle |= WS_HSCROLL | ES_AUTOHSCROLL;
-
-    if ( m_windowStyle & wxTE_READONLY )
-        msStyle |= ES_READONLY;
-
-    if ( m_windowStyle & wxTE_PASSWORD )
-        msStyle |= ES_PASSWORD;
-
-    if ( m_windowStyle & wxTE_AUTO_SCROLL )
-        msStyle |= ES_AUTOHSCROLL;
-
-    if ( m_windowStyle & wxTE_NOHIDESEL )
-        msStyle |= ES_NOHIDESEL;
+    // translate wxWin style flags to MSW ones
+    WXDWORD msStyle = MSWGetCreateWindowFlags();
 
     // do create the control - either an EDIT or RICHEDIT
     wxString windowClass = wxT("EDIT");
@@ -336,10 +300,17 @@ bool wxTextCtrl::Create(wxWindow *parent, wxWindowID id,
 #if wxUSE_RICHEDIT
     if ( IsRich() )
     {
-        // have to enable events manually
-        LPARAM mask = ENM_CHANGE | ENM_DROPFILES | ENM_SELCHANGE | ENM_UPDATE;
+        // enable the events we're interested in: we want to get EN_CHANGE and
+        // EN_UPDATE as for the normal controls
+        LPARAM mask = ENM_CHANGE | ENM_UPDATE;
 
-        if ( m_windowStyle & wxTE_AUTO_URL )
+        if ( GetRichVersion() == 1 )
+        {
+            // we also need EN_MSGFILTER for richedit 1.0 for the reasons
+            // explained in its handler
+           mask |= ENM_MOUSEEVENTS;
+        }
+        else if ( m_windowStyle & wxTE_AUTO_URL )
         {
             mask |= ENM_LINK;
 
@@ -391,6 +362,74 @@ void wxTextCtrl::AdoptAttributesFromHWND()
         m_windowStyle |= wxTE_PROCESS_ENTER;
 }
 
+WXDWORD wxTextCtrl::MSWGetStyle(long style, WXDWORD *exstyle) const
+{
+    // default border for the text controls is the sunken one
+    if ( (style & wxBORDER_MASK) == wxBORDER_DEFAULT )
+    {
+        style |= wxBORDER_SUNKEN;
+    }
+
+    long msStyle = wxControl::MSWGetStyle(style, exstyle);
+
+    // default styles
+    msStyle |= ES_LEFT;
+
+    if ( style & wxTE_MULTILINE )
+    {
+        wxASSERT_MSG( !(style & wxTE_PROCESS_ENTER),
+                      wxT("wxTE_PROCESS_ENTER style is ignored for multiline text controls (they always process it)") );
+
+        msStyle |= ES_MULTILINE | ES_WANTRETURN;
+        if ( !(style & wxTE_NO_VSCROLL) )
+            msStyle |= WS_VSCROLL;
+
+        style |= wxTE_PROCESS_ENTER;
+    }
+    else // !multiline
+    {
+        // there is really no reason to not have this style for single line
+        // text controls
+        msStyle |= ES_AUTOHSCROLL;
+    }
+
+    if ( style & wxHSCROLL )
+        msStyle |= WS_HSCROLL | ES_AUTOHSCROLL;
+
+    if ( style & wxTE_READONLY )
+        msStyle |= ES_READONLY;
+
+    if ( style & wxTE_PASSWORD )
+        msStyle |= ES_PASSWORD;
+
+    if ( style & wxTE_AUTO_SCROLL )
+        msStyle |= ES_AUTOHSCROLL;
+
+    if ( style & wxTE_NOHIDESEL )
+        msStyle |= ES_NOHIDESEL;
+
+    return msStyle;
+}
+
+void wxTextCtrl::SetWindowStyleFlag(long style)
+{
+#if wxUSE_RICHEDIT
+    // we have to deal with some styles separately because they can't be
+    // changed by simply calling SetWindowLong(GWL_STYLE) but can be changed
+    // using richedit-specific EM_SETOPTIONS
+    if ( IsRich() &&
+            ((style & wxTE_NOHIDESEL) != (GetWindowStyle() & wxTE_NOHIDESEL)) )
+    {
+        bool set = (style & wxTE_NOHIDESEL) != 0;
+
+        ::SendMessage(GetHwnd(), EM_SETOPTIONS, set ? ECOOP_OR : ECOOP_AND,
+                      set ? ECO_NOHIDESEL : ~ECO_NOHIDESEL);
+    }
+#endif // wxUSE_RICHEDIT
+
+    wxControl::SetWindowStyleFlag(style);
+}
+
 // ----------------------------------------------------------------------------
 // set/get the controls text
 // ----------------------------------------------------------------------------
@@ -607,7 +646,7 @@ void wxTextCtrl::DoWriteText(const wxString& value, bool selectionOnly)
 #if wxUSE_UNICODE_MSLU
         // RichEdit doesn't have Unicode version of EM_REPLACESEL on Win9x,
         // but EM_STREAMIN works
-        if ( wxGetOsVersion() == wxWIN95 && GetRichVersion() > 1 )
+        if ( wxUsingUnicowsDll() && GetRichVersion() > 1 )
         {
            done = StreamIn(valueDos, wxFONTENCODING_SYSTEM, selectionOnly);
         }
@@ -639,7 +678,10 @@ void wxTextCtrl::DoWriteText(const wxString& value, bool selectionOnly)
     {
         if ( !selectionOnly )
         {
-            SetSelection(-1, -1);
+            //SetSelection(-1, -1);
+            // This eliminates an annoying flashing effect
+            // when replacing all text.
+            Clear();
         }
 
         ::SendMessage(GetHwnd(), EM_REPLACESEL, 0, (LPARAM)valueDos.c_str());
@@ -660,6 +702,25 @@ void wxTextCtrl::Clear()
     ::SetWindowText(GetHwnd(), wxT(""));
 }
 
+#ifdef __WIN32__
+
+bool wxTextCtrl::EmulateKeyPress(const wxKeyEvent& event)
+{
+    SetFocus();
+
+    size_t lenOld = GetValue().length();
+
+    wxUint32 code = event.GetRawKeyCode();
+    ::keybd_event(code, 0, 0 /* key press */, 0);
+    ::keybd_event(code, 0, KEYEVENTF_KEYUP, 0);
+
+    // assume that any alphanumeric key changes the total number of characters
+    // in the control - this should work in 99% of cases
+    return GetValue().length() != lenOld;
+}
+
+#endif // __WIN32__
+
 // ----------------------------------------------------------------------------
 // Clipboard operations
 // ----------------------------------------------------------------------------
@@ -812,6 +873,12 @@ void wxTextCtrl::GetSelection(long* from, long* to) const
 
 bool wxTextCtrl::IsEditable() const
 {
+    // strangely enough, we may be called before the control is created: our
+    // own Create() calls MSWGetStyle() which calls AcceptsFocus() which calls
+    // us
+    if ( !m_hWnd )
+        return TRUE;
+
     long style = ::GetWindowLong(GetHwnd(), GWL_STYLE);
 
     return (style & ES_READONLY) == 0;
@@ -1087,7 +1154,7 @@ bool wxTextCtrl::MSWShouldPreProcessMessage(WXMSG* pMsg)
     // usual preprocessing for them
     if ( msg->message == WM_KEYDOWN )
     {
-        WORD vkey = msg->wParam;
+        WORD vkey = (WORD) msg->wParam;
         if ( (HIWORD(msg->lParam) & KF_ALTDOWN) == KF_ALTDOWN )
         {
             if ( vkey == VK_BACK )
@@ -1160,7 +1227,8 @@ void wxTextCtrl::OnChar(wxKeyEvent& event)
 
 long wxTextCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
 {
-    // we always want the characters and the arrows
+    long lRc = wxTextCtrlBase::MSWWindowProc(nMsg, wParam, lParam);
+
     if ( nMsg == WM_GETDLGCODE )
     {
         // we always want the chars and the arrows
@@ -1168,22 +1236,30 @@ long wxTextCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
 
         // we may have several different cases:
         // 1. normal case: both TAB and ENTER are used for dialog navigation
-        // 2. ctrl which wants TAB for itself: ENTER is used to pass to the next
-        //    control in the dialog
-        // 3. ctrl which wants ENTER for itself: TAB is used for dialog navigation
-        // 4. ctrl which wants both TAB and ENTER: Ctrl-ENTER is used to pass to
-        //    the next control
-        if ( m_windowStyle & wxTE_PROCESS_ENTER )
+        // 2. ctrl which wants TAB for itself: ENTER is used to pass to the
+        //    next control in the dialog
+        // 3. ctrl which wants ENTER for itself: TAB is used for dialog
+        //    navigation
+        // 4. ctrl which wants both TAB and ENTER: Ctrl-ENTER is used to pass
+        //    to the next control
+
+        // the multiline edit control should always get <Return> for itself
+        if ( HasFlag(wxTE_PROCESS_ENTER) || HasFlag(wxTE_MULTILINE) )
             lDlgCode |= DLGC_WANTMESSAGE;
-        if ( m_windowStyle & wxTE_PROCESS_TAB )
+
+        if ( HasFlag(wxTE_PROCESS_TAB) )
             lDlgCode |= DLGC_WANTTAB;
 
-        return lDlgCode;
+        lRc |= lDlgCode;
     }
 
-    return wxTextCtrlBase::MSWWindowProc(nMsg, wParam, lParam);
+    return lRc;
 }
 
+// ----------------------------------------------------------------------------
+// text control event processing
+// ----------------------------------------------------------------------------
+
 bool wxTextCtrl::MSWCommand(WXUINT param, WXWORD WXUNUSED(id))
 {
     switch (param)
@@ -1225,6 +1301,7 @@ bool wxTextCtrl::MSWCommand(WXUINT param, WXWORD WXUNUSED(id))
         case EN_HSCROLL:
         case EN_VSCROLL:
             return FALSE;
+
         default:
             return FALSE;
     }
@@ -1440,63 +1517,94 @@ void wxTextCtrl::OnUpdateRedo(wxUpdateUIEvent& event)
 bool wxTextCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
 {
     NMHDR *hdr = (NMHDR* )lParam;
-    if ( hdr->code == EN_LINK )
+    switch ( hdr->code )
     {
-        ENLINK *enlink = (ENLINK *)hdr;
-
-        switch ( enlink->msg )
-        {
-            case WM_SETCURSOR:
-                // ok, so it is hardcoded - do we really nee to customize it?
-                ::SetCursor(GetHcursorOf(wxCursor(wxCURSOR_HAND)));
-                *result = TRUE;
-                break;
-
-            case WM_MOUSEMOVE:
-            case WM_LBUTTONDOWN:
-            case WM_LBUTTONUP:
-            case WM_LBUTTONDBLCLK:
-            case WM_RBUTTONDOWN:
-            case WM_RBUTTONUP:
-            case WM_RBUTTONDBLCLK:
-                // send a mouse event
+        case EN_MSGFILTER:
+            {
+                const MSGFILTER *msgf = (MSGFILTER *)lParam;
+                UINT msg = msgf->msg;
+
+                // this is a bit crazy but richedit 1.0 sends us all mouse
+                // events _except_ WM_LBUTTONUP (don't ask me why) so we have
+                // generate the wxWin events for this message manually
+                //
+                // NB: in fact, this is still not totally correct as it does
+                //     send us WM_LBUTTONUP if the selection was cleared by the
+                //     last click -- so currently we get 2 events in this case,
+                //     but as I don't see any obvious way to check for this I
+                //     leave this code in place because it's still better than
+                //     not getting left up events at all
+                if ( msg == WM_LBUTTONUP )
                 {
-                    static const wxEventType eventsMouse[] =
-                    {
-                        wxEVT_MOTION,
-                        wxEVT_LEFT_DOWN,
-                        wxEVT_LEFT_UP,
-                        wxEVT_LEFT_DCLICK,
-                        wxEVT_RIGHT_DOWN,
-                        wxEVT_RIGHT_UP,
-                        wxEVT_RIGHT_DCLICK,
-                    };
-
-                    // the event ids are consecutive
-                    wxMouseEvent
-                        evtMouse(eventsMouse[enlink->msg - WM_MOUSEMOVE]);
-
-                    InitMouseEvent(evtMouse,
-                                   GET_X_LPARAM(enlink->lParam),
-                                   GET_Y_LPARAM(enlink->lParam),
-                                   enlink->wParam);
-
-                    wxTextUrlEvent event(m_windowId, evtMouse,
-                                         enlink->chrg.cpMin,
-                                         enlink->chrg.cpMax);
-
-                    InitCommandEvent(event);
-
-                    *result = ProcessCommand(event);
+                    WXUINT flags = msgf->wParam;
+                    int x = GET_X_LPARAM(msgf->lParam),
+                        y = GET_Y_LPARAM(msgf->lParam);
+
+                    HandleMouseEvent(msg, x, y, flags);
                 }
-                break;
-        }
+            }
 
-        return TRUE;
+            // return TRUE to process the event (and FALSE to ignore it)
+            return TRUE;
+
+        case EN_LINK:
+            {
+                const ENLINK *enlink = (ENLINK *)hdr;
+
+                switch ( enlink->msg )
+                {
+                    case WM_SETCURSOR:
+                        // ok, so it is hardcoded - do we really nee to
+                        // customize it?
+                        ::SetCursor(GetHcursorOf(wxCursor(wxCURSOR_HAND)));
+                        *result = TRUE;
+                        break;
+
+                    case WM_MOUSEMOVE:
+                    case WM_LBUTTONDOWN:
+                    case WM_LBUTTONUP:
+                    case WM_LBUTTONDBLCLK:
+                    case WM_RBUTTONDOWN:
+                    case WM_RBUTTONUP:
+                    case WM_RBUTTONDBLCLK:
+                        // send a mouse event
+                        {
+                            static const wxEventType eventsMouse[] =
+                            {
+                                wxEVT_MOTION,
+                                wxEVT_LEFT_DOWN,
+                                wxEVT_LEFT_UP,
+                                wxEVT_LEFT_DCLICK,
+                                wxEVT_RIGHT_DOWN,
+                                wxEVT_RIGHT_UP,
+                                wxEVT_RIGHT_DCLICK,
+                            };
+
+                            // the event ids are consecutive
+                            wxMouseEvent
+                                evtMouse(eventsMouse[enlink->msg - WM_MOUSEMOVE]);
+
+                            InitMouseEvent(evtMouse,
+                                           GET_X_LPARAM(enlink->lParam),
+                                           GET_Y_LPARAM(enlink->lParam),
+                                           enlink->wParam);
+
+                            wxTextUrlEvent event(m_windowId, evtMouse,
+                                                 enlink->chrg.cpMin,
+                                                 enlink->chrg.cpMax);
+
+                            InitCommandEvent(event);
+
+                            *result = ProcessCommand(event);
+                        }
+                        break;
+                }
+            }
+            return TRUE;
     }
 
-    // not processed
-    return FALSE;
+    // not processed, leave it to the base class
+    return wxTextCtrlBase::MSWOnNotify(idCtrl, lParam, result);
 }
 
 // ----------------------------------------------------------------------------
@@ -1692,7 +1800,7 @@ bool wxRichEditModule::OnInit()
 
 void wxRichEditModule::OnExit()
 {
-    for ( int i = 0; i < WXSIZEOF(ms_hRichEdit); i++ )
+    for ( size_t i = 0; i < WXSIZEOF(ms_hRichEdit); i++ )
     {
         if ( ms_hRichEdit[i] )
         {