From 4f26c4e5938b72306f704c547f199ab6fea57ffe Mon Sep 17 00:00:00 2001 From: lappelhans Date: Sat, 8 Dec 2007 19:20:04 +0000 Subject: [PATCH] Initial TorrentFileTreeModel... git-svn-id: svn://anonsvn.kde.org/home/kde/trunk/KDE/kdenetwork@746359 283d02a7-25f6-0310-bc7c-ecb5cbfe19da --- kget/transfer-plugins/bittorrent/CMakeLists.txt | 1 + .../bittorrent/btadvanceddetailswidget.cpp | 4 + .../bittorrent/btadvanceddetailswidget.ui | 32 +- .../bittorrent/torrentfiletreemodel.cpp | 448 +++++++++++++++++++++ .../bittorrent/torrentfiletreemodel.h | 138 +++++++ 5 files changed, 594 insertions(+), 29 deletions(-) create mode 100644 kget/transfer-plugins/bittorrent/torrentfiletreemodel.cpp create mode 100644 kget/transfer-plugins/bittorrent/torrentfiletreemodel.h diff --git a/kget/transfer-plugins/bittorrent/CMakeLists.txt b/kget/transfer-plugins/bittorrent/CMakeLists.txt index f4bc6e02d..908e0b7f1 100644 --- a/kget/transfer-plugins/bittorrent/CMakeLists.txt +++ b/kget/transfer-plugins/bittorrent/CMakeLists.txt @@ -18,6 +18,7 @@ set(kget_bittorrentfactory_PART_SRCS btadvanceddetailswidget.cpp btdownload.cpp peerview.cpp + torrentfiletreemodel.cpp ) kde4_add_ui_files(kget_bittorrentfactory_PART_SRCS btdetailswidgetfrm.ui btsettingswidget.ui btadvanceddetailswidget.ui) diff --git a/kget/transfer-plugins/bittorrent/btadvanceddetailswidget.cpp b/kget/transfer-plugins/bittorrent/btadvanceddetailswidget.cpp index efd170bec..ab461f927 100644 --- a/kget/transfer-plugins/bittorrent/btadvanceddetailswidget.cpp +++ b/kget/transfer-plugins/bittorrent/btadvanceddetailswidget.cpp @@ -14,6 +14,7 @@ #include #include "bttransferhandler.h" +#include "torrentfiletreemodel.h" #include #include @@ -56,6 +57,9 @@ void BTAdvancedDetailsWidget::init() } updateTracker(); + kt::TorrentFileTreeModel *fileTree = new kt::TorrentFileTreeModel(tc,kt::TorrentFileTreeModel::DeselectMode(1),this); + fileTreeView->setModel(fileTree); + connect(deleteTrackerButton, SIGNAL(clicked()), SLOT(deleteTracker())); connect(updateTrackerButton, SIGNAL(clicked()), SLOT(updateTracker())); connect(addTrackerButton, SIGNAL(clicked()), SLOT(addTracker())); diff --git a/kget/transfer-plugins/bittorrent/btadvanceddetailswidget.ui b/kget/transfer-plugins/bittorrent/btadvanceddetailswidget.ui index a5136e9a9..857607c93 100644 --- a/kget/transfer-plugins/bittorrent/btadvanceddetailswidget.ui +++ b/kget/transfer-plugins/bittorrent/btadvanceddetailswidget.ui @@ -13,7 +13,7 @@ - 3 + 0 @@ -21,33 +21,7 @@ - - - - File - - - - - Size - - - - - Download - - - - - Preview - - - - - % downloaded - - - + @@ -299,7 +273,7 @@ - Default Tracker + Default Trackers diff --git a/kget/transfer-plugins/bittorrent/torrentfiletreemodel.cpp b/kget/transfer-plugins/bittorrent/torrentfiletreemodel.cpp new file mode 100644 index 000000000..5693f0ae8 --- /dev/null +++ b/kget/transfer-plugins/bittorrent/torrentfiletreemodel.cpp @@ -0,0 +1,448 @@ +/*************************************************************************** + * Copyright (C) 2007 by Joris Guisson and Ivan Vasic * + * joris.guisson@gmail.com * + * ivasic@gmail.com * + * * + * 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 +#include +#include +#include +#include +#include +#include +#include "torrentfiletreemodel.h" + +using namespace bt; + +namespace kt +{ + + TorrentFileTreeModel::Node::Node(Node* parent,bt::TorrentFileInterface* file,const QString & name) + : parent(parent),file(file),name(name),size(0),expanded(true) + {} + + TorrentFileTreeModel::Node::Node(Node* parent,const QString & name) + : parent(parent),file(0),name(name),size(0),expanded(true) + { + } + + TorrentFileTreeModel::Node::~Node() + { + qDeleteAll(children); + } + + void TorrentFileTreeModel::Node::insert(const QString & path,bt::TorrentFileInterface* file) + { + int p = path.indexOf(bt::DirSeparator()); + if (p == -1) + { + // the file is part of this directory + children.append(new Node(this,file,path)); + } + else + { + QString subdir = path.left(p); + foreach (Node* n,children) + { + if (n->name == subdir) + { + n->insert(path.mid(p+1),file); + return; + } + } + + Node* n = new Node(this,subdir); + children.append(n); + n->insert(path.mid(p+1),file); + } + } + + int TorrentFileTreeModel::Node::row() + { + if (parent) + return parent->children.indexOf(this); + else + return 0; + } + + bt::Uint64 TorrentFileTreeModel::Node::fileSize(const bt::TorrentInterface* tc) + { + if (size > 0) + return size; + + if (!file) + { + // directory + foreach (Node* n,children) + size += n->fileSize(tc); + } + else + { + size = file->getSize(); + } + return size; + } + + bt::Uint64 TorrentFileTreeModel::Node::bytesToDownload(const bt::TorrentInterface* tc) + { + bt::Uint64 s = 0; + + if (!file) + { + // directory + foreach (Node* n,children) + s += n->bytesToDownload(tc); + } + else + { + if (!file->doNotDownload()) + s = file->getSize(); + } + return s; + } + + Qt::CheckState TorrentFileTreeModel::Node::checkState(const bt::TorrentInterface* tc) const + { + if (!file) + { + bool found_checked = false; + bool found_unchecked = false; + // directory + foreach (Node* n,children) + { + Qt::CheckState s = n->checkState(tc); + if (s == Qt::PartiallyChecked) + return s; + else if (s == Qt::Checked) + found_checked = true; + else + found_unchecked = true; + + if (found_checked && found_unchecked) + return Qt::PartiallyChecked; + } + + return found_checked ? Qt::Checked : Qt::Unchecked; + } + else + { + return file->doNotDownload() || file->getPriority() == ONLY_SEED_PRIORITY ? Qt::Unchecked : Qt::Checked; + } + } + + void TorrentFileTreeModel::Node::saveExpandedState(const QModelIndex & index,QTreeView* tv) + { + if (file) + return; + + expanded = tv->isExpanded(index); + + int idx = 0; + foreach (Node* n,children) + { + if (!n->file) + n->saveExpandedState(index.child(idx,0),tv); + idx++; + } + } + + void TorrentFileTreeModel::Node::loadExpandedState(const QModelIndex & index,QTreeView* tv) + { + if (file) + return; + + tv->setExpanded(index,expanded); + + int idx = 0; + foreach (Node* n,children) + { + if (!n->file) + n->loadExpandedState(index.child(idx,0),tv); + idx++; + } + } + + TorrentFileTreeModel::TorrentFileTreeModel(bt::TorrentInterface* tc,DeselectMode mode,QObject* parent) + : QAbstractItemModel(parent),tc(tc),root(0),mode(mode),emit_check_state_change(true) + { + if (tc->getStats().multi_file_torrent) + constructTree(); + else + root = new Node(0,tc->getStats().torrent_name); + } + + + TorrentFileTreeModel::~TorrentFileTreeModel() + {} + + void TorrentFileTreeModel::constructTree() + { + if (!root) + root = new Node(0,tc->getStats().torrent_name); + + for (Uint32 i = 0;i < tc->getNumFiles();i++) + { + bt::TorrentFileInterface & tf = tc->getTorrentFile(i); + root->insert(tf.getPath(),&tf); + } + } + + int TorrentFileTreeModel::rowCount(const QModelIndex & parent) const + { + if (!parent.isValid()) + { + return 1; + } + else + { + Node* n = (Node*)parent.internalPointer(); + return n->children.count(); + } + } + + int TorrentFileTreeModel::columnCount(const QModelIndex & parent) const + { + if (!parent.isValid()) + return 2; + else + return 2; + } + + QVariant TorrentFileTreeModel::headerData(int section, Qt::Orientation orientation,int role) const + { + if (role != Qt::DisplayRole || orientation != Qt::Horizontal) + return QVariant(); + + switch (section) + { + case 0: return i18n("File"); + case 1: return i18n("Size"); + default: + return QVariant(); + } + } + + QVariant TorrentFileTreeModel::data(const QModelIndex & index, int role) const + { + if (!index.isValid()) + return QVariant(); + + Node* n = (Node*)index.internalPointer(); + if (!n) + return QVariant(); + + if (role == Qt::DisplayRole) + { + switch (index.column()) + { + case 0: return n->name; + case 1: + if (tc->getStats().multi_file_torrent) + return BytesToString(n->fileSize(tc)); + else + return BytesToString(tc->getStats().total_bytes); + default: return QVariant(); + } + } + else if (role == Qt::DecorationRole && index.column() == 0) + { + // if this is an empty folder then we are in the single file case + if (!n->file) + return n->children.count() > 0 ? + KIcon("folder") : KIcon(KMimeType::findByPath(tc->getStats().torrent_name)->iconName()); + else + return KIcon(KMimeType::findByPath(n->file->getPath())->iconName()); + } + else if (role == Qt::CheckStateRole && index.column() == 0) + { + if (tc->getStats().multi_file_torrent) + return n->checkState(tc); + } + + return QVariant(); + } + + QModelIndex TorrentFileTreeModel::parent(const QModelIndex & index) const + { + if (!index.isValid()) + return QModelIndex(); + + Node* child = static_cast(index.internalPointer()); + if (!child) + return QModelIndex(); + + Node* parent = child->parent; + if (!parent) + return QModelIndex(); + else + return createIndex(parent->row(), 0, parent); + } + + QModelIndex TorrentFileTreeModel::index(int row,int column,const QModelIndex & parent) const + { + if (!hasIndex(row, column, parent)) + return QModelIndex(); + + Node* p = 0; + + if (!parent.isValid()) + return createIndex(row,column,root); + else + { + p = static_cast(parent.internalPointer()); + + if (row >= 0 && row < p->children.count()) + return createIndex(row,column,p->children.at(row)); + else + return QModelIndex(); + } + } + + Qt::ItemFlags TorrentFileTreeModel::flags(const QModelIndex & index) const + { + if (!index.isValid()) + return 0; + else if (tc->getStats().multi_file_torrent) + return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable; + else + return Qt::ItemIsSelectable | Qt::ItemIsEnabled; + } + + bool TorrentFileTreeModel::setData(const QModelIndex & index, const QVariant & value, int role) + { + if (!index.isValid() || role != Qt::CheckStateRole) + return false; + + Node* n = static_cast(index.internalPointer()); + if (!n) + return false; + + Qt::CheckState newState = static_cast(value.toInt()); + if (!n->file) + { + bool reenable = false; + if (emit_check_state_change) + { + reenable = true; + emit_check_state_change = false; + } + + for (Uint32 i = 0;i < n->children.count();i++) + { + // recurse down the tree + setData(index.child(i,0),value,role); + } + + if (reenable) + emit_check_state_change = true; + } + else + { + bt::TorrentFileInterface* file = n->file; + if (newState == Qt::Checked) + { + if (file->getPriority() == ONLY_SEED_PRIORITY) + file->setPriority(NORMAL_PRIORITY); + else + file->setDoNotDownload(false); + } + else + { + if (mode == KEEP_FILES) + file->setPriority(ONLY_SEED_PRIORITY); + else + file->setDoNotDownload(true); + } + dataChanged(createIndex(index.row(),0),createIndex(index.row(),columnCount(index) - 1)); + + QModelIndex parent = index.parent(); + if (parent.isValid()) + dataChanged(parent,parent); // parent needs to be updated to + } + + if (emit_check_state_change) + checkStateChanged(); + return true; + } + + void TorrentFileTreeModel::checkAll() + { + if (tc->getStats().multi_file_torrent) + setData(index(0,0,QModelIndex()),Qt::Checked,Qt::CheckStateRole); + } + + void TorrentFileTreeModel::uncheckAll() + { + if (tc->getStats().multi_file_torrent) + setData(index(0,0,QModelIndex()),Qt::Unchecked,Qt::CheckStateRole); + } + + void TorrentFileTreeModel::invertCheck() + { + if (!tc->getStats().multi_file_torrent) + return; + + invertCheck(index(0,0,QModelIndex())); + } + + void TorrentFileTreeModel::invertCheck(const QModelIndex & idx) + { + Node* n = static_cast(idx.internalPointer()); + if (!n) + return; + + if (!n->file) + { + for (Uint32 i = 0;i < n->children.count();i++) + { + // recurse down the tree + invertCheck(idx.child(i,0)); + } + } + else + { + if (n->file->doNotDownload()) + setData(idx,Qt::Checked,Qt::CheckStateRole); + else + setData(idx,Qt::Unchecked,Qt::CheckStateRole); + } + } + + bt::Uint64 TorrentFileTreeModel::bytesToDownload() + { + if (tc->getStats().multi_file_torrent) + return root->bytesToDownload(tc); + else + return tc->getStats().total_bytes; + } + + void TorrentFileTreeModel::saveExpandedState(QTreeView* tv) + { + if (tc->getStats().multi_file_torrent) + root->saveExpandedState(index(0,0,QModelIndex()),tv); + } + + + void TorrentFileTreeModel::loadExpandedState(QTreeView* tv) + { + if (tc->getStats().multi_file_torrent) + root->loadExpandedState(index(0,0,QModelIndex()),tv); + } +} + +#include "torrentfiletreemodel.moc" diff --git a/kget/transfer-plugins/bittorrent/torrentfiletreemodel.h b/kget/transfer-plugins/bittorrent/torrentfiletreemodel.h new file mode 100644 index 000000000..505933c03 --- /dev/null +++ b/kget/transfer-plugins/bittorrent/torrentfiletreemodel.h @@ -0,0 +1,138 @@ +/*************************************************************************** + * Copyright (C) 2007 by Joris Guisson and Ivan Vasic * + * joris.guisson@gmail.com * + * ivasic@gmail.com * + * * + * 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. * + ***************************************************************************/ +#ifndef KTTORRENTFILETREEMODEL_H +#define KTTORRENTFILETREEMODEL_H + +#include +#include + +class QTreeView; + +namespace bt +{ + class TorrentInterface; + class TorrentFileInterface; +} + +namespace kt +{ + + /** + * Model for displaying file trees of a torrent + * @author Joris Guisson + */ + class TorrentFileTreeModel : public QAbstractItemModel + { + Q_OBJECT + public: + enum DeselectMode + { + KEEP_FILES,DELETE_FILES + }; + + TorrentFileTreeModel(bt::TorrentInterface* tc,DeselectMode mode,QObject* parent); + virtual ~TorrentFileTreeModel(); + + virtual int rowCount(const QModelIndex & parent) const; + virtual int columnCount(const QModelIndex & parent) const; + virtual QVariant headerData(int section, Qt::Orientation orientation,int role) const; + virtual QVariant data(const QModelIndex & index, int role) const; + virtual QModelIndex parent(const QModelIndex & index) const; + virtual QModelIndex index(int row,int column,const QModelIndex & parent) const; + virtual Qt::ItemFlags flags(const QModelIndex & index) const; + virtual bool setData(const QModelIndex & index, const QVariant & value, int role); + + /** + * Check all the files in the torrent. + */ + void checkAll(); + + /** + * Uncheck all files in the torrent. + */ + void uncheckAll(); + + /** + * Invert the check of each file of the torrent + */ + void invertCheck(); + + /** + * Calculate the number of bytes to download + * @return Bytes to download + */ + bt::Uint64 bytesToDownload(); + + /** + * Save which items are expanded. + * @param tv The QTreeView + */ + void saveExpandedState(QTreeView* tv); + + /** + * Retore the expaned state of the tree.in a QTreeView + * @param tv The QTreeView + */ + void loadExpandedState(QTreeView* tv); + + signals: + /** + * Emitted whenever one or more items changes check state + */ + void checkStateChanged(); + + private: + void constructTree(); + void invertCheck(const QModelIndex & idx); + + protected: + struct Node + { + Node* parent; + bt::TorrentFileInterface* file; // file (0 if this is a directory) + QString name; // name or directory + QList children; // child dirs + bt::Uint64 size; + bool expanded; + + Node(Node* parent,bt::TorrentFileInterface* file,const QString & name); + Node(Node* parent,const QString & name); + ~Node(); + + void insert(const QString & path,bt::TorrentFileInterface* file); + int row(); + bt::Uint64 fileSize(const bt::TorrentInterface* tc); + bt::Uint64 bytesToDownload(const bt::TorrentInterface* tc); + Qt::CheckState checkState(const bt::TorrentInterface* tc) const; + + void saveExpandedState(const QModelIndex & index,QTreeView* tv); + void loadExpandedState(const QModelIndex & index,QTreeView* tv); + }; + + bt::TorrentInterface* tc; + Node* root; + DeselectMode mode; + bool emit_check_state_change; + }; + +} + +#endif -- 2.11.4.GIT