]> git.saurik.com Git - wxWidgets.git/blob - tests/controls/textctrltest.cpp
Applied patch in #13777 (wxRichTextCtrl scroll and delete not refreshed)
[wxWidgets.git] / tests / controls / textctrltest.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: tests/controls/textctrltest.cpp
3 // Purpose: wxTextCtrl unit test
4 // Author: Vadim Zeitlin
5 // Created: 2007-09-25
6 // RCS-ID: $Id$
7 // Copyright: (c) 2007 Vadim Zeitlin <vadim@wxwidgets.org>
8 ///////////////////////////////////////////////////////////////////////////////
9
10 // ----------------------------------------------------------------------------
11 // headers
12 // ----------------------------------------------------------------------------
13
14 #include "testprec.h"
15
16 #if wxUSE_TEXTCTRL
17
18 #ifdef __BORLANDC__
19 #pragma hdrstop
20 #endif
21
22 #ifndef WX_PRECOMP
23 #include "wx/app.h"
24 #include "wx/textctrl.h"
25 #endif // WX_PRECOMP
26
27 #include "wx/scopeguard.h"
28
29 #include "textentrytest.h"
30 #include "testableframe.h"
31 #include "asserthelper.h"
32 #include "wx/uiaction.h"
33
34 // ----------------------------------------------------------------------------
35 // test class
36 // ----------------------------------------------------------------------------
37
38 class TextCtrlTestCase : public TextEntryTestCase, public CppUnit::TestCase
39 {
40 public:
41 TextCtrlTestCase() { }
42
43 virtual void setUp();
44 virtual void tearDown();
45
46 private:
47 virtual wxTextEntry *GetTestEntry() const { return m_text; }
48 virtual wxWindow *GetTestWindow() const { return m_text; }
49
50 CPPUNIT_TEST_SUITE( TextCtrlTestCase );
51 wxTEXT_ENTRY_TESTS();
52 CPPUNIT_TEST( MultiLineReplace );
53 WXUISIM_TEST( ReadOnly );
54 WXUISIM_TEST( MaxLength );
55 CPPUNIT_TEST( StreamInput );
56 CPPUNIT_TEST( Redirector );
57 //WXUISIM_TEST( ProcessEnter );
58 WXUISIM_TEST( Url );
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();
66
67 void MultiLineReplace();
68 void ReadOnly();
69 void MaxLength();
70 void StreamInput();
71 void Redirector();
72 //void ProcessEnter();
73 void Url();
74 void Style();
75 void Lines();
76 void LogTextCtrl();
77 void PositionToCoords();
78 void PositionToCoordsRich();
79 void PositionToCoordsRich2();
80
81 void DoPositionToCoordsTestWithStyle(long style);
82
83 wxTextCtrl *m_text;
84
85 DECLARE_NO_COPY_CLASS(TextCtrlTestCase)
86 };
87
88 // register in the unnamed registry so that these tests are run by default
89 CPPUNIT_TEST_SUITE_REGISTRATION( TextCtrlTestCase );
90
91 // also include in its own registry so that these tests can be run alone
92 CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( TextCtrlTestCase, "TextCtrlTestCase" );
93
94 // ----------------------------------------------------------------------------
95 // test initialization
96 // ----------------------------------------------------------------------------
97
98 void TextCtrlTestCase::setUp()
99 {
100 m_text = new wxTextCtrl(wxTheApp->GetTopWindow(), wxID_ANY);
101 }
102
103 void TextCtrlTestCase::tearDown()
104 {
105 wxDELETE(m_text);
106 }
107
108 // ----------------------------------------------------------------------------
109 // tests themselves
110 // ----------------------------------------------------------------------------
111
112 void TextCtrlTestCase::MultiLineReplace()
113 {
114 // we need a multiline control for this test so recreate it
115 delete m_text;
116 m_text = new wxTextCtrl(wxTheApp->GetTopWindow(), wxID_ANY, "",
117 wxDefaultPosition, wxDefaultSize,
118 wxTE_MULTILINE);
119
120 m_text->SetValue("Hello replace\n"
121 "0123456789012");
122 m_text->SetInsertionPoint(0);
123
124 m_text->Replace(6, 13, "changed");
125
126 CPPUNIT_ASSERT_EQUAL("Hello changed\n"
127 "0123456789012",
128 m_text->GetValue());
129 CPPUNIT_ASSERT_EQUAL(13, m_text->GetInsertionPoint());
130
131 m_text->Replace(13, -1, "");
132 CPPUNIT_ASSERT_EQUAL("Hello changed", m_text->GetValue());
133 CPPUNIT_ASSERT_EQUAL(13, m_text->GetInsertionPoint());
134
135 delete m_text;
136 m_text = new wxTextCtrl(wxTheApp->GetTopWindow(), wxID_ANY);
137 }
138
139 void TextCtrlTestCase::ReadOnly()
140 {
141 #if wxUSE_UIACTIONSIMULATOR
142 // we need a read only control for this test so recreate it
143 delete m_text;
144 m_text = new wxTextCtrl(wxTheApp->GetTopWindow(), wxID_ANY, "",
145 wxDefaultPosition, wxDefaultSize,
146 wxTE_READONLY);
147
148 wxTestableFrame* frame = wxStaticCast(wxTheApp->GetTopWindow(),
149 wxTestableFrame);
150
151 EventCounter count(m_text, wxEVT_COMMAND_TEXT_UPDATED);
152
153 m_text->SetFocus();
154
155 wxUIActionSimulator sim;
156 sim.Text("abcdef");
157 wxYield();
158
159 CPPUNIT_ASSERT_EQUAL("", m_text->GetValue());
160 CPPUNIT_ASSERT_EQUAL(0, frame->GetEventCount());
161
162 // SetEditable() is supposed to override wxTE_READONLY
163 m_text->SetEditable(true);
164
165 sim.Text("abcdef");
166 wxYield();
167
168 CPPUNIT_ASSERT_EQUAL("abcdef", m_text->GetValue());
169 CPPUNIT_ASSERT_EQUAL(6, frame->GetEventCount());
170
171 delete m_text;
172 m_text = new wxTextCtrl(wxTheApp->GetTopWindow(), wxID_ANY);
173 #endif
174 }
175
176 void TextCtrlTestCase::MaxLength()
177 {
178 #if wxUSE_UIACTIONSIMULATOR
179 wxTestableFrame* frame = wxStaticCast(wxTheApp->GetTopWindow(),
180 wxTestableFrame);
181
182 EventCounter count(m_text, wxEVT_COMMAND_TEXT_UPDATED);
183 EventCounter count1(m_text, wxEVT_COMMAND_TEXT_MAXLEN);
184
185 m_text->SetFocus();
186 m_text->SetMaxLength(10);
187
188 wxUIActionSimulator sim;
189 sim.Text("abcdef");
190 wxYield();
191
192 CPPUNIT_ASSERT_EQUAL(0, frame->GetEventCount(wxEVT_COMMAND_TEXT_MAXLEN));
193
194 sim.Text("ghij");
195 wxYield();
196
197 CPPUNIT_ASSERT_EQUAL(0, frame->GetEventCount(wxEVT_COMMAND_TEXT_MAXLEN));
198 CPPUNIT_ASSERT_EQUAL(10, frame->GetEventCount(wxEVT_COMMAND_TEXT_UPDATED));
199
200 sim.Text("k");
201 wxYield();
202
203 CPPUNIT_ASSERT_EQUAL(1, frame->GetEventCount(wxEVT_COMMAND_TEXT_MAXLEN));
204 CPPUNIT_ASSERT_EQUAL(0, frame->GetEventCount(wxEVT_COMMAND_TEXT_UPDATED));
205
206 m_text->SetMaxLength(0);
207
208 sim.Text("k");
209 wxYield();
210
211 CPPUNIT_ASSERT_EQUAL(0, frame->GetEventCount(wxEVT_COMMAND_TEXT_MAXLEN));
212 CPPUNIT_ASSERT_EQUAL(1, frame->GetEventCount(wxEVT_COMMAND_TEXT_UPDATED));
213 #endif
214 }
215
216 void TextCtrlTestCase::StreamInput()
217 {
218 #ifndef __WXOSX__
219 {
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 );
223
224 *m_text << "stringinput"
225 << 10
226 << 1000L
227 << 3.14f
228 << 2.71
229 << 'a'
230 << L'b';
231 }
232
233 CPPUNIT_ASSERT_EQUAL("stringinput1010003.142.71ab", m_text->GetValue());
234
235 m_text->SetValue("");
236
237 #if wxHAS_TEXT_WINDOW_STREAM
238
239 std::ostream stream(m_text);
240
241 // We don't test a wide character as this is not a wide stream
242 stream << "stringinput"
243 << 10
244 << 1000L
245 << 3.14f
246 << 2.71
247 << 'a';
248
249 stream.flush();
250
251 CPPUNIT_ASSERT_EQUAL("stringinput1010003.142.71a", m_text->GetValue());
252
253 #endif // wxHAS_TEXT_WINDOW_STREAM
254 #endif // !__WXOSX__
255 }
256
257 void TextCtrlTestCase::Redirector()
258 {
259 #if wxHAS_TEXT_WINDOW_STREAM && wxUSE_STD_IOSTREAM
260
261 wxStreamToTextRedirector redirect(m_text);
262
263 std::cout << "stringinput"
264 << 10
265 << 1000L
266 << 3.14f
267 << 2.71
268 << 'a';
269
270 CPPUNIT_ASSERT_EQUAL("stringinput1010003.142.71a", m_text->GetValue());
271
272 #endif
273 }
274
275 #if 0
276 void TextCtrlTestCase::ProcessEnter()
277 {
278 #if wxUSE_UIACTIONSIMULATOR
279 wxTestableFrame* frame = wxStaticCast(wxTheApp->GetTopWindow(),
280 wxTestableFrame);
281
282 EventCounter count(m_text, wxEVT_COMMAND_TEXT_ENTER);
283
284 m_text->SetFocus();
285
286 wxUIActionSimulator sim;
287 sim.Char(WXK_RETURN);
288 wxYield();
289
290 CPPUNIT_ASSERT_EQUAL(0, frame->GetEventCount(wxEVT_COMMAND_TEXT_ENTER));
291
292 // we need a text control with wxTE_PROCESS_ENTER for this test
293 delete m_text;
294 m_text = new wxTextCtrl(wxTheApp->GetTopWindow(), wxID_ANY, "",
295 wxDefaultPosition, wxDefaultSize,
296 wxTE_PROCESS_ENTER);
297
298 m_text->SetFocus();
299
300 sim.Char(WXK_RETURN);
301 wxYield();
302
303 CPPUNIT_ASSERT_EQUAL(1, frame->GetEventCount(wxEVT_COMMAND_TEXT_ENTER));
304 #endif
305 }
306 #endif
307
308 void TextCtrlTestCase::Url()
309 {
310 #if wxUSE_UIACTIONSIMULATOR && defined(__WXMSW__)
311 delete m_text;
312 m_text = new wxTextCtrl(wxTheApp->GetTopWindow(), wxID_ANY, "",
313 wxDefaultPosition, wxDefaultSize,
314 wxTE_MULTILINE | wxTE_RICH | wxTE_AUTO_URL);
315
316 wxTestableFrame* frame = wxStaticCast(wxTheApp->GetTopWindow(),
317 wxTestableFrame);
318
319 EventCounter count(m_text, wxEVT_COMMAND_TEXT_URL);
320
321 m_text->AppendText("http://www.wxwidgets.org");
322
323 wxUIActionSimulator sim;
324 sim.MouseMove(m_text->ClientToScreen(wxPoint(5, 5)));
325 sim.MouseClick();
326 wxYield();
327
328 CPPUNIT_ASSERT_EQUAL(1, frame->GetEventCount());
329 #endif
330 }
331
332 void TextCtrlTestCase::Style()
333 {
334 #ifndef __WXOSX__
335 delete m_text;
336 // We need wxTE_RICH under windows for style support
337 m_text = new wxTextCtrl(wxTheApp->GetTopWindow(), wxID_ANY, "",
338 wxDefaultPosition, wxDefaultSize, wxTE_RICH);
339
340 // Red text on a white background
341 m_text->SetDefaultStyle(wxTextAttr(*wxRED, *wxWHITE));
342
343 CPPUNIT_ASSERT_EQUAL(m_text->GetDefaultStyle().GetTextColour(), *wxRED);
344 CPPUNIT_ASSERT_EQUAL(m_text->GetDefaultStyle().GetBackgroundColour(),
345 *wxWHITE);
346
347 m_text->AppendText("red on white ");
348
349 // Red text on a grey background
350 m_text->SetDefaultStyle(wxTextAttr(wxNullColour, *wxLIGHT_GREY));
351
352 CPPUNIT_ASSERT_EQUAL(m_text->GetDefaultStyle().GetTextColour(), *wxRED);
353 CPPUNIT_ASSERT_EQUAL(m_text->GetDefaultStyle().GetBackgroundColour(),
354 *wxLIGHT_GREY);
355
356 m_text->AppendText("red on grey ");
357
358 // Blue text on a grey background
359 m_text->SetDefaultStyle(wxTextAttr(*wxBLUE));
360
361
362 CPPUNIT_ASSERT_EQUAL(m_text->GetDefaultStyle().GetTextColour(), *wxBLUE);
363 CPPUNIT_ASSERT_EQUAL(m_text->GetDefaultStyle().GetBackgroundColour(),
364 *wxLIGHT_GREY);
365
366 m_text->AppendText("blue on grey");
367
368 // Get getting the style at a specific location
369 wxTextAttr style;
370
371 // We have to check that styles are supported
372 if(m_text->GetStyle(3, style))
373 {
374 CPPUNIT_ASSERT_EQUAL(style.GetTextColour(), *wxRED);
375 CPPUNIT_ASSERT_EQUAL(style.GetBackgroundColour(), *wxWHITE);
376 }
377
378 // And then setting the style
379 if(m_text->SetStyle(15, 18, style))
380 {
381 m_text->GetStyle(17, style);
382
383 CPPUNIT_ASSERT_EQUAL(style.GetTextColour(), *wxRED);
384 CPPUNIT_ASSERT_EQUAL(style.GetBackgroundColour(), *wxWHITE);
385 }
386 #endif
387 }
388
389 void TextCtrlTestCase::Lines()
390 {
391 delete m_text;
392 m_text = new wxTextCtrl(wxTheApp->GetTopWindow(), wxID_ANY, "",
393 wxDefaultPosition, wxSize(400, 200), wxTE_MULTILINE);
394
395 m_text->SetValue("line1\nline2\nlong long line 3");
396 m_text->Refresh();
397 m_text->Update();
398
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));
403
404 m_text->AppendText("\n\nMore text on line 5");
405
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));
409
410 // Verify that wrapped lines count as 2 lines.
411 //
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());
418 #else
419 CPPUNIT_ASSERT_EQUAL(7, m_text->GetNumberOfLines());
420 #endif
421 }
422
423 void TextCtrlTestCase::LogTextCtrl()
424 {
425 delete m_text;
426 m_text = new wxTextCtrl(wxTheApp->GetTopWindow(), wxID_ANY, "",
427 wxDefaultPosition, wxSize(400, 200),
428 wxTE_MULTILINE);
429
430 CPPUNIT_ASSERT(m_text->IsEmpty());
431
432 wxLogTextCtrl* logtext = new wxLogTextCtrl(m_text);
433
434 wxLog* old = wxLog::SetActiveTarget(logtext);
435
436 logtext->LogText("text");
437
438 delete wxLog::SetActiveTarget(old);
439
440 CPPUNIT_ASSERT(!m_text->IsEmpty());
441 }
442
443 void TextCtrlTestCase::PositionToCoords()
444 {
445 DoPositionToCoordsTestWithStyle(0);
446 }
447
448 void TextCtrlTestCase::PositionToCoordsRich()
449 {
450 DoPositionToCoordsTestWithStyle(wxTE_RICH);
451 }
452
453 void TextCtrlTestCase::PositionToCoordsRich2()
454 {
455 DoPositionToCoordsTestWithStyle(wxTE_RICH2);
456 }
457
458 void TextCtrlTestCase::DoPositionToCoordsTestWithStyle(long style)
459 {
460 static const int TEXT_HEIGHT = 200;
461
462 delete m_text;
463 m_text = new wxTextCtrl(wxTheApp->GetTopWindow(), wxID_ANY, "",
464 wxDefaultPosition, wxSize(400, TEXT_HEIGHT),
465 wxTE_MULTILINE | style);
466
467 // Asking for invalid index should fail.
468 WX_ASSERT_FAILS_WITH_ASSERT( m_text->PositionToCoords(1) );
469
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 )
474 {
475 #if defined(__WXMSW__) || defined(__WXGTK20__)
476 CPPUNIT_FAIL( "PositionToCoords() unexpectedly failed." );
477 #endif
478 return;
479 }
480
481 CPPUNIT_ASSERT(pos0.x >= 0);
482 CPPUNIT_ASSERT(pos0.y >= 0);
483
484
485 m_text->SetValue("Hello");
486 wxYield(); // Let GTK layout the control correctly.
487
488 // Position of non-first character should be positive.
489 const long posHello4 = m_text->PositionToCoords(4).x;
490 CPPUNIT_ASSERT( posHello4 > 0 );
491
492 // Asking for position beyond the last character should succeed and return
493 // reasonable result.
494 CPPUNIT_ASSERT( m_text->PositionToCoords(5).x > posHello4 );
495
496 // But asking for the next position should fail.
497 WX_ASSERT_FAILS_WITH_ASSERT( m_text->PositionToCoords(6) );
498
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
501 // for it.
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 );
506
507
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.
512
513 // This shouldn't change anything for the first position coordinates.
514 CPPUNIT_ASSERT_EQUAL( pos0, m_text->PositionToCoords(0) );
515
516 // And the last one must be beyond the window boundary and so not be
517 // visible -- but getting its coordinate should still work.
518 CPPUNIT_ASSERT
519 (
520 m_text->PositionToCoords(m_text->GetLastPosition()).y > TEXT_HEIGHT
521 );
522
523
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();
528
529 CPPUNIT_ASSERT( m_text->PositionToCoords(0).y < 0 );
530 CPPUNIT_ASSERT
531 (
532 m_text->PositionToCoords(m_text->GetInsertionPoint()).y <= TEXT_HEIGHT
533 );
534 }
535
536
537 #endif //wxUSE_TEXTCTRL