]> git.saurik.com Git - wxWidgets.git/blobdiff - tests/controls/textctrltest.cpp
Ensure that detached menus don't keep focus grab in wxGTK.
[wxWidgets.git] / tests / controls / textctrltest.cpp
index 8a78cd5f4cc95d2f610e98d8754acfc546fd8fac..a541f74b29f634513acbe083a3fd2b55553f717f 100644 (file)
@@ -31,6 +31,8 @@
 #include "asserthelper.h"
 #include "wx/uiaction.h"
 
+static const int TEXT_HEIGHT = 200;
+
 // ----------------------------------------------------------------------------
 // test class
 // ----------------------------------------------------------------------------
@@ -47,20 +49,53 @@ private:
     virtual wxTextEntry *GetTestEntry() const { return m_text; }
     virtual wxWindow *GetTestWindow() const { return m_text; }
 
+    #define SINGLE_AND_MULTI_TESTS() \
+        WXUISIM_TEST( ReadOnly ); \
+        CPPUNIT_TEST( StreamInput ); \
+        CPPUNIT_TEST( Redirector )
+
     CPPUNIT_TEST_SUITE( TextCtrlTestCase );
+        // These tests run for single line text controls.
         wxTEXT_ENTRY_TESTS();
-        CPPUNIT_TEST( MultiLineReplace );
-        WXUISIM_TEST( ReadOnly );
         WXUISIM_TEST( MaxLength );
-        CPPUNIT_TEST( StreamInput );
-        CPPUNIT_TEST( Redirector );
+        SINGLE_AND_MULTI_TESTS();
+
+        // Now switch to the multi-line text controls.
+        CPPUNIT_TEST( PseudoTestSwitchToMultiLineStyle );
+
+        // Rerun some of the tests above. Notice that not all of them pass, so
+        // we can't just use wxTEXT_ENTRY_TESTS() here. For some of them it's
+        // normal, e.g. Hint() test isn't supposed to work for multi-line
+        // controls. Others, such as InsertionPoint() and TextChangeEvents()
+        // don't pass neither but this could be a bug.
+        CPPUNIT_TEST( SetValue );
+        CPPUNIT_TEST( Selection );
+        CPPUNIT_TEST( Replace );
+        WXUISIM_TEST( Editable );
+        CPPUNIT_TEST( CopyPaste );
+        CPPUNIT_TEST( UndoRedo );
+
+        SINGLE_AND_MULTI_TESTS();
+
+
+        // All tests from now on are for multi-line controls only.
+        CPPUNIT_TEST( MultiLineReplace );
         //WXUISIM_TEST( ProcessEnter );
         WXUISIM_TEST( Url );
         CPPUNIT_TEST( Style );
+        CPPUNIT_TEST( FontStyle );
         CPPUNIT_TEST( Lines );
         CPPUNIT_TEST( LogTextCtrl );
+        CPPUNIT_TEST( PositionToCoords );
+        CPPUNIT_TEST( PositionToCoordsRich );
+        CPPUNIT_TEST( PositionToCoordsRich2 );
     CPPUNIT_TEST_SUITE_END();
 
+    void PseudoTestSwitchToMultiLineStyle()
+    {
+        ms_style = wxTE_MULTILINE;
+    }
+
     void MultiLineReplace();
     void ReadOnly();
     void MaxLength();
@@ -69,27 +104,54 @@ private:
     //void ProcessEnter();
     void Url();
     void Style();
+    void FontStyle();
     void Lines();
     void LogTextCtrl();
+    void PositionToCoords();
+    void PositionToCoordsRich();
+    void PositionToCoordsRich2();
+
+    void DoPositionToCoordsTestWithStyle(long style);
+
+    // Create the control with the following styles added to ms_style which may
+    // (or not) already contain wxTE_MULTILINE.
+    void CreateText(long extraStyles);
 
     wxTextCtrl *m_text;
 
+    static long ms_style;
+
     DECLARE_NO_COPY_CLASS(TextCtrlTestCase)
 };
 
 // register in the unnamed registry so that these tests are run by default
 CPPUNIT_TEST_SUITE_REGISTRATION( TextCtrlTestCase );
 
-// also include in it's own registry so that these tests can be run alone
+// also include in its own registry so that these tests can be run alone
 CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( TextCtrlTestCase, "TextCtrlTestCase" );
 
 // ----------------------------------------------------------------------------
 // test initialization
 // ----------------------------------------------------------------------------
 
