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