]> git.saurik.com Git - wxWidgets.git/blobdiff - src/msw/textctrl.cpp
background colour of a combobox may now be set (bug 805442)
[wxWidgets.git] / src / msw / textctrl.cpp
index f70aa21b7cc4fe296a4af1c64489e5ad8a68cf6f..91ca4d8ce44e417c371b819746c9d2d31721be55 100644 (file)
 
 #endif // wxUSE_RICHEDIT
 
-// ----------------------------------------------------------------------------
-// private functions
-// ----------------------------------------------------------------------------
-
-#if wxUSE_RICHEDIT
-
-DWORD CALLBACK wxRichEditStreamIn(DWORD dwCookie, BYTE *buf, LONG cb, LONG *pcb);
-
-#endif // wxUSE_RICHEDIT
-
 // ----------------------------------------------------------------------------
 // private classes
 // ----------------------------------------------------------------------------
@@ -135,14 +125,14 @@ wxBEGIN_FLAGS( wxTextCtrlStyle )
     wxFLAGS_MEMBER(wxDOUBLE_BORDER)
     wxFLAGS_MEMBER(wxRAISED_BORDER)
     wxFLAGS_MEMBER(wxSTATIC_BORDER)
-    wxFLAGS_MEMBER(wxNO_BORDER)
+    wxFLAGS_MEMBER(wxBORDER)
 
     // standard window styles
     wxFLAGS_MEMBER(wxTAB_TRAVERSAL)
     wxFLAGS_MEMBER(wxCLIP_CHILDREN)
     wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW)
     wxFLAGS_MEMBER(wxWANTS_CHARS)
-    wxFLAGS_MEMBER(wxNO_FULL_REPAINT_ON_RESIZE)
+    wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE)
     wxFLAGS_MEMBER(wxALWAYS_SHOW_SB )
     wxFLAGS_MEMBER(wxVSCROLL)
     wxFLAGS_MEMBER(wxHSCROLL)
