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