Sandboxie/SandboxiePlus/MiscHelpers/Common/SplitTreeView.cpp

370 lines
12 KiB
C++

#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<int> sizes = m_pSplitter->sizes();
return sizes[0];
}
void CSplitTreeView::SetTreeWidth(int Width)
{
QList<int> 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 &current, 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 &current, 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<int> sizes = m_pSplitter->sizes();
for(int i=0; i < sizes.count(); i++)
OnSplitterMoved(sizes[i], i);
return true;
}