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