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: textctrlce.cpp 42816 2006-10-31 08:50:17Z RD $
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 
wxBuddyTextCtrlWndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)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 
Init()136 void wxTextCtrl::Init()
137 {
138     m_suppressNextUpdate = false;
139     m_isNativeCaretShown = true;
140 }
141 
~wxTextCtrl()142 wxTextCtrl::~wxTextCtrl()
143 {
144 }
145 
Create(wxWindow * parent,wxWindowID id,const wxString & value,const wxPoint & pos,const wxSize & size,long style,const wxValidator & validator,const wxString & name)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     SetInitialSize(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)
AdoptAttributesFromHWND()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 
MSWGetStyle(long style,WXDWORD * exstyle) const294 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 
GetValue() const363 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 
GetRange(long from,long to) const369 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 
DoSetValue(const wxString & value,int flags)396 void wxTextCtrl::DoSetValue(const wxString& value, int flags)
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, flags);
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         if ( flags & SetValue_SendEvent )
413             SendUpdateEvent();
414     }
415 
416     // we should reset the modified flag even if the value didn't really change
417 
418     // mark the control as being not dirty - we changed its text, not the
419     // user
420     DiscardEdits();
421 }
422 
WriteText(const wxString & value)423 void wxTextCtrl::WriteText(const wxString& value)
424 {
425     DoWriteText(value);
426 }
427 
DoWriteText(const wxString & value,int flags)428 void wxTextCtrl::DoWriteText(const wxString& value, int flags)
429 {
430     bool selectionOnly = (flags & SetValue_SelectionOnly) != 0;
431     wxString valueDos;
432     if ( m_windowStyle & wxTE_MULTILINE )
433         valueDos = wxTextFile::Translate(value, wxTextFileType_Dos);
434     else
435         valueDos = value;
436 
437     // in some cases we get 2 EN_CHANGE notifications after the SendMessage
438     // call below which is confusing for the client code and so should be
439     // avoided
440     //
441     if ( selectionOnly && HasSelection() )
442     {
443         m_suppressNextUpdate = true;
444     }
445 
446     ::SendMessage(GetBuddyHwnd(), selectionOnly ? EM_REPLACESEL : WM_SETTEXT,
447                   0, (LPARAM)valueDos.c_str());
448 
449     if ( !selectionOnly && !( flags & SetValue_SendEvent ) )
450     {
451         // Windows already sends an update event for single-line
452         // controls.
453         if ( m_windowStyle & wxTE_MULTILINE )
454             SendUpdateEvent();
455     }
456 
457     AdjustSpaceLimit();
458 }
459 
AppendText(const wxString & text)460 void wxTextCtrl::AppendText(const wxString& text)
461 {
462     SetInsertionPointEnd();
463 
464     WriteText(text);
465 }
466 
Clear()467 void wxTextCtrl::Clear()
468 {
469     ::SetWindowText(GetBuddyHwnd(), wxEmptyString);
470 
471     // Windows already sends an update event for single-line
472     // controls.
473     if ( m_windowStyle & wxTE_MULTILINE )
474         SendUpdateEvent();
475 }
476 
477 // ----------------------------------------------------------------------------
478 // Clipboard operations
479 // ----------------------------------------------------------------------------
480 
Copy()481 void wxTextCtrl::Copy()
482 {
483     if (CanCopy())
484     {
485         ::SendMessage(GetBuddyHwnd(), WM_COPY, 0, 0L);
486     }
487 }
488 
Cut()489 void wxTextCtrl::Cut()
490 {
491     if (CanCut())
492     {
493         ::SendMessage(GetBuddyHwnd(), WM_CUT, 0, 0L);
494     }
495 }
496 
Paste()497 void wxTextCtrl::Paste()
498 {
499     if (CanPaste())
500     {
501         ::SendMessage(GetBuddyHwnd(), WM_PASTE, 0, 0L);
502     }
503 }
504 
HasSelection() const505 bool wxTextCtrl::HasSelection() const
506 {
507     long from, to;
508     GetSelection(&from, &to);
509     return from != to;
510 }
511 
CanCopy() const512 bool wxTextCtrl::CanCopy() const
513 {
514     // Can copy if there's a selection
515     return HasSelection();
516 }
517 
CanCut() const518 bool wxTextCtrl::CanCut() const
519 {
520     return CanCopy() && IsEditable();
521 }
522 
CanPaste() const523 bool wxTextCtrl::CanPaste() const
524 {
525     if ( !IsEditable() )
526         return false;
527 
528     // Standard edit control: check for straight text on clipboard
529     if ( !::OpenClipboard(GetHwndOf(wxTheApp->GetTopWindow())) )
530         return false;
531 
532     bool isTextAvailable = ::IsClipboardFormatAvailable(CF_TEXT) != 0;
533     ::CloseClipboard();
534 
535     return isTextAvailable;
536 }
537 
538 // ----------------------------------------------------------------------------
539 // Accessors
540 // ----------------------------------------------------------------------------
541 
SetEditable(bool editable)542 void wxTextCtrl::SetEditable(bool editable)
543 {
544     ::SendMessage(GetBuddyHwnd(), EM_SETREADONLY, (WPARAM)!editable, (LPARAM)0L);
545 }
546 
SetInsertionPoint(long pos)547 void wxTextCtrl::SetInsertionPoint(long pos)
548 {
549     DoSetSelection(pos, pos);
550 }
551 
SetInsertionPointEnd()552 void wxTextCtrl::SetInsertionPointEnd()
553 {
554     if ( GetInsertionPoint() != GetLastPosition() )
555         SetInsertionPoint(GetLastPosition());
556 }
557 
GetInsertionPoint() const558 long wxTextCtrl::GetInsertionPoint() const
559 {
560     DWORD Pos = (DWORD)::SendMessage(GetBuddyHwnd(), EM_GETSEL, 0, 0L);
561     return Pos & 0xFFFF;
562 }
563 
GetLastPosition() const564 wxTextPos wxTextCtrl::GetLastPosition() const
565 {
566     int numLines = GetNumberOfLines();
567     long posStartLastLine = XYToPosition(0, numLines - 1);
568 
569     long lenLastLine = GetLengthOfLineContainingPos(posStartLastLine);
570 
571     return posStartLastLine + lenLastLine;
572 }
573 
GetSelection(long * from,long * to) const574 void wxTextCtrl::GetSelection(long* from, long* to) const
575 {
576     DWORD dwStart, dwEnd;
577     ::SendMessage(GetBuddyHwnd(), EM_GETSEL, (WPARAM)&dwStart, (LPARAM)&dwEnd);
578 
579     *from = dwStart;
580     *to = dwEnd;
581 }
582 
IsEditable() const583 bool wxTextCtrl::IsEditable() const
584 {
585     if ( !GetBuddyHwnd() )
586         return true;
587 
588     long style = ::GetWindowLong(GetBuddyHwnd(), GWL_STYLE);
589 
590     return (style & ES_READONLY) == 0;
591 }
592 
593 // ----------------------------------------------------------------------------
594 // selection
595 // ----------------------------------------------------------------------------
596 
SetSelection(long from,long to)597 void wxTextCtrl::SetSelection(long from, long to)
598 {
599     // if from and to are both -1, it means (in wxWidgets) that all text should
600     // be selected - translate into Windows convention
601     if ( (from == -1) && (to == -1) )
602     {
603         from = 0;
604         to = -1;
605     }
606 
607     DoSetSelection(from, to);
608 }
609 
DoSetSelection(long from,long to,bool scrollCaret)610 void wxTextCtrl::DoSetSelection(long from, long to, bool scrollCaret)
611 {
612     ::SendMessage(GetBuddyHwnd(), EM_SETSEL, (WPARAM)from, (LPARAM)to);
613 
614     if ( scrollCaret )
615     {
616         ::SendMessage(GetBuddyHwnd(), EM_SCROLLCARET, (WPARAM)0, (LPARAM)0);
617     }
618 }
619 
620 // ----------------------------------------------------------------------------
621 // Working with files
622 // ----------------------------------------------------------------------------
623 
LoadFile(const wxString & file)624 bool wxTextCtrl::LoadFile(const wxString& file)
625 {
626     if ( wxTextCtrlBase::LoadFile(file) )
627     {
628         // update the size limit if needed
629         AdjustSpaceLimit();
630 
631         return true;
632     }
633 
634     return false;
635 }
636 
637 // ----------------------------------------------------------------------------
638 // Editing
639 // ----------------------------------------------------------------------------
640 
Replace(long from,long to,const wxString & value)641 void wxTextCtrl::Replace(long from, long to, const wxString& value)
642 {
643     // Set selection and remove it
644     DoSetSelection(from, to, false);
645 
646     DoWriteText(value, SetValue_SelectionOnly);
647 }
648 
Remove(long from,long to)649 void wxTextCtrl::Remove(long from, long to)
650 {
651     Replace(from, to, wxEmptyString);
652 }
653 
IsModified() const654 bool wxTextCtrl::IsModified() const
655 {
656     return ::SendMessage(GetBuddyHwnd(), EM_GETMODIFY, 0, 0) != 0;
657 }
658 
MarkDirty()659 void wxTextCtrl::MarkDirty()
660 {
661     ::SendMessage(GetBuddyHwnd(), EM_SETMODIFY, TRUE, 0L);
662 }
663 
DiscardEdits()664 void wxTextCtrl::DiscardEdits()
665 {
666     ::SendMessage(GetBuddyHwnd(), EM_SETMODIFY, FALSE, 0L);
667 }
668 
GetNumberOfLines() const669 int wxTextCtrl::GetNumberOfLines() const
670 {
671     return (int)::SendMessage(GetBuddyHwnd(), EM_GETLINECOUNT, 0, 0L);
672 }
673 
674 // ----------------------------------------------------------------------------
675 // Positions <-> coords
676 // ----------------------------------------------------------------------------
677 
XYToPosition(long x,long y) const678 long wxTextCtrl::XYToPosition(long x, long y) const
679 {
680     // This gets the char index for the _beginning_ of this line
681     long charIndex = ::SendMessage(GetBuddyHwnd(), EM_LINEINDEX, (WPARAM)y, (LPARAM)0);
682 
683     return charIndex + x;
684 }
685 
PositionToXY(long pos,long * x,long * y) const686 bool wxTextCtrl::PositionToXY(long pos, long *x, long *y) const
687 {
688     // This gets the line number containing the character
689     long lineNo = ::SendMessage(GetBuddyHwnd(), EM_LINEFROMCHAR, (WPARAM)pos, 0);
690 
691     if ( lineNo == -1 )
692     {
693         // no such line
694         return false;
695     }
696 
697     // This gets the char index for the _beginning_ of this line
698     long charIndex = ::SendMessage(GetBuddyHwnd(), EM_LINEINDEX, (WPARAM)lineNo, (LPARAM)0);
699     if ( charIndex == -1 )
700     {
701         return false;
702     }
703 
704     // The X position must therefore be the different between pos and charIndex
705     if ( x )
706         *x = pos - charIndex;
707     if ( y )
708         *y = lineNo;
709 
710     return true;
711 }
712 
713 wxTextCtrlHitTestResult
HitTest(const wxPoint & pt,long * posOut) const714 wxTextCtrl::HitTest(const wxPoint& pt, long *posOut) const
715 {
716     // first get the position from Windows
717     // for the plain ones, we are limited to 16 bit positions which are
718     // combined in a single 32 bit value
719     LPARAM lParam = MAKELPARAM(pt.x, pt.y);
720 
721     LRESULT pos = ::SendMessage(GetBuddyHwnd(), EM_CHARFROMPOS, 0, lParam);
722 
723     if ( pos == -1 )
724     {
725         // this seems to indicate an error...
726         return wxTE_HT_UNKNOWN;
727     }
728 
729     // for plain EDIT controls the higher word contains something else
730     pos = LOWORD(pos);
731 
732 
733     // next determine where it is relatively to our point: EM_CHARFROMPOS
734     // always returns the closest character but we need to be more precise, so
735     // double check that we really are where it pretends
736     POINTL ptReal;
737 
738     LRESULT lRc = ::SendMessage(GetBuddyHwnd(), EM_POSFROMCHAR, pos, 0);
739 
740     if ( lRc == -1 )
741     {
742         // this is apparently returned when pos corresponds to the last
743         // position
744         ptReal.x =
745         ptReal.y = 0;
746     }
747     else
748     {
749         ptReal.x = LOWORD(lRc);
750         ptReal.y = HIWORD(lRc);
751     }
752 
753     wxTextCtrlHitTestResult rc;
754 
755     if ( pt.y > ptReal.y + GetCharHeight() )
756         rc = wxTE_HT_BELOW;
757     else if ( pt.x > ptReal.x + GetCharWidth() )
758         rc = wxTE_HT_BEYOND;
759     else
760         rc = wxTE_HT_ON_TEXT;
761 
762     if ( posOut )
763         *posOut = pos;
764 
765     return rc;
766 }
767 
ShowPosition(long pos)768 void wxTextCtrl::ShowPosition(long pos)
769 {
770     int currentLineLineNo = (int)::SendMessage(GetBuddyHwnd(), EM_GETFIRSTVISIBLELINE, 0, 0L);
771 
772     int specifiedLineLineNo = (int)::SendMessage(GetBuddyHwnd(), EM_LINEFROMCHAR, (WPARAM)pos, 0L);
773 
774     int linesToScroll = specifiedLineLineNo - currentLineLineNo;
775 
776     if (linesToScroll != 0)
777         (void)::SendMessage(GetBuddyHwnd(), EM_LINESCROLL, 0, (LPARAM)linesToScroll);
778 }
779 
GetLengthOfLineContainingPos(long pos) const780 long wxTextCtrl::GetLengthOfLineContainingPos(long pos) const
781 {
782     return ::SendMessage(GetBuddyHwnd(), EM_LINELENGTH, (WPARAM)pos, 0L);
783 }
784 
GetLineLength(long lineNo) const785 int wxTextCtrl::GetLineLength(long lineNo) const
786 {
787     long pos = XYToPosition(0, lineNo);
788 
789     return GetLengthOfLineContainingPos(pos);
790 }
791 
GetLineText(long lineNo) const792 wxString wxTextCtrl::GetLineText(long lineNo) const
793 {
794     size_t len = (size_t)GetLineLength(lineNo) + 1;
795 
796     // there must be at least enough place for the length WORD in the
797     // buffer
798     len += sizeof(WORD);
799 
800     wxString str;
801     {
802         wxStringBufferLength tmp(str, len);
803         wxChar *buf = tmp;
804 
805         *(WORD *)buf = (WORD)len;
806         len = (size_t)::SendMessage(GetBuddyHwnd(), EM_GETLINE, lineNo, (LPARAM)buf);
807 
808         // remove the '\n' at the end, if any (this is how this function is
809         // supposed to work according to the docs)
810         if ( buf[len - 1] == _T('\n') )
811         {
812             len--;
813         }
814 
815         buf[len] = 0;
816         tmp.SetLength(len);
817     }
818 
819     return str;
820 }
821 
SetMaxLength(unsigned long len)822 void wxTextCtrl::SetMaxLength(unsigned long len)
823 {
824     ::SendMessage(GetBuddyHwnd(), EM_LIMITTEXT, len, 0);
825 }
826 
827 // ----------------------------------------------------------------------------
828 // Undo/redo
829 // ----------------------------------------------------------------------------
830 
Undo()831 void wxTextCtrl::Undo()
832 {
833     if (CanUndo())
834     {
835         ::SendMessage(GetBuddyHwnd(), EM_UNDO, 0, 0);
836     }
837 }
838 
Redo()839 void wxTextCtrl::Redo()
840 {
841     if (CanRedo())
842     {
843         ::SendMessage(GetBuddyHwnd(), EM_UNDO, 0, 0);
844     }
845 }
846 
CanUndo() const847 bool wxTextCtrl::CanUndo() const
848 {
849     return ::SendMessage(GetBuddyHwnd(), EM_CANUNDO, 0, 0) != 0;
850 }
851 
CanRedo() const852 bool wxTextCtrl::CanRedo() const
853 {
854     return ::SendMessage(GetBuddyHwnd(), EM_CANUNDO, 0, 0) != 0;
855 }
856 
857 // ----------------------------------------------------------------------------
858 // caret handling
859 // ----------------------------------------------------------------------------
860 
861 // ----------------------------------------------------------------------------
862 // implemenation details
863 // ----------------------------------------------------------------------------
864 
Command(wxCommandEvent & event)865 void wxTextCtrl::Command(wxCommandEvent & event)
866 {
867     SetValue(event.GetString());
868     ProcessCommand (event);
869 }
870 
871 // ----------------------------------------------------------------------------
872 // kbd input processing
873 // ----------------------------------------------------------------------------
874 
OnChar(wxKeyEvent & event)875 void wxTextCtrl::OnChar(wxKeyEvent& event)
876 {
877     switch ( event.GetKeyCode() )
878     {
879         case WXK_RETURN:
880             if ( !HasFlag(wxTE_MULTILINE) )
881             {
882                 wxCommandEvent event(wxEVT_COMMAND_TEXT_ENTER, m_windowId);
883                 InitCommandEvent(event);
884                 event.SetString(GetValue());
885                 if ( GetEventHandler()->ProcessEvent(event) )
886                     return;
887             }
888             //else: multiline controls need Enter for themselves
889 
890             break;
891 
892         case WXK_TAB:
893             // ok, so this is getting absolutely ridiculous but I don't see
894             // any other way to fix this bug: when a multiline text control is
895             // inside a wxFrame, we need to generate the navigation event as
896             // otherwise nothing happens at all, but when the same control is
897             // created inside a dialog, IsDialogMessage() *does* switch focus
898             // all by itself and so if we do it here as well, it is advanced
899             // twice and goes to the next control... to prevent this from
900             // happening we're doing this ugly check, the logic being that if
901             // we don't have focus then it had been already changed to the next
902             // control
903             //
904             // the right thing to do would, of course, be to understand what
905             // the hell is IsDialogMessage() doing but this is beyond my feeble
906             // forces at the moment unfortunately
907             if ( !(m_windowStyle & wxTE_PROCESS_TAB))
908             {
909                 if ( FindFocus() == this )
910                 {
911                     int flags = 0;
912                     if (!event.ShiftDown())
913                         flags |= wxNavigationKeyEvent::IsForward ;
914                     if (event.ControlDown())
915                         flags |= wxNavigationKeyEvent::WinChange ;
916                     if (Navigate(flags))
917                         return;
918                 }
919             }
920             else
921             {
922                 // Insert tab since calling the default Windows handler
923                 // doesn't seem to do it
924                 WriteText(wxT("\t"));
925             }
926             break;
927     }
928 
929     // no, we didn't process it
930     event.Skip();
931 }
932 
MSWWindowProc(WXUINT nMsg,WXWPARAM wParam,WXLPARAM lParam)933 WXLRESULT wxTextCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
934 {
935     WXLRESULT lRc = wxTextCtrlBase::MSWWindowProc(nMsg, wParam, lParam);
936 
937     if ( nMsg == WM_GETDLGCODE )
938     {
939         // we always want the chars and the arrows: the arrows for navigation
940         // and the chars because we want Ctrl-C to work even in a read only
941         // control
942         long lDlgCode = DLGC_WANTCHARS | DLGC_WANTARROWS;
943 
944         if ( IsEditable() )
945         {
946             // we may have several different cases:
947             // 1. normal case: both TAB and ENTER are used for dlg navigation
948             // 2. ctrl which wants TAB for itself: ENTER is used to pass to the
949             //    next control in the dialog
950             // 3. ctrl which wants ENTER for itself: TAB is used for dialog
951             //    navigation
952             // 4. ctrl which wants both TAB and ENTER: Ctrl-ENTER is used to go
953             //    to the next control
954 
955             // the multiline edit control should always get <Return> for itself
956             if ( HasFlag(wxTE_PROCESS_ENTER) || HasFlag(wxTE_MULTILINE) )
957                 lDlgCode |= DLGC_WANTMESSAGE;
958 
959             if ( HasFlag(wxTE_PROCESS_TAB) )
960                 lDlgCode |= DLGC_WANTTAB;
961 
962             lRc |= lDlgCode;
963         }
964         else // !editable
965         {
966             // NB: use "=", not "|=" as the base class version returns the
967             //     same flags is this state as usual (i.e. including
968             //     DLGC_WANTMESSAGE). This is strange (how does it work in the
969             //     native Win32 apps?) but for now live with it.
970             lRc = lDlgCode;
971         }
972     }
973 
974     return lRc;
975 }
976 
977 // ----------------------------------------------------------------------------
978 // text control event processing
979 // ----------------------------------------------------------------------------
980 
SendUpdateEvent()981 bool wxTextCtrl::SendUpdateEvent()
982 {
983     // is event reporting suspended?
984     if ( m_suppressNextUpdate )
985     {
986         // do process the next one
987         m_suppressNextUpdate = false;
988 
989         return false;
990     }
991 
992     wxCommandEvent event(wxEVT_COMMAND_TEXT_UPDATED, GetId());
993     InitCommandEvent(event);
994     event.SetString(GetValue());
995 
996     return ProcessCommand(event);
997 }
998 
MSWCommand(WXUINT param,WXWORD WXUNUSED (id))999 bool wxTextCtrl::MSWCommand(WXUINT param, WXWORD WXUNUSED(id))
1000 {
1001     switch ( param )
1002     {
1003         case EN_SETFOCUS:
1004         case EN_KILLFOCUS:
1005             {
1006                 wxFocusEvent event(param == EN_KILLFOCUS ? wxEVT_KILL_FOCUS
1007                                                          : wxEVT_SET_FOCUS,
1008                                    m_windowId);
1009                 event.SetEventObject(this);
1010                 GetEventHandler()->ProcessEvent(event);
1011             }
1012             break;
1013 
1014         case EN_CHANGE:
1015             SendUpdateEvent();
1016             break;
1017 
1018         case EN_MAXTEXT:
1019             // the text size limit has been hit -- try to increase it
1020             if ( !AdjustSpaceLimit() )
1021             {
1022                 wxCommandEvent event(wxEVT_COMMAND_TEXT_MAXLEN, m_windowId);
1023                 InitCommandEvent(event);
1024                 event.SetString(GetValue());
1025                 ProcessCommand(event);
1026             }
1027             break;
1028 
1029             // the other edit notification messages are not processed
1030         default:
1031             return false;
1032     }
1033 
1034     // processed
1035     return true;
1036 }
1037 
AdjustSpaceLimit()1038 bool wxTextCtrl::AdjustSpaceLimit()
1039 {
1040     unsigned int limit = ::SendMessage(GetBuddyHwnd(), EM_GETLIMITTEXT, 0, 0);
1041 
1042     // HACK: we try to automatically extend the limit for the amount of text
1043     //       to allow (interactively) entering more than 64Kb of text under
1044     //       Win9x but we shouldn't reset the text limit which was previously
1045     //       set explicitly with SetMaxLength()
1046     //
1047     //       we could solve this by storing the limit we set in wxTextCtrl but
1048     //       to save space we prefer to simply test here the actual limit
1049     //       value: we consider that SetMaxLength() can only be called for
1050     //       values < 32Kb
1051     if ( limit < 0x8000 )
1052     {
1053         // we've got more text than limit set by SetMaxLength()
1054         return false;
1055     }
1056 
1057     unsigned int len = ::GetWindowTextLength(GetBuddyHwnd());
1058     if ( len >= limit )
1059     {
1060         limit = len + 0x8000;    // 32Kb
1061 
1062         if ( limit > 0xffff )
1063         {
1064             // this will set it to a platform-dependent maximum (much more
1065             // than 64Kb under NT)
1066             limit = 0;
1067         }
1068 
1069         ::SendMessage(GetBuddyHwnd(), EM_LIMITTEXT, limit, 0L);
1070     }
1071 
1072     // we changed the limit
1073     return true;
1074 }
1075 
AcceptsFocus() const1076 bool wxTextCtrl::AcceptsFocus() const
1077 {
1078     // we don't want focus if we can't be edited unless we're a multiline
1079     // control because then it might be still nice to get focus from keyboard
1080     // to be able to scroll it without mouse
1081     return (IsEditable() || IsMultiLine()) && wxControl::AcceptsFocus();
1082 }
1083 
DoMoveWindow(int x,int y,int width,int height)1084 void wxTextCtrl::DoMoveWindow(int x, int y, int width, int height)
1085 {
1086     int widthBtn = GetBestSpinnerSize(IsVertical(GetWindowStyle())).x / 2;
1087     int widthText = width - widthBtn - MARGIN_BETWEEN;
1088     if ( widthText <= 0 )
1089     {
1090         wxLogDebug(_T("not enough space for wxSpinCtrl!"));
1091     }
1092 
1093     if ( !::MoveWindow(GetBuddyHwnd(), x, y, widthText, height, TRUE) )
1094     {
1095         wxLogLastError(wxT("MoveWindow(buddy)"));
1096     }
1097 
1098     x += widthText + MARGIN_BETWEEN;
1099     if ( !::MoveWindow(GetHwnd(), x, y, widthBtn, height, TRUE) )
1100     {
1101         wxLogLastError(wxT("MoveWindow"));
1102     }
1103 }
1104 
DoGetBestSize() const1105 wxSize wxTextCtrl::DoGetBestSize() const
1106 {
1107     int cx, cy;
1108     wxGetCharSize(GetBuddyHwnd(), &cx, &cy, GetFont());
1109 
1110     int wText = DEFAULT_ITEM_WIDTH;
1111 
1112     int hText = cy;
1113     if ( m_windowStyle & wxTE_MULTILINE )
1114     {
1115         hText *= wxMax(GetNumberOfLines(), 5);
1116     }
1117     //else: for single line control everything is ok
1118 
1119     // we have to add the adjustments for the control height only once, not
1120     // once per line, so do it after multiplication above
1121     hText += EDIT_HEIGHT_FROM_CHAR_HEIGHT(cy) - cy;
1122 
1123     return wxSize(wText, hText);
1124 }
1125 
1126 // ----------------------------------------------------------------------------
1127 // standard handlers for standard edit menu events
1128 // ----------------------------------------------------------------------------
1129 
OnCut(wxCommandEvent & WXUNUSED (event))1130 void wxTextCtrl::OnCut(wxCommandEvent& WXUNUSED(event))
1131 {
1132     Cut();
1133 }
1134 
OnCopy(wxCommandEvent & WXUNUSED (event))1135 void wxTextCtrl::OnCopy(wxCommandEvent& WXUNUSED(event))
1136 {
1137     Copy();
1138 }
1139 
OnPaste(wxCommandEvent & WXUNUSED (event))1140 void wxTextCtrl::OnPaste(wxCommandEvent& WXUNUSED(event))
1141 {
1142     Paste();
1143 }
1144 
OnUndo(wxCommandEvent & WXUNUSED (event))1145 void wxTextCtrl::OnUndo(wxCommandEvent& WXUNUSED(event))
1146 {
1147     Undo();
1148 }
1149 
OnRedo(wxCommandEvent & WXUNUSED (event))1150 void wxTextCtrl::OnRedo(wxCommandEvent& WXUNUSED(event))
1151 {
1152     Redo();
1153 }
1154 
OnDelete(wxCommandEvent & WXUNUSED (event))1155 void wxTextCtrl::OnDelete(wxCommandEvent& WXUNUSED(event))
1156 {
1157     long from, to;
1158     GetSelection(& from, & to);
1159     if (from != -1 && to != -1)
1160         Remove(from, to);
1161 }
1162 
OnSelectAll(wxCommandEvent & WXUNUSED (event))1163 void wxTextCtrl::OnSelectAll(wxCommandEvent& WXUNUSED(event))
1164 {
1165     SetSelection(-1, -1);
1166 }
1167 
OnUpdateCut(wxUpdateUIEvent & event)1168 void wxTextCtrl::OnUpdateCut(wxUpdateUIEvent& event)
1169 {
1170     event.Enable( CanCut() );
1171 }
1172 
OnUpdateCopy(wxUpdateUIEvent & event)1173 void wxTextCtrl::OnUpdateCopy(wxUpdateUIEvent& event)
1174 {
1175     event.Enable( CanCopy() );
1176 }
1177 
OnUpdatePaste(wxUpdateUIEvent & event)1178 void wxTextCtrl::OnUpdatePaste(wxUpdateUIEvent& event)
1179 {
1180     event.Enable( CanPaste() );
1181 }
1182 
OnUpdateUndo(wxUpdateUIEvent & event)1183 void wxTextCtrl::OnUpdateUndo(wxUpdateUIEvent& event)
1184 {
1185     event.Enable( CanUndo() );
1186 }
1187 
OnUpdateRedo(wxUpdateUIEvent & event)1188 void wxTextCtrl::OnUpdateRedo(wxUpdateUIEvent& event)
1189 {
1190     event.Enable( CanRedo() );
1191 }
1192 
OnUpdateDelete(wxUpdateUIEvent & event)1193 void wxTextCtrl::OnUpdateDelete(wxUpdateUIEvent& event)
1194 {
1195     long from, to;
1196     GetSelection(& from, & to);
1197     event.Enable(from != -1 && to != -1 && from != to && IsEditable()) ;
1198 }
1199 
OnUpdateSelectAll(wxUpdateUIEvent & event)1200 void wxTextCtrl::OnUpdateSelectAll(wxUpdateUIEvent& event)
1201 {
1202     event.Enable(GetLastPosition() > 0);
1203 }
1204 
OnSetFocus(wxFocusEvent & WXUNUSED (event))1205 void wxTextCtrl::OnSetFocus(wxFocusEvent& WXUNUSED(event))
1206 {
1207     // be sure the caret remains invisible if the user had hidden it
1208     if ( !m_isNativeCaretShown )
1209     {
1210         ::HideCaret(GetBuddyHwnd());
1211     }
1212 }
1213 
1214 #endif // wxUSE_TEXTCTRL && __SMARTPHONE__ && __WXWINCE__
1215