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