1 ///////////////////////////////////////////////////////////////////////////// 
   4 // Author:      Julian Smart 
   8 // Copyright:   (c) Julian Smart 
   9 // Licence:     wxWindows licence 
  10 ///////////////////////////////////////////////////////////////////////////// 
  13 #pragma implementation "textctrl.h" 
  16 #include <sys/types.h> 
  20 #include "wx/textctrl.h" 
  21 #include "wx/settings.h" 
  22 #include "wx/filefn.h" 
  25 #if defined(__BORLANDC__) && !defined(__WIN32__) 
  34 #include <sys/types.h> 
  38 #include "wx/motif/private.h" 
  41 wxTextWindowChangedProc (Widget w
, XtPointer clientData
, XtPointer ptr
); 
  43 wxTextWindowModifyProc (Widget w
, XtPointer clientData
, XmTextVerifyCallbackStruct 
*cbs
); 
  45 wxTextWindowGainFocusProc (Widget w
, XtPointer clientData
, XmAnyCallbackStruct 
*cbs
); 
  47 wxTextWindowLoseFocusProc (Widget w
, XtPointer clientData
, XmAnyCallbackStruct 
*cbs
); 
  48 static void wxTextWindowActivateProc(Widget w
, XtPointer clientData
, 
  49                       XmAnyCallbackStruct 
*ptr
); 
  51 #if !USE_SHARED_LIBRARY 
  52 IMPLEMENT_DYNAMIC_CLASS(wxTextCtrl
, wxControl
) 
  54 BEGIN_EVENT_TABLE(wxTextCtrl
, wxControl
) 
  55         EVT_DROP_FILES(wxTextCtrl::OnDropFiles
) 
  56         EVT_CHAR(wxTextCtrl::OnChar
) 
  61 wxTextCtrl::wxTextCtrl() 
  62 #ifndef NO_TEXT_WINDOW_STREAM 
  67     m_tempCallbackStruct 
= (void*) NULL
; 
  69     m_processedDefault 
