]> git.saurik.com Git - wxWidgets.git/blame - src/gtk/textentry.cpp
simplify code to return from the end of the function
[wxWidgets.git] / src / gtk / textentry.cpp
CommitLineData
0ec1179b
VZ
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
96a4cdeb
VZ
26#if wxUSE_TEXTCTRL || wxUSE_COMBOBOX
27
0ec1179b 28#ifndef WX_PRECOMP
9dc44eff 29 #include "wx/textentry.h"
d290d2fe
RR
30 #include "wx/window.h"
31 #include "wx/textctrl.h"
0ec1179b
VZ
32#endif //WX_PRECOMP
33
9dc44eff 34#include <gtk/gtk.h>
0ec1179b 35#include "wx/gtk/private.h"
9dc44eff 36#include "wx/gtk/private/gtk2-compat.h"
0ec1179b
VZ
37
38// ============================================================================
39// signal handlers implementation
40// ============================================================================
41
0ec1179b 42// "insert_text" handler for GtkEntry
10434560
VZ
43extern "C"
44void
0ec1179b 45wx_gtk_insert_text_callback(GtkEditable *editable,
656b9f44 46 const gchar * new_text,
e4161a2a
VZ
47 gint WXUNUSED(new_text_length),
48 gint * WXUNUSED(position),
0ec1179b
VZ
49 wxTextEntry *text)
50{
0ec1179b
VZ
51 GtkEntry *entry = GTK_ENTRY (editable);
52
385e8575
PC
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
656b9f44
VZ
58
59 // we should only be called if we have a max len limit at all
385e8575 60 wxCHECK_RET(text_max_length, "shouldn't be called");
0ec1179b
VZ
61
62 // check that we don't overflow the max length limit
656b9f44
VZ
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 )
0ec1179b 69 {
656b9f44 70 // Prevent the new text from being inserted.
0ec1179b
VZ
71 g_signal_stop_emission_by_name (editable, "insert_text");
72
656b9f44
VZ
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.
0ec1179b
VZ
78 text->SendMaxLenEvent();
79 }
80}
81
10434560
VZ
82//-----------------------------------------------------------------------------
83// clipboard events: "copy-clipboard", "cut-clipboard", "paste-clipboard"
84//-----------------------------------------------------------------------------
85
86// common part of the event handlers below
87static void
88DoHandleClipboardCallback( 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
103extern "C"
104{
105
106static void
107wx_gtk_copy_clipboard_callback( GtkWidget *widget, wxWindow *win )
108{
109 DoHandleClipboardCallback(
110 widget, win, wxEVT_COMMAND_TEXT_COPY, "copy-clipboard" );
111}
112
113static void
114wx_gtk_cut_clipboard_callback( GtkWidget *widget, wxWindow *win )
115{
116 DoHandleClipboardCallback(
117 widget, win, wxEVT_COMMAND_TEXT_CUT, "cut-clipboard" );
118}
119
120static void
121wx_gtk_paste_clipboard_callback( GtkWidget *widget, wxWindow *win )
122{
123 DoHandleClipboardCallback(
124 widget, win, wxEVT_COMMAND_TEXT_PASTE, "paste-clipboard" );
125}
126
0ec1179b
VZ
127} // extern "C"
128
129// ============================================================================
130// wxTextEntry implementation
131// ============================================================================
132
133// ----------------------------------------------------------------------------
134// text operations
135// ----------------------------------------------------------------------------
136
137void 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
75377698
PC
163void wxTextEntry::DoSetValue(const wxString& value, int flags)
164{
155ce4f1 165 if (value != DoGetValue())
75377698
PC
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
135b23b2 183wxString wxTextEntry::DoGetValue() const
0ec1179b
VZ
184{
185 const wxGtkString value(gtk_editable_get_chars(GetEditable(), 0, -1));
186
ec592d7f
VZ
187 return wxGTK_CONV_BACK_FONT(value,
188 const_cast<wxTextEntry *>(this)->GetEditableWindow()->GetFont());
0ec1179b
VZ
189}
190
191void wxTextEntry::Remove(long from, long to)
192{
193 gtk_editable_delete_text(GetEditable(), from, to);
194}
195
196// ----------------------------------------------------------------------------
197// clipboard operations
198// ----------------------------------------------------------------------------
199
10434560
VZ
200void 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
0ec1179b
VZ
213void wxTextEntry::Copy()
214{
215 gtk_editable_copy_clipboard(GetEditable());
216}
217
218void wxTextEntry::Cut()
219{
220 gtk_editable_cut_clipboard(GetEditable());
221}
222
223void wxTextEntry::Paste()
224{
225 gtk_editable_paste_clipboard(GetEditable());
226}
227
228// ----------------------------------------------------------------------------
229// undo/redo
230// ----------------------------------------------------------------------------
231
232void wxTextEntry::Undo()
233{
234 // TODO: not implemented
235}
236
237void wxTextEntry::Redo()
238{
239 // TODO: not implemented
240}
241
242bool wxTextEntry::CanUndo() const
243{
244 return false;
245}
246
247bool wxTextEntry::CanRedo() const
248{
249 return false;
250}
251
252// ----------------------------------------------------------------------------
253// insertion point
254// ----------------------------------------------------------------------------
255
256void wxTextEntry::SetInsertionPoint(long pos)
257{
258 gtk_editable_set_position(GetEditable(), pos);
259}
260
261long wxTextEntry::GetInsertionPoint() const
262{
263 return gtk_editable_get_position(GetEditable());
264}
265
266long wxTextEntry::GetLastPosition() const
267{
268 // this can't be implemented for arbitrary GtkEditable so only do it for
269 // GtkEntries
d962569a 270 long pos = -1;
7e3e162f
PC
271 GtkEntry* entry = (GtkEntry*)GetEditable();
272 if (GTK_IS_ENTRY(entry))
273 pos = gtk_entry_get_text_length(entry);
0ec1179b 274
d962569a 275 return pos;
0ec1179b
VZ
276}
277
278// ----------------------------------------------------------------------------
279// selection
280// ----------------------------------------------------------------------------
281
282void wxTextEntry::SetSelection(long from, long to)
283{
e0721133
VZ
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
8b2e0e6d
VZ
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);
3f6439ae 294
9dc44eff 295#ifndef __WXGTK3__
3f6439ae
PC
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 }
9dc44eff 305#endif
0ec1179b
VZ
306}
307
308void 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// ----------------------------------------------------------------------------
ecaed0bc
VZ
336// auto completion
337// ----------------------------------------------------------------------------
338
574479e8 339bool wxTextEntry::DoAutoCompleteStrings(const wxArrayString& choices)
ecaed0bc 340{
7e3e162f
PC
341 GtkEntry* const entry = (GtkEntry*)GetEditable();
342 wxCHECK_MSG(GTK_IS_ENTRY(entry), false, "auto completion doesn't work with this control");
ecaed0bc
VZ
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);
0c3d1aa7 362 return true;
ecaed0bc
VZ
363}
364
365// ----------------------------------------------------------------------------
0ec1179b
VZ
366// editable status
367// ----------------------------------------------------------------------------
368
369bool wxTextEntry::IsEditable() const
370{
d5027818 371 return gtk_editable_get_editable(GetEditable()) != 0;
0ec1179b
VZ
372}
373
374void wxTextEntry::SetEditable(bool editable)
375{
376 gtk_editable_set_editable(GetEditable(), editable);
377}
378
379// ----------------------------------------------------------------------------
380// max text length
381// ----------------------------------------------------------------------------
382
383void wxTextEntry::SetMaxLength(unsigned long len)
384{
7e3e162f
PC
385 GtkEntry* const entry = (GtkEntry*)GetEditable();
386 if (!GTK_IS_ENTRY(entry))
0ec1179b
VZ
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
421void 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
ec592d7f 427 wxWindow * const win = GetEditableWindow();
0ec1179b
VZ
428 wxCommandEvent event(wxEVT_COMMAND_TEXT_MAXLEN, win->GetId());
429 event.SetEventObject(win);
430 event.SetString(GetValue());
937013e0 431 win->HandleWindowEvent(event);
0ec1179b
VZ
432}
433
0847e36e
JS
434// ----------------------------------------------------------------------------
435// margins support
436// ----------------------------------------------------------------------------
437
438bool 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
c39b6e1e 458 newBorder = g_slice_new0(GtkBorder);
0847e36e
JS
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
c39b6e1e
JS
478#if GTK_CHECK_VERSION(2,14,0)
479 gtk_border_free(newBorder);
480#else
481 g_slice_free(GtkBorder, newBorder);
482#endif
483
0847e36e
JS
484 return true;
485#else
486 wxUnusedVar(margins);
487 return false;
488#endif
489}
490
491wxPoint 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
96a4cdeb 510#endif // wxUSE_TEXTCTRL || wxUSE_COMBOBOX