1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/gtk/srchctrl.cpp
3 // Purpose: wxSearchCtrl implementation - native
4 // Author: Kettab Ali
5 // Created: 2019-12-23
6 // Licence: wxWindows licence
7 /////////////////////////////////////////////////////////////////////////////
8
9 // For compilers that support precompilation, includes "wx.h".
10 #include "wx/wxprec.h"
11
12
13 #if wxUSE_SEARCHCTRL
14
15 #include "wx/srchctrl.h"
16
17 #ifndef WX_PRECOMP
18 #include "wx/menu.h"
19 #endif //WX_PRECOMP
20
21 #include "wx/utils.h"
22 #include "wx/gtk/private.h"
23 #include "wx/gtk/private/gtk3-compat.h"
24
25
26 #if GTK_CHECK_VERSION(3,6,0)
27 // GtkSearchEntry is available only for GTK+ >= 3.6
28 #define wxHAS_GTK_SEARCH_ENTRY
29 #endif // GTK >= 3.6
30
31 namespace // anonymous
32 {
33
34 // A more readable way to check for GtkSearchEntry availability.
HasGtkSearchEntry()35 inline bool HasGtkSearchEntry()
36 {
37 #ifdef wxHAS_GTK_SEARCH_ENTRY
38 return wx_is_at_least_gtk3(6);
39 #else
40 return false;
41 #endif
42 }
43
CreateGtkSearchEntryIfAvailable()44 inline GtkWidget* CreateGtkSearchEntryIfAvailable()
45 {
46 #ifdef wxHAS_GTK_SEARCH_ENTRY
47 if ( wx_is_at_least_gtk3(6) )
48 {
49 return gtk_search_entry_new();
50 }
51 #endif // wxHAS_GTK_SEARCH_ENTRY
52
53 // No GtkSearchEntry! fallback to the plain GtkEntry.
54 return gtk_entry_new();
55 }
56
57 }
58
59 // ============================================================================
60 // signal handlers implementation
61 // ============================================================================
62
63 extern "C" {
64
65 static void
wx_gtk_icon_press(GtkEntry * WXUNUSED (entry),gint position,GdkEventButton * WXUNUSED (event),wxSearchCtrl * ctrl)66 wx_gtk_icon_press(GtkEntry* WXUNUSED(entry),
67 gint position,
68 GdkEventButton* WXUNUSED(event),
69 wxSearchCtrl* ctrl)
70 {
71 if ( position == GTK_ENTRY_ICON_PRIMARY )
72 {
73 // 1- Notice that contrary to the generic version, we don't generate the
74 // wxEVT_SEARCH event here, which is the native behaviour of the
75 // GtkSearchEntry (the search icon is inactive for a GtkSearchEntry).
76 //
77 // 2- If the wxSearchCtrl has a menu associated with it, we explicitly
78 // make the search icon clickable as a way to display the menu.
79
80 ctrl->PopupSearchMenu();
81 }
82 else // position == GTK_ENTRY_ICON_SECONDARY
83 {
84 if ( !HasGtkSearchEntry() )
85 {
86 // No need to call this for a GtkSearchEntry.
87 ctrl->Clear();
88 }
89
90 wxCommandEvent event(wxEVT_SEARCH_CANCEL, ctrl->GetId());
91 event.SetEventObject(ctrl);
92 ctrl->HandleWindowEvent(event);
93 }
94 }
95
96 }
97
98 // ============================================================================
99 // wxSearchCtrl implementation
100 // ============================================================================
101 wxBEGIN_EVENT_TABLE(wxSearchCtrl, wxControl)
102 EVT_CHAR(wxSearchCtrl::OnChar)
103 EVT_TEXT(wxID_ANY, wxSearchCtrl::OnText)
104 EVT_TEXT_ENTER(wxID_ANY, wxSearchCtrl::OnTextEnter)
105 wxEND_EVENT_TABLE()
106
107 wxIMPLEMENT_DYNAMIC_CLASS(wxSearchCtrl, wxControl);
108
109 // ----------------------------------------------------------------------------
110 // creation/destruction
111 // ----------------------------------------------------------------------------
112
113 // destruction
114 // -----------
~wxSearchCtrl()115 wxSearchCtrl::~wxSearchCtrl()
116 {
117 #if wxUSE_MENUS
118 delete m_menu;
119 #endif // wxUSE_MENUS
120 }
121
122 // creation
123 // --------
124
Init()125 void wxSearchCtrl::Init()
126 {
127 m_entry = NULL;
128
129 #if wxUSE_MENUS
130 m_menu = NULL;
131 #endif // wxUSE_MENUS
132
133 m_cancelButtonVisible = false;
134 }
135
Create(wxWindow * parent,wxWindowID id,const wxString & value,const wxPoint & pos,const wxSize & size,long style,const wxValidator & validator,const wxString & name)136 bool wxSearchCtrl::Create(wxWindow *parent, wxWindowID id,
137 const wxString& value,
138 const wxPoint& pos,
139 const wxSize& size,
140 long style,
141 const wxValidator& validator,
142 const wxString& name)
143 {
144 if ( !PreCreation(parent, pos, size) ||
145 !CreateBase(parent, id, pos, size, style | wxTE_PROCESS_ENTER,
146 validator, name) )
147 {
148 wxFAIL_MSG( "wxSearchCtrl creation failed" );
149 return false;
150 }
151
152 GTKCreateSearchEntryWidget();
153
154 if ( HasFlag(wxBORDER_NONE) )
155 {
156 g_object_set (m_widget, "has-frame", FALSE, NULL);
157 }
158
159 GtkEntry * const entry = GetEntry();
160
161 // Theoretically m_entry cannot be null, and the test here
162 // is just for safety reasons.
163 if ( !entry )
164 return false;
165
166 // Set it up to trigger default item on enter key press
167 gtk_entry_set_activates_default(entry, !HasFlag(wxTE_PROCESS_ENTER));
168
169 gtk_editable_set_editable(GTK_EDITABLE(entry), true);
170 #ifdef __WXGTK3__
171 gtk_entry_set_width_chars(entry, 1);
172 #endif
173
174 m_parent->DoAddChild(this);
175
176 m_focusWidget = GTK_WIDGET(entry);
177
178 PostCreation(size);
179
180 gtk_entry_set_text(entry, wxGTK_CONV(value));
181
182 SetHint(_("Search"));
183
184 GTKConnectChangedSignal();
185 GTKConnectInsertTextSignal(entry);
186 GTKConnectClipboardSignals(GTK_WIDGET(entry));
187
188 return true;
189 }
190
GTKCreateSearchEntryWidget()191 void wxSearchCtrl::GTKCreateSearchEntryWidget()
192 {
193 m_widget = CreateGtkSearchEntryIfAvailable();
194
195 g_object_ref(m_widget);
196
197 m_entry = GTK_ENTRY(m_widget);
198
199 if ( !HasGtkSearchEntry() )
200 {
201 // Add the search icon and make it looks as native as one would expect
202 // (i.e. GtkSearchEntry).
203 gtk_entry_set_icon_from_icon_name(m_entry,
204 GTK_ENTRY_ICON_PRIMARY,
205 "edit-find-symbolic");
206
207 gtk_entry_set_icon_sensitive(m_entry, GTK_ENTRY_ICON_PRIMARY, FALSE);
208 gtk_entry_set_icon_activatable(m_entry, GTK_ENTRY_ICON_PRIMARY, FALSE);
209 }
210
211 g_signal_connect(m_entry, "icon-press", G_CALLBACK(wx_gtk_icon_press), this);
212 }
213
GetEditable() const214 GtkEditable *wxSearchCtrl::GetEditable() const
215 {
216 return GTK_EDITABLE(m_entry);
217 }
218
Clear()219 void wxSearchCtrl::Clear()
220 {
221 wxTextEntry::Clear();
222 ShowCancelButton(false);
223 }
224
225 // search control specific interfaces
226 // ----------------------------------
227 #if wxUSE_MENUS
228
SetMenu(wxMenu * menu)229 void wxSearchCtrl::SetMenu( wxMenu* menu )
230 {
231 if ( menu == m_menu )
232 {
233 // no change
234 return;
235 }
236
237 delete m_menu;
238 m_menu = menu;
239
240 const bool hasMenu = m_menu != NULL;
241
242 gtk_entry_set_icon_sensitive(m_entry, GTK_ENTRY_ICON_PRIMARY, hasMenu);
243 gtk_entry_set_icon_activatable(m_entry, GTK_ENTRY_ICON_PRIMARY, hasMenu);
244 }
245
GetMenu()246 wxMenu* wxSearchCtrl::GetMenu()
247 {
248 return m_menu;
249 }
250
251 #endif // wxUSE_MENUS
252
ShowSearchButton(bool WXUNUSED (show))253 void wxSearchCtrl::ShowSearchButton(bool WXUNUSED(show))
254 {
255 // Search button is always shown in the native control.
256 }
257
IsSearchButtonVisible() const258 bool wxSearchCtrl::IsSearchButtonVisible() const
259 {
260 // Search button is always shown in the native control.
261 return true;
262 }
263
ShowCancelButton(bool show)264 void wxSearchCtrl::ShowCancelButton(bool show)
265 {
266 // The cancel button is shown/hidden automatically by the GtkSearchEntry.
267 if ( HasGtkSearchEntry() )
268 return;
269
270 if ( show == IsCancelButtonVisible() )
271 {
272 // no change
273 return;
274 }
275
276 gtk_entry_set_icon_from_icon_name(m_entry,
277 GTK_ENTRY_ICON_SECONDARY,
278 show ? "edit-clear-symbolic" : NULL);
279
280 m_cancelButtonVisible = show;
281 }
282
IsCancelButtonVisible() const283 bool wxSearchCtrl::IsCancelButtonVisible() const
284 {
285 if ( HasGtkSearchEntry() )
286 {
287 return !IsEmpty();
288 }
289
290 return m_cancelButtonVisible;
291 }
292
SetDescriptiveText(const wxString & text)293 void wxSearchCtrl::SetDescriptiveText(const wxString& text)
294 {
295 wxTextEntry::SetHint(text);
296 }
297
GetDescriptiveText() const298 wxString wxSearchCtrl::GetDescriptiveText() const
299 {
300 return wxTextEntry::GetHint();
301 }
302
303 // Events
304 // ----------
305
OnChar(wxKeyEvent & key_event)306 void wxSearchCtrl::OnChar(wxKeyEvent& key_event)
307 {
308 wxCHECK_RET( m_entry != NULL, "invalid search ctrl" );
309
310 if ( key_event.GetKeyCode() == WXK_RETURN )
311 {
312 if ( HasFlag(wxTE_PROCESS_ENTER) )
313 {
314 wxCommandEvent event(wxEVT_TEXT_ENTER, m_windowId);
315 event.SetEventObject(this);
316 event.SetString(GetValue());
317 if ( HandleWindowEvent(event) )
318 return;
319
320 // We disable built-in default button activation when
321 // wxTE_PROCESS_ENTER is used, but we still should activate it
322 // if the event wasn't handled, so do it from here.
323 if ( ClickDefaultButtonIfPossible() )
324 return;
325 }
326 }
327
328 key_event.Skip();
329 }
330
OnText(wxCommandEvent & event)331 void wxSearchCtrl::OnText(wxCommandEvent& event)
332 {
333 ShowCancelButton(!IsEmpty());
334 event.Skip();
335 }
336
OnTextEnter(wxCommandEvent & WXUNUSED (event))337 void wxSearchCtrl::OnTextEnter(wxCommandEvent& WXUNUSED(event))
338 {
339 if ( !IsEmpty() )
340 {
341 wxCommandEvent evt(wxEVT_SEARCH, GetId());
342 evt.SetEventObject(this);
343 evt.SetString(GetValue());
344
345 ProcessWindowEvent(evt);
346 }
347 }
348
349 #if wxUSE_MENUS
350
PopupSearchMenu()351 void wxSearchCtrl::PopupSearchMenu()
352 {
353 if ( m_menu )
354 {
355 const wxSize size = GetSize();
356 PopupMenu(m_menu, 0, size.y);
357 }
358 }
359
360 #endif // wxUSE_MENUS
361
362 #endif // wxUSE_SEARCHCTRL
363