]> git.saurik.com Git - wxWidgets.git/blame - src/msw/wince/textctrlce.cpp
Duplicate wxLC_VIRTUAL removed
[wxWidgets.git] / src / msw / wince / textctrlce.cpp
CommitLineData
302e251b
WS
1///////////////////////////////////////////////////////////////////////////////
2// Name: src/msw/wince/textctrlce.cpp
3// Purpose: wxTextCtrl implementation for Smartphones
4// Author: Wlodzimierz ABX Skiba
5// Modified by:
6// Created: 30.08.2004
7// RCS-ID: $Id$
8// Copyright: (c) Wlodzimierz Skiba
9// License: wxWindows licence
10///////////////////////////////////////////////////////////////////////////////
11
12
13// ============================================================================
14// declarations
15// ============================================================================
16
17// ----------------------------------------------------------------------------
18// headers
19// ----------------------------------------------------------------------------
20
21#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
22 #pragma implementation "textctrlce.h"
23#endif
24
25// For compilers that support precompilation, includes "wx.h".
26#include "wx/wxprec.h"
27
28#ifdef __BORLANDC__
29 #pragma hdrstop
30#endif
31
32#ifndef WX_PRECOMP
33 #include "wx/textctrl.h"
34#endif
35
36#include "wx/spinbutt.h"
37#include "wx/textfile.h"
38
39#include <commctrl.h>
40#include "wx/msw/missing.h"
41#include "wx/msw/winundef.h"
42
43#if wxUSE_TEXTCTRL && defined(__SMARTPHONE__)
44
45#define GetBuddyHwnd() (HWND)(m_hwndBuddy)
46
47#define IsVertical(wxStyle) (true)
48
49// ----------------------------------------------------------------------------
50// event tables and other macros
51// ----------------------------------------------------------------------------
52
53#if wxUSE_EXTENDED_RTTI
54// TODO
55#else
56IMPLEMENT_DYNAMIC_CLASS(wxTextCtrl, wxControl)
57#endif
58
59BEGIN_EVENT_TABLE(wxTextCtrl, wxControl)
60 EVT_CHAR(wxTextCtrl::OnChar)
61
62 EVT_MENU(wxID_CUT, wxTextCtrl::OnCut)
63 EVT_MENU(wxID_COPY, wxTextCtrl::OnCopy)
64 EVT_MENU(wxID_PASTE, wxTextCtrl::OnPaste)
65 EVT_MENU(wxID_UNDO, wxTextCtrl::OnUndo)
66 EVT_MENU(wxID_REDO, wxTextCtrl::OnRedo)
67 EVT_MENU(wxID_CLEAR, wxTextCtrl::OnDelete)
68 EVT_MENU(wxID_SELECTALL, wxTextCtrl::OnSelectAll)
69
70 EVT_UPDATE_UI(wxID_CUT, wxTextCtrl::OnUpdateCut)
71 EVT_UPDATE_UI(wxID_COPY, wxTextCtrl::OnUpdateCopy)
72 EVT_UPDATE_UI(wxID_PASTE, wxTextCtrl::OnUpdatePaste)
73 EVT_UPDATE_UI(wxID_UNDO, wxTextCtrl::OnUpdateUndo)
74 EVT_UPDATE_UI(wxID_REDO, wxTextCtrl::OnUpdateRedo)
75 EVT_UPDATE_UI(wxID_CLEAR, wxTextCtrl::OnUpdateDelete)
76 EVT_UPDATE_UI(wxID_SELECTALL, wxTextCtrl::OnUpdateSelectAll)
77
78 EVT_SET_FOCUS(wxTextCtrl::OnSetFocus)
79END_EVENT_TABLE()
80
81// ----------------------------------------------------------------------------
82// constants
83// ----------------------------------------------------------------------------
84
85// the margin between the up-down control and its buddy (can be arbitrary,
86// choose what you like - or may be decide during run-time depending on the
87// font size?)
88static const int MARGIN_BETWEEN = 0;
89
90// ============================================================================
91// implementation
92// ============================================================================
93
94wxArrayTextSpins wxTextCtrl::ms_allTextSpins;
95
96// ----------------------------------------------------------------------------
97// wnd proc for the buddy text ctrl
98// ----------------------------------------------------------------------------
99
100LRESULT APIENTRY _EXPORT wxBuddyTextCtrlWndProc(HWND hwnd,
101 UINT message,
102 WPARAM wParam,
103 LPARAM lParam)
104{
105 wxTextCtrl *spin = (wxTextCtrl *)wxGetWindowUserData(hwnd);
106
107 // forward some messages (the key and focus ones only so far) to
108 // the spin ctrl
109 switch ( message )
110 {
111 case WM_SETFOCUS:
112 // if the focus comes from the spin control itself, don't set it
113 // back to it -- we don't want to go into an infinite loop
114 if ( (WXHWND)wParam == spin->GetHWND() )
115 break;
116 //else: fall through
117
118 case WM_KILLFOCUS:
119 case WM_CHAR:
120 case WM_DEADCHAR:
121 case WM_KEYUP:
122 case WM_KEYDOWN:
123 spin->MSWWindowProc(message, wParam, lParam);
124
125 // The control may have been deleted at this point, so check.
126 if ( !::IsWindow(hwnd) || wxGetWindowUserData(hwnd) != spin )
127 return 0;
128 break;
129
130 case WM_GETDLGCODE:
131 // we want to get WXK_RETURN in order to generate the event for it
132 return DLGC_WANTCHARS;
133 }
134
135 return ::CallWindowProc(CASTWNDPROC spin->GetBuddyWndProc(),
136 hwnd, message, wParam, lParam);
137}
138
139// ----------------------------------------------------------------------------
140// creation
141// ----------------------------------------------------------------------------
142
143void wxTextCtrl::Init()
144{
145 m_suppressNextUpdate = false;
146 m_isNativeCaretShown = true;
147}
148
149wxTextCtrl::~wxTextCtrl()
150{
151}
152
153bool wxTextCtrl::Create(wxWindow *parent, wxWindowID id,
154 const wxString& value,
155 const wxPoint& pos,
156 const wxSize& size,
157 long style,
158 const wxValidator& validator,
159 const wxString& name)
160{
161 if ( (style & wxBORDER_MASK) == wxBORDER_DEFAULT )
162 style |= wxBORDER_SIMPLE;
163
164 SetWindowStyle(style);
165
166 WXDWORD exStyle = 0;
167 WXDWORD msStyle = MSWGetStyle(GetWindowStyle(), & exStyle) ;
168
169 wxSize sizeText(size), sizeBtn(size);
170 sizeBtn.x = GetBestSpinerSize(IsVertical(style)).x / 2;
171
172 if ( sizeText.x == wxDefaultCoord )
173 {
174 // DEFAULT_ITEM_WIDTH is the default width for the text control
175 sizeText.x = DEFAULT_ITEM_WIDTH + MARGIN_BETWEEN + sizeBtn.x;
176 }
177
178 sizeText.x -= sizeBtn.x + MARGIN_BETWEEN;
179 if ( sizeText.x <= 0 )
180 {
181 wxLogDebug(_T("not enough space for wxSpinCtrl!"));
182 }
183
184 wxPoint posBtn(pos);
185 posBtn.x += sizeText.x + MARGIN_BETWEEN;
186
187 // we need to turn '\n's into "\r\n"s for the multiline controls
188 wxString valueWin;
189 if ( m_windowStyle & wxTE_MULTILINE )
190 {
191 valueWin = wxTextFile::Translate(value, wxTextFileType_Dos);
192 }
193 else // single line
194 {
195 valueWin = value;
196 }
197
198 // we must create the list control before the spin button for the purpose
199 // of the dialog navigation: if there is a static text just before the spin
200 // control, activating it by Alt-letter should give focus to the text
201 // control, not the spin and the dialog navigation code will give focus to
202 // the next control (at Windows level), not the one after it
203
204 // create the text window
205
206 m_hwndBuddy = (WXHWND)::CreateWindowEx
207 (
208 exStyle, // sunken border
209 _T("EDIT"), // window class
210 valueWin, // no window title
211 msStyle, // style (will be shown later)
212 pos.x, pos.y, // position
213 0, 0, // size (will be set later)
214 GetHwndOf(parent), // parent
215 (HMENU)-1, // control id
216 wxGetInstance(), // app instance
217 NULL // unused client data
218 );
219
220 if ( !m_hwndBuddy )
221 {
222 wxLogLastError(wxT("CreateWindow(buddy text window)"));
223
224 return false;
225 }
226
227 // initialize wxControl
228 if ( !CreateControl(parent, id, posBtn, sizeBtn, style, validator, name) )
229 return false;
230
231 // now create the real HWND
232 WXDWORD spiner_style = WS_VISIBLE |
233 UDS_ALIGNRIGHT |
234 UDS_EXPANDABLE |
39fc096d 235 UDS_NOSCROLL;
302e251b
WS
236
237 if ( !IsVertical(style) )
238 spiner_style |= UDS_HORZ;
239
240 if ( style & wxSP_WRAP )
241 spiner_style |= UDS_WRAP;
242
243 if ( !MSWCreateControl(UPDOWN_CLASS, spiner_style, posBtn, sizeBtn, _T(""), 0) )
244 return false;
245
246 // subclass the text ctrl to be able to intercept some events
247 wxSetWindowUserData(GetBuddyHwnd(), this);
248 m_wndProcBuddy = (WXFARPROC)wxSetWindowProc(GetBuddyHwnd(),
249 wxBuddyTextCtrlWndProc);
250
251 // set up fonts and colours (This is nomally done in MSWCreateControl)
252 InheritAttributes();
253 if (!m_hasFont)
254 SetFont(GetDefaultAttributes().font);
255
256 // set the size of the text window - can do it only now, because we
257 // couldn't call DoGetBestSize() before as font wasn't set
258 if ( sizeText.y <= 0 )
259 {
260 int cx, cy;
261 wxGetCharSize(GetHWND(), &cx, &cy, GetFont());
262
263 sizeText.y = EDIT_HEIGHT_FROM_CHAR_HEIGHT(cy);
264 }
265
266 SetBestSize(size);
267
268 (void)::ShowWindow(GetBuddyHwnd(), SW_SHOW);
269
270 // associate the list window with the spin button
271 (void)::SendMessage(GetHwnd(), UDM_SETBUDDY, (WPARAM)GetBuddyHwnd(), 0);
272
273 // do it after finishing with m_hwndBuddy creation to avoid generating
274 // initial wxEVT_COMMAND_TEXT_UPDATED message
275 ms_allTextSpins.Add(this);
276
277 return true;
278}
279
280// Make sure the window style (etc.) reflects the HWND style (roughly)
281void wxTextCtrl::AdoptAttributesFromHWND()
282{
283 wxWindow::AdoptAttributesFromHWND();
284
285 long style = ::GetWindowLong(GetBuddyHwnd(), GWL_STYLE);
286
287 if (style & ES_MULTILINE)
288 m_windowStyle |= wxTE_MULTILINE;
289 if (style & ES_PASSWORD)
290 m_windowStyle |= wxTE_PASSWORD;
291 if (style & ES_READONLY)
292 m_windowStyle |= wxTE_READONLY;
293 if (style & ES_WANTRETURN)
294 m_windowStyle |= wxTE_PROCESS_ENTER;
295 if (style & ES_CENTER)
296 m_windowStyle |= wxTE_CENTRE;
297 if (style & ES_RIGHT)
298 m_windowStyle |= wxTE_RIGHT;
299}
300
301WXDWORD wxTextCtrl::MSWGetStyle(long style, WXDWORD *exstyle) const
302{
303 // we never have an external border
304 WXDWORD msStyle = wxControl::MSWGetStyle
305 (
306 (style & ~wxBORDER_MASK) | wxBORDER_NONE, exstyle
307 );
308
309 msStyle |= WS_VISIBLE;
310
311 // styles which we alaways add by default
312 if ( style & wxTE_MULTILINE )
313 {
314 wxASSERT_MSG( !(style & wxTE_PROCESS_ENTER),
315 wxT("wxTE_PROCESS_ENTER style is ignored for multiline text controls (they always process it)") );
316
317 msStyle |= ES_MULTILINE | ES_WANTRETURN;
318 if ( !(style & wxTE_NO_VSCROLL) )
319 {
320 // always adjust the vertical scrollbar automatically if we have it
321 msStyle |= WS_VSCROLL | ES_AUTOVSCROLL;
322 }
323
324 style |= wxTE_PROCESS_ENTER;
325 }
326 else // !multiline
327 {
328 // there is really no reason to not have this style for single line
329 // text controls
330 msStyle |= ES_AUTOHSCROLL;
331 }
332
333 // note that wxTE_DONTWRAP is the same as wxHSCROLL so if we have a horz
334 // scrollbar, there is no wrapping -- which makes sense
335 if ( style & wxTE_DONTWRAP )
336 {
337 // automatically scroll the control horizontally as necessary
338 //
339 // NB: ES_AUTOHSCROLL is needed for richedit controls or they don't
340 // show horz scrollbar at all, even in spite of WS_HSCROLL, and as
341 // it doesn't seem to do any harm for plain edit controls, add it
342 // always
343 msStyle |= WS_HSCROLL | ES_AUTOHSCROLL;
344 }
345
346 if ( style & wxTE_READONLY )
347 msStyle |= ES_READONLY;
348
349 if ( style & wxTE_PASSWORD )
350 msStyle |= ES_PASSWORD;
351
352 if ( style & wxTE_NOHIDESEL )
353 msStyle |= ES_NOHIDESEL;
354
355 // note that we can't do do "& wxTE_LEFT" as wxTE_LEFT == 0
356 if ( style & wxTE_CENTRE )
357 msStyle |= ES_CENTER;
358 else if ( style & wxTE_RIGHT )
359 msStyle |= ES_RIGHT;
360 else
361 msStyle |= ES_LEFT; // ES_LEFT is 0 as well but for consistency...
362
363 return msStyle;
364}
365
366// ----------------------------------------------------------------------------
367// set/get the controls text
368// ----------------------------------------------------------------------------
369
370wxString wxTextCtrl::GetValue() const
371{
372 // range 0..-1 is special for GetRange() and means to retrieve all text
373 return GetRange(0, -1);
374}
375
376wxString wxTextCtrl::GetRange(long from, long to) const
377{
378 wxString str;
379
380 if ( from >= to && to != -1 )
381 {
382 // nothing to retrieve
383 return str;
384 }
385
386 // retrieve all text
387 str = wxGetWindowText(GetBuddyHwnd());
388
389 // need only a range?
390 if ( from < to )
391 {
392 str = str.Mid(from, to - from);
393 }
394
395 // WM_GETTEXT uses standard DOS CR+LF (\r\n) convention - convert to the
396 // canonical one (same one as above) for consistency with the other kinds
397 // of controls and, more importantly, with the other ports
398 str = wxTextFile::Translate(str, wxTextFileType_Unix);
399
400 return str;
401}
402
403void wxTextCtrl::SetValue(const wxString& value)
404{
405 // if the text is long enough, it's faster to just set it instead of first
406 // comparing it with the old one (chances are that it will be different
407 // anyhow, this comparison is there to avoid flicker for small single-line
408 // edit controls mostly)
409 if ( (value.length() > 0x400) || (value != GetValue()) )
410 {
411 DoWriteText(value, false);
39fc096d 412
302e251b
WS
413 // for compatibility, don't move the cursor when doing SetValue()
414 SetInsertionPoint(0);
415 }
416 else // same text
417 {
418 // still send an event for consistency
419 SendUpdateEvent();
420 }
421
422 // we should reset the modified flag even if the value didn't really change
423
424 // mark the control as being not dirty - we changed its text, not the
425 // user
426 DiscardEdits();
427}
428
429void wxTextCtrl::WriteText(const wxString& value)
430{
431 DoWriteText(value);
432}
433
434void wxTextCtrl::DoWriteText(const wxString& value, bool selectionOnly)
435{
436 wxString valueDos;
437 if ( m_windowStyle & wxTE_MULTILINE )
438 valueDos = wxTextFile::Translate(value, wxTextFileType_Dos);
439 else
440 valueDos = value;
441
442 // in some cases we get 2 EN_CHANGE notifications after the SendMessage
443 // call below which is confusing for the client code and so should be
444 // avoided
445 //
446 if ( ( selectionOnly && HasSelection() ) )
447 {
448 m_suppressNextUpdate = true;
449 }
450
451 ::SendMessage(GetBuddyHwnd(), selectionOnly ? EM_REPLACESEL : WM_SETTEXT,
452 0, (LPARAM)valueDos.c_str());
453
454 if ( !selectionOnly )
455 {
456 // Windows already sends an update event for single-line
457 // controls.
458 if ( m_windowStyle & wxTE_MULTILINE )
459 SendUpdateEvent();
460 }
461
462 AdjustSpaceLimit();
463}
464
465void wxTextCtrl::AppendText(const wxString& text)
466{
467 SetInsertionPointEnd();
468
469 WriteText(text);
470}
471
472void wxTextCtrl::Clear()
473{
474 ::SetWindowText(GetBuddyHwnd(), wxEmptyString);
475
476 // Windows already sends an update event for single-line
477 // controls.
478 if ( m_windowStyle & wxTE_MULTILINE )
479 SendUpdateEvent();
480}
481
482// ----------------------------------------------------------------------------
483// Clipboard operations
484// ----------------------------------------------------------------------------
485
486void wxTextCtrl::Copy()
487{
488 if (CanCopy())
489 {
490 ::SendMessage(GetBuddyHwnd(), WM_COPY, 0, 0L);
491 }
492}
493
494void wxTextCtrl::Cut()
495{
496 if (CanCut())
497 {
498 ::SendMessage(GetBuddyHwnd(), WM_CUT, 0, 0L);
499 }
500}
501
502void wxTextCtrl::Paste()
503{
504 if (CanPaste())
505 {
506 ::SendMessage(GetBuddyHwnd(), WM_PASTE, 0, 0L);
507 }
508}
509
510bool wxTextCtrl::HasSelection() const
511{
512 long from, to;
513 GetSelection(&from, &to);
514 return from != to;
515}
516
517bool wxTextCtrl::CanCopy() const
518{
519 // Can copy if there's a selection
520 return HasSelection();
521}
522
523bool wxTextCtrl::CanCut() const
524{
525 return CanCopy() && IsEditable();
526}
527
528bool wxTextCtrl::CanPaste() const
529{
530 if ( !IsEditable() )
531 return false;
532
533 // Standard edit control: check for straight text on clipboard
534 if ( !::OpenClipboard(GetHwndOf(wxTheApp->GetTopWindow())) )
535 return false;
536
537 bool isTextAvailable = ::IsClipboardFormatAvailable(CF_TEXT) != 0;
538 ::CloseClipboard();
539
540 return isTextAvailable;
541}
542
543// ----------------------------------------------------------------------------
544// Accessors
545// ----------------------------------------------------------------------------
546
547void wxTextCtrl::SetEditable(bool editable)
548{
39fc096d 549 ::SendMessage(GetBuddyHwnd(), EM_SETREADONLY, (WPARAM)!editable, (LPARAM)0L);
302e251b
WS
550}
551
552void wxTextCtrl::SetInsertionPoint(long pos)
553{
554 DoSetSelection(pos, pos);
555}
556
557void wxTextCtrl::SetInsertionPointEnd()
558{
559 if ( GetInsertionPoint() != GetLastPosition() )
560 SetInsertionPoint(GetLastPosition());
561}
562
563long wxTextCtrl::GetInsertionPoint() const
564{
565 DWORD Pos = (DWORD)::SendMessage(GetBuddyHwnd(), EM_GETSEL, 0, 0L);
566 return Pos & 0xFFFF;
567}
568
569long wxTextCtrl::GetLastPosition() const
570{
571 int numLines = GetNumberOfLines();
572 long posStartLastLine = XYToPosition(0, numLines - 1);
573
574 long lenLastLine = GetLengthOfLineContainingPos(posStartLastLine);
575
576 return posStartLastLine + lenLastLine;
577}
578
579void wxTextCtrl::GetSelection(long* from, long* to) const
580{
581 DWORD dwStart, dwEnd;
582 ::SendMessage(GetBuddyHwnd(), EM_GETSEL, (WPARAM)&dwStart, (LPARAM)&dwEnd);
583
584 *from = dwStart;
585 *to = dwEnd;
586}
587
588bool wxTextCtrl::IsEditable() const
589{
590 if ( !GetBuddyHwnd() )
591 return true;
592
593 long style = ::GetWindowLong(GetBuddyHwnd(), GWL_STYLE);
594
595 return (style & ES_READONLY) == 0;
596}
597
598// ----------------------------------------------------------------------------
599// selection
600// ----------------------------------------------------------------------------
601
602void wxTextCtrl::SetSelection(long from, long to)
603{
604 // if from and to are both -1, it means (in wxWidgets) that all text should
605 // be selected - translate into Windows convention
606 if ( (from == -1) && (to == -1) )
607 {
608 from = 0;
609 to = -1;
610 }
611
612 DoSetSelection(from, to);
613}
614
615void wxTextCtrl::DoSetSelection(long from, long to, bool scrollCaret)
616{
617 ::SendMessage(GetBuddyHwnd(), EM_SETSEL, (WPARAM)from, (LPARAM)to);
618
619 if ( scrollCaret )
620 {
621 ::SendMessage(GetBuddyHwnd(), EM_SCROLLCARET, (WPARAM)0, (LPARAM)0);
622 }
623}
624
625// ----------------------------------------------------------------------------
626// Working with files
627// ----------------------------------------------------------------------------
628
629bool wxTextCtrl::LoadFile(const wxString& file)
630{
631 if ( wxTextCtrlBase::LoadFile(file) )
632 {
633 // update the size limit if needed
634 AdjustSpaceLimit();
635
636 return true;
637 }
638
639 return false;
640}
641
642// ----------------------------------------------------------------------------
643// Editing
644// ----------------------------------------------------------------------------
645
646void wxTextCtrl::Replace(long from, long to, const wxString& value)
647{
648 // Set selection and remove it
649 DoSetSelection(from, to, false);
650
651 DoWriteText(value, true);
652}
653
654void wxTextCtrl::Remove(long from, long to)
655{
656 Replace(from, to, wxEmptyString);
657}
658
659bool wxTextCtrl::IsModified() const
660{
661 return ::SendMessage(GetBuddyHwnd(), EM_GETMODIFY, 0, 0) != 0;
662}
663
664void wxTextCtrl::MarkDirty()
665{
666 ::SendMessage(GetBuddyHwnd(), EM_SETMODIFY, TRUE, 0L);
667}
668
669void wxTextCtrl::DiscardEdits()
670{
671 ::SendMessage(GetBuddyHwnd(), EM_SETMODIFY, FALSE, 0L);
672}
673
674int wxTextCtrl::GetNumberOfLines() const
675{
676 return (int)::SendMessage(GetBuddyHwnd(), EM_GETLINECOUNT, 0, 0L);
677}
678
679// ----------------------------------------------------------------------------
680// Positions <-> coords
681// ----------------------------------------------------------------------------
682
683long wxTextCtrl::XYToPosition(long x, long y) const
684{
685 // This gets the char index for the _beginning_ of this line
686 long charIndex = ::SendMessage(GetBuddyHwnd(), EM_LINEINDEX, (WPARAM)y, (LPARAM)0);
687
688 return charIndex + x;
689}
690
691bool wxTextCtrl::PositionToXY(long pos, long *x, long *y) const
692{
693 // This gets the line number containing the character
694 long lineNo = ::SendMessage(GetBuddyHwnd(), EM_LINEFROMCHAR, (WPARAM)pos, 0);
695
696 if ( lineNo == -1 )
697 {
698 // no such line
699 return false;
700 }
701
702 // This gets the char index for the _beginning_ of this line
703 long charIndex = ::SendMessage(GetBuddyHwnd(), EM_LINEINDEX, (WPARAM)lineNo, (LPARAM)0);
704 if ( charIndex == -1 )
705 {
706 return false;
707 }
708
709 // The X position must therefore be the different between pos and charIndex
710 if ( x )
711 *x = pos - charIndex;
712 if ( y )
713 *y = lineNo;
714
715 return true;
716}
717
718wxTextCtrlHitTestResult
719wxTextCtrl::HitTest(const wxPoint& pt, long *posOut) const
720{
721 // first get the position from Windows
722 // for the plain ones, we are limited to 16 bit positions which are
723 // combined in a single 32 bit value
724 LPARAM lParam = MAKELPARAM(pt.x, pt.y);
725
726 LRESULT pos = ::SendMessage(GetBuddyHwnd(), EM_CHARFROMPOS, 0, lParam);
727
728 if ( pos == -1 )
729 {
730 // this seems to indicate an error...
731 return wxTE_HT_UNKNOWN;
732 }
733
734 // for plain EDIT controls the higher word contains something else
735 pos = LOWORD(pos);
736
737
738 // next determine where it is relatively to our point: EM_CHARFROMPOS
739 // always returns the closest character but we need to be more precise, so
740 // double check that we really are where it pretends
741 POINTL ptReal;
742
39fc096d 743 LRESULT lRc = ::SendMessage(GetBuddyHwnd(), EM_POSFROMCHAR, pos, 0);
302e251b 744
39fc096d 745 if ( lRc == -1 )
302e251b
WS
746 {
747 // this is apparently returned when pos corresponds to the last
748 // position
749 ptReal.x =
750 ptReal.y = 0;
751 }
752 else
753 {
754 ptReal.x = LOWORD(lRc);
755 ptReal.y = HIWORD(lRc);
756 }
757
758 wxTextCtrlHitTestResult rc;
759
760 if ( pt.y > ptReal.y + GetCharHeight() )
761 rc = wxTE_HT_BELOW;
762 else if ( pt.x > ptReal.x + GetCharWidth() )
763 rc = wxTE_HT_BEYOND;
764 else
765 rc = wxTE_HT_ON_TEXT;
766
767 if ( posOut )
768 *posOut = pos;
769
770 return rc;
771}
772
773void wxTextCtrl::ShowPosition(long pos)
774{
775 int currentLineLineNo = (int)::SendMessage(GetBuddyHwnd(), EM_GETFIRSTVISIBLELINE, 0, 0L);
776
777 int specifiedLineLineNo = (int)::SendMessage(GetBuddyHwnd(), EM_LINEFROMCHAR, (WPARAM)pos, 0L);
778
779 int linesToScroll = specifiedLineLineNo - currentLineLineNo;
780
781 if (linesToScroll != 0)
39fc096d 782 (void)::SendMessage(GetBuddyHwnd(), EM_LINESCROLL, 0, (LPARAM)linesToScroll);
302e251b
WS
783}
784
785long wxTextCtrl::GetLengthOfLineContainingPos(long pos) const
786{
787 return ::SendMessage(GetBuddyHwnd(), EM_LINELENGTH, (WPARAM)pos, 0L);
788}
789
790int wxTextCtrl::GetLineLength(long lineNo) const
791{
792 long pos = XYToPosition(0, lineNo);
793
794 return GetLengthOfLineContainingPos(pos);
795}
796
797wxString wxTextCtrl::GetLineText(long lineNo) const
798{
799 size_t len = (size_t)GetLineLength(lineNo) + 1;
800
801 // there must be at least enough place for the length WORD in the
802 // buffer
803 len += sizeof(WORD);
804
805 wxString str;
806 {
807 wxStringBufferLength tmp(str, len);
808 wxChar *buf = tmp;
809
810 *(WORD *)buf = (WORD)len;
811 len = (size_t)::SendMessage(GetBuddyHwnd(), EM_GETLINE, lineNo, (LPARAM)buf);
812
813 // remove the '\n' at the end, if any (this is how this function is
814 // supposed to work according to the docs)
815 if ( buf[len - 1] == _T('\n') )
816 {
817 len--;
818 }
819
820 buf[len] = 0;
821 tmp.SetLength(len);
822 }
823
824 return str;
825}
826
827void wxTextCtrl::SetMaxLength(unsigned long len)
828{
829 ::SendMessage(GetBuddyHwnd(), EM_LIMITTEXT, len, 0);
830}
831
832// ----------------------------------------------------------------------------
833// Undo/redo
834// ----------------------------------------------------------------------------
835
836void wxTextCtrl::Undo()
837{
838 if (CanUndo())
839 {
840 ::SendMessage(GetBuddyHwnd(), EM_UNDO, 0, 0);
841 }
842}
843
844void wxTextCtrl::Redo()
845{
846 if (CanRedo())
847 {
848 ::SendMessage(GetBuddyHwnd(), EM_UNDO, 0, 0);
849 }
850}
851
852bool wxTextCtrl::CanUndo() const
853{
854 return ::SendMessage(GetBuddyHwnd(), EM_CANUNDO, 0, 0) != 0;
855}
856
857bool wxTextCtrl::CanRedo() const
858{
859 return ::SendMessage(GetBuddyHwnd(), EM_CANUNDO, 0, 0) != 0;
860}
861
862// ----------------------------------------------------------------------------
863// caret handling
864// ----------------------------------------------------------------------------
865
866// ----------------------------------------------------------------------------
867// implemenation details
868// ----------------------------------------------------------------------------
869
870void wxTextCtrl::Command(wxCommandEvent & event)
871{
872 SetValue(event.GetString());
873 ProcessCommand (event);
874}
875
876// ----------------------------------------------------------------------------
877// kbd input processing
878// ----------------------------------------------------------------------------
879
880void wxTextCtrl::OnChar(wxKeyEvent& event)
881{
882 switch ( event.GetKeyCode() )
883 {
884 case WXK_RETURN:
885 if ( !HasFlag(wxTE_MULTILINE) )
886 {
887 wxCommandEvent event(wxEVT_COMMAND_TEXT_ENTER, m_windowId);
888 InitCommandEvent(event);
889 event.SetString(GetValue());
890 if ( GetEventHandler()->ProcessEvent(event) )
891 return;
892 }
893 //else: multiline controls need Enter for themselves
894
895 break;
896
897 case WXK_TAB:
898 // ok, so this is getting absolutely ridiculous but I don't see
899 // any other way to fix this bug: when a multiline text control is
900 // inside a wxFrame, we need to generate the navigation event as
901 // otherwise nothing happens at all, but when the same control is
902 // created inside a dialog, IsDialogMessage() *does* switch focus
903 // all by itself and so if we do it here as well, it is advanced
904 // twice and goes to the next control... to prevent this from
905 // happening we're doing this ugly check, the logic being that if
906 // we don't have focus then it had been already changed to the next
907 // control
908 //
909 // the right thing to do would, of course, be to understand what
910 // the hell is IsDialogMessage() doing but this is beyond my feeble
911 // forces at the moment unfortunately
912 if ( !(m_windowStyle & wxTE_PROCESS_TAB))
913 {
914 if ( FindFocus() == this )
915 {
916 int flags = 0;
917 if (!event.ShiftDown())
918 flags |= wxNavigationKeyEvent::IsForward ;
919 if (event.ControlDown())
920 flags |= wxNavigationKeyEvent::WinChange ;
921 if (Navigate(flags))
922 return;
923 }
924 }
925 else
926 {
927 // Insert tab since calling the default Windows handler
928 // doesn't seem to do it
929 WriteText(wxT("\t"));
930 }
931 break;
932 }
933
934 // no, we didn't process it
935 event.Skip();
936}
937
938WXLRESULT wxTextCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
939{
940 WXLRESULT lRc = wxTextCtrlBase::MSWWindowProc(nMsg, wParam, lParam);
941
942 if ( nMsg == WM_GETDLGCODE )
943 {
944 // we always want the chars and the arrows: the arrows for navigation
945 // and the chars because we want Ctrl-C to work even in a read only
946 // control
947 long lDlgCode = DLGC_WANTCHARS | DLGC_WANTARROWS;
948
949 if ( IsEditable() )
950 {
951 // we may have several different cases:
952 // 1. normal case: both TAB and ENTER are used for dlg navigation
953 // 2. ctrl which wants TAB for itself: ENTER is used to pass to the
954 // next control in the dialog
955 // 3. ctrl which wants ENTER for itself: TAB is used for dialog
956 // navigation
957 // 4. ctrl which wants both TAB and ENTER: Ctrl-ENTER is used to go
958 // to the next control
959
960 // the multiline edit control should always get <Return> for itself
961 if ( HasFlag(wxTE_PROCESS_ENTER) || HasFlag(wxTE_MULTILINE) )
962 lDlgCode |= DLGC_WANTMESSAGE;
963
964 if ( HasFlag(wxTE_PROCESS_TAB) )
965 lDlgCode |= DLGC_WANTTAB;
966
967 lRc |= lDlgCode;
968 }
969 else // !editable
970 {
971 // NB: use "=", not "|=" as the base class version returns the
972 // same flags is this state as usual (i.e. including
973 // DLGC_WANTMESSAGE). This is strange (how does it work in the
974 // native Win32 apps?) but for now live with it.
975 lRc = lDlgCode;
976 }
977 }
978
979 return lRc;
980}
981
982// ----------------------------------------------------------------------------
983// text control event processing
984// ----------------------------------------------------------------------------
985
986bool wxTextCtrl::SendUpdateEvent()
987{
988 // is event reporting suspended?
989 if ( m_suppressNextUpdate )
990 {
991 // do process the next one
992 m_suppressNextUpdate = false;
993
994 return false;
995 }
996
997 wxCommandEvent event(wxEVT_COMMAND_TEXT_UPDATED, GetId());
998 InitCommandEvent(event);
999 event.SetString(GetValue());
1000
1001 return ProcessCommand(event);
1002}
1003
1004bool wxTextCtrl::MSWCommand(WXUINT param, WXWORD WXUNUSED(id))
1005{
1006 switch ( param )
1007 {
1008 case EN_SETFOCUS:
1009 case EN_KILLFOCUS:
1010 {
1011 wxFocusEvent event(param == EN_KILLFOCUS ? wxEVT_KILL_FOCUS
1012 : wxEVT_SET_FOCUS,
1013 m_windowId);
1014 event.SetEventObject(this);
1015 GetEventHandler()->ProcessEvent(event);
1016 }
1017 break;
1018
1019 case EN_CHANGE:
1020 SendUpdateEvent();
1021 break;
1022
1023 case EN_MAXTEXT:
1024 // the text size limit has been hit -- try to increase it
1025 if ( !AdjustSpaceLimit() )
1026 {
1027 wxCommandEvent event(wxEVT_COMMAND_TEXT_MAXLEN, m_windowId);
1028 InitCommandEvent(event);
1029 event.SetString(GetValue());
1030 ProcessCommand(event);
1031 }
1032 break;
1033
1034 // the other edit notification messages are not processed
1035 default:
1036 return false;
1037 }
1038
1039 // processed
1040 return true;
1041}
1042
1043WXHBRUSH wxTextCtrl::OnCtlColor(WXHDC pDC, WXHWND WXUNUSED(pWnd), WXUINT WXUNUSED(nCtlColor),
1044#if wxUSE_CTL3D
1045 WXUINT message,
1046 WXWPARAM wParam,
1047 WXLPARAM lParam
1048#else
1049 WXUINT WXUNUSED(message),
1050 WXWPARAM WXUNUSED(wParam),
1051 WXLPARAM WXUNUSED(lParam)
1052#endif
1053 )
1054{
1055#if wxUSE_CTL3D
1056 if ( m_useCtl3D )
1057 {
1058 HBRUSH hbrush = Ctl3dCtlColorEx(message, wParam, lParam);
1059 return (WXHBRUSH) hbrush;
1060 }
1061#endif // wxUSE_CTL3D
1062
1063 HDC hdc = (HDC)pDC;
1064 wxColour colBack = GetBackgroundColour();
1065
1066 if (!IsEnabled() && (GetWindowStyle() & wxTE_MULTILINE) == 0)
1067 colBack = wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE);
1068
1069 ::SetBkColor(hdc, wxColourToRGB(colBack));
1070 ::SetTextColor(hdc, wxColourToRGB(GetForegroundColour()));
1071
1072 wxBrush *brush = wxTheBrushList->FindOrCreateBrush(colBack, wxSOLID);
1073
1074 return (WXHBRUSH)brush->GetResourceHandle();
1075}
1076
1077bool wxTextCtrl::AdjustSpaceLimit()
1078{
1079 unsigned int limit = ::SendMessage(GetBuddyHwnd(), EM_GETLIMITTEXT, 0, 0);
1080
1081 // HACK: we try to automatically extend the limit for the amount of text
1082 // to allow (interactively) entering more than 64Kb of text under
1083 // Win9x but we shouldn't reset the text limit which was previously
1084 // set explicitly with SetMaxLength()
1085 //
1086 // we could solve this by storing the limit we set in wxTextCtrl but
1087 // to save space we prefer to simply test here the actual limit
1088 // value: we consider that SetMaxLength() can only be called for
1089 // values < 32Kb
1090 if ( limit < 0x8000 )
1091 {
1092 // we've got more text than limit set by SetMaxLength()
1093 return false;
1094 }
1095
1096 unsigned int len = ::GetWindowTextLength(GetBuddyHwnd());
1097 if ( len >= limit )
1098 {
1099 limit = len + 0x8000; // 32Kb
1100
1101 if ( limit > 0xffff )
1102 {
1103 // this will set it to a platform-dependent maximum (much more
1104 // than 64Kb under NT)
1105 limit = 0;
1106 }
1107
1108 ::SendMessage(GetBuddyHwnd(), EM_LIMITTEXT, limit, 0L);
1109 }
1110
1111 // we changed the limit
1112 return true;
1113}
1114
1115bool wxTextCtrl::AcceptsFocus() const
1116{
1117 // we don't want focus if we can't be edited unless we're a multiline
1118 // control because then it might be still nice to get focus from keyboard
1119 // to be able to scroll it without mouse
1120 return (IsEditable() || IsMultiLine()) && wxControl::AcceptsFocus();
1121}
1122
1123void wxTextCtrl::DoMoveWindow(int x, int y, int width, int height)
1124{
1125 int widthBtn = GetBestSpinerSize(IsVertical(GetWindowStyle())).x / 2;
1126 int widthText = width - widthBtn - MARGIN_BETWEEN;
1127 if ( widthText <= 0 )
1128 {
1129 wxLogDebug(_T("not enough space for wxSpinCtrl!"));
1130 }
1131
1132 if ( !::MoveWindow(GetBuddyHwnd(), x, y, widthText, height, TRUE) )
1133 {
1134 wxLogLastError(wxT("MoveWindow(buddy)"));
1135 }
1136
1137 x += widthText + MARGIN_BETWEEN;
1138 if ( !::MoveWindow(GetHwnd(), x, y, widthBtn, height, TRUE) )
1139 {
1140 wxLogLastError(wxT("MoveWindow"));
1141 }
1142}
1143
1144wxSize wxTextCtrl::DoGetBestSize() const
1145{
1146 int cx, cy;
1147 wxGetCharSize(GetBuddyHwnd(), &cx, &cy, GetFont());
1148
1149 int wText = DEFAULT_ITEM_WIDTH;
1150
1151 int hText = cy;
1152 if ( m_windowStyle & wxTE_MULTILINE )
1153 {
1154 hText *= wxMax(GetNumberOfLines(), 5);
1155 }
1156 //else: for single line control everything is ok
1157
1158 // we have to add the adjustments for the control height only once, not
1159 // once per line, so do it after multiplication above
1160 hText += EDIT_HEIGHT_FROM_CHAR_HEIGHT(cy) - cy;
1161
1162 return wxSize(wText, hText);
1163}
1164
1165// ----------------------------------------------------------------------------
1166// standard handlers for standard edit menu events
1167// ----------------------------------------------------------------------------
1168
1169void wxTextCtrl::OnCut(wxCommandEvent& WXUNUSED(event))
1170{
1171 Cut();
1172}
1173
1174void wxTextCtrl::OnCopy(wxCommandEvent& WXUNUSED(event))
1175{
1176 Copy();
1177}
1178
1179void wxTextCtrl::OnPaste(wxCommandEvent& WXUNUSED(event))
1180{
1181 Paste();
1182}
1183
1184void wxTextCtrl::OnUndo(wxCommandEvent& WXUNUSED(event))
1185{
1186 Undo();
1187}
1188
1189void wxTextCtrl::OnRedo(wxCommandEvent& WXUNUSED(event))
1190{
1191 Redo();
1192}
1193
1194void wxTextCtrl::OnDelete(wxCommandEvent& WXUNUSED(event))
1195{
1196 long from, to;
1197 GetSelection(& from, & to);
1198 if (from != -1 && to != -1)
1199 Remove(from, to);
1200}
1201
1202void wxTextCtrl::OnSelectAll(wxCommandEvent& WXUNUSED(event))
1203{
1204 SetSelection(-1, -1);
1205}
1206
1207void wxTextCtrl::OnUpdateCut(wxUpdateUIEvent& event)
1208{
1209 event.Enable( CanCut() );
1210}
1211
1212void wxTextCtrl::OnUpdateCopy(wxUpdateUIEvent& event)
1213{
1214 event.Enable( CanCopy() );
1215}
1216
1217void wxTextCtrl::OnUpdatePaste(wxUpdateUIEvent& event)
1218{
1219 event.Enable( CanPaste() );
1220}
1221
1222void wxTextCtrl::OnUpdateUndo(wxUpdateUIEvent& event)
1223{
1224 event.Enable( CanUndo() );
1225}
1226
1227void wxTextCtrl::OnUpdateRedo(wxUpdateUIEvent& event)
1228{
1229 event.Enable( CanRedo() );
1230}
1231
1232void wxTextCtrl::OnUpdateDelete(wxUpdateUIEvent& event)
1233{
1234 long from, to;
1235 GetSelection(& from, & to);
1236 event.Enable(from != -1 && to != -1 && from != to && IsEditable()) ;
1237}
1238
1239void wxTextCtrl::OnUpdateSelectAll(wxUpdateUIEvent& event)
1240{
1241 event.Enable(GetLastPosition() > 0);
1242}
1243
1244void wxTextCtrl::OnSetFocus(wxFocusEvent& WXUNUSED(event))
1245{
1246 // be sure the caret remains invisible if the user had hidden it
1247 if ( !m_isNativeCaretShown )
1248 {
1249 ::HideCaret(GetBuddyHwnd());
1250 }
1251}
1252
1253#endif // wxUSE_TEXTCTRL && __SMARTPHONE__