]> git.saurik.com Git - wxWidgets.git/blame - src/msw/textctrl.cpp
use hand-written samples/Makefile.in
[wxWidgets.git] / src / msw / textctrl.cpp
CommitLineData
2bda0e17 1/////////////////////////////////////////////////////////////////////////////
bfbd6dc1 2// Name: msw/textctrl.cpp
2bda0e17
KB
3// Purpose: wxTextCtrl
4// Author: Julian Smart
5// Modified by:
6// Created: 04/01/98
7// RCS-ID: $Id$
6c9a19aa
JS
8// Copyright: (c) Julian Smart
9// Licence: wxWindows licence
2bda0e17
KB
10/////////////////////////////////////////////////////////////////////////////
11
a1b82138
VZ
12// ============================================================================
13// declarations
14// ============================================================================
15
2bda0e17 16#ifdef __GNUG__
a1b82138 17 #pragma implementation "textctrl.h"
2bda0e17
KB
18#endif
19
a1b82138
VZ
20// ----------------------------------------------------------------------------
21// headers
22// ----------------------------------------------------------------------------
23
2bda0e17
KB
24// For compilers that support precompilation, includes "wx.h".
25#include "wx/wxprec.h"
26
27#ifdef __BORLANDC__
a1b82138 28 #pragma hdrstop
2bda0e17
KB
29#endif
30
1e6feb95
VZ
31#if wxUSE_TEXTCTRL
32
2bda0e17 33#ifndef WX_PRECOMP
a1b82138
VZ
34 #include "wx/textctrl.h"
35 #include "wx/settings.h"
36 #include "wx/brush.h"
37 #include "wx/utils.h"
381dd4bf 38 #include "wx/intl.h"
a1b82138 39 #include "wx/log.h"
381dd4bf 40 #include "wx/app.h"
2b5f62a0 41 #include "wx/menu.h"
2bda0e17
KB
42#endif
43
f6bcfd97
BP
44#include "wx/module.h"
45
47d67540 46#if wxUSE_CLIPBOARD
a1b82138 47 #include "wx/clipbrd.h"
2bda0e17
KB
48#endif
49
a1b82138
VZ
50#include "wx/textfile.h"
51
52#include <windowsx.h>
53
2bda0e17 54#include "wx/msw/private.h"
2077b148 55#include "wx/msw/winundef.h"
2bda0e17 56
a1b82138 57#include <string.h>
2bda0e17 58#include <stdlib.h>
4676948b
JS
59
60#ifndef __WXWINCE__
a1b82138 61#include <sys/types.h>
4676948b 62#endif
fbc535ff 63
a40c9444
VZ
64#if wxUSE_RICHEDIT
65
66// old mingw32 has richedit stuff directly in windows.h and doesn't have
67// richedit.h at all
68#if !defined(__GNUWIN32_OLD__) || defined(__CYGWIN10__)
cd471848 69 #include <richedit.h>
2bda0e17
KB
70#endif
71
3ec4107d 72#include "wx/msw/missing.h"
a5aa8086 73
a40c9444
VZ
74#endif // wxUSE_RICHEDIT
75
aac7e7fe
VZ
76// ----------------------------------------------------------------------------
77// private functions
78// ----------------------------------------------------------------------------
79
80#if wxUSE_RICHEDIT
81
82DWORD CALLBACK wxRichEditStreamIn(DWORD dwCookie, BYTE *buf, LONG cb, LONG *pcb);
83
84#endif // wxUSE_RICHEDIT
85
b12915c1
VZ
86// ----------------------------------------------------------------------------
87// private classes
88// ----------------------------------------------------------------------------
89
90#if wxUSE_RICHEDIT
91
a5aa8086 92// this module initializes RichEdit DLL(s) if needed
b12915c1
VZ
93class wxRichEditModule : public wxModule
94{
95public:
96 virtual bool OnInit();
97 virtual void OnExit();
98
b12915c1
VZ
99 // load the richedit DLL of at least of required version
100 static bool Load(int version = 1);
101
102private:
a5aa8086
VZ
103 // the handles to richedit 1.0 and 2.0 (or 3.0) DLLs
104 static HINSTANCE ms_hRichEdit[2];
b12915c1
VZ
105
106 DECLARE_DYNAMIC_CLASS(wxRichEditModule)
107};
108
a5aa8086 109HINSTANCE wxRichEditModule::ms_hRichEdit[2] = { NULL, NULL };
b12915c1
VZ
110
111IMPLEMENT_DYNAMIC_CLASS(wxRichEditModule, wxModule)
112
113#endif // wxUSE_RICHEDIT
e702ff0f 114
a1b82138
VZ
115// ----------------------------------------------------------------------------
116// event tables and other macros
117// ----------------------------------------------------------------------------
118
2bda0e17 119IMPLEMENT_DYNAMIC_CLASS(wxTextCtrl, wxControl)
066f1b7a
SC
120/*
121 TODO PROPERTIES :
122 value
123*/
2bda0e17
KB
124
125BEGIN_EVENT_TABLE(wxTextCtrl, wxControl)
a1b82138
VZ
126 EVT_CHAR(wxTextCtrl::OnChar)
127 EVT_DROP_FILES(wxTextCtrl::OnDropFiles)
128
2b5f62a0
VZ
129#if wxUSE_RICHEDIT
130 EVT_RIGHT_UP(wxTextCtrl::OnRightClick)
131#endif
132
a1b82138
VZ
133 EVT_MENU(wxID_CUT, wxTextCtrl::OnCut)
134 EVT_MENU(wxID_COPY, wxTextCtrl::OnCopy)
135 EVT_MENU(wxID_PASTE, wxTextCtrl::OnPaste)
136 EVT_MENU(wxID_UNDO, wxTextCtrl::OnUndo)
137 EVT_MENU(wxID_REDO, wxTextCtrl::OnRedo)
2b5f62a0
VZ
138 EVT_MENU(wxID_CLEAR, wxTextCtrl::OnDelete)
139 EVT_MENU(wxID_SELECTALL, wxTextCtrl::OnSelectAll)
a1b82138
VZ
140
141 EVT_UPDATE_UI(wxID_CUT, wxTextCtrl::OnUpdateCut)
142 EVT_UPDATE_UI(wxID_COPY, wxTextCtrl::OnUpdateCopy)
143 EVT_UPDATE_UI(wxID_PASTE, wxTextCtrl::OnUpdatePaste)
144 EVT_UPDATE_UI(wxID_UNDO, wxTextCtrl::OnUpdateUndo)
145 EVT_UPDATE_UI(wxID_REDO, wxTextCtrl::OnUpdateRedo)
2b5f62a0
VZ
146 EVT_UPDATE_UI(wxID_CLEAR, wxTextCtrl::OnUpdateDelete)
147 EVT_UPDATE_UI(wxID_SELECTALL, wxTextCtrl::OnUpdateSelectAll)
f6bcfd97
BP
148#ifdef __WIN16__
149 EVT_ERASE_BACKGROUND(wxTextCtrl::OnEraseBackground)
150#endif
e3a6a6b2
VZ
151
152 EVT_SET_FOCUS(wxTextCtrl::OnSetFocus)
2bda0e17 153END_EVENT_TABLE()
e702ff0f 154
a1b82138
VZ
155// ============================================================================
156// implementation
157// ============================================================================
158
159// ----------------------------------------------------------------------------
160// creation
161// ----------------------------------------------------------------------------
162
aac7e7fe 163void wxTextCtrl::Init()
2bda0e17 164{
cd471848 165#if wxUSE_RICHEDIT
aac7e7fe 166 m_verRichEdit = 0;
2b5f62a0 167#endif // wxUSE_RICHEDIT
5036ea90 168
2b5f62a0 169 m_privateContextMenu = NULL;
5036ea90 170 m_suppressNextUpdate = FALSE;
e3a6a6b2 171 m_isNativeCaretShown = true;
2b5f62a0
VZ
172}
173
174wxTextCtrl::~wxTextCtrl()
175{
176 if (m_privateContextMenu)
177 {
178 delete m_privateContextMenu;
179 m_privateContextMenu = NULL;
180 }
2bda0e17
KB
181}
182
debe6624 183bool wxTextCtrl::Create(wxWindow *parent, wxWindowID id,
c085e333
VZ
184 const wxString& value,
185 const wxPoint& pos,
a1b82138
VZ
186 const wxSize& size,
187 long style,
c085e333
VZ
188 const wxValidator& validator,
189 const wxString& name)
2bda0e17 190{
a1b82138 191 // base initialization
8d99be5f 192 if ( !CreateBase(parent, id, pos, size, style, validator, name) )
a1b82138 193 return FALSE;
2bda0e17 194
a1b82138
VZ
195 if ( parent )
196 parent->AddChild(this);
2bda0e17 197
b2d5a7ee
VZ
198 // translate wxWin style flags to MSW ones
199 WXDWORD msStyle = MSWGetCreateWindowFlags();
cfdd3a98 200
a1b82138 201 // do create the control - either an EDIT or RICHEDIT
b12915c1 202 wxString windowClass = wxT("EDIT");
cd471848 203
57c208c5 204#if wxUSE_RICHEDIT
a5aa8086
VZ
205 if ( m_windowStyle & wxTE_AUTO_URL )
206 {
207 // automatic URL detection only works in RichEdit 2.0+
208 m_windowStyle |= wxTE_RICH2;
209 }
210
211 if ( m_windowStyle & wxTE_RICH2 )
212 {
213 // using richedit 2.0 implies using wxTE_RICH
214 m_windowStyle |= wxTE_RICH;
215 }
216
217 // we need to load the richedit DLL before creating the rich edit control
c49245f8 218 if ( m_windowStyle & wxTE_RICH )
a1b82138 219 {
a5aa8086
VZ
220 static bool s_errorGiven = FALSE;// MT-FIXME
221
222 // Which version do we need? Use 1.0 by default because it is much more
223 // like the the standard EDIT or 2.0 if explicitly requested, but use
224 // only 2.0 in Unicode mode as 1.0 doesn't support Unicode at all
225 //
226 // TODO: RichEdit 3.0 is apparently capable of emulating RichEdit 1.0
227 // (and thus EDIT) much better than RichEdit 2.0 so we probably
228 // should use 3.0 if available as it is the best of both worlds -
229 // but as I can't test it right now I don't do it (VZ)
230#if wxUSE_UNICODE
231 const int verRichEdit = 2;
232#else // !wxUSE_UNICODE
233 int verRichEdit = m_windowStyle & wxTE_RICH2 ? 2 : 1;
234#endif // wxUSE_UNICODE/!wxUSE_UNICODE
0d0512bd
VZ
235
236 // only give the error msg once if the DLL can't be loaded
237 if ( !s_errorGiven )
238 {
a5aa8086
VZ
239 // try to load the RichEdit DLL (will do nothing if already done)
240 if ( !wxRichEditModule::Load(verRichEdit) )
0d0512bd 241 {
a5aa8086
VZ
242#if !wxUSE_UNICODE
243 // try another version?
244 verRichEdit = 3 - verRichEdit; // 1 <-> 2
245
246 if ( !wxRichEditModule::Load(verRichEdit) )
247#endif // wxUSE_UNICODE
248 {
249 wxLogError(_("Impossible to create a rich edit control, using simple text control instead. Please reinstall riched32.dll"));
0d0512bd 250
a5aa8086
VZ
251 s_errorGiven = TRUE;
252 }
0d0512bd
VZ
253 }
254 }
255
a5aa8086 256 // have we managed to load any richedit version?
aac7e7fe 257 if ( !s_errorGiven )
0d0512bd 258 {
a5aa8086 259 m_verRichEdit = verRichEdit;
aac7e7fe 260 if ( m_verRichEdit == 1 )
b12915c1
VZ
261 {
262 windowClass = wxT("RICHEDIT");
263 }
264 else
265 {
266#ifndef RICHEDIT_CLASS
267 wxString RICHEDIT_CLASS;
aac7e7fe 268 RICHEDIT_CLASS.Printf(_T("RichEdit%d0"), m_verRichEdit);
5fb2f4ba 269#if wxUSE_UNICODE
b12915c1
VZ
270 RICHEDIT_CLASS += _T('W');
271#else // ANSI
272 RICHEDIT_CLASS += _T('A');
273#endif // Unicode/ANSI
274#endif // !RICHEDIT_CLASS
275
276 windowClass = RICHEDIT_CLASS;
277 }
0d0512bd 278 }
a1b82138 279 }
b12915c1 280#endif // wxUSE_RICHEDIT
2bda0e17 281
c8b204e6
VZ
282 // we need to turn '\n's into "\r\n"s for the multiline controls
283 wxString valueWin;
284 if ( m_windowStyle & wxTE_MULTILINE )
285 {
286 valueWin = wxTextFile::Translate(value, wxTextFileType_Dos);
287 }
288 else // single line
289 {
290 valueWin = value;
291 }
292
293 if ( !MSWCreateControl(windowClass, msStyle, pos, size, valueWin) )
7da982c4 294 return FALSE;
2bda0e17 295
a756f210 296 SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
86f14582 297
57c208c5 298#if wxUSE_RICHEDIT
aac7e7fe 299 if ( IsRich() )
a1b82138 300 {
5036ea90
VZ
301 // enable the events we're interested in: we want to get EN_CHANGE as
302 // for the normal controls
303 LPARAM mask = ENM_CHANGE;
c57e3339 304
1dae1d00
VZ
305 if ( GetRichVersion() == 1 )
306 {
307 // we also need EN_MSGFILTER for richedit 1.0 for the reasons
308 // explained in its handler
309 mask |= ENM_MOUSEEVENTS;
310 }
311 else if ( m_windowStyle & wxTE_AUTO_URL )
c57e3339
VZ
312 {
313 mask |= ENM_LINK;
314
315 ::SendMessage(GetHwnd(), EM_AUTOURLDETECT, TRUE, 0);
316 }
317
318 ::SendMessage(GetHwnd(), EM_SETEVENTMASK, 0, mask);
a1b82138 319 }
c57e3339 320#endif // wxUSE_RICHEDIT
2bda0e17 321
a1b82138 322 return TRUE;
2bda0e17
KB
323}
324
325// Make sure the window style (etc.) reflects the HWND style (roughly)
cd471848 326void wxTextCtrl::AdoptAttributesFromHWND()
2bda0e17 327{
aac7e7fe 328 wxWindow::AdoptAttributesFromHWND();
2bda0e17 329
aac7e7fe
VZ
330 HWND hWnd = GetHwnd();
331 long style = ::GetWindowLong(hWnd, GWL_STYLE);
2bda0e17 332
aac7e7fe 333 // retrieve the style to see whether this is an edit or richedit ctrl
cd471848 334#if wxUSE_RICHEDIT
aac7e7fe 335 wxString classname = wxGetWindowClass(GetHWND());
2bda0e17 336
aac7e7fe
VZ
337 if ( classname.IsSameAs(_T("EDIT"), FALSE /* no case */) )
338 {
339 m_verRichEdit = 0;
340 }
341 else // rich edit?
342 {
343 wxChar c;
344 if ( wxSscanf(classname, _T("RichEdit%d0%c"), &m_verRichEdit, &c) != 2 )
345 {
346 wxLogDebug(_T("Unknown edit control '%s'."), classname.c_str());
2bda0e17 347
aac7e7fe
VZ
348 m_verRichEdit = 0;
349 }
350 }
a1b82138 351#endif // wxUSE_RICHEDIT
c085e333 352
aac7e7fe
VZ
353 if (style & ES_MULTILINE)
354 m_windowStyle |= wxTE_MULTILINE;
355 if (style & ES_PASSWORD)
356 m_windowStyle |= wxTE_PASSWORD;
357 if (style & ES_READONLY)
358 m_windowStyle |= wxTE_READONLY;
359 if (style & ES_WANTRETURN)
360 m_windowStyle |= wxTE_PROCESS_ENTER;
e015d1f7
JS
361 if (style & ES_CENTER)
362 m_windowStyle |= wxTE_CENTRE;
363 if (style & ES_RIGHT)
364 m_windowStyle |= wxTE_RIGHT;
2bda0e17
KB
365}
366
b2d5a7ee
VZ
367WXDWORD wxTextCtrl::MSWGetStyle(long style, WXDWORD *exstyle) const
368{
369 long msStyle = wxControl::MSWGetStyle(style, exstyle);
370
db50ec5a 371 // styles which we alaways add by default
b2d5a7ee
VZ
372 if ( style & wxTE_MULTILINE )
373 {
374 wxASSERT_MSG( !(style & wxTE_PROCESS_ENTER),
375 wxT("wxTE_PROCESS_ENTER style is ignored for multiline text controls (they always process it)") );
376
377 msStyle |= ES_MULTILINE | ES_WANTRETURN;
378 if ( !(style & wxTE_NO_VSCROLL) )
db50ec5a
VZ
379 {
380 // always adjust the vertical scrollbar automatically if we have it
381 msStyle |= WS_VSCROLL | ES_AUTOVSCROLL;
382
34433938 383#if wxUSE_RICHEDIT
db50ec5a
VZ
384 // we have to use this style for the rich edit controls because
385 // without it the vertical scrollbar never appears at all in
386 // richedit 3.0 because of our ECO_NOHIDESEL hack (search for it)
387 if ( style & wxTE_RICH2 )
388 {
389 msStyle |= ES_DISABLENOSCROLL;
390 }
34433938 391#endif // wxUSE_RICHEDIT
db50ec5a 392 }
b2d5a7ee
VZ
393
394 style |= wxTE_PROCESS_ENTER;
395 }
396 else // !multiline
397 {
398 // there is really no reason to not have this style for single line
399 // text controls
400 msStyle |= ES_AUTOHSCROLL;
401 }
402
db50ec5a 403 // styles which we add depending on the specified wxWindows styles
b2d5a7ee 404 if ( style & wxHSCROLL )
db50ec5a
VZ
405 {
406 // automatically scroll the control horizontally as necessary
407 msStyle |= WS_HSCROLL;// | ES_AUTOHSCROLL;
408 }
b2d5a7ee
VZ
409
410 if ( style & wxTE_READONLY )
411 msStyle |= ES_READONLY;
412
413 if ( style & wxTE_PASSWORD )
414 msStyle |= ES_PASSWORD;
415
b2d5a7ee
VZ
416 if ( style & wxTE_NOHIDESEL )
417 msStyle |= ES_NOHIDESEL;
418
db50ec5a 419 // note that we can't do do "& wxTE_LEFT" as wxTE_LEFT == 0
e015d1f7
JS
420 if ( style & wxTE_CENTRE )
421 msStyle |= ES_CENTER;
db50ec5a 422 else if ( style & wxTE_RIGHT )
e015d1f7 423 msStyle |= ES_RIGHT;
db50ec5a
VZ
424 else
425 msStyle |= ES_LEFT; // ES_LEFT if 0 as well but for consistency...
e015d1f7 426
b2d5a7ee
VZ
427 return msStyle;
428}
429
430void wxTextCtrl::SetWindowStyleFlag(long style)
431{
432#if wxUSE_RICHEDIT
433 // we have to deal with some styles separately because they can't be
434 // changed by simply calling SetWindowLong(GWL_STYLE) but can be changed
435 // using richedit-specific EM_SETOPTIONS
436 if ( IsRich() &&
437 ((style & wxTE_NOHIDESEL) != (GetWindowStyle() & wxTE_NOHIDESEL)) )
438 {
439 bool set = (style & wxTE_NOHIDESEL) != 0;
440
441 ::SendMessage(GetHwnd(), EM_SETOPTIONS, set ? ECOOP_OR : ECOOP_AND,
442 set ? ECO_NOHIDESEL : ~ECO_NOHIDESEL);
443 }
444#endif // wxUSE_RICHEDIT
445
446 wxControl::SetWindowStyleFlag(style);
447}
448
a1b82138
VZ
449// ----------------------------------------------------------------------------
450// set/get the controls text
451// ----------------------------------------------------------------------------
452
cd471848 453wxString wxTextCtrl::GetValue() const
2bda0e17 454{
a5aa8086
VZ
455 // range 0..-1 is special for GetRange() and means to retrieve all text
456 return GetRange(0, -1);
457}
458
459wxString wxTextCtrl::GetRange(long from, long to) const
460{
461 wxString str;
462
463 if ( from >= to && to != -1 )
464 {
465 // nothing to retrieve
466 return str;
467 }
468
b12915c1 469#if wxUSE_RICHEDIT
aac7e7fe 470 if ( IsRich() )
b12915c1 471 {
3988b155 472 int len = GetWindowTextLength(GetHwnd());
a5aa8086 473 if ( len > from )
3988b155 474 {
de564874
MB
475 {
476 // alloc one extra WORD as needed by the control
477 wxStringBuffer tmp(str, ++len);
478 wxChar *p = tmp;
b12915c1 479
de564874
MB
480 TEXTRANGE textRange;
481 textRange.chrg.cpMin = from;
482 textRange.chrg.cpMax = to == -1 ? len : to;
483 textRange.lpstrText = p;
b12915c1 484
de564874
MB
485 (void)SendMessage(GetHwnd(), EM_GETTEXTRANGE,
486 0, (LPARAM)&textRange);
b12915c1 487
de564874 488 if ( m_verRichEdit > 1 )
a5aa8086 489 {
de564874
MB
490 // RichEdit 2.0 uses just CR ('\r') for the
491 // newlines which is neither Unix nor Windows
492 // style - convert it to something reasonable
493 for ( ; *p; p++ )
494 {
495 if ( *p == _T('\r') )
496 *p = _T('\n');
497 }
a5aa8086 498 }
3988b155
VZ
499 }
500
a5aa8086
VZ
501 if ( m_verRichEdit == 1 )
502 {
503 // convert to the canonical form - see comment below
504 str = wxTextFile::Translate(str, wxTextFileType_Unix);
505 }
3988b155
VZ
506 }
507 //else: no text at all, leave the string empty
b12915c1 508 }
a5aa8086 509 else
b12915c1 510#endif // wxUSE_RICHEDIT
a5aa8086
VZ
511 {
512 // retrieve all text
513 str = wxGetWindowText(GetHWND());
b12915c1 514
a5aa8086
VZ
515 // need only a range?
516 if ( from < to )
517 {
518 str = str.Mid(from, to - from);
519 }
520
521 // WM_GETTEXT uses standard DOS CR+LF (\r\n) convention - convert to the
522 // canonical one (same one as above) for consistency with the other kinds
523 // of controls and, more importantly, with the other ports
524 str = wxTextFile::Translate(str, wxTextFileType_Unix);
525 }
b12915c1 526
a5aa8086 527 return str;
2bda0e17
KB
528}
529
530void wxTextCtrl::SetValue(const wxString& value)
531{
b12915c1
VZ
532 // if the text is long enough, it's faster to just set it instead of first
533 // comparing it with the old one (chances are that it will be different
534 // anyhow, this comparison is there to avoid flicker for small single-line
535 // edit controls mostly)
536 if ( (value.length() > 0x400) || (value != GetValue()) )
07cf98cb 537 {
79d26b32 538 DoWriteText(value, FALSE /* not selection only */);
da32743f 539 }
199fbd70
VZ
540 else // same text
541 {
542 // still send an event for consistency
543 SendUpdateEvent();
544 }
af01f1ba 545
da32743f 546 // we should reset the modified flag even if the value didn't really change
104d7404 547
da32743f
VZ
548 // mark the control as being not dirty - we changed its text, not the
549 // user
550 DiscardEdits();
551
552 // for compatibility, don't move the cursor when doing SetValue()
553 SetInsertionPoint(0);
aac7e7fe 554}
a1b82138 555
0b8e5844 556#if wxUSE_RICHEDIT && (!wxUSE_UNICODE || wxUSE_UNICODE_MSLU)
f6bcfd97 557
aac7e7fe
VZ
558DWORD CALLBACK wxRichEditStreamIn(DWORD dwCookie, BYTE *buf, LONG cb, LONG *pcb)
559{
560 *pcb = 0;
f6bcfd97 561
aac7e7fe
VZ
562 wchar_t *wbuf = (wchar_t *)buf;
563 const wchar_t *wpc = *(const wchar_t **)dwCookie;
564 while ( cb && *wpc )
565 {
566 *wbuf++ = *wpc++;
567
568 cb -= sizeof(wchar_t);
569 (*pcb) += sizeof(wchar_t);
07cf98cb 570 }
aac7e7fe
VZ
571
572 *(const wchar_t **)dwCookie = wpc;
573
574 return 0;
2bda0e17
KB
575}
576
886dd7d2 577// from utils.cpp
bddd7a8d 578extern WXDLLIMPEXP_BASE long wxEncodingToCodepage(wxFontEncoding encoding);
aac7e7fe 579
0b8e5844 580#if wxUSE_UNICODE_MSLU
79d26b32
VZ
581bool wxTextCtrl::StreamIn(const wxString& value,
582 wxFontEncoding WXUNUSED(encoding),
583 bool selectionOnly)
0b8e5844
VS
584{
585 const wchar_t *wpc = value.c_str();
79d26b32
VZ
586#else // !wxUSE_UNICODE_MSLU
587bool wxTextCtrl::StreamIn(const wxString& value,
588 wxFontEncoding encoding,
589 bool selectionOnly)
aac7e7fe
VZ
590{
591 // we have to use EM_STREAMIN to force richedit control 2.0+ to show any
2b5f62a0 592 // text in the non default charset -- otherwise it thinks it knows better
aac7e7fe
VZ
593 // than we do and always shows it in the default one
594
595 // first get the Windows code page for this encoding
596 long codepage = wxEncodingToCodepage(encoding);
597 if ( codepage == -1 )
598 {
599 // unknown encoding
600 return FALSE;
601 }
602
603 // next translate to Unicode using this code page
604 int len = ::MultiByteToWideChar(codepage, 0, value, -1, NULL, 0);
855d6be7
VZ
605
606#if wxUSE_WCHAR_T
aac7e7fe 607 wxWCharBuffer wchBuf(len);
855d6be7
VZ
608#else
609 wchar_t *wchBuf = (wchar_t *)malloc((len + 1)*sizeof(wchar_t));
610#endif
611
aac7e7fe 612 if ( !::MultiByteToWideChar(codepage, 0, value, -1,
855d6be7 613 (wchar_t *)(const wchar_t *)wchBuf, len) )
aac7e7fe
VZ
614 {
615 wxLogLastError(_T("MultiByteToWideChar"));
616 }
617
618 // finally, stream it in the control
619 const wchar_t *wpc = wchBuf;
0b8e5844 620#endif // wxUSE_UNICODE_MSLU
aac7e7fe
VZ
621
622 EDITSTREAM eds;
623 wxZeroMemory(eds);
624 eds.dwCookie = (DWORD)&wpc;
7a25a27c
VZ
625 // the cast below is needed for broken (very) old mingw32 headers
626 eds.pfnCallback = (EDITSTREAMCALLBACK)wxRichEditStreamIn;
92209a39 627
2b5f62a0
VZ
628 // we're going to receive 2 EN_CHANGE notifications if we got any selection
629 // (same problem as in DoWriteText())
630 if ( selectionOnly && HasSelection() )
631 {
632 // so suppress one of them
633 m_suppressNextUpdate = TRUE;
634 }
635
07601f59
VZ
636 ::SendMessage(GetHwnd(), EM_STREAMIN,
637 SF_TEXT |
638 SF_UNICODE |
639 (selectionOnly ? SFF_SELECTION : 0),
640 (LPARAM)&eds);
641
642 if ( eds.dwError )
aac7e7fe
VZ
643 {
644 wxLogLastError(_T("EM_STREAMIN"));
aac7e7fe
VZ
645 }
646
855d6be7
VZ
647#if !wxUSE_WCHAR_T
648 free(wchBuf);
649#endif // !wxUSE_WCHAR_T
650
aac7e7fe
VZ
651 return TRUE;
652}
653
654#endif // wxUSE_RICHEDIT
655
a1b82138 656void wxTextCtrl::WriteText(const wxString& value)
79d26b32
VZ
657{
658 DoWriteText(value);
659}
660
661void wxTextCtrl::DoWriteText(const wxString& value, bool selectionOnly)
2bda0e17 662{
a5aa8086
VZ
663 wxString valueDos;
664 if ( m_windowStyle & wxTE_MULTILINE )
665 valueDos = wxTextFile::Translate(value, wxTextFileType_Dos);
666 else
667 valueDos = value;
2bda0e17 668
4bc1afd5 669#if wxUSE_RICHEDIT
aac7e7fe
VZ
670 // there are several complications with the rich edit controls here
671 bool done = FALSE;
672 if ( IsRich() )
4bc1afd5 673 {
aac7e7fe
VZ
674 // first, ensure that the new text will be in the default style
675 if ( !m_defaultStyle.IsDefault() )
676 {
677 long start, end;
678 GetSelection(&start, &end);
0b8e5844
VS
679 SetStyle(start, end, m_defaultStyle);
680 }
681
682#if wxUSE_UNICODE_MSLU
683 // RichEdit doesn't have Unicode version of EM_REPLACESEL on Win9x,
684 // but EM_STREAMIN works
136cb3c7 685 if ( wxUsingUnicowsDll() && GetRichVersion() > 1 )
0b8e5844 686 {
79d26b32 687 done = StreamIn(valueDos, wxFONTENCODING_SYSTEM, selectionOnly);
aac7e7fe 688 }
0b8e5844 689#endif // wxUSE_UNICODE_MSLU
aac7e7fe 690
b4da152e 691#if !wxUSE_UNICODE
aac7e7fe
VZ
692 // next check if the text we're inserting must be shown in a non
693 // default charset -- this only works for RichEdit > 1.0
694 if ( GetRichVersion() > 1 )
695 {
696 wxFont font = m_defaultStyle.GetFont();
697 if ( !font.Ok() )
698 font = GetFont();
699
700 if ( font.Ok() )
701 {
702 wxFontEncoding encoding = font.GetEncoding();
703 if ( encoding != wxFONTENCODING_SYSTEM )
704 {
79d26b32 705 done = StreamIn(valueDos, encoding, selectionOnly);
aac7e7fe
VZ
706 }
707 }
708 }
9d7de3c2 709#endif // !wxUSE_UNICODE
4bc1afd5 710 }
4bc1afd5 711
aac7e7fe
VZ
712 if ( !done )
713#endif // wxUSE_RICHEDIT
714 {
2b5f62a0
VZ
715 // in some cases we get 2 EN_CHANGE notifications after the SendMessage
716 // call below which is confusing for the client code and so should be
717 // avoided
718 //
719 // these cases are: (a) plain EDIT controls if EM_REPLACESEL is used
720 // and there is a non empty selection currently and (b) rich text
721 // controls in any case
722 if (
5036ea90 723#if wxUSE_RICHEDIT
2b5f62a0
VZ
724 IsRich() ||
725#endif // wxUSE_RICHEDIT
726 (selectionOnly && HasSelection()) )
79d26b32 727 {
5036ea90 728 m_suppressNextUpdate = TRUE;
79d26b32
VZ
729 }
730
5036ea90
VZ
731 ::SendMessage(GetHwnd(), selectionOnly ? EM_REPLACESEL : WM_SETTEXT,
732 0, (LPARAM)valueDos.c_str());
2b5f62a0
VZ
733
734 // OTOH, non rich text controls don't generate any events at all when
735 // we use WM_SETTEXT -- have to emulate them here
736 if ( !selectionOnly
737#if wxUSE_RICHEDIT
738 && !IsRich()
739#endif // wxUSE_RICHEDIT
740 )
741 {
8d1e36f7
JS
742 // Windows already sends an update event for single-line
743 // controls.
744 if ( m_windowStyle & wxTE_MULTILINE )
745 SendUpdateEvent();
2b5f62a0 746 }
aac7e7fe 747 }
2bda0e17 748
a1b82138 749 AdjustSpaceLimit();
2bda0e17
KB
750}
751
a1b82138
VZ
752void wxTextCtrl::AppendText(const wxString& text)
753{
754 SetInsertionPointEnd();
aac7e7fe 755
a1b82138 756 WriteText(text);
2b5f62a0
VZ
757
758#if wxUSE_RICHEDIT
759 if ( IsMultiLine() && GetRichVersion() > 1 )
760 {
761 // setting the caret to the end and showing it simply doesn't work for
762 // RichEdit 2.0 -- force it to still do what we want
763 ::SendMessage(GetHwnd(), EM_LINESCROLL, 0, GetNumberOfLines());
764 }
765#endif // wxUSE_RICHEDIT
a1b82138
VZ
766}
767
768void wxTextCtrl::Clear()
769{
fda7962d 770 ::SetWindowText(GetHwnd(), wxEmptyString);
5036ea90
VZ
771
772#if wxUSE_RICHEDIT
773 if ( !IsRich() )
774#endif // wxUSE_RICHEDIT
775 {
776 // rich edit controls send EN_UPDATE from WM_SETTEXT handler themselves
777 // but the normal ones don't -- make Clear() behaviour consistent by
778 // always sending this event
8d1e36f7
JS
779
780 // Windows already sends an update event for single-line
781 // controls.
782 if ( m_windowStyle & wxTE_MULTILINE )
783 SendUpdateEvent();
5036ea90 784 }
a1b82138
VZ
785}
786
94af7d45
VZ
787#ifdef __WIN32__
788
789bool wxTextCtrl::EmulateKeyPress(const wxKeyEvent& event)
790{
791 SetFocus();
792
793 size_t lenOld = GetValue().length();
794
795 wxUint32 code = event.GetRawKeyCode();
566d84a7
RL
796 ::keybd_event(code, 0, 0 /* key press */, 0);
797 ::keybd_event(code, 0, KEYEVENTF_KEYUP, 0);
94af7d45
VZ
798
799 // assume that any alphanumeric key changes the total number of characters
800 // in the control - this should work in 99% of cases
801 return GetValue().length() != lenOld;
802}
803
804#endif // __WIN32__
805
a1b82138 806// ----------------------------------------------------------------------------
2bda0e17 807// Clipboard operations
a1b82138
VZ
808// ----------------------------------------------------------------------------
809
cd471848 810void wxTextCtrl::Copy()
2bda0e17 811{
e702ff0f
JS
812 if (CanCopy())
813 {
a5aa8086 814 ::SendMessage(GetHwnd(), WM_COPY, 0, 0L);
e702ff0f 815 }
2bda0e17
KB
816}
817
cd471848 818void wxTextCtrl::Cut()
2bda0e17 819{
e702ff0f
JS
820 if (CanCut())
821 {
a5aa8086 822 ::SendMessage(GetHwnd(), WM_CUT, 0, 0L);
e702ff0f 823 }
2bda0e17
KB
824}
825
cd471848 826void wxTextCtrl::Paste()
2bda0e17 827{
e702ff0f
JS
828 if (CanPaste())
829 {
a5aa8086 830 ::SendMessage(GetHwnd(), WM_PASTE, 0, 0L);
e702ff0f 831 }
2bda0e17
KB
832}
833
2b5f62a0 834bool wxTextCtrl::HasSelection() const
a1b82138 835{
a1b82138 836 long from, to;
a5aa8086
VZ
837 GetSelection(&from, &to);
838 return from != to;
a1b82138
VZ
839}
840
2b5f62a0
VZ
841bool wxTextCtrl::CanCopy() const
842{
843 // Can copy if there's a selection
844 return HasSelection();
845}
846
a1b82138
VZ
847bool wxTextCtrl::CanCut() const
848{
a5aa8086 849 return CanCopy() && IsEditable();
a1b82138
VZ
850}
851
852bool wxTextCtrl::CanPaste() const
853{
aac7e7fe
VZ
854 if ( !IsEditable() )
855 return FALSE;
856
a1b82138 857#if wxUSE_RICHEDIT
aac7e7fe 858 if ( IsRich() )
a1b82138 859 {
aac7e7fe
VZ
860 UINT cf = 0; // 0 == any format
861
862 return ::SendMessage(GetHwnd(), EM_CANPASTE, cf, 0) != 0;
a1b82138 863 }
aac7e7fe 864#endif // wxUSE_RICHEDIT
a1b82138
VZ
865
866 // Standard edit control: check for straight text on clipboard
aac7e7fe
VZ
867 if ( !::OpenClipboard(GetHwndOf(wxTheApp->GetTopWindow())) )
868 return FALSE;
869
870 bool isTextAvailable = ::IsClipboardFormatAvailable(CF_TEXT) != 0;
871 ::CloseClipboard();
a1b82138
VZ
872
873 return isTextAvailable;
874}
875
876// ----------------------------------------------------------------------------
877// Accessors
878// ----------------------------------------------------------------------------
879
debe6624 880void wxTextCtrl::SetEditable(bool editable)
2bda0e17 881{
a1b82138
VZ
882 HWND hWnd = GetHwnd();
883 SendMessage(hWnd, EM_SETREADONLY, (WPARAM)!editable, (LPARAM)0L);
2bda0e17
KB
884}
885
debe6624 886void wxTextCtrl::SetInsertionPoint(long pos)
2bda0e17 887{
a5aa8086 888 DoSetSelection(pos, pos);
2bda0e17
KB
889}
890
cd471848 891void wxTextCtrl::SetInsertionPointEnd()
2bda0e17 892{
a9d3434a
VZ
893 // we must not do anything if the caret is already there because calling
894 // SetInsertionPoint() thaws the controls if Freeze() had been called even
895 // if it doesn't actually move the caret anywhere and so the simple fact of
896 // doing it results in horrible flicker when appending big amounts of text
897 // to the control in a few chunks (see DoAddText() test in the text sample)
898 if ( GetInsertionPoint() == GetLastPosition() )
899 return;
900
a5aa8086
VZ
901 long pos;
902
903#if wxUSE_RICHEDIT
904 if ( m_verRichEdit == 1 )
905 {
906 // we don't have to waste time calling GetLastPosition() in this case
907 pos = -1;
908 }
909 else // !RichEdit 1.0
910#endif // wxUSE_RICHEDIT
911 {
912 pos = GetLastPosition();
913 }
914
a1b82138 915 SetInsertionPoint(pos);
2bda0e17
KB
916}
917
cd471848 918long wxTextCtrl::GetInsertionPoint() const
2bda0e17 919{
57c208c5 920#if wxUSE_RICHEDIT
aac7e7fe 921 if ( IsRich() )
a1b82138
VZ
922 {
923 CHARRANGE range;
924 range.cpMin = 0;
925 range.cpMax = 0;
926 SendMessage(GetHwnd(), EM_EXGETSEL, 0, (LPARAM) &range);
927 return range.cpMin;
928 }
aac7e7fe 929#endif // wxUSE_RICHEDIT
2bda0e17 930
a1b82138
VZ
931 DWORD Pos = (DWORD)SendMessage(GetHwnd(), EM_GETSEL, 0, 0L);
932 return Pos & 0xFFFF;
2bda0e17
KB
933}
934
cd471848 935long wxTextCtrl::GetLastPosition() const
2bda0e17 936{
a5aa8086
VZ
937 int numLines = GetNumberOfLines();
938 long posStartLastLine = XYToPosition(0, numLines - 1);
39136494 939
a5aa8086 940 long lenLastLine = GetLengthOfLineContainingPos(posStartLastLine);
2bda0e17 941
a5aa8086 942 return posStartLastLine + lenLastLine;
2bda0e17
KB
943}
944
a1b82138
VZ
945// If the return values from and to are the same, there is no
946// selection.
947void wxTextCtrl::GetSelection(long* from, long* to) const
948{
949#if wxUSE_RICHEDIT
aac7e7fe 950 if ( IsRich() )
a1b82138
VZ
951 {
952 CHARRANGE charRange;
aac7e7fe 953 ::SendMessage(GetHwnd(), EM_EXGETSEL, 0, (LPARAM) &charRange);
a1b82138
VZ
954
955 *from = charRange.cpMin;
956 *to = charRange.cpMax;
a1b82138 957 }
4bc1afd5 958 else
aac7e7fe 959#endif // !wxUSE_RICHEDIT
4bc1afd5
VZ
960 {
961 DWORD dwStart, dwEnd;
aac7e7fe 962 ::SendMessage(GetHwnd(), EM_GETSEL, (WPARAM)&dwStart, (LPARAM)&dwEnd);
a1b82138 963
4bc1afd5
VZ
964 *from = dwStart;
965 *to = dwEnd;
966 }
a1b82138
VZ
967}
968
969bool wxTextCtrl::IsEditable() const
970{
51c14c62
VZ
971 // strangely enough, we may be called before the control is created: our
972 // own Create() calls MSWGetStyle() which calls AcceptsFocus() which calls
973 // us
974 if ( !m_hWnd )
975 return TRUE;
976
a1b82138
VZ
977 long style = ::GetWindowLong(GetHwnd(), GWL_STYLE);
978
a5aa8086 979 return (style & ES_READONLY) == 0;
a1b82138
VZ
980}
981
982// ----------------------------------------------------------------------------
aac7e7fe 983// selection
a1b82138
VZ
984// ----------------------------------------------------------------------------
985
aac7e7fe 986void wxTextCtrl::SetSelection(long from, long to)
2bda0e17 987{
aac7e7fe
VZ
988 // if from and to are both -1, it means (in wxWindows) that all text should
989 // be selected - translate into Windows convention
990 if ( (from == -1) && (to == -1) )
991 {
992 from = 0;
993 to = -1;
994 }
995
a5aa8086
VZ
996 DoSetSelection(from, to);
997}
998
999void wxTextCtrl::DoSetSelection(long from, long to, bool scrollCaret)
1000{
789295bf 1001 HWND hWnd = GetHwnd();
39136494 1002
2bda0e17 1003#ifdef __WIN32__
aac7e7fe
VZ
1004#if wxUSE_RICHEDIT
1005 if ( IsRich() )
1006 {
db50ec5a
VZ
1007 CHARRANGE range;
1008 range.cpMin = from;
1009 range.cpMax = to;
1010 SendMessage(hWnd, EM_EXSETSEL, 0, (LPARAM) &range);
1011 }
1012 else
1013#endif // wxUSE_RICHEDIT
1014 {
1015 SendMessage(hWnd, EM_SETSEL, (WPARAM)from, (LPARAM)to);
1016 }
1017
1018 if ( scrollCaret )
1019 {
1020#if wxUSE_RICHEDIT
98e19a58
VZ
1021 // richedit 3.0 (i.e. the version living in riched20.dll distributed
1022 // with Windows 2000 and beyond) doesn't honour EM_SCROLLCARET when
1023 // emulating richedit 2.0 unless the control has focus or ECO_NOHIDESEL
1024 // option is set (but it does work ok in richedit 1.0 mode...)
1025 //
1026 // so to make it work we either need to give focus to it here which
1027 // will probably create many problems (dummy focus events; window
1028 // containing the text control being brought to foreground
1029 // unexpectedly; ...) or to temporarily set ECO_NOHIDESEL which may
db50ec5a
VZ
1030 // create other problems too -- and in fact it does because if we turn
1031 // on/off this style while appending the text to the control, the
1032 // vertical scrollbar never appears in it even if we append tons of
1033 // text and to work around this the only solution I found was to use
1034 // ES_DISABLENOSCROLL
1035 //
1036 // this is very ugly but I don't see any other way to make this work
98e19a58
VZ
1037 if ( GetRichVersion() > 1 )
1038 {
1039 if ( !HasFlag(wxTE_NOHIDESEL) )
1040 {
1041 ::SendMessage(GetHwnd(), EM_SETOPTIONS,
1042 ECOOP_OR, ECO_NOHIDESEL);
1043 }
1044 //else: everything is already ok
1045 }
aac7e7fe 1046#endif // wxUSE_RICHEDIT
a1b82138 1047
aac7e7fe 1048 SendMessage(hWnd, EM_SCROLLCARET, (WPARAM)0, (LPARAM)0);
98e19a58
VZ
1049
1050#if wxUSE_RICHEDIT
db50ec5a
VZ
1051 // restore ECO_NOHIDESEL if we changed it
1052 if ( GetRichVersion() > 1 && !HasFlag(wxTE_NOHIDESEL) )
1053 {
1054 ::SendMessage(GetHwnd(), EM_SETOPTIONS,
1055 ECOOP_AND, ~ECO_NOHIDESEL);
1056 }
98e19a58 1057#endif // wxUSE_RICHEDIT
db50ec5a 1058 }
aac7e7fe
VZ
1059#else // Win16
1060 // WPARAM is 0: selection is scrolled into view
1061 SendMessage(hWnd, EM_SETSEL, (WPARAM)0, (LPARAM)MAKELONG(from, to));
1062#endif // Win32/16
1063}
1064
1065// ----------------------------------------------------------------------------
1066// Editing
1067// ----------------------------------------------------------------------------
39136494 1068
aac7e7fe
VZ
1069void wxTextCtrl::Replace(long from, long to, const wxString& value)
1070{
1071 // Set selection and remove it
f882d57e 1072 DoSetSelection(from, to, FALSE /* don't scroll caret into view */);
aac7e7fe
VZ
1073
1074 SendMessage(GetHwnd(), EM_REPLACESEL,
2bda0e17 1075#ifdef __WIN32__
aac7e7fe 1076 TRUE,
2bda0e17 1077#else
aac7e7fe 1078 FALSE,
2bda0e17 1079#endif
aac7e7fe
VZ
1080 (LPARAM)value.c_str());
1081}
1082
1083void wxTextCtrl::Remove(long from, long to)
1084{
fda7962d 1085 Replace(from, to, wxEmptyString);
2bda0e17
KB
1086}
1087
1088bool wxTextCtrl::LoadFile(const wxString& file)
1089{
a1b82138 1090 if ( wxTextCtrlBase::LoadFile(file) )
cd471848 1091 {
a1b82138
VZ
1092 // update the size limit if needed
1093 AdjustSpaceLimit();
2bda0e17 1094
a1b82138 1095 return TRUE;
2bda0e17 1096 }
2bda0e17 1097
a1b82138 1098 return FALSE;
2bda0e17
KB
1099}
1100
cd471848 1101bool wxTextCtrl::IsModified() const
2bda0e17 1102{
a5aa8086 1103 return SendMessage(GetHwnd(), EM_GETMODIFY, 0, 0) != 0;
2bda0e17
KB
1104}
1105
1106// Makes 'unmodified'
cd471848 1107void wxTextCtrl::DiscardEdits()
2bda0e17 1108{
a1b82138 1109 SendMessage(GetHwnd(), EM_SETMODIFY, FALSE, 0L);
2bda0e17
KB
1110}
1111
cd471848 1112int wxTextCtrl::GetNumberOfLines() const
2bda0e17 1113{
789295bf 1114 return (int)SendMessage(GetHwnd(), EM_GETLINECOUNT, (WPARAM)0, (LPARAM)0);
2bda0e17
KB
1115}
1116
debe6624 1117long wxTextCtrl::XYToPosition(long x, long y) const
2bda0e17 1118{
2bda0e17 1119 // This gets the char index for the _beginning_ of this line
a5aa8086
VZ
1120 long charIndex = SendMessage(GetHwnd(), EM_LINEINDEX, (WPARAM)y, (LPARAM)0);
1121
1122 return charIndex + x;
2bda0e17
KB
1123}
1124
0efe5ba7 1125bool wxTextCtrl::PositionToXY(long pos, long *x, long *y) const
2bda0e17 1126{
789295bf 1127 HWND hWnd = GetHwnd();
2bda0e17
KB
1128
1129 // This gets the line number containing the character
a5aa8086 1130 long lineNo;
0efe5ba7 1131#if wxUSE_RICHEDIT
aac7e7fe 1132 if ( IsRich() )
0efe5ba7 1133 {
a5aa8086 1134 lineNo = SendMessage(hWnd, EM_EXLINEFROMCHAR, 0, (LPARAM)pos);
0efe5ba7
VZ
1135 }
1136 else
1137#endif // wxUSE_RICHEDIT
a5aa8086
VZ
1138 {
1139 lineNo = SendMessage(hWnd, EM_LINEFROMCHAR, (WPARAM)pos, 0);
1140 }
0efe5ba7
VZ
1141
1142 if ( lineNo == -1 )
1143 {
1144 // no such line
1145 return FALSE;
1146 }
1147
2bda0e17 1148 // This gets the char index for the _beginning_ of this line
a5aa8086 1149 long charIndex = SendMessage(hWnd, EM_LINEINDEX, (WPARAM)lineNo, (LPARAM)0);
0efe5ba7
VZ
1150 if ( charIndex == -1 )
1151 {
1152 return FALSE;
1153 }
1154
2bda0e17 1155 // The X position must therefore be the different between pos and charIndex
0efe5ba7 1156 if ( x )
a5aa8086 1157 *x = pos - charIndex;
0efe5ba7 1158 if ( y )
a5aa8086 1159 *y = lineNo;
0efe5ba7
VZ
1160
1161 return TRUE;
2bda0e17
KB
1162}
1163
debe6624 1164void wxTextCtrl::ShowPosition(long pos)
2bda0e17 1165{
789295bf 1166 HWND hWnd = GetHwnd();
2bda0e17
KB
1167
1168 // To scroll to a position, we pass the number of lines and characters
1169 // to scroll *by*. This means that we need to:
1170 // (1) Find the line position of the current line.
1171 // (2) Find the line position of pos.
1172 // (3) Scroll by (pos - current).
1173 // For now, ignore the horizontal scrolling.
1174
1175 // Is this where scrolling is relative to - the line containing the caret?
1176 // Or is the first visible line??? Try first visible line.
1177// int currentLineLineNo1 = (int)SendMessage(hWnd, EM_LINEFROMCHAR, (WPARAM)-1, (LPARAM)0L);
1178
1179 int currentLineLineNo = (int)SendMessage(hWnd, EM_GETFIRSTVISIBLELINE, (WPARAM)0, (LPARAM)0L);
1180
1181 int specifiedLineLineNo = (int)SendMessage(hWnd, EM_LINEFROMCHAR, (WPARAM)pos, (LPARAM)0L);
39136494 1182
2bda0e17
KB
1183 int linesToScroll = specifiedLineLineNo - currentLineLineNo;
1184
2bda0e17 1185 if (linesToScroll != 0)
de4d7713 1186 (void)SendMessage(hWnd, EM_LINESCROLL, (WPARAM)0, (LPARAM)linesToScroll);
2bda0e17
KB
1187}
1188
a5aa8086
VZ
1189long wxTextCtrl::GetLengthOfLineContainingPos(long pos) const
1190{
1191 return ::SendMessage(GetHwnd(), EM_LINELENGTH, (WPARAM)pos, 0);
1192}
1193
debe6624 1194int wxTextCtrl::GetLineLength(long lineNo) const
2bda0e17 1195{
a5aa8086
VZ
1196 long pos = XYToPosition(0, lineNo);
1197
1198 return GetLengthOfLineContainingPos(pos);
2bda0e17
KB
1199}
1200
debe6624 1201wxString wxTextCtrl::GetLineText(long lineNo) const
2bda0e17 1202{
a1b82138 1203 size_t len = (size_t)GetLineLength(lineNo) + 1;
488fe1fe 1204
f6bcfd97
BP
1205 // there must be at least enough place for the length WORD in the
1206 // buffer
1207 len += sizeof(WORD);
4438caf4 1208
f6bcfd97 1209 wxString str;
de564874
MB
1210 {
1211 wxStringBufferLength tmp(str, len);
1212 wxChar *buf = tmp;
1213
1214 *(WORD *)buf = (WORD)len;
1215 len = (size_t)::SendMessage(GetHwnd(), EM_GETLINE,
1216 lineNo, (LPARAM)buf);
1217 buf[len] = 0;
1218 tmp.SetLength(len);
1219 }
4438caf4
VZ
1220
1221 return str;
2bda0e17
KB
1222}
1223
d7eee191
VZ
1224void wxTextCtrl::SetMaxLength(unsigned long len)
1225{
1226 ::SendMessage(GetHwnd(), EM_LIMITTEXT, len, 0);
1227}
1228
a1b82138 1229// ----------------------------------------------------------------------------
ca8b28f2 1230// Undo/redo
a1b82138
VZ
1231// ----------------------------------------------------------------------------
1232
ca8b28f2
JS
1233void wxTextCtrl::Undo()
1234{
1235 if (CanUndo())
1236 {
789295bf 1237 ::SendMessage(GetHwnd(), EM_UNDO, 0, 0);
ca8b28f2
JS
1238 }
1239}
1240
1241void wxTextCtrl::Redo()
1242{
1243 if (CanRedo())
1244 {
1245 // Same as Undo, since Undo undoes the undo, i.e. a redo.
789295bf 1246 ::SendMessage(GetHwnd(), EM_UNDO, 0, 0);
ca8b28f2
JS
1247 }
1248}
1249
1250bool wxTextCtrl::CanUndo() const
1251{
a5aa8086 1252 return ::SendMessage(GetHwnd(), EM_CANUNDO, 0, 0) != 0;
ca8b28f2
JS
1253}
1254
1255bool wxTextCtrl::CanRedo() const
1256{
a5aa8086 1257 return ::SendMessage(GetHwnd(), EM_CANUNDO, 0, 0) != 0;
ca8b28f2
JS
1258}
1259
e3a6a6b2
VZ
1260// ----------------------------------------------------------------------------
1261// caret handling (Windows only)
1262// ----------------------------------------------------------------------------
1263
1264bool wxTextCtrl::ShowNativeCaret(bool show)
1265{
1266 if ( show != m_isNativeCaretShown )
1267 {
1268 if ( !(show ? ::ShowCaret(GetHwnd()) : ::HideCaret(GetHwnd())) )
1269 {
1270 // not an error, may simply indicate that it's not shown/hidden
1271 // yet (i.e. it had been hidden/showh 2 times before)
1272 return false;
1273 }
1274
1275 m_isNativeCaretShown = show;
1276 }
1277
1278 return true;
1279}
1280
a1b82138
VZ
1281// ----------------------------------------------------------------------------
1282// implemenation details
1283// ----------------------------------------------------------------------------
39136494 1284
2bda0e17
KB
1285void wxTextCtrl::Command(wxCommandEvent & event)
1286{
a1b82138
VZ
1287 SetValue(event.GetString());
1288 ProcessCommand (event);
2bda0e17
KB
1289}
1290
1291void wxTextCtrl::OnDropFiles(wxDropFilesEvent& event)
1292{
a1b82138
VZ
1293 // By default, load the first file into the text window.
1294 if (event.GetNumberOfFiles() > 0)
1295 {
1296 LoadFile(event.GetFiles()[0]);
1297 }
2bda0e17
KB
1298}
1299
a37d422a
VZ
1300// ----------------------------------------------------------------------------
1301// kbd input processing
1302// ----------------------------------------------------------------------------
1303
1304bool wxTextCtrl::MSWShouldPreProcessMessage(WXMSG* pMsg)
1305{
1306 MSG *msg = (MSG *)pMsg;
1307
1308 // check for our special keys here: if we don't do it and the parent frame
1309 // uses them as accelerators, they wouldn't work at all, so we disable
1310 // usual preprocessing for them
1311 if ( msg->message == WM_KEYDOWN )
1312 {
574c939e 1313 WORD vkey = (WORD) msg->wParam;
a37d422a
VZ
1314 if ( (HIWORD(msg->lParam) & KF_ALTDOWN) == KF_ALTDOWN )
1315 {
1316 if ( vkey == VK_BACK )
1317 return FALSE;
1318 }
1319 else // no Alt
1320 {
cf6e951c
VZ
1321 // we want to process some Ctrl-foo and Shift-bar but no key
1322 // combinations without either Ctrl or Shift nor with both of them
1323 // pressed
1324 const int ctrl = wxIsCtrlDown(),
1325 shift = wxIsShiftDown();
1326 switch ( ctrl + shift )
a37d422a 1327 {
cf6e951c
VZ
1328 default:
1329 wxFAIL_MSG( _T("how many modifiers have we got?") );
1330 // fall through
1331
1332 case 0:
1333 case 2:
1334 break;
1335
1336 case 1:
1337 // either Ctrl or Shift pressed
1338 if ( ctrl )
1339 {
1340 switch ( vkey )
1341 {
1342 case 'C':
1343 case 'V':
1344 case 'X':
1345 case VK_INSERT:
1346 case VK_DELETE:
1347 case VK_HOME:
1348 case VK_END:
1349 return FALSE;
1350 }
1351 }
1352 else // Shift is pressed
1353 {
1354 if ( vkey == VK_INSERT || vkey == VK_DELETE )
1355 return FALSE;
1356 }
a37d422a
VZ
1357 }
1358 }
1359 }
1360
1361 return wxControl::MSWShouldPreProcessMessage(pMsg);
1362}
1363
2bda0e17
KB
1364void wxTextCtrl::OnChar(wxKeyEvent& event)
1365{
77e00fe9 1366 switch ( event.GetKeyCode() )
cd471848 1367 {
cd471848 1368 case WXK_RETURN:
39136494 1369 if ( !(m_windowStyle & wxTE_MULTILINE) )
cd471848
VZ
1370 {
1371 wxCommandEvent event(wxEVT_COMMAND_TEXT_ENTER, m_windowId);
bfbd6dc1 1372 InitCommandEvent(event);
f6bcfd97 1373 event.SetString(GetValue());
cd471848
VZ
1374 if ( GetEventHandler()->ProcessEvent(event) )
1375 return;
1376 }
5fb9fcfc
VZ
1377 //else: multiline controls need Enter for themselves
1378
1379 break;
4d91c1d1 1380
cd471848 1381 case WXK_TAB:
319fefa9
VZ
1382 // always produce navigation event - even if we process TAB
1383 // ourselves the fact that we got here means that the user code
1384 // decided to skip processing of this TAB - probably to let it
1385 // do its default job.
cd471848 1386 {
5fb9fcfc
VZ
1387 wxNavigationKeyEvent eventNav;
1388 eventNav.SetDirection(!event.ShiftDown());
8614c467 1389 eventNav.SetWindowChange(event.ControlDown());
5fb9fcfc 1390 eventNav.SetEventObject(this);
39136494 1391
d9506e77 1392 if ( GetParent()->GetEventHandler()->ProcessEvent(eventNav) )
cd471848
VZ
1393 return;
1394 }
341c92a8 1395 break;
cd471848 1396 }
39136494 1397
8614c467 1398 // no, we didn't process it
42e69d6b 1399 event.Skip();
2bda0e17
KB
1400}
1401
0cf5b099
VZ
1402long wxTextCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
1403{
e7e91e03
VZ
1404 long lRc = wxTextCtrlBase::MSWWindowProc(nMsg, wParam, lParam);
1405
0cf5b099
VZ
1406 if ( nMsg == WM_GETDLGCODE )
1407 {
2b5f62a0
VZ
1408 // we always want the chars and the arrows: the arrows for navigation
1409 // and the chars because we want Ctrl-C to work even in a read only
1410 // control
1411 long lDlgCode = DLGC_WANTCHARS | DLGC_WANTARROWS;
1412
080c709f
VZ
1413 if ( IsEditable() )
1414 {
080c709f
VZ
1415 // we may have several different cases:
1416 // 1. normal case: both TAB and ENTER are used for dlg navigation
1417 // 2. ctrl which wants TAB for itself: ENTER is used to pass to the
1418 // next control in the dialog
1419 // 3. ctrl which wants ENTER for itself: TAB is used for dialog
1420 // navigation
1421 // 4. ctrl which wants both TAB and ENTER: Ctrl-ENTER is used to go
1422 // to the next control
1423
1424 // the multiline edit control should always get <Return> for itself
1425 if ( HasFlag(wxTE_PROCESS_ENTER) || HasFlag(wxTE_MULTILINE) )
1426 lDlgCode |= DLGC_WANTMESSAGE;
1427
1428 if ( HasFlag(wxTE_PROCESS_TAB) )
1429 lDlgCode |= DLGC_WANTTAB;
1430
1431 lRc |= lDlgCode;
1432 }
1433 else // !editable
1434 {
e52d9c78
VZ
1435 // NB: use "=", not "|=" as the base class version returns the
1436 // same flags is this state as usual (i.e. including
1437 // DLGC_WANTMESSAGE). This is strange (how does it work in the
1438 // native Win32 apps?) but for now live with it.
2b5f62a0 1439 lRc = lDlgCode;
080c709f 1440 }
0cf5b099
VZ
1441 }
1442
e7e91e03 1443 return lRc;
129223d6
VZ
1444}
1445
1446// ----------------------------------------------------------------------------
1447// text control event processing
1448// ----------------------------------------------------------------------------
1449
5036ea90
VZ
1450bool wxTextCtrl::SendUpdateEvent()
1451{
1452 // is event reporting suspended?
1453 if ( m_suppressNextUpdate )
1454 {
1455 // do process the next one
1456 m_suppressNextUpdate = FALSE;
1457
1458 return FALSE;
1459 }
1460
1461 wxCommandEvent event(wxEVT_COMMAND_TEXT_UPDATED, GetId());
1462 InitCommandEvent(event);
1463 event.SetString(GetValue());
1464
1465 return ProcessCommand(event);
1466}
1467
debe6624 1468bool wxTextCtrl::MSWCommand(WXUINT param, WXWORD WXUNUSED(id))
2bda0e17 1469{
5036ea90 1470 switch ( param )
789295bf
VZ
1471 {
1472 case EN_SETFOCUS:
1473 case EN_KILLFOCUS:
1474 {
1475 wxFocusEvent event(param == EN_KILLFOCUS ? wxEVT_KILL_FOCUS
d7eee191
VZ
1476 : wxEVT_SET_FOCUS,
1477 m_windowId);
5036ea90 1478 event.SetEventObject(this);
789295bf
VZ
1479 GetEventHandler()->ProcessEvent(event);
1480 }
1481 break;
ae29de83 1482
789295bf 1483 case EN_CHANGE:
5036ea90 1484 SendUpdateEvent();
789295bf 1485 break;
2bda0e17 1486
b12915c1 1487 case EN_MAXTEXT:
5036ea90 1488 // the text size limit has been hit -- try to increase it
d7eee191
VZ
1489 if ( !AdjustSpaceLimit() )
1490 {
1491 wxCommandEvent event(wxEVT_COMMAND_TEXT_MAXLEN, m_windowId);
1492 InitCommandEvent(event);
1493 event.SetString(GetValue());
1494 ProcessCommand(event);
1495 }
789295bf
VZ
1496 break;
1497
5036ea90 1498 // the other edit notification messages are not processed
789295bf
VZ
1499 default:
1500 return FALSE;
1501 }
1502
1503 // processed
1504 return TRUE;
2bda0e17
KB
1505}
1506
33ac7e6f 1507WXHBRUSH wxTextCtrl::OnCtlColor(WXHDC pDC, WXHWND WXUNUSED(pWnd), WXUINT WXUNUSED(nCtlColor),
788722ac
JS
1508#if wxUSE_CTL3D
1509 WXUINT message,
1510 WXWPARAM wParam,
1511 WXLPARAM lParam
1512#else
33ac7e6f
KB
1513 WXUINT WXUNUSED(message),
1514 WXWPARAM WXUNUSED(wParam),
788722ac
JS
1515 WXLPARAM WXUNUSED(lParam)
1516#endif
1517 )
f6bcfd97
BP
1518{
1519#if wxUSE_CTL3D
1520 if ( m_useCtl3D )
1521 {
1522 HBRUSH hbrush = Ctl3dCtlColorEx(message, wParam, lParam);
1523 return (WXHBRUSH) hbrush;
1524 }
1525#endif // wxUSE_CTL3D
1526
1527 HDC hdc = (HDC)pDC;
f6bcfd97
BP
1528 wxColour colBack = GetBackgroundColour();
1529
1530 if (!IsEnabled() && (GetWindowStyle() & wxTE_MULTILINE) == 0)
a756f210 1531 colBack = wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE);
f6bcfd97
BP
1532
1533 ::SetBkColor(hdc, wxColourToRGB(colBack));
1534 ::SetTextColor(hdc, wxColourToRGB(GetForegroundColour()));
1535
1536 wxBrush *brush = wxTheBrushList->FindOrCreateBrush(colBack, wxSOLID);
1537
1538 return (WXHBRUSH)brush->GetResourceHandle();
1539}
1540
1541// In WIN16, need to override normal erasing because
1542// Ctl3D doesn't use the wxWindows background colour.
1543#ifdef __WIN16__
1544void wxTextCtrl::OnEraseBackground(wxEraseEvent& event)
1545{
1546 wxColour col(m_backgroundColour);
1547
1548#if wxUSE_CTL3D
1549 if (m_useCtl3D)
a756f210 1550 col = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
f6bcfd97
BP
1551#endif
1552
1553 RECT rect;
1554 ::GetClientRect(GetHwnd(), &rect);
1555
a5aa8086 1556 COLORREF ref = wxColourToRGB(col);
f6bcfd97
BP
1557 HBRUSH hBrush = ::CreateSolidBrush(ref);
1558 if ( !hBrush )
1559 wxLogLastError(wxT("CreateSolidBrush"));
1560
1561 HDC hdc = (HDC)event.GetDC()->GetHDC();
1562
1563 int mode = ::SetMapMode(hdc, MM_TEXT);
1564
1565 ::FillRect(hdc, &rect, hBrush);
1566 ::DeleteObject(hBrush);
1567 ::SetMapMode(hdc, mode);
1568
1569}
aac7e7fe 1570#endif // Win16
f6bcfd97 1571
d7eee191 1572bool wxTextCtrl::AdjustSpaceLimit()
789295bf 1573{
25889d3c 1574#ifndef __WIN16__
d7eee191
VZ
1575 unsigned int limit = ::SendMessage(GetHwnd(), EM_GETLIMITTEXT, 0, 0);
1576
1577 // HACK: we try to automatically extend the limit for the amount of text
1578 // to allow (interactively) entering more than 64Kb of text under
1579 // Win9x but we shouldn't reset the text limit which was previously
1580 // set explicitly with SetMaxLength()
1581 //
1582 // we could solve this by storing the limit we set in wxTextCtrl but
1583 // to save space we prefer to simply test here the actual limit
1584 // value: we consider that SetMaxLength() can only be called for
1585 // values < 32Kb
1586 if ( limit < 0x8000 )
1587 {
1588 // we've got more text than limit set by SetMaxLength()
1589 return FALSE;
1590 }
1591
1592 unsigned int len = ::GetWindowTextLength(GetHwnd());
17d8ee1c 1593 if ( len >= limit )
789295bf
VZ
1594 {
1595 limit = len + 0x8000; // 32Kb
1596
5ea105e0 1597#if wxUSE_RICHEDIT
aac7e7fe 1598 if ( IsRich() )
b12915c1
VZ
1599 {
1600 // as a nice side effect, this also allows passing limit > 64Kb
1601 ::SendMessage(GetHwnd(), EM_EXLIMITTEXT, 0, limit);
1602 }
789295bf 1603 else
b12915c1
VZ
1604#endif // wxUSE_RICHEDIT
1605 {
1606 if ( limit > 0xffff )
1607 {
1608 // this will set it to a platform-dependent maximum (much more
1609 // than 64Kb under NT)
1610 limit = 0;
1611 }
1612
789295bf 1613 ::SendMessage(GetHwnd(), EM_LIMITTEXT, limit, 0);
b12915c1 1614 }
789295bf 1615 }
b12915c1 1616#endif // !Win16
d7eee191
VZ
1617
1618 // we changed the limit
1619 return TRUE;
789295bf 1620}
2bda0e17 1621
a1b82138 1622bool wxTextCtrl::AcceptsFocus() const
2bda0e17 1623{
589c7163
VZ
1624 // we don't want focus if we can't be edited unless we're a multiline
1625 // control because then it might be still nice to get focus from keyboard
1626 // to be able to scroll it without mouse
1627 return (IsEditable() || IsMultiLine()) && wxControl::AcceptsFocus();
a1b82138 1628}
c085e333 1629
f68586e5 1630wxSize wxTextCtrl::DoGetBestSize() const
a1b82138
VZ
1631{
1632 int cx, cy;
1633 wxGetCharSize(GetHWND(), &cx, &cy, &GetFont());
1634
1635 int wText = DEFAULT_ITEM_WIDTH;
1636
1637 int hText = EDIT_HEIGHT_FROM_CHAR_HEIGHT(cy);
1638 if ( m_windowStyle & wxTE_MULTILINE )
1639 {
3988b155 1640 hText *= wxMax(GetNumberOfLines(), 5);
a1b82138
VZ
1641 }
1642 //else: for single line control everything is ok
1643
1644 return wxSize(wText, hText);
2bda0e17 1645}
a1b82138
VZ
1646
1647// ----------------------------------------------------------------------------
1648// standard handlers for standard edit menu events
1649// ----------------------------------------------------------------------------
2bda0e17 1650
bfbd6dc1 1651void wxTextCtrl::OnCut(wxCommandEvent& WXUNUSED(event))
e702ff0f
JS
1652{
1653 Cut();
1654}
1655
bfbd6dc1 1656void wxTextCtrl::OnCopy(wxCommandEvent& WXUNUSED(event))
e702ff0f
JS
1657{
1658 Copy();
1659}
1660
bfbd6dc1 1661void wxTextCtrl::OnPaste(wxCommandEvent& WXUNUSED(event))
e702ff0f
JS
1662{
1663 Paste();
1664}
1665
bfbd6dc1 1666void wxTextCtrl::OnUndo(wxCommandEvent& WXUNUSED(event))
e702ff0f
JS
1667{
1668 Undo();
1669}
1670
bfbd6dc1 1671void wxTextCtrl::OnRedo(wxCommandEvent& WXUNUSED(event))
e702ff0f
JS
1672{
1673 Redo();
1674}
1675
2eb10e2a 1676void wxTextCtrl::OnDelete(wxCommandEvent& WXUNUSED(event))
2b5f62a0
VZ
1677{
1678 long from, to;
1679 GetSelection(& from, & to);
1680 if (from != -1 && to != -1)
1681 Remove(from, to);
1682}
1683
2eb10e2a 1684void wxTextCtrl::OnSelectAll(wxCommandEvent& WXUNUSED(event))
2b5f62a0
VZ
1685{
1686 SetSelection(-1, -1);
1687}
1688
e702ff0f
JS
1689void wxTextCtrl::OnUpdateCut(wxUpdateUIEvent& event)
1690{
1691 event.Enable( CanCut() );
1692}
1693
1694void wxTextCtrl::OnUpdateCopy(wxUpdateUIEvent& event)
1695{
1696 event.Enable( CanCopy() );
1697}
1698
1699void wxTextCtrl::OnUpdatePaste(wxUpdateUIEvent& event)
1700{
1701 event.Enable( CanPaste() );
1702}
1703
1704void wxTextCtrl::OnUpdateUndo(wxUpdateUIEvent& event)
1705{
1706 event.Enable( CanUndo() );
1707}
1708
1709void wxTextCtrl::OnUpdateRedo(wxUpdateUIEvent& event)
1710{
1711 event.Enable( CanRedo() );
1712}
1713
2b5f62a0
VZ
1714void wxTextCtrl::OnUpdateDelete(wxUpdateUIEvent& event)
1715{
1716 long from, to;
1717 GetSelection(& from, & to);
1718 event.Enable(from != -1 && to != -1 && from != to && IsEditable()) ;
1719}
1720
1721void wxTextCtrl::OnUpdateSelectAll(wxUpdateUIEvent& event)
1722{
1723 event.Enable(GetLastPosition() > 0);
1724}
1725
1726void wxTextCtrl::OnRightClick(wxMouseEvent& event)
1727{
1728#if wxUSE_RICHEDIT
1729 if (IsRich())
1730 {
1731 if (!m_privateContextMenu)
1732 {
1733 m_privateContextMenu = new wxMenu;
1734 m_privateContextMenu->Append(wxID_UNDO, _("&Undo"));
1735 m_privateContextMenu->Append(wxID_REDO, _("&Redo"));
1736 m_privateContextMenu->AppendSeparator();
1737 m_privateContextMenu->Append(wxID_CUT, _("Cu&t"));
1738 m_privateContextMenu->Append(wxID_COPY, _("&Copy"));
1739 m_privateContextMenu->Append(wxID_PASTE, _("&Paste"));
1740 m_privateContextMenu->Append(wxID_CLEAR, _("&Delete"));
1741 m_privateContextMenu->AppendSeparator();
1742 m_privateContextMenu->Append(wxID_SELECTALL, _("Select &All"));
1743 }
1744 PopupMenu(m_privateContextMenu, event.GetPosition());
1745 return;
1746 }
1747 else
1748#endif
1749 event.Skip();
1750}
1751
2eb10e2a 1752void wxTextCtrl::OnSetFocus(wxFocusEvent& WXUNUSED(event))
e3a6a6b2
VZ
1753{
1754 // be sure the caret remains invisible if the user had hidden it
1755 if ( !m_isNativeCaretShown )
1756 {
1757 ::HideCaret(GetHwnd());
1758 }
1759}
1760
4bc1afd5
VZ
1761// the rest of the file only deals with the rich edit controls
1762#if wxUSE_RICHEDIT
1763
c57e3339
VZ
1764// ----------------------------------------------------------------------------
1765// EN_LINK processing
1766// ----------------------------------------------------------------------------
1767
a64c3b74 1768bool wxTextCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
c57e3339
VZ
1769{
1770 NMHDR *hdr = (NMHDR* )lParam;
1dae1d00 1771 switch ( hdr->code )
c57e3339 1772 {
3bce6687 1773 case EN_MSGFILTER:
1dae1d00
VZ
1774 {
1775 const MSGFILTER *msgf = (MSGFILTER *)lParam;
1776 UINT msg = msgf->msg;
1777
1778 // this is a bit crazy but richedit 1.0 sends us all mouse
1779 // events _except_ WM_LBUTTONUP (don't ask me why) so we have
1780 // generate the wxWin events for this message manually
1781 //
1782 // NB: in fact, this is still not totally correct as it does
1783 // send us WM_LBUTTONUP if the selection was cleared by the
1784 // last click -- so currently we get 2 events in this case,
1785 // but as I don't see any obvious way to check for this I
1786 // leave this code in place because it's still better than
1787 // not getting left up events at all
1788 if ( msg == WM_LBUTTONUP )
c57e3339 1789 {
1dae1d00
VZ
1790 WXUINT flags = msgf->wParam;
1791 int x = GET_X_LPARAM(msgf->lParam),
1792 y = GET_Y_LPARAM(msgf->lParam);
1793
1794 HandleMouseEvent(msg, x, y, flags);
c57e3339 1795 }
1dae1d00 1796 }
c57e3339 1797
1dae1d00
VZ
1798 // return TRUE to process the event (and FALSE to ignore it)
1799 return TRUE;
1800
1801 case EN_LINK:
1802 {
1803 const ENLINK *enlink = (ENLINK *)hdr;
1804
1805 switch ( enlink->msg )
1806 {
1807 case WM_SETCURSOR:
1808 // ok, so it is hardcoded - do we really nee to
1809 // customize it?
1810 ::SetCursor(GetHcursorOf(wxCursor(wxCURSOR_HAND)));
1811 *result = TRUE;
1812 break;
1813
1814 case WM_MOUSEMOVE:
1815 case WM_LBUTTONDOWN:
1816 case WM_LBUTTONUP:
1817 case WM_LBUTTONDBLCLK:
1818 case WM_RBUTTONDOWN:
1819 case WM_RBUTTONUP:
1820 case WM_RBUTTONDBLCLK:
1821 // send a mouse event
1822 {
1823 static const wxEventType eventsMouse[] =
1824 {
1825 wxEVT_MOTION,
1826 wxEVT_LEFT_DOWN,
1827 wxEVT_LEFT_UP,
1828 wxEVT_LEFT_DCLICK,
1829 wxEVT_RIGHT_DOWN,
1830 wxEVT_RIGHT_UP,
1831 wxEVT_RIGHT_DCLICK,
1832 };
1833
1834 // the event ids are consecutive
1835 wxMouseEvent
1836 evtMouse(eventsMouse[enlink->msg - WM_MOUSEMOVE]);
1837
1838 InitMouseEvent(evtMouse,
1839 GET_X_LPARAM(enlink->lParam),
1840 GET_Y_LPARAM(enlink->lParam),
1841 enlink->wParam);
1842
1843 wxTextUrlEvent event(m_windowId, evtMouse,
1844 enlink->chrg.cpMin,
1845 enlink->chrg.cpMax);
1846
1847 InitCommandEvent(event);
1848
1849 *result = ProcessCommand(event);
1850 }
1851 break;
1852 }
1853 }
1854 return TRUE;
c57e3339 1855 }
3bce6687 1856
d86ab8e2
VZ
1857 // not processed, leave it to the base class
1858 return wxTextCtrlBase::MSWOnNotify(idCtrl, lParam, result);
c57e3339
VZ
1859}
1860
f6bcfd97
BP
1861// ----------------------------------------------------------------------------
1862// colour setting for the rich edit controls
1863// ----------------------------------------------------------------------------
1864
f6bcfd97
BP
1865bool wxTextCtrl::SetBackgroundColour(const wxColour& colour)
1866{
1867 if ( !wxTextCtrlBase::SetBackgroundColour(colour) )
1868 {
1869 // colour didn't really change
1870 return FALSE;
1871 }
1872
1873 if ( IsRich() )
1874 {
1875 // rich edit doesn't use WM_CTLCOLOR, hence we need to send
1876 // EM_SETBKGNDCOLOR additionally
1877 ::SendMessage(GetHwnd(), EM_SETBKGNDCOLOR, 0, wxColourToRGB(colour));
1878 }
1879
1880 return TRUE;
1881}
1882
1883bool wxTextCtrl::SetForegroundColour(const wxColour& colour)
1884{
1885 if ( !wxTextCtrlBase::SetForegroundColour(colour) )
1886 {
1887 // colour didn't really change
1888 return FALSE;
1889 }
1890
1891 if ( IsRich() )
1892 {
1893 // change the colour of everything
1894 CHARFORMAT cf;
1895 wxZeroMemory(cf);
1896 cf.cbSize = sizeof(cf);
1897 cf.dwMask = CFM_COLOR;
1898 cf.crTextColor = wxColourToRGB(colour);
1899 ::SendMessage(GetHwnd(), EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
1900 }
1901
1902 return TRUE;
1903}
1904
4bc1afd5
VZ
1905// ----------------------------------------------------------------------------
1906// styling support for rich edit controls
1907// ----------------------------------------------------------------------------
1908
cc164686
RD
1909#if wxUSE_RICHEDIT
1910
4bc1afd5
VZ
1911bool wxTextCtrl::SetStyle(long start, long end, const wxTextAttr& style)
1912{
1913 if ( !IsRich() )
1914 {
1915 // can't do it with normal text control
1916 return FALSE;
1917 }
1918
a5aa8086
VZ
1919 // the richedit 1.0 doesn't handle setting background colour, so don't
1920 // even try to do anything if it's the only thing we want to change
e00a5d3c
JS
1921 if ( m_verRichEdit == 1 && !style.HasFont() && !style.HasTextColour() &&
1922 !style.HasLeftIndent() && !style.HasRightIndent() && !style.HasAlignment() &&
1923 !style.HasTabs() )
4bc1afd5 1924 {
be329a3d
RD
1925 // nothing to do: return TRUE if there was really nothing to do and
1926 // FALSE if we failed to set bg colour
4bc1afd5
VZ
1927 return !style.HasBackgroundColour();
1928 }
1929
1930 // order the range if needed
1931 if ( start > end )
1932 {
1933 long tmp = start;
1934 start = end;
1935 end = tmp;
1936 }
1937
1938 // we can only change the format of the selection, so select the range we
1939 // want and restore the old selection later
1940 long startOld, endOld;
1941 GetSelection(&startOld, &endOld);
1942
1943 // but do we really have to change the selection?
1944 bool changeSel = start != startOld || end != endOld;
1945
1946 if ( changeSel )
aac7e7fe 1947 {
f882d57e 1948 DoSetSelection(start, end, FALSE /* don't scroll caret into view */);
aac7e7fe 1949 }
4bc1afd5
VZ
1950
1951 // initialize CHARFORMAT struct
be329a3d
RD
1952#if wxUSE_RICHEDIT2
1953 CHARFORMAT2 cf;
1954#else
4bc1afd5 1955 CHARFORMAT cf;
be329a3d 1956#endif
a5aa8086 1957
4bc1afd5 1958 wxZeroMemory(cf);
a5aa8086
VZ
1959
1960 // we can't use CHARFORMAT2 with RichEdit 1.0, so pretend it is a simple
1961 // CHARFORMAT in that case
1962#if wxUSE_RICHEDIT2
1963 if ( m_verRichEdit == 1 )
1964 {
1965 // this is the only thing the control is going to grok
1966 cf.cbSize = sizeof(CHARFORMAT);
1967 }
1968 else
1969#endif
1970 {
1971 // CHARFORMAT or CHARFORMAT2
1972 cf.cbSize = sizeof(cf);
1973 }
4bc1afd5
VZ
1974
1975 if ( style.HasFont() )
1976 {
aac7e7fe
VZ
1977 // VZ: CFM_CHARSET doesn't seem to do anything at all in RichEdit 2.0
1978 // but using it doesn't seem to hurt neither so leaving it for now
1979
784164e1
VZ
1980 cf.dwMask |= CFM_FACE | CFM_SIZE | CFM_CHARSET |
1981 CFM_ITALIC | CFM_BOLD | CFM_UNDERLINE;
4bc1afd5
VZ
1982
1983 // fill in data from LOGFONT but recalculate lfHeight because we need
1984 // the real height in twips and not the negative number which
1985 // wxFillLogFont() returns (this is correct in general and works with
1986 // the Windows font mapper, but not here)
1987 LOGFONT lf;
1988 wxFillLogFont(&lf, &style.GetFont());
1989 cf.yHeight = 20*style.GetFont().GetPointSize(); // 1 pt = 20 twips
1990 cf.bCharSet = lf.lfCharSet;
1991 cf.bPitchAndFamily = lf.lfPitchAndFamily;
1992 wxStrncpy( cf.szFaceName, lf.lfFaceName, WXSIZEOF(cf.szFaceName) );
1993
784164e1
VZ
1994 // also deal with underline/italic/bold attributes: note that we must
1995 // always set CFM_ITALIC &c bits in dwMask, even if we don't set the
1996 // style to allow clearing it
4bc1afd5
VZ
1997 if ( lf.lfItalic )
1998 {
4bc1afd5
VZ
1999 cf.dwEffects |= CFE_ITALIC;
2000 }
2001
2002 if ( lf.lfWeight == FW_BOLD )
2003 {
4bc1afd5
VZ
2004 cf.dwEffects |= CFE_BOLD;
2005 }
2006
2007 if ( lf.lfUnderline )
2008 {
4bc1afd5
VZ
2009 cf.dwEffects |= CFE_UNDERLINE;
2010 }
2011
2012 // strikeout fonts are not supported by wxWindows
2013 }
2014
2015 if ( style.HasTextColour() )
2016 {
2017 cf.dwMask |= CFM_COLOR;
2018 cf.crTextColor = wxColourToRGB(style.GetTextColour());
2019 }
2020
be329a3d 2021#if wxUSE_RICHEDIT2
a5aa8086 2022 if ( m_verRichEdit != 1 && style.HasBackgroundColour() )
be329a3d
RD
2023 {
2024 cf.dwMask |= CFM_BACKCOLOR;
2025 cf.crBackColor = wxColourToRGB(style.GetBackgroundColour());
2026 }
784164e1
VZ
2027#endif // wxUSE_RICHEDIT2
2028
4bc1afd5
VZ
2029 // do format the selection
2030 bool ok = ::SendMessage(GetHwnd(), EM_SETCHARFORMAT,
2031 SCF_SELECTION, (LPARAM)&cf) != 0;
2032 if ( !ok )
2033 {
2034 wxLogDebug(_T("SendMessage(EM_SETCHARFORMAT, SCF_SELECTION) failed"));
2035 }
2036
e00a5d3c
JS
2037 // now do the paragraph formatting
2038 PARAFORMAT2 pf;
2039 wxZeroMemory(pf);
2040 // we can't use PARAFORMAT2 with RichEdit 1.0, so pretend it is a simple
2041 // PARAFORMAT in that case
2042#if wxUSE_RICHEDIT2
2043 if ( m_verRichEdit == 1 )
2044 {
2045 // this is the only thing the control is going to grok
2046 pf.cbSize = sizeof(PARAFORMAT);
2047 }
2048 else
2049#endif
2050 {
2051 // PARAFORMAT or PARAFORMAT2
2052 pf.cbSize = sizeof(pf);
2053 }
2054
2055 if (style.HasAlignment())
2056 {
2057 pf.dwMask |= PFM_ALIGNMENT;
2058 if (style.GetAlignment() == wxTEXT_ALIGNMENT_RIGHT)
2059 pf.wAlignment = PFA_RIGHT;
2060 else if (style.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE)
2061 pf.wAlignment = PFA_CENTER;
2062 else if (style.GetAlignment() == wxTEXT_ALIGNMENT_JUSTIFIED)
2063 pf.wAlignment = PFA_JUSTIFY;
2064 else
2065 pf.wAlignment = PFA_LEFT;
2066 }
2067
2068 if (style.HasLeftIndent())
2069 {
2070 pf.dwMask |= PFM_STARTINDENT;
2071
2072 // Convert from 1/10 mm to TWIPS
2073 pf.dxStartIndent = (int) (((double) style.GetLeftIndent()) * mm2twips / 10.0) ;
2074
2075 // TODO: do we need to specify dxOffset?
2076 }
2077
2078 if (style.HasRightIndent())
2079 {
2080 pf.dwMask |= PFM_RIGHTINDENT;
2081
2082 // Convert from 1/10 mm to TWIPS
2083 pf.dxRightIndent = (int) (((double) style.GetRightIndent()) * mm2twips / 10.0) ;
2084 }
2085
2086 if (style.HasTabs())
2087 {
2088 pf.dwMask |= PFM_TABSTOPS;
2089
2090 const wxArrayInt& tabs = style.GetTabs();
2091
2092 pf.cTabCount = wxMin(tabs.GetCount(), MAX_TAB_STOPS);
2093 size_t i;
2094 for (i = 0; i < (size_t) pf.cTabCount; i++)
2095 {
2096 // Convert from 1/10 mm to TWIPS
2097 pf.rgxTabs[i] = (int) (((double) tabs[i]) * mm2twips / 10.0) ;
2098 }
2099 }
2100
2101 if (pf.dwMask != 0)
2102 {
2103 // do format the selection
2104 bool ok = ::SendMessage(GetHwnd(), EM_SETPARAFORMAT,
2105 0, (LPARAM) &pf) != 0;
2106 if ( !ok )
2107 {
2108 wxLogDebug(_T("SendMessage(EM_SETPARAFORMAT, 0) failed"));
2109 }
2110 }
2111
4bc1afd5
VZ
2112 if ( changeSel )
2113 {
2114 // restore the original selection
aac7e7fe 2115 DoSetSelection(startOld, endOld, FALSE);
4bc1afd5
VZ
2116 }
2117
2118 return ok;
2119}
f6bcfd97 2120
5bf75ae7
VZ
2121bool wxTextCtrl::SetDefaultStyle(const wxTextAttr& style)
2122{
2123 if ( !wxTextCtrlBase::SetDefaultStyle(style) )
2124 return FALSE;
2125
2126 // we have to do this or the style wouldn't apply for the text typed by the
2127 // user
2128 long posLast = GetLastPosition();
2129 SetStyle(posLast, posLast, m_defaultStyle);
2130
2131 return TRUE;
2132}
2133
80185c6c 2134bool wxTextCtrl::GetStyle(long position, wxTextAttr& style)
e00a5d3c 2135{
80185c6c
JS
2136 if ( !IsRich() )
2137 {
2138 // can't do it with normal text control
2139 return FALSE;
2140 }
2141
2142 // initialize CHARFORMAT struct
2143#if wxUSE_RICHEDIT2
2144 CHARFORMAT2 cf;
2145#else
2146 CHARFORMAT cf;
2147#endif
2148
2149 wxZeroMemory(cf);
2150
2151 // we can't use CHARFORMAT2 with RichEdit 1.0, so pretend it is a simple
2152 // CHARFORMAT in that case
2153#if wxUSE_RICHEDIT2
2154 if ( m_verRichEdit == 1 )
2155 {
2156 // this is the only thing the control is going to grok
2157 cf.cbSize = sizeof(CHARFORMAT);
2158 }
2159 else
2160#endif
2161 {
2162 // CHARFORMAT or CHARFORMAT2
2163 cf.cbSize = sizeof(cf);
2164 }
2165 // we can only change the format of the selection, so select the range we
2166 // want and restore the old selection later
2167 long startOld, endOld;
2168 GetSelection(&startOld, &endOld);
2169
2170 // but do we really have to change the selection?
2171 bool changeSel = position != startOld || position != endOld;
2172
2173 if ( changeSel )
2174 {
2175 DoSetSelection(position, position, FALSE /* don't scroll caret into view */);
2176 }
2177
2178 // get the selection formatting
2179 (void) ::SendMessage(GetHwnd(), EM_GETCHARFORMAT,
2180 SCF_SELECTION, (LPARAM)&cf) ;
2181
2182 LOGFONT lf;
2183 lf.lfHeight = cf.yHeight;
2184 lf.lfWidth = 0;
2185 lf.lfCharSet = ANSI_CHARSET; // FIXME: how to get correct charset?
2186 lf.lfClipPrecision = 0;
2187 lf.lfEscapement = 0;
2188 wxStrcpy(lf.lfFaceName, cf.szFaceName);
2189 if (cf.dwEffects & CFE_ITALIC)
2190 lf.lfItalic = TRUE;
2191 lf.lfOrientation = 0;
2192 lf.lfPitchAndFamily = cf.bPitchAndFamily;
2193 lf.lfQuality = 0;
2194 if (cf.dwEffects & CFE_STRIKEOUT)
2195 lf.lfStrikeOut = TRUE;
2196 if (cf.dwEffects & CFE_UNDERLINE)
2197 lf.lfUnderline = TRUE;
2198 if (cf.dwEffects & CFE_BOLD)
2199 lf.lfWeight = FW_BOLD;
2200
2201 wxFont font = wxCreateFontFromLogFont(& lf);
2202 if (font.Ok())
2203 {
2204 style.SetFont(font);
2205 }
2206 style.SetTextColour(wxColour(cf.crTextColor));
2207
2208#if wxUSE_RICHEDIT2
2209 if ( m_verRichEdit != 1 )
2210 {
2211 // cf.dwMask |= CFM_BACKCOLOR;
2212 style.SetBackgroundColour(wxColour(cf.crBackColor));
2213 }
2214#endif // wxUSE_RICHEDIT2
2215
2216 // now get the paragraph formatting
2217 PARAFORMAT2 pf;
2218 wxZeroMemory(pf);
2219 // we can't use PARAFORMAT2 with RichEdit 1.0, so pretend it is a simple
2220 // PARAFORMAT in that case
2221#if wxUSE_RICHEDIT2
2222 if ( m_verRichEdit == 1 )
2223 {
2224 // this is the only thing the control is going to grok
2225 pf.cbSize = sizeof(PARAFORMAT);
2226 }
2227 else
2228#endif
2229 {
2230 // PARAFORMAT or PARAFORMAT2
2231 pf.cbSize = sizeof(pf);
2232 }
2233
2234 // do format the selection
2235 (void) ::SendMessage(GetHwnd(), EM_GETPARAFORMAT, 0, (LPARAM) &pf) ;
2236
2237 style.SetLeftIndent( (int) ((double) pf.dxStartIndent * twips2mm * 10.0) );
2238 style.SetRightIndent( (int) ((double) pf.dxRightIndent * twips2mm * 10.0) );
2239
2240 if (pf.wAlignment == PFA_CENTER)
2241 style.SetAlignment(wxTEXT_ALIGNMENT_CENTRE);
2242 else if (pf.wAlignment == PFA_RIGHT)
2243 style.SetAlignment(wxTEXT_ALIGNMENT_RIGHT);
2244 else if (pf.wAlignment == PFA_JUSTIFY)
2245 style.SetAlignment(wxTEXT_ALIGNMENT_JUSTIFIED);
2246 else
2247 style.SetAlignment(wxTEXT_ALIGNMENT_LEFT);
2248
2249 wxArrayInt tabStops;
2250 size_t i;
2251 for (i = 0; i < (size_t) pf.cTabCount; i++)
2252 {
2253 tabStops[i] = (int) ((double) (pf.rgxTabs[i] & 0xFFFF) * twips2mm * 10.0) ;
2254 }
2255
2256 if ( changeSel )
2257 {
2258 // restore the original selection
2259 DoSetSelection(startOld, endOld, FALSE);
2260 }
2261
2262 return TRUE;
e00a5d3c
JS
2263}
2264
cc164686
RD
2265#endif
2266
b12915c1
VZ
2267// ----------------------------------------------------------------------------
2268// wxRichEditModule
2269// ----------------------------------------------------------------------------
2270
b12915c1
VZ
2271bool wxRichEditModule::OnInit()
2272{
2273 // don't do anything - we will load it when needed
2274 return TRUE;
2275}
2276
2277void wxRichEditModule::OnExit()
2278{
136cb3c7 2279 for ( size_t i = 0; i < WXSIZEOF(ms_hRichEdit); i++ )
b12915c1 2280 {
a5aa8086
VZ
2281 if ( ms_hRichEdit[i] )
2282 {
2283 ::FreeLibrary(ms_hRichEdit[i]);
8c74e477 2284 ms_hRichEdit[i] = NULL;
a5aa8086 2285 }
b12915c1
VZ
2286 }
2287}
2288
2289/* static */
2290bool wxRichEditModule::Load(int version)
2291{
a5aa8086
VZ
2292 // we don't support loading richedit 3.0 as I don't know how to distinguish
2293 // it from 2.0 anyhow
2294 wxCHECK_MSG( version == 1 || version == 2, FALSE,
b12915c1
VZ
2295 _T("incorrect richedit control version requested") );
2296
a5aa8086
VZ
2297 // make it the index in the array
2298 version--;
2299
a5aa8086 2300 if ( ms_hRichEdit[version] == (HINSTANCE)-1 )
b12915c1 2301 {
a5aa8086
VZ
2302 // we had already tried to load it and failed
2303 return FALSE;
b12915c1
VZ
2304 }
2305
28978e0c
VZ
2306 if ( ms_hRichEdit[version] )
2307 {
2308 // we've already got this one
2309 return TRUE;
2310 }
2311
a5aa8086
VZ
2312 wxString dllname = version ? _T("riched20") : _T("riched32");
2313 dllname += _T(".dll");
b12915c1 2314
a5aa8086 2315 ms_hRichEdit[version] = ::LoadLibrary(dllname);
b12915c1 2316
a5aa8086 2317 if ( !ms_hRichEdit[version] )
b12915c1
VZ
2318 {
2319 wxLogSysError(_("Could not load Rich Edit DLL '%s'"), dllname.c_str());
2320
a5aa8086 2321 ms_hRichEdit[version] = (HINSTANCE)-1;
b12915c1
VZ
2322
2323 return FALSE;
2324 }
2325
2326 return TRUE;
2327}
2328
2329#endif // wxUSE_RICHEDIT
2330
1e6feb95 2331#endif // wxUSE_TEXTCTRL