= FALSE
; 
  72 bool wxTextCtrl::Create(wxWindow 
*parent
, wxWindowID id
, 
  73                    const wxString
& value
, 
  75            const wxSize
& size
, long style
, 
  76            const wxValidator
& validator
, 
  79     m_tempCallbackStruct 
= (void*) NULL
; 
  81     m_processedDefault 
= FALSE
; 
  83     m_backgroundColour 
= parent
->GetBackgroundColour(); 
  84     m_foregroundColour 
= parent
->GetForegroundColour(); 
  87     SetValidator(validator
); 
  88     if (parent
) parent
->AddChild(this); 
  90     m_windowStyle 
= style
; 
  93             m_windowId 
= (int)NewControlId(); 
  97     Widget parentWidget 
= (Widget
) parent
->GetClientWidget(); 
  99     bool wantHorizScrolling 
= ((m_windowStyle 
& wxHSCROLL
) != 0); 
 101     // If we don't have horizontal scrollbars, we want word wrap. 
 102     bool wantWordWrap 
= !wantHorizScrolling
; 
 104     if (m_windowStyle 
& wxTE_MULTILINE
) 
 107         XtSetArg (args
[0], XmNscrollHorizontal
, wantHorizScrolling 
? True 
: False
); 
 108         XtSetArg (args
[1], XmNwordWrap
, wantWordWrap 
? True 
: False
); 
 110         m_mainWidget 
= (WXWidget
) XmCreateScrolledText (parentWidget
, (char*) (const char*) name
, args
, 2); 
 112         XtVaSetValues ((Widget
) m_mainWidget
, 
 113                  XmNeditable
, ((style 
& wxTE_READONLY
) ? False 
: True
), 
 114                  XmNeditMode
, XmMULTI_LINE_EDIT
, 
 116         XtManageChild ((Widget
) m_mainWidget
); 
 120         m_mainWidget 
= (WXWidget
) XtVaCreateManagedWidget ((char*) (const char*) name
, 
 121                  xmTextWidgetClass
, parentWidget
, 
 124         // TODO: Is this relevant? What does it do? 
 126         if (!value
.IsNull() && (value
.Length() > (unsigned int) noCols
)) 
 127             noCols 
= value
.Length(); 
 128         XtVaSetValues ((Widget
) m_mainWidget
, 
 134         XmTextSetString ((Widget
) m_mainWidget
, (char*) (const char*) value
); 
 136     XtAddCallback((Widget
) m_mainWidget
, XmNvalueChangedCallback
, (XtCallbackProc
)wxTextWindowChangedProc
, (XtPointer
)this); 
 138     XtAddCallback((Widget
) m_mainWidget
, XmNmodifyVerifyCallback
, (XtCallbackProc
)wxTextWindowModifyProc
, (XtPointer
)this); 
 140     XtAddCallback((Widget
) m_mainWidget
, XmNactivateCallback
, (XtCallbackProc
)wxTextWindowActivateProc
, (XtPointer
)this); 
 142     XtAddCallback((Widget
) m_mainWidget
, XmNfocusCallback
, (XtCallbackProc
)wxTextWindowGainFocusProc
, (XtPointer
)this); 
 144     XtAddCallback((Widget
) m_mainWidget
, XmNlosingFocusCallback
, (XtCallbackProc
)wxTextWindowLoseFocusProc
, (XtPointer
)this); 
 146     SetCanAddEventHandler(TRUE
); 
 147     AttachWidget (parent
, m_mainWidget
, (WXWidget
) NULL
, pos
.x
, pos
.y
, size
.x
, size
.y
); 
 149     SetFont(* parent
->GetFont()); 
 150     ChangeBackgroundColour(); 
 155 WXWidget 
wxTextCtrl::GetTopWidget() const 
 157     return ((m_windowStyle 
& wxTE_MULTILINE
) ? (WXWidget
) XtParent((Widget
) m_mainWidget
) : m_mainWidget
); 
 160 wxString 
wxTextCtrl::GetValue() const 
 162     if (m_windowStyle 
& wxTE_PASSWORD
) 
 166         char *s 
= XmTextGetString ((Widget
) m_mainWidget
); 
 175             return wxEmptyString
; 
 180 void wxTextCtrl::SetValue(const wxString
& value
) 
 182     wxASSERT_MSG( (!value
.IsNull()), "Must not pass a null string to wxTextCtrl::SetValue." ) ; 
 185     XmTextSetString ((Widget
) m_mainWidget
, (char*) (const char*) value
); 
 187     m_inSetValue 
= FALSE
; 
 190 // Clipboard operations 
 191 void wxTextCtrl::Copy() 
 193     XmTextCopy((Widget
) m_mainWidget
, CurrentTime
); 
 196 void wxTextCtrl::Cut() 
 198     XmTextCut((Widget
) m_mainWidget
, CurrentTime
); 
 201 void wxTextCtrl::Paste() 
 203     XmTextPaste((Widget
) m_mainWidget
); 
 206 void wxTextCtrl::SetEditable(bool editable
) 
 208     XmTextSetEditable((Widget
) m_mainWidget
, (Boolean
) editable
); 
 211 void wxTextCtrl::SetInsertionPoint(long pos
) 
 213     XmTextSetInsertionPosition ((Widget
) m_mainWidget
, (XmTextPosition
) pos
); 
 216 void wxTextCtrl::SetInsertionPointEnd() 
 218     long pos 
= GetLastPosition(); 
 219     SetInsertionPoint(pos
); 
 222 long wxTextCtrl::GetInsertionPoint() const 
 224     return (long) XmTextGetInsertionPosition ((Widget
) m_mainWidget
); 
 227 long wxTextCtrl::GetLastPosition() const 
 229     return (long) XmTextGetLastPosition ((Widget
) m_mainWidget
); 
 232 void wxTextCtrl::Replace(long from
, long to
, const wxString
& value
) 
 234     XmTextReplace ((Widget
) m_mainWidget
, (XmTextPosition
) from
, (XmTextPosition
) to
, 
 235                  (char*) (const char*) value
); 
 238 void wxTextCtrl::Remove(long from
, long to
) 
 240     XmTextSetSelection ((Widget
) m_mainWidget
, (XmTextPosition
) from
, (XmTextPosition
) to
, 
 242     XmTextRemove ((Widget
) m_mainWidget
); 
 245 void wxTextCtrl::SetSelection(long from
, long to
) 
 247     XmTextSetSelection ((Widget
) m_mainWidget
, (XmTextPosition
) from
, (XmTextPosition
) to
, 
 251 bool wxTextCtrl::LoadFile(const wxString
& file
) 
 253     if (!wxFileExists(file
)) 
 260     Widget textWidget 
= (Widget
) m_mainWidget
; 
 264     if ((stat ((char*) (const char*) file
, &statb
) == -1) || (statb
.st_mode 
& S_IFMT
) != S_IFREG 
|| 
 265       !(fp 
= fopen ((char*) (const char*) file
, "r"))) 
 271       long len 
= statb
.st_size
; 
 273       if (!(text 
= XtMalloc ((unsigned) (len 
+ 1)))) 
 278       if (fread (text
, sizeof (char), len
, fp
) != (size_t) len
) 
 284       XmTextSetString (textWidget
, text
); 
 285       //      m_textPosition = len; 
 292 // If file is null, try saved file name first 
 293 // Returns TRUE if succeeds. 
 294 bool wxTextCtrl::SaveFile(const wxString
& file
) 
 296     wxString 
theFile(file
); 
 298         theFile 
= m_fileName
; 
 301     m_fileName 
= theFile
; 
 303   Widget textWidget 
= (Widget
) m_mainWidget
; 
 306   if (!(fp 
= fopen ((char*) (const char*) theFile
, "w"))) 
 312       char *text 
= XmTextGetString (textWidget
); 
 313       long len 
= XmTextGetLastPosition (textWidget
); 
 315       if (fwrite (text
, sizeof (char), len
, fp
) != (size_t) len
) 
 317           // Did not write whole file 
 319       // Make sure newline terminates the file 
 320       if (text
[len 
- 1] != '\n') 
 330 void wxTextCtrl::WriteText(const wxString
& text
) 
 332     long textPosition 
= GetInsertionPoint() + strlen (text
); 
 333     XmTextInsert ((Widget
) m_mainWidget
, GetInsertionPoint(), (char*) (const char*) text
); 
 334     XtVaSetValues ((Widget
) m_mainWidget
, XmNcursorPosition
, textPosition
, NULL
); 
 335     SetInsertionPoint(textPosition
); 
 336     XmTextShowPosition ((Widget
) m_mainWidget
, textPosition
); 
 340 void wxTextCtrl::Clear() 
 342     XmTextSetString ((Widget
) m_mainWidget
, ""); 
 346 bool wxTextCtrl::IsModified() const 
 351 // Makes 'unmodified' 
 352 void wxTextCtrl::DiscardEdits() 
 354     XmTextSetString ((Widget
) m_mainWidget
, ""); 
 358 int wxTextCtrl::GetNumberOfLines() const 
 360     // HIDEOUSLY inefficient, but we have no choice. 
 361     char *s 
= XmTextGetString ((Widget
) m_mainWidget
); 
 366       bool finished 
= FALSE
; 
 389 long wxTextCtrl::XYToPosition(long x
, long y
) const 
 391 /* It seems, that there is a bug in some versions of the Motif library, 
 392    so the original wxWin-Code doesn't work. */ 
 394   Widget textWidget = (Widget) handle; 
 395   return (long) XmTextXYToPos (textWidget, (Position) x, (Position) y); 
 397     /* Now a little workaround: */ 
 399     for (int i
=0; i
<y
; i
++) r
+=(GetLineLength(i
)+1); 
 403 void wxTextCtrl::PositionToXY(long pos
, long *x
, long *y
) const 
 406     XmTextPosToXY((Widget
) m_mainWidget
, pos
, &xx
, &yy
); 
 410 void wxTextCtrl::ShowPosition(long pos
) 
 412     XmTextShowPosition ((Widget
) m_mainWidget
, (XmTextPosition
) pos
); 
 415 int wxTextCtrl::GetLineLength(long lineNo
) const 
 417     wxString str 
= GetLineText (lineNo
); 
 418     return (int) str
.Length(); 
 421 wxString 
wxTextCtrl::GetLineText(long lineNo
) const 
 423     // HIDEOUSLY inefficient, but we have no choice. 
 424     char *s 
= XmTextGetString ((Widget
) m_mainWidget
); 
 431         for (i 
= 0; currentLine 
!= lineNo 
&& s
[i
]; i
++ ) 
 436         for (j 
= 0; s
[i
] && s
[i
] != '\n'; i
++, j
++ ) 
 443     return wxEmptyString
; 
 450 void wxTextCtrl::Command(wxCommandEvent 
& event
) 
 452     SetValue (event
.GetString()); 
 453     ProcessCommand (event
); 
 456 void wxTextCtrl::OnDropFiles(wxDropFilesEvent
& event
) 
 458     // By default, load the first file into the text window. 
 459     if (event
.GetNumberOfFiles() > 0) 
 461         LoadFile(event
.GetFiles()[0]); 
 465 // The streambuf code was partly taken from chapter 3 by Jerry Schwarz of 
 466 // AT&T's "C++ Lanuage System Release 3.0 Library Manual" - Stein Somers 
 468 //========================================================================= 
 469 // Called then the buffer is full (gcc 2.6.3)  
 470 // or when "endl" is output (Borland 4.5) 
 471 //========================================================================= 
 472 // Class declaration using multiple inheritance doesn't work properly for 
 473 // Borland. See note in wb_text.h. 
 474 #ifndef NO_TEXT_WINDOW_STREAM 
 475 int wxTextCtrl::overflow(int c
) 
 477   // Make sure there is a holding area 
 478   if ( allocate()==EOF 
) 
 480     wxError("Streambuf allocation failed","Internal error"); 
 484   // Verify that there are no characters in get area 
 485   if ( gptr() && gptr() < egptr() ) 
 487      wxError("Who's trespassing my get area?","Internal error"); 
 494   // Make sure there is a put area 
 497 /* This doesn't seem to be fatal so comment out error message */ 
 498 //    wxError("Put area not opened","Internal error"); 
 499     setp( base(), base() ); 
 502   // Determine how many characters have been inserted but no consumed 
 503   int plen 
= pptr() - pbase(); 
 505   // Now Jerry relies on the fact that the buffer is at least 2 chars 
 506   // long, but the holding area "may be as small as 1" ??? 
 507   // And we need an additional \0, so let's keep this inefficient but 
 510   // If c!=EOF, it is a character that must also be comsumed 
 511   int xtra 
= c
==EOF
? 0 : 1; 
 513   // Write temporary C-string to wxTextWindow 
 515   char *txt 
= new char[plen
+xtra
+1]; 
 516   memcpy(txt
, pbase(), plen
); 
 517   txt
[plen
] = (char)c
;     // append c 
 518   txt
[plen
+xtra
] = '\0';   // append '\0' or overwrite c 
 519     // If the put area already contained \0, output will be truncated there 
 525   setp(pbase(), epptr()); 
 527 #if defined(__WATCOMC__) 
 529 #elif defined(zapeof)     // HP-UX (all cfront based?) 
 532   return c
!=EOF 
? c 
: 0;  // this should make everybody happy 
 536 //========================================================================= 
 537 // called then "endl" is output (gcc) or then explicit sync is done (Borland) 
 538 //========================================================================= 
 539 int wxTextCtrl::sync() 
 541   // Verify that there are no characters in get area 
 542   if ( gptr() && gptr() < egptr() ) 
 544      wxError("Who's trespassing my get area?","Internal error"); 
 548   if ( pptr() && pptr() > pbase() ) return overflow(EOF
); 
 552   int len = pptr() - pbase(); 
 553   char *txt = new char[len+1]; 
 554   strncpy(txt, pbase(), len); 
 557   setp(pbase(), epptr()); 
 563 //========================================================================= 
 564 // Should not be called by a "ostream". Used by a "istream" 
 565 //========================================================================= 
 566 int wxTextCtrl::underflow() 
 572 wxTextCtrl
& wxTextCtrl::operator<<(const wxString
& s
) 
 578 wxTextCtrl
& wxTextCtrl::operator<<(float f
) 
 581     str
.Printf("%.2f", f
); 
 586 wxTextCtrl
& wxTextCtrl::operator<<(double d
) 
 589     str
.Printf("%.2f", d
); 
 594 wxTextCtrl
& wxTextCtrl::operator<<(int i
) 
 602 wxTextCtrl
& wxTextCtrl::operator<<(long i
) 
 605     str
.Printf("%ld", i
); 
 610 wxTextCtrl
& wxTextCtrl::operator<<(const char c
) 
 620 void wxTextCtrl::OnChar(wxKeyEvent
& event
) 
 622   // Indicates that we should generate a normal command, because 
 623   // we're letting default behaviour happen (otherwise it's vetoed 
 624   // by virtue of overriding OnChar) 
 625   m_processedDefault 
= TRUE
; 
 627   if (m_tempCallbackStruct
) 
 629     XmTextVerifyCallbackStruct 
*textStruct 
= 
 630         (XmTextVerifyCallbackStruct 
*) m_tempCallbackStruct
; 
 631     textStruct
->doit 
= True
; 
 632     if (isascii(event
.m_keyCode
) && (textStruct
->text
->length 
== 1)) 
 634       textStruct
->text
->ptr
[0] = ((event
.m_keyCode 
== WXK_RETURN
) ? 10 : event
.m_keyCode
); 
 639 void wxTextCtrl::ChangeFont() 
 641     wxWindow::ChangeFont(); 
 644 void wxTextCtrl::ChangeBackgroundColour() 
 646     wxWindow::ChangeBackgroundColour(); 
 649 void wxTextCtrl::ChangeForegroundColour() 
 651     wxWindow::ChangeForegroundColour(); 
 654 static void wxTextWindowChangedProc (Widget w
, XtPointer clientData
, XtPointer ptr
) 
 656   if (!wxGetWindowFromTable(w
)) 
 657     // Widget has been deleted! 
 660   wxTextCtrl 
*tw 
= (wxTextCtrl 
*) clientData
; 
 661   tw
->SetModified(TRUE
); 
 665 wxTextWindowModifyProc (Widget w
, XtPointer clientData
, XmTextVerifyCallbackStruct 
*cbs
) 
 667     wxTextCtrl 
*tw 
= (wxTextCtrl 
*) clientData
; 
 668     tw
->m_processedDefault 
= FALSE
; 
 670     // First, do some stuff if it's a password control. 
 671     // (What does this do exactly?) 
 673     if (tw
->GetWindowStyleFlag() & wxTE_PASSWORD
) 
 676        * At least on my system (SunOS 4.1.3 + Motif 1.2), you need to think of 
 677        * every event as a replace event.  cbs->text->ptr gives the replacement 
 678        * text, cbs->startPos gives the index of the first char affected by the 
 679        * replace, and cbs->endPos gives the index one more than the last char 
 680        * affected by the replace (startPos == endPos implies an empty range). 
 681        * Hence, a deletion is represented by replacing all input text with a 
 682        * blank string ("", *not* NULL!).  A simple insertion that does not 
 683        * overwrite any text has startPos == endPos. 
 686         if (tw
->m_value
.IsNull()) 
 688             tw
->m_value 
= cbs
->text
->ptr
; 
 692             char * passwd 
= (char*) (const char*) tw
->m_value
;  // Set up a more convenient alias. 
 694             int len 
= passwd 
? strlen(passwd
) : 0; // Enough room for old text 
 695             len 
+= strlen(cbs
->text
->ptr
) + 1;     // + new text (if any) + NUL 
 696             len 
-= cbs
->endPos 
- cbs
->startPos
;    // - text from affected region. 
 698             char * newS 
= new char [len
]; 
 699             char * p 
= passwd
, * dest 
= newS
, * insert 
= cbs
->text
->ptr
; 
 701             // Copy (old) text from passwd, up to the start posn of the change. 
 703             for (i 
= 0; i 
< cbs
->startPos
; ++i
) 
 706             // Copy the text to be inserted). 
 710             // Finally, copy into newS any remaining text from passwd[endPos] on. 
 711             for (p 
= passwd 
+ cbs
->endPos
; *p
; ) 
 720         if (cbs
->text
->length
>0) 
 723             for (i 
= 0; i 
< cbs
->text
->length
; ++i
) 
 724                 cbs
->text
->ptr
[i
] = '*'; 
 725             cbs
->text
->ptr
[i
] = 0; 
 729     // If we're already within an OnChar, return: probably 
 730     // a programmatic insertion. 
 731     if (tw
->m_tempCallbackStruct
) 
 734     // Check for a backspace 
 735     if (cbs
->startPos 
== (cbs
->currInsert 
- 1)) 
 737         tw
->m_tempCallbackStruct 
= (void*) cbs
; 
 739         wxKeyEvent 
event (wxEVT_CHAR
); 
 740         event
.SetId(tw
->GetId()); 
 741         event
.m_keyCode 
= WXK_DELETE
; 
 742         event
.SetEventObject(tw
); 
 744         // Only if wxTextCtrl::OnChar is called 
 745         // will this be set to True (and the character 
 749         tw
->GetEventHandler()->ProcessEvent(event
); 
 751         tw
->m_tempCallbackStruct 
= NULL
; 
 753         if (tw
->InSetValue()) 
 756         if (tw
->m_processedDefault
) 
 758             // Can generate a command 
 759             wxCommandEvent 
commandEvent(wxEVT_COMMAND_TEXT_UPDATED
, tw
->GetId()); 
 760             commandEvent
.SetEventObject(tw
); 
 761             tw
->ProcessCommand(commandEvent
); 
 767     // Pasting operation: let it through without 
 769     if (cbs
->text
->length 
> 1) 
 772     // Something other than text 
 773     if (cbs
->text
->ptr 
== NULL
) 
 776     tw
->m_tempCallbackStruct 
= (void*) cbs
; 
 778     wxKeyEvent 
event (wxEVT_CHAR
); 
 779     event
.SetId(tw
->GetId()); 
 780     event
.SetEventObject(tw
); 
 781     event
.m_keyCode 
= (cbs
->text
->ptr
[0] == 10 ? 13 : cbs
->text
->ptr
[0]); 
 783     // Only if wxTextCtrl::OnChar is called 
 784     // will this be set to True (and the character 
 788     tw
->GetEventHandler()->ProcessEvent(event
); 
 790     tw
->m_tempCallbackStruct 
= NULL
; 
 792     if (tw
->InSetValue()) 
 795     if (tw
->m_processedDefault
) 
 797         // Can generate a command 
 798         wxCommandEvent 
commandEvent(wxEVT_COMMAND_TEXT_UPDATED
, tw
->GetId()); 
 799         commandEvent
.SetEventObject(tw
); 
 800         tw
->ProcessCommand(commandEvent
); 
 805 wxTextWindowGainFocusProc (Widget w
, XtPointer clientData
, XmAnyCallbackStruct 
*cbs
) 
 807   if (!wxGetWindowFromTable(w
)) 
 810   wxTextCtrl 
*tw 
= (wxTextCtrl 
*) clientData
; 
 811   wxFocusEvent 
event(wxEVT_SET_FOCUS
, tw
->GetId()); 
 812   event
.SetEventObject(tw
); 
 813   tw
->GetEventHandler()->ProcessEvent(event
); 
 817 wxTextWindowLoseFocusProc (Widget w
, XtPointer clientData
, XmAnyCallbackStruct 
*cbs
) 
 819   if (!wxGetWindowFromTable(w
)) 
 822   wxTextCtrl 
*tw 
= (wxTextCtrl 
*) clientData
; 
 823   wxFocusEvent 
event(wxEVT_KILL_FOCUS
, tw
->GetId()); 
 824   event
.SetEventObject(tw
); 
 825   tw
->GetEventHandler()->ProcessEvent(event
); 
 828 static void wxTextWindowActivateProc(Widget w
, XtPointer clientData
, 
 829                       XmAnyCallbackStruct 
*ptr
) 
 831   if (!wxGetWindowFromTable(w
)) 
 834   wxTextCtrl 
*tw 
= (wxTextCtrl 
*) clientData
; 
 837     type_event = wxEVENT_TYPE_TEXT_ENTER_COMMAND ; 
 840     type_event = wxEVENT_TYPE_TEXT_COMMAND ; 
 845   if (tw
->InSetValue()) 
 848   wxCommandEvent 
event(wxEVT_COMMAND_TEXT_ENTER
); 
 849   event
.SetId(tw
->GetId()); 
 850   event
.SetEventObject(tw
); 
 851   tw
->ProcessCommand(event
);