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
;
61 // check that we don't overflow the max length limit if we have it
62 if ( text_max_length
)
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.
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 if ( !handled
&& text
->GTKEntryOnInsertText(new_text
) )
84 // If we already handled the new text insertion, don't do it again.
89 g_signal_stop_emission_by_name (editable
, "insert_text");
92 //-----------------------------------------------------------------------------
93 // clipboard events: "copy-clipboard", "cut-clipboard", "paste-clipboard"
94 //-----------------------------------------------------------------------------
96 // common part of the event handlers below
98 DoHandleClipboardCallback( GtkWidget
*widget
,
100 wxEventType eventType
,
101 const gchar
* signal_name
)
103 wxClipboardTextEvent
event( eventType
, win
->GetId() );
104 event
.SetEventObject( win
);
105 if ( win
->HandleWindowEvent( event
) )
107 // don't let the default processing to take place if we did something
108 // ourselves in the event handler
109 g_signal_stop_emission_by_name (widget
, signal_name
);
117 wx_gtk_copy_clipboard_callback( GtkWidget
*widget
, wxWindow
*win
)
119 DoHandleClipboardCallback(
120 widget
, win
, wxEVT_COMMAND_TEXT_COPY
, "copy-clipboard" );
124 wx_gtk_cut_clipboard_callback( GtkWidget
*widget
, wxWindow
*win
)
126 DoHandleClipboardCallback(
127 widget
, win
, wxEVT_COMMAND_TEXT_CUT
, "cut-clipboard" );
131 wx_gtk_paste_clipboard_callback( GtkWidget
*widget
, wxWindow
*win
)
133 DoHandleClipboardCallback(
134 widget
, win
, wxEVT_COMMAND_TEXT_PASTE
, "paste-clipboard" );
139 // ============================================================================
140 // wxTextEntry implementation
141 // ============================================================================
143 // ----------------------------------------------------------------------------
145 // ----------------------------------------------------------------------------
147 void wxTextEntry::WriteText(const wxString
& value
)
149 GtkEditable
* const edit
= GetEditable();
151 // remove the selection if there is one and suppress the text change event
152 // generated by this: we only want to generate one event for this change,
155 EventsSuppressor
noevents(this);
156 gtk_editable_delete_selection(edit
);
159 // insert new text at the cursor position
160 gint len
= gtk_editable_get_position(edit
);
161 gtk_editable_insert_text
164 wxGTK_CONV_FONT(value
, GetEditableWindow()->GetFont()),
165 -1, // text: length: compute it using strlen()
166 &len
// will be updated to position after the text end
169 // and move cursor to the end of new text
170 gtk_editable_set_position(edit
, len
);
173 void wxTextEntry::DoSetValue(const wxString
& value
, int flags
)
175 if (value
!= DoGetValue())
177 // use Remove() rather than SelectAll() to avoid unnecessary clipboard
178 // operations, and prevent triggering an apparent bug in GTK which
179 // causes the the subsequent WriteText() to append rather than overwrite
181 EventsSuppressor
noevents(this);
184 EventsSuppressor
noeventsIf(this, !(flags
& SetValue_SendEvent
));
187 else if (flags
& SetValue_SendEvent
)
188 SendTextUpdatedEvent(GetEditableWindow());
190 SetInsertionPoint(0);
193 wxString
wxTextEntry::DoGetValue() const
195 const wxGtkString
value(gtk_editable_get_chars(GetEditable(), 0, -1));
197 return wxGTK_CONV_BACK_FONT(value
,
198 const_cast<wxTextEntry
*>(this)->GetEditableWindow()->GetFont());
201 void wxTextEntry::Remove(long from
, long to
)
203 gtk_editable_delete_text(GetEditable(), from
, to
);
206 // ----------------------------------------------------------------------------
207 // clipboard operations
208 // ----------------------------------------------------------------------------
210 void wxTextEntry::GTKConnectClipboardSignals(GtkWidget
* entry
)
212 g_signal_connect(entry
, "copy-clipboard",
213 G_CALLBACK (wx_gtk_copy_clipboard_callback
),
214 GetEditableWindow());
215 g_signal_connect(entry
, "cut-clipboard",
216 G_CALLBACK (wx_gtk_cut_clipboard_callback
),
217 GetEditableWindow());
218 g_signal_connect(entry
, "paste-clipboard",
219 G_CALLBACK (wx_gtk_paste_clipboard_callback
),
220 GetEditableWindow());
223 void wxTextEntry::Copy()
225 gtk_editable_copy_clipboard(GetEditable());
228 void wxTextEntry::Cut()
230 gtk_editable_cut_clipboard(GetEditable());
233 void wxTextEntry::Paste()
235 gtk_editable_paste_clipboard(GetEditable());
238 // ----------------------------------------------------------------------------
240 // ----------------------------------------------------------------------------
242 void wxTextEntry::Undo()
244 // TODO: not implemented
247 void wxTextEntry::Redo()
249 // TODO: not implemented
252 bool wxTextEntry::CanUndo() const
257 bool wxTextEntry::CanRedo() const
262 // ----------------------------------------------------------------------------
264 // ----------------------------------------------------------------------------
266 void wxTextEntry::SetInsertionPoint(long pos
)
268 gtk_editable_set_position(GetEditable(), pos
);
271 long wxTextEntry::GetInsertionPoint() const
273 return gtk_editable_get_position(GetEditable());
276 long wxTextEntry::GetLastPosition() const
278 // this can't be implemented for arbitrary GtkEditable so only do it for
281 GtkEntry
* entry
= (GtkEntry
*)GetEditable();
282 if (GTK_IS_ENTRY(entry
))
283 pos
= gtk_entry_get_text_length(entry
);
288 // ----------------------------------------------------------------------------
290 // ----------------------------------------------------------------------------
292 void wxTextEntry::SetSelection(long from
, long to
)
294 // in wx convention, (-1, -1) means the entire range but GTK+ translates -1
295 // (or any negative number for that matter) into last position so we need
296 // to translate manually
297 if ( from
== -1 && to
== -1 )
300 // for compatibility with MSW, exchange from and to parameters so that the
301 // insertion point is set to the start of the selection and not its end as
302 // GTK+ does by default
303 gtk_editable_select_region(GetEditable(), to
, from
);
306 // avoid reported problem with RHEL 5 GTK+ 2.10 where selection is reset by
307 // a clipboard callback, see #13277
308 if (gtk_check_version(2,12,0))
310 GtkEntry
* entry
= GTK_ENTRY(GetEditable());
312 to
= entry
->text_length
;
313 entry
->selection_bound
= to
;
318 void wxTextEntry::GetSelection(long *from
, long *to
) const
321 if ( gtk_editable_get_selection_bounds(GetEditable(), &start
, &end
) )
323 // the output must always be in order, although in GTK+ it isn't
333 // for compatibility with MSW return the empty selection at cursor
335 end
= GetInsertionPoint();
345 // ----------------------------------------------------------------------------
347 // ----------------------------------------------------------------------------
349 bool wxTextEntry::DoAutoCompleteStrings(const wxArrayString
& choices
)
351 GtkEntry
* const entry
= (GtkEntry
*)GetEditable();
352 wxCHECK_MSG(GTK_IS_ENTRY(entry
), false, "auto completion doesn't work with this control");
354 GtkListStore
* const store
= gtk_list_store_new(1, G_TYPE_STRING
);
357 for ( wxArrayString::const_iterator i
= choices
.begin();
361 gtk_list_store_append(store
, &iter
);
362 gtk_list_store_set(store
, &iter
,
363 0, (const gchar
*)i
->utf8_str(),
367 GtkEntryCompletion
* const completion
= gtk_entry_completion_new();
368 gtk_entry_completion_set_model(completion
, GTK_TREE_MODEL(store
));
369 gtk_entry_completion_set_text_column(completion
, 0);
370 gtk_entry_set_completion(entry
, completion
);
371 g_object_unref(completion
);
375 // ----------------------------------------------------------------------------
377 // ----------------------------------------------------------------------------
379 bool wxTextEntry::IsEditable() const
381 return gtk_editable_get_editable(GetEditable()) != 0;
384 void wxTextEntry::SetEditable(bool editable
)
386 gtk_editable_set_editable(GetEditable(), editable
);
389 // ----------------------------------------------------------------------------
391 // ----------------------------------------------------------------------------
393 void wxTextEntry::SetMaxLength(unsigned long len
)
395 GtkEntry
* const entry
= (GtkEntry
*)GetEditable();
396 if (!GTK_IS_ENTRY(entry
))
399 gtk_entry_set_max_length(entry
, len
);
402 void wxTextEntry::SendMaxLenEvent()
404 // remember that the next changed signal is to be ignored to avoid
405 // generating a dummy wxEVT_COMMAND_TEXT_UPDATED event
406 //IgnoreNextTextUpdate();
408 wxWindow
* const win
= GetEditableWindow();
409 wxCommandEvent
event(wxEVT_COMMAND_TEXT_MAXLEN
, win
->GetId());
410 event
.SetEventObject(win
);
411 event
.SetString(GetValue());
412 win
->HandleWindowEvent(event
);
415 // ----------------------------------------------------------------------------
417 // ----------------------------------------------------------------------------
419 int wxTextEntry::GTKIMFilterKeypress(GdkEventKey
* event
) const
421 #if GTK_CHECK_VERSION(2, 22, 0)
422 if ( gtk_check_version(2, 12, 0) == 0 )
423 return gtk_entry_im_context_filter_keypress(GetEntry(), event
);
431 void wxTextEntry::GTKConnectInsertTextSignal(GtkEntry
* entry
)
433 g_signal_connect(entry
, "insert_text",
434 G_CALLBACK(wx_gtk_insert_text_callback
), this);
437 bool wxTextEntry::GTKEntryOnInsertText(const char* text
)
439 return GetEditableWindow()->GTKOnInsertText(text
);
442 // ----------------------------------------------------------------------------
444 // ----------------------------------------------------------------------------
446 bool wxTextEntry::DoSetMargins(const wxPoint
& margins
)
448 #if GTK_CHECK_VERSION(2,10,0)
449 GtkEntry
* entry
= GetEntry();
454 const GtkBorder
* oldBorder
= gtk_entry_get_inner_border(entry
);
455 GtkBorder
* newBorder
;
459 newBorder
= gtk_border_copy(oldBorder
);
463 #if GTK_CHECK_VERSION(2,14,0)
464 newBorder
= gtk_border_new();
466 newBorder
= g_slice_new0(GtkBorder
);
468 // Use some reasonable defaults for initial margins
470 newBorder
->right
= 2;
472 // These numbers seem to let the text remain vertically centered
473 // in common use scenarios when margins.y == -1.
475 newBorder
->bottom
= 3;
478 if ( margins
.x
!= -1 )
479 newBorder
->left
= (gint
) margins
.x
;
481 if ( margins
.y
!= -1 )
482 newBorder
->top
= (gint
) margins
.y
;
484 gtk_entry_set_inner_border(entry
, newBorder
);
486 #if GTK_CHECK_VERSION(2,14,0)
487 gtk_border_free(newBorder
);
489 g_slice_free(GtkBorder
, newBorder
);
494 wxUnusedVar(margins
);
499 wxPoint
wxTextEntry::DoGetMargins() const
501 #if GTK_CHECK_VERSION(2,10,0)
502 GtkEntry
* entry
= GetEntry();
505 return wxPoint(-1, -1);
507 const GtkBorder
* border
= gtk_entry_get_inner_border(entry
);
510 return wxPoint(-1, -1);
512 return wxPoint((wxCoord
) border
->left
, (wxCoord
) border
->top
);
514 return wxPoint(-1, -1);
518 #endif // wxUSE_TEXTCTRL || wxUSE_COMBOBOX