]> git.saurik.com Git - wxWidgets.git/blob - src/msw/textctrl.cpp
added test mode to wxLongLongWx which allowed to find the bug in operator*=()
[wxWidgets.git] / src / msw / textctrl.cpp
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
9 // Licence: wxWindows license
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 #ifdef __GNUG__
17 #pragma implementation "textctrl.h"
18 #endif
19
20 // ----------------------------------------------------------------------------
21 // headers
22 // ----------------------------------------------------------------------------
23
24 // For compilers that support precompilation, includes "wx.h".
25 #include "wx/wxprec.h"
26
27 #ifdef __BORLANDC__
28 #pragma hdrstop
29 #endif
30
31 #ifndef WX_PRECOMP
32 #include "wx/textctrl.h"
33 #include "wx/settings.h"
34 #include "wx/brush.h"
35 #include "wx/utils.h"
36 #include "wx/intl.h"
37 #include "wx/log.h"
38 #include "wx/app.h"
39 #endif
40
41 #if wxUSE_CLIPBOARD
42 #include "wx/clipbrd.h"
43 #endif
44
45 #include "wx/textfile.h"
46
47 #include <windowsx.h>
48
49 #include "wx/msw/private.h"
50
51 #include <string.h>
52 #include <stdlib.h>
53 #include <sys/types.h>
54
55 #if wxUSE_IOSTREAMH
56 # include <fstream.h>
57 #else
58 # include <fstream>
59 #endif
60
61 #if wxUSE_RICHEDIT && (!defined(__GNUWIN32__) || defined(wxUSE_NORLANDER_HEADERS))
62 #include <richedit.h>
63 #endif
64
65 // ----------------------------------------------------------------------------
66 // private classes
67 // ----------------------------------------------------------------------------
68
69 #if wxUSE_RICHEDIT
70
71 // this module initializes RichEdit DLL if needed
72 class wxRichEditModule : public wxModule
73 {
74 public:
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
84 private:
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
94 HINSTANCE wxRichEditModule::ms_hRichEdit = (HINSTANCE)NULL;
95 int wxRichEditModule::ms_verRichEdit = -1;
96
97 IMPLEMENT_DYNAMIC_CLASS(wxRichEditModule, wxModule)
98
99 #endif // wxUSE_RICHEDIT
100
101 // ----------------------------------------------------------------------------
102 // event tables and other macros
103 // ----------------------------------------------------------------------------
104
105 IMPLEMENT_DYNAMIC_CLASS(wxTextCtrl, wxControl)
106
107 BEGIN_EVENT_TABLE(wxTextCtrl, wxControl)
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)
122 END_EVENT_TABLE()
123
124
125 // ============================================================================
126 // implementation
127 // ============================================================================
128
129 // ----------------------------------------------------------------------------
130 // creation
131 // ----------------------------------------------------------------------------
132
133 wxTextCtrl::wxTextCtrl()
134 {
135 #if wxUSE_RICHEDIT
136 m_isRich = FALSE;
137 #endif
138 }
139
140 bool wxTextCtrl::Create(wxWindow *parent, wxWindowID id,
141 const wxString& value,
142 const wxPoint& pos,
143 const wxSize& size,
144 long style,
145 const wxValidator& validator,
146 const wxString& name)
147 {
148 // base initialization
149 if ( !CreateBase(parent, id, pos, size, style, validator, name) )
150 return FALSE;
151
152 if ( parent )
153 parent->AddChild(this);
154
155 // set colours
156 SetupColours();
157
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),
164 wxT("wxTE_PROCESS_ENTER style is ignored for multiline "
165 "text controls (they always process it)") );
166
167 msStyle |= ES_MULTILINE | ES_WANTRETURN;
168 if ((m_windowStyle & wxTE_NO_VSCROLL) == 0)
169 msStyle |= WS_VSCROLL;
170 m_windowStyle |= wxTE_PROCESS_ENTER;
171 }
172 else
173 msStyle |= ES_AUTOHSCROLL;
174
175 if (m_windowStyle & wxHSCROLL)
176 msStyle |= (WS_HSCROLL | ES_AUTOHSCROLL);
177
178 if (m_windowStyle & wxTE_READONLY)
179 msStyle |= ES_READONLY;
180
181 if (m_windowStyle & wxHSCROLL)
182 msStyle |= (WS_HSCROLL | ES_AUTOHSCROLL);
183 if (m_windowStyle & wxTE_PASSWORD) // hidden input
184 msStyle |= ES_PASSWORD;
185
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
201 // do create the control - either an EDIT or RICHEDIT
202 wxString windowClass = wxT("EDIT");
203
204 #if wxUSE_RICHEDIT
205 if ( m_windowStyle & wxTE_RICH )
206 {
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)
214 if ( !wxRichEditModule::Load() )
215 {
216 wxLogError(_("Impossible to create a rich edit control, "
217 "using simple text control instead. Please "
218 "reinstall riched32.dll"));
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;
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 }
252 }
253 }
254 else
255 m_isRich = FALSE;
256 #endif // wxUSE_RICHEDIT
257
258 bool want3D;
259 WXDWORD exStyle = Determine3DEffects(WS_EX_CLIENTEDGE, &want3D);
260
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;
265
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);
278
279 wxCHECK_MSG( m_hWnd, FALSE, wxT("Failed to create text ctrl") );
280
281 #if wxUSE_CTL3D
282 if ( want3D )
283 {
284 Ctl3dSubclassCtl(GetHwnd());
285 m_useCtl3D = TRUE;
286 }
287 #endif
288
289 #if wxUSE_RICHEDIT
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 }
296 #endif
297
298 SubclassWin(GetHWND());
299
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 }
310
311 // Causes a crash for Symantec C++ and WIN32 for some reason
312 #if !(defined(__SC__) && defined(__WIN32__))
313 if ( !value.IsEmpty() )
314 {
315 SetValue(value);
316 }
317 #endif
318
319 SetSize(pos.x, pos.y, size.x, size.y);
320
321 return TRUE;
322 }
323
324 // Make sure the window style (etc.) reflects the HWND style (roughly)
325 void wxTextCtrl::AdoptAttributesFromHWND()
326 {
327 wxWindow::AdoptAttributesFromHWND();
328
329 HWND hWnd = GetHwnd();
330 long style = GetWindowLong(hWnd, GWL_STYLE);
331
332 // retrieve the style to see whether this is an edit or richedit ctrl
333 #if wxUSE_RICHEDIT
334 wxChar buf[256];
335
336 GetClassName(hWnd, buf, WXSIZEOF(buf));
337
338 if ( wxStricmp(buf, wxT("EDIT")) == 0 )
339 m_isRich = FALSE;
340 else
341 m_isRich = TRUE;
342 #endif // wxUSE_RICHEDIT
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;
352 }
353
354 void wxTextCtrl::SetupColours()
355 {
356 // FIXME why is bg colour not inherited from parent?
357 SetBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_WINDOW));
358 SetForegroundColour(GetParent()->GetForegroundColour());
359 }
360
361 // ----------------------------------------------------------------------------
362 // set/get the controls text
363 // ----------------------------------------------------------------------------
364
365 wxString wxTextCtrl::GetValue() const
366 {
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);
405 }
406
407 void wxTextCtrl::SetValue(const wxString& value)
408 {
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()) )
414 {
415 wxString valueDos = wxTextFile::Translate(value, wxTextFileType_Dos);
416
417 SetWindowText(GetHwnd(), valueDos);
418
419 AdjustSpaceLimit();
420 }
421 }
422
423 void wxTextCtrl::WriteText(const wxString& value)
424 {
425 wxString valueDos = wxTextFile::Translate(value, wxTextFileType_Dos);
426
427 SendMessage(GetHwnd(), EM_REPLACESEL, 0, (LPARAM)valueDos.c_str());
428
429 AdjustSpaceLimit();
430 }
431
432 void wxTextCtrl::AppendText(const wxString& text)
433 {
434 SetInsertionPointEnd();
435 WriteText(text);
436 }
437
438 void wxTextCtrl::Clear()
439 {
440 SetWindowText(GetHwnd(), wxT(""));
441 }
442
443 // ----------------------------------------------------------------------------
444 // Clipboard operations
445 // ----------------------------------------------------------------------------
446
447 void wxTextCtrl::Copy()
448 {
449 if (CanCopy())
450 {
451 HWND hWnd = GetHwnd();
452 SendMessage(hWnd, WM_COPY, 0, 0L);
453 }
454 }
455
456 void wxTextCtrl::Cut()
457 {
458 if (CanCut())
459 {
460 HWND hWnd = GetHwnd();
461 SendMessage(hWnd, WM_CUT, 0, 0L);
462 }
463 }
464
465 void wxTextCtrl::Paste()
466 {
467 if (CanPaste())
468 {
469 HWND hWnd = GetHwnd();
470 SendMessage(hWnd, WM_PASTE, 0, 0L);
471 }
472 }
473
474 bool 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
482 bool 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
490 bool 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
517 void wxTextCtrl::SetEditable(bool editable)
518 {
519 HWND hWnd = GetHwnd();
520 SendMessage(hWnd, EM_SETREADONLY, (WPARAM)!editable, (LPARAM)0L);
521 }
522
523 void wxTextCtrl::SetInsertionPoint(long pos)
524 {
525 HWND hWnd = GetHwnd();
526 #ifdef __WIN32__
527 #if wxUSE_RICHEDIT
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);
548 }
549
550 void wxTextCtrl::SetInsertionPointEnd()
551 {
552 long pos = GetLastPosition();
553 SetInsertionPoint(pos);
554 }
555
556 long wxTextCtrl::GetInsertionPoint() const
557 {
558 #if wxUSE_RICHEDIT
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 }
567 #endif
568
569 DWORD Pos = (DWORD)SendMessage(GetHwnd(), EM_GETSEL, 0, 0L);
570 return Pos & 0xFFFF;
571 }
572
573 long wxTextCtrl::GetLastPosition() const
574 {
575 HWND hWnd = GetHwnd();
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);
582
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
590 // If the return values from and to are the same, there is no
591 // selection.
592 void 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
616 bool 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
627 void wxTextCtrl::Replace(long from, long to, const wxString& value)
628 {
629 #if wxUSE_CLIPBOARD
630 HWND hWnd = GetHwnd();
631 long fromChar = from;
632 long toChar = to;
633
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.
643 wxSetClipboardData(wxDF_TEXT, (wxObject *) (const wxChar *)value, 0, 0);
644
645 // Paste into edit control
646 SendMessage(hWnd, WM_PASTE, (WPARAM)0, (LPARAM)0L);
647 #else
648 wxFAIL_MSG("wxTextCtrl::Replace not implemented if wxUSE_CLIPBOARD is 0.");
649 #endif
650 }
651
652 void wxTextCtrl::Remove(long from, long to)
653 {
654 HWND hWnd = GetHwnd();
655 long fromChar = from;
656 long toChar = to;
657
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
667 void wxTextCtrl::SetSelection(long from, long to)
668 {
669 HWND hWnd = GetHwnd();
670 long fromChar = from;
671 long toChar = to;
672
673 // if from and to are both -1, it means (in wxWindows) that all text should
674 // be selected. Translate into Windows convention
675 if ((from == -1) && (to == -1))
676 {
677 fromChar = 0;
678 toChar = -1;
679 }
680
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
690 bool wxTextCtrl::LoadFile(const wxString& file)
691 {
692 if ( wxTextCtrlBase::LoadFile(file) )
693 {
694 // update the size limit if needed
695 AdjustSpaceLimit();
696
697 return TRUE;
698 }
699
700 return FALSE;
701 }
702
703 bool wxTextCtrl::IsModified() const
704 {
705 return (SendMessage(GetHwnd(), EM_GETMODIFY, 0, 0) != 0);
706 }
707
708 // Makes 'unmodified'
709 void wxTextCtrl::DiscardEdits()
710 {
711 SendMessage(GetHwnd(), EM_SETMODIFY, FALSE, 0L);
712 }
713
714 int wxTextCtrl::GetNumberOfLines() const
715 {
716 return (int)SendMessage(GetHwnd(), EM_GETLINECOUNT, (WPARAM)0, (LPARAM)0);
717 }
718
719 long wxTextCtrl::XYToPosition(long x, long y) const
720 {
721 HWND hWnd = GetHwnd();
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
728 bool wxTextCtrl::PositionToXY(long pos, long *x, long *y) const
729 {
730 HWND hWnd = GetHwnd();
731
732 // This gets the line number containing the character
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
749 // This gets the char index for the _beginning_ of this line
750 int charIndex = (int)SendMessage(hWnd, EM_LINEINDEX, (WPARAM)lineNo, (LPARAM)0);
751 if ( charIndex == -1 )
752 {
753 return FALSE;
754 }
755
756 // The X position must therefore be the different between pos and charIndex
757 if ( x )
758 *x = (long)(pos - charIndex);
759 if ( y )
760 *y = (long)lineNo;
761
762 return TRUE;
763 }
764
765 void wxTextCtrl::ShowPosition(long pos)
766 {
767 HWND hWnd = GetHwnd();
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);
783
784 int linesToScroll = specifiedLineLineNo - currentLineLineNo;
785
786 if (linesToScroll != 0)
787 (void)SendMessage(hWnd, EM_LINESCROLL, (WPARAM)0, (LPARAM)linesToScroll);
788 }
789
790 int wxTextCtrl::GetLineLength(long lineNo) const
791 {
792 long charIndex = XYToPosition(0, lineNo);
793 int len = (int)SendMessage(GetHwnd(), EM_LINELENGTH, charIndex, 0);
794 return len;
795 }
796
797 wxString wxTextCtrl::GetLineText(long lineNo) const
798 {
799 size_t len = (size_t)GetLineLength(lineNo) + 1;
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;
810 }
811
812 // ----------------------------------------------------------------------------
813 // Undo/redo
814 // ----------------------------------------------------------------------------
815
816 void wxTextCtrl::Undo()
817 {
818 if (CanUndo())
819 {
820 ::SendMessage(GetHwnd(), EM_UNDO, 0, 0);
821 }
822 }
823
824 void wxTextCtrl::Redo()
825 {
826 if (CanRedo())
827 {
828 // Same as Undo, since Undo undoes the undo, i.e. a redo.
829 ::SendMessage(GetHwnd(), EM_UNDO, 0, 0);
830 }
831 }
832
833 bool wxTextCtrl::CanUndo() const
834 {
835 return (::SendMessage(GetHwnd(), EM_CANUNDO, 0, 0) != 0);
836 }
837
838 bool wxTextCtrl::CanRedo() const
839 {
840 return (::SendMessage(GetHwnd(), EM_CANUNDO, 0, 0) != 0);
841 }
842
843 // ----------------------------------------------------------------------------
844 // implemenation details
845 // ----------------------------------------------------------------------------
846
847 void wxTextCtrl::Command(wxCommandEvent & event)
848 {
849 SetValue(event.GetString());
850 ProcessCommand (event);
851 }
852
853 void wxTextCtrl::OnDropFiles(wxDropFilesEvent& event)
854 {
855 // By default, load the first file into the text window.
856 if (event.GetNumberOfFiles() > 0)
857 {
858 LoadFile(event.GetFiles()[0]);
859 }
860 }
861
862 void wxTextCtrl::OnChar(wxKeyEvent& event)
863 {
864 switch ( event.KeyCode() )
865 {
866 case WXK_RETURN:
867 if ( !(m_windowStyle & wxTE_MULTILINE) )
868 {
869 wxCommandEvent event(wxEVT_COMMAND_TEXT_ENTER, m_windowId);
870 event.SetEventObject( this );
871 if ( GetEventHandler()->ProcessEvent(event) )
872 return;
873 }
874 //else: multiline controls need Enter for themselves
875
876 break;
877
878 case WXK_TAB:
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.
883 //
884 // NB: Notice that Ctrl-Tab is handled elsewhere and Alt-Tab is
885 // handled by Windows
886 {
887 wxNavigationKeyEvent eventNav;
888 eventNav.SetDirection(!event.ShiftDown());
889 eventNav.SetWindowChange(FALSE);
890 eventNav.SetEventObject(this);
891
892 if ( GetEventHandler()->ProcessEvent(eventNav) )
893 return;
894 }
895 break;
896
897 default:
898 event.Skip();
899 return;
900 }
901
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
905
906 // FIXME
907 event.Skip();
908 }
909
910 bool wxTextCtrl::MSWCommand(WXUINT param, WXWORD WXUNUSED(id))
911 {
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;
924
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;
935
936 case EN_MAXTEXT:
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:
943 case EN_ERRSPACE:
944 case EN_HSCROLL:
945 case EN_VSCROLL:
946 default:
947 return FALSE;
948 }
949
950 // processed
951 return TRUE;
952 }
953
954 void wxTextCtrl::AdjustSpaceLimit()
955 {
956 #ifndef __WIN16__
957 unsigned int len = ::GetWindowTextLength(GetHwnd()),
958 limit = ::SendMessage(GetHwnd(), EM_GETLIMITTEXT, 0, 0);
959 if ( len >= limit )
960 {
961 limit = len + 0x8000; // 32Kb
962
963 #if wxUSE_RICHEDIT
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 }
969 else
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
979 ::SendMessage(GetHwnd(), EM_LIMITTEXT, limit, 0);
980 }
981 }
982 #endif // !Win16
983 }
984
985 bool wxTextCtrl::AcceptsFocus() const
986 {
987 // we don't want focus if we can't be edited
988 return IsEditable() && wxControl::AcceptsFocus();
989 }
990
991 wxSize wxTextCtrl::DoGetBestSize() const
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);
1006 }
1007
1008 // ----------------------------------------------------------------------------
1009 // standard handlers for standard edit menu events
1010 // ----------------------------------------------------------------------------
1011
1012 void wxTextCtrl::OnCut(wxCommandEvent& event)
1013 {
1014 Cut();
1015 }
1016
1017 void wxTextCtrl::OnCopy(wxCommandEvent& event)
1018 {
1019 Copy();
1020 }
1021
1022 void wxTextCtrl::OnPaste(wxCommandEvent& event)
1023 {
1024 Paste();
1025 }
1026
1027 void wxTextCtrl::OnUndo(wxCommandEvent& event)
1028 {
1029 Undo();
1030 }
1031
1032 void wxTextCtrl::OnRedo(wxCommandEvent& event)
1033 {
1034 Redo();
1035 }
1036
1037 void wxTextCtrl::OnUpdateCut(wxUpdateUIEvent& event)
1038 {
1039 event.Enable( CanCut() );
1040 }
1041
1042 void wxTextCtrl::OnUpdateCopy(wxUpdateUIEvent& event)
1043 {
1044 event.Enable( CanCopy() );
1045 }
1046
1047 void wxTextCtrl::OnUpdatePaste(wxUpdateUIEvent& event)
1048 {
1049 event.Enable( CanPaste() );
1050 }
1051
1052 void wxTextCtrl::OnUpdateUndo(wxUpdateUIEvent& event)
1053 {
1054 event.Enable( CanUndo() );
1055 }
1056
1057 void wxTextCtrl::OnUpdateRedo(wxUpdateUIEvent& event)
1058 {
1059 event.Enable( CanRedo() );
1060 }
1061
1062 // ----------------------------------------------------------------------------
1063 // wxRichEditModule
1064 // ----------------------------------------------------------------------------
1065
1066 #if wxUSE_RICHEDIT
1067
1068 bool wxRichEditModule::OnInit()
1069 {
1070 // don't do anything - we will load it when needed
1071 return TRUE;
1072 }
1073
1074 void wxRichEditModule::OnExit()
1075 {
1076 if ( ms_hRichEdit )
1077 {
1078 FreeLibrary(ms_hRichEdit);
1079 }
1080 }
1081
1082 /* static */
1083 bool 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