+// ----------------------------------------------------------------------------
+// Clipboard operations
+// ----------------------------------------------------------------------------
+
+void wxTextCtrl::Copy()
+{
+ if (CanCopy())
+ {
+ ::SendMessage(GetHwnd(), WM_COPY, 0, 0L);
+ }
+}
+
+void wxTextCtrl::Cut()
+{
+ if (CanCut())
+ {
+ ::SendMessage(GetHwnd(), WM_CUT, 0, 0L);
+ }
+}
+
+void wxTextCtrl::Paste()
+{
+ if (CanPaste())
+ {
+ ::SendMessage(GetHwnd(), WM_PASTE, 0, 0L);
+ }
+}
+
+bool wxTextCtrl::HasSelection() const
+{
+ long from, to;
+ GetSelection(&from, &to);
+ return from != to;
+}
+
+bool wxTextCtrl::CanCopy() const
+{
+ // Can copy if there's a selection
+ return HasSelection();
+}
+
+bool wxTextCtrl::CanCut() const
+{
+ return CanCopy() && IsEditable();
+}
+
+bool wxTextCtrl::CanPaste() const
+{
+ if ( !IsEditable() )
+ return false;
+
+#if wxUSE_RICHEDIT
+ if ( IsRich() )
+ {
+ UINT cf = 0; // 0 == any format
+
+ return ::SendMessage(GetHwnd(), EM_CANPASTE, cf, 0) != 0;
+ }
+#endif // wxUSE_RICHEDIT
+
+ // Standard edit control: check for straight text on clipboard
+ if ( !::OpenClipboard(GetHwndOf(wxTheApp->GetTopWindow())) )
+ return false;
+
+ bool isTextAvailable = ::IsClipboardFormatAvailable(CF_TEXT) != 0;
+ ::CloseClipboard();
+
+ return isTextAvailable;
+}
+
+// ----------------------------------------------------------------------------
+// Accessors
+// ----------------------------------------------------------------------------
+
+void wxTextCtrl::SetEditable(bool editable)
+{
+ HWND hWnd = GetHwnd();
+ ::SendMessage(hWnd, EM_SETREADONLY, (WPARAM)!editable, (LPARAM)0L);
+}
+
+void wxTextCtrl::SetInsertionPoint(long pos)
+{
+ DoSetSelection(pos, pos);
+}
+
+void wxTextCtrl::SetInsertionPointEnd()
+{
+ // we must not do anything if the caret is already there because calling
+ // SetInsertionPoint() thaws the controls if Freeze() had been called even
+ // 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() )
+ {
+ return;
+ }
+
+ long pos;
+
+#if wxUSE_RICHEDIT
+ if ( m_verRichEdit == 1 )
+ {
+ // we don't have to waste time calling GetLastPosition() in this case
+ pos = -1;
+ }
+ else // !RichEdit 1.0
+#endif // wxUSE_RICHEDIT
+ {
+ pos = GetLastPosition();
+ }
+
+ SetInsertionPoint(pos);
+}
+
+long wxTextCtrl::GetInsertionPoint() const
+{
+#if wxUSE_RICHEDIT
+ if ( IsRich() )
+ {
+ CHARRANGE range;
+ range.cpMin = 0;
+ range.cpMax = 0;
+ ::SendMessage(GetHwnd(), EM_EXGETSEL, 0, (LPARAM) &range);
+ return range.cpMin;
+ }
+#endif // wxUSE_RICHEDIT
+
+ DWORD Pos = (DWORD)::SendMessage(GetHwnd(), EM_GETSEL, 0, 0L);
+ return Pos & 0xFFFF;
+}
+
+wxTextPos wxTextCtrl::GetLastPosition() const
+{
+ int numLines = GetNumberOfLines();
+ long posStartLastLine = XYToPosition(0, numLines - 1);
+
+ long lenLastLine = GetLengthOfLineContainingPos(posStartLastLine);
+
+ return posStartLastLine + lenLastLine;
+}
+
+// If the return values from and to are the same, there is no
+// selection.
+void wxTextCtrl::GetSelection(long* from, long* to) const
+{
+#if wxUSE_RICHEDIT
+ if ( IsRich() )
+ {
+ CHARRANGE charRange;
+ ::SendMessage(GetHwnd(), EM_EXGETSEL, 0, (LPARAM) &charRange);
+
+ *from = charRange.cpMin;
+ *to = charRange.cpMax;
+ }
+ else
+#endif // !wxUSE_RICHEDIT
+ {
+ DWORD dwStart, dwEnd;
+ ::SendMessage(GetHwnd(), EM_GETSEL, (WPARAM)&dwStart, (LPARAM)&dwEnd);
+
+ *from = dwStart;
+ *to = dwEnd;
+ }
+}
+
+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;
+}
+
+// ----------------------------------------------------------------------------
+// selection
+// ----------------------------------------------------------------------------
+
+void wxTextCtrl::SetSelection(long from, long to)
+{
+ // if from and to are both -1, it means (in wxWidgets) that all text should
+ // be selected - translate into Windows convention
+ if ( (from == -1) && (to == -1) )
+ {
+ from = 0;
+ to = -1;
+ }
+
+ DoSetSelection(from, to);
+}
+
+void wxTextCtrl::DoSetSelection(long from, long to, bool scrollCaret)
+{
+ HWND hWnd = GetHwnd();
+
+#if wxUSE_RICHEDIT
+ if ( IsRich() )
+ {
+ CHARRANGE range;
+ range.cpMin = from;
+ range.cpMax = to;
+ ::SendMessage(hWnd, EM_EXSETSEL, 0, (LPARAM) &range);
+ }
+ else
+#endif // wxUSE_RICHEDIT
+ {
+ ::SendMessage(hWnd, EM_SETSEL, (WPARAM)from, (LPARAM)to);
+ }
+
+ if ( scrollCaret && !IsFrozen() )
+ {
+#if wxUSE_RICHEDIT
+ // richedit 3.0 (i.e. the version living in riched20.dll distributed
+ // with Windows 2000 and beyond) doesn't honour EM_SCROLLCARET when
+ // emulating richedit 2.0 unless the control has focus or ECO_NOHIDESEL
+ // option is set (but it does work ok in richedit 1.0 mode...)
+ //
+ // so to make it work we either need to give focus to it here which
+ // will probably create many problems (dummy focus events; window
+ // containing the text control being brought to foreground
+ // unexpectedly; ...) or to temporarily set ECO_NOHIDESEL which may
+ // create other problems too -- and in fact it does because if we turn
+ // on/off this style while appending the text to the control, the
+ // vertical scrollbar never appears in it even if we append tons of
+ // text and to work around this the only solution I found was to use
+ // ES_DISABLENOSCROLL
+ //
+ // this is very ugly but I don't see any other way to make this work
+ if ( GetRichVersion() > 1 )
+ {
+ if ( !HasFlag(wxTE_NOHIDESEL) )
+ {
+ ::SendMessage(GetHwnd(), EM_SETOPTIONS,
+ ECOOP_OR, ECO_NOHIDESEL);
+ }
+ //else: everything is already ok
+ }
+#endif // wxUSE_RICHEDIT
+
+ ::SendMessage(hWnd, EM_SCROLLCARET, (WPARAM)0, (LPARAM)0);
+
+#if wxUSE_RICHEDIT
+ // restore ECO_NOHIDESEL if we changed it
+ if ( GetRichVersion() > 1 && !HasFlag(wxTE_NOHIDESEL) )
+ {
+ ::SendMessage(GetHwnd(), EM_SETOPTIONS,
+ ECOOP_AND, ~ECO_NOHIDESEL);
+ }
+#endif // wxUSE_RICHEDIT
+ }
+}
+
+// ----------------------------------------------------------------------------
+// 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
+// ----------------------------------------------------------------------------
+
+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 */);
+
+ DoWriteText(value, true /* selection only */);
+}
+
+void wxTextCtrl::Remove(long from, long to)
+{
+ Replace(from, to, wxEmptyString);
+}
+
+bool wxTextCtrl::IsModified() const
+{
+ return ::SendMessage(GetHwnd(), EM_GETMODIFY, 0, 0) != 0;
+}
+
+void wxTextCtrl::MarkDirty()
+{
+ ::SendMessage(GetHwnd(), EM_SETMODIFY, TRUE, 0L);
+}
+
+void wxTextCtrl::DiscardEdits()
+{
+ ::SendMessage(GetHwnd(), EM_SETMODIFY, FALSE, 0L);
+}
+
+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
+ long charIndex = ::SendMessage(GetHwnd(), EM_LINEINDEX, (WPARAM)y, (LPARAM)0);
+
+ return charIndex + x;
+}
+
+bool wxTextCtrl::PositionToXY(long pos, long *x, long *y) const
+{
+ HWND hWnd = GetHwnd();
+
+ // This gets the line number containing the character
+ long lineNo;
+#if wxUSE_RICHEDIT
+ if ( IsRich() )
+ {
+ lineNo = ::SendMessage(hWnd, EM_EXLINEFROMCHAR, 0, (LPARAM)pos);
+ }
+ else
+#endif // wxUSE_RICHEDIT
+ {
+ lineNo = ::SendMessage(hWnd, EM_LINEFROMCHAR, (WPARAM)pos, 0);
+ }
+
+ if ( lineNo == -1 )
+ {
+ // no such line
+ return false;
+ }
+
+ // This gets the char index for the _beginning_ of this line
+ long charIndex = ::SendMessage(hWnd, EM_LINEINDEX, (WPARAM)lineNo, (LPARAM)0);
+ if ( charIndex == -1 )
+ {
+ return false;
+ }
+
+ // The X position must therefore be the different between pos and charIndex
+ if ( x )
+ *x = pos - charIndex;
+ if ( y )
+ *y = lineNo;
+
+ return true;
+}
+
+wxTextCtrlHitTestResult
+wxTextCtrl::HitTest(const wxPoint& pt, long *posOut) 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;
+
+ if ( posOut )
+ *posOut = pos;
+
+ return rc;
+}
+
+// ----------------------------------------------------------------------------
+//
+// ----------------------------------------------------------------------------