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/textentry.h"
30 #include "wx/window.h"
31 #include "wx/textctrl.h"
35 #include "wx/gtk/private.h"
36 #include "wx/gtk/private/gtk2-compat.h"
38 // ============================================================================
39 // signal handlers implementation
40 // ============================================================================
42 // "insert_text" handler for GtkEntry
45 wx_gtk_insert_text_callback(GtkEditable
*editable
,
46 const gchar
* new_text
,
47 gint
WXUNUSED(new_text_length
),
48 gint
* WXUNUSED(position
),
51 GtkEntry
*entry
= GTK_ENTRY (editable
);
53 #if GTK_CHECK_VERSION(3,0,0) || defined(GSEAL_ENABLE)
54 const int text_max_length
= gtk_entry_buffer_get_max_length(gtk_entry_get_buffer(entry
));
56 const int text_max_length
= entry
->text_max_length
;
59 // we should only be called if we have a max len limit at all
60 wxCHECK_RET(text_max_length
, "shouldn't be called");
62 // check that we don't overflow the max length limit
64 const int text_length
= gtk_entry_get_text_length(entry
);
66 // We can't use new_text_length as it is in bytes while we want to count
67 // characters (in first approximation, anyhow...).
68 if ( text_length
+ g_utf8_strlen(new_text
, -1) > text_max_length
)
70 // Prevent the new text from being inserted.
71 g_signal_stop_emission_by_name (editable
, "insert_text");
73 // Currently we don't insert anything at all, but it would be better to
74 // insert as many characters as would fit into the text control and
75 // only discard the rest.
77 // Notify the user code about overflow.
78 text
->SendMaxLenEvent();
82 //-----------------------------------------------------------------------------
83 // clipboard events: "copy-clipboard", "cut-clipboard", "paste-clipboard"
84 //-----------------------------------------------------------------------------
86 // common part of the event handlers below
88 DoHandleClipboardCallback( GtkWidget
*widget
,
90 wxEventType eventType
,
91 const gchar
* signal_name
)
93 wxClipboardTextEvent
event( eventType
, win
->GetId() );
94 event
.SetEventObject( win
);
95 if ( win
->HandleWindowEvent( event
) )
97 // don't let the default processing to take place if we did something
98 // ourselves in the event handler
99 g_signal_stop_emission_by_name (widget
, signal_name
);
107 wx_gtk_copy_clipboard_callback( GtkWidget
*widget
, wxWindow
*win
)
109 DoHandleClipboardCallback(
110 widget
, win
, wxEVT_COMMAND_TEXT_COPY
, "copy-clipboard" );
114 wx_gtk_cut_clipboard_callback( GtkWidget
*widget
, wxWindow
*win
)
116 DoHandleClipboardCallback(
117 widget
, win
, wxEVT_COMMAND_TEXT_CUT
, "cut-clipboard" );
121 wx_gtk_paste_clipboard_callback( GtkWidget
*widget
, wxWindow
*win
)
123 DoHandleClipboardCallback(
124 widget
, win
, wxEVT_COMMAND_TEXT_PASTE
, "paste-clipboard" );
129 // ============================================================================
130 // wxTextEntry implementation
131 // ============================================================================
133 // ----------------------------------------------------------------------------
135 // ----------------------------------------------------------------------------
137 void wxTextEntry::WriteText(const wxString
& value
)
139 GtkEditable
* const edit
= GetEditable();
141 // remove the selection if there is one and suppress the text change event
142 // generated by this: we only want to generate one event for this change,
145 EventsSuppressor
noevents(this);
146 gtk_editable_delete_selection(edit
);
149 // insert new text at the cursor position
150 gint len
= gtk_editable_get_position(edit
);
151 gtk_editable_insert_text
154 wxGTK_CONV_FONT(value
, GetEditableWindow()->GetFont()),
155 -1, // text: length: compute it using strlen()
156 &len
// will be updated to position after the text end
159 // and move cursor to the end of new text
160 gtk_editable_set_position(edit
, len
);
163 void wxTextEntry::DoSetValue(const wxString
& value
, int flags
)
165 if (value
!= DoGetValue())
167 // use Remove() rather than SelectAll() to avoid unnecessary clipboard
168 // operations, and prevent triggering an apparent bug in GTK which
169 // causes the the subsequent WriteText() to append rather than overwrite
171 EventsSuppressor
noevents(this);
174 EventsSuppressor
noeventsIf(this, !(flags
& SetValue_SendEvent
));
177 else if (flags
& SetValue_SendEvent
)
178 SendTextUpdatedEvent(GetEditableWindow());
180 SetInsertionPoint(0);
183 wxString
wxTextEntry::DoGetValue() const
185 const wxGtkString
value(gtk_editable_get_chars(GetEditable(), 0, -1));
187 return wxGTK_CONV_BACK_FONT(value
,
188 const_cast<wxTextEntry
*>(this)->GetEditableWindow()->GetFont());
191 void wxTextEntry::Remove(long from
, long to
)
193 gtk_editable_delete_text(GetEditable(), from
, to
);
196 // ----------------------------------------------------------------------------
197 // clipboard operations
198 // ----------------------------------------------------------------------------
200 void wxTextEntry::GTKConnectClipboardSignals(GtkWidget
* entry
)
202 g_signal_connect(entry
, "copy-clipboard",
203 G_CALLBACK (wx_gtk_copy_clipboard_callback
),
204 GetEditableWindow());
205 g_signal_connect(entry
, "cut-clipboard",
206 G_CALLBACK (wx_gtk_cut_clipboard_callback
),
207 GetEditableWindow());
208 g_signal_connect(entry
, "paste-clipboard",
209 G_CALLBACK (wx_gtk_paste_clipboard_callback
),
210 GetEditableWindow());
213 void wxTextEntry::Copy()
215 gtk_editable_copy_clipboard(GetEditable());
218 void wxTextEntry::Cut()
220 gtk_editable_cut_clipboard(GetEditable());
223 void wxTextEntry::Paste()
225 gtk_editable_paste_clipboard(GetEditable());
228 // ----------------------------------------------------------------------------
230 // ----------------------------------------------------------------------------
232 void wxTextEntry::Undo()
234 // TODO: not implemented
237 void wxTextEntry::Redo()
239 // TODO: not implemented
242 bool wxTextEntry::CanUndo() const
247 bool wxTextEntry::CanRedo() const
252 // ----------------------------------------------------------------------------
254 // ----------------------------------------------------------------------------
256 void wxTextEntry::SetInsertionPoint(long pos
)
258 gtk_editable_set_position(GetEditable(), pos
);
261 long wxTextEntry::GetInsertionPoint() const
263 return gtk_editable_get_position(GetEditable());
266 long wxTextEntry::GetLastPosition() const
268 // this can't be implemented for arbitrary GtkEditable so only do it for
271 GtkEntry
* entry
= (GtkEntry
*)GetEditable();
272 if (GTK_IS_ENTRY(entry
))
273 pos
= gtk_entry_get_text_length(entry
);
278 // ----------------------------------------------------------------------------
280 // ----------------------------------------------------------------------------
282 void wxTextEntry::SetSelection(long from
, long to
)
284 // in wx convention, (-1, -1) means the entire range but GTK+ translates -1
285 // (or any negative number for that matter) into last position so we need
286 // to translate manually
287 if ( from
== -1 && to
== -1 )
290 // for compatibility with MSW, exchange from and to parameters so that the
291 // insertion point is set to the start of the selection and not its end as
292 // GTK+ does by default
293 gtk_editable_select_region(GetEditable(), to
, from
);
296 // avoid reported problem with RHEL 5 GTK+ 2.10 where selection is reset by
297 // a clipboard callback, see #13277
298 if (gtk_check_version(2,12,0))
300 GtkEntry
* entry
= GTK_ENTRY(GetEditable());
302 to
= entry
->text_length
;
303 entry
->selection_bound
= to
;
308 void wxTextEntry::GetSelection(long *from
, long *to
) const
311 if ( gtk_editable_get_selection_bounds(GetEditable(), &start
, &end
) )
313 // the output must always be in order, although in GTK+ it isn't
323 // for compatibility with MSW return the empty selection at cursor
325 end
= GetInsertionPoint();
335 // ----------------------------------------------------------------------------
337 // ----------------------------------------------------------------------------
339 bool wxTextEntry::DoAutoCompleteStrings(const wxArrayString
& choices
)
341 GtkEntry
* const entry
= (GtkEntry
*)GetEditable();
342 wxCHECK_MSG(GTK_IS_ENTRY(entry
), false, "auto completion doesn't work with this control");
344 GtkListStore
* const store
= gtk_list_store_new(1, G_TYPE_STRING
);
347 for ( wxArrayString::const_iterator i
= choices
.begin();
351 gtk_list_store_append(store
, &iter
);
352 gtk_list_store_set(store
, &iter
,
353 0, (const gchar
*)i
->utf8_str(),
357 GtkEntryCompletion
* const completion
= gtk_entry_completion_new();
358 gtk_entry_completion_set_model(completion
, GTK_TREE_MODEL(store
));
359 gtk_entry_completion_set_text_column(completion
, 0);
360 gtk_entry_set_completion(entry
, completion
);
361 g_object_unref(completion
);
365 // ----------------------------------------------------------------------------
367 // ----------------------------------------------------------------------------
369 bool wxTextEntry::IsEditable() const
371 return gtk_editable_get_editable(GetEditable()) != 0;
374 void wxTextEntry::SetEditable(bool editable
)
376 gtk_editable_set_editable(GetEditable(), editable
);
379 // ----------------------------------------------------------------------------
381 // ----------------------------------------------------------------------------
383 void wxTextEntry::SetMaxLength(unsigned long len
)
385 GtkEntry
* const entry
= (GtkEntry
*)GetEditable();
386 if (!GTK_IS_ENTRY(entry
))
389 gtk_entry_set_max_length(entry
, len
);
391 // there is a bug in GTK+ 1.2.x: "changed" signal is emitted even if we had
392 // tried to enter more text than allowed by max text length and the text
393 // wasn't really changed
395 // to detect this and generate TEXT_MAXLEN event instead of TEXT_CHANGED
396 // one in this case we also catch "insert_text" signal
398 // when max len is set to 0 we disconnect our handler as it means that we
399 // shouldn't check anything any more
406 G_CALLBACK(wx_gtk_insert_text_callback
),
410 else // no max length
412 g_signal_handlers_disconnect_by_func
415 (gpointer
)wx_gtk_insert_text_callback
,
421 void wxTextEntry::SendMaxLenEvent()
423 // remember that the next changed signal is to be ignored to avoid
424 // generating a dummy wxEVT_COMMAND_TEXT_UPDATED event
425 //IgnoreNextTextUpdate();
427 wxWindow
* const win
= GetEditableWindow();
428 wxCommandEvent
event(wxEVT_COMMAND_TEXT_MAXLEN
, win
->GetId());
429 event
.SetEventObject(win
);
430 event
.SetString(GetValue());
431 win
->HandleWindowEvent(event
);
434 // ----------------------------------------------------------------------------
436 // ----------------------------------------------------------------------------
438 bool wxTextEntry::DoSetMargins(const wxPoint
& margins
)
440 #if GTK_CHECK_VERSION(2,10,0)
441 GtkEntry
* entry
= GetEntry();
446 const GtkBorder
* oldBorder
= gtk_entry_get_inner_border(entry
);
447 GtkBorder
* newBorder
;
451 newBorder
= gtk_border_copy(oldBorder
);
455 #if GTK_CHECK_VERSION(2,14,0)
456 newBorder
= gtk_border_new();
458 newBorder
= g_slice_new0(GtkBorder
);
460 // Use some reasonable defaults for initial margins
462 newBorder
->right
= 2;
464 // These numbers seem to let the text remain vertically centered
465 // in common use scenarios when margins.y == -1.
467 newBorder
->bottom
= 3;
470 if ( margins
.x
!= -1 )
471 newBorder
->left
= (gint
) margins
.x
;
473 if ( margins
.y
!= -1 )
474 newBorder
->top
= (gint
) margins
.y
;
476 gtk_entry_set_inner_border(entry
, newBorder
);
478 #if GTK_CHECK_VERSION(2,14,0)
479 gtk_border_free(newBorder
);
481 g_slice_free(GtkBorder
, newBorder
);
486 wxUnusedVar(margins
);
491 wxPoint
wxTextEntry::DoGetMargins() const
493 #if GTK_CHECK_VERSION(2,10,0)
494 GtkEntry
* entry
= GetEntry();
497 return wxPoint(-1, -1);
499 const GtkBorder
* border
= gtk_entry_get_inner_border(entry
);
502 return wxPoint(-1, -1);
504 return wxPoint((wxCoord
) border
->left
, (wxCoord
) border
->top
);
506 return wxPoint(-1, -1);
510 #endif // wxUSE_TEXTCTRL || wxUSE_COMBOBOX