1 ///////////////////////////////////////////////////////////////////////////// 
   2 // Name:        msw/textctrl.cpp 
   4 // Author:      Julian Smart 
   8 // Copyright:   (c) Julian Smart and Markus Holzem 
   9 // Licence:     wxWindows license 
  10 ///////////////////////////////////////////////////////////////////////////// 
  12 // ============================================================================ 
  14 // ============================================================================ 
  17     #pragma implementation "textctrl.h" 
  20 // ---------------------------------------------------------------------------- 
  22 // ---------------------------------------------------------------------------- 
  24 // For compilers that support precompilation, includes "wx.h". 
  25 #include "wx/wxprec.h" 
  34     #include "wx/textctrl.h" 
  35     #include "wx/settings.h" 
  43 #include "wx/module.h" 
  46     #include "wx/clipbrd.h" 
  49 #include "wx/textfile.h" 
  53 #include "wx/msw/private.h" 
  57 #include <sys/types.h> 
  59 #if wxUSE_RICHEDIT && (!defined(__GNUWIN32_OLD__) || defined(__CYGWIN10__)) 
  63 // old mingw32 doesn't define this 
  65     #define CFM_CHARSET 0x08000000 
  69     #define CFM_BACKCOLOR 0x04000000 
  72 // cygwin does not have these defined for richedit 
  74     #define ENM_LINK 0x04000000 
  77 #ifndef EM_AUTOURLDETECT 
  78     #define EM_AUTOURLDETECT (WM_USER + 91) 
  82     #define EN_LINK 0x070b 
  84     typedef struct _enlink
 
  94 // ---------------------------------------------------------------------------- 
  96 // ---------------------------------------------------------------------------- 
 100 DWORD CALLBACK 
wxRichEditStreamIn(DWORD dwCookie
, BYTE 
*buf
, LONG cb
, LONG 
*pcb
); 
 102 #endif // wxUSE_RICHEDIT 
 104 // ---------------------------------------------------------------------------- 
 106 // ---------------------------------------------------------------------------- 
 110 // this module initializes RichEdit DLL if needed 
 111 class wxRichEditModule 
: public wxModule
 
 114     virtual bool OnInit(); 
 115     virtual void OnExit(); 
 117     // get the version currently loaded, -1 if none 
 118     static int GetLoadedVersion() { return ms_verRichEdit
; } 
 120     // load the richedit DLL of at least of required version 
 121     static bool Load(int version 
= 1); 
 124     // the handle to richedit DLL and the version of the DLL loaded 
 125     static HINSTANCE ms_hRichEdit
; 
 127     // the DLL version loaded or -1 if none 
 128     static int ms_verRichEdit
; 
 130     DECLARE_DYNAMIC_CLASS(wxRichEditModule
) 
 133 HINSTANCE 
wxRichEditModule::ms_hRichEdit 
= (HINSTANCE
)NULL
; 
 134 int       wxRichEditModule::ms_verRichEdit 
= -1; 
 136 IMPLEMENT_DYNAMIC_CLASS(wxRichEditModule
, wxModule
) 
 138 #endif // wxUSE_RICHEDIT 
 140 // ---------------------------------------------------------------------------- 
 141 // event tables and other macros 
 142 // ---------------------------------------------------------------------------- 
 144 IMPLEMENT_DYNAMIC_CLASS(wxTextCtrl
, wxControl
) 
 146 BEGIN_EVENT_TABLE(wxTextCtrl
, wxControl
) 
 147     EVT_CHAR(wxTextCtrl::OnChar
) 
 148     EVT_DROP_FILES(wxTextCtrl::OnDropFiles
) 
 150     EVT_MENU(wxID_CUT
, wxTextCtrl::OnCut
) 
 151     EVT_MENU(wxID_COPY
, wxTextCtrl::OnCopy
) 
 152     EVT_MENU(wxID_PASTE
, wxTextCtrl::OnPaste
) 
 153     EVT_MENU(wxID_UNDO
, wxTextCtrl::OnUndo
) 
 154     EVT_MENU(wxID_REDO
, wxTextCtrl::OnRedo
) 
 156     EVT_UPDATE_UI(wxID_CUT
, wxTextCtrl::OnUpdateCut
) 
 157     EVT_UPDATE_UI(wxID_COPY
, wxTextCtrl::OnUpdateCopy
) 
 158     EVT_UPDATE_UI(wxID_PASTE
, wxTextCtrl::OnUpdatePaste
) 
 159     EVT_UPDATE_UI(wxID_UNDO
, wxTextCtrl::OnUpdateUndo
) 
 160     EVT_UPDATE_UI(wxID_REDO
, wxTextCtrl::OnUpdateRedo
) 
 162     EVT_ERASE_BACKGROUND(wxTextCtrl::OnEraseBackground
) 
 166 // ============================================================================ 
 168 // ============================================================================ 
 170 // ---------------------------------------------------------------------------- 
 172 // ---------------------------------------------------------------------------- 
 174 void wxTextCtrl::Init() 
 181 bool wxTextCtrl::Create(wxWindow 
*parent
, wxWindowID id
, 
 182                         const wxString
& value
, 
 186                         const wxValidator
& validator
, 
 187                         const wxString
& name
) 
 189     // base initialization 
 190     if ( !CreateBase(parent
, id
, pos
, size
, style
, validator
, name
) ) 
 194         parent
->AddChild(this); 
 196     // translate wxWin style flags to MSW ones, checking for consistency while 
 198     long msStyle 
= ES_LEFT 
| WS_TABSTOP
; 
 200     if ( m_windowStyle 
& wxCLIP_SIBLINGS 
) 
 201         msStyle 
|= WS_CLIPSIBLINGS
; 
 203     if ( m_windowStyle 
& wxTE_MULTILINE 
) 
 205         wxASSERT_MSG( !(m_windowStyle 
& wxTE_PROCESS_ENTER
), 
 206                       wxT("wxTE_PROCESS_ENTER style is ignored for multiline text controls (they always process it)") ); 
 208         msStyle 
