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" 
  19     #include "wx/settings.h" 
  23 #include "wx/strconv.h" 
  24 #include "wx/fontutil.h"        // for wxNativeFontInfo (GetNativeFontInfo()) 
  26 #include <sys/types.h> 
  30 #include "wx/gtk/private.h" 
  32 // ---------------------------------------------------------------------------- 
  34 // ---------------------------------------------------------------------------- 
  37 static void wxGtkOnRemoveTag(GtkTextBuffer 
*buffer
, 
  44     g_object_get (tag
, "name", &name
, NULL
); 
  46     if (!name 
|| strncmp(name
, prefix
, strlen(prefix
))) 
  47         // anonymous tag or not starting with prefix - don't remove 
  48         g_signal_stop_emission_by_name (buffer
, "remove_tag"); 
  54 static void wxGtkTextApplyTagsFromAttr(GtkWidget 
*text
, 
  55                                        GtkTextBuffer 
*text_buffer
, 
  56                                        const wxTextAttr
& attr
, 
  60     static gchar buf
[1024]; 
  63     gulong remove_handler_id 
= g_signal_connect (text_buffer
, "remove_tag", 
  64             G_CALLBACK (wxGtkOnRemoveTag
), gpointer("WX")); 
  65     gtk_text_buffer_remove_all_tags(text_buffer
, start
, end
); 
  66     g_signal_handler_disconnect (text_buffer
, remove_handler_id
); 
  70         PangoFontDescription 
*font_description 
= attr
.GetFont().GetNativeFontInfo()->description
; 
  71         wxGtkString 
font_string(pango_font_description_to_string(font_description
)); 
  72         g_snprintf(buf
, sizeof(buf
), "WXFONT %s", font_string
.c_str()); 
  73         tag 
= gtk_text_tag_table_lookup( gtk_text_buffer_get_tag_table( text_buffer 
), 
  76             tag 
= gtk_text_buffer_create_tag( text_buffer
, buf
, 
  77                                               "font-desc", font_description
, 
  79         gtk_text_buffer_apply_tag (text_buffer
, tag
, start
, end
); 
  81         if (attr
.GetFont().GetUnderlined()) 
  83             g_snprintf(buf
, sizeof(buf
), "WXFONTUNDERLINE"); 
  84             tag 
= gtk_text_tag_table_lookup( gtk_text_buffer_get_tag_table( text_buffer 
), 
  87                 tag 
= gtk_text_buffer_create_tag( text_buffer
, buf
, 
  88                                                   "underline-set", TRUE
, 
  89                                                   "underline", PANGO_UNDERLINE_SINGLE
, 
  91             gtk_text_buffer_apply_tag (text_buffer
, tag
, start
, end
); 
  95     if (attr
.HasTextColour()) 
  97         const GdkColor 
*colFg 
= attr
.GetTextColour().GetColor(); 
  98         g_snprintf(buf
, sizeof(buf
), "WXFORECOLOR %d %d %d", 
  99                    colFg
->red
, colFg
->green
, colFg
->blue
); 
 100         tag 
= gtk_text_tag_table_lookup( gtk_text_buffer_get_tag_table( text_buffer 
), 
 103             tag 
= gtk_text_buffer_create_tag( text_buffer
, buf
, 
 104                                               "foreground-gdk", colFg
, NULL 
); 
 105         gtk_text_buffer_apply_tag (text_buffer
, tag
, start
, end
); 
 108     if (attr
.HasBackgroundColour()) 
 110         const GdkColor 
*colBg 
= attr
.GetBackgroundColour().GetColor(); 
 111         g_snprintf(buf
, sizeof(buf
), "WXBACKCOLOR %d %d %d", 
 112                    colBg
->red
, colBg
->green
, colBg
->blue
); 
 113         tag 
= gtk_text_tag_table_lookup( gtk_text_buffer_get_tag_table( text_buffer 
), 
 116             tag 
= gtk_text_buffer_create_tag( text_buffer
, buf
, 
 117                                               "background-gdk", colBg
, NULL 
); 
 118         gtk_text_buffer_apply_tag (text_buffer
, tag
, start
, end
); 
 121     if (attr
.HasAlignment()) 
 123         GtkTextIter para_start
, para_end 
= *end
; 
 124         gtk_text_buffer_get_iter_at_line( text_buffer
, 
 126                                           gtk_text_iter_get_line(start
) ); 
 127         gtk_text_iter_forward_line(¶_end
); 
 129         remove_handler_id 
= g_signal_connect (text_buffer
, "remove_tag", 
 130                                               G_CALLBACK(wxGtkOnRemoveTag
), 
 131                                               gpointer("WXALIGNMENT")); 
 132         gtk_text_buffer_remove_all_tags( text_buffer
, ¶_start
, ¶_end 
); 
 133         g_signal_handler_disconnect (text_buffer
, remove_handler_id
); 
 135         GtkJustification align
; 
 136         switch (attr
.GetAlignment()) 
 139                 align 
= GTK_JUSTIFY_LEFT
; 
 141             case wxTEXT_ALIGNMENT_RIGHT
: 
 142                 align 
= GTK_JUSTIFY_RIGHT
; 
 144             case wxTEXT_ALIGNMENT_CENTER
: 
 145                 align 
= GTK_JUSTIFY_CENTER
; 
 147             // gtk+ doesn't support justify as of gtk+-2.7.4 
 150         g_snprintf(buf
, sizeof(buf
), "WXALIGNMENT %d", align
); 
 151         tag 
= gtk_text_tag_table_lookup( gtk_text_buffer_get_tag_table( text_buffer 
), 
 154             tag 
= gtk_text_buffer_create_tag( text_buffer
, buf
, 
 155                                               "justification", align
, NULL 
); 
 156         gtk_text_buffer_apply_tag( text_buffer
, tag
, ¶_start
, ¶_end 
); 
 159     if (attr
.HasLeftIndent()) 
 161         // Indentation attribute 
 163         // Clear old indentation tags 
 164         GtkTextIter para_start
, para_end 
= *end
; 
 165         gtk_text_buffer_get_iter_at_line( text_buffer
, 
 167                                           gtk_text_iter_get_line(start
) ); 
 168         gtk_text_iter_forward_line(¶_end
); 
 170         remove_handler_id 
= g_signal_connect (text_buffer
, "remove_tag", 
 171                                               G_CALLBACK(wxGtkOnRemoveTag
), 
 172                                               gpointer("WXINDENT")); 
 173         gtk_text_buffer_remove_all_tags( text_buffer
, ¶_start
, ¶_end 
); 
 174         g_signal_handler_disconnect (text_buffer
, remove_handler_id
); 
 176         // Convert indent from 1/10th of a mm into pixels 
 178 #if GTK_CHECK_VERSION(2,2,0) 
 179         if (!gtk_check_version(2,2,0)) 
 180             factor 
= (float)gdk_screen_get_width(gtk_widget_get_screen(text
)) / 
 181                       gdk_screen_get_width_mm(gtk_widget_get_screen(text
)) / 10; 
 184             factor 
= (float)gdk_screen_width() / gdk_screen_width_mm() / 10; 
 186         const int indent 
= (int)(factor 
* attr
.GetLeftIndent()); 
 187         const int subIndent 
= (int)(factor 
* attr
.GetLeftSubIndent()); 
 195             gsubindent 
= -subIndent
; 
 199             gindent 
= -subIndent
; 
 203         g_snprintf(buf
, sizeof(buf
), "WXINDENT %d %d", gindent
, gsubindent
); 
 204         tag 
= gtk_text_tag_table_lookup( gtk_text_buffer_get_tag_table( text_buffer 
), 
 207             tag 
= gtk_text_buffer_create_tag( text_buffer
, buf
, 
 208                                               "left-margin", gindent
, "indent", gsubindent
, NULL 
); 
 209         gtk_text_buffer_apply_tag (text_buffer
, tag
, ¶_start
, ¶_end
); 
 217         GtkTextIter para_start
, para_end 
= *end
; 
 218         gtk_text_buffer_get_iter_at_line( text_buffer
, 
 220                                           gtk_text_iter_get_line(start
) ); 
 221         gtk_text_iter_forward_line(¶_end
); 
 223         remove_handler_id 
= g_signal_connect (text_buffer
, "remove_tag", 
 224                                               G_CALLBACK(wxGtkOnRemoveTag
), 
 226         gtk_text_buffer_remove_all_tags( text_buffer
, ¶_start
, ¶_end 
); 
 227         g_signal_handler_disconnect (text_buffer
, remove_handler_id
); 
 229         const wxArrayInt
& tabs 
= attr
.GetTabs(); 
 231         wxString tagname 
= _T("WXTABS"); 
 232         g_snprintf(buf
, sizeof(buf
), "WXTABS"); 
 233         for (size_t i 
= 0; i 
< tabs
.GetCount(); i
++) 
 234             tagname 
+= wxString::Format(_T(" %d"), tabs
[i
]); 
 236         const wxWX2MBbuf buf 
= tagname
.mb_str(wxConvUTF8
); 
 238         tag 
= gtk_text_tag_table_lookup( gtk_text_buffer_get_tag_table( text_buffer 
), 
 242             // Factor to convert from 1/10th of a mm into pixels 
 244 #if GTK_CHECK_VERSION(2,2,0) 
 245             if (!gtk_check_version(2,2,0)) 
 246                 factor 
= (float)gdk_screen_get_width(gtk_widget_get_screen(text
)) / 
 247                           gdk_screen_get_width_mm(gtk_widget_get_screen(text
)) / 10; 
 250                 factor 
= (float)gdk_screen_width() / gdk_screen_width_mm() / 10; 
 252             PangoTabArray
* tabArray 
= pango_tab_array_new(tabs
.GetCount(), TRUE
); 
 253             for (size_t i 
= 0; i 
< tabs
.GetCount(); i
++) 
 254                 pango_tab_array_set_tab(tabArray
, i
, PANGO_TAB_LEFT
, (gint
)(tabs
[i
] * factor
)); 
 255             tag 
= gtk_text_buffer_create_tag( text_buffer
, buf
, 
 256                                               "tabs", tabArray
, NULL 
); 
 257             pango_tab_array_free(tabArray
); 
 259         gtk_text_buffer_apply_tag (text_buffer
, tag
, ¶_start
, ¶_end
); 
 263 static void wxGtkTextInsert(GtkWidget 
*text
, 
 264                             GtkTextBuffer 
*text_buffer
, 
 265                             const wxTextAttr
& attr
, 
 266                             const wxCharBuffer
& buffer
) 
 270     GtkTextIter iter
, start
; 
 272     gtk_text_buffer_get_iter_at_mark( text_buffer
, &iter
, 
 273                                      gtk_text_buffer_get_insert (text_buffer
) ); 
 274     start_offset 
= gtk_text_iter_get_offset (&iter
); 
 275     gtk_text_buffer_insert( text_buffer
, &iter
, buffer
, strlen(buffer
) ); 
 277     gtk_text_buffer_get_iter_at_offset (text_buffer
, &start
, start_offset
); 
 279     wxGtkTextApplyTagsFromAttr(text
, text_buffer
, attr
, &start
, &iter
); 
 282 // ---------------------------------------------------------------------------- 
 283 // "insert_text" for GtkEntry 
 284 // ---------------------------------------------------------------------------- 
 288 gtk_insert_text_callback(GtkEditable 
*editable
, 
 289                          const gchar 
*new_text
, 
 290                          gint new_text_length
, 
 295         wxapp_install_idle_handler(); 
 297     // we should only be called if we have a max len limit at all 
 298     GtkEntry 
*entry 
= GTK_ENTRY (editable
); 
 300     wxCHECK_RET( entry
->text_max_length
, _T("shouldn't be called") ); 
 302     // check that we don't overflow the max length limit 
 304     // FIXME: this doesn't work when we paste a string which is going to be 
 306     if ( entry
->text_length 
== entry
->text_max_length 
) 
 308         // we don't need to run the base class version at all 
 309         g_signal_stop_emission_by_name (editable
, "insert_text"); 
 311         // remember that the next changed signal is to be ignored to avoid 
 312         // generating a dummy wxEVT_COMMAND_TEXT_UPDATED event 
 313         win
->IgnoreNextTextUpdate(); 
 315         // and generate the correct one ourselves 
 316         wxCommandEvent 
event(wxEVT_COMMAND_TEXT_MAXLEN
, win
->GetId()); 
 317         event
.SetEventObject(win
); 
 318         event
.SetString(win
->GetValue()); 
 319         win
->GetEventHandler()->ProcessEvent( event 
); 
 324 // Implementation of wxTE_AUTO_URL for wxGTK2 by Mart Raudsepp, 
 328 au_apply_tag_callback(GtkTextBuffer 
*buffer
, 
 334     if(tag 
== gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(buffer
), "wxUrl")) 
 335         g_signal_stop_emission_by_name (buffer
, "apply_tag"); 
 339 //----------------------------------------------------------------------------- 
 340 //  GtkTextCharPredicates for gtk_text_iter_*_find_char 
 341 //----------------------------------------------------------------------------- 
 345 pred_whitespace (gunichar ch
, gpointer user_data
) 
 347     return g_unichar_isspace(ch
); 
 353 pred_non_whitespace (gunichar ch
, gpointer user_data
) 
 355     return !g_unichar_isspace(ch
); 
 361 pred_nonpunct (gunichar ch
, gpointer user_data
) 
 363     return !g_unichar_ispunct(ch
); 
 369 pred_nonpunct_or_slash (gunichar ch
, gpointer user_data
) 
 371     return !g_unichar_ispunct(ch
) || ch 
== '/'; 
 375 //----------------------------------------------------------------------------- 
 376 //  Check for links between s and e and correct tags as necessary 
 377 //----------------------------------------------------------------------------- 
 379 // This function should be made match better while being efficient at one point. 
 380 // Most probably with a row of regular expressions. 
 383 au_check_word( GtkTextIter 
*s
, GtkTextIter 
*e 
) 
 385     static const char *URIPrefixes
[] = 
 403     GtkTextIter start 
= *s
, end 
= *e
; 
 404     GtkTextBuffer 
*buffer 
= gtk_text_iter_get_buffer(s
); 
 406     // Get our special link tag 
 407     GtkTextTag 
*tag 
= gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(buffer
), "wxUrl"); 
 409     // Get rid of punctuation from beginning and end. 
 410     // Might want to move this to au_check_range if an improved link checking doesn't 
 411     // use some intelligent punctuation checking itself (beware of undesired iter modifications). 
 412     if(g_unichar_ispunct( gtk_text_iter_get_char( &start 
) ) ) 
 413         gtk_text_iter_forward_find_char( &start
, pred_nonpunct
, NULL
, e 
); 
 415     gtk_text_iter_backward_find_char( &end
, pred_nonpunct_or_slash
, NULL
, &start 
); 
 416     gtk_text_iter_forward_char(&end
); 
 418     gchar
* text 
= gtk_text_iter_get_text( &start
, &end 
); 
 419     size_t len 
= strlen(text
), prefix_len
; 
 422     for( n 
= 0; n 
< WXSIZEOF(URIPrefixes
); ++n 
) 
 424         prefix_len 
= strlen(URIPrefixes
[n
]); 
 425         if((len 
> prefix_len
) && !strncasecmp(text
, URIPrefixes
[n
], prefix_len
)) 
 429     if(n 
< WXSIZEOF(URIPrefixes
)) 
 431         gulong signal_id 
= g_signal_handler_find (buffer
, 
 432                                                   (GSignalMatchType
) (G_SIGNAL_MATCH_FUNC
), 
 434                                                   (gpointer
)au_apply_tag_callback
, NULL
); 
 436         g_signal_handler_block (buffer
, signal_id
); 
 437         gtk_text_buffer_apply_tag(buffer
, tag
, &start
, &end
); 
 438         g_signal_handler_unblock (buffer
, signal_id
); 
 445 au_check_range(GtkTextIter 
*s
, 
 446                GtkTextIter 
*range_end
) 
 448     GtkTextIter range_start 
= *s
; 
 449     GtkTextIter word_end
; 
 450     GtkTextBuffer 
*buffer 
= gtk_text_iter_get_buffer(s
); 
 451     GtkTextTag 
*tag 
= gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(buffer
), "wxUrl"); 
 453     gtk_text_buffer_remove_tag(buffer
, tag
, s
, range_end
); 
 455     if(g_unichar_isspace(gtk_text_iter_get_char(&range_start
))) 
 456         gtk_text_iter_forward_find_char(&range_start
, pred_non_whitespace
, NULL
, range_end
); 
 458     while(!gtk_text_iter_equal(&range_start
, range_end
)) 
 460         word_end 
= range_start
; 
 461         gtk_text_iter_forward_find_char(&word_end
, pred_whitespace
, NULL
, range_end
); 
 463         // Now we should have a word delimited by range_start and word_end, correct link tags 
 464         au_check_word(&range_start
, &word_end
); 
 466         range_start 
= word_end
; 
 467         gtk_text_iter_forward_find_char(&range_start
, pred_non_whitespace
, NULL
, range_end
); 
 472 //----------------------------------------------------------------------------- 
 473 //  "insert-text" for GtkTextBuffer 
 474 //----------------------------------------------------------------------------- 
 478 au_insert_text_callback(GtkTextBuffer 
*buffer
, 
 484     if (!len 
|| !(win
->GetWindowStyleFlag() & wxTE_AUTO_URL
) ) 
 487     GtkTextIter start 
= *end
; 
 488     gtk_text_iter_backward_chars(&start
, g_utf8_strlen(text
, len
)); 
 490     GtkTextIter line_start 
= start
; 
 491     GtkTextIter line_end 
= *end
; 
 492     GtkTextIter words_start 
= start
; 
 493     GtkTextIter words_end 
= *end
; 
 495     gtk_text_iter_set_line(&line_start
, gtk_text_iter_get_line(&start
)); 
 496     gtk_text_iter_forward_to_line_end(&line_end
); 
 497     gtk_text_iter_backward_find_char(&words_start
, pred_whitespace
, NULL
, &line_start
); 
 498     gtk_text_iter_forward_find_char(&words_end
, pred_whitespace
, NULL
, &line_end
); 
 500     au_check_range(&words_start
, &words_end
); 
 504 //----------------------------------------------------------------------------- 
 505 //  "delete-range" for GtkTextBuffer 
 506 //----------------------------------------------------------------------------- 
 510 au_delete_range_callback(GtkTextBuffer 
*buffer
, 
 515     if( !(win
->GetWindowStyleFlag() & wxTE_AUTO_URL
) ) 
 518     GtkTextIter line_start 
= *start
, line_end 
= *end
; 
 520     gtk_text_iter_set_line(&line_start
, gtk_text_iter_get_line(start
)); 
 521     gtk_text_iter_forward_to_line_end(&line_end
); 
 522     gtk_text_iter_backward_find_char(start
, pred_whitespace
, NULL
, &line_start
); 
 523     gtk_text_iter_forward_find_char(end
, pred_whitespace
, NULL
, &line_end
); 
 525     au_check_range(start
, end
); 
 530 //----------------------------------------------------------------------------- 
 532 //----------------------------------------------------------------------------- 
 536 gtk_text_changed_callback( GtkWidget 
*widget
, wxTextCtrl 
*win 
) 
 538     if ( win
->IgnoreTextUpdate() ) 
 541     if (!win
->m_hasVMT
) return; 
 544         wxapp_install_idle_handler(); 
 546     if ( win
->MarkDirtyOnChange() ) 
 549     win
->SendTextUpdatedEvent(); 
 553 //----------------------------------------------------------------------------- 
 554 //  clipboard events: "copy-clipboard", "cut-clipboard", "paste-clipboard" 
 555 //----------------------------------------------------------------------------- 
 557 // common part of the event handlers below 
 559 handle_text_clipboard_callback( GtkWidget 
*widget
, wxTextCtrl 
*win
, 
 560                                 wxEventType eventType
, const gchar 
* signal_name
) 
 562     wxClipboardTextEvent 
event( eventType
, win
->GetId() ); 
 563     event
.SetEventObject( win 
); 
 564     if ( win
->GetEventHandler()->ProcessEvent( event 
) ) 
 566         // don't let the default processing to take place if we did something 
 567         // ourselves in the event handler 
 568         g_signal_stop_emission_by_name (widget
, signal_name
); 
 574 gtk_copy_clipboard_callback( GtkWidget 
*widget
, wxTextCtrl 
*win 
) 
 576     handle_text_clipboard_callback( 
 577         widget
, win
, wxEVT_COMMAND_TEXT_COPY
, "copy-clipboard" ); 
 581 gtk_cut_clipboard_callback( GtkWidget 
*widget
, wxTextCtrl 
*win 
) 
 583     handle_text_clipboard_callback( 
 584         widget
, win
, wxEVT_COMMAND_TEXT_CUT
, "cut-clipboard" ); 
 588 gtk_paste_clipboard_callback( GtkWidget 
*widget
, wxTextCtrl 
*win 
) 
 590     handle_text_clipboard_callback( 
 591         widget
, win
, wxEVT_COMMAND_TEXT_PASTE
, "paste-clipboard" ); 
 595 //----------------------------------------------------------------------------- 
 596 // "expose_event" from scrolled window and textview 
 597 //----------------------------------------------------------------------------- 
 601 gtk_text_exposed_callback( GtkWidget 
*widget
, GdkEventExpose 
*event
, wxTextCtrl 
*win 
) 
 608 //----------------------------------------------------------------------------- 
 610 //----------------------------------------------------------------------------- 
 612 IMPLEMENT_DYNAMIC_CLASS(wxTextCtrl
, wxTextCtrlBase
) 
 614 BEGIN_EVENT_TABLE(wxTextCtrl
, wxTextCtrlBase
) 
 615     EVT_CHAR(wxTextCtrl::OnChar
) 
 617     EVT_MENU(wxID_CUT
, wxTextCtrl::OnCut
) 
 618     EVT_MENU(wxID_COPY
, wxTextCtrl::OnCopy
) 
 619     EVT_MENU(wxID_PASTE
, wxTextCtrl::OnPaste
) 
 620     EVT_MENU(wxID_UNDO
, wxTextCtrl::OnUndo
) 
 621     EVT_MENU(wxID_REDO
, wxTextCtrl::OnRedo
) 
 623     EVT_UPDATE_UI(wxID_CUT
, wxTextCtrl::OnUpdateCut
) 
 624     EVT_UPDATE_UI(wxID_COPY
, wxTextCtrl::OnUpdateCopy
) 
 625     EVT_UPDATE_UI(wxID_PASTE
, wxTextCtrl::OnUpdatePaste
) 
 626     EVT_UPDATE_UI(wxID_UNDO
, wxTextCtrl::OnUpdateUndo
) 
 627     EVT_UPDATE_UI(wxID_REDO
, wxTextCtrl::OnUpdateRedo
) 
 629     // wxTE_AUTO_URL wxTextUrl support. Currently only creates 
 630     // wxTextUrlEvent in the same cases as wxMSW, more can be added here. 
 631     EVT_MOTION      (wxTextCtrl::OnUrlMouseEvent
) 
 632     EVT_LEFT_DOWN   (wxTextCtrl::OnUrlMouseEvent
) 
 633     EVT_LEFT_UP     (wxTextCtrl::OnUrlMouseEvent
) 
 634     EVT_LEFT_DCLICK (wxTextCtrl::OnUrlMouseEvent
) 
 635     EVT_RIGHT_DOWN  (wxTextCtrl::OnUrlMouseEvent
) 
 636     EVT_RIGHT_UP    (wxTextCtrl::OnUrlMouseEvent
) 
 637     EVT_RIGHT_DCLICK(wxTextCtrl::OnUrlMouseEvent
) 
 640 void wxTextCtrl::Init() 
 645     m_countUpdatesToIgnore 
= 0; 
 647     SetUpdateFont(false); 
 651     m_showPositionOnThaw 
= NULL
; 
 652     m_gdkHandCursor 
= NULL
; 
 653     m_gdkXTermCursor 
= NULL
; 
 656 wxTextCtrl::~wxTextCtrl() 
 659         gdk_cursor_unref(m_gdkHandCursor
); 
 661         gdk_cursor_unref(m_gdkXTermCursor
); 
 664 wxTextCtrl::wxTextCtrl( wxWindow 
*parent
, 
 666                         const wxString 
&value
, 
 670                         const wxValidator
& validator
, 
 671                         const wxString 
&name 
) 
 675     Create( parent
, id
, value
, pos
, size
, style
, validator
, name 
); 
 678 bool wxTextCtrl::Create( wxWindow 
*parent
, 
 680                          const wxString 
&value
, 
 684                          const wxValidator
& validator
, 
 685                          const wxString 
&name 
) 
 688     m_acceptsFocus 
= true; 
 690     if (!PreCreation( parent
, pos
, size 
) || 
 691         !CreateBase( parent
, id
, pos
, size
, style
, validator
, name 
)) 
 693         wxFAIL_MSG( wxT("wxTextCtrl creation failed") ); 
 697     bool multi_line 
= (style 
& wxTE_MULTILINE
) != 0; 
 702         m_text 
= gtk_text_view_new(); 
 704         m_buffer 
= gtk_text_view_get_buffer( GTK_TEXT_VIEW(m_text
) ); 
 706         // create "ShowPosition" marker 
 708         gtk_text_buffer_get_start_iter(m_buffer
, &iter
); 
 709         gtk_text_buffer_create_mark(m_buffer
, "ShowPosition", &iter
, true); 
 711         // create scrolled window 
 712         m_widget 
= gtk_scrolled_window_new( NULL
, NULL 
); 
 713         gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( m_widget 
), 
 714                                         GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC 
); 
 715         // for ScrollLines/Pages 
 716         m_scrollBar
[1] = (GtkRange
*)((GtkScrolledWindow
*)m_widget
)->vscrollbar
; 
 718         // Insert view into scrolled window 
 719         gtk_container_add( GTK_CONTAINER(m_widget
), m_text 
); 
 723         GtkScrolledWindowSetBorder(m_widget
, style
); 
 725         gtk_widget_add_events( GTK_WIDGET(m_text
), GDK_ENTER_NOTIFY_MASK 
| GDK_LEAVE_NOTIFY_MASK 
); 
 727         GTK_WIDGET_UNSET_FLAGS( m_widget
, GTK_CAN_FOCUS 
); 
 731         // a single-line text control: no need for scrollbars 
 733         m_text 
= gtk_entry_new(); 
 735         if (style 
& wxNO_BORDER
) 
 736             g_object_set (m_text
, "has-frame", FALSE
, NULL
); 
 739     m_parent
->DoAddChild( this ); 
 741     m_focusWidget 
= m_text
; 
 747         gtk_widget_show(m_text
); 
 755     if (style 
& wxTE_PASSWORD
) 
 758     if (style 
& wxTE_READONLY
) 
 761     // left justification (alignment) is the default anyhow 
 762     if ( style 
& (wxTE_RIGHT 
| wxTE_CENTRE
) ) 
 763         GTKSetJustification(); 
 765     // We want to be notified about text changes. 
 768         g_signal_connect (m_buffer
, "changed", 
 769                           G_CALLBACK (gtk_text_changed_callback
), this); 
 771         // .. and handle URLs on multi-line controls with wxTE_AUTO_URL style 
 772         if (style 
& wxTE_AUTO_URL
) 
 774             GtkTextIter start
, end
; 
 775             m_gdkHandCursor 
= gdk_cursor_new(GDK_HAND2
); 
 776             m_gdkXTermCursor 
= gdk_cursor_new(GDK_XTERM
); 
 778             // We create our wxUrl tag here for slight efficiency gain - we 
 779             // don't have to check for the tag existance in callbacks, 
 780             // hereby it's guaranteed to exist. 
 781             gtk_text_buffer_create_tag(m_buffer
, "wxUrl", 
 782                                        "foreground", "blue", 
 783                                        "underline", PANGO_UNDERLINE_SINGLE
, 
 786             // Check for URLs after each text change 
 787             g_signal_connect_after (m_buffer
, "insert_text", 
 788                                     G_CALLBACK (au_insert_text_callback
), this); 
 789             g_signal_connect_after (m_buffer
, "delete_range", 
 790                                     G_CALLBACK (au_delete_range_callback
), this); 
 792             // Block all wxUrl tag applying unless we do it ourselves, in which case we 
 793             // block this callback temporarily. This takes care of gtk+ internal 
 794             // gtk_text_buffer_insert_range* calls that would copy our URL tag otherwise, 
 795             // which is undesired because only a part of the URL might be copied. 
 796             // The insert-text signal emitted inside it will take care of newly formed 
 797             // or wholly copied URLs. 
 798             g_signal_connect (m_buffer
, "apply_tag", 
 799                               G_CALLBACK (au_apply_tag_callback
), NULL
); 
 801             // Check for URLs in the initial string passed to Create 
 802             gtk_text_buffer_get_start_iter(m_buffer
, &start
); 
 803             gtk_text_buffer_get_end_iter(m_buffer
, &end
); 
 804             au_check_range(&start
, &end
); 
 809         g_signal_connect (m_text
, "changed", 
 810                           G_CALLBACK (gtk_text_changed_callback
), this); 
 813     g_signal_connect (m_text
, "copy-clipboard", 
 814                       G_CALLBACK (gtk_copy_clipboard_callback
), this); 
 815     g_signal_connect (m_text
, "cut-clipboard", 
 816                       G_CALLBACK (gtk_cut_clipboard_callback
), this); 
 817     g_signal_connect (m_text
, "paste-clipboard", 
 818                       G_CALLBACK (gtk_paste_clipboard_callback
), this); 
 820     m_cursor 
= wxCursor( wxCURSOR_IBEAM 
); 
 822     wxTextAttr 
attrDef(GetForegroundColour(), GetBackgroundColour(), GetFont()); 
 823     SetDefaultStyle( attrDef 
); 
 828 // ---------------------------------------------------------------------------- 
 830 // ---------------------------------------------------------------------------- 
 832 void wxTextCtrl::GTKSetEditable() 
 834     gboolean editable 
= !HasFlag(wxTE_READONLY
); 
 835     if ( IsSingleLine() ) 
 836         gtk_editable_set_editable(GTK_EDITABLE(m_text
), editable
); 
 838         gtk_text_view_set_editable(GTK_TEXT_VIEW(m_text
), editable
); 
 841 void wxTextCtrl::GTKSetVisibility() 
 843     // VZ: shouldn't we assert if wxTE_PASSWORD is set for multiline control? 
 844     if ( IsSingleLine() ) 
 845         gtk_entry_set_visibility(GTK_ENTRY(m_text
), !HasFlag(wxTE_PASSWORD
)); 
 848 void wxTextCtrl::GTKSetWrapMode() 
 850     // no wrapping in single line controls 
 851     if ( !IsMultiLine() ) 
 854     // translate wx wrapping style to GTK+ 
 856     if ( HasFlag( wxTE_DONTWRAP 
) ) 
 857         wrap 
= GTK_WRAP_NONE
; 
 858     else if ( HasFlag( wxTE_CHARWRAP 
) ) 
 859         wrap 
= GTK_WRAP_CHAR
; 
 860     else if ( HasFlag( wxTE_WORDWRAP 
) ) 
 861         wrap 
= GTK_WRAP_WORD
; 
 862     else // HasFlag(wxTE_BESTWRAP) always true as wxTE_BESTWRAP == 0 
 864         // GTK_WRAP_WORD_CHAR seems to be new in GTK+ 2.4 
 866         if ( !gtk_check_version(2,4,0) ) 
 868             wrap 
= GTK_WRAP_WORD_CHAR
; 
 871 #endif // __WXGTK24__ 
 872         wrap 
= GTK_WRAP_WORD
; 
 875     gtk_text_view_set_wrap_mode( GTK_TEXT_VIEW( m_text 
), wrap 
); 
 878 void wxTextCtrl::GTKSetJustification() 
 882         GtkJustification just
; 
 883         if ( HasFlag(wxTE_RIGHT
) ) 
 884             just 
= GTK_JUSTIFY_RIGHT
; 
 885         else if ( HasFlag(wxTE_CENTRE
) ) 
 886             just 
= GTK_JUSTIFY_CENTER
; 
 887         else // wxTE_LEFT == 0 
 888             just 
= GTK_JUSTIFY_LEFT
; 
 890         gtk_text_view_set_justification(GTK_TEXT_VIEW(m_text
), just
); 
 895         // gtk_entry_set_alignment was introduced in gtk+-2.3.5 
 896         if (!gtk_check_version(2,4,0)) 
 899             if ( HasFlag(wxTE_RIGHT
) ) 
 901             else if ( HasFlag(wxTE_CENTRE
) ) 
 906             gtk_entry_set_alignment(GTK_ENTRY(m_text
), align
); 
 908 #endif // __WXGTK24__ 
 913 void wxTextCtrl::SetWindowStyleFlag(long style
) 
 915     long styleOld 
= GetWindowStyleFlag(); 
 917     wxTextCtrlBase::SetWindowStyleFlag(style
); 
 919     if ( (style 
& wxTE_READONLY
) != (styleOld 
& wxTE_READONLY
) ) 
 922     if ( (style 
& wxTE_PASSWORD
) != (styleOld 
& wxTE_PASSWORD
) ) 
 925     static const long flagsWrap 
= wxTE_WORDWRAP 
| wxTE_CHARWRAP 
| wxTE_DONTWRAP
; 
 926     if ( (style 
& flagsWrap
) != (styleOld 
& flagsWrap
) ) 
 929     static const long flagsAlign 
= wxTE_LEFT 
| wxTE_CENTRE 
| wxTE_RIGHT
; 
 930     if ( (style 
& flagsAlign
) != (styleOld 
& flagsAlign
) ) 
 931         GTKSetJustification(); 
 934 // ---------------------------------------------------------------------------- 
 936 // ---------------------------------------------------------------------------- 
 938 wxString 
wxTextCtrl::GetValue() const 
 940     wxCHECK_MSG( m_text 
!= NULL
, wxEmptyString
, wxT("invalid text ctrl") ); 
 946         gtk_text_buffer_get_start_iter( m_buffer
, &start 
); 
 948         gtk_text_buffer_get_end_iter( m_buffer
, &end 
); 
 949         wxGtkString 
text(gtk_text_buffer_get_text(m_buffer
, &start
, &end
, true)); 
 951         const wxWxCharBuffer buf 
= wxGTK_CONV_BACK(text
); 
 957         const gchar 
*text 
= gtk_entry_get_text( GTK_ENTRY(m_text
) ); 
 958         const wxWxCharBuffer buf 
= wxGTK_CONV_BACK( text 
); 
 966 wxFontEncoding 
wxTextCtrl::GetTextEncoding() const 
 968     // GTK+ uses UTF-8 internally, we need to convert to it but from which 
 971     // first check the default text style (we intentionally don't check the 
 972     // style for the current position as it doesn't make sense for SetValue()) 
 973     const wxTextAttr
& style 
= GetDefaultStyle(); 
 974     wxFontEncoding enc 
= style
.HasFont() ? style
.GetFont().GetEncoding() 
 975                                          : wxFONTENCODING_SYSTEM
; 
 977     // fall back to the controls font if no style 
 978     if ( enc 
== wxFONTENCODING_SYSTEM 
&& m_hasFont 
) 
 979         enc 
= GetFont().GetEncoding(); 
 984 bool wxTextCtrl::IsEmpty() const 
 987         return gtk_text_buffer_get_char_count(m_buffer
) == 0; 
 989     return wxTextCtrlBase::IsEmpty(); 
 992 void wxTextCtrl::DoSetValue( const wxString 
&value
, int flags 
) 
 994     wxCHECK_RET( m_text 
!= NULL
, wxT("invalid text ctrl") ); 
 996     // the control won't be modified any more as we programmatically replace 
 997     // all the existing text, so reset the flag and don't set it again (and do 
 998     // it now, before the text event handler is ran so that IsModified() called 
 999     // from there returns the expected value) 
1001     DontMarkDirtyOnNextChange(); 
1003     const wxCharBuffer 
buffer(wxGTK_CONV_ENC(value
, GetTextEncoding())); 
1006         // see comment in WriteText() as to why we must warn the user about 
1008         wxLogWarning(_("Failed to set text in the text control.")); 
1012     // if the control is not empty, two "changed" signals are emitted, 
1013     // otherwise only one and we need to ignore either both or one of them 
1014     int ignore 
= flags 
& SetValue_SendEvent 
? 0 : 1; 
1019         IgnoreNextTextUpdate(ignore
); 
1021     if ( IsMultiLine() ) 
1023         gtk_text_buffer_set_text( m_buffer
, buffer
, strlen(buffer
) ); 
1027         gtk_entry_set_text( GTK_ENTRY(m_text
), buffer 
); 
1030     // if, for whatever reason, the callback wasn't called the expected number 
1031     // of times, still reset the flags to the default values 
1032     m_dontMarkDirty 
= false; 
1033     m_countUpdatesToIgnore 
= 0; 
1036     // GRG, Jun/2000: Changed this after a lot of discussion in 
1037     //   the lists. wxWidgets 2.2 will have a set of flags to 
1038     //   customize this behaviour. 
1039     SetInsertionPoint(0); 
1042 void wxTextCtrl::WriteText( const wxString 
&text 
) 
1044     wxCHECK_RET( m_text 
!= NULL
, wxT("invalid text ctrl") ); 
1049     // check if we have a specific style for the current position 
1050     wxFontEncoding enc 
= wxFONTENCODING_SYSTEM
; 
1052     if ( GetStyle(GetInsertionPoint(), style
) && style
.HasFont() ) 
1054         enc 
= style
.GetFont().GetEncoding(); 
1057     if ( enc 
== wxFONTENCODING_SYSTEM 
) 
1058         enc 
= GetTextEncoding(); 
1060     const wxCharBuffer 
buffer(wxGTK_CONV_ENC(text
, enc
)); 
1063         // we must log an error here as losing the text like this can be a 
1064         // serious problem (e.g. imagine the document edited by user being 
1065         // empty instead of containing the correct text) 
1066         wxLogWarning(_("Failed to insert text in the control.")); 
1070     // we're changing the text programmatically 
1071     DontMarkDirtyOnNextChange(); 
1073     if ( IsMultiLine() ) 
1075         // First remove the selection if there is one 
1076         // TODO:  Is there an easier GTK specific way to do this? 
1078         GetSelection(&from
, &to
); 
1083         wxGtkTextInsert( m_text
, m_buffer
, m_defaultStyle
, buffer 
); 
1085         GtkAdjustment 
*adj 
= gtk_scrolled_window_get_vadjustment( GTK_SCROLLED_WINDOW(m_widget
) ); 
1086         // Scroll to cursor, but only if scrollbar thumb is at the very bottom 
1087         // won't work when frozen, text view is not using m_buffer then 
1088         if (!IsFrozen() && wxIsSameDouble(adj
->value
, adj
->upper 
- adj
->page_size
)) 
1090             gtk_text_view_scroll_to_mark( GTK_TEXT_VIEW(m_text
), 
1091                     gtk_text_buffer_get_insert( m_buffer 
), 0.0, FALSE
, 0.0, 1.0 ); 
1096         // First remove the selection if there is one 
1097         gtk_editable_delete_selection( GTK_EDITABLE(m_text
) ); 
1099         // This moves the cursor pos to behind the inserted text. 
1100         gint len 
= gtk_editable_get_position(GTK_EDITABLE(m_text
)); 
1102         gtk_editable_insert_text( GTK_EDITABLE(m_text
), buffer
, strlen(buffer
), &len 
); 
1104         // Bring entry's cursor uptodate. 
1105         gtk_editable_set_position( GTK_EDITABLE(m_text
), len 
); 
1109 void wxTextCtrl::AppendText( const wxString 
&text 
) 
1111     SetInsertionPointEnd(); 
1115 wxString 
wxTextCtrl::GetLineText( long lineNo 
) const 
1118     if ( IsMultiLine() ) 
1121         gtk_text_buffer_get_iter_at_line(m_buffer
,&line
,lineNo
); 
1122         GtkTextIter end 
= line
; 
1123         gtk_text_iter_forward_to_line_end(&end
); 
1124         wxGtkString 
text(gtk_text_buffer_get_text(m_buffer
, &line
, &end
, true)); 
1125         result 
= wxGTK_CONV_BACK(text
); 
1130             result 
= GetValue(); 
1135 void wxTextCtrl::OnDropFiles( wxDropFilesEvent 
&WXUNUSED(event
) ) 
1137   /* If you implement this, don't forget to update the documentation! 
1138    * (file docs/latex/wx/text.tex) */ 
1139     wxFAIL_MSG( wxT("wxTextCtrl::OnDropFiles not implemented") ); 
1142 bool wxTextCtrl::PositionToXY(long pos
, long *x
, long *y 
) const 
1144     if ( IsMultiLine() ) 
1148         if (pos 
> GetLastPosition()) 
1151         gtk_text_buffer_get_iter_at_offset(m_buffer
, &iter
, pos
); 
1154             *y 
= gtk_text_iter_get_line(&iter
); 
1156             *x 
= gtk_text_iter_get_line_offset(&iter
); 
1158     else // single line control 
1160         if ( pos 
<= GTK_ENTRY(m_text
)->text_length 
) 
1169             // index out of bounds 
1177 long wxTextCtrl::XYToPosition(long x
, long y 
) const 
1179     if ( IsSingleLine() ) 
1183     if (y 
>= gtk_text_buffer_get_line_count (m_buffer
)) 
1186     gtk_text_buffer_get_iter_at_line(m_buffer
, &iter
, y
); 
1187     if (x 
>= gtk_text_iter_get_chars_in_line (&iter
)) 
1190     return gtk_text_iter_get_offset(&iter
) + x
; 
1193 int wxTextCtrl::GetLineLength(long lineNo
) const 
1195     if ( IsMultiLine() ) 
1197         int last_line 
= gtk_text_buffer_get_line_count( m_buffer 
) - 1; 
1198         if (lineNo 
> last_line
) 
1202         gtk_text_buffer_get_iter_at_line(m_buffer
, &iter
, lineNo
); 
1203         // get_chars_in_line return includes paragraph delimiters, so need to subtract 1 IF it is not the last line 
1204         return gtk_text_iter_get_chars_in_line(&iter
) - ((lineNo 
== last_line
) ? 0 : 1); 
1208         wxString str 
= GetLineText (lineNo
); 
1209         return (int) str
.length(); 
1213 int wxTextCtrl::GetNumberOfLines() const 
1215     if ( IsMultiLine() ) 
1217         return gtk_text_buffer_get_line_count( m_buffer 
); 
1225 void wxTextCtrl::SetInsertionPoint( long pos 
) 
1227     wxCHECK_RET( m_text 
!= NULL
, wxT("invalid text ctrl") ); 
1229     if ( IsMultiLine() ) 
1232         gtk_text_buffer_get_iter_at_offset( m_buffer
, &iter
, pos 
); 
1233         gtk_text_buffer_place_cursor( m_buffer
, &iter 
); 
1234         GtkTextMark
* mark 
= gtk_text_buffer_get_insert(m_buffer
); 
1236             // defer until Thaw, text view is not using m_buffer now 
1237             m_showPositionOnThaw 
= mark
; 
1239             gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(m_text
), mark
); 
1243         // FIXME: Is the editable's cursor really uptodate without double set_position in GTK2? 
1244         gtk_editable_set_position(GTK_EDITABLE(m_text
), int(pos
)); 
1248 void wxTextCtrl::SetInsertionPointEnd() 
1250     wxCHECK_RET( m_text 
!= NULL
, wxT("invalid text ctrl") ); 
1252     if ( IsMultiLine() ) 
1255         gtk_text_buffer_get_end_iter( m_buffer
, &end 
); 
1256         gtk_text_buffer_place_cursor( m_buffer
, &end 
); 
1260         gtk_editable_set_position( GTK_EDITABLE(m_text
), -1 ); 
1264 void wxTextCtrl::SetEditable( bool editable 
) 
1266     wxCHECK_RET( m_text 
!= NULL
, wxT("invalid text ctrl") ); 
1268     if ( IsMultiLine() ) 
1270         gtk_text_view_set_editable( GTK_TEXT_VIEW(m_text
), editable 
); 
1274         gtk_editable_set_editable( GTK_EDITABLE(m_text
), editable 
); 
1278 bool wxTextCtrl::Enable( bool enable 
) 
1280     if (!wxWindowBase::Enable(enable
)) 
1286     if ( IsMultiLine() ) 
1288         SetEditable( enable 
); 
1292         gtk_widget_set_sensitive( m_text
, enable 
); 
1298 // wxGTK-specific: called recursively by Enable, 
1299 // to give widgets an oppprtunity to correct their colours after they 
1300 // have been changed by Enable 
1301 void wxTextCtrl::OnParentEnable( bool enable 
) 
1303     // If we have a custom background colour, we use this colour in both 
1304     // disabled and enabled mode, or we end up with a different colour under the 
1306     wxColour oldColour 
= GetBackgroundColour(); 
1309         // Need to set twice or it'll optimize the useful stuff out 
1310         if (oldColour 
== * wxWHITE
) 
1311             SetBackgroundColour(*wxBLACK
); 
1313             SetBackgroundColour(*wxWHITE
); 
1314         SetBackgroundColour(oldColour
); 
1318 void wxTextCtrl::MarkDirty() 
1323 void wxTextCtrl::DiscardEdits() 
1328 // ---------------------------------------------------------------------------- 
1329 // max text length support 
1330 // ---------------------------------------------------------------------------- 
1332 bool wxTextCtrl::IgnoreTextUpdate() 
1334     if ( m_countUpdatesToIgnore 
> 0 ) 
1336         m_countUpdatesToIgnore
--; 
1344 bool wxTextCtrl::MarkDirtyOnChange() 
1346     if ( m_dontMarkDirty 
) 
1348         m_dontMarkDirty 
= false; 
1356 void wxTextCtrl::SetMaxLength(unsigned long len
) 
1358     if ( !HasFlag(wxTE_MULTILINE
) ) 
1360         gtk_entry_set_max_length(GTK_ENTRY(m_text
), len
); 
1362         // there is a bug in GTK+ 1.2.x: "changed" signal is emitted even if 
1363         // we had tried to enter more text than allowed by max text length and 
1364         // the text wasn't really changed 
1366         // to detect this and generate TEXT_MAXLEN event instead of 
1367         // TEXT_CHANGED one in this case we also catch "insert_text" signal 
1369         // when max len is set to 0 we disconnect our handler as it means that 
1370         // we shouldn't check anything any more 
1373             g_signal_connect (m_text
, "insert_text", 
1374                               G_CALLBACK (gtk_insert_text_callback
), this); 
1378             g_signal_handlers_disconnect_by_func (m_text
, 
1379                     (gpointer
) gtk_insert_text_callback
, this); 
1384 void wxTextCtrl::SetSelection( long from
, long to 
) 
1386     wxCHECK_RET( m_text 
!= NULL
, wxT("invalid text ctrl") ); 
1388     if (from 
== -1 && to 
== -1) 
1391         to 
= GetValue().length(); 
1394     if ( IsMultiLine() ) 
1396         GtkTextIter fromi
, toi
; 
1397         gtk_text_buffer_get_iter_at_offset( m_buffer
, &fromi
, from 
); 
1398         gtk_text_buffer_get_iter_at_offset( m_buffer
, &toi
, to 
); 
1400         gtk_text_buffer_place_cursor( m_buffer
, &toi 
); 
1401         gtk_text_buffer_move_mark_by_name( m_buffer
, "selection_bound", &fromi 
); 
1405         gtk_editable_select_region( GTK_EDITABLE(m_text
), (gint
)from
, (gint
)to 
); 
1409 void wxTextCtrl::ShowPosition( long pos 
) 
1414         gtk_text_buffer_get_iter_at_offset(m_buffer
, &iter
, int(pos
)); 
1415         GtkTextMark
* mark 
= gtk_text_buffer_get_mark(m_buffer
, "ShowPosition"); 
1416         gtk_text_buffer_move_mark(m_buffer
, mark
, &iter
); 
1418             // defer until Thaw, text view is not using m_buffer now 
1419             m_showPositionOnThaw 
= mark
; 
1421             gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(m_text
), mark
); 
1425 wxTextCtrlHitTestResult
 
1426 wxTextCtrl::HitTest(const wxPoint
& pt
, long *pos
) const 
1428     if ( !IsMultiLine() ) 
1431         return wxTE_HT_UNKNOWN
; 
1435     gtk_text_view_window_to_buffer_coords
 
1437         GTK_TEXT_VIEW(m_text
), 
1438         GTK_TEXT_WINDOW_TEXT
, 
1444     gtk_text_view_get_iter_at_location(GTK_TEXT_VIEW(m_text
), &iter
, x
, y
); 
1446         *pos 
= gtk_text_iter_get_offset(&iter
); 
1448     return wxTE_HT_ON_TEXT
; 
1451 long wxTextCtrl::GetInsertionPoint() const 
1453     wxCHECK_MSG( m_text 
!= NULL
, 0, wxT("invalid text ctrl") ); 
1455     if ( IsMultiLine() ) 
1457         // There is no direct accessor for the cursor, but 
1458         // internally, the cursor is the "mark" called 
1459         // "insert" in the text view's btree structure. 
1461         GtkTextMark 
*mark 
= gtk_text_buffer_get_insert( m_buffer 
); 
1463         gtk_text_buffer_get_iter_at_mark( m_buffer
, &cursor
, mark 
); 
1465         return gtk_text_iter_get_offset( &cursor 
); 
1469         return (long) gtk_editable_get_position(GTK_EDITABLE(m_text
)); 
1473 wxTextPos 
wxTextCtrl::GetLastPosition() const 
1475     wxCHECK_MSG( m_text 
!= NULL
, 0, wxT("invalid text ctrl") ); 
1479     if ( IsMultiLine() ) 
1482         gtk_text_buffer_get_end_iter( m_buffer
, &end 
); 
1484         pos 
= gtk_text_iter_get_offset( &end 
); 
1488         pos 
= GTK_ENTRY(m_text
)->text_length
; 
1494 void wxTextCtrl::Remove( long from
, long to 
) 
1496     wxCHECK_RET( m_text 
!= NULL
, wxT("invalid text ctrl") ); 
1498     if ( IsMultiLine() ) 
1500         GtkTextIter fromi
, toi
; 
1501         gtk_text_buffer_get_iter_at_offset( m_buffer
, &fromi
, from 
); 
1502         gtk_text_buffer_get_iter_at_offset( m_buffer
, &toi
, to 
); 
1504         gtk_text_buffer_delete( m_buffer
, &fromi
, &toi 
); 
1507         gtk_editable_delete_text( GTK_EDITABLE(m_text
), (gint
)from
, (gint
)to 
); 
1510 void wxTextCtrl::Replace( long from
, long to
, const wxString 
&value 
) 
1512     wxCHECK_RET( m_text 
!= NULL
, wxT("invalid text ctrl") ); 
1518         SetInsertionPoint( from 
); 
1523 void wxTextCtrl::Cut() 
1525     wxCHECK_RET( m_text 
!= NULL
, wxT("invalid text ctrl") ); 
1527     if ( IsMultiLine() ) 
1528         g_signal_emit_by_name (m_text
, "cut-clipboard"); 
1530         gtk_editable_cut_clipboard(GTK_EDITABLE(m_text
)); 
1533 void wxTextCtrl::Copy() 
1535     wxCHECK_RET( m_text 
!= NULL
, wxT("invalid text ctrl") ); 
1537     if ( IsMultiLine() ) 
1538         g_signal_emit_by_name (m_text
, "copy-clipboard"); 
1540         gtk_editable_copy_clipboard(GTK_EDITABLE(m_text
)); 
1543 void wxTextCtrl::Paste() 
1545     wxCHECK_RET( m_text 
!= NULL
, wxT("invalid text ctrl") ); 
1547     if ( IsMultiLine() ) 
1548         g_signal_emit_by_name (m_text
, "paste-clipboard"); 
1550         gtk_editable_paste_clipboard(GTK_EDITABLE(m_text
)); 
1554 void wxTextCtrl::Undo() 
1557     wxFAIL_MSG( wxT("wxTextCtrl::Undo not implemented") ); 
1560 void wxTextCtrl::Redo() 
1563     wxFAIL_MSG( wxT("wxTextCtrl::Redo not implemented") ); 
1566 bool wxTextCtrl::CanUndo() const 
1569     //wxFAIL_MSG( wxT("wxTextCtrl::CanUndo not implemented") ); 
1573 bool wxTextCtrl::CanRedo() const 
1576     //wxFAIL_MSG( wxT("wxTextCtrl::CanRedo not implemented") ); 
1580 // If the return values from and to are the same, there is no 
1582 void wxTextCtrl::GetSelection(long* fromOut
, long* toOut
) const 
1584     wxCHECK_RET( m_text 
!= NULL
, wxT("invalid text ctrl") ); 
1588     bool haveSelection 
= false; 
1590      if ( IsMultiLine() ) 
1592          GtkTextIter ifrom
, ito
; 
1593          if ( gtk_text_buffer_get_selection_bounds(m_buffer
, &ifrom
, &ito
) ) 
1595              haveSelection 
= true; 
1596              from 
= gtk_text_iter_get_offset(&ifrom
); 
1597              to 
= gtk_text_iter_get_offset(&ito
); 
1600      else  // not multi-line 
1602          if ( gtk_editable_get_selection_bounds( GTK_EDITABLE(m_text
), 
1605              haveSelection 
= true; 
1609      if (! haveSelection 
) 
1610           from 
= to 
= GetInsertionPoint(); 
1614          // exchange them to be compatible with wxMSW 
1627 bool wxTextCtrl::IsEditable() const 
1629     wxCHECK_MSG( m_text 
!= NULL
, false, wxT("invalid text ctrl") ); 
1631     if ( IsMultiLine() ) 
1633         return gtk_text_view_get_editable(GTK_TEXT_VIEW(m_text
)); 
1637         return gtk_editable_get_editable(GTK_EDITABLE(m_text
)); 
1641 bool wxTextCtrl::IsModified() const 
1646 void wxTextCtrl::Clear() 
1648     SetValue( wxEmptyString 
); 
1651 void wxTextCtrl::OnChar( wxKeyEvent 
&key_event 
) 
1653     wxCHECK_RET( m_text 
!= NULL
, wxT("invalid text ctrl") ); 
1655     if ( key_event
.GetKeyCode() == WXK_RETURN 
) 
1657         if ( HasFlag(wxTE_PROCESS_ENTER
) ) 
1659             wxCommandEvent 
event(wxEVT_COMMAND_TEXT_ENTER
, m_windowId
); 
1660             event
.SetEventObject(this); 
1661             event
.SetString(GetValue()); 
1662             if ( GetEventHandler()->ProcessEvent(event
) ) 
1666         // FIXME: this is not the right place to do it, wxDialog::OnCharHook() 
1668         if ( IsSingleLine() ) 
1670             // This will invoke the dialog default action, such 
1671             // as the clicking the default button. 
1673             wxWindow 
*top_frame 
= m_parent
; 
1674             while (top_frame
->GetParent() && !(top_frame
->IsTopLevel())) 
1675                 top_frame 
= top_frame
->GetParent(); 
1677             if (top_frame 
&& GTK_IS_WINDOW(top_frame
->m_widget
)) 
1679                 GtkWindow 
*window 
= GTK_WINDOW(top_frame
->m_widget
); 
1681                 if (window
->default_widget
) 
1683                     gtk_widget_activate (window
->default_widget
); 
1693 GtkWidget
* wxTextCtrl::GetConnectWidget() 
1695     return GTK_WIDGET(m_text
); 
1698 GdkWindow 
*wxTextCtrl::GTKGetWindow(wxArrayGdkWindows
& WXUNUSED(windows
)) const 
1700     if ( IsMultiLine() ) 
1702         return gtk_text_view_get_window(GTK_TEXT_VIEW(m_text
), 
1703                                         GTK_TEXT_WINDOW_TEXT 
); 
1707         return GTK_ENTRY(m_text
)->text_area
; 
1711 // the font will change for subsequent text insertiongs 
1712 bool wxTextCtrl::SetFont( const wxFont 
&font 
) 
1714     wxCHECK_MSG( m_text 
!= NULL
, false, wxT("invalid text ctrl") ); 
1716     if ( !wxTextCtrlBase::SetFont(font
) ) 
1718         // font didn't change, nothing to do 
1722     if ( IsMultiLine() ) 
1724         SetUpdateFont(true); 
1726         m_defaultStyle
.SetFont(font
); 
1728         ChangeFontGlobally(); 
1734 void wxTextCtrl::ChangeFontGlobally() 
1736     // this method is very inefficient and hence should be called as rarely as 
1739     // TODO: it can be implemented much more efficiently for GTK2 
1740     wxASSERT_MSG( IsMultiLine(), 
1741                   _T("shouldn't be called for single line controls") ); 
1743     wxString value 
= GetValue(); 
1744     if ( !value
.empty() ) 
1746         SetUpdateFont(false); 
1753 bool wxTextCtrl::SetForegroundColour(const wxColour
& colour
) 
1755     if ( !wxControl::SetForegroundColour(colour
) ) 
1758     // update default fg colour too 
1759     m_defaultStyle
.SetTextColour(colour
); 
1764 bool wxTextCtrl::SetBackgroundColour( const wxColour 
&colour 
) 
1766     wxCHECK_MSG( m_text 
!= NULL
, false, wxT("invalid text ctrl") ); 
1768     if ( !wxControl::SetBackgroundColour( colour 
) ) 
1771     if (!m_backgroundColour
.Ok()) 
1774     // change active background color too 
1775     m_defaultStyle
.SetBackgroundColour( colour 
); 
1780 bool wxTextCtrl::SetStyle( long start
, long end
, const wxTextAttr
& style 
) 
1782     if ( IsMultiLine() ) 
1784         if ( style
.IsDefault() ) 
1790         gint l 
= gtk_text_buffer_get_char_count( m_buffer 
); 
1792         wxCHECK_MSG( start 
>= 0 && end 
<= l
, false, 
1793                      _T("invalid range in wxTextCtrl::SetStyle") ); 
1795         GtkTextIter starti
, endi
; 
1796         gtk_text_buffer_get_iter_at_offset( m_buffer
, &starti
, start 
); 
1797         gtk_text_buffer_get_iter_at_offset( m_buffer
, &endi
, end 
); 
1799         // use the attributes from style which are set in it and fall back 
1800         // first to the default style and then to the text control default 
1801         // colours for the others 
1802         wxTextAttr attr 
= wxTextAttr::Combine(style
, m_defaultStyle
, this); 
1804         wxGtkTextApplyTagsFromAttr( m_widget
, m_buffer
, attr
, &starti
, &endi 
); 
1810     // cannot do this for GTK+'s Entry widget 
1814 void wxTextCtrl::DoApplyWidgetStyle(GtkRcStyle 
*style
) 
1816     gtk_widget_modify_style(m_text
, style
); 
1819 void wxTextCtrl::OnCut(wxCommandEvent
& WXUNUSED(event
)) 
1824 void wxTextCtrl::OnCopy(wxCommandEvent
& WXUNUSED(event
)) 
1829 void wxTextCtrl::OnPaste(wxCommandEvent
& WXUNUSED(event
)) 
1834 void wxTextCtrl::OnUndo(wxCommandEvent
& WXUNUSED(event
)) 
1839 void wxTextCtrl::OnRedo(wxCommandEvent
& WXUNUSED(event
)) 
1844 void wxTextCtrl::OnUpdateCut(wxUpdateUIEvent
& event
) 
1846     event
.Enable( CanCut() ); 
1849 void wxTextCtrl::OnUpdateCopy(wxUpdateUIEvent
& event
) 
1851     event
.Enable( CanCopy() ); 
1854 void wxTextCtrl::OnUpdatePaste(wxUpdateUIEvent
& event
) 
1856     event
.Enable( CanPaste() ); 
1859 void wxTextCtrl::OnUpdateUndo(wxUpdateUIEvent
& event
) 
1861     event
.Enable( CanUndo() ); 
1864 void wxTextCtrl::OnUpdateRedo(wxUpdateUIEvent
& event
) 
1866     event
.Enable( CanRedo() ); 
1869 wxSize 
wxTextCtrl::DoGetBestSize() const 
1871     // FIXME should be different for multi-line controls... 
1872     wxSize 
ret( wxControl::DoGetBestSize() ); 
1873     wxSize 
best(80, ret
.y
); 
1874     CacheBestSize(best
); 
1878 // ---------------------------------------------------------------------------- 
1880 // ---------------------------------------------------------------------------- 
1882 void wxTextCtrl::Freeze() 
1884     wxCHECK_RET(m_text 
!= NULL
, wxT("invalid text ctrl")); 
1886     if ( HasFlag(wxTE_MULTILINE
) ) 
1888         if (m_freezeCount
++ == 0) 
1890             // freeze textview updates and remove buffer 
1891             g_signal_connect (m_text
, "expose_event", 
1892                               G_CALLBACK (gtk_text_exposed_callback
), this); 
1893             g_signal_connect (m_widget
, "expose_event", 
1894                               G_CALLBACK (gtk_text_exposed_callback
), this); 
1895             gtk_widget_set_sensitive(m_widget
, false); 
1896             g_object_ref(m_buffer
); 
1897             GtkTextBuffer
* buf_new 
= gtk_text_buffer_new(NULL
); 
1898             GtkTextMark
* mark 
= GTK_TEXT_VIEW(m_text
)->first_para_mark
; 
1899             gtk_text_view_set_buffer(GTK_TEXT_VIEW(m_text
), buf_new
); 
1900             // gtk_text_view_set_buffer adds its own reference 
1901             g_object_unref(buf_new
); 
1902             // This mark should be deleted when the buffer is changed, 
1903             // but it's not (in GTK+ up to at least 2.10.6). 
1904             // Otherwise these anonymous marks start to build up in the buffer, 
1905             // and Freeze takes longer and longer each time it is called. 
1906             if (GTK_IS_TEXT_MARK(mark
) && !gtk_text_mark_get_deleted(mark
)) 
1907                 gtk_text_buffer_delete_mark(m_buffer
, mark
); 
1912 void wxTextCtrl::Thaw() 
1914     if ( HasFlag(wxTE_MULTILINE
) ) 
1916         wxCHECK_RET(m_freezeCount 
!= 0, _T("Thaw() without matching Freeze()")); 
1918         if (--m_freezeCount 
== 0) 
1920             // Reattach buffer and thaw textview updates 
1921             gtk_text_view_set_buffer(GTK_TEXT_VIEW(m_text
), m_buffer
); 
1922             g_object_unref(m_buffer
); 
1923             gtk_widget_set_sensitive(m_widget
, true); 
1924             g_signal_handlers_disconnect_by_func (m_widget
, 
1925                     (gpointer
) gtk_text_exposed_callback
, this); 
1926             g_signal_handlers_disconnect_by_func (m_text
, 
1927                     (gpointer
) gtk_text_exposed_callback
, this); 
1928             if (m_showPositionOnThaw 
!= NULL
) 
1930                 gtk_text_view_scroll_mark_onscreen( 
1931                     GTK_TEXT_VIEW(m_text
), m_showPositionOnThaw
); 
1932                 m_showPositionOnThaw 
= NULL
; 
1938 // ---------------------------------------------------------------------------- 
1939 // wxTextUrlEvent passing if style & wxTE_AUTO_URL 
1940 // ---------------------------------------------------------------------------- 
1942 // FIXME: when dragging on a link the sample gets an "Unknown event". 
1943 // This might be an excessive event from us or a buggy wxMouseEvent::Moving() or 
1944 // a buggy sample, or something else 
1945 void wxTextCtrl::OnUrlMouseEvent(wxMouseEvent
& event
) 
1948     if( !HasFlag(wxTE_AUTO_URL
) ) 
1952     GtkTextIter start
, end
; 
1953     GtkTextTag 
*tag 
= gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(m_buffer
), 
1956     gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(m_text
), GTK_TEXT_WINDOW_WIDGET
, 
1957                                           event
.GetX(), event
.GetY(), &x
, &y
); 
1959     gtk_text_view_get_iter_at_location(GTK_TEXT_VIEW(m_text
), &end
, x
, y
); 
1960     if (!gtk_text_iter_has_tag(&end
, tag
)) 
1962         gdk_window_set_cursor(gtk_text_view_get_window(GTK_TEXT_VIEW(m_text
), 
1963                               GTK_TEXT_WINDOW_TEXT
), m_gdkXTermCursor
); 
1967     gdk_window_set_cursor(gtk_text_view_get_window(GTK_TEXT_VIEW(m_text
), 
1968                           GTK_TEXT_WINDOW_TEXT
), m_gdkHandCursor
); 
1971     if(!gtk_text_iter_begins_tag(&start
, tag
)) 
1972         gtk_text_iter_backward_to_tag_toggle(&start
, tag
); 
1973     if(!gtk_text_iter_ends_tag(&end
, tag
)) 
1974         gtk_text_iter_forward_to_tag_toggle(&end
, tag
); 
1976     // Native context menu is probably not desired on an URL. 
1977     // Consider making this dependant on ProcessEvent(wxTextUrlEvent) return value 
1978     if(event
.GetEventType() == wxEVT_RIGHT_DOWN
) 
1981     wxTextUrlEvent 
url_event(m_windowId
, event
, 
1982                              gtk_text_iter_get_offset(&start
), 
1983                              gtk_text_iter_get_offset(&end
)); 
1985     InitCommandEvent(url_event
); 
1986     // Is that a good idea? Seems not (pleasure with gtk_text_view_start_selection_drag) 
1987     //event.Skip(!GetEventHandler()->ProcessEvent(url_event)); 
1988     GetEventHandler()->ProcessEvent(url_event
); 
1991 bool wxTextCtrl::GTKProcessEvent(wxEvent
& event
) const 
1993     bool rc 
= wxTextCtrlBase::GTKProcessEvent(event
); 
1995     // GtkTextView starts a drag operation when left mouse button is pressed 
1996     // and ends it when it is released and if it doesn't get the release event 
1997     // the next click on a control results in an assertion failure inside 
1998     // gtk_text_view_start_selection_drag() which simply *kills* the program 
1999     // without anything we can do about it, so always let GTK+ have this event 
2000     return rc 
&& (IsSingleLine() || event
.GetEventType() != wxEVT_LEFT_UP
); 
2005 wxTextCtrl::GetClassDefaultAttributes(wxWindowVariant 
WXUNUSED(variant
)) 
2007     return GetDefaultAttributesFromGTKWidget(gtk_entry_new
, true);