1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/gtk/textentry.cpp
3 // Purpose: wxTextEntry implementation for wxGTK
4 // Author: Vadim Zeitlin
5 // Created: 2007-09-24
6 // Copyright: (c) 2007 Vadim Zeitlin <vadim@wxwidgets.org>
7 // Licence: wxWindows licence
8 ///////////////////////////////////////////////////////////////////////////////
9
10 // ============================================================================
11 // declarations
12 // ============================================================================
13
14 // ----------------------------------------------------------------------------
15 // headers
16 // ----------------------------------------------------------------------------
17
18 // for compilers that support precompilation, includes "wx.h".
19 #include "wx/wxprec.h"
20
21
22 #if wxUSE_TEXTCTRL || wxUSE_COMBOBOX
23
24 #ifndef WX_PRECOMP
25 #include "wx/event.h"
26 #include "wx/textentry.h"
27 #include "wx/textctrl.h"
28 #include "wx/window.h"
29 #endif //WX_PRECOMP
30
31 #include "wx/textcompleter.h"
32
33 #include "wx/gtk/private.h"
34 #include "wx/gtk/private/object.h"
35 #include "wx/gtk/private/string.h"
36
37 // ----------------------------------------------------------------------------
38 // wxTextCoalesceData
39 // ----------------------------------------------------------------------------
40
41 class wxTextCoalesceData
42 {
43 public:
wxTextCoalesceData(GtkWidget * widget,gulong handlerAfterKeyPress)44 wxTextCoalesceData(GtkWidget* widget, gulong handlerAfterKeyPress)
45 : m_handlerAfterKeyPress(handlerAfterKeyPress)
46 {
47 m_inKeyPress = false;
48 m_pendingTextChanged = false;
49
50 // This signal handler is unblocked in StartHandlingKeyPress(), so
51 // we need to block it initially to compensate for this.
52 g_signal_handler_block(widget, m_handlerAfterKeyPress);
53 }
54
StartHandlingKeyPress(GtkWidget * widget)55 void StartHandlingKeyPress(GtkWidget* widget)
56 {
57 m_inKeyPress = true;
58 m_pendingTextChanged = false;
59
60 g_signal_handler_unblock(widget, m_handlerAfterKeyPress);
61 }
62
SetPendingIfInKeyPress()63 bool SetPendingIfInKeyPress()
64 {
65 if ( !m_inKeyPress )
66 return false;
67
68 m_pendingTextChanged = true;
69
70 return true;
71 }
72
EndHandlingKeyPressAndCheckIfPending(GtkWidget * widget)73 bool EndHandlingKeyPressAndCheckIfPending(GtkWidget* widget)
74 {
75 g_signal_handler_block(widget, m_handlerAfterKeyPress);
76
77 wxASSERT( m_inKeyPress );
78 m_inKeyPress = false;
79
80 if ( !m_pendingTextChanged )
81 return false;
82
83 m_pendingTextChanged = false;
84
85 return true;
86 }
87
88 private:
89 bool m_inKeyPress;
90 bool m_pendingTextChanged;
91 const gulong m_handlerAfterKeyPress;
92
93 wxDECLARE_NO_COPY_CLASS(wxTextCoalesceData);
94 };
95
96 //-----------------------------------------------------------------------------
97 // helper function to get the length of the text
98 //-----------------------------------------------------------------------------
99
GetEntryTextLength(GtkEntry * entry)100 static int GetEntryTextLength(GtkEntry* entry)
101 {
102 #if GTK_CHECK_VERSION(2, 14, 0)
103 if ( wx_is_at_least_gtk2(14) )
104 {
105 return gtk_entry_get_text_length(entry);
106 }
107 #endif // GTK+ 2.14+
108
109 return strlen(gtk_entry_get_text(entry));
110 }
111
112 // ============================================================================
113 // signal handlers implementation
114 // ============================================================================
115
116 extern "C" {
117
118 // "event-after" handler is only connected when we get a "key-press-event", so
119 // it's effectively called after the end of processing of this event and used
120 // to send a single wxEVT_TEXT even if we received several (typically two, when
121 // the selected text in the control is replaced by new text) "changed" signals.
122 static gboolean
wx_gtk_text_after_key_press(GtkWidget * widget,GdkEventKey * WXUNUSED (gdk_event),wxTextEntry * entry)123 wx_gtk_text_after_key_press(GtkWidget* widget,
124 GdkEventKey* WXUNUSED(gdk_event),
125 wxTextEntry* entry)
126 {
127 wxTextCoalesceData* const data = entry->GTKGetCoalesceData();
128 wxCHECK_MSG( data, FALSE, "must be non-null if this handler is called" );
129
130 if ( data->EndHandlingKeyPressAndCheckIfPending(widget) )
131 {
132 entry->GTKOnTextChanged();
133 }
134
135 return FALSE;
136 }
137
138 // "changed" handler for GtkEntry
139 static void
wx_gtk_text_changed_callback(GtkWidget * WXUNUSED (widget),wxTextEntry * entry)140 wx_gtk_text_changed_callback(GtkWidget* WXUNUSED(widget), wxTextEntry* entry)
141 {
142 if ( wxTextCoalesceData* const data = entry->GTKGetCoalesceData() )
143 {
144 if ( data->SetPendingIfInKeyPress() )
145 {
146 // Don't send the event right now as more might be coming.
147 return;
148 }
149 }
150
151 entry->GTKOnTextChanged();
152 }
153
154 // "insert_text" handler for GtkEntry
155 static void
wx_gtk_insert_text_callback(GtkEditable * editable,const gchar * new_text,gint new_text_length,gint * position,wxTextEntry * text)156 wx_gtk_insert_text_callback(GtkEditable *editable,
157 const gchar * new_text,
158 gint new_text_length,
159 gint * position,
160 wxTextEntry *text)
161 {
162 GtkEntry *entry = GTK_ENTRY (editable);
163
164 #if GTK_CHECK_VERSION(3,0,0) || defined(GSEAL_ENABLE)
165 const int text_max_length = gtk_entry_buffer_get_max_length(gtk_entry_get_buffer(entry));
166 #else
167 const int text_max_length = entry->text_max_length;
168 #endif
169
170 bool handled = false;
171
172 // check that we don't overflow the max length limit if we have it
173 if ( text_max_length )
174 {
175 const int text_length = GetEntryTextLength(entry);
176
177 // We can't use new_text_length as it is in bytes while we want to count
178 // characters (in first approximation, anyhow...).
179 if ( text_length + g_utf8_strlen(new_text, -1) > text_max_length )
180 {
181 // Prevent the new text from being inserted.
182 handled = true;
183
184 // Currently we don't insert anything at all, but it would be better to
185 // insert as many characters as would fit into the text control and
186 // only discard the rest.
187
188 // Notify the user code about overflow.
189 text->SendMaxLenEvent();
190 }
191 }
192
193 // Check if we have to convert all input to upper-case
194 if ( !handled && text->GTKIsUpperCase() )
195 {
196 const wxGtkString upper(g_utf8_strup(new_text, new_text_length));
197
198 // Use the converted text to generate events
199 if ( !text->GTKEntryOnInsertText(upper) )
200 {
201 // Event not handled, so do insert the text: we have to do it
202 // ourselves to use the upper-case version of it
203
204 // Prevent recursive call to this handler again
205 g_signal_handlers_block_by_func
206 (
207 editable,
208 (gpointer)wx_gtk_insert_text_callback,
209 text
210 );
211
212 gtk_editable_insert_text(editable, upper, strlen(upper), position);
213
214 g_signal_handlers_unblock_by_func
215 (
216 editable,
217 (gpointer)wx_gtk_insert_text_callback,
218 text
219 );
220 }
221
222 // Don't call the default handler in any case, either the event was
223 // handled in the user code or we've already inserted the text.
224 handled = true;
225 }
226
227 if ( !handled && text->GTKEntryOnInsertText(new_text) )
228 {
229 // If we already handled the new text insertion, don't do it again.
230 handled = true;
231 }
232
233 if ( handled )
234 {
235 // We must update the position to point after the newly inserted text,
236 // as expected by GTK+.
237 *position = text->GetInsertionPoint();
238
239 g_signal_stop_emission_by_name (editable, "insert_text");
240 }
241 }
242
243 // GTK+ does not expose any mechanism that we can really rely on to detect if/when
244 // the completion popup is shown or hidden. And the sole reliable way (for now) to
245 // know its state is to connect to the "grab-notify" signal and be notified then
246 // for its state. this is the best we can do for now than any other alternative.
247 // (GtkEntryCompletion grabs/ungrabs keyboard and mouse events on popups/popdowns).
248
249 static void
250 wx_gtk_entry_parent_grab_notify (GtkWidget *widget,
251 gboolean was_grabbed,
252 wxTextAutoCompleteData *data);
253
254 } // extern "C"
255
256 //-----------------------------------------------------------------------------
257 // clipboard events: "copy-clipboard", "cut-clipboard", "paste-clipboard"
258 //-----------------------------------------------------------------------------
259
260 // common part of the event handlers below
261 static void
DoHandleClipboardCallback(GtkWidget * widget,wxWindow * win,wxEventType eventType,const gchar * signal_name)262 DoHandleClipboardCallback( GtkWidget *widget,
263 wxWindow *win,
264 wxEventType eventType,
265 const gchar* signal_name)
266 {
267 wxClipboardTextEvent event( eventType, win->GetId() );
268 event.SetEventObject( win );
269 if ( win->HandleWindowEvent( event ) )
270 {
271 // don't let the default processing to take place if we did something
272 // ourselves in the event handler
273 g_signal_stop_emission_by_name (widget, signal_name);
274 }
275 }
276
277 extern "C"
278 {
279
280 static void
wx_gtk_copy_clipboard_callback(GtkWidget * widget,wxWindow * win)281 wx_gtk_copy_clipboard_callback( GtkWidget *widget, wxWindow *win )
282 {
283 DoHandleClipboardCallback(
284 widget, win, wxEVT_TEXT_COPY, "copy-clipboard" );
285 }
286
287 static void
wx_gtk_cut_clipboard_callback(GtkWidget * widget,wxWindow * win)288 wx_gtk_cut_clipboard_callback( GtkWidget *widget, wxWindow *win )
289 {
290 DoHandleClipboardCallback(
291 widget, win, wxEVT_TEXT_CUT, "cut-clipboard" );
292 }
293
294 static void
wx_gtk_paste_clipboard_callback(GtkWidget * widget,wxWindow * win)295 wx_gtk_paste_clipboard_callback( GtkWidget *widget, wxWindow *win )
296 {
297 DoHandleClipboardCallback(
298 widget, win, wxEVT_TEXT_PASTE, "paste-clipboard" );
299 }
300
301 } // extern "C"
302
303 // Base class for wxTextAutoCompleteFixed and wxTextAutoCompleteDynamic below.
304 class wxTextAutoCompleteData
305 {
306 public:
307 // This method is only implemented by wxTextAutoCompleteFixed and will just
308 // return false for wxTextAutoCompleteDynamic.
309 virtual bool ChangeStrings(const wxArrayString& strings) = 0;
310
311 // Conversely, this one is only implemented for wxTextAutoCompleteDynamic
312 // and will just return false (without taking ownership of the argument!)
313 // for wxTextAutoCompleteFixed.
314 virtual bool ChangeCompleter(wxTextCompleter* completer) = 0;
315
316 // We should toggle off wxTE_PROCESS_ENTER flag of our wxTextEntry while
317 // the completion popup is shown to let it see Enter event and process it
318 // on its own (e.g. to dismiss itself). This is done by "grab-notify" signal
319 // see wxTextCtrl::OnChar()
ToggleProcessEnterFlag(bool toggleOff)320 void ToggleProcessEnterFlag(bool toggleOff)
321 {
322 wxWindow* const win = GetEditableWindow(m_entry);
323
324 long flags = win->GetWindowStyleFlag();
325 if ( toggleOff )
326 {
327 // Store the original window flags before we change them.
328 m_hadProcessEnterFlag = (flags & wxTE_PROCESS_ENTER) != 0;
329 if ( !m_hadProcessEnterFlag )
330 {
331 // No need to do anything, it was already off.
332 return;
333 }
334
335 flags &= ~wxTE_PROCESS_ENTER;
336 }
337 else // Restore the original flags.
338 {
339 if ( !m_hadProcessEnterFlag )
340 {
341 // We hadn't turned it off, no need to turn it back on.
342 return;
343 }
344
345 flags |= wxTE_PROCESS_ENTER;
346 }
347
348 win->SetWindowStyleFlag(flags);
349 }
350
~wxTextAutoCompleteData()351 virtual ~wxTextAutoCompleteData()
352 {
353 // Note that we must not use m_entry here because this could result in
354 // using an already half-destroyed wxTextEntry when we're destroyed
355 // from its dtor (which is executed after wxTextCtrl dtor, which had
356 // already destroyed the actual entry). So use the stored widget
357 // instead and only after checking that it is still valid.
358 if ( GTK_IS_ENTRY(m_widgetEntry) )
359 {
360 gtk_entry_set_completion(m_widgetEntry, NULL);
361
362 g_signal_handlers_disconnect_by_data(m_widgetEntry, this);
363 }
364 }
365
366 protected:
367 // Check if completion can be used with this entry.
CanComplete(wxTextEntry * entry)368 static bool CanComplete(wxTextEntry* entry)
369 {
370 // If this check fails, this is probably a multiline wxTextCtrl which
371 // doesn't have any associated GtkEntry.
372 return GTK_IS_ENTRY(entry->GetEntry());
373 }
374
wxTextAutoCompleteData(wxTextEntry * entry)375 explicit wxTextAutoCompleteData(wxTextEntry* entry)
376 : m_entry(entry),
377 m_widgetEntry(entry->GetEntry())
378 {
379 // This will be really set in ToggleProcessEnterFlag().
380 m_hadProcessEnterFlag = false;
381
382 GtkEntryCompletion* const completion = gtk_entry_completion_new();
383
384 gtk_entry_completion_set_text_column (completion, 0);
385 gtk_entry_set_completion(m_widgetEntry, completion);
386
387 g_signal_connect (m_widgetEntry, "grab-notify",
388 G_CALLBACK (wx_gtk_entry_parent_grab_notify),
389 this);
390 }
391
392 // Provide access to wxTextEntry::GetEditableWindow() to the derived
393 // classes: we can call it because this class is a friend of wxTextEntry,
394 // but the derived classes can't do it directly.
GetEditableWindow(wxTextEntry * entry)395 static wxWindow* GetEditableWindow(wxTextEntry* entry)
396 {
397 return entry->GetEditableWindow();
398 }
399
400 // Helper function for appending a string to GtkListStore.
AppendToStore(GtkListStore * store,const wxString & s)401 void AppendToStore(GtkListStore* store, const wxString& s)
402 {
403 GtkTreeIter iter;
404 gtk_list_store_append (store, &iter);
405 gtk_list_store_set (store, &iter, 0, (const gchar *)s.utf8_str(), -1);
406 }
407
408 // Really change the completion model (which may be NULL).
UseModel(GtkListStore * store)409 void UseModel(GtkListStore* store)
410 {
411 GtkEntryCompletion* const c = gtk_entry_get_completion(m_widgetEntry);
412 gtk_entry_completion_set_model (c, GTK_TREE_MODEL(store));
413 gtk_entry_completion_complete (c);
414 }
415
416
417 // The text entry we're associated with.
418 wxTextEntry * const m_entry;
419
420 // And its GTK widget.
421 GtkEntry* const m_widgetEntry;
422
423 // True if the window had wxTE_PROCESS_ENTER flag before we turned it off
424 // in ToggleProcessEnterFlag().
425 bool m_hadProcessEnterFlag;
426
427 wxDECLARE_NO_COPY_CLASS(wxTextAutoCompleteData);
428 };
429
430 // This class simply forwards to GtkListStore.
431 class wxTextAutoCompleteFixed : public wxTextAutoCompleteData
432 {
433 public:
434 // Factory function, may return NULL if entry is invalid.
New(wxTextEntry * entry)435 static wxTextAutoCompleteFixed* New(wxTextEntry *entry)
436 {
437 if ( !CanComplete(entry) )
438 return NULL;
439
440 return new wxTextAutoCompleteFixed(entry);
441 }
442
ChangeStrings(const wxArrayString & strings)443 virtual bool ChangeStrings(const wxArrayString& strings) wxOVERRIDE
444 {
445 wxGtkObject<GtkListStore> store(gtk_list_store_new (1, G_TYPE_STRING));
446
447 for ( wxArrayString::const_iterator i = strings.begin();
448 i != strings.end();
449 ++i )
450 {
451 AppendToStore(store, *i);
452 }
453
454 UseModel(store);
455
456 return true;
457 }
458
ChangeCompleter(wxTextCompleter *)459 virtual bool ChangeCompleter(wxTextCompleter*) wxOVERRIDE
460 {
461 return false;
462 }
463
464 private:
465 // Ctor is private, use New() to create objects of this type.
wxTextAutoCompleteFixed(wxTextEntry * entry)466 explicit wxTextAutoCompleteFixed(wxTextEntry *entry)
467 : wxTextAutoCompleteData(entry)
468 {
469 }
470
471 wxDECLARE_NO_COPY_CLASS(wxTextAutoCompleteFixed);
472 };
473
474 // Dynamic completion using the provided custom wxTextCompleter.
475 class wxTextAutoCompleteDynamic : public wxTextAutoCompleteData
476 {
477 public:
New(wxTextEntry * entry)478 static wxTextAutoCompleteDynamic* New(wxTextEntry *entry)
479 {
480 if ( !CanComplete(entry) )
481 return NULL;
482
483 wxWindow * const win = GetEditableWindow(entry);
484 if ( !win )
485 return NULL;
486
487 return new wxTextAutoCompleteDynamic(entry, win);
488 }
489
~wxTextAutoCompleteDynamic()490 virtual ~wxTextAutoCompleteDynamic()
491 {
492 delete m_completer;
493
494 m_win->Unbind(wxEVT_TEXT, &wxTextAutoCompleteDynamic::OnEntryChanged, this);
495 }
496
ChangeStrings(const wxArrayString &)497 virtual bool ChangeStrings(const wxArrayString&) wxOVERRIDE
498 {
499 return false;
500 }
501
502 // Takes ownership of the pointer which must be non-NULL.
ChangeCompleter(wxTextCompleter * completer)503 virtual bool ChangeCompleter(wxTextCompleter *completer) wxOVERRIDE
504 {
505 delete m_completer;
506 m_completer = completer;
507
508 DoUpdateCompletionModel();
509
510 return true;
511 }
512
513 private:
514 // Ctor is private, use New() to create objects of this type.
wxTextAutoCompleteDynamic(wxTextEntry * entry,wxWindow * win)515 explicit wxTextAutoCompleteDynamic(wxTextEntry *entry, wxWindow *win)
516 : wxTextAutoCompleteData(entry),
517 m_win(win)
518 {
519 m_completer = NULL;
520
521 win->Bind(wxEVT_TEXT, &wxTextAutoCompleteDynamic::OnEntryChanged, this);
522 }
523
OnEntryChanged(wxCommandEvent & event)524 void OnEntryChanged(wxCommandEvent& event)
525 {
526 DoUpdateCompletionModel();
527
528 event.Skip();
529 }
530
531 // Recreate the model to contain all completions for the current prefix.
DoUpdateCompletionModel()532 void DoUpdateCompletionModel()
533 {
534 const wxString& prefix = m_entry->GetValue();
535
536 if ( m_completer->Start(prefix) )
537 {
538 wxGtkObject<GtkListStore> store(gtk_list_store_new (1, G_TYPE_STRING));
539
540 for (;;)
541 {
542 const wxString s = m_completer->GetNext();
543 if ( s.empty() )
544 break;
545
546 AppendToStore(store, s);
547 }
548
549 UseModel(store);
550 }
551 else
552 {
553 UseModel(NULL);
554 }
555 }
556
557
558 // Custom completer.
559 wxTextCompleter *m_completer;
560
561 // The associated window, we need to store it to unbind our event handler.
562 wxWindow* const m_win;
563
564 wxDECLARE_NO_COPY_CLASS(wxTextAutoCompleteDynamic);
565 };
566
567 extern "C"
568 {
569
570 static void
wx_gtk_entry_parent_grab_notify(GtkWidget * widget,gboolean was_grabbed,wxTextAutoCompleteData * data)571 wx_gtk_entry_parent_grab_notify (GtkWidget *widget,
572 gboolean was_grabbed,
573 wxTextAutoCompleteData *data)
574 {
575 g_return_if_fail (GTK_IS_ENTRY(widget));
576
577 bool toggleOff = false;
578
579 if ( gtk_widget_has_focus(widget) )
580 {
581 // If was_grabbed is FALSE that means the topmost grab widget ancestor
582 // of our GtkEntry becomes shadowed by a call to gtk_grab_add()
583 // which means that the GtkEntryCompletion popup window is actually
584 // shown on screen.
585
586 if ( !was_grabbed )
587 toggleOff = true;
588 }
589
590 data->ToggleProcessEnterFlag(toggleOff);
591 }
592
593 } // extern "C"
594
595 // ============================================================================
596 // wxTextEntry implementation
597 // ============================================================================
598
599 // ----------------------------------------------------------------------------
600 // initialization and destruction
601 // ----------------------------------------------------------------------------
602
wxTextEntry()603 wxTextEntry::wxTextEntry()
604 {
605 m_autoCompleteData = NULL;
606 m_coalesceData = NULL;
607 m_isUpperCase = false;
608 }
609
~wxTextEntry()610 wxTextEntry::~wxTextEntry()
611 {
612 delete m_coalesceData;
613 delete m_autoCompleteData;
614 }
615
616 // ----------------------------------------------------------------------------
617 // text operations
618 // ----------------------------------------------------------------------------
619
WriteText(const wxString & value)620 void wxTextEntry::WriteText(const wxString& value)
621 {
622 GtkEditable * const edit = GetEditable();
623
624 // remove the selection if there is one and suppress the text change event
625 // generated by this: we only want to generate one event for this change,
626 // not two
627 {
628 EventsSuppressor noevents(this);
629 gtk_editable_delete_selection(edit);
630 }
631
632 // insert new text at the cursor position
633 gint len = gtk_editable_get_position(edit);
634 gtk_editable_insert_text
635 (
636 edit,
637 wxGTK_CONV_FONT(value, GetEditableWindow()->GetFont()),
638 -1, // text: length: compute it using strlen()
639 &len // will be updated to position after the text end
640 );
641
642 // and move cursor to the end of new text
643 gtk_editable_set_position(edit, len);
644 }
645
DoSetValue(const wxString & value,int flags)646 void wxTextEntry::DoSetValue(const wxString& value, int flags)
647 {
648 if (value != DoGetValue())
649 {
650 // Use Remove() rather than SelectAll() to avoid unnecessary clipboard
651 // operations, and prevent triggering an apparent bug in GTK which
652 // causes the subsequent WriteText() to append rather than overwrite.
653 {
654 EventsSuppressor noevents(this);
655 Remove(0, -1);
656 }
657
658 // Testing whether value is empty here is more than just an
659 // optimization: WriteText() always generates an explicit event in
660 // wxGTK, which we need to avoid unless SetValue_SendEvent is given.
661 if ( !value.empty() )
662 {
663 // Suppress events from here even if we do need them, it's simpler
664 // to send the event below in all cases.
665 EventsSuppressor noevents(this);
666 WriteText(value);
667 }
668
669 // Changing the value is supposed to reset the insertion point. Note,
670 // however, that this does not happen if the text doesn't really change.
671 SetInsertionPoint(0);
672 }
673
674 // OTOH we must send the event even if the text didn't really change for
675 // consistency.
676 if ( flags & SetValue_SendEvent )
677 SendTextUpdatedEvent(GetEditableWindow());
678 }
679
DoGetValue() const680 wxString wxTextEntry::DoGetValue() const
681 {
682 const wxGtkString value(gtk_editable_get_chars(GetEditable(), 0, -1));
683
684 return wxGTK_CONV_BACK_FONT(value,
685 const_cast<wxTextEntry *>(this)->GetEditableWindow()->GetFont());
686 }
687
Remove(long from,long to)688 void wxTextEntry::Remove(long from, long to)
689 {
690 gtk_editable_delete_text(GetEditable(), from, to);
691 }
692
693 // static
GTKGetEntryTextLength(GtkEntry * entry)694 int wxTextEntry::GTKGetEntryTextLength(GtkEntry* entry)
695 {
696 return GetEntryTextLength(entry);
697 }
698
699 // ----------------------------------------------------------------------------
700 // clipboard operations
701 // ----------------------------------------------------------------------------
702
GTKConnectClipboardSignals(GtkWidget * entry)703 void wxTextEntry::GTKConnectClipboardSignals(GtkWidget* entry)
704 {
705 g_signal_connect(entry, "copy-clipboard",
706 G_CALLBACK (wx_gtk_copy_clipboard_callback),
707 GetEditableWindow());
708 g_signal_connect(entry, "cut-clipboard",
709 G_CALLBACK (wx_gtk_cut_clipboard_callback),
710 GetEditableWindow());
711 g_signal_connect(entry, "paste-clipboard",
712 G_CALLBACK (wx_gtk_paste_clipboard_callback),
713 GetEditableWindow());
714 }
715
Copy()716 void wxTextEntry::Copy()
717 {
718 gtk_editable_copy_clipboard(GetEditable());
719 }
720
Cut()721 void wxTextEntry::Cut()
722 {
723 gtk_editable_cut_clipboard(GetEditable());
724 }
725
Paste()726 void wxTextEntry::Paste()
727 {
728 gtk_editable_paste_clipboard(GetEditable());
729 }
730
731 // ----------------------------------------------------------------------------
732 // undo/redo
733 // ----------------------------------------------------------------------------
734
Undo()735 void wxTextEntry::Undo()
736 {
737 // TODO: not implemented
738 }
739
Redo()740 void wxTextEntry::Redo()
741 {
742 // TODO: not implemented
743 }
744
CanUndo() const745 bool wxTextEntry::CanUndo() const
746 {
747 return false;
748 }
749
CanRedo() const750 bool wxTextEntry::CanRedo() const
751 {
752 return false;
753 }
754
755 // ----------------------------------------------------------------------------
756 // insertion point
757 // ----------------------------------------------------------------------------
758
SetInsertionPoint(long pos)759 void wxTextEntry::SetInsertionPoint(long pos)
760 {
761 gtk_editable_set_position(GetEditable(), pos);
762 }
763
GetInsertionPoint() const764 long wxTextEntry::GetInsertionPoint() const
765 {
766 return gtk_editable_get_position(GetEditable());
767 }
768
GetLastPosition() const769 long wxTextEntry::GetLastPosition() const
770 {
771 // this can't be implemented for arbitrary GtkEditable so only do it for
772 // GtkEntries
773 long pos = -1;
774 GtkEntry* entry = (GtkEntry*)GetEditable();
775 if (GTK_IS_ENTRY(entry))
776 pos = GetEntryTextLength(entry);
777
778 return pos;
779 }
780
781 // ----------------------------------------------------------------------------
782 // selection
783 // ----------------------------------------------------------------------------
784
SetSelection(long from,long to)785 void wxTextEntry::SetSelection(long from, long to)
786 {
787 // in wx convention, (-1, -1) means the entire range but GTK+ translates -1
788 // (or any negative number for that matter) into last position so we need
789 // to translate manually
790 if ( from == -1 && to == -1 )
791 from = 0;
792
793 // for compatibility with MSW, exchange from and to parameters so that the
794 // insertion point is set to the start of the selection and not its end as
795 // GTK+ does by default
796 gtk_editable_select_region(GetEditable(), to, from);
797
798 #ifndef __WXGTK3__
799 // avoid reported problem with RHEL 5 GTK+ 2.10 where selection is reset by
800 // a clipboard callback, see #13277
801 if (!wx_is_at_least_gtk2(12))
802 {
803 GtkEntry* entry = GTK_ENTRY(GetEditable());
804 if (to < 0)
805 to = entry->text_length;
806 entry->selection_bound = to;
807 }
808 #endif
809 }
810
GetSelection(long * from,long * to) const811 void wxTextEntry::GetSelection(long *from, long *to) const
812 {
813 gint start, end;
814 if ( gtk_editable_get_selection_bounds(GetEditable(), &start, &end) )
815 {
816 // the output must always be in order, although in GTK+ it isn't
817 if ( start > end )
818 {
819 gint tmp = start;
820 start = end;
821 end = tmp;
822 }
823 }
824 else // no selection
825 {
826 // for compatibility with MSW return the empty selection at cursor
827 start =
828 end = GetInsertionPoint();
829 }
830
831 if ( from )
832 *from = start;
833
834 if ( to )
835 *to = end;
836 }
837
838 // ----------------------------------------------------------------------------
839 // auto completion
840 // ----------------------------------------------------------------------------
841
DoAutoCompleteStrings(const wxArrayString & choices)842 bool wxTextEntry::DoAutoCompleteStrings(const wxArrayString& choices)
843 {
844 // Try to update the existing data first.
845 if ( !m_autoCompleteData || !m_autoCompleteData->ChangeStrings(choices) )
846 {
847 delete m_autoCompleteData;
848 m_autoCompleteData = NULL;
849
850 // If it failed, try creating a new object for fixed completion.
851 wxTextAutoCompleteFixed* const ac = wxTextAutoCompleteFixed::New(this);
852 if ( !ac )
853 return false;
854
855 ac->ChangeStrings(choices);
856
857 m_autoCompleteData = ac;
858 }
859
860 return true;
861 }
862
DoAutoCompleteCustom(wxTextCompleter * completer)863 bool wxTextEntry::DoAutoCompleteCustom(wxTextCompleter *completer)
864 {
865 // First deal with the case when we just want to disable auto-completion.
866 if ( !completer )
867 {
868 if ( m_autoCompleteData )
869 {
870 delete m_autoCompleteData;
871 m_autoCompleteData = NULL;
872 }
873 //else: Nothing to do, we hadn't used auto-completion even before.
874 }
875 else // Have a valid completer.
876 {
877 // As above, try to update the completer of the existing object first
878 // and fall back on creating a new one.
879 if ( !m_autoCompleteData ||
880 !m_autoCompleteData->ChangeCompleter(completer) )
881 {
882 delete m_autoCompleteData;
883 m_autoCompleteData = NULL;
884
885 wxTextAutoCompleteDynamic* const
886 ac = wxTextAutoCompleteDynamic::New(this);
887 if ( !ac )
888 return false;
889
890 ac->ChangeCompleter(completer);
891
892 m_autoCompleteData = ac;
893 }
894 }
895
896 return true;
897 }
898 // ----------------------------------------------------------------------------
899 // editable status
900 // ----------------------------------------------------------------------------
901
IsEditable() const902 bool wxTextEntry::IsEditable() const
903 {
904 return gtk_editable_get_editable(GetEditable()) != 0;
905 }
906
SetEditable(bool editable)907 void wxTextEntry::SetEditable(bool editable)
908 {
909 gtk_editable_set_editable(GetEditable(), editable);
910 }
911
912 // ----------------------------------------------------------------------------
913 // input restrictions
914 // ----------------------------------------------------------------------------
915
SetMaxLength(unsigned long len)916 void wxTextEntry::SetMaxLength(unsigned long len)
917 {
918 GtkEntry* const entry = (GtkEntry*)GetEditable();
919 if (!GTK_IS_ENTRY(entry))
920 return;
921
922 gtk_entry_set_max_length(entry, len);
923 }
924
SendMaxLenEvent()925 void wxTextEntry::SendMaxLenEvent()
926 {
927 // remember that the next changed signal is to be ignored to avoid
928 // generating a dummy wxEVT_TEXT event
929 //IgnoreNextTextUpdate();
930
931 wxWindow * const win = GetEditableWindow();
932 wxCommandEvent event(wxEVT_TEXT_MAXLEN, win->GetId());
933 event.SetEventObject(win);
934 event.SetString(GetValue());
935 win->HandleWindowEvent(event);
936 }
937
ForceUpper()938 void wxTextEntry::ForceUpper()
939 {
940 if ( !m_isUpperCase )
941 {
942 ConvertToUpperCase();
943
944 m_isUpperCase = true;
945 }
946 }
947
948 // ----------------------------------------------------------------------------
949 // IM handling
950 // ----------------------------------------------------------------------------
951
GTKEntryOnKeypress(GtkWidget * widget) const952 void wxTextEntry::GTKEntryOnKeypress(GtkWidget* widget) const
953 {
954 // We coalesce possibly multiple events resulting from a single key press
955 // (this always happens when there is a selection, as we always get a
956 // "changed" event when the selection is removed and another one when the
957 // new text is inserted) into a single wxEVT_TEXT and to do this we need
958 // this extra handler.
959 if ( !m_coalesceData )
960 {
961 // We can't use g_signal_connect_after("key-press-event") because the
962 // emission of this signal is stopped by GtkEntry own key-press-event
963 // handler, so we have to use the generic "event-after" instead to be
964 // notified about the end of handling of this key press and to send any
965 // pending events a.s.a.p.
966 const gulong handler = g_signal_connect
967 (
968 widget,
969 "event-after",
970 G_CALLBACK(wx_gtk_text_after_key_press),
971 const_cast<wxTextEntry*>(this)
972 );
973
974 m_coalesceData = new wxTextCoalesceData(widget, handler);
975 }
976
977 m_coalesceData->StartHandlingKeyPress(widget);
978 }
979
GTKEntryIMFilterKeypress(GdkEventKey * event) const980 int wxTextEntry::GTKEntryIMFilterKeypress(GdkEventKey* event) const
981 {
982 GTKEntryOnKeypress(GTK_WIDGET(GetEntry()));
983
984 int result = false;
985 #if GTK_CHECK_VERSION(2, 22, 0)
986 if (wx_is_at_least_gtk2(22))
987 {
988 result = gtk_entry_im_context_filter_keypress(GetEntry(), event);
989 }
990 #else // GTK+ < 2.22
991 wxUnusedVar(event);
992 #endif // GTK+ 2.22+
993
994 return result;
995 }
996
997 // ----------------------------------------------------------------------------
998 // signals and events
999 // ----------------------------------------------------------------------------
1000
EnableTextChangedEvents(bool enable)1001 void wxTextEntry::EnableTextChangedEvents(bool enable)
1002 {
1003 // Check that we have the associated text, as it may happen (for e.g.
1004 // read-only wxBitmapComboBox) and shouldn't result in any errors, we just
1005 // don't have any events to enable or disable in this case.
1006 void* const entry = GetTextObject();
1007 if ( !entry )
1008 return;
1009
1010 if ( enable )
1011 {
1012 g_signal_handlers_unblock_by_func(entry,
1013 (gpointer)wx_gtk_text_changed_callback, this);
1014 }
1015 else // disable events
1016 {
1017 g_signal_handlers_block_by_func(entry,
1018 (gpointer)wx_gtk_text_changed_callback, this);
1019 }
1020 }
1021
GTKConnectChangedSignal()1022 void wxTextEntry::GTKConnectChangedSignal()
1023 {
1024 g_signal_connect(GetTextObject(), "changed",
1025 G_CALLBACK(wx_gtk_text_changed_callback), this);
1026
1027 }
1028
GTKConnectInsertTextSignal(GtkEntry * entry)1029 void wxTextEntry::GTKConnectInsertTextSignal(GtkEntry* entry)
1030 {
1031 g_signal_connect(entry, "insert_text",
1032 G_CALLBACK(wx_gtk_insert_text_callback), this);
1033 }
1034
GTKEntryOnInsertText(const char * text)1035 bool wxTextEntry::GTKEntryOnInsertText(const char* text)
1036 {
1037 return GetEditableWindow()->GTKOnInsertText(text);
1038 }
1039
1040 // ----------------------------------------------------------------------------
1041 // margins support
1042 // ----------------------------------------------------------------------------
1043
DoSetMargins(const wxPoint & margins)1044 bool wxTextEntry::DoSetMargins(const wxPoint& margins)
1045 {
1046 #if GTK_CHECK_VERSION(2,10,0) && !defined(__WXGTK4__)
1047 GtkEntry* entry = GetEntry();
1048
1049 if ( !entry )
1050 return false;
1051 if ( !wx_is_at_least_gtk2(10) )
1052 return false;
1053
1054 wxGCC_WARNING_SUPPRESS(deprecated-declarations)
1055 const GtkBorder* oldBorder = gtk_entry_get_inner_border(entry);
1056 GtkBorder newBorder;
1057
1058 if ( oldBorder )
1059 newBorder = *oldBorder;
1060 else
1061 {
1062 // Use some reasonable defaults for initial margins
1063 newBorder.left = 2;
1064 newBorder.right = 2;
1065
1066 // These numbers seem to let the text remain vertically centered
1067 // in common use scenarios when margins.y == -1.
1068 newBorder.top = 3;
1069 newBorder.bottom = 3;
1070 }
1071
1072 if ( margins.x != -1 )
1073 newBorder.left = margins.x;
1074
1075 if ( margins.y != -1 )
1076 newBorder.top = margins.y;
1077
1078 gtk_entry_set_inner_border(entry, &newBorder);
1079 wxGCC_WARNING_RESTORE()
1080
1081 return true;
1082 #else
1083 wxUnusedVar(margins);
1084 return false;
1085 #endif
1086 }
1087
DoGetMargins() const1088 wxPoint wxTextEntry::DoGetMargins() const
1089 {
1090 wxPoint point(-1, -1);
1091 #if GTK_CHECK_VERSION(2,10,0) && !defined(__WXGTK4__)
1092 GtkEntry* entry = GetEntry();
1093 if (entry)
1094 {
1095 if (wx_is_at_least_gtk2(10))
1096 {
1097 wxGCC_WARNING_SUPPRESS(deprecated-declarations)
1098 const GtkBorder* border = gtk_entry_get_inner_border(entry);
1099 wxGCC_WARNING_RESTORE()
1100 if (border)
1101 {
1102 point.x = border->left;
1103 point.y = border->top;
1104 }
1105 }
1106 }
1107 #endif
1108 return point;
1109 }
1110
1111 #ifdef __WXGTK3__
SetHint(const wxString & hint)1112 bool wxTextEntry::SetHint(const wxString& hint)
1113 {
1114 #if GTK_CHECK_VERSION(3,2,0)
1115 GtkEntry *entry = GetEntry();
1116 if (entry && gtk_check_version(3,2,0) == NULL)
1117 {
1118 gtk_entry_set_placeholder_text
1119 (
1120 entry,
1121 wxGTK_CONV_FONT(hint, GetEditableWindow()->GetFont())
1122 );
1123 return true;
1124 }
1125 #endif
1126 return wxTextEntryBase::SetHint(hint);
1127 }
1128
GetHint() const1129 wxString wxTextEntry::GetHint() const
1130 {
1131 #if GTK_CHECK_VERSION(3,2,0)
1132 GtkEntry *entry = GetEntry();
1133 if (entry && gtk_check_version(3,2,0) == NULL)
1134 {
1135 return wxGTK_CONV_BACK_FONT
1136 (
1137 gtk_entry_get_placeholder_text(entry),
1138 const_cast<wxTextEntry *>(this)->GetEditableWindow()->GetFont()
1139 );
1140 }
1141 #endif
1142 return wxTextEntryBase::GetHint();
1143 }
1144 #endif // __WXGTK3__
1145
ClickDefaultButtonIfPossible()1146 bool wxTextEntry::ClickDefaultButtonIfPossible()
1147 {
1148 GtkWidget* const widget = GTK_WIDGET(GetEntry());
1149 if (widget == NULL)
1150 return false;
1151
1152 // This does the same thing as gtk_entry_real_activate() in GTK itself.
1153 //
1154 // Note: in GTK 4 we should probably just use gtk_widget_activate_default().
1155 GtkWidget* const toplevel = gtk_widget_get_toplevel(widget);
1156 if ( GTK_IS_WINDOW (toplevel) )
1157 {
1158 GtkWindow* const window = GTK_WINDOW(toplevel);
1159
1160 if ( window )
1161 {
1162 GtkWidget* const default_widget = gtk_window_get_default_widget(window);
1163 GtkWidget* const focus_widget = gtk_window_get_focus(window);
1164
1165 if ( widget != default_widget &&
1166 !(widget == focus_widget &&
1167 (!default_widget ||
1168 !gtk_widget_get_sensitive(default_widget))) )
1169 {
1170 if ( gtk_window_activate_default(window) )
1171 return true;
1172 }
1173 }
1174 }
1175
1176 return false;
1177 }
1178
1179 #endif // wxUSE_TEXTCTRL || wxUSE_COMBOBOX
1180