#include "stdafx.h" #include "SplitTreeView.h" CSplitTreeView::CSplitTreeView(QAbstractItemModel* pModel, QWidget *parent) : QWidget(parent) { m_pModel = pModel; m_pMainLayout = new QHBoxLayout(); m_pMainLayout->setContentsMargins(0,0,0,0); this->setLayout(m_pMainLayout); m_pSplitter = new QSplitter(); m_pSplitter->setOrientation(Qt::Horizontal); m_pMainLayout->addWidget(m_pSplitter); #ifdef WIN32 QStyle* pStyle = QStyleFactory::create("windows"); #endif m_LockSelection = 0; // Tree m_pTree = new QTreeView(); m_pOneModel = new COneColumnModel(this); m_pOneModel->setSourceModel(m_pModel); m_pTree->setModel(m_pOneModel); //m_pTree->setModel(m_pSortProxy); m_pTree->setSelectionMode(QAbstractItemView::ExtendedSelection); #ifdef WIN32 m_pTree->setStyle(pStyle); #endif //m_pTree->setSortingEnabled(true); m_pTree->setSortingEnabled(false); m_pTree->setUniformRowHeights(true); m_pTree->header()->setSortIndicatorShown(true); m_pTree->header()->setSectionsClickable(true); connect(m_pTree->header(), SIGNAL(sectionClicked(int)), this, SLOT(OnTreeCustomSortByColumn(int))); m_pTree->setContextMenuPolicy(Qt::CustomContextMenu); connect(m_pTree, SIGNAL(customContextMenuRequested( const QPoint& )), this, SIGNAL(MenuRequested(const QPoint &))); m_pSplitter->addWidget(m_pTree); //m_pSplitter->setCollapsible(0, false); m_pSplitter->setStretchFactor(0, 0); // // List // Note: It would be convenient to use QTreeViewEx here but qt does not scale well when there are too many columns // hence we will add and remove columns at the model level directly. // This way we can get out operational CPU usage to be quite comparable with TaskInfo na other advanced task managers // Plus there are to many columns to cram them into one simple context menu :-) //m_pList = new QTreeViewEx(); m_pList = new QTreeView(); m_pList->setModel(m_pModel); m_pList->setSelectionMode(QAbstractItemView::ExtendedSelection); #ifdef WIN32 m_pList->setStyle(pStyle); #endif m_pList->setSortingEnabled(true); m_pList->setUniformRowHeights(true); connect(m_pList->header(), SIGNAL(sectionClicked(int)), this, SLOT(OnListCustomSortByColumn(int))); m_pList->setContextMenuPolicy(Qt::CustomContextMenu); connect(m_pList, SIGNAL(customContextMenuRequested( const QPoint& )), this, SIGNAL(MenuRequested(const QPoint &))); m_pSplitter->addWidget(m_pList); m_pSplitter->setCollapsible(1, false); m_pSplitter->setStretchFactor(1, 1); // connect(m_pSplitter, SIGNAL(splitterMoved(int,int)), this, SLOT(OnSplitterMoved(int,int))); // Link selections //m_pTree->setSelectionModel(m_pList->selectionModel()); // this works only when both views share the same data model connect(m_pTree->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(OnTreeSelectionChanged(QItemSelection,QItemSelection))); connect(m_pList->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(OnListSelectionChanged(QItemSelection,QItemSelection))); connect(m_pTree->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), this, SLOT(OnTreeCurrentChanged(QModelIndex,QModelIndex))); connect(m_pList->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), this, SLOT(OnListCurrentChanged(QModelIndex,QModelIndex))); m_bTreeEnabled = true; m_pList->setColumnHidden(0, true); // link expansion connect(m_pTree, SIGNAL(expanded(const QModelIndex)), this, SLOT(OnExpandTree(const QModelIndex))); connect(m_pTree, SIGNAL(collapsed(const QModelIndex)), this, SLOT(OnCollapseTree(const QModelIndex))); //connect(m_pList, SIGNAL(expanded(const QModelIndex)), this, SLOT(expand(const QModelIndex))); //connect(m_pList, SIGNAL(collapsed(const QModelIndex)), this, SLOT(collapse(const QModelIndex))); // link scrollbars m_pTree->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); m_pTree->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn); m_pList->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn); connect(m_pList->verticalScrollBar(), SIGNAL(valueChanged(int)), m_pTree->verticalScrollBar(), SLOT(setValue(int))); connect(m_pTree->verticalScrollBar(), SIGNAL(valueChanged(int)), m_pList->verticalScrollBar(), SLOT(setValue(int))); connect(m_pTree, SIGNAL(clicked(const QModelIndex&)), this, SLOT(OnClickedTree(const QModelIndex&))); connect(m_pList, SIGNAL(clicked(const QModelIndex&)), this, SIGNAL(clicked(const QModelIndex&))); connect(m_pTree, SIGNAL(doubleClicked(const QModelIndex&)), this, SLOT(OnDoubleClickedTree(const QModelIndex&))); connect(m_pList, SIGNAL(doubleClicked(const QModelIndex&)), this, SIGNAL(doubleClicked(const QModelIndex&))); //QTimer::singleShot(0, this, [this]() { // emit TreeEnabled(m_bTreeEnabled); //}); } CSplitTreeView::~CSplitTreeView() { } int CSplitTreeView::GetTreeWidth() const { QList sizes = m_pSplitter->sizes(); return sizes[0]; } void CSplitTreeView::SetTreeWidth(int Width) { QList sizes = m_pSplitter->sizes(); int total = sizes[0] + sizes[1]; sizes[0] = Width; sizes[1] = total - Width; } void CSplitTreeView::OnSplitterMoved(int pos, int index) { if (index != 1) return; //if ((pos > 0) == m_bTreeEnabled) // return; //m_bTreeEnabled = (pos > 0); m_pList->setColumnHidden(0, pos > 0); //emit TreeEnabled(m_bTreeEnabled); } void CSplitTreeView::OnExpandTree(const QModelIndex& index) { m_pList->expand(m_pOneModel->mapToSource(index)); } void CSplitTreeView::OnCollapseTree(const QModelIndex& index) { m_pList->collapse(m_pOneModel->mapToSource(index)); } void CSplitTreeView::OnClickedTree(const QModelIndex& Index) { emit clicked(m_pOneModel->mapToSource(Index)); } void CSplitTreeView::OnDoubleClickedTree(const QModelIndex& Index) { emit doubleClicked(m_pOneModel->mapToSource(Index)); } void CSplitTreeView::expand(const QModelIndex &index) { m_pTree->expand(m_pOneModel->mapFromSource(index)); } void CSplitTreeView::collapse(const QModelIndex &index) { m_pTree->collapse(m_pOneModel->mapFromSource(index)); } void CSplitTreeView::OnTreeSelectionChanged(const QItemSelection& Selected, const QItemSelection& Deselected) { if (m_LockSelection) return; m_LockSelection = 1; QItemSelection SelectedItems; foreach(const QModelIndex& Index, m_pTree->selectionModel()->selectedIndexes()) { QModelIndex ModelIndex = m_pOneModel->mapToSource(Index); QModelIndex ModelL = m_pModel->index(ModelIndex.row(), 0, ModelIndex.parent()); QModelIndex ModelR = m_pModel->index(ModelIndex.row(), m_pModel->columnCount()-1, ModelIndex.parent()); SelectedItems.append(QItemSelectionRange(ModelL, ModelR)); } m_pList->selectionModel()->select(SelectedItems, QItemSelectionModel::ClearAndSelect); /* foreach(const QModelIndex& Index, Selected.indexes()) { QModelIndex ModelIndex = m_pOneModel->mapToSource(Index); QModelIndex ModelL = m_pModel->index(ModelIndex.row(), 0, ModelIndex.parent()); QModelIndex ModelR = m_pModel->index(ModelIndex.row(), m_pModel->columnCount()-1, ModelIndex.parent()); m_pList->selectionModel()->select(QItemSelection(ModelL, ModelR), QItemSelectionModel::Select); } foreach(const QModelIndex& Index, Deselected.indexes()) { QModelIndex ModelIndex = m_pOneModel->mapToSource(Index); QModelIndex ModelL = m_pModel->index(ModelIndex.row(), 0, ModelIndex.parent()); QModelIndex ModelR = m_pModel->index(ModelIndex.row(), m_pModel->columnCount()-1, ModelIndex.parent()); m_pList->selectionModel()->select(QItemSelection(ModelL, ModelR), QItemSelectionModel::Deselect); }*/ m_LockSelection = 0; } void CSplitTreeView::OnListSelectionChanged(const QItemSelection& Selected, const QItemSelection& Deselected) { if (m_LockSelection != 2) emit selectionChanged(Selected, Deselected); if (m_LockSelection) return; m_LockSelection = 1; QItemSelection SelectedItems; foreach(const QModelIndex& Index, m_pList->selectionModel()->selectedIndexes()) { QModelIndex ModelIndex = m_pOneModel->mapFromSource(Index); if (ModelIndex.column() != 0) continue; QModelIndex Model = m_pOneModel->index(ModelIndex.row(), 0, ModelIndex.parent()); SelectedItems.append(QItemSelectionRange(Model)); } m_pTree->selectionModel()->select(SelectedItems, QItemSelectionModel::ClearAndSelect); /*foreach(const QModelIndex& Index, Selected.indexes()) { QModelIndex ModelIndex = m_pOneModel->mapFromSource(Index); if (ModelIndex.column() != 0) continue; m_pTree->selectionModel()->select(ModelIndex, QItemSelectionModel::Select); } foreach(const QModelIndex& Index, Deselected.indexes()) { QModelIndex ModelIndex = m_pOneModel->mapFromSource(Index); if (ModelIndex.column() != 0) continue; m_pTree->selectionModel()->select(ModelIndex, QItemSelectionModel::Deselect); }*/ m_LockSelection = 0; } void CSplitTreeView::OnTreeCurrentChanged(const QModelIndex ¤t, const QModelIndex &previous) { if (m_LockSelection) return; m_LockSelection = 2; int hPos = m_pList->horizontalScrollBar()->value(); // fix horizontalScrollBar position reset on selection m_pList->selectionModel()->setCurrentIndex(m_pOneModel->mapToSource(current), QItemSelectionModel::SelectCurrent); m_pList->horizontalScrollBar()->setValue(hPos); m_LockSelection = 0; emit currentChanged(m_pOneModel->mapToSource(current), m_pOneModel->mapToSource(previous)); } void CSplitTreeView::OnListCurrentChanged(const QModelIndex ¤t, const QModelIndex &previous) { if (m_LockSelection) return; m_LockSelection = 2; m_pTree->selectionModel()->setCurrentIndex(m_pOneModel->mapFromSource(current), QItemSelectionModel::SelectCurrent); m_LockSelection = 0; emit currentChanged(current, previous); } QModelIndexList CSplitTreeView::selectedRows() const { int Column = 0; for (int i = 0; i < m_pModel->columnCount(); i++) { if (!m_pList->isColumnHidden(i)) { Column = i; break; } } QModelIndexList IndexList; foreach(const QModelIndex& Index, m_pList->selectionModel()->selectedIndexes()) { if (Index.column() == Column) IndexList.append(Index); } return IndexList; } void CSplitTreeView::SetTree(bool bSet) { m_bTreeEnabled = bSet; emit TreeEnabled(m_bTreeEnabled); } void CSplitTreeView::OnTreeCustomSortByColumn(int column) { Qt::SortOrder order = m_pTree->header()->sortIndicatorOrder(); if (order == Qt::AscendingOrder) SetTree(!m_bTreeEnabled); m_pList->sortByColumn(column, order); m_pTree->header()->setSortIndicatorShown(true); } void CSplitTreeView::OnListCustomSortByColumn(int column) { Qt::SortOrder order = m_pList->header()->sortIndicatorOrder(); if (column == 0) SetTree(!m_bTreeEnabled); else if (m_bTreeEnabled) SetTree(false); m_pTree->header()->setSortIndicatorShown(false); } static const qint32 CSplitTreeViewMagic = 'STVs'; QByteArray CSplitTreeView::saveState() const { int version = 1; QByteArray data; QDataStream stream(&data, QIODevice::WriteOnly); stream << qint32(CSplitTreeViewMagic); stream << qint32(version); stream << m_pList->header()->saveState(); stream << m_pSplitter->saveState(); stream << m_bTreeEnabled; return data; } bool CSplitTreeView::restoreState(const QByteArray &state) { int version = 1; QByteArray sd = state; QDataStream stream(&sd, QIODevice::ReadOnly); qint32 marker; stream >> marker; qint32 v; stream >> v; if (marker != CSplitTreeViewMagic || v > version) return false; QByteArray header; stream >> header; m_pList->header()->restoreState(header); QByteArray splitter; stream >> splitter; m_pSplitter->restoreState(splitter); stream >> m_bTreeEnabled; emit TreeEnabled(m_bTreeEnabled); m_pTree->header()->setSortIndicatorShown(m_pList->header()->sortIndicatorSection() == 0); QList sizes = m_pSplitter->sizes(); for(int i=0; i < sizes.count(); i++) OnSplitterMoved(sizes[i], i); return true; }