fix some more improper widget casts
[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 long pos = -1;
208 GtkEntry* entry = (GtkEntry*)GetEditable();
209 if (GTK_IS_ENTRY(entry))
210 pos = gtk_entry_get_text_length(entry);
211
212 return pos;
213 }
214
215 // ----------------------------------------------------------------------------
216 // selection
217 // ----------------------------------------------------------------------------
218
219 void wxTextEntry::SetSelection(long from, long to)
220 {
221 // in wx convention, (-1, -1) means the entire range but GTK+ translates -1
222 // (or any negative number for that matter) into last position so we need
223 // to translate manually
224 if ( from == -1 && to == -1 )
225 from = 0;
226
227 // for compatibility with MSW, exchange from and to parameters so that the
228 // insertion point is set to the start of the selection and not its end as
229 // GTK+ does by default
230 gtk_editable_select_region(GetEditable(), to, from);
231
232 #ifndef __WXGTK3__
233 // avoid reported problem with RHEL 5 GTK+ 2.10 where selection is reset by
234 // a clipboard callback, see #13277
235 if (gtk_check_version(2,12,0))
236 {
237 GtkEntry* entry = GTK_ENTRY(GetEditable());
238 if (to < 0)
239 to = entry->text_length;
240 entry->selection_bound = to;
241 }
242 #endif
243 }
244
245 void wxTextEntry::GetSelection(long *from, long *to) const
246 {
247 gint start, end;
248 if ( gtk_editable_get_selection_bounds(GetEditable(), &start, &end) )
249 {
250 // the output must always be in order, although in GTK+ it isn't
251 if ( start > end )
252 {
253 gint tmp = start;
254 start = end;
255 end = tmp;
256 }
257 }
258 else // no selection
259 {
260 // for compatibility with MSW return the empty selection at cursor
261 start =
262 end = GetInsertionPoint();
263 }
264
265 if ( from )
266 *from = start;
267
268 if ( to )
269 *to = end;
270 }
271
272 // ----------------------------------------------------------------------------
273 // auto completion
274 // ----------------------------------------------------------------------------
275
276 bool wxTextEntry::DoAutoCompleteStrings(const wxArrayString& choices)
277 {
278 GtkEntry* const entry = (GtkEntry*)GetEditable();
279 wxCHECK_MSG(GTK_IS_ENTRY(entry), false, "auto completion doesn't work with this control");
280
281 GtkListStore * const store = gtk_list_store_new(1, G_TYPE_STRING);
282 GtkTreeIter iter;
283
284 for ( wxArrayString::const_iterator i = choices.begin();
285 i != choices.end();
286 ++i )
287 {
288 gtk_list_store_append(store, &iter);
289 gtk_list_store_set(store, &iter,
290 0, (const gchar *)i->utf8_str(),
291 -1);
292 }
293
294 GtkEntryCompletion * const completion = gtk_entry_completion_new();
295 gtk_entry_completion_set_model(completion, GTK_TREE_MODEL(store));
296 gtk_entry_completion_set_text_column(completion, 0);
297 gtk_entry_set_completion(entry, completion);
298 g_object_unref(completion);
299 return true;
300 }
301
302 // ----------------------------------------------------------------------------
303 // editable status
304 // ----------------------------------------------------------------------------
305
306 bool wxTextEntry::IsEditable() const
307 {
308 return gtk_editable_get_editable(GetEditable()) != 0;
309 }
310
311 void wxTextEntry::SetEditable(bool editable)
312 {
313 gtk_editable_set_editable(GetEditable(), editable);
314 }
315
316 // ----------------------------------------------------------------------------
317 // max text length
318 // ----------------------------------------------------------------------------
319
320 void wxTextEntry::SetMaxLength(unsigned long len)
321 {
322 GtkEntry* const entry = (GtkEntry*)GetEditable();
323 if (!GTK_IS_ENTRY(entry))
324 return;
325
326 gtk_entry_set_max_length(entry, len);
327
328 // there is a bug in GTK+ 1.2.x: "changed" signal is emitted even if we had
329 // tried to enter more text than allowed by max text length and the text
330 // wasn't really changed
331 //
332 // to detect this and generate TEXT_MAXLEN event instead of TEXT_CHANGED
333 // one in this case we also catch "insert_text" signal
334 //
335 // when max len is set to 0 we disconnect our handler as it means that we
336 // shouldn't check anything any more
337 if ( len )
338 {
339 g_signal_connect
340 (
341 entry,
342 "insert_text",
343 G_CALLBACK(wx_gtk_insert_text_callback),
344 this
345 );
346 }
347 else // no max length
348 {
349 g_signal_handlers_disconnect_by_func
350 (
351 entry,
352 (gpointer)wx_gtk_insert_text_callback,
353 this
354 );
355 }
356 }
357
358 void wxTextEntry::SendMaxLenEvent()
359 {
360 // remember that the next changed signal is to be ignored to avoid
361 // generating a dummy wxEVT_COMMAND_TEXT_UPDATED event
362 //IgnoreNextTextUpdate();
363
364 wxWindow * const win = GetEditableWindow();
365 wxCommandEvent event(wxEVT_COMMAND_TEXT_MAXLEN, win->GetId());
366 event.SetEventObject(win);
367 event.SetString(GetValue());
368 win->HandleWindowEvent(event);
369 }
370
371 // ----------------------------------------------------------------------------
372 // margins support
373 // ----------------------------------------------------------------------------
374
375 bool wxTextEntry::DoSetMargins(const wxPoint& margins)
376 {
377 #if GTK_CHECK_VERSION(2,10,0)
378 GtkEntry* entry = GetEntry();
379
380 if ( !entry )
381 return false;
382
383 const GtkBorder* oldBorder = gtk_entry_get_inner_border(entry);
384 GtkBorder* newBorder;
385
386 if ( oldBorder )
387 {
388 newBorder = gtk_border_copy(oldBorder);
389 }
390 else
391 {
392 #if GTK_CHECK_VERSION(2,14,0)
393 newBorder = gtk_border_new();
394 #else
395 newBorder = g_slice_new0(GtkBorder);
396 #endif
397 // Use some reasonable defaults for initial margins
398 newBorder->left = 2;
399 newBorder->right = 2;
400
401 // These numbers seem to let the text remain vertically centered
402 // in common use scenarios when margins.y == -1.
403 newBorder->top = 3;
404 newBorder->bottom = 3;
405 }
406
407 if ( margins.x != -1 )
408 newBorder->left = (gint) margins.x;
409
410 if ( margins.y != -1 )
411 newBorder->top = (gint) margins.y;
412
413 gtk_entry_set_inner_border(entry, newBorder);
414
415 #if GTK_CHECK_VERSION(2,14,0)
416 gtk_border_free(newBorder);
417 #else
418 g_slice_free(GtkBorder, newBorder);
419 #endif
420
421 return true;
422 #else
423 wxUnusedVar(margins);
424 return false;
425 #endif
426 }
427
428 wxPoint wxTextEntry::DoGetMargins() const
429 {
430 #if GTK_CHECK_VERSION(2,10,0)
431 GtkEntry* entry = GetEntry();
432
433 if ( !entry )
434 return wxPoint(-1, -1);
435
436 const GtkBorder* border = gtk_entry_get_inner_border(entry);
437
438 if ( !border )
439 return wxPoint(-1, -1);
440
441 return wxPoint((wxCoord) border->left, (wxCoord) border->top);
442 #else
443 return wxPoint(-1, -1);
444 #endif
445 }
446
447 #endif // wxUSE_TEXTCTRL || wxUSE_COMBOBOX