@@ -537,6 +527,52 @@ wxString wxTextCtrl::GetRange(long from, long to) const
         int len = GetWindowTextLength(GetHwnd());
         if ( len > from )
         {
+            if ( to == -1 )
+                to = len;
+
+#if !wxUSE_UNICODE
+            // we must use EM_STREAMOUT if we don't want to lose all characters
+            // not representable in the current character set (EM_GETTEXTRANGE
+            // simply replaces them with question marks...)
+            if ( GetRichVersion() > 1 )
+            {
+                // we must have some encoding, otherwise any 8bit chars in the
+                // control are simply *lost* (replaced by '?')
+                wxFontEncoding encoding = wxFONTENCODING_SYSTEM;
+
+                wxFont font = m_defaultStyle.GetFont();
+                if ( !font.Ok() )
+                    font = GetFont();
+
+                if ( font.Ok() )
+                {
+                   encoding = font.GetEncoding();
+                }
+
+                if ( encoding == wxFONTENCODING_SYSTEM )
+                {
+                    encoding = wxLocale::GetSystemEncoding();
+                }
+
+                if ( encoding == wxFONTENCODING_SYSTEM )
+                {
+                    encoding = wxFONTENCODING_ISO8859_1;
+                }
+
+                str = StreamOut(encoding);
+
+                if ( !str.empty() )
+                {
+                    // we have to manually extract the required part, luckily
+                    // this is easy in this case as EOL characters in str are
+                    // just LFs because we remove CRs in wxRichEditStreamOut
+                    str = str.Mid(from, to - from);
+                }
+            }
+
+            // StreamOut() wasn't used or failed, try to do it in normal way
+            if ( str.empty() )
+#endif // !wxUSE_UNICODE
             {
                 // alloc one extra WORD as needed by the control
                 wxStringBuffer tmp(str, ++len);
@@ -544,7 +580,7 @@ wxString wxTextCtrl::GetRange(long from, long to) const
 
                 TEXTRANGE textRange;
                 textRange.chrg.cpMin = from;
-                textRange.chrg.cpMax = to == -1 ? len : to;
+                textRange.chrg.cpMax = to;
                 textRange.lpstrText = p;
 
                 (void)SendMessage(GetHwnd(), EM_GETTEXTRANGE,
@@ -601,6 +637,9 @@ void wxTextCtrl::SetValue(const wxString& value)
     if ( (value.length() > 0x400) || (value != GetValue()) )
     {
         DoWriteText(value, FALSE /* not selection only */);
+        
+        // for compatibility, don't move the cursor when doing SetValue()
+        SetInsertionPoint(0);
     }
     else // same text
     {
@@ -613,19 +652,21 @@ void wxTextCtrl::SetValue(const wxString& value)
     // mark the control as being not dirty - we changed its text, not the
     // user
     DiscardEdits();
-
-    // for compatibility, don't move the cursor when doing SetValue()
-    SetInsertionPoint(0);
 }
 
 #if wxUSE_RICHEDIT && (!wxUSE_UNICODE || wxUSE_UNICODE_MSLU)
 
-DWORD CALLBACK wxRichEditStreamIn(DWORD dwCookie, BYTE *buf, LONG cb, LONG *pcb)
+// TODO: using memcpy() would improve performance a lot for big amounts of text
+
+DWORD CALLBACK
+wxRichEditStreamIn(DWORD dwCookie, BYTE *buf, LONG cb, LONG *pcb)
 {
     *pcb = 0;
 
+    const wchar_t ** const ppws = (const wchar_t **)dwCookie;
+
     wchar_t *wbuf = (wchar_t *)buf;
-    const wchar_t *wpc = *(const wchar_t **)dwCookie;
+    const wchar_t *wpc = *ppws;
     while ( cb && *wpc )
     {
         *wbuf++ = *wpc++;
@@ -634,56 +675,77 @@ DWORD CALLBACK wxRichEditStreamIn(DWORD dwCookie, BYTE *buf, LONG cb, LONG *pcb)
         (*pcb) += sizeof(wchar_t);
     }
 
-    *(const wchar_t **)dwCookie = wpc;
+    *ppws = wpc;
 
     return 0;
 }
 
-// from utils.cpp
-extern WXDLLIMPEXP_BASE long wxEncodingToCodepage(wxFontEncoding encoding);
-
-#if wxUSE_UNICODE_MSLU
-bool wxTextCtrl::StreamIn(const wxString& value,
-                          wxFontEncoding WXUNUSED(encoding),
-                          bool selectionOnly)
+// helper struct used to pass parameters from wxTextCtrl to wxRichEditStreamOut
+struct wxStreamOutData
 {
-    const wchar_t *wpc = value.c_str();
-#else // !wxUSE_UNICODE_MSLU
-bool wxTextCtrl::StreamIn(const wxString& value,
-                          wxFontEncoding encoding,
-                          bool selectionOnly)
+    wchar_t *wpc;
+    size_t len;
+};
+
+DWORD CALLBACK
+wxRichEditStreamOut(DWORD dwCookie, BYTE *buf, LONG cb, LONG *pcb)
 {
-    // we have to use EM_STREAMIN to force richedit control 2.0+ to show any
-    // text in the non default charset -- otherwise it thinks it knows better
-    // than we do and always shows it in the default one
+    *pcb = 0;
 
-    // first get the Windows code page for this encoding
-    long codepage = wxEncodingToCodepage(encoding);
-    if ( codepage == -1 )
+    wxStreamOutData *data = (wxStreamOutData *)dwCookie;
+
+    const wchar_t *wbuf = (const wchar_t *)buf;
+    wchar_t *wpc = data->wpc;
+    while ( cb )
     {
-        // unknown encoding
-        return FALSE;
+        wchar_t wch = *wbuf++;
+
+        // turn "\r\n" into "\n" on the fly
+        if ( wch != L'\r' )
+            *wpc++ = wch;
+        else
+            data->len--;
+
+        cb -= sizeof(wchar_t);
+        (*pcb) += sizeof(wchar_t);
     }
 
-    // next translate to Unicode using this code page
-    int len = ::MultiByteToWideChar(codepage, 0, value, -1, NULL, 0);
+    data->wpc = wpc;
+
+    return 0;
+}
+
+
+#if wxUSE_UNICODE_MSLU
+    #define UNUSED_IF_MSLU(param)
+#else
+    #define UNUSED_IF_MSLU(param) param
+#endif
+
+bool
+wxTextCtrl::StreamIn(const wxString& value,
+                     wxFontEncoding UNUSED_IF_MSLU(encoding),
+                     bool selectionOnly)
+{
+#if wxUSE_UNICODE_MSLU
+    const wchar_t *wpc = value.c_str();
+#else // !wxUSE_UNICODE_MSLU
+    wxCSConv conv(encoding);
+
+    const size_t len = conv.MB2WC(NULL, value, value.length());
 
 #if wxUSE_WCHAR_T
     wxWCharBuffer wchBuf(len);
+    wchar_t *wpc = wchBuf.data();
 #else
     wchar_t *wchBuf = (wchar_t *)malloc((len + 1)*sizeof(wchar_t));
+    wchar_t *wpc = wchBuf;
 #endif
 
-    if ( !::MultiByteToWideChar(codepage, 0, value, -1,
-                                (wchar_t *)(const wchar_t *)wchBuf, len) )
-    {
-        wxLogLastError(_T("MultiByteToWideChar"));
-    }
-
-    // finally, stream it in the control
-    const wchar_t *wpc = wchBuf;
+    conv.MB2WC(wpc, value, value.length());
 #endif // wxUSE_UNICODE_MSLU
 
+    // finally, stream it in the control
     EDITSTREAM eds;
     wxZeroMemory(eds);
     eds.dwCookie = (DWORD)&wpc;
@@ -716,6 +778,69 @@ bool wxTextCtrl::StreamIn(const wxString& value,
     return TRUE;
 }
 
+#if !wxUSE_UNICODE_MSLU
+
+wxString
+wxTextCtrl::StreamOut(wxFontEncoding encoding, bool selectionOnly) const
+{
+    wxString out;
+
+    const int len = GetWindowTextLength(GetHwnd());
+
+#if wxUSE_WCHAR_T
+    wxWCharBuffer wchBuf(len);
+    wchar_t *wpc = wchBuf.data();
+#else
+    wchar_t *wchBuf = (wchar_t *)malloc((len + 1)*sizeof(wchar_t));
+    wchar_t *wpc = wchBuf;
+#endif
+
+    wxStreamOutData data;
+    data.wpc = wpc;
+    data.len = len;
+
+    EDITSTREAM eds;
+    wxZeroMemory(eds);
+    eds.dwCookie = (DWORD)&data;
+    eds.pfnCallback = wxRichEditStreamOut;
+
+    ::SendMessage
+      (
+        GetHwnd(),
+        EM_STREAMOUT,
+        SF_TEXT | SF_UNICODE | (selectionOnly ? SFF_SELECTION : 0),
+        (LPARAM)&eds
+      );
+
+    if ( eds.dwError )
+    {
+        wxLogLastError(_T("EM_STREAMOUT"));
+    }
+    else // streamed out ok
+    {
+        // NUL-terminate the string because its length could have been
+        // decreased by wxRichEditStreamOut
+        *(wchBuf.data() + data.len) = L'\0';
+
+        // now convert to the given encoding (this is a possibly lossful
+        // conversion but what else can we do)
+        wxCSConv conv(encoding);
+        size_t lenNeeded = conv.WC2MB(NULL, wchBuf, 0);
+        if ( lenNeeded++ )
+        {
+            conv.WC2MB(wxStringBuffer(out, lenNeeded), wchBuf, lenNeeded);
+        }
+    }
+
+#if !wxUSE_WCHAR_T
+    free(wchBuf);
+#endif // !wxUSE_WCHAR_T
+
+    return out;
+}
+
+#endif // !wxUSE_UNICODE_MSLU
+
 #endif // wxUSE_RICHEDIT
 
 void wxTextCtrl::WriteText(const wxString& value)
@@ -767,6 +892,10 @@ void wxTextCtrl::DoWriteText(const wxString& value, bool selectionOnly)
                wxFontEncoding encoding = font.GetEncoding();
                if ( encoding != wxFONTENCODING_SYSTEM )
                {
+                   // we have to use EM_STREAMIN to force richedit control 2.0+
+                   // to show any text in the non default charset -- otherwise
+                   // it thinks it knows better than we do and always shows it
+                   // in the default one
                    done = StreamIn(valueDos, encoding, selectionOnly);
                }
             }
@@ -1127,6 +1256,23 @@ void wxTextCtrl::DoSetSelection(long from, long to, bool scrollCaret)
 #endif // Win32/16
 }
 
+// ----------------------------------------------------------------------------
+// Working with files
+// ----------------------------------------------------------------------------
+
+bool wxTextCtrl::LoadFile(const wxString& file)
+{
+    if ( wxTextCtrlBase::LoadFile(file) )
+    {
+        // update the size limit if needed
+        AdjustSpaceLimit();
+
+        return TRUE;
+    }
+
+    return FALSE;
+}
+
 // ----------------------------------------------------------------------------
 // Editing
 // ----------------------------------------------------------------------------
@@ -1136,13 +1282,7 @@ void wxTextCtrl::Replace(long from, long to, const wxString& value)
     // Set selection and remove it
     DoSetSelection(from, to, FALSE /* don't scroll caret into view */);
 
-    SendMessage(GetHwnd(), EM_REPLACESEL,
-#ifdef __WIN32__
-                TRUE,
-#else
-                FALSE,
-#endif
-                (LPARAM)value.c_str());
+    DoWriteText(value, TRUE /* selection only */);
 }
 
 void wxTextCtrl::Remove(long from, long to)
@@ -1150,25 +1290,16 @@ void wxTextCtrl::Remove(long from, long to)
     Replace(from, to, wxEmptyString);
 }
 
