]> git.saurik.com Git - wxWidgets.git/blob - src/gtk/textctrl.cpp
applied patch #103817 to both branches (HTML printing typo in margins code)
[wxWidgets.git] / src / gtk / textctrl.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        textctrl.cpp
3 // Purpose:
4 // Author:      Robert Roebling
5 // Id:          $Id$
6 // Copyright:   (c) 1998 Robert Roebling, Vadim Zeitlin
7 // Licence:     wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
9
10 #ifdef __GNUG__
11 #pragma implementation "textctrl.h"
12 #endif
13
14 #include "wx/textctrl.h"
15 #include "wx/utils.h"
16 #include "wx/intl.h"
17 #include "wx/log.h"
18 #include "wx/settings.h"
19
20 #include <sys/types.h>
21 #include <sys/stat.h>
22 #include <ctype.h>
23
24 #include "gdk/gdk.h"
25 #include "gtk/gtk.h"
26 #include "gdk/gdkkeysyms.h"
27
28 //-----------------------------------------------------------------------------
29 // idle system
30 //-----------------------------------------------------------------------------
31
32 extern void wxapp_install_idle_handler();
33 extern bool g_isIdle;
34
35 //-----------------------------------------------------------------------------
36 // data
37 //-----------------------------------------------------------------------------
38
39 extern bool       g_blockEventsOnDrag;
40 extern wxCursor   g_globalCursor;
41
42 //-----------------------------------------------------------------------------
43 //  "changed"
44 //-----------------------------------------------------------------------------
45
46 static void
47 gtk_text_changed_callback( GtkWidget *WXUNUSED(widget), wxTextCtrl *win )
48 {
49     if (!win->m_hasVMT) return;
50
51     if (g_isIdle)
52         wxapp_install_idle_handler();
53
54     win->SetModified();
55     win->UpdateFontIfNeeded();
56
57     wxCommandEvent event( wxEVT_COMMAND_TEXT_UPDATED, win->GetId() );
58     event.SetString( win->GetValue() );
59     event.SetEventObject( win );
60     win->GetEventHandler()->ProcessEvent( event );
61 }
62
63 //-----------------------------------------------------------------------------
64 // "changed" from vertical scrollbar
65 //-----------------------------------------------------------------------------
66
67 static void
68 gtk_scrollbar_changed_callback( GtkWidget *WXUNUSED(widget), wxTextCtrl *win )
69 {
70     if (!win->m_hasVMT) return;
71
72     if (g_isIdle)
73         wxapp_install_idle_handler();
74
75     win->CalculateScrollbar();
76 }
77
78 //-----------------------------------------------------------------------------
79 //  wxTextCtrl
80 //-----------------------------------------------------------------------------
81
82 IMPLEMENT_DYNAMIC_CLASS(wxTextCtrl,wxControl)
83
84 BEGIN_EVENT_TABLE(wxTextCtrl, wxControl)
85     EVT_CHAR(wxTextCtrl::OnChar)
86
87     EVT_MENU(wxID_CUT, wxTextCtrl::OnCut)
88     EVT_MENU(wxID_COPY, wxTextCtrl::OnCopy)
89     EVT_MENU(wxID_PASTE, wxTextCtrl::OnPaste)
90     EVT_MENU(wxID_UNDO, wxTextCtrl::OnUndo)
91     EVT_MENU(wxID_REDO, wxTextCtrl::OnRedo)
92
93     EVT_UPDATE_UI(wxID_CUT, wxTextCtrl::OnUpdateCut)
94     EVT_UPDATE_UI(wxID_COPY, wxTextCtrl::OnUpdateCopy)
95     EVT_UPDATE_UI(wxID_PASTE, wxTextCtrl::OnUpdatePaste)
96     EVT_UPDATE_UI(wxID_UNDO, wxTextCtrl::OnUpdateUndo)
97     EVT_UPDATE_UI(wxID_REDO, wxTextCtrl::OnUpdateRedo)
98 END_EVENT_TABLE()
99
100 void wxTextCtrl::Init()
101 {
102     m_modified = FALSE;
103     m_updateFont = FALSE;
104     m_text =
105     m_vScrollbar = (GtkWidget *)NULL;
106 }
107
108 wxTextCtrl::wxTextCtrl( wxWindow *parent,
109                         wxWindowID id,
110                         const wxString &value,
111                         const wxPoint &pos,
112                         const wxSize &size,
113                         long style,
114                         const wxValidator& validator,
115                         const wxString &name )
116 {
117     Init();
118
119     Create( parent, id, value, pos, size, style, validator, name );
120 }
121
122 bool wxTextCtrl::Create( wxWindow *parent,
123                          wxWindowID id,
124                          const wxString &value,
125                          const wxPoint &pos,
126                          const wxSize &size,
127                          long style,
128                          const wxValidator& validator,
129                          const wxString &name )
130 {
131     m_needParent = TRUE;
132     m_acceptsFocus = TRUE;
133
134     if (!PreCreation( parent, pos, size ) ||
135         !CreateBase( parent, id, pos, size, style, validator, name ))
136     {
137         wxFAIL_MSG( wxT("wxTextCtrl creation failed") );
138         return FALSE;
139     }
140
141
142     m_vScrollbarVisible = FALSE;
143
144     bool multi_line = (style & wxTE_MULTILINE) != 0;
145     if (multi_line)
146     {
147 #if (GTK_MINOR_VERSION > 2)
148         /* a multi-line edit control: create a vertical scrollbar by default and
149            horizontal if requested */
150         bool bHasHScrollbar = (style & wxHSCROLL) != 0;
151 #else
152         bool bHasHScrollbar = FALSE;
153 #endif
154
155         /* create our control ... */
156         m_text = gtk_text_new( (GtkAdjustment *) NULL, (GtkAdjustment *) NULL );
157
158         /* ... and put into the upper left hand corner of the table */
159         m_widget = gtk_table_new(bHasHScrollbar ? 2 : 1, 2, FALSE);
160         GTK_WIDGET_UNSET_FLAGS( m_widget, GTK_CAN_FOCUS );
161         gtk_table_attach( GTK_TABLE(m_widget), m_text, 0, 1, 0, 1,
162                       (GtkAttachOptions)(GTK_FILL | GTK_EXPAND | GTK_SHRINK),
163                       (GtkAttachOptions)(GTK_FILL | GTK_EXPAND | GTK_SHRINK),
164                        0, 0);
165
166         /* always wrap words */
167         gtk_text_set_word_wrap( GTK_TEXT(m_text), TRUE );
168
169 #if (GTK_MINOR_VERSION > 2)
170         /* put the horizontal scrollbar in the lower left hand corner */
171         if (bHasHScrollbar)
172         {
173             GtkWidget *hscrollbar = gtk_hscrollbar_new(GTK_TEXT(m_text)->hadj);
174             GTK_WIDGET_UNSET_FLAGS( hscrollbar, GTK_CAN_FOCUS );
175             gtk_table_attach(GTK_TABLE(m_widget), hscrollbar, 0, 1, 1, 2,
176                        (GtkAttachOptions)(GTK_EXPAND | GTK_FILL | GTK_SHRINK),
177                        GTK_FILL,
178                        0, 0);
179             gtk_widget_show(hscrollbar);
180
181             /* don't wrap lines, otherwise we wouldn't need the scrollbar */
182             gtk_text_set_line_wrap( GTK_TEXT(m_text), FALSE );
183         }
184 #endif
185
186         /* finally, put the vertical scrollbar in the upper right corner */
187         m_vScrollbar = gtk_vscrollbar_new( GTK_TEXT(m_text)->vadj );
188         GTK_WIDGET_UNSET_FLAGS( m_vScrollbar, GTK_CAN_FOCUS );
189         gtk_table_attach(GTK_TABLE(m_widget), m_vScrollbar, 1, 2, 0, 1,
190                      GTK_FILL,
191                      (GtkAttachOptions)(GTK_EXPAND | GTK_FILL | GTK_SHRINK),
192                      0, 0);
193     }
194     else
195     {
196         /* a single-line text control: no need for scrollbars */
197         m_widget =
198           m_text = gtk_entry_new();
199     }
200
201     m_parent->DoAddChild( this );
202
203     PostCreation();
204
205     SetFont( parent->GetFont() );
206
207     wxSize size_best( DoGetBestSize() );
208     wxSize new_size( size );
209     if (new_size.x == -1)
210         new_size.x = size_best.x;
211     if (new_size.y == -1)
212         new_size.y = size_best.y;
213     if ((new_size.x != size.x) || (new_size.y != size.y))
214         SetSize( new_size.x, new_size.y );
215
216     if (multi_line)
217         gtk_widget_show(m_text);
218
219     /* we want to be notified about text changes */
220     gtk_signal_connect( GTK_OBJECT(m_text), "changed",
221       GTK_SIGNAL_FUNC(gtk_text_changed_callback), (gpointer)this);
222
223     if (multi_line)
224     {
225         gtk_signal_connect(GTK_OBJECT(GTK_TEXT(m_text)->vadj), "changed",
226           (GtkSignalFunc) gtk_scrollbar_changed_callback, (gpointer) this );
227     }
228
229     if (!value.IsEmpty())
230     {
231         gint tmp = 0;
232
233 #if GTK_MINOR_VERSION == 0
234         // if we don't realize it, GTK 1.0.6 dies with a SIGSEGV in
235         // gtk_editable_insert_text()
236         gtk_widget_realize(m_text);
237 #endif // GTK 1.0
238
239 #if wxUSE_UNICODE
240         wxWX2MBbuf val = value.mbc_str();
241         gtk_editable_insert_text( GTK_EDITABLE(m_text), val, strlen(val), &tmp );
242 #else // !Unicode
243         gtk_editable_insert_text( GTK_EDITABLE(m_text), value, value.Length(), &tmp );
244 #endif // Unicode/!Unicode
245
246         if (multi_line)
247         {
248             /* bring editable's cursor uptodate. bug in GTK. */
249
250             GTK_EDITABLE(m_text)->current_pos = gtk_text_get_point( GTK_TEXT(m_text) );
251         }
252     }
253
254     if (style & wxTE_PASSWORD)
255     {
256         if (!multi_line)
257             gtk_entry_set_visibility( GTK_ENTRY(m_text), FALSE );
258     }
259
260     if (style & wxTE_READONLY)
261     {
262         if (!multi_line)
263             gtk_entry_set_editable( GTK_ENTRY(m_text), FALSE );
264     }
265     else
266     {
267         if (multi_line)
268             gtk_text_set_editable( GTK_TEXT(m_text), 1 );
269     }
270
271     SetBackgroundColour( wxSystemSettings::GetSystemColour(wxSYS_COLOUR_WINDOW) );
272     SetForegroundColour( parent->GetForegroundColour() );
273
274     m_cursor = wxCursor( wxCURSOR_IBEAM );
275
276     Show( TRUE );
277
278     return TRUE;
279 }
280
281 void wxTextCtrl::CalculateScrollbar()
282 {
283     if ((m_windowStyle & wxTE_MULTILINE) == 0) return;
284
285     GtkAdjustment *adj = GTK_TEXT(m_text)->vadj;
286
287     if (adj->upper - adj->page_size < 0.8)
288     {
289         if (m_vScrollbarVisible)
290         {
291             gtk_widget_hide( m_vScrollbar );
292             m_vScrollbarVisible = FALSE;
293         }
294     }
295     else
296     {
297         if (!m_vScrollbarVisible)
298         {
299             gtk_widget_show( m_vScrollbar );
300             m_vScrollbarVisible = TRUE;
301         }
302     }
303 }
304
305 wxString wxTextCtrl::GetValue() const
306 {
307     wxCHECK_MSG( m_text != NULL, wxT(""), wxT("invalid text ctrl") );
308
309     wxString tmp;
310     if (m_windowStyle & wxTE_MULTILINE)
311     {
312         gint len = gtk_text_get_length( GTK_TEXT(m_text) );
313         char *text = gtk_editable_get_chars( GTK_EDITABLE(m_text), 0, len );
314         tmp = wxString(text,*wxConvCurrent);
315         g_free( text );
316     }
317     else
318     {
319         tmp = wxString(gtk_entry_get_text( GTK_ENTRY(m_text) ),*wxConvCurrent);
320     }
321     return tmp;
322 }
323
324 void wxTextCtrl::SetValue( const wxString &value )
325 {
326     wxCHECK_RET( m_text != NULL, wxT("invalid text ctrl") );
327
328     if (m_windowStyle & wxTE_MULTILINE)
329     {
330         gint len = gtk_text_get_length( GTK_TEXT(m_text) );
331         gtk_editable_delete_text( GTK_EDITABLE(m_text), 0, len );
332         len = 0;
333 #if wxUSE_UNICODE
334         wxWX2MBbuf tmpbuf = value.mbc_str();
335         gtk_editable_insert_text( GTK_EDITABLE(m_text), tmpbuf, strlen(tmpbuf), &len );
336 #else
337         gtk_editable_insert_text( GTK_EDITABLE(m_text), value.mbc_str(), value.Length(), &len );
338 #endif
339     }
340     else
341     {
342         gtk_entry_set_text( GTK_ENTRY(m_text), value.mbc_str() );
343     }
344
345     // GRG, Jun/2000: Changed this after a lot of discussion in
346     //   the lists. wxWindows 2.2 will have a set of flags to
347     //   customize this behaviour.
348     SetInsertionPoint(0);
349
350     m_modified = FALSE;
351 }
352
353 void wxTextCtrl::WriteText( const wxString &text )
354 {
355     wxCHECK_RET( m_text != NULL, wxT("invalid text ctrl") );
356
357     if (text.IsEmpty()) return;
358
359     if (m_windowStyle & wxTE_MULTILINE)
360     {
361         /* this moves the cursor pos to behind the inserted text */
362         gint len = GTK_EDITABLE(m_text)->current_pos;
363
364 #if wxUSE_UNICODE
365         wxWX2MBbuf buf = text.mbc_str();
366         gtk_editable_insert_text( GTK_EDITABLE(m_text), buf, strlen(buf), &len );
367 #else
368         gtk_editable_insert_text( GTK_EDITABLE(m_text), text, text.Length(), &len );
369 #endif
370
371         /* bring editable's cursor uptodate. bug in GTK. */
372         GTK_EDITABLE(m_text)->current_pos = gtk_text_get_point( GTK_TEXT(m_text) );
373     }
374     else
375     {
376         /* this moves the cursor pos to behind the inserted text */
377         gint len = GTK_EDITABLE(m_text)->current_pos;
378 #if wxUSE_UNICODE
379         wxWX2MBbuf buf = text.mbc_str();
380         gtk_editable_insert_text( GTK_EDITABLE(m_text), buf, strlen(buf), &len );
381 #else
382         gtk_editable_insert_text( GTK_EDITABLE(m_text), text, text.Length(), &len );
383 #endif
384
385         /* bring editable's cursor uptodate. bug in GTK. */
386         GTK_EDITABLE(m_text)->current_pos += text.Len();
387
388         /* bring entry's cursor uptodate. bug in GTK. */
389         gtk_entry_set_position( GTK_ENTRY(m_text), GTK_EDITABLE(m_text)->current_pos );
390     }
391 }
392
393 void wxTextCtrl::AppendText( const wxString &text )
394 {
395     wxCHECK_RET( m_text != NULL, wxT("invalid text ctrl") );
396
397     if (text.IsEmpty()) return;
398
399     if (m_windowStyle & wxTE_MULTILINE)
400     {
401         bool hasSpecialAttributes = m_font.Ok() ||
402                                     m_foregroundColour.Ok() ||
403                                     m_backgroundColour.Ok();
404         if ( hasSpecialAttributes )
405         {
406              gtk_text_insert( GTK_TEXT(m_text),
407                               m_font.GetInternalFont(),
408                               m_foregroundColour.GetColor(),
409                               m_backgroundColour.GetColor(),
410                               text.mbc_str(), text.length());
411
412         }
413         else
414         {
415             /* we'll insert at the last position */
416             gint len = gtk_text_get_length( GTK_TEXT(m_text) );
417 #if wxUSE_UNICODE
418             wxWX2MBbuf buf = text.mbc_str();
419             gtk_editable_insert_text( GTK_EDITABLE(m_text), buf, strlen(buf), &len );
420 #else
421             gtk_editable_insert_text( GTK_EDITABLE(m_text), text, text.Length(), &len );
422 #endif
423         }
424
425         /* bring editable's cursor uptodate. bug in GTK. */
426         GTK_EDITABLE(m_text)->current_pos = gtk_text_get_point( GTK_TEXT(m_text) );
427     }
428     else
429     {
430         gtk_entry_append_text( GTK_ENTRY(m_text), text.mbc_str() );
431     }
432 }
433
434 wxString wxTextCtrl::GetLineText( long lineNo ) const
435 {
436   if (m_windowStyle & wxTE_MULTILINE)
437   {
438     gint len = gtk_text_get_length( GTK_TEXT(m_text) );
439     char *text = gtk_editable_get_chars( GTK_EDITABLE(m_text), 0, len );
440
441     if (text)
442     {
443         wxString buf(wxT(""));
444         long i;
445         int currentLine = 0;
446         for (i = 0; currentLine != lineNo && text[i]; i++ )
447           if (text[i] == '\n')
448             currentLine++;
449         // Now get the text
450         int j;
451         for (j = 0; text[i] && text[i] != '\n'; i++, j++ )
452             buf += text[i];
453
454         g_free( text );
455         return buf;
456     }
457     else
458       return wxEmptyString;
459   }
460   else
461   {
462     if (lineNo == 0) return GetValue();
463     return wxEmptyString;
464   }
465 }
466
467 void wxTextCtrl::OnDropFiles( wxDropFilesEvent &WXUNUSED(event) )
468 {
469   /* If you implement this, don't forget to update the documentation!
470    * (file docs/latex/wx/text.tex) */
471     wxFAIL_MSG( wxT("wxTextCtrl::OnDropFiles not implemented") );
472 }
473
474 bool wxTextCtrl::PositionToXY(long pos, long *x, long *y ) const
475 {
476     if ( m_windowStyle & wxTE_MULTILINE )
477     {
478         wxString text = GetValue();
479
480         // cast to prevent warning. But pos really should've been unsigned.
481         if( (unsigned long)pos > text.Len()  )
482             return FALSE;
483
484         *x=0;   // First Col
485         *y=0;   // First Line
486
487         const wxChar* stop = text.c_str() + pos;
488         for ( const wxChar *p = text.c_str(); p < stop; p++ )
489         {
490             if (*p == wxT('\n'))
491             {
492                 (*y)++;
493                 *x=0;
494             }
495             else
496                 (*x)++;
497         }
498     }
499     else // single line control
500     {
501         if ( pos <= GTK_ENTRY(m_text)->text_length )
502         {
503             *y = 0;
504             *x = pos;
505         }
506         else
507         {
508             // index out of bounds
509             return FALSE;
510         }
511     }
512
513     return TRUE;
514 }
515
516 long wxTextCtrl::XYToPosition(long x, long y ) const
517 {
518     if (!(m_windowStyle & wxTE_MULTILINE)) return 0;
519
520     long pos=0;
521     for( int i=0; i<y; i++ ) pos += GetLineLength(i) + 1; // one for '\n'
522
523     pos += x;
524     return pos;
525 }
526
527 int wxTextCtrl::GetLineLength(long lineNo) const
528 {
529     wxString str = GetLineText (lineNo);
530     return (int) str.Length();
531 }
532
533 int wxTextCtrl::GetNumberOfLines() const
534 {
535     if (m_windowStyle & wxTE_MULTILINE)
536     {
537         gint len = gtk_text_get_length( GTK_TEXT(m_text) );
538         char *text = gtk_editable_get_chars( GTK_EDITABLE(m_text), 0, len );
539
540         if (text)
541         {
542             int currentLine = 0;
543             for (int i = 0; i < len; i++ )
544             {
545                 if (text[i] == '\n')
546                     currentLine++;
547             }
548             g_free( text );
549
550             // currentLine is 0 based, add 1 to get number of lines
551             return currentLine + 1;
552         }
553         else
554         {
555             return 0;
556         }
557     }
558     else
559     {
560         return 1;
561     }
562 }
563
564 void wxTextCtrl::SetInsertionPoint( long pos )
565 {
566     wxCHECK_RET( m_text != NULL, wxT("invalid text ctrl") );
567
568     if (m_windowStyle & wxTE_MULTILINE)
569     {
570         /* seems to be broken in GTK 1.0.X:
571            gtk_text_set_point( GTK_TEXT(m_text), (int)pos ); */
572
573         gtk_signal_disconnect_by_func( GTK_OBJECT(m_text),
574           GTK_SIGNAL_FUNC(gtk_text_changed_callback), (gpointer)this);
575
576         /* we fake a set_point by inserting and deleting. as the user
577            isn't supposed to get to know about thos non-sense, we
578            disconnect so that no events are sent to the user program. */
579
580         gint tmp = (gint)pos;
581         gtk_editable_insert_text( GTK_EDITABLE(m_text), " ", 1, &tmp );
582         gtk_editable_delete_text( GTK_EDITABLE(m_text), tmp-1, tmp );
583
584         gtk_signal_connect( GTK_OBJECT(m_text), "changed",
585           GTK_SIGNAL_FUNC(gtk_text_changed_callback), (gpointer)this);
586
587         /* bring editable's cursor uptodate. another bug in GTK. */
588
589         GTK_EDITABLE(m_text)->current_pos = gtk_text_get_point( GTK_TEXT(m_text) );
590     }
591     else
592     {
593         gtk_entry_set_position( GTK_ENTRY(m_text), (int)pos );
594
595         /* bring editable's cursor uptodate. bug in GTK. */
596
597         GTK_EDITABLE(m_text)->current_pos = (guint32)pos;
598     }
599 }
600
601 void wxTextCtrl::SetInsertionPointEnd()
602 {
603     wxCHECK_RET( m_text != NULL, wxT("invalid text ctrl") );
604
605     if (m_windowStyle & wxTE_MULTILINE)
606         SetInsertionPoint(gtk_text_get_length(GTK_TEXT(m_text)));
607     else
608         gtk_entry_set_position( GTK_ENTRY(m_text), -1 );
609 }
610
611 void wxTextCtrl::SetEditable( bool editable )
612 {
613     wxCHECK_RET( m_text != NULL, wxT("invalid text ctrl") );
614
615     if (m_windowStyle & wxTE_MULTILINE)
616         gtk_text_set_editable( GTK_TEXT(m_text), editable );
617     else
618         gtk_entry_set_editable( GTK_ENTRY(m_text), editable );
619 }
620
621 bool wxTextCtrl::Enable( bool enable )
622 {
623     if (!wxWindowBase::Enable(enable))
624     {
625         // nothing to do
626         return FALSE;
627     }
628
629     if (m_windowStyle & wxTE_MULTILINE)
630     {
631         gtk_text_set_editable( GTK_TEXT(m_text), enable );
632     }
633     else
634     {
635         gtk_widget_set_sensitive( m_text, enable );
636     }
637
638     return TRUE;
639 }
640
641 void wxTextCtrl::DiscardEdits()
642 {
643     m_modified = FALSE;
644 }
645
646 void wxTextCtrl::SetSelection( long from, long to )
647 {
648     wxCHECK_RET( m_text != NULL, wxT("invalid text ctrl") );
649
650     if ( (m_windowStyle & wxTE_MULTILINE) &&
651          !GTK_TEXT(m_text)->line_start_cache )
652     {
653         // tell the programmer that it didn't work
654         wxLogDebug(_T("Can't call SetSelection() before realizing the control"));
655         return;
656     }
657
658     gtk_editable_select_region( GTK_EDITABLE(m_text), (gint)from, (gint)to );
659 }
660
661 void wxTextCtrl::ShowPosition( long WXUNUSED(pos) )
662 {
663 //    SetInsertionPoint( pos );
664 }
665
666 long wxTextCtrl::GetInsertionPoint() const
667 {
668     wxCHECK_MSG( m_text != NULL, 0, wxT("invalid text ctrl") );
669
670     return (long) GTK_EDITABLE(m_text)->current_pos;
671 }
672
673 long wxTextCtrl::GetLastPosition() const
674 {
675     wxCHECK_MSG( m_text != NULL, 0, wxT("invalid text ctrl") );
676
677     int pos = 0;
678     if (m_windowStyle & wxTE_MULTILINE)
679         pos = gtk_text_get_length( GTK_TEXT(m_text) );
680     else
681         pos = GTK_ENTRY(m_text)->text_length;
682
683     return (long)pos;
684 }
685
686 void wxTextCtrl::Remove( long from, long to )
687 {
688     wxCHECK_RET( m_text != NULL, wxT("invalid text ctrl") );
689
690     gtk_editable_delete_text( GTK_EDITABLE(m_text), (gint)from, (gint)to );
691 }
692
693 void wxTextCtrl::Replace( long from, long to, const wxString &value )
694 {
695     wxCHECK_RET( m_text != NULL, wxT("invalid text ctrl") );
696
697     gtk_editable_delete_text( GTK_EDITABLE(m_text), (gint)from, (gint)to );
698
699     if (!value.IsEmpty())
700     {
701         gint pos = (gint)from;
702 #if wxUSE_UNICODE
703         wxWX2MBbuf buf = value.mbc_str();
704         gtk_editable_insert_text( GTK_EDITABLE(m_text), buf, strlen(buf), &pos );
705 #else
706         gtk_editable_insert_text( GTK_EDITABLE(m_text), value, value.Length(), &pos );
707 #endif
708     }
709 }
710
711 void wxTextCtrl::Cut()
712 {
713     wxCHECK_RET( m_text != NULL, wxT("invalid text ctrl") );
714
715 #if (GTK_MINOR_VERSION > 0)
716     gtk_editable_cut_clipboard( GTK_EDITABLE(m_text) );
717 #else
718     gtk_editable_cut_clipboard( GTK_EDITABLE(m_text), 0 );
719 #endif
720 }
721
722 void wxTextCtrl::Copy()
723 {
724     wxCHECK_RET( m_text != NULL, wxT("invalid text ctrl") );
725
726 #if (GTK_MINOR_VERSION > 0)
727     gtk_editable_copy_clipboard( GTK_EDITABLE(m_text) );
728 #else
729     gtk_editable_copy_clipboard( GTK_EDITABLE(m_text), 0 );
730 #endif
731 }
732
733 void wxTextCtrl::Paste()
734 {
735     wxCHECK_RET( m_text != NULL, wxT("invalid text ctrl") );
736
737 #if (GTK_MINOR_VERSION > 0)
738     gtk_editable_paste_clipboard( GTK_EDITABLE(m_text) );
739 #else
740     gtk_editable_paste_clipboard( GTK_EDITABLE(m_text), 0 );
741 #endif
742 }
743
744 bool wxTextCtrl::CanCopy() const
745 {
746     // Can copy if there's a selection
747     long from, to;
748     GetSelection(& from, & to);
749     return (from != to) ;
750 }
751
752 bool wxTextCtrl::CanCut() const
753 {
754     // Can cut if there's a selection
755     long from, to;
756     GetSelection(& from, & to);
757     return (from != to) && (IsEditable());
758 }
759
760 bool wxTextCtrl::CanPaste() const
761 {
762     return IsEditable() ;
763 }
764
765 // Undo/redo
766 void wxTextCtrl::Undo()
767 {
768     // TODO
769     wxFAIL_MSG( wxT("wxTextCtrl::Undo not implemented") );
770 }
771
772 void wxTextCtrl::Redo()
773 {
774     // TODO
775     wxFAIL_MSG( wxT("wxTextCtrl::Redo not implemented") );
776 }
777
778 bool wxTextCtrl::CanUndo() const
779 {
780     // TODO
781     wxFAIL_MSG( wxT("wxTextCtrl::CanUndo not implemented") );
782     return FALSE;
783 }
784
785 bool wxTextCtrl::CanRedo() const
786 {
787     // TODO
788     wxFAIL_MSG( wxT("wxTextCtrl::CanRedo not implemented") );
789     return FALSE;
790 }
791
792 // If the return values from and to are the same, there is no
793 // selection.
794 void wxTextCtrl::GetSelection(long* from, long* to) const
795 {
796     wxCHECK_RET( m_text != NULL, wxT("invalid text ctrl") );
797
798     if (!(GTK_EDITABLE(m_text)->has_selection))
799     {
800         long i = GetInsertionPoint();
801         if (from) *from = i;
802         if (to)   *to = i;
803         return;
804     }
805
806     if (from) *from = (long) GTK_EDITABLE(m_text)->selection_start_pos;
807     if (to)   *to = (long) GTK_EDITABLE(m_text)->selection_end_pos;
808 }
809
810 bool wxTextCtrl::IsEditable() const
811 {
812     wxCHECK_MSG( m_text != NULL, FALSE, wxT("invalid text ctrl") );
813
814     return GTK_EDITABLE(m_text)->editable;
815 }
816
817 bool wxTextCtrl::IsModified() const
818 {
819     return m_modified;
820 }
821
822 void wxTextCtrl::Clear()
823 {
824     SetValue( wxT("") );
825 }
826
827 void wxTextCtrl::OnChar( wxKeyEvent &key_event )
828 {
829     wxCHECK_RET( m_text != NULL, wxT("invalid text ctrl") );
830
831     if ((key_event.KeyCode() == WXK_RETURN) && (m_windowStyle & wxPROCESS_ENTER))
832     {
833         wxCommandEvent event(wxEVT_COMMAND_TEXT_ENTER, m_windowId);
834         event.SetEventObject(this);
835         event.SetString(GetValue());
836         if (GetEventHandler()->ProcessEvent(event)) return;
837     }
838
839     if ((key_event.KeyCode() == WXK_RETURN) && !(m_windowStyle & wxTE_MULTILINE))
840     {
841         wxWindow *top_frame = m_parent;
842         while (top_frame->GetParent() && !(top_frame->IsTopLevel()))
843             top_frame = top_frame->GetParent();
844         GtkWindow *window = GTK_WINDOW(top_frame->m_widget);
845
846         if (window->default_widget)
847         {
848             gtk_widget_activate (window->default_widget);
849             return;
850         }
851     }
852
853     key_event.Skip();
854 }
855
856 GtkWidget* wxTextCtrl::GetConnectWidget()
857 {
858     return GTK_WIDGET(m_text);
859 }
860
861 bool wxTextCtrl::IsOwnGtkWindow( GdkWindow *window )
862 {
863     if (m_windowStyle & wxTE_MULTILINE)
864         return (window == GTK_TEXT(m_text)->text_area);
865     else
866         return (window == GTK_ENTRY(m_text)->text_area);
867 }
868
869 // the font will change for subsequent text insertiongs
870 bool wxTextCtrl::SetFont( const wxFont &font )
871 {
872     wxCHECK_MSG( m_text != NULL, FALSE, wxT("invalid text ctrl") );
873
874     if ( !wxWindowBase::SetFont(font) )
875     {
876         // font didn't change, nothing to do
877         return FALSE;
878     }
879
880     if ( m_windowStyle & wxTE_MULTILINE )
881     {
882         m_updateFont = TRUE;
883
884         ChangeFontGlobally();
885     }
886
887     return TRUE;
888 }
889
890 void wxTextCtrl::ChangeFontGlobally()
891 {
892     // this method is very inefficient and hence should be called as rarely as
893     // possible!
894     wxASSERT_MSG( (m_windowStyle & wxTE_MULTILINE) && m_updateFont,
895                   _T("shouldn't be called for single line controls") );
896
897     wxString value = GetValue();
898     if ( !value.IsEmpty() )
899     {
900         Clear();
901         AppendText(value);
902
903         m_updateFont = FALSE;
904     }
905 }
906
907 void wxTextCtrl::UpdateFontIfNeeded()
908 {
909     if ( m_updateFont )
910         ChangeFontGlobally();
911 }
912
913 bool wxTextCtrl::SetForegroundColour( const wxColour &WXUNUSED(colour) )
914 {
915     wxCHECK_MSG( m_text != NULL, FALSE, wxT("invalid text ctrl") );
916
917     // doesn't work
918     return FALSE;
919 }
920
921 bool wxTextCtrl::SetBackgroundColour( const wxColour &colour )
922 {
923     wxCHECK_MSG( m_text != NULL, FALSE, wxT("invalid text ctrl") );
924
925     wxControl::SetBackgroundColour( colour );
926
927     if (!m_widget->window)
928         return FALSE;
929
930     wxColour sysbg = wxSystemSettings::GetSystemColour( wxSYS_COLOUR_BTNFACE );
931     if (sysbg.Red() == colour.Red() &&
932         sysbg.Green() == colour.Green() &&
933         sysbg.Blue() == colour.Blue())
934     {
935         return FALSE; // FIXME or TRUE?
936     }
937
938     if (!m_backgroundColour.Ok())
939         return FALSE;
940
941     if (m_windowStyle & wxTE_MULTILINE)
942     {
943         GdkWindow *window = GTK_TEXT(m_text)->text_area;
944         if (!window)
945             return FALSE;
946         m_backgroundColour.CalcPixel( gdk_window_get_colormap( window ) );
947         gdk_window_set_background( window, m_backgroundColour.GetColor() );
948         gdk_window_clear( window );
949     }
950
951     return TRUE;
952 }
953
954 void wxTextCtrl::ApplyWidgetStyle()
955 {
956     if (m_windowStyle & wxTE_MULTILINE)
957     {
958         // how ?
959     }
960     else
961     {
962         SetWidgetStyle();
963         gtk_widget_set_style( m_text, m_widgetStyle );
964     }
965 }
966
967 void wxTextCtrl::OnCut(wxCommandEvent& WXUNUSED(event))
968 {
969     Cut();
970 }
971
972 void wxTextCtrl::OnCopy(wxCommandEvent& WXUNUSED(event))
973 {
974     Copy();
975 }
976
977 void wxTextCtrl::OnPaste(wxCommandEvent& WXUNUSED(event))
978 {
979     Paste();
980 }
981
982 void wxTextCtrl::OnUndo(wxCommandEvent& WXUNUSED(event))
983 {
984     Undo();
985 }
986
987 void wxTextCtrl::OnRedo(wxCommandEvent& WXUNUSED(event))
988 {
989     Redo();
990 }
991
992 void wxTextCtrl::OnUpdateCut(wxUpdateUIEvent& event)
993 {
994     event.Enable( CanCut() );
995 }
996
997 void wxTextCtrl::OnUpdateCopy(wxUpdateUIEvent& event)
998 {
999     event.Enable( CanCopy() );
1000 }
1001
1002 void wxTextCtrl::OnUpdatePaste(wxUpdateUIEvent& event)
1003 {
1004     event.Enable( CanPaste() );
1005 }
1006
1007 void wxTextCtrl::OnUpdateUndo(wxUpdateUIEvent& event)
1008 {
1009     event.Enable( CanUndo() );
1010 }
1011
1012 void wxTextCtrl::OnUpdateRedo(wxUpdateUIEvent& event)
1013 {
1014     event.Enable( CanRedo() );
1015 }
1016
1017 void wxTextCtrl::OnInternalIdle()
1018 {
1019     wxCursor cursor = m_cursor;
1020     if (g_globalCursor.Ok()) cursor = g_globalCursor;
1021
1022     if (cursor.Ok())
1023     {
1024         GdkWindow *window = (GdkWindow*) NULL;
1025         if (HasFlag(wxTE_MULTILINE))
1026             window = GTK_TEXT(m_text)->text_area;
1027         else
1028             window = GTK_ENTRY(m_text)->text_area;
1029
1030         if (window)
1031             gdk_window_set_cursor( window, cursor.GetCursor() );
1032
1033         if (!g_globalCursor.Ok())
1034             cursor = *wxSTANDARD_CURSOR;
1035
1036         window = m_widget->window;
1037         if ((window) && !(GTK_WIDGET_NO_WINDOW(m_widget)))
1038             gdk_window_set_cursor( window, cursor.GetCursor() );
1039     }
1040
1041     UpdateWindowUI();
1042 }
1043
1044 wxSize wxTextCtrl::DoGetBestSize() const
1045 {
1046     // FIXME should be different for multi-line controls...
1047     wxSize ret( wxControl::DoGetBestSize() );
1048     return wxSize(80, ret.y);
1049 }