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" 
  20     #include "wx/settings.h" 
  24 #include "wx/strconv.h" 
  25 #include "wx/fontutil.h"        // for wxNativeFontInfo (GetNativeFontInfo()) 
  27 #include <sys/types.h> 
  31 #include "wx/gtk/private.h" 
  32 #include <gdk/gdkkeysyms.h> 
  34 //----------------------------------------------------------------------------- 
  36 //----------------------------------------------------------------------------- 
  38 extern wxWindowGTK 
*g_delayedFocus
; 
  40 // ---------------------------------------------------------------------------- 
  42 // ---------------------------------------------------------------------------- 
  45 static void wxGtkOnRemoveTag(GtkTextBuffer 
*buffer
, 
  52     g_object_get (tag
, "name", &name
, NULL
); 
  54     if (!name 
|| strncmp(name
, prefix
, strlen(prefix
))) 
  55         // anonymous tag or not starting with prefix - don't remove 
  56         g_signal_stop_emission_by_name (buffer
, "remove_tag"); 
  63 static void wxGtkTextApplyTagsFromAttr(GtkTextBuffer 
*text_buffer
, 
  64                                        const wxTextAttr
& attr
, 
  68     static gchar buf
[1024]; 
  71     gulong remove_handler_id 
= g_signal_connect (text_buffer
, "remove_tag", 
  72             G_CALLBACK (wxGtkOnRemoveTag
), gpointer("WX")); 
  73     gtk_text_buffer_remove_all_tags(text_buffer
, start
, end
); 
  74     g_signal_handler_disconnect (text_buffer
, remove_handler_id
); 
  79         PangoFontDescription 
*font_description 
= attr
.GetFont().GetNativeFontInfo()->description
; 
  80         font_string 
= pango_font_description_to_string(font_description
); 
  81         g_snprintf(buf
, sizeof(buf
), "WXFONT %s", font_string
); 
  82         tag 
= gtk_text_tag_table_lookup( gtk_text_buffer_get_tag_table( text_buffer 
), 
  85             tag 
= gtk_text_buffer_create_tag( text_buffer
, buf
, 
  86                                               "font-desc", font_description
, 
  88         gtk_text_buffer_apply_tag (text_buffer
, tag
, start
, end
); 
  91         if (attr
.GetFont().GetUnderlined()) 
  93             g_snprintf(buf
, sizeof(buf
), "WXFONTUNDERLINE"); 
  94             tag 
= gtk_text_tag_table_lookup( gtk_text_buffer_get_tag_table( text_buffer 
), 
  97                 tag 
= gtk_text_buffer_create_tag( text_buffer
, buf
, 
  98                                                   "underline-set", TRUE
, 
  99                                                   "underline", PANGO_UNDERLINE_SINGLE
, 
 101             gtk_text_buffer_apply_tag (text_buffer
, tag
, start
, end
); 
 105     if (attr
.HasTextColour()) 
 107         const GdkColor 
*colFg 
= attr
.GetTextColour().GetColor(); 
 108         g_snprintf(buf
, sizeof(buf
), "WXFORECOLOR %d %d %d", 
 109                    colFg
->red
, colFg
->green
, colFg
->blue
); 
 110         tag 
= gtk_text_tag_table_lookup( gtk_text_buffer_get_tag_table( text_buffer 
), 
 113             tag 
= gtk_text_buffer_create_tag( text_buffer
, buf
, 
 114                                               "foreground-gdk", colFg
, NULL 
); 
 115         gtk_text_buffer_apply_tag (text_buffer
, tag
, start
, end
); 
 118     if (attr
.HasBackgroundColour()) 
 120         const GdkColor 
*colBg 
= attr
.GetBackgroundColour().GetColor(); 
 121         g_snprintf(buf
, sizeof(buf
), "WXBACKCOLOR %d %d %d", 
 122                    colBg
->red
, colBg
->green
, colBg
->blue
); 
 123         tag 
= gtk_text_tag_table_lookup( gtk_text_buffer_get_tag_table( text_buffer 
), 
 126             tag 
= gtk_text_buffer_create_tag( text_buffer
, buf
, 
 127                                               "background-gdk", colBg
, NULL 
); 
 128         gtk_text_buffer_apply_tag (text_buffer
, tag
, start
, end
); 
 131     if (attr
.HasAlignment()) 
 133         GtkTextIter para_start
, para_end 
= *end
; 
 134         gtk_text_buffer_get_iter_at_line( text_buffer
, 
 136                                           gtk_text_iter_get_line(start
) ); 
 137         gtk_text_iter_forward_line(¶_end
); 
 139         remove_handler_id 
= g_signal_connect (text_buffer
, "remove_tag", 
 140                                               G_CALLBACK(wxGtkOnRemoveTag
), 
 141                                               gpointer("WXALIGNMENT")); 
 142         gtk_text_buffer_remove_all_tags( text_buffer
, ¶_start
, ¶_end 
); 
 143         g_signal_handler_disconnect (text_buffer
, remove_handler_id
); 
 145         GtkJustification align
; 
 146         switch (attr
.GetAlignment()) 
 149                 align 
= GTK_JUSTIFY_LEFT
; 
 151             case wxTEXT_ALIGNMENT_RIGHT
: 
 152                 align 
= GTK_JUSTIFY_RIGHT
; 
 154             case wxTEXT_ALIGNMENT_CENTER
: 
 155                 align 
= GTK_JUSTIFY_CENTER
; 
 157             // gtk+ doesn't support justify as of gtk+-2.7.4 
 160         g_snprintf(buf
, sizeof(buf
), "WXALIGNMENT %d", align
); 
 161         tag 
= gtk_text_tag_table_lookup( gtk_text_buffer_get_tag_table( text_buffer 
), 
 164             tag 
= gtk_text_buffer_create_tag( text_buffer
, buf
, 
 165                                               "justification", align
, NULL 
); 
 166         gtk_text_buffer_apply_tag( text_buffer
, tag
, ¶_start
, ¶_end 
); 
 172 static void wxGtkTextInsert(GtkWidget 
*text
, 
 173                             GtkTextBuffer 
*text_buffer
, 
 174                             const wxTextAttr
& attr
, 
 175                             const wxCharBuffer
& buffer
) 
 179     GtkTextIter iter
, start
; 
 181     gtk_text_buffer_get_iter_at_mark( text_buffer
, &iter
, 
 182                                      gtk_text_buffer_get_insert (text_buffer
) ); 
 183     start_offset 
= gtk_text_iter_get_offset (&iter
); 
 184     gtk_text_buffer_insert( text_buffer
, &iter
, buffer
, strlen(buffer
) ); 
 186     gtk_text_buffer_get_iter_at_offset (text_buffer
, &start
, start_offset
); 
 188     wxGtkTextApplyTagsFromAttr(text_buffer
, attr
, &start
, &iter
); 
 192 // ---------------------------------------------------------------------------- 
 193 // "insert_text" for GtkEntry 
 194 // ---------------------------------------------------------------------------- 
 198 gtk_insert_text_callback(GtkEditable 
*editable
, 
 199                          const gchar 
*new_text
, 
 200                          gint new_text_length
, 
 205         wxapp_install_idle_handler(); 
 207     // we should only be called if we have a max len limit at all 
 208     GtkEntry 
*entry 
= GTK_ENTRY (editable
); 
 210     wxCHECK_RET( entry
->text_max_length
, _T("shouldn't be called") ); 
 212     // check that we don't overflow the max length limit 
 214     // FIXME: this doesn't work when we paste a string which is going to be 
 216     if ( entry
->text_length 
== entry
->text_max_length 
) 
 218         // we don't need to run the base class version at all 
 219         g_signal_stop_emission_by_name (editable
, "insert_text"); 
 221         // remember that the next changed signal is to be ignored to avoid 
 222         // generating a dummy wxEVT_COMMAND_TEXT_UPDATED event 
 223         win
->IgnoreNextTextUpdate(); 
 225         // and generate the correct one ourselves 
 226         wxCommandEvent 
event(wxEVT_COMMAND_TEXT_MAXLEN
, win
->GetId()); 
 227         event
.SetEventObject(win
); 
 228         event
.SetString(win
->GetValue()); 
 229         win
->GetEventHandler()->ProcessEvent( event 
); 
 234 // Implementation of wxTE_AUTO_URL for wxGTK2 by Mart Raudsepp, 
 238 au_apply_tag_callback(GtkTextBuffer 
*buffer
, 
 244     if(tag 
== gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(buffer
), "wxUrl")) 
 245         g_signal_stop_emission_by_name (buffer
, "apply_tag"); 
 249 //----------------------------------------------------------------------------- 
 250 //  GtkTextCharPredicates for gtk_text_iter_*_find_char 
 251 //----------------------------------------------------------------------------- 
 255 pred_whitespace (gunichar ch
, gpointer user_data
) 
 257     return g_unichar_isspace(ch
); 
 263 pred_non_whitespace (gunichar ch
, gpointer user_data
) 
 265     return !g_unichar_isspace(ch
); 
 271 pred_nonpunct (gunichar ch
, gpointer user_data
) 
 273     return !g_unichar_ispunct(ch
); 
 279 pred_nonpunct_or_slash (gunichar ch
, gpointer user_data
) 
 281     return !g_unichar_ispunct(ch
) || ch 
== '/'; 
 285 //----------------------------------------------------------------------------- 
 286 //  Check for links between s and e and correct tags as necessary 
 287 //----------------------------------------------------------------------------- 
 289 // This function should be made match better while being efficient at one point. 
 290 // Most probably with a row of regular expressions. 
 293 au_check_word( GtkTextIter 
*s
, GtkTextIter 
*e 
) 
 295     static const char *URIPrefixes
[] = 
 313     GtkTextIter start 
= *s
, end 
= *e
; 
 314     GtkTextBuffer 
*buffer 
= gtk_text_iter_get_buffer(s
); 
 316     // Get our special link tag 
 317     GtkTextTag 
*tag 
= gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(buffer
), "wxUrl"); 
 319     // Get rid of punctuation from beginning and end. 
 320     // Might want to move this to au_check_range if an improved link checking doesn't 
 321     // use some intelligent punctuation checking itself (beware of undesired iter modifications). 
 322     if(g_unichar_ispunct( gtk_text_iter_get_char( &start 
) ) ) 
 323         gtk_text_iter_forward_find_char( &start
, pred_nonpunct
, NULL
, e 
); 
 325     gtk_text_iter_backward_find_char( &end
, pred_nonpunct_or_slash
, NULL
, &start 
); 
 326     gtk_text_iter_forward_char(&end
); 
 328     gchar
* text 
= gtk_text_iter_get_text( &start
, &end 
); 
 329     size_t len 
= strlen(text
), prefix_len
; 
 332     for( n 
= 0; n 
< WXSIZEOF(URIPrefixes
); ++n 
) 
 334         prefix_len 
= strlen(URIPrefixes
[n
]); 
 335         if((len 
> prefix_len
) && !strncasecmp(text
, URIPrefixes
[n
], prefix_len
)) 
 339     if(n 
< WXSIZEOF(URIPrefixes
)) 
 341         gulong signal_id 
= g_signal_handler_find (buffer
, 
 342                                                   (GSignalMatchType
) (G_SIGNAL_MATCH_FUNC
), 
 344                                                   (gpointer
)au_apply_tag_callback
, NULL
); 
 346         g_signal_handler_block (buffer
, signal_id
); 
 347         gtk_text_buffer_apply_tag(buffer
, tag
, &start
, &end
); 
 348         g_signal_handler_unblock (buffer
, signal_id
); 
 355 au_check_range(GtkTextIter 
*s
, 
 356                GtkTextIter 
*range_end
) 
 358     GtkTextIter range_start 
= *s
; 
 359     GtkTextIter word_end
; 
 360     GtkTextBuffer 
*buffer 
= gtk_text_iter_get_buffer(s
); 
 361     GtkTextTag 
*tag 
= gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(buffer
), "wxUrl"); 
 363     gtk_text_buffer_remove_tag(buffer
, tag
, s
, range_end
); 
 365     if(g_unichar_isspace(gtk_text_iter_get_char(&range_start
))) 
 366         gtk_text_iter_forward_find_char(&range_start
, pred_non_whitespace
, NULL
, range_end
); 
 368     while(!gtk_text_iter_equal(&range_start
, range_end
)) 
 370         word_end 
= range_start
; 
 371         gtk_text_iter_forward_find_char(&word_end
, pred_whitespace
, NULL
, range_end
); 
 373         // Now we should have a word delimited by range_start and word_end, correct link tags 
 374         au_check_word(&range_start
, &word_end
); 
 376         range_start 
= word_end
; 
 377         gtk_text_iter_forward_find_char(&range_start
, pred_non_whitespace
, NULL
, range_end
); 
 382 //----------------------------------------------------------------------------- 
 383 //  "insert-text" for GtkTextBuffer 
 384 //----------------------------------------------------------------------------- 
 388 au_insert_text_callback(GtkTextBuffer 
*buffer
, 
 394     if (!len 
|| !(win
->GetWindowStyleFlag() & wxTE_AUTO_URL
) ) 
 397     GtkTextIter start 
= *end
; 
 398     gtk_text_iter_backward_chars(&start
, g_utf8_strlen(text
, len
)); 
 400     GtkTextIter line_start 
= start
; 
 401     GtkTextIter line_end 
= *end
; 
 402     GtkTextIter words_start 
= start
; 
 403     GtkTextIter words_end 
= *end
; 
 405     gtk_text_iter_set_line(&line_start
, gtk_text_iter_get_line(&start
)); 
 406     gtk_text_iter_forward_to_line_end(&line_end
); 
 407     gtk_text_iter_backward_find_char(&words_start
, pred_whitespace
, NULL
, &line_start
); 
 408     gtk_text_iter_forward_find_char(&words_end
, pred_whitespace
, NULL
, &line_end
); 
 410     au_check_range(&words_start
, &words_end
); 
 414 //----------------------------------------------------------------------------- 
 415 //  "delete-range" for GtkTextBuffer 
 416 //----------------------------------------------------------------------------- 
 420 au_delete_range_callback(GtkTextBuffer 
*buffer
, 
 425     if( !(win
->GetWindowStyleFlag() & wxTE_AUTO_URL
) ) 
 428     GtkTextIter line_start 
= *start
, line_end 
= *end
; 
 430     gtk_text_iter_set_line(&line_start
, gtk_text_iter_get_line(start
)); 
 431     gtk_text_iter_forward_to_line_end(&line_end
); 
 432     gtk_text_iter_backward_find_char(start
, pred_whitespace
, NULL
, &line_start
); 
 433     gtk_text_iter_forward_find_char(end
, pred_whitespace
, NULL
, &line_end
); 
 435     au_check_range(start
, end
); 
 440 //----------------------------------------------------------------------------- 
 442 //----------------------------------------------------------------------------- 
 446 gtk_text_changed_callback( GtkWidget 
*widget
, wxTextCtrl 
*win 
) 
 448     if ( win
->IgnoreTextUpdate() ) 
 451     if (!win
->m_hasVMT
) return; 
 454         wxapp_install_idle_handler(); 
 456     if ( win
->MarkDirtyOnChange() ) 
 459     wxCommandEvent 
event( wxEVT_COMMAND_TEXT_UPDATED
, win
->GetId() ); 
 460     event
.SetEventObject( win 
); 
 461     win
->GetEventHandler()->ProcessEvent( event 
); 
 465 //----------------------------------------------------------------------------- 
 466 //  clipboard events: "copy-clipboard", "cut-clipboard", "paste-clipboard" 
 467 //----------------------------------------------------------------------------- 
 469 // common part of the event handlers below 
 471 handle_text_clipboard_callback( GtkWidget 
*widget
, wxTextCtrl 
*win
, 
 472                                 wxEventType eventType
, const gchar 
* signal_name
) 
 474     wxClipboardTextEvent 
event( eventType
, win
->GetId() ); 
 475     event
.SetEventObject( win 
); 
 476     if ( win
->GetEventHandler()->ProcessEvent( event 
) ) 
 478         // don't let the default processing to take place if we did something 
 479         // ourselves in the event handler 
 480         g_signal_stop_emission_by_name (widget
, signal_name
); 
 486 gtk_copy_clipboard_callback( GtkWidget 
*widget
, wxTextCtrl 
*win 
) 
 488     handle_text_clipboard_callback( 
 489         widget
, win
, wxEVT_COMMAND_TEXT_COPY
, "copy-clipboard" ); 
 493 gtk_cut_clipboard_callback( GtkWidget 
*widget
, wxTextCtrl 
*win 
) 
 495     handle_text_clipboard_callback( 
 496         widget
, win
, wxEVT_COMMAND_TEXT_CUT
, "cut-clipboard" ); 
 500 gtk_paste_clipboard_callback( GtkWidget 
*widget
, wxTextCtrl 
*win 
) 
 502     handle_text_clipboard_callback( 
 503         widget
, win
, wxEVT_COMMAND_TEXT_PASTE
, "paste-clipboard" ); 
 507 //----------------------------------------------------------------------------- 
 508 // "expose_event" from scrolled window and textview 
 509 //----------------------------------------------------------------------------- 
 513 gtk_text_exposed_callback( GtkWidget 
*widget
, GdkEventExpose 
*event
, wxTextCtrl 
*win 
) 
 520 //----------------------------------------------------------------------------- 
 522 //----------------------------------------------------------------------------- 
 524 IMPLEMENT_DYNAMIC_CLASS(wxTextCtrl
, wxTextCtrlBase
) 
 526 BEGIN_EVENT_TABLE(wxTextCtrl
, wxTextCtrlBase
) 
 527     EVT_CHAR(wxTextCtrl::OnChar
) 
 529     EVT_MENU(wxID_CUT
, wxTextCtrl::OnCut
) 
 530     EVT_MENU(wxID_COPY
, wxTextCtrl::OnCopy
) 
 531     EVT_MENU(wxID_PASTE
, wxTextCtrl::OnPaste
) 
 532     EVT_MENU(wxID_UNDO
, wxTextCtrl::OnUndo
) 
 533     EVT_MENU(wxID_REDO
, wxTextCtrl::OnRedo
) 
 535     EVT_UPDATE_UI(wxID_CUT
, wxTextCtrl::OnUpdateCut
) 
 536     EVT_UPDATE_UI(wxID_COPY
, wxTextCtrl::OnUpdateCopy
) 
 537     EVT_UPDATE_UI(wxID_PASTE
, wxTextCtrl::OnUpdatePaste
) 
 538     EVT_UPDATE_UI(wxID_UNDO
, wxTextCtrl::OnUpdateUndo
) 
 539     EVT_UPDATE_UI(wxID_REDO
, wxTextCtrl::OnUpdateRedo
) 
 541     // wxTE_AUTO_URL wxTextUrl support. Currently only creates 
 542     // wxTextUrlEvent in the same cases as wxMSW, more can be added here. 
 543     EVT_MOTION      (wxTextCtrl::OnUrlMouseEvent
) 
 544     EVT_LEFT_DOWN   (wxTextCtrl::OnUrlMouseEvent
) 
 545     EVT_LEFT_UP     (wxTextCtrl::OnUrlMouseEvent
) 
 546     EVT_LEFT_DCLICK (wxTextCtrl::OnUrlMouseEvent
) 
 547     EVT_RIGHT_DOWN  (wxTextCtrl::OnUrlMouseEvent
) 
 548     EVT_RIGHT_UP    (wxTextCtrl::OnUrlMouseEvent
) 
 549     EVT_RIGHT_DCLICK(wxTextCtrl::OnUrlMouseEvent
) 
 552 void wxTextCtrl::Init() 
 558     SetUpdateFont(false); 
 562     m_gdkHandCursor 
= NULL
; 
 563     m_gdkXTermCursor 
= NULL
; 
 566 wxTextCtrl::~wxTextCtrl() 
 569         gdk_cursor_unref(m_gdkHandCursor
); 
 571         gdk_cursor_unref(m_gdkXTermCursor
); 
 574 wxTextCtrl::wxTextCtrl( wxWindow 
*parent
, 
 576                         const wxString 
&value
, 
 580                         const wxValidator
& validator
, 
 581                         const wxString 
&name 
) 
 585     Create( parent
, id
, value
, pos
, size
, style
, validator
, name 
); 
 588 bool wxTextCtrl::Create( wxWindow 
*parent
, 
 590                          const wxString 
&value
, 
 594                          const wxValidator
& validator
, 
 595                          const wxString 
&name 
) 
 598     m_acceptsFocus 
= true; 
 600     if (!PreCreation( parent
, pos
, size 
) || 
 601         !CreateBase( parent
, id
, pos
, size
, style
, validator
, name 
)) 
 603         wxFAIL_MSG( wxT("wxTextCtrl creation failed") ); 
 607     bool multi_line 
= (style 
& wxTE_MULTILINE
) != 0; 
 612         m_text 
= gtk_text_view_new(); 
 614         m_buffer 
= gtk_text_view_get_buffer( GTK_TEXT_VIEW(m_text
) ); 
 616         // create scrolled window 
 617         m_widget 
= gtk_scrolled_window_new( NULL
, NULL 
); 
 618         gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( m_widget 
), 
 619                                         GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC 
); 
 620         // for ScrollLines/Pages 
 621         m_scrollBar
[1] = (GtkRange
*)((GtkScrolledWindow
*)m_widget
)->vscrollbar
; 
 623         // Insert view into scrolled window 
 624         gtk_container_add( GTK_CONTAINER(m_widget
), m_text 
); 
 626         // translate wx wrapping style to GTK+ 
 628         if ( HasFlag( wxTE_DONTWRAP 
) ) 
 629             wrap 
= GTK_WRAP_NONE
; 
 630         else if ( HasFlag( wxTE_CHARWRAP 
) ) 
 631             wrap 
= GTK_WRAP_CHAR
; 
 632         else if ( HasFlag( wxTE_WORDWRAP 
) ) 
 633             wrap 
= GTK_WRAP_WORD
; 
 634         else // HasFlag(wxTE_BESTWRAP) always true as wxTE_BESTWRAP == 0 
 636             // GTK_WRAP_WORD_CHAR seems to be new in GTK+ 2.4 
 638             if ( !gtk_check_version(2,4,0) ) 
 640                 wrap 
= GTK_WRAP_WORD_CHAR
; 
 644             wrap 
= GTK_WRAP_WORD
; 
 647         gtk_text_view_set_wrap_mode( GTK_TEXT_VIEW( m_text 
), wrap 
); 
 649         GtkScrolledWindowSetBorder(m_widget
, style
); 
 651         gtk_widget_add_events( GTK_WIDGET(m_text
), GDK_ENTER_NOTIFY_MASK 
| GDK_LEAVE_NOTIFY_MASK 
); 
 653         GTK_WIDGET_UNSET_FLAGS( m_widget
, GTK_CAN_FOCUS 
); 
 657         // a single-line text control: no need for scrollbars 
 659         m_text 
= gtk_entry_new(); 
 661         if (style 
& wxNO_BORDER
) 
 662             g_object_set (m_text
, "has-frame", FALSE
, NULL
); 
 665     m_parent
->DoAddChild( this ); 
 667     m_focusWidget 
= m_text
; 
 673         gtk_widget_show(m_text
); 
 681     if (style 
& wxTE_PASSWORD
) 
 684             gtk_entry_set_visibility( GTK_ENTRY(m_text
), FALSE 
); 
 687     if (style 
& wxTE_READONLY
) 
 690             gtk_editable_set_editable( GTK_EDITABLE(m_text
), FALSE 
); 
 692             gtk_text_view_set_editable( GTK_TEXT_VIEW( m_text
), FALSE
); 
 697         if (style 
& wxTE_RIGHT
) 
 698             gtk_text_view_set_justification( GTK_TEXT_VIEW(m_text
), GTK_JUSTIFY_RIGHT 
); 
 699         else if (style 
& wxTE_CENTRE
) 
 700             gtk_text_view_set_justification( GTK_TEXT_VIEW(m_text
), GTK_JUSTIFY_CENTER 
); 
 701         // Left justify (alignment) is the default and we don't need to apply GTK_JUSTIFY_LEFT 
 706         // gtk_entry_set_alignment was introduced in gtk+-2.3.5 
 707         if (!gtk_check_version(2,4,0)) 
 709             if (style 
& wxTE_RIGHT
) 
 710                 gtk_entry_set_alignment( GTK_ENTRY(m_text
), 1.0 ); 
 711             else if (style 
& wxTE_CENTRE
) 
 712                 gtk_entry_set_alignment( GTK_ENTRY(m_text
), 0.5 ); 
 717     // We want to be notified about text changes. 
 720         g_signal_connect (m_buffer
, "changed", 
 721                           G_CALLBACK (gtk_text_changed_callback
), this); 
 723         // .. and handle URLs on multi-line controls with wxTE_AUTO_URL style 
 724         if (style 
& wxTE_AUTO_URL
) 
 726             GtkTextIter start
, end
; 
 727             m_gdkHandCursor 
= gdk_cursor_new(GDK_HAND2
); 
 728             m_gdkXTermCursor 
= gdk_cursor_new(GDK_XTERM
); 
 730             // We create our wxUrl tag here for slight efficiency gain - we 
 731             // don't have to check for the tag existance in callbacks, 
 732             // hereby it's guaranteed to exist. 
 733             gtk_text_buffer_create_tag(m_buffer
, "wxUrl", 
 734                                        "foreground", "blue", 
 735                                        "underline", PANGO_UNDERLINE_SINGLE
, 
 738             // Check for URLs after each text change 
 739             g_signal_connect_after (m_buffer
, "insert_text", 
 740                                     G_CALLBACK (au_insert_text_callback
), this); 
 741             g_signal_connect_after (m_buffer
, "delete_range", 
 742                                     G_CALLBACK (au_delete_range_callback
), this); 
 744             // Block all wxUrl tag applying unless we do it ourselves, in which case we 
 745             // block this callback temporarily. This takes care of gtk+ internal 
 746             // gtk_text_buffer_insert_range* calls that would copy our URL tag otherwise, 
 747             // which is undesired because only a part of the URL might be copied. 
 748             // The insert-text signal emitted inside it will take care of newly formed 
 749             // or wholly copied URLs. 
 750             g_signal_connect (m_buffer
, "apply_tag", 
 751                               G_CALLBACK (au_apply_tag_callback
), NULL
); 
 753             // Check for URLs in the initial string passed to Create 
 754             gtk_text_buffer_get_start_iter(m_buffer
, &start
); 
 755             gtk_text_buffer_get_end_iter(m_buffer
, &end
); 
 756             au_check_range(&start
, &end
); 
 761         g_signal_connect (m_text
, "changed", 
 762                           G_CALLBACK (gtk_text_changed_callback
), this); 
 765     g_signal_connect (m_text
, "copy-clipboard", 
 766                       G_CALLBACK (gtk_copy_clipboard_callback
), this); 
 767     g_signal_connect (m_text
, "cut-clipboard", 
 768                       G_CALLBACK (gtk_cut_clipboard_callback
), this); 
 769     g_signal_connect (m_text
, "paste-clipboard", 
 770                       G_CALLBACK (gtk_paste_clipboard_callback
), this); 
 772     m_cursor 
= wxCursor( wxCURSOR_IBEAM 
); 
 774     wxTextAttr 
attrDef(GetForegroundColour(), GetBackgroundColour(), GetFont()); 
 775     SetDefaultStyle( attrDef 
); 
 781 void wxTextCtrl::CalculateScrollbar() 
 785 wxString 
wxTextCtrl::GetValue() const 
 787     wxCHECK_MSG( m_text 
!= NULL
, wxEmptyString
, wxT("invalid text ctrl") ); 
 790     if (m_windowStyle 
& wxTE_MULTILINE
) 
 793         gtk_text_buffer_get_start_iter( m_buffer
, &start 
); 
 795         gtk_text_buffer_get_end_iter( m_buffer
, &end 
); 
 796         gchar 
*text 
= gtk_text_buffer_get_text( m_buffer
, &start
, &end
, TRUE 
); 
 798         const wxWxCharBuffer buf 
= wxGTK_CONV_BACK(text
); 
 806         const gchar 
*text 
= gtk_entry_get_text( GTK_ENTRY(m_text
) ); 
 807         const wxWxCharBuffer buf 
= wxGTK_CONV_BACK( text 
); 
 815 void wxTextCtrl::SetValue( const wxString 
&value 
) 
 817     wxCHECK_RET( m_text 
!= NULL
, wxT("invalid text ctrl") ); 
 819     // the control won't be modified any more as we programmatically replace 
 820     // all the existing text, so reset the flag and don't set it again (and do 
 821     // it now, before the text event handler is ran so that IsModified() called 
 822     // from there returns the expected value) 
 824     DontMarkDirtyOnNextChange(); 
 826     if (m_windowStyle 
& wxTE_MULTILINE
) 
 828         const wxCharBuffer 
buffer(wxGTK_CONV(value
)); 
 831             // what else can we do? at least don't crash... 
 835         if (gtk_text_buffer_get_char_count(m_buffer
) != 0) 
 836             IgnoreNextTextUpdate(); 
 838         gtk_text_buffer_set_text( m_buffer
, buffer
, strlen(buffer
) ); 
 842         // gtk_entry_set_text() emits two "changed" signals because internally 
 843         // it calls gtk_editable_delete_text() and gtk_editable_insert_text() 
 844         // but we want to have only one event 
 845         IgnoreNextTextUpdate(); 
 847         gtk_entry_set_text( GTK_ENTRY(m_text
), wxGTK_CONV(value
) ); 
 850     // GRG, Jun/2000: Changed this after a lot of discussion in 
 851     //   the lists. wxWidgets 2.2 will have a set of flags to 
 852     //   customize this behaviour. 
 853     SetInsertionPoint(0); 
 856 void wxTextCtrl::WriteText( const wxString 
&text 
) 
 858     wxCHECK_RET( m_text 
!= NULL
, wxT("invalid text ctrl") ); 
 863     const wxCharBuffer 
buffer(wxGTK_CONV(text
)); 
 866         // what else can we do? at least don't crash... 
 870     // we're changing the text programmatically 
 871     DontMarkDirtyOnNextChange(); 
 873     if ( m_windowStyle 
& wxTE_MULTILINE 
) 
 875         // First remove the selection if there is one 
 876         // TODO:  Is there an easier GTK specific way to do this? 
 878         GetSelection(&from
, &to
); 
 883         wxGtkTextInsert( m_text
, m_buffer
, m_defaultStyle
, buffer 
); 
 885         GtkAdjustment 
*adj 
= gtk_scrolled_window_get_vadjustment( GTK_SCROLLED_WINDOW(m_widget
) ); 
 886         // Scroll to cursor, but only if scrollbar thumb is at the very bottom 
 887         if ( wxIsSameDouble(adj
->value
, adj
->upper 
- adj
->page_size
) ) 
 889             gtk_text_view_scroll_to_mark( GTK_TEXT_VIEW(m_text
), 
 890                     gtk_text_buffer_get_insert( m_buffer 
), 0.0, FALSE
, 0.0, 1.0 ); 
 895         // First remove the selection if there is one 
 896         gtk_editable_delete_selection( GTK_EDITABLE(m_text
) ); 
 898         // This moves the cursor pos to behind the inserted text. 
 899         gint len 
= gtk_editable_get_position(GTK_EDITABLE(m_text
)); 
 901         gtk_editable_insert_text( GTK_EDITABLE(m_text
), buffer
, strlen(buffer
), &len 
); 
 903         // Bring entry's cursor uptodate. 
 904         gtk_editable_set_position( GTK_EDITABLE(m_text
), len 
); 
 908 void wxTextCtrl::AppendText( const wxString 
&text 
) 
 910     SetInsertionPointEnd(); 
 914 wxString 
wxTextCtrl::GetLineText( long lineNo 
) const 
 916     if (m_windowStyle 
& wxTE_MULTILINE
) 
 919         gtk_text_buffer_get_iter_at_line(m_buffer
,&line
,lineNo
); 
 920         GtkTextIter end 
= line
; 
 921         gtk_text_iter_forward_to_line_end(&end
); 
 922         gchar 
*text 
= gtk_text_buffer_get_text(m_buffer
,&line
,&end
,TRUE
); 
 923         wxString 
result(wxGTK_CONV_BACK(text
)); 
 929         if (lineNo 
== 0) return GetValue(); 
 930         return wxEmptyString
; 
 934 void wxTextCtrl::OnDropFiles( wxDropFilesEvent 
&WXUNUSED(event
) ) 
 936   /* If you implement this, don't forget to update the documentation! 
 937    * (file docs/latex/wx/text.tex) */ 
 938     wxFAIL_MSG( wxT("wxTextCtrl::OnDropFiles not implemented") ); 
 941 bool wxTextCtrl::PositionToXY(long pos
, long *x
, long *y 
) const 
 943     if ( m_windowStyle 
& wxTE_MULTILINE 
) 
 947         if (pos 
> GetLastPosition()) 
 950         gtk_text_buffer_get_iter_at_offset(m_buffer
, &iter
, pos
); 
 953             *y 
= gtk_text_iter_get_line(&iter
); 
 955             *x 
= gtk_text_iter_get_line_offset(&iter
); 
 957     else // single line control 
 959         if ( pos 
<= GTK_ENTRY(m_text
)->text_length 
) 
 968             // index out of bounds 
 976 long wxTextCtrl::XYToPosition(long x
, long y 
) const 
 978     if (!(m_windowStyle 
& wxTE_MULTILINE
)) return 0; 
 981     if (y 
>= gtk_text_buffer_get_line_count (m_buffer
)) 
 984     gtk_text_buffer_get_iter_at_line(m_buffer
, &iter
, y
); 
 985     if (x 
>= gtk_text_iter_get_chars_in_line (&iter
)) 
 988     return gtk_text_iter_get_offset(&iter
) + x
; 
 991 int wxTextCtrl::GetLineLength(long lineNo
) const 
 993     if (m_windowStyle 
& wxTE_MULTILINE
) 
 995         int last_line 
= gtk_text_buffer_get_line_count( m_buffer 
) - 1; 
 996         if (lineNo 
> last_line
) 
1000         gtk_text_buffer_get_iter_at_line(m_buffer
, &iter
, lineNo
); 
1001         // get_chars_in_line return includes paragraph delimiters, so need to subtract 1 IF it is not the last line 
1002         return gtk_text_iter_get_chars_in_line(&iter
) - ((lineNo 
== last_line
) ? 0 : 1); 
1006         wxString str 
= GetLineText (lineNo
); 
1007         return (int) str
.length(); 
1011 int wxTextCtrl::GetNumberOfLines() const 
1013     if ( m_windowStyle 
& wxTE_MULTILINE 
) 
1016         gtk_text_buffer_get_iter_at_offset( m_buffer
, &iter
, 0 ); 
1018         // move forward by one display line until the end is reached 
1020         while ( gtk_text_view_forward_display_line(GTK_TEXT_VIEW(m_text
), &iter
) ) 
1025         // If the last character in the text buffer is a newline, 
1026         // gtk_text_view_forward_display_line() will return false without that 
1027         // line being counted. Must add one manually in that case. 
1028         GtkTextIter lastCharIter
; 
1029         gtk_text_buffer_get_iter_at_offset
 
1033             gtk_text_buffer_get_char_count(m_buffer
) - 1 
1035         gchar lastChar 
= gtk_text_iter_get_char( &lastCharIter 
); 
1036         if ( lastChar 
== wxT('\n') ) 
1047 void wxTextCtrl::SetInsertionPoint( long pos 
) 
1049     wxCHECK_RET( m_text 
!= NULL
, wxT("invalid text ctrl") ); 
1051     if ( IsMultiLine() ) 
1054         gtk_text_buffer_get_iter_at_offset( m_buffer
, &iter
, pos 
); 
1055         gtk_text_buffer_place_cursor( m_buffer
, &iter 
); 
1056         gtk_text_view_scroll_mark_onscreen
 
1058             GTK_TEXT_VIEW(m_text
), 
1059             gtk_text_buffer_get_insert( m_buffer 
) 
1064         // FIXME: Is the editable's cursor really uptodate without double set_position in GTK2? 
1065         gtk_editable_set_position(GTK_EDITABLE(m_text
), int(pos
)); 
1069 void wxTextCtrl::SetInsertionPointEnd() 
1071     wxCHECK_RET( m_text 
!= NULL
, wxT("invalid text ctrl") ); 
1073     if (m_windowStyle 
& wxTE_MULTILINE
) 
1076         gtk_text_buffer_get_end_iter( m_buffer
, &end 
); 
1077         gtk_text_buffer_place_cursor( m_buffer
, &end 
); 
1081         gtk_editable_set_position( GTK_EDITABLE(m_text
), -1 ); 
1085 void wxTextCtrl::SetEditable( bool editable 
) 
1087     wxCHECK_RET( m_text 
!= NULL
, wxT("invalid text ctrl") ); 
1089     if (m_windowStyle 
& wxTE_MULTILINE
) 
1091         gtk_text_view_set_editable( GTK_TEXT_VIEW(m_text
), editable 
); 
1095         gtk_editable_set_editable( GTK_EDITABLE(m_text
), editable 
); 
1099 bool wxTextCtrl::Enable( bool enable 
) 
1101     if (!wxWindowBase::Enable(enable
)) 
1107     if (m_windowStyle 
& wxTE_MULTILINE
) 
1109         SetEditable( enable 
); 
1113         gtk_widget_set_sensitive( m_text
, enable 
); 
1119 // wxGTK-specific: called recursively by Enable, 
1120 // to give widgets an oppprtunity to correct their colours after they 
1121 // have been changed by Enable 
1122 void wxTextCtrl::OnParentEnable( bool enable 
) 
1124     // If we have a custom background colour, we use this colour in both 
1125     // disabled and enabled mode, or we end up with a different colour under the 
1127     wxColour oldColour 
= GetBackgroundColour(); 
1130         // Need to set twice or it'll optimize the useful stuff out 
1131         if (oldColour 
== * wxWHITE
) 
1132             SetBackgroundColour(*wxBLACK
); 
1134             SetBackgroundColour(*wxWHITE
); 
1135         SetBackgroundColour(oldColour
); 
1139 void wxTextCtrl::MarkDirty() 
1144 void wxTextCtrl::DiscardEdits() 
1149 // ---------------------------------------------------------------------------- 
1150 // max text length support 
1151 // ---------------------------------------------------------------------------- 
1153 bool wxTextCtrl::IgnoreTextUpdate() 
1155     if ( m_ignoreNextUpdate 
) 
1157         m_ignoreNextUpdate 
= false; 
1165 bool wxTextCtrl::MarkDirtyOnChange() 
1167     if ( m_dontMarkDirty 
) 
1169         m_dontMarkDirty 
= false; 
1177 void wxTextCtrl::SetMaxLength(unsigned long len
) 
1179     if ( !HasFlag(wxTE_MULTILINE
) ) 
1181         gtk_entry_set_max_length(GTK_ENTRY(m_text
), len
); 
1183         // there is a bug in GTK+ 1.2.x: "changed" signal is emitted even if 
1184         // we had tried to enter more text than allowed by max text length and 
1185         // the text wasn't really changed 
1187         // to detect this and generate TEXT_MAXLEN event instead of 
1188         // TEXT_CHANGED one in this case we also catch "insert_text" signal 
1190         // when max len is set to 0 we disconnect our handler as it means that 
1191         // we shouldn't check anything any more 
1194             g_signal_connect (m_text
, "insert_text", 
1195                               G_CALLBACK (gtk_insert_text_callback
), this); 
1199             g_signal_handlers_disconnect_by_func (m_text
, 
1200                     (gpointer
) gtk_insert_text_callback
, this); 
1205 void wxTextCtrl::SetSelection( long from
, long to 
) 
1207     wxCHECK_RET( m_text 
!= NULL
, wxT("invalid text ctrl") ); 
1209     if (from 
== -1 && to 
== -1) 
1212         to 
= GetValue().length(); 
1215     if (m_windowStyle 
& wxTE_MULTILINE
) 
1217         GtkTextIter fromi
, toi
; 
1218         gtk_text_buffer_get_iter_at_offset( m_buffer
, &fromi
, from 
); 
1219         gtk_text_buffer_get_iter_at_offset( m_buffer
, &toi
, to 
); 
1221         gtk_text_buffer_place_cursor( m_buffer
, &toi 
); 
1222         gtk_text_buffer_move_mark_by_name( m_buffer
, "selection_bound", &fromi 
); 
1226         gtk_editable_select_region( GTK_EDITABLE(m_text
), (gint
)from
, (gint
)to 
); 
1230 void wxTextCtrl::ShowPosition( long pos 
) 
1232     if (m_windowStyle 
& wxTE_MULTILINE
) 
1235         gtk_text_buffer_get_start_iter( m_buffer
, &iter 
); 
1236         gtk_text_iter_set_offset( &iter
, pos 
); 
1237         GtkTextMark 
*mark 
= gtk_text_buffer_create_mark( m_buffer
, NULL
, &iter
, TRUE 
); 
1238         gtk_text_view_scroll_to_mark( GTK_TEXT_VIEW(m_text
), mark
, 0.0, FALSE
, 0.0, 0.0 ); 
1242 wxTextCtrlHitTestResult
 
1243 wxTextCtrl::HitTest(const wxPoint
& pt
, long *pos
) const 
1245     if ( !IsMultiLine() ) 
1248         return wxTE_HT_UNKNOWN
; 
1252     gtk_text_view_window_to_buffer_coords
 
1254         GTK_TEXT_VIEW(m_text
), 
1255         GTK_TEXT_WINDOW_TEXT
, 
1261     gtk_text_view_get_iter_at_location(GTK_TEXT_VIEW(m_text
), &iter
, x
, y
); 
1263         *pos 
= gtk_text_iter_get_offset(&iter
); 
1265     return wxTE_HT_ON_TEXT
; 
1268 long wxTextCtrl::GetInsertionPoint() const 
1270     wxCHECK_MSG( m_text 
!= NULL
, 0, wxT("invalid text ctrl") ); 
1272     if (m_windowStyle 
& wxTE_MULTILINE
) 
1274         // There is no direct accessor for the cursor, but 
1275         // internally, the cursor is the "mark" called 
1276         // "insert" in the text view's btree structure. 
1278         GtkTextMark 
*mark 
= gtk_text_buffer_get_insert( m_buffer 
); 
1280         gtk_text_buffer_get_iter_at_mark( m_buffer
, &cursor
, mark 
); 
1282         return gtk_text_iter_get_offset( &cursor 
); 
1286         return (long) gtk_editable_get_position(GTK_EDITABLE(m_text
)); 
1290 wxTextPos 
wxTextCtrl::GetLastPosition() const 
1292     wxCHECK_MSG( m_text 
!= NULL
, 0, wxT("invalid text ctrl") ); 
1296     if (m_windowStyle 
& wxTE_MULTILINE
) 
1299         gtk_text_buffer_get_end_iter( m_buffer
, &end 
); 
1301         pos 
= gtk_text_iter_get_offset( &end 
); 
1305         pos 
= GTK_ENTRY(m_text
)->text_length
; 
1311 void wxTextCtrl::Remove( long from
, long to 
) 
1313     wxCHECK_RET( m_text 
!= NULL
, wxT("invalid text ctrl") ); 
1315     if (m_windowStyle 
& wxTE_MULTILINE
) 
1317         GtkTextIter fromi
, toi
; 
1318         gtk_text_buffer_get_iter_at_offset( m_buffer
, &fromi
, from 
); 
1319         gtk_text_buffer_get_iter_at_offset( m_buffer
, &toi
, to 
); 
1321         gtk_text_buffer_delete( m_buffer
, &fromi
, &toi 
); 
1324         gtk_editable_delete_text( GTK_EDITABLE(m_text
), (gint
)from
, (gint
)to 
); 
1327 void wxTextCtrl::Replace( long from
, long to
, const wxString 
&value 
) 
1329     wxCHECK_RET( m_text 
!= NULL
, wxT("invalid text ctrl") ); 
1335         SetInsertionPoint( from 
); 
1340 void wxTextCtrl::Cut() 
1342     wxCHECK_RET( m_text 
!= NULL
, wxT("invalid text ctrl") ); 
1344     if (m_windowStyle 
& wxTE_MULTILINE
) 
1345         g_signal_emit_by_name (m_text
, "cut-clipboard"); 
1347         gtk_editable_cut_clipboard(GTK_EDITABLE(m_text
)); 
1350 void wxTextCtrl::Copy() 
1352     wxCHECK_RET( m_text 
!= NULL
, wxT("invalid text ctrl") ); 
1354     if (m_windowStyle 
& wxTE_MULTILINE
) 
1355         g_signal_emit_by_name (m_text
, "copy-clipboard"); 
1357         gtk_editable_copy_clipboard(GTK_EDITABLE(m_text
)); 
1360 void wxTextCtrl::Paste() 
1362     wxCHECK_RET( m_text 
!= NULL
, wxT("invalid text ctrl") ); 
1364     if (m_windowStyle 
& wxTE_MULTILINE
) 
1365         g_signal_emit_by_name (m_text
, "paste-clipboard"); 
1367         gtk_editable_paste_clipboard(GTK_EDITABLE(m_text
)); 
1371 void wxTextCtrl::Undo() 
1374     wxFAIL_MSG( wxT("wxTextCtrl::Undo not implemented") ); 
1377 void wxTextCtrl::Redo() 
1380     wxFAIL_MSG( wxT("wxTextCtrl::Redo not implemented") ); 
1383 bool wxTextCtrl::CanUndo() const 
1386     //wxFAIL_MSG( wxT("wxTextCtrl::CanUndo not implemented") ); 
1390 bool wxTextCtrl::CanRedo() const 
1393     //wxFAIL_MSG( wxT("wxTextCtrl::CanRedo not implemented") ); 
1397 // If the return values from and to are the same, there is no 
1399 void wxTextCtrl::GetSelection(long* fromOut
, long* toOut
) const 
1401     wxCHECK_RET( m_text 
!= NULL
, wxT("invalid text ctrl") ); 
1405     bool haveSelection 
= false; 
1407      if (m_windowStyle 
& wxTE_MULTILINE
) 
1409          GtkTextIter ifrom
, ito
; 
1410          if ( gtk_text_buffer_get_selection_bounds(m_buffer
, &ifrom
, &ito
) ) 
1412              haveSelection 
= true; 
1413              from 
= gtk_text_iter_get_offset(&ifrom
); 
1414              to 
= gtk_text_iter_get_offset(&ito
); 
1417      else  // not multi-line 
1419          if ( gtk_editable_get_selection_bounds( GTK_EDITABLE(m_text
), 
1422              haveSelection 
= true; 
1426      if (! haveSelection 
) 
1427           from 
= to 
= GetInsertionPoint(); 
1431          // exchange them to be compatible with wxMSW 
1444 bool wxTextCtrl::IsEditable() const 
1446     wxCHECK_MSG( m_text 
!= NULL
, false, wxT("invalid text ctrl") ); 
1448     if (m_windowStyle 
& wxTE_MULTILINE
) 
1450         return gtk_text_view_get_editable(GTK_TEXT_VIEW(m_text
)); 
1454         return gtk_editable_get_editable(GTK_EDITABLE(m_text
)); 
1458 bool wxTextCtrl::IsModified() const 
1463 void wxTextCtrl::Clear() 
1465     SetValue( wxEmptyString 
); 
1468 void wxTextCtrl::OnChar( wxKeyEvent 
&key_event 
) 
1470     wxCHECK_RET( m_text 
!= NULL
, wxT("invalid text ctrl") ); 
1472     if ((key_event
.GetKeyCode() == WXK_RETURN
) && (m_windowStyle 
& wxTE_PROCESS_ENTER
)) 
1474         wxCommandEvent 
event(wxEVT_COMMAND_TEXT_ENTER
, m_windowId
); 
1475         event
.SetEventObject(this); 
1476         event
.SetString(GetValue()); 
1477         if (GetEventHandler()->ProcessEvent(event
)) return; 
1480     if ((key_event
.GetKeyCode() == WXK_RETURN
) && !(m_windowStyle 
& wxTE_MULTILINE
)) 
1482         // This will invoke the dialog default action, such 
1483         // as the clicking the default button. 
1485         wxWindow 
*top_frame 
= m_parent
; 
1486         while (top_frame
->GetParent() && !(top_frame
->IsTopLevel())) 
1487             top_frame 
= top_frame
->GetParent(); 
1489         if (top_frame 
&& GTK_IS_WINDOW(top_frame
->m_widget
)) 
1491             GtkWindow 
*window 
= GTK_WINDOW(top_frame
->m_widget
); 
1493             if (window
->default_widget
) 
1495                 gtk_widget_activate (window
->default_widget
); 
1504 GtkWidget
* wxTextCtrl::GetConnectWidget() 
1506     return GTK_WIDGET(m_text
); 
1509 bool wxTextCtrl::IsOwnGtkWindow( GdkWindow 
*window 
) 
1511     if (m_windowStyle 
& wxTE_MULTILINE
) 
1513         return window 
== gtk_text_view_get_window( GTK_TEXT_VIEW( m_text 
), GTK_TEXT_WINDOW_TEXT 
);  // pure guesswork 
1517         return (window 
== GTK_ENTRY(m_text
)->text_area
); 
1521 // the font will change for subsequent text insertiongs 
1522 bool wxTextCtrl::SetFont( const wxFont 
&font 
) 
1524     wxCHECK_MSG( m_text 
!= NULL
, false, wxT("invalid text ctrl") ); 
1526     if ( !wxTextCtrlBase::SetFont(font
) ) 
1528         // font didn't change, nothing to do 
1532     if ( m_windowStyle 
& wxTE_MULTILINE 
) 
1534         SetUpdateFont(true); 
1536         m_defaultStyle
.SetFont(font
); 
1538         ChangeFontGlobally(); 
1544 void wxTextCtrl::ChangeFontGlobally() 
1546     // this method is very inefficient and hence should be called as rarely as 
1549     // TODO: it can be implemented much more efficiently for GTK2 
1550     wxASSERT_MSG( (m_windowStyle 
& wxTE_MULTILINE
), 
1551                   _T("shouldn't be called for single line controls") ); 
1553     wxString value 
= GetValue(); 
1554     if ( !value
.empty() ) 
1556         SetUpdateFont(false); 
1563 bool wxTextCtrl::SetForegroundColour(const wxColour
& colour
) 
1565     if ( !wxControl::SetForegroundColour(colour
) ) 
1568     // update default fg colour too 
1569     m_defaultStyle
.SetTextColour(colour
); 
1574 bool wxTextCtrl::SetBackgroundColour( const wxColour 
&colour 
) 
1576     wxCHECK_MSG( m_text 
!= NULL
, false, wxT("invalid text ctrl") ); 
1578     if ( !wxControl::SetBackgroundColour( colour 
) ) 
1581     if (!m_backgroundColour
.Ok()) 
1584     // change active background color too 
1585     m_defaultStyle
.SetBackgroundColour( colour 
); 
1590 bool wxTextCtrl::SetStyle( long start
, long end
, const wxTextAttr
& style 
) 
1592     if ( m_windowStyle 
& wxTE_MULTILINE 
) 
1594         if ( style
.IsDefault() ) 
1600         gint l 
= gtk_text_buffer_get_char_count( m_buffer 
); 
1602         wxCHECK_MSG( start 
>= 0 && end 
<= l
, false, 
1603                      _T("invalid range in wxTextCtrl::SetStyle") ); 
1605         GtkTextIter starti
, endi
; 
1606         gtk_text_buffer_get_iter_at_offset( m_buffer
, &starti
, start 
); 
1607         gtk_text_buffer_get_iter_at_offset( m_buffer
, &endi
, end 
); 
1609         // use the attributes from style which are set in it and fall back 
1610         // first to the default style and then to the text control default 
1611         // colours for the others 
1612         wxTextAttr attr 
= wxTextAttr::Combine(style
, m_defaultStyle
, this); 
1614         wxGtkTextApplyTagsFromAttr( m_buffer
, attr
, &starti
, &endi 
); 
1620     // cannot do this for GTK+'s Entry widget 
1624 void wxTextCtrl::DoApplyWidgetStyle(GtkRcStyle 
*style
) 
1626     gtk_widget_modify_style(m_text
, style
); 
1629 void wxTextCtrl::OnCut(wxCommandEvent
& WXUNUSED(event
)) 
1634 void wxTextCtrl::OnCopy(wxCommandEvent
& WXUNUSED(event
)) 
1639 void wxTextCtrl::OnPaste(wxCommandEvent
& WXUNUSED(event
)) 
1644 void wxTextCtrl::OnUndo(wxCommandEvent
& WXUNUSED(event
)) 
1649 void wxTextCtrl::OnRedo(wxCommandEvent
& WXUNUSED(event
)) 
1654 void wxTextCtrl::OnUpdateCut(wxUpdateUIEvent
& event
) 
1656     event
.Enable( CanCut() ); 
1659 void wxTextCtrl::OnUpdateCopy(wxUpdateUIEvent
& event
) 
1661     event
.Enable( CanCopy() ); 
1664 void wxTextCtrl::OnUpdatePaste(wxUpdateUIEvent
& event
) 
1666     event
.Enable( CanPaste() ); 
1669 void wxTextCtrl::OnUpdateUndo(wxUpdateUIEvent
& event
) 
1671     event
.Enable( CanUndo() ); 
1674 void wxTextCtrl::OnUpdateRedo(wxUpdateUIEvent
& event
) 
1676     event
.Enable( CanRedo() ); 
1679 void wxTextCtrl::OnInternalIdle() 
1681     if (g_delayedFocus 
== this) 
1683         if (GTK_WIDGET_REALIZED(m_widget
)) 
1685             gtk_widget_grab_focus( m_widget 
); 
1686             g_delayedFocus 
= NULL
; 
1690     if (wxUpdateUIEvent::CanUpdate(this)) 
1691         UpdateWindowUI(wxUPDATE_UI_FROMIDLE
); 
1694 wxSize 
wxTextCtrl::DoGetBestSize() const 
1696     // FIXME should be different for multi-line controls... 
1697     wxSize 
ret( wxControl::DoGetBestSize() ); 
1698     wxSize 
best(80, ret
.y
); 
1699     CacheBestSize(best
); 
1703 // ---------------------------------------------------------------------------- 
1705 // ---------------------------------------------------------------------------- 
1707 void wxTextCtrl::Freeze() 
1709     if ( HasFlag(wxTE_MULTILINE
) ) 
1711         if ( !m_frozenness
++ ) 
1713             // freeze textview updates and remove buffer 
1714             g_signal_connect (m_text
, "expose_event", 
1715                               G_CALLBACK (gtk_text_exposed_callback
), this); 
1716             g_signal_connect (m_widget
, "expose_event", 
1717                               G_CALLBACK (gtk_text_exposed_callback
), this); 
1718             gtk_widget_set_sensitive(m_widget
, false); 
1719             g_object_ref(m_buffer
); 
1720             gtk_text_view_set_buffer(GTK_TEXT_VIEW(m_text
), gtk_text_buffer_new(NULL
)); 
1725 void wxTextCtrl::Thaw() 
1727     if ( HasFlag(wxTE_MULTILINE
) ) 
1729         wxASSERT_MSG( m_frozenness 
> 0, _T("Thaw() without matching Freeze()") ); 
1731         if ( !--m_frozenness 
) 
1733             // Reattach buffer and thaw textview updates 
1734             gtk_text_view_set_buffer(GTK_TEXT_VIEW(m_text
), m_buffer
); 
1735             g_object_unref(m_buffer
); 
1736             gtk_widget_set_sensitive(m_widget
, true); 
1737             g_signal_handlers_disconnect_by_func (m_widget
, 
1738                     (gpointer
) gtk_text_exposed_callback
, this); 
1739             g_signal_handlers_disconnect_by_func (m_text
, 
1740                     (gpointer
) gtk_text_exposed_callback
, this); 
1745 // ---------------------------------------------------------------------------- 
1746 // wxTextUrlEvent passing if style & wxTE_AUTO_URL 
1747 // ---------------------------------------------------------------------------- 
1749 // FIXME: when dragging on a link the sample gets an "Unknown event". 
1750 // This might be an excessive event from us or a buggy wxMouseEvent::Moving() or 
1751 // a buggy sample, or something else 
1752 void wxTextCtrl::OnUrlMouseEvent(wxMouseEvent
& event
) 
1755     if(!(m_windowStyle 
& wxTE_AUTO_URL
)) 
1759     GtkTextIter start
, end
; 
1760     GtkTextTag 
*tag 
= gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(m_buffer
), 
1763     gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(m_text
), GTK_TEXT_WINDOW_WIDGET
, 
1764                                           event
.GetX(), event
.GetY(), &x
, &y
); 
1766     gtk_text_view_get_iter_at_location(GTK_TEXT_VIEW(m_text
), &end
, x
, y
); 
1767     if (!gtk_text_iter_has_tag(&end
, tag
)) 
1769         gdk_window_set_cursor(gtk_text_view_get_window(GTK_TEXT_VIEW(m_text
), 
1770                               GTK_TEXT_WINDOW_TEXT
), m_gdkXTermCursor
); 
1774     gdk_window_set_cursor(gtk_text_view_get_window(GTK_TEXT_VIEW(m_text
), 
1775                           GTK_TEXT_WINDOW_TEXT
), m_gdkHandCursor
); 
1778     if(!gtk_text_iter_begins_tag(&start
, tag
)) 
1779         gtk_text_iter_backward_to_tag_toggle(&start
, tag
); 
1780     if(!gtk_text_iter_ends_tag(&end
, tag
)) 
1781         gtk_text_iter_forward_to_tag_toggle(&end
, tag
); 
1783     // Native context menu is probably not desired on an URL. 
1784     // Consider making this dependant on ProcessEvent(wxTextUrlEvent) return value 
1785     if(event
.GetEventType() == wxEVT_RIGHT_DOWN
) 
1788     wxTextUrlEvent 
url_event(m_windowId
, event
, 
1789                              gtk_text_iter_get_offset(&start
), 
1790                              gtk_text_iter_get_offset(&end
)); 
1792     InitCommandEvent(url_event
); 
1793     // Is that a good idea? Seems not (pleasure with gtk_text_view_start_selection_drag) 
1794     //event.Skip(!GetEventHandler()->ProcessEvent(url_event)); 
1795     GetEventHandler()->ProcessEvent(url_event
); 
1800 wxTextCtrl::GetClassDefaultAttributes(wxWindowVariant 
WXUNUSED(variant
)) 
1802     return GetDefaultAttributesFromGTKWidget(gtk_entry_new
, true);