///////////////////////////////////////////////////////////////////////////// // Name: src/qt/treectrl.cpp // Author: Peter Most // Copyright: (c) Peter Most // Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// // For compilers that support precompilation, includes "wx.h". #include "wx/wxprec.h" #ifdef __BORLANDC__ #pragma hdrstop #endif #include "wx/treectrl.h" #include "wx/imaglist.h" #include "wx/settings.h" #include "wx/sharedptr.h" #include "wx/withimages.h" #include "wx/qt/private/winevent.h" #include "wx/qt/private/treeitemdelegate.h" #include #include #include namespace { struct TreeItemDataQt { TreeItemDataQt() { } explicit TreeItemDataQt(wxTreeItemData* data) : data(data) { static bool s_registered = false; if ( !s_registered ) { qRegisterMetaTypeStreamOperators("TreeItemDataQt"); s_registered = true; } } wxTreeItemData *getData() const { return data.get(); } private: wxSharedPtr data; }; QDataStream &operator<<(QDataStream &out, const TreeItemDataQt &WXUNUSED(obj)) { return out; } QDataStream &operator>>(QDataStream &in, TreeItemDataQt &WXUNUSED(obj)) { return in; } QTreeWidgetItem *wxQtConvertTreeItem(const wxTreeItemId &item) { return static_cast(item.GetID()); } wxTreeItemId wxQtConvertTreeItem(QTreeWidgetItem *item) { return wxTreeItemId(item); } size_t CountChildren(QTreeWidgetItem *item) { const int currentCount = item->childCount(); size_t totalCount = currentCount; for ( int i = 0; i < currentCount; ++i ) { totalCount += CountChildren(item->child(i)); } return totalCount; } class ImageState { public: ImageState() : m_state(wxTREE_ITEMSTATE_NONE) { for ( int i = wxTreeItemIcon_Normal; i < wxTreeItemIcon_Max; ++i ) { m_imageStates[i] = wxWithImages::NO_IMAGE; } } int &operator[](size_t index) { wxASSERT(index < wxTreeItemIcon_Max); return m_imageStates[index]; } int operator[](size_t index) const { wxASSERT(index < wxTreeItemIcon_Max); return m_imageStates[index]; } void SetState(int state) { m_state = state; } int GetState() const { return m_state; } private: int m_imageStates[wxTreeItemIcon_Max]; int m_state; }; } Q_DECLARE_METATYPE(TreeItemDataQt) class wxQTreeWidget : public wxQtEventSignalHandler { public: wxQTreeWidget(wxWindow *parent, wxTreeCtrl *handler) : wxQtEventSignalHandler(parent, handler), m_item_delegate(handler), m_closing_editor(0) { connect(this, &QTreeWidget::currentItemChanged, this, &wxQTreeWidget::OnCurrentItemChanged); connect(this, &QTreeWidget::itemActivated, this, &wxQTreeWidget::OnItemActivated); connect(this, &QTreeWidget::itemCollapsed, this, &wxQTreeWidget::OnItemCollapsed); connect(this, &QTreeWidget::itemExpanded, this, &wxQTreeWidget::OnItemExpanded); setItemDelegate(&m_item_delegate); setDragEnabled(true); viewport()->setAcceptDrops(true); setDropIndicatorShown(true); setEditTriggers(QAbstractItemView::SelectedClicked); } virtual void paintEvent (QPaintEvent * event) { //QT generates warnings if we try to paint to a QTreeWidget //(perhaps because it's a compound widget) so we've disabled //wx paint and erase background events QTreeWidget::paintEvent(event); } virtual void mouseReleaseEvent(QMouseEvent * event) wxOVERRIDE { const QPoint qPos = event->pos(); QTreeWidgetItem *item = itemAt(qPos); if ( item != NULL ) { const wxPoint pos(qPos.x(), qPos.y()); switch ( event->button() ) { case Qt::RightButton: { wxTreeEvent treeEvent(wxEVT_TREE_ITEM_RIGHT_CLICK, GetHandler(), wxQtConvertTreeItem(item)); treeEvent.SetPoint(pos); EmitEvent(treeEvent); wxTreeEvent menuEvent(wxEVT_TREE_ITEM_MENU, GetHandler(), wxQtConvertTreeItem(item)); menuEvent.SetPoint(pos); EmitEvent(menuEvent); break; } case Qt::MiddleButton: { wxTreeEvent treeEvent(wxEVT_TREE_ITEM_MIDDLE_CLICK, GetHandler(), wxQtConvertTreeItem(item)); treeEvent.SetPoint(pos); EmitEvent(treeEvent); break; } default: break; } } return wxQtEventSignalHandler::mouseReleaseEvent(event); } wxTextCtrl *GetEditControl() { return m_item_delegate.GetEditControl(); } void SetItemImage(QTreeWidgetItem *item, int image, wxTreeItemIcon which) { m_imageStates[item][which] = image; } int GetItemImage(QTreeWidgetItem *item, wxTreeItemIcon which) { if ( m_imageStates.find(item) == m_imageStates.end() ) return 0; return m_imageStates[item][which]; } void SetItemState(QTreeWidgetItem *item, int state) { m_imageStates[item].SetState(state); } int GetItemState(QTreeWidgetItem *item) const { const ImageStateMap::const_iterator i = m_imageStates.find(item); if ( i == m_imageStates.end() ) return wxTREE_ITEMSTATE_NONE; return i->second.GetState(); } void ResizeIcons(const QSize &size) { m_placeHolderImage = QPixmap(size); m_placeHolderImage.fill(Qt::transparent); ReplaceIcons(invisibleRootItem()); } QPixmap GetPlaceHolderImage() const { return m_placeHolderImage; } void select(QTreeWidgetItem* item, QItemSelectionModel::SelectionFlag selectionFlag) { const QModelIndex &index = indexFromItem(item); selectionModel()->select(index, selectionFlag); } protected: virtual void drawRow( QPainter *painter, const QStyleOptionViewItem &options, const QModelIndex &index ) const wxOVERRIDE { QTreeWidget::drawRow(painter, options, index); QTreeWidgetItem *item = itemFromIndex(index); const int imageIndex = ChooseBestImage(item); if ( imageIndex != -1 ) { const wxImageList *imageList = GetHandler()->GetImageList(); const wxBitmap bitmap = imageList->GetBitmap(imageIndex); const QRect rect = visualRect(index); const int offset = (rect.height() / 2) - (bitmap.GetHeight() / 2); painter->drawPixmap(rect.topLeft() + QPoint(0,offset), *bitmap.GetHandle()); } } bool edit(const QModelIndex &index, EditTrigger trigger, QEvent *event) wxOVERRIDE { // AllEditTriggers means that editor is about to open, not waiting for double click if (trigger == AllEditTriggers) { // Allow event handlers to veto opening the editor wxTreeEvent wx_event( wxEVT_TREE_BEGIN_LABEL_EDIT, GetHandler(), wxQtConvertTreeItem(itemFromIndex(index)) ); if (GetHandler()->HandleWindowEvent(wx_event) && !wx_event.IsAllowed()) return false; } return QTreeWidget::edit(index, trigger, event); } void closeEditor(QWidget *editor, QAbstractItemDelegate::EndEditHint hint) wxOVERRIDE { // Close process can re-signal closeEditor so we need to guard against // reentrant calls. wxRecursionGuard guard(m_closing_editor); if (guard.IsInside()) return; // There can be multiple calls to close editor when the item loses focus const QModelIndex current_index = m_item_delegate.GetCurrentModelIndex(); if (!current_index.isValid()) return; wxTreeEvent event( wxEVT_TREE_END_LABEL_EDIT, GetHandler(), wxQtConvertTreeItem(itemFromIndex(current_index)) ); if (hint == QAbstractItemDelegate::RevertModelCache) { event.SetEditCanceled(true); EmitEvent(event); } else { // Allow event handlers to decide whether to accept edited text const wxString editor_text = m_item_delegate.GetEditControl()->GetLineText(0); event.SetLabel(editor_text); if (!GetHandler()->HandleWindowEvent(event) || event.IsAllowed()) m_item_delegate.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; QTreeWidget::closeEditor(editor, hint); } private: void ReplaceIcons(QTreeWidgetItem *item) { item->setIcon(0, m_placeHolderImage); const int childCount = item->childCount(); for ( int i = 0; i < childCount; ++i ) { ReplaceIcons(item->child(i)); } } void OnCurrentItemChanged( QTreeWidgetItem *current, QTreeWidgetItem *previous ) { wxTreeCtrl* treeCtrl = GetHandler(); wxTreeEvent changingEvent( wxEVT_TREE_SEL_CHANGING, treeCtrl, wxQtConvertTreeItem(current) ); changingEvent.SetOldItem(wxQtConvertTreeItem(previous)); EmitEvent(changingEvent); if ( !changingEvent.IsAllowed() ) { blockSignals(true); setCurrentItem(previous); blockSignals(false); return; } // QT doesn't update the selection until this signal has been // processed. Deferring this event ensures that // wxTreeCtrl::GetSelection returns the new selection in the // wx event handler. wxTreeEvent changedEvent( wxEVT_TREE_SEL_CHANGED, treeCtrl, wxQtConvertTreeItem(current) ); changedEvent.SetOldItem(wxQtConvertTreeItem(previous)); wxPostEvent(treeCtrl, changedEvent); } void OnItemActivated(QTreeWidgetItem *item, int WXUNUSED(column)) { wxTreeEvent event( wxEVT_TREE_ITEM_ACTIVATED, GetHandler(), wxQtConvertTreeItem(item) ); EmitEvent(event); } void OnItemCollapsed(QTreeWidgetItem *item) { wxTreeEvent collapsingEvent( wxEVT_TREE_ITEM_COLLAPSING, GetHandler(), wxQtConvertTreeItem(item) ); EmitEvent(collapsingEvent); if ( !collapsingEvent.IsAllowed() ) { blockSignals(true); item->setExpanded(true); blockSignals(false); return; } wxTreeEvent collapsedEvent( wxEVT_TREE_ITEM_COLLAPSED, GetHandler(), wxQtConvertTreeItem(item) ); EmitEvent(collapsedEvent); } void OnItemExpanded(QTreeWidgetItem *item) { wxTreeEvent expandingEvent( wxEVT_TREE_ITEM_EXPANDING, GetHandler(), wxQtConvertTreeItem(item) ); EmitEvent(expandingEvent); if ( !expandingEvent.IsAllowed() ) { blockSignals(true); item->setExpanded(false); blockSignals(false); return; } wxTreeEvent expandedEvent( wxEVT_TREE_ITEM_EXPANDED, GetHandler(), wxQtConvertTreeItem(item) ); EmitEvent(expandedEvent); } void tryStartDrag(const QMouseEvent *event) { wxEventType command = event->buttons() & Qt::RightButton ? wxEVT_TREE_BEGIN_RDRAG : wxEVT_TREE_BEGIN_DRAG; QTreeWidgetItem *hitItem = itemAt(event->pos()); wxTreeEvent tree_event( command, GetHandler(), wxQtConvertTreeItem(hitItem) ); tree_event.SetPoint(wxQtConvertPoint(event->pos())); // Vetoed unless explicitly accepted. tree_event.Veto(); EmitEvent(tree_event); if ( !tree_event.IsAllowed() ) { setState(DragSelectingState); } } void endDrag(QPoint position) { QTreeWidgetItem *hitItem = itemAt(position); wxTreeEvent tree_event( wxEVT_TREE_END_DRAG, GetHandler(), wxQtConvertTreeItem(hitItem) ); tree_event.SetPoint(wxQtConvertPoint(position)); EmitEvent(tree_event); } virtual void dropEvent(QDropEvent* event) wxOVERRIDE { endDrag(event->pos()); // We don't want Qt to actually do the drop. event->ignore(); } virtual void mouseMoveEvent(QMouseEvent *event) wxOVERRIDE { const bool wasDragging = state() == DraggingState; wxQtEventSignalHandler::mouseMoveEvent(event); const bool nowDragging = state() == DraggingState; if ( !wasDragging && nowDragging ) { tryStartDrag(event); } } int ChooseBestImage(QTreeWidgetItem *item) const { int imageIndex = -1; const ImageStateMap::const_iterator i = m_imageStates.find(item); if ( i == m_imageStates.end() ) { return -1; } const ImageState &states = i->second; if ( item->isExpanded() ) { if ( item->isSelected() ) imageIndex = states[wxTreeItemIcon_SelectedExpanded]; if (imageIndex == -1) imageIndex = states[wxTreeItemIcon_Expanded]; } else { if ( item->isSelected() ) imageIndex = states[wxTreeItemIcon_Selected]; } if ( imageIndex == -1 ) imageIndex = states[wxTreeItemIcon_Normal]; return imageIndex; } wxQTTreeItemDelegate m_item_delegate; wxRecursionGuardFlag m_closing_editor; typedef std::map ImageStateMap; ImageStateMap m_imageStates; // Place holder image to reserve enough space in a row // for us to draw our icon QPixmap m_placeHolderImage; }; wxTreeCtrl::wxTreeCtrl() : m_qtTreeWidget(NULL) { } wxTreeCtrl::wxTreeCtrl(wxWindow *parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style, const wxValidator& validator, const wxString& name) { Create(parent, id, pos, size, style, validator, name); } bool wxTreeCtrl::Create(wxWindow *parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style, const wxValidator& validator, const wxString& name) { m_qtTreeWidget = new wxQTreeWidget(parent, this); m_qtTreeWidget->header()->hide(); SetWindowStyleFlag(style); return QtCreateControl(parent, id, pos, size, style, validator, name); } wxTreeCtrl::~wxTreeCtrl() { if ( m_qtTreeWidget != NULL ) m_qtTreeWidget->deleteLater(); } unsigned wxTreeCtrl::GetCount() const { QTreeWidgetItem *root = m_qtTreeWidget->invisibleRootItem(); if ( root->childCount() == 0 ) return 0; return CountChildren(root->child(0)); } unsigned wxTreeCtrl::GetIndent() const { return m_qtTreeWidget->columnCount(); } void wxTreeCtrl::SetIndent(unsigned int indent) { m_qtTreeWidget->setColumnCount( indent ); } void wxTreeCtrl::SetImageList(wxImageList *imageList) { m_imageListNormal = imageList; int width, height; m_imageListNormal->GetSize(0, width, height); m_qtTreeWidget->ResizeIcons(QSize(width, height)); m_qtTreeWidget->update(); } void wxTreeCtrl::SetStateImageList(wxImageList *imageList) { m_imageListState = imageList; m_qtTreeWidget->update(); } wxString wxTreeCtrl::GetItemText(const wxTreeItemId& item) const { if ( !item.IsOk() ) return wxString(); const QTreeWidgetItem* qTreeItem = wxQtConvertTreeItem(item); return wxQtConvertString(qTreeItem->text(0)); } int wxTreeCtrl::GetItemImage( const wxTreeItemId& item, wxTreeItemIcon which ) const { wxCHECK_MSG(item.IsOk(), -1, "invalid tree item"); return m_qtTreeWidget->GetItemImage(wxQtConvertTreeItem(item), which); } wxTreeItemData *wxTreeCtrl::GetItemData(const wxTreeItemId& item) const { wxCHECK_MSG(item.IsOk(), NULL, "invalid tree item"); const QTreeWidgetItem* qTreeItem = wxQtConvertTreeItem(item); const QVariant itemData = qTreeItem->data(0, Qt::UserRole); const TreeItemDataQt value = itemData.value(); return value.getData(); } wxColour wxTreeCtrl::GetItemTextColour(const wxTreeItemId& item) const { wxCHECK_MSG(item.IsOk(), wxNullColour, "invalid tree item"); const QTreeWidgetItem* qTreeItem = wxQtConvertTreeItem(item); return wxQtConvertColour(qTreeItem->textColor(0)); } wxColour wxTreeCtrl::GetItemBackgroundColour(const wxTreeItemId& item) const { wxCHECK_MSG(item.IsOk(), wxNullColour, "invalid tree item"); const QTreeWidgetItem* qTreeItem = wxQtConvertTreeItem(item); return wxQtConvertColour(qTreeItem->backgroundColor(0)); } wxFont wxTreeCtrl::GetItemFont(const wxTreeItemId& item) const { wxCHECK_MSG(item.IsOk(), wxNullFont, "invalid tree item"); const QTreeWidgetItem* qTreeItem = wxQtConvertTreeItem(item); return wxFont(qTreeItem->font(0)); } void wxTreeCtrl::SetItemText(const wxTreeItemId& item, const wxString& text) { wxCHECK_RET(item.IsOk(), "invalid tree item"); QTreeWidgetItem* qTreeItem = wxQtConvertTreeItem(item); qTreeItem->setText(0, wxQtConvertString(text)); } void wxTreeCtrl::SetItemImage( const wxTreeItemId& item, int image, wxTreeItemIcon which ) { wxCHECK_RET(item.IsOk(), "invalid tree item"); m_qtTreeWidget->SetItemImage(wxQtConvertTreeItem(item), image, which); } void wxTreeCtrl::SetItemData(const wxTreeItemId& item, wxTreeItemData *data) { wxCHECK_RET(item.IsOk(), "invalid tree item"); if ( data != NULL ) data->SetId(item); QTreeWidgetItem *qTreeItem = wxQtConvertTreeItem(item); const TreeItemDataQt treeItemData(data); qTreeItem->setData(0, Qt::UserRole, QVariant::fromValue(treeItemData)); } void wxTreeCtrl::SetItemHasChildren(const wxTreeItemId& item, bool has) { wxCHECK_RET(item.IsOk(), "invalid tree item"); QTreeWidgetItem *qTreeItem = wxQtConvertTreeItem(item); qTreeItem->setChildIndicatorPolicy(has ? QTreeWidgetItem::ShowIndicator : QTreeWidgetItem::DontShowIndicatorWhenChildless); } void wxTreeCtrl::SetItemBold(const wxTreeItemId& item, bool bold) { wxCHECK_RET(item.IsOk(), "invalid tree item"); QTreeWidgetItem *qTreeItem = wxQtConvertTreeItem(item); QFont font = qTreeItem->font(0); font.setBold(bold); qTreeItem->setFont(0, font); } void wxTreeCtrl::SetItemDropHighlight(const wxTreeItemId& item, bool highlight) { wxCHECK_RET(item.IsOk(), "invalid tree item"); QTreeWidgetItem *qTreeItem = wxQtConvertTreeItem(item); wxColour fg, bg; if ( highlight ) { bg = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT); fg = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT); } else { bg = GetBackgroundColour(); fg = GetForegroundColour(); } qTreeItem->setBackgroundColor(0, wxQtConvertColour(bg)); qTreeItem->setTextColor(0, wxQtConvertColour(fg)); } void wxTreeCtrl::SetItemTextColour( const wxTreeItemId& item, const wxColour& col ) { wxCHECK_RET(item.IsOk(), "invalid tree item"); QTreeWidgetItem *qTreeItem = wxQtConvertTreeItem(item); qTreeItem->setTextColor(0, wxQtConvertColour(col)); } void wxTreeCtrl::SetItemBackgroundColour( const wxTreeItemId& item, const wxColour& col ) { wxCHECK_RET(item.IsOk(), "invalid tree item"); QTreeWidgetItem *qTreeItem = wxQtConvertTreeItem(item); qTreeItem->setBackgroundColor(0, wxQtConvertColour(col)); } void wxTreeCtrl::SetItemFont(const wxTreeItemId& item, const wxFont& font) { wxCHECK_RET(item.IsOk(), "invalid tree item"); QTreeWidgetItem *qTreeItem = wxQtConvertTreeItem(item); qTreeItem->setFont(0, font.GetHandle()); } bool wxTreeCtrl::IsVisible(const wxTreeItemId& item) const { wxCHECK_MSG(item.IsOk(), false, "invalid tree item"); const QTreeWidgetItem *qTreeItem = wxQtConvertTreeItem(item); const QRect itemRect = m_qtTreeWidget->visualItemRect(qTreeItem); const QRect clientRect = m_qtTreeWidget->rect(); return itemRect.isValid() && clientRect.contains(itemRect); } bool wxTreeCtrl::ItemHasChildren(const wxTreeItemId& item) const { wxCHECK_MSG(item.IsOk(), false, "invalid tree item"); const QTreeWidgetItem *qTreeItem = wxQtConvertTreeItem(item); return qTreeItem->childCount() > 0; } bool wxTreeCtrl::IsExpanded(const wxTreeItemId& item) const { wxCHECK_MSG(item.IsOk(), false, "invalid tree item"); const QTreeWidgetItem *qTreeItem = wxQtConvertTreeItem(item); return qTreeItem->isExpanded(); } bool wxTreeCtrl::IsSelected(const wxTreeItemId& item) const { wxCHECK_MSG(item.IsOk(), false, "invalid tree item"); const QTreeWidgetItem *qTreeItem = wxQtConvertTreeItem(item); return qTreeItem->isSelected(); } bool wxTreeCtrl::IsBold(const wxTreeItemId& item) const { wxCHECK_MSG(item.IsOk(), false, "invalid tree item"); const QTreeWidgetItem *qTreeItem = wxQtConvertTreeItem(item); const QFont font = qTreeItem->font(0); return font.bold(); } size_t wxTreeCtrl::GetChildrenCount( const wxTreeItemId& item, bool recursively ) const { wxCHECK_MSG(item.IsOk(), 0, "invalid tree item"); QTreeWidgetItem *qTreeItem = wxQtConvertTreeItem(item); if ( recursively ) return CountChildren(qTreeItem); return qTreeItem->childCount(); } wxTreeItemId wxTreeCtrl::GetRootItem() const { const QTreeWidgetItem *root = m_qtTreeWidget->invisibleRootItem(); return wxQtConvertTreeItem(root->child(0)); } wxTreeItemId wxTreeCtrl::GetSelection() const { QList selections = m_qtTreeWidget->selectedItems(); return selections.isEmpty() ? wxTreeItemId() : wxQtConvertTreeItem(selections[0]); } size_t wxTreeCtrl::GetSelections(wxArrayTreeItemIds& selections) const { QList qtSelections = m_qtTreeWidget->selectedItems(); const size_t numberOfSelections = qtSelections.size(); selections.reserve(numberOfSelections); for ( size_t i = 0; i < numberOfSelections; ++i ) { QTreeWidgetItem *item = qtSelections[i]; selections.push_back(wxQtConvertTreeItem(item)); } return numberOfSelections; } void wxTreeCtrl::SetFocusedItem(const wxTreeItemId& item) { wxCHECK_RET(item.IsOk(), "invalid tree item"); m_qtTreeWidget->setCurrentItem(wxQtConvertTreeItem(item), 0); } void wxTreeCtrl::ClearFocusedItem() { m_qtTreeWidget->setCurrentItem(NULL); } wxTreeItemId wxTreeCtrl::GetFocusedItem() const { return wxQtConvertTreeItem(m_qtTreeWidget->currentItem()); } wxTreeItemId wxTreeCtrl::GetItemParent(const wxTreeItemId& item) const { wxCHECK_MSG(item.IsOk(), wxTreeItemId(), "invalid tree item"); QTreeWidgetItem *qTreeItem = wxQtConvertTreeItem(item); return wxQtConvertTreeItem(qTreeItem->parent()); } wxTreeItemId wxTreeCtrl::GetFirstChild( const wxTreeItemId& item, wxTreeItemIdValue& cookie ) const { cookie = 0; return GetNextChild(item, cookie); } wxTreeItemId wxTreeCtrl::GetNextChild( const wxTreeItemId& item, wxTreeItemIdValue& cookie ) const { wxCHECK_MSG(item.IsOk(), wxTreeItemId(), "invalid tree item"); wxIntPtr currentIndex = reinterpret_cast(cookie); const QTreeWidgetItem *qTreeItem = wxQtConvertTreeItem(item); wxTreeItemId childItem; if ( currentIndex < qTreeItem->childCount() ) { childItem = wxQtConvertTreeItem(qTreeItem->child(currentIndex++)); cookie = reinterpret_cast(currentIndex); } return childItem; } wxTreeItemId wxTreeCtrl::GetLastChild(const wxTreeItemId& item) const { wxCHECK_MSG(item.IsOk(), wxTreeItemId(), "invalid tree item"); const QTreeWidgetItem *qTreeItem = wxQtConvertTreeItem(item); const int childCount = qTreeItem->childCount(); return childCount == 0 ? wxTreeItemId() : wxQtConvertTreeItem(qTreeItem->child(childCount - 1)); } wxTreeItemId wxTreeCtrl::GetNextSibling(const wxTreeItemId& item) const { wxCHECK_MSG(item.IsOk(), wxTreeItemId(), "invalid tree item"); QTreeWidgetItem *qTreeItem = wxQtConvertTreeItem(item); QTreeWidgetItem *parent = qTreeItem->parent(); if ( parent != NULL ) { int index = parent->indexOfChild(qTreeItem); wxASSERT(index != -1); ++index; return index < parent->childCount() ? wxQtConvertTreeItem(parent->child(index)) : wxTreeItemId(); } int index = m_qtTreeWidget->indexOfTopLevelItem(qTreeItem); wxASSERT(index != -1); ++index; return index < m_qtTreeWidget->topLevelItemCount() ? wxQtConvertTreeItem(m_qtTreeWidget->topLevelItem(index)) : wxTreeItemId(); } wxTreeItemId wxTreeCtrl::GetPrevSibling(const wxTreeItemId& item) const { wxCHECK_MSG(item.IsOk(), wxTreeItemId(), "invalid tree item"); QTreeWidgetItem *qTreeItem = wxQtConvertTreeItem(item); QTreeWidgetItem *parent = qTreeItem->parent(); if ( parent != NULL ) { int index = parent->indexOfChild(qTreeItem); wxASSERT(index != -1); --index; return index >= 0 ? wxQtConvertTreeItem(parent->child(index)) : wxTreeItemId(); } int index = m_qtTreeWidget->indexOfTopLevelItem(qTreeItem); wxASSERT(index != -1); --index; return index >= 0 ? wxQtConvertTreeItem(m_qtTreeWidget->topLevelItem(index)) : wxTreeItemId(); } wxTreeItemId wxTreeCtrl::GetFirstVisibleItem() const { wxTreeItemId itemid = GetRootItem(); if ( !itemid.IsOk() ) return itemid; do { if ( IsVisible(itemid) ) return itemid; itemid = GetNext(itemid); } while ( itemid.IsOk() ); return wxTreeItemId(); } wxTreeItemId wxTreeCtrl::GetNextVisible(const wxTreeItemId& item) const { wxCHECK_MSG(item.IsOk(), wxTreeItemId(), "invalid tree item"); wxASSERT_MSG(IsVisible(item), "this item itself should be visible"); wxTreeItemId id = item; if ( id.IsOk() ) { while ( id = GetNext(id), id.IsOk() ) { if ( IsVisible(id) ) return id; } } return wxTreeItemId(); } wxTreeItemId wxTreeCtrl::GetPrevVisible(const wxTreeItemId& item) const { wxCHECK_MSG(item.IsOk(), wxTreeItemId(), "invalid tree item"); wxASSERT_MSG(IsVisible(item), "this item itself should be visible"); // find out the starting point wxTreeItemId prevItem = GetPrevSibling(item); if ( !prevItem.IsOk() ) { prevItem = GetItemParent(item); } // find the first visible item after it while ( prevItem.IsOk() && !IsVisible(prevItem) ) { prevItem = GetNext(prevItem); if ( !prevItem.IsOk() || prevItem == item ) { // there are no visible items before item return wxTreeItemId(); } } // from there we must be able to navigate until this item while ( prevItem.IsOk() ) { const wxTreeItemId nextItem = GetNextVisible(prevItem); if ( !nextItem.IsOk() || nextItem == item ) break; prevItem = nextItem; } return prevItem; } wxTreeItemId wxTreeCtrl::AddRoot(const wxString& text, int image, int selImage, wxTreeItemData *data) { QTreeWidgetItem *root = m_qtTreeWidget->invisibleRootItem(); wxTreeItemId newItem = DoInsertItem( wxQtConvertTreeItem(root), 0, text, image, selImage, data ); m_qtTreeWidget->setCurrentItem(NULL); if ( (GetWindowStyleFlag() & wxTR_HIDE_ROOT) != 0 ) m_qtTreeWidget->setRootIndex(m_qtTreeWidget->model()->index(0, 0)); else m_qtTreeWidget->setRootIndex(QModelIndex()); return newItem; } void wxTreeCtrl::Delete(const wxTreeItemId& item) { wxCHECK_RET(item.IsOk(), "invalid tree item"); QTreeWidgetItem *qTreeItem = wxQtConvertTreeItem(item); QTreeWidgetItem *parent = qTreeItem->parent(); DeleteChildren(qTreeItem); if ( parent != NULL ) { parent->removeChild(qTreeItem); } else { m_qtTreeWidget->removeItemWidget(qTreeItem, 0); } SendDeleteEvent(item); delete qTreeItem; } class wxQtEnsureSignalsBlocked { public: wxQtEnsureSignalsBlocked(QWidget *widget) : m_widget(widget) { m_restore = m_widget->blockSignals(true); } ~wxQtEnsureSignalsBlocked() { m_widget->blockSignals(m_restore); } private: QWidget *m_widget; bool m_restore; }; void wxTreeCtrl::DeleteChildren(const wxTreeItemId& item) { wxCHECK_RET(item.IsOk(), "invalid tree item"); QTreeWidgetItem *qTreeItem = wxQtConvertTreeItem(item); wxQtEnsureSignalsBlocked ensureSignalsBlock(m_qtTreeWidget); while ( qTreeItem->childCount() > 0 ) { QTreeWidgetItem *child = qTreeItem->child(0); DeleteChildren(wxQtConvertTreeItem(child)); qTreeItem->removeChild(child); SendDeleteEvent(wxQtConvertTreeItem(child)); delete child; } } void wxTreeCtrl::DeleteAllItems() { DeleteChildren(wxQtConvertTreeItem(m_qtTreeWidget->invisibleRootItem())); } void wxTreeCtrl::Expand(const wxTreeItemId& item) { wxCHECK_RET(item.IsOk(), "invalid tree item"); QTreeWidgetItem *qTreeItem = wxQtConvertTreeItem(item); qTreeItem->setExpanded(true); } void wxTreeCtrl::Collapse(const wxTreeItemId& item) { wxCHECK_RET(item.IsOk(), "invalid tree item"); QTreeWidgetItem *qTreeItem = wxQtConvertTreeItem(item); qTreeItem->setExpanded(false); } void wxTreeCtrl::CollapseAndReset(const wxTreeItemId& item) { wxCHECK_RET(item.IsOk(), "invalid tree item"); Collapse(item); DeleteChildren(item); } void wxTreeCtrl::Toggle(const wxTreeItemId& item) { wxCHECK_RET(item.IsOk(), "invalid tree item"); QTreeWidgetItem *qTreeItem = wxQtConvertTreeItem(item); qTreeItem->setExpanded(!qTreeItem->isExpanded()); } void wxTreeCtrl::Unselect() { QTreeWidgetItem *current = m_qtTreeWidget->currentItem(); if ( current != NULL ) m_qtTreeWidget->select(current, QItemSelectionModel::Deselect); } void wxTreeCtrl::UnselectAll() { m_qtTreeWidget->selectionModel()->clearSelection(); } void wxTreeCtrl::SelectItem(const wxTreeItemId& item, bool select) { wxCHECK_RET(item.IsOk(), "invalid tree item"); if ( !HasFlag(wxTR_MULTIPLE) ) { m_qtTreeWidget->clearSelection(); } QTreeWidgetItem *qTreeItem = wxQtConvertTreeItem(item); if ( qTreeItem ) { m_qtTreeWidget->select(qTreeItem, select ? QItemSelectionModel::Select : QItemSelectionModel::Deselect); if ( select && m_qtTreeWidget->selectionMode() == QTreeWidget::SingleSelection ) { m_qtTreeWidget->setCurrentItem(qTreeItem); } } } void wxTreeCtrl::SelectChildren(const wxTreeItemId& parent) { wxCHECK_RET(parent.IsOk(), "invalid tree item"); QTreeWidgetItem *qTreeItem = wxQtConvertTreeItem(parent); const int childCount = qTreeItem->childCount(); for ( int i = 0; i < childCount; ++i ) { m_qtTreeWidget->select(qTreeItem->child(i), QItemSelectionModel::Select); } } void wxTreeCtrl::EnsureVisible(const wxTreeItemId& item) { wxCHECK_RET(item.IsOk(), "invalid tree item"); QTreeWidgetItem *qTreeItem = wxQtConvertTreeItem(item); QTreeWidgetItem *parent = qTreeItem->parent(); while ( parent != NULL ) { parent->setExpanded(true); parent = parent->parent(); } ScrollTo(item); } void wxTreeCtrl::ScrollTo(const wxTreeItemId& item) { wxCHECK_RET(item.IsOk(), "invalid tree item"); QTreeWidgetItem *qTreeItem = wxQtConvertTreeItem(item); m_qtTreeWidget->scrollToItem(qTreeItem); } wxTextCtrl *wxTreeCtrl::EditLabel( const wxTreeItemId& item, wxClassInfo* WXUNUSED(textCtrlClass) ) { wxCHECK_MSG(item.IsOk(), NULL, "invalid tree item"); m_qtTreeWidget->editItem(wxQtConvertTreeItem(item)); return m_qtTreeWidget->GetEditControl(); } wxTextCtrl *wxTreeCtrl::GetEditControl() const { return m_qtTreeWidget->GetEditControl(); } void wxTreeCtrl::EndEditLabel( const wxTreeItemId& item, bool WXUNUSED(discardChanges) ) { wxCHECK_RET(item.IsOk(), "invalid tree item"); QTreeWidgetItem *qTreeItem = wxQtConvertTreeItem(item); m_qtTreeWidget->closePersistentEditor(qTreeItem); } void wxTreeCtrl::SortChildren(const wxTreeItemId& item) { wxCHECK_RET(item.IsOk(), "invalid tree item"); QTreeWidgetItem *qTreeItem = wxQtConvertTreeItem(item); qTreeItem->sortChildren(0, Qt::AscendingOrder); } bool wxTreeCtrl::GetBoundingRect( const wxTreeItemId& item, wxRect& rect, bool WXUNUSED(textOnly) ) const { wxCHECK_MSG(item.IsOk(), false, "invalid tree item"); const QTreeWidgetItem *qTreeItem = wxQtConvertTreeItem(item); const QRect visualRect = m_qtTreeWidget->visualItemRect(qTreeItem); if ( !visualRect.isValid() ) return false; rect = wxQtConvertRect(visualRect); return true; } void wxTreeCtrl::SetWindowStyleFlag(long styles) { wxControl::SetWindowStyleFlag(styles); m_qtTreeWidget->setSelectionMode( styles & wxTR_MULTIPLE ? QTreeWidget::ExtendedSelection : QTreeWidget::SingleSelection ); } int wxTreeCtrl::DoGetItemState(const wxTreeItemId& item) const { wxCHECK_MSG(item.IsOk(), wxTREE_ITEMSTATE_NONE, "invalid tree item"); return m_qtTreeWidget->GetItemState(wxQtConvertTreeItem(item)); } void wxTreeCtrl::DoSetItemState(const wxTreeItemId& item, int state) { wxCHECK_RET(item.IsOk(), "invalid tree item"); m_qtTreeWidget->SetItemState(wxQtConvertTreeItem(item), state); } wxTreeItemId wxTreeCtrl::DoInsertItem(const wxTreeItemId& parent, size_t pos, const wxString& text, int image, int selImage, wxTreeItemData *data) { wxCHECK_MSG(parent.IsOk(), wxTreeItemId(), "invalid tree item"); QTreeWidgetItem *qTreeItem = wxQtConvertTreeItem(parent); QTreeWidgetItem *newItem = new QTreeWidgetItem; newItem->setText(0, wxQtConvertString(text)); newItem->setFlags(newItem->flags() | Qt::ItemIsEditable); TreeItemDataQt treeItemData(data); newItem->setData(0, Qt::UserRole, QVariant::fromValue(treeItemData)); m_qtTreeWidget->SetItemImage(newItem, image, wxTreeItemIcon_Normal); m_qtTreeWidget->SetItemImage(newItem, selImage, wxTreeItemIcon_Selected); newItem->setIcon(0, m_qtTreeWidget->GetPlaceHolderImage()); wxTreeItemId wxItem = wxQtConvertTreeItem(newItem); if ( data != NULL ) data->SetId(wxItem); if ( pos == static_cast(-1) ) { qTreeItem->addChild(newItem); } else { qTreeItem->insertChild(pos, newItem); } return wxItem; } wxTreeItemId wxTreeCtrl::DoInsertAfter(const wxTreeItemId& parent, const wxTreeItemId& idPrevious, const wxString& text, int image, int selImage, wxTreeItemData *data) { wxCHECK_MSG(parent.IsOk(), wxTreeItemId(), "invalid tree item"); wxCHECK_MSG(idPrevious.IsOk(), wxTreeItemId(), "invalid tree item"); const QTreeWidgetItem *qTreeItem = wxQtConvertTreeItem(parent); const int index = qTreeItem->indexOfChild(wxQtConvertTreeItem(idPrevious)); return DoInsertItem(parent, index + 1, text, image, selImage, data); } wxTreeItemId wxTreeCtrl::DoTreeHitTest(const wxPoint& point, int& flags) const { int w, h; GetSize(&w, &h); flags = 0; if ( point.x < 0 ) flags |= wxTREE_HITTEST_TOLEFT; else if ( point.x > w ) flags |= wxTREE_HITTEST_TORIGHT; if ( point.y < 0 ) flags |= wxTREE_HITTEST_ABOVE; else if ( point.y > h ) flags |= wxTREE_HITTEST_BELOW; if ( flags != 0 ) return wxTreeItemId(); QTreeWidgetItem *hitItem = m_qtTreeWidget->itemAt(wxQtConvertPoint(point)); flags = hitItem == NULL ? wxTREE_HITTEST_NOWHERE : wxTREE_HITTEST_ONITEM; return wxQtConvertTreeItem(hitItem); } QWidget *wxTreeCtrl::GetHandle() const { return m_qtTreeWidget; } void wxTreeCtrl::SendDeleteEvent(const wxTreeItemId &item) { wxTreeEvent event(wxEVT_TREE_DELETE_ITEM, GetId()); event.SetItem(item); HandleWindowEvent(event); } wxTreeItemId wxTreeCtrl::GetNext(const wxTreeItemId &item) const { wxCHECK_MSG(item.IsOk(), wxTreeItemId(), "invalid tree item"); QTreeWidgetItem *qTreeItem = wxQtConvertTreeItem(item); if ( qTreeItem->childCount() > 0 ) { return qTreeItem->child(0); } // Try a sibling of this or ancestor instead wxTreeItemId p = item; wxTreeItemId toFind; do { toFind = GetNextSibling(p); p = GetItemParent(p); } while ( p.IsOk() && !toFind.IsOk() ); return toFind; }