]> git.saurik.com Git - wxWidgets.git/blob - tests/controls/textctrltest.cpp
Fix the size of the font returned from wxTextCtrl::GetStyle() in wxMSW.
[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( 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();
67
68 void MultiLineReplace();
69 void ReadOnly();
70 void MaxLength();
71 void StreamInput();
72 void Redirector();
73 //void ProcessEnter();
74 void Url();
75 void Style();
76 void FontStyle();
77 void Lines();
78 void LogTextCtrl();
79 void PositionToCoords();
80 void PositionToCoordsRich();
81 void PositionToCoordsRich2();
82
83 void DoPositionToCoordsTestWithStyle(long style);
84
85 wxTextCtrl *m_text;
86
87 DECLARE_NO_COPY_CLASS(TextCtrlTestCase)
88 };
89
90 // register in the unnamed registry so that these tests are run by default
91 CPPUNIT_TEST_SUITE_REGISTRATION( TextCtrlTestCase );
92
93 // also include in its own registry so that these tests can be run alone
94 CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( TextCtrlTestCase, "TextCtrlTestCase" );
95
96 // ----------------------------------------------------------------------------
97 // test initialization
98 // ----------------------------------------------------------------------------
99
100 void TextCtrlTestCase::setUp()
101 {
102 m_text = new wxTextCtrl(wxTheApp->GetTopWindow(), wxID_ANY);
103 }
104
105 void TextCtrlTestCase::tearDown()
106 {
107 wxDELETE(m_text);
108 }
109
110 // ----------------------------------------------------------------------------
111 // tests themselves
112 // ----------------------------------------------------------------------------
113
114 void TextCtrlTestCase::MultiLineReplace()
115 {
116 // we need a multiline control for this test so recreate it
117 delete m_text;
118 m_text = new wxTextCtrl(wxTheApp->GetTopWindow(), wxID_ANY, "",
119 wxDefaultPosition, wxDefaultSize,
120 wxTE_MULTILINE);
121
122 m_text->SetValue("Hello replace\n"
123 "0123456789012");
124 m_text->SetInsertionPoint(0);
125
126 m_text->Replace(6, 13, "changed");
127
128 CPPUNIT_ASSERT_EQUAL("Hello changed\n"
129 "0123456789012",
130 m_text->GetValue());
131 CPPUNIT_ASSERT_EQUAL(13, m_text->GetInsertionPoint());
132
133 m_text->Replace(13, -1, "");
134 CPPUNIT_ASSERT_EQUAL("Hello changed", m_text->GetValue());
135 CPPUNIT_ASSERT_EQUAL(13, m_text->GetInsertionPoint());
136
137 delete m_text;
138 m_text = new wxTextCtrl(wxTheApp->GetTopWindow(), wxID_ANY);
139 }
140
141 void TextCtrlTestCase::ReadOnly()
142 {
143 #if wxUSE_UIACTIONSIMULATOR
144 // we need a read only control for this test so recreate it
145 delete m_text;
146 m_text = new wxTextCtrl(wxTheApp->GetTopWindow(), wxID_ANY, "",
147 wxDefaultPosition, wxDefaultSize,
148 wxTE_READONLY);
149
150 wxTestableFrame* frame = wxStaticCast(wxTheApp->GetTopWindow(),
151 wxTestableFrame);
152
153 EventCounter count(m_text, wxEVT_COMMAND_TEXT_UPDATED);
154
155 m_text->SetFocus();
156
157 wxUIActionSimulator sim;
158 sim.Text("abcdef");
159 wxYield();
160
161 CPPUNIT_ASSERT_EQUAL("", m_text->GetValue());
162 CPPUNIT_ASSERT_EQUAL(0, frame->GetEventCount());
163
164 // SetEditable() is supposed to override wxTE_READONLY
165 m_text->SetEditable(true);
166
167 sim.Text("abcdef");
168 wxYield();
169
170 CPPUNIT_ASSERT_EQUAL("abcdef", m_text->GetValue());
171 CPPUNIT_ASSERT_EQUAL(6, frame->GetEventCount());
172
173 delete m_text;
174 m_text = new wxTextCtrl(wxTheApp->GetTopWindow(), wxID_ANY);
175 #endif
176 }
177
178 void TextCtrlTestCase::MaxLength()
179 {
180 #if wxUSE_UIACTIONSIMULATOR
181 wxTestableFrame* frame = wxStaticCast(wxTheApp->GetTopWindow(),
182 wxTestableFrame);
183
184 EventCounter count(m_text, wxEVT_COMMAND_TEXT_UPDATED);
185 EventCounter count1(m_text, wxEVT_COMMAND_TEXT_MAXLEN);
186
187 m_text->SetFocus();
188 m_text->SetMaxLength(10);
189
190 wxUIActionSimulator sim;
191 sim.Text("abcdef");
192 wxYield();
193
194 CPPUNIT_ASSERT_EQUAL(0, frame->GetEventCount(wxEVT_COMMAND_TEXT_MAXLEN));
195
196 sim.Text("ghij");
197 wxYield();
198
199 CPPUNIT_ASSERT_EQUAL(0, frame->GetEventCount(wxEVT_COMMAND_TEXT_MAXLEN));
200 CPPUNIT_ASSERT_EQUAL(10, frame->GetEventCount(wxEVT_COMMAND_TEXT_UPDATED));
201
202 sim.Text("k");
203 wxYield();
204
205 CPPUNIT_ASSERT_EQUAL(1, frame->GetEventCount(wxEVT_COMMAND_TEXT_MAXLEN));
206 CPPUNIT_ASSERT_EQUAL(0, frame->GetEventCount(wxEVT_COMMAND_TEXT_UPDATED));
207
208 m_text->SetMaxLength(0);
209
210 sim.Text("k");
211 wxYield();
212
213 CPPUNIT_ASSERT_EQUAL(0, frame->GetEventCount(wxEVT_COMMAND_TEXT_MAXLEN));
214 CPPUNIT_ASSERT_EQUAL(1, frame->GetEventCount(wxEVT_COMMAND_TEXT_UPDATED));
215 #endif
216 }
217
218 void TextCtrlTestCase::StreamInput()
219 {
220 #ifndef __WXOSX__
221 {
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 );
225
226 *m_text << "stringinput"
227 << 10
228 << 1000L
229 << 3.14f
230 << 2.71
231 << 'a'
232 << L'b';
233 }
234
235 CPPUNIT_ASSERT_EQUAL("stringinput1010003.142.71ab", m_text->GetValue());
236
237 m_text->SetValue("");
238
239 #if wxHAS_TEXT_WINDOW_STREAM
240
241 std::ostream stream(m_text);
242
243 // We don't test a wide character as this is not a wide stream
244 stream << "stringinput"
245 << 10
246 << 1000L
247 << 3.14f
248 << 2.71
249 << 'a';
250
251 stream.flush();
252
253 CPPUNIT_ASSERT_EQUAL("stringinput1010003.142.71a", m_text->GetValue());
254
255 #endif // wxHAS_TEXT_WINDOW_STREAM
256 #endif // !__WXOSX__
257 }
258
259 void TextCtrlTestCase::Redirector()
260 {
261 #if wxHAS_TEXT_WINDOW_STREAM && wxUSE_STD_IOSTREAM
262
263 wxStreamToTextRedirector redirect(m_text);
264
265 std::cout << "stringinput"
266 << 10
267 << 1000L
268 << 3.14f
269 << 2.71
270 << 'a';
271
272 CPPUNIT_ASSERT_EQUAL("stringinput1010003.142.71a", m_text->GetValue());
273
274 #endif
275 }
276
277 #if 0
278 void TextCtrlTestCase::ProcessEnter()
279 {
280 #if wxUSE_UIACTIONSIMULATOR
281 wxTestableFrame* frame = wxStaticCast(wxTheApp->GetTopWindow(),
282 wxTestableFrame);
283
284 EventCounter count(m_text, wxEVT_COMMAND_TEXT_ENTER);
285
286 m_text->SetFocus();
287
288 wxUIActionSimulator sim;
289 sim.Char(WXK_RETURN);
290 wxYield();
291
292 CPPUNIT_ASSERT_EQUAL(0, frame->GetEventCount(wxEVT_COMMAND_TEXT_ENTER));
293
294 // we need a text control with wxTE_PROCESS_ENTER for this test
295 delete m_text;
296 m_text = new wxTextCtrl(wxTheApp->GetTopWindow(), wxID_ANY, "",
297 wxDefaultPosition, wxDefaultSize,
298 wxTE_PROCESS_ENTER);
299
300 m_text->SetFocus();
301
302 sim.Char(WXK_RETURN);
303 wxYield();
304
305 CPPUNIT_ASSERT_EQUAL(1, frame->GetEventCount(wxEVT_COMMAND_TEXT_ENTER));
306 #endif
307 }
308 #endif
309
310 void TextCtrlTestCase::Url()
311 {
312 #if wxUSE_UIACTIONSIMULATOR && defined(__WXMSW__)
313 delete m_text;
314 m_text = new wxTextCtrl(wxTheApp->GetTopWindow(), wxID_ANY, "",
315 wxDefaultPosition, wxDefaultSize,
316 wxTE_MULTILINE | wxTE_RICH | wxTE_AUTO_URL);
317
318 wxTestableFrame* frame = wxStaticCast(wxTheApp->GetTopWindow(),
319 wxTestableFrame);
320
321 EventCounter count(m_text, wxEVT_COMMAND_TEXT_URL);
322
323 m_text->AppendText("http://www.wxwidgets.org");
324
325 wxUIActionSimulator sim;
326 sim.MouseMove(m_text->ClientToScreen(wxPoint(5, 5)));
327 sim.MouseClick();
328 wxYield();
329
330 CPPUNIT_ASSERT_EQUAL(1, frame->GetEventCount());
331 #endif
332 }
333
334 void TextCtrlTestCase::Style()
335 {
336 #ifndef __WXOSX__
337 delete m_text;
338 // We need wxTE_RICH under windows for style support
339 m_text = new wxTextCtrl(wxTheApp->GetTopWindow(), wxID_ANY, "",
340 wxDefaultPosition, wxDefaultSize, wxTE_RICH);
341
342 // Red text on a white background
343 m_text->SetDefaultStyle(wxTextAttr(*wxRED, *wxWHITE));
344
345 CPPUNIT_ASSERT_EQUAL(m_text->GetDefaultStyle().GetTextColour(), *wxRED);
346 CPPUNIT_ASSERT_EQUAL(m_text->GetDefaultStyle().GetBackgroundColour(),
347 *wxWHITE);
348
349 m_text->AppendText("red on white ");
350
351 // Red text on a grey background
352 m_text->SetDefaultStyle(wxTextAttr(wxNullColour, *wxLIGHT_GREY));
353
354 CPPUNIT_ASSERT_EQUAL(m_text->GetDefaultStyle().GetTextColour(), *wxRED);
355 CPPUNIT_ASSERT_EQUAL(m_text->GetDefaultStyle().GetBackgroundColour(),
356 *wxLIGHT_GREY);
357
358 m_text->AppendText("red on grey ");
359
360 // Blue text on a grey background
361 m_text->SetDefaultStyle(wxTextAttr(*wxBLUE));
362
363
364 CPPUNIT_ASSERT_EQUAL(m_text->GetDefaultStyle().GetTextColour(), *wxBLUE);
365 CPPUNIT_ASSERT_EQUAL(m_text->GetDefaultStyle().GetBackgroundColour(),
366 *wxLIGHT_GREY);
367
368 m_text->AppendText("blue on grey");
369
370 // Get getting the style at a specific location
371 wxTextAttr style;
372
373 // We have to check that styles are supported
374 if(m_text->GetStyle(3, style))
375 {
376 CPPUNIT_ASSERT_EQUAL(style.GetTextColour(), *wxRED);
377 CPPUNIT_ASSERT_EQUAL(style.GetBackgroundColour(), *wxWHITE);
378 }
379
380 // And then setting the style
381 if(m_text->SetStyle(15, 18, style))
382 {
383 m_text->GetStyle(17, style);
384
385 CPPUNIT_ASSERT_EQUAL(style.GetTextColour(), *wxRED);
386 CPPUNIT_ASSERT_EQUAL(style.GetBackgroundColour(), *wxWHITE);
387 }
388 #endif
389 }
390
391 void TextCtrlTestCase::FontStyle()
392 {
393 // We need wxTE_RICH under MSW and wxTE_MULTILINE under GTK for style
394 // support so recreate the control with these styles.
395 delete m_text;
396 m_text = new wxTextCtrl(wxTheApp->GetTopWindow(), wxID_ANY, "",
397 wxDefaultPosition, wxDefaultSize,
398 wxTE_MULTILINE | wxTE_RICH);
399
400 // Check that we get back the same font from GetStyle() after setting it
401 // with SetDefaultStyle().
402 wxFont fontIn(14,
403 wxFONTFAMILY_DEFAULT,
404 wxFONTSTYLE_NORMAL,
405 wxFONTWEIGHT_NORMAL);
406 wxTextAttr attrIn;
407 attrIn.SetFont(fontIn);
408 if ( !m_text->SetDefaultStyle(attrIn) )
409 {
410 // Skip the test if the styles are not supported.
411 return;
412 }
413
414 m_text->AppendText("Default font size 14");
415
416 wxTextAttr attrOut;
417 m_text->GetStyle(5, attrOut);
418
419 CPPUNIT_ASSERT( attrOut.HasFont() );
420
421 wxFont fontOut = attrOut.GetFont();
422 #ifdef __WXMSW__
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());
427 #endif
428 CPPUNIT_ASSERT_EQUAL( fontIn, fontOut );
429
430
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);
436
437 m_text->GetStyle(4, attrOut);
438 CPPUNIT_ASSERT( attrOut.HasFont() );
439
440 fontOut = attrOut.GetFont();
441 #ifdef __WXMSW__
442 fontOut.SetEncoding(fontIn.GetEncoding());
443 #endif
444 CPPUNIT_ASSERT_EQUAL( fontIn, fontOut );
445 }
446
447 void TextCtrlTestCase::Lines()
448 {
449 delete m_text;
450 m_text = new wxTextCtrl(wxTheApp->GetTopWindow(), wxID_ANY, "",
451 wxDefaultPosition, wxSize(400, 200), wxTE_MULTILINE);
452
453 m_text->SetValue("line1\nline2\nlong long line 3");
454 m_text->Refresh();
455 m_text->Update();
456
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));
461
462 m_text->AppendText("\n\nMore text on line 5");
463
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));
467
468 // Verify that wrapped lines count as 2 lines.
469 //
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());
476 #else
477 CPPUNIT_ASSERT_EQUAL(7, m_text->GetNumberOfLines());
478 #endif
479 }
480
481 void TextCtrlTestCase::LogTextCtrl()
482 {
483 delete m_text;
484 m_text = new wxTextCtrl(wxTheApp->GetTopWindow(), wxID_ANY, "",
485 wxDefaultPosition, wxSize(400, 200),
486 wxTE_MULTILINE);
487
488 CPPUNIT_ASSERT(m_text->IsEmpty());
489
490 wxLogTextCtrl* logtext = new wxLogTextCtrl(m_text);
491
492 wxLog* old = wxLog::SetActiveTarget(logtext);
493
494 logtext->LogText("text");
495
496 delete wxLog::SetActiveTarget(old);
497
498 CPPUNIT_ASSERT(!m_text->IsEmpty());
499 }
500
501 void TextCtrlTestCase::PositionToCoords()
502 {
503 DoPositionToCoordsTestWithStyle(0);
504 }
505
506 void TextCtrlTestCase::PositionToCoordsRich()
507 {
508 DoPositionToCoordsTestWithStyle(wxTE_RICH);
509 }
510
511 void TextCtrlTestCase::PositionToCoordsRich2()
512 {
513 DoPositionToCoordsTestWithStyle(wxTE_RICH2);
514 }
515
516 void TextCtrlTestCase::DoPositionToCoordsTestWithStyle(long style)
517 {
518 static const int TEXT_HEIGHT = 200;
519
520 delete m_text;
521 m_text = new wxTextCtrl(wxTheApp->GetTopWindow(), wxID_ANY, "",
522 wxDefaultPosition, wxSize(400, TEXT_HEIGHT),
523 wxTE_MULTILINE | style);
524
525 // Asking for invalid index should fail.
526 WX_ASSERT_FAILS_WITH_ASSERT( m_text->PositionToCoords(1) );
527
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 )
532 {
533 #if defined(__WXMSW__) || defined(__WXGTK20__)
534 CPPUNIT_FAIL( "PositionToCoords() unexpectedly failed." );
535 #endif
536 return;
537 }
538
539 CPPUNIT_ASSERT(pos0.x >= 0);
540 CPPUNIT_ASSERT(pos0.y >= 0);
541
542
543 m_text->SetValue("Hello");
544 wxYield(); // Let GTK layout the control correctly.
545
546 // Position of non-first character should be positive.
547 const long posHello4 = m_text->PositionToCoords(4).x;
548 CPPUNIT_ASSERT( posHello4 > 0 );
549
550 // Asking for position beyond the last character should succeed and return
551 // reasonable result.
552 CPPUNIT_ASSERT( m_text->PositionToCoords(5).x > posHello4 );
553
554 // But asking for the next position should fail.
555 WX_ASSERT_FAILS_WITH_ASSERT( m_text->PositionToCoords(6) );
556
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
559 // for it.
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 );
564
565
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.
570
571 // This shouldn't change anything for the first position coordinates.
572 CPPUNIT_ASSERT_EQUAL( pos0, m_text->PositionToCoords(0) );
573
574 // And the last one must be beyond the window boundary and so not be
575 // visible -- but getting its coordinate should still work.
576 CPPUNIT_ASSERT
577 (
578 m_text->PositionToCoords(m_text->GetLastPosition()).y > TEXT_HEIGHT
579 );
580
581
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();
586
587 CPPUNIT_ASSERT( m_text->PositionToCoords(0).y < 0 );
588 CPPUNIT_ASSERT
589 (
590 m_text->PositionToCoords(m_text->GetInsertionPoint()).y <= TEXT_HEIGHT
591 );
592 }
593
594
595 #endif //wxUSE_TEXTCTRL