// declarations
// ============================================================================
-#ifdef __GNUG__
+#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
#pragma implementation "textctrl.h"
#endif
#include <windowsx.h>
#include "wx/msw/private.h"
+#include "wx/msw/winundef.h"
#include <string.h>
#include <stdlib.h>
+
+#ifndef __WXWINCE__
#include <sys/types.h>
+#endif
#if wxUSE_RICHEDIT
#endif // wxUSE_RICHEDIT
-// ----------------------------------------------------------------------------
-// private functions
-// ----------------------------------------------------------------------------
-
-#if wxUSE_RICHEDIT
-
-DWORD CALLBACK wxRichEditStreamIn(DWORD dwCookie, BYTE *buf, LONG cb, LONG *pcb);
-
-#endif // wxUSE_RICHEDIT
-
// ----------------------------------------------------------------------------
// private classes
// ----------------------------------------------------------------------------
// event tables and other macros
// ----------------------------------------------------------------------------
+#if wxUSE_EXTENDED_RTTI
+WX_DEFINE_FLAGS( wxTextCtrlStyle )
+
+wxBEGIN_FLAGS( wxTextCtrlStyle )
+ // new style border flags, we put them first to
+ // use them for streaming out
+ wxFLAGS_MEMBER(wxBORDER_SIMPLE)
+ wxFLAGS_MEMBER(wxBORDER_SUNKEN)
+ wxFLAGS_MEMBER(wxBORDER_DOUBLE)
+ wxFLAGS_MEMBER(wxBORDER_RAISED)
+ wxFLAGS_MEMBER(wxBORDER_STATIC)
+ wxFLAGS_MEMBER(wxBORDER_NONE)
+
+ // old style border flags
+ wxFLAGS_MEMBER(wxSIMPLE_BORDER)
+ wxFLAGS_MEMBER(wxSUNKEN_BORDER)
+ wxFLAGS_MEMBER(wxDOUBLE_BORDER)
+ wxFLAGS_MEMBER(wxRAISED_BORDER)
+ wxFLAGS_MEMBER(wxSTATIC_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(wxFULL_REPAINT_ON_RESIZE)
+ wxFLAGS_MEMBER(wxALWAYS_SHOW_SB )
+ wxFLAGS_MEMBER(wxVSCROLL)
+ wxFLAGS_MEMBER(wxHSCROLL)
+
+ wxFLAGS_MEMBER(wxTE_PROCESS_ENTER)
+ wxFLAGS_MEMBER(wxTE_PROCESS_TAB)
+ wxFLAGS_MEMBER(wxTE_MULTILINE)
+ wxFLAGS_MEMBER(wxTE_PASSWORD)
+ wxFLAGS_MEMBER(wxTE_READONLY)
+ wxFLAGS_MEMBER(wxHSCROLL)
+ wxFLAGS_MEMBER(wxTE_RICH)
+ wxFLAGS_MEMBER(wxTE_RICH2)
+ wxFLAGS_MEMBER(wxTE_AUTO_URL)
+ wxFLAGS_MEMBER(wxTE_NOHIDESEL)
+ wxFLAGS_MEMBER(wxTE_LEFT)
+ wxFLAGS_MEMBER(wxTE_CENTRE)
+ wxFLAGS_MEMBER(wxTE_RIGHT)
+ wxFLAGS_MEMBER(wxTE_DONTWRAP)
+ wxFLAGS_MEMBER(wxTE_LINEWRAP)
+ wxFLAGS_MEMBER(wxTE_WORDWRAP)
+
+wxEND_FLAGS( wxTextCtrlStyle )
+
+IMPLEMENT_DYNAMIC_CLASS_XTI(wxTextCtrl, wxControl,"wx/textctrl.h")
+
+wxBEGIN_PROPERTIES_TABLE(wxTextCtrl)
+ wxEVENT_PROPERTY( TextUpdated , wxEVT_COMMAND_TEXT_UPDATED , wxCommandEvent )
+ wxEVENT_PROPERTY( TextEnter , wxEVT_COMMAND_TEXT_ENTER , wxCommandEvent )
+
+ wxPROPERTY( Font , wxFont , SetFont , GetFont ,, 0 /*flags*/ , wxT("Helpstring") , wxT("group") )
+ wxPROPERTY( Value , wxString , SetValue, GetValue, wxString() , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
+ wxPROPERTY_FLAGS( WindowStyle , wxTextCtrlStyle , long , SetWindowStyleFlag , GetWindowStyleFlag , , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style
+wxEND_PROPERTIES_TABLE()
+
+wxBEGIN_HANDLERS_TABLE(wxTextCtrl)
+wxEND_HANDLERS_TABLE()
+
+wxCONSTRUCTOR_6( wxTextCtrl , wxWindow* , Parent , wxWindowID , Id , wxString , Value , wxPoint , Position , wxSize , Size , long , WindowStyle)
+#else
IMPLEMENT_DYNAMIC_CLASS(wxTextCtrl, wxControl)
+#endif
+
BEGIN_EVENT_TABLE(wxTextCtrl, wxControl)
EVT_CHAR(wxTextCtrl::OnChar)
#ifdef __WIN16__
EVT_ERASE_BACKGROUND(wxTextCtrl::OnEraseBackground)
#endif
+
+ EVT_SET_FOCUS(wxTextCtrl::OnSetFocus)
END_EVENT_TABLE()
// ============================================================================
m_privateContextMenu = NULL;
m_suppressNextUpdate = FALSE;
+ m_isNativeCaretShown = true;
}
wxTextCtrl::~wxTextCtrl()
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);
// styles which we alaways add by default
msStyle |= ES_AUTOHSCROLL;
}
- // styles which we add depending on the specified wxWindows styles
- if ( style & wxHSCROLL )
+ // note that wxTE_DONTWRAP is the same as wxHSCROLL so if we have a horz
+ // scrollbar, there is no wrapping -- which makes sense
+ if ( style & wxTE_DONTWRAP )
{
// automatically scroll the control horizontally as necessary
- msStyle |= WS_HSCROLL;// | ES_AUTOHSCROLL;
+ msStyle |= WS_HSCROLL;
}
if ( style & wxTE_READONLY )
else if ( style & wxTE_RIGHT )
msStyle |= ES_RIGHT;
else
- msStyle |= ES_LEFT; // ES_LEFT if 0 as well but for consistency...
+ msStyle |= ES_LEFT; // ES_LEFT is 0 as well but for consistency...
return msStyle;
}
void wxTextCtrl::SetWindowStyleFlag(long style)
{
- if ( (style & wxBORDER_MASK) == wxBORDER_DEFAULT )
- style |= wxBORDER_SUNKEN;
-
#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
int len = GetWindowTextLength(GetHwnd());
if ( len > from )
{
- // alloc one extra WORD as needed by the control
- wxChar *p = str.GetWriteBuf(++len);
+ if ( to == -1 )
+ to = len;
- TEXTRANGE textRange;
- textRange.chrg.cpMin = from;
- textRange.chrg.cpMax = to == -1 ? len : to;
- textRange.lpstrText = p;
+#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;
- (void)SendMessage(GetHwnd(), EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
+ wxFont font = m_defaultStyle.GetFont();
+ if ( !font.Ok() )
+ font = GetFont();
- if ( m_verRichEdit > 1 )
- {
- // RichEdit 2.0 uses just CR ('\r') for the newlines which is
- // neither Unix nor Windows style - convert it to something
- // reasonable
- for ( ; *p; p++ )
+ if ( font.Ok() )
+ {
+ encoding = font.GetEncoding();
+ }
+
+ if ( encoding == wxFONTENCODING_SYSTEM )
{
- if ( *p == _T('\r') )
- *p = _T('\n');
+ 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);
}
}
- str.UngetWriteBuf();
+ // 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);
+ wxChar *p = tmp;
+
+ TEXTRANGE textRange;
+ textRange.chrg.cpMin = from;
+ textRange.chrg.cpMax = to;
+ textRange.lpstrText = p;
+
+ (void)SendMessage(GetHwnd(), EM_GETTEXTRANGE,
+ 0, (LPARAM)&textRange);
+
+ if ( m_verRichEdit > 1 )
+ {
+ // RichEdit 2.0 uses just CR ('\r') for the
+ // newlines which is neither Unix nor Windows
+ // style - convert it to something reasonable
+ for ( ; *p; p++ )
+ {
+ if ( *p == _T('\r') )
+ *p = _T('\n');
+ }
+ }
+ }
if ( m_verRichEdit == 1 )
{
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
+ {
+ // still send an event for consistency
+ SendUpdateEvent();
}
// we should reset the modified flag even if the value didn't really change
// 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++;
(*pcb) += sizeof(wchar_t);
}
- *(const wchar_t **)dwCookie = wpc;
+ *ppws = wpc;
return 0;
}
-extern long wxEncodingToCodepage(wxFontEncoding encoding); // from utils.cpp
-
-#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;
+
+ wxStreamOutData *data = (wxStreamOutData *)dwCookie;
- // first get the Windows code page for this encoding
- long codepage = wxEncodingToCodepage(encoding);
- if ( codepage == -1 )
+ 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;
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)
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);
}
}
void wxTextCtrl::Clear()
{
- ::SetWindowText(GetHwnd(), wxT(""));
+ ::SetWindowText(GetHwnd(), wxEmptyString);
#if wxUSE_RICHEDIT
if ( !IsRich() )
// 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)
{
- Replace(from, to, _T(""));
+ Replace(from, to, wxEmptyString);
}
bool wxTextCtrl::LoadFile(const wxString& file)
return SendMessage(GetHwnd(), EM_GETMODIFY, 0, 0) != 0;
}
-// Makes 'unmodified'
+void wxTextCtrl::MarkDirty()
+{
+ SendMessage(GetHwnd(), EM_SETMODIFY, TRUE, 0L);
+}
+
void wxTextCtrl::DiscardEdits()
{
SendMessage(GetHwnd(), EM_SETMODIFY, FALSE, 0L);
len += sizeof(WORD);
wxString str;
- wxChar *buf = str.GetWriteBuf(len);
+ {
+ wxStringBufferLength tmp(str, len);
+ wxChar *buf = tmp;
- *(WORD *)buf = (WORD)len;
- len = (size_t)::SendMessage(GetHwnd(), EM_GETLINE, lineNo, (LPARAM)buf);
- buf[len] = 0;
+ *(WORD *)buf = (WORD)len;
+ len = (size_t)::SendMessage(GetHwnd(), EM_GETLINE, lineNo, (LPARAM)buf);
- str.UngetWriteBuf(len);
+#if wxUSE_RICHEDIT
+ if ( IsRich() )
+ {
+ // remove the '\r' returned by the rich edit control, the user code
+ // should never see it
+ if ( buf[len - 2] == _T('\r') && buf[len - 1] == _T('\n') )
+ {
+ buf[len - 2] = _T('\n');
+ len--;
+ }
+ }
+#endif // wxUSE_RICHEDIT
+
+ // remove the '\n' at the end, if any (this is how this function is
+ // supposed to work according to the docs)
+ if ( buf[len - 1] == _T('\n') )
+ {
+ len--;
+ }
+
+ buf[len] = 0;
+ tmp.SetLength(len);
+ }
return str;
}
return ::SendMessage(GetHwnd(), EM_CANUNDO, 0, 0) != 0;
}
+// ----------------------------------------------------------------------------
+// caret handling (Windows only)
+// ----------------------------------------------------------------------------
+
+bool wxTextCtrl::ShowNativeCaret(bool show)
+{
+ if ( show != m_isNativeCaretShown )
+ {
+ if ( !(show ? ::ShowCaret(GetHwnd()) : ::HideCaret(GetHwnd())) )
+ {
+ // not an error, may simply indicate that it's not shown/hidden
+ // yet (i.e. it had been hidden/showh 2 times before)
+ return false;
+ }
+
+ m_isNativeCaretShown = show;
+ }
+
+ return true;
+}
+
// ----------------------------------------------------------------------------
// implemenation details
// ----------------------------------------------------------------------------
#endif // wxUSE_CTL3D
HDC hdc = (HDC)pDC;
- if (GetParent()->GetTransparentBackground())
- SetBkMode(hdc, TRANSPARENT);
- else
- SetBkMode(hdc, OPAQUE);
-
wxColour colBack = GetBackgroundColour();
if (!IsEnabled() && (GetWindowStyle() & wxTE_MULTILINE) == 0)
Redo();
}
-void wxTextCtrl::OnDelete(wxCommandEvent& event)
+void wxTextCtrl::OnDelete(wxCommandEvent& WXUNUSED(event))
{
long from, to;
GetSelection(& from, & to);
Remove(from, to);
}
-void wxTextCtrl::OnSelectAll(wxCommandEvent& event)
+void wxTextCtrl::OnSelectAll(wxCommandEvent& WXUNUSED(event))
{
SetSelection(-1, -1);
}
event.Skip();
}
+void wxTextCtrl::OnSetFocus(wxFocusEvent& WXUNUSED(event))
+{
+ // be sure the caret remains invisible if the user had hidden it
+ if ( !m_isNativeCaretShown )
+ {
+ ::HideCaret(GetHwnd());
+ }
+}
+
// the rest of the file only deals with the rich edit controls
#if wxUSE_RICHEDIT
}
return TRUE;
}
-
+
// not processed, leave it to the base class
return wxTextCtrlBase::MSWOnNotify(idCtrl, lParam, result);
}
if ( ms_hRichEdit[i] )
{
::FreeLibrary(ms_hRichEdit[i]);
+ ms_hRichEdit[i] = NULL;
}
}
}