]> git.saurik.com Git - wxWidgets.git/blame - src/msw/textctrl.cpp
compilation fix for non-threaded compilation (threads are still broken
[wxWidgets.git] / src / msw / textctrl.cpp
CommitLineData
2bda0e17
KB
1/////////////////////////////////////////////////////////////////////////////
2// Name: textctrl.cpp
3// Purpose: wxTextCtrl
4// Author: Julian Smart
5// Modified by:
6// Created: 04/01/98
7// RCS-ID: $Id$
8// Copyright: (c) Julian Smart and Markus Holzem
c085e333 9// Licence: wxWindows license
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
31#ifndef WX_PRECOMP
a1b82138
VZ
32 #include "wx/textctrl.h"
33 #include "wx/settings.h"
34 #include "wx/brush.h"
35 #include "wx/utils.h"
381dd4bf 36 #include "wx/intl.h"
a1b82138 37 #include "wx/log.h"
381dd4bf 38 #include "wx/app.h"
2bda0e17
KB
39#endif
40
47d67540 41#if wxUSE_CLIPBOARD
a1b82138 42 #include "wx/clipbrd.h"
2bda0e17
KB
43#endif
44
a1b82138
VZ
45#include "wx/textfile.h"
46
47#include <windowsx.h>
48
2bda0e17
KB
49#include "wx/msw/private.h"
50
a1b82138 51#include <string.h>
2bda0e17 52#include <stdlib.h>
a1b82138 53#include <sys/types.h>
fbc535ff
JS
54
55#if wxUSE_IOSTREAMH
3f4a0c5b 56# include <fstream.h>
fbc535ff 57#else
3f4a0c5b 58# include <fstream>
fbc535ff 59#endif
2bda0e17 60
c25a510b 61#if wxUSE_RICHEDIT && (!defined(__GNUWIN32__) || defined(wxUSE_NORLANDER_HEADERS))
cd471848 62 #include <richedit.h>
2bda0e17
KB
63#endif
64
b12915c1
VZ
65// ----------------------------------------------------------------------------
66// private classes
67// ----------------------------------------------------------------------------
68
69#if wxUSE_RICHEDIT
70
71// this module initializes RichEdit DLL if needed
72class wxRichEditModule : public wxModule
73{
74public:
75 virtual bool OnInit();
76 virtual void OnExit();
77
78 // get the version currently loaded, -1 if none
79 static int GetLoadedVersion() { return ms_verRichEdit; }
80
81 // load the richedit DLL of at least of required version
82 static bool Load(int version = 1);
83
84private:
85 // the handle to richedit DLL and the version of the DLL loaded
86 static HINSTANCE ms_hRichEdit;
87
88 // the DLL version loaded or -1 if none
89 static int ms_verRichEdit;
90
91 DECLARE_DYNAMIC_CLASS(wxRichEditModule)
92};
93
94HINSTANCE wxRichEditModule::ms_hRichEdit = (HINSTANCE)NULL;
95int wxRichEditModule::ms_verRichEdit = -1;
96
97IMPLEMENT_DYNAMIC_CLASS(wxRichEditModule, wxModule)
98
99#endif // wxUSE_RICHEDIT
e702ff0f 100
a1b82138
VZ
101// ----------------------------------------------------------------------------
102// event tables and other macros
103// ----------------------------------------------------------------------------
104
2bda0e17
KB
105IMPLEMENT_DYNAMIC_CLASS(wxTextCtrl, wxControl)
106
107BEGIN_EVENT_TABLE(wxTextCtrl, wxControl)
a1b82138
VZ
108 EVT_CHAR(wxTextCtrl::OnChar)
109 EVT_DROP_FILES(wxTextCtrl::OnDropFiles)
110
111 EVT_MENU(wxID_CUT, wxTextCtrl::OnCut)
112 EVT_MENU(wxID_COPY, wxTextCtrl::OnCopy)
113 EVT_MENU(wxID_PASTE, wxTextCtrl::OnPaste)
114 EVT_MENU(wxID_UNDO, wxTextCtrl::OnUndo)
115 EVT_MENU(wxID_REDO, wxTextCtrl::OnRedo)
116
117 EVT_UPDATE_UI(wxID_CUT, wxTextCtrl::OnUpdateCut)
118 EVT_UPDATE_UI(wxID_COPY, wxTextCtrl::OnUpdateCopy)
119 EVT_UPDATE_UI(wxID_PASTE, wxTextCtrl::OnUpdatePaste)
120 EVT_UPDATE_UI(wxID_UNDO, wxTextCtrl::OnUpdateUndo)
121 EVT_UPDATE_UI(wxID_REDO, wxTextCtrl::OnUpdateRedo)
2bda0e17 122END_EVENT_TABLE()
e702ff0f 123
2bda0e17 124
a1b82138
VZ
125// ============================================================================
126// implementation
127// ============================================================================
128
129// ----------------------------------------------------------------------------
130// creation
131// ----------------------------------------------------------------------------
132
cd471848 133wxTextCtrl::wxTextCtrl()
2bda0e17 134{
cd471848 135#if wxUSE_RICHEDIT
a1b82138 136 m_isRich = FALSE;
cd471848 137#endif
2bda0e17
KB
138}
139
debe6624 140bool wxTextCtrl::Create(wxWindow *parent, wxWindowID id,
c085e333
VZ
141 const wxString& value,
142 const wxPoint& pos,
a1b82138
VZ
143 const wxSize& size,
144 long style,
c085e333
VZ
145 const wxValidator& validator,
146 const wxString& name)
2bda0e17 147{
a1b82138 148 // base initialization
8d99be5f 149 if ( !CreateBase(parent, id, pos, size, style, validator, name) )
a1b82138 150 return FALSE;
2bda0e17 151
a1b82138
VZ
152 if ( parent )
153 parent->AddChild(this);
2bda0e17 154
a1b82138
VZ
155 // set colours
156 SetupColours();
2bda0e17 157
a1b82138
VZ
158 // translate wxWin style flags to MSW ones, checking for consistency while
159 // doing it
160 long msStyle = ES_LEFT | WS_VISIBLE | WS_CHILD | WS_TABSTOP;
161 if ( m_windowStyle & wxTE_MULTILINE )
162 {
163 wxASSERT_MSG( !(m_windowStyle & wxTE_PROCESS_ENTER),
223d09f6 164 wxT("wxTE_PROCESS_ENTER style is ignored for multiline "
0d0512bd 165 "text controls (they always process it)") );
5fb9fcfc 166
b70ababc
JS
167 msStyle |= ES_MULTILINE | ES_WANTRETURN;
168 if ((m_windowStyle & wxTE_NO_VSCROLL) == 0)
169 msStyle |= WS_VSCROLL;
a1b82138
VZ
170 m_windowStyle |= wxTE_PROCESS_ENTER;
171 }
172 else
173 msStyle |= ES_AUTOHSCROLL;
2bda0e17 174
2c738dd8
UM
175 if (m_windowStyle & wxHSCROLL)
176 msStyle |= (WS_HSCROLL | ES_AUTOHSCROLL);
177
a1b82138
VZ
178 if (m_windowStyle & wxTE_READONLY)
179 msStyle |= ES_READONLY;
2bda0e17 180
a1b82138
VZ
181 if (m_windowStyle & wxHSCROLL)
182 msStyle |= (WS_HSCROLL | ES_AUTOHSCROLL);
183 if (m_windowStyle & wxTE_PASSWORD) // hidden input
184 msStyle |= ES_PASSWORD;
2bda0e17 185
101f488c
VZ
186 // we always want the characters and the arrows
187 m_lDlgCode = DLGC_WANTCHARS | DLGC_WANTARROWS;
188
189 // we may have several different cases:
190 // 1. normal case: both TAB and ENTER are used for dialog navigation
191 // 2. ctrl which wants TAB for itself: ENTER is used to pass to the next
192 // control in the dialog
193 // 3. ctrl which wants ENTER for itself: TAB is used for dialog navigation
194 // 4. ctrl which wants both TAB and ENTER: Ctrl-ENTER is used to pass to
195 // the next control
196 if ( m_windowStyle & wxTE_PROCESS_ENTER )
197 m_lDlgCode |= DLGC_WANTMESSAGE;
198 if ( m_windowStyle & wxTE_PROCESS_TAB )
199 m_lDlgCode |= DLGC_WANTTAB;
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
c49245f8 205 if ( m_windowStyle & wxTE_RICH )
a1b82138 206 {
0d0512bd
VZ
207 static bool s_errorGiven = FALSE; // MT-FIXME
208
209 // only give the error msg once if the DLL can't be loaded
210 if ( !s_errorGiven )
211 {
212 // first try to load the RichEdit DLL (will do nothing if already
213 // done)
b12915c1 214 if ( !wxRichEditModule::Load() )
0d0512bd
VZ
215 {
216 wxLogError(_("Impossible to create a rich edit control, "
b12915c1
VZ
217 "using simple text control instead. Please "
218 "reinstall riched32.dll"));
0d0512bd
VZ
219
220 s_errorGiven = TRUE;
221 }
222 }
223
224 if ( s_errorGiven )
225 {
226 m_isRich = FALSE;
227 }
228 else
229 {
230 msStyle |= ES_AUTOVSCROLL;
231 m_isRich = TRUE;
b12915c1
VZ
232
233 int ver = wxRichEditModule::GetLoadedVersion();
234 if ( ver == 1 )
235 {
236 windowClass = wxT("RICHEDIT");
237 }
238 else
239 {
240#ifndef RICHEDIT_CLASS
241 wxString RICHEDIT_CLASS;
242 RICHEDIT_CLASS.Printf(_T("RichEdit%d0"), ver);
243#ifdef wxUSE_UNICODE
244 RICHEDIT_CLASS += _T('W');
245#else // ANSI
246 RICHEDIT_CLASS += _T('A');
247#endif // Unicode/ANSI
248#endif // !RICHEDIT_CLASS
249
250 windowClass = RICHEDIT_CLASS;
251 }
0d0512bd 252 }
a1b82138
VZ
253 }
254 else
255 m_isRich = FALSE;
b12915c1 256#endif // wxUSE_RICHEDIT
2bda0e17 257
a1b82138
VZ
258 bool want3D;
259 WXDWORD exStyle = Determine3DEffects(WS_EX_CLIENTEDGE, &want3D);
2bda0e17 260
a1b82138
VZ
261 // Even with extended styles, need to combine with WS_BORDER for them to
262 // look right.
263 if ( want3D || wxStyleHasBorder(m_windowStyle) )
264 msStyle |= WS_BORDER;
2bda0e17 265
a1b82138
VZ
266 // NB: don't use pos and size as CreateWindowEx arguments because they
267 // might be -1 in which case we should use the default values (and
268 // SetSize called below takes care of it)
269 m_hWnd = (WXHWND)::CreateWindowEx(exStyle,
270 windowClass,
271 NULL,
272 msStyle,
273 0, 0, 0, 0,
274 GetHwndOf(parent),
275 (HMENU)m_windowId,
276 wxGetInstance(),
277 NULL);
2bda0e17 278
223d09f6 279 wxCHECK_MSG( m_hWnd, FALSE, wxT("Failed to create text ctrl") );
c085e333 280
1f112209 281#if wxUSE_CTL3D
a1b82138
VZ
282 if ( want3D )
283 {
284 Ctl3dSubclassCtl(GetHwnd());
285 m_useCtl3D = TRUE;
286 }
2bda0e17
KB
287#endif
288
57c208c5 289#if wxUSE_RICHEDIT
a1b82138
VZ
290 if (m_isRich)
291 {
292 // Have to enable events
293 ::SendMessage(GetHwnd(), EM_SETEVENTMASK, 0,
294 ENM_CHANGE | ENM_DROPFILES | ENM_SELCHANGE | ENM_UPDATE);
295 }
2bda0e17
KB
296#endif
297
a1b82138 298 SubclassWin(GetHWND());
2bda0e17 299
a1b82138
VZ
300 // set font, position, size and initial value
301 wxFont& fontParent = parent->GetFont();
302 if ( fontParent.Ok() )
303 {
304 SetFont(fontParent);
305 }
306 else
307 {
308 SetFont(wxSystemSettings::GetSystemFont(wxSYS_SYSTEM_FONT));
309 }
2bda0e17 310
a1b82138 311 // Causes a crash for Symantec C++ and WIN32 for some reason
2bda0e17 312#if !(defined(__SC__) && defined(__WIN32__))
a1b82138
VZ
313 if ( !value.IsEmpty() )
314 {
315 SetValue(value);
316 }
2bda0e17
KB
317#endif
318
a1b82138
VZ
319 SetSize(pos.x, pos.y, size.x, size.y);
320
321 return TRUE;
2bda0e17
KB
322}
323
324// Make sure the window style (etc.) reflects the HWND style (roughly)
cd471848 325void wxTextCtrl::AdoptAttributesFromHWND()
2bda0e17 326{
c085e333 327 wxWindow::AdoptAttributesFromHWND();
2bda0e17 328
789295bf 329 HWND hWnd = GetHwnd();
a1b82138 330 long style = GetWindowLong(hWnd, GWL_STYLE);
2bda0e17 331
cd471848
VZ
332 // retrieve the style to see whether this is an edit or richedit ctrl
333#if wxUSE_RICHEDIT
837e5743 334 wxChar buf[256];
2bda0e17 335
a1b82138 336 GetClassName(hWnd, buf, WXSIZEOF(buf));
2bda0e17 337
223d09f6 338 if ( wxStricmp(buf, wxT("EDIT")) == 0 )
c085e333
VZ
339 m_isRich = FALSE;
340 else
341 m_isRich = TRUE;
a1b82138 342#endif // wxUSE_RICHEDIT
c085e333
VZ
343
344 if (style & ES_MULTILINE)
345 m_windowStyle |= wxTE_MULTILINE;
346 if (style & ES_PASSWORD)
347 m_windowStyle |= wxTE_PASSWORD;
348 if (style & ES_READONLY)
349 m_windowStyle |= wxTE_READONLY;
350 if (style & ES_WANTRETURN)
351 m_windowStyle |= wxTE_PROCESS_ENTER;
2bda0e17
KB
352}
353
cd471848 354void wxTextCtrl::SetupColours()
2bda0e17 355{
a1b82138
VZ
356 // FIXME why is bg colour not inherited from parent?
357 SetBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_WINDOW));
358 SetForegroundColour(GetParent()->GetForegroundColour());
2bda0e17
KB
359}
360
a1b82138
VZ
361// ----------------------------------------------------------------------------
362// set/get the controls text
363// ----------------------------------------------------------------------------
364
cd471848 365wxString wxTextCtrl::GetValue() const
2bda0e17 366{
b12915c1
VZ
367 // we can't use wxGetWindowText() (i.e. WM_GETTEXT internally) for
368 // retrieving more than 64Kb under Win9x
369#if wxUSE_RICHEDIT
370 if ( m_isRich )
371 {
372 wxString str;
373
374 int len = GetWindowTextLength(GetHwnd()) + 1;
375 wxChar *p = str.GetWriteBuf(len);
376
377 TEXTRANGE textRange;
378 textRange.chrg.cpMin = 0;
379 textRange.chrg.cpMax = -1;
380 textRange.lpstrText = p;
381
382 (void)SendMessage(GetHwnd(), EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
383
384 // believe it or not, but EM_GETTEXTRANGE uses just CR ('\r') for the
385 // newlines which is neither Unix nor Windows style (Win95 with
386 // riched20.dll shows this behaviour) - convert it to something
387 // reasonable
388 for ( ; *p; p++ )
389 {
390 if ( *p == _T('\r') )
391 *p = _T('\n');
392 }
393
394 str.UngetWriteBuf();
395
396 return str;
397 }
398#endif // wxUSE_RICHEDIT
399
400 // WM_GETTEXT uses standard DOS CR+LF (\r\n) convention - convert to the
401 // same one as above for consitency
402 wxString str = wxGetWindowText(GetHWND());
403
404 return wxTextFile::Translate(str, wxTextFileType_Unix);
2bda0e17
KB
405}
406
407void wxTextCtrl::SetValue(const wxString& value)
408{
b12915c1
VZ
409 // if the text is long enough, it's faster to just set it instead of first
410 // comparing it with the old one (chances are that it will be different
411 // anyhow, this comparison is there to avoid flicker for small single-line
412 // edit controls mostly)
413 if ( (value.length() > 0x400) || (value != GetValue()) )
07cf98cb 414 {
b12915c1
VZ
415 wxString valueDos = wxTextFile::Translate(value, wxTextFileType_Dos);
416
07cf98cb 417 SetWindowText(GetHwnd(), valueDos);
a1b82138 418
07cf98cb
VZ
419 AdjustSpaceLimit();
420 }
2bda0e17
KB
421}
422
a1b82138 423void wxTextCtrl::WriteText(const wxString& value)
2bda0e17 424{
a1b82138 425 wxString valueDos = wxTextFile::Translate(value, wxTextFileType_Dos);
2bda0e17 426
a1b82138 427 SendMessage(GetHwnd(), EM_REPLACESEL, 0, (LPARAM)valueDos.c_str());
2bda0e17 428
a1b82138 429 AdjustSpaceLimit();
2bda0e17
KB
430}
431
a1b82138
VZ
432void wxTextCtrl::AppendText(const wxString& text)
433{
434 SetInsertionPointEnd();
435 WriteText(text);
436}
437
438void wxTextCtrl::Clear()
439{
223d09f6 440 SetWindowText(GetHwnd(), wxT(""));
a1b82138
VZ
441}
442
443// ----------------------------------------------------------------------------
2bda0e17 444// Clipboard operations
a1b82138
VZ
445// ----------------------------------------------------------------------------
446
cd471848 447void wxTextCtrl::Copy()
2bda0e17 448{
e702ff0f
JS
449 if (CanCopy())
450 {
789295bf 451 HWND hWnd = GetHwnd();
e702ff0f
JS
452 SendMessage(hWnd, WM_COPY, 0, 0L);
453 }
2bda0e17
KB
454}
455
cd471848 456void wxTextCtrl::Cut()
2bda0e17 457{
e702ff0f
JS
458 if (CanCut())
459 {
789295bf 460 HWND hWnd = GetHwnd();
e702ff0f
JS
461 SendMessage(hWnd, WM_CUT, 0, 0L);
462 }
2bda0e17
KB
463}
464
cd471848 465void wxTextCtrl::Paste()
2bda0e17 466{
e702ff0f
JS
467 if (CanPaste())
468 {
789295bf 469 HWND hWnd = GetHwnd();
e702ff0f
JS
470 SendMessage(hWnd, WM_PASTE, 0, 0L);
471 }
2bda0e17
KB
472}
473
a1b82138
VZ
474bool wxTextCtrl::CanCopy() const
475{
476 // Can copy if there's a selection
477 long from, to;
478 GetSelection(& from, & to);
479 return (from != to);
480}
481
482bool wxTextCtrl::CanCut() const
483{
484 // Can cut if there's a selection
485 long from, to;
486 GetSelection(& from, & to);
487 return (from != to);
488}
489
490bool wxTextCtrl::CanPaste() const
491{
492#if wxUSE_RICHEDIT
493 if (m_isRich)
494 {
495 int dataFormat = 0; // 0 == any format
496 return (::SendMessage( GetHwnd(), EM_CANPASTE, (WPARAM) (UINT) dataFormat, 0) != 0);
497 }
498#endif
499 if (!IsEditable())
500 return FALSE;
501
502 // Standard edit control: check for straight text on clipboard
503 bool isTextAvailable = FALSE;
504 if ( ::OpenClipboard(GetHwndOf(wxTheApp->GetTopWindow())) )
505 {
506 isTextAvailable = (::IsClipboardFormatAvailable(CF_TEXT) != 0);
507 ::CloseClipboard();
508 }
509
510 return isTextAvailable;
511}
512
513// ----------------------------------------------------------------------------
514// Accessors
515// ----------------------------------------------------------------------------
516
debe6624 517void wxTextCtrl::SetEditable(bool editable)
2bda0e17 518{
a1b82138
VZ
519 HWND hWnd = GetHwnd();
520 SendMessage(hWnd, EM_SETREADONLY, (WPARAM)!editable, (LPARAM)0L);
2bda0e17
KB
521}
522
debe6624 523void wxTextCtrl::SetInsertionPoint(long pos)
2bda0e17 524{
a1b82138 525 HWND hWnd = GetHwnd();
2bda0e17 526#ifdef __WIN32__
57c208c5 527#if wxUSE_RICHEDIT
a1b82138
VZ
528 if ( m_isRich)
529 {
530 CHARRANGE range;
531 range.cpMin = pos;
532 range.cpMax = pos;
533 SendMessage(hWnd, EM_EXSETSEL, 0, (LPARAM) &range);
534 SendMessage(hWnd, EM_SCROLLCARET, (WPARAM)0, (LPARAM)0);
535 }
536 else
537#endif // wxUSE_RICHEDIT
538 {
539 SendMessage(hWnd, EM_SETSEL, pos, pos);
540 SendMessage(hWnd, EM_SCROLLCARET, (WPARAM)0, (LPARAM)0);
541 }
542#else // Win16
543 SendMessage(hWnd, EM_SETSEL, 0, MAKELPARAM(pos, pos));
544#endif // Win32/16
545
546 static const char *nothing = "";
547 SendMessage(hWnd, EM_REPLACESEL, 0, (LPARAM)nothing);
2bda0e17
KB
548}
549
cd471848 550void wxTextCtrl::SetInsertionPointEnd()
2bda0e17 551{
a1b82138
VZ
552 long pos = GetLastPosition();
553 SetInsertionPoint(pos);
2bda0e17
KB
554}
555
cd471848 556long wxTextCtrl::GetInsertionPoint() const
2bda0e17 557{
57c208c5 558#if wxUSE_RICHEDIT
a1b82138
VZ
559 if (m_isRich)
560 {
561 CHARRANGE range;
562 range.cpMin = 0;
563 range.cpMax = 0;
564 SendMessage(GetHwnd(), EM_EXGETSEL, 0, (LPARAM) &range);
565 return range.cpMin;
566 }
2bda0e17
KB
567#endif
568
a1b82138
VZ
569 DWORD Pos = (DWORD)SendMessage(GetHwnd(), EM_GETSEL, 0, 0L);
570 return Pos & 0xFFFF;
2bda0e17
KB
571}
572
cd471848 573long wxTextCtrl::GetLastPosition() const
2bda0e17 574{
789295bf 575 HWND hWnd = GetHwnd();
2bda0e17
KB
576
577 // Will always return a number > 0 (according to docs)
578 int noLines = (int)SendMessage(hWnd, EM_GETLINECOUNT, (WPARAM)0, (LPARAM)0L);
579
580 // This gets the char index for the _beginning_ of the last line
581 int charIndex = (int)SendMessage(hWnd, EM_LINEINDEX, (WPARAM)(noLines-1), (LPARAM)0L);
39136494 582
2bda0e17
KB
583 // Get number of characters in the last line. We'll add this to the character
584 // index for the last line, 1st position.
585 int lineLength = (int)SendMessage(hWnd, EM_LINELENGTH, (WPARAM)charIndex, (LPARAM)0L);
586
587 return (long)(charIndex + lineLength);
588}
589
a1b82138
VZ
590// If the return values from and to are the same, there is no
591// selection.
592void wxTextCtrl::GetSelection(long* from, long* to) const
593{
594#if wxUSE_RICHEDIT
595 if (m_isRich)
596 {
597 CHARRANGE charRange;
598 ::SendMessage(GetHwnd(), EM_EXGETSEL, 0, (LPARAM) (CHARRANGE*) & charRange);
599
600 *from = charRange.cpMin;
601 *to = charRange.cpMax;
602
603 return;
604 }
605#endif
606 DWORD dwStart, dwEnd;
607 WPARAM wParam = (WPARAM) (DWORD*) & dwStart; // receives starting position
608 LPARAM lParam = (LPARAM) (DWORD*) & dwEnd; // receives ending position
609
610 ::SendMessage(GetHwnd(), EM_GETSEL, wParam, lParam);
611
612 *from = dwStart;
613 *to = dwEnd;
614}
615
616bool wxTextCtrl::IsEditable() const
617{
618 long style = ::GetWindowLong(GetHwnd(), GWL_STYLE);
619
620 return ((style & ES_READONLY) == 0);
621}
622
623// ----------------------------------------------------------------------------
624// Editing
625// ----------------------------------------------------------------------------
626
debe6624 627void wxTextCtrl::Replace(long from, long to, const wxString& value)
2bda0e17 628{
acbd13a3 629#if wxUSE_CLIPBOARD
789295bf 630 HWND hWnd = GetHwnd();
2bda0e17
KB
631 long fromChar = from;
632 long toChar = to;
39136494 633
2bda0e17
KB
634 // Set selection and remove it
635#ifdef __WIN32__
636 SendMessage(hWnd, EM_SETSEL, fromChar, toChar);
637#else
638 SendMessage(hWnd, EM_SETSEL, (WPARAM)0, (LPARAM)MAKELONG(fromChar, toChar));
639#endif
640 SendMessage(hWnd, WM_CUT, (WPARAM)0, (LPARAM)0);
641
642 // Now replace with 'value', by pasting.
837e5743 643 wxSetClipboardData(wxDF_TEXT, (wxObject *) (const wxChar *)value, 0, 0);
2bda0e17
KB
644
645 // Paste into edit control
646 SendMessage(hWnd, WM_PASTE, (WPARAM)0, (LPARAM)0L);
acbd13a3
JS
647#else
648 wxFAIL_MSG("wxTextCtrl::Replace not implemented if wxUSE_CLIPBOARD is 0.");
649#endif
2bda0e17
KB
650}
651
debe6624 652void wxTextCtrl::Remove(long from, long to)
2bda0e17 653{
789295bf 654 HWND hWnd = GetHwnd();
2bda0e17
KB
655 long fromChar = from;
656 long toChar = to;
39136494 657
2bda0e17
KB
658 // Cut all selected text
659#ifdef __WIN32__
660 SendMessage(hWnd, EM_SETSEL, fromChar, toChar);
661#else
662 SendMessage(hWnd, EM_SETSEL, (WPARAM)0, (LPARAM)MAKELONG(fromChar, toChar));
663#endif
664 SendMessage(hWnd, WM_CUT, (WPARAM)0, (LPARAM)0);
665}
666
debe6624 667void wxTextCtrl::SetSelection(long from, long to)
2bda0e17 668{
789295bf 669 HWND hWnd = GetHwnd();
2bda0e17
KB
670 long fromChar = from;
671 long toChar = to;
a1b82138
VZ
672
673 // if from and to are both -1, it means (in wxWindows) that all text should
674 // be selected. Translate into Windows convention
2bda0e17
KB
675 if ((from == -1) && (to == -1))
676 {
677 fromChar = 0;
678 toChar = -1;
679 }
39136494 680
2bda0e17
KB
681#ifdef __WIN32__
682 SendMessage(hWnd, EM_SETSEL, (WPARAM)fromChar, (LPARAM)toChar);
683 SendMessage(hWnd, EM_SCROLLCARET, (WPARAM)0, (LPARAM)0);
684#else
685 // WPARAM is 0: selection is scrolled into view
686 SendMessage(hWnd, EM_SETSEL, (WPARAM)0, (LPARAM)MAKELONG(fromChar, toChar));
687#endif
688}
689
690bool wxTextCtrl::LoadFile(const wxString& file)
691{
a1b82138 692 if ( wxTextCtrlBase::LoadFile(file) )
cd471848 693 {
a1b82138
VZ
694 // update the size limit if needed
695 AdjustSpaceLimit();
2bda0e17 696
a1b82138 697 return TRUE;
2bda0e17 698 }
2bda0e17 699
a1b82138 700 return FALSE;
2bda0e17
KB
701}
702
cd471848 703bool wxTextCtrl::IsModified() const
2bda0e17 704{
789295bf 705 return (SendMessage(GetHwnd(), EM_GETMODIFY, 0, 0) != 0);
2bda0e17
KB
706}
707
708// Makes 'unmodified'
cd471848 709void wxTextCtrl::DiscardEdits()
2bda0e17 710{
a1b82138 711 SendMessage(GetHwnd(), EM_SETMODIFY, FALSE, 0L);
2bda0e17
KB
712}
713
cd471848 714int wxTextCtrl::GetNumberOfLines() const
2bda0e17 715{
789295bf 716 return (int)SendMessage(GetHwnd(), EM_GETLINECOUNT, (WPARAM)0, (LPARAM)0);
2bda0e17
KB
717}
718
debe6624 719long wxTextCtrl::XYToPosition(long x, long y) const
2bda0e17 720{
789295bf 721 HWND hWnd = GetHwnd();
2bda0e17
KB
722
723 // This gets the char index for the _beginning_ of this line
724 int charIndex = (int)SendMessage(hWnd, EM_LINEINDEX, (WPARAM)y, (LPARAM)0);
725 return (long)(x + charIndex);
726}
727
0efe5ba7 728bool wxTextCtrl::PositionToXY(long pos, long *x, long *y) const
2bda0e17 729{
789295bf 730 HWND hWnd = GetHwnd();
2bda0e17
KB
731
732 // This gets the line number containing the character
0efe5ba7
VZ
733 int lineNo;
734#if wxUSE_RICHEDIT
735 if ( m_isRich )
736 {
737 lineNo = (int)SendMessage(hWnd, EM_EXLINEFROMCHAR, 0, (LPARAM)pos);
738 }
739 else
740#endif // wxUSE_RICHEDIT
741 lineNo = (int)SendMessage(hWnd, EM_LINEFROMCHAR, (WPARAM)pos, 0);
742
743 if ( lineNo == -1 )
744 {
745 // no such line
746 return FALSE;
747 }
748
2bda0e17
KB
749 // This gets the char index for the _beginning_ of this line
750 int charIndex = (int)SendMessage(hWnd, EM_LINEINDEX, (WPARAM)lineNo, (LPARAM)0);
0efe5ba7
VZ
751 if ( charIndex == -1 )
752 {
753 return FALSE;
754 }
755
2bda0e17 756 // The X position must therefore be the different between pos and charIndex
0efe5ba7
VZ
757 if ( x )
758 *x = (long)(pos - charIndex);
759 if ( y )
760 *y = (long)lineNo;
761
762 return TRUE;
2bda0e17
KB
763}
764
debe6624 765void wxTextCtrl::ShowPosition(long pos)
2bda0e17 766{
789295bf 767 HWND hWnd = GetHwnd();
2bda0e17
KB
768
769 // To scroll to a position, we pass the number of lines and characters
770 // to scroll *by*. This means that we need to:
771 // (1) Find the line position of the current line.
772 // (2) Find the line position of pos.
773 // (3) Scroll by (pos - current).
774 // For now, ignore the horizontal scrolling.
775
776 // Is this where scrolling is relative to - the line containing the caret?
777 // Or is the first visible line??? Try first visible line.
778// int currentLineLineNo1 = (int)SendMessage(hWnd, EM_LINEFROMCHAR, (WPARAM)-1, (LPARAM)0L);
779
780 int currentLineLineNo = (int)SendMessage(hWnd, EM_GETFIRSTVISIBLELINE, (WPARAM)0, (LPARAM)0L);
781
782 int specifiedLineLineNo = (int)SendMessage(hWnd, EM_LINEFROMCHAR, (WPARAM)pos, (LPARAM)0L);
39136494 783
2bda0e17
KB
784 int linesToScroll = specifiedLineLineNo - currentLineLineNo;
785
2bda0e17 786 if (linesToScroll != 0)
de4d7713 787 (void)SendMessage(hWnd, EM_LINESCROLL, (WPARAM)0, (LPARAM)linesToScroll);
2bda0e17
KB
788}
789
debe6624 790int wxTextCtrl::GetLineLength(long lineNo) const
2bda0e17
KB
791{
792 long charIndex = XYToPosition(0, lineNo);
4438caf4 793 int len = (int)SendMessage(GetHwnd(), EM_LINELENGTH, charIndex, 0);
2bda0e17
KB
794 return len;
795}
796
debe6624 797wxString wxTextCtrl::GetLineText(long lineNo) const
2bda0e17 798{
a1b82138 799 size_t len = (size_t)GetLineLength(lineNo) + 1;
4438caf4
VZ
800 char *buf = (char *)malloc(len);
801 *(WORD *)buf = len;
802 int noChars = (int)SendMessage(GetHwnd(), EM_GETLINE, lineNo, (LPARAM)buf);
803 buf[noChars] = 0;
804
805 wxString str(buf);
806
807 free(buf);
808
809 return str;
2bda0e17
KB
810}
811
a1b82138 812// ----------------------------------------------------------------------------
ca8b28f2 813// Undo/redo
a1b82138
VZ
814// ----------------------------------------------------------------------------
815
ca8b28f2
JS
816void wxTextCtrl::Undo()
817{
818 if (CanUndo())
819 {
789295bf 820 ::SendMessage(GetHwnd(), EM_UNDO, 0, 0);
ca8b28f2
JS
821 }
822}
823
824void wxTextCtrl::Redo()
825{
826 if (CanRedo())
827 {
828 // Same as Undo, since Undo undoes the undo, i.e. a redo.
789295bf 829 ::SendMessage(GetHwnd(), EM_UNDO, 0, 0);
ca8b28f2
JS
830 }
831}
832
833bool wxTextCtrl::CanUndo() const
834{
789295bf 835 return (::SendMessage(GetHwnd(), EM_CANUNDO, 0, 0) != 0);
ca8b28f2
JS
836}
837
838bool wxTextCtrl::CanRedo() const
839{
789295bf 840 return (::SendMessage(GetHwnd(), EM_CANUNDO, 0, 0) != 0);
ca8b28f2
JS
841}
842
a1b82138
VZ
843// ----------------------------------------------------------------------------
844// implemenation details
845// ----------------------------------------------------------------------------
39136494 846
2bda0e17
KB
847void wxTextCtrl::Command(wxCommandEvent & event)
848{
a1b82138
VZ
849 SetValue(event.GetString());
850 ProcessCommand (event);
2bda0e17
KB
851}
852
853void wxTextCtrl::OnDropFiles(wxDropFilesEvent& event)
854{
a1b82138
VZ
855 // By default, load the first file into the text window.
856 if (event.GetNumberOfFiles() > 0)
857 {
858 LoadFile(event.GetFiles()[0]);
859 }
2bda0e17
KB
860}
861
2bda0e17
KB
862void wxTextCtrl::OnChar(wxKeyEvent& event)
863{
42e69d6b 864 switch ( event.KeyCode() )
cd471848 865 {
cd471848 866 case WXK_RETURN:
39136494 867 if ( !(m_windowStyle & wxTE_MULTILINE) )
cd471848
VZ
868 {
869 wxCommandEvent event(wxEVT_COMMAND_TEXT_ENTER, m_windowId);
870 event.SetEventObject( this );
871 if ( GetEventHandler()->ProcessEvent(event) )
872 return;
873 }
5fb9fcfc
VZ
874 //else: multiline controls need Enter for themselves
875
876 break;
4d91c1d1 877
cd471848 878 case WXK_TAB:
319fefa9
VZ
879 // always produce navigation event - even if we process TAB
880 // ourselves the fact that we got here means that the user code
881 // decided to skip processing of this TAB - probably to let it
882 // do its default job.
5fb9fcfc
VZ
883 //
884 // NB: Notice that Ctrl-Tab is handled elsewhere and Alt-Tab is
885 // handled by Windows
cd471848 886 {
5fb9fcfc
VZ
887 wxNavigationKeyEvent eventNav;
888 eventNav.SetDirection(!event.ShiftDown());
889 eventNav.SetWindowChange(FALSE);
890 eventNav.SetEventObject(this);
39136494 891
5fb9fcfc 892 if ( GetEventHandler()->ProcessEvent(eventNav) )
cd471848
VZ
893 return;
894 }
341c92a8 895 break;
4d91c1d1
VZ
896
897 default:
898 event.Skip();
39136494 899 return;
cd471848 900 }
39136494 901
cd471848
VZ
902 // don't just call event.Skip() because this will cause TABs and ENTERs
903 // be passed upwards and we don't always want this - instead process it
904 // right here
42e69d6b
VZ
905
906 // FIXME
907 event.Skip();
2bda0e17
KB
908}
909
debe6624 910bool wxTextCtrl::MSWCommand(WXUINT param, WXWORD WXUNUSED(id))
2bda0e17 911{
789295bf
VZ
912 switch (param)
913 {
914 case EN_SETFOCUS:
915 case EN_KILLFOCUS:
916 {
917 wxFocusEvent event(param == EN_KILLFOCUS ? wxEVT_KILL_FOCUS
918 : wxEVT_SET_FOCUS,
919 m_windowId);
920 event.SetEventObject( this );
921 GetEventHandler()->ProcessEvent(event);
922 }
923 break;
ae29de83 924
789295bf
VZ
925 case EN_CHANGE:
926 {
927 wxCommandEvent event(wxEVT_COMMAND_TEXT_UPDATED, m_windowId);
928 wxString val(GetValue());
929 if ( !val.IsNull() )
930 event.m_commandString = WXSTRINGCAST val;
931 event.SetEventObject( this );
932 ProcessCommand(event);
933 }
934 break;
2bda0e17 935
b12915c1 936 case EN_MAXTEXT:
789295bf
VZ
937 // the text size limit has been hit - increase it
938 AdjustSpaceLimit();
939 break;
940
941 // the other notification messages are not processed
942 case EN_UPDATE:
b12915c1 943 case EN_ERRSPACE:
789295bf
VZ
944 case EN_HSCROLL:
945 case EN_VSCROLL:
946 default:
947 return FALSE;
948 }
949
950 // processed
951 return TRUE;
2bda0e17
KB
952}
953
789295bf
VZ
954void wxTextCtrl::AdjustSpaceLimit()
955{
25889d3c 956#ifndef __WIN16__
789295bf
VZ
957 unsigned int len = ::GetWindowTextLength(GetHwnd()),
958 limit = ::SendMessage(GetHwnd(), EM_GETLIMITTEXT, 0, 0);
17d8ee1c 959 if ( len >= limit )
789295bf
VZ
960 {
961 limit = len + 0x8000; // 32Kb
962
5ea105e0 963#if wxUSE_RICHEDIT
b12915c1
VZ
964 if ( m_isRich )
965 {
966 // as a nice side effect, this also allows passing limit > 64Kb
967 ::SendMessage(GetHwnd(), EM_EXLIMITTEXT, 0, limit);
968 }
789295bf 969 else
b12915c1
VZ
970#endif // wxUSE_RICHEDIT
971 {
972 if ( limit > 0xffff )
973 {
974 // this will set it to a platform-dependent maximum (much more
975 // than 64Kb under NT)
976 limit = 0;
977 }
978
789295bf 979 ::SendMessage(GetHwnd(), EM_LIMITTEXT, limit, 0);
b12915c1 980 }
789295bf 981 }
b12915c1 982#endif // !Win16
789295bf 983}
2bda0e17 984
a1b82138 985bool wxTextCtrl::AcceptsFocus() const
2bda0e17 986{
a1b82138
VZ
987 // we don't want focus if we can't be edited
988 return IsEditable() && wxControl::AcceptsFocus();
989}
c085e333 990
f68586e5 991wxSize wxTextCtrl::DoGetBestSize() const
a1b82138
VZ
992{
993 int cx, cy;
994 wxGetCharSize(GetHWND(), &cx, &cy, &GetFont());
995
996 int wText = DEFAULT_ITEM_WIDTH;
997
998 int hText = EDIT_HEIGHT_FROM_CHAR_HEIGHT(cy);
999 if ( m_windowStyle & wxTE_MULTILINE )
1000 {
1001 hText *= wxMin(GetNumberOfLines(), 5);
1002 }
1003 //else: for single line control everything is ok
1004
1005 return wxSize(wText, hText);
2bda0e17 1006}
a1b82138
VZ
1007
1008// ----------------------------------------------------------------------------
1009// standard handlers for standard edit menu events
1010// ----------------------------------------------------------------------------
2bda0e17 1011
e702ff0f
JS
1012void wxTextCtrl::OnCut(wxCommandEvent& event)
1013{
1014 Cut();
1015}
1016
1017void wxTextCtrl::OnCopy(wxCommandEvent& event)
1018{
1019 Copy();
1020}
1021
1022void wxTextCtrl::OnPaste(wxCommandEvent& event)
1023{
1024 Paste();
1025}
1026
1027void wxTextCtrl::OnUndo(wxCommandEvent& event)
1028{
1029 Undo();
1030}
1031
1032void wxTextCtrl::OnRedo(wxCommandEvent& event)
1033{
1034 Redo();
1035}
1036
1037void wxTextCtrl::OnUpdateCut(wxUpdateUIEvent& event)
1038{
1039 event.Enable( CanCut() );
1040}
1041
1042void wxTextCtrl::OnUpdateCopy(wxUpdateUIEvent& event)
1043{
1044 event.Enable( CanCopy() );
1045}
1046
1047void wxTextCtrl::OnUpdatePaste(wxUpdateUIEvent& event)
1048{
1049 event.Enable( CanPaste() );
1050}
1051
1052void wxTextCtrl::OnUpdateUndo(wxUpdateUIEvent& event)
1053{
1054 event.Enable( CanUndo() );
1055}
1056
1057void wxTextCtrl::OnUpdateRedo(wxUpdateUIEvent& event)
1058{
1059 event.Enable( CanRedo() );
1060}
1061
b12915c1
VZ
1062// ----------------------------------------------------------------------------
1063// wxRichEditModule
1064// ----------------------------------------------------------------------------
1065
1066#if wxUSE_RICHEDIT
1067
1068bool wxRichEditModule::OnInit()
1069{
1070 // don't do anything - we will load it when needed
1071 return TRUE;
1072}
1073
1074void wxRichEditModule::OnExit()
1075{
1076 if ( ms_hRichEdit )
1077 {
1078 FreeLibrary(ms_hRichEdit);
1079 }
1080}
1081
1082/* static */
1083bool wxRichEditModule::Load(int version)
1084{
1085 wxCHECK_MSG( version >= 1 && version <= 3, FALSE,
1086 _T("incorrect richedit control version requested") );
1087
1088 if ( version <= ms_verRichEdit )
1089 {
1090 // we've already got this or better
1091 return TRUE;
1092 }
1093
1094 if ( ms_hRichEdit )
1095 {
1096 ::FreeLibrary(ms_hRichEdit);
1097 }
1098
1099 // always try load riched20.dll first - like this we won't have to reload
1100 // it later if we're first asked for RE 1 and then for RE 2 or 3
1101 wxString dllname = _T("riched20.dll");
1102 ms_hRichEdit = ::LoadLibrary(dllname);
1103 ms_verRichEdit = 2; // no way to tell if it's 2 or 3, assume 2
1104
1105 if ( !ms_hRichEdit && (version == 1) )
1106 {
1107 // fall back to RE 1
1108 dllname = _T("riched32.dll");
1109 ms_hRichEdit = ::LoadLibrary(dllname);
1110 ms_verRichEdit = 1;
1111 }
1112
1113 if ( !ms_hRichEdit )
1114 {
1115 wxLogSysError(_("Could not load Rich Edit DLL '%s'"), dllname.c_str());
1116
1117 ms_verRichEdit = -1;
1118
1119 return FALSE;
1120 }
1121
1122 return TRUE;
1123}
1124
1125#endif // wxUSE_RICHEDIT
1126