1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/msw/textctrl.cpp
3 // Purpose:     wxTextCtrl
4 // Author:      Julian Smart
5 // Modified by:
6 // Created:     04/01/98
7 // Copyright:   (c) Julian Smart
8 // Licence:     wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10 
11 // ============================================================================
12 // declarations
13 // ============================================================================
14 
15 // ----------------------------------------------------------------------------
16 // headers
17 // ----------------------------------------------------------------------------
18 
19 // For compilers that support precompilation, includes "wx.h".
20 #include "wx/wxprec.h"
21 
22 
23 #if wxUSE_TEXTCTRL
24 
25 #ifndef WX_PRECOMP
26     #include "wx/textctrl.h"
27     #include "wx/settings.h"
28     #include "wx/brush.h"
29     #include "wx/dcclient.h"
30     #include "wx/utils.h"
31     #include "wx/intl.h"
32     #include "wx/log.h"
33     #include "wx/app.h"
34     #include "wx/menu.h"
35     #include "wx/math.h"
36     #include "wx/module.h"
37     #include "wx/wxcrtvararg.h"
38 #endif
39 
40 #include "wx/fontutil.h"
41 #include "wx/scopedptr.h"
42 #include "wx/stack.h"
43 #include "wx/sysopt.h"
44 
45 #if wxUSE_CLIPBOARD
46     #include "wx/clipbrd.h"
47     #include "wx/dataobj.h"
48 #endif
49 
50 #include "wx/textfile.h"
51 
52 #include <windowsx.h>
53 
54 #include "wx/msw/private.h"
55 #include "wx/msw/winundef.h"
56 
57 #include <string.h>
58 #include <stdlib.h>
59 
60 #include <sys/types.h>
61 
62 #if wxUSE_RICHEDIT
63     #include <richedit.h>
64     #include <richole.h>
65 
66     // MinGW32 doesn't have tom.h and doesn't define the interfaces and the
67     // constants we need, so we can't use ITextDocument::Undo() with it. All
68     // the other compilers do have this header.
69     #ifndef __MINGW32_TOOLCHAIN__
70         #define wxHAS_TOM_H
71     #endif
72 
73     #ifdef wxHAS_TOM_H
74         #include <tom.h>
75     #endif
76 
77     #include "wx/msw/ole/oleutils.h"
78 
79     #include "wx/msw/private/comptr.h"
80 #endif // wxUSE_RICHEDIT
81 
82 #if wxUSE_INKEDIT
83     #include <wx/dynlib.h>
84 #endif
85 
86 #include "wx/msw/missing.h"
87 
88 #ifndef CFM_BACKCOLOR
89     #define CFM_BACKCOLOR 0x04000000
90 #endif
91 
92 #ifndef CFE_AUTOBACKCOLOR
93     #define CFE_AUTOBACKCOLOR 0x04000000
94 #endif
95 
96 // missing defines for MinGW build
97 #ifndef CFM_UNDERLINETYPE
98     #define CFM_UNDERLINETYPE       0x00800000
99 #endif
100 
101 #ifndef CFU_UNDERLINENONE
102     #define CFU_UNDERLINENONE       0
103 #endif
104 
105 #ifndef CFU_UNDERLINE
106     #define CFU_UNDERLINE           1
107 #endif
108 
109 #ifndef CFU_UNDERLINEDOUBLE
110     #define CFU_UNDERLINEDOUBLE     3
111 #endif
112 
113 #ifndef CFU_UNDERLINEWAVE
114     #define CFU_UNDERLINEWAVE       8
115 #endif
116 
117 #if wxUSE_DRAG_AND_DROP && wxUSE_RICHEDIT
118 
119 // dummy value used for m_dropTarget, different from any valid pointer value
120 // (which are all even under Windows) and NULL
121 static wxDropTarget *
122     wxRICHTEXT_DEFAULT_DROPTARGET = reinterpret_cast<wxDropTarget *>(1);
123 
124 #endif // wxUSE_DRAG_AND_DROP && wxUSE_RICHEDIT
125 
126 #if wxUSE_OLE
127 // This must be the last header included to only affect the DEFINE_GUID()
128 // occurrences below but not any GUIDs declared in the standard files included
129 // above.
130 #include <initguid.h>
131 
132 namespace
133 {
134 
135 // Normally the IRichEditOleCallback interface and its IID are defined in
136 // richole.h header file included in the platform SDK but MinGW doesn't
137 // have the IID symbol (but does have the interface). Work around it by
138 // defining it ourselves.
139 DEFINE_GUID(wxIID_IRichEditOleCallback,
140     0x00020d03, 0x0000, 0x0000, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46);
141 
142 } // anonymous namespace
143 
144 #ifdef wxHAS_TOM_H
145 
146 // This one is not defined in the standard libraries at all and MSDN just says
147 // to define it explicitly, so we do it for IID_XXX constant itself and not our
148 // own wxIID_XXX.
149 DEFINE_GUID(IID_ITextDocument,
150     0x8cc497c0, 0xa1df, 0x11ce, 0x80, 0x98, 0x00, 0xaa, 0x00, 0x47, 0xbe, 0x5d);
151 
152 #endif // wxHAS_TOM_H
153 
154 #endif // wxUSE_OLE
155 
156 // ----------------------------------------------------------------------------
157 // private classes
158 // ----------------------------------------------------------------------------
159 
160 #if wxUSE_RICHEDIT
161 
162 // this module initializes RichEdit DLL(s) if needed
163 class wxRichEditModule : public wxModule
164 {
165 public:
166     enum Version
167     {
168         Version_1,          // riched32.dll
169         Version_2or3,       // both use riched20.dll
170         Version_41,         // msftedit.dll (XP SP1 and Windows 2003)
171         Version_Max
172     };
173 
174     virtual bool OnInit() wxOVERRIDE;
175     virtual void OnExit() wxOVERRIDE;
176 
177     // load the richedit DLL for the specified version of rich edit
178     static bool Load(Version version);
179 
180 #if wxUSE_INKEDIT
181     // load the InkEdit library
182     static bool LoadInkEdit();
183 #endif
184 
185 private:
186     // the handles to richedit 1.0 and 2.0 (or 3.0) DLLs
187     static HINSTANCE ms_hRichEdit[Version_Max];
188 
189 #if wxUSE_INKEDIT
190     static wxDynamicLibrary ms_inkEditLib;
191     static bool             ms_inkEditLibLoadAttemped;
192 #endif
193 
194     wxDECLARE_DYNAMIC_CLASS(wxRichEditModule);
195 };
196 
197 HINSTANCE wxRichEditModule::ms_hRichEdit[Version_Max] = { NULL, NULL, NULL };
198 
199 #if wxUSE_INKEDIT
200 wxDynamicLibrary wxRichEditModule::ms_inkEditLib;
201 bool             wxRichEditModule::ms_inkEditLibLoadAttemped = false;
202 #endif
203 
204 wxIMPLEMENT_DYNAMIC_CLASS(wxRichEditModule, wxModule);
205 
206 #if wxUSE_OLE
207 
208 extern wxMenu *wxCurrentPopupMenu;
209 
210 class wxTextCtrlOleCallback : public IRichEditOleCallback
211 {
212 public:
wxTextCtrlOleCallback(wxTextCtrl * text)213     wxTextCtrlOleCallback(wxTextCtrl *text) : m_textCtrl(text), m_menu(NULL) {}
~wxTextCtrlOleCallback()214     virtual ~wxTextCtrlOleCallback() { DeleteContextMenuObject(); }
215 
ContextSensitiveHelp(BOOL WXUNUSED (enterMode))216     wxSTDMETHODIMP ContextSensitiveHelp(BOOL WXUNUSED(enterMode)) wxOVERRIDE { return E_NOTIMPL; }
DeleteObject(LPOLEOBJECT WXUNUSED (oleobj))217     wxSTDMETHODIMP DeleteObject(LPOLEOBJECT WXUNUSED(oleobj)) wxOVERRIDE { return E_NOTIMPL; }
GetClipboardData(CHARRANGE * WXUNUSED (chrg),DWORD WXUNUSED (reco),LPDATAOBJECT * WXUNUSED (dataobj))218     wxSTDMETHODIMP GetClipboardData(CHARRANGE* WXUNUSED(chrg), DWORD WXUNUSED(reco), LPDATAOBJECT* WXUNUSED(dataobj)) wxOVERRIDE { return E_NOTIMPL; }
GetDragDropEffect(BOOL WXUNUSED (drag),DWORD WXUNUSED (grfKeyState),LPDWORD WXUNUSED (effect))219     wxSTDMETHODIMP GetDragDropEffect(BOOL WXUNUSED(drag), DWORD WXUNUSED(grfKeyState), LPDWORD WXUNUSED(effect)) wxOVERRIDE { return E_NOTIMPL; }
GetInPlaceContext(LPOLEINPLACEFRAME * WXUNUSED (frame),LPOLEINPLACEUIWINDOW * WXUNUSED (doc),LPOLEINPLACEFRAMEINFO WXUNUSED (frameInfo))220     wxSTDMETHODIMP GetInPlaceContext(LPOLEINPLACEFRAME* WXUNUSED(frame), LPOLEINPLACEUIWINDOW* WXUNUSED(doc), LPOLEINPLACEFRAMEINFO WXUNUSED(frameInfo)) wxOVERRIDE { return E_NOTIMPL; }
GetNewStorage(LPSTORAGE * WXUNUSED (stg))221     wxSTDMETHODIMP GetNewStorage(LPSTORAGE *WXUNUSED(stg)) wxOVERRIDE { return E_NOTIMPL; }
QueryAcceptData(LPDATAOBJECT WXUNUSED (dataobj),CLIPFORMAT * WXUNUSED (format),DWORD WXUNUSED (reco),BOOL WXUNUSED (really),HGLOBAL WXUNUSED (hMetaPict))222     wxSTDMETHODIMP QueryAcceptData(LPDATAOBJECT WXUNUSED(dataobj), CLIPFORMAT* WXUNUSED(format), DWORD WXUNUSED(reco), BOOL WXUNUSED(really), HGLOBAL WXUNUSED(hMetaPict)) wxOVERRIDE { return E_NOTIMPL; }
QueryInsertObject(LPCLSID WXUNUSED (clsid),LPSTORAGE WXUNUSED (stg),LONG WXUNUSED (cp))223     wxSTDMETHODIMP QueryInsertObject(LPCLSID WXUNUSED(clsid), LPSTORAGE WXUNUSED(stg), LONG WXUNUSED(cp)) wxOVERRIDE { return E_NOTIMPL; }
ShowContainerUI(BOOL WXUNUSED (show))224     wxSTDMETHODIMP ShowContainerUI(BOOL WXUNUSED(show)) wxOVERRIDE { return E_NOTIMPL; }
225 
GetContextMenu(WORD WXUNUSED (seltype),LPOLEOBJECT WXUNUSED (oleobj),CHARRANGE * WXUNUSED (chrg),HMENU * menu)226     wxSTDMETHODIMP GetContextMenu(WORD WXUNUSED(seltype), LPOLEOBJECT WXUNUSED(oleobj), CHARRANGE* WXUNUSED(chrg), HMENU *menu) wxOVERRIDE
227     {
228         // 'menu' will be shown and destroyed by the caller. We need to keep
229         // its wx counterpart, the wxMenu instance, around until it is
230         // dismissed, though, so store it in m_menu and destroy sometime later.
231         DeleteContextMenuObject();
232         m_menu = m_textCtrl->MSWCreateContextMenu();
233         *menu = m_menu->GetHMenu();
234 
235         // Make wx handle events from the popup menu correctly:
236         m_menu->SetInvokingWindow(m_textCtrl);
237         wxCurrentPopupMenu = m_menu;
238 
239         m_menu->UpdateUI();
240 
241         return S_OK;
242     }
243 
244     DECLARE_IUNKNOWN_METHODS;
245 
246 private:
DeleteContextMenuObject()247     void DeleteContextMenuObject()
248     {
249         if ( m_menu )
250         {
251             m_menu->MSWDetachHMENU();
252             if ( wxCurrentPopupMenu == m_menu )
253                 wxCurrentPopupMenu = NULL;
254             wxDELETE(m_menu);
255         }
256     }
257 
258     wxTextCtrl *m_textCtrl;
259     wxMenu *m_menu;
260 
261     wxDECLARE_NO_COPY_CLASS(wxTextCtrlOleCallback);
262 };
263 
264 BEGIN_IID_TABLE(wxTextCtrlOleCallback)
265     ADD_IID(Unknown)
266     ADD_RAW_IID(wxIID_IRichEditOleCallback)
267 END_IID_TABLE;
268 
269 IMPLEMENT_IUNKNOWN_METHODS(wxTextCtrlOleCallback)
270 
271 #endif // wxUSE_OLE
272 
273 #endif // wxUSE_RICHEDIT
274 
275 // a small class used to set m_updatesCount to 0 (to filter duplicate events if
276 // necessary) and to reset it back to -1 afterwards
277 class UpdatesCountFilter
278 {
279 public:
UpdatesCountFilter(int & count)280     UpdatesCountFilter(int& count)
281         : m_count(count)
282     {
283         wxASSERT_MSG( m_count == -1 || m_count == -2,
284                       wxT("wrong initial m_updatesCount value") );
285 
286         if (m_count != -2)
287             m_count = 0;
288         //else: we don't want to count how many update events we get as we're going
289         //      to ignore all of them
290     }
291 
~UpdatesCountFilter()292     ~UpdatesCountFilter()
293     {
294         m_count = -1;
295     }
296 
297     // return true if an event has been received
GotUpdate() const298     bool GotUpdate() const
299     {
300         return m_count == 1;
301     }
302 
303 private:
304     int& m_count;
305 
306     wxDECLARE_NO_COPY_CLASS(UpdatesCountFilter);
307 };
308 
309 namespace
310 {
311 
312 // This stack stores the length of the text being currently inserted into the
313 // current control.
314 //
315 // It is used to pass information from DoWriteText() to AdjustSpaceLimit()
316 // and is global as text can only be inserted into a few text controls at a
317 // time (but possibly more than into one, if wxEVT_TEXT event handler does
318 // something that results in another text control update), and we don't want to
319 // waste space in every wxTextCtrl object for this field unnecessarily.
320 wxStack<int> gs_lenOfInsertedText;
321 
322 } // anonymous namespace
323 
324 // ----------------------------------------------------------------------------
325 // event tables and other macros
326 // ----------------------------------------------------------------------------
327 
wxBEGIN_EVENT_TABLE(wxTextCtrl,wxTextCtrlBase)328 wxBEGIN_EVENT_TABLE(wxTextCtrl, wxTextCtrlBase)
329     EVT_CHAR(wxTextCtrl::OnChar)
330     EVT_KEY_DOWN(wxTextCtrl::OnKeyDown)
331     EVT_DROP_FILES(wxTextCtrl::OnDropFiles)
332 
333     EVT_MENU(wxID_CUT, wxTextCtrl::OnCut)
334     EVT_MENU(wxID_COPY, wxTextCtrl::OnCopy)
335     EVT_MENU(wxID_PASTE, wxTextCtrl::OnPaste)
336     EVT_MENU(wxID_UNDO, wxTextCtrl::OnUndo)
337     EVT_MENU(wxID_REDO, wxTextCtrl::OnRedo)
338     EVT_MENU(wxID_CLEAR, wxTextCtrl::OnDelete)
339     EVT_MENU(wxID_SELECTALL, wxTextCtrl::OnSelectAll)
340 
341     EVT_UPDATE_UI(wxID_CUT, wxTextCtrl::OnUpdateCut)
342     EVT_UPDATE_UI(wxID_COPY, wxTextCtrl::OnUpdateCopy)
343     EVT_UPDATE_UI(wxID_PASTE, wxTextCtrl::OnUpdatePaste)
344     EVT_UPDATE_UI(wxID_UNDO, wxTextCtrl::OnUpdateUndo)
345     EVT_UPDATE_UI(wxID_REDO, wxTextCtrl::OnUpdateRedo)
346     EVT_UPDATE_UI(wxID_CLEAR, wxTextCtrl::OnUpdateDelete)
347     EVT_UPDATE_UI(wxID_SELECTALL, wxTextCtrl::OnUpdateSelectAll)
348 
349     EVT_SET_FOCUS(wxTextCtrl::OnSetFocus)
350 wxEND_EVENT_TABLE()
351 
352 // ============================================================================
353 // implementation
354 // ============================================================================
355 
356 // ----------------------------------------------------------------------------
357 // creation
358 // ----------------------------------------------------------------------------
359 
360 void wxTextCtrl::Init()
361 {
362 #if wxUSE_RICHEDIT
363     m_verRichEdit = 0;
364     m_richDPIscale = 1;
365 #endif // wxUSE_RICHEDIT
366 
367 #if wxUSE_INKEDIT && wxUSE_RICHEDIT
368     m_isInkEdit = 0;
369 #endif
370 
371     m_privateContextMenu = NULL;
372     m_updatesCount = -1;
373     m_isNativeCaretShown = true;
374 }
375 
~wxTextCtrl()376 wxTextCtrl::~wxTextCtrl()
377 {
378 #if wxUSE_DRAG_AND_DROP && wxUSE_RICHEDIT
379     if ( m_dropTarget == wxRICHTEXT_DEFAULT_DROPTARGET )
380     {
381         // don't try to destroy this dummy pointer in the base class dtor
382         m_dropTarget = NULL;
383     }
384 #endif // wxUSE_DRAG_AND_DROP && wxUSE_RICHEDIT
385 
386     delete m_privateContextMenu;
387 }
388 
Create(wxWindow * parent,wxWindowID id,const wxString & value,const wxPoint & pos,const wxSize & size,long style,const wxValidator & validator,const wxString & name)389 bool wxTextCtrl::Create(wxWindow *parent,
390                         wxWindowID id,
391                         const wxString& value,
392                         const wxPoint& pos,
393                         const wxSize& size,
394                         long style,
395                         const wxValidator& validator,
396                         const wxString& name)
397 {
398     // base initialization
399     if ( !CreateControl(parent, id, pos, size, style, validator, name) )
400         return false;
401 
402     if ( !MSWCreateText(value, pos, size) )
403         return false;
404 
405 #if wxUSE_DRAG_AND_DROP && wxUSE_RICHEDIT
406     if ( IsRich() )
407     {
408         // rich text controls have a default associated drop target which
409         // allows them to receive (rich) text dropped on them, which is nice,
410         // but prevents us from associating a user-defined drop target with
411         // them as we need to unregister the old one first
412         //
413         // to make it work, we set m_dropTarget to this special value initially
414         // and check for it in our SetDropTarget()
415         m_dropTarget = wxRICHTEXT_DEFAULT_DROPTARGET;
416     }
417 #endif // wxUSE_DRAG_AND_DROP && wxUSE_RICHEDIT
418 
419     return true;
420 }
421 
422 // returns true if the platform should explicitly apply a theme border
CanApplyThemeBorder() const423 bool wxTextCtrl::CanApplyThemeBorder() const
424 {
425     // Standard text control already handles theming
426     return ((GetWindowStyle() & (wxTE_RICH|wxTE_RICH2)) != 0);
427 }
428 
MSWCreateText(const wxString & value,const wxPoint & pos,const wxSize & size)429 bool wxTextCtrl::MSWCreateText(const wxString& value,
430                                const wxPoint& pos,
431                                const wxSize& size)
432 {
433     // translate wxWin style flags to MSW ones
434     WXDWORD msStyle = MSWGetCreateWindowFlags();
435 
436     // do create the control - either an EDIT or RICHEDIT
437     wxString windowClass = wxT("EDIT");
438 
439 #if wxUSE_RICHEDIT
440     if ( m_windowStyle & wxTE_AUTO_URL )
441     {
442         // automatic URL detection only works in RichEdit 2.0+
443         m_windowStyle |= wxTE_RICH2;
444     }
445 
446     if ( m_windowStyle & wxTE_RICH2 )
447     {
448         // using richedit 2.0 implies using wxTE_RICH
449         m_windowStyle |= wxTE_RICH;
450     }
451 
452     // we need to load the richedit DLL before creating the rich edit control
453     if ( m_windowStyle & wxTE_RICH )
454     {
455         // versions 2.0, 3.0 and 4.1 of rich edit are mostly compatible with
456         // each other but not with version 1.0, so we have separate flags for
457         // the version 1.0 and the others (and so m_verRichEdit may be 0 (plain
458         // EDIT control), 1 for version 1.0 or 2 for any higher version)
459         //
460         // notice that 1.0 has no Unicode support at all so in Unicode build we
461         // must use another version
462 
463 #if wxUSE_UNICODE
464         m_verRichEdit = 2;
465 #else // !wxUSE_UNICODE
466         m_verRichEdit = m_windowStyle & wxTE_RICH2 ? 2 : 1;
467 #endif // wxUSE_UNICODE/!wxUSE_UNICODE
468 
469 #if wxUSE_INKEDIT
470         // First test if we can load an ink edit control. Normally, all edit
471         // controls will be made ink edit controls if a tablet environment is
472         // found (or if msw.inkedit != 0 and the InkEd.dll is present).
473         // However, an application can veto ink edit controls by either specifying
474         // msw.inkedit = 0 or by removing wxTE_RICH[2] from the style.
475         //
476         if ((wxSystemSettings::HasFeature(wxSYS_TABLET_PRESENT) || wxSystemOptions::GetOptionInt(wxT("msw.inkedit")) != 0) &&
477             !(wxSystemOptions::HasOption(wxT("msw.inkedit")) && wxSystemOptions::GetOptionInt(wxT("msw.inkedit")) == 0))
478         {
479             if (wxRichEditModule::LoadInkEdit())
480             {
481                 windowClass = INKEDIT_CLASS;
482 
483 #if wxUSE_INKEDIT && wxUSE_RICHEDIT
484                 m_isInkEdit = 1;
485 #endif
486 
487                 // Fake rich text version for other calls
488                 m_verRichEdit = 2;
489             }
490         }
491 #endif
492 
493 #if wxUSE_INKEDIT
494         if (!IsInkEdit())
495 #endif // wxUSE_INKEDIT
496         {
497             if ( m_verRichEdit == 2 )
498             {
499                 if ( wxRichEditModule::Load(wxRichEditModule::Version_41) )
500                 {
501                     // yes, class name for version 4.1 really is 5.0
502                     windowClass = wxT("RICHEDIT50W");
503 
504                     m_verRichEdit = 4;
505                 }
506                 else if ( wxRichEditModule::Load(wxRichEditModule::Version_2or3) )
507                 {
508                     windowClass = wxT("RichEdit20")
509 #if wxUSE_UNICODE
510                                 wxT("W");
511 #else // ANSI
512                                 wxT("A");
513 #endif // Unicode/ANSI
514                 }
515                 else // failed to load msftedit.dll and riched20.dll
516                 {
517                     m_verRichEdit = 1;
518                 }
519             }
520 
521             if ( m_verRichEdit == 1 )
522             {
523                 if ( wxRichEditModule::Load(wxRichEditModule::Version_1) )
524                 {
525                     windowClass = wxT("RICHEDIT");
526                 }
527                 else // failed to load any richedit control DLL
528                 {
529                     // only give the error msg once if the DLL can't be loaded
530                     static bool s_errorGiven = false; // MT ok as only used by GUI
531 
532                     if ( !s_errorGiven )
533                     {
534                         wxLogError(_("Impossible to create a rich edit control, using simple text control instead. Please reinstall riched32.dll"));
535 
536                         s_errorGiven = true;
537                     }
538 
539                     m_verRichEdit = 0;
540                 }
541             }
542         } // !useInkEdit
543     }
544 #endif // wxUSE_RICHEDIT
545 
546     // we need to turn '\n's into "\r\n"s for the multiline controls
547     wxString valueWin;
548     if ( m_windowStyle & wxTE_MULTILINE )
549     {
550         valueWin = wxTextFile::Translate(value, wxTextFileType_Dos);
551     }
552     else // single line
553     {
554         valueWin = value;
555     }
556 
557     // suppress events sent during control creation: we're called either from
558     // the ctor and then we shouldn't generate any events for compatibility
559     // with the other ports, or from SetWindowStyleFlag() and then we shouldn't
560     // generate the events because our text doesn't really change, the fact
561     // that we (sometimes) need to recreate the control is just an
562     // implementation detail
563     m_updatesCount = -2;
564 
565     if ( !MSWCreateControl(windowClass.t_str(), msStyle, pos, size, valueWin) )
566         return false;
567 
568     m_updatesCount = -1;
569 
570 #if wxUSE_RICHEDIT
571     if (IsRich())
572     {
573 #if wxUSE_INKEDIT
574         if (IsInkEdit())
575         {
576             // Pass IEM_InsertText (0) as wParam, in order to have the ink always
577             // converted to text.
578             ::SendMessage(GetHwnd(), EM_SETINKINSERTMODE, 0, 0);
579 
580             // Make sure the mouse can be used for input
581             ::SendMessage(GetHwnd(), EM_SETUSEMOUSEFORINPUT, 1, 0);
582         }
583 #endif
584 
585         // enable the events we're interested in: we want to get EN_CHANGE as
586         // for the normal controls
587         LPARAM mask = ENM_CHANGE;
588 
589         if (GetRichVersion() == 1 && !IsInkEdit())
590         {
591             // we also need EN_MSGFILTER for richedit 1.0 for the reasons
592             // explained in its handler
593            mask |= ENM_MOUSEEVENTS;
594 
595            // we also need to force the appearance of the vertical scrollbar
596            // initially as otherwise the control doesn't refresh correctly
597            // after resize: but once the vertical scrollbar had been shown
598            // (even if it's subsequently hidden) it does
599            //
600            // this is clearly a bug and for now it has been only noticed under
601            // Windows XP, so if we're sure it works correctly under other
602            // systems we could do this only for XP
603            SetSize(-1, 1); // 1 is small enough to force vert scrollbar
604            SetInitialSize(size);
605         }
606         else if ( m_windowStyle & wxTE_AUTO_URL )
607         {
608             mask |= ENM_LINK;
609 
610             ::SendMessage(GetHwnd(), EM_AUTOURLDETECT, TRUE, 0);
611         }
612 
613         ::SendMessage(GetHwnd(), EM_SETEVENTMASK, 0, mask);
614 
615         bool contextMenuConnected = false;
616 #if wxUSE_OLE
617         if ( m_verRichEdit >= 4 )
618         {
619             wxTextCtrlOleCallback *cb = new wxTextCtrlOleCallback(this);
620             if ( ::SendMessage(GetHwnd(), EM_SETOLECALLBACK, 0, (LPARAM)cb) )
621             {
622                 // If we succeeded in setting up the callback, we don't need to
623                 // connect to wxEVT_CONTEXT_MENU to show the menu ourselves,
624                 // but we do need to connect to wxEVT_RIGHT_UP to generate
625                 // wxContextMenuEvent ourselves as we're not going to get it
626                 // from the control which consumes it.
627                 contextMenuConnected = true;
628                 Bind(wxEVT_RIGHT_UP, &wxTextCtrl::OnRightUp, this);
629             }
630         }
631 #endif
632         if ( !contextMenuConnected )
633             Bind(wxEVT_CONTEXT_MENU, &wxTextCtrl::OnContextMenu, this);
634 
635         // Determine the system DPI and the DPI of the display the rich control
636         // is shown on, and calculate and apply the scaling factor.
637         // When this control is created in a (wxFrame) constructor the zoom is
638         // not correctly applied, use CallAfter to delay setting the zoom.
639         m_richDPIscale = GetDPI().y / (float)::GetDeviceCaps(ScreenHDC(), LOGPIXELSY);
640         CallAfter(&wxTextCtrl::MSWSetRichZoom);
641     }
642     else
643 #endif // wxUSE_RICHEDIT
644     if ( HasFlag(wxTE_MULTILINE) && HasFlag(wxTE_READONLY) )
645     {
646         // non-rich read-only multiline controls have grey background by
647         // default under MSW but this is not always appropriate, so forcefully
648         // reset the background colour to normal default
649         //
650         // this is not ideal but, after a long discussion on wx-dev (see
651         // http://thread.gmane.org/gmane.comp.lib.wxwidgets.devel/116360/) it
652         // was finally deemed to be the best behaviour by default (and ideally
653         // we'd have a way to change this, see #11521)
654         SetBackgroundColour(GetClassDefaultAttributes().colBg);
655     }
656 
657     // Without this, if we pass the size in the constructor and then don't change it,
658     // the themed borders will be drawn incorrectly.
659     SetWindowPos(GetHwnd(), NULL, 0, 0, 0, 0,
660                 SWP_NOZORDER|SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE|
661                 SWP_FRAMECHANGED);
662 
663     if ( IsSingleLine() )
664     {
665         // If we don't set the margins explicitly, their size depends on the
666         // control initial size, see #2438. So explicitly set them to something
667         // consistent. And for this we have 2 candidates: EC_USEFONTINFO (which
668         // sets the left margin to 3 pixels, at least under Windows 7) or 0. We
669         // use the former because it looks like it was meant to be used as the
670         // default (what else would it be there for?) and 0 looks bad in
671         // classic mode, i.e. without themes. Also, the margin can be reset to
672         // 0 easily by calling SetMargins() explicitly but setting it to the
673         // default value is not currently supported.
674         //
675         // Finally, notice that EC_USEFONTINFO is used differently for plain
676         // and rich text controls.
677         WPARAM wParam;
678         LPARAM lParam;
679         if ( IsRich() )
680         {
681             wParam = EC_USEFONTINFO;
682             lParam = 0;
683         }
684         else // plain EDIT, EC_USEFONTINFO is used in lParam with them.
685         {
686             wParam = EC_LEFTMARGIN | EC_RIGHTMARGIN;
687             lParam = MAKELPARAM(EC_USEFONTINFO, EC_USEFONTINFO);
688         }
689 
690         ::SendMessage(GetHwnd(), EM_SETMARGINS, wParam, lParam);
691     }
692 
693 #if wxUSE_RICHEDIT && wxUSE_OLE && defined(wxHAS_TOM_H)
694     // For RichEdit >= 4, SetFont(), called above from MSWCreateControl(), uses
695     // EM_SETCHARFORMAT which affects the undo buffer, meaning that CanUndo()
696     // for a newly created control returns true, which is unexpected. To avoid
697     // this, we explicitly use Undo(tomFalse) here to clear the undo buffer.
698     // And since Undo(tomFalse) also disables the undo buffer, we need to
699     // enable it again immediately after clearing by calling Undo(tomTrue).
700     if ( GetRichVersion() >= 4 )
701     {
702         wxCOMPtr<IRichEditOle> pRichEditOle;
703         if ( SendMessage(GetHwnd(), EM_GETOLEINTERFACE,
704                          0, (LPARAM)&pRichEditOle) && pRichEditOle )
705         {
706             wxCOMPtr<ITextDocument> pDoc;
707             HRESULT hr = pRichEditOle->QueryInterface
708                                        (
709                                         wxIID_PPV_ARGS(ITextDocument, &pDoc)
710                                        );
711             if ( SUCCEEDED(hr) )
712             {
713                 hr = pDoc->Undo(tomFalse, NULL);
714                 if ( SUCCEEDED(hr) )
715                     pDoc->Undo(tomTrue, NULL);
716             }
717         }
718     }
719 #endif // wxUSE_RICHEDIT && wxHAS_TOM_H
720 
721     return true;
722 }
723 
724 // Make sure the window style (etc.) reflects the HWND style (roughly)
AdoptAttributesFromHWND()725 void wxTextCtrl::AdoptAttributesFromHWND()
726 {
727     wxWindow::AdoptAttributesFromHWND();
728 
729     HWND hWnd = GetHwnd();
730     long style = ::GetWindowLong(hWnd, GWL_STYLE);
731 
732     // retrieve the style to see whether this is an edit or richedit ctrl
733 #if wxUSE_RICHEDIT
734     wxString classname = wxGetWindowClass(GetHWND());
735 
736     if ( classname.IsSameAs(wxT("EDIT"), false /* no case */) )
737     {
738         m_verRichEdit = 0;
739     }
740     else // rich edit?
741     {
742         wxChar c;
743         if ( wxSscanf(classname, wxT("RichEdit%d0%c"), &m_verRichEdit, &c) != 2 )
744         {
745             wxLogDebug(wxT("Unknown edit control '%s'."), classname.c_str());
746 
747             m_verRichEdit = 0;
748         }
749     }
750 #endif // wxUSE_RICHEDIT
751 
752     if (style & ES_MULTILINE)
753         m_windowStyle |= wxTE_MULTILINE;
754     if (style & ES_PASSWORD)
755         m_windowStyle |= wxTE_PASSWORD;
756     if (style & ES_READONLY)
757         m_windowStyle |= wxTE_READONLY;
758     if (style & ES_WANTRETURN)
759         m_windowStyle |= wxTE_PROCESS_ENTER;
760     if (style & ES_CENTER)
761         m_windowStyle |= wxTE_CENTRE;
762     if (style & ES_RIGHT)
763         m_windowStyle |= wxTE_RIGHT;
764 }
765 
MSWGetStyle(long style,WXDWORD * exstyle) const766 WXDWORD wxTextCtrl::MSWGetStyle(long style, WXDWORD *exstyle) const
767 {
768     long msStyle = wxControl::MSWGetStyle(style, exstyle);
769 
770     // styles which we always add by default
771     if ( style & wxTE_MULTILINE )
772     {
773         msStyle |= ES_MULTILINE | ES_WANTRETURN;
774         if ( !(style & wxTE_NO_VSCROLL) )
775         {
776             // always adjust the vertical scrollbar automatically if we have it
777             msStyle |= WS_VSCROLL | ES_AUTOVSCROLL;
778 
779 #if wxUSE_RICHEDIT
780             // we have to use this style for the rich edit controls because
781             // without it the vertical scrollbar never appears at all in
782             // richedit 3.0 because of our ECO_NOHIDESEL hack (search for it)
783             if ( style & wxTE_RICH2 )
784             {
785                 msStyle |= ES_DISABLENOSCROLL;
786             }
787 #endif // wxUSE_RICHEDIT
788         }
789 
790         style |= wxTE_PROCESS_ENTER;
791     }
792     else // !multiline
793     {
794         // there is really no reason to not have this style for single line
795         // text controls
796         msStyle |= ES_AUTOHSCROLL;
797     }
798 
799     // note that wxTE_DONTWRAP is the same as wxHSCROLL so if we have a horz
800     // scrollbar, there is no wrapping -- which makes sense
801     if ( style & wxTE_DONTWRAP )
802     {
803         // automatically scroll the control horizontally as necessary
804         //
805         // NB: ES_AUTOHSCROLL is needed for richedit controls or they don't
806         //     show horz scrollbar at all, even in spite of WS_HSCROLL, and as
807         //     it doesn't seem to do any harm for plain edit controls, add it
808         //     always
809         msStyle |= WS_HSCROLL | ES_AUTOHSCROLL;
810     }
811 
812     if ( style & wxTE_READONLY )
813         msStyle |= ES_READONLY;
814 
815     if ( style & wxTE_PASSWORD )
816         msStyle |= ES_PASSWORD;
817 
818     if ( style & wxTE_NOHIDESEL )
819         msStyle |= ES_NOHIDESEL;
820 
821     // note that we can't do do "& wxTE_LEFT" as wxTE_LEFT == 0
822     if ( style & wxTE_CENTRE )
823         msStyle |= ES_CENTER;
824     else if ( style & wxTE_RIGHT )
825         msStyle |= ES_RIGHT;
826     else
827         msStyle |= ES_LEFT; // ES_LEFT is 0 as well but for consistency...
828 
829     return msStyle;
830 }
831 
SetWindowStyleFlag(long style)832 void wxTextCtrl::SetWindowStyleFlag(long style)
833 {
834     // changing the alignment of the control dynamically works under Win2003
835     // (but not older Windows version: it seems to work under some versions of
836     // XP but not other ones, and we have no way to determine it so be
837     // conservative here) and only for plain EDIT controls (not RICH ones) and
838     // we have to recreate the control to make it always work
839     if ( IsRich() || wxGetWinVersion() < wxWinVersion_2003 )
840     {
841         const long alignMask = wxTE_LEFT | wxTE_CENTRE | wxTE_RIGHT;
842         if ( (style & alignMask) != (GetWindowStyle() & alignMask) )
843         {
844             const wxString value = GetValue();
845             const wxPoint pos = GetPosition();
846             const wxSize size = GetSize();
847 
848             // delete the old window
849             HWND hwnd = GetHwnd();
850             DissociateHandle();
851             ::DestroyWindow(hwnd);
852 
853             // create the new one with the updated flags
854             m_windowStyle = style;
855             MSWCreateText(value, pos, size);
856 
857             // and make sure it has the same attributes as before
858             if ( m_hasFont )
859             {
860                 // calling SetFont(m_font) would do nothing as the code would
861                 // notice that the font didn't change, so force it to believe
862                 // that it did
863                 wxFont font = m_font;
864                 m_font = wxNullFont;
865                 SetFont(font);
866             }
867 
868             if ( m_hasFgCol )
869             {
870                 wxColour colFg = m_foregroundColour;
871                 m_foregroundColour = wxNullColour;
872                 SetForegroundColour(colFg);
873             }
874 
875             if ( m_hasBgCol )
876             {
877                 wxColour colBg = m_backgroundColour;
878                 m_backgroundColour = wxNullColour;
879                 SetBackgroundColour(colBg);
880             }
881 
882             // note that text styles are lost but this is probably not a big
883             // problem: if you use styles, you probably don't use nor change
884             // alignment flags anyhow
885 
886             return;
887         }
888     }
889 
890 #if wxUSE_RICHEDIT
891     // we have to deal with some styles separately because they can't be
892     // changed by simply calling SetWindowLong(GWL_STYLE) but can be changed
893     // using richedit-specific EM_SETOPTIONS
894     if ( IsRich() &&
895             ((style & wxTE_NOHIDESEL) != (GetWindowStyle() & wxTE_NOHIDESEL)) )
896     {
897         bool set = (style & wxTE_NOHIDESEL) != 0;
898 
899         ::SendMessage(GetHwnd(), EM_SETOPTIONS, set ? ECOOP_OR : ECOOP_AND,
900                       set ? ECO_NOHIDESEL : ~ECO_NOHIDESEL);
901     }
902 #endif // wxUSE_RICHEDIT
903 
904     wxControl::SetWindowStyleFlag(style);
905 }
906 
907 // ----------------------------------------------------------------------------
908 // set/get the controls text
909 // ----------------------------------------------------------------------------
910 
IsEmpty() const911 bool wxTextCtrl::IsEmpty() const
912 {
913     // this is an optimization for multiline controls containing a lot of text
914     if ( IsMultiLine() && GetNumberOfLines() != 1 )
915         return false;
916 
917     return wxTextCtrlBase::IsEmpty();
918 }
919 
GetValue() const920 wxString wxTextCtrl::GetValue() const
921 {
922     // range 0..-1 is special for GetRange() and means to retrieve all text
923     return GetRange(0, -1);
924 }
925 
GetRange(long from,long to) const926 wxString wxTextCtrl::GetRange(long from, long to) const
927 {
928     wxString str;
929 
930     if ( from >= to && to != -1 )
931     {
932         // nothing to retrieve
933         return str;
934     }
935 
936 #if wxUSE_RICHEDIT
937     if ( IsRich() )
938     {
939         int len = GetWindowTextLength(GetHwnd());
940         if ( len > from )
941         {
942             if ( to == -1 )
943                 to = len;
944 
945 #if !wxUSE_UNICODE
946             // we must use EM_STREAMOUT if we don't want to lose all characters
947             // not representable in the current character set (EM_GETTEXTRANGE
948             // simply replaces them with question marks...)
949             if ( GetRichVersion() > 1 )
950             {
951                 // we must have some encoding, otherwise any 8bit chars in the
952                 // control are simply *lost* (replaced by '?')
953                 wxFontEncoding encoding = wxFONTENCODING_SYSTEM;
954 
955                 wxFont font = m_defaultStyle.GetFont();
956                 if ( !font.IsOk() )
957                     font = GetFont();
958 
959                 if ( font.IsOk() )
960                 {
961                    encoding = font.GetEncoding();
962                 }
963 
964 #if wxUSE_INTL
965                 if ( encoding == wxFONTENCODING_SYSTEM )
966                 {
967                     encoding = wxLocale::GetSystemEncoding();
968                 }
969 #endif // wxUSE_INTL
970 
971                 if ( encoding == wxFONTENCODING_SYSTEM )
972                 {
973                     encoding = wxFONTENCODING_ISO8859_1;
974                 }
975 
976                 str = StreamOut(encoding);
977 
978                 if ( !str.empty() )
979                 {
980                     // we have to manually extract the required part, luckily
981                     // this is easy in this case as EOL characters in str are
982                     // just LFs because we remove CRs in wxRichEditStreamOut
983                     str = str.Mid(from, to - from);
984                 }
985             }
986 
987             // StreamOut() wasn't used or failed, try to do it in normal way
988             if ( str.empty() )
989 #endif // !wxUSE_UNICODE
990             {
991                 // alloc one extra WORD as needed by the control
992                 wxStringBuffer tmp(str, ++len);
993                 wxChar *p = tmp;
994 
995                 TEXTRANGE textRange;
996                 textRange.chrg.cpMin = from;
997                 textRange.chrg.cpMax = to;
998                 textRange.lpstrText = p;
999 
1000                 (void)::SendMessage(GetHwnd(), EM_GETTEXTRANGE,
1001                                     0, (LPARAM)&textRange);
1002 
1003                 if ( m_verRichEdit > 1 )
1004                 {
1005                     // RichEdit 2.0 uses just CR ('\r') for the
1006                     // newlines which is neither Unix nor Windows
1007                     // style - convert it to something reasonable
1008                     for ( ; *p; p++ )
1009                     {
1010                         if ( *p == wxT('\r') )
1011                             *p = wxT('\n');
1012                     }
1013                 }
1014             }
1015 
1016             if ( m_verRichEdit == 1 )
1017             {
1018                 // convert to the canonical form - see comment below
1019                 str = wxTextFile::Translate(str, wxTextFileType_Unix);
1020             }
1021         }
1022         //else: no text at all, leave the string empty
1023     }
1024     else
1025 #endif // wxUSE_RICHEDIT
1026     {
1027         // retrieve all text: wxTextEntry method works even for multiline
1028         // controls and must be used for single line ones to account for hints
1029         str = wxTextEntry::GetValue();
1030 
1031         // need only a range?
1032         if ( from < to )
1033         {
1034             str = str.Mid(from, to - from);
1035         }
1036 
1037         // WM_GETTEXT uses standard DOS CR+LF (\r\n) convention - convert to the
1038         // canonical one (same one as above) for consistency with the other kinds
1039         // of controls and, more importantly, with the other ports
1040         str = wxTextFile::Translate(str, wxTextFileType_Unix);
1041     }
1042 
1043     return str;
1044 }
1045 
DoSetValue(const wxString & value,int flags)1046 void wxTextCtrl::DoSetValue(const wxString& value, int flags)
1047 {
1048     // if the text is long enough, it's faster to just set it instead of first
1049     // comparing it with the old one (chances are that it will be different
1050     // anyhow, this comparison is there to avoid flicker for small single-line
1051     // edit controls mostly)
1052     if ( (value.length() > 0x400) || (value != DoGetValue()) )
1053     {
1054         DoWriteText(value, flags /* doesn't include SelectionOnly here */);
1055 
1056         // mark the control as being not dirty - we changed its text, not the
1057         // user
1058         DiscardEdits();
1059 
1060         // for compatibility, don't move the cursor when doing SetValue()
1061         SetInsertionPoint(0);
1062     }
1063     else // same text
1064     {
1065         // still reset the modified flag even if the value didn't really change
1066         // because now it comes from the program and not the user (and do it
1067         // before generating the event so that the event handler could get the
1068         // expected value from IsModified())
1069         DiscardEdits();
1070 
1071         // still send an event for consistency
1072         if (flags & SetValue_SendEvent)
1073             SendUpdateEvent();
1074     }
1075 }
1076 
1077 #if wxUSE_RICHEDIT && !wxUSE_UNICODE
1078 
1079 // TODO: using memcpy() would improve performance a lot for big amounts of text
1080 
1081 DWORD CALLBACK
wxRichEditStreamIn(DWORD_PTR dwCookie,BYTE * buf,LONG cb,LONG * pcb)1082 wxRichEditStreamIn(DWORD_PTR dwCookie, BYTE *buf, LONG cb, LONG *pcb)
1083 {
1084     *pcb = 0;
1085 
1086     const wchar_t ** const ppws = (const wchar_t **)dwCookie;
1087 
1088     wchar_t *wbuf = (wchar_t *)buf;
1089     const wchar_t *wpc = *ppws;
1090     while ( cb && *wpc )
1091     {
1092         *wbuf++ = *wpc++;
1093 
1094         cb -= sizeof(wchar_t);
1095         (*pcb) += sizeof(wchar_t);
1096     }
1097 
1098     *ppws = wpc;
1099 
1100     return 0;
1101 }
1102 
1103 // helper struct used to pass parameters from wxTextCtrl to wxRichEditStreamOut
1104 struct wxStreamOutData
1105 {
1106     wchar_t *wpc;
1107     size_t len;
1108 };
1109 
1110 DWORD CALLBACK
wxRichEditStreamOut(DWORD_PTR dwCookie,BYTE * buf,LONG cb,LONG * pcb)1111 wxRichEditStreamOut(DWORD_PTR dwCookie, BYTE *buf, LONG cb, LONG *pcb)
1112 {
1113     *pcb = 0;
1114 
1115     wxStreamOutData *data = (wxStreamOutData *)dwCookie;
1116 
1117     const wchar_t *wbuf = (const wchar_t *)buf;
1118     wchar_t *wpc = data->wpc;
1119     while ( cb )
1120     {
1121         wchar_t wch = *wbuf++;
1122 
1123         // turn "\r\n" into "\n" on the fly
1124         if ( wch != L'\r' )
1125             *wpc++ = wch;
1126         else
1127             data->len--;
1128 
1129         cb -= sizeof(wchar_t);
1130         (*pcb) += sizeof(wchar_t);
1131     }
1132 
1133     data->wpc = wpc;
1134 
1135     return 0;
1136 }
1137 
1138 
1139 bool
StreamIn(const wxString & value,wxFontEncoding encoding,bool selectionOnly)1140 wxTextCtrl::StreamIn(const wxString& value,
1141                      wxFontEncoding encoding,
1142                      bool selectionOnly)
1143 {
1144     wxCSConv conv(encoding);
1145 
1146     const size_t len = conv.MB2WC(NULL, value.mb_str(), value.length());
1147 
1148     if (len == wxCONV_FAILED)
1149         return false;
1150 
1151     wxWCharBuffer wchBuf(len); // allocates one extra character
1152     wchar_t *wpc = wchBuf.data();
1153 
1154     conv.MB2WC(wpc, value.mb_str(), len + 1);
1155 
1156     // finally, stream it in the control
1157     EDITSTREAM eds;
1158     wxZeroMemory(eds);
1159     eds.dwCookie = (DWORD_PTR)&wpc;
1160     // the cast below is needed for broken (very) old mingw32 headers
1161     eds.pfnCallback = (EDITSTREAMCALLBACK)wxRichEditStreamIn;
1162 
1163     // same problem as in DoWriteText(): we can get multiple events here
1164     UpdatesCountFilter ucf(m_updatesCount);
1165 
1166     ::SendMessage(GetHwnd(), EM_STREAMIN,
1167                   SF_TEXT |
1168                   SF_UNICODE |
1169                   (selectionOnly ? SFF_SELECTION : 0),
1170                   (LPARAM)&eds);
1171 
1172     // It's okay for EN_UPDATE to not be sent if the selection is empty and
1173     // the text is empty, otherwise warn the programmer about it.
1174     wxASSERT_MSG( ucf.GotUpdate() || ( !HasSelection() && value.empty() ),
1175                   wxT("EM_STREAMIN didn't send EN_UPDATE?") );
1176 
1177     if ( eds.dwError )
1178     {
1179         wxLogLastError(wxT("EM_STREAMIN"));
1180     }
1181 
1182     return true;
1183 }
1184 
1185 wxString
StreamOut(wxFontEncoding encoding,bool selectionOnly) const1186 wxTextCtrl::StreamOut(wxFontEncoding encoding, bool selectionOnly) const
1187 {
1188     wxString out;
1189 
1190     const int len = GetWindowTextLength(GetHwnd());
1191 
1192     wxWCharBuffer wchBuf(len);
1193     wchar_t *wpc = wchBuf.data();
1194 
1195     wxStreamOutData data;
1196     data.wpc = wpc;
1197     data.len = len;
1198 
1199     EDITSTREAM eds;
1200     wxZeroMemory(eds);
1201     eds.dwCookie = (DWORD_PTR)&data;
1202     eds.pfnCallback = wxRichEditStreamOut;
1203 
1204     ::SendMessage
1205       (
1206         GetHwnd(),
1207         EM_STREAMOUT,
1208         SF_TEXT | SF_UNICODE | (selectionOnly ? SFF_SELECTION : 0),
1209         (LPARAM)&eds
1210       );
1211 
1212     if ( eds.dwError )
1213     {
1214         wxLogLastError(wxT("EM_STREAMOUT"));
1215     }
1216     else // streamed out ok
1217     {
1218         // NUL-terminate the string because its length could have been
1219         // decreased by wxRichEditStreamOut
1220         *(wchBuf.data() + data.len) = L'\0';
1221 
1222         // now convert to the given encoding (this is a possibly lossful
1223         // conversion but what else can we do)
1224         wxCSConv conv(encoding);
1225         size_t lenNeeded = conv.WC2MB(NULL, wchBuf, 0);
1226 
1227         if ( lenNeeded != wxCONV_FAILED && lenNeeded++ )
1228         {
1229             conv.WC2MB(wxStringBuffer(out, lenNeeded), wchBuf, lenNeeded);
1230         }
1231     }
1232 
1233     return out;
1234 }
1235 
1236 #endif // wxUSE_RICHEDIT
1237 
WriteText(const wxString & value)1238 void wxTextCtrl::WriteText(const wxString& value)
1239 {
1240     DoWriteText(value);
1241 }
1242 
DoWriteText(const wxString & value,int flags)1243 void wxTextCtrl::DoWriteText(const wxString& value, int flags)
1244 {
1245     bool selectionOnly = (flags & SetValue_SelectionOnly) != 0;
1246     wxString valueDos;
1247     if ( m_windowStyle & wxTE_MULTILINE )
1248         valueDos = wxTextFile::Translate(value, wxTextFileType_Dos);
1249     else
1250         valueDos = value;
1251 
1252 #if wxUSE_RICHEDIT
1253     // there are several complications with the rich edit controls here
1254     bool done = false;
1255     if ( IsRich() )
1256     {
1257         // first, ensure that the new text will be in the default style
1258         if ( !m_defaultStyle.IsDefault() )
1259         {
1260             long start, end;
1261             GetSelection(&start, &end);
1262             SetStyle(start, end, m_defaultStyle);
1263         }
1264 
1265 #if !wxUSE_UNICODE
1266         // next check if the text we're inserting must be shown in a non
1267         // default charset -- this only works for RichEdit > 1.0
1268         if ( GetRichVersion() > 1 )
1269         {
1270             wxFont font = m_defaultStyle.GetFont();
1271             if ( !font.IsOk() )
1272                 font = GetFont();
1273 
1274             if ( font.IsOk() )
1275             {
1276                wxFontEncoding encoding = font.GetEncoding();
1277                if ( encoding != wxFONTENCODING_SYSTEM )
1278                {
1279                    // we have to use EM_STREAMIN to force richedit control 2.0+
1280                    // to show any text in the non default charset -- otherwise
1281                    // it thinks it knows better than we do and always shows it
1282                    // in the default one
1283                    done = StreamIn(valueDos, encoding, selectionOnly);
1284                }
1285             }
1286         }
1287 #endif // !wxUSE_UNICODE
1288     }
1289 
1290     if ( !done )
1291 #endif // wxUSE_RICHEDIT
1292     {
1293         // in some cases we get 2 EN_CHANGE notifications after the SendMessage
1294         // call (this happens for plain EDITs with EM_REPLACESEL and under some
1295         // -- undetermined -- conditions with rich edit) and sometimes we don't
1296         // get any events at all (plain EDIT with WM_SETTEXT), so ensure that
1297         // we generate exactly one of them by ignoring all but the first one in
1298         // SendUpdateEvent() and generating one ourselves if we hadn't got any
1299         // notifications from Windows
1300         if ( !(flags & SetValue_SendEvent) )
1301             m_updatesCount = -2;        // suppress any update event
1302 
1303         UpdatesCountFilter ucf(m_updatesCount);
1304 
1305         // Remember the length of the text we're inserting so that
1306         // AdjustSpaceLimit() could adjust the limit to be big enough for it:
1307         // and also signal us whether it did it by resetting it to 0.
1308         gs_lenOfInsertedText.push(valueDos.length());
1309 
1310         ::SendMessage(GetHwnd(), selectionOnly ? EM_REPLACESEL : WM_SETTEXT,
1311                       // EM_REPLACESEL takes 1 to indicate the operation should be redoable
1312                       selectionOnly ? 1 : 0, wxMSW_CONV_LPARAM(valueDos));
1313 
1314         const int lenActuallyInserted = gs_lenOfInsertedText.top();
1315         gs_lenOfInsertedText.pop();
1316 
1317         if ( lenActuallyInserted == -1 )
1318         {
1319             // Text size limit has been hit and added text has been truncated.
1320             // But the max length has been increased by the EN_MAXTEXT message
1321             // handler, which also reset the top of the lengths stack to -1),
1322             // so we should be able to set it successfully now if we try again.
1323             if ( selectionOnly )
1324                 Undo();
1325 
1326             ::SendMessage(GetHwnd(), selectionOnly ? EM_REPLACESEL : WM_SETTEXT,
1327                           selectionOnly ? 1 : 0, wxMSW_CONV_LPARAM(valueDos));
1328         }
1329 
1330         if ( !ucf.GotUpdate() && (flags & SetValue_SendEvent) )
1331         {
1332             SendUpdateEvent();
1333         }
1334     }
1335 }
1336 
AppendText(const wxString & text)1337 void wxTextCtrl::AppendText(const wxString& text)
1338 {
1339     wxTextEntry::AppendText(text);
1340 
1341 #if wxUSE_RICHEDIT
1342     // don't do this if we're frozen, saves some time
1343     if ( !IsFrozen() && IsMultiLine() && GetRichVersion() > 1 )
1344     {
1345         ::SendMessage(GetHwnd(), WM_VSCROLL, SB_BOTTOM, (LPARAM)NULL);
1346     }
1347 #endif // wxUSE_RICHEDIT
1348 }
1349 
Clear()1350 void wxTextCtrl::Clear()
1351 {
1352     ::SetWindowText(GetHwnd(), wxEmptyString);
1353 
1354     if ( IsMultiLine() && !IsRich() )
1355     {
1356         // rich edit controls send EN_UPDATE from WM_SETTEXT handler themselves
1357         // but the normal ones don't -- make Clear() behaviour consistent by
1358         // always sending this event
1359         SendUpdateEvent();
1360     }
1361 }
1362 
EmulateKeyPress(const wxKeyEvent & event)1363 bool wxTextCtrl::EmulateKeyPress(const wxKeyEvent& event)
1364 {
1365     SetFocus();
1366 
1367     size_t lenOld = GetValue().length();
1368 
1369     wxUint32 code = event.GetRawKeyCode();
1370     ::keybd_event((BYTE)code, 0, 0 /* key press */, 0);
1371     ::keybd_event((BYTE)code, 0, KEYEVENTF_KEYUP, 0);
1372 
1373     // assume that any alphanumeric key changes the total number of characters
1374     // in the control - this should work in 99% of cases
1375     return GetValue().length() != lenOld;
1376 }
1377 
1378 // ----------------------------------------------------------------------------
1379 // Accessors
1380 // ----------------------------------------------------------------------------
1381 
SetInsertionPointEnd()1382 void wxTextCtrl::SetInsertionPointEnd()
1383 {
1384     // we must not do anything if the caret is already there because calling
1385     // SetInsertionPoint() thaws the controls if Freeze() had been called even
1386     // if it doesn't actually move the caret anywhere and so the simple fact of
1387     // doing it results in horrible flicker when appending big amounts of text
1388     // to the control in a few chunks (see DoAddText() test in the text sample)
1389     const wxTextPos lastPosition = GetLastPosition();
1390     if ( GetInsertionPoint() == lastPosition )
1391     {
1392         return;
1393     }
1394 
1395     SetInsertionPoint(lastPosition);
1396 }
1397 
GetInsertionPoint() const1398 long wxTextCtrl::GetInsertionPoint() const
1399 {
1400 #if wxUSE_RICHEDIT
1401     if ( IsRich() )
1402     {
1403         CHARRANGE range;
1404         range.cpMin = 0;
1405         range.cpMax = 0;
1406         ::SendMessage(GetHwnd(), EM_EXGETSEL, 0, (LPARAM) &range);
1407         return range.cpMin;
1408     }
1409 #endif // wxUSE_RICHEDIT
1410 
1411     return wxTextEntry::GetInsertionPoint();
1412 }
1413 
GetLastPosition() const1414 wxTextPos wxTextCtrl::GetLastPosition() const
1415 {
1416     if ( IsMultiLine() )
1417     {
1418 #if wxUSE_RICHEDIT
1419         if ( IsRich() )
1420         {
1421             GETTEXTLENGTHEX gtl;
1422             gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
1423             gtl.codepage = GetRichVersion() > 1 ? 1200 : CP_ACP;
1424             return ::SendMessage(GetHwnd(), EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
1425         }
1426         else
1427 #endif // wxUSE_RICHEDIT
1428         {
1429             return ::GetWindowTextLength(GetHwnd());
1430         }
1431     }
1432 
1433     return wxTextEntry::GetLastPosition();
1434 }
1435 
1436 // If the return values from and to are the same, there is no
1437 // selection.
GetSelection(long * from,long * to) const1438 void wxTextCtrl::GetSelection(long *from, long *to) const
1439 {
1440 #if wxUSE_RICHEDIT
1441     if ( IsRich() )
1442     {
1443         CHARRANGE charRange;
1444         ::SendMessage(GetHwnd(), EM_EXGETSEL, 0, (LPARAM) &charRange);
1445 
1446         *from = charRange.cpMin;
1447         *to = charRange.cpMax;
1448     }
1449     else
1450 #endif // !wxUSE_RICHEDIT
1451     {
1452         wxTextEntry::GetSelection(from, to);
1453     }
1454 }
1455 
1456 // ----------------------------------------------------------------------------
1457 // selection
1458 // ----------------------------------------------------------------------------
1459 
DoSetSelection(long from,long to,int flags)1460 void wxTextCtrl::DoSetSelection(long from, long to, int flags)
1461 {
1462     HWND hWnd = GetHwnd();
1463 
1464 #if wxUSE_RICHEDIT
1465     if ( IsRich() )
1466     {
1467         // if from and to are both -1, it means (in wxWidgets) that all text
1468         // should be selected, translate this into Windows convention
1469         if ( (from == -1) && (to == -1) )
1470         {
1471             from = 0;
1472         }
1473 
1474         CHARRANGE range;
1475         range.cpMin = from;
1476         range.cpMax = to;
1477         ::SendMessage(hWnd, EM_EXSETSEL, 0, (LPARAM)&range);
1478     }
1479     else
1480 #endif // wxUSE_RICHEDIT
1481     {
1482         wxTextEntry::DoSetSelection(from, to, flags);
1483     }
1484 
1485     if ( (flags & SetSel_Scroll) && !IsFrozen() )
1486     {
1487 #if wxUSE_RICHEDIT
1488         // richedit 3.0 (i.e. the version living in riched20.dll distributed
1489         // with Windows 2000 and beyond) doesn't honour EM_SCROLLCARET when
1490         // emulating richedit 2.0 unless the control has focus or ECO_NOHIDESEL
1491         // option is set (but it does work ok in richedit 1.0 mode...)
1492         //
1493         // so to make it work we either need to give focus to it here which
1494         // will probably create many problems (dummy focus events; window
1495         // containing the text control being brought to foreground
1496         // unexpectedly; ...) or to temporarily set ECO_NOHIDESEL which may
1497         // create other problems too -- and in fact it does because if we turn
1498         // on/off this style while appending the text to the control, the
1499         // vertical scrollbar never appears in it even if we append tons of
1500         // text and to work around this the only solution I found was to use
1501         // ES_DISABLENOSCROLL
1502         //
1503         // this is very ugly but I don't see any other way to make this work
1504         long style = 0;
1505         if ( GetRichVersion() > 1 )
1506         {
1507             if ( !HasFlag(wxTE_NOHIDESEL) )
1508             {
1509                 // setting ECO_NOHIDESEL also sets WS_VISIBLE and possibly
1510                 // others, remember the style so we can reset it later if needed
1511                 style = ::GetWindowLong(GetHwnd(), GWL_STYLE);
1512                 ::SendMessage(GetHwnd(), EM_SETOPTIONS,
1513                               ECOOP_OR, ECO_NOHIDESEL);
1514             }
1515             //else: everything is already ok
1516         }
1517 #endif // wxUSE_RICHEDIT
1518 
1519         ::SendMessage(hWnd, EM_SCROLLCARET, 0, (LPARAM)0);
1520 
1521 #if wxUSE_RICHEDIT
1522         // restore ECO_NOHIDESEL if we changed it
1523         if ( GetRichVersion() > 1 && !HasFlag(wxTE_NOHIDESEL) )
1524         {
1525             ::SendMessage(GetHwnd(), EM_SETOPTIONS,
1526                           ECOOP_AND, ~ECO_NOHIDESEL);
1527             if ( style != ::GetWindowLong(GetHwnd(), GWL_STYLE) )
1528                 ::SetWindowLong(GetHwnd(), GWL_STYLE, style);
1529         }
1530 #endif // wxUSE_RICHEDIT
1531     }
1532 }
1533 
1534 // ----------------------------------------------------------------------------
1535 // dirty status
1536 // ----------------------------------------------------------------------------
1537 
IsModified() const1538 bool wxTextCtrl::IsModified() const
1539 {
1540     return ::SendMessage(GetHwnd(), EM_GETMODIFY, 0, 0) != 0;
1541 }
1542 
MarkDirty()1543 void wxTextCtrl::MarkDirty()
1544 {
1545     ::SendMessage(GetHwnd(), EM_SETMODIFY, TRUE, 0);
1546 }
1547 
DiscardEdits()1548 void wxTextCtrl::DiscardEdits()
1549 {
1550     ::SendMessage(GetHwnd(), EM_SETMODIFY, FALSE, 0);
1551 }
1552 
1553 // ----------------------------------------------------------------------------
1554 // Positions <-> coords
1555 // ----------------------------------------------------------------------------
1556 
GetNumberOfLines() const1557 int wxTextCtrl::GetNumberOfLines() const
1558 {
1559     return (int)::SendMessage(GetHwnd(), EM_GETLINECOUNT, 0, 0);
1560 }
1561 
XYToPosition(long x,long y) const1562 long wxTextCtrl::XYToPosition(long x, long y) const
1563 {
1564     // This gets the char index for the _beginning_ of this line
1565     long charIndex;
1566     if ( IsMultiLine() )
1567     {
1568         charIndex = ::SendMessage(GetHwnd(), EM_LINEINDEX, y, 0);
1569         if ( charIndex == -1 )
1570             return -1;
1571     }
1572     else
1573     {
1574         if ( y != 0 )
1575             return -1;
1576 
1577         charIndex = 0;
1578     }
1579 
1580     // Line is identified by a character position!
1581     long lineLength = ::SendMessage(GetHwnd(), EM_LINELENGTH, charIndex, 0);
1582 
1583     // Notice that x == lineLength is still valid because it corresponds either
1584     // to the position of the LF at the end of any line except the last one or
1585     // to the last position, which is the position after the last character,
1586     // for the last line.
1587     if ( x > lineLength )
1588         return -1;
1589 
1590     return charIndex + x;
1591 }
1592 
PositionToXY(long pos,long * x,long * y) const1593 bool wxTextCtrl::PositionToXY(long pos, long *x, long *y) const
1594 {
1595     HWND hWnd = GetHwnd();
1596 
1597     // This gets the line number containing the character
1598     long lineNo;
1599 #if wxUSE_RICHEDIT
1600     if ( IsRich() )
1601     {
1602         lineNo = ::SendMessage(hWnd, EM_EXLINEFROMCHAR, 0, pos);
1603     }
1604     else
1605 #endif // wxUSE_RICHEDIT
1606     {
1607         lineNo = ::SendMessage(hWnd, EM_LINEFROMCHAR, pos, 0);
1608     }
1609 
1610     if ( lineNo == -1 )
1611     {
1612         // no such line
1613         return false;
1614     }
1615 
1616     // This gets the char index for the _beginning_ of this line
1617     long charIndex = ::SendMessage(hWnd, EM_LINEINDEX, lineNo, 0);
1618     if ( charIndex == -1 )
1619     {
1620         return false;
1621     }
1622 
1623     // Line is identified by a character position!
1624     // New lines characters are not included.
1625     long lineLength = ::SendMessage(hWnd, EM_LINELENGTH, charIndex, 0);
1626 
1627     // To simplify further calculations, make position relative
1628     // to the beginning of the line.
1629     pos -= charIndex;
1630 
1631     // We need to apply different approach for the position referring
1632     // to the last line so check if the next line exists.
1633     long charIndexNextLn = IsMultiLine() ?
1634                            ::SendMessage(hWnd, EM_LINEINDEX, lineNo + 1, 0)
1635                            : -1;
1636     if ( charIndexNextLn == -1 )
1637     {
1638         // No next line. Char position refers to the last line so
1639         // the length of the line obtained with EM_LINELENGTH is
1640         // correct because there are no new line characters at the end.
1641         if ( pos > lineLength )
1642         {
1643             return false;
1644         }
1645     }
1646     else
1647     {
1648         // Next line found. Char position doesn't refer to the last line
1649         // so we need to take into account new line characters which were
1650         // not counted by EM_LINELENGTH.
1651         long lineLengthFull = charIndexNextLn - charIndex;
1652         // (lineLengthFull - lineLength) can be 0 (for wrapped line),
1653         // 1 (for \r new line mark) or 2 (for \r\n new line mark).
1654         if ( pos > lineLengthFull )
1655         {
1656             return false;
1657         }
1658         if ( pos > lineLength )
1659         {
1660             // Char position refers to the second character of the CR/LF mark
1661             // and its physical X-Y position is the same as the position
1662             // of the first one.
1663             pos = lineLength;
1664         }
1665     }
1666 
1667     // The X position is therefore a char position in the line.
1668     if ( x )
1669         *x = pos;
1670     if ( y )
1671         *y = lineNo;
1672 
1673     return true;
1674 }
1675 
1676 wxTextCtrlHitTestResult
HitTest(const wxPoint & pt,long * posOut) const1677 wxTextCtrl::HitTest(const wxPoint& pt, long *posOut) const
1678 {
1679     // first get the position from Windows
1680     LPARAM lParam;
1681 
1682 #if wxUSE_RICHEDIT
1683     POINTL ptl;
1684     if ( IsRich() )
1685     {
1686         // for rich edit controls the position is passed iva the struct fields
1687         ptl.x = pt.x;
1688         ptl.y = pt.y;
1689         lParam = (LPARAM)&ptl;
1690     }
1691     else
1692 #endif // wxUSE_RICHEDIT
1693     {
1694         // for the plain ones, we are limited to 16 bit positions which are
1695         // combined in a single 32 bit value
1696         lParam = MAKELPARAM(pt.x, pt.y);
1697     }
1698 
1699     LRESULT pos = ::SendMessage(GetHwnd(), EM_CHARFROMPOS, 0, lParam);
1700 
1701     if ( pos == -1 )
1702     {
1703         // this seems to indicate an error...
1704         return wxTE_HT_UNKNOWN;
1705     }
1706 
1707 #if wxUSE_RICHEDIT
1708     if ( !IsRich() )
1709 #endif // wxUSE_RICHEDIT
1710     {
1711         // for plain EDIT controls the higher word contains something else
1712         pos = LOWORD(pos);
1713     }
1714 
1715 
1716     // next determine where it is relatively to our point: EM_CHARFROMPOS
1717     // always returns the closest character but we need to be more precise, so
1718     // double check that we really are where it pretends
1719     POINTL ptReal;
1720 
1721 #if wxUSE_RICHEDIT
1722     // FIXME: we need to distinguish between richedit 2 and 3 here somehow but
1723     //        we don't know how to do it
1724     if ( IsRich() )
1725     {
1726         ::SendMessage(GetHwnd(), EM_POSFROMCHAR, (WPARAM)&ptReal, pos);
1727     }
1728     else
1729 #endif // wxUSE_RICHEDIT
1730     {
1731         LRESULT lRc = ::SendMessage(GetHwnd(), EM_POSFROMCHAR, pos, 0);
1732 
1733         if ( lRc == -1 )
1734         {
1735             // this is apparently returned when pos corresponds to the last
1736             // position
1737             ptReal.x =
1738             ptReal.y = 0;
1739         }
1740         else
1741         {
1742             ptReal.x = LOWORD(lRc);
1743             ptReal.y = HIWORD(lRc);
1744         }
1745     }
1746 
1747     wxTextCtrlHitTestResult rc;
1748 
1749     if ( pt.y > ptReal.y + GetCharHeight() )
1750         rc = wxTE_HT_BELOW;
1751     else if ( pt.x > ptReal.x + GetCharWidth() )
1752         rc = wxTE_HT_BEYOND;
1753     else
1754         rc = wxTE_HT_ON_TEXT;
1755 
1756     if ( posOut )
1757         *posOut = pos;
1758 
1759     return rc;
1760 }
1761 
DoPositionToCoords(long pos) const1762 wxPoint wxTextCtrl::DoPositionToCoords(long pos) const
1763 {
1764     // FIXME: This code is broken for rich edit version 2.0 as it uses the same
1765     // API as plain edit i.e. the coordinates are returned directly instead of
1766     // filling the POINT passed as WPARAM with them but we can't distinguish
1767     // between 2.0 and 3.0 unfortunately (see also the use of EM_POSFROMCHAR
1768     // above).
1769 #if wxUSE_RICHEDIT
1770     if ( IsRich() )
1771     {
1772         POINT pt;
1773         LRESULT rc = ::SendMessage(GetHwnd(), EM_POSFROMCHAR, (WPARAM)&pt, pos);
1774         if ( rc != -1 )
1775             return wxPoint(pt.x, pt.y);
1776     }
1777     else
1778 #endif // wxUSE_RICHEDIT
1779     {
1780         LRESULT rc = ::SendMessage(GetHwnd(), EM_POSFROMCHAR, pos, 0);
1781         if ( rc == -1 )
1782         {
1783             // Finding coordinates for the last position of the control fails
1784             // in plain EDIT control, try to compensate for it by finding it
1785             // ourselves from the position of the previous character.
1786             if ( pos < GetLastPosition() )
1787             {
1788                 // It's not the expected correctable failure case so just fail.
1789                 return wxDefaultPosition;
1790             }
1791 
1792             if ( pos == 0 )
1793             {
1794                 // We're being asked the coordinates of the first (and last and
1795                 // only) position in an empty control. There is no way to get
1796                 // it directly with EM_POSFROMCHAR but EM_GETMARGINS returns
1797                 // the correct value for at least the horizontal offset.
1798                 rc = ::SendMessage(GetHwnd(), EM_GETMARGINS, 0, 0);
1799 
1800                 // Text control seems to effectively add 1 to margin.
1801                 return wxPoint(LOWORD(rc) + 1, 1);
1802             }
1803 
1804             // We do have a previous character, try to get its coordinates.
1805             rc = ::SendMessage(GetHwnd(), EM_POSFROMCHAR, pos - 1, 0);
1806             if ( rc == -1 )
1807             {
1808                 // If getting coordinates of the previous character failed as
1809                 // well, just give up.
1810                 return wxDefaultPosition;
1811             }
1812 
1813             wxString prevChar = GetRange(pos - 1, pos);
1814             wxSize prevCharSize = GetTextExtent(prevChar);
1815 
1816             if ( prevChar == wxT("\n" ))
1817             {
1818                 // 'pos' is at the beginning of a new line so its X coordinate
1819                 // should be the same as X coordinate of the first character of
1820                 // any other line while its Y coordinate will be approximately
1821                 // (but we can't compute it exactly...) one character height
1822                 // more than that of the previous character.
1823                 LRESULT coords0 = ::SendMessage(GetHwnd(), EM_POSFROMCHAR, 0, 0);
1824                 if ( coords0 == -1 )
1825                     return wxDefaultPosition;
1826 
1827                 rc = MAKELPARAM(LOWORD(coords0), HIWORD(rc) + prevCharSize.y);
1828             }
1829             else
1830             {
1831                 // Simple case: previous character is in the same line so this
1832                 // one is just after it.
1833                 rc += MAKELPARAM(prevCharSize.x, 0);
1834             }
1835         }
1836 
1837         // Notice that {LO,HI}WORD macros return WORDs, i.e. unsigned shorts,
1838         // while we want to have signed values here (the y coordinate of any
1839         // position above the first currently visible line is negative, for
1840         // example), hence the need for casts.
1841         return wxPoint(static_cast<short>(LOWORD(rc)),
1842                         static_cast<short>(HIWORD(rc)));
1843     }
1844 
1845     return wxDefaultPosition;
1846 }
1847 
1848 
1849 // ----------------------------------------------------------------------------
1850 //
1851 // ----------------------------------------------------------------------------
1852 
ShowPosition(long pos)1853 void wxTextCtrl::ShowPosition(long pos)
1854 {
1855     HWND hWnd = GetHwnd();
1856 
1857     // To scroll to a position, we pass the number of lines and characters
1858     // to scroll *by*. This means that we need to:
1859     // (1) Find the line position of the current line.
1860     // (2) Find the line position of pos.
1861     // (3) Scroll by (pos - current).
1862     // For now, ignore the horizontal scrolling.
1863 
1864     // Is this where scrolling is relative to - the line containing the caret?
1865     // Or is the first visible line??? Try first visible line.
1866 //    int currentLineLineNo1 = (int)::SendMessage(hWnd, EM_LINEFROMCHAR, -1, 0L);
1867 
1868     int currentLineLineNo = (int)::SendMessage(hWnd, EM_GETFIRSTVISIBLELINE, 0, 0);
1869 
1870     int specifiedLineLineNo = (int)::SendMessage(hWnd, EM_LINEFROMCHAR, pos, 0);
1871 
1872     int linesToScroll = specifiedLineLineNo - currentLineLineNo;
1873 
1874     if (linesToScroll != 0)
1875       ::SendMessage(hWnd, EM_LINESCROLL, 0, linesToScroll);
1876 }
1877 
GetLengthOfLineContainingPos(long pos) const1878 long wxTextCtrl::GetLengthOfLineContainingPos(long pos) const
1879 {
1880     return ::SendMessage(GetHwnd(), EM_LINELENGTH, pos, 0);
1881 }
1882 
GetLineLength(long lineNo) const1883 int wxTextCtrl::GetLineLength(long lineNo) const
1884 {
1885     long pos = XYToPosition(0, lineNo);
1886 
1887     if ( pos == -1 )
1888         return -1;
1889 
1890     return GetLengthOfLineContainingPos(pos);
1891 }
1892 
GetLineText(long lineNo) const1893 wxString wxTextCtrl::GetLineText(long lineNo) const
1894 {
1895     size_t len = (size_t)GetLineLength(lineNo) + 1;
1896 
1897     // there must be at least enough place for the length WORD in the
1898     // buffer
1899     len += sizeof(WORD);
1900 
1901     wxString str;
1902     {
1903         wxStringBufferLength tmp(str, len);
1904         wxChar *buf = tmp;
1905 
1906         *(WORD *)buf = (WORD)len;
1907         len = (size_t)::SendMessage(GetHwnd(), EM_GETLINE, lineNo, (LPARAM)buf);
1908 
1909 #if wxUSE_RICHEDIT
1910         if ( IsRich() )
1911         {
1912             // remove the '\r' returned by the rich edit control, the user code
1913             // should never see it
1914             if ( buf[len - 2] == wxT('\r') && buf[len - 1] == wxT('\n') )
1915             {
1916                 // richedit 1.0 uses "\r\n" as line terminator, so remove "\r"
1917                 // here and "\n" below
1918                 buf[len - 2] = wxT('\n');
1919                 len--;
1920             }
1921             else if ( buf[len - 1] == wxT('\r') )
1922             {
1923                 // richedit 2.0+ uses only "\r", replace it with "\n"
1924                 buf[len - 1] = wxT('\n');
1925             }
1926         }
1927 #endif // wxUSE_RICHEDIT
1928 
1929         // remove the '\n' at the end, if any (this is how this function is
1930         // supposed to work according to the docs)
1931         if ( len && buf[len - 1] == wxT('\n') )
1932         {
1933             len--;
1934         }
1935 
1936         buf[len] = 0;
1937         tmp.SetLength(len);
1938     }
1939 
1940     return str;
1941 }
1942 
SetMaxLength(unsigned long len)1943 void wxTextCtrl::SetMaxLength(unsigned long len)
1944 {
1945 #if wxUSE_RICHEDIT
1946     if ( IsRich() )
1947     {
1948         ::SendMessage(GetHwnd(), EM_EXLIMITTEXT, 0, len ? len : 0x7fffffff);
1949     }
1950     else
1951 #endif // wxUSE_RICHEDIT
1952     {
1953         wxTextEntry::SetMaxLength(len);
1954     }
1955 }
1956 
1957 // ----------------------------------------------------------------------------
1958 // RTL support
1959 // ----------------------------------------------------------------------------
1960 
SetLayoutDirection(wxLayoutDirection dir)1961 void wxTextCtrl::SetLayoutDirection(wxLayoutDirection dir)
1962 {
1963     // We only need to handle this specifically for plain EDIT controls, rich
1964     // edit ones behave like any other window.
1965     if ( IsRich() )
1966     {
1967         wxTextCtrlBase::SetLayoutDirection(dir);
1968     }
1969     else
1970     {
1971         if ( wxUpdateEditLayoutDirection(GetHwnd(), dir) )
1972         {
1973             // Update text layout by forcing the control to redo it, a simple
1974             // Refresh() is not enough.
1975             SendSizeEvent();
1976             Refresh();
1977         }
1978     }
1979 }
1980 
GetLayoutDirection() const1981 wxLayoutDirection wxTextCtrl::GetLayoutDirection() const
1982 {
1983     // Just as above, we need to handle plain EDIT controls specially.
1984     return IsRich() ? wxTextCtrlBase::GetLayoutDirection()
1985                     : wxGetEditLayoutDirection(GetHwnd());
1986 }
1987 
1988 // ----------------------------------------------------------------------------
1989 // Undo/redo
1990 // ----------------------------------------------------------------------------
1991 
Redo()1992 void wxTextCtrl::Redo()
1993 {
1994 #if wxUSE_RICHEDIT
1995     if ( GetRichVersion() > 1 )
1996     {
1997         ::SendMessage(GetHwnd(), EM_REDO, 0, 0);
1998         return;
1999     }
2000 #endif // wxUSE_RICHEDIT
2001 
2002     wxTextEntry::Redo();
2003 }
2004 
CanRedo() const2005 bool wxTextCtrl::CanRedo() const
2006 {
2007 #if wxUSE_RICHEDIT
2008     if ( GetRichVersion() > 1 )
2009         return ::SendMessage(GetHwnd(), EM_CANREDO, 0, 0) != 0;
2010 #endif // wxUSE_RICHEDIT
2011 
2012     return wxTextEntry::CanRedo();
2013 }
2014 
2015 // ----------------------------------------------------------------------------
2016 // caret handling (Windows only)
2017 // ----------------------------------------------------------------------------
2018 
ShowNativeCaret(bool show)2019 bool wxTextCtrl::ShowNativeCaret(bool show)
2020 {
2021     if ( show != m_isNativeCaretShown )
2022     {
2023         if ( !(show ? ::ShowCaret(GetHwnd()) : ::HideCaret(GetHwnd())) )
2024         {
2025             // not an error, may simply indicate that it's not shown/hidden
2026             // yet (i.e. it had been hidden/shown 2 times before)
2027             return false;
2028         }
2029 
2030         m_isNativeCaretShown = show;
2031     }
2032 
2033     return true;
2034 }
2035 
2036 // ----------------------------------------------------------------------------
2037 // implementation details
2038 // ----------------------------------------------------------------------------
2039 
Command(wxCommandEvent & event)2040 void wxTextCtrl::Command(wxCommandEvent & event)
2041 {
2042     SetValue(event.GetString());
2043     ProcessCommand (event);
2044 }
2045 
OnDropFiles(wxDropFilesEvent & event)2046 void wxTextCtrl::OnDropFiles(wxDropFilesEvent& event)
2047 {
2048     // By default, load the first file into the text window.
2049     if (event.GetNumberOfFiles() > 0)
2050     {
2051         LoadFile(event.GetFiles()[0]);
2052     }
2053 }
2054 
2055 // ----------------------------------------------------------------------------
2056 // kbd input processing
2057 // ----------------------------------------------------------------------------
2058 
MSWShouldPreProcessMessage(WXMSG * msg)2059 bool wxTextCtrl::MSWShouldPreProcessMessage(WXMSG* msg)
2060 {
2061     // check for our special keys here: if we don't do it and the parent frame
2062     // uses them as accelerators, they wouldn't work at all, so we disable
2063     // usual preprocessing for them
2064     if ( msg->message == WM_KEYDOWN )
2065     {
2066         const WPARAM vkey = msg->wParam;
2067         if ( HIWORD(msg->lParam) & KF_ALTDOWN )
2068         {
2069             // Alt-Backspace is accelerator for "Undo"
2070             if ( vkey == VK_BACK )
2071                 return false;
2072         }
2073         else // no Alt
2074         {
2075             // we want to process some Ctrl-foo and Shift-bar but no key
2076             // combinations without either Ctrl or Shift nor with both of them
2077             // pressed
2078             const int ctrl = wxIsCtrlDown(),
2079                       shift = wxIsShiftDown();
2080             switch ( ctrl + shift )
2081             {
2082                 default:
2083                     wxFAIL_MSG( wxT("how many modifiers have we got?") );
2084                     wxFALLTHROUGH;
2085 
2086                 case 0:
2087                     switch ( vkey )
2088                     {
2089                         case VK_RETURN:
2090                             // This one is only special for multi line controls.
2091                             if ( !IsMultiLine() )
2092                                 break;
2093                             wxFALLTHROUGH;
2094 
2095                         case VK_DELETE:
2096                         case VK_HOME:
2097                         case VK_END:
2098                             return false;
2099                     }
2100                     wxFALLTHROUGH;
2101                 case 2:
2102                     break;
2103 
2104                 case 1:
2105                     // either Ctrl or Shift pressed
2106                     if ( ctrl )
2107                     {
2108                         switch ( vkey )
2109                         {
2110                             case 'A':
2111                             case 'C':
2112                             case 'V':
2113                             case 'X':
2114                             case VK_INSERT:
2115                             case VK_DELETE:
2116                             case VK_HOME:
2117                             case VK_END:
2118                                 return false;
2119 
2120                             case VK_BACK:
2121                                 if ( MSWNeedsToHandleCtrlBackspace() )
2122                                     return false;
2123                         }
2124                     }
2125                     else // Shift is pressed
2126                     {
2127                         if ( vkey == VK_INSERT || vkey == VK_DELETE )
2128                             return false;
2129                     }
2130             }
2131         }
2132     }
2133 
2134     return wxControl::MSWShouldPreProcessMessage(msg);
2135 }
2136 
OnChar(wxKeyEvent & event)2137 void wxTextCtrl::OnChar(wxKeyEvent& event)
2138 {
2139     switch ( event.GetKeyCode() )
2140     {
2141         case WXK_RETURN:
2142         case WXK_NUMPAD_ENTER:
2143             // Single line controls only get this key code if they have
2144             // wxTE_PROCESS_ENTER style, but multiline ones always get it
2145             // because they need it for themselves. However we shouldn't
2146             // generate wxEVT_TEXT_ENTER for the controls without this style,
2147             // so test for it explicitly.
2148             if ( HasFlag(wxTE_PROCESS_ENTER) )
2149             {
2150                 wxCommandEvent evt(wxEVT_TEXT_ENTER, m_windowId);
2151                 InitCommandEvent(evt);
2152                 evt.SetString(GetValue());
2153                 if ( HandleWindowEvent(evt) )
2154                     return;
2155             }
2156             break;
2157 
2158         case WXK_TAB:
2159             // ok, so this is getting absolutely ridiculous but I don't see
2160             // any other way to fix this bug: when a multiline text control is
2161             // inside a wxFrame, we need to generate the navigation event as
2162             // otherwise nothing happens at all, but when the same control is
2163             // created inside a dialog, IsDialogMessage() *does* switch focus
2164             // all by itself and so if we do it here as well, it is advanced
2165             // twice and goes to the next control... to prevent this from
2166             // happening we're doing this ugly check, the logic being that if
2167             // we don't have focus then it had been already changed to the next
2168             // control
2169             //
2170             // the right thing to do would, of course, be to understand what
2171             // the hell is IsDialogMessage() doing but this is beyond my feeble
2172             // forces at the moment unfortunately
2173             if ( !(m_windowStyle & wxTE_PROCESS_TAB) || !IsEditable() )
2174             {
2175                 if ( ::GetFocus() == GetHwnd() )
2176                 {
2177                     int flags = wxNavigationKeyEvent::FromTab;
2178                     if (!event.ShiftDown())
2179                         flags |= wxNavigationKeyEvent::IsForward ;
2180                     if (event.ControlDown())
2181                         flags |= wxNavigationKeyEvent::WinChange ;
2182                     if (Navigate(flags))
2183                         return;
2184                 }
2185             }
2186             else
2187             {
2188                 // Insert tab since calling the default Windows handler
2189                 // doesn't seem to do it
2190                 WriteText(wxT("\t"));
2191                 return;
2192             }
2193             break;
2194     }
2195 
2196     // no, we didn't process it
2197     event.Skip();
2198 }
2199 
2200 #if wxUSE_OLE
2201 
MSWProcessSpecialKey(wxKeyEvent & event)2202 void wxTextCtrl::MSWProcessSpecialKey(wxKeyEvent& event)
2203 {
2204     // It is not a good idea, in general, to manually call another event
2205     // handler, but here we need to do exactly the same thing as in OnChar()
2206     // above, so it doesn't seem to make much sense to add another function to
2207     // forward to when we can just call it directly.
2208     OnChar(event);
2209 }
2210 
2211 #endif // wxUSE_OLE
2212 
MSWDeleteWordBack()2213 void wxTextCtrl::MSWDeleteWordBack()
2214 {
2215     // Surprisingly the behaviour of Ctrl+Backspace is different in all three
2216     // cases where it's supported by MSW itself:
2217     //
2218     //  1. Rich edit controls simply ignore selection and handle it as usual.
2219     //  2. Plain edit controls don't do anything when there is selection.
2220     //  3. Notepad in Windows 10 1809 and later deletes just the selection.
2221     //
2222     // The latter behaviour seems the most useful, so do it like this here too.
2223     if ( HasSelection() )
2224     {
2225         RemoveSelection();
2226         return;
2227     }
2228 
2229     // This variable contains one end of the range to delete, the rest of this
2230     // function is concerned with finding the starting end of this range.
2231     const long end = GetInsertionPoint();
2232 
2233     long col, line;
2234     if ( !PositionToXY(end, &col, &line) )
2235         return;
2236 
2237     // We stop at the start of line, so remember it.
2238     const long start = XYToPosition(0, line);
2239 
2240     const wxString& text = GetLineText(line);
2241 
2242     // The way it works in rich text controls or when SHAutoComplete() is used
2243     // is that it deletes everything until the first span of alphanumeric
2244     // characters it finds (moving backwards). But the implementation of the
2245     // same shortcut in in notepad in Windows 10 versions 1809 and later
2246     // doesn't behave in quite the same way and doesn't handle alphanumeric
2247     // characters specially, i.e. it just stops on space. This seems more
2248     // useful and simpler to implement, and it probably will become standard in
2249     // the future, so do it like this here too.
2250 
2251     // First skip all space starting from the character to the left of the
2252     // current one.
2253     long current = end;
2254     for ( ;; )
2255     {
2256         if ( current == start )
2257         {
2258             // When there is nothing but spaces to the left until the start of
2259             // line, we need to delete these spaces (if any) as well as the new
2260             // line separating this line from the previous one (if any).
2261             if ( line > 0 )
2262             {
2263                 // This function is only used with plain EDITs which use "\r\n"
2264                 // and so we need to subtract 2 to account for the new line.
2265                 current -= 2;
2266             }
2267 
2268             break;
2269         }
2270 
2271         // We start from the previous character.
2272         --current;
2273 
2274         // Did we find the end of the previous "word"?
2275         if ( text[current - start] != ' ' )
2276         {
2277             for ( ;; )
2278             {
2279                 if ( current == start )
2280                 {
2281                     // We don't delete the new line in this case, as we're going to
2282                     // delete some non-spaces in this line.
2283                     break;
2284                 }
2285 
2286                 --current;
2287 
2288                 if ( text[current - start] == ' ' )
2289                 {
2290                     // Don't delete the space itself.
2291                     ++current;
2292                     break;
2293                 }
2294             }
2295 
2296             break;
2297         }
2298     }
2299 
2300     Remove(current, end);
2301 }
2302 
MSWNeedsToHandleCtrlBackspace() const2303 bool wxTextCtrl::MSWNeedsToHandleCtrlBackspace() const
2304 {
2305     // We want to handle the undocumented Ctrl+Backspace shortcut only if it's
2306     // not handled by the control itself, which is a bit tricky because it's
2307     // normally only handled by rich edit controls, but plain EDIT ones may
2308     // also handle it if they use SHAutoComplete().
2309     return !HasFlag(wxTE_READONLY) &&
2310                 !IsRich() &&
2311                     !MSWUsesStandardAutoComplete();
2312 }
2313 
OnKeyDown(wxKeyEvent & event)2314 void wxTextCtrl::OnKeyDown(wxKeyEvent& event)
2315 {
2316     // Handle Ctrl+Backspace if necessary: this is not a documented standard
2317     // shortcut, but it's a de facto standard and people expect it to work.
2318     if ( MSWNeedsToHandleCtrlBackspace() &&
2319                 event.GetModifiers() == wxMOD_CONTROL &&
2320                     event.GetKeyCode() == WXK_BACK )
2321     {
2322         MSWDeleteWordBack();
2323         return;
2324     }
2325 
2326     // richedit control doesn't send WM_PASTE, WM_CUT and WM_COPY messages
2327     // when Ctrl-V, X or C is pressed and this prevents wxClipboardTextEvent
2328     // from working. So we work around it by intercepting these shortcuts
2329     // ourselves and emitting clipboard events (which richedit will handle,
2330     // so everything works as before, including pasting of rich text):
2331     if ( IsRich() )
2332     {
2333         if ( event.GetModifiers() == wxMOD_CONTROL )
2334         {
2335             switch ( event.GetKeyCode() )
2336             {
2337                 case 'C':
2338                 case WXK_INSERT:
2339                     Copy();
2340                     return;
2341                 case 'X':
2342                     Cut();
2343                     return;
2344                 case 'V':
2345                     Paste();
2346                     return;
2347                 default:
2348                     break;
2349             }
2350         }
2351         else if ( event.GetModifiers() == wxMOD_SHIFT )
2352         {
2353             switch ( event.GetKeyCode() )
2354             {
2355                 case WXK_INSERT:
2356                     Paste();
2357                     return;
2358                 case WXK_DELETE:
2359                     Cut();
2360                     return;
2361             }
2362         }
2363     }
2364 
2365     if ( IsMultiLine() )
2366     {
2367         // Default window procedure of multiline edit controls posts WM_CLOSE to
2368         // the parent window when it gets Escape key press for some reason, prevent
2369         // it from doing this as this resulted in dialog boxes being closed on
2370         // Escape even when they shouldn't be (we do handle Escape ourselves
2371         // correctly in the situations when it should close them).
2372         if ( event.GetKeyCode() == WXK_ESCAPE )
2373             return;
2374 
2375         // We also handle Ctrl-A as the native EDIT control doesn't do it by
2376         // default (but RICHEDIT one does, so there is no need to check for it
2377         // in the switch above), however it's a de facto standard accelerator
2378         // and people expect it to work.
2379         if ( event.GetModifiers() == wxMOD_CONTROL && event.GetKeyCode() == 'A' )
2380         {
2381             SelectAll();
2382             return;
2383         }
2384     }
2385 
2386     // no, we didn't process it
2387     event.Skip();
2388 }
2389 
Paste()2390 void wxTextCtrl::Paste()
2391 {
2392     // Before pasting, check that the pasted text will fit, unless an explicit
2393     // maximum length was set, to avoid only pasting some part of it.
2394     //
2395     // Note that rich text controls do not send WM_PASTE, so we can't do it in
2396     // response to it, but we could handle EN_PROTECTED (after requesting it by
2397     // specifying ENM_PROTECTED in EM_SETEVENTMASK argument) and check for the
2398     // message being WM_PASTE there, but this doesn't seem to be better than
2399     // the simpler approach used here.
2400     if ( IsRich() )
2401     {
2402         AdjustMaxLengthBeforePaste();
2403     }
2404 
2405     wxTextCtrlBase::Paste();
2406 }
2407 
2408 bool
MSWHandleMessage(WXLRESULT * rc,WXUINT nMsg,WXWPARAM wParam,WXLPARAM lParam)2409 wxTextCtrl::MSWHandleMessage(WXLRESULT *rc,
2410                              WXUINT nMsg,
2411                              WXWPARAM wParam,
2412                              WXLPARAM lParam)
2413 {
2414     bool processed = wxTextCtrlBase::MSWHandleMessage(rc, nMsg, wParam, lParam);
2415 
2416     switch ( nMsg )
2417     {
2418         case WM_CHAR:
2419             // Handle the special case of "Enter" key: the user code needs to specify
2420             // wxTE_PROCESS_ENTER style to get it in the first place, but if this flag
2421             // is used, then even if the wxEVT_TEXT_ENTER handler skips the event, the
2422             // normal action of this key is not performed because IsDialogMessage() is
2423             // not called and, also, an annoying beep is generated by EDIT default
2424             // WndProc.
2425             //
2426             // Fix these problems by explicitly performing the default function of this
2427             // key (which would be done by MSWProcessMessage() if we didn't have
2428             // wxTE_PROCESS_ENTER) and preventing the default WndProc from getting it.
2429             if ( !processed && wParam == VK_RETURN && IsSingleLine() )
2430             {
2431                 if ( ClickDefaultButtonIfPossible() )
2432                     processed = true;
2433             }
2434             break;
2435 
2436         case WM_GETDLGCODE:
2437             {
2438                 // Ensure that the result value is initialized even if the base
2439                 // class didn't handle WM_GETDLGCODE but just update the value
2440                 // returned by it if it did handle it.
2441                 if ( !processed )
2442                 {
2443                     *rc = MSWDefWindowProc(nMsg, wParam, lParam);
2444                     processed = true;
2445                 }
2446 
2447                 // we always want the chars and the arrows: the arrows for
2448                 // navigation and the chars because we want Ctrl-C to work even
2449                 // in a read only control
2450                 long lDlgCode = DLGC_WANTCHARS | DLGC_WANTARROWS;
2451 
2452                 // we may have several different cases:
2453                 // 1. normal: both TAB and ENTER are used for navigation
2454                 // 2. ctrl wants TAB for itself: ENTER is used to pass to
2455                 //    the next control in the dialog
2456                 // 3. ctrl wants ENTER for itself: TAB is used for dialog
2457                 //    navigation
2458                 // 4. ctrl wants both TAB and ENTER: Ctrl-ENTER is used to
2459                 //    go to the next control (we need some way to do it)
2460 
2461                 // multiline controls should always get ENTER for themselves
2462                 if ( HasFlag(wxTE_PROCESS_ENTER) || HasFlag(wxTE_MULTILINE) )
2463                     lDlgCode |= DLGC_WANTMESSAGE;
2464 
2465                 if ( HasFlag(wxTE_PROCESS_TAB) )
2466                     lDlgCode |= DLGC_WANTTAB;
2467 
2468                 *rc |= lDlgCode;
2469 
2470                 if ( IsMultiLine() )
2471                 {
2472                     // The presence of this style, coming from the default EDIT
2473                     // WndProc, indicates that the control contents should be
2474                     // selected when it gets focus, but we don't want this to
2475                     // happen for the multiline controls, so clear it.
2476                     *rc &= ~DLGC_HASSETSEL;
2477                 }
2478             }
2479             break;
2480 
2481 #if wxUSE_MENUS
2482         case WM_SETCURSOR:
2483             // rich text controls seem to have a bug and don't change the
2484             // cursor to the standard arrow one from the I-beam cursor usually
2485             // used by them even when a popup menu is shown (this works fine
2486             // for plain EDIT controls though), so explicitly work around this
2487             if ( IsRich() )
2488             {
2489                 // wxCurrentPopupMenu stores the popup menu that will receive
2490                 // WM_COMMAND, but it may be non-NULL even when the underlying
2491                 // native menu is no longer shown. Use ::IsMenu() to check whether
2492                 // the menu still exists.
2493                 extern wxMenu *wxCurrentPopupMenu;
2494                 if ( wxCurrentPopupMenu &&
2495                         wxCurrentPopupMenu->GetInvokingWindow() == this &&
2496                         ::IsMenu(GetHmenuOf(wxCurrentPopupMenu)) )
2497                     ::SetCursor(GetHcursorOf(*wxSTANDARD_CURSOR));
2498             }
2499             break;
2500 #endif // wxUSE_MENUS
2501 
2502         case WM_PASTE:
2503             // Note that we get this message for plain EDIT controls only, rich
2504             // controls are dealt with in our own Paste().
2505             AdjustMaxLengthBeforePaste();
2506             break;
2507     }
2508 
2509     return processed;
2510 }
2511 
2512 // ----------------------------------------------------------------------------
2513 // text control event processing
2514 // ----------------------------------------------------------------------------
2515 
SendUpdateEvent()2516 bool wxTextCtrl::SendUpdateEvent()
2517 {
2518     switch ( m_updatesCount )
2519     {
2520         case 0:
2521             // remember that we've got an update
2522             m_updatesCount++;
2523             break;
2524 
2525         case 1:
2526             // we had already sent one event since the last control modification
2527             return false;
2528 
2529         default:
2530             wxFAIL_MSG( wxT("unexpected wxTextCtrl::m_updatesCount value") );
2531             wxFALLTHROUGH;
2532 
2533         case -1:
2534             // we hadn't updated the control ourselves, this event comes from
2535             // the user, don't need to ignore it nor update the count
2536             break;
2537 
2538         case -2:
2539             // the control was updated programmatically and we do NOT want to
2540             // send events
2541             return false;
2542     }
2543 
2544     return SendTextUpdatedEvent();
2545 }
2546 
MSWCommand(WXUINT param,WXWORD WXUNUSED (id))2547 bool wxTextCtrl::MSWCommand(WXUINT param, WXWORD WXUNUSED(id))
2548 {
2549     switch ( param )
2550     {
2551         case EN_CHANGE:
2552             SendUpdateEvent();
2553             break;
2554 
2555         case EN_MAXTEXT:
2556             // the text size limit has been hit -- try to increase it
2557             if ( !AdjustSpaceLimit() )
2558             {
2559                 wxCommandEvent event(wxEVT_TEXT_MAXLEN, m_windowId);
2560                 InitCommandEvent(event);
2561                 event.SetString(GetValue());
2562                 ProcessCommand(event);
2563             }
2564             break;
2565 
2566             // the other edit notification messages are not processed (or, in
2567             // the case of EN_{SET/KILL}FOCUS were already handled at WM_SET/
2568             // KILLFOCUS level)
2569         default:
2570             return false;
2571     }
2572 
2573     // processed
2574     return true;
2575 }
2576 
MSWControlColor(WXHDC hDC,WXHWND hWnd)2577 WXHBRUSH wxTextCtrl::MSWControlColor(WXHDC hDC, WXHWND hWnd)
2578 {
2579     if ( !IsThisEnabled() && !HasFlag(wxTE_MULTILINE) )
2580         return MSWControlColorDisabled(hDC);
2581 
2582     return wxTextCtrlBase::MSWControlColor(hDC, hWnd);
2583 }
2584 
HasSpaceLimit(unsigned int * len) const2585 bool wxTextCtrl::HasSpaceLimit(unsigned int *len) const
2586 {
2587     // HACK: we try to automatically extend the limit for the amount of text
2588     //       to allow (interactively) entering more than 64Kb of text under
2589     //       Win9x but we shouldn't reset the text limit which was previously
2590     //       set explicitly with SetMaxLength()
2591     //
2592     //       Unfortunately there is no EM_GETLIMITTEXTSETBYUSER and so we don't
2593     //       know the limit we set (if any). We could solve this by storing the
2594     //       limit we set in wxTextCtrl but to save space we prefer to simply
2595     //       test here the actual limit value: we consider that SetMaxLength()
2596     //       can only be called for small values while EN_MAXTEXT is only sent
2597     //       for large values (in practice the default limit seems to be 30000
2598     //       but make it smaller just to be on the safe side)
2599     *len = ::SendMessage(GetHwnd(), EM_GETLIMITTEXT, 0, 0);
2600     return *len < 10001;
2601 
2602 }
2603 
AdjustSpaceLimit()2604 bool wxTextCtrl::AdjustSpaceLimit()
2605 {
2606     unsigned int limit;
2607     if ( HasSpaceLimit(&limit) )
2608         return false;
2609 
2610     unsigned int len = ::GetWindowTextLength(GetHwnd());
2611     if ( len >= limit )
2612     {
2613         unsigned long increaseBy;
2614 
2615         // We need to increase the size of the buffer and to avoid increasing
2616         // it too many times make sure that we make it at least big enough to
2617         // fit all the text we are currently inserting into the control, if
2618         // we're inserting any, i.e. if we're called from DoWriteText().
2619         if ( !gs_lenOfInsertedText.empty() )
2620         {
2621             increaseBy = gs_lenOfInsertedText.top();
2622 
2623             // Indicate to the caller that we increased the limit.
2624             gs_lenOfInsertedText.top() = -1;
2625         }
2626         else // Not inserting text, must be text actually typed by user.
2627         {
2628             increaseBy = 0;
2629         }
2630 
2631         // But also increase it by at least 32KB chunks -- again, to avoid
2632         // doing it too often -- and round it up to 32KB in any case.
2633         if ( increaseBy < 0x8000 )
2634             increaseBy = 0x8000;
2635         else
2636             increaseBy = (increaseBy + 0x7fff) & ~0x7fff;
2637 
2638         SetMaxLength(len + increaseBy);
2639     }
2640 
2641     // we changed the limit
2642     return true;
2643 }
2644 
AdjustMaxLengthBeforePaste()2645 void wxTextCtrl::AdjustMaxLengthBeforePaste()
2646 {
2647 #if wxUSE_CLIPBOARD
2648     // We only need to do this for multi line controls, single lines should
2649     // never receive more text than fits into them by default anyhow.
2650     if ( IsSingleLine() )
2651         return;
2652 
2653     // Also don't override an explicitly set limit.
2654     unsigned int limit;
2655     if ( HasSpaceLimit(&limit) )
2656         return;
2657 
2658     // Otherwise check if we have enough space for clipboard data. We only do
2659     // it for plain text because this is all we know how to handle here.
2660     wxClipboardLocker lock;
2661     wxTextDataObject textData;
2662     if ( !wxTheClipboard->GetData(textData) )
2663         return;
2664 
2665     // Unfortunately we can't just get the length directly because we need to
2666     // convert EOLs, otherwise our calculation of the required length could be
2667     // way off when there are many lines.
2668     const unsigned long lenPasted =
2669          wxTextFile::Translate(textData.GetText(), wxTextFileType_Dos).length();
2670 
2671     long from, to;
2672     GetSelection(&from, &to);
2673     const unsigned long lenSel = to - from;
2674 
2675     const unsigned long lenCurrent = GetLastPosition();
2676 
2677     // We need enough space for all the current text and all the new
2678     // text, but the selection will be replaced.
2679     const unsigned long lenNeeded = lenCurrent - lenSel + lenPasted;
2680     if ( lenNeeded >= limit )
2681     {
2682         SetMaxLength(lenNeeded);
2683     }
2684 #endif // wxUSE_CLIPBOARD
2685 }
2686 
AcceptsFocusFromKeyboard() const2687 bool wxTextCtrl::AcceptsFocusFromKeyboard() const
2688 {
2689     // we don't want focus if we can't be edited unless we're a multiline
2690     // control because then it might be still nice to get focus from keyboard
2691     // to be able to scroll it without mouse
2692     return (IsEditable() || IsMultiLine()) && wxControl::AcceptsFocus();
2693 }
2694 
DoGetBestSize() const2695 wxSize wxTextCtrl::DoGetBestSize() const
2696 {
2697     return DoGetSizeFromTextSize( FromDIP(DEFAULT_ITEM_WIDTH) );
2698 }
2699 
DoGetSizeFromTextSize(int xlen,int ylen) const2700 wxSize wxTextCtrl::DoGetSizeFromTextSize(int xlen, int ylen) const
2701 {
2702     int cy;
2703     wxFont font = GetFont();
2704     font.WXAdjustToPPI(GetDPI());
2705     wxGetCharSize(GetHWND(), NULL, &cy, font);
2706 
2707     DWORD wText = FromDIP(1);
2708     ::SystemParametersInfo(SPI_GETCARETWIDTH, 0, &wText, 0);
2709     wText += xlen;
2710 
2711     int hText = cy;
2712     if ( m_windowStyle & wxTE_MULTILINE )
2713     {
2714         // add space for vertical scrollbar
2715         if ( !(m_windowStyle & wxTE_NO_VSCROLL) )
2716             wText += wxGetSystemMetrics(SM_CXVSCROLL, m_parent);
2717 
2718         if ( ylen <= 0 )
2719         {
2720             hText *= wxMax(wxMin(GetNumberOfLines(), 10), 2);
2721             // add space for horizontal scrollbar
2722             if ( m_windowStyle & wxHSCROLL )
2723                 hText += wxGetSystemMetrics(SM_CYHSCROLL, m_parent);
2724         }
2725     }
2726     // for single line control cy (height + external leading) is ok
2727     else
2728     {
2729         // Add the margins we have previously set
2730         wxPoint marg( GetMargins() );
2731         wText += wxMax(0, marg.x);
2732         hText += wxMax(0, marg.y);
2733     }
2734 
2735     // Text controls without border are special and have the same height as
2736     // static labels (they also have the same appearance when they're disable
2737     // and are often used as a sort of copyable to the clipboard label so it's
2738     // important that they have the same height as the normal labels to not
2739     // stand out).
2740     if ( !HasFlag(wxBORDER_NONE) )
2741     {
2742         wText += FromDIP(9); // borders and inner margins
2743 
2744         // we have to add the adjustments for the control height only once, not
2745         // once per line, so do it after multiplication above
2746         hText += EDIT_HEIGHT_FROM_CHAR_HEIGHT(cy) - cy;
2747     }
2748 
2749     // Perhaps the user wants something different from CharHeight, or ylen
2750     // is used as the height of a multiline text.
2751     if ( ylen > 0 )
2752         hText += ylen - GetCharHeight();
2753 
2754     return wxSize(wText, hText);
2755 }
2756 
DoMoveWindow(int x,int y,int width,int height)2757 void wxTextCtrl::DoMoveWindow(int x, int y, int width, int height)
2758 {
2759     // We reset the text of single line controls each time their width changes
2760     // because they don't adjust their horizontal offset on their own and there
2761     // doesn't seem to be any way to convince them to do it other than by just
2762     // setting the text again, see #18268.
2763     const bool resetText = IsSingleLine() && !IsShownOnScreen();
2764     int oldWidth = -1;
2765     if ( resetText )
2766     {
2767         oldWidth = GetSize().x;
2768     }
2769 
2770     wxTextCtrlBase::DoMoveWindow(x, y, width, height);
2771 
2772     if ( resetText && GetSize().x != oldWidth )
2773     {
2774         // We need to use DoWriteText() to avoid our own optimization in
2775         // ChangeValue() which does nothing when the text doesn't really
2776         // change.
2777         DoWriteText(DoGetValue(), 0 /* no flags for no events */);
2778     }
2779 }
2780 
2781 // ----------------------------------------------------------------------------
2782 // standard handlers for standard edit menu events
2783 // ----------------------------------------------------------------------------
2784 
OnCut(wxCommandEvent & WXUNUSED (event))2785 void wxTextCtrl::OnCut(wxCommandEvent& WXUNUSED(event))
2786 {
2787     Cut();
2788 }
2789 
OnCopy(wxCommandEvent & WXUNUSED (event))2790 void wxTextCtrl::OnCopy(wxCommandEvent& WXUNUSED(event))
2791 {
2792     Copy();
2793 }
2794 
OnPaste(wxCommandEvent & WXUNUSED (event))2795 void wxTextCtrl::OnPaste(wxCommandEvent& WXUNUSED(event))
2796 {
2797     Paste();
2798 }
2799 
OnUndo(wxCommandEvent & WXUNUSED (event))2800 void wxTextCtrl::OnUndo(wxCommandEvent& WXUNUSED(event))
2801 {
2802     Undo();
2803 }
2804 
OnRedo(wxCommandEvent & WXUNUSED (event))2805 void wxTextCtrl::OnRedo(wxCommandEvent& WXUNUSED(event))
2806 {
2807     Redo();
2808 }
2809 
OnDelete(wxCommandEvent & WXUNUSED (event))2810 void wxTextCtrl::OnDelete(wxCommandEvent& WXUNUSED(event))
2811 {
2812     RemoveSelection();
2813 }
2814 
OnSelectAll(wxCommandEvent & WXUNUSED (event))2815 void wxTextCtrl::OnSelectAll(wxCommandEvent& WXUNUSED(event))
2816 {
2817     SelectAll();
2818 }
2819 
OnUpdateCut(wxUpdateUIEvent & event)2820 void wxTextCtrl::OnUpdateCut(wxUpdateUIEvent& event)
2821 {
2822     event.Enable( CanCut() );
2823 }
2824 
OnUpdateCopy(wxUpdateUIEvent & event)2825 void wxTextCtrl::OnUpdateCopy(wxUpdateUIEvent& event)
2826 {
2827     event.Enable( CanCopy() );
2828 }
2829 
OnUpdatePaste(wxUpdateUIEvent & event)2830 void wxTextCtrl::OnUpdatePaste(wxUpdateUIEvent& event)
2831 {
2832     event.Enable( CanPaste() );
2833 }
2834 
OnUpdateUndo(wxUpdateUIEvent & event)2835 void wxTextCtrl::OnUpdateUndo(wxUpdateUIEvent& event)
2836 {
2837     event.Enable( CanUndo() );
2838 }
2839 
OnUpdateRedo(wxUpdateUIEvent & event)2840 void wxTextCtrl::OnUpdateRedo(wxUpdateUIEvent& event)
2841 {
2842     event.Enable( CanRedo() );
2843 }
2844 
OnUpdateDelete(wxUpdateUIEvent & event)2845 void wxTextCtrl::OnUpdateDelete(wxUpdateUIEvent& event)
2846 {
2847     event.Enable( HasSelection() && IsEditable() );
2848 }
2849 
OnUpdateSelectAll(wxUpdateUIEvent & event)2850 void wxTextCtrl::OnUpdateSelectAll(wxUpdateUIEvent& event)
2851 {
2852     event.Enable( !IsEmpty() );
2853 }
2854 
OnSetFocus(wxFocusEvent & event)2855 void wxTextCtrl::OnSetFocus(wxFocusEvent& event)
2856 {
2857     // be sure the caret remains invisible if the user had hidden it
2858     if ( !m_isNativeCaretShown )
2859     {
2860         ::HideCaret(GetHwnd());
2861     }
2862 
2863     event.Skip();
2864 }
2865 
2866 // the rest of the file only deals with the rich edit controls
2867 #if wxUSE_RICHEDIT
2868 
OnRightUp(wxMouseEvent & eventMouse)2869 void wxTextCtrl::OnRightUp(wxMouseEvent& eventMouse)
2870 {
2871     wxContextMenuEvent eventMenu(wxEVT_CONTEXT_MENU,
2872                                  GetId(),
2873                                  ClientToScreen(eventMouse.GetPosition()));
2874     eventMenu.SetEventObject(this);
2875 
2876     if ( !ProcessWindowEvent(eventMenu) )
2877         eventMouse.Skip();
2878 }
2879 
OnContextMenu(wxContextMenuEvent & event)2880 void wxTextCtrl::OnContextMenu(wxContextMenuEvent& event)
2881 {
2882     if (IsRich())
2883     {
2884         if (!m_privateContextMenu)
2885             m_privateContextMenu = MSWCreateContextMenu();
2886         PopupMenu(m_privateContextMenu);
2887         return;
2888     }
2889     else
2890         event.Skip();
2891 }
2892 
MSWCreateContextMenu()2893 wxMenu *wxTextCtrl::MSWCreateContextMenu()
2894 {
2895     wxMenu *m = new wxMenu;
2896     m->Append(wxID_UNDO, _("&Undo"));
2897     m->Append(wxID_REDO, _("&Redo"));
2898     m->AppendSeparator();
2899     m->Append(wxID_CUT, _("Cu&t"));
2900     m->Append(wxID_COPY, _("&Copy"));
2901     m->Append(wxID_PASTE, _("&Paste"));
2902     m->Append(wxID_CLEAR, _("&Delete"));
2903     m->AppendSeparator();
2904     m->Append(wxID_SELECTALL, _("Select &All"));
2905     return m;
2906 }
2907 
MSWSetRichZoom()2908 void wxTextCtrl::MSWSetRichZoom()
2909 {
2910     // nothing to scale
2911     if ( m_richDPIscale == 1 )
2912         return;
2913 
2914     // get the current zoom ratio
2915     UINT num = 1;
2916     UINT denom = 1;
2917     ::SendMessage(GetHWND(), EM_GETZOOM, (WPARAM)&num, (LPARAM)&denom);
2918 
2919     // combine the zoom ratio with the DPI scale factor
2920     float ratio = m_richDPIscale;
2921     if ( denom > 0 )
2922         ratio = ratio * (num / (float)denom);
2923 
2924     // apply the new zoom ratio, Windows uses a default denominator of 100, so
2925     // do it here as well
2926     num = 100 * ratio;
2927     denom = 100;
2928     ::SendMessage(GetHWND(), EM_SETZOOM, (WPARAM)num, (LPARAM)denom);
2929 }
2930 
MSWUpdateFontOnDPIChange(const wxSize & newDPI)2931 void wxTextCtrl::MSWUpdateFontOnDPIChange(const wxSize& newDPI)
2932 {
2933     // Don't use MSWUpdateFontOnDPIChange for the rich edit controls, they
2934     // (somehow?) update their appearance on their own and changing their
2935     // HFONT, as the base class version does, would reset all the styles used
2936     // by them when the DPI changes, which is unwanted.
2937     if ( !IsRich() )
2938     {
2939         wxTextCtrlBase::MSWUpdateFontOnDPIChange(newDPI);
2940     }
2941     // If the rich control is created on a screen with non-system DPI, an
2942     // initial zoom factor was applied. This needs to be reset after the first
2943     // DPI change. First invert the scale, then set it to 1 so it is not
2944     // applied again.
2945     else if ( m_richDPIscale != 1 )
2946     {
2947         m_richDPIscale = 1 / m_richDPIscale;
2948         MSWSetRichZoom();
2949         m_richDPIscale = 1;
2950     }
2951 }
2952 
2953 // ----------------------------------------------------------------------------
2954 // EN_LINK processing
2955 // ----------------------------------------------------------------------------
2956 
MSWOnNotify(int idCtrl,WXLPARAM lParam,WXLPARAM * result)2957 bool wxTextCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
2958 {
2959     NMHDR *hdr = (NMHDR* )lParam;
2960     switch ( hdr->code )
2961     {
2962        case EN_MSGFILTER:
2963             {
2964                 const MSGFILTER *msgf = (MSGFILTER *)lParam;
2965                 UINT msg = msgf->msg;
2966 
2967                 // this is a bit crazy but richedit 1.0 sends us all mouse
2968                 // events _except_ WM_LBUTTONUP (don't ask me why) so we have
2969                 // generate the wxWin events for this message manually
2970                 //
2971                 // NB: in fact, this is still not totally correct as it does
2972                 //     send us WM_LBUTTONUP if the selection was cleared by the
2973                 //     last click -- so currently we get 2 events in this case,
2974                 //     but as I don't see any obvious way to check for this I
2975                 //     leave this code in place because it's still better than
2976                 //     not getting left up events at all
2977                 if ( msg == WM_LBUTTONUP )
2978                 {
2979                     WXUINT flags = msgf->wParam;
2980                     int x = GET_X_LPARAM(msgf->lParam),
2981                         y = GET_Y_LPARAM(msgf->lParam);
2982 
2983                     HandleMouseEvent(msg, x, y, flags);
2984                 }
2985             }
2986 
2987             // return true to process the event (and false to ignore it)
2988             return true;
2989 
2990         case EN_LINK:
2991             {
2992                 const ENLINK *enlink = (ENLINK *)hdr;
2993 
2994                 switch ( enlink->msg )
2995                 {
2996                     case WM_SETCURSOR:
2997                         // ok, so it is hardcoded - do we really nee to
2998                         // customize it?
2999                         {
3000                             wxCursor cur(wxCURSOR_HAND);
3001                             ::SetCursor(GetHcursorOf(cur));
3002                             *result = TRUE;
3003                             break;
3004                         }
3005 
3006                     case WM_MOUSEMOVE:
3007                     case WM_LBUTTONDOWN:
3008                     case WM_LBUTTONUP:
3009                     case WM_LBUTTONDBLCLK:
3010                     case WM_RBUTTONDOWN:
3011                     case WM_RBUTTONUP:
3012                     case WM_RBUTTONDBLCLK:
3013                         // send a mouse event
3014                         {
3015                             static const wxEventType eventsMouse[] =
3016                             {
3017                                 wxEVT_MOTION,
3018                                 wxEVT_LEFT_DOWN,
3019                                 wxEVT_LEFT_UP,
3020                                 wxEVT_LEFT_DCLICK,
3021                                 wxEVT_RIGHT_DOWN,
3022                                 wxEVT_RIGHT_UP,
3023                                 wxEVT_RIGHT_DCLICK,
3024                             };
3025 
3026                             // the event ids are consecutive
3027                             wxMouseEvent
3028                                 evtMouse(eventsMouse[enlink->msg - WM_MOUSEMOVE]);
3029 
3030                             InitMouseEvent(evtMouse,
3031                                            GET_X_LPARAM(enlink->lParam),
3032                                            GET_Y_LPARAM(enlink->lParam),
3033                                            enlink->wParam);
3034 
3035                             wxTextUrlEvent event(m_windowId, evtMouse,
3036                                                  enlink->chrg.cpMin,
3037                                                  enlink->chrg.cpMax);
3038 
3039                             InitCommandEvent(event);
3040 
3041                             *result = ProcessCommand(event);
3042                         }
3043                         break;
3044                 }
3045             }
3046             return true;
3047     }
3048 
3049     // not processed, leave it to the base class
3050     return wxTextCtrlBase::MSWOnNotify(idCtrl, lParam, result);
3051 }
3052 
3053 #if wxUSE_DRAG_AND_DROP
3054 
SetDropTarget(wxDropTarget * dropTarget)3055 void wxTextCtrl::SetDropTarget(wxDropTarget *dropTarget)
3056 {
3057     if ( m_dropTarget == wxRICHTEXT_DEFAULT_DROPTARGET )
3058     {
3059         // get rid of the built-in drop target
3060         ::RevokeDragDrop(GetHwnd());
3061         m_dropTarget = NULL;
3062     }
3063 
3064     wxTextCtrlBase::SetDropTarget(dropTarget);
3065 }
3066 
3067 #endif // wxUSE_DRAG_AND_DROP
3068 
3069 // ----------------------------------------------------------------------------
3070 // colour setting for the rich edit controls
3071 // ----------------------------------------------------------------------------
3072 
SetBackgroundColour(const wxColour & colour)3073 bool wxTextCtrl::SetBackgroundColour(const wxColour& colour)
3074 {
3075     if ( !wxTextCtrlBase::SetBackgroundColour(colour) )
3076     {
3077         // colour didn't really change
3078         return false;
3079     }
3080 
3081     if ( IsRich() )
3082     {
3083         // rich edit doesn't use WM_CTLCOLOR, hence we need to send
3084         // EM_SETBKGNDCOLOR additionally
3085         ::SendMessage(GetHwnd(), EM_SETBKGNDCOLOR, 0, wxColourToRGB(colour));
3086     }
3087 
3088     return true;
3089 }
3090 
SetForegroundColour(const wxColour & colour)3091 bool wxTextCtrl::SetForegroundColour(const wxColour& colour)
3092 {
3093     if ( !wxTextCtrlBase::SetForegroundColour(colour) )
3094     {
3095         // colour didn't really change
3096         return false;
3097     }
3098 
3099     if ( IsRich() )
3100     {
3101         // change the colour of everything
3102         WinStruct<CHARFORMAT> cf;
3103         cf.dwMask = CFM_COLOR;
3104         cf.crTextColor = wxColourToRGB(colour);
3105         ::SendMessage(GetHwnd(), EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
3106     }
3107 
3108     return true;
3109 }
3110 
SetFont(const wxFont & font)3111 bool wxTextCtrl::SetFont(const wxFont& font)
3112 {
3113     // Native text control sends EN_CHANGE when the font changes, producing
3114     // a wxEVT_TEXT event as if the user changed the value. This is not
3115     // the case, so suppress the event.
3116     wxEventBlocker block(this, wxEVT_TEXT);
3117 
3118     if ( !wxTextCtrlBase::SetFont(font) )
3119         return false;
3120 
3121     if ( GetRichVersion() >= 4 )
3122     {
3123         // Using WM_SETFONT is not enough with RichEdit 4.1: it does work but
3124         // for ASCII characters only and inserting a non-ASCII one into it
3125         // later reverts to the default font so use EM_SETCHARFORMAT to change
3126         // the default font for it.
3127         wxTextAttr attr;
3128         attr.SetFont(font);
3129         SetStyle(-1, -1, attr);
3130     }
3131 
3132     return true;
3133 }
3134 
3135 // ----------------------------------------------------------------------------
3136 // styling support for rich edit controls
3137 // ----------------------------------------------------------------------------
3138 
3139 #if _RICHEDIT_VER >= 0x0800
3140 static const wxColour gs_underlineColourMap[] =
3141 {
3142     // The colours are coming from https://docs.microsoft.com/en-us/windows/desktop/api/tom/nf-tom-itextdocument2-geteffectcolor.
3143     wxNullColour,            // text colour
3144     wxColour(0,   0,   0  ), // black
3145     wxColour(0,   0,   255), // blue
3146     wxColour(0,   255, 255), // cyan
3147     wxColour(0,   255, 0  ), // green
3148     wxColour(255, 0,   255), // magenta
3149     wxColour(255, 0,   0  ), // red
3150     wxColour(255, 255, 0  ), // yellow
3151     wxColour(255, 255, 255), // white
3152     wxColour(0,   0,   128), // navy
3153     wxColour(0,   128, 128), // teal
3154     wxColour(0,   128, 0  ), // light green
3155     wxColour(128, 0,   128), // purple
3156     wxColour(128, 0,   0  ), // maroon
3157     wxColour(128, 128, 0  ), // olive
3158     wxColour(128, 128, 128), // grey
3159     wxColour(192, 192, 192), // light grey
3160 };
3161 #endif
3162 
MSWSetCharFormat(const wxTextAttr & style,long start,long end)3163 bool wxTextCtrl::MSWSetCharFormat(const wxTextAttr& style, long start, long end)
3164 {
3165     // initialize CHARFORMAT struct
3166 #if wxUSE_RICHEDIT2
3167     CHARFORMAT2 cf;
3168 #else
3169     CHARFORMAT cf;
3170 #endif
3171 
3172     wxZeroMemory(cf);
3173 
3174     // we can't use CHARFORMAT2 with RichEdit 1.0, so pretend it is a simple
3175     // CHARFORMAT in that case
3176 #if wxUSE_RICHEDIT2
3177     if ( m_verRichEdit == 1 )
3178     {
3179         // this is the only thing the control is going to grok
3180         cf.cbSize = sizeof(CHARFORMAT);
3181     }
3182     else
3183 #endif
3184     {
3185         // CHARFORMAT or CHARFORMAT2
3186         cf.cbSize = sizeof(cf);
3187     }
3188 
3189     if ( style.HasFont() )
3190     {
3191         // VZ: CFM_CHARSET doesn't seem to do anything at all in RichEdit 2.0
3192         //     but using it doesn't seem to hurt neither so leaving it for now
3193 
3194         cf.dwMask |= CFM_FACE | CFM_SIZE | CFM_CHARSET |
3195                      CFM_ITALIC | CFM_BOLD | CFM_UNDERLINE | CFM_STRIKEOUT;
3196 
3197         // fill in data from LOGFONT but recalculate lfHeight because we need
3198         // the real height in twips and not the negative number used inside
3199         // LOGFONT returns (this is correct in general and works with
3200         // the Windows font mapper, but not here)
3201 
3202         wxFont font(style.GetFont());
3203 
3204         LOGFONT lf = font.GetNativeFontInfo()->lf;
3205         cf.yHeight = 20 * font.GetFractionalPointSize(); // 1 pt = 20 twips
3206         cf.bCharSet = lf.lfCharSet;
3207         cf.bPitchAndFamily = lf.lfPitchAndFamily;
3208         wxStrlcpy(cf.szFaceName, lf.lfFaceName, WXSIZEOF(cf.szFaceName));
3209 
3210         // also deal with underline/italic/bold attributes: note that we must
3211         // always set CFM_ITALIC &c bits in dwMask, even if we don't set the
3212         // style to allow clearing it
3213         if ( lf.lfItalic )
3214         {
3215             cf.dwEffects |= CFE_ITALIC;
3216         }
3217 
3218         if ( lf.lfWeight == FW_BOLD )
3219         {
3220             cf.dwEffects |= CFE_BOLD;
3221         }
3222 
3223         if ( lf.lfUnderline )
3224         {
3225             cf.dwEffects |= CFE_UNDERLINE;
3226         }
3227         if ( lf.lfStrikeOut )
3228         {
3229             cf.dwEffects |= CFE_STRIKEOUT;
3230         }
3231     }
3232 
3233     if ( style.HasFontUnderlined() )
3234     {
3235         cf.dwMask |= CFM_UNDERLINETYPE;
3236         BYTE underlineType = CFU_UNDERLINENONE;
3237         switch ( style.GetUnderlineType() )
3238         {
3239             case wxTEXT_ATTR_UNDERLINE_SOLID:
3240                 underlineType = CFU_UNDERLINE;
3241                 break;
3242             case wxTEXT_ATTR_UNDERLINE_DOUBLE:
3243                 underlineType = CFU_UNDERLINEDOUBLE;
3244                 break;
3245             case wxTEXT_ATTR_UNDERLINE_SPECIAL:
3246                 underlineType = CFU_UNDERLINEWAVE;
3247                 break;
3248             default:
3249                 underlineType = CFU_UNDERLINENONE;
3250                 break;
3251         }
3252         cf.bUnderlineType = underlineType;
3253 
3254 #if _RICHEDIT_VER >= 0x0800
3255         BYTE colour = 0;
3256         const wxColour& col = style.GetUnderlineColour();
3257         for ( size_t c = 0; c < WXSIZEOF(gs_underlineColourMap); ++c )
3258         {
3259             if ( col == gs_underlineColourMap[c] )
3260             {
3261                 colour = static_cast<BYTE>(c);
3262                 break;
3263             }
3264         }
3265         cf.bUnderlineColor = colour;
3266 #endif
3267     }
3268 
3269     if ( style.HasTextColour() )
3270     {
3271         cf.dwMask |= CFM_COLOR;
3272         cf.crTextColor = wxColourToRGB(style.GetTextColour());
3273     }
3274 
3275 #if wxUSE_RICHEDIT2
3276     if ( m_verRichEdit != 1 && style.HasBackgroundColour() )
3277     {
3278         cf.dwMask |= CFM_BACKCOLOR;
3279         cf.crBackColor = wxColourToRGB(style.GetBackgroundColour());
3280     }
3281 #endif // wxUSE_RICHEDIT2
3282 
3283     // Apply the style either to the selection or to the entire control.
3284     WPARAM selMode;
3285     if ( start != -1 || end != -1 )
3286     {
3287         DoSetSelection(start, end, SetSel_NoScroll);
3288         selMode = SCF_SELECTION;
3289     }
3290     else
3291     {
3292         selMode = SCF_ALL;
3293     }
3294 
3295     if ( !::SendMessage(GetHwnd(), EM_SETCHARFORMAT, selMode, (LPARAM)&cf) )
3296     {
3297         wxLogLastError(wxT("SendMessage(EM_SETCHARFORMAT)"));
3298         return false;
3299     }
3300 
3301     return true;
3302 }
3303 
MSWSetParaFormat(const wxTextAttr & style,long start,long end)3304 bool wxTextCtrl::MSWSetParaFormat(const wxTextAttr& style, long start, long end)
3305 {
3306 #if wxUSE_RICHEDIT2
3307     PARAFORMAT2 pf;
3308 #else
3309     PARAFORMAT pf;
3310 #endif
3311 
3312     wxZeroMemory(pf);
3313 
3314     // we can't use PARAFORMAT2 with RichEdit 1.0, so pretend it is a simple
3315     // PARAFORMAT in that case
3316 #if wxUSE_RICHEDIT2
3317     if ( m_verRichEdit == 1 )
3318     {
3319         // this is the only thing the control is going to grok
3320         pf.cbSize = sizeof(PARAFORMAT);
3321     }
3322     else
3323 #endif
3324     {
3325         // PARAFORMAT or PARAFORMAT2
3326         pf.cbSize = sizeof(pf);
3327     }
3328 
3329     if (style.HasAlignment())
3330     {
3331         pf.dwMask |= PFM_ALIGNMENT;
3332         if (style.GetAlignment() == wxTEXT_ALIGNMENT_RIGHT)
3333             pf.wAlignment = PFA_RIGHT;
3334         else if (style.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE)
3335             pf.wAlignment = PFA_CENTER;
3336         else if (style.GetAlignment() == wxTEXT_ALIGNMENT_JUSTIFIED)
3337             pf.wAlignment = PFA_JUSTIFY;
3338         else
3339             pf.wAlignment = PFA_LEFT;
3340     }
3341 
3342     if (style.HasLeftIndent())
3343     {
3344         pf.dwMask |= PFM_STARTINDENT | PFM_OFFSET;
3345 
3346         // Convert from 1/10 mm to TWIPS
3347         pf.dxStartIndent = (int) (((double) style.GetLeftIndent()) * mm2twips / 10.0) ;
3348         pf.dxOffset = (int) (((double) style.GetLeftSubIndent()) * mm2twips / 10.0) ;
3349     }
3350 
3351     if (style.HasRightIndent())
3352     {
3353         pf.dwMask |= PFM_RIGHTINDENT;
3354 
3355         // Convert from 1/10 mm to TWIPS
3356         pf.dxRightIndent = (int) (((double) style.GetRightIndent()) * mm2twips / 10.0) ;
3357     }
3358 
3359     if (style.HasTabs())
3360     {
3361         pf.dwMask |= PFM_TABSTOPS;
3362 
3363         const wxArrayInt& tabs = style.GetTabs();
3364 
3365         pf.cTabCount = (SHORT)wxMin(tabs.GetCount(), MAX_TAB_STOPS);
3366         size_t i;
3367         for (i = 0; i < (size_t) pf.cTabCount; i++)
3368         {
3369             // Convert from 1/10 mm to TWIPS
3370             pf.rgxTabs[i] = (int) (((double) tabs[i]) * mm2twips / 10.0) ;
3371         }
3372     }
3373 
3374 #if wxUSE_RICHEDIT2
3375     if ( style.HasParagraphSpacingAfter() )
3376     {
3377         pf.dwMask |= PFM_SPACEAFTER;
3378 
3379         // Convert from 1/10 mm to TWIPS
3380         pf.dySpaceAfter = (int) (((double) style.GetParagraphSpacingAfter()) * mm2twips / 10.0) ;
3381     }
3382 
3383     if ( style.HasParagraphSpacingBefore() )
3384     {
3385         pf.dwMask |= PFM_SPACEBEFORE;
3386 
3387         // Convert from 1/10 mm to TWIPS
3388         pf.dySpaceBefore = (int) (((double) style.GetParagraphSpacingBefore()) * mm2twips / 10.0) ;
3389     }
3390 #endif // wxUSE_RICHEDIT2
3391 
3392 #if wxUSE_RICHEDIT2
3393     if ( m_verRichEdit > 1 )
3394     {
3395         if ( GetLayoutDirection() == wxLayout_RightToLeft )
3396         {
3397             // Use RTL paragraphs in RTL mode to get proper layout
3398             pf.dwMask |= PFM_RTLPARA;
3399             pf.wEffects |= PFE_RTLPARA;
3400         }
3401     }
3402 #endif // wxUSE_RICHEDIT2
3403 
3404     if ( pf.dwMask )
3405     {
3406         // Do format the selection.
3407         DoSetSelection(start, end, SetSel_NoScroll);
3408 
3409         if ( !::SendMessage(GetHwnd(), EM_SETPARAFORMAT, 0, (LPARAM) &pf) )
3410         {
3411             wxLogLastError(wxT("SendMessage(EM_SETPARAFORMAT)"));
3412 
3413             return false;
3414         }
3415     }
3416 
3417     return true;
3418 }
3419 
SetStyle(long start,long end,const wxTextAttr & style)3420 bool wxTextCtrl::SetStyle(long start, long end, const wxTextAttr& style)
3421 {
3422     if ( !IsRich() )
3423     {
3424         // can't do it with normal text control
3425         return false;
3426     }
3427 
3428     // the richedit 1.0 doesn't handle setting background colour, so don't
3429     // even try to do anything if it's the only thing we want to change
3430     if ( m_verRichEdit == 1 && !style.HasFont() && !style.HasTextColour() &&
3431         !style.HasLeftIndent() && !style.HasRightIndent() && !style.HasAlignment() &&
3432         !style.HasTabs() && !style.GetFontUnderlined() )
3433     {
3434         // nothing to do: return true if there was really nothing to do and
3435         // false if we failed to set bg colour
3436         return !style.HasBackgroundColour();
3437     }
3438 
3439     // order the range if needed
3440     if ( start > end )
3441         wxSwap(start, end);
3442 
3443     // we can only change the format of the selection, so select the range we
3444     // want and restore the old selection later, after MSWSetXXXFormat()
3445     // functions (possibly) change it.
3446     long startOld, endOld;
3447     GetSelection(&startOld, &endOld);
3448 
3449     bool ok = MSWSetCharFormat(style, start, end);
3450     if ( !MSWSetParaFormat(style, start, end) )
3451         ok = false;
3452 
3453     if ( start != startOld || end != endOld )
3454     {
3455         // restore the original selection
3456         DoSetSelection(startOld, endOld, SetSel_NoScroll);
3457     }
3458 
3459     return ok;
3460 }
3461 
SetDefaultStyle(const wxTextAttr & style)3462 bool wxTextCtrl::SetDefaultStyle(const wxTextAttr& style)
3463 {
3464     if ( !wxTextCtrlBase::SetDefaultStyle(style) )
3465         return false;
3466 
3467     if ( IsEditable() )
3468     {
3469         // we have to do this or the style wouldn't apply for the text typed by
3470         // the user
3471         wxTextPos posLast = GetLastPosition();
3472         SetStyle(posLast, posLast, m_defaultStyle);
3473     }
3474 
3475     return true;
3476 }
3477 
GetStyle(long position,wxTextAttr & style)3478 bool wxTextCtrl::GetStyle(long position, wxTextAttr& style)
3479 {
3480     if ( !IsRich() )
3481     {
3482         // can't do it with normal text control
3483         return false;
3484     }
3485 
3486     // initialize CHARFORMAT struct
3487 #if wxUSE_RICHEDIT2
3488     CHARFORMAT2 cf;
3489 #else
3490     CHARFORMAT cf;
3491 #endif
3492 
3493     wxZeroMemory(cf);
3494 
3495     // we can't use CHARFORMAT2 with RichEdit 1.0, so pretend it is a simple
3496     // CHARFORMAT in that case
3497 #if wxUSE_RICHEDIT2
3498     if ( m_verRichEdit == 1 )
3499     {
3500         // this is the only thing the control is going to grok
3501         cf.cbSize = sizeof(CHARFORMAT);
3502     }
3503     else
3504 #endif
3505     {
3506         // CHARFORMAT or CHARFORMAT2
3507         cf.cbSize = sizeof(cf);
3508     }
3509     // we can only change the format of the selection, so select the range we
3510     // want and restore the old selection later
3511     long startOld, endOld;
3512     GetSelection(&startOld, &endOld);
3513 
3514     // but do we really have to change the selection?
3515     const bool changeSel = position != startOld;
3516 
3517     if ( changeSel )
3518     {
3519         DoSetSelection(position, position + 1, SetSel_NoScroll);
3520     }
3521 
3522     // get the selection formatting
3523     (void) ::SendMessage(GetHwnd(), EM_GETCHARFORMAT,
3524                             SCF_SELECTION, (LPARAM)&cf) ;
3525 
3526 
3527     LOGFONT lf;
3528     lf.lfWidth = 0;
3529     lf.lfCharSet = ANSI_CHARSET; // FIXME: how to get correct charset?
3530     lf.lfClipPrecision = 0;
3531     lf.lfEscapement = 0;
3532     wxStrcpy(lf.lfFaceName, cf.szFaceName);
3533 
3534     //NOTE:  we _MUST_ set each of these values to _something_ since we
3535     //do not call wxZeroMemory on the LOGFONT lf
3536     if (cf.dwEffects & CFE_ITALIC)
3537         lf.lfItalic = TRUE;
3538     else
3539         lf.lfItalic = FALSE;
3540 
3541     lf.lfOrientation = 0;
3542     lf.lfPitchAndFamily = cf.bPitchAndFamily;
3543     lf.lfQuality = 0;
3544 
3545     if (cf.dwEffects & CFE_STRIKEOUT)
3546         lf.lfStrikeOut = TRUE;
3547     else
3548         lf.lfStrikeOut = FALSE;
3549 
3550     if (cf.dwEffects & CFE_UNDERLINE)
3551         lf.lfUnderline = TRUE;
3552     else
3553         lf.lfUnderline = FALSE;
3554 
3555     if (cf.dwEffects & CFE_BOLD)
3556         lf.lfWeight = FW_BOLD;
3557     else
3558         lf.lfWeight = FW_NORMAL;
3559 
3560     // Determine the pointSize that was used in SetStyle. Don't worry about
3561     // lfHeight or PPI, style.SetFont() will lose this information anyway.
3562     wxFont font(wxNativeFontInfo(lf, this));
3563     font.SetFractionalPointSize(cf.yHeight / 20.0); // 1 pt = 20 twips
3564     if (font.IsOk())
3565     {
3566         style.SetFont(font);
3567     }
3568     style.SetTextColour(wxColour(cf.crTextColor));
3569 
3570 #if wxUSE_RICHEDIT2
3571     if ( m_verRichEdit != 1 )
3572     {
3573         // Notice that, surprisingly, CFM_BACKCOLOR is still set in the mask
3574         // even when CFE_AUTOBACKCOLOR is set in the effects, indicating that
3575         // the background colour is not used.
3576         if ( !(cf.dwEffects & CFE_AUTOBACKCOLOR) && (cf.dwMask & CFM_BACKCOLOR) )
3577         {
3578             style.SetBackgroundColour(wxColour(cf.crBackColor));
3579         }
3580     }
3581 #endif // wxUSE_RICHEDIT2
3582 
3583     wxTextAttrUnderlineType underlineType = wxTEXT_ATTR_UNDERLINE_NONE;
3584     switch ( cf.bUnderlineType )
3585     {
3586         case CFU_UNDERLINE:
3587             underlineType = wxTEXT_ATTR_UNDERLINE_SOLID;
3588             break;
3589         case CFU_UNDERLINEDOUBLE:
3590             underlineType = wxTEXT_ATTR_UNDERLINE_DOUBLE;
3591             break;
3592         case CFU_UNDERLINEWAVE:
3593             underlineType = wxTEXT_ATTR_UNDERLINE_SPECIAL;
3594             break;
3595         default:
3596             underlineType = wxTEXT_ATTR_UNDERLINE_NONE;
3597             break;
3598     }
3599 
3600     wxColour underlineColour;
3601 #if _RICHEDIT_VER >= 0x0800
3602     if ( cf.bUnderlineColor < WXSIZEOF(gs_underlineColourMap) )
3603         underlineColour = gs_underlineColourMap[cf.bUnderlineColor];
3604 #endif
3605 
3606     if ( underlineType != wxTEXT_ATTR_UNDERLINE_NONE )
3607         style.SetFontUnderlined(underlineType, underlineColour);
3608 
3609     // now get the paragraph formatting
3610     PARAFORMAT2 pf;
3611     wxZeroMemory(pf);
3612     // we can't use PARAFORMAT2 with RichEdit 1.0, so pretend it is a simple
3613     // PARAFORMAT in that case
3614 #if wxUSE_RICHEDIT2
3615     if ( m_verRichEdit == 1 )
3616     {
3617         // this is the only thing the control is going to grok
3618         pf.cbSize = sizeof(PARAFORMAT);
3619     }
3620     else
3621 #endif
3622     {
3623         // PARAFORMAT or PARAFORMAT2
3624         pf.cbSize = sizeof(pf);
3625     }
3626 
3627     // do format the selection
3628     (void) ::SendMessage(GetHwnd(), EM_GETPARAFORMAT, 0, (LPARAM) &pf) ;
3629 
3630     style.SetLeftIndent( (int) ((double) pf.dxStartIndent * twips2mm * 10.0), (int) ((double) pf.dxOffset * twips2mm * 10.0) );
3631     style.SetRightIndent( (int) ((double) pf.dxRightIndent * twips2mm * 10.0) );
3632 
3633     if (pf.wAlignment == PFA_CENTER)
3634         style.SetAlignment(wxTEXT_ALIGNMENT_CENTRE);
3635     else if (pf.wAlignment == PFA_RIGHT)
3636         style.SetAlignment(wxTEXT_ALIGNMENT_RIGHT);
3637     else if (pf.wAlignment == PFA_JUSTIFY)
3638         style.SetAlignment(wxTEXT_ALIGNMENT_JUSTIFIED);
3639     else
3640         style.SetAlignment(wxTEXT_ALIGNMENT_LEFT);
3641 
3642     wxArrayInt tabStops;
3643     size_t i;
3644     for (i = 0; i < (size_t) pf.cTabCount; i++)
3645     {
3646         tabStops.Add( (int) ((double) (pf.rgxTabs[i] & 0xFFFF) * twips2mm * 10.0) );
3647     }
3648 
3649     if ( changeSel )
3650     {
3651         // restore the original selection
3652         DoSetSelection(startOld, endOld, SetSel_NoScroll);
3653     }
3654 
3655     return true;
3656 }
3657 
3658 // ----------------------------------------------------------------------------
3659 // wxRichEditModule
3660 // ----------------------------------------------------------------------------
3661 
3662 static const HINSTANCE INVALID_HINSTANCE = (HINSTANCE)-1;
3663 
OnInit()3664 bool wxRichEditModule::OnInit()
3665 {
3666     // don't do anything - we will load it when needed
3667     return true;
3668 }
3669 
OnExit()3670 void wxRichEditModule::OnExit()
3671 {
3672     for ( size_t i = 0; i < WXSIZEOF(ms_hRichEdit); i++ )
3673     {
3674         if ( ms_hRichEdit[i] && ms_hRichEdit[i] != INVALID_HINSTANCE )
3675         {
3676             ::FreeLibrary(ms_hRichEdit[i]);
3677             ms_hRichEdit[i] = NULL;
3678         }
3679     }
3680 #if wxUSE_INKEDIT
3681     if (ms_inkEditLib.IsLoaded())
3682         ms_inkEditLib.Unload();
3683 #endif
3684 }
3685 
3686 /* static */
Load(Version version)3687 bool wxRichEditModule::Load(Version version)
3688 {
3689     if ( ms_hRichEdit[version] == INVALID_HINSTANCE )
3690     {
3691         // we had already tried to load it and failed
3692         return false;
3693     }
3694 
3695     if ( ms_hRichEdit[version] )
3696     {
3697         // we've already got this one
3698         return true;
3699     }
3700 
3701     static const wxChar *const dllnames[] =
3702     {
3703         wxT("riched32"),
3704         wxT("riched20"),
3705         wxT("msftedit"),
3706     };
3707 
3708     wxCOMPILE_TIME_ASSERT( WXSIZEOF(dllnames) == Version_Max,
3709                             RichEditDllNamesVersionsMismatch );
3710 
3711     ms_hRichEdit[version] = ::LoadLibrary(dllnames[version]);
3712 
3713     if ( !ms_hRichEdit[version] )
3714     {
3715         ms_hRichEdit[version] = INVALID_HINSTANCE;
3716 
3717         return false;
3718     }
3719 
3720     return true;
3721 }
3722 
3723 #if wxUSE_INKEDIT
3724 // load the InkEdit library
LoadInkEdit()3725 bool wxRichEditModule::LoadInkEdit()
3726 {
3727     if (ms_inkEditLibLoadAttemped)
3728         return ms_inkEditLib.IsLoaded();
3729 
3730     ms_inkEditLibLoadAttemped = true;
3731 
3732     wxLogNull logNull;
3733     return ms_inkEditLib.Load(wxT("inked"));
3734 }
3735 #endif // wxUSE_INKEDIT
3736 
3737 
3738 #endif // wxUSE_RICHEDIT
3739 
3740 #endif // wxUSE_TEXTCTRL
3741