-bool wxTextCtrl::LoadFile(const wxString& file)
+bool wxTextCtrl::IsModified() const
 {
-    if ( wxTextCtrlBase::LoadFile(file) )
-    {
-        // update the size limit if needed
-        AdjustSpaceLimit();
-
-        return TRUE;
-    }
-
-    return FALSE;
+    return SendMessage(GetHwnd(), EM_GETMODIFY, 0, 0) != 0;
 }
 
-bool wxTextCtrl::IsModified() const
+void wxTextCtrl::MarkDirty()
 {
-    return SendMessage(GetHwnd(), EM_GETMODIFY, 0, 0) != 0;
+    SendMessage(GetHwnd(), EM_SETMODIFY, TRUE, 0L);
 }
 
-// Makes 'unmodified'
 void wxTextCtrl::DiscardEdits()
 {
     SendMessage(GetHwnd(), EM_SETMODIFY, FALSE, 0L);
@@ -1179,6 +1310,10 @@ int wxTextCtrl::GetNumberOfLines() const
     return (int)SendMessage(GetHwnd(), EM_GETLINECOUNT, (WPARAM)0, (LPARAM)0);
 }
 
+// ----------------------------------------------------------------------------
+// Positions <-> coords
+// ----------------------------------------------------------------------------
+
 long wxTextCtrl::XYToPosition(long x, long y) const
 {
     // This gets the char index for the _beginning_ of this line
@@ -1226,6 +1361,99 @@ bool wxTextCtrl::PositionToXY(long pos, long *x, long *y) const
     return TRUE;
 }
 
