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( FontStyle
);
61 CPPUNIT_TEST( Lines
);
62 CPPUNIT_TEST( LogTextCtrl
);
63 CPPUNIT_TEST( PositionToCoords
);
64 CPPUNIT_TEST( PositionToCoordsRich
);
65 CPPUNIT_TEST( PositionToCoordsRich2
);
66 CPPUNIT_TEST_SUITE_END();
68 void MultiLineReplace();
73 //void ProcessEnter();
79 void PositionToCoords();
80 void PositionToCoordsRich();
81 void PositionToCoordsRich2();
83 void DoPositionToCoordsTestWithStyle(long style
);
87 DECLARE_NO_COPY_CLASS(TextCtrlTestCase
)
90 // register in the unnamed registry so that these tests are run by default
91 CPPUNIT_TEST_SUITE_REGISTRATION( TextCtrlTestCase
);
93 // also include in its own registry so that these tests can be run alone
94 CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( TextCtrlTestCase
, "TextCtrlTestCase" );
96 // ----------------------------------------------------------------------------
97 // test initialization
98 // ----------------------------------------------------------------------------
100 void TextCtrlTestCase::setUp()
102 m_text
= new wxTextCtrl(wxTheApp
->GetTopWindow(), wxID_ANY
);
105 void TextCtrlTestCase::tearDown()
110 // ----------------------------------------------------------------------------
112 // ----------------------------------------------------------------------------
114 void TextCtrlTestCase::MultiLineReplace()
116 // we need a multiline control for this test so recreate it
118 m_text
= new wxTextCtrl(wxTheApp
->GetTopWindow(), wxID_ANY
, "",
119 wxDefaultPosition
, wxDefaultSize
,
122 m_text
->SetValue("Hello replace\n"
124 m_text
->SetInsertionPoint(0);
126 m_text
->Replace(6, 13, "changed");
128 CPPUNIT_ASSERT_EQUAL("Hello changed\n"
131 CPPUNIT_ASSERT_EQUAL(13, m_text
->GetInsertionPoint());
133 m_text
->Replace(13, -1, "");
134 CPPUNIT_ASSERT_EQUAL("Hello changed", m_text
->GetValue());
135 CPPUNIT_ASSERT_EQUAL(13, m_text
->GetInsertionPoint());
138 m_text
= new wxTextCtrl(wxTheApp
->GetTopWindow(), wxID_ANY
);
141 void TextCtrlTestCase::ReadOnly()
143 #if wxUSE_UIACTIONSIMULATOR
144 // we need a read only control for this test so recreate it
146 m_text
= new wxTextCtrl(wxTheApp
->GetTopWindow(), wxID_ANY
, "",
147 wxDefaultPosition
, wxDefaultSize
,
150 wxTestableFrame
* frame
= wxStaticCast(wxTheApp
->GetTopWindow(),
153 EventCounter
count(m_text
, wxEVT_COMMAND_TEXT_UPDATED
);
157 wxUIActionSimulator sim
;
161 CPPUNIT_ASSERT_EQUAL("", m_text
->GetValue());
162 CPPUNIT_ASSERT_EQUAL(0, frame
->GetEventCount());
164 // SetEditable() is supposed to override wxTE_READONLY
165 m_text
->SetEditable(true);
170 CPPUNIT_ASSERT_EQUAL("abcdef", m_text
->GetValue());
171 CPPUNIT_ASSERT_EQUAL(6, frame
->GetEventCount());
174 m_text
= new wxTextCtrl(wxTheApp
->GetTopWindow(), wxID_ANY
);
178 void TextCtrlTestCase::MaxLength()
180 #if wxUSE_UIACTIONSIMULATOR
181 wxTestableFrame
* frame
= wxStaticCast(wxTheApp
->GetTopWindow(),
184 EventCounter
count(m_text
, wxEVT_COMMAND_TEXT_UPDATED
);
185 EventCounter
count1(m_text
, wxEVT_COMMAND_TEXT_MAXLEN
);
188 m_text
->SetMaxLength(10);
190 wxUIActionSimulator sim
;
194 CPPUNIT_ASSERT_EQUAL(0, frame
->GetEventCount(wxEVT_COMMAND_TEXT_MAXLEN
));
199 CPPUNIT_ASSERT_EQUAL(0, frame
->GetEventCount(wxEVT_COMMAND_TEXT_MAXLEN
));
200 CPPUNIT_ASSERT_EQUAL(10, frame
->GetEventCount(wxEVT_COMMAND_TEXT_UPDATED
));
205 CPPUNIT_ASSERT_EQUAL(1, frame
->GetEventCount(wxEVT_COMMAND_TEXT_MAXLEN
));
206 CPPUNIT_ASSERT_EQUAL(0, frame
->GetEventCount(wxEVT_COMMAND_TEXT_UPDATED
));
208 m_text
->SetMaxLength(0);
213 CPPUNIT_ASSERT_EQUAL(0, frame
->GetEventCount(wxEVT_COMMAND_TEXT_MAXLEN
));
214 CPPUNIT_ASSERT_EQUAL(1, frame
->GetEventCount(wxEVT_COMMAND_TEXT_UPDATED
));
218 void TextCtrlTestCase::StreamInput()
222 // Ensure we use decimal point and not a comma.
223 char * const locOld
= setlocale(LC_NUMERIC
, "C");
224 wxON_BLOCK_EXIT2( setlocale
, (int)LC_NUMERIC
, locOld
);
226 *m_text
<< "stringinput"
235 CPPUNIT_ASSERT_EQUAL("stringinput1010003.142.71ab", m_text
->GetValue());
237 m_text
->SetValue("");
239 #if wxHAS_TEXT_WINDOW_STREAM
241 std::ostream
stream(m_text
);
243 // We don't test a wide character as this is not a wide stream
244 stream
<< "stringinput"
253 CPPUNIT_ASSERT_EQUAL("stringinput1010003.142.71a", m_text
->GetValue());
255 #endif // wxHAS_TEXT_WINDOW_STREAM
259 void TextCtrlTestCase::Redirector()
261 #if wxHAS_TEXT_WINDOW_STREAM && wxUSE_STD_IOSTREAM
263 wxStreamToTextRedirector
redirect(m_text
);
265 std::cout
<< "stringinput"
272 CPPUNIT_ASSERT_EQUAL("stringinput1010003.142.71a", m_text
->GetValue());
278 void TextCtrlTestCase::ProcessEnter()
280 #if wxUSE_UIACTIONSIMULATOR
281 wxTestableFrame
* frame
= wxStaticCast(wxTheApp
->GetTopWindow(),
284 EventCounter
count(m_text
, wxEVT_COMMAND_TEXT_ENTER
);
288 wxUIActionSimulator sim
;
289 sim
.Char(WXK_RETURN
);
292 CPPUNIT_ASSERT_EQUAL(0, frame
->GetEventCount(wxEVT_COMMAND_TEXT_ENTER
));
294 // we need a text control with wxTE_PROCESS_ENTER for this test
296 m_text
= new wxTextCtrl(wxTheApp
->GetTopWindow(), wxID_ANY
, "",
297 wxDefaultPosition
, wxDefaultSize
,
302 sim
.Char(WXK_RETURN
);
305 CPPUNIT_ASSERT_EQUAL(1, frame
->GetEventCount(wxEVT_COMMAND_TEXT_ENTER
));
310 void TextCtrlTestCase::Url()
312 #if wxUSE_UIACTIONSIMULATOR && defined(__WXMSW__)
314 m_text
= new wxTextCtrl(wxTheApp
->GetTopWindow(), wxID_ANY
, "",
315 wxDefaultPosition
, wxDefaultSize
,
316 wxTE_MULTILINE
| wxTE_RICH
| wxTE_AUTO_URL
);
318 wxTestableFrame
* frame
= wxStaticCast(wxTheApp
->GetTopWindow(),
321 EventCounter
count(m_text
, wxEVT_COMMAND_TEXT_URL
);
323 m_text
->AppendText("http://www.wxwidgets.org");
325 wxUIActionSimulator sim
;
326 sim
.MouseMove(m_text
->ClientToScreen(wxPoint(5, 5)));
330 CPPUNIT_ASSERT_EQUAL(1, frame
->GetEventCount());
334 void TextCtrlTestCase::Style()
338 // We need wxTE_RICH under windows for style support
339 m_text
= new wxTextCtrl(wxTheApp
->GetTopWindow(), wxID_ANY
, "",
340 wxDefaultPosition
, wxDefaultSize
, wxTE_RICH
);
342 // Red text on a white background
343 m_text
->SetDefaultStyle(wxTextAttr(*wxRED
, *wxWHITE
));
345 CPPUNIT_ASSERT_EQUAL(m_text
->GetDefaultStyle().GetTextColour(), *wxRED
);
346 CPPUNIT_ASSERT_EQUAL(m_text
->GetDefaultStyle().GetBackgroundColour(),
349 m_text
->AppendText("red on white ");
351 // Red text on a grey background
352 m_text
->SetDefaultStyle(wxTextAttr(wxNullColour
, *wxLIGHT_GREY
));
354 CPPUNIT_ASSERT_EQUAL(m_text
->GetDefaultStyle().GetTextColour(), *wxRED
);
355 CPPUNIT_ASSERT_EQUAL(m_text
->GetDefaultStyle().GetBackgroundColour(),
358 m_text
->AppendText("red on grey ");
360 // Blue text on a grey background
361 m_text
->SetDefaultStyle(wxTextAttr(*wxBLUE
));
364 CPPUNIT_ASSERT_EQUAL(m_text
->GetDefaultStyle().GetTextColour(), *wxBLUE
);
365 CPPUNIT_ASSERT_EQUAL(m_text
->GetDefaultStyle().GetBackgroundColour(),
368 m_text
->AppendText("blue on grey");
370 // Get getting the style at a specific location
373 // We have to check that styles are supported
374 if(m_text
->GetStyle(3, style
))
376 CPPUNIT_ASSERT_EQUAL(style
.GetTextColour(), *wxRED
);
377 CPPUNIT_ASSERT_EQUAL(style
.GetBackgroundColour(), *wxWHITE
);
380 // And then setting the style
381 if(m_text
->SetStyle(15, 18, style
))
383 m_text
->GetStyle(17, style
);
385 CPPUNIT_ASSERT_EQUAL(style
.GetTextColour(), *wxRED
);
386 CPPUNIT_ASSERT_EQUAL(style
.GetBackgroundColour(), *wxWHITE
);
391 void TextCtrlTestCase::FontStyle()
393 // We need wxTE_RICH under MSW and wxTE_MULTILINE under GTK for style
394 // support so recreate the control with these styles.
396 m_text
= new wxTextCtrl(wxTheApp
->GetTopWindow(), wxID_ANY
, "",
397 wxDefaultPosition
, wxDefaultSize
,
398 wxTE_MULTILINE
| wxTE_RICH
);
400 // Check that we get back the same font from GetStyle() after setting it
401 // with SetDefaultStyle().
403 wxFONTFAMILY_DEFAULT
,
405 wxFONTWEIGHT_NORMAL
);
407 attrIn
.SetFont(fontIn
);
408 if ( !m_text
->SetDefaultStyle(attrIn
) )
410 // Skip the test if the styles are not supported.
414 m_text
->AppendText("Default font size 14");
417 m_text
->GetStyle(5, attrOut
);
419 CPPUNIT_ASSERT( attrOut
.HasFont() );
421 wxFont fontOut
= attrOut
.GetFont();
423 // Under MSW we get back an encoding in the font even though we hadn't
424 // specified it originally. It's not really a problem but we need this hack
425 // to prevent the assert below from failing because of it.
426 fontOut
.SetEncoding(fontIn
.GetEncoding());
428 CPPUNIT_ASSERT_EQUAL( fontIn
, fontOut
);
431 // Also check the same for SetStyle().
432 fontIn
.SetPointSize(10);
433 fontIn
.SetWeight(wxFONTWEIGHT_BOLD
);
434 attrIn
.SetFont(fontIn
);
435 m_text
->SetStyle(0, 6, attrIn
);
437 m_text
->GetStyle(4, attrOut
);
438 CPPUNIT_ASSERT( attrOut
.HasFont() );
440 fontOut
= attrOut
.GetFont();
442 fontOut
.SetEncoding(fontIn
.GetEncoding());
444 CPPUNIT_ASSERT_EQUAL( fontIn
, fontOut
);
447 void TextCtrlTestCase::Lines()
450 m_text
= new wxTextCtrl(wxTheApp
->GetTopWindow(), wxID_ANY
, "",
451 wxDefaultPosition
, wxSize(400, 200), wxTE_MULTILINE
);
453 m_text
->SetValue("line1\nline2\nlong long line 3");
457 CPPUNIT_ASSERT_EQUAL(3, m_text
->GetNumberOfLines());
458 CPPUNIT_ASSERT_EQUAL(5, m_text
->GetLineLength(0));
459 CPPUNIT_ASSERT_EQUAL("line2", m_text
->GetLineText(1));
460 CPPUNIT_ASSERT_EQUAL(16, m_text
->GetLineLength(2));
462 m_text
->AppendText("\n\nMore text on line 5");
464 CPPUNIT_ASSERT_EQUAL(5, m_text
->GetNumberOfLines());
465 CPPUNIT_ASSERT_EQUAL(0, m_text
->GetLineLength(3));
466 CPPUNIT_ASSERT_EQUAL("", m_text
->GetLineText(3));
468 // Verify that wrapped lines count as 2 lines.
470 // This currently doesn't work neither in wxGTK nor wxOSX/Cocoa, see
471 // #12366, where GetNumberOfLines() always returns the number of logical,
472 // not physical, lines.
473 m_text
->AppendText("\n" + wxString(50, '1') + ' ' + wxString(50, '2'));
474 #if defined(__WXGTK__) || defined(__WXOSX_COCOA__)
475 CPPUNIT_ASSERT_EQUAL(6, m_text
->GetNumberOfLines());
477 CPPUNIT_ASSERT_EQUAL(7, m_text
->GetNumberOfLines());
481 void TextCtrlTestCase::LogTextCtrl()
484 m_text
= new wxTextCtrl(wxTheApp
->GetTopWindow(), wxID_ANY
, "",
485 wxDefaultPosition
, wxSize(400, 200),
488 CPPUNIT_ASSERT(m_text
->IsEmpty());
490 wxLogTextCtrl
* logtext
= new wxLogTextCtrl(m_text
);
492 wxLog
* old
= wxLog::SetActiveTarget(logtext
);
494 logtext
->LogText("text");
496 delete wxLog::SetActiveTarget(old
);
498 CPPUNIT_ASSERT(!m_text
->IsEmpty());
501 void TextCtrlTestCase::PositionToCoords()
503 DoPositionToCoordsTestWithStyle(0);
506 void TextCtrlTestCase::PositionToCoordsRich()
508 DoPositionToCoordsTestWithStyle(wxTE_RICH
);
511 void TextCtrlTestCase::PositionToCoordsRich2()
513 DoPositionToCoordsTestWithStyle(wxTE_RICH2
);
516 void TextCtrlTestCase::DoPositionToCoordsTestWithStyle(long style
)
518 static const int TEXT_HEIGHT
= 200;
521 m_text
= new wxTextCtrl(wxTheApp
->GetTopWindow(), wxID_ANY
, "",
522 wxDefaultPosition
, wxSize(400, TEXT_HEIGHT
),
523 wxTE_MULTILINE
| style
);
525 // Asking for invalid index should fail.
526 WX_ASSERT_FAILS_WITH_ASSERT( m_text
->PositionToCoords(1) );
528 // Getting position shouldn't return wxDefaultPosition except if the method
529 // is not implemented at all in the current port.
530 const wxPoint pos0
= m_text
->PositionToCoords(0);
531 if ( pos0
== wxDefaultPosition
)
533 #if defined(__WXMSW__) || defined(__WXGTK20__)
534 CPPUNIT_FAIL( "PositionToCoords() unexpectedly failed." );
539 CPPUNIT_ASSERT(pos0
.x
>= 0);
540 CPPUNIT_ASSERT(pos0
.y
>= 0);
543 m_text
->SetValue("Hello");
544 wxYield(); // Let GTK layout the control correctly.
546 // Position of non-first character should be positive.
547 const long posHello4
= m_text
->PositionToCoords(4).x
;
548 CPPUNIT_ASSERT( posHello4
> 0 );
550 // Asking for position beyond the last character should succeed and return
551 // reasonable result.
552 CPPUNIT_ASSERT( m_text
->PositionToCoords(5).x
> posHello4
);
554 // But asking for the next position should fail.
555 WX_ASSERT_FAILS_WITH_ASSERT( m_text
->PositionToCoords(6) );
557 // Test getting the coordinates of the last character when it is in the
558 // beginning of a new line to exercise MSW code which has specific logic
560 m_text
->AppendText("\n");
561 const wxPoint posLast
= m_text
->PositionToCoords(m_text
->GetLastPosition());
562 CPPUNIT_ASSERT_EQUAL( pos0
.x
, posLast
.x
);
563 CPPUNIT_ASSERT( posLast
.y
> 0 );
566 // Add enough contents to the control to make sure it has a scrollbar.
567 m_text
->SetValue("First line" + wxString(50, '\n') + "Last line");
568 m_text
->SetInsertionPoint(0);
569 wxYield(); // Let GTK layout the control correctly.
571 // This shouldn't change anything for the first position coordinates.
572 CPPUNIT_ASSERT_EQUAL( pos0
, m_text
->PositionToCoords(0) );
574 // And the last one must be beyond the window boundary and so not be
575 // visible -- but getting its coordinate should still work.
578 m_text
->PositionToCoords(m_text
->GetLastPosition()).y
> TEXT_HEIGHT
582 // Now make it scroll to the end and check that the first position now has
583 // negative offset as its above the visible part of the window while the
584 // last position is in its bounds.
585 m_text
->SetInsertionPointEnd();
587 CPPUNIT_ASSERT( m_text
->PositionToCoords(0).y
< 0 );
590 m_text
->PositionToCoords(m_text
->GetInsertionPoint()).y
<= TEXT_HEIGHT
595 #endif //wxUSE_TEXTCTRL