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