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
* WXUNUSED(new_text
),
47 gint
WXUNUSED(new_text_length
),
48 gint
* WXUNUSED(position
),
51 // we should only be called if we have a max len limit at all
52 GtkEntry
*entry
= GTK_ENTRY (editable
);
54 const int text_length
= gtk_entry_get_text_length(entry
);
55 #if GTK_CHECK_VERSION(3,0,0) || defined(GSEAL_ENABLE)
56 const int text_max_length
= gtk_entry_buffer_get_max_length(gtk_entry_get_buffer(entry
));
58 const int text_max_length
= entry
->text_max_length
;
60 wxCHECK_RET(text_max_length
, "shouldn't be called");
62 // check that we don't overflow the max length limit
64 // FIXME: this doesn't work when we paste a string which is going to be
66 if (text_length
== text_max_length
)
68 // we don't need to run the base class version at all
69 g_signal_stop_emission_by_name (editable
, "insert_text");
71 text
->SendMaxLenEvent();
75 //-----------------------------------------------------------------------------
76 // clipboard events: "copy-clipboard", "cut-clipboard", "paste-clipboard"
77 //-----------------------------------------------------------------------------
79 // common part of the event handlers below
81 DoHandleClipboardCallback( GtkWidget
*widget
,
83 wxEventType eventType
,
84 const gchar
* signal_name
)
86 wxClipboardTextEvent
event( eventType
, win
->GetId() );
87 event
.SetEventObject( win
);
88 if ( win
->HandleWindowEvent( event
) )
90 // don't let the default processing to take place if we did something
91 // ourselves in the event handler
92 g_signal_stop_emission_by_name (widget
, signal_name
);
100 wx_gtk_copy_clipboard_callback( GtkWidget
*widget
, wxWindow
*win
)
102 DoHandleClipboardCallback(
103 widget
, win
, wxEVT_COMMAND_TEXT_COPY
, "copy-clipboard" );
107 wx_gtk_cut_clipboard_callback( GtkWidget
*widget
, wxWindow
*win
)
109 DoHandleClipboardCallback(
110 widget
, win
, wxEVT_COMMAND_TEXT_CUT
, "cut-clipboard" );
114 wx_gtk_paste_clipboard_callback( GtkWidget
*widget
, wxWindow
*win
)
116 DoHandleClipboardCallback(
117 widget
, win
, wxEVT_COMMAND_TEXT_PASTE
, "paste-clipboard" );
122 // ============================================================================
123 // wxTextEntry implementation
124 // ============================================================================
126 // ----------------------------------------------------------------------------
128 // ----------------------------------------------------------------------------
130 void wxTextEntry::WriteText(const wxString
& value
)
132 GtkEditable
* const edit
= GetEditable();
134 // remove the selection if there is one and suppress the text change event
135 // generated by this: we only want to generate one event for this change,
138 EventsSuppressor
noevents(this);
139 gtk_editable_delete_selection(edit
);
142 // insert new text at the cursor position
143 gint len
= gtk_editable_get_position(edit
);
144 gtk_editable_insert_text
147 wxGTK_CONV_FONT(value
, GetEditableWindow()->GetFont()),
148 -1, // text: length: compute it using strlen()
149 &len
// will be updated to position after the text end
152 // and move cursor to the end of new text
153 gtk_editable_set_position(edit
, len
);
156 void wxTextEntry::DoSetValue(const wxString
& value
, int flags
)
158 if (value
!= DoGetValue())
160 // use Remove() rather than SelectAll() to avoid unnecessary clipboard
161 // operations, and prevent triggering an apparent bug in GTK which
162 // causes the the subsequent WriteText() to append rather than overwrite
164 EventsSuppressor
noevents(this);
167 EventsSuppressor
noeventsIf(this, !(flags
& SetValue_SendEvent
));
170 else if (flags
& SetValue_SendEvent
)
171 SendTextUpdatedEvent(GetEditableWindow());
173 SetInsertionPoint(0);
176 wxString
wxTextEntry::DoGetValue() const
178 const wxGtkString
value(gtk_editable_get_chars(GetEditable(), 0, -1));
180 return wxGTK_CONV_BACK_FONT(value
,
181 const_cast<wxTextEntry
*>(this)->GetEditableWindow()->GetFont());
184 void wxTextEntry::Remove(long from
, long to
)
186 gtk_editable_delete_text(GetEditable(), from
, to
);
189 // ----------------------------------------------------------------------------
190 // clipboard operations
191 // ----------------------------------------------------------------------------
193 void wxTextEntry::GTKConnectClipboardSignals(GtkWidget
* entry
)
195 g_signal_connect(entry
, "copy-clipboard",
196 G_CALLBACK (wx_gtk_copy_clipboard_callback
),
197 GetEditableWindow());
198 g_signal_connect(entry
, "cut-clipboard",
199 G_CALLBACK (wx_gtk_cut_clipboard_callback
),
200 GetEditableWindow());
201 g_signal_connect(entry
, "paste-clipboard",
202 G_CALLBACK (wx_gtk_paste_clipboard_callback
),
203 GetEditableWindow());
206 void wxTextEntry::Copy()
208 gtk_editable_copy_clipboard(GetEditable());
211 void wxTextEntry::Cut()
213 gtk_editable_cut_clipboard(GetEditable());
216 void wxTextEntry::Paste()
218 gtk_editable_paste_clipboard(GetEditable());
221 // ----------------------------------------------------------------------------
223 // ----------------------------------------------------------------------------
225 void wxTextEntry::Undo()
227 // TODO: not implemented
230 void wxTextEntry::Redo()
232 // TODO: not implemented
235 bool wxTextEntry::CanUndo() const
240 bool wxTextEntry::CanRedo() const
245 // ----------------------------------------------------------------------------
247 // ----------------------------------------------------------------------------
249 void wxTextEntry::SetInsertionPoint(long pos
)
251 gtk_editable_set_position(GetEditable(), pos
);
254 long wxTextEntry::GetInsertionPoint() const
256 return gtk_editable_get_position(GetEditable());
259 long wxTextEntry::GetLastPosition() const
261 // this can't be implemented for arbitrary GtkEditable so only do it for
264 GtkEntry
* entry
= (GtkEntry
*)GetEditable();
265 if (GTK_IS_ENTRY(entry
))
266 pos
= gtk_entry_get_text_length(entry
);
271 // ----------------------------------------------------------------------------
273 // ----------------------------------------------------------------------------
275 void wxTextEntry::SetSelection(long from
, long to
)
277 // in wx convention, (-1, -1) means the entire range but GTK+ translates -1
278 // (or any negative number for that matter) into last position so we need
279 // to translate manually
280 if ( from
== -1 && to
== -1 )
283 // for compatibility with MSW, exchange from and to parameters so that the
284 // insertion point is set to the start of the selection and not its end as
285 // GTK+ does by default
286 gtk_editable_select_region(GetEditable(), to
, from
);
289 // avoid reported problem with RHEL 5 GTK+ 2.10 where selection is reset by
290 // a clipboard callback, see #13277
291 if (gtk_check_version(2,12,0))
293 GtkEntry
* entry
= GTK_ENTRY(GetEditable());
295 to
= entry
->text_length
;
296 entry
->selection_bound
= to
;
301 void wxTextEntry::GetSelection(long *from
, long *to
) const
304 if ( gtk_editable_get_selection_bounds(GetEditable(), &start
, &end
) )
306 // the output must always be in order, although in GTK+ it isn't
316 // for compatibility with MSW return the empty selection at cursor
318 end
= GetInsertionPoint();
328 // ----------------------------------------------------------------------------
330 // ----------------------------------------------------------------------------
332 bool wxTextEntry::DoAutoCompleteStrings(const wxArrayString
& choices
)
334 GtkEntry
* const entry
= (GtkEntry
*)GetEditable();
335 wxCHECK_MSG(GTK_IS_ENTRY(entry
), false, "auto completion doesn't work with this control");
337 GtkListStore
* const store
= gtk_list_store_new(1, G_TYPE_STRING
);
340 for ( wxArrayString::const_iterator i
= choices
.begin();
344 gtk_list_store_append(store
, &iter
);
345 gtk_list_store_set(store
, &iter
,
346 0, (const gchar
*)i
->utf8_str(),
350 GtkEntryCompletion
* const completion
= gtk_entry_completion_new();
351 gtk_entry_completion_set_model(completion
, GTK_TREE_MODEL(store
));
352 gtk_entry_completion_set_text_column(completion
, 0);
353 gtk_entry_set_completion(entry
, completion
);
354 g_object_unref(completion
);
358 // ----------------------------------------------------------------------------
360 // ----------------------------------------------------------------------------
362 bool wxTextEntry::IsEditable() const
364 return gtk_editable_get_editable(GetEditable()) != 0;
367 void wxTextEntry::SetEditable(bool editable
)
369 gtk_editable_set_editable(GetEditable(), editable
);
372 // ----------------------------------------------------------------------------
374 // ----------------------------------------------------------------------------
376 void wxTextEntry::SetMaxLength(unsigned long len
)
378 GtkEntry
* const entry
= (GtkEntry
*)GetEditable();
379 if (!GTK_IS_ENTRY(entry
))
382 gtk_entry_set_max_length(entry
, len
);
384 // there is a bug in GTK+ 1.2.x: "changed" signal is emitted even if we had
385 // tried to enter more text than allowed by max text length and the text
386 // wasn't really changed
388 // to detect this and generate TEXT_MAXLEN event instead of TEXT_CHANGED
389 // one in this case we also catch "insert_text" signal
391 // when max len is set to 0 we disconnect our handler as it means that we
392 // shouldn't check anything any more
399 G_CALLBACK(wx_gtk_insert_text_callback
),
403 else // no max length
405 g_signal_handlers_disconnect_by_func
408 (gpointer
)wx_gtk_insert_text_callback
,
414 void wxTextEntry::SendMaxLenEvent()
416 // remember that the next changed signal is to be ignored to avoid
417 // generating a dummy wxEVT_COMMAND_TEXT_UPDATED event
418 //IgnoreNextTextUpdate();
420 wxWindow
* const win
= GetEditableWindow();
421 wxCommandEvent
event(wxEVT_COMMAND_TEXT_MAXLEN
, win
->GetId());
422 event
.SetEventObject(win
);
423 event
.SetString(GetValue());
424 win
->HandleWindowEvent(event
);
427 // ----------------------------------------------------------------------------
429 // ----------------------------------------------------------------------------
431 bool wxTextEntry::DoSetMargins(const wxPoint
& margins
)
433 #if GTK_CHECK_VERSION(2,10,0)
434 GtkEntry
* entry
= GetEntry();
439 const GtkBorder
* oldBorder
= gtk_entry_get_inner_border(entry
);
440 GtkBorder
* newBorder
;
444 newBorder
= gtk_border_copy(oldBorder
);
448 #if GTK_CHECK_VERSION(2,14,0)
449 newBorder
= gtk_border_new();
451 newBorder
= g_slice_new0(GtkBorder
);
453 // Use some reasonable defaults for initial margins
455 newBorder
->right
= 2;
457 // These numbers seem to let the text remain vertically centered
458 // in common use scenarios when margins.y == -1.
460 newBorder
->bottom
= 3;
463 if ( margins
.x
!= -1 )
464 newBorder
->left
= (gint
) margins
.x
;
466 if ( margins
.y
!= -1 )
467 newBorder
->top
= (gint
) margins
.y
;
469 gtk_entry_set_inner_border(entry
, newBorder
);
471 #if GTK_CHECK_VERSION(2,14,0)
472 gtk_border_free(newBorder
);
474 g_slice_free(GtkBorder
, newBorder
);
479 wxUnusedVar(margins
);
484 wxPoint
wxTextEntry::DoGetMargins() const
486 #if GTK_CHECK_VERSION(2,10,0)
487 GtkEntry
* entry
= GetEntry();
490 return wxPoint(-1, -1);
492 const GtkBorder
* border
= gtk_entry_get_inner_border(entry
);
495 return wxPoint(-1, -1);
497 return wxPoint((wxCoord
) border
->left
, (wxCoord
) border
->top
);
499 return wxPoint(-1, -1);
503 #endif // wxUSE_TEXTCTRL || wxUSE_COMBOBOX