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