wxwidgets/src/gtk/srchctrl.cpp
Vadim Zeitlin 81807382b7 Remove all wxGTK_CONV macros except for wxGTK_CONV_FN
Use wxString::utf8_str() and FromUTF8() directly, there is no need for
the macros abstracting the difference between ANSI and Unicode builds
any longer.
2022-10-27 19:43:30 +02:00

362 lines
8.6 KiB
C++

/////////////////////////////////////////////////////////////////////////////
// Name: src/gtk/srchctrl.cpp
// Purpose: wxSearchCtrl implementation - native
// Author: Kettab Ali
// Created: 2019-12-23
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
// For compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"
#if wxUSE_SEARCHCTRL
#include "wx/srchctrl.h"
#ifndef WX_PRECOMP
#include "wx/menu.h"
#endif //WX_PRECOMP
#include "wx/utils.h"
#include "wx/gtk/private.h"
#include "wx/gtk/private/gtk3-compat.h"
#if GTK_CHECK_VERSION(3,6,0)
// GtkSearchEntry is available only for GTK+ >= 3.6
#define wxHAS_GTK_SEARCH_ENTRY
#endif // GTK >= 3.6
namespace // anonymous
{
// A more readable way to check for GtkSearchEntry availability.
inline bool HasGtkSearchEntry()
{
#ifdef wxHAS_GTK_SEARCH_ENTRY
return wx_is_at_least_gtk3(6);
#else
return false;
#endif
}
inline GtkWidget* CreateGtkSearchEntryIfAvailable()
{
#ifdef wxHAS_GTK_SEARCH_ENTRY
if ( wx_is_at_least_gtk3(6) )
{
return gtk_search_entry_new();
}
#endif // wxHAS_GTK_SEARCH_ENTRY
// No GtkSearchEntry! fallback to the plain GtkEntry.
return gtk_entry_new();
}
}
// ============================================================================
// signal handlers implementation
// ============================================================================
extern "C" {
static void
wx_gtk_icon_press(GtkEntry* WXUNUSED(entry),
gint position,
GdkEventButton* WXUNUSED(event),
wxSearchCtrl* ctrl)
{
if ( position == GTK_ENTRY_ICON_PRIMARY )
{
// 1- Notice that contrary to the generic version, we don't generate the
// wxEVT_SEARCH event here, which is the native behaviour of the
// GtkSearchEntry (the search icon is inactive for a GtkSearchEntry).
//
// 2- If the wxSearchCtrl has a menu associated with it, we explicitly
// make the search icon clickable as a way to display the menu.
ctrl->PopupSearchMenu();
}
else // position == GTK_ENTRY_ICON_SECONDARY
{
if ( !HasGtkSearchEntry() )
{
// No need to call this for a GtkSearchEntry.
ctrl->Clear();
}
wxCommandEvent event(wxEVT_SEARCH_CANCEL, ctrl->GetId());
event.SetEventObject(ctrl);
ctrl->HandleWindowEvent(event);
}
}
}
// ============================================================================
// wxSearchCtrl implementation
// ============================================================================
wxBEGIN_EVENT_TABLE(wxSearchCtrl, wxControl)
EVT_CHAR(wxSearchCtrl::OnChar)
EVT_TEXT(wxID_ANY, wxSearchCtrl::OnText)
EVT_TEXT_ENTER(wxID_ANY, wxSearchCtrl::OnTextEnter)
wxEND_EVENT_TABLE()
wxIMPLEMENT_DYNAMIC_CLASS(wxSearchCtrl, wxControl);
// ----------------------------------------------------------------------------
// creation/destruction
// ----------------------------------------------------------------------------
// destruction
// -----------
wxSearchCtrl::~wxSearchCtrl()
{
#if wxUSE_MENUS
delete m_menu;
#endif // wxUSE_MENUS
}
// creation
// --------
void wxSearchCtrl::Init()
{
m_entry = nullptr;
#if wxUSE_MENUS
m_menu = nullptr;
#endif // wxUSE_MENUS
m_cancelButtonVisible = false;
}
bool wxSearchCtrl::Create(wxWindow *parent, wxWindowID id,
const wxString& value,
const wxPoint& pos,
const wxSize& size,
long style,
const wxValidator& validator,
const wxString& name)
{
if ( !PreCreation(parent, pos, size) ||
!CreateBase(parent, id, pos, size, style | wxTE_PROCESS_ENTER,
validator, name) )
{
wxFAIL_MSG( "wxSearchCtrl creation failed" );
return false;
}
GTKCreateSearchEntryWidget();
if ( HasFlag(wxBORDER_NONE) )
{
g_object_set (m_widget, "has-frame", FALSE, nullptr);
}
GtkEntry * const entry = GetEntry();
// Theoretically m_entry cannot be null, and the test here
// is just for safety reasons.
if ( !entry )
return false;
// Set it up to trigger default item on enter key press
gtk_entry_set_activates_default(entry, !HasFlag(wxTE_PROCESS_ENTER));
gtk_editable_set_editable(GTK_EDITABLE(entry), true);
#ifdef __WXGTK3__
gtk_entry_set_width_chars(entry, 1);
#endif
m_parent->DoAddChild(this);
m_focusWidget = GTK_WIDGET(entry);
PostCreation(size);
gtk_entry_set_text(entry, value.utf8_str());
SetHint(_("Search"));
GTKConnectChangedSignal();
GTKConnectInsertTextSignal(entry);
GTKConnectClipboardSignals(GTK_WIDGET(entry));
return true;
}
void wxSearchCtrl::GTKCreateSearchEntryWidget()
{
m_widget = CreateGtkSearchEntryIfAvailable();
g_object_ref(m_widget);
m_entry = GTK_ENTRY(m_widget);
if ( !HasGtkSearchEntry() )
{
// Add the search icon and make it looks as native as one would expect
// (i.e. GtkSearchEntry).
gtk_entry_set_icon_from_icon_name(m_entry,
GTK_ENTRY_ICON_PRIMARY,
"edit-find-symbolic");
gtk_entry_set_icon_sensitive(m_entry, GTK_ENTRY_ICON_PRIMARY, FALSE);
gtk_entry_set_icon_activatable(m_entry, GTK_ENTRY_ICON_PRIMARY, FALSE);
}
g_signal_connect(m_entry, "icon-press", G_CALLBACK(wx_gtk_icon_press), this);
}
GtkEditable *wxSearchCtrl::GetEditable() const
{
return GTK_EDITABLE(m_entry);
}
void wxSearchCtrl::Clear()
{
wxTextEntry::Clear();
ShowCancelButton(false);
}
// search control specific interfaces
// ----------------------------------
#if wxUSE_MENUS
void wxSearchCtrl::SetMenu( wxMenu* menu )
{
if ( menu == m_menu )
{
// no change
return;
}
delete m_menu;
m_menu = menu;
const bool hasMenu = m_menu != nullptr;
gtk_entry_set_icon_sensitive(m_entry, GTK_ENTRY_ICON_PRIMARY, hasMenu);
gtk_entry_set_icon_activatable(m_entry, GTK_ENTRY_ICON_PRIMARY, hasMenu);
}
wxMenu* wxSearchCtrl::GetMenu()
{
return m_menu;
}
#endif // wxUSE_MENUS
void wxSearchCtrl::ShowSearchButton(bool WXUNUSED(show))
{
// Search button is always shown in the native control.
}
bool wxSearchCtrl::IsSearchButtonVisible() const
{
// Search button is always shown in the native control.
return true;
}
void wxSearchCtrl::ShowCancelButton(bool show)
{
// The cancel button is shown/hidden automatically by the GtkSearchEntry.
if ( HasGtkSearchEntry() )
return;
if ( show == IsCancelButtonVisible() )
{
// no change
return;
}
gtk_entry_set_icon_from_icon_name(m_entry,
GTK_ENTRY_ICON_SECONDARY,
show ? "edit-clear-symbolic" : nullptr);
m_cancelButtonVisible = show;
}
bool wxSearchCtrl::IsCancelButtonVisible() const
{
if ( HasGtkSearchEntry() )
{
return !IsEmpty();
}
return m_cancelButtonVisible;
}
void wxSearchCtrl::SetDescriptiveText(const wxString& text)
{
wxTextEntry::SetHint(text);
}
wxString wxSearchCtrl::GetDescriptiveText() const
{
return wxTextEntry::GetHint();
}
// Events
// ----------
void wxSearchCtrl::OnChar(wxKeyEvent& key_event)
{
wxCHECK_RET( m_entry != nullptr, "invalid search ctrl" );
if ( key_event.GetKeyCode() == WXK_RETURN )
{
if ( HasFlag(wxTE_PROCESS_ENTER) )
{
wxCommandEvent event(wxEVT_TEXT_ENTER, m_windowId);
event.SetEventObject(this);
event.SetString(GetValue());
if ( HandleWindowEvent(event) )
return;
// We disable built-in default button activation when
// wxTE_PROCESS_ENTER is used, but we still should activate it
// if the event wasn't handled, so do it from here.
if ( ClickDefaultButtonIfPossible() )
return;
}
}
key_event.Skip();
}
void wxSearchCtrl::OnText(wxCommandEvent& event)
{
ShowCancelButton(!IsEmpty());
event.Skip();
}
void wxSearchCtrl::OnTextEnter(wxCommandEvent& WXUNUSED(event))
{
if ( !IsEmpty() )
{
wxCommandEvent evt(wxEVT_SEARCH, GetId());
evt.SetEventObject(this);
evt.SetString(GetValue());
ProcessWindowEvent(evt);
}
}
#if wxUSE_MENUS
void wxSearchCtrl::PopupSearchMenu()
{
if ( m_menu )
{
const wxSize size = GetSize();
PopupMenu(m_menu, 0, size.y);
}
}
#endif // wxUSE_MENUS
#endif // wxUSE_SEARCHCTRL