mirror of
https://bitbucket.org/smil3y/kde-extraapps.git
synced 2025-02-24 10:52:53 +00:00
676 lines
16 KiB
C++
676 lines
16 KiB
C++
/***************************************************************************
|
|
* Copyright (C) 2009 Matthias Fuchs <mat69@gmx.net> *
|
|
* *
|
|
* 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 "filemodel.h"
|
|
|
|
#include "verifier.h"
|
|
|
|
#include <KIcon>
|
|
#include <KLocale>
|
|
#include <KMimeType>
|
|
|
|
FileItem::FileItem(const QString &name, FileItem *parent)
|
|
: m_name(name),
|
|
m_state(Qt::Checked),
|
|
m_status(Job::Stopped),
|
|
m_totalSize(0),
|
|
m_checkusmVerified(0),
|
|
m_parent(parent)
|
|
{
|
|
}
|
|
|
|
FileItem::~FileItem()
|
|
{
|
|
qDeleteAll(m_childItems);
|
|
}
|
|
|
|
void FileItem::appendChild(FileItem *child)
|
|
{
|
|
m_childItems.append(child);
|
|
}
|
|
|
|
FileItem *FileItem::child(int row)
|
|
{
|
|
return m_childItems.value(row);
|
|
}
|
|
|
|
int FileItem::childCount() const
|
|
{
|
|
return m_childItems.count();
|
|
}
|
|
|
|
bool FileItem::isFile() const
|
|
{
|
|
return m_childItems.isEmpty();
|
|
}
|
|
|
|
int FileItem::columnCount() const
|
|
{
|
|
return 5;
|
|
}
|
|
|
|
QVariant FileItem::data(int column, int role) const
|
|
{
|
|
if (column == FileItem::File)
|
|
{
|
|
if (role == Qt::CheckStateRole)
|
|
{
|
|
return m_state;
|
|
}
|
|
else if (role == Qt::DisplayRole)
|
|
{
|
|
return m_name;
|
|
}
|
|
else if (role == Qt::DecorationRole)
|
|
{
|
|
if (m_mimeType.isNull()) {
|
|
if (isFile()) {
|
|
m_mimeType = KIcon(KMimeType::iconNameForUrl(KUrl(m_name)));
|
|
} else {
|
|
m_mimeType = KIcon("folder");
|
|
}
|
|
}
|
|
|
|
return m_mimeType;
|
|
}
|
|
}
|
|
else if (column == FileItem::Status)
|
|
{
|
|
if ((role == Qt::DisplayRole) || (role == Qt::DecorationRole))
|
|
{
|
|
if (isFile()) {
|
|
return m_status;
|
|
}
|
|
}
|
|
}
|
|
else if (column == FileItem::Size)
|
|
{
|
|
if (role == Qt::DisplayRole)
|
|
{
|
|
return KIO::convertSize(m_totalSize);
|
|
}
|
|
} else if (column == FileItem::ChecksumVerified) {
|
|
if (role == Qt::DecorationRole) {
|
|
switch (m_checkusmVerified) {
|
|
case Verifier::Verified:
|
|
return KIcon("dialog-ok");
|
|
case Verifier::NotVerified:
|
|
return KIcon("dialog-error");
|
|
case Verifier::NoResult:
|
|
default:
|
|
return KIcon();
|
|
}
|
|
}
|
|
}
|
|
|
|
return QVariant();
|
|
}
|
|
|
|
bool FileItem::setData(int column, const QVariant &value, FileModel *model, int role)
|
|
{
|
|
if (value.isNull())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (column == FileItem::File)
|
|
{
|
|
if (role == Qt::CheckStateRole)
|
|
{
|
|
m_state = static_cast<Qt::CheckState>(value.toInt());
|
|
model->changeData(this->row(), column, this);
|
|
checkParents(m_state, model);
|
|
checkChildren(m_state, model);
|
|
return true;
|
|
}
|
|
else if (role == Qt::EditRole)
|
|
{
|
|
m_name = value.toString();
|
|
model->changeData(this->row(), column, this);
|
|
return true;
|
|
}
|
|
}
|
|
else if (column == FileItem::Status)
|
|
{
|
|
if (role == Qt::EditRole)
|
|
{
|
|
if (isFile()) {
|
|
m_status = static_cast<Job::Status>(value.toInt());
|
|
bool finished = (m_status == Job::Finished);
|
|
model->changeData(this->row(), column, this, finished);
|
|
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
else if (column == FileItem::Size)
|
|
{
|
|
if (role == Qt::EditRole)
|
|
{
|
|
KIO::fileoffset_t newSize = value.toLongLong();
|
|
if (m_parent)
|
|
{
|
|
m_parent->addSize(newSize - m_totalSize, model);
|
|
}
|
|
m_totalSize = newSize;
|
|
model->changeData(this->row(), column, this);
|
|
return true;
|
|
}
|
|
} else if (column == FileItem::ChecksumVerified) {
|
|
m_checkusmVerified = value.toInt();
|
|
model->changeData(this->row(), column, this);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void FileItem::checkParents(Qt::CheckState state, FileModel *model)
|
|
{
|
|
if (!model)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!m_parent)
|
|
{
|
|
return;
|
|
}
|
|
|
|
foreach (FileItem *child, m_parent->m_childItems)
|
|
{
|
|
if (child->m_state != state)
|
|
{
|
|
state = Qt::Unchecked;
|
|
break;
|
|
}
|
|
}
|
|
|
|
m_parent->m_state = state;
|
|
model->changeData(m_parent->row(), FileItem::File, m_parent);
|
|
m_parent->checkParents(state, model);
|
|
}
|
|
|
|
void FileItem::checkChildren(Qt::CheckState state, FileModel *model)
|
|
{
|
|
if (!model)
|
|
{
|
|
return;
|
|
}
|
|
|
|
m_state = state;
|
|
model->changeData(row(), FileItem::File, this);
|
|
|
|
foreach (FileItem *child, m_childItems)
|
|
{
|
|
child->checkChildren(state, model);
|
|
}
|
|
}
|
|
|
|
FileItem *FileItem::parent()
|
|
{
|
|
return m_parent;
|
|
}
|
|
|
|
int FileItem::row() const
|
|
{
|
|
if (m_parent)
|
|
{
|
|
return m_parent->m_childItems.indexOf(const_cast<FileItem*>(this));
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void FileItem::addSize(KIO::fileoffset_t size, FileModel *model)
|
|
{
|
|
if (!isFile())
|
|
{
|
|
m_totalSize += size;
|
|
model->changeData(this->row(), FileItem::Size, this);
|
|
if (m_parent)
|
|
{
|
|
m_parent->addSize(size, model);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
FileModel::FileModel(const QList<KUrl> &files, const KUrl &destDirectory, QObject *parent)
|
|
: QAbstractItemModel(parent),
|
|
m_destDirectory(destDirectory),
|
|
m_checkStateChanged(false)
|
|
{
|
|
m_rootItem = new FileItem("root");
|
|
m_header << i18nc("file in a filesystem", "File") << i18nc("status of the download", "Status") << i18nc("size of the download", "Size") << i18nc("checksum of a file", "Checksum");
|
|
|
|
setupModelData(files);
|
|
}
|
|
|
|
FileModel::~FileModel()
|
|
{
|
|
delete m_rootItem;
|
|
}
|
|
|
|
void FileModel::setupModelData(const QList<KUrl> &files)
|
|
{
|
|
QString destDirectory = m_destDirectory.pathOrUrl();
|
|
|
|
foreach (const KUrl &file, files)
|
|
{
|
|
FileItem *parent = m_rootItem;
|
|
QStringList directories = file.pathOrUrl().remove(destDirectory).split('/', QString::SkipEmptyParts);
|
|
FileItem *child = 0;
|
|
while (directories.count())
|
|
{
|
|
QString part = directories.takeFirst();
|
|
for (int i = 0; i < parent->childCount(); ++i)
|
|
{
|
|
//folder already exists
|
|
if (parent->child(i)->data(0, Qt::DisplayRole).toString() == part)
|
|
{
|
|
parent = parent->child(i);
|
|
//file already exists
|
|
if (!directories.count())
|
|
{
|
|
break;
|
|
}
|
|
part = directories.takeFirst();
|
|
i = -1;
|
|
continue;
|
|
}
|
|
}
|
|
child = new FileItem(part, parent);
|
|
parent->appendChild(child);
|
|
parent = parent->child(parent->childCount() - 1);
|
|
}
|
|
if (child)
|
|
{
|
|
m_files.append(child);
|
|
}
|
|
}
|
|
}
|
|
|
|
int FileModel::columnCount(const QModelIndex &parent) const
|
|
{
|
|
if (parent.isValid())
|
|
{
|
|
return static_cast<FileItem*>(parent.internalPointer())->columnCount();
|
|
}
|
|
else
|
|
{
|
|
return m_rootItem->columnCount();
|
|
}
|
|
}
|
|
|
|
QVariant FileModel::data(const QModelIndex &index, int role) const
|
|
{
|
|
if (!index.isValid())
|
|
{
|
|
return QVariant();
|
|
}
|
|
|
|
|
|
FileItem *item = static_cast<FileItem*>(index.internalPointer());
|
|
const QVariant data = item->data(index.column(), role);
|
|
|
|
//get the status icon as well as status text
|
|
if (index.column() == FileItem::Status)
|
|
{
|
|
const Job::Status status = static_cast<Job::Status>(data.toInt());
|
|
if (item->isFile()) {
|
|
if (role == Qt::DisplayRole)
|
|
{
|
|
if (m_customStatusTexts.contains(status))
|
|
{
|
|
return m_customStatusTexts[status];
|
|
}
|
|
else
|
|
{
|
|
return Transfer::statusText(status);
|
|
}
|
|
}
|
|
else if (role == Qt::DecorationRole)
|
|
{
|
|
if (m_customStatusIcons.contains(status))
|
|
{
|
|
return m_customStatusIcons[status];
|
|
}
|
|
else
|
|
{
|
|
return Transfer::statusPixmap(status);
|
|
}
|
|
}
|
|
} else {
|
|
return QVariant();
|
|
}
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
bool FileModel::setData(const QModelIndex &index, const QVariant &value, int role)
|
|
{
|
|
if (!index.isValid())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
FileItem *item = static_cast<FileItem*>(index.internalPointer());
|
|
|
|
if ((index.column() == FileItem::File) && (role == Qt::CheckStateRole)) {
|
|
const bool worked = item->setData(index.column(), value, this, role);
|
|
if (worked) {
|
|
m_checkStateChanged = true;
|
|
}
|
|
|
|
return worked;
|
|
}
|
|
|
|
return item->setData(index.column(), value, this, role);
|
|
}
|
|
|
|
Qt::ItemFlags FileModel::flags(const QModelIndex &index) const
|
|
{
|
|
if (!index.isValid())
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (index.column() == FileItem::File)
|
|
{
|
|
return QAbstractItemModel::flags(index) | Qt::ItemIsUserCheckable;
|
|
}
|
|
|
|
return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
|
|
}
|
|
|
|
QVariant FileModel::headerData(int section, Qt::Orientation orientation, int role) const
|
|
{
|
|
if ((orientation == Qt::Horizontal) && (role == Qt::DisplayRole))
|
|
{
|
|
return m_header.value(section);
|
|
}
|
|
|
|
return QVariant();
|
|
}
|
|
|
|
QModelIndex FileModel::index(int row, int column, const QModelIndex &parent) const
|
|
{
|
|
if (!hasIndex(row, column, parent))
|
|
{
|
|
return QModelIndex();
|
|
}
|
|
|
|
FileItem *parentItem;
|
|
if (parent.isValid())
|
|
{
|
|
parentItem = static_cast<FileItem*>(parent.internalPointer());
|
|
}
|
|
else
|
|
{
|
|
parentItem = m_rootItem;
|
|
}
|
|
|
|
FileItem *childItem = parentItem->child(row);
|
|
if (childItem)
|
|
{
|
|
return createIndex(row, column, childItem);
|
|
}
|
|
else
|
|
{
|
|
return QModelIndex();
|
|
}
|
|
}
|
|
|
|
QModelIndex FileModel::index(const KUrl &file, int column)
|
|
{
|
|
FileItem *item = getItem(file);
|
|
if (!item)
|
|
{
|
|
return QModelIndex();
|
|
}
|
|
|
|
return createIndex(item->row(), column, item);
|
|
}
|
|
|
|
QModelIndexList FileModel::fileIndexes(int column) const
|
|
{
|
|
QModelIndexList indexList;
|
|
foreach (FileItem *item, m_files)
|
|
{
|
|
int row = item->row();
|
|
indexList.append(createIndex(row, column, item));
|
|
}
|
|
|
|
return indexList;
|
|
}
|
|
|
|
QModelIndex FileModel::parent(const QModelIndex &index) const
|
|
{
|
|
if (!index.isValid())
|
|
{
|
|
return QModelIndex();
|
|
}
|
|
|
|
FileItem *childItem = static_cast<FileItem*>(index.internalPointer());
|
|
FileItem *parentItem = childItem->parent();
|
|
if ((parentItem == m_rootItem) || (!parentItem))
|
|
{
|
|
return QModelIndex();
|
|
}
|
|
else
|
|
{
|
|
return createIndex(parentItem->row(), 0, parentItem);
|
|
}
|
|
}
|
|
|
|
int FileModel::rowCount(const QModelIndex &parent) const
|
|
{
|
|
if (parent.column() > 0)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
FileItem *parentItem;
|
|
if (parent.isValid())
|
|
{
|
|
parentItem = static_cast<FileItem*>(parent.internalPointer());
|
|
}
|
|
else
|
|
{
|
|
parentItem = m_rootItem;
|
|
}
|
|
|
|
return parentItem->childCount();
|
|
}
|
|
|
|
void FileModel::changeData(int row, int column, FileItem *item, bool finished)
|
|
{
|
|
QModelIndex index = createIndex(row, column, item);
|
|
emit dataChanged(index, index);
|
|
|
|
if (finished) {
|
|
const KUrl file = getUrl(index);
|
|
emit fileFinished(file);
|
|
}
|
|
}
|
|
|
|
|
|
void FileModel::setDirectory(const KUrl &newDirectory)
|
|
{
|
|
m_destDirectory = newDirectory;
|
|
m_itemCache.clear();
|
|
}
|
|
|
|
KUrl FileModel::getUrl(const QModelIndex &index)
|
|
{
|
|
if (!index.isValid()) {
|
|
return KUrl();
|
|
}
|
|
|
|
const QModelIndex file = index.sibling(index.row(), FileItem::File);
|
|
|
|
return getUrl(static_cast<FileItem*>(file.internalPointer()));
|
|
}
|
|
|
|
KUrl FileModel::getUrl(FileItem *item)
|
|
{
|
|
const QString path = getPath(item);
|
|
const QString name = item->data(FileItem::File, Qt::DisplayRole).toString();
|
|
KUrl url = m_destDirectory;
|
|
url.addPath(path + name);
|
|
|
|
return url;
|
|
}
|
|
|
|
QString FileModel::getPath(FileItem *item)
|
|
{
|
|
FileItem *parent = item->parent();
|
|
QString path;
|
|
while (parent && parent->parent())
|
|
{
|
|
path = parent->data(FileItem::File, Qt::DisplayRole).toString() + '/' + path;
|
|
parent = parent->parent();
|
|
}
|
|
|
|
return path;
|
|
}
|
|
|
|
FileItem *FileModel::getItem(const KUrl &file)
|
|
{
|
|
if (m_itemCache.contains(file))
|
|
{
|
|
return m_itemCache[file];
|
|
}
|
|
|
|
QString destDirectory = m_destDirectory.pathOrUrl();
|
|
|
|
FileItem *item = m_rootItem;
|
|
QStringList directories = file.pathOrUrl().remove(destDirectory).split('/', QString::SkipEmptyParts);
|
|
while (directories.count())
|
|
{
|
|
QString part = directories.takeFirst();
|
|
for (int i = 0; i < item->childCount(); ++i)
|
|
{
|
|
//folder already exists
|
|
if (item->child(i)->data(FileItem::File, Qt::DisplayRole).toString() == part)
|
|
{
|
|
item = item->child(i);
|
|
//file already exists
|
|
if (!directories.count())
|
|
{
|
|
break;
|
|
}
|
|
part = directories.takeFirst();
|
|
i = -1;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (item == m_rootItem)
|
|
{
|
|
item = 0;
|
|
}
|
|
else
|
|
{
|
|
m_itemCache[file] = item;
|
|
}
|
|
|
|
return item;
|
|
}
|
|
|
|
bool FileModel::downloadFinished(const KUrl &file)
|
|
{
|
|
FileItem *item = getItem(file);
|
|
if (item)
|
|
{
|
|
const Job::Status status = static_cast<Job::Status>(item->data(FileItem::Status, Qt::DisplayRole).toInt());
|
|
if (status == Job::Finished)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool FileModel::isFile(const QModelIndex &index) const
|
|
{
|
|
if (!index.isValid()) {
|
|
return false;
|
|
}
|
|
|
|
FileItem *item = static_cast<FileItem*>(index.internalPointer());
|
|
|
|
//only files can be renamed, no folders
|
|
return item->isFile();
|
|
}
|
|
|
|
void FileModel::rename(const QModelIndex &file, const QString &newName)
|
|
{
|
|
if (!file.isValid() || (file.column() != FileItem::File))
|
|
{
|
|
return;
|
|
}
|
|
|
|
FileItem *item = static_cast<FileItem*>(file.internalPointer());
|
|
//only files can be renamed, no folders
|
|
if (!item->isFile()) {
|
|
return;
|
|
}
|
|
|
|
//Find out the old and the new KUrl
|
|
QString oldName = file.data(Qt::DisplayRole).toString();
|
|
QString path = getPath(item);
|
|
|
|
KUrl oldUrl = m_destDirectory;
|
|
oldUrl.addPath(path + oldName);
|
|
KUrl newUrl = m_destDirectory;
|
|
newUrl.addPath(path + newName);
|
|
|
|
m_itemCache.remove(oldUrl);
|
|
|
|
setData(file, newName);
|
|
|
|
emit rename(oldUrl, newUrl);
|
|
}
|
|
|
|
void FileModel::renameFailed(const KUrl &beforeRename, const KUrl &afterRename)
|
|
{
|
|
Q_UNUSED(beforeRename)
|
|
Q_UNUSED(afterRename)
|
|
}
|
|
|
|
void FileModel::watchCheckState()
|
|
{
|
|
m_checkStateChanged = false;
|
|
}
|
|
|
|
void FileModel::stopWatchCheckState()
|
|
{
|
|
if (m_checkStateChanged)
|
|
{
|
|
emit checkStateChanged();
|
|
}
|
|
|
|
m_checkStateChanged = false;
|
|
}
|
|
|