]> git.saurik.com Git - wxWidgets.git/blob - src/gtk1/textctrl.cpp
temp compilation fix for wxMac
[wxWidgets.git] / src / gtk1 / 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 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
11 #pragma implementation "textctrl.h"
12 #endif
13
14 // For compilers that support precompilation, includes "wx.h".
15 #include "wx/wxprec.h"
16
17 #include "wx/textctrl.h"
18 #include "wx/utils.h"
19 #include "wx/intl.h"
20 #include "wx/log.h"
21 #include "wx/settings.h"
22 #include "wx/panel.h"
23 #include "wx/strconv.h"
24 #include "wx/fontutil.h"        // for wxNativeFontInfo (GetNativeFontInfo())
25
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <ctype.h>
29 #include <math.h>               // for fabs
30
31 #include "wx/gtk/private.h"
32 #include <gdk/gdkkeysyms.h>
33
34 //-----------------------------------------------------------------------------
35 // idle system
36 //-----------------------------------------------------------------------------
37
38 extern void wxapp_install_idle_handler();
39 extern bool g_isIdle;
40
41 //-----------------------------------------------------------------------------
42 // data
43 //-----------------------------------------------------------------------------
44
45 extern bool       g_blockEventsOnDrag;
46 extern wxCursor   g_globalCursor;
47 extern wxWindowGTK *g_delayedFocus;
48
49 // ----------------------------------------------------------------------------
50 // helpers
51 // ----------------------------------------------------------------------------
52
53 #ifdef __WXGTK20__
54 static void wxGtkTextInsert(GtkWidget *text,
55                             GtkTextBuffer *text_buffer,
56                             const wxTextAttr& attr,
57                             wxCharBuffer buffer)
58
59 {
60     PangoFontDescription *font_description = attr.HasFont()
61                          ? attr.GetFont().GetNativeFontInfo()->description
62                          : NULL;
63
64     GdkColor *colFg = attr.HasTextColour() ? attr.GetTextColour().GetColor()
65                                            : NULL;
66
67     GdkColor *colBg = attr.HasBackgroundColour()
68                         ? attr.GetBackgroundColour().GetColor()
69                         : NULL;
70
71     GtkTextTag *tag;
72     tag = gtk_text_buffer_create_tag( text_buffer, NULL, "font-desc", font_description,
73                                      "foreground-gdk", colFg,
74                                      "background-gdk", colBg, NULL );
75
76     GtkTextIter iter;
77     gtk_text_buffer_get_iter_at_mark( text_buffer, &iter,
78                                      gtk_text_buffer_get_insert (text_buffer) );
79
80     gtk_text_buffer_insert_with_tags( text_buffer, &iter, buffer, strlen(buffer), tag, NULL );
81 }
82 #else
83 static void wxGtkTextInsert(GtkWidget *text,
84                             const wxTextAttr& attr,
85                             const char *txt,
86                             size_t len)
87 {
88     GdkFont *font = attr.HasFont() ? attr.GetFont().GetInternalFont()
89                                    : NULL;
90
91     GdkColor *colFg = attr.HasTextColour() ? attr.GetTextColour().GetColor()
92                                            : NULL;
93
94     GdkColor *colBg = attr.HasBackgroundColour()
95                         ? attr.GetBackgroundColour().GetColor()
96                         : NULL;
97
98     gtk_text_insert( GTK_TEXT(text), font, colFg, colBg, txt, len );
99 }
100 #endif // GTK 1.x
101
102 // ----------------------------------------------------------------------------
103 // "insert_text" for GtkEntry
104 // ----------------------------------------------------------------------------
105
106 static void
107 gtk_insert_text_callback(GtkEditable *editable,
108                          const gchar *new_text,
109                          gint new_text_length,
110                          gint *position,
111                          wxTextCtrl *win)
112 {
113     if (g_isIdle)
114         wxapp_install_idle_handler();
115
116     // we should only be called if we have a max len limit at all
117     GtkEntry *entry = GTK_ENTRY (editable);
118
119     wxCHECK_RET( entry->text_max_length, _T("shouldn't be called") );
120
121     // check that we don't overflow the max length limit
122     //
123     // FIXME: this doesn't work when we paste a string which is going to be
124     //        truncated
125     if ( entry->text_length == entry->text_max_length )
126     {
127         // we don't need to run the base class version at all
128         gtk_signal_emit_stop_by_name(GTK_OBJECT(editable), "insert_text");
129
130         // remember that the next changed signal is to be ignored to avoid
131         // generating a dummy wxEVT_COMMAND_TEXT_UPDATED event
132         win->IgnoreNextTextUpdate();
133
134         // and generate the correct one ourselves
135         wxCommandEvent event(wxEVT_COMMAND_TEXT_MAXLEN, win->GetId());
136         event.SetEventObject(win);
137         event.SetString(win->GetValue());
138         win->GetEventHandler()->ProcessEvent( event );
139     }
140 }
141
142 //-----------------------------------------------------------------------------
143 //  "changed"
144 //-----------------------------------------------------------------------------
145
146 static void
147 gtk_text_changed_callback( GtkWidget *widget, wxTextCtrl *win )
148 {
149     if ( win->IgnoreTextUpdate() )
150         return;
151
152     if (!win->m_hasVMT) return;
153
154     if (g_isIdle)
155         wxapp_install_idle_handler();
156
157     win->SetModified();
158 #ifndef __WXGTK20__
159     win->UpdateFontIfNeeded();
160 #endif // !__WXGTK20__
161
162     wxCommandEvent event( wxEVT_COMMAND_TEXT_UPDATED, win->GetId() );
163     event.SetEventObject( win );
164     event.SetString( win->GetValue() );
165     win->GetEventHandler()->ProcessEvent( event );
166 }
167
168 //-----------------------------------------------------------------------------
169 // "changed" from vertical scrollbar
170 //-----------------------------------------------------------------------------
171
172 #ifndef __WXGTK20__
173 static void
174 gtk_scrollbar_changed_callback( GtkWidget *WXUNUSED(widget), wxTextCtrl *win )
175 {
176     if (!win->m_hasVMT) return;
177
178     if (g_isIdle)
179         wxapp_install_idle_handler();
180
181     win->CalculateScrollbar();
182 }
183 #endif
184
185 // ----------------------------------------------------------------------------
186 // redraw callback for multiline text
187 // ----------------------------------------------------------------------------
188
189 #ifndef __WXGTK20__
190
191 // redrawing a GtkText from inside a wxYield() call results in crashes (the
192 // text sample shows it in its "Add lines" command which shows wxProgressDialog
193 // which implicitly calls wxYield()) so we override GtkText::draw() and simply
194 // don't do anything if we're inside wxYield()
195
196 extern bool wxIsInsideYield;
197
198 extern "C" {
199     typedef void (*GtkDrawCallback)(GtkWidget *widget, GdkRectangle *rect);
200 }
201
202 static GtkDrawCallback gs_gtk_text_draw = NULL;
203
204 extern "C"
205 void wxgtk_text_draw( GtkWidget *widget, GdkRectangle *rect)
206 {
207     if ( !wxIsInsideYield )
208     {
209         wxCHECK_RET( gs_gtk_text_draw != wxgtk_text_draw,
210                      _T("infinite recursion in wxgtk_text_draw aborted") );
211
212         gs_gtk_text_draw(widget, rect);
213     }
214 }
215
216 #endif // __WXGTK20__
217
218 //-----------------------------------------------------------------------------
219 //  wxTextCtrl
220 //-----------------------------------------------------------------------------
221
222 IMPLEMENT_DYNAMIC_CLASS(wxTextCtrl,wxControl)
223
224 BEGIN_EVENT_TABLE(wxTextCtrl, wxControl)
225     EVT_CHAR(wxTextCtrl::OnChar)
226
227     EVT_MENU(wxID_CUT, wxTextCtrl::OnCut)
228     EVT_MENU(wxID_COPY, wxTextCtrl::OnCopy)
229     EVT_MENU(wxID_PASTE, wxTextCtrl::OnPaste)
230     EVT_MENU(wxID_UNDO, wxTextCtrl::OnUndo)
231     EVT_MENU(wxID_REDO, wxTextCtrl::OnRedo)
232
233     EVT_UPDATE_UI(wxID_CUT, wxTextCtrl::OnUpdateCut)
234     EVT_UPDATE_UI(wxID_COPY, wxTextCtrl::OnUpdateCopy)
235     EVT_UPDATE_UI(wxID_PASTE, wxTextCtrl::OnUpdatePaste)
236     EVT_UPDATE_UI(wxID_UNDO, wxTextCtrl::OnUpdateUndo)
237     EVT_UPDATE_UI(wxID_REDO, wxTextCtrl::OnUpdateRedo)
238 END_EVENT_TABLE()
239
240 void wxTextCtrl::Init()
241 {
242     m_ignoreNextUpdate =
243     m_modified = FALSE;
244     SetUpdateFont(FALSE);
245     m_text =
246     m_vScrollbar = (GtkWidget *)NULL;
247 }
248
249 wxTextCtrl::wxTextCtrl( wxWindow *parent,
250                         wxWindowID id,
251                         const wxString &value,
252                         const wxPoint &pos,
253                         const wxSize &size,
254                         long style,
255                         const wxValidator& validator,
256                         const wxString &name )
257 {
258     Init();
259
260     Create( parent, id, value, pos, size, style, validator, name );
261 }
262
263 bool wxTextCtrl::Create( wxWindow *parent,
264                          wxWindowID id,
265                          const wxString &value,
266                          const wxPoint &pos,
267                          const wxSize &size,
268                          long style,
269                          const wxValidator& validator,
270                          const wxString &name )
271 {
272     m_needParent = TRUE;
273     m_acceptsFocus = TRUE;
274
275     if (!PreCreation( parent, pos, size ) ||
276         !CreateBase( parent, id, pos, size, style, validator, name ))
277     {
278         wxFAIL_MSG( wxT("wxTextCtrl creation failed") );
279         return FALSE;
280     }
281
282
283     m_vScrollbarVisible = FALSE;
284
285     bool multi_line = (style & wxTE_MULTILINE) != 0;
286
287 #ifdef __WXGTK20__
288     GtkTextBuffer *buffer = NULL;
289 #endif
290
291     if (multi_line)
292     {
293 #ifdef __WXGTK20__
294         // Create view
295         m_text = gtk_text_view_new();
296
297         buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(m_text) );
298
299         // create scrolled window
300         m_widget = gtk_scrolled_window_new( NULL, NULL );
301         gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( m_widget ),
302                                         GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
303
304         // Insert view into scrolled window
305         gtk_container_add( GTK_CONTAINER(m_widget), m_text );
306
307         // Global settings which can be overridden by tags, I guess.
308         if (HasFlag( wxHSCROLL ) || HasFlag( wxTE_DONTWRAP ))
309             gtk_text_view_set_wrap_mode( GTK_TEXT_VIEW( m_text ), GTK_WRAP_NONE );
310         else
311             gtk_text_view_set_wrap_mode( GTK_TEXT_VIEW( m_text ), GTK_WRAP_WORD );
312
313         if (!HasFlag(wxNO_BORDER))
314             gtk_scrolled_window_set_shadow_type( GTK_SCROLLED_WINDOW(m_widget), GTK_SHADOW_IN );
315             
316         GTK_WIDGET_UNSET_FLAGS( m_widget, GTK_CAN_FOCUS );
317 #else
318         // create our control ...
319         m_text = gtk_text_new( (GtkAdjustment *) NULL, (GtkAdjustment *) NULL );
320
321         // ... and put into the upper left hand corner of the table
322         bool bHasHScrollbar = FALSE;
323         m_widget = gtk_table_new(bHasHScrollbar ? 2 : 1, 2, FALSE);
324         GTK_WIDGET_UNSET_FLAGS( m_widget, GTK_CAN_FOCUS );
325         gtk_table_attach( GTK_TABLE(m_widget), m_text, 0, 1, 0, 1,
326                       (GtkAttachOptions)(GTK_FILL | GTK_EXPAND | GTK_SHRINK),
327                       (GtkAttachOptions)(GTK_FILL | GTK_EXPAND | GTK_SHRINK),
328                        0, 0);
329
330         // always wrap words
331         gtk_text_set_word_wrap( GTK_TEXT(m_text), TRUE );
332
333         // finally, put the vertical scrollbar in the upper right corner
334         m_vScrollbar = gtk_vscrollbar_new( GTK_TEXT(m_text)->vadj );
335         GTK_WIDGET_UNSET_FLAGS( m_vScrollbar, GTK_CAN_FOCUS );
336         gtk_table_attach(GTK_TABLE(m_widget), m_vScrollbar, 1, 2, 0, 1,
337                      GTK_FILL,
338                      (GtkAttachOptions)(GTK_EXPAND | GTK_FILL | GTK_SHRINK),
339                      0, 0);
340 #endif
341     }
342     else
343     {
344         // a single-line text control: no need for scrollbars
345         m_widget =
346         m_text = gtk_entry_new();
347     }
348
349     m_parent->DoAddChild( this );
350
351     m_focusWidget = m_text;
352
353     PostCreation(size);
354
355     if (multi_line)
356         gtk_widget_show(m_text);
357
358 #ifndef __WXGTK20__
359     if (multi_line)
360     {
361         gtk_signal_connect(GTK_OBJECT(GTK_TEXT(m_text)->vadj), "changed",
362           (GtkSignalFunc) gtk_scrollbar_changed_callback, (gpointer) this );
363
364         // only initialize gs_gtk_text_draw once, starting from the next the
365         // klass::draw will already be wxgtk_text_draw
366         if ( !gs_gtk_text_draw )
367         {
368             GtkDrawCallback&
369                 draw = GTK_WIDGET_CLASS(GTK_OBJECT(m_text)->klass)->draw;
370
371             gs_gtk_text_draw = draw;
372
373             draw = wxgtk_text_draw;
374         }
375     }
376 #endif // GTK+ 1.x
377
378     if (!value.IsEmpty())
379     {
380 #ifdef __WXGTK20__
381         SetValue( value );
382 #else
383
384 #if !GTK_CHECK_VERSION(1, 2, 0)
385         // if we don't realize it, GTK 1.0.6 dies with a SIGSEGV in
386         // gtk_editable_insert_text()
387         gtk_widget_realize(m_text);
388 #endif // GTK 1.0
389
390         gint tmp = 0;
391 #if wxUSE_UNICODE
392         wxWX2MBbuf val = value.mbc_str();
393         gtk_editable_insert_text( GTK_EDITABLE(m_text), val, strlen(val), &tmp );
394 #else
395         gtk_editable_insert_text( GTK_EDITABLE(m_text), value, value.Length(), &tmp );
396 #endif
397
398         if (multi_line)
399         {
400             // Bring editable's cursor uptodate. Bug in GTK.
401             SET_EDITABLE_POS(m_text, gtk_text_get_point( GTK_TEXT(m_text) ));
402         }
403
404 #endif
405     }
406
407     if (style & wxTE_PASSWORD)
408     {
409         if (!multi_line)
410             gtk_entry_set_visibility( GTK_ENTRY(m_text), FALSE );
411     }
412
413     if (style & wxTE_READONLY)
414     {
415         if (!multi_line)
416             gtk_entry_set_editable( GTK_ENTRY(m_text), FALSE );
417 #ifdef __WXGTK20__
418         else
419             gtk_text_view_set_editable( GTK_TEXT_VIEW( m_text), FALSE);
420 #else
421     }
422     else
423     {
424         if (multi_line)
425             gtk_text_set_editable( GTK_TEXT(m_text), 1 );
426 #endif
427     }
428
429 #ifdef __WXGTK20__
430     if (multi_line)
431     {
432         if (style & wxTE_RIGHT)
433             gtk_text_view_set_justification( GTK_TEXT_VIEW(m_text), GTK_JUSTIFY_RIGHT );
434         else if (style & wxTE_CENTRE)
435             gtk_text_view_set_justification( GTK_TEXT_VIEW(m_text), GTK_JUSTIFY_CENTER );
436         // Left justify (alignment) is the default and we don't need to apply GTK_JUSTIFY_LEFT
437     }
438     // gtk_entry_set_alignment was introduced in gtk+-2.3.5
439 #if GTK_CHECK_VERSION(2, 3, 5)
440     else
441     {
442         if (style & wxTE_RIGHT)
443             gtk_entry_set_alignment( GTK_ENTRY(m_text), 1.0 );
444         else if (style & wxTE_CENTRE)
445             gtk_entry_set_alignment( GTK_ENTRY(m_text), 0.5 );
446     }
447 #endif // gtk+-2.3.5
448 #endif // __WXGTK20__
449     
450     // We want to be notified about text changes.
451 #ifdef __WXGTK20__
452     if (multi_line)
453     {
454         g_signal_connect( G_OBJECT(buffer), "changed",
455             GTK_SIGNAL_FUNC(gtk_text_changed_callback), (gpointer)this);
456     }
457     else
458 #endif
459     
460     {
461         gtk_signal_connect( GTK_OBJECT(m_text), "changed",
462             GTK_SIGNAL_FUNC(gtk_text_changed_callback), (gpointer)this);
463     }
464
465     m_cursor = wxCursor( wxCURSOR_IBEAM );
466
467     wxTextAttr attrDef(GetForegroundColour(), GetBackgroundColour(), GetFont());
468     SetDefaultStyle( attrDef );
469
470     return TRUE;
471 }
472
473
474 void wxTextCtrl::CalculateScrollbar()
475 {
476 #ifndef __WXGTK20__
477     if ((m_windowStyle & wxTE_MULTILINE) == 0) return;
478
479     GtkAdjustment *adj = GTK_TEXT(m_text)->vadj;
480
481     if (adj->upper - adj->page_size < 0.8)
482     {
483         if (m_vScrollbarVisible)
484         {
485             gtk_widget_hide( m_vScrollbar );
486             m_vScrollbarVisible = FALSE;
487         }
488     }
489     else
490     {
491         if (!m_vScrollbarVisible)
492         {
493             gtk_widget_show( m_vScrollbar );
494             m_vScrollbarVisible = TRUE;
495         }
496     }
497 #endif
498 }
499
500 wxString wxTextCtrl::GetValue() const
501 {
502     wxCHECK_MSG( m_text != NULL, wxT(""), wxT("invalid text ctrl") );
503
504     wxString tmp;
505     if (m_windowStyle & wxTE_MULTILINE)
506     {
507 #ifdef __WXGTK20__
508         GtkTextBuffer *text_buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(m_text) );
509
510         GtkTextIter start;
511         gtk_text_buffer_get_start_iter( text_buffer, &start );
512         GtkTextIter end;
513         gtk_text_buffer_get_end_iter( text_buffer, &end );
514         gchar *text = gtk_text_buffer_get_text( text_buffer, &start, &end, TRUE );
515
516 #if wxUSE_UNICODE
517         wxWCharBuffer buffer( wxConvUTF8.cMB2WX( text ) );
518 #else
519         wxCharBuffer buffer( wxConvLocal.cWC2WX( wxConvUTF8.cMB2WC( text ) ) );
520 #endif
521         tmp = buffer;
522
523         g_free( text );
524 #else
525         gint len = gtk_text_get_length( GTK_TEXT(m_text) );
526         char *text = gtk_editable_get_chars( GTK_EDITABLE(m_text), 0, len );
527         tmp = text;
528         g_free( text );
529 #endif
530     }
531     else
532     {
533         tmp = wxGTK_CONV_BACK( gtk_entry_get_text( GTK_ENTRY(m_text) ) );
534     }
535
536     return tmp;
537 }
538
539 void wxTextCtrl::SetValue( const wxString &value )
540 {
541     wxCHECK_RET( m_text != NULL, wxT("invalid text ctrl") );
542
543     if (m_windowStyle & wxTE_MULTILINE)
544     {
545 #ifdef __WXGTK20__
546
547 #if wxUSE_UNICODE
548         wxCharBuffer buffer( wxConvUTF8.cWX2MB( value) );
549 #else
550         wxCharBuffer buffer( wxConvUTF8.cWC2MB( wxConvLocal.cWX2WC( value ) ) );
551 #endif
552         GtkTextBuffer *text_buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(m_text) );
553         gtk_text_buffer_set_text( text_buffer, buffer, strlen(buffer) );
554
555 #else
556         gint len = gtk_text_get_length( GTK_TEXT(m_text) );
557         gtk_editable_delete_text( GTK_EDITABLE(m_text), 0, len );
558         len = 0;
559         gtk_editable_insert_text( GTK_EDITABLE(m_text), value.mbc_str(), value.Length(), &len );
560 #endif
561     }
562     else
563     {
564         gtk_entry_set_text( GTK_ENTRY(m_text), wxGTK_CONV( value ) );
565     }
566
567     // GRG, Jun/2000: Changed this after a lot of discussion in
568     //   the lists. wxWidgets 2.2 will have a set of flags to
569     //   customize this behaviour.
570     SetInsertionPoint(0);
571
572     m_modified = FALSE;
573 }
574
575 void wxTextCtrl::WriteText( const wxString &text )
576 {
577     wxCHECK_RET( m_text != NULL, wxT("invalid text ctrl") );
578
579     if ( text.empty() )
580         return;
581
582     // gtk_text_changed_callback() will set m_modified to true but m_modified
583     // shouldn't be changed by the program writing to the text control itself,
584     // so save the old value and restore when we're done
585     bool oldModified = m_modified;
586
587     if ( m_windowStyle & wxTE_MULTILINE )
588     {
589 #ifdef __WXGTK20__
590
591 #if wxUSE_UNICODE
592         wxCharBuffer buffer( wxConvUTF8.cWX2MB( text ) );
593 #else
594         wxCharBuffer buffer( wxConvUTF8.cWC2MB( wxConvLocal.cWX2WC( text ) ) );
595 #endif
596         if ( !buffer )
597         {
598             // what else can we do? at least don't crash...
599             return;
600         }
601
602         GtkTextBuffer *text_buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(m_text) );
603
604         // TODO: Call whatever is needed to delete the selection.
605         wxGtkTextInsert( m_text, text_buffer, m_defaultStyle, buffer );
606
607         GtkAdjustment *adj = gtk_scrolled_window_get_vadjustment( GTK_SCROLLED_WINDOW(m_widget) );
608         // Scroll to cursor, but only if scrollbar thumb is at the very bottom
609         if ( adj->value == adj->upper - adj->page_size )
610         {
611             gtk_text_view_scroll_to_mark( GTK_TEXT_VIEW(m_text),
612                     gtk_text_buffer_get_insert( text_buffer ), 0.0, FALSE, 0.0, 1.0 );
613         }
614 #else // GTK 1.x
615         // After cursor movements, gtk_text_get_point() is wrong by one.
616         gtk_text_set_point( GTK_TEXT(m_text), GET_EDITABLE_POS(m_text) );
617
618         // always use m_defaultStyle, even if it is empty as otherwise
619         // resetting the style and appending some more text wouldn't work: if
620         // we don't specify the style explicitly, the old style would be used
621         gtk_editable_delete_selection( GTK_EDITABLE(m_text) );
622         wxGtkTextInsert(m_text, m_defaultStyle, text.c_str(), text.Len());
623
624         // we called wxGtkTextInsert with correct font, no need to do anything
625         // in UpdateFontIfNeeded() any longer
626         if ( !text.empty() )
627         {
628             SetUpdateFont(FALSE);
629         }
630
631         // Bring editable's cursor back uptodate.
632         SET_EDITABLE_POS(m_text, gtk_text_get_point( GTK_TEXT(m_text) ));
633 #endif // GTK 1.x/2.0
634     }
635     else // single line
636     {
637         // First remove the selection if there is one
638         gtk_editable_delete_selection( GTK_EDITABLE(m_text) );
639
640         // This moves the cursor pos to behind the inserted text.
641         gint len = GET_EDITABLE_POS(m_text);
642
643 #ifdef __WXGTK20__
644
645 #if wxUSE_UNICODE
646         wxCharBuffer buffer( wxConvUTF8.cWX2MB( text ) );
647 #else
648         wxCharBuffer buffer( wxConvUTF8.cWC2MB( wxConvLocal.cWX2WC( text ) ) );
649 #endif
650         gtk_editable_insert_text( GTK_EDITABLE(m_text), buffer, strlen(buffer), &len );
651
652 #else
653         gtk_editable_insert_text( GTK_EDITABLE(m_text), text.c_str(), text.Len(), &len );
654 #endif
655
656         // Bring entry's cursor uptodate.
657         gtk_entry_set_position( GTK_ENTRY(m_text), len );
658     }
659
660     m_modified = oldModified;
661 }
662
663 void wxTextCtrl::AppendText( const wxString &text )
664 {
665     SetInsertionPointEnd();
666     WriteText( text );
667 }
668
669 wxString wxTextCtrl::GetLineText( long lineNo ) const
670 {
671     if (m_windowStyle & wxTE_MULTILINE)
672     {
673 #ifndef __WXGTK20__
674         gint len = gtk_text_get_length( GTK_TEXT(m_text) );
675         char *text = gtk_editable_get_chars( GTK_EDITABLE(m_text), 0, len );
676
677         if (text)
678         {
679             wxString buf(wxT(""));
680             long i;
681             int currentLine = 0;
682             for (i = 0; currentLine != lineNo && text[i]; i++ )
683                 if (text[i] == '\n')
684             currentLine++;
685             // Now get the text
686             int j;
687             for (j = 0; text[i] && text[i] != '\n'; i++, j++ )
688                 buf += text[i];
689
690             g_free( text );
691             return buf;
692         }
693         else
694         {
695             return wxEmptyString;
696         }
697 #else
698         GtkTextBuffer *text_buffer;
699         text_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(m_text));
700         GtkTextIter line;
701         gtk_text_buffer_get_iter_at_line(text_buffer,&line,lineNo);
702         GtkTextIter end;
703         gtk_text_buffer_get_end_iter(text_buffer,&end );
704         gchar *text = gtk_text_buffer_get_text(text_buffer,&line,&end,TRUE);
705         wxString result(wxGTK_CONV_BACK(text));
706         g_free(text);
707         return result.BeforeFirst(wxT('\n'));
708 #endif
709     }
710     else
711     {
712         if (lineNo == 0) return GetValue();
713         return wxEmptyString;
714     }
715 }
716
717 void wxTextCtrl::OnDropFiles( wxDropFilesEvent &WXUNUSED(event) )
718 {
719   /* If you implement this, don't forget to update the documentation!
720    * (file docs/latex/wx/text.tex) */
721     wxFAIL_MSG( wxT("wxTextCtrl::OnDropFiles not implemented") );
722 }
723
724 bool wxTextCtrl::PositionToXY(long pos, long *x, long *y ) const
725 {
726     if ( m_windowStyle & wxTE_MULTILINE )
727     {
728         wxString text = GetValue();
729
730         // cast to prevent warning. But pos really should've been unsigned.
731         if( (unsigned long)pos > text.Len()  )
732             return FALSE;
733
734         *x=0;   // First Col
735         *y=0;   // First Line
736
737         const wxChar* stop = text.c_str() + pos;
738         for ( const wxChar *p = text.c_str(); p < stop; p++ )
739         {
740             if (*p == wxT('\n'))
741             {
742                 (*y)++;
743                 *x=0;
744             }
745             else
746                 (*x)++;
747         }
748     }
749     else // single line control
750     {
751         if ( pos <= GTK_ENTRY(m_text)->text_length )
752         {
753             *y = 0;
754             *x = pos;
755         }
756         else
757         {
758             // index out of bounds
759             return FALSE;
760         }
761     }
762
763     return TRUE;
764 }
765
766 long wxTextCtrl::XYToPosition(long x, long y ) const
767 {
768     if (!(m_windowStyle & wxTE_MULTILINE)) return 0;
769
770     long pos=0;
771     for( int i=0; i<y; i++ ) pos += GetLineLength(i) + 1; // one for '\n'
772
773     pos += x;
774     return pos;
775 }
776
777 int wxTextCtrl::GetLineLength(long lineNo) const
778 {
779     wxString str = GetLineText (lineNo);
780     return (int) str.Length();
781 }
782
783 int wxTextCtrl::GetNumberOfLines() const
784 {
785     if (m_windowStyle & wxTE_MULTILINE)
786     {
787 #ifdef __WXGTK20__
788         GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(m_text) );
789
790         return gtk_text_buffer_get_line_count( buffer );
791 #else
792         gint len = gtk_text_get_length( GTK_TEXT(m_text) );
793         char *text = gtk_editable_get_chars( GTK_EDITABLE(m_text), 0, len );
794
795         if (text)
796         {
797             int currentLine = 0;
798             for (int i = 0; i < len; i++ )
799             {
800                 if (text[i] == '\n')
801                     currentLine++;
802             }
803             g_free( text );
804
805             // currentLine is 0 based, add 1 to get number of lines
806             return currentLine + 1;
807         }
808         else
809         {
810             return 0;
811         }
812 #endif
813     }
814     else
815     {
816         return 1;
817     }
818 }
819
820 void wxTextCtrl::SetInsertionPoint( long pos )
821 {
822     wxCHECK_RET( m_text != NULL, wxT("invalid text ctrl") );
823
824     if ( IsMultiLine() )
825     {
826 #ifdef __WXGTK20__
827         GtkTextBuffer *text_buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(m_text) );
828         GtkTextIter iter;
829         gtk_text_buffer_get_iter_at_offset( text_buffer, &iter, pos );
830         gtk_text_buffer_place_cursor( text_buffer, &iter );
831         gtk_text_view_scroll_mark_onscreen
832         (
833             GTK_TEXT_VIEW(m_text),
834             gtk_text_buffer_get_insert( text_buffer )
835         );
836 #else // GTK+ 1.x
837         gtk_signal_disconnect_by_func( GTK_OBJECT(m_text),
838           GTK_SIGNAL_FUNC(gtk_text_changed_callback), (gpointer)this);
839
840         /* we fake a set_point by inserting and deleting. as the user
841            isn't supposed to get to know about this non-sense, we
842            disconnect so that no events are sent to the user program. */
843
844         gint tmp = (gint)pos;
845         gtk_editable_insert_text( GTK_EDITABLE(m_text), " ", 1, &tmp );
846         gtk_editable_delete_text( GTK_EDITABLE(m_text), tmp-1, tmp );
847
848         gtk_signal_connect( GTK_OBJECT(m_text), "changed",
849           GTK_SIGNAL_FUNC(gtk_text_changed_callback), (gpointer)this);
850
851         // bring editable's cursor uptodate. Bug in GTK.
852         SET_EDITABLE_POS(m_text, gtk_text_get_point( GTK_TEXT(m_text) ));
853 #endif // GTK+ 2/1
854     }
855     else
856     {
857         gtk_entry_set_position( GTK_ENTRY(m_text), (int)pos );
858
859         // Bring editable's cursor uptodate. Bug in GTK.
860         SET_EDITABLE_POS(m_text, (guint32)pos);
861     }
862 }
863
864 void wxTextCtrl::SetInsertionPointEnd()
865 {
866     wxCHECK_RET( m_text != NULL, wxT("invalid text ctrl") );
867
868     if (m_windowStyle & wxTE_MULTILINE)
869     {
870 #ifdef __WXGTK20__
871         GtkTextBuffer *text_buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(m_text) );
872         GtkTextIter end;
873         gtk_text_buffer_get_end_iter( text_buffer, &end );
874         gtk_text_buffer_place_cursor( text_buffer, &end );
875 #else
876         SetInsertionPoint(gtk_text_get_length(GTK_TEXT(m_text)));
877 #endif
878     }
879     else
880     {
881         gtk_entry_set_position( GTK_ENTRY(m_text), -1 );
882     }
883 }
884
885 void wxTextCtrl::SetEditable( bool editable )
886 {
887     wxCHECK_RET( m_text != NULL, wxT("invalid text ctrl") );
888
889     if (m_windowStyle & wxTE_MULTILINE)
890     {
891 #ifdef __WXGTK20__
892         gtk_text_view_set_editable( GTK_TEXT_VIEW(m_text), editable );
893 #else
894         gtk_text_set_editable( GTK_TEXT(m_text), editable );
895 #endif
896     }
897     else
898     {
899         gtk_entry_set_editable( GTK_ENTRY(m_text), editable );
900     }
901 }
902
903 bool wxTextCtrl::Enable( bool enable )
904 {
905     if (!wxWindowBase::Enable(enable))
906     {
907         // nothing to do
908         return FALSE;
909     }
910
911     if (m_windowStyle & wxTE_MULTILINE)
912     {
913 #ifdef __WXGTK20__
914         SetEditable( enable );
915 #else
916         gtk_text_set_editable( GTK_TEXT(m_text), enable );
917         OnParentEnable(enable);
918 #endif
919     }
920     else
921     {
922         gtk_widget_set_sensitive( m_text, enable );
923     }
924
925     return TRUE;
926 }
927
928 // wxGTK-specific: called recursively by Enable,
929 // to give widgets an oppprtunity to correct their colours after they
930 // have been changed by Enable
931 void wxTextCtrl::OnParentEnable( bool enable )
932 {
933     // If we have a custom background colour, we use this colour in both
934     // disabled and enabled mode, or we end up with a different colour under the
935     // text.
936     wxColour oldColour = GetBackgroundColour();
937     if (oldColour.Ok())
938     {
939         // Need to set twice or it'll optimize the useful stuff out
940         if (oldColour == * wxWHITE)
941             SetBackgroundColour(*wxBLACK);
942         else
943             SetBackgroundColour(*wxWHITE);
944         SetBackgroundColour(oldColour);
945     }
946 }
947
948 void wxTextCtrl::MarkDirty()
949 {
950     m_modified = TRUE;
951 }
952
953 void wxTextCtrl::DiscardEdits()
954 {
955     m_modified = FALSE;
956 }
957
958 // ----------------------------------------------------------------------------
959 // max text length support
960 // ----------------------------------------------------------------------------
961
962 void wxTextCtrl::IgnoreNextTextUpdate()
963 {
964     m_ignoreNextUpdate = TRUE;
965 }
966
967 bool wxTextCtrl::IgnoreTextUpdate()
968 {
969     if ( m_ignoreNextUpdate )
970     {
971         m_ignoreNextUpdate = FALSE;
972
973         return TRUE;
974     }
975
976     return FALSE;
977 }
978
979 void wxTextCtrl::SetMaxLength(unsigned long len)
980 {
981     if ( !HasFlag(wxTE_MULTILINE) )
982     {
983         gtk_entry_set_max_length(GTK_ENTRY(m_text), len);
984
985         // there is a bug in GTK+ 1.2.x: "changed" signal is emitted even if
986         // we had tried to enter more text than allowed by max text length and
987         // the text wasn't really changed
988         //
989         // to detect this and generate TEXT_MAXLEN event instead of
990         // TEXT_CHANGED one in this case we also catch "insert_text" signal
991         //
992         // when max len is set to 0 we disconnect our handler as it means that
993         // we shouldn't check anything any more
994         if ( len )
995         {
996             gtk_signal_connect( GTK_OBJECT(m_text),
997                                 "insert_text",
998                                 GTK_SIGNAL_FUNC(gtk_insert_text_callback),
999                                 (gpointer)this);
1000         }
1001         else // no checking
1002         {
1003             gtk_signal_disconnect_by_func
1004             (
1005                 GTK_OBJECT(m_text),
1006                 GTK_SIGNAL_FUNC(gtk_insert_text_callback),
1007                 (gpointer)this
1008             );
1009         }
1010     }
1011 }
1012
1013 void wxTextCtrl::SetSelection( long from, long to )
1014 {
1015     wxCHECK_RET( m_text != NULL, wxT("invalid text ctrl") );
1016
1017     if (from == -1 && to == -1)
1018     {
1019         from = 0;
1020         to = GetValue().Length();
1021     }
1022
1023 #ifndef __WXGTK20__
1024     if ( (m_windowStyle & wxTE_MULTILINE) &&
1025          !GTK_TEXT(m_text)->line_start_cache )
1026     {
1027         // tell the programmer that it didn't work
1028         wxLogDebug(_T("Can't call SetSelection() before realizing the control"));
1029         return;
1030     }
1031 #endif
1032
1033     if (m_windowStyle & wxTE_MULTILINE)
1034     {
1035 #ifdef __WXGTK20__
1036         GtkTextBuffer *buf = gtk_text_view_get_buffer( GTK_TEXT_VIEW(m_text) );
1037
1038         GtkTextIter fromi, toi;
1039         gtk_text_buffer_get_iter_at_offset( buf, &fromi, from );
1040         gtk_text_buffer_get_iter_at_offset( buf, &toi, to );
1041
1042         gtk_text_buffer_place_cursor( buf, &toi );
1043         gtk_text_buffer_move_mark_by_name( buf, "selection_bound", &fromi );
1044 #else
1045         gtk_editable_select_region( GTK_EDITABLE(m_text), (gint)from, (gint)to );
1046 #endif
1047     }
1048     else
1049     {
1050         gtk_editable_select_region( GTK_EDITABLE(m_text), (gint)from, (gint)to );
1051     }
1052 }
1053
1054 void wxTextCtrl::ShowPosition( long pos )
1055 {
1056     if (m_windowStyle & wxTE_MULTILINE)
1057     {
1058 #ifdef __WXGTK20__
1059         GtkTextBuffer *buf = gtk_text_view_get_buffer( GTK_TEXT_VIEW(m_text) );
1060         GtkTextIter iter;
1061         gtk_text_buffer_get_start_iter( buf, &iter );
1062         gtk_text_iter_set_offset( &iter, pos );
1063         GtkTextMark *mark = gtk_text_buffer_create_mark( buf, NULL, &iter, TRUE );
1064         gtk_text_view_scroll_to_mark( GTK_TEXT_VIEW(m_text), mark, 0.0, FALSE, 0.0, 0.0 );
1065 #else // GTK 1.x
1066         GtkAdjustment *vp = GTK_TEXT(m_text)->vadj;
1067         float totalLines =  (float) GetNumberOfLines();
1068         long posX;
1069         long posY;
1070         PositionToXY(pos, &posX, &posY);
1071         float posLine = (float) posY;
1072         float p = (posLine/totalLines)*(vp->upper - vp->lower) + vp->lower;
1073         gtk_adjustment_set_value(GTK_TEXT(m_text)->vadj, p);
1074 #endif // GTK 1.x/2.x
1075     }
1076 }
1077
1078 #ifdef __WXGTK20__
1079
1080 wxTextCtrlHitTestResult
1081 wxTextCtrl::HitTest(const wxPoint& pt, long *pos) const
1082 {
1083     if ( !IsMultiLine() )
1084     {
1085         // not supported
1086         return wxTE_HT_UNKNOWN;
1087     }
1088
1089     int x, y;
1090     gtk_text_view_window_to_buffer_coords
1091     (
1092         GTK_TEXT_VIEW(m_text),
1093         GTK_TEXT_WINDOW_TEXT,
1094         pt.x, pt.y,
1095         &x, &y
1096     );
1097
1098     GtkTextIter iter;
1099     gtk_text_view_get_iter_at_location(GTK_TEXT_VIEW(m_text), &iter, x, y);
1100     if ( pos )
1101         *pos = gtk_text_iter_get_offset(&iter);
1102
1103     return wxTE_HT_ON_TEXT;
1104 }
1105
1106 #endif // __WXGTK20__
1107
1108 long wxTextCtrl::GetInsertionPoint() const
1109 {
1110     wxCHECK_MSG( m_text != NULL, 0, wxT("invalid text ctrl") );
1111
1112 #ifdef __WXGTK20__
1113     if (m_windowStyle & wxTE_MULTILINE)
1114     {
1115         GtkTextBuffer *text_buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(m_text) );
1116
1117         // There is no direct accessor for the cursor, but
1118         // internally, the cursor is the "mark" called
1119         // "insert" in the text view's btree structure.
1120
1121         GtkTextMark *mark = gtk_text_buffer_get_insert( text_buffer );
1122         GtkTextIter cursor;
1123         gtk_text_buffer_get_iter_at_mark( text_buffer, &cursor, mark );
1124
1125         return gtk_text_iter_get_offset( &cursor );
1126     }
1127     else
1128 #endif
1129     {
1130     return (long) GET_EDITABLE_POS(m_text);
1131     }
1132 }
1133
1134 long wxTextCtrl::GetLastPosition() const
1135 {
1136     wxCHECK_MSG( m_text != NULL, 0, wxT("invalid text ctrl") );
1137
1138     int pos = 0;
1139
1140     if (m_windowStyle & wxTE_MULTILINE)
1141     {
1142 #ifdef __WXGTK20__
1143         GtkTextBuffer *text_buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(m_text) );
1144         GtkTextIter end;
1145         gtk_text_buffer_get_end_iter( text_buffer, &end );
1146
1147         pos = gtk_text_iter_get_offset( &end );
1148 #else
1149         pos = gtk_text_get_length( GTK_TEXT(m_text) );
1150 #endif
1151     }
1152     else
1153     {
1154         pos = GTK_ENTRY(m_text)->text_length;
1155     }
1156
1157     return (long)pos;
1158 }
1159
1160 void wxTextCtrl::Remove( long from, long to )
1161 {
1162     wxCHECK_RET( m_text != NULL, wxT("invalid text ctrl") );
1163
1164 #ifdef __WXGTK20__
1165     if (m_windowStyle & wxTE_MULTILINE)
1166     {
1167         GtkTextBuffer *
1168             text_buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(m_text) );
1169
1170         GtkTextIter fromi, toi;
1171         gtk_text_buffer_get_iter_at_offset( text_buffer, &fromi, from );
1172         gtk_text_buffer_get_iter_at_offset( text_buffer, &toi, to );
1173
1174         gtk_text_buffer_delete( text_buffer, &fromi, &toi );
1175     }
1176     else // single line
1177 #endif
1178     gtk_editable_delete_text( GTK_EDITABLE(m_text), (gint)from, (gint)to );
1179 }
1180
1181 void wxTextCtrl::Replace( long from, long to, const wxString &value )
1182 {
1183     wxCHECK_RET( m_text != NULL, wxT("invalid text ctrl") );
1184
1185     Remove( from, to );
1186
1187     if (!value.IsEmpty())
1188     {
1189 #ifdef __WXGTK20__
1190         SetInsertionPoint( from );
1191         WriteText( value );
1192 #else // GTK 1.x
1193         gint pos = (gint)from;
1194 #if wxUSE_UNICODE
1195         wxWX2MBbuf buf = value.mbc_str();
1196         gtk_editable_insert_text( GTK_EDITABLE(m_text), buf, strlen(buf), &pos );
1197 #else
1198         gtk_editable_insert_text( GTK_EDITABLE(m_text), value, value.Length(), &pos );
1199 #endif // wxUSE_UNICODE
1200 #endif // GTK 1.x/2.x
1201     }
1202 }
1203
1204 void wxTextCtrl::Cut()
1205 {
1206     wxCHECK_RET( m_text != NULL, wxT("invalid text ctrl") );
1207
1208 #ifdef __WXGTK20__
1209     if (m_windowStyle & wxTE_MULTILINE)
1210         g_signal_emit_by_name(m_text, "cut-clipboard");
1211     else
1212 #endif
1213         gtk_editable_cut_clipboard(GTK_EDITABLE(m_text) DUMMY_CLIPBOARD_ARG);
1214 }
1215
1216 void wxTextCtrl::Copy()
1217 {
1218     wxCHECK_RET( m_text != NULL, wxT("invalid text ctrl") );
1219
1220 #ifdef __WXGTK20__
1221     if (m_windowStyle & wxTE_MULTILINE)
1222         g_signal_emit_by_name(m_text, "copy-clipboard");
1223     else
1224 #endif
1225         gtk_editable_copy_clipboard(GTK_EDITABLE(m_text) DUMMY_CLIPBOARD_ARG);
1226 }
1227
1228 void wxTextCtrl::Paste()
1229 {
1230     wxCHECK_RET( m_text != NULL, wxT("invalid text ctrl") );
1231
1232 #ifdef __WXGTK20__
1233     if (m_windowStyle & wxTE_MULTILINE)
1234         g_signal_emit_by_name(m_text, "paste-clipboard");
1235     else
1236 #endif
1237         gtk_editable_paste_clipboard(GTK_EDITABLE(m_text) DUMMY_CLIPBOARD_ARG);
1238 }
1239
1240 // Undo/redo
1241 void wxTextCtrl::Undo()
1242 {
1243     // TODO
1244     wxFAIL_MSG( wxT("wxTextCtrl::Undo not implemented") );
1245 }
1246
1247 void wxTextCtrl::Redo()
1248 {
1249     // TODO
1250     wxFAIL_MSG( wxT("wxTextCtrl::Redo not implemented") );
1251 }
1252
1253 bool wxTextCtrl::CanUndo() const
1254 {
1255     // TODO
1256     //wxFAIL_MSG( wxT("wxTextCtrl::CanUndo not implemented") );
1257     return FALSE;
1258 }
1259
1260 bool wxTextCtrl::CanRedo() const
1261 {
1262     // TODO
1263     //wxFAIL_MSG( wxT("wxTextCtrl::CanRedo not implemented") );
1264     return FALSE;
1265 }
1266
1267 // If the return values from and to are the same, there is no
1268 // selection.
1269 void wxTextCtrl::GetSelection(long* fromOut, long* toOut) const
1270 {
1271     wxCHECK_RET( m_text != NULL, wxT("invalid text ctrl") );
1272
1273     gint from = -1;
1274     gint to = -1;
1275     bool haveSelection = FALSE;
1276
1277 #ifdef __WXGTK20__
1278      if (m_windowStyle & wxTE_MULTILINE)
1279      {
1280          GtkTextBuffer *buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (m_text));
1281          GtkTextIter ifrom, ito;
1282          if ( gtk_text_buffer_get_selection_bounds(buffer, &ifrom, &ito) )
1283          {
1284              haveSelection = TRUE;
1285              from = gtk_text_iter_get_offset(&ifrom);
1286              to = gtk_text_iter_get_offset(&ito);
1287          }
1288      }
1289      else  // not multi-line
1290      {
1291          if ( gtk_editable_get_selection_bounds( GTK_EDITABLE(m_text),
1292                                                  &from, &to) )
1293          {
1294              haveSelection = TRUE;
1295          }
1296      }
1297 #else //  not GTK2
1298      if ( (GTK_EDITABLE(m_text)->has_selection) )
1299      {
1300          haveSelection = TRUE;
1301          from = (long) GTK_EDITABLE(m_text)->selection_start_pos;
1302          to = (long) GTK_EDITABLE(m_text)->selection_end_pos;
1303      }
1304 #endif
1305
1306      if (! haveSelection )
1307           from = to = GetInsertionPoint();
1308
1309      if ( from > to )
1310      {
1311          // exchange them to be compatible with wxMSW
1312          gint tmp = from;
1313          from = to;
1314          to = tmp;
1315      }
1316
1317     if ( fromOut )
1318         *fromOut = from;
1319     if ( toOut )
1320         *toOut = to;
1321 }
1322
1323
1324 bool wxTextCtrl::IsEditable() const
1325 {
1326     wxCHECK_MSG( m_text != NULL, FALSE, wxT("invalid text ctrl") );
1327
1328 #ifdef __WXGTK20__
1329     if (m_windowStyle & wxTE_MULTILINE)
1330     {
1331         return gtk_text_view_get_editable(GTK_TEXT_VIEW(m_text));
1332     }
1333     else
1334     {
1335         return gtk_editable_get_editable(GTK_EDITABLE(m_text));
1336     }
1337 #else
1338     return GTK_EDITABLE(m_text)->editable;
1339 #endif
1340 }
1341
1342 bool wxTextCtrl::IsModified() const
1343 {
1344     return m_modified;
1345 }
1346
1347 void wxTextCtrl::Clear()
1348 {
1349     SetValue( wxT("") );
1350 }
1351
1352 void wxTextCtrl::OnChar( wxKeyEvent &key_event )
1353 {
1354     wxCHECK_RET( m_text != NULL, wxT("invalid text ctrl") );
1355
1356     if ((key_event.GetKeyCode() == WXK_RETURN) && (m_windowStyle & wxPROCESS_ENTER))
1357     {
1358         wxCommandEvent event(wxEVT_COMMAND_TEXT_ENTER, m_windowId);
1359         event.SetEventObject(this);
1360         event.SetString(GetValue());
1361         if (GetEventHandler()->ProcessEvent(event)) return;
1362     }
1363
1364     if ((key_event.GetKeyCode() == WXK_RETURN) && !(m_windowStyle & wxTE_MULTILINE))
1365     {
1366         // This will invoke the dialog default action, such
1367         // as the clicking the default button.
1368
1369         wxWindow *top_frame = m_parent;
1370         while (top_frame->GetParent() && !(top_frame->IsTopLevel()))
1371             top_frame = top_frame->GetParent();
1372
1373         if (top_frame && GTK_IS_WINDOW(top_frame->m_widget))
1374         {
1375             GtkWindow *window = GTK_WINDOW(top_frame->m_widget);
1376
1377             if (window->default_widget)
1378             {
1379                 gtk_widget_activate (window->default_widget);
1380                 return;
1381             }
1382         }
1383     }
1384
1385     key_event.Skip();
1386 }
1387
1388 GtkWidget* wxTextCtrl::GetConnectWidget()
1389 {
1390     return GTK_WIDGET(m_text);
1391 }
1392
1393 bool wxTextCtrl::IsOwnGtkWindow( GdkWindow *window )
1394 {
1395     if (m_windowStyle & wxTE_MULTILINE)
1396     {
1397 #ifdef __WXGTK20__
1398         return window == gtk_text_view_get_window( GTK_TEXT_VIEW( m_text ), GTK_TEXT_WINDOW_TEXT );  // pure guesswork
1399 #else
1400         return (window == GTK_TEXT(m_text)->text_area);
1401 #endif
1402     }
1403     else
1404     {
1405         return (window == GTK_ENTRY(m_text)->text_area);
1406     }
1407 }
1408
1409 // the font will change for subsequent text insertiongs
1410 bool wxTextCtrl::SetFont( const wxFont &font )
1411 {
1412     wxCHECK_MSG( m_text != NULL, FALSE, wxT("invalid text ctrl") );
1413
1414     if ( !wxTextCtrlBase::SetFont(font) )
1415     {
1416         // font didn't change, nothing to do
1417         return FALSE;
1418     }
1419
1420     if ( m_windowStyle & wxTE_MULTILINE )
1421     {
1422         SetUpdateFont(TRUE);
1423
1424         m_defaultStyle.SetFont(font);
1425
1426         ChangeFontGlobally();
1427     }
1428
1429     return TRUE;
1430 }
1431
1432 void wxTextCtrl::ChangeFontGlobally()
1433 {
1434     // this method is very inefficient and hence should be called as rarely as
1435     // possible!
1436     //
1437     // TODO: it can be implemented much more efficiently for GTK2
1438 #ifndef __WXGTK20__
1439     wxASSERT_MSG( (m_windowStyle & wxTE_MULTILINE) && m_updateFont,
1440
1441                   _T("shouldn't be called for single line controls") );
1442 #else
1443     wxASSERT_MSG( (m_windowStyle & wxTE_MULTILINE),
1444                   _T("shouldn't be called for single line controls") );
1445 #endif
1446
1447     wxString value = GetValue();
1448     if ( !value.IsEmpty() )
1449     {
1450         SetUpdateFont(FALSE);
1451
1452         Clear();
1453         AppendText(value);
1454     }
1455 }
1456
1457 #ifndef __WXGTK20__
1458
1459 void wxTextCtrl::UpdateFontIfNeeded()
1460 {
1461     if ( m_updateFont )
1462         ChangeFontGlobally();
1463 }
1464
1465 #endif // GTK+ 1.x
1466
1467 bool wxTextCtrl::SetForegroundColour(const wxColour& colour)
1468 {
1469     if ( !wxControl::SetForegroundColour(colour) )
1470         return FALSE;
1471
1472     // update default fg colour too
1473     m_defaultStyle.SetTextColour(colour);
1474
1475     return TRUE;
1476 }
1477
1478 bool wxTextCtrl::SetBackgroundColour( const wxColour &colour )
1479 {
1480     wxCHECK_MSG( m_text != NULL, FALSE, wxT("invalid text ctrl") );
1481
1482     if ( !wxControl::SetBackgroundColour( colour ) )
1483         return FALSE;
1484
1485     if (!m_widget->window)
1486         return FALSE;
1487
1488     if (!m_backgroundColour.Ok())
1489         return FALSE;
1490
1491     if (m_windowStyle & wxTE_MULTILINE)
1492     {
1493 #ifndef __WXGTK20__
1494         GdkWindow *window = GTK_TEXT(m_text)->text_area;
1495         if (!window)
1496             return FALSE;
1497         m_backgroundColour.CalcPixel( gdk_window_get_colormap( window ) );
1498         gdk_window_set_background( window, m_backgroundColour.GetColor() );
1499         gdk_window_clear( window );
1500 #endif
1501     }
1502
1503     // change active background color too
1504     m_defaultStyle.SetBackgroundColour( colour );
1505
1506     return TRUE;
1507 }
1508
1509 bool wxTextCtrl::SetStyle( long start, long end, const wxTextAttr& style )
1510 {
1511     if ( m_windowStyle & wxTE_MULTILINE )
1512     {
1513         if ( style.IsDefault() )
1514         {
1515             // nothing to do
1516             return TRUE;
1517         }
1518 #ifdef __WXGTK20__
1519         GtkTextBuffer *text_buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(m_text) );
1520         gint l = gtk_text_buffer_get_char_count( text_buffer );
1521
1522         wxCHECK_MSG( start >= 0 && end <= l, FALSE,
1523                      _T("invalid range in wxTextCtrl::SetStyle") );
1524
1525         GtkTextIter starti, endi;
1526         gtk_text_buffer_get_iter_at_offset( text_buffer, &starti, start );
1527         gtk_text_buffer_get_iter_at_offset( text_buffer, &endi, end );
1528
1529         // use the attributes from style which are set in it and fall back
1530         // first to the default style and then to the text control default
1531         // colours for the others
1532         wxTextAttr attr = wxTextAttr::Combine(style, m_defaultStyle, this);
1533
1534         PangoFontDescription *font_description = attr.HasFont()
1535                              ? attr.GetFont().GetNativeFontInfo()->description
1536                              : NULL;
1537
1538         GdkColor *colFg = attr.HasTextColour() ? attr.GetTextColour().GetColor()
1539                                                : NULL;
1540
1541         GdkColor *colBg = attr.HasBackgroundColour()
1542                             ? attr.GetBackgroundColour().GetColor()
1543                             : NULL;
1544
1545         GtkTextTag *tag;
1546         tag = gtk_text_buffer_create_tag( text_buffer, NULL, "font-desc", font_description,
1547                                          "foreground-gdk", colFg,
1548                                          "background-gdk", colBg, NULL );
1549         gtk_text_buffer_apply_tag( text_buffer, tag, &starti, &endi );
1550
1551          return TRUE;
1552 #else
1553         // VERY dirty way to do that - removes the required text and re-adds it
1554         // with styling (FIXME)
1555
1556         gint l = gtk_text_get_length( GTK_TEXT(m_text) );
1557
1558         wxCHECK_MSG( start >= 0 && end <= l, FALSE,
1559                      _T("invalid range in wxTextCtrl::SetStyle") );
1560
1561         gint old_pos = gtk_editable_get_position( GTK_EDITABLE(m_text) );
1562         char *text = gtk_editable_get_chars( GTK_EDITABLE(m_text), start, end );
1563         wxString tmp(text,*wxConvCurrent);
1564         g_free( text );
1565
1566         gtk_editable_delete_text( GTK_EDITABLE(m_text), start, end );
1567         gtk_editable_set_position( GTK_EDITABLE(m_text), start );
1568
1569 #if wxUSE_UNICODE
1570         wxWX2MBbuf buf = tmp.mbc_str();
1571         const char *txt = buf;
1572         size_t txtlen = strlen(buf);
1573 #else
1574         const char *txt = tmp;
1575         size_t txtlen = tmp.length();
1576 #endif
1577
1578         // use the attributes from style which are set in it and fall back
1579         // first to the default style and then to the text control default
1580         // colours for the others
1581         wxGtkTextInsert(m_text,
1582                         wxTextAttr::Combine(style, m_defaultStyle, this),
1583                         txt,
1584                         txtlen);
1585
1586         /* does not seem to help under GTK+ 1.2 !!!
1587         gtk_editable_set_position( GTK_EDITABLE(m_text), old_pos ); */
1588         SetInsertionPoint( old_pos );
1589 #endif
1590         return TRUE;
1591     }
1592     else // singe line
1593     {
1594         // cannot do this for GTK+'s Entry widget
1595         return FALSE;
1596     }
1597 }
1598
1599 void wxTextCtrl::DoApplyWidgetStyle(GtkRcStyle *style)
1600 {
1601     gtk_widget_modify_style(m_text, style);
1602 }
1603
1604 void wxTextCtrl::OnCut(wxCommandEvent& WXUNUSED(event))
1605 {
1606     Cut();
1607 }
1608
1609 void wxTextCtrl::OnCopy(wxCommandEvent& WXUNUSED(event))
1610 {
1611     Copy();
1612 }
1613
1614 void wxTextCtrl::OnPaste(wxCommandEvent& WXUNUSED(event))
1615 {
1616     Paste();
1617 }
1618
1619 void wxTextCtrl::OnUndo(wxCommandEvent& WXUNUSED(event))
1620 {
1621     Undo();
1622 }
1623
1624 void wxTextCtrl::OnRedo(wxCommandEvent& WXUNUSED(event))
1625 {
1626     Redo();
1627 }
1628
1629 void wxTextCtrl::OnUpdateCut(wxUpdateUIEvent& event)
1630 {
1631     event.Enable( CanCut() );
1632 }
1633
1634 void wxTextCtrl::OnUpdateCopy(wxUpdateUIEvent& event)
1635 {
1636     event.Enable( CanCopy() );
1637 }
1638
1639 void wxTextCtrl::OnUpdatePaste(wxUpdateUIEvent& event)
1640 {
1641     event.Enable( CanPaste() );
1642 }
1643
1644 void wxTextCtrl::OnUpdateUndo(wxUpdateUIEvent& event)
1645 {
1646     event.Enable( CanUndo() );
1647 }
1648
1649 void wxTextCtrl::OnUpdateRedo(wxUpdateUIEvent& event)
1650 {
1651     event.Enable( CanRedo() );
1652 }
1653
1654 void wxTextCtrl::OnInternalIdle()
1655 {
1656     wxCursor cursor = m_cursor;
1657     if (g_globalCursor.Ok()) cursor = g_globalCursor;
1658
1659     if (cursor.Ok())
1660     {
1661 #ifndef __WXGTK20__
1662         GdkWindow *window = (GdkWindow*) NULL;
1663         if (HasFlag(wxTE_MULTILINE))
1664             window = GTK_TEXT(m_text)->text_area;
1665         else
1666             window = GTK_ENTRY(m_text)->text_area;
1667
1668         if (window)
1669             gdk_window_set_cursor( window, cursor.GetCursor() );
1670
1671         if (!g_globalCursor.Ok())
1672             cursor = *wxSTANDARD_CURSOR;
1673
1674         window = m_widget->window;
1675         if ((window) && !(GTK_WIDGET_NO_WINDOW(m_widget)))
1676             gdk_window_set_cursor( window, cursor.GetCursor() );
1677 #endif
1678     }
1679
1680     if (g_delayedFocus == this)
1681     {
1682         if (GTK_WIDGET_REALIZED(m_widget))
1683         {
1684             gtk_widget_grab_focus( m_widget );
1685             g_delayedFocus = NULL;
1686         }
1687     }
1688
1689     if (wxUpdateUIEvent::CanUpdate(this))
1690         UpdateWindowUI(wxUPDATE_UI_FROMIDLE);
1691 }
1692
1693 wxSize wxTextCtrl::DoGetBestSize() const
1694 {
1695     // FIXME should be different for multi-line controls...
1696     wxSize ret( wxControl::DoGetBestSize() );
1697     wxSize best(80, ret.y);
1698     CacheBestSize(best);
1699     return best;
1700 }
1701
1702 // ----------------------------------------------------------------------------
1703 // freeze/thaw
1704 // ----------------------------------------------------------------------------
1705
1706 void wxTextCtrl::Freeze()
1707 {
1708 #ifndef __WXGTK20__
1709     if ( HasFlag(wxTE_MULTILINE) )
1710     {
1711         gtk_text_freeze(GTK_TEXT(m_text));
1712     }
1713 #endif
1714 }
1715
1716 void wxTextCtrl::Thaw()
1717 {
1718 #ifndef __WXGTK20__
1719     if ( HasFlag(wxTE_MULTILINE) )
1720     {
1721         GTK_TEXT(m_text)->vadj->value = 0.0;
1722
1723         gtk_text_thaw(GTK_TEXT(m_text));
1724     }
1725 #endif
1726 }
1727
1728 // ----------------------------------------------------------------------------
1729 // scrolling
1730 // ----------------------------------------------------------------------------
1731
1732 GtkAdjustment *wxTextCtrl::GetVAdj() const
1733 {
1734     if ( !IsMultiLine() )
1735         return NULL;
1736
1737 #ifdef __WXGTK20__
1738     return gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(m_widget));
1739 #else
1740     return GTK_TEXT(m_text)->vadj;
1741 #endif
1742 }
1743
1744 bool wxTextCtrl::DoScroll(GtkAdjustment *adj, int diff)
1745 {
1746     float value = adj->value + diff;
1747
1748     if ( value < 0 )
1749         value = 0;
1750
1751     float upper = adj->upper - adj->page_size;
1752     if ( value > upper )
1753         value = upper;
1754
1755     // did we noticeably change the scroll position?
1756     if ( fabs(adj->value - value) < 0.2 )
1757     {
1758         // well, this is what Robert does in wxScrollBar, so it must be good...
1759         return FALSE;
1760     }
1761
1762     adj->value = value;
1763
1764 #ifdef __WXGTK20__
1765     gtk_adjustment_value_changed(GTK_ADJUSTMENT(adj));
1766 #else
1767     gtk_signal_emit_by_name(GTK_OBJECT(adj), "value_changed");
1768 #endif
1769
1770     return TRUE;
1771 }
1772
1773 bool wxTextCtrl::ScrollLines(int lines)
1774 {
1775     GtkAdjustment *adj = GetVAdj();
1776     if ( !adj )
1777         return FALSE;
1778
1779 #ifdef __WXGTK20__
1780     int diff = (int)ceil(lines*adj->step_increment);
1781 #else
1782     // this is hardcoded to 10 in GTK+ 1.2 (great idea)
1783     int diff = 10*lines;
1784 #endif
1785
1786     return DoScroll(adj, diff);
1787 }
1788
1789 bool wxTextCtrl::ScrollPages(int pages)
1790 {
1791     GtkAdjustment *adj = GetVAdj();
1792     if ( !adj )
1793         return FALSE;
1794
1795     return DoScroll(adj, (int)ceil(pages*adj->page_increment));
1796 }
1797
1798
1799 // static
1800 wxVisualAttributes
1801 wxTextCtrl::GetClassDefaultAttributes(wxWindowVariant WXUNUSED(variant))
1802 {
1803     return GetDefaultAttributesFromGTKWidget(gtk_entry_new, true);
1804 }