+// This is 0 initially and set to wxTE_MULTILINE later to allow running the
+// same tests for both single and multi line controls.
+long TextCtrlTestCase::ms_style = 0;
+
+void TextCtrlTestCase::CreateText(long extraStyles)
+{
+    wxSize size;
+    if ( ms_style == wxTE_MULTILINE )
+        size = wxSize(400, TEXT_HEIGHT);
+
+    m_text = new wxTextCtrl(wxTheApp->GetTopWindow(), wxID_ANY, "",
+                            wxDefaultPosition, size,
+                            ms_style | extraStyles);
+}
+
 void TextCtrlTestCase::setUp()
 {
-    m_text = new wxTextCtrl(wxTheApp->GetTopWindow(), wxID_ANY);
+    CreateText(ms_style);
 }
 
 void TextCtrlTestCase::tearDown()
@@ -103,12 +165,6 @@ void TextCtrlTestCase::tearDown()
 
 void TextCtrlTestCase::MultiLineReplace()
 {
-    // we need a multiline control for this test so recreate it
-    delete m_text;
-    m_text = new wxTextCtrl(wxTheApp->GetTopWindow(), wxID_ANY, "",
-                            wxDefaultPosition, wxDefaultSize,
-                            wxTE_MULTILINE);
-
     m_text->SetValue("Hello replace\n"
                     "0123456789012");
     m_text->SetInsertionPoint(0);
@@ -123,9 +179,6 @@ void TextCtrlTestCase::MultiLineReplace()
     m_text->Replace(13, -1, "");
     CPPUNIT_ASSERT_EQUAL("Hello changed", m_text->GetValue());
     CPPUNIT_ASSERT_EQUAL(13, m_text->GetInsertionPoint());
-
-    delete m_text;
-    m_text = new wxTextCtrl(wxTheApp->GetTopWindow(), wxID_ANY);
 }
 
 void TextCtrlTestCase::ReadOnly()
@@ -133,14 +186,9 @@ void TextCtrlTestCase::ReadOnly()
 #if wxUSE_UIACTIONSIMULATOR
     // we need a read only control for this test so recreate it
     delete m_text;
-    m_text = new wxTextCtrl(wxTheApp->GetTopWindow(), wxID_ANY, "",
-                            wxDefaultPosition, wxDefaultSize,
-                            wxTE_READONLY);
+    CreateText(wxTE_READONLY);
 
-    wxTestableFrame* frame = wxStaticCast(wxTheApp->GetTopWindow(),
-                                          wxTestableFrame);
-
-    EventCounter count(m_text, wxEVT_COMMAND_TEXT_UPDATED);
+    EventCounter updated(m_text, wxEVT_TEXT);
 
     m_text->SetFocus();
 
@@ -149,7 +197,7 @@ void TextCtrlTestCase::ReadOnly()
     wxYield();
 
     CPPUNIT_ASSERT_EQUAL("", m_text->GetValue());
-    CPPUNIT_ASSERT_EQUAL(0, frame->GetEventCount());
+    CPPUNIT_ASSERT_EQUAL(0, updated.GetCount());
 
     // SetEditable() is supposed to override wxTE_READONLY
     m_text->SetEditable(true);
@@ -158,21 +206,15 @@ void TextCtrlTestCase::ReadOnly()
     wxYield();
 
     CPPUNIT_ASSERT_EQUAL("abcdef", m_text->GetValue());
