]> git.saurik.com Git - wxWidgets.git/blame - src/gtk/textentry.cpp
use GTK-specific method to delete selection
[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 58
b2c35774 59 bool handled = false;
0ec1179b 60
b2c35774
VZ
61 // check that we don't overflow the max length limit if we have it
62 if ( text_max_length )
63 {
64 const int text_length = gtk_entry_get_text_length(entry);
656b9f44 65
b2c35774
VZ
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 )
69 {
70 // Prevent the new text from being inserted.
71 handled = true;
656b9f44 72
b2c35774
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.
0ec1179b 76
b2c35774
VZ
77 // Notify the user code about overflow.
78 text->SendMaxLenEvent();
79 }
80 }
656b9f44 81
b2c35774
VZ
82 if ( !handled && text->GTKEntryOnInsertText(new_text) )
83 {
84 // If we already handled the new text insertion, don't do it again.
85 handled = true;
0ec1179b 86 }
b2c35774
VZ
87
88 if ( handled )
89 g_signal_stop_emission_by_name (editable, "insert_text");
0ec1179b
VZ
90}
91
10434560
VZ
92//-----------------------------------------------------------------------------
93// clipboard events: "copy-clipboard", "cut-clipboard", "paste-clipboard"
94//-----------------------------------------------------------------------------
95
96// common part of the event handlers below
97static void
98DoHandleClipboardCallback( GtkWidget *widget,
99 wxWindow *win,
100 wxEventType eventType,
101 const gchar* signal_name)
102{
103 wxClipboardTextEvent event( eventType, win->GetId() );
104 event.SetEventObject( win );
105 if ( win->HandleWindowEvent( event ) )
106 {
107 // don't let the default processing to take place if we did something
108 // ourselves in the event handler
109 g_signal_stop_emission_by_name (widget, signal_name);
110 }
111}
112
113extern "C"
114{
115
116static void
117wx_gtk_copy_clipboard_callback( GtkWidget *widget, wxWindow *win )
118{
119 DoHandleClipboardCallback(
ce7fe42e 120 widget, win, wxEVT_TEXT_COPY, "copy-clipboard" );
10434560
VZ
121}
122
123static void
124wx_gtk_cut_clipboard_callback( GtkWidget *widget, wxWindow *win )
125{
126 DoHandleClipboardCallback(
ce7fe42e 127 widget, win, wxEVT_TEXT_CUT, "cut-clipboard" );
10434560
VZ
128}
129
130static void
131wx_gtk_paste_clipboard_callback( GtkWidget *widget, wxWindow *win )
132{
133 DoHandleClipboardCallback(
ce7fe42e 134 widget, win, wxEVT_TEXT_PASTE, "paste-clipboard" );
10434560
VZ
135}
136
0ec1179b
VZ
137} // extern "C"
138
139// ============================================================================
140// wxTextEntry implementation
141// ============================================================================
142
143// ----------------------------------------------------------------------------
144// text operations
145// ----------------------------------------------------------------------------
146
147void wxTextEntry::WriteText(const wxString& value)
148{
149 GtkEditable * const edit = GetEditable();
150
151 // remove the selection if there is one and suppress the text change event
152 // generated by this: we only want to generate one event for this change,
153 // not two
154 {
155 EventsSuppressor noevents(this);
156 gtk_editable_delete_selection(edit);
157 }
158
159 // insert new text at the cursor position
160 gint len = gtk_editable_get_position(edit);
161 gtk_editable_insert_text
162 (
163 edit,
164 wxGTK_CONV_FONT(value, GetEditableWindow()->GetFont()),
165 -1, // text: length: compute it using strlen()
166 &len // will be updated to position after the text end
167 );
168
169 // and move cursor to the end of new text
170 gtk_editable_set_position(edit, len);
171}
172
75377698
PC
173void wxTextEntry::DoSetValue(const wxString& value, int flags)
174{
155ce4f1 175 if (value != DoGetValue())
75377698
PC
176 {
177 // use Remove() rather than SelectAll() to avoid unnecessary clipboard
178 // operations, and prevent triggering an apparent bug in GTK which
179 // causes the the subsequent WriteText() to append rather than overwrite
180 {
181 EventsSuppressor noevents(this);
182 Remove(0, -1);
183 }
184 EventsSuppressor noeventsIf(this, !(flags & SetValue_SendEvent));
185 WriteText(value);
186 }
187 else if (flags & SetValue_SendEvent)
188 SendTextUpdatedEvent(GetEditableWindow());
189
190 SetInsertionPoint(0);
191}
192
135b23b2 193wxString wxTextEntry::DoGetValue() const
0ec1179b
VZ
194{
195 const wxGtkString value(gtk_editable_get_chars(GetEditable(), 0, -1));
196
ec592d7f
VZ
197 return wxGTK_CONV_BACK_FONT(value,
198 const_cast<wxTextEntry *>(this)->GetEditableWindow()->GetFont());
0ec1179b
VZ
199}
200
201void wxTextEntry::Remove(long from, long to)
202{
203 gtk_editable_delete_text(GetEditable(), from, to);
204}
205
206// ----------------------------------------------------------------------------
207// clipboard operations
208// ----------------------------------------------------------------------------
209
10434560
VZ
210void wxTextEntry::GTKConnectClipboardSignals(GtkWidget* entry)
211{
212 g_signal_connect(entry, "copy-clipboard",
213 G_CALLBACK (wx_gtk_copy_clipboard_callback),
214 GetEditableWindow());
215 g_signal_connect(entry, "cut-clipboard",
216 G_CALLBACK (wx_gtk_cut_clipboard_callback),
217 GetEditableWindow());
218 g_signal_connect(entry, "paste-clipboard",
219 G_CALLBACK (wx_gtk_paste_clipboard_callback),
220 GetEditableWindow());
221}
222
0ec1179b
VZ
223void wxTextEntry::Copy()
224{
225 gtk_editable_copy_clipboard(GetEditable());
226}
227
228void wxTextEntry::Cut()
229{
230 gtk_editable_cut_clipboard(GetEditable());
231}
232
233void wxTextEntry::Paste()
234{
235 gtk_editable_paste_clipboard(GetEditable());
236}
237
238// ----------------------------------------------------------------------------
239// undo/redo
240// ----------------------------------------------------------------------------
241
242void wxTextEntry::Undo()
243{
244 // TODO: not implemented
245}
246
247void wxTextEntry::Redo()
248{
249 // TODO: not implemented
250}
251
252bool wxTextEntry::CanUndo() const
253{
254 return false;
255}
256
257bool wxTextEntry::CanRedo() const
258{
259 return false;
260}
261
262// ----------------------------------------------------------------------------
263// insertion point
264// ----------------------------------------------------------------------------
265
266void wxTextEntry::SetInsertionPoint(long pos)
267{
268 gtk_editable_set_position(GetEditable(), pos);
269}
270
271long wxTextEntry::GetInsertionPoint() const
272{
273 return gtk_editable_get_position(GetEditable());
274}
275
276long wxTextEntry::GetLastPosition() const
277{
278 // this can't be implemented for arbitrary GtkEditable so only do it for
279 // GtkEntries
d962569a 280 long pos = -1;
7e3e162f
PC
281 GtkEntry* entry = (GtkEntry*)GetEditable();
282 if (GTK_IS_ENTRY(entry))
283 pos = gtk_entry_get_text_length(entry);
0ec1179b 284
d962569a 285 return pos;
0ec1179b
VZ
286}
287
288// ----------------------------------------------------------------------------
289// selection
290// ----------------------------------------------------------------------------
291
292void wxTextEntry::SetSelection(long from, long to)
293{
e0721133
VZ
294 // in wx convention, (-1, -1) means the entire range but GTK+ translates -1
295 // (or any negative number for that matter) into last position so we need
296 // to translate manually
297 if ( from == -1 && to == -1 )
298 from = 0;
299
8b2e0e6d
VZ
300 // for compatibility with MSW, exchange from and to parameters so that the
301 // insertion point is set to the start of the selection and not its end as
302 // GTK+ does by default
303 gtk_editable_select_region(GetEditable(), to, from);
3f6439ae 304
9dc44eff 305#ifndef __WXGTK3__
3f6439ae
PC
306 // avoid reported problem with RHEL 5 GTK+ 2.10 where selection is reset by
307 // a clipboard callback, see #13277
308 if (gtk_check_version(2,12,0))
309 {
310 GtkEntry* entry = GTK_ENTRY(GetEditable());
311 if (to < 0)
312 to = entry->text_length;
313 entry->selection_bound = to;
314 }
9dc44eff 315#endif
0ec1179b
VZ
316}
317
318void wxTextEntry::GetSelection(long *from, long *to) const
319{
320 gint start, end;
321 if ( gtk_editable_get_selection_bounds(GetEditable(), &start, &end) )
322 {
323 // the output must always be in order, although in GTK+ it isn't
324 if ( start > end )
325 {
326 gint tmp = start;
327 start = end;
328 end = tmp;
329 }
330 }
331 else // no selection
332 {
333 // for compatibility with MSW return the empty selection at cursor
334 start =
335 end = GetInsertionPoint();
336 }
337
338 if ( from )
339 *from = start;
340
341 if ( to )
342 *to = end;
343}
344
345// ----------------------------------------------------------------------------
ecaed0bc
VZ
346// auto completion
347// ----------------------------------------------------------------------------
348
574479e8 349bool wxTextEntry::DoAutoCompleteStrings(const wxArrayString& choices)
ecaed0bc 350{
7e3e162f
PC
351 GtkEntry* const entry = (GtkEntry*)GetEditable();
352 wxCHECK_MSG(GTK_IS_ENTRY(entry), false, "auto completion doesn't work with this control");
ecaed0bc
VZ
353
354 GtkListStore * const store = gtk_list_store_new(1, G_TYPE_STRING);
355 GtkTreeIter iter;
356
357 for ( wxArrayString::const_iterator i = choices.begin();
358 i != choices.end();
359 ++i )
360 {
361 gtk_list_store_append(store, &iter);
362 gtk_list_store_set(store, &iter,
363 0, (const gchar *)i->utf8_str(),
364 -1);
365 }
366
367 GtkEntryCompletion * const completion = gtk_entry_completion_new();
368 gtk_entry_completion_set_model(completion, GTK_TREE_MODEL(store));
369 gtk_entry_completion_set_text_column(completion, 0);
370 gtk_entry_set_completion(entry, completion);
371 g_object_unref(completion);
0c3d1aa7 372 return true;
ecaed0bc
VZ
373}
374
375// ----------------------------------------------------------------------------
0ec1179b
VZ
376// editable status
377// ----------------------------------------------------------------------------
378
379bool wxTextEntry::IsEditable() const
380{
d5027818 381 return gtk_editable_get_editable(GetEditable()) != 0;
0ec1179b
VZ
382}
383
384void wxTextEntry::SetEditable(bool editable)
385{
386 gtk_editable_set_editable(GetEditable(), editable);
387}
388
389// ----------------------------------------------------------------------------
390// max text length
391// ----------------------------------------------------------------------------
392
393void wxTextEntry::SetMaxLength(unsigned long len)
394{
7e3e162f
PC
395 GtkEntry* const entry = (GtkEntry*)GetEditable();
396 if (!GTK_IS_ENTRY(entry))
0ec1179b
VZ
397 return;
398
399 gtk_entry_set_max_length(entry, len);
0ec1179b
VZ
400}
401
402void wxTextEntry::SendMaxLenEvent()
403{
404 // remember that the next changed signal is to be ignored to avoid
ce7fe42e 405 // generating a dummy wxEVT_TEXT event
0ec1179b
VZ
406 //IgnoreNextTextUpdate();
407
ec592d7f 408 wxWindow * const win = GetEditableWindow();
ce7fe42e 409 wxCommandEvent event(wxEVT_TEXT_MAXLEN, win->GetId());
0ec1179b
VZ
410 event.SetEventObject(win);
411 event.SetString(GetValue());
937013e0 412 win->HandleWindowEvent(event);
0ec1179b
VZ
413}
414
b2c35774
VZ
415// ----------------------------------------------------------------------------
416// IM handling
417// ----------------------------------------------------------------------------
418
419int wxTextEntry::GTKIMFilterKeypress(GdkEventKey* event) const
420{
421#if GTK_CHECK_VERSION(2, 22, 0)
422 if ( gtk_check_version(2, 12, 0) == 0 )
423 return gtk_entry_im_context_filter_keypress(GetEntry(), event);
424#else // GTK+ < 2.22
425 wxUnusedVar(event);
426#endif // GTK+ 2.22+
427
428 return FALSE;
429}
430
431void wxTextEntry::GTKConnectInsertTextSignal(GtkEntry* entry)
432{
433 g_signal_connect(entry, "insert_text",
434 G_CALLBACK(wx_gtk_insert_text_callback), this);
435}
436
437bool wxTextEntry::GTKEntryOnInsertText(const char* text)
438{
439 return GetEditableWindow()->GTKOnInsertText(text);
440}
441
0847e36e
JS
442// ----------------------------------------------------------------------------
443// margins support
444// ----------------------------------------------------------------------------
445
446bool wxTextEntry::DoSetMargins(const wxPoint& margins)
447{
448#if GTK_CHECK_VERSION(2,10,0)
449 GtkEntry* entry = GetEntry();
450
451 if ( !entry )
452 return false;
453
454 const GtkBorder* oldBorder = gtk_entry_get_inner_border(entry);
455 GtkBorder* newBorder;
456
457 if ( oldBorder )
458 {
459 newBorder = gtk_border_copy(oldBorder);
460 }
461 else
462 {
463 #if GTK_CHECK_VERSION(2,14,0)
464 newBorder = gtk_border_new();
465 #else
c39b6e1e 466 newBorder = g_slice_new0(GtkBorder);
0847e36e
JS
467 #endif
468 // Use some reasonable defaults for initial margins
469 newBorder->left = 2;
470 newBorder->right = 2;
471
472 // These numbers seem to let the text remain vertically centered
473 // in common use scenarios when margins.y == -1.
474 newBorder->top = 3;
475 newBorder->bottom = 3;
476 }
477
478 if ( margins.x != -1 )
479 newBorder->left = (gint) margins.x;
480
481 if ( margins.y != -1 )
482 newBorder->top = (gint) margins.y;
483
484 gtk_entry_set_inner_border(entry, newBorder);
485
c39b6e1e
JS
486#if GTK_CHECK_VERSION(2,14,0)
487 gtk_border_free(newBorder);
488#else
489 g_slice_free(GtkBorder, newBorder);
490#endif
491
0847e36e
JS
492 return true;
493#else
494 wxUnusedVar(margins);
495 return false;
496#endif
497}
498
499wxPoint wxTextEntry::DoGetMargins() const
500{
501#if GTK_CHECK_VERSION(2,10,0)
502 GtkEntry* entry = GetEntry();
503
504 if ( !entry )
505 return wxPoint(-1, -1);
506
507 const GtkBorder* border = gtk_entry_get_inner_border(entry);
508
509 if ( !border )
510 return wxPoint(-1, -1);
511
512 return wxPoint((wxCoord) border->left, (wxCoord) border->top);
513#else
514 return wxPoint(-1, -1);
515#endif
516}
517
96a4cdeb 518#endif // wxUSE_TEXTCTRL || wxUSE_COMBOBOX