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