|= ES_MULTILINE 
| ES_WANTRETURN
; 
 209         if ((m_windowStyle 
& wxTE_NO_VSCROLL
) == 0) 
 210             msStyle 
|= WS_VSCROLL
; 
 211         m_windowStyle 
|= wxTE_PROCESS_ENTER
; 
 215         // there is really no reason to not have this style for single line 
 217         msStyle 
|= ES_AUTOHSCROLL
; 
 220     if ( m_windowStyle 
& wxHSCROLL 
) 
 221         msStyle 
|= WS_HSCROLL 
| ES_AUTOHSCROLL
; 
 223     if ( m_windowStyle 
& wxTE_READONLY 
) 
 224         msStyle 
|= ES_READONLY
; 
 226     if ( m_windowStyle 
& wxTE_PASSWORD 
) 
 227         msStyle 
|= ES_PASSWORD
; 
 229     if ( m_windowStyle 
& wxTE_AUTO_SCROLL 
) 
 230         msStyle 
|= ES_AUTOHSCROLL
; 
 232     if ( m_windowStyle 
& wxTE_NOHIDESEL 
) 
 233         msStyle 
|= ES_NOHIDESEL
; 
 235     // we always want the characters and the arrows 
 236     m_lDlgCode 
= DLGC_WANTCHARS 
| DLGC_WANTARROWS
; 
 238     // we may have several different cases: 
 239     // 1. normal case: both TAB and ENTER are used for dialog navigation 
 240     // 2. ctrl which wants TAB for itself: ENTER is used to pass to the next 
 241     //    control in the dialog 
 242     // 3. ctrl which wants ENTER for itself: TAB is used for dialog navigation 
 243     // 4. ctrl which wants both TAB and ENTER: Ctrl-ENTER is used to pass to 
 245     if ( m_windowStyle 
& wxTE_PROCESS_ENTER 
) 
 246         m_lDlgCode 
|= DLGC_WANTMESSAGE
; 
 247     if ( m_windowStyle 
& wxTE_PROCESS_TAB 
) 
 248         m_lDlgCode 
|= DLGC_WANTTAB
; 
 250     // do create the control - either an EDIT or RICHEDIT 
 251     wxString windowClass 
= wxT("EDIT"); 
 254     if ( m_windowStyle 
& wxTE_RICH 
) 
 256         static bool s_errorGiven 
= FALSE
;   // MT-FIXME 
 258         // only give the error msg once if the DLL can't be loaded 
 261             // first try to load the RichEdit DLL (will do nothing if already 
 263             if ( !wxRichEditModule::Load() ) 
 265                 wxLogError(_("Impossible to create a rich edit control, using simple text control instead. Please reinstall riched32.dll")); 
 273             msStyle 
|= ES_AUTOVSCROLL
; 
 275             m_verRichEdit 
= wxRichEditModule::GetLoadedVersion(); 
 276             if ( m_verRichEdit 
== 1 ) 
 278                 windowClass 
= wxT("RICHEDIT"); 
 282 #ifndef RICHEDIT_CLASS 
 283                 wxString RICHEDIT_CLASS
; 
 284                 RICHEDIT_CLASS
.Printf(_T("RichEdit%d0"), m_verRichEdit
); 
 286                 RICHEDIT_CLASS 
+= _T('W'); 
 288                 RICHEDIT_CLASS 
+= _T('A'); 
 289 #endif // Unicode/ANSI 
 290 #endif // !RICHEDIT_CLASS 
 292                 windowClass 
= RICHEDIT_CLASS
; 
 296 #endif // wxUSE_RICHEDIT 
 298     // we need to turn '\n's into "\r\n"s for the multiline controls 
 300     if ( m_windowStyle 
& wxTE_MULTILINE 
) 
 302         valueWin 
= wxTextFile::Translate(value
, wxTextFileType_Dos
); 
 309     if ( !MSWCreateControl(windowClass
, msStyle
, pos
, size
, valueWin
) ) 
 312     SetBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_WINDOW
)); 
 317         // have to enable events manually 
 318         LPARAM mask 
= ENM_CHANGE 
| ENM_DROPFILES 
| ENM_SELCHANGE 
| ENM_UPDATE
; 
 320         if ( m_windowStyle 
& wxTE_AUTO_URL 
) 
 324             ::SendMessage(GetHwnd(), EM_AUTOURLDETECT
, TRUE
, 0); 
 327         ::SendMessage(GetHwnd(), EM_SETEVENTMASK
, 0, mask
); 
 329 #endif // wxUSE_RICHEDIT 
 334 // Make sure the window style (etc.) reflects the HWND style (roughly) 
 335 void wxTextCtrl::AdoptAttributesFromHWND() 
 337     wxWindow::AdoptAttributesFromHWND(); 
 339     HWND hWnd 
= GetHwnd(); 
 340     long style 
= ::GetWindowLong(hWnd
, GWL_STYLE
); 
 342     // retrieve the style to see whether this is an edit or richedit ctrl 
 344     wxString classname 
= wxGetWindowClass(GetHWND()); 
 346     if ( classname
.IsSameAs(_T("EDIT"), FALSE 
/* no case */) ) 
 353         if ( wxSscanf(classname
, _T("RichEdit%d0%c"), &m_verRichEdit
, &c
) != 2 ) 
 355             wxLogDebug(_T("Unknown edit control '%s'."), classname
.c_str()); 
 360 #endif // wxUSE_RICHEDIT 
 362     if (style 
& ES_MULTILINE
) 
 363         m_windowStyle 
|= wxTE_MULTILINE
; 
 364     if (style 
& ES_PASSWORD
) 
 365         m_windowStyle 
|= wxTE_PASSWORD
; 
 366     if (style 
& ES_READONLY
) 
 367         m_windowStyle 
|= wxTE_READONLY
; 
 368     if (style 
& ES_WANTRETURN
) 
 369         m_windowStyle 
|= wxTE_PROCESS_ENTER
; 
 372 // ---------------------------------------------------------------------------- 
 373 // set/get the controls text 
 374 // ---------------------------------------------------------------------------- 
 376 wxString 
