/**********************************************************************
DirectoryTreeModel - model for multiple directories / files
Copyright (C) 2008 Geoffrey R. Hutchison
Inspired by example code from Qt/Examples by Trolltech.
This file is part of the Avogadro molecular editor project.
For more information, see
Avogadro 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.
Avogadro 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 "directorytreemodel.h"
#include
#include
namespace Avogadro {
DirectoryTreeModel::DirectoryTreeModel(const QString &dirList, QObject *parent)
: QAbstractItemModel(parent), _directoryList(dirList.split('\n'))
{
QList rootData;
rootData << "Name"; // This is the header row -- add more columns, e.g. file size, etc.
_rootItem = new FileTreeItem(rootData);
setupModelData(_directoryList, _rootItem);
}
DirectoryTreeModel::DirectoryTreeModel(const QStringList &dirList, QObject *parent)
: QAbstractItemModel(parent), _directoryList(dirList)
{
QList rootData;
rootData << "Name"; // This is the header row -- add more columns here
_rootItem = new FileTreeItem(rootData);
setupModelData(_directoryList, _rootItem);
}
DirectoryTreeModel::~DirectoryTreeModel()
{
delete _rootItem;
}
int DirectoryTreeModel::columnCount(const QModelIndex &parent) const
{
if (parent.isValid()) {
FileTreeItem* item = static_cast(parent.internalPointer());
if (!item)
return 0;
else
return item->columnCount();
}
else
return _rootItem->columnCount();
}
QVariant DirectoryTreeModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
if (role != Qt::DisplayRole)
return QVariant();
FileTreeItem *item = static_cast(index.internalPointer());
if (!item)
return QVariant();
return item->data(index.column());
}
QString DirectoryTreeModel::filePath(const QModelIndex &index) const
{
if (!index.isValid())
return QString();
FileTreeItem *item = static_cast(index.internalPointer());
if (!item)
return QString();
return item->filePath(); // This is a special property of our tree items and isn't user-visible
}
Qt::ItemFlags DirectoryTreeModel::flags(const QModelIndex &index) const
{
if (!index.isValid())
return 0;
return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
}
QVariant DirectoryTreeModel::headerData(int section, Qt::Orientation orientation,
int role) const
{
if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
return _rootItem->data(section);
return QVariant();
}
QModelIndex DirectoryTreeModel::index(int row, int column, const QModelIndex &parent)
const
{
if (!hasIndex(row, column, parent))
return QModelIndex();
FileTreeItem *parentItem = NULL;
if (!parent.isValid())
parentItem = _rootItem;
else
parentItem = static_cast(parent.internalPointer());
if (!parentItem)
return QModelIndex();
FileTreeItem *childItem = parentItem->child(row);
if (childItem)
return createIndex(row, column, childItem);
else
return QModelIndex();
}
QModelIndex DirectoryTreeModel::parent(const QModelIndex &index) const
{
if (!index.isValid())
return QModelIndex();
FileTreeItem *childItem = static_cast(index.internalPointer());
if (!childItem)
return QModelIndex();
FileTreeItem *parentItem = childItem->parent();
if (!parentItem || parentItem == _rootItem)
return QModelIndex();
return createIndex(parentItem->row(), 0, parentItem);
}
int DirectoryTreeModel::rowCount(const QModelIndex &parent) const
{
FileTreeItem *parentItem;
if (parent.column() > 0)
return 0;
if (!parent.isValid())
parentItem = _rootItem;
else
parentItem = static_cast(parent.internalPointer());
if (!parentItem)
return 0;
return parentItem->childCount();
}
const QStringList &DirectoryTreeModel::directoryList() const
{
return _directoryList;
}
void DirectoryTreeModel::setDirectoryList(const QStringList &dirList)
{
_directoryList = dirList;
setupModelData(_directoryList, _rootItem);
}
void DirectoryTreeModel::appendDirectory(const QString &path)
{
_directoryList.append(path);
setupModelData(_directoryList, _rootItem);
}
// HELPER function: return the number of directories in the path
int directoryDepth(QString path)
{
// Split will count the first separator (e.g. /) and give an extra, empty string
return path.split(QDir::separator()).count() - 1;
}
// HELPER function: return the name of the last directory
QString lastDirectory(QFileInfo fileInfo)
{
QStringList directoryPath = fileInfo.filePath().split(QDir::separator());
if (fileInfo.isFile())
return directoryPath[directoryPath.size() - 2]; // next to last item
else // must be a directory, since we only iterator over files or dirs
return directoryPath[directoryPath.size() - 1]; // last item
}
void DirectoryTreeModel::setupModelData(const QStringList &dirList, FileTreeItem *parent)
{
emit layoutAboutToBeChanged(); // we need to tell the view that the data is going to change
parent->deleteChildren(); // remove any previous data
int position = 0; // current number of subdirectories (i.e., the relative path depth)
int absoluteDepth = 0; // depth of the parent directory
foreach (const QString& dir, dirList) {
QDir currentDir(dir);
if (currentDir.exists()) {
absoluteDepth = directoryDepth(currentDir.absolutePath());
QList parents;
QList indentations; // number of subdirectories for each item in the model
parents << parent;
indentations << 0;
// set the first item to be the top-level directory itself
QList topLevel;
topLevel << currentDir.dirName();
parent->appendChild(new FileTreeItem(topLevel, parent));
parents << parents.last()->child(parents.last()->childCount()-1);
indentations << 0;
QDirIterator dirIterator(currentDir.absolutePath(),
QDir::Dirs | QDir::Files | QDir::Readable
| QDir::NoSymLinks | QDir::NoDotAndDotDot,
QDirIterator::Subdirectories);
do {
dirIterator.next();
position = directoryDepth(dirIterator.filePath()) - absoluteDepth;
// First handle the case where we just moved up some directory levels
if (position < indentations.last()) {
while (position < indentations.last() && parents.count() > 0) {
parents.pop_back();
indentations.pop_back();
}
}
// If this is a real directory, add it as a new subdirectory
if (dirIterator.fileInfo().isDir() && !dirIterator.fileInfo().isBundle()) {
// insert a new nested directory
QList dirData;
dirData << dirIterator.fileName();
parents.last()->appendChild(new FileTreeItem(dirData, parents.last()));
// Now the new child becomes the parent for any files in that directory
parents << parents.last()->child(parents.last()->childCount()-1);
indentations << position + 1;
continue;
}
// check to see if its an excluded file
// (hidden or not readable or a Mac OS X bundle
if (dirIterator.fileInfo().isHidden()
|| !dirIterator.fileInfo().isReadable()
|| dirIterator.fileInfo().isBundle())
continue;
// OK, this is a file, and we've set the correct path structure
// Add the filename as the first column
QList columnData;
columnData << dirIterator.fileName();
// Append a new item to the current parent's list of children.
parents.last()->appendChild(new FileTreeItem(columnData, parents.last(), dirIterator.filePath()));
} while (dirIterator.hasNext());
}
}
invalidateIndexes();
emit layoutChanged(); // again, tell the view that we're finished
}
/* From Qt API doc:
* void QAbstractItemModel::layoutAboutToBeChanged () [signal]
*
* ... Subclasses should update any persistent model indexes after emitting
* layoutAboutToBeChanged(). ...
*
* and: http://der-dakon.net/blog/KDE/persistent-crash.html
*/
void DirectoryTreeModel::invalidateIndexes()
{
for (int i = 0; i < persistentIndexList().count(); i++) {
QModelIndex idx = persistentIndexList().at(i);
FileTreeItem *parentItem;
if (!idx.isValid())
parentItem = _rootItem;
else
parentItem = static_cast(idx.internalPointer());
if (parentItem == _rootItem)
continue;
changePersistentIndex(idx, QModelIndex());
}
}
} // end namespace Avogadro
#include "directorytreemodel.moc"