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" 
  15 #include "wx/textctrl.h" 
  21     #include "wx/settings.h" 
  25 #include "wx/scopeguard.h" 
  26 #include "wx/strconv.h" 
  27 #include "wx/fontutil.h"        // for wxNativeFontInfo (GetNativeFontInfo()) 
  29 #include <sys/types.h> 
  34 #include "wx/gtk/private.h" 
  35 #include "wx/gtk/private/gtk2-compat.h" 
  37 // ---------------------------------------------------------------------------- 
  39 // ---------------------------------------------------------------------------- 
  42 static void wxGtkOnRemoveTag(GtkTextBuffer 
*buffer
, 
  44                              GtkTextIter 
* WXUNUSED(start
), 
  45                              GtkTextIter 
* WXUNUSED(end
), 
  49     g_object_get (tag
, "name", &name
, NULL
); 
  51     if (!name 
|| strncmp(name
, prefix
, strlen(prefix
))) 
  52         // anonymous tag or not starting with prefix - don't remove 
  53         g_signal_stop_emission_by_name (buffer
, "remove_tag"); 
  59 // remove all tags starting with the given prefix from the start..end range 
  61 wxGtkTextRemoveTagsWithPrefix(GtkTextBuffer 
*text_buffer
, 
  66     gulong remove_handler_id 
= g_signal_connect
 
  70                                 G_CALLBACK(wxGtkOnRemoveTag
), 
  73     gtk_text_buffer_remove_all_tags(text_buffer
, start
, end
); 
  74     g_signal_handler_disconnect(text_buffer
, remove_handler_id
); 
  77 static void wxGtkTextApplyTagsFromAttr(GtkWidget 
*text
, 
  78                                        GtkTextBuffer 
*text_buffer
, 
  79                                        const wxTextAttr
& attr
, 
  83     static gchar buf
[1024]; 
  88         wxGtkTextRemoveTagsWithPrefix(text_buffer
, "WXFONT", start
, end
); 
  90         wxFont 
font(attr
.GetFont()); 
  92         PangoFontDescription 
*font_description 
= font
.GetNativeFontInfo()->description
; 
  93         wxGtkString 
font_string(pango_font_description_to_string(font_description
)); 
  94         g_snprintf(buf
, sizeof(buf
), "WXFONT %s", font_string
.c_str()); 
  95         tag 
= gtk_text_tag_table_lookup( gtk_text_buffer_get_tag_table( text_buffer 
), 
  98             tag 
= gtk_text_buffer_create_tag( text_buffer
, buf
, 
  99                                               "font-desc", font_description
, 
 101         gtk_text_buffer_apply_tag (text_buffer
, tag
, start
, end
); 
 103         if (font
.GetUnderlined()) 
 105             g_snprintf(buf
, sizeof(buf
), "WXFONTUNDERLINE"); 
 106             tag 
= gtk_text_tag_table_lookup( gtk_text_buffer_get_tag_table( text_buffer 
), 
 109                 tag 
= gtk_text_buffer_create_tag( text_buffer
, buf
, 
 110                                                   "underline-set", TRUE
, 
 111                                                   "underline", PANGO_UNDERLINE_SINGLE
, 
 113             gtk_text_buffer_apply_tag (text_buffer
, tag
, start
, end
); 
 115         if ( font
.GetStrikethrough() ) 
 117             g_snprintf(buf
, sizeof(buf
), "WXFONTSTRIKETHROUGH"); 
 118             tag 
= gtk_text_tag_table_lookup( gtk_text_buffer_get_tag_table( text_buffer 
), 
 121                 tag 
= gtk_text_buffer_create_tag( text_buffer
, buf
, 
 122                                                   "strikethrough-set", TRUE
, 
 123                                                   "strikethrough", TRUE
, 
 125             gtk_text_buffer_apply_tag (text_buffer
, tag
, start
, end
); 
 129     if (attr
.HasTextColour()) 
 131         wxGtkTextRemoveTagsWithPrefix(text_buffer
, "WXFORECOLOR", start
, end
); 
 133         const GdkColor 
*colFg 
= attr
.GetTextColour().GetColor(); 
 134         g_snprintf(buf
, sizeof(buf
), "WXFORECOLOR %d %d %d", 
 135                    colFg
->red
, colFg
->green
, colFg
->blue
); 
 136         tag 
= gtk_text_tag_table_lookup( gtk_text_buffer_get_tag_table( text_buffer 
), 
 139             tag 
= gtk_text_buffer_create_tag( text_buffer
, buf
, 
 140                                               "foreground-gdk", colFg
, NULL 
); 
 141         gtk_text_buffer_apply_tag (text_buffer
, tag
, start
, end
); 
 144     if (attr
.HasBackgroundColour()) 
 146         wxGtkTextRemoveTagsWithPrefix(text_buffer
, "WXBACKCOLOR", start
, end
); 
 148         const GdkColor 
*colBg 
= attr
.GetBackgroundColour().GetColor(); 
 149         g_snprintf(buf
, sizeof(buf
), "WXBACKCOLOR %d %d %d", 
 150                    colBg
->red
, colBg
->green
, colBg
->blue
); 
 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                                               "background-gdk", colBg
, NULL 
); 
 156         gtk_text_buffer_apply_tag (text_buffer
, tag
, start
, end
); 
 159     if (attr
.HasAlignment()) 
 161         GtkTextIter para_start
, para_end 
= *end
; 
 162         gtk_text_buffer_get_iter_at_line( text_buffer
, 
 164                                           gtk_text_iter_get_line(start
) ); 
 165         gtk_text_iter_forward_line(¶_end
); 
 167         wxGtkTextRemoveTagsWithPrefix(text_buffer
, "WXALIGNMENT", ¶_start
, ¶_end
); 
 169         GtkJustification align
; 
 170         switch (attr
.GetAlignment()) 
 172             case wxTEXT_ALIGNMENT_RIGHT
: 
 173                 align 
= GTK_JUSTIFY_RIGHT
; 
 175             case wxTEXT_ALIGNMENT_CENTER
: 
 176                 align 
= GTK_JUSTIFY_CENTER
; 
 178             case wxTEXT_ALIGNMENT_JUSTIFIED
: 
 180                 align 
= GTK_JUSTIFY_FILL
; 
 182 #elif GTK_CHECK_VERSION(2,11,0) 
 183 // gtk+ doesn't support justify before gtk+-2.11.0 with pango-1.17 being available 
 184 // (but if new enough pango isn't available it's a mere gtk warning) 
 185                 if (!gtk_check_version(2,11,0)) 
 187                     align 
= GTK_JUSTIFY_FILL
; 
 193                 align 
= GTK_JUSTIFY_LEFT
; 
 197         g_snprintf(buf
, sizeof(buf
), "WXALIGNMENT %d", align
); 
 198         tag 
= gtk_text_tag_table_lookup( gtk_text_buffer_get_tag_table( text_buffer 
), 
 201             tag 
= gtk_text_buffer_create_tag( text_buffer
, buf
, 
 202                                               "justification", align
, NULL 
); 
 203         gtk_text_buffer_apply_tag( text_buffer
, tag
, ¶_start
, ¶_end 
); 
 206     if (attr
.HasLeftIndent()) 
 208         // Indentation attribute 
 210         // Clear old indentation tags 
 211         GtkTextIter para_start
, para_end 
= *end
; 
 212         gtk_text_buffer_get_iter_at_line( text_buffer
, 
 214                                           gtk_text_iter_get_line(start
) ); 
 215         gtk_text_iter_forward_line(¶_end
); 
 217         wxGtkTextRemoveTagsWithPrefix(text_buffer
, "WXINDENT", ¶_start
, ¶_end
); 
 219         // Convert indent from 1/10th of a mm into pixels 
 221             (float)gdk_screen_get_width(gtk_widget_get_screen(text
)) / 
 222                       gdk_screen_get_width_mm(gtk_widget_get_screen(text
)) / 10; 
 224         const int indent 
= (int)(factor 
* attr
.GetLeftIndent()); 
 225         const int subIndent 
= (int)(factor 
* attr
.GetLeftSubIndent()); 
 233             gsubindent 
= -subIndent
; 
 237             gindent 
= -subIndent
; 
 241         g_snprintf(buf
, sizeof(buf
), "WXINDENT %d %d", gindent
, gsubindent
); 
 242         tag 
= gtk_text_tag_table_lookup( gtk_text_buffer_get_tag_table( text_buffer 
), 
 245             tag 
= gtk_text_buffer_create_tag( text_buffer
, buf
, 
 246                                               "left-margin", gindent
, "indent", gsubindent
, NULL 
); 
 247         gtk_text_buffer_apply_tag (text_buffer
, tag
, ¶_start
, ¶_end
); 
 255         GtkTextIter para_start
, para_end 
= *end
; 
 256         gtk_text_buffer_get_iter_at_line( text_buffer
, 
 258                                           gtk_text_iter_get_line(start
) ); 
 259         gtk_text_iter_forward_line(¶_end
); 
 261         wxGtkTextRemoveTagsWithPrefix(text_buffer
, "WXTABS", ¶_start
, ¶_end
); 
 263         const wxArrayInt
& tabs 
= attr
.GetTabs(); 
 265         wxString tagname 
= wxT("WXTABS"); 
 266         g_snprintf(buf
, sizeof(buf
), "WXTABS"); 
 267         for (size_t i 
= 0; i 
< tabs
.GetCount(); i
++) 
 268             tagname 
+= wxString::Format(wxT(" %d"), tabs
[i
]); 
 270         const wxWX2MBbuf buftag 
= tagname
.utf8_str(); 
 272         tag 
= gtk_text_tag_table_lookup( gtk_text_buffer_get_tag_table( text_buffer 
), 
 276             // Factor to convert from 1/10th of a mm into pixels 
 278                 (float)gdk_screen_get_width(gtk_widget_get_screen(text
)) / 
 279                           gdk_screen_get_width_mm(gtk_widget_get_screen(text
)) / 10; 
 281             PangoTabArray
* tabArray 
= pango_tab_array_new(tabs
.GetCount(), TRUE
); 
 282             for (size_t i 
= 0; i 
< tabs
.GetCount(); i
++) 
 283                 pango_tab_array_set_tab(tabArray
, i
, PANGO_TAB_LEFT
, (gint
)(tabs
[i
] * factor
)); 
 284             tag 
= gtk_text_buffer_create_tag( text_buffer
, buftag
, 
 285                                               "tabs", tabArray
, NULL 
); 
 286             pango_tab_array_free(tabArray
); 
 288         gtk_text_buffer_apply_tag (text_buffer
, tag
, ¶_start
, ¶_end
); 
 292 static void wxGtkTextInsert(GtkWidget 
*text
, 
 293                             GtkTextBuffer 
*text_buffer
, 
 294                             const wxTextAttr
& attr
, 
 295                             const wxCharBuffer
& buffer
) 
 299     GtkTextIter iter
, start
; 
 301     gtk_text_buffer_get_iter_at_mark( text_buffer
, &iter
, 
 302                                      gtk_text_buffer_get_insert (text_buffer
) ); 
 303     start_offset 
= gtk_text_iter_get_offset (&iter
); 
 304     gtk_text_buffer_insert( text_buffer
, &iter
, buffer
, strlen(buffer
) ); 
 306     gtk_text_buffer_get_iter_at_offset (text_buffer
, &start
, start_offset
); 
 308     wxGtkTextApplyTagsFromAttr(text
, text_buffer
, attr
, &start
, &iter
); 
 311 // Implementation of wxTE_AUTO_URL for wxGTK2 by Mart Raudsepp, 
 315 au_apply_tag_callback(GtkTextBuffer 
*buffer
, 
 317                       GtkTextIter 
* WXUNUSED(start
), 
 318                       GtkTextIter 
* WXUNUSED(end
), 
 319                       gpointer 
WXUNUSED(textctrl
)) 
 321     if(tag 
== gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(buffer
), "wxUrl")) 
 322         g_signal_stop_emission_by_name (buffer
, "apply_tag"); 
 326 //----------------------------------------------------------------------------- 
 327 //  GtkTextCharPredicates for gtk_text_iter_*_find_char 
 328 //----------------------------------------------------------------------------- 
 332 pred_whitespace(gunichar ch
, gpointer 
WXUNUSED(user_data
)) 
 334     return g_unichar_isspace(ch
); 
 340 pred_non_whitespace (gunichar ch
, gpointer 
WXUNUSED(user_data
)) 
 342     return !g_unichar_isspace(ch
); 
 348 pred_nonpunct (gunichar ch
, gpointer 
WXUNUSED(user_data
)) 
 350     return !g_unichar_ispunct(ch
); 
 356 pred_nonpunct_or_slash (gunichar ch
, gpointer 
WXUNUSED(user_data
)) 
 358     return !g_unichar_ispunct(ch
) || ch 
== '/'; 
 362 //----------------------------------------------------------------------------- 
 363 //  Check for links between s and e and correct tags as necessary 
 364 //----------------------------------------------------------------------------- 
 366 // This function should be made match better while being efficient at one point. 
 367 // Most probably with a row of regular expressions. 
 370 au_check_word( GtkTextIter 
*s
, GtkTextIter 
*e 
) 
 372     static const char *const URIPrefixes
[] = 
 390     GtkTextIter start 
= *s
, end 
= *e
; 
 391     GtkTextBuffer 
*buffer 
= gtk_text_iter_get_buffer(s
); 
 393     // Get our special link tag 
 394     GtkTextTag 
*tag 
= gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(buffer
), "wxUrl"); 
 396     // Get rid of punctuation from beginning and end. 
 397     // Might want to move this to au_check_range if an improved link checking doesn't 
 398     // use some intelligent punctuation checking itself (beware of undesired iter modifications). 
 399     if(g_unichar_ispunct( gtk_text_iter_get_char( &start 
) ) ) 
 400         gtk_text_iter_forward_find_char( &start
, pred_nonpunct
, NULL
, e 
); 
 402     gtk_text_iter_backward_find_char( &end
, pred_nonpunct_or_slash
, NULL
, &start 
); 
 403     gtk_text_iter_forward_char(&end
); 
 405     wxGtkString 
text(gtk_text_iter_get_text( &start
, &end 
)); 
 406     size_t len 
= strlen(text
), prefix_len
; 
 409     for( n 
= 0; n 
< WXSIZEOF(URIPrefixes
); ++n 
) 
 411         prefix_len 
= strlen(URIPrefixes
[n
]); 
 412         if((len 
> prefix_len
) && !wxStrnicmp(text
, URIPrefixes
[n
], prefix_len
)) 
 416     if(n 
< WXSIZEOF(URIPrefixes
)) 
 418         gulong signal_id 
= g_signal_handler_find (buffer
, 
 419                                                   (GSignalMatchType
) (G_SIGNAL_MATCH_FUNC
), 
 421                                                   (gpointer
)au_apply_tag_callback
, NULL
); 
 423         g_signal_handler_block (buffer
, signal_id
); 
 424         gtk_text_buffer_apply_tag(buffer
, tag
, &start
, &end
); 
 425         g_signal_handler_unblock (buffer
, signal_id
); 
 432 au_check_range(GtkTextIter 
*s
, 
 433                GtkTextIter 
*range_end
) 
 435     GtkTextIter range_start 
= *s
; 
 436     GtkTextIter word_end
; 
 437     GtkTextBuffer 
*buffer 
= gtk_text_iter_get_buffer(s
); 
 438     GtkTextTag 
*tag 
= gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(buffer
), "wxUrl"); 
 440     gtk_text_buffer_remove_tag(buffer
, tag
, s
, range_end
); 
 442     if(g_unichar_isspace(gtk_text_iter_get_char(&range_start
))) 
 443         gtk_text_iter_forward_find_char(&range_start
, pred_non_whitespace
, NULL
, range_end
); 
 445     while(!gtk_text_iter_equal(&range_start
, range_end
)) 
 447         word_end 
= range_start
; 
 448         gtk_text_iter_forward_find_char(&word_end
, pred_whitespace
, NULL
, range_end
); 
 450         // Now we should have a word delimited by range_start and word_end, correct link tags 
 451         au_check_word(&range_start
, &word_end
); 
 453         range_start 
= word_end
; 
 454         gtk_text_iter_forward_find_char(&range_start
, pred_non_whitespace
, NULL
, range_end
); 
 459 //----------------------------------------------------------------------------- 
 460 //  "insert-text" for GtkTextBuffer 
 461 //----------------------------------------------------------------------------- 
 465 au_insert_text_callback(GtkTextBuffer 
* WXUNUSED(buffer
), 
 471     if (!len 
|| !(win
->GetWindowStyleFlag() & wxTE_AUTO_URL
) ) 
 474     GtkTextIter start 
= *end
; 
 475     gtk_text_iter_backward_chars(&start
, g_utf8_strlen(text
, len
)); 
 477     GtkTextIter line_start 
= start
; 
 478     GtkTextIter line_end 
= *end
; 
 479     GtkTextIter words_start 
= start
; 
 480     GtkTextIter words_end 
= *end
; 
 482     gtk_text_iter_set_line(&line_start
, gtk_text_iter_get_line(&start
)); 
 483     gtk_text_iter_forward_to_line_end(&line_end
); 
 484     gtk_text_iter_backward_find_char(&words_start
, pred_whitespace
, NULL
, &line_start
); 
 485     gtk_text_iter_forward_find_char(&words_end
, pred_whitespace
, NULL
, &line_end
); 
 487     au_check_range(&words_start
, &words_end
); 
 491 //----------------------------------------------------------------------------- 
 492 //  "delete-range" for GtkTextBuffer 
 493 //----------------------------------------------------------------------------- 
 497 au_delete_range_callback(GtkTextBuffer 
* WXUNUSED(buffer
), 
 502     if( !(win
->GetWindowStyleFlag() & wxTE_AUTO_URL
) ) 
 505     GtkTextIter line_start 
= *start
, line_end 
= *end
; 
 507     gtk_text_iter_set_line(&line_start
, gtk_text_iter_get_line(start
)); 
 508     gtk_text_iter_forward_to_line_end(&line_end
); 
 509     gtk_text_iter_backward_find_char(start
, pred_whitespace
, NULL
, &line_start
); 
 510     gtk_text_iter_forward_find_char(end
, pred_whitespace
, NULL
, &line_end
); 
 512     au_check_range(start
, end
); 
 516 //----------------------------------------------------------------------------- 
 517 //  "populate_popup" from text control and "unmap" from its poup menu 
 518 //----------------------------------------------------------------------------- 
 522 gtk_textctrl_popup_unmap( GtkMenu 
*WXUNUSED(menu
), wxTextCtrl
* win 
) 
 524     win
->GTKEnableFocusOutEvent(); 
 530 gtk_textctrl_populate_popup( GtkEntry 
*WXUNUSED(entry
), GtkMenu 
*menu
, wxTextCtrl 
*win 
) 
 532     win
->GTKDisableFocusOutEvent(); 
 534     g_signal_connect (menu
, "unmap", G_CALLBACK (gtk_textctrl_popup_unmap
), win 
); 
 538 //----------------------------------------------------------------------------- 
 540 //----------------------------------------------------------------------------- 
 544 gtk_text_changed_callback( GtkWidget 
*WXUNUSED(widget
), wxTextCtrl 
*win 
) 
 546     if ( win
->IgnoreTextUpdate() ) 
 549     if ( win
->MarkDirtyOnChange() ) 
 552     win
->SendTextUpdatedEvent(); 
 556 //----------------------------------------------------------------------------- 
 558 //----------------------------------------------------------------------------- 
 561 static void mark_set(GtkTextBuffer
*, GtkTextIter
*, GtkTextMark
* mark
, GSList
** markList
) 
 563     if (gtk_text_mark_get_name(mark
) == NULL
) 
 564         *markList 
= g_slist_prepend(*markList
, mark
); 
 568 //----------------------------------------------------------------------------- 
 570 //----------------------------------------------------------------------------- 
 572 BEGIN_EVENT_TABLE(wxTextCtrl
, wxTextCtrlBase
) 
 573     EVT_CHAR(wxTextCtrl::OnChar
) 
 575     EVT_MENU(wxID_CUT
, wxTextCtrl::OnCut
) 
 576     EVT_MENU(wxID_COPY
, wxTextCtrl::OnCopy
) 
 577     EVT_MENU(wxID_PASTE
, wxTextCtrl::OnPaste
) 
 578     EVT_MENU(wxID_UNDO
, wxTextCtrl::OnUndo
) 
 579     EVT_MENU(wxID_REDO
, wxTextCtrl::OnRedo
) 
 581     EVT_UPDATE_UI(wxID_CUT
, wxTextCtrl::OnUpdateCut
) 
 582     EVT_UPDATE_UI(wxID_COPY
, wxTextCtrl::OnUpdateCopy
) 
 583     EVT_UPDATE_UI(wxID_PASTE
, wxTextCtrl::OnUpdatePaste
) 
 584     EVT_UPDATE_UI(wxID_UNDO
, wxTextCtrl::OnUpdateUndo
) 
 585     EVT_UPDATE_UI(wxID_REDO
, wxTextCtrl::OnUpdateRedo
) 
 587     // wxTE_AUTO_URL wxTextUrl support. Currently only creates 
 588     // wxTextUrlEvent in the same cases as wxMSW, more can be added here. 
 589     EVT_MOTION      (wxTextCtrl::OnUrlMouseEvent
) 
 590     EVT_LEFT_DOWN   (wxTextCtrl::OnUrlMouseEvent
) 
 591     EVT_LEFT_UP     (wxTextCtrl::OnUrlMouseEvent
) 
 592     EVT_LEFT_DCLICK (wxTextCtrl::OnUrlMouseEvent
) 
 593     EVT_RIGHT_DOWN  (wxTextCtrl::OnUrlMouseEvent
) 
 594     EVT_RIGHT_UP    (wxTextCtrl::OnUrlMouseEvent
) 
 595     EVT_RIGHT_DCLICK(wxTextCtrl::OnUrlMouseEvent
) 
 598 void wxTextCtrl::Init() 
 603     m_countUpdatesToIgnore 
= 0; 
 605     SetUpdateFont(false); 
 609     m_showPositionOnThaw 
= NULL
; 
 610     m_anonymousMarkList 
= NULL
; 
 613 wxTextCtrl::~wxTextCtrl() 
 616         GTKDisconnect(m_text
); 
 618         GTKDisconnect(m_buffer
); 
 620     // this is also done by wxWindowGTK dtor, but has to be done here so our 
 621     // DoThaw() override is called 
 625     if (m_anonymousMarkList
) 
 626         g_slist_free(m_anonymousMarkList
); 
 629 wxTextCtrl::wxTextCtrl( wxWindow 
*parent
, 
 631                         const wxString 
&value
, 
 635                         const wxValidator
& validator
, 
 636                         const wxString 
&name 
) 
 640     Create( parent
, id
, value
, pos
, size
, style
, validator
, name 
); 
 643 bool wxTextCtrl::Create( wxWindow 
*parent
, 
 645                          const wxString 
&value
, 
 649                          const wxValidator
& validator
, 
 650                          const wxString 
&name 
) 
 652     if (!PreCreation( parent
, pos
, size 
) || 
 653         !CreateBase( parent
, id
, pos
, size
, style
, validator
, name 
)) 
 655         wxFAIL_MSG( wxT("wxTextCtrl creation failed") ); 
 659     bool multi_line 
= (style 
& wxTE_MULTILINE
) != 0; 
 663         m_buffer 
= gtk_text_buffer_new(NULL
); 
 664         gulong sig_id 
= g_signal_connect(m_buffer
, "mark_set", G_CALLBACK(mark_set
), &m_anonymousMarkList
); 
 666         m_text 
= gtk_text_view_new_with_buffer(m_buffer
); 
 667         // gtk_text_view_set_buffer adds its own reference 
 668         g_object_unref(m_buffer
); 
 669         g_signal_handler_disconnect(m_buffer
, sig_id
); 
 671         // create "ShowPosition" marker 
 673         gtk_text_buffer_get_start_iter(m_buffer
, &iter
); 
 674         gtk_text_buffer_create_mark(m_buffer
, "ShowPosition", &iter
, true); 
 676         // create scrolled window 
 677         m_widget 
= gtk_scrolled_window_new( NULL
, NULL 
); 
 678         gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( m_widget 
), 
 679                                         GTK_POLICY_AUTOMATIC
, 
 680                                         style 
& wxTE_NO_VSCROLL
 
 682                                             : GTK_POLICY_AUTOMATIC 
); 
 683         // for ScrollLines/Pages 
 684         m_scrollBar
[1] = GTK_RANGE(gtk_scrolled_window_get_vscrollbar(GTK_SCROLLED_WINDOW(m_widget
))); 
 686         // Insert view into scrolled window 
 687         gtk_container_add( GTK_CONTAINER(m_widget
), m_text 
); 
 691         GTKScrolledWindowSetBorder(m_widget
, style
); 
 693         gtk_widget_add_events( GTK_WIDGET(m_text
), GDK_ENTER_NOTIFY_MASK 
| GDK_LEAVE_NOTIFY_MASK 
); 
 695         gtk_widget_set_can_focus(m_widget
, FALSE
); 
 699         // a single-line text control: no need for scrollbars 
 701         m_text 
= gtk_entry_new(); 
 702         // work around probable bug in GTK+ 2.18 when calling WriteText on a 
 703         // new, empty control, see http://trac.wxwidgets.org/ticket/11409 
 704         gtk_entry_get_text((GtkEntry
*)m_text
); 
 706         if (style 
& wxNO_BORDER
) 
 707             g_object_set (m_text
, "has-frame", FALSE
, NULL
); 
 710     g_object_ref(m_widget
); 
 712     m_parent
->DoAddChild( this ); 
 714     m_focusWidget 
= m_text
; 
 720         gtk_widget_show(m_text
); 
 723     // We want to be notified about text changes. 
 726         g_signal_connect (m_buffer
, "changed", 
 727                           G_CALLBACK (gtk_text_changed_callback
), this); 
 731         g_signal_connect (m_text
, "changed", 
 732                           G_CALLBACK (gtk_text_changed_callback
), this); 
 735     // Catch to disable focus out handling 
 736     g_signal_connect (m_text
, "populate_popup", 
 737                       G_CALLBACK (gtk_textctrl_populate_popup
), 
 745     if (style 
& wxTE_PASSWORD
) 
 748     if (style 
& wxTE_READONLY
) 
 751     // left justification (alignment) is the default anyhow 
 752     if ( style 
& (wxTE_RIGHT 
| wxTE_CENTRE
) ) 
 753         GTKSetJustification(); 
 757         // Handle URLs on multi-line controls with wxTE_AUTO_URL style 
 758         if (style 
& wxTE_AUTO_URL
) 
 760             GtkTextIter start
, end
; 
 762             // We create our wxUrl tag here for slight efficiency gain - we 
 763             // don't have to check for the tag existence in callbacks, 
 764             // hereby it's guaranteed to exist. 
 765             gtk_text_buffer_create_tag(m_buffer
, "wxUrl", 
 766                                        "foreground", "blue", 
 767                                        "underline", PANGO_UNDERLINE_SINGLE
, 
 770             // Check for URLs after each text change 
 771             g_signal_connect_after (m_buffer
, "insert_text", 
 772                                     G_CALLBACK (au_insert_text_callback
), this); 
 773             g_signal_connect_after (m_buffer
, "delete_range", 
 774                                     G_CALLBACK (au_delete_range_callback
), this); 
 776             // Block all wxUrl tag applying unless we do it ourselves, in which case we 
 777             // block this callback temporarily. This takes care of gtk+ internal 
 778             // gtk_text_buffer_insert_range* calls that would copy our URL tag otherwise, 
 779             // which is undesired because only a part of the URL might be copied. 
 780             // The insert-text signal emitted inside it will take care of newly formed 
 781             // or wholly copied URLs. 
 782             g_signal_connect (m_buffer
, "apply_tag", 
 783                               G_CALLBACK (au_apply_tag_callback
), NULL
); 
 785             // Check for URLs in the initial string passed to Create 
 786             gtk_text_buffer_get_start_iter(m_buffer
, &start
); 
 787             gtk_text_buffer_get_end_iter(m_buffer
, &end
); 
 788             au_check_range(&start
, &end
); 
 793         // do the right thing with Enter presses depending on whether we have 
 794         // wxTE_PROCESS_ENTER or not 
 795         GTKSetActivatesDefault(); 
 799     GTKConnectClipboardSignals(m_text
); 
 801     m_cursor 
= wxCursor( wxCURSOR_IBEAM 
); 
 806 GtkEditable 
*wxTextCtrl::GetEditable() const 
 808     wxCHECK_MSG( IsSingleLine(), NULL
, "shouldn't be called for multiline" ); 
 810     return GTK_EDITABLE(m_text
); 
 813 GtkEntry 
*wxTextCtrl::GetEntry() const 
 815     return GTK_ENTRY(m_text
); 
 818 // ---------------------------------------------------------------------------- 
 820 // ---------------------------------------------------------------------------- 
 822 void wxTextCtrl::GTKSetEditable() 
 824     gboolean editable 
= !HasFlag(wxTE_READONLY
); 
 825     if ( IsSingleLine() ) 
 826         gtk_editable_set_editable(GTK_EDITABLE(m_text
), editable
); 
 828         gtk_text_view_set_editable(GTK_TEXT_VIEW(m_text
), editable
); 
 831 void wxTextCtrl::GTKSetVisibility() 
 833     wxCHECK_RET( IsSingleLine(), 
 834                  "wxTE_PASSWORD is for single line text controls only" ); 
 836     gtk_entry_set_visibility(GTK_ENTRY(m_text
), !HasFlag(wxTE_PASSWORD
)); 
 839 void wxTextCtrl::GTKSetActivatesDefault() 
 841     wxCHECK_RET( IsSingleLine(), 
 842                  "wxTE_PROCESS_ENTER is for single line text controls only" ); 
 844     gtk_entry_set_activates_default(GTK_ENTRY(m_text
), 
 845                                     !HasFlag(wxTE_PROCESS_ENTER
)); 
 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 
 863         wrap 
= GTK_WRAP_WORD_CHAR
; 
 865     gtk_text_view_set_wrap_mode( GTK_TEXT_VIEW( m_text 
), wrap 
); 
 868 void wxTextCtrl::GTKSetJustification() 
 872         GtkJustification just
; 
 873         if ( HasFlag(wxTE_RIGHT
) ) 
 874             just 
= GTK_JUSTIFY_RIGHT
; 
 875         else if ( HasFlag(wxTE_CENTRE
) ) 
 876             just 
= GTK_JUSTIFY_CENTER
; 
 877         else // wxTE_LEFT == 0 
 878             just 
= GTK_JUSTIFY_LEFT
; 
 880         gtk_text_view_set_justification(GTK_TEXT_VIEW(m_text
), just
); 
 885         if ( HasFlag(wxTE_RIGHT
) ) 
 887         else if ( HasFlag(wxTE_CENTRE
) ) 
 892         gtk_entry_set_alignment(GTK_ENTRY(m_text
), align
); 
 896 void wxTextCtrl::SetWindowStyleFlag(long style
) 
 898     long styleOld 
= GetWindowStyleFlag(); 
 900     wxTextCtrlBase::SetWindowStyleFlag(style
); 
 902     if ( (style 
& wxTE_READONLY
) != (styleOld 
& wxTE_READONLY
) ) 
 905     if ( (style 
& wxTE_PASSWORD
) != (styleOld 
& wxTE_PASSWORD
) ) 
 908     if ( (style 
& wxTE_PROCESS_ENTER
) != (styleOld 
& wxTE_PROCESS_ENTER
) ) 
 909         GTKSetActivatesDefault(); 
 911     static const long flagsWrap 
= wxTE_WORDWRAP 
| wxTE_CHARWRAP 
| wxTE_DONTWRAP
; 
 912     if ( (style 
& flagsWrap
) != (styleOld 
& flagsWrap
) ) 
 915     static const long flagsAlign 
= wxTE_LEFT 
| wxTE_CENTRE 
| wxTE_RIGHT
; 
 916     if ( (style 
& flagsAlign
) != (styleOld 
& flagsAlign
) ) 
 917         GTKSetJustification(); 
 920 // ---------------------------------------------------------------------------- 
 922 // ---------------------------------------------------------------------------- 
 924 wxString 
wxTextCtrl::GetValue() const 
 926     wxCHECK_MSG( m_text 
!= NULL
, wxEmptyString
, wxT("invalid text ctrl") ); 
 931         gtk_text_buffer_get_start_iter( m_buffer
, &start 
); 
 933         gtk_text_buffer_get_end_iter( m_buffer
, &end 
); 
 934         wxGtkString 
text(gtk_text_buffer_get_text(m_buffer
, &start
, &end
, true)); 
 936         return wxGTK_CONV_BACK(text
); 
 940         return wxTextEntry::GetValue(); 
 944 wxFontEncoding 
wxTextCtrl::GetTextEncoding() const 
 946     // GTK+ uses UTF-8 internally, we need to convert to it but from which 
 949     // first check the default text style (we intentionally don't check the 
 950     // style for the current position as it doesn't make sense for SetValue()) 
 951     const wxTextAttr
& style 
= GetDefaultStyle(); 
 952     wxFontEncoding enc 
= style
.HasFontEncoding() ? style
.GetFontEncoding() 
 953                                          : wxFONTENCODING_SYSTEM
; 
 955     // fall back to the controls font if no style 
 956     if ( enc 
== wxFONTENCODING_SYSTEM 
&& m_hasFont 
) 
 957         enc 
= GetFont().GetEncoding(); 
 962 bool wxTextCtrl::IsEmpty() const 
 965         return gtk_text_buffer_get_char_count(m_buffer
) == 0; 
 967     return wxTextEntry::IsEmpty(); 
 970 void wxTextCtrl::DoSetValue( const wxString 
&value
, int flags 
) 
 972     wxCHECK_RET( m_text 
!= NULL
, wxT("invalid text ctrl") ); 
 976     if ( !IsMultiLine() ) 
 978         wxTextEntry::DoSetValue(value
, flags
); 
 984         if ( !(flags 
& SetValue_SendEvent
) ) 
 985             EnableTextChangedEvents(false); 
 987         gtk_text_buffer_set_text( m_buffer
, "", 0 ); 
 989         if ( !(flags 
& SetValue_SendEvent
) ) 
 990             EnableTextChangedEvents(true); 
 996     const wxCharBuffer 
buffer(value
.utf8_str()); 
 998     wxFontEncoding enc 
= m_defaultStyle
.HasFont() 
 999                             ? m_defaultStyle
.GetFont().GetEncoding() 
1000                             : wxFONTENCODING_SYSTEM
; 
1001     if ( enc 
== wxFONTENCODING_SYSTEM 
) 
1002         enc 
= GetTextEncoding(); 
1004     const wxCharBuffer 
buffer(wxGTK_CONV_ENC(value
, enc
)); 
1007         // see comment in WriteText() as to why we must warn the user about 
1009         wxLogWarning(_("Failed to set text in the text control.")); 
1014     if ( !(flags 
& SetValue_SendEvent
) ) 
1016         EnableTextChangedEvents(false); 
1019     gtk_text_buffer_set_text( m_buffer
, buffer
, strlen(buffer
) ); 
1021     if ( !m_defaultStyle
.IsDefault() ) 
1023         GtkTextIter start
, end
; 
1024         gtk_text_buffer_get_bounds( m_buffer
, &start
, &end 
); 
1025         wxGtkTextApplyTagsFromAttr(m_widget
, m_buffer
, m_defaultStyle
, 
1029     if ( !(flags 
& SetValue_SendEvent
) ) 
1031         EnableTextChangedEvents(true); 
1035 void wxTextCtrl::WriteText( const wxString 
&text 
) 
1037     wxCHECK_RET( m_text 
!= NULL
, wxT("invalid text ctrl") ); 
1039     // we're changing the text programmatically 
1040     DontMarkDirtyOnNextChange(); 
1042     if ( !IsMultiLine() ) 
1044         wxTextEntry::WriteText(text
); 
1049     const wxCharBuffer 
buffer(text
.utf8_str()); 
1051     // check if we have a specific style for the current position 
1052     wxFontEncoding enc 
= wxFONTENCODING_SYSTEM
; 
1054     if ( GetStyle(GetInsertionPoint(), style
) && style
.HasFontEncoding() ) 
1056         enc 
= style
.GetFontEncoding(); 
1059     if ( enc 
== wxFONTENCODING_SYSTEM 
) 
1060         enc 
= GetTextEncoding(); 
1062     const wxCharBuffer 
buffer(wxGTK_CONV_ENC(text
, enc
)); 
1065         // we must log an error here as losing the text like this can be a 
1066         // serious problem (e.g. imagine the document edited by user being 
1067         // empty instead of containing the correct text) 
1068         wxLogWarning(_("Failed to insert text in the control.")); 
1073     // First remove the selection if there is one 
1074     // TODO:  Is there an easier GTK specific way to do this? 
1076     GetSelection(&from
, &to
); 
1081     wxGtkTextInsert( m_text
, m_buffer
, m_defaultStyle
, buffer 
); 
1083     // Scroll to cursor, but only if scrollbar thumb is at the very bottom 
1084     // won't work when frozen, text view is not using m_buffer then 
1087         GtkAdjustment
* adj 
= gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(m_widget
)); 
1088         const double value 
= gtk_adjustment_get_value(adj
); 
1089         const double upper 
= gtk_adjustment_get_upper(adj
); 
1090         const double page_size 
= gtk_adjustment_get_page_size(adj
); 
1091         if (wxIsSameDouble(value
, upper 
- page_size
)) 
1093             gtk_text_view_scroll_to_mark(GTK_TEXT_VIEW(m_text
), 
1094                 gtk_text_buffer_get_insert(m_buffer
), 0, false, 0, 1); 
1099 wxString 
wxTextCtrl::GetLineText( long lineNo 
) const 
1102     if ( IsMultiLine() ) 
1105         gtk_text_buffer_get_iter_at_line(m_buffer
,&line
,lineNo
); 
1107         GtkTextIter end 
= line
; 
1108         // avoid skipping to the next line end if this one is empty 
1109         if ( !gtk_text_iter_ends_line(&line
) ) 
1110             gtk_text_iter_forward_to_line_end(&end
); 
1112         wxGtkString 
text(gtk_text_buffer_get_text(m_buffer
, &line
, &end
, true)); 
1113         result 
= wxGTK_CONV_BACK(text
); 
1118             result 
= GetValue(); 
1123 void wxTextCtrl::OnDropFiles( wxDropFilesEvent 
&WXUNUSED(event
) ) 
1125   /* If you implement this, don't forget to update the documentation! 
1126    * (file docs/latex/wx/text.tex) */ 
1127     wxFAIL_MSG( wxT("wxTextCtrl::OnDropFiles not implemented") ); 
1130 bool wxTextCtrl::PositionToXY(long pos
, long *x
, long *y 
) const 
1132     if ( IsMultiLine() ) 
1136         if (pos 
> GetLastPosition()) 
1139         gtk_text_buffer_get_iter_at_offset(m_buffer
, &iter
, pos
); 
1142             *y 
= gtk_text_iter_get_line(&iter
); 
1144             *x 
= gtk_text_iter_get_line_offset(&iter
); 
1146     else // single line control 
1148         if (pos 
<= gtk_entry_get_text_length(GTK_ENTRY(m_text
))) 
1157             // index out of bounds 
1165 long wxTextCtrl::XYToPosition(long x
, long y 
) const 
1167     if ( IsSingleLine() ) 
1171     if (y 
>= gtk_text_buffer_get_line_count (m_buffer
)) 
1174     gtk_text_buffer_get_iter_at_line(m_buffer
, &iter
, y
); 
1175     if (x 
>= gtk_text_iter_get_chars_in_line (&iter
)) 
1178     return gtk_text_iter_get_offset(&iter
) + x
; 
1181 int wxTextCtrl::GetLineLength(long lineNo
) const 
1183     if ( IsMultiLine() ) 
1185         int last_line 
= gtk_text_buffer_get_line_count( m_buffer 
) - 1; 
1186         if (lineNo 
> last_line
) 
1190         gtk_text_buffer_get_iter_at_line(m_buffer
, &iter
, lineNo
); 
1191         // get_chars_in_line return includes paragraph delimiters, so need to subtract 1 IF it is not the last line 
1192         return gtk_text_iter_get_chars_in_line(&iter
) - ((lineNo 
== last_line
) ? 0 : 1); 
1196         wxString str 
= GetLineText (lineNo
); 
1197         return (int) str
.length(); 
1201 wxPoint 
wxTextCtrl::DoPositionToCoords(long pos
) const 
1203     if ( !IsMultiLine() ) 
1205         // Single line text entry (GtkTextEntry) doesn't have support for 
1206         // getting the coordinates for the given offset. Perhaps we could 
1207         // find them ourselves by using GetTextExtent() but for now just leave 
1208         // it unimplemented, this function is more useful for multiline 
1210         return wxDefaultPosition
; 
1213     // Window coordinates for the given position is calculated by getting 
1214     // the buffer coordinates and converting them to window coordinates. 
1215     GtkTextView 
*textview 
= GTK_TEXT_VIEW(m_text
); 
1218     gtk_text_buffer_get_iter_at_offset(m_buffer
, &iter
, pos
); 
1220     GdkRectangle bufferCoords
; 
1221     gtk_text_view_get_iter_location(textview
, &iter
, &bufferCoords
); 
1225     gtk_text_view_buffer_to_window_coords(textview
, GTK_TEXT_WINDOW_WIDGET
, 
1226                                           bufferCoords
.x
, bufferCoords
.y
, 
1227                                           &winCoordX
, &winCoordY
); 
1229     return wxPoint(winCoordX
, winCoordY
); 
1232 int wxTextCtrl::GetNumberOfLines() const 
1234     if ( IsMultiLine() ) 
1236         return gtk_text_buffer_get_line_count( m_buffer 
); 
1244 void wxTextCtrl::SetInsertionPoint( long pos 
) 
1246     wxCHECK_RET( m_text 
!= NULL
, wxT("invalid text ctrl") ); 
1248     if ( IsMultiLine() ) 
1251         gtk_text_buffer_get_iter_at_offset( m_buffer
, &iter
, pos 
); 
1252         gtk_text_buffer_place_cursor( m_buffer
, &iter 
); 
1253         GtkTextMark
* mark 
= gtk_text_buffer_get_insert(m_buffer
); 
1255             // defer until Thaw, text view is not using m_buffer now 
1256             m_showPositionOnThaw 
= mark
; 
1258             gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(m_text
), mark
); 
1262         wxTextEntry::SetInsertionPoint(pos
); 
1266 void wxTextCtrl::SetEditable( bool editable 
) 
1268     wxCHECK_RET( m_text 
!= NULL
, wxT("invalid text ctrl") ); 
1270     if ( IsMultiLine() ) 
1272         gtk_text_view_set_editable( GTK_TEXT_VIEW(m_text
), editable 
); 
1276         wxTextEntry::SetEditable(editable
); 
1280 bool wxTextCtrl::Enable( bool enable 
) 
1282     if (!wxWindowBase::Enable(enable
)) 
1288     gtk_widget_set_sensitive( m_text
, enable 
); 
1289     SetCursor(enable 
? wxCursor(wxCURSOR_IBEAM
) : wxCursor()); 
1294 // wxGTK-specific: called recursively by Enable, 
1295 // to give widgets an opportunity to correct their colours after they 
1296 // have been changed by Enable 
1297 void wxTextCtrl::OnEnabled(bool WXUNUSED(enable
)) 
1299     // If we have a custom background colour, we use this colour in both 
1300     // disabled and enabled mode, or we end up with a different colour under the 
1302     wxColour oldColour 
= GetBackgroundColour(); 
1303     if (oldColour
.IsOk()) 
1305         // Need to set twice or it'll optimize the useful stuff out 
1306         if (oldColour 
== * wxWHITE
) 
1307             SetBackgroundColour(*wxBLACK
); 
1309             SetBackgroundColour(*wxWHITE
); 
1310         SetBackgroundColour(oldColour
); 
1314 void wxTextCtrl::MarkDirty() 
1319 void wxTextCtrl::DiscardEdits() 
1324 // ---------------------------------------------------------------------------- 
1326 // ---------------------------------------------------------------------------- 
1328 void wxTextCtrl::EnableTextChangedEvents(bool enable
) 
1332         g_signal_handlers_unblock_by_func(GetTextObject(), 
1333             (gpointer
)gtk_text_changed_callback
, this); 
1335     else // disable events 
1337         g_signal_handlers_block_by_func(GetTextObject(), 
1338             (gpointer
)gtk_text_changed_callback
, this); 
1342 bool wxTextCtrl::IgnoreTextUpdate() 
1344     if ( m_countUpdatesToIgnore 
> 0 ) 
1346         m_countUpdatesToIgnore
--; 
1354 bool wxTextCtrl::MarkDirtyOnChange() 
1356     if ( m_dontMarkDirty 
) 
1358         m_dontMarkDirty 
= false; 
1366 void wxTextCtrl::SetSelection( long from
, long to 
) 
1368     wxCHECK_RET( m_text 
!= NULL
, wxT("invalid text ctrl") ); 
1370     if ( IsMultiLine() ) 
1372         if (from 
== -1 && to 
== -1) 
1375             to 
= GetValue().length(); 
1378         GtkTextIter fromi
, toi
; 
1379         gtk_text_buffer_get_iter_at_offset( m_buffer
, &fromi
, from 
); 
1380         gtk_text_buffer_get_iter_at_offset( m_buffer
, &toi
, to 
); 
1382         gtk_text_buffer_select_range( m_buffer
, &fromi
, &toi 
); 
1386         wxTextEntry::SetSelection(from
, to
); 
1390 void wxTextCtrl::ShowPosition( long pos 
) 
1395         gtk_text_buffer_get_iter_at_offset(m_buffer
, &iter
, int(pos
)); 
1396         GtkTextMark
* mark 
= gtk_text_buffer_get_mark(m_buffer
, "ShowPosition"); 
1397         gtk_text_buffer_move_mark(m_buffer
, mark
, &iter
); 
1399             // defer until Thaw, text view is not using m_buffer now 
1400             m_showPositionOnThaw 
= mark
; 
1402             gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(m_text
), mark
); 
1406 wxTextCtrlHitTestResult
 
1407 wxTextCtrl::HitTest(const wxPoint
& pt
, long *pos
) const 
1409     if ( !IsMultiLine() ) 
1412         return wxTE_HT_UNKNOWN
; 
1416     gtk_text_view_window_to_buffer_coords
 
1418         GTK_TEXT_VIEW(m_text
), 
1419         GTK_TEXT_WINDOW_TEXT
, 
1425     gtk_text_view_get_iter_at_location(GTK_TEXT_VIEW(m_text
), &iter
, x
, y
); 
1427         *pos 
= gtk_text_iter_get_offset(&iter
); 
1429     return wxTE_HT_ON_TEXT
; 
1432 long wxTextCtrl::GetInsertionPoint() const 
1434     wxCHECK_MSG( m_text 
!= NULL
, 0, wxT("invalid text ctrl") ); 
1436     if ( IsMultiLine() ) 
1438         // There is no direct accessor for the cursor, but 
1439         // internally, the cursor is the "mark" called 
1440         // "insert" in the text view's btree structure. 
1442         GtkTextMark 
*mark 
= gtk_text_buffer_get_insert( m_buffer 
); 
1444         gtk_text_buffer_get_iter_at_mark( m_buffer
, &cursor
, mark 
); 
1446         return gtk_text_iter_get_offset( &cursor 
); 
1450         return wxTextEntry::GetInsertionPoint(); 
1454 wxTextPos 
wxTextCtrl::GetLastPosition() const 
1456     wxCHECK_MSG( m_text 
!= NULL
, 0, wxT("invalid text ctrl") ); 
1460     if ( IsMultiLine() ) 
1463         gtk_text_buffer_get_end_iter( m_buffer
, &end 
); 
1465         pos 
= gtk_text_iter_get_offset( &end 
); 
1469         pos 
= wxTextEntry::GetLastPosition(); 
1475 void wxTextCtrl::Remove( long from
, long to 
) 
1477     wxCHECK_RET( m_text 
!= NULL
, wxT("invalid text ctrl") ); 
1479     if ( IsMultiLine() ) 
1481         GtkTextIter fromi
, toi
; 
1482         gtk_text_buffer_get_iter_at_offset( m_buffer
, &fromi
, from 
); 
1483         gtk_text_buffer_get_iter_at_offset( m_buffer
, &toi
, to 
); 
1485         gtk_text_buffer_delete( m_buffer
, &fromi
, &toi 
); 
1489         wxTextEntry::Remove(from
, to
); 
1493 void wxTextCtrl::Cut() 
1495     wxCHECK_RET( m_text 
!= NULL
, wxT("invalid text ctrl") ); 
1497     if ( IsMultiLine() ) 
1498         g_signal_emit_by_name (m_text
, "cut-clipboard"); 
1503 void wxTextCtrl::Copy() 
1505     wxCHECK_RET( m_text 
!= NULL
, wxT("invalid text ctrl") ); 
1507     if ( IsMultiLine() ) 
1508         g_signal_emit_by_name (m_text
, "copy-clipboard"); 
1510         wxTextEntry::Copy(); 
1513 void wxTextCtrl::Paste() 
1515     wxCHECK_RET( m_text 
!= NULL
, wxT("invalid text ctrl") ); 
1517     if ( IsMultiLine() ) 
1518         g_signal_emit_by_name (m_text
, "paste-clipboard"); 
1520         wxTextEntry::Paste(); 
1523 // If the return values from and to are the same, there is no 
1525 void wxTextCtrl::GetSelection(long* fromOut
, long* toOut
) const 
1527     wxCHECK_RET( m_text 
!= NULL
, wxT("invalid text ctrl") ); 
1529     if ( !IsMultiLine() ) 
1531         wxTextEntry::GetSelection(fromOut
, toOut
); 
1537     GtkTextIter ifrom
, ito
; 
1538     if ( gtk_text_buffer_get_selection_bounds(m_buffer
, &ifrom
, &ito
) ) 
1540         from 
= gtk_text_iter_get_offset(&ifrom
); 
1541         to 
= gtk_text_iter_get_offset(&ito
); 
1545             // exchange them to be compatible with wxMSW 
1551     else // no selection 
1554         to 
= GetInsertionPoint(); 
1564 bool wxTextCtrl::IsEditable() const 
1566     wxCHECK_MSG( m_text 
!= NULL
, false, wxT("invalid text ctrl") ); 
1568     if ( IsMultiLine() ) 
1570         return gtk_text_view_get_editable(GTK_TEXT_VIEW(m_text
)) != 0; 
1574         return wxTextEntry::IsEditable(); 
1578 bool wxTextCtrl::IsModified() const 
1583 void wxTextCtrl::OnChar( wxKeyEvent 
&key_event 
) 
1585     wxCHECK_RET( m_text 
!= NULL
, wxT("invalid text ctrl") ); 
1587     if ( key_event
.GetKeyCode() == WXK_RETURN 
) 
1589         if ( HasFlag(wxTE_PROCESS_ENTER
) ) 
1591             wxCommandEvent 
event(wxEVT_COMMAND_TEXT_ENTER
, m_windowId
); 
1592             event
.SetEventObject(this); 
1593             event
.SetString(GetValue()); 
1594             if ( HandleWindowEvent(event
) ) 
1602 GtkWidget
* wxTextCtrl::GetConnectWidget() 
1604     return GTK_WIDGET(m_text
); 
1607 GdkWindow 
*wxTextCtrl::GTKGetWindow(wxArrayGdkWindows
& WXUNUSED(windows
)) const 
1609     if ( IsMultiLine() ) 
1611         return gtk_text_view_get_window(GTK_TEXT_VIEW(m_text
), 
1612                                         GTK_TEXT_WINDOW_TEXT 
); 
1617         // no access to internal GdkWindows 
1620         return gtk_entry_get_text_window(GTK_ENTRY(m_text
)); 
1625 // the font will change for subsequent text insertiongs 
1626 bool wxTextCtrl::SetFont( const wxFont 
&font 
) 
1628     wxCHECK_MSG( m_text 
!= NULL
, false, wxT("invalid text ctrl") ); 
1630     if ( !wxTextCtrlBase::SetFont(font
) ) 
1632         // font didn't change, nothing to do 
1636     if ( IsMultiLine() ) 
1638         SetUpdateFont(true); 
1640         m_defaultStyle
.SetFont(font
); 
1642         ChangeFontGlobally(); 
1648 void wxTextCtrl::ChangeFontGlobally() 
1650     // this method is very inefficient and hence should be called as rarely as 
1653     // TODO: it can be implemented much more efficiently for GTK2 
1654     wxASSERT_MSG( IsMultiLine(), 
1655                   wxT("shouldn't be called for single line controls") ); 
1657     wxString value 
= GetValue(); 
1658     if ( !value
.empty() ) 
1660         SetUpdateFont(false); 
1667 bool wxTextCtrl::SetForegroundColour(const wxColour
& colour
) 
1669     if ( !wxControl::SetForegroundColour(colour
) ) 
1672     // update default fg colour too 
1673     m_defaultStyle
.SetTextColour(colour
); 
1678 bool wxTextCtrl::SetBackgroundColour( const wxColour 
&colour 
) 
1680     wxCHECK_MSG( m_text 
!= NULL
, false, wxT("invalid text ctrl") ); 
1682     if ( !wxControl::SetBackgroundColour( colour 
) ) 
1685     if (!m_backgroundColour
.IsOk()) 
1688     // change active background color too 
1689     m_defaultStyle
.SetBackgroundColour( colour 
); 
1694 bool wxTextCtrl::SetStyle( long start
, long end
, const wxTextAttr
& style 
) 
1696     if ( IsMultiLine() ) 
1698         if ( style
.IsDefault() ) 
1704         gint l 
= gtk_text_buffer_get_char_count( m_buffer 
); 
1706         wxCHECK_MSG( start 
>= 0 && end 
<= l
, false, 
1707                      wxT("invalid range in wxTextCtrl::SetStyle") ); 
1709         GtkTextIter starti
, endi
; 
1710         gtk_text_buffer_get_iter_at_offset( m_buffer
, &starti
, start 
); 
1711         gtk_text_buffer_get_iter_at_offset( m_buffer
, &endi
, end 
); 
1713         wxGtkTextApplyTagsFromAttr( m_widget
, m_buffer
, style
, &starti
, &endi 
); 
1717     //else: single line text controls don't support styles 
1722 bool wxTextCtrl::GetStyle(long position
, wxTextAttr
& style
) 
1724     if ( !IsMultiLine() ) 
1726         // no styles for GtkEntry 
1730     gint l 
= gtk_text_buffer_get_char_count( m_buffer 
); 
1732     wxCHECK_MSG( position 
>= 0 && position 
<= l
, false, 
1733                  wxT("invalid range in wxTextCtrl::GetStyle") ); 
1735     GtkTextIter positioni
; 
1736     gtk_text_buffer_get_iter_at_offset(m_buffer
, &positioni
, position
); 
1738     // Obtain a copy of the default attributes 
1739     GtkTextAttributes 
* const 
1740         pattr 
= gtk_text_view_get_default_attributes(GTK_TEXT_VIEW(m_text
)); 
1741     wxON_BLOCK_EXIT1(gtk_text_attributes_unref
, pattr
); 
1743     // And query GTK for the attributes at the given position using it as base 
1744     if ( !gtk_text_iter_get_attributes(&positioni
, pattr
) ) 
1746         style 
= m_defaultStyle
; 
1748     else // have custom attributes 
1750         style
.SetBackgroundColour(pattr
->appearance
.bg_color
); 
1751         style
.SetTextColour(pattr
->appearance
.fg_color
); 
1754             pangoFontString(pango_font_description_to_string(pattr
->font
)); 
1757         if ( font
.SetNativeFontInfo(wxString(pangoFontString
)) ) 
1758             style
.SetFont(font
); 
1760         // TODO: set alignment, tabs and indents 
1766 void wxTextCtrl::DoApplyWidgetStyle(GtkRcStyle 
*style
) 
1768     GTKApplyStyle(m_text
, style
); 
1771 void wxTextCtrl::OnCut(wxCommandEvent
& WXUNUSED(event
)) 
1776 void wxTextCtrl::OnCopy(wxCommandEvent
& WXUNUSED(event
)) 
1781 void wxTextCtrl::OnPaste(wxCommandEvent
& WXUNUSED(event
)) 
1786 void wxTextCtrl::OnUndo(wxCommandEvent
& WXUNUSED(event
)) 
1791 void wxTextCtrl::OnRedo(wxCommandEvent
& WXUNUSED(event
)) 
1796 void wxTextCtrl::OnUpdateCut(wxUpdateUIEvent
& event
) 
1798     event
.Enable( CanCut() ); 
1801 void wxTextCtrl::OnUpdateCopy(wxUpdateUIEvent
& event
) 
1803     event
.Enable( CanCopy() ); 
1806 void wxTextCtrl::OnUpdatePaste(wxUpdateUIEvent
& event
) 
1808     event
.Enable( CanPaste() ); 
1811 void wxTextCtrl::OnUpdateUndo(wxUpdateUIEvent
& event
) 
1813     event
.Enable( CanUndo() ); 
1816 void wxTextCtrl::OnUpdateRedo(wxUpdateUIEvent
& event
) 
1818     event
.Enable( CanRedo() ); 
1821 wxSize 
wxTextCtrl::DoGetBestSize() const 
1823     return DoGetSizeFromTextSize(80); 
1826 wxSize 
wxTextCtrl::DoGetSizeFromTextSize(int xlen
, int ylen
) const 
1828     wxASSERT_MSG( m_widget
, wxS("GetSizeFromTextSize called before creation") ); 
1830     wxSize 
tsize(xlen
, 0); 
1831     int cHeight 
= GetCharHeight(); 
1833     if ( IsSingleLine() ) 
1835         if ( HasFlag(wxBORDER_NONE
) ) 
1847             tsize
.y 
= GTKGetPreferredSize(m_widget
).y
; 
1848             // Add the margins we have previously set, but only the horizontal border 
1849             // as vertical one has been taken account at GTKGetPreferredSize(). 
1850             // Also get other GTK+ margins. 
1851             tsize
.IncBy( GTKGetEntryMargins(GetEntry()).x
, 0); 
1858         // add space for vertical scrollbar 
1859         if ( m_scrollBar
[1] && !(m_windowStyle 
& wxTE_NO_VSCROLL
) ) 
1860             tsize
.IncBy(GTKGetPreferredSize(GTK_WIDGET(m_scrollBar
[1])).x 
+ 3, 0); 
1866             tsize
.y 
= 1 + cHeight 
* wxMax(wxMin(GetNumberOfLines(), 10), 2); 
1867             // add space for horizontal scrollbar 
1868             if ( m_scrollBar
[0] && (m_windowStyle 
& wxHSCROLL
) ) 
1869                 tsize
.IncBy(0, GTKGetPreferredSize(GTK_WIDGET(m_scrollBar
[0])).y 
+ 3); 
1872         if ( !HasFlag(wxBORDER_NONE
) ) 
1874             // hardcode borders, margins, etc 
1879     // Perhaps the user wants something different from CharHeight, or ylen 
1880     // is used as the height of a multiline text. 
1882         tsize
.IncBy(0, ylen 
- cHeight
); 
1888 // ---------------------------------------------------------------------------- 
1890 // ---------------------------------------------------------------------------- 
1892 void wxTextCtrl::DoFreeze() 
1894     wxCHECK_RET(m_text 
!= NULL
, wxT("invalid text ctrl")); 
1896     GTKFreezeWidget(m_text
); 
1898     if ( HasFlag(wxTE_MULTILINE
) ) 
1900         // removing buffer dramatically speeds up insertion: 
1901         g_object_ref(m_buffer
); 
1902         GtkTextBuffer
* buf_new 
= gtk_text_buffer_new(NULL
); 
1903         gtk_text_view_set_buffer(GTK_TEXT_VIEW(m_text
), buf_new
); 
1904         // gtk_text_view_set_buffer adds its own reference 
1905         g_object_unref(buf_new
); 
1906         // These marks should be deleted when the buffer is changed, 
1907         // but they are not (in GTK+ up to at least 3.0.1). 
1908         // Otherwise these anonymous marks start to build up in the buffer, 
1909         // and Freeze takes longer and longer each time it is called. 
1910         if (m_anonymousMarkList
) 
1912             for (GSList
* item 
= m_anonymousMarkList
; item
; item 
= item
->next
) 
1914                 GtkTextMark
* mark 
= static_cast<GtkTextMark
*>(item
->data
); 
1915                 if (GTK_IS_TEXT_MARK(mark
) && !gtk_text_mark_get_deleted(mark
)) 
1916                     gtk_text_buffer_delete_mark(m_buffer
, mark
); 
1918             g_slist_free(m_anonymousMarkList
); 
1919             m_anonymousMarkList 
= NULL
; 
1924 void wxTextCtrl::DoThaw() 
1926     if ( HasFlag(wxTE_MULTILINE
) ) 
1929         gulong sig_id 
= g_signal_connect(m_buffer
, "mark_set", G_CALLBACK(mark_set
), &m_anonymousMarkList
); 
1930         gtk_text_view_set_buffer(GTK_TEXT_VIEW(m_text
), m_buffer
); 
1931         g_object_unref(m_buffer
); 
1932         g_signal_handler_disconnect(m_buffer
, sig_id
); 
1934         if (m_showPositionOnThaw 
!= NULL
) 
1936             gtk_text_view_scroll_mark_onscreen( 
1937                 GTK_TEXT_VIEW(m_text
), m_showPositionOnThaw
); 
1938             m_showPositionOnThaw 
= NULL
; 
1942     GTKThawWidget(m_text
); 
1945 // ---------------------------------------------------------------------------- 
1946 // wxTextUrlEvent passing if style & wxTE_AUTO_URL 
1947 // ---------------------------------------------------------------------------- 
1949 // FIXME: when dragging on a link the sample gets an "Unknown event". 
1950 // This might be an excessive event from us or a buggy wxMouseEvent::Moving() or 
1951 // a buggy sample, or something else 
1952 void wxTextCtrl::OnUrlMouseEvent(wxMouseEvent
& event
) 
1955     if( !HasFlag(wxTE_AUTO_URL
) ) 
1959     GtkTextIter start
, end
; 
1960     GtkTextTag 
*tag 
= gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(m_buffer
), 
1963     gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(m_text
), GTK_TEXT_WINDOW_WIDGET
, 
1964                                           event
.GetX(), event
.GetY(), &x
, &y
); 
1966     gtk_text_view_get_iter_at_location(GTK_TEXT_VIEW(m_text
), &end
, x
, y
); 
1967     if (!gtk_text_iter_has_tag(&end
, tag
)) 
1969         SetCursor(wxCursor(wxCURSOR_IBEAM
)); 
1973     SetCursor(wxCursor(wxCURSOR_HAND
)); 
1976     if(!gtk_text_iter_begins_tag(&start
, tag
)) 
1977         gtk_text_iter_backward_to_tag_toggle(&start
, tag
); 
1978     if(!gtk_text_iter_ends_tag(&end
, tag
)) 
1979         gtk_text_iter_forward_to_tag_toggle(&end
, tag
); 
1981     // Native context menu is probably not desired on an URL. 
1982     // Consider making this dependent on ProcessEvent(wxTextUrlEvent) return value 
1983     if(event
.GetEventType() == wxEVT_RIGHT_DOWN
) 
1986     wxTextUrlEvent 
url_event(m_windowId
, event
, 
1987                              gtk_text_iter_get_offset(&start
), 
1988                              gtk_text_iter_get_offset(&end
)); 
1990     InitCommandEvent(url_event
); 
1991     // Is that a good idea? Seems not (pleasure with gtk_text_view_start_selection_drag) 
1992     //event.Skip(!HandleWindowEvent(url_event)); 
1993     HandleWindowEvent(url_event
); 
1996 bool wxTextCtrl::GTKProcessEvent(wxEvent
& event
) const 
1998     bool rc 
= wxTextCtrlBase::GTKProcessEvent(event
); 
2000     // GtkTextView starts a drag operation when left mouse button is pressed 
2001     // and ends it when it is released and if it doesn't get the release event 
2002     // the next click on a control results in an assertion failure inside 
2003     // gtk_text_view_start_selection_drag() which simply *kills* the program 
2004     // without anything we can do about it, so always let GTK+ have this event 
2005     return rc 
&& (IsSingleLine() || event
.GetEventType() != wxEVT_LEFT_UP
); 
2010 wxTextCtrl::GetClassDefaultAttributes(wxWindowVariant 
WXUNUSED(variant
)) 
2012     return GetDefaultAttributesFromGTKWidget(gtk_entry_new
, true); 
2015 #endif // wxUSE_TEXTCTRL