]> git.saurik.com Git - wxWidgets.git/blob - src/gtk/textentry.cpp
support for GTK3
[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 wxString wxTextEntry::DoGetValue() const
114 {
115 const wxGtkString value(gtk_editable_get_chars(GetEditable(), 0, -1));
116
117 return wxGTK_CONV_BACK_FONT(value,
118 const_cast<wxTextEntry *>(this)->GetEditableWindow()->GetFont());
119 }
120
121 void wxTextEntry::Remove(long from, long to)
122 {
123 gtk_editable_delete_text(GetEditable(), from, to);
124 }
125
126 // ----------------------------------------------------------------------------
127 // clipboard operations
128 // ----------------------------------------------------------------------------
129
130 void wxTextEntry::Copy()
131 {
132 gtk_editable_copy_clipboard(GetEditable());
133 }
134
135 void wxTextEntry::Cut()
136 {
137 gtk_editable_cut_clipboard(GetEditable());
138 }
139
140 void wxTextEntry::Paste()
141 {
142 gtk_editable_paste_clipboard(GetEditable());
143 }
144
145 // ----------------------------------------------------------------------------
146 // undo/redo
147 // ----------------------------------------------------------------------------
148
149 void wxTextEntry::Undo()
150 {
151 // TODO: not implemented
152 }
153
154 void wxTextEntry::Redo()
155 {
156 // TODO: not implemented
157 }
158
159 bool wxTextEntry::CanUndo() const
160 {
161 return false;
162 }
163
164 bool wxTextEntry::CanRedo() const
165 {
166 return false;
167 }
168
169 // ----------------------------------------------------------------------------
170 // insertion point
171 // ----------------------------------------------------------------------------
172
173 void wxTextEntry::SetInsertionPoint(long pos)
174 {
175 gtk_editable_set_position(GetEditable(), pos);
176 }
177
178 long wxTextEntry::GetInsertionPoint() const
179 {
180 return gtk_editable_get_position(GetEditable());
181 }
182
183 long wxTextEntry::GetLastPosition() const
184 {
185 // this can't be implemented for arbitrary GtkEditable so only do it for
186 // GtkEntries
187 GtkEntry * const entry = GTK_ENTRY(GetEditable());
188
189 return entry ? gtk_entry_get_text_length(entry) : -1;
190 }
191
192 // ----------------------------------------------------------------------------
193 // selection
194 // ----------------------------------------------------------------------------
195
196 void wxTextEntry::SetSelection(long from, long to)
197 {
198 // in wx convention, (-1, -1) means the entire range but GTK+ translates -1
199 // (or any negative number for that matter) into last position so we need
200 // to translate manually
201 if ( from == -1 && to == -1 )
202 from = 0;
203
204 // for compatibility with MSW, exchange from and to parameters so that the
205 // insertion point is set to the start of the selection and not its end as
206 // GTK+ does by default
207 gtk_editable_select_region(GetEditable(), to, from);
208
209 #ifndef __WXGTK3__
210 // avoid reported problem with RHEL 5 GTK+ 2.10 where selection is reset by
211 // a clipboard callback, see #13277
212 if (gtk_check_version(2,12,0))
213 {
214 GtkEntry* entry = GTK_ENTRY(GetEditable());
215 if (to < 0)
216 to = entry->text_length;
217 entry->selection_bound = to;
218 }
219 #endif
220 }
221
222 void wxTextEntry::GetSelection(long *from, long *to) const
223 {
224 gint start, end;
225 if ( gtk_editable_get_selection_bounds(GetEditable(), &start, &end) )
226 {
227 // the output must always be in order, although in GTK+ it isn't
228 if ( start > end )
229 {
230 gint tmp = start;
231 start = end;
232 end = tmp;
233 }
234 }
235 else // no selection
236 {
237 // for compatibility with MSW return the empty selection at cursor
238 start =
239 end = GetInsertionPoint();
240 }
241
242 if ( from )
243 *from = start;
244
245 if ( to )
246 *to = end;
247 }
248
249 // ----------------------------------------------------------------------------
250 // auto completion
251 // ----------------------------------------------------------------------------
252
253 bool wxTextEntry::DoAutoCompleteStrings(const wxArrayString& choices)
254 {
255 GtkEntry * const entry = GTK_ENTRY(GetEditable());
256 wxCHECK_MSG(entry, false, "auto completion doesn't work with this control");
257
258 GtkListStore * const store = gtk_list_store_new(1, G_TYPE_STRING);
259 GtkTreeIter iter;
260
261 for ( wxArrayString::const_iterator i = choices.begin();
262 i != choices.end();
263 ++i )
264 {
265 gtk_list_store_append(store, &iter);
266 gtk_list_store_set(store, &iter,
267 0, (const gchar *)i->utf8_str(),
268 -1);
269 }
270
271 GtkEntryCompletion * const completion = gtk_entry_completion_new();
272 gtk_entry_completion_set_model(completion, GTK_TREE_MODEL(store));
273 gtk_entry_completion_set_text_column(completion, 0);
274 gtk_entry_set_completion(entry, completion);
275 g_object_unref(completion);
276 return true;
277 }
278
279 // ----------------------------------------------------------------------------
280 // editable status
281 // ----------------------------------------------------------------------------
282
283 bool wxTextEntry::IsEditable() const
284 {
285 return gtk_editable_get_editable(GetEditable()) != 0;
286 }
287
288 void wxTextEntry::SetEditable(bool editable)
289 {
290 gtk_editable_set_editable(GetEditable(), editable);
291 }
292
293 // ----------------------------------------------------------------------------
294 // max text length
295 // ----------------------------------------------------------------------------
296
297 void wxTextEntry::SetMaxLength(unsigned long len)
298 {
299 GtkEntry * const entry = GTK_ENTRY(GetEditable());
300 if ( !entry )
301 return;
302
303 gtk_entry_set_max_length(entry, len);
304
305 // there is a bug in GTK+ 1.2.x: "changed" signal is emitted even if we had
306 // tried to enter more text than allowed by max text length and the text
307 // wasn't really changed
308 //
309 // to detect this and generate TEXT_MAXLEN event instead of TEXT_CHANGED
310 // one in this case we also catch "insert_text" signal
311 //
312 // when max len is set to 0 we disconnect our handler as it means that we
313 // shouldn't check anything any more
314 if ( len )
315 {
316 g_signal_connect
317 (
318 entry,
319 "insert_text",
320 G_CALLBACK(wx_gtk_insert_text_callback),
321 this
322 );
323 }
324 else // no max length
325 {
326 g_signal_handlers_disconnect_by_func
327 (
328 entry,
329 (gpointer)wx_gtk_insert_text_callback,
330 this
331 );
332 }
333 }
334
335 void wxTextEntry::SendMaxLenEvent()
336 {
337 // remember that the next changed signal is to be ignored to avoid
338 // generating a dummy wxEVT_COMMAND_TEXT_UPDATED event
339 //IgnoreNextTextUpdate();
340
341 wxWindow * const win = GetEditableWindow();
342 wxCommandEvent event(wxEVT_COMMAND_TEXT_MAXLEN, win->GetId());
343 event.SetEventObject(win);
344 event.SetString(GetValue());
345 win->HandleWindowEvent(event);
346 }
347
348 // ----------------------------------------------------------------------------
349 // margins support
350 // ----------------------------------------------------------------------------
351
352 bool wxTextEntry::DoSetMargins(const wxPoint& margins)
353 {
354 #if GTK_CHECK_VERSION(2,10,0)
355 GtkEntry* entry = GetEntry();
356
357 if ( !entry )
358 return false;
359
360 const GtkBorder* oldBorder = gtk_entry_get_inner_border(entry);
361 GtkBorder* newBorder;
362
363 if ( oldBorder )
364 {
365 newBorder = gtk_border_copy(oldBorder);
366 }
367 else
368 {
369 #if GTK_CHECK_VERSION(2,14,0)
370 newBorder = gtk_border_new();
371 #else
372 newBorder = g_slice_new0(GtkBorder);
373 #endif
374 // Use some reasonable defaults for initial margins
375 newBorder->left = 2;
376 newBorder->right = 2;
377
378 // These numbers seem to let the text remain vertically centered
379 // in common use scenarios when margins.y == -1.
380 newBorder->top = 3;
381 newBorder->bottom = 3;
382 }
383
384 if ( margins.x != -1 )
385 newBorder->left = (gint) margins.x;
386
387 if ( margins.y != -1 )
388 newBorder->top = (gint) margins.y;
389
390 gtk_entry_set_inner_border(entry, newBorder);
391
392 #if GTK_CHECK_VERSION(2,14,0)
393 gtk_border_free(newBorder);
394 #else
395 g_slice_free(GtkBorder, newBorder);
396 #endif
397
398 return true;
399 #else
400 wxUnusedVar(margins);
401 return false;
402 #endif
403 }
404
405 wxPoint wxTextEntry::DoGetMargins() const
406 {
407 #if GTK_CHECK_VERSION(2,10,0)
408 GtkEntry* entry = GetEntry();
409
410 if ( !entry )
411 return wxPoint(-1, -1);
412
413 const GtkBorder* border = gtk_entry_get_inner_border(entry);
414
415 if ( !border )
416 return wxPoint(-1, -1);
417
418 return wxPoint((wxCoord) border->left, (wxCoord) border->top);
419 #else
420 return wxPoint(-1, -1);
421 #endif
422 }
423
424 #endif // wxUSE_TEXTCTRL || wxUSE_COMBOBOX