Use wxString::utf8_str() and FromUTF8() directly, there is no need for the macros abstracting the difference between ANSI and Unicode builds any longer.
362 lines
8.6 KiB
C++
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
|