wxTextCtrl::GetValue() const 
 378     // we can't use wxGetWindowText() (i.e. WM_GETTEXT internally) for 
 379     // retrieving more than 64Kb under Win9x 
 385         int len 
= GetWindowTextLength(GetHwnd()); 
 388             // alloc one extra WORD as needed by the control 
 389             wxChar 
*p 
= str
.GetWriteBuf(++len
); 
 392             textRange
.chrg
.cpMin 
= 0; 
 393             textRange
.chrg
.cpMax 
= -1; 
 394             textRange
.lpstrText 
= p
; 
 396             (void)SendMessage(GetHwnd(), EM_GETTEXTRANGE
, 0, (LPARAM
)&textRange
); 
 398             // believe it or not, but EM_GETTEXTRANGE uses just CR ('\r') for 
 399             // the newlines which is neither Unix nor Windows style (Win95 with 
 400             // riched20.dll shows this behaviour) - convert it to something 
 404                 if ( *p 
== _T('\r') ) 
 410         //else: no text at all, leave the string empty 
 414 #endif // wxUSE_RICHEDIT 
 416     // WM_GETTEXT uses standard DOS CR+LF (\r\n) convention - convert to the 
 417     // same one as above for consitency 
 418     wxString str 
= wxGetWindowText(GetHWND()); 
 420     return wxTextFile::Translate(str
, wxTextFileType_Unix
); 
 423 void wxTextCtrl::SetValue(const wxString
& value
) 
 425     // if the text is long enough, it's faster to just set it instead of first 
 426     // comparing it with the old one (chances are that it will be different 
 427     // anyhow, this comparison is there to avoid flicker for small single-line 
 428     // edit controls mostly) 
 429     if ( (value
.length() > 0x400) || (value 
!= GetValue()) ) 
 431         // it is simpler to do this but it could be more efficient to reproduce 
 432         // WriteText() logic here 
 437         // for compatibility, don't move the cursor when doing SetValue() 
 438         SetInsertionPoint(0); 
 444 DWORD CALLBACK 
wxRichEditStreamIn(DWORD dwCookie
, BYTE 
*buf
, LONG cb
, LONG 
*pcb
) 
 448     wchar_t *wbuf 
= (wchar_t *)buf
; 
 449     const wchar_t *wpc 
= *(const wchar_t **)dwCookie
; 
 454         cb 
-= sizeof(wchar_t); 
 455         (*pcb
) += sizeof(wchar_t); 
 458     *(const wchar_t **)dwCookie 
= wpc
; 
 463 extern long wxEncodingToCodepage(wxFontEncoding encoding
); // from strconv.cpp 
 465 bool wxTextCtrl::StreamIn(const wxString
& value
, wxFontEncoding encoding
) 
 467     // we have to use EM_STREAMIN to force richedit control 2.0+ to show any 
 468     // text in the non default charset - otherwise it thinks it knows better 
 469     // than we do and always shows it in the default one 
 471     // first get the Windows code page for this encoding 
 472     long codepage 
= wxEncodingToCodepage(encoding
); 
 473     if ( codepage 
== -1 ) 
 479     // next translate to Unicode using this code page 
 480     int len 
= ::MultiByteToWideChar(codepage
, 0, value
, -1, NULL
, 0); 
 481     wxWCharBuffer 
wchBuf(len
); 
 482     if ( !::MultiByteToWideChar(codepage
, 0, value
, -1, 
 483                                 (wchar_t *)wchBuf
.data(), len
) ) 
 485         wxLogLastError(_T("MultiByteToWideChar")); 
 488     // finally, stream it in the control 
 489     const wchar_t *wpc 
= wchBuf
; 
 493     eds
.dwCookie 
= (DWORD
)&wpc
; 
 494     eds
.pfnCallback 
= wxRichEditStreamIn
; 
 497 #define SF_UNICODE 0x0010 
 500     if ( !::SendMessage(GetHwnd(), EM_STREAMIN
, 
 501                         SF_TEXT 
| SF_UNICODE 
| SFF_SELECTION
, 
 502                         (LPARAM
)&eds
) || eds
.dwError 
) 
 504         wxLogLastError(_T("EM_STREAMIN")); 
 512 #endif // wxUSE_RICHEDIT 
 514 void wxTextCtrl::WriteText(const wxString
& value
) 
 516     wxString valueDos 
= wxTextFile::Translate(value
, wxTextFileType_Dos
); 
 519     // there are several complications with the rich edit controls here 
 523         // first, ensure that the new text will be in the default style 
 524         if ( !m_defaultStyle
.IsDefault() ) 
 527             GetSelection(&start
, &end
); 
 528             SetStyle(start
, end
, m_defaultStyle 
); 
 531         // next check if the text we're inserting must be shown in a non 
 532         // default charset -- this only works for RichEdit > 1.0 
 533         if ( GetRichVersion() > 1 ) 
 535             wxFont font 
= m_defaultStyle
.GetFont(); 
 541                wxFontEncoding encoding 
= font
.GetEncoding(); 
 542                if ( encoding 
!= wxFONTENCODING_SYSTEM 
) 
 544                    done 
= StreamIn(valueDos
, encoding
); 
 551 #endif // wxUSE_RICHEDIT 
 553         ::SendMessage(GetHwnd(), EM_REPLACESEL
, 0, (LPARAM
)valueDos
.c_str()); 
 559 void wxTextCtrl::AppendText(const wxString
& text
) 
 561     SetInsertionPointEnd(); 
 566 void wxTextCtrl::Clear() 
 568     ::SetWindowText(GetHwnd(), wxT("")); 
 571 // ---------------------------------------------------------------------------- 
 572 // Clipboard operations 
 573 // ---------------------------------------------------------------------------- 
 575 void wxTextCtrl::Copy() 
 579         HWND hWnd 
= GetHwnd(); 
 580         SendMessage(hWnd
, WM_COPY
, 0, 0L); 
 584 void wxTextCtrl::Cut() 
 588         HWND hWnd 
= GetHwnd(); 
 589         SendMessage(hWnd
, WM_CUT
, 0, 0L); 
 593 void wxTextCtrl::Paste() 
 597         HWND hWnd 
