e307945170
Add wxUILocale class providing functionality which can be implemented portably for all major platforms, including macOS, and doesn't force the change of the global C locale, unlike wxLocale. See https://github.com/wxWidgets/wxWidgets/pull/2464
1516 lines
43 KiB
C++
1516 lines
43 KiB
C++
///////////////////////////////////////////////////////////////////////////////
|
|
// Name: tests/controls/textctrltest.cpp
|
|
// Purpose: wxTextCtrl unit test
|
|
// Author: Vadim Zeitlin
|
|
// Created: 2007-09-25
|
|
// Copyright: (c) 2007 Vadim Zeitlin <vadim@wxwidgets.org>
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// headers
|
|
// ----------------------------------------------------------------------------
|
|
|
|
#include "testprec.h"
|
|
|
|
#if wxUSE_TEXTCTRL
|
|
|
|
|
|
#ifndef WX_PRECOMP
|
|
#include "wx/app.h"
|
|
#include "wx/textctrl.h"
|
|
#endif // WX_PRECOMP
|
|
|
|
#include "wx/platinfo.h"
|
|
#include "wx/scopedptr.h"
|
|
#include "wx/uiaction.h"
|
|
|
|
#if wxUSE_CLIPBOARD
|
|
#include "wx/clipbrd.h"
|
|
#include "wx/dataobj.h"
|
|
#endif // wxUSE_CLIPBOARD
|
|
|
|
#ifdef __WXGTK__
|
|
#include "wx/stopwatch.h"
|
|
#endif
|
|
|
|
#include "wx/private/localeset.h"
|
|
|
|
#include "textentrytest.h"
|
|
#include "testableframe.h"
|
|
#include "asserthelper.h"
|
|
|
|
static const int TEXT_HEIGHT = 200;
|
|
|
|
#if defined(__WXMSW__) && !defined(__WXUNIVERSAL__)
|
|
#define wxHAS_2CHAR_NEWLINES 1
|
|
#else
|
|
#define wxHAS_2CHAR_NEWLINES 0
|
|
#endif
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// test class
|
|
// ----------------------------------------------------------------------------
|
|
|
|
class TextCtrlTestCase : public TextEntryTestCase, public CppUnit::TestCase
|
|
{
|
|
public:
|
|
TextCtrlTestCase() { }
|
|
|
|
virtual void setUp() wxOVERRIDE;
|
|
virtual void tearDown() wxOVERRIDE;
|
|
|
|
private:
|
|
virtual wxTextEntry *GetTestEntry() const wxOVERRIDE { return m_text; }
|
|
virtual wxWindow *GetTestWindow() const wxOVERRIDE { return m_text; }
|
|
|
|
#define SINGLE_AND_MULTI_TESTS() \
|
|
WXUISIM_TEST( ReadOnly ); \
|
|
CPPUNIT_TEST( StreamInput ); \
|
|
CPPUNIT_TEST( Redirector )
|
|
|
|
CPPUNIT_TEST_SUITE( TextCtrlTestCase );
|
|
// These tests run for single line text controls.
|
|
wxTEXT_ENTRY_TESTS();
|
|
WXUISIM_TEST( MaxLength );
|
|
CPPUNIT_TEST( PositionToXYSingleLine );
|
|
CPPUNIT_TEST( XYToPositionSingleLine );
|
|
CPPUNIT_TEST( HitTestSingleLine );
|
|
SINGLE_AND_MULTI_TESTS();
|
|
|
|
// Now switch to the multi-line text controls.
|
|
CPPUNIT_TEST( PseudoTestSwitchToMultiLineStyle );
|
|
|
|
// Rerun the text entry tests not specific to single line controls for
|
|
// multiline ones now.
|
|
wxTEXT_ENTRY_TESTS();
|
|
SINGLE_AND_MULTI_TESTS();
|
|
|
|
|
|
// All tests from now on are for multi-line controls only.
|
|
CPPUNIT_TEST( MultiLineReplace );
|
|
//WXUISIM_TEST( ProcessEnter );
|
|
WXUISIM_TEST( Url );
|
|
CPPUNIT_TEST( Style );
|
|
CPPUNIT_TEST( FontStyle );
|
|
CPPUNIT_TEST( Lines );
|
|
#if wxUSE_LOG
|
|
CPPUNIT_TEST( LogTextCtrl );
|
|
#endif // wxUSE_LOG
|
|
CPPUNIT_TEST( LongText );
|
|
CPPUNIT_TEST( PositionToCoords );
|
|
CPPUNIT_TEST( PositionToCoordsRich );
|
|
CPPUNIT_TEST( PositionToCoordsRich2 );
|
|
CPPUNIT_TEST( PositionToXYMultiLine );
|
|
CPPUNIT_TEST( XYToPositionMultiLine );
|
|
#if wxUSE_RICHEDIT
|
|
CPPUNIT_TEST( PositionToXYMultiLineRich );
|
|
CPPUNIT_TEST( XYToPositionMultiLineRich );
|
|
CPPUNIT_TEST( PositionToXYMultiLineRich2 );
|
|
CPPUNIT_TEST( XYToPositionMultiLineRich2 );
|
|
#endif // wxUSE_RICHEDIT
|
|
CPPUNIT_TEST_SUITE_END();
|
|
|
|
void PseudoTestSwitchToMultiLineStyle()
|
|
{
|
|
ms_style = wxTE_MULTILINE;
|
|
}
|
|
|
|
void MultiLineReplace();
|
|
void ReadOnly();
|
|
void MaxLength();
|
|
void StreamInput();
|
|
void Redirector();
|
|
void HitTestSingleLine();
|
|
//void ProcessEnter();
|
|
void Url();
|
|
void Style();
|
|
void FontStyle();
|
|
void Lines();
|
|
#if wxUSE_LOG
|
|
void LogTextCtrl();
|
|
#endif // wxUSE_LOG
|
|
void LongText();
|
|
void PositionToCoords();
|
|
void PositionToCoordsRich();
|
|
void PositionToCoordsRich2();
|
|
void PositionToXYMultiLine();
|
|
void XYToPositionMultiLine();
|
|
#if wxUSE_RICHEDIT
|
|
void PositionToXYMultiLineRich();
|
|
void XYToPositionMultiLineRich();
|
|
void PositionToXYMultiLineRich2();
|
|
void XYToPositionMultiLineRich2();
|
|
#endif // wxUSE_RICHEDIT
|
|
void PositionToXYSingleLine();
|
|
void XYToPositionSingleLine();
|
|
|
|
void DoPositionToCoordsTestWithStyle(long style);
|
|
void DoPositionToXYMultiLine(long style);
|
|
void DoXYToPositionMultiLine(long style);
|
|
|
|
// Create the control with the following styles added to ms_style which may
|
|
// (or not) already contain wxTE_MULTILINE.
|
|
void CreateText(long extraStyles);
|
|
|
|
wxTextCtrl *m_text;
|
|
|
|
static long ms_style;
|
|
|
|
wxDECLARE_NO_COPY_CLASS(TextCtrlTestCase);
|
|
};
|
|
|
|
// register in the unnamed registry so that these tests are run by default
|
|
CPPUNIT_TEST_SUITE_REGISTRATION( TextCtrlTestCase );
|
|
|
|
// also include in its own registry so that these tests can be run alone
|
|
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( TextCtrlTestCase, "TextCtrlTestCase" );
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// test initialization
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// This is 0 initially and set to wxTE_MULTILINE later to allow running the
|
|
// same tests for both single and multi line controls.
|
|
long TextCtrlTestCase::ms_style = 0;
|
|
|
|
void TextCtrlTestCase::CreateText(long extraStyles)
|
|
{
|
|
const long style = ms_style | extraStyles;
|
|
const int h = (style & wxTE_MULTILINE) ? TEXT_HEIGHT : -1;
|
|
m_text = new wxTextCtrl(wxTheApp->GetTopWindow(), wxID_ANY, "",
|
|
wxDefaultPosition, wxSize(400, h),
|
|
style);
|
|
}
|
|
|
|
void TextCtrlTestCase::setUp()
|
|
{
|
|
CreateText(0);
|
|
}
|
|
|
|
void TextCtrlTestCase::tearDown()
|
|
{
|
|
wxDELETE(m_text);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// tests themselves
|
|
// ----------------------------------------------------------------------------
|
|
|
|
void TextCtrlTestCase::MultiLineReplace()
|
|
{
|
|
m_text->SetValue("Hello replace\n"
|
|
"0123456789012");
|
|
m_text->SetInsertionPoint(0);
|
|
|
|
m_text->Replace(6, 13, "changed");
|
|
|
|
CPPUNIT_ASSERT_EQUAL("Hello changed\n"
|
|
"0123456789012",
|
|
m_text->GetValue());
|
|
CPPUNIT_ASSERT_EQUAL(13, m_text->GetInsertionPoint());
|
|
|
|
m_text->Replace(13, -1, "");
|
|
CPPUNIT_ASSERT_EQUAL("Hello changed", m_text->GetValue());
|
|
CPPUNIT_ASSERT_EQUAL(13, m_text->GetInsertionPoint());
|
|
}
|
|
|
|
void TextCtrlTestCase::ReadOnly()
|
|
{
|
|
#if wxUSE_UIACTIONSIMULATOR
|
|
// we need a read only control for this test so recreate it
|
|
delete m_text;
|
|
CreateText(wxTE_READONLY);
|
|
|
|
EventCounter updated(m_text, wxEVT_TEXT);
|
|
|
|
m_text->SetFocus();
|
|
|
|
wxUIActionSimulator sim;
|
|
sim.Text("abcdef");
|
|
wxYield();
|
|
|
|
CPPUNIT_ASSERT_EQUAL("", m_text->GetValue());
|
|
CPPUNIT_ASSERT_EQUAL(0, updated.GetCount());
|
|
|
|
// SetEditable() is supposed to override wxTE_READONLY
|
|
m_text->SetEditable(true);
|
|
|
|
#if defined(__WXOSX__) || defined(__WXUNIVERSAL__)
|
|
// a ready only text field might not have been focusable at all
|
|
m_text->SetFocus();
|
|
#endif
|
|
|
|
sim.Text("abcdef");
|
|
wxYield();
|
|
|
|
CPPUNIT_ASSERT_EQUAL("abcdef", m_text->GetValue());
|
|
CPPUNIT_ASSERT_EQUAL(6, updated.GetCount());
|
|
#endif
|
|
}
|
|
|
|
void TextCtrlTestCase::MaxLength()
|
|
{
|
|
#if wxUSE_UIACTIONSIMULATOR
|
|
EventCounter updated(m_text, wxEVT_TEXT);
|
|
EventCounter maxlen(m_text, wxEVT_TEXT_MAXLEN);
|
|
|
|
m_text->SetFocus();
|
|
wxYield();
|
|
m_text->SetMaxLength(10);
|
|
|
|
wxUIActionSimulator sim;
|
|
sim.Text("abcdef");
|
|
wxYield();
|
|
|
|
CPPUNIT_ASSERT_EQUAL(0, maxlen.GetCount());
|
|
|
|
sim.Text("ghij");
|
|
wxYield();
|
|
|
|
CPPUNIT_ASSERT_EQUAL(0, maxlen.GetCount());
|
|
CPPUNIT_ASSERT_EQUAL(10, updated.GetCount());
|
|
|
|
maxlen.Clear();
|
|
updated.Clear();
|
|
|
|
sim.Text("k");
|
|
wxYield();
|
|
|
|
CPPUNIT_ASSERT_EQUAL(1, maxlen.GetCount());
|
|
CPPUNIT_ASSERT_EQUAL(0, updated.GetCount());
|
|
|
|
maxlen.Clear();
|
|
updated.Clear();
|
|
|
|
m_text->SetMaxLength(0);
|
|
|
|
sim.Text("k");
|
|
wxYield();
|
|
|
|
CPPUNIT_ASSERT_EQUAL(0, maxlen.GetCount());
|
|
CPPUNIT_ASSERT_EQUAL(1, updated.GetCount());
|
|
#endif
|
|
}
|
|
|
|
void TextCtrlTestCase::StreamInput()
|
|
{
|
|
#ifndef __WXOSX__
|
|
{
|
|
// Ensure we use decimal point and not a comma.
|
|
wxCLocaleSetter setCLocale;
|
|
|
|
*m_text << "stringinput"
|
|
<< 10
|
|
<< 1000L
|
|
<< 3.14f
|
|
<< 2.71
|
|
<< 'a'
|
|
<< L'b';
|
|
}
|
|
|
|
CPPUNIT_ASSERT_EQUAL("stringinput1010003.142.71ab", m_text->GetValue());
|
|
|
|
m_text->SetValue("");
|
|
|
|
#if wxHAS_TEXT_WINDOW_STREAM
|
|
|
|
std::ostream stream(m_text);
|
|
|
|
// We don't test a wide character as this is not a wide stream
|
|
stream << "stringinput"
|
|
<< 10
|
|
<< 1000L
|
|
<< 3.14f
|
|
<< 2.71
|
|
<< 'a';
|
|
|
|
stream.flush();
|
|
|
|
CPPUNIT_ASSERT_EQUAL("stringinput1010003.142.71a", m_text->GetValue());
|
|
|
|
#endif // wxHAS_TEXT_WINDOW_STREAM
|
|
#endif // !__WXOSX__
|
|
}
|
|
|
|
void TextCtrlTestCase::Redirector()
|
|
{
|
|
#if wxHAS_TEXT_WINDOW_STREAM && wxUSE_STD_IOSTREAM
|
|
|
|
wxStreamToTextRedirector redirect(m_text);
|
|
|
|
std::cout << "stringinput"
|
|
<< 10
|
|
<< 1000L
|
|
<< 3.14f
|
|
<< 2.71
|
|
<< 'a';
|
|
|
|
CPPUNIT_ASSERT_EQUAL("stringinput1010003.142.71a", m_text->GetValue());
|
|
|
|
#endif
|
|
}
|
|
|
|
void TextCtrlTestCase::HitTestSingleLine()
|
|
{
|
|
#ifdef __WXQT__
|
|
WARN("Does not work under WxQt");
|
|
#else
|
|
m_text->ChangeValue("Hit me");
|
|
|
|
// We don't know the size of the text borders, so we can't really do any
|
|
// exact tests, just try to verify that the results are roughly as
|
|
// expected.
|
|
const wxSize sizeChar = m_text->GetTextExtent("X");
|
|
const int yMid = sizeChar.y / 2;
|
|
|
|
long pos = -1;
|
|
|
|
#ifdef __WXGTK__
|
|
wxYield();
|
|
#endif
|
|
|
|
// Hitting a point near the left side of the control should find one of the
|
|
// first few characters under it.
|
|
SECTION("Normal")
|
|
{
|
|
REQUIRE( m_text->HitTest(wxPoint(2*sizeChar.x, yMid), &pos) == wxTE_HT_ON_TEXT );
|
|
CHECK( pos >= 0 );
|
|
CHECK( pos < 3 );
|
|
}
|
|
|
|
// Hitting a point well beyond the end of the text shouldn't find any valid
|
|
// character.
|
|
SECTION("Beyond")
|
|
{
|
|
REQUIRE( m_text->HitTest(wxPoint(20*sizeChar.x, yMid), &pos) == wxTE_HT_BEYOND );
|
|
CHECK( pos == m_text->GetLastPosition() );
|
|
}
|
|
|
|
// Making the control scroll, by ensuring that its contents is too long to
|
|
// show inside its window, should change the hit test result for the same
|
|
// position as used above.
|
|
SECTION("Scrolled")
|
|
{
|
|
m_text->ChangeValue(wxString(200, 'X'));
|
|
m_text->SetInsertionPointEnd();
|
|
|
|
#ifdef __WXGTK__
|
|
// wxGTK must be given an opportunity to lay the text out.
|
|
for ( wxStopWatch sw; sw.Time() < 50; )
|
|
wxYield();
|
|
#endif
|
|
|
|
REQUIRE( m_text->HitTest(wxPoint(2*sizeChar.x, yMid), &pos) == wxTE_HT_ON_TEXT );
|
|
CHECK( pos > 3 );
|
|
|
|
// Using negative coordinates works even under Xvfb, so test at least
|
|
// for this -- however this only works in wxGTK, not wxMSW.
|
|
#ifdef __WXGTK__
|
|
REQUIRE( m_text->HitTest(wxPoint(-2*sizeChar.x, yMid), &pos) == wxTE_HT_ON_TEXT );
|
|
CHECK( pos > 3 );
|
|
#endif // __WXGTK__
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#if 0
|
|
void TextCtrlTestCase::ProcessEnter()
|
|
{
|
|
#if wxUSE_UIACTIONSIMULATOR
|
|
wxTestableFrame* frame = wxStaticCast(wxTheApp->GetTopWindow(),
|
|
wxTestableFrame);
|
|
|
|
EventCounter count(m_text, wxEVT_TEXT_ENTER);
|
|
|
|
m_text->SetFocus();
|
|
|
|
wxUIActionSimulator sim;
|
|
sim.Char(WXK_RETURN);
|
|
wxYield();
|
|
|
|
CPPUNIT_ASSERT_EQUAL(0, frame->GetEventCount(wxEVT_TEXT_ENTER));
|
|
|
|
// we need a text control with wxTE_PROCESS_ENTER for this test
|
|
delete m_text;
|
|
CreateText(wxTE_PROCESS_ENTER);
|
|
|
|
m_text->SetFocus();
|
|
|
|
sim.Char(WXK_RETURN);
|
|
wxYield();
|
|
|
|
CPPUNIT_ASSERT_EQUAL(1, frame->GetEventCount(wxEVT_TEXT_ENTER));
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
void TextCtrlTestCase::Url()
|
|
{
|
|
#if wxUSE_UIACTIONSIMULATOR && defined(__WXMSW__) && !defined(__WXUNIVERSAL__)
|
|
// For some reason, this test sporadically fails when run in AppVeyor or
|
|
// GitHub Actions CI environments, even though it passes locally.
|
|
if ( IsAutomaticTest() )
|
|
return;
|
|
|
|
delete m_text;
|
|
CreateText(wxTE_RICH | wxTE_AUTO_URL);
|
|
|
|
EventCounter url(m_text, wxEVT_TEXT_URL);
|
|
|
|
m_text->AppendText("http://www.wxwidgets.org");
|
|
|
|
wxUIActionSimulator sim;
|
|
sim.MouseMove(m_text->ClientToScreen(wxPoint(5, 5)));
|
|
sim.MouseClick();
|
|
wxYield();
|
|
|
|
CPPUNIT_ASSERT_EQUAL(1, url.GetCount());
|
|
#endif
|
|
}
|
|
|
|
void TextCtrlTestCase::Style()
|
|
{
|
|
#if !defined(__WXOSX__) && !defined(__WXQT__)
|
|
delete m_text;
|
|
// We need wxTE_RICH under windows for style support
|
|
CreateText(wxTE_MULTILINE|wxTE_RICH);
|
|
|
|
// Red text on a white background
|
|
m_text->SetDefaultStyle(wxTextAttr(*wxRED, *wxWHITE));
|
|
|
|
CPPUNIT_ASSERT_EQUAL(m_text->GetDefaultStyle().GetTextColour(), *wxRED);
|
|
CPPUNIT_ASSERT_EQUAL(m_text->GetDefaultStyle().GetBackgroundColour(),
|
|
*wxWHITE);
|
|
|
|
m_text->AppendText("red on white ");
|
|
|
|
// Red text on a grey background
|
|
m_text->SetDefaultStyle(wxTextAttr(wxNullColour, *wxLIGHT_GREY));
|
|
|
|
CPPUNIT_ASSERT_EQUAL(m_text->GetDefaultStyle().GetTextColour(), *wxRED);
|
|
CPPUNIT_ASSERT_EQUAL(m_text->GetDefaultStyle().GetBackgroundColour(),
|
|
*wxLIGHT_GREY);
|
|
|
|
m_text->AppendText("red on grey ");
|
|
|
|
// Blue text on a grey background
|
|
m_text->SetDefaultStyle(wxTextAttr(*wxBLUE));
|
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(m_text->GetDefaultStyle().GetTextColour(), *wxBLUE);
|
|
CPPUNIT_ASSERT_EQUAL(m_text->GetDefaultStyle().GetBackgroundColour(),
|
|
*wxLIGHT_GREY);
|
|
|
|
m_text->AppendText("blue on grey");
|
|
|
|
// Get getting the style at a specific location
|
|
wxTextAttr style;
|
|
|
|
// We have to check that styles are supported
|
|
if ( !m_text->GetStyle(3, style) )
|
|
{
|
|
WARN("Retrieving text style not supported, skipping test.");
|
|
return;
|
|
}
|
|
|
|
CHECK( style.GetTextColour() == *wxRED );
|
|
CHECK( style.GetBackgroundColour() == *wxWHITE );
|
|
|
|
// And then setting the style
|
|
REQUIRE( m_text->SetStyle(15, 18, style) );
|
|
|
|
REQUIRE( m_text->GetStyle(17, style) );
|
|
CHECK( style.GetTextColour() == *wxRED );
|
|
CHECK( style.GetBackgroundColour() == *wxWHITE );
|
|
#else
|
|
WARN("Does not work under WxQt or OSX");
|
|
#endif
|
|
}
|
|
|
|
void TextCtrlTestCase::FontStyle()
|
|
{
|
|
// We need wxTE_RICH under MSW and wxTE_MULTILINE under GTK for style
|
|
// support so recreate the control with these styles.
|
|
delete m_text;
|
|
CreateText(wxTE_RICH);
|
|
|
|
// Check that we get back the same font from GetStyle() after setting it
|
|
// with SetDefaultStyle().
|
|
wxFont fontIn(14,
|
|
wxFONTFAMILY_DEFAULT,
|
|
wxFONTSTYLE_NORMAL,
|
|
wxFONTWEIGHT_NORMAL);
|
|
wxTextAttr attrIn;
|
|
attrIn.SetFont(fontIn);
|
|
if ( !m_text->SetDefaultStyle(attrIn) )
|
|
{
|
|
// Skip the test if the styles are not supported.
|
|
return;
|
|
}
|
|
|
|
m_text->AppendText("Default font size 14");
|
|
|
|
wxTextAttr attrOut;
|
|
if ( !m_text->GetStyle(5, attrOut) )
|
|
{
|
|
WARN("Retrieving text style not supported, skipping test.");
|
|
return;
|
|
}
|
|
|
|
CPPUNIT_ASSERT( attrOut.HasFont() );
|
|
|
|
wxFont fontOut = attrOut.GetFont();
|
|
#ifdef __WXMSW__
|
|
// Under MSW we get back an encoding in the font even though we hadn't
|
|
// specified it originally. It's not really a problem but we need this hack
|
|
// to prevent the assert below from failing because of it.
|
|
fontOut.SetEncoding(fontIn.GetEncoding());
|
|
#endif
|
|
CPPUNIT_ASSERT_EQUAL( fontIn, fontOut );
|
|
|
|
|
|
// Also check the same for SetStyle().
|
|
fontIn.SetPointSize(10);
|
|
fontIn.SetWeight(wxFONTWEIGHT_BOLD);
|
|
attrIn.SetFont(fontIn);
|
|
m_text->SetStyle(0, 6, attrIn);
|
|
|
|
m_text->GetStyle(4, attrOut);
|
|
CPPUNIT_ASSERT( attrOut.HasFont() );
|
|
|
|
fontOut = attrOut.GetFont();
|
|
#ifdef __WXMSW__
|
|
fontOut.SetEncoding(fontIn.GetEncoding());
|
|
#endif
|
|
CPPUNIT_ASSERT_EQUAL( fontIn, fontOut );
|
|
}
|
|
|
|
void TextCtrlTestCase::Lines()
|
|
{
|
|
m_text->SetValue("line1\nline2\nlong long line 3");
|
|
m_text->Refresh();
|
|
m_text->Update();
|
|
|
|
CPPUNIT_ASSERT_EQUAL(3, m_text->GetNumberOfLines());
|
|
CPPUNIT_ASSERT_EQUAL(5, m_text->GetLineLength(0));
|
|
CPPUNIT_ASSERT_EQUAL("line2", m_text->GetLineText(1));
|
|
CPPUNIT_ASSERT_EQUAL(16, m_text->GetLineLength(2));
|
|
|
|
m_text->AppendText("\n\nMore text on line 5");
|
|
|
|
CPPUNIT_ASSERT_EQUAL(5, m_text->GetNumberOfLines());
|
|
CPPUNIT_ASSERT_EQUAL(0, m_text->GetLineLength(3));
|
|
CPPUNIT_ASSERT_EQUAL("", m_text->GetLineText(3));
|
|
|
|
// Verify that wrapped lines count as (at least) lines (but it can be more
|
|
// if it's wrapped more than once).
|
|
//
|
|
// This currently doesn't work neither in wxGTK, wxUniv, or wxOSX/Cocoa, see
|
|
// #12366, where GetNumberOfLines() always returns the number of logical,
|
|
// not physical, lines.
|
|
m_text->AppendText("\n" + wxString(50, '1') + ' ' + wxString(50, '2'));
|
|
#if defined(__WXGTK__) || defined(__WXOSX_COCOA__) || defined(__WXUNIVERSAL__) || defined(__WXQT__)
|
|
CPPUNIT_ASSERT_EQUAL(6, m_text->GetNumberOfLines());
|
|
#else
|
|
CPPUNIT_ASSERT(m_text->GetNumberOfLines() > 6);
|
|
#endif
|
|
}
|
|
|
|
#if wxUSE_LOG
|
|
void TextCtrlTestCase::LogTextCtrl()
|
|
{
|
|
CPPUNIT_ASSERT(m_text->IsEmpty());
|
|
|
|
wxLogTextCtrl* logtext = new wxLogTextCtrl(m_text);
|
|
|
|
wxLog* old = wxLog::SetActiveTarget(logtext);
|
|
|
|
logtext->LogText("text");
|
|
|
|
delete wxLog::SetActiveTarget(old);
|
|
|
|
CPPUNIT_ASSERT(!m_text->IsEmpty());
|
|
}
|
|
#endif // wxUSE_LOG
|
|
|
|
void TextCtrlTestCase::LongText()
|
|
{
|
|
// This test is only possible under MSW as in the other ports
|
|
// SetMaxLength() can't be used with multi line text controls.
|
|
#ifdef __WXMSW__
|
|
delete m_text;
|
|
CreateText(wxTE_MULTILINE|wxTE_DONTWRAP);
|
|
|
|
const int numLines = 1000;
|
|
const int lenPattern = 100;
|
|
int i;
|
|
|
|
// Pattern for the line.
|
|
wxChar linePattern[lenPattern+1];
|
|
for (i = 0; i < lenPattern - 1; i++)
|
|
{
|
|
linePattern[i] = wxChar('0' + i % 10);
|
|
}
|
|
linePattern[WXSIZEOF(linePattern) - 1] = wxChar('\0');
|
|
|
|
// Fill the control.
|
|
m_text->SetMaxLength(15000);
|
|
for (i = 0; i < numLines; i++)
|
|
{
|
|
m_text->AppendText(wxString::Format(wxT("[%3d] %s\n"), i, linePattern));
|
|
}
|
|
|
|
// Check the content.
|
|
for (i = 0; i < numLines; i++)
|
|
{
|
|
wxString pattern = wxString::Format(wxT("[%3d] %s"), i, linePattern);
|
|
wxString line = m_text->GetLineText(i);
|
|
CPPUNIT_ASSERT_EQUAL( line, pattern );
|
|
}
|
|
#endif // __WXMSW__
|
|
}
|
|
|
|
void TextCtrlTestCase::PositionToCoords()
|
|
{
|
|
DoPositionToCoordsTestWithStyle(0);
|
|
}
|
|
|
|
void TextCtrlTestCase::PositionToCoordsRich()
|
|
{
|
|
DoPositionToCoordsTestWithStyle(wxTE_RICH);
|
|
}
|
|
|
|
void TextCtrlTestCase::PositionToCoordsRich2()
|
|
{
|
|
DoPositionToCoordsTestWithStyle(wxTE_RICH2);
|
|
}
|
|
|
|
void TextCtrlTestCase::DoPositionToCoordsTestWithStyle(long style)
|
|
{
|
|
delete m_text;
|
|
CreateText(style|wxTE_MULTILINE);
|
|
|
|
// Asking for invalid index should fail.
|
|
WX_ASSERT_FAILS_WITH_ASSERT( m_text->PositionToCoords(1) );
|
|
|
|
// Getting position shouldn't return wxDefaultPosition except if the method
|
|
// is not implemented at all in the current port.
|
|
const wxPoint pos0 = m_text->PositionToCoords(0);
|
|
if ( pos0 == wxDefaultPosition )
|
|
{
|
|
#if ( wxHAS_2CHAR_NEWLINES ) || defined(__WXGTK20__)
|
|
CPPUNIT_FAIL( "PositionToCoords() unexpectedly failed." );
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
CPPUNIT_ASSERT(pos0.x >= 0);
|
|
CPPUNIT_ASSERT(pos0.y >= 0);
|
|
|
|
|
|
m_text->SetValue("Hello");
|
|
wxYield(); // Let GTK layout the control correctly.
|
|
|
|
// Position of non-first character should be positive.
|
|
const long posHello4 = m_text->PositionToCoords(4).x;
|
|
CPPUNIT_ASSERT( posHello4 > 0 );
|
|
|
|
// Asking for position beyond the last character should succeed and return
|
|
// reasonable result.
|
|
CPPUNIT_ASSERT( m_text->PositionToCoords(5).x > posHello4 );
|
|
|
|
// But asking for the next position should fail.
|
|
WX_ASSERT_FAILS_WITH_ASSERT( m_text->PositionToCoords(6) );
|
|
|
|
// Test getting the coordinates of the last character when it is in the
|
|
// beginning of a new line to exercise MSW code which has specific logic
|
|
// for it.
|
|
m_text->AppendText("\n");
|
|
const wxPoint posLast = m_text->PositionToCoords(m_text->GetLastPosition());
|
|
CPPUNIT_ASSERT_EQUAL( pos0.x, posLast.x );
|
|
CPPUNIT_ASSERT( posLast.y > 0 );
|
|
|
|
|
|
// Add enough contents to the control to make sure it has a scrollbar.
|
|
m_text->SetValue("First line" + wxString(50, '\n') + "Last line");
|
|
m_text->SetInsertionPoint(0);
|
|
wxYield(); // Let GTK layout the control correctly.
|
|
|
|
// This shouldn't change anything for the first position coordinates.
|
|
CPPUNIT_ASSERT_EQUAL( pos0, m_text->PositionToCoords(0) );
|
|
|
|
// And the last one must be beyond the window boundary and so not be
|
|
// visible -- but getting its coordinate should still work.
|
|
CPPUNIT_ASSERT
|
|
(
|
|
m_text->PositionToCoords(m_text->GetLastPosition()).y > TEXT_HEIGHT
|
|
);
|
|
|
|
|
|
// Now make it scroll to the end and check that the first position now has
|
|
// negative offset as its above the visible part of the window while the
|
|
// last position is in its bounds.
|
|
m_text->SetInsertionPointEnd();
|
|
|
|
const int pos = m_text->GetInsertionPoint();
|
|
|
|
// wxGTK needs to yield here to update the text control.
|
|
#ifdef __WXGTK__
|
|
wxStopWatch sw;
|
|
while ( m_text->PositionToCoords(0).y == 0 ||
|
|
m_text->PositionToCoords(pos).y > TEXT_HEIGHT )
|
|
{
|
|
if ( sw.Time() > 1000 )
|
|
{
|
|
FAIL("Timed out waiting for wxTextCtrl update.");
|
|
break;
|
|
}
|
|
|
|
wxYield();
|
|
}
|
|
#endif // __WXGTK__
|
|
|
|
wxPoint coords = m_text->PositionToCoords(0);
|
|
INFO("First position coords = " << coords);
|
|
CPPUNIT_ASSERT( coords.y < 0 );
|
|
|
|
coords = m_text->PositionToCoords(pos);
|
|
INFO("Position is " << pos << ", coords = " << coords);
|
|
CPPUNIT_ASSERT( coords.y <= TEXT_HEIGHT );
|
|
}
|
|
|
|
void TextCtrlTestCase::PositionToXYMultiLine()
|
|
{
|
|
DoPositionToXYMultiLine(0);
|
|
}
|
|
|
|
#if wxUSE_RICHEDIT
|
|
void TextCtrlTestCase::PositionToXYMultiLineRich()
|
|
{
|
|
DoPositionToXYMultiLine(wxTE_RICH);
|
|
}
|
|
|
|
void TextCtrlTestCase::PositionToXYMultiLineRich2()
|
|
{
|
|
DoPositionToXYMultiLine(wxTE_RICH2);
|
|
}
|
|
#endif // wxUSE_RICHEDIT
|
|
|
|
void TextCtrlTestCase::DoPositionToXYMultiLine(long style)
|
|
{
|
|
delete m_text;
|
|
CreateText(style|wxTE_MULTILINE|wxTE_DONTWRAP);
|
|
|
|
#if wxHAS_2CHAR_NEWLINES
|
|
const bool isRichEdit = (style & (wxTE_RICH | wxTE_RICH2)) != 0;
|
|
#endif
|
|
|
|
typedef struct { long x, y; } XYPos;
|
|
bool ok;
|
|
wxString text;
|
|
|
|
// empty field
|
|
m_text->Clear();
|
|
const long numChars_0 = 0;
|
|
wxASSERT(numChars_0 == text.Length());
|
|
XYPos coords_0[numChars_0+1] =
|
|
{ { 0, 0 } };
|
|
|
|
CPPUNIT_ASSERT_EQUAL( numChars_0, m_text->GetLastPosition() );
|
|
for ( long i = 0; i < (long)WXSIZEOF(coords_0); i++ )
|
|
{
|
|
long x, y;
|
|
ok = m_text->PositionToXY(i, &x, &y);
|
|
CPPUNIT_ASSERT_EQUAL( true, ok );
|
|
CPPUNIT_ASSERT_EQUAL( coords_0[i].x, x );
|
|
CPPUNIT_ASSERT_EQUAL( coords_0[i].y, y );
|
|
}
|
|
ok = m_text->PositionToXY(WXSIZEOF(coords_0), NULL, NULL);
|
|
CPPUNIT_ASSERT_EQUAL( false, ok );
|
|
|
|
// one line
|
|
text = wxS("1234");
|
|
m_text->SetValue(text);
|
|
const long numChars_1 = 4;
|
|
wxASSERT( numChars_1 == text.Length() );
|
|
XYPos coords_1[numChars_1+1] =
|
|
{ { 0, 0 }, { 1, 0 }, { 2, 0}, { 3, 0 }, { 4, 0 } };
|
|
|
|
CPPUNIT_ASSERT_EQUAL( numChars_1, m_text->GetLastPosition() );
|
|
for ( long i = 0; i < (long)WXSIZEOF(coords_1); i++ )
|
|
{
|
|
long x, y;
|
|
ok = m_text->PositionToXY(i, &x, &y);
|
|
CPPUNIT_ASSERT_EQUAL( true, ok );
|
|
CPPUNIT_ASSERT_EQUAL( coords_1[i].x, x );
|
|
CPPUNIT_ASSERT_EQUAL( coords_1[i].y, y );
|
|
}
|
|
ok = m_text->PositionToXY(WXSIZEOF(coords_1), NULL, NULL);
|
|
CPPUNIT_ASSERT_EQUAL( false, ok );
|
|
|
|
// few lines
|
|
text = wxS("123\nab\nX");
|
|
m_text->SetValue(text);
|
|
|
|
#if wxHAS_2CHAR_NEWLINES
|
|
// Take into account that every new line mark occupies
|
|
// two characters, not one.
|
|
const long numChars_msw_2 = 8 + 2;
|
|
// Note: Two new line characters refer to the same X-Y position.
|
|
XYPos coords_2_msw[numChars_msw_2 + 1] =
|
|
{ { 0, 0 },{ 1, 0 },{ 2, 0 },{ 3, 0 },{ 3, 0 },
|
|
{ 0, 1 },{ 1, 1 },{ 2, 1 },{ 2, 1 },
|
|
{ 0, 2 },{ 1, 2 } };
|
|
#endif // WXMSW
|
|
|
|
const long numChars_2 = 8;
|
|
wxASSERT(numChars_2 == text.Length());
|
|
XYPos coords_2[numChars_2 + 1] =
|
|
{ { 0, 0 }, { 1, 0 }, { 2, 0 }, { 3, 0 },
|
|
{ 0, 1 }, { 1, 1 }, { 2, 1 },
|
|
{ 0, 2 }, { 1, 2 } };
|
|
|
|
const long &ref_numChars_2 =
|
|
#if wxHAS_2CHAR_NEWLINES
|
|
isRichEdit ? numChars_2 : numChars_msw_2;
|
|
#else
|
|
numChars_2;
|
|
#endif
|
|
|
|
XYPos *ref_coords_2 =
|
|
#if wxHAS_2CHAR_NEWLINES
|
|
isRichEdit ? coords_2 : coords_2_msw;
|
|
#else
|
|
coords_2;
|
|
#endif
|
|
|
|
CPPUNIT_ASSERT_EQUAL( ref_numChars_2, m_text->GetLastPosition() );
|
|
for ( long i = 0; i < ref_numChars_2+1; i++ )
|
|
{
|
|
long x, y;
|
|
ok = m_text->PositionToXY(i, &x, &y);
|
|
CPPUNIT_ASSERT_EQUAL( true, ok );
|
|
CPPUNIT_ASSERT_EQUAL( ref_coords_2[i].x, x );
|
|
CPPUNIT_ASSERT_EQUAL( ref_coords_2[i].y, y );
|
|
}
|
|
ok = m_text->PositionToXY(ref_numChars_2 + 1, NULL, NULL);
|
|
CPPUNIT_ASSERT_EQUAL( false, ok );
|
|
|
|
// only empty lines
|
|
text = wxS("\n\n\n");
|
|
m_text->SetValue(text);
|
|
|
|
#if wxHAS_2CHAR_NEWLINES
|
|
// Take into account that every new line mark occupies
|
|
// two characters, not one.
|
|
const long numChars_msw_3 = 3 + 3;
|
|
// Note: Two new line characters refer to the same X-Y position.
|
|
XYPos coords_3_msw[numChars_msw_3 + 1] =
|
|
{ { 0, 0 },{ 0, 0 },
|
|
{ 0, 1 },{ 0, 1 },
|
|
{ 0, 2 },{ 0, 2 },
|
|
{ 0, 3 } };
|
|
#endif // WXMSW
|
|
|
|
const long numChars_3 = 3;
|
|
wxASSERT(numChars_3 == text.Length());
|
|
XYPos coords_3[numChars_3+1] =
|
|
{ { 0, 0 },
|
|
{ 0, 1 },
|
|
{ 0, 2 },
|
|
{ 0, 3 } };
|
|
|
|
const long &ref_numChars_3 =
|
|
#if wxHAS_2CHAR_NEWLINES
|
|
isRichEdit ? numChars_3 : numChars_msw_3;
|
|
#else
|
|
numChars_3;
|
|
#endif
|
|
|
|
XYPos *ref_coords_3 =
|
|
#if wxHAS_2CHAR_NEWLINES
|
|
isRichEdit ? coords_3 : coords_3_msw;
|
|
#else
|
|
coords_3;
|
|
#endif
|
|
|
|
CPPUNIT_ASSERT_EQUAL( ref_numChars_3, m_text->GetLastPosition() );
|
|
for ( long i = 0; i < ref_numChars_3+1; i++ )
|
|
{
|
|
long x, y;
|
|
ok = m_text->PositionToXY(i, &x, &y);
|
|
CPPUNIT_ASSERT_EQUAL( true, ok );
|
|
CPPUNIT_ASSERT_EQUAL( ref_coords_3[i].x, x );
|
|
CPPUNIT_ASSERT_EQUAL( ref_coords_3[i].y, y );
|
|
}
|
|
ok = m_text->PositionToXY(ref_numChars_3 + 1, NULL, NULL);
|
|
CPPUNIT_ASSERT_EQUAL( false, ok );
|
|
|
|
// mixed empty/non-empty lines
|
|
text = wxS("123\na\n\nX\n\n");
|
|
m_text->SetValue(text);
|
|
|
|
#if wxHAS_2CHAR_NEWLINES
|
|
// Take into account that every new line mark occupies
|
|
// two characters, not one.
|
|
const long numChars_msw_4 = 10 + 5;
|
|
// Note: Two new line characters refer to the same X-Y position.
|
|
XYPos coords_4_msw[numChars_msw_4 + 1] =
|
|
{ { 0, 0 },{ 1, 0 },{ 2, 0 },{ 3, 0 },{ 3, 0 },
|
|
{ 0, 1 },{ 1, 1 },{ 1, 1 },
|
|
{ 0, 2 },{ 0, 2 },
|
|
{ 0, 3 },{ 1, 3 },{ 1, 3 },
|
|
{ 0, 4 },{ 0, 4 },
|
|
{ 0, 5 } };
|
|
#endif // WXMSW
|
|
|
|
const long numChars_4 = 10;
|
|
wxASSERT(numChars_4 == text.Length());
|
|
XYPos coords_4[numChars_4+1] =
|
|
{ { 0, 0 }, { 1, 0 }, { 2, 0 }, { 3, 0 },
|
|
{ 0, 1 }, { 1, 1 },
|
|
{ 0, 2 },
|
|
{ 0, 3 }, { 1, 3 },
|
|
{ 0, 4 },
|
|
{ 0, 5 } };
|
|
|
|
const long &ref_numChars_4 =
|
|
#if wxHAS_2CHAR_NEWLINES
|
|
isRichEdit ? numChars_4 : numChars_msw_4;
|
|
#else
|
|
numChars_4;
|
|
#endif
|
|
|
|
XYPos *ref_coords_4 =
|
|
#if wxHAS_2CHAR_NEWLINES
|
|
isRichEdit ? coords_4 : coords_4_msw;
|
|
#else
|
|
coords_4;
|
|
#endif
|
|
|
|
CPPUNIT_ASSERT_EQUAL( ref_numChars_4, m_text->GetLastPosition() );
|
|
for ( long i = 0; i < ref_numChars_4+1; i++ )
|
|
{
|
|
long x, y;
|
|
ok = m_text->PositionToXY(i, &x, &y);
|
|
CPPUNIT_ASSERT_EQUAL( true, ok );
|
|
CPPUNIT_ASSERT_EQUAL( ref_coords_4[i].x, x );
|
|
CPPUNIT_ASSERT_EQUAL( ref_coords_4[i].y, y );
|
|
}
|
|
ok = m_text->PositionToXY(ref_numChars_4 + 1, NULL, NULL);
|
|
CPPUNIT_ASSERT_EQUAL( false, ok );
|
|
}
|
|
|
|
void TextCtrlTestCase::XYToPositionMultiLine()
|
|
{
|
|
DoXYToPositionMultiLine(0);
|
|
}
|
|
|
|
#if wxUSE_RICHEDIT
|
|
void TextCtrlTestCase::XYToPositionMultiLineRich()
|
|
{
|
|
DoXYToPositionMultiLine(wxTE_RICH);
|
|
}
|
|
|
|
void TextCtrlTestCase::XYToPositionMultiLineRich2()
|
|
{
|
|
DoXYToPositionMultiLine(wxTE_RICH2);
|
|
}
|
|
#endif // wxUSE_RICHEDIT
|
|
|
|
void TextCtrlTestCase::DoXYToPositionMultiLine(long style)
|
|
{
|
|
delete m_text;
|
|
CreateText(style|wxTE_MULTILINE|wxTE_DONTWRAP);
|
|
|
|
#if wxHAS_2CHAR_NEWLINES
|
|
const bool isRichEdit = (style & (wxTE_RICH | wxTE_RICH2)) != 0;
|
|
#endif
|
|
|
|
wxString text;
|
|
// empty field
|
|
m_text->Clear();
|
|
const long maxLineLength_0 = 0+1;
|
|
const long numLines_0 = 1;
|
|
CPPUNIT_ASSERT_EQUAL( numLines_0, m_text->GetNumberOfLines() );
|
|
long pos_0[numLines_0+1][maxLineLength_0+1] =
|
|
{ { 0, -1 },
|
|
{ -1, -1 } };
|
|
for ( long y = 0; y < numLines_0+1; y++ )
|
|
for( long x = 0; x < maxLineLength_0+1; x++ )
|
|
{
|
|
long p = m_text->XYToPosition(x, y);
|
|
INFO("x=" << x << ", y=" << y);
|
|
CPPUNIT_ASSERT_EQUAL( pos_0[y][x], p );
|
|
}
|
|
|
|
// one line
|
|
text = wxS("1234");
|
|
m_text->SetValue(text);
|
|
const long maxLineLength_1 = 4+1;
|
|
const long numLines_1 = 1;
|
|
CPPUNIT_ASSERT_EQUAL( numLines_1, m_text->GetNumberOfLines() );
|
|
long pos_1[numLines_1+1][maxLineLength_1+1] =
|
|
{ { 0, 1, 2, 3, 4, -1 },
|
|
{ -1, -1, -1, -1, -1, -1 } };
|
|
for ( long y = 0; y < numLines_1+1; y++ )
|
|
for( long x = 0; x < maxLineLength_1+1; x++ )
|
|
{
|
|
long p = m_text->XYToPosition(x, y);
|
|
INFO("x=" << x << ", y=" << y);
|
|
CPPUNIT_ASSERT_EQUAL( pos_1[y][x], p );
|
|
}
|
|
|
|
// few lines
|
|
text = wxS("123\nab\nX");
|
|
m_text->SetValue(text);
|
|
const long maxLineLength_2 = 4;
|
|
const long numLines_2 = 3;
|
|
CPPUNIT_ASSERT_EQUAL( numLines_2, m_text->GetNumberOfLines() );
|
|
#if wxHAS_2CHAR_NEWLINES
|
|
// Note: New lines are occupied by two characters.
|
|
long pos_2_msw[numLines_2 + 1][maxLineLength_2 + 1] =
|
|
{ { 0, 1, 2, 3, -1 }, // New line occupies positions 3, 4
|
|
{ 5, 6, 7, -1, -1 }, // New line occupies positions 7, 8
|
|
{ 9, 10, -1, -1, -1 },
|
|
{ -1, -1, -1, -1, -1 } };
|
|
#endif // WXMSW
|
|
long pos_2[numLines_2+1][maxLineLength_2+1] =
|
|
{ { 0, 1, 2, 3, -1 },
|
|
{ 4, 5, 6, -1, -1 },
|
|
{ 7, 8, -1, -1, -1 },
|
|
{ -1, -1, -1, -1, -1 } };
|
|
|
|
long (&ref_pos_2)[numLines_2 + 1][maxLineLength_2 + 1] =
|
|
#if wxHAS_2CHAR_NEWLINES
|
|
isRichEdit ? pos_2 : pos_2_msw;
|
|
#else
|
|
pos_2;
|
|
#endif
|
|
|
|
for ( long y = 0; y < numLines_2+1; y++ )
|
|
for( long x = 0; x < maxLineLength_2+1; x++ )
|
|
{
|
|
long p = m_text->XYToPosition(x, y);
|
|
INFO("x=" << x << ", y=" << y);
|
|
CPPUNIT_ASSERT_EQUAL( ref_pos_2[y][x], p );
|
|
}
|
|
|
|
// only empty lines
|
|
text = wxS("\n\n\n");
|
|
m_text->SetValue(text);
|
|
const long maxLineLength_3 = 1;
|
|
const long numLines_3 = 4;
|
|
CPPUNIT_ASSERT_EQUAL( numLines_3, m_text->GetNumberOfLines() );
|
|
#if wxHAS_2CHAR_NEWLINES
|
|
// Note: New lines are occupied by two characters.
|
|
long pos_3_msw[numLines_3 + 1][maxLineLength_3 + 1] =
|
|
{ { 0, -1 }, // New line occupies positions 0, 1
|
|
{ 2, -1 }, // New line occupies positions 2, 3
|
|
{ 4, -1 }, // New line occupies positions 4, 5
|
|
{ 6, -1 },
|
|
{ -1, -1 } };
|
|
#endif // WXMSW
|
|
long pos_3[numLines_3+1][maxLineLength_3+1] =
|
|
{ { 0, -1 },
|
|
{ 1, -1 },
|
|
{ 2, -1 },
|
|
{ 3, -1 },
|
|
{ -1, -1 } };
|
|
|
|
long (&ref_pos_3)[numLines_3 + 1][maxLineLength_3 + 1] =
|
|
#if wxHAS_2CHAR_NEWLINES
|
|
isRichEdit ? pos_3 : pos_3_msw;
|
|
#else
|
|
pos_3;
|
|
#endif
|
|
|
|
for ( long y = 0; y < numLines_3+1; y++ )
|
|
for( long x = 0; x < maxLineLength_3+1; x++ )
|
|
{
|
|
long p = m_text->XYToPosition(x, y);
|
|
INFO("x=" << x << ", y=" << y);
|
|
CPPUNIT_ASSERT_EQUAL( ref_pos_3[y][x], p );
|
|
}
|
|
|
|
// mixed empty/non-empty lines
|
|
text = wxS("123\na\n\nX\n\n");
|
|
m_text->SetValue(text);
|
|
const long maxLineLength_4 = 4;
|
|
const long numLines_4 = 6;
|
|
CPPUNIT_ASSERT_EQUAL( numLines_4, m_text->GetNumberOfLines() );
|
|
#if wxHAS_2CHAR_NEWLINES
|
|
// Note: New lines are occupied by two characters.
|
|
long pos_4_msw[numLines_4 + 1][maxLineLength_4 + 1] =
|
|
{ { 0, 1, 2, 3, -1 }, // New line occupies positions 3, 4
|
|
{ 5, 6, -1, -1, -1 }, // New line occupies positions 6, 7
|
|
{ 8, -1, -1, -1, -1 }, // New line occupies positions 8, 9
|
|
{ 10, 11, -1, -1, -1 }, // New line occupies positions 11, 12
|
|
{ 13, -1, -1, -1, -1 }, // New line occupies positions 13, 14
|
|
{ 15, -1, -1, -1, -1 },
|
|
{ -1, -1, -1, -1, -1 } };
|
|
#endif // WXMSW
|
|
long pos_4[numLines_4+1][maxLineLength_4+1] =
|
|
{ { 0, 1, 2, 3, -1 },
|
|
{ 4, 5, -1, -1, -1 },
|
|
{ 6, -1, -1, -1, -1 },
|
|
{ 7, 8, -1, -1, -1 },
|
|
{ 9, -1, -1, -1, -1 },
|
|
{ 10, -1, -1, -1, -1 },
|
|
{ -1, -1, -1, -1, -1 } };
|
|
|
|
long (&ref_pos_4)[numLines_4 + 1][maxLineLength_4 + 1] =
|
|
#if wxHAS_2CHAR_NEWLINES
|
|
isRichEdit ? pos_4 : pos_4_msw;
|
|
#else
|
|
pos_4;
|
|
#endif
|
|
|
|
for ( long y = 0; y < numLines_4+1; y++ )
|
|
for( long x = 0; x < maxLineLength_4+1; x++ )
|
|
{
|
|
long p = m_text->XYToPosition(x, y);
|
|
INFO("x=" << x << ", y=" << y);
|
|
CPPUNIT_ASSERT_EQUAL( ref_pos_4[y][x], p );
|
|
}
|
|
}
|
|
|
|
void TextCtrlTestCase::PositionToXYSingleLine()
|
|
{
|
|
delete m_text;
|
|
CreateText(wxTE_DONTWRAP);
|
|
|
|
bool ok;
|
|
wxString text;
|
|
// empty field
|
|
m_text->Clear();
|
|
const long numChars_0 = 0;
|
|
CPPUNIT_ASSERT_EQUAL( numChars_0, m_text->GetLastPosition() );
|
|
for ( long i = 0; i <= numChars_0; i++ )
|
|
{
|
|
long x0, y0;
|
|
ok = m_text->PositionToXY(i, &x0, &y0);
|
|
CPPUNIT_ASSERT_EQUAL( true, ok );
|
|
CPPUNIT_ASSERT_EQUAL( i, x0 );
|
|
CPPUNIT_ASSERT_EQUAL( 0, y0 );
|
|
}
|
|
ok = m_text->PositionToXY(numChars_0+1, NULL, NULL);
|
|
CPPUNIT_ASSERT_EQUAL( false, ok );
|
|
|
|
// pure one line
|
|
text = wxS("1234");
|
|
m_text->SetValue(text);
|
|
const long numChars_1 = text.Length();
|
|
CPPUNIT_ASSERT_EQUAL( numChars_1, m_text->GetLastPosition() );
|
|
for ( long i = 0; i <= numChars_1; i++ )
|
|
{
|
|
long x1, y1;
|
|
ok = m_text->PositionToXY(i, &x1, &y1);
|
|
CPPUNIT_ASSERT_EQUAL( true, ok );
|
|
CPPUNIT_ASSERT_EQUAL( i, x1 );
|
|
CPPUNIT_ASSERT_EQUAL( 0, y1 );
|
|
}
|
|
ok = m_text->PositionToXY(numChars_1+1, NULL, NULL);
|
|
CPPUNIT_ASSERT_EQUAL( false, ok );
|
|
|
|
// with new line characters
|
|
text = wxS("123\nab\nX");
|
|
m_text->SetValue(text);
|
|
const long numChars_2 = text.Length();
|
|
CPPUNIT_ASSERT_EQUAL( numChars_2, m_text->GetLastPosition() );
|
|
for ( long i = 0; i <= numChars_2; i++ )
|
|
{
|
|
long x2, y2;
|
|
ok = m_text->PositionToXY(i, &x2, &y2);
|
|
CPPUNIT_ASSERT_EQUAL( true, ok );
|
|
CPPUNIT_ASSERT_EQUAL( i, x2 );
|
|
CPPUNIT_ASSERT_EQUAL( 0, y2 );
|
|
}
|
|
ok = m_text->PositionToXY(numChars_2+1, NULL, NULL);
|
|
CPPUNIT_ASSERT_EQUAL( false, ok );
|
|
}
|
|
|
|
void TextCtrlTestCase::XYToPositionSingleLine()
|
|
{
|
|
delete m_text;
|
|
CreateText(wxTE_DONTWRAP);
|
|
|
|
wxString text;
|
|
// empty field
|
|
m_text->Clear();
|
|
CPPUNIT_ASSERT_EQUAL( 1, m_text->GetNumberOfLines() );
|
|
for( long x = 0; x < m_text->GetLastPosition()+2; x++ )
|
|
{
|
|
long p0 = m_text->XYToPosition(x, 0);
|
|
if ( x <= m_text->GetLastPosition() )
|
|
CPPUNIT_ASSERT_EQUAL( x, p0 );
|
|
else
|
|
CPPUNIT_ASSERT_EQUAL( -1, p0 );
|
|
|
|
p0 = m_text->XYToPosition(x, 1);
|
|
CPPUNIT_ASSERT_EQUAL( -1, p0 );
|
|
}
|
|
|
|
// pure one line
|
|
text = wxS("1234");
|
|
m_text->SetValue(text);
|
|
CPPUNIT_ASSERT_EQUAL( 1, m_text->GetNumberOfLines() );
|
|
for( long x = 0; x < m_text->GetLastPosition()+2; x++ )
|
|
{
|
|
long p1 = m_text->XYToPosition(x, 0);
|
|
if ( x <= m_text->GetLastPosition() )
|
|
CPPUNIT_ASSERT_EQUAL( x, p1 );
|
|
else
|
|
CPPUNIT_ASSERT_EQUAL( -1, p1 );
|
|
|
|
p1 = m_text->XYToPosition(x, 1);
|
|
CPPUNIT_ASSERT_EQUAL( -1, p1 );
|
|
}
|
|
|
|
// with new line characters
|
|
text = wxS("123\nab\nX");
|
|
m_text->SetValue(text);
|
|
CPPUNIT_ASSERT_EQUAL( 1, m_text->GetNumberOfLines() );
|
|
for( long x = 0; x < m_text->GetLastPosition()+2; x++ )
|
|
{
|
|
long p2 = m_text->XYToPosition(x, 0);
|
|
if ( x <= m_text->GetLastPosition() )
|
|
CPPUNIT_ASSERT_EQUAL( x, p2 );
|
|
else
|
|
CPPUNIT_ASSERT_EQUAL( -1, p2 );
|
|
|
|
p2 = m_text->XYToPosition(x, 1);
|
|
CPPUNIT_ASSERT_EQUAL( -1, p2 );
|
|
}
|
|
}
|
|
|
|
TEST_CASE("wxTextCtrl::ProcessEnter", "[wxTextCtrl][enter]")
|
|
{
|
|
class TextCtrlCreator : public TextLikeControlCreator
|
|
{
|
|
public:
|
|
explicit TextCtrlCreator(int styleToAdd = 0)
|
|
: m_styleToAdd(styleToAdd)
|
|
{
|
|
}
|
|
|
|
virtual wxControl* Create(wxWindow* parent, int style) const wxOVERRIDE
|
|
{
|
|
return new wxTextCtrl(parent, wxID_ANY, wxString(),
|
|
wxDefaultPosition, wxDefaultSize,
|
|
style | m_styleToAdd);
|
|
}
|
|
|
|
virtual TextLikeControlCreator* CloneAsMultiLine() const wxOVERRIDE
|
|
{
|
|
return new TextCtrlCreator(wxTE_MULTILINE);
|
|
}
|
|
|
|
private:
|
|
int m_styleToAdd;
|
|
};
|
|
|
|
TestProcessEnter(TextCtrlCreator());
|
|
}
|
|
|
|
TEST_CASE("wxTextCtrl::GetBestSize", "[wxTextCtrl][best-size]")
|
|
{
|
|
struct GetBestSizeFor
|
|
{
|
|
wxSize operator()(const wxString& text) const
|
|
{
|
|
wxScopedPtr<wxTextCtrl>
|
|
t(new wxTextCtrl(wxTheApp->GetTopWindow(), wxID_ANY, text,
|
|
wxDefaultPosition, wxDefaultSize,
|
|
wxTE_MULTILINE));
|
|
return t->GetBestSize();
|
|
}
|
|
} getBestSizeFor;
|
|
|
|
wxString s;
|
|
const wxSize sizeEmpty = getBestSizeFor(s);
|
|
|
|
// Empty control should have some reasonable vertical size.
|
|
CHECK( sizeEmpty.y > 0 );
|
|
|
|
s += "1\n2\n3\n4\n5\n";
|
|
const wxSize sizeMedium = getBestSizeFor(s);
|
|
|
|
// Control with a few lines of text in it should be taller.
|
|
CHECK( sizeMedium.y > sizeEmpty.y );
|
|
|
|
s += "6\n7\n8\n9\n10\n";
|
|
const wxSize sizeLong = getBestSizeFor(s);
|
|
|
|
// And a control with many lines in it should be even more so.
|
|
CHECK( sizeLong.y > sizeMedium.y );
|
|
|
|
s += s;
|
|
s += s;
|
|
s += s;
|
|
const wxSize sizeVeryLong = getBestSizeFor(s);
|
|
|
|
// However there is a cutoff at 10 lines currently, so anything longer than
|
|
// that should still have the same best size.
|
|
CHECK( sizeVeryLong.y == sizeLong.y );
|
|
}
|
|
|
|
#if wxUSE_CLIPBOARD
|
|
|
|
TEST_CASE("wxTextCtrl::LongPaste", "[wxTextCtrl][clipboard][paste]")
|
|
{
|
|
long style = 0;
|
|
|
|
SECTION("Plain")
|
|
{
|
|
style = wxTE_MULTILINE;
|
|
}
|
|
|
|
// wxTE_RICH[2] style only makes any different under MSW, so don't bother
|
|
// testing it under the other platforms.
|
|
#ifdef __WXMSW__
|
|
SECTION("Rich")
|
|
{
|
|
style = wxTE_MULTILINE | wxTE_RICH;
|
|
}
|
|
|
|
SECTION("Rich v2")
|
|
{
|
|
style = wxTE_MULTILINE | wxTE_RICH2;
|
|
}
|
|
#endif // __WXMSW__
|
|
|
|
if ( !style )
|
|
{
|
|
// This can happen when explicitly selecting just a single section to
|
|
// execute -- this code still runs even if the corresponding section is
|
|
// skipped, so we have to explicitly skip it too in this case.
|
|
return;
|
|
}
|
|
|
|
wxScopedPtr<wxTextCtrl>
|
|
text(new wxTextCtrl(wxTheApp->GetTopWindow(), wxID_ANY, wxString(),
|
|
wxDefaultPosition, wxDefaultSize, style));
|
|
|
|
// This could actually be much higher, but it makes the test proportionally
|
|
// slower, so use a relatively small (but still requiring more space than
|
|
// the default maximum length under MSW) number here.
|
|
const int NUM_LINES = 10000;
|
|
|
|
{
|
|
wxClipboardLocker lock;
|
|
|
|
// Build a longish string.
|
|
wxString s;
|
|
s.reserve(NUM_LINES*5 + 10);
|
|
for ( int n = 0; n < NUM_LINES; ++n )
|
|
{
|
|
s += wxString::Format("%04d\n", n);
|
|
}
|
|
|
|
s += "THE END";
|
|
|
|
wxTheClipboard->AddData(new wxTextDataObject(s));
|
|
}
|
|
|
|
text->ChangeValue("THE BEGINNING\n");
|
|
text->SetInsertionPointEnd();
|
|
text->Paste();
|
|
|
|
const int numLines = text->GetNumberOfLines();
|
|
|
|
CHECK( numLines == NUM_LINES + 2 );
|
|
CHECK( text->GetLineText(numLines - 1) == "THE END" );
|
|
}
|
|
|
|
#endif // wxUSE_CLIPBOARD
|
|
|
|
TEST_CASE("wxTextCtrl::EventsOnCreate", "[wxTextCtrl][event]")
|
|
{
|
|
wxWindow* const parent = wxTheApp->GetTopWindow();
|
|
|
|
EventCounter updated(parent, wxEVT_TEXT);
|
|
|
|
wxScopedPtr<wxTextCtrl> text(new wxTextCtrl(parent, wxID_ANY, "Hello"));
|
|
|
|
// Creating the control shouldn't result in any wxEVT_TEXT events.
|
|
CHECK( updated.GetCount() == 0 );
|
|
|
|
// Check that modifying using SetValue() it does generate the event, just
|
|
// to verify that this test works (there are more detailed tests for this
|
|
// in TextEntryTestCase::TextChangeEvents()).
|
|
text->SetValue("Bye");
|
|
CHECK( updated.GetCount() == 1 );
|
|
}
|
|
|
|
TEST_CASE("wxTextCtrl::InitialCanUndo", "[wxTextCtrl][undo]")
|
|
{
|
|
wxWindow* const parent = wxTheApp->GetTopWindow();
|
|
|
|
const long styles[] = { 0, wxTE_RICH, wxTE_RICH2 };
|
|
|
|
for ( size_t n = 0; n < WXSIZEOF(styles); n++ )
|
|
{
|
|
const long style = styles[n];
|
|
|
|
#ifdef __MINGW32_TOOLCHAIN__
|
|
if ( style == wxTE_RICH2 )
|
|
{
|
|
// We can't call ITextDocument::Undo() in wxMSW code when using
|
|
// MinGW32, so this test would always fail with it.
|
|
WARN("Skipping test known to fail with MinGW-32.");
|
|
}
|
|
continue;
|
|
#endif // __MINGW32_TOOLCHAIN__
|
|
|
|
INFO("wxTextCtrl with style " << style);
|
|
|
|
wxScopedPtr<wxTextCtrl> text(new wxTextCtrl(parent, wxID_ANY, "",
|
|
wxDefaultPosition,
|
|
wxDefaultSize,
|
|
style));
|
|
|
|
CHECK( !text->CanUndo() );
|
|
}
|
|
}
|
|
|
|
// This test would always fail with MinGW-32 for the same reason as described
|
|
// above.
|
|
#ifndef __MINGW32_TOOLCHAIN__
|
|
|
|
TEST_CASE("wxTextCtrl::EmptyUndoBuffer", "[wxTextCtrl][undo]")
|
|
{
|
|
if ( wxIsRunningUnderWine() )
|
|
{
|
|
// Wine doesn't implement EM_GETOLEINTERFACE and related stuff currently
|
|
WARN("Skipping test known to fail under Wine.");
|
|
return;
|
|
}
|
|
|
|
wxScopedPtr<wxTextCtrl> text(new wxTextCtrl(wxTheApp->GetTopWindow(),
|
|
wxID_ANY, "",
|
|
wxDefaultPosition,
|
|
wxDefaultSize,
|
|
wxTE_MULTILINE | wxTE_RICH2));
|
|
|
|
text->AppendText("foo");
|
|
|
|
if ( !text->CanUndo() )
|
|
{
|
|
WARN("Skipping test as Undo() is not supported on this platform.");
|
|
return;
|
|
}
|
|
|
|
text->EmptyUndoBuffer();
|
|
|
|
CHECK_FALSE( text->CanUndo() );
|
|
|
|
CHECK_NOTHROW( text->Undo() );
|
|
|
|
CHECK( text->GetValue() == "foo" );
|
|
}
|
|
|
|
#endif // __MINGW32_TOOLCHAIN__
|
|
|
|
#endif //wxUSE_TEXTCTRL
|