Add wxTextCtrl::PositionToCoords() functions for wxMSW and wxGTK.
[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 #ifndef __WXOSX__
392 delete m_text;
393 m_text = new wxTextCtrl(wxTheApp->GetTopWindow(), wxID_ANY, "",
394 wxDefaultPosition, wxSize(400, 200), wxTE_MULTILINE | wxTE_DONTWRAP);
395
396 m_text->SetValue("line1\nline2\nlong long line 3");
397 m_text->Refresh();
398 m_text->Update();
399
400 CPPUNIT_ASSERT_EQUAL(3, m_text->GetNumberOfLines());
401 CPPUNIT_ASSERT_EQUAL(5, m_text->GetLineLength(0));
402 CPPUNIT_ASSERT_EQUAL("line2", m_text->GetLineText(1));
403 CPPUNIT_ASSERT_EQUAL(16, m_text->GetLineLength(2));
404
405 m_text->AppendText("\n\nMore text on line 5");
406
407 CPPUNIT_ASSERT_EQUAL(5, m_text->GetNumberOfLines());
408 CPPUNIT_ASSERT_EQUAL(0, m_text->GetLineLength(3));
409 CPPUNIT_ASSERT_EQUAL("", m_text->GetLineText(3));
410 #endif
411 }
412
413 void TextCtrlTestCase::LogTextCtrl()
414 {
415 delete m_text;
416 m_text = new wxTextCtrl(wxTheApp->GetTopWindow(), wxID_ANY, "",
417 wxDefaultPosition, wxSize(400, 200),
418 wxTE_MULTILINE);
419
420 CPPUNIT_ASSERT(m_text->IsEmpty());
421
422 wxLogTextCtrl* logtext = new wxLogTextCtrl(m_text);
423
424 wxLog* old = wxLog::SetActiveTarget(logtext);
425
426 logtext->LogText("text");
427
428 delete wxLog::SetActiveTarget(old);
429
430 CPPUNIT_ASSERT(!m_text->IsEmpty());
431 }
432
433 void TextCtrlTestCase::PositionToCoords()
434 {
435 DoPositionToCoordsTestWithStyle(0);
436 }
437
438 void TextCtrlTestCase::PositionToCoordsRich()
439 {
440 DoPositionToCoordsTestWithStyle(wxTE_RICH);
441 }
442
443 void TextCtrlTestCase::PositionToCoordsRich2()
444 {
445 DoPositionToCoordsTestWithStyle(wxTE_RICH2);
446 }
447
448 void TextCtrlTestCase::DoPositionToCoordsTestWithStyle(long style)
449 {
450 static const int TEXT_HEIGHT = 200;
451
452 delete m_text;
453 m_text = new wxTextCtrl(wxTheApp->GetTopWindow(), wxID_ANY, "",
454 wxDefaultPosition, wxSize(400, TEXT_HEIGHT),
455 wxTE_MULTILINE | style);
456
457 // Asking for invalid index should fail.
458 WX_ASSERT_FAILS_WITH_ASSERT( m_text->PositionToCoords(1) );
459
460 // Getting position shouldn't return wxDefaultPosition except if the method
461 // is not implemented at all in the current port.
462 const wxPoint pos0 = m_text->PositionToCoords(0);
463 if ( pos0 == wxDefaultPosition )
464 {
465 #if defined(__WXMSW__) || defined(__WXGTK20__)
466 CPPUNIT_FAIL( "PositionToCoords() unexpectedly failed." );
467 #endif
468 return;
469 }
470
471 CPPUNIT_ASSERT(pos0.x >= 0);
472 CPPUNIT_ASSERT(pos0.y >= 0);
473
474
475 m_text->SetValue("Hello");
476 wxYield(); // Let GTK layout the control correctly.
477
478 // Position of non-first character should be positive.
479 const long posHello4 = m_text->PositionToCoords(4).x;
480 CPPUNIT_ASSERT( posHello4 > 0 );
481
482 // Asking for position beyond the last character should succeed and return
483 // reasonable result.
484 CPPUNIT_ASSERT( m_text->PositionToCoords(5).x > posHello4 );
485
486 // But asking for the next position should fail.
487 WX_ASSERT_FAILS_WITH_ASSERT( m_text->PositionToCoords(6) );
488
489 // Test getting the coordinates of the last character when it is in the
490 // beginning of a new line to exercise MSW code which has specific logic
491 // for it.
492 m_text->AppendText("\n");
493 const wxPoint posLast = m_text->PositionToCoords(m_text->GetLastPosition());
494 CPPUNIT_ASSERT_EQUAL( pos0.x, posLast.x );
495 CPPUNIT_ASSERT( posLast.y > 0 );
496
497
498 // Add enough contents to the control to make sure it has a scrollbar.
499 m_text->SetValue("First line" + wxString(50, '\n') + "Last line");
500 m_text->SetInsertionPoint(0);
501 wxYield(); // Let GTK layout the control correctly.
502
503 // This shouldn't change anything for the first position coordinates.
504 CPPUNIT_ASSERT_EQUAL( pos0, m_text->PositionToCoords(0) );
505
506 // And the last one must be beyond the window boundary and so not be
507 // visible -- but getting its coordinate should still work.
508 CPPUNIT_ASSERT
509 (
510 m_text->PositionToCoords(m_text->GetLastPosition()).y > TEXT_HEIGHT
511 );
512
513
514 // Now make it scroll to the end and check that the first position now has
515 // negative offset as its above the visible part of the window while the
516 // last position is in its bounds.
517 m_text->SetInsertionPointEnd();
518
519 CPPUNIT_ASSERT( m_text->PositionToCoords(0).y < 0 );
520 CPPUNIT_ASSERT
521 (
522 m_text->PositionToCoords(m_text->GetInsertionPoint()).y <= TEXT_HEIGHT
523 );
524 }
525
526
527 #endif //wxUSE_TEXTCTRL