= GetHwnd(); 
 598         SendMessage(hWnd
, WM_PASTE
, 0, 0L); 
 602 bool wxTextCtrl::CanCopy() const 
 604     // Can copy if there's a selection 
 606     GetSelection(& from
, & to
); 
 607     return (from 
!= to
) ; 
 610 bool wxTextCtrl::CanCut() const 
 612     // Can cut if there's a selection 
 614     GetSelection(& from
, & to
); 
 615     return (from 
!= to
) && (IsEditable()); 
 618 bool wxTextCtrl::CanPaste() const 
 626         UINT cf 
= 0; // 0 == any format 
 628         return ::SendMessage(GetHwnd(), EM_CANPASTE
, cf
, 0) != 0; 
 630 #endif // wxUSE_RICHEDIT 
 632     // Standard edit control: check for straight text on clipboard 
 633     if ( !::OpenClipboard(GetHwndOf(wxTheApp
->GetTopWindow())) ) 
 636     bool isTextAvailable 
= ::IsClipboardFormatAvailable(CF_TEXT
) != 0; 
 639     return isTextAvailable
; 
 642 // ---------------------------------------------------------------------------- 
 644 // ---------------------------------------------------------------------------- 
 646 void wxTextCtrl::SetEditable(bool editable
) 
 648     HWND hWnd 
= GetHwnd(); 
 649     SendMessage(hWnd
, EM_SETREADONLY
, (WPARAM
)!editable
, (LPARAM
)0L); 
 652 void wxTextCtrl::SetInsertionPoint(long pos
) 
 654     SetSelection(pos
, pos
); 
 660         static const wxChar 
*nothing 
= _T(""); 
 661         SendMessage(GetHwnd(), EM_REPLACESEL
, 0, (LPARAM
)nothing
); 
 665 void wxTextCtrl::SetInsertionPointEnd() 
 667     long pos 
= GetLastPosition(); 
 668     SetInsertionPoint(pos
); 
 671 long wxTextCtrl::GetInsertionPoint() const 
 679         SendMessage(GetHwnd(), EM_EXGETSEL
, 0, (LPARAM
) &range
); 
 682 #endif // wxUSE_RICHEDIT 
 684     DWORD Pos 
= (DWORD
)SendMessage(GetHwnd(), EM_GETSEL
, 0, 0L); 
 688 long wxTextCtrl::GetLastPosition() const 
 690     HWND hWnd 
= GetHwnd(); 
 692     // Will always return a number > 0 (according to docs) 
 693     int noLines 
= (int)SendMessage(hWnd
, EM_GETLINECOUNT
, (WPARAM
)0, (LPARAM
)0L); 
 695     // This gets the char index for the _beginning_ of the last line 
 696     int charIndex 
= (int)SendMessage(hWnd
, EM_LINEINDEX
, (WPARAM
)(noLines
-1), (LPARAM
)0L); 
 698     // Get number of characters in the last line. We'll add this to the character 
 699     // index for the last line, 1st position. 
 700     int lineLength 
= (int)SendMessage(hWnd
, EM_LINELENGTH
, (WPARAM
)charIndex
, (LPARAM
)0L); 
 702     return (long)(charIndex 
+ lineLength
); 
 705 // If the return values from and to are the same, there is no 
 707 void wxTextCtrl::GetSelection(long* from
, long* to
) const 
 713         ::SendMessage(GetHwnd(), EM_EXGETSEL
, 0, (LPARAM
) &charRange
); 
 715         *from 
= charRange
.cpMin
; 
 716         *to 
= charRange
.cpMax
; 
 719 #endif // !wxUSE_RICHEDIT 
 721         DWORD dwStart
, dwEnd
; 
 722         ::SendMessage(GetHwnd(), EM_GETSEL
, (WPARAM
)&dwStart
, (LPARAM
)&dwEnd
); 
 729 wxString 
wxTextCtrl::GetStringSelection() const 
 731     // the base class version works correctly for the rich text controls 
 732     // because there the lines are terminated with just '\r' which means that 
 733     // the string length is not changed in the result of the translations doen 
 734     // in GetValue() but for the normal ones when we replace "\r\n" with '\n' 
 735     // we break the indices 
 738         return wxTextCtrlBase::GetStringSelection(); 
 739 #endif // wxUSE_RICHEDIT 
 742     GetSelection(&from
, &to
); 
 747         str 
= wxGetWindowText(GetHWND()).Mid(from
, to 
- from
); 
 749         // and now that we have the correct selection, convert it to the 
 751         str 
= wxTextFile::Translate(str
, wxTextFileType_Unix
); 
 757 bool wxTextCtrl::IsEditable() const 
 759     long style 
= ::GetWindowLong(GetHwnd(), GWL_STYLE
); 
 761     return ((style 
& ES_READONLY
) == 0); 
 764 // ---------------------------------------------------------------------------- 
 766 // ---------------------------------------------------------------------------- 
 768 void wxTextCtrl::SetSelection(long from
, long to
) 
 770     DoSetSelection(from
, to
); 
 773 void wxTextCtrl::DoSetSelection(long from
, long to
, bool scrollCaret
) 
 775     // if from and to are both -1, it means (in wxWindows) that all text should 
 776     // be selected - translate into Windows convention 
 777     if ( (from 
== -1) && (to 
== -1) ) 
 783     HWND hWnd 
