diff --git a/include/wx/qt/listctrl.h b/include/wx/qt/listctrl.h index f9818722cc..7582d8be43 100644 --- a/include/wx/qt/listctrl.h +++ b/include/wx/qt/listctrl.h @@ -11,7 +11,8 @@ #include "wx/textctrl.h" class wxQtListTreeWidget; -class QTreeWidgetItem; +class wxQtListModel; +class wxQtVirtualListModel; class WXDLLIMPEXP_FWD_CORE wxImageList; @@ -159,6 +160,11 @@ public: // list or report view long GetTopItem() const; + virtual bool HasCheckBoxes() const wxOVERRIDE; + virtual bool EnableCheckBoxes(bool enable = true) wxOVERRIDE; + virtual bool IsItemChecked(long item) const wxOVERRIDE; + virtual void CheckItem(long item, bool check) wxOVERRIDE; + // Add or remove a single window style void SetSingleStyle(long style, bool add = true); @@ -273,16 +279,21 @@ protected: // Implement base class pure virtual methods. virtual long DoInsertColumn(long col, const wxListItem& info) wxOVERRIDE; - QTreeWidgetItem *QtGetItem(int id) const; - wxImageList * m_imageListNormal; // The image list for normal icons wxImageList * m_imageListSmall; // The image list for small icons wxImageList * m_imageListState; // The image list state icons (not implemented yet) bool m_ownsImageListNormal, m_ownsImageListSmall, m_ownsImageListState; + bool m_hasCheckBoxes; + private: + // Allow access to OnGetItemXXX() method from the virtual model class. + friend class wxQtVirtualListModel; + + wxQtListTreeWidget *m_qtTreeWidget; + wxQtListModel *m_model; wxDECLARE_DYNAMIC_CLASS( wxListCtrl ); }; diff --git a/src/qt/listctrl.cpp b/src/qt/listctrl.cpp index 8519c00f2c..75c29efa63 100644 --- a/src/qt/listctrl.cpp +++ b/src/qt/listctrl.cpp @@ -13,15 +13,18 @@ #endif #include -#include +#include #include +#include #ifndef WX_PRECOMP #include "wx/bitmap.h" #endif // WX_PRECOMP +#include "wx/app.h" #include "wx/listctrl.h" #include "wx/imaglist.h" +#include "wx/recguard.h" #include "wx/qt/private/winevent.h" #include "wx/qt/private/treeitemfactory.h" @@ -59,51 +62,914 @@ wxListColumnFormat wxQtConvertAlignFlag(int align) } // anonymous namespace -class wxQtListTreeWidget : public wxQtEventSignalHandler< QTreeWidget, wxListCtrl > +class wxQtStyledItemDelegate : public QStyledItemDelegate +{ +public: + explicit wxQtStyledItemDelegate(wxWindow* parent) + : m_parent(parent), + m_textCtrl(NULL) + { + } + + QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &WXUNUSED(option), const QModelIndex &index) const wxOVERRIDE + { + if (m_textCtrl != NULL) + destroyEditor(m_textCtrl->GetHandle(), m_currentModelIndex); + + m_currentModelIndex = index; + m_textCtrl = new wxQtListTextCtrl(m_parent, parent); + m_textCtrl->SetFocus(); + return m_textCtrl->GetHandle(); + } + + void destroyEditor(QWidget *WXUNUSED(editor), const QModelIndex &WXUNUSED(index)) const wxOVERRIDE + { + if (m_textCtrl != NULL) + { + m_currentModelIndex = QModelIndex(); // invalidate the index + wxTheApp->ScheduleForDestruction(m_textCtrl); + m_textCtrl = NULL; + } + } + + void setModelData(QWidget *WXUNUSED(editor), QAbstractItemModel *WXUNUSED(model), const QModelIndex &WXUNUSED(index)) const wxOVERRIDE + { + // Don't set model data until wx has had a chance to send out events + } + + wxTextCtrl* GetEditControl() const + { + return m_textCtrl; + } + + QModelIndex GetCurrentModelIndex() const + { + return m_currentModelIndex; + } + + void AcceptModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const + { + QStyledItemDelegate::setModelData(editor, model, index); + } + +private: + wxWindow* m_parent; + mutable wxTextCtrl* m_textCtrl; + mutable QModelIndex m_currentModelIndex; +}; + +class wxQtListModel : public QAbstractTableModel +{ +public: + explicit wxQtListModel(wxListCtrl *listCtrl) : + m_view(NULL), + m_listCtrl(listCtrl) + { + } + + int rowCount(const QModelIndex& WXUNUSED(parent)) const wxOVERRIDE + { + return static_cast(m_rows.size()); + } + int columnCount(const QModelIndex& WXUNUSED(parent)) const wxOVERRIDE + { + return static_cast(m_headers.size()); + } + + QVariant data(const QModelIndex &index, int role) const wxOVERRIDE + { + const int row = index.row(); + const int col = index.column(); + + wxCHECK_MSG(row >= 0 && static_cast(row) < m_rows.size(), + QVariant(), + "Invalid row index" + ); + + const RowItem &rowItem = m_rows[row]; + const ColumnItem &columnItem = rowItem[col]; + + const bool isSelected = IsSelected(index); + + switch ( role ) + { + case Qt::DisplayRole: + return QVariant::fromValue(columnItem.m_label); + + case Qt::EditRole: + return QVariant::fromValue(columnItem.m_label); + + case Qt::DecorationRole: + { + wxImageList *imageList = GetImageList(); + if ( imageList == NULL ) + return QVariant(); + + int imageIndex = -1; + + if ( columnItem.m_selectedImage != -1 ) + { + if ( isSelected ) + imageIndex = columnItem.m_selectedImage; + } + + if ( imageIndex == -1 ) + imageIndex = columnItem.m_image; + + if ( imageIndex == -1 ) + return QVariant(); + + wxBitmap image = imageList->GetBitmap(imageIndex); + wxCHECK_MSG(image.IsOk(), QVariant(), "Invalid image"); + return QVariant::fromValue(*image.GetHandle()); + } + case Qt::FontRole: + return QVariant::fromValue(columnItem.m_font); + + case Qt::BackgroundRole: + return columnItem.m_backgroundColour.isValid() && !isSelected + ? QVariant::fromValue(columnItem.m_backgroundColour) + : QVariant(); + + case Qt::ForegroundRole: + return columnItem.m_textColour.isValid() && !isSelected + ? QVariant::fromValue(columnItem.m_textColour) + : QVariant(); + + case Qt::TextAlignmentRole: + return columnItem.m_align; + + case Qt::CheckStateRole: + return col == 0 && m_listCtrl->HasCheckBoxes() + ? rowItem.m_checked + : QVariant(); + + default: + return QVariant(); + } + } + + bool setData( + const QModelIndex &index, + const QVariant &value, + int role + ) wxOVERRIDE + { + const int row = index.row(); + const int col = index.column(); + + wxCHECK_MSG( + row >= 0 && static_cast(row) < m_rows.size(), + false, + "Invalid row index" + ); + wxCHECK_MSG( + col >= 0 && static_cast(col) < m_rows[row].m_columns.size(), + false, + "Invalid column index" + ); + + if ( role == Qt::DisplayRole || role == Qt::EditRole ) + { + m_rows[row][col].m_label = value.toString(); + return true; + } + + if ( role == Qt::CheckStateRole && col == 0 ) + { + m_rows[row].m_checked = + static_cast(value.toUInt()) == Qt::Checked; + + wxListItem listItem; + listItem.SetId(row); + + const wxEventType eventType = m_rows[row].m_checked ? + wxEVT_LIST_ITEM_CHECKED : wxEVT_LIST_ITEM_UNCHECKED; + + wxListEvent event(eventType, m_listCtrl->GetId()); + event.SetEventObject(m_listCtrl); + event.SetItem(listItem); + m_listCtrl->HandleWindowEvent(event); + return true; + } + + return false; + } + + QVariant headerData( + int section, + Qt::Orientation orientation, + int role + ) const wxOVERRIDE + { + if ( orientation == Qt::Vertical ) + return QVariant(); + + wxCHECK_MSG(static_cast(section) < m_headers.size(), + QVariant(), "Invalid header index"); + + const ColumnItem& header = m_headers[section]; + + switch ( role ) + { + case Qt::DisplayRole: + return header.m_label; + + case Qt::TextAlignmentRole: + return header.m_align; + } + return QVariant(); + } + + Qt::ItemFlags flags(const QModelIndex &index) const wxOVERRIDE + { + Qt::ItemFlags itemFlags = Qt::ItemIsSelectable | Qt::ItemNeverHasChildren; + + if ( m_listCtrl->HasFlag(wxLC_EDIT_LABELS) ) + itemFlags |= Qt::ItemIsEditable; + + if ( index.column() == 0 && m_listCtrl->HasCheckBoxes() ) + itemFlags |= Qt::ItemIsUserCheckable; + + return itemFlags | QAbstractTableModel::flags(index); + } + + bool removeRows(int row, int count, const QModelIndex &parent) wxOVERRIDE + { + if ( count == 0 ) + return true; + + beginRemoveRows(parent, row, row + count - 1); + eraseFromContainer(m_rows, row, count); + endRemoveRows(); + return true; + } + + bool removeColumns(int column, int count, const QModelIndex &parent) wxOVERRIDE + { + if ( count == 0 ) + return true; + + beginRemoveColumns(parent, column, column + count - 1); + + eraseFromContainer(m_headers, column, count); + + const int nRows = this->m_rows.size(); + for ( int i = 0; i < nRows; ++i ) + { + eraseFromContainer(m_rows[i].m_columns, column, count); + } + + endRemoveColumns(); + return true; + } + + bool GetColumn(int index, wxListItem &info) const + { + wxCHECK_MSG(static_cast(index) < m_headers.size(), + false, "Invalid column"); + + const ColumnItem &column = m_headers[index]; + info.SetText(wxQtConvertString(column.m_label)); + info.SetAlign(wxQtConvertAlignFlag(column.m_align)); + info.SetWidth(m_view->columnWidth(index)); + return true; + } + + bool SetColumn(int index, const wxListItem& info) + { + wxCHECK_MSG(static_cast(index) < m_headers.size(), + false, "Invalid column"); + + ColumnItem &column = m_headers[index]; + column.m_label = wxQtConvertString(info.GetText()); + column.m_align = wxQtConvertTextAlign(info.GetAlign()); + + headerDataChanged(Qt::Horizontal, index, index); + return true; + + } + + virtual bool GetItem(wxListItem& info) + { + const int row = static_cast(info.GetId()); + const int col = info.m_col; + + wxCHECK_MSG( + row >= 0 && static_cast(row) < m_rows.size(), + false, + "Invalid row" + ); + + wxCHECK_MSG( + col >= 0 && static_cast(col) < m_rows[row].m_columns.size(), + false, + "Invalid col" + ); + + const RowItem &rowItem = m_rows[row]; + const ColumnItem &columnItem = rowItem[col]; + + if ( !info.m_mask ) + // by default, get everything for backwards compatibility + info.m_mask = -1; + + if ( info.m_mask & wxLIST_MASK_TEXT ) + info.SetText(wxQtConvertString(columnItem.m_label)); + + if ( info.m_mask & wxLIST_MASK_DATA ) + { + info.SetData(rowItem.m_data); + } + + CopySelectStatusToItem(info, row, col); + + return true; + } + + bool SetItem(wxListItem &info) + { + const int row = static_cast(info.GetId()); + const int col = info.m_col; + + wxCHECK_MSG( static_cast(row) < m_rows.size(), + false, "Invalid row"); + wxCHECK_MSG( static_cast(col) < m_headers.size(), + false, "Invalid col"); + + const QModelIndex modelIndex = index(row, col); + RowItem &rowItem = m_rows[row]; + ColumnItem &columnItem = rowItem[col]; + + QVector roles; + + if ( (info.m_mask & wxLIST_MASK_TEXT) && !info.GetText().IsNull() ) + { + columnItem.m_label = wxQtConvertString(info.GetText()); + roles.push_back(Qt::DisplayRole); + } + + if ( info.m_mask & wxLIST_MASK_FORMAT ) + { + columnItem.m_align = wxQtConvertTextAlign(info.GetAlign()); + roles.push_back(Qt::TextAlignmentRole); + } + + if ( info.m_mask & wxLIST_MASK_DATA ) + { + rowItem.m_data = (void*)(info.GetData()); + roles.push_back(Qt::UserRole); + } + + if ( info.m_mask & wxLIST_MASK_STATE ) + { + if ( (info.m_stateMask & wxLIST_STATE_FOCUSED) && + (info.m_state & wxLIST_STATE_FOCUSED) ) + m_view->setCurrentIndex(modelIndex); + if ( info.m_stateMask & wxLIST_STATE_SELECTED ) + { + QItemSelectionModel *selection = m_view->selectionModel(); + const QItemSelectionModel::SelectionFlag flag = + info.m_state & wxLIST_STATE_SELECTED + ? QItemSelectionModel::Select + : QItemSelectionModel::Deselect; + selection->select(modelIndex, flag|QItemSelectionModel::Rows); + } + } + + if ( info.m_mask & wxLIST_MASK_IMAGE ) + { + columnItem.m_image = info.m_image; + roles.push_back(Qt::DecorationRole); + } + + if ( info.GetFont().IsOk() ) + { + columnItem.m_font = info.GetFont().GetHandle(); + roles.push_back(Qt::FontRole); + } + + if ( info.GetTextColour().IsOk() ) + { + columnItem.m_textColour = info.GetTextColour().GetQColor(); + roles.push_back(Qt::BackgroundRole); + } + + if ( info.GetBackgroundColour().IsOk() ) + { + columnItem.m_backgroundColour = info.GetBackgroundColour().GetQColor(); + roles.push_back(Qt::ForegroundRole); + } + + dataChanged(modelIndex, modelIndex, roles); + + return true; + } + + void SetView(QTreeView *view) + { + m_view = view; + } + + wxColour GetItemTextColour(long item) + { + wxCHECK_MSG(item >= 0 && static_cast(item) < m_rows.size(), + wxNullColour, "Invalid row"); + + wxCHECK_MSG(!m_rows[item].m_columns.empty(), + wxNullColour, "No columns in model"); + + return wxColour(m_rows[item][0].m_textColour); + } + + wxColour GetItemBackgroundColour(long item) + { + wxCHECK_MSG(item >= 0 && static_cast(item) < m_rows.size(), + wxNullColour, "Invalid row"); + wxCHECK_MSG(!m_headers.empty(), + wxNullColour, "No columns in model"); + + return wxColour(m_rows[item][0].m_backgroundColour); + } + + wxFont GetItemFont(long item) + { + wxCHECK_MSG(item >= 0 && static_cast(item) < m_rows.size(), + wxNullFont, "Invalid row"); + + wxCHECK_MSG(!m_headers.empty(), + wxNullFont, "No columns in model"); + + return wxFont(m_rows[item][0].m_font); + } + + long FindItem(long start, const QString& str, bool partial) const + { + if ( start < 0 ) + start = 0; + + const QString strUpper = str.toUpper(); + + const long numberOfRows = m_rows.size(); + const long numberOfColumns = m_headers.size(); + + if ( partial ) + { + for ( long i = start; i < numberOfRows; ++i ) + { + for ( long j = 0; j < numberOfColumns; ++j ) + { + const QString current = m_rows[i][j].m_label.toUpper(); + if ( current.contains(strUpper) ) + return i; + } + } + + return -1; + } + + for ( long i = start; i < numberOfRows; ++i ) + { + for ( long j = 0; j < numberOfColumns; ++j ) + { + if ( m_rows[i][j].m_label.toUpper() == strUpper ) + return i; + } + } + + return -1; + } + + long FindItem(long start, wxUIntPtr data) const + { + if ( start < 0 ) + start = 0; + + const long count = m_rows.size(); + + for ( long i = start; i < count; ++i ) + { + if ( m_rows[i].m_data == reinterpret_cast(data) ) + return i; + } + + return -1; + } + + + long InsertItem(const wxListItem& info) + { + const int column = info.GetColumn(); + wxCHECK_MSG(column >= 0, -1, "Invalid column index"); + + if ( static_cast(column) >= m_headers.size() ) + { + beginInsertColumns(QModelIndex(), m_headers.size(), column); + + const ColumnItem emptyColumn; + + for ( int c = m_headers.size(); c <= column; ++c ) + { + m_headers.push_back(emptyColumn); + for ( size_t r = 0; r < m_rows.size(); ++r ) + { + m_rows[r].m_columns.push_back(emptyColumn); + } + } + + endInsertColumns(); + } + + const long row = info.GetId(); + long newRowIndex; + + if ( row == -1 || static_cast(row) >= m_rows.size() ) + { + m_rows.push_back(RowItem(m_headers.size())); + newRowIndex = m_rows.size() - 1; + } + else + { + std::vector::iterator i = m_rows.begin(); + std::advance(i, row); + m_rows.insert(i, RowItem(m_headers.size())); + newRowIndex = row; + } + + beginInsertRows(QModelIndex(), newRowIndex, newRowIndex); + + wxListItem newItem = info; + newItem.SetId(newRowIndex); + SetItem(newItem); + + endInsertRows(); + + return newRowIndex; + } + + long InsertColumn(long col, const wxListItem& info) + { + long newColumnIndex; + + ColumnItem newColumn; + if ( info.m_mask & wxLIST_MASK_FORMAT ) + { + newColumn.m_align = wxQtConvertTextAlign(info.GetAlign()); + } + newColumn.m_label = wxQtConvertString(info.GetText()); + + if ( col == -1 || static_cast(col) >= m_headers.size() ) + { + newColumnIndex = m_headers.empty() ? 0 : m_headers.size(); + } + else + { + newColumnIndex = col; + } + + beginInsertColumns(QModelIndex(), newColumnIndex, newColumnIndex); + + std::vector::iterator i = m_headers.begin(); + std::advance(i, newColumnIndex); + m_headers.insert(i, newColumn); + + const int numberOfRows = m_rows.size(); + + for (int i = 0; i < numberOfRows; ++i ) + { + std::vector::iterator it = m_rows[i].m_columns.begin(); + std::advance(it, newColumnIndex); + m_rows[i].m_columns.insert(it, newColumn); + } + + endInsertColumns(); + + return true; + } + + void SortItems(wxListCtrlCompare fn, wxIntPtr data) + { + CompareAdapter compare(fn, data); + std::sort(m_rows.begin(), m_rows.end(), compare); + } + + bool IsItemChecked(long item) const + { + wxCHECK_MSG(item >= 0 && static_cast(item) <= m_rows.size(), + false, "Invalid row"); + + return m_rows[item].m_checked; + } + + void CheckItem(long item, bool check) + { + wxCHECK_RET(item >= 0 && static_cast(item) <= m_rows.size(), + "Invalid row"); + + m_rows[item].m_checked = check; + + QVector roles; + roles.push_back(Qt::CheckStateRole); + const QModelIndex modelIndex = index(item, 0); + dataChanged(modelIndex, modelIndex, roles); + } + + virtual bool IsVirtual() const + { + return false; + } + + virtual void SetVirtualItemCount(long WXUNUSED(count)) + { + } + +protected: + wxImageList *GetImageList() const + { + const int requiredList = m_listCtrl->HasFlag(wxLC_SMALL_ICON) + ? wxIMAGE_LIST_SMALL + : wxIMAGE_LIST_NORMAL; + return m_listCtrl->GetImageList(requiredList); + } + + wxListCtrl *GetListCtrl() const + { + return m_listCtrl; + } + + bool IsSelected(const QModelIndex& index) const + { + QModelIndexList indices = m_view->selectionModel()->selectedIndexes(); + return indices.contains(index); + } + + void CopySelectStatusToItem(wxListItem& info, int row, int col) + { + if ( info.m_mask & wxLIST_MASK_STATE ) + { + info.m_state = wxLIST_STATE_DONTCARE; + if (info.m_stateMask & wxLIST_STATE_FOCUSED) + { + if (m_view->currentIndex().row() == row) + info.m_state |= wxLIST_STATE_FOCUSED; + } + if (info.m_stateMask & wxLIST_STATE_SELECTED) + { + if (IsSelected(index(row, col))) + info.m_state |= wxLIST_STATE_SELECTED; + } + } + } + +private: + template + static void eraseFromContainer(T &container, int start_index, int count) + { + + typename T::iterator first = container.begin(); + std::advance(first, start_index); + + typename T::iterator last = first; + std::advance(last, count); + + container.erase(first, last); + } + + struct ColumnItem + { + ColumnItem() : + m_align(Qt::AlignLeft), + m_image(-1), + m_selectedImage(-1) + { + } + + QString m_label; + QColor m_backgroundColour; + QColor m_textColour; + QFont m_font; + Qt::AlignmentFlag m_align; + int m_image; + int m_selectedImage; + }; + + struct RowItem + { + RowItem() : + m_data(NULL), + m_checked(false) + { + } + + RowItem(int columnCount ) : + m_columns(columnCount), + m_data(NULL), + m_checked(false) + { + } + + const ColumnItem &operator[](int index) const + { + return m_columns[index]; + } + + ColumnItem &operator[](int index) + { + return m_columns[index]; + } + + std::vector m_columns; + void *m_data; + bool m_checked; + + }; + + struct CompareAdapter + { + CompareAdapter(wxListCtrlCompare fn, wxIntPtr data) : + m_fn(fn), + m_data(data) + { + } + + bool operator()(const RowItem &lhs, const RowItem &rhs) const + { + return m_fn( + reinterpret_cast(lhs.m_data), + reinterpret_cast(rhs.m_data), + m_data) < 0; + } + + wxListCtrlCompare m_fn; + wxIntPtr m_data; + }; + + std::vector m_headers; + std::vector m_rows; + QTreeView *m_view; + wxListCtrl *m_listCtrl; +}; + +class wxQtVirtualListModel : public wxQtListModel +{ +public: + wxQtVirtualListModel(wxListCtrl *listCtrl) : + wxQtListModel(listCtrl), + m_rowCount(0) + { + } + + int rowCount(const QModelIndex& WXUNUSED(parent)) const wxOVERRIDE + { + return m_rowCount; + } + + QVariant data(const QModelIndex &index, int role) const wxOVERRIDE + { + wxListCtrl *listCtrl = GetListCtrl(); + + const int row = index.row(); + const int col = index.column(); + + if ( role == Qt::DisplayRole || role == Qt::EditRole ) + { + const wxString text = listCtrl->OnGetItemText(row, col); + return QVariant::fromValue(wxQtConvertString(text)); + } + + if ( role == Qt::DecorationRole ) + { + wxImageList *imageList = GetImageList(); + if ( imageList == NULL ) + return QVariant(); + + const int imageIndex = listCtrl->OnGetItemColumnImage(row, col); + if ( imageIndex == -1 ) + return QVariant(); + wxBitmap image = imageList->GetBitmap(imageIndex); + wxCHECK_MSG(image.IsOk(), QVariant(), "Invalid Bitmap"); + return QVariant::fromValue(*image.GetHandle()); + } + + return QVariant(); + } + + bool GetItem(wxListItem& info) wxOVERRIDE + { + const int row = static_cast(info.GetId()); + const int col = info.m_col; + + if ( info.m_mask & wxLIST_MASK_TEXT ) + info.SetText(GetListCtrl()->OnGetItemText(row, col)); + + CopySelectStatusToItem(info, row, col); + + return true; + } + + bool IsVirtual() const wxOVERRIDE + { + return true; + } + + void SetVirtualItemCount(long count) wxOVERRIDE + { + beginResetModel(); + m_rowCount = static_cast(count); + endResetModel(); + } +private: + int m_rowCount; +}; + + +class wxQtListTreeWidget : public wxQtEventSignalHandler< QTreeView, wxListCtrl > { public: wxQtListTreeWidget( wxWindow *parent, wxListCtrl *handler ); - void EmitListEvent(wxEventType typ, QTreeWidgetItem *qitem, int column) const; + void EmitListEvent(wxEventType typ, const QModelIndex &index) const; - void closeEditor(QWidget *editor, QAbstractItemDelegate::EndEditHint hint) wxOVERRIDE + void closeEditor( + QWidget *editor, + QAbstractItemDelegate::EndEditHint hint + ) wxOVERRIDE { - QTreeWidget::closeEditor(editor, hint); - m_editorFactory.ClearEditor(); + // Close process can re-signal closeEditor so we need to guard against + // reentrant calls. + wxRecursionGuard guard(m_closingEditor); + + if (guard.IsInside()) + return; + + // There can be multiple calls to close editor when the item loses focus + const QModelIndex current_index = m_itemDelegate.GetCurrentModelIndex(); + if (!current_index.isValid()) + return; + + const wxString edited_text = m_itemDelegate.GetEditControl()->GetLineText(0); + wxListItem listItem; + listItem.SetId(current_index.row()); + listItem.SetColumn(current_index.column()); + listItem.SetText(edited_text); + + wxListEvent event(wxEVT_LIST_END_LABEL_EDIT, GetHandler()->GetId()); + event.SetEventObject(GetHandler()); + event.SetItem(listItem); + event.SetIndex(listItem.GetId()); + + if (hint == QAbstractItemDelegate::RevertModelCache) + { + event.SetEditCanceled(true); + EmitEvent(event); + } + else + { + // Allow event handlers to decide whether to accept edited text + if (!GetHandler()->HandleWindowEvent(event) || event.IsAllowed()) + m_itemDelegate.AcceptModelData(editor, model(), current_index); + } + + // wx doesn't have hints to edit next/previous item + if (hint == QAbstractItemDelegate::EditNextItem || hint == QAbstractItemDelegate::EditPreviousItem) + hint = QAbstractItemDelegate::SubmitModelCache; + QTreeView::closeEditor(editor, hint); + closePersistentEditor(current_index); } wxTextCtrl *GetEditControl() { - return m_editorFactory.GetEditControl(); + return m_itemDelegate.GetEditControl(); + } + + virtual void paintEvent(QPaintEvent *event) wxOVERRIDE + { + QTreeView::paintEvent(event); } private: - void itemClicked(QTreeWidgetItem * item, int column); - void itemActivated(QTreeWidgetItem * item, int column); - void itemPressed(QTreeWidgetItem * item, int column); + void itemClicked(const QModelIndex &index); + void itemActivated(const QModelIndex &index); + void itemPressed(const QModelIndex &index); - void ChangeEditorFactory() - { - QItemDelegate *qItemDelegate = static_cast(itemDelegate()); - qItemDelegate->itemEditorFactory(); - qItemDelegate->setItemEditorFactory(&m_editorFactory); - } - - wxQtTreeItemEditorFactory m_editorFactory; + wxQtStyledItemDelegate m_itemDelegate; + wxRecursionGuardFlag m_closingEditor; }; wxQtListTreeWidget::wxQtListTreeWidget( wxWindow *parent, wxListCtrl *handler ) - : wxQtEventSignalHandler< QTreeWidget, wxListCtrl >( parent, handler ), - m_editorFactory(handler) + : wxQtEventSignalHandler< QTreeView, wxListCtrl >( parent, handler ), + m_itemDelegate(handler), + m_closingEditor(0) { - connect(this, &QTreeWidget::itemClicked, this, &wxQtListTreeWidget::itemClicked); - connect(this, &QTreeWidget::itemPressed, this, &wxQtListTreeWidget::itemPressed); - connect(this, &QTreeWidget::itemActivated, this, &wxQtListTreeWidget::itemActivated); + connect(this, &QTreeView::clicked, this, &wxQtListTreeWidget::itemClicked); + connect(this, &QTreeView::pressed, this, &wxQtListTreeWidget::itemPressed); + connect(this, &QTreeView::activated, this, &wxQtListTreeWidget::itemActivated); - ChangeEditorFactory(); + setItemDelegate(&m_itemDelegate); + setEditTriggers(NoEditTriggers); } -void wxQtListTreeWidget::EmitListEvent(wxEventType typ, QTreeWidgetItem *qitem, int column) const +void wxQtListTreeWidget::EmitListEvent(wxEventType typ, const QModelIndex &index) const { wxListCtrl *handler = GetHandler(); if ( handler ) @@ -113,7 +979,8 @@ void wxQtListTreeWidget::EmitListEvent(wxEventType typ, QTreeWidgetItem *qitem, wxListEvent event; event.SetEventType(typ); event.SetId(handler->GetId()); - event.m_itemIndex = this->indexFromItem(qitem, column).row(); + event.m_itemIndex = index.row(); + event.m_col = index.column(); event.m_item.SetId(event.m_itemIndex); event.m_item.SetMask(wxLIST_MASK_TEXT | wxLIST_MASK_IMAGE | @@ -123,19 +990,19 @@ void wxQtListTreeWidget::EmitListEvent(wxEventType typ, QTreeWidgetItem *qitem, } } -void wxQtListTreeWidget::itemClicked(QTreeWidgetItem *qitem, int column) +void wxQtListTreeWidget::itemClicked(const QModelIndex &index) { - EmitListEvent(wxEVT_LIST_ITEM_SELECTED, qitem, column); + EmitListEvent(wxEVT_LIST_ITEM_SELECTED, index); } -void wxQtListTreeWidget::itemPressed(QTreeWidgetItem *qitem, int column) +void wxQtListTreeWidget::itemPressed(const QModelIndex &index) { - EmitListEvent(wxEVT_LIST_ITEM_SELECTED, qitem, column); + EmitListEvent(wxEVT_LIST_ITEM_SELECTED, index); } -void wxQtListTreeWidget::itemActivated(QTreeWidgetItem *qitem, int column) +void wxQtListTreeWidget::itemActivated(const QModelIndex &index) { - EmitListEvent(wxEVT_LIST_ITEM_ACTIVATED, qitem, column); + EmitListEvent(wxEVT_LIST_ITEM_ACTIVATED, index); } @@ -165,18 +1032,31 @@ bool wxListCtrl::Create(wxWindow *parent, const wxValidator& validator, const wxString& name) { - m_qtTreeWidget = new wxQtListTreeWidget( parent, this ); + m_model = style & wxLC_VIRTUAL + ? new wxQtVirtualListModel(this) + : new wxQtListModel(this); + + m_qtTreeWidget = new wxQtListTreeWidget(parent, this); + m_qtTreeWidget->setModel(m_model); + m_model->SetView(m_qtTreeWidget); if (style & wxLC_NO_HEADER) m_qtTreeWidget->setHeaderHidden(true); m_qtTreeWidget->setRootIsDecorated(false); + m_qtTreeWidget->setSelectionBehavior(QAbstractItemView::SelectRows); - return QtCreateControl( parent, id, pos, size, style, validator, name ); + if ( !QtCreateControl(parent, id, pos, size, style, validator, name) ) + return false; + + SetWindowStyleFlag(style); + return true; } void wxListCtrl::Init() { + m_hasCheckBoxes = false; + m_model = NULL; m_imageListNormal = NULL; m_ownsImageListNormal = false; m_imageListSmall = NULL; @@ -194,6 +1074,13 @@ wxListCtrl::~wxListCtrl() delete m_imageListSmall; if ( m_ownsImageListState ) delete m_imageListState; + + m_imageListNormal = NULL; + m_imageListSmall = NULL; + m_imageListState = NULL; + + m_qtTreeWidget->setModel(NULL); + m_model->deleteLater(); } bool wxListCtrl::SetForegroundColour(const wxColour& col) @@ -208,21 +1095,17 @@ bool wxListCtrl::SetBackgroundColour(const wxColour& col) bool wxListCtrl::GetColumn(int col, wxListItem& info) const { - QTreeWidgetItem *qitem = m_qtTreeWidget->headerItem(); - if ( qitem != NULL ) - { - info.SetText(wxQtConvertString(qitem->text(col))); - info.SetAlign(wxQtConvertAlignFlag(qitem->textAlignment(col))); - info.SetWidth(m_qtTreeWidget->columnWidth(col)); - return true; - } - else - return false; + return m_model->GetColumn(col, info); } bool wxListCtrl::SetColumn(int col, const wxListItem& info) { - DoInsertColumn(col, info); + if ( !m_model->SetColumn(col, info) ) + return false; + + if ( info.GetMask() & wxLIST_MASK_WIDTH ) + SetColumnWidth(col, info.GetWidth()); + return true; } @@ -233,6 +1116,12 @@ int wxListCtrl::GetColumnWidth(int col) const bool wxListCtrl::SetColumnWidth(int col, int width) { + if ( width < 0 ) + { + m_qtTreeWidget->resizeColumnToContents(width); + return true; + } + m_qtTreeWidget->setColumnWidth(col, width); return true; } @@ -260,7 +1149,7 @@ bool wxListCtrl::SetColumnsOrder(const wxArrayInt& WXUNUSED(orders)) int wxListCtrl::GetCountPerPage() const { // this may not be exact but should be a good approximation: - const int h = m_qtTreeWidget->visualItemRect(m_qtTreeWidget->headerItem()).height(); + const int h = m_qtTreeWidget->visualRect(m_model->index(0, 0)).height(); if ( h ) return m_qtTreeWidget->height() / h; else @@ -282,105 +1171,14 @@ wxTextCtrl* wxListCtrl::GetEditControl() const return m_qtTreeWidget->GetEditControl(); } -QTreeWidgetItem *wxListCtrl::QtGetItem(int id) const -{ - wxCHECK_MSG( id >= 0 && id < GetItemCount(), NULL, - wxT("invalid item index in wxListCtrl") ); - QModelIndex index = m_qtTreeWidget->model()->index(id, 0); - // note that itemFromIndex(index) is protected - return (QTreeWidgetItem*)index.internalPointer(); -} - bool wxListCtrl::GetItem(wxListItem& info) const { - const long id = info.GetId(); - QTreeWidgetItem *qitem = QtGetItem(id); - if ( qitem != NULL ) - { - if ( !info.m_mask ) - // by default, get everything for backwards compatibility - info.m_mask = -1; - if ( info.m_mask & wxLIST_MASK_TEXT ) - info.SetText(wxQtConvertString(qitem->text(info.GetColumn()))); - if ( info.m_mask & wxLIST_MASK_DATA ) - { - QVariant variant = qitem->data(0, Qt::UserRole); - info.SetData(variant.value()); - } - if ( info.m_mask & wxLIST_MASK_STATE ) - { - info.m_state = wxLIST_STATE_DONTCARE; - if ( info.m_stateMask & wxLIST_STATE_FOCUSED ) - { - if ( m_qtTreeWidget->currentIndex().row() == id ) - info.m_state |= wxLIST_STATE_FOCUSED; - } - if ( info.m_stateMask & wxLIST_STATE_SELECTED ) - { - if ( qitem->isSelected() ) - info.m_state |= wxLIST_STATE_SELECTED; - } - } - return true; - } - else - return false; + return m_model->GetItem(info); } bool wxListCtrl::SetItem(wxListItem& info) { - const long id = info.GetId(); - if ( id < 0 ) - return false; - QTreeWidgetItem *qitem = QtGetItem(id); - if ( qitem != NULL ) - { - if ((info.m_mask & wxLIST_MASK_TEXT) && !info.GetText().IsNull() ) - qitem->setText(info.GetColumn(), wxQtConvertString(info.GetText())); - qitem->setTextAlignment(info.GetColumn(), wxQtConvertTextAlign(info.GetAlign())); - - if ( info.m_mask & wxLIST_MASK_DATA ) - { - QVariant variant = qVariantFromValue(info.GetData()); - qitem->setData(0, Qt::UserRole, variant); - } - if (info.m_mask & wxLIST_MASK_STATE) - { - if ((info.m_stateMask & wxLIST_STATE_FOCUSED) && - (info.m_state & wxLIST_STATE_FOCUSED)) - m_qtTreeWidget->setCurrentItem(qitem, 0); - if (info.m_stateMask & wxLIST_STATE_SELECTED) - qitem->setSelected(info.m_state & wxLIST_STATE_SELECTED); - } - if (info.m_mask & wxLIST_MASK_IMAGE) - { - if (info.m_image >= 0) - { - wxImageList *imglst = GetImageList(InReportView() ? wxIMAGE_LIST_SMALL : wxIMAGE_LIST_NORMAL); - wxCHECK_MSG(imglst, false, "invalid listctrl imagelist"); - const wxBitmap bitmap = imglst->GetBitmap(info.m_image); - // set the new image: - qitem->setIcon( info.GetColumn(), QIcon( *bitmap.GetHandle() )); - } - else - { - // remove the image using and empty qt icon: - qitem->setIcon( info.GetColumn(), QIcon() ); - } - } - for (int col=0; colsetFont(col, info.GetFont().GetHandle() ); - if ( info.GetTextColour().IsOk() ) - qitem->setTextColor(col, info.GetTextColour().GetQColor()); - if ( info.GetBackgroundColour().IsOk() ) - qitem->setBackgroundColor(col, info.GetBackgroundColour().GetQColor()); - } - return true; - } - else - return false; + return m_model->SetItem(info); } long wxListCtrl::SetItem(long index, int col, const wxString& label, int imageId) @@ -443,91 +1241,82 @@ bool wxListCtrl::SetItemColumnImage(long item, long column, int image) wxString wxListCtrl::GetItemText(long item, int col) const { - QTreeWidgetItem *qitem = QtGetItem(item); - if ( qitem ) - return wxQtConvertString( qitem->text(col) ); - else - return wxString(); + wxListItem info; + info.SetId(item); + info.m_col = col; + info.m_mask = wxLIST_MASK_TEXT; + GetItem(info); + return info.GetText(); } void wxListCtrl::SetItemText(long item, const wxString& str) { - QTreeWidgetItem *qitem = QtGetItem(item); - if ( qitem ) - qitem->setText( 0, wxQtConvertString( str ) ); + wxListItem info; + info.SetId(item); + info.m_mask = wxLIST_MASK_TEXT; + info.SetText(str); + SetItem(info); } wxUIntPtr wxListCtrl::GetItemData(long item) const { - QTreeWidgetItem *qitem = QtGetItem(item); - if ( qitem != NULL ) - { - QVariant variant = qitem->data(0, Qt::UserRole); - return variant.value(); - } - else - return 0; + wxListItem info; + info.SetId(item); + info.m_mask = wxLIST_MASK_DATA; + GetItem(info); + return info.GetData(); } bool wxListCtrl::SetItemPtrData(long item, wxUIntPtr data) { - QTreeWidgetItem *qitem = QtGetItem(item); - if ( qitem != NULL ) - { - QVariant variant = qVariantFromValue(data); - qitem->setData(0, Qt::UserRole, variant); - return true; - } - else - return false; + wxListItem info; + info.SetId(item); + info.m_mask = wxLIST_MASK_DATA; + info.SetData(data); + return SetItem(info); } bool wxListCtrl::SetItemData(long item, long data) { - QTreeWidgetItem *qitem = QtGetItem(item); - if ( qitem != NULL ) - { - QVariant variant = qVariantFromValue(data); - qitem->setData(0, Qt::UserRole, variant); - return true; - } - else - return false; + return SetItemPtrData(item, static_cast(data)); } bool wxListCtrl::GetItemRect(long item, wxRect& rect, int WXUNUSED(code)) const { - QTreeWidgetItem *qitem = QtGetItem(item); - if ( qitem != NULL ) - { - rect = wxQtConvertRect( m_qtTreeWidget->visualItemRect(qitem) ); - return true; - } - else + wxCHECK_MSG(item >= 0 && (item < GetItemCount()), false, + "invalid item in GetSubItemRect"); + + const int columnCount = m_model->columnCount(QModelIndex()); + if ( columnCount == 0 ) return false; + + // Calculate the union of the bounds of the items in the first and last + // column for the given row. + QRect first = m_qtTreeWidget->visualRect(m_model->index(item, 0)); + QRect last = m_qtTreeWidget->visualRect(m_model->index(item, columnCount-1)); + rect = wxQtConvertRect(first.united(last)); + + return true; } bool wxListCtrl::GetSubItemRect(long item, long subItem, wxRect& rect, int WXUNUSED(code)) const { - QTreeWidgetItem *qitem = QtGetItem(item); - if ( qitem != NULL ) - { - wxCHECK_MSG( item >= 0 && item < GetItemCount(), NULL, - wxT("invalid row index in GetSubItemRect") ); - wxCHECK_MSG( subItem >= 0 && subItem < GetColumnCount(), NULL, - wxT("invalid column index in GetSubItemRect") ); - QModelIndex qindex = m_qtTreeWidget->model()->index(item, subItem); - rect = wxQtConvertRect( m_qtTreeWidget->visualRect(qindex) ); - return true; - } - else - return false; + wxCHECK_MSG(item >= 0 && item < GetItemCount(), + false, "invalid row index in GetSubItemRect"); + + wxCHECK_MSG(subItem >= 0 && subItem < GetColumnCount(), + false, "invalid column index in GetSubItemRect"); + + const QModelIndex index = m_qtTreeWidget->model()->index(item, subItem); + rect = wxQtConvertRect(m_qtTreeWidget->visualRect(index)); + return true; } bool wxListCtrl::GetItemPosition(long item, wxPoint& pos) const { wxRect rect; - GetItemRect(item, rect); + if ( !GetItemRect(item, rect) ) + return false; pos.x = rect.x; pos.y = rect.y; @@ -542,12 +1331,12 @@ bool wxListCtrl::SetItemPosition(long WXUNUSED(item), const wxPoint& WXUNUSED(po int wxListCtrl::GetItemCount() const { - return m_qtTreeWidget->topLevelItemCount(); + return m_model->rowCount(QModelIndex()); } int wxListCtrl::GetColumnCount() const { - return m_qtTreeWidget->columnCount(); + return m_model->columnCount(QModelIndex()); } wxSize wxListCtrl::GetItemSpacing() const @@ -555,58 +1344,150 @@ wxSize wxListCtrl::GetItemSpacing() const return wxSize(); } -void wxListCtrl::SetItemTextColour( long WXUNUSED(item), const wxColour& WXUNUSED(col)) +void wxListCtrl::SetItemTextColour(long item, const wxColour& col) { + const int columnCount = m_model->columnCount(QModelIndex()); + + wxListItem listItem; + listItem.SetId(item); + listItem.SetTextColour(col); + + for ( int i = 0; i < columnCount; ++i ) + { + listItem.m_col = i; + SetItem(listItem); + } } -wxColour wxListCtrl::GetItemTextColour( long WXUNUSED(item) ) const +wxColour wxListCtrl::GetItemTextColour( long item ) const { - return wxColour(); + return m_model->GetItemTextColour(item); } -void wxListCtrl::SetItemBackgroundColour( long WXUNUSED(item), const wxColour &WXUNUSED(col)) +void wxListCtrl::SetItemBackgroundColour( long item, const wxColour &col) { + wxListItem listItem; + listItem.SetId(item); + listItem.SetBackgroundColour(col); + + const int columnCount = m_model->columnCount(QModelIndex()); + + for ( int i = 0; i < columnCount; ++i ) + { + listItem.m_col = i; + SetItem(listItem); + } } -wxColour wxListCtrl::GetItemBackgroundColour( long WXUNUSED(item) ) const +wxColour wxListCtrl::GetItemBackgroundColour( long item ) const { - return wxColour(); + return m_model->GetItemBackgroundColour(item); } -void wxListCtrl::SetItemFont( long WXUNUSED(item), const wxFont &WXUNUSED(f)) +void wxListCtrl::SetItemFont( long item, const wxFont &f) { + const int columnCount = m_model->columnCount(QModelIndex()); + + wxListItem listItem; + listItem.SetId(item); + listItem.SetFont(f); + + for ( int i = 0; i < columnCount; ++i ) + { + listItem.m_col = i; + SetItem(listItem); + } } -wxFont wxListCtrl::GetItemFont( long WXUNUSED(item) ) const +wxFont wxListCtrl::GetItemFont( long item ) const { - return wxFont(); + return m_model->GetItemFont(item); } int wxListCtrl::GetSelectedItemCount() const { - return m_qtTreeWidget->selectedItems().length(); + QItemSelectionModel *selectionModel = m_qtTreeWidget->selectionModel(); + QModelIndexList selectedRows = selectionModel->selectedRows(); + return selectedRows.length(); } wxColour wxListCtrl::GetTextColour() const { - return wxColour(); + const QPalette palette = m_qtTreeWidget->palette(); + const QColor color = palette.color(QPalette::WindowText); + return wxColour(color); } -void wxListCtrl::SetTextColour(const wxColour& WXUNUSED(col)) +void wxListCtrl::SetTextColour(const wxColour& col) { + QPalette palette = m_qtTreeWidget->palette(); + palette.setColor(QPalette::WindowText, col.GetQColor()); + m_qtTreeWidget->setPalette(palette); } long wxListCtrl::GetTopItem() const { + const long itemCount = GetItemCount(); + for ( long i = 0; i < itemCount; ++i ) + { + wxRect itemRect; + GetItemRect(i, itemRect); + if ( itemRect.GetY() >= 0 ) + return i; + } return 0; } +bool wxListCtrl::HasCheckBoxes() const +{ + return m_hasCheckBoxes; +} + +bool wxListCtrl::EnableCheckBoxes(bool enable /*= true*/) +{ + m_hasCheckBoxes = enable; + QVector roles; + roles.push_back(Qt::CheckStateRole); + m_model->dataChanged(m_model->index(0, 0), + m_model->index(GetItemCount() - 1, GetColumnCount() - 1), + roles); + return true; +} + +bool wxListCtrl::IsItemChecked(long item) const +{ + return m_model->IsItemChecked(item); +} + +void wxListCtrl::CheckItem(long item, bool check) +{ + m_model->CheckItem(item, check); +} + void wxListCtrl::SetSingleStyle(long WXUNUSED(style), bool WXUNUSED(add)) { } -void wxListCtrl::SetWindowStyleFlag(long WXUNUSED(style)) +void wxListCtrl::SetWindowStyleFlag(long style) { + m_windowStyle = style; + m_qtTreeWidget->setHeaderHidden((style & wxLC_NO_HEADER) != 0); + m_qtTreeWidget->setSelectionMode((style & wxLC_SINGLE_SEL) != 0 + ? QAbstractItemView::SingleSelection + : QAbstractItemView::ExtendedSelection + ); + const bool needVirtual = (style & wxLC_VIRTUAL) != 0; + + if ( needVirtual != m_model->IsVirtual() ) + { + wxQtListModel *oldModel = m_model; + m_model = needVirtual + ? new wxQtVirtualListModel(this) + : new wxQtListModel(this); + m_model->SetView(m_qtTreeWidget); + m_qtTreeWidget->setModel(m_model); + delete oldModel; + } } long wxListCtrl::GetNextItem(long item, int WXUNUSED(geometry), int state) const @@ -614,8 +1495,8 @@ long wxListCtrl::GetNextItem(long item, int WXUNUSED(geometry), int state) const wxListItem info; long ret = item; const long max = GetItemCount(); - wxCHECK_MSG( (ret == -1) || (ret < max), -1, - wxT("invalid listctrl index in GetNextItem()") ); + wxCHECK_MSG((ret >= -1) || (ret < max), -1, + "invalid listctrl index in GetNextItem()"); // notice that we start with the next item (or the first one if item == -1) // and this is intentional to allow writing a simple loop to iterate over @@ -626,9 +1507,8 @@ long wxListCtrl::GetNextItem(long item, int WXUNUSED(geometry), int state) const // just no such item return -1; - if ( !state ) - // any will do - return (size_t)ret; + if ( state == wxLIST_STATE_DONTCARE ) + return ret; for ( long line = ret; line < max; line++ ) { @@ -693,12 +1573,17 @@ void wxListCtrl::AssignImageList(wxImageList *imageList, int which) m_ownsImageListState = true; } -void wxListCtrl::RefreshItem(long WXUNUSED(item)) +void wxListCtrl::RefreshItem(long item) { + RefreshItems(item, item); } -void wxListCtrl::RefreshItems(long WXUNUSED(itemFrom), long WXUNUSED(itemTo)) +void wxListCtrl::RefreshItems(long itemFrom, long itemTo) { + const int columnCount = GetColumnCount(); + const QModelIndex start = m_model->index(itemFrom, 0); + const QModelIndex end = m_model->index(itemTo, columnCount - 1); + m_model->dataChanged(start, end); } bool wxListCtrl::Arrange(int WXUNUSED(flag)) @@ -708,115 +1593,124 @@ bool wxListCtrl::Arrange(int WXUNUSED(flag)) bool wxListCtrl::DeleteItem(long item) { - QTreeWidgetItem *qitem = QtGetItem(item); - if ( qitem != NULL ) - { - delete qitem; - return true; - } - else + if ( item < 0 || item >= GetItemCount() ) return false; + + m_model->removeRow(item, QModelIndex()); + + wxListItem listItem; + listItem.SetId(item); + + wxListEvent event(wxEVT_LIST_DELETE_ITEM, GetId()); + event.SetEventObject(this); + event.SetItem(listItem); + + HandleWindowEvent(event); + + return true; } bool wxListCtrl::DeleteAllItems() { - m_qtTreeWidget->clear(); + if ( GetItemCount() == 0 ) + return true; + + m_model->removeRows(0, GetItemCount(), QModelIndex()); + + wxListEvent event(wxEVT_LIST_DELETE_ALL_ITEMS, GetId()); + event.SetEventObject(this); + HandleWindowEvent(event); + return true; } bool wxListCtrl::DeleteColumn(int col) { - // Qt cannot easily add or remove columns, so only the last one can be deleted - if ( col == GetColumnCount() - 1 ) - { - m_qtTreeWidget->setColumnCount(col); - return true; - } - else + if ( col < 0 || col >= m_model->columnCount(QModelIndex()) ) return false; + + m_model->removeColumn(0, QModelIndex()); + return true; } bool wxListCtrl::DeleteAllColumns() { - m_qtTreeWidget->setColumnCount(0); + m_model->removeColumns(0, m_model->columnCount(QModelIndex()), QModelIndex()); return true; } void wxListCtrl::ClearAll() { - m_qtTreeWidget->clear(); + DeleteAllColumns(); + DeleteAllItems(); } wxTextCtrl* wxListCtrl::EditLabel(long item, wxClassInfo* WXUNUSED(textControlClass)) { - QTreeWidgetItem *qitem = QtGetItem(item); - if ( qitem != NULL ) - { - m_qtTreeWidget->openPersistentEditor(qitem); - } - return NULL; + // Open the editor first so that it's available when handling events as per wx standard + const QModelIndex index = m_model->index(item, 0); + m_qtTreeWidget->openPersistentEditor(index); + + wxListItem listItem; + listItem.SetId(index.row()); + listItem.SetColumn(index.column()); + + GetItem(listItem); + + wxListEvent event(wxEVT_LIST_BEGIN_LABEL_EDIT, GetId()); + event.SetEventObject(this); + event.SetItem(listItem); + + // close the editor again if event is vetoed + if (HandleWindowEvent(event) && !event.IsAllowed()) + m_qtTreeWidget->closePersistentEditor(index); + + return m_qtTreeWidget->GetEditControl(); } bool wxListCtrl::EndEditLabel(bool WXUNUSED(cancel)) { const int item = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_FOCUSED); - if (item > 0) - { - QTreeWidgetItem *qitem = QtGetItem(item); - if ( qitem != NULL ) - { - m_qtTreeWidget->closePersistentEditor(qitem); - return true; - } - } - return false; + if ( item < 0 ) + return false; + + m_qtTreeWidget->closePersistentEditor(m_model->index(item, 0)); + return true; } bool wxListCtrl::EnsureVisible(long item) { - QTreeWidgetItem *qitem = QtGetItem(item); - if ( qitem != NULL ) - { - m_qtTreeWidget->scrollToItem(qitem); - return true; - } - else + if ( item < 0 || item >= GetItemCount() ) return false; + + m_qtTreeWidget->scrollTo(m_model->index(item, 0)); + return true; } long wxListCtrl::FindItem(long start, const wxString& str, bool partial) { - QList qitems = m_qtTreeWidget->findItems( - wxQtConvertString(str), - !partial ? Qt::MatchExactly : Qt::MatchContains ); - for (int i=0; iindexOfTopLevelItem(qitems.at(i)); - if ( ret >= start ) - return ret; - } - return -1; + return m_model->FindItem(start, wxQtConvertString(str), partial); } long wxListCtrl::FindItem(long start, wxUIntPtr data) { - QVariant variant = qVariantFromValue(data); - // search only one hit (if any): - QModelIndexList qindexes = m_qtTreeWidget->model()->match( - m_qtTreeWidget->model()->index(start, 0), - Qt::UserRole, variant, 1 ); - if (qindexes.isEmpty()) - return -1; - return qindexes.at(0).row(); + return m_model->FindItem(start, data); } -long wxListCtrl::FindItem(long WXUNUSED(start), const wxPoint& WXUNUSED(pt), int WXUNUSED(direction)) +long wxListCtrl::FindItem( + long WXUNUSED(start), + const wxPoint& WXUNUSED(pt), + int WXUNUSED(direction) +) { return -1; } -long wxListCtrl::HitTest(const wxPoint& point, int &flags, long* ptrSubItem) const +long wxListCtrl::HitTest( + const wxPoint& point, + int &flags, + long* ptrSubItem +) const { QModelIndex index = m_qtTreeWidget->indexAt(wxQtConvertPoint(point)); if ( index.isValid() ) @@ -836,24 +1730,16 @@ long wxListCtrl::HitTest(const wxPoint& point, int &flags, long* ptrSubItem) con long wxListCtrl::InsertItem(const wxListItem& info) { - // default return value if not successful: - int index = -1; - wxASSERT_MSG( info.m_itemId != -1, wxS("Item ID must be set.") ); - QTreeWidgetItem *qitem = new QTreeWidgetItem(); - if ( qitem != NULL ) - { - // insert at the correct index and return it: - m_qtTreeWidget->insertTopLevelItem(info.GetId(), qitem); - // return the correct position of the item or -1 if not found: - index = m_qtTreeWidget->indexOfTopLevelItem(qitem); - if ( index != -1 ) - { - // temporarily copy the item info (we need a non-const instance) - wxListItem tmp = info; - // set the text, image, etc.: - SetItem(tmp); - } - } + const long index = m_model->InsertItem(info); + + wxListItem tmp = info; + tmp.SetId(index); + + wxListEvent event(wxEVT_LIST_INSERT_ITEM, GetId()); + event.SetItem(tmp); + event.SetEventObject(this); + HandleWindowEvent(event); + return index; } @@ -887,22 +1773,14 @@ long wxListCtrl::InsertItem(long index, const wxString& label, int imageIndex) long wxListCtrl::DoInsertColumn(long col, const wxListItem& info) { - QTreeWidgetItem *qitem = m_qtTreeWidget->headerItem(); - if ( qitem != NULL ) - { - qitem->setText(col, wxQtConvertString(info.GetText())); - qitem->setTextAlignment(col, wxQtConvertTextAlign(info.GetAlign())); - if (info.GetWidth()) - m_qtTreeWidget->setColumnWidth(col, info.GetWidth()); - return col; - } - else - return -1; + return m_model->InsertColumn(col, info); } -void wxListCtrl::SetItemCount(long WXUNUSED(count)) +void wxListCtrl::SetItemCount(long count) { + wxASSERT(HasFlag(wxLC_VIRTUAL)); + m_model->SetVirtualItemCount(count); } bool wxListCtrl::ScrollList(int dx, int dy) @@ -912,9 +1790,10 @@ bool wxListCtrl::ScrollList(int dx, int dy) return true; } -bool wxListCtrl::SortItems(wxListCtrlCompare WXUNUSED(fn), wxIntPtr WXUNUSED(data)) +bool wxListCtrl::SortItems(wxListCtrlCompare fn, wxIntPtr data) { - return false; + m_model->SortItems(fn, data); + return true; } QWidget *wxListCtrl::GetHandle() const