-    CPPUNIT_ASSERT_EQUAL(6, frame->GetEventCount());
-
-    delete m_text;
-    m_text = new wxTextCtrl(wxTheApp->GetTopWindow(), wxID_ANY);
+    CPPUNIT_ASSERT_EQUAL(6, updated.GetCount());
 #endif
 }
 
 void TextCtrlTestCase::MaxLength()
 {
 #if wxUSE_UIACTIONSIMULATOR
-    wxTestableFrame* frame = wxStaticCast(wxTheApp->GetTopWindow(),
-                                          wxTestableFrame);
-
-    EventCounter count(m_text, wxEVT_COMMAND_TEXT_UPDATED);
-    EventCounter count1(m_text, wxEVT_COMMAND_TEXT_MAXLEN);
+    EventCounter updated(m_text, wxEVT_TEXT);
+    EventCounter maxlen(m_text, wxEVT_TEXT_MAXLEN);
 
     m_text->SetFocus();
     m_text->SetMaxLength(10);
@@ -181,27 +223,33 @@ void TextCtrlTestCase::MaxLength()
     sim.Text("abcdef");
     wxYield();
 
-    CPPUNIT_ASSERT_EQUAL(0, frame->GetEventCount(wxEVT_COMMAND_TEXT_MAXLEN));
+    CPPUNIT_ASSERT_EQUAL(0, maxlen.GetCount());
 
     sim.Text("ghij");
     wxYield();
 
-    CPPUNIT_ASSERT_EQUAL(0, frame->GetEventCount(wxEVT_COMMAND_TEXT_MAXLEN));
-    CPPUNIT_ASSERT_EQUAL(10, frame->GetEventCount(wxEVT_COMMAND_TEXT_UPDATED));
+    CPPUNIT_ASSERT_EQUAL(0, maxlen.GetCount());
+    CPPUNIT_ASSERT_EQUAL(10, updated.GetCount());
+
+    maxlen.Clear();
+    updated.Clear();
 
     sim.Text("k");
     wxYield();
 
-    CPPUNIT_ASSERT_EQUAL(1, frame->GetEventCount(wxEVT_COMMAND_TEXT_MAXLEN));
-    CPPUNIT_ASSERT_EQUAL(0, frame->GetEventCount(wxEVT_COMMAND_TEXT_UPDATED));
+    CPPUNIT_ASSERT_EQUAL(1, maxlen.GetCount());
+    CPPUNIT_ASSERT_EQUAL(0, updated.GetCount());
+
+    maxlen.Clear();
+    updated.Clear();
 
     m_text->SetMaxLength(0);
 
     sim.Text("k");
     wxYield();
 
-    CPPUNIT_ASSERT_EQUAL(0, frame->GetEventCount(wxEVT_COMMAND_TEXT_MAXLEN));
-    CPPUNIT_ASSERT_EQUAL(1, frame->GetEventCount(wxEVT_COMMAND_TEXT_UPDATED));
+    CPPUNIT_ASSERT_EQUAL(0, maxlen.GetCount());
+    CPPUNIT_ASSERT_EQUAL(1, updated.GetCount());
 #endif
 }
 
@@ -271,7 +319,7 @@ void TextCtrlTestCase::ProcessEnter()
     wxTestableFrame* frame = wxStaticCast(wxTheApp->GetTopWindow(),
                                           wxTestableFrame);
 
-    EventCounter count(m_text, wxEVT_COMMAND_TEXT_ENTER);
+    EventCounter count(m_text, wxEVT_TEXT_ENTER);
 
     m_text->SetFocus();
 
@@ -279,20 +327,18 @@ void TextCtrlTestCase::ProcessEnter()
     sim.Char(WXK_RETURN);
     wxYield();
 
-    CPPUNIT_ASSERT_EQUAL(0, frame->GetEventCount(wxEVT_COMMAND_TEXT_ENTER));
+    CPPUNIT_ASSERT_EQUAL(0, frame->GetEventCount(wxEVT_TEXT_ENTER));
 
     // we need a text control with wxTE_PROCESS_ENTER for this test
     delete m_text;
-    m_text = new wxTextCtrl(wxTheApp->GetTopWindow(), wxID_ANY, "",
-                            wxDefaultPosition, wxDefaultSize,
-                            wxTE_PROCESS_ENTER);
+    CreateText(wxTE_PROCESS_ENTER);
 
     m_text->SetFocus();
 
     sim.Char(WXK_RETURN);
     wxYield();
 
-    CPPUNIT_ASSERT_EQUAL(1, frame->GetEventCount(wxEVT_COMMAND_TEXT_ENTER));
+    CPPUNIT_ASSERT_EQUAL(1, frame->GetEventCount(wxEVT_TEXT_ENTER));
 #endif
 }
 #endif
@@ -301,14 +347,9 @@ void TextCtrlTestCase::Url()
 {
 #if wxUSE_UIACTIONSIMULATOR && defined(__WXMSW__)
     delete m_text;
-    m_text = new wxTextCtrl(wxTheApp->GetTopWindow(), wxID_ANY, "",
-                            wxDefaultPosition, wxDefaultSize,
-                            wxTE_MULTILINE | wxTE_RICH | wxTE_AUTO_URL);
-
-    wxTestableFrame* frame = wxStaticCast(wxTheApp->GetTopWindow(),
-                                          wxTestableFrame);
+    CreateText(wxTE_RICH | wxTE_AUTO_URL);
 
-    EventCounter count(m_text, wxEVT_COMMAND_TEXT_URL);
+    EventCounter url(m_text, wxEVT_TEXT_URL);
 
     m_text->AppendText("http://www.wxwidgets.org");
 
@@ -317,7 +358,7 @@ void TextCtrlTestCase::Url()
     sim.MouseClick();
     wxYield();
 
-    CPPUNIT_ASSERT_EQUAL(1, frame->GetEventCount());
+    CPPUNIT_ASSERT_EQUAL(1, url.GetCount());
 #endif
 }
 
