You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
pavucontrol-qt-packaging/libfm-qt/dirtreeview.cpp

215 lines
6.7 KiB

/*
* <one line to give the program's name and a brief idea of what it does.>
* Copyright (C) 2014 <copyright holder> <email>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#include "dirtreeview.h"
#include <QHeaderView>
#include <QDebug>
#include <QItemSelection>
#include <QGuiApplication>
#include "dirtreemodel.h"
#include "dirtreemodelitem.h"
using namespace Fm;
DirTreeView::DirTreeView(QWidget* parent):
currentExpandingItem_(NULL),
currentPath_(NULL) {
setSelectionMode(QAbstractItemView::SingleSelection);
setHeaderHidden(true);
setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
header()->setStretchLastSection(false);
connect(this, &DirTreeView::collapsed, this, &DirTreeView::onCollapsed);
connect(this, &DirTreeView::expanded, this, &DirTreeView::onExpanded);
}
DirTreeView::~DirTreeView() {
if(currentPath_)
fm_path_unref(currentPath_);
}
void DirTreeView::cancelPendingChdir() {
if(!pathsToExpand_.isEmpty()) {
pathsToExpand_.clear();
if(!currentExpandingItem_)
return;
DirTreeModel* _model = static_cast<DirTreeModel*>(model());
disconnect(_model, &DirTreeModel::rowLoaded, this, &DirTreeView::onRowLoaded);
currentExpandingItem_ = NULL;
}
}
void DirTreeView::expandPendingPath() {
if(pathsToExpand_.isEmpty())
return;
FmPath* path = pathsToExpand_.first().data();
// qDebug() << "expanding: " << Path(path).displayBasename();
DirTreeModel* _model = static_cast<DirTreeModel*>(model());
DirTreeModelItem* item = _model->itemFromPath(path);
// qDebug() << "findItem: " << item;
if(item) {
currentExpandingItem_ = item;
connect(_model, &DirTreeModel::rowLoaded, this, &DirTreeView::onRowLoaded);
if(item->loaded_) { // the node is already loaded
onRowLoaded(item->index());
}
else {
// _model->loadRow(item->index());
item->loadFolder();
}
}
else {
selectionModel()->clear();
/* since we never get it loaded we need to update cwd here */
if(currentPath_)
fm_path_unref(currentPath_);
currentPath_ = fm_path_ref(path);
cancelPendingChdir(); // FIXME: is this correct? this is not done in the gtk+ version of libfm.
}
}
void DirTreeView::onRowLoaded(const QModelIndex& index) {
DirTreeModel* _model = static_cast<DirTreeModel*>(model());
if(!currentExpandingItem_)
return;
if(currentExpandingItem_ != _model->itemFromIndex(index)) {
return;
}
/* disconnect the handler since we only need it once */
disconnect(_model, &DirTreeModel::rowLoaded, this, &DirTreeView::onRowLoaded);
DirTreeModelItem* item = _model->itemFromIndex(index);
// qDebug() << "row loaded: " << item->displayName_;
/* after the folder is loaded, the files should have been added to
* the tree model */
expand(index);
/* remove the expanded path from pending list */
pathsToExpand_.removeFirst();
if(pathsToExpand_.isEmpty()) { /* this is the last one and we're done, select the item */
// qDebug() << "Done!";
selectionModel()->select(index, QItemSelectionModel::SelectCurrent|QItemSelectionModel::Clear);
scrollTo(index, QAbstractItemView::EnsureVisible);
}
else { /* continue expanding next pending path */
expandPendingPath();
}
}
void DirTreeView::setCurrentPath(FmPath* path) {
DirTreeModel* _model = static_cast<DirTreeModel*>(model());
if(!_model)
return;
int rowCount = _model->rowCount(QModelIndex());
if(rowCount == 0 || fm_path_equal(currentPath_, path))
return;
if(currentPath_)
fm_path_unref(currentPath_);
currentPath_ = fm_path_ref(path);
// NOTE: The content of each node is loaded on demand dynamically.
// So, when we ask for a chdir operation, some nodes do not exists yet.
// We have to wait for the loading of child nodes and continue the
// pending chdir operation after the child nodes become available.
// cancel previous pending tree expansion
cancelPendingChdir();
/* find a root item containing this path */
FmPath* root;
for(int row = 0; row < rowCount; ++row) {
QModelIndex index = _model->index(row, 0, QModelIndex());
root = _model->filePath(index);
if(fm_path_has_prefix(path, root))
break;
root = NULL;
}
if(root) { /* root item is found */
do { /* add path elements one by one to a list */
pathsToExpand_.prepend(path);
// qDebug() << "prepend path: " << Path(path).displayBasename();
if(fm_path_equal(path, root))
break;
path = fm_path_get_parent(path);
}
while(path);
expandPendingPath();
}
}
void DirTreeView::setModel(QAbstractItemModel* model) {
Q_ASSERT(model->inherits("Fm::DirTreeModel"));
if(!pathsToExpand_.isEmpty()) // if a chdir request is in progress, cancel it
cancelPendingChdir();
QTreeView::setModel(model);
header()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
connect(selectionModel(), &QItemSelectionModel::selectionChanged, this, &DirTreeView::onSelectionChanged);
}
void DirTreeView::contextMenuEvent(QContextMenuEvent* event) {
QAbstractScrollArea::contextMenuEvent(event);
}
void DirTreeView::onCollapsed(const QModelIndex& index) {
DirTreeModel* treeModel = static_cast<DirTreeModel*>(model());
if(treeModel) {
treeModel->unloadRow(index);
}
}
void DirTreeView::onExpanded(const QModelIndex& index) {
DirTreeModel* treeModel = static_cast<DirTreeModel*>(model());
if(treeModel) {
treeModel->loadRow(index);
}
}
void DirTreeView::onSelectionChanged(const QItemSelection & selected, const QItemSelection & deselected) {
if(!selected.isEmpty()) {
QModelIndex index = selected.first().topLeft();
DirTreeModel* _model = static_cast<DirTreeModel*>(model());
FmPath* path = _model->filePath(index);
if(path && currentPath_ && fm_path_equal(path, currentPath_))
return;
cancelPendingChdir();
if(!path)
return;
if(currentPath_)
fm_path_unref(currentPath_);
currentPath_ = fm_path_ref(path);
// FIXME: use enums for type rather than hard-coded values 0 or 1
int type = 0;
if(QGuiApplication::mouseButtons() & Qt::MiddleButton)
type = 1;
Q_EMIT chdirRequested(type, path);
}
}