1 /////////////////////////////////////////////////////////////////////////////
4 // Author: Robert Roebling
6 // Copyright: (c) 1998 Robert Roebling, Vadim Zeitlin
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
11 #pragma implementation "textctrl.h"
14 #include "wx/textctrl.h"
17 #include "wx/settings.h"
19 #include <sys/types.h>
25 #include "gdk/gdkkeysyms.h"
27 //-----------------------------------------------------------------------------
29 //-----------------------------------------------------------------------------
31 extern bool g_blockEventsOnDrag
;
33 //-----------------------------------------------------------------------------
35 //-----------------------------------------------------------------------------
38 gtk_text_changed_callback( GtkWidget
*WXUNUSED(widget
), wxTextCtrl
*win
)
42 win
->CalculateScrollbar();
44 wxCommandEvent
event( wxEVT_COMMAND_TEXT_UPDATED
, win
->m_windowId
);
45 event
.SetString( win
->GetValue() );
46 event
.SetEventObject( win
);
47 win
->GetEventHandler()->ProcessEvent( event
);
50 //-----------------------------------------------------------------------------
52 //-----------------------------------------------------------------------------
55 gtk_text_size_callback( GtkWidget
*WXUNUSED(widget
), GtkAllocation
* WXUNUSED(alloc
), wxTextCtrl
*win
)
57 win
->CalculateScrollbar();
60 //-----------------------------------------------------------------------------
62 //-----------------------------------------------------------------------------
64 IMPLEMENT_DYNAMIC_CLASS(wxTextCtrl
,wxControl
)
66 BEGIN_EVENT_TABLE(wxTextCtrl
, wxControl
)
67 EVT_CHAR(wxTextCtrl::OnChar
)
70 #ifndef NO_TEXT_WINDOW_STREAM
71 wxTextCtrl::wxTextCtrl() : streambuf()
73 if (allocate()) setp(base(),ebuf());
78 wxTextCtrl::wxTextCtrl()
84 #ifndef NO_TEXT_WINDOW_STREAM
85 wxTextCtrl::wxTextCtrl( wxWindow
*parent
, wxWindowID id
, const wxString
&value
,
86 const wxPoint
&pos
, const wxSize
&size
,
87 int style
, const wxValidator
& validator
, const wxString
&name
) : streambuf()
89 if (allocate()) setp(base(),ebuf());
92 Create( parent
, id
, value
, pos
, size
, style
, validator
, name
);
95 wxTextCtrl::wxTextCtrl( wxWindow
*parent
, wxWindowID id
, const wxString
&value
,
96 const wxPoint
&pos
, const wxSize
&size
,
97 int style
, const wxValidator
& validator
, const wxString
&name
)
100 Create( parent
, id
, value
, pos
, size
, style
, validator
, name
);
104 bool wxTextCtrl::Create( wxWindow
*parent
, wxWindowID id
, const wxString
&value
,
105 const wxPoint
&pos
, const wxSize
&size
,
106 int style
, const wxValidator
& validator
, const wxString
&name
)
109 m_acceptsFocus
= TRUE
;
111 PreCreation( parent
, id
, pos
, size
, style
, name
);
113 SetValidator( validator
);
115 m_vScrollbarVisible
= TRUE
;
117 bool multi_line
= (style
& wxTE_MULTILINE
) != 0;
120 // a multi-line edit control: create a vertical scrollbar by default and
121 // horizontal if requested
122 bool bHasHScrollbar
= (style
& wxHSCROLL
) != 0;
124 // create our control...
125 m_text
= gtk_text_new( (GtkAdjustment
*) NULL
, (GtkAdjustment
*) NULL
);
127 // ... and put into the upper left hand corner of the table
128 m_widget
= gtk_table_new(bHasHScrollbar
? 2 : 1, 2, FALSE
);
129 GTK_WIDGET_UNSET_FLAGS( m_widget
, GTK_CAN_FOCUS
);
131 gtk_table_attach( GTK_TABLE(m_widget
), m_text
, 0, 1, 0, 1,
132 (GtkAttachOptions
)(GTK_FILL
| GTK_EXPAND
| GTK_SHRINK
),
133 (GtkAttachOptions
)(GTK_FILL
| GTK_EXPAND
| GTK_SHRINK
),
136 // put the horizontal scrollbar in the lower left hand corner
139 GtkWidget
*hscrollbar
= gtk_hscrollbar_new(GTK_TEXT(m_text
)->hadj
);
140 GTK_WIDGET_UNSET_FLAGS( hscrollbar
, GTK_CAN_FOCUS
);
142 gtk_table_attach(GTK_TABLE(m_widget
), hscrollbar
, 0, 1, 1, 2,
143 (GtkAttachOptions
)(GTK_EXPAND
| GTK_FILL
),
146 gtk_widget_show(hscrollbar
);
149 // finally, put the vertical scrollbar in the upper right corner
150 m_vScrollbar
= gtk_vscrollbar_new( GTK_TEXT(m_text
)->vadj
);
151 GTK_WIDGET_UNSET_FLAGS( m_vScrollbar
, GTK_CAN_FOCUS
);
153 gtk_table_attach(GTK_TABLE(m_widget
), m_vScrollbar
, 1, 2, 0, 1,
155 (GtkAttachOptions
)(GTK_EXPAND
| GTK_FILL
| GTK_SHRINK
),
157 gtk_widget_show( m_vScrollbar
);
159 gtk_signal_connect( GTK_OBJECT(m_widget
), "size_allocate",
160 GTK_SIGNAL_FUNC(gtk_text_size_callback
), (gpointer
)this );
164 // a single-line text control: no need for scrollbars
166 m_text
= gtk_entry_new();
169 wxSize newSize
= size
;
170 if (newSize
.x
== -1) newSize
.x
= 80;
171 if (newSize
.y
== -1) newSize
.y
= 26;
172 SetSize( newSize
.x
, newSize
.y
);
174 m_parent
->AddChild( this );
176 (m_parent
->m_insertCallback
)( m_parent
, this );
182 gtk_widget_realize(m_text
);
183 gtk_widget_show(m_text
);
186 // we want to be notified about text changes
187 gtk_signal_connect( GTK_OBJECT(m_text
), "changed",
188 GTK_SIGNAL_FUNC(gtk_text_changed_callback
), (gpointer
)this);
190 if (!value
.IsEmpty())
193 gtk_editable_insert_text( GTK_EDITABLE(m_text
), value
, value
.Length(), &tmp
);
197 /* bring editable's cursor uptodate. bug in GTK. */
199 GTK_EDITABLE(m_text
)->current_pos
= gtk_text_get_point( GTK_TEXT(m_text
) );
203 if (style
& wxTE_PASSWORD
)
206 gtk_entry_set_visibility( GTK_ENTRY(m_text
), FALSE
);
209 if (style
& wxTE_READONLY
)
212 gtk_entry_set_editable( GTK_ENTRY(m_text
), FALSE
);
217 gtk_text_set_editable( GTK_TEXT(m_text
), 1 );
222 SetBackgroundColour( parent
->GetBackgroundColour() );
223 SetForegroundColour( parent
->GetForegroundColour() );
228 void wxTextCtrl::CalculateScrollbar()
230 if ((m_windowStyle
& wxTE_MULTILINE
) == 0) return;
232 GtkAdjustment
*adj
= GTK_TEXT(m_text
)->vadj
;
234 if (adj
->upper
- adj
->page_size
< 0.8)
236 if (m_vScrollbarVisible
)
238 gtk_widget_hide( m_vScrollbar
);
240 m_vScrollbarVisible
= FALSE
;
245 if (!m_vScrollbarVisible
)
247 gtk_widget_show( m_vScrollbar
);
249 m_vScrollbarVisible
= TRUE
;
254 wxString
wxTextCtrl::GetValue() const
256 wxCHECK_MSG( m_text
!= NULL
, "", "invalid text ctrl" );
259 if (m_windowStyle
& wxTE_MULTILINE
)
261 gint len
= gtk_text_get_length( GTK_TEXT(m_text
) );
262 char *text
= gtk_editable_get_chars( GTK_EDITABLE(m_text
), 0, len
);
268 tmp
= gtk_entry_get_text( GTK_ENTRY(m_text
) );
273 void wxTextCtrl::SetValue( const wxString
&value
)
275 wxCHECK_RET( m_text
!= NULL
, "invalid text ctrl" );
278 if (!value
.IsNull()) tmp
= value
;
279 if (m_windowStyle
& wxTE_MULTILINE
)
281 gint len
= gtk_text_get_length( GTK_TEXT(m_text
) );
282 gtk_editable_delete_text( GTK_EDITABLE(m_text
), 0, len
);
284 gtk_editable_insert_text( GTK_EDITABLE(m_text
), tmp
, tmp
.Length(), &len
);
288 gtk_entry_set_text( GTK_ENTRY(m_text
), tmp
);
292 void wxTextCtrl::WriteText( const wxString
&text
)
294 wxCHECK_RET( m_text
!= NULL
, "invalid text ctrl" );
296 if (text
.IsNull()) return;
298 if (m_windowStyle
& wxTE_MULTILINE
)
300 /* this moves the cursor pos to behind the inserted text */
301 gint len
= GTK_EDITABLE(m_text
)->current_pos
;
303 gtk_editable_insert_text( GTK_EDITABLE(m_text
), text
, text
.Length(), &len
);
305 /* bring editable's cursor uptodate. bug in GTK. */
306 GTK_EDITABLE(m_text
)->current_pos
= gtk_text_get_point( GTK_TEXT(m_text
) );
310 /* this moves the cursor pos to behind the inserted text */
311 gint len
= GTK_EDITABLE(m_text
)->current_pos
;
312 gtk_editable_insert_text( GTK_EDITABLE(m_text
), text
, text
.Length(), &len
);
314 /* bring editable's cursor uptodate. bug in GTK. */
315 GTK_EDITABLE(m_text
)->current_pos
+= text
.Len();
317 /* bring entry's cursor uptodate. bug in GTK. */
318 gtk_entry_set_position( GTK_ENTRY(m_text
), GTK_EDITABLE(m_text
)->current_pos
);
322 void wxTextCtrl::AppendText( const wxString
&text
)
324 wxCHECK_RET( m_text
!= NULL
, "invalid text ctrl" );
326 if (m_windowStyle
& wxTE_MULTILINE
)
328 /* we'll insert at the last position */
329 gint len
= gtk_text_get_length( GTK_TEXT(m_text
) );
330 gtk_editable_insert_text( GTK_EDITABLE(m_text
), text
, text
.Length(), &len
);
332 /* bring editable's cursor uptodate. bug in GTK. */
333 GTK_EDITABLE(m_text
)->current_pos
= gtk_text_get_point( GTK_TEXT(m_text
) );
337 gtk_entry_append_text( GTK_ENTRY(m_text
), text
);
341 bool wxTextCtrl::LoadFile( const wxString
&file
)
343 wxCHECK_MSG( m_text
!= NULL
, FALSE
, "invalid text ctrl" );
345 if (!wxFileExists(file
)) return FALSE
;
349 FILE *fp
= (FILE*) NULL
;
352 if ((stat ((char*) (const char*) file
, &statb
) == -1) || (statb
.st_mode
& S_IFMT
) != S_IFREG
||
353 !(fp
= fopen ((char*) (const char*) file
, "r")))
359 gint len
= statb
.st_size
;
361 if (!(text
= (char*)malloc ((unsigned) (len
+ 1))))
366 if (fread (text
, sizeof (char), len
, fp
) != (size_t) len
)
373 if (m_windowStyle
& wxTE_MULTILINE
)
376 gtk_editable_insert_text( GTK_EDITABLE(m_text
), text
, len
, &pos
);
380 gtk_entry_set_text( GTK_ENTRY(m_text
), text
);
390 bool wxTextCtrl::SaveFile( const wxString
&file
)
392 wxCHECK_MSG( m_text
!= NULL
, FALSE
, "invalid text ctrl" );
394 if (file
== "") return FALSE
;
398 if (!(fp
= fopen ((char*) (const char*) file
, "w")))
404 char *text
= (char*) NULL
;
407 if (m_windowStyle
& wxTE_MULTILINE
)
409 len
= gtk_text_get_length( GTK_TEXT(m_text
) );
410 text
= gtk_editable_get_chars( GTK_EDITABLE(m_text
), 0, len
);
414 text
= gtk_entry_get_text( GTK_ENTRY(m_text
) );
417 if (fwrite (text
, sizeof (char), len
, fp
) != (size_t) len
)
419 // Did not write whole file
422 // Make sure newline terminates the file
423 if (text
[len
- 1] != '\n')
428 if (m_windowStyle
& wxTE_MULTILINE
) g_free( text
);
437 wxString
wxTextCtrl::GetLineText( long lineNo
) const
439 if (m_windowStyle
& wxTE_MULTILINE
)
441 gint len
= gtk_text_get_length( GTK_TEXT(m_text
) );
442 char *text
= gtk_editable_get_chars( GTK_EDITABLE(m_text
), 0, len
);
449 for (i
= 0; currentLine
!= lineNo
&& text
[i
]; i
++ )
454 for (j
= 0; text
[i
] && text
[i
] != '\n'; i
++, j
++ )
461 return wxEmptyString
;
465 if (lineNo
== 0) return GetValue();
466 return wxEmptyString
;
470 void wxTextCtrl::OnDropFiles( wxDropFilesEvent
&WXUNUSED(event
) )
472 /* If you implement this, don't forget to update the documentation!
473 * (file docs/latex/wx/text.tex) */
474 wxFAIL_MSG( "wxTextCtrl::OnDropFiles not implemented" );
477 long wxTextCtrl::PositionToXY(long pos
, long *x
, long *y
) const
479 if ( m_windowStyle
& wxTE_MULTILINE
)
481 wxString text
= GetValue();
483 // cast to prevent warning. But pos really should've been unsigned.
484 if( (unsigned long)pos
> text
.Len() )
490 const char* stop
= text
.c_str() + pos
;
491 for ( const char *p
= text
.c_str(); p
< stop
; p
++ )
502 else // single line control
504 if ( pos
<= GTK_ENTRY(m_text
)->text_length
)
511 // index out of bounds
519 long wxTextCtrl::XYToPosition(long x
, long y
) const
521 if (!(m_windowStyle
& wxTE_MULTILINE
)) return 0;
524 for( int i
=0; i
<y
; i
++ ) pos
+= GetLineLength(i
) + 1; // one for '\n'
530 int wxTextCtrl::GetLineLength(long lineNo
) const
532 wxString str
= GetLineText (lineNo
);
533 return (int) str
.Length();
536 int wxTextCtrl::GetNumberOfLines() const
538 if (m_windowStyle
& wxTE_MULTILINE
)
540 gint len
= gtk_text_get_length( GTK_TEXT(m_text
) );
541 char *text
= gtk_editable_get_chars( GTK_EDITABLE(m_text
), 0, len
);
546 for (int i
= 0; i
< len
; i
++ )
553 // currentLine is 0 based, add 1 to get number of lines
554 return currentLine
+ 1;
567 void wxTextCtrl::SetInsertionPoint( long pos
)
569 wxCHECK_RET( m_text
!= NULL
, "invalid text ctrl" );
571 if (m_windowStyle
& wxTE_MULTILINE
)
573 /* seems to be broken in GTK 1.0.X:
574 gtk_text_set_point( GTK_TEXT(m_text), (int)pos ); */
576 gtk_signal_disconnect_by_func( GTK_OBJECT(m_text
),
577 GTK_SIGNAL_FUNC(gtk_text_changed_callback
), (gpointer
)this);
579 /* we fake a set_point by inserting and deleting. as the user
580 isn't supposed to get to know about thos non-sense, we
581 disconnect so that no events are sent to the user program. */
583 gint tmp
= (gint
)pos
;
584 gtk_editable_insert_text( GTK_EDITABLE(m_text
), " ", 1, &tmp
);
585 gtk_editable_delete_text( GTK_EDITABLE(m_text
), tmp
-1, tmp
);
587 gtk_signal_connect( GTK_OBJECT(m_text
), "changed",
588 GTK_SIGNAL_FUNC(gtk_text_changed_callback
), (gpointer
)this);
590 /* bring editable's cursor uptodate. another bug in GTK. */
592 GTK_EDITABLE(m_text
)->current_pos
= gtk_text_get_point( GTK_TEXT(m_text
) );
596 gtk_entry_set_position( GTK_ENTRY(m_text
), (int)pos
);
598 /* bring editable's cursor uptodate. bug in GTK. */
600 GTK_EDITABLE(m_text
)->current_pos
= pos
;
604 void wxTextCtrl::SetInsertionPointEnd()
606 wxCHECK_RET( m_text
!= NULL
, "invalid text ctrl" );
608 if (m_windowStyle
& wxTE_MULTILINE
)
609 SetInsertionPoint(gtk_text_get_length(GTK_TEXT(m_text
)));
611 gtk_entry_set_position( GTK_ENTRY(m_text
), -1 );
614 void wxTextCtrl::SetEditable( bool editable
)
616 wxCHECK_RET( m_text
!= NULL
, "invalid text ctrl" );
618 if (m_windowStyle
& wxTE_MULTILINE
)
619 gtk_text_set_editable( GTK_TEXT(m_text
), editable
);
621 gtk_entry_set_editable( GTK_ENTRY(m_text
), editable
);
624 void wxTextCtrl::SetSelection( long from
, long to
)
626 wxCHECK_RET( m_text
!= NULL
, "invalid text ctrl" );
628 gtk_editable_select_region( GTK_EDITABLE(m_text
), (gint
)from
, (gint
)to
);
631 void wxTextCtrl::ShowPosition( long WXUNUSED(pos
) )
633 wxFAIL_MSG( "wxTextCtrl::ShowPosition not implemented" );
636 long wxTextCtrl::GetInsertionPoint() const
638 wxCHECK_MSG( m_text
!= NULL
, 0, "invalid text ctrl" );
640 return (long) GTK_EDITABLE(m_text
)->current_pos
;
643 long wxTextCtrl::GetLastPosition() const
645 wxCHECK_MSG( m_text
!= NULL
, 0, "invalid text ctrl" );
648 if (m_windowStyle
& wxTE_MULTILINE
)
649 pos
= gtk_text_get_length( GTK_TEXT(m_text
) );
651 pos
= GTK_ENTRY(m_text
)->text_length
;
656 void wxTextCtrl::Remove( long from
, long to
)
658 wxCHECK_RET( m_text
!= NULL
, "invalid text ctrl" );
660 gtk_editable_delete_text( GTK_EDITABLE(m_text
), (gint
)from
, (gint
)to
);
663 void wxTextCtrl::Replace( long from
, long to
, const wxString
&value
)
665 wxCHECK_RET( m_text
!= NULL
, "invalid text ctrl" );
667 gtk_editable_delete_text( GTK_EDITABLE(m_text
), (gint
)from
, (gint
)to
);
668 if (value
.IsNull()) return;
669 gint pos
= (gint
)from
;
670 gtk_editable_insert_text( GTK_EDITABLE(m_text
), value
, value
.Length(), &pos
);
673 void wxTextCtrl::Cut()
675 wxCHECK_RET( m_text
!= NULL
, "invalid text ctrl" );
677 #if (GTK_MINOR_VERSION == 1)
678 gtk_editable_cut_clipboard( GTK_EDITABLE(m_text
) );
680 gtk_editable_cut_clipboard( GTK_EDITABLE(m_text
), 0 );
684 void wxTextCtrl::Copy()
686 wxCHECK_RET( m_text
!= NULL
, "invalid text ctrl" );
688 #if (GTK_MINOR_VERSION == 1)
689 gtk_editable_copy_clipboard( GTK_EDITABLE(m_text
) );
691 gtk_editable_copy_clipboard( GTK_EDITABLE(m_text
), 0 );
695 void wxTextCtrl::Paste()
697 wxCHECK_RET( m_text
!= NULL
, "invalid text ctrl" );
699 #if (GTK_MINOR_VERSION == 1)
700 gtk_editable_paste_clipboard( GTK_EDITABLE(m_text
) );
702 gtk_editable_paste_clipboard( GTK_EDITABLE(m_text
), 0 );
706 void wxTextCtrl::Clear()
711 void wxTextCtrl::OnChar( wxKeyEvent
&key_event
)
713 wxCHECK_RET( m_text
!= NULL
, "invalid text ctrl" );
715 if ((key_event
.KeyCode() == WXK_RETURN
) && (m_windowStyle
& wxPROCESS_ENTER
))
717 wxCommandEvent
event(wxEVT_COMMAND_TEXT_ENTER
, m_windowId
);
718 event
.SetEventObject(this);
719 if (GetEventHandler()->ProcessEvent(event
)) return;
725 #ifndef NO_TEXT_WINDOW_STREAM
726 int wxTextCtrl::overflow( int WXUNUSED(c
) )
728 int len
= pptr() - pbase();
729 char *txt
= new char[len
+1];
730 strncpy(txt
, pbase(), len
);
733 setp(pbase(), epptr());
738 int wxTextCtrl::sync()
740 int len
= pptr() - pbase();
741 char *txt
= new char[len
+1];
742 strncpy(txt
, pbase(), len
);
745 setp(pbase(), epptr());
750 int wxTextCtrl::underflow()
755 wxTextCtrl
& wxTextCtrl::operator<<(const wxString
& s
)
761 wxTextCtrl
& wxTextCtrl::operator<<(float f
)
763 static char buf
[100];
764 sprintf(buf
, "%.2f", f
);
769 wxTextCtrl
& wxTextCtrl::operator<<(double d
)
771 static char buf
[100];
772 sprintf(buf
, "%.2f", d
);
777 wxTextCtrl
& wxTextCtrl::operator<<(int i
)
779 static char buf
[100];
780 sprintf(buf
, "%i", i
);
785 wxTextCtrl
& wxTextCtrl::operator<<(long i
)
787 static char buf
[100];
788 sprintf(buf
, "%ld", i
);
793 wxTextCtrl
& wxTextCtrl::operator<<(const char c
)
804 GtkWidget
* wxTextCtrl::GetConnectWidget()
806 return GTK_WIDGET(m_text
);
809 bool wxTextCtrl::IsOwnGtkWindow( GdkWindow
*window
)
811 if (m_windowStyle
& wxTE_MULTILINE
)
812 return (window
== GTK_TEXT(m_text
)->text_area
);
814 return (window
== GTK_ENTRY(m_text
)->text_area
);
817 void wxTextCtrl::SetFont( const wxFont
&WXUNUSED(font
) )
819 wxCHECK_RET( m_text
!= NULL
, "invalid text ctrl" );
824 void wxTextCtrl::SetForegroundColour( const wxColour
&WXUNUSED(colour
) )
826 wxCHECK_RET( m_text
!= NULL
, "invalid text ctrl" );
831 void wxTextCtrl::SetBackgroundColour( const wxColour
&colour
)
833 wxCHECK_RET( m_text
!= NULL
, "invalid text ctrl" );
835 wxControl::SetBackgroundColour( colour
);
837 wxColour sysbg
= wxSystemSettings::GetSystemColour( wxSYS_COLOUR_BTNFACE
);
838 if (sysbg
.Red() == colour
.Red() &&
839 sysbg
.Green() == colour
.Green() &&
840 sysbg
.Blue() == colour
.Blue())
845 if (!m_backgroundColour
.Ok()) return;
847 if (m_windowStyle
& wxTE_MULTILINE
)
849 GdkWindow
*window
= GTK_TEXT(m_text
)->text_area
;
850 m_backgroundColour
.CalcPixel( gdk_window_get_colormap( window
) );
851 gdk_window_set_background( window
, m_backgroundColour
.GetColor() );
852 gdk_window_clear( window
);
856 void wxTextCtrl::ApplyWidgetStyle()
858 if (m_windowStyle
& wxTE_MULTILINE
)
865 gtk_widget_set_style( m_text
, m_widgetStyle
);