= GetHwnd(); 
 792         SendMessage(hWnd
, EM_EXSETSEL
, 0, (LPARAM
) &range
); 
 795 #endif // wxUSE_RICHEDIT 
 797         SendMessage(hWnd
, EM_SETSEL
, (WPARAM
)from
, (LPARAM
)to
); 
 802         SendMessage(hWnd
, EM_SCROLLCARET
, (WPARAM
)0, (LPARAM
)0); 
 805     // WPARAM is 0: selection is scrolled into view 
 806     SendMessage(hWnd
, EM_SETSEL
, (WPARAM
)0, (LPARAM
)MAKELONG(from
, to
)); 
 810 // ---------------------------------------------------------------------------- 
 812 // ---------------------------------------------------------------------------- 
 814 void wxTextCtrl::Replace(long from
, long to
, const wxString
& value
) 
 816     // Set selection and remove it 
 817     DoSetSelection(from
, to
, FALSE
); 
 819     SendMessage(GetHwnd(), EM_REPLACESEL
, 
 825                 (LPARAM
)value
.c_str()); 
 828 void wxTextCtrl::Remove(long from
, long to
) 
 830     Replace(from
, to
, _T("")); 
 833 bool wxTextCtrl::LoadFile(const wxString
& file
) 
 835     if ( wxTextCtrlBase::LoadFile(file
) ) 
 837         // update the size limit if needed 
 846 bool wxTextCtrl::IsModified() const 
 848     return (SendMessage(GetHwnd(), EM_GETMODIFY
, 0, 0) != 0); 
 851 // Makes 'unmodified' 
 852 void wxTextCtrl::DiscardEdits() 
 854     SendMessage(GetHwnd(), EM_SETMODIFY
, FALSE
, 0L); 
 857 int wxTextCtrl::GetNumberOfLines() const 
 859     return (int)SendMessage(GetHwnd(), EM_GETLINECOUNT
, (WPARAM
)0, (LPARAM
)0); 
 862 long wxTextCtrl::XYToPosition(long x
, long y
) const 
 864     HWND hWnd 
= GetHwnd(); 
 866     // This gets the char index for the _beginning_ of this line 
 867     int charIndex 
= (int)SendMessage(hWnd
, EM_LINEINDEX
, (WPARAM
)y
, (LPARAM
)0); 
 868     return (long)(x 
+ charIndex
); 
 871 bool wxTextCtrl::PositionToXY(long pos
, long *x
, long *y
) const 
 873     HWND hWnd 
= GetHwnd(); 
 875     // This gets the line number containing the character 
 880         lineNo 
= (int)SendMessage(hWnd
, EM_EXLINEFROMCHAR
, 0, (LPARAM
)pos
); 
 883 #endif // wxUSE_RICHEDIT 
 884         lineNo 
= (int)SendMessage(hWnd
, EM_LINEFROMCHAR
, (WPARAM
)pos
, 0); 
 892     // This gets the char index for the _beginning_ of this line 
 893     int charIndex 
= (int)SendMessage(hWnd
, EM_LINEINDEX
, (WPARAM
)lineNo
, (LPARAM
)0); 
 894     if ( charIndex 
== -1 ) 
 899     // The X position must therefore be the different between pos and charIndex 
 901         *x 
= (long)(pos 
- charIndex
); 
 908 void wxTextCtrl::ShowPosition(long pos
) 
 910     HWND hWnd 
= GetHwnd(); 
 912     // To scroll to a position, we pass the number of lines and characters 
 913     // to scroll *by*. This means that we need to: 
 914     // (1) Find the line position of the current line. 
 915     // (2) Find the line position of pos. 
 916     // (3) Scroll by (pos - current). 
 917     // For now, ignore the horizontal scrolling. 
 919     // Is this where scrolling is relative to - the line containing the caret? 
 920     // Or is the first visible line??? Try first visible line. 
 921 //    int currentLineLineNo1 = (int)SendMessage(hWnd, EM_LINEFROMCHAR, (WPARAM)-1, (LPARAM)0L); 
 923     int currentLineLineNo 
= (int)SendMessage(hWnd
, EM_GETFIRSTVISIBLELINE
, (WPARAM
)0, (LPARAM
)0L); 
 925     int specifiedLineLineNo 
= (int)SendMessage(hWnd
, EM_LINEFROMCHAR
, (WPARAM
)pos
, (LPARAM
)0L); 
 927     int linesToScroll 
= specifiedLineLineNo 
- currentLineLineNo
; 
 929     if (linesToScroll 
!= 0) 
 930       (void)SendMessage(hWnd
, EM_LINESCROLL
, (WPARAM
)0, (LPARAM
)linesToScroll
); 
 933 int wxTextCtrl::GetLineLength(long lineNo
) const 
 935     long charIndex 
= XYToPosition(0, lineNo
); 
 936     int len 
= (int)SendMessage(GetHwnd(), EM_LINELENGTH
, charIndex
, 0); 
 940 wxString 
wxTextCtrl::GetLineText(long lineNo
) const 
 942     size_t len 
= (size_t)GetLineLength(lineNo
) + 1; 
 944     // there must be at least enough place for the length WORD in the 
 949     wxChar 
*buf 
= str
.GetWriteBuf(len
); 
 951     *(WORD 
*)buf 
= (WORD
)len
; 
 952     len 
= (size_t)::SendMessage(GetHwnd(), EM_GETLINE
, lineNo
, (LPARAM
)buf
); 
 955     str
.UngetWriteBuf(len
); 
 960 void wxTextCtrl::SetMaxLength(unsigned long len
) 
 962     ::SendMessage(GetHwnd(), EM_LIMITTEXT
, len
, 0); 
 965 // ---------------------------------------------------------------------------- 
 967 // ---------------------------------------------------------------------------- 
 969 void wxTextCtrl::Undo() 
 973         ::SendMessage(GetHwnd(), EM_UNDO
, 0, 0); 
 977 void wxTextCtrl::Redo() 
 981         // Same as Undo, since Undo undoes the undo, i.e. a redo. 
 982         ::SendMessage(GetHwnd(), EM_UNDO
, 0, 0); 
 986 bool wxTextCtrl::CanUndo() const 
 988     return (::SendMessage(GetHwnd(), EM_CANUNDO
, 0, 0) != 0); 
 991 bool wxTextCtrl::CanRedo() const 
 993     return (::SendMessage(GetHwnd(), EM_CANUNDO
, 0, 0) != 0); 
 996 // ---------------------------------------------------------------------------- 
 997 // implemenation details 
 998 // ---------------------------------------------------------------------------- 
