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
49IMPLEMENT_DYNAMIC_CLASS(wxTextCtrl, wxControl)
50#endif
51
52BEGIN_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)
72END_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?)
81static const int MARGIN_BETWEEN = 0;
82
83// ============================================================================
84// implementation
85// ============================================================================
86
87wxArrayTextSpins wxTextCtrl::ms_allTextSpins;
88
89// ----------------------------------------------------------------------------
90// wnd proc for the buddy text ctrl
91// ----------------------------------------------------------------------------
92
93LRESULT 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
136void wxTextCtrl::Init()
137{
138    m_suppressNextUpdate = false;
139    m_isNativeCaretShown = true;
140}
141
142wxTextCtrl::~wxTextCtrl()
143{
144}
145
146bool 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)
274void 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
294WXDWORD 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
363wxString 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
369wxString 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
396void 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
423void wxTextCtrl::WriteText(const wxString& value)
424{
425    DoWriteText(value);
426}
427
428void 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
460void wxTextCtrl::AppendText(const wxString& text)
461{
462    SetInsertionPointEnd();
463
464    WriteText(text);
465}
466
467void 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
481void wxTextCtrl::Copy()
482{
483    if (CanCopy())
484    {
485        ::SendMessage(GetBuddyHwnd(), WM_COPY, 0, 0L);
486    }
487}
488
489void wxTextCtrl::Cut()
490{
491    if (CanCut())
492    {
493        ::SendMessage(GetBuddyHwnd(), WM_CUT, 0, 0L);
494    }
495}
496
497void wxTextCtrl::Paste()
498{
499    if (CanPaste())
500    {
501        ::SendMessage(GetBuddyHwnd(), WM_PASTE, 0, 0L);
502    }
503}
504
505bool wxTextCtrl::HasSelection() const
506{
507    long from, to;
508    GetSelection(&from, &to);
509    return from != to;
510}
511
512bool wxTextCtrl::CanCopy() const
513{
514    // Can copy if there's a selection
515    return HasSelection();
516}
517
518bool wxTextCtrl::CanCut() const
519{
520    return CanCopy() && IsEditable();
521}
522
523bool 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
542void wxTextCtrl::SetEditable(bool editable)
543{
544    ::SendMessage(GetBuddyHwnd(), EM_SETREADONLY, (WPARAM)!editable, (LPARAM)0L);
545}
546
547void wxTextCtrl::SetInsertionPoint(long pos)
548{
549    DoSetSelection(pos, pos);
550}
551
552void wxTextCtrl::SetInsertionPointEnd()
553{
554    if ( GetInsertionPoint() != GetLastPosition() )
555        SetInsertionPoint(GetLastPosition());
556}
557
558long wxTextCtrl::GetInsertionPoint() const
559{
560    DWORD Pos = (DWORD)::SendMessage(GetBuddyHwnd(), EM_GETSEL, 0, 0L);
561    return Pos & 0xFFFF;
562}
563
564wxTextPos 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
574void 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
583bool 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
597void 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
610void 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
624bool 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
641void 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
649void wxTextCtrl::Remove(long from, long to)
650{
651    Replace(from, to, wxEmptyString);
652}
653
654bool wxTextCtrl::IsModified() const
655{
656    return ::SendMessage(GetBuddyHwnd(), EM_GETMODIFY, 0, 0) != 0;
657}
658
659void wxTextCtrl::MarkDirty()
660{
661    ::SendMessage(GetBuddyHwnd(), EM_SETMODIFY, TRUE, 0L);
662}
663
664void wxTextCtrl::DiscardEdits()
665{
666    ::SendMessage(GetBuddyHwnd(), EM_SETMODIFY, FALSE, 0L);
667}
668
669int wxTextCtrl::GetNumberOfLines() const
670{
671    return (int)::SendMessage(GetBuddyHwnd(), EM_GETLINECOUNT, 0, 0L);
672}
673
674// ----------------------------------------------------------------------------
675// Positions <-> coords
676// ----------------------------------------------------------------------------
677
678long 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
686bool 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
713wxTextCtrlHitTestResult
714wxTextCtrl::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
768void 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
780long wxTextCtrl::GetLengthOfLineContainingPos(long pos) const
781{
782    return ::SendMessage(GetBuddyHwnd(), EM_LINELENGTH, (WPARAM)pos, 0L);
783}
784
785int wxTextCtrl::GetLineLength(long lineNo) const
786{
787    long pos = XYToPosition(0, lineNo);
788
789    return GetLengthOfLineContainingPos(pos);
790}
791
792wxString 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
822void wxTextCtrl::SetMaxLength(unsigned long len)
823{
824    ::SendMessage(GetBuddyHwnd(), EM_LIMITTEXT, len, 0);
825}
826
827// ----------------------------------------------------------------------------
828// Undo/redo
829// ----------------------------------------------------------------------------
830
831void wxTextCtrl::Undo()
832{
833    if (CanUndo())
834    {
835        ::SendMessage(GetBuddyHwnd(), EM_UNDO, 0, 0);
836    }
837}
838
839void wxTextCtrl::Redo()
840{
841    if (CanRedo())
842    {
843        ::SendMessage(GetBuddyHwnd(), EM_UNDO, 0, 0);
844    }
845}
846
847bool wxTextCtrl::CanUndo() const
848{
849    return ::SendMessage(GetBuddyHwnd(), EM_CANUNDO, 0, 0) != 0;
850}
851
852bool 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
865void wxTextCtrl::Command(wxCommandEvent & event)
866{
867    SetValue(event.GetString());
868    ProcessCommand (event);
869}
870
871// ----------------------------------------------------------------------------
872// kbd input processing
873// ----------------------------------------------------------------------------
874
875void 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
933WXLRESULT 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
981bool 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
999bool 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
1038bool 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
1076bool 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
1084void 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
1105wxSize 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
1130void wxTextCtrl::OnCut(wxCommandEvent& WXUNUSED(event))
1131{
1132    Cut();
1133}
1134
1135void wxTextCtrl::OnCopy(wxCommandEvent& WXUNUSED(event))
1136{
1137    Copy();
1138}
1139
1140void wxTextCtrl::OnPaste(wxCommandEvent& WXUNUSED(event))
1141{
1142    Paste();
1143}
1144
1145void wxTextCtrl::OnUndo(wxCommandEvent& WXUNUSED(event))
1146{
1147    Undo();
1148}
1149
1150void wxTextCtrl::OnRedo(wxCommandEvent& WXUNUSED(event))
1151{
1152    Redo();
1153}
1154
1155void 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
1163void wxTextCtrl::OnSelectAll(wxCommandEvent& WXUNUSED(event))
1164{
1165    SetSelection(-1, -1);
1166}
1167
1168void wxTextCtrl::OnUpdateCut(wxUpdateUIEvent& event)
1169{
1170    event.Enable( CanCut() );
1171}
1172
1173void wxTextCtrl::OnUpdateCopy(wxUpdateUIEvent& event)
1174{
1175    event.Enable( CanCopy() );
1176}
1177
1178void wxTextCtrl::OnUpdatePaste(wxUpdateUIEvent& event)
1179{
1180    event.Enable( CanPaste() );
1181}
1182
1183void wxTextCtrl::OnUpdateUndo(wxUpdateUIEvent& event)
1184{
1185    event.Enable( CanUndo() );
1186}
1187
1188void wxTextCtrl::OnUpdateRedo(wxUpdateUIEvent& event)
1189{
1190    event.Enable( CanRedo() );
1191}
1192
1193void 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
1200void wxTextCtrl::OnUpdateSelectAll(wxUpdateUIEvent& event)
1201{
1202    event.Enable(GetLastPosition() > 0);
1203}
1204
1205void 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