]> git.saurik.com Git - wxWidgets.git/blob - src/gtk/textentry.cpp
Forward WM_{CUT,COPY,PASTE} from edit control to wxComboBox in wxMSW too.
[wxWidgets.git] / src / gtk / textentry.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/gtk/textentry.cpp
3 // Purpose: wxTextEntry implementation for wxGTK
4 // Author: Vadim Zeitlin
5 // Created: 2007-09-24
6 // RCS-ID: $Id$
7 // Copyright: (c) 2007 Vadim Zeitlin <vadim@wxwindows.org>
8 // Licence: wxWindows licence
9 ///////////////////////////////////////////////////////////////////////////////
10
11 // ============================================================================
12 // declarations
13 // ============================================================================
14
15 // ----------------------------------------------------------------------------
16 // headers
17 // ----------------------------------------------------------------------------
18
19 // for compilers that support precompilation, includes "wx.h".
20 #include "wx/wxprec.h"
21
22 #ifdef __BORLANDC__
23 #pragma hdrstop
24 #endif
25
26 #if wxUSE_TEXTCTRL || wxUSE_COMBOBOX
27
28 #ifndef WX_PRECOMP
29 #include "wx/textentry.h"
30 #include "wx/window.h"
31 #include "wx/textctrl.h"
32 #endif //WX_PRECOMP
33
34 #include <gtk/gtk.h>
35 #include "wx/gtk/private.h"
36 #include "wx/gtk/private/gtk2-compat.h"
37
38 // ============================================================================
39 // signal handlers implementation
40 // ============================================================================
41
42 extern "C"
43 {
44
45 // "insert_text" handler for GtkEntry
46 static void
47 wx_gtk_insert_text_callback(GtkEditable *editable,
48 const gchar * WXUNUSED(new_text),
49 gint WXUNUSED(new_text_length),
50 gint * WXUNUSED(position),
51 wxTextEntry *text)
52 {
53 // we should only be called if we have a max len limit at all
54 GtkEntry *entry = GTK_ENTRY (editable);
55
56 const int text_length = gtk_entry_get_text_length(entry);
57 #if GTK_CHECK_VERSION(3,0,0) || defined(GSEAL_ENABLE)
58 const int text_max_length = gtk_entry_buffer_get_max_length(gtk_entry_get_buffer(entry));
59 #else
60 const int text_max_length = entry->text_max_length;
61 #endif
62 wxCHECK_RET(text_max_length, "shouldn't be called");
63
64 // check that we don't overflow the max length limit
65 //
66 // FIXME: this doesn't work when we paste a string which is going to be
67 // truncated
68 if (text_length == text_max_length)
69 {
70 // we don't need to run the base class version at all
71 g_signal_stop_emission_by_name (editable, "insert_text");
72
73 text->SendMaxLenEvent();
74 }
75 }
76
77 } // extern "C"
78
79 // ============================================================================
80 // wxTextEntry implementation
81 // ============================================================================
82
83 // ----------------------------------------------------------------------------
84 // text operations
85 // ----------------------------------------------------------------------------
86
87 void wxTextEntry::WriteText(const wxString& value)
88 {
89 GtkEditable * const edit = GetEditable();
90
91 // remove the selection if there is one and suppress the text change event
92 // generated by this: we only want to generate one event for this change,
93 // not two
94 {
95 EventsSuppressor noevents(this);
96 gtk_editable_delete_selection(edit);
97 }
98
99 // insert new text at the cursor position
100 gint len = gtk_editable_get_position(edit);
101 gtk_editable_insert_text
102 (
103 edit,
104 wxGTK_CONV_FONT(value, GetEditableWindow()->GetFont()),
105 -1, // text: length: compute it using strlen()
106 &len // will be updated to position after the text end
107 );
108
109 // and move cursor to the end of new text
110 gtk_editable_set_position(edit, len);
111 }
112
113 void wxTextEntry::DoSetValue(const wxString& value, int flags)
114 {
115 if (value != GetValue())
116 {
117 // use Remove() rather than SelectAll() to avoid unnecessary clipboard
118 // operations, and prevent triggering an apparent bug in GTK which
119 // causes the the subsequent WriteText() to append rather than overwrite
120 {
121 EventsSuppressor noevents(this);
122 Remove(0, -1);
123 }
124 EventsSuppressor noeventsIf(this, !(flags & SetValue_SendEvent));
125 WriteText(value);
126 }
127 else if (flags & SetValue_SendEvent)
128 SendTextUpdatedEvent(GetEditableWindow());
129
130 SetInsertionPoint(0);
131 }
132
133 wxString wxTextEntry::DoGetValue() const
134 {
135 const wxGtkString value(gtk_editable_get_chars(GetEditable(), 0, -1));
136
137 return wxGTK_CONV_BACK_FONT(value,
138 const_cast<wxTextEntry *>(this)->GetEditableWindow()->GetFont());
139 }
140
141 void wxTextEntry::Remove(long from, long to)
142 {
143 gtk_editable_delete_text(GetEditable(), from, to);
144 }
145
146 // ----------------------------------------------------------------------------
147 // clipboard operations
148 // ----------------------------------------------------------------------------
149
150 void wxTextEntry::Copy()
151 {
152 gtk_editable_copy_clipboard(GetEditable());
153 }
154
155 void wxTextEntry::Cut()
156 {
157 gtk_editable_cut_clipboard(GetEditable());
158 }
159
160 void wxTextEntry::Paste()
161 {
162 gtk_editable_paste_clipboard(GetEditable());
163 }
164
165 // ----------------------------------------------------------------------------
166 // undo/redo
167 // ----------------------------------------------------------------------------
168
169 void wxTextEntry::Undo()
170 {
171 // TODO: not implemented
172 }
173
174 void wxTextEntry::Redo()
175 {
176 // TODO: not implemented
177 }
178
179 bool wxTextEntry::CanUndo() const
180 {
181 return false;
182 }
183
184 bool wxTextEntry::CanRedo() const
185 {
186 return false;
187 }
188
189 // ----------------------------------------------------------------------------
190 // insertion point
191 // ----------------------------------------------------------------------------
192
193 void wxTextEntry::SetInsertionPoint(long pos)
194 {
195 gtk_editable_set_position(GetEditable(), pos);
196 }
197
198 long wxTextEntry::GetInsertionPoint() const
199 {
200 return gtk_editable_get_position(GetEditable());
201 }
202
203 long wxTextEntry::GetLastPosition() const
204 {
205 // this can't be implemented for arbitrary GtkEditable so only do it for
206 // GtkEntries
207 GtkEntry * const entry = GTK_ENTRY(GetEditable());
208
209 return entry ? gtk_entry_get_text_length(entry) : -1;
210 }
211
212 // ----------------------------------------------------------------------------
213 // selection
214 // ----------------------------------------------------------------------------
215
216 void wxTextEntry::SetSelection(long from, long to)
217 {
218 // in wx convention, (-1, -1) means the entire range but GTK+ translates -1
219 // (or any negative number for that matter) into last position so we need
220 // to translate manually
221 if ( from == -1 && to == -1 )
222 from = 0;
223
224 // for compatibility with MSW, exchange from and to parameters so that the
225 // insertion point is set to the start of the selection and not its end as
226 // GTK+ does by default
227 gtk_editable_select_region(GetEditable(), to, from);
228
229 #ifndef __WXGTK3__
230 // avoid reported problem with RHEL 5 GTK+ 2.10 where selection is reset by
231 // a clipboard callback, see #13277
232 if (gtk_check_version(2,12,0))
233 {
234 GtkEntry* entry = GTK_ENTRY(GetEditable());
235 if (to < 0)
236 to = entry->text_length;
237 entry->selection_bound = to;
238 }
239 #endif
240 }
241
242 void wxTextEntry::GetSelection(long *from, long *to) const
243 {
244 gint start, end;
245 if ( gtk_editable_get_selection_bounds(GetEditable(), &start, &end) )
246 {
247 // the output must always be in order, although in GTK+ it isn't
248 if ( start > end )
249 {
250 gint tmp = start;
251 start = end;
252 end = tmp;
253 }
254 }
255 else // no selection
256 {
257 // for compatibility with MSW return the empty selection at cursor
258 start =
259 end = GetInsertionPoint();
260 }
261
262 if ( from )
263 *from = start;
264
265 if ( to )
266 *to = end;
267 }
268
269 // ----------------------------------------------------------------------------
270 // auto completion
271 // ----------------------------------------------------------------------------
272
273 bool wxTextEntry::DoAutoCompleteStrings(const wxArrayString& choices)
274 {
275 GtkEntry * const entry = GTK_ENTRY(GetEditable());
276 wxCHECK_MSG(entry, false, "auto completion doesn't work with this control");
277
278 GtkListStore * const store = gtk_list_store_new(1, G_TYPE_STRING);
279 GtkTreeIter iter;
280
281 for ( wxArrayString::const_iterator i = choices.begin();
282 i != choices.end();
283 ++i )
284 {
285 gtk_list_store_append(store, &iter);
286 gtk_list_store_set(store, &iter,
287 0, (const gchar *)i->utf8_str(),
288 -1);
289 }
290
291 GtkEntryCompletion * const completion = gtk_entry_completion_new();
292 gtk_entry_completion_set_model(completion, GTK_TREE_MODEL(store));
293 gtk_entry_completion_set_text_column(completion, 0);
294 gtk_entry_set_completion(entry, completion);
295 g_object_unref(completion);
296 return true;
297 }
298
299 // ----------------------------------------------------------------------------
300 // editable status
301 // ----------------------------------------------------------------------------
302
303 bool wxTextEntry::IsEditable() const
304 {
305 return gtk_editable_get_editable(GetEditable()) != 0;
306 }
307
308 void wxTextEntry::SetEditable(bool editable)
309 {
310 gtk_editable_set_editable(GetEditable(), editable);
311 }
312
313 // ----------------------------------------------------------------------------
314 // max text length
315 // ----------------------------------------------------------------------------
316
317 void wxTextEntry::SetMaxLength(unsigned long len)
318 {
319 GtkEntry * const entry = GTK_ENTRY(GetEditable());
320 if ( !entry )
321 return;
322
323 gtk_entry_set_max_length(entry, len);
324
325 // there is a bug in GTK+ 1.2.x: "changed" signal is emitted even if we had
326 // tried to enter more text than allowed by max text length and the text
327 // wasn't really changed
328 //
329 // to detect this and generate TEXT_MAXLEN event instead of TEXT_CHANGED
330 // one in this case we also catch "insert_text" signal
331 //
332 // when max len is set to 0 we disconnect our handler as it means that we
333 // shouldn't check anything any more
334 if ( len )
335 {
336 g_signal_connect
337 (
338 entry,
339 "insert_text",
340 G_CALLBACK(wx_gtk_insert_text_callback),
341 this
342 );
343 }
344 else // no max length
345 {
346 g_signal_handlers_disconnect_by_func
347 (
348 entry,
349 (gpointer)wx_gtk_insert_text_callback,
350 this
351 );
352 }
353 }
354
355 void wxTextEntry::SendMaxLenEvent()
356 {
357 // remember that the next changed signal is to be ignored to avoid
358 // generating a dummy wxEVT_COMMAND_TEXT_UPDATED event
359 //IgnoreNextTextUpdate();
360
361 wxWindow * const win = GetEditableWindow();
362 wxCommandEvent event(wxEVT_COMMAND_TEXT_MAXLEN, win->GetId());
363 event.SetEventObject(win);
364 event.SetString(GetValue());
365 win->HandleWindowEvent(event);
366 }
367
368 // ----------------------------------------------------------------------------
369 // margins support
370 // ----------------------------------------------------------------------------
371
372 bool wxTextEntry::DoSetMargins(const wxPoint& margins)
373 {
374 #if GTK_CHECK_VERSION(2,10,0)
375 GtkEntry* entry = GetEntry();
376
377 if ( !entry )
378 return false;
379
380 const GtkBorder* oldBorder = gtk_entry_get_inner_border(entry);
381 GtkBorder* newBorder;
382
383 if ( oldBorder )
384 {
385 newBorder = gtk_border_copy(oldBorder);
386 }
387 else
388 {
389 #if GTK_CHECK_VERSION(2,14,0)
390 newBorder = gtk_border_new();
391 #else
392 newBorder = g_slice_new0(GtkBorder);
393 #endif
394 // Use some reasonable defaults for initial margins
395 newBorder->left = 2;
396 newBorder->right = 2;
397
398 // These numbers seem to let the text remain vertically centered
399 // in common use scenarios when margins.y == -1.
400 newBorder->top = 3;
401 newBorder->bottom = 3;
402 }
403
404 if ( margins.x != -1 )
405 newBorder->left = (gint) margins.x;
406
407 if ( margins.y != -1 )
408 newBorder->top = (gint) margins.y;
409
410 gtk_entry_set_inner_border(entry, newBorder);
411
412 #if GTK_CHECK_VERSION(2,14,0)
413 gtk_border_free(newBorder);
414 #else
415 g_slice_free(GtkBorder, newBorder);
416 #endif
417
418 return true;
419 #else
420 wxUnusedVar(margins);
421 return false;
422 #endif
423 }
424
425 wxPoint wxTextEntry::DoGetMargins() const
426 {
427 #if GTK_CHECK_VERSION(2,10,0)
428 GtkEntry* entry = GetEntry();
429
430 if ( !entry )
431 return wxPoint(-1, -1);
432
433 const GtkBorder* border = gtk_entry_get_inner_border(entry);
434
435 if ( !border )
436 return wxPoint(-1, -1);
437
438 return wxPoint((wxCoord) border->left, (wxCoord) border->top);
439 #else
440 return wxPoint(-1, -1);
441 #endif
442 }
443
444 #endif // wxUSE_TEXTCTRL || wxUSE_COMBOBOX