+#if wxUSE_RICHEDIT
+ if ( IsRich() )
+ {
+ // if from and to are both -1, it means (in wxWidgets) that all text
+ // should be selected, translate this into Windows convention
+ if ( (from == -1) && (to == -1) )
+ {
+ from = 0;
+ }
+
+ CHARRANGE range;
+ range.cpMin = from;
+ range.cpMax = to;
+ ::SendMessage(hWnd, EM_EXSETSEL, 0, (LPARAM)&range);
+ }
+ else
+#endif // wxUSE_RICHEDIT
+ {
+ wxTextEntry::DoSetSelection(from, to, flags);
+ }
+
+ if ( (flags & SetSel_Scroll) && !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
+ long style = 0;
+ if ( GetRichVersion() > 1 )
+ {
+ if ( !HasFlag(wxTE_NOHIDESEL) )
+ {
+ // setting ECO_NOHIDESEL also sets WS_VISIBLE and possibly
+ // others, remember the style so we can reset it later if needed
+ style = ::GetWindowLong(GetHwnd(), GWL_STYLE);
+ ::SendMessage(GetHwnd(), EM_SETOPTIONS,
+ ECOOP_OR, ECO_NOHIDESEL);
+ }
+ //else: everything is already ok
+ }
+#endif // wxUSE_RICHEDIT
+
+ ::SendMessage(hWnd, EM_SCROLLCARET, 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);
+ if ( style != ::GetWindowLong(GetHwnd(), GWL_STYLE) )
+ ::SetWindowLong(GetHwnd(), GWL_STYLE, style);
+ }
+#endif // wxUSE_RICHEDIT
+ }
+}
+
+// ----------------------------------------------------------------------------
+// Working with files
+// ----------------------------------------------------------------------------
+
+bool wxTextCtrl::DoLoadFile(const wxString& file, int fileType)
+{
+ if ( wxTextCtrlBase::DoLoadFile(file, fileType) )
+ {
+ // update the size limit if needed
+ AdjustSpaceLimit();
+
+ return true;
+ }
+
+ return false;
+}
+
+// ----------------------------------------------------------------------------
+// dirty status
+// ----------------------------------------------------------------------------
+
+bool wxTextCtrl::IsModified() const
+{
+ return ::SendMessage(GetHwnd(), EM_GETMODIFY, 0, 0) != 0;
+}
+
+void wxTextCtrl::MarkDirty()
+{
+ ::SendMessage(GetHwnd(), EM_SETMODIFY, TRUE, 0);
+}
+
+void wxTextCtrl::DiscardEdits()
+{
+ ::SendMessage(GetHwnd(), EM_SETMODIFY, FALSE, 0);
+}
+
+// ----------------------------------------------------------------------------
+// Positions <-> coords
+// ----------------------------------------------------------------------------
+
+int wxTextCtrl::GetNumberOfLines() const
+{
+ return (int)::SendMessage(GetHwnd(), EM_GETLINECOUNT, 0, 0);
+}
+
+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, y, 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, pos);
+ }
+ else
+#endif // wxUSE_RICHEDIT
+ {
+ lineNo = ::SendMessage(hWnd, EM_LINEFROMCHAR, 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, lineNo, 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;
+}
+
+wxPoint wxTextCtrl::DoPositionToCoords(long pos) const
+{
+ // FIXME: This code is broken for rich edit version 2.0 as it uses the same
+ // API as plain edit i.e. the coordinates are returned directly instead of
+ // filling the POINT passed as WPARAM with them but we can't distinguish
+ // between 2.0 and 3.0 unfortunately (see also the use of EM_POSFROMCHAR
+ // above).
+#if wxUSE_RICHEDIT
+ if ( IsRich() )
+ {
+ POINT pt;
+ LRESULT rc = ::SendMessage(GetHwnd(), EM_POSFROMCHAR, (WPARAM)&pt, pos);
+ if ( rc != -1 )
+ return wxPoint(pt.x, pt.y);
+ }
+ else
+#endif // wxUSE_RICHEDIT
+ {
+ LRESULT rc = ::SendMessage(GetHwnd(), EM_POSFROMCHAR, pos, 0);
+ if ( rc == -1 )
+ {
+ // Finding coordinates for the last position of the control fails
+ // in plain EDIT control, try to compensate for it by finding it
+ // ourselves from the position of the previous character.
+ if ( pos < GetLastPosition() )
+ {
+ // It's not the expected correctable failure case so just fail.
+ return wxDefaultPosition;
+ }
+
+ if ( pos == 0 )
+ {
+ // We're being asked the coordinates of the first (and last and
+ // only) position in an empty control. There is no way to get
+ // it directly with EM_POSFROMCHAR but EM_GETMARGINS returns
+ // the correct value for at least the horizontal offset.
+ rc = ::SendMessage(GetHwnd(), EM_GETMARGINS, 0, 0);
+
+ // Text control seems to effectively add 1 to margin.
+ return wxPoint(LOWORD(rc) + 1, 1);
+ }
+
+ // We do have a previous character, try to get its coordinates.
+ rc = ::SendMessage(GetHwnd(), EM_POSFROMCHAR, pos - 1, 0);
+ if ( rc == -1 )
+ {
+ // If getting coordinates of the previous character failed as
+ // well, just give up.
+ return wxDefaultPosition;
+ }
+
+ wxString prevChar = GetRange(pos - 1, pos);
+ wxSize prevCharSize = GetTextExtent(prevChar);
+
+ if ( prevChar == wxT("\n" ))
+ {
+ // 'pos' is at the beginning of a new line so its X coordinate
+ // should be the same as X coordinate of the first character of
+ // any other line while its Y coordinate will be approximately
+ // (but we can't compute it exactly...) one character height
+ // more than that of the previous character.
+ LRESULT coords0 = ::SendMessage(GetHwnd(), EM_POSFROMCHAR, 0, 0);
+ if ( coords0 == -1 )
+ return wxDefaultPosition;
+
+ rc = MAKELPARAM(LOWORD(coords0), HIWORD(rc) + prevCharSize.y);
+ }
+ else
+ {
+ // Simple case: previous character is in the same line so this
+ // one is just after it.
+ rc += MAKELPARAM(prevCharSize.x, 0);
+ }
+ }
+
+ // Notice that {LO,HI}WORD macros return WORDs, i.e. unsigned shorts,
+ // while we want to have signed values here (the y coordinate of any
+ // position above the first currently visible line is negative, for
+ // example), hence the need for casts.
+ return wxPoint(static_cast<short>(LOWORD(rc)),
+ static_cast<short>(HIWORD(rc)));
+ }
+
+ return wxDefaultPosition;
+}
+
+
+// ----------------------------------------------------------------------------
+//
+// ----------------------------------------------------------------------------
+
+void wxTextCtrl::ShowPosition(long pos)
+{
+ HWND hWnd = GetHwnd();
+
+ // To scroll to a position, we pass the number of lines and characters
+ // to scroll *by*. This means that we need to:
+ // (1) Find the line position of the current line.
+ // (2) Find the line position of pos.
+ // (3) Scroll by (pos - current).
+ // For now, ignore the horizontal scrolling.
+
+ // Is this where scrolling is relative to - the line containing the caret?
+ // Or is the first visible line??? Try first visible line.
+// int currentLineLineNo1 = (int)::SendMessage(hWnd, EM_LINEFROMCHAR, -1, 0L);
+
+ int currentLineLineNo = (int)::SendMessage(hWnd, EM_GETFIRSTVISIBLELINE, 0, 0);
+
+ int specifiedLineLineNo = (int)::SendMessage(hWnd, EM_LINEFROMCHAR, pos, 0);
+
+ int linesToScroll = specifiedLineLineNo - currentLineLineNo;
+
+ if (linesToScroll != 0)
+ ::SendMessage(hWnd, EM_LINESCROLL, 0, linesToScroll);
+}
+
+long wxTextCtrl::GetLengthOfLineContainingPos(long pos) const
+{
+ return ::SendMessage(GetHwnd(), EM_LINELENGTH, pos, 0);
+}
+
+int wxTextCtrl::GetLineLength(long lineNo) const
+{
+ long pos = XYToPosition(0, lineNo);
+
+ return GetLengthOfLineContainingPos(pos);
+}
+
+wxString wxTextCtrl::GetLineText(long lineNo) const
+{
+ size_t len = (size_t)GetLineLength(lineNo) + 1;
+
+ // there must be at least enough place for the length WORD in the
+ // buffer
+ len += sizeof(WORD);
+
+ wxString str;
+ {
+ wxStringBufferLength tmp(str, len);
+ wxChar *buf = tmp;
+
+ *(WORD *)buf = (WORD)len;
+ len = (size_t)::SendMessage(GetHwnd(), EM_GETLINE, lineNo, (LPARAM)buf);
+
+#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] == wxT('\r') && buf[len - 1] == wxT('\n') )
+ {
+ // richedit 1.0 uses "\r\n" as line terminator, so remove "\r"
+ // here and "\n" below
+ buf[len - 2] = wxT('\n');
+ len--;
+ }
+ else if ( buf[len - 1] == wxT('\r') )
+ {
+ // richedit 2.0+ uses only "\r", replace it with "\n"
+ buf[len - 1] = wxT('\n');
+ }
+ }
+#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] == wxT('\n') )
+ {
+ len--;
+ }
+
+ buf[len] = 0;
+ tmp.SetLength(len);
+ }
+
+ return str;
+}
+
+void wxTextCtrl::SetMaxLength(unsigned long len)
+{
+#if wxUSE_RICHEDIT
+ if ( IsRich() )
+ {
+ ::SendMessage(GetHwnd(), EM_EXLIMITTEXT, 0, len ? len : 0x7fffffff);
+ }
+ else
+#endif // wxUSE_RICHEDIT
+ {
+ wxTextEntry::SetMaxLength(len);
+ }
+}
+
+// ----------------------------------------------------------------------------
+// Undo/redo
+// ----------------------------------------------------------------------------
+
+void wxTextCtrl::Redo()
+{
+#if wxUSE_RICHEDIT
+ if ( GetRichVersion() > 1 )
+ {
+ ::SendMessage(GetHwnd(), EM_REDO, 0, 0);
+ return;
+ }
+#endif // wxUSE_RICHEDIT
+
+ wxTextEntry::Redo();
+}
+
+bool wxTextCtrl::CanRedo() const
+{
+#if wxUSE_RICHEDIT
+ if ( GetRichVersion() > 1 )
+ return ::SendMessage(GetHwnd(), EM_CANREDO, 0, 0) != 0;
+#endif // wxUSE_RICHEDIT
+
+ return wxTextEntry::CanRedo();
+}
+
+// ----------------------------------------------------------------------------
+// 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/shown 2 times before)
+ return false;
+ }
+
+ m_isNativeCaretShown = show;
+ }
+
+ return true;
+}
+
+// ----------------------------------------------------------------------------
+// implementation details
+// ----------------------------------------------------------------------------
+
+void wxTextCtrl::Command(wxCommandEvent & event)
+{
+ SetValue(event.GetString());
+ ProcessCommand (event);
+}
+
+void wxTextCtrl::OnDropFiles(wxDropFilesEvent& event)
+{
+ // By default, load the first file into the text window.
+ if (event.GetNumberOfFiles() > 0)
+ {
+ LoadFile(event.GetFiles()[0]);
+ }
+}
+
+// ----------------------------------------------------------------------------
+// kbd input processing
+// ----------------------------------------------------------------------------
+
+bool wxTextCtrl::MSWShouldPreProcessMessage(WXMSG* msg)
+{
+ // check for our special keys here: if we don't do it and the parent frame
+ // uses them as accelerators, they wouldn't work at all, so we disable
+ // usual preprocessing for them
+ if ( msg->message == WM_KEYDOWN )
+ {
+ const WPARAM vkey = msg->wParam;
+ if ( HIWORD(msg->lParam) & KF_ALTDOWN )
+ {
+ // Alt-Backspace is accelerator for "Undo"
+ if ( vkey == VK_BACK )
+ return false;
+ }
+ else // no Alt
+ {
+ // we want to process some Ctrl-foo and Shift-bar but no key
+ // combinations without either Ctrl or Shift nor with both of them
+ // pressed
+ const int ctrl = wxIsCtrlDown(),
+ shift = wxIsShiftDown();
+ switch ( ctrl + shift )
+ {
+ default:
+ wxFAIL_MSG( wxT("how many modifiers have we got?") );
+ // fall through
+
+ case 0:
+ switch ( vkey )
+ {
+ case VK_RETURN:
+ // This one is only special for multi line controls.
+ if ( !IsMultiLine() )
+ break;
+ // fall through
+
+ case VK_DELETE:
+ case VK_HOME:
+ case VK_END:
+ return false;
+ }
+ // fall through
+ case 2:
+ break;
+
+ case 1:
+ // either Ctrl or Shift pressed
+ if ( ctrl )
+ {
+ switch ( vkey )
+ {
+ case 'C':
+ case 'V':
+ case 'X':
+ case VK_INSERT:
+ case VK_DELETE:
+ case VK_HOME:
+ case VK_END:
+ return false;
+ }
+ }
+ else // Shift is pressed
+ {
+ if ( vkey == VK_INSERT || vkey == VK_DELETE )
+ return false;
+ }
+ }
+ }
+ }
+
+ return wxControl::MSWShouldPreProcessMessage(msg);
+}
+
+void wxTextCtrl::OnChar(wxKeyEvent& event)
+{
+ switch ( event.GetKeyCode() )
+ {
+ case WXK_RETURN:
+ {
+ wxCommandEvent event(wxEVT_COMMAND_TEXT_ENTER, m_windowId);
+ InitCommandEvent(event);
+ event.SetString(GetValue());
+ if ( HandleWindowEvent(event) )
+ if ( !HasFlag(wxTE_MULTILINE) )
+ return;
+ //else: multiline controls need Enter for themselves
+ }
+ break;
+
+ case WXK_TAB:
+ // 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 ( !(m_windowStyle & wxTE_PROCESS_TAB))
+ {
+ if ( FindFocus() == this )
+ {
+ int flags = 0;
+ if (!event.ShiftDown())
+ flags |= wxNavigationKeyEvent::IsForward ;
+ if (event.ControlDown())
+ flags |= wxNavigationKeyEvent::WinChange ;
+ if (Navigate(flags))
+ return;
+ }
+ }
+ else
+ {
+ // Insert tab since calling the default Windows handler
+ // doesn't seem to do it
+ WriteText(wxT("\t"));
+ return;
+ }
+ break;
+ }
+
+ // no, we didn't process it
+ event.Skip();
+}
+
+void wxTextCtrl::OnKeyDown(wxKeyEvent& event)
+{
+ // richedit control doesn't send WM_PASTE, WM_CUT and WM_COPY messages
+ // when Ctrl-V, X or C is pressed and this prevents wxClipboardTextEvent
+ // from working. So we work around it by intercepting these shortcuts
+ // ourselves and emitting clipboard events (which richedit will handle,
+ // so everything works as before, including pasting of rich text):
+ if ( event.GetModifiers() == wxMOD_CONTROL && IsRich() )
+ {
+ switch ( event.GetKeyCode() )
+ {
+ case 'C':
+ Copy();
+ return;
+ case 'X':
+ Cut();
+ return;
+ case 'V':
+ Paste();
+ return;
+ default:
+ break;
+ }
+ }
+
+ // Default window procedure of multiline edit controls posts WM_CLOSE to
+ // the parent window when it gets Escape key press for some reason, prevent
+ // it from doing this as this resulted in dialog boxes being closed on
+ // Escape even when they shouldn't be (we do handle Escape ourselves
+ // correctly in the situations when it should close them).
+ if ( event.GetKeyCode() == WXK_ESCAPE && IsMultiLine() )
+ return;
+
+ // no, we didn't process it
+ event.Skip();
+}
+
+WXLRESULT wxTextCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
+{
+ WXLRESULT lRc = wxTextCtrlBase::MSWWindowProc(nMsg, wParam, lParam);
+
+ switch ( nMsg )
+ {
+ case WM_GETDLGCODE:
+ {
+ // we always want the chars and the arrows: the arrows for
+ // navigation and the chars because we want Ctrl-C to work even
+ // in a read only control
+ long lDlgCode = DLGC_WANTCHARS | DLGC_WANTARROWS;
+
+ if ( IsEditable() )
+ {
+ // we may have several different cases:
+ // 1. normal: both TAB and ENTER are used for navigation
+ // 2. ctrl wants TAB for itself: ENTER is used to pass to
+ // the next control in the dialog
+ // 3. ctrl wants ENTER for itself: TAB is used for dialog
+ // navigation
+ // 4. ctrl wants both TAB and ENTER: Ctrl-ENTER is used to
+ // go to the next control (we need some way to do it)
+
+ // multiline controls should always get ENTER for themselves
+ if ( HasFlag(wxTE_PROCESS_ENTER) || HasFlag(wxTE_MULTILINE) )
+ lDlgCode |= DLGC_WANTMESSAGE;
+
+ if ( HasFlag(wxTE_PROCESS_TAB) )
+ lDlgCode |= DLGC_WANTTAB;
+
+ lRc |= lDlgCode;
+ }
+ else // !editable
+ {
+ // NB: use "=", not "|=" as the base class version returns
+ // the same flags in the disabled state as usual (i.e.
+ // including DLGC_WANTMESSAGE). This is strange (how
+ // does it work in the native Win32 apps?) but for now
+ // live with it.
+ lRc = lDlgCode;
+ }
+ if (IsMultiLine())
+ // Clear the DLGC_HASSETSEL bit from the return value
+ lRc &= ~DLGC_HASSETSEL;
+ }
+ break;
+
+#if wxUSE_MENUS
+ case WM_SETCURSOR:
+ // rich text controls seem to have a bug and don't change the
+ // cursor to the standard arrow one from the I-beam cursor usually
+ // used by them even when a popup menu is shown (this works fine
+ // for plain EDIT controls though), so explicitly work around this
+ if ( IsRich() )
+ {
+ extern wxMenu *wxCurrentPopupMenu;
+ if ( wxCurrentPopupMenu &&
+ wxCurrentPopupMenu->GetInvokingWindow() == this )
+ ::SetCursor(GetHcursorOf(*wxSTANDARD_CURSOR));
+ }
+#endif // wxUSE_MENUS
+ }
+
+ return lRc;
+}
+
+// ----------------------------------------------------------------------------
+// text control event processing
+// ----------------------------------------------------------------------------
+
+bool wxTextCtrl::SendUpdateEvent()
+{
+ switch ( m_updatesCount )
+ {
+ case 0:
+ // remember that we've got an update
+ m_updatesCount++;
+ break;
+
+ case 1:
+ // we had already sent one event since the last control modification
+ return false;
+
+ default:
+ wxFAIL_MSG( wxT("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;
+
+ case -2:
+ // the control was updated programmatically and we do NOT want to
+ // send events
+ return false;
+ }
+
+ return SendTextUpdatedEvent();
+}
+
+bool wxTextCtrl::MSWCommand(WXUINT param, WXWORD WXUNUSED(id))
+{
+ switch ( param )
+ {
+ case EN_CHANGE:
+ SendUpdateEvent();
+ break;
+
+ case EN_MAXTEXT:
+ // the text size limit has been hit -- try to increase it
+ if ( !AdjustSpaceLimit() )
+ {
+ wxCommandEvent event(wxEVT_COMMAND_TEXT_MAXLEN, m_windowId);
+ InitCommandEvent(event);
+ event.SetString(GetValue());
+ ProcessCommand(event);
+ }
+ break;
+
+ // the other edit notification messages are not processed (or, in
+ // the case of EN_{SET/KILL}FOCUS were already handled at WM_SET/
+ // KILLFOCUS level)
+ default:
+ return false;
+ }
+
+ // processed
+ return true;
+}
+
+WXHBRUSH wxTextCtrl::MSWControlColor(WXHDC hDC, WXHWND hWnd)
+{
+ if ( !IsThisEnabled() && !HasFlag(wxTE_MULTILINE) )
+ return MSWControlColorDisabled(hDC);
+
+ return wxTextCtrlBase::MSWControlColor(hDC, hWnd);
+}
+
+bool wxTextCtrl::HasSpaceLimit(unsigned int *len) const
+{
+ // HACK: we try to automatically extend the limit for the amount of text
+ // to allow (interactively) entering more than 64Kb of text under
+ // Win9x but we shouldn't reset the text limit which was previously
+ // set explicitly with SetMaxLength()
+ //
+ // Unfortunately there is no EM_GETLIMITTEXTSETBYUSER and so we don't
+ // know the limit we set (if any). We could solve this by storing the
+ // limit we set in wxTextCtrl but to save space we prefer to simply
+ // test here the actual limit value: we consider that SetMaxLength()
+ // can only be called for small values while EN_MAXTEXT is only sent
+ // for large values (in practice the default limit seems to be 30000
+ // but make it smaller just to be on the safe side)
+ *len = ::SendMessage(GetHwnd(), EM_GETLIMITTEXT, 0, 0);
+ return *len < 10001;
+
+}
+
+bool wxTextCtrl::AdjustSpaceLimit()
+{
+ unsigned int limit;
+ if ( HasSpaceLimit(&limit) )
+ return false;
+
+ unsigned int len = ::GetWindowTextLength(GetHwnd());
+ if ( len >= limit )
+ {
+ // increment in 32Kb chunks
+ SetMaxLength(len + 0x8000);
+ }
+
+ // we changed the limit
+ return true;
+}
+
+bool wxTextCtrl::AcceptsFocusFromKeyboard() const
+{
+ // we don't want focus if we can't be edited unless we're a multiline
+ // control because then it might be still nice to get focus from keyboard
+ // to be able to scroll it without mouse
+ return (IsEditable() || IsMultiLine()) && wxControl::AcceptsFocus();
+}
+
+wxSize wxTextCtrl::DoGetBestSize() const
+{
+ return DoGetSizeFromTextSize( DEFAULT_ITEM_WIDTH );
+}
+
+wxSize wxTextCtrl::DoGetSizeFromTextSize(int xlen, int ylen) const
+{
+ int cx, cy;
+ wxGetCharSize(GetHWND(), &cx, &cy, GetFont());
+
+ DWORD wText = 1;
+ ::SystemParametersInfo(SPI_GETCARETWIDTH, 0, &wText, 0);
+ wText += xlen;
+
+ int hText = cy;
+ if ( m_windowStyle & wxTE_MULTILINE )
+ {
+ // add space for vertical scrollbar
+ if ( !(m_windowStyle & wxTE_NO_VSCROLL) )
+ wText += ::GetSystemMetrics(SM_CXVSCROLL);
+
+ if ( ylen <= 0 )
+ {
+ hText *= wxMax(wxMin(GetNumberOfLines(), 10), 2);
+ // add space for horizontal scrollbar
+ if ( m_windowStyle & wxHSCROLL )
+ hText += ::GetSystemMetrics(SM_CYHSCROLL);
+ }
+ }
+ // for single line control cy (height + external leading) is ok
+ else
+ {
+ // Add the margins we have previously set
+ wxPoint marg( GetMargins() );
+ wText += wxMax(0, marg.x);
+ hText += wxMax(0, marg.y);
+ }
+
+ // Text controls without border are special and have the same height as
+ // static labels (they also have the same appearance when they're disable
+ // and are often used as a sort of copyable to the clipboard label so it's
+ // important that they have the same height as the normal labels to not
+ // stand out).
+ if ( !HasFlag(wxBORDER_NONE) )
+ {
+ wText += 9; // borders and inner margins
+
+ // we have to add the adjustments for the control height only once, not
+ // once per line, so do it after multiplication above
+ hText += EDIT_HEIGHT_FROM_CHAR_HEIGHT(cy) - cy;
+ }
+
+ // Perhaps the user wants something different from CharHeight, or ylen
+ // is used as the height of a multiline text.
+ if ( ylen > 0 )
+ hText += ylen - GetCharHeight();
+
+ return wxSize(wText, hText);
+}
+
+// ----------------------------------------------------------------------------
+// standard handlers for standard edit menu events
+// ----------------------------------------------------------------------------
+
+void wxTextCtrl::OnCut(wxCommandEvent& WXUNUSED(event))
+{
+ Cut();
+}
+
+void wxTextCtrl::OnCopy(wxCommandEvent& WXUNUSED(event))
+{
+ Copy();
+}
+
+void wxTextCtrl::OnPaste(wxCommandEvent& WXUNUSED(event))
+{
+ Paste();
+}
+
+void wxTextCtrl::OnUndo(wxCommandEvent& WXUNUSED(event))
+{
+ Undo();
+}
+
+void wxTextCtrl::OnRedo(wxCommandEvent& WXUNUSED(event))
+{
+ Redo();
+}
+
+void wxTextCtrl::OnDelete(wxCommandEvent& WXUNUSED(event))
+{
+ RemoveSelection();
+}
+
+void wxTextCtrl::OnSelectAll(wxCommandEvent& WXUNUSED(event))
+{
+ SelectAll();
+}
+
+void wxTextCtrl::OnUpdateCut(wxUpdateUIEvent& event)
+{
+ event.Enable( CanCut() );
+}
+
+void wxTextCtrl::OnUpdateCopy(wxUpdateUIEvent& event)
+{
+ event.Enable( CanCopy() );
+}
+
+void wxTextCtrl::OnUpdatePaste(wxUpdateUIEvent& event)
+{
+ event.Enable( CanPaste() );
+}
+
+void wxTextCtrl::OnUpdateUndo(wxUpdateUIEvent& event)
+{
+ event.Enable( CanUndo() );
+}
+
+void wxTextCtrl::OnUpdateRedo(wxUpdateUIEvent& event)
+{
+ event.Enable( CanRedo() );
+}
+
+void wxTextCtrl::OnUpdateDelete(wxUpdateUIEvent& event)
+{
+ event.Enable( HasSelection() && IsEditable() );
+}
+
+void wxTextCtrl::OnUpdateSelectAll(wxUpdateUIEvent& event)
+{
+ event.Enable( !IsEmpty() );
+}
+
+void wxTextCtrl::OnContextMenu(wxContextMenuEvent& event)
+{
+#if wxUSE_RICHEDIT
+ if (IsRich())
+ {
+ if (!m_privateContextMenu)
+ {
+ m_privateContextMenu = new wxMenu;
+ m_privateContextMenu->Append(wxID_UNDO, _("&Undo"));
+ m_privateContextMenu->Append(wxID_REDO, _("&Redo"));
+ m_privateContextMenu->AppendSeparator();
+ m_privateContextMenu->Append(wxID_CUT, _("Cu&t"));
+ m_privateContextMenu->Append(wxID_COPY, _("&Copy"));
+ m_privateContextMenu->Append(wxID_PASTE, _("&Paste"));
+ m_privateContextMenu->Append(wxID_CLEAR, _("&Delete"));
+ m_privateContextMenu->AppendSeparator();
+ m_privateContextMenu->Append(wxID_SELECTALL, _("Select &All"));
+ }
+ PopupMenu(m_privateContextMenu);
+ return;
+ }
+ else
+#endif
+ event.Skip();
+}
+
+void wxTextCtrl::OnSetFocus(wxFocusEvent& event)
+{
+ // be sure the caret remains invisible if the user had hidden it
+ if ( !m_isNativeCaretShown )
+ {
+ ::HideCaret(GetHwnd());
+ }
+
+ event.Skip();
+}
+
+// the rest of the file only deals with the rich edit controls
+#if wxUSE_RICHEDIT
+
+// ----------------------------------------------------------------------------
+// EN_LINK processing
+// ----------------------------------------------------------------------------
+
+bool wxTextCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
+{
+ NMHDR *hdr = (NMHDR* )lParam;
+ switch ( hdr->code )
+ {
+ 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 )
+ {
+ WXUINT flags = msgf->wParam;
+ int x = GET_X_LPARAM(msgf->lParam),
+ y = GET_Y_LPARAM(msgf->lParam);
+
+ HandleMouseEvent(msg, x, y, flags);
+ }
+ }
+
+ // 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?
+ {
+ wxCursor cur(wxCURSOR_HAND);
+ ::SetCursor(GetHcursorOf(cur));
+ *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, leave it to the base class
+ return wxTextCtrlBase::MSWOnNotify(idCtrl, lParam, result);
+}
+
+#if wxUSE_DRAG_AND_DROP
+
+void wxTextCtrl::SetDropTarget(wxDropTarget *dropTarget)
+{
+ if ( m_dropTarget == wxRICHTEXT_DEFAULT_DROPTARGET )