]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/gtk/textentry.cpp
Fix broken and missing DataView interface items for Phoenix
[wxWidgets.git] / src / gtk / textentry.cpp
... / ...
CommitLineData
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/textentry.h"
30 #include "wx/window.h"
31 #include "wx/textctrl.h"
32#endif //WX_PRECOMP
33
34#include <gtk/gtk.h>
35#include "wx/gtk/private.h"
36#include "wx/gtk/private/gtk2-compat.h"
37
38// ============================================================================
39// signal handlers implementation
40// ============================================================================
41
42// "insert_text" handler for GtkEntry
43extern "C"
44void
45wx_gtk_insert_text_callback(GtkEditable *editable,
46 const gchar * new_text,
47 gint WXUNUSED(new_text_length),
48 gint * WXUNUSED(position),
49 wxTextEntry *text)
50{
51 GtkEntry *entry = GTK_ENTRY (editable);
52
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
58
59 // we should only be called if we have a max len limit at all
60 wxCHECK_RET(text_max_length, "shouldn't be called");
61
62 // check that we don't overflow the max length limit
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 )
69 {
70 // Prevent the new text from being inserted.
71 g_signal_stop_emission_by_name (editable, "insert_text");
72
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.
78 text->SendMaxLenEvent();
79 }
80}
81
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
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
163void wxTextEntry::DoSetValue(const wxString& value, int flags)
164{
165 if (value != DoGetValue())
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
183wxString wxTextEntry::DoGetValue() const
184{
185 const wxGtkString value(gtk_editable_get_chars(GetEditable(), 0, -1));
186
187 return wxGTK_CONV_BACK_FONT(value,
188 const_cast<wxTextEntry *>(this)->GetEditableWindow()->GetFont());
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
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
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
270 long pos = -1;
271 GtkEntry* entry = (GtkEntry*)GetEditable();
272 if (GTK_IS_ENTRY(entry))
273 pos = gtk_entry_get_text_length(entry);
274
275 return pos;
276}
277
278// ----------------------------------------------------------------------------
279// selection
280// ----------------------------------------------------------------------------
281
282void wxTextEntry::SetSelection(long from, long to)
283{
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
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);
294
295#ifndef __WXGTK3__
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 }
305#endif
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// ----------------------------------------------------------------------------
336// auto completion
337// ----------------------------------------------------------------------------
338
339bool wxTextEntry::DoAutoCompleteStrings(const wxArrayString& choices)
340{
341 GtkEntry* const entry = (GtkEntry*)GetEditable();
342 wxCHECK_MSG(GTK_IS_ENTRY(entry), false, "auto completion doesn't work with this control");
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);
362 return true;
363}
364
365// ----------------------------------------------------------------------------
366// editable status
367// ----------------------------------------------------------------------------
368
369bool wxTextEntry::IsEditable() const
370{
371 return gtk_editable_get_editable(GetEditable()) != 0;
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{
385 GtkEntry* const entry = (GtkEntry*)GetEditable();
386 if (!GTK_IS_ENTRY(entry))
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
427 wxWindow * const win = GetEditableWindow();
428 wxCommandEvent event(wxEVT_COMMAND_TEXT_MAXLEN, win->GetId());
429 event.SetEventObject(win);
430 event.SetString(GetValue());
431 win->HandleWindowEvent(event);
432}
433
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
458 newBorder = g_slice_new0(GtkBorder);
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
478#if GTK_CHECK_VERSION(2,14,0)
479 gtk_border_free(newBorder);
480#else
481 g_slice_free(GtkBorder, newBorder);
482#endif
483
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
510#endif // wxUSE_TEXTCTRL || wxUSE_COMBOBOX