///////////////////////////////////////////////////////////////////////////// // Name: src/qt/listctrl.cpp // Author: Mariano Reingart, Peter Most // Copyright: (c) 2010 wxWidgets dev team // Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// // For compilers that support precompilation, includes "wx.h". #include "wx/wxprec.h" #ifdef __BORLANDC__ #pragma hdrstop #endif #include #ifndef WX_PRECOMP #include "wx/bitmap.h" #endif // WX_PRECOMP #include "wx/listctrl.h" #include "wx/imaglist.h" #include "wx/qt/private/winevent.h" class wxQtTreeWidget : public wxQtEventSignalHandler< QTreeWidget, wxListCtrl > { public: wxQtTreeWidget( wxWindow *parent, wxListCtrl *handler ); void EmitListEvent(wxEventType typ, QTreeWidgetItem *qitem, int column) const; private: void itemClicked(QTreeWidgetItem * item, int column); void itemActivated(QTreeWidgetItem * item, int column); void itemPressed(QTreeWidgetItem * item, int column); }; wxQtTreeWidget::wxQtTreeWidget( wxWindow *parent, wxListCtrl *handler ) : wxQtEventSignalHandler< QTreeWidget, wxListCtrl >( parent, handler ) { connect(this, &QTreeWidget::itemClicked, this, &wxQtTreeWidget::itemClicked); connect(this, &QTreeWidget::itemPressed, this, &wxQtTreeWidget::itemPressed); connect(this, &QTreeWidget::itemActivated, this, &wxQtTreeWidget::itemActivated); } void wxQtTreeWidget::EmitListEvent(wxEventType typ, QTreeWidgetItem *qitem, int column) const { wxListCtrl *handler = GetHandler(); if ( handler ) { // prepare the event // ----------------- wxListEvent event; event.SetEventType(typ); event.SetId(handler->GetId()); event.m_itemIndex = this->indexFromItem(qitem, column).row(); event.m_item.SetId(event.m_itemIndex); event.m_item.SetMask(wxLIST_MASK_TEXT | wxLIST_MASK_IMAGE | wxLIST_MASK_DATA); handler->GetItem(event.m_item); EmitEvent(event); } } void wxQtTreeWidget::itemClicked(QTreeWidgetItem *qitem, int column) { EmitListEvent(wxEVT_LIST_ITEM_SELECTED, qitem, column); } void wxQtTreeWidget::itemPressed(QTreeWidgetItem *qitem, int column) { EmitListEvent(wxEVT_LIST_ITEM_SELECTED, qitem, column); } void wxQtTreeWidget::itemActivated(QTreeWidgetItem *qitem, int column) { EmitListEvent(wxEVT_LIST_ITEM_ACTIVATED, qitem, column); } Qt::AlignmentFlag wxQtConvertTextAlign(wxListColumnFormat align) { switch (align) { case wxLIST_FORMAT_LEFT: return Qt::AlignLeft; case wxLIST_FORMAT_RIGHT: return Qt::AlignRight; case wxLIST_FORMAT_CENTRE: return Qt::AlignCenter; } return Qt::AlignLeft; } wxListColumnFormat wxQtConvertAlignFlag(int align) { switch (align) { case Qt::AlignLeft: return wxLIST_FORMAT_LEFT; case Qt::AlignRight: return wxLIST_FORMAT_RIGHT; case Qt::AlignCenter: return wxLIST_FORMAT_CENTRE; } return wxLIST_FORMAT_LEFT; } wxListCtrl::wxListCtrl() { Init(); } wxListCtrl::wxListCtrl(wxWindow *parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style, const wxValidator& validator, const wxString& name) { Init(); Create( parent, id, pos, size, style, validator, name ); } bool wxListCtrl::Create(wxWindow *parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style, const wxValidator& validator, const wxString& name) { m_qtTreeWidget = new wxQtTreeWidget( parent, this ); if (style & wxLC_NO_HEADER) m_qtTreeWidget->setHeaderHidden(true); m_qtTreeWidget->setRootIsDecorated(false); return QtCreateControl( parent, id, pos, size, style, validator, name ); } void wxListCtrl::Init() { m_imageListNormal = NULL; m_ownsImageListNormal = false; m_imageListSmall = NULL; m_ownsImageListSmall = false; m_imageListState = NULL; m_ownsImageListState = false; } wxListCtrl::~wxListCtrl() { if (m_ownsImageListNormal) delete m_imageListNormal; if (m_ownsImageListSmall) delete m_imageListSmall; if (m_ownsImageListState) delete m_imageListState; } bool wxListCtrl::SetForegroundColour(const wxColour& col) { return wxListCtrlBase::SetForegroundColour(col); } bool wxListCtrl::SetBackgroundColour(const wxColour& col) { return wxListCtrlBase::SetBackgroundColour(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; } bool wxListCtrl::SetColumn(int col, const wxListItem& info) { DoInsertColumn(col, info); return true; } int wxListCtrl::GetColumnWidth(int col) const { return m_qtTreeWidget->columnWidth(col); } bool wxListCtrl::SetColumnWidth(int col, int width) { m_qtTreeWidget->setColumnWidth(col, width); return true; } int wxListCtrl::GetColumnOrder(int col) const { return col; } int wxListCtrl::GetColumnIndexFromOrder(int order) const { return order; } wxArrayInt wxListCtrl::GetColumnsOrder() const { return wxArrayInt(); } bool wxListCtrl::SetColumnsOrder(const wxArrayInt& WXUNUSED(orders)) { return false; } int wxListCtrl::GetCountPerPage() const { // this may not be exact but should be a good aproximation: int h = m_qtTreeWidget->visualItemRect(m_qtTreeWidget->headerItem()).height(); if ( h ) return m_qtTreeWidget->height() / h; else return 0; } wxRect wxListCtrl::GetViewRect() const { // this may not be exact but should be a good aproximation: wxRect rect = wxQtConvertRect(m_qtTreeWidget->rect()); int h = m_qtTreeWidget->header()->defaultSectionSize(); rect.SetTop(h); rect.SetHeight(rect.GetHeight() - h); return rect; } wxTextCtrl* wxListCtrl::GetEditControl() const { return NULL; } 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; } bool wxListCtrl::SetItem(wxListItem& info) { const long id = info.GetId(); 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->GetBitmapPtr(info.m_image); if (bitmap != NULL) { // 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; } long wxListCtrl::SetItem(long index, int col, const wxString& label, int imageId) { wxListItem info; info.m_text = label; info.m_mask = wxLIST_MASK_TEXT; info.m_itemId = index; info.m_col = col; if ( imageId > -1 ) { info.m_image = imageId; info.m_mask |= wxLIST_MASK_IMAGE; } return SetItem(info); } int wxListCtrl::GetItemState(long item, long stateMask) const { wxListItem info; info.m_mask = wxLIST_MASK_STATE; info.m_stateMask = stateMask; info.m_itemId = item; if (!GetItem(info)) return 0; return info.m_state; } bool wxListCtrl::SetItemState(long item, long state, long stateMask) { wxListItem info; info.m_mask = wxLIST_MASK_STATE; info.m_stateMask = stateMask; info.m_state = state; info.m_itemId = item; return SetItem(info); } bool wxListCtrl::SetItemImage(long item, int image, int WXUNUSED(selImage)) { return SetItemColumnImage(item, 0, image); } bool wxListCtrl::SetItemColumnImage(long item, long column, int image) { wxListItem info; info.m_mask = wxLIST_MASK_IMAGE; info.m_image = image; info.m_itemId = item; info.m_col = column; return SetItem(info); } wxString wxListCtrl::GetItemText(long item, int WXUNUSED(col)) const { QTreeWidgetItem *qitem = QtGetItem(item); if ( qitem ) return wxQtConvertString( qitem->text(0) ); else return wxString(); } void wxListCtrl::SetItemText(long item, const wxString& str) { QTreeWidgetItem *qitem = QtGetItem(item); if ( qitem ) qitem->setText( 0, wxQtConvertString( str ) ); } 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; } 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; } 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; } 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 return false; } 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; } bool wxListCtrl::GetItemPosition(long item, wxPoint& pos) const { wxRect rect; GetItemRect(item, rect); pos.x = rect.x; pos.y = rect.y; return true; } bool wxListCtrl::SetItemPosition(long WXUNUSED(item), const wxPoint& WXUNUSED(pos)) { return false; } int wxListCtrl::GetItemCount() const { return m_qtTreeWidget->topLevelItemCount(); } int wxListCtrl::GetColumnCount() const { return m_qtTreeWidget->columnCount(); } wxSize wxListCtrl::GetItemSpacing() const { return wxSize(); } void wxListCtrl::SetItemTextColour( long WXUNUSED(item), const wxColour& WXUNUSED(col)) { } wxColour wxListCtrl::GetItemTextColour( long WXUNUSED(item) ) const { return wxColour(); } void wxListCtrl::SetItemBackgroundColour( long WXUNUSED(item), const wxColour &WXUNUSED(col)) { } wxColour wxListCtrl::GetItemBackgroundColour( long WXUNUSED(item) ) const { return wxColour(); } void wxListCtrl::SetItemFont( long WXUNUSED(item), const wxFont &WXUNUSED(f)) { } wxFont wxListCtrl::GetItemFont( long WXUNUSED(item) ) const { return wxFont(); } int wxListCtrl::GetSelectedItemCount() const { return m_qtTreeWidget->selectedItems().length(); } wxColour wxListCtrl::GetTextColour() const { return wxColour(); } void wxListCtrl::SetTextColour(const wxColour& WXUNUSED(col)) { } long wxListCtrl::GetTopItem() const { return 0; } void wxListCtrl::SetSingleStyle(long WXUNUSED(style), bool WXUNUSED(add)) { } void wxListCtrl::SetWindowStyleFlag(long WXUNUSED(style)) { } long wxListCtrl::GetNextItem(long item, int WXUNUSED(geometry), int state) const { wxListItem info; long ret = item, max = GetItemCount(); wxCHECK_MSG( (ret == -1) || (ret < max), -1, wxT("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 // all selected items ret++; if ( ret == max ) // this is not an error because the index was OK initially, // just no such item return -1; if ( !state ) // any will do return (size_t)ret; size_t count = GetItemCount(); for ( size_t line = (size_t)ret; line < count; line++ ) { if ( GetItemState(line, state) ) return line; } return -1; } wxImageList *wxListCtrl::GetImageList(int which) const { if ( which == wxIMAGE_LIST_NORMAL ) { return m_imageListNormal; } else if ( which == wxIMAGE_LIST_SMALL ) { return m_imageListSmall; } else if ( which == wxIMAGE_LIST_STATE ) { return m_imageListState; } return NULL; } void wxListCtrl::SetImageList(wxImageList *imageList, int which) { if ( which == wxIMAGE_LIST_NORMAL ) { if (m_ownsImageListNormal) delete m_imageListNormal; m_imageListNormal = imageList; m_ownsImageListNormal = false; } else if ( which == wxIMAGE_LIST_SMALL ) { if (m_ownsImageListSmall) delete m_imageListSmall; m_imageListSmall = imageList; m_ownsImageListSmall = false; } else if ( which == wxIMAGE_LIST_STATE ) { if (m_ownsImageListState) delete m_imageListState; m_imageListState = imageList; m_ownsImageListState = false; } } void wxListCtrl::AssignImageList(wxImageList *imageList, int which) { SetImageList(imageList, which); if ( which == wxIMAGE_LIST_NORMAL ) m_ownsImageListNormal = true; else if ( which == wxIMAGE_LIST_SMALL ) m_ownsImageListSmall = true; else if ( which == wxIMAGE_LIST_STATE ) m_ownsImageListState = true; } void wxListCtrl::RefreshItem(long WXUNUSED(item)) { } void wxListCtrl::RefreshItems(long WXUNUSED(itemFrom), long WXUNUSED(itemTo)) { } bool wxListCtrl::Arrange(int WXUNUSED(flag)) { return false; } bool wxListCtrl::DeleteItem(long item) { QTreeWidgetItem *qitem = QtGetItem(item); if ( qitem != NULL ) { delete qitem; return true; } else return false; } bool wxListCtrl::DeleteAllItems() { m_qtTreeWidget->clear(); 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 return false; } bool wxListCtrl::DeleteAllColumns() { m_qtTreeWidget->setColumnCount(0); return true; } void wxListCtrl::ClearAll() { m_qtTreeWidget->clear(); } wxTextCtrl* wxListCtrl::EditLabel(long item, wxClassInfo* WXUNUSED(textControlClass)) { QTreeWidgetItem *qitem = QtGetItem(item); if ( qitem != NULL ) { m_qtTreeWidget->openPersistentEditor(qitem); } return NULL; } bool wxListCtrl::EndEditLabel(bool WXUNUSED(cancel)) { 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; } bool wxListCtrl::EnsureVisible(long item) { QTreeWidgetItem *qitem = QtGetItem(item); if ( qitem != NULL ) { m_qtTreeWidget->scrollToItem(qitem); return true; } else return false; } long wxListCtrl::FindItem(long start, const wxString& str, bool partial) { int ret; QList qitems = m_qtTreeWidget->findItems( wxQtConvertString(str), !partial ? Qt::MatchExactly : Qt::MatchContains ); for (int i=0; iindexOfTopLevelItem(qitems.at(0)); if ( ret >= start ) return ret; } return -1; } 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(); } 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 { QModelIndex index = m_qtTreeWidget->indexAt(wxQtConvertPoint(point)); if ( index.isValid() ) { flags = wxLIST_HITTEST_ONITEM; if (ptrSubItem) *ptrSubItem = index.column(); } else { flags = wxLIST_HITTEST_NOWHERE; if (ptrSubItem) *ptrSubItem = 0; } return index.row(); } 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); } } return index; } long wxListCtrl::InsertItem(long index, const wxString& label) { wxListItem info; info.m_text = label; info.m_mask = wxLIST_MASK_TEXT; info.m_itemId = index; return InsertItem(info); } long wxListCtrl::InsertItem(long index, int imageIndex) { wxListItem info; info.m_image = imageIndex; info.m_mask = wxLIST_MASK_IMAGE; info.m_itemId = index; return InsertItem(info); } long wxListCtrl::InsertItem(long index, const wxString& label, int imageIndex) { wxListItem info; //info.m_image = imageIndex == -1 ? I_IMAGENONE : imageIndex; info.m_text = label; info.m_mask = wxLIST_MASK_TEXT | wxLIST_MASK_IMAGE; info.m_image = imageIndex; info.m_itemId = index; return InsertItem(info); } 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; } void wxListCtrl::SetItemCount(long WXUNUSED(count)) { } bool wxListCtrl::ScrollList(int dx, int dy) { // aproximate, as scrollContentsBy is protected m_qtTreeWidget->scroll(dx, dy); return true; } bool wxListCtrl::SortItems(wxListCtrlCompare WXUNUSED(fn), wxIntPtr WXUNUSED(data)) { return false; } // ---------------------------------------------------------------------------- // virtual list controls (not currently implemented in wxQT) // ---------------------------------------------------------------------------- wxString wxListCtrl::OnGetItemText(long WXUNUSED(item), long WXUNUSED(col)) const { // this is a pure virtual function, in fact - which is not really pure // because the controls which are not virtual don't need to implement it wxFAIL_MSG( wxT("wxListCtrl::OnGetItemText not supposed to be called") ); return wxEmptyString; } int wxListCtrl::OnGetItemImage(long WXUNUSED(item)) const { wxCHECK_MSG(!GetImageList(wxIMAGE_LIST_SMALL), -1, wxT("List control has an image list, OnGetItemImage or OnGetItemColumnImage should be overridden.")); return -1; } int wxListCtrl::OnGetItemColumnImage(long item, long column) const { if (!column) return OnGetItemImage(item); return -1; } QTreeWidget *wxListCtrl::GetHandle() const { return m_qtTreeWidget; }