diff --git a/include/wx/qt/choice.h b/include/wx/qt/choice.h index 06602b9b68..208005ec06 100644 --- a/include/wx/qt/choice.h +++ b/include/wx/qt/choice.h @@ -71,6 +71,8 @@ protected: virtual void DoClear(); virtual void DoDeleteOneItem(unsigned int pos); + void QtInitSort(QComboBox *combo); + QComboBox *m_qtComboBox; private: diff --git a/include/wx/qt/combobox.h b/include/wx/qt/combobox.h index 490e2cb117..bc0222d5cb 100644 --- a/include/wx/qt/combobox.h +++ b/include/wx/qt/combobox.h @@ -52,7 +52,7 @@ public: const wxValidator& validator = wxDefaultValidator, const wxString& name = wxComboBoxNameStr); - virtual void SetSelection(int n) wxOVERRIDE { wxChoice::SetSelection(n); } + virtual void SetSelection(int n) wxOVERRIDE; virtual void SetSelection(long from, long to) wxOVERRIDE; virtual int GetSelection() const wxOVERRIDE { return wxChoice::GetSelection(); } @@ -70,6 +70,12 @@ public: bool IsTextEmpty() const { return wxTextEntry::IsEmpty(); } virtual void SetValue(const wxString& value) wxOVERRIDE; + virtual void ChangeValue(const wxString& value) wxOVERRIDE; + virtual void AppendText(const wxString &value) wxOVERRIDE; + virtual void Replace(long from, long to, const wxString &value) wxOVERRIDE; + virtual void WriteText(const wxString &value) wxOVERRIDE; + virtual void SetInsertionPoint(long insertion) wxOVERRIDE; + virtual long GetInsertionPoint() const wxOVERRIDE; virtual void Popup(); virtual void Dismiss(); @@ -80,6 +86,7 @@ protected: virtual wxString DoGetValue() const wxOVERRIDE; private: + void SetActualValue(const wxString& value); // From wxTextEntry: virtual wxWindow *GetEditableWindow() wxOVERRIDE { return this; } diff --git a/src/qt/choice.cpp b/src/qt/choice.cpp index 1bd6511202..491398b718 100644 --- a/src/qt/choice.cpp +++ b/src/qt/choice.cpp @@ -10,7 +10,38 @@ #include "wx/choice.h" #include "wx/qt/private/winevent.h" + #include +#include + +namespace +{ + +class LexicalSortProxyModel : public QSortFilterProxyModel +{ +public: + bool lessThan( const QModelIndex &left, const QModelIndex &right ) const wxOVERRIDE + { + const QVariant leftData = sourceModel()->data( left ); + const QVariant rightData = sourceModel()->data( right ); + + if ( leftData.type() != QVariant::String ) + return false; + + int insensitiveResult = QString::compare( + leftData.value(), + rightData.value(), + Qt::CaseInsensitive ); + + if ( insensitiveResult == 0 ) + { + return QString::compare( leftData.value(), + rightData.value() ) < 0; + } + + return insensitiveResult < 0; + } +}; class wxQtChoice : public wxQtEventSignalHandler< QComboBox, wxChoice > { @@ -36,12 +67,22 @@ void wxQtChoice::activated(int WXUNUSED(index)) handler->SendSelectionChangedEvent(wxEVT_CHOICE); } +} // anonymous namespace + wxChoice::wxChoice() : m_qtComboBox(NULL) { } +void wxChoice::QtInitSort( QComboBox *combo ) +{ + QSortFilterProxyModel *proxyModel = new LexicalSortProxyModel(); + proxyModel->setSourceModel(combo->model()); + combo->model()->setParent(proxyModel); + combo->setModel(proxyModel); +} + wxChoice::wxChoice( wxWindow *parent, wxWindowID id, const wxPoint& pos, @@ -90,6 +131,8 @@ bool wxChoice::Create( wxWindow *parent, wxWindowID id, { m_qtComboBox = new wxQtChoice( parent, this ); + QtInitSort( m_qtComboBox ); + while ( n-- > 0 ) m_qtComboBox->addItem( wxQtConvertString( *choices++ )); @@ -169,11 +212,17 @@ int wxChoice::DoInsertItems(const wxArrayStringsAdapter & items, int wxChoice::DoInsertOneItem(const wxString& item, unsigned int pos) { + // Maintain unselected state + const bool unselected = m_qtComboBox->currentIndex() == -1; + m_qtComboBox->insertItem(pos, wxQtConvertString(item)); if ( IsSorted() ) m_qtComboBox->model()->sort(0); + if ( unselected ) + m_qtComboBox->setCurrentIndex(-1); + return pos; } @@ -197,6 +246,12 @@ void wxChoice::DoClear() void wxChoice::DoDeleteOneItem(unsigned int pos) { + const int selection = GetSelection(); + + if ( selection >= 0 && static_cast(selection) == pos ) + { + SetSelection( wxNOT_FOUND ); + } m_qtComboBox->removeItem(pos); } diff --git a/src/qt/combobox.cpp b/src/qt/combobox.cpp index 4ca376f23d..36685b1954 100644 --- a/src/qt/combobox.cpp +++ b/src/qt/combobox.cpp @@ -23,13 +23,37 @@ public: virtual void showPopup() wxOVERRIDE; virtual void hidePopup() wxOVERRIDE; + class IgnoreTextChange + { + public: + // Note that wxComboBox inherits its QComboBox pointer from wxChoice, + // where it can't be stored as wxQtComboBox, but its dynamic type is + // nevertheless always wxQtComboBox, so the cast below is safe. + explicit IgnoreTextChange(QComboBox *combo) + : m_combo(static_cast(combo)) + { + m_combo->m_textChangeIgnored = true; + } + + ~IgnoreTextChange() + { + m_combo->m_textChangeIgnored = false; + } + + private: + wxQtComboBox* m_combo; + }; + private: void activated(int index); void editTextChanged(const QString &text); + + bool m_textChangeIgnored; }; wxQtComboBox::wxQtComboBox( wxWindow *parent, wxComboBox *handler ) - : wxQtEventSignalHandler< QComboBox, wxComboBox >( parent, handler ) + : wxQtEventSignalHandler< QComboBox, wxComboBox >( parent, handler ), + m_textChangeIgnored( false ) { setEditable( true ); connect(this, static_cast(&QComboBox::activated), @@ -61,6 +85,9 @@ void wxQtComboBox::activated(int WXUNUSED(index)) void wxQtComboBox::editTextChanged(const QString &text) { + if ( m_textChangeIgnored ) + return; + wxComboBox *handler = GetHandler(); if ( handler ) { @@ -70,6 +97,11 @@ void wxQtComboBox::editTextChanged(const QString &text) } } +void wxComboBox::SetSelection( int n ) +{ + wxQtComboBox::IgnoreTextChange ignore( m_qtComboBox ); + wxChoice::SetSelection( n ); +} wxComboBox::wxComboBox() { @@ -128,6 +160,8 @@ bool wxComboBox::Create(wxWindow *parent, wxWindowID id, const wxString& name ) { m_qtComboBox = new wxQtComboBox( parent, this ); + QtInitSort( m_qtComboBox ); + while ( n-- > 0 ) m_qtComboBox->addItem( wxQtConvertString( *choices++ )); m_qtComboBox->setEditText( wxQtConvertString( value )); @@ -135,12 +169,67 @@ bool wxComboBox::Create(wxWindow *parent, wxWindowID id, return QtCreateControl( parent, id, pos, size, style, validator, name ); } -void wxComboBox::SetValue(const wxString& value) +void wxComboBox::SetActualValue(const wxString &value) { if ( HasFlag(wxCB_READONLY) ) - SetStringSelection(value); + { + SetStringSelection( value ); + } else + { wxTextEntry::SetValue(value); + m_qtComboBox->setEditText( wxQtConvertString(value) ); + } +} + +void wxComboBox::SetValue(const wxString& value) +{ + SetActualValue( value ); + SetInsertionPoint( 0 ); +} + +void wxComboBox::ChangeValue(const wxString &value) +{ + wxQtComboBox::IgnoreTextChange ignore( m_qtComboBox ); + SetValue( value ); +} + +void wxComboBox::AppendText(const wxString &value) +{ + SetActualValue( GetValue() + value ); +} + +void wxComboBox::Replace(long from, long to, const wxString &value) +{ + const wxString original( GetValue() ); + + if ( to < 0 ) + { + to = original.length(); + } + + if ( from == 0 ) + { + SetActualValue( value + original.substr(to, original.length()) ); + } + + wxString front = original.substr( 0, from ) + value; + + long iPoint = front.length(); + if ( front.length() <= original.length() ) + { + SetActualValue( front + original.substr(to, original.length()) ); + } + else + { + SetActualValue( front ); + } + SetInsertionPoint( iPoint ); +} + +void wxComboBox::WriteText(const wxString &value) +{ + m_qtComboBox->lineEdit()->insert( wxQtConvertString( value ) ); } wxString wxComboBox::DoGetValue() const @@ -166,15 +255,40 @@ void wxComboBox::Clear() void wxComboBox::SetSelection( long from, long to ) { - // SelectAll uses -1 to -1, adjust for qt: - if (from == -1 && to == -1) + if ( from == -1 ) { from = 0; + } + if ( to == -1 ) + { to = GetValue().length(); } + + SetInsertionPoint( from ); // use the inner text entry widget (note that can be null if not editable) if ( m_qtComboBox->lineEdit() != NULL ) - m_qtComboBox->lineEdit()->setSelection(from, to); + { + m_qtComboBox->lineEdit()->setSelection( from, to - from ); + } +} + +void wxComboBox::SetInsertionPoint( long pos ) +{ + // check if pos indicates end of text: + if ( pos == -1 ) + m_qtComboBox->lineEdit()->end( false ); + else + m_qtComboBox->lineEdit()->setCursorPosition( pos ); +} + +long wxComboBox::GetInsertionPoint() const +{ + long selectionStart = m_qtComboBox->lineEdit()->selectionStart(); + + if ( selectionStart >= 0 ) + return selectionStart; + + return m_qtComboBox->lineEdit()->cursorPosition(); } void wxComboBox::GetSelection(long* from, long* to) const diff --git a/src/qt/textentry.cpp b/src/qt/textentry.cpp index f1c088165a..6a5cbc71dd 100644 --- a/src/qt/textentry.cpp +++ b/src/qt/textentry.cpp @@ -20,9 +20,11 @@ void wxTextEntry::WriteText(const wxString& WXUNUSED(text)) void wxTextEntry::Remove(long from, long to) { + const long insertionPoint = GetInsertionPoint(); wxString string = GetValue(); string.erase(from, to - from); SetValue(string); + SetInsertionPoint( std::min(insertionPoint, static_cast(string.length())) ); } void wxTextEntry::Copy() diff --git a/tests/controls/textentrytest.cpp b/tests/controls/textentrytest.cpp index 9c1dbe7d95..b053e80489 100644 --- a/tests/controls/textentrytest.cpp +++ b/tests/controls/textentrytest.cpp @@ -201,6 +201,28 @@ void TextEntryTestCase::Replace() CPPUNIT_ASSERT_EQUAL(2, entry->GetInsertionPoint()); } +void TextEntryTestCase::WriteText() +{ + wxTextEntry * const entry = GetTestEntry(); + + entry->SetValue("foo"); + entry->SetInsertionPoint(3); + entry->WriteText("bar"); + CPPUNIT_ASSERT_EQUAL( "foobar", entry->GetValue() ); + + entry->SetValue("foo"); + entry->SetInsertionPoint(0); + entry->WriteText("bar"); + CPPUNIT_ASSERT_EQUAL( "barfoo", entry->GetValue() ); + + entry->SetValue("abxxxhi"); + entry->SetSelection(2, 5); + entry->WriteText("cdefg"); + CPPUNIT_ASSERT_EQUAL( "abcdefghi", entry->GetValue() ); + CPPUNIT_ASSERT_EQUAL( 7, entry->GetInsertionPoint() ); + CPPUNIT_ASSERT_EQUAL( false, entry->HasSelection() ); +} + #if wxUSE_UIACTIONSIMULATOR class TextEventHandler diff --git a/tests/controls/textentrytest.h b/tests/controls/textentrytest.h index 648d601872..fc784c9d6f 100644 --- a/tests/controls/textentrytest.h +++ b/tests/controls/textentrytest.h @@ -44,7 +44,8 @@ protected: WXUISIM_TEST( Editable ); \ CPPUNIT_TEST( Hint ); \ CPPUNIT_TEST( CopyPaste ); \ - CPPUNIT_TEST( UndoRedo ) + CPPUNIT_TEST( UndoRedo ); \ + CPPUNIT_TEST( WriteText ) void SetValue(); void TextChangeEvents(); @@ -55,6 +56,7 @@ protected: void Hint(); void CopyPaste(); void UndoRedo(); + void WriteText(); private: // Selection() test helper: verify that selection is as described by the