1 /////////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/gtk/textentry.cpp 
   3 // Purpose:     wxTextEntry implementation for wxGTK 
   4 // Author:      Vadim Zeitlin 
   7 // Copyright:   (c) 2007 Vadim Zeitlin <vadim@wxwindows.org> 
   8 // Licence:     wxWindows licence 
   9 /////////////////////////////////////////////////////////////////////////////// 
  11 // ============================================================================ 
  13 // ============================================================================ 
  15 // ---------------------------------------------------------------------------- 
  17 // ---------------------------------------------------------------------------- 
  19 // for compilers that support precompilation, includes "wx.h". 
  20 #include "wx/wxprec.h" 
  26 #if wxUSE_TEXTCTRL || wxUSE_COMBOBOX 
  29     #include "wx/window.h" 
  30     #include "wx/textctrl.h" 
  33 #include "wx/textentry.h" 
  35 #include "wx/gtk/private.h" 
  37 // ============================================================================ 
  38 // signal handlers implementation 
  39 // ============================================================================ 
  44 // "insert_text" handler for GtkEntry 
  46 wx_gtk_insert_text_callback(GtkEditable 
*editable
, 
  47                             const gchar 
* WXUNUSED(new_text
), 
  48                             gint 
WXUNUSED(new_text_length
), 
  49                             gint 
* WXUNUSED(position
), 
  52     // we should only be called if we have a max len limit at all 
  53     GtkEntry 
*entry 
= GTK_ENTRY (editable
); 
  55     wxCHECK_RET( entry
->text_max_length
, wxT("shouldn't be called") ); 
  57     // check that we don't overflow the max length limit 
  59     // FIXME: this doesn't work when we paste a string which is going to be 
  61     if ( entry
->text_length 
== entry
->text_max_length 
) 
  63         // we don't need to run the base class version at all 
  64         g_signal_stop_emission_by_name (editable
, "insert_text"); 
  66         text
->SendMaxLenEvent(); 
  72 // ============================================================================ 
  73 // wxTextEntry implementation 
  74 // ============================================================================ 
  76 // ---------------------------------------------------------------------------- 
  78 // ---------------------------------------------------------------------------- 
  80 void wxTextEntry::WriteText(const wxString
& value
) 
  82     GtkEditable 
* const edit 
= GetEditable(); 
  84     // remove the selection if there is one and suppress the text change event 
  85     // generated by this: we only want to generate one event for this change, 
  88         EventsSuppressor 
noevents(this); 
  89         gtk_editable_delete_selection(edit
); 
  92     // insert new text at the cursor position 
  93     gint len 
= gtk_editable_get_position(edit
); 
  94     gtk_editable_insert_text
 
  97         wxGTK_CONV_FONT(value
, GetEditableWindow()->GetFont()), 
  98         -1,     // text: length: compute it using strlen() 
  99         &len    
// will be updated to position after the text end 
 102     // and move cursor to the end of new text 
 103     gtk_editable_set_position(edit
, len
); 
 106 wxString 
wxTextEntry::DoGetValue() const 
 108     const wxGtkString 
value(gtk_editable_get_chars(GetEditable(), 0, -1)); 
 110     return wxGTK_CONV_BACK_FONT(value
, 
 111             const_cast<wxTextEntry 
*>(this)->GetEditableWindow()->GetFont()); 
 114 void wxTextEntry::Remove(long from
, long to
) 
 116     gtk_editable_delete_text(GetEditable(), from
, to
); 
 119 // ---------------------------------------------------------------------------- 
 120 // clipboard operations 
 121 // ---------------------------------------------------------------------------- 
 123 void wxTextEntry::Copy() 
 125     gtk_editable_copy_clipboard(GetEditable()); 
 128 void wxTextEntry::Cut() 
 130     gtk_editable_cut_clipboard(GetEditable()); 
 133 void wxTextEntry::Paste() 
 135     gtk_editable_paste_clipboard(GetEditable()); 
 138 // ---------------------------------------------------------------------------- 
 140 // ---------------------------------------------------------------------------- 
 142 void wxTextEntry::Undo() 
 144     // TODO: not implemented 
 147 void wxTextEntry::Redo() 
 149     // TODO: not implemented 
 152 bool wxTextEntry::CanUndo() const 
 157 bool wxTextEntry::CanRedo() const 
 162 // ---------------------------------------------------------------------------- 
 164 // ---------------------------------------------------------------------------- 
 166 void wxTextEntry::SetInsertionPoint(long pos
) 
 168     gtk_editable_set_position(GetEditable(), pos
); 
 171 long wxTextEntry::GetInsertionPoint() const 
 173     return gtk_editable_get_position(GetEditable()); 
 176 long wxTextEntry::GetLastPosition() const 
 178     // this can't be implemented for arbitrary GtkEditable so only do it for 
 180     GtkEntry 
* const entry 
= GTK_ENTRY(GetEditable()); 
 182     return entry 
? entry
->text_length 
: - 1; 
 185 // ---------------------------------------------------------------------------- 
 187 // ---------------------------------------------------------------------------- 
 189 void wxTextEntry::SetSelection(long from
, long to
) 
 191     // in wx convention, (-1, -1) means the entire range but GTK+ translates -1 
 192     // (or any negative number for that matter) into last position so we need 
 193     // to translate manually 
 194     if ( from 
== -1 && to 
== -1 ) 
 197     // for compatibility with MSW, exchange from and to parameters so that the 
 198     // insertion point is set to the start of the selection and not its end as 
 199     // GTK+ does by default 
 200     gtk_editable_select_region(GetEditable(), to
, from
); 
 203 void wxTextEntry::GetSelection(long *from
, long *to
) const 
 206     if ( gtk_editable_get_selection_bounds(GetEditable(), &start
, &end
) ) 
 208         // the output must always be in order, although in GTK+ it isn't 
 218         // for compatibility with MSW return the empty selection at cursor 
 220         end 
= GetInsertionPoint(); 
 230 // ---------------------------------------------------------------------------- 
 232 // ---------------------------------------------------------------------------- 
 234 bool wxTextEntry::AutoComplete(const wxArrayString
& choices
) 
 236     GtkEntry 
* const entry 
= GTK_ENTRY(GetEditable()); 
 237     wxCHECK_MSG(entry
, false, "auto completion doesn't work with this control"); 
 239     GtkListStore 
* const store 
= gtk_list_store_new(1, G_TYPE_STRING
); 
 242     for ( wxArrayString::const_iterator i 
= choices
.begin(); 
 246         gtk_list_store_append(store
, &iter
); 
 247         gtk_list_store_set(store
, &iter
, 
 248                            0, (const gchar 
*)i
->utf8_str(), 
 252     GtkEntryCompletion 
* const completion 
= gtk_entry_completion_new(); 
 253     gtk_entry_completion_set_model(completion
, GTK_TREE_MODEL(store
)); 
 254     gtk_entry_completion_set_text_column(completion
, 0); 
 255     gtk_entry_set_completion(entry
, completion
); 
 256     g_object_unref(completion
); 
 260 // ---------------------------------------------------------------------------- 
 262 // ---------------------------------------------------------------------------- 
 264 bool wxTextEntry::IsEditable() const 
 266     return gtk_editable_get_editable(GetEditable()); 
 269 void wxTextEntry::SetEditable(bool editable
) 
 271     gtk_editable_set_editable(GetEditable(), editable
); 
 274 // ---------------------------------------------------------------------------- 
 276 // ---------------------------------------------------------------------------- 
 278 void wxTextEntry::SetMaxLength(unsigned long len
) 
 280     GtkEntry 
* const entry 
= GTK_ENTRY(GetEditable()); 
 284     gtk_entry_set_max_length(entry
, len
); 
 286     // there is a bug in GTK+ 1.2.x: "changed" signal is emitted even if we had 
 287     // tried to enter more text than allowed by max text length and the text 
 288     // wasn't really changed 
 290     // to detect this and generate TEXT_MAXLEN event instead of TEXT_CHANGED 
 291     // one in this case we also catch "insert_text" signal 
 293     // when max len is set to 0 we disconnect our handler as it means that we 
 294     // shouldn't check anything any more 
 301             G_CALLBACK(wx_gtk_insert_text_callback
), 
 305     else // no max length 
 307         g_signal_handlers_disconnect_by_func
 
 310             (gpointer
)wx_gtk_insert_text_callback
, 
 316 void wxTextEntry::SendMaxLenEvent() 
 318     // remember that the next changed signal is to be ignored to avoid 
 319     // generating a dummy wxEVT_COMMAND_TEXT_UPDATED event 
 320     //IgnoreNextTextUpdate(); 
 322     wxWindow 
* const win 
= GetEditableWindow(); 
 323     wxCommandEvent 
event(wxEVT_COMMAND_TEXT_MAXLEN
, win
->GetId()); 
 324     event
.SetEventObject(win
); 
 325     event
.SetString(GetValue()); 
 326     win
->HandleWindowEvent(event
); 
 329 // ---------------------------------------------------------------------------- 
 331 // ---------------------------------------------------------------------------- 
 333 bool wxTextEntry::DoSetMargins(const wxPoint
& margins
) 
 335 #if GTK_CHECK_VERSION(2,10,0) 
 336     GtkEntry
* entry 
= GetEntry(); 
 341     const GtkBorder
* oldBorder 
= gtk_entry_get_inner_border(entry
); 
 342     GtkBorder
* newBorder
; 
 346         newBorder 
= gtk_border_copy(oldBorder
); 
 350     #if GTK_CHECK_VERSION(2,14,0) 
 351         newBorder 
= gtk_border_new(); 
 353         newBorder 
= g_slice_new0(GtkBorder
); 
 355         // Use some reasonable defaults for initial margins 
 357         newBorder
->right 
= 2; 
 359         // These numbers seem to let the text remain vertically centered 
 360         // in common use scenarios when margins.y == -1. 
 362         newBorder
->bottom 
= 3; 
 365     if ( margins
.x 
!= -1 ) 
 366         newBorder
->left 
= (gint
) margins
.x
; 
 368     if ( margins
.y 
!= -1 ) 
 369         newBorder
->top 
= (gint
) margins
.y
; 
 371     gtk_entry_set_inner_border(entry
, newBorder
); 
 373 #if GTK_CHECK_VERSION(2,14,0) 
 374     gtk_border_free(newBorder
); 
 376     g_slice_free(GtkBorder
, newBorder
); 
 381     wxUnusedVar(margins
); 
 386 wxPoint 
wxTextEntry::DoGetMargins() const 
 388 #if GTK_CHECK_VERSION(2,10,0) 
 389     GtkEntry
* entry 
= GetEntry(); 
 392         return wxPoint(-1, -1); 
 394     const GtkBorder
* border 
= gtk_entry_get_inner_border(entry
); 
 397         return wxPoint(-1, -1); 
 399     return wxPoint((wxCoord
) border
->left
, (wxCoord
) border
->top
); 
 401     return wxPoint(-1, -1); 
 405 #endif // wxUSE_TEXTCTRL || wxUSE_COMBOBOX