@@ -326,8 +367,7 @@ void TextCtrlTestCase::Style()
 #ifndef __WXOSX__
     delete m_text;
     // We need wxTE_RICH under windows for style support
-    m_text = new wxTextCtrl(wxTheApp->GetTopWindow(), wxID_ANY, "",
-                            wxDefaultPosition, wxDefaultSize, wxTE_RICH);
+    CreateText(wxTE_RICH);
 
     // Red text on a white background
     m_text->SetDefaultStyle(wxTextAttr(*wxRED, *wxWHITE));
@@ -378,13 +418,62 @@ void TextCtrlTestCase::Style()
 #endif
 }
 
-void TextCtrlTestCase::Lines()
+void TextCtrlTestCase::FontStyle()
 {
-#ifndef __WXOSX__
+    // We need wxTE_RICH under MSW and wxTE_MULTILINE under GTK for style
+    // support so recreate the control with these styles.
     delete m_text;
-    m_text = new wxTextCtrl(wxTheApp->GetTopWindow(), wxID_ANY, "",
-                            wxDefaultPosition, wxSize(400, 200), wxTE_MULTILINE | wxTE_DONTWRAP);
+    CreateText(wxTE_RICH);
+
+    // Check that we get back the same font from GetStyle() after setting it
+    // with SetDefaultStyle().
+    wxFont fontIn(14,
+                  wxFONTFAMILY_DEFAULT,
+                  wxFONTSTYLE_NORMAL,
+                  wxFONTWEIGHT_NORMAL);
+    wxTextAttr attrIn;
+    attrIn.SetFont(fontIn);
+    if ( !m_text->SetDefaultStyle(attrIn) )
+    {
+        // Skip the test if the styles are not supported.
+        return;
+    }
+
+    m_text->AppendText("Default font size 14");
+
+    wxTextAttr attrOut;
+    m_text->GetStyle(5, attrOut);
 
+    CPPUNIT_ASSERT( attrOut.HasFont() );
+
+    wxFont fontOut = attrOut.GetFont();
+#ifdef __WXMSW__
+    // Under MSW we get back an encoding in the font even though we hadn't
+    // specified it originally. It's not really a problem but we need this hack
+    // to prevent the assert below from failing because of it.
+    fontOut.SetEncoding(fontIn.GetEncoding());
+#endif
+    CPPUNIT_ASSERT_EQUAL( fontIn, fontOut );
+
+
+    // Also check the same for SetStyle().
+    fontIn.SetPointSize(10);
+    fontIn.SetWeight(wxFONTWEIGHT_BOLD);
+    attrIn.SetFont(fontIn);
+    m_text->SetStyle(0, 6, attrIn);
+
+    m_text->GetStyle(4, attrOut);
+    CPPUNIT_ASSERT( attrOut.HasFont() );
+
+    fontOut = attrOut.GetFont();
+#ifdef __WXMSW__
+    fontOut.SetEncoding(fontIn.GetEncoding());
+#endif
+    CPPUNIT_ASSERT_EQUAL( fontIn, fontOut );
+}
+
+void TextCtrlTestCase::Lines()
+{
     m_text->SetValue("line1\nline2\nlong long line 3");
     m_text->Refresh();
     m_text->Update();
@@ -399,16 +488,22 @@ void TextCtrlTestCase::Lines()
     CPPUNIT_ASSERT_EQUAL(5, m_text->GetNumberOfLines());
     CPPUNIT_ASSERT_EQUAL(0, m_text->GetLineLength(3));
     CPPUNIT_ASSERT_EQUAL("", m_text->GetLineText(3));
+
+    // Verify that wrapped lines count as 2 lines.
+    //
+    // This currently doesn't work neither in wxGTK nor wxOSX/Cocoa, see
+    // #12366, where GetNumberOfLines() always returns the number of logical,
+    // not physical, lines.
+    m_text->AppendText("\n" + wxString(50, '1') + ' ' + wxString(50, '2'));
+#if defined(__WXGTK__) || defined(__WXOSX_COCOA__)
+    CPPUNIT_ASSERT_EQUAL(6, m_text->GetNumberOfLines());
+#else
+    CPPUNIT_ASSERT_EQUAL(7, m_text->GetNumberOfLines());
 #endif
 }
 
 void TextCtrlTestCase::LogTextCtrl()
 {
-    delete m_text;
-    m_text = new wxTextCtrl(wxTheApp->GetTopWindow(), wxID_ANY, "",
-                            wxDefaultPosition, wxSize(400, 200),
-                            wxTE_MULTILINE);
-
     CPPUNIT_ASSERT(m_text->IsEmpty());
 
     wxLogTextCtrl* logtext = new wxLogTextCtrl(m_text);
@@ -422,4 +517,94 @@ void TextCtrlTestCase::LogTextCtrl()
     CPPUNIT_ASSERT(!m_text->IsEmpty());
 }
 