1000 void wxTextCtrl::Command(wxCommandEvent 
& event
) 
1002     SetValue(event
.GetString()); 
1003     ProcessCommand (event
); 
1006 void wxTextCtrl::OnDropFiles(wxDropFilesEvent
& event
) 
1008     // By default, load the first file into the text window. 
1009     if (event
.GetNumberOfFiles() > 0) 
1011         LoadFile(event
.GetFiles()[0]); 
1015 // ---------------------------------------------------------------------------- 
1016 // kbd input processing 
1017 // ---------------------------------------------------------------------------- 
1019 bool wxTextCtrl::MSWShouldPreProcessMessage(WXMSG
* pMsg
) 
1021     MSG 
*msg 
= (MSG 
*)pMsg
; 
1023     // check for our special keys here: if we don't do it and the parent frame 
1024     // uses them as accelerators, they wouldn't work at all, so we disable 
1025     // usual preprocessing for them 
1026     if ( msg
->message 
== WM_KEYDOWN 
) 
1028         WORD vkey 
= msg
->wParam
; 
1029         if ( (HIWORD(msg
->lParam
) & KF_ALTDOWN
) == KF_ALTDOWN 
) 
1031             if ( vkey 
== VK_BACK 
) 
1036             if ( wxIsCtrlDown() ) 
1050             else if ( wxIsShiftDown() ) 
1052                 if ( vkey 
== VK_INSERT 
|| vkey 
== VK_DELETE 
) 
1058     return wxControl::MSWShouldPreProcessMessage(pMsg
); 
1061 void wxTextCtrl::OnChar(wxKeyEvent
& event
) 
1063     switch ( event
.KeyCode() ) 
1066             if ( !(m_windowStyle 
& wxTE_MULTILINE
) ) 
1068                 wxCommandEvent 
event(wxEVT_COMMAND_TEXT_ENTER
, m_windowId
); 
1069                 InitCommandEvent(event
); 
1070                 event
.SetString(GetValue()); 
1071                 if ( GetEventHandler()->ProcessEvent(event
) ) 
1074             //else: multiline controls need Enter for themselves 
1079             // always produce navigation event - even if we process TAB 
1080             // ourselves the fact that we got here means that the user code 
1081             // decided to skip processing of this TAB - probably to let it 
1082             // do its default job. 
1084                 wxNavigationKeyEvent eventNav
; 
1085                 eventNav
.SetDirection(!event
.ShiftDown()); 
1086                 eventNav
.SetWindowChange(event
.ControlDown()); 
1087                 eventNav
.SetEventObject(this); 
1089                 if ( GetParent()->GetEventHandler()->ProcessEvent(eventNav
) ) 
1095     // no, we didn't process it 
1099 bool wxTextCtrl::MSWCommand(WXUINT param
, WXWORD 
WXUNUSED(id
)) 
1106                 wxFocusEvent 
event(param 
== EN_KILLFOCUS 
? wxEVT_KILL_FOCUS
 
1109                 event
.SetEventObject( this ); 
1110                 GetEventHandler()->ProcessEvent(event
); 
1116                 wxCommandEvent 
event(wxEVT_COMMAND_TEXT_UPDATED
, m_windowId
); 
1117                 InitCommandEvent(event
); 
1118                 event
.SetString(GetValue()); 
1119                 ProcessCommand(event
); 
1124             // the text size limit has been hit - increase it 
1125             if ( !AdjustSpaceLimit() ) 
1127                 wxCommandEvent 
event(wxEVT_COMMAND_TEXT_MAXLEN
, m_windowId
); 
1128                 InitCommandEvent(event
); 
1129                 event
.SetString(GetValue()); 
1130                 ProcessCommand(event
); 
1134             // the other notification messages are not processed 
1148 WXHBRUSH 
wxTextCtrl::OnCtlColor(WXHDC pDC
, WXHWND 
WXUNUSED(pWnd
), WXUINT 
WXUNUSED(nCtlColor
), 
1154                                WXUINT 
WXUNUSED(message
), 
1155                                WXWPARAM 
WXUNUSED(wParam
), 
1156                                WXLPARAM 
WXUNUSED(lParam
) 
1163         HBRUSH hbrush 
= Ctl3dCtlColorEx(message
, wParam
, lParam
); 
1164         return (WXHBRUSH
) hbrush
; 
1166 #endif // wxUSE_CTL3D 
1169     if (GetParent()->GetTransparentBackground()) 
1170         SetBkMode(hdc
, TRANSPARENT
); 
1172         SetBkMode(hdc
, OPAQUE
); 
1174     wxColour colBack 
= GetBackgroundColour(); 
1176     if (!IsEnabled() && (GetWindowStyle() & wxTE_MULTILINE
) == 0) 
1177         colBack 
= wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DFACE
); 
1179     ::SetBkColor(hdc
, wxColourToRGB(colBack
)); 
1180     ::SetTextColor(hdc
, wxColourToRGB(GetForegroundColour())); 
1182     wxBrush 
*brush 
= wxTheBrushList
->FindOrCreateBrush(colBack
, wxSOLID
); 
1184     return (WXHBRUSH
)brush
->GetResourceHandle(); 
1187 // In WIN16, need to override normal erasing because 
1188 // Ctl3D doesn't use the wxWindows background colour. 
1190 void wxTextCtrl::OnEraseBackground(wxEraseEvent
& event
) 
1192     wxColour 
col(m_backgroundColour
); 
1196         col 
= wxSystemSettings::GetSystemColour(wxSYS_COLOUR_WINDOW
); 
1200     ::GetClientRect(GetHwnd(), &rect
); 
1202     COLORREF ref 
= PALETTERGB(col
.Red(), 
1205     HBRUSH hBrush 
= ::CreateSolidBrush(ref
); 
1207         wxLogLastError(wxT("CreateSolidBrush")); 
1209     HDC hdc 
= (HDC
)event
.GetDC()->GetHDC(); 
1211     int mode 
= ::SetMapMode(hdc
, MM_TEXT
); 
1213     ::FillRect(hdc
, &rect
, hBrush
); 
1214     ::DeleteObject(hBrush
); 
1215     ::SetMapMode(hdc
, mode
); 
1220 bool wxTextCtrl::AdjustSpaceLimit() 
1223     unsigned int limit 
= ::SendMessage(GetHwnd(), EM_GETLIMITTEXT
, 0, 0); 
1225     // HACK: we try to automatically extend the limit for the amount of text 
1226     //       to allow (interactively) entering more than 64Kb of text under 
1227     //       Win9x but we shouldn't reset the text limit which was previously 
1228     //       set explicitly with SetMaxLength() 
1230     //       we could solve this by storing the limit we set in wxTextCtrl but 
1231     //       to save space we prefer to simply test here the actual limit 
1232     //       value: we consider that SetMaxLength() can only be called for 
1234     if ( limit 
< 0x8000 ) 
1236         // we've got more text than limit set by SetMaxLength() 
1240     unsigned int len 
= ::GetWindowTextLength(GetHwnd()); 
1243         limit 
= len 
+ 0x8000;    // 32Kb 
1248             // as a nice side effect, this also allows passing limit > 64Kb 
1249             ::SendMessage(GetHwnd(), EM_EXLIMITTEXT
, 0, limit
); 
1252 #endif // wxUSE_RICHEDIT 
1254             if ( limit 
> 0xffff ) 
1256                 // this will set it to a platform-dependent maximum (much more 
1257                 // than 64Kb under NT) 
1261             ::SendMessage(GetHwnd(), EM_LIMITTEXT
, limit
, 0); 
1266     // we changed the limit 
1270 bool wxTextCtrl::AcceptsFocus() const 
1272     // we don't want focus if we can't be edited 
1273     return IsEditable() && wxControl::AcceptsFocus(); 
1276 wxSize 
wxTextCtrl::DoGetBestSize() const 
1279     wxGetCharSize(GetHWND(), &cx
, &cy
, &GetFont()); 
1281     int wText 
= DEFAULT_ITEM_WIDTH
; 
1283     int hText 
= EDIT_HEIGHT_FROM_CHAR_HEIGHT(cy
); 
1284     if ( m_windowStyle 
& wxTE_MULTILINE 
) 
1286         hText 
*= wxMax(GetNumberOfLines(), 5); 
1288     //else: for single line control everything is ok 
1290     return wxSize(wText
, hText
); 
1293 // ---------------------------------------------------------------------------- 
1294 // standard handlers for standard edit menu events 
1295 // ---------------------------------------------------------------------------- 
1297 void wxTextCtrl::OnCut(wxCommandEvent
& WXUNUSED(event
)) 
1302 void wxTextCtrl::OnCopy(wxCommandEvent
& WXUNUSED(event
)) 
1307 void wxTextCtrl::OnPaste(wxCommandEvent
& WXUNUSED(event
)) 
1312 void wxTextCtrl::OnUndo(wxCommandEvent
& WXUNUSED(event
)) 
1317 void wxTextCtrl::OnRedo(wxCommandEvent
& WXUNUSED(event
)) 
1322 void wxTextCtrl::OnUpdateCut(wxUpdateUIEvent
& event
) 
1324     event
.Enable( CanCut() ); 
1327 void wxTextCtrl::OnUpdateCopy(wxUpdateUIEvent
& event
) 
1329     event
.Enable( CanCopy() ); 
1332 void wxTextCtrl::OnUpdatePaste(wxUpdateUIEvent
& event
) 
1334     event
.Enable( CanPaste() ); 
1337 void wxTextCtrl::OnUpdateUndo(wxUpdateUIEvent
& event
) 
1339     event
.Enable( CanUndo() ); 
1342 void wxTextCtrl::OnUpdateRedo(wxUpdateUIEvent
& event
) 
1344     event
.Enable( CanRedo() ); 
1347 // the rest of the file only deals with the rich edit controls 
1350 // ---------------------------------------------------------------------------- 
1351 // EN_LINK processing 
1352 // ---------------------------------------------------------------------------- 
1354 bool wxTextCtrl::MSWOnNotify(int idCtrl
, WXLPARAM lParam
, WXLPARAM 
*result
) 
1356     NMHDR 
*hdr 
= (NMHDR
* )lParam
; 
1357     if ( hdr
->code 
== EN_LINK 
) 
1359         ENLINK 
*enlink 
= (ENLINK 
*)hdr
; 
1361         switch ( enlink
->msg 
) 
1364                 // ok, so it is hardcoded - do we really nee to customize it? 
1365                 ::SetCursor(GetHcursorOf(wxCursor(wxCURSOR_HAND
))); 
1370             case WM_LBUTTONDOWN
: 
1372             case WM_LBUTTONDBLCLK
: 
1373             case WM_RBUTTONDOWN
: 
1375             case WM_RBUTTONDBLCLK
: 
1376                 // send a mouse event 
1378                     static const wxEventType eventsMouse
[] = 
1389                     // the event ids are consecutive 
1391                         evtMouse(eventsMouse
[enlink
->msg 
- WM_MOUSEMOVE
]); 
1393                     InitMouseEvent(evtMouse
, 
1394                                    GET_X_LPARAM(enlink
->lParam
), 
1395                                    GET_Y_LPARAM(enlink
->lParam
), 
1398                     wxTextUrlEvent 
event(m_windowId
, evtMouse
, 
1400                                          enlink
->chrg
.cpMax
); 
1402                     InitCommandEvent(event
); 
1404                     *result 
= ProcessCommand(event
); 
1416 // ---------------------------------------------------------------------------- 
1417 // colour setting for the rich edit controls 
1418 // ---------------------------------------------------------------------------- 
1420 // Watcom C++ doesn't define this 
1422 #define SCF_ALL 0x0004 
1425 bool wxTextCtrl::SetBackgroundColour(const wxColour
& colour
) 
1427     if ( !wxTextCtrlBase::SetBackgroundColour(colour
) ) 
1429         // colour didn't really change 
1435         // rich edit doesn't use WM_CTLCOLOR, hence we need to send 
1436         // EM_SETBKGNDCOLOR additionally 
1437         ::SendMessage(GetHwnd(), EM_SETBKGNDCOLOR
, 0, wxColourToRGB(colour
)); 
1443 bool wxTextCtrl::SetForegroundColour(const wxColour
& colour
) 
1445     if ( !wxTextCtrlBase::SetForegroundColour(colour
) ) 
1447         // colour didn't really change 
1453         // change the colour of everything 
1456         cf
.cbSize 
= sizeof(cf
); 
1457         cf
.dwMask 
= CFM_COLOR
; 
1458         cf
.crTextColor 
= wxColourToRGB(colour
); 
1459         ::SendMessage(GetHwnd(), EM_SETCHARFORMAT
, SCF_ALL
, (LPARAM
)&cf
); 
1465 // ---------------------------------------------------------------------------- 
1466 // styling support for rich edit controls 
1467 // ---------------------------------------------------------------------------- 
1469 bool wxTextCtrl::SetStyle(long start
, long end
, const wxTextAttr
& style
) 
1473         // can't do it with normal text control 
1477     // the rich text control doesn't handle setting background colour, so don't 
1478     // even try if it's the only thing we want to change 
1479     if ( wxRichEditModule::GetLoadedVersion() < 2 && 
1480          !style
.HasFont() && !style
.HasTextColour() ) 
1482         // nothing to do: return TRUE if there was really nothing to do and 
1483         // FALSE if we failed to set bg colour 
1484         return !style
.HasBackgroundColour(); 
1487     // order the range if needed 
1495     // we can only change the format of the selection, so select the range we 
1496     // want and restore the old selection later 
1497     long startOld
, endOld
; 
1498     GetSelection(&startOld
, &endOld
); 
1500     // but do we really have to change the selection? 
1501     bool changeSel 
= start 
!= startOld 
|| end 
!= endOld
; 
1505         DoSetSelection(start
, end
, FALSE
); 
1508     // initialize CHARFORMAT struct 
1515     cf
.cbSize 
= sizeof(cf
); 
1517     if ( style
.HasFont() ) 
1519         // VZ: CFM_CHARSET doesn't seem to do anything at all in RichEdit 2.0 
1520         //     but using it doesn't seem to hurt neither so leaving it for now 
1522         cf
.dwMask 
|= CFM_FACE 
| CFM_SIZE 
| CFM_CHARSET 
| 
1523                      CFM_ITALIC 
| CFM_BOLD 
| CFM_UNDERLINE
; 
1525         // fill in data from LOGFONT but recalculate lfHeight because we need 
1526         // the real height in twips and not the negative number which 
1527         // wxFillLogFont() returns (this is correct in general and works with 
1528         // the Windows font mapper, but not here) 
1530         wxFillLogFont(&lf
, &style
.GetFont()); 
1531         cf
.yHeight 
= 20*style
.GetFont().GetPointSize(); // 1 pt = 20 twips 
1532         cf
.bCharSet 
= lf
.lfCharSet
; 
1533         cf
.bPitchAndFamily 
= lf
.lfPitchAndFamily
; 
1534         wxStrncpy( cf
.szFaceName
, lf
.lfFaceName
, WXSIZEOF(cf
.szFaceName
) ); 
1536         // also deal with underline/italic/bold attributes: note that we must 
1537         // always set CFM_ITALIC &c bits in dwMask, even if we don't set the 
1538         // style to allow clearing it 
1541             cf
.dwEffects 
|= CFE_ITALIC
; 
1544         if ( lf
.lfWeight 
== FW_BOLD 
) 
1546             cf
.dwEffects 
|= CFE_BOLD
; 
1549         if ( lf
.lfUnderline 
) 
1551             cf
.dwEffects 
|= CFE_UNDERLINE
; 
1554         // strikeout fonts are not supported by wxWindows 
1557     if ( style
.HasTextColour() ) 
1559         cf
.dwMask 
|= CFM_COLOR
; 
1560         cf
.crTextColor 
= wxColourToRGB(style
.GetTextColour()); 
1564     if ( wxRichEditModule::GetLoadedVersion() > 1 && style
.HasBackgroundColour() ) 
1566         cf
.dwMask 
|= CFM_BACKCOLOR
; 
1567         cf
.crBackColor 
= wxColourToRGB(style
.GetBackgroundColour()); 
1569 #endif // wxUSE_RICHEDIT2 
1571     // do format the selection 
1572     bool ok 
= ::SendMessage(GetHwnd(), EM_SETCHARFORMAT
, 
1573                             SCF_SELECTION
, (LPARAM
)&cf
) != 0; 
1576         wxLogDebug(_T("SendMessage(EM_SETCHARFORMAT, SCF_SELECTION) failed")); 
1581         // restore the original selection 
1582         DoSetSelection(startOld
, endOld
, FALSE
); 
1588 // ---------------------------------------------------------------------------- 
1590 // ---------------------------------------------------------------------------- 
1592 bool wxRichEditModule::OnInit() 
1594     // don't do anything - we will load it when needed 
1598 void wxRichEditModule::OnExit() 
1602         FreeLibrary(ms_hRichEdit
); 
1607 bool wxRichEditModule::Load(int version
) 
1609     wxCHECK_MSG( version 
>= 1 && version 
<= 3, FALSE
, 
1610                  _T("incorrect richedit control version requested") ); 
1612     if ( version 
<= ms_verRichEdit 
) 
1614         // we've already got this or better 
1620         ::FreeLibrary(ms_hRichEdit
); 
1623     // always try load riched20.dll first - like this we won't have to reload 
1624     // it later if we're first asked for RE 1 and then for RE 2 or 3 
1625     wxString dllname 
= _T("riched20.dll"); 
1626     ms_hRichEdit 
= ::LoadLibrary(dllname
); 
1627     ms_verRichEdit 
= 2; // no way to tell if it's 2 or 3, assume 2 
1629     if ( !ms_hRichEdit 
&& (version 
== 1) ) 
1631         // fall back to RE 1 
1632         dllname 
= _T("riched32.dll"); 
1633         ms_hRichEdit 
= ::LoadLibrary(dllname
); 
1637     if ( !ms_hRichEdit 
) 
1639         wxLogSysError(_("Could not load Rich Edit DLL '%s'"), dllname
.c_str()); 
1641         ms_verRichEdit 
= -1; 
1649 #endif // wxUSE_RICHEDIT 
1651 #endif // wxUSE_TEXTCTRL