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