]> git.saurik.com Git - wxWidgets.git/blob - src/gtk/textentry.cpp
Implement wxComboCtrl::GetSizeFromTextSize().
[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 // "insert_text" handler for GtkEntry
43 extern "C"
44 void
45 wx_gtk_insert_text_callback(GtkEditable *editable,
46 const gchar * new_text,
47 gint WXUNUSED(new_text_length),
48 gint * WXUNUSED(position),
49 wxTextEntry *text)
50 {
51 GtkEntry *entry = GTK_ENTRY (editable);
52
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));
55 #else
56 const int text_max_length = entry->text_max_length;
57 #endif
58
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");
61
62 // check that we don't overflow the max length limit
63
64 const int text_length = gtk_entry_get_text_length(entry);
65
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 )
69 {
70 // Prevent the new text from being inserted.
71 g_signal_stop_emission_by_name (editable, "insert_text");
72
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.
76
77 // Notify the user code about overflow.
78 text->SendMaxLenEvent();
79 }
80 }
81
82 //-----------------------------------------------------------------------------
83 // clipboard events: "copy-clipboard", "cut-clipboard", "paste-clipboard"
84 //-----------------------------------------------------------------------------
85
86 // common part of the event handlers below
87 static void
88 DoHandleClipboardCallback( GtkWidget *widget,
89 wxWindow *win,
90 wxEventType eventType,
91 const gchar* signal_name)
92 {
93 wxClipboardTextEvent event( eventType, win->GetId() );
94 event.SetEventObject( win );
95 if ( win->HandleWindowEvent( event ) )
96 {
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);
100 }
101 }
102
103 extern "C"
104 {
105
106 static void
107 wx_gtk_copy_clipboard_callback( GtkWidget *widget, wxWindow *win )
108 {
109 DoHandleClipboardCallback(
110 widget, win, wxEVT_COMMAND_TEXT_COPY, "copy-clipboard" );
111 }
112
113 static void
114 wx_gtk_cut_clipboard_callback( GtkWidget *widget, wxWindow *win )
115 {
116 DoHandleClipboardCallback(
117 widget, win, wxEVT_COMMAND_TEXT_CUT, "cut-clipboard" );
118 }
119
120 static void
121 wx_gtk_paste_clipboard_callback( GtkWidget *widget, wxWindow *win )
122 {
123 DoHandleClipboardCallback(
124 widget, win, wxEVT_COMMAND_TEXT_PASTE, "paste-clipboard" );
125 }
126
127 } // extern "C"
128
129 // ============================================================================
130 // wxTextEntry implementation
131 // ============================================================================
132
133 // ----------------------------------------------------------------------------
134 // text operations
135 // ----------------------------------------------------------------------------
136
137 void wxTextEntry::WriteText(const wxString& value)
138 {
139 GtkEditable * const edit = GetEditable();
140
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,
143 // not two
144 {
145 EventsSuppressor noevents(this);
146 gtk_editable_delete_selection(edit);
147 }
148
149 // insert new text at the cursor position
150 gint len = gtk_editable_get_position(edit);
151 gtk_editable_insert_text
152 (
153 edit,
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
157 );
158
159 // and move cursor to the end of new text
160 gtk_editable_set_position(edit, len);
161 }
162
163 void wxTextEntry::DoSetValue(const wxString& value, int flags)
164 {
165 if (value != DoGetValue())
166 {
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
170 {
171 EventsSuppressor noevents(this);
172 Remove(0, -1);
173 }
174 EventsSuppressor noeventsIf(this, !(flags & SetValue_SendEvent));
175 WriteText(value);
176 }
177 else if (flags & SetValue_SendEvent)
178 SendTextUpdatedEvent(GetEditableWindow());
179
180 SetInsertionPoint(0);
181 }
182
183 wxString wxTextEntry::DoGetValue() const
184 {
185 const wxGtkString value(gtk_editable_get_chars(GetEditable(), 0, -1));
186
187 return wxGTK_CONV_BACK_FONT(value,
188 const_cast<wxTextEntry *>(this)->GetEditableWindow()->GetFont());
189 }
190
191 void wxTextEntry::Remove(long from, long to)
192 {
193 gtk_editable_delete_text(GetEditable(), from, to);
194 }
195
196 // ----------------------------------------------------------------------------
197 // clipboard operations
198 // ----------------------------------------------------------------------------
199
200 void wxTextEntry::GTKConnectClipboardSignals(GtkWidget* entry)
201 {
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());
211 }
212
213 void wxTextEntry::Copy()
214 {
215 gtk_editable_copy_clipboard(GetEditable());
216 }
217
218 void wxTextEntry::Cut()
219 {
220 gtk_editable_cut_clipboard(GetEditable());
221 }
222
223 void wxTextEntry::Paste()
224 {
225 gtk_editable_paste_clipboard(GetEditable());
226 }
227
228 // ----------------------------------------------------------------------------
229 // undo/redo
230 // ----------------------------------------------------------------------------
231
232 void wxTextEntry::Undo()
233 {
234 // TODO: not implemented
235 }
236
237 void wxTextEntry::Redo()
238 {
239 // TODO: not implemented
240 }
241
242 bool wxTextEntry::CanUndo() const
243 {
244 return false;
245 }
246
247 bool wxTextEntry::CanRedo() const
248 {
249 return false;
250 }
251
252 // ----------------------------------------------------------------------------
253 // insertion point
254 // ----------------------------------------------------------------------------
255
256 void wxTextEntry::SetInsertionPoint(long pos)
257 {
258 gtk_editable_set_position(GetEditable(), pos);
259 }
260
261 long wxTextEntry::GetInsertionPoint() const
262 {
263 return gtk_editable_get_position(GetEditable());
264 }
265
266 long wxTextEntry::GetLastPosition() const
267 {
268 // this can't be implemented for arbitrary GtkEditable so only do it for
269 // GtkEntries
270 long pos = -1;
271 GtkEntry* entry = (GtkEntry*)GetEditable();
272 if (GTK_IS_ENTRY(entry))
273 pos = gtk_entry_get_text_length(entry);
274
275 return pos;
276 }
277
278 // ----------------------------------------------------------------------------
279 // selection
280 // ----------------------------------------------------------------------------
281
282 void wxTextEntry::SetSelection(long from, long to)
283 {
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 )
288 from = 0;
289
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);
294
295 #ifndef __WXGTK3__
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))
299 {
300 GtkEntry* entry = GTK_ENTRY(GetEditable());
301 if (to < 0)
302 to = entry->text_length;
303 entry->selection_bound = to;
304 }
305 #endif
306 }
307
308 void wxTextEntry::GetSelection(long *from, long *to) const
309 {
310 gint start, end;
311 if ( gtk_editable_get_selection_bounds(GetEditable(), &start, &end) )
312 {
313 // the output must always be in order, although in GTK+ it isn't
314 if ( start > end )
315 {
316 gint tmp = start;
317 start = end;
318 end = tmp;
319 }
320 }
321 else // no selection
322 {
323 // for compatibility with MSW return the empty selection at cursor
324 start =
325 end = GetInsertionPoint();
326 }
327
328 if ( from )
329 *from = start;
330
331 if ( to )
332 *to = end;
333 }
334
335 // ----------------------------------------------------------------------------
336 // auto completion
337 // ----------------------------------------------------------------------------
338
339 bool wxTextEntry::DoAutoCompleteStrings(const wxArrayString& choices)
340 {
341 GtkEntry* const entry = (GtkEntry*)GetEditable();
342 wxCHECK_MSG(GTK_IS_ENTRY(entry), false, "auto completion doesn't work with this control");
343
344 GtkListStore * const store = gtk_list_store_new(1, G_TYPE_STRING);
345 GtkTreeIter iter;
346
347 for ( wxArrayString::const_iterator i = choices.begin();
348 i != choices.end();
349 ++i )
350 {
351 gtk_list_store_append(store, &iter);
352 gtk_list_store_set(store, &iter,
353 0, (const gchar *)i->utf8_str(),
354 -1);
355 }
356
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);
362 return true;
363 }
364
365 // ----------------------------------------------------------------------------
366 // editable status
367 // ----------------------------------------------------------------------------
368
369 bool wxTextEntry::IsEditable() const
370 {
371 return gtk_editable_get_editable(GetEditable()) != 0;
372 }
373
374 void wxTextEntry::SetEditable(bool editable)
375 {
376 gtk_editable_set_editable(GetEditable(), editable);
377 }
378
379 // ----------------------------------------------------------------------------
380 // max text length
381 // ----------------------------------------------------------------------------
382
383 void wxTextEntry::SetMaxLength(unsigned long len)
384 {
385 GtkEntry* const entry = (GtkEntry*)GetEditable();
386 if (!GTK_IS_ENTRY(entry))
387 return;
388
389 gtk_entry_set_max_length(entry, len);
390
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
394 //
395 // to detect this and generate TEXT_MAXLEN event instead of TEXT_CHANGED
396 // one in this case we also catch "insert_text" signal
397 //
398 // when max len is set to 0 we disconnect our handler as it means that we
399 // shouldn't check anything any more
400 if ( len )
401 {
402 g_signal_connect
403 (
404 entry,
405 "insert_text",
406 G_CALLBACK(wx_gtk_insert_text_callback),
407 this
408 );
409 }
410 else // no max length
411 {
412 g_signal_handlers_disconnect_by_func
413 (
414 entry,
415 (gpointer)wx_gtk_insert_text_callback,
416 this
417 );
418 }
419 }
420
421 void wxTextEntry::SendMaxLenEvent()
422 {
423 // remember that the next changed signal is to be ignored to avoid
424 // generating a dummy wxEVT_COMMAND_TEXT_UPDATED event
425 //IgnoreNextTextUpdate();
426
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);
432 }
433
434 // ----------------------------------------------------------------------------
435 // margins support
436 // ----------------------------------------------------------------------------
437
438 bool wxTextEntry::DoSetMargins(const wxPoint& margins)
439 {
440 #if GTK_CHECK_VERSION(2,10,0)
441 GtkEntry* entry = GetEntry();
442
443 if ( !entry )
444 return false;
445
446 const GtkBorder* oldBorder = gtk_entry_get_inner_border(entry);
447 GtkBorder* newBorder;
448
449 if ( oldBorder )
450 {
451 newBorder = gtk_border_copy(oldBorder);
452 }
453 else
454 {
455 #if GTK_CHECK_VERSION(2,14,0)
456 newBorder = gtk_border_new();
457 #else
458 newBorder = g_slice_new0(GtkBorder);
459 #endif
460 // Use some reasonable defaults for initial margins
461 newBorder->left = 2;
462 newBorder->right = 2;
463
464 // These numbers seem to let the text remain vertically centered
465 // in common use scenarios when margins.y == -1.
466 newBorder->top = 3;
467 newBorder->bottom = 3;
468 }
469
470 if ( margins.x != -1 )
471 newBorder->left = (gint) margins.x;
472
473 if ( margins.y != -1 )
474 newBorder->top = (gint) margins.y;
475
476 gtk_entry_set_inner_border(entry, newBorder);
477
478 #if GTK_CHECK_VERSION(2,14,0)
479 gtk_border_free(newBorder);
480 #else
481 g_slice_free(GtkBorder, newBorder);
482 #endif
483
484 return true;
485 #else
486 wxUnusedVar(margins);
487 return false;
488 #endif
489 }
490
491 wxPoint wxTextEntry::DoGetMargins() const
492 {
493 #if GTK_CHECK_VERSION(2,10,0)
494 GtkEntry* entry = GetEntry();
495
496 if ( !entry )
497 return wxPoint(-1, -1);
498
499 const GtkBorder* border = gtk_entry_get_inner_border(entry);
500
501 if ( !border )
502 return wxPoint(-1, -1);
503
504 return wxPoint((wxCoord) border->left, (wxCoord) border->top);
505 #else
506 return wxPoint(-1, -1);
507 #endif
508 }
509
510 #endif // wxUSE_TEXTCTRL || wxUSE_COMBOBOX