+wxTextCtrlHitTestResult
+wxTextCtrl::HitTest(const wxPoint& pt, wxTextCoord *col, wxTextCoord *row) const
+{
+    // first get the position from Windows
+    LPARAM lParam;
+
+#if wxUSE_RICHEDIT
+    POINTL ptl;
+    if ( IsRich() )
+    {
+        // for rich edit controls the position is passed iva the struct fields
+        ptl.x = pt.x;
+        ptl.y = pt.y;
+        lParam = (LPARAM)&ptl;
+    }
+    else
+#endif // wxUSE_RICHEDIT
+    {
+        // for the plain ones, we are limited to 16 bit positions which are
+        // combined in a single 32 bit value
+        lParam = MAKELPARAM(pt.x, pt.y);
+    }
+
+    LRESULT pos = SendMessage(GetHwnd(), EM_CHARFROMPOS, 0, lParam);
+
+    if ( pos == -1 )
+    {
+        // this seems to indicate an error...
+        return wxTE_HT_UNKNOWN;
+    }
+
+#if wxUSE_RICHEDIT
+    if ( !IsRich() )
+#endif // wxUSE_RICHEDIT
+    {
+        // for plain EDIT controls the higher word contains something else
+        pos = LOWORD(pos);
+    }
+
+
+    // next determine where it is relatively to our point: EM_CHARFROMPOS
+    // always returns the closest character but we need to be more precise, so
+    // double check that we really are where it pretends
+    POINTL ptReal;
+
+#if wxUSE_RICHEDIT
+    // FIXME: we need to distinguish between richedit 2 and 3 here somehow but
+    //        we don't know how to do it
+    if ( IsRich() )
+    {
+        SendMessage(GetHwnd(), EM_POSFROMCHAR, (WPARAM)&ptReal, pos);
+    }
+    else
+#endif // wxUSE_RICHEDIT
+    {
+        LRESULT lRc = SendMessage(GetHwnd(), EM_POSFROMCHAR, pos, 0);
+
+        if ( lRc == -1 )
+        {
+            // this is apparently returned when pos corresponds to the last
+            // position
+            ptReal.x =
+            ptReal.y = 0;
+        }
+        else
+        {
+            ptReal.x = LOWORD(lRc);
+            ptReal.y = HIWORD(lRc);
+        }
+    }
+
+    wxTextCtrlHitTestResult rc;
+
+    if ( pt.y > ptReal.y + GetCharHeight() )
+        rc = wxTE_HT_BELOW;
+    else if ( pt.x > ptReal.x + GetCharWidth() )
+        rc = wxTE_HT_BEYOND;
+    else
+        rc = wxTE_HT_ON_TEXT;
+
+    // finally translate to column/row
+    if ( !PositionToXY(pos, col, row) )
+    {
+        wxFAIL_MSG( _T("PositionToXY() not expected to fail in HitTest()") );
+    }
+
+    return rc;
+}
+
+// ----------------------------------------------------------------------------
+// 
+// ----------------------------------------------------------------------------
+
 void wxTextCtrl::ShowPosition(long pos)
 {
     HWND hWnd = GetHwnd();
@@ -1451,7 +1679,7 @@ void wxTextCtrl::OnChar(wxKeyEvent& event)
     switch ( event.GetKeyCode() )
     {
         case WXK_RETURN:
-            if ( !(m_windowStyle & wxTE_MULTILINE) )
+            if ( !HasFlag(wxTE_MULTILINE) )
             {
                 wxCommandEvent event(wxEVT_COMMAND_TEXT_ENTER, m_windowId);
                 InitCommandEvent(event);
@@ -1464,10 +1692,26 @@ void wxTextCtrl::OnChar(wxKeyEvent& event)
             break;
 
         case WXK_TAB:
-            // always produce navigation event - even if we process TAB
+            // always produce navigation event -- even if we process TAB
             // ourselves the fact that we got here means that the user code
-            // decided to skip processing of this TAB - probably to let it
+            // decided to skip processing of this TAB -- probably to let it
             // do its default job.
+
+            // ok, so this is getting absolutely ridiculous but I don't see
+            // any other way to fix this bug: when a multiline text control is
+            // inside a wxFrame, we need to generate the navigation event as
+            // otherwise nothing happens at all, but when the same control is
+            // created inside a dialog, IsDialogMessage() *does* switch focus
+            // all by itself and so if we do it here as well, it is advanced
+            // twice and goes to the next control... to prevent this from
+            // happening we're doing this ugly check, the logic being that if
+            // we don't have focus then it had been already changed to the next
+            // control
+            //
+            // the right thing to do would, of course, be to understand what
+            // the hell is IsDialogMessage() doing but this is beyond my feeble
+            // forces at the moment unfortunately
+            if ( FindFocus() == this )
             {
                 wxNavigationKeyEvent eventNav;
                 eventNav.SetDirection(!event.ShiftDown());