Merge branch 'search-events'
Harmonize the behaviour across platforms. Also fix ChangeValue() for this control. And finally rename the events to use simpler names. See https://github.com/wxWidgets/wxWidgets/pull/699
This commit is contained in:
@@ -178,6 +178,8 @@ All (GUI):
|
||||
- Add Set/GetFooter/Text/Icon() to wxRichMessageDialog (Tobias Taschner)
|
||||
- Add wxFloatingPointValidator::SetFactor().
|
||||
- Add "hint" property to wxSearchCtrl XRC handler.
|
||||
- Add wxEVT_SEARCH[_CANCEL] synonyms for wxSearchCtrl events.
|
||||
- Generate wxEVT_SEARCH on Enter under all platforms.
|
||||
|
||||
wxGTK:
|
||||
|
||||
|
@@ -193,7 +193,10 @@ private:
|
||||
if ( child == this )
|
||||
return; // not a child, we don't want to Connect() to ourselves
|
||||
|
||||
// Always capture wxEVT_KILL_FOCUS:
|
||||
child->Connect(wxEVT_SET_FOCUS,
|
||||
wxFocusEventHandler(wxCompositeWindow::OnSetFocus),
|
||||
NULL, this);
|
||||
|
||||
child->Connect(wxEVT_KILL_FOCUS,
|
||||
wxFocusEventHandler(wxCompositeWindow::OnKillFocus),
|
||||
NULL, this);
|
||||
@@ -221,6 +224,27 @@ private:
|
||||
event.Skip();
|
||||
}
|
||||
|
||||
void OnSetFocus(wxFocusEvent& event)
|
||||
{
|
||||
event.Skip();
|
||||
|
||||
// When a child of a composite window gains focus, the entire composite
|
||||
// focus gains focus as well -- unless it had it already.
|
||||
//
|
||||
// We suppose that we hadn't had focus if the event doesn't carry the
|
||||
// previously focused window as it normally means that it comes from
|
||||
// outside of this program.
|
||||
wxWindow* const oldFocus = event.GetWindow();
|
||||
if ( !oldFocus || oldFocus->GetMainWindowOfCompositeControl() != this )
|
||||
{
|
||||
wxFocusEvent eventThis(wxEVT_SET_FOCUS, this->GetId());
|
||||
eventThis.SetEventObject(this);
|
||||
eventThis.SetWindow(event.GetWindow());
|
||||
|
||||
this->ProcessWindowEvent(eventThis);
|
||||
}
|
||||
}
|
||||
|
||||
void OnKillFocus(wxFocusEvent& event)
|
||||
{
|
||||
// Ignore focus changes within the composite control:
|
||||
|
@@ -88,6 +88,8 @@ public:
|
||||
// operations
|
||||
// ----------
|
||||
|
||||
virtual void ChangeValue(const wxString& value) wxOVERRIDE;
|
||||
|
||||
// editing
|
||||
virtual void Clear() wxOVERRIDE;
|
||||
virtual void Replace(long from, long to, const wxString& value) wxOVERRIDE;
|
||||
@@ -223,7 +225,6 @@ protected:
|
||||
|
||||
void OnCancelButton( wxCommandEvent& event );
|
||||
|
||||
void OnSetFocus( wxFocusEvent& event );
|
||||
void OnSize( wxSizeEvent& event );
|
||||
|
||||
bool HasMenu() const
|
||||
|
@@ -41,8 +41,8 @@
|
||||
|
||||
extern WXDLLIMPEXP_DATA_CORE(const char) wxSearchCtrlNameStr[];
|
||||
|
||||
wxDECLARE_EXPORTED_EVENT(WXDLLIMPEXP_CORE, wxEVT_SEARCHCTRL_CANCEL_BTN, wxCommandEvent);
|
||||
wxDECLARE_EXPORTED_EVENT(WXDLLIMPEXP_CORE, wxEVT_SEARCHCTRL_SEARCH_BTN, wxCommandEvent);
|
||||
wxDECLARE_EXPORTED_EVENT(WXDLLIMPEXP_CORE, wxEVT_SEARCH_CANCEL, wxCommandEvent);
|
||||
wxDECLARE_EXPORTED_EVENT(WXDLLIMPEXP_CORE, wxEVT_SEARCH, wxCommandEvent);
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// a search ctrl is a text control with a search button and a cancel button
|
||||
@@ -90,13 +90,20 @@ private:
|
||||
// macros for handling search events
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#define EVT_SEARCHCTRL_CANCEL_BTN(id, fn) \
|
||||
wx__DECLARE_EVT1(wxEVT_SEARCHCTRL_CANCEL_BTN, id, wxCommandEventHandler(fn))
|
||||
#define EVT_SEARCH_CANCEL(id, fn) \
|
||||
wx__DECLARE_EVT1(wxEVT_SEARCH_CANCEL, id, wxCommandEventHandler(fn))
|
||||
|
||||
#define EVT_SEARCHCTRL_SEARCH_BTN(id, fn) \
|
||||
wx__DECLARE_EVT1(wxEVT_SEARCHCTRL_SEARCH_BTN, id, wxCommandEventHandler(fn))
|
||||
#define EVT_SEARCH(id, fn) \
|
||||
wx__DECLARE_EVT1(wxEVT_SEARCH, id, wxCommandEventHandler(fn))
|
||||
|
||||
// old wxEVT_COMMAND_* constants
|
||||
// old synonyms
|
||||
#define wxEVT_SEARCHCTRL_CANCEL_BTN wxEVT_SEARCH_CANCEL
|
||||
#define wxEVT_SEARCHCTRL_SEARCH_BTN wxEVT_SEARCH
|
||||
|
||||
#define EVT_SEARCHCTRL_CANCEL_BTN(id, fn) EVT_SEARCH_CANCEL(id, fn)
|
||||
#define EVT_SEARCHCTRL_SEARCH_BTN(id, fn) EVT_SEARCH(id, fn)
|
||||
|
||||
// even older wxEVT_COMMAND_* constants
|
||||
#define wxEVT_COMMAND_SEARCHCTRL_CANCEL_BTN wxEVT_SEARCHCTRL_CANCEL_BTN
|
||||
#define wxEVT_COMMAND_SEARCHCTRL_SEARCH_BTN wxEVT_SEARCHCTRL_SEARCH_BTN
|
||||
|
||||
|
@@ -12,10 +12,6 @@
|
||||
control, and a cancel button.
|
||||
|
||||
@beginStyleTable
|
||||
@style{wxTE_PROCESS_ENTER}
|
||||
The control will generate the event @c wxEVT_TEXT_ENTER
|
||||
(otherwise pressing Enter key is either processed internally by the
|
||||
control or used for navigation between dialog controls).
|
||||
@style{wxTE_PROCESS_TAB}
|
||||
The control will receive @c wxEVT_CHAR events for TAB pressed -
|
||||
normally, TAB is used for passing to the next control in a dialog
|
||||
@@ -39,16 +35,19 @@
|
||||
@endStyleTable
|
||||
|
||||
@beginEventEmissionTable{wxCommandEvent}
|
||||
To retrieve actual search queries, use EVT_TEXT and EVT_TEXT_ENTER events,
|
||||
just as you would with wxTextCtrl.
|
||||
@event{EVT_SEARCHCTRL_SEARCH_BTN(id, func)}
|
||||
Respond to a @c wxEVT_SEARCHCTRL_SEARCH_BTN event, generated when the
|
||||
To react to the changes in the control contents, use wxEVT_TEXT event, just
|
||||
as you would do with wxTextCtrl. However it is recommended to use
|
||||
wxEVT_SEARCH to actually start searching to avoid doing it too soon, while
|
||||
the user is still typing (note that wxEVT_SEARCH is also triggered by
|
||||
pressing Enter in the control).
|
||||
@event{EVT_SEARCH(id, func)}
|
||||
Respond to a @c wxEVT_SEARCH event, generated when the
|
||||
search button is clicked. Note that this does not initiate a search on
|
||||
its own, you need to perform the appropriate action in your event
|
||||
handler. You may use @code event.GetString() @endcode to retrieve the
|
||||
string to search for in the event handler code.
|
||||
@event{EVT_SEARCHCTRL_CANCEL_BTN(id, func)}
|
||||
Respond to a @c wxEVT_SEARCHCTRL_CANCEL_BTN event, generated when the
|
||||
@event{EVT_SEARCH_CANCEL(id, func)}
|
||||
Respond to a @c wxEVT_SEARCH_CANCEL event, generated when the
|
||||
cancel button is clicked.
|
||||
@endEventTable
|
||||
|
||||
@@ -56,7 +55,7 @@
|
||||
@category{ctrl}
|
||||
@appearance{searchctrl}
|
||||
|
||||
@see wxTextCtrl::Create, wxValidator
|
||||
@see wxTextCtrl
|
||||
*/
|
||||
class wxSearchCtrl : public wxTextCtrl
|
||||
{
|
||||
@@ -162,5 +161,5 @@ public:
|
||||
};
|
||||
|
||||
|
||||
wxEventType wxEVT_SEARCHCTRL_CANCEL_BTN;
|
||||
wxEventType wxEVT_SEARCHCTRL_SEARCH_BTN;
|
||||
wxEventType wxEVT_SEARCH_CANCEL;
|
||||
wxEventType wxEVT_SEARCH;
|
||||
|
@@ -83,9 +83,15 @@ protected:
|
||||
void OnToggleCancelButton(wxCommandEvent&);
|
||||
void OnToggleSearchMenu(wxCommandEvent&);
|
||||
|
||||
void OnText(wxCommandEvent& event);
|
||||
void OnTextEnter(wxCommandEvent& event);
|
||||
|
||||
void OnSearch(wxCommandEvent& event);
|
||||
void OnSearchCancel(wxCommandEvent& event);
|
||||
|
||||
void OnSetFocus(wxFocusEvent& event);
|
||||
void OnKillFocus(wxFocusEvent& event);
|
||||
|
||||
wxMenu* CreateTestMenu();
|
||||
|
||||
// (re)create the control
|
||||
@@ -111,8 +117,11 @@ wxBEGIN_EVENT_TABLE(SearchCtrlWidgetsPage, WidgetsPage)
|
||||
EVT_CHECKBOX(ID_CANCEL_CB, SearchCtrlWidgetsPage::OnToggleCancelButton)
|
||||
EVT_CHECKBOX(ID_MENU_CB, SearchCtrlWidgetsPage::OnToggleSearchMenu)
|
||||
|
||||
EVT_SEARCHCTRL_SEARCH_BTN(wxID_ANY, SearchCtrlWidgetsPage::OnSearch)
|
||||
EVT_SEARCHCTRL_CANCEL_BTN(wxID_ANY, SearchCtrlWidgetsPage::OnSearchCancel)
|
||||
EVT_TEXT(wxID_ANY, SearchCtrlWidgetsPage::OnText)
|
||||
EVT_TEXT_ENTER(wxID_ANY, SearchCtrlWidgetsPage::OnTextEnter)
|
||||
|
||||
EVT_SEARCH(wxID_ANY, SearchCtrlWidgetsPage::OnSearch)
|
||||
EVT_SEARCH_CANCEL(wxID_ANY, SearchCtrlWidgetsPage::OnSearchCancel)
|
||||
wxEND_EVENT_TABLE()
|
||||
|
||||
// ============================================================================
|
||||
@@ -171,6 +180,9 @@ void SearchCtrlWidgetsPage::CreateControl()
|
||||
|
||||
m_srchCtrl = new wxSearchCtrl(this, -1, wxEmptyString, wxDefaultPosition,
|
||||
wxSize(150, -1), style);
|
||||
|
||||
m_srchCtrl->Bind(wxEVT_SET_FOCUS, &SearchCtrlWidgetsPage::OnSetFocus, this);
|
||||
m_srchCtrl->Bind(wxEVT_KILL_FOCUS, &SearchCtrlWidgetsPage::OnKillFocus, this);
|
||||
}
|
||||
|
||||
void SearchCtrlWidgetsPage::RecreateWidget()
|
||||
@@ -227,6 +239,18 @@ void SearchCtrlWidgetsPage::OnToggleSearchMenu(wxCommandEvent&)
|
||||
m_srchCtrl->SetMenu(NULL);
|
||||
}
|
||||
|
||||
void SearchCtrlWidgetsPage::OnText(wxCommandEvent& event)
|
||||
{
|
||||
wxLogMessage("Search control: text changes, contents is \"%s\".",
|
||||
event.GetString());
|
||||
}
|
||||
|
||||
void SearchCtrlWidgetsPage::OnTextEnter(wxCommandEvent& event)
|
||||
{
|
||||
wxLogMessage("Search control: enter pressed, contents is \"%s\".",
|
||||
event.GetString());
|
||||
}
|
||||
|
||||
void SearchCtrlWidgetsPage::OnSearch(wxCommandEvent& event)
|
||||
{
|
||||
wxLogMessage("Search button: search for \"%s\".", event.GetString());
|
||||
@@ -239,4 +263,18 @@ void SearchCtrlWidgetsPage::OnSearchCancel(wxCommandEvent& event)
|
||||
event.Skip();
|
||||
}
|
||||
|
||||
void SearchCtrlWidgetsPage::OnSetFocus(wxFocusEvent& event)
|
||||
{
|
||||
wxLogMessage("Search control got focus");
|
||||
|
||||
event.Skip();
|
||||
}
|
||||
|
||||
void SearchCtrlWidgetsPage::OnKillFocus(wxFocusEvent& event)
|
||||
{
|
||||
wxLogMessage("Search control lost focus");
|
||||
|
||||
event.Skip();
|
||||
}
|
||||
|
||||
#endif // wxUSE_SEARCHCTRL
|
||||
|
@@ -34,8 +34,8 @@
|
||||
|
||||
const char wxSearchCtrlNameStr[] = "searchCtrl";
|
||||
|
||||
wxDEFINE_EVENT(wxEVT_SEARCHCTRL_CANCEL_BTN, wxCommandEvent);
|
||||
wxDEFINE_EVENT(wxEVT_SEARCHCTRL_SEARCH_BTN, wxCommandEvent);
|
||||
wxDEFINE_EVENT(wxEVT_SEARCH_CANCEL, wxCommandEvent);
|
||||
wxDEFINE_EVENT(wxEVT_SEARCH, wxCommandEvent);
|
||||
|
||||
|
||||
#endif // wxUSE_SEARCHCTRL
|
||||
|
@@ -92,19 +92,16 @@ protected:
|
||||
m_search->GetEventHandler()->ProcessEvent(event);
|
||||
}
|
||||
|
||||
void OnTextUrl(wxTextUrlEvent& eventText)
|
||||
void OnTextEnter(wxCommandEvent& WXUNUSED(event))
|
||||
{
|
||||
// copy constructor is disabled for some reason?
|
||||
//wxTextUrlEvent event(eventText);
|
||||
wxTextUrlEvent event(
|
||||
m_search->GetId(),
|
||||
eventText.GetMouseEvent(),
|
||||
eventText.GetURLStart(),
|
||||
eventText.GetURLEnd()
|
||||
);
|
||||
event.SetEventObject(m_search);
|
||||
if ( !IsEmpty() )
|
||||
{
|
||||
wxCommandEvent event(wxEVT_SEARCH, m_search->GetId());
|
||||
event.SetEventObject(m_search);
|
||||
event.SetString(m_search->GetValue());
|
||||
|
||||
m_search->GetEventHandler()->ProcessEvent(event);
|
||||
m_search->ProcessWindowEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __WXMSW__
|
||||
@@ -149,8 +146,7 @@ private:
|
||||
|
||||
wxBEGIN_EVENT_TABLE(wxSearchTextCtrl, wxTextCtrl)
|
||||
EVT_TEXT(wxID_ANY, wxSearchTextCtrl::OnText)
|
||||
EVT_TEXT_ENTER(wxID_ANY, wxSearchTextCtrl::OnText)
|
||||
EVT_TEXT_URL(wxID_ANY, wxSearchTextCtrl::OnTextUrl)
|
||||
EVT_TEXT_ENTER(wxID_ANY, wxSearchTextCtrl::OnTextEnter)
|
||||
EVT_TEXT_MAXLEN(wxID_ANY, wxSearchTextCtrl::OnText)
|
||||
wxEND_EVENT_TABLE()
|
||||
|
||||
@@ -166,7 +162,9 @@ public:
|
||||
m_search(search),
|
||||
m_eventType(eventType),
|
||||
m_bmp(bmp)
|
||||
{ }
|
||||
{
|
||||
SetBackgroundStyle(wxBG_STYLE_PAINT);
|
||||
}
|
||||
|
||||
void SetBitmapLabel(const wxBitmap& label)
|
||||
{
|
||||
@@ -198,7 +196,7 @@ protected:
|
||||
wxCommandEvent event(m_eventType, m_search->GetId());
|
||||
event.SetEventObject(m_search);
|
||||
|
||||
if ( m_eventType == wxEVT_SEARCHCTRL_SEARCH_BTN )
|
||||
if ( m_eventType == wxEVT_SEARCH )
|
||||
{
|
||||
// it's convenient to have the string to search for directly in the
|
||||
// event instead of having to retrieve it from the control in the
|
||||
@@ -211,7 +209,7 @@ protected:
|
||||
m_search->SetFocus();
|
||||
|
||||
#if wxUSE_MENUS
|
||||
if ( m_eventType == wxEVT_SEARCHCTRL_SEARCH_BTN )
|
||||
if ( m_eventType == wxEVT_SEARCH )
|
||||
{
|
||||
// this happens automatically, just like on Mac OS X
|
||||
m_search->PopupSearchMenu();
|
||||
@@ -240,8 +238,7 @@ wxBEGIN_EVENT_TABLE(wxSearchButton, wxControl)
|
||||
wxEND_EVENT_TABLE()
|
||||
|
||||
wxBEGIN_EVENT_TABLE(wxSearchCtrl, wxSearchCtrlBase)
|
||||
EVT_SEARCHCTRL_CANCEL_BTN(wxID_ANY, wxSearchCtrl::OnCancelButton)
|
||||
EVT_SET_FOCUS(wxSearchCtrl::OnSetFocus)
|
||||
EVT_SEARCH_CANCEL(wxID_ANY, wxSearchCtrl::OnCancelButton)
|
||||
EVT_SIZE(wxSearchCtrl::OnSize)
|
||||
wxEND_EVENT_TABLE()
|
||||
|
||||
@@ -325,10 +322,10 @@ bool wxSearchCtrl::Create(wxWindow *parent, wxWindowID id,
|
||||
m_text = new wxSearchTextCtrl(this, value, style);
|
||||
|
||||
m_searchButton = new wxSearchButton(this,
|
||||
wxEVT_SEARCHCTRL_SEARCH_BTN,
|
||||
wxEVT_SEARCH,
|
||||
m_searchBitmap);
|
||||
m_cancelButton = new wxSearchButton(this,
|
||||
wxEVT_SEARCHCTRL_CANCEL_BTN,
|
||||
wxEVT_SEARCH_CANCEL,
|
||||
m_cancelBitmap);
|
||||
|
||||
SetBackgroundColour( m_text->GetBackgroundColour() );
|
||||
@@ -916,6 +913,14 @@ wxTextCtrl& operator<<(double d);
|
||||
wxTextCtrl& operator<<(const wxChar c);
|
||||
#endif
|
||||
|
||||
// Note that overriding DoSetValue() is currently insufficient because the base
|
||||
// class ChangeValue() only updates m_hintData of this object (which is null
|
||||
// anyhow), instead of updating m_text->m_hintData, see #16998.
|
||||
void wxSearchCtrl::ChangeValue(const wxString& value)
|
||||
{
|
||||
m_text->ChangeValue(value);
|
||||
}
|
||||
|
||||
void wxSearchCtrl::DoSetValue(const wxString& value, int flags)
|
||||
{
|
||||
m_text->DoSetValue(value, flags);
|
||||
@@ -1235,14 +1240,6 @@ void wxSearchCtrl::OnCancelButton( wxCommandEvent& event )
|
||||
event.Skip();
|
||||
}
|
||||
|
||||
void wxSearchCtrl::OnSetFocus( wxFocusEvent& /*event*/ )
|
||||
{
|
||||
if ( m_text )
|
||||
{
|
||||
m_text->SetFocus();
|
||||
}
|
||||
}
|
||||
|
||||
void wxSearchCtrl::OnSize( wxSizeEvent& WXUNUSED(event) )
|
||||
{
|
||||
LayoutControls();
|
||||
|
@@ -209,7 +209,7 @@ bool wxSearchCtrl::Create(wxWindow *parent, wxWindowID id,
|
||||
|
||||
bool wxSearchCtrl::HandleSearchFieldSearchHit()
|
||||
{
|
||||
wxCommandEvent event(wxEVT_SEARCHCTRL_SEARCH_BTN, m_windowId );
|
||||
wxCommandEvent event(wxEVT_SEARCH, m_windowId );
|
||||
event.SetEventObject(this);
|
||||
|
||||
// provide the string to search for directly in the event, this is more
|
||||
@@ -221,7 +221,7 @@ bool wxSearchCtrl::HandleSearchFieldSearchHit()
|
||||
|
||||
bool wxSearchCtrl::HandleSearchFieldCancelHit()
|
||||
{
|
||||
wxCommandEvent event(wxEVT_SEARCHCTRL_CANCEL_BTN, m_windowId );
|
||||
wxCommandEvent event(wxEVT_SEARCH_CANCEL, m_windowId );
|
||||
event.SetEventObject(this);
|
||||
return ProcessCommand(event);
|
||||
}
|
||||
|
@@ -20,50 +20,41 @@
|
||||
|
||||
#include "wx/srchctrl.h"
|
||||
|
||||
class SearchCtrlTestCase : public CppUnit::TestCase
|
||||
class SearchCtrlTestCase
|
||||
{
|
||||
public:
|
||||
SearchCtrlTestCase() { }
|
||||
SearchCtrlTestCase()
|
||||
: m_search(new wxSearchCtrl(wxTheApp->GetTopWindow(), wxID_ANY))
|
||||
{
|
||||
}
|
||||
|
||||
virtual void setUp();
|
||||
virtual void tearDown();
|
||||
~SearchCtrlTestCase()
|
||||
{
|
||||
delete m_search;
|
||||
}
|
||||
|
||||
private:
|
||||
CPPUNIT_TEST_SUITE( SearchCtrlTestCase );
|
||||
CPPUNIT_TEST( Focus );
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
void Focus();
|
||||
|
||||
wxSearchCtrl* m_search;
|
||||
|
||||
wxDECLARE_NO_COPY_CLASS(SearchCtrlTestCase);
|
||||
protected:
|
||||
wxSearchCtrl* const m_search;
|
||||
};
|
||||
|
||||
// register in the unnamed registry so that these tests are run by default
|
||||
CPPUNIT_TEST_SUITE_REGISTRATION( SearchCtrlTestCase );
|
||||
#define SEARCH_CTRL_TEST_CASE(name, tags) \
|
||||
TEST_CASE_METHOD(SearchCtrlTestCase, name, tags)
|
||||
|
||||
// also include in its own registry so that these tests can be run alone
|
||||
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( SearchCtrlTestCase, "SearchCtrlTestCase" );
|
||||
|
||||
void SearchCtrlTestCase::setUp()
|
||||
{
|
||||
m_search = new wxSearchCtrl(wxTheApp->GetTopWindow(), wxID_ANY);
|
||||
}
|
||||
|
||||
void SearchCtrlTestCase::tearDown()
|
||||
{
|
||||
delete m_search;
|
||||
m_search = NULL;
|
||||
}
|
||||
|
||||
void SearchCtrlTestCase::Focus()
|
||||
{
|
||||
// TODO OS X test only passes when run solo ...
|
||||
// TODO OS X test only passes when run solo ...
|
||||
#ifndef __WXOSX__
|
||||
SEARCH_CTRL_TEST_CASE("wxSearchCtrl::Focus", "[wxSearchCtrl][focus]")
|
||||
{
|
||||
m_search->SetFocus();
|
||||
CPPUNIT_ASSERT( m_search->HasFocus() );
|
||||
#endif
|
||||
CHECK( m_search->HasFocus() );
|
||||
}
|
||||
#endif // !__WXOSX__
|
||||
|
||||
SEARCH_CTRL_TEST_CASE("wxSearchCtrl::ChangeValue", "[wxSearchCtrl][text]")
|
||||
{
|
||||
CHECK( m_search->GetValue() == wxString() );
|
||||
|
||||
m_search->ChangeValue("foo");
|
||||
CHECK( m_search->GetValue() == "foo" );
|
||||
}
|
||||
|
||||
#endif // wxUSE_SEARCHCTRL
|
||||
|
Reference in New Issue
Block a user