1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/msw/textctrl.cpp
3 // Purpose:     wxTextCtrl
4 // Author:      Julian Smart
5 // Modified by:
6 // Created:     04/01/98
7 // RCS-ID:      $Id: textctrl.cpp 60394 2009-04-26 18:41:02Z VZ $
8 // Copyright:   (c) Julian Smart
9 // Licence:     wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11 
12 // ============================================================================
13 // declarations
14 // ============================================================================
15 
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19 
20 // For compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
22 
23 #ifdef __BORLANDC__
24     #pragma hdrstop
25 #endif
26 
27 #if wxUSE_TEXTCTRL && !(defined(__SMARTPHONE__) && defined(__WXWINCE__))
28 
29 #ifndef WX_PRECOMP
30     #include "wx/textctrl.h"
31     #include "wx/settings.h"
32     #include "wx/brush.h"
33     #include "wx/utils.h"
34     #include "wx/intl.h"
35     #include "wx/log.h"
36     #include "wx/app.h"
37     #include "wx/menu.h"
38     #include "wx/math.h"
39     #include "wx/module.h"
40 #endif
41 
42 #include "wx/sysopt.h"
43 
44 #if wxUSE_CLIPBOARD
45     #include "wx/clipbrd.h"
46 #endif
47 
48 #include "wx/textfile.h"
49 
50 #include <windowsx.h>
51 
52 #include "wx/msw/private.h"
53 #include "wx/msw/winundef.h"
54 #include "wx/msw/mslu.h"
55 
56 #include <string.h>
57 #include <stdlib.h>
58 
59 #ifndef __WXWINCE__
60 #include <sys/types.h>
61 #endif
62 
63 #if wxUSE_RICHEDIT
64 
65 #if wxUSE_INKEDIT
66 #include "wx/dynlib.h"
67 #endif
68 
69 // old mingw32 has richedit stuff directly in windows.h and doesn't have
70 // richedit.h at all
71 #if !defined(__GNUWIN32_OLD__) || defined(__CYGWIN10__)
72     #include <richedit.h>
73 #endif
74 
75 #endif // wxUSE_RICHEDIT
76 
77 #include "wx/msw/missing.h"
78 
79 // ----------------------------------------------------------------------------
80 // private classes
81 // ----------------------------------------------------------------------------
82 
83 #if wxUSE_RICHEDIT
84 
85 // this module initializes RichEdit DLL(s) if needed
86 class wxRichEditModule : public wxModule
87 {
88 public:
89     enum Version
90     {
91         Version_1,          // riched32.dll
92         Version_2or3,       // both use riched20.dll
93         Version_41,         // msftedit.dll (XP SP1 and Windows 2003)
94         Version_Max
95     };
96 
97     virtual bool OnInit();
98     virtual void OnExit();
99 
100     // load the richedit DLL for the specified version of rich edit
101     static bool Load(Version version);
102 
103 #if wxUSE_INKEDIT
104     // load the InkEdit library
105     static bool LoadInkEdit();
106 #endif
107 
108 private:
109     // the handles to richedit 1.0 and 2.0 (or 3.0) DLLs
110     static HINSTANCE ms_hRichEdit[Version_Max];
111 
112 #if wxUSE_INKEDIT
113     static wxDynamicLibrary ms_inkEditLib;
114     static bool             ms_inkEditLibLoadAttemped;
115 #endif
116 
117     DECLARE_DYNAMIC_CLASS(wxRichEditModule)
118 };
119 
120 HINSTANCE wxRichEditModule::ms_hRichEdit[Version_Max] = { NULL, NULL, NULL };
121 
122 #if wxUSE_INKEDIT
123 wxDynamicLibrary wxRichEditModule::ms_inkEditLib;
124 bool             wxRichEditModule::ms_inkEditLibLoadAttemped = false;
125 #endif
126 
127 IMPLEMENT_DYNAMIC_CLASS(wxRichEditModule, wxModule)
128 
129 #endif // wxUSE_RICHEDIT
130 
131 // a small class used to set m_updatesCount to 0 (to filter duplicate events if
132 // necessary) and to reset it back to -1 afterwards
133 class UpdatesCountFilter
134 {
135 public:
UpdatesCountFilter(int & count)136     UpdatesCountFilter(int& count)
137         : m_count(count)
138     {
139         wxASSERT_MSG( m_count == -1 || m_count == -2,
140                       _T("wrong initial m_updatesCount value") );
141 
142         if (m_count != -2)
143             m_count = 0;
144         //else: we don't want to count how many update events we get as we're going
145         //      to ignore all of them
146     }
147 
~UpdatesCountFilter()148     ~UpdatesCountFilter()
149     {
150         m_count = -1;
151     }
152 
153     // return true if an event has been received
GotUpdate() const154     bool GotUpdate() const
155     {
156         return m_count == 1;
157     }
158 
159 private:
160     int& m_count;
161 
162     DECLARE_NO_COPY_CLASS(UpdatesCountFilter)
163 };
164 
165 // ----------------------------------------------------------------------------
166 // event tables and other macros
167 // ----------------------------------------------------------------------------
168 
169 #if wxUSE_EXTENDED_RTTI
170 WX_DEFINE_FLAGS( wxTextCtrlStyle )
171 
wxBEGIN_FLAGS(wxTextCtrlStyle)172 wxBEGIN_FLAGS( wxTextCtrlStyle )
173     // new style border flags, we put them first to
174     // use them for streaming out
175     wxFLAGS_MEMBER(wxBORDER_SIMPLE)
176     wxFLAGS_MEMBER(wxBORDER_SUNKEN)
177     wxFLAGS_MEMBER(wxBORDER_DOUBLE)
178     wxFLAGS_MEMBER(wxBORDER_RAISED)
179     wxFLAGS_MEMBER(wxBORDER_STATIC)
180     wxFLAGS_MEMBER(wxBORDER_NONE)
181 
182     // old style border flags
183     wxFLAGS_MEMBER(wxSIMPLE_BORDER)
184     wxFLAGS_MEMBER(wxSUNKEN_BORDER)
185     wxFLAGS_MEMBER(wxDOUBLE_BORDER)
186     wxFLAGS_MEMBER(wxRAISED_BORDER)
187     wxFLAGS_MEMBER(wxSTATIC_BORDER)
188     wxFLAGS_MEMBER(wxBORDER)
189 
190     // standard window styles
191     wxFLAGS_MEMBER(wxTAB_TRAVERSAL)
192     wxFLAGS_MEMBER(wxCLIP_CHILDREN)
193     wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW)
194     wxFLAGS_MEMBER(wxWANTS_CHARS)
195     wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE)
196     wxFLAGS_MEMBER(wxALWAYS_SHOW_SB )
197     wxFLAGS_MEMBER(wxVSCROLL)
198     wxFLAGS_MEMBER(wxHSCROLL)
199 
200     wxFLAGS_MEMBER(wxTE_PROCESS_ENTER)
201     wxFLAGS_MEMBER(wxTE_PROCESS_TAB)
202     wxFLAGS_MEMBER(wxTE_MULTILINE)
203     wxFLAGS_MEMBER(wxTE_PASSWORD)
204     wxFLAGS_MEMBER(wxTE_READONLY)
205     wxFLAGS_MEMBER(wxHSCROLL)
206     wxFLAGS_MEMBER(wxTE_RICH)
207     wxFLAGS_MEMBER(wxTE_RICH2)
208     wxFLAGS_MEMBER(wxTE_AUTO_URL)
209     wxFLAGS_MEMBER(wxTE_NOHIDESEL)
210     wxFLAGS_MEMBER(wxTE_LEFT)
211     wxFLAGS_MEMBER(wxTE_CENTRE)
212     wxFLAGS_MEMBER(wxTE_RIGHT)
213     wxFLAGS_MEMBER(wxTE_DONTWRAP)
214     wxFLAGS_MEMBER(wxTE_CHARWRAP)
215     wxFLAGS_MEMBER(wxTE_WORDWRAP)
216 
217 wxEND_FLAGS( wxTextCtrlStyle )
218 
219 IMPLEMENT_DYNAMIC_CLASS_XTI(wxTextCtrl, wxControl,"wx/textctrl.h")
220 
221 wxBEGIN_PROPERTIES_TABLE(wxTextCtrl)
222     wxEVENT_PROPERTY( TextUpdated , wxEVT_COMMAND_TEXT_UPDATED , wxCommandEvent )
223     wxEVENT_PROPERTY( TextEnter , wxEVT_COMMAND_TEXT_ENTER , wxCommandEvent )
224 
225     wxPROPERTY( Font , wxFont , SetFont , GetFont  , EMPTY_MACROVALUE, 0 /*flags*/ , wxT("Helpstring") , wxT("group") )
226     wxPROPERTY( Value , wxString , SetValue, GetValue, wxString() , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
227     wxPROPERTY_FLAGS( WindowStyle , wxTextCtrlStyle , long , SetWindowStyleFlag , GetWindowStyleFlag , EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style
228 wxEND_PROPERTIES_TABLE()
229 
230 wxBEGIN_HANDLERS_TABLE(wxTextCtrl)
231 wxEND_HANDLERS_TABLE()
232 
233 wxCONSTRUCTOR_6( wxTextCtrl , wxWindow* , Parent , wxWindowID , Id , wxString , Value , wxPoint , Position , wxSize , Size , long , WindowStyle)
234 #else
235 IMPLEMENT_DYNAMIC_CLASS(wxTextCtrl, wxTextCtrlBase)
236 #endif
237 
238 
239 BEGIN_EVENT_TABLE(wxTextCtrl, wxTextCtrlBase)
240     EVT_CHAR(wxTextCtrl::OnChar)
241     EVT_KEY_DOWN(wxTextCtrl::OnKeyDown)
242     EVT_DROP_FILES(wxTextCtrl::OnDropFiles)
243 
244 #if wxUSE_RICHEDIT
245     EVT_CONTEXT_MENU(wxTextCtrl::OnContextMenu)
246 #endif
247 
248     EVT_MENU(wxID_CUT, wxTextCtrl::OnCut)
249     EVT_MENU(wxID_COPY, wxTextCtrl::OnCopy)
250     EVT_MENU(wxID_PASTE, wxTextCtrl::OnPaste)
251     EVT_MENU(wxID_UNDO, wxTextCtrl::OnUndo)
252     EVT_MENU(wxID_REDO, wxTextCtrl::OnRedo)
253     EVT_MENU(wxID_CLEAR, wxTextCtrl::OnDelete)
254     EVT_MENU(wxID_SELECTALL, wxTextCtrl::OnSelectAll)
255 
256     EVT_UPDATE_UI(wxID_CUT, wxTextCtrl::OnUpdateCut)
257     EVT_UPDATE_UI(wxID_COPY, wxTextCtrl::OnUpdateCopy)
258     EVT_UPDATE_UI(wxID_PASTE, wxTextCtrl::OnUpdatePaste)
259     EVT_UPDATE_UI(wxID_UNDO, wxTextCtrl::OnUpdateUndo)
260     EVT_UPDATE_UI(wxID_REDO, wxTextCtrl::OnUpdateRedo)
261     EVT_UPDATE_UI(wxID_CLEAR, wxTextCtrl::OnUpdateDelete)
262     EVT_UPDATE_UI(wxID_SELECTALL, wxTextCtrl::OnUpdateSelectAll)
263 
264     EVT_SET_FOCUS(wxTextCtrl::OnSetFocus)
265 END_EVENT_TABLE()
266 
267 // ============================================================================
268 // implementation
269 // ============================================================================
270 
271 // ----------------------------------------------------------------------------
272 // creation
273 // ----------------------------------------------------------------------------
274 
275 void wxTextCtrl::Init()
276 {
277 #if wxUSE_RICHEDIT
278     m_verRichEdit = 0;
279 #endif // wxUSE_RICHEDIT
280 
281 #if wxUSE_INKEDIT && wxUSE_RICHEDIT
282     m_isInkEdit = 0;
283 #endif
284 
285     m_privateContextMenu = NULL;
286     m_updatesCount = -1;
287     m_isNativeCaretShown = true;
288 }
289 
~wxTextCtrl()290 wxTextCtrl::~wxTextCtrl()
291 {
292     delete m_privateContextMenu;
293 }
294 
Create(wxWindow * parent,wxWindowID id,const wxString & value,const wxPoint & pos,const wxSize & size,long style,const wxValidator & validator,const wxString & name)295 bool wxTextCtrl::Create(wxWindow *parent,
296                         wxWindowID id,
297                         const wxString& value,
298                         const wxPoint& pos,
299                         const wxSize& size,
300                         long style,
301                         const wxValidator& validator,
302                         const wxString& name)
303 {
304 #ifdef __WXWINCE__
305     if ((style & wxBORDER_MASK) == 0)
306         style |= wxBORDER_SIMPLE;
307 #else
308     // Standard text control already handles theming
309     if ((style & (wxTE_RICH|wxTE_RICH2)) && ((style & wxBORDER_MASK) == wxBORDER_DEFAULT))
310         style |= wxBORDER_THEME;
311 #endif
312 
313     // base initialization
314     if ( !CreateControl(parent, id, pos, size, style, validator, name) )
315         return false;
316 
317     if ( !MSWCreateText(value, pos, size) )
318         return false;
319 
320     return true;
321 }
322 
MSWCreateText(const wxString & value,const wxPoint & pos,const wxSize & size)323 bool wxTextCtrl::MSWCreateText(const wxString& value,
324                                const wxPoint& pos,
325                                const wxSize& size)
326 {
327     // translate wxWin style flags to MSW ones
328     WXDWORD msStyle = MSWGetCreateWindowFlags();
329 
330     // do create the control - either an EDIT or RICHEDIT
331     wxString windowClass = wxT("EDIT");
332 
333 #if defined(__POCKETPC__) || defined(__SMARTPHONE__)
334     // A control that capitalizes the first letter
335     if ( HasFlag(wxTE_CAPITALIZE) )
336         windowClass = wxT("CAPEDIT");
337 #endif
338 
339 #if wxUSE_RICHEDIT
340     if ( m_windowStyle & wxTE_AUTO_URL )
341     {
342         // automatic URL detection only works in RichEdit 2.0+
343         m_windowStyle |= wxTE_RICH2;
344     }
345 
346     if ( m_windowStyle & wxTE_RICH2 )
347     {
348         // using richedit 2.0 implies using wxTE_RICH
349         m_windowStyle |= wxTE_RICH;
350     }
351 
352     // we need to load the richedit DLL before creating the rich edit control
353     if ( m_windowStyle & wxTE_RICH )
354     {
355         // versions 2.0, 3.0 and 4.1 of rich edit are mostly compatible with
356         // each other but not with version 1.0, so we have separate flags for
357         // the version 1.0 and the others (and so m_verRichEdit may be 0 (plain
358         // EDIT control), 1 for version 1.0 or 2 for any higher version)
359         //
360         // notice that 1.0 has no Unicode support at all so in Unicode build we
361         // must use another version
362 
363 #if wxUSE_UNICODE
364         m_verRichEdit = 2;
365 #else // !wxUSE_UNICODE
366         m_verRichEdit = m_windowStyle & wxTE_RICH2 ? 2 : 1;
367 #endif // wxUSE_UNICODE/!wxUSE_UNICODE
368 
369 #if wxUSE_INKEDIT
370         // First test if we can load an ink edit control. Normally, all edit
371         // controls will be made ink edit controls if a tablet environment is
372         // found (or if msw.inkedit != 0 and the InkEd.dll is present).
373         // However, an application can veto ink edit controls by either specifying
374         // msw.inkedit = 0 or by removing wxTE_RICH[2] from the style.
375         //
376         if ((wxSystemSettings::HasFeature(wxSYS_TABLET_PRESENT) || wxSystemOptions::GetOptionInt(wxT("msw.inkedit")) != 0) &&
377             !(wxSystemOptions::HasOption(wxT("msw.inkedit")) && wxSystemOptions::GetOptionInt(wxT("msw.inkedit")) == 0))
378         {
379             if (wxRichEditModule::LoadInkEdit())
380             {
381                 windowClass = INKEDIT_CLASS;
382 
383 #if wxUSE_INKEDIT && wxUSE_RICHEDIT
384                 m_isInkEdit = 1;
385 #endif
386 
387                 // Fake rich text version for other calls
388                 m_verRichEdit = 2;
389             }
390         }
391 #endif
392 
393         if (!IsInkEdit())
394         {
395             if ( m_verRichEdit == 2 )
396             {
397                 if ( wxRichEditModule::Load(wxRichEditModule::Version_41) )
398                 {
399                     // yes, class name for version 4.1 really is 5.0
400                     windowClass = _T("RICHEDIT50W");
401                 }
402                 else if ( wxRichEditModule::Load(wxRichEditModule::Version_2or3) )
403                 {
404                     windowClass = _T("RichEdit20")
405 #if wxUSE_UNICODE
406                                 _T("W");
407 #else // ANSI
408                                 _T("A");
409 #endif // Unicode/ANSI
410                 }
411                 else // failed to load msftedit.dll and riched20.dll
412                 {
413                     m_verRichEdit = 1;
414                 }
415             }
416 
417             if ( m_verRichEdit == 1 )
418             {
419                 if ( wxRichEditModule::Load(wxRichEditModule::Version_1) )
420                 {
421                     windowClass = _T("RICHEDIT");
422                 }
423                 else // failed to load any richedit control DLL
424                 {
425                     // only give the error msg once if the DLL can't be loaded
426                     static bool s_errorGiven = false; // MT ok as only used by GUI
427 
428                     if ( !s_errorGiven )
429                     {
430                         wxLogError(_("Impossible to create a rich edit control, using simple text control instead. Please reinstall riched32.dll"));
431 
432                         s_errorGiven = true;
433                     }
434 
435                     m_verRichEdit = 0;
436                 }
437             }
438         } // !useInkEdit
439     }
440 #endif // wxUSE_RICHEDIT
441 
442     // we need to turn '\n's into "\r\n"s for the multiline controls
443     wxString valueWin;
444     if ( m_windowStyle & wxTE_MULTILINE )
445     {
446         valueWin = wxTextFile::Translate(value, wxTextFileType_Dos);
447     }
448     else // single line
449     {
450         valueWin = value;
451     }
452 
453     if ( !MSWCreateControl(windowClass, msStyle, pos, size, valueWin) )
454         return false;
455 
456 #if wxUSE_RICHEDIT
457     if (IsRich())
458     {
459 #if wxUSE_INKEDIT
460         if (IsInkEdit())
461         {
462             // Pass IEM_InsertText (0) as wParam, in order to have the ink always
463             // converted to text.
464             ::SendMessage(GetHwnd(), EM_SETINKINSERTMODE, 0, 0);
465 
466             // Make sure the mouse can be used for input
467             ::SendMessage(GetHwnd(), EM_SETUSEMOUSEFORINPUT, 1, 0);
468         }
469 #endif
470 
471         // enable the events we're interested in: we want to get EN_CHANGE as
472         // for the normal controls
473         LPARAM mask = ENM_CHANGE;
474 
475         if (GetRichVersion() == 1 && !IsInkEdit())
476         {
477             // we also need EN_MSGFILTER for richedit 1.0 for the reasons
478             // explained in its handler
479            mask |= ENM_MOUSEEVENTS;
480 
481            // we also need to force the appearance of the vertical scrollbar
482            // initially as otherwise the control doesn't refresh correctly
483            // after resize: but once the vertical scrollbar had been shown
484            // (even if it's subsequently hidden) it does
485            //
486            // this is clearly a bug and for now it has been only noticed under
487            // Windows XP, so if we're sure it works correctly under other
488            // systems we could do this only for XP
489            SetSize(-1, 1); // 1 is small enough to force vert scrollbar
490            SetInitialSize(size);
491         }
492         else if ( m_windowStyle & wxTE_AUTO_URL )
493         {
494             mask |= ENM_LINK;
495 
496             ::SendMessage(GetHwnd(), EM_AUTOURLDETECT, TRUE, 0);
497         }
498 
499         ::SendMessage(GetHwnd(), EM_SETEVENTMASK, 0, mask);
500     }
501 #endif // wxUSE_RICHEDIT
502 
503 #ifndef __WXWINCE__
504     // Without this, if we pass the size in the constructor and then don't change it,
505     // the themed borders will be drawn incorrectly.
506     SetWindowPos(GetHwnd(), NULL, 0, 0, 0, 0,
507                 SWP_NOZORDER|SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE|
508                 SWP_FRAMECHANGED);
509 #endif
510 
511     return true;
512 }
513 
514 // Make sure the window style (etc.) reflects the HWND style (roughly)
AdoptAttributesFromHWND()515 void wxTextCtrl::AdoptAttributesFromHWND()
516 {
517     wxWindow::AdoptAttributesFromHWND();
518 
519     HWND hWnd = GetHwnd();
520     long style = ::GetWindowLong(hWnd, GWL_STYLE);
521 
522     // retrieve the style to see whether this is an edit or richedit ctrl
523 #if wxUSE_RICHEDIT
524     wxString classname = wxGetWindowClass(GetHWND());
525 
526     if ( classname.IsSameAs(_T("EDIT"), false /* no case */) )
527     {
528         m_verRichEdit = 0;
529     }
530     else // rich edit?
531     {
532         wxChar c;
533         if ( wxSscanf(classname, _T("RichEdit%d0%c"), &m_verRichEdit, &c) != 2 )
534         {
535             wxLogDebug(_T("Unknown edit control '%s'."), classname.c_str());
536 
537             m_verRichEdit = 0;
538         }
539     }
540 #endif // wxUSE_RICHEDIT
541 
542     if (style & ES_MULTILINE)
543         m_windowStyle |= wxTE_MULTILINE;
544     if (style & ES_PASSWORD)
545         m_windowStyle |= wxTE_PASSWORD;
546     if (style & ES_READONLY)
547         m_windowStyle |= wxTE_READONLY;
548     if (style & ES_WANTRETURN)
549         m_windowStyle |= wxTE_PROCESS_ENTER;
550     if (style & ES_CENTER)
551         m_windowStyle |= wxTE_CENTRE;
552     if (style & ES_RIGHT)
553         m_windowStyle |= wxTE_RIGHT;
554 }
555 
MSWGetStyle(long style,WXDWORD * exstyle) const556 WXDWORD wxTextCtrl::MSWGetStyle(long style, WXDWORD *exstyle) const
557 {
558     long msStyle = wxControl::MSWGetStyle(style, exstyle);
559 
560     // styles which we alaways add by default
561     if ( style & wxTE_MULTILINE )
562     {
563         msStyle |= ES_MULTILINE | ES_WANTRETURN;
564         if ( !(style & wxTE_NO_VSCROLL) )
565         {
566             // always adjust the vertical scrollbar automatically if we have it
567             msStyle |= WS_VSCROLL | ES_AUTOVSCROLL;
568 
569 #if wxUSE_RICHEDIT
570             // we have to use this style for the rich edit controls because
571             // without it the vertical scrollbar never appears at all in
572             // richedit 3.0 because of our ECO_NOHIDESEL hack (search for it)
573             if ( style & wxTE_RICH2 )
574             {
575                 msStyle |= ES_DISABLENOSCROLL;
576             }
577 #endif // wxUSE_RICHEDIT
578         }
579 
580         style |= wxTE_PROCESS_ENTER;
581     }
582     else // !multiline
583     {
584         // there is really no reason to not have this style for single line
585         // text controls
586         msStyle |= ES_AUTOHSCROLL;
587     }
588 
589     // note that wxTE_DONTWRAP is the same as wxHSCROLL so if we have a horz
590     // scrollbar, there is no wrapping -- which makes sense
591     if ( style & wxTE_DONTWRAP )
592     {
593         // automatically scroll the control horizontally as necessary
594         //
595         // NB: ES_AUTOHSCROLL is needed for richedit controls or they don't
596         //     show horz scrollbar at all, even in spite of WS_HSCROLL, and as
597         //     it doesn't seem to do any harm for plain edit controls, add it
598         //     always
599         msStyle |= WS_HSCROLL | ES_AUTOHSCROLL;
600     }
601 
602     if ( style & wxTE_READONLY )
603         msStyle |= ES_READONLY;
604 
605     if ( style & wxTE_PASSWORD )
606         msStyle |= ES_PASSWORD;
607 
608     if ( style & wxTE_NOHIDESEL )
609         msStyle |= ES_NOHIDESEL;
610 
611     // note that we can't do do "& wxTE_LEFT" as wxTE_LEFT == 0
612     if ( style & wxTE_CENTRE )
613         msStyle |= ES_CENTER;
614     else if ( style & wxTE_RIGHT )
615         msStyle |= ES_RIGHT;
616     else
617         msStyle |= ES_LEFT; // ES_LEFT is 0 as well but for consistency...
618 
619     return msStyle;
620 }
621 
SetWindowStyleFlag(long style)622 void wxTextCtrl::SetWindowStyleFlag(long style)
623 {
624     // changing the alignment of the control dynamically works under Win2003
625     // (but not older Windows version: it seems to work under some versions of
626     // XP but not other ones, and we have no way to determine it so be
627     // conservative here) and only for plain EDIT controls (not RICH ones) and
628     // we have to recreate the control to make it always work
629     if ( IsRich() || wxGetWinVersion() < wxWinVersion_2003 )
630     {
631         const long alignMask = wxTE_LEFT | wxTE_CENTRE | wxTE_RIGHT;
632         if ( (style & alignMask) != (GetWindowStyle() & alignMask) )
633         {
634             const wxString value = GetValue();
635             const wxPoint pos = GetPosition();
636             const wxSize size = GetSize();
637 
638             // delete the old window
639             HWND hwnd = GetHwnd();
640             DissociateHandle();
641             ::DestroyWindow(hwnd);
642 
643             // create the new one with the updated flags
644             m_windowStyle = style;
645             MSWCreateText(value, pos, size);
646 
647             // and make sure it has the same attributes as before
648             if ( m_hasFont )
649             {
650                 // calling SetFont(m_font) would do nothing as the code would
651                 // notice that the font didn't change, so force it to believe
652                 // that it did
653                 wxFont font = m_font;
654                 m_font = wxNullFont;
655                 SetFont(font);
656             }
657 
658             if ( m_hasFgCol )
659             {
660                 wxColour colFg = m_foregroundColour;
661                 m_foregroundColour = wxNullColour;
662                 SetForegroundColour(colFg);
663             }
664 
665             if ( m_hasBgCol )
666             {
667                 wxColour colBg = m_backgroundColour;
668                 m_backgroundColour = wxNullColour;
669                 SetBackgroundColour(colBg);
670             }
671 
672             // note that text styles are lost but this is probably not a big
673             // problem: if you use styles, you probably don't use nor change
674             // alignment flags anyhow
675 
676             return;
677         }
678     }
679 
680 #if wxUSE_RICHEDIT
681     // we have to deal with some styles separately because they can't be
682     // changed by simply calling SetWindowLong(GWL_STYLE) but can be changed
683     // using richedit-specific EM_SETOPTIONS
684     if ( IsRich() &&
685             ((style & wxTE_NOHIDESEL) != (GetWindowStyle() & wxTE_NOHIDESEL)) )
686     {
687         bool set = (style & wxTE_NOHIDESEL) != 0;
688 
689         ::SendMessage(GetHwnd(), EM_SETOPTIONS, set ? ECOOP_OR : ECOOP_AND,
690                       set ? ECO_NOHIDESEL : ~ECO_NOHIDESEL);
691     }
692 #endif // wxUSE_RICHEDIT
693 
694     wxControl::SetWindowStyleFlag(style);
695 }
696 
697 // ----------------------------------------------------------------------------
698 // set/get the controls text
699 // ----------------------------------------------------------------------------
700 
IsEmpty() const701 bool wxTextCtrl::IsEmpty() const
702 {
703     // this is an optimization for multiline controls containing a lot of text
704     if ( IsMultiLine() && GetNumberOfLines() != 1 )
705         return false;
706 
707     return wxTextCtrlBase::IsEmpty();
708 }
709 
GetValue() const710 wxString wxTextCtrl::GetValue() const
711 {
712     // range 0..-1 is special for GetRange() and means to retrieve all text
713     return GetRange(0, -1);
714 }
715 
GetRange(long from,long to) const716 wxString wxTextCtrl::GetRange(long from, long to) const
717 {
718     wxString str;
719 
720     if ( from >= to && to != -1 )
721     {
722         // nothing to retrieve
723         return str;
724     }
725 
726 #if wxUSE_RICHEDIT
727     if ( IsRich() )
728     {
729         int len = GetWindowTextLength(GetHwnd());
730         if ( len > from )
731         {
732             if ( to == -1 )
733                 to = len;
734 
735 #if !wxUSE_UNICODE
736             // we must use EM_STREAMOUT if we don't want to lose all characters
737             // not representable in the current character set (EM_GETTEXTRANGE
738             // simply replaces them with question marks...)
739             if ( GetRichVersion() > 1 )
740             {
741                 // we must have some encoding, otherwise any 8bit chars in the
742                 // control are simply *lost* (replaced by '?')
743                 wxFontEncoding encoding = wxFONTENCODING_SYSTEM;
744 
745                 wxFont font = m_defaultStyle.GetFont();
746                 if ( !font.Ok() )
747                     font = GetFont();
748 
749                 if ( font.Ok() )
750                 {
751                    encoding = font.GetEncoding();
752                 }
753 
754 #if wxUSE_INTL
755                 if ( encoding == wxFONTENCODING_SYSTEM )
756                 {
757                     encoding = wxLocale::GetSystemEncoding();
758                 }
759 #endif // wxUSE_INTL
760 
761                 if ( encoding == wxFONTENCODING_SYSTEM )
762                 {
763                     encoding = wxFONTENCODING_ISO8859_1;
764                 }
765 
766                 str = StreamOut(encoding);
767 
768                 if ( !str.empty() )
769                 {
770                     // we have to manually extract the required part, luckily
771                     // this is easy in this case as EOL characters in str are
772                     // just LFs because we remove CRs in wxRichEditStreamOut
773                     str = str.Mid(from, to - from);
774                 }
775             }
776 
777             // StreamOut() wasn't used or failed, try to do it in normal way
778             if ( str.empty() )
779 #endif // !wxUSE_UNICODE
780             {
781                 // alloc one extra WORD as needed by the control
782                 wxStringBuffer tmp(str, ++len);
783                 wxChar *p = tmp;
784 
785                 TEXTRANGE textRange;
786                 textRange.chrg.cpMin = from;
787                 textRange.chrg.cpMax = to;
788                 textRange.lpstrText = p;
789 
790                 (void)::SendMessage(GetHwnd(), EM_GETTEXTRANGE,
791                                     0, (LPARAM)&textRange);
792 
793                 if ( m_verRichEdit > 1 )
794                 {
795                     // RichEdit 2.0 uses just CR ('\r') for the
796                     // newlines which is neither Unix nor Windows
797                     // style - convert it to something reasonable
798                     for ( ; *p; p++ )
799                     {
800                         if ( *p == _T('\r') )
801                             *p = _T('\n');
802                     }
803                 }
804             }
805 
806             if ( m_verRichEdit == 1 )
807             {
808                 // convert to the canonical form - see comment below
809                 str = wxTextFile::Translate(str, wxTextFileType_Unix);
810             }
811         }
812         //else: no text at all, leave the string empty
813     }
814     else
815 #endif // wxUSE_RICHEDIT
816     {
817         // retrieve all text
818         str = wxGetWindowText(GetHWND());
819 
820         // need only a range?
821         if ( from < to )
822         {
823             str = str.Mid(from, to - from);
824         }
825 
826         // WM_GETTEXT uses standard DOS CR+LF (\r\n) convention - convert to the
827         // canonical one (same one as above) for consistency with the other kinds
828         // of controls and, more importantly, with the other ports
829         str = wxTextFile::Translate(str, wxTextFileType_Unix);
830     }
831 
832     return str;
833 }
834 
DoSetValue(const wxString & value,int flags)835 void wxTextCtrl::DoSetValue(const wxString& value, int flags)
836 {
837     // if the text is long enough, it's faster to just set it instead of first
838     // comparing it with the old one (chances are that it will be different
839     // anyhow, this comparison is there to avoid flicker for small single-line
840     // edit controls mostly)
841     if ( (value.length() > 0x400) || (value != GetValue()) )
842     {
843         DoWriteText(value, flags /* doesn't include SelectionOnly here */);
844 
845         // mark the control as being not dirty - we changed its text, not the
846         // user
847         DiscardEdits();
848 
849         // for compatibility, don't move the cursor when doing SetValue()
850         SetInsertionPoint(0);
851     }
852     else // same text
853     {
854         // still reset the modified flag even if the value didn't really change
855         // because now it comes from the program and not the user (and do it
856         // before generating the event so that the event handler could get the
857         // expected value from IsModified())
858         DiscardEdits();
859 
860         // still send an event for consistency
861         if (flags & SetValue_SendEvent)
862             SendUpdateEvent();
863     }
864 }
865 
866 #if wxUSE_RICHEDIT && (!wxUSE_UNICODE || wxUSE_UNICODE_MSLU)
867 
868 // TODO: using memcpy() would improve performance a lot for big amounts of text
869 
870 DWORD CALLBACK
wxRichEditStreamIn(DWORD dwCookie,BYTE * buf,LONG cb,LONG * pcb)871 wxRichEditStreamIn(DWORD dwCookie, BYTE *buf, LONG cb, LONG *pcb)
872 {
873     *pcb = 0;
874 
875     const wchar_t ** const ppws = (const wchar_t **)dwCookie;
876 
877     wchar_t *wbuf = (wchar_t *)buf;
878     const wchar_t *wpc = *ppws;
879     while ( cb && *wpc )
880     {
881         *wbuf++ = *wpc++;
882 
883         cb -= sizeof(wchar_t);
884         (*pcb) += sizeof(wchar_t);
885     }
886 
887     *ppws = wpc;
888 
889     return 0;
890 }
891 
892 // helper struct used to pass parameters from wxTextCtrl to wxRichEditStreamOut
893 struct wxStreamOutData
894 {
895     wchar_t *wpc;
896     size_t len;
897 };
898 
899 DWORD CALLBACK
wxRichEditStreamOut(DWORD_PTR dwCookie,BYTE * buf,LONG cb,LONG * pcb)900 wxRichEditStreamOut(DWORD_PTR dwCookie, BYTE *buf, LONG cb, LONG *pcb)
901 {
902     *pcb = 0;
903 
904     wxStreamOutData *data = (wxStreamOutData *)dwCookie;
905 
906     const wchar_t *wbuf = (const wchar_t *)buf;
907     wchar_t *wpc = data->wpc;
908     while ( cb )
909     {
910         wchar_t wch = *wbuf++;
911 
912         // turn "\r\n" into "\n" on the fly
913         if ( wch != L'\r' )
914             *wpc++ = wch;
915         else
916             data->len--;
917 
918         cb -= sizeof(wchar_t);
919         (*pcb) += sizeof(wchar_t);
920     }
921 
922     data->wpc = wpc;
923 
924     return 0;
925 }
926 
927 
928 #if wxUSE_UNICODE_MSLU
929     #define UNUSED_IF_MSLU(param)
930 #else
931     #define UNUSED_IF_MSLU(param) param
932 #endif
933 
934 bool
StreamIn(const wxString & value,wxFontEncoding UNUSED_IF_MSLU (encoding),bool selectionOnly)935 wxTextCtrl::StreamIn(const wxString& value,
936                      wxFontEncoding UNUSED_IF_MSLU(encoding),
937                      bool selectionOnly)
938 {
939 #if wxUSE_UNICODE_MSLU
940     const wchar_t *wpc = value.c_str();
941 #else // !wxUSE_UNICODE_MSLU
942     wxCSConv conv(encoding);
943 
944     const size_t len = conv.MB2WC(NULL, value, value.length());
945 
946     if (len == wxCONV_FAILED)
947         return false;
948 
949 #if wxUSE_WCHAR_T
950     wxWCharBuffer wchBuf(len); // allocates one extra character
951     wchar_t *wpc = wchBuf.data();
952 #else
953     wchar_t *wchBuf = (wchar_t *)malloc((len + 1)*sizeof(wchar_t));
954     wchar_t *wpc = wchBuf;
955 #endif
956 
957     conv.MB2WC(wpc, value, len + 1);
958 #endif // wxUSE_UNICODE_MSLU
959 
960     // finally, stream it in the control
961     EDITSTREAM eds;
962     wxZeroMemory(eds);
963     eds.dwCookie = (DWORD)&wpc;
964     // the cast below is needed for broken (very) old mingw32 headers
965     eds.pfnCallback = (EDITSTREAMCALLBACK)wxRichEditStreamIn;
966 
967     // same problem as in DoWriteText(): we can get multiple events here
968     UpdatesCountFilter ucf(m_updatesCount);
969 
970     ::SendMessage(GetHwnd(), EM_STREAMIN,
971                   SF_TEXT |
972                   SF_UNICODE |
973                   (selectionOnly ? SFF_SELECTION : 0),
974                   (LPARAM)&eds);
975 
976     // It's okay for EN_UPDATE to not be sent if the selection is empty and
977     // the text is empty, otherwise warn the programmer about it.
978     wxASSERT_MSG( ucf.GotUpdate() || ( !HasSelection() && value.empty() ),
979                   _T("EM_STREAMIN didn't send EN_UPDATE?") );
980 
981     if ( eds.dwError )
982     {
983         wxLogLastError(_T("EM_STREAMIN"));
984     }
985 
986 #if !wxUSE_WCHAR_T
987     free(wchBuf);
988 #endif // !wxUSE_WCHAR_T
989 
990     return true;
991 }
992 
993 #if !wxUSE_UNICODE_MSLU
994 
995 wxString
StreamOut(wxFontEncoding encoding,bool selectionOnly) const996 wxTextCtrl::StreamOut(wxFontEncoding encoding, bool selectionOnly) const
997 {
998     wxString out;
999 
1000     const int len = GetWindowTextLength(GetHwnd());
1001 
1002 #if wxUSE_WCHAR_T
1003     wxWCharBuffer wchBuf(len);
1004     wchar_t *wpc = wchBuf.data();
1005 #else
1006     wchar_t *wchBuf = (wchar_t *)malloc((len + 1)*sizeof(wchar_t));
1007     wchar_t *wpc = wchBuf;
1008 #endif
1009 
1010     wxStreamOutData data;
1011     data.wpc = wpc;
1012     data.len = len;
1013 
1014     EDITSTREAM eds;
1015     wxZeroMemory(eds);
1016     eds.dwCookie = (DWORD)&data;
1017     eds.pfnCallback = wxRichEditStreamOut;
1018 
1019     ::SendMessage
1020       (
1021         GetHwnd(),
1022         EM_STREAMOUT,
1023         SF_TEXT | SF_UNICODE | (selectionOnly ? SFF_SELECTION : 0),
1024         (LPARAM)&eds
1025       );
1026 
1027     if ( eds.dwError )
1028     {
1029         wxLogLastError(_T("EM_STREAMOUT"));
1030     }
1031     else // streamed out ok
1032     {
1033         // NUL-terminate the string because its length could have been
1034         // decreased by wxRichEditStreamOut
1035         *(wchBuf.data() + data.len) = L'\0';
1036 
1037         // now convert to the given encoding (this is a possibly lossful
1038         // conversion but what else can we do)
1039         wxCSConv conv(encoding);
1040         size_t lenNeeded = conv.WC2MB(NULL, wchBuf, 0);
1041 
1042         if ( lenNeeded != wxCONV_FAILED && lenNeeded++ )
1043         {
1044             conv.WC2MB(wxStringBuffer(out, lenNeeded), wchBuf, lenNeeded);
1045         }
1046     }
1047 
1048 #if !wxUSE_WCHAR_T
1049     free(wchBuf);
1050 #endif // !wxUSE_WCHAR_T
1051 
1052     return out;
1053 }
1054 
1055 #endif // !wxUSE_UNICODE_MSLU
1056 
1057 #endif // wxUSE_RICHEDIT
1058 
WriteText(const wxString & value)1059 void wxTextCtrl::WriteText(const wxString& value)
1060 {
1061     DoWriteText(value);
1062 }
1063 
DoWriteText(const wxString & value,int flags)1064 void wxTextCtrl::DoWriteText(const wxString& value, int flags)
1065 {
1066     bool selectionOnly = (flags & SetValue_SelectionOnly) != 0;
1067     wxString valueDos;
1068     if ( m_windowStyle & wxTE_MULTILINE )
1069         valueDos = wxTextFile::Translate(value, wxTextFileType_Dos);
1070     else
1071         valueDos = value;
1072 
1073 #if wxUSE_RICHEDIT
1074     // there are several complications with the rich edit controls here
1075     bool done = false;
1076     if ( IsRich() )
1077     {
1078         // first, ensure that the new text will be in the default style
1079         if ( !m_defaultStyle.IsDefault() )
1080         {
1081             long start, end;
1082             GetSelection(&start, &end);
1083             SetStyle(start, end, m_defaultStyle);
1084         }
1085 
1086 #if wxUSE_UNICODE_MSLU
1087         // RichEdit doesn't have Unicode version of EM_REPLACESEL on Win9x,
1088         // but EM_STREAMIN works
1089         if ( wxUsingUnicowsDll() && GetRichVersion() > 1 )
1090         {
1091            done = StreamIn(valueDos, wxFONTENCODING_SYSTEM, selectionOnly);
1092         }
1093 #endif // wxUSE_UNICODE_MSLU
1094 
1095 #if !wxUSE_UNICODE
1096         // next check if the text we're inserting must be shown in a non
1097         // default charset -- this only works for RichEdit > 1.0
1098         if ( GetRichVersion() > 1 )
1099         {
1100             wxFont font = m_defaultStyle.GetFont();
1101             if ( !font.Ok() )
1102                 font = GetFont();
1103 
1104             if ( font.Ok() )
1105             {
1106                wxFontEncoding encoding = font.GetEncoding();
1107                if ( encoding != wxFONTENCODING_SYSTEM )
1108                {
1109                    // we have to use EM_STREAMIN to force richedit control 2.0+
1110                    // to show any text in the non default charset -- otherwise
1111                    // it thinks it knows better than we do and always shows it
1112                    // in the default one
1113                    done = StreamIn(valueDos, encoding, selectionOnly);
1114                }
1115             }
1116         }
1117 #endif // !wxUSE_UNICODE
1118     }
1119 
1120     if ( !done )
1121 #endif // wxUSE_RICHEDIT
1122     {
1123         // in some cases we get 2 EN_CHANGE notifications after the SendMessage
1124         // call (this happens for plain EDITs with EM_REPLACESEL and under some
1125         // -- undetermined -- conditions with rich edit) and sometimes we don't
1126         // get any events at all (plain EDIT with WM_SETTEXT), so ensure that
1127         // we generate exactly one of them by ignoring all but the first one in
1128         // SendUpdateEvent() and generating one ourselves if we hadn't got any
1129         // notifications from Windows
1130         if ( !(flags & SetValue_SendEvent) )
1131             m_updatesCount = -2;        // suppress any update event
1132 
1133         UpdatesCountFilter ucf(m_updatesCount);
1134 
1135         ::SendMessage(GetHwnd(), selectionOnly ? EM_REPLACESEL : WM_SETTEXT,
1136                       // EM_REPLACESEL takes 1 to indicate the operation should be redoable
1137                       selectionOnly ? 1 : 0, (LPARAM)valueDos.c_str());
1138 
1139         if ( !ucf.GotUpdate() && (flags & SetValue_SendEvent) )
1140         {
1141             SendUpdateEvent();
1142         }
1143     }
1144 }
1145 
AppendText(const wxString & text)1146 void wxTextCtrl::AppendText(const wxString& text)
1147 {
1148     SetInsertionPointEnd();
1149 
1150     WriteText(text);
1151 
1152 #if wxUSE_RICHEDIT
1153     // don't do this if we're frozen, saves some time
1154     if ( !IsFrozen() && IsMultiLine() && GetRichVersion() > 1 )
1155     {
1156         // setting the caret to the end and showing it simply doesn't work for
1157         // RichEdit 2.0 -- force it to still do what we want
1158         ::SendMessage(GetHwnd(), EM_LINESCROLL, 0, GetNumberOfLines());
1159     }
1160 #endif // wxUSE_RICHEDIT
1161 }
1162 
Clear()1163 void wxTextCtrl::Clear()
1164 {
1165     ::SetWindowText(GetHwnd(), wxEmptyString);
1166 
1167 #if wxUSE_RICHEDIT
1168     if ( !IsRich() )
1169 #endif // wxUSE_RICHEDIT
1170     {
1171         // rich edit controls send EN_UPDATE from WM_SETTEXT handler themselves
1172         // but the normal ones don't -- make Clear() behaviour consistent by
1173         // always sending this event
1174 
1175         // Windows already sends an update event for single-line
1176         // controls.
1177         if ( m_windowStyle & wxTE_MULTILINE )
1178             SendUpdateEvent();
1179     }
1180 }
1181 
1182 #ifdef __WIN32__
1183 
EmulateKeyPress(const wxKeyEvent & event)1184 bool wxTextCtrl::EmulateKeyPress(const wxKeyEvent& event)
1185 {
1186     SetFocus();
1187 
1188     size_t lenOld = GetValue().length();
1189 
1190     wxUint32 code = event.GetRawKeyCode();
1191     ::keybd_event((BYTE)code, 0, 0 /* key press */, 0);
1192     ::keybd_event((BYTE)code, 0, KEYEVENTF_KEYUP, 0);
1193 
1194     // assume that any alphanumeric key changes the total number of characters
1195     // in the control - this should work in 99% of cases
1196     return GetValue().length() != lenOld;
1197 }
1198 
1199 #endif // __WIN32__
1200 
1201 // ----------------------------------------------------------------------------
1202 // Clipboard operations
1203 // ----------------------------------------------------------------------------
1204 
Copy()1205 void wxTextCtrl::Copy()
1206 {
1207     if (CanCopy())
1208     {
1209         ::SendMessage(GetHwnd(), WM_COPY, 0, 0L);
1210     }
1211 }
1212 
Cut()1213 void wxTextCtrl::Cut()
1214 {
1215     if (CanCut())
1216     {
1217         ::SendMessage(GetHwnd(), WM_CUT, 0, 0L);
1218     }
1219 }
1220 
Paste()1221 void wxTextCtrl::Paste()
1222 {
1223     if (CanPaste())
1224     {
1225         ::SendMessage(GetHwnd(), WM_PASTE, 0, 0L);
1226     }
1227 }
1228 
HasSelection() const1229 bool wxTextCtrl::HasSelection() const
1230 {
1231     long from, to;
1232     GetSelection(&from, &to);
1233     return from != to;
1234 }
1235 
CanCopy() const1236 bool wxTextCtrl::CanCopy() const
1237 {
1238     // Can copy if there's a selection
1239     return HasSelection();
1240 }
1241 
CanCut() const1242 bool wxTextCtrl::CanCut() const
1243 {
1244     return CanCopy() && IsEditable();
1245 }
1246 
CanPaste() const1247 bool wxTextCtrl::CanPaste() const
1248 {
1249     if ( !IsEditable() )
1250         return false;
1251 
1252 #if wxUSE_RICHEDIT
1253     if ( IsRich() )
1254     {
1255         UINT cf = 0; // 0 == any format
1256 
1257         return ::SendMessage(GetHwnd(), EM_CANPASTE, cf, 0) != 0;
1258     }
1259 #endif // wxUSE_RICHEDIT
1260 
1261     // Standard edit control: check for straight text on clipboard
1262     if ( !::OpenClipboard(GetHwndOf(wxTheApp->GetTopWindow())) )
1263         return false;
1264 
1265     bool isTextAvailable = ::IsClipboardFormatAvailable(CF_TEXT) != 0;
1266     ::CloseClipboard();
1267 
1268     return isTextAvailable;
1269 }
1270 
1271 // ----------------------------------------------------------------------------
1272 // Accessors
1273 // ----------------------------------------------------------------------------
1274 
SetEditable(bool editable)1275 void wxTextCtrl::SetEditable(bool editable)
1276 {
1277     HWND hWnd = GetHwnd();
1278     ::SendMessage(hWnd, EM_SETREADONLY, (WPARAM)!editable, (LPARAM)0L);
1279 }
1280 
SetInsertionPoint(long pos)1281 void wxTextCtrl::SetInsertionPoint(long pos)
1282 {
1283     DoSetSelection(pos, pos);
1284 }
1285 
SetInsertionPointEnd()1286 void wxTextCtrl::SetInsertionPointEnd()
1287 {
1288     // we must not do anything if the caret is already there because calling
1289     // SetInsertionPoint() thaws the controls if Freeze() had been called even
1290     // if it doesn't actually move the caret anywhere and so the simple fact of
1291     // doing it results in horrible flicker when appending big amounts of text
1292     // to the control in a few chunks (see DoAddText() test in the text sample)
1293     const wxTextPos lastPosition = GetLastPosition();
1294     if ( GetInsertionPoint() == lastPosition )
1295     {
1296         return;
1297     }
1298 
1299     long pos;
1300 
1301 #if wxUSE_RICHEDIT
1302     if ( m_verRichEdit == 1 )
1303     {
1304         // we don't have to waste time calling GetLastPosition() in this case
1305         pos = -1;
1306     }
1307     else // !RichEdit 1.0
1308 #endif // wxUSE_RICHEDIT
1309     {
1310         pos = lastPosition;
1311     }
1312 
1313     SetInsertionPoint(pos);
1314 }
1315 
GetInsertionPoint() const1316 long wxTextCtrl::GetInsertionPoint() const
1317 {
1318 #if wxUSE_RICHEDIT
1319     if ( IsRich() )
1320     {
1321         CHARRANGE range;
1322         range.cpMin = 0;
1323         range.cpMax = 0;
1324         ::SendMessage(GetHwnd(), EM_EXGETSEL, 0, (LPARAM) &range);
1325         return range.cpMin;
1326     }
1327 #endif // wxUSE_RICHEDIT
1328 
1329     DWORD Pos = (DWORD)::SendMessage(GetHwnd(), EM_GETSEL, 0, 0L);
1330     return Pos & 0xFFFF;
1331 }
1332 
GetLastPosition() const1333 wxTextPos wxTextCtrl::GetLastPosition() const
1334 {
1335     int numLines = GetNumberOfLines();
1336     long posStartLastLine = XYToPosition(0, numLines - 1);
1337 
1338     long lenLastLine = GetLengthOfLineContainingPos(posStartLastLine);
1339 
1340     return posStartLastLine + lenLastLine;
1341 }
1342 
1343 // If the return values from and to are the same, there is no
1344 // selection.
GetSelection(long * from,long * to) const1345 void wxTextCtrl::GetSelection(long* from, long* to) const
1346 {
1347 #if wxUSE_RICHEDIT
1348     if ( IsRich() )
1349     {
1350         CHARRANGE charRange;
1351         ::SendMessage(GetHwnd(), EM_EXGETSEL, 0, (LPARAM) &charRange);
1352 
1353         *from = charRange.cpMin;
1354         *to = charRange.cpMax;
1355     }
1356     else
1357 #endif // !wxUSE_RICHEDIT
1358     {
1359         DWORD dwStart, dwEnd;
1360         ::SendMessage(GetHwnd(), EM_GETSEL, (WPARAM)&dwStart, (LPARAM)&dwEnd);
1361 
1362         *from = dwStart;
1363         *to = dwEnd;
1364     }
1365 }
1366 
IsEditable() const1367 bool wxTextCtrl::IsEditable() const
1368 {
1369     // strangely enough, we may be called before the control is created: our
1370     // own Create() calls MSWGetStyle() which calls AcceptsFocus() which calls
1371     // us
1372     if ( !m_hWnd )
1373         return true;
1374 
1375     long style = ::GetWindowLong(GetHwnd(), GWL_STYLE);
1376 
1377     return (style & ES_READONLY) == 0;
1378 }
1379 
1380 // ----------------------------------------------------------------------------
1381 // selection
1382 // ----------------------------------------------------------------------------
1383 
SetSelection(long from,long to)1384 void wxTextCtrl::SetSelection(long from, long to)
1385 {
1386     // if from and to are both -1, it means (in wxWidgets) that all text should
1387     // be selected - translate into Windows convention
1388     if ( (from == -1) && (to == -1) )
1389     {
1390         from = 0;
1391         to = -1;
1392     }
1393 
1394     DoSetSelection(from, to);
1395 }
1396 
DoSetSelection(long from,long to,bool scrollCaret)1397 void wxTextCtrl::DoSetSelection(long from, long to, bool scrollCaret)
1398 {
1399     HWND hWnd = GetHwnd();
1400 
1401 #if wxUSE_RICHEDIT
1402     if ( IsRich() )
1403     {
1404         CHARRANGE range;
1405         range.cpMin = from;
1406         range.cpMax = to;
1407         ::SendMessage(hWnd, EM_EXSETSEL, 0, (LPARAM) &range);
1408     }
1409     else
1410 #endif // wxUSE_RICHEDIT
1411     {
1412         ::SendMessage(hWnd, EM_SETSEL, (WPARAM)from, (LPARAM)to);
1413     }
1414 
1415     if ( scrollCaret && !IsFrozen() )
1416     {
1417 #if wxUSE_RICHEDIT
1418         // richedit 3.0 (i.e. the version living in riched20.dll distributed
1419         // with Windows 2000 and beyond) doesn't honour EM_SCROLLCARET when
1420         // emulating richedit 2.0 unless the control has focus or ECO_NOHIDESEL
1421         // option is set (but it does work ok in richedit 1.0 mode...)
1422         //
1423         // so to make it work we either need to give focus to it here which
1424         // will probably create many problems (dummy focus events; window
1425         // containing the text control being brought to foreground
1426         // unexpectedly; ...) or to temporarily set ECO_NOHIDESEL which may
1427         // create other problems too -- and in fact it does because if we turn
1428         // on/off this style while appending the text to the control, the
1429         // vertical scrollbar never appears in it even if we append tons of
1430         // text and to work around this the only solution I found was to use
1431         // ES_DISABLENOSCROLL
1432         //
1433         // this is very ugly but I don't see any other way to make this work
1434         long style = 0;
1435         if ( GetRichVersion() > 1 )
1436         {
1437             if ( !HasFlag(wxTE_NOHIDESEL) )
1438             {
1439                 // setting ECO_NOHIDESEL also sets WS_VISIBLE and possibly
1440                 // others, remember the style so we can reset it later if needed
1441                 style = ::GetWindowLong(GetHwnd(), GWL_STYLE);
1442                 ::SendMessage(GetHwnd(), EM_SETOPTIONS,
1443                               ECOOP_OR, ECO_NOHIDESEL);
1444             }
1445             //else: everything is already ok
1446         }
1447 #endif // wxUSE_RICHEDIT
1448 
1449         ::SendMessage(hWnd, EM_SCROLLCARET, (WPARAM)0, (LPARAM)0);
1450 
1451 #if wxUSE_RICHEDIT
1452         // restore ECO_NOHIDESEL if we changed it
1453         if ( GetRichVersion() > 1 && !HasFlag(wxTE_NOHIDESEL) )
1454         {
1455             ::SendMessage(GetHwnd(), EM_SETOPTIONS,
1456                           ECOOP_AND, ~ECO_NOHIDESEL);
1457             if ( style != ::GetWindowLong(GetHwnd(), GWL_STYLE) )
1458                 ::SetWindowLong(GetHwnd(), GWL_STYLE, style);
1459         }
1460 #endif // wxUSE_RICHEDIT
1461     }
1462 }
1463 
1464 // ----------------------------------------------------------------------------
1465 // Working with files
1466 // ----------------------------------------------------------------------------
1467 
DoLoadFile(const wxString & file,int fileType)1468 bool wxTextCtrl::DoLoadFile(const wxString& file, int fileType)
1469 {
1470     if ( wxTextCtrlBase::DoLoadFile(file, fileType) )
1471     {
1472         // update the size limit if needed
1473         AdjustSpaceLimit();
1474 
1475         return true;
1476     }
1477 
1478     return false;
1479 }
1480 
1481 // ----------------------------------------------------------------------------
1482 // Editing
1483 // ----------------------------------------------------------------------------
1484 
Replace(long from,long to,const wxString & value)1485 void wxTextCtrl::Replace(long from, long to, const wxString& value)
1486 {
1487     // Set selection and remove it
1488     DoSetSelection(from, to, false /* don't scroll caret into view */);
1489 
1490     DoWriteText(value);
1491 }
1492 
Remove(long from,long to)1493 void wxTextCtrl::Remove(long from, long to)
1494 {
1495     Replace(from, to, wxEmptyString);
1496 }
1497 
IsModified() const1498 bool wxTextCtrl::IsModified() const
1499 {
1500     return ::SendMessage(GetHwnd(), EM_GETMODIFY, 0, 0) != 0;
1501 }
1502 
MarkDirty()1503 void wxTextCtrl::MarkDirty()
1504 {
1505     ::SendMessage(GetHwnd(), EM_SETMODIFY, TRUE, 0L);
1506 }
1507 
DiscardEdits()1508 void wxTextCtrl::DiscardEdits()
1509 {
1510     ::SendMessage(GetHwnd(), EM_SETMODIFY, FALSE, 0L);
1511 }
1512 
GetNumberOfLines() const1513 int wxTextCtrl::GetNumberOfLines() const
1514 {
1515     return (int)::SendMessage(GetHwnd(), EM_GETLINECOUNT, (WPARAM)0, (LPARAM)0);
1516 }
1517 
1518 // ----------------------------------------------------------------------------
1519 // Positions <-> coords
1520 // ----------------------------------------------------------------------------
1521 
XYToPosition(long x,long y) const1522 long wxTextCtrl::XYToPosition(long x, long y) const
1523 {
1524     // This gets the char index for the _beginning_ of this line
1525     long charIndex = ::SendMessage(GetHwnd(), EM_LINEINDEX, (WPARAM)y, (LPARAM)0);
1526 
1527     return charIndex + x;
1528 }
1529 
PositionToXY(long pos,long * x,long * y) const1530 bool wxTextCtrl::PositionToXY(long pos, long *x, long *y) const
1531 {
1532     HWND hWnd = GetHwnd();
1533 
1534     // This gets the line number containing the character
1535     long lineNo;
1536 #if wxUSE_RICHEDIT
1537     if ( IsRich() )
1538     {
1539         lineNo = ::SendMessage(hWnd, EM_EXLINEFROMCHAR, 0, (LPARAM)pos);
1540     }
1541     else
1542 #endif // wxUSE_RICHEDIT
1543     {
1544         lineNo = ::SendMessage(hWnd, EM_LINEFROMCHAR, (WPARAM)pos, 0);
1545     }
1546 
1547     if ( lineNo == -1 )
1548     {
1549         // no such line
1550         return false;
1551     }
1552 
1553     // This gets the char index for the _beginning_ of this line
1554     long charIndex = ::SendMessage(hWnd, EM_LINEINDEX, (WPARAM)lineNo, (LPARAM)0);
1555     if ( charIndex == -1 )
1556     {
1557         return false;
1558     }
1559 
1560     // The X position must therefore be the different between pos and charIndex
1561     if ( x )
1562         *x = pos - charIndex;
1563     if ( y )
1564         *y = lineNo;
1565 
1566     return true;
1567 }
1568 
1569 wxTextCtrlHitTestResult
HitTest(const wxPoint & pt,long * posOut) const1570 wxTextCtrl::HitTest(const wxPoint& pt, long *posOut) const
1571 {
1572     // first get the position from Windows
1573     LPARAM lParam;
1574 
1575 #if wxUSE_RICHEDIT
1576     POINTL ptl;
1577     if ( IsRich() )
1578     {
1579         // for rich edit controls the position is passed iva the struct fields
1580         ptl.x = pt.x;
1581         ptl.y = pt.y;
1582         lParam = (LPARAM)&ptl;
1583     }
1584     else
1585 #endif // wxUSE_RICHEDIT
1586     {
1587         // for the plain ones, we are limited to 16 bit positions which are
1588         // combined in a single 32 bit value
1589         lParam = MAKELPARAM(pt.x, pt.y);
1590     }
1591 
1592     LRESULT pos = ::SendMessage(GetHwnd(), EM_CHARFROMPOS, 0, lParam);
1593 
1594     if ( pos == -1 )
1595     {
1596         // this seems to indicate an error...
1597         return wxTE_HT_UNKNOWN;
1598     }
1599 
1600 #if wxUSE_RICHEDIT
1601     if ( !IsRich() )
1602 #endif // wxUSE_RICHEDIT
1603     {
1604         // for plain EDIT controls the higher word contains something else
1605         pos = LOWORD(pos);
1606     }
1607 
1608 
1609     // next determine where it is relatively to our point: EM_CHARFROMPOS
1610     // always returns the closest character but we need to be more precise, so
1611     // double check that we really are where it pretends
1612     POINTL ptReal;
1613 
1614 #if wxUSE_RICHEDIT
1615     // FIXME: we need to distinguish between richedit 2 and 3 here somehow but
1616     //        we don't know how to do it
1617     if ( IsRich() )
1618     {
1619         ::SendMessage(GetHwnd(), EM_POSFROMCHAR, (WPARAM)&ptReal, pos);
1620     }
1621     else
1622 #endif // wxUSE_RICHEDIT
1623     {
1624         LRESULT lRc = ::SendMessage(GetHwnd(), EM_POSFROMCHAR, pos, 0);
1625 
1626         if ( lRc == -1 )
1627         {
1628             // this is apparently returned when pos corresponds to the last
1629             // position
1630             ptReal.x =
1631             ptReal.y = 0;
1632         }
1633         else
1634         {
1635             ptReal.x = LOWORD(lRc);
1636             ptReal.y = HIWORD(lRc);
1637         }
1638     }
1639 
1640     wxTextCtrlHitTestResult rc;
1641 
1642     if ( pt.y > ptReal.y + GetCharHeight() )
1643         rc = wxTE_HT_BELOW;
1644     else if ( pt.x > ptReal.x + GetCharWidth() )
1645         rc = wxTE_HT_BEYOND;
1646     else
1647         rc = wxTE_HT_ON_TEXT;
1648 
1649     if ( posOut )
1650         *posOut = pos;
1651 
1652     return rc;
1653 }
1654 
1655 // ----------------------------------------------------------------------------
1656 //
1657 // ----------------------------------------------------------------------------
1658 
ShowPosition(long pos)1659 void wxTextCtrl::ShowPosition(long pos)
1660 {
1661     HWND hWnd = GetHwnd();
1662 
1663     // To scroll to a position, we pass the number of lines and characters
1664     // to scroll *by*. This means that we need to:
1665     // (1) Find the line position of the current line.
1666     // (2) Find the line position of pos.
1667     // (3) Scroll by (pos - current).
1668     // For now, ignore the horizontal scrolling.
1669 
1670     // Is this where scrolling is relative to - the line containing the caret?
1671     // Or is the first visible line??? Try first visible line.
1672 //    int currentLineLineNo1 = (int)::SendMessage(hWnd, EM_LINEFROMCHAR, (WPARAM)-1, (LPARAM)0L);
1673 
1674     int currentLineLineNo = (int)::SendMessage(hWnd, EM_GETFIRSTVISIBLELINE, (WPARAM)0, (LPARAM)0L);
1675 
1676     int specifiedLineLineNo = (int)::SendMessage(hWnd, EM_LINEFROMCHAR, (WPARAM)pos, (LPARAM)0L);
1677 
1678     int linesToScroll = specifiedLineLineNo - currentLineLineNo;
1679 
1680     if (linesToScroll != 0)
1681       (void)::SendMessage(hWnd, EM_LINESCROLL, (WPARAM)0, (LPARAM)linesToScroll);
1682 }
1683 
GetLengthOfLineContainingPos(long pos) const1684 long wxTextCtrl::GetLengthOfLineContainingPos(long pos) const
1685 {
1686     return ::SendMessage(GetHwnd(), EM_LINELENGTH, (WPARAM)pos, 0);
1687 }
1688 
GetLineLength(long lineNo) const1689 int wxTextCtrl::GetLineLength(long lineNo) const
1690 {
1691     long pos = XYToPosition(0, lineNo);
1692 
1693     return GetLengthOfLineContainingPos(pos);
1694 }
1695 
GetLineText(long lineNo) const1696 wxString wxTextCtrl::GetLineText(long lineNo) const
1697 {
1698     size_t len = (size_t)GetLineLength(lineNo) + 1;
1699 
1700     // there must be at least enough place for the length WORD in the
1701     // buffer
1702     len += sizeof(WORD);
1703 
1704     wxString str;
1705     {
1706         wxStringBufferLength tmp(str, len);
1707         wxChar *buf = tmp;
1708 
1709         *(WORD *)buf = (WORD)len;
1710         len = (size_t)::SendMessage(GetHwnd(), EM_GETLINE, lineNo, (LPARAM)buf);
1711 
1712 #if wxUSE_RICHEDIT
1713         if ( IsRich() )
1714         {
1715             // remove the '\r' returned by the rich edit control, the user code
1716             // should never see it
1717             if ( buf[len - 2] == _T('\r') && buf[len - 1] == _T('\n') )
1718             {
1719                 // richedit 1.0 uses "\r\n" as line terminator, so remove "\r"
1720                 // here and "\n" below
1721                 buf[len - 2] = _T('\n');
1722                 len--;
1723             }
1724             else if ( buf[len - 1] == _T('\r') )
1725             {
1726                 // richedit 2.0+ uses only "\r", replace it with "\n"
1727                 buf[len - 1] = _T('\n');
1728             }
1729         }
1730 #endif // wxUSE_RICHEDIT
1731 
1732         // remove the '\n' at the end, if any (this is how this function is
1733         // supposed to work according to the docs)
1734         if ( buf[len - 1] == _T('\n') )
1735         {
1736             len--;
1737         }
1738 
1739         buf[len] = 0;
1740         tmp.SetLength(len);
1741     }
1742 
1743     return str;
1744 }
1745 
SetMaxLength(unsigned long len)1746 void wxTextCtrl::SetMaxLength(unsigned long len)
1747 {
1748 #if wxUSE_RICHEDIT
1749     if ( IsRich() )
1750     {
1751         ::SendMessage(GetHwnd(), EM_EXLIMITTEXT, 0, len ? len : 0x7fffffff);
1752     }
1753     else
1754 #endif // wxUSE_RICHEDIT
1755     {
1756         if ( len >= 0xffff )
1757         {
1758             // this will set it to a platform-dependent maximum (much more
1759             // than 64Kb under NT)
1760             len = 0;
1761         }
1762 
1763         ::SendMessage(GetHwnd(), EM_LIMITTEXT, len, 0);
1764     }
1765 }
1766 
1767 // ----------------------------------------------------------------------------
1768 // Undo/redo
1769 // ----------------------------------------------------------------------------
1770 
Undo()1771 void wxTextCtrl::Undo()
1772 {
1773     if (CanUndo())
1774     {
1775         ::SendMessage(GetHwnd(), EM_UNDO, 0, 0);
1776     }
1777 }
1778 
Redo()1779 void wxTextCtrl::Redo()
1780 {
1781     if (CanRedo())
1782     {
1783 #if wxUSE_RICHEDIT
1784         if (GetRichVersion() > 1)
1785             ::SendMessage(GetHwnd(), EM_REDO, 0, 0);
1786         else
1787 #endif
1788         // Same as Undo, since Undo undoes the undo, i.e. a redo.
1789         ::SendMessage(GetHwnd(), EM_UNDO, 0, 0);
1790     }
1791 }
1792 
CanUndo() const1793 bool wxTextCtrl::CanUndo() const
1794 {
1795     return ::SendMessage(GetHwnd(), EM_CANUNDO, 0, 0) != 0;
1796 }
1797 
CanRedo() const1798 bool wxTextCtrl::CanRedo() const
1799 {
1800 #if wxUSE_RICHEDIT
1801     if (GetRichVersion() > 1)
1802         return ::SendMessage(GetHwnd(), EM_CANREDO, 0, 0) != 0;
1803     else
1804 #endif
1805     return ::SendMessage(GetHwnd(), EM_CANUNDO, 0, 0) != 0;
1806 }
1807 
1808 // ----------------------------------------------------------------------------
1809 // caret handling (Windows only)
1810 // ----------------------------------------------------------------------------
1811 
ShowNativeCaret(bool show)1812 bool wxTextCtrl::ShowNativeCaret(bool show)
1813 {
1814     if ( show != m_isNativeCaretShown )
1815     {
1816         if ( !(show ? ::ShowCaret(GetHwnd()) : ::HideCaret(GetHwnd())) )
1817         {
1818             // not an error, may simply indicate that it's not shown/hidden
1819             // yet (i.e. it had been hidden/showh 2 times before)
1820             return false;
1821         }
1822 
1823         m_isNativeCaretShown = show;
1824     }
1825 
1826     return true;
1827 }
1828 
1829 // ----------------------------------------------------------------------------
1830 // implemenation details
1831 // ----------------------------------------------------------------------------
1832 
Command(wxCommandEvent & event)1833 void wxTextCtrl::Command(wxCommandEvent & event)
1834 {
1835     SetValue(event.GetString());
1836     ProcessCommand (event);
1837 }
1838 
OnDropFiles(wxDropFilesEvent & event)1839 void wxTextCtrl::OnDropFiles(wxDropFilesEvent& event)
1840 {
1841     // By default, load the first file into the text window.
1842     if (event.GetNumberOfFiles() > 0)
1843     {
1844         LoadFile(event.GetFiles()[0]);
1845     }
1846 }
1847 
1848 // ----------------------------------------------------------------------------
1849 // kbd input processing
1850 // ----------------------------------------------------------------------------
1851 
MSWShouldPreProcessMessage(WXMSG * msg)1852 bool wxTextCtrl::MSWShouldPreProcessMessage(WXMSG* msg)
1853 {
1854     // check for our special keys here: if we don't do it and the parent frame
1855     // uses them as accelerators, they wouldn't work at all, so we disable
1856     // usual preprocessing for them
1857     if ( msg->message == WM_KEYDOWN )
1858     {
1859         const WPARAM vkey = msg->wParam;
1860         if ( HIWORD(msg->lParam) & KF_ALTDOWN )
1861         {
1862             // Alt-Backspace is accelerator for "Undo"
1863             if ( vkey == VK_BACK )
1864                 return false;
1865         }
1866         else // no Alt
1867         {
1868             // we want to process some Ctrl-foo and Shift-bar but no key
1869             // combinations without either Ctrl or Shift nor with both of them
1870             // pressed
1871             const int ctrl = wxIsCtrlDown(),
1872                       shift = wxIsShiftDown();
1873             switch ( ctrl + shift )
1874             {
1875                 default:
1876                     wxFAIL_MSG( _T("how many modifiers have we got?") );
1877                     // fall through
1878 
1879                 case 0:
1880                     if ( IsMultiLine() && vkey == VK_RETURN )
1881                         return false;
1882                     // fall through
1883                 case 2:
1884                     break;
1885 
1886                 case 1:
1887                     // either Ctrl or Shift pressed
1888                     if ( ctrl )
1889                     {
1890                         switch ( vkey )
1891                         {
1892                             case 'C':
1893                             case 'V':
1894                             case 'X':
1895                             case VK_INSERT:
1896                             case VK_DELETE:
1897                             case VK_HOME:
1898                             case VK_END:
1899                                 return false;
1900                         }
1901                     }
1902                     else // Shift is pressed
1903                     {
1904                         if ( vkey == VK_INSERT || vkey == VK_DELETE )
1905                             return false;
1906                     }
1907             }
1908         }
1909     }
1910 
1911     return wxControl::MSWShouldPreProcessMessage(msg);
1912 }
1913 
OnChar(wxKeyEvent & event)1914 void wxTextCtrl::OnChar(wxKeyEvent& event)
1915 {
1916     switch ( event.GetKeyCode() )
1917     {
1918         case WXK_RETURN:
1919             {
1920                 wxCommandEvent event(wxEVT_COMMAND_TEXT_ENTER, m_windowId);
1921                 InitCommandEvent(event);
1922                 event.SetString(GetValue());
1923                 if ( GetEventHandler()->ProcessEvent(event) )
1924                 if ( !HasFlag(wxTE_MULTILINE) )
1925                     return;
1926                 //else: multiline controls need Enter for themselves
1927             }
1928             break;
1929 
1930         case WXK_TAB:
1931             // ok, so this is getting absolutely ridiculous but I don't see
1932             // any other way to fix this bug: when a multiline text control is
1933             // inside a wxFrame, we need to generate the navigation event as
1934             // otherwise nothing happens at all, but when the same control is
1935             // created inside a dialog, IsDialogMessage() *does* switch focus
1936             // all by itself and so if we do it here as well, it is advanced
1937             // twice and goes to the next control... to prevent this from
1938             // happening we're doing this ugly check, the logic being that if
1939             // we don't have focus then it had been already changed to the next
1940             // control
1941             //
1942             // the right thing to do would, of course, be to understand what
1943             // the hell is IsDialogMessage() doing but this is beyond my feeble
1944             // forces at the moment unfortunately
1945             if ( !(m_windowStyle & wxTE_PROCESS_TAB))
1946             {
1947                 if ( FindFocus() == this )
1948                 {
1949                     int flags = 0;
1950                     if (!event.ShiftDown())
1951                         flags |= wxNavigationKeyEvent::IsForward ;
1952                     if (event.ControlDown())
1953                         flags |= wxNavigationKeyEvent::WinChange ;
1954                     if (Navigate(flags))
1955                         return;
1956                 }
1957             }
1958             else
1959             {
1960                 // Insert tab since calling the default Windows handler
1961                 // doesn't seem to do it
1962                 WriteText(wxT("\t"));
1963                 return;
1964             }
1965             break;
1966     }
1967 
1968     // no, we didn't process it
1969     event.Skip();
1970 }
1971 
OnKeyDown(wxKeyEvent & event)1972 void wxTextCtrl::OnKeyDown(wxKeyEvent& event)
1973 {
1974     // richedit control doesn't send WM_PASTE, WM_CUT and WM_COPY messages
1975     // when Ctrl-V, X or C is pressed and this prevents wxClipboardTextEvent
1976     // from working. So we work around it by intercepting these shortcuts
1977     // ourselves and emitting clipboard events (which richedit will handle,
1978     // so everything works as before, including pasting of rich text):
1979     if ( event.GetModifiers() == wxMOD_CONTROL && IsRich() )
1980     {
1981         switch ( event.GetKeyCode() )
1982         {
1983             case 'C':
1984                 Copy();
1985                 return;
1986             case 'X':
1987                 Cut();
1988                 return;
1989             case 'V':
1990                 Paste();
1991                 return;
1992             default:
1993                 break;
1994         }
1995     }
1996 
1997     // no, we didn't process it
1998     event.Skip();
1999 }
2000 
MSWWindowProc(WXUINT nMsg,WXWPARAM wParam,WXLPARAM lParam)2001 WXLRESULT wxTextCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
2002 {
2003     // we must handle clipboard events before calling MSWWindowProc, otherwise
2004     // the event would be handled twice if there's a handler for it in user
2005     // code:
2006     switch ( nMsg )
2007     {
2008         case WM_CUT:
2009         case WM_COPY:
2010         case WM_PASTE:
2011             if ( HandleClipboardEvent(nMsg) )
2012                 return 0;
2013             break;
2014     }
2015 
2016     WXLRESULT lRc = wxTextCtrlBase::MSWWindowProc(nMsg, wParam, lParam);
2017 
2018     switch ( nMsg )
2019     {
2020         case WM_GETDLGCODE:
2021             {
2022                 // we always want the chars and the arrows: the arrows for
2023                 // navigation and the chars because we want Ctrl-C to work even
2024                 // in a read only control
2025                 long lDlgCode = DLGC_WANTCHARS | DLGC_WANTARROWS;
2026 
2027                 if ( IsEditable() )
2028                 {
2029                     // we may have several different cases:
2030                     // 1. normal: both TAB and ENTER are used for navigation
2031                     // 2. ctrl wants TAB for itself: ENTER is used to pass to
2032                     //    the next control in the dialog
2033                     // 3. ctrl wants ENTER for itself: TAB is used for dialog
2034                     //    navigation
2035                     // 4. ctrl wants both TAB and ENTER: Ctrl-ENTER is used to
2036                     //    go to the next control (we need some way to do it)
2037 
2038                     // multiline controls should always get ENTER for themselves
2039                     if ( HasFlag(wxTE_PROCESS_ENTER) || HasFlag(wxTE_MULTILINE) )
2040                         lDlgCode |= DLGC_WANTMESSAGE;
2041 
2042                     if ( HasFlag(wxTE_PROCESS_TAB) )
2043                         lDlgCode |= DLGC_WANTTAB;
2044 
2045                     lRc |= lDlgCode;
2046                 }
2047                 else // !editable
2048                 {
2049                     // NB: use "=", not "|=" as the base class version returns
2050                     //     the same flags is this state as usual (i.e.
2051                     //     including DLGC_WANTMESSAGE). This is strange (how
2052                     //     does it work in the native Win32 apps?) but for now
2053                     //     live with it.
2054                     lRc = lDlgCode;
2055                 }
2056             }
2057             break;
2058     }
2059 
2060     return lRc;
2061 }
2062 
2063 // ----------------------------------------------------------------------------
2064 // text control event processing
2065 // ----------------------------------------------------------------------------
2066 
SendUpdateEvent()2067 bool wxTextCtrl::SendUpdateEvent()
2068 {
2069     switch ( m_updatesCount )
2070     {
2071         case 0:
2072             // remember that we've got an update
2073             m_updatesCount++;
2074             break;
2075 
2076         case 1:
2077             // we had already sent one event since the last control modification
2078             return false;
2079 
2080         default:
2081             wxFAIL_MSG( _T("unexpected wxTextCtrl::m_updatesCount value") );
2082             // fall through
2083 
2084         case -1:
2085             // we hadn't updated the control ourselves, this event comes from
2086             // the user, don't need to ignore it nor update the count
2087             break;
2088 
2089         case -2:
2090             // the control was updated programmatically and we do NOT want to
2091             // send events
2092             return false;
2093     }
2094 
2095     wxCommandEvent event(wxEVT_COMMAND_TEXT_UPDATED, GetId());
2096     InitCommandEvent(event);
2097 
2098     return ProcessCommand(event);
2099 }
2100 
MSWCommand(WXUINT param,WXWORD WXUNUSED (id))2101 bool wxTextCtrl::MSWCommand(WXUINT param, WXWORD WXUNUSED(id))
2102 {
2103     switch ( param )
2104     {
2105         case EN_SETFOCUS:
2106         case EN_KILLFOCUS:
2107             {
2108                 wxFocusEvent event(param == EN_KILLFOCUS ? wxEVT_KILL_FOCUS
2109                                                          : wxEVT_SET_FOCUS,
2110                                    m_windowId);
2111                 event.SetEventObject(this);
2112                 GetEventHandler()->ProcessEvent(event);
2113             }
2114             break;
2115 
2116         case EN_CHANGE:
2117             SendUpdateEvent();
2118             break;
2119 
2120         case EN_MAXTEXT:
2121             // the text size limit has been hit -- try to increase it
2122             if ( !AdjustSpaceLimit() )
2123             {
2124                 wxCommandEvent event(wxEVT_COMMAND_TEXT_MAXLEN, m_windowId);
2125                 InitCommandEvent(event);
2126                 event.SetString(GetValue());
2127                 ProcessCommand(event);
2128             }
2129             break;
2130 
2131             // the other edit notification messages are not processed
2132         default:
2133             return false;
2134     }
2135 
2136     // processed
2137     return true;
2138 }
2139 
MSWControlColor(WXHDC hDC,WXHWND hWnd)2140 WXHBRUSH wxTextCtrl::MSWControlColor(WXHDC hDC, WXHWND hWnd)
2141 {
2142     if ( !IsEnabled() && !HasFlag(wxTE_MULTILINE) )
2143         return MSWControlColorDisabled(hDC);
2144 
2145     return wxTextCtrlBase::MSWControlColor(hDC, hWnd);
2146 }
2147 
HasSpaceLimit(unsigned int * len) const2148 bool wxTextCtrl::HasSpaceLimit(unsigned int *len) const
2149 {
2150     // HACK: we try to automatically extend the limit for the amount of text
2151     //       to allow (interactively) entering more than 64Kb of text under
2152     //       Win9x but we shouldn't reset the text limit which was previously
2153     //       set explicitly with SetMaxLength()
2154     //
2155     //       Unfortunately there is no EM_GETLIMITTEXTSETBYUSER and so we don't
2156     //       know the limit we set (if any). We could solve this by storing the
2157     //       limit we set in wxTextCtrl but to save space we prefer to simply
2158     //       test here the actual limit value: we consider that SetMaxLength()
2159     //       can only be called for small values while EN_MAXTEXT is only sent
2160     //       for large values (in practice the default limit seems to be 30000
2161     //       but make it smaller just to be on the safe side)
2162     *len = ::SendMessage(GetHwnd(), EM_GETLIMITTEXT, 0, 0);
2163     return *len < 10001;
2164 
2165 }
2166 
AdjustSpaceLimit()2167 bool wxTextCtrl::AdjustSpaceLimit()
2168 {
2169     unsigned int limit;
2170     if ( HasSpaceLimit(&limit) )
2171         return false;
2172 
2173     unsigned int len = ::GetWindowTextLength(GetHwnd());
2174     if ( len >= limit )
2175     {
2176         // increment in 32Kb chunks
2177         SetMaxLength(len + 0x8000);
2178     }
2179 
2180     // we changed the limit
2181     return true;
2182 }
2183 
AcceptsFocus() const2184 bool wxTextCtrl::AcceptsFocus() const
2185 {
2186     // we don't want focus if we can't be edited unless we're a multiline
2187     // control because then it might be still nice to get focus from keyboard
2188     // to be able to scroll it without mouse
2189     return (IsEditable() || IsMultiLine()) && wxControl::AcceptsFocus();
2190 }
2191 
DoGetBestSize() const2192 wxSize wxTextCtrl::DoGetBestSize() const
2193 {
2194     int cx, cy;
2195     wxGetCharSize(GetHWND(), &cx, &cy, GetFont());
2196 
2197     int wText = DEFAULT_ITEM_WIDTH;
2198 
2199     int hText = cy;
2200     if ( m_windowStyle & wxTE_MULTILINE )
2201     {
2202         hText *= wxMax(wxMin(GetNumberOfLines(), 10), 2);
2203     }
2204     //else: for single line control everything is ok
2205 
2206     // we have to add the adjustments for the control height only once, not
2207     // once per line, so do it after multiplication above
2208     hText += EDIT_HEIGHT_FROM_CHAR_HEIGHT(cy) - cy;
2209 
2210     return wxSize(wText, hText);
2211 }
2212 
2213 // ----------------------------------------------------------------------------
2214 // standard handlers for standard edit menu events
2215 // ----------------------------------------------------------------------------
2216 
OnCut(wxCommandEvent & WXUNUSED (event))2217 void wxTextCtrl::OnCut(wxCommandEvent& WXUNUSED(event))
2218 {
2219     Cut();
2220 }
2221 
OnCopy(wxCommandEvent & WXUNUSED (event))2222 void wxTextCtrl::OnCopy(wxCommandEvent& WXUNUSED(event))
2223 {
2224     Copy();
2225 }
2226 
OnPaste(wxCommandEvent & WXUNUSED (event))2227 void wxTextCtrl::OnPaste(wxCommandEvent& WXUNUSED(event))
2228 {
2229     Paste();
2230 }
2231 
OnUndo(wxCommandEvent & WXUNUSED (event))2232 void wxTextCtrl::OnUndo(wxCommandEvent& WXUNUSED(event))
2233 {
2234     Undo();
2235 }
2236 
OnRedo(wxCommandEvent & WXUNUSED (event))2237 void wxTextCtrl::OnRedo(wxCommandEvent& WXUNUSED(event))
2238 {
2239     Redo();
2240 }
2241 
OnDelete(wxCommandEvent & WXUNUSED (event))2242 void wxTextCtrl::OnDelete(wxCommandEvent& WXUNUSED(event))
2243 {
2244     long from, to;
2245     GetSelection(& from, & to);
2246     if (from != -1 && to != -1)
2247         Remove(from, to);
2248 }
2249 
OnSelectAll(wxCommandEvent & WXUNUSED (event))2250 void wxTextCtrl::OnSelectAll(wxCommandEvent& WXUNUSED(event))
2251 {
2252     SetSelection(-1, -1);
2253 }
2254 
OnUpdateCut(wxUpdateUIEvent & event)2255 void wxTextCtrl::OnUpdateCut(wxUpdateUIEvent& event)
2256 {
2257     event.Enable( CanCut() );
2258 }
2259 
OnUpdateCopy(wxUpdateUIEvent & event)2260 void wxTextCtrl::OnUpdateCopy(wxUpdateUIEvent& event)
2261 {
2262     event.Enable( CanCopy() );
2263 }
2264 
OnUpdatePaste(wxUpdateUIEvent & event)2265 void wxTextCtrl::OnUpdatePaste(wxUpdateUIEvent& event)
2266 {
2267     event.Enable( CanPaste() );
2268 }
2269 
OnUpdateUndo(wxUpdateUIEvent & event)2270 void wxTextCtrl::OnUpdateUndo(wxUpdateUIEvent& event)
2271 {
2272     event.Enable( CanUndo() );
2273 }
2274 
OnUpdateRedo(wxUpdateUIEvent & event)2275 void wxTextCtrl::OnUpdateRedo(wxUpdateUIEvent& event)
2276 {
2277     event.Enable( CanRedo() );
2278 }
2279 
OnUpdateDelete(wxUpdateUIEvent & event)2280 void wxTextCtrl::OnUpdateDelete(wxUpdateUIEvent& event)
2281 {
2282     long from, to;
2283     GetSelection(& from, & to);
2284     event.Enable(from != -1 && to != -1 && from != to && IsEditable()) ;
2285 }
2286 
OnUpdateSelectAll(wxUpdateUIEvent & event)2287 void wxTextCtrl::OnUpdateSelectAll(wxUpdateUIEvent& event)
2288 {
2289     event.Enable(GetLastPosition() > 0);
2290 }
2291 
OnContextMenu(wxContextMenuEvent & event)2292 void wxTextCtrl::OnContextMenu(wxContextMenuEvent& event)
2293 {
2294 #if wxUSE_RICHEDIT
2295     if (IsRich())
2296     {
2297         if (!m_privateContextMenu)
2298         {
2299             m_privateContextMenu = new wxMenu;
2300             m_privateContextMenu->Append(wxID_UNDO, _("&Undo"));
2301             m_privateContextMenu->Append(wxID_REDO, _("&Redo"));
2302             m_privateContextMenu->AppendSeparator();
2303             m_privateContextMenu->Append(wxID_CUT, _("Cu&t"));
2304             m_privateContextMenu->Append(wxID_COPY, _("&Copy"));
2305             m_privateContextMenu->Append(wxID_PASTE, _("&Paste"));
2306             m_privateContextMenu->Append(wxID_CLEAR, _("&Delete"));
2307             m_privateContextMenu->AppendSeparator();
2308             m_privateContextMenu->Append(wxID_SELECTALL, _("Select &All"));
2309         }
2310         PopupMenu(m_privateContextMenu);
2311         return;
2312     }
2313     else
2314 #endif
2315     event.Skip();
2316 }
2317 
OnSetFocus(wxFocusEvent & event)2318 void wxTextCtrl::OnSetFocus(wxFocusEvent& event)
2319 {
2320     // be sure the caret remains invisible if the user had hidden it
2321     if ( !m_isNativeCaretShown )
2322     {
2323         ::HideCaret(GetHwnd());
2324     }
2325 
2326     event.Skip();
2327 }
2328 
2329 // ----------------------------------------------------------------------------
2330 // Default colors for MSW text control
2331 //
2332 // Set default background color to the native white instead of
2333 // the default wxSYS_COLOUR_BTNFACE (is triggered with wxNullColour).
2334 // ----------------------------------------------------------------------------
2335 
GetDefaultAttributes() const2336 wxVisualAttributes wxTextCtrl::GetDefaultAttributes() const
2337 {
2338     wxVisualAttributes attrs;
2339     attrs.font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
2340     attrs.colFg = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
2341     attrs.colBg = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); //white
2342 
2343     return attrs;
2344 }
2345 
2346 // the rest of the file only deals with the rich edit controls
2347 #if wxUSE_RICHEDIT
2348 
2349 // ----------------------------------------------------------------------------
2350 // EN_LINK processing
2351 // ----------------------------------------------------------------------------
2352 
MSWOnNotify(int idCtrl,WXLPARAM lParam,WXLPARAM * result)2353 bool wxTextCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
2354 {
2355     NMHDR *hdr = (NMHDR* )lParam;
2356     switch ( hdr->code )
2357     {
2358        case EN_MSGFILTER:
2359             {
2360                 const MSGFILTER *msgf = (MSGFILTER *)lParam;
2361                 UINT msg = msgf->msg;
2362 
2363                 // this is a bit crazy but richedit 1.0 sends us all mouse
2364                 // events _except_ WM_LBUTTONUP (don't ask me why) so we have
2365                 // generate the wxWin events for this message manually
2366                 //
2367                 // NB: in fact, this is still not totally correct as it does
2368                 //     send us WM_LBUTTONUP if the selection was cleared by the
2369                 //     last click -- so currently we get 2 events in this case,
2370                 //     but as I don't see any obvious way to check for this I
2371                 //     leave this code in place because it's still better than
2372                 //     not getting left up events at all
2373                 if ( msg == WM_LBUTTONUP )
2374                 {
2375                     WXUINT flags = msgf->wParam;
2376                     int x = GET_X_LPARAM(msgf->lParam),
2377                         y = GET_Y_LPARAM(msgf->lParam);
2378 
2379                     HandleMouseEvent(msg, x, y, flags);
2380                 }
2381             }
2382 
2383             // return true to process the event (and false to ignore it)
2384             return true;
2385 
2386         case EN_LINK:
2387             {
2388                 const ENLINK *enlink = (ENLINK *)hdr;
2389 
2390                 switch ( enlink->msg )
2391                 {
2392                     case WM_SETCURSOR:
2393                         // ok, so it is hardcoded - do we really nee to
2394                         // customize it?
2395                         {
2396                             wxCursor cur(wxCURSOR_HAND);
2397                             ::SetCursor(GetHcursorOf(cur));
2398                             *result = TRUE;
2399                             break;
2400                         }
2401 
2402                     case WM_MOUSEMOVE:
2403                     case WM_LBUTTONDOWN:
2404                     case WM_LBUTTONUP:
2405                     case WM_LBUTTONDBLCLK:
2406                     case WM_RBUTTONDOWN:
2407                     case WM_RBUTTONUP:
2408                     case WM_RBUTTONDBLCLK:
2409                         // send a mouse event
2410                         {
2411                             static const wxEventType eventsMouse[] =
2412                             {
2413                                 wxEVT_MOTION,
2414                                 wxEVT_LEFT_DOWN,
2415                                 wxEVT_LEFT_UP,
2416                                 wxEVT_LEFT_DCLICK,
2417                                 wxEVT_RIGHT_DOWN,
2418                                 wxEVT_RIGHT_UP,
2419                                 wxEVT_RIGHT_DCLICK,
2420                             };
2421 
2422                             // the event ids are consecutive
2423                             wxMouseEvent
2424                                 evtMouse(eventsMouse[enlink->msg - WM_MOUSEMOVE]);
2425 
2426                             InitMouseEvent(evtMouse,
2427                                            GET_X_LPARAM(enlink->lParam),
2428                                            GET_Y_LPARAM(enlink->lParam),
2429                                            enlink->wParam);
2430 
2431                             wxTextUrlEvent event(m_windowId, evtMouse,
2432                                                  enlink->chrg.cpMin,
2433                                                  enlink->chrg.cpMax);
2434 
2435                             InitCommandEvent(event);
2436 
2437                             *result = ProcessCommand(event);
2438                         }
2439                         break;
2440                 }
2441             }
2442             return true;
2443     }
2444 
2445     // not processed, leave it to the base class
2446     return wxTextCtrlBase::MSWOnNotify(idCtrl, lParam, result);
2447 }
2448 
2449 // ----------------------------------------------------------------------------
2450 // colour setting for the rich edit controls
2451 // ----------------------------------------------------------------------------
2452 
SetBackgroundColour(const wxColour & colour)2453 bool wxTextCtrl::SetBackgroundColour(const wxColour& colour)
2454 {
2455     if ( !wxTextCtrlBase::SetBackgroundColour(colour) )
2456     {
2457         // colour didn't really change
2458         return false;
2459     }
2460 
2461     if ( IsRich() )
2462     {
2463         // rich edit doesn't use WM_CTLCOLOR, hence we need to send
2464         // EM_SETBKGNDCOLOR additionally
2465         ::SendMessage(GetHwnd(), EM_SETBKGNDCOLOR, 0, wxColourToRGB(colour));
2466     }
2467 
2468     return true;
2469 }
2470 
SetForegroundColour(const wxColour & colour)2471 bool wxTextCtrl::SetForegroundColour(const wxColour& colour)
2472 {
2473     if ( !wxTextCtrlBase::SetForegroundColour(colour) )
2474     {
2475         // colour didn't really change
2476         return false;
2477     }
2478 
2479     if ( IsRich() )
2480     {
2481         // change the colour of everything
2482         CHARFORMAT cf;
2483         wxZeroMemory(cf);
2484         cf.cbSize = sizeof(cf);
2485         cf.dwMask = CFM_COLOR;
2486         cf.crTextColor = wxColourToRGB(colour);
2487         ::SendMessage(GetHwnd(), EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
2488     }
2489 
2490     return true;
2491 }
2492 
2493 // ----------------------------------------------------------------------------
2494 // styling support for rich edit controls
2495 // ----------------------------------------------------------------------------
2496 
2497 #if wxUSE_RICHEDIT
2498 
SetStyle(long start,long end,const wxTextAttr & style)2499 bool wxTextCtrl::SetStyle(long start, long end, const wxTextAttr& style)
2500 {
2501     if ( !IsRich() )
2502     {
2503         // can't do it with normal text control
2504         return false;
2505     }
2506 
2507     // the richedit 1.0 doesn't handle setting background colour, so don't
2508     // even try to do anything if it's the only thing we want to change
2509     if ( m_verRichEdit == 1 && !style.HasFont() && !style.HasTextColour() &&
2510         !style.HasLeftIndent() && !style.HasRightIndent() && !style.HasAlignment() &&
2511         !style.HasTabs() )
2512     {
2513         // nothing to do: return true if there was really nothing to do and
2514         // false if we failed to set bg colour
2515         return !style.HasBackgroundColour();
2516     }
2517 
2518     // order the range if needed
2519     if ( start > end )
2520     {
2521         long tmp = start;
2522         start = end;
2523         end = tmp;
2524     }
2525 
2526     // we can only change the format of the selection, so select the range we
2527     // want and restore the old selection later
2528     long startOld, endOld;
2529     GetSelection(&startOld, &endOld);
2530 
2531     // but do we really have to change the selection?
2532     bool changeSel = start != startOld || end != endOld;
2533 
2534     if ( changeSel )
2535     {
2536         DoSetSelection(start, end, false /* don't scroll caret into view */);
2537     }
2538 
2539     // initialize CHARFORMAT struct
2540 #if wxUSE_RICHEDIT2
2541     CHARFORMAT2 cf;
2542 #else
2543     CHARFORMAT cf;
2544 #endif
2545 
2546     wxZeroMemory(cf);
2547 
2548     // we can't use CHARFORMAT2 with RichEdit 1.0, so pretend it is a simple
2549     // CHARFORMAT in that case
2550 #if wxUSE_RICHEDIT2
2551     if ( m_verRichEdit == 1 )
2552     {
2553         // this is the only thing the control is going to grok
2554         cf.cbSize = sizeof(CHARFORMAT);
2555     }
2556     else
2557 #endif
2558     {
2559         // CHARFORMAT or CHARFORMAT2
2560         cf.cbSize = sizeof(cf);
2561     }
2562 
2563     if ( style.HasFont() )
2564     {
2565         // VZ: CFM_CHARSET doesn't seem to do anything at all in RichEdit 2.0
2566         //     but using it doesn't seem to hurt neither so leaving it for now
2567 
2568         cf.dwMask |= CFM_FACE | CFM_SIZE | CFM_CHARSET |
2569                      CFM_ITALIC | CFM_BOLD | CFM_UNDERLINE;
2570 
2571         // fill in data from LOGFONT but recalculate lfHeight because we need
2572         // the real height in twips and not the negative number which
2573         // wxFillLogFont() returns (this is correct in general and works with
2574         // the Windows font mapper, but not here)
2575         LOGFONT lf;
2576         wxFillLogFont(&lf, &style.GetFont());
2577         cf.yHeight = 20*style.GetFont().GetPointSize(); // 1 pt = 20 twips
2578         cf.bCharSet = lf.lfCharSet;
2579         cf.bPitchAndFamily = lf.lfPitchAndFamily;
2580         wxStrncpy( cf.szFaceName, lf.lfFaceName, WXSIZEOF(cf.szFaceName) );
2581 
2582         // also deal with underline/italic/bold attributes: note that we must
2583         // always set CFM_ITALIC &c bits in dwMask, even if we don't set the
2584         // style to allow clearing it
2585         if ( lf.lfItalic )
2586         {
2587             cf.dwEffects |= CFE_ITALIC;
2588         }
2589 
2590         if ( lf.lfWeight == FW_BOLD )
2591         {
2592             cf.dwEffects |= CFE_BOLD;
2593         }
2594 
2595         if ( lf.lfUnderline )
2596         {
2597             cf.dwEffects |= CFE_UNDERLINE;
2598         }
2599 
2600         // strikeout fonts are not supported by wxWidgets
2601     }
2602 
2603     if ( style.HasTextColour() )
2604     {
2605         cf.dwMask |= CFM_COLOR;
2606         cf.crTextColor = wxColourToRGB(style.GetTextColour());
2607     }
2608 
2609 #if wxUSE_RICHEDIT2
2610     if ( m_verRichEdit != 1 && style.HasBackgroundColour() )
2611     {
2612         cf.dwMask |= CFM_BACKCOLOR;
2613         cf.crBackColor = wxColourToRGB(style.GetBackgroundColour());
2614     }
2615 #endif // wxUSE_RICHEDIT2
2616 
2617     // do format the selection
2618     bool ok = ::SendMessage(GetHwnd(), EM_SETCHARFORMAT,
2619                             SCF_SELECTION, (LPARAM)&cf) != 0;
2620     if ( !ok )
2621     {
2622         wxLogDebug(_T("SendMessage(EM_SETCHARFORMAT, SCF_SELECTION) failed"));
2623     }
2624 
2625     // now do the paragraph formatting
2626     PARAFORMAT2 pf;
2627     wxZeroMemory(pf);
2628     // we can't use PARAFORMAT2 with RichEdit 1.0, so pretend it is a simple
2629     // PARAFORMAT in that case
2630 #if wxUSE_RICHEDIT2
2631     if ( m_verRichEdit == 1 )
2632     {
2633         // this is the only thing the control is going to grok
2634         pf.cbSize = sizeof(PARAFORMAT);
2635     }
2636     else
2637 #endif
2638     {
2639         // PARAFORMAT or PARAFORMAT2
2640         pf.cbSize = sizeof(pf);
2641     }
2642 
2643     if (style.HasAlignment())
2644     {
2645         pf.dwMask |= PFM_ALIGNMENT;
2646         if (style.GetAlignment() == wxTEXT_ALIGNMENT_RIGHT)
2647             pf.wAlignment = PFA_RIGHT;
2648         else if (style.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE)
2649             pf.wAlignment = PFA_CENTER;
2650         else if (style.GetAlignment() == wxTEXT_ALIGNMENT_JUSTIFIED)
2651             pf.wAlignment = PFA_JUSTIFY;
2652         else
2653             pf.wAlignment = PFA_LEFT;
2654     }
2655 
2656     if (style.HasLeftIndent())
2657     {
2658         pf.dwMask |= PFM_STARTINDENT | PFM_OFFSET;
2659 
2660         // Convert from 1/10 mm to TWIPS
2661         pf.dxStartIndent = (int) (((double) style.GetLeftIndent()) * mm2twips / 10.0) ;
2662         pf.dxOffset = (int) (((double) style.GetLeftSubIndent()) * mm2twips / 10.0) ;
2663     }
2664 
2665     if (style.HasRightIndent())
2666     {
2667         pf.dwMask |= PFM_RIGHTINDENT;
2668 
2669         // Convert from 1/10 mm to TWIPS
2670         pf.dxRightIndent = (int) (((double) style.GetRightIndent()) * mm2twips / 10.0) ;
2671     }
2672 
2673     if (style.HasTabs())
2674     {
2675         pf.dwMask |= PFM_TABSTOPS;
2676 
2677         const wxArrayInt& tabs = style.GetTabs();
2678 
2679         pf.cTabCount = (SHORT)wxMin(tabs.GetCount(), MAX_TAB_STOPS);
2680         size_t i;
2681         for (i = 0; i < (size_t) pf.cTabCount; i++)
2682         {
2683             // Convert from 1/10 mm to TWIPS
2684             pf.rgxTabs[i] = (int) (((double) tabs[i]) * mm2twips / 10.0) ;
2685         }
2686     }
2687 
2688 #if wxUSE_RICHEDIT2
2689     if ( m_verRichEdit > 1 )
2690     {
2691         if ( wxTheApp->GetLayoutDirection() == wxLayout_RightToLeft )
2692         {
2693             // Use RTL paragraphs in RTL mode to get proper layout
2694             pf.dwMask |= PFM_RTLPARA;
2695             pf.wEffects |= PFE_RTLPARA;
2696         }
2697     }
2698 #endif // wxUSE_RICHEDIT2
2699 
2700     if ( pf.dwMask )
2701     {
2702         // do format the selection
2703         bool ok = ::SendMessage(GetHwnd(), EM_SETPARAFORMAT,
2704                                 0, (LPARAM) &pf) != 0;
2705         if ( !ok )
2706         {
2707             wxLogDebug(_T("SendMessage(EM_SETPARAFORMAT, 0) failed"));
2708         }
2709     }
2710 
2711     if ( changeSel )
2712     {
2713         // restore the original selection
2714         DoSetSelection(startOld, endOld, false);
2715     }
2716 
2717     return ok;
2718 }
2719 
SetDefaultStyle(const wxTextAttr & style)2720 bool wxTextCtrl::SetDefaultStyle(const wxTextAttr& style)
2721 {
2722     if ( !wxTextCtrlBase::SetDefaultStyle(style) )
2723         return false;
2724 
2725     if ( IsEditable() )
2726     {
2727         // we have to do this or the style wouldn't apply for the text typed by
2728         // the user
2729         wxTextPos posLast = GetLastPosition();
2730         SetStyle(posLast, posLast, m_defaultStyle);
2731     }
2732 
2733     return true;
2734 }
2735 
GetStyle(long position,wxTextAttr & style)2736 bool wxTextCtrl::GetStyle(long position, wxTextAttr& style)
2737 {
2738     if ( !IsRich() )
2739     {
2740         // can't do it with normal text control
2741         return false;
2742     }
2743 
2744     // initialize CHARFORMAT struct
2745 #if wxUSE_RICHEDIT2
2746     CHARFORMAT2 cf;
2747 #else
2748     CHARFORMAT cf;
2749 #endif
2750 
2751     wxZeroMemory(cf);
2752 
2753     // we can't use CHARFORMAT2 with RichEdit 1.0, so pretend it is a simple
2754     // CHARFORMAT in that case
2755 #if wxUSE_RICHEDIT2
2756     if ( m_verRichEdit == 1 )
2757     {
2758         // this is the only thing the control is going to grok
2759         cf.cbSize = sizeof(CHARFORMAT);
2760     }
2761     else
2762 #endif
2763     {
2764         // CHARFORMAT or CHARFORMAT2
2765         cf.cbSize = sizeof(cf);
2766     }
2767     // we can only change the format of the selection, so select the range we
2768     // want and restore the old selection later
2769     long startOld, endOld;
2770     GetSelection(&startOld, &endOld);
2771 
2772     // but do we really have to change the selection?
2773     bool changeSel = position != startOld || position != endOld;
2774 
2775     if ( changeSel )
2776     {
2777         DoSetSelection(position, position+1, false /* don't scroll caret into view */);
2778     }
2779 
2780     // get the selection formatting
2781     (void) ::SendMessage(GetHwnd(), EM_GETCHARFORMAT,
2782                             SCF_SELECTION, (LPARAM)&cf) ;
2783 
2784 
2785     LOGFONT lf;
2786     lf.lfHeight = cf.yHeight;
2787     lf.lfWidth = 0;
2788     lf.lfCharSet = ANSI_CHARSET; // FIXME: how to get correct charset?
2789     lf.lfClipPrecision = 0;
2790     lf.lfEscapement = 0;
2791     wxStrcpy(lf.lfFaceName, cf.szFaceName);
2792 
2793     //NOTE:  we _MUST_ set each of these values to _something_ since we
2794     //do not call wxZeroMemory on the LOGFONT lf
2795     if (cf.dwEffects & CFE_ITALIC)
2796         lf.lfItalic = TRUE;
2797     else
2798         lf.lfItalic = FALSE;
2799 
2800     lf.lfOrientation = 0;
2801     lf.lfPitchAndFamily = cf.bPitchAndFamily;
2802     lf.lfQuality = 0;
2803 
2804     if (cf.dwEffects & CFE_STRIKEOUT)
2805         lf.lfStrikeOut = TRUE;
2806     else
2807         lf.lfStrikeOut = FALSE;
2808 
2809     if (cf.dwEffects & CFE_UNDERLINE)
2810         lf.lfUnderline = TRUE;
2811     else
2812         lf.lfUnderline = FALSE;
2813 
2814     if (cf.dwEffects & CFE_BOLD)
2815         lf.lfWeight = FW_BOLD;
2816     else
2817         lf.lfWeight = FW_NORMAL;
2818 
2819     wxFont font = wxCreateFontFromLogFont(& lf);
2820     if (font.Ok())
2821     {
2822         style.SetFont(font);
2823     }
2824     style.SetTextColour(wxColour(cf.crTextColor));
2825 
2826 #if wxUSE_RICHEDIT2
2827     if ( m_verRichEdit != 1 )
2828     {
2829         // cf.dwMask |= CFM_BACKCOLOR;
2830         style.SetBackgroundColour(wxColour(cf.crBackColor));
2831     }
2832 #endif // wxUSE_RICHEDIT2
2833 
2834     // now get the paragraph formatting
2835     PARAFORMAT2 pf;
2836     wxZeroMemory(pf);
2837     // we can't use PARAFORMAT2 with RichEdit 1.0, so pretend it is a simple
2838     // PARAFORMAT in that case
2839 #if wxUSE_RICHEDIT2
2840     if ( m_verRichEdit == 1 )
2841     {
2842         // this is the only thing the control is going to grok
2843         pf.cbSize = sizeof(PARAFORMAT);
2844     }
2845     else
2846 #endif
2847     {
2848         // PARAFORMAT or PARAFORMAT2
2849         pf.cbSize = sizeof(pf);
2850     }
2851 
2852     // do format the selection
2853     (void) ::SendMessage(GetHwnd(), EM_GETPARAFORMAT, 0, (LPARAM) &pf) ;
2854 
2855     style.SetLeftIndent( (int) ((double) pf.dxStartIndent * twips2mm * 10.0), (int) ((double) pf.dxOffset * twips2mm * 10.0) );
2856     style.SetRightIndent( (int) ((double) pf.dxRightIndent * twips2mm * 10.0) );
2857 
2858     if (pf.wAlignment == PFA_CENTER)
2859         style.SetAlignment(wxTEXT_ALIGNMENT_CENTRE);
2860     else if (pf.wAlignment == PFA_RIGHT)
2861         style.SetAlignment(wxTEXT_ALIGNMENT_RIGHT);
2862     else if (pf.wAlignment == PFA_JUSTIFY)
2863         style.SetAlignment(wxTEXT_ALIGNMENT_JUSTIFIED);
2864     else
2865         style.SetAlignment(wxTEXT_ALIGNMENT_LEFT);
2866 
2867     wxArrayInt tabStops;
2868     size_t i;
2869     for (i = 0; i < (size_t) pf.cTabCount; i++)
2870     {
2871         tabStops.Add( (int) ((double) (pf.rgxTabs[i] & 0xFFFF) * twips2mm * 10.0) );
2872     }
2873 
2874     if ( changeSel )
2875     {
2876         // restore the original selection
2877         DoSetSelection(startOld, endOld, false);
2878     }
2879 
2880     return true;
2881 }
2882 
2883 #endif
2884 
2885 // ----------------------------------------------------------------------------
2886 // wxRichEditModule
2887 // ----------------------------------------------------------------------------
2888 
2889 static const HINSTANCE INVALID_HINSTANCE = (HINSTANCE)-1;
2890 
OnInit()2891 bool wxRichEditModule::OnInit()
2892 {
2893     // don't do anything - we will load it when needed
2894     return true;
2895 }
2896 
OnExit()2897 void wxRichEditModule::OnExit()
2898 {
2899     for ( size_t i = 0; i < WXSIZEOF(ms_hRichEdit); i++ )
2900     {
2901         if ( ms_hRichEdit[i] && ms_hRichEdit[i] != INVALID_HINSTANCE )
2902         {
2903             ::FreeLibrary(ms_hRichEdit[i]);
2904             ms_hRichEdit[i] = NULL;
2905         }
2906     }
2907 #if wxUSE_INKEDIT
2908     if (ms_inkEditLib.IsLoaded())
2909         ms_inkEditLib.Unload();
2910 #endif
2911 }
2912 
2913 /* static */
Load(Version version)2914 bool wxRichEditModule::Load(Version version)
2915 {
2916     if ( ms_hRichEdit[version] == INVALID_HINSTANCE )
2917     {
2918         // we had already tried to load it and failed
2919         return false;
2920     }
2921 
2922     if ( ms_hRichEdit[version] )
2923     {
2924         // we've already got this one
2925         return true;
2926     }
2927 
2928     static const wxChar *dllnames[] =
2929     {
2930         _T("riched32"),
2931         _T("riched20"),
2932         _T("msftedit"),
2933     };
2934 
2935     wxCOMPILE_TIME_ASSERT( WXSIZEOF(dllnames) == Version_Max,
2936                             RichEditDllNamesVersionsMismatch );
2937 
2938     ms_hRichEdit[version] = ::LoadLibrary(dllnames[version]);
2939 
2940     if ( !ms_hRichEdit[version] )
2941     {
2942         ms_hRichEdit[version] = INVALID_HINSTANCE;
2943 
2944         return false;
2945     }
2946 
2947     return true;
2948 }
2949 
2950 #if wxUSE_INKEDIT
2951 // load the InkEdit library
LoadInkEdit()2952 bool wxRichEditModule::LoadInkEdit()
2953 {
2954     if (ms_inkEditLibLoadAttemped)
2955         return ms_inkEditLib.IsLoaded();
2956 
2957     ms_inkEditLibLoadAttemped = true;
2958 
2959     wxLogNull logNull;
2960     return ms_inkEditLib.Load(wxT("inked"));
2961 }
2962 #endif
2963 
2964 
2965 #endif // wxUSE_RICHEDIT
2966 
2967 #endif // wxUSE_TEXTCTRL && !(__SMARTPHONE__ && __WXWINCE__)
2968