1 ///////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/gtk/textctrl.cpp 
   4 // Author:      Robert Roebling 
   6 // Copyright:   (c) 1998 Robert Roebling, Vadim Zeitlin, 2005 Mart Raudsepp 
   7 // Licence:     wxWindows licence 
   8 ///////////////////////////////////////////////////////////////////////////// 
  10 // For compilers that support precompilation, includes "wx.h". 
  11 #include "wx/wxprec.h" 
  13 #include "wx/textctrl.h" 
  18 #include "wx/settings.h" 
  20 #include "wx/strconv.h" 
  21 #include "wx/fontutil.h"        // for wxNativeFontInfo (GetNativeFontInfo()) 
  23 #include <sys/types.h> 
  28 #include "wx/gtk/private.h" 
  29 #include <gdk/gdkkeysyms.h> 
  31 //----------------------------------------------------------------------------- 
  33 //----------------------------------------------------------------------------- 
  35 extern wxCursor   g_globalCursor
; 
  36 extern wxWindowGTK 
*g_delayedFocus
; 
  38 // ---------------------------------------------------------------------------- 
  40 // ---------------------------------------------------------------------------- 
  43 static void wxGtkOnRemoveTag(GtkTextBuffer 
*buffer
, 
  50     g_object_get (tag
, "name", &name
, NULL
); 
  52     if (!name 
|| strncmp(name
, prefix
, strlen(prefix
))) 
  53         // anonymous tag or not starting with prefix - don't remove 
  54         g_signal_stop_emission_by_name (buffer
, "remove_tag"); 
  61 static void wxGtkTextApplyTagsFromAttr(GtkTextBuffer 
*text_buffer
, 
  62                                        const wxTextAttr
& attr
, 
  66     static gchar buf
[1024]; 
  69     gulong remove_handler_id 
= g_signal_connect (text_buffer
, "remove_tag", 
  70             G_CALLBACK (wxGtkOnRemoveTag
), gpointer("WX")); 
  71     gtk_text_buffer_remove_all_tags(text_buffer
, start
, end
); 
  72     g_signal_handler_disconnect (text_buffer
, remove_handler_id
); 
  77         PangoFontDescription 
*font_description 
= attr
.GetFont().GetNativeFontInfo()->description
; 
  78         font_string 
= pango_font_description_to_string(font_description
); 
  79         g_snprintf(buf
, sizeof(buf
), "WXFONT %s", font_string
); 
  80         tag 
= gtk_text_tag_table_lookup( gtk_text_buffer_get_tag_table( text_buffer 
), 
  83             tag 
= gtk_text_buffer_create_tag( text_buffer
, buf
, 
  84                                               "font-desc", font_description
, 
  86         gtk_text_buffer_apply_tag (text_buffer
, tag
, start
, end
); 
  89         if (attr
.GetFont().GetUnderlined()) 
  91             g_snprintf(buf
, sizeof(buf
), "WXFONTUNDERLINE"); 
  92             tag 
= gtk_text_tag_table_lookup( gtk_text_buffer_get_tag_table( text_buffer 
), 
  95                 tag 
= gtk_text_buffer_create_tag( text_buffer
, buf
, 
  96                                                   "underline-set", TRUE
, 
  97                                                   "underline", PANGO_UNDERLINE_SINGLE
, 
  99             gtk_text_buffer_apply_tag (text_buffer
, tag
, start
, end
); 
 103     if (attr
.HasTextColour()) 
 105         GdkColor 
*colFg 
= attr
.GetTextColour().GetColor(); 
 106         g_snprintf(buf
, sizeof(buf
), "WXFORECOLOR %d %d %d", 
 107                    colFg
->red
, colFg
->green
, colFg
->blue
); 
 108         tag 
= gtk_text_tag_table_lookup( gtk_text_buffer_get_tag_table( text_buffer 
), 
 111             tag 
= gtk_text_buffer_create_tag( text_buffer
, buf
, 
 112                                               "foreground-gdk", colFg
, NULL 
); 
 113         gtk_text_buffer_apply_tag (text_buffer
, tag
, start
, end
); 
 116     if (attr
.HasBackgroundColour()) 
 118         GdkColor 
*colBg 
= attr
.GetBackgroundColour().GetColor(); 
 119         g_snprintf(buf
, sizeof(buf
), "WXBACKCOLOR %d %d %d", 
 120                    colBg
->red
, colBg
->green
, colBg
->blue
); 
 121         tag 
= gtk_text_tag_table_lookup( gtk_text_buffer_get_tag_table( text_buffer 
), 
 124             tag 
= gtk_text_buffer_create_tag( text_buffer
, buf
, 
 125                                               "background-gdk", colBg
, NULL 
); 
 126         gtk_text_buffer_apply_tag (text_buffer
, tag
, start
, end
); 
 129     if (attr
.HasAlignment()) 
 131         GtkTextIter para_start
, para_end 
= *end
; 
 132         gtk_text_buffer_get_iter_at_line( text_buffer
, 
 134                                           gtk_text_iter_get_line(start
) ); 
 135         gtk_text_iter_forward_line(¶_end
); 
 137         remove_handler_id 
= g_signal_connect (text_buffer
, "remove_tag", 
 138                                               G_CALLBACK(wxGtkOnRemoveTag
), 
 139                                               gpointer("WXALIGNMENT")); 
 140         gtk_text_buffer_remove_all_tags( text_buffer
, ¶_start
, ¶_end 
); 
 141         g_signal_handler_disconnect (text_buffer
, remove_handler_id
); 
 143         GtkJustification align
; 
 144         switch (attr
.GetAlignment()) 
 147                 align 
= GTK_JUSTIFY_LEFT
; 
 149             case wxTEXT_ALIGNMENT_RIGHT
: 
 150                 align 
= GTK_JUSTIFY_RIGHT
; 
 152             case wxTEXT_ALIGNMENT_CENTER
: 
 153                 align 
= GTK_JUSTIFY_CENTER
; 
 155             // gtk+ doesn't support justify as of gtk+-2.7.4 
 158         g_snprintf(buf
, sizeof(buf
), "WXALIGNMENT %d", align
); 
 159         tag 
= gtk_text_tag_table_lookup( gtk_text_buffer_get_tag_table( text_buffer 
), 
 162             tag 
= gtk_text_buffer_create_tag( text_buffer
, buf
, 
 163                                               "justification", align
, NULL 
); 
 164         gtk_text_buffer_apply_tag( text_buffer
, tag
, ¶_start
, ¶_end 
); 
 170 static void wxGtkTextInsert(GtkWidget 
*text
, 
 171                             GtkTextBuffer 
*text_buffer
, 
 172                             const wxTextAttr
& attr
, 
 173                             const wxCharBuffer
& buffer
) 
 177     GtkTextIter iter
, start
; 
 179     gtk_text_buffer_get_iter_at_mark( text_buffer
, &iter
, 
 180                                      gtk_text_buffer_get_insert (text_buffer
) ); 
 181     start_offset 
= gtk_text_iter_get_offset (&iter
); 
 182     gtk_text_buffer_insert( text_buffer
, &iter
, buffer
, strlen(buffer
) ); 
 184     gtk_text_buffer_get_iter_at_offset (text_buffer
, &start
, start_offset
); 
 186     wxGtkTextApplyTagsFromAttr(text_buffer
, attr
, &start
, &iter
); 
 190 // ---------------------------------------------------------------------------- 
 191 // "insert_text" for GtkEntry 
 192 // ---------------------------------------------------------------------------- 
 196 gtk_insert_text_callback(GtkEditable 
*editable
, 
 197                          const gchar 
*new_text
, 
 198                          gint new_text_length
, 
 203         wxapp_install_idle_handler(); 
 205     // we should only be called if we have a max len limit at all 
 206     GtkEntry 
*entry 
= GTK_ENTRY (editable
); 
 208     wxCHECK_RET( entry
->text_max_length
, _T("shouldn't be called") ); 
 210     // check that we don't overflow the max length limit 
 212     // FIXME: this doesn't work when we paste a string which is going to be 
 214     if ( entry
->text_length 
== entry
->text_max_length 
) 
 216         // we don't need to run the base class version at all 
 217         g_signal_stop_emission_by_name (editable
, "insert_text"); 
 219         // remember that the next changed signal is to be ignored to avoid 
 220         // generating a dummy wxEVT_COMMAND_TEXT_UPDATED event 
 221         win
->IgnoreNextTextUpdate(); 
 223         // and generate the correct one ourselves 
 224         wxCommandEvent 
event(wxEVT_COMMAND_TEXT_MAXLEN
, win
->GetId()); 
 225         event
.SetEventObject(win
); 
 226         event
.SetString(win
->GetValue()); 
 227         win
->GetEventHandler()->ProcessEvent( event 
); 
 232 // Implementation of wxTE_AUTO_URL for wxGTK2 by Mart Raudsepp, 
 236 au_apply_tag_callback(GtkTextBuffer 
*buffer
, 
 242     if(tag 
== gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(buffer
), "wxUrl")) 
 243         g_signal_stop_emission_by_name (buffer
, "apply_tag"); 
 247 //----------------------------------------------------------------------------- 
 248 //  GtkTextCharPredicates for gtk_text_iter_*_find_char 
 249 //----------------------------------------------------------------------------- 
 253 pred_whitespace (gunichar ch
, gpointer user_data
) 
 255     return g_unichar_isspace(ch
); 
 261 pred_non_whitespace (gunichar ch
, gpointer user_data
) 
 263     return !g_unichar_isspace(ch
); 
 269 pred_nonpunct (gunichar ch
, gpointer user_data
) 
 271     return !g_unichar_ispunct(ch
); 
 277 pred_nonpunct_or_slash (gunichar ch
, gpointer user_data
) 
 279     return !g_unichar_ispunct(ch
) || ch 
== '/'; 
 283 //----------------------------------------------------------------------------- 
 284 //  Check for links between s and e and correct tags as necessary 
 285 //----------------------------------------------------------------------------- 
 287 // This function should be made match better while being efficient at one point. 
 288 // Most probably with a row of regular expressions. 
 291 au_check_word( GtkTextIter 
*s
, GtkTextIter 
*e 
) 
 293     static const char *URIPrefixes
[] = 
 311     GtkTextIter start 
= *s
, end 
= *e
; 
 312     GtkTextBuffer 
*buffer 
= gtk_text_iter_get_buffer(s
); 
 314     // Get our special link tag 
 315     GtkTextTag 
*tag 
= gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(buffer
), "wxUrl"); 
 317     // Get rid of punctuation from beginning and end. 
 318     // Might want to move this to au_check_range if an improved link checking doesn't 
 319     // use some intelligent punctuation checking itself (beware of undesired iter modifications). 
 320     if(g_unichar_ispunct( gtk_text_iter_get_char( &start 
) ) ) 
 321         gtk_text_iter_forward_find_char( &start
, pred_nonpunct
, NULL
, e 
); 
 323     gtk_text_iter_backward_find_char( &end
, pred_nonpunct_or_slash
, NULL
, &start 
); 
 324     gtk_text_iter_forward_char(&end
); 
 326     gchar
* text 
= gtk_text_iter_get_text( &start
, &end 
); 
 327     size_t len 
= strlen(text
), prefix_len
; 
 330     for( n 
= 0; n 
< WXSIZEOF(URIPrefixes
); ++n 
) 
 332         prefix_len 
= strlen(URIPrefixes
[n
]); 
 333         if((len 
> prefix_len
) && !strncasecmp(text
, URIPrefixes
[n
], prefix_len
)) 
 337     if(n 
< WXSIZEOF(URIPrefixes
)) 
 339         gulong signal_id 
= g_signal_handler_find (buffer
, 
 340                                                   (GSignalMatchType
) (G_SIGNAL_MATCH_FUNC
), 
 342                                                   (gpointer
)au_apply_tag_callback
, NULL
); 
 344         g_signal_handler_block (buffer
, signal_id
); 
 345         gtk_text_buffer_apply_tag(buffer
, tag
, &start
, &end
); 
 346         g_signal_handler_unblock (buffer
, signal_id
); 
 353 au_check_range(GtkTextIter 
*s
, 
 354                GtkTextIter 
*range_end
) 
 356     GtkTextIter range_start 
= *s
; 
 357     GtkTextIter word_end
; 
 358     GtkTextBuffer 
*buffer 
= gtk_text_iter_get_buffer(s
); 
 359     GtkTextTag 
*tag 
= gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(buffer
), "wxUrl"); 
 361     gtk_text_buffer_remove_tag(buffer
, tag
, s
, range_end
); 
 363     if(g_unichar_isspace(gtk_text_iter_get_char(&range_start
))) 
 364         gtk_text_iter_forward_find_char(&range_start
, pred_non_whitespace
, NULL
, range_end
); 
 366     while(!gtk_text_iter_equal(&range_start
, range_end
)) 
 368         word_end 
= range_start
; 
 369         gtk_text_iter_forward_find_char(&word_end
, pred_whitespace
, NULL
, range_end
); 
 371         // Now we should have a word delimited by range_start and word_end, correct link tags 
 372         au_check_word(&range_start
, &word_end
); 
 374         range_start 
= word_end
; 
 375         gtk_text_iter_forward_find_char(&range_start
, pred_non_whitespace
, NULL
, range_end
); 
 380 //----------------------------------------------------------------------------- 
 381 //  "insert-text" for GtkTextBuffer 
 382 //----------------------------------------------------------------------------- 
 386 au_insert_text_callback(GtkTextBuffer 
*buffer
, 
 392     if (!len 
|| !(win
->GetWindowStyleFlag() & wxTE_AUTO_URL
) ) 
 395     GtkTextIter start 
= *end
; 
 396     gtk_text_iter_backward_chars(&start
, g_utf8_strlen(text
, len
)); 
 398     GtkTextIter line_start 
= start
; 
 399     GtkTextIter line_end 
= *end
; 
 400     GtkTextIter words_start 
= start
; 
 401     GtkTextIter words_end 
= *end
; 
 403     gtk_text_iter_set_line(&line_start
, gtk_text_iter_get_line(&start
)); 
 404     gtk_text_iter_forward_to_line_end(&line_end
); 
 405     gtk_text_iter_backward_find_char(&words_start
, pred_whitespace
, NULL
, &line_start
); 
 406     gtk_text_iter_forward_find_char(&words_end
, pred_whitespace
, NULL
, &line_end
); 
 408     au_check_range(&words_start
, &words_end
); 
 412 //----------------------------------------------------------------------------- 
 413 //  "delete-range" for GtkTextBuffer 
 414 //----------------------------------------------------------------------------- 
 418 au_delete_range_callback(GtkTextBuffer 
*buffer
, 
 423     if( !(win
->GetWindowStyleFlag() & wxTE_AUTO_URL
) ) 
 426     GtkTextIter line_start 
= *start
, line_end 
= *end
; 
 428     gtk_text_iter_set_line(&line_start
, gtk_text_iter_get_line(start
)); 
 429     gtk_text_iter_forward_to_line_end(&line_end
); 
 430     gtk_text_iter_backward_find_char(start
, pred_whitespace
, NULL
, &line_start
); 
 431     gtk_text_iter_forward_find_char(end
, pred_whitespace
, NULL
, &line_end
); 
 433     au_check_range(start
, end
); 
 438 //----------------------------------------------------------------------------- 
 440 //----------------------------------------------------------------------------- 
 444 gtk_text_changed_callback( GtkWidget 
*widget
, wxTextCtrl 
*win 
) 
 446     if ( win
->IgnoreTextUpdate() ) 
 449     if (!win
->m_hasVMT
) return; 
 452         wxapp_install_idle_handler(); 
 456     wxCommandEvent 
event( wxEVT_COMMAND_TEXT_UPDATED
, win
->GetId() ); 
 457     event
.SetEventObject( win 
); 
 458     win
->GetEventHandler()->ProcessEvent( event 
); 
 462 //----------------------------------------------------------------------------- 
 463 // "expose_event" from scrolled window and textview 
 464 //----------------------------------------------------------------------------- 
 468 gtk_text_exposed_callback( GtkWidget 
*widget
, GdkEventExpose 
*event
, wxTextCtrl 
*win 
) 
 475 //----------------------------------------------------------------------------- 
 477 //----------------------------------------------------------------------------- 
 479 IMPLEMENT_DYNAMIC_CLASS(wxTextCtrl
,wxControl
) 
 481 BEGIN_EVENT_TABLE(wxTextCtrl
, wxControl
) 
 482     EVT_CHAR(wxTextCtrl::OnChar
) 
 484     EVT_MENU(wxID_CUT
, wxTextCtrl::OnCut
) 
 485     EVT_MENU(wxID_COPY
, wxTextCtrl::OnCopy
) 
 486     EVT_MENU(wxID_PASTE
, wxTextCtrl::OnPaste
) 
 487     EVT_MENU(wxID_UNDO
, wxTextCtrl::OnUndo
) 
 488     EVT_MENU(wxID_REDO
, wxTextCtrl::OnRedo
) 
 490     EVT_UPDATE_UI(wxID_CUT
, wxTextCtrl::OnUpdateCut
) 
 491     EVT_UPDATE_UI(wxID_COPY
, wxTextCtrl::OnUpdateCopy
) 
 492     EVT_UPDATE_UI(wxID_PASTE
, wxTextCtrl::OnUpdatePaste
) 
 493     EVT_UPDATE_UI(wxID_UNDO
, wxTextCtrl::OnUpdateUndo
) 
 494     EVT_UPDATE_UI(wxID_REDO
, wxTextCtrl::OnUpdateRedo
) 
 496     // wxTE_AUTO_URL wxTextUrl support. Currently only creates 
 497     // wxTextUrlEvent in the same cases as wxMSW, more can be added here. 
 498     EVT_MOTION      (wxTextCtrl::OnUrlMouseEvent
) 
 499     EVT_LEFT_DOWN   (wxTextCtrl::OnUrlMouseEvent
) 
 500     EVT_LEFT_UP     (wxTextCtrl::OnUrlMouseEvent
) 
 501     EVT_LEFT_DCLICK (wxTextCtrl::OnUrlMouseEvent
) 
 502     EVT_RIGHT_DOWN  (wxTextCtrl::OnUrlMouseEvent
) 
 503     EVT_RIGHT_UP    (wxTextCtrl::OnUrlMouseEvent
) 
 504     EVT_RIGHT_DCLICK(wxTextCtrl::OnUrlMouseEvent
) 
 507 void wxTextCtrl::Init() 
 511     SetUpdateFont(false); 
 513     m_vScrollbar 
= (GtkWidget 
*)NULL
; 
 515     m_gdkHandCursor 
= NULL
; 
 516     m_gdkXTermCursor 
= NULL
; 
 519 wxTextCtrl::~wxTextCtrl() 
 522         gdk_cursor_unref(m_gdkHandCursor
); 
 524         gdk_cursor_unref(m_gdkXTermCursor
); 
 527 wxTextCtrl::wxTextCtrl( wxWindow 
*parent
, 
 529                         const wxString 
&value
, 
 533                         const wxValidator
& validator
, 
 534                         const wxString 
&name 
) 
 538     Create( parent
, id
, value
, pos
, size
, style
, validator
, name 
); 
 541 bool wxTextCtrl::Create( wxWindow 
*parent
, 
 543                          const wxString 
&value
, 
 547                          const wxValidator
& validator
, 
 548                          const wxString 
&name 
) 
 551     m_acceptsFocus 
= true; 
 553     if (!PreCreation( parent
, pos
, size 
) || 
 554         !CreateBase( parent
, id
, pos
, size
, style
, validator
, name 
)) 
 556         wxFAIL_MSG( wxT("wxTextCtrl creation failed") ); 
 561     m_vScrollbarVisible 
= false; 
 563     bool multi_line 
= (style 
& wxTE_MULTILINE
) != 0; 
 568         m_text 
= gtk_text_view_new(); 
 570         m_buffer 
= gtk_text_view_get_buffer( GTK_TEXT_VIEW(m_text
) ); 
 572         // create scrolled window 
 573         m_widget 
= gtk_scrolled_window_new( NULL
, NULL 
); 
 574         gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( m_widget 
), 
 575                                         GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC 
); 
 577         // Insert view into scrolled window 
 578         gtk_container_add( GTK_CONTAINER(m_widget
), m_text 
); 
 580         // translate wx wrapping style to GTK+ 
 582         if ( HasFlag( wxTE_DONTWRAP 
) ) 
 583             wrap 
= GTK_WRAP_NONE
; 
 584         else if ( HasFlag( wxTE_CHARWRAP 
) ) 
 585             wrap 
= GTK_WRAP_CHAR
; 
 586         else if ( HasFlag( wxTE_WORDWRAP 
) ) 
 587             wrap 
= GTK_WRAP_WORD
; 
 588         else // HasFlag(wxTE_BESTWRAP) always true as wxTE_BESTWRAP == 0 
 590             // GTK_WRAP_WORD_CHAR seems to be new in GTK+ 2.4 
 592             if ( !gtk_check_version(2,4,0) ) 
 594                 wrap 
= GTK_WRAP_WORD_CHAR
; 
 598             wrap 
= GTK_WRAP_WORD
; 
 601         gtk_text_view_set_wrap_mode( GTK_TEXT_VIEW( m_text 
), wrap 
); 
 603         if (!HasFlag(wxNO_BORDER
)) 
 604             gtk_scrolled_window_set_shadow_type( GTK_SCROLLED_WINDOW(m_widget
), GTK_SHADOW_IN 
); 
 606         gtk_widget_add_events( GTK_WIDGET(m_text
), GDK_ENTER_NOTIFY_MASK 
| GDK_LEAVE_NOTIFY_MASK 
); 
 608         GTK_WIDGET_UNSET_FLAGS( m_widget
, GTK_CAN_FOCUS 
); 
 612         // a single-line text control: no need for scrollbars 
 614         m_text 
= gtk_entry_new(); 
 616         if (style 
& wxNO_BORDER
) 
 617             g_object_set( GTK_ENTRY(m_text
), "has-frame", FALSE
, NULL 
); 
 620     m_parent
->DoAddChild( this ); 
 622     m_focusWidget 
= m_text
; 
 628         gtk_widget_show(m_text
); 
 629         SetVScrollAdjustment(gtk_scrolled_window_get_vadjustment((GtkScrolledWindow
*)m_widget
)); 
 637     if (style 
& wxTE_PASSWORD
) 
 640             gtk_entry_set_visibility( GTK_ENTRY(m_text
), FALSE 
); 
 643     if (style 
& wxTE_READONLY
) 
 646             gtk_editable_set_editable( GTK_EDITABLE(m_text
), FALSE 
); 
 648             gtk_text_view_set_editable( GTK_TEXT_VIEW( m_text
), FALSE
); 
 653         if (style 
& wxTE_RIGHT
) 
 654             gtk_text_view_set_justification( GTK_TEXT_VIEW(m_text
), GTK_JUSTIFY_RIGHT 
); 
 655         else if (style 
& wxTE_CENTRE
) 
 656             gtk_text_view_set_justification( GTK_TEXT_VIEW(m_text
), GTK_JUSTIFY_CENTER 
); 
 657         // Left justify (alignment) is the default and we don't need to apply GTK_JUSTIFY_LEFT 
 662         // gtk_entry_set_alignment was introduced in gtk+-2.3.5 
 663         if (!gtk_check_version(2,4,0)) 
 665             if (style 
& wxTE_RIGHT
) 
 666                 gtk_entry_set_alignment( GTK_ENTRY(m_text
), 1.0 ); 
 667             else if (style 
& wxTE_CENTRE
) 
 668                 gtk_entry_set_alignment( GTK_ENTRY(m_text
), 0.5 ); 
 673     // We want to be notified about text changes. 
 676         g_signal_connect (m_buffer
, "changed", 
 677                           G_CALLBACK (gtk_text_changed_callback
), this); 
 679         // .. and handle URLs on multi-line controls with wxTE_AUTO_URL style 
 680         if (style 
& wxTE_AUTO_URL
) 
 682             GtkTextIter start
, end
; 
 683             m_gdkHandCursor 
= gdk_cursor_new(GDK_HAND2
); 
 684             m_gdkXTermCursor 
= gdk_cursor_new(GDK_XTERM
); 
 686             // We create our wxUrl tag here for slight efficiency gain - we 
 687             // don't have to check for the tag existance in callbacks, 
 688             // hereby it's guaranteed to exist. 
 689             gtk_text_buffer_create_tag(m_buffer
, "wxUrl", 
 690                                        "foreground", "blue", 
 691                                        "underline", PANGO_UNDERLINE_SINGLE
, 
 694             // Check for URLs after each text change 
 695             g_signal_connect_after (m_buffer
, "insert_text", 
 696                                     G_CALLBACK (au_insert_text_callback
), this); 
 697             g_signal_connect_after (m_buffer
, "delete_range", 
 698                                     G_CALLBACK (au_delete_range_callback
), this); 
 700             // Block all wxUrl tag applying unless we do it ourselves, in which case we 
 701             // block this callback temporarily. This takes care of gtk+ internal 
 702             // gtk_text_buffer_insert_range* calls that would copy our URL tag otherwise, 
 703             // which is undesired because only a part of the URL might be copied. 
 704             // The insert-text signal emitted inside it will take care of newly formed 
 705             // or wholly copied URLs. 
 706             g_signal_connect (m_buffer
, "apply_tag", 
 707                               G_CALLBACK (au_apply_tag_callback
), NULL
); 
 709             // Check for URLs in the initial string passed to Create 
 710             gtk_text_buffer_get_start_iter(m_buffer
, &start
); 
 711             gtk_text_buffer_get_end_iter(m_buffer
, &end
); 
 712             au_check_range(&start
, &end
); 
 717         g_signal_connect (m_text
, "changed", 
 718                           G_CALLBACK (gtk_text_changed_callback
), this); 
 721     m_cursor 
= wxCursor( wxCURSOR_IBEAM 
); 
 723     wxTextAttr 
attrDef(GetForegroundColour(), GetBackgroundColour(), GetFont()); 
 724     SetDefaultStyle( attrDef 
); 
 730 void wxTextCtrl::CalculateScrollbar() 
 734 wxString 
wxTextCtrl::GetValue() const 
 736     wxCHECK_MSG( m_text 
!= NULL
, wxEmptyString
, wxT("invalid text ctrl") ); 
 739     if (m_windowStyle 
& wxTE_MULTILINE
) 
 742         gtk_text_buffer_get_start_iter( m_buffer
, &start 
); 
 744         gtk_text_buffer_get_end_iter( m_buffer
, &end 
); 
 745         gchar 
*text 
= gtk_text_buffer_get_text( m_buffer
, &start
, &end
, TRUE 
); 
 748         wxWCharBuffer 
buffer( wxConvUTF8
.cMB2WX( text 
) ); 
 750         wxCharBuffer 
buffer( wxConvLocal
.cWC2WX( wxConvUTF8
.cMB2WC( text 
) ) ); 
 759         tmp 
= wxGTK_CONV_BACK( gtk_entry_get_text( GTK_ENTRY(m_text
) ) ); 
 765 void wxTextCtrl::SetValue( const wxString 
&value 
) 
 767     wxCHECK_RET( m_text 
!= NULL
, wxT("invalid text ctrl") ); 
 769     if (m_windowStyle 
& wxTE_MULTILINE
) 
 772         wxCharBuffer 
buffer( wxConvUTF8
.cWX2MB( value
) ); 
 774         wxCharBuffer 
buffer( wxConvUTF8
.cWC2MB( wxConvLocal
.cWX2WC( value 
) ) ); 
 776         if (gtk_text_buffer_get_char_count(m_buffer
) != 0) 
 777             IgnoreNextTextUpdate(); 
 781             // what else can we do? at least don't crash... 
 785         gtk_text_buffer_set_text( m_buffer
, buffer
, strlen(buffer
) ); 
 789         gtk_entry_set_text( GTK_ENTRY(m_text
), wxGTK_CONV( value 
) ); 
 792     // GRG, Jun/2000: Changed this after a lot of discussion in 
 793     //   the lists. wxWidgets 2.2 will have a set of flags to 
 794     //   customize this behaviour. 
 795     SetInsertionPoint(0); 
 800 void wxTextCtrl::WriteText( const wxString 
&text 
) 
 802     wxCHECK_RET( m_text 
!= NULL
, wxT("invalid text ctrl") ); 
 807     // gtk_text_changed_callback() will set m_modified to true but m_modified 
 808     // shouldn't be changed by the program writing to the text control itself, 
 809     // so save the old value and restore when we're done 
 810     bool oldModified 
= m_modified
; 
 812     if ( m_windowStyle 
& wxTE_MULTILINE 
) 
 815         wxCharBuffer 
buffer( wxConvUTF8
.cWX2MB( text 
) ); 
 817         wxCharBuffer 
buffer( wxConvUTF8
.cWC2MB( wxConvLocal
.cWX2WC( text 
) ) ); 
 821             // what else can we do? at least don't crash... 
 825         // TODO: Call whatever is needed to delete the selection. 
 826         wxGtkTextInsert( m_text
, m_buffer
, m_defaultStyle
, buffer 
); 
 828         GtkAdjustment 
*adj 
= gtk_scrolled_window_get_vadjustment( GTK_SCROLLED_WINDOW(m_widget
) ); 
 829         // Scroll to cursor, but only if scrollbar thumb is at the very bottom 
 830         if ( wxIsSameDouble(adj
->value
, adj
->upper 
- adj
->page_size
) ) 
 832             gtk_text_view_scroll_to_mark( GTK_TEXT_VIEW(m_text
), 
 833                     gtk_text_buffer_get_insert( m_buffer 
), 0.0, FALSE
, 0.0, 1.0 ); 
 838         // First remove the selection if there is one 
 839         gtk_editable_delete_selection( GTK_EDITABLE(m_text
) ); 
 841         // This moves the cursor pos to behind the inserted text. 
 842         gint len 
= gtk_editable_get_position(GTK_EDITABLE(m_text
)); 
 845         wxCharBuffer 
buffer( wxConvUTF8
.cWX2MB( text 
) ); 
 847         wxCharBuffer 
buffer( wxConvUTF8
.cWC2MB( wxConvLocal
.cWX2WC( text 
) ) ); 
 851             // what else can we do? at least don't crash... 
 855         gtk_editable_insert_text( GTK_EDITABLE(m_text
), buffer
, strlen(buffer
), &len 
); 
 857         // Bring entry's cursor uptodate. 
 858         gtk_editable_set_position( GTK_EDITABLE(m_text
), len 
); 
 861     m_modified 
= oldModified
; 
 864 void wxTextCtrl::AppendText( const wxString 
&text 
) 
 866     SetInsertionPointEnd(); 
 870 wxString 
wxTextCtrl::GetLineText( long lineNo 
) const 
 872     if (m_windowStyle 
& wxTE_MULTILINE
) 
 875         gtk_text_buffer_get_iter_at_line(m_buffer
,&line
,lineNo
); 
 876         GtkTextIter end 
= line
; 
 877         gtk_text_iter_forward_to_line_end(&end
); 
 878         gchar 
*text 
= gtk_text_buffer_get_text(m_buffer
,&line
,&end
,TRUE
); 
 879         wxString 
result(wxGTK_CONV_BACK(text
)); 
 885         if (lineNo 
== 0) return GetValue(); 
 886         return wxEmptyString
; 
 890 void wxTextCtrl::OnDropFiles( wxDropFilesEvent 
&WXUNUSED(event
) ) 
 892   /* If you implement this, don't forget to update the documentation! 
 893    * (file docs/latex/wx/text.tex) */ 
 894     wxFAIL_MSG( wxT("wxTextCtrl::OnDropFiles not implemented") ); 
 897 bool wxTextCtrl::PositionToXY(long pos
, long *x
, long *y 
) const 
 899     if ( m_windowStyle 
& wxTE_MULTILINE 
) 
 902         gtk_text_buffer_get_iter_at_offset(m_buffer
, &iter
, pos
); 
 903         if (gtk_text_iter_is_end(&iter
)) 
 906         *y 
= gtk_text_iter_get_line(&iter
); 
 907         *x 
= gtk_text_iter_get_line_offset(&iter
); 
 909     else // single line control 
 911         if ( pos 
<= GTK_ENTRY(m_text
)->text_length 
) 
 918             // index out of bounds 
 926 long wxTextCtrl::XYToPosition(long x
, long y 
) const 
 928     if (!(m_windowStyle 
& wxTE_MULTILINE
)) return 0; 
 931     if (y 
>= gtk_text_buffer_get_line_count (m_buffer
)) 
 934     gtk_text_buffer_get_iter_at_line(m_buffer
, &iter
, y
); 
 935     if (x 
>= gtk_text_iter_get_chars_in_line (&iter
)) 
 938     return gtk_text_iter_get_offset(&iter
) + x
; 
 941 int wxTextCtrl::GetLineLength(long lineNo
) const 
 943     if (m_windowStyle 
& wxTE_MULTILINE
) 
 945         int last_line 
= gtk_text_buffer_get_line_count( m_buffer 
) - 1; 
 946         if (lineNo 
> last_line
) 
 950         gtk_text_buffer_get_iter_at_line(m_buffer
, &iter
, lineNo
); 
 951         // get_chars_in_line return includes paragraph delimiters, so need to subtract 1 IF it is not the last line 
 952         return gtk_text_iter_get_chars_in_line(&iter
) - ((lineNo 
== last_line
) ? 0 : 1); 
 956         wxString str 
= GetLineText (lineNo
); 
 957         return (int) str
.length(); 
 961 int wxTextCtrl::GetNumberOfLines() const 
 963     if ( m_windowStyle 
& wxTE_MULTILINE 
) 
 966         gtk_text_buffer_get_iter_at_offset( m_buffer
, &iter
, 0 ); 
 968         // move forward by one display line until the end is reached 
 970         while ( gtk_text_view_forward_display_line(GTK_TEXT_VIEW(m_text
), &iter
) ) 
 975         // If the last character in the text buffer is a newline, 
 976         // gtk_text_view_forward_display_line() will return false without that 
 977         // line being counted. Must add one manually in that case. 
 978         GtkTextIter lastCharIter
; 
 979         gtk_text_buffer_get_iter_at_offset
 
 983             gtk_text_buffer_get_char_count(m_buffer
) - 1 
 985         gchar lastChar 
= gtk_text_iter_get_char( &lastCharIter 
); 
 986         if ( lastChar 
== wxT('\n') ) 
 997 void wxTextCtrl::SetInsertionPoint( long pos 
) 
 999     wxCHECK_RET( m_text 
!= NULL
, wxT("invalid text ctrl") ); 
1001     if ( IsMultiLine() ) 
1004         gtk_text_buffer_get_iter_at_offset( m_buffer
, &iter
, pos 
); 
1005         gtk_text_buffer_place_cursor( m_buffer
, &iter 
); 
1006         gtk_text_view_scroll_mark_onscreen
 
1008             GTK_TEXT_VIEW(m_text
), 
1009             gtk_text_buffer_get_insert( m_buffer 
) 
1014         // FIXME: Is the editable's cursor really uptodate without double set_position in GTK2? 
1015         gtk_editable_set_position(GTK_EDITABLE(m_text
), int(pos
)); 
1019 void wxTextCtrl::SetInsertionPointEnd() 
1021     wxCHECK_RET( m_text 
!= NULL
, wxT("invalid text ctrl") ); 
1023     if (m_windowStyle 
& wxTE_MULTILINE
) 
1026         gtk_text_buffer_get_end_iter( m_buffer
, &end 
); 
1027         gtk_text_buffer_place_cursor( m_buffer
, &end 
); 
1031         gtk_editable_set_position( GTK_EDITABLE(m_text
), -1 ); 
1035 void wxTextCtrl::SetEditable( bool editable 
) 
1037     wxCHECK_RET( m_text 
!= NULL
, wxT("invalid text ctrl") ); 
1039     if (m_windowStyle 
& wxTE_MULTILINE
) 
1041         gtk_text_view_set_editable( GTK_TEXT_VIEW(m_text
), editable 
); 
1045         gtk_editable_set_editable( GTK_EDITABLE(m_text
), editable 
); 
1049 bool wxTextCtrl::Enable( bool enable 
) 
1051     if (!wxWindowBase::Enable(enable
)) 
1057     if (m_windowStyle 
& wxTE_MULTILINE
) 
1059         SetEditable( enable 
); 
1063         gtk_widget_set_sensitive( m_text
, enable 
); 
1069 // wxGTK-specific: called recursively by Enable, 
1070 // to give widgets an oppprtunity to correct their colours after they 
1071 // have been changed by Enable 
1072 void wxTextCtrl::OnParentEnable( bool enable 
) 
1074     // If we have a custom background colour, we use this colour in both 
1075     // disabled and enabled mode, or we end up with a different colour under the 
1077     wxColour oldColour 
= GetBackgroundColour(); 
1080         // Need to set twice or it'll optimize the useful stuff out 
1081         if (oldColour 
== * wxWHITE
) 
1082             SetBackgroundColour(*wxBLACK
); 
1084             SetBackgroundColour(*wxWHITE
); 
1085         SetBackgroundColour(oldColour
); 
1089 void wxTextCtrl::MarkDirty() 
1094 void wxTextCtrl::DiscardEdits() 
1099 // ---------------------------------------------------------------------------- 
1100 // max text length support 
1101 // ---------------------------------------------------------------------------- 
1103 void wxTextCtrl::IgnoreNextTextUpdate() 
1105     m_ignoreNextUpdate 
= true; 
1108 bool wxTextCtrl::IgnoreTextUpdate() 
1110     if ( m_ignoreNextUpdate 
) 
1112         m_ignoreNextUpdate 
= false; 
1120 void wxTextCtrl::SetMaxLength(unsigned long len
) 
1122     if ( !HasFlag(wxTE_MULTILINE
) ) 
1124         gtk_entry_set_max_length(GTK_ENTRY(m_text
), len
); 
1126         // there is a bug in GTK+ 1.2.x: "changed" signal is emitted even if 
1127         // we had tried to enter more text than allowed by max text length and 
1128         // the text wasn't really changed 
1130         // to detect this and generate TEXT_MAXLEN event instead of 
1131         // TEXT_CHANGED one in this case we also catch "insert_text" signal 
1133         // when max len is set to 0 we disconnect our handler as it means that 
1134         // we shouldn't check anything any more 
1137             g_signal_connect (m_text
, "insert_text", 
1138                               G_CALLBACK (gtk_insert_text_callback
), this); 
1142             g_signal_handlers_disconnect_by_func (m_text
, 
1143                     (gpointer
) gtk_insert_text_callback
, this); 
1148 void wxTextCtrl::SetSelection( long from
, long to 
) 
1150     wxCHECK_RET( m_text 
!= NULL
, wxT("invalid text ctrl") ); 
1152     if (from 
== -1 && to 
== -1) 
1155         to 
= GetValue().length(); 
1158     if (m_windowStyle 
& wxTE_MULTILINE
) 
1160         GtkTextIter fromi
, toi
; 
1161         gtk_text_buffer_get_iter_at_offset( m_buffer
, &fromi
, from 
); 
1162         gtk_text_buffer_get_iter_at_offset( m_buffer
, &toi
, to 
); 
1164         gtk_text_buffer_place_cursor( m_buffer
, &toi 
); 
1165         gtk_text_buffer_move_mark_by_name( m_buffer
, "selection_bound", &fromi 
); 
1169         gtk_editable_select_region( GTK_EDITABLE(m_text
), (gint
)from
, (gint
)to 
); 
1173 void wxTextCtrl::ShowPosition( long pos 
) 
1175     if (m_windowStyle 
& wxTE_MULTILINE
) 
1178         gtk_text_buffer_get_start_iter( m_buffer
, &iter 
); 
1179         gtk_text_iter_set_offset( &iter
, pos 
); 
1180         GtkTextMark 
*mark 
= gtk_text_buffer_create_mark( m_buffer
, NULL
, &iter
, TRUE 
); 
1181         gtk_text_view_scroll_to_mark( GTK_TEXT_VIEW(m_text
), mark
, 0.0, FALSE
, 0.0, 0.0 ); 
1185 wxTextCtrlHitTestResult
 
1186 wxTextCtrl::HitTest(const wxPoint
& pt
, long *pos
) const 
1188     if ( !IsMultiLine() ) 
1191         return wxTE_HT_UNKNOWN
; 
1195     gtk_text_view_window_to_buffer_coords
 
1197         GTK_TEXT_VIEW(m_text
), 
1198         GTK_TEXT_WINDOW_TEXT
, 
1204     gtk_text_view_get_iter_at_location(GTK_TEXT_VIEW(m_text
), &iter
, x
, y
); 
1206         *pos 
= gtk_text_iter_get_offset(&iter
); 
1208     return wxTE_HT_ON_TEXT
; 
1211 long wxTextCtrl::GetInsertionPoint() const 
1213     wxCHECK_MSG( m_text 
!= NULL
, 0, wxT("invalid text ctrl") ); 
1215     if (m_windowStyle 
& wxTE_MULTILINE
) 
1217         // There is no direct accessor for the cursor, but 
1218         // internally, the cursor is the "mark" called 
1219         // "insert" in the text view's btree structure. 
1221         GtkTextMark 
*mark 
= gtk_text_buffer_get_insert( m_buffer 
); 
1223         gtk_text_buffer_get_iter_at_mark( m_buffer
, &cursor
, mark 
); 
1225         return gtk_text_iter_get_offset( &cursor 
); 
1229         return (long) gtk_editable_get_position(GTK_EDITABLE(m_text
)); 
1233 wxTextPos 
wxTextCtrl::GetLastPosition() const 
1235     wxCHECK_MSG( m_text 
!= NULL
, 0, wxT("invalid text ctrl") ); 
1239     if (m_windowStyle 
& wxTE_MULTILINE
) 
1242         gtk_text_buffer_get_end_iter( m_buffer
, &end 
); 
1244         pos 
= gtk_text_iter_get_offset( &end 
); 
1248         pos 
= GTK_ENTRY(m_text
)->text_length
; 
1254 void wxTextCtrl::Remove( long from
, long to 
) 
1256     wxCHECK_RET( m_text 
!= NULL
, wxT("invalid text ctrl") ); 
1258     if (m_windowStyle 
& wxTE_MULTILINE
) 
1260         GtkTextIter fromi
, toi
; 
1261         gtk_text_buffer_get_iter_at_offset( m_buffer
, &fromi
, from 
); 
1262         gtk_text_buffer_get_iter_at_offset( m_buffer
, &toi
, to 
); 
1264         gtk_text_buffer_delete( m_buffer
, &fromi
, &toi 
); 
1267         gtk_editable_delete_text( GTK_EDITABLE(m_text
), (gint
)from
, (gint
)to 
); 
1270 void wxTextCtrl::Replace( long from
, long to
, const wxString 
&value 
) 
1272     wxCHECK_RET( m_text 
!= NULL
, wxT("invalid text ctrl") ); 
1278         SetInsertionPoint( from 
); 
1283 void wxTextCtrl::Cut() 
1285     wxCHECK_RET( m_text 
!= NULL
, wxT("invalid text ctrl") ); 
1287     if (m_windowStyle 
& wxTE_MULTILINE
) 
1288         g_signal_emit_by_name (m_text
, "cut-clipboard"); 
1290         gtk_editable_cut_clipboard(GTK_EDITABLE(m_text
)); 
1293 void wxTextCtrl::Copy() 
1295     wxCHECK_RET( m_text 
!= NULL
, wxT("invalid text ctrl") ); 
1297     if (m_windowStyle 
& wxTE_MULTILINE
) 
1298         g_signal_emit_by_name (m_text
, "copy-clipboard"); 
1300         gtk_editable_copy_clipboard(GTK_EDITABLE(m_text
)); 
1303 void wxTextCtrl::Paste() 
1305     wxCHECK_RET( m_text 
!= NULL
, wxT("invalid text ctrl") ); 
1307     if (m_windowStyle 
& wxTE_MULTILINE
) 
1308         g_signal_emit_by_name (m_text
, "paste-clipboard"); 
1310         gtk_editable_paste_clipboard(GTK_EDITABLE(m_text
)); 
1314 void wxTextCtrl::Undo() 
1317     wxFAIL_MSG( wxT("wxTextCtrl::Undo not implemented") ); 
1320 void wxTextCtrl::Redo() 
1323     wxFAIL_MSG( wxT("wxTextCtrl::Redo not implemented") ); 
1326 bool wxTextCtrl::CanUndo() const 
1329     //wxFAIL_MSG( wxT("wxTextCtrl::CanUndo not implemented") ); 
1333 bool wxTextCtrl::CanRedo() const 
1336     //wxFAIL_MSG( wxT("wxTextCtrl::CanRedo not implemented") ); 
1340 // If the return values from and to are the same, there is no 
1342 void wxTextCtrl::GetSelection(long* fromOut
, long* toOut
) const 
1344     wxCHECK_RET( m_text 
!= NULL
, wxT("invalid text ctrl") ); 
1348     bool haveSelection 
= false; 
1350      if (m_windowStyle 
& wxTE_MULTILINE
) 
1352          GtkTextIter ifrom
, ito
; 
1353          if ( gtk_text_buffer_get_selection_bounds(m_buffer
, &ifrom
, &ito
) ) 
1355              haveSelection 
= true; 
1356              from 
= gtk_text_iter_get_offset(&ifrom
); 
1357              to 
= gtk_text_iter_get_offset(&ito
); 
1360      else  // not multi-line 
1362          if ( gtk_editable_get_selection_bounds( GTK_EDITABLE(m_text
), 
1365              haveSelection 
= true; 
1369      if (! haveSelection 
) 
1370           from 
= to 
= GetInsertionPoint(); 
1374          // exchange them to be compatible with wxMSW 
1387 bool wxTextCtrl::IsEditable() const 
1389     wxCHECK_MSG( m_text 
!= NULL
, false, wxT("invalid text ctrl") ); 
1391     if (m_windowStyle 
& wxTE_MULTILINE
) 
1393         return gtk_text_view_get_editable(GTK_TEXT_VIEW(m_text
)); 
1397         return gtk_editable_get_editable(GTK_EDITABLE(m_text
)); 
1401 bool wxTextCtrl::IsModified() const 
1406 void wxTextCtrl::Clear() 
1408     SetValue( wxEmptyString 
); 
1411 void wxTextCtrl::OnChar( wxKeyEvent 
&key_event 
) 
1413     wxCHECK_RET( m_text 
!= NULL
, wxT("invalid text ctrl") ); 
1415     if ((key_event
.GetKeyCode() == WXK_RETURN
) && (m_windowStyle 
& wxTE_PROCESS_ENTER
)) 
1417         wxCommandEvent 
event(wxEVT_COMMAND_TEXT_ENTER
, m_windowId
); 
1418         event
.SetEventObject(this); 
1419         event
.SetString(GetValue()); 
1420         if (GetEventHandler()->ProcessEvent(event
)) return; 
1423     if ((key_event
.GetKeyCode() == WXK_RETURN
) && !(m_windowStyle 
& wxTE_MULTILINE
)) 
1425         // This will invoke the dialog default action, such 
1426         // as the clicking the default button. 
1428         wxWindow 
*top_frame 
= m_parent
; 
1429         while (top_frame
->GetParent() && !(top_frame
->IsTopLevel())) 
1430             top_frame 
= top_frame
->GetParent(); 
1432         if (top_frame 
&& GTK_IS_WINDOW(top_frame
->m_widget
)) 
1434             GtkWindow 
*window 
= GTK_WINDOW(top_frame
->m_widget
); 
1436             if (window
->default_widget
) 
1438                 gtk_widget_activate (window
->default_widget
); 
1447 GtkWidget
* wxTextCtrl::GetConnectWidget() 
1449     return GTK_WIDGET(m_text
); 
1452 bool wxTextCtrl::IsOwnGtkWindow( GdkWindow 
*window 
) 
1454     if (m_windowStyle 
& wxTE_MULTILINE
) 
1456         return window 
== gtk_text_view_get_window( GTK_TEXT_VIEW( m_text 
), GTK_TEXT_WINDOW_TEXT 
);  // pure guesswork 
1460         return (window 
== GTK_ENTRY(m_text
)->text_area
); 
1464 // the font will change for subsequent text insertiongs 
1465 bool wxTextCtrl::SetFont( const wxFont 
&font 
) 
1467     wxCHECK_MSG( m_text 
!= NULL
, false, wxT("invalid text ctrl") ); 
1469     if ( !wxTextCtrlBase::SetFont(font
) ) 
1471         // font didn't change, nothing to do 
1475     if ( m_windowStyle 
& wxTE_MULTILINE 
) 
1477         SetUpdateFont(true); 
1479         m_defaultStyle
.SetFont(font
); 
1481         ChangeFontGlobally(); 
1487 void wxTextCtrl::ChangeFontGlobally() 
1489     // this method is very inefficient and hence should be called as rarely as 
1492     // TODO: it can be implemented much more efficiently for GTK2 
1493     wxASSERT_MSG( (m_windowStyle 
& wxTE_MULTILINE
), 
1494                   _T("shouldn't be called for single line controls") ); 
1496     wxString value 
= GetValue(); 
1497     if ( !value
.empty() ) 
1499         SetUpdateFont(false); 
1506 bool wxTextCtrl::SetForegroundColour(const wxColour
& colour
) 
1508     if ( !wxControl::SetForegroundColour(colour
) ) 
1511     // update default fg colour too 
1512     m_defaultStyle
.SetTextColour(colour
); 
1517 bool wxTextCtrl::SetBackgroundColour( const wxColour 
&colour 
) 
1519     wxCHECK_MSG( m_text 
!= NULL
, false, wxT("invalid text ctrl") ); 
1521     if ( !wxControl::SetBackgroundColour( colour 
) ) 
1524     if (!m_backgroundColour
.Ok()) 
1527     // change active background color too 
1528     m_defaultStyle
.SetBackgroundColour( colour 
); 
1533 bool wxTextCtrl::SetStyle( long start
, long end
, const wxTextAttr
& style 
) 
1535     if ( m_windowStyle 
& wxTE_MULTILINE 
) 
1537         if ( style
.IsDefault() ) 
1543         gint l 
= gtk_text_buffer_get_char_count( m_buffer 
); 
1545         wxCHECK_MSG( start 
>= 0 && end 
<= l
, false, 
1546                      _T("invalid range in wxTextCtrl::SetStyle") ); 
1548         GtkTextIter starti
, endi
; 
1549         gtk_text_buffer_get_iter_at_offset( m_buffer
, &starti
, start 
); 
1550         gtk_text_buffer_get_iter_at_offset( m_buffer
, &endi
, end 
); 
1552         // use the attributes from style which are set in it and fall back 
1553         // first to the default style and then to the text control default 
1554         // colours for the others 
1555         wxTextAttr attr 
= wxTextAttr::Combine(style
, m_defaultStyle
, this); 
1557         wxGtkTextApplyTagsFromAttr( m_buffer
, attr
, &starti
, &endi 
); 
1563     // cannot do this for GTK+'s Entry widget 
1567 void wxTextCtrl::DoApplyWidgetStyle(GtkRcStyle 
*style
) 
1569     gtk_widget_modify_style(m_text
, style
); 
1572 void wxTextCtrl::OnCut(wxCommandEvent
& WXUNUSED(event
)) 
1577 void wxTextCtrl::OnCopy(wxCommandEvent
& WXUNUSED(event
)) 
1582 void wxTextCtrl::OnPaste(wxCommandEvent
& WXUNUSED(event
)) 
1587 void wxTextCtrl::OnUndo(wxCommandEvent
& WXUNUSED(event
)) 
1592 void wxTextCtrl::OnRedo(wxCommandEvent
& WXUNUSED(event
)) 
1597 void wxTextCtrl::OnUpdateCut(wxUpdateUIEvent
& event
) 
1599     event
.Enable( CanCut() ); 
1602 void wxTextCtrl::OnUpdateCopy(wxUpdateUIEvent
& event
) 
1604     event
.Enable( CanCopy() ); 
1607 void wxTextCtrl::OnUpdatePaste(wxUpdateUIEvent
& event
) 
1609     event
.Enable( CanPaste() ); 
1612 void wxTextCtrl::OnUpdateUndo(wxUpdateUIEvent
& event
) 
1614     event
.Enable( CanUndo() ); 
1617 void wxTextCtrl::OnUpdateRedo(wxUpdateUIEvent
& event
) 
1619     event
.Enable( CanRedo() ); 
1622 void wxTextCtrl::OnInternalIdle() 
1624     wxCursor cursor 
= m_cursor
; 
1625     if (g_globalCursor
.Ok()) cursor 
= g_globalCursor
; 
1627     if (g_delayedFocus 
== this) 
1629         if (GTK_WIDGET_REALIZED(m_widget
)) 
1631             gtk_widget_grab_focus( m_widget 
); 
1632             g_delayedFocus 
= NULL
; 
1636     if (wxUpdateUIEvent::CanUpdate(this)) 
1637         UpdateWindowUI(wxUPDATE_UI_FROMIDLE
); 
1640 wxSize 
wxTextCtrl::DoGetBestSize() const 
1642     // FIXME should be different for multi-line controls... 
1643     wxSize 
ret( wxControl::DoGetBestSize() ); 
1644     wxSize 
best(80, ret
.y
); 
1645     CacheBestSize(best
); 
1649 // ---------------------------------------------------------------------------- 
1651 // ---------------------------------------------------------------------------- 
1653 void wxTextCtrl::Freeze() 
1655     if ( HasFlag(wxTE_MULTILINE
) ) 
1657         if ( !m_frozenness
++ ) 
1659             // freeze textview updates and remove buffer 
1660             g_signal_connect (m_text
, "expose_event", 
1661                               G_CALLBACK (gtk_text_exposed_callback
), this); 
1662             g_signal_connect (m_widget
, "expose_event", 
1663                               G_CALLBACK (gtk_text_exposed_callback
), this); 
1664             gtk_widget_set_sensitive(m_widget
, false); 
1665             g_object_ref(m_buffer
); 
1666             gtk_text_view_set_buffer(GTK_TEXT_VIEW(m_text
), gtk_text_buffer_new(NULL
)); 
1671 void wxTextCtrl::Thaw() 
1673     if ( HasFlag(wxTE_MULTILINE
) ) 
1675         wxASSERT_MSG( m_frozenness 
> 0, _T("Thaw() without matching Freeze()") ); 
1677         if ( !--m_frozenness 
) 
1679             // Reattach buffer and thaw textview updates 
1680             gtk_text_view_set_buffer(GTK_TEXT_VIEW(m_text
), m_buffer
); 
1681             g_object_unref(m_buffer
); 
1682             gtk_widget_set_sensitive(m_widget
, true); 
1683             g_signal_handlers_disconnect_by_func (m_widget
, 
1684                     (gpointer
) gtk_text_exposed_callback
, this); 
1685             g_signal_handlers_disconnect_by_func (m_text
, 
1686                     (gpointer
) gtk_text_exposed_callback
, this); 
1691 // ---------------------------------------------------------------------------- 
1692 // wxTextUrlEvent passing if style & wxTE_AUTO_URL 
1693 // ---------------------------------------------------------------------------- 
1695 // FIXME: when dragging on a link the sample gets an "Unknown event". 
1696 // This might be an excessive event from us or a buggy wxMouseEvent::Moving() or 
1697 // a buggy sample, or something else 
1698 void wxTextCtrl::OnUrlMouseEvent(wxMouseEvent
& event
) 
1701     if(!(m_windowStyle 
& wxTE_AUTO_URL
)) 
1705     GtkTextIter start
, end
; 
1706     GtkTextTag 
*tag 
= gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(m_buffer
), 
1709     gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(m_text
), GTK_TEXT_WINDOW_WIDGET
, 
1710                                           event
.GetX(), event
.GetY(), &x
, &y
); 
1712     gtk_text_view_get_iter_at_location(GTK_TEXT_VIEW(m_text
), &end
, x
, y
); 
1713     if (!gtk_text_iter_has_tag(&end
, tag
)) 
1715         gdk_window_set_cursor(gtk_text_view_get_window(GTK_TEXT_VIEW(m_text
), 
1716                               GTK_TEXT_WINDOW_TEXT
), m_gdkXTermCursor
); 
1720     gdk_window_set_cursor(gtk_text_view_get_window(GTK_TEXT_VIEW(m_text
), 
1721                           GTK_TEXT_WINDOW_TEXT
), m_gdkHandCursor
); 
1724     if(!gtk_text_iter_begins_tag(&start
, tag
)) 
1725         gtk_text_iter_backward_to_tag_toggle(&start
, tag
); 
1726     if(!gtk_text_iter_ends_tag(&end
, tag
)) 
1727         gtk_text_iter_forward_to_tag_toggle(&end
, tag
); 
1729     // Native context menu is probably not desired on an URL. 
1730     // Consider making this dependant on ProcessEvent(wxTextUrlEvent) return value 
1731     if(event
.GetEventType() == wxEVT_RIGHT_DOWN
) 
1734     wxTextUrlEvent 
url_event(m_windowId
, event
, 
1735                              gtk_text_iter_get_offset(&start
), 
1736                              gtk_text_iter_get_offset(&end
)); 
1738     InitCommandEvent(url_event
); 
1739     // Is that a good idea? Seems not (pleasure with gtk_text_view_start_selection_drag) 
1740     //event.Skip(!GetEventHandler()->ProcessEvent(url_event)); 
1741     GetEventHandler()->ProcessEvent(url_event
); 
1746 wxTextCtrl::GetClassDefaultAttributes(wxWindowVariant 
WXUNUSED(variant
)) 
1748     return GetDefaultAttributesFromGTKWidget(gtk_entry_new
, true);