+void TextCtrlTestCase::PositionToCoords()
+{
+    DoPositionToCoordsTestWithStyle(0);
+}
+
+void TextCtrlTestCase::PositionToCoordsRich()
+{
+    DoPositionToCoordsTestWithStyle(wxTE_RICH);
+}
+
+void TextCtrlTestCase::PositionToCoordsRich2()
+{
+    DoPositionToCoordsTestWithStyle(wxTE_RICH2);
+}
+
+void TextCtrlTestCase::DoPositionToCoordsTestWithStyle(long style)
+{
+    delete m_text;
+    CreateText(style);
+
+    // Asking for invalid index should fail.
+    WX_ASSERT_FAILS_WITH_ASSERT( m_text->PositionToCoords(1) );
+
+    // Getting position shouldn't return wxDefaultPosition except if the method
+    // is not implemented at all in the current port.
+    const wxPoint pos0 = m_text->PositionToCoords(0);
+    if ( pos0 == wxDefaultPosition )
+    {
+#if defined(__WXMSW__) || defined(__WXGTK20__)
+        CPPUNIT_FAIL( "PositionToCoords() unexpectedly failed." );
+#endif
+        return;
+    }
+
+    CPPUNIT_ASSERT(pos0.x >= 0);
+    CPPUNIT_ASSERT(pos0.y >= 0);
+
+
+    m_text->SetValue("Hello");
+    wxYield(); // Let GTK layout the control correctly.
+
+    // Position of non-first character should be positive.
+    const long posHello4 = m_text->PositionToCoords(4).x;
+    CPPUNIT_ASSERT( posHello4 > 0 );
+
+    // Asking for position beyond the last character should succeed and return
+    // reasonable result.
+    CPPUNIT_ASSERT( m_text->PositionToCoords(5).x > posHello4 );
+
+    // But asking for the next position should fail.
+    WX_ASSERT_FAILS_WITH_ASSERT( m_text->PositionToCoords(6) );
+
+    // Test getting the coordinates of the last character when it is in the
+    // beginning of a new line to exercise MSW code which has specific logic
+    // for it.
+    m_text->AppendText("\n");
+    const wxPoint posLast = m_text->PositionToCoords(m_text->GetLastPosition());
+    CPPUNIT_ASSERT_EQUAL( pos0.x, posLast.x );
+    CPPUNIT_ASSERT( posLast.y > 0 );
+
+
+    // Add enough contents to the control to make sure it has a scrollbar.
+    m_text->SetValue("First line" + wxString(50, '\n') + "Last line");
+    m_text->SetInsertionPoint(0);
+    wxYield(); // Let GTK layout the control correctly.
+
+    // This shouldn't change anything for the first position coordinates.
+    CPPUNIT_ASSERT_EQUAL( pos0, m_text->PositionToCoords(0) );
+
+    // And the last one must be beyond the window boundary and so not be
+    // visible -- but getting its coordinate should still work.
+    CPPUNIT_ASSERT
+    (
+        m_text->PositionToCoords(m_text->GetLastPosition()).y > TEXT_HEIGHT
+    );
+
+
+    // Now make it scroll to the end and check that the first position now has
+    // negative offset as its above the visible part of the window while the
+    // last position is in its bounds.
+    m_text->SetInsertionPointEnd();
+
+    CPPUNIT_ASSERT( m_text->PositionToCoords(0).y < 0 );
+    CPPUNIT_ASSERT
+    (
+        m_text->PositionToCoords(m_text->GetInsertionPoint()).y <= TEXT_HEIGHT
+    );
+}
+
+
 #endif //wxUSE_TEXTCTRL