1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: tests/controls/textctrltest.cpp
3 // Purpose: wxTextCtrl unit test
4 // Author: Vadim Zeitlin
7 // Copyright: (c) 2007 Vadim Zeitlin <vadim@wxwidgets.org>
8 ///////////////////////////////////////////////////////////////////////////////
10 // ----------------------------------------------------------------------------
12 // ----------------------------------------------------------------------------
24 #include "wx/textctrl.h"
27 #include "wx/scopeguard.h"
29 #include "textentrytest.h"
30 #include "testableframe.h"
31 #include "asserthelper.h"
32 #include "wx/uiaction.h"
34 // ----------------------------------------------------------------------------
36 // ----------------------------------------------------------------------------
38 class TextCtrlTestCase
: public TextEntryTestCase
, public CppUnit::TestCase
41 TextCtrlTestCase() { }
44 virtual void tearDown();
47 virtual wxTextEntry
*GetTestEntry() const { return m_text
; }
48 virtual wxWindow
*GetTestWindow() const { return m_text
; }
50 CPPUNIT_TEST_SUITE( TextCtrlTestCase
);
52 CPPUNIT_TEST( MultiLineReplace
);
53 WXUISIM_TEST( ReadOnly
);
54 WXUISIM_TEST( MaxLength
);
55 CPPUNIT_TEST( StreamInput
);
56 CPPUNIT_TEST( Redirector
);
57 //WXUISIM_TEST( ProcessEnter );
59 CPPUNIT_TEST( Style
);
60 CPPUNIT_TEST( Lines
);
61 CPPUNIT_TEST( LogTextCtrl
);
62 CPPUNIT_TEST( PositionToCoords
);
63 CPPUNIT_TEST( PositionToCoordsRich
);
64 CPPUNIT_TEST( PositionToCoordsRich2
);
65 CPPUNIT_TEST_SUITE_END();
67 void MultiLineReplace();
72 //void ProcessEnter();
77 void PositionToCoords();
78 void PositionToCoordsRich();
79 void PositionToCoordsRich2();
81 void DoPositionToCoordsTestWithStyle(long style
);
85 DECLARE_NO_COPY_CLASS(TextCtrlTestCase
)
88 // register in the unnamed registry so that these tests are run by default
89 CPPUNIT_TEST_SUITE_REGISTRATION( TextCtrlTestCase
);
91 // also include in its own registry so that these tests can be run alone
92 CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( TextCtrlTestCase
, "TextCtrlTestCase" );
94 // ----------------------------------------------------------------------------
95 // test initialization
96 // ----------------------------------------------------------------------------
98 void TextCtrlTestCase::setUp()
100 m_text
= new wxTextCtrl(wxTheApp
->GetTopWindow(), wxID_ANY
);
103 void TextCtrlTestCase::tearDown()
108 // ----------------------------------------------------------------------------
110 // ----------------------------------------------------------------------------
112 void TextCtrlTestCase::MultiLineReplace()
114 // we need a multiline control for this test so recreate it
116 m_text
= new wxTextCtrl(wxTheApp
->GetTopWindow(), wxID_ANY
, "",
117 wxDefaultPosition
, wxDefaultSize
,
120 m_text
->SetValue("Hello replace\n"
122 m_text
->SetInsertionPoint(0);
124 m_text
->Replace(6, 13, "changed");
126 CPPUNIT_ASSERT_EQUAL("Hello changed\n"
129 CPPUNIT_ASSERT_EQUAL(13, m_text
->GetInsertionPoint());
131 m_text
->Replace(13, -1, "");
132 CPPUNIT_ASSERT_EQUAL("Hello changed", m_text
->GetValue());
133 CPPUNIT_ASSERT_EQUAL(13, m_text
->GetInsertionPoint());
136 m_text
= new wxTextCtrl(wxTheApp
->GetTopWindow(), wxID_ANY
);
139 void TextCtrlTestCase::ReadOnly()
141 #if wxUSE_UIACTIONSIMULATOR
142 // we need a read only control for this test so recreate it
144 m_text
= new wxTextCtrl(wxTheApp
->GetTopWindow(), wxID_ANY
, "",
145 wxDefaultPosition
, wxDefaultSize
,
148 wxTestableFrame
* frame
= wxStaticCast(wxTheApp
->GetTopWindow(),
151 EventCounter
count(m_text
, wxEVT_COMMAND_TEXT_UPDATED
);
155 wxUIActionSimulator sim
;
159 CPPUNIT_ASSERT_EQUAL("", m_text
->GetValue());
160 CPPUNIT_ASSERT_EQUAL(0, frame
->GetEventCount());
162 // SetEditable() is supposed to override wxTE_READONLY
163 m_text
->SetEditable(true);
168 CPPUNIT_ASSERT_EQUAL("abcdef", m_text
->GetValue());
169 CPPUNIT_ASSERT_EQUAL(6, frame
->GetEventCount());
172 m_text
= new wxTextCtrl(wxTheApp
->GetTopWindow(), wxID_ANY
);
176 void TextCtrlTestCase::MaxLength()
178 #if wxUSE_UIACTIONSIMULATOR
179 wxTestableFrame
* frame
= wxStaticCast(wxTheApp
->GetTopWindow(),
182 EventCounter
count(m_text
, wxEVT_COMMAND_TEXT_UPDATED
);
183 EventCounter
count1(m_text
, wxEVT_COMMAND_TEXT_MAXLEN
);
186 m_text
->SetMaxLength(10);
188 wxUIActionSimulator sim
;
192 CPPUNIT_ASSERT_EQUAL(0, frame
->GetEventCount(wxEVT_COMMAND_TEXT_MAXLEN
));
197 CPPUNIT_ASSERT_EQUAL(0, frame
->GetEventCount(wxEVT_COMMAND_TEXT_MAXLEN
));
198 CPPUNIT_ASSERT_EQUAL(10, frame
->GetEventCount(wxEVT_COMMAND_TEXT_UPDATED
));
203 CPPUNIT_ASSERT_EQUAL(1, frame
->GetEventCount(wxEVT_COMMAND_TEXT_MAXLEN
));
204 CPPUNIT_ASSERT_EQUAL(0, frame
->GetEventCount(wxEVT_COMMAND_TEXT_UPDATED
));
206 m_text
->SetMaxLength(0);
211 CPPUNIT_ASSERT_EQUAL(0, frame
->GetEventCount(wxEVT_COMMAND_TEXT_MAXLEN
));
212 CPPUNIT_ASSERT_EQUAL(1, frame
->GetEventCount(wxEVT_COMMAND_TEXT_UPDATED
));
216 void TextCtrlTestCase::StreamInput()
220 // Ensure we use decimal point and not a comma.
221 char * const locOld
= setlocale(LC_NUMERIC
, "C");
222 wxON_BLOCK_EXIT2( setlocale
, (int)LC_NUMERIC
, locOld
);
224 *m_text
<< "stringinput"
233 CPPUNIT_ASSERT_EQUAL("stringinput1010003.142.71ab", m_text
->GetValue());
235 m_text
->SetValue("");
237 #if wxHAS_TEXT_WINDOW_STREAM
239 std::ostream
stream(m_text
);
241 // We don't test a wide character as this is not a wide stream
242 stream
<< "stringinput"
251 CPPUNIT_ASSERT_EQUAL("stringinput1010003.142.71a", m_text
->GetValue());
253 #endif // wxHAS_TEXT_WINDOW_STREAM
257 void TextCtrlTestCase::Redirector()
259 #if wxHAS_TEXT_WINDOW_STREAM && wxUSE_STD_IOSTREAM
261 wxStreamToTextRedirector
redirect(m_text
);
263 std::cout
<< "stringinput"
270 CPPUNIT_ASSERT_EQUAL("stringinput1010003.142.71a", m_text
->GetValue());
276 void TextCtrlTestCase::ProcessEnter()
278 #if wxUSE_UIACTIONSIMULATOR
279 wxTestableFrame
* frame
= wxStaticCast(wxTheApp
->GetTopWindow(),
282 EventCounter
count(m_text
, wxEVT_COMMAND_TEXT_ENTER
);
286 wxUIActionSimulator sim
;
287 sim
.Char(WXK_RETURN
);
290 CPPUNIT_ASSERT_EQUAL(0, frame
->GetEventCount(wxEVT_COMMAND_TEXT_ENTER
));
292 // we need a text control with wxTE_PROCESS_ENTER for this test
294 m_text
= new wxTextCtrl(wxTheApp
->GetTopWindow(), wxID_ANY
, "",
295 wxDefaultPosition
, wxDefaultSize
,
300 sim
.Char(WXK_RETURN
);
303 CPPUNIT_ASSERT_EQUAL(1, frame
->GetEventCount(wxEVT_COMMAND_TEXT_ENTER
));
308 void TextCtrlTestCase::Url()
310 #if wxUSE_UIACTIONSIMULATOR && defined(__WXMSW__)
312 m_text
= new wxTextCtrl(wxTheApp
->GetTopWindow(), wxID_ANY
, "",
313 wxDefaultPosition
, wxDefaultSize
,
314 wxTE_MULTILINE
| wxTE_RICH
| wxTE_AUTO_URL
);
316 wxTestableFrame
* frame
= wxStaticCast(wxTheApp
->GetTopWindow(),
319 EventCounter
count(m_text
, wxEVT_COMMAND_TEXT_URL
);
321 m_text
->AppendText("http://www.wxwidgets.org");
323 wxUIActionSimulator sim
;
324 sim
.MouseMove(m_text
->ClientToScreen(wxPoint(5, 5)));
328 CPPUNIT_ASSERT_EQUAL(1, frame
->GetEventCount());
332 void TextCtrlTestCase::Style()
336 // We need wxTE_RICH under windows for style support
337 m_text
= new wxTextCtrl(wxTheApp
->GetTopWindow(), wxID_ANY
, "",
338 wxDefaultPosition
, wxDefaultSize
, wxTE_RICH
);
340 // Red text on a white background
341 m_text
->SetDefaultStyle(wxTextAttr(*wxRED
, *wxWHITE
));
343 CPPUNIT_ASSERT_EQUAL(m_text
->GetDefaultStyle().GetTextColour(), *wxRED
);
344 CPPUNIT_ASSERT_EQUAL(m_text
->GetDefaultStyle().GetBackgroundColour(),
347 m_text
->AppendText("red on white ");
349 // Red text on a grey background
350 m_text
->SetDefaultStyle(wxTextAttr(wxNullColour
, *wxLIGHT_GREY
));
352 CPPUNIT_ASSERT_EQUAL(m_text
->GetDefaultStyle().GetTextColour(), *wxRED
);
353 CPPUNIT_ASSERT_EQUAL(m_text
->GetDefaultStyle().GetBackgroundColour(),
356 m_text
->AppendText("red on grey ");
358 // Blue text on a grey background
359 m_text
->SetDefaultStyle(wxTextAttr(*wxBLUE
));
362 CPPUNIT_ASSERT_EQUAL(m_text
->GetDefaultStyle().GetTextColour(), *wxBLUE
);
363 CPPUNIT_ASSERT_EQUAL(m_text
->GetDefaultStyle().GetBackgroundColour(),
366 m_text
->AppendText("blue on grey");
368 // Get getting the style at a specific location
371 // We have to check that styles are supported
372 if(m_text
->GetStyle(3, style
))
374 CPPUNIT_ASSERT_EQUAL(style
.GetTextColour(), *wxRED
);
375 CPPUNIT_ASSERT_EQUAL(style
.GetBackgroundColour(), *wxWHITE
);
378 // And then setting the style
379 if(m_text
->SetStyle(15, 18, style
))
381 m_text
->GetStyle(17, style
);
383 CPPUNIT_ASSERT_EQUAL(style
.GetTextColour(), *wxRED
);
384 CPPUNIT_ASSERT_EQUAL(style
.GetBackgroundColour(), *wxWHITE
);
389 void TextCtrlTestCase::Lines()
392 m_text
= new wxTextCtrl(wxTheApp
->GetTopWindow(), wxID_ANY
, "",
393 wxDefaultPosition
, wxSize(400, 200), wxTE_MULTILINE
);
395 m_text
->SetValue("line1\nline2\nlong long line 3");
399 CPPUNIT_ASSERT_EQUAL(3, m_text
->GetNumberOfLines());
400 CPPUNIT_ASSERT_EQUAL(5, m_text
->GetLineLength(0));
401 CPPUNIT_ASSERT_EQUAL("line2", m_text
->GetLineText(1));
402 CPPUNIT_ASSERT_EQUAL(16, m_text
->GetLineLength(2));
404 m_text
->AppendText("\n\nMore text on line 5");
406 CPPUNIT_ASSERT_EQUAL(5, m_text
->GetNumberOfLines());
407 CPPUNIT_ASSERT_EQUAL(0, m_text
->GetLineLength(3));
408 CPPUNIT_ASSERT_EQUAL("", m_text
->GetLineText(3));
410 // Verify that wrapped lines count as 2 lines.
412 // This currently doesn't work neither in wxGTK nor wxOSX/Cocoa, see
413 // #12366, where GetNumberOfLines() always returns the number of logical,
414 // not physical, lines.
415 m_text
->AppendText("\n" + wxString(50, '1') + ' ' + wxString(50, '2'));
416 #if defined(__WXGTK__) || defined(__WXOSX_COCOA__)
417 CPPUNIT_ASSERT_EQUAL(6, m_text
->GetNumberOfLines());
419 CPPUNIT_ASSERT_EQUAL(7, m_text
->GetNumberOfLines());
423 void TextCtrlTestCase::LogTextCtrl()
426 m_text
= new wxTextCtrl(wxTheApp
->GetTopWindow(), wxID_ANY
, "",
427 wxDefaultPosition
, wxSize(400, 200),
430 CPPUNIT_ASSERT(m_text
->IsEmpty());
432 wxLogTextCtrl
* logtext
= new wxLogTextCtrl(m_text
);
434 wxLog
* old
= wxLog::SetActiveTarget(logtext
);
436 logtext
->LogText("text");
438 delete wxLog::SetActiveTarget(old
);
440 CPPUNIT_ASSERT(!m_text
->IsEmpty());
443 void TextCtrlTestCase::PositionToCoords()
445 DoPositionToCoordsTestWithStyle(0);
448 void TextCtrlTestCase::PositionToCoordsRich()
450 DoPositionToCoordsTestWithStyle(wxTE_RICH
);
453 void TextCtrlTestCase::PositionToCoordsRich2()
455 DoPositionToCoordsTestWithStyle(wxTE_RICH2
);
458 void TextCtrlTestCase::DoPositionToCoordsTestWithStyle(long style
)
460 static const int TEXT_HEIGHT
= 200;
463 m_text
= new wxTextCtrl(wxTheApp
->GetTopWindow(), wxID_ANY
, "",
464 wxDefaultPosition
, wxSize(400, TEXT_HEIGHT
),
465 wxTE_MULTILINE
| style
);
467 // Asking for invalid index should fail.
468 WX_ASSERT_FAILS_WITH_ASSERT( m_text
->PositionToCoords(1) );
470 // Getting position shouldn't return wxDefaultPosition except if the method
471 // is not implemented at all in the current port.
472 const wxPoint pos0
= m_text
->PositionToCoords(0);
473 if ( pos0
== wxDefaultPosition
)
475 #if defined(__WXMSW__) || defined(__WXGTK20__)
476 CPPUNIT_FAIL( "PositionToCoords() unexpectedly failed." );
481 CPPUNIT_ASSERT(pos0
.x
>= 0);
482 CPPUNIT_ASSERT(pos0
.y
>= 0);
485 m_text
->SetValue("Hello");
486 wxYield(); // Let GTK layout the control correctly.
488 // Position of non-first character should be positive.
489 const long posHello4
= m_text
->PositionToCoords(4).x
;
490 CPPUNIT_ASSERT( posHello4
> 0 );
492 // Asking for position beyond the last character should succeed and return
493 // reasonable result.
494 CPPUNIT_ASSERT( m_text
->PositionToCoords(5).x
> posHello4
);
496 // But asking for the next position should fail.
497 WX_ASSERT_FAILS_WITH_ASSERT( m_text
->PositionToCoords(6) );
499 // Test getting the coordinates of the last character when it is in the
500 // beginning of a new line to exercise MSW code which has specific logic
502 m_text
->AppendText("\n");
503 const wxPoint posLast
= m_text
->PositionToCoords(m_text
->GetLastPosition());
504 CPPUNIT_ASSERT_EQUAL( pos0
.x
, posLast
.x
);
505 CPPUNIT_ASSERT( posLast
.y
> 0 );
508 // Add enough contents to the control to make sure it has a scrollbar.
509 m_text
->SetValue("First line" + wxString(50, '\n') + "Last line");
510 m_text
->SetInsertionPoint(0);
511 wxYield(); // Let GTK layout the control correctly.
513 // This shouldn't change anything for the first position coordinates.
514 CPPUNIT_ASSERT_EQUAL( pos0
, m_text
->PositionToCoords(0) );
516 // And the last one must be beyond the window boundary and so not be
517 // visible -- but getting its coordinate should still work.
520 m_text
->PositionToCoords(m_text
->GetLastPosition()).y
> TEXT_HEIGHT
524 // Now make it scroll to the end and check that the first position now has
525 // negative offset as its above the visible part of the window while the
526 // last position is in its bounds.
527 m_text
->SetInsertionPointEnd();
529 CPPUNIT_ASSERT( m_text
->PositionToCoords(0).y
< 0 );
532 m_text
->PositionToCoords(m_text
->GetInsertionPoint()).y
<= TEXT_HEIGHT
537 #endif //wxUSE_TEXTCTRL